最近遇到一个问题,应用程序是Blazor+Server的模式,部署在阿里云上,同时有开多个进程,然后用Nginx做反向代理和负载均衡,具体的部署配置在后面,这里先说遇到的问题和解决的经历。
首先说明一下,Blazor+Server是使用WebSocket保持长连接的方式来进行客户端与服务端之间的传输。
第一步,首先要保证应用程序本身是正常可用的,就在不使用Nginx之前,它是能运行正常的。
第二步, 按照微软文档的说明,将应用程序部署在Nginx上。Blazor Server部署
部署之后,开始访问应用程序。同时打开谷歌浏览器的控制台。
这时候会看到,偶尔的WebSocket的404,以及请求头上会有Provisional headers are shown。最令人抓狂的是这种情况只是偶尔的,
就是有时候它是正常的,有时候才会有这个提示。具体错误截图如下:
最初看起来是404,所以以为是URL异常,或者包的参数不对,于是我尝试过抓包查看包里的信息,发现包里面的关键信息都是一样的。
这里我最开始用的是Nginx 1.17.3,我不知道版本不同是否会有影响,于是我改成使用Nginx 1.19.8,问题依旧。
然后我开始配置Nginx的日志,查看error.log,发现确实存在返回WebSocket的连接返回404记录,但是除了返回404之外并没有看到其他有用的信息。
然后我开始回到没有Nginx时候访问应用程序的方式,偶然在一个网络环境较差的地方,访问我的应用程序的时候,我发现也会有同样的问题。但是浏览器会自动做重新连接的尝试,最终表现出来的效果是连接的时间变长,但是最终客户端是可以连上的,只是在浏览器的控制台那里可以看到跟上面错误截图一样的错误。
于是我开始在Nginx上也添加配置,当Http返回404是进行重试,最终发现可以解决这个问题,而且就算有重试机制,它的访问速度不会比一次成功要慢多少,完全可以接受,而且没有再出现过偶尔404的情况。
猜测引起的原因是在Nginx和应用程序之间WebSocket的握手还未结束之时Nginx已经给客户端返回消息了,这时候因为WebSocket还未连上,所以返回的是404,偶尔能成功的原因是握手已经结束,WebSocket已经连上了,基于这个思路,添加重试之后,重试的时间其实是用来等待WebSocket握手成功的(这个原因纯属猜测,如有大佬知道真实原因欢迎指出,这个问题真的困扰我很久了,谢谢)。
最终Nginx.Conf主要配置如下(test.com,localhost改成自己的域名或者服务器IP,仅供参考,如有可以改进的配置欢迎各位大佬指出,蟹蟹):
Http
{
#Nginx需要以下配置来识别以及将HTTP升级为WebSocket
map $http_upgrade $connection_upgrade {
default Upgrade;
'' close;
}
upstream Web {
server localhost:10005 max_fails=3 fail_timeout=120s;
server localhost:10006 max_fails=3 fail_timeout=120s;
server localhost:10007 max_fails=3 fail_timeout=120s;
server localhost:10008 max_fails=3 fail_timeout=120s;
server localhost:10009 max_fails=3 fail_timeout=120s;
}
Server
{
keepalive_requests 120; #单连接请求上限次数。
listen 10000 ssl default http2;
server_name test.com;
#中间还有一些SSL的配置就不贴出来了,只贴重要的信息
location / {
#begin 最终就是添加了这两个配置,表示Nginx发现返回404时做一下重试,下面的10是重试次数,这个还可以添加配置 http_502等等,表示遇到那些情况也做一下重试。
proxy_next_upstream error timeout http_404;
proxy_next_upstream_tries 10;
#end
proxy_pass https://Web;
#start 下面这几条是配合上面的Map,Nginx在识别下列信息会转成WebSocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
#end
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 4s; #配置点1
proxy_read_timeout 600s; #配置点2,如果没效,可以考虑这个时间配置长一点Nginx发现该客户端在这个时间内如果没有心跳将断开连接
proxy_send_timeout 120s; #配置点3
}
}
}