Docker化MySQL依赖Web应用遇java.net.ConnectException连接拒绝问题
我有一个依赖MySQL的Web应用,想把它Docker化来学习Docker并提升可分享性(他人无需手动搭建MySQL即可使用)。以下是我的操作步骤:
步骤1:编写Dockerfile
在项目根目录创建Dockerfile,内容如下:
FROM amazoncorretto:17-alpine-jdk WORKDIR /app COPY target/spring_bootstrap-0.0.1-SNAPSHOT.jar . EXPOSE 8080 CMD ["java", "-jar", "spring_bootstrap-0.0.1-SNAPSHOT.jar"]
步骤2:编写docker-compose.yml
因需同时运行应用和MySQL组件,编写了如下docker-compose.yml:
version: '3' services: app: build: context: . dockerfile: Dockerfile ports: - "8080:8080" depends_on: - db environment: SPRING_DATASOURCE_URL: jdbc:mysql://db:3307/users_db?useSSL=false&serverTimezone=UTC SPRING_DATASOURCE_USERNAME: pp_user SPRING_DATASOURCE_PASSWORD: pp_user db: image: mysql:latest ports: - "3307:3306" environment: MYSQL_DATABASE: users_db MYSQL_USER: pp_user MYSQL_PASSWORD: pp_user MYSQL_ROOT_PASSWORD: root_password
注:使用3307端口是因为3306被主机占用(外部未运行MySQL)。
步骤3:注释application.properties中的数据源配置
# connection properties #spring.datasource.url=jdbc:mysql://localhost:3306/users_db?useSSL=false&serverTimezone=UTC #spring.datasource.username=pp_user #spring.datasource.password=pp_user
步骤4:执行clean和package生成最新jar包
步骤5:进入项目目录运行docker-compose up
执行后MySQL容器正常启动,但应用启动失败并退出,日志抛出如下错误:
bootstrap_fetch-app-1 | Caused by: java.net.ConnectException: Connection refused bootstrap_fetch-app-1 | at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na] bootstrap_fetch-app-1 | at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672) ~[na:na] bootstrap_fetch-app-1 | at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:547) ~[na:na] bootstrap_fetch-app-1 | at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:602) ~[na:na] bootstrap_fetch-app-1 | at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327) ~[na:na] bootstrap_fetch-app-1 | at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na] bootstrap_fetch-app-1 | at com.mysql.cj.protocol.StandardSocketFactory.connect(StandardSocketFactory.java:153) ~[mysql-connector-j-8.0.32.jar!/:8.0.32] bootstrap_fetch-app-1 | at com.mysql.cj.protocol.a.NativeSocketConnection.connect(NativeSocketConnection.java:63) ~[mysql-connector-j-8.0.32.jar!/:8.0.32] bootstrap_fetch-app-1 | ... 51 common frames omitted bootstrap_fetch-app-1 | bootstrap_fetch-app-1 exited with code 1
你的问题主要出在两个地方:
1. JDBC URL中的端口配置错误
Docker Compose的服务之间通过内部网络通信时,使用的是容器自身暴露的端口,而不是映射到主机的端口。你的MySQL容器内部端口是3306(MySQL默认端口),3307:3306只是把主机的3307端口映射到容器的3306端口,供外部主机访问用的。
所以要修改app服务的SPRING_DATASOURCE_URL,把端口从3307改成3306:
SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/users_db?useSSL=false&serverTimezone=UTC
2. depends_on不保证MySQL服务就绪
depends_on: [db]仅仅保证app容器在db容器之后启动,但不保证MySQL服务已经完成初始化并可以接受连接。MySQL容器启动后,通常需要几秒到十几秒的时间完成数据库初始化、用户创建等操作,这时候Spring Boot应用已经尝试连接,就会抛出连接拒绝错误。
解决这个问题有几种常用方式:
方式一:在Docker Compose中配置健康检查
给db服务添加健康检查,让app等待MySQL健康检查通过后再启动:
services: app: # ... 其他配置 depends_on: db: condition: service_healthy # 等待db服务健康检查通过 db: image: mysql:latest ports: - "3307:3306" environment: MYSQL_DATABASE: users_db MYSQL_USER: pp_user MYSQL_PASSWORD: pp_user MYSQL_ROOT_PASSWORD: root_password healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"] timeout: 20s retries: 10
方式二:在Spring Boot中配置连接重试
添加Spring Boot的重试依赖,并配置数据源的重试机制:
- 在
pom.xml中添加依赖:
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
- 在
application.properties或环境变量中添加配置:
# 连接超时和重试配置 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.maximum-pool-size=10 # 启用重试 spring.retry.enabled=true spring.datasource.hikari.initialization-fail-timeout=0 # 无限等待直到连接成功
方式三:使用等待脚本
在Dockerfile中添加wait-for-it.sh脚本,让应用启动前先等待MySQL端口就绪:
- 下载
wait-for-it.sh到项目根目录,修改Dockerfile:
FROM amazoncorretto:17-alpine-jdk WORKDIR /app COPY wait-for-it.sh . RUN chmod +x wait-for-it.sh COPY target/spring_bootstrap-0.0.1-SNAPSHOT.jar . EXPOSE 8080 CMD ["./wait-for-it.sh", "db:3306", "--", "java", "-jar", "spring_bootstrap-0.0.1-SNAPSHOT.jar"]
内容的提问来源于stack exchange,提问作者Sergey Zolotarev

