Back to index

nordugrid-arc-nox  1.1.0~rc6
perftest.cpp
Go to the documentation of this file.
00001 #ifdef HAVE_CONFIG_H
00002 #include <config.h>
00003 #endif
00004 
00005 // perftest.cpp
00006 
00007 #include <iostream>
00008 #include <fstream>
00009 #include <string>
00010 #include <stdlib.h>
00011 #include <glibmm/thread.h>
00012 #include <glibmm/timer.h>
00013 
00014 #include <arc/ArcConfig.h>
00015 #include <arc/message/MCCLoader.h>
00016 #include <arc/message/SOAPEnvelope.h>
00017 #include <arc/message/PayloadSOAP.h>
00018 #include <arc/message/MCC.h>
00019 #include <arc/StringConv.h>
00020 #include <arc/Logger.h>
00021 
00022 // Some global shared variables...
00023 Glib::Mutex* mutex;
00024 bool run;
00025 int finishedThreads;
00026 unsigned long completedRequests;
00027 unsigned long failedRequests;
00028 unsigned long totalRequests;
00029 Glib::TimeVal completedTime;
00030 Glib::TimeVal failedTime;
00031 Glib::TimeVal totalTime;
00032 
00033 // The configuration string.
00034 std::string confString = "\
00035     <ArcConfig\
00036       xmlns=\"http://www.nordugrid.org/schemas/ArcConfig/2007\"\
00037       xmlns:tcp=\"http://www.nordugrid.org/schemas/ArcMCCTCP/2007\">\
00038      <ModuleManager>\
00039         <Path>.libs/</Path>\
00040         <Path>../../hed/mcc/http/.libs/</Path>\
00041         <Path>../../hed/mcc/soap/.libs/</Path>\
00042         <Path>../../hed/mcc/tls/.libs/</Path>\
00043         <Path>../../hed/mcc/tcp/.libs/</Path>\
00044         <Path>../../lib/arc/</Path>\
00045      </ModuleManager>\
00046      <Plugins><Name>mcctcp</Name></Plugins>\
00047      <Plugins><Name>mcctls</Name></Plugins>\
00048      <Plugins><Name>mcchttp</Name></Plugins>\
00049      <Plugins><Name>mccsoap</Name></Plugins>\
00050      <Chain>\
00051       <Component name='tcp.client' id='tcp'><tcp:Connect><tcp:Host>HOSTNAME</tcp:Host><tcp:Port>PORTNUMBER</tcp:Port></tcp:Connect></Component>\
00052       <Component name='tls.client' id='tls'><next id='tcp'/></Component> \
00053       <Component name='http.client' id='http'><next id='tls'/><Method>POST</Method><Endpoint>HTTPPATH</Endpoint></Component>\
00054       <Component name='soap.client' id='soap' entry='soap'><next id='http'/></Component>\
00055      </Chain>\
00056     </ArcConfig>";
00057 
00058 // Replace a substring by another substring.
00059 void replace(std::string& str,
00060             const std::string& out,
00061             const std::string& in)
00062 {
00063   std::string::size_type index = str.find(out);
00064   if (index!=std::string::npos)
00065     str.replace(index, out.size(), in);
00066 }
00067 
00068 // Round off a double to an integer.
00069 int Round(double x){
00070   return int(x+0.5);
00071 }
00072 
00073 // Send requests and collect statistics.
00074 void sendRequests(){
00075   // Some variables...
00076   unsigned long completedRequests = 0;
00077   unsigned long failedRequests = 0;
00078   Glib::TimeVal completedTime(0,0);
00079   Glib::TimeVal failedTime(0,0);
00080   Glib::TimeVal tBefore;
00081   Glib::TimeVal tAfter;
00082   bool connected;
00083       
00084   while(run){
00085     
00086     // Create a client chain.
00087     Arc::Config client_config(confString);
00088     if(!client_config) {
00089       std::cerr << "Failed to load client configuration." << std::endl;
00090       return;
00091     }
00092     Arc::MCCLoader client_loader(client_config);
00093     Arc::MCC* client_entry = client_loader["soap"];
00094     if(!client_entry) {
00095       std::cerr << "Client chain have no entry point." << std::endl;
00096       return;
00097     }
00098     connected=true;
00099 
00100     Arc::MessageContext context;
00101     while(run and connected){
00102       // Prepare the request.
00103       Arc::NS echo_ns;
00104       echo_ns["echo"]="http://www.nordugrid.org/schemas/echo";
00105       Arc::PayloadSOAP req(echo_ns);
00106       req.NewChild("echo").NewChild("say")="HELLO";
00107       Arc::Message reqmsg;
00108       Arc::Message repmsg;
00109       Arc::MessageAttributes attributes_req;
00110       Arc::MessageAttributes attributes_rep;
00111       reqmsg.Attributes(&attributes_req);
00112       reqmsg.Context(&context);
00113       reqmsg.Payload(&req);
00114       repmsg.Attributes(&attributes_rep);
00115       repmsg.Context(&context);
00116       
00117       // Send the request and time it.
00118       tBefore.assign_current_time();
00119       Arc::MCC_Status status = client_entry->process(reqmsg,repmsg);
00120       tAfter.assign_current_time();
00121       
00122       if(!status) {
00123         // Request failed.
00124         failedRequests++;
00125         failedTime+=tAfter-tBefore;
00126            connected=false;
00127       } else {
00128         Arc::PayloadSOAP* resp = NULL;
00129         if(repmsg.Payload() != NULL) {
00130           // There is response.
00131           try{
00132             resp = dynamic_cast<Arc::PayloadSOAP*>(repmsg.Payload());
00133           }
00134           catch(std::exception&){ };
00135         }
00136         if(resp == NULL) {
00137           // Response was not SOAP or no response at all.
00138           failedRequests++;
00139           failedTime+=tAfter-tBefore;
00140           connected=false;
00141         } else {
00142           std::string xml;
00143           resp->GetXML(xml);
00144           if (std::string((*resp)["echoResponse"]["hear"]).size()==0){
00145             // The response was not what it should be.
00146             failedRequests++;
00147             failedTime+=tAfter-tBefore;
00148             connected=false;
00149           }
00150           else{
00151             // Everything worked just fine!
00152             completedRequests++;
00153             completedTime+=tAfter-tBefore;
00154           }
00155         }
00156       }
00157       if(repmsg.Payload()) delete repmsg.Payload();
00158     }
00159   }
00160 
00161   // Update global variables.
00162   Glib::Mutex::Lock lock(*mutex);
00163   ::completedRequests+=completedRequests;
00164   ::failedRequests+=failedRequests;
00165   ::completedTime+=completedTime;
00166   ::failedTime+=failedTime;
00167   finishedThreads++;
00168   std::cout << "Number of finished threads: " << finishedThreads << std::endl;
00169 }
00170 
00171 int main(int argc, char* argv[]){
00172   // Some variables...
00173   std::string serviceHost;
00174   std::string portNumber;
00175   std::string httpPath("/Echo");
00176   int numberOfThreads;
00177   int duration;
00178   int i;
00179   Glib::Thread** threads;
00180   const char* config_file = NULL;
00181   int debug_level = -1;
00182   Arc::LogStream logcerr(std::cerr);
00183 
00184   // Process options - quick hack, must use Glib options later
00185   while(argc >= 3) {
00186     if(strcmp(argv[1],"-c") == 0) {
00187       config_file = argv[2];
00188       argv[2]=argv[0]; argv+=2; argc-=2;
00189     } else if(strcmp(argv[1],"-d") == 0) {
00190       debug_level=Arc::string_to_level(argv[2]);
00191       argv[2]=argv[0]; argv+=2; argc-=2;
00192     } else {
00193       break;
00194     };
00195   } 
00196   if(config_file) {
00197     std::ifstream f(config_file);
00198     if(!f) {
00199       std::cerr << "File "<<config_file<<" can't be open for reading!" << std::endl;
00200       exit(EXIT_FAILURE);
00201     }
00202     std::getline<char>(f, confString, 0);
00203   }
00204   if(debug_level >= 0) {
00205     Arc::Logger::getRootLogger().setThreshold((Arc::LogLevel)debug_level);
00206     Arc::Logger::getRootLogger().addDestination(logcerr);
00207   }
00208   // Extract command line arguments.
00209   if (argc!=5){
00210     std::cerr << "Wrong number of arguments!" << std::endl
00211              << std::endl
00212              << "Usage:" << std::endl
00213              << "perftest [-c config] [-d debug] host port threads duration" << std::endl
00214              << std::endl
00215              << "Arguments:" << std::endl
00216              << "host     The name of the host of the service." << std::endl
00217              << "port     The port to use on the host." << std::endl
00218              << "threads  The number of concurrent requests." << std::endl
00219              << "duration The duration of the test in seconds." << std::endl
00220              << "config   The file containing client chain XML configuration with " << std::endl
00221               << "         'soap' entry point and HOSTNAME, PORTNUMBER and PATH " << std::endl
00222               << "         keyword for hostname, port and HTTP path of 'echo' service." << std::endl
00223              << "debug    The textual representation of desired debug level. Available " << std::endl
00224               << "         levels: DEBUG, VERBOSE, INFO, WARNING, ERROR, FATAL." << std::endl;
00225     exit(EXIT_FAILURE);
00226   }
00227   serviceHost = std::string(argv[1]);
00228   portNumber = std::string(argv[2]);
00229   numberOfThreads = atoi(argv[3]);
00230   duration = atoi(argv[4]);
00231   
00232   // Insert host name and port number into the configuration string.
00233   replace(confString, "HOSTNAME", serviceHost);
00234   replace(confString, "PORTNUMBER", portNumber);
00235   replace(confString, "HTTPPATH", httpPath);
00236 
00237   // Start threads.
00238   run=true;
00239   finishedThreads=0;
00240   //Glib::thread_init();
00241   mutex=new Glib::Mutex;
00242   threads = new Glib::Thread*[numberOfThreads];
00243   for (i=0; i<numberOfThreads; i++)
00244     threads[i]=Glib::Thread::create(sigc::ptr_fun(sendRequests),true);
00245 
00246   // Sleep while the threads are working.
00247   Glib::usleep(duration*1000000);
00248 
00249   // Stop the threads
00250   run=false;
00251   while(finishedThreads<numberOfThreads)
00252     Glib::usleep(100000);
00253 
00254   // Print the result of the test.
00255   Glib::Mutex::Lock lock(*mutex);
00256   totalRequests = completedRequests+failedRequests;
00257   totalTime = completedTime+failedTime;
00258   std::cout << "========================================" << std::endl;
00259   std::cout << "Host: "
00260            << serviceHost << std::endl;
00261   std::cout << "Port: "
00262            << portNumber << std::endl;
00263   std::cout << "Number of threads: "
00264            << numberOfThreads << std::endl;
00265   std::cout << "Duration: "
00266            << duration << " s" << std::endl;
00267   std::cout << "Number of requests: "
00268            << totalRequests << std::endl;
00269   std::cout << "Completed requests: "
00270            << completedRequests << " ("
00271            << Round(completedRequests*100.0/totalRequests)
00272            << "%)" << std::endl;
00273   std::cout << "Failed requests: "
00274            << failedRequests << " ("
00275            << Round(failedRequests*100.0/totalRequests)
00276            << "%)" << std::endl;
00277   std::cout << "Average response time for all requests: "
00278            << Round(1000*totalTime.as_double()/totalRequests)
00279            << " ms" << std::endl;
00280   if (completedRequests!=0)
00281     std::cout << "Average response time for completed requests: "
00282              << Round(1000*completedTime.as_double()/completedRequests)
00283              << " ms" << std::endl;
00284   if (failedRequests!=0)
00285     std::cout << "Average response time for failed requests: "
00286              << Round(1000*failedTime.as_double()/failedRequests)
00287              << " ms" << std::endl;
00288   std::cout << "========================================" << std::endl;
00289 
00290   return 0;
00291 }