refactor: use nginx -s reload instead of systemd
- doc: add comment
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// Package healthcheck 提供针对 Spring Boot Actuator 健康端点的采样与等待能力,
|
||||
// 供更新执行器在启动容器后确认工作负载是否就绪。
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
@@ -11,23 +13,29 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// maximumResponseBytes 限制 Actuator 响应体的最大读取字节数,防止异常大响应。
|
||||
const maximumResponseBytes = 1 << 20
|
||||
|
||||
// ErrWorkloadStopped 表示工作负载在变为健康之前已经停止。
|
||||
var ErrWorkloadStopped = errors.New("workload stopped before becoming healthy")
|
||||
|
||||
// RunningProbe 在每次健康采样前核对容器是否仍处于运行状态。
|
||||
type RunningProbe func(context.Context) (bool, error)
|
||||
|
||||
// ActuatorReport 是一次成功健康采样的非敏感摘要。
|
||||
// ActuatorReport 一次成功健康采样的非敏感摘要。
|
||||
type ActuatorReport struct {
|
||||
Status string
|
||||
// Status 表示顶层健康状态,例如 UP、DOWN。
|
||||
Status string
|
||||
// Components 表示各子组件的健康状态映射。
|
||||
Components map[string]string
|
||||
}
|
||||
|
||||
// ActuatorChecker 等待 Spring Boot Actuator 顶层状态进入 UP。
|
||||
// 它只采样健康状态,不负责重启容器。
|
||||
type ActuatorChecker struct {
|
||||
client *http.Client
|
||||
// client 执行健康检查 HTTP 请求的客户端。
|
||||
client *http.Client
|
||||
// interval 相邻两次采样之间的间隔。
|
||||
interval time.Duration
|
||||
}
|
||||
|
||||
@@ -88,6 +96,7 @@ func (c *ActuatorChecker) Wait(ctx context.Context, endpoint string, timeout tim
|
||||
}
|
||||
}
|
||||
|
||||
// validateEndpoint 校验 Actuator 端点必须是合法的 http URL 且包含主机部分。
|
||||
func validateEndpoint(endpoint string) error {
|
||||
parsed, err := url.ParseRequestURI(endpoint)
|
||||
if err != nil || parsed.Scheme != "http" || parsed.Host == "" {
|
||||
@@ -96,6 +105,9 @@ func validateEndpoint(endpoint string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// sample 执行一次完整健康采样:先核对容器仍在运行,
|
||||
// 再向 Actuator 端点发起 GET 请求并解析返回的 JSON 状态。
|
||||
// 返回的报告在状态非 UP 时仍会携带实际采样到的状态信息。
|
||||
func (c *ActuatorChecker) sample(ctx context.Context, endpoint string, running RunningProbe) (ActuatorReport, bool, error) {
|
||||
isRunning, err := running(ctx)
|
||||
if err != nil {
|
||||
@@ -140,11 +152,13 @@ func (c *ActuatorChecker) sample(ctx context.Context, endpoint string, running R
|
||||
return report, true, nil
|
||||
}
|
||||
|
||||
// actuatorPayload Actuator health 端点的顶层 JSON 结构。
|
||||
type actuatorPayload struct {
|
||||
Status string `json:"status"`
|
||||
Components map[string]actuatorComponent `json:"components"`
|
||||
}
|
||||
|
||||
// actuatorComponent 健康检查组件的 JSON 结构,这里只关心其状态。
|
||||
type actuatorComponent struct {
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// healthyActuatorSample 与线上抓包结果一致的 Actuator 健康响应样例。
|
||||
const healthyActuatorSample = `{"status":"UP","components":{"db":{"status":"UP","components":{"dorisDataSource":{"status":"UP"},"postgresqlDataSource":{"status":"UP"}}},"diskSpace":{"status":"UP"},"ping":{"status":"UP"},"redis":{"status":"UP"}}}`
|
||||
|
||||
// TestActuatorCheckerAcceptsConfirmedResponseShape 验证检查器能接受与真实抓包一致的健康响应并正确解析各组件状态。
|
||||
func TestActuatorCheckerAcceptsConfirmedResponseShape(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := newHTTPClient(func(request *http.Request) *http.Response {
|
||||
@@ -31,6 +33,7 @@ func TestActuatorCheckerAcceptsConfirmedResponseShape(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestActuatorCheckerChecksOnceForTransactionRecovery 验证事务恢复路径中单次采样即可确认已记录的健康步骤。
|
||||
func TestActuatorCheckerChecksOnceForTransactionRecovery(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := newHTTPClient(func(*http.Request) *http.Response {
|
||||
@@ -43,6 +46,7 @@ func TestActuatorCheckerChecksOnceForTransactionRecovery(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestActuatorCheckerSamplesReadinessWithoutRestarting 验证在服务暂未就绪时会持续采样直到就绪,且不会触发重启。
|
||||
func TestActuatorCheckerSamplesReadinessWithoutRestarting(t *testing.T) {
|
||||
t.Parallel()
|
||||
var requests atomic.Int32
|
||||
@@ -61,6 +65,7 @@ func TestActuatorCheckerSamplesReadinessWithoutRestarting(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestActuatorCheckerStopsImmediatelyWhenContainerStops 验证容器停止时等待立即以 ErrWorkloadStopped 失败。
|
||||
func TestActuatorCheckerStopsImmediatelyWhenContainerStops(t *testing.T) {
|
||||
t.Parallel()
|
||||
checker := newTestChecker(t, http.DefaultClient)
|
||||
@@ -71,6 +76,7 @@ func TestActuatorCheckerStopsImmediatelyWhenContainerStops(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestActuatorCheckerHonorsOverallTimeout 验证整体超时到达后返回 DeadlineExceeded 错误。
|
||||
func TestActuatorCheckerHonorsOverallTimeout(t *testing.T) {
|
||||
t.Parallel()
|
||||
client := newHTTPClient(func(*http.Request) *http.Response {
|
||||
@@ -83,6 +89,7 @@ func TestActuatorCheckerHonorsOverallTimeout(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// newTestChecker 以固定短间隔创建测试用检查器。
|
||||
func newTestChecker(t *testing.T, client *http.Client) *ActuatorChecker {
|
||||
t.Helper()
|
||||
checker, err := NewActuatorChecker(client, 5*time.Millisecond)
|
||||
@@ -92,22 +99,27 @@ func newTestChecker(t *testing.T, client *http.Client) *ActuatorChecker {
|
||||
return checker
|
||||
}
|
||||
|
||||
// alwaysRunning 表示工作负载始终处于运行状态,用于大多数测试场景。
|
||||
func alwaysRunning(context.Context) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// roundTripFunc 将普通函数适配为 http.RoundTripper,便于在测试中模拟响应。
|
||||
type roundTripFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
// RoundTrip 直接调用底层的处理函数返回响应。
|
||||
func (f roundTripFunc) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
return f(request)
|
||||
}
|
||||
|
||||
// newHTTPClient 返回一个使用给定处理函数生成响应的测试 HTTP 客户端。
|
||||
func newHTTPClient(handle func(*http.Request) *http.Response) *http.Client {
|
||||
return &http.Client{Transport: roundTripFunc(func(request *http.Request) (*http.Response, error) {
|
||||
return handle(request), nil
|
||||
})}
|
||||
}
|
||||
|
||||
// response 构造一个带指定状态码与响应体的最小 HTTP 响应。
|
||||
func response(statusCode int, body string) *http.Response {
|
||||
return &http.Response{
|
||||
StatusCode: statusCode,
|
||||
|
||||
Reference in New Issue
Block a user