爲什麼嘗試做這個集成
vertx是一套封裝了netty的異步事件驅動的框架,netty採用的線程模型可以高效處理某些情況下的網絡通訊,然而這套框架需要程序員使用函數編程的方式,不是傳統的方式。本項目主要是爲了構建一個框架。熟悉springboot編程的程序員只需要通過註解或者接口編程的式就可以使用到 vertx-kafka-client。
項目依賴
集成demo採用的依賴如下,主要是spring-boot-starter-web和vertx-kafka-client。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.vertx/vertx-kafka-client -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-kafka-client</artifactId>
<version>4.0.0</version>
</dependency>
初始化生產者和消費者Bean
初始化生產者
初始化生產者的邏輯很簡單,通過ApplicationContext取出所有的Consumer類,再通過反射取到消費者上的註解 MessageHandler所標註的信息(包括topic,msgType等)。在config類中,註冊了消費者的事件,並進行topic監聽。代碼如下:
@Bean
public List<KafkaConsumer> kafkaConsumers(){
// use consumer for interacting with Apache Kafka
List<KafkaConsumer> kafkaConsumers = new ArrayList<>();
Map<String, IKafkaHandler> consumerHandlers = this.context.getBeansOfType(IKafkaHandler.class);
for(String kafkaHandlerBean : consumerHandlers.keySet()){
//通過反射獲取MessageHandler裏的元信息
try {
IKafkaHandler handler = consumerHandlers.get(kafkaHandlerBean);
Class clazz = handler.getClass();
Method handleMethod = clazz.getDeclaredMethod("handle",Object.class);
MessageHandler anno = handleMethod.getAnnotation(MessageHandler.class);
String consumerGroup = anno.consumerGroup();
Class msgType = anno.msgType();
Map<String, String> config = new HashMap<>();
config.put("bootstrap.servers", "localhost:9092");
config.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
config.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
if(Strings.isNotBlank(consumerGroup)) {
config.put("group.id", consumerGroup);
}
config.put("auto.offset.reset", "earliest");
config.put("enable.auto.commit", "false");
KafkaConsumer<String,String> consumer = KafkaConsumer.create(vertx, config);
String topic = anno.topic();
consumer.handler(message->{
String value = message.record().value();
String key = message.record().key();
try {
if (msgType.getSimpleName().equals("String")) {
handler.handle(value);
} else {
Object var1 = JSON.parseObject(value, msgType);
handler.handle(var1);
}
} catch (Exception e){
logger.info("consume error,msg = {}",value);
}
});
consumer.exceptionHandler(error->logger.info("consumer出錯{}",error.toString()));
consumer.subscribe(topic);
kafkaConsumers.add(consumer);
} catch (Exception e) {
logger.error("error",e);
}
}
return kafkaConsumers;
}
初始化生產者
生產者的初始化很簡單,直接初始化一個Bean即可。代碼如下:
@Bean
public KafkaProducer kafkaProducer(){
Map<String, String> config = new HashMap<>();
config.put("bootstrap.servers", "localhost:9092");
config.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
config.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
config.put("acks", "1");
// use producer for interacting with Apache Kafka
KafkaProducer<String, String> producer = KafkaProducer.create(vertx, config);
return producer;
}
如何使用消費者
用戶可以通過接口編程的方式的實現來進行 consumer 的使用。繼承如下接口即可,使用 MessageHanlder 表明其 topic, 示例說明:
public class Topic1Handler implements IKafkaHandler<Message> {
private static final Logger logger = LoggerFactory.getLogger(Topic1Handler.class);
/**
* msgType 需要和 handle裏的參數類型相同
* @param message
*/
@MessageHandler(topic = "topic1",msgType = Message.class)
@Override
public void handle(Message message) {
logger.info("topic1 收到消息:{}",message);
}
}
如何使用生產者
通過注入producer即可實現一個默認配置的KafkaProducer,進行消息生產的代碼。
@Autowired
KafkaProducer producer;
以上便是大概的代碼,在我的git上有完整的demo,大家可以看看。
https://github.com/chifanchen/intergrate-springboot-with-vertx-kafka-client