Reprinted from: https://segmentfault.com/a/1190000010486459
I don't know from which version ( 目测跟版本无关,应该是ribbon.eureka.enabled=true的情况下
), since configuring the physical server list for the ribbon, simply configuring xxx.ribbon.listOfServers will not work, so I started the journey of burying the pit.
Currently using Camden.SR6 version
abnormal
java.lang.IllegalStateException: No instances available for xxx
at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.execute(RibbonLoadBalancerClient.java:75)
at org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor.intercept(LoadBalancerInterceptor.java:53)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86)
at org.springframework.cloud.netflix.metrics.MetricsClientHttpRequestInterceptor.intercept(MetricsClientHttpRequestInterceptor.java:68)
at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:86)
at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:70)
at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:407)
at org.springframework.web.client.RestTemplate$$FastClassBySpringCGLIB$$aa4e9ed0.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:721)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
at org.springframework.cloud.netflix.metrics.RestTemplateUrlTemplateCapturingAspect.captureUrlTemplate(RestTemplateUrlTemplateCapturingAspect.java:33)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:629)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:618)
at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:70)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:656)
at org.springframework.web.client.RestTemplate$$EnhancerBySpringCGLIB$$ec1f757d.postForEntity(<generated>)
Recurrence condition
- RibbonClient is used to specify the server list
- Instead of specifying @RibbonClients, place the annotation RibbonClient configuration class under the @ComponentScan scan package
- There are multiple services specified like this
- And when one of the services uses load balanced restTemplate, the service name in the url is wrong, and it is written as a non-existing service name (
当时是把外部的服务名改了导致的
)
Causes the wrong server list to be used.
problem configuration
@RibbonClient(name = "xxx",configuration = XxxRibbonConfig.class)
public class XxxRibbonConfig {
String listOfServers = "http://192.168.99.100:8080,http://192.168.99.101:8080";
@Bean
public ServerList<Server> ribbonServerList() {
List<Server> list = Lists.newArrayList();
if (!Strings.isNullOrEmpty(listOfServers)) {
for (String s: listOfServers.split(",")) {
list.add(new Server(s.trim()));
}
}
return new StaticServerList<Server>(list);
}
}
The problem with this is that this configuration is placed under the package scanned by @ComponentScan
view/beans
{
"bean": "ribbonServerList",
"aliases": [],
"scope": "singleton",
"type": "org.springframework.cloud.netflix.ribbon.StaticServerList",
"resource": "class path resource [com/xxx/XxxRibbonConfig.class]",
"dependencies": []
}
If multiple ribbonClients are found, the ribbonServerList of one of them is registered as global. ( 为什么会注册为全局的?
) while spring-cloud-netflix-core-1.2.6.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerList<Server> ribbonServerList(IClientConfig config) {
if (this.propertiesFactory.isSet(ServerList.class, name)) {
return this.propertiesFactory.get(ServerList.class, config, name);
}
ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return serverList;
}
It is found here that there is already a ribbonServerList, and the server list configuration of the specified service name is not found, so the global one is used.
solution
The solution is very simple, just modify it to the correct service name. Another solution is to not use the RibbonClient configuration. The easiest is to add the NIWSServerListClassName property to the configuration file:
xxx:
ribbon:
ReadTimeout: 60000
ConnectTimeout: 60000
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
listOfServers: http://192.168.99.100:8080,http://192.168.99.101:8080
A scheme to circumvent the global ribbonServerList
How to avoid the global ribbonServerList without using the NIWSServerListClassName property configuration and using the RibbonClient configuration?
Scenario 1: Use @RibbonClients configuration
@SpringCloudApplication
@RibbonClients(value = {
@RibbonClient(name = "xxx",configuration = XxxRibbonConfig.class),
@RibbonClient(name = "demo",configuration = DemoRibbonConfig.class)
})
public class DemoServiceApplication {
public static void main(String[] args) {
SpringApplication.run(DemoServiceApplication.class, args);
}
}
Then there are no annotations on the XxxRibbonConfig and DemoRibbonConfig classes, such as
public class XxxRibbonConfig {
String listOfServers = "http://192.168.99.100:8080,http://192.168.99.101:8080";
@Bean
public ServerList<Server> ribbonServerList() {
List<Server> list = Lists.newArrayList();
if (!Strings.isNullOrEmpty(listOfServers)) {
for (String s: listOfServers.split(",")) {
list.add(new Server(s.trim()));
}
}
return new StaticServerList<Server>(list);
}
}
Since there are no annotations on the classes, it does not matter whether their classpath is under the package scanned by @ComponentScan at this time.
Scenario 2
Just put the XxxRibbonConfig and DemoRibbonConfig marked @RibbonClient outside the package scanned by @ComponentScan.