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

《爆料》从完整的秒杀架构设计到关键技术点的“情报信息”

时间:2023-04-01 21:21:59 Java

前提声明本文内容完全是作者自己的技术分析和总结沉淀。希望大家多多见谅,多多指正,多多指正,谢谢。秒杀系统-信息背景相信大家都接触过新浪微博、淘宝、京东等访问量比较大的平台和网站。所有读者都必须面对的一个棘手的“包袱”问题。哎,看来要在这个行业继续下去,即使可能不会接触到高并发的场景,也要创造“高并发”去迎难而上,因为只有这样才能走得更远!秒杀系统-资料介绍今天我们要介绍的是一种最极端的高并发场景:“秒杀”,这个名词一般会出现在“大促”的时候,当然也会出现在一些平台上,比如出现在活动中,肯定会有朋友说,秒杀系统应该注意哪些问题!为什么更难?困难在哪里?秒杀系统-特性分析瞬时激增:在某一时刻,流量开始进入(很少有预热和缓慢增长的机制),大量用户会同时涌入购买同一款产品秒杀期间,网站流量瞬间暴增。僧多粥少:商品存货有限,限时抢购下单数量会远大于库存量,只有小部分用户可以抢购成功。资源锁定:秒杀的业务流程比较简单,一般就是下单,减少库存。库存是用户争夺的“资源”。实际消耗的“资源”不能超过计划出售的“资源”,即不能“超卖”。秒杀系统-难度分析其难点在于完成一个“60-100分”的秒杀系统,那么至少要兼顾以下三个方面才算合格。这三个“魔鬼”叫做“服务可用性”、“数据一致性”和“快速响应”,有点“苛刻”!在我们现在的场景下,很难考虑非分布式系统的架构。(分布式架构)相信大家都知道CAP理论吧!如果你什么都不知道也没关系。可以看内容:CAP理论也叫CAP定理。它指的是服务(数据)层面的一致性(Consistency),服务本身的可用性(Availability),以及分布式系统中网络的不同节点。分区容忍度。A和C相信大家从字面上都能理解。这里要声明一下比较陌生的P:意思是如果要保证在网络出现问题的情况下,不同的节点仍然可以访问数据,那么最直接的方式就是Redundantassignmentnodes,否则一切都是空谈,那么作为一个分布式系统,P是不能忽略的,我们可以理解为它是A和C的基础。CAP系统的总结只是保证AC是单体应用,根本不是分布式的。当然是有意义的。在分布式出现之前,系统是这样搭建的。如果这个系统有一个节点挂了,不会出现脑裂,整个系统会直接宕机。此外,如果网络中的节点越多,分区容忍度越高,但复制和更新的数据越多,一致性就越难保证。为了保证一致性,所有节点的数据更新时间越长,可用性越低。ServiceAvailability服务可用性是指在高并发流量的冲击下,仍能保持服务的可用性,始终保证向外界输出服务的能力,不会造成宕机或资源损坏,即使是在内存和网络中.在硬件资源有限的情况下,不会被压垮和“死亡”。数据一致性我们都知道,在我们开发程序的时候,大多数服务器,比如数据库,在处理数据的时候,很可能会有多个线程同时修改同一行数据或者同一块内存。从Java的角度来看,本身会有不一致的地方,而从程序和中间件的角度来看,是一样的。同时会出现数据修改顺序乱序,数据乱序,导致数据重复操作,导致相同我们预期的不同场景。除非你能实现序列化,一个一个处理,同时防止它们同时修改或操作数据,这是最本质也是最安全的方式,但也是最影响性能的方式。(悲观锁、同步队列)。另外,还有一种方式,就是一直在原子层面,也就是最靠近底层的计算机修改数据时,或者建立一个应用层的中间汇总主干点(redis的主干点或者数据库)在所有节点之间,在上面添加一个写屏障和一个读屏障。修改前先进行验证判断。如果数据与预期的不同,则不会执行修改。这就是著名的乐观锁!快速的服务响应一般来说,这属于用户体验。一个比较合格的秒杀系统,不应该让用户长时间等待,最好能尽快反馈。实现快速响应,不需要异步返回,直接快速响应。另外,需要尽快帮用户计算出数据,直接返回。秒杀系统-架构设计我们将秒杀架构分为三个层次进行分析:从外到内,分别是:应用层、服务层、数据访问层。应用层架构设计动静分离+CDN技术动静分离分析场景分析:秒杀活动开始前,用户一般会尝试不断刷新浏览器页面(俗称F5),以确保不会错过任何商品秒杀活动。根据常用的网站应用架构:我们假设如果这些无用的请求频繁的打到我们的后台服务器,比如通过:Web服务器(LVS、Nginx等)->应用服务器(tomcat或者Jetty等),连接到数据库(MySQL),无疑会给后端服务和服务器带来很大的压力。解决方案:重新设计秒杀产品页面,不使用网站原有的产品详情页面,页面内容静态化,通过后台服务减少/隔离无用请求。CDN技术分析增加网络带宽如果网站静态页面数据大小为100K,那么需要的网络和服务器带宽为2G(100K×10000)。即使动态业务转为静态页面,秒杀activity会大大增加网络带宽的消耗,同时不会减轻前端网站服务器的压力,所以如果可以的话,需要进一步在CDN中缓存秒杀产品页面,而不仅仅是我们前端-endNginx服务器级别,所以需要临时租用CDN服务商新增的出口带宽。拦截缓存页面给页面添加一个JavaScript文件引用(传输随机数+状态位),JavaScript文件包含秒杀启动标志为no;秒杀启动时,生成一个新的JavaScript文件(文件名不变,内容不同),更新秒杀启动标志为yes,添加订单页面的URL和随机数参数(此随机数只会生成一,就是大家看到的是同一个URL,服务器可以使用redis这种分布式缓存服务器来保存随机数)并由用户的浏览器加载来控制秒杀产品页面的显示。此JavaScript文件可以使用随机版本号(例如xx.js?v=32353823)加载,因此它不会被浏览器、CDN和反向代理服务器缓存。这个JavaScript文件非常小,每次浏览器刷新时访问JavaScript文件服务器不会对服务器集群和网络带宽造成太大压力。根据ID限频为了控制公平原则,黄牛或者一些黑客会使用“高科技”,比如使用爬虫脚本疯狂操作刷新页面。为了防止某些人破坏和公平分配,使用相同的标准来控制UID(用户ID)访问频率信息。当超过每个人需要达到的频率阈值时,需要限制访问交互窗口中刷新的数据。数量!例如:可以使用Redis对每个用户进行访问统计,根据用户ID和商品标识来控制用户对某个商品的访问频率。超过访问频率后,他的请求会被暂时屏蔽。.负载均衡秒杀系统必须是集群系统。在硬件没有提升的情况下使用nginx做负载均衡也是一个不错的选择。LoadBalance是集群技术(Cluster)的一种应用,可以将工作任务分配给多个处理单元,从而提高并发处理能力,有助于提高中大型网站的性能。需要通过服务集群和横向扩展,让“高峰期”的请求分布到不同的服务器上进行处理。http协议负载均衡根据用户http请求的DNAT计算出一个真实的web服务器地址,并将该web服务器地址写入http重定向响应返回给浏览器重新访问。这种方法比较简单,但是性能较差。DNS解析负载均衡在DNS服务器上配置多个域名对应的IP记录。这种方式直接将负载均衡的工作交给了DNS,为网站管理和维护省去了很多麻烦,提高了访问速度,有效提升了性能。反向代理负载均衡反向代理服务器在提供负载均衡功能的同时,管理着一组Web服务器。根据负载均衡算法,将请求的浏览器访问转发到不同的Web服务器进行处理,处理结果由反向服务器返回给浏览器。网络层IP负载均衡网络层通过修改目标地址来进行负载均衡。这种方法在响应请求时比反向服务器负载均衡更快。但是,当请求数据量大(大视频或文件)时,响应速度会慢一些。MAC层负载均衡数据链路层修改Mac地址进行负载均衡,负载均衡服务器的IP与其管理的web服务组的虚拟IP一致。不需要负载均衡服务器进行地址转换,但对负载均衡服务器的网卡带宽要求较高。硬件负载均衡F5的全称是F5-BIG-IP-GTM,一款并发能力高达100%的硬件负载均衡设备。这种方式可以实现多链路负载均衡和冗余,可以接入多个ISP链路,实现链路间的负载均衡和高可用。服务层架构设计降速机制即使我们扩展更多的应用服务,使用更多的应用服务器,部署更多的负载均衡器,我们也会遇到无法支持海量请求的时候。排队处理排队处理就像我们日常的购物排队。将请求放入队列使用的是FIFO(FirstInputFirstOutput,先进先出),这样我们就不会造成一些请求永远得不到Lock。看到这里,有一些多线程的处理方式变成了单线程的处理机制,会极大的影响数据的效率和性能!阻塞队列ArrayBlockingQueue是一个初始容量固定的阻塞队列。ConcurrentLinkedQueue是使用CAS原语无锁队列实现的。它是一个异步队列。入队速度很快,出队时队列是锁死的,所以性能稍慢。LinkedBlockingQueue也是一个阻塞队列。锁定用于进入和退出队列。当队列为空时,线程会暂时阻塞。批量发布在同步排队的基础上,可以增加批量发布执行机制。我们可以考虑在达到预定阈值后进行相关的执行后端服务,可以提升一定的性能,减少后端请求的次数和压力,如下图所示:利用缓存和队列技术降低应用处理压力,通过异步请求实现最终一致性。带限流机制的漏桶算法漏桶算法的思想很简单。水(请求)先进入漏桶,漏桶以一定的速度放水。限制数据传输速率。设置漏桶的流出速度和漏桶的总容量。当请求到来时,判断当前漏桶是否满。如果不是,则可以将该请求存储在桶中,否则该请求将被丢弃。一个线程以设定的速率获取请求进行处理。该算法的缺点是只能以特定速率处理请求。rate设置的太小会浪费性能资源,设置的太大会导致资源不足。无论输入速率如何波动,都不会反映到服务器上。即使资源空闲,也无法及时处理突发请求。因此,这种方式不适合突发请求处理的需要。令牌桶算法令牌桶算法的原理是系统会以恒定的速度向桶中放入令牌,如果请求需要处理,需要先从桶中取出令牌,当没有令牌时桶中的令牌当卡可用时,服务被拒绝。实现原理设置令牌添加到令牌桶的速率,并设置桶中最大可存储的令牌。当一个请求到来时,从桶中请求token(根据应用需求可以是1个或多个),如果token个数满足要求,则删除对应个数的token,并通过当前请求,如果桶中的token个数满足bucket不足,触发限流规则。为了解决固定窗口计数导致周期切换时的流量突发问题,可以使用滑动窗口计数。滑动窗口计算本质上是固定窗口计数,区别在于计数周期细化了。滑动窗口与固定窗口计数法相比,滑动窗口计数法除了计数周期T和最大访问(调用)次数这两个参数外,还增加了一个参数M来设置周期T中滑动窗口的个数N在循环中。由于数据访问层要承受高并发,所以在高并发的情况下,mysql的性能下降尤为明显。数据更新点需要控制数据更新点!悲观锁可以从“悲观锁”的方向进行悲观锁定,即在修改数据时,采用锁状态来排除外部请求的修改。当遇到锁定状态时,必须等待。虽然解决了线程安全问题,但是在“高并发”的场景下,这样的修改请求会很多,每个请求都需要等待“锁”。有些线程可能永远没有机会抢到这个“锁”。这样的要求会死在那里。这样的请求会很多,会瞬间增加系统的平均响应时间。结果,可用连接数将被耗尽,系统将陷入异常。乐观锁我们来讨论一下“乐观锁”的思想。经常有缓存乐观锁和数据库乐观锁。(判断更新行数是否>0)乐观锁采用了比“悲观锁”更为宽松的锁机制。大多采用版本更新/状态更新操作机制。所有请求都可以修改,但会获取数据的版本号。只有版本号匹配的才能更新成功,其他请求会返回失败的购买。不用考虑队列的问题,但是会增加CPU的计算开销。但当冲突较小时,这是一个更好的解决方案。

猜你喜欢