入门
Ocelot 只能用于.NET Core,目前是为 netcoreapp2.0 构建的,这个文档可能会帮你了解 Ocelot 是否适合你。
.NET Core 2.0
安装 NuGet 包
使用 nuget 安装 Ocelot 和它的依赖。你需要创建一个 netcoreapp2.0 的项目并安装 Ocelot 包。然后按照下面的配置部分启动并运行。
Install-Package Ocelot
所有版本可以在这里找到。
配置
下面是个很简单的 ocelot.json。它不做任何事情,但是可以上 Ocelot 启动了。
{
"ReRoutes": [],
"GlobalConfiguration": {
"BaseUrl": "https://api.mybusiness.com"
}
}
这里最重要的就是 BaseUrl 了。Ocelot 需要知道它正在运行的 URL,以便头部查找和替换以及一些管理配置。设置此 URL 时,它应该是客户端将看到的 Ocelot 运行的外部 URL。例如,假如你在容器中运行 Ocelot 的 url 是 http://123.12.1.1:6543,但是在它前面有像 nginx 一样的代理在响应 https://api.mybusiness.com。在这种情况下 Ocelot 的 BaseUrl 应该是 https://api.mybusiness.com。
如果由于某种原因你使用的是容器,并且希望 Ocelot 在 http://123.12.1.1:6543 上响应客户端,那么你可以这样做,但如果你正在部署多个 Ocelot,你可能会希望通过某种脚本在命令行上传递它。 希望您使用的任何调度程序都可以传递这个 IP。
程序
然后在你的 Program.cs 中,你会想要以下内容。主要就是 AddOcelot()(添加 ocelot 服务),UseOcelot().Wait()(设置所有的 Ocelot 中间件)。
public class Program
{
public static void Main(string[] args)
{
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json")
.AddEnvironmentVariables();
})
.ConfigureServices(s => {
s.AddOcelot();
})
.ConfigureLogging((hostingContext, logging) =>
{
//add your logging
})
.UseIISIntegration()
.Configure(app =>
{
app.UseOcelot().Wait();
})
.Build()
.Run();
}
}
.NET Core 1.0
不支持
Ocelot 不支持一下几点...
- 分块编码 - Ocelot 将始终获取 body 大小并返回 Content-Length 头。 如果这不适合你的场景,只能表示抱歉!
- 转发 host 头 - 您发给 Ocelot 的 host 头不会转发给下游服务。显然这会打破一切 :(
- Swagger - 我已经多次看过从 Ocelot 的 ocelot.json 构建 swagger.json,但它看起来不适合
我有 Ocelot 就够了。如果您想在 Ocelot 中使用 Swagger,那么您必须生成自己的 swagger.json,并在 Startup.cs 或 Program.cs 中执行以下操作。 下面的代码示例注册了一个加载您手动生成的 swagger.json 并将其返回到/swagger/v1/swagger.json 的中间件。 然后使用 Swashbuckle.AspNetCore 注册 SwaggerUI 中间件。
app.Map("/swagger/v1/swagger.json", b =>
{
b.Run(async x => {
var json = File.ReadAllText("swagger.json");
await x.Response.WriteAsync(json);
});
});
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ocelot");
});
app.UseOcelot().Wait();
我认为 Swagger 没意义的主要原因是我们已经在 ocelot.json 中手动编写了我们的定义。如果我们希望针对 Ocelot 开发的人员能够查看可用的路由,那么可以与他们共享 ocelot.json(这应该与访问仓库一样简单)或者使用 Ocelot 管理 API,以便他们可以查询 Ocelot 的配置。
除此之外,许多人还会配置 Ocelot 将所有流量例如/products/{everything}代理到产品服务,如果您解析并将其转换为 Swagger 路径,则不会描述实际可用的内容。另外 Ocelot 没有有关下游服务可以返回模型的概念,并且连接到上述一个端口可以返回多个模型的问题。Ocelot 不知道哪些模块需要使用 POST 还是 PUT,所以会变得混乱,最后当 swagger.json 在运行时发生变化是,Swashbuckle 包不会重新加载它。Ocelot 的配置可以在运行时更改,这样导致 Swagger 和 Ocelot 的信息不匹配。除非我推出我自己的 Swagger 实现。
如果有人想要对 Ocelot API 进行简单的测试,我建议使用 Postman。甚至可以写一些将 ocelot.json 映射到 postman json 规范的东西。但是我不打算这样做。
配置
这里有一个配置的列子。其中有两个配置块。一个 ReRoutes 数组和一个 GlobalConfiguration。ReRoutes 配置块是一些告诉 Ocelot 如何处理上游请求的对象。Globalconfiguration 有些奇特,可以覆盖 ReRoute 节点的特殊设置。如果你不想管理大量的 ReRoute 特定的设置的话,这将很有用。
{
"ReRoutes": [],
"GlobalConfiguration": {}
}
这是一个 ReRoute 配置的例子,你不需要全部都设置,但这是目前可用的所有设置:
{
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/",
"UpstreamHttpMethod": [
"Get"
],
"AddHeadersToRequest": {},
"AddClaimsToRequest": {},
"RouteClaimsRequirement": {},
"AddQueriesToRequest": {},
"RequestIdKey": "",
"FileCacheOptions": {
"TtlSeconds": 0,
"Region": ""
},
"ReRouteIsCaseSensitive": false,
"ServiceName": "",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51876,
}
],
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 0,
"DurationOfBreak": 0,
"TimeoutValue": 0
},
"LoadBalancer": "",
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": false,
"Period": "",
"PeriodTimespan": 0,
"Limit": 0
},
"AuthenticationOptions": {
"AuthenticationProviderKey": "",
"AllowedScopes": []
},
"HttpHandlerOptions": {
"AllowAutoRedirect": true,
"UseCookieContainer": true,
"UseTracing": true
},
"UseServiceDiscovery": false,
"DangerousAcceptAnyServerCertificateValidator": false
}
有关如何使用这些选项的更多信息如下..
多环境
和其他任何 asp.net core 项目一样,Ocelot 支持像 configuration.dev.json、configuration.test.json 等的配置文件名。为了实现多环境配置,需要添加如下的代码。
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("ocelot.json")
.AddJsonFile($"configuration.{hostingContext.HostingEnvironment.EnvironmentName}.json")
.AddEnvironmentVariables();
})
Ocelot 现在将使用特定于环境的配置,如果没有,则返回到 ocelot.json。
您还需要设置相应的 ASPNETCORE_ENVIRONMENT 环境变量。更多信息可以查看 asp.net core 文档.
合并配置文件
此功能在问题 Issue 296 提出要求,并允许用户有多个配置文件,以便管理大型配置。
不直接使用.AddJsonFile("ocelot.json")这样添加配置,还可以使用 AddOcelot()方法添加
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("appsettings.json", true, true)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddOcelot()
.AddEnvironmentVariables();
})
在这种情况下,Ocelot 将查找与模式(?i)ocelot.([a-zA-Z0-9]*).json 匹配的任何文件,然后将它们合并。 如果你想设置 GlobalConfiguration 属性,你必须有一个名为 ocelot.global.json 的文件。
Ocelot 合并文件的方式基本上是加载、循环它们,添加所有 ReRoutes,添加所有 AggregateReRoutes,如果文件名为 ocelot.global.json,则添加 GlobalConfiguration 以及所有 ReRoutes 或 AggregateReRoutes。 然后 Ocelot 将合并后的配置保存到一个名为 ocelot.json 的文件中,这才是 ocelot 运行时配置的真正来源。
目前在这个阶段没有验证,只有当 Ocelot 最终合并配置时才会发生验证。 当你排查问题时要注意这一点。如果您有任何问题,建议您始终检查 ocelot.json 中的内容。
将配置存储在 consul 中
如果您在注册服务时添加以下内容,Ocelot 将尝试在 consul 的 KV 数据中存储和检索其配置。
services
.AddOcelot()
.AddStoreOcelotConfigurationInConsul();
您还需要将以下内容添加到您的 ocelot.json 中。 这使 Ocelot 知道如何找到您的 Consul 代理并进行交互以及从 Consul 加载和存储配置。
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 9500
}
}
我在研究过 raft 算法并发现其超级之难后决定创建此功能。 为什么不利用 Consul 已经给你的这个便利呢!我想这意味着如果你想最大限度地使用 Ocelot,你现在就需要将 Consul 作为依赖。
此功能在向本地 consul 代理发出新请求之前有 3 秒 TTL 缓存。
重定向 / 使用 CookieContainer
在 ReRoute 配置中使用 HttpHandlerOptions 来设置 HttpHandler 行为:
-
AllowAutoRedirect 的值指示请求是否应该遵循重定向响应。 如果请求应该自动重定向来自下游资源的响应,请将其设置为 true; 否则为 false。 默认值是 false。
-
UseCookieContainerd 的值指示处理程序是否使用 CookieContainer 属性存储服务器 cookie,并在发送请求时使用这些 cookie。默认值是 false。值得注意的是,如果您使用 CookieContainer,Ocelot 为每个下游服务缓存 HttpClient。这意味着对该下游的所有请求都将共享相同的 Cookie。 问题 274 的提出是因为用户观察到 Cookie 被共享了。我试图想出一个好的解决方案,但我认为是不可能的。如果您不缓存客户端,这意味着每个请求都获得一个新客户端并因此获得一个新的 cookie 容器。如果您清除缓存客户端容器中的 Cookie,则会因为正在进行的请求而竞争。这也意味着后续请求不能使用以前响应中的 cookie!总而言之,这不是一个好的局面。我会避免将 UseCookieContainer 设置为 true,除非有一个非常好的理由。只需查看您的响应头,并在您的下一个请求把 cookie 转发回来!
SSL 错误
如果您想忽略 SSL 警告/错误,请在 ReRoute 配置中设置以下内容。
"DangerousAcceptAnyServerCertificateValidator": false
我不建议这么做,如果可以我建议你创建自己的证书,然后让本地/远程的机器信任它。
路由
Ocelot 的主要功能是接管进入的 http 请求并把它们转发给下游服务。目前是以另一个 http 请求的形式(将来可能是任何传输机制)。
Ocelot 将路由一个请求到另一个请求描述为 ReRoute。为了在 Ocelot 做任何工作,都需要在配置中设置一个 ReRoute。
{
"ReRoutes": [
]
}
为了设置 ReRoute,你需要如下所示添加一个 ReRoute 到 ReRoutes 的 json 数组。
{
"DownstreamPathTemplate": "/api/posts/{postId}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 80,
}
],
"UpstreamPathTemplate": "/posts/{postId}",
"UpstreamHttpMethod": [ "Put", "Delete" ]
}
DownstreamPathTemplate、DownstreamScheme 和 DownstreamHostAndPorts 确定请求的转发。
DownstreamHostAndPorts 是一个数组,包含请求要转发的主机和端口。通常这只包含一个条目,但有时您可能需要将请求负载平衡到您的下游服务,这是 Ocelot 允许我们添加多个条目,然后选择一个负载均衡器。
UpstreamPathTemplate 是 Ocelot 用来标识哪个 DownstreamPathTemplate 用于给定的请求 URL。 最后,UpstreamHttpMethod 的使用,可以让 Ocelot 区分对同一个 URL 的请求,并且显然这是需要的工作。
你可以指定一个 Http 请求方法列表,或者一个空的列表以允许任务 Http 请求方法。在 Ocelot 中,你可以使用{something}的方式在模板中添加变量占位符。占位符需要在 DownstreamPathTemplate 和 UpstreamPathTemplate 中都添加。如果是这样,当请求到达时 Ocelot 将试图使用上游 url 中的正确的变量值来替换占位符。
你也可以想这样使用一个 ReRoute 处理所有请求:
{
"DownstreamPathTemplate": "/api/{everything}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 80,
}
],
"UpstreamPathTemplate": "/{everything}",
"UpstreamHttpMethod": [ "Get", "Post" ]
}
这将转发所有请求到下游服务路径/api。
目前在没有任何配置的请求下,Ocelot 将默认所有 ReRoutes 不区分大小写。 为了改变这种情况,您可以在每个 ReRoute 中指定以下设置:
"ReRouteIsCaseSensitive": true
这意味着,当 Ocelot 尝试将上行 url 与上游模板匹配时将区分大小写。 此设置默认为 false,这也是我的建议。因此只有在您希望 ReRoute 区分大小写时才用设置它。
捕获所有
Ocelot 的路由还支持捕获所有样式的路由,用户可以指定他们想要匹配所有流量。如果你像下面那样设置你的配置,请求将被直接代理(它不一定叫 url,任何占位符名称都可以)。
{
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 80,
}
],
"UpstreamPathTemplate": "/{url}",
"UpstreamHttpMethod": [ "Get" ]
}
该捕获所有的优先级低于其他任何 ReRoute。 如果你的配置中还有下面的 ReRoute,那么 Ocelot 会在捕获所有配置之前先匹配它。
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "10.0.10.1",
"Port": 80,
}
],
"UpstreamPathTemplate": "/",
"UpstreamHttpMethod": [ "Get" ]
}
上游主机
此功能允许您基于上游主机进行 ReRoutes。 这是通过查看客户端使用的主机头来工作,然后将其用作识别 ReRoute 的信息的一部分。
为了使用这个功能,在你的配置中加上如下配置。
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "10.0.10.1",
"Port": 80,
}
],
"UpstreamPathTemplate": "/",
"UpstreamHttpMethod": [ "Get" ],
"UpstreamHost": "somedomain.com"
}
上面的 ReRoute 只会匹配主机头是 somedomain.com 的请求。
如果您没有在 ReRoue 上设置 UpstreamHost,则任何主机头都可以匹配它。 这基本上是一个捕获所有功能并保留构建功能时的现有功能。这意味着如果您有两个相同的 ReRoute,其中一个与 UpstreamHost 是 null,另一个有值。 Ocelot 会倾向于设定值的那个。
这个功能在问题 216 提出要求。
优先级
在问题 270 中,我最终决定在 ocelot.json 中公开 ReRoute 的优先级。这意味着您可以决定上游 HttpRequest 与你的 ReRoutes 的匹配顺序。
为了是其起作用,将以下内容添加到 ocelot.json 的 ReRoute 中,0 仅仅是一个示例值,将在下面解释。
{
"Priority": 0
}
0 是最低优先级,Ocelot 将始终使用 0 作为/{catchAll}路由条目,并且可以硬编码。之后,你可以自由设置你想要的任何优先级。
例如你可以这样:
{
"UpstreamPathTemplate": "/goods/{catchAll}"
"Priority": 0
}
还可以:
{
"UpstreamPathTemplate": "/goods/delete"
"Priority": 1
}
在上面的例子中,如果您向 Ocelot 请求/goods/delete,Ocelot 将匹配/goods/delete 这个 ReRoute。不过在不设置优先级以前它会匹配/goods/{catchAll}(因为这是列表中的第一个 ReRoute!)。
请求聚合
Ocelot 允许您指定聚合多个普通 ReRoutes 的 Aggregate ReRoutes(聚合路由),并将其响应映射到一个对象中。一般用于当您有一个客户端向服务器发出多个请求,而这些请求可以合并成一个的时候。此功能允许您通过 Ocelot 实现前端类型结构的后端。
此功能是问题 79 的一部分,并且作为问题 298 的一部分进行了进一步改进。
为了设置它,你必须在 ocelot.json 中做如下的事情。 这里我们已经指定了两个普通的 ReRoutes,每一个都有一个 Key 属性。 然后,我们使用 ReRouteKeys 列表中的键指定组成两个 ReRoutes 的聚合,然后设置 UpstreamPathTemplate,它的工作方式与普通的 ReRoute 相似。 很明显,您不能在 ReRoutes 和 Aggregates 之间复制 UpstreamPathTemplates。 除 RequestIdKey 之外,您可以使用普通 ReRoute 所有的选项(在下面的陷阱中进行了解释)。
高级应用-注册你自己的聚合器
Ocelot 只是基本的请求聚合,然后我们添加了一个更高级的方法,让用户从下游服务中获取响应,然后将它们聚合到响应对象中。
ocelot.json 的设置与基本聚合方法几乎相同,只需额外添加一个 Aggregator 属性,如下所示。
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/laura",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51881
}
],
"Key": "Laura"
},
{
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/tom",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51882
}
],
"Key": "Tom"
}
],
"Aggregates": [
{
"ReRouteKeys": [
"Tom",
"Laura"
],
"UpstreamPathTemplate": "/",
"Aggregator": "FakeDefinedAggregator"
}
]
}
这里我们添加了一个叫 FakeDefinedAggregator 的聚合器。当 Ocelot 尝试聚合这个 ReRoute 的时候,会去查看这个聚合器。
为了使这个聚合器可用,我们必须像下面这样把 FakeDefinedAggregator 添加到 OcelotBuilder。
services
.AddOcelot()
.AddSingletonDefinedAggregator<FakeDefinedAggregator>();
现在,当 Ocelot 尝试聚合上述 ReRoute 时,它会在容器中找到 FakeDefinedAggregator 并使用它来聚合 ReRoute。 由于 FakeDefinedAggregator 是在容器中注册,因此您可以将它需要的任何依赖项都添加到容器中,如下所示。
services.AddSingleton<FooDependency>();
services
.AddOcelot()
.AddSingletonDefinedAggregator<FooAggregator>();
在这个例子中 FooAggregator 依赖 FooDependency,将会被容器解析。
除此之外,Ocelot 还允许您添加如下所示的瞬态聚合器。(参考.net core 依赖注入,译者注)
services
.AddOcelot()
.AddTransientDefinedAggregator<FakeDefinedAggregator>();
为了实现一个聚合器,你必须实现这个接口。
public interface IDefinedAggregator
{
Task<DownstreamResponse> Aggregate(List<DownstreamResponse> responses);
}
使用此功能,您几乎可以做任何您想做的事情,因为 DownstreamResponse 包含内容,头和状态代码。 如果需要,我们可以添加额外的东西,只需在 GitHub 上提出这个问题。请注意,如果在向聚合中的 ReRoute 发出请求时 HttpClient 抛出异常,那么您将不会获得其 DownstreamResponse,但您会获得其他请求成功的 DownstreamResponse。 如果某个请求抛出异常,则会被记录。
基本演示
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/laura",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51881
}
],
"Key": "Laura"
},
{
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/tom",
"UpstreamHttpMethod": [
"Get"
],
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51882
}
],
"Key": "Tom"
}
],
"Aggregates": [
{
"ReRouteKeys": [
"Tom",
"Laura"
],
"UpstreamPathTemplate": "/"
}
]
}
你也可以设置 Aggregate 的 UpstreamHost 和 ReRouteIsCaseSensitive,和其他 ReRoutes 的作用是一样的。
如何路由/tom 返回 {“Age”: 19},路由/laura 返回{“Age”: 25},那么聚合之后的相应就如下所示。
{"Tom":{"Age": 19},"Laura":{"Age": 25}}
目前的聚合功能非常简单。 Ocelot 只是从你的下游服务获得响应,并将其复制到 json 字典中,如上所示。将 ReRoute 键作为字典的关键字,下游服务的响应体作为值。你可以看到这个对象就是没有任何缩进空格的 JSON。
来自下游服务相应的所有头部都会丢失。
Ocelot 将总是将聚合请求的内容类型返回 application/json。
如果下游服务返回 404,那么聚合将为该下游服务返回空内容。 即使所有下游都返回 404,它也不会是聚合响应为 404。
疑难杂症 / 更多信息
您不能将 ReRoutes 与特定的 RequestIdKeys 一起使用,因为这将使跟踪非常的复杂。
聚合仅支持 GET HTTP 动词。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于