远程服务调用框架 Apache Thrift

一、 Facebook 开发的远程服务调用框架 Apache Thrift,它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,所包含的代码生成引擎可以在多种语言中,如 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk 等创建高效的、无缝的服务,其传输数据采用二进制格式,相对 XML 和 JSON 体积更小,对于高并发、大数据量和多语言的环境更有优势

 

二、Thrift实例 : 创建一个简单的服务Hello。

清单一 hello.thrift 

namespace java service.demo 
 service Hello{ 
  string helloString(1:string para) 
  i32 helloInt(1:i32 para) 
  bool helloBoolean(1:bool para) 
  void helloVoid() 
  string helloNull() 
 }

其中定义了服务Hello的五个方法,每个方法包括一个方法名,参数列表和返回类型。

使用Thrift工具编译Hello.thrift, 就会生成相应的Hello.java 文件。该文件包含了在Hello.thrift文件中描述的服务Hello的接口定义,即Hello.Iface接口。

创建 HelloServiceImpl.java 文件并实现 Hello.java 文件中的 Hello.Iface 接口,代码如下:

 
清单 2. HelloServiceImpl.java
 package service.demo; 
 import org.apache.thrift.TException; 
 public class HelloServiceImpl implements Hello.Iface { 
    @Override 
    public boolean helloBoolean(boolean para) throws TException { 
        return para; 
    } 
    @Override 
    public int helloInt(int para) throws TException { 
        try { 
            Thread.sleep(20000); 
        } catch (InterruptedException e) { 
            e.printStackTrace(); 
        } 
        return para; 
    } 
    @Override 
    public String helloNull() throws TException { 
        return null; 
    } 
    @Override 
    public String helloString(String para) throws TException { 
        return para; 
    } 
    @Override 
    public void helloVoid() throws TException { 
        System.out.println("Hello World"); 
    } 
 }

 创建服务器端实现代码,将 HelloServiceImpl 作为具体的处理器传递给 Thrift 服务器,代码如下:

 

 清单 3. HelloServiceServer.java

 package service.server; 
 import org.apache.thrift.TProcessor; 
 import org.apache.thrift.protocol.TBinaryProtocol; 
 import org.apache.thrift.protocol.TBinaryProtocol.Factory; 
 import org.apache.thrift.server.TServer; 
 import org.apache.thrift.server.TThreadPoolServer; 
 import org.apache.thrift.transport.TServerSocket; 
 import org.apache.thrift.transport.TTransportException; 
 import service.demo.Hello; 
 import service.demo.HelloServiceImpl; 

 public class HelloServiceServer { 
    /** 
     * 启动 Thrift 服务器
     * @param args 
     */ 
    public static void main(String[] args) { 
        try { 
            // 设置服务端口为 7911 
            TServerSocket serverTransport = new TServerSocket(7911); 
            // 设置协议工厂为 TBinaryProtocol.Factory 
            Factory proFactory = new TBinaryProtocol.Factory(); 
            // 关联处理器与 Hello 服务的实现
            TProcessor processor = new Hello.Processor(new HelloServiceImpl()); 
            TServer server = new TThreadPoolServer(processor, serverTransport, 
                    proFactory); 
            System.out.println("Start server on port 7911..."); 
            server.serve(); 
        } catch (TTransportException e) { 
            e.printStackTrace(); 
        } 
    } 
 }

创建客户端实现代码,调用 Hello.client 访问服务端的逻辑实现,代码如下:

扫描二维码关注公众号,回复: 279095 查看本文章
清单 4. HelloServiceClient.java
 package service.client; 
 import org.apache.thrift.TException; 
 import org.apache.thrift.protocol.TBinaryProtocol; 
 import org.apache.thrift.protocol.TProtocol; 
 import org.apache.thrift.transport.TSocket; 
 import org.apache.thrift.transport.TTransport; 
 import org.apache.thrift.transport.TTransportException; 
 import service.demo.Hello; 

 public class HelloServiceClient { 
 /** 
     * 调用 Hello 服务
     * @param args 
     */ 
    public static void main(String[] args) { 
        try { 
            // 设置调用的服务地址为本地,端口为 7911 
            TTransport transport = new TSocket("localhost", 7911); 
            transport.open(); 
            // 设置传输协议为 TBinaryProtocol 
            TProtocol protocol = new TBinaryProtocol(transport); 
            Hello.Client client = new Hello.Client(protocol); 
            // 调用服务的 helloVoid 方法
            client.helloVoid(); 
            transport.close(); 
        } catch (TTransportException e) { 
            e.printStackTrace(); 
        } catch (TException e) { 
            e.printStackTrace(); 
        } 
    } 
 }

代码编写完后运行服务器,再启动客户端调用服务 Hello 的方法 helloVoid,在服务器端的控制台窗口输出“Hello World”(helloVoid 方法实现在控制台打印字符串,没有返回值,所以客户端调用方法后没有返回值输出,读者可以自己尝试其他有返回值方法的调用,其结果可以打印在客户端的控制台窗口 )。

 

三 数据类型

Thrift 脚本可定义的数据类型包括以下几种类型:

基本类型:

bool:布尔值,true 或 false,对应 Java 的 boolean

byte:8 位有符号整数,对应 Java 的 byte

i16:16 位有符号整数,对应 Java 的 short

i32:32 位有符号整数,对应 Java 的 int

i64:64 位有符号整数,对应 Java 的 long

double:64 位浮点数,对应 Java 的 double

string:未知编码文本或二进制字符串,对应 Java 的 String

结构体类型:

struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean

容器类型:

list:对应 Java 的 ArrayList 

set:对应 Java 的 HashSet

map:对应 Java 的 HashMap

异常类型:exception:对应 Java 的 Exception

服务类型:service:对应服务的类

四、协议

TBinaryProtocol 二进制编码格式进行数据传输

TCompactProtocol 高效率的、密集的二进制编码格式进行数据传输

TJSONProtocol 使用 JSON 的数据编码协议进行数据传输

TSimpleJSONProtocol 只提供 JSON 只写的协议,适用于通过脚本语言解析

五、传输层

TSocket —— 使用阻塞式 I/O 进行传输,是最常见的模式

TFramedTransport —— 使用非阻塞方式,按块的大小进行传输,类似于 Java 中的 NIO(若使用 TFramedTransport 传输层,其服务器必须修改为非阻塞的服务类型)

TNonblockingTransport —— 使用非阻塞方式,用于构建异步客户端

六、服务端类型

TSimpleServer —— 单线程服务器端使用标准的阻塞式 I/O

TThreadPoolServer —— 多线程服务器端使用标准的阻塞式 I/O

TNonblockingServer —— 多线程服务器端使用非阻塞式 I/O

七、Thrift 异步客户端构建

Thrift提供非阻塞的调用方式,可构建异步客户端。

清单 12.CallBack 的实现:MethodCallback.java

onComplete : 成功时,返回数据值

onError : 失败时,返回异常

 package service.callback; 
 import org.apache.thrift.async.AsyncMethodCallback; 

 public class MethodCallback implements AsyncMethodCallback { 
    Object response = null; 

    public Object getResult() { 
        // 返回结果值
        return this.response; 
    } 

    // 处理服务返回的结果值
    @Override 
    public void onComplete(Object response) { 
        this.response = response; 
    } 
    // 处理调用服务过程中出现的异常
    @Override 
    public void onError(Throwable throwable) { 

    } 
 }

清单 13.HelloServiceAsyncServer.java

HelloServiceAsyncServer 通过 java.nio.channels.ServerSocketChannel 创建非阻塞的服务器端等待客户端的连接。

 package service.server; 
 import org.apache.thrift.server.TNonblockingServer; 
 import org.apache.thrift.server.TServer; 
 import org.apache.thrift.transport.TNonblockingServerSocket; 
 import org.apache.thrift.transport.TNonblockingServerTransport; 
 import org.apache.thrift.transport.TTransportException; 
 import service.demo.Hello; 
 import service.demo.HelloServiceImpl; 

 public class HelloServiceAsyncServer { 
    /** 
     * 启动 Thrift 异步服务器
     * @param args 
     */ 
    public static void main(String[] args) { 
        TNonblockingServerTransport serverTransport; 
        try { 
            serverTransport = new TNonblockingServerSocket(10005); 
            Hello.Processor processor = new Hello.Processor( 
                    new HelloServiceImpl()); 
            TServer server = new TNonblockingServer(processor, serverTransport); 
            System.out.println("Start server on port 10005 ..."); 
            server.serve(); 
        } catch (TTransportException e) { 
            e.printStackTrace(); 
        } 
    } 
 }

创建异步客户端实现代码,调用 Hello.AsyncClient 访问服务端的逻辑实现,将 MethodCallback 对象作为参数传入调用方法中,代码如下:

清单 14.HelloServiceAsyncClient.java
 package service.client; 
 import java.io.IOException; 
 import org.apache.thrift.async.AsyncMethodCallback; 
 import org.apache.thrift.async.TAsyncClientManager; 
 import org.apache.thrift.protocol.TBinaryProtocol; 
 import org.apache.thrift.protocol.TProtocolFactory; 
 import org.apache.thrift.transport.TNonblockingSocket; 
 import org.apache.thrift.transport.TNonblockingTransport; 
 import service.callback.MethodCallback; 
 import service.demo.Hello; 

 public class HelloServiceAsyncClient { 
    /** 
     * 调用 Hello 服务
     * @param args 
     */ 
    public static void main(String[] args) throws Exception { 
        try { 
            TAsyncClientManager clientManager = new TAsyncClientManager(); 
            TNonblockingTransport transport = new TNonblockingSocket( 
                    "localhost", 10005); 
            TProtocolFactory protocol = new TBinaryProtocol.Factory(); 
            Hello.AsyncClient asyncClient = new Hello.AsyncClient(protocol, 
                    clientManager, transport); 
            System.out.println("Client calls ....."); 
            MethodCallback callBack = new MethodCallback(); 
            asyncClient.helloString("Hello World", callBack); 
            Object res = callBack.getResult(); 
            while (res == null) { 
                res = callBack.getResult(); 
            } 
            System.out.println(((Hello.AsyncClient.helloString_call) res) 
                    .getResult()); 
        } catch (IOException e) { 
            e.printStackTrace(); 
        } 
  } 
 }

猜你喜欢

转载自kayak2015.iteye.com/blog/2296824