(转)spring 集成 MetaQ

来源:互联网 发布:vscode的php插件 编辑:程序博客网 时间:2024/05/21 11:21

原地址:https://my.oschina.net/u/216764/blog/871379

service-metaq-config.xml(需要在web.xml配置以便web启动的时候进行加载)

<context-param>        <param-name>contextConfigLocation</param-name>        <param-value>            classpath*:config/matrix.xml,            classpath*:config/spring-config.xml,            classpath*:config/spring-quartz.xml,            classpath*:config/spring-transaction.xml,            classpath*:config/spring-mybatis.xml,            classpath*:config/dubbo-*.xml,            classpath*:config/core-config.xml,            classpath*:config/service-config.xml,            classpath*:config/service-metaq-config.xml,            classpath:config/codis-config.xml        </param-value>    </context-param>

1、配置消息会话工厂

在spring容器内配置metaqSessionFactory

<bean id="metaqSessionFactory" class="com.taobao.metamorphosis.client.extension.spring.MetaqMessageSessionFactoryBean">        <property name="zkConnect" value="${zkConnect}"/>        <property name="zkSessionTimeoutMs" value="${zkSessionTimeoutMs}"/>        <property name="zkConnectionTimeoutMs" value="${zkConnectionTimeoutMs}"/>        <property name="zkSyncTimeMs" value="${zkSyncTimeMs}"/>    </bean>

主要是zookeeper参数配置,需要跟服务端的zk配置保持一致。更多参数参见AbstractMetaqMessageSessionFactory的javadoc。

2、消息转换器

MetaQ客户端提供了Java序列化实现的JavaSerializationMessageBodyConverter给你使用,你也可以自定义自己的消息body转换器,比如采用其他序列化协议,如protobufs,hessian等。配置一个消息body转换器,比如我们就使用Java序列化

<bean id="messageBodyConverter" class="com.xxx.metaq.hession.serializable.HessianMessageBodyConvert"/>

3、使用MetaqTemplate发送消息

<bean id ="metaqTemplate" class="com.taobao.metamorphosis.client.extension.spring.MetaqTemplate">            <property name="messageSessionFactory" ref="metaqSessionFactory"/>        <property name="messageBodyConverter" ref="messageBodyConverter"/>    </bean>

metaqTemplate用到了上面配置的metaqSessionFactory和messageBodyConverter,用来创建producer和转换消息体。

配置了metaqTemplate之后,你将可以在你要发送消息的JavaBean里引用这个对象,并使用它发送消息。

4、MessageBuilder创建消息并发送

创建一个发送metaq消息的类MetaqApiImplpackage com.xxx.metaq.api.impl;import java.util.concurrent.Semaphore;import javax.annotation.Resource;import com.xxx.dlcommon.logger.Logger;import com.xxx.dlcommon.logger.LoggerFactory;import com.xxx.metaq.api.DlMetaqAPI;import com.taobao.metamorphosis.client.extension.spring.MessageBuilder;import com.taobao.metamorphosis.client.extension.spring.MetaqTemplate;import com.taobao.metamorphosis.client.producer.SendMessageCallback;import com.taobao.metamorphosis.client.producer.SendResult;public class MetaqApiImpl implements DlMetaqAPI {    private final static Logger logger = LoggerFactory.getLogger(MetaqApiImpl.class);    private final static Semaphore permits = new Semaphore(2000);    @Resource    private MetaqTemplate metaqTemplate;    public boolean syncSendMessage(Object message, String topic,String attribute){        try {            MessageBuilder builder = MessageBuilder.withTopic(topic).withBody(message).withAttribute(attribute);            SendResult result  =  metaqTemplate.send(builder);            this.printSendResult(result,topic,attribute);            return result.isSuccess();        } catch (InterruptedException e) {            logger.error("同步推送metaq消息异常.", e);        }        return false;    }    public void asyncSendMessage(Object message, String topic,String attribute){        final String t = topic;        final String attr = attribute;        while(true){            if (permits.tryAcquire()) {                try {                    MessageBuilder builder = MessageBuilder.withTopic(topic).withBody(message).withAttribute(attribute);                    metaqTemplate.send(builder, new SendMessageCallback() {                        public void onMessageSent(SendResult result) {                             printSendResult(result,t,attr);                        }                        public void onException(Throwable e) {                            logger.error("异步推送metaq服务器响应异常.",e);                        }                    });                    break;                }catch (Exception e) {                    logger.error("异步推送metaq消息异常", e);                }finally{                    permits.release();                }            }else{                Thread.yield();            }        }    }    private void printSendResult(SendResult result,String topic,String attr){        if(result !=null){            logger.info("发送metaq消息服务器响应结果success=>{},topic=>{},attr=>{},offset=>{},partition=>{}",                result.isSuccess(), topic, attr, result.getOffset(),                (result.getPartition() != null ? result.getPartition().toString() : ""));        }else{            logger.info("发送metaq消息服务器响应结果为空.");        }    }}注入到service-metaq-config.xml<bean id="dlMetaqAPI" class="com.xxx.metaq.api.impl.MetaqApiImpl"></bean>

5、消息的订阅

<bean id="companyOrderCollection" class="com.taobao.metamorphosis.client.extension.spring.MetaqTopic">        <property name="group" value="companyOrderCollection" />        <property name="topic" value="orderdata" />        <property name="maxBufferSize" value="1048576" />    </bean>    <bean id="companyOrderCommentCollection" class="com.taobao.metamorphosis.client.extension.spring.MetaqTopic">        <property name="group" value="companyOrderCommentCollection" />        <property name="topic" value="orderdata" />        <property name="maxBufferSize" value="1048576" />    </bean>

配置了订阅者的分组,想要订阅的topic以及maxBufferSize大小。

6、继承DefaultMessageListener

/** * 订单评价消息处理 * @author lushuai * */public class OrderCommentMsgListener extends DlMessageListener<OrderMessage>{    private static final Logger       logger = LoggerFactory.getLogger(OrderCommentMsgListener.class);    @Resource    private DlOrderCommentAPI dlOrderCommentAPI;    @Resource    private DlorderServiceAPI dlorderServiceAPI;    @Resource    private DlBizBaseInfoAPI dlBizBaseInfoAPI;    @Override    public boolean filter(String arg0, Message arg1) {        return true;    }    @Override    public void recieveMessage(MetaqMessage<OrderMessage> message) {        logger.info("single msg attribute------>" + message.getAttribute());        logger.info("single msg body----------->" + message.getBody().toString());        System.out.println("-------------->Single Received Message:"+message.getBody().toString());        System.out.println("-------------->Single Received Message:"+message.getBody().getOrderNo());        try {            //如果订单"完成",更新订单商品对应的常购信息            if(null != message && null != message.getBody() && null != message.getBody().getOrderNo() && ("finish".equals(message.getAttribute()) || "update".equals(message.getAttribute()))){                ServiceResult<OrderDTO> serviceResult = dlorderServiceAPI.searchOrderByOrderId(message.getBody().getOrderNo());                Map<String, Object> mapParam = new HashMap<String, Object>();                mapParam.put("orderNo", message.getBody().getOrderNo());                //通过订单号获取订单评价表状态                ServiceResult<List<DlOrderCommentDTO>> serviceResult1 = dlOrderCommentAPI.getOrderCommentDetailList(mapParam,                        null);                if(serviceResult.getSuccess() && serviceResult.getResult() != null && serviceResult.getResult().getStatus().equals(OrderCommon.ORDER_HAVE_FINISH) && serviceResult1==null){  //判断如果是交易完成                    DlOrderCommentDTO orderCommentDTO = new DlOrderCommentDTO();                    orderCommentDTO.setBuyerId(serviceResult.getResult().getBuyerId());                    orderCommentDTO.setSellerId(serviceResult.getResult().getSellerId());                    Map<String, Object> map = new HashMap<String, Object>();                    map.put("companyId", serviceResult.getResult().getBuyerId());                    ServiceResult<DlBizBaseInfoDTO> result = dlBizBaseInfoAPI.getBizBaseInfo(map);                    if(result.getSuccess() && result.getResult() != null){                        orderCommentDTO.setBuyerType(result.getResult().getCompanyType().equals("S01")?"01":"02");                    }                    orderCommentDTO.setOrderNo(serviceResult.getResult().getOrderNo());                    orderCommentDTO.setAmount(serviceResult.getResult().getAmount());                    orderCommentDTO.setPlaceOrderTime(serviceResult.getResult().getDate()); //下单时间                    orderCommentDTO.setFinishOrderTime(serviceResult.getResult().getConfirmDate()); //订单完成时间                    dlOrderCommentAPI.addOrderComment(orderCommentDTO);                }            }        } catch (Exception e) {            logger.error(e.getMessage());        }    }    @Override    public void recieveMessages(List<MetaqMessage<OrderMessage>> messages) {        try {            for(MetaqMessage<OrderMessage> message: messages){                logger.info("multi msg------>" + message.getAttribute());                logger.info("multi msg------>" + message.getBody().toString());                System.out.println("-------------->Multi Received Message:"+message.getBody().toString());                System.out.println("-------------->Multi Received Message:"+message.getBody().getOrderNo());                if(null != message && null != message.getBody() && null != message.getBody().getOrderNo() && ("finish".equals(message.getAttribute()) || "update".equals(message.getAttribute()))){                    ServiceResult<OrderDTO> serviceResult = dlorderServiceAPI.searchOrderByOrderId(message.getBody().getOrderNo());                    Map<String, Object> mapParam = new HashMap<String, Object>();                    mapParam.put("orderNo", message.getBody().getOrderNo());                    //通过订单号获取订单评价表状态                    ServiceResult<List<DlOrderCommentDTO>> serviceResult1 = dlOrderCommentAPI.getOrderCommentDetailList(mapParam,                            null);                    if(serviceResult.getSuccess() && serviceResult.getResult() != null && serviceResult.getResult().getStatus().equals(OrderCommon.ORDER_HAVE_FINISH) && serviceResult1.getResult()==null){  //判断如果是交易完成                        DlOrderCommentDTO orderCommentDTO = new DlOrderCommentDTO();                        orderCommentDTO.setBuyerId(serviceResult.getResult().getBuyerId());                        orderCommentDTO.setSellerId(serviceResult.getResult().getSellerId());                        //获取买家名称                        Map<String, Object> map = new HashMap<String, Object>();                        map.put("companyId", serviceResult.getResult().getBuyerId());                        ServiceResult<DlBizBaseInfoDTO> result = dlBizBaseInfoAPI.getBizBaseInfo(map);                        if(result.getSuccess() && result.getResult() != null){                            orderCommentDTO.setBuyerType(result.getResult().getCompanyType().equals("S01")?"01":"02");                            orderCommentDTO.setBuyerName(result.getResult().getCompanyName());                            orderCommentDTO.setBuyerAreaCode(result.getResult().getRegAreaCode());                        }                        //获取卖家名称                        map.clear();                        map.put("companyId", serviceResult.getResult().getSellerId());                        ServiceResult<DlBizBaseInfoDTO> result1 = dlBizBaseInfoAPI.getBizBaseInfo(map);                        if(result1.getSuccess() && result1.getResult() != null){                            orderCommentDTO.setSellerName(result1.getResult().getCompanyName());                            orderCommentDTO.setSellerAreaCode(result1.getResult().getRegAreaCode());                        }                        orderCommentDTO.setCreatedPerson(CompanyCommon.OPERATING_PERSON);                        orderCommentDTO.setUpdatedPerson(CompanyCommon.OPERATING_PERSON);                        orderCommentDTO.setOrderNo(serviceResult.getResult().getOrderNo());                        orderCommentDTO.setAmount(serviceResult.getResult().getAmount());                        orderCommentDTO.setPlaceOrderTime(serviceResult.getResult().getDate()); //下单时间                        orderCommentDTO.setFinishOrderTime(serviceResult.getResult().getConfirmDate()); //订单完成时间                        dlOrderCommentAPI.addOrderComment(orderCommentDTO);                    }                }            }        } catch (Exception e) {            logger.error(e.getMessage());        }    }}/** * 消息消费监听器代替metaq Client提供的com.taobao.metamorphosis.client.extension.spring.DefaultMessageListener</br> * <tt>说明:</tt></br> * <tt>1.使用此监听器是原来的com.taobao.metamorphosis.client.extension.spring.MessageListenerContainer需要换成com.xxx.metaq.listener.DlMessageListenerContainer</tt></br> * <tt>2.通过配置isMultiple可实现是否批量触发消息给消费者。默认isMultiple=true。若消费者不需要批量消费消息。设置isMultiple=false即可(等同于使用com.taobao.metamorphosis.client.extension.spring.DefaultMessageListener) * 当isMultiple=false消费者必须实现{@link com.xxx.metaq.listener.DlMessageListener#recieveMessage(MetaqMessage)}</tt></br> * <tt>3.通过配置maxSize设置每次触发消息条数默认200条</tt></br> * <tt>4.通过waitTime设置等待时长默认500ms</tt></br> * <tt>5.建议当isMultiple=true,若不是特殊需要建议maxSize与waitTime使用默认值。同时分别实现{@link com.xxx.metaq.listener.DlMessageListener#recieveMessage(MetaqMessage)}与{@link com.xxx.metaq.listener.DlMessageListener#recieveMessages(List)}</br> * </tt> * <tt>6.{@link com.xxx.metaq.listener.DlMessageListener#filter(String, Message)}可实现消息过滤。若不需要过滤消息直接返回true,返回false表示当条消息不需要拉取到client</tt></br> *  <tt>示例代码</tt>    <pre> *     class A extends DlMessageListener<Object>{ *      public void recieveMessages(List<MetaqMessage<Object>> msgs) {            logger.info("ticket接收到->" + msgs.size());            this.dealWithMsg(msgs);        }        public void recieveMessage(MetaqMessage<Object> msg) {            if (msg != null) {                List<MetaqMessage<Object>> msgs = new ArrayList<MetaqMessage<Object>>();                msgs.add(msg);                this.dealWithMsg(new ArrayList<MetaqMessage<Object>>());            }        }                    只消费消息Attribute是ticket的消息        public boolean filter(String group, Message message) {                return "ticket".equals(message.getAttribute());        }        private void dealWithMsg(List<MetaqMessage<Object>> msgs) {                //            do something        } *     } *  </pre> * @Filename: DlMessageListener.java * @Version: 1.0 * @Author: WL * @date:2016年4月29日 下午5:19:41 * */public abstract class DlMessageListener<T> extends DefaultMessageListener<T>                                                                            implements                                                                            ConsumerMessageFilter,                                                                            org.springframework.beans.factory.InitializingBean,                                                                            DisposableBean,                                                                            BeanNameAware {    private MessageBodyConverter<?> messageBodyConverter;    static final Logger             logger = LoggerFactory.getLogger(DefaultMessageListener.class);    public abstract void recieveMessages(List<MetaqMessage<T>> msgs);    public abstract void recieveMessage(MetaqMessage<T> msg);    public abstract boolean filter(String group, Message message);    /**     * @param msg     * @see com.taobao.metamorphosis.client.extension.spring.DefaultMessageListener#onReceiveMessages(com.taobao.metamorphosis.client.extension.spring.MetaqMessage)     */    @Override    public void onReceiveMessages(MetaqMessage<T> msg) {        logger.info("消息传到错误..");        logger.info("msgId->" + msg.getId());    }    /**     * @param name     * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String)     */    @Override    public void setBeanName(String name) {        this.name = name;    }    /**     * @param group     * @param message     * @return     * @see com.taobao.metamorphosis.consumer.ConsumerMessageFilter#accept(java.lang.String, com.taobao.metamorphosis.Message)     */    @Override    public boolean accept(String group, Message message) {        return this.filter(group, message);    }    @Override    public void recieveMessages(Message message) throws InterruptedException {        if (this.messageBodyConverter != null) {            try {                T body = (T) this.messageBodyConverter.fromByteArray(message.getData());                this.trigger(new MetaqMessage<T>(message, body));                //                this.onReceiveMessages(new MetaqMessage<T>(message, body));            } catch (Exception e) {                logger.error(                    "Convert message body from byte array failed,msg id is " + message.getId()                            + " and topic is " + message.getTopic(), e);                message.setRollbackOnly();            }        } else {            this.trigger(new MetaqMessage<T>(message, null));            //            this.onReceiveMessages(new MetaqMessage<T>(message, null));        }    }    private void trigger(MetaqMessage<T> msg) {        Lock lock = new ReentrantLock();        try {            //            lock.lock();            if (this.isMultiple) {                logger.debug("消费者[" + getName() + "]当前缓存消息记录数->" + message.size());                try {                    lock.lock();                    if (message.size() == maxSize) {                        logger.info("消费者[" + getName() + "]达到设置[" + maxSize + "]条数触发消息.");                        this.releaseMessage();                    }                } catch (Exception e) {                } finally {                    lock.unlock();                }                message.add(msg);                //启动后台线程                await();            } else {                this.recieveMessage(msg);            }        } catch (Exception e) {        } finally {            //            lock.unlock();        }    }    private void await() {        try {            if (mark.compareAndSet(false, true)) {                logger.info("消费者[" + getName() + "]启动后台线程等待消息.");                Runnable run = new Runnable() {                    public void run() {                        timeWorker();                    }                };                getExecutor().execute(run);            }        } catch (Exception e) {            logger.info("消费者[" + getName() + "]后台线程等待消息异常.", e);        }    }    private void timeWorker() {        long st = System.currentTimeMillis();        long en = 0;        long diff = 0;        logger.info("消费者[" + getName() + "]设置超时时长为->" + waitTime);        while (mark.get()) {            if (diff >= waitTime && message.size() > 0 && mark.compareAndSet(true, false)) {                logger.info("消费者[" + getName() + "]超过设置等待时间[" + waitTime + "]触发[" + message.size()                            + "]消息.");                releaseMessage();                en = 0;            }            en = System.currentTimeMillis();            diff = en - st;        }    }    private void releaseMessage() {        logger.info("消费者[" + getName() + "]开始释放缓存消息...");        Lock lock = new ReentrantLock();        try {            lock.lock();            logger.info("消费者[" + getName() + "]释放[" + message.size() + "]条消息.");            if (message.size() > 0) {                List<MetaqMessage<T>> temp = new ArrayList<MetaqMessage<T>>(message);                message.clear();                this.recieveMessages(temp);            }        } catch (Exception e) {            e.printStackTrace();            logger.info("消费者[" + getName() + "]释放缓存消息异常.", e);        } finally {            lock.unlock();            logger.info("消费者[" + getName() + "]释放缓存消息完成.");        }    }    @Override    public void afterPropertiesSet() throws Exception {        if (this.processThreads > 0) {            this.executor = Executors.newFixedThreadPool(this.processThreads);        }    }    @Override    public void destroy() throws Exception {        if (this.executor != null) {            this.executor.shutdown();            this.executor = null;        }    }    @Override    public Executor getExecutor() {        return this.executor;    }    private AtomicBoolean                       mark           = new AtomicBoolean(false);    private int                                 processThreads = -1;    private ExecutorService                     executor;    /**     * 达到多条触发 默认200 仅当isMultiple=true 生效     */    private Integer                             maxSize        = 200;    /**     * 等待时间 默认500ms仅当isMultiple=true生效     */    private Long                                waitTime       = 500l;    private ArrayBlockingQueue<MetaqMessage<T>> message        = new ArrayBlockingQueue<MetaqMessage<T>>(                                                                   maxSize, true);    /**     * 是否多条触发 默认true     */    private Boolean                             isMultiple     = true;    protected String                            name;    /**     * @param name     * The name to set.     */    public void setName(String name) {        this.name = name;    }    /**     * @return Returns the name     */    public String getName() {        return name;    }    protected void setExecutor(ExecutorService executor) {        this.executor = executor;    }    /**     * 默认true     */    public void setIsMultiple(Boolean isMultiple) {        this.isMultiple = isMultiple;    }    /**     * 默认200大小     * The maxSize to set.     */    public void setMaxSize(Integer maxSize) {        this.maxSize = maxSize;    }    /**     * 默认值 200ms     * The waitTime to set.     */    public void setWaitTime(Long waitTime) {        this.waitTime = waitTime;    }    /**     * Returns the threads number for processing messages.     *      * @return     */    public int getProcessThreads() {        return this.processThreads;    }    public MessageBodyConverter<?> getMessageBodyConverter() {        return this.messageBodyConverter;    }    public void setMessageBodyConverter(MessageBodyConverter<?> messageBodyConverter) {        this.messageBodyConverter = messageBodyConverter;    }    /**     * Set the threads number for processing messages.     *      * @param processThreads     */    public void setProcessThreads(int processThreads) {        if (processThreads < 0) {            throw new IllegalArgumentException("Invalid processThreads value:" + processThreads);        }        this.processThreads = processThreads;    }}

onReceiveMessages收到的是封装Message后的MetaqMessage对象,
它会使用你配置的消息体转换器将byte数组转换成java.util.Date对象,
你可以直接通过getBody获取上文metaqTemplate例子中发送的日期对象。

7 、配置MessageListenerContainer

<bean id="companyOrderCollection" class="com.taobao.metamorphosis.client.extension.spring.MetaqTopic">        <property name="group" value="companyOrderCollection" />        <property name="topic" value="orderdata" />        <property name="maxBufferSize" value="1048576" />    </bean>    <bean id="companyOrderCommentCollection" class="com.taobao.metamorphosis.client.extension.spring.MetaqTopic">        <property name="group" value="companyOrderCommentCollection" />        <property name="topic" value="orderdata" />        <property name="maxBufferSize" value="1048576" />    </bean>    <bean id="orderMessageListener" class="com.xxx.dlcompany.service.util.OrderMessageConsumer">        <property name="processThreads" value="10" />    </bean>    <bean id="orderCommentMessageListener" class="com.xxx.dlcompany.service.util.OrderCommentMsgListener">        <property name="processThreads" value="10" />    </bean>    <bean id="listenerContainer" class="com.xxx.metaq.listener.DlMessageListenerContainer">        <property name="messageSessionFactory" ref="metaqSessionFactory" />        <property name="messageBodyConverter" ref="messageBodyConverter" />        <property name="subscribers">            <map>                <entry key-ref="companyOrderCollection" value-ref="orderMessageListener" />                <entry key-ref="companyOrderCommentCollection" value-ref="orderCommentMessageListener" />            </map>        </property>    </bean>