您的当前位置:首页正文

go-zero 实战(3)

2024-11-28 来源:个人技术集锦

引入 Redis

在之前的 user 微服务中引入 redis。

1. 修改 user/internal/config/config.go

package config

import (
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/zrpc"
)

type Config struct {
	zrpc.RpcServerConf
	Mysql      MysqlConfig
	CacheRedis cache.CacheConf // redis config -- 加入这一行代码
}

type MysqlConfig struct {
	DataSource string
}

2. 修改 user/etc/user.yaml 文件

加入 redis的配置

CacheRedis:
  - Host: 127.0.0.1:6379
    Pass: thinker
    Type: node

3. 修改 user/database/sqlx.go 文件

修改这里,是为了在查询数据时候,利用redis做缓存。

import (
	"github.com/zeromicro/go-zero/core/stores/cache"
	"github.com/zeromicro/go-zero/core/stores/sqlc"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

// we use go-zero sqlx

type DBConn struct {
	Conn      sqlx.SqlConn    // mysql
	ConnCache sqlc.CachedConn // redis
}

func Connect(datasource string, conf cache.CacheConf) *DBConn {
	sqlConn := sqlx.NewMysql(datasource)
	d := &DBConn{
		Conn: sqlConn,
	}
	if conf != nil {
		cachedConn := sqlc.NewConn(sqlConn, conf)
		d.ConnCache = cachedConn
	}
	return d
}

4. 修改 /user/internal/svc/servicecontext.go 文件

package svc

import (
	"user/database"
	"user/internal/config"
	"user/internal/dao"
	"user/internal/repo"
)

type ServiceContext struct {
	Config   config.Config
	UserRepo repo.UserRepo
}

func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config:   c,
		UserRepo: dao.NewUserDao(database.Connect(c.Mysql.DataSource, c.CacheRedis)),  // 增加了 redis的缓冲配置
	}
}

5. 在 user/internal/repo/user.go 中增加接口

package repo

import (
	"context"
	"user/internal/model"
)

type UserRepo interface {
	Save(ctx context.Context, user *model.User) error
	FindById(ctx context.Context, id int64) (user *model.User, err error)  //新增
}

6. 在 user/internal/dao/user.go 中实现接口

func (d *UserDao) FindById(ctx context.Context, id int64) (user *model.User, err error) {

	user = &model.User{}
	querySql := fmt.Sprintf("select * from %s where id = ?", user.TableName())
	userIdKey := fmt.Sprintf("%s%d", cacheUserIdPrefix, id)
	err = d.ConnCache.QueryRowCtx(ctx, user, userIdKey,
		func(ctx context.Context, conn sqlx.SqlConn, v any) error {
			return conn.QueryRowCtx(ctx, v, querySql, id)
		})
	return
}

7. 在 user/logic/userlogic.go 中实现 GetUser rpc

func (l *UserLogic) GetUser(in *user.IdRequest) (*user.UserResponse, error) {
	// todo: add your logic here and delete this line

	id, err := strconv.ParseInt(in.Id, 10, 64)
	if err != nil {
		return nil, err
	}
	u, err := l.svcCtx.UserRepo.FindById(context.Background(), id)
	if err != nil {
		return nil, err
	}
	return &user.UserResponse{
		Id:     in.GetId(),
		Name:   u.Name,
		Gender: u.Gender,
	}, nil
}

User 微服务rpc接口增加缓存完成。

测试

1. 修改 userapi/internal/handler/routes.go 文件

增加如下路由代码

{
	Method: http.MethodGet,
	Path: "/user/get/:id",
	Handler: handler.GetUser,
},

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

增加一个 IdRequest 结构体,主要是为了处理上一步中的请求

type IdRequest struct {
	Id string `json:"name" path:"id"`
}

3. 修改 userapi/internal/handler/userhandler.go 文件,实现GetUser接口

文件中增加如下代码:

func (u *UserHandler) GetUser(w http.ResponseWriter, r *http.Request) {

	var req types.IdRequest
	if err := httpx.ParsePath(r, &req); err != nil {
		httpx.ErrorCtx(r.Context(), w, err)
		return
	}

	l := logic.NewUserLogic(r.Context(), u.svcCtx)
	resp, err := l.GetUser(&req)
	if err != nil {
		httpx.ErrorCtx(r.Context(), w, err)
	} else {
		httpx.OkJsonCtx(r.Context(), w, resp)
	}
}

4. 修改 userapi/internal/logic/userapilogic.go 文件

文件中增加 GetUser 方法

func (l *UserLogic) GetUser(t *types.IdRequest) (resp *types.Response, err error) {
	userResponse, err := l.svcCtx.UserRpc.GetUser(context.Background(), &user.IdRequest{
		Id: t.Id,
	})
	if err != nil {
		return nil, err
	}
	resp = &types.Response{
		Message: "success",
		Data:    userResponse,
	}
	return
}

该方法主要调用了 user微服务的 RPC GetUser接口 服务

5. 启动微服务开始测试

go-zero 中的 JWT 使用

jwt 在目前登录、鉴权等场景中引用非常广泛
jwt 是加密的字符串,需要一个密钥,并且可以通过设置过期时间来使 jwt 生成的 token 失效。

由于我们的 userapi 微服务是对外提供接口的,因此,我们在 userapi 中使用 jwt。

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

添加 Auth 结构体

package config

import (
	"github.com/zeromicro/go-zero/rest"
	"github.com/zeromicro/go-zero/zrpc"
)

type Config struct {
	rest.RestConf
	UserRpc zrpc.RpcClientConf
	Auth    struct {
		AccessSecret string
		AccessExpire int64
	}
}

2. 修改 userapi/etc/userapi-api.yaml 文件

在文件中,增加如下配置。

Auth:
  AccessSecret: "sdskewie@129120$%120&*!"
  AccessExpire: 604800

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

func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {

	handler := NewUserHandler(serverCtx)
	server.AddRoutes(
		[]rest.Route{
			{
				Method:  http.MethodPost,
				Path:    "/Register",
				Handler: handler.Register,
			},
			{
				Method:  http.MethodPost,
				Path:    "/Login",
				Handler: handler.Login,
			},
		},
	)

	server.AddRoutes(
		[]rest.Route{
			{
				Method: http.MethodGet,
				Path: "/user/get/:id",
				Handler: handler.GetUser,
			},
		},
		rest.WithJwt(serverCtx.Config.Auth.AccessSecret),   // need jwt
	)
}

需要 jwt 认证的接口,就需要 rest.WithJwt(serverCtx.Config.Auth.AccessSecret), 这行go代码 。

4. 修改 userapi/internal/handler/userhandler.go 文件 ,实现 Login 接口

在该 文件 中,添加 Login 方法

func (u *UserHandler) Login(w http.ResponseWriter, r *http.Request) {
	var req types.LoginRequest
	if err := httpx.ParseJsonBody(r, &req); err != nil {
		httpx.ErrorCtx(r.Context(), w, err)
		return
	}

	l := logic.NewUserLogic(r.Context(), u.svcCtx)
	resp, err := l.Login(&req)
	if err != nil {
		httpx.ErrorCtx(r.Context(), w, err)
	} else {
		httpx.OkJsonCtx(r.Context(), w, resp)
	}
}

5. 修改 userapi/internal/login/userapilogin.go 文件

在该文件中,添加 login 方法,登录成功后生成 jwt

func (l *UserLogic) getToken(secretKey string, iat, seconds int64, userId int) (string, error) {

	claims := make(jwt.MapClaims)
	claims["exp"] = iat + seconds
	claims["iat"] = iat
	claims["userId"] = userId
	token := jwt.New(jwt.SigningMethodES256)
	token.Claims = claims
	return token.SignedString([]byte(secretKey))
}

func (l *UserLogic) Login(t *types.LoginRequest) (string, error) {

	userId := 100
	auth := l.svcCtx.Config.Auth
	return l.getToken(auth.AccessSecret, time.Now().Unix(), auth.AccessExpire, userId)
}

6. 启动 useapi微服务 测试

  1. 加入 jwt 后,再次测试 user/get/1接口,返回401
  2. 先登录,拿到 jwt
  3. 使用 jwt 访问 user/get/1 接口

显示全文