一、安装rocketmq(一主一从异步刷盘)
1、下载rocketmq
地址: https://www.apache.org/dyn/closer.cgi?path=rocketmq/4.4.0/rocketmq-all-4.4.0-source-release.zip 可以下载二进制文件,这样无需自己重新编译打包。
2、安装rocketmq
2.1、 安装java
2.1.1、下载jdk1.8
2.1.2、配置环境变量
export JAVA_HOME=/home/jdk1.8.0_191
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
2.2、安装rocketmq (开始集群搭建)
执行命令unzip rocketmq-all-4.4.0-bin-release.zip解压文件。
配置环境变量:
#设置rocketmq环境变量
export ROCKETMQ_HOME=/home/rocketmq4.4
export PATH=${ROCKETMQ_HOME}/bin:$PATH
新建存储目录
mkdir -p /home/rocketmq4.4/data/store/{commitlog,consumequeue,index}
修改配置文件 (broker-a.properties)
[root@localhost 2m-2s-sync]# pwd
/home/rocketmq4.4/conf/2m-2s-sync
[root@localhost 2m-2s-sync]#
[root@localhost 2m-2s-sync]# vim broker-a.properties
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#brokerClusterName=DefaultCluster
#brokerName=broker-a
#brokerId=0
#deleteWhen=04
#fileReservedTime=48
#brokerRole=SYNC_MASTER
#flushDiskType=ASYNC_FLUSH
#所属集群名称,如果多个master,那么每个master配置的名称应该一致,要不然识别不了
brokerClusterName=rocketmq-cluster
#broker名称
brokerName=broker-a
#0 表示master,>0 表示slave
brokerId=0
#nameServer地址,分号隔开
namesrvAddr=127.0.0.1:9876
#在发送消息时,自动创建服务器不存在的topic,默认创建的队列数
defaultTopicQueueNums=4
#是否允许broker自动创建topic,建议线下开启,线上关闭
autoCreateTopicEnable=true
#是否允许broker自动创建订阅组,建议线下开始,线上关闭
autoCreateSubscriptionGroup=true
#broker对外服务的监听端口,
#同一台机器部署多个broker,端口号要不同,且端口号之间要相距大些
listenPort=10911
#删除文件的时间节点,默认凌晨4点
deleteWhen=04
#文件保留时间,默认48小时
fileReservedTime=120
#commitLog每个文件的大小,默认大小1g
mapedFileSizeCommitLog=1073741824
#consumeQueue每个文件默认存30w条,根据自身业务进行调整
mapedFileSizeConsumeQueue=300000
destroyMapedFileInterval=120000
redeleteHangedFileInterval=120000
#检查物理文件磁盘空间
diskMaxUsedSpaceRatio=88
#store存储路径,master与slave目录要不同
storePathRootDir=/home/rocketmq4.4/data/store
#commitLog存储路径
storePathCommitLog=/home/rocketmq4.4/data/store/commitlog
#限制的消息大小
maxMessageSize=65536
flushCommitLogLeastPages=4
flushConsumeQueueLeastPages=2
flushCommitLogThoroughInterval=10000
flushConsumeQueueThoroughInterval=60000
checkTransactionMessageEnable=false
#发消息线程池数
sendMessageThreadPoolNums=128
#拉去消息线程池数
pullMessageThreadPoolNums=128
#broker角色:
#ASYSC_MASTER 异步复制master
#SYSC_MASTER 同步复制master
#SLAVE 从
brokerRole=SYSC_MASTER
#刷盘方式
#ASYNC_FLUSH 异步刷盘
#SYNC_FLUSH 同步刷盘
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.128.128
注意上处中的配置 brokerIP1 。 当本地的网络不止有一个ip地址的时候,容易出现连接不上的错误。会出现“ Caused by: org.apache.rocketmq.remoting.exception.RemotingConnectException: connect to 172.17.0.2:10911 failed 的问题”
据说这个问题的原因是因为虚拟机的多网卡造成的. 当使用命令 ifconfig 发现172.17.0.2 是docker的ip地址。当指定brikerIP1后,此问题解决。
修改配置文件(broker-a-s.properties)
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#brokerClusterName=DefaultCluster
#brokerName=broker-a
#brokerId=1
#deleteWhen=04
#fileReservedTime=48
#brokerRole=SLAVE
#flushDiskType=ASYNC_FLUSH
brokerClusterName=rocketmq-cluster
brokerName=broker-a
brokerId=1
namesrvAddr=127.0.0.1:9876
defaultTopicQueueNums=4
autoCreateTopicEnable=true
autoCreateSubscriptionGroup=true
listenPort=10950
deleteWhen=04
fileReservedTime=120
mapedFileSizeCommitLog=1073741824
mapedFileSizeConsumeQueue=300000
destroyMapedFileInterval=120000
redeleteHangedFileInterval=120000
diskMaxUsedSpaceRatio=88
storePathRootDir=/home/rocketmq4.4/data/store/slave
storePathCommitLog=/home/rocketmq4.4/data/store/slave/commitlog
maxMessageSize=65536
flushCommitLogLeastPages=4
flushConsumeQueueLeastPages=2
flushCommitLogThoroughInterval=10000
flushConsumeQueueThoroughInterval=60000
checkTransactionMessageEnable=false
sendMessageThreadPoolNums=128
pullMessageThreadPoolNums=128
brokerRole=SLAVE
flushDiskType=ASYNC_FLUSH
brokerIP1=192.168.128.128
修改日志配置文件:
#创建日志目录
mkdir -p /home/rocketmq4.4/logs
#替换*.xml文件中的{user.home}为自己指定的目录
cd /home/rocketmq4.4/conf && sed -i 's#${user.home}#/home/rocketmq4.4#g' *.xml
改参数:
runbroker.sh,runserver.sh启动参数默认对jvm的堆内存设置比较大(不改启动不起来),如果是虚拟机非线上环境需要改下参数,大小可以根据自己机器来决定。
[root@localhost bin]# pwd
/home/rocketmq4.4/bin
[root@localhost bin]# ls
cachedog.sh mqadmin mqbroker.cmd mqbroker.numanode3 mqnamesrv.xml play.cmd runbroker.sh startfsrv.sh
cleancache.sh mqadmin.cmd mqbroker.numanode0 mqbroker.xml mqshutdown play.sh runserver.cmd tools.cmd
cleancache.v1.sh mqadmin.xml mqbroker.numanode1 mqnamesrv mqshutdown.cmd README.md runserver.sh tools.sh
hs_err_pid1551.log mqbroker mqbroker.numanode2 mqnamesrv.cmd os.sh runbroker.cmd setcache.sh
[root@localhost bin]#
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m"
JAVA_OPT="${JAVA_OPT} -XX:+UseG1GC -XX:G1HeapRegionSize=16m -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -XX:SoftRefLRUPolicyMSPerMB=0 -XX:SurvivorRatio=8"
JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:/dev/shm/mq_gc_%p.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintAdaptiveSizePolicy"
JAVA_OPT="${JAVA_OPT} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=30m"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
JAVA_OPT="${JAVA_OPT} -XX:+AlwaysPreTouch"
JAVA_OPT="${JAVA_OPT} -XX:MaxDirectMemorySize=15g"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages -XX:-UseBiasedLocking"
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib"
#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"
JAVA_OPT="${JAVA_OPT} -server -Xms256m -Xmx256m -Xmn128m"
JAVA_OPT="${JAVA_OPT} -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled -XX:SoftRefLRUPolicyMSPerMB=0 -XX:+CMSClassUnloadingEnabled -XX:SurvivorRatio=8 -XX:-UseParNewGC"
JAVA_OPT="${JAVA_OPT} -verbose:gc -Xloggc:/dev/shm/rmq_srv_gc.log -XX:+PrintGCDetails"
JAVA_OPT="${JAVA_OPT} -XX:-OmitStackTraceInFastThrow"
JAVA_OPT="${JAVA_OPT} -XX:-UseLargePages"
JAVA_OPT="${JAVA_OPT} -Djava.ext.dirs=${JAVA_HOME}/jre/lib/ext:${BASE_DIR}/lib"
#JAVA_OPT="${JAVA_OPT} -Xdebug -Xrunjdwp:transport=dt_socket,address=9555,server=y,suspend=n"
JAVA_OPT="${JAVA_OPT} ${JAVA_OPT_EXT}"
JAVA_OPT="${JAVA_OPT} -cp ${CLASSPATH}"
如果还是出现内存问题,可尝试修改下面几个文件:
[root@node-100 bin]# pwd
/usr/local/rocketmq/rocketmq-4.2/bin
[root@node-100 bin]# ls *.xml
mqadmin.xml mqbroker.xml mqfiltersrv.xml mqnamesrv.xml
[root@node-100 bin]#
将这几个文件中下面参数给删掉:
-XX:PermSize 设置持久代(perm gen)初始值 物理内存的1/64
-XX:MaxPermSize 设置持久代最大值 物理内存的1/4
3、开始启动
先启动namesrv
[root@localhost bin]# cd /home/rocketmq4.4/bin
[root@localhost bin]#nohup sh mqnamesrv &
[root@localhost bin]# jps
2584 Jps
2553 NamesrvStartup
启动BrokerServer
[root@localhost bin] nohup sh mqbroker -c /home/rocketmq4.4/conf/2m-2s-async/broker-a.properties &
启动BrokerServer (slave)
[root@localhost bin] nohup sh mqbroker -c /home/rocketmq4.4/conf/2m-2s-async/broker-a-s.properties &
启动完成,查看集群信息
[root@localhost 2m-2s-async]# sh mqadmin clusterlist -n 127.0.0.1:9876
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize=128m; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=128m; support was removed in 8.0
#Cluster Name #Broker Name #BID #Addr #Version #InTPS(LOAD) #OutTPS(LOAD) #PCWait(ms) #Hour #SPACE
rocketmq-cluster broker-a 0 172.17.0.1:10911 V4_4_0 0.00(0,0ms) 0.00(0,0ms) 0 430405.67 0.6731
rocketmq-cluster broker-a 1 172.17.0.1:10950 V4_4_0 0.00(0,0ms) 0.00(0,0ms) 0 430405.67 0.6731
[root@localhost 2m-2s-async]#
启动成功!
二、RocketMQ Console搭建
RocketMQ有一个对其扩展的开源项目incubator-rocketmq-externals,这个项目中有一个子模块叫“rocketmq-console”,这个便是管理控制台项目了。
先将incubator-rocketmq-externals拉到本地,因为我们需要自己对rocketmq-console进行编译打包运行。
下载完成可以在下载路径看到有一个名为" rocketmq-externals "的文件夹,点进去可以看到rocketmq-console的子文件夹,这个呐,就是我们所需要的了~
进入该文件…\rocketmq-externals\rocketmq-console\src\main\resources\application.properties这个配置文件
标红线的地方是需要添加自己的NameServer地址,至于那个isVIPChannel设置为false,是因为开启VIP通道的话,端口就会改变,(连不上10911的问题,记得把防火墙把端口开放)
另外在rocketmq-console工程中的pom.xml中修改一下版本号(去除快照的版本号,默认是4.4.0-SNAPSHOT):此处困扰了好久,原来官方还有这个坑。。
修改好之后进行保存,打开Dos命令找到rocketmq-console的位置,对其进行编译打包成jar包
打包命令 mvn clean package -Dmaven.test.skip=true,我用mvn package一直报错,jar也生成不了。
之后会在该路径下生成一个jar文件
mvn clean package -f D:\opensource\rocketmq\rocketmq-console\rocketmq-externals\rocketmq-console\pom.xml -Dmaven.test.skip=true
获取到jar文件后,执行
java -jar rocketmq-console-ng-1.0.0.jar 启动控制台
三、java调用rocketmq
添加rocketmq的pom的依赖
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
- 同步发送消息:
原理:同步发送是指消息发送方发出数据后,会在收到接收方发回响应之后才发下一个数据包的通讯方式。
应用场景:此种方式应用场景非常广泛,例如重要通知邮件、报名短信通知、营销短信系统等。
public class SyncProducer {
public static void main(String[] args) throws Exception {
//Instantiate with a producer group name.
//此处名字可以自己随便填写,rocketmq会自动创建;(我们在配置文件中有配置
// #是否允许broker自动创建订阅组,建议线下开始,线上关闭
// autoCreateSubscriptionGroup=true) 具体见broker-a.properties文件
DefaultMQProducer producer = new
DefaultMQProducer("rocketmq-cluster-SyncProducer");
// Specify name server addresses.
producer.setNamesrvAddr("localhost:9876");
//Launch the instance.
producer.start();
for (int i = 0; i < 100; i++) {
//Create a message instance, specifying topic, tag and message body.
Message msg = new Message("TopicTest" /* Topic */,
"TagA" /* Tag */,
("Hello RocketMQ " +
i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
//Call send message to deliver message to one of brokers.
SendResult sendResult = producer.send(msg);
System.out.printf("%s%n", sendResult);
}
//Shut down once the producer instance is not longer in use.
producer.shutdown();
}
}
- 异步发送消息:
原理:异步发送是指发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。 消息队列 RocketMQ 的异步发送,需要用户实现异步发送回调接口(SendCallback)。消息发送方在发送了一条消息后,不需要等待服务器响应即可返回,进行第二条消息发送。发送方通过回调接口接收服务器响应,并对响应结果进行处理。
应用场景:异步发送一般用于链路耗时较长,对 RT 响应时间较为敏感的业务场景,例如用户视频上传后通知启动转码服务,转码完成后通知推送转码结果等。
public class AsyncProducer {
public static void main(String[] args) throws Exception {
//Instantiate with a producer group name.
//此处名字可以自己随便填写,rocketmq会自动创建;(我们在配置文件中有配置
// #是否允许broker自动创建订阅组,建议线下开始,线上关闭
// autoCreateSubscriptionGroup=true) 具体见broker-a.properties文件
DefaultMQProducer producer = new DefaultMQProducer("rocketmq-cluster-AsyncProducer");
// Specify name server addresses.
producer.setNamesrvAddr("localhost:9876");
//Launch the instance.
producer.start();
producer.setRetryTimesWhenSendAsyncFailed(0);
for (int i = 0; i < 100; i++) {
final int index = i;
//Create a message instance, specifying topic, tag and message body.
Message msg = new Message("TopicTest",
"TagA",
"OrderID188",
"Hello world".getBytes(RemotingHelper.DEFAULT_CHARSET));
producer.send(msg, new SendCallback() {
@Override
public void onSuccess(SendResult sendResult) {
System.out.printf("%-10d OK %s %n", index,
sendResult.getMsgId());
}
@Override
public void onException(Throwable e) {
System.out.printf("%-10d Exception %s %n", index, e);
e.printStackTrace();
}
});
}
//Shut down once the producer instance is not longer in use.
// producer.shutdown();
//注意此处要设置线程不退出,否则线程直接退出,就会发送消息失败。
Thread.sleep(Long.MAX_VALUE);
}
}
3.单项传输
原理:单向(Oneway)发送特点为发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。 此方式发送消息的过程耗时非常短,一般在微秒级别。
应用场景:适用于某些耗时非常短,但对可靠性要求并不高的场景,例如日志收集。
public class OnewayProducer {
public static void main(String[] args) throws Exception{
//Instantiate with a producer group name.
DefaultMQProducer producer = new DefaultMQProducer("rocketmq-cluster");
// Specify name server addresses.
producer.setNamesrvAddr("192.168.128.128:9876");
//Launch the instance.
producer.start();
for (int i = 0; i < 10; i++) {
//Create a message instance, specifying topic, tag and message body.
Message msg = new Message("TopicTest" /* Topic */,
"TagA" /* Tag */,
("Hello RocketMQ " +
i).getBytes(RemotingHelper.DEFAULT_CHARSET) /* Message body */
);
//Call send message to deliver message to one of brokers.
producer.sendOneway(msg);
}
//Shut down once the producer instance is not longer in use.
producer.shutdown();
}
}
4.接收消息
public class Consumer {
public static void main(String[] args) throws InterruptedException, MQClientException {
//此处名字可以自己随便填写,rocketmq会自动创建;(我们在配置文件中有配置
// #是否允许broker自动创建订阅组,建议线下开始,线上关闭
// autoCreateSubscriptionGroup=true) 具体见broker-a.properties文件
// Instantiate with specified consumer group name.
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("rocketmq-cluster");
// Specify name server addresses.
consumer.setNamesrvAddr("192.168.128.128:9876");
// Subscribe one more more topics to consume.
consumer.subscribe("TopicTest", "*");
// Register callback to execute on arrival of messages fetched from brokers.
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
//Launch the consumer instance.
consumer.start();
System.out.printf("Consumer Started.%n");
Thread.sleep(Long.MAX_VALUE);
}
}
5、多播模式 (consumer.setMessageModel(MessageModel.BROADCASTING);)
public class BroadcastConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group_name");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
//set to broadcast mode
consumer.setMessageModel(MessageModel.BROADCASTING);
consumer.subscribe("TopicTest", "TagA || TagC || TagD");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
System.out.printf(Thread.currentThread().getName() + " Receive New Messages: " + msgs + "%n");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
consumer.start();
System.out.printf("Broadcast Consumer Started.%n");
}
}
- 顺序消息
消息有序指的是可以按照消息的发送顺序来消费。
RocketMQ可以严格的保证消息有序。但这个顺序,不是全局顺序,只是分区(queue)顺序。要全局顺序只能一个分区。
下面是官方的一个例子
public class OrderedProducer {
public static void main(String[] args) throws Exception {
//Instantiate with a producer group name.
MQProducer producer = new DefaultMQProducer("example_group_name");
//Launch the instance.
producer.start();
String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 100; i++) {
int orderId = i % 10;
//Create a message instance, specifying topic, tag and message body.
Message msg = new Message("TopicTestjjj", tags[i % tags.length], "KEY" + i,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Integer id = (Integer) arg;
int index = id % mqs.size();
return mqs.get(index);
}
}, orderId);
System.out.printf("%s%n", sendResult);
}
//server shutdown
producer.shutdown();
}
}
消费(注意: 此处是MessageListenerOrderly)
public class OrderedConsumer {
public static void main(String[] args) throws Exception {
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group_name");
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
consumer.subscribe("TopicTest", "TagA || TagC || TagD");
consumer.registerMessageListener(new MessageListenerOrderly() {
AtomicLong consumeTimes = new AtomicLong(0);
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeOrderlyContext context) {
context.setAutoCommit(false);
System.out.printf(Thread.currentThread().getName() + " Receive New Messages: " + msgs + "%n");
this.consumeTimes.incrementAndGet();
if ((this.consumeTimes.get() % 2) == 0) {
return ConsumeOrderlyStatus.SUCCESS;
} else if ((this.consumeTimes.get() % 3) == 0) {
return ConsumeOrderlyStatus.ROLLBACK;
} else if ((this.consumeTimes.get() % 4) == 0) {
return ConsumeOrderlyStatus.COMMIT;
} else if ((this.consumeTimes.get() % 5) == 0) {
context.setSuspendCurrentQueueTimeMillis(3000);
return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;
}
return ConsumeOrderlyStatus.SUCCESS;
}
});
consumer.start();
System.out.printf("Consumer Started.%n");
}
}
3、事务消息
事务消息生产者:TransactionProducer.java
public class TransactionProducer {
public static void main(String[] args) throws MQClientException, InterruptedException {
TransactionListener transactionListener = new TransactionListenerImpl();
TransactionMQProducer producer = new TransactionMQProducer("transactionGroup1");
ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("client-transaction-msg-check-thread");
return thread;
}
});
producer.setNamesrvAddr("192.168.128.128:9876");
producer.setExecutorService(executorService);
producer.setTransactionListener(transactionListener);
producer.start();
String[] tags = new String[] {"TagA", "TagB", "TagC", "TagD", "TagE"};
for (int i = 0; i < 10; i++) {
try {
Message msg =
new Message("TopicTest1234", tags[i % tags.length], "KEY" + i,
("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
SendResult sendResult = producer.sendMessageInTransaction(msg, null);
System.out.printf("%s%n", sendResult);
Thread.sleep(10);
} catch (MQClientException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 100000; i++) {
Thread.sleep(1000);
}
producer.shutdown();
}
}
TransactionListenerImpl.java
public class TransactionListenerImpl implements TransactionListener {
private AtomicInteger transactionIndex = new AtomicInteger(0);
private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();
//会优先执行该方法
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
int value = transactionIndex.getAndIncrement();
int status = value % 3;
localTrans.put(msg.getTransactionId(), status);
return LocalTransactionState.UNKNOW;
}
//当返回是UNKNOW 的时候,此检查方法会被定时的调用。
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
Integer status = localTrans.get(msg.getTransactionId());
if (null != status) {
switch (status) {
case 0:
return LocalTransactionState.UNKNOW;
case 1:
return LocalTransactionState.COMMIT_MESSAGE;
case 2:
return LocalTransactionState.ROLLBACK_MESSAGE;
}
}
return LocalTransactionState.COMMIT_MESSAGE;
}
}
消费者和其他的一致,只要保障Topic和nameserver地址配置对即可。