肇庆做网站的有网站解析后显示建设中
文章目录
- 关于监控服务器指标、CPU、内存、警报的一些解决方案
- `Prometheus` + `Grafana` 配置 `IRIS` / `Caché` 监控服务器
- `Prometheus`
- 简介
- 特点
- 架构图
- `Grafana`
- 简介
- 特点
- 配置流程
- 自定义`Prometheus`接口定义
- 配置 `Exporter` 监控服务器系统资源
- 简介
- 配置流程
- 使用 `Alertmanager`报警
- 简介
- 配置流程
- 基于`M`实现监控服务器,并用邮件报警
- 解析`Prometheus`数据接口信息
- 使用嵌入式`Python`方式获取系统`CPU`、内存等
- `python`模块之`psutil`详解
- `CPU`相关
- `Memory`内存相关
- `Disk`相关
- `Network`相关
- `Process`相关
- 通过嵌入式`Python`调用`psutil`库
- 使用邮件发送报警信息
- 使用`M`发送邮件发送消息
- 通过配置文件`csv`文件,来获取维护指标,进行邮件报警。
- 总结
- 思考
- 附
关于监控服务器指标、CPU、内存、警报的一些解决方案
本文章主要介绍以下几个章节内容:
Prometheus+Grafana配置IRIS/Caché监控服务器- 自定义
Prometheus接口定义 - 配置Caché监控服务 - 配置
Exporter监控服务器系统资源 - 使用
Alertmanager报警 - 基于
M实现监控服务器,并用邮件报警- 解析
Prometheus数据接口信息 - 使用嵌入式
Python方式获取系统CPU、内存等 - 使用邮件发送报警信息
- 解析
Prometheus + Grafana 配置 IRIS / Caché 监控服务器
首先我们介绍一下如何用IRIS结合Prometheus + Grafana的使用,在介绍如何配置之前我们先了简单了解一下工具。
Prometheus
简介
Prometheus是一个最初在SoundCloud上构建的开源系统监视和警报工具包 。 自2012年成立以来,许多公司和组织都采用了Prometheus,该项目拥有一个非常活跃的开发人员和用户社区。它现在是一个独立的开源项目,可以独立于任何公司进行维护。为了强调这一点,并澄清项目的治理结构,Prometheus于2016年加入 云计算本地计算基金会,作为继Kubernetes之后的第二个托管项目。
特点
- 具有由度量名称和键/值对标识的时间序列数据的多维数据模型。
- 一个灵活的查询语言 来利用这一维度。
- 不依赖分布式存储,单个服务器节点是自治的。
- 时间序列收集通过
HTTP上的拉模型发生。 - 通过中间网关支持推送时间序列。
- 通过服务发现或静态配置发现目标。
- 多种图形和仪表板支持模式。
架构图

Grafana
简介
Grafana是一个跨平台的开源的度量分析和可视化工具,可以通过将采集的数据查询然后可视化的展示,并及时通知。
特点
- 展示方式:快速灵活的客户端图表,面板插件有许多不同方式的可视化指标和日志,官方库中具有丰富的仪表盘插件,比如热图、折线图、图表等多种展示方式。
- 支持多种数据源:
Graphite,InfluxDB,OpenTSDB,Prometheus,Elasticsearch,CloudWatch和KairosDB等。 - 通知提醒:以可视方式定义最重要指标的警报规则,
Grafana将不断计算并发送通知,在数据达到阈值时通过Slack、PagerDuty等获得通知。 - 混合展示:在同一图表中混合使用不同的数据源,可以基于每个查询指定数据源,甚至自定义数据源。
- 注释:使用来自不同数据源的丰富事件注释图表,将鼠标悬停在事件上会显示完整的事件元数据和标记。
- 过滤器:
Ad-hoc过滤器允许动态创建新的键/值过滤器,这些过滤器会自动应用于使用该数据源的所有查询。
配置流程
- 下载
Prometheus,选择操作系统,下载对应安装包,以windows为例。
- 下载地址:https://prometheus.io/download/

- 下载完成后,直接解压在所需目录即可。解压后进入目录直接运行
premetheus.exe,prometheus默认端口为9090。

- 在浏览器输入地址、即可查看监控页面。
- 地址 - http://localhost:9090/

- 配置监控服务器信息,编辑
prometheus-2.27.1.windows-amd64/prometheus.yml文件。在scrape_configs:输入以下代码:- 这里以我的私有服务器地址为例:https://8.142.29.250:2443/api/monitor/metrics。
输入以下代码:
- job_name: "250-IRIS"metrics_path: /api/monitor/metricsscheme: httpstls_config:insecure_skip_verify: truestatic_configs:- targets: ['8.142.29.250:2443']
其中:
job_name- 服务器名称。metrics_path- 监控服务器地址路径。scheme- 网络协议。tls_config - insecure_skip_verify- 过滤完成验证。static_configs-targets-IP端口号。
prometheus.yml文件修改完成后,重新启动prometheus.exe程序,进入到Status->Targets下,即可看到添加的监控服务器信息;

- 监控具体指标,点击
Graph->勾选UseLocalime->输入监控指标iris_cpu_usage->点击Excute->选择监控时长范围30min。
iris_cpu_usage- 代表CPU使用率。

说明:这里监控指标为服务器接口里的具体指标,输入接口地址具体查看。

- 由于
Prometheus展示的信息相对简单,图表类型也比较少,下面介绍如何使用Grafana展示数据。输入Grafana地址,下载对应版本。
- 下载地址:https://grafana.com/grafana/download?

- 下载后直接运行安装,安装完成后进入
grafana/bin目录下,双击运行garafana-server.exe。garafana默认端口为3030- 访问地址:http://localhost:3000/
- 默认用户名密码
admin/admin

- 在
grafana里添加数据源Data Source。

- 选择
Prometheus。

- 填写数据源信息,并保存。
Name-PrometheusUrl-http://localhost:9090

- 点击
Save&test显示测试成功。

- 创建模版,点击
Dashboards->点击Browse->输入模版名称->New->NewDashboard。

- 点击
Add a new panel。

- 选择数据源
Data source,指标iris_cpu_usage->Run query。
Title- 修改仪表盘名称。

- 输入仪表盘名称,点击保存即可。

- 最终效果,依次类推建立多个监控指标。
iris_csp_sessions- 会话使用数iris_cpu_usage-cpu使用率iris_system_alerts- 系统警报数iris_process_count- 进程数量iris_glo_ref_per_sec-Global每秒引用数量

自定义Prometheus接口定义
那么我们是否可以自定义Prometheus接口呢?
答案肯定是可以的,整好 Caché 没有自带的监控服务,所以我们给Caché 自定义个一个监控服务接口。
- 首先我们需要创建一个Rest接口类
M.Metrics。
Class M.Metrics Extends %CSP.REST
{
}
- 在
Portal中配置Rest接口,系统->安全管理->Web应用程序->编辑Web引用程序。
- 名称 -
/api/metrics - 命名空间 - 选择
Rest接口类所在命名空间 - 分派类 -
M.Metrics

- 编写
Rest接口逻辑,下面展示主要业务逻辑代码。
- 基础仪表盘接口
ClassMethod GetDashboardMetrics(ByRef array As %DynamicArray) As %Status
{s dashboard = ##class(SYS.Stats.Dashboard).Sample()s properties = ##class(%Dictionary.ClassDefinition).%OpenId(dashboard.%ClassName(1)).Propertiesfor i = 1 : 1 : properties.Count() {s property = properties.GetAt(i)s propertyName = property.Names propertyValue = $property(dashboard, propertyName)if ((propertyValue '= "") && ('$match(propertyValue, ".*[-A-Za-z ]+.*"))) {s metricsName = ..CamelCase2Underscore(propertyName)s metricsValue = propertyValues obj = {}s obj.key = metricsNames obj.val = metricsValued array.%Push(obj)}}q $$$OK
}
- 系统使用情况
d ##class(SYS.Metrics).GetMainMetrics("", 0, .pValues, .pStatus, .pMsg)
- 系统监视器统计
d ##class(SYS.Metrics).GetGlobalStatistics("", 0, .pValues, .pStatus, .pMsg)
ECP数据统计
d ##class(SYS.Metrics).GetECPStatistics("", 0, .pValues, .pStatus, .pMsg)
- 磁盘和缓冲区数据统计
d ##class(SYS.Metrics).GetStatistics("",0,.pValues,.pStatus,.pMsg)
- 许可数据统计
ClassMethod GetLicenseStatistics(ByRef array As %DynamicArray)
{s licenseUsed = ##class(%SYSTEM.License).LUConsumed()s obj = {}s obj.key = ..#PREFIX _ "_license_used"s obj.val = licenseUsedd array.%Push(obj)s licenseAvailable=##class(%SYSTEM.License).LUAvailable()s obj = {}s obj.key = ..#PREFIX _ "_license_avail"s obj.val = licenseAvailabled array.%Push(obj)s licenseTotal=##class(%SYSTEM.License).GetUserLimit()s obj = {}s obj.key = ..#PREFIX _ "_license_total"s obj.val = licenseTotald array.%Push(obj)s obj = {}s obj.key =..#PREFIX _ "_license_load"s obj.val = $fn((licenseUsed / (licenseAvailable + licenseUsed)), "N", "4")d array.%Push(obj)
}
- 开放性事务数据统计
ClassMethod GetOpenTransactionStatistics(ByRef array As %DynamicArray)
{if ($zv [ "Cache") {s index = "^CacheTemp.SysMetrics"} else {s index = "^IRIS.Temp.SysMetrics"}if (@index@("Transactions")) = "OK" {s val = 1} else {s val = 0}s obj = {}s obj.key = ..#PREFIX _ "_open_transaction"s obj.val = vald array.%Push(obj)
}
- 数据库情况
ClassMethod GetDatabaseStatistics(ByRef array As %DynamicArray)
{s statement = ##class(%SQL.Statement).%New()s sc = statement.%PrepareClassQuery("%SYS.DatabaseQuery", "FreeSpace")s rs = statement.%Execute()while (rs.%Next()) {s databaseName = rs.%Get("DatabaseName")s freeRate = rs.%GetData(7)s availableNum = rs.%Get("AvailableNum")s diskFreeSpaceNum = rs.%Get("DiskFreeSpaceNum")s obj = {}s obj.key =..#PREFIX _ "_database_free"s obj.val = (100 - freeRate) / 100s tag = {}s tag.id = databaseNames obj.tag = tagd array.%Push(obj)}
}
- 把所有数据组装到一起。
ClassMethod GetMetrics()
{s array = []#; 基础仪表盘d ..GetDashboardMetrics(.array)#; 系统使用情况d ##class(SYS.Metrics).GetMainMetrics("", 0, .pValues, .pStatus, .pMsg)d ..ParseJson(.pValues, .array)#; 系统监视器统计d ##class(SYS.Metrics).GetGlobalStatistics("", 0, .pValues, .pStatus, .pMsg)d ..ParseJson(.pValues, .array)#; ECP 数据统计d ##class(SYS.Metrics).GetECPStatistics("", 0, .pValues, .pStatus, .pMsg)d ..ParseJson(.pValues, .array)#; 磁盘和缓冲区数据统计d ##class(SYS.Metrics).GetStatistics("",0,.pValues,.pStatus,.pMsg)d ..ParseJson(.pValues, .array)#; 许可数据统计d ..GetLicenseStatistics(.array)#; 开放性事务数据统计d ..GetOpenTransactionStatistics(.array)#; 数据库情况d ..GetDatabaseStatistics(.array)q array
}
- 将数据解析成
Prometheus数据格式。
prometheus将所有数据保存为timeseries data,用metric name和label区分,label是在metric name上的更细维度的划分,其中的每一个实例是由一个float64和timestamp组成,只不过timestamp是隐式加上去的,#HELP代表指标的注释信息,#TYPE用于定义样本的类型注释信息。如下示例:
# HELP go_gc_duration_seconds A summary of the pause duration of garbage collection cycles.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
metrics name- 为go_gc_duration_secondslabel- 为quantile="0"float64- 为0
ClassMethod Metrics2Prometheus() As %Status
{s %response.ContentType = "text/plain;version=0.0.4;charset=utf-8"s array = ..GetMetrics()s iter = array.%GetIterator()while iter.%GetNext(.key, .json) { s str = json.keyif (json.%IsDefined("tag")) {s tag = json.tags tagIter = tag.%GetIterator()s str = str _ "{" while tagIter.%GetNext(.tagKey, .tagVal) { s str = str _ tagKey _ "=" _ """" _ tagVal _ """" _ ","}s str = $e(str, 1, * - 1) _ "}" }w str _" "_ json.val _ $c(10)}q $$$OK
}
- 配置路由
XData UrlMap [ XMLNamespace = "http://www.intersystems.com/urlmap" ]
{
<Routes><Route Url="/prometheus" Method="GET" Call="Metrics2Prometheus" /><Route Url="/json" Method="GET" Call="Metrics2Json" />
</Routes>
}
- 在界面输入自定义配置的
Prometheus接口地址。
prometheus格式地址接口:http://localhost:57772/api/metrics/prometheus

Json格式地址接口:http://localhost:57772/api/metrics/json

- 在
prometheus.yml配置文件中添加自定义的接口。代码如下:
- job_name: "localhost-Cache"metrics_path: /api/metrics/prometheusstatic_configs:- targets: ['localhost:57772']
- 重新启动
prometheus,查看配置界面。这样我们就可以给Caché配置一个自定义的接口拉。

配置 Exporter 监控服务器系统资源
- 通过服务器配置
Exporter监控服务器的系统信息,例如内存使用情况,CPU使用情况。
简介
为
Prometheus提供监控数据源的应用都可以被成为Exporter,比如Node Exporter则用来提供节点相关的资源使用状况,而Prometheus从这些不同的Exporter中获取监控数据,然后可以在诸如Grafana这样的可视化工具中进行结果的显示。广义上讲所有可以向
Prometheus提供监控样本数据的程序都可以被称为一个Exporter。而Exporter的一个实例称为target,如下所示,Prometheus通过轮询的方式定期从这些target中获取样本数据。

配置流程
- 下载
windows_exporter-0.21.0-amd64.msi文件后,直接双击运行;
- 下载地址:https://github.com/prometheus-community/windows_exporter/releases

windows_exporter默认端口号为9182,运行后浏览器访问地址, 出现以下信息,说明安装成功。
- 地址:http://localhost:9182/metrics

- 将
Exporter接口添加到Prometheus中。
- job_name: "localhost-Exporter"metrics_path: /metricsstatic_configs:- targets: ['localhost:9182']

使用 Alertmanager报警
简介
Alertmanager是一个独立的告警模块,接收Prometheus等客户端发来的警报,之后通过分组、删除重复等处理,并将它们通过路由发送给正确的接收器。
Prometheus 的报警分为两部分:
Prometheus服务器中的警报规则向警报管理器(Alertmanager)发送警报。- 警报管理器负责管理这些警报,包括告警信息进行去重,降噪,分组等,并通过丰富的告警通知渠道,如电子邮件、微信、钉钉、
Slack等常用沟通工具发出通知。
配置流程
- 下载
Prometheus,选择操作系统,下载对应安装包,下载完毕后并挤压,以windows为例。
- 下载地址:https://github.com/prometheus/alertmanager/releases

- 双击
alertmanager.exe,启动alertmanager服务,浏览器访问端口地址可以看到默认提供的UI页面。
- 端口 -
9093 - 地址 -
http://localhost:9093/#/alerts

AlertManager默认配置文件为alertmanager.yml,打开配置文件,配置使用Email方式通知报警信息,这里以QQ邮箱为例,配置信息如下:
global- 全局配置,包括报警解决后的超时时间、SMTP相关配置、各种渠道通知的API地址等等。route- 用来设置报警的分发策略,它是一个树状结构,按照深度优先从左向右的顺序进行匹配。receivers- 配置告警消息接受者信息,例如常用的email、wechat、slack、webhook等消息通知方式。inhibit_rules- 抑制规则配置,当存在与另一组匹配的警报时,抑制规则将禁用与一组匹配的警报(目标)。
global:resolve_timeout: 5msmtp_from: 'xxxx@qq.com'smtp_smarthost: 'smtp.qq.com:465'smtp_auth_username: 'xxxx@qq.com'smtp_auth_password: 'xxxxxxxxxxxxxx'smtp_require_tls: falsesmtp_hello: 'qq.com'route:group_by: ['alertname']group_wait: 5sgroup_interval: 5srepeat_interval: 5mreceiver: 'email'
receivers:
- name: 'email'email_configs:- to: 'xxxxx@mediway.cn'send_resolved: trueinhibit_rules:- source_match:severity: 'critical'target_match:severity: 'warning'equal: ['alertname', 'dev', 'instance']
- 接下来,需要在
Prometheus配置AlertManager服务地址以及告警规则,新建报警规则文件alert_rules.yml,,放在prometheus.yml同级路径下,配置如下:
name- 规则名称,可自己定义。alert- 报警名称,当触发报警时,会作为邮件标题显示。expr- 报警规则,为PromQL表达式验证特定节点,如上述配置中up{job="250-IRIS"}为验证job="250-IRIS"是否还活着,如果等于0则启动报警。for- 表示报警状态为Pending后等待15s变成Firing状态,一旦变成Firing状态则将报警发送到AlertManager。summary- 报警信息描述。
groups:- name: "服务器报警"rules:- alert: yx服务器报警expr: up{job="250-IRIS"} == 0for: 15slabels:status: warningannotations:summary: "服务器{{ $labels.instance }} 挂了"description: "姚鑫服务器挂了.请立即查看!"- alert: 本地Cache报警expr: up{job="localhost-Cache"} == 0for: 15slabels:status: warningannotations:summary: "服务器{{ $labels.instance }} 挂了"description: "本地Cache服务器挂了.请立即查看!"- alert: 本地IRIS报警expr: up{job="localhost-IRIS"} == 0for: 15slabels:status: warningannotations:summary: "服务器{{ $labels.instance }} 挂了"description: "本地IRIS服务器挂了.请立即查看!"- name: test-rulesrules:- alert: "内存报警"expr: 100 - ((node_memory_MemAvailable_bytes * 100) / node_memory_MemTotal_bytes) > 3for: 15slabels:status: warningannotations:summary: "服务器:{{$labels.instance}}内存使用率超过3%了"description: "内存使用率: {{ $value }}"value: "{{ $value }}"
- 配置好报警规则文件后,需要修改
prometheus.yml配置文件,添加rules规则文件:
alerting:alertmanagers:- static_configs:- targets:- 'localhost:9093'# - alertmanager:9093# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:- "alert_rules.yml"# - "second_rules.yml"
- 配置完毕后打,浏览器访问
prometheus主页,进入Alerts和Rules界面,可查看报警规则信息:
- 地址:http://localhost:9090/alerts?search=

- 测试报警功能,将本地
Caché关闭,停库。

- 观察页面本地
Cache警报状态。
Prometheus Alert 告警状态有三种:Inactive、Pending、Firing。
Inactive- 非活动状态,表示正在监控,但是还未有任何警报触发。Pending- 表示这个警报将被触发。由于警报可以被分组、压抑/抑制或静默/静音,所以等待验证,一旦所有的验证都通过,则将转到Firing状态。Firing- 将警报发送到AlertManager,将按照配置将警报的发送给所有接收者。一旦警报解除,则将状态转到Inactive,如此循环。
首先发现警报变为Pending状态。

过了15秒后变为Firing状态

- 触发报警,根据定义的报警规则,如果有触发告警的条件,自动向配置的接收邮箱中发送邮件,如下:


基于M实现监控服务器,并用邮件报警
解析Prometheus数据接口信息
- 获取接口信息
ClassMethod GetMetrics(url) As %Status
{#; 初始化request对象#dim request as %Net.HttpRequest= ##class(%Net.HttpRequest).%New()#; 根据地址解析是否是https调用SSL配置d ##class(%Net.URLParser).Parse(url, .components)s scheme = components("scheme")if (scheme = "https") {s request.Https = 1if ($zv [ "IRIS") {s request.SSLConfiguration = "ISC.FeatureTracker.SSL.Config"} else {s request.SSLConfiguration = "WebTerminalSSL"} s request.SSLCheckServerIdentity = 0 }#; 请求地址并返回流数据s sc = request.Get(url)$$$ThrowOnError(sc)s response = request.HttpResponses stream = response.Dataq stream
}
- 将接口信息转为
Json格式
ClassMethod Metrics2Json(stream) As %Status
{s array = []while (stream.AtEnd = 0) {#; 读取每行信息s metrics = stream.ReadLine()#; 过滤注释描述信息continue:(metrics [ "# HELP")continue:(metrics [ "# TYPE")#; 指标字符串s str = $p(metrics, " " ,1)#; 指标值s value = $p(metrics, " " ,2)#; 指标字名称s metricsName = $p(str, "{", 1)#; 指标字label信息s label = $e($p(str, "{", 2), 1, * - 1)s obj = {}s obj.key = metricsNames obj.val = value#; 如果label不为空将label信息转为JSONif (label '= "") {s tag = {}s len = $l(label, ",")for i = 1 : 1 : len{s item = $p(label, """," ,i)s key = $p(item, "=", 1) s val = $e($p(item, "=", 2), 2, *-1)d tag.%Set(key, val)}s obj.tag = tag}d array.%Push(obj)}q array
}
- 调用方法。
/// w ##class(M.ParseMetrics).Main("https://localhost:2443/api/monitor/metrics").%ToJSON()
/// w ##class(M.ParseMetrics).Main("http://localhost:9182/metrics").%ToJSON()
/// w ##class(M.ParseMetrics).Main("http://localhost:57772/api/metrics/prometheus").%ToJSON()
ClassMethod Main(url) As %Status
{#; 获取指标接口信息s stream = ..GetMetrics(url)#; 将接口信息转为JSON格式s json = ..Metrics2Json(stream)q json
}
- 调用自定义
Caché接口
IMP>w ##class(M.ParseMetrics).Main("http://localhost:57772/api/metrics/prometheus").%ToJSON() [{"key":"yx_application_errors","val":"0"},{"key":"yx_csp_sessions","val":"1"},{"key":"yx_cache_efficiency","val":"235.85"},{"key":"yx_disk_reads","val":"3316"},{"key":"yx_disk_writes","val":"1881"},{"key":"yx_ecp_app_srv_rate","val":"0"},{"key":"yx_ecp_data_srv_rate","val":"0"},{"key":"yx_glo_refs","val":"1225714"},{"key":"yx_glo_refs_per_sec","val":"439.00"},{"key":"yx_glo_sets","val":"108449"},{"key":"yx_journal_entries","val":"17178"},
...
{"key":"yx_database_free","val":".8988","tag":{"id":"DHC-EPRMETADATA"}},{"key":"yx_database_free","val":".45","tag":{"id":"DHC-EPRQUALITYDATA"}},{"key":"yx_database_free","val":".71","tag":{"id":"DHC-EPRRBACINST"}},{"key":"yx_database_free","val":".74","tag":{"id":"DHC-EPRRBACMETA"}},{"key":"yx_database_free","val":".78","tag":{"id":"DHC-HEIS"}},{"key":"yx_database_free","val":".48","tag":{"id":"DHC-HL7"}},{"key":"yx_database_free","val":".9936","tag":{"id":"DHC-HR"}},{"key":"yx_database_free","val":".0345","tag":{"id":"DHC-LISDATA"}},{"key":"yx_database_free","val":".9328","tag":{"id":"DHC-LISSRC"}},{"key":"yx_database_free","val":".9884","tag":{"id":"DHC-LOGS"}},{"key":"yx_database_free","val":".9098","tag":{"id":"DHC-MEDSRC"}},{"key":"yx_database_free","val":".8996","tag":{"id":"DHC-MRQDATA"}},{"key":"yx_database_free","val":".9871","tag":{"id":"DHC-MRQSRC"}},{"key":"yx_database_free","val":".1","tag":{"id":"DHC-MSG"}},{"key":"yx_database_free","val":".0481","tag":{"id":"DHC-ORDDATA"}},{"key":"yx_database_free","val":".0417","tag":{"id":"DHC-ORDINDEX"}},{"key":"yx_database_free","val":".8782","tag":{"id":"PACS"}},{"key":"yx_database_free","val":".6","tag":{"id":"DHC-PISDATA"}},{"key":"yx_database_free","val":".831","tag":{"id":"DHC-PISSRC"}},{"key":"yx_database_free","val":".9548","tag":{"id":"DHC-RIS"}},{"key":"yx_database_free","val":".9615","tag":{"id":"DHC-SYS"}},{"key":"yx_database_free","val":".9218","tag":{"id":"DHC-TEMP"}},{"key":"yx_database_free","val":".8096","tag":{"id":"DHC-DWR"}},{"key":"yx_database_free","val":".039","tag":{"id":"DHC-WORKLOAD"}},{"key":"yx_database_free","val":".1797","tag":{"id":"IMP"}}]
- 调用自定义
IRIS接口
IMP>w ##class(M.ParseMetrics).Main("https://localhost:2443/api/monitor/metrics").%ToJSON() [{"key":"iris_cpu_pct","val":"0","tag":{"id":"CSPSRV"}},{"key":"iris_cpu_pct","val":"0","tag":{"id":"ECPWorker"}},{"key":"iris_cpu_pct","val":"0","tag":{"id":"GARCOL"}},{"key":"iris_cpu_pct","val":"0","tag":{"id":"JRNDMN"}},
...
{"key":"iris_wqm_max_work_queue_depth","val":"0","tag":{"id":"SYS"}},{"key":"iris_wqm_waiting_worker_jobs","val":"1","tag":{"id":"SYS"}}]
- 调用自定义
Exporter接口
IMP>w ##class(M.ParseMetrics).Main("http://localhost:9182/metrics").%ToJSON()
[{"key":"go_gc_duration_seconds","val":"0","tag":{"quantile":"0"}},{"key":"go_gc_duration_seconds","val":"0","tag":{"quantile":"0.25"}},{"key":"go_gc_duration_seconds","val":"0","tag":{"quantile":"0.5"}},{"key":"go_gc_duration_seconds","val":"0","tag":{"quantile":"0.75"}},{"key":"go_gc_duration_seconds","val":"0.0023476","tag":{"quantile":"1"}},{"key":"go_gc_duration_seconds_sum","val":"0.0038789"},{"key":"go_gc_duration_seconds_count","val":"87"},{"key":"go_goroutines","val":"14"},{"key":"go_info","val":"1","tag":
...
{"key":"go_threads","val":"19"},{"key":"process_cpu_seconds_total","val":"2.015625"},{"key":"process_max_fds","val":"1.6777216e+07"},{"key":"process_open_fds","val":"414"},{"key":"process_resident_memory_bytes","val":"3.5504128e+07"},{"key":"process_start_time_seconds","val":"1.67670656e+09"},{"key":"process_virtual_memory_bytes","val":"4.0259584e+07"},]
使用嵌入式Python方式获取系统CPU、内存等
python模块之psutil详解
在讲嵌入式Python之前我们先了解一下psutil库
psutil是一个开源切跨平台的库,其提供了便利的函数用来获取才做系统的信息,比如CPU,内存,磁盘,网络等。此外,psutil还可以用来进行进程管理,包括判断进程是否存在、获取进程列表、获取进程详细信息等。而且psutil还提供了许多命令行工具提供的功能,包括:ps,top,lsof,netstat,ifconfig,who,df,kill,free,nice,ionice,iostat,iotop,uptime,pidof,tty,taskset,pmap。
psutil是一个跨平台的库,在官方网站上查到其支持如下操作系统。
LinuxWindowsOSXFreeBSDOpenBSDNetBSDSun SolarisAIX
CPU相关
| 函数 | 描述 |
|---|---|
psutil.cpu_count() | cpu_count(,[logical]):默认返回逻辑CPU的个数,当设置logical的参数为False时,返回物理CPU的个数。 |
psutil.cpu_percent() | cpu_percent(,[percpu],[interval]):返回CPU的利用率,percpu为True时显示所有物理核心的利用率,interval不为0时,则阻塞时显示interval执行的时间内的平均利用率。 |
psutil.cpu_times() | cpu_times(,[percpu]):以命名元组(namedtuple)的形式返回cpu的时间花费,percpu=True表示获取每个CPU的时间花费。 |
psutil.cpu_times_percent() | cpu_times_percent(,[percpu]):功能和cpu_times大致相同,看字面意思就能知道,该函数返回的是耗时比例。 |
psutil.cpu_stats() | cpu_stats()以命名元组的形式返回CPU的统计信息,包括上下文切换,中断,软中断和系统调用次数。 |
psutil.cpu_freq() | cpu_freq([percpu]):返回cpu频率。 |
Memory内存相关
| 函数 | 描述 |
|---|---|
virtual_memory() | 以命名元组的形式返回内存使用情况,包括总内存,可用内存,内存利用率,buffer和cache等。单位为字节。 |
swap_memory() | 以命名元组的形式返回swap/memory使用情况,包含swap中页的换入和换出。 |
Disk相关
| 函数 | 描述 |
|---|---|
psutil.disk_io_counters() | disk_io_counters([perdisk]):以命名元组的形式返回磁盘io统计信息,包括读、写的次数,读、写的字节数等。 当perdisk的值为True,则分别列出单个磁盘的统计信息(字典:key为磁盘名称,value为统计的namedtuple)。 |
psutil.disk_partitions() | disk_partitions([all=False]):以命名元组的形式返回所有已挂载的磁盘,包含磁盘名称,挂载点,文件系统类型等信息。 当all等于True时,返回包含/proc等特殊文件系统的挂载信息。 |
psutil.disk_usage() | disk_usage(path):以命名元组的形式返回path所在磁盘的使用情况,包括磁盘的容量、已经使用的磁盘容量、磁盘的空间利用率等。 |
Network相关
| 函数 | 详情 |
|---|---|
psutil.net_io_counter([pernic]) | 以命名元组的形式返回当前系统中每块网卡的网络io统计信息,包括收发字节数,收发包的数量、出错的情况和删包情况。当pernic为True时,则列出所有网卡的统计信息。 |
psutil.net_connections([kind]) | 以列表的形式返回每个网络连接的详细信息(namedtuple)。命名元组包含fd, family, type, laddr, raddr, status, pid等信息。kind表示过滤的连接类型,支持的值如下:(默认为inet)。 |
psutil.net_if_addrs() | 以字典的形式返回网卡的配置信息,包括IP地址和mac地址、子网掩码和广播地址。 |
psutil.net_if_stats() | 返回网卡的详细信息,包括是否启动、通信类型、传输速度与mtu。 |
psutil.users() | 以命名元组的方式返回当前登陆用户的信息,包括用户名,登陆时间,终端,与主机信息。 |
psutil.boot_time() | 以时间戳的形式返回系统的启动时间。 |
Process相关
| 函数 | 详情 |
|---|---|
psutil.pids() | 以列表的形式返回当前正在运行的进程。 |
psutil.pid_exists(1) | 判断给点定的pid是否存在。 |
psutil.process_iter() | 迭代当前正在运行的进程,返回的是每个进程的Process对象。 |
psutil.Process() | 对进程进行封装,可以使用该类的方法获取进行的详细信息,或者给进程发送信号。 |
通过嵌入式Python调用psutil库
这里仅简单介绍个主要方法、其他获取系统资源方法由读者自行实现。
- 在安装路径
C:\InterSystems\IRISHealth\bin,输入cmd,进入命令行控制台输入命令。安装psutil库。
irispip install --target C:\InterSystems\IRISHealth\mgr\python psutil

- 获取服务器
CPU使用率
ClassMethod GetSystemCpu() As %String [ Language = python ]
{from psutil.__init__ import cpu_percent,virtual_memoryreturn cpu_percent(interval=2)
}
USER> w ##class(M.SystemMetrics).GetSystemCpu()
14.59999999999999964

根据上图可以观察到获取的内容使用 1 - 是 IRIS监控接口、2 - 是M方法实现、 3 - 是任务管理器,三者监控的CPU使用率基本使用是一致的。(因为CPU使用率是瞬态的,所以可以认为是准确的)
- 获取服务系内存使用状态
ClassMethod GetSystemMemory() As %String [ Language = python ]
{from psutil.__init__ import cpu_percent,virtual_memoryreturn virtual_memory()[2]
}
USER>w ##class(M.SystemMetrics).GetSystemMemory()
60

根据上图可以观察到获取的内容使用 1 - 是 360监控、2 - 是任务管理器、 3 - 是M方法实现,三者监控的内存使用是一致的。
使用邮件发送报警信息
这里考虑使用邮件来发送警报信息是因为:
- 邮件相对其他方式来说比较方便,只管发送,不用管是否接收。
IRIS自带发送邮件接口,操作性大。- 相比其他方式,例如微信,公众号等,还需要做接口交互。使用邮件相对简单。
那么下一个考虑的问题是通过什么方式来触发监控发邮件呢,笔者考虑以下两点可以触发:
- 通过挂任务的方式定时轮询。
- 通过
SessionEvent事件来触发。
这里仅提供代码示例,具体使用哪种方式由读者自行决定。
使用M发送邮件发送消息
- 这里以
QQ邮箱例,配置信息与Alertmanager邮箱配置相同
ClassMethod SetMailConfig() As %Net.SMTP
{#; 配置smtp服务s server = ##class(%Net.SMTP).%New()s server.smtpserver = "smtp.qq.com"s server.port = 465if ($zv [ "IRIS") {s server.SSLConfiguration = "ISC.FeatureTracker.SSL.Config"} else {s server.SSLConfiguration = "WebTerminalSSL"} #; 配置邮箱s auth = ##class(%Net.Authenticator).%New()s auth.UserName = "454115408@qq.com"s auth.Password = "xxxxxxxxxxxxxxxx"s server.authenticator = auths server.AuthFrom = auth.UserNameq server
}
- 发送邮件具体方法
ClassMethod SendMailMessage(title, content) As %List
{#; 获取配置s server = ..SetMailConfig()#; 初始化邮件对象s msg = ##class(%Net.MailMessage).%New()#; 添加from - 发送人,to - 接收人,Cc - 抄送人s from = server.authenticator.UserNames msg.From = fromd msg.To.Insert("yaoxin@mediway.cn")d msg.Cc.Insert("965274651@qq.com")#; 填写邮件具体内容s msg.Subject = titles msg.IsBinary = 0s msg.IsHTML = 0d msg.TextData.Write(content)#; 执行发送s sc = server.Send(msg)$$$ThrowOnError(sc)q server.FailedSend
}
- 测试邮件方法,发送成功目标邮箱会接收到邮件。
IMP>w ##class(M.Mail).SendMailMessage("yx发的邮件消息头","这是yx发的消息内容content") 10@%Collection.ListOfDT

通过配置文件csv文件,来获取维护指标,进行邮件报警。
- 使用
csv文件通过手动的方式来维护需要监控的指标key- 指标名称,这里要与IRIS接口信息的具体指标保持一致。val- 报警的阈值。oper- 操作符。指具体报警时操作符。是大于val时报警,还是小于val时报警,或其他。desc- 指标描述信息。

- 读取
csv文件将数据转为Json。
ClassMethod Csv2Json(filename)
{q:('##class(%File).Exists(filename)) $$$NOs stream = ##class(%Stream.FileCharacter).%New()s stream.Filename = filenames array = []s keyStr = ""while 'stream.AtEnd {s str = stream.ReadLine()if ($i(count) = 1) {s keyStr = str}s obj = {} s len = $l(str, ",")for i = 1 : 1 : len {s val = $p(str, ",", i)s key = $p(keyStr, ",", i)d obj.%Set(key, val)}d array.%Push(obj)}q array
}
IMP>w ##class(M.Mail).LoadFile("E:\temp\metrics.csv").%ToJSON()
[{"key":"iris_cpu_usage ","val":"10","oper":">"},{"key":"iris_license_available","val":"290","oper":">"},{"key":"iris_trans_open_count","val":"0","oper":">"},{"key":"memory","val":"40","oper":">"}]
- 对比维护指标与接口指标,超过阈值发送邮件进行警报。
注:这里双循环,时间复杂O2,数据量大,效率可能会比较低,需要注意下。
ClassMethod Main(url As %String, filename As %String) As %Status
{#; 获取接口指标Jsons irismetrics = ##class(M.ParseMetrics).Main(url)#; 获取Csv维护指标Jsons config = ##class(M.Mail).Csv2Json(filename)#; 双向对比,维护指标超过阈值发送邮件警报for i = 1 : 1 : irismetrics.%Size() - 1{s metrics = irismetrics.%Get(i)for j = 1 : 1 : config.%Size() - 1{s obj = config.%Get(j)if (obj.key = metrics.key) {if @(metrics.val _ obj.oper _ obj.val) {s content = obj.desc _ "警报,超过阈值:" _ obj.vald ..SendMailMessage("来自系统的警报",content)ret $$$NO} }}}#; 监控内存使用率,接口中没有监视内存选项所以调用嵌入式接口s memory = ..GetSystemMemory()if (memory > 30) {d ..SendMailMessage("来自系统的警报","内存使用率已经大于" _ 30 _ "")}ret $$$YES
}
USER>w ##class(M.Mail).Main("https://localhost:2443/api/monitor/metrics", "E:\temp\metrics.csv")
0

注:这里基本思路已经实现,可以通过挂任务或其他方式来定时轮询。
总结
以上是个人关于监控服务器指标、CPU、内存、警报的一些理解,由于个人能力有限,欢迎大家提出意见,共同交流。
思考
IRIS还提供了alert警报接口,该接口提供alert.log警报日志内容,基于以上方案,思考如何通过M程序进行监控
- 接口地址:https://localhost:2443/api/monitor/alerts
- 该接口有警报时会提示报警信息,调用一次后清空。

- 如果有警报,警报信息如下:
{"time":"02/19/23-15:49:22:650","severity":2,"message":"Previous system shutdown was abnormal, system forced down or crashed."}
附
/api/monitor/metrics指标含义列表:
| 指标 | 描述 |
|---|---|
iris_cpu_pct {id="ProcessType"} | IRIS 进程类型的 CPU 使用百分比。 ProcessType 可以是以下任何一项:ECPWorker、ECPCliR、ECPCliW、ECPSrvR、ECPSrvW、LICENSESRV、WDAUX、WRTDMN、JRNDMN、GARCOL、CSPDMN、CSPSRV、ODBCSRC、MirrorMaster、MirrorPri、MirrorBack、MirrorPre、MirrorSvrR、MirrorJrnR、MirrorSK、MirrorComm |
iris_cpu_usage | 操作系统上所有程序的 CPU 使用率百分比 |
iris_csp_activity {id="IPaddress:port"} | Web 网关服务器自启动以来处理的 Web 请求数 |
iris_csp_actual_connections {id="IPAddress:port"} | Web 网关服务器与该服务器的当前连接数 |
iris_csp_gateway_latency {id="IPaddress:port"} | 获取 iris_csp_ 指标时从 Web 网关服务器获得响应的时间,以毫秒为单位 |
iris_csp_in_use_connections {id="IPaddress:port"} | 正在处理 Web 请求的 Web 网关服务器与此服务器的当前连接数 |
iris_csp_private_connections {id="IPaddress:port"} | 为状态感知应用程序保留的 Web 网关服务器与此服务器的当前连接数(保留模式 1) |
iris_csp_sessions | 此服务器上当前活动的 Web 会话 ID 数 |
iris_cache_efficiency | 全局引用与物理读写的比率,以百分比表示 |
iris_db_expansion_size_mb {id="database"} | 扩展数据库的数量,以兆字节为单位 |
iris_db_free_space {id="database"} | 数据库中可用的可用空间,以兆字节为单位(此指标每天仅更新一次,可能不会反映最近的更改。) |
iris_db_latency {id="database"} | 完成从数据库随机读取的时间量,以毫秒为单位 |
iris_db_max_size_mb {id="database"} | `数据库可以增长到的最大大小,以兆字节为单位 |
iris_db_size_mb {id="database",dir="path"} | 数据库大小,以兆字节为单位 |
iris_directory_space {id="database",dir="path"} | 数据库目录存储卷上的可用空间,以兆字节为单位 |
iris_disk_percent_full {id="database",dir="path"} | 数据库目录存储卷上的空间百分比 |
iris_ecp_conn | 此 ECP 应用程序服务器上的活动客户端连接总数 |
iris_ecp_conn_max | 来自此 ECP 应用程序服务器的最大活动客户端连接数 |
iris_ecp_connections | 此 ECP 应用程序服务器与其配置的 ECP 数据服务器同步时同步的服务器数 |
iris_ecp_latency | ECP 应用服务器和 ECP 数据服务器之间的延迟,以毫秒为单位 |
iris_ecps_conn | 每秒与此 ECP 数据服务器的活动客户端连接总数 |
iris_ecps_conn_max | 与此 ECP 数据服务器的最大活动客户端连接数 |
iris_glo_a_seize_per_sec | 每秒全局资源上的 Aseizes 数 |
iris_glo_n_seize_per_sec | 每秒全局资源上的 Nseizes 数 |
iris_glo_ref_per_sec | 每秒对位于本地数据库上的全局变量的引用数 |
iris_glo_ref_rem_per_sec | 每秒对位于远程数据库上的全局变量的引用数 |
iris_glo_seize_per_sec | 每秒占用全局资源的次数 |
iris_glo_update_per_sec | 每秒对本地数据库上的全局变量进行更新(SET 和 KILL 命令)的次数 |
iris_glo_update_rem_per_sec | 每秒对位于远程数据库上的全局变量的更新(SET 和 KILL 命令)数 |
iris_jrn_block_per_sec | 每秒写入磁盘的日志块 |
iris_jrn_free_space {id="JournalType",dir="path"} | 每个日志目录的存储卷上可用的可用空间,以兆字节为单位。 JournalType 可以是 WIJ、primary 或 secondary |
iris_jrn_size {id="JournalType"} | 每个日志文件的当前大小,以兆字节为单位。 JournalType 可以是 WIJ、primary 或 secondary |
iris_license_available | 当前未使用的许可证数量 |
iris_license_consumed | 当前使用的许可证数量 |
iris_license_percent_used | 当前使用的许可证的百分比 |
iris_log_reads_per_sec | 每秒逻辑读取 |
iris_obj_a_seize_per_sec | 每秒对象资源上的 Aseizes 数 |
iris_obj_del_per_sec | 每秒删除的对象数 |
iris_obj_hit_per_sec | 进程内存中每秒的对象引用数 |
iris_obj_load_per_sec | 每秒从磁盘加载的对象数,不在共享内存中 |
iris_obj_miss_per_sec | 每秒在内存中找不到的对象引用数 |
iris_obj_new_per_sec | 每秒初始化的对象数 |
iris_obj_seize_per_sec | 每秒占用对象资源的次数 |
iris_page_space_percent_used | 已用最大分配页面文件空间的百分比 |
iris_phys_mem_percent_used | 当前使用的物理内存 (RAM) 的百分比 |
iris_phys_reads_per_sec | 每秒从磁盘读取的物理数据库块 |
iris_phys_writes_per_sec | 每秒写入磁盘的物理数据库块 |
iris_process_count | 活跃的 IRIS 进程总数 |
iris_rtn_a_seize_per_sec | 每秒例程资源上的 Aseizes 数 |
iris_rtn_call_local_per_sec | 每秒对位于远程数据库上的全局变量的本地例程调用数 |
iris_rtn_call_miss_per_sec | 每秒在内存中找不到的例程调用数 |
iris_rtn_call_remote_per_sec | 每秒远程例程调用次数 |
iris_rtn_load_per_sec | 每秒从本地加载或保存到磁盘的例程数 |
iris_rtn_load_rem_per_sec | 每秒从磁盘远程加载或保存到磁盘的例程数 |
iris_rtn_seize_per_sec | 每秒占用例程资源的次数 |
iris_sam_get_db_sensors_seconds | 收集 iris_db* 传感器所花费的时间,以秒为单位 |
iris_sam_get_jrn_sensors_seconds | 收集 iris_jrn* 传感器所花费的时间,以秒为单位 |
iris_sam_get_sql_sensors_seconds | 收集 iris_sql* 传感器所花费的时间,以秒为单位 |
iris_sam_get_wqm_sensors_seconds | 收集 iris_wqm* 传感器所花费的时间,以秒为单位 |
iris_smh_available {id="purpose"} | 按目的可用的共享内存,以千字节为单位 |
iris_smh_percent_full {id="purpose"} | 按目的使用的已分配共享内存的百分比 |
iris_smh_total | 为当前实例分配的共享内存,以千字节为单位 |
iris_smh_total_percent_full | 当前实例使用的已分配共享内存的百分比 |
iris_smh_used {id="purpose"} | 按目的使用的共享内存,以千字节为单位 |
iris_sql_active_queries {id="namespace"} | 当前执行的 SQL语句数 |
iris_sql_active_queries_95_percentile {id="namespace"} | 对于当前的活动 SQL 语句集,自语句开始执行以来经过的第 95 个百分位数的时间 |
iris_sql_active_queries_99_percentile {id="namespace"} | 对于当前活动的 SQL 语句集,自语句开始执行以来经过的第 99 个百分位数的时间 |
iris_sql_queries_avg_runtime {id="namespace"} | 平均 SQL 语句运行时间,以秒为单位 |
iris_sql_queries_avg_runtime_std_dev {id="namespace"} | 平均 SQL 语句运行时间的标准偏差 |
iris_sql_queries_per_second {id="namespace"} | 每秒平均 SQL 语句数 |
iris_system_alerts | 自系统启动以来发布到消息日志的警报数 |
iris_system_alerts_log | 当前位于警报日志中的警报数 |
iris_system_alerts_new | /api/monitor/alerts 端点上是否有新警报,作为布尔值 |
iris_system_state | 表示系统监视器健康状态的数字 |
iris_trans_open_count | 当前实例上打开的事务数 |
iris_trans_open_secs | 当前实例上打开事务的平均持续时间,以秒为单位 |
iris_trans_open_secs_max | 当前实例上当前打开的最长事务的 |
iris_wd_buffer_redirty | 写入守护进程在最近一个周期中写入并且也在前一个周期中写入的数据库缓冲区数 |
iris_wd_buffer_write | 写入守护进程在其最近周期写入的数据库缓冲区数 |
iris_wd_cycle_time | 完成最近的写入守护进程周期所花费的时间量,以毫秒为单位 |
iris_wd_proc_in_global | 在最近的写入守护进程周期开始时主动持有全局缓冲区的进程数 |
iris_wd_size_write | 写入守护程序在其最近周期写入的数据库缓冲区的大小,以千字节为单位 |
iris_wd_sleep | 写入守护进程在其最近的周期开始之前处于非活动状态的时间量,以毫秒为单位 |
iris_wd_temp_queue | 写入守护进程在其最近周期开始时使用的内存缓冲区数 |
iris_wd_temp_write | 写入守护进程在其最近周期中写入的内存缓冲区数 |
iris_wdwij_time | 写入守护进程在其最近的周期内写入 WIJ 文件所花费的时间,以毫秒为单位 |
iris_wd_write_time | 写守护进程在其最近的周期内将缓冲区写入数据库所花费的时间,以毫秒为单位 |
iris_wij_writes_per_sec | WIJ 每秒物理块写入 |
iris_wqm_active_worker_jobs {id="category"} | 未被阻塞的运行逻辑的平均工作者作业数 |
iris_wqm_commands_per_sec {id="category"} | 平均每秒在此工作队列管理类别中执行的命令数 |
iris_wqm_globals_per_sec {id="category"} | 平均每秒在此工作队列管理类别中运行的全局引用数 |
iris_wqm_max_active_worker_jobs {id="category"} | 自上次记录日志条目以来的最大活动工作人员数 |
iris_wqm_max_work_queue_depth {id="category"} | 自上次记录以来此工作队列管理类别队列中的最大条目数 |
iris_wqm_waiting_worker_jobs {id="category"} | 等待一个组连接并为其工作的空闲工人作业的平均数量 |
