Spring cloud 实例关闭时的Spring云网关500

Spring cloud 实例关闭时的Spring云网关500,spring-cloud,spring-cloud-gateway,Spring Cloud,Spring Cloud Gateway,我有一个SpringCloudGateway(eureka客户端)应用程序,它使用SpringCloud负载平衡器(SpringCloud版本:Hoxton.SR6),我有一个SpringBoot应用程序的实例(SpringBoot2.3,启用了优雅关机,(eureka客户端) 当我关闭spring引导服务并通过网关执行请求时,网关抛出500错误(连接被拒绝),而不是503。503在1-2分钟后出现 有人能澄清这是否是一种预期的行为吗 问题似乎来自eureka客户端(在我的例子中是1.9.21版

我有一个SpringCloudGateway(eureka客户端)应用程序,它使用SpringCloud负载平衡器(SpringCloud版本:Hoxton.SR6),我有一个SpringBoot应用程序的实例(SpringBoot2.3,启用了优雅关机,(eureka客户端)

当我关闭spring引导服务并通过网关执行请求时,网关抛出500错误(连接被拒绝),而不是503。503在1-2分钟后出现

有人能澄清这是否是一种预期的行为吗

问题似乎来自eureka客户端(在我的例子中是1.9.21版本)
AtomicReference localRegionApps
不经常更新

谢谢

更新: 我决定更深入地检查这个500错误。结果是,如果未使用端口,我的系统(ubuntu)会出现此错误:

curl -v localhost:9722
 Rebuilt URL to: localhost:9722/
   Trying 127.0.0.1...
 TCP_NODELAY set
 connect to 127.0.0.1 port 9722 failed: Connection refused
 Failed to connect to localhost port 9722: Connection refused
 Closing connection 0
所以我把我的申请表放进去了。yml:

spring:
  cloud:
    gateway:
      routes:
        - id: my_route
          uri: http://localhost:9722/
然后,当我的请求被路由到my_route并且没有任何应用使用9722时,我会得到一个错误:

io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: localhost/127.0.0.1:9722
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]
    |_ checkpoint ⇢ HTTP GET "/internal/mail/internal/health-check" [ExceptionHandlingWebHandler]
Stack trace:
Caused by: java.net.ConnectException: finishConnect(..) failed: Connection refused
    at io.netty.channel.unix.Errors.throwConnectException(Errors.java:124)
    at io.netty.channel.unix.Socket.finishConnect(Socket.java:251)
    at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.doFinishConnect(AbstractEpollChannel.java:672)
    at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.finishConnect(AbstractEpollChannel.java:649)
    at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.epollOutReady(AbstractEpollChannel.java:529)
    at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:465)
    at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:378)
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989)
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:834)

这似乎是一个意外的例外,因为不可能使用断路器或任何网关过滤器来处理它


是否可以正确处理此错误?在这种情况下,我想返回503,请尝试定义自定义ErrorWebExceptionHandler。 见:
org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler

org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler
尝试定义自定义ErrorWebExceptionHandler。 见:
org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler

org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler
将特定异常映射到特定HTTP状态代码的最简单方法之一是提供类型为
org.springframework.boot.web.reactive.error.ErrorAttributes
的自定义bean。以下是一个示例:

@Bean
public ErrorAttributes errorAttributes() {
    return new CustomErrorAttributes(httpStatusExceptionTypeMapper);
}

public class CustomErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Map<String, Object> attributes = super.getErrorAttributes(request, options);
        Throwable error = getError(request);
        MergedAnnotation<ResponseStatus> responseStatusAnnotation = MergedAnnotations
            .from(error.getClass(), MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);
        HttpStatus errorStatus = determineHttpStatus(error, responseStatusAnnotation);
        attributes.put("status", errorStatus.value());
        return attributes;
    }

    private HttpStatus determineHttpStatus(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        if (error instanceof ResponseStatusException) {
            return ((ResponseStatusException) error).getStatus();
        }
        return responseStatusAnnotation.getValue("code", HttpStatus.class).orElseGet(() -> {
           if (error instanceof java.net.ConnectException) {
               return HttpStatus.SERVICE_UNAVAILABLE;
           }
           return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }
}
@Bean
公共ErrorAttributes ErrorAttributes(){
返回新的CustomErrorAttributes(httpStatusExceptionTypeMapper);
}
公共类CustomErrorAttributes扩展了DefaultErrorAttributes{
@凌驾
公共映射getErrorAttributes(ServerRequest请求、ErrorAttributeOptions选项){
Map attributes=super.getErrorAttributes(请求、选项);
Throwable error=getError(请求);
MergedAnnotation responseStatusAnnotation=MergedAnnotations
.from(error.getClass(),MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);
HttpStatus errorStatus=确定HttpStatus(错误,响应状态注释);
attributes.put(“status”,errorStatus.value());
返回属性;
}
私有HttpStatus确定HttpStatus(可丢弃错误,MergedAnnotation responseStatusAnnotation){
if(错误实例of ResponseStatusException){
返回((ResponseStatusException)错误).getStatus();
}
返回responseStatusAnnotation.getValue(“code”,HttpStatus.class).OrelSetGet(()->{
if(java.net.ConnectException的错误实例){
返回HttpStatus.SERVICE\u不可用;
}
返回HttpStatus.INTERNAL\u SERVER\u错误;
}
}
}

将特定异常映射到特定HTTP状态代码的最简单方法之一是提供类型为
org.springframework.boot.web.reactive.error.ErrorAttributes的自定义bean。以下是一个示例:

@Bean
public ErrorAttributes errorAttributes() {
    return new CustomErrorAttributes(httpStatusExceptionTypeMapper);
}

public class CustomErrorAttributes extends DefaultErrorAttributes {
    @Override
    public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
        Map<String, Object> attributes = super.getErrorAttributes(request, options);
        Throwable error = getError(request);
        MergedAnnotation<ResponseStatus> responseStatusAnnotation = MergedAnnotations
            .from(error.getClass(), MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);
        HttpStatus errorStatus = determineHttpStatus(error, responseStatusAnnotation);
        attributes.put("status", errorStatus.value());
        return attributes;
    }

    private HttpStatus determineHttpStatus(Throwable error, MergedAnnotation<ResponseStatus> responseStatusAnnotation) {
        if (error instanceof ResponseStatusException) {
            return ((ResponseStatusException) error).getStatus();
        }
        return responseStatusAnnotation.getValue("code", HttpStatus.class).orElseGet(() -> {
           if (error instanceof java.net.ConnectException) {
               return HttpStatus.SERVICE_UNAVAILABLE;
           }
           return HttpStatus.INTERNAL_SERVER_ERROR;
        }
    }
}
@Bean
公共ErrorAttributes ErrorAttributes(){
返回新的CustomErrorAttributes(httpStatusExceptionTypeMapper);
}
公共类CustomErrorAttributes扩展了DefaultErrorAttributes{
@凌驾
公共映射getErrorAttributes(ServerRequest请求、ErrorAttributeOptions选项){
Map attributes=super.getErrorAttributes(请求、选项);
Throwable error=getError(请求);
MergedAnnotation responseStatusAnnotation=MergedAnnotations
.from(error.getClass(),MergedAnnotations.SearchStrategy.TYPE_HIERARCHY).get(ResponseStatus.class);
HttpStatus errorStatus=确定HttpStatus(错误,响应状态注释);
attributes.put(“status”,errorStatus.value());
返回属性;
}
私有HttpStatus确定HttpStatus(可丢弃错误,MergedAnnotation responseStatusAnnotation){
if(错误实例of ResponseStatusException){
返回((ResponseStatusException)错误).getStatus();
}
返回responseStatusAnnotation.getValue(“code”,HttpStatus.class).OrelSetGet(()->{
if(java.net.ConnectException的错误实例){
返回HttpStatus.SERVICE\u不可用;
}
返回HttpStatus.INTERNAL\u SERVER\u错误;
}
}
}

不需要从超类重复逻辑。只需
if(getError(request)instanceof ConnectException)属性。put(“status”,SERVICE_UNAVAILABLE);
不需要从超类重复逻辑。只需
if(getError(request)instanceof ConnectException)属性。put(“status”,SERVICE_UNAVAILABLE);