微服务的远程调用

1 Spring Cloud OpenFeign

1.1 远程调用需求出现的原因

在使用微服务时,服务器之间要想互相调用,需要通过注册中心,但是注册中心最重要的作用是注册和管理服务器,虽然提供了方法实现服务器互相调用,也自动实现了负载均衡,但是实现过程要写的代码太繁琐了

如图通过注册中心提供的方法实现服务器之间互相调用,在实际开发中不希望手写那么多调用代码,所以就出现了远程调用需求

微服务的远程调用

假如order模块需要调用user模块,只需要通过openFeign就能找到,而openFeign通过连接注册中心实现查找服务器,需要调用的模块存在,再去调用服务

微服务的远程调用

1.2 Spring Cloud OpenFeign 是什么

Spring Cloud OpenFeign 基于Netflix Feign 实现的,并且实现了声明式的Web服务客户端定义方式,它使得web服务客户端更容易编写。

为了更方便的与Spring 组件集成,Spring Cloud还为OpenFeign提供了SpringMVC的支持,所以client接口可以直接把被调用模块的controller方法拿过来。

OpenFeign配合Eureka和Ribbon可以很方便的实现客户端负载均衡。所谓客户端负载均衡,就是OpenFeign在拉取某个业务模块目前可用的tomcat服务器后,会通过随机、轮询、均衡请求数量等机制对请求进行分流。(不过新版springboot中的OpenFeign似乎修改了负载均衡实现方式,已经没有ribbon了,而负载均衡策略只有“轮询”这一种方式)

微服务的远程调用

1.3 环境要求

作为远程调用的分布式组件,由于Spring Cloud OpenFeign是基于Netflix Feign实现的,所以它也与Spring Cloud Eureka一样都属于Netflix套件,所以
版本要求与Spring Cloud Eureka一样

1.4 使用方法

1.4.1 官方说明

根据官方文档,只需要为springboot启动类和调用别的模块的方法的接口添加注解即可使用,相较于注册中心使用HttpClient(现在是RestTemplate)的调用方式,OpenFeign更简单明了,无论是配置还是使用起来都很方便。

微服务的远程调用

1.4.2 引入pom依赖

首先引入pom依赖

微服务的远程调用

微服务的远程调用

1.4.3 启动类上使用注解

然后在springboot启动类上添加注解

微服务的远程调用

1.4.4 接口上使用注解

根据需求在order模块下创建XXXClient接口,例如order模块要调用user模块,此时就要创建一个UserClient接口,并把User模块的controller接口放入,最后使用@FeignClient注解

微服务的远程调用

User模块中,需要放入UserClient接口的方法名和请求路径

微服务的远程调用

最终UserClient接口代码如下,Client接口中的方法除了请求路径和请求方式需要与被调用的接口一致以外,方法名和参数名并不是必须得一致。

微服务的远程调用

@FeignClient注解中的属性值不写属性名时默认为name属性,这个name属性就是需要调用的系统(user模块)的spring.application.name 属性的值(user模块注册到Eureka注册中心的名字)

微服务的远程调用

1.5 公共模块

1.5.1 存在的问题

在上面的使用案例中,UserClient放置在order模块中,这就存在一个问题,如果UserClient中的方法返回的是一个user模块中的entity而不是现在这种object,由于order模块中不存在user里的entity,就会报错

假如把user模块中的entity复制到order模块中,那么当User模块的负责人修改entity,还得通知其他调用这个UserClient的人注意修改

所以最佳做法是把UserClient提取到一个公共模块commons中

1.5.2 创建公共模块

创建一个commons模块,pom依赖中只需要声明一个打包方式以及父模块,其他依赖都不需要,当然,如果真的有需求,比如junit,那就选择性留一些依赖

微服务的远程调用

1.5.3 解决方法

现在可以把所有Client类以及需要的eneity都复制到公共模块中

微服务的远程调用

接着让所有子模块在pom文件中引用这个公共模块,依赖要给出公共模块的组名、模块名、版本号

微服务的远程调用

1.5.4 父模块不能引用这个公共模块

千万不要在父模块的pom文件中引用公共模块,因为公共模块已经挂在了父模块下,如果父模块又引用公共模块,就会导致整个项目在打包时陷入死循环

1.5.5 公共模块可以放置其他东西

公共模块除了放置远程调用组件需要用到的client接口、client接口依赖的entity,也可以放一些公共配置,比如拦截器

保证每个业务模块尽量只放自己模块的controller、service、mapper、entity、VO

1.6 解决项目打包报错

报错

微服务的远程调用

说明

控制台提示打包错误,其实是因为刚才创建的公共模块commons不属于一个springboot项目,它只是起到一个工具类的作用

微服务的远程调用

而父模块的pom中设定了打包方式,那么一旦项目打包,父模块就会基于pom设定的打包方式为所有子模块打包,这就会导致打包commons出错,只需要把父模块中的这段打包依赖删掉即可

微服务的远程调用

此时虽然父模块中的打包方式被删除了,但是各个子模块的pom文件中依然存在打包方式,运行整个项目时,这些子模块就会打包,而公共模块Commons不受影响,这就解决了打包报错的问题

1.7 解决子模块的启动类扫描不到公共模块的问题

1.7.1 @SpringBootApplication问题

报错

微服务的远程调用

说明

如图是springboot启动类的默认写法,该写法会把当前启动类的package作为路径,此时@SpringBootApplication只能扫描自身所在层级以及子级的文件,自然没办法扫描到另一个模块下的UserClient接口

微服务的远程调用

再举一个例子

假如现在有一个类在启动类上层,如图

微服务的远程调用

此时TestController如图

微服务的远程调用

前端访问时就会报错,因为user模块的springboot启动类扫不到这个TestController

微服务的远程调用

所以需要通过设置scanBasePackages告诉springboot启动类,要从哪开始扫描,通过属性名可以知道,这是一个数组,可以写入多个包路径

微服务的远程调用

1.7.2 @EnableFeignClients问题

报错

微服务的远程调用

说明

@EnableFeignClients和@springbootapplication一样,默认写法下只能扫描自己所在层级和所有子级的@FeignClient注解,无法扫描到其他模块中的@FeignClient注解,因此提示UserClient没有找到

微服务的远程调用

解决办法

在需要使用远程调用的order模块的springboot启动类上,给@EnableFeignClients设置要扫描的路径

微服务的远程调用

2 spring Cloud Ribbon

2.1 介绍

Ribbon是一个客户端负载均衡器,在Feign中实际上也默认使用了Ribbon来做负载,且该功能是默认启用的。

客户端负载实际上跟Nginx类似,都是将访问请求转发到其他服务器。它通过Eureka注册中心的Application名称来获取服务器列表,然后在调用时通过负载策略来访问具体的服务器地址。

2.2 环境配置说明

在springcloud项目中切换springboot版本

OpenFeign默认集成Ribbon,如果使用的是旧版springboot,那么无需做什么配置,可以直接使用

在新版springboot中,集成的OpenFeign已经没有Ribbon了,估计是修改了实现负载均衡的方法

下面的知识点都依照旧版springboot中OpenFeign继承了ribbon来说明

2.3 修改ribbon的负载策略

ribbon默认开启轮询策略,也可以手动设置为其他负载策略

策略类名 策略描述
AvailabilityFilteringRule 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端Server,并过滤掉那些高并发的后端Server(active connections 超过配置的阈值)
一句话:把请求转到ribbon认为比较空闲的服务器上
BestAvailableRule 选择一个最小的并发请求的Server
RandomRule 随机选择一个Server
RetryRule 重试查找选择一个可用的Server
RoundRobinRule 轮询选择Server(默认策略)
WeightedResponseTimeRule 根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。(也就是根据服务器性能分配请求)
ZoneAvoidanceRule 复合判断。判断区域是否可用,并过滤掉区域中的连接过多的Server

配置负载策略也比较简单,只需要在调用方(order模块)的yml中声明被调用方在注册中心的名字以及负载策略的全类名,负载策略是按照服务名单独来进行配置的

user-system:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

微服务的远程调用

3 spring Cloud Hystrix

3.1 介绍

Netflix提供了一个叫Hystrix的类库,它实现了断路器模式,也就是提供了服务熔断机制。

在微服务架构中,通常一个微服务会调用多个其他的微服务。当某一个环节的微服务调用失败后,它将会导致上一层服务失败,服务访问越大则失败率越高,而这一连串的失败就是雪崩效益,下层失败导致上层所有服务崩溃,因此需要一个服务熔断机制,避免越错越多。

Hystrix的作用就是,当发现某一服务调用失败后,告知调用者该接口失败,从而避免调用者服务资源消耗。

而这样的处理就是我们经常会在微博以及淘宝上经常看到的繁忙页面或相关提示,这实际上就是发生了服务熔断,过几分钟后就好了。

实际可见:

微服务的远程调用

微服务的远程调用

微服务的远程调用

3.2 原理图

当访问量超过设定的阈值时,断路器起作用,Hystrix将执行fallback方法来返回给api调用,也就是让接口对所有请求返回失败,不再处理这些请求,给后端运维时间处理宕机的服务器。

微服务的远程调用

3.3 环境要求

与其他netflix组件版本一致

3.4 单独使用Hystrix

3.4.1 引入pom依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

3.4.2 启动类使用注解

在各个子模块的springboot启动类上使用@EnableCircuitBreaker

微服务的远程调用

如果本来就要使用@EnableEurekaClient @EnableFeignClients 和@EnableCircuitBreaker 那么可以使用一个注解替代这三个注解——@SpringCloudApplication

微服务的远程调用

进入这个注解,可以看到它是哪些注解的延伸注解,但是可以看到,它并不能直接配置这三个延伸注解的scanBasePackages和basePackages,所以我们需要自定义一个类似@SpringCloudApplication的注解,实现合并注解+配置注解

微服务的远程调用

3.4.3 使用Hystrix

在服务调用方的controller层的接口上使用@HystrixCommand注解标记在需要熔断的方法上,使用fallbackMethod声明当接口熔断时需要调用的方法A。

@HystrixCommand(fallbackMethod="发生熔断时执行的controller方法")

方法A可以返回一个默认提示文案或者默认对象,且方法A的入参参数列表必须与使用了@HystrixCommand的接口方法的参数列表保持一致。

微服务的远程调用

3.4.4 局部配置

3.4.4.1 说明

通过设置配置项,可以修改发生服务熔断时熔断多久自动恢复

3.4.4.2 常用参数说明

Hystrinx的熔断由三个参数的设置共同完成

属性 说明
circuitBreaker.requestVolumeThreshold 默认值20,表示10秒内20个请求为一个轮回
circuitBreaker.errorThresholdPercentage 默认值是50,表示50%的错误率
circuitBreaker.sleepWindowInMilliseconds 默认值是5000,表示熔断后,拒绝所有请求5秒

3.4.4.3 配置方式

  • Hystrix的配置信息可以通过@HystrixCommand的commandProperties属性来进行配置。

  • commandProperties是配置Hystrix配置的属性,该属性的值为@HystrixProperty类型的数组。

  • 在@HystrixProperty中配置属性名和属性值即可完成属性配置

  • Hystrix在熔断期间会对服务进行一次请求测试,如果服务能够正常访问,则会重新关闭熔断

下图的配置代表10秒内20个请求中错误率达到百分之60以上,就会触发服务熔断,熔断后拒绝请求50秒

微服务的远程调用

3.4.4.4 其他可配置属性一览

更多可配属性在HystrixCommandProperties类中查看。

超时配置
Hystrix默认超时时间是1秒钟,当远程服务调用超过1秒则会判定该远程服务调用失败。

微服务的远程调用

execution.isolation.thread.timeoutInMilliseconds:连接超时时间,单位毫秒,默认为1秒。

3.4.5 类全局配置

按照上面的配置方式,若一个类中存在多个接口需要进行熔断配置,则需要在每一个方法上加入注解和属性配置,这样非常麻烦。

Hystrix也提供了类全局配置来方便开发者进行参数设置,使用@DefaultProperties即可完成全局声明,可以配置全局服务熔断的参数以及触发服务熔断时被调用的方法

方法

微服务的远程调用

发生服务熔断时被调用的方法

微服务的远程调用

注意点:

    1. @DefaultProperties 和 @HystrixCommand可以共存,后者的优先级大于前者。

    2. defaultFallback属性指定的发生服务熔断时被调用的方法的参数列表必须为空

3.5 Openfeign集成Hystrix

3.5.1 开启Openfeign集成的Hystrix

Openfeign组件中默认集成了Hystrix,只需要在配置文件中开启即可使用Hystrix的功能。

feign:
  #开启feign的hystrix
  hystrix:
    enabled: true

3.5.2 创建fallback类

在公共模块Commons中创建一个fallback类,用于设置发生服务熔断时被调用的方法,这个类放置位置如图

微服务的远程调用

具体代码如下

微服务的远程调用

说明:

fallback类通过实现Client接口,完成与client接口的绑定,这样一旦client接口中的方法出现问题,就调用fallback类中的方法,由于接口中的方法返回的是对象,因此这里只能返回null

3.5.3 使用Hystrix

随后直接在@FeignClient上使用fallback属性指定fallback类

微服务的远程调用

3.5.4 yml配置

直接在yml中对Hystrix进行属性配置,实现全局配置,在yml中的配置实际上与@HystrixCommand相差无几,只是在配置属性前面多了hystrix.command.default

其中default代表默认配置,可以更改为具体某个@FeignClient的name,为每一个feign单独配置

//配置超时配置
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 4000
//配置错误百分比
hystrix:
  command:
    default:
      circuitBreaker:
        errorThresholdPercentage: 60

3.6 公共返回类

3.6.1 微服务中返回给前端的数据

在上面创建的fallback类中,发生服务熔断时不应该直接返回一个null,而是返回一个公共返回类,包含状态码、信息、数据

3.6.2 创建公共返回类

这个公共返回类写在commons模块中,代码如下,实际开发时会封装得更为精细,把状态码和信息用枚举类进一步封装

微服务的远程调用

3.6.3 各个返回类型的改变

下面举例部分方法的返回类型的变化

client接口的方法的返回类型变化

微服务的远程调用

fallback类的方法的返回类型变化

微服务的远程调用

controller类的方法的返回类型变化

微服务的远程调用

3.7 fallbackFactory

基于一个工厂类创建Client接口的fallback类

微服务的远程调用

对比之前直接创建Client接口的fallback类

微服务的远程调用

无非是@FeignClient注解上,属性名的区别,以及fallbackFactory会获取client端的报错信息

微服务的远程调用

3.8 整体流程

微服务的远程调用

上一篇:服务降级之Hystrix使用


下一篇:微服务-熔断器Hystrix