搭建grpc环境

前情提要

我这个demo是在mac(intel)环境实现的,windows和linux大同小异,可自行在网上搜索。

环境依赖

  • go
  • buf cli

go

1
2
3
4
brew install go

go version
go version go1.17 darwin/amd64

buf

buf其实是一种protobuf编译器,相比入传统的protoc命令行方式,buf以配置文件的方式更加方便明了。buf又有以下几个优势:

1
2
3
4
5
6
BIN="/usr/local/bin" && \
VERSION="1.4.0" && \
curl -sSL \
"https://github.com/bufbuild/buf/releases/download/v${VERSION}/buf-$(uname -s)-$(uname -m)" \
-o "${BIN}/buf" && \
chmod +x "${BIN}/buf"

官网参考

开始

  1. 在你的工作目录创建一个文件夹用于存放项目
1
mkdri example_grpc
  1. 创建以下目录结构
1
2
3
4
example_grpc
- api
- cmd
- pkg
  1. 初始化项目
1
2
go mod init example_grpc
go mod tidy
  1. 在api/下创建api.proto文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
syntax = "proto3";
package api;

option go_package = "/api";

service Example {
rpc Ping(PingRequest) returns (PingResponse) {}
}

message PingRequest {}

message PingResponse {
int32 Code = 10;
string Msg = 20;
}
  1. 在项目根目录下创建以下buf配置文件:
  • buf.yaml

    配置依赖

1
2
3
4
5
6
version: v1beta1
name: buf.build/adjutant/grpc_example
deps:
# it will download this dep if you run buf generate
- buf.build/beta/googleapis
- buf.build/grpc-ecosystem/grpc-gateway
  • buf.gen.yaml

    指定插件和生成的文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
version: v1beta1
plugins:
- name: go
out: .
opt:
- paths=source_relative
- name: go-grpc
out: .
opt:
- paths=source_relative
- require_unimplemented_servers=false
- name: grpc-gateway
out: .
opt:
- paths=source_relative
- grpc_api_configuration=gw_mapping.yaml
- name: openapiv2
out: .
opt:
- grpc_api_configuration=gw_mapping.yaml
  • gw.mapping.yaml

    grpc方法和http接口路由配置

1
2
3
4
5
6
7
8
type: google.api.Service
config_version: 3

# Please refer google.api.Http in https://github.com/googleapis/googleapis/blob/master/google/api/http.proto file for details.
http:
rules:
- selector: api.Example.Ping
get: /ping

安装go插件

1
2
3
4
5
go install \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway \
github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2 \
google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpc

编译proto文件

1
2
buf mod update
buf generate

执行上面两条命令之后,得到以下文件

1
2
3
4
5
- api/
- api.pb.go
- api.gw.pb.go
- api.pb.go
- api.swagger.json

注册服务

注册

在cmd/下创建main.go文件,内容为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package main

import (
"context"
"example_grpc/api"
"example_grpc/pkg/handler"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"log"
"net"
"net/http"
)

const (
grpcServerEndpoint = "127.0.0.1:9091"
)

func main() {
go toGRPC()
go toHTTP()

select {}
}

func toGRPC(){
s := grpc.NewServer()

api.RegisterExampleServer(s, &handler.Handler{})

lis, err := net.Listen("tcp", grpcServerEndpoint)
if err != nil {
log.Panicf("net.Listen err: %+v", err)
}

if err := s.Serve(lis); err != nil {
log.Panicf("s.Server err: %+v", err)
}
}

func toHTTP(){
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()

// Register gRPC server endpoint
// Note: Make sure the gRPC server is running properly and accessible
mux := runtime.NewServeMux()
opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())}
err := api.RegisterExampleHandlerFromEndpoint(ctx, mux, grpcServerEndpoint, opts)
if err != nil {
log.Panicf("RegisterLabHandlerFromEndpoint err: %+v", err)
}

// Start HTTP server (and proxy calls to gRPC server endpoint)
if err := http.ListenAndServe(":8082", mux); err != nil {
log.Panicf("ListenAndServe err: %+v", err)
}
}

在pkg/api/下创建ping.go文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package handler

import (
"context"
"example_grpc/api"
)

type Handler struct {}

func (Handler) Ping(ctx context.Context, request *api.PingRequest) (*api.PingResponse, error) {
resp := &api.PingResponse{
Code: 0,
Msg: "ok",
}

return resp, nil
}

测试

1
2
curl --location --request GET 'http://127.0.0.1:8082/ping'
{"Code":0,"Msg":"ok"}