https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;
假设disruptor的Sequence是long型,那么一个生产者和一个消费者的disruptor应该有两个long型Sequence,在L1中缓存这两个数字时,因为每个缓存行大小是64字节,所以两个Sequence很有可能在一个缓存行中
此时如果程序修改了生产者Sequence的值,就会让L1上对应的缓存行失效,再从Main memory中读取最新的值,此时因为消费者Sequence也在同一个缓存行中,因此也被失效了,这就导致一个没有变化的值也被清理掉,还要再去Main memory中取一次,这是影响性能的行为
看到这里,聪明的您一定想到解决问题的思路:不要让两个Sequence在同一个缓存行中
具体的手段呢?您有没有联想到日常生活中的占座位,在身边座位放个背包,其他人就不能使用了(这是不文明行为,仅举例用)
实际上disruptor用的也是占座的套路,咱们来看看Sequence的源码就一目了然了,如下图所示,Sequence的值是Value对象的成员变量value:
// 父类, class LhsPadding { protected long p1, p2, p3, p4, p5, p6, p7; } class Value extends LhsPadding { protected volatile long value; } class RhsPadding extends Value { protected long p9, p10, p11, p12, p13, p14, p15; } public class Sequence extends RhsPadding { ...
Translators是个小的编程技巧,disruptor帮使用者做了些封装,让发布事件的代码更简洁;
打开disruptor-tutorials项目的consume-mode这个module,回顾一下,业务发布事件要调用的方法,在OrderEventProducer.java中:
public void onData(String content) { // ringBuffer是个队列,其next方法返回的是下最后一条记录之后的位置,这是个可用位置 long sequence = ringBuffer.next(); try { // sequence位置取出的事件是空事件 OrderEvent orderEvent = ringBuffer.get(sequence); // 空事件添加业务信息 orderEvent.setValue(content); } finally { // 发布 ringBuffer.publish(sequence); } }
上面的代码中,其实开发者最关注的是orderEvent.setValue(content)这部分,其他几行是我从官方demo抄的…
显然disruptor也发现了这个小问题,于是从3.0版本开始提供了EventTranslatorOneArg接口,开发者将业务逻辑放入放在此接口的实现类中,至于前面代码中的ringBuffer.next()、ringBuffer.get(sequence)这些,以及try-finally代码块这些东西统统都省去了,咱们可以将OrderEventProducer.java改造成一个新的类,代码如下,可见新增内部类EventTranslatorOneArg,里面是将数据转为事件的业务逻辑,对外提供调用的onData方法中,只需一行代码即可,和业务无关的代码全部省掉了:
package com.bolingcavalry.service; import com.lmax.disruptor.EventTranslatorOneArg; import com.lmax.disruptor.RingBuffer; public class OrderEventProducerWithTranslator { // 存储数据的环形队列 private final RingBuffer<OrderEvent> ringBuffer; public OrderEventProducerWithTranslator(RingBuffer<OrderEvent> ringBuffer) { this.ringBuffer = ringBuffer; } /** * 内部类 */ private static final EventTranslatorOneArg<OrderEvent, String> TRANSLATOR = new EventTranslatorOneArg<OrderEvent, String>() { @Override public void translateTo(OrderEvent event, long sequence, String arg0) { event.setValue(arg0); } }; public void onData(String content) { ringBuffer.publishEvent(TRANSLATOR, content); } }
在consume-mode中,上述代码有对应的服务类TranslatorPublishServiceImpl.java,并且有对应的单元测试代码(ConsumeModeServiceTest.testTranslatorPublishService),这里就不占篇幅了,您若有兴趣可以自行查阅;
看看ringBuffer.publishEvent的内部实现,是如何帮咱们省去之前那几行的,首先是调用了sequencer.next:
@Override public <A> void publishEvent(EventTranslatorOneArg<E, A> translator, A arg0) { final long sequence = sequencer.next(); translateAndPublish(translator, sequence, arg0); }
private <A> void translateAndPublish(EventTranslatorOneArg<E, A> translator, long sequence, A arg0) { try { translator.translateTo(get(sequence), sequence, arg0); } finally { sequencer.publish(sequence); } }
// lambda类型的实例化 disruptor = new Disruptor<OrderEvent>(OrderEvent::new, BUFFER_SIZE, DaemonThreadFactory.INSTANCE);
// lambda表达式指定具体消费逻辑 disruptor.handleEventsWith((event, sequence, endOfBatch) -> { log.info("lambda操作, sequence [{}], endOfBatch [{}], event : {}", sequence, endOfBatch, event); // 这里延时100ms,模拟消费事件的逻辑的耗时 Thread.sleep(100); // 计数 eventCountPrinter.accept(null); });
public void publistEvent(EventTranslatorOneArg<OrderEvent, String> translator, String value) { disruptor.getRingBuffer().publishEvent(translator, value); }
for(int i=0;i<EVENT_COUNT;i++) { log.info("publich {}", i); final String content = String.valueOf(i); lambdaService.publistEvent((event, sequence, value) -> event.setValue(value), content); }
我是欣宸,期待与您一同畅游Java世界…
https://github.com/zq2599/blog_demos