先说结论:Apache Apollo 虽然官方早就停止更新,但我相信现在还点进这篇文章的朋友,大概率不是在选型,而是在“救火”——公司某个旧系统还在用 Apollo 做消息中间件,现在要把服务从 Windows 迁到 Linux 服务器。我上个月刚把一套生产环境的 Apollo 1.7.1 从 Windows Server 迁到了 CentOS 7.9,整个过程踩了不少坑,也整理出了一套可以照着抄的流程。今天这篇就把完整迁移过程、关键命令、配置改动和避坑点一次性说清楚。
如果你的情况和我类似:Apollo 跑在 Windows 上,数据文件已经积累了几个月甚至几年,业务方不给 downtime 太长,同时又没有专门的 MQ 运维专家,那这篇文章就是给你准备的。我不讲太虚的架构设计,只讲迁移当天你要做什么、先做哪一步、哪几个文件最容易出错、启动不了时怎么定位。
1. 迁移前准备:先搞清楚手上有什么
1.1 版本、目录结构与迁移边界
接手一个 Windows 上的 Apollo 实例,第一步不是急着打包上传,而是先看清楚它的目录结构。一个典型的 Apollo broker 实例目录包含 etc、data、log、bin 四块,分别对应配置、持久化数据、运行时日志和启动脚本。迁移时真正需要带走的只有两块:etc 和 data。
etc 目录里面是 broker.xml、users.properties、groups.properties、log4j.properties,有时候还有日志 login.config 和证书文件。data 目录则是 Apollo 使用 BDB(Berkeley DB Java Edition)落盘的消息存储,里面有事务日志、消息队列索引等文件。搞清楚这两块就够了,不要试图把整个 Windows 安装包拷过去,更不要带着 Windows 版的启动脚本去 Linux 上跑,后面我会解释为什么。
版本方面,Apollo 最多人用的是 1.7.1,几乎没有比这更新的版本。迁移前后务必保持一致,尤其是不能一边是 1.7.1、一边是 1.7.0,BDB 存储文件对版本很敏感,版本不一致轻则告警,重则直接起不来。先到 Windows 服务器上执行一下:
bash复制apollo version
或者直接看 lib 目录下 apollo 相关 jar 包的版本号,记下来,后面在 Linux 上必须用同一个版本。
1.2 Java 环境与系统架构确认
Apollo 是纯 Java 应用,核心就是一个 JVM 进程,所以迁移前要确认 Java 版本。Apollo 1.7.1 基于 Java 7/8 时代构建,实测在 JDK 8 下最稳。在 Windows 上执行:
bash复制java -version
如果在用 JDK 11 以上跑,建议你在 Linux 上还是退回 JDK 8。倒不是说完全跑不起来,而是遇到一些反射、类加载或 GC 参数问题后,排查成本会明显上升,生产环境迁移本来就是要尽量降低变量的。
还要看一眼 Windows 服务器的 CPU 架构。绝大多数是 x86_64,对应 Linux 侧直接装 amd64 的 JDK 即可。如果 Windows 跑在 arm 上,虽然少见,也要在 Linux 侧选择 arm64 版本 JDK,否则 JNI 相关的组件会出问题。
1.3 迁移方案选型:整包拷贝还是重建实例
我强烈建议采用“Linux 新建 broker 骨架 + 拷贝 etc/data 覆盖”的方案,而不是把整个 Windows broker 目录原封不动传过去。原因有三个:
- Windows 下的启动脚本是
.cmd,Linux 需要的是 shell 脚本,拷贝过去没法直接用,还会遇到 CRLF 换行符问题。 - 新建骨架能确保脚本、目录结构、权限都符合 Linux 环境习惯。
- 重建实例时你会被迫重新审视 broker.xml 中的路径,避免把 Windows 盘符路径带到 Linux。
但不要担心配置丢失,新创建的 broker 骨架里都是模板,我们只需要把 Windows 上真正的 etc 和 data 覆盖进去。等于说我们用模板搭了个架子,再把家当填进去,既干净又安全。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 在 Linux 侧搭建运行环境
2.1 安装 JDK 8 与基础依赖
Linux 服务器上先装 JDK,CentOS 7 下面我通常用 OpenJDK 8,命令如下:
bash复制sudo yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
装完后一定要配置 JAVA_HOME。Apollo 的启动脚本会直接引用 JAVA_HOME 来定位 java 命令,不配置后面大概率起不来。在 /etc/profile 或当前用户的 ~/.bashrc 里加上:
bash复制export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.xxx
export PATH=$JAVA_HOME/bin:$PATH
验证一下:
bash复制java -version
which java
确认输出里没有指向错误目录。注意,服务器上如果有多个 JDK 版本,which java 和 JAVA_HOME 不一致是常见坑,后面启动时脚本可能找到旧版 Java。
2.2 解压 Apache Apollo 并创建 broker 骨架
从官方存档下载 Apollo 1.7.1 的 tar.gz,放到 /opt 下解压:
bash复制cd /opt
tar -zxvf apache-apollo-1.7.1-unix-distro.tar.gz
mv apache-apollo-1.7.1 apollo
然后创建一个专用于运行服务的系统用户,不建议直接用 root 跑消息中间件,万一程序有漏洞,风险太大:
bash复制sudo useradd -r -s /sbin/nologin apollo
创建 broker 实例目录,比如规划在 /opt/apollo-broker/mybroker:
bash复制/opt/apollo/bin/apollo create /opt/apollo-broker/mybroker
执行成功后,/opt/apollo-broker/mybroker 下会自动生成上述的 etc、data、log、bin 结构。这就是我们迁移用的骨架。此时你应该先看一眼生成的 broker.xml,确认里面默认配置了什么,后面才好对照替换。
2.3 规划数据目录与日志目录
data 目录是消息存储的核心,最好放到独立磁盘或至少单独分区。如果服务器上有数据盘,比如 /data,建议把 broker 建到数据盘下。比如:
bash复制/opt/apollo/bin/apollo create /data/apollo/mybroker
这样数据目录自动就在 /data/apollo/mybroker/data,避免将来根分区被日志和 BDB 文件塞满。如果没有独立数据盘,就保持默认路径,但一定要留意剩余空间。
创建好目录后,先把整个 broker 目录的所有者改成 apollo 用户:
bash复制sudo chown -R apollo:apollo /opt/apollo-broker/mybroker
这一步别看简单,很多迁移后启动失败就是因为用 root 解压和创建,后续 apollo 用户没有写权限。记住,Apollo 在初始化 BDB 存储时会在 data 目录下创建临时文件,没写权限会直接抛异常。
3. Windows 侧数据与配置打包
3.1 停服,必须停服
在 Windows 服务器上,先停掉 Apollo 服务,强烈建议用官方方式干净退出,不要直接 kill 进程。Apollo 在传递 BDB 日志时如果被强杀,data 目录可能留下未落盘的事务状态,迁移到 Linux 后打开时会报错或者进入恢复流程,增加不必要的风险。
如果注册成了 Windows 服务,用服务管理界面停止;如果前台或后台进程,执行:
bash复制apollo-broker stop
或者找到 Java 进程后 jstack 看一眼再结束。等进程完全退出后,再开始打包。
3.2 备份 etc 目录
在 Windows 的 broker 实例目录下,进入 etc,把以下文件单独复制出来:
- broker.xml
- users.properties
- groups.properties
- log4j.properties
- 如果有 keyStore、trustStore、certificate 文件,一并备份
users.properties 是用户名密码列表,密码默认是明文或简单编码,迁移后如果担心泄密,可以在 Linux 上改掉。groups.properties 是角色组配置,千万不能漏,漏了会导致鉴权异常。
3.3 备份 data 目录
data 目录是消息存储,也是整个迁移里最敏感的部分。先在 Windows 上把整个 data 目录压缩,推荐用 zip 或 tar 格式,不要用过大的压缩级别,避免传输时间过长。命令示例:
bash复制tar -zcvf apollo-data-backup.tar.gz data
注意只打包 data 目录本身,不要带 etc 和 log。日志文件没必要迁移,过去的历史日志留着只是占空间。
有一个细节:如果 data 目录很大,传输前先确认 Linux 目标盘剩余空间,再确认文件系统支持大文件。别传到一半才发现磁盘不够,那就尴尬了。
3.4 清理配置文件中的 Windows 痕迹
在 Windows 上打开 broker.xml,重点检查三处:
store或directory相关配置,看里面是否有 Windows 盘符路径,比如D:/apollo/data。- 虚拟主机或队列配置里是否引用了 Windows 绝对路径。
acceptors和connectors里的地址,是否有 Windows 网卡绑定的内网 IP,而不是0.0.0.0。
Apollo 的 broker.xml 里通常会有类似:
xml复制<store directory="${apollo.data}"/>
如果是这种写法,路径会自动适配目录实例,不需要改。但如果写死了 Windows 路径,就必须先记下来,到了 Linux 侧再统一改。推荐在打包前把 broker.xml 里所有路径先改成相对目录或占位符,减少后面对照的负担。
4. 数据与配置迁移到 Linux
4.1 上传并解压
把 Windows 上备份出来的 etc 和 data 压缩包上传到 Linux 服务器,比如放到 /tmp 下。然后解压:
bash复制cd /tmp
tar -zxvf apollo-data-backup.tar.gz
tar -zxvf apollo-etc-backup.tar.gz
解压后你会得到 data 和 etc 两个目录。先不要急着覆盖,看一下 data 目录的大小和文件列表,确认数据文件确实在里面。Apollo 的 BDB 存储文件通常是一系列 .jdb 后缀文件,如果发现 data 目录是空的,那就意味着之前可能配置了别的存储路径,需要回到 Windows 再核实。
4.2 覆盖到新 broker 骨架
确认无误后,把数据文件覆盖到新创建的 broker 实例:
bash复制cp -r /tmp/data/* /opt/apollo-broker/mybroker/data/
cp -r /tmp/etc/* /opt/apollo-broker/mybroker/etc/
覆盖后,再次执行:
bash复制sudo chown -R apollo:apollo /opt/apollo-broker/mybroker
这一步尤其重要。用 root 拷贝过来的文件所有者默认是 root,apollo 用户启动时无法创建 BDB 锁文件和日志文件,会直接报权限错误。我遇到过好几次,最后都是靠 chown 解决的。
4.3 调整 broker.xml 中的地址与端口
打开 Linux 上的 broker.xml,逐项检查以下内容:
brokerName是否还沿用原来的名字,不需要改就保留。acceptors中是否绑定了 Windows 的内网 IP,如果是,改成0.0.0.0或新的 Linux 内网 IP。比如原配置:
xml复制<acceptor id="tcp" bind="tcp://192.168.1.10:61613"/>
改成:
xml复制<acceptor id="tcp" bind="tcp://0.0.0.0:61613"/>
- 如果使用多个协议,比如 STOMP、MQTT、OpenWire、AMQP,逐个检查对应的 acceptor,确认端口没有被 Linux 上的其他进程占用。
- 如果原来配置了 SSL 证书,确认 keyStore 文件路径存在,且证书里面有密码的
<key_store_password>等配置已经同步修改。 - 如果原来
store或临时目录配置了路径,改成 Linux 的绝对路径。
4.4 启动前自检清单
启动之前按下面清单快速过一遍:
- 端口是否被占用:
netstat -tlnp | grep 61613 - data 目录权限是否正确:
ls -ld /opt/apollo-broker/mybroker/data - JAVA_HOME 是否生效:
echo $JAVA_HOME - 配置 XML 是否有语法问题:可以用
xmllint /opt/apollo-broker/mybroker/etc/broker.xml检查 - 防火墙和 SELinux 状态:至少先确认一下,后面会细说
这一步自检做好了,启动时基本都是一次过。如果跳过直接启动,遇到问题再回头看,反而浪费时间。
5. 启动验证与连通性测试
5.1 前台启动并观察日志
第一次启动建议用前台方式,这样日志直接输出到终端,能第一时间看到异常:
bash复制cd /opt/apollo-broker/mybroker
bin/apollo-broker run
看到类似 “Apollo Broker ... started” 之类的日志后,说明 JVM 和 BDB 存储初始化成功。如果启动失败,重点看异常栈里有没有 Permission denied、Cannot create directory、BDB 相关的提示。
前台启动时不要急着 Ctrl+C,先等 10 到 20 秒,让 BDB 把恢复流程跑完。Apollo 迁移后第一次启动可能会自动做事务恢复,日志里会有恢复记录,这是正常现象。
5.2 多协议收发的实战验证
启动成功后,找一个测试用的生产者消费者脚本,验证消息能否正常收发。我这里用 Python 的 stomp.py 做个示例:
bash复制pip install stomp.py
然后写一段发送和订阅脚本:
python复制import stomp
import time
class Listener(stomp.ConnectionListener):
def on_message(self, frame):
print("received:", frame.body)
conn = stomp.Connection([('127.0.0.1', 61613)])
conn.set_listener('', Listener())
conn.connect('admin', 'password', wait=True)
conn.subscribe(destination='/queue/test', id=1, ack='auto')
conn.send(body='hello from windows migration', destination='/queue/test')
time.sleep(2)
conn.disconnect()
运行脚本,如果控制台能打印出 received: hello from windows migration,说明核心链路已经通了。如果有多个协议,比如 MQTT 或 OpenWire,也分别用对应客户端做一次简单收发测试。
注意:如果
conn.connect时报认证失败,说明 users.properties 里的账号没有同步过来,或者账号密码在传输过程中被改了。重新核对 users.properties 里的 admin 账号。
5.3 服务化与开机自启
验证没问题后,把 Apollo 配置成 systemd 服务。在 /etc/systemd/system/apollo-broker.service 写入:
ini复制[Unit]
Description=Apache Apollo Broker
After=network.target
[Service]
Type=simple
User=apollo
Group=apollo
ExecStart=/opt/apollo-broker/mybroker/bin/apollo-broker run
Restart=on-failure
RestartSec=10
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
保存后执行:
bash复制sudo systemctl daemon-reload
sudo systemctl enable apollo-broker
sudo systemctl start apollo-broker
这里要强调,ExecStart 里写的是 run,不是 start。Apollo 的 apollo-broker 脚本 run 表示前台运行,systemd 的 Type=simple 正好对应这种模式,日志会被 systemd 收集,通过 journalctl -u apollo-broker -f 查看。
还有一个细节,如果启动后 systemd 状态显示 active,但客户端连不上,先看监听地址是不是真的监听在外网网卡上:
bash复制netstat -tlnp | grep java
确认监听地址是 0.0.0.0 或指定内网 IP,而不是只有 127.0.0.1。
6. 避坑手册与常见问题
6.1 文件权限与 SELinux 导致的启动失败
这个是迁移中遇到最多的问题。症状通常是启动几秒后进程退出,日志里有类似:
code复制java.io.IOException: Permission denied
排查顺序:先看 data 目录所有者,再看父目录权限,最后看 SELinux。对于 SELinux,CentOS 7 默认是 enforcing,如果端口或进程域不对,会有 AVC 拒绝日志。临时验证最快的方式:
bash复制sudo setenforce 0
启动服务,如果能起来,说明的确是 SELinux 策略问题。此时不要直接关掉 SELinux,而是把端口加到策略里:
bash复制sudo semanage port -a -t http_port_t -p tcp 61613
不过更稳妥的做法是只给服务进程所在的目录和脚本打上合适的上下文,这部分要根据你实际开放哪些端口来定。如果是测试环境,直接 setenforce 0 图省事也没问题,生产环境建议认真配策略。
6.2 端口、防火墙与客户端连接超时
服务起来了但客户端从外部连不上,最常见的就是防火墙。CentOS 7 用 firewalld,先用以下命令开放端口:
bash复制sudo firewall-cmd --permanent --add-port=61613/tcp
sudo firewall-cmd --reload
如果多个协议,把对应的 STOMP 61613、MQTT 61613、OpenWire 61616、AMQP 5672 等端口都放行。还有一个经典坑:云服务器或者虚拟化环境里的安全组规则也要同步放行,否则 Linux 本机防火墙全开了也没用。
排查时用:
bash复制nc -vz <linux-ip> 61613
从客户端机器执行,能通就说明网络层没问题,再往应用层面查。
6.3 CRLF 换行符导致脚本报错
如果你没有按我说的“新建骨架 + 覆盖 etc/data”流程,而是直接把整个 Windows broker 目录拖到 Linux,大概率会遇到 /bin/bash^M: bad interpreter 之类的报错。原因是 Windows 下生成的 shell 脚本或配置文件带 CRLF 换行符。
解决方法很简单:
bash复制sudo yum install -y dos2unix
cd /opt/apollo-broker/mybroker/bin
dos2unix apollo-broker
配置文件也可以转一下,避免在 XML 解析时出现意外空格:
bash复制dos2unix ../etc/broker.xml
这个坑防不胜防,强烈建议从头就按重建实例的方式做,能少折腾很多。
6.4 BDB 数据文件版本不一致
Apollo 的持久化存储基于 BDB Java Edition。BDB 文件格式与版本绑定,如果 Windows 上的 Apollo 版本和 Linux 上的不一致,启动时很可能出现 BDB 相关的异常,比如:
code复制Log file is not valid log file
或:
code复制Database ... has an unknown format
解决办法只有一条:确保迁移前后 Apollo 版本完全一致。可以在 Linux 上启动前先查看一下 lib 目录下 bdb-je 的 jar 包版本,再和 Windows 侧的对照。如果发现不一致,马上换成同一版本的 Apollo 发行包,不要试图用高版本去打开低版本数据,即使能打开,后续写入也可能触发异常。
6.5 主机名、绑定地址与 brokerName 不一致
Apollo 的 broker 启动后,客户端有时会依赖 broker 的地址或 brokerName 做连接判断。如果原来的 Windows 服务器名或 IP 是多少,迁移后 Linux 上是另一个,客户端配置没有同步改,就会出现连上了但认账失败的情况。
处理思路:broker.xml 里 brokerName 最好保持和原来一样,让客户端侧的配置变更尽可能小。acceptors 里的绑定地址如果原来是具体 IP,现在改成 0.0.0.0 或者把新 IP 同步给客户端。一些业务系统在代码里硬编码了旧 IP,更要提前做好变更单。
6.6 systemd 环境下 JVM 内存参数调整
Apollo 默认启动脚本里会有 JVM 内存参数,保存在 bin/apollo-broker 脚本开头的配置区。如果迁移后消息量大,可能在 Linux 上出现堆内存不足。建议在 systemd 服务里单独加入环境变量覆盖:
ini复制Environment='JAVA_OPTS=-Xms1g -Xmx2g -XX:MaxDirectMemorySize=512m'
具体数值根据业务量调整。记住,Apollo 作为 Java 进程,最大内存受限于系统物理内存和内核 overcommit 配置,不要盲目给太大的 Xmx,避免物理内存不足触发 OOM Killer。
6.7 日志文件里的乱码与编码问题
Windows 下的 log4j.properties 或 broker.xml 如果用 GBK 编码保存,到了 Linux 下可能显示乱码,严重时 XML 解析失败。检查方式:
bash复制file etc/broker.xml
如果显示 ISO-8859 或 Non-ISO extended-ASCII,基本就是编码问题了。建议在 Windows 打包前把 broker.xml 和 properties 文件另存为 UTF-8 编码。如果没有提前处理,迁移后在 Linux 上用 iconv 转换:
bash复制iconv -f GBK -t UTF-8 etc/broker.xml > etc/broker.xml.utf8
mv etc/broker.xml.utf8 etc/broker.xml
这个不常见,但一旦遇到就会莫名折腾很久,写进来提醒一下。
7. 经验总结
整个迁移过程走下来,我最深的体会是:Apache Apollo 迁移本身不难,难的是细节边界。数据文件、配置文件、证书、脚本换行、防火墙、JDK 版本,任何一个环节出问题,启动报错都能把人绕晕。
根据我个人经验,最有价值的几个建议是:
- 迁移前一定要停服,别图省事做在线迁移,BDB 存储在有日志落盘的瞬间会被拷贝出不一致状态,到时候数据恢复的复杂度远超停服几分钟的成本。
- 迁移时的“最低交付物”是
broker.xml + users.properties + groups.properties + data,其他都不是必须的。 - 启动后一定要做一次真实的消息收发测试,光看进程起来不算成功,很多配置问题要等到客户端连上来才会暴露。
- 如果有机会,还是要考虑对 Apollo 做后续替换,毕竟它的上游已经停止维护多年。迁移到 Linux 只是把现有系统从“一个旧坑”挪到“另一个稳定的土坑”,长期来看还是要评估 ActiveMQ Artemis 或其他现代消息中间件。
希望这篇避坑指南能帮你少走几趟弯路。如果你迁移过程中遇到比我这更离谱的报错,欢迎多交流,旧系统的问题往往比新系统更有意思。
