Skip to content

Commit

Permalink
support monitoring memcached metrics and add a help doc (#1423)
Browse files Browse the repository at this point in the history
Co-authored-by: 东风 <[email protected]>
  • Loading branch information
ZY945 and 东风 committed Dec 15, 2023
1 parent 3ff13aa commit af58e85
Show file tree
Hide file tree
Showing 7 changed files with 434 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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<String, String> 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<String> 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<String, String> 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<String, String> 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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ public interface DispatchConstants {
* protocol websocket
*/
String PROTOCOL_WEBSOCKET = "websocket";
/**
* protocol memcached
*/
String PROTOCOL_MEMCACHED = "memcached";
/**
* protocol udp
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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进行服务端口探测配置信息
Expand Down
Original file line number Diff line number Diff line change
@@ -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;


}
69 changes: 69 additions & 0 deletions home/docs/help/memcached.md
Original file line number Diff line number Diff line change
@@ -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:https://) |
| 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 |
Loading

0 comments on commit af58e85

Please sign in to comment.