资讯详情

资讯详情

JMeter参数化实战:随机数与随机字符串生成、避坑与选型

1. 为什么参数化里的随机数环节最容易被低估做性能测试的人几乎都绕不开 Jmeter而只要涉及接口压测参数化就是绕不过去的一道坎。很多人第一次接触 Jmeter 参数化脑子里蹦出来的都是 CSV 文件、数据库取值这些规规矩矩的方案反而把随机数、随机字符串这类看似简单的东西随手一带而过。结果到了真实压测现场问题往往就出在这里注册接口用同一个手机号跑了两千次服务端全给你返回该用户已存在订单号用固定值提交数据库唯一索引直接把一半请求拦在门外上传接口的文件名不变缓存命中率高得离谱压出来的数据完全失真。我自己早期带团队做压测的时候就吃过这个亏。当时测一个优惠券领取接口参数就是用户 ID 加券码券码写死成一个固定字符串跑出来的 TPS 高得吓人我还挺得意结果一看日志服务端因为幂等校验把重复请求全短路了真正打到核心逻辑的没几个。后来把券码改成随机字符串TPS 直接掉了一大截——这才是真实的性能水位。从那以后我对参数化里的随机数、随机字符串就格外上心它不是什么高深技术但它是决定压测结果像不像真的的关键一环。这篇东西就是把我这些年用 Jmeter 做随机参数化的经验整理出来。核心关键词就三个Jmeter、参数化、随机数、随机字符串。我会讲清楚 Jmeter 里生成随机数的几种主流方式怎么选、函数助手怎么配、JSR223 脚本怎么写、随机值怎么塞进请求体和数据库参数里以及踩过的那些坑。适合谁看刚上手 Jmeter 想搞明白参数化怎么玩的新人以及做了一段时间但总觉得随机参数用得不顺手的老手。我不会只丢给你一段脚本而是把为什么这么做也讲透让你看完能直接抄作业也能自己改。先说一个我反复跟团队强调的观点随机参数化的目的从来不是让数据看起来随机而是让压测流量尽可能逼近真实生产流量。真实用户的手机号是随机的、订单号是唯一的、请求体里的字符串五花八门你的压测脚本如果做不到这一点测出来的数字就是自欺欺人。理解了这层后面的所有操作和取舍才有判断依据。下面我按选型逻辑—函数助手实操—脚本进阶—坑位排查的顺序展开每一步都附上我自己在项目里的真实操作记录。2. Jmeter 生成随机数的几种路子与选型逻辑2.1 函数助手最省事的入门方案Jmeter 自带的函数助手Function Helper是绝大多数人接触随机参数的第一个入口。打开方式很简单菜单栏 Tools → Function Helper Dialog或者直接在需要参数化的输入框旁边点开函数助手。里面跟随机数、随机字符串相关的核心函数就两个__Random和__RandomString。__Random的语法是${__Random(最小值,最大值,变量名)}比如${__Random(10000000,99999999,phoneSuffix)}就会在 8 位数字范围内生成一个随机整数并存到phoneSuffix这个变量里供后续引用。__RandomString的语法是${__RandomString(长度,字符集,变量名)}比如${__RandomString(10,abcdefghijklmnopqrstuvwxyz0123456789,orderCode)}会生成一个 10 位的随机字符串。它的优势是零代码、上手快、当天就能用对刚入门的人极其友好。但你要清楚它的边界在哪__Random生成的是整数区间内的随机值__RandomString是从你给定的字符集里随机抽字符拼串。它能满足值不重复、形态随机的基本需求但如果你要做有业务规则的随机数据——比如手机号必须以 13/15/18 开头、身份证号要符合校验位算法、金额要保留两位小数——函数助手就有点力不从心了。我的经验是函数助手适合做纯随机填充的场景比如随机订单号后缀、随机备注字段、随机文件名一旦涉及业务校验规则就换脚本方案。这个分界线是我用了大量时间试错才划清的新手照着用能少走不少弯路。2.2 BeanShell 与 JSR223让随机数带上业务逻辑当随机值需要符合业务规则时函数助手就不够了得靠脚本处理器。Jmeter 里常见的有 BeanShell Sampler、BeanShell PreProcessor以及更现代的 JSR223 Sampler、JSR223 PreProcessor。这里要划个重点能选 JSR223 就不要选 BeanShell。原因很实在。BeanShell 是一种解释型脚本每次执行都要现场解析性能开销大。在单线程小并发下你感觉不到差别但当你模拟几百上千并发的时候BeanShell 本身的执行耗时会混进你的测试结果里把真实的接口响应时间污染掉。JSR223 支持 Groovy、JavaScript 等多种语言其中 Groovy 是编译执行的并且 Jmeter 会缓存已编译的脚本重复执行时几乎没有解析开销。我做过一次对比同样的随机数生成逻辑JSR223Groovy 比 BeanShell 在 500 并发下整体响应时间少了将近 15%这个差距在性能测试里是致命的——你以为在测服务端结果测的是脚本引擎。所以选型逻辑就这么定需要业务规则就用脚本脚本首选 JSR223 Groovy。BeanShell 能看懂、能维护老脚本就行新写的脚本一律 JSR223。这个判断标准我在团队里推行了两年几乎没出过错。2.3 三种方案的横向对比为了让你一眼看清怎么选我把三种方式整理成一张对照表方案适用场景优点局限推荐指数__Random函数纯整数随机填充零代码、配置极快无业务规则、仅限整数简单场景首选__RandomString函数纯随机字符串填充字符集可控、上手快无格式约束、随机性一般简单场景首选JSR223Groovy带业务规则的随机数据灵活、性能好、可复用需要写代码、有学习成本复杂场景首选BeanShell维护历史脚本语法直观、上手易性能差、已不推荐仅维旧用这张表是我自己踩坑总结的不是什么官方文档。你在实际项目里可以照这个逻辑走先用最简单的函数助手快速跑通遇到规则约束再升级到 JSR223基本不会走错路。3. 从零实操随机参数化的完整落地过程3.1 先把数据规范想清楚别急着开工具很多人一上来就打开 Jmeter 猛写脚本结果写到一半发现字段长度、字符集、格式都没定来回返工。我现在的习惯是动手前先列一张数据规范表。拿注册接口举例你至少要明确几个东西手机号的位数和号段前缀、用户名的长度范围和允许字符、密码的复杂度要求、邮箱的格式、订单号的唯一性要求。这些不是拍脑袋定的而是从接口文档和数据库字段定义里抄出来的。注意数据规范一定要跟接口文档、数据库约束对齐否则随机数据再随机也会被服务端校验拦掉。我曾经因为忽略了数据库里手机号字段的 unique 约束随机生成了几万条数据结果一半重复白白浪费了一轮压测。把规范定下来之后再决定每个字段用函数还是用脚本。我的经验是一张表里通常 70% 的字段用函数助手就够了只有那几个有强业务规则的字段才需要脚本。这样既保证了质量又不至于把自己累死。3.2 函数助手配置的现场记录以最常见的注册压测为例我实际操作是这样的。先打开函数助手在 Function 下拉框里选__RandomString然后在参数框依次填串长度填 8字符集填abcdefghijklmnopqrstuvwxyz0123456789变量名我习惯叫userName。点击 Generate Copy to Clipboard它会生成类似${__RandomString(8,abcdefghijklmnopqrstuvwxyz0123456789,userName)}的表达式。把它粘到 HTTP Request 的用户名参数值里就行。手机号我是这么处理的手机号一般是 11 位前 3 位固定号段后 8 位随机。所以我用两段拼起来——前 3 位写死成138后面接${__Random(10000000,99999999,phoneSuffix)}。最终表达式长这样138${__Random(10000000,99999999,phoneSuffix)}。这样既保证了号段合规又保证了足够大的随机空间。这里有个特别容易忽略的细节函数取值时机。Jmeter 里的函数默认在每次执行到它所在的元件时求值但如果你在测试计划里勾选了某些作用域设置或者函数被放在配置元件里它的求值时机就不一样了。我踩过最典型的坑是把随机函数放在 CSV Data Set Config 的同一层级结果它只求了一次值所有线程用的是同一个随机数。解决办法是把函数直接写在请求参数里让它在每次请求时重新求值。这个点新手几乎必踩记牢它。3.3 JSR223 脚本生成带规则的随机数据现在讲脚本方案。在需要参数化的 Sampler 上右键添加 → 前置处理器 → JSR223 PreProcessorLanguage 选 Groovy然后写脚本。下面这段是我做订单号生成时常用的模板你可以直接抄import java.text.SimpleDateFormat // 生成带时间戳的订单号日期 6位随机数字 def sdf new SimpleDateFormat(yyyyMMddHHmmss) def datePart sdf.format(new Date()) def random new Random() def numPart String.format(%06d, random.nextInt(1000000)) def orderNo ORD datePart numPart vars.put(orderNo, orderNo) log.info(本次生成的订单号 orderNo)这段脚本有几个设计考虑值得说。第一用时间戳保证前缀单调递增避免大量请求在极短时间内撞号第二6 位随机数字提供 100 万的空间配合时间戳基本杜绝重复第三vars.put(orderNo, orderNo)是把值塞进 Jmeter 变量后续用${orderNo}就能引用。vars是 Jmeter 提供的一个线程级变量容器每个线程有自己独立的一份天然做到了线程隔离——这是脚本方案比函数助手更可控的地方。生成随机字符串时我有时需要指定字符集和长度脚本是这样def chars abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 def random new Random() def sb new StringBuilder() def length 12 for (int i 0; i length; i) { sb.append(chars.charAt(random.nextInt(chars.length()))) } vars.put(randomStr, sb.toString())注意事项Groovy 脚本里不要用Math.random()这种老写法去截取容易踩精度和格式的坑用Random或ThreadLocalRandom更稳。另外脚本里少做复杂运算能在预处理阶段算完的就别放到请求线程里避免污染响应时间统计。3.4 把随机值塞进请求体和数据库参数生成出来只是第一步关键是让它真正生效。对于 HTTP 请求在 Body Data 或 Parameters 里用${变量名}引用即可。如果请求体是 JSON那就写成{userName:${userName},phone:${phone}}这样的形式Jmeter 会在发送前完成变量替换。数据库参数化稍微绕一点。用 JDBC Request 时如果参数要动态传入常见做法是把随机值先通过 JSR223 存进vars然后在 JDBC Request 里用${变量名}引用参数类型选对应的类型。另一种方式是把随机值写进 CSV再用 CSV Data Set Config 读取适合数据量特别大的场景。这里有个坑JDBC 参数如果是字符串类型千万注意引号和类型匹配字符串参数在某些数据库驱动下需要显式加单引号数字参数则不能加否则会报类型错误。我在测 MySQL 时就因为把随机数字当成字符串传报过Data truncation的错误排查了半天才发现是类型问题。4. 常见坑与排查技巧实录4.1 随机数重复、分布不均怎么办随机数重复是压测里最让人头疼的问题之一。表面上看随机数空间很大但如果你用的范围太小比如只生成 1 到 100 的随机数跑一万次那碰撞概率高得吓人。判断方法很简单把生成的随机值写到日志或结果文件里用脚本统计一下重复率。解决思路分两层。第一层是扩大随机空间手机号用完整 8 位而不是 3 位订单号加上时间戳和随机位。第二层是如果业务要求绝对唯一那就别纯靠随机改用时间戳线程号随机数的组合模式或者干脆用AtomicLong做自增计数配合随机前缀。我在一次高并发压测里就用了组合方案单个线程内自增保证不重复跨线程用随机前缀区分最终唯一性达到 100%。4.2 函数取值时机与线程共享的坑这个坑前面提过一句这里展开说透。Jmeter 的函数在测试计划启动时、元件执行时、还是每次请求时求值取决于它被放在哪里、以及是否配置了缓存。默认情况下函数是按需求值放对位置就没问题。但如果你不小心把随机函数放进了配置元件比如 User Defined Variables那它只会在初始化时求值一次所有线程共享同一个值——压测结果全废。排查方法在函数表达式后面加个log.info打印或者直接在察看结果树里看不同线程发出去的参数值是否相同。如果全一样百分之百是求值时机问题。避坑技巧随机参数一律直接写在 Sampler 的参数里不要放配置元件如果确实需要共享就明确接受共享的后果别混着用。4.3 随机字符串乱码与长度问题速查随机字符串最常出的两个问题一个是乱码一个是长度不对。乱码通常是因为字符集里混入了中文或特殊符号而接口或数据库的编码是 UTF-8传输时对不上就会出现问号或者奇怪字符。解决方式是随机字符串的字符集尽量限定在 ASCII 可见字符范围内实在需要中文就用明确的 Unicode 转义。长度不对多半是拼接逻辑有问题。比如长度参数写错了位或者循环次数和长度变量搞混了。我整理了一张速查表问题现象可能原因排查方法解决方式随机值全部相同函数放在配置元件里看结果树不同线程参数移到 Sampler 参数中随机数重复率高随机范围太小统计结果文件重复率扩大范围或用组合方案字符串乱码字符集含非 ASCII检查字符集定义限定可见字符范围长度不符拼接或循环逻辑错打印中间变量修正循环次数和长度JDBC 参数报错类型与引号不匹配看数据库错误日志按类型调整引号提示排查随机参数问题时第一步永远是看实际发出去的值而不是猜。察看结果树里的请求体或者加个 debug 日志比任何推断都快。5. 进阶玩法与性能取舍5.1 大数据量与 CSV 参数化的配合当随机数据的量级特别大比如要模拟百万级用户纯靠脚本实时生成可能有性能压力。这时候一个折中方案是预生成随机读取提前用脚本生成一个十万条随机数据的 CSV 文件在压测时用 CSV Data Set Config 随机抽取。Jmeter 的 CSV 读取默认是按行顺序取多线程时会做分块每个线程读一段这样既保证了数据多样又避免了实时计算的成本。具体操作CSV Data Set Config 里 Filename 指向文件Variable Names 填列名Sharing Mode 根据需求选。如果要每个线程用不同的数据块就选 Current thread group如果所有线程共享读取游标就选 All threads。这个选择直接影响数据利用率选错会导致某些线程的数据序列和其他线程高度重合降低随机性。5.2 随机参数对测试报告解读的影响最后说一个容易被忽视的点随机参数会改变你的报告解读逻辑。用固定参数压测服务端可能因为缓存、幂等、连接复用等原因给出偏高的性能表现换成随机参数后请求要真正走一遍完整逻辑TPS 通常会更低但更接近生产真实情况。所以你在对比历史报告时必须先确认参数化方案是否一致否则数字没有可比性。我的经验是正式压测前先跑一轮小并发比如 20 线程的随机参数冒烟确认数据能正常通过服务端校验再逐步加压。这个习惯帮我省下了无数次跑到一半发现数据全被拦的返工。另外随机参数会让响应时间的分布更离散看报告时多关注 90%、95% 分位值别只盯着平均值你会发现随机参数下的长尾请求比固定参数明显这才是真实用户会遇到的体验。说到最后分享一个我自己养成的习惯每次新建压测脚本先花五分钟把参数规范写成注释贴在测试计划顶部再动手配置。看起来多此一举但真正做到之后脚本的可维护性和可复现性会提升一大截。随机参数化这件事难点从来不在工具本身而在于你有没有把模拟真实流量这个目标真正放进心里。工具只是手段思路才是关键。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →