springgateway 启动报错(springboot gateway)

项目中再使用springcloud gateway做微服务网关,在线上的环境忽然发现有时候接口会出现卡顿,更多集中在登陆的时候,而且卡顿呈现不确定性再检查springcloud gateway的日志时候发现出现了Connection链接的异常reactor.netty.channel.AbortedException: Connection has been closed,期间还发现一个问题,因为服务部署在k8s上有时候即使出现这个异常后,也不影响服务使用但是时间长了之后,就会导致请求卡顿严重时候还会出现请求处理失败,在网上搜索包括查看github的issue之后发现偏幅都太宽泛,并不聚焦没有太多实质性的制导,最终只能硬着头皮完整走一遍,并且讲结果记录下来方便以后查看。

springgateway 启动报错(springboot gateway)

场景复现

gateway日志中的异常信息

2021-12-10 19:18:27.776 ERROR 22734 --- [or-http-epoll-1] com.ops.config.AppConfig    : Web exception handing: Connection has been closedreactor.netty.channel.AbortedException: Connection has been closed	at reactor.netty.channel.ChannelOperationsHandler.discard(ChannelOperationsHandler.java:356)	at reactor.netty.channel.ChannelOperationsHandler.drain(ChannelOperationsHandler.java:366)	at reactor.netty.channel.ChannelOperationsHandler.handlerRemoved(ChannelOperationsHandler.java:210)	at io.netty.channel.AbstractChannelHandlerContext.callHandlerRemoved(AbstractChannelHandlerContext.java:961)	at io.netty.channel.DefaultChannelPipeline.callHandlerRemoved0(DefaultChannelPipeline.java:638)	at io.netty.channel.DefaultChannelPipeline.destroyDown(DefaultChannelPipeline.java:887)	at io.netty.channel.DefaultChannelPipeline.destroyUp(DefaultChannelPipeline.java:853)	at io.netty.channel.DefaultChannelPipeline.destroy(DefaultChannelPipeline.java:845)	at io.netty.channel.DefaultChannelPipeline.access$700(DefaultChannelPipeline.java:46)	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelUnregistered(DefaultChannelPipeline.java:1390)	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelUnregistered(AbstractChannelHandlerContext.java:178)	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelUnregistered(AbstractChannelHandlerContext.java:164)	at io.netty.channel.DefaultChannelPipeline.fireChannelUnregistered(DefaultChannelPipeline.java:830)	at io.netty.channel.AbstractChannel$AbstractUnsafe$8.run(AbstractChannel.java:835)	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:333)	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905)	at java.lang.Thread.run(Thread.java:748)2021-12-10 19:18:27.779 ERROR 22734 --- [or-http-epoll-1] o.s.w.s.adapter.HttpWebHandlerAdapter    : [26191cc8] Error [java.lang.UnsupportedOperationException] for HTTP GET "/auth/oauth/authorize?response_type=code&scope=read&client_id=00&redirect_uri=http%3A%2F%2F192.168.101.64%2Fapi%2Foauth2OAuth&state=%7B%22redirectUrl%22:%22http://192.168.101.64/1000/team/role", but ServerHttpResponse already committed (302 FOUND)2021-12-11 13:05:57.278 ERROR 27785 --- [or-http-epoll-5] com.ops.config.AppConfig    : Web exception handing: Connection has been closedreactor.netty.channel.AbortedException: Connection has been closed	at reactor.netty.channel.ChannelOperationsHandler.discard(ChannelOperationsHandler.java:356)	at reactor.netty.channel.ChannelOperationsHandler.drain(ChannelOperationsHandler.java:366)	at reactor.netty.channel.ChannelOperationsHandler.handlerRemoved(ChannelOperationsHandler.java:210)	at io.netty.channel.AbstractChannelHandlerContext.callHandlerRemoved(AbstractChannelHandlerContext.java:961)	at io.netty.channel.DefaultChannelPipeline.callHandlerRemoved0(DefaultChannelPipeline.java:638)	at io.netty.channel.DefaultChannelPipeline.destroyDown(DefaultChannelPipeline.java:887)	at io.netty.channel.DefaultChannelPipeline.destroyUp(DefaultChannelPipeline.java:853)	at io.netty.channel.DefaultChannelPipeline.destroy(DefaultChannelPipeline.java:845)	at io.netty.channel.DefaultChannelPipeline.access$700(DefaultChannelPipeline.java:46)	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelUnregistered(DefaultChannelPipeline.java:1390)	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelUnregistered(AbstractChannelHandlerContext.java:178)	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelUnregistered(AbstractChannelHandlerContext.java:164)	at io.netty.channel.DefaultChannelPipeline.fireChannelUnregistered(DefaultChannelPipeline.java:830)	at io.netty.channel.AbstractChannel$AbstractUnsafe$8.run(AbstractChannel.java:835)	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)	at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:333)	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905)	at java.lang.Thread.run(Thread.java:748)2021-12-11 13:05:57.281 ERROR 27785 --- [or-http-epoll-5] o.s.w.s.adapter.HttpWebHandlerAdapter    : [8d36ff6a] Error [java.lang.UnsupportedOperationException] for HTTP POST "/sws/webhook/hws/alert", but ServerHttpResponse already committed (200 OK)

抛析问题

问题分析比较简单了,第一次碰到这个异常我也不认识他,他也不认识我话不多少直接上google查看一番,经过baidu和google之后,基本上就能确定了是因为netty本身的bug引起的,那么解决问题就简单了升级netty版本到修复了该问题的新版本就ok了。官方bug链接:github.com/reactor/rea…

解决问题

既然问题确定了后那么就直接开始动手升级

1.升级netty到0.9.7RELEASE版本

通过查看官方在github中提供的信息,说到在netty的0.8.5版本中就进行了修复(但是实际结果中bug还是存在)。项目中实际使用的是springboot2.1.4.RELEASE + springcloud Greenwich.SR1自动引入的io.projectreactor.netty:reactor-netty:0.8.6.RELEASE版本。结合搜到的一些文章提到0.9.4版本修复了该问题,但是又因为0.9.4还有问题因此计划一次性升级到0.9.7修复缺陷。

方法1:单独升级reactor-netty到0.9.7版本 io.projectreactor.netty:reactor-netty:0.9.7.RELEASE编译通过启动服务时候失败,因为版本差异太大导致其他依赖所需方法和类启动报错,不兼容低版本的springboot,因此决定通过整体升级springboot的方式来进行升级,但是springboot和springcloud的版本关联也比较大,因此需要做同步升级;

方法2:升级springboot到2.2.7.RELEASE版本,同时升级springcloud到Hoxton.SR12通过查看springboot中的依赖然后找到对应的版本,具体步骤如下:

springboot第三方依赖版本查看

  1. spring-boot/spring-boot-project/spring-boot-dependencies/pom.xml查看引用的依赖版本: github.com/spring-proj…;
  2. release note 查看依赖版本变化:github.com/spring-proj…;
  3. netty版本查看:github.com/reactor/rea…
  4. 升级springcloud到对应的版本 start.spring.io/actuator/in… (浏览器打不开可以通过curl查看);

相关链接:springboot start工作原理:blog.csdn.net/weixin_3956… www.jb51.net/article/188…

2.解决版本升级带来的其他问题

问题1:缺少tomcat依赖升级springboot到2.2.7.RELEASE 和 springcloud Hoxton.SR12之后,启动服务提示缺少servlet.

处理方式:添加tomcat依赖 implementation 'org.springframework.boot:spring-boot-starter-tomcat'相关链接: stackoverflow.com/questions/3…

问题2:0.9.7版本reactor-netty找不到方法,服务启动失败

***************************APPLICATION FAILED TO START***************************Description:An attempt was made to call a method that does not exist. The attempt was made from the following location:    org.springframework.cloud.gateway.config.GatewayAutoConfiguration$NettyConfiguration.buildConnectionProvider(GatewayAutoConfiguration.java:798)The following method did not exist:    reactor.netty.resources.ConnectionProvider$Builder.evictInBackground(Ljava/time/Duration;)Lreactor/netty/resources/ConnectionProvider$ConnectionPoolSpec;The method's class, reactor.netty.resources.ConnectionProvider$Builder, is available from the following locations:    jar:file:/Users/qu/.gradle/caches/modules-2/files-2.1/io.projectreactor.netty/reactor-netty/0.9.7.RELEASE/3a63889c056a7da153d695726daa0614bc02bcf5/reactor-netty-0.9.7.RELEASE.jar!/reactor/netty/resources/ConnectionProvider$Builder.classIt was loaded from the following location:    file:/Users/qu/.gradle/caches/modules-2/files-2.1/io.projectreactor.netty/reactor-netty/0.9.7.RELEASE/3a63889c056a7da153d695726daa0614bc02bcf5/reactor-netty-0.9.7.RELEASE.jarAction:Correct the classpath of your application so that it contains a single, compatible version of reactor.netty.resources.ConnectionProvider$BuilderProcess finished with exit code 1

处理方式:升级reactor-netty版本到0.9.13.RELEASE相关链接:stackoom.com/question/4a…www.codenong.com/cs107047281…

问题3. 升级到0.9.13之后ES health Bean找不到服务启动失败Failed to instantiate [org.springframework.boot.actuate.health.HealthContributor]: Factory method 'elasticsearchRestHealthContributor' threw exception; nested exception is java.lang.IllegalArgumentException: Beans must not be empty 找不到

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'healthContributorRegistry' defined in class path resource [org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthContributorRegistry]: Factory method 'healthContributorRegistry' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchRestHealthContributor' defined in class path resource [org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthContributor]: Factory method 'elasticsearchRestHealthContributor' threw exception; nested exception is java.lang.IllegalArgumentException: Beans must not be empty	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656)	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1306)	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:885)	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:789)	... 55 common frames omittedCaused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthContributorRegistry]: Factory method 'healthContributorRegistry' threw exception; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchRestHealthContributor' defined in class path resource [org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthContributor]: Factory method 'elasticsearchRestHealthContributor' threw exception; nested exception is java.lang.IllegalArgumentException: Beans must not be empty	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651)	... 69 common frames omittedCaused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'elasticsearchRestHealthContributor' defined in class path resource [org/springframework/boot/actuate/autoconfigure/elasticsearch/ElasticSearchRestHealthContributorAutoConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthContributor]: Factory method 'elasticsearchRestHealthContributor' threw exception; nested exception is java.lang.IllegalArgumentException: Beans must not be empty	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656)	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:623)	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeansOfType(DefaultListableBeanFactory.java:611)	at org.springframework.context.support.AbstractApplicationContext.getBeansOfType(AbstractApplicationContext.java:1242)	at org.springframework.boot.actuate.autoconfigure.health.HealthEndpointConfiguration.healthContributorRegistry(HealthEndpointConfiguration.java:78)	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:498)	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)	... 70 common frames omittedCaused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.boot.actuate.health.HealthContributor]: Factory method 'elasticsearchRestHealthContributor' threw exception; nested exception is java.lang.IllegalArgumentException: Beans must not be empty	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651)	... 88 common frames omittedCaused by: java.lang.IllegalArgumentException: Beans must not be empty	at org.springframework.util.Assert.notEmpty(Assert.java:549)	at org.springframework.boot.actuate.autoconfigure.health.AbstractCompositeHealthContributorConfiguration.createContributor(AbstractCompositeHealthContributorConfiguration.java:52)	at org.springframework.boot.actuate.autoconfigure.elasticsearch.ElasticSearchRestHealthContributorAutoConfiguration.elasticsearchRestHealthContributor(ElasticSearchRestHealthContributorAutoConfiguration.java:55)	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)	... 89 common frames omitted

处理方式1: 直接关闭设置management.health.elasticsearch.enabled=false,问题解决但是过于暴力不推荐使用。相关链接: github.com/spring-proj…

处理方式2: 配置Elasticsearch high level客户端Bean并且配置management.health.elasticsearch.response-timeout,配置完成后问题解决。

@Beanpublic RestHighLevelClient client() {    //todo 生成host的逻辑根据当前es host配置方式转换,线上就不需要修改ES配置    List hostList = Arrays.stream(hosts.split(Consts.COMMA)).map(h -> new HttpHost(h, port, httpType)).collect(Collectors.toList());    HttpHost[] hosts = new HttpHost[hostList.size()];    hostList.toArray(hosts);    RestClientBuilder builder = RestClient.builder(hosts);    // 异步httpclient连接延时配置    builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {        @Override        public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {            requestConfigBuilder.setConnectTimeout(RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS);            requestConfigBuilder.setSocketTimeout(RestClientBuilder.DEFAULT_SOCKET_TIMEOUT_MILLIS);            requestConfigBuilder.setConnectionRequestTimeout(RestClientBuilder.DEFAULT_CONNECT_TIMEOUT_MILLIS);            return requestConfigBuilder;        }    });    // 异步httpclient连接数配置    builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {        @Override        public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {            httpClientBuilder.setMaxConnTotal(RestClientBuilder.DEFAULT_MAX_CONN_TOTAL);            httpClientBuilder.setMaxConnPerRoute(RestClientBuilder.DEFAULT_MAX_CONN_PER_ROUTE);            return httpClientBuilder;        }    });    RestHighLevelClient client = new RestHighLevelClient(builder);    return client;}

bootstrap.yml

management:  health:    elasticsearch:      response-timeout: 1000ms # 增加超时时间,避免不必要的检查失败

启动服务发现服务启动成功,并且服务调用成功。(如果服务启动成功遇到redis反序列化问题,参考链接)

总结

1.在spring cloud gateway中使用reactor-netty如果遇到reactor.netty.channel.AbortedException: Connection has been closed需要考虑升级netty版本;2.对于依赖版本的修改会引起很多其他新问题,但是无论是新旧问题那种问题修改,都需要结合官方的文档和资料作为更新依据;

作者:troyqu链接:https://juejin.cn/post/7043621474405449764

本站部分内容由互联网用户自发贡献,该文观点仅代表作者本人,本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如发现本站有涉嫌抄袭侵权/违法违规等内容,请联系我们举报!一经查实,本站将立刻删除。