前面在学习微服务间调用时都是使用RestTemplate,本章将学习Feign进行服务之间通信
代码地址:https://gitee.com/webprogram/springcloud_learn
FeignClient简化了请求的编写,且通过动态负载进行选择要使用哪个服务进行消费,而这一切都由Spring动态配置实现,我们不用关心这些,只管使用方法即可。再说,就是简化了编写,RestTemplate还需要写上服务器IP这些信息等等,而FeignClient则不用
Fegin远程调用
Feign使用
Feign是一个声明式的http客户端,其作用就是帮助我们优雅的实现http请求的发送
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
开启Feign功能
//在启动类添加@EnableFegin
@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication{
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class);
}
编写Feign客户端
// 定义Feign调用服务接口
@FeignClient("userservice")
public interface UserServiceFeign {
// url路径和userservice的接口路径对应
// 参数和userservice接口参数对应
@GetMapping("user/{id}")
UserEntity getUser(@PathVariable String id);
}
主要基于SpringMVC的注解来声明远程调用信息:
- 服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:String id
- 返回值类型:UserEntity
具体参数值,根据自身情况进行修改
controller利用feign调用userservice服务
@RestController
@RequestMapping("order")
public class OrderController {
//注入定义的feign接口
@Autowired
private UserServiceFeign userServiceFeign;
@RequestMapping("/{id}")
public OrderEntity getOrder(@PathVariable String id){
OrderEntity order = queryOrder(id);
// 通过Feign调用userservice获取用户信息
UserEntity userEntity = userServiceFeign.getUser(id);
order.setUser(userEntity);
return order;
}
}
访问:ip:port/order/001 正确得到响应结果,成功查询到了userservice中的数据
自定义配置
Feign运行自定义配置来覆盖默认配置,可修该配置如下:
类型 | 作用 | 说明 |
---|---|---|
feign.Logger.Level | 修改日志级别 | 包含四种:NONE、BASIC、HEADERS、FULL |
feign.codec.Decoder | 响应结果的解析器 | http远程调用的结果做解析,例如解析json字符串为java对象 |
feign.codec.Encode | 请求参数编码 | 设置http请求参数编码 |
feign.Contract | 支持的注解格式 | 默认是SpringMVC的注解 |
feign.Retryer | 失败重试机制 | 请求失败的重试机制,默认是没有,可使用Ribbon的重试 |
一般我们只配置日志级别
修改日志级别
方式一:配置文件方式
- 全局生效
feign:
client:
config:
default: # 此处为default则表示全局配置,若为某服务名则特指访问该服务时的配置
loggerLevel: FULL # 日志级别
- 局部生效
feign:
client:
config:
userservice: # 此处为default则表示全局配置,若为某服务名则特指访问该服务时的配置
loggerLevel: FULL # 日志级别
方式二:java代码方式,声明Bean
@Configuration
public class FeignClientConfiguration {
@Bean
public Logger.Level feignLogLevel(){
// 配置日志级别
return Logger.Level.FULL;
}
}
- 全局配置
/**
全局配置日志级别,开启feign注解(@EnableFeignClients)中添加属性defaultConfiguration
/*
@EnableFeignClients(defaultConfiguration = {FeignClientConfiguration.class})
@SpringBootApplication
public class OrderServiceApplication{
}
- 局部配置
/**
在特定服务中配置日志级别,在@FeignClient添加属性configuration = FeignClientConfiguration.class
*/
@FeignClient(value = "userservice",configuration = {FeignClientConfiguration.class})
public interface UserServiceFeign {
@RequestMapping("user/{id}")
UserEntity getUser(@PathVariable String id);
}
注: 本人在日志配置过程中,控制台并未按预期打印出日志
原因:logback日志级别未配置,解决方案参考博文:https://blog.csdn.net/qq_38668544/article/details/120268110
application.yml中添加如下
logging:
level:
com:
cloud: debug #com.cloud为包路径
配置后,FULL级别日志成功打印,如下图:
Feign性能优化
Feign底层客户端的实现:
- URLConnection:默认实现,不支持连接池
连接的创建和销毁会消耗资源
- Apache HTTPClient:支持连接池
- OKHttp:支持连接池
因此,feign的优化主要包括:
- 使用连接池代替默认的URLConnection
- 日志级别,最好用basic或者none
连接池配置
采用HTTPClient
引入依赖
<!-- feign连接池依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
application.yml配置相关支持
# feign的配置
feign:
httpclient:
enabled: true # 开启feign对HTTPClient的支持
max-connections: 200 # 最大连接数
max-connections-per-route: 50 # 每个路径的最大连接数
最佳实践
方式一(继承):给消费者的FeignClient和提供者的Controller定义同意的父接口作为标准
由于Feign客户端定义调用接口和Controller的方法,代码相似度很高,故可将这部分提取出一个父接口来作为标准
缺点:紧耦合
方法二(抽取):将FeignClient抽取为独立模块(feign-api),并把接口有关的POJO、默认的Feign配置放在该模块中,提供给消费者使用
缺点:引入feign-api相当于引入整个微服务项目所有相关的feign配置
注:该方式引入feign-api,相关组件并不会被扫描添加到IOC容器中,原因是springboot默认扫包范围为当前包及其子包
解决方案:
@EnableFeignClients(basePackages = "XXXXXX")
// XXXXX 为feign-api中feignClient所在包路径
或者
@EnableFeignClients(clients = UserServiceFeign.class)
// 指定某个feignClient
可根据实际项做选择