《存储工具系列文章》主要介绍存储相关的测试和调试工具,包括不限于dd、fio、vdbench、iozone、iometer、cosbench等性能负载工具,及strace等调试工具。


1 概述

COSBench 是一个用于测试对象存储系统的分布式基准测试工具,也允许用户为额外的存储系统创建适配器。

由两个主要组件组成Driver和Controller。

  • Driver(Load Generator):

负责生成工作负载,向目标对象存储发出操作;性能统计

可通过http://:18088/driver/index.html访问

  • Controller:

负责协调drivers集体执行工作,收集和汇总聚合来自driver实例的运行时状态或基准测试结果。

两个组件可以混合部署在一个节点上,也可以分开来部署.

COSBench 现支持:

    OpenStack* Swift
    Amazon* S3
    Amplidata v2.3,2.5 和 3.1
    Scality*
    Ceph
    CDMI
    Google* Cloud Storage
    自定义适配器

2 安装

2.1 安装依赖包

1、java: 需要java1.6 版本及以上

2、nc(nmap-ncat): 用来检测服务端口

[root@ceph-node1 ~]# yum install java nc
[root@ceph-node1 ~]# java -version      #测试java是否可用
openjdk version "1.8.0_312"
OpenJDK Runtime Environment (build 1.8.0_312-b07)
OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)

2.2 安装cosbench

下载解压并查看

[root@ceph-node1 ~]# wget https://github.com/intel-cloud/cosbench/releases/download/v0.4.2.c4/0.4.2.c4.zip
[root@ceph-node1 ~]# unzip 0.4.2.c4.zip

官方英文使用文档(超详细,可以翻翻看)

[root@ceph-node1 0.4.2.c4]# ll *.pdf
-rw-r--r--. 1 root root  348912 Jul  9  2014 3rd-party-licenses.pdf
-rw-r--r--. 1 root root  985318 Jul  9  2014 COSBenchAdaptorDevGuide.pdf
-rw-r--r--. 1 root root 2516640 Apr 27  2016 COSBenchUserGuide.pdf

服务调用脚本说明

[root@ceph-node1 0.4.2.c4]# ll *.sh
-rw-r--r--. 1 root root 2639 Oct 30 00:25 cli.sh    #通过命令行操作cosbench
-rw-r--r--. 1 root root 2944 Apr 27  2016 cosbench-start.sh   #其他启动脚本调用的脚本
-rw-r--r--. 1 root root 1423 Dec 30  2014 cosbench-stop.sh    #其他启动脚本调用的脚本
-rw-r--r--. 1 root root  727 Apr 27  2016 start-all.sh   #在当前节点启动driver和controller组件
-rw-r--r--. 1 root root  724 Apr 27  2016 stop-all.sh    #在当前节点停止driver和controller组件
-rw-r--r--. 1 root root 1062 Jul  9  2014 start-controller.sh   #在当前节点启动controller
-rw-r--r--. 1 root root 1912 Oct 30 00:22 start-driver.sh       #在当前节点启动driver
-rw-r--r--. 1 root root  809 Jul  9  2014 stop-controller.sh    #在当前节点关闭controller
-rw-r--r--. 1 root root 1490 Apr 27  2016 stop-driver.sh        #在当前节点关闭driver

2.3 Cosbench简单验证

一、修改driver配置(如果是本地访问可忽略)

默认driver配置的页面地址为127.0.0.1,要修改为当前服务器IP,用以其他节点访问。

[root@ceph-node1 0.4.2.c4]# cat /root/0.4.2.c4/start-driver.sh
#!/bin/baship=192.168.1.51   #默认为127.0.0.1,将之修改为部署节点的IP,供其他节点访问
num=1
base_port=18088
……

二、启动cosbench

启动前为了保证driver和controller之间交互正常,需要关闭HTTP代理

[root@ceph-node1 0.4.2.c4]# unset http_proxy

启动controller和driver

[root@ceph-node1 0.4.2.c4]# sh start-all.sh
Launching osgi framwork ...
Successfully launched osgi framework!
Booting cosbench driver ...
.
Starting    cosbench-log_0.4.2    [OK]
.
Starting    cosbench-tomcat_0.4.2    [OK]
Starting    cosbench-config_0.4.2    [OK]
Starting    cosbench-http_0.4.2    [OK]
Starting    cosbench-cdmi-util_0.4.2    [OK]
Starting    cosbench-core_0.4.2    [OK]
Starting    cosbench-core-web_0.4.2    [OK]
Starting    cosbench-api_0.4.2    [OK]
Starting    cosbench-mock_0.4.2    [OK]
Starting    cosbench-ampli_0.4.2    [OK]
Starting    cosbench-swift_0.4.2    [OK]
Starting    cosbench-keystone_0.4.2    [OK]
Starting    cosbench-httpauth_0.4.2    [OK]
Starting    cosbench-s3_0.4.2    [OK]
Starting    cosbench-librados_0.4.2    [OK]
Starting    cosbench-scality_0.4.2    [OK]
Starting    cosbench-cdmi-swift_0.4.2    [OK]
Starting    cosbench-cdmi-base_0.4.2    [OK]
Starting    cosbench-driver_0.4.2    [OK]
Starting    cosbench-driver-web_0.4.2    [OK]
Successfully started cosbench driver!
Listening on port 0.0.0.0/0.0.0.0:18089 ...
Persistence bundle starting...
Persistence bundle started.
----------------------------------------------
!!! Service will listen on web port: 18088 !!!
----------------------------------------------======================================================Launching osgi framwork ...
Successfully launched osgi framework!
Booting cosbench controller ...
.
Starting    cosbench-log_0.4.2    [OK]
.
Starting    cosbench-tomcat_0.4.2    [OK]
Starting    cosbench-config_0.4.2    [OK]
Starting    cosbench-core_0.4.2    [OK]
Starting    cosbench-core-web_0.4.2    [OK]
Starting    cosbench-controller_0.4.2    [OK]
Starting    cosbench-controller-web_0.4.2    [OK]
Successfully started cosbench controller!
Listening on port 0.0.0.0/0.0.0.0:19089 ...
Persistence bundle starting...
Persistence bundle started.
----------------------------------------------
!!! Service will listen on web port: 19088 !!!
----------------------------------------------

        三、验证

查看java进程和端口

[root@ceph-node1 0.4.2.c4]# netstat -nplt |grep 18088
tcp6       0      0 :::18088                :::*                    LISTEN      6084/java
[root@ceph-node1 0.4.2.c4]# netstat -nplt |grep 19088
tcp6       0      0 :::19088                :::*                    LISTEN      6235/java
[root@ceph-node1 0.4.2.c4]# ps aux |grep java
root        6084  5.3  4.4 3562620 178000 pts/1  Sl   02:26   0:10 java -Dcosbench.tomcat.config=conf/driver-tomcat-server.xml -server -cp main/org.eclipse.equinox.launcher_1.2.0.v20110502.jar org.eclipse.equinox.launcher.Main -configuration conf/.driver -console 18089
root        6235  5.2  3.8 3555108 153032 pts/1  Sl   02:26   0:10 java -Dcosbench.tomcat.config=conf/controller-tomcat-server.xml -server -cp main/org.eclipse.equinox.launcher_1.2.0.v20110502.jar org.eclipse.equinox.launcher.Main -configuration conf/.controller -console 19089
root        6327  0.0  0.0 112812   980 pts/1    S+   02:29   0:00 grep --color=auto java

使用cli.sh查看启动信息

[root@ceph-node1 0.4.2.c4]# sh cli.sh info
Drivers:
driver1 http://127.0.0.1:18088/driver
Total: 1 driversActive Workloads:
Total: 0 active workloads

由上信息得知,controller和driver都已启动,访问http://<IP>:19088/controller/index.html验证。

2.4    常见问题

如果不安装nc,会报错

which: no nc in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin)
No appropriate tool found to detect cosbench driver status.

如果不安装java,会报

Ncat: Connection refused.

3 配置文件说明

进入conf目录下,查看s3-config-sample.xml配置文件内容如下:

[root@k8s-01 conf]# cat s3-config-sample.xml
<?xml version="1.0" encoding="UTF-8" ?>
<workload name="s3-sample" description="sample benchmark for s3"><storage type="s3" config="accesskey=<accesskey>;secretkey=<scretkey>;proxyhost=<proxyhost>;proxyport=<proxyport>;endpoint=<endpoint>" /><workflow><workstage name="init"><work type="init" workers="1" config="cprefix=s3testqwer;containers=r(1,2)" /></workstage><workstage name="prepare"><work type="prepare" workers="1" config="cprefix=s3testqwer;containers=r(1,2);objects=r(1,10);sizes=c(64)KB" /></workstage><workstage name="main"><work name="main" workers="8" runtime="30"><operation type="read" ratio="80" config="cprefix=s3testqwer;containers=u(1,2);objects=u(1,10)" /><operation type="write" ratio="20" config="cprefix=s3testqwer;containers=u(1,2);objects=u(11,20);sizes=c(64)KB" /></work></workstage><workstage name="cleanup"><work type="cleanup" workers="1" config="cprefix=s3testqwer;containers=r(1,2);objects=r(1,20)" /></workstage><workstage name="dispose"><work type="dispose" workers="1" config="cprefix=s3testqwer;containers=r(1,2)" /></workstage></workflow></workload>

下面对配置文件的参数进行说明:

workload name : 测试时显示的任务名称,这里可以自行定义

description : 描述信息,这里可以自己定义

storage type: 存储类型,这里配置为s3即可

config : 对该类型的配置,

workstage name : cosbench是分阶段按顺序执行,此处为init初始化阶段,主要是进行bucket的创建,workers表示执行该阶段的时候开启多少个工作线程,创建bucket通过不会计算为性能,所以单线程也可以;config处配置的是存储桶bucket的名称前缀;containers表示轮询数,上例中将会创建以s3testqwer为前缀,后缀分别为1和2的bucket

prepare阶段 : 配置为bucket写入的数据,workers和config以及containers与init阶段相同,除此之外还需要配置objects,表示一轮写入多少个对象,以及object的大小。

main阶段 : 这里是进行测试的阶段,runtime表示运行的时间,时间默认为秒

operation type : 操作类型,可以是read、write、delete等。ratio表示该操作所占有操作的比例,例如上面的例子中测试读写,read的比例为80%,write的比例为20%; config中配置bucket的前缀后缀信息。注意write的sizes可以根据实际测试进行修改

cleanup阶段 : 这个阶段是进行环境的清理,主要是删除bucket中的数据,保证测试后的数据不会保留在集群中

dispose阶段 : 这个阶段是删除bucket

4 测试案例配置

cosbench工具使用按照流程可分为以下几个步骤:参数配置--服务启动--提交测试--分析结果
示例使用三个节点联机测试,配置示意如下:

节点主机名

节点IP地址

cosbench角色

node241

66.66.66.241

controller、driver

node242

66.66.66.242

driver

node243

66.66.66.243

driver

4.1 参数配置

controller和driver依赖不同系统配置来启动服务,在启动controller和driver服务时,需要先行定义角色配置

一、controller配置

controller在初始化时读取conf/controller.conf配置文件启动控制器服务

[root@node241 0.4.2.c4]# cat conf/controller.conf
[controller]
concurrency=1
drivers=3
log_level=INFO
log_file=log/system.log
archive_dir=archive[driver1]
name=driver1
url=http://66.66.66.241:18088/driver[driver2]
name=driver2
url=http://66.66.66.242:18088/driver[driver3]
name=driver3
url=http://66.66.66.243:18088/driver

         [controller]

  • concurrency:默认值为1,表示可以同时执行的工作负载数量
  • drivers:默认值为1,表示此controller控制的driver数量
  • log_level:默认值为INFO,可选值为TRACE、DEBUG、INFO、WARN、ERROR,表示日志打印等级
  • log_file:默认值为log/system.log,表示日志文件存放位置
  • archive_dir:默认值为archive,表示工作负载结果存放位置,当参数值为archive,则工作负载结果存放于0.4.2/archive目录内

        [driver#x]

当有多个driver时,第{n}个driver命名为[driver{n}]

  • name:用于标识driver节点的标签,名称可以自定义
  • url:访问driver节点的地址

二、driver配置

driver在初始化时读取conf/driver.conf配置文件启动负载器服务。

[root@node241 0.4.2.c4]# cat conf/driver.conf
[driver]
name=127.0.0.1:18088
url=http://127.0.0.1:18088/driver

        [driver]

  • name:用于标识driver节点的标签,名称可以自定义
  • url:访问driver节点的地址

4.2 服务启动

一、controller启动

在controller角色节点执行以下命令启动controller

[root@node241 0.4.2.c4]# sh start-controller.sh

#检查controller服务启动情况

[root@node241 0.4.2.c4]# lsof -i:19088
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java    30012 root   88u  IPv6 184509      0t0  TCP *:19088 (LISTEN)
[root@node241 0.4.2.c4]# ps -aux | grep 30012
root     30012  0.6  4.2 4585852 165528 pts/0  Sl   15:51   0:06 java -Dcosbench.tomcat.config=conf/controller-tomcat-server.xml -server -cp main/org.eclipse.equinox.launcher_1.2.0.v20110502.jar org.eclipse.equinox.launcher.Main -configuration conf/.controller -console 19089

注:若节点需要同时启动controller和driver角色,也可以使用sh start-all.sh命令启动(start-all.sh=start-driver.sh+start-controller.sh)

二、driver启动

        在所有driver角色节点执行以下命令启动driver

[root@node242 0.4.2.c4]# sh start-driver.sh 

#检查driver服务启动情况

[root@node242 0.4.2.c4]# lsof -i:18088
COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java    23102 root  100u  IPv6 161668      0t0  TCP *:18088 (LISTEN)
[root@node242 0.4.2.c4]# ps -aux | grep 23102
root     23102  0.2  5.2 4597000 203344 pts/0  Sl   15:12   0:07 java -Dcosbench.tomcat.config=conf/driver-tomcat-server.xml -server -cp main/org.eclipse.equinox.launcher_1.2.0.v20110502.jar org.eclipse.equinox.launcher.Main -configuration conf/.driver -console 18089

4.3 提交测试

可以使用以下两种方法提交测试(示例测试参数文件为config.xml)

一、使用命令行接口

在controller节点cosbench目录下,使用sh cli.sh submit {test.xml}命令提交测试

[root@node241 0.4.2.c4]# sh cli.sh submit conf/config.xml

二、使用web控制台

web访问控制台地址http://{controller-ip}:19088/controller,点击submit new workloadsurl,提交测试文件即可

4.4 分析结果

一、使用命令行接口

在controller节点cosbench目录下,使用sh cli.sh info命令查看当前运行任务

二、使用web控制台

web访问控制台地址http://{controller-ip}:19088/controller,点击对应任务view detailsurl,查看当前运行状态及结果

5 选择表达式

5.1  概述

在测试参数文件中,auth、storage、storage、work定义中支持config属性配置,该属性包含一个可选的参数列表(使用键值对格式表示,如"a=a_val;b=b_val")

在参数列表中,常用的键包括containers、objects、sizes,用来指定如何选择容器、对象、大小.

5.2 选择器

表达式

格式

注释

constant

c(number)

仅使用指定数字

一般在常用于对象大小定义,如sizes=c(512)KB,则表示对象大小为512KB

uniform

u(min, max)

从(min,max)中均匀选择

u(1,100)表示从1到100中均匀地选取一个数字,选择是随机的,有些数字可能被选中多次,有些数字永远不会被选中

range

r(min,max)

从(min,max)递增选择

r(1,100)表示从1到100递增地选取一个数字(每个数字只被选中一次),这通常被用于特殊work(init、prepare、cleanup、dispose)

sequential

s(min,max)

从(min,max)递增选择

s(1,100)表示从1到100递增地选取一个数字(每个数字只被选中一次),这通常被用于常规work

histogram

h(min1|max1|weight1,…)

它提供了一个加权直方图生成器,要配置它,需要指定一个逗号分隔的桶列表,其中每个桶由一个范围和一个整数权重定义。例如: h(1|64|10,64|512|20,512|2048|30)KB 其中定义了一个配置文件,其中(1,64)KB被加权为10,(64,512)KB被加权为20,(512,2048)KB被加权为30.权重之和不一定是100

5.3 参数组合

基于元素类型和工作类型的选择器有额外的约束,下面两个表列出了允许的组合

  • 元素类型选择器

Key

constant (c(num))

uniform (u(min,max))

range (r(min,max))

sequential (s(min,max))

Histogram

(h(min|max|ratio))

containers

objects

sizes

  • 工作类型选择器

Key

init

prepare

normal (read)

normal (write)

normal (delete)

cleanup

dispose

containers

r(), s()

r(), s()

c(), u(), r(), s()

c(), u(), r(), s()

c(), u(), r(), s()

r(), s()

r(), s()

objects

r(), s()

c(), u(), r(), s()

c(), u(), r()

c(), u(), r(), s()

r(), s()

sizes

c(), u(), h()

c(), u(), h()

6 负载配置详解

本小节主要介绍工作负载参数定义,通常为标签对目录结构的xml文件,目录结构示意如下:

<workload><auth />    #可选项<storage /><workflow><workstage><auth />    #可选项<storage />    #可选项<work /><auth />    #可选项<storage />        #可选项<operation /></workstage></workflow>
</workload>

        相关说明:

  • 参数定义可分为多个级别,具体流程为workload-workstage-work-operation
  • 身份验证定义(auth)和存储定义(storage)可以在多个级别中定义
  • 通常只在workload工作负载级别定义即可
  • 当在多个级别定义,低级别定义优先于高级别定义,如在workload和work两个级别分别定义不同的auth和storage,最终以最低级别(work)定义为准
  • 一个workload可以定义一个或多个workstage(多个测试项时指定多个workstage),一个workstage可以定义一个或多个work(多个客户端测试时指定多个work),一个work可以定义一个或多个operation(混合读写操作时指定多个operation)
  • 多个workstage执行顺序是串行的,当执行完一个之后才会执行下一个
  • 多个work执行顺序是并行的,当执行到具体workstage时,其定义的多个work同时执行

示例:

<?xml version="1.0" encoding="UTF-8" ?>
<workload name="s3-sample" description="sample benchmark for s3"><storage type="s3" config="accesskey=<accesskey>;secretkey=<scretkey>;proxyhost=<proxyhost>;proxyport=<proxyport>;endpoint=<endpoint>" /><workflow><workstage name="init"><work type="init" workers="1" config="cprefix=s3testqwer;containers=r(1,2)" /></workstage><workstage name="prepare"><work type="prepare" workers="1" config="cprefix=s3testqwer;containers=r(1,2);objects=r(1,10);sizes=c(64)KB" /></workstage><workstage name="main"><work name="main" workers="8" runtime="30"><operation type="read" ratio="80" config="cprefix=s3testqwer;containers=u(1,2);objects=u(1,10)" /><operation type="write" ratio="20" config="cprefix=s3testqwer;containers=u(1,2);objects=u(11,20);sizes=c(64)KB" /></work></workstage><workstage name="cleanup"><work type="cleanup" workers="1" config="cprefix=s3testqwer;containers=r(1,2);objects=r(1,20)" /></workstage><workstage name="dispose"><work type="dispose" workers="1" config="cprefix=s3testqwer;containers=r(1,2)" /></workstage></workflow>
</workload>

6.1 workload定义(工作负载)

<?xml version="1.0" encoding="UTF-8" ?>
<workload name="s3-sample" description="sample benchmark for s3">
</workload>
  • name:工作负载名称定义
  • description:工作负载一些相关描述

6.2 auth定义(认证机制)

cosbench认证机制有none、mock、swauth、keystone、httpauth

  • none (do nothing, default):默认值,不进行任何操作
<auth type="none" config="" />

config参数列表如下:

参数

类型

默认值

注释

logging

布尔型

false

将信息打印到日志

retry

整型

0

指定认证失败时的重试次数

Caching

布尔型

false

是否缓存认证信息

type

字符串

“cdmi”

选项:“cdmi”或“non-cdmi”,它表示要使用的内容类型,“cdmi”表示存储访问将遵循cdmi内容类型,“non-cdmi”表示存储访问将遵循非cdmi内容类型.

Customer_headers

字符串

这是一个实验参数,用于查看是否可能支持cdmi衍生物,这可能需要额外的标头。 可以在不通知的情况下移除该参数。

  • mock (delay specified time):延迟指定时间
<auth type="mock" config="" />

config参数列表如下:

参数

类型

默认值

注释

token

字符串

“token”

Token字符串

delay

长整型

20

延迟时间(以毫秒为单位)

retry

整型

0

指定认证失败时的重试次数

  • swauth (for OpenStack Swift):适用于OpenStack Swift
<auth type="swauth" config="username={username};password={password};url=http://{controller-ip}:8080/auth/v1.0 />"

config参数列表如下:

参数

类型

默认值

注释

url

字符串

http:/{controller-ip}:8080/auth/v1.0

auth节点的URL,一般为controller节点

username

字符串

用于认证的用户名,语法account:user

password

字符串

用于认证的密码

timeout

整型

30,000

连接超时值(以毫秒为单位)

retry

整型

0

指定认证失败时的重试次数

  • keystone (for OpenStack Swift):适用于OpenStack Swift
<auth type="keystone" config="username={username};password={password};tenant_name={tenant_name};url=http://{controller-ip}:8080/v2.0;service=swift"/>

config参数列表如下:

参数

类型

默认值

注释

url

字符串

http://{controller-ip}:8080/auth/v2.0

auth节点的URL

username

字符串

用于认证的用户名。 语法account:user

password

字符串

用于认证的密码

tenant_name

字符串

用户所属的租户名称

service

字符串

swift

请求的服务

timeout

整型

30,000

连接超时值(毫秒)

retry

整型

0

指定认证失败时的重试次数

  • httpauth (Http BASIC/DIGEST)

参数

类型

默认值

注释

auth_url

字符串

http://{controller-ip}:8080/

auth节点的URL

username

字符串

用于认证的用户名。

password

字符串

用于认证的密码

timeout

整型

30,000

连接超时值(毫秒)

retry

整型

0

指定认证失败时的重试次数

6.3 storage定义(存储系统)

  • none (do nothing, default):默认值,不进行任何操作
<storage type="none" config="" />

config参数列表如下:

参数

类型

默认值

注释

logging

布尔型

false

将信息打印到日志

  • delay specified time
<storage type="mock" config="" />

config参数列表如下:

参数

类型

默认值

注释

logging

布尔型

false

将信息打印到日志

size

整型

1024

对象大小(字节)

delay

整型

10

延迟时间(毫秒)

errors

整型

0

设置错误限制以模拟失败

printing

布尔型

False

是否打印出数据内容

  • Swift (OpenStack Swift)
<storage type="swift" config="" />

config参数列表如下:

参数

类型

默认值

注释

timeout

整型

30,000

连接超时值(毫秒)

token

字符串

AUTH_xxx

认证令牌,只有在用户希望绕过认证时才需要此参数。

storage_url

字符串

http://127.0.0.1:8080/auth/v1.0

存储URL,只有在用户希望绕过认证时才需要此参数。

policy

字符串

存储策略是Swift 2.0中引入的一项功能,允许应用程序在每个容器的基础上为其存储选择一组不同的特征。 有关完整信息,请参阅最新的Swift文档:Welcome to Swift’s documentation! — Swift 2.30.1.dev7 documentation. 只有当用户希望利用不同的存储策略而不是默认存储策略时,才需要它。

  • Ampli (Amplidata)
<storage type="ampli" config="host={controller-ip};port=8080;nsroot=/namespace;policy={policy-id}" />

config参数列表如下:

参数

类型

默认值

注释

timeout

整型

30,000

连接超时值(毫秒)

host

字符串

要连接的controller节点IP

port

整型

端口

nsroot

字符串

“/namespace”

命名空间root

policy

字符串

命名空间将访问的策略ID

  • S3 (Amazon S3)
<storage type="s3" config="accesskey={accesskey};secretkey={scretkey};endpoint={endpoint}; proxyhost={proxyhost};proxyport={proxyport}" />

config参数列表如下:

参数

类型

默认值

注释

timeout

整型

30,000

连接超时值(毫秒)

accesskey

字符串

base64编码的用户名

secretkey

字符串

base64编码的密码

endpoint

字符串

http://s3.amazonaws.com

端点url(s3存储公开以供外部访问的url).

proxyhost

字符串

非必选项,按需配置,http代理主机名或IP地址

proxyport

整型

非必选项,按需配置,http代理端口。

  • Sproxyd (Scality)
<storage type="sproxyd" config="hosts={host1,host2,…};port={port};base_path={path};pool_size={maxTotal,maxPerRoute}" />

config参数列表如下:

参数

类型

默认值

注释

hosts

字符串

127.0.0.1

以逗号分隔的主机名/IP地址列表。 使用简单的循环算法在所有主机上实现请求的负载平衡

port

整型

81

connector使用的端口

base_path

字符串

/proxy/chord

sproxyd配置文件的路径(此配置文件必须具有by_path_enabled=1)

pool_size

整型或逗号分隔的整数对

60,10

第一个值是连接池的大小。 第二个值(如果提供)是给定HTTP路由的最大连接数。

  • Cdmi (SNIA CDMI)
<storage type="cdmi" config="type=<cdmi|non-cdmi;custom_headers=<header:value_reference>" />

config参数列表如下:

参数

类型

默认值

注释

type

字符串

“cdmi”

选项:“cdmi”或“non-cdmi”,它表示要使用的内容类型,“cdmi”表示存储访问将遵循cdmi内容类型,“non-cdmi”表示存储访问将遵循非cdmi内容类型

Customer_headers

字符串

这是一个实验参数,用于查看是否可能支持cdmi衍生物,这可能需要额外的标头。 可以在不通知的情况下移除该参数

  • Cdmi_swift (SNIA CDMI for swift)
<storage type="cdmi_swift" config="" />

config参数列表如下:

参数

类型

默认值

注释

timeout

整型

30,000

连接超时值(毫秒)

  • librados (for Ceph)
<storage type="librados" config="endpoint={endpoint};accesskey={accesskey};secretkey={secretkey}" />

config参数列表如下:

参数

类型

默认值

注释

endpoint

字符串

127.0.0.1

端点可以是例如监视器节点。

accesskey

字符串

用户名如“admin”。

secretkey

字符串

secretkey是admin keyring的key。

6.4 workstage定义(工作阶段)

<workstage name="<name>" >
</workstage>

name:阶段的一个名字

6.5 work定义

6.5.1 通用格式

<work name="main" type="normal" workers="128" interval="5" division="none" runtime="60" rampup="0" rampdown="0" totalOps="0" totalBytes="0" afr=”200000” config="" >
. . .
</work>

有一种常规work(normal)和四种特殊work(init、prepare、cleanup、dispose),上面示例参数针对所有组合情况,不同工作类型会有不同的参数组合,一般规则如下:

  • 通常使用workers控制负载情况
  • 通常使用runtime(包括rampup和rampdown)、totalOps、totalBytes控制负载什么时候结束,一般一个work定义只能设置其中一种,特殊work不需要定义此项参数。

参数列表如下:

属性

类型

默认值

注释

name

字符串

work的一个名称

type

字符串

normal

work的类型,可选参数为normal、init、prepare、cleanup、dispose、delay

workers

整型

并行进行work的workers数量,即同时起多少个线程运行负载

interval

整型

5

性能快照之间的间隔,即多久统计一次性能数据

division

字符串

none

控制workers之间的work分配方式,可选参数为none、container、object

runtime

整型

0

结束选项,work将执行多少秒

rampup

整型

0

结束选项,加速工作负载的秒数(需要多少秒来增加工作负载);此时间不包括在runtime中

rampdown

整型

0

结束选项,减速工作负载的秒数(需要多少秒来减少工作负载);此时间不包括在runtime中

totalOps

整型

0

结束选项,将执行多少个操作;应该是workers的倍数

totalBytes

整型

0

结束选项,要传输多少字节,应该是workers和size的乘积的倍数。

driver

字符串

将执行此work的driver,默认情况下,所有driver都将参与执行,可手动指定该work由哪个driver执行负载测试

afr

整型

200000或0

可接受的失败率,是百万分之一。

200000(常规work类型),0(特殊work类型)

参数解释

  • division(划分策略)

1)division用于将一个work划分为多个不重叠区域,这些区域有着较小的容器或者对象范围,支持的策略有none、container、object

2)不同阶段有不同的默认划分策略

  • 对于init/dispose,默认的划分策略为container
  • 对于prepare/cleanup,默认的划分策略为object
  • 对于常规work,默认划分策略为none

#示例参数如下:

<work name="main" workers="4" runtime="300" division="?"><operation type="read" ratio="100" config="containers=u(1,8);objects=u(1,1000)" />
</work>

若division="container",则表示在当前work中,worker通过container划分负载区域范围,访问模式示例如下:

注:workers数量不允许超过container

Worker

Container Range

Object Range

#1

1-2

1-1000

#2

3-4

1-1000

#3

5-6

1-1000

#4

7-8

1-1000

若division="object",则表示在当前work中,worker通过object划分负载区域范围,访问模式示例如下:

注:wrokers数量不允许超过objects

Worker

Container Range

Object Range

#1

1-8

1-250

#2

1-8

251-500

#3

1-8

501-750

#4

1-8

751-1000

6.5.2 特殊work

一、通用格式

<work type="init|prepare|cleanup|dispose|delay" workers="{number}"
config="{key}={value};{key}={value}" />

特殊work与常规work有以下不同的地方:

1)    它内部采用totalOps并计算具体数值来控制负载运行时长,因此不需要额外去定义结束选项
2)    它有隐形定义的操作,因此不需要额外再定义具体操作内容(operation)
3)    "delay"与其他不同,这会导致work只休眠指定的秒数

二、支持的特殊work

  • init(批量创建特定桶)
<work type="init" workers="4" config="containers=r(1,100)" />

参数列表如下:

参数

类型

默认值

注释

containers

字符串

容器选择表达式;例如: c(1), r(1,100)

cprefix

字符串

mycontainers_

容器前缀

csuffix

字符串

容器后缀

  • prepare(批量创建特定对象)
<work type="prepare" workers="4" config="containers=r(1,10);objects=r(1,100);sizes=c(64)KB" />

参数列表如下:

参数

类型

默认值

注释

containers

字符串

容器选择表达式;例如: c(1), u(1,100)

cprefix

字符串

mycontainers_

容器前缀

csuffix

字符串

容器后缀

objects

字符串

对象选择表达式;例如 c(1), u(1,100)

oprefix

字符串

myobjects_

对象前缀

osuffix

字符串

对象后缀

sizes

字符串

带单位(B/KB/MB/GB)的大小选择表达式;例如: c(128)KB, u(2,10)MB

chunked

布尔型

False

是否以chunked模式上传数据

content

字符串

random

使用随机数据或全零填充对象内容,可选参数为random、zero

createContainer

布尔型

False

创建相关容器(如果不存在)

hashCheck

布尔型

False

做与对象完整性检查相关的工作

  • cleanup(批量删除特定对象)
<work type="cleanup" workers="4" config="containers=r(1,10);objects=r(1,100)" />

参数列表如下:

参数

类型

默认值

注释

containers

字符串

容器选择表达式;例如 c(1), u(1,100)

cprefix

字符串

mycontainers_

容器前缀

csuffix

字符串

容器后缀

objects

字符串

对象选择表达式;例如 c(1), u(1,100)

oprefix

字符串

myobjects_

对象前缀

osuffix

字符串

对象后缀

deleteContainer

布尔型

False

删除相关容器(如果存在)

  • dispose(批量删除特定桶)
<work type="dispose" workers="4" config="containers=r(1,100)" />

参数列表如下:

参数

类型

默认值

注释

containers

字符串

容器选择表达式;例如 c(1), u(1,100)

cprefix

字符串

mycontainers_

容器前缀

csuffix

字符串

容器后缀

  • delay(插入几秒的延迟)
<workstage name=”delay” closuredelay=”60” ><work type="delay" workers="1" />
</workstage>

注:closuredelay即延迟时间(单位为秒)

6.6 operation定义

注:ratio为当前操作数占总操作数的比例,单个work定义中,所有operation的ratio之和为100

  • read(读)
<operation type="read" ratio="70" config="containers=c(1);objects=u(1,100)" />

参数列表如下:

参数

类型

默认值

注释

containers

字符串

容器选择表达式;例如 c(1), u(1,100)

cprefix

字符串

mycontainers_

容器前缀

csuffix

字符串

容器后缀

objects

字符串

对象选择表达式;例如 c(1), u(1,100)

oprefix

字符串

myobjects_

对象前缀

osuffix

字符串

对象后缀

hashCheck

布尔型

False

做与对象完整性检查相关的工作

  • write(写)
<operation type="write" ratio="20" config="containers=c(2);objects=u(1,1000);sizes=c(2)MB" />

参数列表如下:

参数

类型

默认值

注释

containers

字符串

容器选择表达式;例如 c(1), u(1,100)

cprefix

字符串

mycontainers_

容器前缀

csuffix

字符串

容器后缀

objects

字符串

对象选择表达式;例如 c(1), u(1,100)

oprefix

字符串

myobjects_

对象前缀

osuffix

字符串

对象后缀

sizes

字符串

带单位(B/KB/MB/GB)的大小选择表达式;例如: c(128)KB, u(2,10)MB

chunked

布尔型

False

是否以chunked模式上传数据

content

字符串

random

使用随机数据或全零填充对象内容,可选参数为random、zero

hashCheck

Boolean

False

做与对象完整性检查相关的工作

  • filewrite(上传)
<operation type="filewrite" ratio="20" config="containers=c(2);fileselection=s;files=/tmp/testfiles" />

参数列表如下:

参数

类型

默认值

注释

containers

字符串

容器选择表达式;例如 c(1), u(1,100)

cprefix

字符串

mycontainers_

容器前缀

csuffix

字符串

容器后缀

fileselection

字符串

哪种选择器应该只使用put选择器标识符(例如,s代表顺序)。*

files

字符串

包含要上载的文件的文件夹的路径,路径必须存在

chunked

布尔型

False

是否以chunked模式上传数据

hashCheck

布尔型

False

做与对象完整性检查相关的工作

  • delete(删除)
<operation type="delete" ratio="10" config="containers=c(2);objects=u(1,1000)" />

参数列表如下:

参数

类型

默认值

注释

containers

字符串

容器选择表达式;例如 c(1), u(1,100)

cprefix

字符串

mycontainers_

容器前缀

csuffix

字符串

容器后缀

objects

字符串

对象选择表达式;例如 c(1), u(1,100)

oprefix

字符串

myobjects_

对象前缀

osuffix

字符串

对象后缀

7 测试结果分析

  • Op-Type

操作类型,常用操作类型有read、write、filewrite、delete,具体详见operation定义(type="")

  • Op-Count

总操作数

  • Byte-Count

总字节数

  • Avg-ResTime

时延(平均响应时间,请求开始到请求完成的持续时间)

  • Throughput(Operations/s

IOPS(每秒完成的操作总数)

注:此处的值由总操作成功请求数除以总运行时间计算而来

  • Bandwidth

带宽(每秒传输的数据量)

注:此处数据量进制换算跟实际有所不同(1 MB = 1000 × 1000 bytes),故测试的性能值要高于实际值

  • Succ-Ratio

操作成功率(成功请求数/总请求数)

8 其他

8.1 测试总结

  • 为实现最大性能,此处endpoint指向的为haproxy端口(后端使用均衡模式roundrobin,指向三个集群节点不同网关)
  • 所有测试模型执行前需手动清理下缓存sudo sync && echo 3 | sudo tee /proc/sys/vm/drop_caches
  • workers一般设置为客户端CPU线程数总大小 grep 'processor' /proc/cpuinfo | sort -u | wc -l
  • objects对象数需为wokers并发数的整数倍,否则会因为线程数无法除尽,出现文件上传不全的情况

如并发数为32,上传对象10000个,实际上传对象为‭9984‬个‬

8.2 测试模型

序号

客户端个数

客户端

driver个数

driver

并发数

对象数量

对象大小

读写类型

读结果标识

写结果标识

1.

1

1

32

200000

4KB

读写

OR1

OW1

2.

3

1

32

600000

4KB

读写

OR2

OW2

3.

1

1

32

60000

4MB

读写

OR3

OW3

4.

3

1

32

180000

4MB

读写

OR4

OW4

一、单客户端4K读写测试

单个客户端,单个客户端线程数32、100个桶、单桶2000个对象(共20w个对象)、单个对象大小4KB

<?xml version="1.0" encoding="UTF-8" ?>
<workload name="4k-rw-1driver" description="sample benchmark for s3"><storage type="s3" config="accesskey={userak};secretkey={usersk};endpoint=http://{haproxy-ip}:{port};timeout=300000;max_connections=400" /><workflow><workstage name="init"><work type="init" workers="10" config="cprefix=test4kbucket;containers=r(1,100)" /></workstage><workstage name="write test4k"><work type="prepare" workers="32" driver="driver1" config="cprefix=test4kbucket;containers=r(1,100);objects=r(1,2000);sizes=c(4)KB" /></workstage><workstage name="read test4k"><work name="read" workers="32" driver="driver1" runtime="300"><operation type="read" ratio="100" config="cprefix=test4kbucket;containers=u(1,100);objects=u(1,2000)" /></work></workstage><workstage name="cleanup"><work type="cleanup" workers="32" config="cprefix=test4kbucket;containers=r(1,100);objects=r(1,2000)" /></workstage><workstage name="dispose"><work type="dispose" workers="10" config="cprefix=test4kbucket;containers=r(1,100)" /></workstage></workflow>
</workload>

二、多客户端4K读写测试

三个客户端,单个客户端线程数32、100个桶、单桶2000个对象(共60w个对象)、单个对象大小4KB

<?xml version="1.0" encoding="UTF-8" ?>
<workload name="4k-rw-3driver" description="sample benchmark for s3"><storage type="s3" config="accesskey={userak};secretkey={usersk};proxyhost=;proxyport=;endpoint=http://{haproxy-ip}:{port};timeout=300000;max_connections=400" /><workflow><workstage name="init"><work type="init" workers="10" config="cprefix=test4kbucket;containers=r(1,300)" /></workstage><workstage name="write test4k"><work type="prepare" workers="32" driver="driver1" config="cprefix=test4kbucket;containers=r(1,100);objects=r(1,2000);sizes=c(4)KB" /><work type="prepare" workers="32" driver="driver2" config="cprefix=test4kbucket;containers=r(101,200);objects=r(1,2000);sizes=c(4)KB" /><work type="prepare" workers="32" driver="driver3" config="cprefix=test4kbucket;containers=r(201,300);objects=r(1,2000);sizes=c(4)KB" /></workstage><workstage name="read test4k"><work name="read" workers="32" driver="driver1" runtime="300"><operation type="read" ratio="100" config="cprefix=test4kbucket;containers=u(1,100);objects=u(1,2000)" /></work><work name="read" workers="32" driver="driver2" runtime="300"><operation type="read" ratio="100" config="cprefix=test4kbucket;containers=u(101,200);objects=u(1,2000)" /></work><work name="read" workers="32" driver="driver3" runtime="300"><operation type="read" ratio="100" config="cprefix=test4kbucket;containers=u(201,300);objects=u(1,2000)" /></work></workstage><workstage name="cleanup"><work type="cleanup" workers="32" config="cprefix=test4kbucket;containers=r(1,300);objects=r(1,2000)" /></workstage><workstage name="dispose"><work type="dispose" workers="10" config="cprefix=test4kbucket;containers=r(1,300)" /></workstage></workflow>
</workload>

三、单客户端4M读写测试

一个客户端,单个客户端线程数32、100个桶、单桶600个对象(共6w个对象)、单个对象大小4MB

<?xml version="1.0" encoding="UTF-8" ?>
<workload name="4m-rw-1driver" description="sample benchmark for s3"><storage type="s3" config="accesskey={userak};secretkey={usersk};proxyhost=;proxyport=;endpoint=http://{haproxy-ip}:{port};timeout=300000;max_connections=400" /><workflow><workstage name="init"><work type="init" workers="10" config="cprefix=test4mbucket;containers=r(1,100)" /></workstage><workstage name="write test4m"><work type="prepare" workers="32" driver="driver1" config="cprefix=test4mbucket;containers=r(1,100);objects=r(1,600);sizes=c(4096)KB" /></workstage><workstage name="read test4m"><work name="read" workers="32" driver="driver1" runtime="300"><operation type="read" ratio="100" config="cprefix=test4mbucket;containers=u(1,100);objects=u(1,600)" /></work>  </workstage><workstage name="cleanup"><work type="cleanup" workers="32" config="cprefix=test4mbucket;containers=r(1,100);objects=r(1,600)" /></workstage><workstage name="dispose"><work type="dispose" workers="10" config="cprefix=test4mbucket;containers=r(1,100)" /></workstage></workflow>
</workload>

四、多客户端4M读写测试
        三个客户端,单个客户端线程数32、100个桶、单桶600个对象(共18w个对象)、单个对象大小4MB

<?xml version="1.0" encoding="UTF-8" ?>
<workload name="4m-rw-3driver" description="sample benchmark for s3"><storage type="s3" config="accesskey={userak};secretkey={usersk};proxyhost=;proxyport=;endpoint=http://{haproxy-ip}:{port};timeout=300000;max_connections=400" /><workflow><workstage name="init"><work type="init" workers="10" config="cprefix=test4mbucket;containers=r(1,300)" /></workstage><workstage name="write test4m"><work type="prepare" workers="32" driver="driver1" config="cprefix=test4mbucket;containers=r(1,100);objects=r(1,600);sizes=c(4096)KB" /><work type="prepare" workers="32" driver="driver2" config="cprefix=test4mbucket;containers=r(101,200);objects=r(1,600);sizes=c(4096)KB" /><work type="prepare" workers="32" driver="driver3" config="cprefix=test4mbucket;containers=r(201,300);objects=r(1,600);sizes=c(4096)KB" /></workstage><workstage name="read test4m"><work name="read" workers="32" driver="driver1" runtime="300"><operation type="read" ratio="100" config="cprefix=test4mbucket;containers=u(1,100);objects=u(1,600)" /></work><work name="read" workers="32" driver="driver2" runtime="300"><operation type="read" ratio="100" config="cprefix=test4mbucket;containers=u(101,200);objects=u(1,600)" /></work><work name="read" workers="32" driver="driver3" runtime="300"><operation type="read" ratio="100" config="cprefix=test4mbucket;containers=u(201,300);objects=u(1,600)" /></work></workstage><workstage name="cleanup"><work type="cleanup" workers="32" config="cprefix=test4mbucket;containers=r(1,300);objects=r(1,600)" /></workstage><workstage name="dispose"><work type="dispose" workers="10" config="cprefix=test4mbucket;containers=r(1,300)" /></workstage></workflow>
</workload>

9 使用建议

注:此处引用文章**COSBench测Ceph对象存储:那些网上找不到的细节**

1、cosbench部署建议

  • 根据实际情况决定客户端数量,以能压满带宽为准;
  • 运行COSBench的节点需要万兆网或更大带宽。为测试出性能瓶颈,客户端出口应大于或等于集群入口;
  • 运行COSBench的节点为专用集群外测试节点或至少是业务网与存储网分离的纯OSD节点(没有其它任何服务);

2、测试用例设计建议

1)    进行预测试,对各规模用例在集群中的表现进行预估,为正式测试用例和压力测试用例的设计与选取作参考;
2)    并发数的选取,应以能够执行完成为准。其标准是,大规模文件数量的上传测试能够上传完全;
3)    文件大小的选取,如无特殊要求,下限为分片后大于磁盘条带宽度,上限为大于rgw分片大小一倍,并应考虑横向对比需要;
4)    文件数的选取,应以用例执行时间为准,以单个用例执行时间在30分钟至90分钟为佳。选取标准参考预测试的结果;
5)    调优用例设计应选取低并发用例,不能触及带宽与磁盘I/O等物理瓶颈。
6)    一组同时测写读删时Workload各使用一个桶进行操作,分离任务;

3、用例执行建议

1)    执行前务必确认桶存在;
2)    对于所有测试用例,建议先依次执行全部写用例,清缓存后再依次执行全部读用例,再依次删除;
3)    将大规模用例拆分成多个相同的小用例,确保单个桶内的对象不会太多,上限可定为1千万;
4)    写完成后,务必使用radosgw-admin bucket stats –bucket={bucket-name}查看桶内对象数是否达到指定用例规模;
5)    读之前,应手动清除内存缓存;
6)    用例执行过程中,应不时监看用例执行情况,若发现性能表现异常,应及时进行调整并重新提交执行测试用例;
7)    删除完成后,务必查看桶内对象数,以确认删除干净;

4、结果分析建议

1)    确认结果的合理性:执行时间过短不可用(数据不准),数据异常不可用(计算错误),运行时间不成比例不可用(说明有性能显著升降),上传不完全不可用(导致结果异常);
2)    带宽需要手动计算,提取COSBench中的原始网速数据(以字节为单位)进行手工换算即可;
3)    在混合操作测试中,操作数不会严格按照比例分布,但不影响性能结果。

10 参考资料

[01] https://blog.csdn.net/mpu_nice/article/details/108235226


cosbench - 对象存储性能压力测试工具相关推荐

  1. Web服务器性能压力测试工具http_load、webbench、ab、Siege使用教程

    Web服务器性能压力测试工具http_load.webbench.ab.Siege使用教程 作者: feng 日期: 2012/07/25 发表评论 (0) 查看评论 一.http_load 程序非常 ...

  2. Web必备性能压力测试工具WebBench与ApcheBench(ab)详解

    在运维工作中,压力测试是一项很重要的工作.比如在一个网站上线之前,能承受多大访问量.在大访问量情况下性能怎样,这些数据指标好坏将会直接影响用户体验.但是,在压力测试中存在一个共性,那就是压力测试的结果 ...

  3. 【总结】个人推荐的接口测试工具和接口性能/压力测试工具

    一般接口有2种测试,现在很多是模糊了这个,但是如果细分.一般还要写<接口测试用例>和<接口性能测试用例>.同样,也分接口测试报告和接口性能测试报告. 接口的本质及其工作原理 接 ...

  4. 三种web性能压力测试工具

    三种web性能压力测试工具http_load webbench ab小结 题记:压力和性能测试工具很多,下文讨论的是我觉得比较容易上手,用的比较多的三种 http_load 下载地址:http://w ...

  5. (转)MySQL自带的性能压力测试工具mysqlslap详解

    mysqlslap 是 Mysql 自带的压力测试工具,可以模拟出大量客户端同时操作数据库的情况,通过结果信息来了解数据库的性能状况 mysqlslap 的一个主要工作场景就是对数据库服务器做基准测试 ...

  6. Web性能压力测试工具——Siege详解

    Siege是一款开源的压力测试工具,设计用于评估WEB应用在压力下的承受能力.可以根据配置对一个WEB站点进行多用户的并发访问,记录每个用户所有请求过程的相应时间,并在一定数量的并发访问下重复进行. ...

  7. Web服务器性能/压力测试工具http_load、webbench、ab、Siege使用教程

    一.http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载.但是它不同于大多数压力测试工 具,它可以以一个单一的进程运行,一般 ...

  8. 服务器性能/压力测试工具http_load、webbench、ab、Siege使用教程

    一.http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载.但是它不同于大多数压力测试工 具,它可以以一个单一的进程运行,一般 ...

  9. Web性能压力测试工具之WebBench详解

    在运维工作中,压力测试是一项很重要的工作.比如在一个网站上线之前,能承受多大访问量.在大访问量情况下性能怎样,这些数据指标好坏将会直接影响用户体验.但是,在压力测试中存在一个共性,那就是压力测试的结果 ...

最新文章

  1. subprocess installed post-installation script returned error exit status 1
  2. 诺基亚Lumia 800生产背后的故事——萨罗工厂[多图]
  3. 【原】母版页、皮肤、导航 那点事 Master Pages Themes and Navigation Controls FAQ
  4. the catalina_home environment variable
  5. WinDbg使用摘要
  6. luogu P1307 数字反转
  7. Java爬虫工程师技能列表
  8. python多进程与多线程_第十五章 Python多进程与多线程
  9. 使用httpclient下载网络上的图片
  10. Windows系统为指定用户设定本地组策略
  11. .less文件转换成.css文件,除法没有运算结果,没有换成小数表示结果
  12. 我的小米智能家居系统
  13. viper4android+xhifi+音效驱动,【资源】ViPER4Android FX 音效驱动 v2.3.0.1
  14. 情人节快到了,我部署了一套情侣头像小程序,并过审了
  15. linux非root用户如何将自己安装的python添加到环境变量
  16. 读书笔记_《统计陷阱》达莱尔.哈夫
  17. java计算机毕业设计基于springboo+vue的共享单车自行车管理系统
  18. 阿里云邮箱设置smtp服务
  19. java循环练习题及答案_循环练习题及答案
  20. React官网Hook API 索引模块知识点整理(五)

热门文章

  1. 7、统计字母、空格、数字 与 其它字符的个数
  2. Java中数组的定义和使用
  3. Python中的各种占位符
  4. vCode组件的使用
  5. 新手必看!如何在windows下安装Python(Python入门教程)
  6. windows下python运行的方法
  7. vue使用render函数自定义标签动态导入js文件
  8. 软件测试中如何测试算法?
  9. linux关于安装tar、tar.gz、tar.xz等文件的贴士
  10. java word 题目导入_java使用poi导入word题库