黑羽压测编程接口

黑羽压测工具提供了一套Python编程接口 给大家开发。

虽然大部分的功能,可以通过代码助手自动产生,但是要做到灵活的控制,大家需要了解详细的接口和参数。

本文给出了这些接口和参数的具体说明。

HttpClient 对象

HttpClient 对象模拟一个客户端,它的初始化方法定义如下

def __init__(self
             host, 
             port=None, 
             timeout=None, 
             source_address=None, 
             blocksize=8192
            )

参数 依次为

  • host

    指定要连接的服务端 IP地址或者主机名,字符串类型。

    比如 ‘127.0.0.1’、‘www.baidu.com

    请不要 加上 http:// 这样的协议前缀

  • port

    指定要连接的服务端 端口号,整数类型。

    缺省值为 80 端口

  • timeout

    指定网络操作(比如连接)超时时间,整数类型。

    缺省值为操作系统默认设置

    比如

    client = HttpClient('127.0.0.1',
                      timeout=10   # 超时时间设定为10秒
                      ) 
    
  • source_address

    指定连接中 客户端使用的地址和端口,元组类型。缺省值为操作系统自动挑选值

    通常不需要设置此参数

  • blocksize

    指定发送缓冲大小,整数类型。 缺省值为8192个bytes

    通常不需要设置此参数

发送请求

发起请求可以使用 send方法,定义如下

def send(self,
         method,
         url,
         params=None,
         data=None, 
         json=None,
         encoding = 'utf8',
         headers={}, 
         duration=None):

请求方法

发送HTTP请求的 方法 ,由 method 参数指定,比如 GET、POST、PUT、DELETE、PATCH、HEAD 等等

client.send('GET', '/api/path1')

也可以更简单一些,直接使用对应名称(转化为小写字母)的函数方法,比如

client.get('/api/path1')

两种方法,效果是一样的。

URL

发送HTTP请求的 URL,由 url 参数指定。

由于在创建初始化实例的时候,已经指定了主机域名和协议,所以URL部分只需要 URL路径即可,比如

client.get(
    '/api/path1'  # 请求URL
    )

如果 url 参数指定了 主机域名和协议,如下所示

client.get(
    'http://127.0.0.1/api/path1'  # 请求URL
    )

主机域名和协议部分会被直接忽略,以创建 HttpClient对象的初始化参数中的 主机域名为准。

URL参数

如果你要发送的HTTP请求 有URL参数,有如下两种方式指定:

  • 直接在url中自己填入

    url参数是问号后面的以&符隔开的,如下所示

    client.get(
      'http://127.0.0.1/api/path1?p1=value1&p2=value2'
      )
    
  • 通过 params 参数指定

    可以把url参数对应的键值对存入 字典 ,传给params,如下所示

    client.get(
      'http://127.0.0.1/api/path1'
      params={
          'p1':'value1',
          'p2':'value2'
      }
      )
    

    效果和上面的例子相同

消息体 - 概述

发送HTTP请求的消息体中的数据 由 data、json、encoding 这3个参数决定。

首先大家要了解一个基本常识:

不管你的消息体中的数据是字符串、图像、视频 或者是你们自定义的数据格式,最终HTTP请求发送的消息体,都是一个个字节组成的 字节串

大多数情况下,API接口消息的 消息体 都是字符串, 需要编码为字节串发送。

消息体 - 字符串数据

大多数情况下,API接口消息的 HTTP请求消息体 都是字符串数据。

根据接口设计,字符串消息体有不同的格式,比如 JSON、XML、UrlEncoded

json格式

如果消息体是json格式的字符串(目前非常普遍),可以使用 json 参数,直接把数据对象传递给json参数

比如一个添加朋友的API接口,可以像这样

client.post(
    '/api/path1',
    # 通过json传入指定json格式的消息体参数
    json={
        'action':'addfriends'
        'userid': 3344,
        'friends': [3242,234545,232]
    })

消息体字符串最终编码为字节串的时候,使用encoding参数指定的编码方式,缺省是utf8编码

比如,你可以这样指定gbk编码方式

client.post(
    '/api/path1',
    # 通过json传入指定json格式的消息体参数
    data={
        'action':'addfriends'
        'userid': 3344,
        'friends': ['白月黑羽','紫气一元']
    })
    encoding = 'gbk'

UrlEncoded 格式

如果消息体是UrlEncoded 格式的字符串 ,可以使用 data 参数,直接把 数据对象 传递给data参数

比如一个添加朋友的API接口,可以像这样

client.post(
    '/api/path1',
    # 通过data传入指定urlencode格式的消息体参数
    # urlencode格式所有参数键值对只能是字符串格式
    data={
        'action':'addfriends'
        'userid': '3344',
        'friends': '3242,234545,232'
    })

同样的,消息体字符串最终编码为字节串的时候,使用encoding参数指定的编码方式,缺省是utf8编码

其他字符串格式

如果消息体是其他格式的字符串,比如 XML、YAML、 TOML 或者你们自己定义的字符串格式,还是可以使用 data 参数。

需要你们自己把最终的产生的 字符串 传递给data参数。

而且,要设定 相应的HTTP消息头 Content-Type , 指定消息类型。

比如

client.post(
    '/api/path1',
    headers={
        # 说明消息体是 xml格式
        'Content-Type':'application/xml'
    }
    # 下面填写内容
    data='''
    <?xml version="1.0" encoding="UTF-8"?>
    <CreateBucketConfiguration>
        <StorageClass>Standard</StorageClass>
    </CreateBucketConfiguration>
    '''
    )

前面的json格式 和 urlencoded 格式 不需要像这样指定消息头,我们的库会自动帮你指定为 application/jsonapplication/x-www-form-urlencoded

同样的,消息体字符串最终编码为字节串的时候,使用encoding参数指定的编码方式,缺省是utf8编码

消息体 - 字节数据

如果你的消息体不是纯字符串,而是其他二进制数据,比如 图片、视频、或者你们自己定义的消息体格式,

还是可以使用 data 参数。

需要你们自己把最终的产生的 字节串bytes对象 传递给data参数。

比如

client.post(
    '/api/path1',
    headers={
        # 说明消息体是哪种格式
        'Content-Type':'application/3gpp'
    }
    # 下面填写bytes的内容,注意最后的编码格式
    data=b'\x9f\x5c\x56\x90\xee\x34\x5c\x90\xee\x34\x56\x90'
    )

消息头

发起的HTTP请求,如果有特殊消息头设置,可以使用 headers参数指定。

直接把消息头对应的 一个个键值对数据 放在 字典 中, 传递给headers参数

比如

client.get(
    '/api/path1',
    # 通过headers传入指定消息头
    headers={
        'header1':'value1',
        'header2':'value2'
    })

使用代理

有时,我们希望发送的请求通过一个代理转发。

比如,发现服务端返回错误提示,我们希望通过类似 fiddler 这种抓包工具,查看具体的消息内容。

可以通过 proxy方法指定代理,比如

client = HttpClient('127.0.0.1',
                    timeout=10
                   ) 

client.proxy('127.0.0.1:8888')

获取响应信息

调用对象的send方法发送请求后,接收到服务端的响应消息后,会返回一个 响应对象 HyResponse

通过这个对象,我们可以获取一些信息,比如响应速度,HTTP响应 消息状态码、消息头、消息体等

响应速度

通过 HyResponse 对象的 responseTime 属性,可以获取 从发出请求 到 获取响应的 时间间隔。

比如

# 返回一个 HyResponse 类型的 实例对象,赋值给变量res
res = client.get('/api/path1' )

print(f"响应时长为 {res.responseTime} ms") 

响应状态码

通过 HyResponse 对象的 status 属性,可以获取 HTTP响应 消息状态码

比如

res = client.get('/api/path1' )
print(f"响应状态码为 {res.status} ") 

响应消息头

通过 HyResponse 对象的 getheader 方法,可以获取 指定 HTTP响应消息头的值

比如

client = HttpClient('www.163.com') 
res = client.get('/api/path1' )
# 获取消息头Content-Type值
ct = res.getheader('Content-Type')        
print(f"消息头Content-Type值为 {ct} ") 

如果你想获取 所有 的响应头,可以通过 getheaders 方法,比如

client = HttpClient('www.163.com') 
res = client.get('/api/path1' )
headers = res.getheaders()    
for header,value in headers: 
    print(f"消息头 {header} 值为 {value} ") 

运行结果类似

消息头 Date 值为 Tue, 28 Apr 2020 10:01:22 GMT
消息头 Content-Type 值为 text/html
消息头 Transfer-Encoding 值为 chunked
消息头 Connection 值为 keep-alive
消息头 Expires 值为 Tue, 28 Apr 2020 10:01:22 GMT
消息头 Server 值为 nginx
消息头 Location 值为 http://www.163.com
消息头 Cache-Control 值为 max-age=120
消息头 cdn-user-ip 值为 112.80.117.132
消息头 cdn-ip 值为 59.83.221.16
消息头 X-Cache-Remote 值为 MISS
消息头 cdn-source 值为 baishan

响应消息体 - 纯文本

基础知识: 返回的响应消息体不管里面存储的是什么内容,一定是以字节方式存放的。

如果你确定 返回响应消息体 是 纯文本字符串 ,需要从字节串解码为字符串。

可以直接使用 返回的 HyResponse 对象的 string 方法进行解码。

缺省的解码方式是utf8,你可以指定其他解码方式,比如gbk

比如

client = HttpClient('www.163.com') 
res = client.get('/' )
# 指定gbk解码
content = res.string('gbk')
print(f"消息体字符串为\n {content} ") 

响应消息体 - json格式

如果你确定 返回响应消息体 是 json格式 纯文本字符串 ,需要从字节串解码为字符串。

json格式字符串 当然 也还是字符串,你仍然可以先使用string方法获取json格式的字符串。

但是为了方便后续处理,通常我们获取json格式的字符串后 ,还需要 反序列化 为 Python中的数据对象进行处理。

这样,我们就需要再 调用 json库的loads 方法了。这样就比较麻烦了。


我们可以直接使用 返回的 HyResponse 对象的 json 方法进行解码。

这个方法内部实现就是先解码为字符串,再调用 json库的loads反序列化。

json 方法 缺省的解码方式是utf8,你可以指定其他解码方式,比如gbk

比如

client = HttpClient('127.0.0.1') 
res = client.get('/api/students' )

# json方法自动把json格式的消息体 
# 转化为 Python中的数据对象
studentsObj = res.json()

响应消息体 - 原始字节

如果你确定 返回响应消息体不是纯文本字符串数据,而是特殊格式的字节数据。

你要直接获取消息体中的原始字节 进行处理,可以直接使用 返回的 HyResponse 对象的 raw 方法。

这个方法会返回Python中的 bytes对象。

比如

client = HttpClient('127.0.0.1') 
res = client.get('/api/3gpp/authinterface' )

# 返回一个bytes对象,保存了消息体中的字节串
body = res.raw()

数据监测

内置功能

黑羽压测测试过程中,界面上会有 实时的性能数据显示。

而且性能测试的数据会实时保存在项目目录中,在测试完 性能场景后,可以根据这些数据绘制性能图表。

这些都是黑羽压测已经做好的功能。具体请参考这一章

错误汇报

有些业务错误,比如返回数据的格式和接口文档不一致,这种错误黑羽压测框架本身是没法检查的,只能通过你的代码检查。

如果你的代码判断响应消息有错误,可以使用 黑羽压测的内置类 Stats 的方法 oneError汇报这个错误给 黑羽压测框架 ,这样统计错误的响应消息,就会计数+1。

代码示例,如下所示


# 检查状态码,必须是400
if (response.status != 400):
    print('错误密码登录时,返回状态码不是400,测试不通过')
    # 汇报错误,加入统计结果中
    Stats.oneError()

定制功能

如果你有更细致的性能数据要统计,可以使用 黑羽压测的内置类 TestLogger 的方法 write ,记录下任意你需要记录的信息

比如,要统计整个测试过程中 响应时长 的各种范围,以及各范围内 API请求数量和对应的用户

就可以自行添加如下代码:

response = client.get(
  "/api/mgr/signin")
rt = response.responseTime # 获取响应时长

if rt > 1000: # 大于1秒
    TestLogger.write(f'API list_order >1s|{username}')
elif rt > 500: # 大于0.5秒
    TestLogger.write(f'API list_order >0.5s|{username}')
elif rt > 100: # 大于0.1秒
    TestLogger.write(f'API list_order >0.1s|{username}')
elif rt > 50: # 大于0.05秒
    TestLogger.write(f'API list_order >0.05s|{username}')

在测试结束后,就会产生时间戳日志文件名,比如

2020-04-28_22.01.08.log

我们分析测试日志,即可到这些统计数据信息。

如果有几百万条统计数据,怎么分析日志?

写Python程序啊,数据文件分析是Python最擅长的了,可以很方便的统计数据,并且使用matplotlib等库,做出统计图。


上一页
下一页