上一次我們通過一張架構圖(.Net Core with 微服務 - 架構圖)來講述了微服務的結構,分層等內容,從現在開始我們開始慢慢搭建一個最簡單的微服務架構,這次我們先用幾個簡單的 web api 專案以及 ocelot 網關專案來演示下網關是如何配置,如何作業的,
Ocelot 網關
Ocelot 是使用 asp.net core 開發的一個 api 網關專案,它功能豐富,集成了路由、限流、快取、聚合等功能,它使用 .net 撰寫,本質上就是一堆 asp.net core 的中間件,所以它天生對 .net 友好,這些中間件攔截外部的請求,根據路由配置轉發到對應的內部服務上,再把內部的回傳結果對外暴露,
搭建專案結構

新建一個解決方案,新建幾個專案,
- api_gateway API網關
- hotel_base 酒店基本資訊服務
- member_center 會員中心服務
- ordering 訂單服務
安裝 Ocelot
在API網關專案上使用nuget安裝Ocelot的類別庫,Ocelot本質上就是一堆 asp.net Core 的 middleware,所以我們需要在UseOcelot擴展方法在注冊這些中間件,
Install-Package Ocelot
public static void Main(string[] args)
{
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile($"appsettings.{hostingContext.HostingEnvironment.EnvironmentName}.json", true, true)
.AddJsonFile("routes.json")
.AddEnvironmentVariables();
})
.ConfigureServices(s => {
s.AddOcelot();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConsole();
})
.UseIISIntegration()
.Configure(app =>
{
app.UseOcelot().Wait();
})
.Build()
.Run();
}
}
在 main 函式內注冊Ocelot的中間件,服務,使用AddJsonFile指定路由的組態檔,
路由
Ocelot最基本的功能就是反向代理,代理的配置通過一個json檔案來配置,下面讓我們來簡單的演示下如何配置,
以下是通過網關代理訪問酒店服務的酒店串列的配置示例,
{
//獲取酒店串列
"UpstreamPathTemplate": "/api/hotel",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamPathTemplate": "/hotel",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
//hotel service
"Host": "localhost",
"Port": 6003
}
]
}
配置主要是分為Upstream跟Downstream兩部分,Upstream其實就是指代ocelot網關本身,Downstream代表真正的服務,
- UpstreamPathTemplate 網關匹配的路徑
- UpstreamHttpMethod 網關匹配的請求方法
- DownstreamPathTemplate 服務匹配的路徑
- DownstreamScheme 服務的Scheme,http、https
- DownstreamHostAndPorts 服務的主機地址跟埠
上面的配置描述的意思是:把對網關的/api/hotel的GET請求轉發到主機http://localhost:6003/hotel介面上,

路由引數
Ocelot的path模板可以使用{param}模式來匹配引數,然后傳遞到下游服務器上,
{
//獲取單個酒店
"UpstreamPathTemplate": "/api/hotel/{hotel_id}",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamPathTemplate": "/hotel/{hotel_id}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
//hotel service
"Host": "localhost",
"Port": 6003
}
],
"Key": "hotel_base_info",
}
使用{hotel_id}匹配hotelId引數,
{
//獲取酒店房間串列
"UpstreamPathTemplate": "/api/hotel_rooms/{hotel_id}",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamPathTemplate": "/room/hotel_rooms/{hotel_id}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
//hotel service
"Host": "localhost",
"Port": 6003
}
],
"Key": "hotel_rooms"
}
使用{hotel_id}匹配hotelId引數,
{
//獲取查詢訂單
"UpstreamPathTemplate": "/api/order/query?day={day}",
"UpstreamHttpMethod": [ "Get" ],
"DownstreamPathTemplate": "/order/get_orders?day={day}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
//order service
"Host": "localhost",
"Port": 6001
}
]
}
在QueryString上使用{day}匹配引數,
限流
Ocelot支持對請求的限流操作,
"RateLimitOptions": {
"EnableRateLimiting": true,
"Period": "1s",
"PeriodTimespan": 1,
"Limit": 1
}
在路由配置節點添加RateLimitOptions節點,
- EnableRateLimiting = true 開啟限流
- Period = 1s 限流的時間區間為1s
- PeriodTimespan = 1 限流后重置時間
- Limit = 1 限制請求的數量
上面的配置的意思是1秒內限制一次請求,1秒后重置這個限制,
快取
Ocelot可以對請求的回應值提供快取服務,
//快取5s
"FileCacheOptions": { "TtlSeconds": 5 }
在路由配置節點上配置FileCacheOptions欄位,TtlSeconds代表需要快取的時間,單位是秒,
聚合
上一回我們講微服務架構的時候說到“聚合服務層”,我們說這一層的主要功能是對請求進行聚合適配跟裁剪,其實ocelot已經提供了簡單的api聚合功能,如果聚合的需求比較簡單,那么可以使用ocelot直接實作,
簡單聚合
簡單聚合可以通過配置把幾個請求的聚合成一個請求,一次性回傳幾個請求的回應,回應通過json格式被包裝回傳,
"Aggregates": [
{
//聚合 查詢酒店資訊跟酒店房間串列
"RouteKeys": [
"hotel_base_info",
"hotel_rooms"
],
"UpstreamPathTemplate": "/api/hotel_detail/{hotel_id}"
},
]
RouteKeys 代表需要聚合的請求的鍵值,
使用代碼聚合
上面我們直接通過配置實作了api之間聚合請求,這種聚合比較簡單,會把聚合的幾個請求的回應值原封不動的回傳回來,有的時候我們需要對回傳值做一些轉換或者裁剪,比如同一個api我們對移動端的回應可能需要裁剪掉部分欄位,這種需求在ocelot內我們可以使用代碼來完成,
這里不太推薦這種聚合方式,這會造成網關跟下游服務的強耦合關系,
這里我們演示下如何把獲取酒店資訊跟酒店房間串列的回傳值進行裁剪,并回傳一個新的回應,
public class HotelDetailInfoForMobileAggregator : IDefinedAggregator
{
public async Task<DownstreamResponse> Aggregate(List<HttpContext> responses)
{
dynamic hotelInfo = new ExpandoObject();
List<dynamic> rooms = new List<dynamic>();
foreach (var context in responses)
{
if ((context.Items["DownstreamRoute"] as dynamic).Key == "hotel_base_info")
{
var respContent = await context.Items.DownstreamResponse().Content.ReadAsStringAsync();
hotelInfo = JsonConvert.DeserializeObject<dynamic>(respContent);
}
if ((context.Items["DownstreamRoute"] as dynamic).Key == "hotel_rooms")
{
var respContent = await context.Items.DownstreamResponse().Content.ReadAsStringAsync();
rooms = JsonConvert.DeserializeObject<List<dynamic>>(respContent);
}
}
dynamic newResponse = new ExpandoObject();
newResponse.hotel = new {
hotelInfo.id,
hotelInfo.name
};
newResponse.rooms = rooms.Select(x => new {
x.id,
x.no
});
var stringContent = new StringContent(JsonConvert.SerializeObject(newResponse));
return new DownstreamResponse(
stringContent,
System.Net.HttpStatusCode.OK,
responses.SelectMany(x => x.Items.DownstreamResponse().Headers).ToList(),
"OK");
}
}
每一個聚合都需要繼承IDefinedAggregator這個介面然后實作Aggregate方法,在這個方法內對每個請求的回應值進行裁剪,然后重新組合,
{
//聚合 查詢酒店資訊跟酒店房間串列 移動端 裁剪
"RouteKeys": [
"hotel_base_info",
"hotel_rooms"
],
"UpstreamPathTemplate": "/api/m/hotel_detail/{hotel_id}",
"Aggregator": "HotelDetailInfoForMobileAggregator"
}
在組態檔的Aggregates內添加一個配置節點在“Aggregator”欄位上指定Aggregator的類名,
.ConfigureServices(s => {
s.AddOcelot()
.AddTransientDefinedAggregator<HotelDetailInfoForMobileAggregator>();
})
同時在ConfigureServices方法內配置HotelDetailInfoForMobileAggregator的依賴注入,
總結
本次我們通過幾個最簡單的web api專案,演示了如何使用 ocelot 網關進行反向代理,限流,聚合等常用功能,可以看到 ocelot 的配置使用還是比較簡單的,因為是 .net 代碼撰寫,所以對.net 開發者比較友好,我們可以直接使用 .net 代碼來撰寫一些功能,比如直接使用代碼來聚合請求的結果,
相關文章
NET Core with 微服務 - 什么是微服務
.Net Core with 微服務 - 架構圖
演示代碼
https://github.com/kklldog/myhotel_microservice
關注我的公眾號一起玩轉技術

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/285375.html
標籤:.NET Core
上一篇:如何在 Docker 環境下自動給 .NET 程式生成 Dump
下一篇:Net5學習筆記
