Showing posts with label thrift. Show all posts
Showing posts with label thrift. Show all posts

Wednesday, July 22, 2015

Lets understand Apache Thrift

Apache Thrift is framework, for implementing RPC in services, with cross-language support. 
RPC (Remote Procedure Call) is very similar to normal function, only that it is present remotely on a different server as a service. A service exposes many such functions/procedure to its client. And client requires some way to know what are the functions/procedures exposed by this service and what are their parameters.

This is where Apache Thrift comes in. It has its own "Interface Definition Language" (IDL). In this language you define what are the functions and what are their parameters. And then use Thrift compiler to generate corresponding code for any language of your choice. What this means, is that you can implement a function in java, host it on a server and then remotely call it from python or any other language for that matter. 


In this post we will try to understand what thrift does, its architecture and components:

Thrift networking stack can be represented as:



Lets understand each component.

Transport

The Transport layer provides a simple abstraction for reading/writing from/to the network. This enables Thrift to decouple the underlying transport from the rest of the system (serialization/deserialization, for instance).

Here are some of the methods exposed by the Transport interface:
  • open
  • close
  • read
  • write
  • flush
Here are some of the transports available for majority of the Thrift-supported languages:
  • file: read/write to/from a file on disk
  • http: as the name suggests
Eg - We create a thrift HTTP transport in java as following
org.apache.thrift.transport.THttpClient transport = new THttpClient("http://" + serverIP + ":" + serverPortserviceUrl);

Protocol

The Protocol abstraction defines a mechanism to map in-memory data structures to a wire-format. In other words, a protocol specifies how datatypes use the underlying Transport to encode/decode themselves. Thus the protocol implementation governs the encoding scheme and is responsible for (de)serialization. Some examples of protocols in this sense include JSON, XML, plain text, compact binary etc.

Eg- We create a thrift Binary Protocol in java as following
org.apache.thrift.protocol.TProtocol protocol = new TBinaryProtocol(transport);

Processor

A Processor encapsulates the ability to read data from input streams and write to output streams. The input and output streams are represented by Protocol objects. Service-specific processor implementations are generated by the compiler. The Processor essentially reads data from the wire (using the input protocol), delegates processing to the handler (implemented by the user) and writes the response over the wire (using the output protocol).

Incase you have to create a thrift processor (Eg - Thrift with servlet), we create as following in java
Processor<Iface> processor = new Processor<Iface>(<ThriftInterfaceImpl.Iface>);

Server

A Server pulls together all of the various features described above. 


  • Create a transport
  • Create input/output protocols for the transport
  • Create a processor based on the input/output protocols
  • Wait for incoming connections and hand them off to the processor
That concludes this session, do let me know if you have any comments. And Happy coding :)

Tuesday, July 7, 2015

Using Thrift with Java servlet

Today we will see how can we use Thrift for webservice call in Java. 

We will start with our Thrift Servlet. This will be a very simple class which will be used to delegate our http request

public class ThriftDelegateServlet extends TServlet {

private static final long serialVersionUID = 5744887369513855991L;

public ThriftDelegateServlet(TProcessor processor, TProtocolFactory protocolFactory) {
super(processor, protocolFactory);
}

@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doPost(request, response);
    }
    
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        super.doGet(request, response);
    }

}

As you can see, we are just extending org.apache.thrift.server.TServlet class and then overriding doPost and doGet method.

Next we will create our servlet class which will be used as end point of our service. 

public class TestServlet extends HttpServlet {

private static final long serialVersionUID = -8777659712204723236L;
private ThriftDelegateServlet thriftServlet;

    @Override
    public void init() throws ServletException {

            Iface serviceHandler = BeanFactoryHolder.getBean("serviceImpl");

            Processor<Iface> processor = new Processor<Iface>(serviceHandler);
            thriftServlet = new ThriftDelegateServlet(processor, new TBinaryProtocol.Factory());
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            if (thriftServlet == null) {
                init();
            }
            thriftServlet.doPost(req, resp);

    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        this.doPost(req, resp);
    }

}


Self explanatory, but couple of points to add here:
  • In the init(), we are using Bean Factory to get bean of "serviceImpl". serviceImpl is basically our service implemented code i.e Implementation of your Thrift IDL. Hence it will give you an object of <Class>.Iface
  • Next we will create a Thrift Processor of Iface type
  • And finally create an object of ThriftDelegateServlet using processor and thrift protocol (TBinary protocol in our case)
  • When ever we get a request (doPost()) we just see if thriftdelegate is initialised and pass the request to thriftDelegate
Finally we will see how our web.xml should look:
  • Add SpringContext
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
            classpath*:context.xml
</param-value>
</context-param>
  • Add Spring listner

<!-- Spring Listeners -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

  • Add Servlet details

<!-- Start: Servlet for starting thrift over http protocol -->
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.test.tutorial.service.thrift.common.TestServlet</eservlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/service</url-pattern>
</servlet-mapping>

That's it, we are good to go now. We can now create thrift HTTP client and hit webservice. This is how we can create client:

private final String serviceUrl = "/dummy/service";
protected Client getClient() {

THttpClient transport = null;
try {
transport = new THttpClient("http://" + serverIP + ":" + serverPort
+ serviceUrl);
} catch (TTransportException e) {
throw new RuntimeException(e);
}
TProtocol protocol = new TBinaryProtocol(transport);
return new Client(protocol);
}

Before we wrap this is what happens in a nut shell:

  • We create a servlet which is our end point for webservice
  • Whenever a request comes it will first call its init() where we initialise thriftDelegateServlet by passing Thrift Processor and Protocol. To create Processor we get an object of Service Implemented Class
  • ThriftDelegateServlet simply passes the call to thrift Servlet
Will come back with more posts, do let me know your thought and till then Happy Coding :)