快速入门
如果使用的是阿里云主账号,则可以通过本文来体验从开通服务、创建资源、到使用 SDK 收发消息的完整流程,快速上手消息队列 RocketMQ 版。
本文以 HTTP
协议下的 Java SDK 为例进行说明。
步骤一:开通服务
- 在消息队列 RocketMQ 版产品页,单击立即开通。
- 在确认订单页面,选择我已阅读并同意《消息队列MQ服务协议》,再单击立即开通即可完成开通。
步骤二:创建资源
在使用消息队列 RocketMQ 版时,请注意以下网络访问限制:
Topic
和Group ID
需创建在同一个地域(Region)下的同一个实例中才能互通。例如,当某 Topic 创建在华东1(杭州)下的实例 A 中,那么该 Topic 只能被在华东1(杭州)下的实例 A 中创建的 Group ID 对应的生产端和消费端访问。- 如果只是测试,或者需要在本地(非阿里云 ECS 服务器)使用消息队列 RocketMQ 版的服务,请将
Topic
和Group ID
都创建在公网地域下的实例中。生产端和消费端可以部署在本地或者部署在任意地域的 ECS 上,前提是本地服务器或者相应的 ECS 需要能够访问公网。
创建实例
实例是用于消息队列 RocketMQ 版服务的虚拟机资源,会存储消息主题(Topic
)和客户端 ID(Group ID
)信息。
- 登录消息队列 RocketMQ 版控制台。在页面顶部导航栏,选择地域,如公网地域。
- 在左侧导航栏,单击实例管理。
- 在实例管理页面右上角,单击创建实例按钮。
- 在创建实例对话框,选择实例类型,并输入实例名和描述,然后单击确认。
创建 Topic
Topic
是消息队列 RocketMQ 版里对消息的一级归类,例如可以创建 Topic_Trade 这一 Topic 来识别交易类消息,消息生产者将消息发送到 Topic_Trade,而消息消费者则通过订阅该 Topic 来获取和消费消息。
- Topic 不能跨实例使用,例如在实例 A 中创建的 Topic A 不能在实例 B 中使用。
- Topic 名称必须在同一实例中是唯一的。
- 可创建不同的 Topic 来发送不同类型的消息,例如用 Topic A 发送普通消息,Topic B 发送事务消息,Topic C 发送定时/延时消息。
- 在控制台左侧导航栏,单击 Topic 管理。
- 在 Topic 管理页面上方选择刚创建的实例,单击创建 Topic 按钮。
- 在创建 Topic 对话框中的 Topic 一栏,输入 Topic 名称,选择该 Topic 对应的消息类型,输入该 Topic 的备注内容,然后单击确定。
创建的 Topic 将出现在 Topic 列表中。
创建 Group ID
创建完实例和 Topic
后,您需要为消息的消费者(或生产者)创建客户端 ID ,即 Group ID
作为标识。
- Group ID 必须在同一实例中是唯一的。
- Group ID 和 Topic 的关系是 N:N,即一个消费者可以订阅多个 Topic,同一个 Topic 也可以被多个消费者订阅;一个生产者可以向多个 Topic 发送消息,同一个 Topic 也可以接收来自多个生产者的消息。
说明 消费者必须有对应的 Group ID,生产者不做强制要求。
- 在控制台左侧导航栏,单击 Group 管理。
- 在 Group 管理页面上方选择刚创建的实例,然后选择 TCP 协议 > 创建 Group ID。本文以 TCP 协议为例。
说明 TCP 和 HTTP 协议下的 Group ID 不可以共用,因此需分别创建。 - 在创建 Group ID 对话框中,输入 Group ID 和描述,然后单击确认。
创建阿里云 AccessKey
阿里云 AccessKey 用于收发消息时进行账户鉴权。
在调用 SDK 发送和订阅消息的时候,除了需要指定创建的 Topic 和 Group ID
以外,还需输入您在 RAM 控制台创建的身份验证信息,即 AccessKey
。AccessKey 的信息包含 AccessKeyId 和 AcessKeySecret。
步骤三:获取接入点
在控制台创建好资源后,需通过控制台获取实例的接入点。在收发消息时,您需要为生产端和消费端配置该接入点,以此接入某个具体实例或地域的服务。
-
在控制台左侧导航栏,单击实例管理。
-
在实例管理页面上方选择刚创建的实例。
-
在默认显示的实例信息页签的获取接入点信息区域,您可以分别看到新创建实例的 TCP 和 HTTP 协议接入点。接入点性质因协议而异,具体说明如下:
-
HTTP 协议:在控制台看到的 HTTP 协议接入点是某个地域的接入点,跟具体实例无关。在收发消息时还需另外设置实例 ID。
-
完成以上准备工作后,您就可以运行示例代码,用消息队列 RocketMQ 版进行消息发送和订阅了。
步骤四:发送消息
可以通过以下方式发送消息:
-
控制台发送消息:用于快速验证 Topic 资源的可用性,主要用作测试。
* 项目 在控制台左侧导航栏,单击 Topic 管理。
* 在 Topic 管理页面,找到您刚刚创建的 Topic,单击右侧操作列的发送。
* 在发送消息对话框中的 Message Body 一栏,输入消息的具体内容,单击确定。
控制台会返回消息发送成功通知以及相应的 Message ID。 -
调用 SDK 发送消息:用于生产环境下使用消息队列 RocketMQ 版。
-
下文以调用 HTTP Java SDK 为例进行说明。
* 通过以下任一方式引入依赖:
+ Maven 方式引入依赖:<groupId>com.aliyun.mq</groupId> <artifactId>mq-http-sdk</artifactId> <version>1.0.1</version> <classifier>jar-with-dependencies</classifier> </dependency>
根据以下说明设置相关参数,运行示例代码:
/**
4. @author hcy
*/
public class MqUtil {
private static String accountEndpoint = "";
private static String accessId = "";
private static String accessKey = "";
private static MqUtil mqUtil;
// 所属的 Topic
private static String topic = "";
private static String groupId = "";
private static String instanceId = "";
static {
// 链接直接从链接池工厂进行获得
try {
ResourceBundle bundle = ResourceBundle.getBundle("rocketmq");
accountEndpoint = bundle.getString("accountEndpoint");
accessId = bundle.getString("accessId");
accessKey = bundle.getString("accessKey");
topic = bundle.getString("topic");
instanceId = bundle.getString("instanceId");
} catch (Exception e) {
e.printStackTrace();
// TODO: handle exception
}
}
// 发送消息
private void send(String json, String messageTag) {
MQClient mqClient = new MQClient(
// 设置HTTP接入域名(此处以公共云生产环境为例)
accountEndpoint,
// AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
accessId,
// SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
accessKey
);
// 获取Topic的生产者
MQProducer producer;
if (instanceId != null && instanceId != "") {
producer = mqClient.getProducer(instanceId, topic);
} else {
producer = mqClient.getProducer(topic);
}
try {
TopicMessage pubMsg = new TopicMessage(
// 消息内容
json.getBytes(),
// 消息标签
messageTag
);
// 同步发送消息,只要不抛异常就是成功
TopicMessage pubResultMsg = producer.publishMessage(pubMsg);
// 同步发送消息,只要不抛异常就是成功
System.out.println(new Date() + " Send mq message success. Topic is:" + topic + ", msgId is: " + pubResultMsg.getMessageId()
+ ", bodyMD5 is: " + pubResultMsg.getMessageBodyMD5());
} catch (Throwable e) {
// 消息发送失败,需要进行重试处理,可重新发送这条消息或持久化这条数据进行补偿处理
System.out.println(new Date() + " Send mq message failed. Topic is:" + topic);
e.printStackTrace();
}
}
public static MqUtil getInstance() {
if (mqUtil == null) {
mqUtil = new MqUtil();
}
return mqUtil;
}
public synchronized void sendMessage(String json, String messageTag)
throws Exception {
//处理日志
this.send(json, messageTag);
}
}
查看消息是否发送成功
消息发送后,可以在控制台查看消息发送状态,步骤如下:
- 在控制台左侧导航栏,选择消息查询 > 按 Message ID 查询。
- 在搜索框中输入发送消息后返回的 Message ID,单击搜索查询消息发送状态。
储存时间表示消息队列 RocketMQ 版服务端存储这条消息的时间。如果查询到此消息,表示消息已经成功发送到服务端。
步骤五:调用 SDK 订阅消息
消息发送成功后,需要启动消费者来订阅消息。下文以调用 HTTP Java SDK 为例说明如何订阅消息。
public static void main(String[] args) {
MQClient mqClient = new MQClient(
// 设置HTTP接入域名(此处以公共云生产环境为例)
"XXX",
// AccessKey 阿里云身份验证,在阿里云服务器管理控制台创建
"XXX",
// SecretKey 阿里云身份验证,在阿里云服务器管理控制台创建
"XXX"
);
// 所属的 Topic
final String topic = "XXX";
// 您在控制台创建的 Group ID
final String groupId = "XXX";
// Topic所属实例ID,默认实例为空
final String instanceId = "XXX";
final MQConsumer consumer;
if (instanceId != null && instanceId != "") {
consumer = mqClient.getConsumer(instanceId, topic, groupId, "tagA");
} else {
consumer = mqClient.getConsumer(topic, groupId, "tagA");
}
// 在当前线程循环消费消息,建议是多开个几个线程并发消费消息
do {
List<Message> messages = null;
try {
// 长轮询消费消息
// 长轮询表示如果topic没有消息则请求会在服务端挂住3s,3s内如果有消息可以消费则立即返回
messages = consumer.consumeMessage(
3,// 一次最多消费3条(最多可设置为16条)
3// 长轮询时间3秒(最多可设置为30秒)
);
} catch (Throwable e) {
e.printStackTrace();
try {
Thread.sleep(2000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
// 没有消息
if (messages == null || messages.isEmpty()) {
//System.out.println(Thread.currentThread().getName() + ": no new message, continue!");
continue;
}
// 处理业务逻辑
for (Message message : messages) {
byte[] messageBodyBytes = message.getMessageBodyBytes();
String messageRec = new String(messageBodyBytes);
System.out.println(messageRec);
}
// Message.nextConsumeTime前若不确认消息消费成功,则消息会重复消费
// 消息句柄有时间戳,同一条消息每次消费拿到的都不一样
{
List<String> handles = new ArrayList<String>();
for (Message message : messages) {
handles.add(message.getReceiptHandle());
}
try {
consumer.ackMessage(handles);
} catch (Throwable e) {
// 某些消息的句柄可能超时了会导致确认不成功
if (e instanceof AckMessageException) {
AckMessageException errors = (AckMessageException) e;
System.out.println("Ack message fail, requestId is:" + errors.getRequestId() + ", fail handles:");
if (errors.getErrorMessages() != null) {
for (String errorHandle : errors.getErrorMessages().keySet()) {
System.out.println("Handle:" + errorHandle + ", ErrorCode:" + errors.getErrorMessages().get(errorHandle).getErrorCode()
+ ", ErrorMsg:" + errors.getErrorMessages().get(errorHandle).getErrorMessage());
}
}
continue;
}
e.printStackTrace();
}
}
} while (true);
}
- 查看消息订阅是否成功。
完成上述步骤后,您可以在控制台查看消费者是否启动成功,即消息订阅是否成功。
在控制台左侧导航栏,单击 Group 管理。
找到要查看的 Group ID,单击该 Group ID 所在行操作列的订阅关系。
如果是否在线显示为是,且订阅关系一致,则说明订阅成功。否则说明订阅失败。
也可以在打印台查看是否输出消息。
Topic与Tag最佳实践
在消息队列 RocketMQ 版中,Topic 与 Tag 都是业务上用来归类的标识,区分在于 Topic 是一级分类,而 Tag 可以理解为是二级分类。您可通过本文了解如何搭配使用 Topic 和 Tag 来实现消息过滤。
背景信息
Topic
和 Tag
的定义如下:
Topic
消息主题,通过 Topic 对不同的业务消息进行分类。
Tag
消息标签,用来进一步区分某个 Topic 下的消息分类,消息从生产者发出即带上的属性。
Topic 和 Tag 的关系如下图所示。
适用场景
到底什么时候该用 Topic,什么时候该用 Tag?
建议从以下几个方面进行判断:
- 消息类型是否一致:如普通消息、事务消息、定时(延时)消息、顺序消息,不同的消息类型使用不同的 Topic,无法通过 Tag 进行区分。
- 业务是否相关联:没有直接关联的消息,如淘宝交易消息,京东物流消息使用不同的 Topic
进行区分;而同样是天猫交易消息,电器类订单、女装类订单、化妆品类订单的消息可以用 Tag 进行区分。 - 消息优先级是否一致:如同样是物流消息,盒马必须小时内送达,天猫超市 24 小时内送达,淘宝物流则相对会慢一些,不同优先级的消息用不同的Topic 进行区分。
- 消息量级是否相当:有些业务消息虽然量小但是实时性要求高,如果跟某些万亿量级的消息使用同一个
Topic,则有可能会因为过长的等待时间而“饿死”,此时需要将不同量级的消息进行拆分,使用不同的 Topic。
总的来说,针对消息分类,您可以选择创建多个 Topic,或者在同一个 Topic 下创建多个 Tag。但通常情况下,不同的 Topic 之间的消息没有必然的联系,而 Tag 则用来区分同一个 Topic 下相互关联的消息,例如全集和子集的关系、流程先后的关系。
订阅关系一致
订阅关系一致指的是同一个消费者 Group ID 下所有 Consumer 实例的处理逻辑必须完全一致。一旦订阅关系不一致,消息消费的逻辑就会混乱,甚至导致消息丢失。本文提供订阅关系不一致的示例代码,帮助你顺畅地订阅消息。
背景信息
消息队列 RocketMQ 版里的一个消费者 Group ID 代表一个 Consumer 实例群组。对于大多数分布式应用来说,一个消费者 Group ID 下通常会挂载多个 Consumer 实例。
由于消息队列 RocketMQ 版的订阅关系主要由 Topic + Tag 共同组成,因此,保持订阅关系一致意味着同一个消费者 Group ID 下所有的实例需在以下两方面均保持一致:
- 订阅的 Topic 必须一致
- 订阅的 Topic 中的 Tag 必须一致
正确订阅关系图片示例
多个 Group ID 订阅了多个 Topic,并且每个 Group ID 里的多个消费者实例的订阅关系保持了一致。
错误订阅关系图片示例
单个 Group ID 订阅了多个 Topic,但是该 Group ID 里的多个消费者实例的订阅关系并没有保持一致。