问题描述:一个典型的大型互联网应用系统使用了哪些技术方案和手段,主要解决什么问题?请列举描述。
性能与可扩展性
-
垂直扩展
垂直扩展通过提升单机处理能力扩展性能,包括提升硬件性能、提升单机架构等等措施。在预算允许的情况下,垂直扩展是最简单最靠谱的提升手段
-
水平扩展
水平扩展通过增加服务器数量扩充系统性能。当垂直扩展达到物理瓶颈时,我们只能通过水平扩展提升性能。水平扩展是互联网公司最常用的手段,但也对系统架构提出了更高的要求,小公司还是得审慎采用
DNS
DNS 是一种分布式网络目录服务,主要用于域名与 IP 地址的相互转换。它能够使人更方便的访问互联网,而不用去记住能够被机器直接读取的 IP 数串。当查询(域名) IP 时,路由或 ISP 提供连接 DNS 服务器的信息,在一些全球布局的互联网服务中,DNS 可以帮助用户连接到就近部署服务器。
CDN
内容分发网络是一个全球性的代理服务器分布式网络,它从靠近用户的位置提供内容。通常,网页的 HTML、CSS、JS、图片和视频等静态内容由 CDN 提供。
将内容存储在 CDN 上可以从两个方面来提供性能:
- 从靠近用户的数据中心提供资源
- 通过 CDN 获取的资源,消耗服务器计算资源
负载均衡
负载均衡器将传入的请求分发到不同的应用服务器和数据库等计算资源中。负载均衡器的效用在于:
- 防止请求进入不好的服务器
- 防止资源过载
- 帮助消除单一的故障点
- 负载均衡器还能帮助水平扩展,提高性能和可用性。
反向代理
反向代理是一种可以集中调用内部资源的服务;它为用户提供了一个统一接口的 web 服务器。来自客户端的请求先被反向代理服务器转发到可响应请求的服务上,然后代理再把该服务的响应结果返回给客户端。反向代理的好处是:
- 隐藏后端服务器的信息,屏蔽黑名单中的 IP,限制每个客户端的连接数。
- 客户端只能看到反向代理服务器的 IP,这使你可以增减服务器或者修改它们的配置。
- 此外,还可以做加密、压缩、缓存等操作提供性能或安全性
微服务
微服务将一个大型应用拆分成一些列可以独立部署的小型的、模块化的服务。各个模块的服务又可以通过上面提到的负载均衡器或是反向代理进一步扩展为服务集群,提升服务的吞吐率。每个服务被运行在一个独立的容器中,通过明确定义的通讯机制互相调用,共同实现业务目标。
数据库
数据库还可以做进一步的拆分提升读写性能:
-
主从复制
一般的应用都是读大于写,当数据库读取速率成为系统瓶颈时,可以将数据库进行读写分离:主库主要负责写入操作,并复制写入到多个从库中,从库只负责读操作。
-
联合
联合就是将数据库按对应功能分割。例如,你可以将用户、产品、业务状态三种功能的数据存放在三个不同的数据库中,从而减少每个数据库的读取和写入流量。
-
分片
分片就是将相同功能的数据分散存储到不同的数据库上,使得每个数据库仅管理整个数据集的一个子集。以用户数据库为例,我们可以把 A~F 开头的用户存放在一个数据库,G~Z 开头的用户存放在另一个数据库中,这样可以提升单个数据库的读写效率。
-
NoSQL
NoSQL 是键-值数据库、文档型数据库、列型数据库或图数据库的统称。NoSQL 通常用于存储简单数据模型或频繁修改的数据,某些操作不需要很强的 ACID 的事务,我们可以通过 NoSQL 提升读写性能。
缓存
缓存利用了所谓的信息二八原则,将一些未更改的数据存放在更高速读取的硬件设备上,用于减少服务器和数据库的负载。系统经过分层后,每一层都可以有缓存,如:
客户端缓存:像端浏览器里就有内存级缓存、cookie、local storage、indexDB 等等。
CDN:上面提到的 CDN,在全球边缘站点分发静态资源,这也是一种缓存。
反向代理 & 负载均衡,无论是软硬件的设备都自带缓存
内存缓存:这个就比较多了,服务本身就可以在内存里缓存,还有一些专门的内存级缓存应用如 Redis、memcached 等等
数据库缓存:如 DB 里的查询级别的缓存、对象缓存等等
异步
异步工作有助于减少那些原本顺序无关请求的响应时间:
Nodejs 应用:JS 本身就是异步语言,可以使用原生方法
Promise.all
来做异步操作消息队列:有 RabbitMQ、Amazon SQS、RocketMQ 等等专用服务来托管异步消息,解耦不同应用
通讯
通讯就是上面提到的客户端/服务器,或是微服务间互相调用使用的一些交互机制,常用的通讯机制有:
- RPC: 远程过程调用协议,通常用于处理内部通讯,如阿里的 Dubbo 框架就是典型的 RPC 应用
- Rest:表述性状态转移,各种模块都通用的交互协议,以 JSON 的格式传送数据
- Graphql:FaceBook 出品的一款 API 查询语言,常用于客户端/服务器通讯,是 Rest 的一种替代品
安全
安全涉及到的面很多很多了,常用的手段有:
- 在运输和等待过程中加密
- 使用参数化的查询来防止 SQL 注入
- 对所有的用户输入和从用户那里发来的参数进行处理以防止 XSS
- 权限控制等等