8张图带你了解大型应用架构演进历程

Wesley13
• 阅读 415

最简单的架构

刚开始应用没有太多访问量,所以只需要一台服务器,这时候的架构如下图:

8张图带你了解大型应用架构演进历程

应用程序、文件、数据库往往都部署在一台服务器上。应用程序可以采用Java开发,部署在Tomcat服务器上,数据库可以使用开源的MySQL


应用与数据服务分隔

随着应用的业务越来越复杂,应用访问量越来越大,导致性能越来越差,存储空间严重不足,这时候我们考虑把服务增加到三台(能通过加机器解决的问题都不是问题);分离出应用服务器、数据库服务器、文件服务器。

  • 应用服务器需要处理大量的访问,所以需要性能更好的CPU
  • 数据库服务器需要存储大量的数据以及快速的检索,所以需磁盘的检索速度较快以及存储空间大
  • 文件服务器需要存储上传的文件,需要更大的磁盘;现在通常情况下会选择第三方的存储服务

8张图带你了解大型应用架构演进历程

根据每个服务器对应的场景,配置服务器后应用的性能能够大大提高,更好的支持业务的发展。但是随之业务的发展,访问量的增大,这种架构又将再次面临挑战,应用服务器处理能力下降,存储空间不足


应用服务器集群

在高并发,大流量的情况下,一台服务器是肯定处理不过来的,这个时候增加服务器,部署集群提供服务,来分担每台服务器的压力。部署集群的另一个好处是可伸缩行,比如当遇到了双11大流量的场景下,可以增加服务器分摊流量,等双11过后,减少服务器节约成本。架构如下:

8张图带你了解大型应用架构演进历程

如果应用服务器是Tomcat,那么可以部署一个Tomcat的集群,外部在部署一个负载均衡器,可以采用随机、轮询或者一致性哈希算法达将用户的请求分发到不同应用服务集群;通常选择的免费的负载均衡是nginx。在这种架构下,应用服务器的负载将不会是整个应用的瓶颈点;

虽然应用程序的处理速度在这种架构下提升了许多,但是又会暴露一个问题,数据库的压力大大增大,导致访问响应延迟,影响整个应用的性能。 这种架构还有个问题,通常应用是有状态的,需要记录用户的登录信息,如果每次用户的请求都是随机路由到后端的应用服务器,那么用户的会话将会丢失;解决这个问题两个方案:

  • 采用一致性hash把用户的请求路由到同一个Tomcat,如果有一台服务器跪了,那么这台服务器上面的用户信息将会丢失
  • Tomcat集群之间通过配置session复制,达到共享,此方案效率较低

两个方案都不是很好,那么还有其他的方案吗?请继续往下看


缓存

根据二八原则,80%的的业务都是集中访问20%的数据,这20%的数据通常称为热点数据,但是这20%的数据占用的内存也不会小,如果每个应用服务器都存放一份,有些浪费存储空间,所以这时候需要考虑加入分布式缓存服务器(常用的是Redis);当引入了分布式缓存服务器,再来看上面那个方案的问题,就可以解决了,把用户的会话存放到缓存服务器,不仅可以防止用户数据丢失,效率也不低;架构图如下:

8张图带你了解大型应用架构演进历程

由于分布式缓存服务器毕竟存放在远程,需要经过网络,所以取数据还是要花一点时间;本地缓存访问速度更快,但是内存空间有限,并且还会出现和应用程序争抢资源;所以这种架构搭配了分布式缓存和本地缓存,本地缓存存放少量常用热点数据,当本地缓存中没有命中时在去集中式缓存取

在引进缓存之后,数据库的访问压力可以的一定的缓解


数据库读写分离

虽然在加入了缓存之后,部分数据可以直接走缓存,不需要访问数据库,但是任然会有一些请求,会访问数据库,比如:缓存失效,缓存未命中;当流量大的时候,数据库的访问量也不小。这时候我们需要考虑搭建数据库集群,读写分离

8张图带你了解大型应用架构演进历程

当应用服务器有写操作时,访问主库,当应用程序有读操作时,访问从库;大多数的应用都是读的操作远远大于写的操作,所以可以配置数据库一主多从来分担数据库的压力;为了让应用程序对应主库和从库无感知,通常需要引入一些读写分离的框架做一个统一的数据访问模块。

这种架构通常需要警惕的一个问题是主从延迟,当在高并发的场景下,主库刚写成功,数据库还未成功同步完从库,这时候另一个请求进入读取数据发现不存在;解放方案是在应用程序中高并发的场景下设置强制走主库查询

> 兄弟们,请不要白嫖哦,文章看一半,请先点个赞


反向代理和CDN

假如随着业务的不断扩大,全国各地都会使用到我们的应用,由于各地区的网络情况不同,所以有的人请求响应速度快,有的人请求响应速度慢,这会严重的影响到用户的体验。为了提高响应速度需要引入反向代理和CDN;CDN和反向代理都是采用的缓存,目的:

  • 尽可能快的把数据呈现给用户
  • 减轻后端服务器的压力

架构图如下:

8张图带你了解大型应用架构演进历程

CDN: 部署在网络提供商的机房,当用户来访问的时候,从距离用户最近的服务器返回数据,尽快呈现给用户;通常情况下在CDN中缓存的是静态资源(html,js,css),达到动静分离;但是有时候遇到了某些数据访问量特别大的时候,后端会生成静态资源放入到CDN,比如:商城的首页,每个用户进入都需要访问的页面,如果每次请求都进入到后端,那么服务器的压力肯定不小,这种情况下会把首页生成静态的文件缓存到cdn和反向代理服务器

反向代理:部署在应用的中心机房,通常也是缓存的静态资源,当用户通过CDN未请求到需要的数据时,先进入反向代理服务器,如果有缓存用户访问的数据,那么直接返回给用户;这里也有特殊情况,对于有些场景下的热点数据,在这里根据用户的请求去分布式缓存服务器中获取,能拿到就直接返回。

这种架构已经把缓存做到了4级

  • 第一级:CDN 缓存静态资源
  • 第二级:反向代理缓存静态资源以及部分热点数据
  • 第三级:应用服务器的本地缓存
  • 第四级:分布式缓存服务器

通常情况下经过了这4级缓存,能够进入到数据库的请求也不多了,很好的释放了数据库的压力


搜索引擎和NoSQL

随着业务的不断扩大,对于数据的存储和查询的需求也越来越复杂,通常情况我们需要引入非关系型数据库,比如搜索引擎和NoSQL数据库

8张图带你了解大型应用架构演进历程

有时候我们的查询场景很复杂,需要查询很多数据表,经过一系列的计算才能完成,这时候可以考虑通过数据同步工具(比如canal)拉去数据到大数据平台,使用批处理框架离线计算,把输出的结果存放到搜索引擎或者NoSQL数据库中,应用程序直接查询计算的结果返回给用户。也有可能我们需要汇总多个表的数据做一张宽表,方便应用程序查询

由于引入的数据存储方式增多,为了减轻应用程序的管理多个数据源的麻烦,需要封装统一数据访问模块,如果使用的时Java,可以考虑spring-data


业务纵向拆分

互联网公司通常的宗旨是小步迭代试错快跑,当业务发展到足够大,对于单体应用想要达到这个宗旨是有难度的,随着业务的发展,应用程序越来越大,研发、维护、发布的成本也越来越大,这时候就需要考虑根据业务把单体应用拆分为多个服务,服务之间可以通过RPC远程调用和消息队列来一起完成用户的请求。

由于业务的拆分,通常情况下也会相应的对数据库进行拆分,达到一个服务对应一个数据库的理想状态

8张图带你了解大型应用架构演进历程

引入MQ的好处:

  • 提高系统的可用性:当消费服务器发送故障时,消息还在消息队列中,数据不会丢失
  • 加快请求的响应:当用户请求到达服务器后,把请求中可以异步处理的数据放入到MQ,让系统逐一消费,不需要用户等待,加快了响应速度
  • 削峰填谷:当大量请求都同时进入到系统之后,会全部放入到消息队列,系统逐一消费,不会对系统造成很大的冲击

总结

还有一个情况未谈及到,就是数据库的水平拆分,这也是数据库拆分的最后手段,只有当单表数据特别大,不能满足业务的需要才使用。使用最多的还是进行数据库的业务纵向拆分,把数据库中不同业务的数据放到不同的物理服务器上。

应用当前到底选择什么架构,一定要根据实际业务的需求进行灵活的选择,驱动技术架构发展的主要动力还是在于业务的发展,不要为了技术而技术。


写在最后

  • 首先感谢大家可以耐心地读到这里。点关注,不迷路
  • 当然,文中或许会存在或多或少的不足、错误之处,有建议或者意见也非常欢迎大家在评论交流。
  • 最后,白嫖不好,创作不易,希望朋友们可以点赞评论关注三连,因为这些就是我分享的全部动力来源🙏

> 原创不易 转载请注明出处:https://silently9527.cn/archives/64 > > 参考资料《大型网站技术架构》

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
2星期前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Stella981 Stella981
2年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
6个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这