Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

场景知识

限流

漏桶和令牌桶都是常用的限流算法,用于控制系统的流量,防止系统被过度访问而崩溃。总的来说,漏桶算法适用于需要稳定处理请求的场景,而令牌桶算法适用于需要应对瞬时流量激增的场景。

固定窗口算法

劣势:临界时间点产生突发流量,统计数量不准确。

滑动窗口算法

滑动窗口算法把间隔时间划分成更小的粒度,当更小粒度的时间间隔过去后,把过去的间隔请求数减掉,再补充一个空的时间间隔。当滑动窗口的格子划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越精确。但滑动窗口的时间间隔(小格子)多了,存储的空间也会增加。

漏桶

漏桶算法是一种固定容量的算法,类似于水桶,它可以以恒定的速率流出请求,无论输入速率有多快,最终输出速率都不会超过指定的限制。实现方式是,在服务端维护一个固定容量的队列(即漏桶),并以恒定速率处理请求。如果请求速率过快,请求将被加入到漏桶中,等待服务端处理。当漏桶已满时,新的请求将被丢弃,从而控制了流量。

令牌桶

原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。优点是可以应对瞬时流量激增的情况,因为它可以通过调整发放令牌的速率来控制请求的处理速度,但是实现相对漏桶算法更复杂一些。

使用Redis实现令牌桶算法:

  1. 首先判断当前令牌桶是否存在,可能是过期或刚启动
  2. 如果不存在就创建桶
  3. 存在就通过时间计算差计算当前剩余值减一放回,更新时间。
  4. 如果桶中剩余数量小于零,返回限流

鉴权JWT

JWT( JSON-WEB-TOKEN ) 是比较新的一种登录方式,他利用时间换空间的方式,服务端将用户的信息相关信息进行加密并返回到客户端,即签发了 一个”令牌”,在令牌的有效期内,客户端可以通过传递令牌的方式与服务端通信。

JWT 登录整体流程:

  • 用户输入用户名密码进行登录
  • 服务端验证用户名密码,成功后,将用户的相关信息(通常是 user_id)及一些附加信息通过 JWT 方式进行加密,并返回给客户端。
  • 客户端可以用任意方式储存服务器返回的 JWT ,之后只需在每次请求时,将 JWT 通过某种方式传递给后端。
  • 服务器收到请求后,获取并验证 JWT,从而获取用户的信息(通常是 user_id 及一些附加信息),即服务器不需要储存每个用户的状态(即 session), 只需要在每次请求时获取并解析 JWT,即可完成用户身份校验和用户基本信息的获取。

JWT默认的传递方式为:

1
2
3
"headers": {
'Authorization': 'Bearer ' + token // JWT 规定的的表示形式
}

特点

  1. JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次。不加密的情况下,不要将秘密数据写入 JWT。
  2. JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数。
  3. JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑。
  4. JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
  5. 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。
  6. 后端每次接口请求都需要进行 JWT 的加解密,计算压力增大。

refreshToken就是用来在accessToken过期以后来重新获取accessToken。

Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中。

Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。

cookie + session 是最传统的登录方式,利用浏览器默认行为,每次请求将登录后设置好的 cookie 发送给服务端, 服务端通过 cookie 中的信息( session_id),获取用户的登录信息。

整体流程如下:

  • 用户输入用户名密码进行登录
  • 服务端验证用户名密码,成功后,生成唯一的 session_id 储存起来(可以是内存、数据库等,通常使用 redis )。
  • 通过设置 set-cookie,将 session_id 返回给前端并储存在浏览器 cookie 中。
  • cookie 过期前,对该系统的每次请求都将会带上 cookie(浏览器默认行为),后端通过 cookie 中的信息,获取用户的 session_id 信息。 并在后端( redis )查询出对应用户的信息。

优点 :原理简单、实现方便

缺点:

  • 服务器端需维护大量 session_id,有一定负担。(目前通常将 session_id 放在 redis中,也解决了服务器集群下 session_id 同步问题)
  • 无法阻止跨站请求伪造CSRF 攻击。
  • 这种模式的问题在于,扩展性(scaling)不好。单机当然没有问题,如果是服务器集群,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session。

异步写库

实现异步写库可以帮助减轻数据库的压力,提高系统的吞吐量和响应速度。

为了解决这个问题,我把所有的更新、插入数据库的需求,放入一个独立的goroutine中,使用channel进行数据的异步传递,也避免了多个goroutine之间的竞争和锁的使用,使代码更加简洁易于维护。

数据一致性

采用先更新数据库再删除缓存的方案。

回写redis

先更新数据库再删除缓存,使用双检加锁机制锁住mysql,只让一个线程回写redis,完成数据一致性。

双检加锁

双检加锁就是在加锁后再次查询reids缓存,二次查询无数据才去查询mysql。

更新频繁的热点key

同一热点key保留2份,A有过期时间,B无

先查询A的,查询不到,则:

  1. 后端查询DB更新缓存
  2. 查询带后缀返回给调用方

延迟双删

先删除缓存在写数据库再延迟一段时间再次删除缓存。

假设A删除完redis缓存,然后更新mysql, 此时B读取数据,redis找不到,读mysql然后回写到redis中 A再次回写redis出现问题,因此在A第一次删除缓存时,延迟一段时间再次删除。

删除缓存失败

把要删除的缓存值或者要更新的数据库值暂存到消息队列中,从消息队列中读取这些值,然后再次进行删除或更新。

mysql有改动,立即同步redis

MySQL binlog增量订阅消费+消息队列+增量数据更新到redis读Redis,一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。

评论