1. Apache Thrift, a remote service invocation framework developed by Facebook, uses an interface description language to define and create services, and supports scalable cross-language service development. The included code generation engine can be used in multiple languages, such as C++, Java, Python , PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk, etc. to create efficient and seamless services, which transmit data in binary format, which is smaller than XML and JSON, and is suitable for high concurrency, large data volume and multiple The language environment is more advantageous
2. Thrift instance: Create a simple service Hello.
Listing 1 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() }
It defines five methods of service Hello, each method includes a method name, parameter list and return type.
Use the Thrift tool to compile Hello.thrift, and the corresponding Hello.java file will be generated. This file contains the interface definition for the service Hello described in the Hello.thrift file, the Hello.Iface interface.
Create the HelloServiceImpl.java file and implement the Hello.Iface interface in the Hello.java file. The code is as follows:
Listing 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 to; } @Override public int helloInt(int para) throws TException { try { Thread.sleep(20000); } catch (InterruptedException e) { e.printStackTrace (); } return to; } @Override public String helloNull() throws TException { return null; } @Override public String helloString(String para) throws TException { return to; } @Override public void helloVoid() throws TException { System.out.println("Hello World"); } }
Create the server-side implementation code and pass HelloServiceImpl as a specific processor to the Thrift server. The code is as follows:
Listing 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 访问服务端的逻辑实现,代码如下:
清单 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(); } } }