WAR 部署的实际情况与并行部署(Parallel Deployment)
一句话总结
单纯复制WAR的部署中存在一个名为404的漏洞,Tomcat已经有填补该漏洞的手段(##버전虽然有并行分发),但大多数现场都不知道。
为什么这是问题?
停止发布虽然安全,但在此期间服务会停止。如果只覆盖WAR进行无中断操作,直到展开结束为止,路径会显示为404,这段时间越是班级越多,可能超过30秒。不能简单地认为时间短的原因是,这30秒发生在开放的第一天上午九点。
而且失败很安静。复制过程中,Tomcat开始展开,解开了一半的WAR,如果失败的话,会留在日志中,但发布脚本的结束代码是0。发布的人相信自己成功了,然后下班。
WAR是如何分发的?
webapps/order.war复制的话汤姆猫会webapps/order/解开后/order通过路线提供服务。
这个autoDeploy="true"的动作。虽然方便,但在运营中却存在陷阱。
- **复制过程中,Tomcat开始展开。**如果从网络上复制大型WAR的话
试图展开只有一半的文件失败。所以发布脚本是
用其他名字复制后
mv更改为罗原子名。 - **
webapps/order/目录会先被删除。**从那一刻开始,直到新应用程序出现为止。/order都是404。短的话3秒,班级多的话30秒以上。 - 删除失败。由于打开的文件手柄,展开目录无法完全删除。 有时会有旧班级留下来的事情。所以在分发手续书中写着“Tomcat停止→目录删除→ 输入work删除→WAR复制→机动。虽然安全,但在此期间服务将停止。
并行分发——Tomcat原有的无中断手段
Tomcat有一个不太为人知的功能。在文件名中##버전加上的话
可以在同一上下文路径上同时上传多个版本。
webapps/order##001.war ← 기존 버전
webapps/order##002.war ← 새 버전
在这种状态下,Tomcat会这样移动。
- 新请求(无会话)是最高版本的
##002去罗。 - 有现有会话的请求继续
##001去罗。 ##001如果的会话全部到期的话,到时候##001即使放下也不会有人受伤。
也就是说,不中断会话,直接跳转到新版本。在L4前端取出并插入服务器 在无法进行传统无中断部署的单一WAS环境中,这张卡相当有用。
版本字符串会通过字符串比较进行排序##1,##2,##10像这样用的话
##10这个##2看着继续向前走。##001像这样填写0或
##2026-08-19_1430使用相同的时间戳。
即便如此,在停止分发的情况下
并行分发不是万能的。在以下情况下不能使用。
- DB结构发生变化时—两个版本看到相同的DB,但结构不同的话 两者中之一会崩溃。所以结构更改是扩展(expand) → 转移 → 缩小(contract) 分为三个阶段,一个分发中不能放入两个阶段。
- 静态资源缓存被卡住时——两个版本给不同的JS,URL相同。
- 使用全局状态时——单线缓存、调度器、文件锁。
所以实际判断是这样的: 如果只有画面和逻辑变更,则进行并行发布;如果模式、部署和全域状态交织在一起,则停止发布。
WAR和可执行JAR有什么不同
最近的新开发用Spring Boot fat jar,现有系统都是WAR。 在同一个组织中两个人共存是很常见的,所以整理一下差异比较好。
| 项目 | WAR + 外部 Tomcat | 可执行 JAR (内置 Tomcat) |
|---|---|---|
| 分发单位 | WAR文件 | JAR文件 |
| Tomcat版本 | 由服务器管理员管理(多个应用程序共享) | 应用程序在运行(每个应用程序可能有所不同) |
| 端口 | server.xml | 执行参数 / 属性 |
| 放置多个应用程序 | 在一个WAS上放置多个上下文 | 打开多个进程 |
| 还原 | 更换为以前的WAR | 更换为以前的JAR进行进程更换 |
| 无中断 | 并行部署或L4 | 进程更换 + LB |
| 运营标准 | WAS标准程序书 | 流程/容器标准程序书 |
运营组织的标准程序书写在哪一边决定了实际的选择。 即使从技术上讲JAR很方便,但在以WAS单位进行监控、机动、备份程序标准化的组织中 因为一个JAR,我们需要重新制定程序。这可能比技术优势的成本还要高。
分发脚本必须具备的最低条件
现场可以使用的分发脚本做到这个程度。
- 因子验证 — WAR文件是否实际存在,不是0字节
- 备份 — 将当前发布版附上时间戳保存
- 原子性布局 — 用临时名称复制后
mv - 展开等待 — 持续 polling,直到上下文在最大 N 秒内响应。
- 验证 — 健康检查URL是200,版本字符串是否符合预期值
- 失败时立即中断 — 5次失败时不报告为部署成功
4次“进展等待”特别重要。cp紧接着curl做的话还是404。
也看过判定为失败并进行回溯的脚本,相反,没有等待
我看过更多输出“部署完成”后结束的脚本。
后者将失败的部署视为成功。这更糟糕。
在现场相遇的样子
所以分配脚本必须具备的最低条件已经确定。用其他名字复制后mv首先将名称改成原子,等到展开结束为止,确认实际响应是否为200后输出终止代码。复制后立即确认的话,因为还在展开中,所以会失败,但经常误认为这是“部署失败”而退回。