Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Thrift - different Handler instance for each Socket

Im developing a 'proxy' server in Thrift. My problem is, that each connection incomming to the proxy uses the same instance of the Handler. The client implementation of the proxy is in the Handler, so all the clients communicate throuh the same connection to the end server.

I have : n clients -> n sockets -> 1 handler -> 1 socket -> 1 server What I want to implement : n clients -> n sockets -> n handlers -> n sockets -> 1 server

Now the problem is that if a client changes a 'local' parameter (something that is defined for each client independently) on the server, other clients will work with the changed environment too.

shared_ptr<CassProxyHandler> handler(new CassProxyHandler(adr_s,port_s,keyspace));
shared_ptr<TProcessor> processor(new CassandraProcessor(handler));
shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
shared_ptr<TTransportFactory> transportFactory(new TFramedTransportFactory());
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
TThreadedServer server(processor, serverTransport, transportFactory, protocolFactory);
server.serve();

Is there a way to implement a server, that creates a new instance of the Handler for each server socket instead of using the same handler?

Thanks for any suggestions or help, @

like image 378
Lendvay András Avatar asked Jul 08 '11 09:07

Lendvay András


2 Answers

I have managed to solve this problem. There was a solution already implemented in Java. I have used the same idea and implemented it in C++.

First thing I did is I created a TProcessorFactory instead of the TTransport class. This handles the TProcessors for each connection. It has a map structure in it, so its' get function returns the corresponding TProcessor for each TTransport. The corresponding (unique) TProcessor for each client.

I had to create a new TServer, so it would accept the newly created parameter TProcessorFactory instead of the TProcessor. In the TServer is also necessary to change a couple function calls. Your getProcessor function will no longer return a TProcessor but a TProcessorFactory (so change return type and rename).

The last thing you have to do is implement a server that allows instantiation, a derive class of TServer. I suggest using the TNonblockingServer (bit harder to implement the change) or the TThreadPoolServer. You have to change a couple function calls. Use a get function on the TProcessorFactory with a TTransport parameter to get a TProcessor where needed. The TTransport parameter is unique for each thread, each client connection is handled by one thread.

Also make sure you delete the old TProcessors, because thrift reuses (at least with the TNonblockingServer) the TTransport, so if you do not delete them and a client connects, he will probably get an inactive previous session and you probably don't want it. If you use shared pointers, just remove them from the map structure, when the client disconnects, and if there are no longer needed by thrift, they will be destructed.

I hope this helps to anyone, who encounters the same problem I did. If you don't know the inner structure of thrift, here a good guide : http://diwakergupta.github.com/thrift-missing-guide/

I hope the Thrift developers are going to implement something similar, but more sophisticated and abstract solution in the near future.

@

like image 104
Lendvay András Avatar answered Sep 22 '22 13:09

Lendvay András


I know this is an old thread, but in case it's ever of use to anyone - I have contributed a change to the C# implementation of Thrift to solve this problem...

https://issues.apache.org/jira/browse/THRIFT-3397

In addition to the old method of passing a TProcessor as the first argument to the threaded servers, one can now set up something like

new ThreadPoolServer(processorFactory,serverTransport,
                                    transportFactory,protocolFactory);

Where 'processorFactory' is a TProcessorFactory.

I've created TPrototypeProcessorFactory<TProcessor,Handler>(object[] handlerArgs) which would be set up like so:

TProcessorFactory processorFactory = 
      new TPrototypeProcessorFactory<ThriftGenerated.Processor, MyHandlerClass>(); 

The 'MyHandlerClass' implements your ThriftGenerated.Iface. Optionally, if this class takes arguments, they can be added as an array of objects to the processor factory. Internally - For each new client connection, this processor factory will:

  1. Create a new instance of 'MyHandlerClass' using any arguments supplied (using Activator.CreateInstance)

  2. If 'MyHandlerClass' implements 'TControllingHandler' it will set its 'server' property to the parent TServer (e.g. to allow control of the TServer using a thift client)

  3. Return a new instance of ThriftGenerated.Processor(handler)

Therefore for C# you get n clients -> n sockets -> n handlers -> n sockets -> 1 server

I hope this becomes useful to other people - it's certainly solved a problem for me.

like image 33
JonathanH-UK Avatar answered Sep 22 '22 13:09

JonathanH-UK