原始调用方式
虽然现在企业中也有一部分采用RestTemplate + Ribbon来解决服务间通信的方式,但是这样的方案造成了代码高耦合,因为要将URL集成在代码中。为了解决这样的问题,Spring Cloud提供了一个OpenFeign组件,该组件底层封装了RestTemplate和Ribbon,使用更简单的代码就可以进行服务之间的通信。
造成的问题:
- 每次调用服务都需要写这些代码,存在大量的代码冗余。
- 服务地址如果修改,维护成本增高。
- 使用时不够灵活。
OpenFeign组件
1. 说明
Feign是一个声明式的伪HTTP客户端,它使得写HTTP客户端变得更简单。使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性(可以使用Spring MVC的注解),可使用Feign注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,默认实现了负载均衡的效果并且Spring Cloud为Feign添加了Spring MVC注解的支持。
2. 使用
-
调用方引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> -
调用方入口类加入注解开启OpenFeign支持
@SpringBootApplication @EnableDiscoveryClient // 开启OpenFeign支持 @EnableFeignClients public class UsersApplication { public static void main(String[] args) { SpringApplication.run(UsersApplication.class, args); } } -
调用方创建client包
-
调用方创建被调用方的Feign客户端
// 注解中填写被调用方服务名 @FeignClient("products") public interface ProductClient { /** * 接口必须和被调用接口一致 * 方法名尽量和被调用方法名一致 * @return 响应信息 */ @GetMapping("/product/showProducts") Map<String, Object> showProducts(); } -
调用方Controller中使用
@Autowired private ProductClient productClient; /** * 在users微服务中通过服务注册中心负载均衡调用products微服务中某一服务器暴露出来的接口(OpenFeign) * @return 响应信息 */ @GetMapping("/user/showProducts") public Map<String, Object> showProducts() { Map<String, Object> result = productClient.showProducts(); return result; }注意:OpenFeign组件的负载均衡调用算法和Ribbon一样,修改算法的方式也和Ribbon一样。
OpenFeign参数传递
1. GET参数传递
假设调用方的相应方法已创建,需要在被调用方和调用方中的Fengin客户端中的方法参数上加上@RequestParam注解进行参数传递。
-
被调用方
@GetMapping("/product/findOne") public Map<String, Object> findOne(@RequestParam("id") int id) { Map<String, Object> result = new HashMap<>(); result.put("status", true); result.put("msg", "商品"); result.put("id", id); return result; } -
调用方Feign客户端
/** * OpenFeign组件GET类型请求传递的参数需要使用@RequestParam,注解中value必须和被调用方函数参数的参数名一致 * 方法参数名建议和被调用方法参数名一致 * @param id 商品id * @return 响应信息 */ @GetMapping("/product/findOne") Map<String, Object> findOne(@RequestParam("id") int id);
2. GET(RESTful)参数传递
如果URL符合RESTful规范,同时使用GET方式进行参数传递,需要在被调用方和调用方中的Fengin客户端中的方法参数上加上@PathVariable注解进行参数传递
-
被调用方
@GetMapping("/product/findOne/{id}") public Map<String, Object> findOne(@PathVariable("id") int id) { Map<String, Object> result = new HashMap<>(); result.put("status", true); result.put("msg", "商品"); result.put("id", id); return result; } -
调用方Feign客户端
@GetMapping("/product/findOne/{id}") Map<String, Object> findOne(@PathVariable("id") int id);
3. POST参数传递
如果POST方式传递的参数不是对象的话,可用和GET传递参数一样,使用@RequestParam解决。
但是通常POST方式传递的是Bean,这个时候解决方案为:调用方的Fengin客户端使用@RequestBody将对象转成JSON格式进行数据传递,被调用方也使用@RequestBody将接收到的JSON格式的数据转为Bean进行处理。
-
被调用方
@PostMapping("/product/updateOne") public Map<String, Object> updateOne(@RequestBody Product product) { Map<String, Object> result = new HashMap<>(); result.put("status", true); result.put("msg", "修改商品"); result.put("product", product); return result; } -
调用方Feign客户端
/** * OpenFeign组件POST类型请求传递对象参数 * @param product 修改商品信息 * @return 响应信息 */ @PostMapping("/product/updateOne") Map<String, Object> updateOne(@RequestBody Product product);
OpenFeign超时设置
1. 说明
默认情况下,OpenFiegn在进行服务调用时,要求服务提供方(被调用方)处理业务逻辑时间必须在1S内返回,如果超过1S没有返回则OpenFeign会直接报错,不会等待服务执行。但是往往在处理复杂业务逻辑是可能会超过1S,因此需要修改OpenFeign的默认服务调用超时时间。
2. 调用方配置
-
指定服务配置
application.yml
# 配置Feign指定服务超时等待 feign: client: config: # 被调用服务名 products: # 服务连接超时 单位(ms) connectTimeout: 5000 # 服务等待超时 单位(ms) readTimeout: 5000 -
全局配置
application.yml
# 配置Feign所有服务超时等待 feign: client: config: default: connectTimeout: 5000 readTimeout: 5000
OpenFeign日志调试
1. 说明
往往在服务调用时我们需要详细展示Feign的日志,默认Feign在调用时是没有日志输出的,因此在调试程序时应该开启Feign的详细日志展示。Feign对日志的处理非常灵活,可为每个Feign客户端指定日志记录策略。每个客户端都会创建一个logger默认情况下logger的名称是Feign的全限定名。需要注意的是,Feign日志的打印只会在DEBUG级别做出响应。
我们可以为Feign客户端配置各自的loggerLevel对象,告诉Feign记录哪些日志。
关于SpringBoot日志级别:TARCE < DEBUG < INFO < WARN < ERROR < FATAL
SpringBoot默认日志级别为INFO,因此默认TARCE,DEBUG级别的日志是看不到的。
2. 日志展示类型
可用通过配置loggerLevel的值实现:
- NONE:不记录任何日志。
- BASIC:仅仅记录请求方法,URL,响应状态代码及执行时间。
- HEADERS:记录BASIC级别的基础上,记录请求和响应的header。
- FULL:记录请求和响应的header,body和元数据。
3. 调用方开启日志展示
-
指定Feign客户端所在包(指定和全局都需要配置)
# 指定Feign客户端所在包 logging: level: # 包路径 org: aydenbryan: users: # 必须是DEBUG级别 clients: debug -
指定服务日志展示
# 配置Feign指定服务的日志展示 feign: client: config: # 被调用服务名 products: # 展示类型 loggerLevel: full -
全局配置日志展示
# 配置Feign所有服务的日志展示 feign: client: config: default: loggerLevel: full