Spring集成RocketMQ以及应用实例代码

背景

Apache RocketMQ™是一个统一的消息传递引擎,轻量级的数据处理平台。根据我们的研究,在使用越来越多的队列和虚拟主题的情况下,ActiveMQ IO模块遇到了瓶颈。我们尽力通过节流,断路器或降级来解决此问题,但效果不佳。因此,我们那时开始关注流行的消息传递解决方案Kafka。不幸的是,Kafka不能满足我们的要求,特别是在低延迟和高可靠性方面。在这种情况下,我们决定发明一个新的消息传递引擎来处理更广泛的用例集,从传统的发布/订阅方案到大批量实时零损失容忍交易系统。我们认为此解决方案可能是有益的,因此我们希望向社区开放它。如今,已有100多家公司在其业务中使用RocketMQ的开源版本。
————————————————
以上翻译自官网:http://rocketmq.apache.org/docs/faq/ 即为什么Apache为什么在有ActiveMQ和Kafka等很方便实用的消息中间件进而推出RocketMQ的开源版本。

简介

RocketMQ是由阿里捐赠给Apache的一款分布式、队列模型的开源消息中间件,经历了淘宝双十一的洗礼。 所以其非常适用于超大数据量的使用场景。
官网(http://rocketmq.apache.org/)
Spring集成RocketMQ以及应用实例代码_第1张图片
1.发展历程
Spring集成RocketMQ以及应用实例代码_第2张图片
2.新特性
Spring集成RocketMQ以及应用实例代码_第3张图片
3.基本概念

Client端
Producer Group 一类Producer的集合名称,这类Producer通常发送一类消息,且发送逻辑一致
Consumer Group 一类Consumer的集合名称,这类Consumer通常消费一类消息,且消费逻辑一致

Server端
Broker 消息中转角色,负责存储消息,转发消息,这里就是RocketMQ Server
Topic 消息的主题,用于定义并在服务端配置,消费者可以按照主题进行订阅,也就是消息分类,通常一个系统一个Topic
Message 在生产者、消费者、服务器之间传递的消息,一个message必须属于一个Topic 消息是要传递的信息。邮件中必须包含一个主题,该主题可以解释为要发送给您的信的地址。消息还可能具有可选标签和额外的键值对。例如,您可以为消息设置业务密钥,然后在代理服务器上查找消息以在开发过程中诊断问题。
Namesrver 一个无状态的名称服务,可以集群部署,每一个broker启动的时候都会向名称服务器注册,主要是接收broker的注册,接收客户端的路由请求并返回路由信息
Offset 偏移量,消费者拉取消息时需要知道上一次消费到了什么位置, 这一次从哪里开始
Partition 分区,Topic物理上的分组,一个Topic可以分为多个分区,每个分区是一一个有序的队列。 分区中的每条消息都会给分配一个有序的ID,也就是偏移量,保证了顺序,消费的正确性
Tag 用于对消息进行过滤,理解为message的标记,同一业务不同目的的message可以用相同的topic但是 可以用不同的tag来区分
key 消息的KEY字段是为了唯- -表示消息的,方便查问题,不是说必须设置,只是说设置为了方便开发和运维定位问题。 比如:这个KEY可以是订单ID等。
4.下载与安装(下载地址:http://rocketmq.apache.org/release_notes/release-notes-4.7.0/)
Spring集成RocketMQ以及应用实例代码_第4张图片
解压

unzip -d /usr rocketmq-all-4.7.0-bin-release.zip
mv /usr/rocketmq-all-4.7.0-bin-release /usr/rocketmq

启动
启动nameserver,默认4G,在bin目录下执行:nohup sh mqnamesrv &,查看启动是否成功的日志命令:tail -f ~/logs/rocketmqlogs/namesrv.log ,打印如下表示启动成功:
Spring集成RocketMQ以及应用实例代码_第5张图片
启动broker,默认8G,在bin目录下执行:nohup sh mqbroker -n localhost:9876 & ,也可以 nohup sh mqbroker -c …/conf/broker.conf -n 192.168.153.128:9876 autoCreateTopicEnable=true &
这样启动的服务器可以自动创建主题(客户端),不过生产一般不推荐.
查看日志命令:tail -f ~/logs/rocketmqlogs/broker.log,打印如下表示启动成功:
Spring集成RocketMQ以及应用实例代码_第6张图片
停止Broker

bin/mqshutdown broker

停止nameserver

bin/mqshutdown namesrv

发送消息

export NAMESRV ADDR=localhost:9876
bin/tools.sh org.apache.rocketmq.example.quickstart.Producer

接收消息

bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer

引入jar包
rocketmq-client.jar

5.在Spring中引入RocketMq
这里以大家比较熟悉的SSM框架中引入和使用rocketmq为例子,方便大家理解和使用。首先我们需要添加相关的jar包。相关资源也会在文章末尾提供下载:
需要引入的jar包
配置文件:

# rocketmq
jmsconfig.namesrvAddr=127.0.0.1:9876
# 消费RZ数据queue
jmsconfig.topic1=RZdev
# 往RZ推送数据queue
jmsconfig.topic2=TORZdev

在Spring的配置文件中添加相关的配置:
(1)添加注解扫描,如你的项目已经添加,可忽略
	<context:component-scan base-package="com.zhangb">
		<context:exclude-filter type="annotation"
			expression="org.springframework.stereotype.Controller" />
	</context:component-scan>

(2)消息生产者以及消费者相关的配置

<!--RocketMQ  -->
	<!-- 1生产者给RZ发送消息-->
 	<bean id="rocketMQProducerToRZ" class="com.zhangb.rocketmq.RocketMQProducerToRZ"
		init-method="init" destroy-method="destroy" scope="singleton">
		<property name="producerGroup" value="SQProducerGroup" />
		<property name="namesrvAddr" value="${jmsconfig.namesrvAddr}" />
	</bean>
	<!-- 2.消费者,消费RZ发送过来的信息-->
	<bean class="com.zhangb.rocketmq.RocketMQConsumerFromRZ" init-method="init"
		destroy-method="destroy" scope="singleton">
		<property name="consumerGroup" value="SQConsumerGroup" />
		<property name="namesrvAddr" value="${jmsconfig.namesrvAddr}" />
		<property name="topic" value="${jmsconfig.topic1}"/>
		<property name="tag" value="tag"/>
	</bean>
	<!-- 1生产者给KX发送消息-->
	 <bean id="rockeMQProducerToKX" class="com.zhangb.rocketmq.RocketMQProducerToKX"
		init-method="init" destroy-method="destroy" scope="singleton">
		<property name="producerGroup" value="SQProducerGroup" />
		<property name="namesrvAddr" value="${jmsconfig.namesrvAddr}" />
	</bean>

这里我已我所做的项目中用到的三个实例作为例子为大家阐述用法:
1、生产者给RZ发送消息,以下为发送消息的类

package com.zhangb.rocketmq;

import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Value;


public class RocketMQProducerToRZ extends RocketMQProducer<String> {
     
	@Value("${jmsconfig.topic2}")
	private String topic;

	@Override
	public SendResult send(String messageText)
			throws MQClientException, RemotingException, MQBrokerException, InterruptedException {
     
		//我们将消息接收
		Message message = new Message(topic,"tag",messageText.getBytes());
		//传送到服务端
		SendResult send = super.getDefaultMQProducer().send(message);
		return send;
	}
}

在代码中如何调用,这里仅以部分片段展示用法:

	@Autowired
	private RocketMQProducerToRZ rocketMQProducerToRZ;
	
	@RequestMapping(value = "/test")
	@ResponseBody
	public String updateSubUserStatus(HttpServletRequest request, String subUserId, String subStatus) {
     
		JSONObject resultObj = new JSONObject();
		boolean isSucc = false;
		try {
     
			if (subUser != null) {
     
				user = userService.findUserByUserId(subUser.getUserId());
				List<Organization> org = userService.getUserOrganizations(subUser.getUserId());
				if (org != null && org.size() > 0) {
     
					long[] organIds = new long[] {
      org.get(0).getId() };
					user.setOrganizationIds(organIds);
				}
			}
			// 发送至MQ
			List<UserSub> subList = new ArrayList<>();
			subList.add(subUser);
			user.setSubUserList(subList);
			List<User> addList = new ArrayList<>();
			addList.add(user);
			Map<String, Object> map = new HashMap<>();
			map.put("user", addList);
			try {
     
			//此处调用
				rocketMQProducerToRZ.send(JsonUtil.toJson(map));
			} catch (MQClientException | RemotingException | MQBrokerException | InterruptedException e) {
     
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		return getSubUserList(request);
	}

2.消费RZ在队列中的消息

/**
 * 来自RZ的消费消息
 * 
 * @author zhouwei
 *
 */
public class RocketMQConsumerFromRZ {
     
	private final Logger LOGGER = LoggerFactory.getLogger(RocketMQConsumerFromRZ.class);
	private static final String MESSAGE_SUCCESS = "消费成功";
	private static final String MESSAGE_FAILE = "消费失败";

	private DefaultMQPushConsumer defaultMQPushConsumer;
	private String namesrvAddr;
	private String consumerGroup;
	private String topic;
	private String tag;


	@Autowired
	private OrganizationService organizationService;

	@Autowired
	private UserService userService;

	@Autowired
	private SyncDeviceService syncDeviceService;

	@Autowired
	TaskInfoService taskInfoService;

	/**
	 * Spring 初始化
	 */
	public void init() throws InterruptedException, MQClientException {
     

		/**
		 * 设置默认参数
		 */
		defaultMQPushConsumer = new DefaultMQPushConsumer(consumerGroup);
		defaultMQPushConsumer.setNamesrvAddr(namesrvAddr);
		defaultMQPushConsumer.setInstanceName(String.valueOf(System.currentTimeMillis()));

		/**
		 * 暂时不制定Tag
		 */
		//defaultMQPushConsumer.subscribe("RZ2SQdev", "tag");
		defaultMQPushConsumer.subscribe(topic, tag);
		/**
		 * 按照上一次的位置继续消费消息
		 */
		defaultMQPushConsumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

		/**
		 * 设置为集群方式
		 */
		defaultMQPushConsumer.setMessageModel(MessageModel.CLUSTERING);
		/**
		 * 内部类消费消息
		 */
		defaultMQPushConsumer.registerMessageListener(new MessageListenerConcurrently() {
     

			@Override
			public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
     
				for (MessageExt messageExt : msgs) {
     
					String messageText = new String(messageExt.getBody());
					Map<String, String> parseObject = JSONObject.parseObject(messageText,
							new TypeReference<Map<String, String>>() {
     
							});
					if (parseObject.get("org") != null) {
     
						// 组织信息消费
						String string = parseObject.get("org");
						List<Organization> parseArray = JSONObject.parseArray(string, Organization.class);
						organizationService.save(parseArray);
						LOGGER.info(MESSAGE_SUCCESS + messageText);
					}
					if (parseObject.get("user") != null) {
     
						// 用户信息消费
						String string = parseObject.get("user");
						List<User> parseArray = JSONObject.parseArray(string, User.class);
						userService.save(parseArray);
						LOGGER.info(MESSAGE_SUCCESS + messageText);
					}
					if (parseObject.get("device") != null) {
     
						// 设备信息消费
						String string = parseObject.get("device");
						List<SyncDevice> parseArray = JSONObject.parseArray(string, SyncDevice.class);
						syncDeviceService.saveDevice(parseArray);
						LOGGER.info(MESSAGE_SUCCESS + messageText);
					}

				}
				return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
			}
		});

		/**
		 * 启动消费
		 */
		defaultMQPushConsumer.start();

		LOGGER.debug("DefaultMQPushConsumer start success!");
	}

	/**
	 * Spring 关闭消费
	 */
	public void destroy() {
     
		defaultMQPushConsumer.shutdown();
	}

	public void setNamesrvAddr(String namesrvAddr) {
     
		this.namesrvAddr = namesrvAddr;
	}

	public void setConsumerGroup(String consumerGroup) {
     
		this.consumerGroup = consumerGroup;
	}
	public void setTopic(String topic) {
     
		this.topic = topic;
	}

	public void setTag(String tag) {
     
		this.tag = tag;
	}

}

至此完毕,会运用以上方法,我们基本可以在项目中运用rocketmq了。

你可能感兴趣的:(消息队列工具,java,队列,分布式)