import lombok.Setter;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import javax.annotation.PostConstruct;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
public abstract class ConsumeModeService {
/**
- 独立消费者数量
*/
public static final int INDEPENDENT_CONSUMER_NUM = 2;
/**
- 环形缓冲区大小
*/
protected int BUFFER_SIZE = 16;
protected Disruptor disruptor;
@Setter
private OrderEventProducer producer;
/**
- 统计消息总数
*/
protected final AtomicLong eventCount = new AtomicLong();
/**
-
这是辅助测试用的,
-
测试的时候,完成事件发布后,测试主线程就用这个countDownLatch开始等待,
-
在消费到指定的数量(countDownLatchGate)后,消费线程执行countDownLatch的countDown方法,
-
这样测试主线程就可以结束等待了
*/
private CountDownLatch countDownLatch;
/**
-
这是辅助测试用的,
-
测试的时候,完成事件发布后,测试主线程就用这个countDownLatch开始等待,
-
在消费到指定的数量(countDownLatchGate)后,消费线程执行countDownLatch的countDown方法,
-
这样测试主线程就可以结束等待了
*/
private int countDownLatchGate;
/**
-
准备一个匿名类,传给disruptor的事件处理类,
-
这样每次处理事件时,都会将已经处理事件的总数打印出来
*/
protected Consumer<?> eventCountPrinter = new Consumer() {
@Override
public void accept(Object o) {
long count = eventCount.incrementAndGet();
/**
-
这是辅助测试用的,
-
测试的时候,完成事件发布后,测试主线程就用这个countDownLatch开始等待,
-
在消费到指定的数量(countDownLatchGate)后,消费线程执行countDownLatch的countDown方法,
-
这样测试主线程就可以结束等待了
*/
if (null!=countDownLatch && count>=countDownLatchGate) {
countDownLatch.countDown();
}
}
};
/**
-
发布一个事件
-
@param value
-
@return
*/
public void publish(String value) {
producer.onData(value);
}
/**
-
返回已经处理的任务总数
-
@return
*/
public long eventCount() {
return eventCount.get();
}
/**
-
这是辅助测试用的,
-
测试的时候,完成事件发布后,测试主线程就用这个countDownLatch开始等待,
-
在消费到指定的数量(countDownLatchGate)后,消费线程执行countDownLatch的countDown方法,
-
这样测试主线程就可以结束等待了
-
@param countDownLatch
-
@param countDownLatchGate
*/
public void setCountDown(CountDownLatch countDownLatch, int countDownLatchGate) {
this.countDownLatch = countDownLatch;
this.countDownLatchGate = countDownLatchGate;
}
/**
- 留给子类实现具体的事件消费逻辑
*/
protected abstract void disruptorOperate();
@PostConstruct
private void init() {
// 实例化
disruptor = new Disruptor<>(new OrderEventFactory(),
BUFFER_SIZE,
new CustomizableThreadFactory(“event-handler-”));
// 留给子类实现具体的事件消费逻辑
disruptorOperate();
// 启动
disruptor.start();
// 生产者
setProducer(new OrderEventProducer(disruptor.getRingBuffer()));
}
}
- 上述代码,有以下几处需要注意:
-
init方法是spring bean实例化后要执行的方法,这里面实例化Disruptor,还启动了消费线程,并且实例化了事件生产者,具体的事件消费逻辑,由子类在disruptorOperate方法中实现;
-
eventCountPrinter是个匿名类实例,传给事件消费的handler后,每消费一个事件都会执行一次eventCountPrinter.accept方法,这样就把消费事件的总数准确的保存在eventCount变量中了;
-
countDownLatch和countDownLatchGate是为了辅助单元测试而准备的,测试的时候,完成事件发布后,测试主线程就用这个countDownLatch开始等待,在消费到指定的数量(countDownLatchGate)后,消费线程执行countDownLatch的countDown方法,这样测试主线程就可以结束等待了
- 至此,公用代码就写完了,可见抽象父类已经做好了大部分事情,咱们的子类可以聚焦事件消费的逻辑编排了,开始挨个实现那三个场景;
100个订单,短信和邮件系统独立消费
- 两个消费者独立消费的逻辑非常简单,就一行代码,调用handleEventsWith方法把所有消费者实例传进去,就完事了:
package com.bolingcavalry.service.impl;
import com.bolingcavalry.service.ConsumeModeService;
import com.bolingcavalry.service.MailEventHandler;
import com.bolingcavalry.service.SmsEventHandler;
import org.springframework.stereotype.Service;
@Service(“independentModeService”)
public class IndependentModeServiceImpl extends ConsumeModeService {
@Override
protected void disruptorOperate() {
// 调用handleEventsWith,表示创建的多个消费者,每个都是独立消费的
// 这里创建两个消费者,一个是短信的,一个是邮件的
disruptor.handleEventsWith(new SmsEventHandler(eventCountPrinter), new MailEventHandler(eventCountPrinter));
}
}
- 单元测试代码如下,要注意的地方是发布完100事件后,调用countDownLatch.await()方法开始等待,直到消费者线程调用countDownLatch.countDown()方法解除等待,还有就是预期的消费消息总数等于200:
package com.bolingcavalry.service.impl;
import com.bolingcavalry.service.ConsumeModeService;
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 java.util.concurrent.CountDownLatch;
import static org.junit.Assert.assertEquals;
@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
public class ConsumeModeServiceTest {
@Autowired
@Qualifier(“independentModeService”)
ConsumeModeService independentModeService;
/**
- 测试时生产的消息数量
*/
private static final int EVENT_COUNT = 100;
private void testConsumeModeService(ConsumeModeService service, int eventCount, int expectEventCount) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(1);
// 告诉service,等消费到expectEventCount个消息时,就执行countDownLatch.countDown方法
service.setCountDown(countDownLatch, expectEventCount);
for(int i=0;i<eventCount;i++) {
log.info(“publich {}”, i);
service.publish(String.valueOf(i));
}
// 当前线程开始等待,前面的service.setCountDown方法已经告诉过service,
// 等消费到expectEventCount个消息时,就执行countDownLatch.countDown方法
// 千万注意,要调用await方法,而不是wait方法!
countDownLatch.await();
// 消费的事件总数应该等于发布的事件数
assertEquals(expectEventCount, service.eventCount());
}
@Test
public void testIndependentModeService() throws InterruptedException {
log.info(“start testIndependentModeService”);
testConsumeModeService(independentModeService,
EVENT_COUNT,
EVENT_COUNT * ConsumeModeService.INDEPENDENT_CONSUMER_NUM);
}
}
- 单元测试执行结果如下,符合预期:
100个订单,邮件系统的两个邮件服务器共同消费
- 两个消费者共同消费的代码也很简单,调用handleEventsWithWorkerPool方法即可,把共同消费的MailWorkHandler实例作为参数传入:
package com.bolingcavalry.service.impl;
import com.bolingcavalry.service.ConsumeModeService;
import com.bolingcavalry.service.MailWorkHandler;
import org.springframework.stereotype.Service;
@Service(“shareModeService”)
public class ShareModeServiceImpl extends ConsumeModeService {
@Override
protected void disruptorOperate() {
// mailWorkHandler1模拟一号邮件服务器
MailWorkHandler mailWorkHandler1 = new MailWorkHandler(eventCountPrinter);
// mailWorkHandler2模拟一号邮件服务器
MailWorkHandler mailWorkHandler2 = new MailWorkHandler(eventCountPrinter);
// 调用handleEventsWithWorkerPool,表示创建的多个消费者以共同消费的模式消费
disruptor.handleEventsWithWorkerPool(mailWorkHandler1, mailWorkHandler2);
}
}
- 单元测试是在ConsumeModeServiceTest.java中添加如下代码,注意由于是共同消费,因此预期的消费事件数等于消息数,都是100:
@Autowired
@Qualifier(“shareModeService”)
ConsumeModeService shareModeService;
@Test
public void testShareModeService() throws InterruptedException {
log.info(“start testShareModeService”);
testConsumeModeService(shareModeService, EVENT_COUNT, EVENT_COUNT);
}
- 执行单元测试,结果如下图:
100个订单,短信系统独立消费,与此同时,两个邮件服务器共同消费
- 最后一个场景,依旧很简单,handleEventsWith调用一次,再调用一次handleEventsWithWorkerPool即可:
package com.bolingcavalry.service.impl;
import com.bolingcavalry.service.ConsumeModeService;
import com.bolingcavalry.service.MailWorkHandler;
import com.bolingcavalry.service.SmsEventHandler;
import org.springframework.stereotype.Service;
@Service(“independentAndShareModeService”)
public class IndependentAndShareModeServiceImpl extends ConsumeModeService {
@Override
protected void disruptorOperate() {
// 调用handleEventsWith,表示创建的多个消费者,每个都是独立消费的
// 这里创建一个消费者,短信服务
disruptor.handleEventsWith(new SmsEventHandler(eventCountPrinter));
// mailWorkHandler1模拟一号邮件服务器
MailWorkHandler mailWorkHandler1 = new MailWorkHandler(eventCountPrinter);
// mailWorkHandler2模拟一号邮件服务器
MailWorkHandler mailWorkHandler2 = new MailWorkHandler(eventCountPrinter);
// 调用handleEventsWithWorkerPool,表示创建的多个消费者以共同消费的模式消费
disruptor.handleEventsWithWorkerPool(mailWorkHandler1, mailWorkHandler2);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
![img](https://i-blog.csdnimg.cn/blog_migrate/81b29b96b234bda267c66b47c2debfe0.jpeg)
Kafka实战笔记
关于这份笔记,为了不影响大家的阅读体验,我只能在文章中展示部分的章节内容和核心截图
- Kafka入门
- 为什么选择Kafka
- Karka的安装、管理和配置
- Kafka的集群
- 第一个Kafka程序
afka的生产者
- Kafka的消费者
- 深入理解Kafka
- 可靠的数据传递
- Spring和Kalka的整合
- Sprinboot和Kafka的整合
- Kafka实战之削峰填谷
- 数据管道和流式处理(了解即可)
- Kafka实战之削峰填谷
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
…(img-kWqxnxOi-1712097038771)]
- Kafka的集群
- 第一个Kafka程序
- [外链图片转存中…(img-WjuGn0YM-1712097038771)]
afka的生产者
[外链图片转存中…(img-KHuUgDuR-1712097038771)]
- Kafka的消费者
- 深入理解Kafka
- 可靠的数据传递
[外链图片转存中…(img-clTUfNs1-1712097038772)]
[外链图片转存中…(img-x2SCfTvF-1712097038772)]
- Spring和Kalka的整合
- Sprinboot和Kafka的整合
- Kafka实战之削峰填谷
- 数据管道和流式处理(了解即可)
[外链图片转存中…(img-vCmqTUlU-1712097038772)]
- Kafka实战之削峰填谷
[外链图片转存中…(img-7TdbY3wc-1712097038772)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!