关于Thrift的学习

简介

thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。

优点

Thrift不仅是一个数据交换格式,它有着一站式跨语言服务的解决方案。因此与Protobuf相比,Thrift更加简单易用直接上手,而且Thrift也支持更多的语言类型。并且因为Thrift与Protobuf相同,都基于二进制数据传输格式,因此与传统的数据交换格式相比,Thrift更快,更小并且没有误解。

综上优点:

  • 支持更多的语言类型
  • 简单易用直接上手
  • 高效的解析速度
  • 小巧的传输体积

缺点

与protobuf相比Thrift编码优化不够好,导致编码时间长,编码后体积大。由facebook出品,所以没有文档。虽然提供一站式的服务,但提供的网络层只局限于cs构架,不支持服务器推送,并且有人抱怨网络层的性能也不好。

综上缺点:

  • 编码时间较长
  • 编码后体积大
  • 没有文档
  • 不支持服务器推送
  • 网络层的性能也不好

用途

如果是简单的RPC,可以使用Thrift快速开发。如果做稳定的服务器开发,则因考虑把其网络层替换。

简单上手

预装环境

  • org.apache.thrift的jar包http://central.maven.org/maven2/org/apache/thrift/libthrift/0.10.0/
  • thrift.exehttp://archive.apache.org/dist/thrift/0.10.0/

编写.thrift

namespace java com.thrift.demo

service HelloWorldService {
    string sayHello(1:string username)
}

生成java文件

thrift-0.10.0.exe --gen java xxx.thrift

编写Server

import org.apache.thrift.TException;
import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.server.TServer.*;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

import com.thrift.demo.HelloWorldService.Iface;
import com.thrift.demo.HelloWorldService.Processor;

public class Server implements Iface{
    public static void main(String[] args) throws TTransportException {
        startNioServer();
    }

    //无阻塞Server
    public static void  startNioServer() throws TTransportException{
        //绑定端口
        TNonblockingServerSocket socket = new TNonblockingServerSocket(9090);
        TNonblockingServer.Args options = new TNonblockingServer.Args(socket);  
        Server server = new Server();
        //注册handler
        TProcessor processor = new HelloWorldService.Processor(server);  
        options.processor(processor);  
        //设置编解码协议
        options.protocolFactory(new TCompactProtocol.Factory());  
        TServer tserver = new TNonblockingServer(options);  
        System.out.println("Thrift Server is running at 9090 port");  
        //启动server
        tserver.serve(); 
    }

    //简单的阻塞Server
    public static void  startSimpleServer() throws TTransportException{
        //绑定端口
        TServerTransport serverTransport = new TServerSocket(9090);
        Server server = new Server();
        //注册handler
        TProcessor processor = new Processor<>((Iface)server);
        TServer.Args options = new Args(serverTransport);
        //设置编解码协议
        options.protocolFactory(new TCompactProtocol.Factory());
        options.processor(processor);
        TServer tServer = new TSimpleServer(options);

        System.out.println("Thrift Server is running at 9090 port");  
        //启动server
        tServer.serve();

    }
    @Override
    public String sayHello(String username) throws TException {
        System.out.println("resv:"+username);

        return "resv from server : "+username;
    }
}

编写client

import java.io.IOException;

import org.apache.thrift.TException;
import org.apache.thrift.async.AsyncMethodCallback;
import org.apache.thrift.async.TAsyncClientManager;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TNonblockingServerTransport;
import org.apache.thrift.transport.TNonblockingSocket;
import org.apache.thrift.transport.TNonblockingTransport;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;

import com.thrift.demo.HelloWorldService.*;

public class client  {
    public static void main(String[] args) throws TException, IOException, InterruptedException {
        startNonblockingClient();
    }
    //无阻塞,异步回调client
    public static void startNonblockingClient() throws IOException, TException, InterruptedException{
        TNonblockingTransport transport = new TNonblockingSocket("localhost", 9090);
        TProtocol protocol = new  TBinaryProtocol(transport);
        AsyncClient client ;
        TAsyncClientManager clientManager = new TAsyncClientManager();
        TProtocolFactory protocolFactory = new TCompactProtocol.Factory();
        client = new AsyncClient.Factory(clientManager, protocolFactory).getAsyncClient(transport);
        client.sayHello("helloworld", new AsyncMethodCallback() {

            @Override
            public void onError(Exception exception) {
                System.out.println("exception : "+exception);
            }

            @Override
            public void onComplete(String response) {
                System.out.println("response : "+response);
            }
        });
        Thread.sleep(1000l);
    }
    //简单阻塞,同步client
    public static void startSimpleClient() throws TException{
        TTransport transport =  new TSocket("localhost", 9090);
        transport.open();
        TProtocol protocol = new TCompactProtocol.Factory().getProtocol(transport);

        Client client = new Client(protocol);
        String resv =  client.sayHello("helloworld");
        System.out.println("resv:"+resv);
    }
}

运行

控制台:

Thrift Server is running at 9090 port
resv:helloworld

生成文件分析

生成文件结构如下:

  • Iface
  • AsyncIface
  • Client
  • AsyncClient
  • Processor

Iface

Iface是一个同步的服务端handler接口

AsyncIface

Iface是一个异步的服务端handler接口

Client

同步客户端

AsyncClient

异步客户端

可见Thrift同时支持同步和异步两种方式的服务端和客户端。并且由前面的例子可以看出Thrift同时也支持NIO和IO。

Codec

public class Codec {
    private AtomicInteger seqid_ = new AtomicInteger();
    TTransport transport =  new MTransport();
    TProtocol protocol = new TCompactProtocol.Factory().getProtocol(transport);
    Map registerMap = new ConcurrentHashMap<>();

    public boolean registe(String name,Snapshot ss){
        Snapshot res = registerMap.put(name, ss);
        if(res == null) return false;
        return true;
    }

    public byte[] encode(MessageInfo info) throws ThriftEncoderException{
        try{
            protocol.writeMessageBegin(new TMessage(info.getMethodName(), info.getType(), seqid_.incrementAndGet()));
            write(info.getArgs(),protocol);
            protocol.writeMessageEnd();
            byte[] bs = protocol.getTransport().getBuffer();
            protocol.getTransport().flush();
            return bs;
        }catch(Exception e){
            throw new ThriftEncoderException(e.getMessage());
        }
    }

    public void decode(byte[] bs) throws TException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException{
        MTransport trans = (MTransport) transport;
        trans.put(bs);
        TMessage msg = protocol.readMessageBegin();
        String MethodName = msg.name;
        Snapshot ss = registerMap.get(MethodName);
        Object args = ss.args_class.newInstance();
        read(args,protocol);
        Object res = null ;
        //... res = trans(args) 太麻烦了,不写了 
        ss.getHandler().handle(res);
    }

    @SuppressWarnings("unused")
    private static void write(Object args,TProtocol protocol) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        Class clazz = args.getClass();
        Method m = clazz.getMethod("write", TProtocol.class);
        m.invoke(args, protocol);
    }
    @SuppressWarnings("unused")
    private static void read(Object args,TProtocol protocol) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        Class clazz = args.getClass();
        Method m = clazz.getMethod("read", TProtocol.class);
        m.invoke(args, protocol);
    }

}

class ThriftEncoderException extends Exception{

    public ThriftEncoderException(String message) {
        super(message);
    }

}
class MTransport extends TIOStreamTransport{

    MTransport(){
        this.outputStream_ = new ByteArrayOutputStream();
    }

    public void put(byte[] bs){
        inputStream_ = new ByteArrayInputStream(bs);
    }

    @Override
    public byte[] getBuffer() {
        // TODO Auto-generated method stub
        return ((ByteArrayOutputStream)this.outputStream_).toByteArray();
    }
    @Override
    public void flush() throws TTransportException {
        super.flush();
    }
}
class MessageInfo{
    Object args;
    //TMessageType
    byte type; 
    String methodName;
    public Object getArgs() {
        return args;
    }
    public void setArgs(Object args) {
        this.args = args;
    }
    public byte getType() {
        return type;
    }
    public void setType(byte type) {
        this.type = type;
    }
    public String getMethodName() {
        return methodName;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public MessageInfo(Object args, byte type, String methodName) {
        super();
        this.args = args;
        this.type = type;
        this.methodName = methodName;
    }
}
class Snapshot{
    Class args_class;
    String methodName;
    Class result;
    Handler handler;

    public Handler getHandler() {
        return handler;
    }
    public void setHandler(Handler handler) {
        this.handler = handler;
    }
    public Class getArgs_class() {
        return args_class;
    }
    public void setArgs_class(Class args_class) {
        this.args_class = args_class;
    }
    public String getMethodName() {
        return methodName;
    }
    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }
    public Class getResult() {
        return result;
    }
    public void setResult(Class result) {
        this.result = result;
    }
    public Snapshot(Class args_class, String methodName, Class result, Handler handler) {
        super();
        this.args_class = args_class;
        this.methodName = methodName;
        this.result = result;
        this.handler = handler;
    }


}
interface Handler{
    T handle(V message);
}

更多关于Thrift的内容请参考http://dongxicheng.org/search-engine/thrift-guide/

你可能感兴趣的:(java)