Golang实现GRPC与REST微服务开发指南

Golang实现GRPC与REST微服务开发指南 我确实看到了这篇帖子,内容有些相似,但我希望进行更广泛的讨论,并且我的问题略有不同。

因此,我希望建立一些基础架构,我的服务将主要使用 Go/gRPC,在需要时使用 REST,其他情况下则使用 gRPC。

最初的需求比较基础:gRPC、HTTP 包装器、Swagger/OpenAPI(或一些自动文档)。我也在考虑使用 Envoy,但对于使用代理进行服务注册和其他有趣的事情,我还是个新手。

我基本上已经拼凑出了一些能实现这些功能的代码,但老实说,我更愿意不重复造轮子。我真的很惊讶,周围竟然没有其他现成的解决方案,或者也许我只是错过了。

我查看过的东西:

go-kit:

  • 优点:它似乎在为扩展、日志记录和服务注册等方面做长远考虑,这都很好。
  • 缺点:在我看来,这真的过于复杂了。他们的简单 hello world 示例长达 12 页,只是为了帮我做一个非常简单的入门示例。

go-micro:

  • 优点:比 go-kit 简单。
  • 缺点:
    • 似乎需要特定的生态系统才能工作。
    • 添加一个非 Go 语言编写的服务似乎…

Lile:

当我看到这个时,我非常兴奋,但我无法让它运行起来,所以不确定是它本身有问题,还是只是我的问题。

Clayr:

另一个对我来说似乎不工作的项目。根据我的尝试,示例和代码生成似乎相当混乱。

我很希望在 Go 中看到类似 https://www.dropwizard.io/ 的东西,它是一个简单的技术栈,你可以反复使用,让你创建一个服务,并拥有日志记录、健康检查等钩子,但不需要每次你想用一个新响应返回一个新端点时都更新 7 个类。

在我的研究中,我是否遗漏了什么?是每个人都在编写自己的家庭技术栈,还是我正在尝试做的事情并不主流?


更多关于Golang实现GRPC与REST微服务开发指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang实现GRPC与REST微服务开发指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中构建同时支持gRPC和REST的微服务,确实有几个成熟的方案。以下是具体的实现示例:

方案一:gRPC-Gateway(官方推荐)

这是最标准的解决方案,通过protobuf注解自动生成REST代理。

1. 定义proto文件:

syntax = "proto3";

package example;
option go_package = "github.com/example/proto";

import "google/api/annotations.proto";

service UserService {
  rpc GetUser(GetUserRequest) returns (User) {
    option (google.api.http) = {
      get: "/v1/users/{id}"
    };
  }
  
  rpc CreateUser(CreateUserRequest) returns (User) {
    option (google.api.http) = {
      post: "/v1/users"
      body: "*"
    };
  }
}

message GetUserRequest {
  string id = 1;
}

message CreateUserRequest {
  string name = 1;
  string email = 2;
}

message User {
  string id = 1;
  string name = 2;
  string email = 3;
}

2. 生成代码:

# 安装工具
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2

# 生成代码
protoc -I . \
  --go_out=./gen \
  --go-grpc_out=./gen \
  --grpc-gateway_out=./gen \
  --openapiv2_out=./docs \
  user.proto

3. 服务实现:

package main

import (
    "context"
    "log"
    "net"
    "net/http"

    "github.com/example/proto/gen"
    "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    "google.golang.org/grpc"
)

type server struct {
    gen.UnimplementedUserServiceServer
}

func (s *server) GetUser(ctx context.Context, req *gen.GetUserRequest) (*gen.User, error) {
    return &gen.User{
        Id:    req.Id,
        Name:  "John Doe",
        Email: "john@example.com",
    }, nil
}

func (s *server) CreateUser(ctx context.Context, req *gen.CreateUserRequest) (*gen.User, error) {
    return &gen.User{
        Id:    "123",
        Name:  req.Name,
        Email: req.Email,
    }, nil
}

func main() {
    // 启动gRPC服务
    lis, _ := net.Listen("tcp", ":50051")
    grpcServer := grpc.NewServer()
    gen.RegisterUserServiceServer(grpcServer, &server{})
    go grpcServer.Serve(lis)

    // 启动REST网关
    ctx := context.Background()
    mux := runtime.NewServeMux()
    opts := []grpc.DialOption{grpc.WithInsecure()}
    
    err := gen.RegisterUserServiceHandlerFromEndpoint(ctx, mux, "localhost:50051", opts)
    if err != nil {
        log.Fatal(err)
    }

    // 提供OpenAPI文档
    fs := http.FileServer(http.Dir("./docs"))
    http.Handle("/docs/", http.StripPrefix("/docs/", fs))
    http.Handle("/", mux)

    log.Println("REST server listening on :8080")
    http.ListenAndServe(":8080", nil)
}

方案二:Connect(现代替代方案)

Connect提供更简单的API,同时支持gRPC、gRPC-Web和REST。

1. 安装:

go install github.com/bufbuild/buf/cmd/buf@latest
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
go install github.com/bufbuild/connect-go/cmd/protoc-gen-connect-go@latest

2. 服务实现:

package main

import (
    "context"
    "log"
    "net/http"

    "github.com/bufbuild/connect-go"
    "github.com/example/proto/gen/user/v1/userv1connect"
    userv1 "github.com/example/proto/gen/user/v1"
)

type UserServer struct{}

func (s *UserServer) GetUser(
    ctx context.Context,
    req *connect.Request[userv1.GetUserRequest],
) (*connect.Response[userv1.User], error) {
    user := &userv1.User{
        Id:    req.Msg.Id,
        Name:  "John Doe",
        Email: "john@example.com",
    }
    return connect.NewResponse(user), nil
}

func main() {
    userServer := &UserServer{}
    
    mux := http.NewServeMux()
    path, handler := userv1connect.NewUserServiceHandler(userServer)
    mux.Handle(path, handler)
    
    // 自动支持RESTful端点
    log.Println("Listening on :8080")
    http.ListenAndServe(":8080", mux)
}

方案三:自定义包装器(轻量级)

如果只需要基础功能,可以自己实现:

package main

import (
    "encoding/json"
    "net/http"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials/insecure"
)

type RESTHandler struct {
    grpcClient YourServiceClient
}

func NewRESTHandler() *RESTHandler {
    conn, _ := grpc.Dial("localhost:50051", 
        grpc.WithTransportCredentials(insecure.NewCredentials()))
    return &RESTHandler{
        grpcClient: NewYourServiceClient(conn),
    }
}

func (h *RESTHandler) GetUser(w http.ResponseWriter, r *http.Request) {
    id := r.URL.Query().Get("id")
    
    // 调用gRPC
    resp, err := h.grpcClient.GetUser(r.Context(), &GetUserRequest{Id: id})
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    
    json.NewEncoder(w).Encode(resp)
}

func main() {
    handler := NewRESTHandler()
    
    http.HandleFunc("/users", handler.GetUser)
    http.ListenAndServe(":8080", nil)
}

健康检查和指标

集成prometheus指标和健康检查:

package main

import (
    "net/http"

    "github.com/prometheus/client_golang/prometheus/promhttp"
    "google.golang.org/grpc/health"
    "google.golang.org/grpc/health/grpc_health_v1"
)

func setupMonitoring() {
    // Prometheus指标
    http.Handle("/metrics", promhttp.Handler())
    
    // 健康检查
    healthServer := health.NewServer()
    healthServer.SetServingStatus("", grpc_health_v1.HealthCheckResponse_SERVING)
    
    // 就绪检查端点
    http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("OK"))
    })
}

这些方案都提供了生产就绪的gRPC+REST实现。gRPC-Gateway是最完整的解决方案,Connect提供了更现代的API,自定义包装器则适合简单场景。

回到顶部