公司kiwi-util包源码分析
代码是监听消息中间件的工具包。最开始招我进来的主管写的,现在已经是总监级别,不再写代码。记得16年夏天入职时候还是我还是小白一枚,感谢主管给了我机会。来了之后才发现真的是大神一枚,在此收下我的膝盖。
使用java监听activeMQ的相关链接为:
https://blog.csdn.net/zbw18297786698/article/details/52994746
话不多说,开始解析。
一 初始化加载配置信息
代码监听mq消息时配置的消息队列如下,启动时候,加载该配置:
@Component("BootSpringListener")
public class BootSpringListener implements ApplicationListener {
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
final String key = UUID.randomUUID().toString();
try {
Resource[] resource = new PathMatchingResourcePatternResolver().getResources("classpath*:bussiness/" + bussinessSide + "/message*.xml");
logger.info("[key={}][init messageService resource size={}]", key, resource.length);
for (Resource res : resource) {
messageService.configure(res.getInputStream());
}
} catch (Exception e) {
logger.error("[key={}][exception={}]", key, e.getMessage(), e);
}
logger.info("init messageService");
}
}
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<MessageConfigs>
<client name="cartoonactivemq">
<factory>com.*.kiwi.message.impl.activemq.ActiveMQMessageClientFactory</factory>
<properties>
<property key="messageSwitch" value="${cartoon.message.switch}"/>
<property key="maxConnections" value="5"/>
<property key="maximumActive" value="100"/>
<property key="username" value="${cartoon.activemq.username}"/>
<property key="password" value="${cartoon.activemq.password}"/>
<property key="url" value="${cartoon.activemq.url}"/>
</properties>
</client>
<destination>
<name>${activemq.reading.message.prefix}picturebook_property_modify</name>
<type>queue</type>
<client>readingactivemq</client>
<processor>com.*.search.api.core.service.search.message.mongo.PartialMongoUpdateProcessor</processor>
<mode>listen</mode>
<properties>
<property key="threadPoolSize" value="100"/>
</properties>
</destination>
</MessageConfigs>
二 加载配置步骤:
(1)解析xml: MessageConfigs configs = jaxbBinder.fromXML(is);
这里的类 MessageConfigs 为使用XmlRootElement将类映射到XML元素,具体参考:
http://desert3.iteye.com/blog/1570092
(2)从client中初始化客户端连接池【详细见代码注释A处】,并将每个destination中都加入在xml中对应的客户端,并通过destination的initialize方法真正开始监听消息【详细见代码注释B处】
(3)destination中含有消息队列的基本配置,消息队列名称、消息处理器、线程池size大小,以及封装的consumer。而consumer则是根据destination的信息来构造,并初始化。Consumer consumer = client.createConsumer(this);这里this即为destination。
(3)destination.initialize 会调用consumer.start
public void configure(InputStream is) {
try {
JaxbBinder jaxbBinder = new JaxbBinder(MessageConfigs.class);
MessageConfigs configs = jaxbBinder.fromXML(is);
//初始化客户端、连接池
for (ClientConfig clientConfig : configs.getClientConfig()) {
//使用类加载器创建对象实例
MessageClientFactory messageClientFactory = (MessageClientFactory) Class.forName(clientConfig.getFactory()).newInstance();
//A.此处保存mq连接的基本信息
MessageClient client = messageClientFactory.createMessageClient(clientConfig.getProperties());
client.setProperties(clientConfig.getProperties());
client.setName(clientConfig.getName());
client.initialize();
clientMap.put(client.getName(), client);
}
//初始化每一个消息目的地(Queue或topic,及其生产者和消费者)
for (DestinationConfig destinationConfig : configs.getDestinationConfigs()) {
MessageClient client = this.clientMap.get(destinationConfig.getClient());
DestinationImpl destination = new DestinationImpl(destinationConfig.getName(), destinationConfig.getType(), client);
destination.setProcessor(destinationConfig.getProcessor());
destination.setMode(destinationConfig.getMode());
destination.setProperties(destinationConfig.getProperties());
destination.setClient(client);
destination.setTransacted(destinationConfig.isTransacted());
destination.setEnableRecover(destinationConfig.isEnableRecover());
//B.真正开始监听消息
destination.initialize();
this.destinationMap.put(destination.getName(), destination);
}
} catch (Exception e) {
throw new RuntimeException("message configure error: ", e);
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
throw new RuntimeException("message configure error: ", e);
}
}
}
}
###三 创建Consumer以及session
Client根据destination构造Consumer类,注意这里创建session的方式:
session = connection.createSession(destination.isTransacted(), ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE);
即为逐条消息确认,每消费一条确认一条(INDIVIDUAL_ACKNOWLEDGE)),且不开启事务。
public Consumer createConsumer(Destination destination) {
try {
connection = connectionFactory.getConnectionFactory().createConnection();
//注意这里sessi
session = connection.createSession(destination.isTransacted(), ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE);
javax.jms.Destination dest = null;
if(Destination.TYPE_QUEUE.equals(destination.getType())) {
dest = session.createQueue(destination.getName());
} else if(Destination.TYPE_TOPIC.equals(destination.getType())) {
dest = session.createTopic(destination.getName());
}
MessageConsumer messageConsumer = null;
if(isDurable && dest instanceof Topic) {
messageConsumer = session.createDurableSubscriber((Topic)dest, clientId +"-"+ destination.getName());
} else {
messageConsumer = session.createConsumer(dest);
}
ActiveMQConsumer consumer = new ActiveMQConsumer();
consumer.setConsumer(messageConsumer);
consumer.setSession(session);
consumer.setDestination(destination);
consumer.setConnection(connection);
String threadPoolSizeString = destination.getProperties().get("threadPoolSize");
if(threadPoolSizeString != null) {
consumer.setThreadPoolSize(Integer.parseInt(threadPoolSizeString));
}
return consumer;
} catch (JMSException e) {
logger.error("init producer for {} error", destination.getName(), e);
}
return null;
}
###四 consumer类详解
consumer基础:
consumer类已经与destination绑定,一个queue对应了一个consumer类对象的实例。
consumer类实现了callback【各种processor 调用finished时候回调 】,监听消息队列MessageListener 【onMessage接口】,以及conumser【start、stop】
当消息抵达时,consumer的onMessage接口被调用,触发处理逻辑:
(1)设置回调为当前类,finished时候触发,如果finished为false则session回滚或recover【根据是否配置事务来设置】
(2)实例化该处理器,并提交至线程池
@Override
public void onMessage(Message message) {
this.processingMessageCount.incrementAndGet();
try {
logger.info("[module:message] [action:start] [id:{}]", message.getJMSMessageID());
Object msg = null;
try {
messageText = JsonBinder.buildNormalBinder().toJson(msg);
} catch (Exception e) {
}
logger.info("[module:message] [action:parse] [id:{}] [content:{}]", message.getJMSMessageID(), messageText);
if (destination.getProcessor() != null) {
Class compileClazz = Class.forName(destination.getProcessor());
Processor processorObject = (Processor) compileClazz.newInstance();
Map<String, Object> context = new HashMap<String, Object>();
context.put("original", message);
//设置回调为当前consumer类
processorObject.setCallback(this);
processorObject.setContext(context);
processorObject.setMessage(msg);
//
threadPoolExecutor.execute(processorObject);
logger.debug("[module:message] [action:execute] [id:{}] [content:{}]", message.getJMSMessageID(), messageText);
logger.info("[module:message] [action:end] [id:{}] [content:{}]", message.getJMSMessageID(), messageText);
} catch (JMSException e) {
logger.error("consume message {} error!", message.toString(), e);
} finally {
while (this.processingMessageCount.get() >= maxProcessingMessageCount) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.error("consume message {} error!", message.toString(), e);
}
}
}
}