2026-11-10 20:32:48
用 RuoYi (Spring Boot) 开发了一套访客管理系统,由于要频繁的更新部署,我担心重启会影响到正在处理的请求。所以查了一下 Spring Boot 是否支持 Graceful Shutdown。因为 Spring Boot 这种非常成熟的框架,不能没有这种机制的,所以问题就变成了,如何配置来启用 Graceful Shutdown.
这个 RuoYi 版本中的 Spring Boot 版本为 2.5.15。 Spring Boot 从 2.3.0 起就已引入
# application.yml
server:
shutdown: graceful # 开启优雅停机,默认为 immediate
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 设置等待在途请求完成的最大超时时间,默认30秒
具体说明:
server.shutdown: graceful —— Spring Boot 2.3.0 引入(2.3 之前只有 immediate 一种行为,配了也不认)spring.lifecycle.timeout-per-shutdown-phase —— 同版本引入,Duration 类型,默认就是 30sTomcat 9.0.106 的 NIO/NIO2/APR 连接器都支持 pause() 优雅停机(Tomcat 侧需要 9.0.33+),没有问题。应用用的是内嵌 Tomcat(spring-boot-starter-web),正好是这套配置支持的容器类型。
| 配置 | 作用 |
|---|---|
server.shutdown: immediate(默认) |
JVM 收到 SIGTERM 直接销毁上下文,在途请求被硬切 |
server.shutdown: graceful |
先暂停 Tomcat 连接器(不再接新连接,新请求返回 503),等在途请求跑完再关上下文 |
spring.lifecycle.timeout-per-shutdown-phase |
同一个值同时用于:① Web 服务器等待在途请求的上限;② DefaultLifecycleProcessor 每个停机 phase 的超时。超时后会打 warn 并强行继续往下走 |
所以不必去 Tomcat 里另找“grace period”属性 —— Spring Boot 就是复用这个 lifecycle 超时作为 Web 容器的等待上限。
重启时看日志,正常会出现类似:
o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
实测方式:起一个慢接口(sleep 10s),请求发出后立刻 kill(SIGTERM,不要 -9)进程,观察该请求是否返回正常响应、日志是否等到请求结束后才 Graceful shutdown complete。
不会。30s 是「最长等待上限」,不是「固定等 20 秒」
timeout-per-shutdown-phase 的语义是 deadline,不是 sleep。空闲时它会在毫秒级结束。
Spring Boot 的 GracefulShutdown(org.springframework.boot.web.embedded.tomcat.GracefulShutdown)停机过程是:
Graceful shutdown complete,继续往下做容器销毁。Connector#setStopTimeout 交给它,只有”等满 30s 还有请求没跑完”这种情况,才会强制中断那些请求。所以没有线程在处理请求时,它压根不进等待循环,第一个轮询周期就判定空闲走了。
| 阶段 | 耗时 |
|---|---|
| pause + 活跃计数检查 | ~0 ms(≤ 1 个轮询周期) |
Context 销毁:Quartz shutdown、Druid 连接池 close、Lettuce close、ShutdownManager 等异步池(空闲立即返回) |
通常 0.x ~ 2~3 s |
| 合计 | 一般 1~3 秒,与 30s 无关 |
日志上表现为 Commencing graceful shutdown... 和 Graceful shutdown complete 两行时间戳几乎相同。
浏览器 / 微信 webview 持有的长连接不算”活跃请求”,计数只统计正在被 Servlet 处理的请求。所以不会因为有几十个空闲长连接就多等——Tomcat 关 Connector 时会把它们直接关掉。
2026-11-08 12:55:18
经常在 MES 中看到 BOM 和工艺路线。但是一直不清楚具体的差异是什么?而且好多教程里也没有解释清楚。
一句话概括:BOM 管“用什么料”,工艺路线管“怎么加工”。即,BOM 是产品结构数据,回答“用什么装”;工艺路线是生产过程数据,回答“怎么装、在哪装、装多久”。
BOM 是 Bill of Materials 的缩写,中文通常叫 物料清单 或 物料明细表。
它用来描述一个产品由哪些物料组成、各物料的数量/层级关系等,是生产排程、领料、投料、追溯和成本核算的重要基础数据。这里以最简单的一只中性笔为例子:
1 支中性笔 = 1 支笔芯 + 1 个笔杆 + 1 个笔帽 + 1 个弹簧 + 1 个包装袋。
如果 MES 要生产 1000 支中性笔,按这个 BOM 展开就是:
MES 可以用它做:工单展开、齐套检查、领料发料、装配防错、批次追溯。
复杂一点的 BOM,例如笔记本电脑,一个主板还会包含两个内存条,一个 CPU 等,存在上下级关系。
工艺路线的英文是 Routing。工艺路线在 MES 里回答三个问题:这个产品要经过哪几道工序,每道工序在哪个工作中心完成,每道工序的标准工时是多少。
Routing(工艺路线)是产品级的,只描述“经过哪些工序”。 它的结构是“物料 → 工艺路线 → 工序”,不包含具体设备、参数、资源细节。它回答的问题是:“这类产品应该做哪些事情。” 排程系统和订单工艺路径生成主要依赖这一层。
工艺路线梳理中最难的取舍是颗粒度。太细,每道工序都要报工,工人嫌麻烦,数据质量必然下降;太粗,出问题时无法定位,追溯链会断。实用的判断依据是:有质量风险的工序单独列(焊接、热处理、压装等),不同设备或不同工作中心的工序分开(直接决定排程和产能计算),同一台设备连续完成、无质量风险的工序可以合并。
需要注意,工艺路线的管理并非集中在单一系统里,它通常由 PLM(产品生命周期管理)系统负责“源头设计”,然后同步分发到ERP和MES系统。
BOM 和工艺路线分开管理,不意味着它们互不相干。恰恰相反,它们必须在执行层通过一个明确的关联点连起来:某一行 BOM 物料,是在哪一道工序被消耗的。
如果没有这个关联,系统会默认所有物料都在第一道工序前齐套。结果就是:仓库提前把大量物料拉到车间,堆在本来就拥挤的现场,而真正到某道工序才需要的物料反而被提前“消耗”掉了账面数量。
2026-11-07 10:17:24
可能人到中年对什么都提不起兴趣了,又开始炒股了。没想到买入的银行和电信,家电都表现良好,比业余折腾省心多了。但是逛雪球,和短视频,我发现大家都在骂 A 股市场,我总觉得是这没有什么逻辑,明明是买的垃圾股,纯靠赌运气,暴涨暴跌不是很正常么?买固定收益类的分红股不香么。而另一个疑问是,到底 A 股总市值是个什么概念,于是我查了一下全球各国股市总市值:
截至2026年,全球股市总市值高度集中,美国以绝对优势占据近半壁江山,前十大市场拿走了全球绝大部分份额。不同统计时点的数据略有浮动,以下基于彭博社2026年4月的数据,列出全球主要股市的市值排名。
全球股市的头部格局长期由美国主导,但内部排名正被AI产业链和区域金融中心的兴衰持续改写。
2026-11-05 20:08:55
Magento 中的同一个产品详情页链接支持多种 URL 格式,例如:
但是我用 golang 重新实现的版本,只支持第一种格式。在需要从旧外贸站迁移到 golang 实现的站点时,就会丢失第二种链接格式的支持,导致 404 错误,破坏之前积累的搜索引擎收录的积累。
开始我感觉不太好支持,但是第二天早上在厕所嘘嘘时,突然想到了一个解决方案。。。
golang 中如果找不到 sku,则拆分出最后一部分,再查询一次,即可解决。即,即在查询不到时,取 / 分割的最后一部分,提取出 sku 再查询一次。为什么这么简单的解决方式,昨天晚上没有想到。看来睡眠也是思考过程。
两种地址都能被访问,但对外只有一个规范地址;长地址 200 打开并声明 canonical 指向短地址,权重合并到短地址,无重复内容风险。而 Magento 的实现则是反的,canonical 指向长地址。
2026-11-01 15:47:09
最近在开发 MES 系统的过程中,发现对一些 MES 的基础概念还是不太了解。所以买了一本书翻了翻,还是有不少收获。虽然,书是大学的那种毫无实战经验的理论书籍,但是对我这种新手来说,还是能学到不少东西。这里整理一下库位管理相关的概念。
库位,即物料或者生产的成品存在在仓库中的位置。
如果是 MES 界面字段,通常用 Location 最简洁;如果是仓储管理语境,用 Storage Location 更准确。
需要管理 & 展示的信息:
英文为 Line-side Storage 或 Line-side Location。线边库,指在车间设置的一个物料的缓冲区,或者临时存储区。例如,物料从库房 -> 车间线边库 -> 车间工位。
当然,这个线边库并不是必须的,也可以直接从库房直接配送到工位。例如,通过物料传送带,或者 AGV (Automated Guided Vehicle,自动导引车)
线边库通常是使用手持设备进行操作。
2026-10-31 19:14:57
国内通过 git push 到 github 时,会报错:
> git push github --all
fatal: unable to access 'https://github.com/xxx/yyy.git/': Failed to connect to github.com port 443 after 21164 ms: Timed out
虽然能在浏览器里访问 github,但是命令行里却会网络超时。
git config –global http.https://github.com.proxy socks5://127.0.0.1:4455
这样配置,有个好处,就是只对 github.com 采用这个配置,对于其他 git 仓库不会有影响。
> cat C:\Users\zhong\.gitconfig
[user]
name = Zhongwei Sun
email = [email protected]
[credential]
helper = manager
[http "https://github.com"]
proxy = socks5://127.0.0.1:4455
没想到 powershell 下也能使用 cat 和 ~/.