• 《精通并发与Netty》学习笔记(06


    一、概述

    Apache Thrift 是 Facebook 实现的一种高效的、支持多种编程语言的远程服务调用的框架。Thrift是由Facebook开发的,并在2008年捐给了Apache基金会,成为了一个孵化器项目。

    Thrift 主要用于各个服务之间的RPC通信,支持跨语言,常用的语言比如C++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,Cocoa,JavaScript,Node.js,Smalltalk,and OCaml都支持。
    Thrift是一个典型的CS(客户端/服务端)结构,客户端和服务端可以使用不同的语言开发,既然客户端和服务端能使用不同的语言开发,
    那么一定就要有一种中间语言来联系客户端和服务端的语言,这种语言就是IDL(Interface Description Language)

    Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,Thrift是一个驱动层接口,它提供了用于客户端使用多种语言实现的API。

    Thrift是个代码生成库,支持的客户端语言包括C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 。它的目标是为了各种流行的语言提供便利的RPC调用机制,而不需要使用那些开销巨大的方式,比如SOAP。

    二、Thrift的特性
    1、语言无关的类型
    因为类型是使用定义文件按照语言中立的方式规定的,所以它们可以被不同的语言分析。比如,C++的结构可以和Python的字典类型相互交换数据。
    2、通用传输接口
    不论你使用的是磁盘文件、内存数据还是socket流,都可以使用同一段应用代码。
    3、协议无关
    Thrift会对数据类型进行编码和解码,可以跨协议使用。
    4、支持版本
    数据类型可以加入版本信息,来支持客户端API的更新。

    三、Thrift的数据类型

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

    基本类型:
      bool: 布尔值
      byte: 8位有符号整数
      i16: 16位有符号整数
      i32: 32位有符号整数
      i64: 64位有符号整数
      double: 64位浮点数
      string: UTF-8编码的字符串
      binary: 二进制串
    结构体类型:
      struct是Thrift IDL中的基本组成块,由域组成,每个域有唯一整数标识符,类型,名字和可选的缺省参数组成。如定义一个类似于Twitter服务:

    struct Tweet {
        1: required i32 userId;                  // (1)
        2: required string userName;             // (2)
        3: required string text;
        4: optional Location loc;                // (3)
        16: optional string language = "english" // (4)
    }
    
    struct Location {                            // (5)
        1: required double latitude;
        2: required double longitude;
    }

    容器类型:

      list: 有序元素列表
      set: 无序无重复元素集合
      map: 有序的key/value集合
    异常类型:
      exception: 异常类型
    服务类型:
      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的文件,由thrift文件(IDL)生成双方语言的接口、model,在生成的model以及接口中会有解码编码的代码

    五、Thrift案例解析

    第一步:打开idea编写data.thrift文件 

    namespace java thrift.generated
    
    typedef i16 short
    typedef i32 int
    typedef i64 long
    typedef bool boolean
    typedef string String
    
    struct Person{
        1: optional String username,
        2: optional int age,
        3: optional boolean married
    }
    
    exception DataException{
        1: optional String message,
        2: optional String callStack,
        3: optional String date
    }
    
    service PersonService{
        Person getPersonByUsername(1: required String username) throws(1: DataException dataException),
        void savePerson(1: required Person person) throws (1: DataException dataException)
    }

    第二步:打开ides设置File——Setting——Tools——Terminal——Shell path中为cmd.exe路径,一般为C:WINDOWSsystem32目录下

    第三步:打开终端,输入命令:thrift --gen java src/thrift/data.thrift 生成java文件

     第四步:将生成java文件拷贝到项目中,并修改build.gradle文件增加对thrift的引用

     第五步:编写实现类PersonServiceImpl

    package com.ssy.netty.thrift;
    
    import generated.DataException;
    import generated.Person;
    import generated.PersonService;
    import org.apache.thrift.TException;
    
    public class PersonServiceImpl implements PersonService.Iface {
        @Override
        public Person getPersonByUsername(String username) throws DataException, TException {
            System.out.println("Get client param:"+username);
            Person person = new Person();
            person.setUsername(username);
            person.setAge(20);
            person.setMarried(false);
            return person;
        }
    
        @Override
        public void savePerson(Person person) throws DataException, TException {
            System.out.println("Get client param:");
            System.out.println(person.getUsername());
            System.out.println(person.getAge());
            System.out.println(person.isMarried());
        }
    }

    第六步:编写服务端程序ThriftServer,这里我们服务端和客户端都采用java编写

    package com.ssy.netty.thrift;
    
    import generated.PersonService;
    import org.apache.thrift.TProcessorFactory;
    import org.apache.thrift.protocol.TCompactProtocol;
    import org.apache.thrift.server.THsHaServer;
    import org.apache.thrift.server.TServer;
    import org.apache.thrift.transport.TFramedTransport;
    import org.apache.thrift.transport.TNonblockingServerSocket;
    
    public class ThriftServer {
        public static void main(String[] args) throws Exception{
            TNonblockingServerSocket socket = new TNonblockingServerSocket(8899);
            THsHaServer.Args arg = new THsHaServer.Args(socket).minWorkerThreads(2).maxWorkerThreads(4);
            PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());
            arg.protocolFactory(new TCompactProtocol.Factory());
            arg.transportFactory(new TFramedTransport.Factory());
            arg.processorFactory(new TProcessorFactory(processor));
    
            TServer server = new THsHaServer(arg);
            System.out.println("Thrift Server Started!");
            server.serve();
        }
    }

    第七步:编写客户端ThriftClient

    package com.ssy.netty.thrift;
    
    import generated.Person;
    import generated.PersonService;
    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;
    
    public class ThriftClient {
        public static void main(String[] args) {
            TTransport transport = new TFramedTransport(new TSocket("localhost",8899),600);
            TProtocol protocol = new TCompactProtocol(transport);
            PersonService.Client client = new PersonService.Client(protocol);
    
            try {
                transport.open();
                Person person = client.getPersonByUsername("张三");
                System.out.println(person.getUsername());
                System.out.println(person.getAge());
                System.out.println(person.isMarried());
                System.out.println("---------------------------");
                Person person2 = new Person();
                person2.setUsername("李四");
                person2.setAge(30);
                person2.setMarried(true);
                client.savePerson(person2);
    
            }catch (Exception e){
                throw new RuntimeException(e.getMessage(),e);
            }finally {
                transport.close();
            }
        }
    }

    第八步:分别运行服务端和客户端查看效果

       

     通过本节案例,我们通过java编写的服务端和客户端实现了基于Thrift的通信,下节我们分别用java和python来实现同样的功能。

  • 相关阅读:
    云计算架构
    Java多线程中static变量的使用
    Spring单例与线程安全小结
    sparkSQL实战详解
    sparkSQL整体实现框架
    spark架构
    如何快速定位出一个IP地址的归属地?——二分查找变体
    如何在 1000 万个整数中快速查找某个整数?——二分查找
    语言库中常用的排序算法qsort()底层结构
    链表常考笔试面试题(常备)
  • 原文地址:https://www.cnblogs.com/happy2010/p/10886352.html
Copyright © 2020-2023  润新知