使用Connect-Go+Docker来建立服务器开发环境
这篇文章是 Cocone Advent Calendar 2023 中的第9篇。
首先
最近由于各种情况,开始使用connect-go。
这次我们想要简化使用方法,并使用Docker进行protocol buffer编译,以便能够立即开始开发。为此,我们总结了所需的步骤。
※假设您可以使用golang和Docker来进行操作。
关于connect-go
这是一个能够以gRPC互换方式、更轻巧更智能地使用的HTTP API库。本次我们使用golang,但它也在不同的环境中进行了兼容,如web、kotlin、swift、node等等。它支持Unary RPC、Client Streaming RPC、Server Streaming RPC和Bidirectional Streaming RPC,虽然本次不详细介绍,但也可以使用各种拦截器。
即使系统已经在gRPC上构建,也相对容易地进行切换。如果在“计划在应用上发布,但也考虑在Web上发布”的情况下,由于可以直接在一份代码中进行适配,跨平台已经成为当今的事实标准,非常有吸引力。
还有一点要提及的是,最近也有关于客户端(Unity)支持HTTP/2的讨论,所以在这方面也有考虑切换的余地。
目录结构
.
├─ protos
| └─ echo.proto
├─ server
│ └─ main.go
├─ buf.gen.yaml
├─ docker-compose.yaml
├─ Dockerfile.proto
└─ generate-proto.sh
原型
syntax = "proto3";
package pb;
option go_package = ".;pb";
service EchoService {
rpc Echo(EchoRequest) returns (EchoResponse) {}
}
message EchoRequest { string msg = 1; }
message EchoResponse { string msg = 1; }
准备构建容器用于proto。
接下来,我们将建立一个能够构建proto的环境。
docker-compose.yaml 文件
为了方便地管理这次的组合,我使用了docker-compose。它所做的事情很简单,只是挂载所需的文件并启动构建脚本。
version: "3.7"
services:
proto_builder_connect_go:
build:
context: ./
dockerfile: ./Dockerfile.proto
volumes:
- ./protos:/builder/protos
- ./server/gen/pb:/builder/gen
command: bash generate-proto.sh
Dockerfile
下面是Dockerfile。安装了连接go所需的各种模块。
FROM golang:1.20.10-bullseye
WORKDIR /builder
RUN apt update && apt install -y unzip rsync clang-format vim
COPY ./generate-proto.sh ./
COPY ./buf.gen.yaml ./
RUN go install github.com/bufbuild/buf/cmd/buf@latest
RUN go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
RUN go install github.com/bufbuild/connect-go/cmd/protoc-gen-connect-go@latest
ENV PATH $PATH:/root/.local/bin/:/go/bin/
buf.gen.yaml
这是构建配置。如果您可以参考链接获得更多详细信息,但是样本将输出到 server/gen/pb 作为产物。
version: v1
managed:
enabled: true
go_package_prefix:
default: server/gen/pb
plugins:
- name: go
out: gen
opt: paths=source_relative
- name: connect-go
out: gen
opt: paths=source_relative
生成-原型.sh
最后是构建脚本。通过调用 buf generate,我们可以从创建的 proto 文件中生成可用于 golang 的代码。
#!/bin/bash
set -e -o pipefail
PROTO_DIR='/builder/protos'
OUT_TMP='/builder/gen'
buf generate --template buf.gen.yaml protos
exit 0
执行
docker-compose -f docker-compose.yaml up
当执行完成后,将在 server/gen/pb 目录下生成以下文件。现在已准备就绪,我们将试试在实际的Golang中使用。
-
- echo.pb.go
- pbconnect/echo.pbconnect.go
准备golang方面的工作
# モジュール初期化
cd server
go mod init server
# 必要なパッケージを取得
go get google.golang.org/protobuf
go get github.com/bufbuild/connect-go
go get github.com/bufbuild/connect-grpcreflect-go
go get golang.org/x/net
# メインファイル作成
touch main.go
这是服务器的实施。
package main
import (
"context"
"fmt"
"log"
"net/http"
"server/gen/pb"
"server/gen/pb/pbconnect"
"github.com/bufbuild/connect-go"
grpcreflect "github.com/bufbuild/connect-grpcreflect-go"
"golang.org/x/net/http2"
"golang.org/x/net/http2/h2c"
)
func main() {
log.Println("setup server")
// EchoServiceの作成
echoService := EchoService{}
// サービス登録
mux := http.NewServeMux()
handle := mux.Handle
handle(pbconnect.NewEchoServiceHandler(echoService))
// テスト用にリフレクションサービスを利用
reflector := grpcreflect.NewStaticReflector(
pbconnect.EchoServiceName,
)
handle(grpcreflect.NewHandlerV1(reflector))
// サーバー起動
log.Println("start server")
if err := http.ListenAndServe(
":8090",
h2c.NewHandler(mux, &http2.Server{}),
); err != nil {
log.Fatal(err)
}
}
// EchoService: EchoServiceの実装
type EchoService struct{}
// Echo: 送られてきたメッセージをそのまま返す
func (s EchoService) Echo(ctx context.Context, req *connect.Request[pb.EchoRequest]) (*connect.Response[pb.EchoResponse], error) {
log.Println("EchoService.Echo:", req.Msg.Message)
return connect.NewResponse(&pb.EchoResponse{
Message: fmt.Sprintf("%s from server", req.Msg.Message),
}), nil
}
实际上尝试调用一下
启动服务器,并尝试使用grpcurl或curl访问。
cd server
go run main.go
>2023/12/06 09:42:44 setup server
>2023/12/06 09:42:44 start server
# curl
curl --header "Content-Type: application/json" --data '{"message": "hello"}' http://localhost:8080/pb.EchoService/Echo
>{"message":"hello from server"}
# grpccurl
grpcurl -plaintext -d '{"message": "hello"}' localhost:8080 pb.EchoService/Echo
>{
> "message": "hello from server"
>}
最终
在Unity的gRPC支持问题出现时,Cysharp发表了可以在Unity中使用HTTP/2的YetAnotherHttpHandler,并出现了将客户端从gRPC切换到HTTP/2的场景。
在这种情况下,提前创造一个更顺利的情况是非常重要的,我认为这种积累将导致更好的开发环境和更好的服务,所以如果您目前使用gRPC,不妨考虑转换为这个机会。
在这种情况下,提前创造一个更顺利的情况是非常重要的,我认为这种积累将导致更好的开发环境和更好的服务,因此,如果您目前使用gRPC,不妨考虑这个机会进行切换。