activemq消息丢失_线上真实生产环境使用ActiveMQ集群案例分享

  • 概述
  • 集群部署架构
  • 线上集群指标
  • 集群使用流程
  • 基本规则
  • 客户端配置
  • 主要参数
  • Java客户端配置与代码实现

一、概述

  • ActiveMQ集群为贝壳找房(前身为链家网)所有业务线产品提供可靠稳定高速消息中间件服务,帮助系统间实现异步通信和模块解耦。长期时间稳定为SE、链家网和搜索团队提供服务。
  • 笔者所在2018年初,线上持续维护的ActiveMQ集群有两个,均采用一主三从方式部署。
  • MQ集群当时为贝壳大多数团队服务,使用方持续增长。当时对于客户端使用的限制较少,集群的连接数,可用队列缓存都是有限的,所以建议大家在线上使用时,务必谨慎规范!

二、集群部署架构

部署说明

  • 集群为“一主三从”架构,LevelDB用于持久化消息的缓存服务。
  • 客户端使用Failover机制连接集群。若集群主节点宕机,ZK将选择出新的主节点,持久化消息不会丢失,非持久化消息丢失。
bdf97869d7407d1045998bc7a2f2c1fe.png

集群架构图

三、线上集群指标

  • 单集群允许总连接数小于1000。
  • 单集群,消息在非持久化状态下性能为,生产者30000 QPS,消费者12000 QPS。
  • 单集群,消息在持久化状态下性能为,生产者5000 QPS,消费者4000 QPS。
  • 单个消息,消息体过大时,对发送速率有明显影响。

四、集群使用流程

1 发送邮件给MQ管理员,注明如下信息:

  • 业务使用场景,生产者和消费者系统说明
  • 是否需要持久化消息
  • 发送方/接收方模式(点对点,主题订阅)
  • 发送方QPS均值、峰值预估
  • 发送方,接收方的连接数均值、峰值预估

2 MQ管理员会评估需求,确认现有资源能否满足。如果可以满足,将提供线上集群连接配置以及线下测试环境集群配置。

3 业务方根据自身系统特点,选择相应开发库(比如Java、PHP已有成熟库和使用方案)。在线下联调测试。

4 告知MQ管理员业务上线时间,MQ管理员负责监控线上服务是否正常运行。

5 在AMQ业务接入方登记页,填写自己的服务信息。

五、基本规则

1.客户端配置

  • MQ允许持久化消息,和非持久化消息。持久化消息会在集群所有节点持久化存储,集群保证不丢消息,但对性能影响较大。消息量不大时,或者消息非常需要可靠传达时,可用持久化方式
  • 消息体内容在2KB以内时,性能较好。建议控制消息体长度,消息体长度超过1MB时,谨慎使用MQ
  • 与任何通信协议相同,生产者/消费者和MQ建立连接和释放连接时会消耗较多资源。确保自己的客户端每次subscribe时,尽量处理完queue所有消息再退出!一定要释放连接
  • MQ可工作在两种方式:点对点、主题订阅模型。请根据自己的业务特点选择合适的模式。

2.主要参数

8119c6c6cbc099cd8a8780d49c6c52ce.png

amq主要参数

六、Java客户端配置与代码实现

  1. spring与activemq 的maven依赖
org.apache.activemqactivemq-core5.7.0org.apache.activemqactivemq-pool5.12.0org.apache.xbeanxbean-spring3.16org.springframeworkspring-jms4.2.1.RELEASE
  1. 属性配置文件 config-activemq.properties
#######activemq#######实际项目中属性值分多环境配置,为方便说明下面列出#activemq.broker.url=${activemq.broker.url}activemq.broker.url=failover:(tcp://172.30.xx.xx:61616, tcp://172.30.xx.xx:61616,                              tcp://172.30.xx.xx:61616) ?initialReconnectDelay=1000                              &maxReconnectDelay=10000 &jms.prefetchPolicy.all=1000&timeout=3000feed.queue.subject=FEED.QUEUE.DEV1feed.topic.subject=FEED.TOPIC.DEVmobile.queue.subject=CONSUMER.MESSAGE.QUEUE.FOLIOqueue.interlink_customer_prod=interlink_customer_prodqueue.interlink_house_prod=interlink_house_prodqueue.check_customer_prod=check_customer_prodqueue.redstar_task=HONGXING.TASK.SCHEDULE.DOCKINGqueue.callRecord_customer_prod=callRecord_customer_prod
  1. mq总(概括性)配置文件spring-activemq.xml
<?xml version="1.0" encoding="UTF-8"?>
  1. 生产者基本配置文件 spring-producer-basic.xml
<?xml version="1.0" encoding="UTF-8"?>
  1. 生产者实例配置spring-producer-callrecord.xml
<?xml version="1.0" encoding="UTF-8"?>

其他生产者配置,如spring-producer-redstar.xml,类似spring-producer-callrecord.xml配置,不再啰嗦列举。

  1. 生产者java代码实例CallRecordMessageProducer.java
package com.lianjia.customer.message.producer;import com.alibaba.fastjson.JSONObject;import com.lianjia.blacklist.api.dto.PhoneHasMarkedDTO;import com.lianjia.common.datasource.DataRegion;import com.lianjia.common.log.RlaLogUtil;import com.lianjia.customer.log.CustomerRlaLogCode;import com.lianjia.customer.service.constant.Constants;import org.apache.commons.collections.CollectionUtils;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.jms.core.JmsTemplate;import org.springframework.stereotype.Service;import javax.annotation.Resource;import javax.jms.Destination;import java.util.*;@Servicepublic class CallRecordMessageProducer {protected Logger logger = LoggerFactory.getLogger(this.getClass());@ResourceJmsTemplate callRecordJmsTemplate;@ResourceDestination callRecordProductDestination;public void sendMessageForDealCallRecord(final String regionStr, final Long brokerUid,                                           final Map phoneHasMarkedDTOMap,                       final Collection phones,final Boolean isEncrypt) {try {if(CollectionUtils.isNotEmpty(phones)){Constants.THREAD_POOL_MQ.execute(new Runnable() {@Overridepublic void run() {long beginTime = System.currentTimeMillis();JSONObject jo = new JSONObject();jo.put("brokerUid", brokerUid);jo.put("phones", phones);jo.put("regionStr", regionStr);jo.put("phoneHasMarkedDTOMap", phoneHasMarkedDTOMap);jo.put("isEncrypt", isEncrypt);//jo.put("businessId", businessId);callRecordJmsTemplate.convertAndSend(callRecordProductDestination, jo.toJSONString());logger.info("==========sendCallRecordMessage:{},threadId:{},brokerUid:{},phones:{},time:{}",jo, Thread.currentThread().getId(), regionStr, brokerUid, phones, System.currentTimeMillis()- beginTime);}});}} catch (Exception e) {RlaLogUtil.log(logger, e, CustomerRlaLogCode.errorMessageSendCheck, brokerUid, phones);}}}
  1. 消费者基本配置文件 spring-consumer-basic.xml
<?xml version="1.0" encoding="UTF-8"?>
  1. 消费者实例配置 spring-consumer-callrecord.xml
<?xml version="1.0" encoding="UTF-8"?>

其他消费者配置,如spring-consumer-resblock.xml参考上面

  1. 消费者实例java代码实现 CallRecordMessageConsumer.java
package com.lianjia.customer.message.consumer;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONArray;import com.alibaba.fastjson.JSONObject;import com.lianjia.common.datasource.DataRegion;import com.lianjia.common.datasource.impl.CustomerRegion;import com.lianjia.common.datasource.transaction.DataRegionsTransactional;import com.lianjia.common.log.RlaLogUtil;import com.lianjia.customer.log.CustomerRlaLogCode;import com.lianjia.customer.service.facade.DataFacade;import com.lianjia.customer.service.facade.LogicFacade;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.jms.support.converter.MessageConverter;import org.springframework.stereotype.Service;import javax.annotation.Resource;import javax.jms.Message;import javax.jms.MessageListener;import java.util.*;@Servicepublic class CallRecordMessageConsumer implements MessageListener {    protected Logger logger = LoggerFactory.getLogger(this.getClass());    @Resource    MessageConverter jmsSimpleMessageConverter;    @Resource    private LogicFacade logicFacade;    @Resource    private DataFacade dataFacade;    @DataRegionsTransactional    public void onMessage(Message message) {        try {            String objMessage = (String) this.jmsSimpleMessageConverter.fromMessage(message);            RlaLogUtil.log(logger, CustomerRlaLogCode.infoCallRecordMessageAccepted, objMessage);            JSONObject jo = JSON.parseObject(objMessage);            String regionStr = jo.getString("regionStr");            Long brokerUid = jo.getLong("brokerUid");            Boolean isEncrypt = jo.getBoolean("isEncrypt");            Object phoneHasMarkedDTOMapObject = jo.get("phoneHasMarkedDTOMap");            JSONArray phoneArr = jo.getJSONArray("phones");            if (phoneArr == null || phoneArr.size() == 0) {                logger.warn("phones is empty: {}", phoneArr);                return;            }            Collection phones = new ArrayList();            for (int i = 0; i < phoneArr.size(); i++) {                phones.add((String) phoneArr.get(i));            }            DataRegion region = CustomerRegion.getByRegionName(regionStr);            //同步黑名单            //TODO 代码省略            //同步客源潜客            //TODO 代码省略        } catch (Exception e) {            RlaLogUtil.log(logger, e, CustomerRlaLogCode.errorCallRecordMessageAccepted);            try {                Thread.sleep(1000);            } catch (InterruptedException e1) {                RlaLogUtil.log(logger, e1, CustomerRlaLogCode.errorInternalServer);            }            throw new RuntimeException(e);        }    }}

按上面配置,生产、消费mq消息demo已经可用了。

欢迎大家关注我,后面我会分享更多精彩的文章给大家。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值