Python 作为服务端,Java 作为客户端调用 Python 提供的接口。本文主要按照这篇文章的思路连通 Python 与 Java,下面简单介绍一下如何使用,具体可参看原文章。最后重点提供一个 Java 的通用工厂方法,用来调用 Thrift 提供的接口。

生成文件

首先定义thrift接口文件,hello.thrift

1service Hello {    
2
3    string helloString(1:string word)    
4
5}  

生成 Python 代码:

1thrift --gen py hello.thrift

生成 Java 代码:

1thrift --gen java hello.thrift

生成完的文件要各自放到 Python 和 Java 的工程中。

Python 客户端

 1from hello import Hello
 2from thrift.transport import TSocket
 3from thrift.transport import TTransport
 4from thrift.protocol import TBinaryProtocol
 5from thrift.server import TServer
 6
 7
 8class HelloHandler:
 9    def __init__(self):
10        pass
11
12    def helloString(self, word):
13        ret = "Hello Thrift! Received: " + word
14        return ret
15
16
17# handler processer类
18handler = HelloHandler()
19processor = Hello.Processor(handler)
20transport = TSocket.TServerSocket("127.0.0.1", 8989)
21# 传输方式,使用buffer
22tfactory = TTransport.TBufferedTransportFactory()
23# 传输的数据类型:二进制
24pfactory = TBinaryProtocol.TBinaryProtocolFactory()
25# 创建一个thrift 服务~
26server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)
27
28print("Starting thrift server in python...")
29server.serve()
30print("done!")

Java 通用工厂方法

提供一个工厂类,不管 Thrift 文件怎么变化,核心调用代码都不需要变,只需要替换生成的代码即可。Thrift 的 ip 地址和端口一般写在配置文件中,由于各人读取配置文件的方法不尽相同,这里就写死在代码里,使用时换成从配置文件读就行了。

  1import org.apache.thrift.protocol.TBinaryProtocol;
  2import org.apache.thrift.protocol.TProtocol;
  3import org.apache.thrift.transport.TSocket;
  4import org.apache.thrift.transport.TTransport;
  5import org.apache.thrift.transport.TTransportException;
  6import org.slf4j.Logger;
  7import org.slf4j.LoggerFactory;
  8
  9import java.lang.reflect.Constructor;
 10import java.lang.reflect.Method;
 11
 12/**
 13 * Thrift 连接工厂
 14 * Created by cipher on 2017/9/22.
 15 */
 16public class ThriftFactory {
 17
 18    private ThriftFactory() {
 19    }
 20
 21    private static final Logger LOG = LoggerFactory.getLogger(ThriftFactory.class);
 22
 23    private static TProtocol protocol;
 24
 25    private static TTransport transport;
 26
 27    /**
 28     * 获取二进制 protocol
 29     *
 30     * @return 二进制 protocol
 31     */
 32    public static TProtocol getTProtocol() {
 33        // 单例获取 protocol
 34        if (protocol == null) {
 35            protocol = new TBinaryProtocol(getTTransport());
 36        }
 37        return protocol;
 38    }
 39
 40    /**
 41     * 获取传输对象
 42     *
 43     * @return 传输对象
 44     */
 45    public static TTransport getTTransport() {
 46        // 单例获取 transport
 47        if (transport == null) {
 48            // 应改成从配置文件读取
 49            String ip = "127.0.0.1";
 50            Integer port = 8989;
 51            transport = new TSocket(ip, port);
 52        }
 53        return transport;
 54    }
 55
 56    /**
 57     * 获取客户端实例
 58     *
 59     * @param clazz 客户端类
 60     * @param <T>   泛型
 61     * @return 客户端实例
 62     */
 63    public static <T> T getClient(Class<T> clazz) {
 64        T instance = null;
 65        try {
 66            //获取有参构造器
 67            Constructor c = clazz.getConstructor(TProtocol.class);
 68            // 实例化客户端,需要传入 protocol
 69            instance = (T) c.newInstance(getTProtocol());
 70        } catch (Exception e) {
 71            LOG.error("", e);
 72            throw new RuntimeException(e.getMessage());
 73        }
 74        return instance;
 75    }
 76
 77    /**
 78     * 发起请求
 79     *
 80     * @param clazz      客户端类
 81     * @param methodName 方法名,客户端中不能有重载的方法
 82     * @param param      方法参数
 83     * @param <T>        泛型
 84     * @return 方法返回值
 85     */
 86    public static <T> Object doRequest(Class<T> clazz, String methodName, Object... param) {
 87        Object result = null;
 88        try {
 89            // 获取客户端实例
 90            T instance = getClient(clazz);
 91            Method[] methods = clazz.getMethods();
 92            for (Method method : methods) {
 93                // 获取指定的方法
 94                if (method.getName().equals(methodName)) {
 95                    open();
 96                    result = method.invoke(instance, param);
 97                    close();
 98                    break;
 99                }
100            }
101        } catch (Exception e) {
102            LOG.error("", e);
103            throw new RuntimeException(e.getMessage());
104        }
105        return result;
106    }
107
108    /**
109     * 打开传输
110     */
111    public static void open() {
112        try {
113            getTTransport().open();
114        } catch (TTransportException e) {
115            LOG.error("", e);
116            throw new RuntimeException(e.getMessage());
117        }
118    }
119
120    /**
121     * 关闭传输
122     */
123    public static void close() {
124        getTTransport().close();
125    }
126
127}

测试代码:

1public static void main(String[] args) throws Exception {
2    String msg = (String) ThriftFactory.doRequest(Hello.Client.class, "helloString", "测试");
3    System.out.println(msg);
4}

Hello是 Thrift 生成的 java 代码,以后如果接口改了或者新增的接口,只需要使用 Thrift 生成代码,放到 java 工程中,修改doRequst的参数就行了。