通過Dapr實現一個簡單的基於.net的微服務電商系統(十七)——服務保護之動態配置與熱過載

a1010發表於2021-06-11

  在上一篇文章裡,我們通過注入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的微服務電商系統(十七)——服務保護之動態配置與熱過載
附錄:(如果你覺得對你有用,請給個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~

相關文章