ETJava Beta | Java    注册   登录
  • 搜索:
  • 《使用Gin框架构建分布式应用》阅读笔记:p77-p87

    发表于      阅读(1)     博客类别:Crawler     转自:https://www.cnblogs.com/codists/p/18473307
    如有侵权 请联系我们删除  (页面底部联系我们)  

    《用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)