Bootstrap

.NET 5 微服务之 Ocelot + Consul

  • 微服务也是分布式。

  • 微服务是分布式服务拆分业务逻辑,完成解耦的架构模式

  • 微服务——把方法拆分成服务

1、怎么保证服务的高可用? Ocelot

2、服务的可伸缩 Consul

在高峰时,可以扩展或缩小服务集群

集群:相同的实例,干相同的活, nginx

基于集群去完成高可用以及伸缩性

问题:

  • 1、服务怎么发现服务

  • 2、负载均衡,如何调用服务

二、consul

1、服务在 startup.cs 中向 Consul 注册

2、consul 监测服务的状态是否健康-- 挂掉就把服务移出掉

3、客户端先检查有几个服务可以调用

缺点:客户端复杂点,要代码实现负载均衡策略

三、Ocelot 网关

只有网关也能实现集群 + 负载均衡,但是没有办法做服务发现和服务自动下线(所以要配合 consul)

1、Nuget Ocelot 、Ocelot.Provider.consul(用于服务管理)

2、在startup.cs 中配置 Ocelot 中间键

public void ConfigureServices(IServiceCollection services)
 {
    services.AddOcelot()
        .AddConsul() //加 Consul服务
        .AddCacheManager(x => //加缓存
                         {
                             x.WithDictionaryHandle();//默认字典存储
                         });//Ocelot怎么处理请求
    services.AddControllers();
    services.AddSingleton<IOcelotCache<CachedResponse>, CustomCache>();//利用IOC容器的映射关系,重写Ocelot的缓存
 }
 
 public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
     #region 管道事件扩展  复古式的 跟Asp.Net那套基于事件扩展的套路,下面这些就是IHttpModule
     var logger = loggerFactory.CreateLogger<Startup>();
     var configuration = new OcelotPipelineConfiguration
     {
         PreErrorResponderMiddleware = async (ctx, next) =>
         {
             logger.LogWarning("This is Custom PreErrorResponderMiddleware....");
             await next.Invoke();
         },
         PreAuthenticationMiddleware = async (ctx, next) =>
         {
             logger.LogWarning("This is Custom PreAuthenticationMiddleware....");
             await next.Invoke();
         },
         AuthenticationMiddleware = async (ctx, next) =>
         {
             logger.LogWarning("This is Custom AuthenticationMiddleware....");
             await next.Invoke();
         },
         PreQueryStringBuilderMiddleware = async (ctx, next) =>
         {
             logger.LogWarning("This is Custom PreQueryStringBuilderMiddleware....");
             await next.Invoke();
         }
     };
     //app.UseOcelot(configuration);
     //app.UseOcelotEleven(configuration).Wait();
     #endregion
 
    app.UseOcelot(configuration);//请求交给Ocelot
 }
​

3、配置Ocelot 配置文件

配置文件 configuration.json

program.cs 中读取配置文件

服务治理:Rate Limiting — Ocelot 1.0.0 documentation

  • 在这边可以做,热数据缓存、限流、熔断【请求服务失败多少次,就表示服务挂掉了】、超时

问题:微服务的session 使用会受到限制,怎么解决,用 Redis ?

四、实践笔记

4.1 Consul 实现服务注册,负载均衡

4.1.1 Docker 安装 Consul

docker pull consul      -- 拉取最新的consul
docker run -d --name consul -p 8500:8500 consul    -- 启动,默认端口为 8500
  • 验证

4.1.2 创建一个 .NET 5 的 mvc 项目

  • Nuget consul 包

  • 写服务注册的扩展

public static void ConsulRegist(this IConfiguration configuration)
        {
            try
            {
                //读取配置文件
                string ip = configuration["ip"];
                string port = configuration["port"];
                string weight = configuration["weight"];
                string consulAddress = configuration["ConsulAddress"];
                string consulCenter = configuration["ConsulCenter"];
​
                //创建注册服务
                ConsulClient client = new ConsulClient(c =>
                {
                    c.Address = new Uri(consulAddress);
                    c.Datacenter = consulCenter;
                });
​
                client.Agent.ServiceRegister(new AgentServiceRegistration()
                {
                    ID = "service " + ip + ":" + port,//Ray--唯一的--乔峰虚竹段誉
                    Name = "ConsulRegister",//分组---天龙三兄弟
                    Address = ip,
                    Port = int.Parse(port),
                    Tags = new string[] { weight.ToString() },//额外标签信息
                    Check = new AgentServiceCheck() 
                    {
                        Interval = TimeSpan.FromSeconds(12),
                        HTTP = $"http://{ip}:{port}/Api/Health/Index", //心跳监测方法
                        Timeout = TimeSpan.FromSeconds(5),  //超时时间
                        DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(20) 
                    }//配置心跳
                });
                Console.WriteLine($"{ip}:{port}--weight:{weight}"); //命令行参数获取
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Consul注册:{ex.Message}");
            }
        }
    }
​

  • 在 startup.cs 中配置 consul 服务

 public void ConfigureServices(IServiceCollection services)
 {
     services.AddControllers(); 
     services.AddControllersWithViews(); 
 }
 
 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
 {
     if (env.IsDevelopment())
     {
        app.UseDeveloperExceptionPage(); 
     }
     else
     {
        app.UseExceptionHandler("/Home/Error");
     }
     app.UseStaticFiles(); 
     app.UseRouting(); 
     app.UseAuthorization(); 
     app.UseEndpoints(endpoints =>
     {
         endpoints.MapControllerRoute(
         name: "default",
         pattern: "{controller=Home}/{action=Index}/{id?}");
     });
     //程序启动时就执行---且只执行一次
     this.Configuration.ConsulRegist();
 }
​

  • 心跳监测方法

    
[Route("api/[controller]")]
    [ApiController]
    public class HealthController : ControllerBase
    {
        private IConfiguration _iConfiguration;
​
        public HealthController(IConfiguration configuration)
        {
            this._iConfiguration = configuration;
        }
​
        [HttpGet]
        [Route("Index")]
        public IActionResult Index()
        {
            Console.WriteLine($"This is HealthController  {this._iConfiguration["port"]} Invoke");
            
            return Ok();//只是个200 
        }
​
    }

  • 启动项目的时候,会自动向 consul 注册

 dotnet ConsulAndOcelot.dll --urls="http://*:992" --ip="192.168.0.137" --port=992
 -- 这边 ip 不能写 127.0.0.1 ,consul 注册不能成功,要写实体机真实 ip

4.1.3 在调用端可以获取到 consul 的服务,然后用代码进行负载均衡

 #region Consul
string url = null;
//url = "http://consulGroup/home/getdata";
ConsulClient client = new ConsulClient(c =>
                                       { c.Address = new Uri("http://127.0.0.1:8500/");//consul地址
                                         c.Datacenter = "dc1";
                                       });
var response = client.Agent.Services().Result.Response;
foreach (var item in response)
{
    Console.WriteLine("***************************************");
    Console.WriteLine(item.Key);
    var service = item.Value;
    Console.WriteLine($"{service.Address}--{service.Port}--{service.Service}");
    Console.WriteLine("***************************************");
}
​
Uri uri = new Uri(url);
string groupName =  "consulGroup";//uri.Host;
AgentService agentService = null;
var serviceDictionary = response.Where(s => s.Value.Service.Equals(groupName, StringComparison.OrdinalIgnoreCase)).ToArray();//获取的全部服务实例信息 5726/5727/5728
{
   // agentService = serviceDictionary[0].Value;//写死--死心眼,怼一个
   //轮询策略
   agentService = serviceDictionary[iTotalCount++ % serviceDictionary.Length].Value;
}
​
url = $"{uri.Scheme}://{agentService.Address}:{agentService.Port}{uri.PathAndQuery}";
​
string content = this.InvokeApi(url);
base.ViewBag.Users = JsonConvert.DeserializeObject<IEnumerable<User>>(content);
Console.WriteLine($"This is {url} Invoke");
#endregion
    
private static int iTotalCount = 0;

如果只用 consul 已经可以实现微服务+负载均衡

4.2 Ocelot 网关,服务治理(限流、熔断、负载均衡、缓存..)

4.2.1 Nuget 依赖包 Ocelot ,搭配 Ocelot.Provider.Consul 做服务注册和监测

  • startup.cs 配置服务和管道

public void ConfigureServices(IServiceCollection services)
{ 
services.AddOcelot();
}
​
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseOcelot(); 
}

  • 添加配置文件 configuration.json(默认的配置文件为 appsetting,这边单独写一个配置文件) 和startup.cs 同级

//*****************************单地址配置********************************
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/{url}", //服务地址--url变量
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "192.168.0.137",
          "Port": 991 //服务端口
        }
      ],
      "UpstreamPathTemplate": "/d991/{url}", //网关地址--url变量
      "UpstreamHttpMethod": [ "Get", "Post" ]
    }
  ]
}

*****************************超时+限流+熔断+降级+Consul+Polly********************************
//{
//  "Routes": [
//    {
//      "DownstreamPathTemplate": "/api/{url}", //服务地址--url变量
//      "DownstreamScheme": "http",
//      "UpstreamPathTemplate": "/T/{url}", //网关地址--url变量
//      "UpstreamHttpMethod": [ "Get", "Post" ],
//      "UseServiceDiscovery": true,
//      "ServiceName": "ZhaoxiService", //consul服务名称
//      "LoadBalancerOptions": {
//        "Type": "RoundRobin" //轮询      LeastConnection-最少连接数的服务器   NoLoadBalance不负载均衡
//      },
//      "RateLimitOptions": {
//        "ClientWhitelist": [ "eleven", "seven" ], //白名单 ClientId 区分大小写
//        "EnableRateLimiting": true,
//        "Period": "5m", //1s, 5m, 1h, 1d
//        "PeriodTimespan": 30, //多少秒之后客户端可以重试
//        "Limit": 5 //统计时间段内允许的最大请求数量
//      },
//      "AuthenticationOptions": {
//        "AuthenticationProviderKey": "UserGatewayKey",
//        "AllowedScopes": []
//      },
//      "QoSOptions": {
//        "ExceptionsAllowedBeforeBreaking": 3, //允许多少个异常请求
//        "DurationOfBreak": 10000, // 熔断的时间,单位为ms
//        "TimeoutValue": 2000 //单位ms 如果下游请求的处理时间超过多少则自如将请求设置为超时 默认90秒
//      }
//      //"FileCacheOptions": {
//      //  "TtlSeconds": 15,
//      //  "Region": "UserCache" //可以调用Api清理
//      //}
//    }
//  ],
//  "GlobalConfiguration": {
//    "BaseUrl": "http://127.0.0.1:6299", //网关对外地址
//    "ServiceDiscoveryProvider": {
//      "Host": "47.95.2.2",
//      "Port": 8089,
//      "Type": "Consul" //由Consul提供服务发现
//    },
//    "RateLimitOptions": {
//      "QuotaExceededMessage": "Too many requests, maybe later? 11", // 当请求过载被截断时返回的消息
//      "HttpStatusCode": 666, // 当请求过载被截断时返回的http status
//      //"ClientIdHeader": "client_id" // 用来识别客户端的请求头,默认是 ClientId
//    }
//  }
//}

解释

我们访问的地址为 UpstreamPathTemplate 上游地址,Ocelot 会个根据这个规则去找下游地址 DownstreamPathTemplate 进行转发

  • 修改 Program.cs 文件

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureAppConfiguration(conf=> {//重置了默认配置文件目录(默认的是 appsetting.json )
                    conf.AddJsonFile("configuration.json", optional: false, reloadOnChange: true);
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });

验证

4.3  服务治理,添加 Polly

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddLogging(logging => { logging.AddNLog(); });
            services.AddClsLogging(Configuration);

            services.AddOcelot()
                .AddConsul()
                .AddPolly();//瞬态故障库 限流、黑白名单、
        }

然后在配置文件配置

{
  "Routes": [
    {
      "UpstreamHttpMethod": [ "Post" ],
      "UpstreamPathTemplate": "/ShuXi.IdService/{ext}",
      "DownstreamPathTemplate": "/command/{ext}",
      "DownstreamScheme": "http",
      "LoadBalancerOptions": { "Type": "RoundRobin" },
      "UseServiceDiscovery": true,
      "ServiceName": "ShuXi.IdService",
      "RateLimitOptions": {
        "EnableRateLimiting": true,
        "Period": "1s",
        "PeriodTimespan": 2,
        "Limit": 500
      },
      "QoSOptions": {
        "ExceptionsAllowedBeforeBreaking": 10,
        "DurationOfBreak": 3000,
        "TimeoutValue": 3000
      }
    }
  ]
}

;