Kafka-Plus
一、前提
作为程序员的我们,日常工作中相信大家都会用到消息中间件,这时大家的选择常用的有ActiveMQ、RabbitMQ、RocketMQ、Kafka、ZeroMQ 等,各有各的特性和特长,由于笔者日常工作中使用Kafka较多。下面就来讨论讨论kafka使用过程中的一些痛点(自己感觉);以及笔这是如何解决这些痛点的。
二、使用痛点
相信大家在公司使用Kafka 是都会存在如下几个痛点。
1、Producer端
- 不同的地址发送消息需要配置不同的生产者
- 新项目接入Kafka 总是需要配置一堆信息
2、Consumer端
- 使用不同业务线Kafka消息格式不统一
- 每新增一个消费,或多或少都需要开发一点消费者拉取kafka代码
- 使用不同业务线Kafka 消息序列化方式不同
三、解决思路
针对于上述两端使用中的一些问题,对Kafka的生产者,消费者进行了一次封装(Kafka-Plus),目的是让项目更快速接入Kafak,提升使用体验
架构设想:
消息
Event
类型:Class
作用:消息统一封装
@Data
public class Event<T> {
private String eventType; //用于后续消费者消息分发
String msgId;
T msg;
/**
* 消息产生的时间戳
*/
private long ts;
public Event(String eventType, T msg) {
this.eventType = eventType;
this.msg = msg;
this.ts = System.currentTimeMillis();
this.msgId = MsgIdUtil.generateMsgId();
}
}
生产者
KafkaEventChannel
类型:Class
作用:生产者封装,用于发送消息
KafkaEventChannelClient
类型:注解
作用:标识生产者作用域—对应配置文件Producer
KafkaProducerStarter
类型:Class
作用:项目启动加载类, 与配置类KafkaChannelAutoConfiguration 中使用
使用方式:
@KafkaEventChannelClient("common") //此处common与consumer 中@EventType注解中想对应;随意指配
private KafkaEventChannel eventChannel;
消费者
EventType
类型:注解
作用:同一Topic 下的消息通过EventType进行分组;与@KafkaEventChannelClient 发送这配合使用
Function:
类型:Class
作用:业务逻辑
FunctionProcessor
类型:Class
作用: 初始化类,创建不同消费者业务Function,交个FunctionManager管理
FunctionManager
类型:Class
作用:管理function
public class FunctionManager {
// Key- EventType 对应的类型;Value 真正需要执行的方法
public Map<String, List<Function>> functionMap = Maps.newConcurrentMap();
private ReentrantLock lock = new ReentrantLock();
public List<Function> getFunction(String eventType) {
return functionMap.get(eventType);
}
public void setFunction(String eventType, Function function) {
lock.lock();
try {
List<Function> functionList = functionMap.get(eventType);
if (functionList == null) {
functionList = Lists.newArrayList();
functionMap.put(eventType, functionList);
}
functionList.add(function);
} finally {
lock.unlock();
}
}
}
EventExecutor
类型:Class
作用:获取业务Function,处理Function前做前置过滤
KafkaEventWorker
类型:Class
作用: 继承java.util.Objects.Consumer,接口kafkax消费过来的消息
使用方式:
private void doConsumer(String topic, Properties properties) {
KafkaConsumer<String, String> kafkaConsumer;
try {
kafkaConsumer = new KafkaConsumer<>(properties);
kafkaConsumer.subscribe(Arrays.asList(topic));
EventExecutor eventExecutor = new EventExecutor(functionManager);
KafkaEventWorker worker = new KafkaEventWorker(eventExecutor, topic);
executorService.submit(() -> {
try {
while (true) {
ConsumerRecords<String, String> records = kafkaConsumer.poll(Duration.ofMillis(200));
records.forEach(x -> worker.accept(x.value()));
}
} catch (Exception e) {
log.error("fail to consumer message.", e);
}
});
} catch (Exception e) {
log.error("", e);
}
}
配置
KafkaConsumerStarter|KafkaProducerStarter
类型:Class
作用:Consumer|Producer处理业务逻辑初始化;—入口
CustomKafkaProerties
类型:Class (ConfigurationProperties)
作用: 加载配置文件信息
KafkaChannelAutoConfiguration|KafkaConsumerAutoConfiguration
类型:Class
作用:springBoot 自动装配类,项目启动入口
四、开发
Kafka-Plus框架做的几件事情:
五、使用(友情提示:application.yaml 文件中kafkas servers地址记得更改)
1、下载源码自己打包;源码见第六项
2、引入Kafka-Plus-starter
<dependency>
<groupId>com.kafka.plus</groupId>
<artifactId>kafak-plus-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
3、配置启动包扫描(com.kafka.plus)
@SpringBootApplication
@ComponentScan(value = {"com.kafka.kafkastartertest", "com.kafka.plus"})
public class KafkaStarterTestApplication {
public static void main(String[] args) {
SpringApplication.run(KafkaStarterTestApplication.class, args);
}
}
4、配置文件配置(application.yaml)
config.kafka:
producers:
common: #对应@KafkaChannelEveltClient 中value属性
topic: test1 #topic
servers: xxx #kafka地址
consumers:
- group: message
topic: test1
servers: xxx
- group: eventbBus
topic: test2
servers: yyy
5、发送消息
public class TestProducer {
@KafkaEventChannelClient
KafkaEventChannel kafkaEventChannel;
@GetMapping("/sendMsg")
public String sendMsg() {
kafkaEventChannel.publish(new Event("tttt", "tttMsg:" + System.currentTimeMillis()));
return "success";
}
}
6、消费消息
public class TestConsumer extends FunctionProcessor<String> {
@Override
@EventType(type = "tttt") //此处tttt 与生产者中的tttt对应;针对同一topic下的消息做二次分组
public boolean execute(String s, String s2) {
log.info("msgId {}, msg {}", s, s2);
return true;
}
}
六、源码
百度网盘地址:
Kafka-Plus 链接: pan.baidu.com/s/1GKsGdTPW… 提取码: 96gs
Kafka-Plus-test 链接: pan.baidu.com/s/1diVddbpX… 提取码: 4piq
github地址: 后续更新
七、结束语
上述是我自己关于kafka使用的封装,欢迎大家拍砖!!!!