《使用Gin框架構建分散式應用》閱讀筆記:p77-p87

codists發表於2024-10-17

《用Gin框架構建分散式應用》學習第5天,p77-p87總結,總計11頁。

一、技術總結

1.Go知識點

(1)context

2.on-premises software

p80, A container is like a separate OS, but not virtualized; it only contains the dependencies needed for that one application, which makes the container portable and deployable on-premises or on the cloud。

premises的意思是“the land and buildings owned by someone, especially by a company or organization(歸屬於某人(尤指公司、組織)的土地或建築物)”。簡單來說 on-premises software 指的是執行在本地的服務(軟體)。

wiki 對這個名稱的定義很好,這裡直接引用“On-premises software (abbreviated to on-prem, and often written as "on-premise") is installed and runs on computers on the premises of the person or organization using the software, rather than at a remote facility such as a server farm or cloud”。

3.openssl rand命令

openssl rand -base64 12 | docker secret create mongodb_password
-

rand 命令語法:

openssl rand [-help] [-out file] [-base64] [-hex] [-engine id] [-rand files] [-writerand file] [-provider name] [-provider-path path] [-propquery propq] num[K|M|G|T]

12對應 num引數。rand詳細用法參考https://docs.openssl.org/3.4/man1/openssl-rand/。對於一個命令,我們需要掌握其有哪些引數及每個引數的含義。

4.檢視docker latest tag對應的版本號

(1)未下載映象

開啟latest tag對應的頁面,如:https://hub.docker.com/layers/library/mongo/latest/images/sha256-e6e25844ac0e7bc174ab712bdd11bfca4128bf64d28f85d0c6835c979e4a5ff9,搜尋 VERSION,然後找到版本號。

(2)已下載映象

使用 docker inspect 命令進行檢視。

# docker inspect d32 | grep -i version
        "DockerVersion": "",
                "GOSU_VERSION=1.17",
                "JSYAML_VERSION=3.13.1",
                "MONGO_VERSION=8.0.1",
                "org.opencontainers.image.version": "24.04"

5.mongo-go-driver示例

書上程式碼使用的mongo-go-driver v1.4.5,現在已經更新到了v2.0.0,導致有些程式碼無法執行,建議使用最新版。這裡還得再吐槽下 Go 生態圈的文件寫得太糟糕了。示例:

client, _ := mongo.Connect(options.Client().ApplyURI("mongodb://localhost:27017"))
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

_ = client.Ping(ctx, readpref.Primary())

個人認為,上面的程式碼 err 就不應該忽略掉。而應該是:

package main

import (
	"context"
	"github.com/gin-gonic/gin"
	"github.com/rs/xid"
	"go.mongodb.org/mongo-driver/v2/mongo"
	"go.mongodb.org/mongo-driver/v2/mongo/options"
	"go.mongodb.org/mongo-driver/v2/mongo/readpref"
	"log"
	"net/http"
	"strings"
	"time"
)

type Recipe struct {
	ID           string    `json:"id"`
	Name         string    `json:"name"`
	Tags         []string  `json:"tags"`
	Ingredients  []string  `json:"ingredients"`
	Instructions []string  `json:"instructions"`
	PublishAt    time.Time `json:"publishAt"`
}

var recipes []Recipe

func init() {
	// 方式1:使用記憶體進行初始化
	// recipes = make([]Recipe, 0)
	// file, err := os.Open("recipes.json")
	// if err != nil {
	// 	log.Fatal(err)
	// 	return
	// }
	// defer file.Close()
	//
	// // 反序列化:將json轉為slice
	// decoder := json.NewDecoder(file)
	// err = decoder.Decode(&recipes)
	// if err != nil {
	// 	log.Fatal(err)
	// 	return
	// }

	// 方式2:使用 MongoDB 儲存資料
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()
	// 建立連線,這裡的 err 針對的是 URI 錯誤
	client, err := mongo.Connect(options.Client().ApplyURI("mongodb1://admin:admin@localhost:27017"))
	if err != nil {
		log.Fatal("MongoDB connect error: ", err)
	}

	// 判斷連線是否成功
	if err := client.Ping(ctx, readpref.Primary()); err != nil {
		log.Fatal("MongoDB Ping error: ", err)
	}

	log.Println("Connected to MongoDB successfully.")

}

// swagger:route POST /recipes  createRecipe
//
// # Create a new recipe
//
// Responses:
//
//	200: recipeResponse
//
// NewRecipeHandler 新增recipe,是按照單個新增,所以這裡名稱這裡用的是單數
func NewRecipeHandler(c *gin.Context) {
	var recipe Recipe
	if err := c.ShouldBindJSON(&recipe); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	recipe.ID = xid.New().String()
	recipe.PublishAt = time.Now()
	recipes = append(recipes, recipe)
	c.JSON(http.StatusOK, recipe)
}

// swagger:route GET /recipes  listRecipes
// Returns list of recipes
// ---
// produces:
// - application/json
// responses:
// '200':
// description: Successful operation
// ListRecipesHandler 差下recipes,因為是查詢所有,所以名稱這裡用的是複數
func ListRecipesHandler(c *gin.Context) {
	c.JSON(http.StatusOK, recipes)
}

// UpdateRecipeHandler 更新 recipe,因為是單個,所以使用的是單數。
// id 從 path 獲取,其它引數從 body 獲取。
func UpdateRecipeHandler(c *gin.Context) {
	id := c.Param("id")

	// 資料解碼
	var recipe Recipe
	if err := c.ShouldBindJSON(&recipe); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	// 判斷 id 是否存在
	index := -1
	for i := 0; i < len(recipes); i++ {
		if recipes[i].ID == id {
			index = i
		}
	}

	// 如果 id 不存在
	if index == -1 {
		c.JSON(http.StatusNotFound, gin.H{"error": "recipe not found"})
		return
	}
	// 如果 id 存在則進行更新
	recipes[index] = recipe
	c.JSON(http.StatusOK, recipe)
}

// DeleteRecipeHandler 刪除 recipe: 1.刪除之前判斷是否存在,存在就刪除,不存在就提示不存在。
func DeleteRecipeHandler(c *gin.Context) {
	id := c.Param("id")
	index := -1
	for i := 0; i < len(recipes); i++ {
		if recipes[i].ID == id {
			index = i
		}
	}

	if index == -1 {
		c.JSON(http.StatusNotFound, gin.H{"message": "recipe not found"})
		return
	}
	recipes = append(recipes[:index], recipes[index+1:]...)
	c.JSON(http.StatusOK, gin.H{
		"message": "recipe deleted",
	})
}

// SearchRecipesHandler 查詢 recipes
func SearchRecipesHandler(c *gin.Context) {
	tag := c.Query("tag")
	listOfRecipes := make([]Recipe, 0)

	for i := 0; i < len(recipes); i++ {
		found := false
		for _, t := range recipes[i].Tags {
			if strings.EqualFold(t, tag) {
				found = true
			}
			if found {
				listOfRecipes = append(listOfRecipes, recipes[i])
			}
		}
	}
	c.JSON(http.StatusOK, listOfRecipes)
}
func main() {
	router := gin.Default()
	router.POST("/recipes", NewRecipeHandler)
	router.GET("/recipes", ListRecipesHandler)
	router.PUT("/recipes/:id", UpdateRecipeHandler)
	router.DELETE("/recipes/:id", DeleteRecipeHandler)
	router.GET("/recipes/search", SearchRecipesHandler)
	err := router.Run()
	if err != nil {
		return
	}
}

二、英語總結

1.ephemeral

p79, I opted to go with Docker duce to its popularity and simplicity in runing ephermeral environment.

(1)ephemeral: ephemera + -al。

(2)ephemera: epi-("on") + hemera("day"), lasting one day , short-lived"。

三、其它

從目前的閱讀體驗來看,作者預設讀者充分掌握Golang,絲毫不會展開介紹。

四、參考資料

1. 程式設計

(1) Mohamed Labouardy,《Building Distributed Applications in Gin》:https://book.douban.com/subject/35610349

2. 英語

(1) Etymology Dictionary:https://www.etymonline.com

(2) Cambridge Dictionary:https://dictionary.cambridge.org

歡迎搜尋及關注:程式設計人(a_codists)

相關文章