Scott

kratos api定义http grpc双协议 4 months ago

go
微服务
5753个字符
共有37人围观

根据 Kratos 文档的说明,通过 Protobuf IDL(Interface Definition Language)可以同时定义 REST API 和 gRPC API。以下是一个全面的例子,展示如何使用 Protobuf 定义一个服务,包括 gRPC 方法和对应的 REST API 映射,并提供生成代码的步骤。这个例子基于一个简单的“用户管理”服务。


1. Protobuf 文件定义

我们将创建一个 user.proto 文件,定义一个用户管理服务,支持获取用户信息和创建用户。文件内容如下:

syntax = "proto3";

package user.v1;

import "google/api/annotations.proto";

// 指定 Go 包路径
option go_package = "github.com/example/user-service/api/user/v1;v1";
// Java 相关选项(可选)
option java_multiple_files = true;
option java_package = "com.example.user.v1";
option java_outer_classname = "UserProtoV1";

// 定义 UserService 服务
service UserService {
  // 获取用户信息 (GetUser)
  rpc GetUser (GetUserRequest) returns (GetUserResponse) {
    option (google.api.http) = {
      get: "/v1/users/{id}"  // REST API 的 GET 方法
    };
  }

  // 创建用户 (CreateUser)
  rpc CreateUser (CreateUserRequest) returns (CreateUserResponse) {
    option (google.api.http) = {
      post: "/v1/users"      // REST API 的 POST 方法
      body: "*"              // 请求体映射到整个 CreateUserRequest
    };
  }
}

// 获取用户请求消息
message GetUserRequest {
  string id = 1;  // 用户 ID
}

// 获取用户响应消息
message GetUserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

// 创建用户请求消息
message CreateUserRequest {
  string name = 1;
  string email = 2;
}

// 创建用户响应消息
message CreateUserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

说明:

  1. syntax = "proto3": 使用 Protocol Buffers 的第 3 版语法。
  2. package user.v1: 定义命名空间,避免冲突,通常包含服务名和版本。
  3. import "google/api/annotations.proto": 引入 Google 的 HTTP 注解,用于映射 REST API。
  4. option go_package: 指定生成的 Go 代码的包路径。
  5. service UserService: 定义服务,包含两个方法:
    • GetUser: 获取用户信息,支持 gRPC 和 REST(GET 方法)。
    • CreateUser: 创建用户,支持 gRPC 和 REST(POST 方法)。
  6. google.api.http: 为每个方法指定 REST API 的 HTTP 方法和路径。
    • get: "/v1/users/{id}": 将 GetUser 映射为 HTTP GET 请求,id 是路径参数。
    • post: "/v1/users": 将 CreateUser 映射为 HTTP POST 请求,请求体映射到整个 CreateUserRequest

2. 生成代码

假设上述文件保存为 api/user/v1/user.proto,我们需要使用 protoc 工具生成 Go 代码。以下是命令:

安装必要的工具

确保已安装以下工具: - protoc: Protocol Buffers 编译器。 - protoc-gen-go: Go 语言插件。 - protoc-gen-go-http: Kratos 的 HTTP 插件,用于生成 REST API 代码。 - protoc-gen-go-grpc: gRPC 插件。

安装命令(以 Go 为例):

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install github.com/go-kratos/kratos/cmd/protoc-gen-go-http@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

生成代码

运行以下命令生成代码:

protoc --proto_path=. \
       --go_out=. \
       --go-grpc_out=. \
       --go-http_out=. \
       api/user/v1/user.proto

生成的文件

执行后,会在 api/user/v1/ 目录下生成以下文件: - user.pb.go: 包含消息的结构体定义和序列化代码。 - user_grpc.pb.go: 包含 gRPC 的服务接口和客户端存根。 - user_http.pb.go: 包含 REST API 的 HTTP 处理函数。


3. 服务端实现

以下是一个简单的服务端实现,使用 Go 和 Kratos 框架。

项目结构

user-service/
├── api/
│   └── user/
│       └── v1/
│           ├── user.proto
│           ├── user.pb.go
│           ├── user_grpc.pb.go
│           └── user_http.pb.go
├── internal/
│   └── service/
│       └── user.go
├── cmd/
│   └── main.go
└── go.mod

服务实现 (internal/service/user.go)

package service

import (
    pb "github.com/example/user-service/api/user/v1"
    "context"
)

type UserService struct {
    pb.UnimplementedUserServiceServer
}

func NewUserService() *UserService {
    return &UserService{}
}

func (s *UserService) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.GetUserResponse, error) {
    // 模拟获取用户信息
    return &pb.GetUserResponse{
        Id:    req.Id,
        Name:  "John Doe",
        Email: "john@example.com",
    }, nil
}

func (s *UserService) CreateUser(ctx context.Context, req *pb.CreateUserRequest) (*pb.CreateUserResponse, error) {
    // 模拟创建用户
    return &pb.CreateUserResponse{
        Id:    "123",
        Name:  req.Name,
        Email: req.Email,
    }, nil
}

主程序 (cmd/main.go)

package main

import (
    "flag"
    "log"
    "github.com/go-kratos/kratos/v2"
    "github.com/go-kratos/kratos/v2/transport/grpc"
    "github.com/go-kratos/kratos/v2/transport/http"
    pb "github.com/example/user-service/api/user/v1"
    "github.com/example/user-service/internal/service"
)

func main() {
    flag.Parse()

    // 创建 HTTP 服务
    httpSrv := http.NewServer(http.Address(":8000"))
    // 创建 gRPC 服务
    grpcSrv := grpc.NewServer(grpc.Address(":9000"))

    // 初始化服务
    userService := service.NewUserService()

    // 注册 HTTP 和 gRPC 服务
    pb.RegisterUserServiceHTTPServer(httpSrv, userService)
    pb.RegisterUserServiceServer(grpcSrv, userService)

    // 创建 Kratos 应用
    app := kratos.New(
        kratos.Name("user-service"),
        kratos.Server(
            httpSrv,
            grpcSrv,
        ),
    )

    // 启动应用
    if err := app.Run(); err != nil {
        log.Fatal(err)
    }
}

4. 测试服务

测试 gRPC

使用 gRPC 客户端(如 grpcurl)测试:

# 获取用户信息
grpcurl -plaintext -d '{"id": "123"}' localhost:9000 user.v1.UserService/GetUser

# 创建用户
grpcurl -plaintext -d '{"name": "Alice", "email": "alice@example.com"}' localhost:9000 user.v1.UserService/CreateUser

测试 REST API

使用 curl 测试 REST 接口:

# 获取用户信息 (GET)
curl http://localhost:8000/v1/users/123

# 创建用户 (POST)
curl -X POST http://localhost:8000/v1/users -d '{"name": "Alice", "email": "alice@example.com"}'

5. 总结

这个例子展示了如何通过 Protobuf IDL 定义一个同时支持 gRPC 和 REST API 的服务:

  1. 定义阶段: 在 .proto 文件中定义服务和消息,并使用 google.api.http 注解映射 REST 路径。
  2. 生成阶段: 使用 protoc 生成 Go 代码,包括 gRPC 和 HTTP 的实现。
  3. 实现阶段: 在 Go 中实现服务逻辑,并使用 Kratos 启动 HTTP 和 gRPC 服务。
  4. 测试阶段: 使用工具验证 gRPC 和 REST 接口的功能。

这种方式利用了 Protobuf 的强类型和跨语言优势,同时结合 Kratos 框架支持 REST 和 gRPC 的双协议特性,非常适合微服务架构。