Feign简介
Feign是Netflix开发的声明式、模板化的HTTP客户端:
- Feign可帮助我们更加便捷、优雅地调用HTTP API;
- 在Spring Cloud中,使用Feign非常简单-----创建一个接口,并在类上添加一些注解,代码就完成了;
- Feign支持多种注解,例如Feign自带的注解或者JAX-RS注解等;
- Spring Cloud对Feign进行了增强,使Feign支持了Spring MVC注解,并整合了 Spring Cloud Ribbon 与 Spring Cloud Hystrix,从而让Feign的使用更加方便;
使用Feign
1:新建Spring Boot项目EurekaServer,添加依赖Eureka Server
2:启动类
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3:配置文件application.yml
server:
port: 8761
eureka:
client:
registerWithEureka: false #是否将自己注册到Eureka Server,默认为True。由于当前应用就是Eureka Server,故false
fetchRegistry: false #是否从Eureka Server获取注册信息,默认True。因为这是一个单节点的Eureka Server,不需要同步其他的Eureka Server节点,故false
serviceUrl:
defaultZone: http://localhost:8761/eureka/
4:新建Spring Boot项目EurekaClient,添加依赖Eureka Client、Web
5:启动类
@EnableDiscoveryClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
6:MyController- - -测试Feign调用会使用到
@RestController
public class MyController {
@RequestMapping("/eureka/test")
public String test()
{
return "Hello Feign";
}
}
7:配置文件application.yml
server:
port: 8020
spring:
application:
name: EurekaClinet
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
8:新建Spring Boot项目CloudFeignDemo,添加依赖Eureka Client、Web、OpenFeign
9:启动类
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class ServerFeignApplication {
public static void main(String[] args) {
SpringApplication.run(ServerFeignApplication.class, args);
}
}
10:配置文件application.yml
server:
port: 8040
spring:
application:
name: FeignClinet
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
11:Feign接口
//指定需要调用的微服务名称
@FeignClient(name = "EurekaClinet")
public interface UserFeignClient { //不需要实现
@RequestMapping(value = "/eureka/test", method = RequestMethod.GET)
public String test();
}
12:MyController
@RestController
public class MyController{
@Autowired
private UserFeignClient userFeignClient;
@GetMapping("/testFeign")
public String testFeign() {
return this.userFeignClient.test(); //会去访问EurekaClinet的/eureka/test,前面已经定义过了
}
private static final Logger LOGGER = LoggerFactory.getLogger(MyController.class);
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/log-user-instance")
public void logUserInstance() {
ServiceInstance serviceInstance = this.loadBalancerClient.choose("EurekaClinet");
// 打印当前选择的是哪个节点
MyController.LOGGER.info("{}:{}:{}", serviceInstance.getServiceId(), serviceInstance.getHost(), serviceInstance.getPort());
}
}
12:项目结构
13:运行测试
启动项目EurekaServer
启动项目EurekaClient(以8020端口启动)
启动项目EurekaClient(以8030端口启动)
启动项目CloudFeignDemo
访问:http://localhost:8040/testFeign
访问:http://localhost:8040/log-user-instance,控制台打印信息:
可以看到,不但实现了声明式REST API调用,同时还实现了客户端的负载均衡!
自定义Feign配置
在Spring Cloud中Feign默认使用的契约是SpringMvcContract,因此它可以使用Spring MVC的注解。下面来自定义Feign的配置,让它使用Feign自带的注解进行工作:
1:项目ServerFeign添加Feign的配置类
@Configuration
public class FeignConfiguration {
/**
* 将契约改为feign原生的默认契约。这样就可以使用feign自带的注解了。
* @return 默认的feign契约
*/
@Bean
public Contract feignContract() {
return new feign.Contract.Default();
}
}
2:修改Feign接口
@FeignClient(name = "EurekaClinet",configuration=FeignConfiguration.class)
public interface UserFeignClient {
@RequestLine("GET /eureka/test")
public String test();
}
3:项目结构
4:运行测试
结果与上面的一致!
Feign对压缩的支持
一些场景下,可能需要对请求或响应进行压缩,此时可使用以下属性启用Feign的压缩功能:
feign:
compression:
request:
enabled: true # 开启请求压缩
response:
enabled: true # 开启响应压缩
对于请求的压缩,Feign还提供了更为详细的设置:
feign:
compression:
request:
enabled: true # 开启请求压缩
mime-types: text/html,application/xml,application/json # 设置压缩的数据类型,默认它们三
min-request-size: 2048 # 设置触发压缩的大小下限,默认2048
该特性在Spring Cloud Camden SR4不生效。不知道以后会不会解决!
Feign的日志
Feign对日志的处理非常灵活,可为每个Feign客户端指定日志记录策略,每个Feign客户端都会创建一个logger。默认情况下,logger的名称是Feign接口的完整类名。需要注意的是,Feign的日志打印只会对DEBUG级别做出响应。
我们可为每个Feign客户端配置各自的Logging .Level对象,告诉Feign记录哪些日志。Logging .Level的值有以下选择:
1:修改ServerFeign配置类
@Configuration
public class FeignConfiguration {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL; //将日志级别设置为FULL
}
}
2:在Feign接口,指定配置类
@FeignClient(name = "EurekaClinet",configuration=FeignConfiguration.class)
public interface UserFeignClient {
@RequestMapping(value = "/eureka/test", method = RequestMethod.GET)
public String test();
}
3:在application.yml添加
logging:
level:
com.example.demo.UserFeignClient: DEBUG
# 将Feign接口的日志级别设置成DEBUG,全包名+类名
4:运行测试
启动项目EurekaServer
启动项目EurekaClient
启动项目CloudFeignDemo
访问:http://localhost:8040/testFeign,查看CloudFeignDemo项目控制台日志输出!
将日志Feign的日志级别改为BASIC,重启项目访问:http://localhost:8011/testFeign
此时,只打印了请求方法、请求的URL和相应的状态码和响应的时间!
当然也可以全使用配置的方式,application.yml添加:
logging:
level:
com.example.demo.UserFeignClient: DEBUG # 将Feign接口的日志级别设置成DEBUG
feign:
client:
config:
EurekaClinet:
loggerLevel: FULL
使用Feign构造多参数请求
GET请求多参数的URL:
假设请求http://localhost:8040/testFeign/get?id=5&username=yangdong
Feign接口
@FeignClient(name = "EurekaClinet",configuration=FeignConfiguration.class)
public interface UserFeignClient {
@RequestMapping(value = "/eureka/test", method = RequestMethod.GET)
public String testGet(@RequestParam("id") Long id,@RequestParam("username") String username);
}
URL有几个参数,Feign接口中的方法就有几个参数!!!
CloudFeignDemo控制器MyController
@GetMapping("/testFeign/get")
public String testGet(@RequestParam("id") Long id,@RequestParam("username") String username) {
return this.userFeignClient.testGet(id, username);
}
EurekaClient控制器MyController
@RestController
public class MyController {
@RequestMapping("/eureka/test")
public String test(@RequestParam("id") Long id,@RequestParam("username") String username)
{
return "Hello"+id+username;
}
}
运行测试
访问:http://localhost:8040/testFeign/get?id=5&username=yangdong
访问流程为:CloudFeignDemo控制器MyController的testGet方法,获取到id参数与username参数。执行Feign接口的testGet方法,去访问EurekaClient控制器MyController的test方法。
常见问题总结
1:项目启动报错
Caused by: java.lang.IllegalStateException: Failed to introspect annotated methods on class org.springframework.cloud.openfeign.FeignClientsConfiguration
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:162) ~[spring-core-5.3.3.jar:5.3.3]
at org.springframework.context.annotation.ConfigurationClassParser.retrieveBeanMethodMetadata(ConfigurationClassParser.java:403) ~[spring-context-5.3.3.jar:5.3.3]
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:326) ~[spring-context-5.3.3.jar:5.3.3]
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250) ~[spring-context-5.3.3.jar:5.3.3]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:207) ~[spring-context-5.3.3.jar:5.3.3]
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:175) ~[spring-context-5.3.3.jar:5.3.3]
... 44 common frames omitted
Caused by: java.lang.IllegalStateException: Failed to introspect Class [org.springframework.cloud.openfeign.FeignClientsConfiguration] from ClassLoader [sun.misc.Launcher$AppClassLoader@18b4aac2]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:481) ~[spring-core-5.3.3.jar:5.3.3]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:455) ~[spring-core-5.3.3.jar:5.3.3]
at org.springframework.core.type.StandardAnnotationMetadata.getAnnotatedMethods(StandardAnnotationMetadata.java:151) ~[spring-core-5.3.3.jar:5.3.3]
... 49 common frames omitted
Caused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/Module
at java.lang.Class.getDeclaredMethods0(Native Method) ~[na:1.8.0_221]
at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) ~[na:1.8.0_221]
at java.lang.Class.getDeclaredMethods(Class.java:1975) ~[na:1.8.0_221]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:463) ~[spring-core-5.3.3.jar:5.3.3]
... 51 common frames omitted
如果项目启动报上述错误的话,则说明项目没有引入Eureka Client依赖!
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
pom.xml引入重启即可!
2:项目启动后就自动关闭
2021-01-20 15:11:56.281 INFO 5020 --- [ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application FEIGNCLINET with eureka with status UP
2021-01-20 15:11:56.282 INFO 5020 --- [ main] com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1611126716282, current=UP, previous=STARTING]
2021-01-20 15:11:56.285 INFO 5020 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_FEIGNCLINET/DESKTOP-MC17MVP:FeignClinet:8040: registering service...
2021-01-20 15:11:56.305 INFO 5020 --- [ main] c.e.demo.CloudFeignDemoApplication : Started CloudFeignDemoApplication in 7.665 seconds (JVM running for 9.408)
2021-01-20 15:11:56.319 INFO 5020 --- [extShutdownHook] o.s.c.n.e.s.EurekaServiceRegistry : Unregistering application FEIGNCLINET with eureka with status DOWN
2021-01-20 15:11:56.319 INFO 5020 --- [extShutdownHook] com.netflix.discovery.DiscoveryClient : Saw local status change event StatusChangeEvent [timestamp=1611126716319, current=DOWN, previous=UP]
2021-01-20 15:11:56.329 INFO 5020 --- [extShutdownHook] com.netflix.discovery.DiscoveryClient : Shutting down DiscoveryClient ...
2021-01-20 15:11:56.377 INFO 5020 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_FEIGNCLINET/DESKTOP-MC17MVP:FeignClinet:8040 - registration status: 204
2021-01-20 15:11:56.377 INFO 5020 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_FEIGNCLINET/DESKTOP-MC17MVP:FeignClinet:8040: registering service...
2021-01-20 15:11:56.390 INFO 5020 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_FEIGNCLINET/DESKTOP-MC17MVP:FeignClinet:8040 - registration status: 204
2021-01-20 15:11:56.393 INFO 5020 --- [extShutdownHook] com.netflix.discovery.DiscoveryClient : Unregistering ...
2021-01-20 15:11:56.396 INFO 5020 --- [extShutdownHook] com.netflix.discovery.DiscoveryClient : DiscoveryClient_FEIGNCLINET/DESKTOP-MC17MVP:FeignClinet:8040 - deregister status: 200
2021-01-20 15:11:56.406 INFO 5020 --- [extShutdownHook] com.netflix.discovery.DiscoveryClient : Completed shut down of DiscoveryClient
Process finished with exit code 0
如果项目启动如上所述的话,则说明项目没有引入Web依赖!
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
pom.xml引入重启即可!
参考书籍:Spring Cloud与Docker微服务架构实战
以上只是学习所做的笔记,以供日后参考!!!