分布式架构设计模式
限流
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 分布式情况下的限流可以进行整体限流和局部限流的结合,根据具体情况进行选择。
1. 整体限流
整体限流是对整个应用程序进行限流控制,通常是根据应用程序的压力、负载以及所需的资源进行限流控制。整体限流可以通过多种方式来实现,比如使用熔断器、队列长度(MQ)控制,或者通过分布式限流工具来控制。
2. 局部限流
局部限流则是根据实际使用情况,对不同的服务、接口或者方法进行独立的限流控制。通常是针对不同的服务或者接口,根据实际的业务需求和资源使用情况,进行不同的限流策略和算法,以达到最佳的限流效果。
微服务、容器模式、服务栅格都可以使用合适的分布式限流工具来实现整体限流和局部限流的需求。
1. 对于微服务,可以使用Sentinel、Resilience4j等分布式限流、熔断工具来满足限流需求。这些工具都可以独立运行,也可以与SpringCloud等微服务框架或者服务注册中心进行集成,提供更方便的分布式限流控制。
2. 在容器环境中,可以使用Kubernetes提供的限流策略限流控制。Kubernetes提供了CPU、内存、网络等多种资源的限流策略,可根据不同的资源需求和限制条件,针对不同的容器进行限流。
3. 服务栅格提供了更多的限流控制策略和算法,包括服务端限流、客户端限流、基于令牌桶算法的限流等,可根据实际应用场景和需求进行选择。
综上所述,分布式情况下的限流可以进行整体限流和局部限流的结合,根据实际需求和场景选择合适的分布式限流工具来实现。Sentinel、Resilience4j等工具适用于微服务环境,Kubernetes适用于容器模式下,服务栅格提供了更多的限流控制策略和算法,可根据实际情况进行选择。
|
断路器和熔断器 – 容错
1 2 3 4 5 6 7 8 9 10 11
| 断路器模式和熔断器模式都是用于实现容错机制的设计模式,但它们的作用、用途和实现方式有所不同。
断路器模式是一种应对于外部服务调用的错误保护机制,在分布式系统中,往往多个微服务之间存在复杂的依赖关系,这些服务之间调用发生错误时,可能会产生级联错误的情况。为避免这种情况的发生,断路器模式通过在容器中放置断路器,当触发错误的次数达到一定数值后,自动切断访问,自身直接返回一个错误,并且会在触发断路器的过程中记录请求次数、错误次数等指标,在达到一定的时间后快速进行恢复。通过这种方式,可以保护整个系统不被错误请求拖垮。熔断器模式是一种限流保护机制,通过降级处理请求,如直接返回默认值、调用缓存值等方式来保护系统。当系统中出现请求量过载,资源紧张或交互故障等情况时,熔断器会宋体自动熔断,将请求转向熔断器自身,通过熔断器防止请求持续调用,降低应用层次出现异常的几率。
总的来说,断路器模式是一个容错机制,主要是为了保护整个系统不被错误请求拖垮;而熔断器模式则是一个限流保护机制,主要是为了保护服务本身不受过载而导致瘫痪。当服务出现问题时,断路器模式可以通过短路来解决问题;而熔断器模式则是通过限制服务流量来进行保护服务的目的同时,断路器模式和熔断器模式都可以通过实时监控请求情况、响应时间、错误率等指标来帮助监控服务质量,并判断是否需要进行熔断或者关闭断路。
在实际应用中,断路器和熔断器通常会结合起来使用,以提高应用程序的稳定性和可靠性。其中,熔断器可以用于限制请求访问流量,并抛弃不必要的请求,从而减轻应用程序的负载,保护应用程序免受过载的影响。如果服务器出现故障或错误,断路器可以快速切断服务访问,防止错误循环引起系统瘫痪,保护整个系统的稳定性。
断路器和熔断器的实现方式也有所不同。在实际应用中,通常使用熔断器框架(如Hystrix、Resilience4j等)来实现断路器和熔断器的功能,这些框架提供了响应式的API和管理控制插件,可为应用程序提供可靠的容错和限流保护机制,在支持高并发、分布式部署和云计算等方面具有重要意义。
综上所述,断路器模式和熔器模式都是容错机制,但它们的作用、用途和实现方式有所不同。断路器模式主要针对于外部服务调用的错误保护机制,通过实现对请求次数、错误次数的监控,快速切断访问,保护整个系统不被错误请求拖垮。熔断器模式则主要是限流保护机制,通过限制请求流量,保护服务本身不受过载而导致瘫痪。在实际应用中,断路器和熔断器通常结合使用,以提高应用程序的稳定性和可靠性。
|
示例代码
以下是基于Hystrix实现断路器和熔断器的示例代码:
- 断路器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @HystrixCommand(fallbackMethod = "fallbackMethod") @GetMapping("/message") public String getMessage(@RequestParam String name) { return restTemplate.getForObject("http://producer/hello?name=" + name, String.class); }
HystrixCommand.Setter commandSetter = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("group_key")) .andCommandKey(HystrixCommandKey.Factory.asKey("command_key")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("thread_pool_key")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withCircuitEnabled(true)) rixCommand helloServiceCommand = new HystrixCommand(commandSetter) { @Override protected String getFallback() { return "fallbackMethod"; }
@Override protected String run() throws Exception { return restTemplate.getForObject("http://producer/hello?name=" + name, String.class); } }; String result = helloServiceCommand.execute();
|
- 熔断器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| @HystrixCommand(fallbackMethod = "fallbackMethod",commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1000") }) @GetMapping("/message") public String getMessage(@RequestParam String name) { return restTemplate.getForObject("http://producer/hello?name=" + name, String.class); }
HystrixCommand.Setter commandSetter = HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("group_key")) .andCommandKey(HystrixCommandKey.Factory.asKey("command_key")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("thread_pool_key")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withCircuitBreakerEnabled(true) .withCircuitBreakerRequestVolumeThreshold(5) .withCircuitBreakerErrorThresholdPercentage(50) .withCircuitBreakerSleepWindowInMilliseconds(5000) ); HystrixCommand helloServiceCommand = new HystrixCommand(commandSetter) { @Override protected String getFallback() { return "fallbackMethod"; }
@Override protected String run() throws Exception { List<String> result = restTemplate.getForObject("http://producer/hello?q=" + query, List.class); return result.toString(); }
@Override protected String getCacheKey() { return "cache_key"; } };
try { helloServiceCommand.execute(); } catch (HystrixRuntimeException e) { log.error("Hystrix command error: {}", e.getMessage(), e); }
|
在断路器和熔断器的示例代码中,断路器使用 HystrixCommand
实现,熔断器使用 HystrixObservableCommand
实现。其中,断路器和熔断器在 fallbackMethod
方法中,通常是返回异常信息或者预先设定的备选结果。在实现过程中,我们可以通过各种属性的设置来控制断路器和熔断器的行为,以满足不同的需求。
选举算法
选举策略算法主要用于分布式系统中,处理节点失效问题,从而保障系统的高可用性。常见的选举策略算法有Bully算法、环算法、Paxos算法等,下面将逐一介绍这些算法的工作原理、优缺点。
Bully算法
分布式基础-谁来当老大(Bully算法) - 知乎 (zhihu.com)
Bully算法是一种基于选举的故障恢复机制,在分布式系统中,当某个节点失败时,其他节点可以通过选举机制,选出一个新的主节点来取代失效的节点。Bully算法的工作原理如下:
- 所有节点按照ID大小进行排序,ID最大的节点被选举为主节点。
- 如果当前主节点失效,各个节点开始进行选举,比当前节点ID值大的节点先启动选举过。
- 其他节点向ID比它大的节点发出选举请求。
- 如果没有节点响应,则该节点成为新主节点。
- 如果有节点响应,则节点中ID最大的节点成为新主节点。
Bully算法的优点在于:
- 发现故障节点并快速进行选举,可以保证分布式系统的高可用性。
- 算法简单易懂,易于实现。
Bully算法的缺点在于:
- 在选举过程中,可能存在多次SELECT的过程,造成效率低下,不适合大规模分布式系统。
- 需要维护节点ID的大小关系,增加了节点数量限制,不适合无限制扩展的系统。
环算法
环算法是一种不需要进行主节点选举的故障恢复机制,在分布式系统中,每个节点都拥有一个唯一的ID,在一个环上进行排列。环算法的工作原理如下:
- 当一个节点失效时,开始一个寻找下一个可用节点的过程。
- 当当前节点发现下一个节点失效后,继续向“成功节点”发送请求。
- 如果沿着环找到了一个节点,该节点可以被视为“成功节点”(假设该节点一直存活)。
- 新的节点将发送请求到“成功节点”,要求获取之前由断开连接的节点控制的所有资源。
环算法的优点在于:
- 算法简单,不需要进行主节点选举过程。
- 可以适用于较大的分布式系统,易于扩展。
环算法的缺点在于:
- 当节点在环形上相距较远时,消息转发的路径可能会过长,影响效率。
- 节点加入或退出时,需要重新构建整个环,影响系统效率。
Paxos算法
Paxos算法是一种经典的一致性算法,适用于分布式系统中的故障恢复、主节点选举等问题。Paxos算法包括议员选取阶段、提交提案阶段和批准提案阶段等三个阶段,工作原理如下:
- 提案者P向提案集传输一个包含值的提案。
- 如果协调者C没有收到提案,它就会指定P为提案者,并允许P提交它的提案。
- 如果一个协调者收到了提案,它可能会批准它或者阻止这个提案,由此决定是否向提案集传送提案。如果该提案已经被批准,它会开始第三阶段,否则它需要重新开始第二阶段。
- 如果协调者C批准了一个提案,那么该提案就会被提议参议员进行处理。
- 如果超过一半的参议员接受了提案,那么该提案就会被通过,否则该提案将无法通过。
- 如果另一个提案与当前提案冲突,则该提案会在协调者C的帮助下开始一个两两提案达成决定的过程。
Paxos算法的优点在于:
- 通过多个提案者提交提案的方式,避免了单点故障的发生。
- 可以保证系统的一致性,即使发生网络分区和其他异常情况。
Paxos算法的缺点在于:
- 算法较为复杂,实现困难。
- 执行效率较低,在处理大量的提案时容易造成系统负载过大。
综上所述,Bully算法、环算法和Paxos算法都是常用的选举策略算法。Bully算法和环算法都具有简单易懂、易于实现的优点,同时也存在效率低下、可扩展性差等缺点,不适合大规模分布式系统。Paxos算法则具有分布式系统一致性、可靠性等优势,但实现难度较,执行效率较低。因此,在实际应用中,需要根据不同的业务需求和系统规模来选择合适的选举策略算法。