一.安装与部署
在windows上的安装,可搜索其他博客。并进行环境变量配置。
环境:windows10 + jdk1.8(32 位的会报错,建议64),亲身经历。
二.基础实战
启动mq
1.先启动namespace
2.启动broker
3。下载一个rocket-console。
百度云链接:百度网盘 请输入提取码 密码: 7k49
启动并访问http://127.0.0.1:18080/#/topic
打开主题,新建如下
3.代码实操
创建项目,引入pom
org.apache.rocketmq
rocketmq-client
4.4.0
1.生产者
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.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
public class MyProducer {
public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {
DefaultMQProducer producer = new DefaultMQProducer("my_producer");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
Message mes = new Message("tp_1","测试下rocketmq生产者".getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult send = producer.send(mes);
System.out.println(send);
producer.shutdown();
}
}
并启动
2.消费者拉模式
import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer;
import org.apache.rocketmq.client.consumer.PullResult;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Set;
public class MyPullComsumer {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException {
DefaultMQPullConsumer consumer = new DefaultMQPullConsumer("consumer_0");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.start();
Set mes = consumer.fetchSubscribeMessageQueues("tp_1");
for (MessageQueue m : mes) {
PullResult reult = consumer.pull(m, "*", 0, 10);
System.out.println("消息信息:" + m);
List msgFoundList = reult.getMsgFoundList();
for(MessageExt ext : msgFoundList){
System.out.println(ext);
System.out.println(new String(ext.getBody(),"utf-8"));
}
}
consumer.shutdown();
}
}
并启动
消费成功。
3.消费者还有另一种模式为推模式,见下列
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.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
import java.util.List;
public class MyPushComsumer {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException, UnsupportedEncodingException {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer");
consumer.setNamesrvAddr("127.0.0.1:9876");
consumer.subscribe("tp_1","*");
consumer.setMessageListener(new MessageListenerConcurrently() {
public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext context) {
MessageQueue messageQueue = context.getMessageQueue();
System.out.println(messageQueue);
for (MessageExt m : list) {
try {
System.out.println(new String(m.getBody(),"utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
}
}
并启动,同样的
4.上述描述,都是以生产者同步发送消息为例,生产者还可以异步发送,具体代码如下
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.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
public class MyAsynProducer {
public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {
DefaultMQProducer producer = new DefaultMQProducer("my_producer");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
for (int i = 0; i < 10; i++) {
Message mes = new Message("tp_1", ("MQ异步的世界" + i).getBytes("utf-8"));
producer.send(mes, new SendCallback() {
public void onSuccess(SendResult sendResult) {
System.out.println("发送成功" + sendResult);
}
public void onException(Throwable throwable) {
System.out.println("发送失败" + throwable.getMessage());
}
});
}
Thread.sleep(1000);
producer.shutdown();
}
}
启动项目,并观察消费者Push模式
异步发送消息的结果如下。
三.延迟队列实战
场景:电商提交一个订单,一个小时后检查此订单状态;定时任务轮询查询数据库状态等。
RocketMQ 支持发送延迟消息,预设值的延迟时间间隔为:1s、 5s、 10s、 30s、 1m、 2m、 3m、 4m、 5m、 6m、 7m、 8m、 9m、 10m、 20m、 30m、 1h、 2h;
在上方 1.生产者 的代码基础上稍加改造即可。先启动 MyPushComsumer 消费者
package Rocket;
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.common.RemotingHelper;
import org.apache.rocketmq.remoting.exception.RemotingException;
import java.io.UnsupportedEncodingException;
public class MyProducer {
public static void main(String[] args) throws MQClientException, UnsupportedEncodingException, RemotingException, InterruptedException, MQBrokerException {
DefaultMQProducer producer = new DefaultMQProducer("my_producer");
producer.setNamesrvAddr("127.0.0.1:9876");
producer.start();
Message mes = new Message("tp_1","测试下rocketmq生产者".getBytes(RemotingHelper.DEFAULT_CHARSET));
// 延迟队列设置 (1s、 5s、 10s、 30s、 1m、 2m、 3m、 4m、 5m、 6m、 7m、 8m、 9m、 10m、 20m、 30m、 1h、 2h) 18个等级
// 3 表示的不是3s,而是第三个等级 即10s
mes.setDelayTimeLevel(3);
SendResult send = producer.send(mes);
System.out.println(send);
producer.shutdown();
}
}
使用 MyPushComsumer 的方式接受消息,会在十秒后接受到消息;
四.如何确保消息不丢失
消息的产生到消费主要经过3个流程,生产者、broker、消费者。只要在这3处保证消息不丢失即可。
1.生产者
生产者同步发送消息时,会有返回值SendResult 接收,当 sendStatus = SEND_OK 即为发送成功。
sendStatus 有4中状态
官方的解释为
SEND_OK:消息发送成功
FLUSH_DISK_TIMEOUT:消息发送成功但是服务器刷盘超时
FLUSH_SLAVE_TIMEOUT:消息发送成功但是服务器同步到slave超时
SLAVE_NOT_AVAILABLE:消息发送成功但是此时slave不可用
代码改造确保消息发送成功,简易版
异步发送消息时需要重写成功方法和异常方法来处理
消费者
消费者再拉取模式下会 返回 ConsumeConcurrentlyStatus.CONSUME_SUCCESS 以表示消息消费成功。
对于broker 储存 消息来说,消息过来先放入内存,再刷盘刷入磁盘中,只需要保证为同步刷盘方式,刷盘成功才返回即可
在broker 的配置文件broker.conf 中设置 flushDiskType
默认为 ASYNC_FLUSH 异步方式,需要改为同步。
flushDiskType = SYNC_FLUSH
我们可以看到配置文件中有个 brokerRole = ASYNC_MASTER 的配置,此配置是配置broker为主从模式时的刷盘机制。默认为异步。
如果系统为主从模式时,将其master节点配置和slave均改为同步,刷盘是否成功可以及时反馈。
brokerRole = SYNC_MASTER
五.如何确保消息幂等性
可以设置全局 id 持久话入消息表中,设置唯一键来保证幂等性;也可以放入缓存中通过查询缓存来解决;并发操作,可以引入分布式锁。