今天我们来做一个有趣的 Go 实践。使用同一套代码,在一个进程中,同时启动 7种不同的 Go Web 框架。
为什么做这么无聊的事儿?
主要目的就是介绍 rookie-ninja/rk-boot 库。
启动哪些 Go Web 框架?
我们同时启动如下几个 Go Web 框架。
Web 框架 | rk-boot 依赖 | 版本 |
---|---|---|
gin-gonic/gin | go get github.com/rookie-ninja/rk-boot/gin | v1.2.14 (Stable) |
gRPC | go get github.com/rookie-ninja/rk-boot/grpc | v1.2.18 (Stable) |
labstack/echo | go get github.com/rookie-ninja/rk-boot/echo | v0.0.8 (Stable) |
gogf/gf | go get github.com/rookie-ninja/rk-boot/gf | v0.0.6 (Stable) |
gofiber/fiber | go get github.com/rookie-ninja/rk-boot/fiber | v0.0.4 (Testing) |
zeromicro/go-zero | go get github.com/rookie-ninja/rk-boot/zero | v0.0.2 (Testing) |
gorilla/mux | go get github.com/rookie-ninja/rk-boot/mux | v0.0.2 (Testing) |
快速开始
1.安装
我们通过 go get 安装如下依赖。
代码语言:txt复制go get github.com/rookie-ninja/rk-boot/grpc
go get github.com/rookie-ninja/rk-boot/gin
go get github.com/rookie-ninja/rk-boot/echo
go get github.com/rookie-ninja/rk-boot/gf
go get github.com/rookie-ninja/rk-boot/fiber
go get github.com/rookie-ninja/rk-boot/zero
go get github.com/rookie-ninja/rk-boot/mux
2.创建 boot.yaml
Web 框架 | 端口 |
---|---|
gRPC | 8081 |
gin-gonic/gin | 8082 |
labstack/echo | 8083 |
gogf/gf | 8084 |
gofiber/fiber | 8085 |
zeromicro/go-zero | 8086 |
gorilla/mux | 8087 |
除了指定端口,我们还开启了如下两个选项:
- commonService: 提供 /rk/v1/healthy 这类通用 API。
- loggingZap: RPC 日志
---
grpc:
- name: grpc
port: 8081
enabled: true
commonService:
enabled: true # Optional, default: false
interceptors:
loggingZap:
enabled: true # Optional, default: false
gin:
- name: gin
port: 8082
enabled: true
commonService:
enabled: true # Optional, default: false
interceptors:
loggingZap:
enabled: true # Optional, default: false
echo:
- name: echo
port: 8083
enabled: true
commonService:
enabled: true # Optional, default: false
interceptors:
loggingZap:
enabled: true # Optional, default: false
gf:
- name: gf
port: 8084
enabled: true
commonService:
enabled: true # Optional, default: false
interceptors:
loggingZap:
enabled: true # Optional, default: false
fiber:
- name: fiber
port: 8085
enabled: true
commonService:
enabled: true # Optional, default: false
interceptors:
loggingZap:
enabled: true # Optional, default: false
zero:
- name: zero
port: 8086
enabled: true
commonService:
enabled: true # Optional, default: false
interceptors:
loggingZap:
enabled: true # Optional, default: false
mux:
- name: mux
port: 8087
enabled: true
commonService:
enabled: true # Optional, default: false
interceptors:
loggingZap:
enabled: true # Optional, default: false
3.创建 main.go
所有 Web 框架都是用 handleRequest() 函数来处理请求。
代码语言:txt复制gRPC 例外,我们没有使用 protocol buffer,因为 PB 的协议不同。我们使用 grpc-gateway 来模拟。
grpcEntry 默认会开启 grpc-gateway,通过 grpcEntry.HttpMux 往 grpc-gateway 里注册 API。
// 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"
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gofiber/adaptor/v2"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/labstack/echo/v4"
"github.com/rookie-ninja/rk-boot"
"github.com/rookie-ninja/rk-boot/echo"
"github.com/rookie-ninja/rk-boot/fiber"
"github.com/rookie-ninja/rk-boot/gf"
"github.com/rookie-ninja/rk-boot/gin"
"github.com/rookie-ninja/rk-boot/grpc"
"github.com/rookie-ninja/rk-boot/mux"
"github.com/rookie-ninja/rk-boot/zero"
"github.com/tal-tech/go-zero/rest"
"net/http"
)
const handlePath = "/v1/hello"
// Application entrance.
func main() {
// Create a new boot instance.
boot := rkboot.NewBoot()
// 6: go-zero @8086, must adds route before bootstrap
rkbootzero.GetZeroEntry("zero").Server.AddRoute(rest.Route{
Method: http.MethodGet,
Path: handlePath,
Handler: handleRequest,
})
// Bootstrap
boot.Bootstrap(context.Background())
// 1: grpc-gateway @8081
rkbootgrpc.GetGrpcEntry("grpc").HttpMux.HandleFunc(handlePath, handleRequest)
// 2: gin @8082
rkbootgin.GetGinEntry("gin").Router.Handle(http.MethodGet, handlePath, gin.WrapF(handleRequest))
// 3: echo @8083
rkbootecho.GetEchoEntry("echo").Echo.GET(handlePath, echo.WrapHandler(http.HandlerFunc(handleRequest)))
// 4: GoFrame @8084
rkbootgf.GetGfEntry("gf").Server.BindHandler(handlePath, ghttp.WrapF(handleRequest))
// 5: Fiber @8085, must call RefreshFiberRoutes()
rkbootfiber.GetFiberEntry("fiber").App.Get(handlePath, adaptor.HTTPHandler(http.HandlerFunc(handleRequest)))
rkbootfiber.GetFiberEntry("fiber").RefreshFiberRoutes()
// 7: mux @8087
rkbootmux.GetMuxEntry("mux").Router.HandleFunc(handlePath, handleRequest)
// Wait for shutdown sig
boot.WaitForShutdownSig(context.Background())
}
// Handle request for all web frameworks
func handleRequest(writer http.ResponseWriter, req *http.Request) {
// 1: get query
name := req.URL.Query().Get("name")
// 2: marshal response
bytes, _ := json.Marshal(&Response{
Message: fmt.Sprintf("Hello %s", name),
})
// 3: write response
writer.WriteHeader(http.StatusOK)
writer.Write(bytes)
}
type Response struct {
Message string `json:"message"`
}
4.文件夹结构 & go.mod
- 文件夹结构
.
├── boot.yaml
├── go.mod
├── go.sum
└── main.go
0 directories, 4 files
- go.mod 文件
module github.com/rookie-ninja/rk-demo
go 1.16
require (
github.com/gin-gonic/gin v1.7.7
github.com/gofiber/adaptor/v2 v2.1.15
github.com/gogf/gf/v2 v2.0.0-beta
github.com/labstack/echo/v4 v4.6.1
github.com/rookie-ninja/rk-boot v1.4.0
github.com/rookie-ninja/rk-boot/echo v0.0.8
github.com/rookie-ninja/rk-boot/fiber v0.0.4
github.com/rookie-ninja/rk-boot/gf v0.0.6
github.com/rookie-ninja/rk-boot/gin v1.2.14
github.com/rookie-ninja/rk-boot/grpc v1.2.18
github.com/rookie-ninja/rk-boot/mux v0.0.2
github.com/rookie-ninja/rk-boot/zero v0.0.2
github.com/tal-tech/go-zero v1.2.4
)
5.启动 main.go
代码语言:txt复制$ go run main.go
2022-01-03T19:15:14.016 0800 INFO boot/gf_entry.go:1050 Bootstrap gfEntry {"eventId": "264033ff-be9c-4147-871c-e4873ea1510d", "entryName": "gf"}
------------------------------------------------------------------------
endTime=2022-01-03T19:15:14.016473 08:00
startTime=2022-01-03T19:15:14.016057 08:00
elapsedNano=416178
timezone=CST
ids={"eventId":"264033ff-be9c-4147-871c-e4873ea1510d"}
app={"appName":"rk","appVersion":"","entryName":"gf","entryType":"GfEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"commonServiceEnabled":true,"commonServicePathPrefix":"/rk/v1/","gfPort":8084}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost
operation=Bootstrap
resCode=OK
eventStatus=Ended
EOE
...
调用 /rk/v1/healthy 来确定服务是否启动。
代码语言:txt复制$ curl localhost:8081/rk/v1/healthy
{"healthy":true}
$ curl localhost:8082/rk/v1/healthy
{"healthy":true}
$ curl localhost:8083/rk/v1/healthy
{"healthy":true}
$ curl localhost:8084/rk/v1/healthy
{"healthy":true}
$ curl localhost:8085/rk/v1/healthy
{"healthy":true}
$ curl localhost:8086/rk/v1/healthy
{"healthy":true}
$ curl localhost:8087/rk/v1/healthy
{"healthy":true}
6.验证
我们将会发送 /v1/hello 请求到各个端口,并查看 RPC 日志。
- grpc-gateway@8081 (grpc-gateway 目前暂无 RPC 日志输出)
$ curl "localhost:8081/v1/hello?name=rk-dev"
{"message":"Hello rk-dev"}
- gin@8082
$ curl "localhost:8082/v1/hello?name=rk-dev"
{"message":"Hello rk-dev"}
# RPC log from server side
------------------------------------------------------------------------
endTime=2022-01-03T19:18:26.23983 08:00
startTime=2022-01-03T19:18:26.239805 08:00
elapsedNano=25460
timezone=CST
ids={"eventId":"0d284016-f714-4c85-8af8-9715dc9ed35f"}
app={"appName":"rk","appVersion":"","entryName":"gin","entryType":"GinEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/v1/hello","apiProtocol":"HTTP/1.1","apiQuery":"name=rk-dev","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:54102
operation=/v1/hello
resCode=200
eventStatus=Ended
EOE
- echo@8083
$ curl "localhost:8082/v1/hello?name=rk-dev"
{"message":"Hello rk-dev"}
# RPC log from server side
------------------------------------------------------------------------
endTime=2022-01-03T19:19:11.109838 08:00
startTime=2022-01-03T19:19:11.109817 08:00
elapsedNano=21242
timezone=CST
ids={"eventId":"34419c7c-1a78-484f-ba7a-bfca69178b82"}
app={"appName":"rk","appVersion":"","entryName":"echo","entryType":"EchoEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/v1/hello","apiProtocol":"HTTP/1.1","apiQuery":"name=rk-dev","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:56995
operation=/v1/hello
resCode=200
eventStatus=Ended
EOE
- gf@8084
$ curl "localhost:8084/v1/hello?name=rk-dev"
{"message":"Hello rk-dev"}
# RPC log from server side
------------------------------------------------------------------------
endTime=2022-01-03T19:19:39.905883 08:00
startTime=2022-01-03T19:19:39.905858 08:00
elapsedNano=24703
timezone=CST
ids={"eventId":"58bf8706-09ff-434e-b405-d6cdb8dbe8c2"}
app={"appName":"rk","appVersion":"","entryName":"gf","entryType":"GfEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/v1/hello","apiProtocol":"HTTP/1.1","apiQuery":"name=rk-dev","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:58802
operation=/v1/hello
resCode=200
eventStatus=Ended
EOE
- fiber@8085
$ curl "localhost:8085/v1/hello?name=rk-dev"
{"message":"Hello rk-dev"}
# RPC log from server side
------------------------------------------------------------------------
endTime=2022-01-03T19:20:48.834567 08:00
startTime=2022-01-03T19:20:48.834425 08:00
elapsedNano=142332
timezone=CST
ids={"eventId":"a98a6e9f-6519-4ded-971e-0b6e59f66096"}
app={"appName":"rk","appVersion":"","entryName":"fiber","entryType":"FiberEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/v1/hello","apiProtocol":"http","apiQuery":"name=rk-dev","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=127.0.0.1:63237
operation=/v1/hello
resCode=200
eventStatus=Ended
EOE
- zero@8086
$ curl "localhost:8086/v1/hello?name=rk-dev"
{"message":"Hello rk-dev"}
# RPC log from server side
------------------------------------------------------------------------
endTime=2022-01-03T19:21:20.835415 08:00
startTime=2022-01-03T19:21:20.835391 08:00
elapsedNano=24495
timezone=CST
ids={"eventId":"a6a53d21-4cf4-4b45-97ca-2b190e438e9c","traceId":"bf7a2359d0813de4388dd11c4f161321"}
app={"appName":"rk","appVersion":"","entryName":"zero","entryType":"ZeroEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/v1/hello","apiProtocol":"HTTP/1.1","apiQuery":"name=rk-dev","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=localhost:65299
operation=/v1/hello
resCode=200
eventStatus=Ended
EOE
- mux@8087
$ curl "localhost:8086/v1/hello?name=rk-dev"
{"message":"Hello rk-dev"}
# RPC log from server side
------------------------------------------------------------------------
endTime=2022-01-03T19:22:13.13191 08:00
startTime=2022-01-03T19:22:13.131889 08:00
elapsedNano=21449
timezone=CST
ids={"eventId":"8a0f2db6-8e13-4773-bedd-962060adbe41"}
app={"appName":"rk","appVersion":"","entryName":"mux","entryType":"MuxEntry"}
env={"arch":"amd64","az":"*","domain":"*","hostname":"lark.local","localIP":"10.8.0.2","os":"darwin","realm":"*","region":"*"}
payloads={"apiMethod":"GET","apiPath":"/v1/hello","apiProtocol":"HTTP/1.1","apiQuery":"name=rk-dev","userAgent":"curl/7.64.1"}
error={}
counters={}
pairs={}
timing={}
remoteAddr=127.0.0.1:52277
operation=/v1/hello
resCode=200
eventStatus=Ended
EOE
rk-boot 介绍
rk-boot 是一个可通过 YAML 启动多种 Web 服务的框架。
有点类似于 Spring boot。通过集成 rk-xxx 系列库,可以启动多种 Web 框架。当然,用户也可以自定义 rk-xxx 库集成到 rk-boot 中。
rk-boot 亮点
- 通过同样格式的 YAML 文件,启动不同 Web 框架。
- 即使是不同框架,统一日志,Metrics, Tracing 等格式
- 用户可以通过实现 rkentry.Entry 来自定义 YAML 文件。
rk-boot 支持的 Web 框架
欢迎贡献新的 Web 框架到 rk-boot 系列中。
参考 docs & rk-gin 作为例子。
框架 | 开发状态 | 安装 | 依赖 |
---|---|---|---|
Gin | Stable | go get github.com/rookie-ninja/rk-boot/gin | rk-gin |
gRPC | Stable | go get github.com/rookie-ninja/rk-boot/grpc | rk-grpc |
Echo | Stable | go get github.com/rookie-ninja/rk-boot/echo | rk-echo |
GoFrame | Stable | go get github.com/rookie-ninja/rk-boot/gf | rk-gf |
Fiber | Testing | go get github.com/rookie-ninja/rk-boot/fiber | rk-fiber |
go-zero | Testing | go get github.com/rookie-ninja/rk-boot/zero | rk-zero |
GorillaMux | Testing | go get github.com/rookie-ninja/rk-boot/mux | rk-mux |