go-zero 實戰(2)

go-zero 實戰(1) 中,使用了go-zero 創建了order 和 user 兩個微服務。而order作為grpc的客戶端,user 作為grpc的服務端,打通了 order 到 user的調用。接下來,我們在user中,加入mysql組件。確保數據能夠寫到數據庫。

引入MySQL

1. 啟動mysql,創建數據庫 zero-mall

可以使用 DBeaver 工具,連接mysql,并創建zero-mall數據庫。
在這里插入圖片描述
并且執行如下腳本創建表:

use zero_mall;create table `user`(id bigint(0) not null auto_increment,name varchar(255) character set utf8mb4 COLLATE utf8mb4_general_ci not null,gender varchar(255) character set utf8mb4 COLLATE utf8mb4_general_ci not null,PRIMARY key (id) using btree
);

2. 在 user/internal 目錄下創建 model目錄,并創建user.sql

創建 user.sql 文件,并將上面腳本放入 user.sql 文件中。然后,在當前目錄下,執行:

goctl model mysql ddl -src user.sql -dir . -c

這步操作,會生成操作數據庫相關的代碼。 由于生成的代碼比較亂。我們在做數據庫連接的時候,會摘取部分代碼,按照自己的思路做數據庫相關操作。

在當前目錄下新建 user.go (user/internal/model/user.go)文件,把當前生成的 usermodel_gen.go 文件中的 User 結構體拿出來,放到user.go 文件中,并添加一個 TableName函數。然后,把生成的 usermodel.go、usermodel_gen.go、vars.go文件刪除。最終的 user.go 代碼如下:

package modeltype User struct {Id     int64  `db:"id"`Name   string `db:"name"`Gender string `db:"gender"`
}
// 返回表名
func (User) TableName() string {return "user"
}

當我們創建了model之后,就有User 實體,該實體映射數據庫的User表。接下來的我們需要創建數據庫的連接。

3. 在user下創建database目錄

創建database目錄,在該目錄下創建sqlx.go文件,主要為了使用 go-zero的orm框架。 當然,這個目錄下也可以創建如 mongo、redis的連接。也可以使用不同的orm框架,如gorm等。

sqlx.go 文件

package databaseimport "github.com/zeromicro/go-zero/core/stores/sqlx"// we use go-zero sqlxtype DBConn struct {Conn sqlx.SqlConn
}func Connect(datasource string) *DBConn {return &DBConn{Conn: sqlx.NewMysql(datasource),}
}

4. 創建操作數據的接口,并提供實現

在 user/internal/ 下創建 repo 目錄,并創建 user.go 文件

package repoimport ("context""user/internal/model"
)type UserRepo interface {Save(ctx context.Context, user *model.User) error
}

該代碼提供了一個Save接口,用來保存 User。

在 user/internal/ 下創建 dao 目錄,并創建 user.go 文件,提供 接口的實現。

package daoimport ("context""fmt""user/database""user/internal/model"
)type UserDao struct {*database.DBConn
}func NewUserDao(conn *database.DBConn) *UserDao {return &UserDao{conn,}
}func (d *UserDao) Save(ctx context.Context, user *model.User) error {sql := fmt.Sprintf("insert into %s (name, gender) values(?, ?)", user.TableName())result, err := d.Conn.ExecCtx(ctx, sql, user.Name, user.Gender)if err != nil {return err}id, err := result.LastInsertId()if err != nil {return err}user.Id = idreturn nil}

5. 修改 user/internal/config目錄下的 config.go文件

由于,我們需要連接 mysql 數據庫。因此,我們需要從配置文件中讀取 mysql 連接的配置。go-zero 提供了一種簡便方式,可以自動讀取配置。

首先,修改 user/etc/user.yaml中的配置, 如下:

Name: user.rpc
ListenOn: 0.0.0.0:8080
Etcd:Hosts:- 127.0.0.1:2379Key: user.rpcMysql:Datasource: root:thinker@tcp(127.0.0.1:33306)/zero_mall?charset=utf8mb4&parseTime=True&loc=Asia%2FShanghai

Mysql 的配置是我自己手動添加的。

user/internal/config/config.go 文件如下:

package configimport "github.com/zeromicro/go-zero/zrpc"type Config struct {zrpc.RpcServerConfMysql MysqlConfig
}type MysqlConfig struct {DataSource string
}

該文件中添加了 MySqlConfig 結構體,并且在Config 結構體中添加了 Mysql 變量。這樣 go-zero 可以自動讀取到 user.yaml 中 Mysql連接配置。

6. 修改 user/rpc/user.proto 文件,并重新生成代碼

user.proto 文件

option go_package = "./user";message IdRequest {string id = 1;
}message UserRequest {string id = 1;string name = 2;string gender = 3;
}message UserResponse {string id = 1;string name = 2;string gender = 3;
}service User {rpc getUser(IdRequest) returns(UserResponse);rpc save(UserRequest) returns(UserResponse);
}

該代碼中,添加了 rpc save(UserRequest) returns(UserResponse); 接口。并使用如下命令重新生成代碼:

goctl rpc protoc user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=.

7. 修改userserver.go 和 getuserlogic.go 代碼

將生成的 types/user 和 userclient/下的代碼,覆蓋之前生成的代碼。并且把 internal/server/userserver.go 文件中的 如下代碼(新生成的代碼):

func (s *UserServer) Save(ctx context.Context, in *user.UserRequest) (*user.UserResponse, error) {l := logic.NewUserLogic(ctx, s.svcCtx)return l.SaveUser(in)
}

放到 user/internal/server/userserver.go(舊文件中) 文件中。

修改 user/internal/logic/getuserlogic.go代碼,為了命名規范,我將getuserlogic.go 該成了 userlogic.go。

package logicimport ("context""strconv""user/internal/model""user/internal/svc""user/types/user""github.com/zeromicro/go-zero/core/logx"
)type UserLogic struct {ctx    context.ContextsvcCtx *svc.ServiceContextlogx.Logger
}func NewUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLogic {return &UserLogic{ctx:    ctx,svcCtx: svcCtx,Logger: logx.WithContext(ctx),}
}func (l *UserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) {// todo: add your logic here and delete this linereturn &user.UserResponse{Id:     in.GetId(),Name:   "hello user name",Gender: "man",}, nil
}func (l *UserLogic) SaveUser(in *user.UserRequest) (*user.UserResponse, error) {data := &model.User{Name:   in.Name,Gender: in.Gender,}err := l.svcCtx.UserRepo.Save(context.Background(), data)if err != nil {return nil, err}return &user.UserResponse{Id:     strconv.FormatInt(data.Id, 10),Name:   data.Name,Gender: data.Gender,}, nil
}

userlogic 相當于業務組件,這里實現了用戶保存到數據庫的邏輯。

到此,在user服務中連接mysql數據庫,并實現通過rpc接口調用將用戶數據保存到 mysql 邏輯已經完成。

8. 調用 rpc 接口,測試 user 保存到數據庫

1. 在mall 目錄下執行如下命令,創建 userapi微服務(為了測試user rpc 保存到數據的功能):

goctl api new userapi

2. 在 userapi 目錄下,創建一個 go.mod 文件,文件內容如下:

module userapigo 1.22.2

3. 在 mall 目錄下執行如下命令,將 userapi 加入workspace中

go work use userapi/
cd userapi/
go mod tidy

到此,生成的代碼結構如下:
在這里插入圖片描述
在這里插入圖片描述
生成的包名稍微有點問題,建議直接手動修改一下。將user/api 改為 userapi

4. 修改 userapi/etc/user-api.yaml 文件如下:

Name: userapi-api
Host: 0.0.0.0
Port: 8888
UserRpc:Etcd:Hosts:- 127.0.0.1:2379Key: user.rpc

該文件中增加了 UserRpc 配置,主要是為了調用rpc接口。

5. 修改 userapi/internal/config/config.go 文件

package configimport ("github.com/zeromicro/go-zero/rest""github.com/zeromicro/go-zero/zrpc"
)type Config struct {rest.RestConfUserRpc zrpc.RpcClientConf
}

增加了 UserRpc 變量,為了讀取 user-api.yaml 中的配置。

6. 修改userapi/internal/handler/routers.go文件

在 userapi/internal/handler 目錄下創建 register.go 文件 和 userhandler.go文件

register.go

package handlerimport ("github.com/zeromicro/go-zero/rest/httpx""net/http""userapi/internal/logic""userapi/internal/types"
)func (u *UserHandler) register(w http.ResponseWriter, r *http.Request) {var req types.Requestif err := httpx.ParseJsonBody(r, &req); err != nil {httpx.ErrorCtx(r.Context(), w, err)return}l := logic.NewUserLogic(r.Context(), u.svcCtx)resp, err := l.Register(&req)if err != nil {httpx.ErrorCtx(r.Context(), w, err)} else {httpx.OkJsonCtx(r.Context(), w, resp)}
}

userhandler.go

package handlerimport ("userapi/internal/svc"
)type UserHandler struct {svcCtx *svc.ServiceContext
}func NewUserHandler(svcCtx *svc.ServiceContext) *UserHandler {return &UserHandler{svcCtx: svcCtx,}}

刪除 自動生成的代碼 userapihandler.go 文件。

將生成的 userapi/internal/handler/routers.go 文件修改如下:

// Code generated by goctl. DO NOT EDIT.
package handlerimport ("net/http""userapi/internal/svc""github.com/zeromicro/go-zero/rest"
)func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {handler := NewUserHandler(serverCtx)server.AddRoutes([]rest.Route{{Method:  http.MethodPost,Path:    "/register",Handler: handler.register,},},)
}

7. 修改 userapi/internal/types.go 文件

// Code generated by goctl. DO NOT EDIT.
package typestype Request struct {Name string `json:"name"`Gender string `json:"gender"`
}type Response struct {Message string `json:"message"`Data any `json:"data"`
}

這里主要是 為了處理 http請求過來的 json數據。

8. 為了使用 Rpc 服務,修改 userapi/internal/svc/servicecontext.go 文件

servicecontext.go 文件

package svcimport ("github.com/zeromicro/go-zero/zrpc""user/userclient""userapi/internal/config"
)type ServiceContext struct {Config  config.ConfigUserRpc userclient.User
}func NewServiceContext(c config.Config) *ServiceContext {return &ServiceContext{Config:  c,UserRpc: userclient.NewUser(zrpc.MustNewClient(c.UserRpc)),}
}

這里加入了 UserRpc 變量,為了遠程調用User服務提供Save方法。

9.修改業務代碼 userapi/internal/logic/userapilogic.go

package logicimport ("context""time""user/types/user""userapi/internal/svc""userapi/internal/types""github.com/zeromicro/go-zero/core/logx"
)type UserLogic struct {logx.Loggerctx    context.ContextsvcCtx *svc.ServiceContext
}func NewUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserLogic {return &UserLogic{Logger: logx.WithContext(ctx),ctx:    ctx,svcCtx: svcCtx,}
}func (l *UserLogic) Register(req *types.Request) (resp *types.Response, err error) {// todo: add your logic here and delete this linectx, cancelFunc := context.WithTimeout(context.Background(), 5*time.Second)defer cancelFunc()userResponse, err := l.svcCtx.UserRpc.Save(ctx, &user.UserRequest{Name:   req.Name,Gender: req.Gender,})if err != nil {return nil, err}return &types.Response{Message: "success",Data:    userResponse,}, nil
}

9 測試

  1. 啟動 user 服務
  2. 啟動 userapi 服務
  3. 用 postman測試,并查看數據庫
    在這里插入圖片描述
    測試成功。

10. 重構代碼。

由于在 userapi中,用到user中的代碼。并且之前的 order中也直接引用了user中的代碼。這樣增加了耦合性。我們可以把這部分公共的代碼拿出來,這樣以后。即使user服務發生變動,只要公共部分不變。那么userapi和order服務就不會受到影響。

1. 創建公共目錄

mkdir rpc-common
cd rpc-common

2. 創建 go.mod 文件

在 mall/rpc-common下創建 go.mod文件

module rpc-commongo 1.22.2

3. 將 rpc-common 加入 workspacke

在 mall 目錄下,執行命令

go work use rpc-common

4. 重新調整一下 order、user、userapi中包的引用

5. 測試

在這里插入圖片描述
截圖顯示,測試成功。代碼調整成功。

11. 整理代碼之后的 git 地址

github 當前整理后代碼,放在了 mysql 分支下。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/18241.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/18241.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/18241.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

我說同事咋找工作命中率這么高,原來是學習了這些招式

最近有兩個同事離職了,其中一個還是專科,他倆一個是前端開發,一個是python開發,兩個人都接近35歲了。我們還勸告他們,不要離職,要騎驢找馬。但了解后,他倆非常有信心的說:不怕&#…

富格林:遵守可信準則安全交易

富格林指出,當下的金融市場,投資者大多都會更傾向于盈利效率高的理財產品,而近年來興起的現貨黃金,正合投資者的心意。不過,投資現貨黃金若是不遵循其中的可信準則,是難以實現安全盈利的。那么有哪些可信準…

3D視覺技術|螺栓分揀測試

隨著制造業自動化程度的不斷提高,某大型汽配企業為提升生產效率、減少人力成本,提出了使用復合機器人完成螺栓分揀的需求。富唯智能通過采用復合機器人,結合3D工業相機和高性能控制器,實現螺栓的自動抓取,從而提升生產…

鴻蒙OS開發:【一次開發,多端部署】(一多天氣)項目

一多天氣 介紹 本示例展示一個天氣應用界面,包括首頁、城市管理、添加城市、更新時間彈窗,體現一次開發,多端部署的能力。 1.本示例參考一次開發,多端部署的指導,主要使用響應式布局的柵格斷點系統實現在不同尺寸窗…

【Qt 學習筆記】Qt窗口 | 工具欄 | QToolBar的使用及說明

博客主頁:Duck Bro 博客主頁系列專欄:Qt 專欄關注博主,后期持續更新系列文章如果有錯誤感謝請大家批評指出,及時修改感謝大家點贊👍收藏?評論? Qt窗口 | 工具欄 | QToolBar的使用及說明 文章編號:Qt 學習…

怎么看智慧城市的發展?

智慧城市,就像一個擁有高度智慧和感知能力的未來城市居民,正在不斷地學習、適應和進化。它通過無數的眼睛(傳感器)和耳朵(數據收集設備)來觀察和傾聽城市的脈動,通過強大的大腦(數據…

opencv文檔py_contours示例整理

文章目錄 目錄說明contours_begin目標什么是輪廓?如何畫等高線?輪廓逼近法contour_features目標1.Moments 時刻2. Contour Area 輪廓面積3. Contour Perimeter 輪廓周長4. Contour Approximation 輪廓近似5. Convex Hull 凸包6. Checking Convexity 檢查凸性7. Bounding Rect…

B2118 驗證子串

驗證子串 題目描述 輸入兩個字符串,驗證其中一個串是否為另一個串的子串。 輸入格式 兩行,每行一個字符串。 輸出格式 若第一個串 s 1 s_1 s1? 是第二個串 s 2 s_2 s2? 的子串,則輸出(s1) is substring of (s2); 否則&…

Python并發與異步編程

Python的并發與異步編程是兩個不同的概念,但它們經常一起使用,以提高程序的性能和響應能力。以下是對這兩個概念的詳細講解: 并發編程 (Concurrency) 并發編程是指在程序中同時執行多個任務的能力。Python提供了幾種實現并發的機制&#xff…

嵌入式進階——RTC時鐘

🎬 秋野醬:《個人主頁》 🔥 個人專欄:《Java專欄》《Python專欄》 ??心若有所向往,何懼道阻且長 文章目錄 RTC時鐘原理圖PCF8563寄存器控制與狀態寄存器 設備地址I2C環境初始化RTC寄存器數據讀取RTC寄存器數據寫入RTC鬧鐘設置RTC定時器設置…

2024.5.28晚訓題解

提前預告&#xff0c;市賽初中組會考算法題&#xff0c;應該會有兩道模板題 比如DFS BFS 二分 簡單動態規劃&#xff0c;雖然我們沒學多久&#xff0c;但是模板題你還是要會寫的 A題 編輯距離 動態規劃 注意多組輸入 #include<iostream> using namespace std; int dp[1…

9、C#【進階】特性

特性 文章目錄 1、特性概念2、自定義特性 Attribute3、特性的使用4、限制自定義特性的使用范圍5、系統自帶特性1、過時特性2、調用者信息特性3、條件編譯特性4、外部dll包函數特性 1、特性概念 特性是一種允許我們向程序的程序集添加元數據的語言結構 它是用于保存程序機構信息…

【機器學習300問】103、簡單的經典卷積神經網絡結構設計成什么樣?以LeNet-5為例說明。

一個簡單的經典CNN網絡結構由&#xff1a;輸入層、卷積層、池化層、全連接層和輸出層&#xff0c;這五種神經網絡層結構組成。它最最經典的實例是LeNet-5&#xff0c;它最早被設計用于手寫數字識別任務&#xff0c;包含兩個卷積層、兩個池化層、幾個全連接層&#xff0c;以及最…

ansible批量漏洞升級openssh版本

1、ansible宿主機準備好環境&#xff0c;并寫好hosts文件 [rootoxidized ansible]# cat hosts [all] 10.10.200.33 10.10.200.34 10.10.200.35跑playbook之前記得提前發送秘鑰 ssh-copy-id 10.10.200.33/34/352、下載好安裝包&#xff0c;然后編寫yml [rootoxidized ansible]…

【實用的 IDEA 配置和操作技巧總結】

前置知識 IDEA的設置快捷鍵為ctrlalts鍵&#xff0c;后文介紹IDEA常見的配置就不再贅述這一點了。 基礎配置 取消默認打開上次項目 日常開發都會打開不同的項目&#xff0c;初次安裝IDEA之后&#xff0c;每次打開IDEA都會開啟上一次啟動的項目&#xff0c;所以我們需要進入設…

0基礎學習Mybatis系列數據庫操作框架——Mysql的Geometry數據處理之WKB方案

大綱 序列化反序列化完整TypeHandlerSQL XML完整XML Mapper測試代碼代碼 在《0基礎學習Mybatis系列數據庫操作框架——Mysql的Geometry數據處理之WKT方案》中&#xff0c;我們介紹WTK方案的優點&#xff0c;也感受到它的繁瑣和缺陷。比如&#xff1a; 需要借助ST_GeomFromText…

element+ 引入圖標報錯 Failed to resolve import “@element-plus/icons-vue“ from “

element 引入圖標報錯 Internal server error: Failed to resolve import “element-plus/icons-vue” from “src\components\TimeLine.vue”. Does the file exist? 原因&#xff1a;element-plus需要單獨引入 icons 文檔 pnpm install element-plus/icons-vue之后就可以…

350種類型、10W+量級的API,企業應該怎么管?

忽如一夜春風來&#xff0c;萬物皆可API。 在互聯網時代&#xff0c;API無處不在&#xff1a;企業對外開放的數據、服務和業務能力&#xff0c;以API的形式提供給合作方&#xff1b;企業內部應用與應用、App與App之間的通信&#xff0c;通過API進行&#xff1b;甚至應用內部的…

php 連接sqlserver步驟

1.首先要確定使用的是sqlserver的哪個版本&#xff0c;比如sqlserver2012 2.確定服務器是64位還是32位的 3.確認一下使用php的哪個版本&#xff0c;比如php7.1 SQL Server 的 Microsoft PHP 驅動程序 Microsoft Drivers for PHP 支持矩陣 - PHP drivers for SQL Server | Mi…

Flutter 中的 CupertinoTabView 小部件:全面指南

Flutter 中的 CupertinoTabView 小部件&#xff1a;全面指南 在 Flutter 中&#xff0c;CupertinoTabView 是 Cupertino 組件庫中的一個 widget&#xff0c;它用于創建 iOS 風格的標簽頁視圖。這個 widget 通常與 CupertinoTabScaffold 結合使用&#xff0c;提供了一個底部帶有…