在了解 Thrift IDL 后,就能开始编写自己的 RPC 服务端和客户端了。对 Thrift 的安装过程和命令操作略过不表,主要还是关注如何利用 Thrift 实现 Java 的 RPC 服务端和客户端。

服务接口描述

首先需要定义服务接口描述,即 .thrift 文件,再由 Thrift 将接口描述文件编译成相应的客户端和服务端的 stub 代码。

官网 Tutorial 给出的示例略复杂,不妨自己写一个简单的 Hello World 文件:

// tutorial.thrift
namespace java com.isudox.thrift.tutorial

typedef i32 int

service CustomService {
    int add(1:int a, 2:int b)
    string sayHello(1:string name)
}

描述文件写的很简单,只定义了一个接口,包含两个函数。将 tutorial.thrift 编译成 Java 代码:

thrift -r --gen java tutorial.thrift

生成如下 Java 文件:

gen-java
└── com
    └── isudox
        └── thrift
            └── tutorial
                └── CustomService.java

下面是 CustomService.java 的部分代码:

package com.isudox.thrift.tutorial;

@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.10.0)", date = "2017-04-10")
public class CustomService {

  public interface Iface {

    public int add(int a, int b) throws org.apache.thrift.TException;

    public java.lang.String sayHello(java.lang.String name) throws org.apache.thrift.TException;

  }
  // 后面省略...
}

可以看到,Thrift 自动生成了接口 CustomService.Iface,包含 add() 和 sayHello() 函数。

另外,如果是 Maven 构建的项目,在模块的 pom.xml 文件中添加 Thrift 依赖:

<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.10.0</version>
</dependency>

有了上面的编译后的 stub 代码,就能对 RPC 服务端和客户端具体实现进行编码了。

Server & Client

在编写 RPC Server 和 Client 代码前,得先把 Thrift 定义的接口实现。编写 CustomServiceHandler.java

// CustomServiceHandler.java
package com.isudox.thrift.tutorial

import org.apache.thrift.*

public class CustomServiceHandler implements CustomService.Iface {
    @Override  
    public int add(int a, int b) throws TException {  
        return a + b;  
    }

    @Override
    public String sayHello(String name) throws TException {
        return "Hello " + name;
    }
}

然后就能编写 Server 和 Client 的具体实现了,通过 CustomServiceHandler 来实现远程调用;

CustomServer.java

// CustomServer.java
package com.isudox.thrift.tutorial

import org.apache.thrift.*

public class CustomServer {

    public static CustomServiceHandler handler;

    public static CustomService.Processor processor;

    public static void main(String[] args) {
        try {
            handler = new CustomServiceHandler();
            processor = new CustomService.Processor(handler);
            
            Runnable simple = new Runnable() {
                public void run() {
                    simple(processor);
                }
            };

            new Thread(simple).start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void simple(CustomService.Processor<CustomServiceHandler> processor) {
        try {
            TServerTransport st = new TServerSocket(9090);
            TServer server = new TSimpleServer(new Args(st).processor(processor));
            System.out.println("Starting the simple server...");
            server.serve();
        }
         catch (Exception e) {
             e.printStackTrace();
        }
    }
}

CustomClient.java

// CustomClient.java
package com.isudox.thrift.tutorial

import org.apache.thrift.*

public class CustomClient {
    public static void main(String[] args) {
        try {
            TTransport transport = new TSocket("localhost", 9090);
            transport.open();

            TProtocol protocol = new TBinaryProtocol(transport);  // 二进制格式
            CustomService.Client client = new CustomService.Client(protocol);

            System.out.print(client.add(1, 2));
            System.out.print(client.sayHello("sudoz"));

            transport.close();
        } catch (TTransportException te) {
            te.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }
    }
}

运行 Server 端 main 函数,会以 simple 的模式启动,然后运行 Client,可以看到输出了 Thrift 定义的接口的函数运行结果。


参考资料: