-
发布一个事件
-
@param value
-
@return
*/
void publish(String value);
/**
-
返回已经处理的任务总数
-
@return
*/
long eventCount();
}
- 以上就是公共代码了,接下来逐个实现之前规划的三个场景;
100个事件,单个消费者消费
- 这是最简单的功能了,实现发布消息和单个消费者消费的功能,代码如下,有几处要注意的地方稍后提到:
package com.bolingcavalry.service.impl;
import com.bolingcavalry.service.*;
import com.lmax.disruptor.BatchEventProcessor;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
@Service(“oneConsumer”)
@Slf4j
public class OneConsumerServiceImpl implements LowLevelOperateService {
private RingBuffer ringBuffer;
private StringEventProducer producer;
/**
- 统计消息总数
*/
private final AtomicLong eventCount = new AtomicLong();
private ExecutorService executors;
@PostConstruct
private void init() {
// 准备一个匿名类,传给disruptor的事件处理类,
// 这样每次处理事件时,都会将已经处理事件的总数打印出来
Consumer<?> eventCountPrinter = new Consumer() {
@Override
public void accept(Object o) {
long count = eventCount.incrementAndGet();
log.info(“receive [{}] event”, count);
}
};
// 创建环形队列实例
ringBuffer = RingBuffer.createSingleProducer(new StringEventFactory(), BUFFER_SIZE);
// 准备线程池
executors = Executors.newFixedThreadPool(1);
//创建SequenceBarrier
SequenceBarrier sequenceBarrier = ringBuffer.newBarrier();
// 创建事件处理的工作类,里面执行StringEventHandler处理事件
BatchEventProcessor batchEventProcessor = new BatchEventProcessor<>(
ringBuffer,
sequenceBarrier,
new StringEventHandler(eventCountPrinter));
// 将消费者的sequence传给环形队列
ringBuffer.addGatingSequences(batchEventProcessor.getSequence());
// 在一个独立线程中取事件并消费
executors.submit(batchEventProcessor);
// 生产者
producer = new StringEventProducer(ringBuffer);
}
@Override
public void publish(String value) {
producer.onData(value);
}
@Override
public long eventCount() {
return eventCount.get();
}
}
- 上述代码有以下几处需要注意:
-
自己创建环形队列RingBuffer实例
-
自己准备线程池,里面的线程用来获取和消费消息
-
自己动手创建BatchEventProcessor实例,并把事件处理类传入
-
通过ringBuffer创建sequenceBarrier,传给BatchEventProcessor实例使用
-
将BatchEventProcessor的sequence传给ringBuffer,确保ringBuffer的生产和消费不会出现混乱
-
启动线程池,意味着BatchEventProcessor实例在一个独立线程中不断的从ringBuffer中获取事件并消费;
- 为了验证上述代码能否正常工作,我这里写了个单元测试类,如下所示,逻辑很简单,调用OneConsumerServiceImpl.publish方法一百次,产生一百个事件,再检查OneConsumerServiceImpl记录的消费事件总数是不是等于一百:
package com.bolingcavalry.service.impl;
import com.bolingcavalry.service.LowLevelOperateService;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class LowLeverOperateServiceImplTest {
@Autowired
@Qualifier(“oneConsumer”)
LowLevelOperateService oneConsumer;
private static final int EVENT_COUNT = 100;
private void testLowLevelOperateService(LowLevelOperateService service, int eventCount, int expectEventCount) throws InterruptedException {
for(int i=0;i<eventCount;i++) {
log.info(“publich {}”, i);
service.publish(String.valueOf(i));
}
// 异步消费,因此需要延时等待
Thread.sleep(10000);
// 消费的事件总数应该等于发布的事件数
assertEquals(expectEventCount, service.eventCount());
}
@Test
public void testOneConsumer() throws InterruptedException {
log.info(“start testOneConsumerService”);
testLowLevelOperateService(oneConsumer, EVENT_COUNT, EVENT_COUNT);
}
- 注意,如果您是直接在IDEA上点击图标来执行单元测试,记得勾选下图红框中选项,否则可能出现编译失败:
- 执行上述单元测试类,结果如下图所示,消息的生产和消费都符合预期,并且消费逻辑是在独立线程中执行的:
- 继续挑战下一个场景;
100个事件,三个消费者,每个都独自消费这个100个事件
-
这个场景在kafka中也有,就是三个消费者的group不同,这样每一条消息,这两个消费者各自消费一次;
-
因此,100个事件,3个消费者每人都会独立消费这100个事件,一共消费300次;
-
代码如下,有几处要注意的地方稍后提到:
package com.bolingcavalry.service.impl;
import com.bolingcavalry.service.*;
import com.lmax.disruptor.BatchEventProcessor;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
@Service(“multiConsumer”)
@Slf4j
public class MultiConsumerServiceImpl implements LowLevelOperateService {
private RingBuffer ringBuffer;
private StringEventProducer producer;
/**
- 统计消息总数
*/
private final AtomicLong eventCount = new AtomicLong();
/**
-
生产一个BatchEventProcessor实例,并且启动独立线程开始获取和消费消息
-
@param executorService
*/
private void addProcessor(ExecutorService executorService) {
// 准备一个匿名类,传给disruptor的事件处理类,
// 这样每次处理事件时,都会将已经处理事件的总数打印出来
Consumer<?> eventCountPrinter = new Consumer() {
@Override
public void accept(Object o) {
long count = eventCount.incrementAndGet();
log.info(“receive [{}] event”, count);
}
};
BatchEventProcessor batchEventProcessor = new BatchEventProcessor<>(
ringBuffer,
ringBuffer.newBarrier(),
new StringEventHandler(eventCountPrinter));
// 将当前消费者的sequence实例传给ringBuffer
ringBuffer.addGatingSequences(batchEventProcessor.getSequence());
// 启动独立线程获取和消费事件
executorService.submit(batchEventProcessor);
}
@PostConstruct
private void init() {
ringBuffer = RingBuffer.createSingleProducer(new StringEventFactory(), BUFFER_SIZE);
ExecutorService executorService = Executors.newFixedThreadPool(CONSUMER_NUM);
// 创建多个消费者,并在独立线程中获取和消费事件
for (int i=0;i<CONSUMER_NUM;i++) {
addProcessor(executorService);
}
// 生产者
producer = new StringEventProducer(ringBuffer);
}
@Override
public void publish(String value) {
producer.onData(value);
}
@Override
public long eventCount() {
return eventCount.get();
}
}
-
上述代码和前面的OneConsumerServiceImpl相比差别不大,主要是创建了多个BatchEventProcessor实例,然后分别在线程池中提交;
-
验证方法依旧是单元测试,在刚才的LowLeverOperateServiceImplTest.java中增加代码即可,注意testLowLevelOperateService的第三个参数是EVENT_COUNT * LowLevelOperateService.CONSUMER_NUM,表示预期的被消费消息数为300:
@Autowired
@Qualifier(“multiConsumer”)
LowLevelOperateService multiConsumer;
@Test
public void testMultiConsumer() throws InterruptedException {
log.info(“start testMultiConsumer”);
testLowLevelOperateService(multiConsumer, EVENT_COUNT, EVENT_COUNT * LowLevelOperateService.CONSUMER_NUM);
}
- 执行单元测试,如下图所示,一共消费了300个事件,并且三个消费者在不动线程:
100个事件,三个消费者共同消费这个100个事件
-
本篇的最后一个实战是发布100个事件,然后让三个消费者共同消费100个(例如A消费33个,B消费33个,C消费34个);
-
前面用到的BatchEventProcessor是用来独立消费的,不适合多个消费者共同消费,这种多个消费共同消费的场景需要借助WorkerPool来完成,这个名字还是很形象的:一个池子里面有很多个工作者,把任务放入这个池子,工作者们每人处理一部分,大家合力将任务完成;
-
传入WorkerPool的消费者需要实现WorkHandler接口,于是新增一个实现类:
package com.bolingcavalry.service;
import com.lmax.disruptor.WorkHandler;
import lombok.extern.slf4j.Slf4j;
import java.util.function.Consumer;
@Slf4j
public class StringWorkHandler implements WorkHandler {
public StringWorkHandler(Consumer<?> consumer) {
this.consumer = consumer;
}
// 外部可以传入Consumer实现类,每处理一条消息的时候,consumer的accept方法就会被执行一次
private Consumer<?> consumer;
@Override
public void onEvent(StringEvent event) throws Exception {
log.info(“work handler event : {}”, event);
// 这里延时100ms,模拟消费事件的逻辑的耗时
Thread.sleep(100);
// 如果外部传入了consumer,就要执行一次accept方法
if (null!=consumer) {
consumer.accept(null);
}
}
}
- 新增服务类,实现共同消费的逻辑,有几处要注意的地方稍后会提到:
package com.bolingcavalry.service.impl;
import com.bolingcavalry.service.*;
import com.lmax.disruptor.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
@Service(“workerPoolConsumer”)
@Slf4j
public class WorkerPoolConsumerServiceImpl implements LowLevelOperateService {
private RingBuffer ringBuffer;
private StringEventProducer producer;
/**
- 统计消息总数
*/
private final AtomicLong eventCount = new AtomicLong();
@PostConstruct
private void init() {
ringBuffer = RingBuffer.createSingleProducer(new StringEventFactory(), BUFFER_SIZE);
ExecutorService executorService = Executors.newFixedThreadPool(CONSUMER_NUM);
StringWorkHandler[] handlers = new StringWorkHandler[CONSUMER_NUM];
// 创建多个StringWorkHandler实例,放入一个数组中
for (int i=0;i < CONSUMER_NUM;i++) {
handlers[i] = new StringWorkHandler(o -> {
long count = eventCount.incrementAndGet();
log.info(“receive [{}] event”, count);
});
}
// 创建WorkerPool实例,将StringWorkHandler实例的数组传进去,代表共同消费者的数量
WorkerPool workerPool = new WorkerPool<>(ringBuffer, ringBuffer.newBarrier(), new IgnoreExceptionHandler(), handlers);
// 这一句很重要,去掉就会出现重复消费同一个事件的问题
ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
workerPool.start(executorService);
// 生产者
producer = new StringEventProducer(ringBuffer);
}
@Override
public void publish(String value) {
producer.onData(value);
}
@Override
public long eventCount() {
return eventCount.get();
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

Java高频面试专题合集解析:
当然在这还有更多整理总结的Java进阶学习笔记和面试题未展示,其中囊括了Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构资料和完整的Java架构学习进阶导图!
更多Java架构进阶资料展示
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

Java高频面试专题合集解析:
[外链图片转存中…(img-JqRV8FCf-1712097005179)]
当然在这还有更多整理总结的Java进阶学习笔记和面试题未展示,其中囊括了Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构资料和完整的Java架构学习进阶导图!
[外链图片转存中…(img-wLcbuJr3-1712097005180)]
更多Java架构进阶资料展示
[外链图片转存中…(img-i1MNoZE7-1712097005180)]
[外链图片转存中…(img-IkQiUXq3-1712097005180)]
[外链图片转存中…(img-G89a3v71-1712097005180)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!