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 {@Autowiredprivate 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)