通过Dapr实现一个简单的基于.net的微服务电商系统(七)——一步一步教你如何撸Dapr之服务限流...

  在一般的互联网应用中限流是一个比较常见的场景,也有很多常见的方式可以实现对应用的限流比如通过令牌桶通过滑动窗口等等方式都可以实现,也可以在整个请求流程中进行限流比如客户端限流就是在客户端通过随机数直接返回成功失败来决定是否发起请求。也可以在网关层直接根据一定策略丢弃一部分流量达到限流的目的,亦可请求到业务端后由业务端判断是否进行限流。而一般的service mesh框架会在代理的sidecar部分完成限流的工作。今天就讲讲dapr是如何通过简易的配置来实现一个限流的。

目录:

一、通过Dapr实现一个简单的基于.net的微服务电商系统

二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

四、通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务

附录:(如果你觉得对你有用,请给个star)
一、电商Demo地址:https://github.com/sd797994/Oxygen-Dapr.EshopSample

二、通讯框架地址:https://github.com/sd797994/Oxygen-Dapr

  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进行限流!关于限流今天就到这,下次分享一下如何做链路监控~

相关文章:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Dapr pubsub 中,可通过在订阅函数中添加幂等性判断来解决重复消费的问题。以下是一个基于 .NET 6 的幂等性实现示例: ```csharp using Dapr.Client; using Dapr.Client.Autogen.Grpc.v1; using Dapr.Client.Autogen.Protos; using Google.Protobuf; using Grpc.Core; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; namespace DaprPubSubDemo.Controllers { [ApiController] [Route("[controller]")] public class DaprPubSubController : ControllerBase { private readonly DaprClient _daprClient; public DaprPubSubController(DaprClient daprClient) { _daprClient = daprClient; } [HttpPost("subscribe")] public async Task<ActionResult> Subscribe([FromBody] CloudEvent cloudEvent) { // 获取消息 ID string messageId = cloudEvent.Id; // 判断消息是否已处理过 if (await _daprClient.GetStateEntryAsync<bool>("message", messageId) == false) { // 标记消息已处理 await _daprClient.SaveStateAsync("message", messageId, true); // 处理消息 // TODO: 在此处添加具体的消息处理逻辑 } return Ok(); } } } ``` 上述代码中,我们使用 Dapr 的状态管理功能来实现幂等性判断。在订阅函数中,首先获取消息的 ID,然后通过调用 `GetStateEntryAsync` 方法获取该消息的处理状态。如果消息尚未处理,则标记消息已处理,并执行具体的消息处理逻辑;否则,直接返回结果。 需要注意的是,上述代码中的状态存储使用的是 Dapr 默认的状态存储,可以根据实际需求进行修改。同时,还需要注意保证状态存储的可靠性和一致性,以确保幂等性判断的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值