菜鸟也能学会RocketMq
RocketMq是什么
RocketMQ 是一款分布式、队列模型的消息中间件,具有以下特点:
能够保证严格的消息顺序
提供丰富的消息拉取模式
高效的订阅者水平扩展能力
实时的消息订阅机制
亿级消息堆积能力
选择RocketMQ的理由:
强调集群模式无单点,可扩展,任意一点高可用,水平扩展
海量数据的堆积能力,消息堆积后,写入延迟低
支持上万个队列
消息失败重试机制
消息可查询
开源社区灵活
成熟度(支持阿里双十一)
Windows下开发环境搭建
考虑到大部分Java开发者还是习惯于在windows环境下开发,因此,推荐一篇文章,从资料下载,环境配置到实际操作都有详细的描述
https://blog.csdn.net/zxl646801924/article/details/105637278#comments_15530472
学会上面的搭建以后,我们来到代码实战
代码实战
思路:启动消息服务器->生产者实现->消费者实现
启动服务器
上面的文章里已经有 NameServer 和 Broker 两个部分的教学,这里就不细说了。
生产者实现
我们需要在项目中加上依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.7.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.14</version>
<scope>provided</scope>
</dependency>
首先可以先创建一个配置类
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: lfr
* @Date: 2020/4/12 10:28
* @Description: mq生产者配置
*/
@Getter
@Setter
@ToString
@Configuration
@ConfigurationProperties(prefix = "rocketmq.producer")
public class MQProducerConfigure {
public static final Logger LOGGER = LoggerFactory.getLogger(MQProducerConfigure.class);
private String groupName="producer-group";
private String namesrvAddr="127.0.0.1:9876";
// 消息最大值
private Integer maxMessageSize=4096;
// 消息发送超时时间
private Integer sendMsgTimeOut=300;
// 失败重试次数
private Integer retryTimesWhenSendFailed=2;
/**
* mq 生成者配置
* @return
* @throws MQClientException
*/
@Bean
@ConditionalOnProperty(prefix = "rocketmq.producer", value = "isOnOff", havingValue = "on")
public DefaultMQProducer defaultProducer() throws MQClientException {
int num = (int) (Math.random()*100);
LOGGER.info("defaultProducer 正在创建---------------------------------------");
DefaultMQProducer producer = new DefaultMQProducer(groupName+num);
producer.setNamesrvAddr(namesrvAddr);
producer.setVipChannelEnabled(false);
producer.setNamespace("restful-service");
producer.setMaxMessageSize(maxMessageSize);
producer.setSendMsgTimeout(sendMsgTimeOut);
producer.setRetryTimesWhenSendAsyncFailed(retryTimesWhenSendFailed);
producer.start();
LOGGER.info("rocketmq producer server 开启成功----------------------------------");
return producer;
}
}
然后再创建一个生产者Controller,用来测试发送消息
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author: lfr
* @Date: 2021/4/12 11:17
* @Description:
*/
@RestController
@RequestMapping("/mqProducer")
public class MQProducerController {
public static final Logger LOGGER = LoggerFactory.getLogger(MQProducerController.class);
@Autowired(required = false)
DefaultMQProducer defaultMQProducer ;//= new DefaultMQProducer();
/**
* 发送简单的MQ消息
* @param msg
* @return
*/
@GetMapping("/send")
public String send(String msg) throws InterruptedException, RemotingException, MQClientException, MQBrokerException {
MQProducerConfigure configure = new MQProducerConfigure();
defaultMQProducer = configure.defaultProducer();
String s="";
if (StringUtils.isEmpty(msg)) {
return s;
}
LOGGER.info("发送MQ消息内容:" + msg);
System.out.println("发送MQ消息内容:" + msg);
Message sendMsg = new Message("TestTopic", "TestTag", msg.getBytes());
// 默认3秒超时
SendResult sendResult = defaultMQProducer.send(sendMsg);
LOGGER.info("消息发送响应:" + sendResult.toString());
System.out.println();
return sendResult.toString();
}
}
启动项目,在浏览器访问我们的生产者
http://127.0.0.1:9010/restful-service/mqProducer/send?msg=hello
可以看到,调用后返回了这样的信息
消费者实现
根据生产者的地址,toptic来生成消费者
import java.util.List;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
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.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.protocol.heartbeat.MessageModel;
public class MQPushConsumer {
public static void main(String[] args) throws MQClientException {
String groupName = "consumer-group";
// 用于把多个Consumer组织到一起,提高并发处理能力
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(groupName);
// 设置nameServer地址,多个以;分隔
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.setConsumeThreadMin(5);
consumer.setConsumeThreadMax(32);
consumer.setConsumeMessageBatchMaxSize(1);
/**
* 1. CONSUME_FROM_LAST_OFFSET:第一次启动从队列最后位置消费,后续再启动接着上次消费的进度开始消费
2. CONSUME_FROM_FIRST_OFFSET:第一次启动从队列初始位置消费,后续再启动接着上次消费的进度开始消费
3. CONSUME_FROM_TIMESTAMP:第一次启动从指定时间点位置消费,后续再启动接着上次消费的进度开始消费
以上所说的第一次启动是指从来没有消费过的消费者,如果该消费者消费过,那么会在broker端记录该消费者的消费位置,如果该消费者挂了再启动,那么自动从上次消费的进度开始
*/
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
/**
* CLUSTERING:默认模式,同一个ConsumerGroup(groupName相同)每个consumer只消费所订阅消息的一部分内容,同一个ConsumerGroup里所有的Consumer消息加起来才是所
* 订阅topic整体,从而达到负载均衡的目的
* BROADCASTING:同一个ConsumerGroup每个consumer都消费到所订阅topic所有消息,也就是一个消费会被多次分发,被多个consumer消费。
*
*/
consumer.setMessageModel(MessageModel.BROADCASTING);
consumer.subscribe("restful-service%TestTopic", "*");
//4.注册消息监听器
consumer.registerMessageListener(
new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext Context) {
Message msg = list.get(0);
System.out.println(msg);
String msgstr = new String(msg.getBody());
System.out.println(msgstr);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
);
try {
//5.启动消费者
consumer.start();
System.out.println("consumer创建成功");
//Thread.sleep(50 * 1000);
} catch (Exception e) {
e.printStackTrace();
}
//System.out.println("shutdown...");
//6.关闭消费者
//consumer.shutdown();
}
}
启动后,可以看到成功接收生产者发送的消息
监听类我们可以根据实际业务要求来自定义监听类。