暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

微服务之Ocelot网关实战

码农游乐场 2021-09-08
869

上一篇大概介绍了下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"//负载均衡
}

可用的负载均衡器类型为:

  1. LeastConnection-跟踪哪些服务正在处理请求,并将新请求发送到具有最少现有请求的服务。算法状态没有分布在Ocelot集群中。

  2. RoundRobin-遍历可用服务并发送请求。算法状态没有分布在Ocelot集群中。

  3. NoLoadBalancer-从配置或服务发现中获取第一个可用服务。

  4. 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测试

  1. 访问api01服务的接口【http://localhost:5000/api01/api/Default】 


  2. 访问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等。

微信识别二维码关注

相关阅读推荐

  1. 微服务2.0版本

  2. 微服务之Consul服务自动注册

  3. Ocelot是什么?微服务之Ocelot简单介绍

文章转载自码农游乐场,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论