SpringBoot与Apache Thrift整合,解决不同编程语言的微服务通信问题

开发 架构
Apache Thrift是一种跨语言的服务开发框架,由 Facebook 开发并捐赠给 Apache 基金会。它允许开发者使用一种定义接口的方式,生成不同编程语言的代码,从而实现不同语言之间的高效通信。

Apache Thrift是一种跨语言的服务开发框架,由 Facebook 开发并捐赠给 Apache 基金会。它允许开发者使用一种定义接口的方式,生成不同编程语言的代码,从而实现不同语言之间的高效通信。

哪些公司使用Apache Thrift?

Facebook:

  • Facebook 是 Apache Thrift 的最初开发者之一,广泛用于其内部的微服务通信。
  • 通过 Thrift,Facebook 能够实现不同语言之间的高效通信,并支持大规模的分布式系统。

Baidu:

  • Baidu 使用 Thrift 来构建其搜索引擎和其他在线服务。
  • Thrift 的高效性和可扩展性满足了 Baidu 对高性能的需求。

Yahoo!:

  • Yahoo! 在其多个项目中使用 Thrift,包括搜索、广告和其他核心服务。
  • Thrift 的可靠性和成熟度使其成为 Yahoo! 技术选择的一部分。

Twitter:

  • Twitter 使用 Thrift 来处理大量的实时数据流和 API 请求。
  • Thrift 的跨语言特性和高效的序列化能力帮助 Twitter 构建了高性能的服务架构。

Airbnb:

  • Airbnb 利用 Thrift 实现其内部服务间的通信,特别是在需要高吞吐量和低延迟的应用场景中。
  • Thrift 的灵活性和可扩展性使其成为 Airbnb 技术栈的重要组成部分。

Uber:

  • Uber 在其后端服务中广泛使用 Thrift 来管理微服务之间的通信。
  • Thrift 的高效性和可靠性确保了 Uber 系统的稳定运行。

Yelp:

  • Yelp 使用 Thrift 来管理其评论、搜索和其他关键服务之间的通信。
  • Thrift 的高效性和灵活性提升了 Yelp 的整体性能和可靠性。

选择Apache Thrift的好处

提高开发效率:

  • 自动代码生成:Thrift 编译器根据 IDL 文件自动生成客户端和服务端代码,减少手动编码的工作量。
  • 一致的数据结构:通过统一的 IDL 文件定义数据结构,确保前后端数据的一致性。

性能优化:

  • 低延迟:由于使用高效的二进制协议和灵活的传输层,Thrift 在高性能场景下表现出色。
  • 资源利用率高:较少的 CPU 和内存开销,适合大规模部署。

跨语言支持:

  • 多语言兼容性:Thrift 支持多种编程语言(如 Java、C++、Python、PHP、Ruby、JavaScript、Node.js、Go、Delphi 等),这对于需要在不同语言之间进行通信的分布式系统尤为重要。
  • 简化开发:开发者可以使用自己熟悉的语言编写服务端和客户端代码,而不需要担心底层协议的复杂性。

高效的序列化和反序列化:

  • 二进制协议:Thrift 使用高效的二进制协议进行数据传输,相比于 JSON 或 XML 更加紧凑,减少了带宽消耗并提高了传输速度。
  • 多种协议选项:除了默认的二进制协议外,Thrift 还提供了其他协议选项(如 TCompactProtocol、TJSONProtocol),可以根据具体需求选择合适的协议。

灵活的传输层:

  • 多种传输方式:Thrift 支持多种传输层实现,包括 TCP/IP、HTTP、内存缓冲区等,可以轻松集成到现有的网络架构中。
  • 可扩展性:易于扩展和定制,可以根据项目需求调整传输层的行为。

安全性:

  • 加密传输:可以通过 SSL/TLS 加密传输层,保护数据安全。
  • 身份验证和授权:结合其他安全机制,可以实现细粒度的身份验证和授权控制。

RPC 模式:

  • 同步和异步调用:Thrift 支持同步和异步 RPC 调用,适用于各种客户端-服务器模型的应用场景。
  • 简单易用:通过定义 IDL 文件,可以快速生成客户端和服务端代码,减少重复工作。

创建service.thrift文件

首先,在项目的根目录下创建一个名为idl的文件夹,然后在其中创建service.thrift文件。

// idl/service.thrift
namespace java com.example.service
namespace go service

enum ErrorCode {
    SUCCESS = 0,          // 成功
    UNKNOWN_ERROR = 1,    // 未知错误
    INVALID_REQUEST = 2,  // 请求无效
}

struct Request {
    1: required string message,  // 请求消息
}

struct Response {
    1: required ErrorCode errorCode,  // 错误码
    2: optional string errorMessage,  // 错误信息
    3: optional string result,        // 结果
}

service MyService {
    Response processRequest(1: Request request) throws (1: Exception e),  // 处理请求的方法
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

使用Thrift编译器生成Java和Go代码

# 生成Java代码
thrift --gen java -out src/main/java idl/service.thrift

# 生成Go代码
thrift --gen go -out ./gen-go idl/service.thrift
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

之后,src/main/java目录下会生成Java代码,而在当前目录下生成gen-go文件夹,包含Go代码。

Java代码实操

<!-- pom.xml -->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath/><!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Detailed demo project for Spring Boot and Apache Thrift integration</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
   <groupId>org.apache.thrift</groupId>
   <artifactId>libthrift</artifactId>
   <version>0.18.1</version>
</dependency>
<dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
   <groupId>ch.qos.logback</groupId>
   <artifactId>logback-classic</artifactId>
</dependency>
<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
   <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
   </plugin>
</plugins>
</build>
</project>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.

Thrift客户端配置

// src/main/java/com/example/demo/config/ThriftConfig.java
package com.example.demo.config;

import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;
import org.apache.thrift.transport.TTransportException;
import com.example.service.MyService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Configuration
publicclass ThriftConfig {

    privatestaticfinal Logger logger = LoggerFactory.getLogger(ThriftConfig.class);

    @Bean
    public MyService.Client thriftClient() {
        // 创建Thrift传输层,连接到本地9090端口
        TTransport transport = new TSocket("localhost", 9090);
        try {
            // 打开传输层连接
            transport.open();
            // 返回MyService客户端实例,使用二进制协议
            returnnew MyService.Client(new TBinaryProtocol(transport));
        } catch (TTransportException e) {
            // 记录错误日志并抛出异常
            logger.error("Failed to open Thrift client connection", e);
            thrownew RuntimeException(e);
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

Controller

接收HTTP请求并通过Thrift客户端调用远程服务:

// src/main/java/com/example/demo/controller/ApiController.java
package com.example.demo.controller;

import com.example.service.Request;
import com.example.service.Response;
import com.example.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RestController
@RequestMapping("/api")
publicclass ApiController {

    privatestaticfinal Logger logger = LoggerFactory.getLogger(ApiController.class);

    privatefinal MyService.Client client;

    @Autowired
    public ApiController(MyService.Client client) {
        this.client = client;
    }

    @PostMapping("/process")
    public ResponseEntity<String> process(@RequestBody String message) {
        // 创建请求对象
        Request request = new Request(message);
        try {
            // 调用Thrift客户端的processRequest方法
            Response response = client.processRequest(request);
            // 根据响应结果返回不同的HTTP状态码和消息
            if (response.getErrorCode() == com.example.service.ErrorCode.SUCCESS) {
                returnnew ResponseEntity<>(response.getResult(), HttpStatus.OK);
            } else {
                logger.warn("Error processing request: {}", response.getErrorMessage());
                returnnew ResponseEntity<>(response.getErrorMessage(), HttpStatus.BAD_REQUEST);
            }
        } catch (Exception e) {
            // 捕获异常并记录错误日志,返回内部服务器错误
            logger.error("Exception while processing request", e);
            returnnew ResponseEntity<>("Internal Server Error", HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.

启动主类

// src/main/java/com/example/demo/DemoApplication.java
package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

实现Go的服务端

// gen-go/server.go
package main

import (
"context"
"fmt"
"log"
"net"

"github.com/apache/thrift/lib/go/thrift"
 service "path/to/gen-go/service"
)

type myHandler struct{}

// ProcessRequest 处理请求的方法
func (h *myHandler) ProcessRequest(ctx context.Context, request *service.Request) (*service.Response, error) {
 log.Printf("Received request: %s\n", request.Message)
iflen(request.Message) == 0 {
// 如果请求消息为空,返回INVALID_REQUEST错误
return &service.Response{
   ErrorCode:    service.ErrorCode_INVALID_REQUEST,
   ErrorMessage: "Invalid request",
  }, nil
 }
// 返回成功响应
return &service.Response{
  ErrorCode: service.ErrorCode_SUCCESS,
  Result:    fmt.Sprintf("Processed: %s", request.Message),
 }, nil
}

func main() {
 handler := &myHandler{}
 processor := service.NewMyServiceProcessor(handler)

// 创建Thrift服务器监听9090端口
 serverTransport, err := thrift.NewTServerSocket(":9090")
if err != nil {
  log.Fatalf("Error opening server socket: %s", err.Error())
 }

 tFactory := thrift.NewTFramedTransportFactory(thrift.NewTBufferedTransportFactory(8192))
 pFactory := thrift.NewTBinaryProtocolFactoryDefault()

// 创建Thrift简单服务器
 server := thrift.NewTSimpleServer4(processor, serverTransport, tFactory, pFactory)

 fmt.Println("Starting the simple server... on port 9090...")
 err = server.Serve()
if err != nil {
  log.Fatalf("Error starting Thrift server: %s", err.Error())
 }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.

运行Go服务端

在终端中运行Go服务端:

cd gen-go
go run server.go
  • 1.
  • 2.

测试

curl -X POST http://localhost:8080/api/process -H "Content-Type: text/plain" -d "Hello from Go!"
  • 1.

Respons:

Processed: Hello from Go!
  • 1.


责任编辑:武晓燕 来源: Java知识日历
相关推荐

2025-03-12 08:42:28

2025-02-12 08:52:44

2012-01-18 10:41:29

ibmdw

2025-03-06 08:37:01

2025-03-06 10:59:24

2023-03-21 15:30:54

微服务通信架构

2009-01-08 17:45:10

服务器WebLinux

2025-03-07 08:31:54

2025-03-11 09:28:34

2022-05-16 08:07:15

微服务容器通信

2010-09-14 10:34:17

DIV CSS

2022-01-16 23:10:40

语言服务注册

2022-08-08 13:55:47

通信设计模式微服务

2024-04-24 12:03:20

2010-03-15 18:08:01

Python编程语言

2023-12-04 07:14:40

通信微服务

2025-01-20 00:10:00

Go语言Kratos

2017-08-03 09:37:35

SparkStreamKafkaDirect

2010-03-11 14:13:20

Python编程

2023-12-14 07:11:24

编程语言微服务
点赞
收藏

51CTO技术栈公众号