大型网站技术架构核心原理与案例分析笔记
网站的演化历史 :
【考虑到可用性, 下面每一步都可以做主备】
一、小型网站 : lamp (linux apache mysql php), 将所有东西都部署在单台机器上。
应用程序的开发越快越好, 可以选择 php 、 django 等。
不一定需要 redis 等缓存, 但是数据库是必须的。
代理的话如果语言层面框架有实现好的代理可以不使用 apache 等代理。
所以最简单的网站可能是这样的 :
在一台 linux 服务器里面部署 mysql 用来存储数据, 部署一个 go的 iris 用来接收和处理请求。
二、业务请求增加, 数据库查询慢, 引入缓存(redis)。
架构变成 应用程序 + redis + mysql 。 一台机器。
三、 用户上传的静态文件等越来越多, 导致服务器内存不够, 此时应该将静态文件服务器独立出去。
架构变成 [应用程序 + redis + mysql 一台机器] + [静态文件服务器 一台机器]
四、业务请求增加, 将 数据库 和 应用程序、缓存程序分开。
架构变成 [ 应用程序 + redis 部署在一台机器] + [mysql 部署在另外一台机器] + [静态文件服务器 一台机器]
五、 业务请求再增加, 应用程序需要做集群, 因此引入负载均衡, 可以使用 nginx 的反向代理实现,
与此同时也会带来各个应用服务器上的缓存不相同的问题, (如session同步),此时应该将缓存服务器也独立出去。
架构就变成了 :
一台 nginx 服务器做负载均衡转发请求 + 多台应用服务器运行 iris + 一台redis服务器 + 一台 mysql 存储数据
+ [静态文件服务器 一台机器]
六、 业务请求再增加, 单台数据库成为性能瓶颈, 此时应该做数据库的读写分离(一般写入的操作比读取的操作少得多)。
架构变成 :
一台 nginx 服务器做负载均衡转发请求 + 多台应用服务器运行 iris + 一台redis服务器 +
一台 mysql 写服务器 + 多台 mysql 读服务器 + [静态文件服务器 一台机器]
七、 业务请求再增加, 单台缓存服务器成为性能瓶颈(容量或者并发),此时应该做缓存服务器集群(redis cluster)
架构变成 :
一台 nginx 服务器做负载均衡转发请求 + 多台应用服务器运行 iris + 多台redis服务器
+ 一台 mysql 写服务器 + 多台 mysql 读服务器 + [静态文件服务器 一台机器]
八、 此时用户量太多, 静态文件一台放不下, 也需要做集群。
架构变成 :
一台 nginx 服务器做负载均衡转发请求 + 多台应用服务器运行 iris + 多台redis服务器
+ 一台 mysql 写服务器 + 多台 mysql 读服务器 + 多台静态文件服务器
九、当数据写入数据库再次成为瓶颈, 可以对mysql 的写入服务器做集群或者在将数据写入数据库之前写写入 kafka 消息队列,
然后控制线程并发将数据从kafka消息队列读出来写入到 mysql 数据库。
架构变成 :
一台 nginx 服务器做负载均衡转发请求 + 多台应用服务器运行 iris + 多台redis服务器 + 多台 mysql 写服务器 + 多台 mysql 读服务器 + [静态文件服务器 一台机器]
或者
一台 nginx 服务器做负载均衡转发请求 + 多台应用服务器运行 iris + 多台redis服务器 + kafka消息队列 + 一台 mysql 写服务器 + 多台 mysql 读服务器 + [静态文件服务器 一台机器]
或者
一台 nginx 服务器做负载均衡转发请求 + 多台应用服务器运行 iris + 多台redis服务器 + kafka消息队列 + 多台 mysql 写服务器 + 多台 mysql 读服务器 + [静态文件服务器 一台机器]
十、最后就需要实现对业务进行拆分, 做成微服务, 每一个子系统都可以按照要求按上面的设计部署。
---------------------------------------------------
业务拆分的难点 :
难点一 : 在将系统拆分后, 如何提供统一的 api 网关 ?
方案一、
加一级Nginx网关反代你的api。
例如 :
www.test.com/user => 用户系统负载均衡 => 用户系统
www.test.com/book => 图书系统负载均衡 => 图书系统
难点二:这些系统间如何实现用户校验 ?
方案一、
保存系统间生成 token 的 key 相同, 使用 jwt 校验,
可以把用户id、用户名等非私密、各个系统都用得到的信息加入到 token 中。
例如 :
用户系统 :
保存用户信息, 通过登录功能, 发放 token , 可以校验token
图书系统 :
通过保存与用户系统相同的 key 来保证可以校验用户系统发放的 token, 自己做登录校验
难点三 : 原来用户表与其他东西使用外键建立的对应关系在数据库被拆分的情况下如何维持?
方案一、
将外键直接替换成 int 等普通的字段, 然后各个系统只要把这个 id 当作普通字段处理就行了,
查询的时候通过 id 分开查询。
(很多公司都不使用外键 : https://www.cnblogs.com/rjzheng/p/9907304.html)
使用外键约束带来的具体问题 :
性能问题 :
假设一张表名为user_tb。那么这张表里有两个外键字段,指向两张表。那么,每次往user_tb表里插入数据,就必须往两个外键对应的表里查询是否有对应数据。如果交由程序控制,这种查询过程就可以控制在我们手里,可以省略一些不必要的查询过程。但是如果由数据库控制,则是必须要去这两张表里判断。
并发问题 :
在使用外键的情况下,每次修改数据都需要去另外一个表检查数据,需要获取额外的锁。若是在高并发大流量事务场景,使用外键更容易造成死锁
扩展性问题 :
这里主要是分为两点
做平台迁移方便,比如你从Mysql迁移到Oracle,像触发器、外键这种东西,都可以利用框架本身的特性来实现,而不用依赖于数据库本身的特性,做迁移更加方便。
分库分表方便,在水平拆分和分库的情况下,外键是无法生效的。将数据间关系的维护,放入应用程序中,为将来的分库分表省去很多的麻烦
技术问题 :
使用外键,其实将应用程序应该执行的判断逻辑转移到了数据库上。那么这意味着一点,数据库的性能开销变大了,那么这就对DBA的要求就更高了。很多中小型公司由于资金问题,并没有聘用专业的DBA,因此他们会选择不用外键,降低数据库的消耗。
相反的,如果该约束逻辑在应用程序中,发现应用服务器性能不够,可以加机器,做水平扩展。如果是在数据库服务器上,数据库服务器会成为性能瓶颈,做水平扩展比较困难。
----------------------------------------------
【 可能导致性能瓶颈的地方 】 :
内存、 磁盘、网络、CPU (散热也会影响cpu, 具体的外部原因还有很多)。
各种服务器的硬件要求 :
【负载均衡服务器】 网络 【会成为性能瓶颈, 上硬件】
【应用程序服务器】 CPU、内存 【无限扩容】
【缓存服务器】 内存 【Redis Cluster可扩容】
【数据库服务器】 CPU、内存、磁盘 【容量达到上限则分库分表或者换 tidb】
【静态文件服务器】 磁盘、网络 【阿里云oos、七牛云、通过(glusterfs,fastdfs,SeaweedFS)自建】
【消息队列服务器】 CPU、内存 【选择本身就支持集群的消息队列】
=======================================================================
微服务几个底座 :
消息总线(也可以用 http)
注册发现
配置中心
日志中心
监控告警
-----------------------------------
系统拆分成 :
用户系统(用户认证等)
注册发现
配置中心
日志中心
监控告警
剩下的就是各种业务
每个系统维护自己的数据库。
各个系统提供 api 给外部访问, 系统间调用使用消息总线(用 mq 或者 http 或者 proto 七七八八)
系统间通过 nginx 反向代理提供统一的对外接口。
这样就可以把原来系统的一个菜单做成一个微服务子系统。