Skip to content

八、Spring Cloud

1、使用 Spring Cloud 有什么优势?

使用 Spring Boot 开发分布式微服务时,我们面临以下问题

  • 与分布式系统相关的复杂性-这种开销包括网络问题,延迟开销,带宽问题,安全问题。

  • 服务发现-服务发现工具管理群集中的流程和服务如何查找和互相交谈。它涉及一个服务目录,在该目录中注册服务,然后能够查找并连接到该目录中的服务。

  • 冗余-分布式系统中的冗余问题。

  • 负载平衡 --负载平衡改善跨多个计算资源的工作负荷,诸如计算机,计算机集群,网络链路,中央处理单元,或磁盘驱动器的分布。

  • 性能-问题 由于各种运营开销导致的性能问题。

  • 部署复杂性-Devops 技能的要求。

2、什么是 Hystrix?它如何实现容错?

Hystrix是Netflix开源的一个用于实现容错的库,它主要用于处理分布式系统中的延迟和故障。Hystrix可以帮助开发人员实现服务的容错和降级,从而提高系统的可用性和稳定性。

Hystrix的容错机制主要包括以下几个方面:

  1. 服务降级

当服务不可用或响应时间超过阈值时,Hystrix会自动将请求转发到服务的备用实现或缓存中,从而避免服务的故障影响到整个系统的性能和可用性。

  1. 服务熔断

Hystrix可以根据一定的规则来监控服务的健康状态,当服务出现故障或响应时间超过阈值时,Hystrix会自动断开服务的调用,避免服务的故障对整个系统的性能和可用性造成影响。

  1. 服务限流

Hystrix可以根据一定的规则来限制服务的请求流量,当服务的请求流量超过一定阈值时,Hystrix会自动拒绝部分请求或延迟部分请求的执行,从而保护服务的可用性和稳定性。

  1. 监控和报警

Hystrix可以实时监控服务的健康状态和性能指标,包括响应时间、失败率、请求量等,同时可以通过报警机制来及时发现和处理服务的异常情况,从而保障系统的可用性和稳定性。

总之,Hystrix是一个用于实现容错的库,它通过服务降级、服务熔断、服务限流和监控报警等机制来保障系统的可用性和稳定性。在分布式系统的开发中,Hystrix是一个非常重要的工具,可以帮助开发人员实现高可用、高性能的分布式系统。

3、Feign是什么?

Feign集成了Ribbon、RestTemplate实现了负载均衡的执行Http调用,只不过对原有的方式(Ribbon+RestTemplate)进行了封装,开发者不必手动使用RestTemplate调服务,而是定义一个接口,在这个接口中标注一个注解即可完成服务调用,这样更加符合面向接口编程的宗旨,简化了开发。

4、openFeign是什么?

OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

5、Feign和openFeign有什么区别?

FeignopenFiegn
Feign是SpringCloud组件中一个轻量级RESTful的HTTP服务客户端,Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务OpenFeign 是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等。OpenFeign 的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

6、openFeign如何传参?

1、传递JSON数据

这个也是接口开发中常用的传参规则,在Spring Boot 中通过@RequestBody标识入参。

java
/**
     * 参数默认是@RequestBody标注的,这里的@RequestBody可以不填
     * 方法名称任意
     */
@PostMapping("/openfeign/provider/order2")
Order createOrder2(@RequestBody Order order);

注意:openFeign默认的传参方式就是JSON传参(@RequestBody),因此定义接口的时候可以不用@RequestBody注解标注,不过为了规范,一般都填上。

2、POJO表单传参

java
/**
     * 参数默认是@RequestBody标注的,如果通过POJO表单传参的,使用@SpringQueryMap标注
     */
@PostMapping("/openfeign/provider/order1")
Order createOrder1(@SpringQueryMap Order order);

图片

openFeign提供了一个注解@SpringQueryMap完美解决POJO表单传参。

3、URL中携带参数

java
@GetMapping("/openfeign/provider/test/{id}")
String get(@PathVariable("id")Integer id);

使用注解@PathVariable接收url中的占位符,这种方式很好理解。

4、另类传参

java
/**
     * 必须要@RequestParam注解标注,且value属性必须填上参数名
     * 方法参数名可以任意,但是@RequestParam注解中的value属性必须和provider中的参数名相同
     */
@PostMapping("/openfeign/provider/test2")
String test(@RequestParam("id") String arg1,@RequestParam("name") String arg2);

7、超时如何处理?

java
@PostMapping("/test2")
public String test2(String id,String name) throws InterruptedException {
        // 休眠3秒
    Thread.sleep(3000);
        return MessageFormat.format("accept on msg id={0},name={1}",id,name);
}

openFeign其实是有默认的超时时间的,默认分别是连接超时时间10秒、读超时时间60秒,源码在feign.Request.Options#Options()这个方法中,如下图:

图片

那么问题来了:为什么我只设置了睡眠3秒就报超时呢?

其实openFeign集成了Ribbon,Ribbon的默认超时连接时间、读超时时间都是是1秒,源码在org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute()方法中,如下图:

图片

源码大致意思:如果openFeign没有设置对应得超时时间,那么将会采用Ribbon的默认超时时间。

理解了超时设置的原理,由之产生两种方案也是很明了了,如下:

  • 设置openFeign的超时时间
  • 设置Ribbon的超时时间

1、设置Ribbon的超时时间(不推荐)

设置很简单,在配置文件中添加如下设置:

yaml
ribbon:
  # 值的是建立链接所用的时间,适用于网络状况正常的情况下, 两端链接所用的时间
  ReadTimeout: 5000
  # 指的是建立链接后从服务器读取可用资源所用的时间
  ConectTimeout: 5000

2、设置openFeign的超时时间(推荐)

openFeign设置超时时间非常简单,只需要在配置文件中配置,如下:

yaml
feign:
  client:
    config:
      ## default 设置的全局超时时间,指定服务名称可以设置单个服务的超时时间
      default:
        connectTimeout: 5000
        readTimeout: 5000

default设置的是全局超时时间,对所有的openFeign接口服务都生效

8、如何开启日志增强?

openFeign虽然提供了日志增强功能,但是默认是不显示任何日志的,不过开发者在调试阶段可以自己配置日志的级别。

openFeign的日志级别如下:

  • NONE:默认的,不显示任何日志;
  • BASIC:仅记录请求方法、URL、响应状态码及执行时间;
  • HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;
  • FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

1、配置类中配置日志级别

图片

注意:这里的logger是feign包里的。

2、yaml文件中设置接口日志级别

只需要在配置文件中调整指定包或者openFeign的接口日志级别,如下:

yml
logging:
  level:
    cn.xx.service: debug

这里的cn.xx.service是openFeign接口所在的包名,当然你也可以配置一个特定的openFeign接口。

上述步骤将日志设置成了FULL,此时发出请求,日志效果如下图:

图片

日志中详细的打印出了请求头、请求体的内容。

9、如何替换默认的httpclient?

Feign在默认情况下使用的是JDK原生的URLConnection发送HTTP请求,没有连接池,但是对每个地址会保持一个长连接,即利用HTTP的persistence connection。

在生产环境中,通常不使用默认的http client,通常有如下两种选择:

  • 使用ApacheHttpClient
  • 使用OkHttp

至于哪个更好,其实各有千秋,我比较倾向于ApacheHttpClient,毕竟老牌子了,稳定性不在话下。

那么如何替换掉呢?其实很简单,下面演示使用ApacheHttpClient替换。

1、添加ApacheHttpClient依赖

在openFeign接口服务的pom文件添加如下依赖:

xml
<!--     使用Apache HttpClient替换Feign原生httpclient-->
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
    </dependency>
    
    <dependency>
      <groupId>io.github.openfeign</groupId>
      <artifactId>feign-httpclient</artifactId>
    </dependency>

为什么要添加上面的依赖呢?从源码中不难看出,请看org.springframework.cloud.openfeign.FeignAutoConfiguration.HttpClientFeignConfiguration这个类,代码如下:

图片

上述红色框中的生成条件,其中的@ConditionalOnClass(ApacheHttpClient.class),必须要有ApacheHttpClient这个类才会生效,并且feign.httpclient.enabled这个配置要设置为true

2、配置文件中开启

在配置文件中要配置开启,代码如下:

yaml
feign:
  client:
    httpclient:
      # 开启 Http Client
      enabled: true

3、如何验证已经替换成功?

其实很简单,在feign.SynchronousMethodHandler#executeAndDecode()这个方法中可以清楚的看出调用哪个client,如下图:

图片

上图中可以看到最终调用的是ApacheHttpClient

4、总结

上述步骤仅仅演示一种替换方案,剩下的一种不再演示了,原理相同。

10、如何通讯优化?

在讲如何优化之前先来看一下GZIP 压缩算法,概念如下:

gzip是一种数据格式,采用用deflate算法压缩数据;gzip是一种流行的数据压缩算法,应用十分广泛,尤其是在Linux平台。

当GZIP压缩到一个纯文本数据时,效果是非常明显的,大约可以减少70%以上的数据大小。

网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是GZIP与搜索引擎的抓取工具有着更好的关系。例如 Google就可以通过直接读取GZIP文件来比普通手工抓取更快地检索网页。

GZIP压缩传输的原理如下图:

图片

按照上图拆解出的步骤如下:

  • 客户端向服务器请求头中带有:Accept-Encoding:gzip,deflate 字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。
  • 服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式压缩过的。
  • 客户端接收到响应之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。

openFeign支持请求/响应开启GZIP压缩,整体的流程如下图:

图片

上图中涉及到GZIP传输的只有两块,分别是Application client -> Application ServiceApplication Service->Application client

注意:openFeign支持的GZIP仅仅是在openFeign接口的请求和响应,即是openFeign消费者调用服务提供者的接口。

openFeign开启GZIP步骤也是很简单,只需要在配置文件中开启如下配置:

feign:
  ## 开启压缩
  compression:
    request:
      enabled: true
      ## 开启压缩的阈值,单位字节,默认2048,即是2k,这里为了演示效果设置成10字节
      min-request-size: 10
      mime-types: text/xml,application/xml,application/json
    response:
      enabled: true

上述配置完成之后,发出请求,可以清楚看到请求头中已经携带了GZIP压缩,如下图:

图片

11、如何熔断降级

常见的熔断降级框架有HystrixSentinel,openFeign默认支持的就是Hystrix,这个在官方文档上就有体现,毕竟是一奶同胞嘛,哈哈...........

但是阿里的Sentinel无论是功能特性、简单易上手等各方面都完全秒杀Hystrix,因此此章节就使用openFeign+Sentinel进行整合实现服务降级。

1、添加Sentinel依赖

openFeign-consumer9006消费者的pom文件添加sentinel依赖(由于使用了聚合模块,不指定版本号),如下:

xml
<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2、配置文件中开启sentinel熔断降级

要想openFeign使用sentinel的降级功能,还需要在配置文件中开启,添加如下配置:

yaml
feign:
  sentinel:
    enabled: true

3、添加降级回调类

这个类一定要和openFeign接口实现同一个类,如下图:

图片

OpenFeignFallbackService这个是降级回调的类,一旦OpenFeignService中对应得接口出现了异常则会调用这个类中对应得方法进行降级处理。

4、添加fallback属性

@FeignClient中添加fallback属性,属性值是降级回调的类,如下:

@FeignClient(value = "openFeign-provider",fallback = OpenFeignFallbackService.class)
public interface OpenFeignService {}

5、演示

经过如上4个步骤,openFeign的熔断降级已经设置完成了,此时演示下效果。

通过postman调用http://localhost:9006/openfeign/order3这个接口,正常逻辑返回如下图:

图片

现在手动造个异常,在服务提供的接口中抛出异常,如下图:

图片

此时重新调用http://localhost:9006/openfeign/order3,返回如下图:

图片

12、Sentinel如何做分布式限流

Sentinel 是一种分布式系统的流量控制组件,可以实现流量控制、服务降级、熔断降级等功能。在 Sentinel 中,可以通过配置流控规则来控制每个服务的请求流量,防止服务被过度请求而导致服务宕机或者响应变慢。

Sentinel 中的分布式限流是通过令牌桶算法和滑动窗口算法实现的。具体来说,Sentinel 会在每个服务的入口处配置流控规则,根据规则来限制每个服务的请求流量。当某个服务的请求量超过流控规则中设置的阈值时,Sentinel 会根据算法来拒绝或者延迟请求,从而达到限流的效果。

在令牌桶算法中,Sentinel 会为每个服务配置一个令牌桶,令牌桶中存储了一定数量的令牌,每次请求时需要从令牌桶中获取令牌,如果令牌不足则请求被拒绝或者延迟。在滑动窗口算法中,Sentinel 会将每个服务的请求量按照时间窗口进行统计,如果请求量超过了设置的阈值,请求会被拒绝或者延迟。

总的来说,Sentinel 的分布式限流是通过令牌桶算法和滑动窗口算法实现的,可以根据业务需求进行配置和调整,从而实现对服务的流量控制和限制。

13、Sentinel限流策略

Sentinel 提供了多种限流策略,可以根据实际情况选择合适的限流策略。以下是 Sentinel 支持的一些限流策略:

  1. 直接拒绝:当请求超出阈值时,直接拒绝请求,返回错误信息。
  2. 预热/冷启动:在系统启动时,逐渐增加流量,以避免系统在刚启动时被大量请求压垮。
  3. 排队等待:当请求超出阈值时,将请求放入队列中等待处理,避免请求被拒绝后直接影响用户体验。
  4. 慢启动:在系统启动时,逐渐增加流量,避免系统在刚启动时被大量请求压垮。
  5. 平滑降级:当系统负载过高时,逐渐降低系统的处理能力,避免系统崩溃。
  6. 热点参数限流:根据请求参数进行限流,避免热点请求对系统造成过大的压力。
  7. 熔断降级:当系统负载过高或者出现异常时,将请求拦截或者延迟处理,避免系统崩溃或者响应时间过长。

以上是 Sentinel 支持的一些限流策略,根据业务需求和实际情况选择合适的策略进行配置和应用。在配置限流规则时,需要综合考虑多个方面,例如系统负载、业务需求、用户体验等因素,从而达到合理的限流效果。

14、Sentinel线程池隔离和信号量隔离

Sentinel 提供了两种隔离策略,分别是线程池隔离和信号量隔离。

线程池隔离是将每个被保护的资源分配到一个线程池中进行处理,每个资源都有自己的线程池,不同资源之间相互独立。在线程池隔离的模式下,当一个资源被限流或者熔断时,只会影响当前资源的请求,不会影响到其他资源的请求。线程池隔离的优点是隔离性好,可以有效地限制每个资源的请求量,避免资源互相影响,但是线程池的创建和销毁会占用系统资源。

信号量隔离是将每个被保护的资源分配到一个信号量中进行处理,每个资源都有自己的信号量,不同资源之间相互独立。在信号量隔离的模式下,当一个资源被限流或者熔断时,会影响到同一信号量下的所有资源的请求。信号量隔离的优点是资源消耗少,但是隔离性相对较差,容易造成资源互相影响。

在选择线程池隔离和信号量隔离时,需要根据实际情况进行选择。如果需要更好的隔离性和更精细的流量控制,可以选择线程池隔离;如果需要更高的性能和更少的资源消耗,可以选择信号量隔离。另外,在配置隔离策略时,需要根据业务需求和实际情况选择合适的配置参数,例如线程池大小、并发度等,以达到更好的效果。

15、Nacos是怎么实现配置动态更新的

Nacos 通过监听配置数据变更的方式实现配置动态更新。具体来说,当 Nacos 中的配置数据发生变化时,Nacos 会通过监听器机制将变化的信息通知给客户端,客户端在接收到通知后会重新获取最新的配置数据,并更新本地的配置信息。

Nacos 的配置监听机制包括两个部分:客户端监听和服务端监听。客户端监听是指客户端通过订阅 Nacos 的配置服务,当配置数据发生变化时,Nacos 会主动通知客户端,客户端在接收到通知后会重新获取最新的配置数据。服务端监听是指 Nacos 在配置数据发生变化时,会通知所有订阅该配置的客户端,以便客户端能够及时更新配置信息。

Nacos 中的配置监听机制是基于长连接实现的,客户端和服务端之间通过长连接来实现实时通信。当客户端与 Nacos 建立长连接后,Nacos 会将客户端所订阅的配置信息发送给客户端,并保持连接状态。当配置数据发生变化时,Nacos 会主动向客户端发送通知,客户端在接收到通知后会重新获取最新的配置数据,并更新本地的配置信息。

总之,Nacos 通过监听配置数据变化的方式实现配置动态更新,利用长连接实现实时通信,保证配置信息的及时更新和同步。这种机制能够满足配置实时更新的需求,提高了系统的可靠性和可维护性。