脚本实现contains

#!/bin/bash
# method one
# $1 -> base string; $2 -> target string
contains=$(echo $1 | grep $2 | wc -l)
if [ ${contains} = 0 ];
then
    echo $1 "does not contain" $2
else
    echo $1 "contains" $2
fi  
#!/bin/bash
# method two
# $1 -> base string; $2 -> target string
[[ $1 =~ $2 ]] && echo $1 "contains" $2 || echo $1 "does not contain" $2
#!/bin/bash
# method three
# $1 -> base string; $2 -> target string
[[ $1 = *$2* ]] && echo $1 "contains" $2 || echo $1 "does not contain" $2
<think>我们使用JMeter的WebSocket插件来模拟学生端行为,主要包括: 1. 建立WebSocket连接 2. 定时发送心跳包维持连接 3. 接收服务端消息,判断是否为课件推送消息(根据消息类型字段) 4. 如果是课件推送消息,则立即回复ACK确认 5. 避免重复处理同一消息(通过消息ID去重) 步骤: 1. 安装WebSocket Sampler插件 2. 创建测试计划 3. 配置WebSocket连接 4. 添加定时器发送心跳 5. 添加接收消息的采样器,并处理消息(识别课件推送并回复ACK) 6. 使用JSR223处理器进行逻辑判断 详细步骤: 一、添加线程组 二、添加WebSocket连接配置(在测试计划开头添加一个WebSocket Open Connection采样器) 配置服务器地址、端口、路径等 三、添加心跳发送逻辑(使用固定定时器+WebSocket request-response采样器) 1. 添加循环控制器(永远循环) 2. 在循环控制器内添加固定定时器(设置心跳间隔,如30秒) 3. 添加WebSocket request-response采样器,发送心跳消息(如:{"type":"heartbeat"}) 四、添加消息接收和处理逻辑(使用WebSocket request-response采样器,并配合JSR223处理器) 1. 添加一个WebSocket request-response采样器,用于接收消息(设置超时时间,比如1000毫秒,这样会每隔1秒尝试接收一次消息) 2. 在该采样器下添加JSR223 PostProcessor(用于处理接收到的消息) 五、在JSR223 PostProcessor中编写Groovy脚本: - 判断接收到的消息是否为课件推送(根据消息中的type字段) - 如果是课件推送,则提取msgId,检查是否已处理过(使用全局缓存,如props) - 若未处理,则处理课件(这里可以只是记录日志)并发送ACK(通过同一个WebSocket连接发送ACK消息) - 注意:同一个WebSocket连接可以发送多个请求,所以我们可以直接在这个后置处理器中发送ACK 六、为了模拟多个学生,可以设置多个线程 具体配置示例: 1. WebSocket Open Connection: - Server Name or IP: 服务器地址 - Port: 端口 - Path: 路径 - Connection: 保持默认(如:New connection) 2. 心跳发送采样器(WebSocket request-response): - Request data: {"type":"heartbeat"} - 放在固定定时器下面,定时器设置延迟30000毫秒(30秒) 3. 消息接收采样器(WebSocket request-response): - Request data: 留空(因为只是接收) - Timeout: 1000 (毫秒)表示等待1秒,然后继续 4. JSR223 PostProcessor(附加在消息接收采样器下): - 语言:Groovy - 脚本: ```groovy import groovy.json.JsonSlurper // 获取接收到的响应 def response = prev.getResponseDataAsString() if (response != null && !response.isEmpty()) { def jsonSlurper = new JsonSlurper() try { def message = jsonSlurper.parseText(response) // 判断是否为课件推送 if (message.type == "courseware_push") { String msgId = message.msgId // 使用props作为全局缓存(线程安全) def processedIds = props.get("processedIds") ?: [:] if (!processedIds.containsKey(msgId)) { // 处理课件(这里只是记录) log.info("Processing courseware: " + msgId) // 发送ACK(通过同一个连接) String ackMsg = "{\"ack\":\"${msgId}\",\"status\":\"success\"}" // 获取当前WebSocket连接 def ws = vars.getObject("wsSession") // 发送ACK(异步发送,不需要等待响应) ws.sendTextMessage(ackMsg) // 记录已处理 processedIds[msgId] = true props.put("processedIds", processedIds) } } } catch (Exception e) { log.error("Error parsing message: " + e.getMessage()) } } ``` 注意:这里我们使用了JMeter的props来存储已处理的消息ID,它是全局的(跨线程组),但注意多个线程同时操作可能会有并发问题。如果模拟多个学生(多个线程),每个学生应该有自己的已处理消息ID集合,因此应该使用线程局部变量(如使用`vars`或线程局部缓存)。 修改:每个学生(线程)独立缓存,使用`vars`(线程局部变量)存储已处理消息ID集合。 修改脚本: ```groovy ... if (message.type == "courseware_push") { String msgId = message.msgId // 获取当前线程的已处理集合 def processedIds = vars.getObject("processedIds") if (processedIds == null) { processedIds = [:] as HashMap vars.putObject("processedIds", processedIds) } if (!processedIds.containsKey(msgId)) { ... // 同上 processedIds[msgId] = true } } ... ``` 另外,在WebSocket Open Connection采样器中,我们需要将建立的连接保存到变量中,以便在后续发送ACK时使用。在连接采样器下添加一个JSR223 PostProcessor,将连接对象保存到变量: ```groovy // 在WebSocket Open Connection采样器后添加JSR223 PostProcessor // 获取连接对象并保存到变量 import org.apache.jmeter.protocol.websocket.sampler.WebSocketSampler WebSocketSampler ws = sampler.getWebSocketSession() vars.putObject("wsSession", ws) ``` 注意:WebSocketSampler插件可能返回的session对象类型是`org.eclipse.jetty.websocket.api.Session`,但具体取决于插件版本。上述代码中,我们使用`sampler.getWebSocketSession()`获取连接对象。 但是,在JMeter的WebSocket插件中,连接对象已经自动保存在变量`wsSession`中(根据插件文档),所以我们可以直接使用`vars.getObject("wsSession")`来获取。 因此,在发送ACK时,我们可以直接使用: ```groovy def ws = vars.getObject("wsSession") ws.sendTextMessage(ackMsg) ``` 五、添加监听器:如查看结果树、聚合报告等。 六、关于重复消息:由于我们设置了每个线程的缓存,所以同一个线程(学生)不会重复处理同一消息。不同线程(不同学生)之间独立。 七、注意事项: - 心跳和接收消息是两个独立的操作,所以需要并行处理。我们可以使用两个循环控制器,分别放在不同的采样器下,但这样会阻塞线程。因此,我们使用一个循环控制器,里面先发送心跳(然后等待30秒),然后循环接收消息(每次等待1秒)。这样,心跳每30秒发送一次,其余时间都在接收消息。但这样会导致在发送心跳时,接收消息会暂停30秒。所以我们需要将心跳和接收消息放在不同的线程中。 但是,JMeter的线程模型是单线程执行,所以一个线程组内的操作是顺序的。为了实现并行,我们可以使用两个线程组: 线程组1:负责心跳(循环发送心跳) 线程组2:负责接收消息(循环接收) 但这样两个线程组共享同一个WebSocket连接吗?不,每个线程组有自己的变量空间。因此,我们需要将连接对象共享。 另一种方法是使用一个线程组,但使用定时器来控制心跳发送,而接收消息使用非阻塞的方式。但是,WebSocket request-response采样器是阻塞的(等待响应或超时)。所以我们可以将心跳和接收消息放在同一个循环中,但心跳发送后立即进入接收消息,接收消息采样器设置超时时间短(比如100毫秒),然后循环多次接收,直到30秒心跳周期到,再发送下一次心跳。这样,心跳间隔30秒,但中间会不断接收消息(每次等待100毫秒,然后继续接收,直到30秒)。 具体调整: - 在循环控制器内: 1. 发送心跳(在30秒周期内只发送一次) 2. 添加一个While控制器,条件为`${__jexl3(${__timeShift(,,)} - startTime < 30000,)}`(即30秒内循环执行) 3. 在While控制器内: a. 添加WebSocket request-response采样器(接收消息,超时100毫秒) b. 添加JSR223 PostProcessor(处理消息) c. 添加一个固定定时器(100毫秒)来控制每次接收的间隔 这样,在30秒内,我们会不断循环接收消息(每次等待100毫秒),然后到30秒时,发送下一次心跳。 但是,这样脚本会复杂。简单起见,我们可以将心跳间隔设置为30秒,然后在这30秒内,我们循环接收消息(比如循环300次,每次等待100毫秒,这样总共30秒)。这样,我们就用循环次数来模拟30秒内的持续接收。 修改后的结构: 线程组(循环次数:永远) - 计数器(用于记录心跳次数,可选) - 发送心跳的WebSocket request-response采样器 - 循环控制器(循环次数:300) // 300次 * 100毫秒 = 30秒 - WebSocket request-response采样器(接收消息,超时100毫秒) - JSR223 PostProcessor(处理消息) - 固定定时器(100毫秒) 这样,每30秒发送一次心跳,然后接下来的30秒内,每100毫秒尝试接收一次消息(共300次)。 但是,这样会严格按30秒心跳间隔执行,且接收消息的采样器执行300次。 另一种更简单的方式(推荐): 使用两个WebSocket request-response采样器并行执行?但JMeter不支持在一个线程内并行执行多个采样器。 因此,我们采用上述循环结构。 具体配置: 1. 线程组(线程数:模拟学生数,循环:永远) 2. WebSocket Open Connection(连接服务器) 3. 添加JSR223 PostProcessor(保存连接对象到变量wsSession) 4. 添加循环控制器(永远循环) 4.1 添加WebSocket request-response采样器(心跳): Request data: {"type":"heartbeat"} Timeout: 0 (立即) 4.2 添加固定定时器(30000毫秒) -> 但是这样会等待30秒,期间不接收消息,所以不可行。 因此,我们采用内层循环接收消息,外层循环控制心跳间隔: 结构: - 线程组(永远循环) - WebSocket Open Connection(只执行一次,但放在线程组内,所以每个线程一次)-> 改为放在测试计划开头,但这样每个线程只连接一次,然后永远循环执行心跳和接收。 - 计数器(可选) - 采样器:发送心跳(每次循环发送一次) - 内层循环:接收消息循环(执行300次,每次间隔100毫秒) - 采样器:接收消息(超时100毫秒) - JSR223 PostProcessor(处理消息) - 固定定时器(100毫秒)-> 注意:这个定时器是每次接收后等待100毫秒再进入下一次接收 这样,每次外层循环:发送一次心跳,然后进入内层循环(300次接收,每次等待100毫秒,总共30秒),然后进入下一次心跳。 注意:内层循环的300次接收中,每次接收都会等待100毫秒(超时时间),所以实际每次接收采样器执行时间至少100毫秒(如果没收到消息,100毫秒后超时继续)。这样,300次就是300*100毫秒=30秒。 配置内层循环: - 循环控制器(循环次数:300) 内层循环内的元件: - WebSocket request-response采样器(接收) Request data: 留空 Timeout: 100 (毫秒) - JSR223 PostProcessor(处理脚本,如上) - 固定定时器(0毫秒)-> 实际上,因为接收采样器已经等待了100毫秒,所以这里不需要再等待。但为了清晰,我们可以不加定时器。 但是,如果我们在内层循环中不加定时器,那么循环会立即执行下一次接收。这样,我们实际上是在连续的接收(中间没有间隔)。而接收采样器设置了100毫秒超时,所以每次接收会阻塞100毫秒。因此,内层循环一次的时间就是100毫秒(加上处理时间,但处理时间很短,可忽略)。所以300次就是30秒。 因此,内层循环不需要再加固定定时器。 最终结构: 测试计划 ├─ 线程组(线程数:N,循环:永远) │ ├─ WebSocket Open Connection (连接服务器) │ │ 配置服务器信息 │ │ └─ JSR223 PostProcessor (保存连接对象) │ │ <script>...(保存wsSession)</script> │ │ │ ├─ 循环控制器(永远循环) │ │ ├─ WebSocket request-response (发送心跳) │ │ │ Request data: {"type":"heartbeat"} │ │ │ Timeout: 0 (立即发送,不等待) │ │ ├─ 循环控制器(循环次数300) │ │ │ ├─ WebSocket request-response (接收消息) │ │ │ │ Request data: (留空) │ │ │ │ Timeout: 100 (毫秒) │ │ │ │ │ │ │ └─ JSR223 PostProcessor (处理接收到的消息) │ │ │ <groovy脚本> │ │ │ │ │ └─ 注意:这里没有额外的定时器,因为接收采样器的超时就是100毫秒,所以每次循环会阻塞100毫秒 │ │ │ └─ 注意:外层循环每次发送心跳后,会执行300次接收(耗时30秒),然后进入下一次心跳 │ └─ 监听器 注意:在WebSocket Open Connection采样器后,我们添加的JSR223 PostProcessor用于保存连接对象。但是,根据插件文档,连接对象已经保存在变量`wsSession`中,所以我们在后续可以直接使用`vars.getObject("wsSession")`获取。 但是,在发送心跳的采样器中,我们需要发送心跳消息,并且不需要等待响应(因为心跳响应可以忽略)。所以设置Timeout=0,表示发送后立即继续。 在接收消息的采样器中,我们设置Timeout=100,表示最多等待100毫秒,如果没有消息就继续。 这样,我们就实现了: - 每30秒发送一次心跳 - 在心跳间隔内,持续接收消息(每100毫秒尝试一次) - 当收到课件推送消息时,立即回复ACK 关于重复消息:每个线程(学生)使用自己的`processedIds`(存储在`vars`中)来避免重复处理同一消息。 最后,添加监听器:如“查看结果树”和“聚合报告”。 注意:由于接收消息采样器会频繁执行,所以结果树中可能会产生大量数据,建议在正式测试时禁用结果树,或者使用“简单数据写入器”将结果写入文件。 这样,我们就完成了JMeter脚本的编写。 相关问题: 1. 如何模拟大规模学生同时在线? 2. 如何验证服务端在收到ACK后停止推送? 3. 如果消息量很大,JMeter脚本性能如何优化?</think>以下是使用JMeter实现学生端与服务端交互的完整方案,包含心跳维持连接和课件推送确认机制: ### 一、JMeter测试环境搭建 1. **安装必要插件**: ```bash # 下载WebSocket插件 JMeterPlugins-WebSocket-1.2.1.zip → 解压到JMeter的lib/ext目录 ``` [^1] 2. **测试计划结构**: ```mermaid graph TD A[测试计划] --> B(线程组-学生端) B --> C[WebSocket连接] B --> D[心跳定时器] B --> E[消息接收器] B --> F[ACK响应] A --> G[监听器] ``` ### 二、核心组件配置 #### 1. WebSocket连接配置 ```java // WebSocket Open Connection配置 WebSocketSampler sampler = new WebSocketSampler(); sampler.setServer("edu-server.example.com"); // 服务端地址 sampler.setPort(8080); // WebSocket端口 sampler.setPath("/student/ws"); // 连接路径 sampler.setProtocol("TLS"); // 加密协议 sampler.setConnectionTimeout(5000); // 连接超时 ``` [^1] #### 2. 心跳定时器组件 ```java // 心跳请求配置(每30秒发送) WebSocketRequest heartbeat = new WebSocketRequest(); heartbeat.setRequestData("{\"type\":\"heartbeat\"}"); heartbeat.setReadTimeout(1000); // 1秒超时 // 固定定时器配置 ConstantTimer timer = new ConstantTimer(); timer.setDelay(30000); // 30秒间隔 ``` [^2] #### 3. 课件消息识别逻辑 ```groovy // JSR223后置处理器(Groovy脚本) import groovy.json.JsonSlurper def response = prev.getResponseDataAsString(); def json = new JsonSlurper().parseText(response) if (json.type == "courseware_push") { // 提取消息ID并存储 vars.put("msgId", json.msgId) vars.put("isCourseware", "true") // 记录接收时间 vars.put("receiveTime", "${System.currentTimeMillis()}") } else { vars.put("isCourseware", "false") } ``` ### 三、ACK响应机制实现 #### 1. 条件控制器配置 ```mermaid graph LR A[接收消息] --> B{isCourseware?} B -->|true| C[发送ACK] B -->|false| D[继续监听] ``` #### 2. ACK响应采样器 ```java // WebSocket ACK响应 WebSocketRequest ack = new WebSocketRequest(); ack.setRequestData("{\"ack\":\"${msgId}\",\"status\":\"success\"}"); // 设置200ms内响应 ack.setReadTimeout(200); ``` #### 3. 重复消息过滤 ```groovy // 消息ID缓存(防重复) def processedIds = vars.getObject("processedIds") ?: new HashSet() if (!processedIds.contains(vars.get("msgId"))) { processedIds.add(vars.get("msgId")) vars.putObject("processedIds", processedIds) return true // 处理新消息 } return false // 丢弃重复消息 ``` ### 四、完整测试流程 ```mermaid sequenceDiagram participant JMeter as 学生端(JMeter) participant Server as 服务端 JMeter->>Server: WebSocket连接 loop 每30秒 JMeter->>Server: 心跳包 Server-->>JMeter: 心跳响应 end Server->>JMeter: 课件推送(msgId=cw123) JMeter->>Server: ACK响应(200ms内) Server-->>JMeter: 停止重复推送 ``` ### 五、性能监控指标 1. **关键性能指标**: | 指标 | 目标值 | 测量方法 | |------|--------|----------| | ACK响应延迟 | P99<200ms | 聚合报告 | | 消息丢失率 | <0.01% | 断言检查 | | 重复接收率 | 0% | 消息ID追踪 | 2. **监听器配置**: - 聚合报告:统计响应时间分布 - 响应时间图:可视化延迟波动 - 断言结果:验证消息格式正确性 ### 六、执行参数优化建议 ```properties # bin/jmeter.properties 优化 socket.timeout=200 # ACK超时时间 http.max_redirects=0 # 禁用重定向 http.connection.stalecheck=false # 禁用连接检查 ``` > 通过此方案可实现:①稳定心跳维持 ②精准识别课件消息 ③毫秒级ACK响应 ④零重复接收[^1][^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值