针对Springboot3.0.x,对应的spring-cloud-openfeign的版本为4.0.x,如果你使用spring-cloud-dependencies来管理依赖,需要使用2022.0.x
<spring-cloud-dependencies.version>2022.0.1spring-cloud-dependencies.version>
<spring-cloud-openfeign.version>4.0.1spring-cloud-openfeign.version>
修改pom之后,启动application,不出意外的出现了新的error
Caused by: java.lang.IllegalStateException: No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?
at org.springframework.cloud.openfeign.FeignClientFactoryBean.loadBalance(FeignClientFactoryBean.java:401) ~[spring-cloud-openfeign-core-4.0.1.jar:4.0.1]
at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:446) ~[spring-cloud-openfeign-core-4.0.1.jar:4.0.1]
at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:421) ~[spring-cloud-openfeign-core-4.0.1.jar:4.0.1]
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:148) ~[spring-beans-6.0.6.jar:6.0.6]
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:90) ~[spring-beans-6.0.6.jar:6.0.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1823) ~[spring-beans-6.0.6.jar:6.0.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.getObjectForBeanInstance(AbstractAutowireCapableBeanFactory
查了一下,网上给出的方案是增加一个新的依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-loadbalancerartifactId>
dependency>
加上之后application确实可以顺利启动,但是对我们来说还是存在疑问,首先我们没有使用loadbalance,我们的Url要么是配置文件里指定的虚地址,需要就是作为参数传给Feign的,为什么现在却需要引入loadbalancer?之前我们默认的HttpClient是ApacheClient,现在换成loadbalancer会不会有潜在的风险?
首先根据根据异常栈,我们定位到出错的代码FeignClientFactoryBean#getTarget
<T> T getTarget() {
FeignClientFactory feignClientFactory = beanFactory != null ? beanFactory.getBean(FeignClientFactory.class)
: applicationContext.getBean(FeignClientFactory.class);
Feign.Builder builder = feign(feignClientFactory);
if (!StringUtils.hasText(url) && !isUrlAvailableInConfig(contextId)) {
if (LOG.isInfoEnabled()) {
LOG.info("For '" + name + "' URL not provided. Will try picking an instance via load-balancing.");
}
if (!name.startsWith("http")) {
url = "http://" + name;
}
else {
url = name;
}
url += cleanPath();
return (T) loadBalance(builder, feignClientFactory, new HardCodedTarget<>(type, name, url));
}
if (StringUtils.hasText(url) && !url.startsWith("http")) {
url = "http://" + url;
}
//、、、
}
getTarget方法就是为我们生成一个FeignClient实例,根据第一个if
语句,如果我们没有为Feign配置Url,就会进入loadBalance方法
protected <T> T loadBalance(Feign.Builder builder, FeignClientFactory context, HardCodedTarget<T> target) {
Client client = getOptional(context, Client.class);
if (client != null) {
builder.client(client);
applyBuildCustomizers(context, builder);
Targeter targeter = get(context, Targeter.class);
return targeter.target(this, builder, context, target);
}
throw new IllegalStateException(
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
}
根据loadBalance的代码,如果在getOptional为空,就会抛出我们上面见到的异常。
我们将版本回退到Springboot2.7,通过debug发现在这里我们会拿到一个ApacheHttpClient,在3.0却拿到了一个null,难道是需要修改什么配置吗?我们找到openFeign的配置文件,发现没有什么变化,
{
"name": "spring.cloud.openfeign.httpclient.enabled",
"type": "java.lang.Boolean",
"description": "Enables the use of the Apache HTTP Client by Feign.",
"defaultValue": "true"
}
最后我们在官网找到了答案:
https://docs.spring.io/spring-cloud-openfeign/docs/current/reference/html/
Starting with Spring Cloud OpenFeign 4, the Feign Apache HttpClient 4 is no longer supported. We suggest using Apache HttpClient 5 instead.
难怪我们这里一直取不到,OpenFeign不再支持Apache HttpClient 4,需要我们使用Apache HttpClient 5来进行替代
<dependency>
<groupId>io.github.openfeigngroupId>
<artifactId>feign-hc5artifactId>
dependency>
然后我们移除了loadbalance的依赖,启动application,报错也没有再次出现。
Apache HttpClient 4 到5并不是能够完全替代的,默认情况下5不再支持TLSv1.1,如果你们还在用1.1需要进行如下配置
@Configuration
public class HT5Configuration {
@Bean
public PoolingHttpClientConnectionManager customeHttpClient () {
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
SSLContexts.createDefault(),
new String[] { "TLSv1.1"}, null, null);
//
// PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
// cm.
return PoolingHttpClientConnectionManagerBuilder.create().setSSLSocketFactory(sslsf).build();
}