Apache Vysper 配置参数详解
Apache Vysper 0.7 全部配置文件逐项拆解:spring-config.xml 存储与端点、log4j 日志、ehcache 缓存、JVM 参数与 TLS 证书更换,均在 Ubuntu 22.04 实测验证
Apache Vysper 配置参数详解
目标:把 Apache Vysper 0.7 发行包中的每一个配置文件、每一组参数讲清楚:它控制什么、默认值是什么、改了之后如何验证。本文所有参数修改均在 Ubuntu 22.04 服务器上完成「修改 → 重启 → 实测验证」的完整闭环,关键结论均附真实命令回显。
一、配置体系总览
Vysper 0.7 采用 tar.gz 压缩包发布,解压即用,所有配置集中在安装目录的 config/ 子目录下(find . -maxdepth 2 -type d 实测仅 bin config lib log 四个)。config/ 目录内的文件清单与作用如下(实测 ls -l config/):
-rw-r--r-- 1 root root 5955 Oct 16 07:02 spring-config.xml # 服务器核心装配(必须)
-rw-r--r-- 1 root root 4351 Oct 16 07:02 log4j.xml # 日志输出(必须)
-rw-r--r-- 1 root root 9705 Oct 16 07:02 ehcache.xml # 在线状态缓存(可选)
-rw-r--r-- 1 root root 1847 Oct 16 07:02 bogus_mina_tls.cert # 自带 TLS 证书(JKS,2005 年生成)
💡 技术拓展:为什么配置目录能被程序读到?
bin/run.sh把安装目录自身与config/一起加入了 JVM classpath(CLASSPATH="$CLASSPATH_PREFIX:$BASEDIR:$BASEDIR/config:$BASEDIR/lib/*")。因此certificateFile的classpath:前缀实际指向的就是config/目录,这直接解释了第六节的「前导斜杠」坑。
三个业务配置文件(spring-config.xml、log4j.xml、ehcache.xml)的修改都要重启服务才能生效,Vysper 没有运行时热加载。重启的完整命令见《Apache Vysper 常用 CLI 命令与工具详解》。
二、spring-config.xml:服务器核心装配
这是 Vysper 唯一的"主配置",一个 Spring DTD 风格的 XML,负责装配存储注册器、预置账号、网络端点、域名、证书与协议模块。逐段拆解。
2.1 存储注册器:inmemory 与 jcr 两套实现
spring-config.xml 中定义了两个可选的存储注册器 Bean,通过 <alias> 完成二选一:
<bean id="inmemoryStorageRegistry" class="org.apache.vysper.storage.inmemory.MemoryStorageProviderRegistry" />
<bean id="jcrStorageRegistry" class="org.apache.vysper.storage.jcr.JcrStorageProviderRegistry" />
<!-- choose one of the storage registries -->
<alias name="inmemoryStorageRegistry" alias="storageRegistry" />
| 参数 | 值与含义 |
|---|---|
inmemoryStorageRegistry | 纯内存存储,重启后所有账号/花名册/私人数据全部丢失 |
jcrStorageRegistry | 基于 Apache Jackrabbit(JCR 内容仓库)的持久存储,落盘 Derby 数据库 |
alias | 把选中的 Bean 暴露为 storageRegistry 供其他 Bean 引用,切换存储只改这一行 |
实测 JCR 持久化闭环。把 alias 指向 jcrStorageRegistry 并重启:
sed -i 's|<alias name="inmemoryStorageRegistry" alias="storageRegistry" />|<alias name="jcrStorageRegistry" alias="storageRegistry" />|' config/spring-config.xml
kill $(ps -ef | awk '/[S]erverMain/{print $2}')
nohup bin/run.sh > /root/vysper-c1-jcr.log 2>&1 & sleep 15
ss -tlnp | grep 5222
实测输出:
LISTEN 0 50 *:5222 *:* users:(("java",pid=4431,fd=107))
5222 正常监听,JCR 模式开箱即用。第一次以 JCR 模式启动后,工作目录下多出一个 Jackrabbit 仓库:
ls -d /opt/vysper/vysper-0.7/repository && du -sh /opt/vysper/vysper-0.7/repository
/opt/vysper/vysper-0.7/repository
6.0M /opt/vysper/vysper-0.7/repository
启动日志中可确认其内嵌 Derby 数据库版本:
18:33:58,568 |-INFO org.apache.jackrabbit.core.persistence.pool.BundleDbPersistenceManager - Database: Apache Derby, Version: 10.2.1.6
持久性验证:JCR 模式下通过管理命令注册 user4(过程见《Apache Vysper 常用 CLI 命令与工具详解》),然后重启服务,再登录 user4:
cd /root && python3 jcr_persist_check.py
实测输出:
USER4-LOGIN-RESULT: SUCCESS
结论:JCR 模式下通过 XEP-0133 动态注册的账号跨重启存活,而 in-memory 模式下同一操作重启后登录返回 <failure><not-authorized/></failure>(对照实测)。生产上想保住账号,就把 alias 切到 jcrStorageRegistry,或者用 jcr 的同时删掉 2.2 节的预置账号 Bean。
💡 技术拓展:JCR(Java Content Repository,JSR-170/283)是一种面向树形内容的 Java 内容仓库 API,Jackrabbit 是其参考实现。Vysper 用它持久化账号、花名册、私人存储(XEP-0049 Private Data)等,仓库数据全部落在
repository/目录,备份该目录即备份了用户数据。
2.2 预置账号:addUsers Bean
<bean id="addUsers" class="org.apache.vysper.spring.AddUserHelper">
<constructor-arg index="0">
<map>
<entry key="admin@vysper.org" value="CHOOSE A SECURE PASSWORD" />
<entry key="user1@vysper.org" value="CHOOSE A SECURE PASSWORD" />
</map>
</constructor-arg>
<property name="storageProviderRegistry" ref="storageRegistry" />
</bean>
参数说明:
| 位置 | 含义 |
|---|---|
key="admin@vysper.org" | JID(节点名@服务域名),域名必须与 2.5 节的 constructor-arg 一致,否则该账号无法登录 |
value="..." | 明文密码。官方注释特别说明:密码留空会生成随机密码并打印在控制台,默认安装是安全的 |
<property name="storageProviderRegistry" ...> | 把账号注册进当前激活的存储(别名 storageRegistry 指向谁就写进谁) |
官方注释同时提醒:remove if you have persistently stored users——如果已经用持久存储并靠管理命令/带内注册维护账号,应删除本 Bean,避免每次启动都重建固定的演示账号。
实测修改方式(把默认口令替换为测试口令并新增 user1/user2):
cp config/spring-config.xml config/spring-config.xml.orig
sed -i 's|<entry key="admin@vysper.org" value="CHOOSE A SECURE PASSWORD" />|<entry key="admin@vysper.org" value="q1w2e3r4" />\n <entry key="user1@vysper.org" value="q1w2e3r4" />\n <entry key="user2@vysper.org" value="q1w2e3r4" />|' config/spring-config.xml
sed -n '35,44p' config/spring-config.xml
实测输出:
<constructor-arg index="0">
<map>
<entry key="admin@vysper.org" value="q1w2e3r4" />
<entry key="user1@vysper.org" value="q1w2e3r4" />
<entry key="user2@vysper.org" value="q1w2e3r4" />
<!-- 其余为发行包自带的注释占位项,略 -->
</map>
</constructor-arg>
重启后用客户端脚本以 user1/user2 登录并互发消息(脚本见《Ubuntu 22.04 安装 Apache Vysper 指南》安装验证一节),实测输出:
--- [user1] TLS established: TLSv1.2
=== [user1] login OK, full jid = user1@vysper.org/051a8713c60e4704b1c968f2d4d57d0e
=== [user2] login OK, full jid = user2@vysper.org/386c8ad3baf942ca8e3951a6b36247c9
ALL MESSAGE EXCHANGE OK
两个 map 内账号均可 SASL 认证通过,证明每一条 <entry> 都会在启动时写入当前存储。
💡 技术拓展:预置账号只是演示便利,Vysper 支持三种账号维护方式:① 本节的 addUsers 静态预置;② XEP-0133 服务管理命令在线加账号(需 admin 权限,实测见《Apache Vysper 常用 CLI 命令与工具详解》);③ XEP-0077 带内注册由用户自助注册(见 2.6 节实测)。三者账号最终都落在当前存储注册器里。
2.3 TCP 端点:TCPEndpoint
<bean id="tcpEndpoint" class="org.apache.vysper.mina.TCPEndpoint">
</bean>
默认属性全部留空:使用默认端口 5222、TLS 证书取 server Bean 中配置的那份。TCPEndpoint 支持哪些属性,直接看字节码(javadoc 与发行包文档均不完整):
/opt/jdk/jdk8u504-b01/bin/javap -classpath lib/vysper-core-0.7.jar org.apache.vysper.mina.TCPEndpoint
实测输出:
public class org.apache.vysper.mina.TCPEndpoint extends org.apache.vysper.xmpp.net.Endpoint {
public org.apache.vysper.mina.TCPEndpoint();
public void setPort(int);
public void setSSLEnabled(boolean);
public void setSSLCertificateFile(java.lang.File);
public void setSSLCertificatePassphrase(java.lang.String);
}
| 属性 | 类型 | 说明 |
|---|---|---|
port | int | TCP 监听端口,默认 5222 |
sslEnabled | boolean | 是否在端点级启用 TLS(默认继承 server Bean 的证书配置) |
sslCertificateFile | File | 端点专属证书文件 |
sslCertificatePassphrase | String | 端点专属证书口令 |
实测改端口:在 Bean 内加一行 <property name="port" value="15222"/>,重启后验证:
sed -i 's|<bean id="tcpEndpoint" class="org.apache.vysper.mina.TCPEndpoint">|<bean id="tcpEndpoint" class="org.apache.vysper.mina.TCPEndpoint">\n <property name="port" value="15222"/>|' config/spring-config.xml
kill $(ps -ef | awk '/[S]erverMain/{print $2}')
nohup bin/run.sh > /root/vysper-c1-port.log 2>&1 & sleep 12
ss -tlnp | grep -E "15222|5222"
实测输出:
LISTEN 0 50 *:15222 *:* users:(("java",pid=3973,fd=107))
5222 不再监听、15222 生效。客户端脚本把目标端口改成 15222 后重跑,登录与消息互发全部成功:
--- [user1] TLS established: TLSv1.2
=== [user1] login OK, full jid = user1@vysper.org/...
=== [user2] login OK, full jid = user2@vysper.org/...
ALL MESSAGE EXCHANGE OK
验证后删除该行还原 5222。改端口一般用于 5222 被占用或想规避运营商封禁的场景。
💡 技术拓展:XMPP 官方约定 5222 为客户端到服务器(c2s)端口、5269 为服务器间联帮(s2s)端口。Vysper 0.7 没有实现 s2s(服务器到服务器)联帮,所以这里只关心 5222。
2.4 BOSH 端点:BoshEndpoint
发行包默认把 BOSH 端点注释掉。取消注释即可启用 HTTP 传输(XEP-0124/0206):
<bean id="boshEndpoint" class="org.apache.vysper.xmpp.extension.xep0124.BoshEndpoint">
<property name="accessControlAllowOrigin">
<list><value>*</value></list>
</property>
<property name="port" value="8080" />
<property name="contextPath" value="/bosh" />
</bean>
并在 server Bean 的 endpoints 列表中加 <ref bean="boshEndpoint"/>。逐参数说明:
| 参数 | 默认值 | 含义 |
|---|---|---|
port | 无(必须显式配置) | HTTP 监听端口,示例用 8080 |
contextPath | 无(必须显式配置) | HTTP 上下文路径,客户端 POST 地址为 http://IP:PORT/contextPath |
accessControlAllowOrigin | 无 | BOSH 响应的 CORS 头 Access-Control-Allow-Origin 列表,浏览器端 Web 客户端跨域调用必须配,* 表示全部放行 |
BoshEndpoint 完整属性(javap 实测,除 setContextPath/setAccessControlAllowOrigin/setPort 外还有三组 SSL 属性):
public void setSSLEnabled(boolean);
public void setSSLCertificateKeystore(java.lang.String);
public void setSSLCertificateKeystorePassword(java.lang.String);
后三个属性可在 HTTP 层配置独立证书与口令(HTTPS BOSH)。setSSLEnabled(true) 且不单独设置 keystore 时,会退回使用内嵌 Jetty 的默认行为——0.7 代码较老,此组参数建议以"能配"为预期,若需 HTTPS 建议前置 Nginx 反代终结 TLS,而不是依赖这个 2011 年的 Jetty。
实测启用:取消注释并重启后:
ss -tlnp | grep -E "5222|8080"
LISTEN 0 50 *:8080 *:* users:(("java",pid=5676,fd=119))
LISTEN 0 50 *:5222 *:* users:(("java",pid=5676,fd=113))
用 curl 模拟 BOSH 客户端创建会话(注意 contextPath 有尾斜杠,访问 /bosh 会 302 到 /bosh/):
curl -s -i -X POST -H 'Content-Type: text/xml; charset=utf-8' \
--data "<body content='text/xml; charset=utf-8' hold='1' rid='100000001' to='vysper.org' ver='1.9' wait='60' xml:lang='en' xmlns='http://jabber.org/protocol/httpbind'/>" \
http://127.0.0.1:8080/bosh/ | head -12
实测输出(会话创建成功,拿到 sid):
HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Access-Control-Allow-Origin: *
Server: Vysper/0.5
<body wait='60' xmlns:stream='http://etherx.jabber.org/streams' inactivity='10' polling='5' ack='1' sid='fe93e78c-4ce5-4b3f-a52f-ec0a99b601e2' ver='1.9' requests='2' hold='1' secure='false' from='vysper.org' authid='fe93e78c-4ce5-4b3f-a52f-ec0a99b601e2' maxpause='120' xmlns='http://jabber.org/protocol/httpbind'/>
会话创建成功(拿到 sid)。注意 Server: Vysper/0.5——0.7 发行包的 BOSH 响应头仍写着 0.5,属于历史遗留的版本串,不代表运行版本,可通过 2.6 节的 SoftwareVersionModule(返回 0.7)交叉验证。
💡 技术拓展:BOSH(Bidirectional-streams Over Synchronous HTTP)让 XMPP 跑在 HTTP 长轮询上,是 WebSocket 之前浏览器端即时通讯的主流方案。
rid是请求序号、sid是会话标识、hold是服务端挂起请求数,这几个参数是 BOSH 协议的握手核心。
2.5 server Bean:域名、证书与模块装配
server Bean 是整个 Spring 装配的终点,逐行拆解:
<bean id="server" class="org.apache.vysper.spring.SpringCompatibleXMPPServer"
init-method="init" destroy-method="destroy">
<!-- TODO change domain name to your domain -->
<constructor-arg value="vysper.org"/>
<property name="endpoints">
<list>
<ref bean="tcpEndpoint"/>
<ref bean="boshEndpoint"/>
</list>
</property>
<property name="storageProviderRegistry" ref="storageRegistry" />
<property name="certificateFile" value="classpath:bogus_mina_tls.cert" />
<property name="certificatePassword" value="boguspw" />
<property name="modules" ><list> ... </list></property>
</bean>
| 参数 | 说明 |
|---|---|
<constructor-arg value="vysper.org"/> | 服务域名,决定流头 from=、账号 JID 后缀、disco 身份。改名必须同步改 2.2 节所有账号 key 与 2.4 节 BOSH 的 to 属性 |
endpoints | 启用的端点列表,TCP 必选,BOSH 可选 |
storageProviderRegistry | 固定引用别名 storageRegistry(即 2.1 节二选一的结果) |
certificateFile | TLS 证书文件(JKS keystore 格式),默认用内置 2005 年的 bogus 证书 |
certificatePassword | 证书 keystore 口令,与 bogus_mina_tls.cert 配套为 boguspw |
modules | 启用的协议模块清单 |
域名实测:把 vysper.org 改为 im.demo.local 并重启:
sed -i 's|<constructor-arg value="vysper.org"/>|<constructor-arg value="im.demo.local"/>|' config/spring-config.xml
kill $(ps -ef | awk '/[S]erverMain/{print $2}')
nohup bin/run.sh > /root/vysper-domain2.log 2>&1 &
sleep 12
裸流探测确认服务器身份已变(单行脚本,实测输出见下):
python3 -c "
import socket
s = socket.create_connection(('127.0.0.1', 5222), 10)
s.sendall(b\"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' to='im.demo.local' version='1.0'>\")
print(s.recv(4096).decode()[:300]); s.close()"
实测输出:
<?xml version="1.0" encoding="UTF-8"?><stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" from="im.demo.local" version="1.0" id="7f3ca97451c546a3a86ee8f7ba8a1ef7">
流头 from="im.demo.local" 生效。注意此时旧账号 user1@vysper.org 用的 SASL 认证 JID 与新域名不匹配会登录失败——改域名必须同步改账号,这是本参数最大的联动项。
modules 清单与实测状态(当前发行包内 jar 均在 classpath,取消注释即启用):
| 模块 Bean | XEP | 功能 | 本文实测状态 |
|---|---|---|---|
| SoftwareVersionModule | XEP-0092 | 软件版本应答 | 启用可用 |
| EntityTimeModule | XEP-0202/0203 | 实体时间 | 启用可用 |
| VcardTempModule | XEP-0054 | 用户名片 | 启用可用 |
| XmppPingModule | XEP-0119 | XMPP Ping | 启用可用 |
| PrivateDataModule | XEP-0049 | 私人数据存储 | 启用可用 |
| AdhocCommandsModule | XEP-0050 | Ad-hoc 命令框架 | 启用可用 |
| ServiceAdministrationModule | XEP-0133 | 服务管理命令(加用户等) | 启用可用,addAdmins 列表指定管理员 JID |
| MUCModule | XEP-0045 | 多人聊天室 | 启用可用 |
| PublishSubscribeModule | XEP-0060 | 发布订阅 | 发行包已含 jar,默认注释 |
| InBandRegistrationModule | XEP-0077 | 带内注册 | 默认未启用,见 2.6 实测 |
addAdmins 是 ServiceAdministrationModule 的属性,决定谁可以执行 XEP-0133 管理命令,值必须是完整 JID 且与登录账号一致:
<bean class="org.apache.vysper.xmpp.modules.extension.xep0133_service_administration.ServiceAdministrationModule" >
<property name="addAdmins">
<list><value>admin@vysper.org</value></list>
</property>
</bean>
2.6 启用 XEP-0077 带内注册(模块开关实测)
在 modules 列表加一行 Bean 即可启用带内注册:
<bean class="org.apache.vysper.xmpp.modules.extension.xep0077_inbandreg.InBandRegistrationModule"/>
重启后实测完整闭环(先 admin 上线,再注册 user5 并向 admin 发消息):
<<< [reg] <iq xmlns="jabber:client" type="result" id="r1" from="vysper.org"><query xmlns="jabber:iq:register"><registered/><username>user5</username></query></iq>
=== [user5] login OK
=== USER5 -> ADMIN MESSAGE OK (XEP-0077 on Spring runtime)
注册应答 type="result" 且带 <registered/> 标记,新账号立即登录成功。启用前需评估:0.7 的带内注册没有验证码/邮件确认等防滥用机制,暴露公网即等于开放注册,生产环境建议关闭或只在内网启用。
三、log4j.xml:日志参数
Vysper 用 log4j 1.x,配置在 config/log4j.xml,定义三个 appender:
| Appender | 类型 | 输出目标 | Threshold 默认值 |
|---|---|---|---|
CONS | ConsoleAppender | System.out(即 nohup 重定向的文件) | TRACE |
FILE | RollingFileAppender | log/vysper_server.log | INFO |
STANZA | RollingFileAppender | log/stanza.log | INFO |
三个 appender 的共同参数:
| 参数 | 说明 |
|---|---|
File | 日志文件路径,相对 run.sh 的启动目录 |
Append | 追加模式,重启不清空 |
MaxFileSize | 单文件上限 500KB,超过滚动 |
MaxBackupIndex | 保留 1 个滚动备份,即最多 vysper_server.log + vysper_server.log.1 两个文件 |
ConversionPattern | 输出格式:`时间 |
分类别调优(category 节点):
<category name="org.apache.mina"><priority value="WARN"/></category>
<category name="org.apache.vysper"><priority value="DEBUG"/></category>
<category name="stanza"><priority value="INFO"/><appender-ref ref="STANZA"/></category>
stanza 类别专收 XMPP 报文:stanza.log 一行一条记录收发的每一个 stanza(方向、类型、内容摘要),排查协议问题时价值最高。实测样式:
INFO 07:25:58,263 | client | iq.id='d1'.to='vysper.org'.type='get'.query.disco.info
INFO 07:25:58,264 | server | iq.id='d1'.from='vysper.org'.type='result'.query.disco.info.feature='http://jabber.org/protocol/disco#info'.feature='jabber:iq:version'.feature='urn:xmpp:time'.feature='vcard-temp'.feature='urn:xmpp:ping'
INFO 07:25:58,265 | client | message.body.hello from user1 at 2026-10-16 07:25:58
INFO 07:25:58,304 | server | message.body.reply from user2
其中 client 前缀代表客户端发来的报文、server 前缀代表服务器回出的报文。
实测 Threshold 调优:把 CONS 的 Threshold 从 TRACE 改为 WARN,观察控制台输出量变化:
sed -i '0,/<param name="Threshold" value="TRACE"\/>/s//<param name="Threshold" value="WARN"\/>/' config/log4j.xml
kill $(ps -ef | awk '/[S]erverMain/{print $2}') 2>/dev/null; sleep 3
cd /opt/vysper/vysper-0.7 && nohup bin/run.sh > /root/vysper-log4j-warn.log 2>&1 &
sleep 12
wc -l < /root/vysper-log4j-warn.log
实测输出:
5
同样 12 秒窗口,TRACE 阈值下控制台日志超过 60 行(模块加载、存储注册、端点监听等全量 INFO),WARN 阈值下仅剩 5 行(只剩真正的 WARN/ERROR)。生产后台运行建议 WARN;排障期改回 TRACE 并重启。注意改 CONS 不影响 FILE/STANZA,log/ 下两个文件始终按自身阈值记录。
四、ehcache.xml:在线状态缓存
config/ehcache.xml 供 EhCachePresenceCacheImpl 使用——该类从 classpath 加载此文件构建缓存(源码注释明确:缓存策略 LRU,参数可通过 ehcache.xml 调整)。默认只有两个 cache 定义:
<cache name="PresenceCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
<cache name="JIDCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LRU"/>
| 参数 | 值 | 含义 |
|---|---|---|
maxElementsInMemory | 10000 | 内存中最多缓存的元素数(在线状态条目/JID 条目) |
eternal | true | 条目永不过期(在线状态有自己的生命周期,不走 TTL) |
overflowToDisk | false | 不落盘,纯内存 |
memoryStoreEvictionPolicy | LRU | 超过上限时淘汰最久未使用条目 |
大规模式部署时按并发在线用户数上调 maxElementsInMemory 即可;该文件缺失或非法时缓存类会回退默认行为,但日志会出现 WARN。
五、JVM 参数:bin/setenv.sh
run.sh 启动时会 source 同目录 setenv.sh(默认不存在,需自建)。这是配置 JAVA_HOME 与 JVM 内存的正规入口:
cat > bin/setenv.sh <<'EOF'
export JAVA_HOME=/opt/jdk/jdk8u504-b01
export JAVA_OPTS="-Xms256m -Xmx256m"
EOF
chmod +x bin/setenv.sh
参数逐项说明:
| 变量 | 说明 |
|---|---|
JAVA_HOME | 指定 JDK。Vysper 0.7 是 2011 年代码,实测 Temurin 8(1.8.0_504-b01)可用;更高版本 JDK 未通过验证(Spring 2.x 字节码增强与老依赖存在兼容风险) |
JAVA_OPTS | 传给 JVM 的参数。-Xms/-Xmx 设堆大小,100 并发内 256m 足够 |
CLASSPATH_PREFIX | run.sh 支持的高级变量:需要外加 jar(如自研模块)时把目录或 jar 路径赋给它 |
启动后用 jps 验证参数确实生效:
/opt/jdk/jdk8u504-b01/bin/jps -v | grep ServerMain
实测输出:
3948 ServerMain -Xms256m -Xmx256m
💡 技术拓展:若
JAVA_OPTS写成老式-XX:PermSize=64m -XX:MaxPermSize=256m,JDK 8 启动日志会输出Java HotSpot(TM) 64-Bit Server VM warning: ignoring option PermSize...; support was removed in 8.0——参数被忽略但不影响启动(持久代元空间化)。该警告在 JDK 7 下不会出现。日志格式详见实测记录,属无害警告,建议直接删掉 PermSize 参数。
六、TLS 证书:更换自带 2005 年证书全流程
默认证书 bogus_mina_tls.cert 是 2005 年生成的 511 位 MD5 签名 JKS(口令 boguspw),现代客户端对其约束很严:OpenSSL 3.0 默认安全级别直接拒绝(ca md too weak / key too small),JDK 8 客户端报 Certificates do not conform to algorithm constraints,python 也必须在握手时强制 TLS 1.2 才能连上(详见《Ubuntu 22.04 安装 Apache Vysper 指南》的兼容性说明)。生产环境必须换证,完整流程实测如下。
6.1 生成新证书
/opt/jdk/jdk8u504-b01/bin/keytool -genkeypair -alias vysper -keyalg RSA -keysize 2048 \
-sigalg SHA256withRSA -validity 3650 -keystore config/vysper_tls.jks \
-storepass changeme -keypass changeme \
-dname "CN=vysper.org, OU=Demo, O=Demo, L=Beijing, C=CN"
命令无报错即生成成功,config/vysper_tls.jks 约 2.2KB。Vysper 的 certificateFile 实际加载的是 JKS keystore(内置证书也是 JKS),keytool 默认格式即 JKS,直接可用。
6.2 配置与「前导斜杠」坑
sed -i 's|<property name="certificateFile" value="classpath:bogus_mina_tls.cert" />|<property name="certificateFile" value="/opt/vysper/vysper-0.7/config/vysper_tls.jks" />|; s|<property name="certificatePassword" value="boguspw" />|<property name="certificatePassword" value="changeme" />|' config/spring-config.xml
重启后 5222 不监听、进程在,日志报错(-A3 只截关键三行):
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException:
class path resource [opt/vysper/vysper-0.7/config/vysper_tls.jks] cannot be opened because it does not exist
原因:certificateFile 的值按 Spring 资源路径解析,绝对路径的前导 / 被剥离,当成 classpath 相对路径去找。正确写法是加 file: 前缀:
<property name="certificateFile" value="file:/opt/vysper/vysper-0.7/config/vysper_tls.jks" />
<property name="certificatePassword" value="changeme" />
6.3 验证:openssl、python、Java 三方回归
重启后先用 openssl 确认新证书生效(这次不降级、不加任何放宽参数):
echo | openssl s_client -starttls xmpp -connect 127.0.0.1:5222 -servername vysper.org 2>/dev/null | openssl x509 -noout -subject -dates
实测输出:
subject=CN=vysper.org, OU=Demo, O=Demo, L=Beijing, C=CN
notBefore=Oct 16 07:04:02 2026 GMT
notAfter=Oct 13 07:04:02 2036 GMT
再看协商结果:
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Verification error: self-signed certificate
(命令同上:echo | openssl s_client -starttls xmpp -connect 127.0.0.1:5222 2>/dev/null | grep -E "New, |Verification"。)老证书下 openssl 协商 TLS 1.2 且报 ca md too weak;换新证书后 TLS 1.3 直接协商成功。Verification error: self-signed certificate 是自签证书的固有提示,客户端侧导入信任或跳过校验即可,不影响服务端配置正确性。
python 客户端(TLS 1.2)与 Java 客户端回归登录+消息互发,实测尾部回显:
ALL MESSAGE EXCHANGE OK
ALL OK: legacy-TLS login + message exchange verified
验证完成后如需还原默认证书,把两行改回 classpath:bogus_mina_tls.cert / boguspw 重启即可。
💡 技术拓展:正式环境建议用 Let's Encrypt 证书(PEM+Key),但 Vysper 只认 JKS,需先用 openssl 把 PEM 转 PKCS12、再用 keytool 导入 JKS;或者更省事——5222 后面挂 Nginx/Haproxy 做 TLS 终结,Vysper 端点只对内网监听。
七、常见问题与排查
1. 改完配置 5222 不监听,但进程还在? 先看启动日志(nohup 重定向的文件)grep -A3 Caused。本机实测两种典型:证书路径前导斜杠坑(见 6.2);端口被上一实例占用——此时 Vysper 绑定失败但不会退出,进程变成"僵尸",先 ps -ef | grep ServerMain 清理所有旧实例再启动:
Caused by: org.apache.mina.core.RuntimeIoException: Failed to bind
... Address already in use
2. BOSH 响应头为什么写 Server: Vysper/0.5? 0.7 发行包里 BOSH 模块的版本串没更新,历史遗留,与实际运行版本无关。核实真实版本用 XEP-0092:<iq type='get' id='v1'><query xmlns='jabber:iq:version'/></iq> 应答 <version>0.7</version>。
3. XEP-0133 加用户的表单字段名是什么? 必须用 accountjid / password / password-verify(单数、jid-single),不是较新 XEP-0133 修订里的 accountjids 复数形式,字段名错了服务器返回 action-not-allowed。
4. 改了域名后客户端登录失败? SASL PLAIN 认证的 authzid/authcid 里 JID 后缀必须与新域名一致,预置账号的 key 也要同步改,否则 <failure><not-authorized/></failure>。
5. JCR 模式报 Jackrabbit 初始化错误? 确认 lib/ 下 jackrabbit-*、derby-*.jar 在 classpath(发行包默认齐全),且启动目录可写(会生成 repository/)。Vysper 没提供仓库路径参数,仓库位置固定为启动时的工作目录,因此用 systemd 托管时务必固定 WorkingDirectory。
本文参数均在 Ubuntu 22.04 + Vysper 0.7 + Temurin JDK 8(1.8.0_504-b01)环境实测,交叉验证可复现。安装与闭环验证见《Ubuntu 22.04 安装 Apache Vysper 指南》,命令行工具与运行观测见《Apache Vysper 常用 CLI 命令与工具详解》。