解决 java.net.ConnectException: Connection refused 报错

前言:一个高频却常被误解的异常

在 Java 企业级应用开发中,无论是构建 Web 后端服务、微服务架构,还是进行数据集成与批处理任务,开发者几乎不可避免地会遇到如下异常堆栈:

java 复制代码
java.net.ConnectException: Connection refused: connect
    at java.base/sun.nio.ch.Net.connect0(Native Method)
    at java.base/sun.nio.ch.Net.connect(Net.java:589)
    at java.base/sun.nio.ch.Net.connect(Net.java:578)
    at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:583)
    at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
    at java.base/java.net.Socket.connect(Socket.java:751)
    ...

该异常通常出现在尝试建立 TCP 连接时------例如通过 JDBC 连接 MySQL、调用 RESTful API、连接 Redis 缓存、或与消息队列(如 RabbitMQ、Kafka)通信。尽管其字面意思看似直白("连接被拒绝"),但许多开发者在初次遭遇时仍会陷入困惑:是代码写错了?是网络不通?还是服务宕机了?


一、问题本质:操作系统层面的"拒绝"信号

1.1 异常来源并非 Java 虚拟机

首先必须明确:java.net.ConnectException 是 Java 对底层操作系统返回错误码的封装。当 Java 应用调用 Socket.connect() 方法时,JVM 会委托操作系统内核发起 TCP 连接请求。若内核在尝试建立连接过程中收到特定的网络响应(如 RST 包),便会向 JVM 返回一个错误码(在 Linux/Unix 系统中为 ECONNREFUSED,在 Windows 中为 WSAECONNREFUSED),JVM 再将其转换为 ConnectException 抛出。

因此,该异常反映的是网络层或传输层的问题,而非应用逻辑错误

1.2 TCP 三次握手中的"拒绝"机制

理解此异常的关键在于掌握 TCP 协议的行为:

  • 当客户端向目标主机的某端口发送 SYN(同步)包以发起连接时;
  • 若目标主机的操作系统内核发现没有任何进程正在监听该端口
  • 内核将立即向客户端回送一个 RST(Reset)包,表示"此端口无服务,请勿重试";
  • 客户端操作系统收到 RST 后,向上层应用(即 Java 程序)报告"Connection refused"。

核心结论
Connection refused = 目标主机可达 + 目标端口无服务监听

这与 Connection timed out(连接超时)有本质区别:

  • Timeout:SYN 包发出后未收到任何响应(可能因网络中断、防火墙丢包、主机宕机);
  • Refused:SYN 包已送达,但对方明确告知"此处无服务"。

二、根本原因分类与排查指南

根据多年工程实践经验,导致 Connection refused 的原因可归纳为以下五大类。

2.1 服务进程未启动(最常见原因)

这是本地开发、测试环境中占比最高的原因。服务未运行,自然无法监听端口。

🔧 Windows 系统操作指南

在 Windows 操作系统中,MySQL、PostgreSQL、Redis 等数据库服务通常以"Windows 服务"的形式安装和管理。如果服务未启动,则对应的端口(如 MySQL 默认 3306)将不会被监听,从而导致 Connection refused 错误。

图形化界面操作步骤(推荐初学者使用)
  1. 打开"服务"管理控制台

    • 按下键盘上的 Win + R 组合键,弹出"运行"对话框。
    • 在输入框中键入 services.msc,然后按回车键或点击"确定"。
    • 此时将打开"服务"窗口,其中列出了当前系统中所有已注册的服务。
  2. 查找目标数据库服务

    • 在服务列表中,滚动查找名称包含 MySQL 的条目。
    • 常见的服务名称包括:
      • MySQL80(MySQL 8.0 版本)
      • MySQL57(MySQL 5.7 版本)
      • MySQL(通用名称,多见于旧版安装)
      • MariaDB(MariaDB 数据库)
      • Redis(Redis 服务)
    • 如果你不确定具体名称,可以右键点击任意服务 → "属性",查看"路径到可执行文件"字段,确认是否为你安装的数据库程序。
  3. 检查服务状态并启动

    • 查看"状态"列。如果该列为空白,说明服务当前未运行
    • 右键点击该服务名称,在弹出的上下文菜单中选择"启动"。
    • 启动成功后,"状态"列将显示为"正在运行"。
    • 如果启动失败(例如提示"错误 1067:进程意外终止"),则需进一步检查数据库的日志文件(通常位于 C:\ProgramData\MySQL\MySQL Server X.X\Data\ 目录下的 .err 文件)。
  4. 设置开机自动启动(可选)

    • 为避免每次重启电脑后手动启动服务,可右键点击服务 → "属性"。
    • 在"启动类型"下拉菜单中选择"自动"。
    • 点击"应用"并"确定"。下次系统启动时,该服务将自动运行。
命令行方式(适用于脚本化或高级用户)

Windows 提供了强大的命令行工具来管理服务,无需依赖图形界面。

  1. 列出所有与 MySQL 相关的服务

    cmd 复制代码
    sc queryex type= service state= all | findstr /i "mysql"
    • sc(Service Control)是 Windows 内置的服务管理命令。
    • 此命令将输出所有服务名中包含"mysql"(不区分大小写)的服务及其状态。
  2. 启动指定服务

    cmd 复制代码
    net start MySQL80
    • MySQL80 替换为你实际的服务名称。
    • 成功启动后,命令行会显示"MySQL80 服务正在启动... MySQL80 服务已经启动成功。"
  3. 停止服务

    cmd 复制代码
    net stop MySQL80
  4. 查询服务详细信息

    cmd 复制代码
    sc qc MySQL80
    • 此命令可查看服务的可执行文件路径、依赖关系、启动账户等关键信息,对排错非常有帮助。

🔧 macOS 系统操作指南

macOS 上的数据库服务管理方式取决于其安装途径。主流方式有两种:Homebrew (开发者首选)和 官方 .dmg 安装包

A. 通过 Homebrew 安装的 MySQL(强烈推荐)

Homebrew 是 macOS 上最流行的包管理器,它能自动处理依赖、配置和后台服务管理。

  1. 检查 Homebrew 服务状态

    打开"终端"(Terminal),输入以下命令:

    bash 复制代码
    brew services list
    • 该命令会列出所有由 Homebrew 管理的后台服务。

    • 输出示例:

      复制代码
      Name    Status  User     Plist
      mysql   stopped          /Users/yourname/Library/LaunchAgents/homebrew.mxcl.mysql.plist
      redis   started yourname /Users/yourname/Library/LaunchAgents/homebrew.mxcl.redis.plist
    • Status 列显示 stopped 表示服务未运行。

  2. 启动 MySQL 服务

    bash 复制代码
    # 启动服务,并设置为登录时自动启动(仅对当前用户)
    brew services start mysql
    
    # 或者,仅启动一次(不设为自动启动)
    brew services run mysql
    • start 命令会创建一个 launchd 配置文件(.plist),确保服务在用户登录时自动启动。
    • run 命令则只在当前终端会话中运行服务,关闭终端后服务也会停止。
  3. 停止服务

    bash 复制代码
    brew services stop mysql
  4. 验证服务是否正常运行

    bash 复制代码
    # 检查 MySQL 进程
    ps aux | grep mysqld
    
    # 尝试连接数据库(默认 root 用户无密码,首次安装后建议运行 mysql_secure_installation)
    mysql -u root
  5. 查看日志(排错必备)

    Homebrew 安装的 MySQL 日志通常位于:

    bash 复制代码
    cat /opt/homebrew/var/mysql/$(hostname).err
    # 或 Intel Mac:
    cat /usr/local/var/mysql/$(hostname).err
B. 通过官方 .dmg 安装包安装的 MySQL

这种方式会将 MySQL 安装到 /usr/local/mysql/ 目录,并提供一个系统偏好设置面板。

  1. 图形化启动方式

    • 打开"系统设置"(System Settings)或旧版的"系统偏好设置"(System Preferences)。
    • 在底部或侧边栏找到并点击 "MySQL" 图标。
    • 在弹出的窗口中,你会看到一个大按钮:"Start MySQL Server"。
    • 点击该按钮,状态会从 "MySQL Server is stopped." 变为 "MySQL Server is running."。
  2. 命令行启动/停止

    官方安装包提供了便捷的脚本:

    bash 复制代码
    # 启动 MySQL
    sudo /usr/local/mysql/support-files/mysql.server start
    
    # 停止 MySQL
    sudo /usr/local/mysql/support-files/mysql.server stop
    
    # 重启 MySQL
    sudo /usr/local/mysql/support-files/mysql.server restart
    
    # 检查状态
    sudo /usr/local/mysql/support-files/mysql.server status
    • 这些命令本质上是调用了 mysqld_safe 脚本来管理进程。
    • 注意 :需要 sudo 权限,因为服务通常以 _mysql 用户身份运行。
  3. 添加 PATH(方便使用 mysql 命令)

    为了能在任意目录下直接使用 mysql 命令,需要将 MySQL 的 bin 目录加入环境变量。

    • 编辑你的 shell 配置文件(如 ~/.zshrc~/.bash_profile):

      bash 复制代码
      echo 'export PATH="/usr/local/mysql/bin:$PATH"' >> ~/.zshrc
      source ~/.zshrc
    • 之后即可直接在终端输入 mysql -u root -p 进行连接。


🔧 Linux 系统操作指南

Linux 发行版众多,但现代主流发行版(如 Ubuntu 16.04+、CentOS 7+、Debian 8+)均采用 systemd 作为初始化系统和服务管理器。旧版系统(如 CentOS 6)则使用 SysV init

A. 使用 systemd(现代 Linux 发行版标准)

systemd 通过 systemctl 命令统一管理所有系统服务。

  1. 检查 MySQL 服务状态

    不同发行版对 MySQL 服务的命名略有差异:

    • Ubuntu/Debian 系 :服务名为 mysql
    • CentOS/RHEL/Fedora 系 :服务名为 mysqld

    执行以下命令之一:

    bash 复制代码
    # Ubuntu/Debian
    sudo systemctl status mysql
    
    # CentOS/RHEL
    sudo systemctl status mysqld
    • 关键观察点
      • Active: active (running):服务正在运行。
      • Active: inactive (dead):服务已停止。
      • 如果提示 Unit ... not found,说明 MySQL 未安装。
  2. 启动、停止、重启服务

    bash 复制代码
    # 启动服务
    sudo systemctl start mysql        # 或 mysqld
    
    # 停止服务
    sudo systemctl stop mysql
    
    # 重启服务(修改配置后常用)
    sudo systemctl restart mysql
    
    # 重新加载配置(不中断服务)
    sudo systemctl reload mysql
  3. 设置开机自启

    bash 复制代码
    # 启用开机自启
    sudo systemctl enable mysql
    
    # 禁用开机自启
    sudo systemctl disable mysql
    • enable 命令会在 /etc/systemd/system/multi-user.target.wants/ 目录下创建一个符号链接。
  4. 查看实时服务日志(排错神器)

    systemd 集成了日志系统 journald,可通过以下命令实时跟踪服务日志:

    bash 复制代码
    # 实时跟踪 MySQL 服务日志
    sudo journalctl -u mysql -f
    
    # 查看最近 100 行日志
    sudo journalctl -u mysql -n 100
    • 这比直接查看 /var/log/mysql/error.log 更方便,因为它包含了 systemd 的元数据(如时间戳、进程ID)。
B. 使用 SysV init(旧版 Linux 系统)

虽然已逐渐被淘汰,但在一些遗留系统中仍会遇到。

  1. 检查服务状态

    bash 复制代码
    sudo service mysql status
    # 或直接调用 init 脚本
    sudo /etc/init.d/mysql status
  2. 管理服务

    bash 复制代码
    # 启动
    sudo service mysql start
    
    # 停止
    sudo service mysql stop
    
    # 重启
    sudo service mysql restart
  3. 设置开机自启

    bash 复制代码
    # Ubuntu/Debian (使用 update-rc.d)
    sudo update-rc.d mysql defaults
    
    # CentOS 6 (使用 chkconfig)
    sudo chkconfig --level 35 mysql on

2.2 客户端连接配置错误

连接字符串(URL)中的 IP 地址、端口号或协议写错。

  • 常见错误示例

    • JDBC URL:jdbc:mysql://localhost:3360/testdb(端口应为 3306);
    • 主机名拼写错误:locahost127.0..1
    • 使用了错误的环境变量(如 DB_HOST=prod-db 但在 dev 环境运行)。
  • 排查重点

    • 检查 application.properties / application.yml
    • 核对 .env 文件或 Kubernetes ConfigMap;
    • 在 IDE 中打印完整的连接 URL(注意脱敏)。

2.3 服务绑定地址限制(Bind Address Issue)

服务虽已启动,但仅绑定到 127.0.0.1(loopback 接口),拒绝来自外部 IP 的连接。

  • 典型表现

    • 本机可通过 127.0.0.1:3306 连接 MySQL;
    • 但从同一局域网的另一台机器使用 192.168.1.100:3306 连接失败,报 Connection refused
  • 验证命令(在服务所在主机执行):

    bash 复制代码
    ss -tuln | grep :3306
    # 或
    netstat -tuln | grep :3306
    • 输出示例
      • tcp LISTEN 0 80 127.0.0.1:3306 0.0.0.0:*仅本机可连
      • tcp LISTEN 0 80 0.0.0.0:3306 0.0.0.0:*所有接口可连
  • 解决方案

    • MySQL :修改 my.cnf(位置通常为 /etc/mysql/my.cnf/etc/my.cnf),在 [mysqld] 部分添加或修改 bind-address = 0.0.0.0,然后重启服务。
    • Spring Boot :添加 server.address=0.0.0.0 到配置文件。
    • 自定义服务 :确保 ServerSocket 绑定到 0.0.0.0 或具体网卡 IP。

2.4 防火墙或安全组策略拦截

虽然服务在监听,但网络中间设备阻止了连接请求。

  • 本地防火墙

    • Windows:检查"Windows Defender 防火墙" -> "高级设置" -> "入站规则",确保有允许目标端口(如 3306)的规则。

    • Linux (UFW)

      bash 复制代码
      sudo ufw status verbose
      sudo ufw allow 3306/tcp
    • Linux (firewalld)

      bash 复制代码
      sudo firewall-cmd --list-ports
      sudo firewall-cmd --permanent --add-port=3306/tcp
      sudo firewall-cmd --reload
  • 云平台安全组(极易被忽视!):

    • 阿里云/腾讯云/AWS/Azure:登录控制台,找到你的云服务器实例,进入"安全组"配置页面。
    • 添加一条入站规则 :协议类型 TCP,端口范围 3306,授权对象 0.0.0.0/0(测试用)或你的办公 IP。

2.5 容器化环境网络配置问题

在 Docker、Kubernetes 等容器平台中,网络命名空间隔离增加了复杂性。

  • Docker 常见问题

    • 未使用 -p 参数映射端口:docker run mysql ❌ vs docker run -p 3306:3306 mysql ✅;
    • 容器间通信使用了宿主机 IP 而非 Docker 自定义网络中的服务名。
  • Kubernetes 问题

    • Service 的 targetPort 与 Pod 实际监听端口不一致;
    • Pod 未通过 Readiness Probe,Service 未将流量转发;
    • NetworkPolicy 限制了跨命名空间通信。
  • 排查命令

    bash 复制代码
    docker ps                          # 查看运行容器
    docker port <container_id>         # 检查端口映射
    kubectl get svc,pods -n <namespace> # 查看 K8s 服务状态

三、标准化排查流程(推荐顺序)

遵循 "由近及远、由内到外、先服务后网络" 的原则,逐步缩小问题范围。

步骤 1:确认目标服务是否真正运行并监听端口

操作位置:服务所在主机

bash 复制代码
# 检查进程
ps aux | grep <service_name>

# 检查端口监听(关键!)
ss -tuln | grep :<PORT>
# 或
netstat -tuln | grep :<PORT>
  • 若无输出 → 服务未启动或未监听该端口 → 启动服务。
  • 若输出显示 127.0.0.1:<PORT> → 仅本地可连 → 修改绑定地址。

步骤 2:核对客户端连接配置

操作位置:客户端代码/配置文件

  • 检查 JDBC URL、HTTP endpoint、Redis 连接字符串等;
  • 确保 IP、端口、协议(如 jdbc:mysql://)完全正确;
  • 注意环境变量覆盖问题(如 SPRING_DATASOURCE_URL)。

步骤 3:使用 telnetnc 测试 TCP 连通性

操作位置:客户端主机

bash 复制代码
telnet <IP> <PORT>
# 或
nc -vz <IP> <PORT>
  • 成功:屏幕变黑或显示服务 banner(如 MySQL 版本信息);
  • Connection refused:服务未监听(回到步骤 1);
  • Timeout:网络不通或防火墙拦截(进入步骤 4)。

💡 提示:Windows 可启用 Telnet 客户端功能,或使用 PowerShell 命令:

powershell 复制代码
Test-NetConnection -ComputerName 127.0.0.1 -Port 3306

🔍 步骤 4:检查防火墙与云安全组

操作位置:服务所在主机 + 云控制台

  • 临时关闭本地防火墙测试;
  • 登录云平台控制台,检查实例关联的安全组规则;
  • 确保入站规则允许 <PORT>/TCP 来自你的 IP 或 0.0.0.0/0(测试用)。

🔍 步骤 5:容器环境专项检查

操作位置:Docker Host / Kubernetes Cluster

  • 确认容器运行状态:docker ps / kubectl get pods
  • 检查端口映射:docker port <id>
  • 在容器内部测试连接:docker exec -it <container> telnet 127.0.0.1 <PORT>

四、预防措施与工程最佳实践

4.1 自动化依赖管理

  • 使用 docker-compose.yml 统一管理本地开发依赖(MySQL、Redis、Elasticsearch 等),避免手动启停遗漏。

    yaml 复制代码
    version: '3'
    services:
      mysql:
        image: mysql:8.0
        ports:
          - "3306:3306"
        environment:
          MYSQL_ROOT_PASSWORD: root

4.2 健康检查与启动探针

  • 在应用启动时加入数据库连接探针,失败时提供友好提示:

    java 复制代码
    @PostConstruct
    public void checkDatabaseConnection() {
        try (Connection conn = dataSource.getConnection()) {
            // 成功
        } catch (SQLException e) {
            log.error("无法连接数据库,请检查服务是否启动及配置是否正确", e);
            System.exit(1); // 或抛出自定义异常
        }
    }

4.3 配置分离与环境隔离

  • 使用 Spring Profiles、Kubernetes ConfigMap/Secrets 管理不同环境配置;
  • 避免硬编码连接信息,全部通过配置注入。

4.4 日志增强与可观测性

  • 在捕获 ConnectException 时,记录完整的连接目标(脱敏后):

    java 复制代码
    log.error("连接 {} 失败", maskUrl(url), e);
  • 集成 APM 工具(如 SkyWalking、Prometheus)监控服务依赖健康状态。

4.5 基础设施即代码(IaC)

  • 使用 Terraform、Ansible 管理云资源与安全组,确保网络策略可版本化、可审计。

最后提醒 :在生产环境中,切勿随意开放 0.0.0.0 绑定或关闭防火墙。安全与可用性需平衡,建议结合白名单、VPC 内网、TLS 加密等手段保障服务安全。

相关推荐
-南帝-1 小时前
行尾符格式转换问题(CRLF vs LF)如何快速解决(Agent)
java·ai
gumichef1 小时前
栈和队列(1)
开发语言·数据结构
小新同学^O^1 小时前
算法学习 --> 快速输入和输出
java·学习·算法
喜欢小苹果的码农1 小时前
Java动态多定时任务
java
无所事事O_o1 小时前
基于netty的websocket服务优化
java·websocket·netty·优化
2601_953465612 小时前
纯前端高性能!m3u8live.cn 重新定义 M3U8 在线播放与调试体验
开发语言·前端·javascript·m3u8
有趣灵魂2 小时前
Java Spring Boot根据Word模板和动态数据生成Word文件
java·spring boot·word·apache
云天AI实战派2 小时前
Python 智能体实战:从 0 搭建模块化 Agent 路由系统,落地小龙虾门店运营助手
开发语言·人工智能·python
tumu_C2 小时前
C++模板:Ret(Arg...)的相关
开发语言·c++·算法