相信大家都听过这道经典的面试题:“请描述一下在淘宝上从输入一个关键词到最终显示网页的整个过程,越详细越好”。
这道题比较难,涉及到HTTP、TCP、网关、LVS等一系列相关概念,以及很多协议的工作机制。如果能掌握这些知识点,将大大照亮你的技能树,对网络的工作原理也会有一个清晰的认识。即使不能完全掌握,知道流量是如何流动的,对你排查问题、定位问题也会有很大的帮助。我之前就利用这些知识定位过很多问题,为了搞清楚整个流程,查阅了很多资料,相信应该可以把这个问题说清楚。但是写着写着,发现篇幅太长了,就分成两部分分别介绍。本文先介绍后端流量的整体架构图,下一篇会深入分析细节,比如LVS、NAT的工作细节等,这会涉及到交换机、路由器的工作机制等知识点,相信大家看完都会很有帮助。
李大牛创业了,由于前期流量不大,所以只部署了一台服务器,让客户端直接向这台服务器发送请求。
一开始这种部署是没有问题的,因为业务量不大,单台机器足以应付。但后来李大牛的业务碰上了风口浪尖,发展迅速,所以单台机器的性能逐渐遇到了瓶颈。而且由于只部署了一台机器,如果这台机器宕机,业务也会降为零,这是不可接受的。因此,为了避免单台机器的性能瓶颈,解决单点故障的隐患,李大牛决定多部署几台机器(假设是三台),这样就可以随机攻击其中一台机器。这样,即使其中一台机器宕机,其他机器还活着,因此可以将攻击导向其他没有宕机的机器。
现在的问题是,这三台机器应该叫哪台呢?让它选择肯定是不合适的,因为如果让它选择具体的一台,它必须知道有哪几台,然后通过轮询等方式随机连接其中一台机器,但是如果其中一台机器宕机了,它无法提前感知到,很有可能会去连接宕机的机器,所以选择连接哪台机器的工作最好放在中间。具体怎么做呢?架构设计上有一个经典的共识:没有什么是加一层解决不了的,有的话就再加一层,所以我们在最后面再加一层,命名为LB(Load,负载均衡),它接收所有的请求,然后决定和哪台通信。一般业界常用Nginx来做LB。
这种架构设计最终支撑起了业务的快速增长,但不久之后,李大牛发现这种架构存在一个问题:所有的流量都可以发往服务器,这显然是有问题的,也不太安全。那么,我们能不能在流量发往服务器之前,再做一层认证?认证通过之后,我们才会允许流量发往服务器。我们把这一层称为网关(为了避免单点故障,网关也必须以集群的形式存在)
这样,所有流量在命中前都必须经过网关层,认证通过后才会转发到服务器,否则会返回错误信息。网关除了认证之外,还起到风控(防止蹭网者)、协议转换(比如将 HTTP 转换为 Dubbo)、流量管控等功能,最大程度保证转发到服务器的流量是安全可控的。
这个设计持续了很久,但是后来李大牛发现这个设计还是有问题的,动态请求和静态资源(比如js,css文件)请求都会被命中,在流量大的时候会造成很大的压力。其实静态资源的处理还不如Nginx,每次都从磁盘加载文件,会影响性能。Nginx有代理缓存等功能可以大大提高静态资源的处理能力。
画外音:所谓代理缓存,就是指nginx从静态资源服务器获取资源之后,会先缓存在本地内存+磁盘中,下次请求若命中缓存,则直接从Nginx自身的缓存中返回。
于是李大牛做了如下优化:如果是动态请求,就由Nginx命中,如果是Nginx,就由静态资源服务器命中。
这就是我们所说的动静分离,将静态请求和动态请求分离,让它专注于处理它所擅长的动态请求,静态资源则利用了Nginx的代理缓存等功能,后端处理能力提升了一个档次。
还需要注意的是,并不是所有的动态请求都需要走网关,比如我们的运营中心后端是内部员工使用的,它的认证和网关API的认证是不一样的,所以我们直接部署了两台运营中心的机器,让Nginx直接把运营中心的请求发到这两台机器上,绕过网关。
当然为了避免单点故障,Nginx 也需要部署至少两台机器,所以我们的架构就变成了这样:Nginx 部署在两台机器上,一主一备的形式,备 Nginx 会通过机制(发送心跳包)及时感知主 Nginx 的存活情况,如果发现宕机就会接替主 Nginx 的角色。
看起来这个架构确实不错,但是要注意的是,Nginx 是一个七层(即应用层)的负载均衡器,也就是说它如果要转发流量,必须先和上游服务器建立 TCP 连接,而转发的时候,也必须和要转发到的上游服务器建立 TCP 连接。我们知道,建立 TCP 连接其实是需要消耗内存的(TCP、接收/发送缓冲区等都需要内存)。客户端和上游服务器如果要发送数据,必须先把数据发到 Nginx 暂存,然后再通过另一端的 TCP 连接互相传输。
所以Nginx的负载能力受到机器I/O、CPU内存等一系列配置的限制,一旦连接数很多(例如达到百万级),Nginx的抗负载能力就会急剧下降。
经过分析,我们知道Nginx负载能力差主要是因为它是一个七层的负载均衡器,必须在上下游建立两个TCP连接。那么,我们能否设计一个只转发数据包而不需要建立连接的负载均衡器,就像路由器一样呢?这样,既然不需要建立连接,它只负责转发数据包,不需要维护额外的TCP连接,它的负载能力必然会大大提高。因此,四层负载均衡器LVS诞生了。下面我们来简单比较一下两者的区别。
可以看出LVS只是简单的转发数据包,并不和上下游建立连接,相比Nginx抗负载能力强,性能高(可达F5硬件的60%),消耗的内存和CPU资源更少。
那么第 4 层负载均衡器如何工作?
当负载均衡设备收到客户端第一个SYN请求后,通过负载均衡算法选出最优的服务器,修改报文中的目标IP地址(为后端服务器IP),并直接转发给服务器。客户端与服务器之间直接建立TCP连接,即三次握手,负载均衡设备只起到类似路由器的转发作用。在某些部署情况下,为了保证服务器回复包能正确返回给负载均衡设备,在转发报文的同时,可能会修改报文的原始源地址。
总结一下,我们在 Nginx 上又加了一层 LVS,让它来处理我们所有的流量。当然,为了保证 LVS 的可用性,我们还将 LVS 部署为主从模式。另外,如果 Nginx 的容量不够,我们还可以很方便地进行水平扩展。所以我们的架构做了如下改进:
当然如果只有一个LVS的话,流量太大了,那怎么办呢?再加几个。使用DNS负载均衡,DNS服务器解析域名的时候,会随机命中其中一个LVS。
这样,流量终于可以稳定地流通了,有朋友可能对这一点有疑问,下面我们一起来看看吧。
既然LVS可以部署在多台机器上,避免单点故障,那么Nginx也可以做到,而且Nginx在1.9之后也开始支持四层负载均衡了,这样看来LVS也不是很有必要了?
如果不使用LVS,架构图如下
在流量不是很大的情况下,部署多台 Nginx 服务器是可行的,但是 LVS 是 Linux 内核模块,工作在内核态,而 Nginx 工作在用户态,比较重,所以在性能和稳定性上 Nginx 不如 LVS,这也是为什么我们要采用 LVS + Nginx 的部署方式。
另外相信大家也注意到了,如果流量很大的话,静态资源应该部署在CDN上,CDN会自动选择距离用户最近的节点返回给用户,所以我们最终的架构改进如下
总结
架构一定要结合实际业务情况来设计,不考虑业务就谈架构是不负责任的。可以看到上面提到的各个架构的演进都和我们的业务发展息息相关。对于流量没那么多的中小型公司,用Nginx做负载均衡器就够了,流量快速增长之后再考虑用lvs+nginx。当然对于美团这种海量流量(几十Gbps的流量,几千万的并发连接)来说,lvs是没用的(虽然实际测试用了lvs,但是丢包还是很多)。所以他们自己开发了四层负载均衡器MGW
另外,看完这篇文章,相信大家对分层这个概念应该理解得更透彻了。没有什么是分层解决不了的,如果有,那就再加一层。分层可以让各个模块各司其职,功能解耦,方便扩展。我们熟悉的TCP/IP就是一个很好的例子,每一层只负责自己的事情,至于下层实现什么,上层是不管的。
扫一扫在手机端查看
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。