【精品】服务端 架构演进 历程,逆袭面经分享

最后再选择一个数据库管理系统来存储数据,如mysql、sqlserver、oracle,然后通过JDBC进行数据库的连接和操作。

以淘宝为例子,在网站最初的时候,应用数量与用户数量都比较少,可以把Tomcat和数据库部署在同一台服务器上。浏览器往www.taobao.com发起请求时,首先经过DNS服务器(域名系统)把域名转换成实际IP地址10.102.4.1,浏览器转而访问该IP对应的Tomcat。

在这里插入图片描述

单体架构在规模比较小的情况下工作情况良好,但是随着系统规模的扩大,它暴露出来的问题也越来越多,主要有以下几点:

  • 复杂性逐渐变高

比如有的项目有几十万行代码,各个模块之间区别比较模糊,逻辑比较混乱,代码越多复杂性越高,越难解决遇到的问题。

  • 技术债务逐渐上升

公司的人员流动是再正常不过的事情,有的员工在离职之前,疏于代码质量的自我管束,导致留下来很多坑。由于单体项目代码量庞大的惊人,留下的坑很难被发觉,这就给新来的员工带来很大的烦恼,人员流动越大所留下的坑越多,也就是所谓的技术债务越来越多。

  • 部署速度逐渐变慢

这个就很好理解了,单体架构模块非常多,代码量非常庞大,导致部署项目所花费的时间越来越多,曾经有的项目启动就要一二十分钟,这是多么恐怖的事情啊,启动几次项目一天的时间就过去了,留给开发者开发的时间就非常少了。

  • 阻碍技术创新

比如以前的某个项目使用struts2写的,由于各个模块之间有着千丝万缕的联系,代码量大,逻辑不够清楚,如果现在想用spring mvc来重构这个项目将是非常困难的,付出的成本将非常大,所以更多的时候公司不得不硬着头皮继续使用老的struts2架构,这就阻碍了技术的创新。

  • 无法按需伸缩

比如说用户模块是CPU密集型的模块,而订单模块是IO密集型的模块,假如我们要提升订单模块的性能,比如加大内存、增加硬盘,但是由于所有的模块都在一个架构下,因此我们在扩展订单模块的性能时不得不考虑其它模块的因素,因为我们不能因为扩展某个模块的性能而损害其它模块的性能,从而无法按需进行伸缩。

第一次演进:应用服务器与数据库分开部署


随着网站的上线,访问量逐步上升,服务器的负载慢慢提高,在服务器还没有超载的时候,我们应该就要做好准备,提升网站的负载能力。假如我们代码层面已难以优化,在不提高单台机器的性能的情况下,增加机器是一个不错的方式,不仅可以有效地提高系统的负载能力,而且性价比高。

增加的机器用来做什么呢?此时我们可以把数据库,web服务器拆分开来,这样不仅提高了单台机器的负载能力,也提高了容灾能力。

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

在这里插入图片描述

web服务器(Tomcat)和数据库分别独占服务器资源,显著地提高两者各自的性能。

随着用户数量的增长,并发读写数据库成为了性能的瓶颈。

第二次演进:引入本地缓存和分部式缓存


在Tomcat服务器或JVM中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的HTML页面等。通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。其中涉及的技术包括:使用Memcached作为本地缓存,使用Redis作为分布式缓存,这会涉及到缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题。

在这里插入图片描述

具体和缓存方案:

  • 后台应用层和数据库层的缓存

随着访问量的增加,逐渐出现了许多用户访问同一部分内容的情况,对于这些比较热门的内容,没必要每次都从数据库读取。我们可以使用缓存技术,例如可以使用google的开源缓存技术guava或者使用memcacahe作为应用层的缓存,也可以使用redis作为数据库层的缓存。

另外,在某些场景下,关系型数据库并不是很适合,例如我想做一个“每日输入密码错误次数限制”的功能,思路大概是在用户登录时,如果登录错误,则记录下该用户的IP和错误次数,那么这个数据要放在哪里呢?

假如放在内存中,那么显然会占用太大的内容;假如放在关系型数据库中,那么既要建立数据库表,还要建立对应的java bean,还要写SQL等等。而分析一下我们要存储的数据,无非就是类似{ip:errorNumber}这样的key:value数据。对于这种数据,我们可以用NOSQL数据库来代替传统的关系型数据库。

  • 页面缓存

除了数据缓存,还有页面缓存。比如使用HTML5的localstroage或者cookie。

  • 优点:

  • 减轻数据库的压力

  • 大幅度提高访问速度

  • 缺点:

  • 需要维护缓存服务器

  • 提高了编码的复杂性

值得一提的是:缓存集群的调度算法不同与上面提到的应用服务器和数据库。最好采用“一致性哈希算法”,这样才能提高命中率。

缓存虽然抗住了大部分的访问请求,但是随着用户数量的增长,并发的压力还是主要落在单机的Tomcat上,响应逐渐变慢。

第三次演进:引入反向代理和负载均衡


随着访问量继续增加,单台应用服务器已经无法满足需求了。在假设数据库服务器没有压力的情况下,我们可以把应用服务器从一台变成了两台甚至多台,把用户的请求分散到不同的服务器中,从而提高负载能力。

多台应用服务器之间没有直接的交互,他们都是依赖数据库各自对外提供服务。

在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层(最高层、应用层)的反向代理软件,主要支持HTTP协议,还会涉及Session共享,文件上传、下载的问题。

在这里插入图片描述

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

  1. 用户的请求由谁来转发到到具体的应用服务器

  2. 有什么转发的算法

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

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

解决方案:

  • 第一个问题即是负载均衡的问题,一般有5种解决方案:

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

优点:简单。

缺点:性能较差。

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

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

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

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

优点:部署简单。

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

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

优点:性能更好。

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

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

  • 第二个问题即是集群调度算法问题,常见的调度算法有以下10种:

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

  • 第三个问题是集群模式问题,一般3种解决方案:

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

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

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

  • 第四个问题是session问题,一般有以下4种解决方案:

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

优点:实现简单。

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

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

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

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

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

优点:相比session replication的方案,集群间对于宽带和内存的压力减少了很多。

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

  • 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共享的支持。

虽然反向代理使应用服务器可以支持的并发量大大增加,但是并发量的增加也意味着更多请求穿透到数据库,单机的数据库最终会称为性能瓶颈。

第四次演进:数据库读写分离


上面我们总是假设数据库负载正常,但随着访问量的的提高,数据库的负载也在慢慢增大。那么可能有人马上就想到跟应用服务器一样,把数据库一份为二再负载均衡即可。

但对于数据库来说,并没有那么简单。假如我们简单的把数据库一分为二,然后对于数据库的请求,分别负载到A机器和B机器,那么显而易见会造成两台数据库数据不统一的问题。那么对于这种情况,我们可以先考虑使用读写分离的方式。

在这里插入图片描述

把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据的场景,可以在缓存中多写一份,通过缓存获得最新数据。其中涉及的技术包括Mycat,它是数据库中间件,可通过它来组织数据库的读写分离和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题。

这个结构变化后也会带来两个问题:

  • 主从数据库之间数据同步问题

  • 应用对于数据源的选择问题

解决问题方案:

  • 我们可以使用MYSQL自带的master+slave的方式实现主从复制。

  • 采用第三方数据库中间件,例如mycat。mycat是从cobar发展而来的,而cobar是阿里开源的数据库中间件,后来停止开发。mycat是国内比较好的mysql开源数据库分库分表中间件。

随着业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库资源,相互影响性能。

第五次演进:数据库水平拆分与垂直拆分


我们的网站演进到现在,交易、商品、用户的数据都还在同一个数据库中。尽管采取了增加缓存,读写分离的方式,但随着数据库的压力继续增加,数据库的瓶颈越来越突出,此时,我们可以有数据垂直拆分和水平拆分两种选择。

数据库垂直拆分(按业务分库)

垂直拆分的意思是把数据库中不同的业务数据拆分道不同的数据库中,比如把电商项目中的交易、商品、用户的数据分开。

把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低。对于访问量大的业务,可以部署更多的服务器来支撑。

在这里插入图片描述

  • 优点:

  • 解决了原来把所有业务放在一个数据库中的压力问题。

  • 可以根据业务的特点进行更多的优化

  • 缺点:

需要维护多个数据库

  • 问题:

  • 需要考虑原来跨业务的事务

  • 跨数据库的join

  • 解决问题方案:

  • 应该在应用层尽量避免跨数据库的事物,如果非要跨数据库,尽量在代码中控制。

  • 可以通过第三方应用来解决,如上面提到的mycat,mycat提供了丰富的跨库join方案,详情可参考mycat官方文档。

随着用户数量的增长,单机的写库会逐渐达到性能瓶颈。

水平拆分(把大表拆分为小表(分表))

数据水平拆分就是把同一个表中的数据拆分到两个甚至多个数据库中。产生数据水平拆分的原因是某个业务的数据量或者更新量到达了单个数据库的瓶颈,这时就可以把这个表拆分到两个或更多个数据库中。

比如针对评论数据,可以按照商品的ID进行Hash,路由到对应的表中存储;针对支付记录,可以按照支付的小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据。只要实时操作的表数据量足够小,请求能够足够均匀地分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提升性能。其中前面提到的Mycat也支持在大表拆分为小表的情况下进行访问控制。

在这里插入图片描述

这种做法显著地增加了数据库运维的难度,对DBA的要求较高。当数据库设计到这种结构时,已经可以称为分布式数据库,但是这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的,比如分库分表的管理和请求分发由Mycat实现,SQL的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等,这种架构其实是MPP(大规模并行处理)架构的一类实现。

目前开源和商用都已经有不少MPP数据库,开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibraA等,不同的MPP数据库的侧重点也不一样,比如TiDB侧重于分布式OLTP场景,Greenplum侧重于分布式OLAP场景,这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回,也提供了注入权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据

库运维的成本,并且使数据库也能够水平扩展。

  • 优点:

如果我们能克服以上问题,那么我们将能够很好地应对数据量及写入量增长的情况。

  • 问题:

  • 访问用户信息(假设用户表进行了水平拆分)的应用系统需要解决SQL路由的问题,因为现在用户信息分在了两个数据库中,需要在进行数据操作时了解需要操作的数据在哪里。

  • 主键的处理也变得不同,例如原来自增字段,现在不能简单地继续使用了。

  • 如果需要分页,就麻烦了。

  • 解决问题方案:

  • 可以通过可以解决第三方中间件,如mycat。mycat可以通过SQL解析模块对我们的SQL进行解析,再根据我们的配置,把请求转发到具体的某个数据库。

  • 可以通过UUID保证唯一或自定义ID方案来解决。

  • mycat也提供了丰富的分页查询方案,比如先从每个数据库做分页查询,再合并数据做一次分页查询等等。

虽然数据库和Tomcat都能够水平扩展,可以支撑的并发量大幅提升,但是随着用户量的增长,最终单机的Nginx会成为性能上的瓶颈。

第七次演进:使用LVS或F5来使多个Nginx负载均衡

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
img

最后

今天的文章可谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。不过光是看完而不去付出行动,或者直接进入你的收藏夹里吃灰,那么我写这篇文章就没多大意义了。所以看完之后,还是多多行动起来吧!

可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。

提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-SjKPbl0L-1711996582649)]

最后

今天的文章可谓是积蓄了我这几年来的应聘和面试经历总结出来的经验,干货满满呀!如果你能够一直坚持看到这儿,那么首先我还是十分佩服你的毅力的。不过光是看完而不去付出行动,或者直接进入你的收藏夹里吃灰,那么我写这篇文章就没多大意义了。所以看完之后,还是多多行动起来吧!

可以非常负责地说,如果你能够坚持把我上面列举的内容都一个不拉地看完并且全部消化为自己的知识的话,那么你就至少已经达到了中级开发工程师以上的水平,进入大厂技术这块是基本没有什么问题的了。

CodeChina开源项目:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值