主动拨测监控工具开发-单页面检测功能

322 阅读3分钟

1. 产品说明

Web应用系统是现代互联网产业应用的重要实现形式,开发者通过适配浏览器,综合采用html/css/js等前端技术,构建高度易用的Web页面,实现人机交互。为探测网络质量,计算应用可用率,拨测节点模拟人工访问,其基本原理是,在保证网络可达和不违反组织安全规范的前提下,构造一个http/https客户端,以特定时间频率,向被检测对象发起访问。

因单页面检测功能模拟人工操作,从请求发起、执行到结果解析,都有严格、清晰的规范可依,检测结果准确,流程也不复杂,是拨测类工具最重要的一种的检测手段(本文后续代码说明,均使用Golang语言)

2. 检测指标

从一次http(s)请求发起到接收到响应,理论上有DNS解析、TCP建联、SSL解析、客户端总耗时、首包耗时、重定向、静态内容下载等环节,依照这个流程,就可以构建一套完整的metric(指标)系统,将每次拨测结果上报存储,为后续数据分析提供原始材料。

3. 任务配置

一个单一页面检测任务,至少应包含以下配置选项:

  • url访问地址
  • 超时时间
  • 允许的重定向次数
  • 执行频率
  • 拨测节点网络域选择

此外,完善的监控系统还会提供任务起止时间、告警时间段、告警方式、告警接收人、页面关键字匹配等额外选项,在拨测任务执行失败后,可以执行Ping、Traceroute、Tcpdump、Nslookup等系统命令,辅助排查问题。

4. 代码实现

构造http请求客户端

Golang语言中创建http请求非常方便,为了提高灵活性,这里使用"http.NewRequest"的方式。

    import (
        "net"
        "net/http"
        "net/url"
        .....
    )
    
    // 初始化关键参数,后续计算时会用到
    var (
		dnsStartTime             time.Time
		dnsDoneTime              time.Time
		connectStartTime         time.Time
		connectDoneTime          time.Time
		tlsHandshakeStartTime    time.Time
		tlsHandshakeDoneTime     time.Time
		gotConnTime              time.Time
		gotFirstResponseByteTime time.Time
		requestStartTime         time.Time
		requestDoneTime          time.Time
	)
    
    // 编写一个方法,实现http请求
    // method: 请求类型
    // uri:    请求地址
    // header: 设置http请求头
    // body:   http请求体,单页面一般是GET请求,nil值
func NewHTTPRequest(method string, uri string, header map[string]string, body io.Reader) (req *http.Request, err error) {
	req, err = http.NewRequest(method, uri, body)
	if err != nil {
		return
	}

	//设置http请求的冗余信息,这样看起来更像是一个浏览器
	req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
	req.Header.Set("Accept-Encoding", "gzip, deflate, br")
	req.Header.Set("Accept-Language", "zh,zh-CN;q=0.9")
	req.Header.Set("Cache-Control", "no-cache")
	req.Header.Set("Connection", "keep-alive")
	req.Header.Set("Pragma", "no-cache")
	req.Header.Set("Upgrade-Insecure-Requests", "1")
	req.Header.Set("UA", "patech-dialing")
	req.Header.Set("User-Agent", "/patech-dialing/Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36")

	if header == nil {
		return
	}

	for k, v := range header {
		// Set Header Host
		if strings.EqualFold(k, "host") {
			req.Host = v
			continue
		}

		req.Header.Set(k, v)
	}
	return
}

Golang中,"net/http/httptrace"包提供了完善的http请求跟踪机制,基本可以满足对一次tcp通信的跟踪和反馈。下面,利用这个工具包展示部分代码实现。


// 定义返回数据结构
type response struct {
    Common ....
}
.....

trace := &httptrace.ClientTrace{
		DNSStart: func(_ httptrace.DNSStartInfo) {
			dnsStartTime = time.Now()
		},
		DNSDone: func(info httptrace.DNSDoneInfo) {
			dnsDoneTime = time.Now()
			if info.Err != nil {
				response.Common.StatusCode = common.StatusDNSResolveFail
				logger.Warnf("httptrace dns err:%v", info.Err)
			}
		},
		ConnectStart: func(_, _ string) {
			connectStartTime = time.Now()
		},
		ConnectDone: func(_, addr string, err error) {
			response.RemoteAddr = addr
			connectDoneTime = time.Now()
			if err != nil {
				response.Common.StatusCode = common.StatusConnRefused
				logger.Warnf("httptrace connect err:%v", err)
			}
		},
		GotConn: func(_ httptrace.GotConnInfo) {
			gotConnTime = time.Now()
		},
		GotFirstResponseByte: func() {
			gotFirstResponseByteTime = time.Now()
		},
		TLSHandshakeStart: func() {
			tlsHandshakeStartTime = time.Now()
		},
		TLSHandshakeDone: func(_ tls.ConnectionState, err error) {
			tlsHandshakeDoneTime = time.Now()
			if err != nil {
				response.Common.StatusCode = common.StatusInsecureCert
				logger.Warnf("httptrace tls err:%v", err)
			}
		},
	}
// 设置任务执行上下文、超时时间等
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
httpRequest = httpRequest.WithContext(httptrace.WithClientTrace(ctx, trace))
......

以上,记录各请求环节的时间戳,基本可以获取想要的metric指标。