* 1、SpringCloud有哪些常用组件分别是什么作用答:Nacos, OpenFeign, Sentinel,Seata,RabbitMQ,GatewayNacos:服务注册中心,提供服务注册和发现功能OpenFeign:实现远程调用Sentinel:提供服务容错保护Seata:实现分布式事务管理RabbitMQ:实现异步通知GatewayAPI网关服务作用安全路由限流监控* 2、服务注册发现的基本流程是怎样的答:服务启动时就会注册自己的服务信息服务名、IP、端口到注册中心* 调用者可以从注册中心订阅想要的服务获取服务对应的实例列表1个服务可能多实例部署​* 3、Eureka和Nacos有哪些区别答: Nacos与Eureka的区别1.Nacos支持服务端主动检测提供者状态: 临时实例采用心跳模式,非临时实例采用主动检测模式2.临时实例心跳不正常会被剔除,非临时实例则不会被剔除3.Nacos支持服务列表变更的消息推送模式,服务列表更新更及时4.Nacos集群默认采用AP方式,但也支持CP;Eureka采用AP方式​* 4、Nacos的分级存储模型是什么意思答:任何一个微服务的实例在注册到Nacos时都会生成以下几个信息用来确认当前实例的身份从外到内依次是namespace命名空间group分组service服务名cluster集群instance实例包含ip和端口这就是nacos中的服务分级模型。​* 5、OpenFeign是如何实现负载均衡的答:Spring在整合OpenFeign的时候实现org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient类其中定义了OpenFeign发起远程调用的核心流程。也就是四步* 获取请求中的serviceId* 根据serviceId负载均衡找出一个可用的服务实例* 利用服务实例的ip和port信息重构url* 向真正的url发起请求而具体的负载均衡则是不是由OpenFeign组件负责。而是分成了负载均衡的接口规范以及负载均衡的具体实现两部分。负载均衡的接口规范是定义在Spring-Cloud-Common模块中包含下面的接口* LoadBalancerClient负载均衡客户端职责是根据serviceId最终负载均衡选出一个服务实例* ReactiveLoadBalancer负载均衡器负责具体的负载均衡算法OpenFeign的负载均衡是基于Spring-Cloud-Common模块中的负载均衡规则接口并没有写死具体实现。这就意味着以后还可以拓展其它各种负载均衡的实现。不过目前SpringCloud中只有Spring-Cloud-Loadbalancer这一种实现。​Spring-Cloud-Loadbalancer模块中实现了Spring-Cloud-Common模块的相关接口具体如下* BlockingLoadBalancerClient实现了LoadBalancerClient会根据serviceId选出负载均衡器并调用其算法实现负载均衡。* RoundRobinLoadBalancer基于轮询算法实现了ReactiveLoadBalancer* RandomLoadBalancer基于随机算法实现了ReactiveLoadBalancer* 6、 什么是服务雪崩常见的解决方案有哪些答:服务雪崩是指在分布式系统中由于某个服务的响应时间过长或不可用导致调用该服务的其他服务也受到影响进而引发连锁反应最终导致整个系统瘫痪的现象。这种现象通常发生在微服务架构中因为微服务之间的依赖关系复杂一个服务的故障可能会迅速传播到整个系统。解决方案熔断机制熔断器模式Circuit Breaker Pattern是一种常见的解决方案。当某个服务的错误率达到一定阈值时熔断器会自动断开对该服务的调用避免请求继续发送到故障服务从而防止故障扩散。熔断器通常有三种状态关闭正常、打开熔断和半开尝试恢复。限流机制限流Rate Limiting是指限制某个服务的请求速率防止服务过载。常见的限流算法包括令牌桶算法、漏桶算法等。降级机制降级Degradation是指在服务出现故障时提供一个备用的、简化版的实现以保证核心功能的可用性。例如当某个服务不可用时可以返回一个默认值或缓存数据而不是直接抛出异常。超时机制超时Timeout是指设置一个合理的超时时间当服务调用超过该时间时直接返回错误避免长时间等待导致资源耗尽。重试机制重试Retry是指在服务调用失败时自动重试一定次数以提高请求成功的概率。但需要注意重试机制可能会加重服务负载因此需要合理设置重试次数和间隔时间。负载均衡负载均衡Load Balancing是指将请求分发到多个服务实例上避免单个实例过载。常见的负载均衡算法包括轮询、加权轮询、最少连接等。* 线程池隔离给每个服务调用业务分配一个线程池利用线程池本身实现隔离效果* 信号量隔离不创建线程池而是计数器模式记录业务使用的线程数量达到信号量上限时禁止新的请求​* 7、Hystix和Sentinel有什么区别和联系答:无论是Hystix还是Sentinel都支持线程隔离不过其实现方式不同。Hystix默认是基于线程池实现的线程隔离,每一个被隔离的业务都要创建一个独立的线程池,线程过多会带来额外的CPU开销,性能一般,但是隔离性更强.支持主动超时和异步调用,适用场景为低扇出Sentinel则是基于信号量隔离的原理,这种方式不用创建线程池,性能较好,但是隔离性一般.不支持主动超时和异步调用,使用场景为高频调用和高扇出Sentinel的线程隔离就是基于信号量隔离实现的而Hystix两种都支持但默认是基于线程池隔离。​* 8、限流的常见算法有哪些答:固定窗口计算器算法固定窗口计数器算法概念如下将时间划分为多个窗口,窗口时间跨度称为Interval,本例中为1000ms每个窗口分别计数统计,每有一次请求就将计数器加一,限流就是设置计数器阈值,本例为3如果计数器超过了限流阈值,则超出阈值的请求都被丢弃​滑动窗口算法(默认区间数量是2,区间数越多,计数器越多,压力越大)Sentinel滑动窗口计数器算法会将一个窗口划分为n个更小的区间,例如窗口时间跨度Interval为1秒;区间数量n2,则每个小区间时间跨度为500ms,每个区间都有计数器限流阈值依然为3,时间窗口(1秒)内请求超过阈值时,超出的请求被限流窗口会根据当前请求所在时间(currentTime)移动,窗口范围是从(currentTime-Interval)之后的第一个时区开始,到currentTime​限流之后(滑动窗口算法后,默认方案是快速拒绝:抛出异常,失败)其他方案:漏桶算法将每个请求视作水滴放入漏桶进行存储漏桶以固定速率向外漏出请求来执行,如果漏桶空了则停止漏水如果漏桶满了则多余的水滴会被直接丢弃可以理解成请求在桶内排队等令牌桶算法以固定的速率生成令牌,存入令牌桶中,如果令牌桶满了以后,停止生成请求进入后,必须先尝试从桶中获取令牌,获取到令牌后才可以被处理如果令牌桶中没有令牌,则请求等待或丢弃​* 9、什么是CAP理论和BASE思想答:1998年,加州大学的计算机科学家Eric Brewer提出,分布式系统有三个指标:Consistency(一致性): 用户访问分布式系统中的任意节点,得到的数据必须一致Availability(可用性): 用户访问分布式系统时,读或写操作总能成功.只能读不能写,或者只能写不能读,或者两者都不执行,就说明系统弱可用或不可用Partition tolerance(分区容错性): 因为网络故障或其他原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区, 系统要能容忍网络分区现象,出现分区时,整个系统也要持续对外提供服务分布式系统无法同时满足这三个指标这个结论就叫做CAP定理​BASE理论是对CAP的一种解决思路,包含三个思想:Basically Available(基本可用): 分布式系统在出现故障时,允许损失部分可用性,即保证核心可用Soft State(软状态): 在一定时间内,允许出现中间状态,比如临时的不一致状态Eventually Consistent(最终一致性): 虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致* 10、项目中碰到过分布式事务问题吗怎么解决的答:分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致.但事务等待过程中,处于弱可用状态AP模式:各个事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致.​* 11、AT模式如何解决脏读和脏写问题的答:AT模式也分为两个阶段第一阶段是记录数据快照执行并提交事务第二阶段根据阶段一的结果来判断* 如果每一个分支事务都成功则事务已经结束因为阶段一已经提交因此删除阶段一的快照即可* 如果有任意分支事务失败则需要根据快照恢复到更新前数据。然后删除快照这种模式在大多数情况下99%并不会有什么问题不过在极端情况下特别是多线程并发访问AT模式的分布式事务时有可能出现脏写问题解决思路就是引入了全局锁的概念。在释放DB锁之前先拿到全局锁。避免同一时刻有另外一个事务来操作当前数据。* 12、TCC模式与AT模式对比有哪些优缺点答:TCC模式的每个阶段是做什么的?Try: 资源检查和预留Confirm: 业务执行和提交Cancel: 预留资源的释放TCC的优点是什么?一阶段完成直接提交事务,释放数据库资源,性能好相比AT模型,无需生成快照,无需使用全局锁,性能最强不依赖数据库,而是依赖补偿操作,可以用于非事务型数据库TCC的缺点是什么?有代码侵入,需要人为编写try,Confirm和Cancel接口,太麻烦软状态,事务是最终一致需要考虑Confirm和Cancel的失败情况,做好幂等处理​* 13、RabbitMQ是如何确保消息的可靠性的答:1.首先第一种情况就是生产者发送消息时出现了网络故障导致与MQ的连接中断。生产者重试机制:为了解决这个问题SpringAMQP提供的消息发送时的重试机制。即当RabbitTemplate与MQ连接超时后多次重试。2.在少数情况下也会出现消息发送到MQ之后丢失的现象生产者确认机制:RabbitMQ提供了生产者消息确认机制包括Publisher Confirm和Publisher Return两种。在开启确认机制的情况下当生产者发送消息给MQ后MQ会根据消息处理的情况返回不同的回执。3.消息到达MQ以后如果MQ不能及时保存也会导致消息丢失所以MQ的可靠性也非常重要。数据持久化:为了提升性能默认情况下MQ的数据都是在内存存储的临时数据重启后就会消失。为了保证数据的可靠性必须配置数据持久化包括- 交换机持久化 - 队列持久化 - 消息持久化4.为了确认消费者是否成功处理消息RabbitMQ提供了消费者确认机制Consumer Acknowledgement。即当消费者处理消息结束后应该向RabbitMQ发送一个回执告知RabbitMQ自己消息处理状态。回执有三种可选值* ack成功处理消息RabbitMQ从队列中删除该消息* nack消息处理失败RabbitMQ需要再次投递消息* reject消息处理失败并拒绝该消息RabbitMQ从队列中删除该消息5.当消费者出现异常后消息会不断requeue重入队到队列再重新发送给消费者。如果消费者再次执行依然出错消息会再次requeue到队列再次投递直到消息处理成功为止.失败重试机制在消费者出现异常时利用本地重试而不是无限制的requeue到mq队列。在yml文件上配置retry:enabled: true* 开启本地重试时消息处理过程中抛出异常不会requeue到队列而是在消费者本地重试* 重试达到最大次数后Spring会返回reject消息会被丢弃6.本地测试达到最大重试次数后消息会被丢弃。这在某些对于消息可靠性要求较高的业务场景下显然不太合适了。失败处理策略:因此Spring允许我们自定义重试次数耗尽后的消息处理策略这个策略是由MessageRecovery接口来定义的它有3个不同 实现* RejectAndDontRequeueRecoverer重试耗尽后直接reject丢弃消息。默认就是这种方式* ImmediateRequeueMessageRecoverer重试耗尽后返回nack消息重新入队* RepublishMessageRecoverer重试耗尽后将失败消息投递到指定的交换机​* 14、RabbitMQ是如何解决消息堆积问题的答:在默认情况下RabbitMQ会将接收到的信息保存在内存中以降低消息收发的延迟。但在某些特殊情况下这会导致消息积压比如* 消费者宕机或出现网络故障* 消息发送量激增超过了消费者处理速度* 消费者处理业务发生阻塞一旦出现消息堆积问题RabbitMQ的内存占用就会越来越高直到触发内存预警上限。此时RabbitMQ会将内存消息刷到磁盘上这个行为成为PageOut. PageOut会耗费一段时间并且会阻塞队列进程。因此在这个过程中RabbitMQ不会再处理新的消息生产者的所有请求都会被阻塞。​为了解决这个问题从RabbitMQ的3.6.0版本开始就增加了Lazy Queues的模式也就是惰性队列。惰性队列的特征如下* 接收到消息后直接存入磁盘而非内存* 消费者要消费消息时才会从磁盘中读取并加载到内存也就是懒加载* 支持数百万条的消息存储