Skip to content

gRPC

Create gRPC server with rk-boot and rk-grpc plugins.

Overview#

We will use rk-boot start gRPC microservice and add /v1/hello API into it.

Furthermore, we will enable bellow functionalities.

Functionality Description
Swagger UI Enable Swagger UI
API Docs UI Enable RapiDoc UI
Prometheus Client Enable Prometheus Client
Logging middleware Automatically record logs for every API calls
Prometheus middleware Automatically record prometheus metrics for every API calls
Meta middleware Automatically add requestID for every API response

In order to compile protocol buffer, we need to install a couple of CLI.

User can use rk to install them quickly.

# Install RK CLI
$ go get -u github.com/rookie-ninja/rk/cmd/rk

# List available installation
$ rk install
COMMANDS:
    buf                      install buf on local machine
    cfssl                    install cfssl on local machine
    cfssljson                install cfssljson on local machine
    gocov                    install gocov on local machine
    golangci-lint            install golangci-lint on local machine
    mockgen                  install mockgen on local machine
    pkger                    install pkger on local machine
    protobuf                 install protobuf on local machine
    protoc-gen-doc           install protoc-gen-doc on local machine
    protoc-gen-go            install protoc-gen-go on local machine
    protoc-gen-go-grpc       install protoc-gen-go-grpc on local machne
    protoc-gen-grpc-gateway  install protoc-gen-grpc-gateway on local machine
    protoc-gen-openapiv2     install protoc-gen-openapiv2 on local machine
    swag                     install swag on local machine
    rk-std                   install rk standard environment on local machine
    help, h                  Shows a list of commands or help for one command

# Install buf, protoc-gen-go, protoc-gen-go-grpc, protoc-gen-grpc-gateway, protoc-gen-openapiv2
$ rk install protoc-gen-go
$ rk install protoc-gen-go-grpc
$ rk install protoc-gen-go-grpc-gateway
$ rk install protoc-gen-openapiv2
$ rk install buf
CLI Description 安装
buf Compile protocol buffer API Install
protoc-gen-go Generate .go files Install
protoc-gen-go-grpc Generate gRPC related .go files Install
protoc-gen-grpc-gateway Generate grpc-gateway related .go files Install
protoc-gen-openapiv2 Generate swagger UI related .json files Install

Install#

go get github.com/rookie-ninja/rk-boot/v2
go get github.com/rookie-ninja/rk-grpc/v2

1. Create greeter.proto#

Create greeter.proto file at api/v1 folder.

syntax = "proto3";

package api.v1;

option go_package = "api/v1/greeter";

service Greeter {
  rpc Hello (HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {}

message HelloResponse {
  string message = 1;
}

2. Copy googleapis files#

rk-grpc plugin use some data structures in googleapi. We recommend putting those proto files in project folder.

Copy files from github/googleapi, or copy from rk-boot.

Please copy files into third-party folder.

third-party
    └── googleapis
        └── google
            ├── api
               ├── annotations.proto
               ├── http.proto
               └── httpbody.proto
            └── rpc
                ├── code.proto
                ├── error_details.proto
                └── status.proto

buf.yaml

version: v1beta1
name: github.com/rk-dev/rk-boot
build:
  roots:
    - api
    - third-party/googleapis

buf.gen.yaml

version: v1beta1
plugins:
  - name: go
    out: api/gen
    opt:
      - paths=source_relative
  - name: go-grpc
    out: api/gen
    opt:
      - paths=source_relative
      - require_unimplemented_servers=false
  - name: grpc-gateway
    out: api/gen
    opt:
      - paths=source_relative
      - grpc_api_configuration=api/v1/gw_mapping.yaml
      - allow_repeated_fields_in_body=true
      - generate_unbound_methods=true
  - name: openapiv2
    out: api/gen
    opt:
      - grpc_api_configuration=api/v1/gw_mapping.yaml
      - allow_repeated_fields_in_body=true

api/v1/gw_mapping.yaml

type: google.api.Service
config_version: 3

# Please refer google.api.Http in third-party/googleapis/google/api/http.proto file for details.
http:
  rules:
    - selector: api.v1.Greeter.Hello
      get: /v1/hello

Run buf command

$ buf generate --path api/v1
# By configuration in buf.gen.yaml, generated files would be write to api/gen folder
$ tree
.
├── api
│   ├── gen
│      ├── google
│         ...
│      └── v1
│          ├── greeter.pb.go
│          ├── greeter.pb.gw.go
│          ├── greeter.swagger.json
│          └── greeter_grpc.pb.go
│   └── v1
│       ├── greeter.proto
│       └── gw_mapping.yaml
├── boot.yaml
├── buf.gen.yaml
├── buf.yaml
├── go.mod
├── go.sum
├── main.go
└── third-party
    └── googleapis
        ...

4. Create boot.yaml#

grpc:
  - name: greeter
    port: 8080
#   gwPort: 8081                  # Optional, default: gateway port will be the same as grpc port if not provided
    enabled: true  
    enableReflection: true        # Enable gRPC reflection mainly for grpcurl
    enableRkGwOption: true        # Enable RK style grpc-gateway options
    sw:
      enabled: true               # Enable Swagger UI,default path: /sw
    docs:
      enabled: true               # Enable API Doc UI,default path: /docs
    prom:
      enabled: true               # Enable Prometheus Client,default path: /metrics
    middleware:
      logging:
        enabled: true
      prom:
        enabled: true
      meta:
        enabled: true

5. Create main.go#

// Copyright (c) 2021 rookie-ninja
//
// Use of this source code is governed by an Apache-style
// license that can be found in the LICENSE file.

package main

import (
    "context"
    "github.com/rookie-ninja/rk-boot/v2"
    "github.com/rookie-ninja/rk-demo/api/gen/v1"
    "github.com/rookie-ninja/rk-grpc/v2/boot"
    "google.golang.org/grpc"
)

func main() {
    boot := rkboot.NewBoot()

    // 注册 RPC
    entry := rkgrpc.GetGrpcEntry("greeter")
    entry.AddRegFuncGrpc(registerGreeter)
    entry.AddRegFuncGw(greeter.RegisterGreeterHandlerFromEndpoint)

    // 启动
    boot.Bootstrap(context.TODO())

    // 等待关闭信号
    boot.WaitForShutdownSig(context.TODO())
}

func registerGreeter(server *grpc.Server) {
    greeter.RegisterGreeterServer(server, &GreeterServer{})
}

type GreeterServer struct{}

func (server *GreeterServer) Hello(_ context.Context, _ *greeter.HelloRequest) (*greeter.HelloResponse, error) {
    return &greeter.HelloResponse{
        Message: "Hello!",
    }, nil
}

6. Run main.go#

$ go run main.go 
2022-04-14T16:25:23.538+0800    INFO    boot/grpc_entry.go:960  Bootstrap grpcEntry     {"eventId": "f09d0b94-b491-4148-8438-3e65610fbdde", "entryName": "greeter", "entryType": "gRPCEntry"}
2022-04-14T16:25:23.542+0800    INFO    boot/grpc_entry.go:681  SwaggerEntry: http://localhost:8080/sw/
2022-04-14T16:25:23.542+0800    INFO    boot/grpc_entry.go:684  DocsEntry: http://localhost:8080/docs/
2022-04-14T16:25:23.542+0800    INFO    boot/grpc_entry.go:687  PromEntry: http://localhost:8080/metrics
------------------------------------------------------------------------
endTime=2022-04-14T16:25:23.54236+08:00
startTime=2022-04-14T16:25:23.537974+08:00
elapsedNano=4385739
timezone=CST
ids={"eventId":"f09d0b94-b491-4148-8438-3e65610fbdde"}
app={"appName":"rk","appVersion":"local","entryName":"greeter","entryType":"gRPCEntry"}
env={"arch":"amd64","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin"}
payloads={"docsEnabled":true,"docsPath":"/docs/","grpcPort":8080,"gwPort":8080,"promEnabled":true,"promPath":"/metrics","promPort":8080,"swEnabled":true,"swPath":"/sw/"}
counters={}
pairs={}
timing={}
remoteAddr=localhost
operation=Bootstrap
resCode=OK
eventStatus=Ended
EOE

7. Validate#

7.1 Swagger UI#

http://localhost:8080/sw/

7.2 API Docs UI#

http://localhost:8080/docs/

7.3 Prometheus Client#

http://localhost:8080/metrics

7.4 Send request#

Restful API

$ curl -vs localhost:8080/v1/hello               
...
< X-Request-Id: b047072d-a433-4b98-b2ff-3ba54bbb0243
< X-Rk-App-Domain: *
< X-Rk-App-Name: rk
< X-Rk-App-Unix-Time: 2022-04-14T16:35:28.387937+08:00
< X-Rk-App-Version: local
< X-Rk-Received-Time: 2022-04-14T16:35:28.387937+08:00
...
{"message":"Hello!"}

gRPC

$ grpcurl -v -plaintext localhost:8080 api.v1.Greeter.Hello

Resolved method descriptor:
rpc Hello ( .api.v1.HelloRequest ) returns ( .api.v1.HelloResponse );

Request metadata to send:
(empty)

Response headers received:
content-type: application/grpc
x-request-id: c783eeaa-2a77-44ec-bb9c-9cbf19a58ee6
x-rk-app-domain: *
x-rk-app-name: rk
x-rk-app-unix-time: 2022-04-14T16:36:59.258112+08:00
x-rk-app-version: local
x-rk-received-time: 2022-04-14T16:36:59.258112+08:00

Response contents:
{
  "message": "Hello!"
}
...

7.5 API logs#

By default, rk-boot will use bellow format of logs. JSON is also supported, please visit user-guide for details.

------------------------------------------------------------------------
endTime=2022-04-14T16:35:28.387969+08:00
startTime=2022-04-14T16:35:28.387928+08:00
elapsedNano=41020
timezone=CST
ids={"eventId":"b047072d-a433-4b98-b2ff-3ba54bbb0243","requestId":"b047072d-a433-4b98-b2ff-3ba54bbb0243"}
app={"appName":"rk","appVersion":"local","entryName":"greeter","entryType":"gRPCEntry"}
env={"arch":"amd64","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin"}
payloads={"apiMethod":"","apiPath":"/api.v1.Greeter/Hello","apiProtocol":"","apiQuery":"","grpcMethod":"Hello","grpcService":"api.v1.Greeter","grpcType":"UnaryServer","gwMethod":"GET","gwPath":"/v1/hello","gwScheme":"http","gwUserAgent":"curl/7.64.1","userAgent":""}
counters={}
pairs={}
timing={}
remoteAddr=127.0.0.1:56369
operation=/api.v1.Greeter/Hello
resCode=OK
eventStatus=Ended
EOE

7.6 Prometheus Metrics#

http://localhost:8080/metrics

Cheers#