Сервер на Golang с gRPC и REST API одновременно

В этой статье описывается простой способ создания Go-сервера, который одновременно прослушивает gRPC и REST-запросы.

Самое интересное, что мы будем использовать простую схему Protocol Buffer, которая будет компилироваться для обоих типов API.

TL;DR – Вы можете посмотреть, как это делается в моем репозитории Github.

Начнем с инициализации нового проекта Go.

$ mkdir hello
$ cd hello
$ go mod init github.com/your-username/hello

Создание буферов протоколов

Установить компилятор буферов протоколов

Если у вас еще не установлен protoc, следуйте этому руководству по установке.

Затем добавьте поддержку protoc для grpc-gateway и, опционально, для open-api-v2:

$ go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway@latest
$ go install github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2@latest

Далее необходимо убедиться, что необходимые зависимости доступны компилятору во время компиляции. Это можно сделать, вручную клонировав и скопировав соответствующие файлы из репозитория googleapis в свой проект в папку ./google/api. Потребуются следующие файлы:

google/api/annotations.proto
google/api/field_behavior.proto
google/api/http.proto
google/api/httpbody.proto

Создание файла proto

Теперь создадим буферы протокола. Создадим файл ./proto/hello.proto и заполним его следующим кодом:

syntax = "proto3";
package hello;
option go_package = "github.com/your-username/hello";
import "google/api/annotations.proto";
option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
 info: {
  title: "HelloService";
  version: "1.0";
  contact: {
   name: "grpc-with-rest";
   url: "https://github.com/your-username/hello";
    };
  };
  schemes: HTTP;
  consumes: "application/json";
  produces: "application/json";
  responses: {
  key: "404";
  value: {
   description: "Returned when the resource does not exist.";
   schema: {
    json_schema: {
     type: STRING;
    }
   }
  }
 }
};
message HelloRequest {
 string name = 1;
}
message HelloResponse {
 string message = 1;
}
service HelloService {
 rpc SayHello (HelloRequest) returns (HelloResponse) {
  option(google.api.http) = {
   get: "/api/hello/{name}",
  };
 }
}

Компиляция прототипов

И, наконец, запустим в консоли скрипт protoc, который скомпилирует буферы протокола в необходимый код:

$ protoc -I ./proto \
  --go_out ./proto --go_opt paths=source_relative \
  --go-grpc_out ./proto --go-grpc_opt paths=source_relative \
  --grpc-gateway_out ./proto --grpc-gateway_opt paths=source_relative \
  --openapiv2_out ./proto --openapiv2_opt use_go_templates=true \
  ./proto/hello.proto

Это должно создать 4 новых файла в папке ./proto.

Создание сервера

Создайте файл ./server/main.go и заполните его этим кодом:

package main

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

 "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
 "google.golang.org/grpc"
 "google.golang.org/grpc/credentials/insecure"
 "google.golang.org/grpc/reflection"

 pb "github.com/jannden/golang-examples/grpc-with-rest/proto"
)

type server struct {
 pb.UnimplementedHelloServiceServer
}

func NewServer() *server {
 return &server{}
}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
 return &pb.HelloResponse{Message: "Hello, " + in.Name}, nil
}

func main() {
 lis, err := net.Listen("tcp", ":50051")
 if err != nil {
  log.Fatalln("Failed to listen:", err)
 }

 s := grpc.NewServer()
 reflection.Register(s)
 pb.RegisterHelloServiceServer(s, &server{})

 go func() {
  log.Println("Serving gRPC on 0.0.0.0:50051")
  if err := s.Serve(lis); err != nil {
   log.Fatalf("Failed to serve gRPC server: %v", err)
  }
 }()

 conn, err := grpc.DialContext(
  context.Background(),
  "0.0.0.0:50051",
  grpc.WithBlock(),
  grpc.WithTransportCredentials(insecure.NewCredentials()),
 )
 if err != nil {
  log.Fatalln("Failed to dial server:", err)
 }

 gwmux := runtime.NewServeMux()
 err = pb.RegisterHelloServiceHandler(context.Background(), gwmux, conn)
 if err != nil {
  log.Fatalln("Failed to register gateway:", err)
 }

 gwServer := &http.Server{
  Addr:    ":50052",
  Handler: gwmux,
 }

 log.Println("Serving gRPC-Gateway for REST on http://0.0.0.0:50052")
 if err := gwServer.ListenAndServe(); err != nil {
  log.Fatalf("Failed to serve gRPC-Gateway server: %v", err)
 }
}

Запуск сервера

Запустите сервер:

$ go run server/main.go

Откройте новый терминал.

Протестируйте соединение с gRPC с помощью grpcurl:

$ grpcurl -d '{"name":"John"}' -plaintext 0.0.0.0:50051 hello.HelloService/SayHello

Протестируйте соединение с REST с помощью curl:

$ curl 0.0.0.0:50052/api/hello/John

Заключение

Надеюсь, вы хорошо разобрались в создании Go-сервера с gRPC и REST 😊.

+1
0
+1
1
+1
0
+1
0
+1
1

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *