如何设计高并发、高性能、高可用的系统

本文详细探讨了高并发、高可用和高性能的概念,并提供了各种技术手段如负载均衡、网关保护、服务限流熔断、CDN加速、数据库优化、缓存策略等来解决这些问题。强调了服务优化需要系统性和协作性。
摘要由CSDN通过智能技术生成


前言

        随着社会的不断发展,科技的不断进步,互联网服务已经成为人们生活中不可或缺的一部分。从智能手机、社交媒体、在线购物平台到数字支付系统,这些产品不仅改变了我们的沟通方式,还影响了我们的工作习惯、消费模式和生活方式。那么从技术角度来讲是如何满足这些需求的?如何保证这些服安全有效的运行的?试想一下如果你在网上购物,看到一个自己非常喜欢的商品,高高兴兴的点击商品详情,结果提示你“当前网络繁忙,请稍后再试”,那么你的心情会如何?如果你做过技术,恐怕大部分人都会问候负责这个模块的开发人员的祖先:“这TMD做的什么玩意”。那如何解决这种有损用户体验的问题呢?下面就要引出今天的主题了,也是互联网常见的、绕不过去的话题,俗称“三高”,即“高并发、高可用、高性能”


什么是高并发

高并发就是同一时间内有大量的请求访问服务,这通常发生在Web系统或其他网络服务中,当大量用户几乎同时发送请求时,系统需要能够快速有效地处理这些请求,以保持服务的响应性和可用性。高并发是衡量系统性能和稳定性的重要指标之一

什么是高可用

用通俗的话来说就是服务在任何时间都能对外提供服务,这里当然不是100%可用(否则就不叫高可用了,就要交一直可用或者百分百可用了),但是也需要达到一个相当的高度,业界公认的的是4个9或者更高。那么按照惯例,本着不装会死的原则,下面是一段比较官方的解释:高可用(High Availability,简称HA)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。具体来说,假设系统一直能够提供服务,我们说系统的可用性是100%。如果系统每运行100个时间单位,会有1个时间单位无法提供服务,我们说系统的可用性是99%。很多公司的高可用目标是4个9,也就是99.99%,这就意味着,系统的年停机时间为8.76个小时

什么是高性能

高性能一般是指一种能力,一种能在短时间内完成复杂的任务或者处理大量的数据能力。总整个分布式系统的角度来说这也就意味着你的服务有极快的处理速度、极高的吞吐量以及极优的计算能力。从单机角度来讲,高性能意味着你对计算机的压榨,通过技术手段让计算机释放出最高的性能

小结

其实高并发、高可用、高性能是相辅相成的,单独拿出来任何一个讲都是耍流氓,解决他们的问题也是系统性的,自上而下的。高并发必然会引起系统的高可用问题,如果服务做的很烂本身性能上不去那必然也会引起可用性的问题。试问如果单独服务性能上不去,那整个系统会高可用吗?系统本身不提供高可用的服务拿什么去支撑高并发?所以解决“三高”问题要系统性解决,自上而下的解决。就像你把自己的服务接口优化到了极致,结果上游模块非常拉胯,根本用不到你接口性能的万分之一。或者下游接口非常拉胯,不能支撑你服务接口的万分之一,以上的情况都是不可接受的。

如何解决这些问题

负载均衡(高可用)

负载均衡是一种对流量进行按需分发的服务,通过将流量分发到不同的后端服务来扩展应用系统的服务吞吐能力,并且可以消除系统中的单点故障,提升应用系统的可用性。常见的技术如lvs和nginx。现在大多数服务治理大礼包也包含相应的模块,比如ribbon(spring cloud)、dubbo等都有自己的负载均衡策略

网关(高可用)

网关这层拦截不安全的请求。比如黑客攻击、恶意刷单等等。做好网关防护非常重要,否则你服务性能再高、服务再高可用都没有用,因为这些都是无效请求

服务保护(高可用)

  • 限流
             当服务资源不能满足流量或者说流量大大高于系统资源时,为了保证服务的可用可以采用限流的策略。大家都经历过抢单、抢火车票或者前不久抢华为mate 60 pro的场景,这些场景的相同点是资源少但是需求量大(俗称的狼多肉少)。比如华为mate 60 pro在10:00要开售,库存只有10000台,但是现在有100万人要点击购买,如果将所有流量都打得到秒杀服务的话,那意味着有99万的流量是无效的,因为最后只有10000人能抢到mate 60 pro,这会造成多么大的浪费,甚至对服务也是一个考验。这时我们就需要进行性限流,把一部分流量按照先后顺序放在等待队列,把其余的流量直接引导到降级页。
    常见的限流组件比如spring cloud gateway(网关层)、RateLimiter(Guava里的工具)、Bucket4j等等

  • 服务熔断
    熔断这一概念来源于电子工程中的断路器(Circuit Breaker)。在互联网系统中,当下游服务因访问压力过大而响应变慢或失败,上游服务为了保护系统整体的可用性,可以暂时切断对下游服务的调用。这种牺牲局部,保全整体的措施就叫做熔断。常见的比如spring cloud hystrix(spring cloud netflix全家桶中的一员), 当hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态。 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN).。hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力。
    当然还有其他的熔断组件,例如Resilience4j(比较轻量级)、Sentinel(阿里开源的,也是spring cloud alibaba全家桶中的一员)、Istio(一个开源的服务网格框架,提供服务降级、限流、熔断等功能,可以与Kubernetes等容器编排工具集成)。
    以上都是经过大公司以及大佬们验证过的,也是比较成熟和稳定的,可以根据自身的业务需求来选择适合的框架来实现服务熔断

  • 服务降级
            当整个微服务架构整体的负载超出了预设的上限阈值或即将到来的流量预计将会超过预设的阈值时,为了保证重要或基本的服务能正常运行,可以将一些 不重要 或 不紧急 的服务或任务进行服务的延迟使用或暂停使用。
            其实服务降级就是熔断的升级版。熔断是认为服务还可用只是不太稳定,后续还能对外提供服务。降级则是认为当前服务已经不满足对外提供的条件了,后续请求不要再来了。
            hystrix也提供了这种实现机制。我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值 。allback方法的返回值一般是设置的默认值或者来自缓存。告知后面的请求服务不可用了,不要再来了

  • 服务灾备
            互联网服务商通常会将服务部署在不同地方或不同机房的集群上以避免集群崩溃导致服务整体不可用。灾备简单一点来说就是假如一个服务集群挂掉,另一个地方或者机房的服务可以继续正常提供服务。
             一般灾备有主备和多活两种策略。主备就是有一半的集群提供服务,另一半待命,等主集群挂掉立刻切到备集群中。异地多活就是将流量平均分配到每个集群,如果某个集群挂掉会将流量均分到其他集群上。当然灾备也有问题要解决,比如面对瞬间涌入的流量怎么办等等,这个在后面的文章讨论

  • 小结
    通过以上对服务保护策略的介绍,我们应该能看到服务可用性问题除了不可抗因素(网络断了)以外是逐步升级的,先是单个服务可用->单个服务不是100%可用->单个服务不可用->集群部分可用->集群整体不可用。就像是锁升级一样从无锁到偏向锁到轻量级锁到重锁。往往问题就是从小问题开始然后主线放大,中医里讲久病必瘀也是这个道理。所以要想把系统做好,不要放弃任何一个小问题,否则后患无穷

CDN加速

CDN(Content Delivery Network,内容分发网络),是一种常用的网络加速手段。就是将网站的内容缓存到离用户最近的节点上,从而实现内容的快速传输。当用户请求访问某个网站时,CDN系统会根据用户的地理位置和网络环境,智能选择最优的节点服务器提供内容服务。这样,用户可以就近获取所需内容,大大减少了网络延迟,提高了网站的访问速度和用户体验。比如我们所看的一些视频、图片、或者一些html页面等等都可以预先放在cdn服务器上对外提供服务

数据库(高性能)

  • 读写分离
    这个比较好理解,读操作和写操作分开,一个库对外提供读操作,一个库对外提供写操作,要注意数据同步、延迟
  • 分库分表
    如果数据量巨大,如果不对表进行拆分的话,那么随着数据量的增加,数据库操作会急剧下降,那么我们就要考虑拆分表了。最简单的方式就是根据id%数据库表个数进行数据存放,可以用sharding-jdbc来做

空间换时间(高性能)

空间换时间就是利用缓存技术来加速服,减少直接操作数据库的概率,以此来提高服务的响应能力。因为缓存技术用的实在太多了,这里就不过多赘述了,后面会结合场景来说明。

异步处理(高性能)

异步处理是不用阻塞当前的线程,可以使用一个其他线程后台执行,直到其他线程处理完成后回调通知当前线程。要说明一下,不要看到前面说的是线程就认为只有多线程才能完成异步处理。异步处理只是一种设计理念,不是只有多线程一种。比如使用mq中间件,或者pub/sub都是异步处理的手段。需要注意的是,如果使用中间件,那么不要让中间件成为你性能的瓶颈,这并不是危言耸听

无锁化处理(高性能)

在我们实现需求的过程中,难免会遇到资源抢占的情况,这时我们第一想到的就是加锁,这完全没有问题,但是有没有考虑过可以不加锁呢?没错,我们也有不加锁的做法。下面就介绍两种实现方法

  • ThreadLocal
    ThreadLocal是本地线程(这不是废话吗?直接翻译谁不会),他是当前线程的副本,里面填充的是当前线程的变量,这个变量但对于其他来现成来说并不可见,ThreadLocal为其他线程创建了这个变量的副本。那么什么情况下可以使用ThreadLocal呢?
    1、进行对象跨层传递的时候可以使用ThreadLocal
    2、进行事务操作的时候,用于存储线程事务,最终提交
    3、数据库连接管理(去看看spring dao是如何使用数据库连接的)
  • disruptor
    disruptor是一个开源框架,研发的初衷是为了解决高并发下队列锁的问题,最早由LMAX提出并使用,能够在无锁的情况下实现队列的并发操作,并号称能够在一个线程里每秒处理6百万笔订单。这里就不展开说了,后面会专门针对它做一次介绍。

池化处理(高性能)

比如线程池、数据库连接池等。目的是减少线程切换带来的损耗和网络开销等

批量处理(高性能)

合并请求,进行批量处理,减少资源消耗。例如现在过来了1000个入库订单请求,如果按照常规的做法就是1000个订单操作1000次数据库,那这将极大的损耗数据库资源,同时会给下游服务带来性能瓶颈的风险。所以考虑把这1000个请求分批进行批量请求,比如200个为一组去操作数据库,那这最多也就操作5次,这就极大的减少了数据库不必要的损耗,同时也提高了响应速度。那么如何进行请求合并,并将结果分发给对应的1000个请求呢?好问题,这个后面单独开一片文章用代码来实现更有说服力。

合理的日志管理(高性能)

别不信,打印日志也能成为服务的瓶颈。一般我们打印日志会打到文件或者hdfs上,这肯定会有io消耗。所以合理的日志输出必不可少。要严格遵守规范,没必要的输出尽量不输出,线上线下严格执行输出等级

策略相关

除了技术相关的手段之外,我们还可以从策略方面下手,下面就介绍几个比较常见策略手段

  • 数据预加载
    这个想法其实和CDN差不多,就是项目上线前把需要操作的资源提前加载到缓存里。比如秒杀系统可以提前把库存和产品信息等提前加载到缓存中,避免大量请求直接打到数据库。

  • 操作缓处理
    这个按照字面理解就可以,就是将不是特别急的操作延后处理。比如春晚上面抢红包,抢到后不立刻进行划账操作,先告知用户已经抢到的红包金额,然后告知他红包将在24小时内入账。这样就把抢到红包和划账分拆来进行缓处理操作,避免实时将后面的银行服务搞挂。还有银行间转账24小时内到账也一样

总结

上面对互联网常见的“三高”问题进行了系统的阐述并给出了对应的解决方案,其中有好多手段可以更详细的展开来聊,后面还会开新文章对这些细节进行详细的阐述。上面的方案也只是提供一个思路,可能会有不对的地方,这里只做抛转引玉的工作。还是那句话,服务优化是系统性的工作,不光涉及到你所负责的模块,还需要你的上游、下游、网关等等所有模块的通力配合才行

  • 42
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值