咱们以 Java Web
为例,来搭建一个简单的电商系统,看看这个系统能够如何一步步演变。前端
该系统具有的功能:算法
网站的初期,咱们常常会在单机上跑咱们全部的程序和软件。此时咱们使用一个容器,如Tomcat
、Jetty
、Jboss
,而后直接使用JSP/Servlet技术,或者使用一些开源的框架如Maven + Spring + Struts + Hibernate
、Maven + Spring + Spring MVC + Mybatis
。最后再选择一个数据库管理系统来存储数据,如MySQL
、SqlServer
、Oracle
,而后经过JDBC
进行数据库的链接和操做。数据库
把以上的全部软件包括数据库、应用程序都装载同一台机器上,应用跑起来了,也算是一个小系统了。此时系统结果以下:编程
随着网站的上线,访问量逐步上升,服务器的负载慢慢提升,在服务器尚未超载的时候,咱们应该就要作好准备,提高网站的负载能力。假如咱们代码层面已难以优化,在不提升单台机器的性能的状况下,采用增长机器是一个不错的方式,不只能够有效地提升系统的负载能力,并且性价比高。后端
增长的机器用来作什么呢?此时咱们能够把数据库服务器和Web服务器拆分开来,这样不只提升了单台机器的负载能力,也提升了容灾能力。浏览器
应用服务器与数据库分开后的架构以下图所示:缓存
随着访问量继续增长,单台应用服务器已经没法知足需求了。在假设数据库服务器没有压力的状况下,咱们能够把应用服务器从一台变成了两台甚至多台,把用户的请求分散到不一样的服务器中,从而提升负载能力。而多台应用服务器之间没有直接的交互,他们都是依赖数据库各自对外提供服务。著名的作故障切换的软件有KeepAlived
,KeepAlived
是一个相似于Layer三、四、7交换机制的软件,他不是某个具体软件故障切换的专属品,而是能够适用于各类软件的一款产品。KeepAlived
配合上ipvsadm
又能够作负载均衡,可谓是神器。安全
咱们以增长了一台应用服务器为例,增长后的系统结构图以下:服务器
系统演变到这里,将会出现下面四个问题:cookie
针对以上问题,经常使用的解决方案以下:
通常如下有5种解决方案:
一、HTTP重定向
HTTP
重定向就是应用层的请求转发。用户的请求其实已经到了HTTP
重定向负载均衡服务器,服务器根据算法要求用户重定向,用户收到重定向请求后,再次请求真正的集群
二、DNS域名解析负载均衡
DNS
域名解析负载均衡就是在用户请求DNS服务器,获取域名对应的IP地址时,DNS服务器直接给出负载均衡后的服务器IP。
DNS
,不用咱们去维护负载均衡服务器;DNS
,并且DNS
负载均衡的控制权在域名服务商那里,网站没法作更多的改善和更强大的管理。三、反向代理服务器
在用户的请求到达反向代理服务器时(已经到达网站机房),由反向代理服务器根据算法转发到具体的服务器。经常使用的
Apache
,Nginx
均可以充当反向代理服务器。
四、IP层负载均衡
在请求到达负载均衡器后,负载均衡器经过修改请求的目的
IP
地址,从而实现请求的转发,作到负载均衡。
五、数据链路层负载均衡
在请求到达负载均衡器后,负载均衡器经过修改请求的
MAC
地址,从而作到负载均衡,与IP
负载均衡不同的是,当请求访问完服务器以后,直接返回客户。而无需再通过负载均衡器。
一、rr轮询调度算法
顾名思义,轮询分发请求。
二、wrr加权调度算法
咱们给每一个服务器设置权值
Weight
,负载均衡调度器根据权值调度服务器,服务器被调用的次数跟权值成正比。
三、sh原地址散列算法
提取用户
IP
,根据散列函数得出一个key
,再根据静态映射表,查处对应的value
,即目标服务器IP
。过目标机器超负荷,则返回空。
四、dh目标地址散列算法
原理同上,只是如今提取的是目标地址的
IP
来作哈希。
五、lc最少链接算法
优先把请求转发给链接数少的服务器。
六、wlc加权最少链接算法
在
lc
的基础上,为每台服务器加上权值。算法为:(活动链接数 * 256 + 非活动链接数) ÷ 权重
,计算出来的值小的服务器优先被选择。
七、sed最短时间望延迟算法
其实sed跟wlc相似,区别是不考虑非活动链接数。算法为:
(活动链接数 +1 ) * 256 ÷ 权重
,一样计算出来的值小的服务器优先被选择。
八、nq永不排队算法
改进的
sed
算法。咱们想一下什么状况下才能“永不排队”,那就是服务器的链接数为0的时候,那么假若有服务器链接数为0,均衡器直接把请求转发给它,无需通过sed的计算。
九、LBLC基于局部性最少链接算法
负载均衡器根据请求的目的
IP
地址,找出该IP
地址最近被使用的服务器,把请求转发之。若该服务器超载,最采用最少链接数算法。
十、LBLCR带复制的基于局部性最少链接算法
负载均衡器根据请求的目的IP地址,找出该IP地址最近使用的“服务器组”,注意,并非具体某个服务器,而后采用最少链接数从该组中挑出具体的某台服务器出来,把请求转发之。若该服务器超载,那么根据最少链接数算法,在集群的非本服务器组的服务器中,找出一台服务器出来,加入本服务器组,而后把请求转发。
一、NAT
负载均衡器接收用户的请求,转发给具体服务器,服务器处理完请求返回给均衡器,均衡器再从新返回给用户。
二、DR
负载均衡器接收用户的请求,转发给具体服务器,服务器出来玩请求后直接返回给用户。须要系统支持
IP Tunneling
协议,难以跨平台。
三、TUN
同上,但无需
IP Tunneling
协议,跨平台性好,大部分系统均可以支持。
一、Session Sticky
Session sticky
就是把同一个用户在某一个会话中的请求,都分配到固定的某一台服务器中,这样咱们就不须要解决跨服务器的session
问题了,常见的算法有ip_hash
算法,即上面提到的两种散列算法。
二、Session Replication
Session replication
就是在集群中复制session
,使得每一个服务器都保存有所有用户的session
数据。
Session
占用内存大且浪费。三、Session数据集中存储
Session
数据集中存储就是利用数据库来存储session
数据,实现了session
和应用服务器的解耦。
Session replication
的方案,集群间对于宽带和内存的压力大幅减小;Session
的数据库。四、Cookie Base
Cookie base
就是把Session
存在Cookie
中,由浏览器来告诉应用服务器个人session
是什么,一样实现了session
和应用服务器的解耦。
值得一提的是:
Nginx
目前支持的负载均衡算法有wrr
、sh
(支持一致性哈希)、fair
(lc)。但Nginx
做为均衡器的话,还能够一同做为静态资源服务器。Keepalived + ipvsadm
比较强大,目前支持的算法有:rr
、wrr
、lc
、wlc
、lblc
、sh
、dh
Keepalived
支持集群模式有:NAT
、DR
、TUN
Nginx
自己并无提供session
同步的解决方案,而Apache
则提供了session
共享的支持。解决了以上的问题以后,系统的结构以下:
上面咱们老是假设数据库负载正常,但随着访问量的的提升,数据库的负载也在慢慢增大。那么可能有人立刻就想到跟应用服务器同样,把数据库一份为二再负载均衡便可。
但对于数据库来讲,并无那么简单。假如咱们简单的把数据库一分为二,而后对于数据库的请求,分别负载到A机器和B机器,那么显而易见会形成两台数据库数据不统一的问题。那么对于这种状况,咱们能够先考虑使用读写分离和主从复制的方式。
读写分离后的系统结构以下:
这个结构变化后也会带来两个问题:
解决方案:
MySQL
自带的Master + Slave
的方式实现主从复制。MyCat
。MyCat
是从Cobar
发展而来的,而Cobar
是阿里开源的数据库中间件,后来中止开发。MyCat
是国内比较好的MySql
开源数据库分库分表中间件。数据库作读库的话,经常对模糊查找力不从心,即便作了读写分离,这个问题还未能解决。以咱们所举的交易网站为例,发布的商品存储在数据库中,用户最常使用的功能就是查找商品,尤为是根据商品的标题来查找对应的商品。对于这种需求,通常咱们都是经过like
功能来实现的,可是这种方式的代价很是大,并且结果很是不许确。此时咱们可使用搜索引擎的倒排索引来完成。
搜索引擎具备的优势:它可以大大提升查询速度和搜索准确性。
搜索引擎并不能替代数据库,它解决了某些场景下的精准、快速、高效的“读”操做,是否引入搜索引擎,须要综合考虑整个系统的需求。
引入搜索引擎后的系统结构以下:
经常使用的缓存机制包括页面级缓存、应用数据缓存和数据库缓存。
随着访问量的增长,逐渐出现了许多用户访问同一部分热门内容的状况,对于这些比较热门的内容,不必每次都从数据库读取。咱们可使用缓存技术,例如可使用Google的开源缓存技术Guava
或者使用Memecahed
做为应用层的缓存,也可使用Redis
做为数据库层的缓存。
另外,在某些场景下,关系型数据库并非很适合,例如我想作一个“每日输入密码错误次数限制”的功能,思路大概是在用户登陆时,若是登陆错误,则记录下该用户的
IP
和错误次数,那么这个数据要放在哪里呢?假如放在内存中,那么显然会占用太大的内容;假如放在关系型数据库中,那么既要创建数据库表,还要简历对应的Java bean
,还要写SQL
等等。而分析一下咱们要存储的数据,无非就是相似{ip:errorNumber}
这样的key:value
数据。对于这种数据,咱们能够用NOSQL
数据库来代替传统的关系型数据库。
除了数据缓存,还有页面缓存。好比使用HTML5
的localstroage
或者Cookie
。除了页面缓存带来的性能提高外,对于并发访问且页面置换频率小的页面,应尽可能使用页面静态化技术。
值得一提的是:
缓存集群的调度算法不一样与上面提到的应用服务器和数据库。最好采用一致性哈希算,这样才能提升命中率。
加入缓存后的系统结构以下:
咱们的网站演进到如今,交易、商品、用户的数据都还在同一个数据库中。尽管采起了增长缓存和读写分离的方式,但随着数据库的压力继续增长,数据库数据量的瓶颈愈来愈突出,此时,咱们能够有数据垂直拆分和水平拆分两种选择。
垂直拆分的意思是把数据库中不一样的业务数据拆分到不一样的数据库中,结合如今的例子,就是把交易、商品、用户的数据分开。
优势:
缺点:
问题:
Join
。解决问题方案:
MyCat
,MyCat
提供了丰富的跨库Join
方案,详情可参考MyCat
官方文档。数据垂直拆分后的结构以下:
数据水平拆分就是把同一个表中的数据拆分到两个甚至多个数据库中。产生数据水平拆分的缘由是某个业务的数据量或者更新量到达了单个数据库的瓶颈,这时就能够把这个表拆分到两个或更多个数据库中。
优势:
问题:
SQL路由
的问题,由于如今用户信息分在了两个数据库中,须要在进行数据操做时了解须要操做的数据在哪里。解决问题方案:
MyCat
。MyCat
能够经过SQL
解析模块对咱们的SQL
进行解析,再根据咱们的配置,把请求转发到具体的某个数据库。 咱们能够经过UUID
保证惟一或自定义ID方案来解决。MyCat
也提供了丰富的分页查询方案,好比先从每一个数据库作分页查询,再合并数据作一次分页查询等等。数据水平拆分后的结构以下:
随着业务的发展,业务愈来愈多,应用愈来愈大。咱们须要考虑如何避免让应用愈来愈臃肿。这就须要把应用拆开,从一个应用变为俩个甚至更多。仍是以咱们上面的例子,咱们能够把用户、商品、交易拆分开。变成“用户、商品”和“用户,交易”两个子系统。
拆分后的结构:
问题:
这样拆分后,可能会有一些相同的代码,如用户相关的代码,商品和交易都须要用户信息,因此在两个系统中都保留差很少的操做用户信息的代码。如何保证这些代码能够复用是一个须要解决的问题。
解决问题:
经过走服务化SOA的路线来解决频繁公共的服务。
为了解决上面拆分应用后所出现的问题,咱们把公共的服务拆分出来,造成一种服务化的模式,简称SOA
。
采用服务化以后的系统结构:
优势:
问题:
如何进行远程的服务调用?
解决方法:
能够经过下面的引入消息中间件来解决。
随着网站的继续发展,的系统中可能出现不一样语言开发的子模块和部署在不一样平台的子系统。此时咱们须要一个平台来传递可靠的,与平台和语言无关的数据,而且可以把负载均衡透明化,能在调用过程当中收集并分析调用数据,推测出网站的访问增加率等等一系列需求,对于网站应该如何成长作出预测。开源消息中间件有阿里的Dubbo
,能够搭配Google开源的分布式程序协调服务Zookeeper
实现服务器的注册与发现。
引入消息中间件后的结构:
以上的演变过程只是一个例子,并不适合全部的网站,实际中网站演进过程与自身业务和不一样遇到的问题有密切的关系,没有固定的模式。只有认真的分析和不断地探究,才能发现适合本身网站的架构。
欢迎关注技术公众号: 零壹技术栈
本账号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。