近日在使用zuul組件時,出現下面的異常:
java.lang.NullPointerException: null
at org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator.getRoutes(SimpleRouteLocator.java:72)
at org.springframework.cloud.netflix.zuul.filters.CompositeRouteLocator.getRoutes(CompositeRouteLocator.java:55)
at org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping.registerHandlers(ZuulHandlerMapping.java:102)
at org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping.lookupHandler(ZuulHandlerMapping.java:93)
at org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.getHandlerInternal(AbstractUrlHandlerMapping.java:118)
at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:352)
翻看源碼時, 其中模塊spring-cloud-netflix的版本號爲:1.3.0.RELEASE。類org.springframework.cloud.netflix.zuul.filters.SimpleRouteLocator 的代碼如下:
public List<Route> getRoutes() { if (this.routes.get() == null) { this.routes.set(locateRoutes()); } List<Route> values = new ArrayList<>(); for (String url : this.routes.get().keySet()) { ZuulRoute route = this.routes.get().get(url); String path = route.getPath(); values.add(getRoute(route, path)); } return values; }
線程在調用getRoutes()方法時,其餘線程可能正在調用doRefresh()方法。所以在getRoutes()方法的兩次this.routes.get()時,成員屬性routes的值可能已經發生變更。
protected void doRefresh() { this.routes.set(locateRoutes()); }
解決辦法(二選一):
1:升級SpringCloud辦法爲:Edgware.SR3。
2:重寫RouteLocator類,並改寫getRoutes()方法,比如:
public class CustomerRouteLocator implements RouteLocator, Ordered, RefreshableRouteLocator
@Override public List<Route> getRoutes() { if (this.routes.get() == null) { this.routes.set(locateRoutes()); } List<Route> values = new ArrayList<>(); Set<Map.Entry<String, ZuulProperties.ZuulRoute>> entries = this.routes.get().entrySet(); for (Map.Entry<String, ZuulProperties.ZuulRoute> entry : entries) { ZuulProperties.ZuulRoute route = entry.getValue(); String path = route.getPath(); values.add(getRoute(route, path)); } return values; }
然後將此類註冊到Spring,如下:
@Configuration public class CustomerRouteLocatorConfig extends ZuulProxyConfiguration {
@Bean @ConditionalOnMissingBean(CustomerRouteLocator.class) public CustomerRouteLocator customerRouteLocator() { return new CustomerRouteLocator(this.server.getServletPrefix(), this.discovery, this.zuulProperties, this.serviceRouteMapper); } @Bean @Override @ConditionalOnMissingClass("com.route.locator.CustomerRouteLocator") public DiscoveryClientRouteLocator discoveryRouteLocator() { return null; } @Bean @Override @ConditionalOnMissingClass("com.route.locator.CustomerRouteLocator") public SimpleRouteLocator simpleRouteLocator() { return null; }
@EnableDiscoveryClient @EnableCircuitBreaker @SpringBootApplication(scanBasePackages = {"com.route"}) @Import(CustomerRouteLocatorConfig.class) public class CustomerRouteZuulApplication {