微服务相关知识
微服务架构总结
go+protobuf+grpc+consul简单的服务发现模型
GO-zero doc
网关创建
- 创建网关目录go-zero-demo\api\user
- 创建网关文件user.api
syntax = "v2"
info(
author: "tutu"
date: "2022-08-23"
desc: "用户服务"
)
type (
// 登录
LoginReq {
Username string `json:"username"`
Password string `json:"password"`
Code int `json:"code"`
}
LoginRes {
Uid int64 `json:"uid"`
Nickname string `json:"nickname"`
Sexy int `json:"sexy"`
Address string `json:"address"`
}
// 获取用户信息
UserInfoReq {
Uid int64 `form:"uid"`
}
UserInfoRes {
Uid int64 `json:"uid"`
Nickname string `json:"nickname"`
Sexy int `json:"sexy"`
Address string `json:"address"`
Email string `json:"email"`
LastRuntime string `json:"last_runtime"`
LastRunIp string `json:"last_run_ip"`
Balance float32 `json:"balance"`
}
)
service user {
@doc "登录"
@handler login
post /user/login (LoginReq) returns (LoginRes)
@doc "获取用户信息"
@handler getUserInfo
get /user/userinfo (UserInfoReq) returns (UserInfoRes)
}
- 生成文件
goctl api go -api ./user.api -dir ./ --style=goZero
- 生成markdown文档
goctl api doc --dir ./
rpc创建和使用
- 创建rpc文件路径:go-zero-demo\rpc\user\user.proto
- 编写proto文件内容
syntax = "proto3";
package user;
option go_package = "./user_rpc";
// 通用消息返回
message Echo {
int32 code = 1;
}
message LoginReq {
string username = 1;
string password = 2;
}
message Userinfo {
int64 uid = 1;
string username = 2;
string password = 3;
string nickname = 4;
int32 gender = 5;
string address = 6;
string email = 7;
string last_runtime = 8;
string last_run_ip = 9;
float balance = 10;
}
message IdReq {
int64 uid = 1;
}
message ListReq {
int32 page = 1;
int32 page_size = 2;
}
message ListRes {
repeated int32 Userinfo = 1;
}
service User {
rpc creatAccount(Userinfo) returns(Echo);
rpc updateAccount(Userinfo) returns(Echo);
rpc deleteAccount(IdReq) returns(Echo);
rpc getAccount(LoginReq) returns(Userinfo);
rpc listAccount(ListReq) returns(ListRes);
}
- 生成文件
goctl rpc protoc .\user.proto --go_out=./types --go-grpc_out=./types --zrpc_out=.
- 连接使用Rpc,在go-zero-demo\api\user\etc\user.yaml配置中添加服务发现地址和对应的key
RpcCli:
Etcd:
Hosts:
- 127.0.0.1:2379
Key: user.rpc
- api配置中添加rpc服务的客户端go-zero-demo\api\user\internal\config\config.go
RpcCli zrpc.RpcClientConf
- api service 添加Rpc调用
package svc
import (
"github.com/zeromicro/go-zero/zrpc"
"go-zero-demo/api/user/internal/config"
// 使用proto
userRpc "go-zero-demo/rpc/user/user"
)
type ServiceContext struct {
Config config.Config
// 对应rpc 包连接
UserRpc userRpc.User
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
// 获取客户端
UserRpc: userRpc.NewUser(zrpc.MustNewClient(c.RpcCli)),
}
}
- api 中Rpc调用方法示例
func (l *LoginLogic) Login(req *types.LoginReq) (resp *types.LoginRes, err error) {
if req.Code < 0{
err = errors.New("验证码错误")
return
}
userinfo, err := l.svcCtx.UserRpc.GetAccount(l.ctx,&user.LoginReq{
Username: req.Username,
Password: req.Password,
})
return &types.LoginRes{
Uid: userinfo.Uid,
Nickname: userinfo.Nickname,
Gender: int(userinfo.Gender),
Address: userinfo.Address,
},err
}
数据库的使用
自动生成代码及使用
- 通过datasource连接生成model文件
// 不缓存
goctl model mysql datasource -url="root:123456@tcp(127.0.0.1:3306)/tutu" -table="user" -dir="./user"
// 需要缓存
goctl model mysql datasource -url="root:123456@tcp(127.0.0.1:3306)/tutu" -table="user" -dir="./user" -cache=true
重复执行生成代码时,文件 userModel.go 和 vars.go 不会修改,自定义的代码可以写在这两个文件个文件中
- 在rpc配置及配置文件中添加相关配置项
################## 文件 go-zero-demo\rpc\user\etc\user.yaml 中添加 #######################
DB:
DataSource: root:123456@tcp(127.0.0.1:3306)/tutu?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai
Cache:
Host: 127.0.0.1:49153
Pass: redispw
################################################################
################## 文件 go-zero-demo\rpc\user\internal\config\config.go 中添加 #######################
DB struct{
DataSource string
}
Cache cache.CacheConf
################################################################
- 在服务依赖里面创建数据库资源句柄 go-zero-demo\rpc\user\internal\svc\servicecontext.go
- package svc
import (
"github.com/zeromicro/go-zero/core/stores/sqlx"
"go-zero-demo/rpc/user/internal/config"
// 引入model
userModel "go-zero-demo/model/user"
)
type ServiceContext struct {
Config config.Config
UserModel userModel.UserModel
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
// 创建资源
UserModel: userModel.NewUserModel(sqlx.NewMysql(c.DB.DataSource),c.Cache),
}
}
- 调用model示例
func (l *GetAccountLogic) Test(in *user_rpc.LoginReq) (*user_rpc.Userinfo, error) {
l.svcCtx.UserModel.Insert()
}
自定义model
- go-zero-demo\model\user\userModel.go文件修改
package user
import (
"context"
"github.com/Masterminds/squirrel"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ UserModel = (*customUserModel)(nil)
type (
// UserModel is an interface to be customized, add more methods here,
// and implement the added methods in customUserModel.
UserModel interface {
userModel
// 定义接口方法
Trans(ctx context.Context, fn func(context context.Context, session sqlx.Session) error) error
RowBuilder() squirrel.SelectBuilder
CountBuilder(field string) squirrel.SelectBuilder
SumBuilder(field string) squirrel.SelectBuilder
FindOneByQuery(ctx context.Context, rowBuilder squirrel.SelectBuilder) (*User, error)
}
customUserModel struct {
*defaultUserModel
}
)
// NewUserModel returns a model for the database table.
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel {
return &customUserModel{
defaultUserModel: newUserModel(conn, c),
}
}
func (m *defaultUserModel) FindOneByQuery(ctx context.Context, rowBuilder squirrel.SelectBuilder) (*User, error) {
query, values, err := rowBuilder.ToSql()
if err != nil {
return nil, err
}
var resp User
// 不进行数据缓存
err = m.QueryRowNoCacheCtx(ctx, &resp, query, values...)
return &resp,err
}
// export logic
func (m *defaultUserModel) Trans(ctx context.Context, fn func(ctx context.Context, session sqlx.Session) error) error {
return m.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
return fn(ctx, session)
})
}
// export logic
func (m *defaultUserModel) RowBuilder() squirrel.SelectBuilder {
return squirrel.Select(userRows).From(m.table)
}
// export logic
func (m *defaultUserModel) CountBuilder(field string) squirrel.SelectBuilder {
return squirrel.Select("COUNT(" + field + ")").From(m.table)
}
// export logic
func (m *defaultUserModel) SumBuilder(field string) squirrel.SelectBuilder {
return squirrel.Select("IFNULL(SUM(" + field + "),0)").From(m.table)
}
- 在go-zero-demo\rpc\user\internal\logic\getaccountlogic.go 中调用model
func (l *GetAccountLogic) Login(in *user_rpc.LoginReq) (*user_rpc.Userinfo, error) {
// 创建sql
rowBuilder := l.svcCtx.UserModel.RowBuilder().Where("username =?",in.Username)
one, err := l.svcCtx.UserModel.FindOneByQuery(l.ctx,rowBuilder)
if err !=nil{
return &user_rpc.Userinfo{},err
}
if one.Password.String != util.Md5ByString(in.Password) {
err = errors.New("password fail")
return &user_rpc.Userinfo{},err
}
return &user_rpc.Userinfo{
Uid: one.Uid,
Username: one.Username,
Nickname: one.Nickname,
Gender: int32(one.Gender),
Address: one.Address.String,
Email: one.Email.String,
}, nil
}
路由分组
@server(
group: user
prefix: user_api
)
检查路由go-zero-demo\api\user\internal\handler\routes.go下生成的代码就会增加路由分组
rest.WithPrefix(“/user_api”),