使用Redis缓存加速后端响应时间(1)

此篇博客介绍如何用Redis加速前后端分离的项目, JWT认证中间件的方法

1.没有Redis缓存之前的认证方法

目前常用的认证方式中, 使用JWT做认证的网站很多, 这种认证方法的流程

  1. 用户拿token调用api
  2. 后端使用jwt库的解密函数解开token, 拿到用户ID, 过期时间
  3. 如果过期, 直接返回
  4. 拿着用户ID去数据库查询用户是否存在
  5. 把查询到的用户信息作为一个变量赋值给请求上下文给后面的路由函数使用

看看代码流程:

	if claims, ok := token.Claims.(jwt.MapClaims); ok {
		//拿到token过期时间
		expire := claims["exp"].(string)
		expTime, _ := time.Parse(time.RFC3339, expire)

		//如果token过期, 直接返回
		if time.Now().Unix() > expTime.Unix() {
			return user, code.UnauthorizedTokenTimeout
		}

		// 之前的写法, 每次去数据库里查这个用户是不是存在
		if err := service.Query(&user, "id", claims["uid"].(string), []string{"Group"}, false, false); err != nil {
			return user, code.ServerError
		}

		if user.State == code.Disabled {
			return user, code.Forbidden
		}
                return user, nil

这种认证方法的缺点, 就是每一次需要鉴权的api请求都需要经过数据库, 高并发时, 性能不佳


2.使用Redis加速

现在我们使用redis来加速这个函数, 让我们的后端避免每一次请求都经过mysql, 大概的流程

  1. 用户拿token调用api
  2. 后端使用jwt库的解密函数解开token, 拿到用户ID, 过期时间
  3. 如果过期, 直接返回
  4. 拿着用户ID去redis查缓存是否存在
  5. 存在, 把redis里的数据转化成结构体, 赋值给请求上下文
  6. 不存在, 开始查询数据库
  7. 数据库不存在, 直接返回错误
  8. 数据库存在, 把查询过来的用户信息,转化成字符串, 存到redis中, 然后把用户信息赋值给请求上下文

可以看到, 我们这里加速的点, 主要就是把用户信息存到redis中, 这样避免了每次请求都经过mysql

但是这里要注意的是, 缓存失效的问题, 比如你修改或删除了用户, 那么redis里的缓存必须删除

当修改用户信息时, 必须开启数据库事务, 要保证数据库提交和redis删除都完成, 我们再提交事务,保证数据一致性

看看代码流程

	//开启事务
	tx := service.CreateTransaction()

	//修改用户数据
	user.State = patchJson.State
	user.Group = *group
	if service.TranactionSave(tx, user); err != nil {
		app.NewResponse(c).ToErrorResponse(code.ServerError)
		return
	}

	//删除redis里的key, 如果异常, 事务回滚
	if err = service.DeleteKey(`user:` + strconv.Itoa(user.ID) + ``); err != nil {
		tx.Rollback()
		app.NewResponse(c).ToErrorResponse(code.ServerError)
		return
	}

	//redis和数据库都修改完成, 提交事务
	tx.Commit()

然后用apifox做测试,可以看到我们的平均接口延迟从412ms优化到了335ms, 提升还是很不错的

这里还有一个小坑, 就是缓存击穿的问题, 看我们代码流程, 如果有人一直使用一个不存在的用户请求, 我们还是会每次调用数据库查询, 这里就留给大家思考一下怎么写吧~


评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注