跳到主要内容
DevOps2026-09-085 分钟阅读

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. 端口记错了

各服务默认端口速查:

服务默认端口
MySQL3306
PostgreSQL5432
Redis6379
MongoDB27017
Elasticsearch9200
RabbitMQ5672

两个易错点: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 被拒」的考古。

排查清单

  1. docker ps / systemctl status — 服务活着吗?(含崩溃循环检查)
  2. ss -tlnp | grep <端口> — 实际监听端口和地址是什么?
  3. 监听地址是 127.0.0.1 还是 0.0.0.0 / ::?远程与 IPv6 客户端要对上绑定
  4. Docker 里连宿主机服务:用 host.docker.internal,不是 localhost
  5. 云服务器查安全组规则,服务器内查 iptables REJECT
  6. 分清 REFUSED(到了没人听)和 TIMEOUT(到不了),方向完全不同
  7. TCP 通后用真实客户端验 TLS/HTTP;k8s 确认 Endpoints 非空

排查注记

开头先摆 REFUSED 和 TIMEOUT 的对照表再讲五种原因,是因为这个区分直接决定排查方向:包到了没人听就查服务,包到不了就查网络——方向放反,两层排查全白做。三层排查路线和原因编号是配套设计的:第一层没人听落在原因 1/2,本机能通远程不能落在原因 3/5,容器里连不上落在原因 4。防火墙那条特意指出 REJECT 回 RST、表现和「没人监听」一模一样,只有 DROP 才表现为超时——这是 REFUSED 被误判成服务问题的常见来源。


ToolVault 工具匣 出品的开发者指南。相关工具:Dockerfile 生成器、docker run 转 compose、CIDR 子网计算、API 在线测试。相关阅读:端口被占用 EADDRINUSE 排查。访问 首页 查看更多开发者工具。