Thrift是一个跨语言的服务部署框架,最初由facebook开发用做系统内各语言之间的RPC(Remote Procedure Call Protocol,远程过程调用协议通信),2007年由facebook贡献到apache基金,08年5月进入apache孵化器。Thrift通过一个中间语言IDL(interface description language, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成不同语言的代码,并由生成的代码负责RPC协议层和传输层的实现。支持的语言有C++, Java, Python, PHP,Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml,Delphi等。
Thrift通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。用户在Thirft描述文件中声明自己的服务,这些服务经过编译后会生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提服务)便可以了。其中protocol(协议层, 定义数据传输格式,可以为二进制或者XML等)和transport(传输层,定义数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等)被用作运行时库。
Server 服务模型
Handler 数据处理接口
Processor 数据处理对象
Protocol 数据传输协议
Transport 数据传输方式
TSimpleServer – 简单的单线程服务模型,常用于测试
TThreadPoolServer – 多线程服务模型,使用标准的阻塞式IO。
TNonblockingServer – 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)
TBinaryProtocol – 二进制格式
TCompactProtocol – 压缩格式
TJSONProtocol – JSON格式
TSimpleJSONProtocol –提供JSON只写协议, 生成的文件很容易通过脚本语言解析。
TDebugProtocol – 使用易懂的可读的文本格式,以便于debug
TFileTransport:文件(日志)传输类,允许client将文件传给server,允许server将收到的数据写到文件中。
THttpTransport:采用Http传输协议进行数据传输
TSocket:采用TCPSocket进行数据传输
TZlibTransport:压缩后对数据进行传输,或者将收到的数据解压
登录官网下载(http://thrift.apache.org/),windows下载thrift-0.9.3.exe
放到某一路径下,例如:D:\ProgramFiles\thirft\thrift.exe(为了方便执行命令,将文件名改为thrift.exe)
环境变量Path值追加"D:\ProgramFiles\thirft"
打开cmd.exe,输入thrift –help,并回车可以看到帮助信息说明已安装成功
创建hello.thrift文件:
namespace java com.tongyin.thrift.demo
service HelloWorldService { string sayHello(1:string username) } |
将文件放到某路径下,例:D:\dev\hello.thrift
打开cmd.exe,并执行thrift -r --gen java hello.thrift
在D:\dev\gen-java目录下编译生成了java文件,文件所在的包com.tongyin.thrift.demo是hello.thrift文件定义的namespace,
再将生成的文件拷贝到项目中
拷贝编译生成的HelloWorldService.java到服务端项目
package com.tongyin.thrift.demo;
import org.apache.thrift.TException;
/** * 服务接口实现类 * @authorliulin * */ public class HelloWorldImpl implements HelloWorldService.Iface {
@Override public String sayHello(Stringusername)throws TException { return"Hi, " +username ; }
} |
package com.tongyin.thrift.demo;
import org.apache.thrift.TProcessor; import org.apache.thrift.protocol.TBinaryProtocol; import org.apache.thrift.server.TServer; import org.apache.thrift.server.TThreadPoolServer; import org.apache.thrift.transport.TServerSocket;
/** * thrift服务端 * @authorliulin * */ public class HelloServerDemo { //thrift服务端口号 public static final int SERVER_PORT = 7911;
public void startServer() { try { System.out.println("Thrift TThreadPoolServer start ....");
TProcessor tprocessor =new HelloWorldService.Processor new HelloWorldImpl());
TServerSocket serverTransport =new TServerSocket(SERVER_PORT); TThreadPoolServer.Args ttpsArgs = new TThreadPoolServer.Args( serverTransport); ttpsArgs.processor(tprocessor); ttpsArgs.protocolFactory(new TBinaryProtocol.Factory());
// 线程池服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。 TServer server =new TThreadPoolServer(ttpsArgs); server.serve();
} catch (Exceptione) { System.out.println("Server start error!!!"); e.printStackTrace(); } }
/** * @param args */ public static void main(String[] args) { HelloServerDemo server =new HelloServerDemo(); server.startServer(); }
} |
拷贝编译生成的HelloWorldService.java到客户端项目
package com.tongyin.thrift.demo;
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 org.apache.thrift.transport.TTransportException;
/** * thrift客户端 * @authorliulin * */ public class HelloClientDemo {
public static final String SERVER_IP ="localhost"; public static final int SERVER_PORT = 7911; public static final int TIMEOUT = 30000;
/** * * @param userName */ public void startClient(String userName) { TTransport transport =null; try { transport = new TSocket(SERVER_IP,SERVER_PORT,TIMEOUT); // 协议要和服务端一致 TProtocol protocol =new TBinaryProtocol(transport); HelloWorldService.Client client = new HelloWorldService.Client( protocol); transport.open(); String result =client.sayHello(userName); System.out.println("Thrify client result = " +result); } catch (TTransportExceptione) { e.printStackTrace(); } catch (TExceptione) { e.printStackTrace(); } finally { if (null !=transport) { transport.close(); } } }
/** * @param args */ public static void main(String[] args) { HelloClientDemo client =new HelloClientDemo(); client.startClient("Linda"); }
} |
运行HelloServerDemo,启动服务
执行HelloClientDemo,客户端获得服务端返回的数据
我采用基于httpclient的方式,将thrift与spring进行了集成,具体代码不再列出,只列出与测试相关的信息。
namespace java com.tongyin.thrift.demo
struct User { 1: required string uid;#注释:用户id 2: optional string name;#注释:用户名 3: optional string mobile;#注释:用户手机号 4: optional string email;#注释:用户邮箱 5: optional i32 partyId;#注释:用户微信分组id,无用 6: optional i32 sysId;#注释:用户关心系统,系统id }
service UserService { list } |
返回包含10000个实例的list
package com.tongyin.thrift.demo;
import java.util.ArrayList; import java.util.List;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.thrift.TException; import org.springframework.stereotype.Service;
@Service(value="userService") public class UserServiceImpl implements UserService.Iface{
@Override public List intuserNum = 10000; List User tempuser; for(inti=1;i<=userNum;i++){ tempuser = new User(); tempuser.setUid(String.valueOf(i)); tempuser.setName("name"+i); tempuser.setMobile("10086"+i); tempuser.setPartyId(i/10); tempuser.setSysId(1); users.add(tempuser); } returnusers; }
}
|
耗时(毫秒) |
并发量=1 |
并发量=50 |
并发量=200 |
第1次 |
615 |
1671 |
3273 |
第2次 |
68 |
918 |
3025 |
第3次 |
29 |
948 |
2973 |
第4次 |
31 |
1020 |
2875 |
第5次 |
32 |
1062 |
2938 |
第6次 |
61 |
915 |
3031 |
第7次 |
31 |
1001 |
2910 |
第8次 |
29 |
941 |
2958 |
第9次 |
28 |
915 |
2892 |
第10次 |
27 |
962 |
2893 |
平均值 |
95.1 |
1035.3 |
2976.8 |
2-10次平均 |
37.33 |
964.67 |
2943.89 |
WebService是常用的远程调用技术,也是一种RPC(Remote Procedure CallProtocol,远程过程调用协议通信),下面以Apache CXF为例,进行性能对比。
采用与thrift相同的字段
与thrift相同,返回包含10000个实例的list
package com.tongyin.weixin.webservice;
import java.util.ArrayList; import java.util.List;
import javax.jws.WebService;
import org.springframework.stereotype.Service;
import com.tongyin.weixin.vo.UserVo;
@WebService(serviceName="helloWs", endpointInterface="com.tongyin.weixin.webservice.ITestService") @Service("testService") public class TestService implements ITestService {
public List intuserNum = 10000; List UserVo tempvo; for(inti=1;i<=userNum;i++){ tempvo = new UserVo(); tempvo.setUid(String.valueOf(i)); tempvo.setName("name"+i); tempvo.setMobile("10086"+i); tempvo.setPartyId(i/10); tempvo.setSysId(1); vos.add(tempvo); } returnvos; }
}
|
耗时(毫秒) |
并发量=1 |
CXF并发量=50 |
CXF并发量=200 |
|||
Thrift |
CXF |
Thrift |
CXF |
Thrift |
CXF |
|
第1次 |
615 |
885 |
1671 |
2506 |
3273 |
8504 |
第2次 |
68 |
128 |
918 |
2831 |
3025 |
6758 |
第3次 |
29 |
46 |
948 |
2119 |
2973 |
8564 |
第4次 |
31 |
57 |
1020 |
2028 |
2875 |
6701 |
第5次 |
32 |
60 |
1062 |
2289 |
2938 |
6788 |
第6次 |
61 |
47 |
915 |
2234 |
3031 |
7959 |
第7次 |
31 |
53 |
1001 |
2178 |
2910 |
7757 |
第8次 |
29 |
48 |
941 |
2248 |
2958 |
8099 |
第9次 |
28 |
44 |
915 |
2205 |
2892 |
7266 |
第10次 |
27 |
46 |
962 |
1874 |
2893 |
8580 |
平均值 |
95.1 |
141.4 |
1035.3 |
2251.2 |
2976.8 |
7697.6 |
2-10次平均 |
37.33 |
58.78 |
964.67 |
2222.89 |
2943.89 |
7608 |
l 初次调用时,Thrift和CXF都需要进行一些初始化操作,耗时较长。Thrift耗时615毫秒,CXF耗时885毫秒,CXF比Thrift要多耗时270毫秒,是Thrift耗时的1.44倍;
l 在第一次调用完毕后,随后的调用中,性能都明显提升。Thrift在2-10次耗时的平均值为37.33,CXF在2-10次耗时的平均值为58.78,CXF平均耗时要比Thrift多21.5毫秒,是Thrift的1.57倍;
l 当有50个并发请求时,Thrift耗时1035.3毫秒,CXF耗时2251.2毫秒,CXF比Thrift多耗时1215.9,是Thrift耗时的2.17倍。
l 当有200个并发请求时,Thrift耗时2976.8毫秒,CXF耗时7697.6毫秒,CXF比Thrift多耗时4720.8,是Thrift耗时的2.58倍。
l 从以上对比结果来看,Thrift的性能比Apache CXF要好很多;
l Thrift支持的语言很多,可以很方便的进行跨语言远程调用,甚至可以直接与JavaScript进行交互;
l Thrift想要与Spring进行整合还需要编写很多代码,Apache CXF能够很容易的与spring进行整合;
l Thrift的相关资料比较少,开发难度大,Apache CXF有丰富的资料。
参考资料:
1. http://blog.sina.com.cn/s/blog_72995dcc0101gn82.html
2. http://blog.csdn.net/taizhenba/article/details/49149589
3. http://baike.baidu.com/link?url=Kn_cU65EdYIupMDdBiYn_zuopJXx_r0-zPAtvvh6jL0ts6UsSyAKXo_YYFpwNwoGvhCIB7CBxtwqrofyYjWzCK