实现一个简单的健康检查接口
接下来,我们基于 gorilla/mux 路由器实现一个简单的健康检查接口,对一个应用来说,健康检查无非是检查应用本身是否可用,以及应用依赖的核心服务是否可用,这些核心服务通常包括 DB、缓存等。
由于我们编写的是一个最简化版本的健康检查接口,所以只检查应用本身是否可用,判断的方式是健康检查接口是否可以正常访问并返回 200 OK 响应:
代码语言:javascript复制// server.go
package main
import (
"github.com/gorilla/mux"
"io"
"log"
"net/http"
)
func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
// 一个非常简单的健康检查实现:如果此 HTTP 接口调用成功,则表示应用健康
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
// 后续我们还可以通过执行 PING 指令反馈 DB、缓存状态,并将它们的健康检查结果放到响应中
io.WriteString(w, `{"alive": true}`)
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/health", HealthCheckHandler)
log.Fatal(http.ListenAndServe("localhost:8080", r))
}
代码语言:javascript复制
通过 curl 访问健康检查接口
启动这个服务,然后通过 curl -v http://localhost:8080/health
测试健康检查接口 /health
是否可用:
在实际项目中,我们可以结合 Docker 的 HEALTHCHECK
指令通过 curl 请求健康检查接口返回的结果非常方便地在容器服务集群中对应用实例健康状态进行检查,并且及时剔除不可用的节点:
HEALTHCHECK --interval=3s --timeout=3s CMD curl -fs http://localhost:8080/health || exit 1
代码语言:javascript复制
上述指令表示每隔 3s 调用一次健康检查接口 http://localhost:8080/health
,如果健康检查接口返回的响应状态码不是 200,则停用该容器服务并重新发布。
对 HTTP 接口进行测试
除了通过 curl 对 HTTP 接口进行测试外,还可以编写测试代码对 HTTP 接口进行测试,这里,我们使用 Go 语言自带的 httptest 测试包来编写 HTTP 测试代码。
httptest
测试包可用于模拟 Web 服务器,来测试 net/http
包提供的发送 HTTP 请求和捕获 HTTP 响应的方法。关于 HTTP 测试我们后面还会单独有一个章节来详细介绍,这里,我们先简单熟悉一下流程,要编写一个 HTTP 测试,包含以下步骤:
- 创建一个 HTTP 多路复用器(路由器);
- 将要测试的处理器方法应用到上述多路复用器,以便进行测试;
- 基于
net/http
包提供的方法创建一个 Request 实例模拟客户端请求(包含请求 URL 和参数); - 基于
net/http
包提供的方法创建一个 ResponseRecorder 实例用于捕获测绘请求返回的响应; - 我们将上述 Request 和 ResponseRecorder 实例传递到多路复用器的 ServeHTTP 方法发起请求,接收响应(这里的响应被 ResponseRecorder 捕获);
- 最后从 ResponseRecorder 实例中取出响应状态码和响应实体进行断言,进而判断测试是否通过。
接下来,我们按照上述流程编写 HTTP 测试,HTTP 测试和单元测试约定规则一样,因此,我们在 server.go
同级目录下创建一个测试文件 server_test.go
,并编写测试代码如下:
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestHealthCheckHandler(t *testing.T) {
// 初始化路由器并添加被测试的处理器方法
mux := http.NewServeMux()
mux.HandleFunc("/health", HealthCheckHandler)
// 新建一个请求实例模拟客户段请求,其中包含了请求方法、URL、参数等
req, err := http.NewRequest("GET", "/health", nil)
if err != nil {
t.Fatal(err)
}
// 新建一个 ResponseRecorder 来捕获响应
rr := httptest.NewRecorder()
// 传入测试请求和响应类实例并执行请求
mux.ServeHTTP(rr, req)
// 检查响应状态码(通过 ResponseRecorder 获取)是否是200,如果不是,则测试不通过
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
// 检查响应实体是否符合预期结果,如果不是,则测试不通过
expected := `{"alive": true}`
if rr.Body.String() != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
rr.Body.String(), expected)
}
}
代码语言:javascript复制
在 server_test.go
、server.go
所在目录下运行测试命令 go test .
:
上述结果表明测试成功。