欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Thrift初学

程序员文章站 2022-06-17 12:14:04
...

参考文章:

https://blog.csdn.net/zkp_java/article/details/81879577

https://www.cnblogs.com/newboys/p/9366762.html

 

本篇文章思路如下:

(如果不明白什么是RPC的,先去大致看一下RPC是做什么的,流程图是什么亚子的https://blog.csdn.net/weixin_41485567/article/details/103179034

先来说一下thrift是什么?用来做什么?它的优势是什么?

再来说一下它的架构(这里可以不必理解的很透彻,可以看完例子之后,再回想),

写一个thrift的例子(客户端、服务端均使用的Java,没体现出跨语言哈),

把这个例子与它的架构对应上,

思考它在RPC框架上,做了哪些封装(也就是说他帮我们做了哪些工作;我们需要做什么工作即可)。

 

附:

关于RPC的文章中,提到的RPC流程图为:

Thrift初学

化简图:

Thrift初学

 

什么是Thrift?

Thrift是一种接口描述语言和二进制通讯协议,它被用来定义和创建跨语言的服务。它被当作一个远程过程调用(RPC)框架来使用,是由Facebook为“大规模跨语言服务开发”而开发的。--百度百科

Apache Thrift软件框架(用于可扩展的跨语言服务开发)将软件堆栈与代码生成引擎结合在一起,以构建可在C ++,Java,Python,PHP,Ruby,Erlang,Perl,Haskell,C#,可可,JavaScript,Node.js,Smalltalk,OCaml和Delphi等语言。--Apache Thrift官网

提取关键字:一个RPC框架,支持跨语言,二进制通讯协议

他是一个典型的C/S结构,通过IDL(Interface Description Language)来关联客户端和服务端

(IDL就是后面写的data.thrift文件的内容)

优势:在高并发、大数据量、多语言环境下有优势

e.g. 相比于HTTP这个文本传输协议,Thrift可用TCP进行二进制传输,节省了一部分开销

 

Thrift的架构

Thrift初学

TTrasport层

Thrift的数据传输方式:

  1. TSocket:阻塞式Socket
  2. TFramedTransport:以frame为单位进行传输,非阻塞式服务中使用
  3. TFileTransport:以文件形式进行传输

TProtocol层

客户端和服务端之间传输数据的协议(传输数据的格式)

  1. TBinaryProtocol:二进制格式
  2. TCompactProtocol:压缩格式
  3. TJSONProtocol:JSON格式
  4. TSimpleJSONProtocol:提供只写的JSON协议

Server模型

  1. TSimpleServer:简单的单线程服务模型,常用于测试
  2. TThreadPoolServer:多线程服务模型,使用标准的阻塞式IO
  3. TNonBlockingServer:多线程服务模型,使用非阻塞式IO(需要使用TFramedTransport数据传输方式)
  4. THsHaServer:THsHa引入了线程池去处理,其模型读写任务放到线程池去处理,Half-sync/Half-async处理模式,Half-async是在处理IO事件上(accept/read/write io)异步处理,Half-sync用于handler(数据处理部分/服务实现部分)对rpc的同步处理

 

一个简单的例子

首先我们需要写一个IDL文件

先普及一下IDL~ https://blog.csdn.net/weixin_41485567/article/details/103165822

data.thrift文件

// data.thrift
namespace java thrift

typedef i16 short
typedef i32 int
typedef i64 long
typedef bool boolean
typedef string String

// struct关键字用于定义结构体,相当于面向对象编程语言中的类
struct Person {
    // 相当于定义类中的成员,并生成相应的get和set方法,optional表示username这个成员可以没有
    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 {
    // service中可以定义若干个服务,相当于Java Interface中定义的方法
    Person getPersonByUsername(1: required String username) throws (1: DataException data),
    void savePerson(1: required Person person)
}

在IDL文件所在的目录下执行thrift --gen data.thrift生成对应的java代码,并引入到Java工程当中

或者thrift -r --gen data.thrift

生成的文件名就是定义的元素名,这里有三个。

项目的结构图如下:

Thrift初学

编写服务端代码

服务(data.thrift的service中定义的两个服务)的实现类

package com.thrift.practice.server;

import thrift.generated.DataException;
import thrift.generated.Person;
import thrift.generated.PersonService;

/**
 * 我们在data.thrift里面定义了两个服务(接口)
 * 现在要写这两个服务的实现类
 */
public class PersonServiceImpl implements PersonService.Iface {

    @Override
    public Person getPersonByUsername(String username) throws DataException {
        System.out.println("Got Client Param: " + username);

        return new Person().setUsername(username).setAge(20).setMarried(false);
    }

    @Override
    public void savePerson(Person person) throws DataException {
        System.out.println("Got Client Param:");

        System.out.println(person.username);
        System.out.println(person.age);
        System.out.println(person.married);
    }
}

实现一个能监听RPC的服务器

package com.thrift.practice.server;

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;
import thrift.generated.PersonService;

/**
 * 实现一个服务器
 */
public class ThriftServer {
    public static void main(String[] args) throws Exception {

        // 定义服务器使用的socket类型
        TNonblockingServerSocket tNonblockingServerSocket = new TNonblockingServerSocket(8899);

        // 创建服务器参数
        THsHaServer.Args arg = new THsHaServer.Args(tNonblockingServerSocket).minWorkerThreads(2).maxWorkerThreads(4);

        // 配置数据传输的方式
        arg.transportFactory(new TFramedTransport.Factory());

        // 配置传输数据的格式
        arg.protocolFactory(new TCompactProtocol.Factory());

        // 请求处理器(就是你实现的那个接口啦~)
        PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());
        arg.processorFactory(new TProcessorFactory(processor)); // 配置处理器用来处理rpc请求

        // 本示例中使用半同步半异步方式的服务器模型
        TServer server = new THsHaServer(arg);

        System.out.println("Thrift Server Started!");

        // 启动服务
        server.serve();
    }
}

编写客户端

package com.thrift.practice.client;

import org.apache.thrift.TException;
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;
import thrift.generated.Person;
import thrift.generated.PersonService;

public class ThriftClient {

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

        TSocket tSocket = new TSocket("localhost", 8899);
        tSocket.setTimeout(900);

        TTransport tTransport = new TFramedTransport(tSocket);

        TProtocol tProtocol = new TCompactProtocol(tTransport);

        PersonService.Client client = new PersonService.Client(tProtocol);

        tTransport.open();

        // 这一句重点体现了RPC!!看似像一个本地调用,实际上这个方法负责发送给服务端,让服务端处理,并接收返回值
        Person person = client.getPersonByUsername("zjy");
        // 打印接收的结果
        System.out.println(person.username);
        System.out.println(person.age);
        System.out.println(person.married);

        System.out.println("-----------------------");

        Person newPerson = new Person();
        newPerson.username = "yjz";
        newPerson.age = 30;
        newPerson.married = true;

        client.savePerson(newPerson);

        tTransport.close();
    }
}

将例子与架构对应上

服务器端

Thrift初学

 

客户端

Thrift初学

这里我都是按照由底层到高层顺序写的。(这不是必须的)

 

关于RPC

首先,RPC有如下两个特点:

  1. 远程调用服务
  2. 使用时,要像本地调用一样,让使用者感受不到远程调用的逻辑

显然,thrift框架满足第一个特点,那么他是如何满足第二个特点的呢?

服务端代码中

// 这一句重点体现了RPC!!看似像一个本地调用,实际上这个方法负责发送给服务端,让服务端处理,并接收返回值 Person

person = client.getPersonByUsername("zjy");

该方法调用了PersonService(thrift生成)中

public Person getPersonByUsername(String username) throws DataException, org.apache.thrift.TException
{
   send_getPersonByUsername(username);
   return recv_getPersonByUsername();
}

它负责发送数据,接收结果。

这就满足了RPC的第二个特点。

 

所以,换个说法就是,thrift框架帮助我们写了远程调用的逻辑,也就是我们只需要实现服务,并调用服务,可以忽略远程调用的逻辑。(当然客户端和服务端的配置还是要编写的)

相关标签: Thrift