在上一篇文章里,我們通過注入sentinel component到apigateway實現了對下游服務的保護,不過受限於目前變更component需要人工的重新注入配置以及重啟應用更新component等等原因,對於真實的環境運維稍有難度,最近我根據sentinel-golang相關文檔重新編寫了一個動態配置的功能並集成到了我們的電商demo管理端,今天就講解並演示一下它是如何工作的。
目錄:
一、通過Dapr實現一個簡單的基於.net的微服務電商系統
二、通過Dapr實現一個簡單的基於.net的微服務電商系統(二)——通訊框架講解
三、通過Dapr實現一個簡單的基於.net的微服務電商系統(三)——一步一步教你如何擼Dapr
四、通過Dapr實現一個簡單的基於.net的微服務電商系統(四)——一步一步教你如何擼Dapr之訂閱發布
五、通過Dapr實現一個簡單的基於.net的微服務電商系統(五)——一步一步教你如何擼Dapr之狀態管理
六、通過Dapr實現一個簡單的基於.net的微服務電商系統(六)——一步一步教你如何擼Dapr之Actor服務
七、通過Dapr實現一個簡單的基於.net的微服務電商系統(七)——一步一步教你如何擼Dapr之服務限流
八、通過Dapr實現一個簡單的基於.net的微服務電商系統(八)——一步一步教你如何擼Dapr之鏈路追蹤
九、通過Dapr實現一個簡單的基於.net的微服務電商系統(九)——一步一步教你如何擼Dapr之OAuth2授權 && 百度版Oauth2
十、通過Dapr實現一個簡單的基於.net的微服務電商系統(十)——一步一步教你如何擼Dapr之綁定
十一、通過Dapr實現一個簡單的基於.net的微服務電商系統(十一)——一步一步教你如何擼Dapr之自動擴/縮容
十二、通過Dapr實現一個簡單的基於.net的微服務電商系統(十二)——istio+dapr構建多運行時服務網格
十三、通過Dapr實現一個簡單的基於.net的微服務電商系統(十三)——istio+dapr構建多運行時服務網格之生產環境部署
十四、通過Dapr實現一個簡單的基於.net的微服務電商系統(十四)——開發環境容器調試小技巧
十五、通過Dapr實現一個簡單的基於.net的微服務電商系統(十五)——集中式接口文檔實現
十六、通過Dapr實現一個簡單的基於.net的微服務電商系統(十六)——dapr+sentinel中間件實現服務保護
十七、通過Dapr實現一個簡單的基於.net的微服務電商系統(十七)——服務保護之動態配置與熱重載
十八、通過Dapr實現一個簡單的基於.net的微服務電商系統(十八)——服務保護之多級緩存
十九、通過Dapr實現一個簡單的基於.net的微服務電商系統(十九)——分布式事務之Saga模式
二十、通過Dapr實現一個簡單的基於.net的微服務電商系統(二十)——Saga框架實現思路分享
附錄:(如果你覺得對你有用,請給個star)
一、電商Demo地址
首先我們看看最終效果如何,重新拉取代碼並rebuild之后,登錄admin.dapreshop.com:30882在基礎配置新增了兩個模塊,其中swagger文檔只是簡單的對系列15文章中創建的集中式文檔的簡易集成。服務保護配置就是本次新增的部分了,其界面如下:
當我們需要保護某個接口時,點擊新增限流規則,並通過下拉選擇我們的服務+路徑即可配置一個規則,點擊保存並重啟網關會自動調用k8s進行component的重載並重啟apigateway。
在稍微等待20秒左右網關重啟后(亦可通過使用kubectl get po -n dapreshop | findstr apigateway觀察網關重啟)即可通過並發測試來看看其效果。可以看到正確的對我們的接口產生了保護,也就是10秒內產生了100次左右的有效訪問,剩余的訪問被攔截並返回了429請求過多。
在dapr的middleware-sentinel文檔中可以看到還支持熔斷降級、並發隔離、熱點參數等等規則,不過目前測試過發現僅有服務限流規則拒絕類型的限流對dapr有效,其他規則暫時沒有效果,不知道是不是dapr1.2的bug還是什么情況,已經github提了issuesl...
下面簡單講講如何實現熱更新的。首先我們需要在apigateway注入一個空的sentinel config component:
apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: sentinel namespace: dapreshop spec: type: middleware.http.sentinel version: v1 metadata: - name: appName value: "rules" - name: logDir value: "/tmp" - name: flowRules value: >- [] - name: circuitbreakerRules value: >- []
接着我在publicservice實現了熱更新相關代碼,具體代碼在PublicService\Infrastructure\Common\AliSentinel中。通過引入了KubernetesClient的方式通過sdk操作component的讀寫以及deployment的更新。核心代碼如下:
static Kubernetes kubernetes = new Kubernetes(KubernetesClientConfiguration.BuildConfigFromConfigFile(SentinelComponentBaseConfig.kubeconfig)); /// <summary> /// 注冊規則 /// </summary> /// <param name="aliSentinelConfig"></param> public static async Task RegisterSentinelConfig(SentinelConfigList aliSentinelConfigList) { await GetAndSaveSentinelComponent(component => { component.FlowRules = aliSentinelConfigList.FlowRules.GetDistinct(); component.BreakingRules = aliSentinelConfigList.BreakingRules.GetDistinct(); }); } /// <summary> /// 獲取所有注冊規則 /// </summary> /// <returns></returns> public static async Task<SentinelConfigList> GetAll() { var component = await GetDefaultSentinelComponent(); return new SentinelConfigList() { FlowRules = component.FlowRules, BreakingRules = component.BreakingRules }; } #region 本地方法 /// <summary> /// 獲取默認的SentinelComponent /// </summary> /// <returns></returns> static async Task<SentinelComponent> GetDefaultSentinelComponent() { var component = new SentinelComponent(); await component.Create(kubernetes); return component; } /// <summary> /// 傳遞委托變更默認SentinelComponent /// </summary> /// <param name="operatorComponent"></param> static async Task GetAndSaveSentinelComponent(Action<SentinelComponent> operatorComponent) { var component = await GetDefaultSentinelComponent(); operatorComponent(component); component.SetMetaData(); Patch(component); ReloadDeploy(); } /// <summary> /// Patch SentinelComponent到k8s環境 /// </summary> /// <param name="component"></param> static void Patch(SentinelComponent component) { var patch = new JsonPatchDocument<SentinelComponent>(); patch.Replace(x => x.spec.metadata, component.spec.metadata); kubernetes.PatchNamespacedCustomObject(new V1Patch(patch, V1Patch.PatchType.JsonPatch), SentinelComponentBaseConfig.Group, SentinelComponentBaseConfig.Version, SentinelComponentBaseConfig.NamespaceParameter, SentinelComponentBaseConfig.Plural, SentinelComponentBaseConfig.ComponentName); } /// <summary> /// 重啟相關deploy更新SentinelComponent /// </summary> static void ReloadDeploy() { var deploy = kubernetes.ReadNamespacedDeployment(SentinelComponentBaseConfig.DeploymentName, SentinelComponentBaseConfig.NamespaceParameter); deploy.Spec.Template.Metadata.Annotations[SentinelComponentBaseConfig.restart] = DateTime.UtcNow.ToString("s"); var patch = new JsonPatchDocument<V1Deployment>(); patch.Replace(e => e.Spec.Template.Metadata.Annotations, deploy.Spec.Template.Metadata.Annotations); kubernetes.PatchNamespacedDeployment(new V1Patch(patch, V1Patch.PatchType.JsonPatch), SentinelComponentBaseConfig.DeploymentName, SentinelComponentBaseConfig.NamespaceParameter); }
接着我們在application暴露兩個接口用於get component和save component。在頁面上接入相關接口后即可正確的讀取和寫入component並滾動更新相關k8s資源從而實現熱更新。整個限流流程大致如下:
好了,今天的分享就到這里,照例歡迎fork+star~