JAVA封装消息中间件调用二(kafka消费者篇)
上一遍我简单介绍了kafka的生成者使用,调用方式比较简单,今天我给大家分享下封装kafka消费者,作为中间件,我们做的就是最大程度的解耦,使业务方接入我们依赖程度降到最低。
第一步,我们先配置一个消费者核心类
package com.meiren.message.kafka.consumer; import com.meiren.message.kafka.beans.ConsumerProperty;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer; import java.util.List;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; /**
* Created by zhangwentao on 2017/5/18.
*/
public class ConsumerHandler {
private final KafkaConsumer<String, String> consumer;
private ExecutorService executors; public ConsumerHandler(ConsumerProperty consumerProperty, List<String> topics) {
Properties props = new Properties();
props.put("bootstrap.servers", consumerProperty.getBrokerList());
props.put("group.id", consumerProperty.getGroupId());
props.put("enable.auto.commit", consumerProperty.getEnableAutoCommit());
props.put("auto.commit.interval.ms", consumerProperty.getAutoCommitInterval());
props.put("session.timeout.ms", consumerProperty.getSessionTimeout());
props.put("key.deserializer", consumerProperty.getKeySerializer());
props.put("value.deserializer", consumerProperty.getValueSerializer());
consumer = new KafkaConsumer<String, String>(props);
consumer.subscribe(topics);
} public void execute(int workerNum) {
executors = new ThreadPoolExecutor(workerNum, workerNum, 0L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue(1000), new ThreadPoolExecutor.CallerRunsPolicy());
Thread t = new Thread(new Runnable(){//启动一个子线程来监听kafka消息
public void run(){
while (true) {
ConsumerRecords<String, String> records = consumer.poll(200);
for (final ConsumerRecord record : records) {
System.out.println("监听到kafka消息。。。。。。");
executors.submit(new ConsumerWorker(record));
}
}
}});
t.start(); } public void shutdown() {
if (consumer != null) {
consumer.close();
}
if (executors != null) {
executors.shutdown();
}
try {
if (!executors.awaitTermination(10, TimeUnit.SECONDS)) {
System.out.println("Timeout.... Ignore for this case");
}
} catch (InterruptedException ignored) {
System.out.println("Other thread interrupted this shutdown, ignore for this case.");
Thread.currentThread().interrupt();
}
}
}
这个核心类有3个部分组成:1.构造方法(生成一个消费者配置,订阅topic),2.开启多线程监听消费者 3.关闭线程是和消费者
public ConsumerHandler(ConsumerProperty consumerProperty, List<String> topics)
consumerProperty是消费者配置信息类,包含了消费者的配置属性和topic
public class ConsumerProperty { private String brokerList; private String groupId; private String enableAutoCommit="true"; private String autoCommitInterval="1000"; private String sessionTimeout="30000"; private String keySerializer="org.apache.kafka.common.serialization.StringDeserializer"; private String valueSerializer="org.apache.kafka.common.serialization.StringDeserializer";
/**
* topic以及消费的实现类
*/
private List<MessageContainer> messageContainers;
2.监听消费者信息
public void execute(int workerNum) { } 这段代码的入参是线程数,开启一个线程池ThreadPoolExecutor,建立一个长连接,每200毫秒去kafka服务器拉取消息,每拉到一个消息,就分配给一个线程类ConsumerWorker去处理这个消息
这里要特别注意是,监听kafka的过程需要另起一个线程去监听,不然主线程会一直在while(true)里面阻塞掉。 3.关闭线程池和消费者(一般情况下会一直处于监听状态)
第二步,我们设置服务启动的时候去监听
public class PropertyFactory { public static ProducerProperty producerProperty; public static ConsumerProperty consumerProperty; public ProducerProperty getProducerProperty() {
return producerProperty;
} public void setProducerProperty(ProducerProperty producerProperty) {
this.producerProperty = producerProperty;
} public ConsumerProperty getConsumerProperty() {
return consumerProperty;
} public void setConsumerProperty(ConsumerProperty consumerProperty) {
this.consumerProperty = consumerProperty;
} ConsumerHandler consumer=null; @PostConstruct
public void startListerConsumer(){
consumer= new ConsumerListener(consumerProperty).startListen();
} @PreDestroy
public void shutDown(){
if(consumer!=null){
consumer.shutdown();
}
}
}
这是一个属性工程的bean,当这个bean被创建完成后,会执行startListerConsumer()方法(@PostConstruct的含义就是在bean被创建之后执行) ,startListerConsumer的作用开启监听
ConsumerHandler consumers = new ConsumerHandler( consumerProperty, topics);
consumers.execute(workerNum);
另外,我们看到这个beanFactory有2个属性ProducerProperty 和ConsumerProperty ,这个2个分别是消费者个和生产者的配置,是bean在初始化的时候注入进去的
这里重点介绍一下说ConsumerProperty 的messageContainers属性,它是一个集合对象,包含需要订阅的topic和处理该Topic的实现了MessageListener接口的实现类
public class MessageContainer {
private String topic; private MessageListener messageHandle; }
public interface MessageListener { public void onMessage(ConsumerMessageBO message);
}
上面说到监听到每个消息都会分配一个ConsumerWorker去处理消息,我们看看具ConsumerWorker的
public class ConsumerWorker implements Runnable { private ConsumerRecord<String, String> consumerRecord; public ConsumerWorker(ConsumerRecord record) {
this.consumerRecord = record;
} public void run() {
ConsumerMessageBO consumerMessageBO= JSONObject.parseObject(consumerRecord.value(),ConsumerMessageBO.class);
consumerMessageBO.setOffset(consumerRecord.offset());
consumerMessageBO.setPartition(consumerRecord.partition());
for(MessageContainer messageContainer: PropertyFactory.consumerProperty.getMessageContainers()){
if(consumerRecord.topic().equals(messageContainer.getTopic())){
messageContainer.getMessageHandle().onMessage(consumerMessageBO);
}
} }
根据监听到topic,然后和ConsumerProperty 的messageContainers属性的topic进行比对,找到对应topic处理的实现类调用其onMessage方法
我们JAVA的核心代码基本已经写完了
第三步、业务方接入我们封装的部分
新建一个spring-kafka.xml文件
<bean id="consumerProperty" class="com.meiren.message.kafka.beans.ConsumerProperty">
<property name="brokerList" value="${kafka.bootstrap.servers}"/>
<property name="groupId" value="${kafka.group.id}"/>
<property name="messageContainers" >
<list>
<ref bean="smsMessageContainer"></ref>
<ref bean="emailMessageContainer"></ref>
</list>
</property>
</bean>
<bean id="producerProperty" class="com.meiren.message.kafka.beans.ProducerProperty">
<property name="brokerList" value="${kafka.bootstrap.servers}"/>
</bean> <bean id ="emailMessageHandler" class="com.meiren.message.kafka.handle.EmailMessageHandler"/>
<bean id ="smsMessageHandler" class="com.meiren.message.kafka.handle.SmsMessageHandler"/> <bean id="smsMessageContainer" class="com.meiren.message.kafka.beans.MessageContainer">
<constructor-arg value="sms_async_send"/>
<property name="messageHandle" ref="smsMessageHandler"></property>
</bean>
<bean id ="emailMessageContainer" class="com.meiren.message.kafka.beans.MessageContainer">
<constructor-arg value="email_async_send"/>
<property name="messageHandle" ref="emailMessageHandler"></property>
</bean>
<!--配置工厂类 -->
<bean class="com.meiren.message.kafka.beans.PropertyFactory">
<property name="consumerProperty" ref="consumerProperty"/>
<property name="producerProperty" ref="producerProperty"/>
</bean>
</beans>
这个配置文件对应的就是PropertyFactory的属性,其实就是消费者个和生产者的配置。
我们配置好这个文件后,我们需要一个消息实现类
public class SmsMessageHandler implements MessageListener{
public static final Logger log= LoggerFactory.getLogger(SmsMessageHandler.class);
@Autowired
private SmsSendLogDao smsSendLogDao;
public void onMessage(ConsumerMessageBO consumerMessageBO) { } }catch (Exception e){
System.out.println("转换消息异常:"+e.getMessage());
} }
只要实现了MessageListener接口,并且在spring-kafka.xm配置好对应的topic就可以了
<bean id="smsMessageContainer" class="com.meiren.message.kafka.beans.MessageContainer">
<constructor-arg value="sms_async_send"/>
<property name="messageHandle" ref="smsMessageHandler"></property>
</bean>
整个接入就完成了,由于这是第一版本,所以封装的程度还不算很好,但是也基本符合应用(一个配置文件,一个实现类),有不足之处将会在后面版本进行完善迭代。
至此我们已经将kafka集成spring的功能简单实现了,下一篇我将介绍消息队列(kafka)的一些实际应用。
JAVA封装消息中间件调用二(kafka消费者篇)的更多相关文章
- JAVA封装消息中间件调用一(kafka生产者篇)
这段时间因为工作关系一直在忙于消息中间件的发开,现在趁着项目收尾阶段分享下对kafka的一些使用心得. kafka的原理我这里就不做介绍了,可参考http://orchome.com/kafka/in ...
- Spring Boot 自定义kafka 消费者配置 ContainerFactory最佳实践
Spring Boot 自定义kafka 消费者配置 ContainerFactory最佳实践 本篇博文主要提供一个在 SpringBoot 中自定义 kafka配置的实践,想象这样一个场景:你的系统 ...
- 二、Cocos2dx中Android部分的c++和java实现相互调用(高级篇)
本文由qinning199原创,转载请注明:http://www.cocos2dx.net/?p=97 本文目的 要完成在cocos2dx的场景上一个点击事件,传递一个消息到java层,下面让我们看看 ...
- Java 面试知识点解析(二)——高并发编程篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- 消息中间件系列三:使用RabbitMq原生Java客户端进行消息通信(消费者(接收方)自动确认模式、消费者(接收方)自行确认模式、生产者(发送方)确认模式)
准备工作: 1)安装RabbitMQ,参考文章:消息中间件系列二:RabbitMQ入门(基本概念.RabbitMQ的安装和运行) 2.)分别新建名为OriginalRabbitMQProducer和O ...
- 4 kafka集群部署及kafka生产者java客户端编程 + kafka消费者java客户端编程
本博文的主要内容有 kafka的单机模式部署 kafka的分布式模式部署 生产者java客户端编程 消费者java客户端编程 运行kafka ,需要依赖 zookeeper,你可以使用已有的 zo ...
- Java微服务(二):服务消费者与提供者搭建
本文接着上一篇写的<Java微服务(一):dubbo-admin控制台的使用>,上篇文章介绍了docker,zookeeper环境的安装,并参考dubbo官网演示了dubbo-admin控 ...
- Java多线程编程实战指南(核心篇)读书笔记(二)
(尊重劳动成果,转载请注明出处:http://blog.csdn.net/qq_25827845/article/details/76651408冷血之心的博客) 博主准备恶补一番Java高并发编程相 ...
- android-async-http二次封装和调用
Android android-async-http二次封装和调用 在开发过程中,网络请求这块的使我们常常遇到的一个问题,今天去github 站点上面学习android-async-http,认为还 ...
随机推荐
- 【CF625E】Frog Fights(模拟)
[CF625E]Frog Fights(模拟) 题面 CF 洛谷 翻译: 有\(n\)只青蛙在一个被分为了\(m\)等分的圆上,对于每份顺时针依次标号. 初始时每只青蛙所在的位置是\(p_i\),速度 ...
- 【learning】加权拟阵与贪心
首先.. 这篇东西的话算是一个关于拟阵部分知识的小总结,有些语言相对来说偏向便于理解方面,所以可能..有一些说法会不是那么严谨大概是这样 一些概念 线性无关:一组数据中没有一个量可以写成其余量的线 ...
- 浅谈cocosd之autorelease\retain\release的理解
三种情况,引出问题: 1) new出来的对象需要释放,而释放时,如果有其他人引用了这个对象,再次使用这个对象时,则会出现野指针情况. ==> 于是出现了引用计数的释放管理机制. 2) 对于一 ...
- HDU 4352 数位dp
XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- 题解 UVA1184 【Air Raid】
有向无环图(DAG)的最小路径覆盖的模板题. 定义:在一个有向图中,找出最少的路径,使得这些路径经过了所有的点. 由题意可得这是一个有向图,而路径不能相交,于是我们知道这是无向图的不相交最小路径覆盖问 ...
- Tensorflow Batch normalization函数
Tensorflow Batch normalization函数 觉得有用的话,欢迎一起讨论相互学习~Follow Me 参考文献 stackoverflow上tensorflow实现BN的不同函数的 ...
- C++构造函数和析构函数顺序
构造函数 先看看构造函数的调用顺序规则,只要我们在平时编程的时候遵守这种约定,任何关于构造函数的调用问题都能解决:构造函数的调用顺序总是如下:1.基类构造函数.如果有多个基类,则构造函数的调用顺 ...
- nginx 初探 之反向代理
首先要解释的是什么叫做反向代理? 平时我们浏览网页可以输入网址直接访问, 但如果访问国外的网站, 可能就没那么简单('中国特色'), 这时候我们需要配置一个代理服务器, 然后通过此服务器中转来访 ...
- 2017.5.27 NOIP模拟赛(hzwer2014-5-16 NOIP模拟赛)
期望得分:100+100+60+30=290 实际得分:100+20+60+0=180 当务之急:提高一次正确率 Problem 1 双色球(ball.cpp/c/pas) [题目描述] 机房来了新一 ...
- SQL Server (MSSQLSERVER) 无法启动,错误代码 3417,提示Windows不能在本地计算机启动。
我的电脑为例: 1.打开sql server的安装路径:C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA 2.将 ...