应用架构经过不同阶段,逐渐由单一发展至分布式,由功能化发展至服务化,主要的几类架构如下:
单一应用架构(ORM)-> 垂直应用架构(MVC)-> 分布式服务架构(RPC)-> 流动计算架构(SOA)
以电商系统为例,按演变阶段介绍各个应用架构。该系统功能:用户模块、商品模块以及交易模块。
Phase 1:单机构建网站
单机构建网站,是一个高内聚版本,所有功能部署在一起。通过一个容器和JSP/Servlet技术或通过一些开源的框架如SSM以及SSH,通过数据库管理系统来存储数据。
单机下的系统结构如下:
优点:
1. 成本比较低,适合功能少又简单的应用场景。
缺点:
1. 无法适应高流量
2. 部署成本高
Phase 2: 数据库应用服务器分离
随着访问量上升,服务器的负载也随之逐渐提高,相较于单机系统的无法适应高流量,提升网站的负载能力成为了首要课题。若代码层面难以优化,通过增加机器的数量是提升整体性能一个方式。
优点:
1. 提高系统的负载能力
2. 较高的性价比
通过增加机器的数量,将数据库服务器和web服务器拆分开来。
优点:
1. 提高单机的负载能力
2. 提高单机的容灾能力
此架构下的系统结构:
Phase 3:分布式服务器
虽然我们已经将服务和存储分离,但随着访问量继续增加,单台应用服务器也无法满足需求。在假设数据库服务器没有压力的情况下,此时通过将服务器部署至更多的机器,即应用服务器从一台变成多台,把用户的请求分散到不同的服务器中进响应,进而提高负载能力。
而服务器之间不存在直接交互,所有的服务器都是依赖数据库各自对外提供服务。此架构下系统结构如下:
此阶段有4个问题随之而来:
1. 负载均衡的问题?
通常有以下几种解决方案:
1.1 HTTP重定向
即应用层的请求转发。当用户的请求到了HTTP重定向负载均衡服务器,服务器根据算法要求用户重定向,用户收到重定向请求后,再次请求真正的集群。
优点:简单易用
缺点:性能较差
1.2 DNS域名解析负载均衡
即在用户请求DNS服务器,获取域名对应的IP地址时,DNS服务器直接给出负载均衡后的服务器IP。
优点:交给DNS处理,无需人为维护负载均衡服务器
缺点:
1. 当一个应用服务器出现问题,无法及时通知DNS
2. DNS负载均衡的控制权在域名服务商,不利于管理
1.3 反向代理服务器
在用户的请求到达反向代理服务器时,由反向代理服务器根据算法转发到具体的服务器。
优点:部署简单
缺点:代理服务器可能成为性能瓶颈,针对大文件
1.4 IP层负载均衡
在请求到达负载均衡器后,负载均衡器通过修改请求的目的IP地址,从而实现请求的转发,做到负载均衡。
优点:性能更好
缺点:负载均衡器的宽带成为瓶颈
1.5 数据链路层负载均衡
在请求到达负载均衡器后,负载均衡器通过修改请求的MAC地址,从而做到负载均衡,与IP负载均衡不一样的是,当请求访问完服务器之后,直接返回客户。而无需再经过负载均衡器。
2. 调度的算法和策略?
常用的集群调度算法如下:
2.1 rr轮询调度算法
轮询分发请求。
优点:容易实现
缺点:未考虑每台服务器的处理能力
2.2 wrr加权调度算法
为每个服务器设置权值Weight,负载均衡调度器根据权值调度服务器,服务器被调用的次数跟权值成正比。
优点:考虑不同服务器处理能力不同
2.3 sh原地址散列算法
提取用户IP,根据散列函数得出一个key,再根据静态映射表,查处对应的value,即目标服务器IP。过目标机器超负荷,则返回空。
优点:实现同一个用户访问同一个服务器
2.4 lc最少连接算法
优先把请求转发给连接数少的服务器。
优点:使集群中各个服务器的负载更加均匀
2.5 dh目标地址散列算法
原理同上,只是现在提取的是目标地址的IP来做哈希。
优点:实现同一个用户访问同一个服务器
2.6 wlc加权最少连接算法
在lc的基础上,为每台服务器加上权值。算法为:(活动连接数 * 256 + 非活动连接数) ÷ 权重,计算出来的值小的服务器优先被选择。
优点:根据服务器的能力分配请求
2.7 sed最短期望延迟算法
其实sed跟wlc类似,区别是不考虑非活动连接数。算法为:(活动连接数 +1 ) * 256 ÷ 权重,同样计算出来的值小的服务器优先被选择。
2.8 nq永不排队算法
改进的sed算法。我们想一下什么情况下才能“永不排队”,那就是服务器的连接数为0的时候,那么假如有服务器连接数为0,均衡器直接把请求转发给它,无需经过sed的计算。
3. 集群请求返回模式问题?
3.1 NAT
负载均衡器接收用户的请求,转发给具体服务器,服务器处理请求返回给均衡器,均衡器再重新返回给用户。
3.2 DR
负载均衡器接收用户的请求,转发给具体服务器,服务器处理请求后直接返回给用户。
缺点:系统需支持IP Tunneling协议,不易跨平台
3.3 TUN
同DR,但无需IP Tunneling协议,跨平台性好,可以支持大部分系统都。
4. 集群Session一致性问题
用户如果每次访问到的服务器不一样,那么如何维护session的一致性?
4.1 Session Sticky
即把同一个用户在某一个会话中的请求,都分配到固定的某一台服务器中。
优点:易于实现
缺点:应用服务器重启则session消失
4.2 Session Replication
即在集群中复制session,使得每个服务器都保存有全部用户的session数据。
优点:减轻负载均衡服务器的压力
缺点:复制时网络带宽开销大,若访问量大的话session占用内存大且浪费。
4.3 Session数据集中存储
即利用数据库来存储session数据,实现了session和应用服务器的解耦。
优点:相比Session Replication的方案,集群间对于宽带和内存的压力大幅减少
缺点:需要额外维护存储session的数据库
4.4 Cookie Base
即把session存在cookie中,通过浏览器来告知应用服务器session,同样实现了session和应用服务器的解耦。
优点:实现简单,基本免维护
缺点:cookie长度限制,安全性低,带宽消耗
根据不同的场景,选择不同解决方案来应对上述这些问题后,此时系统结构:
Phase 4 :数据库读写分离化
同样的,随着访问量的增加,数据库的负载也在慢慢增大。对于这种情况,可以先考虑使用读写分离和主从复制的方式。
读写分离后的系统结构:
2个问题:
4.1 主从数据库之间数据同步问题
使用MySQL自带的Master + Slave的方式实现主从复制。
4.2 应用服务对数据源的选择问题
采用第三方数据库中间件。蚂蚁金服目前采用zdal作为数据库分库分表中间件。
Phase 5 : 缓存缓解读库压力
常用的缓存机制包括页面级缓存、应用数据缓存和数据库缓存。
5.1 页面缓存
除了页面缓存带来的性能提升外,对于并发访问且页面置换频率小的页面,应尽量使用页面静态化技术。例如HTML5的localstroage或者cookie。
缓存集群的调度算法最好采用一致性哈希算,以此提高命中率。
优点:减轻数据库的压力, 大幅度提高访问速度
缺点:需要维护缓存服务器,提高了编码的复杂性
5.2 应用层和数据库层的缓存
随着访问量的增加,会出现许多用户访问同一部分热门内容的情况,对于这些比较热门的内容,不需要每次都从数据库读取。可以通过缓存。例如,可以使用Google的开源缓存技术Guava或者使用Memecahed作为应用层的缓存,也可以使用Redis作为数据库层的缓存。
加入缓存后的系统结构:
Phase 6 :数据库水平拆分与垂直拆分
随着数据库的压力继续增加,数据库数据量的瓶颈越来越突出,此时,可以采取数据垂直拆分和水平拆分两种选择。
6.1 数据垂直拆分
即根据不同的业务数据拆分到不同的数据库中。
结合现在的例子,就是把交易、商品、用户的数据分开。
优点:
1. 解决了原来把所有业务放在一个数据库中的压力问题
2. 可以根据业务的特点进行更多的优化
缺点:
1. 需要维护多个数据库的状态一致性和数据同步。
垂直拆分同时存在着2个问题:
6.1.1 需考虑之前跨业务的事务
解决方案:
应该在应用层尽量避免跨数据库的分布式事务
6.1.2 跨数据库的Join
解决方案:
通过数据库中间件来解决。
垂直拆分后的系统结构:
6.2 数据库水平拆分
即把同一个表中的数据拆分到两个甚至多个数据库中。产生数据水平拆分的原因是某个业务的数据量或者更新量到达了单个数据库的瓶颈。
水平分库存在3个问题:
6.2.1 SQL路由
访问用户信息的应用系统需要解决SQL路由的问题,因为现在用户信息分在了两个数据库中,需要在进行数据操作时了解需要操作的数据在哪里。
解决方案:
通过可以解决第三方数据库中间件,如MyCat。MyCat可以通过SQL解析模块对SQL进行解析,再根据我们的配置,把请求转发到具体的某个数据库。
6.2.2 主键的处理发生变化
例如原来自增字段,现在不能简单地继续使用。
解决问题方案:
可以通过UUID保证唯一或自定义ID方案来解决。
6.2.3 不易于分页查询
解决问题方案:
可以通过第三方数据库中间件,如 MyCat也提供了丰富的分页查询方案,比如先从每个数据库做分页查询,再合并数据做一次分页查询等等。
数据水平拆分后的结构如下:
Phase 7 :应用的拆分
按微服务拆分应用
随着业务的发展,业务越来越多,应用越来越大。我们需要考虑如何避免让应用越来越臃肿。这就需要把应用拆开,从一个应用变为俩个甚至更多。
还是以上面的例子,可以把用户、商品、交易拆分开。变成“用户、商品”和“用户,交易”两个子系统。
拆分后的结构:
产生的问题:产生相同冗余的代码
如用户相关的代码,商品和交易都需要用户信息,所以在两个系统中都保留差不多的操作用户信息的代码。如何保证这些代码可以复用是一个需要解决的问题。
解决方案:
通过服务化SOA的解决频繁公共的服务。
7.1 SOA服务化
为了解决上面拆分应用后所出现的问题,把公共的服务拆分出来,形成一种服务化的模式,简称SOA。
优点:
1. 相同的代码不会散落在不同的应用中,这些代码实现放在各个服务中心,易于更好的维护
2. 把对数据库的交互业务放在各个服务中心,让前端web应用更专心与浏览器的交互工作
可以通过引入消息中间件来解决远程的服务调用。
Phase 8 :引入消息中间件
系统中可能会出现不同语言开发的子模块和部署在不同平台的子系统。此时需要一个平台来传递可靠的,与平台和语言无关的数据,并能够把负载均衡透明化,能在调用过程中收集并分析调用数据,推测出网站的访问增长率等等一系列需求,对于网站应该如何成长做出预测。
开源消息中间件有阿里的Dubbo,可以搭配Google开源的分布式程序协调服务Zookeeper实现服务器的注册与发现。
引入消息中间件后的结构:
引用
应用架构演进
服务化架构演进
阿里分布式服务框架Dubbo的架构总结
微服务理论之一:应用架构演进史
浅谈大型分布式Web系统的架构演进
面向服务的体系架构(SOA)—架构篇
分布式服务框架设计
浅析应用架构演进
简单聊聊SOA和微服务
应用架构演进《一》
分布式架构演进