springcloud概念:

SpringCloud,是一系列框架的有序集合,基于SpringBoot提供了一套微服务解决方案,有快速开发、持续交付和容易部署等特点。Spring Cloud 的组件非常多,涉及微服务的方方面面。包括服务注册与发现,配置中心,全链路监控,服务网关,负载均衡,熔断器等组件。

 

Eureka组件

作用:作为微服务的注册中心,提供服务的注册与发现

架构:

Register(服务注册):把自己的IP和端口注册给Eureka。
Renew(服务续约):发送心跳包,每30秒发送一次。告诉Eureka自己还活着。
Cancel(服务下线):当provider关闭时会向Eureka发送消息,把自己从服务列表中删除。防止consumer调用到不存在的服务。
Get Registry(获取服务注册列表):获取其他服务列表。
Replicate(集群中数据同步):eureka集群中的数据复制与同步。
Make Remote Call(远程调用):完成服务的远程调用。

Eureka包含两个组件:Eureka Server和Eureka Client。

  1.1 Eureka Server

  Eureka Server提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册,这样Eureka Server中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到。
  Eureka Server本身也是一个服务,默认情况下会自动注册到Eureka注册中心。
  如果搭建单机版的Eureka Server注册中心,则需要配置取消Eureka Server的自动注册逻辑。毕竟当前服务注册到当前服务代表的注册中心中是一个说不通的逻辑。
  Eureka Server通过Register、Get、Renew等接口提供服务的注册、发现和心跳检测等服务。

  2.1 Eureka Client

  Eureka Client是一个java客户端,用于简化与Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳,默认周期为30秒,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)
  Eureka Client分为两个角色,分别是:Application Service(Service Provider)和Application Client(Service Consumer)

  2.1.1 Application Service

  服务提供方,是注册到Eureka Server中的服务。

  2.1.2 Application Client

  服务消费方,通过Eureka Server发现服务,并消费。

  在这里,Application Service和Application Client不是绝对上的定义,因为Provider在提供服务的同时,也可以消费其他Provider提供的服务;Consumer在消费服务的同时,也可以提供对外服务。

Eureka的三个角色:

   1、Eureka Server:提供服务注册和发现

   2、Service Provider(服务提供方):将自身服务注册到Eureka,从而使服务消费方能够找到

   3、Service Consumer(服务消费方):从Eureka获取注册服务列表,从而能够消费服务

 

关于注册中心的解决方案,dubbo 支持了 Zookeeper、Redis、Multicast 和 Simple,官方推荐 Zookeeper。Spring Cloud 支持了 Zookeeper、Consul 和 Eureka,官方推荐 Eureka。

两者之所以推荐不同的实现方式,原因在于组件的特点以及适用场景不同。简单来说:

  • ZK 的设计原则是 CP,即强一致性和分区容错性。他保证数据的强一致性,但舍弃了可用性,如果出现网络问题可能会影响 ZK 的选举,导致 ZK 注册中心的不可用

  • Eureka 的设计原则是 AP,即可用性和分区容错性。他保证了注册中心的可用性,但舍弃了数据一致性,各节点上的数据有可能是不一致的(会最终一致)

  • Eureka 采用纯 Java 实现,除实现了注册中心基本的服务注册和发现之外,极大的满足注册中心的可用性,即使只有一台服务可用,也可以保证注册中心的可用性。

 

Feign组件

Feign是一个声明式的web服务客户端,实现远程调用的核心就是通过一系列的封装和处理,将以JAVA注解的方式定义的远程调用API接口,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,最终转换成HTTP的请求形式,

通过Feign以及JAVA的动态代理机制,使得Java 开发人员,可以不用通过HTTP框架去封装HTTP请求报文的方式,完成远程服务的HTTP调用。然后将HTTP的请求的响应结果,解码成JAVA Bean,放回给调用者。

Feign 可以与 Eureka 和 Ribbon 组合使用以支持负载均衡,与 Hystrix 组合使用,支持熔断回退。

用到的设计模式是:动态代理

在微服务启动时,Feign会进行包扫描,对加@FeignClient注解的接口,按照注解的规则,创建远程接口的本地JDK Proxy代理实例。然后,将这些本地Proxy代理实例,注入到Spring IOC容器中。当远程接口的方法被调用,由Proxy代理实例去完成真正的远程访问,并且返回结果。

开启熔断:feign.hystrix.enable=true

开启feign的功能:@EnableFeignClients

实现:定义一个接口,使用注解@FeignClient(value="服务名", fallback=实现类.class),方法上的注解使用的都是springMVC的注解

和使用http框架的区别:主要就是解耦,如果使用调用方的url或者接口信息修改了的话,使用http框架就需要修改代码去实现调用,而有了注册中心组件,就能实现解耦,不用修改本地代码

 

Ribbon组件

Ribbon是一个负载均衡的客户端框架,Ribbon利用从Eureka中读取到的服务提供者列表信息,在调用服务节点提供的服务时,基于内置的负载均衡算法,进行请求服务。Ribbon框架具备以下特点:

  • 负载均衡
  • 容错
  • 多协议(HTTP, TCP, UDP)支持异步和反应模型。
  • 缓存和批处理

使用方法:

在需要负载均衡的接口上加上@LoadBalanced ,在RestTemplate的@Bean方法上。

 

Hystrix组件

开源的一款容错框架,用来处理分布式系统中的容错需求,提供资源隔离(线程池隔离、信号量隔离)、熔断、降级回退功能,防止分布式系统出现级联故障,来保证分布式系统的正常运行。

Hystrix的工作流程

1、初始化,有两种方式初始化一个Hystrix命令,通过new HystrixCommand或者new HystrixObservableCommand创建,使用服务实例和请求服务需要的参数来构造一个Hystrix命令。

2、成功创建Hystrix后,有四种方法执行实际的命令并得到返回结果。这里Hystrix还使用了响应式编程来设计,这个主题比较大,一时半会解释不出,之后再深入探索。

对于使用HystrixCommand创建命令的实例,执行execute或者queue;而对于使用HystrixObservableCommand创建命令的实例,执行observe或者toObservable方法,可以请求服务然后得到执行结果。这四个方法的特性是:

  • execute - 会阻塞,然后返回依赖服务的结果

  • queue - 返回一个Future,然后可以通过get方法获得以来服务的结果。

  • observe - 订阅包含依赖服务响应结果的订阅器,当有结果时返回一个订阅器。

  • toObservable - 返回一个订阅器,当订阅它时,会知晓Hystrix命令并返回结果。



execute的源码如下:

public R execute() {

try {

return queue().get();

} catch (Exception e) {

throw Exceptions.sneakyThrow(decomposeException(e));

}

}

public Future<R> queue() {

/*

* The Future returned by Observable.toBlocking().toFuture() does not implement the

* interruption of the execution thread when the "mayInterrupt" flag of Future.cancel(boolean) is set to true;

* thus, to comply with the contract of Future, we must wrap around it.

*/

final Future<R> delegate = toObservable().toBlocking().toFuture();

// 其他定义

}

 

从源码看到,execute方法会调用queue().get()方法,queue()会调用toObservable().toBlocking().toFuture(),说明每一个Hystrix命令最终都回到Observable对象的实现,即使是为了返回一个简单的值。

3、判断Hystrix是否启用缓存且对应请求有缓存值,则返回缓存的结果。

4、如果3没有缓存,Hystrix会检查它的熔断器,如果此时熔断器开启了,那么Hystrix不会执行命令,直接返回降级结果。

5、如果信号或者线程池拒绝请求,返回降级结果。

6、Hystrix通过调用HystrixCommand.run()或者HystrixObservableCommand.construct()方法来触发调用外部服务的操作,如果超时或者失败,返回降级结果。

如果run或者construct方法超过了命令定义的超时值,线程会抛出TimeoutException,此时Hystrix捕捉到异常,就会忽略run或construct方法的返回值,进入fallback。

注意:没有任何方式可以阻止延迟的线程停止工作,在JVM中,Hystrix可以做到最好的就是抛出一个InterruptedException,如果Hystrix封装的服务没有捕获InterruptedException,Hystrix线程池中的线程会继续它的工作。

7、不管请求如何进行:成功、失败、超时、熔断,Hystrix都会上报健康状态到熔断器,记录服务状态,用于判断是否启动/半启动熔断器。

8、fallback,进行降级操作,会触发回退操作的条件:

  • construct或者run方法抛出异常

  • 熔断器开启

  • 线程池以及队列或者信号容量不足

  • Hystrix命令超时

对于每一个Hystrix命令,都需要覆盖getFallback方法,在fallback函数中实现降级的方案,如果需要在fallback中使用网络调用,那么需要通过另一个HystrixCommand或者HystrixObservableCommand。在HystrixCommand中是实现getFallback方法,在HystrixObservableCommand中,是实现sumeWithFallback方法。

如果没有实现fallback方法,或者fallback方法抛出了异常,Hystrix还是会返回一个Observerable,但是不会返回内容并通过一个onError通知来马上终止。通过onError通知,发生异常的会被返回Hystrix的调用者。尽量不要写出可能会抛出异常fallback实现。

9、如果一切正常,那么Hystrix会发送成功的结果到Observable,程序再去获取。

 

 

Hystrix提供了以下几种容错机制:

1.资源隔离

Hystrix提供了两种线程隔离方式:线程池和信号量。

①线程池隔离(为每一个依赖项维护一个线程池,这样就算某个依赖服务出现延迟过高的情况,也只是对该依赖服务的调用产生影响,而不会拖慢其他的依赖服务。如果依赖项的线程池满了,新的依赖请求不会继续排队等待,而是马上被拒绝访问)

Hystrix通过命令模式对发送请求的对象和执行请求的对象进行解耦,将不同类型的业务请求封装为对应的命令请求。如订单服务查询商品,查询商品请求->商品Command;商品服务查询库存,查询库存请求->库存Command。并且为每个类型的Command配置一个线程池,当第一次创建Command时,根据配置创建一个线程池,并放入ConcurrentHashMap,如商品Command:

final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>();
threadPools.put(“hystrix-order”, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));

通过将发送请求线程与执行请求的线程分离,可有效防止发生级联故障。当线程池或请求队列饱和时,Hystrix将拒绝服务,使得请求线程可以快速失败,从而避免依赖问题扩散。

线程池隔离的缺点:

线程池的主要缺点是增加了计算开销。每个命令的执行都在单独的线程完成,增加了排队、调度和上下文切换的开销。因此,要使用Hystrix,就必须接受它带来的开销,以换取它所提供的好处。

通常情况下,线程池引入的开销足够小,不会有重大的成本或性能影响。但对于一些访问延迟极低的服务,如只依赖内存缓存,线程池引入的开销就比较明显了,这时候使用线程池隔离技术就不适合了,我们需要考虑更轻量级的方式,如信号量隔离。

 

②信号量隔离

通过设置信号量来限制对任何给定依赖的并发调用量。限制了总的并发数,每一次请求过来,请求线程和调用依赖服务的线程是同一个线程,那么如果不涉及远程RPC调用(没有网络开销)则使用信号量来隔离,更为轻量,开销更小。

由于Hystrix默认使用线程池做线程隔离,使用信号量隔离需要显示地将属性execution.isolation.strategy设置为ExecutionIsolationStrategy.SEMAPHORE,同时配置信号量个数,默认为10。客户端需向依赖服务发起请求时,首先要获取一个信号量才能真正发起调用,由于信号量的数量有限,当并发请求量超过信号量个数时,后续的请求都会直接拒绝,进入fallback流程。

下面这张图说明了线程池隔离和信号量隔离的主要区别:线程池方式下业务请求线程和执行依赖的服务的线程不是同一个线程;信号量方式下业务请求线程和执行依赖服务的线程是同一个线程。

 

2.熔断器

HystrixCircuitBreaker 有三种状态 :

CLOSED :关闭

OPEN :打开

HALF_OPEN :半开

熔断器开关由关闭到打开的状态转换是通过当前服务健康状况和设定阈值比较决定的.

  1. 当熔断器开关关闭时, 请求被允许通过熔断器. 如果当前健康状况高于设定阈值, 开关继续保持关闭. 如果当前健康状况低于设定阈值, 开关则切换为打开状态.

  2. 当熔断器开关打开时, 请求被禁止通过.

  3. 当熔断器开关处于打开状态, 经过一段时间后, 熔断器会自动进入半开状态, 这时熔断器只允许一个请求通过. 当该请求调用成功时, 熔断器恢复到关闭状态. 若该请求失败, 熔断器继续保持打开状态, 接下来的请求被禁止通过.

熔断器的开关能保证服务调用者在调用异常服务时, 快速返回结果, 避免大量的同步等待. 并且熔断器能在一段时间后继续侦测请求执行结果, 提供恢复服务调用的可能.

 

3.回退降级

降级,通常指务高峰期,为了保证核心服务正常运行,需要停掉一些不太重要的业务,或者某些服务不可用时,执行备用逻辑从故障服务中快速失败或快速返回,以保障主体业务不受影响。Hystrix提供的降级主要是为了容错,保证当前服务不受依赖服务故障的影响,从而提高服务的健壮性。要支持回退或降级处理,可以重写HystrixCommand的getFallBack方法或HystrixObservableCommand的resumeWithFallback方法。

Hystrix一共有如下几种降级回退模式:

  1. Fail Fast 快速失败
  2. Fail Silent 无声失败:返回null,空Map,空List
  3. Fallback: Static 返回默认值:回退的时候返回静态嵌入代码中的默认值,这样就不会导致功能以Fail Silent的方式被清楚,也就是用户看不到任何功能了。而是按照一个默认的方式显示。
  4. Fallback: Stubbed 自己组装一个值返回
  5. Fallback: Cache via Network 利用远程缓存

https://www.jianshu.com/p/3e11ac385c73

https://www.cnblogs.com/crazymakercircle/p/11965726.html

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐