All Products
Search
Document Center

ApsaraMQ for RocketMQ:Send and subscribe to normal messages with TCP client SDKs

Last Updated:Oct 26, 2026

ApsaraMQ for RocketMQ provides commercial TCP client SDKs for Java, C/C++, and .NET. This guide walks you through creating a producer to send normal messages, verifying delivery, and starting a consumer to subscribe to those messages.

Note

Each message type -- normal, scheduled, delayed, ordered, and transactional -- requires a dedicated topic. Do not reuse a normal message topic for other message types.

Before you begin

Make sure you have:

Step 1: Set up the TCP client SDK

Commercial SDKs provide more features and higher stability than open-source SDKs. Use open-source SDKs only when migrating from an Apache RocketMQ cluster without code changes.

Choose your language and follow the setup instructions:

Language

Release notes

Environment setup

Sample code

Java

Release notes

Environment preparation

Sample code

C/C++

Release notes

Environment preparation

Sample code

.NET

Release notes

Environment preparation

Sample code

Step 2: Send normal messages

Create a producer and send normal messages with the following sample code.

Replace the placeholders with your actual values:

Placeholder

Description

Where to find it

<YOUR_ACCESS_KEY>

AccessKey ID for authentication

RAM console

<YOUR_SECRET_KEY>

AccessKey secret for authentication

RAM console

<YOUR_TOPIC>

Topic created for normal messages

ApsaraMQ for RocketMQ console

<YOUR_GROUP_ID>

Group ID created in the console

ApsaraMQ for RocketMQ console

<YOUR_TCP_ENDPOINT>

TCP endpoint of your instance

TCP Endpoint section on the Instance Details page

Java

Note

In the Internet environment of a RocketMQ V4 instance, using a gRPC-based SDK such as ons-client 2.0.x may cause resource verification errors. We recommend that you use ons-client 1.9.1.Final, which is based on the Remoting protocol and supports JDK 17. This version provides the best compatibility with the Internet environment of V4 instances.

The Java example sends a message asynchronously and returns the result through a callback.

import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.OnExceptionContext;
import com.aliyun.openservices.ons.api.Producer;
import com.aliyun.openservices.ons.api.SendCallback;
import com.aliyun.openservices.ons.api.SendResult;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;

import java.util.Properties;

public class ProducerTest {
    public static void main(String[] args) {
        Properties properties = new Properties();
        // AccessKey ID for authentication.
        properties.put(PropertyKeyConst.AccessKey, "<YOUR_ACCESS_KEY>");
        // AccessKey secret for authentication.
        properties.put(PropertyKeyConst.SecretKey, "<YOUR_SECRET_KEY>");
        // Timeout for sending a message, in milliseconds.
        properties.setProperty(PropertyKeyConst.SendMsgTimeoutMillis, "3000");
        // TCP endpoint of your instance.
        properties.put(PropertyKeyConst.NAMESRV_ADDR, "<YOUR_TCP_ENDPOINT>");

        Producer producer = ONSFactory.createProducer(properties);
        // Call start() once before sending messages.
        producer.start();

        Message msg = new Message(
                // Topic for the message.
                "<YOUR_TOPIC>",
                // Message tag for filtering on the broker.
                "TagA",
                // Message body in binary format.
                // Producer and consumer must agree on serialization and deserialization.
                "Hello MQ".getBytes());

        // Message key: a business-specific identifier, ideally globally unique.
        // Useful for querying or resending messages in the console if delivery fails.
        // Messages can be sent without a key.
        msg.setKey("ORDERID_100");

        // Send the message asynchronously. The result returns through the callback.
        producer.sendAsync(msg, new SendCallback() {
            @Override
            public void onSuccess(final SendResult sendResult) {
                System.out.println("send message success. topic=" +
                    sendResult.getTopic() + ", msgId=" + sendResult.getMessageId());
            }

            @Override
            public void onException(OnExceptionContext context) {
                // Handle the failure: retry or persist the message.
                System.out.println("send message failed. topic=" +
                    context.getTopic() + ", msgId=" + context.getMessageId());
            }
        });

        // The msgId is available before the callback returns.
        System.out.println("send message async. topic=" +
            msg.getTopic() + ", msgId=" + msg.getMsgID());

        // Shut down the producer before exiting. (Optional for Java)
        producer.shutdown();
    }
}

Expected output:

send message async. topic=<YOUR_TOPIC>, msgId=...
send message success. topic=<YOUR_TOPIC>, msgId=...

.NET

The .NET example sends messages synchronously in a loop.

using System;
using ons;

public class ProducerExampleForEx
{
    public ProducerExampleForEx()
    {
    }

    static void Main(string[] args) {
        ONSFactoryProperty factoryInfo = new ONSFactoryProperty();
        // AccessKey ID for authentication.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.AccessKey, "<YOUR_ACCESS_KEY>");
        // AccessKey secret for authentication.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.SecretKey, "<YOUR_SECRET_KEY>");
        // Group ID created in the ApsaraMQ for RocketMQ console.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.ProducerId, "<YOUR_GROUP_ID>");
        // Topic created in the ApsaraMQ for RocketMQ console.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.PublishTopics, "<YOUR_TOPIC>");
        // TCP endpoint of your instance.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.NAMESRV_ADDR, "<YOUR_TCP_ENDPOINT>");
        // Log path.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.LogPath, "C://log");

        // Create the producer instance.
        // A producer instance is thread-safe and can send messages to different topics.
        // Typically, one producer instance per thread is sufficient.
        Producer producer = ONSFactory.getInstance().createProducer(factoryInfo);

        // Start the producer.
        producer.start();

        // Build and send the message.
        Message msg = new Message(factoryInfo.getPublishTopics(), "tagA", "Example message body");
        msg.setKey(Guid.NewGuid().ToString());
        for (int i = 0; i < 32; i++) {
            try
            {
                SendResultONS sendResult = producer.send(msg);
                Console.WriteLine("send success {0}", sendResult.getMessageId());
            }
            catch (Exception ex)
            {
                Console.WriteLine("send failure{0}", ex.ToString());
            }
        }

        // Shut down the producer before exiting the thread.
        producer.shutdown();
    }
}

Expected output:

send success <message-id>
send success <message-id>
...

C/C++

The C/C++ example sends a message synchronously with exception handling.

#include "ONSFactory.h"
#include "ONSClientException.h"

using namespace ons;

int main()
{
    // Configure the producer.
    ONSFactoryProperty factoryInfo;
    // Group ID created in the ApsaraMQ for RocketMQ console.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::ProducerId, "<YOUR_GROUP_ID>");
    // TCP endpoint of your instance.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "<YOUR_TCP_ENDPOINT>");
    // Topic created in the ApsaraMQ for RocketMQ console.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::PublishTopics, "<YOUR_TOPIC>");
    // Message content.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::MsgContent, "Hello MQ");
    // AccessKey ID for authentication.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::AccessKey, "<YOUR_ACCESS_KEY>");
    // AccessKey secret for authentication.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::SecretKey, "<YOUR_SECRET_KEY>");

    // Create the producer.
    Producer *pProducer = ONSFactory::getInstance()->createProducer(factoryInfo);

    // Call start() once before sending messages.
    pProducer->start();

    Message msg(
            // Topic for the message.
            factoryInfo.getPublishTopics(),
            // Message tag for filtering on the broker.
            "TagA",
            // Message body. Cannot be empty.
            // Producer and consumer must agree on serialization and deserialization.
            factoryInfo.getMessageContent()
    );

    // Message key: a business-specific identifier, ideally globally unique.
    // Useful for querying or resending messages in the console if delivery fails.
    // Messages can be sent without a key.
    msg.setKey("ORDERID_100");

    // Send the message. If no exception is thrown, the send succeeded.
    try
    {
        SendResultONS sendResult = pProducer->send(msg);
    }
    catch(ONSClientException & e)
    {
        // Handle the exception: log the error or retry.
    }

    // Shut down the producer before exiting.
    // Failing to shut down may cause memory leaks.
    pProducer->shutdown();

    return 0;
}
Note

To start your instance from the console, open the ApsaraMQ for RocketMQ console, find the instance, click More in the Actions column, and select Quick Start.

To quickly send and receive messages, perform the following steps: Log on to the ApsaraMQ for RocketMQ console. Find the topic that you created and click More in the Actions column. From the drop-down list, select Quick Start. On the panel that appears, select Console or Docker.

Check whether messages are sent

After a message is sent, you can check its status in the ApsaraMQ for RocketMQ console by performing the following operations:

  1. On the Instance Details page, click Message Query in the left-side navigation pane.
  2. On the Message Query page, select a query method and specify the parameters as required, and then click Search.

    Stored At indicates the time when the ApsaraMQ for RocketMQ broker stores the message. If a message can be queried, the message has been sent to the Message Queue for Apache RocketMQ broker.

Important This step demonstrates the scenario where ApsaraMQ for RocketMQ is used for the first time and the consumer has not been started. Therefore, no consumption data is displayed in the console. To start the consumer and subscribe to messages, see the next section. For more information about the message status, see Message query and Query message traces.

Step 3: Verify message delivery

After sending a message, confirm delivery in the ApsaraMQ for RocketMQ console:

  1. Open the Instance Details page, then click Message Query in the left-side navigation pane.

  2. On the Message Query page, select a query method, specify the required parameters, and click Search.

If the message appears in the results, it has been delivered to the ApsaraMQ for RocketMQ broker. The Stored At column shows when the broker stored the message.

Note

The consumer has not started yet, so no consumption data appears. Proceed to the next step to start the consumer. For details about message status, see Query messages and Query message traces.

Step 4: Subscribe to normal messages

After verifying message delivery, start a consumer to subscribe to messages. Replace the placeholders with your actual values and run the sample code.

Java

The Java consumer subscribes to messages from one or more topics. Filter by specific tags with the "TagA||TagB" syntax, or use "*" to receive all tags.

import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.ConsumeContext;
import com.aliyun.openservices.ons.api.Consumer;
import com.aliyun.openservices.ons.api.Message;
import com.aliyun.openservices.ons.api.MessageListener;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;

import java.util.Properties;

public class ConsumerTest {
   public static void main(String[] args) {
       Properties properties = new Properties();
       // Group ID created in the ApsaraMQ for RocketMQ console.
       properties.put(PropertyKeyConst.GROUP_ID, "<YOUR_GROUP_ID>");
       // AccessKey ID for authentication.
       properties.put(PropertyKeyConst.AccessKey, "<YOUR_ACCESS_KEY>");
       // AccessKey secret for authentication.
       properties.put(PropertyKeyConst.SecretKey, "<YOUR_SECRET_KEY>");
       // TCP endpoint of your instance.
       properties.put(PropertyKeyConst.NAMESRV_ADDR, "<YOUR_TCP_ENDPOINT>");

       // Consumption mode (optional):
       // Clustering (default): each message is delivered to one consumer in the group.
       // properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.CLUSTERING);
       // Broadcasting: each message is delivered to every consumer in the group.
       // properties.put(PropertyKeyConst.MessageModel, PropertyValueConst.BROADCASTING);

       Consumer consumer = ONSFactory.createConsumer(properties);

       // Subscribe to specific tags on a topic.
       consumer.subscribe("<YOUR_TOPIC>", "TagA||TagB", new MessageListener() {
           public Action consume(Message message, ConsumeContext context) {
               System.out.println("Receive: " + message);
               return Action.CommitMessage;
           }
       });

       // Subscribe to another topic with all tags.
       // To unsubscribe, remove the subscription code and restart the consumer.
       consumer.subscribe("TopicTestMQ-Other", "*", new MessageListener() {
           public Action consume(Message message, ConsumeContext context) {
               System.out.println("Receive: " + message);
               return Action.CommitMessage;
           }
       });

       consumer.start();
       System.out.println("Consumer Started");
   }
}

Expected output:

Consumer Started
Receive: <message-details>

.NET

The .NET consumer uses a push consumer with a custom message listener.

using System;
using System.Threading;
using System.Text;
using ons;

// Message listener invoked when a message is pulled from the broker.
public class MyMsgListener : MessageListener
{
    public MyMsgListener()
    {
    }

    ~MyMsgListener()
    {
    }

    public override ons.Action consume(Message value, ConsumeContext context)
    {
        Byte[] text = Encoding.Default.GetBytes(value.getBody());
        Console.WriteLine(Encoding.UTF8.GetString(text));
        return ons.Action.CommitMessage;
    }
}

public class ConsumerExampleForEx
{
    public ConsumerExampleForEx()
    {
    }

    static void Main(string[] args) {
        ONSFactoryProperty factoryInfo = new ONSFactoryProperty();
        // AccessKey ID for authentication.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.AccessKey, "<YOUR_ACCESS_KEY>");
        // AccessKey secret for authentication.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.SecretKey, "<YOUR_SECRET_KEY>");
        // Group ID created in the ApsaraMQ for RocketMQ console.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.ConsumerId, "<YOUR_GROUP_ID>");
        // Topic created in the ApsaraMQ for RocketMQ console.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.PublishTopics, "<YOUR_TOPIC>");
        // TCP endpoint of your instance.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.NAMESRV_ADDR, "<YOUR_TCP_ENDPOINT>");
        // Log path.
        factoryInfo.setFactoryProperty(ONSFactoryProperty.LogPath, "C://log");

        // Consumption mode (optional):
        // Clustering (default): each message is delivered to one consumer in the group.
        // factoryInfo.setFactoryProperty(ONSFactoryProperty:: MessageModel, ONSFactoryProperty.CLUSTERING);
        // Broadcasting: each message is delivered to every consumer in the group.
        // factoryInfo.setFactoryProperty(ONSFactoryProperty:: MessageModel, ONSFactoryProperty.BROADCASTING);

        // Create the push consumer instance.
        PushConsumer consumer = ONSFactory.getInstance().createPushConsumer(factoryInfo);

        // Subscribe to the topic with all tags.
        consumer.subscribe(factoryInfo.getPublishTopics(), "*", new MyMsgListener());

        // Start the consumer.
        consumer.start();

        // Keep the process alive to receive messages. In production, do not exit.
        Thread.Sleep(300000);

        // Shut down the consumer before exiting.
        consumer.shutdown();
    }
}

C/C++

The C/C++ consumer uses a push consumer with a message listener callback.

#include "ONSFactory.h"
using namespace ons;

// Message listener that processes each received message.
class MyMsgListener : public MessageListener
{
    public:
        MyMsgListener()
        {
        }

        virtual ~MyMsgListener()
        {
        }

        virtual Action consume(Message &message, ConsumeContext &context)
        {
            // Add your message processing logic here.
            return CommitMessage;
        }
};


int main(int argc, char* argv[])
{
    // Configure the push consumer.
    ONSFactoryProperty factoryInfo;
    // Group ID created in the ApsaraMQ for RocketMQ console.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::ConsumerId, "<YOUR_GROUP_ID>");
    // TCP endpoint of your instance.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::NAMESRV_ADDR, "<YOUR_TCP_ENDPOINT>");
    // Topic created in the ApsaraMQ for RocketMQ console.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::PublishTopics, "<YOUR_TOPIC>");
    // AccessKey ID for authentication.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::AccessKey, "<YOUR_ACCESS_KEY>");
    // AccessKey secret for authentication.
    factoryInfo.setFactoryProperty(ONSFactoryProperty::SecretKey, "<YOUR_SECRET_KEY>");

    // Consumption mode (optional):
    // Clustering (default): each message is delivered to one consumer in the group.
    // factoryInfo.setFactoryProperty(ONSFactoryProperty:: MessageModel, ONSFactoryProperty::CLUSTERING);
    // Broadcasting: each message is delivered to every consumer in the group.
    // factoryInfo.setFactoryProperty(ONSFactoryProperty:: MessageModel, ONSFactoryProperty::BROADCASTING);

    // Create the push consumer.
    PushConsumer* pushConsumer = ONSFactory::getInstance()->createPushConsumer(factoryInfo);

    // Subscribe to the topic and register the message listener.
    MyMsgListener msglistener;
    pushConsumer->subscribe(factoryInfo.getPublishTopics(), "*", &msglistener);

    // Start the push consumer.
    pushConsumer->start();

    // Keep the process alive to receive messages.
    // Call shutdown() only when the consumer is idle (not processing messages).
    // Failing to shut down may cause memory leaks.
    pushConsumer->shutdown();
    return 0;
}

Step 5: Verify the subscription

After starting the consumer, check the subscription status:

  1. Open the Instance Details page of your instance, then click Groups in the left-side navigation pane.

  2. On the Groups page, click the TCP tab.

  3. Find your Group ID and click Details in the Actions column.

The subscription is working correctly when:

  • Consumer Status is Online.

  • Is Subscription Consistent is Yes.

Configuration reference

Producer parameters

Parameter

Description

Required

AccessKey

AccessKey ID for authentication

Yes

SecretKey

AccessKey secret for authentication

Yes

NAMESRV_ADDR

TCP endpoint of your instance

Yes

SendMsgTimeoutMillis

Send timeout in milliseconds (example value: 3000)

No

ProducerId

Group ID (.NET and C/C++ only)

Yes

PublishTopics

Topic name (.NET and C/C++ only)

Yes

LogPath

Log file path (.NET only)

No

Consumer parameters

Parameter

Description

Required

AccessKey

AccessKey ID for authentication

Yes

SecretKey

AccessKey secret for authentication

Yes

NAMESRV_ADDR

TCP endpoint of your instance

Yes

GROUP_ID / ConsumerId

Group ID (Java uses GROUP_ID; .NET and C/C++ use ConsumerId)

Yes

MessageModel

Consumption mode: CLUSTERING (default) or BROADCASTING

No

PublishTopics

Topic name (.NET and C/C++ only)

Yes

LogPath

Log file path (.NET only)

No

Message properties

Property

Description

Required

Topic

Topic the message belongs to

Yes

Tag

Label for filtering messages on the broker. Consumers filter by tag when subscribing.

No

Body

Message payload in binary format. Producer and consumer must use the same serialization and deserialization methods.

Yes

Key

Business-specific identifier, ideally globally unique. Helps query and resend failed messages in the console.

No

What's next