Apache RocketMQ™是一个统一的消息传递引擎,轻量级的数据处理平台。根据我们的研究,在使用越来越多的队列和虚拟主题的情况下,ActiveMQ IO模块遇到了瓶颈。我们尽力通过节流,断路器或降级来解决此问题,但效果不佳。因此,我们那时开始关注流行的消息传递解决方案Kafka。不幸的是,Kafka不能满足我们的要求,特别是在低延迟和高可靠性方面。在这种情况下,我们决定发明一个新的消息传递引擎来处理更广泛的用例集,从传统的发布/订阅方案到大批量实时零损失容忍交易系统。我们认为此解决方案可能是有益的,因此我们希望向社区开放它。如今,已有100多家公司在其业务中使用RocketMQ的开源版本。
————————————————
以上翻译自官网:http://rocketmq.apache.org/docs/faq/ 即为什么Apache为什么在有ActiveMQ和Kafka等很方便实用的消息中间件进而推出RocketMQ的开源版本。
RocketMQ是由阿里捐赠给Apache的一款分布式、队列模型的开源消息中间件,经历了淘宝双十一的洗礼。 所以其非常适用于超大数据量的使用场景。
官网(http://rocketmq.apache.org/)
1.发展历程
2.新特性
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/)
解压
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 ,打印如下表示启动成功:
启动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,打印如下表示启动成功:
停止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包。相关资源也会在文章末尾提供下载:
配置文件:
# 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了。