1. 问题背景与现象描述
最近接手了一个老项目的容器化迁移任务,这个周报管理系统采用JSP技术栈开发,原本运行在Windows环境下一切正常。但在使用Docker部署到Linux服务器后,系统虽然能正常启动运行,但所有中文内容都变成了类似"æ´å°±å»ºç«"这样的乱码。
这种情况在传统Java Web项目迁移到容器环境时相当常见,特别是在使用较老技术栈(如Tomcat 7 + MySQL 5.7)的情况下。乱码问题看似简单,实则可能涉及多个环节的编码设置,需要系统性地排查和解决。
提示:中文乱码问题本质上都是字符编码不一致导致的,关键是要找到整个数据流转过程中哪个环节的编码设置出了问题。
2. 乱码问题全面排查
2.1 应用层编码检查
首先确认应用本身的编码配置是否完整:
-
JSP页面编码声明:检查所有JSP文件是否包含
<%@ page contentType="text/html;charset=UTF-8"%>声明。老项目有时会遗漏这个设置。 -
web.xml过滤器配置:查看是否配置了CharacterEncodingFilter,这是Spring项目中确保请求/响应使用UTF-8编码的标准做法:
xml复制<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
- HTTP响应头检查:使用浏览器开发者工具检查响应头中是否包含
Content-Type: text/html;charset=UTF-8。
2.2 Tomcat容器编码设置
即使应用配置了UTF-8,Tomcat容器本身的编码设置也至关重要:
- URI编码问题:Tomcat默认使用ISO-8859-1解码URL,需要在server.xml中显式配置:
xml复制<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8"
useBodyEncodingForURI="true"/>
- JVM默认编码:在Dockerfile中设置JVM参数确保使用UTF-8:
dockerfile复制ENV JAVA_OPTS="-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"
- 日志输出编码:检查Tomcat日志文件的编码是否一致,避免日志乱码影响问题排查。
2.3 MySQL数据库编码配置
数据库是乱码问题的重灾区,特别是MySQL 5.7版本:
- 服务端字符集:启动MySQL容器时需要指定字符集参数:
yaml复制command: >
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--init-connect='SET NAMES utf8mb4'
- 数据库创建脚本:确认建表语句中显式指定了字符集:
sql复制CREATE DATABASE weekly DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
- 表字段检查:使用
SHOW FULL COLUMNS FROM table_name确认各字段的字符集设置。
2.4 JDBC连接参数优化
老版本MySQL驱动对连接参数比较敏感:
- 简化连接字符串:去掉不必要的参数,保留核心配置:
code复制jdbc:mysql://mysql:3306/weekly?useUnicode=true&characterEncoding=utf-8&useSSL=false
-
驱动版本匹配:确保使用的mysql-connector-java版本与MySQL服务端版本兼容。
-
连接池配置:如果使用连接池(如HikariCP),检查是否有额外的字符集设置。
3. 完整解决方案实施
3.1 Dockerfile优化配置
基于tomcat:7-jre8镜像的完整Dockerfile配置:
dockerfile复制FROM tomcat:7-jre8
LABEL maintainer="your_name@example.com"
# 清理默认应用
RUN rm -rf /usr/local/tomcat/webapps/ROOT
# 部署应用
COPY ./webapp /usr/local/tomcat/webapps/weekly
# 编码设置
ENV JAVA_OPTS="-Dfile.encoding=UTF-8 -Dsun.jnu.encoding=UTF-8"
RUN sed -i 's/<Connector port="8080"/<Connector port="8080" URIEncoding="UTF-8" useBodyEncodingForURI="true"/' \
/usr/local/tomcat/conf/server.xml
EXPOSE 8080
CMD ["catalina.sh", "run"]
3.2 docker-compose.yml完整配置
yaml复制version: '3.8'
services:
mysql:
image: mysql:5.7.44
container_name: weekly_mysql
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: weekly
MYSQL_USER: weekly_user
MYSQL_PASSWORD: userpass
ports:
- "3308:3306"
volumes:
- ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
- mysql_data:/var/lib/mysql
command: >
--character-set-server=utf8mb4
--collation-server=utf8mb4_unicode_ci
--init-connect='SET NAMES utf8mb4'
--skip-character-set-client-handshake
tomcat:
build: .
container_name: weekly_tomcat
depends_on:
mysql:
condition: service_healthy
ports:
- "8082:8080"
volumes:
- tomcat_logs:/usr/local/tomcat/logs
volumes:
mysql_data:
tomcat_logs:
3.3 数据库初始化脚本示例
init.sql文件内容:
sql复制CREATE DATABASE IF NOT EXISTS weekly
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE weekly;
CREATE TABLE IF NOT EXISTS reports (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
content TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4. 部署验证与问题排查
4.1 部署流程
- 构建并启动服务:
bash复制docker-compose up -d --build
- 检查容器状态:
bash复制docker-compose ps
- 查看日志:
bash复制docker-compose logs -f tomcat
4.2 验证步骤
- 数据库字符集验证:
bash复制docker exec -it weekly_mysql mysql -uweekly_user -p
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';
- 应用编码验证:
- 访问应用页面,查看中文显示
- 检查浏览器接收的响应头Content-Type
- 提交包含中文的表单,检查数据库存储
- 全链路测试:
- 从页面输入→HTTP传输→应用处理→数据库存储→查询返回→页面展示的全流程中文测试
4.3 常见问题排查
- 部分页面仍显示乱码:
- 检查特定JSP页面是否缺少<%@ page指令
- 确认静态资源(如JS、CSS)的编码设置
- 数据库查询结果乱码:
- 确认连接字符串参数正确
- 检查MySQL用户权限是否允许设置字符集
- 日志文件乱码:
- 设置Tomcat日志编码:在logging.properties中添加
code复制java.util.logging.ConsoleHandler.encoding = UTF-8
5. 经验总结与最佳实践
在实际解决这个问题的过程中,我总结了以下几点经验:
-
统一编码标准:整个应用栈(浏览器→Tomcat→应用→JDBC→MySQL)必须统一使用UTF-8编码,任何一个环节不匹配都可能导致乱码。
-
MySQL版本选择:
- 5.7版本建议使用utf8mb4字符集
- 8.0+版本默认就是utf8mb4,但需要注意collation设置
- 容器化时的特殊考虑:
- 基础镜像的语言环境设置(locale)
- 终端连接的编码设置(如通过exec进入容器时的LANG环境变量)
- 日志文件的编码处理
- 测试策略:
- 准备包含各种语言字符(中文、日文、特殊符号等)的测试数据
- 测试增删改查全流程
- 检查数据库底层存储的实际字节数据
- 监控与维护:
- 定期检查数据库字符集设置
- 新表创建时显式指定字符集
- 升级MySQL版本时的字符集兼容性测试
这个案例的解决过程让我深刻体会到,乱码问题虽然表象简单,但需要系统性地排查整个数据流转链路。特别是在容器化环境中,各个环节的默认设置可能与本地开发环境不同,更需要仔细验证每个环节的编码配置。