Ocelot是一個用.NET Core實現(xiàn)并且開源的API網(wǎng)關,它功能強大,包括了:路由、請求聚合、服務發(fā)現(xiàn)、認證、鑒權、限流熔斷、并內(nèi)置了負載均衡器與Service Fabric、Butterfly Tracing集成。這些功能只都只需要簡單的配置即可完成,下面我們會對這些功能的配置一一進行說明。
創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設、高性價比武陟網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫,直接使用。一站式武陟網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設找我們,業(yè)務覆蓋武陟地區(qū)。費用合理售后完善,十年實體公司更值得信賴。簡單的來說Ocelot是一堆的asp.net core middleware組成的一個管道。當它拿到請求之后會用一個request builder來構造一個HttpRequestMessage發(fā)到下游的真實服務器,等下游的服務返回response之后再由一個middleware將它返回的HttpResponseMessage映射到HttpResponse上。
API網(wǎng)關—— 它是系統(tǒng)的暴露在外部的一個訪問入口。這個有點像代理訪問的家伙,就像一個公司的門衛(wèi)承擔著尋址、限制進入、安全檢查、位置引導、等等功能。
Ocelot的基本使用
用一臺web service來host Ocelot,在這里有一個json配置文件,里面設置了所有對當前這個網(wǎng)關的配置。它會接收所有的客戶端請求,并路由到對應的下游服務器進行處理,再將請求結果返回。而這個上下游請求的對應關系也被稱之為路由。
集成Identity Server
當我們涉及到認證和鑒權的時候,我們可以跟Identity Server進行結合。當網(wǎng)關需要請求認證信息的時候會與Identity Server服務器進行交互來完成。
網(wǎng)關集群
只有一個網(wǎng)關是很危險的,也就是我們通常所講的單點,只要它掛了,所有的服務全掛。這顯然無法達到高可用,所以我們也可以部署多臺網(wǎng)關。當然這個時候在多臺網(wǎng)關前,你還需要一臺負載均衡器。
Consul 服務發(fā)現(xiàn)
在Ocelot已經(jīng)支持簡單的負載功能,也就是當下游服務存在多個結點的時候,Ocelot能夠承擔起負載均衡的作用。但是它不提供健康檢查,服務的注冊也只能通過手動在配置文件里面添加完成。這不夠靈活并且在一定程度下會有風險。這個時候我們就可以用Consul來做服務發(fā)現(xiàn),它能與Ocelot完美結合。
在asp.net core 2.0里通過nuget即可完成集成,或者命令行dotnet add package Ocelot以及通過vs2017 UI添加Ocelot nuget引用都可以。
Install-Package Ocelot
我們需要添加一個.json的文件用來添加Ocelot的配置,以下是最基本的配置信息。
{
"ReRoutes": [],
"GlobalConfiguration": {
"BaseUrl": "https://api.mybusiness.com"
}
}
要特別注意一下BaseUrl是我們外部暴露的Url,比如我們的Ocelot運行在http://123.111.1.1的一個地址上,但是前面有一個 nginx綁定了域名http://api.jessetalk.cn,那這里我們的BaseUrl就是 http://api.jessetalk.cn。
將配置文件加入ASP.NET Core Configuration
在startup.cs中我們首先需要引用兩個命名空間
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddOcelot(new ConfigurationBuilder()
.AddJsonFile("OcelotConfig.json")
.Build());
services.AddCors(options =>
{
options.AddPolicy("AllowAll",
builder => builder.AllowAnyOrigin().AllowAnyHeader()
.WithMethods("GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS")
);
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("AllowAll");
app.UseAuthorization();
app.UseOcelot().Wait();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
通過配置文件可以完成對Ocelot的功能配置:路由、服務聚合、服務發(fā)現(xiàn)、認證、鑒權、限流、熔斷、緩存、Header頭傳遞等。在配置文件中包含兩個根節(jié)點:ReRoutes和GlobalConfiguration。ReRoutes是一個數(shù)組,其中的每一個元素代表了一個路由,我們可以針對每一個路由進行以上功能配置。下面是一個完整的路由配置
{
"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
}
我們接下來將對這些功能一一進行介紹和配置
路由是API網(wǎng)關最基本也是最核心的功能、ReRoutes下就是由多個路由節(jié)點組成。
{
"ReRoutes": [
]
}
而每一個路由由以下幾個基本信息組成:
下面這個配置信息就是將用戶的請求 /post/1 轉(zhuǎn)發(fā)到 localhost/api/post/1
{
"DownstreamPathTemplate": "/api/post/{postId}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 80,
}
],
"UpstreamPathTemplate": "/post/{postId}",
"UpstreamHttpMethod": [ "Get"]
}
萬能模板
萬能模板即所有請求全部轉(zhuǎn)發(fā),UpstreamPathTemplate 與DownstreamPathTemplate 設置為 “/{url}”
{
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 80,
}
],
"UpstreamPathTemplate": "/{url}",
"UpstreamHttpMethod": [ "Get" ]
}
萬能模板的優(yōu)先級最低,只要有其它的路由模板,其它的路由模板則會優(yōu)先生效。
上游Host
上游Host也是路由用來判斷的條件之一,由客戶端訪問時的Host來進行區(qū)別。比如當a.jesetalk.cn/users/{userid}和b.jessetalk.cn/users/{userid}兩個請求的時候可以進行區(qū)別對待。
{
"DownstreamPathTemplate": "/",
"DownstreamScheme": "https",
"DownstreamHostAndPorts": [
{
"Host": "10.0.10.1",
"Port": 80,
}
],
"UpstreamPathTemplate": "/",
"UpstreamHttpMethod": [ "Get" ],
"UpstreamHost": "a.jessetalk.cn"
}
Prioirty優(yōu)先級
對多個產(chǎn)生沖突的路由設置優(yōu)化級
{
"UpstreamPathTemplate": "/goods/{catchAll}"
"Priority": 0
}
{
"UpstreamPathTemplate": "/goods/delete"
"Priority": 1
}
比如你有同樣兩個路由,當請求/goods/delete的時候,則下面那個會生效。也就是說Prority是大的會被優(yōu)先選擇。
當下游服務有多個結點的時候,我們可以在DownstreamHostAndPorts中進行配置。
{
"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" ]
}
LoadBalancer將決定負載均衡的算法
在負載均衡這里,我們還可以和Consul結合來使用服務發(fā)現(xiàn),我們將在后面的小節(jié)中進行詳述。
即將多個API請求結果合并為一個返回。要實現(xiàn)請求聚合我們需要給其它參與的路由起一個Key。
{
"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": "/"
}
]
}
當我們請求/的時候,會將/tom和/laura兩個結果合并到一個response返回
{"Tom":{"Age": 19},"Laura":{"Age": 25}}
需要注意的是:
有一些其它的功能會在將來實現(xiàn)
對請求進行限流可以防止下游服務器因為訪問過載而崩潰,這個功能就是我們的張善友張隊進添加進去的。非常優(yōu)雅的實現(xiàn),我們只需要在路由下加一些簡單的配置即可以完成。
"RateLimitOptions": {
"ClientWhitelist": [],
"EnableRateLimiting": true,
"Period": "1s",
"PeriodTimespan": 1,
"Limit": 1
}
在 GlobalConfiguration下我們還可以進行以下配置
"RateLimitOptions": {
"DisableRateLimitHeaders": false,
"QuotaExceededMessage": "Customize Tips!",
"HttpStatusCode": 999,
"ClientIdHeader" : "Test"
}
熔斷的意思是停止將請求轉(zhuǎn)發(fā)到下游服務。當下游服務已經(jīng)出現(xiàn)故障的時候再請求也是功而返,并且增加下游服務器和API網(wǎng)關的負擔。這個功能是用的Pollly來實現(xiàn)的,我們只需要為路由做一些簡單配置即可
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking":3,
"DurationOfBreak":5,
"TimeoutValue":5000
}
Ocelot可以對下游請求結果進行緩存 ,目前緩存的功能還不是很強大。它主要是依賴于CacheManager 來實現(xiàn)的,我們只需要在路由下添加以下配置即可
"FileCacheOptions": { "TtlSeconds": 15, "Region": "somename" }
Region是對緩存進行的一個分區(qū),我們可以調(diào)用Ocelot的 administration API來移除某個區(qū)下面的緩存 。
如果我們需要對下游API進行認證以及鑒權服務的,則首先Ocelot 網(wǎng)關這里需要添加認證服務。這和我們給一個單獨的API或者ASP.NET Core Mvc添加認證服務沒有什么區(qū)別。
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
services.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, x =>
{
});
}
然后在ReRoutes的路由模板中的AuthenticationOptions進行配置,只需要我們的AuthenticationProviderKey一致即可。
"ReRoutes": [{
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 51876,
}
],
"DownstreamPathTemplate": "/",
"UpstreamPathTemplate": "/",
"UpstreamHttpMethod": ["Post"],
"ReRouteIsCaseSensitive": false,
"DownstreamScheme": "http",
"AuthenticationOptions": {
"AuthenticationProviderKey": "TestKey",
"AllowedScopes": []
}
}]
JWT Tokens
要讓網(wǎng)關支持JWT 的認證其實和讓API支持JWT Token的認證是一樣的
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
services.AddAuthentication()
.AddJwtBearer(authenticationProviderKey, x =>
{
x.Authority = "test";
x.Audience = "test";
});
services.AddOcelot();
}
Identity Server Bearer Tokens
添加Identity Server的認證也是一樣
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "TestKey";
var options = o =>
{
o.Authority = "https://whereyouridentityserverlives.com";
o.ApiName = "api";
o.SupportedTokens = SupportedTokens.Both;
o.ApiSecret = "secret";
};
services.AddAuthentication()
.AddIdentityServerAuthentication(authenticationProviderKey, options);
services.AddOcelot();
}
Allowed Scopes
這里的Scopes將從當前 token 中的 claims中來獲取,我們的鑒權服務將依靠于它來實現(xiàn) 。當前路由的下游API需要某個權限時,我們需要在這里聲明 。和oAuth2中的 scope意義一致。
我們通過認證中的AllowedScopes 拿到claims之后,如果要進行權限的鑒別需要添加以下配置
"RouteClaimsRequirement": {
"UserType": "registered"
}
當前請求上下文的token中所帶的claims如果沒有 name=”UserType” 并且 value=”registered” 的話將無法訪問下游服務。
請求頭轉(zhuǎn)發(fā)分兩種:轉(zhuǎn)化之后傳給下游和從下游接收轉(zhuǎn)化之后傳給客戶端。在Ocelot的配置里面叫做Pre Downstream Request和Post Downstream Request。目前的轉(zhuǎn)化只支持查找和替換。我們用到的配置主要是 UpstreamHeaderTransform 和 DownstreamHeaderTransform
Pre Downstream Request
"Test": "http://www.bbc.co.uk/, http://ocelot.com/"
比如我們將客戶端傳過來的Header中的 Test 值改為 http://ocelot.com/之后再傳給下游
"UpstreamHeaderTransform": {
"Test": "http://www.bbc.co.uk/, http://ocelot.com/"
},
Post Downstream Request
而我們同樣可以將下游Header中的Test再轉(zhuǎn)為 http://www.bbc.co.uk/之后再轉(zhuǎn)給客戶端。
"DownstreamHeaderTransform": {
"Test": "http://www.bbc.co.uk/, http://ocelot.com/"
},
變量
在請求頭轉(zhuǎn)化這里Ocelot為我們提供了兩個變量:BaseUrl和DownstreamBaseUrl。BaseUrl就是我們在GlobalConfiguration里面配置的BaseUrl,后者是下游服務的Url。這里用301跳轉(zhuǎn)做一個示例如何使用這兩個變量。
默認的301跳轉(zhuǎn),我們會返回一個Location的頭,于是我們希望將http://www.bbc.co.uk 替換為 http://ocelot.com,后者者網(wǎng)關對外的域名。
"DownstreamHeaderTransform": {
"Location": "http://www.bbc.co.uk/, http://ocelot.com/"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
我們通過DownstreamHeaderTranfrom將下游返回的請求頭中的Location替換為了網(wǎng)關的域名,而不是下游服務的域名。所以在這里我們也可以使用BaseUrl來做為變量替換。
"DownstreamHeaderTransform": {
"Location": "http://localhost:6773, {BaseUrl}"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
當我們的下游服務有多個的時候,我們就沒有辦法找到前面的那個http://localhost:6773,因為它可能是多個值。所以這里我們可以使用DownstreamBaseUrl。
"DownstreamHeaderTransform": {
"Location": "{DownstreamBaseUrl}, {BaseUrl}"
},
"HttpHandlerOptions": {
"AllowAutoRedirect": false,
},
Claims轉(zhuǎn)化功能可以將Claims中的值轉(zhuǎn)化到請求頭、Query String、或者下游的Claims中,對于Claims的轉(zhuǎn)化,比較特殊的一點是它提供了一種對字符串進行解析的方法。舉個例子,比如我們有一個sub的claim。這個claims的 name=”sub” value=”usertypevalue|useridvalue”,實際上我們不會弄這么復雜的value,它是拼接來的,但是我們?yōu)榱搜菔具@個字符串解析的功能,所以使用了這么一個復雜的value。
Ocelot為我們提供的功能分為三段,第一段是Claims[sub],很好理解[] 里面是我們的claim的名稱。第二段是 > 表示對字符串進行拆分, 后面跟著拆分完之后我們要取的那個數(shù)組里面的某一個元素用 value[index]來表示,取第0位元素也可以直接用value。第三段也是以 > 開頭后面跟著我們的分隔符,在我們上面的例子分隔符是 |
所以在這里如果我們要取 usertype這個claim就會這樣寫: Claims[sub] > value[0] > |
Claim取到之后我們?nèi)绻诺秸埱箢^、QueryString、以及Claim當中對應有以下三個配置。
Claims to Claims
"AddClaimsToRequest": {
"UserType": "Claims[sub] > value[0] > |",
"UserId": "Claims[sub] > value[1] > |"
}
Claims to Headers
"AddHeadersToRequest": {
"CustomerId": "Claims[sub] > value[1] > |"
}
這里我們還是用的上面那個 sub = usertypevalue|useridvalue 的claim來進行處理和轉(zhuǎn)化。
Claims to Query String
"AddQueriesToRequest": {
"LocationId": "Claims[LocationId] > value",
}
這里沒有進行分隔,所以直接取了value。
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/{url}",//任意的url
"DownstreamScheme": "http",//
"UpstreamPathTemplate": "/VideoInspectionConfig/{url}",//可配置
"UpstreamHttpMethod": [ "Get", "Post", "Delete", "Put" ],
"ServiceName": "TestCoreWebAPI.VideoInspectionConfig",//跟前幾篇中consul中注冊的名字要一樣
"LoadBalancerOptions": {
"Type":"RoundRobin"
},
"UseServiceDiscovery": true,
"ReRouteIsCaseSensitive": false
}
],
"GlobalConfiguration": {
"ServiceDiscoveryProvider": {
"Host": "114.115.215.189",//consul的服務器
"Port": 8500, //consul端口, "Token": "245d0a09-7139-bbea-aadc-ff170a0562b1" // consul acl里的toekn,如果沒配置acl,這個節(jié)點不需要
}
}
}
轉(zhuǎn)載:https://www.cnblogs.com/jackielyj/p/13266841.html