做性能测试这些年我几乎每次拿到一台新的压测机器第一步不是急着跑脚本而是先打开JMeter安装目录里的bin文件夹看一眼system.properties有没有被做过手脚。这个文件平时存在感很低很多人跑一年测试都不碰它一次可真遇到启动卡在半路、接口证书报错、自定义Jar死活加载不出来这类问题的时候它往往是真正的根源。这篇就把system.properties里最常用、也最容易踩坑的配置项系统过一遍并说明哪些问题别指望它解决。正在搭JMeter压测环境、被各种诡异环境问题折磨过的新手可以参考老手也可以当一份速查索引翻一翻。1. 先分清system.properties和jmeter.properties的区别1.1 位置、加载机制和文件本质在JMeter的bin目录下有一堆.properties文件system.properties和jmeter.properties看起来很像但干的事完全不同。system.properties本质上是给JVM用的系统属性文件JMeter进程启动时会把里面的键值对塞进Java的系统属性池里。Java里所有通过System.getProperty()能读到的值都可以在这里改默认。而jmeter.properties是JMeter自己维护的应用配置里面存的是JMeter内部框架要读的业务配置比如结果保存格式、日志级别、UI行为这些。从加载时机来看两个文件都在启动早期被读但作用域不一样。系统属性影响的是整个Java运行时环境像SSL、默认编码、安全随机源这些基础行为JMeter属性影响的则是工具本身的功能开关。打个比方system.properties像你给操作系统改用户环境变量jmeter.properties像给某个软件改自己的设置偏好。二者偶尔会重叠但定位完全不同。我在实际工作中有一条原则能写进jmeter.properties的JMeter业务参数就不要挪到system.properties里。因为JMeter很多配置项是按约定键名从jmeter.properties读取的硬塞到系统属性文件里某些版本下不一定会被对应功能解析最后就是改了等于没改。把两者分开排查问题时才能快速判断该看哪个文件。1.2 为什么JMeter要单独拆一个系统属性文件单独一个文件放系统属性核心原因就是隔离。jmeter.properties里本来就有几千个内部参数如果再堆一堆javax.net.ssl.*、java.security.*这种系统级参数文件会变得非常臃肿团队协作时大家互相改同一个文件也容易起冲突。拆成单独的system.properties责任边界就清楚了应用层配置归jmeter.properties系统层配置归system.properties。另一个好处是便于统一环境。实际项目里一台服务器可能部署多套JMeter或者多台压测机要跑同一套压测方案所有人只需要维护一份统一的system.properties就能保证所有实例的基础环境一致。这比我每次都在命令行里手拼JVM参数要靠谱得多也方便交给自动化部署工具去处理。以下是两个文件的定位对照方便收藏对比项system.propertiesjmeter.properties本质JVM系统属性JMeter应用属性加载时机启动时注入系统属性池JMeter内部初始化时读取主要影响SSL、编码、安全随机源、网络库底层行为采样器默认值、结果保存格式、UI开关等典型键javax.net.ssl.、java.security.、search_pathslog_level、mode、结果文件格式项放错位置的后果业务配置可能不生效且无报错系统级属性反过来也一样这里再多说一句很多人以为在system.properties里写jmeter.properties的键也能生效这个理解不完整。能不能生效取决于JMeter版本和具体键的实现不要默认两个文件可以互相替代。2. 实际最常用的配置项逐个拆解2.1 扩展Jar与自定义代码search_pathsJMeter加载第三方扩展最容易的方式是把Jar丢到lib或lib/ext目录但有些团队会把公共依赖统一放在专门的目录不希望谁去动JMeter的lib文件夹。这时候就需要search_paths这个键它写在system.properties里告诉JMeter除了默认lib目录之外还去哪几个目录搜索Jar包。多个路径之间用系统路径分隔符分开Windows下是分号Linux和macOS下是冒号。这个坑我见过太多次有人习惯在Windows上写分号把同一个文件拷到Linux服务器上继续用分号结果扩展死活加载不出来。路径结尾不要画蛇添足加多余的斜杠JMeter拼接路径时容易出怪问题。路径支持相对路径和绝对路径相对路径是相对于JMeter的bin目录但我个人强烈建议用绝对路径因为压测脚本经常从不同工作目录启动相对路径一飘就是好几个小时排查。还有一个容易忽略的点search_paths不会替换lib目录的默认搜索只是额外追加所以原来放在lib里的扩展不受影响。配置这个键之后如果改动的是某个Jar里面的类文件内容光重启JMeter可能还不够有时候JVM对Jar的缓存会让人以为改坏了实际上需要清理临时目录或确保旧的类卸载干净。2.2 网络层SSL证书、信任库和协议开关HTTPS接口是压测里绕不开的场景证书配置一旦错了连握手都过不去。system.properties里最常动的是这几个键jmeter.ssl.keyStore和jmeter.ssl.keyStorePassword双向认证时客户端证书库的位置和密码jmeter.ssl.trustStore和jmeter.ssl.trustStorePassword服务端证书信任库的位置和密码ssl.keystore.type证书库类型常见的是PKCS12或JKS。做HTTPS双向认证时你需要把客户端证书放到指定KeyStore里再把文件路径写进system.properties。不同JMeter版本对默认证书库类型的处理不一样有些环境默认旧版JKS如果你传的证书是PKCS12格式就会报类似keystore password was incorrect或者找不到key的错误。排查这个问题时第一件事不是怀疑密码而是确认证书库类型和JMeter默认类型是否匹配。另外要提醒一句如果是普通的HTTPS单向认证不建议在system.properties里全局关闭证书校验。更合适的做法是在HTTP请求层忽略证书校验影响范围只限当前请求。全局关掉证书校验会让同一份测试计划里所有需要严格验证的请求都失去保护风险很大。我的经验是把system.properties里的证书配置留给双向认证场景其他情况别在这里做全局动作。2.3 字符编码处理乱码问题的关键入口压测中文接口时偶尔看到返回体乱码或者CSV参数文件读出来是乱码这类问题经常被甩锅给“脚本写得不行”但其实元凶往往是编码没有在系统层面统一。sampleresult.default.encoding在很多JMeter版本里是放在jmeter.properties的但有人习惯在system.properties里覆盖这个键决定采样结果默认按什么字符集解析。如果接口返回UTF-8而JMeter用的是操作系统平台默认编码Windows中文环境很容易变成GBK结果树里看全是看不懂的内容。所以要么在每个HTTP请求里设置响应编码要么在这个配置文件里统一设成UTF-8。还有一个相关的系统属性是file.encoding它影响JMeter读取本地文件的默认编码。CSV Data Set Config里的文件路径如果没有显式指定编码就会走这个默认值。不过这里有个坑较新的JDK版本里运行时修改file.encoding有一定限制很可能出现你设置了但没报错也不生效的情况。所以最稳妥的做法是双保险CSV Data Set Config里显式指定文件编码HTTP请求的响应编码也显式填好system.properties只是用来兜底。别把全部希望压在这一个文件上不然某个接口自己带了个怪异的charset头你在这边改半天也没有用。2.4 主从模式服务端口和RMI配置分布式压测时Master和Slave之间通信依赖RMI。server_port和server.rmi.port这类端口配置主要作用是固定服务端口方便防火墙放行和安全管控。默认情况下JMeter的RMI端口是随机的真实机房环境里经常因为随机端口被防火墙拦死而导致连接失败在system.properties里把端口固定下来就非常关键。注意主从模式下不是只改Master一台机器。Slave上的system.properties也要一并配置两边端口、编码、证书等基础设置必须保持一致。我在项目里踩过最大的坑就在这Master上配得整整齐齐Slave那边完全没动然后怎么连都失败排查到最后发现是Slave还是默认配置。另外某些JMeter版本把RMI相关参数放在jmeter.properties里维护但system.properties中的系统属性仍然会被底层RMI库读取。实际配置时两个文件都可能需要动不同版本读法不一样不要想当然只改一个。分布式压测还有一个隐含问题如果你有多台Slave很难手工确认每台机器的配置都同步了。所以我后面会建议写部署脚本做自动同步和校验单靠人肉点检早晚漏一台。2.5 启动慢和JDK安全随机源java.security.egdJMeter在Linux服务器上启动时卡在JVM running那一步半天不出来是我见过最高频的现场问题之一。这通常不是JMeter的故障而是JVM的安全随机数初始化在等系统熵源。解决思路是给java.security.egd设置成file:/dev/./urandom。这个配置在Java中间件领域非常常见。原理是让JVM用非阻塞的伪随机数生成器来初始化安全随机对象避免在熵不足的服务器上长时间等待。为什么路径里要特意写成带一个点的/dev/./urandom我的理解是某些Java版本对路径做了字符串判断写成这个形式可以绕开它默认的校验分支直接用我们想要的那个随机源。实测效果明显一台普通压测机没配的时候启动要三十多秒配完之后几秒就起来了。当然这个问题也可以在启动脚本里通过JVM参数来处理比如在jmeter.sh的JVM_ARGS里加-Djava.security.egdfile:/dev/./urandom。两种做法不冲突。我习惯在system.properties里配置因为这样更利于统一分发多台压测机拷同一份文件就都生效不用每台去改启动脚本。另外提醒一下遇到启动卡顿不要只靠日志瞎猜用jstack看一下线程栈通常能看到卡在SecureRandom相关的调用上定位比瞎猜快得多。2.6 其他值得留个心眼的系统属性还有几个偶尔会用到user.language和user.country可以强制JMeter运行时的区域语言有些日志和界面显示异常与区域设置有关部分java.util.logging.*属性在排查底层类加载或者做二次开发时可能用到javax.net.ssl.keyStore和javax.net.ssl.trustStore这两个是Java更底层的通用系统属性如果你挂在JMeter外部的自定义网络库里有可能被读取要注意和JMeter自己的jmeter.ssl.*区分开。说句实际的system.properties不是越满越好。它影响的是整个运行环境改坏了排查成本非常高。我一般保持最小修改原则只放那些确实需要覆盖的系统属性能用测试计划局部配置解决的就不要提升到系统级降低影响面。3. 三个常见场景的配置实操3.1 场景一Linux启动卡住几十秒有一次在服务器上第一次执行命令行模式压测命令敲下去之后日志已经打印了JVM running但测试任务迟迟不开始等了差不多半分钟才动起来。刚开始我还以为是测试计划加载慢但换了空测试计划也慢才意识到是JVM启动本身卡住了。排查后确认是安全随机源的问题处理方式很简单在bin目录的system.properties里添加一行java.security.egdfile:/dev/./urandom保存后重启JMeter同样一个测试计划启动时间从原来的30秒左右降到3到5秒。这个配置后来又同步到另外几台压测机上效果一致。为什么写在system.properties而不是改启动脚本呢因为在这套环境里多台机器的启动脚本是镜像统一烧录的不想为了其中一台单独改模板。而system.properties本身就在bin目录自动化部署时统一分发一份文件就都覆盖了。这套思路适用于大部分基础配置的统一管理但前提是你得保证分发脚本会真的把文件覆盖到位不然就是一台台手工改又回到老路。3.2 场景二中文响应乱码某一次压测一个中文JSON接口在结果树里看到一串问号和乱码。当时不想在每个HTTP请求上都手动加响应编码就用system.properties来统一兜底。设置内容如下sampleresult.default.encodingUTF-8 file.encodingUTF-8重启之后大部分带中文的响应都正常了但还是有个别接口乱码依旧。一查发现那个接口在响应头里自带了非UTF-8的charset声明JMeter会优先按响应头里的编码去解码这时system.properties里的默认值不生效。所以这个配置只解决“响应头无编码信息”的情况接口自带的内容还是得回到接口层去核对。这件事给我的教训是配置文件能做全局默认值但永远不要指望它解决所有编码问题。最可靠的组合拳还是请求层面显式指定编码配置文件当作最后一道防线。遇到个别异常要从数据源头看不要在一个配置上反复折腾。3.3 场景三第三方Jar加载不出来有一个项目里封装了加解密工具Jar放在公共目录/data/common/jars下机器上运行着多套JMeter不能往其中任何一套的lib/ext里扔否则互相影响。这时候search_paths就派上用场了。在system.properties里添加search_paths/data/common/jars如果有多个目录Linux下用冒号分隔search_paths/data/common/jars:/data/tools/extra配置完重启JMeter在JSR223 Sampler里直接import自己封装好的类不再报类找不到。这个方案比直接丢lib目录干净扩展和核心JMeter完全分离升级JMeter版本时不需要担心自己的Jar混入默认目录导致兼容性问题。这里有另一个经验如果你改动了Jar里的代码内容重新打包后放到search_paths目录偶尔会遇到重启后还是旧行为的情况。这不是配置失效而是JVM层面的缓存残留必要时清除临时目录缓存或者确认JMeter进程真的完全退出后再启动。3.4 怎么确认你的配置真的生效了配置写进去了只是第一步。验证配置是否生效我一般用两个方法。最快的方法是借助JMeter自带的调试采样器在测试计划里加一个Debug Sampler执行后展开输出面板里面会列出JVM的系统属性列表。搜索你配置的键名能找到就说明已经被载入系统属性池。想要更精确的验证可以写一小段JSR223脚本用Groovy打印出来log.info(egd System.getProperty(java.security.egd)); log.info(search System.getProperty(search_paths));这里有个极易踩的坑Java的System.getProperty读的是系统属性而JMeter自身的属性要用props.get(xxx)去读。验证时先用错API就会以为配置没生效白白浪费半天排查时间。还有一个点如果配置和SSL相关不能只看属性被加载了就认为万事大吉。系统属性被加载和真正被底层网络库使用是两个层面后者必须实际发起一次HTTPS请求确认握手成功。像这种配置最终验证一定要回归到真实请求上。4. 配置不生效排查与避坑清单4.1 改了文件却不生效的几种常见原因这里我按实际遇到频率整理了一张排查顺序表能省很多时间现象可能原因快速处置启动特别慢安全随机源阻塞配java.security.egd为file:/dev/./urandom响应乱码默认编码与接口不一致配sampleresult.default.encodingUTF-8请求层也显式指定Jar加载失败search_paths没配或路径分隔符错了检查路径和Windows/Linux分隔符分布式连接失败端口未固定或Slave配置没同步固定端口并同步所有节点配置配置不生效文件路径错、键名拼错、被启动参数覆盖用Debug Sampler验证系统属性最容易踩的五个原因逐个说明。第一修改的不是JMeter实际使用的bin目录。有些环境装了多个JMeter终端里PATH指向的和图形界面打开的可能是不同安装目录。先确认当前进程的真实路径否则改了等于白改。第二注释或值里含有非ASCII字符。Java的properties文件默认行为对非ASCII字符处理比较敏感注释里的中文尤其容易出问题。解决办法是配置内容尽量用纯ASCII中文注释改成英文实在要写中文就用\uXXXX转义。第三键名拼写错误。properties文件不会报警告写错了就静默忽略。比如search_paths拼成search_path排查很久才发现是拼写问题。第四被启动脚本里的同名JVM参数覆盖。有些环境在jmeter.sh里预先设置了JVM_ARGS里面的系统属性优先级往往高于文件配置。想排查的话临时清空启动脚本里的自定义参数再验证system.properties是否生效。第五进程没有彻底重启。JMeter的非GUI进程有时候在后台挂住你以为重启了实际还是旧进程在跑。强制结束进程再验证。4.2 分布式压测里的配置同步问题分布式模式下Master和Slave是两个独立的JVM进程。system.properties只对启动它的那个进程生效。你只在Master上配了字符集和证书Slave还是默认值跑起来照样乱码、照样握手失败。规模一大靠人肉同步基本不现实。我的做法是写一个简短的部署脚本把bin目录下的system.properties和jmeter.properties同步到所有Slave同步后立刻做哈希校验比如计算md5值比对。证书文件也要同步而且要注意路径Slave上的证书路径如果和Master不一样配置文件里写的路径就要动态调整。这类环境问题最怕的就是“时好时坏”本质就是节点间配置漂移。固定端口、统一证书、统一编码三个都做到一致分布式压测的环境问题能少掉一大半。4.3 千万别在这里配置JVM堆内存这是非常经典的误区。有人看到system.properties的名字觉得“系统配置都放这”于是把-Xmx4g、-Xms1g写进去结果发现完全没作用。原因很简单堆内存参数必须在JVM启动之前指定而properties文件是JVM跑起来之后才被读取的。想调堆内存去bin目录下的jmeter脚本里改HEAP变量或者设置环境变量JVM_ARGS。这是唯一正确的通道。顺手提醒一句调完堆内存后要用jstat或内存监控工具实际看一下堆使用率很多人堆配了4G压测时却只用到几百MB那这个配置大概率没起到预想作用。性能测试讲究用数据说话内存配置也一样。4.4 升级JMeter版本时的配置保留换JMeter版本时bin目录下新的默认system.properties可能会覆盖你之前手动改的配置。我的习惯是不直接改安装包里的默认文件而是维护一份自己的配置片段。升级之后拿新版本的默认文件和我维护的片段做对照合并再统一部署。更省事的做法是写个小脚本把配置片段自动合并到bin目录。这样升级后跑一遍部署脚本就恢复完整配置不用每次手工重新记一遍改过什么。这个习惯帮我避免过好几次“升级之后压测环境悄悄变坏”的问题值得保留。5. 几个我长期坚持的使用习惯做了这么多年压测环境维护我对system.properties最大的体会是它真正的价值不在于帮你把某个参数一次性调对而在于把环境差异从业务脚本里剥离出来。同样的测试计划在不同的压测机器上跑最怕的就是节点间环境不一致。有了system.properties再配合自动化部署机器之间的差异可以被压到很小。最后分享一个我认为最值得养成的习惯把system.properties纳入版本管理但不要把服务器专属密码或证书路径硬写死在文件里。敏感信息用环境变量占位在部署脚本里动态替换。这样既享受了统一配置的便利又不用每次换机器就从头手填一遍。配置维护永远是最琐碎的事情但把这些琐碎制度化之后省出来的时间远比你想象的要多。
阅读完成 · 觉得有帮助?