目录
1. 引言
Tengine 是一款高性能的 Web 服务器,其 Sysguard 模块作为其核心功能之一,为用户提供了强大的系统监控和管理能力。通过 Sysguard 模块,用户可以实时监测服务器的运行状态、性能指标和资源利用情况,对异常压力实施熔断处理,从而有效优化系统运行和提升稳定性。本文将深入探讨 Tengine 的 Sysguard 模块,介绍其功能特点、部署配置,以及通过源码分析来深入理解其实现原理。
2. 开启sysguard模块
2.1 编译
需要开启sysguard模块,因为tengine默认是不加载这个模块的,因此在编译的时候要将这个模块编译进去,如下:
./configure --add-module=modules/ngx_http_sysguard_module
2.2 配置
直接拿官网文档的例子来举例:
server {
sysguard on; # 开启sysguard模块
sysguard_mode or; # 多个负载条件匹配模式采用or还是and方式
# 格式:负载类型 负载阈值 超过规定阈值后执行的跳转动作目标路径
# 其中部分指令中定义的period表示性能指标采样的周期
sysguard_load load=10.5 action=/loadlimit;
sysguard_cpu usage=20 period=3s action=/cpulimit;
sysguard_mem swapratio=20% action=/swaplimit;
sysguard_mem free=100M action=/freelimit;
sysguard_rt rt=0.01 period=5s action=/rtlimit;
# 以下几个location定义的是熔断以后,nginx将请求跳转到这里来执行响应处理
location /loadlimit {
return 503;
}
location /swaplimit {
return 503;
}
location /freelimit {
return 503;
}
location /rtlimit {
return 503;
}
location /cpulimit {
return 503;
}
}
针对上面的范例总结一下,我们可以定义一个或者若干个负载条件(包括load、cpu使用率、内存使用率)采用“与”或者“或”的方式进行组合,当nginx的负载超过了设定的条件的时候,就会执行对应的跳转动作。
具体的各个配置指令大家可以参考sysguard 模块官方文档。
3. 源码分析
3.1 配置参数分析
首先看一下配置加载的c语言的目标结构体。
typedef struct {
ngx_flag_t enable; // 是否启用本模块
ngx_int_t load; // load阈值
ngx_str_t load_action; // locad超阈值的跳转地址
ngx_int_t cpuusage; // cpu使用率阈值
ngx_str_t cpuusage_action; // cpu使用率超阈值的跳转地址
ngx_int_t swap; // swap阈值
ngx_str_t swap_action; // swap超阈值的跳转地址
size_t free; // free内存阈值
ngx_str_t free_action; // free内存超阈值的跳转地址
ngx_int_t rt; // 平均响应时间的阈值
ngx_int_t rt_period; // 平均响应时间的更新周期
ngx_str_t rt_action; // 超平均响应时间阈值的跳转地址
time_t interval; // 获取系统信息时的缓存时间
time_t cpu_interval; // CPU负载采样更新周期
ngx_uint_t log_level; // 超阈值事件的日志等级
ngx_uint_t mode; // or还是and模式
ngx_http_sysguard_rt_ring_t *rt_ring;
} ngx_http_sysguard_conf_t;
配置加载的时候会根据配置的设置填充以上结构体中对应的字段。
3.2 模块的初始化
模块的初始化是在ngx_http_sysguard_init函数用执行的。而ngx_http_sysguard_init函数是通过下面的声明来注册到nginx的框架中的。
static ngx_http_module_t ngx_http_sysguard_module_ctx = {
NULL, /* preconfiguration */
ngx_http_sysguard_init, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_sysguard_create_conf, /* create location configuration */
ngx_http_sysguard_merge_conf /* merge location configuration */
};
ngx_module_t ngx_http_sysguard_module = {
NGX_MODULE_V1,
&ngx_http_sysguard_module_ctx, /* module context */
ngx_http_sysguard_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
分析ngx_http_sysguard_init函数,我们可以知道,它在NGX_HTTP_PREACCESS_PHASE阶段和NGX_HTTP_LOG_PHASE阶段分别注册了ngx_http_sysguard_handler和ngx_http_sysguard_log_handler两个回调函数,函数定义如下:
static ngx_int_t
ngx_http_sysguard_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_sysguard_handler;
h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);
if (h == NULL) {
return NGX_ERROR;
}
*h = ngx_http_sysguard_log_handler;
return NGX_OK;
}
3.3 ngx_http_sysguard_handler函数
该函数在NGX_HTTP_PREACCESS_PHASE阶段被调用。
首先判断本模块是否已经执行过了并且已经被enable了,如果没有enable,则跳过本模块的后续逻辑。
if (r->main->sysguard_set) {
return NGX_DECLINED;
}
glcf = ngx_http_get_module_loc_conf(r, ngx_http_sysguard_module);
if (!glcf->enable) {
return NGX_DECLINED;
}
接着设置sysgaurd_set标记位,表示本模块已经处理过了,后续如果重新进入本函数就不需要重新处理了。
r->main->sysguard_set = 1;
为什么会再次进入本函数呢?譬如rewrite操作,或者发生了subrequest请求,都可能导致。
接下去以load系统负载为例,来看程序的实现逻辑,源码如下:
/* load */
if (glcf->load != NGX_CONF_UNSET) {
if (ngx_http_sysguard_cached_load_exptime < ngx_time()) {
ngx_http_sysguard_update_load(r, glcf->interval);
}
ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http sysguard handler load: %1.3f %1.3f %V %V",
ngx_http_sysguard_cached_load * 1.0 / 1000,
glcf->load * 1.0 / 1000,
&r->uri,
&glcf->load_action);
if (ngx_http_sysguard_cached_load > glcf->load) {
if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_OR) {
ngx_log_error(glcf->log_level, r->connection