ECONNREFUSED 连接被拒绝怎么排查?5 种原因一次讲清(含 Docker 场景)
现象:各种语言,同一个错
# Node.js
Error: connect ECONNREFUSED 127.0.0.1:3306
at TCPConnectWrap.afterConnect (node:net:1607:16)
# curl
curl: (7) Failed to connect to 127.0.0.1 port 6379: Connection refused
# Java
java.net.ConnectException: Connection refused (Connection refused)
一行结论:网络能到达那台机器,但你连的那个端口上没有任何进程在监听。操作系统直接回了个 RST(reset)拒绝连接——注意它和「超时」的区别:超时(ETIMEDOUT)是包根本到不了或被丢,拒绝(REFUSED)是到了但没人应答,这个区分直接决定排查方向。
| 报错 | 含义 | 大方向 |
|---|---|---|
| ECONNREFUSED | 到了,端口没人听 | 查服务/端口 |
| ETIMEDOUT | 包没到或被丢 | 查网络/防火墙 |
五种原因,按概率排序
1. 服务压根没起来(最常见)
systemctl status mysql # Linux 服务
docker ps # 容器在跑吗?重启过吗?
brew services list # macOS
容器退出后自动重启失败是高频场景:docker ps -a 里看 STATUS 是 Restarting (1) 5 seconds ago 还是 Up。前者说明容器在崩溃循环,端口自然连不上。
2. 端口记错了
各服务默认端口速查:
| 服务 | 默认端口 |
|---|---|
| MySQL | 3306 |
| PostgreSQL | 5432 |
| Redis | 6379 |
| MongoDB | 27017 |
| Elasticsearch | 9200 |
| RabbitMQ | 5672 |
两个易错点:Redis 配置里写的是 port 6379,但你用 --port 参数覆盖过;MySQL 改过 my.cnf 的 port=。以实际监听为准,别以记忆为准:
ss -tlnp | grep -E '3306|6379' # Linux
lsof -nP -iTCP:3306 -sTCP:LISTEN # macOS
3. 服务只监听了 127.0.0.1,你从别的机器连
ss -tlnp | grep 5432
# LISTEN 127.0.0.1:5432 ← 只接受本机连接
# LISTEN 0.0.0.0:5432 ← 接受任意来源
# LISTEN [::]:5432 ← IPv6 任意来源
如果是 127.0.0.1:端口,远程连接一律拒绝。修法按服务改监听地址:MySQL 注释掉 bind-address=127.0.0.1、PostgreSQL 改 listen_addresses = '*'、Redis 改 bind 0.0.0.0(同时务必配 requirepass,Redis 裸奔公网是勒索重灾区)。
4. Docker 场景:localhost 在容器里不是宿主机
容器内连宿主机上的服务,127.0.0.1 指的是容器自己,不是宿主机:
# 容器里的应用连数据库——两种正确姿势
# 姿势一:都在 docker network 里,用服务名(推荐)
spring.datasource.url: jdbc:mysql://interview-mysql:3306/mydb
# 姿势二:容器连宿主机上的服务
# macOS/Windows Docker Desktop 用专属域名:
jdbc:mysql://host.docker.internal:3306/mydb
# Linux 宿主机(Docker 20.10+)加 extra_hosts:
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
反过来,宿主机连容器:必须依赖 docker run -p 127.0.0.1:3306:3306 这样的端口映射,没映射 -p,宿主机上 curl localhost:3306 必然 ECONNREFUSED。
5. 防火墙主动 REJECT
iptables 的 REJECT 规则会回 RST,表现和「没人监听」一模一样(DROP 才表现为超时):
sudo iptables -L -n | grep -i reject
云服务器还有一层安全组——阿里云/腾讯云控制台里的安全组规则没放行端口,表现也可能是拒绝或超时,且服务器内部 ss 看一切正常。这是「本地正常、上云就连不上」的经典原因。
排查路线(从下往上三层)
# 第一层:端口上有人听吗?
ss -tlnp | grep <端口>
# 第二层:本机能连通吗?
nc -zv 127.0.0.1 <端口>
# 第三层:目标机器/容器网络里能连通吗?
docker exec -it <容器> sh -c 'nc -zv <目标地址> <端口>'
第一层没人听 → 原因 1/2;本机能通远程不能 → 原因 3/5;容器里连不上 → 原因 4。三层走完,问题基本定位。
顺手区分:ECONNREFUSED vs EADDRINUSE
一个端口的两端视角:连接别人时被拒是 ECONNREFUSED(对面没人听);自己监听时被占是 EADDRINUSE(这端口已被别人占了)。后者的排查看这篇:端口被占用怎么办?EADDRINUSE 报错排查。
IPv6 的 localhost 坑(::1 vs 127.0.0.1)
双栈机器上 localhost 可能优先解析到 ::1。若服务只 listen(3306, '127.0.0.1'),客户端走 IPv6 连 localhost 会 ECONNREFUSED,同时 ss 能看到 IPv4 监听、mysql -h 127.0.0.1 正常。处理:有意绑定 :: / 0.0.0.0,或客户端 URL 写死 127.0.0.1。先打印 DNS 结果(getent hosts localhost),再怪服务。
Kubernetes 与 Service DNS
集群内连 my-svc.my-ns.svc.cluster.local:8080 被拒,常见是:Service selector 匹配不到 Ready Pod、端口名写错、或连了已回收的 Pod IP。优先用 Service DNS + 命名端口;看 kubectl get endpoints——Deployment 还在但 Endpoints 为空,就是集群版的「容器崩溃循环」。NetworkPolicy 用 REJECT(不是 DROP)时,表现也是 refused。
TCP 通了应用仍不通
进程在听,业务仍可能废:明文客户端打到只收 TLS 的端口、只开 HTTP/2、或反代先接 TCP 再 502。nc -zv 成功后,用 curl 或浏览器里的 API 在线测试 打真实协议。Compose healthcheck 失败而端口仍开着也很常见——编排把容器标 unhealthy 后停止路由,裸 TCP 却还能连上。
给同事画网段 / 发布端口时,可用 CIDR 子网计算,并用 docker run 转 compose 对齐 ports: / expose:,减少「docker run 能连、compose 被拒」的考古。
排查清单
docker ps/systemctl status— 服务活着吗?(含崩溃循环检查)ss -tlnp | grep <端口>— 实际监听端口和地址是什么?- 监听地址是
127.0.0.1还是0.0.0.0/::?远程与 IPv6 客户端要对上绑定 - Docker 里连宿主机服务:用
host.docker.internal,不是 localhost - 云服务器查安全组规则,服务器内查 iptables REJECT
- 分清 REFUSED(到了没人听)和 TIMEOUT(到不了),方向完全不同
- TCP 通后用真实客户端验 TLS/HTTP;k8s 确认 Endpoints 非空
排查注记
开头先摆 REFUSED 和 TIMEOUT 的对照表再讲五种原因,是因为这个区分直接决定排查方向:包到了没人听就查服务,包到不了就查网络——方向放反,两层排查全白做。三层排查路线和原因编号是配套设计的:第一层没人听落在原因 1/2,本机能通远程不能落在原因 3/5,容器里连不上落在原因 4。防火墙那条特意指出 REJECT 回 RST、表现和「没人监听」一模一样,只有 DROP 才表现为超时——这是 REFUSED 被误判成服务问题的常见来源。
ToolVault 工具匣 出品的开发者指南。相关工具:Dockerfile 生成器、docker run 转 compose、CIDR 子网计算、API 在线测试。相关阅读:端口被占用 EADDRINUSE 排查。访问 首页 查看更多开发者工具。
相关工具
相关文章
Linux crontab 定时任务实战:命令、日志与排查
crontab 不只是写表达式:三个必会命令、/etc/cron.d 与用户 crontab 的区别、任务没跑的五步排查法、PATH/时区/% 转义、MAILTO,以及秒级任务的正确解法。
docker run 转 docker-compose 完全指南:为什么转、怎么转、参数对照表
把 docker run 命令迁移到 docker-compose.yml 的完整指南:compose 相比裸 run 的五大优势、参数逐项对照表(端口/卷/环境变量/重启策略/网络)、常见坑(裸 -e 透传、匿名卷、--rm 语义),附在线转换器用法。
Permission denied (publickey) 怎么解决?Git 推送失败的 6 种原因与修复
git clone/push 报 Permission denied (publickey) fatal: Could not read from remote repository?覆盖公钥没生成、没加载进 agent、没添加到 GitHub/Gitee、多账号配错密钥、deploy key 权限、remote URL 写错六种根因,附 ssh -v 诊断方法。