MongoDB Javaクライアントと統合されたサービスのデバッグ情報を最適化し改善するために、ClusterListenerの使用例を探していました。MongoでClusterListenerをどのように利用できますか?
Replicationを使用してMongoクラスタセットを改善するために、これをどのように効果的に使うことができますか?
MongoDB Javaクライアントと統合されたサービスのデバッグ情報を最適化し改善するために、ClusterListenerの使用例を探していました。MongoでClusterListenerをどのように利用できますか?
Replicationを使用してMongoクラスタセットを改善するために、これをどのように効果的に使うことができますか?
TL; DRは
ClusterListener
インタフェースはさらにreplicaSetのいくつかの側面を監視するために使用することができますが、あなたがしたい場合は深く掘るために、および/またはあなたがのためのイベントの外にさらにreplicaSet状況を調べるためにしたい場合ClusterListener
がコールバックを提供する場合は、replSetGetStatus
コマンドを呼び出してその出力を検査することをお勧めします。
詳細
ClusterListener
あなたは/あなたの見てさらにreplicaSetへの変化に対応できるようにするコールバックを提供します。例えば、CLusterListener
...
public class LoggingClusterListener implements ClusterListener {
private static final Logger logger = LoggerFactory.getLogger(LoggingClusterListener.class);
@Override
public void clusterOpening(final ClusterOpeningEvent clusterOpeningEvent) {
logger.info("clusterOpening: {}", clusterOpeningEvent.getClusterId().getValue());
}
@Override
public void clusterClosed(final ClusterClosedEvent clusterClosedEvent) {
logger.info("clusterClosed: {}", clusterClosedEvent.getClusterId().getValue());
}
@Override
public void clusterDescriptionChanged(final ClusterDescriptionChangedEvent event) {
logger.info("clusterDescriptionChanged: {}", event.getClusterId().getValue());
for (ServerDescription sd : event.getNewDescription().getServerDescriptions()) {
logger.info("{}/{}/{}/{}", sd.getType(), sd.getCanonicalAddress(), sd.getState().name());
}
}
}
次...このようなMongoClient
に関連付けられているとき...
final MongoClientOptions options = MongoClientOptions.builder()
.addClusterListener(new LoggingClusterListener())
.build();
return new MongoClient(serverAddresses, options);
は...次のログが出力されます:
// cluster starting up ...
2017-08-17 12:49:55,977 [main] clusterOpening: 599582e36d47c231ec963b0b
2017-08-17 12:49:56,076 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] clusterDescriptionChanged: 599582e36d47c231ec963b0b
2017-08-17 12:49:56,076 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostB:27017] clusterDescriptionChanged: 599582e36d47c231ec963b0b
2017-08-17 12:49:56,076 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostC:27017] clusterDescriptionChanged: 599582e36d47c231ec963b0b
2017-08-17 12:49:56,076 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] clusterDescriptionChanged 599582e36d47c231ec963b0b
2017-08-17 12:49:56,076 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] REPLICA_SET_OTHER/hostB:27017/CONNECTED/{}
2017-08-17 12:49:56,077 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] REPLICA_SET_OTHER/hostC:27017/CONNECTED/{}
2017-08-17 12:49:56,077 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] REPLICA_SET_SECONDARY/hostA:27017/CONNECTED/{}
// ... the primary fails over to hostA:27017
2017-08-17 12:50:06,080 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] clusterDescriptionChanged: 599582e36d47c231ec963b0b
2017-08-17 12:50:06,080 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] REPLICA_SET_OTHER/hostB:27017/CONNECTED/{}
2017-08-17 12:50:06,080 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] REPLICA_SET_SECONDARY/hostC:27017/CONNECTED/{}
2017-08-17 12:50:06,080 [cluster-ClusterId{value='599582e36d47c231ec963b0b', description='null'}-hostA:27017] REPLICA_SET_PRIMARY/hostA:27017/CONNECTED/{}
2017-08-17 12:50:07,126 [main] clusterClosed: 599582e36d47c231ec963b0b
おそらくこれで十分ですが、そうでない場合は、たとえば次のいずれかが発生したときに応答するのではなく、レプリカセットの状態を積極的に監視したい場合...
を変更します。これは、replSetGetStatus
コマンドを実行して結果を問い合わせることで実行できます。このコマンドは、BsonDocument(形式はhereと記載されています)を返します。
ステータス文書のロギングは最も単純な応答ですが、そのアプローチは、文書の内容に基づいてアラートを発生させることによって、監視ソリューションの基礎を形成するように強化することができます。
次のコードは、レプリカセットの状態を読み取ります(レプリケーション・ラグの計算を含む)問合せを行い、出力をログに記録します。
MongoReplicaSetStatusLogger mongoReplicaSetStatusLogger = new MongoReplicaSetStatusLogger();
// periodically ...
MongoClient mongoClient = getMongoClient();
MongoDatabase admin = mongoClient.getDatabase("admin");
BsonDocument commandResult = admin.runCommand(new BsonDocument("replSetGetStatus", new BsonInt32(1)), BsonDocument.class);
mongoReplicaSetStatusLogger.report(commandResult);
はここMongoReplicaSetStatusLogger
実装の:
import org.bson.BsonDocument;
import org.bson.BsonInvalidOperationException;
import org.bson.BsonNumber;
import org.bson.BsonValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Optional;
public class MongoReplicaSetStatusLogger {
private static final Logger logger = LoggerFactory.getLogger(MongoReplicaSetStatusLogger.class);
private static final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss,SSSZ");
private static final String DEFAULT_VALUE = "UNKNOWN";
private static final String MEMBERS = "members";
public void report(BsonDocument replicasetStatusDocument) {
if (hasMembers(replicasetStatusDocument)) {
replicasetStatusDocument.getArray(MEMBERS).stream()
.filter(BsonValue::isDocument)
.map(memberDocument -> (BsonDocument) memberDocument)
.forEach(memberDocument -> logMemberDocument(memberDocument));
} else {
logger.warn("The replicaset status document does not contain a '{}' attributes, perhaps there has been " +
"a MongoDB upgrade and the format has changed!", MEMBERS);
}
}
private boolean hasMembers(BsonDocument replicasetStatusDocument) {
return replicasetStatusDocument.containsKey(MEMBERS) && replicasetStatusDocument.get(MEMBERS).isArray();
}
private void logMemberDocument(BsonDocument memberDocument) {
StringBuilder stringBuilder = new StringBuilder()
.append(logAttribute("node", getStringValue(memberDocument, "name")))
.append(logAttribute("health", getNumericValue(memberDocument, "health")))
.append(logAttribute("state", getStringValue(memberDocument, "stateStr")))
.append(logAttribute("uptime(s)", getNumericValue(memberDocument, "uptime")))
.append(logAttribute("lastOptime", getDateTimeValue(memberDocument, "optimeDate")))
.append(logAttribute("lastHeartbeat", getDateTimeValue(memberDocument, "lastHeartbeat")))
.append(logAttribute("lastHeartbeatRecv", getDateTimeValue(memberDocument, "lastHeartbeatRecv")))
.append(logAttribute("ping(ms)", getNumericValue(memberDocument, "pingMs")))
.append(logAttribute("replicationLag(s)", getReplicationLag(memberDocument)));
logger.error(stringBuilder.toString());
}
private String logAttribute(String key, Optional<String> value) {
return new StringBuilder(key).append("=").append(value.orElse(DEFAULT_VALUE)).append("|").toString();
}
private Optional<String> getStringValue(BsonDocument memberDocument, String key) {
if (memberDocument.containsKey(key)) {
try {
return Optional.of(memberDocument.getString(key).getValue().toUpperCase());
} catch (BsonInvalidOperationException e) {
logger.warn("Exception reading: {} from replicaset status document, message: {}.", key, e.getMessage());
}
}
return Optional.empty();
}
private Optional<String> getNumericValue(BsonDocument memberDocument, String key) {
if (memberDocument.containsKey(key)) {
BsonNumber bsonNumber = memberDocument.getNumber(key);
if (bsonNumber.isInt32()) {
return Optional.of(Integer.toString(bsonNumber.intValue()));
} else if (bsonNumber.isInt64()) {
return Optional.of(Long.toString(bsonNumber.longValue()));
} else if (bsonNumber.isDouble()) {
return Optional.of(Double.toString(bsonNumber.doubleValue()));
}
}
return Optional.empty();
}
private Optional<String> getDateTimeValue(BsonDocument memberDocument, String key) {
if (memberDocument.containsKey(key)) {
try {
return Optional.of(dateFormatter.format(new Date(memberDocument.getDateTime(key).getValue())));
} catch (BsonInvalidOperationException e) {
logger.warn("Exception reading: {} from replicaset status document due to: {}!", key, e.getMessage());
}
}
return Optional.empty();
}
private Optional<String> getReplicationLag(BsonDocument memberDocument) {
if (memberDocument.containsKey("optimeDate") && memberDocument.containsKey("lastHeartbeat")) {
try {
long optimeDate = memberDocument.getDateTime("optimeDate").getValue();
long lastHeartbeat = memberDocument.getDateTime("lastHeartbeat").getValue();
long replicationLag = lastHeartbeat - optimeDate;
return Optional.of(Long.toString(replicationLag));
} catch (BsonInvalidOperationException e) {
logger.warn("Exception reading 'optimeDate' or 'lastHeartbeat' from replicaset status document due to: {}!", e.getMessage());
} catch (IllegalArgumentException e) {
logger.warn("Exception calculating the replication lag due to: {}!", e.getMessage());
}
}
return Optional.empty();
}
}
は、ここでは、出力の例です:
2017-08-17 15:44:35,192|[main]|ERROR|MongoReplicaSetStatusLogger|node=hostA:27017|health=1.0|state=PRIMARY|uptime(s)=21|lastOptime=2017-08-17T15:43:32,000+0100|lastHeartbeat=UNKNOWN|lastHeartbeatRecv=UNKNOWN|ping(ms)=UNKNOWN|replicationLag(s)=UNKNOWN|
2017-08-17 15:44:35,193|[main]|ERROR|MongoReplicaSetStatusLogger|node=hostB:27017|health=1.0|state=SECONDARY|uptime(s)=17|lastOptime=2017-08-17T15:43:20,000+0100|lastHeartbeat=2017-08-17T15:43:35,443+0100|lastHeartbeatRecv=2017-08-17T15:43:36,412+0100|ping(ms)=0|replicationLag(s)=15443|
2017-08-17 15:44:35,193|[main]|ERROR|MongoReplicaSetStatusLogger|node=hostC:27017|health=1.0|state=SECONDARY|uptime(s)=17|lastOptime=2017-08-17T15:43:20,000+0100|lastHeartbeat=2017-08-17T15:43:35,444+0100|lastHeartbeatRecv=2017-08-17T15:43:36,470+0100|ping(ms)=0|replicationLag(s)=15444|
洞察力をいただき、ありがとうございます。 :) – nullpointer