面试题
1.一:阅读下面代码,并尽可能找出存在的所有bug,并修复。
def find_max(nums): max = 0 for num in nums: if num > max: max = num return max
def find_max(nums): if not nums: # 处理空列表情况 return None maximum = nums[0] # 将初始值设为列表的第一个元素 for num in nums: if num > maximum: maximum = num return maximum # 修正缩进,确保在函数体内
二:实现一个小工具(Java或者Python),从一个日志文件中统计给定的关键字的出现次数(时间精确到秒,有可能同一时间关键字会出现多次),并按照时间倒序输出。
1、文件内容格式:"YYYY-MM-DD HH:MM:SS <LOG_LEVEL> <MESSAGE>"。
2、举例,例如文件内容为:
2021-12-14 12:05:00 LV5 ERROR
2021-12-14 12:05:00 LV5 ERROR
2021-12-14 12:05:01 LV1 Info
2021-12-14 12:05:02 LV1 Info
2021-12-14 12:05:03 LV1 Info
2021-12-14 12:05:05 LV5 ERROR
3、输出如下:
2021-12-14 12:05:05 ERROR 1
2021-12-14 12:05:00 ERROR 2
import sys from collections import defaultdict #from typing import Dict, DefaultDict, List, Tuple, Optional def count_keywords(log_file: str, level: str) -> defaultdict[str, defaultdict[str, int]]: """ 统计日志文件中每个时间戳下特定级别的出现次数 参数: log_file: 日志文件路径 level: 需要统计的日志级别 返回: 嵌套字典,格式为 {时间戳: {关键字: 次数}} """ time_keyword_counts: defaultdict[str, defaultdict[str, int]] = defaultdict(lambda: defaultdict(int)) with open(log_file, 'r') as f: for line in f: parts = line.strip().split() if len(parts) < 4: continue # 跳过格式不正确的行 timestamp = ''.join(parts[:2]) keyword = parts[3] # 假设关键字是第4列 # 对应时间戳的关键字计数加1 time_keyword_counts[timestamp][keyword] += 1 return time_keyword_counts def main() -> None: log_file: str = 'F:/Users/Administrator/PycharmProjects/PythonProject/your_log_file.txt' level: str = 'Info' # 统计所有关键字出现次数 counts: defaultdict[str, defaultdict[str, int]] = count_keywords(log_file, level) # 按时间倒序输出结果 for timestamp in sorted(counts.keys(), reverse=True): for keyword, count in counts[timestamp].items(): if keyword == level: print(f"{timestamp} {keyword} {count}") if __name__ == "__main__": main()
三:请试着回答以下问题:
1.解释吞吐量、tps、rps。
在计算机系统、性能测试和网络领域中,**吞吐量(Throughput)、TPS(Transactions Per Second)、RPS(Requests Per Second)** 是常用的性能指标,用于衡量系统的处理能力和效率。以下是它们的详细解释: ### **一、吞吐量(Throughput)** **定义**: 吞吐量是指系统在**单位时间内处理的任务或数据量**,通常用于衡量系统的整体处理能力。它可以表示为: - 单位时间内处理的请求数、任务数、数据量(如字节、数据包等)。 - 例如:Web 服务器每秒处理的 HTTP 请求数、数据库每秒执行的事务数、网络链路每秒传输的数据量(Mbps)。 **特点**: 1. **范围广泛**:吞吐量是一个笼统的指标,需结合具体场景定义单位(如请求数、数据量等)。 2. **受资源限制**:受 CPU、内存、磁盘 I/O、网络带宽等硬件资源或软件瓶颈的影响。 3. **动态变化**:系统负载不同时,吞吐量可能波动(如峰值流量下吞吐量可能下降)。 **应用场景**: - 衡量网络带宽:如“某链路吞吐量为 100 Mbps”。 - 评估服务器性能:如“某 API 服务的最大吞吐量为 5000 请求/秒”。 ### **二、TPS(Transactions Per Second,每秒事务数)** **定义**: TPS 是指系统在**每秒内完成的事务数**。这里的“事务”是一个**逻辑概念**,通常指一组相关操作的集合(如数据库事务、用户业务操作等)。 - 例如: - 数据库事务:一条 SQL 插入语句或包含多个步骤的事务(如转账操作:扣减余额 + 增加对方余额)。 - 业务事务:用户下单(包含查询库存、生成订单、扣减库存等多个子操作)。 **特点**: 1. **业务相关性**:依赖于具体业务逻辑,不同事务的复杂度可能差异很大(如简单查询 vs. 复杂事务)。 2. **原子性**:事务通常要求“要么全部成功,要么全部失败”,因此 TPS 仅统计**成功完成的事务数**。 3. **与响应时间关联**:高 TPS 可能伴随较长的响应时间(因系统负载高),需平衡两者。 **应用场景**: - 数据库性能测试:如“MySQL 集群的 TPS 峰值为 2000”。 - 业务系统压测:如“电商平台每秒完成的订单事务数”。 ### **三、RPS(Requests Per Second,每秒请求数)** **定义**: RPS 是指系统在**每秒内处理的请求数**,这里的“请求”通常指**单次独立的操作**,如客户端向服务器发送的一个 HTTP 请求、一次 API 调用等。 - 例如: - 浏览器向服务器发送一个 GET 请求获取网页。 - 客户端调用一个微服务接口获取数据。 **特点**: 1. **细粒度**:RPS 衡量单个请求的处理效率,不涉及事务的“原子性”或“完整性”,仅统计请求的处理次数(成功或失败)。 2. **无状态性**:每个请求通常是独立的,不依赖其他请求的上下文(如 RESTful API 请求)。 3. **容易统计**:在性能测试工具(如 JMeter、Postman)中可直接监控 RPS。 **应用场景**: - Web 服务性能测试:如“某 API 的 RPS 极限为 10,000”。 - 接口压测:评估单个接口在高并发下的处理能力。 ### **四、三者的区别与联系** | **指标** | **单位** | **粒度** | **是否关注业务逻辑** | **统计范围** | |----------------|-----------------------|----------------|----------------------|----------------------------| | **吞吐量** | 自定义(如请求数、字节数) | 宏观 | 否(通用指标) | 系统整体处理能力 | | **TPS** | 事务数/秒 | 中观(业务级) | 是(依赖事务定义) | 成功完成的事务数 | | **RPS** | 请求数/秒 | 微观(请求级) | 否(仅统计请求次数) | 处理的请求数(成功/失败) | **联系**: - TPS 和 RPS 是吞吐量的具体表现形式,吞吐量可通过 TPS 或 RPS 等指标量化。 - 一般来说,**RPS ≥ TPS**(一个事务可能包含多个请求)。例如,一个下单事务可能包含 3 次 API 请求(查询库存、生成订单、扣减库存),则 TPS=1 时,RPS=3。 ### **五、实际应用建议** 1. **选择指标的依据**: - 若关注**业务完整性**(如数据库事务),用 **TPS**。 - 若关注**接口并发能力**(如单个 API),用 **RPS**。 - 若需宏观评估系统性能(如数据传输效率),用 **吞吐量**。 2. **性能优化方向**: - 提升 TPS/RPS:优化代码逻辑、增加硬件资源(如扩容服务器)、引入缓存或异步处理。 - 降低响应时间:减少请求处理耗时(如优化 SQL、减少网络调用)。 3. **工具支持**: - 吞吐量监控:常用工具包括 Prometheus、Grafana、Wireshark。 - TPS/RPS 压测:JMeter、Apache Benchmark(AB)、LoadRunner。 通过合理运用这些指标,可以更精准地评估系统性能、定位瓶颈并优化架构。
2.tps上不去,可能的原因是什么?
TPS(Transactions Per Second)上不去通常是系统性能瓶颈的表现,可能涉及硬件资源、软件架构、数据库、网络或代码逻辑等多个层面。以下是常见的原因及排查方向: ### **一、硬件资源瓶颈** 1. **CPU 利用率过高** - **表现**:CPU 使用率长期超过 80%,导致线程等待 CPU 时间片,事务处理变慢。 - **可能原因**: - 复杂计算逻辑(如加密、压缩)。 - 死循环或无限递归。 - 线程数过多导致上下文切换频繁。 - **排查工具**:`top`、`htop`、`vmstat`、JVM 线程分析(如 VisualVM)。 2. **内存不足** - **表现**:频繁触发 GC(垃圾回收)或出现 OOM(内存溢出),系统卡顿。 - **可能原因**: - 内存泄漏(如缓存未释放、长生命周期对象持有引用)。 - 堆内存分配过小(如 JVM 参数 `-Xmx` 设置不合理)。 - 大量数据加载到内存(如全量查询数据库)。 - **排查工具**:`free -h`、`jstat`、内存分析工具(如 MAT)。 3. **磁盘 I/O 瓶颈** - **表现**:磁盘读写耗时高(如平均 I/O 等待时间超过 10ms)。 - **可能原因**: - 频繁的磁盘读写(如日志写入、文件上传)。 - 磁盘顺序读写变为随机读写(如索引失效导致全表扫描)。 - 机械硬盘性能不足(建议升级 SSD)。 - **排查工具**:`iostat`、`iotop`、`sar`。 4. **网络带宽不足** - **表现**:网络延迟高、丢包率高,跨节点调用耗时增加。 - **可能原因**: - 网络带宽被占满(如大量文件下载)。 - 网络拓扑不合理(如单点带宽限制)。 - 防火墙或代理服务器配置导致请求阻塞。 - **排查工具**:`iftop`、`netstat`、`ping`、`traceroute`。 ### **二、数据库性能问题** 1. **SQL 语句低效** - **表现**:单条 SQL 执行时间过长,导致事务阻塞。 - **可能原因**: - 缺少必要索引(如 WHERE 条件字段未建索引)。 - 全表扫描或全索引扫描。 - 复杂查询(如多表 JOIN 未优化)。 - **排查工具**:数据库慢查询日志、EXPLAIN 分析执行计划。 2. **数据库连接池配置不合理** - **表现**:连接池耗尽,新事务等待连接。 - **可能原因**: - 最大连接数设置过小。 - 连接泄漏(如未及时释放连接)。 - 长事务占用连接时间过长。 - **排查工具**:数据库连接监控、应用日志。 3. **事务设计不合理** - **表现**:事务持有锁时间过长,导致其他事务阻塞。 - **可能原因**: - 事务范围过大(如包含网络调用、文件操作)。 - 事务隔离级别过高(如 SERIALIZABLE)。 - 死锁(多个事务循环等待锁)。 - **排查工具**:数据库死锁日志、事务监控。 ### **三、应用架构与代码问题** 1. **同步阻塞调用过多** - **表现**:线程池被占满,新请求排队等待。 - **可能原因**: - 大量同步 I/O 操作(如文件读写、网络请求)。 - 未使用异步处理(如 CompletableFuture、消息队列)。 - **优化方向**:引入异步框架(如 Reactor、Netty)或消息队列(如 Kafka)。 2. **锁竞争激烈** - **表现**:线程频繁等待锁释放,CPU 使用率高但吞吐量低。 - **可能原因**: - 全局锁(如 `synchronized` 块范围过大)。 - 锁粒度粗(如对整个对象加锁而非特定资源)。 - **优化方向**:使用细粒度锁(如 ReentrantLock)、无锁数据结构(如 ConcurrentHashMap)。 3. **缓存未生效** - **表现**:重复查询相同数据,数据库压力大。 - **可能原因**: - 缓存命中率低(如缓存过期策略不合理)。 - 未使用缓存(如频繁查询热点数据)。 - **优化方向**:引入 Redis 等缓存中间件,设置合理的缓存过期时间。 ### **四、中间件与配置问题** 1. **线程池配置不合理** - **表现**:线程池拒绝策略触发,请求被丢弃。 - **可能原因**: - 核心线程数/最大线程数设置过小。 - 任务队列长度不合理(如无界队列导致 OOM)。 - **优化方向**:根据业务特性调整线程池参数(如 CPU 密集型 vs. I/O 密集型)。 2. **容器/框架性能参数未调优** - **表现**:应用服务器(如 Tomcat、Nginx)无法处理高并发。 - **可能原因**: - Tomcat 的 maxThreads、acceptCount 参数过低。 - Nginx 的 worker_processes、worker_connections 设置不合理。 - **优化方向**:根据服务器硬件资源调整参数(如 Tomcat 的 maxThreads 设为 CPU 核心数×2)。 ### **五、外部依赖问题** 1. **第三方服务响应慢** - **表现**:调用外部 API 耗时过长,导致事务阻塞。 - **可能原因**: - 第三方服务自身性能问题。 - 网络延迟高(如跨区域调用)。 - **优化方向**:添加熔断、降级机制(如 Hystrix、Sentinel),缓存常用结果。 2. **分布式系统协调开销大** - **表现**:微服务间调用链路长,依赖服务过多。 - **可能原因**: - 服务拆分过细,导致 RPC 调用频繁。 - 分布式事务一致性开销(如两阶段提交)。 - **优化方向**:合理设计服务边界,减少不必要的服务间调用。 ### **六、排查思路建议** 1. **监控工具先行**: - 查看系统资源监控(CPU、内存、磁盘、网络)。 - 分析应用日志(如慢请求日志、异常堆栈)。 - 使用 APM 工具(如 Skywalking、Pinpoint)追踪请求链路。 2. **逐步定位瓶颈**: - 从最外层(网络、负载均衡)向内层(应用、数据库)排查。 - 对比不同负载下的性能表现(如低负载 vs. 高负载)。 3. **压测验证**: - 使用 JMeter、Gatling 等工具复现问题,观察 TPS、响应时间、错误率的变化趋势。 通过以上分析,结合具体业务场景和系统架构,通常可以定位并解决 TPS 上不去的问题。
3.我有一个jmeter脚本,我期望,某个时间点开始运行,运行一段时间之后,暂停,然后再过一段时间又运行?怎么设计?
我理解你需要使用 JMeter 设计一个具有定时启动、暂停和恢复功能的测试计划。以下是几种实现方案: ### **方案一:使用 JMeter 内置定时器和线程组控制** JMeter 的 **线程组** 和 **定时器** 可以组合实现定时启停逻辑: 1. **设置启动延迟**:在线程组中设置 `延迟启动时间`(Delay Thread Creation Until Needed),让测试在指定时间点开始。 2. **控制运行时长**:使用 `持续时间(秒)`(Duration)限制测试运行时间。 3. **多次循环执行**:通过设置多个线程组,并配合 `固定定时器`(Constant Timer)或 `BeanShell 定时器` 实现暂停后再次启动。 ```xml <?xml version="1.0" encoding="UTF-8"?> <jmeterTestPlan version="1.2" properties="5.0" jmeter="5.5"> <hashTree> <!-- 线程组1:09:00 启动,运行 30 分钟 --> <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="线程组1 - 09:00-09:30" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> <stringProp name="LoopController.loops">-1</stringProp> </elementProp> <stringProp name="ThreadGroup.num_threads">100</stringProp> <stringProp name="ThreadGroup.ramp_time">10</stringProp> <boolProp name="ThreadGroup.scheduler">true</boolProp> <stringProp name="ThreadGroup.duration">1800</stringProp> <!-- 30分钟 --> <stringProp name="ThreadGroup.delay">21600</stringProp> <!-- 6小时(09:00相对于00:00的秒数) --> <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp> <stringProp name="ThreadGroup.num_threads">100</stringProp> <hashTree/> </ThreadGroup> <!-- 线程组2:10:30 启动,运行 20 分钟 --> <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="线程组2 - 10:30-10:50" enabled="true"> <stringProp name="ThreadGroup.on_sample_error">continue</stringProp> <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="循环控制器" enabled="true"> <boolProp name="LoopController.continue_forever">false</boolProp> <stringProp name="LoopController.loops">-1</stringProp> </elementProp> <stringProp name="ThreadGroup.num_threads">150</stringProp> <stringProp name="ThreadGroup.ramp_time">10</stringProp> <boolProp name="ThreadGroup.scheduler">true</boolProp> <stringProp name="ThreadGroup.duration">1200</stringProp> <!-- 20分钟 --> <stringProp name="ThreadGroup.delay">37800</stringProp> <!-- 10小时30分钟相对于00:00的秒数 --> <boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp> <hashTree/> </ThreadGroup> <!-- 添加你的 HTTP 请求、监听器等元素 --> <hashTree/> </hashTree> </jmeterTestPlan> ``` ### **方案二:使用外部脚本控制 JMeter 进程** 通过编写脚本调用 JMeter 的命令行接口(CLI),结合系统定时任务实现更灵活的控制: 1. **编写 Shell 脚本**(适用于 Linux/macOS): ```bash #!/bin/bash # 定义测试时间点(24小时制) START_TIME_1="09:00" END_TIME_1="09:30" START_TIME_2="10:30" END_TIME_2="10:50" # JMeter 路径和测试脚本 JMETER_PATH="/path/to/jmeter/bin/jmeter" TEST_PLAN="/path/to/your/test.jmx" LOG_FILE="/path/to/results/$(date +%Y%m%d)_jmeter.log" # 计算时间间隔(秒) function get_seconds_until() { target_time=$1 current_time=$(date +%H:%M) target_seconds=$(date -d "$target_time" +%s) current_seconds=$(date -d "$current_time" +%s) if [ $target_seconds -lt $current_seconds ]; then target_seconds=$((target_seconds + 86400)) # 加一天 fi echo $((target_seconds - current_seconds)) } # 等待到指定时间 function wait_until() { target_time=$1 seconds=$(get_seconds_until "$target_time") echo "等待 $seconds 秒直到 $target_time..." sleep $seconds } # 运行 JMeter 测试 function run_test() { start_time=$1 end_time=$2 duration=$(( $(date -d "$end_time" +%s) - $(date -d "$start_time" +%s) )) echo "[$(date)] 开始测试: $start_time 到 $end_time (持续 $duration 秒)" $JMETER_PATH -n -t $TEST_PLAN -l $LOG_FILE -e -o /path/to/report -Jduration=$duration & # 等待测试结束 sleep $duration echo "[$(date)] 测试结束" } # 主流程 wait_until "$START_TIME_1" run_test "$START_TIME_1" "$END_TIME_1" wait_until "$START_TIME_2" run_test "$START_TIME_2" "$END_TIME_2" ``` 2. **添加系统定时任务**: ```bash # 使用 cron 调度脚本每天执行 0 8 * * * /path/to/your/script.sh > /path/to/script.log 2>&1 ``` ### **方案三:使用 BeanShell 或 Groovy 脚本动态控制** 在 JMeter 中添加 **BeanShell 前置处理器** 或 **JSR223 采样器**,通过脚本实现复杂的时间控制逻辑: ```java import java.util.Calendar; import java.util.Date; // 定义时间点(小时和分钟) int startTime1Hour = 9; int startTime1Minute = 0; int endTime1Hour = 9; int endTime1Minute = 30; int startTime2Hour = 10; int startTime2Minute = 30; int endTime2Hour = 10; int endTime2Minute = 50; // 获取当前时间 Calendar now = Calendar.getInstance(); int currentHour = now.get(Calendar.HOUR_OF_DAY); int currentMinute = now.get(Calendar.MINUTE); // 计算当前分钟数(从00:00开始) int currentMinutes = currentHour * 60 + currentMinute; int start1Minutes = startTime1Hour * 60 + startTime1Minute; int end1Minutes = endTime1Hour * 60 + endTime1Minute; int start2Minutes = startTime2Hour * 60 + startTime2Minute; int end2Minutes = endTime2Hour * 60 + endTime2Minute; // 判断是否在测试时间段内 boolean isInTestWindow = (currentMinutes >= start1Minutes && currentMinutes < end1Minutes) || (currentMinutes >= start2Minutes && currentMinutes < end2Minutes); // 如果不在测试窗口内,暂停执行 if (!isInTestWindow) { // 计算下一个测试窗口的等待时间 long waitMillis = 0; if (currentMinutes < start1Minutes) { waitMillis = (start1Minutes - currentMinutes) * 60 * 1000; } else if (currentMinutes < end1Minutes) { waitMillis = 0; // 正在第一个窗口内 } else if (currentMinutes < start2Minutes) { waitMillis = (start2Minutes - currentMinutes) * 60 * 1000; } else { // 今天的测试窗口已过,等待明天 waitMillis = (24 * 60 - currentMinutes + start1Minutes) * 60 * 1000; } // 暂停线程 if (waitMillis > 0) { System.out.println("当前时间: " + currentHour + ":" + currentMinute); System.out.println("等待 " + (waitMillis / 1000 / 60) + " 分钟进入下一个测试窗口"); Thread.sleep(waitMillis); } } ``` ### **方案四:使用 JMeter 的插件(如 Ultimate Thread Group)** 安装 **JMeter Plugins**,使用 **Ultimate Thread Group** 图形化配置复杂的线程调度: 1. **安装插件**: - 下载 [JMeter Plugins Manager](https://jmeter-plugins.org/wiki/PluginsManager/),并放入 JMeter 的 `lib/ext` 目录。 - 启动 JMeter,通过菜单 `Options → Plugins Manager` 安装 `jpgc-casutg`。 2. **配置 Ultimate Thread Group**: - 添加 `jp@gc - Ultimate Thread Group` 元件。 - 使用图形界面设置多个线程启动/停止时间点,精确控制吞吐量随时间的变化。 ### **推荐方案** - **简单场景**(固定时间点,无需动态调整):优先使用 **方案一**,通过线程组的调度器实现。 - **复杂场景**(动态时间逻辑或需要外部干预):使用 **方案二** 或 **方案三**,结合脚本实现灵活控制。 - **需要图形化配置**:使用 **方案四** 的 Ultimate Thread Group 插件。 通过以上方法,你可以实现 JMeter 测试在指定时间点自动启动、暂停和恢复,满足复杂的性能测试需求。
4.常用的linux性能分析命令有哪些?