当前位置: 首页 > news >正文

Go语言实战:入门篇-4:与数据库、redis、消息队列、API

我觉得调用别人的接口来查看效果是一个很有趣的开始,在写自己的接口之前我想首先来探索和这些组件的连接配合方法,毕竟绝大多数程序员都是单线程的API调用工程师(doge)。还有一点则是因为我已经有了Python基础,写了不少接口。只要Go也能够如Python般让我操作这些组件,我能更快地将我的想法落地,或者进行更多的尝试。

一、 API接口

1. 保存读取数据

2. 调用LLM得到回复

3. 轮询获得AI绘图的图片

二、 与Redis

三、 与DB(各种各样的DB,这里以pgsql和mongodb为例)

1. pgsql

2. mongodb

四、与消息队列(这里仅以kafka为例)


一、 API接口

让我们先通过各种各样的LLM接口来尝试它的使用,获得LLM回复、保存读取数据、轮询一个接口保存绘制的图片......

1. 保存读取数据

很不幸地一点是,Go中保存读取数据并不像Python般方便,尤其是在读取中文的时候会出现意想不到的乱码,这是因utf-8编码产生的影响。在开始接下来的任何一个任务之前,我们首先应该解决这个问题。如果大家试过将文本保存到txt文件就会发现Go对于中文的读取有极大的不便,为了一劳永逸地解决再读取过程的不确定性,通过二进制文件保存是一个非常不错的选择。

package mainimport ("encoding/gob""encoding/json""fmt""log""os"
)type Resume struct {Name     stringAge      intAddress  stringLanguage string
}func main() {// 创建一些数据resume := Resume{Name:     "张三",Age:      30,Address:  "唐山市",Language: "中文",}// 创建一个文件来保存数据(.gob 格式)fileGob, err := os.Create("resume_data.gob")if err != nil {log.Fatal("Error creating .gob file:", err)}defer fileGob.Close()// 使用 gob 编码器将数据序列化为二进制并写入文件encoder := gob.NewEncoder(fileGob)err = encoder.Encode(resume)if err != nil {log.Fatal("Error encoding .gob data:", err)}fmt.Println("数据已保存为二进制格式 (.gob)")// 创建一个文件来保存数据(.json 格式)fileJson, err := os.Create("resume_data.json")if err != nil {log.Fatal("Error creating .json file:", err)}defer fileJson.Close()// 使用 json 编码器将数据序列化为 JSON 格式并写入文件encoderJson := json.NewEncoder(fileJson)err = encoderJson.Encode(resume)if err != nil {log.Fatal("Error encoding .json data:", err)}fmt.Println("数据已保存为 JSON 格式 (.json)")
}

我们这里将一个结构体保存到二进制文件中,包含特殊字符。之后我们再读取出来。那如果我们想让数据可视化怎么办呢,总不能一直看二进制数据吧?这个问题很好解决,我们保存的时候一式两份就可以了(json格式似乎同样稳定,但是txt、docx、excel等格式都可能出现中文或者特殊字符加载问题),二进制文件用于后续程序的读取保证数据传递的稳定性,这样我们就即完成了数据的稳定传输和可视化。

package mainimport ("encoding/gob""encoding/json""fmt""log""os"
)type Resume struct {Name     stringAge      intAddress  stringLanguage string
}func main() {// 打开已保存的二进制文件fileGob, err := os.Open("resume_data.gob")if err != nil {log.Fatal("Error opening .gob file:", err)}defer fileGob.Close()// 创建解码器从二进制文件读取数据var resumeGob ResumedecoderGob := gob.NewDecoder(fileGob)err = decoderGob.Decode(&resumeGob)if err != nil {log.Fatal("Error decoding .gob data:", err)}// 打印读取的二进制数据fmt.Println("从二进制文件读取的数据:")fmt.Printf("Gob 数据: %+v\n", resumeGob)// 打开已保存的 JSON 文件fileJson, err := os.Open("resume_data.json")if err != nil {log.Fatal("Error opening .json file:", err)}defer fileJson.Close()// 创建解码器从 JSON 文件读取数据var resumeJson ResumedecoderJson := json.NewDecoder(fileJson)err = decoderJson.Decode(&resumeJson)if err != nil {log.Fatal("Error decoding .json data:", err)}// 打印读取的 JSON 数据fmt.Println("从 JSON 文件读取的数据:")fmt.Printf("JSON 数据: %+v\n", resumeJson)// 比较两者的输出if resumeGob == resumeJson {fmt.Println("\n从 .gob 文件和 .json 文件读取的数据相同")} else {fmt.Println("\n从 .gob 文件和 .json 文件读取的数据不同")}
}

2. 调用LLM得到回复

我们依然使用IPPO网站:https://ppio.com/user/register?invited_by=HBCTMT 中的模型来测试这两个方面的调用。Go中实现流式调用有着意想不到的问题,我们不能简单地通过json解码来获取响应的数据,因为流式传输的不仅内容是被拆开的,json格式也是同样被拆开的。在Python中我们可以调用from fastapi.responses import StreamingResponse来包裹流式响应,让它呈现比较好的结构。但是在Go中就需要我们首先通过line将结果读出,自行实现算法。在这里我们就简单展示,将来再探索这部分的内容。

(1)流式调用

package mainimport ("bytes""encoding/json""fmt""log""net/http""os""time"
)// 请求结构体
type Message struct {Role    string `json:"role"`Content string `json:"content"`
}type ChatRequest struct {Model    string    `json:"model"`Messages []Message `json:"messages"`Stream   bool      `json:"stream"`
}func main() {baseURL := "https://api.ppinfra.com/openai"apiKey := "" // 替换为你的 API Keymodel := "deepseek/deepseek-r1-0528-qwen3-8b"// 设置请求体chatRequest := ChatRequest{Model: model,Messages: []Message{{Role:    "user",Content: "Hi there!",},},Stream: true, // 设置为 true 以启用流式输出}// 将请求结构体编码成 JSONreqBody, err := json.Marshal(chatRequest)if err != nil {log.Fatalf("Error marshalling request body: %v", err)}// 创建 HTTP 请求client := &http.Client{Timeout: 30 * time.Second,}req, err := http.NewRequest("POST", baseURL+"/v1/chat/completions", bytes.NewBuffer(reqBody))if err != nil {log.Fatalf("Error creating request: %v", err)}// 设置请求头req.Header.Set("Authorization", "Bearer "+apiKey)req.Header.Set("Content-Type", "application/json")// 发送请求resp, err := client.Do(req)if err != nil {log.Fatalf("Error sending request: %v", err)}defer resp.Body.Close()// 逐步读取流数据decoder := json.NewDecoder(resp.Body)// 临时存储部分内容var partialContent stringfor {// 解析一个数据块var chunk interface{} // 不指定类型,接收任意类型的内容// 读取一个完整的数据块,检查流中的数据if err := decoder.Decode(&chunk); err != nil {if err.Error() == "EOF" {// 如果到达流的末尾(EOF),则退出time.Sleep(1)break}log.Fatalf("Error decoding response chunk: %v", err)}// 打印每次接收到的 chunk 内容fmt.Printf("Received chunk: %v\n", chunk)// 如果接收到 [DONE],表示流结束if chunk == "[DONE]" {log.Println("Stream finished with [DONE] message.")break}// 直接将接收到的 chunk 转换为字符串并打印// 如果是 string 类型的数据块,则直接输出内容switch v := chunk.(type) {case string:fmt.Print(v) // 输出接收到的文本内容partialContent += vdefault:fmt.Printf("Received non-text chunk: %v\n", v)}}// 打印流结束后的内容fmt.Println("\nStream finished. Full response:", partialContent)// 将内容保存到指定的 JSON 文件err = saveToJSONFile("output.json", partialContent)if err != nil {log.Fatalf("Error saving to file: %v", err)}
}// 将内容保存到 JSON 文件
func saveToJSONFile(filename, content string) error {// 创建文件file, err := os.Create(filename)if err != nil {return fmt.Errorf("error creating file: %v", err)}defer file.Close()// 将内容作为 JSON 保存data := map[string]string{"response": content}encoder := json.NewEncoder(file)encoder.SetIndent("", "  ") // 设置格式化输出err = encoder.Encode(data)if err != nil {return fmt.Errorf("error encoding JSON to file: %v", err)}fmt.Println("Response saved to", filename)return nil
}

一般调用的话只需要将stream改为false即可。或者使用更加直白的方法:

package mainimport ("bytes""encoding/json""fmt""io""log""net/http""os""time"
)// 请求结构体
type Message struct {Role    string `json:"role"`Content string `json:"content"`
}type ChatRequest struct {Model    string    `json:"model"`Messages []Message `json:"messages"`Stream   bool      `json:"stream"`
}type ChatResponse struct {ID      string `json:"id"`Object  string `json:"object"`Created int64  `json:"created"`Model   string `json:"model"`Choices []struct {Index   int `json:"index"`Message struct {Role    string `json:"role"`Content string `json:"content"`} `json:"message"`FinishReason string `json:"finish_reason"`} `json:"choices"`
}func main() {baseURL := "https://api.ppinfra.com/openai"apiKey := "" // 替换为你的 API Keymodel := "deepseek/deepseek-r1-0528-qwen3-8b"// 设置请求体chatRequest := ChatRequest{Model: model,Messages: []Message{{Role:    "user",Content: "Hi there!",},},Stream: false, // 设置为 true 以启用流式输出}// 将请求结构体编码成 JSONreqBody, err := json.Marshal(chatRequest)if err != nil {log.Fatalf("Error marshalling request body: %v", err)}// 创建 HTTP 请求client := &http.Client{Timeout: 30 * time.Second,}req, err := http.NewRequest("POST", baseURL+"/v1/chat/completions", bytes.NewBuffer(reqBody))if err != nil {log.Fatalf("Error creating request: %v", err)}// 设置请求头req.Header.Set("Authorization", "Bearer "+apiKey)req.Header.Set("Content-Type", "application/json")// 发送请求resp, err := client.Do(req)if err != nil {log.Fatalf("Error sending request: %v", err)}defer resp.Body.Close()body, err := io.ReadAll(resp.Body)if err != nil {log.Fatalf("Error reading response body: %v", err)}fmt.Println(string(body))// 解析成结构体var chatResp ChatResponseif err := json.Unmarshal(body, &chatResp); err != nil {log.Fatalf("Error unmarshalling JSON: %v", err)}// 打印解析后的字段fmt.Println("Model:", chatResp.Model)fmt.Println("Content:", chatResp.Choices[0].Message.Content)saveToJSONFile("response.txt", chatResp.Choices[0].Message.Content)
}// 将内容保存到 JSON 文件
func saveToJSONFile(filename, content string) error {// 创建文件file, err := os.Create(filename)if err != nil {return fmt.Errorf("error creating file: %v", err)}defer file.Close()// 将内容作为 JSON 保存data := map[string]string{"response": content}encoder := json.NewEncoder(file)encoder.SetIndent("", "  ") // 设置格式化输出err = encoder.Encode(data)if err != nil {return fmt.Errorf("error encoding JSON to file: %v", err)}fmt.Println("Response saved to", filename)return nil
}

3. 轮询获得AI绘图的图片

同样使用的是PPIO为例子:

package mainimport ("bytes""encoding/json""fmt""io""log""net/http""os""time"
)type GraphRequest struct {Prompt                    string `json:"prompt"`Size                      string `json:"size"`Watermark                 bool   `json:"watermark"`SequentialImageGeneration string `json:"sequential_image_generation"`MaxImages                 int    `json:"max_images"`
}type Response struct {Images []string `json:"images"`
}func main() {baseURL := "https://api.ppinfra.com/v3/seedream-4.0"apiKey := "" // 替换为你的 API Key// 设置请求体graphRequest := GraphRequest{Prompt: `动漫风格,高清8K画质,细腻光影渲染,电影感构图。场景设定于极具设计感的现代办公楼长廊内,采用对称式走廊构图增强空间纵深感。【人物塑造】
主角少女:形象特征:茶色波波头短发搭配复古圆框眼镜,镜片反射微妙高光服装设计:简约白色衬衫配灰色百褶裙,外套材质选用低调的羊绒质感标志道具:手持靛青色传统油纸伞,伞面绘有浮世绘风格波纹图案动态表现:撑伞转身的瞬间,裙摆扬起优雅弧线,展现灵动气质叙述者视角:职业装扮:标准深色西装套装,手持电子记事本互动姿态:微微倾身聆听,表情带着好奇与思索【环境构建】
建筑空间:廊道设计:极简主义风格的宽阔走廊,抛光大理石地面呈现完美倒影窗外景致:左侧整面落地窗外是精心打理的和风庭院,包含枯山水、惊鹿与红枫光线设计:午后斜阳透过格栅形成几何光斑,与伞影构成明暗对比【细节刻画】伞骨末端悬挂迷你推理小说挂坠衬衫领口别着隐藏家纹的胸针地面倒影中隐约显现非常规的景物变形窗外庭院暗藏数字投影的仙鹤影像【色彩体系】
主色调:高级灰与米白为基底
强调色:伞面靛青与枫叶朱红形成视觉焦点
光效处理:柔光滤镜配合局部补光,营造神秘氛围整体画面平衡现代建筑与传统元素,通过油纸伞与办公环境的戏剧性碰撞,营造出“日常中的非凡”的独特质感,完美呈现角色神秘气质与场景叙事张力。`,Size:                      "2048x2048",Watermark:                 false,SequentialImageGeneration: "disabled", // 如果需要序列化生成MaxImages:                 1,          // 生成最大图像数}// 将请求结构体编码成 JSONreqBody, err := json.Marshal(graphRequest)if err != nil {log.Fatalf("Error marshalling request body: %v", err)}fmt.Println(string(reqBody))// 创建 HTTP 请求client := &http.Client{Timeout: 30 * time.Second,}req, err := http.NewRequest("POST", baseURL, bytes.NewBuffer(reqBody))if err != nil {log.Fatalf("Error creating request: %v", err)}// 设置请求头req.Header.Set("Authorization", "Bearer "+apiKey)req.Header.Set("Content-Type", "application/json")// 发送请求resp, err := client.Do(req)if err != nil {log.Fatalf("Error sending request: %v", err)}defer resp.Body.Close()body, _ := io.ReadAll(resp.Body)fmt.Println(resp)fmt.Println(string(body))// 创建结构体用于解析 JSON 数据var result Response// 解析 JSON 数据err = json.Unmarshal(body, &result)if err != nil {fmt.Println("解析 JSON 错误:", err)return}// 获取图片 URLimageURL := result.Images[0] // 假设返回只有一张图片// 下载图片respImage, err := http.Get(imageURL)if err != nil {fmt.Println("下载图片失败:", err)return}defer respImage.Body.Close()// 获取当前时间戳,并生成文件名currentTime := time.Now().Format("20060102150405") // 格式化为:年年年年月月日日时分秒fileName := fmt.Sprintf("image_%s.jpg", currentTime)// 创建文件用于保存图片outFile, err := os.Create(fileName)if err != nil {fmt.Println("创建文件失败:", err)return}defer outFile.Close()// 将图片写入文件_, err = io.Copy(outFile, respImage.Body)if err != nil {fmt.Println("保存图片失败:", err)return}fmt.Println("图片已成功保存为 image.jpg")
}

二、 与Redis

Redis相比DB来说要简单得多,数据结构并不复杂,操作花样也不多,我们就先从软柿子开始捏。首先我们使用Go连通Redis:

// 首先运行这段代码将Go需要的redis包安装好
go get github.com/go-redis/redis/v8//然后就可以使用了package mainimport ("context""fmt""log""time""github.com/go-redis/redis/v8"
)func main() {// 创建 Redis 客户端rdb := redis.NewClient(&redis.Options{Addr:     "localhost:6379", // Redis 服务器地址Password: "",               // 没有密码,默认空DB:       0,                // 默认数据库 0})// 使用 contextctx := context.Background()// 🔹 1. 推送一个新的键值对err := rdb.Set(ctx, "username", "GoRedisUser", 10*time.Minute).Err()if err != nil {log.Fatalf("设置键值对失败: %v", err)}fmt.Println("已成功推送键值对 -> username: GoRedisUser")// 🔹 2. 获取所有的键keys, err := rdb.Keys(ctx, "*").Result()if err != nil {log.Fatalf("获取键失败: %v", err)}// 🔹 3. 打印所有键值对fmt.Println("当前 Redis 中的所有键值对:")for _, key := range keys {val, err := rdb.Get(ctx, key).Result()if err != nil {if err == redis.Nil {continue}log.Printf("获取值失败:%v", err)continue}fmt.Printf("键: %s, 值: %s\n", key, val)}
}

测试连接的同时我们也看到了如何向其中推送和查找kv对数据。那其他的比如列表、数值、字符串、队列、栈等的数据结构也可以通过我们的rdb客户端实现。我们可以使用下面的代码去分别测试各种各样的数据结构的表现:

package mainimport ("context""fmt""log""github.com/go-redis/redis/v8"
)func main() {// 创建 Redis 客户端rdb := redis.NewClient(&redis.Options{Addr:     "localhost:6379", // Redis 服务器地址Password: "",               // 没有密码,默认空DB:       0,                // 默认数据库 0})// 使用 contextctx := context.Background()// --------- 1. 推送字符串(String)数据 ---------err := rdb.Set(ctx, "username", "GoRedisUser", 0).Err()if err != nil {log.Fatalf("设置键值对失败: %v", err)}fmt.Println("推送字符串数据 -> username: GoRedisUser")// --------- 2. 推送列表(List)数据 ---------rdb.RPush(ctx, "myList", "item1", "item2", "item3")fmt.Println("推送列表数据 -> myList: item1, item2, item3")// --------- 3. 推送集合(Set)数据 ---------rdb.SAdd(ctx, "mySet", "item1", "item2", "item3")fmt.Println("推送集合数据 -> mySet: item1, item2, item3")// --------- 4. 推送有序集合(Sorted Set)数据 ---------rdb.ZAdd(ctx, "mySortedSet",&redis.Z{Score: 1, Member: "item1"},&redis.Z{Score: 2, Member: "item2"},)fmt.Println("推送有序集合数据 -> mySortedSet: item1(1), item2(2)")// --------- 5. 推送哈希(Hash)数据 ---------rdb.HSet(ctx, "myHash", "field1", "value1", "field2", "value2")fmt.Println("推送哈希数据 -> myHash: field1=value1, field2=value2")// --------- 6. 推送队列(Queue)数据 ---------rdb.LPush(ctx, "myQueue", "task1", "task2", "task3")fmt.Println("推送队列数据 -> myQueue: task1, task2, task3")// --------- 7. 推送栈(Stack)数据 ---------rdb.LPush(ctx, "myStack", "top1", "top2", "top3")fmt.Println("推送栈数据 -> myStack: top1, top2, top3")// 获取并打印 Redis 中的数据:// --------- 1. 获取字符串(String)数据 ---------username, err := rdb.Get(ctx, "username").Result()if err != nil {log.Fatalf("获取字符串数据失败: %v", err)}fmt.Printf("获取字符串数据 -> username: %s\n", username)// --------- 2. 获取列表(List)数据 ---------list, err := rdb.LRange(ctx, "myList", 0, -1).Result()if err != nil {log.Fatalf("获取列表数据失败: %v", err)}fmt.Printf("获取列表数据 -> myList: %v\n", list)// --------- 3. 获取集合(Set)数据 ---------set, err := rdb.SMembers(ctx, "mySet").Result()if err != nil {log.Fatalf("获取集合数据失败: %v", err)}fmt.Printf("获取集合数据 -> mySet: %v\n", set)// --------- 4. 获取有序集合(Sorted Set)数据 ---------sortedSet, err := rdb.ZRange(ctx, "mySortedSet", 0, -1).Result()if err != nil {log.Fatalf("获取有序集合数据失败: %v", err)}fmt.Printf("获取有序集合数据 -> mySortedSet: %v\n", sortedSet)// --------- 5. 获取哈希(Hash)数据 ---------hash, err := rdb.HGetAll(ctx, "myHash").Result()if err != nil {log.Fatalf("获取哈希数据失败: %v", err)}fmt.Printf("获取哈希数据 -> myHash: %v\n", hash)// --------- 6. 获取队列(Queue)数据 ---------queue, err := rdb.LRange(ctx, "myQueue", 0, -1).Result()if err != nil {log.Fatalf("获取队列数据失败: %v", err)}fmt.Printf("获取队列数据 -> myQueue: %v\n", queue)// --------- 7. 获取栈(Stack)数据 ---------stack, err := rdb.LRange(ctx, "myStack", 0, -1).Result()if err != nil {log.Fatalf("获取栈数据失败: %v", err)}fmt.Printf("获取栈数据 -> myStack: %v\n", stack)
}

三、 与DB(各种各样的DB,这里以pgsql和mongodb为例)

这两个数据库比较有代表性,一个是关系型数据库,一个是非关系型数据库,囊括了主流的两个大方向的数据库操作。

1. pgsql

为什么不选择mysql呢?其实我是有一个比较单纯的想法。目前我接触到的凡是使用mysql的场景都是使用”上古时代“的版本,其中某些聚合函数无法使用,并且听说存在着性能瓶颈。其中如果需要存储json类型的数据或者二进制数据在一开始的版本中支持的并不是特别优秀,往往还是回到表单形式来存储。而pgsql比起mysql就更加灵活一点儿,正好使用一个比较新的版本来学习也能跟得上未来的时代。其对于json和二进制数据的支持就稍微好一些,性能上限也高一些,我还听说直接用pgsql存储知识图谱结构和向量数据的做法,想必它一定有它的道理(笑)。

// 同样在一开始的时候需要导入pgsql包go get github.com/jackc/pgx/v4// 接下来就可以是使用了package mainimport ("context""fmt""log""github.com/jackc/pgx/v4"
)func main() {// PostgreSQL 连接字符串connStr := "postgres://myuser:mypassword@localhost:5432/mydb"// 连接到 PostgreSQL 数据库conn, err := pgx.Connect(context.Background(), connStr)if err != nil {log.Fatalf("无法连接到数据库: %v", err)}defer conn.Close(context.Background())// 创建表createTableSQL := `CREATE TABLE IF NOT EXISTS test_table (id SERIAL PRIMARY KEY,name VARCHAR(50) NOT NULL);`_, err = conn.Exec(context.Background(), createTableSQL)if err != nil {log.Fatalf("创建表失败: %v", err)}fmt.Println("表 test_table 创建成功")// 查询所有的表名queryTablesSQL := `SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';`rows, err := conn.Query(context.Background(), queryTablesSQL)if err != nil {log.Fatalf("查询表名失败: %v", err)}defer rows.Close()fmt.Println("当前数据库中的表名:")for rows.Next() {var tableName stringif err := rows.Scan(&tableName); err != nil {log.Fatalf("扫描表名失败: %v", err)}fmt.Println(tableName)}// 删除表dropTableSQL := `DROP TABLE IF EXISTS test_table;`_, err = conn.Exec(context.Background(), dropTableSQL)if err != nil {log.Fatalf("删除表失败: %v", err)}fmt.Println("表 test_table 删除成功")
}

2. mongodb

这是非常典型的非关系型数据库,相信各位AI应用开发者都感受到结构化数据在AI领域中的不方便,如果想要设计一个健壮、智能的AI处理工作流,对于一些字段的开放性是非常必要的,比如某些字段我需要是灵活的字符串、数值、列表等数据,我会灵活地对传递的状态字典,各种各样的字典在不同地方进行不同的处理、不同的取舍。这一切对于结构化数据库都是非常折磨的开发,一旦接触了mongodb存储的json结构或者二进制数据后,就再也回不去了(笑)。

// 这里是需要安装的依赖
go get go.mongodb.org/mongo-driver/mongo
go get go.mongodb.org/mongo-driver/mongo/options
//然后就可以使用了package mainimport ("context""fmt""log""go.mongodb.org/mongo-driver/bson""go.mongodb.org/mongo-driver/mongo""go.mongodb.org/mongo-driver/mongo/options"
)func main() {// 连接 MongoDB 本地数据库clientOptions := options.Client().ApplyURI("mongodb://localhost:27017") // 默认本地 MongoDB 连接client, err := mongo.Connect(context.Background(), clientOptions)if err != nil {log.Fatalf("无法连接到 MongoDB: %v", err)}defer client.Disconnect(context.Background())// 测试连接err = client.Ping(context.Background(), nil)if err != nil {log.Fatalf("连接测试失败: %v", err)}fmt.Println("成功连接到 MongoDB!")// 选择数据库和集合collection := client.Database("testdb").Collection("testcollection")// 向集合中插入一条文档doc := bson.D{{Key: "name", Value: "John Doe"},{Key: "age", Value: 30},{Key: "city", Value: "New York"},}insertResult, err := collection.InsertOne(context.Background(), doc)if err != nil {log.Fatalf("插入文档失败: %v", err)}fmt.Printf("成功插入文档,ID: %v\n", insertResult.InsertedID)// 查询插入的文档var result bson.Merr = collection.FindOne(context.Background(), bson.D{{Key: "name", Value: "John Doe"}}).Decode(&result)if err != nil {log.Fatalf("查询文档失败: %v", err)}fmt.Printf("查询到文档: %v\n", result)// 删除该文档deleteResult, err := collection.DeleteOne(context.Background(), bson.D{{Key: "name", Value: "John Doe"}})if err != nil {log.Fatalf("删除文档失败: %v", err)}fmt.Printf("删除了 %v 条文档\n", deleteResult.DeletedCount)
}

四、与消息队列(这里仅以kafka为例)

常用的消息队列工具有kafka和rabbitmq。这里选择kafka的原因在于我了解到rabbitmq实际上更加简单一些,它更专注于确保消息的一定传递送达,而不是kafka这样需要面对极端并发的场景,也不会像kafka这样有极大的自定义空间来确定它的表现。于是乎如果我学会了kafka,那么rabbitmq上手也会很快。退一步来说,消息队列在我们的开发当中更多只是充当一个”功能“来使用,不需要太过在意它的性能,最基础的已经足够应对绝大多数的情况了。所以不如就从kafka学起吧~

//这里是安装的依赖
go get github.com/confluentinc/confluent-kafka-go/kafka
//然后就可以开始了
package mainimport ("fmt""log""github.com/confluentinc/confluent-kafka-go/kafka"
)func main() {// Kafka 配置broker := "localhost:9092" // Kafka broker 地址,假设本地运行topic := "test-topic"      // 主题groupID := "test-group"    // 消费者组// 生产者配置producerConfig := &kafka.ConfigMap{"bootstrap.servers": broker,}// 创建生产者producer, err := kafka.NewProducer(producerConfig)if err != nil {log.Fatalf("Failed to create producer: %v", err)}defer producer.Close()// 消费者配置consumerConfig := &kafka.ConfigMap{"bootstrap.servers": broker,"group.id":          groupID,"auto.offset.reset": "earliest", // 从最早的消息开始消费}// 创建消费者consumer, err := kafka.NewConsumer(consumerConfig)if err != nil {log.Fatalf("Failed to create consumer: %v", err)}defer consumer.Close()// 订阅主题err = consumer.Subscribe(topic, nil)if err != nil {log.Fatalf("Failed to subscribe to topic: %v", err)}// 生产消息message := "Hello Kafka!"err = producer.Produce(&kafka.Message{TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny},Value:          []byte(message),}, nil)if err != nil {log.Fatalf("Failed to produce message: %v", err)}fmt.Printf("Produced message: %s\n", message)// 消费消息msg, err := consumer.ReadMessage(-1) // 等待消息if err != nil {log.Fatalf("Failed to read message: %v", err)}fmt.Printf("Consumed message: %s\n", string(msg.Value))
}

到现在为止,我们已经将API接口、内存管理Redis、关系型和非关系型数据库、消息队列都成功跑通。创建一个Web应用的所有代码组件我们都掌握了,如果不关心接口文档的可视化和一些方便调试的工具的话,我们已经能够完成一个完整Web应用的后端服务开发了。这无疑是振奋人心的!

http://www.dtcms.com/a/512136.html

相关文章:

  • Go语言:一文学搞懂核心函数“make”
  • 什么网站是教做纸工的测量为什么要建站
  • 徐州专业做网站的提高自己网站
  • FFmpeg--FlvPaser源码解析
  • html+js 实现生活缴费页面模板
  • Linux小课堂: 定时与延时执行机制之date、at、sleep 与 crontab 的深度解析
  • Linux第二弹
  • 【VSCode中git管理工具】无法初始化仓库
  • 二手房网站建设自己学习建设网站
  • 网站模板找超速云建站自动化毕设题目网站开发
  • Web原生架构如何优化数据库权限管理:简化操作与增强安全性
  • HashMap扩容过程是什么?怎么解决哈希冲突?
  • OpenSSH 安全配置核心概念解析
  • TCL华星t8项目正式开工,总投资额约295亿元
  • 营销网站制作信ls15227微信网站建设公司首选
  • 新手指南:如何在悟空AI CRM中创建和管理客户
  • 网站建设來选宙斯站长网站建设运营合同范本
  • 新能源汽车的“隐形守护者”:深度解析车载充电机(OBC)的关键作用
  • AAIA:从 “普通审计” 到 “AI 专家” 的跃迁
  • 【系统分析师】核心考点:100个高频知识点汇总
  • 基于单片机的机房环境监测系统设计与实现
  • 做网站的每天打电话咋办深圳 微网站
  • 网站建设视屏电子商务网站开发工具
  • 2.常见软件测试分类的串联
  • Gemini CLI接入CloudBase-AI-Toolkit(MCP)保姆级教程
  • 阿里云代理商:阿里云CDN访问问题怎么诊断?
  • 关于yolov5 v2.0本地运行出现 的一些问题的解决
  • jsp做的网页是网站吗毕业设计拼车网站的建设雨实现
  • 不用ftp可以做网站吗html个人网页设计代码
  • AI 时代的数据通道:云消息队列 Kafka 的演进与实践