在一般的網際網路應用中限流是一個比較常見的場景,也有很多常見的方式可以實現對應用的限流比如通過令牌桶通過滑動視窗等等方式都可以實現,也可以在整個請求流程中進行限流比如客戶端限流就是在客戶端通過隨機數直接返回成功失敗來決定是否發起請求。也可以在閘道器層直接根據一定策略丟棄一部分流量達到限流的目的,亦可請求到業務端後由業務端判斷是否進行限流。而一般的service mesh框架會在代理的sidecar部分完成限流的工作。今天就講講dapr是如何通過簡易的配置來實現一個限流的。
目錄:
一、通過Dapr實現一個簡單的基於.net的微服務電商系統
二、通過Dapr實現一個簡單的基於.net的微服務電商系統(二)——通訊框架講解
三、通過Dapr實現一個簡單的基於.net的微服務電商系統(三)——一步一步教你如何擼Dapr
四、通過Dapr實現一個簡單的基於.net的微服務電商系統(四)——一步一步教你如何擼Dapr之訂閱釋出
五、通過Dapr實現一個簡單的基於.net的微服務電商系統(五)——一步一步教你如何擼Dapr之狀態管理
六、通過Dapr實現一個簡單的基於.net的微服務電商系統(六)——一步一步教你如何擼Dapr之Actor服務
七、通過Dapr實現一個簡單的基於.net的微服務電商系統(七)——一步一步教你如何擼Dapr之服務限流
附錄:(如果你覺得對你有用,請給個star)
一、電商Demo地址
Dapr限流包含兩種模式,一種是客戶端限流,一種是服務端限流。
客戶端限流簡單來講就是對下游服務的一種限流保護,舉個例子比如我的閘道器要保護後面的所有服務,我可以配置一個ratelimit的component下游限流注入到閘道器的sidecar中,Dapr會為流經該閘道器例項的流量的下游服務根據遠端IP和路徑進行限流,確保單位時間內最大請求數被限制在規定範圍之內。
下面我們就來模擬這種限流模式,首先我們建立一個型別為middleware.http.ratelimit的Component,其metadata可以設定一個maxRequestsPerSecond引數,代表每秒流經該sidecar的請求最大能通過多少前往下游服務。如果超出這個請求,則sidecar會直接返回一個429響應碼提示客戶端請求過多
apiVersion: dapr.io/v1alpha1 kind: Component metadata: name: ratelimit spec: type: middleware.http.ratelimit version: v1 metadata: - name: maxRequestsPerSecond value: 1
接著我們配置一個Configuration並注入到clientsample的deployment中(注意紅字部分)
apiVersion: dapr.io/v1alpha1 kind: Configuration metadata: name: appconfig spec: httpPipeline: handlers: - name: ratelimit type: middleware.http.ratelimit
apiVersion: apps/v1 kind: Deployment metadata: name: clientsample labels: app: clientsample spec: replicas: 1 selector: matchLabels: app: clientsample template: metadata: labels: app: clientsample annotations: dapr.io/enabled: "true" dapr.io/app-id: "clientsample" dapr.io/app-port: "80" dapr.io/config: "appconfig" spec: containers: - name: web image: clientsample:release imagePullPolicy: Never ports: - containerPort: 80
現在我們在程式碼中,讓clientsample通過多現成同時發起10個下游請求
public async Task<dynamic> Call() { var result1 = new OutDto(); var remoteService = serviceProxyFactory.CreateProxy<IHelloService>(); var tasks = new Task<OutDto>[10]; for (int i = 0; i < 10; i++) { tasks[i] = remoteService.HelloWorldByName(new InputDto() { Name = "xiaoming" }); } await Task.WhenAll(tasks); foreach (var item in tasks) { Console.WriteLine($"result is :{(item.GetAwaiter().GetResult().Word ?? "noresult")}"); } return "操作完成"; }
可以看到併發訪問10條task,只有1條返回了result,其餘的請求傳送到自己的sidecar後就直接返回429然後被通訊框架捕獲429後拋到日誌中。
接下來我們看看服務端模式,服務端模式顧名思義就是保護自己,確保所有流向自己的請求會以一個限定頻率被處理,有點類似於C#的semaphore,通過訊號量來阻塞執行緒併發訪問數。注意該模式並不是通過限制每秒流量來實現的,而是指同時只能有多個請求被處理。
接著我們看看yaml需要調整的部分,通過dapr.io/app-max-concurrency引數即可實現併發數控制
apiVersion: apps/v1 kind: Deployment metadata: name: servicesample labels: app: servicesample spec: replicas: 1 selector: matchLabels: app: servicesample template: metadata: labels: app: servicesample annotations: dapr.io/enabled: "true" dapr.io/app-id: "servicesample" dapr.io/app-port: "80" dapr.io/app-max-concurrency: "1" spec: containers: - name: web image: servicesample:release imagePullPolicy: Never ports: - containerPort: 80
clientsample不用修改,我們在servicesample的方法中增加以下模擬耗時操作
重新生成後(注意需要刪除之前配置在clientsample上的dapr.io/config),我們通過postman模擬發起請求:
可以看到請求全部都執行成功並獲取回撥了,但是整個請求耗時是10秒,恰好就是一次處理1個請求,單個請求耗時1秒得到的結果,我們可以再次驗證一下將app-max-concurrency設定為2,應該會5秒請求完畢:
可以看到訊號量每次放入了兩個執行緒同步處理,我們的請求確實被壓縮到了5秒處理完畢。
整個限流其實分為下游限流+併發控制兩種方式,其實都是為了保護自己/下游服務。另外大家注意一下如果你的請求並不是通過sidecar進入到服務的比如直接暴露服務埠到network或通過ingress+service的方式訪問應用也就是說流量不走sidecar,則無法通過dapr進行限流!關於限流今天就到這,下次分享一下如何做鏈路監控~