Apache Thrift入门学习

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


全部代码下载:Github链接:github链接,点击惊喜;写文章不易,欢迎大家采我的文章,以及给出有用的评论,当然大家也可以关注一下我的github;多谢;

1.Thrift介绍

Thrift是一种接口描述语言和二进制通讯协议,[1]它被用来定义和创建跨语言的服务。[2]它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。它通过一个代码生成引擎联合了一个软件栈,来创建不同程度的、无缝的跨平台高效服务,可以使用C#、C++(基于POSIX兼容系统[3])、Cappuccino、[4]Cocoa、Delphi、Erlang、Go、Haskell、Java、Node.js、OCaml、Perl、PHP、Python、Ruby和Smalltalk。[5]虽然它以前是由Facebook开发的,但它现在是Apache软件基金会的开源项目了。该实现被描述在2007年4月的一篇由Facebook发表的技术论文中,该论文现由Apache掌管(摘自维基百科)

2.Thrift安装

安装Thrift主要是为了通过IDL文件生成需要的接口和类。
下载地址:http://archive.apache.org/dist/thrift/0.9.3/

2.1windowx的安装:

  1. 下载thrift-0.9.3.exe
  2. 新建一个文件夹如:Thtift
    3.将thrift-0.9.3.exe该名为thrift(可以不改名)
    4.修改系统的环境变量:
    00
  3. 测试安装成功否:
    01

2.2Linux的安装:

  1. 下载:thrift-0.9.3.tar.gz
    2.tar -xzf thrift-0.9.3.tar.gz
    3.cd thrift-0.9.3
  2. ./configure
  3. make 有点慢
    6.sudo make install

3.Thrift的IDL格式要求

IDL即接口定义语言,主要是为了生成各种语言(例如:java,c/C++等)能够理解的消息结构、接口定义的描述形式。因为要生成其他语言的类,接口,或者结构体,所以必须遵守一些规范。

3.1文件支持的基本类型:

  • bool: 布尔值
  • byte: 有符号字节
  • i16: 16位有符号整型
  • i32: 32位有符号整型
  • i64: 64位有符号整型
  • double: 64位浮点型
  • string: 字符串/字符数组
  • binary: 二进制数据

3.2容器类型:

  • list: 一系列由T类型的数据组成的有序列表,元素可以重复
  • set: 一系列由T类型的数据组成的无序集合,元素不可重复
  • map

3.3结构体:

结构体经过解析后在面向对象语言中,表现为“类定义”;在弱类型语言、动态语言中,表现为“结构/结构体”。
定义格式如下:

struct <结构体名称> {
        <序号>:[字段性质] <字段类型> <字段名称> [= <默认值>] [;|,]
}
·结构体名称:可以按照您的业务需求,给定不同的名称(区分大小写)。但是要注意,一组IDL定义文件中结构体名称不能重复,且不能使用IDL已经占用的关键字(例如required 、struct 等单词)。

·序号:序号非常重要。正整数,按照顺序排列使用。这个属性在Apache Thrift进行序列化的时候被使用。

·字段性质:包括两种关键字:required 和 optional,如果您不指定,那么系统会默认为required。required表示这个字段必须有值,并且Apache Thrift在进行序列化时,这个字段都会被序列化;optional表示这个字段不一定有值,且Apache Thrift在进行序列化时,这个字段只有有值的情况下才会被序列化。

·字段类型:在struct中,字段类型可以是某一个基础类型,也可以是某一个之前定义好的struct,还可以是某种Apache Thrift支持的容器(setmaplist),还可以是定义好的枚举。字段的类型是必须指定的。

·字段名称:字段名称区分大小写,不能重复,且不能使用IDL已经占用的关键字(例如required 、struct 等单词)。

·默认值:您可以为某一个字段指定默认值(也可以不指定)。

·结束符:在struct中,支持两种结束符,您可以使用“;”或者“,”。当然您也可以不使用结束符(Apache Thrift代码生成程序,会自己识别到)

3.4枚举:

枚举的定义形式和C的Enum定义差不多

enum <枚举名称> {
        <枚举字段名> = <枚举值>[;|,]
}

3.5常量定义:

IDL允许定义常量。常量的关键字为“const”,与C语言类似,常量可以是基础类型,也可以是定义的Struct。
如:const i32 MAX_AREA = 60

3.6异常定义:

IDL允许定义异常,在定义服务接口使用。其定义方法类似于上面的Struct。因为exception也是类,定义时只需要将struct换成exception就行,如下:

exception <异常名称> {
        <序号>:[字段性质] <字段类型> <字段名称> [= <默认值>] [;|,]
}

3.7服务接口:

服务接口在生成代码过程中,生成的接口类是我们需要实现的提供给客户端调用的。service服务接口定义如下:

service <服务名称> {
    <void | 返回指类型> <服务方法名>([<入参序号>:[required | optional] <参数类型> <参数名> ...]) [throws ([<异常序号>:[required | optional] <异常类型> <异常参数名>...])]
}
·服务名称:服务名可以按照您的业务需求自行制定,注意服务名是区分大小写的。IDL中服务名称只有两个限制,就是不能重复使用相同的名称,不能使用IDL已经占用的关键字(例如required 、struct 等单词)。

·返回值类型:如果这个调用方法没有返回类型,那么可以关键字“void”; 可以是Apache Thrift的基础类型,也可以是某一个之前定义好的struct,还可以是某种Apache Thrift支持的容器(set、map、list),还可以是定义好的枚举。

·服务方法名:服务方法名可以根据您的业务需求自定制定,注意区分大小写。在同一个服务中,不能重复使用一个服务方法名命名多个方法(一定要注意),不能使用IDL已经占用的关键字。

·服务方法参数:<入参序号>:[required | optional] <参数类型> <参数名>。注意和struct中的字段定义相似,可以指定required或者optional;如果不指定则系统默认为required 。如果一个服务方法中有多个参数名,那么这些参数名称不能重复。

·服务方法异常:throws ([<异常序号>:[required | optional] <异常类型> <异常参数名>。throws关键字是服务方法异常定义的开始点。在throws关键字后面,可以定义1个或者多个不同的异常类型。

3.8命名空间:

thrift的命名空间相当于Java中的package的意思,主要目的是组织代码。thrift使用关键字namespace定义命名空间,例如:

namespace java cn.wpeace.thrift

3.9其他特性:

1.注释:

支持下面三种方式:
/*
* 注释方式1:
**/// 注释方式2# 注释方式3
  1. IDL文件包含
    thrift也支持文件包含,相当于C/C++中的include,Java中的import。使用关键字include定义,例 如:
include "wpeace.thrift"

4.Hello world Thrift

下面讲解下编写一个Hello world工程的步骤:
1. 编写IDL文件,以及编译
2.下载jar包建立服务端代码:实现接口,和启动服务
3.建立客户端代码进行访问

4.1编写IDL文件和编译

  1. 按照第三节的规范,编写IDL文件。如下:我命名为hello.thrift
//命名空间定义:java包
namespace java cn.wpeace.thrift
//结构体定义:转化java中的实体类
struct Request{
      1:required string userName;
      2:required string password;
}
//定义返回类型
struct Student{
        1:required string naem;
        2:required i32 age;
}
//异常描述定义
exception HelloException{
       1:required string msg;
}
//服务定义,生成接口用
service StudentService{
                                 list<Student> getAllStudent(1:Request request)throws (1:HelloException e);
}
  1. 打开终端输入命令:thrift -gen java ./hello.thrift 后会在当前目录生成gen-java文件夹,里面包含了我们定义的类和接口。按namespace路径存放。
    04

4.2下载jar包建立服务端代码

1.需要下载的jar包:

org.slf4j.api.jar
org.slf4j.simple.jar
libthrift-0.9.1.jar

点击下载
2.建立服务端代码,需要实现StudnetService类中的Iface接口,代码如下:详见注释

package cn.wpeace.thrift;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.server.TThreadPoolServer.Args;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TTransportException;
import cn.wpeace.thrift.StudentService.Iface;
import cn.wpeace.thrift.StudentService.Processor;
public class StudentServiceImpl implements Iface {// 实现的是StudentService类下面的接口
    @Override
    public List<Student> getAllStudent(Request request) throws HelloException, TException {
        System.out.println(request.getUserName());
        System.out.println(request.getPassword());
        List<Student> students = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Student student = new Student();
            student.setNaem("peace" + i);
            student.setAge(22 + i);
            students.add(student);
        }
        return students;
    }   
}

3.启动服务器:

public static void main(String[] args) {
        try {
            System.out.println("服务启动");
            // 非阻塞式
            TNonblockingServerSocket serverSocket=new TNonblockingServerSocket(8081);
            // 服务执行控制器,类似于rmi中的bind
            Processor<Iface> processor = new StudentService.Processor<Iface>(new StudentServiceImpl());
            // 为服务器设置对应的IO网络模型
            TNonblockingServer.Args tArgs = new TNonblockingServer.Args(serverSocket);
            // 设置控制器
            tArgs.processor(processor);
            //设置传输方式
            // tArgs.transportFactory(new TFramedTransport.Factory());
            // 设置消息封装格式
            tArgs.protocolFactory(new TBinaryProtocol.Factory());//Thrift特有的一种二进制描述格式
            // 启动Thrift服务
            TNonblockingServer server = new TNonblockingServer(tArgs);
            server.serve();//启动后,程序就停在这里了。
            System.out.println("服务结束");
        } catch (TTransportException e) {
            e.printStackTrace();
        }
    }
  @Test
  public  void test(String[] args) {
        try {
            System.out.println("服务启动");
            // 阻塞式同步socket
            TServerSocket serverSocket = new TServerSocket(8081);
            // 服务执行控制器,类似于rmi中的bind
            Processor<Iface> processor = new StudentService.Processor<Iface>(new StudentServiceImpl());
            // 为服务器设置对应的IO网络模型
            Args tArgs = new Args(serverSocket);
            // 设置控制器
            tArgs.processor(processor);
            // 设置消息封装格式
            tArgs.protocolFactory(new TBinaryProtocol.Factory());//Thrift特有的一种二进制描述格式
            // 设置线程池参数
            tArgs.executorService(Executors.newFixedThreadPool(10));//线程池调度器,由于是阻塞模式需要设置线程池。
            // 启动Thrift服务
            TThreadPoolServer server = new TThreadPoolServer(tArgs);
            server.serve();//启动后,程序就停在这里了。
            System.out.println("服务结束");
        } catch (TTransportException e) {
            e.printStackTrace();
        }
    }  

4.3建立客户段代码:

注意:客户端和服务端的传输协议需要一至,同时阻塞和非阻塞传输也有区别。详细见代码

public static void main(String[] args) {
        try {
            //建立socket连接
            //TSocket tSocket = new TSocket("192.168.1.118",8081);
            //如果是非阻塞型  需要使用
            TTransport tSocket = new TFramedTransport(new TSocket("192.168.1.118",8081,  30000));  
            //设置封装协议
            TBinaryProtocol protocol = new TBinaryProtocol(tSocket);
            //建立调用client
            StudentService.Client client=new StudentService.Client(protocol);
            //设置调用参数:
            Request request=new Request().setUserName("peace").setPassword("123456");
            //准备传输
            tSocket.open();
            //正式调用接口
            List<Student> allStudent = client.getAllStudent(request);
            //请求结束,断开连接
            tSocket.close();
            for(Student student:allStudent)

            {
                System.out.println(student.getNaem()+":"+student.getAge());
            }

        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (HelloException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }

    }  

4.4测试:

1.启动服务器
2.启动客户端
客户端结果:
02
服务端结果:
03

本文来自伊豚(blog.wpeace.cn)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值