关于这个话题,网上有大量的文章;但是配置部分都讲得比较混乱,直接拷来并不能跑通。 本文以python和java分别作为服务端/客户端及其逆形式进行了演示,不求甚解,只求能用。 ## 定义好thrift文件,作为C/S的契约[^1] xxx.thrift *注意:以下代码都是手动混淆过的,仅大致示意写法,不保证内容正确* ```thrift namespace java com.x1.x2.x3.x4.x5.gen_java # 注意,如果是为java版本准备的,一定要写好package struct MyStruct1{ 1:string field1, 2:list field2, 3:optional list field3, # 注意java不支持optinal,生成的代码中只有必选项 } struct MyStruct2{ 1:list data } service MyService{ void f1(1:MyStruct1 s1) list f2(1:string field1, 2:MyStruct1 data) } ``` 将thrift文件分别编译成服务器端语言和客户端语言 *注意:自动生成的python代码中,可能有class A依赖class B,而A的代码却写到了同一个文件中B的下方的情况;这可能是thrift的一个BUG,手动调整代码顺序即可* ```bash mkdir gen_py gen_java # 默认名字是用短横线分隔(gen-py),不符合python的包命名要求;手动改成下划线 thrift -r --gen py -out gen_py xxx.thrift thrift -r --gen java -out ../../../../../../ xxx.thrift # 路径中有x1-x5,就要上6层 ``` ## 实现服务端逻辑 注意: * gen_py和gen_java文件夹的内容最好别改动;它们都只是作为辅助类库使用的 * 服务器端要自己写一个Handler类去override指定的方法 * 客户端只要绑定到指定的端口上调用即可 ### python版服务器端 先用自己的类来继承IFace,实现各个接口 ```python # thrift 相关的引用 from thrift.protocol import TBinaryProtocol from thrift.server import TServer from thrift.transport import TSocket, TTransport # import ... # 逻辑相关的引用 class MyHandler(MyService.Iface): def __init__(self): transport = TSocket.TSocket('hostname', 8095) # 最好用IP,稳一点 transport = TTransport.TBufferedTransport(transport) transport.open() # 注意这一行不能少 protocol = TBinaryProtocol.TBinaryProtocol(transport) # self.xxx = yyy 之类的初始化,省略 ... def f1(self, arg1): pass # ... ``` 然后写一个`server.py`来包裹handler并控制服务的起停 ```python from thrift.protocol import TBinaryProtocol from thrift.server import TServer from thrift.transport import TSocket, TTransport # sys.path.append('thrift/gen_py') # 可能需要把gen_py加到PYTHONPATH # import ... # 逻辑相关的引用 def main(): handler = MyHandler() processor = MyService.Processor(handler) transport = TSocket.TServerSocket('10.1.x.x', 8081) tfactory = TTransport.TBufferedTransportFactory() pfactory = TBinaryProtocol.TBinaryProtocolFactory() server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory) logger.info('---------- starting xxx server over thrift...') server.serve() logger.info('---------- shutting down xxx server over thrift...') if __name__ == '__main__': main() ``` ### java版的服务器端 同理,先写个Handler类来覆盖相应的method ```java package com.xxx.xxx.xxx // import ... public class MyHandler implements MyService.Iface { @Override public synchronized void f1(MyStruct1 arg1) throws TException { // ... } // ... } ``` 然后写个Server类来包裹Handler并控制服务的起停 ```java package com.x.x.x.xxx import ... // 逻辑相关的引用 import org.apache.thrift.server.TServer; import org.apache.thrift.server.TSimpleServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import java.io.IOException; import java.net.InetSocketAddress; public class MyServer { public static void main(String[] args) throws IOException, TTransportException { MyService.Processor processor = new MyService.Processor(new MyHandler()); TServerSocket serverTransport = new TServerSocket(new InetSocketAddress("1.2.3.4", 1234)); TServer server = new TSimpleServer(new TThreadPoolServer.Args(serverTransport).processor(processor)); System.out.println("Starting server..."); server.serve(); } } ``` ## 实现客户端逻辑 ### java版客户端 先把之前运行`thrift -r --gen java ...`命令生成的`gen_java`文件夹拷进来,作为一个package; 然后把Guava等其它各种类库写进maven/Gradle。 *插一句:如果用到读写wav文件的功能,可以把wavFile相关的类库[^2]下载了拷进来。* ```java package com.xxx.yyy; 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 ... public class App { public void work(List arg) { try { TTransport transport = new TSocket("1.2.3.4", 1234); // 注意两边都尽量用IP,比较稳一点 transport.open(); TProtocol protocol = new TBinaryProtocol(transport); MyService.Client client = new MyService.Client(protocol); // arg1 = ... client.f1(arg); // 多次调用 client.f2(...) transport.close(); System.out.println("----- client done"); } catch (TException ex) { ex.printStackTrace(); } // 更多catch ... } public static void main(String[] args) { App app = new App(); app.work(Lists.newArrayList("arm", "aunt", "autumn")); } } ``` ### python版的客户端写法 ```python from thrift.protocol import TBinaryProtocol from thrift.transport import TSocket, TTransport # 可能需要将gen_py加到PYTHONPATH # import 逻辑相关的模块 ... def main(): transport = TSocket.TSocket('1.2.3.4', 8081) transport = TTransport.TBufferedTransport(transport) transport.open() protocol = TBinaryProtocol.TBinaryProtocol(transport) client = MyService.Client(protocol) # arg = ... client.f1(arg) # 多次调用 client.f2(...) print('----- client done') if __name__ == '__main__': main() ``` [^1]: thrift文件写法参考:http://www.jianshu.com/p/0f4113d6ec4b [^2]: Java Wav File IO API:http://www.labbookpages.co.uk/audio/javaWavFiles.html