• 欢迎访问VPS岛网站,国外VPS,国内VPS,国外服务器,国内服务器,服务器主机,测评及优惠码,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站 QQ群

Apache Thrift系列详解(一) – 概述与入门

Apache技术 mb5ff97fc6948e0 26次浏览 已收录 0个评论

前言

Thrift是一个轻量级、跨语言的远程服务调用框架,最初由 Facebook开发,后面进入 Apache开源项目。它通过自身的 IDL中间语言, 并借助代码生成引擎生成各种主流语言的 RPC服务端/客户端模板代码。

Thrift支持多种不同的编程语言,包括 C++、 Java、 Python、 PHP、 Ruby等,本系列主要讲述基于 Java语言的 Thrift的配置方式和具体使用。

正文

Thrift的技术栈

Thrift对软件栈的定义非常的清晰, 使得各个组件能够松散的耦合, 针对不同的应用场景, 选择不同是方式去搭建服务。

Apache Thrift系列详解(一) - 概述与入门

Thrift软件栈分层从下向上分别为:传输层( TransportLayer)、协议层( ProtocolLayer)、处理层( ProcessorLayer)和服务层( ServerLayer)。

  • 传输层( TransportLayer):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议;比如说 TCP/IP传输等。

  • 协议层( ProtocolLayer):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说 JSON、 XML、二进制数据等。

  • 处理层( ProcessorLayer):处理层是由具体的 IDL(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的 Handler进行处理。

  • 服务层( ServerLayer):整合上述组件,提供具体的网络线程/IO服务模型,形成最终的服务。

Thrift的特性

(一) 开发速度快

通过编写 RPC接口 ThriftIDL文件,利用编译生成器自动生成服务端骨架( Skeletons)和客户端桩( Stubs)。从而省去开发者自定义和维护接口编解码、消息传输、服务器多线程模型等基础工作。

  • 服务端:只需要按照服务骨架即接口,编写好具体的业务处理程序( Handler)即实现类即可。

  • 客户端:只需要拷贝 IDL定义好的客户端桩和服务对象,然后就像调用本地对象的方法一样调用远端服务。

(二) 接口维护简单

通过维护 Thrift格式的IDL(接口描述语言)文件(注意写好注释),即可作为给 Client使用的接口文档使用,也自动生成接口代码,始终保持代码和文档的一致性。且 Thrift协议可灵活支持接口的可扩展性。

(三) 学习成本低

因为其来自 GoogleProtobuf开发团队,所以其 IDL文件风格类似 GoogleProtobuf,且更加易读易懂;特别是 RPC服务接口的风格就像写一个面向对象的 Class一样简单。

初学者只需参照:http://thrift.apache.org/,一个多小时就可以理解 ThriftIDL文件的语法使用。

(四) 多语言/跨语言支持

Thrift支持 C++、 Java、 Python、 PHP、 Ruby、 Erlang、 Perl、 Haskell、 C#、 Cocoa、 JavaScript、 Node.js、 Smalltalk等多种语言,即可生成上述语言的服务器端和客户端程序。

对于我们经常使用的 Java、 PHP、 Python、 C++支持良好,虽然对 iOS环境的 Objective-C( Cocoa)支持稍逊,但也完全满足我们的使用要求。

(五) 稳定/广泛使用

Thrift在很多开源项目中已经被验证是稳定和高效的,例如 Cassandra、 Hadoop、 HBase等;国外在 Facebook中有广泛使用,国内包括百度、美团小米、和饿了么等公司。
Apache Thrift系列详解(一) - 概述与入门

Thrift的数据类型

Thrift 脚本可定义的数据类型包括以下几种类型:

  1. 基本类型:   
  • bool: 布尔值   

  • byte: 8位有符号整数   

  • i16: 16位有符号整数   

  • i32: 32位有符号整数   

  • i64: 64位有符号整数   

  • double: 64位浮点数   

  • string: UTF-8编码的字符串   

  • binary: 二进制串
  1. 结构体类型:   
  • struct: 定义的结构体对象
  1. 容器类型:   
  • list: 有序元素列表   

  • set: 无序无重复元素集合   

  • map: 有序的key/value集合
  1. 异常类型:   
  • exception: 异常类型
  1. 服务类型:   
  • service: 具体对应服务的类

Thrift的协议

Thrift可以让用户选择客户端与服务端之间传输通信协议的类别,在传输协议上总体划分为文本( text)和二进制( binary)传输协议。为节约带宽,提高传输效率,一般情况下使用二进制类型的传输协议为多数,有时还会使用基于文本类型的协议,这需要根据项目/产品中的实际需求。常用协议有以下几种:

  • TBinaryProtocol:二进制编码格式进行数据传输

  • TCompactProtocol:高效率的、密集的二进制编码格式进行数据传输

  • TJSONProtocol: 使用 JSON文本的数据编码协议进行数据传输

  • TSimpleJSONProtocol:只提供 JSON只写的协议,适用于通过脚本语言解析

Thrift的传输层

常用的传输层有以下几种:

  • TSocket:使用阻塞式 I/O进行传输,是最常见的模式

  • TNonblockingTransport:使用非阻塞方式,用于构建异步客户端

  • TFramedTransport:使用非阻塞方式,按块的大小进行传输,类似于 Java中的 NIO

Thrift的服务端类型

  • TSimpleServer:单线程服务器端,使用标准的阻塞式 I/O

  • TThreadPoolServer:多线程服务器端,使用标准的阻塞式 I/O

  • TNonblockingServer:单线程服务器端,使用非阻塞式 I/O

  • THsHaServer:半同步半异步服务器端,基于非阻塞式 IO读写和多线程工作任务处理

  • TThreadedSelectorServer:多线程选择器服务器端,对 THsHaServer在异步 IO模型上进行增强

Thrift入门示例

(一) 编写Thrift IDL文件

a). 下载 0.10.0的 ThriftIDL编译器,下载地址:http://thrift.apache.org/docs/install。 通过编译生成器生成 .java接口的类文件。
Apache Thrift系列详解(一) - 概述与入门
b). 下载 Windows安装环境的 .exe文件,将 thrift.exe的路径加入环境变量中。在 Idea上安装 Thrift编辑插件。
Apache Thrift系列详解(一) - 概述与入门

c). 编写 hello.thrift的 IDL文件:

service HelloWorldService {

  string say(1: string username)

}

d). 使用代码生成工具生成代码,执行以下命令:

thrift -gen java hello.thrift

e). 由于未指定代码生成的目标目录,生成的类文件默认存放在 gen-java目录下。这里生成一个 HelloWorldService.java类文件,文件大小超过数千行,下面截取一部分核心代码。

public class HelloWorldService {

    public interface Iface {

        public String say(String username) throws org.apache.thrift.TException;

    }

    public interface AsyncIface {

        public void say(String username, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws org.apache.thrift.TException;

    }

    public static class Client extends org.apache.thrift.TServiceClient implements Iface {

        public static class Factory implements org.apache.thrift.TServiceClientFactory<Client> {

            public Factory() {

            }

            public Client getClient(org.apache.thrift.protocol.TProtocol prot) {

                return new Client(prot);

            }

            public Client getClient(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {

                return new Client(iprot, oprot);

            }

        }

        public Client(org.apache.thrift.protocol.TProtocol prot) {

            super(prot, prot);

        }

        public Client(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {

            super(iprot, oprot);

        }

        public String say(String username) throws org.apache.thrift.TException {

            send_say(username);

            return recv_say();

        }

        // 省略.....

    }

    public static class AsyncClient extends org.apache.thrift.async.TAsyncClient implements AsyncIface {

        public static class Factory implements org.apache.thrift.async.TAsyncClientFactory<AsyncClient> {

            private org.apache.thrift.async.TAsyncClientManager clientManager;

            private org.apache.thrift.protocol.TProtocolFactory protocolFactory;

            public Factory(org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.protocol.TProtocolFactory protocolFactory) {

                this.clientManager = clientManager;

                this.protocolFactory = protocolFactory;

            }

            public AsyncClient getAsyncClient(org.apache.thrift.transport.TNonblockingTransport transport) {

                return new AsyncClient(protocolFactory, clientManager, transport);

            }

        }

        public AsyncClient(org.apache.thrift.protocol.TProtocolFactory protocolFactory, org.apache.thrift.async.TAsyncClientManager clientManager, org.apache.thrift.transport.TNonblockingTransport transport) {

            super(protocolFactory, clientManager, transport);

        }

        public void say(String username, org.apache.thrift.async.AsyncMethodCallback<String> resultHandler) throws org.apache.thrift.TException {

            checkReady();

            say_call method_call = new say_call(username, resultHandler, this, ___protocolFactory, ___transport);

            this.___currentMethod = method_call;

            ___manager.call(method_call);

        }

        // 省略.....

    }

    // 省略.....

}

对于开发人员而言,使用原生的 Thrift框架,仅需要关注以下四个核心内部接口/类: Iface, AsyncIface, Client和 AsyncClient。

  • Iface:服务端通过实现 HelloWorldService.Iface接口,向客户端的提供具体的同步业务逻辑。

  • AsyncIface:服务端通过实现 HelloWorldService.Iface接口,向客户端的提供具体的异步业务逻辑。

  • Client:客户端通过 HelloWorldService.Client的实例对象,以同步的方式访问服务端提供的服务方法。

  • AsyncClient:客户端通过 HelloWorldService.AsyncClient的实例对象,以异步的方式访问服务端提供的服务方法。

(二) 新建Maven工程

a). 新建 maven工程,引入 thrift的依赖,这里使用的是版本 0.10.0。

  <dependency>

      <groupId>org.apache.thrift</groupId>

      <artifactId>libthrift</artifactId>

      <version>0.10.0</version>

  </dependency>

b). 将生成类的 HelloWorldService.java源文件拷贝进项目源文件目录中,并实现 HelloWorldService.Iface的定义的 say()方法。

HelloWorldServiceImpl.java

public class HelloWorldServiceImpl implements HelloWorldService.Iface {

    @Override

    public String say(String username) throws TException {

        return "Hello " + username;

    }

}

c). 服务器端程序编写:

SimpleServer.java

public class SimpleServer {

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

        ServerSocket serverSocket = new ServerSocket(ServerConfig.SERVER_PORT);

        TServerSocket serverTransport = new TServerSocket(serverSocket);

        HelloWorldService.Processor processor =

                new HelloWorldService.Processor<HelloWorldService.Iface>(new HelloWorldServiceImpl());

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

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

        tArgs.processor(processor);

        tArgs.protocolFactory(protocolFactory);

        // 简单的单线程服务模型 一般用于测试

        TServer tServer = new TSimpleServer(tArgs);

        System.out.println("Running Simple Server");

        tServer.serve();

    }

}

d). 客户端程序编写:

SimpleClient.java

public class SimpleClient {

    public static void main(String[] args) {

        TTransport transport = null;

        try {

            transport = new TSocket(ServerConfig.SERVER_IP, ServerConfig.SERVER_PORT, ServerConfig.TIMEOUT);

            TProtocol protocol = new TBinaryProtocol(transport);

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

            transport.open();

            String result = client.say("Leo");

            System.out.println("Result =: " + result);

        } catch (TException e) {

            e.printStackTrace();

        } finally {

            if (null != transport) {

                transport.close();

            }

        }

    }

}

e). 运行服务端程序,服务端在指定端口监听客户端的连接请求,控制台输出启动日志:
Apache Thrift系列详解(一) - 概述与入门

f). 运行客户端程序,客户端通过网络请求 HelloWorldService的 say()方法的具体实现,控制台输出返回结果:
Apache Thrift系列详解(一) - 概述与入门

这里使用的一个基于单线程同步的简单服务模型,一般仅用于入门学习和测试!

小结

本文对 Thrift的概念做了相关介绍,体验了一番 thrift程序如何编写!

欢迎技术公众号: 零壹技术栈

Apache Thrift系列详解(一) - 概述与入门

本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。


VPS岛 的文章和资源来自互联网,仅作为参考资料,如果有侵犯版权的资源请尽快联系站长,我们会在24h内删除有争议的资源。丨 转载请注明Apache Thrift系列详解(一) – 概述与入门
喜欢 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址