Spring cloud网关gateway代理websocket报io.netty.handler.codec.http.websocketx.CorruptedWebSocketFrameException Max frame length of 65536 has been exceeded


使用spring cloud代理websocket后,默认的包大小是64Kb,如果传比较大的图片或其他数据时会报Spring cloud网关gateway代理websocket报io.netty.handler.codec.http.websocketx.CorruptedWebSocketFrameException Max frame length of 65536 has been exceeded错误,此记录
解决问题的过程。

一、错误结果如下:

2022-02-21 17:00:46,701 WARN [reactor-http-nio-14] reactor.netty.channel.FluxReceive.warn:294 - [id: 0x8f99a4bf, L:0.0.0.0/0.0.0.0:51471 ! R:/127.0.0.1:8081] An exception has been observed post termination, use DEBUG level to see the full stack: io.netty.handler.codec.http.websocketx.CorruptedWebSocketFrameException: Max frame length of 65536 has been exceeded.

二、解决方式:

先是通过这篇文章:https://www.freesion.com/article/2527480206/了解到了大概的问题是由于message超出了netty websocket限制的最大帧长度的原因,另外也给出了自己写个class继承ReactorNettyWebSocketClient然后做一系列的操作,但看下来还是觉得太麻烦了,又要排除Gateway配置,又要引入其他的东西等,因此放弃。

但这也给了解决的思路,即然ReactorNettyWebSocketClient源码中有关于包大小的代码,那就直接在这个类的代码复制出来,放在springboot工程中,然后修改重启即可。

我使用的springboot版本:2.3.12.RELEASE,其他版本的代码可能有点不同,但都可以类似原理修改

1.拷代码到springboot中

org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient代码位置:spring-webflux-5.2.15.RELEASE.jar中,如下图
在这里插入图片描述
将org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient的代码直接同路径和包名拷到springboot工程中,如:
在这里插入图片描述

2.修改关于包大小的业务逻辑

修改改很简单,直接在ReactorNettyWebSocketClient的成员变量改即可。
修改前:

private int maxFramePayloadLength = NettyWebSocketSessionSupport.DEFAULT_FRAME_MAX_SIZE;

在这里插入图片描述

修改后:

private int maxFramePayloadLength = 64 * 1024 * 1024 ; //64MB

在这里插入图片描述
maxFramePayloadLength变量主要是在execute中用到的,如下,springboot版本不同,方法可能有些细小差别
在这里插入图片描述

三、重启springboot即可生效

重启springboot生效

四、延伸:为什么优先读取springboot中修改后的源码?

修改后的源码ReactorNettyWebSocketClient是和我们的应用代码在一起的,因此通过springboot打包后,会在BOOT-INF/classes中。
在这里插入图片描述
而其他的第三方jar会在classpath.idx中。
在这里插入图片描述
类在springboot打包的jar中查找的顺序为:先在BOOT-INF/classes中查询,如果找到就返回,找不到继续按在classpath.idx中列出的jar包顺序进行查找
因为我们改之后的源码在BOOT-INF/classes中,因为会优先生效,起到了覆盖原来的代码的效果

我们也可通过org.springframework.boot.loader.JarLauncher进行代码调试跟踪,在执行到ExecutableArchiveLauncher的createClassLoader方法时,会依次读取springboot打包后的jar文件,把里面的信息解析出来放urls中,并作为org.springframework.boot.loader.LaunchedURLClassLoader类的构造参数。
在程序运行中需要加载类的时候,就以urls中列出的目录或jar集合顺序进行类的查询。
在这里插入图片描述
从上面看,也进一步验证了springboot加载类时是优先加载BOOT-INF/classes的。

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐