Java Dev Work Flow
2026-04-03
3 min read
在典型的 Java 后端迭代里,真正吞噬效率的不止有代码编写,还有冗长的重启和低质量发布反馈。
dev work flow
这篇笔记聚焦一件事:把「本地热替换调试 + 远程可重复部署 + 实时日志观测」串成一条最短反馈链路。
- 本地使用 JRebel 进行快速调试(减少反复重启)
- 通过 SSH 配置连接远程机器
- 本地执行一键部署脚本,远程拉代码、构建、重启
- 登录服务器实时追日志验证结果
核心目标是缩短「code -> build -> boot -> observe -> verify」闭环延迟,同时保证线上变更可控、可回滚、可审计。
确实,今天可选的 CI/CD 工具已经非常多:Jenkins、GitLab CI、GitHub Actions、ArgoCD 都能很好地解决流水线编排问题。
但这个场景里我更倾向于手写一套可维护的最小 CI/CD(script-first):
- 步骤透明:每个阶段输入/输出清晰,失败点可定位
- 状态可见:不依赖黑盒插件,shell 级即可复盘
- 依赖可控:最小外部耦合,迁移成本低
- 变更可审计:脚本即规范,review 即发布策略
这不是“手工点点点部署”,而是把发布链路代码化,用工程化方式维护一个轻量、可演进的交付系统。
architecture flow
+---------------------+
| local workspace |
| code + jrebel run |
+----------+----------+
|
| ssh skytech-prod
v
+---------------------+
| remote server |
| git pull + mvn pkg |
+----------+----------+
|
| start jar
v
+---------------------+
| application up |
| tail log and verify |
+---------------------+
ssh config
在 `~/.ssh/config` 中配置目标主机别名,后续脚本和命令都可以复用这个别名。
Host skytech-prod
Hostname 10.100.10.178
User root
IdentityFile ~/.ssh/id_rsa
build on local
-
use jrebel
https://www.jrebel.com/jrebel-releases
在项目中创建 `src/main/resources/rebel.xml`,让 JRebel 知道 class 目录位置。
<application generated-by = "maven" build-tool-version = "3.6.1" plugin-version = "1.1.10" xmlns = "http://www.zeroturnaround.com" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://www.zeroturnaround.com http://update.zeroturnaround.com/jrebel/rebel-2_2.xsd"> <classpath> <dir name="~/workspace/skytech/skytech-app/target/classes"></dir> </classpath> </application>说明:
- `target/classes` 是 Maven 编译产物目录
- 修改 Java 代码后,重新编译对应模块即可热更新(无需整体重启)
- 热替换主要覆盖方法体与实现细节;涉及类结构漂移(字段签名、继承层次、Spring 上下文装配边界)时通常仍需冷启动
-
start on local
"$JAVA_11_HOME"/bin/java -agentpath:/Users/van/ZY/workspace/jrebel/lib/libjrebel64.dylib \ -Dspring.profiles.active=test -Dserver.port=8383 -jar api.jar建议补充 JVM 参数(按需):
- `-Xms512m -Xmx1024m`:避免本地调试时频繁 GC 抖动
- `-Dfile.encoding=UTF-8`:确保日志和接口字符集一致
- `-XX:+HeapDumpOnOutOfMemoryError`:本地复现疑难问题时保留故障现场
build on server
首次部署先克隆仓库:
git clone https://user:password@git.example.com/group/repo.git
注意:生产环境不建议把用户名密码直接写在 clone URL 中,建议改为 SSH Key 或 token。
另外建议把部署用户降权,避免长期使用 root 直接发布。
-
script on local
本地触发部署(只负责连接与调用远程脚本):
#!/bin/bash set -e # 记录发布日志 TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S') echo "[$TIMESTAMP] 部署 skytech-app" >> deploy.log ssh skytech-prod 'cd /root/skytech/skytech-app/ && sh run-on-server-prod.sh' -
script on server
服务器构建并重启服务:
#!/bin/bash set -e git pull export JAVA_HOME=/root/jdk-11.0.11 /root/apache-maven-3.9.14/bin/mvn clean package -U export TZ="Asia/Shanghai" # 仅杀掉当前应用,避免误杀机器上其它 Java 进程 APP_NAME=skytech-app-1.0.0-SNAPSHOT.jar ps -ef | grep -v grep | grep "${APP_NAME}" | awk '{print $2}' | xargs -r kill -9 echo '' > nohup.out nohup /root/jdk-11.0.11/bin/java -jar ./target/skytech-app-1.0.0-SNAPSHOT.jar \ --spring.profiles.active=prod \ --logging.level.com.vanniuner.platform.sys.config.filter=error \ --spring.cloud.nacos.config.enabled=false \ --spring.cloud.nacos.discovery.enabled=false & tail -f nohup.out说明:
- `set -e`:任一步骤失败立即退出,避免半成功状态
- `xargs -r`:无匹配进程时不执行 kill,防止空参数报错
- `tail -f`:部署后第一时间观察启动日志和异常堆栈
- 可以进一步把 `kill -9` 改成 `TERM -> 等待 -> KILL` 两阶段退出,降低强杀带来的副作用
log tail on server
持续查看最新业务日志:
ssh skytech-prod "ls -t ~/skytech/skytech-app/logs/*.log | head -1 | xargs tail -f -n 1000"