Ocelot 中文文档三

本贴最后更新于 1968 天前,其中的信息可能已经渤澥桑田

转换 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/"

上面键是"Test",值是“http://www.bbc.co.uk/,http://ocelot.com/”,这个值的意思是使用 http://ocelot.com/替换 http://www.bbc.co.uk/,语法是{find},{replace}。希望还算简单明了,更多解释在下面例子中。

下游请求之前

在 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,他们的运行顺序如下:

  1. 所有在服务中并且不在 ocelot.json 的 DelegatingHandlers 数组中的全局处理程序按加入的顺序排序。
  2. 所有非全局的 DelegatingHandlers 以及来自 ocelot.json 的 DelegatingHandlers 数组中的所有全局变量都按照它们在 DelegatingHandlers 数组中的顺序排列。
  3. 如果启用了跟踪,那么这一步是跟踪 DelegatingHandler(看跟踪文档).
  4. 如果启用了 QoS,那么这一步是 QoS DelegatingHandler (看服务质文档).
  5. 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 的每个实例。 然后服务器应该开始彼此通信! 您可以通过发布配置更新来检测一切是否正常工作,并通过配置来检查它是否已复制到所有服务器。

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...