写作背景:
在目前的众多前端工程化CLI中大多都提供在本地启动静态服务来辅助开发中页面的渲染,这些个CLI在遇到本地的端口占用的时候往往采用端口自增来重新启动服务。我相信这个现象在你开发项目的过程中也一定遇到过,如果没有遇到那你有没有发现控制台抛出包含“EADDRINUSE”的错误呢?
涉及知识点:
在你在使用NodeJs做服务开发的过程中可能会遇到一个名为EADDRINUSE的错误,EADDRINUSE是error address in use的缩写,当我们重复启动同一个服务的时候会遭遇这个问题。意思就是已经有相同服务在监听这个端口、地址、处理器了,在NodeJs中文网的serverlisten模块下得知,处理这种问题的通常做法是延迟重试。下面是对应的示例代码:
server.on('error', (e) => { if (e.code === 'EADDRINUSE') { console.log('Address in use, retrying...'); setTimeout(() => { server.close(); server.listen(PORT, HOST); }, 1000); } }); 复制代码
实现分析:
通过上面的示例代码可以看出要想对端口实现自增后重启服务的效果的处理位置就在error的回调监听里面操作,当我们监听到抛出EADDRINUSE的错误后,将PORT自增后重新执行listen函数。同样在NodeJs中文网中提示:当且仅当在第一次调用 server.listen()
期间出现错误或调用 server.close()
时,才能再次调用 server.listen()
方法。 否则,将抛出 **ERR_SERVER_ALREADY_LISTEN **错误。
让启动服务不再尴尬:
- 构建一个基础的http服务:
const http = require('http'); let port = 3000; const server = http.createServer((request, response) => { response.writeHead(200, { 'Content-Type': 'text/plain;charset=utf-8', }); response.end('欢迎访问'); }); server.listen(port, () => { console.log(`> Local: http://localhost:${port}`); }); 复制代码
- 注册一个http服务失败的处理回调函数:
const onError = (e) => { if (e.code === 'EADDRINUSE') { // TODO } }; server.on('error', onError); 复制代码
- 在失败的回调函数中当判断错误码为EADDRINUSE对port进行自增:
const onError = (e) => { if (e.code === 'EADDRINUSE') { console.log(`Port ${port} is in use, trying another one...`); server.listen(++port); } else { console.log('其他错误:', e); } }; 复制代码
- 完善代码,在成功启动服务和遇到其他失败的情况将失败的监听移除,以为我们将不再需要:
const http = require('http'); let port = 3000; const server = http.createServer((request, response) => { response.writeHead(200, { 'Content-Type': 'text/plain;charset=utf-8', }); response.end('欢迎访问'); }); const onError = (e) => { if (e.code === 'EADDRINUSE') { console.log(`Port ${port} is in use, trying another one...`); server.listen(++port); } else { console.log('其他错误:', e); } }; server.listen(port, () => { server.removeListener('error', onError); console.log(`> Local: http://localhost:${port}`); }); 复制代码
扩展学习:
Vite就是使用这种方式来处理端口占用的构建工具,具体的代码可以参考vite项目的packages\vite\src\node\http.ts
模块中的httpServerStart()函数。