.Net Core with 微服务 - Ocelot 网关
上一次我們通過一張架構(gòu)圖(.Net Core with 微服務 - 架構(gòu)圖)來講述了微服務的結(jié)構(gòu),分層等內(nèi)容。從現(xiàn)在開始我們開始慢慢搭建一個最簡單的微服務架構(gòu)。這次我們先用幾個簡單的 web api 項目以及 ocelot 網(wǎng)關(guān)項目來演示下網(wǎng)關(guān)是如何配置,如何工作的。
Ocelot 網(wǎng)關(guān)
Ocelot 是使用 asp.net core 開發(fā)的一個 api 網(wǎng)關(guān)項目。它功能豐富,集成了路由、限流、緩存、聚合等功能。它使用 .net 編寫,本質(zhì)上就是一堆 asp.net core 的中間件,所以它天生對 .net 友好。這些中間件攔截外部的請求,根據(jù)路由配置轉(zhuǎn)發(fā)到對應的內(nèi)部服務上,再把內(nèi)部的返回結(jié)果對外暴露。
搭建項目結(jié)構(gòu)
新建一個解決方案,新建幾個項目。
api_gateway API網(wǎng)關(guān)
hotel_base 酒店基本信息服務
member_center 會員中心服務
ordering 訂單服務
安裝 Ocelot
在API網(wǎng)關(guān)項目上使用nuget安裝Ocelot的類庫。Ocelot本質(zhì)上就是一堆 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 函數(shù)內(nèi)注冊Ocelot的中間件,服務,使用AddJsonFile指定路由的配置文件。
路由
Ocelot最基本的功能就是反向代理。代理的配置通過一個json文件來配置。下面讓我們來簡單的演示下如何配置。
以下是通過網(wǎng)關(guān)代理訪問酒店服務的酒店列表的配置示例。
配置主要是分為Upstream跟Downstream兩部分。Upstream其實就是指代ocelot網(wǎng)關(guān)本身。Downstream代表真正的服務。
UpstreamPathTemplate 網(wǎng)關(guān)匹配的路徑
UpstreamHttpMethod 網(wǎng)關(guān)匹配的請求方法
DownstreamPathTemplate 服務匹配的路徑
DownstreamScheme 服務的Scheme,http、https
DownstreamHostAndPorts 服務的主機地址跟端口
上面的配置描述的意思是:把對網(wǎng)關(guān)的/api/hotel的GET請求轉(zhuǎn)發(fā)到主機http://localhost:6003/hotel接口上。
路由參數(shù)
Ocelot的path模板可以使用{param}模式來匹配參數(shù),然后傳遞到下游服務器上。
{//獲取單個酒店"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參數(shù)。
{//獲取酒店房間列表"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參數(shù)。
{//獲取查詢訂單"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}匹配參數(shù)。
限流
Ocelot支持對請求的限流操作。
"RateLimitOptions": {"EnableRateLimiting": true,"Period": "1s","PeriodTimespan": 1,"Limit": 1}在路由配置節(jié)點添加RateLimitOptions節(jié)點。
EnableRateLimiting = true 開啟限流
Period = 1s 限流的時間區(qū)間為1s
PeriodTimespan = 1 限流后重置時間
Limit = 1 限制請求的數(shù)量
上面的配置的意思是1秒內(nèi)限制一次請求,1秒后重置這個限制。
緩存
Ocelot可以對請求的響應值提供緩存服務。
//緩存5s"FileCacheOptions": { "TtlSeconds": 5 }在路由配置節(jié)點上配置FileCacheOptions字段,TtlSeconds代表需要緩存的時間,單位是秒。
聚合
上一回我們講微服務架構(gòu)的時候說到“聚合服務層”,我們說這一層的主要功能是對請求進行聚合適配跟裁剪。其實ocelot已經(jīng)提供了簡單的api聚合功能。如果聚合的需求比較簡單,那么可以使用ocelot直接實現(xiàn)。
簡單聚合
簡單聚合可以通過配置把幾個請求的聚合成一個請求,一次性返回幾個請求的響應。響應通過json格式被包裝返回。
"Aggregates": [{//聚合 查詢酒店信息跟酒店房間列表"RouteKeys": ["hotel_base_info","hotel_rooms"],"UpstreamPathTemplate": "/api/hotel_detail/{hotel_id}"},]RouteKeys 代表需要聚合的請求的鍵值。
使用代碼聚合
上面我們直接通過配置實現(xiàn)了api之間聚合請求。這種聚合比較簡單,會把聚合的幾個請求的響應值原封不動的返回回來。有的時候我們需要對返回值做一些轉(zhuǎn)換或者裁剪,比如同一個api我們對移動端的響應可能需要裁剪掉部分字段。這種需求在ocelot內(nèi)我們可以使用代碼來完成。
這里不太推薦這種聚合方式,這會造成網(wǎng)關(guān)跟下游服務的強耦合關(guān)系。
這里我們演示下如何把獲取酒店信息跟酒店房間列表的返回值進行裁剪,并返回一個新的響應。
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這個接口然后實現(xiàn)Aggregate方法。在這個方法內(nèi)對每個請求的響應值進行裁剪,然后重新組合。
{//聚合 查詢酒店信息跟酒店房間列表 移動端 裁剪"RouteKeys": ["hotel_base_info","hotel_rooms"],"UpstreamPathTemplate": "/api/m/hotel_detail/{hotel_id}","Aggregator": "HotelDetailInfoForMobileAggregator"}在配置文件的Aggregates內(nèi)添加一個配置節(jié)點在“Aggregator”字段上指定Aggregator的類名。
.ConfigureServices(s => {s.AddOcelot().AddTransientDefinedAggregator<HotelDetailInfoForMobileAggregator>();})同時在ConfigureServices方法內(nèi)配置HotelDetailInfoForMobileAggregator的依賴注入。
總結(jié)
本次我們通過幾個最簡單的web api項目,演示了如何使用 ocelot 網(wǎng)關(guān)進行反向代理,限流,聚合等常用功能。可以看到 ocelot 的配置使用還是比較簡單的。因為是 .net 代碼編寫,所以對.net 開發(fā)者比較友好,我們可以直接使用 .net 代碼來編寫一些功能,比如直接使用代碼來聚合請求的結(jié)果。
相關(guān)文章
.Net Core with 微服務 - 架構(gòu)圖
.NET Core with 微服務 - 什么是微服務
演示代碼
https://github.com/kklldog/myhotel_microservice
總結(jié)
以上是生活随笔為你收集整理的.Net Core with 微服务 - Ocelot 网关的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EF Core中高效批量删除、更新数据的
- 下一篇: WPF 记一个Popup踩坑记录