以下是 rocketmq-client-cpp
的主要功能及其对应的 C++ 示例代码。每个功能都附带详细的解释,帮助你快速理解如何在实际项目中使用该库。
源文件准备
- 编译,安装rocket-client-cpp
- 在main.cpp添加文件common.h,并将附录"附录 官网 example 中 common.h"内容添加到common.h中
- 添加对接测试Demo,即可成功运行
1. 消息生产
1.1 同步消息发送 SyncProducer.cpp
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>
#include "common.h"
using namespace rocketmq;
std::atomic<bool> g_quit;
std::mutex g_mtx;
std::condition_variable g_finished;
TpsReportService g_tps;
void SyncProducerWorker(RocketmqSendAndConsumerArgs* info, DefaultMQProducer* producer) {
while (!g_quit.load()) {
if (g_msgCount.load() <= 0) {
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
}
MQMessage msg(info->topic, // topic
"*", // tag
info->body); // body
try {
auto start = std::chrono::system_clock::now();
SendResult sendResult = producer->send(msg, info->SelectUnactiveBroker);
g_tps.Increment();
--g_msgCount;
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
if (duration.count() >= 500) {
std::cout << "send RT more than: " << duration.count() << " ms with msgid: " << sendResult.getMsgId() << endl;
}
} catch (const MQException& e) {
std::cout << "send failed: " << std::endl;
}
}
}
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
PrintRocketmqSendAndConsumerArgs(info);
DefaultMQProducer producer("please_rename_unique_group_name");
producer.setNamesrvAddr(info.namesrv);
producer.setNamesrvDomain(info.namesrv_domain);
producer.setGroupName(info.groupname);
producer.setInstanceName(info.groupname);
producer.setSessionCredentials("mq acesskey", "mq secretkey", "ALIYUN");
producer.setSendMsgTimeout(500);
producer.setTcpTransportTryLockTimeout(1000);
producer.setTcpTransportConnectTimeout(400);
producer.start();
std::vector<std::shared_ptr<std::thread>> work_pool;
auto start = std::chrono::system_clock::now();
int msgcount = g_msgCount.load();
g_tps.start();
int threadCount = info.thread_count;
for (int j = 0; j < threadCount; j++) {
std::shared_ptr<std::thread> th = std::make_shared<std::thread>(SyncProducerWorker, &info, &producer);
work_pool.push_back(th);
}
{
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.wait(lck);
g_quit.store(true);
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n"
<< "========================finished==============================\n";
for (size_t th = 0; th != work_pool.size(); ++th) {
work_pool[th]->join();
}
producer.shutdown();
return 0;
}
- 功能:同步等待消息发送的结果,适合对可靠性要求高的场景。
**1.2 异步消息发送 **AsyncProducer.cpp
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <chrono>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>
#include "common.h"
using namespace rocketmq;
std::atomic<bool> g_quit;
std::mutex g_mtx;
std::condition_variable g_finished;
SendCallback* g_callback = NULL;
TpsReportService g_tps;
class MySendCallback : public SendCallback {
virtual void onSuccess(SendResult& sendResult) {
g_msgCount--;
g_tps.Increment();
if (g_msgCount.load() <= 0) {
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
}
}
virtual void onException(MQException& e) { cout << "send Exception\n"; }
};
class MyAutoDeleteSendCallback : public AutoDeleteSendCallBack {
public:
virtual ~MyAutoDeleteSendCallback() {}
virtual void onSuccess(SendResult& sendResult) {
g_msgCount--;
if (g_msgCount.load() <= 0) {
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
}
}
virtual void onException(MQException& e) { std::cout << "send Exception" << e << "\n"; }
};
void AsyncProducerWorker(RocketmqSendAndConsumerArgs* info, DefaultMQProducer* producer) {
while (!g_quit.load()) {
if (g_msgCount.load() <= 0) {
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
}
MQMessage msg(info->topic, // topic
"*", // tag
info->body); // body
if (info->IsAutoDeleteSendCallback) {
g_callback = new MyAutoDeleteSendCallback(); // auto delete
}
try {
producer->send(msg, g_callback);
} catch (MQException& e) {
std::cout << e << endl; // if catch excepiton , need re-send this msg by
// service
}
}
}
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
DefaultMQProducer producer("please_rename_unique_group_name");
if (!info.IsAutoDeleteSendCallback) {
g_callback = new MySendCallback();
}
PrintRocketmqSendAndConsumerArgs(info);
if (!info.namesrv.empty())
producer.setNamesrvAddr(info.namesrv);
producer.setGroupName(info.groupname);
producer.setInstanceName(info.groupname);
producer.setNamesrvDomain(info.namesrv_domain);
producer.start();
g_tps.start();
std::vector<std::shared_ptr<std::thread>> work_pool;
auto start = std::chrono::system_clock::now();
int msgcount = g_msgCount.load();
for (int j = 0; j < info.thread_count; j++) {
std::shared_ptr<std::thread> th = std::make_shared<std::thread>(AsyncProducerWorker, &info, &producer);
work_pool.push_back(th);
}
{
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.wait(lck);
g_quit.store(true);
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n"
<< "========================finished==============================\n";
producer.shutdown();
for (size_t th = 0; th != work_pool.size(); ++th) {
work_pool[th]->join();
}
if (!info.IsAutoDeleteSendCallback) {
delete g_callback;
}
return 0;
}
- 功能:异步发送消息,提高吞吐量,适合性能敏感场景。
异步消息发送(v1.2) BatchProducer.cpp
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>
#include <vector>
#include "common.h"
using namespace rocketmq;
using namespace std;
std::atomic<bool> g_quit;
std::mutex g_mtx;
std::condition_variable g_finished;
TpsReportService g_tps;
void SyncProducerWorker(RocketmqSendAndConsumerArgs* info, DefaultMQProducer* producer) {
while (!g_quit.load()) {
if (g_msgCount.load() <= 0) {
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
break;
}
vector<MQMessage> msgs;
MQMessage msg1(info->topic, "*", info->body);
msg1.setProperty("property1", "value1");
MQMessage msg2(info->topic, "*", info->body);
msg2.setProperty("property1", "value1");
msg2.setProperty("property2", "value2");
MQMessage msg3(info->topic, "*", info->body);
msg3.setProperty("property1", "value1");
msg3.setProperty("property2", "value2");
msg3.setProperty("property3", "value3");
msgs.push_back(msg1);
msgs.push_back(msg2);
msgs.push_back(msg3);
try {
auto start = std::chrono::system_clock::now();
SendResult sendResult = producer->send(msgs);
g_tps.Increment();
--g_msgCount;
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
if (duration.count() >= 500) {
std::cout << "send RT more than: " << duration.count() << " ms with msgid: " << sendResult.getMsgId() << endl;
}
} catch (const MQException& e) {
std::cout << "send failed: " << e.what() << std::endl;
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
return;
}
}
}
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
PrintRocketmqSendAndConsumerArgs(info);
DefaultMQProducer producer("please_rename_unique_group_name");
producer.setNamesrvAddr(info.namesrv);
producer.setNamesrvDomain(info.namesrv_domain);
producer.setGroupName(info.groupname);
producer.setInstanceName(info.groupname);
producer.setSessionCredentials("mq acesskey", "mq secretkey", "ALIYUN");
producer.setSendMsgTimeout(500);
producer.setTcpTransportTryLockTimeout(1000);
producer.setTcpTransportConnectTimeout(400);
producer.start();
std::vector<std::shared_ptr<std::thread>> work_pool;
auto start = std::chrono::system_clock::now();
int msgcount = g_msgCount.load();
g_tps.start();
int threadCount = info.thread_count;
for (int j = 0; j < threadCount; j++) {
std::shared_ptr<std::thread> th = std::make_shared<std::thread>(SyncProducerWorker, &info, &producer);
work_pool.push_back(th);
}
{
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.wait(lck);
g_quit.store(true);
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n"
<< "========================finished==============================\n";
for (size_t th = 0; th != work_pool.size(); ++th) {
work_pool[th]->join();
}
producer.shutdown();
return 0;
}
1.3 单向消息发送
1
- 功能:不等待服务器响应,适合对可靠性要求不高但性能要求极高的场景。
2. 消息消费
**2.1 推模式消费 **PushConsumer.cpp
/*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include <chrono>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "common.h"
std::mutex g_mtx;
std::condition_variable g_finished;
TpsReportService g_tps;
using namespace rocketmq;
class MyMsgListener : public MessageListenerConcurrently {
public:
MyMsgListener() {}
virtual ~MyMsgListener() {}
virtual ConsumeStatus consumeMessage(const std::vector<MQMessageExt>& msgs) {
g_msgCount.store(g_msgCount.load() - msgs.size());
for (size_t i = 0; i < msgs.size(); ++i) {
g_tps.Increment();
cout << "msg body: "<< msgs[i].getBody() << endl;
}
if (g_msgCount.load() <= 0) {
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
}
return CONSUME_SUCCESS;
}
};
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
PrintRocketmqSendAndConsumerArgs(info);
DefaultMQPushConsumer consumer("please_rename_unique_group_name");
DefaultMQProducer producer("please_rename_unique_group_name");
producer.setSessionCredentials("AccessKey", "SecretKey", "ALIYUN");
producer.setTcpTransportTryLockTimeout(1000);
producer.setTcpTransportConnectTimeout(400);
producer.setNamesrvDomain(info.namesrv_domain);
producer.setNamesrvAddr(info.namesrv);
producer.setGroupName("msg-persist-group_producer_sandbox");
producer.start();
consumer.setNamesrvAddr(info.namesrv);
consumer.setGroupName(info.groupname);
consumer.setSessionCredentials("AccessKey", "SecretKey", "ALIYUN");
consumer.setConsumeThreadCount(info.thread_count);
consumer.setNamesrvDomain(info.namesrv_domain);
consumer.setConsumeFromWhere(CONSUME_FROM_LAST_OFFSET);
if (info.syncpush)
consumer.setAsyncPull(false); // set sync pull
if (info.broadcasting) {
consumer.setMessageModel(rocketmq::BROADCASTING);
}
consumer.setInstanceName(info.groupname);
consumer.subscribe(info.topic, "*");
consumer.setConsumeThreadCount(15);
consumer.setTcpTransportTryLockTimeout(1000);
consumer.setTcpTransportConnectTimeout(400);
MyMsgListener msglistener;
consumer.registerMessageListener(&msglistener);
try {
consumer.start();
} catch (MQClientException& e) {
cout << e << endl;
}
g_tps.start();
int msgcount = g_msgCount.load();
for (int i = 0; i < msgcount; ++i) {
MQMessage msg(info.topic, // topic
"*", // tag
info.body); // body
// std::this_thread::sleep_for(std::chrono::seconds(100000));
try {
producer.send(msg);
} catch (MQException& e) {
std::cout << e << endl; // if catch excepiton , need re-send this msg by
// service
}
}
{
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.wait(lck);
}
producer.shutdown();
consumer.shutdown();
return 0;
}
- 功能:客户端自动接收并处理消息,适合实时消费场景。
推模式消费 AsyncPushConsumer.cpp
/*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <vector>
#include "common.h"
std::mutex g_mtx;
std::condition_variable g_finished;
TpsReportService g_tps;
using namespace rocketmq;
class MyMsgListener : public MessageListenerConcurrently {
public:
MyMsgListener() {}
virtual ~MyMsgListener() {}
virtual ConsumeStatus consumeMessage(const std::vector<MQMessageExt>& msgs) {
for (const auto& msg : msgs) {
std::cout << "Received message: "<< "Tags:" << msg.getTags() << ", body:" << msg.getBody() << std::endl;
}
g_msgCount.store(g_msgCount.load() - msgs.size());
for (size_t i = 0; i < msgs.size(); ++i) {
g_tps.Increment();
}
if (g_msgCount.load() <= 0) {
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
}
return CONSUME_SUCCESS;
}
};
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
PrintRocketmqSendAndConsumerArgs(info);
DefaultMQPushConsumer consumer("please_rename_unique_group_name");
DefaultMQProducer producer("please_rename_unique_group_name");
producer.setNamesrvAddr(info.namesrv);
producer.setGroupName("msg-persist-group_producer_sandbox");
producer.setNamesrvDomain(info.namesrv_domain);
producer.start();
consumer.setNamesrvAddr(info.namesrv);
consumer.setGroupName(info.groupname);
consumer.setNamesrvDomain(info.namesrv_domain);
consumer.setConsumeFromWhere(CONSUME_FROM_LAST_OFFSET);
consumer.setInstanceName(info.groupname);
consumer.subscribe(info.topic, "*");
consumer.setConsumeThreadCount(15);
consumer.setTcpTransportTryLockTimeout(1000);
consumer.setTcpTransportConnectTimeout(400);
MyMsgListener msglistener;
consumer.registerMessageListener(&msglistener);
try {
consumer.start();
} catch (MQClientException& e) {
cout << e << endl;
}
g_tps.start();
int msgcount = g_msgCount.load();
for (int i = 0; i < msgcount; ++i) {
MQMessage msg(info.topic, // topic
"*", // tag
info.body); // body
try {
producer.send(msg);
} catch (MQException& e) {
std::cout << e << endl; // if catch excepiton , need re-send this msg by
// service
}
}
{
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.wait(lck);
}
producer.shutdown();
consumer.shutdown();
return 0;
}
推模式消费 OrderlyPushConsumer.cpp
/*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <map>
#include <mutex>
#include <thread>
#include <vector>
#include "common.h"
using namespace rocketmq;
std::condition_variable g_finished;
std::mutex g_mtx;
std::atomic<int> g_consumedCount(0);
std::atomic<bool> g_quit(false);
TpsReportService g_tps;
class MyMsgListener : public MessageListenerOrderly {
public:
MyMsgListener() {}
virtual ~MyMsgListener() {}
virtual ConsumeStatus consumeMessage(const vector<MQMessageExt>& msgs) {
if (g_consumedCount.load() >= g_msgCount) {
std::unique_lock<std::mutex> lK(g_mtx);
g_quit.store(true);
g_finished.notify_one();
}
for (size_t i = 0; i < msgs.size(); i++) {
++g_consumedCount;
g_tps.Increment();
}
return CONSUME_SUCCESS;
}
};
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
PrintRocketmqSendAndConsumerArgs(info);
DefaultMQPushConsumer consumer("please_rename_unique_group_name");
DefaultMQProducer producer("please_rename_unique_group_name");
producer.setNamesrvAddr(info.namesrv);
producer.setGroupName("msg-persist-group_producer_sandbox");
producer.start();
consumer.setNamesrvAddr(info.namesrv);
consumer.setNamesrvDomain(info.namesrv_domain);
consumer.setGroupName(info.groupname);
consumer.setConsumeFromWhere(CONSUME_FROM_LAST_OFFSET);
consumer.subscribe(info.topic, "*");
consumer.setConsumeThreadCount(info.thread_count);
consumer.setConsumeMessageBatchMaxSize(31);
if (info.syncpush)
consumer.setAsyncPull(false);
MyMsgListener msglistener;
consumer.registerMessageListener(&msglistener);
g_tps.start();
try {
consumer.start();
} catch (MQClientException& e) {
std::cout << e << std::endl;
}
int msgcount = g_msgCount.load();
for (int i = 0; i < msgcount; ++i) {
MQMessage msg(info.topic, // topic
"*", // tag
info.body); // body
try {
producer.send(msg);
} catch (MQException& e) {
std::cout << e << endl; // if catch excepiton , need re-send this msg by
// service
}
}
while (!g_quit.load()) {
std::unique_lock<std::mutex> lk(g_mtx);
g_finished.wait(lk);
}
producer.shutdown();
consumer.shutdown();
return 0;
}
2.2 拉模式消费 PullConsumer.cpp
/*
* 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.
*/
#include <stdlib.h>
#include <chrono>
#include <iomanip>
#include <iostream>
#include <map>
#include <vector>
#include "common.h"
using namespace rocketmq;
std::map<MQMessageQueue, uint64_t> g_offseTable;
void putMessageQueueOffset(MQMessageQueue mq, uint64_t offset) {
g_offseTable[mq] = offset;
}
uint64_t getMessageQueueOffset(MQMessageQueue mq) {
map<MQMessageQueue, uint64_t>::iterator it = g_offseTable.find(mq);
if (it != g_offseTable.end()) {
return it->second;
}
return 0;
}
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
PrintRocketmqSendAndConsumerArgs(info);
DefaultMQPullConsumer consumer("please_rename_unique_group_name");
consumer.setNamesrvAddr(info.namesrv);
consumer.setNamesrvDomain(info.namesrv_domain);
consumer.setGroupName(info.groupname);
consumer.setInstanceName(info.groupname);
consumer.registerMessageQueueListener(info.topic, NULL);
consumer.start();
std::vector<MQMessageQueue> mqs;
try {
consumer.fetchSubscribeMessageQueues(info.topic, mqs);
auto iter = mqs.begin();
for (; iter != mqs.end(); ++iter) {
std::cout << "mq:" << (*iter).toString() << endl;
}
} catch (MQException& e) {
std::cout << e << endl;
}
auto start = std::chrono::system_clock::now();
auto iter = mqs.begin();
for (; iter != mqs.end(); ++iter) {
MQMessageQueue mq = (*iter);
// if cluster model
// putMessageQueueOffset(mq, g_consumer.fetchConsumeOffset(mq,true));
// if broadcast model
// putMessageQueueOffset(mq, your last consume offset);
bool noNewMsg = false;
do {
try {
PullResult result = consumer.pull(mq, "*", getMessageQueueOffset(mq), 32);
g_msgCount += result.msgFoundList.size();
std::cout << result.msgFoundList.size() << std::endl;
// if pull request timeout or received NULL response, pullStatus will be
// setted to BROKER_TIMEOUT,
// And nextBeginOffset/minOffset/MaxOffset will be setted to 0
if (result.pullStatus != BROKER_TIMEOUT) {
putMessageQueueOffset(mq, result.nextBeginOffset);
PrintPullResult(&result);
} else {
cout << "broker timeout occur" << endl;
}
switch (result.pullStatus) {
case FOUND:
case NO_MATCHED_MSG:
case OFFSET_ILLEGAL:
case BROKER_TIMEOUT:
break;
case NO_NEW_MSG:
noNewMsg = true;
break;
default:
break;
}
} catch (MQClientException& e) {
std::cout << e << std::endl;
}
} while (!noNewMsg);
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "msg count: " << g_msgCount.load() << "\n";
std::cout << "per msg time: " << duration.count() / (double)g_msgCount.load() << "ms \n"
<< "========================finished==============================\n";
consumer.shutdown();
return 0;
}
- 功能:手动拉取消息,适合对消费过程需要精细控制的场景。
3. 事务消息 TransactionProducer.cpp
/*
* 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.
*/
#include <atomic>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>
#include "TransactionListener.h"
#include "TransactionMQProducer.h"
#include "TransactionSendResult.h"
#include "common.h"
using namespace rocketmq;
std::atomic<bool> g_quit;
std::mutex g_mtx;
std::condition_variable g_finished;
TpsReportService g_tps;
class MyTransactionListener : public TransactionListener {
virtual LocalTransactionState executeLocalTransaction(const MQMessage& msg, void* arg) {
if (!arg) {
std::cout << "executeLocalTransaction transactionId:" << msg.getTransactionId()
<< ", return state: COMMIT_MESAGE " << endl;
return LocalTransactionState::COMMIT_MESSAGE;
}
LocalTransactionState state = (LocalTransactionState)(*(int*)arg % 3);
std::cout << "executeLocalTransaction transactionId:" << msg.getTransactionId() << ", return state: " << state
<< endl;
return state;
}
virtual LocalTransactionState checkLocalTransaction(const MQMessageExt& msg) {
std::cout << "checkLocalTransaction enter msg:" << msg.toString() << endl;
return LocalTransactionState::COMMIT_MESSAGE;
}
};
void SyncProducerWorker(RocketmqSendAndConsumerArgs* info, TransactionMQProducer* producer) {
while (!g_quit.load()) {
if (g_msgCount.load() <= 0) {
std::this_thread::sleep_for(std::chrono::seconds(60));
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
break;
}
MQMessage msg(info->topic, // topic
"*", // tag
info->body); // body
try {
auto start = std::chrono::system_clock::now();
std::cout << "before sendMessageInTransaction" << endl;
LocalTransactionState state = LocalTransactionState::UNKNOWN;
TransactionSendResult sendResult = producer->sendMessageInTransaction(msg, &state);
std::cout << "after sendMessageInTransaction msgId: " << sendResult.getMsgId() << endl;
g_tps.Increment();
--g_msgCount;
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
if (duration.count() >= 500) {
std::cout << "send RT more than: " << duration.count() << " ms with msgid: " << sendResult.getMsgId() << endl;
}
} catch (const MQException& e) {
std::cout << "send failed: " << e.what() << std::endl;
}
}
}
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
PrintRocketmqSendAndConsumerArgs(info);
TransactionMQProducer producer("please_rename_unique_group_name");
producer.setNamesrvAddr(info.namesrv);
producer.setNamesrvDomain(info.namesrv_domain);
producer.setGroupName(info.groupname);
producer.setInstanceName(info.groupname);
producer.setSessionCredentials("mq acesskey", "mq secretkey", "ALIYUN");
producer.setSendMsgTimeout(500);
producer.setTcpTransportTryLockTimeout(1000);
producer.setTcpTransportConnectTimeout(400);
producer.setLogLevel(eLOG_LEVEL_DEBUG);
producer.setTransactionListener(new MyTransactionListener());
producer.start();
std::vector<std::shared_ptr<std::thread>> work_pool;
auto start = std::chrono::system_clock::now();
int msgcount = g_msgCount.load();
g_tps.start();
int threadCount = info.thread_count;
for (int j = 0; j < threadCount; j++) {
std::shared_ptr<std::thread> th = std::make_shared<std::thread>(SyncProducerWorker, &info, &producer);
work_pool.push_back(th);
}
{
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.wait(lck);
g_quit.store(true);
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n"
<< "========================finished==============================\n";
for (size_t th = 0; th != work_pool.size(); ++th) {
work_pool[th]->join();
}
producer.shutdown();
return 0;
}
- 输出结果
Failed to send message: msg: transactionListener is null,error:-1,in file </root/rocketmq-client-cpp-2.2.0/src/producer/TransactionMQProducerImpl.cpp> line:44
[root@localhost myproject_1_35]#
- 功能:确保消息与本地事务的一致性,适合分布式事务场景。
**4. 延时消息 ** SendDelayMsg.cpp
- 延迟消息的等级定义在服务端MessageStoreConfig.java 中,如下:
private String messageDelayLevel = "1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h";
/*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>
#include "common.h"
using namespace rocketmq;
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
PrintRocketmqSendAndConsumerArgs(info);
DefaultMQProducer producer("please_rename_unique_group_name");
producer.setNamesrvAddr(info.namesrv);
producer.setNamesrvDomain(info.namesrv_domain);
producer.setGroupName(info.groupname);
producer.setInstanceName(info.groupname);
producer.setSendMsgTimeout(500);
producer.setTcpTransportTryLockTimeout(1000);
producer.setTcpTransportConnectTimeout(400);
producer.start();
MQMessage msg(info.topic, // topic
"*", // tag
info.body); // body
// messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h
// 2h
msg.setDelayTimeLevel(5); // 1m
try {
SendResult sendResult = producer.send(msg, info.SelectUnactiveBroker);
} catch (const MQException& e) {
std::cout << "send failed: " << std::endl;
}
producer.shutdown();
return 0;
}
- 功能:发送延时消息,适合定时任务或延迟通知场景。
5. 顺序消息 OrderProducer.cpp
/*
* 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.
*/
#include <stdlib.h>
#include <string.h>
#include <condition_variable>
#include <iomanip>
#include <iostream>
#include <thread>
#include "common.h"
using namespace rocketmq;
std::condition_variable g_finished;
std::mutex g_mtx;
std::atomic<bool> g_quit(false);
class SelectMessageQueueByHash : public MessageQueueSelector {
public:
MQMessageQueue select(const std::vector<MQMessageQueue>& mqs, const MQMessage& msg, void* arg) {
int orderId = *static_cast<int*>(arg);
int index = orderId % mqs.size();
return mqs[index];
}
};
SelectMessageQueueByHash g_mySelector;
void ProducerWorker(RocketmqSendAndConsumerArgs* info, DefaultMQProducer* producer) {
while (!g_quit.load()) {
if (g_msgCount.load() <= 0) {
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.notify_one();
}
MQMessage msg(info->topic, // topic
"*", // tag
info->body); // body
int orderId = 1;
SendResult sendResult =
producer->send(msg, &g_mySelector, static_cast<void*>(&orderId), info->retrytimes, info->SelectUnactiveBroker);
--g_msgCount;
}
}
int main(int argc, char* argv[]) {
RocketmqSendAndConsumerArgs info;
#if 0
if (!ParseArgs(argc, argv, &info)) {
exit(-1);
}
#else
info.namesrv = "192.168.8.138:9876;192.168.8.139:9876";
info.namesrv_domain = "";
info.groupname = "please_rename_unique_group_name";
info.topic = "TopicTest";
info.body = "msgbody for test";
#endif
DefaultMQProducer producer("please_rename_unique_group_name");
PrintRocketmqSendAndConsumerArgs(info);
producer.setNamesrvAddr(info.namesrv);
producer.setNamesrvDomain(info.namesrv_domain);
producer.setGroupName(info.groupname);
producer.setInstanceName(info.groupname);
producer.start();
int msgcount = g_msgCount.load();
std::vector<std::shared_ptr<std::thread>> work_pool;
int threadCount = info.thread_count;
for (int j = 0; j < threadCount; j++) {
std::shared_ptr<std::thread> th = std::make_shared<std::thread>(ProducerWorker, &info, &producer);
work_pool.push_back(th);
}
auto start = std::chrono::system_clock::now();
{
std::unique_lock<std::mutex> lck(g_mtx);
g_finished.wait(lck);
g_quit.store(true);
}
auto end = std::chrono::system_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "per msg time: " << duration.count() / (double)msgcount << "ms \n"
<< "========================finished==============================\n";
for (size_t th = 0; th != work_pool.size(); ++th) {
work_pool[th]->join();
}
producer.shutdown();
return 0;
}
- 功能:保证同一分区内消息顺序,适合需要有序处理的场景。
6. 消息过滤
基于 Tag 的过滤
consumer.subscribe("TestTopic", "TagA || TagB");
- 功能:仅接收指定 Tag 的消息。
基于 SQL 表达式的过滤
- 功能:通过 SQL 表达式过滤消息,适合复杂过滤场景。
7. 监控与日志
- 客户端监控:支持获取生产者/消费者状态、统计信息等。
- 日志记录:默认集成日志功能,便于排查问题。
总结
上述代码覆盖了 rocketmq-client-cpp
的主要功能,包括生产消息、消费消息、事务消息、延时消息、顺序消息等。这些功能可以根据业务需求自由组合。如果有更复杂的使用场景或问题,欢迎进一步交流!
附录 官网 example 中 common.h
/*
* 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.
*/
#ifndef ROCKETMQ_CLIENT4CPP_EXAMPLE_COMMON_H_
#define ROCKETMQ_CLIENT4CPP_EXAMPLE_COMMON_H_
#include <functional>
#include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <vector>
#ifndef WIN32
#include "unistd.h"
#endif
#include "Arg_helper.h"
#include "DefaultMQProducer.h"
#include "DefaultMQPullConsumer.h"
#include "DefaultMQPushConsumer.h"
using namespace std;
std::atomic<int> g_msgCount(1);
class RocketmqSendAndConsumerArgs {
public:
RocketmqSendAndConsumerArgs()
: body("msgbody for test"),
thread_count(std::thread::hardware_concurrency()),
broadcasting(false),
syncpush(false),
SelectUnactiveBroker(false),
IsAutoDeleteSendCallback(false),
retrytimes(5),
PrintMoreInfo(false) {}
public:
std::string namesrv;
std::string namesrv_domain;
std::string groupname;
std::string topic;
std::string body;
int thread_count;
bool broadcasting;
bool syncpush;
bool SelectUnactiveBroker; // default select active broker
bool IsAutoDeleteSendCallback;
int retrytimes; // default retry 5 times;
bool PrintMoreInfo;
};
class TpsReportService {
public:
TpsReportService() : tps_interval_(1), quit_flag_(false), tps_count_(0) {}
void start() {
if (tps_thread_ == NULL) {
std::cout << "tps_thread_ is null" << std::endl;
return;
}
tps_thread_.reset(new std::thread(std::bind(&TpsReportService::TpsReport, this)));
}
~TpsReportService() {
quit_flag_.store(true);
if (tps_thread_ == NULL) {
std::cout << "tps_thread_ is null" << std::endl;
return;
}
if (tps_thread_->joinable())
tps_thread_->join();
}
void Increment() { ++tps_count_; }
void TpsReport() {
while (!quit_flag_.load()) {
std::this_thread::sleep_for(tps_interval_);
std::cout << "tps: " << tps_count_.load() << std::endl;
tps_count_.store(0);
}
}
private:
std::chrono::seconds tps_interval_;
std::shared_ptr<std::thread> tps_thread_;
std::atomic<bool> quit_flag_;
std::atomic<long> tps_count_;
};
void PrintPullResult(rocketmq::PullResult* result) {
std::cout << result->toString() << std::endl;
if (result->pullStatus == rocketmq::FOUND) {
std::cout << result->toString() << endl;
std::vector<rocketmq::MQMessageExt>::iterator it = result->msgFoundList.begin();
for (; it != result->msgFoundList.end(); ++it) {
cout << "=======================================================" << endl << (*it).toString() << endl;
}
}
}
static void PrintRocketmqSendAndConsumerArgs(const RocketmqSendAndConsumerArgs& info) {
std::cout << "nameserver: " << info.namesrv << endl
<< "topic: " << info.topic << endl
<< "groupname: " << info.groupname << endl
<< "produce content: " << info.body << endl
<< "msg count: " << g_msgCount.load() << endl
<< "thread count: " << info.thread_count << endl;
}
static void help() {
std::cout << "need option,like follow: \n"
<< "-n nameserver addr, if not set -n and -i ,no nameSrv will be got \n"
"-i nameserver domain name, if not set -n and -i ,no nameSrv will be "
"got \n"
"-g groupname \n"
"-t msg topic \n"
"-m messagecout(default value: 1) \n"
"-c content(default value: only test ) \n"
"-b (BROADCASTING model, default value: CLUSTER) \n"
"-s sync push(default is async push)\n"
"-r setup retry times(default value: 5 times)\n"
"-u select active broker to send msg(default value: false)\n"
"-d use AutoDeleteSendcallback by cpp client(defalut value: false) \n"
"-T thread count of send msg or consume msg(defalut value: system cpu "
"core number) \n"
"-v print more details information \n";
}
static bool ParseArgs(int argc, char* argv[], RocketmqSendAndConsumerArgs* info) {
#ifndef WIN32
int ch;
while ((ch = getopt(argc, argv, "n:i:g:t:m:c:b:s:h:r:T:bu")) != -1) {
switch (ch) {
case 'n':
info->namesrv.insert(0, optarg);
break;
case 'i':
info->namesrv_domain.insert(0, optarg);
break;
case 'g':
info->groupname.insert(0, optarg);
break;
case 't':
info->topic.insert(0, optarg);
break;
case 'm':
g_msgCount.store(atoi(optarg));
break;
case 'c':
info->body.insert(0, optarg);
break;
case 'b':
info->broadcasting = true;
break;
case 's':
info->syncpush = true;
break;
case 'r':
info->retrytimes = atoi(optarg);
break;
case 'u':
info->SelectUnactiveBroker = true;
break;
case 'T':
info->thread_count = atoi(optarg);
break;
case 'v':
info->PrintMoreInfo = true;
break;
case 'h':
help();
return false;
default:
help();
return false;
}
}
#else
rocketmq::Arg_helper arg_help(argc, argv);
info->namesrv = arg_help.get_option_value("-n");
info->namesrv_domain = arg_help.get_option_value("-i");
info->groupname = arg_help.get_option_value("-g");
info->topic = arg_help.get_option_value("-t");
info->broadcasting = atoi(arg_help.get_option_value("-b").c_str());
string msgContent(arg_help.get_option_value("-c"));
if (!msgContent.empty())
info->body = msgContent;
info->syncpush = atoi(arg_help.get_option_value("-s").c_str());
int retrytimes = atoi(arg_help.get_option_value("-r").c_str());
if (retrytimes > 0)
info->retrytimes = retrytimes;
info->SelectUnactiveBroker = atoi(arg_help.get_option_value("-u").c_str());
int thread_count = atoi(arg_help.get_option_value("-T").c_str());
if (thread_count > 0)
info->thread_count = thread_count;
info->PrintMoreInfo = atoi(arg_help.get_option_value("-v").c_str());
g_msgCount = atoi(arg_help.get_option_value("-m").c_str());
#endif
if (info->groupname.empty() || info->topic.empty() || (info->namesrv_domain.empty() && info->namesrv.empty())) {
std::cout << "please use -g to setup groupname and -t setup topic \n";
help();
return false;
}
return true;
}
#endif // ROCKETMQ_CLIENT4CPP_EXAMPLE_COMMON_H_