最近写了一个小程序,使用netty作为后端框架搭建了一个聊天服务,协议用的webSocket,本地调试好后,部署到测试服,使用nginx反向代理服务器。这时候客户端出现报错:{code: 1006, reason: “abnormal closure”}。
碰到这样的问题,本能地想要祭出试错大法:在网络上查找方案,然后不断地尝试看看能否解决问题。最近看过netty是如何完成websocket连接的源码,所以心想是不是可以自己去解决这个问题。
说干就干,第一步自然是加上日志。重写了WebSocketServerProtocolHandler的exceptionCaught方法。
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
LOGGER.error("Exception: ", cause);
super.exceptionCaught(ctx, cause);
}
这时候才看到报错,可以猜到是header参数错了,但不确定是哪个地方。
2024-10-10 14:16:23.518 ERROR 41226 --- [ntLoopGroup-3-4] c.s.n.n.MyWebSocketServerProtocolHandler : Exception:
io.netty.handler.codec.http.websocketx.WebSocketServerHandshakeException: not a WebSocket request: a |Connection| header must includes a token 'Upgrade'
at io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker13.newHandshakeResponse(WebSocketServerHandshaker13.java:140) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
at io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker.handshake(WebSocketServerHandshaker.java:202) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
at io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker.handshake(WebSocketServerHandshaker.java:278) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
at io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker.handshake(WebSocketServerHandshaker.java:256) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
at io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandshakeHandler.channelRead(WebSocketServerProtocolHandshakeHandler.java:97) ~[netty-codec-http-4.1.84.Final.jar!/:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.84.Final.jar!/:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.84.Final.jar!/:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.84.Final.jar!/:4.1.84.Final]
at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93) ~[netty-transport-4.1.84.Final.jar!/:4.1.84.Final]
nginx配置如下:
location /netty/ {
proxy_redirect off;
proxy_pass http://wss_psy/;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
proxy_http_version 1.1;
proxy_connect_timeout 65;
proxy_send_timeout 65;
proxy_read_timeout 300;
proxy_ignore_client_abort on; #客户端断网时,nginx服务器是否终端对被代理服务器的请求。默认为off。
proxy_buffering off;
#keepalive_requests 8192;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “upgrade”;
}
继续加日志,在HttpObjectAggregator解析出http消息之后,加了一个HttpHeaderHandler用于输出header参数:
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new HttpServerCodec())
.addLast(new ChunkedWriteHandler())
.addLast(new HttpObjectAggregator(65536))
.addLast(new HttpHeaderHandler()) //输出header参数
// .addLast(new WebSocketServerProtocolHandler("/ws"))
.addLast(new MyWebSocketServerProtocolHandler("/ws"))
.addLast(new WebsocketServerHandler());
}
});
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
LOGGER.info("url: {}", request.uri());
Iterator<Map.Entry<String, String>> iterator = request.headers().iterator();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
LOGGER.info("{}: {}", entry.getKey(), entry.getValue());
}
super.channelRead(ctx, msg);
} else {
super.channelRead(ctx, msg);
}
}
日志输出如下:
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : url: /ws
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Host: psy.zhihe.site
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : X-Real_IP: 103.91.179.202
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : X-Forwarded-For: 103.91.179.202:49343
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Upgrade: websocket
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Connection: âupgradeâ
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Pragma: no-cache
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Cache-Control: no-cache
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : User-Agent: wechatdevtools desktopapp appservice port/51058 token/c5012f423f12b6a93f67a0ef768a66c7 runtime/2 MicroMessenger
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Origin: https://psy.zhihe.site
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Sec-WebSocket-Version: 13
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Accept-Encoding: gzip, deflate, br
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Accept-Language: zh-CN,zh;q=0.9
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : referer: https://servicewechat.com/wx271a8a9364230880/devtools/page-frame.html
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Sec-WebSocket-Key: aWZOjVrQAyZl0iBc7VTHbA==
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
2024-10-10 14:29:05.912 INFO 41226 --- [tLoopGroup-3-13] c.s.n.netty.HttpHeaderProcessor : content-length: 0
就此可以看出是header中的Connection参数出了问题,几经修改后问题解决,最后更正如下:
location /netty/ {
proxy_redirect off;
proxy_pass http://wss_psy/;
proxy_set_header Host $host;
proxy_set_header X-Real_IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr:$remote_port;
proxy_http_version 1.1;
proxy_connect_timeout 65;
proxy_send_timeout 65;
proxy_read_timeout 300;
proxy_ignore_client_abort on; #客户端断网时,nginx服务器是否终端对被代理服务器的请求。默认为off。
proxy_buffering off;
#keepalive_requests 8192;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection Upgrade;
}
nginx配置是从另外一个项目抄过来的,另外一个项目没有出现问题,也许是netty版本不同,未作深究。