当前位置: 首页 > 后端技术 > Java

6000字+,助你了解互联网架构演进!

时间:2023-04-01 19:33:00 Java

作者:小M\来源:https://cnblogs.com/xiaoMzjm/...前言下面我们以javaweb为例搭建一个简单的电子商务系统,看看这个系统是如何一步步演进的。系统功能:用户模块:用户注册和管理商品模块:商品展示和管理交易模块:交易创建和管理阶段1.单机网站建设在网站前期,我们经常运行我们所有的程序和软件在一台计算机上。此时我们使用一个容器,比如tomcat,jetty,jboos,然后直接使用JSP/servlet技术,或者使用一些开源框架,比如maven+spring+struct+hibernate,maven+spring+springmvc+mybatis;最后选择一个数据库管理系统来存储数据,比如mysql、sqlserver、oracle,然后通过JDBC连接和操作数据库。在同一台机器上加载以上所有软件,运行应用程序,可以看作是一个小系统。此时系统运行结果如下:Phase2.应用服务器与数据库分离随着网站的上线,访问量逐渐增加,服务器的负载也逐渐增加。在服务器不超载的情况下,我们应该准备升级网站的负载能力。如果我们的代码层面难以优化,在不提升单机性能的情况下,增加机器是一个很好的方式。不仅可以有效提高系统的负载能力,而且性价比高。额外的机器将用于什么?此时,我们可以将数据库和web服务器分离,这样既提高了单机的负载能力,又提高了容灾能力。应用服务器与数据库分离后的架构如下图所示:阶段三:应用服务器集群随着访问量不断增加,单个应用服务器无法满足需求。假设数据库服务器没有压力,我们可以将应用服务器从一台改为两台或多台,将用户请求分散到不同的服务器上,从而增加负载能力。多个应用服务器之间没有直接交互,都依赖数据库对外提供服务。众所周知的故障转移软件是keepalived。keepalived是一个类似于三四七层交换机制的软件。它不是针对特定软件故障转移的专属产品,而是可以应用于各种软件的产品。keepalived结合ipvsadm还可以做负载均衡,可谓神器。我们以添加应用服务器为例。添加后的系统结构图如下:当系统演进到这里,会出现以下四个问题:谁将用户的请求转发给具体的应用服务器,有什么转发算法?应用服务器如何返回用户的请求?如果用户每次访问不同的服务器,如何保持会话的一致性?下面看一下问题的解决方法:1.第一个问题是负载均衡的问题。两种解决方案:1.http重定向。HTTP重定向是应用层的请求转发。用户的请求实际上已经到达了HTTP重定向负载均衡服务器。服务器要求根据算法重定向用户。用户收到重定向请求后,再次请求真实集群。优点:简单。缺点:性能较差。2、DNS域名解析负载均衡。DNS域名解析负载均衡是指当用户请求DNS服务器获取该域名对应的IP地址时,DNS服务器直接提供负载均衡后的服务器IP。优点:交给DNS,我们不需要维护负载均衡服务器。缺点:当应用服务器宕机时,无法及时通知DNS,DNS负载均衡的控制权在域名服务商手中,无法对网站进行改进和更有力的管理。3.反向代理服务器。当用户的请求到达反向代理服务器时(已经到达网站机房),反向代理服务器根据算法转发给具体的服务器。常用的apache和nginx都可以作为反向代理服务器。优点:易于部署。缺点:代理服务器可能成为性能瓶颈,尤其是在一次上传大文件时。4.IP层负载均衡。请求到达负载均衡器后,负载均衡器修改请求的目的IP地址转发请求,实现负载均衡。优点:更好的性能。缺点:负载均衡器的带宽成为瓶颈。5.数据链路层负载均衡。请求到达负载均衡器后,负载均衡器通过修改请求的mac地址实现负载均衡。与IP负载均衡不同的是,当请求访问完服务器后,直接返回给客户端。无需通过负载均衡器。2、第二个问题是集群调度算法问题。常见的调度算法有10种。1.rr循环调度算法。顾名思义,轮询分发请求。优点:实现简单缺点:不考虑每台服务器的处理能力2.wrr加权调度算法。我们为每个服务器设置一个权重,负载均衡调度器根据权重对服务器进行调度,服务器被调用的次数与权重成正比。优点:考虑到服务器处理能力的差异3.sh原始地址哈希:提取用户IP,根据哈希函数得到一个key,然后根据静态映射表查对应的值,即目标服务器IP.如果目标机器过载,则返回空。4、dh目标地址哈希:同上,只是现在提取目标地址的IP进行哈希。优点:以上两种算法可以实现同一个用户访问同一个服务器。5、lc最少连接。优先将请求转发到连接较少的服务器。优点:使集群中各服务器的负载更加均匀。6.wlc加权最少连接。在lc的基础上,给各个server增加权重。算法为:(活跃连接数*256+不活跃连接数)÷权重,计算出的值较小的服务器优先被选中。优点:可以根据服务器的能力分发请求。7.sed最短的预期延迟。其实sed和wlc类似,不同的是不考虑不活跃的连接数。算法为:(活跃连接数+1)*256÷权重,计算出的值较小的服务器优先被选中。8.nq从不排队。改进的sed算法。我们想一下在什么情况下我们可以“永不排队”,即当服务器连接数为0时,那么如果服务器连接数为0,balancer会直接把请求转发给它,而不经过计算sed。9.LBLC基于局部最少连接。balancer根据请求的目的IP地址找出最近使用过IP地址的服务器,将请求转发给它。如果服务器过载,则采用最少连接数算法。10.LBLCR基于位置的最小连接与复制。平衡器根据请求的目的IP地址找出该IP地址最近使用的“服务器组”。注意不是特定的服务器,然后从连接数最少的组中选择特定的服务器。请转发。如果服务器过载,则根据最小连接数算法,从集群中不属于服务器组的服务器中找出一台服务器,加入服务器组,将请求转发给它。3、第三个问题是集群模式问题。一般有三种解决方案:1.NAT:负载均衡器接收到用户的请求,转发给特定的服务器。服务器处理完请求后返回给平衡器,平衡器再返回给用户。2.DR:负载均衡器接收到用户的请求,转发给特定的服务器。服务器出来播放请求后,直接返回给用户。系统需要支持IPTunneling协议,难以跨平台。3.TUN:同上,但不带IPTunneling协议,跨平台性好,大部分系统都支持。4.第四个问题是session问题。一般有4种解决方法:1.SessionSticky。会话粘性就是把同一个用户在某个会话中的请求分配到一个固定的服务器上,这样我们就不用解决跨服务器会话的问题了。常见的算法是ip_hash方法,也就是上面提到的两种哈希算法。优点:实施简单。缺点:应用服务器重启后session消失。2.会话复制。会话复制就是在集群中复制会话,让每个服务器保存所有用户的会话数据。优点:减轻负载均衡服务器的压力,不需要实现ip_hasp算法转发请求。缺点:复制时宽带开销大,访问量大时session占用内存大,浪费。3、Session数据集中存储:Session数据集中存储就是利用数据库来存储Session数据,实现Session与应用服务器的解耦。优点:与会话复制方案相比,集群间带宽和内存压力大大降低。缺点:需要维护存储会话的数据库。4、CookieBase:CookieBase是将session保存在cookie中,浏览器可以告诉应用服务器我的session是什么,同时也实现了session和应用服务器的解耦。优点:实现简单,基本免维护。缺点:cookie长度限制,安全性低,占用带宽。值得一提的是,目前nginx支持的负载均衡算法有wrr、sh(支持一致性哈希)、fair(我觉得可以归功于lc)。但是,如果nginx用作平衡器,它也可以用作静态资源服务器。keepalived+ipvsadm更强大,目前支持的算法有:rr、wrr、lc、wlc、lblc、sh、dhkeepalived支持集群模式:NAT、DR、TUNnginx本身不提供session同步方案,但是apache提供了Session共享支持。好了,解决了以上问题之后,系统结构如下:阶段4,数据库读写分离上面我们一直假设数据库负载是正常的,但是随着访问量的增加,数据库负载也在慢慢增加。那么有人可能立马想到,就像应用服务器一样,把数据库一分为二,然后做负载均衡。但是对于数据库,事情就没那么简单了。如果我们简单地将数据库一分为二,然后将数据库请求分别加载到机器A和机器B上,显然会造成两个数据库数据不一致的问题。那么对于这种情况,我们可以先考虑使用读写分离的方式。读写分离后的数据库系统结构如下:这种结构变化也会带来两个问题:主从数据库之间的数据同步问题和数据源选择的应用问题解决方案:我们可以使用MYSQL自带的master+slave方式实现主从复制。使用第三方数据库中间件,如mycat。mycat由cobar发展而来,cobar是阿里开源的数据库中间件,后来停止开发。mycat是国内一款比较优秀的mysql开源数据库分库分表中间件。第五阶段,利用搜索引擎缓解阅读数据库的压力。如果数据库是用来读数据库的,对于模糊搜索往往是无能为力的。即使做了读写分离,也没有解决这个问题。以我们提到的交易网站为例,发布的产品存储在数据库中,用户最常使用的功能是搜索产品,尤其是根据产品标题查找对应的产品。对于这种需求,我们一般都是通过like函数来实现的,但是这种方式的成本是非常高的。此时我们可以利用搜索引擎的倒排索引来完成。该搜索引擎具有以下优点:可以大大提高查询速度。引入搜索引擎也会带来以下开销:大量的维护工作,我们需要自己实现索引构建过程,设计全/增构建方式来满足非实时和实时查询需求.需要维护搜索引擎集群。搜索引擎不能取代数据库。它们解决了某些场景下的“阅读”问题。是否引入搜索引擎需要综合考虑整个系统的需求。引入搜索引擎后的系统结构如下:阶段六、使用缓存缓解读库压力1、后台应用层和数据库层的缓存。对于比较热门的内容,没有必要每次都从数据库中读取。我们可以使用缓存技术,比如谷歌开源的缓存技术guava或者memcacahe作为应用层缓存,或者redis作为数据库层缓存。另外,在某些场景下,关系型数据库并不是很适合。比如我想做一个“限制每天输入错误密码的次数”的功能。思路是记录用户在登录IP时是否登录错误以及错误次数,那么这个数据应该放在哪里呢?如果放在内存中,显然会占用过多的内容;如果是放在关系型数据库中,那么就需要创建数据库表,恢复对应的javabean,写SQL等等。并分析我们要存储的数据,无非就是{ip:errorNumber}这样的key:value数据。对于这类数据,我们可以使用NOSQL数据库代替传统的关系型数据库。2.页面缓存除了数据缓存,还有页面缓存。比如使用HTML5的localstroage或者cookies。优点:减轻数据库压力,大大提高访问速度缺点:需要维护缓存服务器,增加编码复杂度。值得一提的是:缓存集群的调度算法不同于上面提到的应用服务器和数据库。最好使用“一致性哈希算法”,这样可以提高命中率。这个就不多说了,有兴趣的可以参考相关资料。添加缓存后的结构:第七阶段,数据库水平拆分和垂直拆分我们的网站发展到现在,交易数据、商品数据、用户数据还在同一个数据库中。虽然采用了增加缓存和读写分离的方式,但是随着数据库压力的不断增加,数据库的瓶颈也越来越突出。这时候,我们可以选择纵向或者横向拆分数据。7.1.数据的垂直拆分垂直拆分是指将数据库中的不同业务数据拆分到不同的数据库中。结合现在的例子,就是把交易数据、商品数据、用户数据分开。优点:解决了所有业务放在一个数据库中的压力问题。可以根据业务的特点做更多的优化缺点:需要维护多个数据库在代码中尽量控制数据库。我们可以通过第三方应用来解决,比如上面提到的mycat,mycat提供了丰富的跨库join解决方案,具体可以参考mycat官方文档。垂直拆分后的结构如下:7.2.数据水平拆分数据水平拆分就是将同一个表中的数据拆分到两个或多个数据库中。数据水平拆分的原因是某项业务的数据量或更新量已经达到了单个数据库的瓶颈。这时可以将表拆分到两个或多个数据库中。优点:如果能够克服以上问题,那么就能够很好的应对数据量和写入量的增长。问题:访问用户信息的应用系统需要解决SQL路由的问题,因为现在用户信息分为两个数据库,在进行数据操作的时候需要知道要操作的数据在哪里。主键的处理也发生了变化,比如原来的自增字段现在不能简单的继续使用了。如果需要分页,那就麻烦了。问题解决:我们还是可以解决第三方中间件,比如mycat。mycat可以通过SQL解析模块来解析我们的SQL,然后根据我们的配置将请求转发到具体的数据库。我们可以通过UUID保证唯一或自定义ID方案来解决。mycat也提供了丰富的分页查询方案,比如先从各个数据库做分页查询,然后再组合数据做分页查询等。数据水平拆分后的结构:stage8、应用拆分8.1、拆分应用随着业务的发展,业务越来越多,应用越来越大。我们需要思考如何避免让应用程序越来越臃肿。这就需要将应用拆解,从一个应用拆成两个或更多。仍然使用我们上面的示例,我们可以将用户、产品和交易分开。成为“用户、商品”和“用户、交易”两个子系统。拆分结构:问题:这种拆分之后,可能会有一些相同的代码,比如用户相关的代码,商品和交易都需要用户信息,所以两个系统都保留了类似的操作用户信息的代码。如何保证这些代码可以复用,是一个需要解决的问题。解决问题:走面向服务的路线解决问题8.2。走面向服务的道路为了解决上面应用拆分后出现的问题,我们将公共服务进行拆分,形成面向服务的模型,简称SOA。采用服务后的体系结构:优点:相同的代码不会分散在不同的应用中,这些实现都放在各个服务中心,这样可以更好的维护代码。我们把与数据库的交互放在各个服务中心,让“前端”的web应用更加关注与浏览器交互的工作。问题:如何进行远程服务调用解决方案:我们可以通过引入消息中间件来解决问题Stage9.引入消息中间件随着网站的不断发展,我们的系统中可能会出现使用不同语言开发的子模块和部署Subsystems上不同的平台。这时候,我们需要一个平台来传递可靠的、平台和语言无关的数据,并使负载平衡透明化,在调用过程中收集调用数据并进行分析,推断网站访问量的增长率等。需求,预测网站应该如何发展。开源的消息中间件包括阿里的dubbo,可以与谷歌开源的分布式程序协调服务zookeeper配合使用,实现服务器注册和发现。引入消息中间件后的结构:10.总结上面的演进过程只是一个例子,并不适用于所有的网站。在实践中,网站的演进过程与自身业务和遇到的不同问题密切相关,并没有固定的模式。只有认真分析,不断探索,才能找到适合自己网站的架构。近期热点文章推荐:1.1000+Java面试题及答案(2022最新版)2.厉害了!Java协程来了。..3.SpringBoot2.x教程,太全面了!4.不要用爆破爆满画面,试试装饰者模式,这才是优雅的方式!!5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!