Thrift 连接 Java 与 Python,附 Java 通用工厂方法
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的参数就行了。
