目录

何为架构

网站架构演变史

阶段一、单机构建网站

阶段二、应用服务器与数据库分离

阶段三:增加应用服务器

阶段四:前后端分离


Editor's Note:

Hi,我是伊凡,一个互联网大厂码农,准备从0搭建一个GitHub开源教培管理系统。我会以文章形式完整记录从网站架构、数据模型、业务模型、技术选型、工程组织、中间件、稳定性建设,一直到服务器选型,工程部署等整个软件开发流程。目标是打造一个高性能、高可用、可拓展、易伸缩、安全、低成本的项目。本节是开端,我将跟大家讲述网站架构的前世今生。

                                                                                                                                                                                                                                                                                                文|伊凡

大型网站架构设计 pdf 大型网站架构演变_中间件

何为架构

写文章时先写出大纲往往利于整体行文,写作过程会很流畅,写出的文章肯定也思路清晰。盖房子时要先打好地基,搭出房子的骨架,地基和骨架搭的好,房子才会坚固耐用。同理,在软件工程中,也是有架构这种概念的,不同架构的网站性能差别可能会十分巨大。

大型网站架构设计 pdf 大型网站架构演变_java_02

网站架构演变史

我们以javaweb为例,来搭建一个简单的在线教育系统,看看这个系统如何一步步演变。

该系统具备的功能

会员模块——会员注册和管理

课程模块——课程展示和管理

交易模块——创建交易和管理

阶段一、单机构建网站

起初,前端程序员根据UI设计进行切图和HTML网页制作,然后将HTML文件交给后端程序员,后端根据需求将之转换成JSP,JSP可以被容器转为类Servlet,调用后端核心逻辑(除了JSP、Servlet技术,还可以选择一些开源的框架如Maven + Spring + Struts + Hibernate、Maven + Spring + Spring MVC + Mybatis,效果一样),再选择一个数据库管理系统来存储数据,如MySQL、SqlServer、Oracle,然后通过JDBC进行数据库的连接和操作。Web应用在前后端同学的共同努力下终于开发完成,测试通过后准备发布上线了,我们采购了一台主机(又叫服务器),先将数据库部署到主机上,然后安装web容器(Tomcat/jetty/jboos/weblogic),接着将web应用打成war包(包含html、css、jsp、JavaBean、Service、Dao等),上传到容器中,最后启动容器,如果不出意外,我们的应用就成功跑起来了,也算是一个小系统了。

此时系统结构如下:

大型网站架构设计 pdf 大型网站架构演变_架构_03

阶段二、应用服务器与数据库分离

我们网站的发展的还不错,网站会员在稳步增长,但是运维人员报告说网站的负载越来越高了,已经接近水位线了,有宕机风险,要赶紧治理一下。开发人员首先针对代码进行了一些列优化,包括减少不必要的调用、降低堆内存的分配,优化线程池,优化GC策略等,优化后网站负载明显降低了。但随着会员数量的持续增多,网站再次即将过载,此时代码已经没有优化空间了,该怎么办呢?既然单台主机的性能达到了上限,我们再买一台,替它分担一部分负载不就行了。我们将数据库单独部署在新购买的服务器上,这样一来,web应用和数据库单独部署在不同的服务器,这样不仅提高了单台机器的负载能力,也提高了容灾能力。

应用服务器与数据库分开后的架构如下图所示:

大型网站架构设计 pdf 大型网站架构演变_个人开发_04

阶段三:增加应用服务器

随着访问量继续增加,单台应用服务器已经无法满足需求了。在假设数据库服务器没有压力的情况下,我们可以把应用服务器从一台变成了两台甚至多台,把用户的请求分散到不同的服务器中,从而提高负载能力。而多台应用服务器之间没有直接的交互,他们都是依赖数据库各自对外提供服务。著名的做故障切换的软件有KeepAlived,KeepAlived是一个类似于Layer3、4、7交换机制的软件,他不是某个具体软件故障切换的专属品,而是可以适用于各种软件的一款产品。KeepAlived配合上ipvsadm又可以做负载均衡,可谓是神器。

我们以增加了一台应用服务器为例,增加后的系统结构图如下:

大型网站架构设计 pdf 大型网站架构演变_java_05

系统演变到这里,将会出现下面四个问题:

用户的请求由谁来转发到到具体的应用服务器(负载均衡问题)?

有那些转发的算法和策略可以使用?

应用服务器如何返回用户的请求?

用户如果每次访问到的服务器不一样,那么如何维护session的一致性?

针对以上问题,常用的解决方案如下:

1、负载均衡的问题

一般以下有5种解决方案:

1)HTTP重定向

HTTP重定向就是应用层的请求转发。用户的请求其实已经到了HTTP重定向负载均衡服务器,服务器根据算法要求用户重定向,用户收到重定向请求后,再次请求真正的集群

优点:简单易用;

缺点:性能较差。

2)DNS域名解析负载均衡

DNS域名解析负载均衡就是在用户请求DNS服务器,获取域名对应的IP地址时,DNS服务器直接给出负载均衡后的服务器IP。

优点:交给DNS,不用我们去维护负载均衡服务器;

缺点:当一个应用服务器挂了,不能及时通知DNS,而且DNS负载均衡的控制权在域名服务商那里,网站无法做更多的改善和更强大的管理。

3)反向代理服务器

在用户的请求到达反向代理服务器时(已经到达网站机房),由反向代理服务器根据算法转发到具体的服务器。常用的Apache,Nginx都可以充当反向代理服务器。

优点:部署简单;

缺点:代理服务器可能成为性能的瓶颈,特别是一次上传大文件。

4)IP层负载均衡

在请求到达负载均衡器后,负载均衡器通过修改请求的目的IP地址,从而实现请求的转发,做到负载均衡。

优点:性能更好;

缺点:负载均衡器的宽带成为瓶颈。

5)数据链路层负载均衡

在请求到达负载均衡器后,负载均衡器通过修改请求的MAC地址,从而做到负载均衡,与IP负载均衡不一样的是,当请求访问完服务器之后,直接返回客户。而无需再经过负载均衡器。

2、集群调度转发算法

1)rr轮询调度算法

顾名思义,轮询分发请求。

优点:实现简单

缺点:不考虑每台服务器的处理能力

2)wrr加权调度算法

我们给每个服务器设置权值Weight,负载均衡调度器根据权值调度服务器,服务器被调用的次数跟权值成正比。

优点:考虑了服务器处理能力的不同

3)sh原地址散列算法

提取用户IP,根据散列函数得出一个key,再根据静态映射表,查处对应的value,即目标服务器IP。过目标机器超负荷,则返回空。

优点:实现同一个用户访问同一个服务器。

4)dh目标地址散列算法

原理同上,只是现在提取的是目标地址的IP来做哈希。

优点:实现同一个用户访问同一个服务器。

5)lc最少连接算法

优先把请求转发给连接数少的服务器。

优点:使得集群中各个服务器的负载更加均匀。

6)wlc加权最少连接算法

在lc的基础上,为每台服务器加上权值。算法为:(活动连接数 * 256 + 非活动连接数) ÷ 权重,计算出来的值小的服务器优先被选择。

优点:可以根据服务器的能力分配请求。

7)sed最短期望延迟算法

其实sed跟wlc类似,区别是不考虑非活动连接数。算法为:(活动连接数 +1 ) * 256 ÷ 权重,同样计算出来的值小的服务器优先被选择。

8)nq永不排队算法

改进的sed算法。我们想一下什么情况下才能“永不排队”,那就是服务器的连接数为0的时候,那么假如有服务器连接数为0,均衡器直接把请求转发给它,无需经过sed的计算。

9)LBLC基于局部性最少连接算法

负载均衡器根据请求的目的IP地址,找出该IP地址最近被使用的服务器,把请求转发之。若该服务器超载,最采用最少连接数算法。

10)LBLCR带复制的基于局部性最少连接算法

负载均衡器根据请求的目的IP地址,找出该IP地址最近使用的“服务器组”,注意,并不是具体某个服务器,然后采用最少连接数从该组中挑出具体的某台服务器出来,把请求转发之。若该服务器超载,那么根据最少连接数算法,在集群的非本服务器组的服务器中,找出一台服务器出来,加入本服务器组,然后把请求转发。

3、集群请求返回模式问题

1)NAT

负载均衡器接收用户的请求,转发给具体服务器,服务器处理完请求返回给均衡器,均衡器再重新返回给用户。

2)DR

负载均衡器接收用户的请求,转发给具体服务器,服务器出来完成请求后直接返回给用户。需要系统支持IP Tunneling协议,难以跨平台。

3)TUN

同上,但无需IP Tunneling协议,跨平台性好,大部分系统都可以支持。

4、集群Session一致性问题

1)Session

Session 就是把同一个用户在某一个会话中的请求,都分配到固定的某一台服务器中,这样我们就不需要解决跨服务器的session问题了,常见的算法有ip_hash算法,即上面提到的两种散列算法。

优点:实现简单;

缺点:应用服务器重启则session消失。

2)Session Replication

Session replication就是在集群中复制session,使得每个服务器都保存有全部用户的session数据。

优点:减轻负载均衡服务器的压力,不需要要实现ip_hasp算法来转发请求;

缺点:复制时网络带宽开销大,访问量大的话Session占用内存大且浪费。

3)Session数据集中存储

Session数据集中存储就是利用数据库来存储session数据,实现了session和应用服务器的解耦。

优点:相比Session replication的方案,集群间对于宽带和内存的压力大幅减少;

缺点:需要维护存储Session的数据库。

4)Cookie Base

Cookie base就是把Session存在Cookie中,由浏览器来告诉应用服务器我的session是什么,同样实现了session和应用服务器的解耦。

优点:实现简单,基本免维护。

缺点:cookie长度限制,安全性低,带宽消耗。

值得一提的是:

  • Nginx目前支持的负载均衡算法有wrr、sh(支持一致性哈希)、fair(lc)。但Nginx作为均衡器的话,还可以一同作为静态资源服务器。
  • Keepalived + ipvsadm比较强大,目前支持的算法有:rr、wrr、lc、wlc、lblc、sh、dh
  • Keepalived支持集群模式有:NAT、DR、TUN
  • Nginx本身并没有提供session同步的解决方案,而Apache则提供了session共享的支持。

解决了以上的问题之后,系统的结构如下:

大型网站架构设计 pdf 大型网站架构演变_中间件_06

阶段四:前后端分离

我们的网站发展的越来越好,用户持续增长,随之而来的新需求也越来越多,此时前后端混杂在一起开发,效率问题逐渐凸显,后端需要等待前端的 HTML代码完成之后,再整合成 JSP,而且出错率较高,遇到 Bug 解决起来也很麻烦,需要双方协同处理,这就给开发带来了很大的问题,开发效率极低。

我们决定将前后端的开发独立起来,前后端程序猿只需要提前约定好接口文档(参数、数据类型),然后并行开发即可,最后完成前后端集成,遇到问题同步修改即可,真正实现了前后端应用的解耦合,可以极大地提升开发效率。

说直白点就是把原来一个应用,拆分成两个应用,一个纯前端应用,专门负责数据展示和用户交互,一个纯后端应用,专门负责提供数据处理接口,前端 HTML 页面通过 Ajax 调用后端 RESTful API 接口进行数据交互。

前后端开发完毕,通过测试后,分别打包部署至应用服务器,也可以把前端代码地理部署到新的服务器,但要主意跨域调用问题。

前后端分离后架构如下:

大型网站架构设计 pdf 大型网站架构演变_中间件_07

欲知后事如何,请移步下一篇文章《彻底搞懂大型网站架构演变(下)》

- End -