首页 > 其他分享 >《使用Gin框架构建分布式应用》阅读笔记:p77-p87

《使用Gin框架构建分布式应用》阅读笔记:p77-p87

时间:2024-10-17 23:20:51浏览次数:7  
标签:p77 mongo err p87 recipes recipe 分布式应用 gin id

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

标签:p77,mongo,err,p87,recipes,recipe,分布式应用,gin,id
From: https://www.cnblogs.com/codists/p/18473307

相关文章

  • E61 树形DP P8744 [蓝桥杯 2021 省 A] 左孩子右兄弟
    视频链接:  P8744[蓝桥杯2021省A]左孩子右兄弟-洛谷|计算机科学教育新生态(luogu.com.cn)//树形DPO(n)#include<bits/stdc++.h>usingnamespacestd;constintN=100005;intn,f[N],son[N];inthead[N],idx;structE{intv,ne;}e[N<<1];voidadd(intu......
  • P7730 [JDWOI-1] 蜀道难
    首先,区间增加定值并且要求单调不降,很容易想到差分。于是先把\(h\)数组差分一下,题目的要求即为最小代价使得\(h\)均为非负数。观察一下两种操作,发现\(n\)的范围很小,可以枚举操作的起点\(i\),然后如果操作是压低,相当于\(h[i]--,h[i+l[i]]++\)。而如果操作是抬高,相当于......
  • P8735
    Statement给出\(k,p,L\),数序列\(a\),满足如下条件:\(1\lea_i\lek\)\(\sum_ia_i=L\)\(\nexistsi,a_i\gep\landa_{i+1}\gep\)答案对\(20201114\)取模,\(p\lek\le1000,L\le10^{18}\).Solution30pts注意到可以dp,记\(f(i,0/1)\)为凑出\(i\)的方案......
  • 洛谷P8774 [蓝桥杯 2022 省 A] 爬树的甲壳虫 题解 期望DP
    题目链接:https://www.luogu.com.cn/problem/P8774思路:设\(f_i\)为甲壳虫从高度\(i\)到达高度\(n\)因为从高度\(i\)走\(1\)步有\(1-P_{i+1}\)的概率到达高度\(i+1\),有\(P_{i+1}\)的概率到达高度\(0\),所以:\(f_i=1+(1-P_{i+1})\timesf_{i+1}+P_{i+1}\times......
  • P7730 [JDWOI-1] 蜀道难
    感觉每一步都挺自然的。首先连续加减让我们不难想到差分,每次给\(d_i\)加一或减一,每次给\(d_{i+l}\)减一或加一。然后要求单调不降就是要求每个\(d_i\)大于等于\(0\)。然后注意到我们每次操作相当于是\(i\)向\(i+l\)贡献\(1\)或者\(i+l\)向\(i\)贡献\(1\),结合......
  • Springboot“科教兴国”支教门户网站rp778程序+源码+数据库+调试部署+开发环境
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表用户,支教介绍,支教新闻,招聘信息,职位申请,志愿者活动,活动报名,募捐信息,捐赠信息,支教教师,支教学生,科教职位开题报告内容一、项目背景在“科教兴国”战略......
  • P7706 文文的摄影布置 题解
    P7706文文的摄影布置题解原题读完题,发现是线段树。单点修改+区间查询。不过查询的值有些奇怪,就是了,我们考虑用线段树维护这个ψ值(下称待求值)。对于一个区间的待求值,大概有四种情况:如上图四种情况分别为:待求值最大值在左区间待求值最大值在右区间\(a_i与b_j\)在左......
  • P8735 蓝跳跳 题解
    Statement给出\(k,p,L\),数序列\(a\),满足如下条件:\(1\lea_i\lek\)\(\sum_ia_i=L\)\(\nexistsi,a_i\gep\landa_{i+1}\gep\)答案对\(20201114\)取模,\(p\lek\le1000,L\le10^{18}\).Solution30pts注意到可以dp,记\(f(i,0/1)\)为凑出\(i\)的方案......
  • P8734 奇偶覆盖 题解
    Statement矩形面积并,但是覆盖奇数次、覆盖偶数次的面积要分别输出。Solution提供一种不费脑子的做法:首先离散化、扫描线,问题变成维护区间+1-1、询问全局有多少正数是奇数、多少正数是偶数。若去除“正数”的条件,这是很容易用一个标记下传的线段树维护的,区间分别维护0,1个......
  • P8776 最长不下降子序列 题解
    Statement最长不下降子序列(LIS),但是有一次机会,让序列中连续\(k\)个数改成同一个数。\(n\le10^5,a_i\le10^6\).Solution记\(f(i)\)为以\(i\)结尾的LIS长度,\(g(i)\)为以\(i\)开始的LIS长度,可预处理.答案一定是\(f(i)+k+g(j)\)这样拼接起来的,其中\(i+k+1\le......