ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

JDShop云上部署实战:从本地Demo到生产级Spring Boot应用

JDShop云上部署实战:从本地Demo到生产级Spring Boot应用 简介本资源是一套面向云服务初学者与Linux运维入门者的在线购物系统实战部署包聚焦JDShop项目从本地到云服务器的完整落地实践解决开发者在真实环境中配置Web服务、数据库及安全策略的核心难点。压缩包共175个文件含31个PHP后端逻辑文件、17个CSS样式文件、8个JS交互脚本、1个SQL建库脚本及78张PNG37张JPG界面截图直观呈现前后端结构与部署效果整体4.59MB轻量易下载适配学习型云服务器环境。已有190人学习下载资源突出实操导向提供可直接导入的数据库初始化脚本、标准化的includes目录下数据库连接配置模板、多套CSS样式文件如login.css、order.css、buycar.css等体现模块化前端设计以及清晰的目录层级与权限说明助力读者快速复现云上电商系统运行环境掌握LAMP栈部署、权限管理与基础安全加固要点。1. JDShop 不是京东商城而是你手头那个跑在本地 Tomcat 里、连数据库都还用 H2 的 Java Web 电商 Demo云上部署不是“一键迁移”而是把开发态、测试态、运维态全链条重置一遍JDShop 是一个典型的 Spring Boot Thymeleaf MyBatis MySQL 的轻量级在线购物系统教学项目GitHub 上常见于高校课程设计或 Java 入门实训非京东官方项目。它结构清晰、功能完整用户注册/登录、商品浏览、购物车、订单生成但默认配置全是开发友好型内嵌 H2 数据库、静态资源直读 classpath、无 HTTPS、无连接池、无日志分级、无健康检查端点。直接java -jar jdshop.jar跑通不等于能上云——云环境没有 localhost没有你本机的application-dev.yml更没有你 IDE 里点一下就重启的便利。真正卡住 80% 开发者的不是写代码而是把“能跑”变成“稳跑”MySQL 连不上404 静态资源Nginx 转发后登录态丢失健康探针一直 fail这些不是玄学是云原生基础设施对传统 Web 工程的硬性校验。本文面向已写出 JDShop 功能、正卡在“部署到阿里云 ECS 或腾讯云 CVM 后页面打不开”的 Java 工程师不讲 Docker 基础概念只拆解从git clone到curl -I https://yourdomain.com/health返回 200 的六步实操链环境隔离、数据库外置、配置中心化、反向代理加固、进程守护、可观测性接入。每一步都对应一个真实翻车现场和我当年在测试环境反复重装三次 Nginx 的血泪经验。2. 用 Spring Boot Profile 外置 application-prod.yml 实现配置分离别再把数据库密码写进 Git 仓库JDShop 默认的application.yml通常包含如下危险片段spring: datasource: url: jdbc:h2:mem:testdb username: sa password:这在云上是致命的H2 内存库重启即丢数据明文密码进 Git 历史等于裸奔且未指定连接池HikariCP参数高并发下连接耗尽。生产环境必须强制切换 Profile并将敏感配置彻底剥离代码仓库。2.1 创建 application-prod.yml 并禁用 profile 自动激活在src/main/resources/下新建application-prod.yml内容严格按云环境填写# src/main/resources/application-prod.yml spring: profiles: active: prod datasource: url: jdbc:mysql://rm-xxxxxx.mysql.rds.aliyuncs.com:3306/jdshop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse username: jdshop_prod_user password: ${DB_PASSWORD:changeme} # 使用占位符运行时注入 hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 jpa: hibernate: ddl-auto: validate # 严禁 use update 或 create生产必须 validate show-sql: false properties: hibernate: format_sql: false thymeleaf: cache: true # 生产必须开启模板缓存 enabled: true server: port: 8080 servlet: context-path: / # 统一上下文路径避免 Nginx 转发时路径错乱 logging: level: root: INFO com.jdshop: DEBUG # 按需开启业务包日志 file: name: logs/jdshop.log # 指定日志文件路径便于云服务器集中采集提示ddl-auto: validate是底线。JDShop 的建表 SQL 通常放在src/main/resources/data.sql中首次部署需手动执行该 SQL 到 RDS 实例后续仅校验表结构一致性。任何update模式在生产环境都是定时炸弹。2.2 构建时指定 Profile 并注入密码环境变量Maven 打包命令必须显式指定 profile且禁止将密码写入命令行会被ps aux窥探# 在云服务器上执行假设 JDK 17、Maven 3.8 mvn clean package -Dmaven.test.skiptrue -Pprod启动时通过环境变量注入密码推荐方式# 将密码存入服务器环境变量建议用 systemd service 文件管理 export DB_PASSWORDYourStrongRdsPassword123! java -jar target/jdshop-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod或更安全地使用 Linux systemd service 文件/etc/systemd/system/jdshop.service[Unit] DescriptionJDShop Online Shopping System Afternetwork.target [Service] Typesimple Userjdshop WorkingDirectory/opt/jdshop ExecStart/usr/bin/java -jar /opt/jdshop/jdshop-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod EnvironmentDB_PASSWORDYourStrongRdsPassword123! Restartalways RestartSec10 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target然后启用服务sudo systemctl daemon-reload sudo systemctl enable jdshop sudo systemctl start jdshop关键逻辑说明--spring.profiles.activeprod强制激活生产配置覆盖application.yml中的默认配置Environment在 systemd 中安全传递环境变量避免密码出现在进程列表WorkingDirectory和User确保应用以非 root 用户运行符合云安全基线RestartSec10防止启动失败时高频重启压垮 RDS 连接数。3. 把 H2 换成云数据库 RDS不只是改 URL更要处理连接泄漏与字符集陷阱JDShop 本地用 H2 是为了省事但上云必须用托管数据库如阿里云 RDS MySQL 8.0。这不是简单替换 JDBC URL而是要解决三个隐藏雷区连接未关闭导致 RDS 连接数满、MySQL 8.0 默认字符集 utf8mb4 与 JDShop 旧版建表语句冲突、以及 SSL 连接强制要求。3.1 手动初始化 RDS 并修正建表 SQLRDS 实例创建后不要依赖 Spring Boot 的schema.sql自动执行易因权限或字符集失败。登录 RDS 控制台用 DMS数据管理服务执行以下初始化步骤创建数据库jdshop字符集选utf8mb4排序规则utf8mb4_unicode_ci创建专用账号jdshop_prod_user仅授予jdshop库的SELECT, INSERT, UPDATE, DELETE, INDEX权限执行src/main/resources/schema.sql前全局替换所有CHARACTER SET utf8为CHARACTER SET utf8mb4并确保VARCHAR字段长度足够utf8mb4单字符占 4 字节原utf8占 3 字节超长字段会报错关键修正示例原 JDShop 的 user 表-- ❌ 错误utf8 不兼容 emoji且长度计算错误 CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(100) NOT NULL, email VARCHAR(100) ) ENGINEInnoDB DEFAULT CHARSETutf8; -- ✅ 正确utf8mb4 支持 emoji长度按字节重算VARCHAR(191) 是 InnoDB 索引长度上限 CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(191) NOT NULL, password VARCHAR(255) NOT NULL, email VARCHAR(191), INDEX idx_username (username), INDEX idx_email (email) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;3.2 在 application-prod.yml 中强制启用 SSL 并验证证书阿里云/腾讯云 RDS 默认强制 SSL 连接。若忽略此步启动时会报Access denied for user实际是 SSL 握手失败被拒绝spring: datasource: url: jdbc:mysql://rm-xxxxxx.mysql.rds.aliyuncs.com:3306/jdshop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLtrueverifyServerCertificatefalse # ⚠️ 注意verifyServerCertificatefalse 仅用于测试生产应下载 RDS CA 证书并配置 trustCertificateKeyStoreUrl生产环境必须启用证书验证以阿里云为例在 RDS 控制台下载rds-ca-2023.pem将其转为 Java KeyStore 格式openssl x509 -outform der -in rds-ca-2023.pem -out rds-ca-2023.der keytool -import -alias rds-ca -keystore rds-truststore.jks -file rds-ca-2023.der -storepass changeit更新application-prod.ymlspring: datasource: url: jdbc:mysql://rm-xxxxxx.mysql.rds.aliyuncs.com:3306/jdshop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLtrueverifyServerCertificatetruetrustCertificateKeyStoreUrlfile:/opt/jdshop/rds-truststore.jkstrustCertificateKeyStorePasswordchangeit参数说明useSSLtrue强制启用 SSLverifyServerCertificatetrue验证服务器证书有效性trustCertificateKeyStoreUrl指向本地信任库文件路径需确保 Java 进程有读取权限trustCertificateKeyStorePasswordKeyStore 密码keytool创建时设置。4. Nginx 反向代理 HTTPS 强制跳转让 JDShop 的 /static 资源不再 404Session 不再丢失JDShop 默认静态资源CSS/JS/图片放在src/main/resources/static/Spring Boot 会自动映射到/下。但直接暴露 8080 端口不安全且云服务器安全组通常只开放 80/443。必须用 Nginx 做反向代理并解决两个经典问题静态资源路径错乱、HTTP Session 丢失。4.1 Nginx 配置文件/etc/nginx/conf.d/jdshop.confupstream jdshop_backend { server 127.0.0.1:8080 max_fails3 fail_timeout30s; } server { listen 80; server_name yourdomain.com; return 301 https://$server_name$request_uri; # HTTP 强制跳转 HTTPS } server { listen 443 ssl http2; server_name yourdomain.com; # SSL 证书从腾讯云/阿里云免费证书下载 ssl_certificate /etc/nginx/ssl/yourdomain.com.pem; ssl_certificate_key /etc/nginx/ssl/yourdomain.com.key; # 安全加固 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # 关键Proxy 设置解决 Session 和静态资源问题 location / { proxy_pass http://jdshop_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; # 解决 Spring Session 丢失关键 proxy_cookie_path / /; Path/; HttpOnly; Secure; # 缓存静态资源可选提升性能 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control public, immutable; } } # 健康检查端点透传供云监控使用 location /actuator/health { proxy_pass http://jdshop_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }注意proxy_cookie_path / /; Path/; HttpOnly; Secure;这行是解决登录后跳转回首页又变未登录的罪魁祸首。JDShop 使用 Spring Session默认 Cookie Path 为/但 Nginx 代理后若不显式重写浏览器可能因Secure属性缺失而拒绝发送 Cookie。4.2 Spring Boot 中启用 Forwarded Headers 支持在application-prod.yml中添加server: forward-headers-strategy: framework # 启用 Spring 对 X-Forwarded-* 头的解析并在主类JDShopApplication.java中添加 BeanSpring Boot 2.6 必须Bean public ServletWebServerFactory servletContainer() { TomcatServletWebServerFactory tomcat new TomcatServletWebServerFactory(); tomcat.addAdditionalTomcatConnectors(redirectHttpConnector()); // 关键启用 RemoteIpValve解析 X-Forwarded-For tomcat.addAdditionalTomcatConnectors(httpsConnector()); return tomcat; } private Connector httpsConnector() { Connector connector new Connector(org.apache.coyote.http11.Http11NioProtocol); connector.setScheme(https); connector.setSecure(true); connector.setPort(8443); // 此端口不对外暴露仅内部使用 connector.setAttribute(proxyPort, 443); connector.setAttribute(proxyHost, yourdomain.com); return connector; }逻辑说明forward-headers-strategy: framework告诉 Spring Boot 信任X-Forwarded-*头正确解析客户端真实 IP 和协议proxy_cookie_path确保 Nginx 重写 Cookie 的Path和Secure属性使浏览器在 HTTPS 下发送 CookieaddAdditionalTomcatConnectors是 Tomcat 特有配置确保重定向 URL 生成为https://yourdomain.com而非http://localhost:8080。5. 避坑JDShop 云上部署的 4 个血泪现场与解法部署 JDShop 到云服务器最常卡在这四个点每个都曾让我凌晨三点重启服务器5.1 现象Nginx 访问首页返回 404但curl http://localhost:8080能看到 HTML原因JDShop 的application-prod.yml中server.servlet.context-path设为/但 Nginx 的location /代理规则未正确透传请求路径或 Spring Boot 的静态资源处理器被覆盖。解决检查application-prod.yml中server.servlet.context-path: /必须为/不能为空或/jdshop确认src/main/resources/static/下的index.html存在且 Thymeleaf 模板中{/css/app.css}路径正确不带 context-path在 Nginx 配置中删除所有rewrite规则确保proxy_pass后无路径即http://jdshop_backend;而非http://jdshop_backend/。5.2 现象登录成功后跳转到/login?error反复重定向原因Spring Security 默认重定向到/login但 Nginx 未透传X-Forwarded-Proto: https导致 Spring 认为当前是 HTTP生成的重定向 URL 为http://yourdomain.com/login浏览器拒绝跳转混合内容。解决确保 Nginx 配置中proxy_set_header X-Forwarded-Proto $scheme;存在且$scheme为https在application-prod.yml中添加spring: security: require-ssl: true # 强制 HTTPS session: cookie: secure: true # Cookie 仅 HTTPS 传输 http-only: true5.3 现象RDS 连接数达到上限如 1000应用无法响应原因JDShop 的 DAO 层未使用try-with-resources或 Spring 的Transactional数据库连接未及时释放或 HikariCP 配置过小连接池饥饿。解决检查所有JdbcTemplate或MyBatis Mapper调用确保在 Service 层用Transactional包裹在application-prod.yml中调大连接池spring: datasource: hikari: maximum-pool-size: 30 # 根据 RDS 规格调整如 2C4G RDS 建议 20-30 minimum-idle: 10 connection-timeout: 30000 validation-timeout: 3000 idle-timeout: 600000 max-lifetime: 1800000 leak-detection-threshold: 60000 # 启用连接泄漏检测毫秒启用 HikariCP 日志logging.level.com.zaxxer.hikari: DEBUG观察connection timeout或leak detection日志。5.4 现象/actuator/health返回 DOWN显示diskSpace或db不健康原因Spring Boot Actuator 默认检查磁盘空间/tmp目录和数据库连接但云服务器/tmp可能被清理或 RDS 连接池未初始化完成。解决排除磁盘检查非关键management: endpoint: health: show-details: when_authorized endpoints: web: exposure: include: health,info,metrics,loggers health: diskspace: path: /opt/jdshop # 指向应用目录而非 /tmp threshold: 10485760 # 10MB确保数据库健康检查使用 HikariCP 连接池spring: datasource: hikari: connection-test-query: SELECT 1 validation-timeout: 30006. 用 Prometheus Grafana 监控 JDShop不写一行新代码只加 3 个依赖和 1 个配置JDShop 云上稳定运行后真正的运维才开始。你不需要重构代码只需引入 Spring Boot Actuator Micrometer就能把 JVM 内存、HTTP 请求延迟、数据库连接池状态全部可视化。这是我给团队立下的规矩没监控的线上服务等于没上线。6.1 添加依赖pom.xmldependencies !-- Actuator 提供健康检查端点 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency !-- Micrometer 作为指标收集门面 -- dependency groupIdio.micrometer/groupId artifactIdmicrometer-registry-prometheus/artifactId /dependency !-- 可选暴露 JVM 指标 -- dependency groupIdio.micrometer/groupId artifactIdmicrometer-core/artifactId /dependency /dependencies6.2 配置 Actuator 端点application-prod.ymlmanagement: endpoints: web: exposure: include: health,info,metrics,prometheus,loggers,threaddump # 关键暴露 prometheus endpoint: prometheus: scrape-interval: 15s # Prometheus 拉取间隔 metrics: export: prometheus: enabled: true health: show-details: when_authorized # 关键启用 JVM 和 HTTP 指标 management: metrics: enable: jvm: true http: true process: true system: true tags: application: jdshop6.3 部署 Prometheus云服务器上下载 Prometheusv2.45编辑prometheus.ymlglobal: scrape_interval: 15s scrape_configs: - job_name: jdshop static_configs: - targets: [localhost:8080] # JDShop 的 Actuator Prometheus 端点 metrics_path: /actuator/prometheus启动 Prometheus./prometheus --config.fileprometheus.yml --storage.tsdb.path/data/prometheus --web.listen-address0.0.0.0:90906.4 Grafana 面板关键指标直接导入 JSON我常用这三个核心面板JSON 已验证可用Grafana v10.1面板名称PromQL 查询说明HTTP 请求延迟 P95histogram_quantile(0.95, sum(rate(http_server_requests_seconds_bucket{applicationjdshop}[5m])) by (le, uri, status))查看/api/order/create等关键接口是否超时JVM 堆内存使用率jvm_memory_used_bytes{areaheap, applicationjdshop} / jvm_memory_max_bytes{areaheap, applicationjdshop} * 100堆内存 80% 持续 5 分钟需告警HikariCP 活跃连接数hikaricp_connections_active{applicationjdshop}持续接近maximum-pool-size说明连接泄漏我的习惯每次上线新版本必开 Grafana 看 15 分钟——不是看是否正常而是看P95 延迟是否比上一版升高 20%、GC 次数是否突增、活跃连接数是否阶梯式上涨。这些数字比日志里的INFO更诚实。有一次发现/cart/add接口延迟从 120ms 涨到 450ms排查发现是 MyBatis 的Select注解没加Options(useCache false)二级缓存污染导致查询变慢。没有监控这种问题要等用户投诉才能发现。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进