• Thrift IDL


    Thrift 采用IDL(Interface Description Language)来定义通用的服务接口,并通过生成不同的语言代理实现来达到跨语言、平台的功能,本文对Thrift IDL文件的常用语法进行说明。

    数据类型

    基本类型(Base Types)

    Thrift 不支持无符号整数类型,因为很多编程语言不存在无符号整数类型。

    Type Desc GO
    bool 布尔值 bool
    byte 有符号的 8 位整数 int8
    i16 有符号的 16 位整数 int16
    i32 有符号的 32 位整数 int32
    i64 有符号的 64 位整数 int64
    double 64 位浮点数 float64
    string 文本字符串(UTF-8编码格式) string

    特殊类型(Special Types)

    Type Desc GO
    binary 未编码的字节序列,string 的一种特殊形式 []byte

    集合容器(Containers)

    Type Desc GO
    list<T> 元素有序列表,允许重复 []T
    set<T> 元素无序列表,不允许重复 []T
    map<K,V> key-value结构数据,key 不允许重复 map[K]V

    在使用容器类型时必须指定泛型,否则无法编译 IDL 文件。上述类型中,Go 语言中没有 set 类型,所以set<T>对应的是切片[]T。容器中元素可以除了 service 之外的任何类型,包括 exception。


    Thrift 文件组成

    Document   ::= Header* Definition*
    Header     ::= Include | CppInclude | Namespace
    Definition ::= Const | Typedef | Enum | Senum | Struct | Union | Exception | Service
    

    一个 Thrift 文件就是一个 Document,而一个 thrift 文件又是由若干 Header 和 Definition 组成的,当然也可以是一个空文件。

    命名空间(namespace)

    Thrift 中的命名空间类似 Go 或者 Java 中的package,提供一种组织(隔离)代码的简便方式。名字空间也可以用于解决类型定义中的名字冲突。由于每种语言均有自己的命名空间定义方式,Thrift 允许开发者针对特定语言定义namespace

    namespace go base
    namespace py base 
    
    struct Base {
      ...
    }
    
    // Go 语言转换为以下代码
    package base
    
    type Base struct {
        ...
    }
    

    include

    在真正的业务开发中,我们不可能把所有的服务都定义到一个文件中,通常会根据业务模块进行拆分,然后将这些服务include到一个入口文件中。

    // base.thrift
    namespace go base
    
    struct Base {
      ...
    }
    
    // example.thrift
    include "base.thrift"  // 使用 include关键字 + Thrift文件路径,引用其它 thrift 文件
    
    struct Example {
        // 当引用其它 Thrift 文件中的对象时,使用被引用的 Thrift 文件名作为前缀进行访问
        1: base.Base ExampleBase   
    }
    

    注释(comment)

    Thrift 支持 C 多行风格和Java/C++单行风格,示例如下:

    /**
     * 多行注释
     * 类似 C
     */
    
    // C++/Java 当行注释
    

    常量(const)

    const 字段类型 名称标识 = 值 | 列表
    
    // 常量定义
    const i32 MALE_INT = 1
    const map<i32, string> GENDER_MAP = {1: "male", 2: "female"}
    

    类型别名(typedef)

    typedef 原类型 自定义类型
    
    // 某些数据类型比较长可以用别名简化
    typedef i32 MyInteger
    typedef map<i32, string> gmp
    

    枚举类型(enum)

    enum 名称标识 {
        字段标识 = int常量
    }
    
    // 枚举常量必须是 32 位的正整数,默认从 0 开始赋值
    enum GenderEnum{
        UNKNOWN // 0
        MALE    // 1,前一个元素加 1
        FEMALE  // 2,前一个元素加 1 
    }
    
    enum Numbers {
        ONE = 1   // 1
        TWO       // 2,前一个元素加 1
        THREE     // 3,前一个元素加 1
        FIVE = 5  // 5
        SIX       // 6,前一个元素加 1
        EIGHT = 8 // 8
    }
    

    结构体类型(struct)

    struct 是 Thrift 定义的一个通用对象,相当于面向对象语言中的类。格式定义如下:

    struct 名称标识 {
        数字标识: (required|optional)? 类型 名称标识 (= 值)?
    }
    
    // 示例
    struct User {
        1: required string name  // 该字段必填
        2: optional i32 age = 0  // 该字段选填
        3: bool gender           // 默认类型为 required 和 optional 的结合
    }
    
    • struct 不能继承,但是可以嵌套,包括引用自己;
    • 成员字段是强类型,即明确指定类型,字段不允许重复;
    • 成员字段前使用正整数+冒号进行编号(如1:),编号不允许重复,可以不连续;
    • 成员字段间分隔符可以使用,;,可以混用也可均不用,为方便阅读,建议统一;
    • 成员字段可以使用 optional 和 required 修饰,也可以不填,使用默认类型。区别在于,optional 修饰的字段只有在被传值时才会被序列化;required 修饰的字段在远程调用时必须传值,也必须被序列化;默认类型字段是 required 和 optional 的结合,可以不传值,但是必须被序列化;
    • 成员字段可以设置默认值。

    ☕️ 示例代码

    // guild.thrift
    namespace go io.buman.guild
    
    struct Guild {
        10: i32 id      // 公会 ID
        20: string name // 公会名称
    }
    
    // player.thrift
    include "guild.thrift"  // 引用 guild.thrift 文件
    namespace go io.buman.player
    
    // 变量默认赋值从 0 开始
    enum GenderEnum{
        UNKNOWN   // 0
        MALE      // 1
        FEMALE    // 2
    }
    
    // 第一个变量赋值为 1,后面的变量一次递增
    enum RoleEnum{
        WARIOR = 1  // 战吊 1
        MAGE        // 法爷 2
        WARLOCK     // 术士 3
        PRIEST      // 牧师 4
        DRUID       // 德鲁伊 5
    }
    
    struct Player {
        1: i32 id
        2: required string name
        3: required RoleEnum role
        4: required GenderEnum gender = GenderEnum.UNKNOWN
        5: Player cp  // 可以自己嵌套自己
        // 当引用其它 Thrift 文件中的对象时,使用被引用的 Thrift 文件名作为前缀进行访问
        6: guild.Guild guild
    }
    

    异常(exception)

    Thrift 支持在服务里面使用 exception 关键字自定义异常,结构上等同于结构体,便于客户端更好的识别和处理服务的各种异常状况。

    exception 名称标识 {
        数字标识: (required|optional)? 类型 名称标识 (= 值)?
    }
    
    exception PlayerNotFoundException {
        1: i32 code = 400,
        2: string msg
    }
    

    服务(service)

    service 名称标识 (extends 名称标识)? {
        (oneway)? 返回类型 名称标识(字段列表) throws(字段列表)?
    }
    
    service PlayerService {
        // 玩家注册
        Player signIn(1: Player player)
    
        // 查询所有玩家
        list<Player> queryAllPlayer()
    
        // 注册公会
        guild.Guild registerGuild(1: guild.Guild guild)
    
        // 查询所有公会
        list<guild.Guild> queryAllGuild()
    
        // 为玩家添加cp
        Player addCp(1: i32 pid, 2: Player player) throws (1: PlayerNotFoundException e)
    
        // 玩家加入公会
        Player joinGuild(1: i32 pid, 2: i32 gid) throws (1: PlayerNotFoundException e)
    }
    
    service ExampleService {
        oneway void GetName(1: string UserId
        void GetAge(1: string UserId) throws (1: Error err)
    }
    
    • 命名函数具有参数列表和返回值类型,参数列表可以为空,没有返回值使用 void 关键字修饰;
    • 参数列表类似于 struct 中的成员字段定义,每个参数前都需要正整数+冒号编号,编号可以不连续;
    • 参数列表分隔符可以使用,;,可以混用也可均不用,习惯使用,
    • 抛出的异常 exception 也需要被编号;
    • 默认情况下,非oneway修饰的函数是应答式,即req-resp,客户端发请求,服务端返回响应;被oneway修饰后的函数,客户端只是发起请求,无须关注返回,服务端也不会响应,与 void 的区别在于 void 类型的函数还可以返回异常。

    参考

    1. Thrift入门基础知识-thrift文件(IDL)说明和生成目标语言源代码
    2. Thrift之IDL
    3. Thrift & IDL 介绍
    4. https://thrift.apache.org/docs/idl
  • 相关阅读:
    crawlspider的源码学习
    df 参数说明
    Linux top 命令各参数详解
    Redis info参数总结
    python 读写 Excel文件
    python之Selenium库的使用
    heapq模块
    Python数据库连接池DButils
    【leetcode】701. Insert into a Binary Search Tree
    【leetcode】940. Distinct Subsequences II
  • 原文地址:https://www.cnblogs.com/zongmin/p/16372253.html
Copyright © 2020-2023  润新知