thrift 小结

概述

Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目。Thrift通过一个中间语言(IDL, 接口定义语言)来定义RPC的接口和数据类型,然后通过一个编译器生成不同语言的代码(C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml),并由生成的代码负责RPC协议层和传输层的实现。

相关资料

  1. official-github
  2. thrift指南
  3. thrift demo
  4. thrift wiki
  5. thrift-tutorial

thrift安装

windows

1.安装thrift.exe
2.配置maven,通过maven执行IDL生成代码工作

<?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>thriftTest</groupId>
    <artifactId>simplecConsole</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>thrift-demo-console</name>
    <description>A maven project to study thrift.</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <compiler-plugin.version>2.3.2</compiler-plugin.version>
        <thrift.version>0.9.3</thrift.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.thrift</groupId>
            <artifactId>libthrift</artifactId>
            <version>${thrift.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${compiler-plugin.version}</version>
                <configuration>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
         <!-- <plugin> <groupId>org.apache.thrift.tools</groupId> <artifactId>maven-thrift-plugin</artifactId> <version>0.1.11</version> <configuration> <thriftExecutable>D:\Thrift\thrift.exe</thriftExecutable> <generator>java</generator> <outputDirectory>src/main/java/thrift/demo</outputDirectory> <thriftSourceRoot>${project.basedir}/src/main/resources</thriftSourceRoot> </configuration> <executions> <execution> <id>thrift-sources</id> <phase>generate-sources</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>thrift-test-sources</id> <phase>generate-test-sources</phase> <goals> <goal>testCompile</goal> </goals> </execution> </executions> </plugin>-->
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <executions>
                    <execution>
                        <id>compile-thrift</id>
                        <phase>generate-sources</phase>
                        <configuration>
                            <tasks>
                                <mkdir dir="src/main/java/com/anjuke/demo/thrift/auto"/>
                                <path id="thrift.path">
                                    <fileset dir="${project.basedir}/src/main/resources">
                                        <include name="**/*.thrift"/>
                                    </fileset>
                                </path>
                                <pathconvert pathsep=" " property="thrift.files" refid="thrift.path"/>
                                <exec executable="D:\Thrift\thrift">
                                    <arg value="-r --gen"/>
                                    <arg value="java"/>
                                    <arg value="-out"/>
                                    <arg value="src/main/java"/>
                                    <arg line="${thrift.files}"/>
                                </exec>
                            </tasks>
                            <sourceRoot>src/main/java/thrift/demo/thrift</sourceRoot>
                        </configuration>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-clean-plugin</artifactId>
                <configuration>
                    <verbose>true</verbose>
                    <filesets>
                        <fileset>
                            <directory>${project.basedir}/src/main/java/com/anjuke/demo/thrift/auto</directory>
                        </fileset>
                    </filesets>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

linux

ReferenceLink

基本概念

开发流程

定义thrift的IDL文件

生成对应语言的代码

代码生成服务的所有数据协议转换,并提供接口调用系统的传输方式(TSocket,THttpTransport,TFileTransport),以及同步\异步客户端,Processor负责Client请求的响应

实现服务端

有阻塞,非阻塞,线程池,半同步半异步,Selector多种服务端实现模式。

TSimpleServer – 简单的单线程服务模型,常用于测试
TThreadedServer – 多线程服务模型,使用阻塞式IO,每个请求创建一个线程。(java 不支持)
TThreadPoolServer – 多线程服务模型,使用标准的阻塞式IO,预先创建一组线程处理请求。
TThreadedSelectorServer 允许你用多个线程来处理网络I/O。它维护了两个线程池,一个用来处理网络I/O,另一个用来进行请求的处理
TNonblockingServer – 多线程服务模型,使用非阻塞式IO(需使用TFramedTransport数据传输方式),只有一个线程来处理消息
THsHaServer - 半同步半异步的服务模型,一个单独的线程用来处理网络I/O,一个worker线程池用来进行消息的处理

服务端流程

  • 基于服务的具体实现类,实现processor
TProcessor tprocessor = new HelloWorldService.Processor<HelloWorldService.Iface>(
                    new HelloWorldImpl());
  • create 服务端socket(Transport)
TServerSocket 阻塞型socket, 用于服务器端, accecpt到的socket类型都是TSocket(即阻塞型socket);
TNonblockingServerSocket 非阻塞型socket, 用于服务器端(NIO)
  • create Protocol 必须与客户端的Protocol一致
  • 基于以上信息create Server
  • 对于客户端使用封装了基本TTransport的Transport的例如(TZlibTransport,TFramedTransport),需要对Server设置该TTransport的工厂类

Sever demo

TProcessor tprocessor = new HelloWorldService.Processor&lt;HelloWorldService.Iface&gt;(
                    new HelloWorldImpl());

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

实现客户端

包括同步,异步两种客户端形式

客户端流程

  • create Socket
TSocket:采用TCP Socket进行数据传输,阻塞型socket,用于客户端,采用系统函数readwrite进行读写数据;(BIO)
TNonblockingSocket (NIO) 异步客户端使用
  • create Transport
TSocket:采用TCP Socket进行数据传输,阻塞型socket,用于客户端,采用系统函数readwrite进行读写数据;(BIO)
TNonblockingSocket (NIO) 异步客户端使用
TSSLSocket 继承TSocket,阻塞型socket, 用于客户端;采用openssl的接口进行读写数据。
THttpTransport:采用Http传输协议进行数据传输
TFileTransport – 以文件形式进行传输。
TMemoryTransport – 将内存用于I/O. java实现时内部实际使用了简单的ByteArrayOutputStream。
TZlibTransport – 使用zlib进行压缩, 与其他传输方式联合使用。当前无java实现。
TFramedTransport – 以frame为单位进行传输,非阻塞式服务中使用。类似于Java中的NIO。
TFastFramedTransport 与TFramedTransport相比,始终使用相同的Buffer,提高了内存的使用率。
TSaslClientTransport与TSaslServerTransport, 提供SSL校验
  • create Protocol must same as service based on transport
TBinaryProtocol – 二进制格式.
TCompactProtocol – 压缩格式
TDenseProtocol -继承TCompactProtocol,不包含meta信息
TJSONProtocol – JSON格式
TSimpleJSONProtocol –提供JSON只写协议, 生成的文件很容易通过脚本语言解析。
  • create Client based on Transport and Protocol
  • open transport
  • client.method call service

client demo

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

async client demo

TAsyncClientManager clientManager = new TAsyncClientManager();
    TNonblockingTransport transport = new TNonblockingSocket(SERVER_IP,SERVER_PORT, TIMEOUT);

    TProtocolFactory tprotocol = new TCompactProtocol.Factory();
    HelloWorldService.AsyncClient asyncClient = new HelloWorldService.AsyncClient(tprotocol, clientManager, transport);
    System.out.println("Client start .....");

    CountDownLatch latch = new CountDownLatch(1);
    AsynCallback callBack = new AsynCallback(latch);
            System.out.println("call method sayHello start ...");
    asyncClient.sayHello(userName, callBack);
    System.out.println("call method sayHello .... end");
    boolean wait = latch.await(30, TimeUnit.SECONDS);

Thrift框架

框架和时序图

thrift Type

base type

  • bool: 布尔值 (true or false), one byte
  • byte: 有符号字节
  • i16: 16位有符号整型
  • i32: 32位有符号整型
  • i64: 64位有符号整型
  • double: 64位浮点型
  • string: 编码无关的文本

struct

struct是定义为一种对象,和面向对象语言的class差不多.,但是struct有以下一些约束:

  • struct不能继承,但是可以嵌套,不能嵌套自己。
  • 其成员都是有明确类型
  • 成员是被正整数编号过的,其中的编号使不能重复的,这个是为了在传输过程中编码使用。
  • 成员分割符可以是逗号(,)或是分号(;),而且可以混用,但是为了清晰期间,建议在定义中只使用一种,比如C++学习者可以就使用分号(;)。
  • 字段会有optional和required之分和protobuf一样,但是如果不指定则为无类型—可以不填充该值,但是在序列化传输的时候也会序列化进去,optional是不填充则部序列化,required是必须填充也必须序列化。
  • 每个字段可以设置默认值
  • 同一文件可以定义多个struct,也可以定义在不同的文件,进行include引入。

struct demo


struct Report
{
  1: required string msg, //改字段必须填写
  2: optional i32 type = 0; //默认值
  3: i32 time //默认字段类型为optional
}

Containers

如protobuf 通过 repeated 标识实现 Containers 不同

  • list<t>: 元素类型为t的有序表,容许元素重复。对应c++的vector,java的ArrayList或者其他语言的数组(官方文档说是ordered list不知道如何理解?排序的?c++的vector不排序
  • set<t>:元素类型为t的无序表,不容许元素重复。对应c++中的set,java中的HashSet,python中的set,php中没有set,则转换为list类型了
  • map<t,t>: 键类型为t,值类型为t的kv对,键不容许重复。对用c++中的map, Java的HashMap, PHP 对应 array, Python/Ruby 的dictionary。

Container demo


struct Test {
1: map<Numberz, UserId> user_map,
2: set<Numberz> num_sets,
3: list<Stusers> users
}

Enum

不同于protocal buffer,thrift不支持枚举类嵌套,枚举常量必须是32位的正整数

  • 编译器默认从0开始赋值
  • 可以赋予某个常量某个整数
  • 允许常量是十六进制整数
  • 末尾没有分号
  • 给常量赋缺省值时,使用常量的全称

Enumeration demo


enum EnOpType {
CMD_OK = 0, // (0)   
CMD_NOT_EXIT = 2000, // (2000)
CMD_EXIT = 2001, // (2001)   
CMD_ADD = 2002 // (2002)
}

struct StUser {
1: required i32 userId;
2: required string userName;
3: optional EnOpType cmd_code = EnOpType.CMD_OK; // (0)
4: optional string language = “english”
}

Exception

Thrift结构体将会被转换成面向对象语言的类。异常在语法和功能上类似于结构体,差别是异常使用关键字exception,而且异常是继承每种语言的基础异常类。

Exception demo

exception Extest {
1: i32 errorCode,
2: string message,
3: StUser userinfo
}

Services

服务的定义方法在语义(semantically)上等同于面向对象语言中的接口。Thrift编译器会产生执行这些接口的client和server stub。具体参见下一节。在流行的序列化/反序列化框架(如protocal buffer)中,Thrift是少有的提供多语言间RPC服务的框架。这是Thrift的一大特色。

services demo


service SeTest {
      void ping(),
      bool postTweet(1: StUser user);
      StUser searchTweets(1:string name);
     oneway void zip()

}

Namespace

Thrift中的命名空间类似于C++中的namespace和java中的package,它们提供了一种组织(隔离)代码的简便方式。名字空间也可以用于解决类型定义中的名字冲突。

Namespace demo

namespace cpp com.example.test
namespace java com.example.test 
namespace php com.example.test  

Includes

便于管理、重用和提高模块性/组织性,常常分割Thrift定义在不同的文件中。包含文件搜索方式与c++一样。Thrift允许文件包含其它thrift文件,用户需要使用thrift文件名作为前缀访问被包含的对象

Include demo

include "test.thrift"   
...
struct StSearchResult {
    1: in32 uid; 
    ...
}

Thrift IDL

基于Thrift Type 书写IDL,文件后缀名thrift

IDL demo

struct User{
    1:i64 id,
    2:string name,
    3:i64 timestamp,
    4:bool vip  
}

service UserService{
    User getById(1:i64 id)
}

My demo

demo-github-link

你可能感兴趣的:(java,thrift)