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


    简介

      微服务的系统应用中,网关系统使用的是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地址,截几张管理图

     

  • 相关阅读:
    指数
    汉诺塔问题
    只用递归和当前的栈实现栈的逆序
    让你996的不是你的老板,而是其他愿意996的人
    luke towan
    2020-9-3
    2020-9-3
    springboot注解
    2020-9-2
    20200827
  • 原文地址:https://www.cnblogs.com/tianxiangzhe/p/10336923.html
Copyright © 2020-2023  润新知