openfeign(*)
简介
官网:https://cloud.spring.io/spring-cloud-openfeign/reference/html/
Feign是一个声明式的伪HTTP客户端(底层使用RestTemplate),它使编写web服务客户端变得更容易。使用feign,只需要创建一个接口并对其添加注解。它具有可插入的注释支持(可以使用springmvc的注解),可使用feign注解和JAX-RS注解。Feign还支持可插拔编码器和解码器。feign默认集成了Ribbon,默认实现了负载均衡的效果,并且spring cloud为feign添加了springmvc注解的支持
OpenFeign调用
- 1.引入依赖
<!-- OpenFeign 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 项目集成其他依赖 -->
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 健康检查 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- consul服务注册与发现 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
- 2.配置文件
server.port=8787
spring.application.name=CATEGORY
# consul注册中心地址
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
- 3.启动类添加注解@EnableFeignClients
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CategoryApplication {
}
- 4.编写feign客户端接口,添加注解@FeignClient
@FeignClient(value = "PRODUCT") // value:服务名
@Api("商品客户端接口定义")
public interface ProductClient {
@ApiOperation("简单实现OpenFeign通信")
@GetMapping("/product")
String product();
@ApiOperation("使用拼接参数?k=v&k1=v2方式传参。需指定@RequestParam和属性value")
@GetMapping("/test")
String test(@RequestParam(value = "id") Integer id, @RequestParam(value = "productName") String productName);
@ApiOperation("使用路劲url/{id}/{productName}传参。需指定@PathVariable和属性value")
@GetMapping("/test1/{id}/{productName}")
String test1(@PathVariable(value = "id") Integer id, @PathVariable(value = "productName") String productName);
@ApiOperation("使用对象传递,用JSON方式")
@PostMapping("/test2")
String test2(@RequestBody ProductDto productDto);
}
- 5.Category服务和Product服务进行feign调用测试
@RestController
@Slf4j
public class CategoryController {
@Autowired
private ProductClient productClient;
@GetMapping("/category")
public String category() {
log.info("category enter......");
// 使用OpenFeign完成服务间的调用
//String result = productClient.product();
// 使用拼接参数?k=v&k1=v2方式传参
//String result = productClient.test(12, "裙子");
// 使用路劲/{id}/{productName}传参
//String result = productClient.test1(12, "裙子");
// 使用对象传递,用JSON方式
//String result = productClient.test2(new ProductDto(12, "裙子", 37.80, new Date()));
// 使用数组传参。了解即可
//String result = productClient.test3(new Integer[]{1, 2, 3});
// 使用集合传参。服务费需要集合定义到对象中(不能直接使用集合形参)。了解即可
String result = productClient.test4(new Integer[]{1, 2, 3});
return "category ok!!" + result;
}
}
@RestController
@Slf4j
@Api("商品服务")
public class ProductController {
@Value("${server.port}")
private int port;
@ApiOperation("简单实现OpenFeign通信")
@GetMapping("/product")
public String product() {
log.info("product enter......");
return "product ok,当前端口号:" + port;
}
@ApiOperation("使用拼接参数?k=v&k1=v2方式传参")
@GetMapping("/test")
public String test(@RequestParam Integer id, @RequestParam String productName) {
log.info("test enter....id={},productName={}", id, productName);
return "product ok,当前端口号:" + port;
}
@ApiOperation("使用路劲url/{id}/{productName}传参")
@GetMapping("/test1/{id}/{productName}")
public String test1(@PathVariable Integer id, @PathVariable String productName) {
log.info("test1 enter....id={},productName={}", id, productName);
return "product ok,当前端口号:" + port;
}
@ApiOperation("使用对象传递,用JSON方式")
@PostMapping("/test2")
public String test2(@RequestBody ProductDto productDto) {
log.info("test enter....productDto={}", productDto);
return "product ok,当前端口号:" + port;
}
@ApiOperation("使用数组传参。了解即可")
@GetMapping("/test3")
public String test3(Integer[] ids) {
for (Integer id : ids) {
log.info("id:{}",id);
}
return "product ok,当前端口号:" + port;
}
@ApiOperation("通过集合传参,不能直接使用List等集合接收,需要用对象定义。了解即可")
@GetMapping("/test4")
public String test4(ProductIdsDTO productIdsDTO) {
productIdsDTO.getIds().forEach(id -> log.info("id:{}", id));
return "product ok,当前端口号:" + port;
}
}
- 6.feign接口传参
需要指定传参格式,OpenFeign是一个伪的HTTP客户端,底层用的RestTemplate,所以需要在feign接口中告诉它如何去传参,否则会报错
1.通过?拼接传参。例如:url/?k=v&k1=k2
@GetMapping("/test")
String test(@RequestParam(value = "id") Integer id, @RequestParam(value = "productName") String productName);
备注:需要指定@RequestParam注解和value属性值
2.通过路径传参。例如:url/{id}/{productName}
@GetMapping("/test1/{id}/{productName}")
String test1(@PathVariable(value = "id") Integer id, @PathVariable(value = "productName") String productName);
备注:需要指定@PathVariable注解和value属性值
3.通过对象传参,推荐使用JSON格式
@PostMapping("/test2")
String test2(@RequestBody ProductDto productDto);
备注:需要指定@RequestBody注解
4.使用数组传参和集合传参,了解即可
数组传参,feign接口定义:
@GetMapping("/test3")
String test3(@RequestParam Integer[] ids);
备注:必须指定@RequestParam注解
使用集合传参,跟数组传参定义一致。但服务方形参接收定义不一样,详情见"5.feign接口测试"示例
OpenFeign接口调用超时设置
- OpenFeign接口调用,超时时间默认为1秒,当服务方执行时间超过1秒时,会报错
There was an unexpected error (type=Internal Server Error, status=500).
Read timed out executing GET http://PRODUCT/product
- 修改OpenFeign默认超时时间配置
# 修改某个服务(PRODUCT)的OpenFeign默认超时时间(默认为1秒)
# PRODUCT:服务名。指定服务连接超时,单位毫秒
feign.client.config.PRODUCT.connect-timeout=3000
# PRODUCT:服务名。指定服务等待超时,单位毫秒
feign.client.config.PRODUCT.read-timeout=3000
# 修改所有服务的OpenFeifn默认超时时间(默认为1秒)
#feign.client.config.default.connect-timeout=3000
#feign.client.config.default.read-timeout=3000
OpenFeign调用日志
- OpenFeign日志级别
- feign对日志的处理非常灵活,可以为每个feign客户端指定日志记录策略,feign日志的打印只会DEBUG级别做出响应
- 可以为feign客户端配置各自的logger.level对象,日志级别有如下几种
- NONE:不记录任何日志(默认)
- BASIC:仅记录请求方法,url,响应状态码及执行时间
- HEADERS:记录BASIC级别的基础上,记录请求和响应的header
- FULL:记录请求和响应的header,body及元数据
- 开启日志打印
# 开启某个feign客户端(PRODUCT)的日志打印
# feign.client.config.PRODUCT.logger-level=full
# 开启所有的feign客户端的日志打印
feign.client.config.default.logger-level=full
# 指定包路径日志打印级别(指定feign客户端的包日志级别必须是debug)
logging.level.com.coolw.cloud.study.feign=debug
日志信息如:
22-01-12 22:51:04.139 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] ---> GET http://PRODUCT/product HTTP/1.1
2022-01-12 22:51:04.139 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] ---> END HTTP (0-byte body)
......
2022-01-12 22:51:05.410 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] <--- HTTP/1.1 200 (1267ms)
2022-01-12 22:51:05.410 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] connection: keep-alive
2022-01-12 22:51:05.410 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] content-length: 33
2022-01-12 22:51:05.410 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] content-type: text/plain;charset=UTF-8
2022-01-12 22:51:05.411 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] date: Wed, 12 Jan 2022 14:51:05 GMT
2022-01-12 22:51:05.411 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] keep-alive: timeout=60
2022-01-12 22:51:05.411 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product]
2022-01-12 22:51:05.413 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] product ok,当前端口号:8788
2022-01-12 22:51:05.413 DEBUG 8324 --- [nio-8787-exec-1] c.coolw.cloud.study.feign.ProductClient : [ProductClient#product] <--- END HTTP (33-byte body)