上一篇大概介绍了下Ocelot是什么,微服务分布式部署是一个趋势,那么多的服务之间调用关系怎么维护,当服务很多的时候,这个关系网就如同蜘蛛网,很难维护。那么网关可以对外提供统一的入口,客户端服务不需要维护很多地址,这就简单了。
Ocelot能帮我们做什么
路由(ReRoutes):主要功能是接收传入的HTTP请求,并将其转发到下游服务,当前仅以一个http请求的形式支持此功能(将来可能是任何传输机制)。
聚合(Aggregate):可以把下游的多个服务的响应映射到一个对象当中。
服务发现(Consul):允许您指定服务发现提供程序,并将使用它来查找Ocelot将请求转发到的下游服务的主机和端口,目前支持Consul。
限速:支持上游请求的速率限制,以使您的下游服务不会过载。
缓存:目前支持CacheManager项目提供的一些非常基本的缓存。
服务质量:当前支持一种QoS功能。如果要在向下游服务发出请求时使用断路器,则可以基于每个重新路由进行设置。
负载均衡:可以为每个重新路由在可用的下游服务之间实现负载平衡(LeastConnection,RoundRobin,NoLoadBalancer,CookieStickySessions)。
Kubernetes:将在给定的命名空间中调用k8s端点API,以获取pod的所有端点,然后在它们之间进行负载平衡。
认证方式:支持JWT Tokens,dentity Server Bearer Tokens,Okta等。
内部管理API来管理。
http方法转换:Ocelot允许用户更改向下游服务发出请求时将使用的HTTP请求方法。
其实功能还挺多的,我这里并没有完全写出来,此时此刻,是不是很想做一个网关呢?
实践
首先我们还是基于上一篇Consul服务中心做,WebApi项目就用之前的那个作为示例。
新建一个CustomMiddleware.Gateway的空api项目
引入Oclot、Ocelot.Provider.Consul包,Core 3.1支持15.0.6版
PM> Install-Package Ocelot -Version 15.0.6
因为我们这里用到了Consul来做服务自动发现,所以必须引入 Ocelot.Provider.Consul
PM> Install-Package Ocelot.Provider.Consul -Version 15.0.6
新建一个configuration.json
{
"ReRoutes": [
{
"UseServiceDiscovery": true, 启用服务发现
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"ServiceName": "Api01",//服务名称
"LoadBalancerOptions": {
"Type": "RoundRobin"//负载均衡
},
"UpstreamPathTemplate": "/Api01/{url}",
"UpstreamHttpMethod": [ "Get", "Post" ],
"ReRoutesCaseSensitive": false
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Host": "192.168.10.134", Consul client IP
"Port": 8500, Consul HTTP API端口
"Type": "Consul"
}
}
}
修改Program类,把自定义的配置文件注入进来
public static IWebHostBuilder CreateHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args).UseStartup<Startup>()
.ConfigureAppConfiguration(conf =>
{
conf.AddJsonFile("configuration.json", false, true);
});
修改Startup类
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot(Configuration)
.AddConsul();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseOcelot();
}
项目最终呈现,非常之简单,因为我把不需要的都删掉了。
从上面看得出来,我把其他不用的代码全部删除了,项目还是可以正常运行的,只是作为一个单纯的网关。
服务发现&负载均衡
运行项目,进入到publish目录,输入命令。
dotnet CustomMiddleware.Gateway.dll --urls="http://*:5000/"
我们在Postman中输入【http://localhost:5000/api01/api/default】接口。
服务发现
我们并没有在路由配置中加上下游的地址,为什么会找得到对应的下游接口呢?却返回了正确的结果。其实,我在GlobalConfiguration加上了服务发现的配置。
"ServiceDiscoveryProvider": {
"Host": "192.168.10.134", //Consul client IP
"Port": 8500, //Consul HTTP API端口
"Type": "Consul"
}
负载均衡器
其次我们发现返回的结果会轮询consul上的服务节点。其实也是在配置文件中已经加入了负载均衡器。
"LoadBalancerOptions": {
"Type": "RoundRobin"//负载均衡
}
可用的负载均衡器类型为:
LeastConnection-跟踪哪些服务正在处理请求,并将新请求发送到具有最少现有请求的服务。算法状态没有分布在Ocelot集群中。
RoundRobin-遍历可用服务并发送请求。算法状态没有分布在Ocelot集群中。
NoLoadBalancer-从配置或服务发现中获取第一个可用服务。
CookieStickySessions-使用cookie将所有请求粘贴到特定服务器。
限流
对请求进行限流可以防止下游服务器因为访问过载而崩溃,我们只需要在路由下加一些简单的配置即可以完成。
修改配置文件,可以对每个服务进行如下配置:
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "1m",
"PeriodTimespan": 1000.0,
"Limit": 5
}
同时,我们可以做一些全局配置:
"RateLimitOptions": {
"DisableRateLimitHeaders": false, // Http头部 X-Rate-Limit 和 Retry-After 是否禁用
"QuotaExceededMessage": "Too many requests, are you OK?", // 当请求过载被截断时返回的消息
"HttpStatusCode": 999, // 当请求过载被截断时返回的http status
"ClientIdHeader": "client_id" // 用来识别客户端的请求头,默认是 ClientId
}
重新运行网关项目,访问刚才那个接口,连续请求几次。
我们可以看到,当1分钟之内请求超过5次,自动返回999,提示用户访问太频繁了,起到了保护底层服务的功能。
缓存
安装依赖Ocelot.Cache.CacheManager
PM>Install-Package Ocelot.Cache.CacheManager -Version 15.0.6
在服务路由配置中加入以下配置
TtlSeconds:10秒内是同样的请求,会从缓存中读取
"FileCacheOptions": { "TtlSeconds": 10, "Region": "somename" }
修改ConfigureServices
services.AddOcelot(Configuration)
.AddCacheManager(x =>
{
x.WithDictionaryHandle();
})
.AddConsul();
重新启动项目,打开Postman进行测试
我们发现10秒内返回的内容都是一样的,并没有实实在的去请求接口,这就是缓存的作用了。
熔断器(QoS)
熔断的意思是停止将请求转发到下游服务。当下游服务已经出现故障的时候再请求也是无功而返,并且还会增加下游服务器和API网关的负担。这个功能是用的Polly来实现的,我们只需要为路由做一些简单配置即可。
安装Polly
PM>Install-Package Ocelot.Provider.Polly -Version 15.0.6
修改配置文件,在服务中加上以下配置
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,//允许多少个异常请求
"DurationOfBreak": 5000,//熔断的时间,单位为毫秒
"TimeoutValue": 3000 //如果下游请求的处理时间超过多少则视该请求超时
}
修改ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddOcelot(Configuration)
.AddCacheManager(x =>
{
x.WithDictionaryHandle();
})
.AddConsul()
.AddPolly();
}
在Api01的项目中增加一个接口用来测试熔断,如下所示:
[HttpGet("TestQos")]
public ResultInfo TestQos()
{
//延时5秒
System.Threading.Thread.Sleep(5000);
ResultInfo result = new ResultInfo();
result.Code = 200;
result.CreateTime = DateTime.Now;
result.Message = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}{HttpContext.Request.Path}";
result.Data = new
{
name = "TestQos",
server = "Api01"
};
return result;
}
网关的配置TimeoutValue是3秒,那么我在接口返回前,简单粗暴的延时个5秒。
重新启动API项目和网关项目
用Postman进行测试,我们看到正在等待返回结果。
3秒超时时间之后,返回了503。
连续点击,发现直接返回了503 Service Unavaliable,其实这时便进入了5秒中的服务不可访问期。休息5秒之后又能访问该接口了。
通过日志也可以确认Ocelot触发了熔断保护:
动态路由
由于不再需要配置ReRoutes,所以我们需要做一些“通用性”的改造,详见下面的GlobalConfiguration:
{
"ReRoutes": [],
"Aggregates": [],
"GlobalConfiguration": {
"RequestIdKey": null,
"ServiceDiscoveryProvider": {
"Host": "192.168.10.134", // Consul Service IP
"Port": 8500// Consul Service Port
},
"RateLimitOptions": {
"DisableRateLimitHeaders": false, // Http头部 X-Rate-Limit 和 Retry-After 是否禁用
"QuotaExceededMessage": "Too many requests, are you OK?", // 当请求过载被截断时返回的消息
"HttpStatusCode": 999, // 当请求过载被截断时返回的http status
"ClientIdHeader": "client_id"// 用来识别客户端的请求头,默认是 ClientId
},
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10000,
"TimeoutValue": 5000
},
"BaseUrl": null,
"LoadBalancerOptions": {
"Type": "LeastConnection",
"Key": null,
"Expiry": 0
},
"DownstreamScheme": "http",
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
"UseCookieContainer": false,
"UseTracing": false
}
}
}
在配置文件中我们并没有设定路由,而是让ocelot动态分配,接下来测试下结果。
启动网关测试
先启动网关项目,然后我们启动Api02项目,那么此时Consul中肯定就有两个服务了,如下所示:
Postman测试
访问api01服务的接口【http://localhost:5000/api01/api/Default】
访问api02服务的接口【http://localhost:5000/api02/api/Factory】
以上结果证实了路由是动态分配的,在不需要修改网关配置的情况下,自动发现了注册上来的服务。大功告成。
Ocelot还允许您设置DynamicRoutes,从而可以为每个下游服务设置速率限制规则。例如,如果您有产品和搜索服务,并且希望对两者进行最高限价,这将非常有用。一个例子如下
{
"DynamicReRoutes": [
{
"ServiceName": "api02",
"RateLimitRule": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "1m",
"PeriodTimespan": 30,
"Limit": 2
}
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Host": "192.168.10.134",
"Port": 8500,
"Type": "Consul"
},
"LoadBalancerOptions": {
"Type": "LeastConnection",
"Key": null,
"Expiry": 0
},
"RateLimitOptions": {
"DisableRateLimitHeaders": true, Http头部 X-Rate-Limit 和 Retry-After 是否禁用
"QuotaExceededMessage": "Too many requests, are you OK?", 当请求过载被截断时返回的消息
"HttpStatusCode": 999, // 当请求过载被截断时返回的http status
"ClientIdHeader": "client_id" // 用来识别客户端的请求头,默认是 ClientId
},
"DownstreamScheme": "http"
}
}
Ocelot还有很多其他功能,比如上面提到的鉴权、API管理、Kubernetes等。
微信识别二维码关注
相关阅读推荐