Service Fabric 與 Ocelot 集成
概要
雲應用程序通常都需要使用前端網關,為用戶、設備或其他應用程序提供同一個入口點。 在 Service Fabric 中,網關可以是任意無狀態服務(如 ASP.NET Core 應用程序) 。
本文介紹了如何將Ocelot用作 Service Fabric 應用程序的網關。Ocelot直接與 Service Fabric 集成,以便可以使用一組豐富的路由規則向後端 Service Fabric 服務發布 API。
架構
常見 Service Fabric 體系結構使用單頁 Web 應用程序,向公開 HTTP API 的後端服務發出 HTTP 調用請求。
隨著應用程序越來越復雜,必須向大量後端服務發布API的網關亦是如此。Ocelot旨在通過路由規則、訪問控制、速率限制、監視、事件日誌記錄和響應緩存來處理復雜 API,最大限度地減少用戶需要執行的操作。 Ocelot支持 Service Fabric 服務發現、分區解析和副本選擇,從而智能地將請求直接路由到 Service Fabric 中的後端服務,用戶無需編寫自己的無狀態 API 網關。
應用程序方案
Service Fabric 中的服務可以是無狀態服務,也可以是有狀態服務,可采用以下三種方案之一進行分區:單獨分區、Int64 範圍分區和已命名分區。 必須確定特定服務實例的具體分區,才能解析服務終結點。解析服務終結點時,必須指定服務實例名稱(例如,fabric:/myapp/myservice)以及服務的具體分區,但單獨分區情況除外。
Ocelot可與無狀態服務、有狀態服務和任何分區方案的任意組合配合使用。
https://ocelot.readthedocs.io/en/latest/features/servicefabric.html
如果您正在使用無狀態/Guest服務,則ocelot將能夠通過命名服務進行代理而無需其他任何操作。但是,如果您正在使用有狀態服務/ actor服務,則必須使用客戶端請求發送PartitionKind和PartitionKey查詢字符串值。
以下示例展示如何設置一個ReRoute以便在在Service Fabric中工作。 最重要的是ServiceName,它由Service Fabric應用程序名稱和特定服務名稱組成的。 我們還需要將UseServiceDiscovery設置為true,並在GlobalConfiguration中設置ServiceDiscoveryProvider。 這裏的例子顯示了一個典型的配置。 它假定Service Fabric在本地主機上運行,並且命名服務位於19081端口上。
{
"ReRoutes": [
{
"DownstreamPathTemplate": "/api/values",
"UpstreamPathTemplate": "/servicea/api/values",
"UpstreamHttpMethod": [ "Get"],
"DownstreamScheme": "http",
"ServiceName": "NanoFabric_ServiceFabric/ServiceA",
"UseServiceDiscovery": true,
"AuthenticationOptions": {
"AuthenticationProviderKey": "apikey",
"AllowedScopes": []
},
"AddHeadersToRequest": {
"claims_City": "Claims[City] > value > |",
"claims_State": "Claims[State] > value > |"
},
"QoSOptions": {
"ExceptionsAllowedBeforeBreaking": 3,
"DurationOfBreak": 10,
"TimeoutValue": 5000
}
},
{
"DownstreamPathTemplate": "/{route}",
"UpstreamPathTemplate": "/serviceoauth/{route}",
"UpstreamHttpMethod": [ "Get", "Options", "Post" ],
"DownstreamScheme": "http",
"ServiceName": "NanoFabric_ServiceFabric/ServiceOAuth",
"UseServiceDiscovery": true
}
],
"GlobalConfiguration": {
"RequestIdKey": "OcRequestId",
"AdministrationPath": "/administration",
"ServiceDiscoveryProvider": {
"Host": "localhost",
"Port": 19081,
"Type": "ServiceFabric"
}
}
}
原理就是借助 Service Fabric 中內置的反向代理,Service Fabric 群集中運行的微服務可以發現包含 http 終結點的其他服務,並與之通信。
微服務通信模型
Service Fabric 中的微服務在群集中的部分節點上運行,可以出於各種原因在這些節點之間遷移。 因此,微服務的終結點可能會動態變化。 若要發現群集中的其他服務並與之通信,微服務必須完成以下步驟:
l 通過命名服務解析服務位置。
l 連接到服務。
l 在實現服務解析以及在發生連接故障時應用的重試策略的循環中,包裝上述步驟
使用反向代理通信
反向代理是在每個節點上運行的服務,用於代表客戶端服務處理終結點解析、自動重試及其他連接故障。 可以將反向代理配置為,一邊處理客戶端服務的請求,一邊應用各種策略。 借助反向代理,客戶端服務可以使用任意客戶端 HTTP 通信庫,無需服務中有特殊的解析和重試邏輯。
反向代理在本地節點上公開一個或多個終結點,以供客戶端服務用來向其他服務發送請求。
反向代理使用特定的統一資源標識符 (URI) 格式來識別傳入請求應該轉發到的服務分區:
http(s)://<Cluster FQDN | internal IP>:Port/<ServiceInstanceName>/<Suffix path>?PartitionKey=<key>&PartitionKind=<partitionkind>&ListenerName=<listenerName>&TargetReplicaSelector=<targetReplicaSelector>&Timeout=<timeout_in_seconds>
l http(s): 可以將反向代理配置為接受 HTTP 或 HTTPS 流量。 對於 HTTPS 轉發,在設置反向代理偵聽 HTTPS 後,請參閱使用反向代理連接到安全服務。
l 群集的完全限定域名 (FQDN) | 內部 IP: 對於外部客戶端,可以配置反向代理,以便可以通過群集域(例如 mycluster.eastus.cloudapp.azure.com)訪問反向代理。 默認情況下,反向代理在每個節點上運行。 對於內部流量,可在本地主機或任意內部節點 IP(例如 10.0.0.1)上訪問反向代理。
l Port:為反向代理指定的端口,例如 19081。
l ServiceInstanceName: 在不使用“fabric:/”方案的情況下嘗試訪問的已部署服務實例的完全限定名稱。 例如,若要訪問 fabric:/myapp/myservice/ 服務,可以使用 myapp/myservice。
l 服務實例名稱要區分大小寫。 若 URL 中的服務實例名稱大小寫不同,則會導致請求失敗,並顯示 404(未找到)。
l 後綴路徑: 要連接到的服務的實際 URL 路徑,例如 myapi/values/add/3。
l PartitionKey: 對於分區服務,這是針對要訪問的分區計算出的分區鍵。 請註意,這不是分區 ID GUID。 對於使用單獨分區方案的服務,此參數不是必需的。
l PartitionKind: 服務分區方案。 該方案可以是“Int64Range”或“Named”。 對於使用單獨分區方案的服務,此參數不是必需的。
l ListenerName 服務中的終結點采用以下形式:{"Endpoints":{"Listener1":"Endpoint1","Listener2":"Endpoint2" ...}}。 當服務公開了多個終結點時,此參數標識應將客戶端請求轉發到的終結點。 如果服務只有一個偵聽器,則可以省略此項。
l TargetReplicaSelector 這指定應當如何選擇目標副本或實例。
l 當目標服務為有狀態服務時,TargetReplicaSelector 可以是下列其中一項:“PrimaryReplica”、“RandomSecondaryReplica”或“RandomReplica”。 如果未指定此參數,默認值為“PrimaryReplica”。
l 當目標服務為無狀態服務時,反向代理將選擇服務分區的一個隨機實例來將實例轉發到其中。
l Timeout: 此參數指定反向代理針對服務創建的 HTTP 請求(代表客戶端請求)的超時。 默認值為 60 秒。 這是一個可選參數
Ocelot充當微服務和外部客戶端之間的網絡邊界,可以進行網絡地址轉換並將外部請求轉發到內部的 IP:端口終結點。 要允許外部客戶端直接訪問微服務的終結點,必須先將Ocelot配置為將流量轉發到群集中服務使用的每個端口。 另外,大多數微服務(尤其是有狀態微服務)並不駐留在群集的所有節點上。 這些微服務在故障轉移時可在節點之間移動。 在這種情況下,負載均衡器無法有效確定要將流量轉發到的副本的目標節點位置。
可以在Ocelot中直接配置反向代理的端口,而無需配置單個服務的端口。 這種配置可讓群集外部的客戶端使用反向代理訪問群集內部的服務,無需經過額外的配置。
通過Ocelot可從群集外部訪問群集中公開 HTTP 終結點的所有微服務。 這意味著微服務設計為內部的可能會被確定的惡意用戶發現。這潛在地提供可被利用的嚴重漏洞;例如:
惡意用戶可以通過反復調用沒有足夠強化的攻擊面的內部服務來發起拒絕服務攻擊。
惡意用戶可能會將格式錯誤的數據包傳送到內部服務,從而導致意外行為。
設計為內部的服務可能會返回不應公開給群集外部的服務的私有或敏感信息,從而將此敏感信息泄露給惡意用戶。在網關上開啟身份認證、流控等措施來解決安全問題。
反向代理是一種可選的 Azure Service Fabric 服務,有助於在 Service Fabric 群集中運行的微服務發現包含 http 終結點的其他服務,並與之通信,在創建新的 Service Fabric 群集時,Azure 門戶提供了一個啟用反向代理的選項。 無法通過門戶升級現有群集來使用反向代理。
我們的示例項目
我們的示例項目代碼放在 https://github.com/geffzhang/NanoFabric-ServiceFabric ,解決方案中包含了一個後端服務ServiceA,是個無狀態的服務,一個Ocelot 網關和一個Identity Server 4的認證服務,在網關上集成了IdentityServer4 的認證服務 ,由網關負責認證,認證完成將Claims 轉換為HttpHeader 中轉發到下遊服務。
服務實例A是一個無狀態的服務
我們將其配置為運行2個實例。在Application Parameters中,我將* _InstanceCount參數值設置為2:
讓Service Fabric選擇端口,我們將從端點中刪除該Port
屬性:
當開發機器上的無法實現在同一端口上運行多個實例,如果填寫了Port 屬性,_InstanceCount只能保持為1. 讓端口保持動態,我們可以在本地實現服務的伸縮。
部署自己的網關
部署自己的網關這聽起來像是需要做很多工作,實際上非常簡單。我們需要與反向代理相同的行為,只需要更多的控制。在我們這個開源的開發的世界,這個問題已經解決了,我們有開源的API網關Ocelot http://threemammals.com/ocelot ,而且做得非常好,可以完美的和Service Fabric 一起工作。
我們將添加一個新的空aspnet core無狀態服務
讓我們配置我們的端點。您需要知道我們的網關在哪裏,所以我們給它一個特定的端口。在ServiceManifest中,設置端點的端口:
<Endpoint Protocol="http" Name="ServiceEndpoint" Type="Input" Port="8492" />
網關是系統的入口點,必須保持可用狀態。我們在每個節點上部署它。修改ApplicationParameters中添加的參數NanoFabricGateway_InstanceCount。確保生產部署的值為-1。
<Parameter Name="NanoFabricGateway_InstanceCount" Value="-1"/>
請註意,如果部署到本地群集,則無法在同一端口上運行多個服務實例。對於本地開發群集,要麽將其保留為1,要麽讓端口為動態。
代碼實現上並沒有什麽特殊的地方。
配置Azure負載均衡器
當然,沒有人想通過端口訪問您的網站8492。接下來,我們需要設置負載均衡器以指向我們新部署的網關。在部署在azure上的新集群(可以參考這篇文章使用Powershell https://noelbundick.com/posts/service-fabric-cluster-quickstart/ ),現有AppPortLBRule1端口80。編輯它,並將後端端口更改為指向網關端口:8492在我的情況下。同時請註意,Load Balancer定義了一個Health Probe。如果健康檢測未成功,則負載均衡器將假定後端服務池不健康,並且不會重定向您的請求。
參考文章:
https://docs.microsoft.com/zh-cn/azure/service-fabric/service-fabric-api-management-overview
https://blog.geist.no/azure-service-fabric-introduction-part-2-our-endpoint-a-webapi-based-stateless-service/
https://ocelot.readthedocs.io/en/latest/features/servicefabric.html
Service Fabric 與 Ocelot 集成