工程开发概念汇总(二) | 青训营笔记

170 阅读7分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第5篇笔记.

高并发专题

透过现象看本质

应对高并发其实是个复杂的系统性的任务,需要考虑的因素很多。我们将以一种自上而下的思考方式来梳理一下什么是高并发、高并发的分类、如何应对等,并在梳理的过程中,进行一些相关知识点的串联。

通产意义上讲,我们每天上网(比如刷抖音、购物、看新闻等)其实是一次次的网络访问。简单来讲,网络访问由网络请求(Request)和网络响应(Response)。

经典面试题:浏览器中输入www.baidu.com,展示了网页,都经历了什么?

  • 在浏览器地址栏中输入网址

  • 浏览器获取这个网址之后,会先去缓存中看看有没有要访问的资源,从浏览器缓存-系统缓存-路由缓存中查看,如果有就不再进行http请求,直接从缓存中加载资源。

  • 浏览器拿到域名自动去向DNS(域名系统)服务器发起请求,查询用户输入的域名对应的ip地址。

    • 这一步很多幺蛾子:
    • DNS优化 1.DNS缓存 DNS存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。 2.DNS负载均衡 DNS可以返回一个合适的机器的IP给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,DNS可以返回一个合适的机器的IP给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,
  • 浏览器拿到ip地址后,通过Ip地址和端口号和服务器建立tcp连接(三次握手)

  • 建立连接成功之后,浏览器开始向服务器发起http请求,并通过http协议将请求信息包装成请求报文(包含请求行、请求头、空行、请求体),然后通过socket发送到服务器。

    • 注:url与uri的区别:

    • uri:统一资源标志符(Uniform Resource Identifier, URI),指示每一个资源,由三部分组成:资源的命名机制、存放资源的主机名、资源自身的名称

    • url:URL是URI的一个子集。它是Uniform Resource Locator的缩写,译为“统一资源定位符”。格式:protocol :// hostname[:port] / path / ;parameters#fragment

    • url格式由三部分组成:

      ①第一部分是协议(或称为服务方式)。

      ②第二部分是存有该资源的主机IP地址(有时也包括端口号)。

      ③第三部分是主机资源的具体地址,如目录和文件名等。

    • 两者区别:URI和URL都定义了资源是什么,但URL还定义了该如何访问资源。URL是一种具体的URI,它是URI的一个子集,它不仅唯一标识资源,而且还提供了定位该资源的信息。URI 是一种语义上的抽象概念,可以是绝对的,也可以是相对的,而URL则必须提供足够的信息来定位,是绝对的。

  • 后端处理完后发送报文给浏览器

  • 浏览器按照HTTP协议将报文解析出来

  • 浏览器拿到响应报文中响应体的数据开始渲染html、css,执行JS

  • 如果在解析过程中(从上到下)中,发现有外链的标签(link、css、img),浏览器会自动对该标签的路径地址发起新的请求,同上。

什么是高并发?

定性:单位时间内,非常密集的网络请求。

定量:并发到底有多密集,一般用QPS来表示。

结果:如果没有好的应对措施或者架构建设,常见的结果是:用户发出网络请求,但网络响应延迟严重,甚至得不到网络响应。

应对高并发的难点在于,高并发来到的时候,高可用无法保证。

极端的例子:恶意的高并发:DDOS(Distributed Denial of Service)攻击。找很多ip抢占流量、耗费服务器资源。

高并发的分类

网络请求分两种:读请求、写请求

对应的高并发场景也可分两种:高并发读场景、高并发写场景(往往伴随有高并发读)。

应对和讨论高并发场景的时候,我们首先要分清我们是应对高并发读场景还是应对高并发写场景。

高并发读

现象:单位时间内,读请求的流量突然变大了;可能会导致服务响应变慢,甚至无法提供服务。

通常的应对思路

应对高并发读,通常的思路有分布式和缓存。

其他思路,还有类似限流、降级、熔断这种偏防御型策略。

分布式

关键词:More

思路:靠量取胜,一台不行就一百台,一百台不行就一千台。

分布式是一种思想或者架构。支持高并发的同时,带来高可用性。

常见名词:分布式系统、分布式集群、分布式计算、分布式存储、分布式锁、分布式事务。

分布式的存在,解决问题的同时,也极大地带来了服务端技术的复杂性。

分布式相关问题:

1.负载均衡:

a.常见的负载均衡算法:轮询(Round Robin)、随机、加权随机、最小连接数等。

b.动态的负载均衡算法:一致性哈希

2.分布式协议算法:Paxos、Raft、ZAB、Gossip

3.CAP理论、BASE理论

注:分布式和集群的区别

  • 集群:很多人干一个活,所有人干一样的事
  • 分布式:很多人干一个大活,每个人负责一部分小活

缓存

关键词:Better

思路:如果用铁锹不行,那用挖土机呢?如果DB撑不住,用缓存呢?

缓存的使用,利用了计算机存储介质访问速度的金字塔原理。

缓存也可以看作是一种思想,让更快的缓存来提供访问的结果。

缓存的使用在各个维度都有,到处都是。例如Nginx、浏览器、CDN、MySQL、操作系统等。

常见的缓存,内存(Local Cache)、分布式缓存(Redis Memcached)

缓存的使用,也会带来相关的复杂性。比如:

  • 缓存的容量相对不大,需要提高缓存的命中率,让高频的热的数据存在于缓存中。

    • 缓存的预加载和过期策略;
    • 缓存的替换算法:LRU、LFU等;
  • 缓存或者多级缓存的使用,当有数据写操作时,会涉及到数据如何同步。

    • 缓存一致性问题
  • 缓存失效情况的考虑和处理

    • 缓存穿透:访问一个缓存和DB都不存在的key

      • 接口鉴权:调之前先看你有没有权利调

      • 缓存空值:缓存你攻击的key,value为空值

      • 布隆过滤器:判断不存在的,则一定不存在;判断存在的,大概率存在;比HashMap节省空间==》

        • 用三个哈希函数算三个值,输入的key如果没有重合则一定不存在,如果重合则可能存在(因为可能组合不同)
    • 缓存击穿:热key过期,导致大量请求打到DB上=》热key不过期,关注更新策略

    • 缓存雪崩:大量key或者热key同时或者密集过期,导致系统压力骤增,引起雪崩==》过期时间打散

  • 缓存数据结构的选择、关注大KEY(会影响redis的响应速度)

以下是防御型措施:

限流

关键词:Limit

思路:既然处理不了那么多量,那就排个队~

限流通常是用消息队列来实现的,几种常见的限流算法:漏桶、令牌桶

漏桶算法

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

(连桶都放不下的请求就直接拒绝)

特点:没有流量红峰,而且对于突发流量一点变法都无,无法一定程度上应对突然增加的流量。

令牌桶算法

在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择等待可用的令牌、或者直接拒绝。

令牌桶算法,除了能限制数据的平均传输速率外,还能允许某种程度的突发流量。==》之前有一段空闲累积了很多令牌的话,下一次请求可以拿所有空闲令牌。

降级、熔断

降级:在有限的资源情况下,为了能抗住大量的请求,就需要对系统的分支功能做出一些牺牲,有点“弃卒保帅”的意思。放弃一些功能,保证整个系统能平稳运行。

熔断:系统中,由于某些原因使得服务出现了过载现象,为了防止造成整个系统故障,从而采用一种保护措施,暂时“熔断”对下游的访问。所以很多地方把熔断亦称为过载保护,熔断一般还可以自动检测修复。

降级和熔断在严格意义上说不是应对高并发的,而是尽量保证在高并发下的高可用程度和万一出现问题的恢复速度。

高并发写

大部分高并发写场景,也会同时伴随高并发读,并且可能读的并发量比写请求的并发量还大。

高并发写需要注意的问题

  • 幂等问题(其任意多次执行所产生的影响均与一次执行的影响相同)

  • 数据一致性问题

    • 写覆盖或写乱序
    • 写与读之间的延迟
    • 原子性(或者全部执行、或者全部不执行)

通常的应对思路

对于幂等问题,一般的思路是使用分布式锁(有一个共有的地方能够控制,只有一份有效);

对于数据一致性问题

  • 写覆盖或写乱序:使用锁
  • 写与读之间的延迟:保证最终一致性
  • 原子性:使用分布式事务