ActiveMQ CPP是一個提供消息庫,即API。通過使用其提供的接口,我們可以使用多種協議來與消息中間件進行通信。下面我將我在Linux下的安裝經驗分享給大家。首先,我給大家一個忠告:本教程僅供參考,不要直接照搬本教程或者其他教程!
如果可以閱讀E文的話,一定要認真閱讀軟件包中的README文件。
我可是有慘痛經歷的,在參考網上教程安裝之前一定要首先確認你所安裝的版本和上面是否一致,如果一致的話,那當然很好。如果不一致,那就需要注意了;另外,就算版本一致,也需要適當地留心,你需要注意每一步產生的輸出結果是否和教程中一致,或者明白每一步輸出結果到底再說什麼。
我這裏的教程針對ActiveMQ-CPP 3.9.4,其他版本可以參考。
你必須要明白ActiveMQ CPP Library是需要編譯安裝的,無論是Linux還是windows,所以安裝過程是非常耗時間的。如果你不想反覆經歷這個痛苦的過程,那麼就耐心點兒、細心點兒。
安裝依賴
爲了在Linux或者其他類Unix系統上編譯安裝ActiveMQ-CPP,我們需要安裝必要的依賴。
Tool | Recommended Version |
---|---|
autoconf | >= 2.61 |
automake | >= 1.10 |
libtool | >= 1.5.24 |
APR | >= 1.3* |
CPPUnit | >= 1.10.2* (推薦1.12.1) |
OpenSSL |>= 0.9.8m* (推薦1.0.0或更高版本,這是一個可選依賴) |
*表示其相應的開發包也需要安裝
對於autoconf、automake、libtool我們可以直接安裝,比如在ubuntu下,可以運行下面的命令:
sudo apt-get install autoconf
sudo apt-get install automake
sudo apt-get install libtool
如果你用的是其他的Linux的發行版本,可以採用其他的安裝方式。
安裝Cppunit
這個包中包含了一套完整的C++測試。爲了編譯和運行測試,你需要下載和安裝CPPUnit套件。 具體可以參考:http://cppunit.sourceforge.net/cppunit-wiki
在Fedora(一種Linux發行版本)上,可以運行下面的命令安裝:
sudo yum install cppunit cppunit-devel
在Debian/ubuntu(Linux發行版本)上,可以運行下面的命令安裝:
sudo apt-get install libcppunit-dev
安裝APR
編譯安裝ActiveMQ-CPP需要APR庫。這個庫可以從http://apr.apache.org/得到。下載之後,請參考其中的安裝文檔,即README文件。這個庫其實也需要編譯安裝的,這裏給出參考的安裝方法。
- 進入到解壓好的安裝包中
- 運行配置腳本
./configure
- 編譯
make
- 編譯結果檢查
make test
- 安裝
make install
安裝完之後,你可以看到結果中提示安裝的路徑,這個路徑你需要記下來,因爲後面我們會需要。如果你按照我上面的步驟的話,那麼它會被安裝在默認路徑下:
/usr/local/
安裝OpenSSL
這個軟件是可以選擇安裝的,如果你編程時需要用到SSL傳輸的話,你就需要安裝這個軟件了。官方推薦使用1.0.0或者更高版本的OpenSSL。具體安裝方法請參考OpenSSL的文檔。如果你不需要它的話,建議不要安裝,因爲筆者曾今不小心安裝了,結果後面就出現了問題。
編譯安裝ActiveMQ-CPP
在進行這一步之前,請確保前面的依賴全部安裝正確。
NOTE:對於ubuntu,建議安裝Build Essentials包:
sudo apt-get install build-essential
進入到解壓好的安裝包中
運行配置腳本
./configure
如果前面你不需要OpenSSL,你需要在該命令後添加--disable-ssl
選項。
- 當這個腳本運行成功之後,下面就可以編譯了:
make
- 編譯好之後就可以安裝了:
make install
單元測試
依次運行下面的命令(不用切換路徑),我們可以進行單元測試:
make check
./src/test/activemq-test
集成測試
運行下面命令即可完成集成測試:
./sr/test-integration/activemq-test-integration
編程測試
下面我們進行簡單的測試,我使用Qt Creator進行程序開發。下面是示例代碼:
/*
* 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.
*/
// START SNIPPET: demo
#include <activemq/library/ActiveMQCPP.h>
#include <decaf/lang/Thread.h>
#include <decaf/lang/Runnable.h>
#include <decaf/util/concurrent/CountDownLatch.h>
#include <decaf/lang/Integer.h>
#include <decaf/lang/Long.h>
#include <decaf/lang/System.h>
#include <activemq/core/ActiveMQConnectionFactory.h>
#include <activemq/util/Config.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::core;
using namespace decaf::util::concurrent;
using namespace decaf::util;
using namespace decaf::lang;
using namespace cms;
using namespace std;
class HelloWorldProducer : public Runnable {
private:
Connection* connection;
Session* session;
Destination* destination;
MessageProducer* producer;
int numMessages;
bool useTopic;
bool sessionTransacted;
std::string brokerURI;
private:
HelloWorldProducer(const HelloWorldProducer&);
HelloWorldProducer& operator=(const HelloWorldProducer&);
public:
HelloWorldProducer(const std::string& brokerURI, int numMessages, bool useTopic = false, bool sessionTransacted = false) :
connection(NULL),
session(NULL),
destination(NULL),
producer(NULL),
numMessages(numMessages),
useTopic(useTopic),
sessionTransacted(sessionTransacted),
brokerURI(brokerURI) {
}
virtual ~HelloWorldProducer(){
cleanup();
}
void close() {
this->cleanup();
}
virtual void run() {
try {
// Create a ConnectionFactory
auto_ptr<ConnectionFactory> connectionFactory(
ConnectionFactory::createCMSConnectionFactory(brokerURI));
// Create a Connection
connection = connectionFactory->createConnection();
connection->start();
// Create a Session
if (this->sessionTransacted) {
session = connection->createSession(Session::SESSION_TRANSACTED);
} else {
session = connection->createSession(Session::AUTO_ACKNOWLEDGE);
}
// Create the destination (Topic or Queue)
destination = session->createQueue("TEST.FOO");
// 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 (int ix = 0; ix < numMessages; ++ix) {
std::auto_ptr<TextMessage> message(session->createTextMessage(text));
message->setIntProperty("Integer", ix);
printf("Sent message #%d from thread %s\n", ix + 1, threadIdStr.c_str());
producer->send(message.get());
}
} catch (CMSException& e) {
e.printStackTrace();
}
}
private:
void cleanup() {
if (connection != NULL) {
try {
connection->close();
} catch (cms::CMSException& ex) {
ex.printStackTrace();
}
}
// Destroy resources.
try {
delete destination;
destination = NULL;
delete producer;
producer = NULL;
delete session;
session = NULL;
delete connection;
connection = NULL;
} catch (CMSException& e) {
e.printStackTrace();
}
}
};
class HelloWorldConsumer : public ExceptionListener,
public MessageListener,
public Runnable {
private:
CountDownLatch latch;
CountDownLatch doneLatch;
Connection* connection;
Session* session;
Destination* destination;
MessageConsumer* consumer;
long waitMillis;
bool useTopic;
bool sessionTransacted;
std::string brokerURI;
private:
HelloWorldConsumer(const HelloWorldConsumer&);
HelloWorldConsumer& operator=(const HelloWorldConsumer&);
public:
HelloWorldConsumer(const std::string& brokerURI, int numMessages, bool useTopic = false, bool sessionTransacted = false, int waitMillis = 30000) :
latch(1),
doneLatch(numMessages),
connection(NULL),
session(NULL),
destination(NULL),
consumer(NULL),
waitMillis(waitMillis),
useTopic(useTopic),
sessionTransacted(sessionTransacted),
brokerURI(brokerURI) {
}
virtual ~HelloWorldConsumer() {
cleanup();
}
void close() {
this->cleanup();
}
void waitUntilReady() {
latch.await();
}
virtual void run() {
try {
// Create a ConnectionFactory
auto_ptr<ConnectionFactory> connectionFactory(
ConnectionFactory::createCMSConnectionFactory(brokerURI));
// Create a Connection
connection = connectionFactory->createConnection();
connection->start();
connection->setExceptionListener(this);
// Create a Session
if (this->sessionTransacted == true) {
session = connection->createSession(Session::SESSION_TRANSACTED);
} else {
session = connection->createSession(Session::AUTO_ACKNOWLEDGE);
}
// Create the destination (Topic or Queue)
destination = session->createQueue("TEST.FOO");
// Create a MessageConsumer from the Session to the Topic or Queue
consumer = session->createConsumer(destination);
consumer->setMessageListener(this);
std::cout.flush();
std::cerr.flush();
// Indicate we are ready for messages.
latch.countDown();
// Wait while asynchronous messages come in.
doneLatch.await(waitMillis);
} catch (CMSException& e) {
// Indicate we are ready for messages.
latch.countDown();
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!";
}
printf("Message #%d Received: %s\n", count, text.c_str());
} catch (CMSException& e) {
e.printStackTrace();
}
// Commit all messages.
if (this->sessionTransacted) {
session->commit();
}
// No matter what, tag the count down latch until done.
doneLatch.countDown();
}
// 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");
ex.printStackTrace();
exit(1);
}
private:
void cleanup() {
if (connection != NULL) {
try {
connection->close();
} catch (cms::CMSException& ex) {
ex.printStackTrace();
}
}
// Destroy resources.
try {
delete destination;
destination = NULL;
delete consumer;
consumer = NULL;
delete session;
session = NULL;
delete connection;
connection = NULL;
} catch (CMSException& e) {
e.printStackTrace();
}
}
};
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 IP Address 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/
//
// Wire Format Options:
// =========================
// Use either stomp or openwire, the default ports are different for each
//
// Examples:
// tcp://127.0.0.1:61616 default to openwire
// tcp://127.0.0.1:61613?wireFormat=stomp use stomp instead
//
// SSL:
// =========================
// To use SSL you need to specify the location of the trusted Root CA or the
// certificate for the broker you want to connect to. Using the Root CA allows
// you to use failover with multiple servers all using certificates signed by
// the trusted root. If using client authentication you also need to specify
// the location of the client Certificate.
//
// System::setProperty( "decaf.net.ssl.keyStore", "<path>/client.pem" );
// System::setProperty( "decaf.net.ssl.keyStorePassword", "password" );
// System::setProperty( "decaf.net.ssl.trustStore", "<path>/rootCA.pem" );
//
// The you just specify the ssl transport in the URI, for example:
//
// ssl://localhost:61617
//
std::string brokerURI =
"failover:(tcp://localhost:61616)";
//============================================================
// set to true to use topics instead of queues
// Note in the code above that this causes createTopic or
// createQueue to be used in both consumer an producer.
//============================================================
bool useTopics = true;
bool sessionTransacted = false;
int numMessages = 2000;
long long startTime = System::currentTimeMillis();
HelloWorldProducer producer(brokerURI, numMessages, useTopics);
HelloWorldConsumer consumer(brokerURI, numMessages, useTopics, sessionTransacted);
// Start the consumer thread.
Thread consumerThread(&consumer);
consumerThread.start();
// Wait for the consumer to indicate that its ready to go.
consumer.waitUntilReady();
// Start the producer thread.
Thread producerThread(&producer);
producerThread.start();
// Wait for the threads to complete.
producerThread.join();
consumerThread.join();
long long endTime = System::currentTimeMillis();
double totalTime = (double)(endTime - startTime) / 1000.0;
consumer.close();
producer.close();
std::cout << "Time to completion = " << totalTime << " seconds." << std::endl;
std::cout << "-----------------------------------------------------\n";
std::cout << "Finished with the example." << std::endl;
std::cout << "=====================================================\n";
}
activemq::library::ActiveMQCPP::shutdownLibrary();
}
// END SNIPPET: demo
另外,qt的工程文件(.pro)中,我們需要添加相應的頭文件路徑和庫文件路徑,注意下面是筆者的安裝相應文件的路徑,僅供參考,,具體視情況而定:
# activemq-cpp-3.9.4的頭文件路徑和庫文件路徑
INCLUDEPATH += /usr/local/include/activemq-cpp-3.9.4/
INCLUDEPATH += /usr/apr/include/apr-1
# apr的頭文件路徑和庫文件路徑
LIBS += /usr/local/lib/libactivemq-cpp.a
LIBS += /usr/apr/lib/libapr-1.a
下面編譯該程序,如果所有都安裝正確並且配置正確的話,應該不會得到任何編譯錯誤。
如果編譯成功的話,那麼祝賀你!Enjoy!
參考資料
- activemq-cpp-library-3.9.4 README文件
- apr1.5.2 README文件
本作品採用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。