diff --git a/collector/src/main/java/org/dromara/hertzbeat/collector/collect/memcached/MemcachedCollectImpl.java b/collector/src/main/java/org/dromara/hertzbeat/collector/collect/memcached/MemcachedCollectImpl.java new file mode 100644 index 00000000000..1772af67f97 --- /dev/null +++ b/collector/src/main/java/org/dromara/hertzbeat/collector/collect/memcached/MemcachedCollectImpl.java @@ -0,0 +1,145 @@ +package org.dromara.hertzbeat.collector.collect.memcached; + +import lombok.extern.slf4j.Slf4j; +import org.dromara.hertzbeat.collector.collect.AbstractCollect; +import org.dromara.hertzbeat.collector.dispatch.DispatchConstants; +import org.dromara.hertzbeat.common.constants.CollectorConstants; +import org.dromara.hertzbeat.common.constants.CommonConstants; +import org.dromara.hertzbeat.common.entity.job.Metrics; +import org.dromara.hertzbeat.common.entity.job.protocol.MemcachedProtocol; +import org.dromara.hertzbeat.common.entity.message.CollectRep; +import org.dromara.hertzbeat.common.util.CommonUtil; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * + */ +@Slf4j +public class MemcachedCollectImpl extends AbstractCollect { + public MemcachedCollectImpl() { + } + + private static final String STATS = "stats"; + private static final String STATS_SETTINGS = "stats settings"; + private static final String STATS_ITEMS = "stats items"; + private static final String STATS_SIZES = "stats sizes"; + private static final String STATS_END_RSP = "END"; + + @Override + public void collect(CollectRep.MetricsData.Builder builder, long monitorId, String app, Metrics metrics) { + long startTime = System.currentTimeMillis(); + if (metrics == null || metrics.getMemcached() == null) { + builder.setCode(CollectRep.Code.FAIL); + builder.setMsg("Memcached collect must has Memcached params"); + return; + } + MemcachedProtocol memcachedProtocol = metrics.getMemcached(); + String memcachedHost = memcachedProtocol.getHost(); + String memcachedPort = memcachedProtocol.getPort(); + Socket socket = null; + try { + socket = new Socket(); + SocketAddress socketAddress = new InetSocketAddress(memcachedHost, Integer.parseInt(memcachedPort)); + socket.connect(socketAddress); + if (socket.isConnected()) { + long responseTime = System.currentTimeMillis() - startTime; + PrintWriter out = new PrintWriter(socket.getOutputStream(), true); + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); + // 发送统计命令 + Map resultMap = new HashMap<>(128); + parseCMDResponse(resultMap, in, out, STATS); + parseCMDResponse(resultMap, in, out, STATS_SETTINGS); + parseSizesOutput(resultMap, in, out); + + resultMap.put(CollectorConstants.RESPONSE_TIME, Long.toString(responseTime)); + + // 关闭输出流和Socket连接 + in.close(); + out.close(); + socket.close(); + List aliasFields = metrics.getAliasFields(); + CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder(); + for (String field : aliasFields) { + String fieldValue = resultMap.get(field); + valueRowBuilder.addColumns(Objects.requireNonNullElse(fieldValue, CommonConstants.NULL_VALUE)); + } + builder.addValues(valueRowBuilder.build()); + } else { + builder.setCode(CollectRep.Code.UN_CONNECTABLE); + builder.setMsg("Peer connect failed:"); + } + } catch (UnknownHostException unknownHostException) { + String errorMsg = CommonUtil.getMessageFromThrowable(unknownHostException); + log.info(errorMsg); + builder.setCode(CollectRep.Code.UN_CONNECTABLE); + builder.setMsg("UnknownHost:" + errorMsg); + } catch (SocketTimeoutException socketTimeoutException) { + String errorMsg = CommonUtil.getMessageFromThrowable(socketTimeoutException); + log.info(errorMsg); + builder.setCode(CollectRep.Code.UN_CONNECTABLE); + builder.setMsg("Socket connect timeout: " + errorMsg); + } catch (IOException ioException) { + String errorMsg = CommonUtil.getMessageFromThrowable(ioException); + log.info(errorMsg); + builder.setCode(CollectRep.Code.UN_CONNECTABLE); + builder.setMsg("Connect fail:" + errorMsg); + } finally { + if (socket != null) { + try { + socket.close(); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + } + } + + private static void parseCMDResponse(Map statsMap, + BufferedReader in, + PrintWriter out, + String cmd) throws IOException { + out.println(cmd); + String line; + while ((line = in.readLine()) != null && !line.equals(STATS_END_RSP)) { + // 解析每一行,将键值对存入HashMap + String[] parts = line.split(" "); + if (parts.length == 3) { + statsMap.put(parts[1], parts[2]); + } + } + } + + private static void parseSizesOutput(Map statsMap, + BufferedReader in, + PrintWriter out) throws IOException { + out.println(STATS_SIZES); + String line; + while ((line = in.readLine()) != null && !line.equals(STATS_END_RSP)) { + String[] parts = line.split("\\s+"); + // 提取 slab size 和 slab count,并放入HashMap + if (parts.length >= 3 && "STAT".equals(parts[0])) { + statsMap.put("item_size", parts[1]); + statsMap.put("item_count", parts[2]); + } + } + } + + + @Override + public String supportProtocol() { + return DispatchConstants.PROTOCOL_MEMCACHED; + } +} diff --git a/collector/src/main/java/org/dromara/hertzbeat/collector/dispatch/DispatchConstants.java b/collector/src/main/java/org/dromara/hertzbeat/collector/dispatch/DispatchConstants.java index cbef92430ce..0e77f32a116 100644 --- a/collector/src/main/java/org/dromara/hertzbeat/collector/dispatch/DispatchConstants.java +++ b/collector/src/main/java/org/dromara/hertzbeat/collector/dispatch/DispatchConstants.java @@ -51,6 +51,10 @@ public interface DispatchConstants { * protocol websocket */ String PROTOCOL_WEBSOCKET = "websocket"; + /** + * protocol memcached + */ + String PROTOCOL_MEMCACHED = "memcached"; /** * protocol udp */ diff --git a/collector/src/main/resources/META-INF/services/org.dromara.hertzbeat.collector.collect.AbstractCollect b/collector/src/main/resources/META-INF/services/org.dromara.hertzbeat.collector.collect.AbstractCollect index 5167f7cdf54..796ca561afc 100644 --- a/collector/src/main/resources/META-INF/services/org.dromara.hertzbeat.collector.collect.AbstractCollect +++ b/collector/src/main/resources/META-INF/services/org.dromara.hertzbeat.collector.collect.AbstractCollect @@ -17,3 +17,4 @@ org.dromara.hertzbeat.collector.collect.udp.UdpCollectImpl org.dromara.hertzbeat.collector.collect.push.PushCollectImpl org.dromara.hertzbeat.collector.collect.dns.DnsCollectImpl org.dromara.hertzbeat.collector.collect.nginx.NginxCollectImpl +org.dromara.hertzbeat.collector.collect.memcached.MemcachedCollectImpl diff --git a/common/src/main/java/org/dromara/hertzbeat/common/entity/job/Metrics.java b/common/src/main/java/org/dromara/hertzbeat/common/entity/job/Metrics.java index 5969edd2d7c..e0def944e5d 100644 --- a/common/src/main/java/org/dromara/hertzbeat/common/entity/job/Metrics.java +++ b/common/src/main/java/org/dromara/hertzbeat/common/entity/job/Metrics.java @@ -130,6 +130,11 @@ public class Metrics { * 使用websocket的监控配置信息 */ private WebsocketProtocol websocket; + /** + * Monitoring configuration information using the memcached protocol + * 使用memcached的监控配置信息 + */ + private MemcachedProtocol memcached; /** * Use udp implemented by socket for service port detection configuration information * 使用socket实现的udp进行服务端口探测配置信息 diff --git a/common/src/main/java/org/dromara/hertzbeat/common/entity/job/protocol/MemcachedProtocol.java b/common/src/main/java/org/dromara/hertzbeat/common/entity/job/protocol/MemcachedProtocol.java new file mode 100644 index 00000000000..f6343530a5e --- /dev/null +++ b/common/src/main/java/org/dromara/hertzbeat/common/entity/job/protocol/MemcachedProtocol.java @@ -0,0 +1,28 @@ +package org.dromara.hertzbeat.common.entity.job.protocol; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class MemcachedProtocol { + + /** + * Memcached 主机ip或域名 + */ + private String host; + + /** + * Memcached 主机端口(默认11211) + */ + private String port; + + +} diff --git a/home/docs/help/memcached.md b/home/docs/help/memcached.md new file mode 100644 index 00000000000..5d89ce0977b --- /dev/null +++ b/home/docs/help/memcached.md @@ -0,0 +1,69 @@ +--- +id: memcached +title: Monitoring Memcached +sidebar_label: Memcached Monitor +keywords: [ open source monitoring tool, open source Memcached monitoring tool, monitoring memcached metrics ] +--- + +> Collect and monitor the general performance Metrics of Memcached. + +**Protocol Use:Memcached** + +```text +The default YML configuration for the memcache version is in compliance with 1.4.15. +You need to use the stats command to view the parameters that your memcache can monitor +``` + +### + +**1、Obtain usable parameter indicators through commands such as stats、stats setting、stats settings. + +```shell +# telnet ip port +[root@server ~]# telnet localhost 11211 +Trying ::1... +Connected to localhost. +Escape character is '^]'. +stats +STAT pid 15168 +STAT uptime 11691 +STAT time 1702569246 +STAT version 1.4.15 +... +``` + +**There is help_doc: https://www.runoob.com/memcached/memcached-stats.html** + +### Configuration parameter + +| Parameter name | Parameter help description | +|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Monitoring Host | Monitored IPV4, IPV6 or domain name. Note⚠️Without protocol header (eg: https://, http://) | +| Monitoring name | Identify the name of this monitoring. The name needs to be unique | +| Port | Port provided by Memcached | +| Collection interval | Interval time of monitor periodic data collection, unit: second, and the minimum interval that can be set is 30 seconds | +| Whether to detect | Whether to detect and check the availability of monitoring before adding monitoring. Adding and modifying operations will continue only after the detection is successful | +| Description remarks | For more information about identifying and describing this monitoring, users can note information here | + +### Collection Metrics + +#### Metrics Set:server_info + +| Metric name | Metric unit | Metric help description | +|------------------|-------------|---------------------------------------------------| +| pid | | Memcache server process ID | +| uptime | s | The number of seconds the server has been running | +| version | | Memcache version | +| curr_connections | | Current number of connections | +| auth_errors | | Number of authentication failures | +| threads | | Current number of threads | +| item_size | byte | The size of the item | +| item_count | | Number of items | +| curr_items | | The total number of data currently stored | +| total_items | | The total number of data stored since startup | +| bytes | byte | The current number of bytes occupied by storage | +| cmd_get | | Get command request count | +| cmd_set | | Set command request count | +| cmd_flush | | Flush command request count | +| get_misses | | Get command misses | +| delete_misses | | Delete command misses | \ No newline at end of file diff --git a/manager/src/main/resources/define/app-memcached.yml b/manager/src/main/resources/define/app-memcached.yml new file mode 100644 index 00000000000..5c65f4117e8 --- /dev/null +++ b/manager/src/main/resources/define/app-memcached.yml @@ -0,0 +1,182 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# The monitoring type category:service-application service monitoring db-database monitoring mid-middleware custom-custom monitoring os-operating system monitoring +# 监控类型所属类别:service-应用服务 program-应用程序 db-数据库 custom-自定义 os-操作系统 bigdata-大数据 mid-中间件 webserver-web服务器 cache-缓存 cn-云原生 network-网络监控等等 +category: cache +# The monitoring type eg: linux windows tomcat mysql aws... +# 监控类型 eg: linux windows tomcat mysql aws... +app: memcached +# 监控类型国际化名称 +name: + zh-CN: Memcached + en-US: Memcached +# The description and help of this monitoring type +# 监控类型的帮助描述信息 +help: + zh-CN: HertzBeat 支持 Memcached 服务监控,通过(stats、stats settings、stats sizes)命令对(服务器进程ID、服务器已运行秒数、版本、当前线程数、item的大小、item的数量、当前存储占用的字节数)等指标进行监测。 + en-US: HertzBeat supports Memcached service monitoring by using commands such as stats, stats settings, and stats sizes to monitor indicators such as server process ID, server running seconds, version, current number of threads, item size, number of items, and current byte storage usage. + zh-TW: HertzBeat支持Memcached服務監控,通過(stats、stats settings、stats sizes)命令對(服務器行程ID、服務器已運行秒數、版本、當前線程數、item的大小、item的數量、當前存儲佔用的位元組數)等名額進行監測。 +# 监控所需输入参数定义(根据定义渲染页面UI) +# Input params define for monitoring(render web ui by the definition) +params: + # field-param field key + # field-字段名称标识符 + - field: host + # name-param field display i18n name + # name-参数字段显示名称 + name: + zh-CN: Memcached服务的Host + en-US: Host of Memcached service + # type-param field type(most mapping the html input type) + # type-字段类型,样式(大部分映射input标签type属性) + type: host + # required-true or false + # 是否是必输项 true-必填 false-可选 + required: true + # field-param field key + # field-字段名称标识符 + - field: port + # name-param field display i18n name + # name-参数字段显示名称 + name: + zh-CN: 端口 + en-US: Port + # type-param field type(most mapping the html input type) + # type-字段类型,样式(大部分映射input标签type属性) + type: number + # when type is number, range is required + # 当type为number时,用range表示范围 + range: '[0,65535]' + # required-true or false + # 是否是必输项 true-必填 false-可选 + required: true + # default value + # 默认值 + defaultValue: 11211 +# collect metrics config list +# 采集指标配置列表 +metrics: + # metrics - summary + # 监控指标 - summary + - name: server_info + # metrics scheduling priority(0->127)->(high->low), metrics with the same priority will be scheduled in parallel + # priority 0's metrics is availability metrics, it will be scheduled first, only availability metrics collect success will the scheduling continue + # 指标采集调度优先级(0->127)->(优先级高->低) 优先级低的指标会等优先级高的指标采集完成后才会被调度, 相同优先级的指标会并行调度采集 + # 优先级为0的指标为可用性指标,即它会被首先调度,采集成功才会继续调度其它指标,采集失败则中断调度 + priority: 0 + # 指标信息 包括 field名称 type字段类型:0-number数字,1-string字符串 label-是否是指标标签字段 unit:指标单位 + # field-metric name, type-metric type(0-number,1-string), unit-metric unit('%','ms','MB'), label-whether it is a metrics label field + # field-指标名称, type-指标类型(0-number数字,1-string字符串), unit-指标单位('%','ms','MB'), label-是否是指标标签字段 + fields: + - field: responseTime + type: 0 + unit: ms + i18n: + zh-CN: 响应时间 + en-US: ResponseTime + - field: pid + type: 1 + i18n: + zh-CN: memcache服务器进程ID + en-US: pid + - field: uptime + type: 0 + unit: s + i18n: + zh-CN: 服务器已运行秒数 + en-US: uptime + - field: version + type: 1 + i18n: + zh-CN: memcache版本 + en-US: version + - field: curr_connections + type: 0 + i18n: + zh-CN: 当前连接数量 + en-US: curr_connections + - field: auth_errors + type: 0 + i18n: + zh-CN: 认证失败数目 + en-US: auth_errors + - field: threads + type: 0 + i18n: + zh-CN: 当前线程数 + en-US: threads + - field: item_size + type: 0 + unit: byte + i18n: + zh-CN: item的大小 + en-US: item_size + - field: item_count + type: 0 + i18n: + zh-CN: item的数量 + en-US: item_count + - field: curr_items + type: 0 + i18n: + zh-CN: 当前存储的数据总数 + en-US: curr_items + - field: total_items + type: 0 + i18n: + zh-CN: 启动以来存储的数据总数 + en-US: total_items + - field: bytes + type: 0 + unit: byte + i18n: + zh-CN: 当前存储占用的字节数 + en-US: bytes + - field: cmd_get + type: 0 + i18n: + zh-CN: get命令请求次数 + en-US: cmd_get + - field: cmd_set + type: 0 + i18n: + zh-CN: set命令请求次数 + en-US: cmd_set + - field: cmd_flush + type: 0 + i18n: + zh-CN: flush命令请求次数 + en-US: cmd_flush + - field: get_misses + type: 0 + i18n: + zh-CN: get命令未命中次数 + en-US: get_misses + - field: delete_misses + type: 0 + i18n: + zh-CN: delete命令未命中次数 + en-US: delete_misses + # the protocol used for monitoring, eg: sql, ssh, http, telnet, wmi, snmp, sdk + # 采集协议, 目前支持sql, ssh, http, telnet, wmi, snmp, sdk + protocol: memcached + # Specific collection configuration when protocol is telnet protocol + # 当protocol为telnet协议时具体的采集配置 + memcached: + # telnet host + # 远程登录主机 + host: ^_^host^_^ + port: ^_^port^_^