记一次折腾后台nodejs服务的经历

avatar
作者
猴君
阅读量:0

前言

之前写了一个基于websocket的你画我猜小游戏,优势在于无广告、不限制参与人数,并在gitee上开源了:https://gitee.com/bychang/draw-and-guess。当时租了一年的云服务器,现在快到期了,所以打算迁移到自己的服务器上。

不过由于自己的服务器用了https、网站服务是部署在docker里的、并且端口不能随便开放,折腾了好半天,终于算是能用了(尽管可能不那么优雅hh)。因此写一篇博客记录一下踩坑历史。

背景

  • 服务器只有两个对外开放的端口,都在2000以上。防火墙会将外部访问服务器7777端口的请求转发到服务器的2345端口,访问6666端口的请求转发到服务器的1999端口。
  • 网站服务用的github上的docker-compose-lamp项目,集成好了apache、php、mysql等环境。
  • 在apache(版本2.4.59)的config那里配置了四级域名转发,主机1999端口映射到容器443端口,conf文件类似如下:
ServerName 172.18.0.5 SSLEngine on SSLCertificateFile /etc/apache2/ssl/xxx_yyy_com.pem SSLCertificateKeyFile /etc/apache2/ssl/xxx_yyy_com.key  <VirtualHost *:443>     DocumentRoot ${APACHE_DOCUMENT_ROOT}/default     ServerName draw-and-guess.xxx.yyy.com 	<Directory ${APACHE_DOCUMENT_ROOT}/default> 		AllowOverride all 	</Directory> </VirtualHost> 
  • 用vue写了个前端,用js写了个后端,前后端websocket逻辑分别为:
//前端 ... import { io } from 'socket.io-client'; ... const SERVER_ADDR = 'https://draw-and-guess.xxx.yyy.com:7777/'; const socket = ref(io(SERVER_ADDR, { transports: ['websocket'] })); ...  //后端 var app = require('http').createServer(); var io = require('socket.io')(app); app.listen(2345,'127.0.0.1') ... 

预期效果与现有效果

预期效果:玩家访问https://draw-and-guess.xxx.yyy.com:6666时,会自动和https://draw-and-guess.xxx.yyy.com:7777建立websocket连接,从而可以正常开始游戏。

现有效果:网站可以正常打开,但是建立连接时浏览器控制台报错 WebSocket connection to 'wss://draw-and-guess.xxx.yyy.com:7777/socket.io/?EIO=4&transport=websocket' failed,没有进一步的报错信息。服务器后端js控制台也没有显示报错信息。

尝试1

先确保服务器可以收到消息:nc -lk 2345然后打开网站,发现确实有显示。然后看了一下socket.io官方的排错教https://socket.io/docs/v4/troubleshooting-connection-issues/,说是用curl可以检测一下。测试curl "https://draw-and-guessd.xxx.yyy.com:7777/socket.io/?EIO=4&transport=polling"发现回显是curl: (35) error:0A00010B:SSL routines::wrong version number。应该是服务器端2345没有tls,所以无法连接。

尝试2

那么能不能用http呢?在curl测试curl "http://draw-and-guessd.xxx.yyy.com:7777/socket.io/?EIO=4&transport=polling"是可以连接的,但是浏览器会报错Mixed Content: The page at 'https://draw-and-guess.xxx.yyy.com:6666/' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://draw-and-guess.xxx.yyy.com:7777/socket.io/?EIO=4&transport=websocket'. This request has been blocked; this endpoint must be available over WSS.。所以是https场景下不能使用http做websocket了。之前用华为云服务器的时候因为就没弄https,所以http是可以的。

尝试3

那么既然1999端口能提供tls,是否可以也把websocket放在这个端口呢?显然不行,因为docker在做1999->443端口映射的时候是跑了一个docker-proxy程序的,这个端口就被占用了,没法再跑websocket。

尝试4

能否在2345端口再开一个tls服务,然后处理websocket?查了下发现是可以的,只需要启用proxy模块,然后添加转发规则就行了。首先先进入apache的docker,启用proxy和wstunnel模块:

cd /etc/apache2/mods-enabled ln -s ../mods-available/proxy.load proxy.load ln -s ../mods-available/proxy.conf proxy.conf ln -s ../mods-available/proxy_wstunnel.load proxy_wstunnel.load 

然后修改config

<VirtualHost *:443>     ProxyPass /socket.io ws://127.0.0.1:2345/socket.io     ProxyPassReverse /socket.io ws://127.0.0.1:2345/socket.io     DocumentRoot ${APACHE_DOCUMENT_ROOT}/draw-and-guess/dist     ServerName draw-and-guess.xxx.yyy.com 	<Directory ${APACHE_DOCUMENT_ROOT}/draw-and-guess/dist> 		AllowOverride all 	</Directory> </VirtualHost>  

其中的ProxyPass 和ProxyPassReverse用来转发。

结果发现apache错误日志文件报错:

[proxy:error] [pid 298] (111)Connection refused: AH00957: WS: attempt to connect to 127.0.0.1:2345 (127.0.0.1:2345) failed [proxy_wstunnel:error] [pid 298] [client 192.3.118.133:42940] AH02452: failed to make connection to backend: 127.0.0.1 

原来是因为apache跑在docker里,而2345端口是在宿主机监听的,docker里2345没有运行服务。

也没法把宿主机2345和容器2345绑定,这样又回到了docker-proxy的端口占用问题。

尝试5

本来顺理成章地可以在apache的docker里启动node后端,但当时想的是一种更为“优雅”的方法,就是再启动一个nodejs的docker和apache联动。试了下似乎不管怎么写command,启动以后都自己退了。折腾半天docker compose,略过不表。

尝试6

最后祭出大杀器,直接在compose配置文件把宿主机的nodejs映射到apache容器里,然后在容器内部运行js后端。为了保证控制台退出可用,研究了下screen里启动docker后怎么在不退出docker的情况下退出screen,无果。也研究了下docker exec -d xxxx xxx.sh来试图运行一个后台执行后端程序的docker,也失败了。

最后直接在宿主机开一个screen,里面进入apache docker,手动启动后端,再直接鼠标关了终端才圆满解决(至少服务可以用了)hhh。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!