disruptor笔记之八:知识点补充(终篇)

  • 类图如下,可见Value的父子类中都是占位的long型:

在这里插入图片描述

  • 因此,Sequence对象有16个成员变量,在L1 cache中是下图的排列方式:

在这里插入图片描述

  • 想像一下,L1 cache的缓存行,每64字节为一个,也就是说上面那一串,每八个就占据一个缓存行(每个long型8字节),于是就有以下三种排列的可能:
  1. V出现在一个缓存行的首位;

  2. V出现在一个缓存行的末尾;

  3. V出现在一个缓存行的首位和末尾之间的其他六个位置之一;

  • 也就是下图三种可能(U是L1 cache中的其他内容),可见不论哪种可能,V都能用P把自己所在缓存行全部占座,这样就不会出现两个Sequence出现在同一个缓存行的情况了:

在这里插入图片描述

Translators

  • 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 ringBuffer;

public OrderEventProducerWithTranslator(RingBuffer 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 void publishEvent(EventTranslatorOneArg<E, A> translator, A arg0)

{

final long sequence = sequencer.next();

translateAndPublish(translator, sequence, arg0);

}

  • 再打开translateAndPublish看看,ringBuffer.get、try-finally代码块、sequencer.publish都在,这下放心了,以前咱们做的事情,现在disruptor帮我们做了,咱们可以专心业务逻辑了:

private void translateAndPublish(EventTranslatorOneArg<E, A> translator, long sequence, A arg0)

{

try

{

translator.translateTo(get(sequence), sequence, arg0);

}

finally

{

sequencer.publish(sequence);

}

}

Lambda风格

  • disruptor的重要API也支持Lambda表达式作为入参,这里将几处常用的API整理如下:
  1. Disruptor类实例化(LambdaServiceImpl.java):

// lambda类型的实例化

disruptor = new Disruptor(OrderEvent::new, BUFFER_SIZE, DaemonThreadFactory.INSTANCE);

  1. 设置事件消费者的时候,可以用Lambda取代之前的对象(LambdaServiceImpl.java):

// lambda表达式指定具体消费逻辑

disruptor.handleEventsWith((event, sequence, endOfBatch) -> {

log.info(“lambda操作, sequence [{}], endOfBatch [{}], event : {}”, sequence, endOfBatch, event);

// 这里延时100ms,模拟消费事件的逻辑的耗时

Thread.sleep(100);

// 计数

eventCountPrinter.accept(null);

});

  1. 发布事件的操作,也支持Lambda表达式,如下所示,我在父类ConsumeModeService.java中新增publistEvent方法,里面调用的disruptor.getRingBuffer().publishEvent的入参就是Lambda表达式和事件所需的业务数据,这样就省区了以前发布事件的类OrderEventProducer.java:

public void publistEvent(EventTranslatorOneArg<OrderEvent, String> translator, String value) {

disruptor.getRingBuffer().publishEvent(translator, value);

}

  1. 如下所示,现在拿到业务数据后发布事件的操作变得非常轻了,Lambda表达式中做好业务数据转事件的逻辑即可,最终,不再需要OrderEventProducer.java,一行代码完成事件发布(ConsumeModeServiceTest.java):

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);

}

清理数据

  • 由于存储的数据结构是环形队列,对于每个事件的实例,会一直保存在队列中,直到再次在这个位置写入时才会被新的事件实例覆盖,考虑到可能有的场景要求数据被消费后就立即被清除,disruptor官方提供了以下建议:
  1. 事件定义的类中,增加一个清理业务数据的方法(假设是ObjectEvent类的clear方法);

  2. 新增一个事件处理类(假设是ClearingEventHandler),在里面主动调用事件定义类的清理业务数据的方法;

  3. 在编写事件消费逻辑时,最后添加上述事件处理类ClearingEventHandler,这样就会调用ObjectEvent实例的clear方法,将业务数据清理掉;

  • 官方给出的代码如下:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值