关于Kafka如何封装更便捷使用,我是这样的思考的!

874 阅读3分钟

Kafka-Plus

一、前提

作为程序员的我们,日常工作中相信大家都会用到消息中间件,这时大家的选择常用的有ActiveMQ、RabbitMQ、RocketMQ、Kafka、ZeroMQ 等,各有各的特性和特长,由于笔者日常工作中使用Kafka较多。下面就来讨论讨论kafka使用过程中的一些痛点(自己感觉);以及笔这是如何解决这些痛点的。

二、使用痛点

相信大家在公司使用Kafka 是都会存在如下几个痛点。

1、Producer端

  • 不同的地址发送消息需要配置不同的生产者
  • 新项目接入Kafka 总是需要配置一堆信息

2、Consumer端

  • 使用不同业务线Kafka消息格式不统一
  • 每新增一个消费,或多或少都需要开发一点消费者拉取kafka代码
  • 使用不同业务线Kafka 消息序列化方式不同

三、解决思路

针对于上述两端使用中的一些问题,对Kafka的生产者,消费者进行了一次封装(Kafka-Plus),目的是让项目更快速接入Kafak,提升使用体验

架构设想:

未命名文件_(1).png

消息

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框架做的几件事情:

项目启动Kafka-Plus框架做的几件事情.png

五、使用(友情提示: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使用的封装,欢迎大家拍砖!!!!