ExecuteLimitFilter

ExecuteLimitFilter ,在服务提供者,通过 <dubbo:service /> 的 "executes" 统一配置项开启:

表示每服务的每方法最大可并行执行请求数。

ExecuteLimitFilter是通过信号量来实现的对服务端的并发数的控制。

ExecuteLimitFilter执行流程:

  1. 首先会去获得服务提供者每服务每方法最大可并行执行请求数
  2. 如果每服务每方法最大可并行执行请求数大于零,那么就基于基于服务 URL + 方法维度获取一个RpcStatus实例
  3. 通过RpcStatus实例获取一个信号量,若果获取的这个信号量调用tryAcquire返回false,则抛出异常
  4. 如果没有抛异常,那么久调用RpcStatus静态方法beginCount,给这个 URL + 方法维度开始计数
  5. 调用服务
  6. 调用结束后计数调用RpcStatus静态方法endCount,计数结束
  7. 释放信号量

ExecuteLimitFilter

  1. @Override
  2. public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
  3. URL url = invoker.getUrl();
  4. String methodName = invocation.getMethodName();
  5. Semaphore executesLimit = null;
  6. boolean acquireResult = false;
  7. int max = url.getMethodParameter(methodName, Constants.EXECUTES_KEY, 0);
  8. if (max > 0) {
  9. RpcStatus count = RpcStatus.getStatus(url, invocation.getMethodName());
  10. // if (count.getActive() >= max) {
  11. /**
  12. * http://manzhizhen.iteye.com/blog/2386408
  13. * use semaphore for concurrency control (to limit thread number)
  14. */
  15. executesLimit = count.getSemaphore(max);
  16. if(executesLimit != null && !(acquireResult = executesLimit.tryAcquire())) {
  17. throw new RpcException("Failed to invoke method " + invocation.getMethodName() + " in provider " + url + ", cause: The service using threads greater than <dubbo:service executes=\"" + max + "\" /> limited.");
  18. }
  19. }
  20. long begin = System.currentTimeMillis();
  21. boolean isSuccess = true;
  22. RpcStatus.beginCount(url, methodName);
  23. try {
  24. Result result = invoker.invoke(invocation);
  25. return result;
  26. } catch (Throwable t) {
  27. isSuccess = false;
  28. if (t instanceof RuntimeException) {
  29. throw (RuntimeException) t;
  30. } else {
  31. throw new RpcException("unexpected exception when ExecuteLimitFilter", t);
  32. }
  33. } finally {
  34. RpcStatus.endCount(url, methodName, System.currentTimeMillis() - begin, isSuccess);
  35. if(acquireResult) {
  36. executesLimit.release();
  37. }
  38. }
  39. }

我们接下来看看RpcStatus这个类

  1. private static final ConcurrentMap<String, ConcurrentMap<String, RpcStatus>> METHOD_STATISTICS = new ConcurrentHashMap<String, ConcurrentMap<String, RpcStatus>>();
  2. public static RpcStatus getStatus(URL url, String methodName) {
  3. String uri = url.toIdentityString();
  4. ConcurrentMap<String, RpcStatus> map = METHOD_STATISTICS.get(uri);
  5. if (map == null) {
  6. METHOD_STATISTICS.putIfAbsent(uri, new ConcurrentHashMap<String, RpcStatus>());
  7. map = METHOD_STATISTICS.get(uri);
  8. }
  9. RpcStatus status = map.get(methodName);
  10. if (status == null) {
  11. map.putIfAbsent(methodName, new RpcStatus());
  12. status = map.get(methodName);
  13. }
  14. return status;
  15. }

这个方法很简单,大概就是给RpcStatus这个类里面的静态属性METHOD_STATISTICS里面设值。外层的map是以url为key,里层的map是以方法名为key。

  1. private volatile int executesPermits;
  2. public Semaphore getSemaphore(int maxThreadNum) {
  3. if(maxThreadNum <= 0) {
  4. return null;
  5. }
  6. if (executesLimit == null || executesPermits != maxThreadNum) {
  7. synchronized (this) {
  8. if (executesLimit == null || executesPermits != maxThreadNum) {
  9. executesLimit = new Semaphore(maxThreadNum);
  10. executesPermits = maxThreadNum;
  11. }
  12. }
  13. }
  14. return executesLimit;
  15. }

这个方法是获取信号量,如果这个实例里面的信号量是空的,那么就添加一个,如果不是空的就返回。

TPSLimiter

TpsLimitFilter 过滤器,用于服务提供者中,提供限流的功能。

配置方式:

  1. 通过 <dubbo:parameter key="tps" value="" /> 配置项,添加到 <dubbo:service /> 或 <dubbo:provider /> 或 <dubbo:protocol /> 中开启,例如:
  1. dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoServiceImpl" protocol="injvm" >
  2. <dubbo:parameter key="tps" value="100" />
  3. </dubbo:service>
  1. 通过 <dubbo:parameter key="tps.interval" value="" /> 配置项,设置 TPS 周期。

源码分析

TpsLimitFilter

  1. private final TPSLimiter tpsLimiter = new DefaultTPSLimiter();
  2. @Override
  3. public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
  4. if (!tpsLimiter.isAllowable(invoker.getUrl(), invocation)) {
  5. throw new RpcException(
  6. "Failed to invoke service " +
  7. invoker.getInterface().getName() +
  8. "." +
  9. invocation.getMethodName() +
  10. " because exceed max service tps.");
  11. }
  12. return invoker.invoke(invocation);
  13. }

invoke方法调用了DefaultTPSLimiter的isAllowable,我们进入到isAllowable方法看一下

DefaultTPSLimiter

  1. private final ConcurrentMap<String, StatItem> stats
  2. = new ConcurrentHashMap<String, StatItem>();
  3. @Override
  4. public boolean isAllowable(URL url, Invocation invocation) {
  5. //获取tps这个参数设置的大小
  6. int rate = url.getParameter(Constants.TPS_LIMIT_RATE_KEY, -1);
  7. //获取tps.interval这个参数设置的大小,默认60秒
  8. long interval = url.getParameter(Constants.TPS_LIMIT_INTERVAL_KEY,
  9. Constants.DEFAULT_TPS_LIMIT_INTERVAL);
  10. String serviceKey = url.getServiceKey();
  11. if (rate > 0) {
  12. StatItem statItem = stats.get(serviceKey);
  13. if (statItem == null) {
  14. stats.putIfAbsent(serviceKey,
  15. new StatItem(serviceKey, rate, interval));
  16. statItem = stats.get(serviceKey);
  17. }
  18. return statItem.isAllowable();
  19. } else {
  20. StatItem statItem = stats.get(serviceKey);
  21. if (statItem != null) {
  22. stats.remove(serviceKey);
  23. }
  24. }
  25. return true;
  26. }

若要限流,调用 StatItem#isAllowable(url, invocation) 方法,根据 TPS 限流规则判断是否限制此次调用。

StatItem

  1. private long lastResetTime;
  2. private long interval;
  3. private AtomicInteger token;
  4. private int rate;
  5. public boolean isAllowable() {
  6. long now = System.currentTimeMillis();
  7. // 若到达下一个周期,恢复可用种子数,设置最后重置时间。
  8. if (now > lastResetTime + interval) {
  9. token.set(rate);// 回复可用种子数
  10. lastResetTime = now;// 最后重置时间
  11. }
  12. // CAS ,直到或得到一个种子,或者没有足够种子
  13. int value = token.get();
  14. boolean flag = false;
  15. while (value > 0 && !flag) {
  16. flag = token.compareAndSet(value, value - 1);
  17. value = token.get();
  18. }
  19. return flag;
  20. }

dubbo是如何控制并发数和限流的?的更多相关文章

  1. WCF之并发,吞吐量和限流

    并发 Single重入模式.对于每一个服务实例,同一时刻只能处理一个请求,其他对该实例的请求被排队. PerCall,每一线程会分配一个新的服务实例上.不会有并发性问题.不影响吞吐量. PerSess ...

  2. spring控制并发数的工具类ConcurrencyThrottleSupport和ConcurrencyThrottleInterceptor

    官方文档: /** * Support class for throttling concurrent access to a specific resource. * * <p>Desi ...

  3. Sentinel整合Dubbo限流实战(分布式限流)

    之前我们了解了 Sentinel 集成 SpringBoot实现限流,也探讨了Sentinel的限流基本原理,那么接下去我们来学习一下Sentinel整合Dubbo及 Nacos 实现动态数据源的限流 ...

  4. 微服务架构 | 5.2 基于 Sentinel 的服务限流及熔断

    目录 前言 1. Sentinel 基础知识 1.1 Sentinel 的特性 1.2 Sentinel 的组成 1.3 Sentinel 控制台上的 9 个功能 1.4 Sentinel 工作原理 ...

  5. 阿里巴巴开源限流组件Sentinel初探

    1 Sentinel主页 https://github.com/alibaba/Sentinel/wiki/主页 1.1 Sentinel介绍 随着微服务的流行,服务和服务之间的稳定性变得越来越重要. ...

  6. 高可用服务设计之二:Rate limiting 限流与降级

    <高可用服务设计之二:Rate limiting 限流与降级> <nginx限制请求之一:(ngx_http_limit_conn_module)模块> <nginx限制 ...

  7. 基于Redis的限流系统的设计

    本文讲述基于Redis的限流系统的设计,主要会谈及限流系统中限流策略这个功能的设计:在实现方面,算法使用的是令牌桶算法来,访问Redis使用lua脚本.   1.概念 In computer netw ...

  8. Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵动态限流规则

    Spring Cloud Alibaba | Sentinel:分布式系统的流量防卫兵动态限流规则 前面几篇文章较为详细的介绍了Sentinel的使用姿势,还没看过的小伙伴可以访问以下链接查看: &l ...

  9. SpringCloudGateWay之限流

    一.引言在高并发系统中,经常需要限制系统中的电流化妆.一方面是防止大量的请求使服务器过载,导致服务不可用,另一方面是防止网络攻击.常用的限流方法,如hystrix.应用线程池隔离.超过线程池的负载和g ...

随机推荐

  1. Mac上PyCharm运行多进程报错的解决方案

    Mac上PyCharm运行多进程报错的解决方案 运行时报错 may have been in progress in another thread when fork() was called. We ...

  2. 通过phpmyadmin设置数据库密码后若出现phpmyadmin拒绝访问的情况

    方法一:可以修改config.inc.php配置文件中的$cfg['Servers'][$i]['password'] = '你的密码'; 方法二:将config.inc.php配置文件中的$cfg[ ...

  3. python查询elasticsearch(Query DSL) 实例

    import datetime import sys import getopt import hashlib from elasticsearch import Elasticsearch &quo ...

  4. ORACLE导入数据库详细步骤

    登录PLSQL 点击然后打开命令窗口执行命令 创建表空间(红色字体是你需要创建表空间的地址,蓝色的是表空间大小) create temporary tablespace ZJY_TEMP tempfi ...

  5. URL的命名和反向解析

    1. 分组 url(r'^del_publisher/(\d+)', views.del_publisher), 匹配到参数,按照位置参数的方式传递给视图函数 视图函数需要定义形参接收变量 2. 命名 ...

  6. S7-1200与S7-200 通信西门子链接

    只要这两从站的通讯格式时一样的,而且都为modbus rtu格式的话,是可以走modbus通讯.你在用主站在编程时直接调用modbus rtu通讯库.同时200做为从站,在程序里面将从站的程序写好. ...

  7. Python Day_1

    听说Python很6,然后我的偶像小甲鱼竟然在6年前(现在2019年)就开始制作Python的教程,而前不久(世界读书日前一个星期左右),京东有活动,小甲鱼的Python第一版本打折,顺便买了本(还凑 ...

  8. Linux命令学习-ps命令

    Linux中,ps命令的全称是process status,即进程状态的意思,主要作用是列出系统中当前正在运行的进程信息. ps命令的功能很强大,参数也非常多,下面只举几个简单的实例. 显示所有进程信 ...

  9. GreenPlum完全安装_GP5.11.3完整安装

    1 概述 1.1 背景 1.2 目标 1.3 使用对象 2 配置系统信息 2.1 配置系统信息,做安装Greenplum的准备工作 Greenplum 数据库版本5.11.3 2.1.1 Greenp ...

  10. vue源码阅读(二)

    一 一个实例 如果简单了解过些Vue的API的话,肯定会对一下这个特别熟悉,在上一篇里,分析了Vue的核心文件core的index.js构造vue函数执行的流程. 那么下边这个则是实例化构造函数,也就 ...