(转)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>
- (转)spring 集成 MetaQ
- metaq spring
- Spring整合metaq
- metaq spring配置
- MetaQ
- metaq
- metaq
- 转:dwr+spring集成
- MetaQ源码阅读及与Spring结合使用
- Spring集成
- spring spring mvc集成
- Kafka&MetaQ
- metaq部署
- MetaQ初探
- MetaQ-1
- metaq原理
- metaq实例
- MetaQ 安装
- JVM基础(一):内存空间分配及其回收原理
- 访问修饰符public、private、protected、以及不写(默认)时的区别
- 线程间的互斥量和条件变量
- 工作
- 《数据结构学习与实验指导》2-8:用扑克牌计算24点
- (转)spring 集成 MetaQ
- Redis Sentinel 高可用实现说明
- Qt之QSS(样式表语法)
- linux安装redis及phpredis环境配置(超详细)
- spring aop类内部调用不拦截原因及解决方案
- JS 在页面上直接将json数据导出到excel,支持chrome,edge,IE10+,IE9,IE8,Safari,Firefox
- STM32可用的QRCODE二维码生成库
- Zookeeper集群请求处理过程的消息类型
- JAVA 中有关Null的9件事