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

mongodb monitoring support custom connection timeout param #1697

Merged
merged 2 commits into from
Mar 29, 2024
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
Expand Up @@ -17,49 +17,52 @@

package org.dromara.hertzbeat.collector.collect.mongodb;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Optional;

import com.mongodb.ConnectionString;
import com.mongodb.MongoClientSettings;
import com.mongodb.MongoServerUnavailableException;
import com.mongodb.MongoTimeoutException;
import com.mongodb.client.ClientSession;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.dromara.hertzbeat.collector.collect.AbstractCollect;
import org.dromara.hertzbeat.collector.collect.common.cache.CacheIdentifier;
import org.dromara.hertzbeat.collector.collect.common.cache.ConnectionCommonCache;
import org.dromara.hertzbeat.collector.collect.common.cache.MongodbConnect;
import org.dromara.hertzbeat.common.util.CommonUtil;
import org.dromara.hertzbeat.collector.dispatch.DispatchConstants;
import org.dromara.hertzbeat.common.constants.CommonConstants;
import org.dromara.hertzbeat.common.entity.job.Metrics;
import org.dromara.hertzbeat.common.entity.job.protocol.MongodbProtocol;
import org.dromara.hertzbeat.common.entity.message.CollectRep;
import org.dromara.hertzbeat.common.constants.CommonConstants;
import org.bson.Document;
import org.dromara.hertzbeat.common.util.CommonUtil;
import org.springframework.util.Assert;

import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;
import org.dromara.hertzbeat.collector.collect.AbstractCollect;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Optional;

import lombok.extern.slf4j.Slf4j;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

/**
* Mongodb single collect
*
*
* <p>
* <p>
* see also https://www.mongodb.com/languages/java,
* https://www.mongodb.com/docs/manual/reference/command/serverStatus/#metrics
*/
@Slf4j
public class MongodbSingleCollectImpl extends AbstractCollect {

/**
* 支持的 mongodb diagnostic 命令,排除internal/deprecated相关的命令
* 可参考 <a href="https://www.mongodb.com/docs/manual/reference/command/nav-diagnostic/">...</a>,
* mongodb diagnostic commands supported, excluding internal/deprecated related commands
* can refer to <a href="https://www.mongodb.com/docs/manual/reference/command/nav-diagnostic/">...</a>,
* <a href="https://www.mongodb.com/docs/mongodb-shell/run-commands/">...</a>
* 注意:一些命令需要相应的权限才能执行,否则执行虽然不会报错,但是返回的结果是空的,
* 详见 <a href="https://www.mongodb.com/docs/manual/reference/built-in-roles/">...</a>
* Note: Some commands require the corresponding permissions to execute,
* otherwise the execution will not report an error, but the return result is empty.
* seeDetails <a href="https://www.mongodb.com/docs/manual/reference/built-in-roles/">...</a>
*/
private static final String[] SUPPORTED_MONGODB_DIAGNOSTIC_COMMANDS = {
"buildInfo",
Expand Down Expand Up @@ -89,20 +92,24 @@ public void collect(CollectRep.MetricsData.Builder builder, long monitorId, Stri
builder.setMsg(e.getMessage());
return;
}
// command 命名规则约定为上述 mongodb diagnostic 支持的命令,同时也通过 . 支持子文档
// 如果 command 不包括 . 则直接执行命令,使用其返回的文档,否则需要先执行 metricsParts[0] 命令,再获取相关的子文档
// 例如 serverStatus.metrics.operation 支持 serverStatus 命令的 metrics 子文档下面的 operation 子文档
// The command naming convention is the command supported by the above mongodb diagnostic. Support subdocument
// If the command does not include., execute the command directly and use the document it returns;
// otherwise, you need to execute the metricsParts[0] command first and then obtain the related subdocument
// For example serverStatus. Metrics. Operation support serverStatus command the metrics of the following document operation documents
String[] metricsParts = metrics.getMongodb().getCommand().split("\\.");
String command = metricsParts[0];
// 检查 . 分割的第一部分是否是 mongodb diagnostic 支持的命令
// Check whether the first part of the. Split is a command supported by the mongodb diagnostic
if (Arrays.stream(SUPPORTED_MONGODB_DIAGNOSTIC_COMMANDS).noneMatch(command::equals)) {
builder.setCode(CollectRep.Code.FAIL);
builder.setMsg("unsupported mongodb diagnostic command: " + command);
return;
}
ClientSession clientSession = null;
MongoClient mongoClient;
CacheIdentifier identifier= null;
try {
MongoClient mongoClient = getClient(metrics);
identifier = getIdentifier(metrics.getMongodb());
mongoClient = getClient(metrics, identifier);
MongoDatabase mongoDatabase = mongoClient.getDatabase(metrics.getMongodb().getDatabase());
clientSession = mongoClient.startSession();
CollectRep.ValueRow.Builder valueRowBuilder = CollectRep.ValueRow.newBuilder();
Expand All @@ -116,11 +123,12 @@ public void collect(CollectRep.MetricsData.Builder builder, long monitorId, Stri
}
}
if (document == null) {
throw new RuntimeException("the document get from command " + metrics.getMongodb().getCommand() + " is null.");
throw new RuntimeException("the document get from command " + metrics.getMongodb().getCommand() + " is null.");
}
fillBuilder(metrics, valueRowBuilder, document);
builder.addValues(valueRowBuilder.build());
} catch (MongoServerUnavailableException | MongoTimeoutException unavailableException) {
ConnectionCommonCache.getInstance().removeCache(identifier);
builder.setCode(CollectRep.Code.UN_CONNECTABLE);
String message = CommonUtil.getMessageFromThrowable(unavailableException);
builder.setMsg(message);
Expand All @@ -144,7 +152,7 @@ public String supportProtocol() {
}

/**
* 使用 metrics 中配置的收集指标以及执行mongodb命令返回的文档,填充 valueRowBuilder
* Populate the valueRowBuilder with the collection metrics configured in Metrics and the documentation returned by executing the mongodb command
*/
private void fillBuilder(Metrics metrics, CollectRep.ValueRow.Builder valueRowBuilder, Document document) {
metrics.getAliasFields().forEach(it -> {
Expand All @@ -162,7 +170,7 @@ private void fillBuilder(Metrics metrics, CollectRep.ValueRow.Builder valueRowBu
}

/**
* 检查 metrics 中的 mongodb 连接信息是否完整
* Check that the mongodb connection information in metrics is complete
*/
private void preCheck(Metrics metrics) {
if (metrics == null || metrics.getMongodb() == null) {
Expand All @@ -176,17 +184,22 @@ private void preCheck(Metrics metrics) {
Assert.hasText(mongodbProtocol.getPassword(), "Mongodb Protocol password is required.");
}

public static CacheIdentifier getIdentifier(MongodbProtocol mongodbProtocol){
// try to reuse connection
return CacheIdentifier.builder()
.ip(mongodbProtocol.getHost()).port(mongodbProtocol.getPort())
.username(mongodbProtocol.getUsername()).password(mongodbProtocol.getPassword()).build();
}

/**
* 通过metrics中的mongodb连接信息获取
* mongodb client本身不存在网络调用,和网络链接。对每次采集,我们需要新建session并使用后关闭它
* Obtained from mongodb connection information in metrics
* The mongodb client itself does not have network calls and network links.
* For each collection, we need to create a new session and close it after use
* mongodb client is thread pool, we need to create the session for each collect
*/
private MongoClient getClient(Metrics metrics) {
private MongoClient getClient(Metrics metrics, CacheIdentifier identifier) {
MongodbProtocol mongodbProtocol = metrics.getMongodb();
// try to reuse connection
CacheIdentifier identifier = CacheIdentifier.builder()
.ip(mongodbProtocol.getHost()).port(mongodbProtocol.getPort())
.username(mongodbProtocol.getUsername()).password(mongodbProtocol.getPassword()).build();

Optional<Object> cacheOption = ConnectionCommonCache.getInstance().getCache(identifier, true);
MongoClient mongoClient = null;
if (cacheOption.isPresent()) {
Expand All @@ -196,12 +209,22 @@ private MongoClient getClient(Metrics metrics) {
if (mongoClient != null) {
return mongoClient;
}
// 复用失败则新建连接 connect to mongodb
// 密码可能包含特殊字符,需要使用类似js的encodeURIComponent进行编码,这里使用java的URLEncoder
// If the multiplexing fails, create a new connection to connect to mongodb
// Passwords may contain special characters and need to be encoded using JS-like encodeURIComponent, which uses java URLEncoder
String url = String.format("mongodb://%s:%s@%s:%s/%s?authSource=%s", mongodbProtocol.getUsername(),
URLEncoder.encode(mongodbProtocol.getPassword(), StandardCharsets.UTF_8), mongodbProtocol.getHost(), mongodbProtocol.getPort(),
mongodbProtocol.getDatabase(), mongodbProtocol.getAuthenticationDatabase());
mongoClient = MongoClients.create(url);
// Use the Mongo Client Settings builder to configure timeouts and other configurations
MongoClientSettings settings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(url))
.applyToClusterSettings(builder ->
// Set the timeout period for server selection
builder.serverSelectionTimeout(Long.parseLong(mongodbProtocol.getTimeout()), MILLISECONDS))
.build();

// CREATE THE MONGO CLIENT USING THE CONFIGURATION
mongoClient = MongoClients.create(settings);

MongodbConnect mongodbConnect = new MongodbConnect(mongoClient);
ConnectionCommonCache.getInstance().addCache(identifier, mongodbConnect, 3600 * 1000L);
return mongoClient;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,9 @@ public class MongodbProtocol {
*/
private String command;

/**
* TIME OUT PERIOD
*/
private String timeout;

}
27 changes: 27 additions & 0 deletions manager/src/main/resources/define/app-mongodb.yml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,24 @@ params:
limit: 20
required: true
defaultValue: 'admin'
- field: timeout
# name-param field display i18n name
# name-参数字段显示名称
name:
zh-CN: 连接超时时间(ms)
en-US: Connect Timeout(ms)
# 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,100000]'
# required-true or false
# 是否是必输项 true-必填 false-可选
required: true
# default value 6000
# 默认值 6000
defaultValue: 6000
# can get status information and other config params of MongoDB by running Diagnostic Commands
# 可以通过执行 Diagnostic Commands 获取 mongodb 的状态信息以及一些性能、配置参数
# https://www.mongodb.com/docs/manual/reference/command/nav-diagnostic/
Expand Down Expand Up @@ -182,6 +200,9 @@ metrics:
authenticationDatabase: ^_^authenticationDatabase^_^
#The buildInfo command is an administrative command which returns a build summary for the current mongod.
command: buildInfo
# timeout unit:ms
# 超时时间 单位:ms
timeout: ^_^timeout^_^

# https://www.mongodb.com/docs/manual/reference/command/serverStatus/#metrics
- name: server_document
Expand Down Expand Up @@ -219,6 +240,7 @@ metrics:
database: ^_^database^_^
authenticationDatabase: ^_^authenticationDatabase^_^
command: serverStatus.metrics.document
timeout: ^_^timeout^_^

- name: server_operation
priority: 1
Expand All @@ -245,6 +267,7 @@ metrics:
database: ^_^database^_^
authenticationDatabase: ^_^authenticationDatabase^_^
command: serverStatus.metrics.operation
timeout: ^_^timeout^_^

- name: server_ttl
priority: 1
Expand All @@ -271,6 +294,7 @@ metrics:
database: ^_^database^_^
authenticationDatabase: ^_^authenticationDatabase^_^
command: serverStatus.metrics.ttl
timeout: ^_^timeout^_^

# https://www.mongodb.com/docs/manual/reference/command/hostInfo/
- name: systemInfo
Expand Down Expand Up @@ -330,6 +354,7 @@ metrics:
database: ^_^database^_^
authenticationDatabase: ^_^authenticationDatabase^_^
command: hostInfo.system
timeout: ^_^timeout^_^

- name: os
priority: 1
Expand Down Expand Up @@ -361,6 +386,7 @@ metrics:
database: ^_^database^_^
authenticationDatabase: ^_^authenticationDatabase^_^
command: hostInfo.os
timeout: ^_^timeout^_^

- name: extra
priority: 1
Expand Down Expand Up @@ -417,3 +443,4 @@ metrics:
database: ^_^database^_^
authenticationDatabase: ^_^authenticationDatabase^_^
command: hostInfo.extra.extra
timeout: ^_^timeout^_^
Loading