Exemples de code pour l'envoi et la réception de messages planifiés et différés avec le SDK client TCP pour C++ (édition communautaire).
Différence entre messages planifiés et messages différés
ApsaraMQ for RocketMQ prend en charge deux types de livraison temporelle :
Messages planifiés : livrés aux consommateurs à un instant précis. Par exemple, envoyer une notification demain à 9 h 00.
Messages différés : livrés aux consommateurs après un délai spécifié. Par exemple, envoyer un rappel dans 30 minutes.
Ces deux types utilisent la propriété de message __STARTDELIVERTIME. La différence réside dans le calcul de l'horodatage :
| Type | Valeur de l'horodatage | Exemple |
|---|---|---|
| Planifié | Heure absolue en millisecondes (époque Unix) | 1583568060000 (7 mars 2020, 16:21:00) |
| Différé | Heure actuelle + délai en millisecondes | currentTimeMillis + 10000 (délai de 10 secondes) |
Pour plus d'informations, consultez la rubrique Messages planifiés et messages différés.
Différences par rapport à Apache RocketMQ
| Fonctionnalité | ApsaraMQ for RocketMQ | Apache RocketMQ |
|---|---|---|
| Messages planifiés | Pris en charge | Non pris en charge |
| Messages différés | Pris en charge | Pris en charge |
| Précision temporelle | Précision à la seconde | N/A |
| API dédiée | Propriété __STARTDELIVERTIME |
Aucune interface dédiée pour les messages planifiés |
Apache RocketMQ prend en charge les messages différés, mais pas les messages planifiés. Le SDK open source ne propose aucune interface dédiée pour les messages planifiés. ApsaraMQ for RocketMQ prend en charge les messages planifiés et différés, avec une précision à la seconde pour l'heure de planification et les délais, ainsi qu'une concurrence plus élevée.
Règles de définition de l'heure
La valeur
__STARTDELIVERTIMEest un horodatage Unix en millisecondes.Définissez un horodatage absolu pour les messages planifiés, ou définissez
currentTimeMillis + delayMillispour les messages différés.Si l'horodatage est antérieur à l'heure actuelle, le message est livré immédiatement.
Prérequis
Avant de commencer, assurez-vous d'avoir :
Installé la bibliothèque dynamique C++. Pour plus d'informations, consultez la page Installer la bibliothèque dynamique C++
Une paire AccessKey pour votre compte Alibaba Cloud. Pour plus d'informations, consultez la page Créer une paire AccessKey
Créé un topic et un ID de groupe dans la console ApsaraMQ for RocketMQ
Envoyer des messages planifiés ou différés
Pour planifier la livraison, définissez la propriété __STARTDELIVERTIME sur le message :
msg.setProperty("__STARTDELIVERTIME", to_string(deliverTimestamp));
Définissez deliverTimestamp sur un horodatage Unix en millisecondes. Pour un délai de 10 secondes, calculez-le comme suit :
long deliverTimestamp = currentTimeMillis + 10000;
Exemple complet de producteur
Enregistrez le code suivant sous le nom
DelayProducerDemo.cpp.Compilez :
g++ -o delay_producer_demo -std=c++11 -lz -lrocketmq DelayProducerDemo.cpp
Exécutez le binaire compilé.
Remplacez les espaces réservés dans le code par vos valeurs réelles :
| Espace réservé | Description | Exemple |
|---|---|---|
<group-id> |
ID de groupe provenant de la console ApsaraMQ for RocketMQ | GID_delay_demo |
<endpoint> |
Endpoint TCP provenant de la page Instance Details | http://MQ_INST_xxx.mq-internet-access.mq-internet.aliyuncs.com:80 |
<topic> |
Topic provenant de la console ApsaraMQ for RocketMQ | delay_topic |
#include <iostream>
#include <chrono>
#include <thread>
#include "DefaultMQProducer.h"
using namespace std;
using namespace rocketmq;
int main() {
std::cout << "=======Before sending messages=======" << std::endl;
// Group ID from the ApsaraMQ for RocketMQ console.
DefaultMQProducer producer("<group-id>");
// TCP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console.
producer.setNamesrvAddr("<endpoint>");
// Authenticate with your AccessKey ID and AccessKey secret from environment variables.
// Default user channel: ALIYUN.
producer.setSessionCredentials(getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"), getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"), "ALIYUN");
// Start the producer.
producer.start();
auto start = std::chrono::system_clock::now();
int count = 32;
for (int i = 0; i < count; ++i) {
// Topic from the ApsaraMQ for RocketMQ console.
MQMessage msg("<topic>", "HiTAG", "Hello,CPP SDK, Delay Message.");
chrono::system_clock::duration d = chrono::system_clock::now().time_since_epoch();
chrono::milliseconds mil = chrono::duration_cast<chrono::milliseconds>(d);
// Set the delivery time.
// - Scheduled message: use an absolute timestamp in milliseconds.
// For example, 1583568060000 delivers the message at 2020-03-07 16:21:00.
// - Delayed message: add the delay in milliseconds to the current time.
// The following example delays delivery by 10 seconds.
// If the timestamp is earlier than the current time, the message is delivered immediately.
long exp = mil.count() + 10000;
msg.setProperty("__STARTDELIVERTIME", to_string(exp));
std::cout << "Now: " << mil.count() << " Exp:" << exp << std::endl;
try {
SendResult sendResult = producer.send(msg);
std::cout << "SendResult:" << sendResult.getSendStatus() << ", Message ID: " << sendResult.getMsgId()
<< std::endl;
this_thread::sleep_for(chrono::seconds(1));
} catch (MQException e) {
std::cout << "ErrorCode: " << e.GetError() << " Exception:" << e.what() << std::endl;
}
}
auto interval = std::chrono::system_clock::now() - start;
std::cout << "Send " << count << " messages OK, costs "
<< std::chrono::duration_cast<std::chrono::milliseconds>(interval).count() << "ms" << std::endl;
producer.shutdown();
std::cout << "=======After sending messages=======" << std::endl;
return 0;
}
Recevoir des messages planifiés ou différés
La consommation de messages planifiés et différés fonctionne de la même manière que celle des messages normaux. Enregistrez un écouteur MessageListenerConcurrently et abonnez-vous au topic.
Exemple complet de consommateur
Enregistrez le code suivant sous le nom
DelayConsumerDemo.cpp.Compilez :
g++ -o delay_consumer_demo -std=c++11 -lz -lrocketmq DelayConsumerDemo.cpp
Exécutez le binaire compilé.
Remplacez les espaces réservés par vos valeurs réelles (identiques à l'exemple du producteur).
#include <iostream>
#include <thread>
#include <chrono>
#include "DefaultMQPushConsumer.h"
using namespace rocketmq;
using namespace std;
class ExampleDelayMessageListener : public MessageListenerConcurrently {
public:
ConsumeStatus consumeMessage(const std::vector<MQMessageExt> &msgs) {
for (auto item = msgs.begin(); item != msgs.end(); item++) {
chrono::system_clock::duration d = chrono::system_clock::now().time_since_epoch();
chrono::milliseconds mil = chrono::duration_cast<chrono::milliseconds>(d);
std::cout << "Now: " << mil.count() << " Received Message Topic:" << item->getTopic() << ", MsgId:"
<< item->getMsgId() << " DelayTime:" << item->getProperty("__STARTDELIVERTIME") << std::endl;
}
return CONSUME_SUCCESS;
}
};
int main(int argc, char *argv[]) {
std::cout << "=======Before consuming messages=======" << std::endl;
// Group ID from the ApsaraMQ for RocketMQ console.
DefaultMQPushConsumer *consumer = new DefaultMQPushConsumer("<group-id>");
// TCP endpoint from the Instance Details page in the ApsaraMQ for RocketMQ console.
consumer->setNamesrvAddr("<endpoint>");
// Authenticate with your AccessKey ID and AccessKey secret from environment variables.
// Default user channel: ALIYUN.
consumer->setSessionCredentials(getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"), getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"), "ALIYUN");
auto start = std::chrono::system_clock::now();
// Register a message listener.
ExampleDelayMessageListener *messageListener = new ExampleDelayMessageListener();
consumer->subscribe("<topic>", "*");
consumer->registerMessageListener(messageListener);
// Before starting the consumer:
// 1. Make sure that the subscription is configured.
// 2. Make sure that consumers in the same group have consistent subscriptions.
consumer->start();
// Keep the thread running. Do not shut down the consumer while messages are being received.
std::this_thread::sleep_for(std::chrono::seconds(600));
consumer->shutdown();
std::cout << "=======After consuming messages======" << std::endl;
return 0;
}
Bonnes pratiques
Stockez les identifiants dans des variables d'environnement. Les deux exemples utilisent les variables d'environnement
ALIBABA_CLOUD_ACCESS_KEY_IDetALIBABA_CLOUD_ACCESS_KEY_SECRETau lieu de valeurs codées en dur.Maintenez la cohérence des abonnements des consommateurs. Tous les consommateurs d'un même groupe doivent s'abonner aux mêmes topics et tags. Des abonnements incohérents entraînent un routage imprévisible des messages.