Skip to content

JsonToGrpc 网关过滤器工厂

概述

JsonToGrpc 它能够将 JSON 格式的请求负载转换为 gRPC 请求。这个过滤器解决了微服务架构中不同协议之间的通信问题,特别是当前端使用 HTTP/JSON,而后端服务使用 gRPC 时的协议转换需求。

业务场景

在现代微服务架构中,我们经常遇到以下场景:

  • 前端应用:使用 HTTP/JSON 协议进行 API 调用
  • 后端微服务:基于性能考虑使用 gRPC 协议
  • 网关层:需要在两种协议之间进行转换

JsonToGrpc 过滤器让您可以在不修改前端代码的情况下,将后端服务迁移到高性能的 gRPC 协议。

配置参数

JsonToGrpc 过滤器支持以下配置参数:

必需参数

  • protoDescriptor: Proto 描述符文件路径
  • protoFile: Proto 定义文件路径
  • service: 处理请求的服务短名称
  • method: 服务中处理请求的方法名称

生成 Proto 描述符文件

Proto 描述符文件可以使用 protoc 工具生成,需要指定 --descriptor_set_out 标志:

bash
protoc --proto_path=src/main/resources/proto/ \
--descriptor_set_out=src/main/resources/proto/hello.pb  \
src/main/resources/proto/hello.proto

当前版本不支持 `streaming` 流式传输。

配置示例

Java DSL 配置

kotlin
@Configuration
class GatewayRouteConfig {

    @Bean
    fun routes(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("json-grpc") { r ->
                r.path("/json/hello")
                    .filters { f ->
                        val protoDescriptor = "file:src/main/proto/hello.pb"
                        val protoFile = "file:src/main/proto/hello.proto"
                        val service = "HelloService"
                        val method = "hello"
                        f.jsonToGRPC(protoDescriptor, protoFile, service, method)
                    }
                    .uri("https://localhost:6565/testhello")
            }
            .build()
    }
}
java
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
            .route("json-grpc", r -> r.path("/json/hello").filters(f -> {
                String protoDescriptor = "file:src/main/proto/hello.pb";
                String protoFile = "file:src/main/proto/hello.proto";
                String service = "HelloService";
                String method = "hello";
                return f.jsonToGRPC(protoDescriptor, protoFile, service, method);
            }).uri("https://localhost:6565/testhello"))
            .build();
}

YAML 配置

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: json-grpc
          uri: https://localhost:6565/testhello
          predicates:
            - Path=/json/**
          filters:
            - name: JsonToGrpc
              args:
                protoDescriptor: file:proto/hello.pb
                protoFile: file:proto/hello.proto
                service: HelloService
                method: hello

工作原理

当通过网关向 /json/hello 发送请求时,处理流程如下:

  1. 接收 JSON 请求:网关接收来自客户端的 HTTP JSON 请求
  2. 协议转换:使用 hello.proto 中的定义将 JSON 转换为 gRPC 格式
  3. 服务调用:向 HelloService/hello 发送 gRPC 请求
  4. 响应转换:将 gRPC 响应转换回 JSON 格式
  5. 返回结果:将 JSON 响应返回给客户端

SSL/TLS 配置

默认情况下,JsonToGrpc 过滤器使用默认的 TrustManagerFactory 创建 NettyChannel。您可以通过创建 GrpcSslConfigurer 类型的 Bean 来自定义 TrustManager

kotlin
@Configuration
class GrpcSslConfiguration {

    @Bean
    fun grpcSslContext(): GrpcSslContext {
        // 创建自定义的 TrustManager
        val trustManager = createCustomTrustManager()
        return GrpcSslContext(trustManager)
    }

    private fun createCustomTrustManager(): TrustManager {
        // 实现自定义的信任管理器逻辑
        // 注意:在生产环境中不要使用 trustAllCerts()
        return trustAllCerts()
    }
}
java
@Configuration
public class GRPCLocalConfiguration {

    @Bean
    public GRPCSSLContext sslContext() {
        TrustManager trustManager = trustAllCerts();
        return new GRPCSSLContext(trustManager);
    }
}

在生产环境中,请确保使用适当的 SSL/TLS 配置,不要使用 `trustAllCerts()` 这样的不安全配置。

实际应用示例

场景:电商订单服务

假设我们有一个电商系统,前端需要创建订单,后端使用 gRPC 服务处理订单逻辑:

1. 定义 Proto 文件

protobuf
// order.proto
syntax = "proto3";

package order;

service OrderService {
  rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
}

message CreateOrderRequest {
  string user_id = 1;
  repeated OrderItem items = 2;
  string delivery_address = 3;
}

message OrderItem {
  string product_id = 1;
  int32 quantity = 2;
  double price = 3;
}

message CreateOrderResponse {
  string order_id = 1;
  string status = 2;
  double total_amount = 3;
}

2. 网关配置

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: grpc://localhost:9090
          predicates:
            - Path=/api/orders/**
          filters:
            - name: JsonToGrpc
              args:
                protoDescriptor: file:proto/order.pb
                protoFile: file:proto/order.proto
                service: OrderService
                method: CreateOrder
            - RewritePath=/api/orders/create, /order.OrderService/CreateOrder

3. 前端调用示例

javascript
// 前端 JavaScript 代码
const createOrder = async (orderData) => {
  const response = await fetch("/api/orders/create", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      user_id: "user123",
      items: [
        {
          product_id: "prod456",
          quantity: 2,
          price: 29.99,
        },
      ],
      delivery_address: "123 Main St, City, Country",
    }),
  });

  return await response.json();
};

注意事项与最佳实践

JsonToGrpc 过滤器主要用于单向的请求-响应模式,不支持 gRPC 的流式传输功能。

最佳实践

  1. Proto 文件管理:将 Proto 文件集中管理,确保版本一致性
  2. 错误处理:合理配置错误处理机制,确保协议转换异常能够正确返回给客户端
  3. 性能监控:监控转换过程的性能,避免成为系统瓶颈
  4. 安全配置:在生产环境中正确配置 SSL/TLS

常见问题

Details

协议转换失败的排查步骤

  1. 检查 Proto 文件路径是否正确
  2. 验证 Proto 描述符文件是否最新
  3. 确认服务名称和方法名称拼写正确
  4. 检查 JSON 数据结构是否与 Proto 定义匹配

总结

JsonToGrpc 网关过滤器工厂是 Spring Cloud Gateway 中实现协议转换的重要工具,它解决了微服务架构中 HTTP/JSON 与 gRPC 协议之间的转换问题。通过合理的配置和使用,可以让您在享受 gRPC 高性能的同时,保持前端 API 的简洁性。

在设计微服务架构时,可以考虑在网关层统一处理协议转换,这样既能保持各个服务的技术栈独立性,又能提供统一的 API 接口。