Gin 是一个 Golang 的 web 框架,封装使用简单,目前被广泛使用。实际上对于 Golang 而言,web 框架的依赖要远比 Python,Java 之类的要小。因为其自身的 net/http 足够简单,性能也非常不错。那么包括 Gin 在内的各种 Go web 框架出现的意义仅仅只是为了开发方便吗?关于这方面更深层次的探讨可以看这篇文章,同时也可以参考我学长博客中的相关内容

这里同时推荐一个简洁实用的 Gin 中文文档

简而言之,在 Http 连接建立方面,Golang 原生的 net/http 只用了一个简单的服务多路复用器,通过匹配 URL 提供对应的请求响应服务,几乎无法处理当下规范的 RESTful Web 接口;而 Gin 框架则以 前缀树 作为数据结构,使用了多棵 路由树 来实现响应服务与对应 Http 请求的匹配,使得 Gin 框架下的 Http 通信在有更丰富的功能性的前提下,获得了更高的性能。

安装

创建 Gin 项目,控制台输入

1
go get -u github.com/gin-gonic/gin

.go 文件中加入依赖

1
2
3
import (
"github.com/gin-gonic/gin"
)

一个简单的例子

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
package main

import (
"github.com/gin-gonic/gin"
)

func main() {

// 1. 初始化路由/初始化 web 引擎
router := gin.Default()

// 2. 做路由匹配,通过匿名函数
router.GET("/", func(context *gin.Context) {
_, err := context.Writer.WriteString("hello world!")
if err != nil {
return
}
})

// 3. 启动运行,在 127.0.0.1:8080
err := router.Run(":8080")
if err != nil {
return
}
}

该程序启动后,浏览器访问 127.0.0.1:8080 将会输出 **hello world!**。

将 Gin 框架接入 Consul 服务发现与 Go-Micro 微服务

通过注册到 Consul 服务发现,无论是服务前端还是微服务端都仅需要知道 Consul 服务器的信息并将服务在其上进行注册。除了服务名, Gin 服务前端和 Go-Micro 微服务端是无法相互了解的(双方都不知道对方的 ip 地址和端口号)。接下来将通过一个例子来说明这是如何实现的。

Consul 服务发现

具体配置操作详见 Golang 微服务实战 - 2. Consul 。在这个例子中, 必须 首先启动 Consul 服务发现。在控制台中输入 consul agent -dev 即可。然后,在浏览器的 http://127.0.0.1:8500/ui/dc1/services 查看 Consul 仪表板

Go-Micro 微服务

Golang 微服务实战 - 3. Go-Micro 框架 的基础上,修改 Go-Micro 框架服务端代码如下所示:

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
package main

import (
"Micro-Learning/handler" // 导入 handler 文件夹内容
pb "Micro-Learning/proto"
"github.com/go-micro/plugins/v4/registry/consul"
"go-micro.dev/v4"
"go-micro.dev/v4/logger"
"go-micro.dev/v4/registry"
)

var (
service = "micro-learning"
version = "latest"
)

func main() {
consulReg := consul.NewRegistry(registry.Addrs("127.0.0.1:8500"))

// Create service
srv := micro.NewService()
srv.Init(
micro.Name(service),
micro.Registry(consulReg), // 将服务注册到 consul
micro.Version(version),
)

// Register handler
// handler.MicroLearning 在 hanler 文件夹下定义
if err := pb.RegisterMicroLearningHandler(srv.Server(), new(handler.MicroLearning)); err != nil {
logger.Fatal(err)
}
// Run service
if err := srv.Run(); err != nil {
logger.Fatal(err)
}
}

Go-Micro 框架为了符合 MVC 标准,将业务代码放在了 hanler 文件夹下,其中的业务代码如下:

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

import (
"context"
"fmt"
"go-micro.dev/v4/logger"

pb "Micro-Learning/proto"
)

type MicroLearning struct{}

func (e *MicroLearning) Call(ctx context.Context, req *pb.CallRequest, rsp *pb.CallResponse) error {
logger.Infof("Received MicroLearning.Call request: %v", req)
fmt.Printf("Received MicroLearning.Call request: %v\n", req)
rsp.Msg = "Hello " + req.Name
return nil
}

直接运行 main.go ,控制台出现如下信息说明运行成功,且服务已经注册到了 Consul。

1
2
3
4
2022-10-20 20:21:50  file=Micro-Learning/main.go:35 level=info Starting [service] micro-learning
2022-10-20 20:21:50 file=v4@v4.9.0/service.go:96 level=info Transport [http] Listening on [::]:55163
2022-10-20 20:21:50 file=v4@v4.9.0/service.go:96 level=info Broker [http] Connected to 127.0.0.1:55164
2022-10-20 20:21:50 file=server/rpc_server.go:832 level=info Registry [consul] Registering node: micro-learning-160dc67a-5352-44b1-9b03-84f31f53e889

在 Consul 仪表板 http://127.0.0.1:8500/ui/dc1/services 可以看到 micro-learning 服务已经被注册了。

micro-learning 已注册到 Consul 上

Gin 服务前端

首先,必须将 Go-Micro 微服务部分项目文件中的文件夹 /proto/ 直接拷贝到 Gin 项目文件夹中,不能自己再重写或者重新编译 protobuf ,因为服务前端和微服务端二者要连通, protobuf 必须完全相同。虽然复制黏贴的操作看起来很蠢, 事实上确实如此, 但在实际生产环境中一般都是服务前端直接在 import 中调用微服务端所在 git 仓库的 protobuf ,此处为了测试和演示方便直接使用复制黏贴即可。

一个简单的例子 的基础上修改代码如下:

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
package main

import (
pb "Gin-Web/proto"
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-micro/plugins/v4/registry/consul"
"go-micro.dev/v4"
"go-micro.dev/v4/client"
"go-micro.dev/v4/registry"
)

func CallRemote(ctx *gin.Context) {
// 初始化服务发现 Consul
consulReg := consul.NewRegistry(registry.Addrs("127.0.0.1:8500"))
srv := micro.NewService()
srv.Init(
micro.Client(client.NewClient()),
micro.Registry(consulReg), // 将服务注册到 consul
)

microLearningService := pb.NewMicroLearningService("micro-learning", srv.Client())
callResponse, err := microLearningService.Call(context.TODO(), &pb.CallRequest{Name: "Kevin"})
if err != nil {
fmt.Println(err)
return
}
respString := callResponse.GetMsg()
_, err = ctx.Writer.WriteString(respString)
if err != nil {
fmt.Println(err)
return
}
}

func main() {

// 1. 初始化路由/初始化 web 引擎
router := gin.Default()

// 2. 做路由匹配
router.GET("/", CallRemote)

// 3. 启动运行
err := router.Run(":8090")
if err != nil {
fmt.Println(err)
return
}
}

然后直接运行 Gin 服务前端,在浏览器中访问 http://127.0.0.1:8090/ ,输出 Hello Kevin 则说明调用成功。