1.需求场景:
封装基于JVM内存的高性能并发框架Disruptor,优化项目之前邮件发送、ftp文件输送功能,实现并行发送,提升发送效率。
2.直接上封装代码
定义生产者发布消息
package com.icon.disruptor.producer;
import com.icon.config.ThreadPoolMonitor;
import com.icon.disruptor.eventHandler.DisEventHandler;
import com.icon.disruptor.entity.DisEvent;
import com.icon.disruptor.exception.EventExceptionHandler;
import com.icon.disruptor.factory.DisEventFactory;
import com.icon.entity.common.LookCupDO;
import com.icon.repository.common.LookCupRepository;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SequenceBarrier;
import com.lmax.disruptor.SleepingWaitStrategy;
import com.lmax.disruptor.WorkerPool;
import com.lmax.disruptor.dsl.ProducerType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.text.ParseException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
/**
* describe
*
* @author 晴日朗
* @date 2022年10月13日14:14
*/
public abstract class DisPublishBuilder {
private static final Logger LOGGER = LoggerFactory.getLogger(DisPublishBuilder.class);
@Autowired
private LookCupRepository lookCupRepository;
/**
* 发布环境构建
*
* @param jsonParamList 要发布的数据 支持批量
* @param className 类名大写
* @throws Exception 异常
*/
public void disPublishData(List<Map<String, Object>> jsonParamList, String className) throws Exception {
LOGGER.info("DisPublishBuilder.className={},jsonParamList={}", className, jsonParamList);
if (CollectionUtils.isEmpty(jsonParamList) || StringUtils.isEmpty(className)) return;
ExecutorService executor = null;
WorkerPool<DisEvent> workerPool = null;
try {
// 1.创建可以复用的线程池,提供给consumer
List<LookCupDO> lookCupDOS = lookCupRepository.findAllDataByType("EMAIL_CONSUMERS");
int consumersCounts = CollectionUtils.isEmpty(lookCupDOS) ? 6 : StringUtils.isEmpty(lookCupDOS.get(0).getColumn1()) ? 6 : Integer.parseInt(lookCupDOS.get(0).getColumn1());
BlockingDeque<Runnable> blockingDeque = new LinkedBlockingDeque<>(10);
executor = new ThreadPoolMonitor(consumersCounts, 10, 1, TimeUnit.SECONDS, blockingDeque, "disruptorPool");
// 2.创建event工厂
DisEventFactory eventFactory = new DisEventFactory();
// 3.定义ringBuffer数组大小,设置为2的N次方,位运算,效率高
int ringBufferSize = 1024 * 1024;
// 4.创建ringBuffer
RingBuffer<DisEvent> ringBuffer = RingBuffer.create(ProducerType.MULTI, eventFactory, ringBufferSize, new SleepingWaitStrategy());
SequenceBarrier barriers = ringBuffer.newBarrier();
// 5.注册消费者,可注册多个,分摊消费模式 消费者实现的是WorkHandle接口
DisEventHandler[] consumers = new DisEventHandler[consumersCounts];
for (int i = 0; i < consumers.length; i++) {
consumers[i] = new DisEventHandler();
}
workerPool = new WorkerPool<DisEvent>(ringBuffer, barriers,
new EventExceptionHandler(), consumers);
ringBuffer.addGatingSequences(workerPool.getWorkerSequences());
workerPool.start(executor);
// 6.创建生产者
DisEventProducer producer = new DisEventProducer(ringBuffer);
// 7.业务参数投递
for (Map<String, Object> map : jsonParamList) {
// 逐条发布,多线程并发处理
producer.onData(map, className);
}
} catch (ParseException e) {
throw new Exception(e);
} finally {
if (null != workerPool) {
LOGGER.info("workerPool.running={}", workerPool.isRunning());
workerPool.drainAndHalt();
LOGGER.info("workerPool.drainAndHalt,running={}", workerPool.isRunning());
}
if (null != executor) {
executor.shutdown();
LOGGER.info("executor.shutdown");
}
}
}
}
生产者发布消息
package com.icon.disruptor.producer;
import com.icon.disruptor.entity.DisEvent;
import com.lmax.disruptor.RingBuffer;
import java.util.Map;
/**
* describe 生产者-定义如何将邮件的业务信息通过事件发布到环形队列
*
* @author 晴日朗
* @date 2022年03月17日17:22
*/
public class DisEventProducer {
// 存储数据的环形队列
private final RingBuffer<DisEvent> ringBuffer;
public DisEventProducer(RingBuffer<DisEvent> ringBuffer) {
this.ringBuffer = ringBuffer;
}
public void onData(Map<String, Object> map, String className) throws Exception {
// ringBuffer是个队列,其next方法返回的是下一条记录之后的位置,这是个可用位置
long sequence = ringBuffer.next();
try {
// sequence位置取出事件
DisEvent event = ringBuffer.get(sequence);
// 添加到消费端信息
event.setObjectMap(map);
event.setClassName(className);
} catch (Exception e) {
throw new Exception("EmailEventProducer.onData.error={}", e);
} finally {
// 发布
ringBuffer.publish(sequence);
}
}
}
定义工厂
package com.icon.disruptor.factory;
import com.icon.disruptor.entity.DisEvent;
import com.lmax.disruptor.EventFactory;
/**
* describe 声明一个Factory来实例化DisEvent
*
* @author 晴日朗
* @date 2022年03月17日17:25
*/
public class DisEventFactory implements EventFactory<DisEvent> {
@Override
public DisEvent newInstance() {
return new DisEvent();
}
}
异常处理
package com.icon.disruptor.exception;
import com.lmax.disruptor.ExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* disruptor框架异常抛出日志打印
*/
public class EventExceptionHandler implements ExceptionHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(EventExceptionHandler.class);
@Override
public void handleEventException(Throwable ex, long sequence, Object event) {
LOGGER.error("handleEventException={}", ex);
}
@Override
public void handleOnShutdownException(Throwable ex) {
LOGGER.error("handleOnShutdownException={}", ex);
}
@Override
public void handleOnStartException(Throwable ex) {
LOGGER.error("handleOnStartException={}", ex);
}
}
消息体-传输数据
package com.icon.disruptor.entity;
import java.util.Map;
/**
* describe 生产者和消费者传递数据消息体
*
* @author 一叶孤舟
* @date 2022年03月17日17:25
*/
public class DisEvent {
// 消息数据
private Map<String,Object> objectMap;
private String className;
public Map<String, Object> getObjectMap() {
return objectMap;
}
public void setObjectMap(Map<String, Object> objectMap) {
this.objectMap = objectMap;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
}
消费者处理器
package com.icon.disruptor.eventHandler;
import com.icon.disruptor.entity.DisEvent;
import com.icon.enums.EventHandlerEnum;
import com.lmax.disruptor.WorkHandler;
/**
* describe 消费者服务
*
* @author 晴日朗
* @date 2022年03月17日17:41
*/
public class DisEventHandler implements WorkHandler<DisEvent> {
@Override
public void onEvent(DisEvent event) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
String valueByClassName = EventHandlerEnum.getValueByClassName(event.getClassName());
Class<?> aClass = Class.forName(valueByClassName);
DisConsumerService disConsumerService = (DisConsumerService) aClass.newInstance();
disConsumerService.consume(event.getObjectMap());
}
}
定义消费者映射关系
package com.icon.enums;
/**
* describe 消费者映射关系
*
* @author 晴日朗
* @date 2022年10月17日14:22
*/
public enum EventHandlerEnum {
EMAIL_EVENT_HANDLER("EmailConsumerService", "com.icon.disruptor.consumer.EmailConsumerService");
private String className;
private String classFullName;
EventHandlerEnum() {
throw new RuntimeException();
}
EventHandlerEnum(String className, String classFullName) {
this.className = className;
this.classFullName = classFullName;
}
public static String getValueByClassName(String className) {
for (EventHandlerEnum eventHandlerEnum : EventHandlerEnum.values()) {
if (className.equals(eventHandlerEnum.getClassName())) {
return eventHandlerEnum.getClassFullName();
}
}
return null;
}
public String getClassName() {
return className;
}
public String getClassFullName() {
return classFullName;
}
}
消费者统一入口
package com.icon.disruptor.eventHandler;
import java.util.Map;
/**
* describe 消费者统一入口
*
* @author 一叶孤舟
* @date 2022年10月17日15:42
*/
public interface DisConsumerService {
void consume(Map<String, Object> eventMap);
}
以上disruptor框架封装完毕,接下来是使用步骤:
1.自定义消费者,实现DisConsumerService接口,重写consume(…)方法
2.配置消费者映射关系EventHandlerEnum
3.定义disruptor生产者,继承DisPublishBuilder抽象类,执行super.disPublishData(…)方法,发布消息
自定义消费者李子:
/**
* describe 具体的消费者
*
* @author 晴日朗
* @date 2022年10月17日15:42
*/
public class EmailConsumerService implements DisConsumerService {
private static final Logger LOGGER = LoggerFactory.getLogger(EmailConsumerService.class);
@Override
public void consume(Map<String, Object> objectMap) {
LOGGER.info("EmailConsumerService.consume.data={}", JSON.toJSONString(objectMap));
}
}
小结:封装了disruptor框架,达到了复用效果。害,第一次实操封装,感觉有点别扭,实力不允许啊,哈哈。disruptor并发处理任务,并发处理性能相当高,具体的我在项目中作了测试,由之前的串行到并行,大大提升了效率,大家可以试试或者可以试试其他的并行框架或者实现并发处理,对比一下效果。当然,在封装过程中也遇到了很多问题,我都一一记录在了另一篇文章【首次使用disruptor框架遇到的问题】。慢慢完善吧。加油啊,奥利给。