Ocelot笔记

简介

Ocelot是一个用.NET Core实现并且开源的API网关,它功能强大,包括了:路由请求聚合服务发现认证鉴权限流熔断、并内置了 负载均衡器Service FabricButterfly Tracing 集成。

本质上,Ocelot是一堆的asp.net core middleware 组成的一个管道。当它拿到请求之后会用一个request builder来构造一个HttpRequestMessage发到下游的真实服务器,等下游的服务返回response之后再由一个middleware将它返回的HttpResponseMessage映射到HttpResponse上。

  • asp.net core中间件示意图

moddleware

API网关

API网关—— 它是系统的暴露在外部的一个访问入口。这个有点像代理访问,就像一个公司的门卫承担着寻址、限制进入、安全检查、位置引导、等等功能。

API网关路径

路由

配置文件中的ReRoutes可以实现由上游到下游路由的转发。

上游路由

API网关匹配到的请求路由地址。

下游路由

API网关转发到真实服务主机的路由地址。

路由负载均衡

当下游服务有多个结点的时候,我们可以在DownstreamHostAndPorts中进行配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"DownstreamPathTemplate": "/api/posts/{postId}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "10.0.1.10",
"Port": 5000,
},
{
"Host": "10.0.1.11",
"Port": 5000,
}
],
"UpstreamPathTemplate": "/posts/{postId}",
"LoadBalancer": "LeastConnection",
"UpstreamHttpMethod": [ "Put", "Delete" ]
}

部署

基本使用

基本使用

IdentityServer 集成

当我们涉及到认证和鉴权的时候,我们可以跟Identity Server进行结合。当网关需要请求认证信息的时候会与Identity Server服务器进行交互来完成

与indentityserver集成

网关集群

只有一个网关是很危险的,也就是我们通常所讲的单点,只要它挂了,所有的服务全挂。这显然无法达到高可用,所以我们也可以部署多台网关。
集群

Consul服务发现

在Ocelot已经支持简单的负载功能,也就是当下游服务存在多个结点的时候,Ocelot能够承担起负载均衡的作用。但是它不提供健康检查,服务的注册也只能通过手动在配置文件里面添加完成。这不够灵活并且在一定程度下会有风险。这个时候我们就可以用Consul来做服务发现,它能与Ocelot完美结合。

Consul服务发现

配置

Ocelot有两个配置块。一个ReRoutes数组和一个GlobalConfiguration

  • ReRoutes配置块是一些告诉Ocelot如何处理上游请求的对象
  • Globalconfiguration有些奇特,可以覆盖ReRoute节点的特殊设置。如果你不想管理大量的ReRoute特定的设置的话,这将很有用。
1
2
3
4
{
"ReRoutes": [],
"GlobalConfiguration": {}
}

ReRoutes路由配置例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
"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
}

请求聚合

什么是聚合路由?

把多个正常的ReRoutes打包并映射到一个对象来对客户端的请求进行响应。

比如,你请求订单信息,订单中又包含商品信息,这里就设计到两个微服务,一个是商品服务,一个是订单服务。如果不运用聚合路由的话,对于一个订单信息,客户端可能需要请求两次服务端。实际上这会造成服务端额外的开销。这时候有了聚合路由后,你只需要请求一次聚合路由,然后聚合路由会合并订单跟商品的结果都一个对象中,并把这个对象响应给客户端。使用Ocelot的此特性可以让你很容易的实现前后端分离的架构。

为了实现Ocelot的请求功能,你需要在ocelot.json中进行如下的配置。这里我们指定了了两个正常的ReRoutes,然后给每个ReRoute设置一个Key属性。最后我们再Aggregates节点中的ReRouteKeys属性中加入我们刚刚指定的两个Key从而组成了两个ReRoutes的聚合。当然我们还需要设置UpstreamPathTemplate匹配上游的用户请求,它的工作方式与正常的ReRoute类似。

Notes: 不要把Aggregates中UpstreamPathTemplate设置的跟ReRoutes中的UpstreamPathTemplate设置成一样

例如:

  1. 存在两个微服务: OrderApiGoodApi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//GoodApi项目中
[Route("api/[controller]")]
[ApiController]
public class GoodController : ControllerBase
{
// GET api/Good/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
var item = new Goods
{
Id = id,
Content = $"{id}的关联的商品明细",
};
return JsonConvert.SerializeObject(item);
}
}
//OrderApi项目中
[Route("api/[controller]")]
[ApiController]
public class OrderController : ControllerBase
{
// GET api/Order/5
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
var item = new Orders {
Id=id,
Content=$"{id}的订单明细",
};
return JsonConvert.SerializeObject(item);
}
}
  1. 分别在ocelot.good.json以及ocelot.order.json中新增一个路由,并给出Keys

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    //ocelot.good.json
    {
    "DownstreamPathTemplate": "/api/Good/{id}",
    "DownstreamScheme": "http",
    "DownstreamHostAndPorts": [
    {
    "Host": "localhost",
    "Port": 1001
    }
    ],
    "UpstreamPathTemplate": "/good/{id}",
    "UpstreamHttpMethod": [ "Get", "Post" ],
    "Key": "Good",
    "Priority": 2
    }
    //ocelot.order.json
    {
    "DownstreamPathTemplate": "/api/Order/{id}",
    "DownstreamScheme": "http",
    "DownstreamHostAndPorts": [
    {
    "Host": "localhost",
    "Port": 1002
    }
    ],
    "UpstreamPathTemplate": "/order/{id}",
    "UpstreamHttpMethod": [ "Get", "Post" ],
    "Key": "Order",
    "Priority": 2
    }
  2. ocelot.all.json中加入聚合配置

1
2
3
4
5
6
7
8
9
"Aggregates": [
{
"ReRouteKeys": [
"Good",
"Order"
],
"UpstreamPathTemplate": "/GetOrderDetail/{id}"
}
]

Notes: 这里AggregatesReRoutes同级,ReRouteKeys中填写的数组就是上面步骤3中设置的Key属性对应的值。

  1. 客户端请求接口地址 http://localhost:1000/GetOrderDetail/1 叫得到如下响应
1
2
3
4
5
6
7
8
9
10
{
"Good":{
"Id":1,
"Content":"1的关联的商品明细"
},
"Order":{
"Id":1,
"Content":"1的订单明细"
}
}

服务发现

Ocelot允许您指定服务发现提供程序,并将使用它来查找Ocelot将请求转发到的下游服务的主机和端口。目前,这仅在GlobalConfiguration部分中受支持,这意味着相同的服务发现提供程序将用于为ReRoute级别指定ServiceName的所有ReRoutes

使用步骤

  1. 安装Consul支持包
1
Install-Package Ocelot.Provider.Consul

startup类中的ConfigureServices方法中增加

1
2
services.AddOcelot()//注入Ocelot服务
.AddConsul();
  1. GlobalConfiguration中需要加入以下内容。如果您未指定主机和端口,则将使用Consul默认值。
1
2
3
4
5
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 8500,
"Type": "Consul"
}