com.netflix.zuul.exception.ZuulException: Forwarding error 看了好多的博客,各种方案都有,有的说是包的问题:把spring-cloud-netflix-eureka-client 修改成 spring-cloud-starter-netflix-eureka-client 这个包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
原因是:
zuul 要用到ribbon-zuul ,如果是 spring-cloud-netflix-eureka-client 里面是没有的这个包的
我的情况是:
eureka:
看到网关和接口系统都是正常的状态,同时也是可以通过 url 去访问各自的服务接口:
user 接口:测试接口是正常的
测试通过服务名称方式去调用接口也是正常的:
======================= 下面是我的问题的复盘记录 ======================
通过网关调用时候就出现了这个500 异常,
错误日志:
com.netflix.zuul.exception.ZuulException: Forwarding error
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.handleException(RibbonRoutingFilter.java:189) ~[spring-cloud-netflix-zuul-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.forward(RibbonRoutingFilter.java:164) ~[spring-cloud-netflix-zuul-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.run(RibbonRoutingFilter.java:112) ~[spring-cloud-netflix-zuul-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:117) ~[zuul-core-1.3.1.jar:1.3.1]
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:193) ~[zuul-core-1.3.1.jar:1.3.1]
at com.netflix.zuul.FilterProcessor.runFilters(FilterProcessor.java:157) ~[zuul-core-1.3.1.jar:1.3.1]
at com.netflix.zuul.FilterProcessor.route(FilterProcessor.java:118) ~[zuul-core-1.3.1.jar:1.3.1]
at com.netflix.zuul.ZuulRunner.route(ZuulRunner.java:96) ~[zuul-core-1.3.1.jar:1.3.1]
at com.netflix.zuul.http.ZuulServlet.route(ZuulServlet.java:116) ~[zuul-core-1.3.1.jar:1.3.1]
at com.netflix.zuul.http.ZuulServlet.service(ZuulServlet.java:81) ~[zuul-core-1.3.1.jar:1.3.1]
at org.springframework.web.servlet.mvc.ServletWrappingController.handleRequestInternal(ServletWrappingController.java:165) [spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.cloud.netflix.zuul.web.ZuulController.handleRequest(ZuulController.java:44) [spring-cloud-netflix-zuul-2.0.1.RELEASE.jar:2.0.1.RELEASE]
at org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter.handle(SimpleControllerHandlerAdapter.java:52) [spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991) [spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) [spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) [spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866) [spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) [spring-webmvc-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90) [spring-boot-actuator-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:155) [spring-boot-actuator-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:123) [spring-boot-actuator-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108) [spring-boot-actuator-2.0.5.RELEASE.jar:2.0.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.9.RELEASE.jar:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) [tomcat-embed-core-8.5.34.jar:8.5.34]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.34.jar:8.5.34]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_351]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_351]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.34.jar:8.5.34]
at java.lang.Thread.run(Thread.java:750) [na:1.8.0_351]
Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: route1
at com.netflix.loadbalancer.LoadBalancerContext.getServerFromLoadBalancer(LoadBalancerContext.java:483) ~[ribbon-loadbalancer-2.2.5.jar:2.2.5]
zuul 配置:
zuul.routes.route1.path= /user/**
zuul.routes.ronte1.serviceId=EUREKAUSER
URL: 通过下面两个方式都无法访问我的接口服务
192.166.88.2:9999/api-gateway/user/getjData
192.166.88.2:9999/api-gateway/user/user/getjData
分析:log
2023-02-02 13:57:47.922 INFO 33420 --- [nio-9999-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/user/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2023-02-02 13:57:47.922 INFO 33420 --- [nio-9999-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/ronte1/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2023-02-02 13:57:47.922 INFO 33420 --- [nio-9999-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/consumer/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2023-02-02 13:57:47.923 INFO 33420 --- [nio-9999-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/eurekaconsumer/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
2023-02-02 13:57:47.923 INFO 33420 --- [nio-9999-exec-1] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/eurekauser/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
我什么时候定义了/ronte1/**那 还有这个/eurekauser/**是怎么来的,这个问题后面会解析
通过异常去打断点:
192.166.88.2:9999/api-gateway/user/user/getjData 看到他匹配给我的zuul.routes.route1.path= /user/**
seriveId 是 route1 所以导致找不到服务名称 ,所以就是说
zuul.routes.route1.path= /user/** 表示会拦截 /user/** 会识别到 seriveId = route1 proxy = route1 通过这个方式怎么才可以正常访问,可以通过 path + url 方式去访问
zuul.routes.route1.path= /user/** zuul.routes.route1.url=http://192.166.88.2:8081/user/
通过 192.166.88.2:9999/api-gateway/user/getjData 就可以访问了
因为url 的方式没有通过 RibbonCommandContext ,所以他没有负载均衡的作用,只是普通的url 映射。
回到前面的疑问 还有这个/eurekauser/**是怎么来的 ,其实这个是zuul 自动来取eureka 的
zuul 注册到eureka 的时候,会自动生成一个匹配, 所以是不用配置路由也是可以通过服务名去调用
案例测试: 在没有配置 网关服务名称的情况下,zuul 通过eureka 会生成对于的服务名和匹配的url
比如:servierId=eurekauser 会自动匹配url 是这个的 /eurekauser/**
所以可以直接通过服务名称去调用
网关的log: 看到有一个这样的服务url 匹配,这个我明明是没有配置怎么来的那?
Mapped URL path [/eurekaconsumer/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
: Mapped URL path [/eurekauser/**]
这样问题来了,这个下面这个配置表示什么意思那,明明都已经有了服务名了
zuul.routes.ronte1.serviceId=EUREKAUSER
其实这个的含义就是 url 匹配到ronte1 ,他会mapping 到 EUREKAUSER 服务名称 Mapped URL path [/ronte1/**] ,
就是 /ronte1/** --》》》 serviceId= EUREKAUSER
所以可以用服务名或者前缀去访问我们定义的接口
192.166.88.2:9999/api-gateway/ronte1/user/getjData
192.166.88.2:9999/api-gateway/eurekauser/user/getjData
zuul的yml如果不配置路由,那么访问zuul网关的默认方式:http://zuulHostIp:port/要访问的微服务名称/服务中的url
zuul.routes.ronte1.serviceId=EUREKAUSER
所以是用serviceId 方式,前面的路由名字 ronte1 其实是有作用的,不是说随意的,我看到网上说这个是随意的,感觉不太对的
代码的追踪:
protected RibbonCommandContext buildCommandContext(RequestContext context) {
HttpServletRequest request = context.getRequest();
MultiValueMap<String, String> headers = this.helper.buildZuulRequestHeaders(request);
MultiValueMap<String, String> params = this.helper.buildZuulRequestQueryParams(request);
String verb = this.getVerb(request);
InputStream requestEntity = this.getRequestBody(request);
if (request.getContentLength() < 0 && !verb.equalsIgnoreCase("GET")) {
context.setChunkedRequestBody();
}
String serviceId = (String)context.get("serviceId");
Boolean retryable = (Boolean)context.get("retryable");
Object loadBalancerKey = context.get("loadBalancerKey");
String uri = this.helper.buildZuulRequestURI(request);
uri = uri.replace("//", "/");
long contentLength = this.useServlet31 ? request.getContentLengthLong() : (long)request.getContentLength();
return new RibbonCommandContext(serviceId, verb, uri, retryable, headers, params, requestEntity, this.requestCustomizers, contentLength, loadBalancerKey);
}