RPC框架之Thrift—实现Go和Java远程过程调用

简介: RPC框架之Thrift—实现Go和Java远程过程调用

官网:https://thrift.apache.org/

1 概览

+-------------------------------------------+
| Server                                    |
| (single-threaded, event-driven etc)       |
+-------------------------------------------+
| Processor                                 |
| (compiler generated)                      |
+-------------------------------------------+
| Protocol                                  |
| (JSON, compact etc)                       |
+-------------------------------------------+
| Transport                                 |
| (raw TCP, HTTP etc)                       |
+-------------------------------------------+

我们把他换成自己容易理解的方式:

2 分层解析

2.1 Transport(传输层)

传输层为网络的读写提供了一个简单的抽象。这使得Thrift能够将底层传输与系统的其他部分解耦(例如,序列化/反序列化)。

接口支持的方法:

  • open
  • close
  • read
  • write
  • flush

可传输的底层协议:

  • 文件传输(I/O)
  • HTTP
2.2 Protocol(协议层)

协议层抽象定义了一种机制来将内存中的数据结构映射到连线格式。换句话说,协议指定数据类型如何使用底层的Transport对自己进行编码/解码。因此,协议实现控制编码方案并负责(反)序列化。这种意义上的协议的一些例子包括JSON、XML、纯文本、压缩二进制文件等。

接口支持的方法:

写(编码)方法:

  • writeMessageBegin(name, type, seq)
  • writeMessageEnd()
  • writeStructBegin(name)
  • writeStructEnd()
  • writeFieldBegin(name, type, id)
  • writeFieldEnd()
  • writeFieldStop()
  • writeMapBegin(ktype, vtype, size)
  • writeMapEnd()
  • writeListBegin(etype, size)
  • writeListEnd()
  • writeSetBegin(etype, size)
  • writeSetEnd()
  • writeBool(bool)
  • writeByte(byte)
  • writeI16(i16)
  • writeI32(i32)
  • writeI64(i64)
  • writeDouble(double)
  • writeString(string)

读(解码)方法:

  • name, type, seq = readMessageBegin()
    readMessageEnd()
  • name = readStructBegin()
    readStructEnd()
  • name, type, id = readFieldBegin()
    readFieldEnd()
  • k, v, size = readMapBegin()
    readMapEnd()
  • etype, size = readListBegin()
    readListEnd()
  • etype, size = readSetBegin()
    readSetEnd()
  • bool = readBool()
  • byte = readByte()
  • i16 = readI16()
  • i32 = readI32()
  • i64 = readI64()
  • double = readDouble()
  • string = readString()

支持的编码协议:

  • 二进制
  • compact
  • json
2.3 Processor(处理器层)

处理器封装了从输入流读取数据和向输出流写入数据的能力。输入和输出流由Protocol对象表示。

接口方法样例:

interface TProcessor {
    bool process(TProtocol in, TProtocol out) throws TException
}
2.4 Server(应用服务层)

一个服务器集合了上面描述的所有不同的特性:

  • 创建一个运输
  • 为传输创建输入/输出协议
  • 创建基于输入/输出协议的处理器
  • 等待传入的连接,并将它们交给处理器

3 thrift安装

下载页:https://thrift.apache.org/download

下载Windows版本:

下载后可以直接使用,或者放在固定文件设置环境变量

4 Go使用thrift

4.1 项目结构和依赖安装

依赖安装:

go get -u github.com/apache/thrift
4.2 编写thrift文件并编译
namespace go hello
struct HelloReq {
    1: string msg;
}
struct HelloResp {
    1: string msg;
}
service HelloService {
    //返回值类型 方法名(参数序号:参数类型 参数名);
    HelloResp Hello(1: HelloReq req);
}

编译:

thrift -r --gen go hello.thrift
4.3 服务端代码
type HelloServiceImpl struct {
}
func (e *HelloServiceImpl) Hello(ctx context.Context, req *hello.HelloReq) (*hello.HelloResp, error) {
   fmt.Printf("message from client: %v\n", req.GetMsg())
   res := &hello.HelloResp{
      Msg: "Hi Client ~",
   }
   return res, nil
}
func main() {
  transport, err := thrift.NewTServerSocket(":9898")
  if err != nil {
    panic(err)
  }
  handler := &HelloServiceImpl{}
  processor := hello.NewHelloServiceProcessor(handler)
  //也可以选择Framed传输协议,但是必须确保服务端和客户端的传输协议一致
  transportFactory := thrift.NewTBufferedTransportFactory(1024)
  //应对Java客户端
  //factory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
  //可以选择任意一种编码协议,但是必须确保服务端和客户端的编码协议一致
  protocolFactory := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{}) //布尔参数strictRead, strictWrite,读和写时是否加入版本校验。
  server := thrift.NewTSimpleServer4(
    processor,
    transport,
    transportFactory,
    //factory,
    protocolFactory,
  )
  if err := server.Serve(); err != nil {
    panic(err)
  }
}
4.4 客户端代码
func main() {
  transportFactory := thrift.NewTBufferedTransportFactory(1024)
  //可以选择任意一种通信协议,但是必须确保服务端和客户端的通信协议一致
  protocolFactory := thrift.NewTBinaryProtocolFactoryConf(&thrift.TConfiguration{}) //布尔参数strictRead, strictWrite,读和写时是否加入版本校验。
  //Java服务端
  //factory := thrift.NewTCompactProtocolFactoryConf(&thrift.TConfiguration{})
  transport:= thrift.NewTSocketConf("127.0.0.1:9898",&thrift.TConfiguration{})
  useTransport, err := transportFactory.GetTransport(transport)
  client := hello.NewHelloServiceClientFactory(useTransport, protocolFactory)
  if err := transport.Open(); err != nil {
    fmt.Fprintln(os.Stderr, "Error opening socket to 127.0.0.1:9898", " ", err)
    os.Exit(1)
  }
  defer transport.Close()
  req := &hello.HelloReq{Msg: "Hello Server ~"}
  res, err := client.Hello(context.Background(), req)
  if err != nil {
    log.Println("Echo failed:", err)
    return
  }
  log.Println("response:", res.Msg)
}
4.5 验证

5 Java使用thrift

5.1 项目结构和依赖

依赖:

<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.15.0</version>
</dependency>
5.2 编写thrift文件并编译
namespace java hello
struct HelloReq {
    1: string msg;
}
struct HelloResp {
    1: string msg;
}
service HelloService {
    HelloResp Hello(1: HelloReq req);
}

编译:

thrift -r --gen java hello.thrift

注意:编译完成之后需要将生成的.java文件放到项目结构中的对应位置。

5.3 服务端代码
public class ThriftServer {
    public static void main(String[] args) throws TTransportException {
        //设置服务器端口  TNonblockingServerSocket-非堵塞服务模型
        TNonblockingServerSocket serverSocket = new TNonblockingServerSocket(8899);
        //参数设置
        THsHaServer.Args arg = new THsHaServer.Args(serverSocket).minWorkerThreads(2).maxWorkerThreads(4);
        //处理器
        //PersonService.Processor<PersonServiceImpl> processor = new PersonService.Processor<>(new PersonServiceImpl());
        HelloService.Processor<HelloServiceImpl> processor = new HelloService.Processor<>(new HelloServiceImpl());
        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.serve();
    }
}
//Handler
class HelloServiceImpl implements HelloService.Iface {
    @Override
    public HelloResp Hello(HelloReq req) throws TException {
        System.out.println(req.msg);
        return new HelloResp("Hello Client ~");
    }
}
5.4 客户端代码
public class ThriftClient {
    public static void main(String[] args) throws TException {
        TTransport transport = new TFramedTransport(new TSocket("localhost", 8899), 600);
        TProtocol protocol = new TCompactProtocol(transport);
        HelloService.Client client = new HelloService.Client(protocol);
        transport.open();
        String msg = client.Hello(new HelloReq("Hi Server ~")).msg;
        System.out.println(msg);
        transport.close();
    }
}

6 使用thrift进行Java和Go的远程通信

  • 原则:客户端与服务端的协议一致即可

例如Java做客户端:

public class ThriftClientGo {
    public static void main(String[] args) throws TTransportException {
        TTransport transport = new TFramedTransport(new TSocket("127.0.0.1", 9898), 8000);
        TBinaryProtocol binaryProtocol = new TBinaryProtocol(transport);
        HelloService.Client client = new HelloService.Client(binaryProtocol);
        try {
            transport.open();
            String msg = client.Hello(new HelloReq("Hi Server~")).msg;
            System.out.println(msg);
        }catch (Exception ex){
            throw new RuntimeException(ex.getMessage(),ex);
        }finally {
            transport.close();
        }
    }
}

参考文章:

https://thrift.apache.org/docs/concepts.html

https://thrift.apache.org/docs/concepts.html

https://blog.csdn.net/pengpengzhou/article/details/114841347

相关文章
|
JavaScript 前端开发 Java
通义灵码 Rules 库合集来了,覆盖Java、TypeScript、Python、Go、JavaScript 等
通义灵码新上的外挂 Project Rules 获得了开发者的一致好评:最小成本适配我的开发风格、相当把团队经验沉淀下来,是个很好功能……
2333 103
|
安全 Java 编译器
对比Java学习Go——基础理论篇
本章介绍了Java开发者学习Go语言的必要性。Go语言以简单、高效、并发为核心设计哲学,摒弃了传统的类继承和异常机制,采用组合、接口和多返回值错误处理,提升了代码清晰度与开发效率。Go直接编译为静态二进制文件,启动迅速、部署简便,其基于Goroutine和Channel的并发模型相较Java的线程与锁机制更轻量安全。此外,Go Modules简化了依赖管理,与Java的Maven/Gradle形成鲜明对比,提升了构建与部署效率。
743 1
|
人工智能 Kubernetes Java
回归开源,两位 Java 和 Go 程序员分享的开源贡献指引
Higress是一个基于Istio和Envoy的云原生API网关,支持AI功能扩展。它通过Go/Rust/JS编写的Wasm插件提供可扩展架构,并包含Node和Java的console模块。Higress起源于阿里巴巴,解决了Tengine配置重载及gRPC/Dubbo负载均衡问题,现已成为阿里云API网关的基础。本文介绍Higress的基本架构、功能(如AI网关、API管理、Ingress流量网关等)、部署方式以及如何参与开源贡献。此外,还提供了有效的开源贡献指南和社区交流信息。
2628 33
|
12月前
|
存储 Java Go
对比Java学习Go——函数、集合和OOP
Go语言的函数支持声明与调用,具备多返回值、命名返回值等特性,结合`func`关键字与类型后置语法,使函数定义简洁直观。函数可作为一等公民传递、赋值或作为参数,支持匿名函数与闭包。Go通过组合与接口实现面向对象编程,结构体定义数据,方法定义行为,接口实现多态,体现了Go语言的简洁与高效设计。
348 4
|
存储 Java 编译器
对比Java学习Go——程序结构与变量
本节对比了Java与Go语言的基础结构,包括“Hello, World!”程序、代码组织方式、入口函数定义、基本数据类型及变量声明方式。Java强调严格的面向对象结构,所有代码需置于类中,入口方法需严格符合`public static void main(String[] args)`格式;而Go语言结构更简洁,使用包和函数组织代码,入口函数为`func main()`。两种语言在变量声明、常量定义、类型系统等方面也存在显著差异,体现了各自的设计哲学。
406 0
|
Java Shell Maven
【Azure Container App】构建Java应用镜像时候遇无法编译错误:ERROR [build 10/10] RUN ./mvnw.cmd dependency:go-offline -B -Dproduction package
在部署Java应用到Azure Container App时,构建镜像过程中出现错误:“./mvnw.cmd: No such file or directory”。尽管项目根目录包含mvnw和mvnw.cmd文件,但依然报错。问题出现在Dockerfile构建阶段执行`./mvnw dependency:go-offline`命令时,系统提示找不到可执行文件。经过排查,确认是mvnw文件内容异常所致。最终通过重新生成mvnw文件解决该问题,镜像成功构建。
749 1
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
351 10
|
Java Apache C++
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
Thrift 是一个轻量级、跨语言的远程服务调用框架,由 Facebook 开发并贡献给 Apache。它通过 IDL 生成多种语言的 RPC 服务端和客户端代码,支持 C++、Java、Python 等。Thrift 的主要特点包括开发速度快、接口维护简单、学习成本低和多语言支持。广泛应用于 Cassandra、Hadoop 等开源项目及 Facebook、百度等公司。
别再手写RPC了,Apache Thrift帮你自动生成RPC客户端及服务端代码
|
安全 Java Go
Java&Go泛型对比
总的来说,Java和Go在泛型的实现和使用上各有特点,Java的泛型更注重于类型安全和兼容性,而Go的泛型在保持类型安全的同时,提供了更灵活的类型参数和类型集的概念,同时避免了运行时的性能开销。开发者在使用时可以根据自己的需求和语言特性来选择使用哪种语言的泛型特性。
403 7
|
负载均衡 Java
使用Java实现RPC框架
使用Java实现RPC框架

热门文章

最新文章