一、Feign替代RestTemplate
RestTemplate示例
String url = "http://userservice/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
RestTemplate的缺陷:
- 代码可读性差,编码体验不统一。
- 参数复杂URL难以维护
(一)Fegin的概念
Fegin是一个声明式的http客户端,官网:https://github.com/OpenFeign/feign
作用是 帮助简化实现http请求的发送。
(二)基本使用Fegin
1、引入依赖
<dependency>
<groupId>org.springframework.cloud</gourpId>
<artifactId>spring.cloud.starter.openfeign</artifactId>
</dependency>
2、启动类加注解启动Fegin
@EnableFeignClients //启动Fegin客户端
@MapperScan("xxx.xxx.xxx.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class,args);
}
}
3、定义Fegin客户端
@FeignClient("userservice")
public interface UserClient {
@GetMapping("/user/{id}")
User findById(@PathVariable("id") Long id);
}
主要是基于SpringMVC的注解来声明远程调用的信息,比如:
- 服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:Long id
- 返回值:User
3、Service使用Feign客户端
@Service
public class OrderService {
@Autowired
private OrderMapper mapper;
@Autowired
private UserClient userClient;
public Order queryOrderById(Long orderId) {
Order order = orderMapper.findById(orderId);
User user = userClient.getById(order.getUserId());
order.setUser(user);
return order;
}
}
二、自定义配置
可以修改的配置表
类型 | 作用 | 说明 |
---|---|---|
fegin.Logger.Level | 修改日志级别(常用) |
包含四种级别:NONE、BASIC、HEADERS、FULL
|
fegin.codec.Decoder | 响应结果解析器 |
http 远程调用的结果做分析,例如解析JSON
|
fegin.codec.Encoder | 请求参数编码格式 |
将请求参数编码,便于http请求发送
|
fegin.Contract | 支持的注解格式 |
默认是SpringMVC注解
|
fegin.Retryer | 失败重试机制 |
请求失败的重试机制,默认是没有,不过会使用Ribbon的重试
|
(一)修改配置的实现
方式一:配置文件方式
fegin:
client:
config:
default: #设置全局配置,若是直接写服务名称,则是针对某一个微服务的配置
loggerLevel: FULL #全日志级别
方式二:自定义Feign配置
FeignClientConfiguration 配置类
//配置注解选择一种即可
//全局配置:在启动类上加上该注解
//@EnableFeginClients(defaultConfiguration = FeignClientConfiguration.class)
//局部配置:具体Client类上加该注解
//@FeginClient(value = "userservice", configuration = FeignClientConfiguration.class)
public class FeignClientConfiguration {
@Bean
public Logger.Level.feignLogLevel(){
return Logger.Level.BASIC;
}
}
三、Feign优化
(一)Feign底层的客户端实现(三种模式)
- URLConnection: 默认实现,不支持连接池(JDK自带)
- Apache HttpClient: 支持连接池
- OKHttp: 支持连接池
(二)对Fegin进行优化的性能
- 1、使用连接池代替URLConnection
- 2、日志级别,最好配置为 BASIC 或 NONE
(三)配置连接池代替URLConnection
1、引入HttpClient依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
2、配置连接池
feign:
client:
config:
default: #全局配置
loggerLevel: BASIC #日志级别,打印最基本的请求和响应信息
httpclient:
enabled: true #开启feign对httpclient的支持
max-connections: 200 #最大连接数
max-connections-per-route: 50 #每个路径的最大连接数
四、实践总结的实现FeignClient的经验
(一)方式一(继承): 给消费者的FeignClient和提供者的controller定义统一的父接口作为标准。
1、实现:
父接口
public interface UserAPI{
@GetMapping("/user/{id})
User findById(@PathVariable("id") Long id);
}
UserClient继承父接口
@FeignClient(value = "userservice")
public interface UserClient extends UserAPI{
}
UserController实现父接口
@RestController
public class UserController implements UserAPI {
public User findById(@PathVariable("id") Long id) {
//...实现业务
}
}
2、这种方式的缺陷
- 服务紧耦合
- 父接口参数列表映射不会被继承
(二)方式二(抽取): 将FeignClient抽取为独立模块,并且把接口有关的POJO、默认Feign配置都放置在这个模块中给所有消费者使用。
1、实现
(1)模块创建
创建一个module,命名为feign-api,引入feign的starter依赖
(2)模块功能类抽取
将需要抽取的类放到feign-api模块中。
(3)使用feign-api
消费者服务引入feign-api依赖
(4)消费者服务 import feign-api
2、可能会出现的问题
UserClient 没有被Spring 创建 Bean 对象
-
原因: FeginClient脱离了消费者服务包扫描的范围,导致有引入没实例的情况。
-
解决方案:
方式一:指定FeignClient坐在包
@EnableFeignClients(basePackages = "xxx.xxx.xxx.clients")
方式二:指定FeignClient字节码
@EnableFeignClients(clients= {UserClient.class})