在上篇.Net微服务实践(三)[网关]:Ocelot配置路由和请求聚合中我们介绍了Ocelot的配置,主要特性路由以及服务聚合。接下来,我们会介绍Ocelot的限流、熔断、缓存以及负载均衡。
限流
我们先来看限流的配置
Reroute节点中的配置如下:
{
"DownstreamPathTemplate": "/api/orders",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"UpstreamPathTemplate": "/api/orders",
"UpstreamHttpMethod": [ "Get" ],
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "10m",
"PeriodTimespan": 3,
"Limit": 1
}
}
GlobalConfiguration中的配置如下:
"GlobalConfiguration": {
"BaseUrl": "http://localhost:5000",
//限流
"RateLimitOptions": {
"QuotaExceededMessage": "您的请求量超过了配额1/10分钟",
"HttpStatusCode": 999
}
}
配置说明
在Reroute和GlobalConfiguration节点中添加了RateLimitOptions节点
- ClientWhitelist - 白名单,也就是不受限流控制的客户端
- EnableRateLimiting - 是否开启限流
- Period & Limit - 在一段时间内允许的请求次数
- PeriodTimespan - 客户端的重试间隔数,也就是客户端间隔多长时间可以重试
- QuotaExceededMessage - 限流以后的提示信息
- HttpStatusCode - 超出配额时,返回的http状态码
示例说明
客户端在10分钟之内只允许请求一次http://localhost:5000/api/orders,在请求之后3秒钟之后可以重试
验证
修改配置,运行示例程序,
访问http://localhost:5000/api/orders,第一次可以正常获取返回结果,再次访时,显示"您的请求量超过了配额1/10分钟, 并且response状态码是999
PeriodTimespan的验证
修改Period为1s, 修改PeriodTimespan为10,这样当前的配置是1秒中允许一个请求,10秒后才能重试。 再次运行示例程序。
访问http://localhost:5000/api/orders,第一次可以正常获取返回结果, 等待两秒,再次访问,大家想一下,这个时候,会不会返回正常结果(已经过了两秒)。这时还是返回999,为什么? 因为尽管配额上是允许的,但是因为配置是客户端10秒以后才能重试,而这时只等待了2秒,所以还是返回999.
熔断
Ocelot的熔断使用了Polly来实现,在OcelotGateway项目添加Polly包
注入Polly
services
.AddOcelot()
.AddPolly();
修改配置
{
"DownstreamPathTemplate": "/api/orders",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"UpstreamPathTemplate": "/api/orders",
"UpstreamHttpMethod": [ "Get" ],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 2,
"DurationOfBreak": 5000,
"TimeoutValue": 2000
}
}
配置说明
在Reroute节点中添加了QoSOptions节点
- ExceptionsAllowedBeforeBreaking - 在熔断之前允许的异常次数
- DurationOfBreak - 熔断时长 ,单位毫秒
- TimeoutValue - 请求超时设置, 单位毫秒
示例说明
当访问http://localhost:5000/api/orders出现2次异常后,服务熔断5秒,如果服务响应超过2秒,也触发熔断条件
验证
场景一:服务宕机
修改配置,只启动网关,不启动oder api,访问http://localhost:5000/api/orders,第一次有响应耗时,返回500,第二次也有响应耗时,返回500. 第三次则快速返回503 Service Unavalible, 服务熔断了。场景二:超时
修改配置
修改api/orders代码,等待3秒
// GET: api/orders
[Route("api/orders")]
[HttpGet]
public IEnumerable<string> Get()
{
Task.Delay(3000).Wait();
return new string[] { "刘明的订单", "王天的订单" };
}
启动网关,启动order-api,访问http://localhost:5000/api/orders,返回503
- 场景三:服务正常响应,但是服务500 内部错误
修改配置
修改api/orders代码,抛出异常
// GET: api/orders
[Route("api/orders")]
[HttpGet]
public IEnumerable<string> Get()
{
throw new Exception("获取所有订单出错");
}
启动网关,启动order-api,访问http://localhost:5000/api/orders, 不触发熔断
缓存
缓存使用了CacheManageer来实现,添加CacheManager包
Install-Package Ocelot.Cache.CacheManager
注入缓存组件
services.AddOcelot()
.AddCacheManager(x =>
{
x.WithDictionaryHandle();
});
Ocelot.json配置文件修改
{
"DownstreamPathTemplate": "/api/orders",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"UpstreamPathTemplate": "/api/orders",
"UpstreamHttpMethod": [ "Get" ],
"FileCacheOptions": {
"TtlSeconds": 60,
"Region": "orders"
}
}
缓存是根据 downstream service 的URL来缓存的
配置说明
在Reroute节点中添加了FileCacheOptions节点
- TtlSeconds - 缓存有效期,单位是秒
- Region - 缓存分区, 可以通过调用后台Api 来清空一个region下的缓存
示例说明
当访问http://localhost:5000/api/orders后,结果会缓存60秒,在缓存有效期内即使原始的order api的返回结果发生变化,通过网关请求时,还是会返回缓存的结果。
验证
- 修改配置,只启动网关,启动oder api,访问http://localhost:5000/api/orders,返回的结果如下
"刘明的订单", "王天的订单"
- 修改api/orders, 重新启动api/orders
[Route("api/orders")]
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "帅的订单", "我的订单" };
}
- 再次访问http://localhost:5000/api/orders, 这个时候是会返回什么结果呢?, 验证显示还是返回
"刘明的订单", "王天的订单"
因为结果被缓存了
- 等待2分钟,再访问http://localhost:5000/api/orders,这个时候是会返回什么结果呢?, 验证显示返回了新的结果
"帅的订单", "我的订单"
因为缓存有效期已经过了
Header转化
Ocelot允许在上游服务的request和下游服务的response的header中添加、替换信息
配置如下:
{
"DownstreamPathTemplate": "/api/shopping-carts",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"DownstreamHeaderTransform": {
"devops": "rdc"
},
"UpstreamPathTemplate": "/api/shopping-carts",
"UpstreamHttpMethod": [ "Get" ],
"UpstreamHeaderTransform": {
"lakin": "rdc",
"CI": "msbuild, jenkins",
"Location": "http://localhost:5001, {BaseUrl}"
}
}
配置说明
在Reroute节点中添加了DownstreamHeaderTransform节点和UpstreamHeaderTransform节点
"DownstreamHeaderTransform": {
"devops": "rdc"
}
说明:在下游服务的response中添加一个header, key是devops, value是rdc
"UpstreamHeaderTransform": {
"lakin": "rdc",
"CI": "msbuild, jenkins",
"Location": "http://localhost:5001, {BaseUrl}"
}
- 添加header信息
在上游服务的request中添加一个header, key是lakin, value是rdc - 替换header信息
在上游服务的request中, 将key是CI的header, 其值由msbuild替换为jenkins - 替换时使用placeholder
在上游服务的request中, 将key是Location的header, 其值由http://localhost:5001替换为{BaseUrl} placehokder
示例说明
当访问http://localhost:5000/api/orders后,结果会缓存60秒,在缓存有效期内即使原始的order api的返回结果发生变化,通过网关请求时,还是会返回缓存的结果。
验证
- 修改Order Service, 添加一个ShoppingCart api, 代码如下
// GET: api/shopping-carts
[Route("api/shopping-carts")]
[HttpGet]
public IEnumerable<string> Get()
{
Console.WriteLine($"开始打印header信息");
foreach (var item in this.Request.Headers)
{
Console.WriteLine($"{item.Key} - {item.Value}");
}
Console.WriteLine($"打印header信息完成");
return new string[] { "洗发水", "无人机" };
}
- 启动网关、启动Order Service
- 使用Postman调用http://localhost:5000/api/shopping-carts, 在request中添加两个header项
"CI": "msbuild",
"Location": "http://localhost:5001"
- 发起请求
- 在Order Service的控制台,可以看到如下输出, 添加一个header项,替换两个header项的值
开始打印header信息CI
lakin - rdc
CI - jenkins
Location - http://localhost:5000
打印header信息完成
- 检查Postman中response的header信息,会发现添加了如下的header项
devops - rdc
HTTP方法转换
Ocelot允许在路由时转化HTTP方法
{
"DownstreamPathTemplate": "/api/shopping-carts",
"DownstreamScheme": "http",
"DownstreamHttpMethod": "POST",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
}
],
"UpstreamPathTemplate": "/api/shopping-carts",
"UpstreamHttpMethod": [ "Get" ]
}
示例说明
上述示例中,将GET /api/shopping-carts 路由到 POST /api/shopping-carts, 将GET转换成了POST
适用场景:例如有些已经存在的的API,因为某些历史原因都是用POST,在通过网关对外提供服务时,就可以按照标准API进行转换
验证
- 当前GET /api/shopping-carts返回如下结果
"洗发水", "无人机"
- 我们在ShoppingCartController中添加一个新的POST API
[Route("api/shopping-carts")]
[HttpPost]
public string Post()
{
return "添加商品到购物车成功";
}
- 运行网关和Order Service
- 访问GET http://localhost:5000/api/shopping-carts, 返回的结果如下,已经将GET转换成了POST
添加商品到购物车成功
负载均衡
Ocelot内置了负载均衡,我们先来看配置
{
"DownstreamPathTemplate": "/api/orders",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 5001
},
{
"Host": "localhost",
"Port": 6001
}
],
"UpstreamPathTemplate": "/api/orders",
"UpstreamHttpMethod": [ "Get" ],
"LoadBalancerOptions": {
"Type": "RoundRobin"
}
}
配置说明
在DownstreamHostAndPorts指指定多个服务地址
在Reroute节点中添加LoadBalancerOptions,这是负载均衡的配置节点,其中Type属性指定了负载均衡的算法, 它有如下几个值:
- LeastConnection – 将请求发往最空闲的那个服务器
- RoundRobin – 轮流发送
- NoLoadBalance – 总是发往第一个请求(如果配置了服务发现,则总是发往发现的第一个服务地址)
验证
- 启动网关
- 修改api/orders的代码,并启动Order Service
[Route("api/orders")]
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "刘明的订单", "王天的订单" };
}
- 拷贝一份Order Service的副本,修改api/orders的代码,修改副本的启动端口为6001, 并启动Order Service
[Route("api/orders")]
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "帅的订单", "我的订单" };
}
- 在Postman中调用GET http://localhost:5000/api/orders
第一次的结果是
"刘明的订单", "王天的订单"
第二次的结果是
"帅的订单", "我的订单"
第三次的结果是
"刘明的订单", "王天的订单"
注入/重写中间件
Ocelot本身是一组中间件,它也提供了方式来注入和重写其中的某些中间件:
- PreErrorResponderMiddleware - 在所有Ocelot中间件之前运行, 允许用户在Ocelot管道运行之前和运行之后提供任何行为。
- PreAuthenticationMiddleware - 提供预身份认证逻辑,在身份认证中间件之前运行
- AuthenticationMiddleware - 覆写Ocelot中间件提供的身份认证逻辑
- PreAuthorisationMiddleware - 提供预授权逻辑,在授权中间件之前运行
- AuthorisationMiddleware - 覆写Ocelot中间件提供的授权逻辑
- PreQueryStringBuilderMiddleware - 在QueryString转换之前,提供预处理逻辑
下面是注入PreErrorResponderMiddleware中间件的代码示例:
//注入中间件
var configuration = new OcelotPipelineConfiguration
{
PreErrorResponderMiddleware = async (ctx, next) =>
{
ctx.HttpContext.Request.Headers.Add("myreq", "ocelot-request");
await next.Invoke();
}
};
app.UseOcelot(configuration).Wait();
注意: Ocelot也是一组中间件,所以可以在Ocelot中间件之前,按常规方式添加任何中间件, 但是不能在Ocelot中间件之后添加,因为Ocelot没有调用 next
后台管理
Ocelot提供了一组后台管理的API, 从前三篇文章可以看出,Ocelot主要也就是配置文件的管理,所以API主要也就是管理配置
- 获取管理后台的token
POST {adminPath}/connect/token - 获取配置
GET {adminPath}/configuration - 创建/修改配置
POST {adminPath}/configuration - 删除缓存
DELETE {adminPath}/outputcache/{region}
最后
本篇我们介绍了Ocelot的限流、熔断、缓存、负载均衡以及其他一些特性。到目前为止,Ocelot的基本配置和功能都已经介绍完了。接下里我们会结合consul来介绍服务发现,以及Ocelot和Consul的集成。.Net微服务实践(五)[服务发现]:Consul介绍和环境搭建
示例代码下载地址: https://github.com/lcyhjx/ocelot-demo/tree/master
.Net微服务实践(四)[网关]:Ocelot限流熔断、缓存以及负载均衡的更多相关文章
-
BeetleX服务网关之限流和缓存
限流和缓存相关是网关中两个非常重要的功能,前者是保障服务更可靠地运行,后者则可以大大提高应用的吞吐能力.Beetlex.Bumblebee微服务网关提供了两个扩展插件来实现这两个功能,分别是Beetl ...
-
spring cloud微服务实践四
spring cloud的hystrix还有一个配搭的库hystrix-dashboard,它是hystrix的一款监控工具,能直观的显示hystrix响应信息,请求成功率等.但是hystrix-da ...
-
.Net微服务实践(三):Ocelot配置路由和请求聚合
目录 配置 路由 基本配置 占位符 万能模板 优先级 查询参数 请求聚合 默认聚合 自定义聚合 最后 在上篇.Net微服务实践(二):Ocelot介绍和快速开始中我们介绍了Ocelot,创建了一个Oc ...
-
.Net微服务实践(五)[服务发现]:Consul介绍和环境搭建
目录 介绍 服务发现 健康检查.键值存储和数据中心 架构 Consul模式 环境安装 HTTP API 和Command CLI 示例API介绍 最后 在上篇.Net微服务实践(四)[网关]:Ocel ...
-
.Net微服务实践(二):Ocelot介绍和快速开始
目录 介绍 基本原理 集成方式 快速开始 创建订单服务 创建产品服务 创建网关 运行验证 最后 上篇.Net微服务实践(一):微服务框架选型 我们对微服务框架整体做了介绍,接下来我们从网关Ocelot ...
-
.net 微服务实践
l 前言 本文记录了我的一次.net core 微服务架构实践经验,以及所用到的技术 l 优点 每个服务聚焦于一块业务,无论在开发阶段或是部署阶段都是独立的,更适合被各个小团队开发维护,团队对服务 ...
-
AspNetCore微服务下的网关-Kong(一)
Kong是Mashape开源的高性能高可用API网关和API服务管理层.它基于OpenResty,进行API管理,并提供了插件实现API的AOP.Kong在Mashape 管理了超过15,000 个A ...
-
QCon技术干货:个推基于Docker和Kubernetes的微服务实践
2016年伊始,Docker无比兴盛,如今Kubernetes万人瞩目.在这个无比需要创新与速度的时代,由容器.微服务.DevOps构成的云原生席卷整个IT界.在近期举办的QCon全球软件开发大会上, ...
-
微服务实践(七):从单体式架构迁移到微服务架构 - DockOne.io
原文:微服务实践(七):从单体式架构迁移到微服务架构 - DockOne.io [编者的话]这是用微服务开发应用系列博客的第七篇也是最后一篇.第一篇中介绍了微服务架构模式,并且讨论了微服架构的优缺点: ...
随机推荐
-
简析.NET Core 以及与 .NET Framework的关系
简析.NET Core 以及与 .NET Framework的关系 一 .NET 的 Framework 们 二 .NET Core的到来 1. Runtime 2. Unified BCL 3. W ...
-
WEB用户访问控制方法
分享到 一键分享 QQ空间 新浪微博 百度云收藏 人人网 腾讯微博 百度相册 开心网 腾讯朋友 百度贴吧 豆瓣网 搜狐微博 百度新首页 QQ好友 和讯微博 更多... 百度分享 一直以来,我对用户/权 ...
-
Java创建Oracle数据库表
我们通常只用java执行DML(即:insert, update, delete, select)操作,很少用来执行DDL(create, drop, alert)操作.今天试了下如何用java来创建 ...
-
eclipse svn不显示提交人、提交时间的问题
最近eclipse换到最新的mars版本,装了svn插件subversive后,拉下项目后发现都不显示提交人了,以前都不这样的呀,新版不一样了么? 后来网上搜索总算找到方法解决: Window--&g ...
-
bzoj4540 序列 (单调栈+莫队+rmq)
首先,如果我知道[l,r],要转移到[l,r+1]的时候,就是加上以r+1为右端点,[l,r+1]为左端点的区间的最小值,其他情况和这个类似 考虑这玩意怎么求 右端点固定的话,我左端点越往左走,这个最 ...
-
keepalived高可用系列~通用基础
简介:今天咱们来聊聊keepalived一 keepalived 架构 1 标准架构: keepalived+lvs/haproxy+后端 real server(mysql从库,nginx.myc ...
-
新建 django 项目
安装 django ,就不必多说,python 环境是 python 3.6,django 安装的命令为: pip3 install django==2.1.7 开始demo,名字为 guest dj ...
-
HDU-1004.Let the ballon Rise(STL-map)
2019-02-28-08:56:03 初次做本题是用字符串硬钢,最近校队训练时又遇到才知道用map是真的舒服.需要注意的是map的用法. clear : 清除map中的所有元素,map.clear( ...
-
ubuntu双网卡准备配置
近日有个需求,交换机有两台,做了堆叠,服务器双网卡,每个分别连到一台交换机上.这样就需要将服务器的网卡做成主备模式,以增加安全性,使得当其中一个交换机不通的时候网卡能够自动切换. 整体配置不难,网上也 ...
-
Hibernate学习---第七节:关联关系
一.关联关系一对一外键(双向) 1.实体类,代码如下: package learn.hibernate.bean; import java.util.Date; /** * 持久化类设计 * 注意: ...