RocketMq
- 文档:https://rocketmq.apache.org/docs/quick-start/
- 文档:https://github.com/apache/rocketmq/tree/master/docs/cn
一般来说,程序对文件进行顺序读写的速度几乎接近于内存的读写速度,主要原因就是由于OS使用PageCache机制对读写访问操作进行了性能优化,将一部分的内存用作PageCache
RocketMq角色
- producer (topic message queue)
- broker,
- consumer (consumer group)
- name server 生产者或消费者能够通过名字服务查找各主题相应的Broker IP列表。多个Namesrv实例组成集群,但相互独立,没有信息交换。
tag过滤
- RocketMQ的消费者可以根据Tag进行消息过滤,也支持自定义属性过滤。消息过滤目前是在Broker端实现的,优点是减少了对于Consumer无用消息的网络传输,缺点是增加了Broker的负担、而且实现相对复杂。
消费
- 集群消费模式下, 相同Consumer Group的每个Consumer实例平均分摊消息。
- 广播消费模式下,相同Consumer Group的每个Consumer实例都接收全量的消息。
Rocket Mq部署方式
- 单主
- 单主从
- 双主
- 双主从
主从主要是数据安全,双主是高可用
producer负载均衡
轮询topic的队列
consumer负载均衡
当消息队列比consumer多的时候,则部分consumer消费多个队列
当消息队列比consumer少的时候,则部分consumer不消费队列
延时消息的使用场景
- 电商里,提交了一个订单就可以发送一个延时消息,1h后去检查这个订单的状态,如果还是未付款就取消订单释放库存。
- 解决定时任务
- 类似短连接 轮询 和长连接即时
- messageDelayLevel = “1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h”;
死信队列
消息幂等
消息堆积
RocketMq可视化后台
- git地址
https://github.com/apache/rocketmq-externals 子项目rocketmq-console
- 修改application.proerties的nameSrvAddr
docker pull apacherocketmq/rocketmq-console:2.0.0
docker run -e “JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.136.200:9875;192.168.136.200:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false” -p 8080:8080 -t apacherocketmq/rocketmq-console-ng
部署双master
- 用户组创建:groupadd -g 1100 rocketmq
- 用户创建:useradd -d /home/rk1 -g rocketmq rk1 useradd -d /home/rk2 -g rocketmq rk2
- 上传rocketmq文件并解压
- 启动namesrv:nohup sh ./bin/mqnamesrv -c namesrv9875.properties &
- 启动broker:nohup sh ./bin/mqbroker -c conf/broker.conf &
- 关闭broker sh ./bin/mqshutdown broker
- 关闭namesrv sh ./bin/mqshutdown namesrv
部署rocketmq console
- docker pull apacherocketmq/rocketmq-console:2.0.0
- docker run -e “JAVA_OPTS=-Drocketmq.namesrv.addr=192.168.136.200:9875;192.168.136.200:9876 -Dcom.rocketmq.sendMessageWithVIPChannel=false” -p 8080:8080 -t apacherocketmq/rocketmq-console-ng:2.0.0
测试 demo
public class RocketMqProducerSvcImpl {
private static final Logger logger = LoggerFactory.getLogger(RocketMqProducerSvcImpl.class);
@Value("${rocketmq.namesrvAddr}")
private String namesrvAddr;
@Value("${rocketmq.group}")
private String group;
private DefaultMQProducer producer;
private boolean online;
@PostConstruct
public void init() {
DefaultMQProducer producer = new DefaultMQProducer(group);
// 设置NameServer的地址
producer.setNamesrvAddr("192.168.136.200:9876;192.168.136.200:9875");
try {
producer.start();
online = true;
} catch (MQClientException e) {
logger.error("rocketmq producer start error.", e);
online = false;
}
}
@PreDestroy
public void preDestroy() {
producer.shutdown();
}
private void check() {
if (!online) {
synchronized (this) {
if (!online) {
init();
}
}
}
}
public void sendMsgSync(String topic, String msgStr, String uniqueKey) {
check();
try {
Message msg = new Message(topic, msgStr.getBytes(RemotingHelper.DEFAULT_CHARSET));
msg.setKeys(uniqueKey);
SendResult sendResult = producer.send(msg);
if (sendResult.getSendStatus() != SendStatus.SEND_OK) {
logger.warn("{topic:{}, msg:{}, key:{}}, sendStatus:{}", topic, msgStr, uniqueKey,
sendResult.getSendStatus());
}
} catch (Exception e) {
logger.warn("send msg error: {topic:{}, msg:{}, key:{}}", topic, msgStr, uniqueKey);
logger.error("send msg error.", e);
}
}
}