# 使用 gin
## 使用 go module
要求 Go version >= 1.13 或者 Go version >= 1.11 同时将 `GO111MODULE=on` 开启。
### 初始化 go module
创建项目目录,任意目录即可不必在 GOPATH/src 目录,并进入项目目录
```shell
mkdir ginProj
cd ginProj
```
执行 `go mod init moduleName` 初始化 go module 项目:
```
$ go mod init ginProj
go: creating new go.mod: module ginProj
```
其中 ginProj 是自定义的 moduleName,本例与目录一致,其实是任意名称。
### 编写 main.main()
利用编辑器(推荐 GoLand),创建 `ginProj/main.go` 文件,并写入如下代码:
```go
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
```
### 运行
直接运行 `go run main.go` 即可。
```shell
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /ping --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
exit status 2
```
表示运行成功。
通过浏览器或者其他请求工具(例如 postman,curl 等),访问 localhost:8080/ping 即可获取 JSON 数据格式响应 `{"message": "pong"}`。
## GOPATH
### 安装
利用 go get 完成下载安装。
```shell
$ go get -u github.com/gin-gonic/gin
```
### 编写 main.main()
在 GOPATH/src 目录创建项目目录,例如 ginProj,利用编辑器(推荐 GoLand),创建 `ginProj/main.go` 文件,并写入如下代码:
```go
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
```
### 运行
直接运行 `go run main.go` 即可。
```shell
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /ping --> main.main.func1 (3 handlers)
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
exit status 2
```
表示运行成功。
通过浏览器或者其他请求工具(例如 postman,curl 等),访问 localhost:8080/ping 即可获取 JSON 数据格式响应 `{"message": "pong"}`。
## 构建发布
直接运行 `go run main.go` 会先编译再运行。在生产部署时,应该先编译形成可执行性文件,再运行可执行性文件。使用命令 go build 完成:
```shell
$ go build -o backend main.go
./backend
```
以上命令会编译生成 backend 执行文件,若省略 -o 选项,默认生成 main 执行文件。
在生成环境部署时,应该使用 `releaseMode` 发布模式,代码中默认使用的为 `debug` 调试模式,调用函数 `gin.SetMode(gin.ReleaseMode)` 可设置为发布模式。在 main.go 中:
```go
func main() {
// 设置为发布模式
gin.SetMode(gin.ReleaseMode)
/*
其他代码
*/
r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}
```
发布模式会减少一些调试输出和做出一些特定优化等,例如路由日志在发布模式下就不会输出到终端。
## 推荐目录结构
gin 框架对于项目目录结构没有要求,只要符合典型的包管理机制即可。根据典型的 MVC 框架的代码架构,建议的目录结构为:
```
/main.go 入口文件
/config/ 配置
/router/ 路由
api.go
web.go
/handler/ 请求处理器
user.go
/middleware/ 中间件
jwtToken.go
/model/ 模型
user.go
/test/ 测试
```
以上结构是推荐而不是强制的。
## 停止监听
### 暴力停止
我们在 go run main.go 后,若需要停止监听,需要使用 `ctrl+c` 终止监听。该方案会立即终止服务器监听,同时结束正在处理的请求,也就是说若存在未处理完毕的请求,是不能继续处理的。
### 优雅停止
代替示例代码中 `router.Run()` 方法,我们可以使用 `http.Server` 内置的 [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) 方法优雅地停止。所谓优雅,指的是可以将正在处理的请求处理完毕后再关闭服务器。示例代码如下 main.go :
```go
package main
import (
"context"
"log"
"net/http"
"os"
"os/signal"
"time"
"github.com/gin-gonic/gin"
)
func main() {
// 常规的初始化路由
router := gin.Default()
router.GET("/", func(c *gin.Context) {
time.Sleep(5 * time.Second)
c.String(http.StatusOK, "Welcome Gin Server")
})
// 定义服务器
srv := &http.Server{
Addr: ":8080",
Handler: router,
}
// 利用 goroutine 启动监听
go func() {
// srv.ListenAndServe() 监听
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 等待中断信号以优雅地关闭服务器(设置 5 秒的超时时间)
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
// quit 信道是同步信道,若没有信号进来,处于阻塞状态
// 反之,则执行后续代码
<-quit
log.Println("Shutdown Server ...")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 调用 srv.Shutdown() 完成优雅停止
// 调用时传递了一个上下文对象,对象中定义了超时时间
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("Server Shutdown:", err)
}
log.Println("Server exiting")
}
```
以上代码的思路是:主 goroutine 用于监听信号,若进来信号则优雅停止。而调用的 goroutine 用于监听 HTTP 请求。
测试以上代码,结果为:
```shell
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET / --> main.main.func1 (3 handlers)
2019/11/05 22:49:04 Shutdown Server ...
[GIN] 2019/11/05 - 22:49:07 | 200 | 5.0000438s | ::1 | GET /
2019/11/05 22:49:08 Server exiting
```
本例测试中,请求处理器增加一个 5s 的延迟,我们用请求代理端进行请求,在服务器响应完成前,通过 `ctrl+c` 关闭服务器。测试服务器还是会将该请求处理完毕,再关闭!优雅不?
> 关注 红牛慕课,或访问 [红牛慕课](http://www.hellokang.net/gin/)
![subscribe_gin.png](https://static.studygolang.com/191104/01cd42a58db0c53d411315ca5d845f85.png)
有疑问加站长微信联系(非本文作者))