revel框架需手动注册/health端点:在routes中添加get /health health.check,在controllers/health.go中实现check方法,返回200 ok文本;k8s中应分离startup/liveness/readiness探针,健康检查须设超时、并行验证依赖,避免串行阻塞或写操作。

Revel框架里怎么暴露/health端点
Revel本身不内置健康检查中间件,得自己注册一个HTTP handler。别试图找AddHealthCheck这种方法——它不存在。最直接的做法是,在app/init.go的init()函数里,用revel.InterceptFunc或直接挂路由。
推荐走revel.BEFORE拦截器链之外的独立路由,避免被中间件阻塞或影响性能:
- 在
app/routes末尾加一行:GET /health Health.Check - 在
app/controllers/health.go里写 handler(注意别放错包):
package controllers
import (
"github.com/revel/revel"
)
type Health struct {
*revel.Controller
}
func (c Health) Check() revel.Result {
c.Response.Status = "200 OK"
return c.RenderText("OK")
}
这个端点只返回200和纯文本,适合做Liveness探针。如果用在Kubernetes里,记得配livenessProbe时设initialDelaySeconds,否则Revel启动慢(尤其带DB初始化时)容易被误杀。
Readiness检查必须验证依赖,不能只看进程
很多团队把/health当万能接口,结果数据库连不上还返回200,负载均衡照转流量。Revel没自动注入DB连接池状态,得手动查。
常见错误是:在Check()里调用db.QueryRow但没设超时,导致整个健康端点卡住,拖垮所有探针请求。
- 用
context.WithTimeout包一层,超时设为1.5秒以内(K8s默认probe timeout是1秒) - 缓存、消息队列、下游HTTP服务也得查,但要并行+短超时,别串行等
- 别在
Check()里触发任何写操作或日志刷盘,防止健康检查反成压测
示例片段(省略error处理):
func (c Health) Check() revel.Result {
ctx, cancel := context.WithTimeout(c.Request.Context(), 1200*time.Millisecond)
defer cancel()
err := dbPing(ctx) // 自定义ping函数,用sql.DB.PingContext
if err != nil {
c.Response.Status = "503 Service Unavailable"
return c.RenderText("DB unreachable")
}
return c.RenderText("OK")
}
Revel应用启动慢时,Startup Probe不能省
Revel启动阶段会扫描controller、加载配置、初始化DB连接池——这些都可能耗时数秒。如果你只配了livenessProbe且initialDelaySeconds设太小(比如5秒),Kubernetes会在应用真正ready前就连续失败,触发重启风暴。
正确做法是三探针分离:
-
startupProbe:路径同/health,但failureThreshold设高(如30)、periodSeconds拉长(如10),给Revel足够预热时间 -
livenessProbe:轻量,只确认进程活着(甚至可单独开个/ping端点,不碰DB) -
readinessProbe:走完整依赖检查,失败就从Service endpoint摘除
K8s YAML关键段:
startupProbe:
httpGet:
path: /health
port: 9000
failureThreshold: 30
periodSeconds: 10
livenessProbe:
httpGet:
path: /ping
port: 9000
initialDelaySeconds: 15
readinessProbe:
httpGet:
path: /health
port: 9000
initialDelaySeconds: 10
健康端点响应体要不要JSON?看谁在调用
Revel默认RenderText返回纯文本,简单高效。但有些监控系统(比如Datadog)或自研巡检脚本,会解析JSON里的status字段做聚合。这时候改用RenderJSON更稳妥。
不过要注意两点:
- 别在JSON里塞大量调试信息(比如堆栈、完整DB连接字符串),健康端点可能被未授权访问
- 如果用了
RenderJSON,确保Content-Type是application/json,否则某些Ingress(如Nginx)可能因类型不匹配拒绝转发
简单结构够用:
return c.RenderJSON(map[string]string{
"status": "ok",
"timestamp": time.Now().UTC().Format(time.RFC3339),
})
复杂度真正卡点不在代码怎么写,而在于你是否清楚每个探针背后回答的问题——是“该不该重启”,还是“该不该发流量”,抑或“能不能开始判断前两个”。混用路径、共用逻辑、忽略超时,才是线上故障最常藏身的地方。











