大家好,我是Leo!今天来和大家分享RocketMQ的一些用法。

领域模型介绍

Producer: 用于生产消息的运行实体。

Topic: 主题,用于消息传输和存储的分组容器。

MessageQueue: 消息传输和存储的实际单元容器。

Message: 消息传输的最小单元。

ConsumerGroup: 消费者组。

Consumer: 消费者。

Subscription: 订阅关系,发布订阅模式中消息过滤、重试、消费进度的规则配置。

MQ的优势

MQ的明显优势有3个。

应用解耦: 以多服务为例,用户下单,需要通知订单服务和库存服务,我们可以通过MQ消息来解除下单和库存系统的耦合。

异步提速: 以秒杀为例,我们可以先返回秒杀结果,后续再通过MQ异步消息去插入记录和扣减库存等,减少调用的链路长度。

削峰填谷: 将某一时间内的请求量分摊到更多时间处理,比如系统A一秒只能处理10000个请求,但是我有100000个请求需要处理,我可以将请求发到MQ中,再分成10秒去消费这些请求。

当然MQ也有劣势系统可用性降低系统复杂度提高一致性问题

RocketMQ的主要角色

主要包括Producer、Broker、Consumer、NameServer Cluster。

一对多

可以通过设置不同的消费者组

不同组通过不同的消费者组既可以实现同时收到一样数量的消息,那同一个消费者组需要怎样才能收到同样数量的消息呢?

// 消费者消费模式
consumer.setMessageModel(MessageModel.BROADCASTING);

默认是集群模式CLUSTERING,设置成广播模式

既可以实现一对多的发送。

同步消息(普通消息)

同步消息需要阻塞等待消息发送结果的返回

public class ProducerDemo {

    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {

        DefaultMQProducer producer = new DefaultMQProducer("group1");

        producer.setNamesrvAddr("localhost:9876");
producer.start();
Message message = new Message();
message.setTopic("MQLearn");
message.setTags("1.0.0");
message.setBody("Hello MQ!".getBytes(StandardCharsets.UTF_8));
SendResult result = producer.send(message);
if (result.getSendStatus().equals(SendStatus.SEND_OK)) {
System.out.println(result);
System.out.println("发送成功:" + message);
} producer.shutdown();
}
}

异步消息

异步消息需要实现发送成功和失败的回调函数。

public class Producer {

    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {

        DefaultMQProducer producer = new DefaultMQProducer("group1");

        producer.setNamesrvAddr("192.168.246.140:9876");
producer.start();
// 异步消息
for (int i = 0; i < 10; i++) {
Message message = new Message();
message.setTopic("topic7");
message.setTags("1.0.0"); message.setBody(("Hello World !" + i).getBytes(StandardCharsets.UTF_8));
producer.send(message, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
// 发送成功的回调方法
System.out.println(sendResult);
} @Override
public void onException(Throwable e) {
// 发送失败的回调方法
System.out.println(e);
}
});
}
TimeUnit.SECONDS.sleep(10);
System.out.println("异步发送完成!");
}
}

单向消息

单向消息就类似UDP,只顾单向发送,不管是否发送成功,常用于日志收集等场景。

public class SingleDirectionProducer {

    public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("group1"); producer.setNamesrvAddr("localhost:9876");
producer.start();
// 单向消息
for (int i = 0; i < 10; i++) {
Message message = new Message();
message.setTopic("topic8");
message.setTags("1.0.0");
message.setBody(("Hello World !" + i).getBytes(StandardCharsets.UTF_8));
producer.sendOneway(message);
}
System.out.println("带向发送完成!");
}
}

延时(定时)消息

RocketMQ提供的定时消息并不能指定在什么时间点去投递消息。而是根据设定的等待时间,起到延时到达的缓冲作用在RocketMQ中,延时消息的delayTimeLevel支持以下级别:

1 1s 2 5s 3 10s 4 30s 5 1m 6 2m 7 3m 8 4m 9 5m 10 6m 11 7m 12 8m 13 9m 14 10m 15 20m 16 30m 17 1h 18 2h

// 设置消息延时级别
message.setDelayTimeLevel(3);

批量消息

批量消息支持一次发送多条消息。

注意:

  • 批量消息需要有相同的topic

  • 不能是延时消息

  • 消息内容不能超过4M,可以通过producer.setMaxMessageSize()和broker进行设置设置(可以通过拆分多次发送)

public class BatchProducer {

    public static void main(String[] args) throws Exception {
DefaultMQProducer producer = new DefaultMQProducer("group1"); producer.setNamesrvAddr("localhost:9876");
producer.start();
List<Message> list = new ArrayList<>();
// 批量消息
for (int i = 0; i < 10; i++) {
Message message = new Message();
message.setTopic("topic10");
message.setTags("1.0.0");
message.setBody(("Hello World !" + i).getBytes(StandardCharsets.UTF_8));
list.add(message);
}
SendResult result = producer.send(list);
System.out.println(result);
TimeUnit.SECONDS.sleep(2);
System.out.println("发送完成!");
}
}

顺序消息

顺序消息支持按照消息的发送消息先后获取消息。

比如:我的一笔订单有多个流程需要处理,比如创建->付款->推送->完成。

通过同一笔订单放到一个队列中,这样就可以解决消费的无序问题。

通过实现MessageQueueSelector来选择一个队列。

public class Producer {

    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {

        DefaultMQProducer producer = new DefaultMQProducer("group1");

        producer.setNamesrvAddr("localhost:9876");
producer.start(); Message message = new Message();
// 模拟业务ID
int step = 10;
message.setTopic("topic12");
message.setTags("1.0.0");
message.setBody(("Hello World !").getBytes(StandardCharsets.UTF_8));
producer.send(message, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
// 队列数
int size = mqs.size();
// 取模
int orderId = step;
return mqs.get(orderId % size);
}
}, null); System.out.println("发送完成!");
}
}
public class Consumer {

    public static void main(String[] args) throws Exception{

        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer();
consumer.setConsumerGroup("group1");
consumer.setNamesrvAddr("localhost:9876");
consumer.subscribe("topic12", "*");
// 消费者,起一个顺序监听,一个线程,只监听一个队列
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
for (MessageExt msg : msgs) {
System.out.println(msg);
byte[] body = msg.getBody();
System.out.println(new String(body));
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.println("消费者启动了!"); }
}

事务消息

RocketMQ中的事务消息支持在分布式场景下消息生产和本地事务的最终一致性。

大致流程为,

  1. 生产者先将消息发送至RocketMQ。

  2. RocketMQBroker将消息持久化成功后,向生产者返回ACK消息确认已经返回成功,消息状态为暂时不能投递状态。

  3. 执行本地事务逻辑。

  4. 生产者根据事务执行结果向Broker提交commit或者rollback结果。

  5. 如果在断网或者重启情况下,未收到4的结果,或者返回Unknown未知状态,在固定时间对消息进行回查。

  6. 生产者收到消息回查后,需要本地事务执行的最终结果。

  7. 生产者对本地事务状态进行二次提交或确认。

public class Producer {

    public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {
TransactionMQProducer producer = new TransactionMQProducer("group1");
producer.setNamesrvAddr("localhost:9876");
// 设置事务监听
producer.setTransactionListener(new TransactionListener() {
// 正常事务监听
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
// 把消息保存到mysql数据库
boolean ok = false; if (ok) {
System.out.println("正常执行事务过程");
return LocalTransactionState.COMMIT_MESSAGE;
} else {
System.out.println("事务补偿过程");
return LocalTransactionState.UNKNOW;
//return LocalTransactionState.ROLLBACK_MESSAGE;
} }
// 事务补偿事务
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
System.out.println("事务补偿过程");
// sql select
if (true) { } else { }
return LocalTransactionState.COMMIT_MESSAGE;
}
});
producer.start();
String msg = "Hello Transaction Message!";
Message message = new Message("topic13", "tag", msg.getBytes(StandardCharsets.UTF_8));
TransactionSendResult transactionSendResult = producer.sendMessageInTransaction(message, null);
TimeUnit.SECONDS.sleep(2);
System.out.println(transactionSendResult);
System.out.println("发送完成!");
}
}

消息的过滤

在RocketMQ中的消息过滤功能能通过生产者和消费者对消息的属性和Tag进行定义,在消费端可以根据过滤条件进行筛选匹配,将符合条件的消息投递给消费者进行消费。

支持两种方式:Tag标签过滤和SQL属性过滤。

Message message = new Message();
message.setTopic("topic11");
message.setTags("tag");
message.setBody(("Hello World !" + "tag").getBytes(StandardCharsets.UTF_8));
message.putUserProperty("name", "zhangsan");
message.putUserProperty("age", "16");
SendResult result = producer.send(message);

subscribe方法subExpression参数也支持Tag过滤

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer();
consumer.setConsumerGroup("group1");
consumer.setNamesrvAddr("112.74.125.184:9876");
consumer.subscribe("topic11", MessageSelector.bySql("age > 16"));

SpringBoot整合RocketMQ的使用

在SpringBoot项目中主要通过RocketMQTemplate进行消息的发送。

// 普通消息
rocketMQTemplate.convertAndSend("topic10", user);
rocketMQTemplate.send("topic10", MessageBuilder.withPayload(user).
SendResult result = rocketMQTemplate.syncSend("topic10", user);
// 异步消息
rocketMQTemplate.asyncSend("topic10", user, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.println("成功!");
}
@Override
public void onException(Throwable e) {
System.out.println(e);
}
}, 1000L);
// 单向消息
rocketMQTemplate.sendOneWay("topic10", user);
// 延时消息
rocketMQTemplate.syncSend("topic10", MessageBuilder.withPayload(user).build(), 2000L, 3);
// 批量消息
rocketMQTemplate.syncSend("topic10", list, 1000);

消费者:在注解中可以实现根据Tag和SQL进行属性的过滤。

@Service
//@RocketMQMessageListener(
// consumerGroup = "group1",
// topic = "topic10",
// selectorExpression = "tag1 || tag2"
//)
@RocketMQMessageListener(
consumerGroup = "group1",
topic = "topic10",
selectorType = SelectorType.SQL92,
selectorExpression = "age > 16",
messageModel = MessageModel.BROADCASTING
)
public class UserConsumer implements RocketMQListener<User> { @Override
public void onMessage(User message) { }
}

总结

今天主要分享了一下RocketMQ的一些基础使用,包括各种类型的消息的使用,偏向于代码实现部分,对于原理篇没有过多涉及。

RocketMQ的简单使用的更多相关文章

  1. 阿里云RocketMQ的生产者简单实现

    // MQ的应用场景有比如 订单变更消息可以通过产生这个事件的地方(比如前端调用后端的接口post一个订单,那么就是在这个mapping方法里做一个生产者[不过最好通过aop来实现,不然n多个接口都要 ...

  2. 阿里RocketMq试用记录+简单的Spring集成

    CSDN学院招募微信小程序讲师啦 程序猿全指南,让[移动开发]更简单! [观点]移动原生App开发 PK HTML 5开发 云端应用征文大赛,秀绝招,赢无人机! 阿里RocketMq试用记录+简单的S ...

  3. 阅读rocketmq技术内幕、实战与原理杂记 - 设计

    最近正在研究rocketmq,简单记录下设计的不同 互联网系统中Rpc.服务治理.消息中间件基本都是标配,消息中间件能解耦,削峰,高可用并能间接提供达到最终一致性 消息中间件中,消息消费分为最多一次, ...

  4. RocketMq 学习记录

    最近因为工作需求,领导让我安装一下RocketMQ 这里简单记录一下 这里我的操作系统是centos 6.5 64位 我们看一下官网的RocketMQ安装要求 Prerequisite The fol ...

  5. RocketMQ 消息队列单机部署及使用

    转载请注明来源:http://blog.csdn.net/loongshawn/article/details/51086876 相关文章: <RocketMQ 消息队列单机部署及使用> ...

  6. RocketMq在SparkStreaming中的总结

    其实Rocketmq的给第三方的插件已经全了,如果大家有兴趣的话请移步https://github.com/apache/rocketmq-externals.本文主要是结合笔者已有的rmq在spar ...

  7. RocketMq在SparkStreaming中的应用总结

    其实Rocketmq的给第三方的插件已经全了,如果大家有兴趣的话请移步https://github.com/apache/rocketmq-externals.本文主要是结合笔者已有的rmq在spar ...

  8. RocketMQ基础概念剖析,并分析一下Producer的底层源码

    由于篇幅原因,本次的源码分析只限于Producer侧的发送消息的核心逻辑,我会通过流程图.代码注释.文字讲解的方式来对源码进行解释,后续应该会专门开几篇文章来做源码分析. 这篇博客聊聊关于Rocket ...

  9. Kafka/Metaq设计思想学习笔记 转

    转载自: http://my.oschina.net/geecoodeer/blog/194829 本文没有特意区分它们之间的区别,仅仅是列出其中笔者认为好的设计思想,供后续设计参考. 目前笔者并没有 ...

  10. RocketMQ4.2 最佳实践之集群搭建

    学习了RocketMQ的基本概念后,我们来看看RocketMQ最简单的使用场景.RocketMQ的服务器最简单的结构,必须包含一个NameServer和一个Broker.Producer把某个主题的消 ...

随机推荐

  1. 使用Kong网关API接口配置

    一.Upstream1.创建Upstream: curl -i -X POST IPAddress:8001/upstreams -d 'name=upstream-test' -d 'slots=1 ...

  2. 微信小程序-实现微信登录

    业务流程: 1:首先需要一个按钮触发事件 2:调用微信小程序的登录接口wx.login,拿到code 3:调用微信小程序的获取用户信息的接口wx.getUserProfile,拿到用户的个人信息 4: ...

  3. C++ accumulate()函数的用法

    accumulate定义在 numeric 中,作用有两个,一个是累加求和,另一个是自定义类型数据的处理. 头文件 #include <numeric> 原型 默认求累加和 templat ...

  4. [imx6ull][nand] uboot烧录固件

    背景 在调试阶段使用nxp的mfg-tools烧录比较麻烦,故考虑使用uboot指令实现固件烧录 烧录方法 //烧写内核 nand erase 0x4000000 0x800000 tftp zIma ...

  5. 机器学习(二):感知机+svm习题 感知机手工推导参数更新 svm手推求解二维坐标超平面直线方程

    作业1: 输入: 训练数据集 \(T = {(x1; y1); (x2; y2),..., (xN; yN)}\) 其中,\(x \in R^n\), \(y \in Y = \{+1, -1\}\) ...

  6. IDEA编写JSP无代码提示

    网上的版本 网上的版本 网上的我试了 但是未能解决 我自己解决的方法是 File => Poject Struct=>Modules=>"+"=>找到自己系 ...

  7. Tarjan强连通分量(scc)

    概念解释 节点强连通:\(v_i\)与\(v_j\)(\(v_i ≠ v_j\))强连通是指从\(vi\)到\(vj\)和从\(vj\)到\(vi\)都存在路径,即两节点互相可达 强连通图:在有向图\ ...

  8. 移动端网页--better-scroll介绍

    移动端网页--better-scroll介绍 Options 起始位置及滚动方向 startX:0 开始时的X轴位置 startY:0 开始时的Y轴位置 scrollY: true 滚动方向为 Y 轴 ...

  9. 各类电商平台批量获取商品信息 API 详细操作说明

    前言获取商品信息可以更加快捷的查看商品的详请参数,同理批量获取商品信息的话就可以查看多个商品的信息参数,便于我们查看整个店铺的数据情况方便运营管理.具体操作如下:先获取一个key和secret,登入测 ...

  10. Redis使用之缓存清除

    1. Redis到期缓存清除策略(三种) 定时删除:在设置key的过期时间的同时,为该key创建一个定时器,让定时器在key的过期时间来临时,对key进行删除. 优点:定时删除策略对内存是友好的,通过 ...