disruptor组件在超级牛掰开源网关组件star80K+【shenyu】中的运用+研究
- 作者:爱抄中间件代码的路人丙
源码地址:https://gitee.com/Apache-ShenYu/shenyu?_from=gitee_search
打个小广告,觉得有收获的小伙伴,希望点赞、收藏,笔者将会持续的分享自己研究源码的学习和收获!
阅读提醒:源码部分建议文章和代码一起食用
本文主要探究以下几个问题:
- shenyu 使用disruptor的场景是什么?
- shenyu 为什么使用disruptor 而不使用其他的内存队列?
- shenyu 是如何使用disruptor
- shenyu 对disruptor 做了其他的扩展么
阅读本文你将收获到的内容
- 参考disruptor 在shenyu开源项目中的优雅使用方式
一、shenyu如何封装disruptor
大家可以去gitee或者github把 shenyu
的代码克隆下来一起食用
首先,简单看一下源码的目录结构,图中标记也是基于笔者使用过disruptor经历的猜测
对于一个新的组件或者源码,首先我们先从宏观到微观去了解他
DisruptorProviderManage 生产者管理类,顾名思义,他可能是一个控制类
ctr+F12 看一下主要的成员和方法,发现方法挺少的,代码也比较简单,简单总结一下
* 简单总结一下该类:
* (1)提供构造方法以及成员变量去配置 ringbuffer、消费者数量大小(不设置,提供默认ringbuffer 4096 *2 * 2 消费者数量: cpu核数 *2)
* (2)提供获取生产者实列方法
* (3)提供生产者初始化启动方法
* (4)该类只维护了一个生产者实列
startup()方法代码稍稍多一点
public void startup(final boolean isOrderly) {
// (1)弄了一个线程池
// 这个线程池可以支持有序 只有核心线程、阻塞队列基本无界 拒绝策略抛异常
OrderlyExecutor executor = new OrderlyExecutor(isOrderly, consumerSize, consumerSize, 0, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
DisruptorThreadFactory.create("shenyu_disruptor_consumer_", false), new ThreadPoolExecutor.AbortPolicy());
int newConsumerSize = this.consumerSize;
// (2)初始化EventFactory
// 事件封装:把泛型data包装了一下
EventFactory<DataEvent<T>> eventFactory;
if (isOrderly) {
// 2.1 如果有序,则设置消费者线程为1 eventFactory为OrderlyDisruptorEventFactory
newConsumerSize = 1;
// 有序事件工厂 生产 OrderlyDataEvent(DataEvent的子类) 多了一个hash属性
eventFactory = new OrderlyDisruptorEventFactory<>();
} else {
// 2.2 无序 eventFactory为DisruptorEventFactory
// 无序事件工厂
eventFactory = new DisruptorEventFactory<>();
}
// (3) 初始化 disruptor实列
Disruptor<DataEvent<T>> disruptor = new Disruptor<>(eventFactory,
size, // buffer大小
DisruptorThreadFactory.create("shenyu_disruptor_provider_" + consumerFactory.fixName(), false),
ProducerType.MULTI, // 采用多生产者
new BlockingWaitStrategy()); // 采用阻塞策略
// (4) 初始化消费者队列 (塞了一个可支持排序的多线程线程池和consumerFactory进去)
// QueueConsumer封装了业务逻辑 主要将数据委托给了executor线程池执行 具体的业务逻辑封装在QueueConsumer子类的run方法里,大概是做注册(URI、API、METADATA)
// 所以shenyu 使用disruptor主要的场景为:使用其多生产者多消费者模式 做各种客户端、服务端注册请求的削峰、缓存、分发(最主要还是一个分发作用,并没有在队列线程里执行业务逻辑)
@SuppressWarnings("all")
QueueConsumer<T>[] consumers = new QueueConsumer[newConsumerSize];
for (int i = 0; i < newConsumerSize; i++) {
consumers[i] = new QueueConsumer<>(executor, consumerFactory);
}
// (5) disruptor必备流程 塞值
disruptor.handleEventsWithWorkerPool(consumers);
disruptor.setDefaultExceptionHandler(new IgnoreExceptionHandler());
disruptor.start();
// (6) 拿到ringBuffer 然后封装成DisruptorProvider实列
RingBuffer<DataEvent<T>> ringBuffer = disruptor.getRingBuffer();
provider = new DisruptorProvider<>(ringBuffer, disruptor, isOrderly);
}
熟悉disruptor的小伙伴,应该了解以上几个步骤是使用disruptor的必要步骤
第(6)步,封装了一个DisruptorProvider实列,猜测这个对象应该是给生产者用的
接下来我们学习一下,优秀的开源组件如何优雅的封装DisruptorProvider
DisruptorProvider 类源码比较简单,简单小结一下
* 小结:简单来说就是给生产者用的
* (1)提供2种往ringbuffer放数据的方法:有序(多传一个hash)、无序
* (2) 提供destory销毁的方法
消费者:QueueConsumer
QueueConsumer 的onEvent方法是具体的消费逻辑,我们简单看一下源码:
public void onEvent(final DataEvent<T> t) {
if (Objects.nonNull(t)) {
ThreadPoolExecutor executor = orderly(t);// 获取线程池
QueueConsumerExecutor<T> queueConsumerExecutor = factory.create(); // 消费者队列执行器
queueConsumerExecutor.setData(t.getData()); // 把DataEvent的data塞给了queueConsumerExecutor QueueConsumerExecutor实现了Runnable接口
// help gc
t.setData(null);
executor.execute(queueConsumerExecutor);// 然后把queueConsumerExecutor放到线程池执行 QueueConsumerExecutor的具体实现类封装了业务逻辑
}
}
很明显,shenyu在使用disruptor的时候并没有在消费者线程里执行具体的业务逻辑,而是把业务逻辑委托给了disruptor构造时的线程池,具体的业务逻辑主要封装在QueueConsumerExecutor类的run方法(实现了Runnable接口,这个就不多说了)
后面又跟了一下QueueConsumerExecutor的2个子类的run方法,猜测大概是做client、server注册(URI、API、METADATA)到网关吧,等后面用过shenyu才探究吧
二、回答最开始提出的4个问题
看到这里,我相信大概能回答前面提出的问题了,“shenyu 使用disruptor的场景是什么?”
- shenyu 使用disruptor的场景:
使用其多生产者多消费者模式 做各种客户端、服务端注册请求的削峰、缓存、分发(最主要还是一个分发作用,并没有在队列线程里执行业务逻辑,而是委托给了另外的线程池执行各种方式注册逻辑)
所以简单来说,shenyu 使用disruptor主要看重了它的高并发性能,利用其做一个多生产者多消费者场景下的削峰和分发
- shenyu 是如何使用disruptor
关于这个问题,相信不用解释了,源码其实写的也比较清除了,笔者参考其中的使用方式,比如封装DisruptorProvider 更优雅的供生产方使用
- shenyu 对disruptor 做了其他的扩展么
暂时未发现
- shenyu 为什么使用disruptor 而不使用其他的内存队列?
解释这个问题的话,就比较麻烦了,需要对disruptor 的源码有一个清晰的认识
简单总结3个点:破除伪造共享、无锁编程、环形队列 (这个百度一搜索一堆)
一句话来说,就是生产者消费者模式下的高并发框架
笔者因为参与公司项目的优化,其实也研究了disruptor 的源码,等后面有时间梳理一下,以上对于问题的回答仅是笔者的个人思考,仅供参考,如果当前的你也感兴趣的话,可以评论你的想法!
三、总结一下收获和思考
(1)发现了disruptor更优雅的封装方式(中间件中抄代码 杠杠的爽)
(2)拓宽了自己disruptor 的应用场景认识:disruptor 在shenyu中主要负责注册请求的中转,起到一个可以处理高并发的作用