org.redisson.connection.DNSMonitor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redisson-all Show documentation
Show all versions of redisson-all Show documentation
Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC
/**
* Copyright (c) 2013-2024 Nikita Koksharov
*
* Licensed 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.
*/
package org.redisson.connection;
import io.netty.resolver.AddressResolver;
import io.netty.resolver.AddressResolverGroup;
import io.netty.util.NetUtil;
import io.netty.util.Timeout;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import org.redisson.client.RedisClient;
import org.redisson.misc.RedisURI;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
/**
* DNS changes monitor.
*
* @author Nikita Koksharov
*
*/
public class DNSMonitor {
private static final Logger log = LoggerFactory.getLogger(DNSMonitor.class);
private final AddressResolver resolver;
private final ConnectionManager connectionManager;
private final Map masters = new HashMap<>();
private final Map slaves = new HashMap<>();
private volatile Timeout dnsMonitorFuture;
private final long dnsMonitoringInterval;
private boolean printed;
public DNSMonitor(ConnectionManager connectionManager, RedisClient masterHost, Collection slaveHosts, long dnsMonitoringInterval, AddressResolverGroup resolverGroup) {
this.resolver = resolverGroup.getResolver(connectionManager.getServiceManager().getGroup().next());
masterHost.resolveAddr().join();
masters.put(masterHost.getConfig().getAddress(), masterHost.getAddr());
for (RedisURI host : slaveHosts) {
Future resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(host.getHost(), host.getPort()));
resolveFuture.syncUninterruptibly();
slaves.put(host, resolveFuture.getNow());
}
this.connectionManager = connectionManager;
this.dnsMonitoringInterval = dnsMonitoringInterval;
}
public void start() {
monitorDnsChange();
log.debug("DNS monitoring enabled; Current masters: {}, slaves: {}", masters, slaves);
}
public void stop() {
if (dnsMonitorFuture != null) {
dnsMonitorFuture.cancel();
}
}
private void monitorDnsChange() {
dnsMonitorFuture = connectionManager.getServiceManager().newTimeout(t -> {
if (connectionManager.getServiceManager().isShuttingDown()) {
return;
}
CompletableFuture mf = monitorMasters();
CompletableFuture sf = monitorSlaves();
CompletableFuture.allOf(mf, sf)
.whenComplete((r, e) -> monitorDnsChange());
}, dnsMonitoringInterval, TimeUnit.MILLISECONDS);
}
private CompletableFuture monitorMasters() {
List> futures = new ArrayList<>();
for (Entry entry : masters.entrySet()) {
CompletableFuture promise = new CompletableFuture<>();
futures.add(promise);
CompletableFuture> ipsFuture = connectionManager.getServiceManager().resolveAll(entry.getKey());
ipsFuture.whenComplete((addresses, ex) -> {
if (ex != null) {
log.error("Unable to resolve {}", entry.getKey().getHost(), ex);
promise.complete(null);
return;
}
if (addresses.size() > 1) {
if (!printed) {
log.warn("Use Proxy mode to utilize all ip addresses: {}", addresses);
printed = true;
}
}
for (RedisURI address : addresses) {
if (address.equals(entry.getValue())) {
log.debug("{} resolved to {}", entry.getKey().getHost(), addresses);
promise.complete(null);
return;
}
}
int index = 0;
if (addresses.size() > 1) {
addresses.sort(Comparator.comparing(RedisURI::getHost));
}
RedisURI address = addresses.get(index);
log.debug("{} resolved to {} and {} selected", entry.getKey().getHost(), addresses, address);
try {
InetSocketAddress currentMasterAddr = entry.getValue();
byte[] addr = NetUtil.createByteArrayFromIpAddressString(address.getHost());
InetSocketAddress newMasterAddr = new InetSocketAddress(InetAddress.getByAddress(entry.getKey().getHost(), addr), address.getPort());
if (!address.equals(currentMasterAddr)) {
log.info("Detected DNS change. Master {} has changed ip from {} to {}",
entry.getKey(), currentMasterAddr.getAddress().getHostAddress(),
newMasterAddr.getAddress().getHostAddress());
MasterSlaveEntry masterSlaveEntry = connectionManager.getEntry(currentMasterAddr);
if (masterSlaveEntry == null) {
log.error("Unable to find entry for current master {}", currentMasterAddr);
promise.complete(null);
return;
}
CompletableFuture changeFuture = masterSlaveEntry.changeMaster(newMasterAddr, entry.getKey());
changeFuture.whenComplete((r, e) -> {
promise.complete(null);
if (e == null) {
masters.put(entry.getKey(), newMasterAddr);
}
});
} else {
promise.complete(null);
}
} catch (UnknownHostException e) {
log.error(e.getMessage(), e);
promise.complete(null);
}
});
}
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}
private CompletableFuture monitorSlaves() {
List> futures = new ArrayList<>();
for (Entry entry : slaves.entrySet()) {
CompletableFuture promise = new CompletableFuture<>();
futures.add(promise);
log.debug("Request sent to resolve ip address for slave host: {}", entry.getKey().getHost());
Future resolveFuture = resolver.resolve(InetSocketAddress.createUnresolved(entry.getKey().getHost(), entry.getKey().getPort()));
resolveFuture.addListener((FutureListener) future -> {
if (!future.isSuccess()) {
log.error("Unable to resolve {}", entry.getKey().getHost(), future.cause());
promise.complete(null);
return;
}
log.debug("Resolved ip: {} for slave host: {}", future.getNow().getAddress(), entry.getKey().getHost());
InetSocketAddress currentSlaveAddr = entry.getValue();
InetSocketAddress newSlaveAddr = future.getNow();
if (!newSlaveAddr.getAddress().equals(currentSlaveAddr.getAddress())) {
log.info("Detected DNS change. Slave {} has changed ip from {} to {}",
entry.getKey().getHost(), currentSlaveAddr.getAddress().getHostAddress(), newSlaveAddr.getAddress().getHostAddress());
boolean slaveFound = false;
for (MasterSlaveEntry masterSlaveEntry : connectionManager.getEntrySet()) {
if (!masterSlaveEntry.hasSlave(currentSlaveAddr)) {
continue;
}
slaveFound = true;
if (masterSlaveEntry.hasSlave(newSlaveAddr)) {
CompletableFuture slaveUpFuture = masterSlaveEntry.slaveUpAsync(newSlaveAddr);
slaveUpFuture.whenComplete((r, e) -> {
if (e != null) {
promise.complete(null);
return;
}
if (r) {
slaves.put(entry.getKey(), newSlaveAddr);
masterSlaveEntry.slaveDown(currentSlaveAddr);
}
promise.complete(null);
});
} else {
CompletableFuture addFuture = masterSlaveEntry.addSlave(newSlaveAddr, entry.getKey());
addFuture.whenComplete((res, e) -> {
if (e != null) {
log.error("Can't add slave: {}", newSlaveAddr, e);
promise.complete(null);
return;
}
slaves.put(entry.getKey(), newSlaveAddr);
masterSlaveEntry.slaveDown(currentSlaveAddr);
promise.complete(null);
});
}
break;
}
if (!slaveFound) {
promise.complete(null);
}
} else {
promise.complete(null);
}
});
}
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy