一、OpenFeign简介
OpenFeign是Spring Cloud的声明式HTTP客户端,简化了微服务间的HTTP调用。
特点:
- 声明式调用,面向接口编程
- 集成Ribbon实现负载均衡
- 集成Sentinel实现服务熔断
- 支持Spring MVC注解
二、基本使用
添加依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
|
启用Feign
1 2 3 4 5 6 7
| @EnableFeignClients @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
|
定义Feign客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @FeignClient(name = "user-service") public interface UserClient {
@GetMapping("/user/{id}") User getUserById(@PathVariable("id") Long id);
@PostMapping("/user") User createUser(@RequestBody User user);
@PutMapping("/user") User updateUser(@RequestBody User user);
@DeleteMapping("/user/{id}") void deleteUser(@PathVariable("id") Long id); }
|
使用Feign客户端
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Service public class OrderService {
@Autowired private UserClient userClient;
public Order getOrderDetail(Long orderId) { Order order = orderMapper.selectById(orderId); User user = userClient.getUserById(order.getUserId()); order.setUserName(user.getName()); return order; } }
|
三、配置
超时配置
1 2 3 4 5 6 7 8 9
| feign: client: config: default: connectTimeout: 5000 readTimeout: 10000 user-service: connectTimeout: 3000 readTimeout: 5000
|
日志配置
日志级别:
- NONE:无日志(默认)
- BASIC:请求方法、URL、响应状态码、执行时间
- HEADERS:BASIC + 请求和响应头
- FULL:HEADERS + 请求和响应体
1 2 3 4 5
| feign: client: config: user-service: loggerLevel: FULL
|
1 2 3 4 5 6 7 8
| @Configuration public class FeignLogConfig { @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } }
|
1 2 3
| logging: level: com.example.feign.UserClient: DEBUG
|
拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Component public class FeignAuthInterceptor implements RequestInterceptor {
@Override public void apply(RequestTemplate template) { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes != null) { HttpServletRequest request = attributes.getRequest(); String token = request.getHeader("Authorization"); if (token != null) { template.header("Authorization", token); } } } }
|
1 2 3 4 5 6
| feign: client: config: default: requestInterceptors: - com.example.interceptor.FeignAuthInterceptor
|
四、熔断降级
添加Sentinel依赖
1 2 3 4
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
|
启用Sentinel
1 2 3
| feign: sentinel: enabled: true
|
定义降级类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @FeignClient(name = "user-service", fallback = UserClientFallback.class) public interface UserClient {
@GetMapping("/user/{id}") User getUserById(@PathVariable("id") Long id); }
@Component public class UserClientFallback implements UserClient {
@Override public User getUserById(Long id) { User user = new User(); user.setId(id); user.setName("默认用户"); return user; } }
|
定义降级工厂
可获取异常信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @FeignClient(name = "user-service", fallbackFactory = UserClientFallbackFactory.class) public interface UserClient {
@GetMapping("/user/{id}") User getUserById(@PathVariable("id") Long id); }
@Component public class UserClientFallbackFactory implements FallbackFactory<UserClient> {
@Override public UserClient create(Throwable cause) { return new UserClient() { @Override public User getUserById(Long id) { log.error("调用用户服务失败: {}", cause.getMessage()); User user = new User(); user.setId(id); user.setName("降级用户"); return user; } }; } }
|
五、请求压缩
1 2 3 4 5 6 7 8
| feign: compression: request: enabled: true mime-types: text/xml, application/xml, application/json min-request-size: 2048 response: enabled: true
|
六、文件上传
添加依赖
1 2 3 4
| <dependency> <groupId>io.github.openfeign.form</groupId> <artifactId>feign-form-spring</artifactId> </dependency>
|
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Configuration public class FeignMultipartConfig {
@Bean @Primary @Scope("prototype") public Encoder multipartFormEncoder() { return new SpringFormEncoder(new SpringEncoder(new ObjectFactory<HttpMessageConverters>() { @Override public HttpMessageConverters getObject() { return new HttpMessageConverters(new RestTemplate().getMessageConverters()); } })); } }
|
使用
1 2 3 4 5 6
| @FeignClient(name = "file-service", configuration = FeignMultipartConfig.class) public interface FileClient {
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) String upload(@RequestPart("file") MultipartFile file); }
|
七、GET请求传递对象
1 2 3
| @GetMapping("/user/list") List<User> listUsers(@SpringQueryMap UserQuery query);
|
八、Feign性能优化
使用HttpClient替换默认客户端
1 2 3 4
| <dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId> </dependency>
|
1 2 3 4 5
| feign: httpclient: enabled: true max-connections: 200 max-connections-per-route: 50
|
连接池配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration public class HttpClientConfig {
@Bean public HttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(5000) .setSocketTimeout(10000) .build(); return HttpClientBuilder.create() .setMaxConnTotal(200) .setMaxConnPerRoute(50) .setDefaultRequestConfig(requestConfig) .build(); } }
|
九、总结
OpenFeign要点:
- 声明式调用,简化HTTP通信
- 合理配置超时和日志
- 集成Sentinel实现熔断降级
- 使用连接池提升性能
- 拦截器统一处理请求头
OpenFeign是微服务间通信的核心组件,掌握其使用是微服务开发的基础。