一、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
// 需要设置Feign客户端的日志级别为DEBUG
@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
// 使用@SpringQueryMap
@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是微服务间通信的核心组件,掌握其使用是微服务开发的基础。