kiwi包源码解析

公司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);
                }
            }
        }

    }
千里马8年Android系统及应用开发经验,曾担任过美国unokiwi公司移动端技术总监兼架构师,对系统开发,性能优化,应用高级开发有深入的研究,Android开源定制ROM Lineage的贡献者之一,国内首家线下开辟培训Android Framework课程,拥有2年的Android系统培训经验。成为腾讯课堂专业负责android framework课程分享第一人,致力于提高国内android Framework水平Android Framework领域内是国内各大手机终端科技公司需要的人才,应用开发者都对Android系统充满着好奇,其中的binder是重中之重,都说无binder无Android,binde是Android系统的任督二脉。课程水平循序渐进,由中级再到高级,满足各个层次水平的android开发者。1、灵活使用binder跨进程通信,在app端对它的任何api方法等使用自如2、可以单独分析android系统源码中任何binder部分,分析再也没有难度3、掌握binder驱动本质原理,及对应binder驱动怎么进行跨进程通信,及内存等拷贝方式数据等4、对binder从上层的java app端一直到最底层的内核binder驱动,都可以顺利理通5、针对系统开发过程中遇到的binder报错等分析方法,及binder bug案例学习6、针对面试官任何的binder问题都可以对答自如7、socket这种跨进程通信实战使用8、针对android源码中使用的socket源码轻松掌握9、android系统源码中最常见的socketpair中双向跨进程通信10、使用socket实现一个可以让app执行shell命令的程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值