I need a custom Rule which can route requests to different service according to weight option (actually I want to deploy an A/B test service and only small percent of user will hit it), the code is like following
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
public class ConfigWeightRule extends RoundRobinRule { private Logger logger = LoggerFactory.getLogger("ConfigWeightRule"); @Override public Server choose(Object key) { RequestContext currentContext = RequestContext.getCurrentContext(); logger.info("LOG00120: currentContext: {} ", currentContext); int weight = 5; if (new Random().nextInt(100) > weight) { List<Server> abTestServers = this.getLoadBalancer().getAllServers().stream().filter(s -> ((DiscoveryEnabledServer) s).getInstanceInfo().getInstanceId().contains("ab-test")).collect(Collectors.toList()); if (abTestServers.size() > 0) { return abTestServers.get(0); } } return super.choose(key); } } |
And following is application.yml configuration file
1 2 3 4 |
facade: ribbon: NFLoadBalancerClassName: com.netflix.loadbalancer.DynamicServerListLoadBalancer NFLoadBalancerRuleClassName: cn.jh.microservises.support.gateway.loadbalancer.rule.ConfigWeightRule |
Adding breakpoints at constructor of ZoneAwareLoadBalancer and DynamicServerListLoadBalancer, then access the Zuul service via browser and I found ZoneAwareLoadBalancer constructor breakpoint is reached first.
1 2 3 |
public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter) { super(clientConfig, rule, ping, serverList, filter); } |
Inspect the value clientConfig->properties->NFLoadBalancerClassName, its value is still "com.netflix.loadbalancer.ZoneAwareLoadBalancer", and the passed in rule is not my configured rule as well.
This means my configuration didn't work.
Even I changed the class name to an invalid value, there is no error
1 2 3 4 |
facade: ribbon: NFLoadBalancerClassName: com.netflix.loadbalancer.DynamicServerListLoadBalancer NFLoadBalancerRuleClassName: aaa |
In Spring Cloud documentation, under Customizing the Ribbon Client using properties section, it's saying
Starting with version 1.2.0, Spring Cloud Netflix now supports customizing Ribbon clients using properties to be compatible with the Ribbon documentation.
Checking maven pom file I found it's using 1.1.0
1 2 3 4 5 6 7 8 9 10 |
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> <version>1.1.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> <version>1.1.0.RELEASE</version> </dependency> |
Changing them to 1.2.0 and run again, following exception is thrown
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ribbonRule' defined in org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.netflix.loadbalancer.IRule]: Factory method 'ribbonRule' threw exception; nested exception is java.lang.IllegalArgumentException: Unknown class to load aaa for class interface com.netflix.loadbalancer.IRule named facade at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) at org.springframework.cloud.context.named.NamedContextFactory.createContext(NamedContextFactory.java:110) at org.springframework.cloud.context.named.NamedContextFactory.getContext(NamedContextFactory.java:79) at org.springframework.cloud.context.named.NamedContextFactory.getInstance(NamedContextFactory.java:115) at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getInstance(SpringClientFactory.java:111) at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getClient(SpringClientFactory.java:49) at org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory.create(HttpClientRibbonCommandFactory.java:41) at org.springframework.cloud.netflix.zuul.filters.route.apache.HttpClientRibbonCommandFactory.create(HttpClientRibbonCommandFactory.java:30) at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.forward(RibbonRoutingFilter.java:131) at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.run(RibbonRoutingFilter.java:84) at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112) at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:197) at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:161) at com.netflix.zuul.FilterProcessor.route(FilterProcessor.java:120) at com.netflix.zuul.ZuulRunner.route(ZuulRunner.java:96) at com.netflix.zuul.http.ZuulServlet.route(ZuulServlet.java:116) at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:81) at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:158) at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequestInternal(ZuulController.java:43) at org.springframework.web.servlet.mvc.AbstractController.handleRequest(AbstractController.java:147) at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:50) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:961) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.boot.actuate.autoconfigure.EndpointWebMvcAutoConfiguration$ApplicationContextHeaderFilter.doFilterInternal(EndpointWebMvcAutoConfiguration.java:261) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:92) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:115) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:87) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:103) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:121) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:522) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1095) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1502) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1458) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.netflix.loadbalancer.IRule]: Factory method 'ribbonRule' threw exception; nested exception is java.lang.IllegalArgumentException: Unknown class to load aaa for class interface com.netflix.loadbalancer.IRule named facade at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ... 90 more Caused by: java.lang.IllegalArgumentException: Unknown class to load aaa for class interface com.netflix.loadbalancer.IRule named facade at org.springframework.cloud.netflix.ribbon.PropertiesFactory.get(PropertiesFactory.java:58) at org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration.ribbonRule(RibbonClientConfiguration.java:87) at org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration$$EnhancerBySpringCGLIB$$dae93cac.CGLIB$ribbonRule$4(<generated>) at org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration$$EnhancerBySpringCGLIB$$dae93cac$$FastClassBySpringCGLIB$$5a79f483.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:228) at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:356) at org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration$$EnhancerBySpringCGLIB$$dae93cac.ribbonRule(<generated>) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:162) ... 91 more |
It's because NFLoadBalancerRuleClassName is aaa, changing it to a valid name and try it again, it works now