[Turn] Ribbon problem

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.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326514185&siteId=291194637