转换 Headers
Ocelot 允许在请求下游服务之前和之后转换头部.目前 Ocelot 只支持查找和替换.这个功能在 Github #190 提出.我确定这个功能可以在各个方面发挥作用。
添加到请求
这个功能在 GitHub #313 被提出。
如果你想在你的上游请求中添加一个头,请在 ocelot.json 文件的 ReRoute 中添加如下配置:
"UpstreamHeaderTransform": {
"Uncle": "Bob"
}
上面例子中,一个键为 Uncle,值为 Bob 的头将被添加到上游服务中。
也支持占位符(看下面)。
添加到相应
这个功能在 GitHub #280 被提出。
如果你想在你的下游响应中添加一个头,请在 ocelot.json 文件的 ReRoute 中添加如下配置:
"DownstreamHeaderTransform": {
"Uncle": "Bob"
},
上面例子中,当请求一个特定 ReRoute 的时候,Ocelot 将返回一个键为 Uncle,值为 Bob 的头。
如果你想返回 Butterfly 跟踪 id,需要像下面这样...
"DownstreamHeaderTransform": {
"AnyKey": "{TraceId}"
},
查找并替换
为了变换头,首先我们指定头的键,然后指定我们想要的变换内容,例如
"Test": "http://www.bbc.co.uk/, http://ocelot.com/"
下游请求之前
在 ocelot.json 的 ReRoute 中添加如下配置 ,以便用 http://ocelot.com/替换 http://www.bbc.co.uk/ .Test 头将被替换并发送到下游服务.
"UpstreamHeaderTransform": {
"Test": "http://www.bbc.co.uk/, http://ocelot.com/"
},
下游请求之后
在 ocelot.json 的 ReRoute 中添加如下配置 ,以便用 http://ocelot.com/替换 http://www.bbc.co.uk/ . 当 Ocelot 收到下游服务响应之后将进行替换.
"DownstreamHeaderTransform": {
"Test": "http://www.bbc.co.uk/, http://ocelot.com/"
},
占位符
Ocelot 允许在头不转换中使用占位符.
{BaseUrl} - 这个是 Ocelot 的基本 url. 例如 http://localhost:5000/. {DownstreamBaseUrl} - 这个是下游服务的基本 url 例如 http://localhost:5001/. 目前这个只在 DownstreamHeaderTransform 中起作用. {TraceId} - 这个是 Butterfly 的跟踪 id.目前这个也只在 DownstreamHeaderTransform 中起作用.
处理 302 重定向
Ocelot 默认会自动遵循重定向,但是如果您想将 location 头返回给客户端,您可能需要将 location 更改为 Ocelot 而不是下游服务。 Ocelot 可以使用以下配置实现。
"DownstreamHeaderTransform": {
"Location": "http://www.bbc.co.uk/, http://ocelot.com/"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
你也可以使用 BaseUrl 占位符.
"DownstreamHeaderTransform": {
"Location": "http://localhost:6773, {BaseUrl}"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
最后,如果你使用负载均衡的话,你将得到多个下游基地址,所以像上面那样是不能正常工作的.在这种情况下你可以如下配置.
"DownstreamHeaderTransform": {
"Location": "{DownstreamBaseUrl}, {BaseUrl}"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
未来
理想情况下,这个特性能够支持实际上头部可以有多个值的情况。目前只是假设一个。 如果它可以查找和替换多个值应该是非常棒的。
"DownstreamHeaderTransform": {
"Location": "[{one,one},{two,two}"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
如果有人想在这一展身手,请自己搞定吧!
转换 Claims
Ocelot 允许用户访问 claims 并把它们转换到头部,请求字符串参数和其他 claims 中.这仅在用户通过身份验证后才可用。
用户通过身份验证之后,我们运行 claims 转换中间件.这个中间件允许在授权中间件调用之前转换 claims.当用户身份验证之后,首先会调用 claims 转换到头的中间件,最后调用 claims 转换到查询字符串的中间件.
执行转换的语法对于每个处理都是相同的。在 ReRoute 配置中,使用特定名称 AddClaimsToRequest,AddHeadersToRequest,AddQueriesToRequest 添加一个 json 字典。
注意,我不是一个编程专家,所以不知道这个语法是否好...
在词典中,这些条目指定了 Ocelot 应该如何转换! 字典的键将成为 claim,头,查询参数的键。
条目的值将被解析成转换的逻辑.首先指定了一个字典访问器,例如 Claims[CustomerId].意思是我们想访问 claims 并获取键为 CustomerId 的 claim 类型.然后一个大于号(>)用于分隔.下一个条目是值或带索引器的值.如果指定了单个值,Ocelot 将取该值并将其添加到变换中。如果该值有一个索引器,Ocelot 将查找在另一个大于符号后面提供的分隔符。 然后,Ocelot 会使用分隔符将值分开,并将所需的索引添加到转换中。
Claims 到 Claims 的转换
下面是一个 Claims 到 Claims 转换的例子
"UserType": "Claims[sub] > value[0] > |",
"UserId": "Claims[sub] > value[1] > |"
}
这显示了 Ocelot 查看用户的 sub 声明并将其转换为 UserType 和 UserId 声明的转换。 假设 sub 声明看起来像这样“usertypevalue | useridvalue”。
Claims 到头的转换
下面是一个 Claims 到头转换的例子
"AddHeadersToRequest": {
"CustomerId": "Claims[sub] > value[1] > |"
}
这显示了 Ocelot 查看用户的 sub 声明并将其转换为 CustomerId 头的转换。 假设 sub 声明看起来像这样“usertypevalue | useridvalue”。
Claims 到查询字符串参数的转换
下面是一个 Claims 到查询字符串参数转换的例子
"AddQueriesToRequest": {
"LocationId": "Claims[LocationId] > value",
}
这显示了 Ocelot 查看用户的 LocationId 声明并将其作为发往下游服务的查询字符串参数 LocationId 的转换。
日志
目前,Ocelot 使用标准的日志记录接口 ILoggerFactory/ILogger 。 在 IOcelotLogger / IOcelotLoggerFactory 中提供了标准的 asp.net core 日志记录的一个实现。 因为 Ocelot 在日志中添加了一些额外的信息,如请求 ID(如果已配置的话)。
这有个全局的错误处理程序,可以捕获所有异常并作为错误记录他们。
最后,如果日志记录设置为跟踪级别,Ocelot 将记录开始,结束和任何抛出异常的中间件,这些异常可能非常有用。
不是使用标准框架的日志记录的原因是,我无法覆盖将 IncludeScopes 设置为 true 时记录的请求标识。
警告
如果您记录日志到控制台,您将获得糟糕的性能。 我遇到过很多关于 Ocelot 性能的问题,它始终记录调试级别的日志,并记录到控制台 :) 所有请确保您生产中记录了正确的东西 : )
跟踪
Ocelot 使用一个杰出的项目 Butterfly 提供了跟踪功能。
为了使用跟踪,请阅读 Butterfly 的文档。
在 Ocelot 中如果你想跟踪一个 ReRoute,你需要做如下事情:
在 ConfigureServices 方法中
services
.AddOcelot()
.AddOpenTracing(option =>
{
//this is the url that the butterfly collector server is running on...
option.CollectorUrl = "http://localhost:9618";
option.Service = "Ocelot";
});
然后在 ocelot.json 文件中,添加如下配置到你想要跟随的 ReRoute 中。
"HttpHandlerOptions": {
"UseTracing": true
},
现在,当这个 ReRoute 被调用的时候,Ocelot 会发生跟踪信息到 Butterfly。
请求 Id 和关联 Id
Ocelot 支持一个客户端以头的形式发送 requestid。 如果设置了,一旦中间件管道中可用,Ocelot 便会使用这个 requestid 进行日志记录。 Ocelot 也会使用指定头将 requireid 转发给下游服务。
如果在日志配置中你设置 IncludeScopes 为 true,你还可以在日志中获取 asp.net core 的请求 id。
为了是用 requestid,有两种选择。
全局
在 ocelot.json 的 GlobalConfiguration 配置块中如下设置。这样所有进入 Ocelot 的请求都会起作用。
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId"
}
我建议使用 GlobalConfiguration,除非你真的需要它是指定 ReRoute 的。
ReRoute
如果你想覆盖全局设置,在 ocelot.json 的特定 ReRoute 中添加如下设置。
"RequestIdKey": "OcRequestId"
一旦 Ocelot 识别出与 ReRoute 对象匹配的请求,它将根据 ReRoute 的配置来设置 requestid。
这可能会导致一下困惑。如果你在 GlobalConfiguration 中设置了 requestid,可能在 ReRoute 被匹配前是一个,匹配后是另一个,因为 requestid 的 key 会变。这是因为设计如此,而且这是我目前能想到的最好的解决方案了。在这种情况下 OcelotLogger 会在日志中记录当前 requestid 和上一个 requestid。
下面的例子是 debug 级别下一个正常请求的日志记录。
dbug: Ocelot.Errors.Middleware.ExceptionHandlerMiddleware[0]
requestId: asdf, previousRequestId: no previous request id, message: ocelot pipeline started,
dbug: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0]
requestId: asdf, previousRequestId: no previous request id, message: upstream url path is {upstreamUrlPath},
dbug: Ocelot.DownstreamRouteFinder.Middleware.DownstreamRouteFinderMiddleware[0]
requestId: asdf, previousRequestId: no previous request id, message: downstream template is {downstreamRoute.Data.ReRoute.DownstreamPath},
dbug: Ocelot.RateLimit.Middleware.ClientRateLimitMiddleware[0]
requestId: asdf, previousRequestId: no previous request id, message: EndpointRateLimiting is not enabled for Ocelot.Values.PathTemplate,
dbug: Ocelot.Authorisation.Middleware.AuthorisationMiddleware[0]
requestId: 1234, previousRequestId: asdf, message: /posts/{postId} route does not require user to be authorised,
dbug: Ocelot.DownstreamUrlCreator.Middleware.DownstreamUrlCreatorMiddleware[0]
requestId: 1234, previousRequestId: asdf, message: downstream url is {downstreamUrl.Data.Value},
dbug: Ocelot.Request.Middleware.HttpRequestBuilderMiddleware[0]
requestId: 1234, previousRequestId: asdf, message: setting upstream request,
dbug: Ocelot.Requester.Middleware.HttpRequesterMiddleware[0]
requestId: 1234, previousRequestId: asdf, message: setting http response message,
dbug: Ocelot.Responder.Middleware.ResponderMiddleware[0]
requestId: 1234, previousRequestId: asdf, message: no pipeline errors, setting and returning completed response,
dbug: Ocelot.Errors.Middleware.ExceptionHandlerMiddleware[0]
requestId: 1234, previousRequestId: asdf, message: ocelot pipeline finished,
中间件注入和重写
警告!请谨慎使用。 如果您在中间件管道中看到任何异常或奇怪的行为,并且正在使用以下任何一种行为。删除它们,然后重试!
当在 Startup.cs 中配置 Ocelot 的时候,可以添加或覆盖中间件。如下所示:
var configuration = new OcelotPipelineConfiguration
{
PreErrorResponderMiddleware = async (ctx, next) =>
{
await next.Invoke();
}
app.UseOcelot(configuration);
};
在上面的例子中,提供的函数将在第一个 Ocelot 中间件之前运行。 这允许用户在 Ocelot 管道运行之前和之后提供他们想要的任何行为。 这意味着你可以打破一切,你开心就好!
用户可以针对以下内容设置功能。
- PreErrorResponderMiddleware - 上面已经解释过了.
- PreAuthenticationMiddleware - 这个允许用户执行预认证逻辑,然后再调用 Ocelot 的认证中间件。
- AuthenticationMiddleware - 可以重写 Ocelot 的认证中间件。
- PreAuthorisationMiddleware - 这个允许用户执行预授权逻辑,然后再调用 Ocelot 的授权中间件。
- AuthorisationMiddleware - 可以重写 Ocelot 的授权中间件。
- PreQueryStringBuilderMiddleware - 这允许用户在传递给 Ocelot 请求创建器之前在 http 请求上处理查询字符串。
很明显,您只能在调用 app.UseOcelot()之前添加中间件,而不能在它之后,因为 Ocelot 不会调用下一个中间件。
负载均衡
Ocelot 能通过可用的下游服务对每个 ReRoute 进行负载平衡。 这意味着您可以扩展您的下游服务,并且 Ocelot 可以有效地使用它们。
可用的负载均衡器的类型是:
LeastConnection - 最少连接,跟踪哪些服务正在处理请求,并把新请求发送到现有请求最少的服务上。该算法状态不在整个 Ocelot 集群中分布。
RoundRobin - 轮询可用的服务并发送请求。 该算法状态不在整个 Ocelot 集群中分布。
NoLoadBalancer - 不负载均衡,从配置或服务发现提供程序中取第一个可用的下游服务。
CookieStickySessions - 使用 cookie 关联所有相关的请求到制定的服务。下面有更多信息。
你必须在你的配置中选择使用哪种负载均衡方式。
配置
下面展示了如何使用 ocelot.json 给一个 ReRoute 设置多个下游服务,并选择 LeadConnection 负载均衡器。这是设置负载均衡最简单的方法。
{
"DownstreamPathTemplate": "/api/posts/{postId}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "10.0.1.10",
"Port": 5000,
},
{
"Host": "10.0.1.11",
"Port": 5000,
}
],
"UpstreamPathTemplate": "/posts/{postId}",
"LoadBalancerOptions": {
"Type": "LeastConnection"
},
"UpstreamHttpMethod": [ "Put", "Delete" ]
}
服务发现
下面展示了如何使用服务发现设置一个 ReRoute,并选择 LeadConnection 负载均衡器。
{
"DownstreamPathTemplate": "/api/posts/{postId}",
"DownstreamScheme": "https",
"UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Put" ],
"ServiceName": "product",
"LoadBalancerOptions": {
"Type": "LeastConnection"
},
"UseServiceDiscovery": true
}
设置此操作时,Ocelot 将从服务发现提供程序查找下游主机和端口,并通过所有可用服务来负载均衡请求。 如果您向服务发现提供程序(consul)添加和删除服务,那么 Ocelot 将遵循这一点,停止调用已被删除的服务并开始调用已添加的服务。
CookieStickySessions
我已经实现了一个非常基本的粘性会话类型的负载平衡器。 它意味着支持的场景是你有一堆不共享会话状态的下游服务器,如果你为其中一台服务器获得多个请求,那么它应该每次都去同一个盒子,否则给用户的会话状态可能不正确。 这个特性在问题#322 中有被提出,尽管用户想要的比只是粘性会话更加复杂 : ),无论如何,我认为这是个不错的功能!
为了设置 CookieStickySessions 负载均衡器,你需要做如下事情。
{
"DownstreamPathTemplate": "/api/posts/{postId}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "10.0.1.10",
"Port": 5000,
},
{
"Host": "10.0.1.11",
"Port": 5000,
}
],
"UpstreamPathTemplate": "/posts/{postId}",
"LoadBalancerOptions": {
"Type": "CookieStickySessions",
"Key": "ASP.NET_SessionId",
"Expiry": 1800000
},
"UpstreamHttpMethod": [ "Put", "Delete" ]
}
LoadBalancerOptions 的 Type 需要是 CookieStickySessions,Key 是您希望用于粘性会话的 cookie 的名称,Expiry 是您希望会话被粘合的时间,以毫秒为单位。 请记住,每次请求都会刷新,这意味着会模仿会话的工作方式(滑动过期--译者注)。
如果您有多个具有相同 LoadBalancerOptions 的 ReRoutes,那么所有这些 ReRoutes 将为随后的请求使用相同的负载均衡器。 这意味着会话将会跨 ReRoute 进行粘合。
请注意,如果您提供多个 DownstreamHostAndPort 或者您正在使用 Consul 等服务发现提供程序,并且返回多个服务,则 CookieStickySessions 将使用循环选择下一个服务器。 目前是硬编码,但可以改变。
委托处理程序
Ocelot 允许用户将委托处理程序添加到 HttpClient 传输中。 这个功能在 github #208 中提出,我确定它会以各种方式被使用。之后我们在 GitHub#264 中进行了扩展。
用法
为了将委托处理程序添加到 HttpClient 传输中,有两件重要的事情要做。
首先,为了创建一个可以用于委托处理程序的类,它必须如下所示。 我们将在 asp.net core 容器中注册这些处理程序,以便您可以将您已注册的其他服务注入到处理程序的构造函数中。
public class FakeHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
//do stuff and optionally call the base handler..
return await base.SendAsync(request, cancellationToken);
}
}
Next you must add the handlers to Ocelot’s container either as singleton like follows..
其次,您必须将处理程序添加到 Ocelot 的容器,要么作为单例注册。
services.AddOcelot()
.AddSingletonDelegatingHandler<FakeHandler>()
.AddSingletonDelegatingHandler<FakeHandlerTwo>()
要么注册为临时的...
services.AddOcelot()
.AddTransientDelegatingHandler<FakeHandler>()
.AddTransientDelegatingHandler<FakeHandlerTwo>()
这两个 Add 方法都有一个名为 global 的参数,它默认为 false。 如果它是 false,那么 DelegatingHandler 需要通过 ocelot.json 设置特定的 ReRoutes(稍后更多)。 如果设置为 true,则它将成为全局处理程序,并将应用于所有 ReRoutes。
例如:
services.AddOcelot()
.AddSingletonDelegatingHandler<FakeHandler>(true)
或者临时注册
services.AddOcelot()
.AddTransientDelegatingHandler<FakeHandler>(true)
最后,如果你想要 ReRoute 指定 DelegatingHandlers 为你的特定 DelegatingHandlers,或全局(稍后会详细介绍)DelegatingHandlers,那么你必须将下面的 json 添加到 ocelot.json 中的特定 ReRoute 中。 数组中的名称必须与您的 DelegatingHandlers 类名匹配,以便 Ocelot 将它们匹配在一起。
"DelegatingHandlers": [
"FakeHandlerTwo",
"FakeHandler"
]
你可以有多个 DelegatingHandlers,他们的运行顺序如下:
- 所有在服务中并且不在 ocelot.json 的 DelegatingHandlers 数组中的全局处理程序按加入的顺序排序。
- 所有非全局的 DelegatingHandlers 以及来自 ocelot.json 的 DelegatingHandlers 数组中的所有全局变量都按照它们在 DelegatingHandlers 数组中的顺序排列。
- 如果启用了跟踪,那么这一步是跟踪 DelegatingHandler(看跟踪文档).
- 如果启用了 QoS,那么这一步是 QoS DelegatingHandler (看服务质文档).
- HttpClient 发生 HttpRequestMessage.
希望其他人会洞悉这个功能是很有用的!
Raft(实验功能不能用于生产环境)
Ocelot 最近整合了 Rafty,这是我在去年一直研究的 Raft 的一个实现。 这个项目实验性非常强,所以在我认为它没问题之前,请不要在生产环境中使用 Ocelot 的这个功能。
Raft 是一种分布式一致性算法,它允许一组服务器(Ocelots)保持本地状态,而不需要一个集中式数据库(例如 SQL Server)存储状态。
为了在 Ocelot 中启用 Rafty,您必须对 Startup.cs 进行以下改动。
public virtual void ConfigureServices(IServiceCollection services)
{
services
.AddOcelot()
.AddAdministration("/administration", "secret")
.AddRafty();
}
除此之外,您还必须在您的主项目中添加名为 peers.json 的文件,其内容看起来如下所示:
{
"Peers": [{
"HostAndPort": "http://localhost:5000"
},
{
"HostAndPort": "http://localhost:5002"
},
{
"HostAndPort": "http://localhost:5003"
},
{
"HostAndPort": "http://localhost:5004"
},
{
"HostAndPort": "http://localhost:5001"
}
]
}
Ocelot 的每个实例必须在数组中有它的地址,以便它们可以使用 Rafty 进行通信。
完成这些配置更改后,您必须使用 peers.json 文件中的地址部署和启动 Ocelot 的每个实例。 然后服务器应该开始彼此通信! 您可以通过发布配置更新来检测一切是否正常工作,并通过配置来检查它是否已复制到所有服务器。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于