Skip to content

Commit ed85cc0

Browse files
committed
feat: feign 集成 apache httpclient
1 parent 4d84efc commit ed85cc0

2 files changed

Lines changed: 93 additions & 50 deletions

File tree

  • framework

framework/framework_api/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
<groupId>org.springframework.cloud</groupId>
2323
<artifactId>spring-cloud-starter-openfeign</artifactId>
2424
</dependency>
25+
<dependency>
26+
<groupId>io.github.openfeign</groupId>
27+
<artifactId>feign-httpclient</artifactId>
28+
</dependency>
2529
<dependency>
2630
<groupId>org.springframework.cloud</groupId>
2731
<artifactId>spring-cloud-starter-loadbalancer</artifactId>

framework/framework_base/src/main/java/com/github/cadecode/uniboot/framework/base/config/FeignConfig.java

Lines changed: 89 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import com.fasterxml.jackson.databind.ObjectMapper;
99
import com.github.cadecode.uniboot.common.core.util.JacksonUtil;
1010
import com.github.cadecode.uniboot.framework.api.consts.HttpConst;
11+
import com.github.cadecode.uniboot.framework.base.config.FeignConfig.ApacheHttpFeignConfig;
12+
import com.github.cadecode.uniboot.framework.base.config.FeignConfig.DefaultFeignConfig;
1113
import com.github.cadecode.uniboot.framework.base.feign.FeignClientDecorator;
1214
import com.github.cadecode.uniboot.framework.base.feign.FeignErrorDecoder;
1315
import com.github.cadecode.uniboot.framework.base.security.model.SysUserDetails;
@@ -17,8 +19,10 @@
1719
import feign.Client.Default;
1820
import feign.Logger.Level;
1921
import feign.RequestInterceptor;
20-
import feign.RequestTemplate;
2122
import feign.codec.ErrorDecoder;
23+
import feign.httpclient.ApacheHttpClient;
24+
import lombok.extern.slf4j.Slf4j;
25+
import org.apache.http.client.HttpClient;
2226
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2327
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
2428
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
@@ -28,6 +32,7 @@
2832
import org.springframework.cloud.openfeign.loadbalancer.RetryableFeignBlockingLoadBalancerClient;
2933
import org.springframework.context.annotation.Bean;
3034
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.context.annotation.Import;
3136

3237
import javax.servlet.http.HttpServletRequest;
3338

@@ -37,63 +42,44 @@
3742
* @author Cade Li
3843
* @since 2023/7/29
3944
*/
45+
@Slf4j
4046
@Configuration
47+
@Import({ApacheHttpFeignConfig.class, DefaultFeignConfig.class})
4148
public class FeignConfig {
4249

4350
/**
4451
* feign 拦截器
4552
*/
4653
@Bean
4754
public RequestInterceptor requestInterceptor() {
48-
return this::configRequestTemplate;
49-
}
50-
51-
protected void configRequestTemplate(RequestTemplate requestTemplate) {
52-
// 设置内部请求来源标识
53-
requestTemplate.header(HttpConst.HEAD_SOURCE, HttpConst.HEAD_SOURCE_VALUE);
54-
HttpServletRequest servletRequest = RequestUtil.getRequest();
55-
if (ObjUtil.isNull(servletRequest)) {
56-
return;
57-
}
58-
// 配置客户端 IP
59-
requestTemplate.header(HttpConst.HEAD_FORWARDED_FOR, ServletUtil.getClientIP(servletRequest));
60-
// 传递 trace id
61-
String traceId = ServletUtil.getHeader(servletRequest, HttpConst.HEAD_TRACE_ID, CharsetUtil.CHARSET_UTF_8);
62-
if (ObjUtil.isNotEmpty(traceId)) {
63-
requestTemplate.header(HttpConst.HEAD_TRACE_ID, traceId);
64-
}
65-
// 传递用户 token
66-
String token = SecurityUtil.getTokenFromRequest(servletRequest);
67-
if (StrUtil.isNotEmpty(token)) {
68-
requestTemplate.header(HttpConst.HEAD_TOKEN, token);
69-
}
70-
// 传递用户信息
71-
String userDetailsJson = ServletUtil.getHeader(servletRequest, HttpConst.HEAD_USER_DETAILS, CharsetUtil.CHARSET_UTF_8);
72-
// 不存在则生成
73-
if (StrUtil.isEmpty(userDetailsJson)) {
74-
SysUserDetails userDetailsDto = SecurityUtil.getUserDetails(null);
75-
userDetailsJson = JacksonUtil.toJson(userDetailsDto);
76-
}
77-
requestTemplate.header(HttpConst.HEAD_USER_DETAILS, EscapeUtil.escape(userDetailsJson));
78-
}
79-
80-
// 覆盖 feign 配置
81-
// org.springframework.cloud.openfeign.loadbalancer.DefaultFeignLoadBalancerConfiguration
82-
83-
@Bean
84-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "false")
85-
public Client feignClient(LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties,
86-
LoadBalancerClientFactory loadBalancerClientFactory) {
87-
FeignClientDecorator decorator = new FeignClientDecorator(new Default(null, null));
88-
return new FeignBlockingLoadBalancerClient(decorator, loadBalancerClient, properties, loadBalancerClientFactory);
89-
}
90-
91-
@Bean
92-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true", matchIfMissing = true)
93-
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, LoadBalancedRetryFactory loadBalancedRetryFactory,
94-
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
95-
FeignClientDecorator decorator = new FeignClientDecorator(new Default(null, null));
96-
return new RetryableFeignBlockingLoadBalancerClient(decorator, loadBalancerClient, loadBalancedRetryFactory, properties, loadBalancerClientFactory);
55+
return requestTemplate -> {
56+
// 设置内部请求来源标识
57+
requestTemplate.header(HttpConst.HEAD_SOURCE, HttpConst.HEAD_SOURCE_VALUE);
58+
HttpServletRequest servletRequest = RequestUtil.getRequest();
59+
if (ObjUtil.isNull(servletRequest)) {
60+
return;
61+
}
62+
// 配置客户端 IP
63+
requestTemplate.header(HttpConst.HEAD_FORWARDED_FOR, ServletUtil.getClientIP(servletRequest));
64+
// 传递 trace id
65+
String traceId = ServletUtil.getHeader(servletRequest, HttpConst.HEAD_TRACE_ID, CharsetUtil.CHARSET_UTF_8);
66+
if (ObjUtil.isNotEmpty(traceId)) {
67+
requestTemplate.header(HttpConst.HEAD_TRACE_ID, traceId);
68+
}
69+
// 传递用户 token
70+
String token = SecurityUtil.getTokenFromRequest(servletRequest);
71+
if (StrUtil.isNotEmpty(token)) {
72+
requestTemplate.header(HttpConst.HEAD_TOKEN, token);
73+
}
74+
// 传递用户信息
75+
String userDetailsJson = ServletUtil.getHeader(servletRequest, HttpConst.HEAD_USER_DETAILS, CharsetUtil.CHARSET_UTF_8);
76+
// 不存在则生成
77+
if (StrUtil.isEmpty(userDetailsJson)) {
78+
SysUserDetails userDetailsDto = SecurityUtil.getUserDetails(null);
79+
userDetailsJson = JacksonUtil.toJson(userDetailsDto);
80+
}
81+
requestTemplate.header(HttpConst.HEAD_USER_DETAILS, EscapeUtil.escape(userDetailsJson));
82+
};
9783
}
9884

9985
/**
@@ -111,4 +97,57 @@ public ErrorDecoder errorDecoder(ObjectMapper objectMapper) {
11197
public Level feignLogLevel() {
11298
return Level.BASIC;
11399
}
100+
101+
/**
102+
* 覆盖 feign 配置, {@link org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration}
103+
* 参照 DefaultFeignLoadBalancerConfiguration
104+
* 集成 FeignClientDecorator 装饰类
105+
*/
106+
@ConditionalOnProperty(value = "feign.httpclient.enabled", havingValue = "false")
107+
static class DefaultFeignConfig {
108+
@Bean
109+
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "false")
110+
public Client feignClient(LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties,
111+
LoadBalancerClientFactory loadBalancerClientFactory) {
112+
log.debug("Feign client use default");
113+
FeignClientDecorator decorator = new FeignClientDecorator(new Default(null, null));
114+
return new FeignBlockingLoadBalancerClient(decorator, loadBalancerClient, properties, loadBalancerClientFactory);
115+
}
116+
117+
@Bean
118+
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true", matchIfMissing = true)
119+
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, LoadBalancedRetryFactory loadBalancedRetryFactory,
120+
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
121+
log.debug("Feign client use default, is retryable");
122+
FeignClientDecorator decorator = new FeignClientDecorator(new Default(null, null));
123+
return new RetryableFeignBlockingLoadBalancerClient(decorator, loadBalancerClient, loadBalancedRetryFactory, properties, loadBalancerClientFactory);
124+
}
125+
}
126+
127+
/**
128+
* 覆盖 feign 配置, {@link org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration}
129+
* 参照 HttpClientFeignLoadBalancerConfiguration
130+
* 集成 FeignClientDecorator 装饰类,使用 ApacheHttpClient
131+
*/
132+
@ConditionalOnProperty(value = "feign.httpclient.enabled", havingValue = "true", matchIfMissing = true)
133+
static class ApacheHttpFeignConfig {
134+
@Bean
135+
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "false")
136+
public Client apacheFeignClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
137+
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
138+
log.debug("Feign client use apache httpclient");
139+
FeignClientDecorator decorator = new FeignClientDecorator(new ApacheHttpClient(httpClient));
140+
return new FeignBlockingLoadBalancerClient(decorator, loadBalancerClient, properties, loadBalancerClientFactory);
141+
}
142+
143+
@Bean
144+
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true", matchIfMissing = true)
145+
public Client apacheFeignRetryClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
146+
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
147+
LoadBalancerClientFactory loadBalancerClientFactory) {
148+
log.debug("Feign client use apache httpclient, is retryable");
149+
FeignClientDecorator decorator = new FeignClientDecorator(new ApacheHttpClient(httpClient));
150+
return new RetryableFeignBlockingLoadBalancerClient(decorator, loadBalancerClient, loadBalancedRetryFactory, properties, loadBalancerClientFactory);
151+
}
152+
}
114153
}

0 commit comments

Comments
 (0)