source: http://activemq.apache.org/cms/cms-api-overview.html
1. CMS是啥?
C++版本的API,用于收发消息(JMS)。
如果您已熟悉JMS,首先要开始的地方之一就是查看CMS API文档。
2. CMS入门
本节介绍使用CMS API的基础知识。 为了帮助讨论,我们假设您使用ActiveMQ-CPP连接到ActiveMQ Broker,当然使用CMS,您还可以链接到C ++应用程序中CMS API的另一个实现并连接到其他一些Message服务。
2. 1 CMS ConnectionFactory
您通常在CMS API中使用的第一个接口是ConnectionFactory。 ConnectionFactory允许您创建CMS连接对象,这些对象维护某些Messaging服务的连接,例如, 一个ActiveMQ broker。
获取CMS ConnectionFactory实例的最简单方法是使用所有CMS提供程序库都需要实现的静态方法createCMSConnectionFactory。 下面的代码片段演示了如何获取新的ConnectionFactory:
std::auto_ptr<cms::ConnectionFactory> connectionFactory( cms::ConnectionFactory::createCMSConnectionFactory( "tcp://127.0.0.1:61616" ) );
正如您所看到的,createCMSConnectionFactory采用单个字符串参数,该参数以URI的形式定义了创建的连接所在的位置以及应使用的协议,在上述情况下为TCP / IP 例。 另外,配置信息可以在URI中编码。 有关可通过URI传递给ActiveMQ-CPP的配置参数的更多信息,请参阅“配置”页面。
创建ConnectionFactory之后,接下来要做的是使用ConnectionFactory创建CMS连接。 Connection是管理客户端与Provider的连接的Object。 下一节将介绍CMS连接的使用,创建连接的代码如下所示:
std::auto_ptr<cms::Connection> connection( connectionFactory->createConnection() );
2.1.1 连接和身份验证
有几个版本的createConnection方法允许您为新创建的Connection指定登录参数。 您最常使用的那个通常会使用一个用户名和密码对,该对传输给Broker进行身份验证。 如果凭据无效,则会抛出CMSException。 下面的示例代码显示了在创建Connection对象时如何传递用户名和密码。
std::auto_ptr<cms::Connection> connection( connectionFactory->createConnection( "<USERNAME>", "<PASSWORD>") );
如果您不想将值硬编码到源代码中或编写代码以从其他地方读取登录数据,则可以使用另一个传递用户名和密码的选项,传递给createConnectionFactory的URI可以编码为 connectionFactory在解析URI时将从系统环境中读取值。 以下示例显示如何使用URI中的登录数据集创建连接工厂。
std::auto_ptr<cms::ConnectionFactory> connectionFactory( cms::ConnectionFactory::createCMSConnectionFactory( "tcp://127.0.0.1:61616?username=${USERNAME}&password=${PASSWORD}" ) );
2.2 CMS Connection
CMS连接接口定义一个对象,该对象是客户端与CMS提供程序的活动连接。 在大多数情况下,客户端只会创建一个连接对象,因为它被视为重量级对象。
Connection有多种用途:
- 它封装了与JMS提供程序的开放连接。 它通常表示客户端和提供者服务守护程序之间的开放TCP / IP套接字。
- 它的创建是进行客户端身份验证的地方。
- 它可以指定唯一的客户端标识符。
- 它提供了一个ConnectionMetaData对象。
- 它支持可选的ExceptionListener对象。
如前所述,CMS连接是从CMS ConnectionFactory创建的。 如果ConnectionFactory创建调用成功,则返回的Connection对象将连接到CMS提供程序。 Connection对象是在停止状态下创建的,在Connection启动之前,不会将消息传递给客户端创建的Message使用者。 将Connection保持在已停止状态是正常的,直到客户端创建了它打算使用的初始Sessions,Message Producers和Message Consumers集合。 一旦客户端的设置阶段完成,它应该调用Connection的start方法开始接收来自Provider的消息。 未能调用start方法是CMS和JMS客户端的新用户之间的一个非常常见的错误,如果您发现没有收到任何消息,首先要检查的是您调用start。
创建连接后,客户端必须创建CMS会话以创建消息生成者和使用者。 下面的代码片段汇总了我们到目前为止所看到的内容,然后展示了如何从Connection实例创建CMS Session对象,下面的部分将更详细地讨论CMS Session。
std::auto_ptr<cms::ConnectionFactory> connectionFactory( cms::ConnectionFactory::createCMSConnectionFactory( "tcp://127.0.0.1:61616" ) ); std::auto_ptr<cms::Connection> connection( connectionFactory->createConnection() ); std::auto_ptr<cms::Session> session( connection->createSession() );
2.3 CMS Session
成功创建CMS连接之后,您将要做的下一件事是使用新的Connection实例创建一个或多个CMS Session对象。 Session被定义为用于生成和使用消息的单线程上下文。
Session有多种用途:
- 它是消息生产者和消费者的工厂。
- 它提供提供商优化的消息工厂。
- 它是TemporaryTopics和TemporaryQueues的工厂。
- 它为需要动态操作特定于提供程序的目标名称的客户端提供了创建Queue或Topic对象的方法。
- 它支持一系列事务,这些事务将跨越生产者和消费者的工作结合到原子单元中。
- 它定义了它消耗的消息的序列顺序以及它产生的消息。
- 它会保留消息,直到它们被确认为止。
- 它序列化了使用其消息使用者注册的消息侦听器的执行。
- 它是QueueBrowsers的工厂*(尚未实施)*。
会话可以创建和服务多个消息生成者和消费者。
当客户端创建CMS会话时,它必须指定会话将确认其接收和分派的消息的应答模式。 支持的模式总结在下表中。
应答模式 | 描述 |
---|---|
AUTO_ACKNOWLEDGE | 使用此确认模式,会话自动确认客户端收到消息时,或者当会话已成功从接收调用返回时,或者会话已调用以处理消息成功返回的消息侦听器。 |
CLIENT_ACKNOWLEDGE | 使用此确认模式,客户端通过调用消息的确认方法来确认消耗的消息。 确认已消耗的消息会确认会话已消耗的所有消息。 当使用客户端确认模式时,客户端可能在尝试处理它们时构建大量未确认的消息。 CMS提供商应该为管理员提供一种限制客户端溢出的方法,以便客户端不会被资源耗尽,并且当他们正在使用的某些资源被暂时阻止时,会导致失败。 |
DUPS_OK_ACKNOWLEDGE | 此确认模式指示会话懒惰地确认消息的传递。 如果JMS提供程序失败,这可能会导致传递一些重复消息,因此只应由能够容忍重复消息的使用者使用。 使用此模式可以通过最小化会话所做的工作来减少会话开销,从而防止重复。 |
SESSION_TRANSACTED | 会话已处理,消息的确认在内部处理。 |
INDIVIDUAL_ACKNOWLEDGE | 确认仅适用于单个消息。 与CLIENT_ACKNOWLEDGE不同,其中确认适用于在整个会话期间收到的所有消息,此模式仅应用于单个消息,允许客户端更加选择性地确认哪些消息。 |
在上一节中,我们向您展示了如何创建会话,让我们再次在这里看一下这个例子(上面的代码段),在此代码段中,使用不带参数的createSession创建Session,这将创建一个处于AUTO_ACKNOWLEDGE模式的Session。 要使用上面列出的模式之一创建会话,CMS会话界面中有第二个创建方法,该方法采用指定模式的单个参数,让我们看一个示例:
std::auto_ptr<cms::ConnectionFactory> connectionFactory( cms::ConnectionFactory::createCMSConnectionFactory( "tcp://127.0.0.1:61616" ) ); std::auto_ptr<cms::Connection> connection( connectionFactory->createConnection() ); std::auto_ptr<cms::Session> session( connection->createSession( cms::Session::CLIENT_ACKNOWLEDGE ) );
2.4 从CMS Session创建的对象
在本节中,我们将介绍从CMS Session对象的实例创建的对象类型。
2.4.1 CMS Messages
顾名思义,CMS存在发送和接收消息,因此从覆盖可以使用CMS发送和接收的消息开始是有意义的。 在撰写本文时,CMS支持四种主要的消息类型,其他可能会遵循,但我们将坚持现在完全支持的消息类型。 下表显示了消息类型以及消息使用的简要说明,有关特定消息类型的界面和用法的完整详细信息,请参阅CMS API文档。
Message 类型 | 描述 |
---|---|
Message | 该接口定义了最简单的CMS Message. 它没有Body或者消息内容,但是可以通过setter来添加属性。它是其他消息类型的基类。 |
TextMessage | TextMessage类携带由C++字符串组成的消息内容。 TextMessage接口扩展Message接口,添加用于设置和获取有效内容文本的方法,并保留对设置Message属性的支持。 由于Java对象无法直接从JMS客户端发送到CMS客户端,因此Text消息是将对象序列化为XML并将其发送到JMS客户端的理想方式。 |
BytesMessage | BytesMessage消息内容由一系列不间断的字节组成,接收器负责解释这些字节。 BytesMessage添加了用于获取和设置字节数组到Message接口方法的标准集的方法。 |
MapMessage | Map消息内容是一组名称/值对。 Name是C++字符串类型,值是C++原始类型或字符串。 |
StreamMessage | Stream Message主体包含一个自描述基元类型的列表。 StreamMessage接口提供了可以向/从消息中读取和写入基元类型的访问器方法。 在不会损失数据的时候,read方法允许数据类型转换。 |
现在我们已经看到了我们可以创建的Message类型,让我们看看如何实际创建它们并探索Message类中可用的一些操作。
2.4.2 创建Message
您可能已经猜到,使用我们之前创建的CMS Session实例创建消息。 会话提供了创建上述四种消息类型的方法。 Session是工厂,它创建CMS中定义的Message接口的提供程序实现,它知道如何配置内部数据结构并防止客户端直接绑定到提供程序实现,这就是为什么我们必须使用Session来创建一个Message对象而不是直接创建它们。 让我们看一下创建TextMessage实例的代码片段,并在该Message上设置一些属性。
// Create the ConnectionFactory std::auto_ptr<cms::ConnectionFactory> connectionFactory( cms::ConnectionFactory::createCMSConnectionFactory( "tcp://127.0.0.1:61616" ) ); // Create a Connection std::auto_ptr<cms::Connection> connection( connectionFactory->createConnection() ); // Create a new Session from our Connection std::auto_ptr<cms::Session> session( connection->createSession(); // Now create a TextMessage std::auto_ptr<cms::TextMessage> textMessage( session->createTextMessage() ); // Set the payload textMessage->setText( "Payload Text" ); // Set some Properties textMessage->setStringProperty( "USER_NAME", "Steve" ); textMessage->setIntProperty( "USER_CODE", 42 );
从上面的代码中可以看出,创建TextMessage就像创建Session或Connection实例一样,只需在CMS会话实例中调用createTextMessage,然后返回一个新的TextMessage指针,然后可以使用文本和属性填充该指针。
2.4.3 CMS Destinations
顾名思义,CMS Destination接口定义了一个对象,该对象表示消息由Messaging代理路由到的端点。 客户端创建目标并向其发送消息或等待在他们订阅的目标上接收消息。 CMS中有两种基本类型的目标,即主题和队列,其中有两种子类型,即临时主题和临时队列。 下表总结了四种不同的Destination类型。
Destination 类型 | 描述 |
---|---|
Topic | 在CMS中,主题实现了发布和订阅语义。 当您发布消息时,它会发送给所有感兴趣的订阅者 - 因此零到多订阅者将收到该消息的副本。 只有在broker收到消息时才有活动订阅的订阅者才能获得该消息的副本。 |
Queue | CMS队列实现负载均衡器语义。 一条消息只有一个消费者可以收到。 如果在发送消息时没有可用的消费者,则将保留消息,直到有消费者可以处理消息为止。 如果消费者收到消息并且在关闭之前没有确认消息,则该消息将被重新传送给另一个消费者。 队列可以让许多消费者在可用的消费者之间对消息进行负载平衡。 |
TemporaryTopic | TemporaryTopic对象是在Connection的持续时间内创建的唯一Topic对象。 它是系统定义的主题,只能由创建它的Connection使用。 |
TemporaryQueue | TemporaryQueue对象是在Connection持续时间内创建的唯一Queue对象。 它是系统定义的队列,只能由创建它的Connection使用。 |
现在我们已经看到目标类型是什么,让我们看一下代码片段,展示如何创建Destination对象。 下面的示例显示了如何使用现在应该非常熟悉的模式创建Topic实例。
// Create the ConnectionFactory std::auto_ptr<cms::ConnectionFactory> connectionFactory( cms::ConnectionFactory::createCMSConnectionFactory( "tcp://127.0.0.1:61616" ) ); // Create a Connection std::auto_ptr<cms::Connection> connection( connectionFactory->createConnection() ); // Create a new Session from our Connection std::auto_ptr<cms::Session> session( connection->createSession(); // Now create a Topic std::auto_ptr<cms::Topic> myTopic( session->createTopic( "EXAMPLE-TOPIC" ) );
创建topic或queue需要传递Destination的名称,名称类似于地址,发送到“EXAMPLE-TOPIC”目的地的消息由订阅了相同Destination的客户端接收。
2.4.4 CMS MessageConsumer
现在我们已经介绍了如何创建消息和目标,我们将介绍如何创建CMS MessageConsumer。 MessageConsumer允许客户端应用程序接收其他客户端发送到topic或queue的消息。 在我们讨论与消费者接收消息之前,先看看如何创建MessageConsumer。
// Create the ConnectionFactory std::auto_ptr<cms::ConnectionFactory> connectionFactory( cms::ConnectionFactory::createCMSConnectionFactory( "tcp://127.0.0.1:61616" ) ); // Create a Connection std::auto_ptr<cms::Connection> connection( connectionFactory->createConnection() ); // Create a new Session from our Connection std::auto_ptr<cms::Session> session( connection->createSession(); // Now create a Topic std::auto_ptr<cms::Topic> myTopic( session->createTopic( "EXAMPLE-TOPIC" ) ); // Now create the Consumer std::auto_ptr<cms::MessageConsumer> myConsumer( session->createConsumer( myTopic ) );
如您所见,通过从CMS会话对象的实例调用createConsumer来创建MessageConsumer。 MessageConsumer被赋予一个Destination,用于在创建Message时监听Messages。 一旦你创建MessageConsumer它开始使用它来接收消息的时间,MessageConsumer接收消息的两种方法,一种是同步的,另一种是异步的。
接收消息的同步方法涉及对消费者接收方法的调用。 如果没有给出超时,则接收调用将阻塞,直到在有问题的目的地上收到消息,或者如果超时给定并且没有新消息到达,则将返回NULL。 让我们看一下使用CMS的同步消息轮询循环的简单示例。
//无限时间等待同步轮寻 while( !done ) { std::auto_ptr<Message> message( myConsumer->receive() ); //...Do Something with the message... }
正如您在上面的代码中所看到的,我们调用了MessageConsumer的receive方法,期望它会在某个时刻返回一个新的Message对象。 此处的代码将阻止,直到收到新消息。 这种方法假设您的程序在消息到达之前没有其他任何事情需要做,但是如果您的应用程序需要执行其他处理,则可以选择其他方法。 下面的代码示例显示了一个轮询循环,它使用MessageConsumer的receiveNoWait方法进行轮询并立即返回以允许进行其他处理。
//Synchronous Polling with no wait. while( !done ) { std::auto_ptr<Message> message( myConsumer->receiveNoWait() ); if( message.get() != NULL ) { //...Do Something with the message... } //...Perform other application logic before checking for another message... }
异步方法涉及实现CMS MessageListener接口并将实现的实例传递给MessageConsumer的setMessageListener方法。 当新消息到达时,消费者将在另一个线程的上下文中调用侦听器的onMessage方法,以允许您处理收到的消息。 下面是一个代码片段,演示了如何实现MessageListener接口。
//Simple MessageListener Implementation. class SimpleListener : public cms::MessageListener { virtual void onMessage( const Message* message ) { const TextMessage* textMessage = dynamic_cast< const TextMessage* >( message ); string text = ""; if( textMessage != NULL ) { text = textMessage->getText(); } else { text = "NOT A TEXTMESSAGE!"; } printf( "Message Received: %s\n", text.c_str() ); } };
在上面的示例中,我们创建了一个名为SimpleListener的新类,它在接收时打印TextMessage的内容,或者打印一条消息,指示它没有像预期的那样接收TextMessage。 请注意,onMessage方法接收指向基本Message接口的指针,然后我们尝试动态转换为我们认为应该接收的类型。 这允许您的代码在一个方法中处理多种消息类型。 传递的指针由调用者或onMessage拥有,因此您不应该存储它或将其删除,如果您需要保留Message的副本,则必须通过调用Message的clone方法创建副本。
现在我们有了一个MessageListener实现来处理它。之前使用我们创建的MessageConsumer来设置异步Comsumer。
//Setting up Async Consumption. SimpleListener listener; myConsumer->setMessageListener( &listener );
就是这样,我们现在将收到发送到我们在SimpleListener实例的onMessage方法中创建的Destination的消息。
2.4.5 CMS MessageProducer
我们已经看到了如何消费消息。现在我们如何生产它们? 答案是CMS MessageProducer,它用于向代理发送消息,以便分发给侦听主题或队列上的消息的各个客户端。 创建MessageProducer与创建MessageConsumer非常相似,首先创建Connection,Session和Destination对象,然后使用Session创建MessageProducer。 下面的代码片段演示了如何创建MessageProducer。
//Creating a MessageProducer from a CMS Session object // Create the ConnectionFactory std::auto_ptr<cms::ConnectionFactory> connectionFactory( cms::ConnectionFactory::createCMSConnectionFactory( "tcp://127.0.0.1:61616" ) ); // Create a Connection std::auto_ptr<cms::Connection> connection( connectionFactory->createConnection() ); // Create a new Session from our Connection std::auto_ptr<cms::Session> session( connection->createSession(); // Now create a Topic std::auto_ptr<cms::Topic> myTopic( session->createTopic( "EXAMPLE-TOPIC" ) ); // Now create the Consumer std::auto_ptr<cms::MessageProducer> myProducer( session->createProducer( myTopic ) );
2.5 完整的例子
现在我们已经涵盖了CMS API的大部分基础知识。 是时候看一些完整的例子来演示如何在自己的应用程序中使用CMS API。 第一个示例将展示如何创建一个可以从ActiveMQ代理接收TextMessage对象的简单异步使用者,然后在第二个示例中,我们将看一个简单的生成器,它将TextMessage对象发布到我们的消费者正在侦听的目标。
2.5.1 简单异步Consumer
在本示例中,我们将CMS API用法包装在名为SimpleAsyncConsumer的类中。 此类公开一个构造函数,该构造函数允许用户创建连接到特定代理和目标的类的实例,以及目标是queue还是topic。 用户还可以指定默认AUTO_ACKNOWLEDGE模式的CLIENT_ACKNOWLEDGE的确认模式。
一旦创建了此类的实例,用户就会调用runConsumer方法来开始侦听指定的目标。 runConsumer方法创建与代理的连接,并启动使用已配置的确认模式配置的新会话。 创建会话后,可以创建新的Consumer并将其附加到配置的Destination。 由于我们希望异步侦听新消息,因此SimpleAsyncConsumer继承自cms :: MessageListener,因此它可以将自身注册为带有在runConsumer中创建的MessageConsumer的消息侦听器。
在runConsumer方法返回main方法等待用户输入退出之后,在应用程序运行时收到的所有消息将被分派到SimpleAsyncConsumer的onMessage方法,如果消息是TextMessage,则其内容将被打印在屏幕上。
/* * 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 <decaf/lang/Thread.h> #include <decaf/lang/Runnable.h> #include <decaf/util/concurrent/CountDownLatch.h> #include <activemq/core/ActiveMQConnectionFactory.h> #include <activemq/core/ActiveMQConnection.h> #include <activemq/transport/DefaultTransportListener.h> #include <activemq/library/ActiveMQCPP.h> #include <decaf/lang/Integer.h> #include <activemq/util/Config.h> #include <decaf/util/Date.h> #include <cms/Connection.h> #include <cms/Session.h> #include <cms/TextMessage.h> #include <cms/BytesMessage.h> #include <cms/MapMessage.h> #include <cms/ExceptionListener.h> #include <cms/MessageListener.h> #include <stdlib.h> #include <stdio.h> #include <iostream> using namespace activemq; using namespace activemq::core; using namespace activemq::transport; using namespace decaf::lang; using namespace decaf::util; using namespace decaf::util::concurrent; using namespace cms; using namespace std; //// class SimpleAsyncConsumer : public ExceptionListener, public MessageListener, public DefaultTransportListener { private: Connection* connection; Session* session; Destination* destination; MessageConsumer* consumer; bool useTopic; std::string brokerURI; std::string destURI; bool clientAck; private: SimpleAsyncConsumer( const SimpleAsyncConsumer& ); SimpleAsyncConsumer& operator= ( const SimpleAsyncConsumer& ); public: SimpleAsyncConsumer( const std::string& brokerURI, const std::string& destURI, bool useTopic = false, bool clientAck = false ) : connection(NULL), session(NULL), destination(NULL), consumer(NULL), useTopic(useTopic), brokerURI(brokerURI), destURI(destURI), clientAck(clientAck) { } virtual ~SimpleAsyncConsumer() { this->cleanup(); } void close() { this->cleanup(); } void runConsumer() { try { // Create a ConnectionFactory ActiveMQConnectionFactory* connectionFactory = new ActiveMQConnectionFactory( brokerURI ); // Create a Connection connection = connectionFactory->createConnection(); delete connectionFactory; ActiveMQConnection* amqConnection = dynamic_cast<ActiveMQConnection*>( connection ); if( amqConnection != NULL ) { amqConnection->addTransportListener( this ); } connection->start(); connection->setExceptionListener(this); // Create a Session if( clientAck ) { session = connection->createSession( Session::CLIENT_ACKNOWLEDGE ); } else { session = connection->createSession( Session::AUTO_ACKNOWLEDGE ); } // Create the destination (Topic or Queue) if( useTopic ) { destination = session->createTopic( destURI ); } else { destination = session->createQueue( destURI ); } // Create a MessageConsumer from the Session to the Topic or Queue consumer = session->createConsumer( destination ); consumer->setMessageListener( this ); } catch (CMSException& e) { e.printStackTrace(); } } // Called from the consumer since this class is a registered MessageListener. virtual void onMessage( const Message* message ) { static int count = 0; try { count++; const TextMessage* textMessage = dynamic_cast< const TextMessage* >( message ); string text = ""; if( textMessage != NULL ) { text = textMessage->getText(); } else { text = "NOT A TEXTMESSAGE!"; } if( clientAck ) { message->acknowledge(); } printf( "Message #%d Received: %s\n", count, text.c_str() ); } catch (CMSException& e) { e.printStackTrace(); } } // If something bad happens you see it here as this class is also been // registered as an ExceptionListener with the connection. virtual void onException( const CMSException& ex AMQCPP_UNUSED ) { printf("CMS Exception occurred. Shutting down client.\n"); exit(1); } virtual void transportInterrupted() { std::cout << "The Connection's Transport has been Interrupted." << std::endl; } virtual void transportResumed() { std::cout << "The Connection's Transport has been Restored." << std::endl; } private: void cleanup(){ try { if( connection != NULL ) { connection->close(); } } catch ( CMSException& e ) { e.printStackTrace(); } delete destination; delete consumer; delete session; delete connection; } }; //// int main(int argc AMQCPP_UNUSED, char* argv[] AMQCPP_UNUSED) { activemq::library::ActiveMQCPP::initializeLibrary(); std::cout << "=====================================================\n"; std::cout << "Starting the example:" << std::endl; std::cout << "-----------------------------------------------------\n"; // Set the URI to point to the IPAddress of your broker. // add any optional params to the url to enable things like // tightMarshalling or tcp logging etc. See the CMS web site for // a full list of configuration options. // // http://activemq.apache.org/cms/ // std::string brokerURI = "failover:(tcp://127.0.0.1:61616)"; //============================================================ // This is the Destination Name and URI options. Use this to // customize where the consumer listens, to have the consumer // use a topic or queue set the 'useTopics' flag. //============================================================ std::string destURI = "TEST.FOO"; //?consumer.prefetchSize=1"; //============================================================ // set to true to use topics instead of queues // Note in the code above that this causes createTopic or // createQueue to be used in the consumer. //============================================================ bool useTopics = false; //============================================================ // set to true if you want the consumer to use client ack mode // instead of the default auto ack mode. //============================================================ bool clientAck = false; // Create the consumer SimpleAsyncConsumer consumer( brokerURI, destURI, useTopics, clientAck ); // Start it up and it will listen forever. consumer.runConsumer(); // Wait to exit. std::cout << "Press 'q' to quit" << std::endl; while( std::cin.get() != 'q') {} // All CMS resources should be closed before the library is shutdown. consumer.close(); std::cout << "-----------------------------------------------------\n"; std::cout << "Finished with the example." << std::endl; std::cout << "=====================================================\n"; activemq::library::ActiveMQCPP::shutdownLibrary(); }
2.5.2 Simple Producer
与Simple Asynchronous Consumer示例非常相似,Simple Producer示例将生成生成器所需的CMS API detials包装到名为SimpleProducer的类中。 此类公开了与使用者示例类似的接口,有一个构造函数允许创建实例,其中包含代理和目标的配置选项以及要发送到配置目标的消息数。 创建客户端代码后,只需调用SimpleProducer的run方法即可发布指定数量的消息。 一旦run方法完成,客户端就可以自由关闭SimpleProducer,它可以清理已分配的CMS资源,一旦关闭,应用程序就会退出。
/* * 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 <decaf/lang/Thread.h> #include <decaf/lang/Runnable.h> #include <decaf/util/concurrent/CountDownLatch.h> #include <decaf/lang/Long.h> #include <decaf/util/Date.h> #include <activemq/core/ActiveMQConnectionFactory.h> #include <activemq/util/Config.h> #include <activemq/library/ActiveMQCPP.h> #include <cms/Connection.h> #include <cms/Session.h> #include <cms/TextMessage.h> #include <cms/BytesMessage.h> #include <cms/MapMessage.h> #include <cms/ExceptionListener.h> #include <cms/MessageListener.h> #include <stdlib.h> #include <stdio.h> #include <iostream> #include <memory> using namespace activemq; using namespace activemq::core; using namespace decaf; using namespace decaf::lang; using namespace decaf::util; using namespace decaf::util::concurrent; using namespace cms; using namespace std; //// class SimpleProducer : public Runnable { private: Connection* connection; Session* session; Destination* destination; MessageProducer* producer; bool useTopic; bool clientAck; unsigned int numMessages; std::string brokerURI; std::string destURI; private: SimpleProducer( const SimpleProducer& ); SimpleProducer& operator= ( const SimpleProducer& ); public: SimpleProducer( const std::string& brokerURI, unsigned int numMessages, const std::string& destURI, bool useTopic = false, bool clientAck = false ) : connection(NULL), session(NULL), destination(NULL), producer(NULL), useTopic(useTopic), clientAck(clientAck), numMessages(numMessages), brokerURI(brokerURI), destURI(destURI) { } virtual ~SimpleProducer(){ cleanup(); } void close() { this->cleanup(); } virtual void run() { try { // Create a ConnectionFactory auto_ptr<ActiveMQConnectionFactory> connectionFactory( new ActiveMQConnectionFactory( brokerURI ) ); // Create a Connection try{ connection = connectionFactory->createConnection(); connection->start(); } catch( CMSException& e ) { e.printStackTrace(); throw e; } // Create a Session if( clientAck ) { session = connection->createSession( Session::CLIENT_ACKNOWLEDGE ); } else { session = connection->createSession( Session::AUTO_ACKNOWLEDGE ); } // Create the destination (Topic or Queue) if( useTopic ) { destination = session->createTopic( destURI ); } else { destination = session->createQueue( destURI ); } // Create a MessageProducer from the Session to the Topic or Queue producer = session->createProducer( destination ); producer->setDeliveryMode( DeliveryMode::NON_PERSISTENT ); // Create the Thread Id String string threadIdStr = Long::toString( Thread::currentThread()->getId() ); // Create a messages string text = (string)"Hello world! from thread " + threadIdStr; for( unsigned int ix=0; ix<numMessages; ++ix ){ TextMessage* message = session->createTextMessage( text ); message->setIntProperty( "Integer", ix ); // Tell the producer to send the message printf( "Sent message #%d from thread %s\n", ix+1, threadIdStr.c_str() ); producer->send( message ); delete message; } }catch ( CMSException& e ) { e.printStackTrace(); } } private: void cleanup(){ try { if( connection != NULL ) { connection->close(); } } catch ( CMSException& e ) { e.printStackTrace(); } delete destination; delete producer; delete session; delete connection; } }; //// int main(int argc AMQCPP_UNUSED, char* argv[] AMQCPP_UNUSED) { activemq::library::ActiveMQCPP::initializeLibrary(); std::cout << "=====================================================\n"; std::cout << "Starting the example:" << std::endl; std::cout << "-----------------------------------------------------\n"; // Set the URI to point to the IPAddress of your broker. // add any optional params to the url to enable things like // tightMarshalling or tcp logging etc. See the CMS web site for // a full list of configuration options. // // http://activemq.apache.org/cms/ // std::string brokerURI = "failover://(tcp://127.0.0.1:61616)"; //============================================================ // Total number of messages for this producer to send. //============================================================ unsigned int numMessages = 2000; //============================================================ // This is the Destination Name and URI options. Use this to // customize where the Producer produces, to have the producer // use a topic or queue set the 'useTopics' flag. //============================================================ std::string destURI = "TEST.FOO"; //============================================================ // set to true to use topics instead of queues // Note in the code above that this causes createTopic or // createQueue to be used in the producer. //============================================================ bool useTopics = false; // Create the producer and run it. SimpleProducer producer( brokerURI, numMessages, destURI, useTopics ); // Publish the given number of Messages producer.run(); // Before exiting we ensure that all CMS resources are closed. producer.close(); std::cout << "-----------------------------------------------------\n"; std::cout << "Finished with the example." << std::endl; std::cout << "=====================================================\n"; activemq::library::ActiveMQCPP::shutdownLibrary(); }