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.mzph.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…

嵌入式进阶——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…

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…

怎么做好客户信息管理?

根据Forrester的调查表示&#xff0c;客户满意度的影响可能会使某些行业的收入每年增加高达 10 亿美元。而提升客户满意度的关键环节便是做好客户信息管理。但企业在进行客户信息管理中往往会遇到以下问题&#xff1a; 客户信息乱&#xff1a;客户信息存在各个 Excel表格、个人…

PMP报考条件怎么查询?如何判定自己是否符合条件?

PMP报考条件在PMI官网上就可以查询&#xff0c;PMP报考条件只需要符合项目管理培训经历和项目管理经验两个方面的要求即可&#xff0c;大家可以对照下方的规定判断自己是否符合PMP报名条件 PMP报考条件 以下是PMI&#xff08;中国&#xff09;官网对于PMP报名条件的规定&…

ubuntu strace命令

strace 是 Linux 系统中的一个调试工具&#xff0c;用于跟踪并记录系统调用&#xff08;system calls&#xff09;和信号&#xff08;signals&#xff09;。在 Ubuntu 中&#xff0c;strace 命令可以帮助开发者和系统管理员了解一个程序在运行时如何与操作系统内核进行交互&…

vue+antd实践:在输入框光标处插入内容

今天来看一个很简单的需求。 需求描述&#xff1a;在输入框光标处&#xff0c;插入指定的内容。 效果如下&#xff1a; 实现思路&#xff1a;刚开始还在想怎么获取光标的位置&#xff0c;但是发现所做的项目是基于vue3antd组件&#xff0c;那么不简单了嘛&#xff0c;只要调…

配置物联网平台 保姆级教程

一、云平台配置&#xff08;我们这里使用阿里云&#xff09; 1、注册和登录 &#xff08;1&#xff09;找到云平台官网&#xff0c;点击右上角的注册登录&#xff0c;完成之后&#xff0c;进行实名认证&#xff0c;任选一种认证方式。 ​​​​​​​ 2、实例的开通和创建 …

Scala环境的搭建

要搭建Scala&#xff0c;我们必须先下载java&#xff0c;由于我的电脑已经搭建好了环境&#xff0c;因此我这里用截图来教大家搭建环境。 可以从网上搜索安装包对其进行安装 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE 不建议下载最新版的&#xff0c;大家下载的版本可以下…