From 0d34f6ec144f8207382711ccd42a417f6f8d6bf9 Mon Sep 17 00:00:00 2001 From: CyC2018 <1029579233@qq.com> Date: Tue, 7 Aug 2018 17:52:24 +0800 Subject: [PATCH] auto commit --- notes/Leetcode 题解.md | 8 +- notes/Linux.md | 9 +- notes/MySQL.md | 2 +- notes/消息队列.md | 76 ++++++++++++++++ notes/系统设计基础.md | 100 ++++++++++++++++++++ notes/集群.md | 202 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 391 insertions(+), 6 deletions(-) diff --git a/notes/Leetcode 题解.md b/notes/Leetcode 题解.md index 293f4328..2813ce5d 100644 --- a/notes/Leetcode 题解.md +++ b/notes/Leetcode 题解.md @@ -2371,7 +2371,9 @@ public List diffWaysToCompute(String input) { 题目描述:有 N 阶楼梯,每次可以上一阶或者两阶,求有多少种上楼梯的方法。 -定义一个数组 dp 存储上楼梯的方法数(为了方便讨论,数组下标从 1 开始),dp[i] 表示走到第 i 个楼梯的方法数目。第 i 个楼梯可以从第 i-1 和 i-2 个楼梯再走一步到达,走到第 i 个楼梯的方法数为走到第 i-1 和第 i-2 个楼梯的方法数之和。 +定义一个数组 dp 存储上楼梯的方法数(为了方便讨论,数组下标从 1 开始),dp[i] 表示走到第 i 个楼梯的方法数目。 + +第 i 个楼梯可以从第 i-1 和 i-2 个楼梯再走一步到达,走到第 i 个楼梯的方法数为走到第 i-1 和第 i-2 个楼梯的方法数之和。

@@ -2400,7 +2402,9 @@ public int climbStairs(int n) { 题目描述:抢劫一排住户,但是不能抢邻近的住户,求最大抢劫量。 -定义 dp 数组用来存储最大的抢劫量,其中 dp[i] 表示抢到第 i 个住户时的最大抢劫量。由于不能抢劫邻近住户,因此如果抢劫了第 i 个住户那么只能抢劫 i - 2 或者 i - 3 的住户,所以 +定义 dp 数组用来存储最大的抢劫量,其中 dp[i] 表示抢到第 i 个住户时的最大抢劫量。 + +由于不能抢劫邻近住户,因此如果抢劫了第 i 个住户那么只能抢劫 i - 2 或者 i - 3 的住户,所以

diff --git a/notes/Linux.md b/notes/Linux.md index ebcc347b..14d3b317 100644 --- a/notes/Linux.md +++ b/notes/Linux.md @@ -275,8 +275,6 @@ BIOS 不可以读取 GPT 分区表,而 UEFI 可以。 ## 组成 -

- 最主要的几个组成部分如下: - inode:一个文件占用一个 inode,记录文件的属性,同时记录此文件的内容所在的 block 编号; @@ -287,6 +285,9 @@ BIOS 不可以读取 GPT 分区表,而 UEFI 可以。 - superblock:记录文件系统的整体信息,包括 inode 和 block 的总量、使用量、剩余量,以及文件系统的格式与相关信息等; - block bitmap:记录 block 是否被使用的位域; +

+ + ## 文件读取 对于 Ext2 文件系统,当要读取一个文件的内容时,先在 inode 中去查找文件内容所在的所有 block,然后把所有 block 的内容读出来。 @@ -336,7 +337,9 @@ inode 中记录了文件内容所在的 block 编号,但是每个 block 非常 ## 目录 -建立一个目录时,会分配一个 inode 与至少一个 block。block 记录的内容是目录下所有文件的 inode 编号以及文件名。可以看出文件的 inode 本身不记录文件名,文件名记录在目录中,因此新增文件、删除文件、更改文件名这些操作与目录的 w 权限有关。 +建立一个目录时,会分配一个 inode 与至少一个 block。block 记录的内容是目录下所有文件的 inode 编号以及文件名。 + +可以看出文件的 inode 本身不记录文件名,文件名记录在目录中,因此新增文件、删除文件、更改文件名这些操作与目录的 w 权限有关。 ## 日志 diff --git a/notes/MySQL.md b/notes/MySQL.md index 1c575fae..690425db 100644 --- a/notes/MySQL.md +++ b/notes/MySQL.md @@ -139,7 +139,7 @@ B+ Tree 是基于 B Tree 和叶子节点顺序访问指针进行实现,它具 ### 2. 操作 -操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。 +进行查找操作时,首先在根节点进行二分查找,找到一个 key 所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出 key 所对应的 data。 插入删除操作记录会破坏平衡树的平衡性,因此在插入删除时,需要对树进行一个分裂、合并、旋转等操作。 diff --git a/notes/消息队列.md b/notes/消息队列.md index 649f563a..74155802 100644 --- a/notes/消息队列.md +++ b/notes/消息队列.md @@ -1,4 +1,80 @@ +* [一、消息模型](#一消息模型) + * [点对点](#点对点) + * [发布/订阅](#发布订阅) +* [二、使用场景](#二使用场景) + * [异步处理](#异步处理) + * [流量削锋](#流量削锋) + * [应用解耦](#应用解耦) +* [三、可靠性](#三可靠性) + * [发送端的可靠性](#发送端的可靠性) + * [接收端的可靠性](#接收端的可靠性) +# 一、消息模型 + +## 点对点 + +消息生产者向消息队列中发送了一个消息之后,只能被一个消费者消费一次。 + +

+ +## 发布/订阅 + +消息生产者向频道发送一个消息之后,多个消费者可以从该频道订阅到这条消息并消费。 + +

+ +发布与订阅模式和观察者模式有以下不同: + +- 观察者模式中,观察者和主题都知道对方的存在;而在发布与订阅模式中,发布者与订阅者不知道对方的存在,它们之间通过频道进行通信。 +- 观察者模式是同步的,当事件触发时,主题会去调用观察者的方法,然后等待方法返回;而发布与订阅模式是异步的,发布者向频道发送一个消息之后,就不需要关心订阅者何时去订阅这个消息。 + +

+ +参考: + +- [Observer vs Pub-Sub](http://developers-club.com/posts/270339/) +- [消息队列中点对点与发布订阅区别](https://blog.csdn.net/lizhitao/article/details/47723105) + +# 二、使用场景 + +## 异步处理 + +发送者将消息发送给消息队列之后,不需要同步等待消息接收者处理完毕,而是立即返回进行其它操作。消息接收者从消息队列中订阅消息之后异步处理。 + +例如在注册流程中通常需要发送验证邮件来确保注册用户的身份合法,可以使用消息队列使发送验证邮件的操作异步处理,用户在填写完注册信息之后就可以完成注册,而将发送验证邮件这一消息发送到消息队列中。 + +只有在业务流程允许异步处理的情况下才能这么做,例如上面的注册流程中,如果要求用户对验证邮件进行点击之后才能完成注册的话,就不能再使用消息队列。 + +## 流量削锋 + +在高并发的场景下,如果短时间有大量的请求到达会压垮服务器。 + +可以将请求发送到消息队列中,服务器按照其处理能力从消息队列中订阅消息进行处理。 + +## 应用解耦 + +如果模块之间不直接进行调用,模块之间耦合度就会很低,那么修改一个模块或者新增一个模块对其它模块的影响会很小,从而实现可扩展性。 + +通过使用消息队列,一个模块只需要向消息队列中发送消息,其它模块可以选择性地从消息队列中订阅消息从而完成调用。 + +# 三、可靠性 + +## 发送端的可靠性 + +发送端完成操作后一定能将消息成功发送到消息队列中。 + +实现方法: + +- 在本地数据库建一张消息表,将消息数据与业务数据保存在同一数据库实例里,这样就可以利用本地数据库的事务机制。事务提交成功后,将消息表中的消息转移到消息队列中,若转移消息成功则删除消息表中的数据,否则继续重传。 + +## 接收端的可靠性 + +接收端能够从消息中间件成功消费一次消息。 + +实现方法: + +- 保证接收端处理消息的业务逻辑具有幂等性:只要具有幂等性,那么消费多少次消息,最后处理的结果都是一样的。 +- 保证消息具有唯一编号,并使用一张日志表来记录已经消费的消息编号。 diff --git a/notes/系统设计基础.md b/notes/系统设计基础.md index 649f563a..40f0ca9d 100644 --- a/notes/系统设计基础.md +++ b/notes/系统设计基础.md @@ -1,4 +1,104 @@ +* [一、性能](#一性能) +* [二、伸缩性](#二伸缩性) +* [三、扩展性](#三扩展性) +* [四、可用性](#四可用性) +* [五、安全性](#五安全性) +# 一、性能 + +## 性能指标 + +### 1. 响应时间 + +指从某个请求从发出到接收到响应消耗的时间。 + +在对响应时间进行测试时,通常采用重复请求方式,然后计算平均响应时间。 + +### 2. 吞吐量 + +指系统在单位时间内可以处理的请求数量,通常使用每秒的请求数来衡量。 + +### 3. 并发用户数 + +指系统能同时处理的并发用户请求数量。 + +在没有并发存在的系统中,请求被顺序执行,此时响应时间为吞吐量的倒数。例如系统支持的吞吐量为 100 req/s,那么平均响应时间应该为 0.01s。 + +目前的大型系统都支持多线程来处理并发请求,多线程能够提高吞吐量以及缩短响应时间,主要有两个原因: + +- 多 CPU +- IO 等待时间 + +使用 IO 多路复用等方式,系统在等待一个 IO 操作完成的这段时间内不需要被阻塞,可以去处理其它请求。通过将这个等待时间利用起来,使得 CPU 利用率大大提高。 + +并发用户数不是越高越好,因为如果并发用户数太高,系统来不及处理这么多的请求,会使得过多的请求需要等待,那么响应时间就会大大提高。 + +## 性能优化 + +### 1. 集群 + +将多台服务器组成集群,使用负载均衡将请求转发到集群中,避免单一服务器的负载压力过大导致性能降低。 + +### 2. 缓存 + +缓存能够提高性能的原因如下: + +- 缓存数据通常位于内存等介质中,这种介质对于读操作特别快; +- 缓存数据可以位于靠近用户的地理位置上; +- 可以将计算结果进行缓存,从而避免重复计算。 + +### 3. 异步 + +某些流程可以将操作转换为消息,将消息发送到消息队列之后立即返回,之后这个操作会被异步处理。 + +# 二、伸缩性 + +指不断向集群中添加服务器来缓解不断上升的用户并发访问压力和不断增长的数据存储需求。 + +## 伸缩性与性能 + +如果系统存在性能问题,那么单个用户的请求总是很慢的; + +如果系统存在伸缩性问题,那么单个用户的请求可能会很快,但是在并发数很高的情况下系统会很慢。 + +## 实现伸缩性 + +应用服务器只要不具有状态,那么就可以很容易地通过负载均衡器向集群中添加新的服务器。 + +关系型数据库的伸缩性通过 Sharding 来实现,将数据按一定的规则分布到不同的节点上,从而解决单台存储服务器存储空间限制。 + +对于非关系型数据库,它们天生就是为海量数据而诞生,对伸缩性的支持特别好。 + +# 三、扩展性 + +指的是添加新功能时对现有系统的其它应用无影响,这就要求不同应用具备低耦合的特点。 + +实现可扩展主要有两种方式: + +- 使用消息队列进行解耦,应用之间通过消息传递的方式进行通信; +- 使用分布式服务将业务和可复用的服务分离开来,业务使用分布式服务框架调用可复用的服务。新增的产品可以用过调用可复用的服务来实现业务逻辑,对其它产品没有影响。 + +# 四、可用性 + +## 冗余 + +保证高可用的主要手段是使用冗余,当某个服务器故障时就请求其它服务器。 + +应用服务器的冗余比较容易实现,只要保证应用服务器不具有状态,那么某个应用服务器故障时,负载均衡器将该应用服务器原先的用户请求转发到另一个应用服务器上不会对用户有任何影响。 + +存储服务器的冗余需要使用主从复制来实现,当主服务器故障时,需要提升从服务器为主服务器,这个过程称为切换。 + +## 监控 + +对 CPU、内存、磁盘、网络等系统负载信息进行监控,当某个数据达到一定阈值时通知运维人员,从而在系统发生故障之前及时发现问题。 + +## 服务降级 + +服务器降级是系统为了应对大量的请求,主动关闭部分功能,从而保证核心功能可用。 + +# 五、安全性 + +要求系统的应对各种攻击手段时能够有可靠的应对措施。 diff --git a/notes/集群.md b/notes/集群.md index 649f563a..717ae637 100644 --- a/notes/集群.md +++ b/notes/集群.md @@ -1,4 +1,206 @@ +* [一、负载均衡](#一负载均衡) + * [算法实现](#算法实现) + * [转发实现](#转发实现) +* [二、集群下的 Session 管理](#二集群下的-session-管理) + * [Sticky Session](#sticky-session) + * [Session Replication](#session-replication) + * [Session Server](#session-server) +# 一、负载均衡 + +集群中的应用服务器通常被设计成无状态,用户可以请求任何一个节点(应用服务器)。 + +负载均衡器会根据集群中每个节点的负载情况,将用户请求转发到合适的节点上。 + +负载均衡器可以用来实现高可用以及伸缩性: + +- 高可用:当某个节点故障时,负载均衡器不会将用户请求转发到该节点上,从而保证所有服务持续可用; +- 伸缩性:可以很容易地添加移除节点。 + +负载均衡运行过程包含两个部分: + +1. 根据负载均衡算法得到请求转发的节点; +2. 将请求进行转发; + +## 算法实现 + +### 1. 轮询(Round Robin) + +轮询算法把每个请求轮流发送到每个服务器上。 + +下图中,一共有 6 个客户端产生了 6 个请求,这 6 个请求按 (1, 2, 3, 4, 5, 6) 的顺序发送。最后,(1, 3, 5) 的请求会被发送到服务器 1,(2, 4, 6) 的请求会被发送到服务器 2。 + +

+ +该算法比较适合每个服务器的性能差不多的场景,如果有性能存在差异的情况下,那么性能较差的服务器可能无法承担过大的负载(下图的 Server 2)。 + +

+ +### 2. 加权轮询(Weighted Round Robbin) + +加权轮询是在轮询的基础上,根据服务器的性能差异,为服务器赋予一定的权值,性能高的服务器分配更高的权值。 + +例如下图中,服务器 1 被赋予的权值为 5,服务器 2 被赋予的权值为 1,那么 (1, 2, 3, 4, 5) 请求会被发送到服务器 1,(6) 请求会被发送到服务器 2。 + +

+ +### 3. 最少连接(least Connections) + +由于每个请求的连接时间不一样,使用轮询或者加权轮询算法的话,可能会让一台服务器当前连接数过大,而另一台服务器的连接过小,造成负载不均衡。 + +例如下图中,(1, 3, 5) 请求会被发送到服务器 1,但是 (1, 3) 很快就断开连接,此时只有 (5) 请求连接服务器 1;(2, 4, 6) 请求被发送到服务器 2,只有 (2) 的连接断开。该系统继续运行时,服务器 2 会承担过大的负载。 + +

+ +最少连接算法就是将请求发送给当前最少连接数的服务器上。 + +例如下图中,服务器 1 当前连接数最小,那么新到来的请求 6 就会被发送到服务器 1 上。 + +

+ +### 4. 加权最少连接(Weighted Least Connection) + +在最少连接的基础上,根据服务器的性能为每台服务器分配权重,再根据权重计算出每台服务器能处理的连接数。 + +

+ +### 5. 随机算法(Random) + +把请求随机发送到服务器上。 + +和轮询算法类似,该算法比较适合服务器性能差不多的场景。 + +

+ +### 6. 源地址哈希法 (IP Hash) + +源地址哈希通过对客户端 IP 计算哈希值之后,再对服务器数量取模得到目标服务器的序号。 + +可以保证同一 IP 的客户端的请求会转发到同一台服务器上,用来实现会话粘滞(Sticky Session) + +

+ +## 转发实现 + +### 1. HTTP 重定向 + +HTTP 重定向负载均衡服务器使用某种负载均衡算法计算得到服务器的 IP 地址之后,将该地址写入 HTTP 重定向报文中,状态码为 302。客户端收到重定向报文之后,需要重新向服务器发起请求。 + +缺点: + +- 需要两次请求,因此访问延迟比较高; +- HTTP 负载均衡器处理能力有限,会限制集群的规模。 + +该负载均衡转发的缺点比较明显,实际场景中很少使用它。 + +

+ +### 2. DNS 域名解析 + +在 DNS 解析域名的同时使用负载均衡算法计算服务器地址。 + +优点: + +- DNS 能够根据地理位置进行域名解析,返回离用户最近的服务器 IP 地址。 + +缺点: + +- 由于 DNS 具有多级结构,每一级的域名记录都可能被缓存,当下线一台服务器需要修改 DNS 记录时,需要过很长一段时间才能生效。 + +大型网站基本使用了 DNS 做为第一级负载均衡手段,然后在内部使用其它方式做第二级负载均衡。也就是说,域名解析的结果为内部的负载均衡服务器 IP 地址。 + +

+ +### 3. 反向代理服务器 + +首先了解一下正向代理与反向代理的区别: + +- 正向代理:发生在客户端,是由用户主动发起的。比如翻墙,客户端通过主动访问代理服务器,让代理服务器获得需要的外网数据,然后转发回客户端; +- 反向代理:发生在服务器端,用户不知道代理的存在。 + +反向代理服务器位于源服务器前面,用户的请求需要先经过反向代理服务器才能到达源服务器。反向代理可以用来进行缓存、日志记录等,同时也可以用来做为负载均衡服务器。 + +在这种负载均衡转发方式下,客户端不直接请求源服务器,因此源服务器不需要外部 IP 地址,而反向代理需要配置内部和外部两套 IP 地址。 + +优点: + +- 与其它功能集成在一起,部署简单。 + +缺点: + +- 所有请求和响应都需要经过反向代理服务器,它可能会成为性能瓶颈。 + +### 4. 网络层 + +在操作系统内核进程获取网络数据包,根据负载均衡算法计算源服务器的 IP 地址,并修改请求数据包的目的 IP 地址,最后进行转发。 + +源服务器返回的响应也需要经过负载均衡服务器,通常是让负载均衡服务器同时作为集群的网关服务器来实现。 + +优点: + +- 在内核进程中进行处理,性能比较高。 + +缺点: + +- 和反向代理一样,所有的请求和响应都经过负载均衡服务器,会成为性能瓶颈。 + +### 5. 链路层 + +在链路层根据负载均衡算法计算源服务器的 MAC 地址,并修改请求数据包的目的 MAC 地址,并进行转发。 + +通过配置源服务器的虚拟 IP 地址和负载均衡服务器的 IP 地址一致,从而不需要修改 IP 地址就可以进行转发。也正因为 IP 地址一样,所以源服务器的响应不需要转发回负载均衡服务器,直接转发给客户端,避免了负载均衡服务器的成为瓶颈。这是一种三角传输模式,被称为直接路由,对于提供下载和视频服务的网站来说,直接路由避免了大量的网络传输数据经过负载均衡服务器。 + +这是目前大型网站使用最广负载均衡转发方式,在 Linux 平台可以使用的负载均衡服务器为 LVS(Linux Virtual Server)。 + +参考: + +- [Comparing Load Balancing Algorithms](http://www.jscape.com/blog/load-balancing-algorithms) +- [Redirection and Load Balancing](http://slideplayer.com/slide/6599069/#) + +# 二、集群下的 Session 管理 + +一个用户的 Session 信息如果存储在一个服务器上,那么当负载均衡器把用户的下一个请求转发到另一个服务器,由于服务器没有用户的 Session 信息,那么该用户就需要重新进行登录等操作。 + +## Sticky Session + +需要配置负载均衡器,使得一个用户的所有请求都路由到同一个服务器,这样就可以把用户的 Session 存放在该服务器中。 + +缺点: + +- 当服务器宕机时,将丢失该服务器上的所有 Session。 + +

+ +## Session Replication + +在服务器之间进行 Session 同步操作,每个服务器都有所有用户的 Session 信息,因此用户可以向任何一个服务器进行请求。 + +缺点: + +- 占用过多内存; +- 同步过程占用网络带宽以及服务器处理器时间。 + + +

+ +## Session Server + +使用一个单独的服务器存储 Session 数据,可以使用 MySQL,也使用 Redis 或者 Memcached 这种内存型数据库。 + +优点: + +- 为了使得大型网站具有伸缩性,集群中的应用服务器通常需要保持无状态,那么应用服务器不能存储用户的会话信息。Session Server 将用户的会话信息单独进行存储,从而保证了应用服务器的无状态。 + +缺点: + +- 需要去实现存取 Session 的代码。 + +

+ +参考: + +- [Session Management using Spring Session with JDBC DataStore](https://sivalabs.in/2018/02/session-management-using-spring-session-jdbc-datastore/) +