RPC 框架之Thrift入门(一)

2023-12-13 04:02:51

📋 个人简介

  • 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜
  • 📝 个人主页:馆主阿牛🔥
  • 🎉 支持我:点赞👍+收藏??+留言📝
  • 💬格言:迄今所有人生都大写着失败,但不妨碍我继续向前!🔥
    请添加图片描述

前言

随着近些年微服务的盛行,使得服务逐渐模块化,功能化,单个服务仅仅实现某个特定的功能或者模块,因此服务间的调用变得常见且频繁,所以高性能且快速响应的服务调用成了必须去面对的问题,传统的http请求能面对跨语言的问题,但是性能远远无法达到高并发的要求,因此更偏向底层的RPC框架越来越受到青睐,像阿里的Dubbo,谷歌的gRPC,facebook的Thrift等等!本节将学习Thrift这个跨语言的Thrift RPC框架!

Thrift简介

Thrift是一个跨语言的服务部署框架,最初由Facebook于2007年开发,2008年进入Apache开源项目,主要用于各个服务之间的RPC通信,支持跨语言,常用的语言比如C++, Java, Python, PHP, Ruby, Erlang, Perl, C#, JavaScript, Node.js等都支持。(博主也是一名多语言爱好者,因此对Thrift也比较感兴趣,所以在后面,我也会写java,python 的这两种语言的RPC调用。)

IDL介绍

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

IDL 是一种用于定义接口和数据结构的语言,用于描述 Thrift 的服务接口和数据类型。在 Thrift 中使用的是 Thrift 自定义的 IDL 语言,它具有类似于其他接口描述语言的特性。

因此我们需要写IDL,然后使用Thrift编译器将IDL转换为对应的语言,我们开发者只需要实现具体的业务逻辑,无需关注底层逻辑!

IDL语法学习

在学习thrift之前,我们需要先简单的学习一下IDL的语法,很简单,和其他语言结构差不多。

1.基本类型

类型解释
bool布尔值
byte8位有符号整数
i1616位有符号整数
i3232位有符号整数
i6464位有符号整数
double64位浮点数
stringUTF-8编码的字符串
binary二进制串

2.struct结构体

先看例子:

struct Person {  // 定义 Person 结构体
    1: required string name;   // 姓名,必选字段
    2: required i32 age;       // 年龄,必选字段
    3: optional string sex;    // 性别,可选字段
}

如上面所示,它类似C语言的结构体,对应java中的Bean,其中required修饰的他的值初始化是必传项。

3.container容器

有三种可用的容器类型:

  • list
    元素类型为t的有序列表,允许重复。类似于java中的ArrayList。
  • set
    元素类型为t的无序表,不允许重复。类似于java中的HashSet。
  • map<t, t>
    键类型为t,值类型为t的键值对,键不允许重复。类似于java中的HashMap。

例如:

struct Test {
    1: map<string, User> usermap,
    2: set<i32> intset,
    3: list<double> doublelist
}

4.service服务

服务的定义方法在语义上等同于面向对象语言中的接口。

service PersonService {  // 定义 PersonService 服务接口
    Person getByName(1: string name);  // 根据姓名获取 Person 信息
    bool save(1: Person person);       // 保存 Person 信息
}

5.枚举(enum)

枚举的定义形式和Java的Enum定义差不多,例如:

enum Sex {
    MALE,
    FEMALE
}

6.异常(exception)

thrift支持自定义exception,规则和struct一样,如下:

exception RequestException {
    1: i32 code;
    2: string reason;
}

7.命名空间

thrift的命名空间相当于Java中的package的意思,主要目的是组织代码。thrift使用关键字namespace定义命名空间,例如:

namespace java com.aniu.service
namespace py example

namespace 后跟的是你要转化的语言以及生成的文件所在的包!

Thrift 编译器安装

写完IDL文件后,我们需要将其转换成对应语言!因此,我们需要先安装Thrift编译器!这里博主用的Windows,macos和Linux自行下载!

网址:https://dlcdn.apache.org/thrift/0.19.0/thrift-0.19.0.exe

下载安装完成后,配置完环境变量,如下图,可查看版本!后续在其他语言例如java中引入maven包时,需要对应版本!
在这里插入图片描述

入门案例

这里编写一个简单的入门案例,实现rpc远程过程调用!
定义thrift 文件 person.thrift

namespace java com.aniu.service

struct Person {  // 定义 Person 结构体
    1: required string name;   // 姓名,必选字段
    2: required i32 age;       // 年龄,必选字段
    3: optional string sex;    // 性别,可选字段
}

service PersonService {  // 定义 PersonService 服务接口
    Person getByName(1: string name);  // 根据姓名获取 Person 信息
    bool save(1: Person person);       // 保存 Person 信息
}

使用

thrift --gen java person.thrift

命令,即可在当前目录下生成gen-java目录,里面即是生成的java代码!
在这里插入图片描述
在这里插入图片描述
引入对应版本的Maven包

<dependency>
    <groupId>org.apache.thrift</groupId>
      <artifactId>libthrift</artifactId>
      <version>0.19.0</version>
</dependency>

目录结构如下:
在这里插入图片描述
如上图,Person和PersonService即为thrift编译器将IDL转换后生成的java代码。

业务逻辑

PersonService里面有两个接口,我们需要实现接口,在实现类里面写业务逻辑。
在这里插入图片描述

package com.aniu.service.impl;

import com.aniu.service.Person;
import com.aniu.service.PersonService;
import org.apache.thrift.TException;

public class PersonServiceImpl implements PersonService.Iface {

    @Override
    public Person getByName(String name) throws TException {
        return new Person(name,18);
    }

    @Override
    public boolean save(Person person) throws TException {
        return false;
    }
}

服务端

package com.aniu.server;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TSimpleServer;
import org.apache.thrift.transport.TServerSocket;
import com.aniu.service.PersonService;
import com.aniu.service.impl.PersonServiceImpl;
public class Server {
    public static void main(String[] args) {
        try{
            // 创建一个新的 Thrift 服务端套接字,监听在端口 9000 上
            TServerSocket socket = new TServerSocket(9000);
            // 创建一个 PersonService 的 Processor。Processor 是 Thrift 中用于处理请求的接口,它需要一个实现了 PersonService 接口的对象作为参数。
            PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());
            // 创建一个二进制协议工厂对象。Thrift 支持多种协议,如 TBinaryProtocol、TCompactProtocol、TJSONProtocol 等,这里选择的是二进制协议。
            TBinaryProtocol.Factory factory = new TBinaryProtocol.Factory();
            // 创建一个 TSimpleServer 的参数对象 args1,并将之前创建的套接字、Processor 和协议工厂设置为其属性。
            TServer.Args args1 = new TSimpleServer.Args(socket);
            args1.processor(processor);
            args1.protocolFactory(factory);
            // 使用之前设置好的参数创建 TSimpleServer 对象
            TSimpleServer tSimpleServer = new TSimpleServer(args1);

            // 开始执行 TSimpleServer,开始监听并处理客户端的请求。
            tSimpleServer.serve();
        }catch (Exception e){
            System.out.println(e);
        }
    }
}

客户端

import com.aniu.service.PersonService;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.transport.TSocket;
public class Client {
    public static void main(String[] args) {
        try{
            // 创建一个 Thrift 的套接字对象,连接到在本地主机(localhost)的9000端口上运行的 Thrift 服务。
            TSocket socket = new TSocket("localhost", 9000);
            // 创建一个使用二进制协议的实例,该协议用于在客户端和服务器之间传输数据。
            TBinaryProtocol protocol = new TBinaryProtocol(socket);
            // 创建一个Thrift 客户端实例,它使用前面创建的二进制协议实例进行通信。
            PersonService.Client client = new PersonService.Client(protocol);
            // 打开与服务器端的连接,通过客户端对象(client)进行远程过程调用(RPC)或其他通信操作
            socket.open();

            // RPC 调用
            Person person = client.getByName("aniu");
            System.out.println(person);
        }catch (Exception e){
            System.out.println(e);
        }
    }
}

pom.xml

<dependencies>
    <dependency>
      <groupId>org.apache.thrift</groupId>
      <artifactId>libthrift</artifactId>
      <version>0.19.0</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.7.5</version>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.30</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>jakarta.annotation</groupId>
      <artifactId>jakarta.annotation-api</artifactId>
      <version>1.3.5</version>
      <scope>compile</scope>
    </dependency>
</dependencies>

运行

先启动服务端,再启动客户端,即可实现RPC调用!
在这里插入图片描述

结语

本节实现了一个简单的案例,但对于thrift的服务端如何创建的,客户端如何调用没有具体讲解,基本都在注释里,下一篇博文来讲解原理以及实现跨语言RPC调用!

文章来源:https://blog.csdn.net/qq_57421630/article/details/133588555
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。