thrift学习笔记(一) thrift简介及第一个helloword程序

简介

facebook开源的RPC框架,秉承了Facebook一贯的只管拉屎不管擦屁股的作风.

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

Thrift最初由facebook开发,07年四月开放源码,08年5月进入apache孵化器。thrift允许你定义一个简单的定义文件中的数据类型和服务接口。以作为输入文件,编译器生成代码用来方便地生成RPC客户端和服务器通信的无缝跨编程语言。

Windows下安装thrift

  1. 到官网下载Windows版本的编译器(thrift-0.9.3.exe):http://thrift.apache.org/download
  2. 下载完成后放到【C:\Program Files\Thrift】目录下,并将名字改为【thrift.exe】。注:这里改名只是为了敲命令行方便
  3. 将目录【C:\Program Files\Thrift】添加到环境变量即可

到此thrift安装完成,简单吧,我们来验证一下。

编写helloword服务接口描述文件代码:

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

命名为【Hello.thrift】放到【D:\workSpaceIdea\thrift】目录下,执行命令【thrift –gen java Hello.thrift】生成java文件:

创建第一个thrift程序

创建一个maven项目

目录结构:

pom 文件:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.lbl</groupId>
    <artifactId>thrift.demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>0.9.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.12</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

注:Hello.java是通过thrift自动生成的

HelloServiceImpl.java是实现类:

package service.demo;

import org.apache.thrift.TException;
 public class HelloServiceImpl implements Hello.Iface {
    @Override
 public boolean helloBoolean(boolean para) throws TException {
        return para;
  }
    @Override
 public int helloInt(int para) throws TException {
        try {
            Thread.sleep(20000);
 } catch (InterruptedException e) {
            e.printStackTrace();
 }
        return para;
 }
    @Override
 public String helloNull() throws TException {
        return null;
 }
    @Override
 public String helloString(String para) throws TException {
        System.out.println("入参:" + para);
 return "hello  " + para;
  }
    @Override
 public void helloVoid() throws TException {
        System.out.println("Hello World");
 }
 }
 ```
使用线程池模式的server

HelloServiceServer.java
```java
package server;

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;
import org.apache.thrift.transport.TTransportException;
import service.demo.Hello;
import service.demo.HelloServiceImpl;

public class HelloServiceServer {
    /**
     * 启动 Thrift 服务器
 * @param args
 */
 public static void main(String[] args) {
        try {
            // 设置服务端口为 7911
  TServerSocket serverTransport = new TServerSocket(7911);
  // 设置协议工厂为 TBinaryProtocol.Factory
  TBinaryProtocol.Factory proFactory = new TBinaryProtocol.Factory();
  // 关联处理器与 Hello 服务的实现
  TProcessor processor = new Hello.Processor(new HelloServiceImpl());
  TThreadPoolServer.Args args1 = new TThreadPoolServer.Args(serverTransport);
  args1.processor(processor);
  args1.protocolFactory(proFactory);

  TServer server = new TThreadPoolServer(args1);
  System.out.println("Start server on port 7911...");
  server.serve();
  } catch (TTransportException e) {
            e.printStackTrace();
  }
    }
 }

<div class="se-preview-section-delimiter"></div>

HelloServiceClient.java

运行

运行server以后,运行client,会在server的console控制台看到输出内容

注意:

客户端和服务端要使用同一中 Protocol 和 Transport,否则会抛出异常

TNonblockingServer 服务模型 的 server

HelloServiceServer1.java 展开原码

HelloServiceClient1.java 展开原码

我在开发中遇到的错误及解决办法

java.lang.NoSuchMethodError: org.apache.thrift.EncodingUtils.setBit(BIZ)B

出现这个问题的原因是dubbox本身集成的thrift版本是0.8.0,我添加的版本是0.9.3导致的,解决方法:去掉对0.8.0版本的依赖

这个问题是因为我thrift的IDL描述文件多了个逗号,在thrift生产java文件的时候没有报错,但是在dubbox运行的时候就出错了。

IDL文件简介

基本类型:

bool:布尔值,true 或 false,对应 Java 的 boolean

byte:8 位有符号整数,对应 Java 的 byte

i16:16 位有符号整数,对应 Java 的 short

i32:32 位有符号整数,对应 Java 的 int

i64:64 位有符号整数,对应 Java 的 long

double:64 位浮点数,对应 Java 的 double

string:utf-8编码的字符串,对应 Java 的 String

结构体类型:

struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean

容器类型:

list:对应 Java 的 ArrayList

set:对应 Java 的 HashSet

map:对应 Java 的 HashMap

异常类型:

exception:对应 Java 的 Exception

服务类型:

service:对应服务的类

数据传输协议

注:不同版本稍有区别

TBinaryProtocol : 二进制格式.

TCompactProtocol : 压缩格式

TJSONProtocol : JSON格式

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

数据传输格式 类型 优点 缺点

Xml 文本

1、良好的可读性

2、序列化的数据包含完整的结构

3、调整不同属性的顺序对序列化/反序列化不影响

1、数据传输量大

2、不支持二进制数据类型

Json 文本

1、良好的可读性

2、调整不同属性的顺序对序列化/反序列化不影响

1、丢弃了类型信息, 比如”price”:100, 对price类型是int/double解析有二义性

2、不支持二进制数据类型

Thrift 二进制 高效

1、不宜读

2、向后兼容有一定的约定限制,采用id递增的方式标识并以optional修饰来添加

Google Protobuf 二进制 高效

1、不宜读

2、向后兼容有一定的约定限制

服务模型

Thrif 提供网络模型:单线程、多线程、事件驱动。从另一个角度划分为:阻塞服务模型、非阻塞服务模型。

阻塞服务

TSimpleServer

TThreadPoolServer

非阻塞服务模型

TNonblockingServer

THsHaServer

TThreadedSelectorServer

1、TSimpleServer

TSimpleServer实现是非常的简单,循环监听新请求的到来并完成对请求的处理,是个单线程阻塞模型。由于是一次只能接收和处理一个socket连接,效率比较低,在实际开发过程中很少用到它。

2、TThreadPoolServer

ThreadPoolServer为解决了TSimpleServer不支持并发和多连接的问题, 引入了线程池。但仍然是多线程阻塞模式即实现的模型是One Thread Per Connection。

线程池采用能线程数可伸缩的模式,线程池中的队列采用同步队列(SynchronousQueue)。

ThreadPoolServer拆分了监听线程(accept)和处理客户端连接的工作线程(worker), 监听线程每接到一个客户端, 就投给线程池去处理。

TThreadPoolServer模式优点:

线程池模式中,数据读取和业务处理都交由线程池完成,主线程只负责监听新连接,因此在并发量较大时新连接也能够被及时接受。线程池模式比较适合服务器端能预知最多有多少个客户端并发的情况,这时每个请求都能被业务线程池及时处理,性能也非常高。

TThreadPoolServer模式缺点:

线程池模式的处理能力受限于线程池的工作能力,当并发请求数大于线程池中的线程数时,新请求也只能排队等待。

3、TNonblockingServer

TNonblockingServer采用单线程非阻塞(NIO)的模式, 借助Channel/Selector机制, 采用IO事件模型来处理。所有的socket都被注册到selector中,在一个线程中通过seletor循环监控所有的socket,每次selector结束时,处理所有的处于就绪状态的socket,对于有数据到来的socket进行数据读取操作,对于有数据发送的socket则进行数据发送,对于监听socket则产生一个新业务socket并将其注册到selector中。

select代码里对accept/read/write等IO事件进行监控和处理, 唯一可惜的这个单线程处理. 当遇到handler里有阻塞的操作时, 会导致整个服务被阻塞住。

TNonblockingServer模式优点:

相比于TSimpleServer效率提升主要体现在IO多路复用上,TNonblockingServer采用非阻塞IO,同时监控多个socket的状态变化;

TNonblockingServer模式缺点:

TNonblockingServer模式在业务处理上还是采用单线程顺序来完成,在业务处理比较复杂、耗时的时候,例如某些接口函数需要读取数据库执行时间较长,此时该模式效率也不高,因为多个调用请求任务依然是顺序一个接一个执行。

4、THsHaServer

THsHaServer类是TNonblockingServer类的子类,为解决TNonblockingServer的缺点, THsHa引入了线程池去处理, 其模型把读写任务放到线程池去处理即多线程非阻塞模式。HsHa是: Half-sync/Half-async的处理模式, Half-aysnc是在处理IO事件上(accept/read/write io), Half-sync用于handler对rpc的同步处理上。因此可以认为THsHaServer半同步半异步。

THsHaServer的优点:

与TNonblockingServer模式相比,THsHaServer在完成数据读取之后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操作,效率大大提升;

THsHaServer的缺点:

主线程需要完成对所有socket的监听以及数据读写的工作,当并发请求数较大时,且发送数据量较多时,监听socket上新连接请求不能被及时接受。

5、TThreadedSelectorServer

TThreadedSelectorServer是大家广泛采用的服务模型,其多线程服务器端使用非堵塞式I/O模型,是对TNonblockingServer的扩充, 其分离了Accept和Read/Write的Selector线程, 同时引入Worker工作线程池。

(1)一个AcceptThread线程对象,专门用于处理监听socket上的新连接;

(2)若干个SelectorThread对象专门用于处理业务socket的网络I/O操作,所有网络数据的读写均是有这些线程来完成;

(3)一个负载均衡器SelectorThreadLoadBalancer对象,主要用于AcceptThread线程接收到一个新socket连接请求时,决定将这个新连接请求分配给哪个SelectorThread线程。

(4)一个ExecutorService类型的工作线程池,在SelectorThread线程中,监听到有业务socket中有调用请求过来,则将请求读取之后,交个ExecutorService线程池中的线程完成此次调用的具体执行

MainReactor就是Accept线程, 用于监听客户端连接, SubReactor采用IO事件线程(多个), 主要负责对所有客户端的IO读写事件进行处理. 而Worker工作线程主要用于处理每个rpc请求的handler回调处理(这部分是同步的)。因此其也是Half-Sync/Half-Async(半异步-半同步)的 。
TThreadedSelectorServer模式对于大部分应用场景性能都不会差,因为其有一个专门的线程AcceptThread用于处理新连接请求,因此能够及时响应大量并发连接请求;另外它将网络I/O操作分散到多个SelectorThread线程中来完成,因此能够快速对网络I/O进行读写操作,能够很好地应对网络I/O较多的情况。
HelloServiceClient.java
```java
package client;

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;
import service.demo.Hello;

 public class HelloServiceClient {
 /**
     * 调用 Hello 服务
 * @param args
 */
 public static void main(String[] args) {
        try {
            // 设置调用的服务地址为本地,端口为 7911
 TTransport transport = new TSocket("localhost", 7911);
 transport.open();
 // 设置传输协议为 TBinaryProtocol
 TProtocol protocol = new TBinaryProtocol(transport);
 Hello.Client client = new Hello.Client(protocol);
 // 调用服务的 helloVoid 方法
//            client.helloVoid();
  String resp = client.helloString("wertyuiopsdfghjkl;");
  System.out.println(resp);
  transport.close();
 } catch (TTransportException e) {
            e.printStackTrace();
 } catch (TException e) {
            e.printStackTrace();
 }
    }
 }

<div class="se-preview-section-delimiter"></div>

运行

运行server以后,运行client,会在server的console控制台看到输出内容

注意:

客户端和服务端要使用同一中 Protocol 和 Transport,否则会抛出异常

TNonblockingServer 服务模型 的 server

HelloServiceServer1.java

“`java

HelloServiceClient1.java 展开原码

我在开发中遇到的错误及解决办法

java.lang.NoSuchMethodError: org.apache.thrift.EncodingUtils.setBit(BIZ)B

出现这个问题的原因是dubbox本身集成的thrift版本是0.8.0,我添加的版本是0.9.3导致的,解决方法:去掉对0.8.0版本的依赖

这个问题是因为我thrift的IDL描述文件多了个逗号,在thrift生产java文件的时候没有报错,但是在dubbox运行的时候就出错了。

IDL文件简介

基本类型:

bool:布尔值,true 或 false,对应 Java 的 boolean

byte:8 位有符号整数,对应 Java 的 byte

i16:16 位有符号整数,对应 Java 的 short

i32:32 位有符号整数,对应 Java 的 int

i64:64 位有符号整数,对应 Java 的 long

double:64 位浮点数,对应 Java 的 double

string:utf-8编码的字符串,对应 Java 的 String

结构体类型:

struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean

容器类型:

list:对应 Java 的 ArrayList

set:对应 Java 的 HashSet

map:对应 Java 的 HashMap

异常类型:

exception:对应 Java 的 Exception

服务类型:

service:对应服务的类

数据传输协议

注:不同版本稍有区别

TBinaryProtocol : 二进制格式.

TCompactProtocol : 压缩格式

TJSONProtocol : JSON格式

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

数据传输格式 类型 优点 缺点

Xml 文本

1、良好的可读性

2、序列化的数据包含完整的结构

3、调整不同属性的顺序对序列化/反序列化不影响

1、数据传输量大

2、不支持二进制数据类型

Json 文本

1、良好的可读性

2、调整不同属性的顺序对序列化/反序列化不影响

1、丢弃了类型信息, 比如”price”:100, 对price类型是int/double解析有二义性

2、不支持二进制数据类型

Thrift 二进制 高效

1、不宜读

2、向后兼容有一定的约定限制,采用id递增的方式标识并以optional修饰来添加

Google Protobuf 二进制 高效

1、不宜读

2、向后兼容有一定的约定限制

服务模型

Thrif 提供网络模型:单线程、多线程、事件驱动。从另一个角度划分为:阻塞服务模型、非阻塞服务模型。

阻塞服务

TSimpleServer

TThreadPoolServer

非阻塞服务模型

TNonblockingServer

THsHaServer

TThreadedSelectorServer

1、TSimpleServer

TSimpleServer实现是非常的简单,循环监听新请求的到来并完成对请求的处理,是个单线程阻塞模型。由于是一次只能接收和处理一个socket连接,效率比较低,在实际开发过程中很少用到它。

2、TThreadPoolServer

ThreadPoolServer为解决了TSimpleServer不支持并发和多连接的问题, 引入了线程池。但仍然是多线程阻塞模式即实现的模型是One Thread Per Connection。

线程池采用能线程数可伸缩的模式,线程池中的队列采用同步队列(SynchronousQueue)。

ThreadPoolServer拆分了监听线程(accept)和处理客户端连接的工作线程(worker), 监听线程每接到一个客户端, 就投给线程池去处理。

TThreadPoolServer模式优点:

线程池模式中,数据读取和业务处理都交由线程池完成,主线程只负责监听新连接,因此在并发量较大时新连接也能够被及时接受。线程池模式比较适合服务器端能预知最多有多少个客户端并发的情况,这时每个请求都能被业务线程池及时处理,性能也非常高。

TThreadPoolServer模式缺点:

线程池模式的处理能力受限于线程池的工作能力,当并发请求数大于线程池中的线程数时,新请求也只能排队等待。

3、TNonblockingServer

TNonblockingServer采用单线程非阻塞(NIO)的模式, 借助Channel/Selector机制, 采用IO事件模型来处理。所有的socket都被注册到selector中,在一个线程中通过seletor循环监控所有的socket,每次selector结束时,处理所有的处于就绪状态的socket,对于有数据到来的socket进行数据读取操作,对于有数据发送的socket则进行数据发送,对于监听socket则产生一个新业务socket并将其注册到selector中。

select代码里对accept/read/write等IO事件进行监控和处理, 唯一可惜的这个单线程处理. 当遇到handler里有阻塞的操作时, 会导致整个服务被阻塞住。

TNonblockingServer模式优点:

相比于TSimpleServer效率提升主要体现在IO多路复用上,TNonblockingServer采用非阻塞IO,同时监控多个socket的状态变化;

TNonblockingServer模式缺点:

TNonblockingServer模式在业务处理上还是采用单线程顺序来完成,在业务处理比较复杂、耗时的时候,例如某些接口函数需要读取数据库执行时间较长,此时该模式效率也不高,因为多个调用请求任务依然是顺序一个接一个执行。

4、THsHaServer

THsHaServer类是TNonblockingServer类的子类,为解决TNonblockingServer的缺点, THsHa引入了线程池去处理, 其模型把读写任务放到线程池去处理即多线程非阻塞模式。HsHa是: Half-sync/Half-async的处理模式, Half-aysnc是在处理IO事件上(accept/read/write io), Half-sync用于handler对rpc的同步处理上。因此可以认为THsHaServer半同步半异步。

THsHaServer的优点:

与TNonblockingServer模式相比,THsHaServer在完成数据读取之后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操作,效率大大提升;

THsHaServer的缺点:

主线程需要完成对所有socket的监听以及数据读写的工作,当并发请求数较大时,且发送数据量较多时,监听socket上新连接请求不能被及时接受。

5、TThreadedSelectorServer

TThreadedSelectorServer是大家广泛采用的服务模型,其多线程服务器端使用非堵塞式I/O模型,是对TNonblockingServer的扩充, 其分离了Accept和Read/Write的Selector线程, 同时引入Worker工作线程池。

(1)一个AcceptThread线程对象,专门用于处理监听socket上的新连接;

(2)若干个SelectorThread对象专门用于处理业务socket的网络I/O操作,所有网络数据的读写均是有这些线程来完成;

(3)一个负载均衡器SelectorThreadLoadBalancer对象,主要用于AcceptThread线程接收到一个新socket连接请求时,决定将这个新连接请求分配给哪个SelectorThread线程。

(4)一个ExecutorService类型的工作线程池,在SelectorThread线程中,监听到有业务socket中有调用请求过来,则将请求读取之后,交个ExecutorService线程池中的线程完成此次调用的具体执行

MainReactor就是Accept线程, 用于监听客户端连接, SubReactor采用IO事件线程(多个), 主要负责对所有客户端的IO读写事件进行处理. 而Worker工作线程主要用于处理每个rpc请求的handler回调处理(这部分是同步的)。因此其也是Half-Sync/Half-Async(半异步-半同步)的 。
TThreadedSelectorServer模式对于大部分应用场景性能都不会差,因为其有一个专门的线程AcceptThread用于处理新连接请求,因此能够及时响应大量并发连接请求;另外它将网络I/O操作分散到多个SelectorThread线程中来完成,因此能够快速对网络I/O进行读写操作,能够很好地应对网络I/O较多的情况。

## 运行
运行server以后,运行client,会在server的console控制台看到输出内容
![这里写图片描述](http://img.blog.csdn.net/20170123111820299?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1YmVubG9uZzAwNw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![这里写图片描述](http://img.blog.csdn.net/20170123111829393?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGl1YmVubG9uZzAwNw==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
注意:
**客户端和服务端要使用同一中 Protocol 和 Transport,否则会抛出异常**
TNonblockingServer 服务模型 的 server
HelloServiceServer1.java
```java
package server;

import org.apache.thrift.TProcessor;
import org.apache.thrift.protocol.TCompactProtocol;
import org.apache.thrift.server.TNonblockingServer;
import org.apache.thrift.server.TServer;
import org.apache.thrift.transport.TFramedTransport;
import org.apache.thrift.transport.TNonblockingServerSocket;
import org.apache.thrift.transport.TTransportException;
import service.demo.Hello;
import service.demo.HelloServiceImpl;

public class HelloServiceServer1 {

    public static final int SERVER_PORT = 8090;

  /**
     * 启动 Thrift 服务器
 * @param args
 */
 public static void main(String[] args) {
        try {
            System.out.println("HelloWorld TNonblockingServer start ....");

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

  TNonblockingServerSocket tnbSocketTransport = new TNonblockingServerSocket(SERVER_PORT);
  TNonblockingServer.Args tnbArgs = new TNonblockingServer.Args(tnbSocketTransport);
  tnbArgs.processor(tprocessor);
  tnbArgs.transportFactory(new TFramedTransport.Factory());
  tnbArgs.protocolFactory(new TCompactProtocol.Factory());

  // 使用非阻塞式IO,服务端和客户端需要指定TFramedTransport数据传输的方式
  TServer server = new TNonblockingServer(tnbArgs);
  server.serve();
  } catch (TTransportException e) {
            e.printStackTrace();
  }
    }
 }

<div class="se-preview-section-delimiter"></div>

HelloServiceClient1.java

“`java

我在开发中遇到的错误及解决办法

java.lang.NoSuchMethodError: org.apache.thrift.EncodingUtils.setBit(BIZ)B

出现这个问题的原因是dubbox本身集成的thrift版本是0.8.0,我添加的版本是0.9.3导致的,解决方法:去掉对0.8.0版本的依赖

这个问题是因为我thrift的IDL描述文件多了个逗号,在thrift生产java文件的时候没有报错,但是在dubbox运行的时候就出错了。

IDL文件简介

基本类型:

bool:布尔值,true 或 false,对应 Java 的 boolean

byte:8 位有符号整数,对应 Java 的 byte

i16:16 位有符号整数,对应 Java 的 short

i32:32 位有符号整数,对应 Java 的 int

i64:64 位有符号整数,对应 Java 的 long

double:64 位浮点数,对应 Java 的 double

string:utf-8编码的字符串,对应 Java 的 String

结构体类型:

struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean

容器类型:

list:对应 Java 的 ArrayList

set:对应 Java 的 HashSet

map:对应 Java 的 HashMap

异常类型:

exception:对应 Java 的 Exception

服务类型:

service:对应服务的类

数据传输协议

注:不同版本稍有区别

TBinaryProtocol : 二进制格式.

TCompactProtocol : 压缩格式

TJSONProtocol : JSON格式

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

数据传输格式 类型 优点 缺点

Xml 文本

1、良好的可读性

2、序列化的数据包含完整的结构

3、调整不同属性的顺序对序列化/反序列化不影响

1、数据传输量大

2、不支持二进制数据类型

Json 文本

1、良好的可读性

2、调整不同属性的顺序对序列化/反序列化不影响

1、丢弃了类型信息, 比如”price”:100, 对price类型是int/double解析有二义性

2、不支持二进制数据类型

Thrift 二进制 高效

1、不宜读

2、向后兼容有一定的约定限制,采用id递增的方式标识并以optional修饰来添加

Google Protobuf 二进制 高效

1、不宜读

2、向后兼容有一定的约定限制

服务模型

Thrif 提供网络模型:单线程、多线程、事件驱动。从另一个角度划分为:阻塞服务模型、非阻塞服务模型。

阻塞服务

TSimpleServer

TThreadPoolServer

非阻塞服务模型

TNonblockingServer

THsHaServer

TThreadedSelectorServer

1、TSimpleServer

TSimpleServer实现是非常的简单,循环监听新请求的到来并完成对请求的处理,是个单线程阻塞模型。由于是一次只能接收和处理一个socket连接,效率比较低,在实际开发过程中很少用到它。

2、TThreadPoolServer

ThreadPoolServer为解决了TSimpleServer不支持并发和多连接的问题, 引入了线程池。但仍然是多线程阻塞模式即实现的模型是One Thread Per Connection。

线程池采用能线程数可伸缩的模式,线程池中的队列采用同步队列(SynchronousQueue)。

ThreadPoolServer拆分了监听线程(accept)和处理客户端连接的工作线程(worker), 监听线程每接到一个客户端, 就投给线程池去处理。

TThreadPoolServer模式优点:

线程池模式中,数据读取和业务处理都交由线程池完成,主线程只负责监听新连接,因此在并发量较大时新连接也能够被及时接受。线程池模式比较适合服务器端能预知最多有多少个客户端并发的情况,这时每个请求都能被业务线程池及时处理,性能也非常高。

TThreadPoolServer模式缺点:

线程池模式的处理能力受限于线程池的工作能力,当并发请求数大于线程池中的线程数时,新请求也只能排队等待。

3、TNonblockingServer

TNonblockingServer采用单线程非阻塞(NIO)的模式, 借助Channel/Selector机制, 采用IO事件模型来处理。所有的socket都被注册到selector中,在一个线程中通过seletor循环监控所有的socket,每次selector结束时,处理所有的处于就绪状态的socket,对于有数据到来的socket进行数据读取操作,对于有数据发送的socket则进行数据发送,对于监听socket则产生一个新业务socket并将其注册到selector中。

select代码里对accept/read/write等IO事件进行监控和处理, 唯一可惜的这个单线程处理. 当遇到handler里有阻塞的操作时, 会导致整个服务被阻塞住。

TNonblockingServer模式优点:

相比于TSimpleServer效率提升主要体现在IO多路复用上,TNonblockingServer采用非阻塞IO,同时监控多个socket的状态变化;

TNonblockingServer模式缺点:

TNonblockingServer模式在业务处理上还是采用单线程顺序来完成,在业务处理比较复杂、耗时的时候,例如某些接口函数需要读取数据库执行时间较长,此时该模式效率也不高,因为多个调用请求任务依然是顺序一个接一个执行。

4、THsHaServer

THsHaServer类是TNonblockingServer类的子类,为解决TNonblockingServer的缺点, THsHa引入了线程池去处理, 其模型把读写任务放到线程池去处理即多线程非阻塞模式。HsHa是: Half-sync/Half-async的处理模式, Half-aysnc是在处理IO事件上(accept/read/write io), Half-sync用于handler对rpc的同步处理上。因此可以认为THsHaServer半同步半异步。

THsHaServer的优点:

与TNonblockingServer模式相比,THsHaServer在完成数据读取之后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操作,效率大大提升;

THsHaServer的缺点:

主线程需要完成对所有socket的监听以及数据读写的工作,当并发请求数较大时,且发送数据量较多时,监听socket上新连接请求不能被及时接受。

5、TThreadedSelectorServer

TThreadedSelectorServer是大家广泛采用的服务模型,其多线程服务器端使用非堵塞式I/O模型,是对TNonblockingServer的扩充, 其分离了Accept和Read/Write的Selector线程, 同时引入Worker工作线程池。

(1)一个AcceptThread线程对象,专门用于处理监听socket上的新连接;

(2)若干个SelectorThread对象专门用于处理业务socket的网络I/O操作,所有网络数据的读写均是有这些线程来完成;

(3)一个负载均衡器SelectorThreadLoadBalancer对象,主要用于AcceptThread线程接收到一个新socket连接请求时,决定将这个新连接请求分配给哪个SelectorThread线程。

(4)一个ExecutorService类型的工作线程池,在SelectorThread线程中,监听到有业务socket中有调用请求过来,则将请求读取之后,交个ExecutorService线程池中的线程完成此次调用的具体执行

MainReactor就是Accept线程, 用于监听客户端连接, SubReactor采用IO事件线程(多个), 主要负责对所有客户端的IO读写事件进行处理. 而Worker工作线程主要用于处理每个rpc请求的handler回调处理(这部分是同步的)。因此其也是Half-Sync/Half-Async(半异步-半同步)的 。
TThreadedSelectorServer模式对于大部分应用场景性能都不会差,因为其有一个专门的线程AcceptThread用于处理新连接请求,因此能够及时响应大量并发连接请求;另外它将网络I/O操作分散到多个SelectorThread线程中来完成,因此能够快速对网络I/O进行读写操作,能够很好地应对网络I/O较多的情况。
HelloServiceClient1.java
```java
package client;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TCompactProtocol;
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 org.apache.thrift.transport.TTransportException;
import service.demo.Hello;

public class HelloServiceClient1 {

    public static final String SERVER_IP = "localhost";
 public static final int SERVER_PORT = 8090;
 public static final int TIMEOUT = 30000;

/**
 * 调用 Hello 服务 * @param args
  */
  public static void main(String[] args) {
       TTransport transport = null;
 try {
           transport = new TFramedTransport(new TSocket(SERVER_IP, SERVER_PORT, TIMEOUT));
  // 协议要和服务端一致
  TProtocol protocol = new TCompactProtocol(transport);
  Hello.Client client = new Hello.Client(protocol);
  transport.open();
  String result = client.helloString("hfjtydiyghfkjdhftgh");
  System.out.println("Thrify client result =: " + result);
  } catch (TTransportException e) {
           e.printStackTrace();
  } catch (TException e) {
           e.printStackTrace();
  } finally {
           if (null != transport) {
               transport.close();
  }
       }
   }
}

我在开发中遇到的错误及解决办法

  • java.lang.NoSuchMethodError: org.apache.thrift.EncodingUtils.setBit(BIZ)B

出现这个问题的原因是dubbox本身集成的thrift版本是0.8.0,我添加的版本是0.9.3导致的,解决方法:去掉对0.8.0版本的依赖

这个问题是因为我thrift的IDL描述文件多了个逗号,在thrift生产java文件的时候没有报错,但是在dubbox运行的时候就出错了。

IDL文件简介

  • 基本类型

    • bool:布尔值,true 或 false,对应 Java 的 boolean
    • byte:8 位有符号整数,对应 Java 的 byte
    • i16:16 位有符号整数,对应 Java 的 short
    • i32:32 位有符号整数,对应 Java 的 int
    • i64:64 位有符号整数,对应 Java 的 long
    • double:64 位浮点数,对应 Java 的 double
    • string:utf-8编码的字符串,对应 Java 的 String
  • 结构体类型:
    • struct:定义公共的对象,类似于 C 语言中的结构体定义,在 Java 中是一个 JavaBean
  • 容器类型:
    • list:对应 Java 的 ArrayList
    • set:对应 Java 的 HashSet
    • map:对应 Java 的 HashMap
  • 异常类型:
    • exception:对应 Java 的 Exception
  • 服务类型:
    • service:对应服务的类

数据传输协议

不同版本稍有区别

  • TBinaryProtocol : 二进制格式.
  • TCompactProtocol : 压缩格式
  • TJSONProtocol : JSON格式
  • TSimpleJSONProtocol : 提供JSON只写协议, 生成的文件很容易通过脚本语言解析
数据传输格式 类型 优点 缺点
Xml 文本 1、良好的可读性。 2、序列化的数据包含完整的结构。3、调整不同属性的顺序对序列化/反序列化不影响 1、数据传输量大。2、不支持二进制数据类型
Json 文本 1、良好的可读性。2、调整不同属性的顺序对序列化/反序列化不影响 1、丢弃了类型信息, 比如”price”:100, 对price类型是int/double解析有二义性。2、不支持二进制数据类型
Thrift 二进制 高效 1、不宜读。2、向后兼容有一定的约定限制,采用id递增的方式标识并以optional修饰来添加
Google Protobuf 二进制 高效 1、不宜读。2、向后兼容有一定的约定限制

服务模型

Thrif 提供网络模型:单线程、多线程、事件驱动。从另一个角度划分为:阻塞服务模型、非阻塞服务模型。

  • 阻塞服务

    • TSimpleServer
    • TThreadPoolServer
  • 非阻塞服务模型
    • TNonblockingServer
    • THsHaServer
    • TThreadedSelectorServer

TSimpleServer

TSimpleServer实现是非常的简单,循环监听新请求的到来并完成对请求的处理,是个单线程阻塞模型。由于是一次只能接收和处理一个socket连接,效率比较低,在实际开发过程中很少用到它。

TThreadPoolServer

ThreadPoolServer为解决了TSimpleServer不支持并发和多连接的问题, 引入了线程池。但仍然是多线程阻塞模式即实现的模型是One Thread Per Connection。

线程池采用能线程数可伸缩的模式,线程池中的队列采用同步队列(SynchronousQueue)。

ThreadPoolServer拆分了监听线程(accept)和处理客户端连接的工作线程(worker), 监听线程每接到一个客户端, 就投给线程池去处理。

TThreadPoolServer模式优点:

线程池模式中,数据读取和业务处理都交由线程池完成,主线程只负责监听新连接,因此在并发量较大时新连接也能够被及时接受。线程池模式比较适合服务器端能预知最多有多少个客户端并发的情况,这时每个请求都能被业务线程池及时处理,性能也非常高。

TThreadPoolServer模式缺点:

线程池模式的处理能力受限于线程池的工作能力,当并发请求数大于线程池中的线程数时,新请求也只能排队等待。

TNonblockingServer

TNonblockingServer采用单线程非阻塞(NIO)的模式, 借助Channel/Selector机制, 采用IO事件模型来处理。所有的socket都被注册到selector中,在一个线程中通过seletor循环监控所有的socket,每次selector结束时,处理所有的处于就绪状态的socket,对于有数据到来的socket进行数据读取操作,对于有数据发送的socket则进行数据发送,对于监听socket则产生一个新业务socket并将其注册到selector中。

select代码里对accept/read/write等IO事件进行监控和处理, 唯一可惜的这个单线程处理. 当遇到handler里有阻塞的操作时, 会导致整个服务被阻塞住。

TNonblockingServer模式优点:

相比于TSimpleServer效率提升主要体现在IO多路复用上,TNonblockingServer采用非阻塞IO,同时监控多个socket的状态变化;

TNonblockingServer模式缺点:

TNonblockingServer模式在业务处理上还是采用单线程顺序来完成,在业务处理比较复杂、耗时的时候,例如某些接口函数需要读取数据库执行时间较长,此时该模式效率也不高,因为多个调用请求任务依然是顺序一个接一个执行。

THsHaServer

THsHaServer类是TNonblockingServer类的子类,为解决TNonblockingServer的缺点, THsHa引入了线程池去处理, 其模型把读写任务放到线程池去处理即多线程非阻塞模式。HsHa是: Half-sync/Half-async的处理模式, Half-aysnc是在处理IO事件上(accept/read/write io), Half-sync用于handler对rpc的同步处理上。因此可以认为THsHaServer半同步半异步。

THsHaServer的优点:

与TNonblockingServer模式相比,THsHaServer在完成数据读取之后,将业务处理过程交由一个线程池来完成,主线程直接返回进行下一次循环操作,效率大大提升;

THsHaServer的缺点:

主线程需要完成对所有socket的监听以及数据读写的工作,当并发请求数较大时,且发送数据量较多时,监听socket上新连接请求不能被及时接受。

TThreadedSelectorServer

TThreadedSelectorServer是大家广泛采用的服务模型,其多线程服务器端使用非堵塞式I/O模型,是对TNonblockingServer的扩充, 其分离了Accept和Read/Write的Selector线程, 同时引入Worker工作线程池。

(1)一个AcceptThread线程对象,专门用于处理监听socket上的新连接;

(2)若干个SelectorThread对象专门用于处理业务socket的网络I/O操作,所有网络数据的读写均是有这些线程来完成;

(3)一个负载均衡器SelectorThreadLoadBalancer对象,主要用于AcceptThread线程接收到一个新socket连接请求时,决定将这个新连接请求分配给哪个SelectorThread线程。

(4)一个ExecutorService类型的工作线程池,在SelectorThread线程中,监听到有业务socket中有调用请求过来,则将请求读取之后,交个ExecutorService线程池中的线程完成此次调用的具体执行

MainReactor就是Accept线程, 用于监听客户端连接, SubReactor采用IO事件线程(多个), 主要负责对所有客户端的IO读写事件进行处理. 而Worker工作线程主要用于处理每个rpc请求的handler回调处理(这部分是同步的)。因此其也是Half-Sync/Half-Async(半异步-半同步)的 。

TThreadedSelectorServer模式对于大部分应用场景性能都不会差,因为其有一个专门的线程AcceptThread用于处理新连接请求,因此能够及时响应大量并发连接请求;另外它将网络I/O操作分散到多个SelectorThread线程中来完成,因此能够快速对网络I/O进行读写操作,能够很好地应对网络I/O较多的情况。

时间: 2024-10-05 09:06:22

thrift学习笔记(一) thrift简介及第一个helloword程序的相关文章

[原创]java WEB学习笔记04:Servlet 简介及第一个Servlet程序

本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 ---------------------------------

Directx11学习笔记【三】 第一个D3D11程序

在先前的解决方案中新建一个新的Win32项目FirstD3D11Demo.在写代码之前,我们必须先添加dx11所需要的库.为了链接dx库,右键项目选择属性->vc++目录,在包含目录中添加你所安装的SDK根目录\Include,在库目录中添加 根目录\lib\x86(或x64),在链接器->输入的附加依赖项中添加d3d11.lib.d3dx11.lib.dxerr.lib. 第一次使用d3d,首先应该从初始化开始. 初始化d3d11的步骤主要有以下几个: 1.定义我们要检查的设备类型和特征级别

django官方文档1.6学习笔记-编写你的第一个django程序

运行环境  centos6.5  django1.6 python2.7.8 python -c "import django;print djang.get_version()" or django.VERSION 创建django项目: django-admin.py startproject mysite 目录结构如下: mysite/                             ###这个名字对django命名成什么都没关系.       manage.py    

django官方文档1.6学习笔记-编写你的第一个django程序&lt;2&gt;

写几个有用的视图 每个视图负责做两件事情,返回一个HttpResponse对象包含被请求的页面的内容,或者抛出一个异常,例如Http404.剩下的视图做什么取决于你自己. 你的视图可以从数据库中读记录,或者不读.可以利用一个模板系统比如django自带的或者第三方的Python模板系统,或者不用.可以实时生成一个pdf文件,输出XML,创建一个ZIP压缩包.所有你想做的一切都可以做,使用任何你想使用python类库. 方便起见,我们使用django自带的数据库API,让我们简单的看一个index

thrift学习笔记

Thrift学习笔记 一:thrift介绍 Thrift是facebook开发的用来处理各不同系统之间数据通讯的rpc服务框架,后来成为apche的开源项目.thrift支持多种程序语言,包括Java,Python,Ruby,JavaScript,Node.js,Go,C,C++,C#,Erlang,Delphi,Perl,Php,SmallTalk,OCaml,Haxe,Haskell,D语言.Thrift采用IDL(Interface Defination Language)描述性语言来定义

NFC学习笔记二——Libnfc简介与安装

一直想把自己对过的英文文章做一下翻译记录下来,趁着学习NFC,现将libnfc首页的对libnfc介绍和在不同操作系统上对libnfc安装的文章做一下翻译,一方面提高一下自己的英语,另一方面学习一下libnfc. 原文地址:http://nfc-tools.org/index.php?title=Libnfc 公共平台独立的近场通讯(NFC)库 libnfc是GNU公共许可正下发布的第一个免费的底层的NFC开发包和编程API.它对任何人事完全免费和公开的.这个列表显示了libnfc支持的功能.l

GDI+学习笔记(五)绘制一个正方体

本文将介绍如何利用GDI+绘制一个正方体. (一)准备阶段 想象一下,高中的时候,我们在学立体几何的时候是怎样画一个正方体的,我们在一张纸上利用投影的思路将其绘制在一张纸上,对吧,这计算投影的部分,我们暂且忽略.下图是我用windows的画图绘制的一个正方体: 我们计算出这些点在平面上的坐标如下: Point A(100,200); Point B(200,200); Point C(100,300); Point D(200,300); Point E(100+50*1.414, 200-50

C++ Primer 学习笔记_83_模板与泛型编程 --一个泛型句柄类

模板与泛型编程 --一个泛型句柄类 引言: [小心地雷] 这个例子体现了C++相当复杂的语言应用,理解它需要很好地理解继承和模板.在熟悉了这些特性之后再研究这个例子也许会帮助.另一方面,这个例子还能很好地测试你对这些特性的理解程度. 前面示例的Sales_item和Query两个类的使用计数的实现是相同的.这类问题非常适合于泛型编程:可以定义类模板管理指针和进行使用计数.原本不相关的Sales_item类型和 Query类型,可通过使用该模板进行公共的使用计数工作而得以简化.至于是公开还是隐藏下

WCF学习笔记 -- 如何用C#开发一个WebService

假设所有工程的命名空间是demo. 新建一个C#的ClassLibrary(类库)工程. 在工程引用中加入System.ServiceModel引用. 定义接口,你可以删除自动生成的代码,或者直接修改代码来添加接口. [ServiceContract] Interface IMath { [Operationcontract] Int add (int a, int b); } 实现接口 添加一个新类,如Math实现该接口. Public class Math : IMath{ Int add(