您好!欢迎来到爱源码

爱源码

热门搜索: 抖音快手短视频下载   

面试官:几千万几十亿的流量怎么解决? [免费源码]

  • 时间:2022-09-14 00:15 编辑: 来源: 阅读:298
  • 扫一扫,手机访问
摘要:面试官:几千万几十亿的流量怎么解决? [免费源码]
这是一个很常见的面试问题,但是大多数人都不知道怎么回答。其实这种问题可以问很多形式。你一定看到了,觉得很无奈:面对快速增长的业务,你是怎么解决的?如何解决业务量10倍、100倍增长的问题?你的系统如何支持高并发?如何设计高并发系统?高并发系统有什么特点?.......等等,提问的方式有很多种,但是面试的时候很难看这种类型的问题,但是我们可以有一个常规的思路来回答,就是如何围绕一个支持高并发的业务场景来设计一个系统?如果你能想到这一点,那么我们就可以把重点放在如何在硬件和软件上支持高并发这个话题上了。 本质上,这个问题是对你是否知道如何解决每个细节,是否有解决的经验的综合测试。 面对超高并发,首先硬件级机器要能承受;其次,架构设计要做好微服务的拆分工作;在代码层面,要解决缓存、削峰、解耦等各种问题;数据库层面,要做好读写分离、子数据库、子表;应监控稳定性;保险丝限流和降级应该是必要的;并能及时发现和解决问题。 这样,整个系统设计就有了初步的概念。 微架构在互联网早期演进的时候,单一的架构足以支撑日常的业务需求。每个人的所有业务服务都在一个项目中,并部署在一台物理机器上。 所有业务,包括您的交易系统、会员信息、库存、商品等。,都混合在一起。一旦流量起来,架构单一的问题就暴露出来了,挂机时所有业务都无法使用。 于是,集群架构的架构开始出现,单机无法抵御压力。最简单的方法是水平和水平扩展。这样,压力流通过负载平衡被分配到不同的机器上,暂时处理了单点引起的服务不可用的问题。 然而,随着业务的发展,在一个项目中维护所有的业务场景变得越来越困难。一个简单的需求变更需要发布整个服务,代码合并冲突会越来越频繁,上线失败的可能性更大。 微服务架构模型诞生了。 通过将各个独立的业务拆分成独立的部署,降低了开发和维护的成本,也增加了集群所能承受的压力。永远不会有一个小的改变点需要去触碰。 从高并发的角度来看,似乎以上几点都可以归为通过服务拆分和集群物理机的扩展来提高整体系统的抗压能力。那么,拆分带来的问题也是高并发系统需要处理的问题。 拆分RPC微服务带来的好处和便利是显而易见的,但同时也需要考虑微服务之间的通信。 传统的HTTP通信方式是对性能的极大浪费。这时候就需要引入Dubbo等RPC框架来提高整个集群基于TCP长连接的通信效率。 假设来自客户端的原始QPS是9000,那么它将通过负载均衡策略被分配到每台机器上。HTTP改为RPC后,接口时间会缩短,单机和整体的QPS会提高。 RPC框架本身一般有自己的负载均衡和熔丝降级机制,可以更好地维护整个系统的高可用性。 那么在RPC之后,接下来的问题是Dubbo的几个基本原理,这在国内基本是一个普遍的选择。 Dubbo的工作原理服务启动时,提供者和消费者根据配置信息连接到注册表注册器,分别在注册表中注册和订阅服务注册器。根据服务订阅关系,注册器将提供者信息返回给消费者,消费者将提供者信息缓存在本地。 如果信息发生变化,消费者会从注册器接收推送消费者,生成代理对象,同时根据负载均衡策略,选择一个提供者,定期记录接口对监控器的调用次数和时间信息。获取代理对象后,消费者收到请求后通过代理对象发起接口调用提供者反序列化数据,再通过代理调用特定接口实现Dubbo负载均衡策略的加权随机性:假设我们有一组服务器= [A,B,C],它们对应的权重为weights = [5,3,2],总权重为10。 现在将这些权重值平铺在一维坐标值上。区间[0,5]属于服务器A,区间[5,8]属于服务器B,区间[8,10]属于服务器c。 接下来,由随机数生成器生成[0,10]范围内的随机数,然后计算随机数将落入的区间。 最小有效号码:每个服务提供商对应一个有效号码。最初,所有服务提供商的活动数量为0。 每收到一个请求,活动数加1,请求完成后活动数减1。 服务运行一段时间后,性能好的服务提供者可以更快的解决请求,所以活动数下降的更快。此时,这样的服务提供商可以首先获得新的服务请求。 一致性哈希:通过哈希算法,从提供者和随机节点的调用中生成一个哈希,并将这个哈希投影到[0,2 32-1]的圆上。查询时根据密钥进行md5,然后进行哈希,得到第一个节点的值大于等于当前哈希的调用者。 轮询:例如,如果服务器A、B、C的重量比为5:2:1,那么在这八个请求中,服务器A会收到其中的五个,服务器B会收到其中的两个,服务器C会收到其中的一个。 容错故障转移集群故障自动切换:dubbo的默认容错方案在调用失败时自动切换到其他可用节点。具体的重试次数和间隔可以参照服务进行配置。默认重试次数为1,即只有一次呼叫。 快速故障回复集群:当呼叫失败时,记录日志和呼叫信息,然后向消费者返回空结果,并通过计划任务每5秒重试失败的呼叫。Failfast集群无法自动恢复:它只会被调用一次,失败后会立即引发异常。Failsafe集群安全地失败:调用有一个异常,日志没有被抛出,但是返回一个空结果。分叉集群并行调用多个服务提供者:通过线程池创建多个线程,并发调用多个提供者,结果保存在阻塞队列中。只要有一个提供者成功返回结果,它就会立即返回结果。广播集群广播模式:逐个调用每个提供者,如果其中一个报告错误,则在循环调用后抛出异常。 大家应该了解消息队列对于MQ的作用,比如削峰填谷,解耦等。 根据消息队列的不同,同步到异步模式可以降低微服务之间的耦合。 对于少数不需要同步执行的接口,可以通过引入消息队列来异步执行,提高接口响应时间。 交易完成后,需要扣除库存,之后可能需要给会员发放积分。本质上,发点这个动作应该属于表演服务,对实时性的要求不高。我们只需要保证最后的一致性,也就是演出能够成功。 对于这类请求,MQ可以是异步的,这将提高系统的抗压能力。 对于消息队列,如何保证消息的可靠性,在使用时不丢失?可靠性的消息丢失可能发生在三个方面:生产者发送的消息、MQ自身丢失的消息和消费者丢失的消息。 生产者丢失生产者消息的可能点是程序发送失败,抛出异常且没有再次尝试解决,或者发送方发送过程成功,但网络flash时没有收到MQ,消息丢失。 因为同步传输一般不会这样出现,所以我们不考虑同步传输的问题。我们基于异步传输场景。 异步发送可以分为有回调的异步和无回调的异步两种方式。无论结果如何,消息都可能在制作者发送后丢失。我们可以通过异步发送+回调通知+本地消息表的形式来制定处理方案。 下面的单一场景就是一个例子。 下单后,会先保存本地数据和MQ消息表。此时,消息状态为正在发送。如果本地事务失败,订单将失败,事务将回滚。 如果下单成功,直接返回客户端成功,异步发送MQ消息,MQ回调通知消息发送结果。与升级数据库的MQ发送状态相对应的作业轮询将用于重试已发送超过一定时间的不成功消息(该时间基于业务配置)。如果配置了监控平台或作业程序解决了发送超过一定次数的不成功消息、报警和手动干预。 一般来说,对于大部分场景,异步回调的形式就可以了,我们只会针对那些需要完全保证不丢失消息的场景,做出完整的解决方案。 MQ丢失如果生产者保证消息将被发送到MQ,并且MQ在接收到消息后仍将在内存中,那么它将在同步到从节点之前关闭,这可能导致消息丢失。 比如RocketMQ:RocketMQ可以分为同步刷和异步刷。默认为异步刷机,可能会导致消息在刷机到硬盘前丢失。可以通过设置为同步刷来保证消息的可靠性,这样即使MQ挂了,也可以在恢复的时候从磁盘恢复消息。 比如Kafka也可以这样配置:acks = all。只有当参与复制的所有节点都收到消息时,才会返回生产者成功。 这样,消息就会丢失,除非所有节点都死了。 Replication.factor=N,设置一个大于1的数,这将要求每个分区至少有2个副本min.insync.replicas=N,并设置一个大于1的数,这将要求领导者感知到至少有一个跟随者仍然保持连接重试次数=N,并设置一个非常大的值,以使生产者在发送失败时反复尝试。虽然我们可以通过配置实现MQ本身的高可用性,但是都有性能损失。怎么配置? 丢失消费者丢失消息的场景:消费者刚收到消息,此时服务器宕机。MQ认为消费者已经消费了消息,所以不会重复发送消息,消息丢失了。 RocketMQ默认要求消费者回复ack确认,kafka则需要手动开启配置,关闭自动偏移。 消费者不返回ack确认,根据MQ类型的不同,重传机制也不同。如果重试次数超过次数,就会进入死信队列,需要手动解决。 (Kafka没有这些)消息的最终一致性事务消息可以达到分布式事务的最终一致性,事务消息是类似MQ提供的XA的分布式事务能力。 半交易消息是MQ已经收到生产者发来的消息,但是没有收到第二次确认,无法投递的消息。 实现原理如下:生产者先向MQMQ发送半事务消息,收到消息后返回ack,确认生产者开始执行本地事务。如果事务执行成功,它会向MQ发送commit,而不会发送rollback。如果MQ长时间没有收到来自生产者的提交或回滚的第二次确认,MQ将向生产者发出一条消息,以检查生产者的查询事务执行的最终状态。最后,如果MQ收到第二次确认提交,它可以将消息传递给消费者。否则,如果是回滚,消息将在3天后保存并删除。 对于数据库整个系统来说,所有流量的查询和写入最终都会落在数据库上,这是支撑系统高并发的核心。 如何减轻数据库的压力,提高数据库的性能,是支撑高并发的基石。 处理这个问题的主要方法是读写分离,把数据库分成表。 对于整个系统,流动应该是漏斗的形式 比如我们的日常客户DAU有20万,但实际上每天可能只有3万QPS客户来到提货单页面,只有1万QPS客户成功转化为订单支付。 那么对于系统来说,阅读大于写作。这时候可以通过读写分离来减轻数据库的压力。 读写分离相当于数据库集群,减轻了单个节点的压力。 面对数据的快速增长,原有的单数据库、单表的存储模式已经不能支撑整个业务的发展。这时候就需要把数据库分成数据库和表。 就微服务而言,垂直知识库本身已经做了,剩下的大部分都是分表方案。 级表拆分首先根据业务场景确定哪些字段作为表拆分字段(sharding_key)。比如我们日订单1000万,我们的场景大部分来自C端。我们可以使用user_id作为分片_key。数据查询支持最近三个月的订单,超过三个月的归档,所以三个月的数据量是9亿,可以拆分1024张表,所以每张表 比如客户id是100,那么我们都经过hash(100),然后取模1024,也就是可以落地到对应的表上。 表拆分后ID的唯一性。由于我们的主键默认都是自增的,所以在不同的表中,表拆分后主键之间会有冲突。 有几种方法可以考虑:设置步长,比如1-1024个表。我们会分别设置1-1024的基本步长,这样主键落到不同的表就不会冲突。 分布式ID,自己实现一套分布式的ID生成算法,或者使用雪花算法等开源的,分表后不使用主键作为查询依据,而是单独给每个表增加一个新字段作为唯一主键。比如订单表的订单号是唯一的,无论最终登陆哪个表都是以订单号为查询依据,升级也是如此。 主从同步原理提交事务后,Master编写binlogslave连接master,获取binlogmaster创建的dump线程,将binlog推送给slaveslave slave启动一个io线程读取同步master的binlog。在中继日志中记录中继日志。slave然后打开一个sql线程来读取RelayLog事件并在slave中执行它。完全同步。slave记录它的binglog,因为mysql的默认复制方法是异步的。主库将日志发送给从库后,并不关心从库是否已经解决了。这样就会造成一个问题,就是假设主库挂了,从库解决不了。此时,从库提升为主库后,日志将丢失。 这就产生了两个概念。 完全同步复制主库写入binlog后,会强制同步日志到从库,所有从库在返回客户端之前都会被执行。但是,显然,这种方法会严重影响性能。 半同步复制与全同步复制的区别在于,半同步复制的逻辑如下:从库成功写入日志后,向主库返回ACK确认,主库在收到至少一个从库确认时,认为写入操作完成。 作为高性能的代表,缓存在一些特殊业务中可能会承担90%以上的热流量。 对于少数活动,比如spike,并发QPS可能是几十万,引入缓存预热可以大大减轻数据库的压力,单个数据库可能挂10万QPS,但对于redis这样的缓存来说不是问题。 以秒杀系统为例,活动预热商品信息可以提前缓存提供查询服务,活动库存数据可以提前缓存,下单过程完全缓存扣除。spike之后会异步写入数据库,数据库承受的压力会太小。 当然,引入缓存后,还要考虑缓存崩溃、雪崩、热点等一系列问题。 热键问题所谓热键问题,就是在redis上突然发出几十万个访问某个特定键的请求,会导致流量过于集中,达到物理网卡的上限,从而导致redis的服务器宕机,引发雪崩。 热键的解决方案:提前将热键拆分到不同的服务器,减少加入二级缓存的压力,将热键数据提前加载到内存中。如果redis宕机,内存查询缓存崩溃的概念就是单键并发访问过高,过期直接将所有请求发送到db。这类似于热键的问题,但关键是当它过期时,所有请求都被发送到DB。 解决方法:锁升级,比如一个请求查询A,发现不在缓存中,就锁关键A,同时去数据库查询数据,写入缓存,然后返回给客户,这样后续的请求就可以从缓存中获取数据。 在value中写入到期时间组合,用异步的方式不断刷新到期时间,防止这种现象。 缓存穿透缓存穿透是指查询缓存中不存在的数据,每个请求都会命中DB,就像缓存不存在一样。 要解决这个问题,加一层Bloom滤镜。 Bloom filter的原理是,当你存储数据时,它会被hash函数映射到一个位数组中的K个点上,它们同时被设置为1。 这样,当客户再次来查询A,A在Bloom filter值为0时直接返回,就不会有击穿请求打到DB。 显然,使用Bloom filter后,会出现误判的问题。因为它本身是一个数组,所以可能会有多个值落在同一个位置。所以理论上,如果我们的数组长度足够长,误判的概率会更低。这种问题会根据实际情况。 缓存雪崩当某个时刻发生大规模的缓存故障时,比如你的缓存服务宕机了,大量的请求会进来,直接冲击DB,可能导致整个系统的崩溃,这就是所谓的雪崩。 与雪崩击穿和热键不同,它意味着大规模缓存已经过期。 雪崩有几种解决方案:为不同的密钥设置不同的到期时间,避免时限流量同时到期;如果redis宕机,可以限制电流,避免大量请求同时崩溃DB L2缓存,同时加热按键。 稳定熔断,比如营销服务挂机或者大量接口超时的异常情况,不能影响订单的主要环节,少量涉及扣分的操作可以事后补救。 限流对突发脉冲(如大尖峰脉冲)有很高的并发性。如果少数接口没有通过限流解决,可能会直接暂停服务。根据对各接口压力测量性能的评估,进行适当的电流限制尤为重要。 降级熔断后,其实可以说是降级的一种。比如融合营销接口后的降级方案,就是短时间内不会调用营销服务,等营销恢复后再调用。 一般来说,即使有统一的配置中心,也是不允许在业务高峰期做任何改动的,但是在紧急情况下可以通过配置适当的方案进行少量改动。 针对检查各种分布式系统产生的分布式事务的一致性或攻击导致的数据异常,验证平台做最后的数据验证是非常必要的。 比如下游支付系统和订单系统的金额是否能正确核对,如果收到中间人攻仓的数据是否能保证正确性? 其实从总结中可以看出,如何设计高并发系统这个问题本身并不难。无非是基于你所知道的知识点,从物理硬件层面到软件架构和代码层面的优化,用什么中间件来不断提高系统的抗压能力。 但是,这个问题本身会带来更多的问题。微服务的拆分本身会带来分布式事务的问题。使用http和RPC框架会带来通信效率、路由和容错等问题。MQ的引入会带来消息丢失、积压、事务消息和顺序消息等问题。缓存的引入也会带来一致性、雪崩、崩溃的问题。数据库读写分离,数据库和表分离会带来主从同步延迟、分布式ID、事务一致性等问题。为了解决这些问题,我们必须不断增加各种措施,如熔断、限流、降级、离线验证和方案求解,以防止和跟踪这些问题。


  • 全部评论(0)
资讯详情页最新发布上方横幅
最新发布的资讯信息
【技术支持|常见问题】1556原创ng8文章搜索页面不齐(2024-05-01 14:43)
【技术支持|常见问题】1502企业站群-多域名跳转-多模板切换(2024-04-09 12:19)
【技术支持|常见问题】1126完美滑屏版视频只能显示10个(2024-03-29 13:37)
【技术支持|常见问题】响应式自适应代码(2024-03-24 14:23)
【技术支持|常见问题】1126完美滑屏版百度未授权使用地图api怎么办(2024-03-15 07:21)
【技术支持|常见问题】如何集成阿里通信短信接口(2024-02-19 21:48)
【技术支持|常见问题】算命网微信支付宝产品名称年份在哪修改?风水姻缘合婚配对_公司起名占卜八字算命算财运查吉凶源码(2024-01-07 12:27)
【域名/主机/服务器|】帝国CMS安装(2023-08-20 11:31)
【技术支持|常见问题】通过HTTPs测试Mozilla DNS {免费源码}(2022-11-04 10:37)
【技术支持|常见问题】别告诉我你没看过邰方这两则有思想的创意广告! (2022-11-04 10:37)

联系我们
Q Q:375457086
Q Q:526665408
电话:0755-84666665
微信:15999668636
联系客服
企业客服1 企业客服2 联系客服
86-755-84666665
手机版
手机版
扫一扫进手机版
返回顶部