Spring Cloud OpenFeign 基于 Netflix Feign 实现,整合了 Spring Cloud Ribbon 与 Spring Cloud Hystrix ,除了提供这两者的强大功能之外,它还提供了一种声明式的 Web 服务客户端定义方式,让我们可以像使用本地方法一样来进行远程服务调用。
快速入门
通过一个简单的示例来展现 Spring Cloud Feign 在服务客户端定义上所带来的便利。
下面的示例将继续使用之前我们实现的 order-service 服务,这里我们会通过 Spring Cloud Feign 提供的声明式服务绑定功能来实现对该服务接口的调用。
配置代码
1.引入依赖
orderservice 服务的 pom.xml
文件中,引入如下依赖:
1 2 3 4 5 |
<!--spring cloud openFeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> |
openfeig 依赖中已然包含了 ribbon 的依赖
2.启用 Feign
在 orderservice 的启动类上,添加注解@EnableFeignClients
1 2 3 4 5 6 7 8 |
@MapperScan("com.niit.order.mapper") @SpringBootApplication @EnableFeignClients public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } } |
3.定义服务接口
定义一个接口,通过@FeignClient
注解指定服务名来绑定服务,然后再使用 Spring MVC 的注解来绑定具体该服务提供的 REST 接口。
1 2 3 4 5 6 7 8 |
@FeignClient("userservice") public interface UserClient { /** * 根据ID获取用户信息 */ @GetMapping("/user/{id}") User findUserById(@PathVariable("id") String id); } |
注意:
注解中的服务名不区分大小写,所以使用USERSERVICE
和userservice
都 是可以的。注意接口的访问路径要完整, 如果匹配的路径不对, 会报 404 错误.
4.创建 Controller
创建一个 Controller 来实现对 Feign 客户端的调用。使用 @Autowired
直接注入上面定义的 UserClient 实例,并在 helloConsumer 函数中调用这个绑定了 userservice 服务接口的客户端来向该服务发起接口的调用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@RestController @RequestMapping("/feign") public class FeignDemoController { @Autowired private OrderService orderService; @Autowired private UserClient userClient; @GetMapping("/order/{orderId}") public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) { // 根据id查询订单并返回 Order order = orderService.queryOrderById(orderId); // 使用 Feign 封装的客户端 User forObject = userClient.findUserById(order.getUserId()+""); //封装返回的数据 order.setUser(forObject); return order; } } |
测试验证
如之前验证 Ribbon 客户端负载均衡一样,我们先启动服务注册中心 Nacos 以及 orderservice 服务和多个 userservice 服务。此时我们在 Nacos 信息面板中可看到 如下内容:
发送几次 GET 请求到 http://localhost:8080/feign/order/{orderId}
, 可以得到如 之前 Ribbon 实现时一样的效果,正确返回了 userservice 的数据。
并且根据控制台的输出,我们可以看到 Feign 实现的消费者,依然是利用 Ribbon 维护了针对 userservice 的服务列表信息,并且通过轮询实现了客户端负载均衡。而与 Ribbon 不同的是,通过 Feign 我们只需定义服务绑定接口,以声明式的方法,优雅而简单地实现了服务调用。
Feign 使用起来看起来更像是一个本地接口的实现,而不是一个远程服务的调用。这样的设计使得我们在使用 Feign 的时候,可以像调用本地方法一样调用远程服务。
小问题 :
RestTemplate 己经实现了对 __ 请求的封装处理,形成了一套模板化的调用方法?
- FTP
- HTTP
- TCP
- IP
答案
1 |
2. HTTP |
参数绑定
在本节中,我们将详细介绍 Feign 中对几种不同形式参数的绑定方法。
扩展一下服务提供方 orderservice 。增加下面这些接口定义,其中包含:
- 带有 Request 参数的请求
- 带有 Header 信息的请求
- 带有 RequestBody 的请求并且请求响应体中是一个对象的请求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
@RestController @RequestMapping("/provider/feign") public class FeignDemoController { /* request参数的请求 */ @GetMapping(value = "/request") public String hello (@RequestParam("name") String name) { return "Hello" + name; } /* header信息的请求 */ @GetMapping(value = "/header" ) public User hello(@RequestHeader String name, @RequestHeader String address) { User user = new User(); user.setId(IdUtil.simpleUUID()); user.setUsername(name); user.setAddress(address); return user; } /* requestbody的请求 */ @PostMapping(value = "/body") public String hello(@RequestBody User user) { return "Hello, " + user .getUsername() + ", " + user .getAddress(); } } |
实体类 User 的定义如下:
1 2 3 4 5 6 |
@Data public class User { private String id; private String username; private String address; } |
需要注意的是,这里必须要有 User 的默认构造函数。不然, Spring Cloud Feign 根据 JSON 字符串转换 User 对象时会抛出异常。我们使用 Lombok 插件的 @Data 注解, 可以避免编写标准 java bean 的繁琐步骤,同时提供相应的默认构造。
在完成了对 orderservice 的改造之后,下面我们开始在快速入门示例的 UserClient 接口中实现这些新增的请求的绑定。
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 |
@FeignClient("userservice") public interface UserClient { /** * 根据ID获取用户信息 * @param id * @return */ @GetMapping("/user/{id}") User findUserById(@PathVariable("id") String id); /** * request参数的请求 * @param name * @return */ @GetMapping(value = "/provider/feign/request") public String hello (@RequestParam("name") String name); /** * header信息的请求 * @param name * @param address * @return */ @GetMapping(value = "/provider/feign/header" ) public User hello(@RequestHeader String name, @RequestHeader String address); /** * requestbody的请求 * @param user * @return */ @PostMapping(value = "/provider/feign/body") public String hello(@RequestBody User user) ; } |
最后,在 Controller 中新增方法,来对本节新增的声明接口进行调用,修改后的完整代码如下所示:
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 |
@RestController @RequestMapping("/feign") public class FeignConsumerController { @Autowired private OrderService orderService; @Autowired private UserClient userClient; @GetMapping("/order/{orderId}") public Order queryOrderByUserId(@PathVariable("orderId") Long orderId) { // 根据id查询订单并返回 Order order = orderService.queryOrderById(orderId); // 使用Feign 封装的客户端 User forObject = userClient.findUserById(order.getUserId()+""); //封装返回的数据 order.setUser(forObject); return order; } @GetMapping("/order/request") public String requestParamDemo(@RequestParam String param){ return userClient.requestParam(param); } @GetMapping("/order/header") public User headerInfo(@RequestHeader String name,@RequestHeader String address){ return userClient.headerInfo(name,address); } @GetMapping("/order/body") public String bodyInfo(@RequestBody User user){ return userClient.bodyInfo(user); } } |
测试验证
在完成上述改造之后,启动服务注册中心 Nacos、order-service 服务以及 user-service 服务 。通过浏览器访问 order-service 的 FeignConsumerController 的 requestParamDemo 方法, 触发新增接口的调用。最终,我们会获得如下输出,代表接口绑定和调用成功:
其他两种参数绑定的调用,我们可以使用 IDEA 的一款小插件FastRequest
进行测试:(FastRequest
是收费插件, 可以用免费插件 RestfulTool
代替)
添加请求头:
点击发送并查看响应结果:
同样的, RequestBody
的参数也可以添加请求体,点击发送按钮进行测试,结果如下:
实践方案
我们几乎完全可以从服务提供方的 Controller 中依靠复制操作,构建出相应的服务客户端绑定接口, 然后添加@FeignClient 注解即可。使用的时候,直接注入到 Controller 中即可。
这部分内容是否可以得到进一步的抽象呢?在 Spring Cloud Feign 中,针对该问题我们通常有两种解决方案,以进一步减少编码量。
- 通过继承的方式实现接口定义的共享
- 创建独立的 Api 模块, 通过依赖的方式实现接口定义的共享
继承方案
一样的代码可以通过继承来共享:
-
定义业务相关接口,利用接口定义,并基于 SpringMVC 注解做声明。
- 注意定义 MappingUrl 时,需要使用绝对路径,否则会出现 404 错误。
-
Feign 客户端和 Controller 都继承该接口。
- 这样接口实际只需要定义一次,就可以在 Feign 客户端和 Controller 中使用。免去了复制操作。
图示中, UserClient 继承了 UserApi 接口。这样,我们就可以在 UserClient 中直接使用 UserApi 中定义的接口,而不需要再次定义。在 Controller 中, 也可以直接注入 UserClient,并调用其中的方法。
使用 Spring Cloud Feign 继承特性的优点很明显,可以将接口的定义从 Controller 中剥离,实现在构建期的接口绑定,从而有效减少服务客户端的绑定配置。这么做虽然可以很方便地实现接口定义和依赖 的共享,不用再复制粘贴接口进行绑定.
但是这样通过共享接口的做法使用不当的话会带来副作用。
由于接口在构建期间就建立起了依赖,所以微服务之间的耦合度会变得很高。因此接口变动就会对项目构建造成影响。
可能服务提供方修改了一个接口定义,那么会直接导致客户端工程的构建失败。所以,如果开发团队通过此方法来实现接口共享的话,建议在开发评审期间严格遵守面向对象的开闭原则,尽可能地做好前后版本的兼容,防止牵一发而动全身的后果,增加团队不必要的维护工作量。
基于以上原因,官方并不推荐在项目中使用继承方案,而是更推荐我们由服务提供方新建立一个 API 服务,提供给所有的消费者使用。接下来,我们将会详细讲解这种实践方案。
独立模块
将 Feign 的 Client 抽取为独立的微服务模块,并且把接口有关的 POJO、默认的 Feign 配置都放到这个微服务模块中,提供给所有消费者微服务项目使用。例如,将 UserClient、User、Feign 的默认配置都抽取到一个 feign-api 包中,所有微服务引用该依赖包,即可直接使用。
下面是构建 feign-api 的具体步骤:
1.创建 module
首先创建一个 module,命名为 feign-api:
2.引入依赖
在 feign-api 中然后引入 feign 的 starter 依赖
1 2 3 4 |
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> |
然后,order-service 中编写的 UserClient
、User
都复制到 feign-api 项目中。
通过 maven 的 install
对项目进行构建和安装,将 feign-api 安装到本地仓库中。
3.使用 feign-api
在 order-service 的 pom 文件中从本地 maven 仓库中引入 feign-api 的依赖:
1 2 3 4 5 |
<dependency> <groupId>com.niit.demo</groupId> <artifactId>feign-api</artifactId> <version>1.0</version> </dependency> |
修改 order-service 中的所有与上述三个组件有关的导包部分,改成导入 feign-api 中的包
4.修改启动类
由于 feign-api 与 order-service 项目的包路径并不完全一致,为了解决 spring boot 工程默认包扫描策略导致无法注入问题,需要在启动类上声明所用的 feign 客户端的列表。 修改 order-service 的启动类,补充@EnabledFeignClients 的属性内容。
1 2 3 4 5 6 7 8 9 10 11 |
@MapperScan("com.niit.order.mapper") @SpringBootApplication @EnableCircuitBreaker //启用断路器功能 @ServletComponentScan(basePackages = {"com.niit.order.filter"}) @EnableFeignClients(clients = {UserClient.class}) //解决包扫描无法注入问题 public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } } |
5.启动测试
现在,像上一小节参数绑定中的案例一样,依次启动服务注册中心 Nacos,order-service 与 user-service 服务进行测试。测试结果如下:
添加 Ribbon 配置
由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 客户端的方式来自定义各个服务客户端调用的参数。那么我们 如何在使用 Spring Cloud Feign 的工程中使用 Ribbon 的配置呢?
全局 Ribbon 配置
全局配置的方法非常简单,我们可以直接在配置文件中设置:
1 2 3 4 5 6 7 |
ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #负载均衡规则的全类名 ConnectTimeout: 250 #请求连接的超时时间 ReadTimeout: 1000 #请求处理的超时时间 OkToRetryOnAllOperations: true #对所有操作请求都进行重试 MaxAutoRetriesNextServer: 2 #切换实例的重试次数 MaxAutoRetries: 1 #对当前实例的重试次数。 |
对具体服务进行 Ribbon 配置
在配置文件中, 除了针对所有服务进行全局配置, 还可以针对特定的服务进行个性化配置.
在定义 Feign 客户端的时候,我们使用了 @FeignClient
注解。在初始化 过程中,Spring Cloud Feign 会根据该注解的 name 属性或 value 属性指定的服务名,自动创建一个同名的 Ribbon 客户端。
例如使用 @FeignClient(value ="userservice")
来创建 Feign 客户端的时候,同时也创建了一个名为 userservice
的 Ribbon 客户端。
既然如此,我们就可以使用 @FeignClient
注解中指定的服务提供者名称来设置对应的 Ribbon 参数,比如:
1 2 3 4 5 6 7 8 |
userservice: # 需要远程访问的服务名 ribbon: NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule #负载均衡规则的全类名 ConnectTimeout: 250 #请求连接的超时时间 ReadTimeout: 1000 #请求处理的超时时间 OkToRetryOnAllOperations: true #对所有操作请求都进行重试 MaxAutoRetriesNextServer: 2 #切换实例的重试次数 MaxAutoRetries: 1 #对当前实例的重试次数。 |
添加 Ribbon 重试机制
在 Spring Cloud Feign 中默认实现了请求的重试机制,而上面我们对于 orderservice 客户端的配置内容就是对于请求超时以及重试机制配置的详情,具体内 容可参考第 4 章最后一节关于 Spring Cloud Ribbon 重试机制的介绍。
这里需要注意一点,Ribbon 的超时与 Hystrix 的超时是两个概念。为了让上述实现有效,我们需要让 Hystrix 的超时时间大于 Ribbon 的超时时间,否则 Hystrix 命令超时后,该命令直接熔断,重试机制就没有任何意义了。
hystrix 的超时时间配置如下:
1 2 3 4 5 6 7 |
hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 3000 #Hystrix 超时时间 |
Ribbon 的超时时间配置如下:
1 2 3 |
ribbon: ConnectTimeout: 250 #请求连接的超时时间 ReadTimeout: 1000 #请求处理的超时时间 |
添加 Hystrix 配置
在 Spring Cloud Feign 中,除了引入了用于客户端负载均衡的 Spring Cloud Ribbon 之外,还引入了服务保护与容错的工具 Hystrix 。
默认情况Spring Cloud Feign 是关闭Hystrix支持的, 因此需要手动启用Hystrix。开启Hystrix后所有 Feign 客户端的方法都会封装成为 Hystrix 命令中进行服务保护。
在对 Hystrix 进行配置之前,我们需要确认 feign.hystrix.enabled
参数需要先设置为 true
, 否则默认会关闭 Feign 客户端的 Hystrix 支持。
1 2 3 |
feign: hystrix: enabled: true # 启用ystrix功能 |
默认
feign.hystrix.enabled=false
, Hystrix 支持是关闭的使用
hystrix.command.default.execution.timeout.enabled=false
关闭超时熔断
添加 Hystrix 全局配置
对于 Hystrix 的全局配置同 Spring Cloud Ribbon 的全局配置一样,直接使用它的默认配置前缀 hystrix.command.default
就可以进行设置,比如设置全局的超时时间:
1 2 3 4 5 6 7 8 |
# 断路器的超时时间需要大于Ribbon的超时时间,不然不会触发重试。 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 5000 |
局部禁用 Hystrix
如果全局启用 Hystrix 支持,但是想要针对某个服务客户端关闭 Hystrix 支持时,可以通过使用 @Scope("prototype")
注解为指定的客户端配置 Feign.Builder
实例.
详细实现步骤如下所示。
1.构建一个配置类
1 2 3 4 5 6 7 8 9 10 11 12 |
/** * 针对指定的Feign客户端禁用Hystrix的配置 */ @Configuration public class UserClientConfig { @Bean @Scope("prototype") public Feign.Builder feignBuilder() { return Feign.builder(); } } |
2.引入配置到 Feign 客户端
在 UserClient 的@FeignClient
注解中,通过 configuration
参数引入上面实现的配置。
1 2 3 4 |
@FeignClient(value = "userservice",configuration = UserClientConfig.class) public interface UserClient { ...... } |
这样,就可以针对指定的 Feign 客户端来关闭 Hystrix 支持了。
原理是通过
@Scope("prototype")
注解为每个客户端创建一个新的实例,而此实例不受 Hystrix 配置的影响。
通过 Hystrix 命令的键值进行配置
对于 Hystrix 命令的配置,在实际应用时往往也会根据实际业务情况制定出不同的配置方案。配置方法也跟传统的 Hystrix 命令的参数配置相似,采用 hystrix.command.<commandKey>
作为前缀。而 <commandKey>
默认情况下会采用 Feign 客户端中的方法名作为标识,所以,针对上一节介绍的重试机制中对 /showAppInfo
接口的熔断超时时间的配置可以通过其方法名作为 <commandKey>
来进行配置.
具体如下:
1 2 3 4 5 6 7 8 |
# 断路器的超时时间需要大于Ribbon的超时时间,不然不会触发重试。 hystrix: showAppInfo: # @FeignClient中的方法名(Hystrix命令的键值) default: execution: isolation: thread: timeoutInMilliseconds: 5000 |
在使用指定命令配置的时候,需要注意,由于方法名很有可能重复,这个时候相同方法名的 Hystrix 配置会共用,所以在进行方法定义与配置的时候需要做好一定的规划。
服务降级配置
Hystrix 提供的服务降级是服务容错的重要功能, Spring Cloud Feign 提供了一种简单的定义方式,可以优雅的完成服务降级的配置。下面我们在之前创建的 feign-api 工程中进行改造。
- 服务降级逻辑的实现只需要为 Feign 客户端的定义接口编写一个具体的接口实现类。 比如为
UserClient
接口实现一个服务降级类UserClientFallback
,其中每个重写方法的实现逻辑都可以用来定义相应的服务降级逻辑,具体如下:
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 |
/** * 服务降级实现类, 用于定义服务降级逻辑 * 该类需要实现Feign客户端接口拿到所有需要配置降级的方法 */ public class UserClientFallback implements UserClient { @Override public User findUserById(String id) { User user = new User(); user.setUsername("findUserById 调用失败"); user.setId(UUID.randomUUID().toString()); return user; } @Override public String requestParam(String name) { return "requestParam 调用失败"; } @Override public User headerInfo(String name, String address) { User user = new User(); user.setUsername("headerInfo 调用失败"); user.setId(UUID.randomUUID().toString()); return user; } @Override public String bodyInfo(User user) { return "bodyInfo 调用失败"; } } |
- 在服务绑定接口 UserClient 中,通过
@FeignClient
注解的fallback
属性来指定对应的服务降级实现类。
1 2 3 4 |
@FeignClient(value = "userservice",fallback = UserClientFallback.class) public interface UserClient { ... } |
-
在 order-service 服务的启动类上使用
@Bean
注解声明服务降级类,解决 UserClientFallback 实例无法找到异常1234567891011121314151617@MapperScan("com.niit.order.mapper")@SpringBootApplication@EnableCircuitBreaker //启用断路器功能@ServletComponentScan(basePackages = {"com.niit.order.filter"})@EnableFeignClients(clients = {UserClient.class}) //解决包扫描无法注入问题public class OrderApplication {public static void main(String[] args) {SpringApplication.run(OrderApplication.class, args);}@Beanpublic UserClientFallback userClientFallback(){return new UserClientFallback();}}
上面的做法,是使用了 feign-api 提供的默认的统一服务降级配置类, 对每个接口都配置了对应的服务降级逻辑.
如果,order-service 想自定义某个服务降级逻辑, 只需在 order-service 中创建一个新的类,继承 UserClientFallback
,重写其方法, 注入到 spring 容器里即可:
创建新的自定义服务降级类:
1 2 3 4 5 6 7 |
public class MyUserClientFallback extends UserClientFallback { @Override public String requestParam(String name) { return "orderservice服务的自定义服务降级方法被触发了..."; } } |
重新注入到 Spring 容器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@MapperScan("com.niit.order.mapper") @SpringBootApplication @EnableCircuitBreaker //启用断路器功能 @ServletComponentScan(basePackages = {"com.niit.order.filter"}) @EnableFeignClients(clients = {UserClient.class}) //解决包扫描无法注入问题 public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } //注入order-service自定义的 MyUserClientFallback服务降级类 @Bean public UserClientFallback userClientFallback(){ return new MyUserClientFallback(); } } |
测试验证
下面我们来验证一下服务降级逻辑的实现。启动服务注册中心 Nacos 和 order-service, 但是不启动 user-service 服务。发送 GET 请求到 http://localhost:8080/feign/order/request?param=world, 因为 user-service 服务没有启动,会直接触发服务降级.
如果没有降级发生, 检查order-service是否在springboot应用配置文件中启用hystrix:
1 2 3 |
feign: hystrix: enabled: true |
注意 springcloud2020.0.1之后的版本改为
feign.circuitbreaker.enabled=true
正如我们在 MyUserClientFallback
类中实现的内容,每一个服务接口的断路器实际就是实现类中的重写函数的实现。
小问题:
在 Spring Cloud Feign 中,可以通过 _____ 来关闭 Hystrix 功能?
- feign.hystrix.enabled=false
- feign.hystrix.hystrixCommand=false
- feign.hystrix.disabled=false
- feign.hystrix.hystrix=false
答案
1 |
1. feign.hystrix.enabled=false |
其他配置
请求压缩
Feign 是通过 http 调用的,那么就牵扯到一个数据大小的问题。如果不经过压缩就发送请求、获取响应,那么会因为流量过大导致浪费流量,这时就需要使用数据压缩,将大流量压缩成小流量。
Spring Cloud Feign 支持对请求与响应进行 GZIP 压缩,以减少通信过程中的性能损耗。
配置请求压缩支持, 找到需要调用 feign 的服务消费者 order-service 模块配置文件中添加如下配置,即可开启:
1 2 3 4 5 6 |
feign: compression: request: enabled: true # 开启请求压缩 response: enabled: true # 开启响应压缩 |
同时,我们也可以对请求的数据类型,以及触发压缩的大小下限进行设置:
1 2 3 4 5 6 |
feign: compression: request: enabled: true # 开启请求压缩 mime-types: text/html,application/xml,application/json # 设置压缩的数据类型 min-request-size: 2048 # 设置触发压缩的大小下限 |
值得注意的是,上面的数据类型、压缩大小下限均为默认值。
日志配置
Spring Cloud Feign 在构建服务客户端时,会为每一个客户端都创建一个 feign. Logger 实例,我们可以利用该日志对象的 DEBUG 模式来帮助分析 Feign 的请求细节。
对于 Feign 的 Logger 级别主要有下面 4 类,可根据实际需要进行调整使用。
NONE
:不记录任何信息。BASIC
:仅记录请求方法、 URL 以及响应状态码和执行时间。HEADERS
:除了记录 BASIC 级别的信息之外,还会记录请求和响应的头信息。FULL
:记录所有请求与响应的明细,包括头信息、请求体、元数据等
配置 Feign 日志有两种方式:
- 配置文件方式
- Java 配置类方式
日志配置可以配置全局生效,也可以配置针对某个微服务生效,下面我们详细介绍两种方式是如何开启日志配置。
配置文件方式
通过修改服务消费方的 application.yml 文件,配置 Spring Cloud Feign 的日志级别。
全局 FeignClient 配置:
1 2 3 4 5 6 7 8 |
feign: client: config: metaDataClient: connect-timeout: 3000 read-timeout: 3000 default: #default 代表全局配置 loggerLevel: FULL |
针对实例的 FeignClient 配置:
1 2 3 4 5 6 7 8 |
feign: client: config: metaDataClient: connect-timeout: 3000 read-timeout: 3000 userservice: #服务名称 针对某个微服务的配置 loggerLevel: BASIC |
我们以 FULL 级别的日志配置为例,发送一次请求测试,查看控制台日志输出,可以看到日志信息非常完整:
通过 Java 配置类方式配置 FeignClient
需要先声明一个 Bean,用来定义日志的级别。
1 2 3 4 5 6 |
public class UserClientConfig { @Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } } |
使用 Java 配置类进行全局 FeignClient 配置
全局配置,则需要在服务消费方的主启动类上进行配置指定:
1 2 3 4 5 6 7 8 9 10 11 12 |
@MapperScan("com.niit.order.mapper") @SpringBootApplication @EnableCircuitBreaker //启用断路器功能 @ServletComponentScan(basePackages = {"com.niit.order.filter"}) //解决包扫描无法注入, 指定feign的全局配置属性 @EnableFeignClients(clients = {UserClient.class},defaultConfiguration = UserClientConfig.class) public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } } |
使用 Java 配置类进行局部 FeignClient 配置
局部配置,则放在相应的 feign 客户端的 @FeignClient
这个注解中:
1 2 3 4 |
@FeignClient(value = "userservice",configuration = UserClientConfig.class) public interface UserClient { ... } |
值得注意的是,当使用配置文件和使用 Java 代码两种方式同时存在的时候,配置文件的优先级更高。当然,我们不推荐同时配置两种方式,只保留一种方式即可。
活动 6.1:
使用 Feign 实现服务远程调用
练习问题
-
在服务绑定接口 HelloService 中,通过 @FeignClient 注解的 _____ 属性来指定对应的服务降级实现类。
a. fallback
b. back
c. fullback
d. fall查看答案
1fallback
-
对于 Hystrix 的全局配置同 Spring Cloud Ribbon 的全局配置一样,直接使用它的默认配置前缀 _____ 就可以进行设置
a. hystrix.command.index
b. hystrix.command.default
c. hystrix.default.command
d. hystrix.index.command查看答案
1hystrix.command.default
-
Ribbon 的超时与 Hystrix 的超时是两个概念。为了让上述实现有效,我们需要让 Hystrix 的超时时间 _____ Ribbon 的超时时间?
a. 小于
b. 等于
c. 大于
d. 不等于查看答案
1大于
-
我们在使用 Spring Cloud Ribbon 时,通常都会利用它对 _____ 的请求拦截来实现对依赖服务的接口调用。
a. FeignTemplate
b. SpringTemplate
c. CloudTemplate
d. RestTemplate查看答案
1RestTemplate
小结
在本章中,您学习了:
- 我们在使用 Spring Cloud Ribbon 时,通常都会利用它对 RestTemplate 的请求拦截来实现对依赖服务的接口调用,而 RestTemplate 己经实现了对 HTTP 请求的封装处理,形成了一套模板化的调用方法。
- 在 Spring Cloud Feign 的实现下,我们只需创建一个接口并用注解的方式来配置它,即可完成对服务提供方的接口绑定,简化了在使用 Spring Cloud Ribbon 时自行封装服务调用客户端的开发量。
- Spring Cloud Feign 具备可插拔的注解支持, 包括 Feign 注解和 JAX-RS 注解。同时,为了适应 Spring 的广大用户,它在 NetflixFeign 的基础上扩展了对 Spring MVC 的注解支持。
- 由于 Spring Cloud Feign 的客户端负载均衡是通过 Spring Cloud Ribbon 实现的,所以我们可以直接通过配置 Ribbon 客户端的方式来自定义各个服务客户端调用的参数。
- 在 Spring Cloud Feign 中,除了引入了用于客户端负载均衡的 Spring Cloud Ribbon 之外,还引入了服务保护与容错的工具 Hystrix。
- 在对 Hystrix 进行配置之前,我们需要确认 feign.hystrix.enabled 参数没有被设置为 false, 否则该参数设置会关闭 Feign 客户端的 Hystrix 支持。
- Hystrix 提供的服务降级是服务容错的重要功能。
1 |
value属性是必须的,用于指定服务提供方的服务名 |
查看答案
1 |
正确 |
查看答案
1 |
正确 |
查看答案
1 |
正确 |
查看答案
1 |
正确 |
查看答案
1 |
正确 |
Views: 12