go語言gin web框架學習筆記(1)-初始化一個gin engine

main.go

// mail.go
// 一個 go 程序中只有一個入口,main 包下的 main 函數。
package main

// 引入包。
import (
	"apiserver/router"
	"errors"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"time"
)

// 每一個可執行程序所必須包含的,通常來講都是在啓動後第一個執行的函數(若是有 init() 函數則會先執行該函數)。
func main() {
	// Create the Gin engine.
	// 如下格式只能在函數內使用。此爲定義一個變量。
	// Go語言的變量和函數的公有私有靠首字母大小寫區分,首字母大寫是公有的,小寫的私有的。
	g := gin.New()

	// Go 語言切片是對數組的抽象。Go 數組的長度不可改變,切片爲 Go 內置類型,也叫"動態數組",追加元素可能使切片容量增大。
	middlewares := []gin.HandlerFunc{}

	// Routes.
	// router 包是咱們本身寫的一個包。裏面定義了一個 Load 函數。
	// Load 函數接收 *gin.Engine 和 ...gin.HandlerFunc 做爲實參。
	// * 表明變量的指針,實際使用時經過指針引用變量地址使用。
	// ... 表明實參是個變長參數(長度不固定)。
	// gin 中的 middleware,本質是一個匿名回調函數。這和綁定到一個路徑下的處理函數本質是同樣的。
	// 回調函數(callback function),就是一個應用傳遞給一個庫函數的參數,而這個參數爲一個函數。
	// 打個比方,住酒店(應用)時須要使用叫醒服務(庫函數),須要客人(用戶在app上操做)指定叫醒的方法(回調函數)。
	// 叫醒的方法裏預約義了幾個方式(回調函數中的屬性),好比"打電話叫醒", "敲門叫醒", "從天花板落水下來"等。
	router.Load(
		// Cores.
		g,

		// Middlewares.
		middlewares...,
	)

	// Ping the server to make sure the router is working.
	// go 語言經過 goroutine 實現高併發,而開啓 goroutine 的鑰匙正時 go 關鍵字。
	// 這裏其實是一個匿名 goroutine 函數。
	go func() {
		if err := pingServer(); err != nil {
			log.Fatal("The router has no response, or it might took too long to start up.", err)
		}
		log.Print("The router has been deployed successfully.")
	}()

	// 日誌模塊的基本使用方法。
	// 格式化打印的基本使用方法。
	log.Printf("Start to listening the incoming requests on http address: %s", ":8080")
	log.Printf(http.ListenAndServe(":8080", g).Error())
}

// pingServer pings the http server to make sure the router is working.
func pingServer() error {
	for i := 0; i < 2; i++ {
		resp, err := http.Get("http://127.0.0.1:8080" + "/sd/health")
		if err == nil && resp.StatusCode == 200 {
			return nil
		}

		// Sleep for a secongd to continue the next ping.
		log.Print("Waiting for the router, retry in 1 second.")
		time.Sleep(time.Second)
	}
	return errors.New("Cannot connect to the router.")
}
複製代碼

apiserver/router/router.go

package router

import (
	"net/http"

	"apiserver_demo1/apiserver/handler/sd"
	"apiserver_demo1/apiserver/router/middleware"

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

// Load loads the middlewares, routes, handlers.
// 經過 gin 包的 Engine 結構體的 Use 方法來具體實現路由引擎接口。
// Go 語言沒有嚴格的重載的概念,可是一個結構體若是擁有實現某個接口的方法,就至關於繼承了該接口。
// 這樣就能夠達到擁有各自的內部實現的子結構體都用相同接口來建立實例。
// gin 包下的 Engine 結構體便實現了 Iroutes 等接口的方法。
func Load(g *gin.Engine, mw ...gin.HandlerFunc) *gin.Engine {
	// Middlewares.
	// Engine 實現了 IRoutes 接口下的 Use 函數。同時 Routergroup 也實現了此函數。
	// Engine 中又定義了一個 Routergroup 成員,Use 方法在 Routergroup 中具體實現是相似於數組的 append 方法,這裏就是添加
	// 處理器方法的操做,而 Engine 的 Use 方法在調用完 Routergroup 的 Use 以後又再增長了本身的邏輯。
	// 此處爲經過結構體中定義同父級接口的結構體來達到進一步"繼承"的效果。
	g.Use(gin.Recovery())
	g.Use(middleware.NoCache)
	g.Use(middleware.Options)
	g.Use(middleware.Secure)
	g.Use(mw...)
	// 404 Handler.
	// 函數也能夠經過 type function_name func( [parameter_list] ) [return_types]
	// 這樣的格式來定義,而且能夠做爲參數傳遞給其餘函數。
	// NoRoute 方法屬於 Engine 結構體,經過下面這種方式將自定義的回調函數傳遞給本身的成員,達到重寫方法的目的。
	g.NoRoute(func(c *gin.Context) {
		c.String(http.StatusNotFound, "The incorrect API route.")
	})

	// The health check handlers
	// Engine 的 Group 方法是 Routergroup 結構體中的實現,而 Engine 有一個 Routergroup 成員,Routergroup 又有一個 Engine
	// 對象指針成員。經過此方法在 Engine 中實現 Routergroup 的方法重寫。
	svcd := g.Group("/sd")
	{
		svcd.GET("/health", sd.HealthCheck)
		svcd.GET("/disk", sd.DiskCheck)
		svcd.GET("/cpu", sd.CPUCheck)
		svcd.GET("/ram", sd.RAMCheck)
	}

	return g
}
複製代碼

apiserver/router/middleware/header.go

package middleware

import (
	"net/http"
	"time"

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

// NoCache is a middleware function that appends headers
// to prevent the client from caching the HTTP response.
func NoCache(c *gin.Context) {
	c.Header("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate, value")
	c.Header("Expires", "Thu, 01 Jan 1970 00:00:00 GMT")
	c.Header("Last-Modified", time.Now().UTC().Format(http.TimeFormat))
	c.Next()
}

// Options is a middleware function that appends headers
// for options requests and aborts then exits the middleware
// chain and ends the request.
func Options(c *gin.Context) {
	if c.Request.Method != "OPTIONS" {
		c.Next()
	} else {
		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
		c.Header("Access-Control-Allow-Headers", "authorization, origin, content-type, accept")
		c.Header("Allow", "HEAD,GET,POST,PUT,PATCH,DELETE,OPTIONS")
		c.Header("Content-Type", "application/json")
		c.AbortWithStatus(200)
	}
}

// Secure is a middleware function that appends security
// and resource access headers.
func Secure(c *gin.Context) {
	c.Header("Access-Control-Allow-Origin", "*")
	c.Header("X-Frame-Options", "DENY")
	c.Header("X-Content-Type-Options", "nosniff")
	c.Header("X-XSS-Protection", "1; mode=block")
	if c.Request.TLS != nil {
		c.Header("Strict-Transport-Security", "max-age=31536000")
	}

	// Also consider adding Content-Security-Policy headers
	// c.Header("Content-Security-Policy", "script-src 'self' https://cdnjs.cloudflare.com")
}
複製代碼

apiserver/handler/sd/check.go

package sd

import (
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/shirou/gopsutil/cpu"
	"github.com/shirou/gopsutil/disk"
	"github.com/shirou/gopsutil/load"
	"github.com/shirou/gopsutil/mem"
)

const (
	B  = 1
	KB = 1024 * B
	MB = 1024 * KB
	GB = 1024 * MB
)

// HealthCheck shows `OK` as the ping-pong result.
func HealthCheck(c *gin.Context) {
	message := "OK"
	c.String(http.StatusOK, "\n"+message)
}

// DiskCheck checks the disk usage.
func DiskCheck(c *gin.Context) {
	u, _ := disk.Usage("/")

	usedMB := int(u.Used) / MB
	usedGB := int(u.Used) / GB
	totalMB := int(u.Total) / MB
	totalGB := int(u.Total) / GB
	usedPercent := int(u.UsedPercent)

	status := http.StatusOK
	text := "OK"

	if usedPercent >= 95 {
		status = http.StatusOK
		text = "CRITICAL"
	} else if usedPercent >= 90 {
		status = http.StatusTooManyRequests
		text = "WARNING"
	}

	message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
	c.String(status, "\n"+message)
}

// CPUCheck checks the cpu usage.
func CPUCheck(c *gin.Context) {
	cores, _ := cpu.Counts(false)

	a, _ := load.Avg()
	l1 := a.Load1
	l5 := a.Load5
	l15 := a.Load15

	status := http.StatusOK
	text := "OK"

	if l5 >= float64(cores-1) {
		status = http.StatusInternalServerError
		text = "CRITICAL"
	} else if l5 >= float64(cores-2) {
		status = http.StatusTooManyRequests
		text = "WARNING"
	}

	message := fmt.Sprintf("%s - Load average: %.2f, %.2f, %.2f | Cores: %d", text, l1, l5, l15, cores)
	c.String(status, "\n"+message)
}

// RAMCheck checks the disk usage.
func RAMCheck(c *gin.Context) {
	u, _ := mem.VirtualMemory()

	usedMB := int(u.Used) / MB
	usedGB := int(u.Used) / GB
	totalMB := int(u.Total) / MB
	totalGB := int(u.Total) / GB
	usedPercent := int(u.UsedPercent)

	status := http.StatusOK
	text := "OK"

	if usedPercent >= 95 {
		status = http.StatusInternalServerError
		text = "CRITICAL"
	} else if usedPercent >= 90 {
		status = http.StatusTooManyRequests
		text = "WARNING"
	}

	message := fmt.Sprintf("%s - Free space: %dMB (%dGB) / %dMB (%dGB) | Used: %d%%", text, usedMB, usedGB, totalMB, totalGB, usedPercent)
	c.String(status, "\n"+message)
}
複製代碼
相關文章
相關標籤/搜索