实际环境中我用GeoServer发布了一个WMTS服务后,我想了解一下这个服务能够承受住多少并发请求,能够满足多少用户同时访问。
先前的一段时间里我尝试用jmeter对我的WMTS服务的接口进行测试,但是效果并不理想。主要的问题是WMTS服务每次发送请求参数是变化的,而且每一次请求都要求服务器进行运算并返回数据才算是一次成功的请求和响应。我用jmeter进行并发访问的时候,当我的并发数给到了10000,jmeter给出的平均响应时间也很短,这不符合实际环境下浏览地图的预期。而且服务器的CPU使用率始终没有上来,说明jmeter没有真正地对WMTS服务进行有效的测试。
因为测试WMTS请求变化的参数有很多,比如说坐标系,缩放级别,行列数值,返回数据格式等,用jmeter已经不能很好地满足我的需求(当然我对jmeter了解也不是那么深入,如果有测试的大佬看到也请指点一二)。干脆一不做二不休,没有人比我更了解WMTS服务参数的变化(狗头),那就自己写一套脚本去测试不就完了。
我选择了用JS脚本的方式去实现这个并发测试程序。因为我把这个脚本运行在浏览器上后,一方面我有界面去填参数,对测试结果进行可视化监控及结果输出,另一方面我可以在浏览器的控制台查看每个并发的请求并查看其响应情况。
wmts服务分析
要对wmts服务进行测试,首先要对wmts服务是如何发起请求的,请求参数是如何定义的有一定的了解。
我们首先应用Cesium加载GeoServer发布的WMTS服务,打开控制台选取一个WMTS请求地址进行分析。
注:关于Cesium加载GeoServer发布的WMTS服务,请参考我的另一篇博客,
geoserver生产地图瓦片并发布wmts服务,并用cesium进行加载
链接---------------------------------------------
https://blog.csdn.net/weixin_43311389/article/details/99979275
var wmtsImageryProvider = new Cesium.WebMapTileServiceImageryProvider({
url: 'http:///192.168.213.202:8080/geoserver/gwc/service/wmts/rest/sichuan:sv1-03_20181210_l2a0000808046_1103190005003_011/{style}/{TileMatrixSet}/{TileMatrixSet}:{TileMatrix}/{TileRow}/{TileCol}?format=image/png',
layer: 'sichuan:sv1-03_20181210_l2a0000808046_1103190005003_011',
style: '',
format: 'image/png',
tileMatrixSetID: 'EPSG:900913' //一般使用EPSG:3857坐标系
});
viewer.imageryLayers.addImageryProvider(wmtsImageryProvider);
打开地图后,从控制台的network中选取一个wmts服务请求地址进行分析
cesium代码中的resourceUrl
http:///192.168.213.202:8080/geoserver/gwc/service/wmts/rest/sichuan:sv1-03_20181210_l2a0000808046_1103190005003_011/{style}/{TileMatrixSet}/{TileMatrixSet}:{TileMatrix}/{TileRow}/{TileCol}?format=image/png
发送的请求的Url
http://192.168.213.202:8080/geoserver/gwc/service/wmts/rest/sichuan:sv1-03_20181210_l2a0000808046_1103190005003_011//EPSG%3A900913/EPSG%3A900913:14/6865/12788?format=image%2Fpng
仔细对比两个url,可以发现有如下对应:
{style} -------- 空
{TileMatrixSet} --------- EPSG%3A900913
{TileMatrixSet}:{TileMatrix}-------------EPSG%3A900913:14
{TileRow}--------------6865
{TileCol}----------------12788
format=image/png----------------format=image%2Fpng
这几个参数组合起来的请求的意思是:
请求该wmts服务下默认风格的,坐标系为EPSG:900913的,缩放级别为14级的,第6856行,第12788列的一个png图片。
其实每一次去拖动或缩放地图,都会同时发起n(一到两位数)个wmts请求,这些请求返回的png图片在浏览器拼接成你当前浏览区域的地图。
可以看到当前这一个wmts请求返回的就是一个png图片:
针对于从前端发起的请求,风格,坐标系,返回图片格式,都是相对固定的,每一次请求都在发生变化的参数只有三个:
{TileMatrixSet}:{TileMatrix}-------------EPSG%3A900913:14 缩放级别
{TileRow}--------------6865 行号
{TileCol}----------------12788 列号
分割线-----------------------------------------------------------------------------------------
so,要进行服务测试,我的测试程序请求的url只要变换这三个参数即可。少侠等等,我沙某人哪知道我在测试程序中这三个参数怎么填,是个什么范围?
这就需要问生产地图瓦片并发布WMTS服务的兔子了,从GeoServer中查看该Tile Layer用到的gridSet有多少级,并从GeoServer的WMTS能力文档中找到每一级对应的行列号取值范围:
我发布的wmts服务是使用的默认缓存格网,所以有多少级,看“Zoom Level”就可以得到。加载服务的时候用的是EPSG:900913,所以缩放级别的范围是[0,31]。
也可以点开Gridsets具体查看该瓦片生成策略的具体比例尺信息等:
对应每个缩放级别下瓦片行列号的范围点击首页的WMTS链接,查询对应的工作空间:图层名称,可获得每一个缩放级别下对应的行列号范围:
至此,该服务的三个重要参数的取值范围我们已经确定,现在可以在脚本程序中随机变换参数集合,应用循环发起对该WMTS服务的请求轰炸。
我要测试的数据有:
- 在6S之内服务器能正确返回多少瓦片请求(能同时满足流畅访问请求数)?
- 在6S之能同时满足多少用户并发访问(能满足的流畅使用的用户数)?
- 有多少并发访问时服务器的cpu使用率超过90%(服务器最大用户数)?
脚本整理中
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>WMTS并发测试</title>
<style>
div {
height: 200px;
margin-bottom: 10px;
background-color: #a43035;
display: none;
}
</style>
<!--1. 需要引入jQuery文件-->
<script src="jquery-1.12.4.min.js"></script>
<script>
//生成两数之间的随机数
function RandomNumBoth(Min, Max) {
var Range = Max - Min;
var Rand = Math.random();
var num = Min + Math.round(Rand * Range); //四舍五入
return num;
}
//打印当前日期
function writeCurrentDate() {
var now = new Date();
var year = now.getFullYear(); //得到年份
var month = now.getMonth();//得到月份
var date = now.getDate();//得到日期
var day = now.getDay();//得到周几
var hour = now.getHours();//得到小时
var minu = now.getMinutes();//得到分钟
var sec = now.getSeconds();//得到秒
var MS = now.getMilliseconds();//获取毫秒
var week;
month = month + 1;
if (month < 10) month = "0" + month;
if (date < 10) date = "0" + date;
if (hour < 10) hour = "0" + hour;
if (minu < 10) minu = "0" + minu;
if (sec < 10) sec = "0" + sec;
if (MS < 100) MS = "0" + MS;
var arr_week = new Array("星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六");
week = arr_week[day];
var time = "";
return time = year + "年" + month + "月" + date + "日" + "______" + hour + "时" + minu + "分" + sec + "秒" + MS + "毫秒" + "________" + week;
//当前日期赋值给当前日期输入框中(jQuery easyUI)
//$("#currentDate").html(time);
//设置得到当前日期的函数的执行间隔时间,每1000毫秒刷新一次。
//var timer = setTimeout("writeCurrentDate()", 1000);
}
//校验xml
function validateXml() {
}
//2. 入口函数的标准
$(document).ready(function () {
//并发数控制
$("#add50").click(function () {
$("#count").val(Number($("#count").val()) + 50)
})
$("#sub50").click(function () {
$("#count").val(Number($("#count").val()) - 50)
})
$("#validate").click(function () {
let TileMatrixSetLimits = [];
let xml = new DOMParser().parseFromString($("#xml").val(), "text/xml");
$(xml).find("TileMatrixLimits").each(function (i) {
// var oid = $(this).attr("id");
// var TileMatrix = $(this).children("TileMatrix").text();
// var MinTileRow = $(this).children("MinTileRow").text();
// var MaxTileRow = $(this).children("MaxTileRow").text();
// var MinTileCol = $(this).children("MinTileCol").text();
// var MaxTileCol = $(this).children("MaxTileCol").text();
// ///后续操作。。。
// console.log(TileMatrix,MinTileRow,MaxTileRow,MinTileCol,MaxTileCol)
//定义当前坐标系缩放级别下的范围取值对象
var TileMatrixLimits = {};
TileMatrixLimits.TileMatrix = $(this).children("TileMatrix").text();
TileMatrixLimits.MinTileRow = Number($(this).children("MinTileRow").text());
TileMatrixLimits.MaxTileRow = Number($(this).children("MaxTileRow").text());
TileMatrixLimits.MinTileCol = Number($(this).children("MinTileCol").text());
TileMatrixLimits.MaxTileCol = Number($(this).children("MaxTileCol").text());
///后续操作。。。
console.log(TileMatrixLimits)
TileMatrixSetLimits.push(TileMatrixLimits)
});
console.log(TileMatrixSetLimits)
})
//点击执行后操作
$("#btn1").click(function () {
//获取当前并发数
let count = $("#count").val();
//获取测试服务基础地址
let address = $("#address").val();
//获取工作空间及图层信息
let spaceAndLayer = $("#spaceAndLayer").val();
//获取坐标系
let srs = $("#srs").val();
//读取xml的内容,将上一步坐标系下
//1.定义不同缩放级别坐标范围集合
let TileMatrixSetLimits = [];
//2.序列化xml文本为xml对象。
let xml = new DOMParser().parseFromString($("#xml").val(), "text/xml");
$(xml).find("TileMatrixLimits").each(function (i) {
//定义当前坐标系缩放级别下的范围取值对象
var TileMatrixLimits = {};
TileMatrixLimits.TileMatrix = $(this).children("TileMatrix").text();
TileMatrixLimits.MinTileRow = Number($(this).children("MinTileRow").text());
TileMatrixLimits.MaxTileRow = Number($(this).children("MaxTileRow").text());
TileMatrixLimits.MinTileCol = Number($(this).children("MinTileCol").text());
TileMatrixLimits.MaxTileCol = Number($(this).children("MaxTileCol").text());
///后续操作。。。
console.log(TileMatrixLimits)
//将读到的每一组数据加入数组中
TileMatrixSetLimits.push(TileMatrixLimits)
});
//获取要测试切片的比例尺范围
let minZoom = Number($("#minZoom").val());
let maxZoom = Number($("#maxZoom").val());
//定义每一次循环的缩放级别,行列号
let level = 0;
let row = 0;
let col = 0;
//成功接收返回值的请求数量
let finishCount = 0;
//开始计时
//$("#startTime").text(writeCurrentDate());
var time1 = new Date();
for (let i = 1; i <= count; i++) {
//每次发起请求时生成一组请求参数 级别, 行值 列值
//1.从界面输入的切片缩放级别中,随机生成一个要访问的缩放级别
level = RandomNumBoth(minZoom, maxZoom);
//2.根据获得的level,从对应的level缩放等级中找范围生成行列随机数
row = RandomNumBoth(TileMatrixSetLimits[level].MinTileRow, TileMatrixSetLimits[level].MaxTileRow);
col = RandomNumBoth(TileMatrixSetLimits[level].MinTileCol, TileMatrixSetLimits[level].MaxTileCol);
//3.构造本次请求的url
let url = address + '/' + spaceAndLayer + '//' + srs + '/' + srs + ':' + level + '/' + row + '/' + col + '?format=image/png';
//每执行一次循环,发送一次ajax请求,记录已经发送的请求数。
$("#sendCount").text(i);
$.ajax({
type: "GET",
// url : 'http://192.168.213.201:9090/geoserver/gwc/service/wmts/rest/sichuan:sv1-03_20181210_l2a0000808046_1103190005003_011//EPSG:900913/EPSG:900913:18/'+row+'/'+col+'?format=image/png',
url: url,
success: function () {
//带data参数就要处理data
//成功接收返回值的请求数量
console.log('第' + i + "个请求返回");
$("#finishCount").text(++finishCount);
if (finishCount == count) {
//$("#endTime").text(writeCurrentDate());
var time2 = new Date();
var interval = time2.getTime() - time1.getTime();
$("#interval").text(interval / 1000.0 + "秒");//换算成秒
}
}
});
}
});
$("#btn2").click(function () {
$("#count").text("");
$("#sendCount").text("");
$("#finishCount").text("");
$("#interval").text("");
});
});
</script>
</head>
<body>
<p>请输入测试地址:</p>
<input type="text" name="address" id="address" style="width: 1200px;"
value="http://192.168.213.202:8080/geoserver/gwc/service/wmts/rest">
<p>工作空间及图层名称:</p>
<input type="text" name="spaceAndLayer" id="spaceAndLayer" style="width: 500px;" value="sichuan:yongnian">
<p>坐标系(要和下方能力文档中给定的一致):</p>
<select id="srs">
<option value="EPSG:900913">EPSG:900913</option>
<option value="EPSG:4326">EPSG:4326</option>
</select>
<p>从能力文档中粘贴坐标系信息到此处:</p>
<textarea name="xml" id="xml" cols="150" rows="10"><TileMatrixSetLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:0</TileMatrix>
<MinTileRow>0</MinTileRow>
<MaxTileRow>0</MaxTileRow>
<MinTileCol>0</MinTileCol>
<MaxTileCol>0</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:1</TileMatrix>
<MinTileRow>0</MinTileRow>
<MaxTileRow>0</MaxTileRow>
<MinTileCol>1</MinTileCol>
<MaxTileCol>1</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:2</TileMatrix>
<MinTileRow>1</MinTileRow>
<MaxTileRow>1</MaxTileRow>
<MinTileCol>3</MinTileCol>
<MaxTileCol>3</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:3</TileMatrix>
<MinTileRow>3</MinTileRow>
<MaxTileRow>3</MaxTileRow>
<MinTileCol>6</MinTileCol>
<MaxTileCol>6</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:4</TileMatrix>
<MinTileRow>6</MinTileRow>
<MaxTileRow>6</MaxTileRow>
<MinTileCol>13</MinTileCol>
<MaxTileCol>13</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:5</TileMatrix>
<MinTileRow>12</MinTileRow>
<MaxTileRow>12</MaxTileRow>
<MinTileCol>26</MinTileCol>
<MaxTileCol>26</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:6</TileMatrix>
<MinTileRow>24</MinTileRow>
<MaxTileRow>25</MaxTileRow>
<MinTileCol>52</MinTileCol>
<MaxTileCol>52</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:7</TileMatrix>
<MinTileRow>49</MinTileRow>
<MaxTileRow>50</MaxTileRow>
<MinTileCol>104</MinTileCol>
<MaxTileCol>104</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:8</TileMatrix>
<MinTileRow>99</MinTileRow>
<MaxTileRow>100</MaxTileRow>
<MinTileCol>209</MinTileCol>
<MaxTileCol>209</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:9</TileMatrix>
<MinTileRow>199</MinTileRow>
<MaxTileRow>200</MaxTileRow>
<MinTileCol>418</MinTileCol>
<MaxTileCol>419</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:10</TileMatrix>
<MinTileRow>398</MinTileRow>
<MaxTileRow>400</MaxTileRow>
<MinTileCol>837</MinTileCol>
<MaxTileCol>838</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:11</TileMatrix>
<MinTileRow>797</MinTileRow>
<MaxTileRow>800</MaxTileRow>
<MinTileCol>1674</MinTileCol>
<MaxTileCol>1677</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:12</TileMatrix>
<MinTileRow>1595</MinTileRow>
<MaxTileRow>1600</MaxTileRow>
<MinTileCol>3348</MinTileCol>
<MaxTileCol>3355</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:13</TileMatrix>
<MinTileRow>3190</MinTileRow>
<MaxTileRow>3200</MaxTileRow>
<MinTileCol>6697</MinTileCol>
<MaxTileCol>6710</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:14</TileMatrix>
<MinTileRow>6380</MinTileRow>
<MaxTileRow>6400</MaxTileRow>
<MinTileCol>13395</MinTileCol>
<MaxTileCol>13420</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:15</TileMatrix>
<MinTileRow>12761</MinTileRow>
<MaxTileRow>12801</MaxTileRow>
<MinTileCol>26791</MinTileCol>
<MaxTileCol>26841</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:16</TileMatrix>
<MinTileRow>25523</MinTileRow>
<MaxTileRow>25603</MaxTileRow>
<MinTileCol>53583</MinTileCol>
<MaxTileCol>53682</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:17</TileMatrix>
<MinTileRow>51046</MinTileRow>
<MaxTileRow>51207</MaxTileRow>
<MinTileCol>107167</MinTileCol>
<MaxTileCol>107364</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:18</TileMatrix>
<MinTileRow>102093</MinTileRow>
<MaxTileRow>102414</MaxTileRow>
<MinTileCol>214335</MinTileCol>
<MaxTileCol>214729</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:19</TileMatrix>
<MinTileRow>204187</MinTileRow>
<MaxTileRow>204829</MaxTileRow>
<MinTileCol>428670</MinTileCol>
<MaxTileCol>429458</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:20</TileMatrix>
<MinTileRow>408375</MinTileRow>
<MaxTileRow>409659</MaxTileRow>
<MinTileCol>857340</MinTileCol>
<MaxTileCol>858916</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:21</TileMatrix>
<MinTileRow>816751</MinTileRow>
<MaxTileRow>819319</MaxTileRow>
<MinTileCol>1714680</MinTileCol>
<MaxTileCol>1717832</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:22</TileMatrix>
<MinTileRow>1633503</MinTileRow>
<MaxTileRow>1638639</MaxTileRow>
<MinTileCol>3429360</MinTileCol>
<MaxTileCol>3435664</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:23</TileMatrix>
<MinTileRow>3267007</MinTileRow>
<MaxTileRow>3277279</MaxTileRow>
<MinTileCol>6858720</MinTileCol>
<MaxTileCol>6871328</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:24</TileMatrix>
<MinTileRow>6534015</MinTileRow>
<MaxTileRow>6554559</MaxTileRow>
<MinTileCol>13717440</MinTileCol>
<MaxTileCol>13742656</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:25</TileMatrix>
<MinTileRow>13068031</MinTileRow>
<MaxTileRow>13109119</MaxTileRow>
<MinTileCol>27434880</MinTileCol>
<MaxTileCol>27485312</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:26</TileMatrix>
<MinTileRow>26136063</MinTileRow>
<MaxTileRow>26218239</MaxTileRow>
<MinTileCol>54869760</MinTileCol>
<MaxTileCol>54970624</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:27</TileMatrix>
<MinTileRow>52272127</MinTileRow>
<MaxTileRow>52436479</MaxTileRow>
<MinTileCol>109739520</MinTileCol>
<MaxTileCol>109941248</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:28</TileMatrix>
<MinTileRow>104544255</MinTileRow>
<MaxTileRow>104872959</MaxTileRow>
<MinTileCol>219479040</MinTileCol>
<MaxTileCol>219882496</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:29</TileMatrix>
<MinTileRow>209088511</MinTileRow>
<MaxTileRow>209745919</MaxTileRow>
<MinTileCol>438958080</MinTileCol>
<MaxTileCol>439764992</MaxTileCol>
</TileMatrixLimits>
<TileMatrixLimits>
<TileMatrix>EPSG:900913:30</TileMatrix>
<MinTileRow>418177023</MinTileRow>
<MaxTileRow>419491839</MaxTileRow>
<MinTileCol>877916160</MinTileCol>
<MaxTileCol>879529984</MaxTileCol>
</TileMatrixLimits>
</TileMatrixSetLimits></textarea>
<input type="button" id="validate" value="校验xml">
<p>xml格式正确</p>
<p>要测试的最小缩放级别:</p>
<input type="number" id="minZoom" value="10">
<p>要测试的最大缩放级别:</p>
<input type="number" id="maxZoom" value="15">
<p>请输入并发数:</p>
<input type="number" name="count" id="count" value="500">
<input type="button" value="+50" id="add50">
<input type="button" value="-50" id="sub50">
<hr>
<input type="button" value="开始执行" id="btn1">
<input type="button" value="清空" id="btn2">
<p>当前已发送请求数:</p>
<h5 id="sendCount"></h5>
<span>当前已返回请求数:</span>
<span id="finishCount"></span>
<span> 耗时:</span>
<span id="interval"></span>
<div></div>
<div></div>
<div></div>
</body>
</html>
操作页面如下: