Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support monitoring memcached metrics and add a help doc #1423

Merged
merged 4 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;

/**
* @author dongfeng
*/
@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;

/**
* @author dongfeng
*/
@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
Loading