Thrift Multiplexing in C#

Feb 15, 2017 - 2 min read

Thrift multiplexing support was added in version 0.9.1 and it enabled running multiple services on the same address and port. Before this feature, only two options were available: host each service on a different port, or write a monolithic service that does everything.

Only differences between non-multiplexed and multiplexed implementation will be highlighted in this article. You can download the source code or check it out at GitHub. Visual Studio Community 2015 was used to create the solution.

Note: This article continues from C# Apache Thrift Demo, so make sure you understand the way Thrift works before you continue.

In a nutshell

  • Server needs to use the TMultiplexedProcessor
  • Each service processor needs to be registered with TMultiplexedProcessor with a unique name
  • Client needs to use the TMultiplexedProtocol that wraps any other protocol
  • When creating the TMultiplexedProtocol, the unique service name that was used on the server needs to be provided

Services (.thrift files)

Multiple smaller services can be defined in one or more files.

// BookService.thrift

service BookService
{
  list<BookInfo> GetAllBooks();
  BookInfo GetBook(1: i32 bookId);
}

// UserAccountService.thrift

service UserAccountService
{
  list<UserAccount> GetAllUserAccounts();
  UserAccount GetUserAccount(1: i32 userAccountId);
}

Server

TMultiplexedProcessor enables multiplexing on the server. Each individual service processor is registered with a unique name.

// Server

var bookServiceHandler = new BookServiceHandler();
var userAccountServiceHandler = new UserAccountServiceHandler();

var bookServiceProcessor = new BookService.Processor(bookServiceHandler);
var userAccountProcessor = new UserAccountService.Processor(userAccountServiceHandler);

// TMultiplexedProcessor is required for multiplexing
var processor = new TMultiplexedProcessor();

// Register the processors: unique service name and the processor itself
processor.RegisterProcessor(nameof(BookService), bookServiceProcessor);
processor.RegisterProcessor(nameof(UserAccountService), userAccountProcessor);

TServerTransport transport = new TServerSocket(9090);
TServer server = new TThreadPoolServer(processor, transport);

Client

TMultiplexedProtocol is required for communicating with a multiplexing server. It wraps an underlying protocol and adds the service name to each request the client makes to the server. This is how the server knows on which service to execute the call.

// Client

var transport = new TSocket("localhost", 9090, 100); // address, port, timeout
var binaryProtocol = new TBinaryProtocol(transport); // The underlying protocol

// TMultiplexedProtocol is required to work with a multiplexing server
// Underlying protocol, along with a unique service name
var bookServiceProtocol = new TMultiplexedProtocol(binaryProtocol, nameof(BookService));
var userAccountServiceProtocol = new TMultiplexedProtocol(binaryProtocol, nameof(UserAccountService));

var bookServiceClient = new BookService.Client(bookServiceProtocol);
var userAccountServiceClient = new UserAccountService.Client(userAccountServiceProtocol);