一、RocketMQ简介
1、开源的分布式消息中间件
2、可以支持亿级消息堆积,而且在堆积了这么多消息后依然保持写入低延迟。
3、RocketMQ 核心的四大组件:Name Server、Broker、Producer、Consumer
二、本地搭建
1、下载rocket
https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.2.0/rocketmq-all-4.2.0-source-release.zip
2、解压。 rocketmq是个mvn工程
3、进入到项目下
cd rocketmq-all-4.2.0
4、安装
mvn -Prelease-all -DskipTests clean install -U
5、启动
备注:记得配置JAVA_HOME啊!!!
5.1启动nameserver
cd到bin目录下 cd rocketmq-all-4.2.0/distribution/target/apache-rocketmq/bin
执行启动命令 sh mqnamesrv
启动成功日志:
OpenJDK 64-Bit Server VM warning: Using the DefNew young collector with the CMS collector is deprecated and will likely be removed in a future release
OpenJDK 64-Bit Server VM warning: UseCMSCompactAtFullCollection is deprecated and will likely be removed in a future release.
OpenJDK 64-Bit Server VM warning: Cannot open file /dev/shm/rmq_srv_gc.log due to No such file or directory
The Name Server boot success. serializeType=JSON
5.2启动broker
[如果不加autoCreateTopicEnable=true,broker会启动成功,但不会注册到你启动的nameserver上,produce消息时候会报:No route info of this topic]
sh mqbroker -n localhost:9876 autoCreateTopicEnable=true
小插曲:如何查看broker日志,日志路径 Users/xxx/logs/rocketmqlogs/broker.log
注册&启动成功日志
Set user specified name server address: localhost:9876
2020-05-18 14:59:19 INFO PullRequestHoldService - PullRequestHoldService service started
2020-05-18 14:59:19 INFO main - register broker to name server localhost:9876 OK
2020-05-18 14:59:19 INFO main - The broker[xxxxMacBook-Pro.local, 192.168.3.86:10911] boot success. serializeType=JSON and name server is localhost:9876
仅仅启动成功日志
PullRequestHoldService - PullRequestHoldService service started
2020-05-18 14:54:02 INFO main - The broker[xxxxMacBook-Pro.local, 192.168.3.86:10911] boot success. serializeType=JSON
如何查看你的broker有没注册成功
sh mqadmin clusterList -n localhost:9876
6、关闭
sh mqshutdown namesrv
sh mqshutdown broker
三、 生产者和消费者开发
开发代码实现生产者生产消息,消费者消费消息的功能
1、消息生产者开发
记得加上下行代码,关闭vip通道,不然可能会连不上nameserver
producer.setVipChannelEnabled(false);
http://www.daleizhou.tech/posts/rocketmq-remoting.html
https://my.oschina.net/bingoding/blog/685079
package com.liu.mq;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author: beauty
* @Date: 2020/5/6 11:15 上午
*/
public class SpringProducer {
final static Logger logger = LoggerFactory.getLogger(SpringProducer.class);
private String producerGroupName;
private String nameServerAddr;
private DefaultMQProducer producer;
public SpringProducer(String producerGroupName, String nameServerAddr) {
this.producerGroupName = producerGroupName;
this.nameServerAddr = nameServerAddr;
}
public void init() throws Exception {
logger.info("开始启动消息生产者服务...");
//创建一个消息生产者,并设置一个消息生产者组
producer = new DefaultMQProducer(producerGroupName);
//指定 NameServer 地址
producer.setNamesrvAddr(nameServerAddr);
producer.setVipChannelEnabled(false);
//初始化 SpringProducer,整个应用生命周期内只需要初始化一次
producer.start();
logger.info("消息生产者服务启动成功.");
}
public void destroy() {
logger.info("开始关闭消息生产者服务...");
producer.shutdown();
logger.info("消息生产者服务已关闭.");
}
public DefaultMQProducer getProducer() {
return producer;
}
}
2、消息消费者
package com.liu.mq;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @Author: beauty
* @Date: 2020/5/6 1:53 下午
*/
public class SpringConsumer {
final static Logger logger = LoggerFactory.getLogger("SpringConsumer");
private String consumerGroupName;
private String nameServerAddr;
private String topicName;
private DefaultMQPushConsumer consumer;
private MessageListenerConcurrently messageListener;
public SpringConsumer(String consumerGroupName, String nameServerAddr, String topicName, MessageListenerConcurrently messageListener) {
this.consumerGroupName = consumerGroupName;
this.nameServerAddr = nameServerAddr;
this.topicName = topicName;
this.messageListener = messageListener;
}
public void init() throws Exception {
logger.info("开始启动消息消费者服务...");
//创建一个消息消费者,并设置一个消息消费者组
consumer = new DefaultMQPushConsumer(consumerGroupName);
//指定 NameServer 地址
consumer.setNamesrvAddr(nameServerAddr);
//设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//订阅指定 Topic 下的所有消息
consumer.subscribe(topicName, "*");
//注册消息监听器
consumer.registerMessageListener(messageListener);
// 消费者对象在使用之前必须要调用 start 初始化
consumer.start();
logger.info("消息消费者服务启动成功.");
}
public void destroy(){
logger.info("开始关闭消息消费者服务...");
consumer.shutdown();
logger.info("消息消费者服务已关闭.");
}
public DefaultMQPushConsumer getConsumer() {
return consumer;
}
}
3、消息监听器在接收到消息后执行具体的处理逻辑
package com.liu.mq;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.UnsupportedEncodingException;
import java.util.List;
/**
* @Author: beauty
* @Date: 2020/5/6 1:57 下午
*/
public class MessageListener implements MessageListenerConcurrently{
final static Logger logger = LoggerFactory.getLogger("MessageListener");
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
if (list != null) {
for (MessageExt ext : list) {
try {
logger.info("监听到消息 : " + new String(ext.getBody(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
4、生产者的xml配置
(因为你启的是本地的mq,所以nameserverAddr是本地地址,如果你想做单测,往公司的nameserver上发消息,需要问下开发你们公司的nameserver地址)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="producer" class="com.liu.mq.SpringProducer" init-method="init" destroy-method="destroy">
<constructor-arg name="nameServerAddr" value="localhost:9876"/>
<constructor-arg name="producerGroupName" value="MARKET-PRODUCER-GROUPNAME-NOTIFY"/>
</bean>
</beans>
5、消费者的xml文件配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean id="messageListener" class="com.liu.mq.MessageListener" />
<bean id="consumer" class="com.liu.mq.SpringConsumer" init-method="init" destroy-method="destroy">
<constructor-arg name="nameServerAddr" value="localhost:9876"/>
<constructor-arg name="consumerGroupName" value="spring_consumer_group"/>
<constructor-arg name="topicName" value="spring-rocketMQ-topic" />
<constructor-arg name="messageListener" ref="messageListener" />
</bean>
</beans>
四、 调试
1、生产者单测
package com.liu.sptest.mq;
import com.liu.mq.SpringProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.apache.rocketmq.remoting.common.RemotingHelper;
public class SpringLocalProducerTest {
private ApplicationContext container;
@Before
public void setup() {
container = new ClassPathXmlApplicationContext("classpath:spring-producer.xml");
}
@Test
public void sendMessage() throws Exception {
SpringProducer producer = container.getBean(SpringProducer.class);
for (int i = 0; i < 20; i++) {
//创建一条消息对象,指定其主题、标签和消息内容
Message msg = new Message(
"spring-rocketMQ-topic",
null,
("Spring RocketMQ demo " + i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* 消息内容 */
);
//发送消息并返回结果
SendResult sendResult = producer.getProducer().send(msg);
System.out.printf("%s%n", sendResult);
}
}
}
2、消费者单测
package com.liu.sptest.mq;
import com.liu.mq.SpringConsumer;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @Author: beauty
* @Date: 2020/5/6 2:48 下午
*/
public class SpringConsumerTest {
private ApplicationContext container;
@Before
public void setup() {
container = new ClassPathXmlApplicationContext("classpath:spring-consumer.xml");
}
@Test
public void consume() throws Exception {
SpringConsumer consumer = container.getBean(SpringConsumer.class);
Thread.sleep(200 * 1000);
consumer.destroy();
}
}
3、生产者单测执行结果
4、消费者单测结果
五、RocketMQ应用
例如你要用mq模拟消息测试用
可以直接把nameserver改成你们公司的,topic和tag和开发确认,直接用producer就可以发消息了,公司的应用就会消费你的mq
在平台也可以查到mq
参考资料来源:
https://blog.csdn.net/chenaima1314/article/details/79403113
https://my.oschina.net/bingoding/blog/685079
https://blog.csdn.net/wd2014610/article/details/79867322
https://juejin.im/post/5af02571f265da0b9e64fcfd