python调用rocketmq的api_98--RocketMQ原生API收发消息

RocketMQ原生API收发消息测试

创建Maven项目

pom文件

org.apache.rocketmq

rocketmq-client

4.7.1

org.apache.rocketmq

rocketmq-store

4.7.1

org.apache.maven.plugins

maven-compiler-plugin

3.8.0

1.8

1.8

模式一:同步消息

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

同步消息发送要保证强一致性,发到master的消息向slave复制后,才会向生产者发送反馈信息。

这种可靠性同步地发送方式使用的比较广泛,比如:重要的消息通知,短信通知。

生产者

package demo1;

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 java.util.Scanner;

//生产者

public class Producer {

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

/*

group 相同的生产者成为一个生产者组

标识发送同一类消息的Producer,通常发送逻辑一致。

发送普通消息的时候,仅标识使用,并无特别用处。

若发送事务消息,发送某条消息的producer-A宕机,

使得事务消息一直处于PREPARED状态并超时,

则broker会回查同一个group的其他producer,

确认这条消息应该commit还是rollback。

但开源版本并不完全支持事务消息(阉割了事务回查的代码)。

*/

//生产者组 防止生产者死掉 可以向其他生产者反馈

DefaultMQProducer producer = new DefaultMQProducer("producer-demo1");

//若是多太服务器,则用分号隔开写多个地址,从源码中可以得知

producer.setNamesrvAddr("192.168.64.141:9876");

//启动生产者

producer.start();

//主题相当于是消息的分类, 一类消息使用一个主题

String topic = "Topic1";

// tag 相当于是消息的二级分类, 在一个主题下, 可以通过 tag 再对消息进行分类

String tag = "TagA";

while (true){

System.out.println("输入消息 用逗号分割多条消息");

String input = new Scanner(System.in).nextLine();

String[] a = input.split(",");

for (String s : a) {

Message message = new Message(topic, tag, s.getBytes());

// 发送消息后会得到服务器反馈, 包含: smsgId, sendStatus, queue, queueOffset, offsetMsgId

SendResult result = producer.send(message);

System.out.println("反馈结果:"+result);

}

}

}

}

消费者

消费者的要点:

1. push 和 pull

消费者有两种模式:push 和 pull。

push 模式由服务器主动向消费者发送消息;pull 模式由消费者主动向服务器请求消息。

在消费者处理能力有限时,为了减轻消费者的压力,可以采用pull模式。多数情况下都采用 pull 模式。

2. NameServer

消费者需要向 NameServer 询问 Topic 的路由信息。

3. Topic

从指定的Topic接收消息。Topic相当于是一级分类。

4. Tag

Topic 相当于是一级分类,Tag 相当于是2级分类。

多个 Tag 可以这样写: TagA || TagB || TagC

不指定 Tag,或者说接收所有的 Tag,可以写星号: *

package demo1;

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.message.MessageExt;

import java.util.List;

//消费者

public class Consumer {

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

/*

标识一类Consumer的集合名称,

这类Consumer通常消费一类消息,且消费逻辑一致。

同一个Consumer Group下的各个实例将共同消费

topic的消息,起到负载均衡的作用。

消费进度以Consumer Group为粒度管理,不同

Consumer Group之间消费进度彼此不受影响,

即消息A被Consumer Group1消费过,也会再

给Consumer Group2消费。

注: RocketMQ要求同一个Consumer Group的

消费者必须要拥有相同的注册信息,即必须要听一样

的topic(并且tag也一样)。

*/

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-demo1");

//

consumer.setNamesrvAddr("192.168.64.141:9876");

//处理消息

// * 表示接收多个标签

consumer.subscribe("Topic1","TagA || TagB");

consumer.registerMessageListener(new MessageListenerConcurrently() {

@Override

public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

for (MessageExt msg : list) {

String string = new String(msg.getBody());

System.out.println("收到 "+string);

}

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; // 消费成功

// return ConsumeConcurrentlyStatus.RECONSUME_LATER; // 稍后再次重试

}

});

consumer.start();

System.out.println("开始消费数据");

}

}

模式二:异步消息

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

master 收到消息后立即向生产者进行反馈。之后再以异步方式向 slave 复制消息。

异步消息通常用在对响应时间敏感的业务场景,即发送端不能容忍长时间地等待Broker的响应。

生产者

package demo2;

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.exception.RemotingException;

import java.util.Scanner;

/*

异步发送消息

一条消息送出后, 不必暂停等待服务器针对这条消息的反馈, 而是可以立即发送后续消息.

使用监听器, 以异步的方式接收服务器的反馈

*/

public class Producer {

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

DefaultMQProducer p = new DefaultMQProducer("producer-demo2");

p.setNamesrvAddr("192.168.64.141:9876");

p.start();

p.setRetryTimesWhenSendAsyncFailed(0);

String topic = "Topic2";

String tag = "TagA";

String key = "Key-demo2";

while (true) {

System.out.print("输入消息,用逗号分隔多条消息: ");

String[] a = new Scanner(System.in).nextLine().split(",");

for (String s : a) {

Message msg = new Message(topic, tag, key, s.getBytes());

p.send(msg, new SendCallback() {

@Override

public void onSuccess(SendResult sendResult) {

System.out.println("\n\n消息发送成功 : "+sendResult);

}

@Override

public void onException(Throwable throwable) {

System.out.println("\n\n消息发送失败");

}

});

System.out.println("--------------------消息已送出-----------------------");

}

}

}

}

消费者

package demo2;

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.message.MessageExt;

import java.util.List;

/*

与 demo1.Consumer 完全相同

*/

public class Consumer {

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

DefaultMQPushConsumer c = new DefaultMQPushConsumer("consumer-demo2");

p.setNamesrvAddr("192.168.64.141:9876");

c.subscribe("Topic2", "TagA");

c.registerMessageListener(new MessageListenerConcurrently() {

@Override

public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

for (MessageExt msg : list) {

System.out.println(new String(msg.getBody()) + " - " + msg);

}

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

}

});

c.start();

System.out.println("开始消费数据");

}

}

模式三:单向消息

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

这种方式主要用在不特别关心发送结果的场景,例如日志发送。

不在乎是否发送成功,发送即可,不太重要的数据可以通过该模式

生产者

package demo3;

import org.apache.rocketmq.client.exception.MQClientException;

import org.apache.rocketmq.client.producer.DefaultMQProducer;

import org.apache.rocketmq.common.message.Message;

import org.apache.rocketmq.remoting.exception.RemotingException;

import java.util.Scanner;

//单向消息 生产者 消息发出后, 服务器不会返回结果

public class Producer {

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

DefaultMQProducer producer = new DefaultMQProducer("producer-demo3");

producer.setNamesrvAddr("192.168.64.141:9876");

producer.start();

//发送消息

while (true){

System.out.println("输入消息");

String s = new Scanner(System.in).nextLine();

Message message = new Message("Topic2", "TagA", s.getBytes());

producer.sendOneway(message);

}

}

}

消费者

package demo3;

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.message.MessageExt;

import java.util.List;

//消费者

public class Consumer {

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

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-demo3");

consumer.setNamesrvAddr("192.168.64.141:9876");

consumer.subscribe("Topic2","*");

consumer.registerMessageListener(new MessageListenerConcurrently() {

@Override

public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

for (MessageExt msg : list) {

System.out.println(new String(msg.getBody()) + " - " + msg);

}

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

}

});

consumer.start();

System.out.println("开始消费数据");

}

}

模式四:顺序消息

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

上图演示了 Rocketmq 顺序消息的基本原理:

同一组有序的消息序列,会被发送到同一个队列,按照 FIFO 的方式进行处理

一个队列只允许一个消费者线程接收消息,这样就保证消息按顺序被接收

下面以订单为例:

一个订单的顺序流程是:创建、付款、推送、完成。订单号相同的消息会被先后发送到同一个队列中。消费时,从同一个队列接收同一个订单的消息。

生产者

package demo4;

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.MessageQueueSelector;

import org.apache.rocketmq.client.producer.SendResult;

import org.apache.rocketmq.common.message.Message;

import org.apache.rocketmq.common.message.MessageQueue;

import org.apache.rocketmq.remoting.exception.RemotingException;

import java.util.List;

import java.util.Scanner;

//顺序消息 生产者

public class Producer {

static String[] msgs = {

"15103111039,创建",

"15103111065,创建",

"15103111039,付款",

"15103117235,创建",

"15103111065,付款",

"15103117235,付款",

"15103111065,完成",

"15103111039,推送",

"15103117235,完成",

"15103111039,完成"

};

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

DefaultMQProducer producer = new DefaultMQProducer("producer-demo4");

producer.setNamesrvAddr("192.168.64.141:9876");

producer.start();

for (String s : msgs) {

System.out.println("按回车之后发送消息"+s);

new Scanner(System.in).nextLine();

Message message = new Message("Topic3", "TagA", s.getBytes());

String sid = s.split(",")[0];

long id = Long.parseLong(sid);

/*

MessageQueueSelector用来选择发送的队列,

这里用订单的id对队列数量取余来计算队列索引

send(msg, queueSelector, obj)

第三个参数会传递到queueSelector, 作为它的第三个参数

*/

SendResult r = producer.send(message, new MessageQueueSelector() {

/*

三个参数的含义:

queueList: 当前Topic中所有队列的列表

message: 消息

o: send()方法传入的orderId

*/

@Override

public MessageQueue select(List list, Message message, Object o) {

long id = (long) o;

//订单id对队列数量取余, 相同订单id得到相同的队列索引

int index = (int) (id % list.size());

System.out.println("消息已发送到: " + list.get((int) index));

return list.get(index);

}

}, id);

System.out.println("-------------------------"+r);

}

}

}

消费者

package demo4;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;

import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;

import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;

import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;

import org.apache.rocketmq.client.exception.MQClientException;

import org.apache.rocketmq.common.message.MessageExt;

import java.util.List;

//消费者

public class Consumer {

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

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-demo4");

consumer.setNamesrvAddr("192.168.64.141:9876");

consumer.subscribe("Topic3","*");

consumer.registerMessageListener(new MessageListenerOrderly() {

@Override

public ConsumeOrderlyStatus consumeMessage(List list, ConsumeOrderlyContext consumeOrderlyContext) {

for (MessageExt ext : list) {

String s = new String(ext.getBody());

System.out.println("收到"+s);

}

return ConsumeOrderlyStatus.SUCCESS;

}

});

consumer.start();

System.out.println("开始处理消息");

}

}

模式五:延时消息

消息发送到 Rocketmq 服务器后, 延迟一定时间再向消费者进行投递。

延时消息的使用场景:

比如电商里,提交了一个订单就可以发送一个延时消息,1h后去检查这个订单的状态,如果还是未付款就取消订单释放库存。

生产者发送消息时,对消息进行延时设置:

msg.setDelayTimeLevel(3);

其中 3 代表级别而不是一个具体的时间值,级别和延时时长对应关系是在 MessageStoreConfig 类种进行定义的:

this.messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";

对应关系表:

级别

延时时长

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

生产者

package demo5;

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.common.message.Message;

import org.apache.rocketmq.remoting.exception.RemotingException;

import java.util.Scanner;

// 延时消息 生产者

public class Producer {

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

DefaultMQProducer producer = new DefaultMQProducer("producer-demo5");

producer.setNamesrvAddr("192.168.64.141:9876");

producer.start();

while (true){

System.out.println("输入消息");

String s = new Scanner(System.in).nextLine();

Message message = new Message("Topic4", "*",s.getBytes());

//设置等级

message.setDelayTimeLevel(3);

producer.send(message);

}

}

}

消费者

package demo5;

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.message.MessageExt;

import java.util.List;

//消费者

public class Consumer {

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

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-demo5");

consumer.setNamesrvAddr("192.168.64.141:9876");

consumer.subscribe("Topic4","*");

consumer.registerMessageListener(new MessageListenerConcurrently() {

@Override

public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

for (MessageExt msg : list) {

System.out.println(new String(msg.getBody()) + " - " + msg);

}

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

}

});

consumer.start();

System.out.println("开始消费数据");

}

}

模式六:批量消息

批量发送消息能显著提高传递小消息的性能。限制是这些批量消息应该有相同的topic,相同的waitStoreMsgOK,而且不能是延时消息。此外,这一批消息的总大小不应超过4MB。

生产者

package demo6;

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.common.message.Message;

import org.apache.rocketmq.remoting.exception.RemotingException;

import java.util.ArrayList;

import java.util.Scanner;

/*

批量发送消息能显著提高传递小消息的性能。限制是:

- 这些批量消息应该有相同的topic,

- 相同的waitStoreMsgOK,

- 而且不能是延时消息。

- 这一批消息的总大小不应超过4MB。

如果超出4M需要进行数据分割, 请参考官方代码样例https://github.com/apache/rocketmq/blob/master/docs/cn/RocketMQ_Example.md

*/

public class Producer {

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

DefaultMQProducer p = new DefaultMQProducer("producer-demo6");

c.setNamesrvAddr("192.168.64.141:9876");

p.start();

String topic = "Topic6";

while (true) {

System.out.print("输入消息,用逗号分隔多条消息: ");

String[] a = new Scanner(System.in).nextLine().split(",");

ArrayList messages = new ArrayList<>();

for (String s : a) {

messages.add(new Message(topic, s.getBytes()));

}

p.send(messages);

System.out.println("批量消息已发送");

}

}

}

消费者

package demo6;

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.message.MessageExt;

import java.util.List;

public class Consumer {

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

DefaultMQPushConsumer c = new DefaultMQPushConsumer("consumer-demo6");

c.setNamesrvAddr("192.168.64.141:9876");

c.subscribe("Topic6", "*");

c.registerMessageListener(new MessageListenerConcurrently() {

@Override

public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

for (MessageExt msg :

list) {

System.out.println("收到: "+new String(msg.getBody()));

}

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

}

});

c.start();

System.out.println("开始消费数据");

}

}

模式七:消息过滤

Tag 过滤

Tag 可以满足大多数消息过滤的需求。使用 Tag 过滤非常简单,例如:

consumer.subscribe("Topic1", "TagA || TagB || TagC");

对自定义属性过滤

生产者可以在消息中添加自定义的属性:

msg.putUserProperty("prop1", "1");

msg.putUserProperty("prop2", "2");

消费者接收数据时,可以根据属性来过滤消息:

consumer.subscribe("Topic7", MessageSelector.bySql("prop1=1 or prop2=2"));

可以看到,自定义属性的过滤语法是 Sql 语法,RocketMQ只定义了一些基本语法来支持这个特性,支持的 Sql 过滤语法如下:

数值比较,比如:>,>=,<,<=,BETWEEN,=;

字符比较,比如:=,<>,IN;

IS NULL 或者 IS NOT NULL;

逻辑符号 AND,OR,NOT;

生产者

package demo6;

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.common.message.Message;

import org.apache.rocketmq.remoting.exception.RemotingException;

import java.util.Random;

import java.util.Scanner;

//消息过滤

public class Producer {

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

DefaultMQProducer producer = new DefaultMQProducer("producer-demo6");

producer.setNamesrvAddr("192.168.64.141:9876");

producer.start();

while (true){

System.out.println("输入消息");

String s = new Scanner(System.in).nextLine();

Message msg = new Message("Topic5", s.getBytes());

msg.putUserProperty("rnd",""+new Random().nextInt(4));

producer.send(msg);

}

}

}

消费者

package demo6;

import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;

import org.apache.rocketmq.client.consumer.MessageSelector;

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.message.MessageExt;

import java.util.List;

//消费者

public class Consumer {

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

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-demo6");

consumer.setNamesrvAddr("192.168.64.141:9876");

consumer.subscribe("Topic5", MessageSelector.bySql("rnd=1 or rnd > 2"));

consumer.registerMessageListener(new MessageListenerConcurrently() {

@Override

public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

for (MessageExt ext : list) {

String s = new String(ext.getBody());

String rnd = ext.getUserProperty("rnd");

System.out.println("收到"+s);

System.out.println("属性"+rnd);

}

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

}

});

consumer.start();

System.out.println("开始消费数据");

}

}

模式八:事务消息

RocketMQ 提供了可靠性消息,也叫事务消息。

事务消息的原理

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

下面来看 RocketMQ 的事务消息是如何来发送“可靠消息”的,只需要以下三步:

发送半消息(半消息不会发送给消费者)

执行本地事务

提交消息

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

完成事务消息发送后,消费者就可以以正常的方式来消费数据。

RocketMQ 的自动重发机制在绝大多数情况下,都可以保证消息被正确消费。

假如消息最终消费失败了,还可以由人工处理进行托底。

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

上面分析的是正常情况下的执行流程。下面再来看两种错误情况:

事务执行失败时回滚消息

服务器无法得知消息状态时,需要主动回查消息状态

回滚:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

消息回查:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl8zODMwNTQ0MA==,size_16,color_FFFFFF,t_70#pic_center

生产者

package demo7;

import org.apache.rocketmq.client.producer.LocalTransactionState;

import org.apache.rocketmq.client.producer.TransactionListener;

import org.apache.rocketmq.client.producer.TransactionMQProducer;

import org.apache.rocketmq.client.producer.TransactionSendResult;

import org.apache.rocketmq.common.message.Message;

import org.apache.rocketmq.common.message.MessageExt;

import java.util.Scanner;

import java.util.concurrent.ConcurrentHashMap;

//事务生产者

public class Producer {

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

TransactionMQProducer producer = new TransactionMQProducer("producer-demo7");

producer.setNamesrvAddr("192.168.64.141:9876");

producer.start();

producer.setTransactionListener(new TransactionListener() {

//为了处理回查的信息

ConcurrentHashMap localTx = new ConcurrentHashMap<>();

/*

在这里执行本地事务

*/

@Override

public LocalTransactionState executeLocalTransaction(Message message, Object o) {

System.out.println("执行本地事务");

if (Math.random()<0.333) {

System.out.println("本地事务执行成功, 按回车提交事务消息");

new Scanner(System.in).nextLine();

localTx.put(message.getTransactionId(), LocalTransactionState.COMMIT_MESSAGE);

return LocalTransactionState.COMMIT_MESSAGE;

} else if (Math.random()<0.666) {

System.out.println("本地事务执行失败, 按回车回滚事务消息");

new Scanner(System.in).nextLine();

localTx.put(message.getTransactionId(), LocalTransactionState.ROLLBACK_MESSAGE);

return LocalTransactionState.ROLLBACK_MESSAGE;

} else {

System.out.println("本地事务执行情况未知, 按回车继续");

new Scanner(System.in).nextLine();

localTx.put(message.getTransactionId(), LocalTransactionState.UNKNOW);

return LocalTransactionState.UNKNOW;

}

}

/*

回查方法

检测频率默认1分钟,可通过在broker.conf文件中设置transactionCheckInterval的值来改变默认值,单位为毫秒。

*/

@Override

public LocalTransactionState checkLocalTransaction(MessageExt messageExt) {

System.out.println("服务器正在回查消息状态");

LocalTransactionState s = localTx.get(messageExt.getTransactionId());

if (s == null || s == LocalTransactionState.UNKNOW) {

s = LocalTransactionState.ROLLBACK_MESSAGE;

}

return s;

}

});

while (true){

System.out.println("输入消息");

String s = new Scanner(System.in).nextLine();

Message message = new Message("Topic6", s.getBytes());

// 阻塞等待事务消息处理结束

TransactionSendResult t = producer.sendMessageInTransaction(message, null);

System.out.println("事务消息处理完成");

}

}

}

消费者

package demo7;

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.message.MessageExt;

import java.util.List;

//消费者

public class Consumer {

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

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer-demo7");

consumer.setNamesrvAddr("192.168.64.141:9876");

consumer.subscribe("Topic6","*");

consumer.registerMessageListener(new MessageListenerConcurrently() {

@Override

public ConsumeConcurrentlyStatus consumeMessage(List list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {

for (MessageExt ext : list) {

String msg = new String(ext.getBody());

System.out.println("收到"+msg);

}

if (Math.random()<0.5){

System.out.println("消息处理成功");

return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;

}else {

System.out.println("消息处理失败,稍后重试");

return ConsumeConcurrentlyStatus.RECONSUME_LATER;

}

}

});

consumer.start();

System.out.println("开始处理消息");

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值