Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

【NET CORE微服务一条龙应用】第一章 网关使用与配置

天翔者 2019-01-30 16:47:00 阅读数:157 评论数:0 点赞数:0 收藏数:0

简介

  微服务的系统应用中,网关系统使用的是ocelot,ocelot目前已经比较成熟了

  ocelot就不做介绍了,等整体介绍完后再进行各类扩展介绍,ocelot源码地址:https://github.com/ThreeMammals/Ocelot

  ocelot目前由很多功能组件组成,每个组件都可以根据自己的实际情况进行扩展(暂时不做过多介绍)

  本文主要介绍ocelot网关使用中个人认为应该最先处理的东西

健康检查

  在实际的应用中网关项目都会部署多台,然后通过nginx进行软负载,在更新部署网关项目的过程中服务肯定是无法使用,这个时候我们就需要利用nginx的健康检查机制进行控制

  网关需要给nginx提供一个健康检查地址,ocelot使用的url path地址进行路由匹配,当匹配不到时会返回404,所以我们需要单独处理一个健康检查地址

  Ocelot提供了一个中间件配置替换的方法OcelotPipelineConfiguration,我们对OcelotPipelineConfiguration的PreErrorResponderMiddleware中间件方法进行扩展,代码如下:

 1 var conf = new OcelotPipelineConfiguration()
 2 {
 3 PreErrorResponderMiddleware = async (ctx, next) =>
 4  {
 5 if (ctx.HttpContext.Request.Path.Equals(new PathString("/")))
 6  {
 7 await ctx.HttpContext.Response.WriteAsync("ok");
 8  }
 9 else
10  {
11 await next.Invoke();
12  }
13  }
14 };
15 app.UseOcelot(conf).Wait();

网关和路由配置

  网关的配置包含四个部分,ReRoutes、DynamicReRoutes、Aggregates、GlobalConfiguration,

  ocelot配置的获取默认是使用配置文件的方式,上面已经说了网关一般都会部署多台,使用文件配置还是存在一定弊端

  ocelot的配置获取方法是IFileConfigurationRepository接口,所以如果我们实现了此接口就可以满足配置存储方式的扩展,目前已扩展mysql和redis,代码如下

  redis:

 1 public class RedisFileConfigurationRepository: IFileConfigurationRepository
 2  {
 3 private readonly RedisClient _redisClient;
 4 private readonly string _apiGatewayKey;
 5 private readonly string _redisConnection;
 6
 7 public RedisFileConfigurationRepository(RedisClient redisClient, string apiGatewayKey, string redisConnection)
 8  {
 9 _redisClient = redisClient;
10 _apiGatewayKey = apiGatewayKey;
11 _redisConnection = redisConnection;
12  }
13
14 public async Task<Response<FileConfiguration>> Get()
15  {
16 var redis = _redisClient.GetDatabase(_redisConnection, 11);
17
18 var json = await redis.StringGetAsync($"ApiGatewayConfig:{_apiGatewayKey}");
19
20 if(json.IsNullOrEmpty)
21 return new OkResponse<FileConfiguration>(new FileConfiguration { });
22
23 var fileConfig = JsonConvert.DeserializeObject<FileConfiguration>(json);
24
25 return new OkResponse<FileConfiguration>(fileConfig);
26  }
27
28 public async Task<Response> Set(FileConfiguration fileConfiguration)
29  {
30 return await Task.FromResult(new OkResponse());
31  }
32 }

mysql:

 1 public class MySqlFileConfigurationRepository : IFileConfigurationRepository
 2  {
 3 private readonly IDbRepository<ConfigurationInfo> _configDbRepository;
 4 private readonly IDbRepository<ReRouteInfo> _routeDbRepository;
 5 private readonly string _apiGatewayKey;
 6
 7 public MySqlFileConfigurationRepository(IDbRepository<ConfigurationInfo> configDbRepository, IDbRepository<ReRouteInfo> routeDbRepository, string apiGatewayKey)
 8  {
 9 _configDbRepository = configDbRepository;
 10 _routeDbRepository = routeDbRepository;
 11 _apiGatewayKey = apiGatewayKey;
 12  }
 13
 14 public async Task<Response<FileConfiguration>> Get()
 15  {
 16 var st = DateTime.Now;
 17 var fileConfig = new FileConfiguration();
 18 var configInfo = await _configDbRepository.GetFirstAsync(it => it.GatewayKey == _apiGatewayKey);
 19 if (configInfo != null)
 20  {
 21 // config
 22 var fgc = new FileGlobalConfiguration
 23  {
 24 BaseUrl = configInfo.BaseUrl,
 25 DownstreamScheme = configInfo.DownstreamScheme,
 26 RequestIdKey = configInfo.RequestIdKey,
 27  };
 28 if (!string.IsNullOrWhiteSpace(configInfo.HttpHandlerOptions))
 29 fgc.HttpHandlerOptions = ToObject<FileHttpHandlerOptions>(configInfo.HttpHandlerOptions);
 30 if (!string.IsNullOrWhiteSpace(configInfo.LoadBalancerOptions))
 31 fgc.LoadBalancerOptions = ToObject<FileLoadBalancerOptions>(configInfo.LoadBalancerOptions);
 32 if (!string.IsNullOrWhiteSpace(configInfo.QoSOptions))
 33 fgc.QoSOptions = ToObject<FileQoSOptions>(configInfo.QoSOptions);
 34 if (!string.IsNullOrWhiteSpace(configInfo.RateLimitOptions))
 35 fgc.RateLimitOptions = ToObject<FileRateLimitOptions>(configInfo.RateLimitOptions);
 36 if (!string.IsNullOrWhiteSpace(configInfo.ServiceDiscoveryProvider))
 37 fgc.ServiceDiscoveryProvider = ToObject<FileServiceDiscoveryProvider>(configInfo.ServiceDiscoveryProvider);
 38 fileConfig.GlobalConfiguration = fgc;
 39
 40 // reroutes
 41 var reRouteResult = await _routeDbRepository.GetListAsync(it => it.GatewayId == configInfo.GatewayId && it.State == 1);
 42 if (reRouteResult.Count > 0)
 43  {
 44 var reroutelist = new List<FileReRoute>();
 45 foreach (var model in reRouteResult)
 46  {
 47 var m = new FileReRoute()
 48  {
 49 UpstreamHost = model.UpstreamHost,
 50 UpstreamPathTemplate = model.UpstreamPathTemplate,
 51
 52 DownstreamPathTemplate = model.DownstreamPathTemplate,
 53 DownstreamScheme = model.DownstreamScheme,
 54
 55 ServiceName = model.ServiceName,
 56 Priority = model.Priority,
 57 RequestIdKey = model.RequestIdKey,
 58 Key = model.Key,
 59 Timeout = model.Timeout,
 60  };
 61 if (!string.IsNullOrWhiteSpace(model.UpstreamHttpMethod))
 62 m.UpstreamHttpMethod = ToObject<List<string>>(model.UpstreamHttpMethod);
 63 if (!string.IsNullOrWhiteSpace(model.DownstreamHostAndPorts))
 64 m.DownstreamHostAndPorts = ToObject<List<FileHostAndPort>>(model.DownstreamHostAndPorts);
 65 if (!string.IsNullOrWhiteSpace(model.SecurityOptions))
 66 m.SecurityOptions = ToObject<FileSecurityOptions>(model.SecurityOptions);
 67 if (!string.IsNullOrWhiteSpace(model.CacheOptions))
 68 m.FileCacheOptions = ToObject<FileCacheOptions>(model.CacheOptions);
 69 if (!string.IsNullOrWhiteSpace(model.HttpHandlerOptions))
 70 m.HttpHandlerOptions = ToObject<FileHttpHandlerOptions>(model.HttpHandlerOptions);
 71 if (!string.IsNullOrWhiteSpace(model.AuthenticationOptions))
 72 m.AuthenticationOptions = ToObject<FileAuthenticationOptions>(model.AuthenticationOptions);
 73 if (!string.IsNullOrWhiteSpace(model.RateLimitOptions))
 74 m.RateLimitOptions = ToObject<FileRateLimitRule>(model.RateLimitOptions);
 75 if (!string.IsNullOrWhiteSpace(model.LoadBalancerOptions))
 76 m.LoadBalancerOptions = ToObject<FileLoadBalancerOptions>(model.LoadBalancerOptions);
 77 if (!string.IsNullOrWhiteSpace(model.QoSOptions))
 78 m.QoSOptions = ToObject<FileQoSOptions>(model.QoSOptions);
 79 if (!string.IsNullOrWhiteSpace(model.DelegatingHandlers))
 80 m.DelegatingHandlers = ToObject<List<string>>(model.DelegatingHandlers);
 81  reroutelist.Add(m);
 82  }
 83 fileConfig.ReRoutes = reroutelist;
 84  }
 85  }
 86 Console.WriteLine((DateTime.Now - st).TotalMilliseconds);
 87 return new OkResponse<FileConfiguration>(fileConfig);
 88  }
 89
 90 public async Task<Response> Set(FileConfiguration fileConfiguration)
 91  {
 92 return await Task.FromResult(new OkResponse());
 93  }
 94
 95 /// <summary>
 96 /// 将Json字符串转换为对象
 97 /// </summary>
 98 /// <param name="json">Json字符串</param>
 99 private T ToObject<T>(string json)
100  {
101 if (string.IsNullOrWhiteSpace(json))
102 return default(T);
103 return JsonConvert.DeserializeObject<T>(json);
104  }
105 }

可以看到四项配置里并不是全部都进行可配置化,如果有需求可以自行增加字段实现

redis的存储是大json方式,而mysql是一条一条的,因为配置的管理是以mysql为主,然后同步到其他存储介质中的

网关配置的更新

 有加载就有更新,在ocelot中配置的更新是使用自己的实现来完成配置的热更新,方式如下

 1、配置文件方式是通过配置文件的IOptionsMonitor的OnChange方式重新加载配置信息

 2、第三方存储方式是通过默认实现的FileConfigurationPoller方法定时(默认1s)取获取配置信息的

 所以我们扩展的获取配置形式,在注册的时候要把FileConfigurationPoller HostedService一同注入进去,代码如下

 1 public static IOcelotBuilder AddConfigStoredInRedis(this IOcelotBuilder builder, string apiGatewayKey, string redisConnectionString)
 2  {
 3 builder.Services.AddSingleton<RedisClient>();
 4 builder.Services.AddHostedService<FileConfigurationPoller>();
 5 builder.Services.AddSingleton<IFileConfigurationRepository>(sp =>
 6  {
 7 return new RedisFileConfigurationRepository(sp.GetRequiredService<RedisClient>(), apiGatewayKey, redisConnectionString);
 8  });
 9 return builder;
10 }

其中涉及到Bucket.DbContext和Bucket.Redis组件很简单,也可自行实现

配置的管理

  其实最开始的时候,使用的是consul存储配置,然后通过网关自带的配置接口进行配置的管理,但是在ocelot的一次升级的时候出现了一个问题(配置信息丢失),虽然当时修改了ocelot的源码解决了,后来还是决定扩展存储方式,所以上面的获取配置接口的set方法都不实现了

  上面已经说了是已mysql进行配置存储然后同步到其他介质上,所以我们只要维护好mysql数据库就可以了

  具体代码就不贴了,后续会进行具体介绍,管理项目地址:github地址,截几张管理图

 

 

版权声明
本文为[天翔者]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/tianxiangzhe/p/10336923.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领