Thrift快速入门和简单示例

2022-06-27 09:51:01 浏览数 (1)

文章目录
  • Thrift介绍
  • Thrift的架构
  • Thrift的特性
    • 开发速度快
    • 接口维护简单
    • 学习成本低
    • 多语言/跨语言支持
    • 稳定/广泛使用
  • 快速入门例子
    • 编译安装
    • 创建Thrift IDL文件
    • 通过编译器编译user.thrift文件,生成java接口类文件
    • 实现UserServiceService.Iface的定义方法
    • 服务器端程序编写
    • python客户端代码编写

Thrift介绍

Thrift是一个轻量级、跨语言的RPC框架,主要用于各个服务之间的RPC通信,它通过自身的IDL中间语言, 并借助代码生成引擎生成各种主流语言的RPC服务端/客户端模板代码。Thrift支持多种不同的编程语言,包括C , Java, Python, PHP等。

Thrift官网地址,以下内容基于Apache Thrift v0.15.0。

RPC 全称 Remote Procedure Call——远程过程调用。RPC技术简单说就是为了解决远程调用服务的一种技术,使得调用者像调用本地服务一样方便透明

Thrift是一个典型的CS(客户端/服务端)结构,客户端和服务端可以使用不同的语言开发。既然客户端和服务端能使用不同的语言开发,那么一定就要有一种中间语言来关联客户端和服务端的语言,这种语言就是IDL (InterfaceDescription Language)

Thrift的架构

Thrift技术栈分层从下向上分别为:传输层(Transport Layer)、协议层(Protocol Layer)、处理层(Processor Layer)和服务层(Server Layer)。最底层是IO层,如通过socket进行网络通信。

  • 传输层(Transport Layer):传输层负责直接从网络中读取和写入数据,它定义了具体的网络传输协议。thrift传输层支持阻塞式IO和非阻塞式IO。
  • 协议层(Protocol Layer):协议层定义了数据传输格式,负责网络传输数据的序列化和反序列化;比如说JSON、XML、二进制数据等。
  • 处理层(Processor Layer):处理层是由具体的IDL(接口描述语言)生成的,封装了具体的底层网络传输和序列化方式,并委托给用户实现的Handler进行处理。
  • 服务层(Server Layer):整合上述组件,提供具体的网络IO模型(单线程/多线程/事件驱动),形成最终的服务。即业务逻辑代码。

Thrift的特性

开发速度快

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

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

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

接口维护简单

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

学习成本低

因为其来自Google Protobuf开发团队,所以其IDL文件风格类似Google Protobuf,且更加易读易懂;特别是RPC服务接口的风格就像写一个面向对象的Class一样简单。 初学者只需参照:http://thrift.apache.org/,一个多小时就可以理解Thrift IDL文件的语法使用。

多语言/跨语言支持

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

稳定/广泛使用

Thrift在很多开源项目中已经被验证是稳定和高效的,例如Cassandra、Hadoop、HBase等;国外在Facebook中有广泛使用,国内包括百度、美团小米、和饿了么等公司。

快速入门例子

编写user.thrift,通过IDL中间语言生成java代码,python代码,结合生成的代码,编写java服务端和python客户端,实现python跨语言调用java服务端接口代码。

编译安装

thrift编译器的安装 参考文档:https://thrift.apache.org/docs/install/ windows 安装 下载地址:https://thrift.apache.org/download centos 安装 参考文档:https://thrift.apache.org/docs/install/centos.html

安装好后,配置好环境变量,测试安装是否成功:

代码语言:javascript复制
#可以通过以下命令查看生成命令的格式
thrift -help

创建Thrift IDL文件

代码语言:javascript复制
namespace java com.test
namespace py com.test

struct User{
    1:i32 id
    2:string name
    3:i32 age=0
}

service  UserService {
  User getById(1:i32 id)
  bool isExist(1:string name)
}

通过编译器编译user.thrift文件,生成java接口类文件

代码语言:javascript复制
# 编译user.thrift
thrift -gen java user.thrift

生成UserService.java、User.java

User.java:

代码语言:javascript复制
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.15.0)", date = "2022-01-14")
public class User implements org.apache.thrift.TBase<User, User._Fields>, java.io.Serializable, Cloneable, Comparable<User> {
  private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("User");

  private static final org.apache.thrift.protocol.TField ID_FIELD_DESC = new org.apache.thrift.protocol.TField("id", org.apache.thrift.protocol.TType.I32, (short)1);
  private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)2);
  private static final org.apache.thrift.protocol.TField AGE_FIELD_DESC = new org.apache.thrift.protocol.TField("age", org.apache.thrift.protocol.TType.I32, (short)3);

  private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new UserStandardSchemeFactory();
  private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new UserTupleSchemeFactory();
  ...

UserService.java:

代码语言:javascript复制
@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"})
@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.15.0)", date = "2022-01-14")
public class UserService {

  public interface Iface {

    public User getById(int id) throws org.apache.thrift.TException;

    public boolean isExist(java.lang.String name) throws org.apache.thrift.TException;

  }
...

实现UserServiceService.Iface的定义方法

将生成类的UserService.java、User.java源文件拷贝进项目源文件目录中,并实现UserServiceService.Iface的定义的getById()方法。

代码语言:javascript复制
package com.test.service;

import org.apache.thrift.TException;

import com.test.User;
import com.test.UserService;

public class UserServiceImpl implements UserService.Iface {

    @Override
    public User getById(int id) throws TException {

        System.out.println("=====调用getById=====");
        //todo 模拟业务调用
        User user = new User();
        user.setId(id);
        user.setName("fox");
        user.setAge(30);

        return user;
    }

    @Override
    public boolean isExist(String name) throws TException {
        return false;
    }
}

服务器端程序编写

代码语言:javascript复制
public class SimpleService {

    public static void main(String[] args) {

        try {
            TServerTransport serverTransport = new TServerSocket(9090);

            //获取processor
            UserService.Processor processor = new UserService.Processor(new UserServiceImpl());
            //指定TBinaryProtocol
            TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory();

            TSimpleServer.Args targs = new TSimpleServer.Args(serverTransport);
            targs.processor(processor);
            targs.protocolFactory(protocolFactory);

            //单线程服务模型,一般用于测试
            TServer server = new TSimpleServer(targs);

            System.out.println("Starting the simple server...");
            //暴露服务
            server.serve();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

python客户端代码编写

  1. 通过编译器编译user.thrift文件,生成python代码然后将生成的 python 代码 和 文件,放到新建的 python 项目中
代码语言:javascript复制
thrift -gen py user.thrift
  1. python中使用thrift需要安装thrift模块
代码语言:javascript复制
pip install thrift
  1. 创建python客户端程序
代码语言:javascript复制
from thrift.transport import TSocket, TTransport
from thrift.protocol import TBinaryProtocol
from com.tuling import UserService
# Make socket
transport = TSocket.TSocket('localhost', 9090)
transport.setTimeout(600)
# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)
# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the protocol encoder
client = UserService.Client(protocol)
# Connect!
transport.open()
result = client.getById(1)
print(result)

0 人点赞