Published on

Using Apache Thrift 5: A C++ Nonblocking Server

Authors

Today I will build a Nonblocking Server in C++.

The TSimpleServer we were using up through yesterday is a blocking server. As the name suggests, with a blocking server, once you make a request, control does not return to the client side until the processing is complete.

If that is acceptable for your use case, there is no problem, but in many cases you want to process multiple requests at once or return to other work on the client side immediately.

First, change the server side by adding a few includes and updating main. The number passed to newSimpleThreadManager is the number of requests that can be processed at the same time. Do not forget to link libthriftnb and libevent. If I remember correctly, Python on Windows did not support Nonblocking Server.

#define HAVE_CONFIG_H

#include "gen-cpp/Calculator.h"
#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TServerSocket.h>
#include <thrift/transport/TBufferTransports.h>
#include <concurrency/ThreadManager.h>
#include <concurrency/BoostThreadFactory.h>
#include <server/TNonblockingServer.h>

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace apache::thrift::server;
using namespace apache::thrift::concurrency;

using boost::shared_ptr;

using namespace ::calc;

class CalculatorHandler : virtual public CalculatorIf {
 public:
  CalculatorHandler() {
    // Your initialization goes here
  }

  double plus(const double arg1, const double arg2) {
    // Your implementation goes here
    printf("plusn");
 return arg1 + arg2;
  }

  double minus(const double arg1, const double arg2) {
    // Your implementation goes here
    printf("minusn");
 return arg1 - arg2;
  }

  double multiplies(const double arg1, const double arg2) {
    // Your implementation goes here
    printf("multipliesn");
 return arg1 * arg2;
  }

  double divides(const double arg1, const double arg2) {
    // Your implementation goes here
    printf("dividesn");
 return arg1 / arg2;
  }

};

int main(int argc, char **argv) {

  WSADATA wsaData;
  WSAStartup(MAKEWORD(2, 0), &wsaData);

  int port = 9090;
  shared_ptr handler(new CalculatorHandler());
  shared_ptr processor(new CalculatorProcessor(handler));
  shared_ptr protocolFactory(new TBinaryProtocolFactory());
  shared_ptr threadManager = ThreadManager::newSimpleThreadManager(15);
  shared_ptr threadFactory(new BoostThreadFactory());

  threadManager->threadFactory(threadFactory);
  threadManager->start();

  TNonblockingServer server(processor, protocolFactory, port, threadManager);

  server.serve();
  return 0;
}

The client side is almost the same. The important point is to change TBufferedTransport to TFramedTransport. I remember it not working when that change was missing. In Python as well, yesterday's source should work if you switch it to TFramedTransport.

#define HAVE_CONFIG_H

#include "gen-cpp/Calculator.h"

#include <protocol/TBinaryProtocol.h>
#include <transport/TSocket.h>
#include <transport/TTransportUtils.h>

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;
using namespace calc;

int main(int argc, char** argv) {

  boost::shared_ptr socket(new TSocket("localhost", 9090));
  boost::shared_ptr transport(new TFramedTransport(socket));
  boost::shared_ptr protocol(new TBinaryProtocol(transport));

  CalculatorClient client(protocol);

  try {
    transport->open();

    printf("2 + 3 = %fn", client.plus(2, 3));
    printf("6 - 3 = %fn", client.minus(6, 3));
    printf("8 * 3 = %fn", client.multiplies(8, 3));
    printf("16 / 5 = %fn", client.divides(16, 5));

    transport->close();
  } catch (TException& tx) {
    printf("ERROR: %sn", tx.what());
  }
  return 0;
}