Thrift 指导文档 [0.9.2]

0 前言

Thrift 是一个软件框架(远程过程调用框架),用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引 擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。

thrift最初由facebook开发,07年四月开放源码,08年5月进入apache孵化器,现在是 Apache 基金会的顶级项目

thrift允许你定义一个简单的定义文件中的数据类型和服务接口,以作为输入文件,编译器生成代码用来方便地生成RPC客户端和服务器通信的无缝跨编程语言。。

著名的 Key-Value 存储服务器 Cassandra 就是使用 Thrift 作为其客户端API的。

 

1 编译环境

参考 http://thrift.apache.org/docs/install/centos 

一步一步来就行了。 

看到如下的提示就OK了。

102354_2Yxl_1382024.png

 

2 脚本文件

2.1 编写

文件清单:  Hello.thrift

namespace java service.demo

 service Hello{

  string helloString(1:string para)

  i32 helloInt(1:i32 para)

  bool helloBoolean(1:bool para)

  void helloVoid()

  string helloNull()

 }

 

说明:

定义了服务Hello5个方法。

方法格式: 返回类型  方法名     参数列表

参数格式:   参数序号  参数类型   参数名

 

2.2 编译

使用Thrift工具编译Hello.thrift,就可以生成对应的Hello.java文件,该文件包含了在 Hello.thrift 文件中描述的服务 Hello 的接口定义,即 Hello.Iface 接口,以及服务调用的底层通信细节,包括客户端的调用逻辑 Hello.Client 以及服务器端的处理逻辑 Hello.Processor,用于构建客户端和服务器端的功能。

 

具体编译过程:

102858_2XUo_1382024.png

 

可以看到,编译成功,生成了一个文件夹gen-java.

我们来看看这个文件夹下面有什么。

102950_yGaO_1382024.png

 

可以看到产生了一个文件

103042_6REp_1382024.png

 

3 Thrift例子

3.1 新建工程

新建一个java project.

然后按照http://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/

的做法,在工程里引入HelloServiceImpl.javaHelloServiceServer.javaHelloServiceClient.java

不要忘了要引入根据脚本生成的那个Hello.java.

这样,工程里就有4个文件。

 

3.2 引入thrift源码

此时,eclipse肯定报错,因为我们还没有引入Thrift的源码。

下载地址

http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.2/thrift-0.9.2.tar.gz

解压缩后,源码位置在

103138_cpop_1382024.png

 

将文件夹复制到eclipse里即可。

 

3.3 依赖包

然后加入所依赖的包

103225_npLM_1382024.png

 

3.4 兼容性

最后还有最后一个问题,就是HelloServiceServer报错。

提示

103315_VsvR_1382024.png

 

 

这个构造方法不存在,修改成下面的

TThreadPoolServer.Args tArgs = new TThreadPoolServer.Args(serverTransport);

            tArgs.protocolFactory(proFactory);

            tArgs.processor(processor);

            TServer server = new TThreadPoolServer(tArgs);

 

3.5 运行

然后开始运行整个程序。

启动服务器:

103357_CBdf_1382024.png

 

 

再启动client

程序顺利执行,client的方法没有输出。

 

我们在client里添加代码如下

String str=client.helloString("hello,Thrift");

            System.out.println("resp:---"+str);

 

观察client输出

103442_T8Mg_1382024.png

 

 至此,Thrift顺利完成运行过程。

下面我们需要来通过研究这个例子的源码来研究Thrift的运行原理。

4 异步IO改造

为了研究Thrift源码,上面的例子需要修改,因为上面的例子没有用到很过高级功能,所以我们需要改造成高级功能,这里参考了文章 http://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/ 

 

HelloServiceAsyncServer.java修改后的源码如下所示: 

package service.server;

 

import org.apache.thrift.TProcessor;

import org.apache.thrift.protocol.TBinaryProtocol;

import org.apache.thrift.server.THsHaServer;

import org.apache.thrift.server.TServer;

import org.apache.thrift.server.TThreadedSelectorServer;

import org.apache.thrift.transport.TFramedTransport;

import org.apache.thrift.transport.TNonblockingServerSocket;

import org.apache.thrift.transport.TNonblockingServerTransport;

import org.apache.thrift.transport.TTransportException;

 

import service.demo.Hello;

import service.demo.HelloServiceImpl;

 

public class HelloServiceAsyncServer {

   

    public static void main(String[] args) {

        try {

          

            // TServerSocket serverTransport = new TServerSocket(7911);

            TNonblockingServerTransport serverTransport = new TNonblockingServerSocket(7911);

            TFramedTransport.Factory transportFactory=new TFramedTransport.Factory();

            //TNonblockingTransport.

            // 设置协议工厂为 TBinaryProtocol.Factory

            TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();

            // 关联处理器与 Hello 服务的实现

            TProcessor processor = new Hello.Processor(new HelloServiceImpl());

 

            TThreadedSelectorServer.Args tArgs = new TThreadedSelectorServer.Args(serverTransport);

            tArgs.transportFactory(transportFactory);

            tArgs.protocolFactory(protocolFactory);

            tArgs.processor(processor);

            THsHaServer a;

 

            // TServer server = new TThreadPoolServer(tArgs);

            TServer server = new TThreadedSelectorServer(tArgs);

            System.out.println("Start server on port 7911...");

            server.serve();

        } catch (TTransportException e) {

            e.printStackTrace();

        }

    }

}

 

 HelloServiceSyncClient.java修改后

package service.client;

 

import org.apache.thrift.protocol.TBinaryProtocol;

import org.apache.thrift.protocol.TProtocol;

import org.apache.thrift.transport.TFramedTransport;

import org.apache.thrift.transport.TSocket;

import org.apache.thrift.transport.TTransport;

 

import service.demo.Hello;

 

public class HelloServiceSyncClient {

 

    public static void main(String[] args) throws Exception {

 

        // 设置传输通道,对于非阻塞服务,需要使用TFramedTransport,它将数据分块发送

        TTransport transport = new TFramedTransport(new TSocket("192.168.56.102", 7911));

        transport.open(); 

        // 使用二进制协议

        TProtocol protocol = new TBinaryProtocol(transport); 

        // 创建Client

        Hello.Client client = new Hello.Client(protocol); 

        long start = System.currentTimeMillis();

        for (int i = 0; i < 1; i++) {

            System.out.println("client.helloBoolean(false)---"+client.helloBoolean(false));

            //System.out.println("client.helloInt(111)---"+client.helloInt(111));

            //client.helloNull();

            System.out.println("client.helloString(\"360buy\")---"+client.helloString("360buy"));

            client.helloVoid();

        }

        System.out.println("耗时:" + (System.currentTimeMillis() - start)); 

        // 关闭资源

        transport.close();

    }

}

  

Hello.java & HelloServiceImpl.java 内容保持不变。

读者可自行分析修改过的地方。

运行之,看看是否OK。 

 

5 调试环境Linux

Java提供了强大的jdb工具来调试源码,可以执行程序,进入函数,以及打印当前值。

103601_s9tS_1382024.png

 

6 原理分析

6.1 线程模型

Thrift的线程模型是怎样的,下面开始讲解

先看代码段1

  @Override

  protected boolean startThreads() {

    try {

      for (int i = 0; i < args.selectorThreads; ++i) {

        selectorThreads.add(new SelectorThread(args.acceptQueueSizePerThread));

      }

      acceptThread = new AcceptThread((TNonblockingServerTransport) serverTransport_,

        createSelectorThreadLoadBalancer(selectorThreads));

      for (SelectorThread thread : selectorThreads) {

        thread.start();

      }

      acceptThread.start();

      return true;

    } catch (IOException e) {

      LOGGER.error("Failed to start threads!", e);

      return false;

    }

  }

 

可以看到这里启动了若干线程。

 

再看代码段2

/**

   * Create the server with the specified Args configuration

   */

  public TThreadedSelectorServer(Args args) {

    super(args);

    args.validate();

    invoker = args.executorService == null ? createDefaultExecutor(args) : args.executorService;

    this.args = args;

  } 

  /**

   * Helper to create the invoker if one is not specified

   */

  protected static ExecutorService createDefaultExecutor(Args options) {

    return (options.workerThreads > 0) ? Executors.newFixedThreadPool(options.workerThreads) : null;

  }

 所以,总的线程图如下图所示:

103815_sGL9_1382024.png

 

 

 

6

3

Socket

6.2 处理模型

 

 103926_huTM_1382024.png

 

 

 

1)Accept线程通过accept获取一个client.

2)Accept线程通过一定的负载策略(比如轮询)发给某个Selector线程。

3)Selector线程读取新建的Client,注册它的read事件。

4)Selector线程轮询所有注册的Socket的读写事件,当读条件满足时,触发Client的读取事件,拿到一个完整的Frame,封装成Runnable对象,通过(4.1)抛给ExecutorService线程组。同时屏蔽此socketread事件。

5)ExecutorService执行完毕后,保存执行结果在此Runnable对象里,然后返回给Selector线程。

6)Selector线程检查自己的某个写就绪队列,读取,然后注册对应SocketChannelwrite事件。

7)当写条件满足时,会在4中触发写事件,然后将内容发给远程client,发送完毕后,会屏蔽socket的写事件,注册读事件。这样重新来一个新的循环。

 

 

7 客户端连接池

可以采用第三方的连接池组件.

http://www.cnblogs.com/lihaozy/archive/2013/04/22/3035113.html

http://www.cnblogs.com/51cto/archive/2010/08/18/thrift_connection_pool.html

8 集群

方案采用KeepAlive + HaProxy

 

9 序列化协议格式简介

104045_GRYX_1382024.png

 10 关于Thrift的优缺点讨论

http://www.zhihu.com/question/20189791

参考文档

1 http://www.ibm.com/developerworks/cn/java/j-lo-apachethrift/

2 http://dongxicheng.org/search-engine/thrift-guide/

3 http://m.blog.csdn.net/blog/tianwei7518/44115297 (线程模型选择)

4 http://blog.csdn.net/m13321169565/article/details/7836006

5 http://blog.csdn.net/azhao_dn/article/details/8898610 [到底选择哪种网络模型?看这篇文章就可以了,最终定论]

6 http://www.codelast.com/?p=4824

 

 

 

 

 

 

 

 

 

 

 

 

 

转载于:https://my.oschina.net/qiangzigege/blog/495920

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值