解锁新姿势 | 如何用配置中心实现全局动态流控?

简介: 当资源成为瓶颈时,服务框架需要对消费者做限流,启动流控保护机制。流量控制有多种策略,比较常用的有:针对访问速率的静态流控、针对资源占用的动态流控、针对消费者并发连接数的连接控制和针对并行访问数的并发控制。在分布式架构中,应用和应用之间的调用类型分为以下两种,流控方式也略有不同。

当资源成为瓶颈时,服务框架需要对消费者做限流,启动流控保护机制。流量控制有多种策略,比较常用的有:针对访问速率的静态流控、针对资源占用的动态流控、针对消费者并发连接数的连接控制和针对并行访问数的并发控制。在实践中,各种流量控制策略需要综合使用才能起到较好的效果。

在分布式架构中,应用和应用之间的调用类型分为以下两种,流控方式也略有不同。

同步RPC类调用,比如RESTful,Dubbo,HSF等都属于该类。对于该类同步调用,通常限流方式为两种:针对服务提供者的并发全局流控,或针对服务消费者的并发局部流控。两种的控制手段类似,都是通过限制服务端或客服端并发调用数来进行限制。

异步MQ类调用,典型如RocketMQ, Kafka,等。对于该类异步调用,通常限流方式是在订阅端限流。限流方式为两种:针对消息订阅者的并发流控,或针对消息订阅者的消费延时流控。

针对消息订阅者的消费延时流控基本原理是,在每次客户端消费时,可以增加一个延时来控制消费速度,这样理论消费并发最快速度为:

MaxRate = 1 / ConsumInterval * ConcurrentThreadNumber

比如如果消息并发消费线程为20,延时为100ms,则理论上可以将并发消费控制在200以下。具体公式如下:

200 = 1 / 0.1 * 20

相比并发线程数流控,消费延时流控优点在于实现相对简单,对MQ类客户端包依赖较少,不需要客户端提供控制并发线程数的动态调整接口。

以上各种流量控制方法,在分布式架构下,如果要做到全局动态控制,一个简单的技术方法是依赖配置中心,即通过配置中心来进行流控参数的下发。

下面章节详细介绍如何基于配置中心来实现异步消息消费的全局动态流控。使用的例子为阿里云上的 MQ (消息队列)和 ACM (应用配置管理)两款产品。

注:之所以用MQ为示例是因为在本文撰写之时,正好MQ Consumer Client SDK并不支持动态调整现成并发数,因此通过基于ACM来动态调整消费延迟的方法正好可以解决MQ消费流控动态的问题。

基于消费延时流控的基本原理

基本原理如下。其中,管理员或应用程序通过ACM控制台发布消费延时配置(RCV_INTERVAL_TIME),所有MQ消费程序订阅该配置。理论上,该配置从发布到下发所有客户端,可以在1秒内完成(取决于网络延时)。

代码示例

该章节基于配置中心来实现异步消息消费的全局动态流控的代码示例。使用的例子为阿里云上的MQ(消息队列)和ACM(应用配置管理)两款产品,基于Java语言。关于SDK的详细介绍,可参见两款产品的官方文档。

在ACM上创建消费延时的参数,截屏如下。

设置全局消费延时变量

首先,设置消费接收延时的全局变量, 如下。

     // 初始化消息接收延时参数,单位为millisecond

        static int RCV_INTERVAL_TIME = 10000;

        // 初始化配置服务,控制台通过示例代码自动获取下面参数

        ConfigService.init("acm.aliyun.com", /*租户ID*/"xxx", /*AK*/"xxx", /*SK*/"yyy");    

        // 主动获取配置

        String content = ConfigService.getConfig("app.mq.qos", "DEFAULT_GROUP", 6000);

        Properties p = new Properties();

        try {

            p.load(new StringReader(content));

            RCV_INTERVAL_TIME = Integer.valueOf(p.getProperty("RCV_INTERVAL_TIME"));

        } catch (IOException e) {

            e.printStackTrace();

        }

其次,设置ACM listener,确保当配置被修改时,即使更新 RCV_INTERVAL_TIME 参数, 如下。

     // 初始化的时候,给配置添加监听,配置变更会回调通知

        ConfigService.addListener("app.mq.qos", "DEFAULT_GROUP", new ConfigChangeListener() {

            public void receiveConfigInfo(String configInfo) {

                Properties p = new Properties();

                try {

                    p.load(new StringReader(configInfo));

                    RCV_INTERVAL_TIME = Integer.valueOf(p.getProperty("RCV_INTERVAL_TIME"));

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

        });

设置 MQ 消费延时逻辑

完整实例如下。

注:这里 RCV_INTERVAL_TIME 参数的访问是故意没有加锁的,读者可以自行思考原因。Aliyun ONS Client不提供动态线程并发数,默认并发为20。因此这里正好使用消费延时参数来动态调节QoS。

     //以下代码可直接贴在Main()函数里

    Properties properties = new Properties();

    properties.put(PropertyKeyConst.ConsumerId, "CID_consumer_group");

    properties.put(PropertyKeyConst.AccessKey,"xxx");

    properties.put(PropertyKeyConst.SecretKey, "yyy");

    properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");

    // 设置 TCP 接入域名(此处以公共云生产环境为例)

    properties.put(PropertyKeyConst.ONSAddr,

      "http://onsaddr-internet.aliyun.com/rocketmq/nsaddr4client-internet");

    Consumer consumer = ONSFactory.createConsumer(properties);

    consumer.subscribe(/*Topic*/"topic-name", /*Tag*/null, new MessageListener() 

    {

        public Action consume(Message message, ConsumeContext context) {

            // MQ Subscribe QoS logical start, 

            // Each consuming process will sleep for RCV_INTERVAL_TIME seconds with 100 ms sleeping cycle.

            // Within each cycle, the thread will check RCV_INTERVAL_TIME in case it's set to a smaller value. 

            // RCV_INTERVAL_TIME <= 0 means no sleeping.

            int rcvIntervalTimeLeft = RCV_INTERVAL_TIME;

            while (rcvIntervalTimeLeft > 0) {

                if (rcvIntervalTimeLeft > RCV_INTERVAL_TIME) {

                    rcvIntervalTimeLeft = RCV_INTERVAL_TIME;

                }

                try {

                    if (rcvIntervalTimeLeft >= 100) {

                        rcvIntervalTimeLeft -= 100;

                        Thread.sleep(100);

                    } else {

                        Thread.sleep(rcvIntervalTimeLeft);

                        rcvIntervalTimeLeft = 0;

                    }

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

            }

            // MQ Subscribe interval logical ends

            System.out.println("Receive: " + message);

            /*

             * Put your business logic here.

             */

            doSomething();

            return Action.CommitMessage;

        }

    });

    consumer.start();

运行结果

单机运行consumer进行消费,假设queue内的消息无限多,不存在消费万的情况,分三段测试,分别运行约5分钟,通过ACM配置推送来达到以下效果。

RCV_INTERVAL_TIME = 100 ms

RCV_INTERVAL_TIME = 5000 ms

RCV_INTERVAL_TIME = 1000 ms

结果如下,在单MQ消费业务处理耗时约100ms情况下的,单机并发20线程的测试结果。

RCV_INTERVAL_TIME = 100 ms:平均消费性能约为 9000 tpm 左右

RCV_INTERVAL_TIME = 5000 ms:平均消费性能被限制到了 200 tpm 左右

RCV_INTERVAL_TIME = 1000 ms:平均消费性能回升到到了 1100 tpm 左右

以上结果基本达到消费和 tpm 成反比的预期,最关键的是整个过程中,应用不中断,流控推送结果秒级生效到分布式集群。单机性能结果如下所示。

相关产品详情请参见:

相关实践学习
快速体验阿里云云消息队列RocketMQ版
本实验将带您快速体验使用云消息队列RocketMQ版Serverless系列实例进行获取接入点、创建Topic、创建订阅组、收发消息、查看消息轨迹和仪表盘。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
监控 Java API
图解 Apache SkyWalking UI 的使用
Apache SkyWalking的UI界面主要分为以下几个区域: 功能选择区:这里列出了主要的UI功能,包括仪表盘、拓扑图、追踪、性能刨析、告警等功能 重新加载区:控制重新加载机制,包括定期重新加载或手动重新加载。 时间选择器:控制时区和时间范围。这里有一个中文/英文切换按钮,默认,UI使用浏览器语言设置。 下面逐一介绍功能选择区的各个功能:
2755 0
图解 Apache SkyWalking UI 的使用
|
监控 数据可视化 中间件
平台化三部曲之三流程编排 - 平台化是舞台,流程编排就是导演一场戏
在上两篇ATA中,第一篇讨论了平台的扩展性(《从Eclipse平台看交易平台化》),强调微内核和扩展机制实现,第二篇讨论平台的模块化开发(《Google Guice平台模块化开发的果汁》),强调业务隔离,松耦合。这这第三篇ATA中,想分享下平台化中另一个重要方面,平台的服务流程编排 (*备注:本文.
18981 2
|
Arthas Java 测试技术
超好用的自带火焰图的 Java 性能分析工具 Async-profiler 了解一下
超好用的自带火焰图的 Java 性能分析工具 Async-profiler 了解一下
3869 0
超好用的自带火焰图的 Java 性能分析工具 Async-profiler 了解一下
|
Linux 开发者 Windows
告别手动上传!开源FTP批量同步工具(免费跨平台)​​
自己开发的一个简单实用的 FTP 文件夹同步工具,支持定时自动同步和系统托盘运行,免去繁琐的配置。
363 1
|
JSON Java Nacos
SpringCloud 应用 Nacos 配置中心注解
在 Spring Cloud 应用中可以非常低成本地集成 Nacos 实现配置动态刷新,在应用程序代码中通过 Spring 官方的注解 @Value 和 @ConfigurationProperties,引用 Spring enviroment 上下文中的属性值,这种用法的最大优点是无代码层面侵入性,但也存在诸多限制,为了解决问题,提升应用接入 Nacos 配置中心的易用性,Spring Cloud Alibaba 发布一套全新的 Nacos 配置中心的注解。
1930 155
|
SQL 关系型数据库 MySQL
如何确认SQL用了索引:详细技巧与方法
在数据库管理中,索引是提高SQL查询性能的重要手段
2991 5
|
监控 小程序 前端开发
基于微信小程序充电桩预约管理系统的设计与实现(论文+源码)_kaic
基于微信小程序充电桩预约管理系统的设计与实现(论文+源码)_kaic
|
druid Java 关系型数据库
在Spring Boot中集成Druid实现多数据源有两种常用的方式:使用Spring Boot的自动配置和手动配置。
在Spring Boot中集成Druid实现多数据源有两种常用的方式:使用Spring Boot的自动配置和手动配置。
4027 5
|
SQL 开发框架 安全
超市商品信息管理系统设计与实现(论文+源码)_kaic
超市商品信息管理系统设计与实现(论文+源码)_kaic