Locust源码分析之stats.py模块(7)

stats.py模块是性能测试运行过程中测试数据记录的核心模块

常量定义

在该模块中,定义了如下一些常量,我们来分析下这些常量的作用

STATS_NAME_WIDTH = 60 # STATS_NAME宽度设定
CSV_STATS_INTERVAL_SEC = 2 # CSV文件写入间隔设置(s)
CONSOLE_STATS_INTERVAL_SEC = 2 # 默认在console中打印频率间隔时间(s)
CURRENT_RESPONSE_TIME_PERCENTILE_WINDOW = 10 # 页面刷新当前响应时间的间隔时间(s)
CachedResponseTimes = namedtuple("CachedResponseTimes", ["response_times", "num_requests"]) # 缓存响应时间

辅助函数

这些辅助函数主要用来按需计算响应时间


def calculate_response_time_percentile(response_times, num_requests, percent):
    """
    获取已经完成某个比例的响应时间,例如50%, 90%等
    response_times是一个StatsEntry.response_times变量,字典。
    num_requests是已经发送的请求数目。
    percent是我们指定的期望比例
    """
    num_of_request = int((num_requests * percent)) # 期望百分比的请求数量

    processed_count = 0 
    for response_time in sorted(six.iterkeys(response_times), reverse=True):
        # 对响应时间进行排序,耗时从低到高依次查找,直到查找数目大于等于当前所需要的比例为值
        processed_count += response_times[response_time]
        if(num_requests - processed_count <= num_of_request):
            return response_time


def diff_response_time_dicts(latest, old):
    """
    与缓存结合,计算响应时间某个时间段的响应时间的变化量,time是latest的Key,即时间记录是离散的
    """
    new = {}
    for time in latest:
        diff = latest[time] - old.get(time, 0)
        if diff:
            new[time] = diff
    return new

性能数据全局变量

主要用来记录salve节点全局性能数据

global_stats = RequestStats() #全局变量,记录性能测试metrics数据
#全局成功请求数
def on_request_success(request_type, name, response_time, response_length, **kwargs):
    global_stats.log_request(request_type, name, response_time, response_length)
#全局失败请求数
def on_request_failure(request_type, name, response_time, exception, **kwargs):
    global_stats.log_error(request_type, name, exception)
#生成发送给master节点的性能数据
def on_report_to_master(client_id, data):
    data["stats"] = global_stats.serialize_stats()
    data["stats_total"] = global_stats.total.get_stripped_report()
    data["errors"] =  global_stats.serialize_errors()
    global_stats.errors = {}

#slave节点更新全局metrcis
def on_slave_report(client_id, data):
    for stats_data in data["stats"]:
        entry = StatsEntry.unserialize(stats_data)
        request_key = (entry.name, entry.method)
        if not request_key in global_stats.entries:
            global_stats.entries[request_key] = StatsEntry(global_stats, entry.name, entry.method)
        global_stats.entries[request_key].extend(entry)

    for error_key, error in six.iteritems(data["errors"]):
        if error_key not in global_stats.errors:
            global_stats.errors[error_key] = StatsError.from_dict(error)
        else:
            global_stats.errors[error_key].occurences += error["occurences"]
    
    # save the old last_request_timestamp, to see if we should store a new copy
    # of the response times in the response times cache
    old_last_request_timestamp = global_stats.total.last_request_timestamp
    # update the total StatsEntry
    global_stats.total.extend(StatsEntry.unserialize(data["stats_total"]))
    if global_stats.total.last_request_timestamp > old_last_request_timestamp:
        # If we've entered a new second, we'll cache the response times. Note that there 
        # might still be reports from other slave nodes - that contains requests for the same 
        # time periods - that hasn't been received/accounted for yet. This will cause the cache to 
        # lag behind a second or two, but since StatsEntry.current_response_time_percentile() 
        # (which is what the response times cache is used for) uses an approximation of the 
        # last 10 seconds anyway, it should be fine to ignore this. 
        global_stats.total._cache_response_times(global_stats.total.last_request_timestamp)
    
#添加event应答函数
events.request_success += on_request_success 
events.request_failure += on_request_failure
events.report_to_master += on_report_to_master
events.slave_report += on_slave_report

RequestStats类

性能测试状态全局变量,主要用来记录性能测试metrics,如总量、失败、成功、异常请求数等。

这个类是用于实例化global_stats这个变量。而global_stats是一个全局变量,用于记录性能测试的运行状态和数据。

class RequestStats(object):
    def __init__(self):
        self.entries = {}
        self.errors = {}
        self.total = StatsEntry(self, "Total", None, use_response_times_cache=True) # RequestStats类中的核心属性是StatsEntry的实例
        self.start_time = None
    
    @property
    def num_requests(self): # num_requests属性记录了total下的num_requests
        return self.total.num_requests
    
    @property
    def num_failures(self): # num_failures属性记录了total下的num_failures
        return self.total.num_failures
    
    @property
    def last_request_timestamp(self): # last_request_timestamp属性记录了total下的last_request_timestamp
        return self.total.last_request_timestamp
    
    def log_request(self, method, name, response_time, content_length): # 记录一条请求数据信息
        self.total.log(response_time, content_length) # 首先在total中记录
        self.get(name, method).log(response_time, c
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Locust是一个基于Python的开源负载测试工具,它可以模拟大量的用户并发访问应用程序,从而测试应用程序的性能和稳定性。下面是Locust源码分析。 ## 1. 项目结构 Locust的代码结构如下: ``` locust/ ├── core/ │ ├── events.py │ ├── exception.py │ ├── runners.py │ ├── stats.py │ └── taskset.py ├── runners/ │ ├── __init__.py │ ├── base.py │ ├── master.py │ ├── worker.py │ └── web.py ├── stats/ │ ├── __init__.py │ ├── distributions.py │ ├── history.py │ ├── percentile.py │ └── stats.py ├── test/ │ └── __init__.py ├── ui/ │ ├── __init__.py │ ├── static/ │ ├── templates/ │ └── web.py ├── util/ │ ├── __init__.py │ ├── exceptions.py │ ├── roundrobin.py │ ├── runners.py │ └── web.py ├── __init__.py ├── contrib/ ├── runners.py └── web.py ``` 其中,`core`目录下是Locust的核心代码,`runners`目录下是Locust的运行器,`stats`目录下是Locust的统计代码,`ui`目录下是Locust的Web界面代码,`util`目录下是Locust的工具代码。 ## 2. 核心代码 Locust的核心代码位于`core`目录下,其中比较重要的文件包括: - `events.py`:定义了Locust的事件管理器,用于管理不同事件的触发和处理。 - `exception.py`:定义了Locust的自定义异常。 - `runners.py`:定义了Locust的运行器,包括单机运行器和分布式运行器。 - `stats.py`:定义了Locust的统计数据模块,包括整个测试的统计数据和单个任务的统计数据。 - `taskset.py`:定义了Locust的任务集合,即一组任务的集合。 ## 3. 运行器 Locust的运行器位于`runners`目录下,其中包括如下文件: - `base.py`:定义了运行器的基。 - `master.py`:定义了主节点运行器,用于控制整个测试的运行。 - `worker.py`:定义了工作节点运行器,用于执行任务并向主节点报告测试结果。 - `web.py`:定义了Web界面运行器,用于提供Web界面。 从上面的文件可以看出,Locust支持分布式测试,其中主节点负责控制整个测试的运行,而工作节点负责执行任务和向主节点报告测试结果。 ## 4. 统计数据 Locust的统计数据位于`stats`目录下,其中包括如下文件: - `distributions.py`:定义了一些分布函数,用于统计数据分析。 - `history.py`:定义了历史统计数据,用于保存历史统计数据并进行比较。 - `percentile.py`:定义了百分位数,用于统计数据分析。 - `stats.py`:定义了一些统计数据的,包括请求数、错误数、响应时间等。 可以看出,Locust的统计数据比较丰富,可以帮助我们更好地分析测试结果。 ## 5. Web界面 Locust的Web界面位于`ui`目录下,其中包括如下文件: - `web.py`:定义了Web界面运行器,用于提供Web界面。 - `templates`:定义了Web界面的HTML模板。 - `static`:定义了Web界面的静态资源文件,包括CSS、JavaScript等。 通过Web界面,我们可以方便地启动测试、查看测试结果以及实时监控测试进度和统计数据。 ## 6. 工具代码 Locust的工具代码位于`util`目录下,其中包括如下文件: - `exceptions.py`:定义了一些自定义异常。 - `roundrobin.py`:定义了一个循环列表,用于轮询任务执行。 - `runners.py`:定义了运行器相关的工具函数。 - `web.py`:定义了一些Web相关的工具函数。 这些工具代码为Locust的实现提供了一些辅助函数和数据结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值