记一次H5游戏因Nginx反代设置不当导致Websocket降级为HTTP引起的卡顿

发布于 2022-02-05  81 次阅读


前言

H5游戏项目是这个:bhuvan-byte/street-soccer-multi,因为这个项目的作者最近正好在重构整个项目,所以先git clone到本地然后git reset --hard 395de056fe3aa45b5914a24f329b6229ad81d761到最后一个重构前的commit,运行之后访问http://172.21.0.2:8000(docker拉了个node环境起来的)发现一切正常,于是在Nginx上配置了反代,配置文件如下:

#PROXY-START/street-soccer-multi/

location ^~ /street-soccer-multi/
{
    proxy_pass http://172.21.0.2:8000/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header REMOTE-HOST $remote_addr;
}

#PROXY-END/street-soccer-multi/

然后进http://loaclhost/street-soccer-multi/之后发现好些资源文件404了,问题不大,应该是作者代码习惯不好写了一堆绝对路径导致资源瞎飘。

进代码把所有资源文件都改成相对路径后,页面成功加载,但是游戏的Websocket连接还是连不上,也是一直往根路径ws://loaclhost/socket.io/请求。

于是改动前端在新建连接时的配置,从原来的sock = io();改为sock = io({path: window.location.pathname + 'socket.io'}),此处为了保险应写为'/socket.io',但咱们的Ngnix反代路径就是/street-soccer-multi/,所以无伤大雅。

再次进游戏,发现可以正常玩了,但是游戏画面疯狂掉帧,不过左上角帧率又完全正常,所以猜测不是性能问题而是代码层面的数据处理问题。

排查

此时重新去访问http://172.21.0.2:8000发现一切正常,所以可以排除掉刚才爆改资源路径的问题,那么先看一下控制台报错吧,有一个ws://loaclhost/street-soccer-multi/socket.io/无法连接的报错,但是游戏确确实实是可以玩并且有联机交互的,所以赶紧去看了下网络数据包,发现所有打算Upgradews连接都返回了个{"code":3,"message":"Bad request"}的报错,然后就以HTTP模拟Websocket进行连接了(socket.io库的特色功能,不过感觉大部分时候都是帮倒忙的),于是猜测可能问题出在这,由于没有建立ws长连接而导致交互缓慢,从而导致画面卡顿。

那么既然是websocket握手期间发生的问题,那感觉就是Nginx反打的锅了,于是上网找Nginx proxy socket.io,发现了好几个同样问题的,根据推荐把反代的配置文件加了一下两行,再次尝试发现websocket成功建立了。

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

其中connection_upgrade参数需要在Nginx配置文件中设定map。

map $http_upgrade $connection_upgrade {
    'websocket' upgrade;
    '' close;
}

既然如此,那么还不如把那个降级的兼容性功能给关掉,于是前端新建连接的配置又加了俩参数。

sock = io({
    transports: ['websocket'],
    upgrade: false,
    path: window.location.pathname + 'socket.io'
});

但是之前用Nginx转发到phpWorkerman库编写的websocket后端就没有这个握手问题,所以猜测应该是socket.io的在处理上过于严谨了,非得Header参数存在才升级连接,而不是只通过路径判断。


The End