thrift示例及性能测试

来源:互联网 发布:封闭式小区 知乎 编辑:程序博客网 时间:2024/05/21 22:01

1     简介

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等。


2     原理


2.1    架构

Thrift通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。用户在Thirft描述文件中声明自己的服务,这些服务经过编译后会生成相应语言的代码文件,然后用户实现服务(客户端调用服务,服务器端提服务)便可以了。其中protocol(协议层, 定义数据传输格式,可以为二进制或者XML等)和transport(传输层,定义数据传输方式,可以为TCP/IP传输,内存共享或者文件共享等)被用作运行时库。


2.2    基本概念

  • Thrift中的几个概念

              Server 服务模型

              Handler 数据处理接口

              Processor 数据处理对象

              Protocol 数据传输协议

              Transport 数据传输方式

 

  • 支持的服务模型[Server]

              TSimpleServer – 简单的单线程服务模型,常用于测试

              TThreadPoolServer – 多线程服务模型,使用标准的阻塞式IO。

              TNonblockingServer – 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式)

 

  • 支持的传输格式[Protocol]

              TBinaryProtocol – 二进制格式

              TCompactProtocol – 压缩格式

              TJSONProtocol – JSON格式

              TSimpleJSONProtocol –提供JSON只写协议, 生成的文件很容易通过脚本语言解析。

              TDebugProtocol – 使用易懂的可读的文本格式,以便于debug

 

  • 支持的通信方式(数据传输方式)[Transport]

              TFileTransport:文件(日志)传输类,允许client将文件传给server,允许server将收到的数据写到文件中。

              THttpTransport:采用Http传输协议进行数据传输

              TSocket:采用TCPSocket进行数据传输

              TZlibTransport:压缩后对数据进行传输,或者将收到的数据解压


3     简单示例


3.1    下载安装

登录官网下载(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,并回车可以看到帮助信息说明已安装成功


3.2    编写thrift文件,并编译

创建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,

再将生成的文件拷贝到项目中


3.3    服务端代码


3.3.1        HelloWorldService编译生成的thrift服务类

拷贝编译生成的HelloWorldService.java到服务端项目


3.3.2        HelloWorldImpl服务实现类

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 ;

      }

 

}


3.3.3        HelloServerDemo服务入口类

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<HelloWorldService.Iface>(

                            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();

      }

 

}


3.4    客户端代码


3.4.1        HelloWorldService编译生成的thrift服务类

拷贝编译生成的HelloWorldService.java到客户端项目


3.4.2        HelloClientDemo客户入口类

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");

      }

 

}


3.5    启动项目

运行HelloServerDemo,启动服务


执行HelloClientDemo,客户端获得服务端返回的数据



4     性能测试


4.1    测试准备

我采用基于httpclient的方式,将thrift与spring进行了集成,具体代码不再列出,只列出与测试相关的信息。


4.1.1        user.thrift配置文件

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<User> getAllUser();

}


4.1.2        服务实现类

返回包含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<User> getAllUser()throws TException {

           intuserNum = 10000;

           List<User> users = new ArrayList<User>();

           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;

      }

 

}

 


4.2    thrift测试结果

耗时(毫秒)

并发量=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


5     与CXF性能对比

WebService是常用的远程调用技术,也是一种RPC(Remote Procedure CallProtocol,远程过程调用协议通信),下面以Apache CXF为例,进行性能对比。


5.1    测试准备


5.1.1        VO

采用与thrift相同的字段


5.1.2        CXF服务实现类

与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<UserVo> getAllUser(){

           intuserNum = 10000;

           List<UserVo> vos = new ArrayList<UserVo>();

           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;

      }

 

}

 


5.2    对比结果

耗时(毫秒)

并发量=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倍。


5.3    总结

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



 

1 0