分布式

基本概念

水杉在线是一个分布式系统,事实上我们实验室的技术栈一直在努力的向当前主流的技术看齐,所以我们会考虑分布式,考虑云原生等等。说到分布式系统,有些概念不得不说。

对于分布式系统来说,最大的难点就是各个节点如何同步。CAP是这方面的基本定理。

CAP指的是:一致性(consistency)、可用性(Availability)和分区容错性(Partitiontolerance)。对于一个系统来说,不可能同时满足以下三点:

  • C:所有节点访问一份最新的数据副本
  • A:每次请求都能获取到非错的响应-但是不保证获取的数据是最新数据。
  • P:实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,Juin意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。

数据一致性模型

一些分布式系统通过复制数据来提高系统的可靠性和容错性,将数据的不同副本存放在不同的机器,但是维护数据副本的代价高,因此很多系统采用弱一致性:

  • 强一致性:无论更新操作在哪一个副本执行,之后所有的读操作都要获取到最新的数据。
  • 弱一致性:用户读到某一数据的更新需要一定时间,这段时间称为不一致性窗口。
  • 最终一致性:弱一致性的一种特例,保证用户最终能够读到数据的更新。

一致性解决方案:

  • 分布式事务:两段提交
  • 分布式锁
  • MQ消息持久化 重试 幂等
  • Paxos算法

服务可用性

可用性是指只要收到用户请求,服务器就必须给出回应。

高可用的解决方案:

  • 负载均衡
  • 降级:服务器压力剧增的时候,根据业务情况和流量对一些服务有策略的降级,释放资源保证核心任务的平稳运行。
  • 熔断:对于目标服务的请求和调用大量失败,熔断该服务的所有调用,并且对于后续调用直接返回,从而快速释放资源,确保目标服务在不可用的这段时间内,所有对他的调用都是立即返回的,不会阻塞。
  • 流量控制
  • 异地多活

分区容错性

大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。分区容错的意思是,区间通信可能失败。比如,一台服务器放在中国,另一台服务器放在美国,这就是两个区,它们之间可能无法通信。

分区容错无法避免,因此,P总是成立,那么C和A无法同时做到啊。

高并发

  • 系统拆分

    将一个系统拆分成多个子系统,然后每个系统连一个数据库。

  • 缓存

    大部分的高并发场景都是读多写少,数据库和缓存都存一份,然后大量走缓存。redis单机可以几万的并发。

  • MQ

    对于高频率写的情况,redis就无能为力了,这时候可以把大量的写请求存入MQ,后边系统消费之后再慢慢的写,控制在MySQL承载范围之内。所以可以考虑用MQ来异步写,提高并发性。

  • 分库分表

    分库分表,可能到了最后数据库层面还是免不了抗高并发的要求,好吧,那么就将一个数据库拆分为多个库,多个库来扛更高的并发;然后将一个表拆分为多个表,每个表的数据量保持少一点,提高 SQL 跑的性能。

  • 读写分离

    读写分离,这个就是说大部分时候数据库可能也是读多写少,没必要所有请求都集中在一个库上吧,可以搞个主从架构,主库写入,从库读取,搞一个读写分离。读流量太多的时候,还可以加更多的从库。

  • ES

    ES 是分布式的,可以随便扩容,分布式天然就可以支撑高并发,因为动不动就可以扩容加机器来扛更高的并发。那么一些比较简单的查询、统计类的操作,可以考虑用 ES 来承载,还有一些全文搜索类的操作,也可以考虑用 ES 来承载。

高并发下的流量控制

如果不进行流量控制,服务器会承受很大的压处理压力。请求量很高,服务器负载也很高,超过极限时系统崩溃,导致所有人不能访问。

为了实现服务高可用,可以对大流量请求进行限流,拦截掉大部分请求,只允许一部分请求可以真正进入服务器。

令牌桶漏桶计数器是常用的三种限流算法。

限流算法

  • 计数器

    qps是100,从第一个请求开始算,在接下来的1s,每次一个请求,计数器+1,到100之后就会拒绝所有的请求,然后在下一秒计数器恢复为0.

    算法有一个弊端,就是在1s的前10ms,已经通过了100个请求,后面的都会拒绝,这种现象称为突刺现象

  • 漏桶

    算法内部有个容器,请求进来时,不管流量有多大,都以匀速的方式请求,比如1ms10个。

    不管服务调用方多么不稳定,通过漏桶算法进行限流,每 10 毫秒处理一次请求。因为处理的速度是固定的,请求进来的速度是未知的,可能突然进来很多请求,没来得及处理的请求就先放在桶里,既然是个桶,肯定是有容量上限,如果桶满了,那么新进来的请求就丢弃。

    算法实现:队列保存请求,另外通过一个线程池定期从队列中获取请求并执行,可以一次获取多个并发执行。

    这种算法,在使用过后也存在弊端:无法应对短时间的突发流量,同时它的优点也是可以平滑网络上的突发流量,请求可以被整形成稳定的流量。

  • 令牌桶

    桶算法可以限制请求调用的速率,令牌算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。

    桶用来存放固定数量的令牌。以一定的速率往桶中放令牌。每次请求先获取令牌,拿到令牌才可以执行,否则等待可用的令牌,或者直接拒绝。

    桶中令牌上限则丢弃。所以存在这种情况,桶中一直有大量可用的令牌,请求可以拿令牌直接执行。比如设置 qps100 ,那么限流器初始化完成一秒后,桶中就已经有 100 个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的 100 个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

    算法实现:

    队列存放令牌,通过线程池定期生成令牌到队列中,每次来一个请求,就从队列中获取一个令牌,然后继续执行。

集群限流

  • Redis请求窗口

    采用redis的计时和计数方式,在规定的时间窗口,允许通过的最大请求量。

    每次有相关操作的时候,就向 redis 服务器发送一个 incr 命令,比如需要限制某个用户访问 /index 接口的次数,只需要拼接用户 id 和接口名生成 rediskey ,每次该用户访问此接口时,只需要对这个 key 执行 incr 命令,在这个 key 带上过期时间,就可以实现指定时间的访问频率。

  • Nginx限流

    Nginx按请求速率限速模块使用的是漏桶算法,即能够强行保证请求的实时处理速度不会超过设置的阈值。

    Nginx官方版本限制IP的连接和并发分别有两个模块:

    • limit_req_zone 用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 “leaky bucket”。
    • limit_req_conn 用来限制同一时间连接数,即并发限制。

短链系统

秒杀系统

分布式

分布式一致性与共识算法

分布式缓存

分布式事务

消息队列

RPC

关于认证授权

认证:认证身份凭证,通过凭证知道是你。

授权:认证之后,授权主要掌管着我们访问系统的权限。

cookie和session

简单来说,cookie存放在客户端,用来保存用户信息。

  1. 我们在 Cookie 中保存已经登录过的用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了。除此之外,Cookie 还能保存用户首选项,主题和其他设置信息。
  2. 使用Cookie 保存 session 或者 token ,向后端发送请求的时候带上 Cookie,这样后端就能取到session或者token了。这样就能记录用户当前的状态了,因为 HTTP 协议是无状态的。
  3. Cookie 还可以用来记录和分析用户行为。举个简单的例子你在网上购物的时候,因为HTTP协议是没有状态的,如果服务器想要获取你在某个页面的停留状态或者看了哪些商品,一种常用的实现方式就是将这些信息存放在Cookie

服务端使用cookie可以使用以下方法:

  • 设置cookie返回给客户顿

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    @GetMapping("/change-username")
    public String setCookie(HttpServletResponse response) {
    // 创建一个 cookie
    Cookie cookie = new Cookie("username", "Jovan");
    //设置 cookie过期时间
    cookie.setMaxAge(7 * 24 * 60 * 60); // expires in 7 days
    //添加到 response 中
    response.addCookie(cookie);

    return "Username is changed!";
    }
  • 使用spring框架的@CookieValue获取特定cookie值

    1
    2
    3
    4
    @GetMapping("/")
    public String readCookie(@CookieValue(value = "username", defaultValue = "Atta") String username) {
    return "Hey! My username is " + username;
    }
  • 读取所有cookie值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @GetMapping("/all-cookies")
    public String readAllCookies(HttpServletRequest request) {

    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
    return Arrays.stream(cookies)
    .map(c -> c.getName() + "=" + c.getValue()).collect(Collectors.joining(", "));
    }

    return "No cookies";
    }

session

主要作用是通过服务端记录用户状态。

Cookie存放在客户端,session存放在服务端,所以安全性更高,敏感信息一般不直接写在cookie,最好将cookie信息加密然后使用的时候去服务端解密。

session如何进行身份验证

通过sessionId来实现特定用户,ID一般存放在redis中,用户登陆系统,然后返回给客户端具有sessionId的cookie,请求时带上,这样就知道用户了。

需要注意:

  • 依赖session的业务需要确保客户端开启了cookie
  • session的过期时间。

没有cookie的话,session还能用吗

可以呀,可以重写请求串啊,在url里面带着sessionId。可行但是安全性和用户体验感降低。当然了,也可以加密后传到服务端进行解密。

token

什么是token

session有问题,比如他没法做分布式,需要保证session信息服务器的可用性、不适合移动端。

Token不需要再客户端存放,只需要校验。JWT就是这种实现方式。JWT本质上就是一种签名串,由于是有签名的,所以接收者可以验证他的真实性。

JWT三部分构成:

  • Header:描述JWT的元数据,定义了生成签名的算法和token类型。
  • Payload(负载):存放实际需要传递的数据。
  • Signature:服务器通过PayloadHeader和一个密钥(secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。

在基于 Token 进行身份验证的的应用程序中,服务器通过PayloadHeader和一个密钥(secret)创建令牌(Token)并将 Token 发送给客户端,客户端将 Token 保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP Header 的 Authorization字段中: Authorization: Bearer Token

为什么token可以防止CSRF攻击

CSRF:跨域请求伪造。

Session 认证中 Cookie 中的 SessionId是由浏览器发送到服务端的,借助这个特性,攻击者就可以通过让用户误点攻击链接,达到攻击效果。

但是,我们使用 token 的话就不会存在这个问题,在我们登录成功获得 token 之后,一般会选择存放在 local storage 中。然后我们在前端通过某些方式会给每个发到后端的请求加上这个 token,这样就不会出现 CSRF 漏洞的问题。因为,即使有个你点击了非法链接发送了请求到服务端,这个非法请求是不会携带 token 的,所以这个请求将是非法的。

需要注意的是不论是 Cookie 还是 token 都无法避免跨站脚本攻击(Cross Site Scripting)XSS。

Oauth2.0

这个之前总结过

sso

SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。举个例子我们在登陆了京东金融之后,我们同时也成功登陆京东的京东超市、京东家电等子系统。

sso和oauth2.0的区别。

OAuth 是一个行业的标准授权协议,主要用来授权第三方应用获取有限的权限。SSO解决的是一个公司的多个相关的自系统的之间的登陆问题比如京东旗下相关子系统京东金融、京东超市、京东家电等等。

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

请我喝杯咖啡吧~

支付宝
微信