多语言协作工具thrift
1背景
如果讨论每个语言的特点,相信各个语言的程序员都能说得天花乱坠,争的面红耳赤。但是在如今大互联网时代,没有哪种语言是银弹,各种语言都各有所长。为了截取各家之长,各种跨语言通信框架涌现出来,thrift就是其中的一个典型代表。
thrift是基于socket或者http协议来进行通信的,所以适合分布式的服务器之间进行通信。众所周知,php中是无法使内存常驻的,java、c、c++之类的就可以;对于处理html页面,php擅长,对于数据库连接java更擅长,对于高并发处理node.js erlang更擅长。thrift将各者结合起来,做到扬长避短,而且支持的语言要远远多于刚才提到几种。
关于thrift的入门介绍可以参见http://www.javabloger.com/article/apache-thrift-architecture.html
2安装
下面以java和php为例介绍一下thrift的使用。
首先从https://dist.apache.org/repos/dist/release/thrift/0.9.0/thrift-0.9.0.tar.gz 下载最新的thrift源码和在windows下的工具https://dist.apache.org/repos/dist/release/thrift/0.9.0/thrift-0.9.0.exe。将thrift-0.9.0.tar.gz解压到任意目录,在里面新建目录bin,将thrift-0.9.0.exe拷贝到这个目录,重命名为thrift.exe,并且将这个bin目录加入环境变量,方便以后从命令行。
进入thrift解压目录下的lib/java目录,执行ant命令(关于ant的配置,请自行搜索,当然也可以下载随后给出的ant生成物),在生成的build目录中的libthrift-0.9.0.jar和build/lib目录中的所有jar包备份出来,方便建立java项目时引用。
注意php需要配置apc支持,如果没有安装,请先安装之。
3脚本编写
新建thrift数据定义文件test.txt内容如下:
namespace java com.whyun.thrift # 注释1 定义生成代码的命名空间,与你需要定义的package相对应。 namespace php test struct Blog { # 注释2.1 定义实体名称和数据结构,类似你业务逻辑中的pojo get/set 1: string topic # 注释2.2 参数类型可以参见 Thrift wiki 2: binary content 3: i64 createdTime 4: string id 5: string ipAddress 6: map<string,string> props } service ThriftCase { # 注释3 代码生成的类名,你的业务逻辑代码需要实现代码生成的ThriftCase.Iface接口 i32 testCase1(1:i32 num1, 2:i32 num2, 3:string num3) #注释4.1 方法名称和方法中的入参,入参类型参见wiki list<string> testCase2(1:map<string,string> num1) void testCase3() void testCase4(1:list<Blog> blog) # 注释4.2 list 是thrift中基本数据类型中的一种,list中包含的Blog对象是上面struct中定义的 }
在test.txt所在目录下运行thrift --gen java test.txt生成java代码,运行thrift --gen php test.txt来生成php代码,运行完成后,在当前目录下回生成gen-java和gen-php两个目录。
4java代码编写
新建java项目thrift-demo,在项目中新建目录lib,将刚才安装中生成的jar包都拷贝过来,并将其添加到classpath中,将之前生成的gen-java目录下生成代码拷贝到工程中,然后新建包com.whyun.thrift.business来编写我们的处理代码,首先实现sevice的处理类,如下:
package com.whyun.thrift.business; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.thrift.TException; import com.whyun.thrift.Blog; import com.whyun.thrift.ThriftCase.Iface; public class CaseImpl implements Iface { @Override public int testCase1(int num1, int num2, String num3) throws TException { // TODO Auto-generated method stub return 1; } @Override public List<String> testCase2(Map<String, String> num1) throws TException { List<String> list = new ArrayList<String>(); list.add("aaa"); list.add("bbb"); return list; } @Override public void testCase3() throws TException { System.out.println("testCase3"); } @Override public void testCase4(List<Blog> blog) throws TException { System.out.println("testCase4"); } }
代码片段4.1
接着编写server端程序:
package com.whyun.thrift.business; 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.server.TThreadPoolServer.Args; import org.apache.thrift.transport.TServerSocket; import org.apache.thrift.transport.TTransportException; import com.whyun.thrift.ThriftCase; import com.whyun.thrift.ThriftCase.Processor; public class Server { public void startServer() { try { TServerSocket serverTransport = new TServerSocket(1234); ThriftCase.Processor process = new Processor(new CaseImpl()); Factory portFactory = new TBinaryProtocol.Factory(true, true); Args args = new Args(serverTransport); args.processor(process); args.protocolFactory(portFactory); TServer server = new TThreadPoolServer(args); server.serve(); } catch (TTransportException e) { e.printStackTrace(); } } public static void main(String[] args) { Server server = new Server(); server.startServer(); } }
代码片段4.2
5php程序编写
在apache的web根目录中新建文件夹thrift,在其下新建classes目录。在将安装步骤中thrift解压目录下的lib/php/lib目录下Thrift文件夹拷贝到新建的classes目录下,再将脚本编写步骤中gen-php目录下test文件夹拷贝到classes目录中,将test文件夹下的ThriftCase.php改名为ThriftCaseClient.php,最终形成的目录结构如下:
图5.1
在thrift目录下新建package.php,编码如下:
<?php use Thrift\Transport\TSocket as TSocket; use Thrift\Transport\TBufferedTransport as TBufferedTransport; use Thrift\Protocol\TBinaryProtocol as TBinaryProtocol; use test\ThriftCaseClient as ThriftCaseClient; // autoload function function __autoload($class) { // convert namespace to full file path $class = 'classes/'.str_replace('\\', '/', $class). '.php'; require_once($class); } $thriftHost = '127.0.0.1'; //Thrift接口服务器IP $thriftPort = 1234;//Thrift接口服务器端口 $socket = new TSocket($thriftHost, $thriftPort); $socket->setSendTimeout(10000); $socket->setRecvTimeout(20000); $transport = new TBufferedTransport($socket); $protocol = new TBinaryProtocol($transport); $client = new ThriftCaseClient($protocol); $transport->open(); $socket->setDebug(TRUE); echo $client->testCase1(1,2,3).'<br />'; var_dump( $client->testCase2(array('xxx'=>'1','ddd'=>'2'))); ?>
代码片段5.1
6运行
在eclipse中运行代码片段4.2,然后在浏览器中运行代码片段5.1,会输出(这些输出其实是由于在代码片段4.1中随便写的):
1 array(2) { [0]=> string(3) "aaa" [1]=> string(3) "bbb" }
以上java和php代码已上传到csdn。