先记住这个答案
HTTP Server 继承底层网络服务器的事件语义,监听失败可能通过 error 事件报告,例如端口被占用的 EADDRINUSE 或权限相关的 EACCES。应在 listen 前注册错误处理,并在 listening 成功后才置为就绪;单独包一层 try/catch 无法捕获以后发生的异步事件。启动失败时记录受控诊断,清理已创建资源并以失败状态结束,或执行明确有界的恢复策略。不要遇到端口占用就自动杀其他进程,也不要静默随机换端口,让代理仍指向一个并未提供服务的地址。
- listen 调用返回与监听成功不是同一个时刻
- 同步参数异常和异步 error 要分别处理
- 恢复策略必须维持部署端口与就绪契约
为什么 try/catch 有时看起来完全没用
非法端口等参数错误可能在调用期间直接抛出,而系统绑定端口失败通常在之后通过事件报告。同步调用栈已经返回时,外面的 try/catch 不会重新进入,因此需要同时理解两条错误通道。
使用 Promise 包装启动过程时,应在调用 listen 之前安装一次性成功和失败监听,并在完成后移除这组临时监听。运行期需要的长期错误策略应另行保留,不能完成启动后就让后续 error 无人处理。
用两个受控服务器复现端口占用
测试中先让一个服务器绑定本机回环地址的随机空闲端口,再读取实际分配值。第二个服务器尝试绑定同一地址和端口,应得到 EADDRINUSE;这样不依赖开发机上恰好有某个服务占用固定端口。
测试应断言第二个实例没有进入就绪状态,并在 finally 中关闭第一个实例。这个验证只涉及测试自己创建的资源,不需要扫描并终止其他进程;权限错误则应在合适的隔离环境验证,不要修改系统权限来凑结果。
决定退出还是重试要看故障是否可恢复
若配置错误或权限不满足,盲目重试通常只会延长不可用时间,应直接报告具体错误类别。若确有可恢复的短暂条件,重试应有次数、退避与总期限,并保持未就绪状态。
端口是部署契约的一部分,静默换成另一个端口可能让进程看起来活着却无法接收代理流量。失败后还应关闭先前建立的数据库连接和定时器,避免资源句柄让一个启动失败的进程长期挂着。
容易答错的地方
- 在 listen 后立即输出服务启动成功
- 这只能证明已经调用 API,不能证明系统绑定完成。应把就绪日志和健康状态放到成功事件之后,并让失败路径输出非成功状态,否则监控和发布系统会得到相互矛盾的信息。
- 端口被占用就自动 kill 占用者
- 占用者可能是正确运行的实例或其他必要服务,自动终止会把配置冲突扩大为故障。应先识别部署拓扑和端口契约,由明确的进程管理流程处理替换,而不是在错误回调里越权清场。
面试官还会怎么问?
EACCES 一定是低端口权限问题吗?
不一定,权限限制与操作系统、绑定目标和运行环境有关。应保留错误码及受控地址信息,检查容器权限和配置;不能只凭错误名就建议提高整个服务权限。
error 监听器里再抛错会怎样?
新异常可能逃出当前处理边界并升级为进程级未捕获异常。错误处理函数应尽量简单可靠,明确选择清理与失败退出,避免因为格式化日志或恢复代码再次抛错而掩盖原始原因。
健康检查只检查进程存在够吗?
不够,进程存在但监听失败或关键依赖未准备好时仍无法服务。应区分存活与就绪,并让启动状态真实反映入口是否建立,避免把流量送到尚未启动成功的实例。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。