1.OpenFeign简介
1.什么是OpenFeign
是一个声明式的webService客户端。使用OpenFeign能让Web Service客户端更加简单。
它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。SpringCloud对Feign进行了封装,使其支持了SpringMVC标准注解和HeetMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
官网地址:https://spring.io/projects/spring-cloud-openfeign
2.Feign能干什么
Feign旨在使编写Java客户端变得更容易。
之前使用是用Ribbon+RestTemplate来进行服务间调用。在实际开发中,由于对服务的调用不止一处,往往一个接口会被多次调用,所以通常会针对每个微服务自行封装一些客户端来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。
Feign集成了Ribbon,利用Ribbon维护服务的信息,并且通过轮询实现客户端的负载均衡。而与Ribbon不同的是,Feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。
3.OpenFeign和Feign的区别
4.OpenFeign默认集成了Ribbon
比如我们查看依赖图如下:
2 OpenFeign使用
1.新建项目cloud-consumer-feign-order80
2.修改pom
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>cloud</artifactId> <groupId>cn.qz.cloud</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>cloud-consumer-feign-order80</artifactId> <dependencies> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <!--eureka-client--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!--引入自己抽取的工具包--> <dependency> <groupId>cn.qz.cloud</groupId> <artifactId>cloud-api-commons</artifactId> <version>${project.version}</version> </dependency> <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> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project>
3.修改yml
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
register-with-eureka: true
service-url:
#单机版
defaultZone: http://localhost:7001/eureka/
4.主启动类:
package cn.qz.cloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; /** * @Author: qlq * @Description * @Date: 14:07 2020/10/17 */ @SpringBootApplication @EnableFeignClients public class OrderFeignMain80 { public static void main(String[] args) { SpringApplication.run(OrderFeignMain80.class, args); } }
5.业务类:
(1)Service接口:FeignClient注解声明用Feign获取服务,调用CLOUD-PAYMENT-SERVICE中相关接口
package cn.qz.cloud.service; import cn.qz.cloud.utils.JSONResultUtil; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; import java.util.Map; /** * @Author: qlq * @Description * @Date: 14:08 2020/10/17 */ @Component @FeignClient(value = "CLOUD-PAYMENT-SERVICE") public interface PaymentFeignService { @GetMapping(value = "/pay/listAll") JSONResultUtil<List<Map<String, Object>>> listAll(); @GetMapping("/pay/getServerPort") JSONResultUtil<String> getServerPort(); }
(2)Controller
package cn.qz.cloud.controller; import cn.qz.cloud.service.PaymentFeignService; import cn.qz.cloud.utils.JSONResultUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; import java.util.Map; /** * @Author: qlq * @Description * @Date: 14:14 2020/10/17 */ @RestController @RequestMapping("/consumer") @Slf4j public class OrderFeignController { @Resource private PaymentFeignService paymentFeignService; @GetMapping(value = "/pay/listAll}") public JSONResultUtil<List<Map<String, Object>>> listAll() { return paymentFeignService.listAll(); } @GetMapping("/pay/getServerPort") JSONResultUtil<String> getServerPort() { return paymentFeignService.getServerPort(); } }
3.OpenFeign超时控制
消费者调用提供者,存在超时现场。比如服务提供者Payment服务中使用线程休眠模拟处理请求耗时3秒钟,
@GetMapping("/getServerPort") public JSONResultUtil<String> getServerPort() { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } return JSONResultUtil.successWithData(serverPort); }
我们在上面用Feign调用时,报错如下:
2020-10-17 15:12:32.519 ERROR 8356 --- [p-nio-80-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.RetryableException: connect timed out executing GET http://CLOUD-PAYMENT-SERVICE/pay/getServerPort] with root cause java.net.SocketTimeoutException: connect timed out
解决办法:增加客户端超时时间(修改yml,由于feign是封装了ribbon,所以是设置ribbon相关设置)
server: port: 80 spring: application: name: cloud-order-service eureka: client: register-with-eureka: true service-url: #单机版 defaultZone: http://localhost:7001/eureka/ #设置feign客户端超时时间(OpenFeign默认支持ribbon) ribbon: #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间 ReadTimeout: 5000 #指的是建立连接后从服务器读取到可用资源所用的时间 ConnectTimeout: 5000
4.OpenFeign日志增强
openFeign增加日志打印功能。打印请求以及相应的相关信息,方便联调。可以通过设置日志级别,来了解Feign Http中的请求细节。也就是对Feign接口的调用情况进行监控和输出。
相关日志级别有:
NONE:默认的,不显示任何日志
BASIC:仅记录请求方法、URL、 响应状态码及执行时间
HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据
配置方式:
(1)增加配置类
package cn.qz.cloud.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Author: qlq * @Description * @Date: 15:45 2020/10/17 */ @Configuration public class FeignConfig { @Bean Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } }
(2)yml配置日志级别:
logging:
level:
# feign日志以什么级别监控哪个接口
cn.qz: debug
(3)访问查看日志:
2020-10-17 15:48:41.903 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] ---> GET http://CLOUD-PAYMENT-SERVICE/pay/getServerPort HTTP/1.1 2020-10-17 15:48:41.904 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] ---> END HTTP (0-byte body) 2020-10-17 15:48:41.943 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] <--- HTTP/1.1 200 (37ms) 2020-10-17 15:48:41.944 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] connection: keep-alive 2020-10-17 15:48:41.945 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] content-type: application/json 2020-10-17 15:48:41.945 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] date: Sat, 17 Oct 2020 07:48:41 GMT 2020-10-17 15:48:41.946 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] keep-alive: timeout=60 2020-10-17 15:48:41.946 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] transfer-encoding: chunked 2020-10-17 15:48:41.947 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] 2020-10-17 15:48:41.948 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] {"success":true,"code":"200","msg":"","data":"8081"} 2020-10-17 15:48:41.948 DEBUG 10024 --- [p-nio-80-exec-2] cn.qz.cloud.service.PaymentFeignService : [PaymentFeignService#getServerPort] <--- END HTTP (52-byte body)