CurlCmd
请求参数
对数字资产对象的监测通过Curl进行,其请求参数如下表所示:
Parameters |
Interpretation |
-L |
自动重定向,当前资源已经移至其他地址时,自动重定向到新的地址 |
-k |
解除curl的安全认证(不解除有的服务就会报60错误) |
–connect-timeout [seconds] |
设置最大连接时间(后接时间) |
-m/–max-time [seconds] |
设置最大操作总时间(后接时间) |
-w |
按照后面的格式写出返回消息 |
time_namelookup |
DNS解析域名的时间 |
time_connect |
client和server端建立TCP连接的时间(包括解析域名的时间) |
time_starttransfer |
从开始到server 响应第一个字节的时间,即发送请求和服务器响应的时间之和(包括前面2个时间) |
time_total |
从开始到server发送完所有的响应数据,并关闭连接的时间(包括前面所有时间) |
speed_download |
下载速度,单位byte/s |
http_code |
返回的HTTP状态码 |
size_download |
返回内容大小,单位byte |
content_type |
返回内容的格式 |
Request示例
curl -L -k –connect-timeout 30 -m 60 -w “\n”%{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}::%{http_code}::%{size_download}::%{content_type}”\n” URL
返回说明
返回内容格式
Curl在不同环境下返回的格式不同:
- Windows环境:Windows环境下返回的内容最后一行即为上述请求参数的结果,以“::”分开。
- Linux环境:Linux环境下返回内容的格式和Windows相同,但需注意的是Linux下输入命令和输出结果的每一行都会自动加上双引号,所以代码中Linux环境下输入命令的url前后都不需加双引号,否则协议会被视为http而导致命令无法执行;返回结果中由于最后一行是一个双引号,所以要取倒数第二行,同时倒数第二行也会自动加上双引号,要进行处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| if (this.osName.startsWith("Windows")) { curlCmd = "curl -L -k --connect-timeout " + con_timeout + " -m " + ope_timeout + " -w \"\\n\"%{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}::%{http_code}::%{size_download}::%{content_type}\"\\n\" \"" + url + "\""; } else if ((this.osName.contains("Linux")) || (this.osName.contains("CentOS"))) { curlCmd = "curl -L -k --connect-timeout " + con_timeout + " -m " + ope_timeout + " -w \"\\n\"%{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}::%{http_code}::%{size_download}::%{content_type}\"\\n\" " + url; }
if (this.osName.startsWith("Windows")) { mess = response.get(size1 - 1); } else if ((this.osName.contains("Linux")) || (osName.contains("CentOS"))) { String messT = response.get(size1 - 2); mess = messT.substring(1, messT.length() - 1); }
|
返回内容处理
由于使用curl工具时,命令行内显示的是inputstream,即命令执行的结果;同时命令还会产生错误流errorstream,显示每隔一段时间接受数据量、下载速度等内容,会不断生成。输出缓冲区的size有限,如果不及时处理这两个流,就会发生阻塞,失去响应,所以程序中另外开了一个线程去读取错误流,保证两个流都及时的被读取,防止发生阻塞。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| Process process = Runtime.getRuntime().exec(curlCmd); final InputStreamReader ir1 = new InputStreamReader(process.getInputStream(), "GBK"); final InputStreamReader ir2 = new InputStreamReader(process.getErrorStream(), "GBK"); BufferedReader br1 = new BufferedReader(ir1); String line = null; final List<String> response = new ArrayList<String>(); final List<String> errs = new ArrayList<String>();
Thread thread = new Thread(() -> { BufferedReader br2 = new BufferedReader(ir2); try { String line2 = null; while ((line2 = br2.readLine()) != null) { if (!"".equals(line2)) { errs.add(line2); } } } catch (IOException e) { e.printStackTrace(); } finally { try { ir2.close(); } catch (IOException e) { e.printStackTrace(); } } }); thread.start(); while ((line = br1.readLine()) != null) { if (!"".equals(line)) { response.add(line); } } thread.join(); int exitValue = process.waitFor(); process.destroy(); ir1.close();
|
返回内容解析
返回内容提取最后一行或倒数第二行,按照“::”进行分割,一般情况下得到8个字符串,但是当curl失败时,最后一个参数“content_type”的返回结果为空,则得到7个字符串,需要单独处理。
返回内容经分割后,分别对应请求中各参数的结果,其中分为2大类结果,分别是Curl成功和Curl不成功,对应命令的exitvalue为0和不为0。
具体分类见下表:
cURL默认连接超时时间为20秒,即–connect-timeout参数设置为20秒以上时,仍然会以20秒作为最大连接时间。
当达到默认最大连接时间(即20秒)仍未连接上时会报7错误;当达到用户设定最大连接时间(只有设置小于20秒才有效)仍未连接上时会报28错误;当总操作时间达到用户设定最大操作时间(即-m参数)仍未完成时报28错误。
即7错误是达到默认连接超时时间的错误,28错误是达到用户设置超时时间的错误。
返回结果存储
Database Field |
Convertion |
Interpretation |
statusCode |
Original + Self-Define |
按照上表方式处理 |
connect_time |
time_connect - time_namelookup |
截止到建立完TCP连接的时间减去截止到解析完域名的时间 |
server_time |
time_starttransfer - time_connect |
截止到Server响应第一个字节的时间减去截止到建立完TCP连接的时间 |
transfer_time |
time_total - time_starttransfer |
总时间减去截止到server响应第一个字节的时间 |
total_time |
time_total |
原内容 |
download_speed |
speed_download |
原内容 |
data_byte |
size_download |
原内容 |
content_type |
content_type |
原内容 |
计算公式为:
total_time = namelookup_time + connect_time + server_time + transfer_time
整个请求过程可以描述为:
域名解析 → 建立TCP连接(三次握手) → 发送http的Get请求(请求能力文档或图层) → 服务器处理 → 传输数据(能力文档或图层)
其中下载速度是指整个过程的平均下载速度,即download_speed = data_byte/total_time
server_time实际上包含了发送Get请求的时间和服务器响应时间两个时间,无法分开。
需要注意的是,curl如果失败的话,返回的各种时间就会错乱,只有总时间始终有效,所以在数据库存储过程中,curl失败的情况只有total_time始终有效,其他时间可能记录为0。
详细代码
CurlCmd.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
| package com.rsgis.utils;
import com.rsgis.bean.CurlResponse; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor;
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.ArrayList; import java.util.List; import java.util.Objects;
@NoArgsConstructor @AllArgsConstructor public class CurlCmd { private String url; private int con_timeout; private int ope_timeout; private String osName;
public CurlResponse doCurlCmd() throws InterruptedException { try { String curlCmd = null; if (this.osName.startsWith("Windows")) { curlCmd = "curl -L -k --connect-timeout " + con_timeout + " -m " + ope_timeout + " -w \"\\n\"%{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}::%{http_code}::%{size_download}::%{content_type}\"\\n\" \"" + url + "\""; } else if ((this.osName.contains("Linux")) || (this.osName.contains("CentOS"))) { curlCmd = "curl -L -k --connect-timeout " + con_timeout + " -m " + ope_timeout + " -w \"\\n\"%{time_namelookup}::%{time_connect}::%{time_starttransfer}::%{time_total}::%{speed_download}::%{http_code}::%{size_download}::%{content_type}\"\\n\" " + url; }
Process process = Runtime.getRuntime().exec(curlCmd); final InputStreamReader ir1 = new InputStreamReader(process .getInputStream(), "GBK"); final InputStreamReader ir2 = new InputStreamReader(process .getErrorStream(), "GBK"); BufferedReader br1 = new BufferedReader(ir1);
String line = null; final List<String> response = new ArrayList<String>(); final List<String> errs = new ArrayList<String>();
Thread thread = new Thread(() -> { BufferedReader br2 = new BufferedReader(ir2); try { String line2 = null; while ((line2 = br2.readLine()) != null) { if (!"".equals(line2)) { errs.add(line2); } } } catch (IOException e) { e.printStackTrace(); } finally { try { ir2.close(); } catch (IOException e) { e.printStackTrace(); } } }); thread.start();
while ((line = br1.readLine()) != null) { if (!"".equals(line)) { response.add(line); } }
thread.join(); int exitValue = process.waitFor(); process.destroy();
ir1.close(); return parseMessage(response, exitValue);
} catch (IOException e) { System.out.println("IOException " + e.getMessage()); return null; } }
private CurlResponse parseMessage(List<String> response, int exitValue) { int size1 = response.size(); CurlResponse cr = new CurlResponse(); if (size1 == 0) { cr.setTime_connect(0); cr.setTime_server(0); cr.setTime_transfer(0); cr.setTime_total(0); cr.setData_size(0); cr.setDownload_speed(0); cr.setStatusCode(700); cr.setContentType(""); return cr; } else { String mess = null; if (this.osName.startsWith("Windows")) { mess = response.get(size1 - 1); } else if ((this.osName.contains("Linux")) || (osName.contains("CentOS"))) { String messT = response.get(size1 - 2); mess = messT.substring(1, messT.length() - 1); } assert mess != null; String[] message = mess.split("::"); String type = null; if (message.length == 8) { type = message[7].toLowerCase(); } else { type = ""; } boolean valid = false; if (exitValue == 0) { valid = ContentValid(response,type); }
float time0 = Float.parseFloat(message[0]); float time1 = Float.parseFloat(message[1]); float time2 = Float.parseFloat(message[2]); float time3 = Float.parseFloat(message[3]); int code = Integer.parseInt(message[5]); String data_type = type;
float connect, server, tran, total, speed; float result_speed, result_connect, result_server, result_tran, result_total; if (exitValue == 0) { connect = time1 - time0; server = time2 - time1; tran = time3 - time2; } else { connect = 0; server = 0; tran = 0; }
total = time3; speed = Float.parseFloat(message[4]);
BigDecimal bg1 = new BigDecimal(speed); BigDecimal bg2 = new BigDecimal(connect); BigDecimal bg3 = new BigDecimal(server); BigDecimal bg4 = new BigDecimal(tran); BigDecimal bg5 = new BigDecimal(total);
result_speed = bg1.setScale(3, RoundingMode.HALF_UP).floatValue(); result_connect = bg2.setScale(3, RoundingMode.HALF_UP).floatValue(); result_server = bg3.setScale(3, RoundingMode.HALF_UP).floatValue(); result_tran = bg4.setScale(3, RoundingMode.HALF_UP).floatValue(); result_total = bg5.setScale(3, RoundingMode.HALF_UP).floatValue();
cr.setContentType(data_type); cr.setTime_connect(result_connect); cr.setTime_transfer(result_tran); cr.setTime_server(result_server); cr.setTime_total(result_total); cr.setData_size(Integer.parseInt(message[6])); cr.setDownload_speed(result_speed);
if (exitValue != 0) { cr.setStatusCode(exitValue); } else { if ((code == 200) && (!valid)) { cr.setStatusCode(600);; } else { cr.setStatusCode(code); } } return cr; } } }
|