压测工具ab不能统计http请求的错误情况,包括http状态码错误和响应正文的错误关键字。
所以加层代理用于统计http错误情况,重在统计错误情况,而不是代理的性能,主要用于功能接口的测试,比如测试一下请求多少次接口会返回空数据。
被认为是错误的情况:
1、非200状态码
2、响应正文里不包含表示正确关键字
3、响应正文里包含表示错误关键字
编译方式:
go build go_ab_proxy.go
启动方式:
./go_ab_proxy
./go_ab_proxy -okstr 'code":"0'
./go_ab_proxy -failstr error
测试方式:
ab -c 2 -n 10 -X 127.0.0.1:9090 'http://www.baidu.com/'
curl -x 127.0.0.1:9090 'http://www.baidu.com/'
查看错误统计:
ctrl-c或kill结束进程时显示统计结果。
golang代码如下:
go_ab_proxy.go
Go
package main
import (
"bytes"
"flag"
"fmt"
"net/http"
"net/http/httputil"
"os"
"os/signal"
"strings"
"sync/atomic"
"syscall"
)
var (
successCount int32
failCount int32
okStr = flag.String("okstr", "", "indicate ok string")
failStr = flag.String("failstr", "", "indicate fail string")
)
func main() {
flag.Parse()
if *okStr != "" && *failStr != "" {
fmt.Printf("-okstr and -failstr only one can be used\n")
return
}
http.HandleFunc("/", middleware(func(w http.ResponseWriter, r *http.Request) {
proxy := httputil.NewSingleHostReverseProxy(r.URL)
proxy.ServeHTTP(w, r)
}))
go installSignal()
err := http.ListenAndServe(":9090", nil)
if err != nil {
fmt.Printf("proxy server start fail, %v\n", err)
return
}
}
func middleware(handler http.HandlerFunc) http.HandlerFunc {
return func(response http.ResponseWriter, request *http.Request) {
responseWrapper := &ResponseWithRecorder{
ResponseWriter: response,
StatusCode: http.StatusOK,
Body: bytes.Buffer{},
}
handler(responseWrapper, request)
if responseWrapper.StatusCode != http.StatusOK {
atomic.AddInt32(&failCount, 1)
} else {
if *okStr == "" && *failStr == "" {
atomic.AddInt32(&successCount, 1)
return
}
resBody := string(responseWrapper.Body.Bytes())
if *okStr != "" && !strings.Contains(resBody, *okStr) {
atomic.AddInt32(&failCount, 1)
return
} else if *failStr != "" && strings.Contains(resBody, *failStr) {
atomic.AddInt32(&failCount, 1)
return
}
atomic.AddInt32(&successCount, 1)
}
}
}
func installSignal() {
sigs := make(chan os.Signal, 1)
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigs
fmt.Printf("successCount=%d, failCount=%d\n", atomic.LoadInt32(&successCount), atomic.LoadInt32(&failCount))
os.Exit(0)
}()
}
type ResponseWithRecorder struct {
http.ResponseWriter
StatusCode int
Body bytes.Buffer
}
func (rec *ResponseWithRecorder) WriteHeader(statusCode int) {
rec.ResponseWriter.WriteHeader(statusCode)
rec.StatusCode = statusCode
}
func (rec *ResponseWithRecorder) Write(d []byte) (n int, err error) {
n, err = rec.ResponseWriter.Write(d)
if err != nil {
return
}
rec.Body.Write(d)
return
}
--end--