1.什么是CAP
CAP定理,也被称为Brewer定理,是分布式计算中的一个重要概念。它由计算机科学家Eric Brewer于2000年提出,并由Seth Gilbert和Nancy Lynch于2002年正式证明。CAP定理强调了分布式系统中三个关键属性之间的固有权衡,这三个属性分别是一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)。
以下是CAP定理的详细解释:
- 一致性(Consistency) :
- 在分布式系统中,一致性意味着系统中的所有节点在同一时间看到相同的数据。
- 换句话说,当发生写操作时,所有后续的读操作都应反映该写操作。
- 一致性保证了数据的同步性。
- 可用性(Availability) :
- 可用性指的是系统中每个节点对读和写请求的响应能力,即使一些节点经历故障或延迟。
- 可用性系统确保请求得到响应,但不保证它包含最新的写入。 1 15 2 10+5=15
- 在一个高可用的系统中,即使某些节点出现故障,用户仍然能够提交请求并获得响应。
- 分区容忍性(Partition Tolerance) :
- 分区容忍性涉及系统在发生网络分区(通信失败)时继续运行和提供服务的能力。
- 它意味着系统可以容忍消息的丢失或节点间通信的延迟。
- 在分布式系统中,节点之间的网络通信可能会发生故障,导致节点之间无法直接通信,形成网络分区。
CAP定理的核心思想是,一个分布式系统最多只能同时实现这三个属性中的两个。具体来说,系统设计者必须在以下三者之间做出权衡:
- 一致性与可用性 :如果系统选择一致性和可用性,那么它会牺牲分区容忍性。在这种情况下,系统在发生网络分区时可能会停止服务,直到网络恢复,以确保数据的一致性和系统的可用性。传统的关系型数据库(如MySQL、PostgreSQL)通常属于这一类别。
- 一致性与分区容忍性 :如果系统选择一致性和分区容忍性,那么它会牺牲可用性。在网络分区发生时,系统会优先保证数据的一致性,即使这意味着在分区期间无法提供服务。HBase和Zookeeper等系统在某些配置下可以保证一致性和分区容忍性。
- 可用性与分区容忍性 :如果系统选择可用性和分区容忍性,那么它会牺牲一致性。在网络分区发生时,系统会继续处理请求,即使这可能导致数据的不一致。这种系统通常依赖于“最终一致性”模型,即数据在一定时间内会达到一致性状态。DynamoDB、Cassandra等NoSQL数据库系统在设计时会优先保证可用性和分区容忍性。
为什么必须要保证分区容错性(Base理论)
BASE理论由eBay的架构师提出,是对CAP理论的一种延伸和补充。CAP理论指出,一个分布式系统不可能同时满足一致性(Consistency)、可用性(Availability)和分区容忍性(Partition Tolerance)这三个属性。而BASE理论则提供了一种新的视角,即在无法做到强一致性的情况下,每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
BASE理论包含三个核心要素:
- 基本可用(Basically Available) :
- 系统保证在出现故障或部分失效的情况下仍然可以保证基本的可用性。这意味着虽然系统可能无法保证100%的可用性,但是它仍然会尽力保证在任何时候都能够提供基本的服务。
- 基本可用通常允许系统在出现故障时部分功能暂时失效,但其他功能仍然可以正常工作。
- 软状态(Soft State) :
- 系统中的数据状态可以在一段时间内是不一致的,即系统中的数据副本可能存在短暂的冲突或不同步。这种状态是暂时的,系统会通过后续的处理来逐渐将数据状态调整为一致。
- 软状态允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性。
- 最终一致性(Eventually Consistent) :
- 系统的数据最终会达到一致的状态,但在某个时间点上可能存在不一致的情况。分布式系统中的不同节点可能具有不同的数据副本,而这些副本之间的同步需要一定的时间。
- 最终一致性要求系统在一定时间范围内能够达到数据的一致性,允许在同步过程中存在短暂的不一致状态。
二、BASE理论的应用场景
BASE理论适用于大型高可用、可扩展的分布式系统。与传统ACID(原子性、一致性、隔离性、持久性)特性相反,BASE理论通过牺牲强一致性来获得可用性,并允许数据在短时间内的不一致,但最终达到一致状态。
在实际应用中,BASE理论常用于以下场景:
- 电子商务网站 :
- 在高峰期,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面,但其他功能(如浏览商品)仍然可以正常使用。
- 允许异步的数据复制方式,将数据副本在后台进行异步同步,从而提高系统的响应性能和可用性。
- 社交媒体平台 :
- 用户需要实时地与其他用户进行互动,并获取最新的信息更新。
- 可以采用缓存技术和分布式数据存储,以加速数据访问和查询。
- 在一致性方面,可以采用最终一致性的策略,即用户可以看到稍有延迟的最新数据。
- 金融交易系统 :
- 数据的一致性和可靠性至关重要。
- 但在某些情况下,为了提高性能和可用性,可能会采用最终一致性的策略,并通过其他手段(如分布式事务)来确保关键数据的一致性。
三、BASE理论与CAP理论的关系
BASE理论是对CAP理论的一种延伸和补充。CAP理论强调了分布式系统中三个关键属性之间的固有权衡,而BASE理论则提供了一种新的视角和解决方案。具体来说:
- CAP理论指出,一个分布式系统不可能同时满足一致性、可用性和分区容忍性这三个属性。
- BASE理论则强调,在无法做到强一致性的情况下,可以通过牺牲部分一致性来获得更高的可用性和灵活性。
- BASE理论中的基本可用、软状态和最终一致性三个要素,正是对CAP理论中一致性、可用性和分区容忍性权衡的结果。
四、BASE理论的优缺点
优点 :
- 提高了系统的可用性和灵活性 :通过牺牲部分一致性,BASE理论允许系统在出现故障或部分失效的情况下仍然能够提供服务。
- 支持大规模分布式系统 :BASE理论适用于大型高可用、可扩展的分布式系统,能够满足高并发和大规模数据处理的需求。
缺点 :
- 数据一致性难以保证 :由于允许数据在短时间内的不一致,BASE理论可能导致数据冲突和不一致的问题。
- 系统复杂度增加 :为了实现最终一致性,系统需要采用复杂的同步机制和算法,增加了系统的复杂度和维护成本。
综上所述,BASE理论是分布式系统设计中的一种重要理论,它强调在一致性、可用性和分区容忍性之间做出权衡。通过牺牲部分一致性,BASE理论提高了系统的可用性和灵活性,并支持大规模分布式系统。然而,它也存在数据一致性难以保证和系统复杂度增加等缺点。因此,在设计和实现分布式系统时,需要充分考虑BASE理论的应用场景和优缺点,并根据具体需求进行权衡和选择。
2.如何解决架构的复杂度问题,或者换句话说,如何降低架构的复杂度
我们到底要如何降低系统的复杂度呢?本质思想如上所述,需要加入外力的干预,最好是强力的干预。而这个外力我认为就是架构设计思想和架构解耦工具
1.架构分层
我认为分层几乎是系统设计中最重要的思想,可以参考计算机网络协议和计算机缓存设计等。
三层架构
四层架构 职责单一化
五层架构
六边形架构 干净架构 洋葱架构
2.领域拆分
良好的领域拆分需要依赖系统设计人员对系统应用场景的深刻理解。且具备很好的抽象能力。
领域驱动设计
3.服务聚合
同拆分一样,找到同类的功能进行聚合,也是需要对场景的理解,且需要不断优化和尝试。
A +B = C
4.高度自治
系统拆分与聚合清晰后,需要独立模块有高度自治的能力。对外定义控制输入和输出协议,对内实现独立且明确的能力。 高内聚 低耦合
5.链路简单清晰
系统链路一定要简化,尤其系统核心链路。简单意味着好理解、易维护、稳定性强、容易扩展。
用户-商品-订单- 库存- 支付 冗余到一起
用户—- 首页 —–商品——购物车— 订单——库存— 支付—订单
核心链路 (核心模块 下单 )
3.微服务项目中有哪些特殊的解耦方式(代码层面)
解耦工具-主要指常用的代码设计方法
1.事件消息机制
1 | import java.util.ArrayList; |
在这个事件驱动的代码中,我们有一个Event类来表示事件,并具有一个类型属性。我们还有一个EventHandler接口,用于处理事件。最后,我们有一个EventDispatcher类,用于添加处理程序并分派事件。
可以创建不同类型的事件,并创建实现EventHandler接口的处理程序来处理这些事件。然后,可以将处理程序添加到EventDispatcher中以便可以启动事件处理。
例如,我们可以创建一个名为“ButtonClickEvent”的事件,并创建一个名为“ButtonClickEventHandler”的处理程序来处理该事件。然后,可以将该处理程序添加到EventDispatcher中,以便在单击按钮时启动事件处理。
1 | public class ButtonClickEvent extends Event { |
在这个示例中,我们创建了一个名为“ButtonClickEvent”的事件,并创建一个名为“ButtonClickEventHandler”的处理程序来处理它。然后,我们创建了一个事件分派器,并添加了该处理程序。最后,我们分派按钮单击事件,该事件将触发执行单击按钮时要执行的操作。
可以根据需要添加更多的事件和处理程序,并根据事件类型执行不同的操作。
2.策略、责任链等设计模式
在电商场景中,责任链模式可以用于处理订单退款申请。下面是一个简单的电商场景的责任链Java代码实现:
1 | public abstract class RefundHandler { |
在这个责任链的实现中,我们定义了一个抽象的RefundHandler类,它有一个指向下一个处理程序的引用,还有一个处理退款申请的抽象方法handle。如果当前处理程序无法处理退款申请,则将退款请求转发给下一个处理程序。
我们还定义了三个具体的处理程序CustomerServiceHandler、FinanceHandler和LogisticsHandler,它们分别处理不同类型的退款请求。如果一个处理程序可以处理退款请求,则处理该请求并结束责任链。否则,将该请求传递给下一个处理程序。
我们可以将这些处理程序按照责任链的顺序连接起来:
1 | RefundHandler logisticsHandler = new LogisticsHandler(); |
然后,当收到一个退款请求时,我们将它传递给责任链的第一个处理程序:
1 | Order order = getOrder(); |
如果第一个处理程序无法处理该请求,则将其传递给下一个处理程序。如果所有处理程序都无法处理该请求,则在最后提供默认响应。
1 | System.out.println("没有处理该退款请求的处理程序"); |
3.规则引擎
规则引擎可以帮助你将逻辑和数据解耦,数据放入领域模型中,逻辑放入规则中
电商领域的规则引擎可以用来处理价格计算、促销活动、优惠券使用等复杂的业务逻辑。以下是一个电商领域的规则引擎的简单示例代码:
假设我们需要计算某个商品的价格,考虑到会有多种不同的优惠策略,如会员折扣、新用户优惠、满减等,这时我们可以使用规则引擎来实现灵活的计价方案。
首先我们定义一个Rule类,用来表示一个规则:
1 | public abstract class Rule<T> { |
在这里,我们定义了两个抽象方法 evaluate
和 calculate
,分别用来评估当前规则是否适用于给定的对象(如某个订单)以及如何计算折扣后的价格等。
接下来,我们定义一个RuleEngine类,用来实现规则的执行逻辑:
1 | public class RuleEngine<T> { |
在这里,我们定义了一个 addRule
方法和一个 execute
方法。其中 addRule
用来添加规则,而 execute
方法则用来执行规则引擎的逻辑。在 execute
方法中,我们首先对规则按照优先级进行排序,然后依次运行每一个规则。如果当前规则适用于给定的对象,则执行该规则的计算方法,累加价格并返回。
最后,我们可以使用具体的规则来实现价格计算:
1 | public class MemberDiscountRule extends Rule<Order> { |
在这里,我们实现了会员折扣、新用户优惠和满减等三个规则。通过 evaluate
方法判断当前规则是否适用于给定的订单,通过 calculate
计算满足当前规则后的价格。我们可以通过如下方式来应用这些规则:
1 | RuleEngine<Order> engine = new RuleEngine<>(); |
当然,市面上也有一些开源的规则引擎,这个更好用一些,比如drools这种
4.状态机
1 | public enum State { |
在这个状态机中,我们有三个状态: STATE_ONE,STATE_TWO和STATE_THREE。在构造函数中,我们将初始状态设置为STATE_ONE。
processInput方法接受输入,并根据当前状态进行转换。在每个状态中,我们检查输入并根据需要更新状态。
例如,如果我们处于STATE_ONE状态,并且输入是“A”,我们将转换到STATE_TWO状态。如果我们处于STATE_TWO状态并且输入是“B”,我们将转换到STATE_THREE状态。如果我们处于STATE_TWO状态但输入不是“B”,我们将返回到STATE_ONE状态。在STATE_THREE状态中,如果输入是“C”,我们将返回到STATE_ONE状态。否则,我们将保持在STATE_THREE状态中。
可以根据需要修改此状态机,例如,添加更多状态或更改状态之间的转换条件。
4.服务划分原则
在微服务架构中,服务划分可以基于多种因素,例如业务功能、数据域、可扩展性、可维护性等。以下是一些常用的划分方法:
- 基于业务功能:将服务划分为不同的业务功能单元,例如订单服务、支付服务、用户服务等。
- 基于数据域:将服务按照数据领域进行划分,例如客户服务(用户中心)、订单服务(订单中心)、库存服务(WMS数据中心)等。
- 基于可扩展性:将服务划分为可以水平扩展的单元,例如将前端服务划分为多个负载均衡的实例,每个实例都可以处理一部分流量。
- 基于可维护性:将服务划分为易于维护和更新的单元,例如将核心服务与辅助服务分离,将通用功能提取为独立的服务。
在面试时,应该清楚地解释您所选择的划分方法,并说明其优缺点以及在什么情况下该方法适用。此外,您应该能够描述如何将这些服务组合成一个完整的应用程序,并讨论在不同服务之间通信的方式。最后,您可能需要讨论一些与微服务相关的挑战,例如服务发现、服务治理、数据一致性等,并说明您如何解决这些挑战
5.微服务架构设计的优缺点
1.微服务架构的优点
- 灵活性高 :它将应用程序分解为小型服务(松散耦合),使其开发、维护更快,更易于理解,可以提供更高的灵活性;
- 独立扩展 :它使每个服务能够独立扩展,将系统中的不同功能模块拆分成多个不同的服务,这些服务进行独立地开发和部署,每个服务都运行在自己的进程内,这样每个服务的更新都不会影响其他服务的运行;
- 支持多种编程语言 :微服务可通过最佳及最合适的不同的编程语言与工具进行开发,能够做到有的放矢地解决针对性问题;异构开发
- 自动部署与持续集成工具集成 :它允许以灵活的方式将自动部署与持续集成工具集成,例如Jenkins,Hudson等;
- 通用性 :通过服务实现应用的组件化(按功能拆分、可独立部署和维护),围绕业务能力组织服务,根据业务不同的需求进行不同组件的使用,所做产品非项目化,对于平台具有一定的通用性。
2.微服务架构的缺点
- 处理故障难度高 :微服务架构是一个分布式系统,必须构建一个相互通信机制并处理部分故障;
- 部署工作量大 :整体式应用程序可以部署在负载平衡器后面的相同服务器上。但对于微服务,每个服务都有不同的实例,每个实例都需要配置、部署、缩放和监控;
- 测试复杂度高 :微服务在一定程度上也会导致系统变得越来越复杂,增加了集成测试的复杂度;
- 运营成本增加 :整体应用可能只需部署至一小片应用服务区集群,而微服务架构可能变成需要构建/测试/部署/运行数十个独立的服务,并可能需要支持多种语言和环境。这导致一个整体式系统如果由20个微服务组成,可能需要40~60个进程;
- 发布风险高 :把系统分为多个协作组件后会产生新的接口,这意味着简单的交叉变化可能需要改变许多组件,并需协调一起发布。在实际环境中,一个新品发布可能被迫同时发布大量服务,由于集成点的大量增加,微服务架构会有更高的发布风险;
- 分布性系统问题 :作为一种分布式系统,微服务引入了复杂性和其他若干问题,例如网络延迟、容错性、消息序列化、不可靠的网络、异步机制、版本化、差异化的工作负载等,开发人员需要考虑以上的分布式系统问题。
6.你们项目接口性能指标有哪些标准?
互联网项目中,接口性能指标可以从不同的角度进行考虑,以下是一些常用的指标:
- 响应时间:指接口处理请求并返回结果所需的时间。通常,响应时间应该尽可能地短,一般来说不超过1秒,最好控制在0.5秒以内。 接口响应比较慢
网络传输的事件 + 业务程序处理的时间 +数据库连接时间 + 数据库的处理逻辑的时间 + 返回的时间 - 吞吐量:指接口每秒处理的请求数。高吞吐量可以使系统更有效地使用资源并提高访问效率。对于高并发的应用,吞吐量要求会比较高,需要根据实际情况进行合理的调整。 一般项目要求96%以上,但是根据CPU上下文切换时间不同,有所下降 JVM的吞吐量
- 并发数:指在同一时间内有多少个请求在处理。并发数过高可能会导致请求响应时间延长或者系统崩溃,需要合理地控制并发数。 网关 业务服务层面
- 错误率:指接口处理失败或者返回错误结果的概率。错误率应该尽可能低,一般来说不超过1%,同时还需要对错误进行详细记录与分析,及时发现并解决问题。
- 延迟分布情况:指接口响应时间在不同请求场景下的分布情况,如 90%的请求响应时间在1秒以内,99%的请求响应时间在2秒以内等。延迟分布情况可以给出更详细的性能数据,并根据数据来判断是否需要调整系统架构或者调整业务流程。
- 网络流量:指网络传输的数据量,可以根据实际情况进行监控和调整。Zabbix进行监控
- 系统资源利用率:指系统使用的CPU、内存、磁盘等资源的利用率,需要根据实际情况进行监控和调整。
综上所述,接口性能指标有很多,需要根据具体业务场景来选择和权衡。在实际项目中,可以使用一些专业的性能测试工具,如JMeter、Gatling等来进行性能测试,并根据测试结果进行适当的调整和优化。
7.在微服务架构下,如何判定性能的标准:
在微服务架构下,判别接口性能的标准一般可以从以下几个方面进行评估:
- 请求响应时间:这是最基本的性能评估指标之一。请求响应时间指的是从客户端发出请求到服务器处理完请求并返回响应所需要的时间。一般来说,请求响应时间越短,说明服务性能越好。
- 吞吐量:吞吐量指的是在一定时间内可以处理的请求数量。一般来说,吞吐量越高,说明服务性能越好。
- 并发能力:并发能力指的是服务器同时处理多个请求的能力。一般来说,服务器的并发能力越强,说明服务性能越好。
- 容错能力:容错能力指的是在出现异常情况时,服务器的自我保护和恢复能力。一般来说,容错能力越强,说明服务性能越好。
- 稳定性:稳定性指的是服务在长时间运行过程中,是否出现过崩溃、死锁等问题。一般来说,稳定性越高,说明服务性能越好。
在实际的性能测试中,除了以上几个方面,还可以根据具体业务需求进行评估。同时,在进行性能测试时,需要考虑到并发场景、请求量、负载均衡、缓存等因素对服务性能的影响,综合评估服务的性能表现。
微服务各个组件的理论短聊知识点:
断路器的隔离方式(线程池隔离以及信号量隔离)有什么区别
在断路器中,介绍两种处理高并发的解决方案。
首先需要理解高并发的情况下系统会出现什么样的问题。
当部署完一个服务后,这个服务会向外界开放多个接口, 比如 一个烂大街的商城系统可能有 订单查询接口, 个人中心接口 , 付款接口 ,商品查询接口。 当服务部署好之后,没有其他配置时, tomcat默认开启一个线程池, 这个线程池中有200个线程供使用。 这时候, 这四个接口都有对这个线程池的使用权,也就是说这四个接口共享一个线程池。 当访问量小的时候系统没有问题, 但是遇到突发情况,比如一类爆款商品降价, 导致了商品查询接口访问量激增。 商品查询接口占用了线程池中大量的线程, 导致其他三个接口抢不到线程从而没有线程可用, 这时候, 由于四个接口共享一个线程池, 当一个接口访问量激增而占用大量资源时, 导致其他三个接口抢不到资源进而导致自身功能不可用。
线程池隔离
这时候,提出一种解决方案–线程池隔离。
线程池隔离的思想是: 把tomcat中先一个线程池分成两个线程池. 比如tomcat线程池中初始有200个线程, 分成两个线程池A , B后, A线程池有50个线程可以用, B线程池有150个线程可以用. 将访问量较大的接口单独配置给一个线程池, 其他接口使用另一个线程池 , 使其访问量激增时不要影响其他接口的调用.
然后, 将访问量暴增的接口访问交给A线程池, 其他接口的访问交给B线程池. A , B两个线程池是相互隔离的, 互不影响. 这时候, 如果商品查询接口访问量激增 , 被挤爆的线程池也只是A线程池, A,B线程池互不影响, 所以其他接口如: 个人中心接口, 付款接口, 订单查询接口依然可用.
线程池隔离主要针对C端用户对服务的访问. 线程池隔离起到分流的作用.
信号量隔离
还有一种是新思路是采用信号量隔离方式.
可以把信号量理解成一个计数器 , 对这个计数器规定一个计数上限, 代表一个接口被访问的最大量.
假定设置 付款接口的信号量最大值为10,(这个接口最多占用线程池中10个线程) 初始值为0. 每调用一次接口信号量加一 , 接口处理完后信号量减一. 当信号量值达到最大时 , (10时) , 对后续的调用请求拒接处理.
信号量隔离主要是针对各个服务内部的调用处理, 起到限流的作用.
API网关的工作流程
客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射(Gateway Handler Mapping)确定请求与路由匹配,则将其发送到网关Web处理程序(Gateway Web Handler)。该处理程序通过特定于请求的过滤器链来运行请求。过滤器器由虚线分隔的原因是,过滤器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后置”过滤器逻辑。图中虚线左边的对应于前置过滤器,虚线右边的对应于后置过滤器。
- 前置过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;
- 后置过滤器可以做响应内容、响应头的修改、日志的输出、流量监控等。
SpringCloud Gateway的核心逻辑其实就是路由转发和执行过滤器链
过滤器执行顺序
请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter
请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:
排序的规则是什么呢?
每一个过滤器都必须指定一个int类型的order值,order值越小,优先级越高,执行顺序越靠前。
GlobalFilter通过实现Ordered接口,或者添加@Order注解来指定order值,由我们自己指定
路由过滤器和defaultFilter的order由Spring指定,默认是按照声明顺序从1递增。
当过滤器的order值一样时,会按照 defaultFilter > 路由过滤器 > GlobalFilter的顺序执行。