MicroService(Ocelot)

为了防止调用服务主动权在客户端,让客户端调网关,网关调服务

网关作用:

1,重试策略(Retry):当服务调用失败时,Polly 可以自动进行重试,这有助于
处理那些可能因为暂时性问题导致的服务不可用情况。
2、断路器(Circuit Breaker):当检测到服务连续不可用时,断路器策略会介入,
快速返回错误响应,避免对下游服务的持续请求,从而预防服务雪崩现象。(熔断
ocelot+polly)
polly:瞬态故障处理库
3、超时策略(Timeout):为服务调用设置一个最大执行时间,超过这个时间的服
务调用将被认为失败,可以采取预设的应对措施。
4、舱壁隔离(Bulkhead Isolation):通过限制对服务的并发调用数量,防止因
某个服务的问题影响到整个系统的稳定性。(ocelot 限流)
5、缓存策略(Cache):提供一种机制,可以在服务调用的结果不变的情况下直接
使用缓存结果,减少不必要的服务调用。(ocelot 缓存)
6、降级策略(Fallback):当服务调用失败时,可以提供一个备用的逻辑或者数
据作为响应,提高用户体验。(polly 服务降级)
7、策略组合(PolicyWrap) : Polly 针对不同

1下载nuget包

 <PackageReference Include="Ocelot" Version="23.1.0" />
 <PackageReference Include="Ocelot.Provider.Consul" Version="23.1.0" />
 <PackageReference Include="Ocelot.Provider.Polly" Version="23.1.0" />

2基本认识Ocelot.json文件

////***************************单地址*ַ********************************
//访问: http://localhost:9005/T9001/Users/All
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/{url}", //服务地址,Url变量
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "127.0.0.1",
          "Port": 9001 //服务端口
        } //http://127.0.0.1:9001/api/users/all
      ],
      "UpstreamPathTemplate": "/T9001/{url}", //网关地址: Url变量
      "UpstreamHttpMethod": [ "Get", "Post" ]
    }
  ]
}


  //*****************************多地址********************************
//访问: https://localhost:7253/T9001/Users/All
//访问: https://localhost:7253/T9002/Users/All
//访问: https://localhost:7253/T9003/Users/All
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/{url}", //服务地址,Url变量
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "127.0.0.1",
          "Port": 9001 //服务端口
        }
      ],
      "UpstreamPathTemplate": "/T9001/{url}", //网关地址: Url变量
      "UpstreamHttpMethod": [ "Get", "Post" ]
    },
    {
      "DownstreamPathTemplate": "/api/{url}", //服务地址,Url变量
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "127.0.0.1",
          "Port": 9002 //服务端口
        }
      ],
      "UpstreamPathTemplate": "/T9002/{url}", //网关地址: Url变量
      "UpstreamHttpMethod": [ "Get", "Post" ]
    },
    {
      "DownstreamPathTemplate": "/api/{url}", //服务地址,Url变量
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "127.0.0.1",
          "Port": 9003 //服务端口
        }
      ],
      "UpstreamPathTemplate": "/T9003/{url}", //网关地址: Url变量
      "UpstreamHttpMethod": [ "Get", "Post" ]
    }
  ]
}


  //*****************************单地址多实例负载均衡********************************
//可以负载均衡--但是不能动态伸缩服务, 所以一般不会使用他来做负载均衡, 需要和Consul做集成
{
  "Routes": [
    {
      "DownstreamPathTemplate": "/api/{url}", //服务地址Url变量
      "DownstreamScheme": "http",
      "DownstreamHostAndPorts": [
        {
          "Host": "127.0.0.1",
          "Port": 9001
        },
        {
          "Host": "127.0.0.1",
          "Port": 9002
        },
        {
          "Host": "127.0.0.1",
          "Port": 9003
        } //能负载均衡,但是不能动态伸缩, 需要结合Consul来完成
      ],
      "UpstreamPathTemplate": "/T/{url}", //网关地址: Url变量
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "LoadBalancerOptions": {
        "Type": "RoundRobin" //轮询 // "LeastConnection" //最小连接数的服务器  "NoLoadBalance" //不负载均衡 //
      }
      //"LoadBalancerOptions": {
      //  "Type": "CookieStickySessions",
      //  "Key": "ASP.NET_SessionId",
      //  "Expiry": 1800000
      //}
    }
  ]
}

完整文件,包含ocelot+consul+Polly+缓存

3使用Ocelot

{
  "Routes": [ //这里注意一下版本(旧版本用ReRoutes)
    {
      "DownstreamPathTemplate": "/{controller}",
      "DownstreamScheme": "http", //下游方案
      "UpstreamPathTemplate": "/TestGate/{controller}",
      "UpstreamHttpMethod": [ "Get", "Post" ],
      "UseServiceDiscovery": true,//使用服务发现
      "ServiceName": "ConsulService", //请求服务名称
      "LoadBalancerOptions": {
        "Type": "RoundRobin"//LeastConnection最少连接数的服务器,NoLoadBalance不负债均衡CookieStickySessions会话粘滞
      },
      "FileCacheOptions": {
        "TtlSeconds": 10,
        "Region": "myRegion"
      },
      "RateLimitOptions": {//限流
        "ClientWhitelist": ["Admin"],//白名单,在其中的不限流,请求头加上ClientId:Admin,区分大小写
        "EnableRateLimiting": true,
        "Period": "1m",//1s,1h
        "PeriodTimespan": 5,//多少秒可以重试
        "Limit": 4//时间段允许的最大请求数量
      },
      "QoSOptions": {
        "ExceptionsAllowedBeforeBreaking": 3,//允许3次异常polly
        "DurationOfBreak": 5000, //ms,熔断时间
        "TimeoutValue": 2000//2s内不给响应,异常时间超过2s,熔断
      }
    }
  ],
  "GlobalConfiguration": {//服务发现中心
    "BaseUrl": "http://localhost:6001", //进行标头查找和替换以及某些管理配置
    "ServiceDiscoveryProvider": {
      "Scheme": "http",
      "Host": "127.0.0.1", //你的Consul的ip地址
      "Port": 8500, //你的Consul的端口
      "Type": "Consul" //类型
    },
    "RateLimitOptions": {//限流响应
      "QuotaExceededMessage": "Too Many Requests,please wait a moment",
      "HttpStatusCode": 503
    }
  }
}
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;
using Ocelot.Provider.Polly;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddOcelot().AddConsul().AddPolly();//注册

builder.Configuration.AddJsonFile("ocelot.json", false, true);
var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();
app.UseOcelot().Wait();//使用Ocelot
app.Run();


4进阶

扩展自定义缓存

namespace LearnOcelot.OcelotExtend
{

    public class CustomCacheExtend : IOcelotCache<CachedResponse>
    {

        private ILogger<CustomCacheExtend> _logger = null;
        public CustomCacheExtend(ILogger<CustomCacheExtend> logger)
        {
            this._logger = logger;
        }
         
        private static Dictionary<string, CacheDataModel> CustomCacheExtendDictionary = new
          Dictionary<string, CacheDataModel>();

        /// <summary>
        /// 向缓存中去缓存数据--会调用这个方法
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="ttl"></param>
        /// <param name="region"></param>
        public void Add(string key, CachedResponse value, TimeSpan ttl, string region)
        {
            this._logger.LogWarning($"This is {nameof(CustomCacheExtend)}.{nameof(Add)}");
            //CustomCacheExtendDictionary.Add(key, new CacheDataModel()
            //{
            //    CachedResponse = value,
            //    Region = region,
            //    Timeout = DateTime.Now.Add(ttl)
            //});
            CustomCacheExtendDictionary[key] = new CacheDataModel()
            {
                CachedResponse = value,
                Region = region,
                Timeout = DateTime.Now.Add(ttl)
            };
        }

        /// <summary>
        /// 添加缓存, 如果有历史缓存, 就覆盖
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="ttl"></param>
        /// <param name="region"></param>
        public void AddAndDelete(string key, CachedResponse value, TimeSpan ttl, string region)
        {
            this._logger.LogWarning($"This is {nameof(CustomCacheExtend)}.{nameof(AddAndDelete)}");
            CustomCacheExtendDictionary[key] = new CacheDataModel()
            {
                CachedResponse = value,
                Region = region,
                Timeout = DateTime.Now.Add(ttl)
            };
        }

        /// <summary>
        /// 清除缓存的数据
        /// </summary>
        /// <param name="region"></param>
        public void ClearRegion(string region)
        {
            this._logger.LogWarning($"This is {nameof(CustomCacheExtend)}.{nameof(ClearRegion)}");

            var keyList = CustomCacheExtendDictionary.Where(kv => kv.Value.Region.Equals(region)).Select(kv => kv.Key);
            foreach (var key in keyList)
            {
                CustomCacheExtendDictionary.Remove(key);
            }
        }

        public CachedResponse Get(string key, string region)
        {
            this._logger.LogWarning($"This is {nameof(CustomCacheExtend)}.{nameof(Get)}");
            if (CustomCacheExtendDictionary.ContainsKey(key) && CustomCacheExtendDictionary[key] != null
                && CustomCacheExtendDictionary[key].Timeout > DateTime.Now
                && CustomCacheExtendDictionary[key].Region.Equals(region))
            {
                return CustomCacheExtendDictionary[key].CachedResponse;
            }
            else
                return null;
        }
    }


    public class CacheDataModel
    {
        public CachedResponse CachedResponse { get; set; }
        public DateTime Timeout { get; set; }
        public string Region { get; set; }
    }
}

 Program里添加

builder.Services.AddSingleton<IOcelotCache<CachedResponse>, CustomCacheExtend>();

AddandDelete和ClearRegion需要你自己调用,用于清除脏数据

Add方法在Get方法返回null调用。

扩展自定义轮询

public class CustomPollingLoadBalancer : ILoadBalancer
{
    /// <summary>
    /// 通过注入的一段逻辑---就是在IOC注册的时候, 指定的策略的规则
    /// </summary>
    private readonly Func<Task<List<Service>>> _DownstreamServicesTaskList;

    /// <summary>
    /// 锁
    /// </summary>
    private readonly object CustomPollingLoadBalancer_Lock = new object();
    private int _lastIndex;

    //内部自己注册的ioc的
    public CustomPollingLoadBalancer(Func<Task<List<Service>>> services)
    {
        this._DownstreamServicesTaskList = services;
    }


    /// <summary>
    /// 决定负载均衡的策略
    /// </summary>
    /// <param name="httpContext"></param>
    /// <returns></returns>
    /// <exception cref="NotImplementedException"></exception>
    public async Task<Response<ServiceHostAndPort>> Lease(HttpContext httpContext)
    {

        ///downstreamServices: 服务列表
        var downstreamServices = await this._DownstreamServicesTaskList();
        lock (CustomPollingLoadBalancer_Lock)
        {
            Console.WriteLine($"This is {nameof(CustomPollingLoadBalancer)}.Lease");
            Console.WriteLine($"This is {httpContext.Request.Host.Value}");
            Console.WriteLine($"This is {string.Join(",", downstreamServices.Select(s => s.HostAndPort.DownstreamHost + s.HostAndPort.DownstreamPort))}");


            if (_lastIndex >= downstreamServices.Count)
            {
                _lastIndex = 0;
            } 

            var next = downstreamServices[_lastIndex];
            _lastIndex++;
            return new OkResponse<ServiceHostAndPort>(next.HostAndPort);
        }
    }

    public void Release(ServiceHostAndPort hostAndPort)
    {
       
    }
}

Program.cs里添加 

#region IOC扩展负载均衡策略
Func<IServiceProvider, DownstreamRoute, IServiceDiscoveryProvider, CustomPollingLoadBalancer> loadBalancerFactoryFunc = (serviceProvider, Route, serviceDiscoveryProvider) => new CustomPollingLoadBalancer(serviceDiscoveryProvider.Get);
#endregion



builder.Services.AddOcelot()
    //注意: nuget引入: Ocelot.Provider.Consul
    .AddConsul() //Ocelot注册到IOC容器
                 //扩展 Ocelot-缓存
                 //Nuet: Ocelot.Cache.CacheManager
    .AddCacheManager(x =>
    {
        x.WithDictionaryHandle();//字典缓存
        //如果我自己来扩展下, 可能会使用redis  Mongo  memaryCache 
    })
    .AddCustomLoadBalancer<CustomPollingLoadBalancer>(loadBalancerFactoryFunc);  //配置生效自定义负载均衡策略

 

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容