blob: b95abd9d66bf61d44876df4525edfb6b60d7848d [file] [log] [blame]
/*
*
* 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.
*
*/
/**
* This file provides one half of a test and example of a pub-sub
* style of interaction. See topic_listener.cpp for the other half, in
* which the logic for subscribers is defined.
*
* This file contains the publisher logic. The publisher will send a
* number of messages to the exchange with the appropriate routing key
* for the logical 'topic'. Once it has done this it will then send a
* request that each subscriber report back with the number of message
* it has received and the time that elapsed between receiving the
* first one and receiving the report request. Once the expected
* number of reports are received, it sends out a request that each
* subscriber shutdown.
*/
#include <QpidError.h>
#include <ClientChannel.h>
#include <Connection.h>
#include <ClientExchange.h>
#include <MessageListener.h>
#include <ClientQueue.h>
#include <sys/Monitor.h>
#include "unistd.h"
#include <sys/Time.h>
#include <cstdlib>
#include <iostream>
using namespace qpid::client;
using namespace qpid::sys;
using std::string;
/**
* The publishing logic is defined in this class. It implements
* message listener and can therfore be used to receive messages sent
* back by the subscribers.
*/
class Publisher : public MessageListener{
Channel* const channel;
const std::string controlTopic;
const bool transactional;
Monitor monitor;
int count;
void waitForCompletion(int msgs);
string generateData(int size);
public:
Publisher(Channel* channel, const std::string& controlTopic, bool tx);
virtual void received(Message& msg);
int64_t publish(int msgs, int listeners, int size);
void terminate();
};
/**
* A utility class for managing the options passed in to the test
*/
class Args{
string host;
int port;
int messages;
int subscribers;
int ackMode;
bool transactional;
int prefetch;
int batches;
int delay;
int size;
bool trace;
bool help;
public:
inline Args() : host("localhost"), port(5672), messages(1000), subscribers(1),
ackMode(NO_ACK), transactional(false), prefetch(1000), batches(1),
delay(0), size(256), trace(false), help(false){}
void parse(int argc, char** argv);
void usage();
inline const string& getHost() const { return host;}
inline int getPort() const { return port; }
inline int getMessages() const { return messages; }
inline int getSubscribers() const { return subscribers; }
inline int getAckMode(){ return ackMode; }
inline bool getTransactional() const { return transactional; }
inline int getPrefetch(){ return prefetch; }
inline int getBatches(){ return batches; }
inline int getDelay(){ return delay; }
inline int getSize(){ return size; }
inline bool getTrace() const { return trace; }
inline bool getHelp() const { return help; }
};
int main(int argc, char** argv){
Args args;
args.parse(argc, argv);
if(args.getHelp()){
args.usage();
}else{
try{
Connection connection(args.getTrace());
connection.open(args.getHost(), args.getPort());
Channel channel(args.getTransactional(), args.getPrefetch());
connection.openChannel(&channel);
//declare queue (relying on default binding):
Queue response("response");
channel.declareQueue(response);
//set up listener
Publisher publisher(&channel, "topic_control", args.getTransactional());
std::string tag("mytag");
channel.consume(response, tag, &publisher, args.getAckMode());
channel.start();
int batchSize(args.getBatches());
int64_t max(0);
int64_t min(0);
int64_t sum(0);
for(int i = 0; i < batchSize; i++){
if(i > 0 && args.getDelay()) sleep(args.getDelay());
Time time = publisher.publish(
args.getMessages(), args.getSubscribers(), args.getSize());
if(!max || time > max) max = time;
if(!min || time < min) min = time;
sum += time;
std::cout << "Completed " << (i+1) << " of " << batchSize
<< " in " << time/TIME_MSEC << "ms" << std::endl;
}
publisher.terminate();
int64_t avg = sum / batchSize;
if(batchSize > 1){
std::cout << batchSize << " batches completed. avg=" << avg <<
", max=" << max << ", min=" << min << std::endl;
}
channel.close();
connection.close();
}catch(qpid::QpidError error){
std::cout << error.what() << std::endl;
}
}
}
Publisher::Publisher(Channel* _channel, const std::string& _controlTopic, bool tx) :
channel(_channel), controlTopic(_controlTopic), transactional(tx){}
void Publisher::received(Message& ){
//count responses and when all are received end the current batch
Monitor::ScopedLock l(monitor);
if(--count == 0){
monitor.notify();
}
}
void Publisher::waitForCompletion(int msgs){
count = msgs;
monitor.wait();
}
int64_t Publisher::publish(int msgs, int listeners, int size){
Message msg;
msg.setData(generateData(size));
Time start = now();
{
Monitor::ScopedLock l(monitor);
for(int i = 0; i < msgs; i++){
channel->publish(msg, Exchange::STANDARD_TOPIC_EXCHANGE, controlTopic);
}
//send report request
Message reportRequest;
reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST");
channel->publish(reportRequest, Exchange::STANDARD_TOPIC_EXCHANGE, controlTopic);
if(transactional){
channel->commit();
}
waitForCompletion(listeners);
}
Time finish = now();
return finish - start;
}
string Publisher::generateData(int size){
string data;
for(int i = 0; i < size; i++){
data += ('A' + (i / 26));
}
return data;
}
void Publisher::terminate(){
//send termination request
Message terminationRequest;
terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST");
channel->publish(terminationRequest, Exchange::STANDARD_TOPIC_EXCHANGE, controlTopic);
if(transactional){
channel->commit();
}
}
void Args::parse(int argc, char** argv){
for(int i = 1; i < argc; i++){
string name(argv[i]);
if("-help" == name){
help = true;
break;
}else if("-host" == name){
host = argv[++i];
}else if("-port" == name){
port = atoi(argv[++i]);
}else if("-messages" == name){
messages = atoi(argv[++i]);
}else if("-subscribers" == name){
subscribers = atoi(argv[++i]);
}else if("-ack_mode" == name){
ackMode = atoi(argv[++i]);
}else if("-transactional" == name){
transactional = true;
}else if("-prefetch" == name){
prefetch = atoi(argv[++i]);
}else if("-batches" == name){
batches = atoi(argv[++i]);
}else if("-delay" == name){
delay = atoi(argv[++i]);
}else if("-size" == name){
size = atoi(argv[++i]);
}else if("-trace" == name){
trace = true;
}else{
std::cout << "Warning: unrecognised option " << name << std::endl;
}
}
}
void Args::usage(){
std::cout << "Options:" << std::endl;
std::cout << " -help" << std::endl;
std::cout << " Prints this usage message" << std::endl;
std::cout << " -host <host>" << std::endl;
std::cout << " Specifies host to connect to (default is localhost)" << std::endl;
std::cout << " -port <port>" << std::endl;
std::cout << " Specifies port to conect to (default is 5762)" << std::endl;
std::cout << " -messages <count>" << std::endl;
std::cout << " Specifies how many messages to send" << std::endl;
std::cout << " -subscribers <count>" << std::endl;
std::cout << " Specifies how many subscribers to expect reports from" << std::endl;
std::cout << " -ack_mode <mode>" << std::endl;
std::cout << " Sets the acknowledgement mode" << std::endl;
std::cout << " 0=NO_ACK (default), 1=AUTO_ACK, 2=LAZY_ACK" << std::endl;
std::cout << " -transactional" << std::endl;
std::cout << " Indicates the client should use transactions" << std::endl;
std::cout << " -prefetch <count>" << std::endl;
std::cout << " Specifies the prefetch count (default is 1000)" << std::endl;
std::cout << " -batches <count>" << std::endl;
std::cout << " Specifies how many batches to run" << std::endl;
std::cout << " -delay <seconds>" << std::endl;
std::cout << " Causes a delay between each batch" << std::endl;
std::cout << " -size <bytes>" << std::endl;
std::cout << " Sets the size of the published messages (default is 256 bytes)" << std::endl;
std::cout << " -trace" << std::endl;
std::cout << " Indicates that the frames sent and received should be logged" << std::endl;
}