Merge branch 'master' into 3.0.0
commit
3909f68548
@ -1,79 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright 2018 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 java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import org.redisson.api.RFuture;
|
|
||||||
import org.redisson.client.RedisConnection;
|
|
||||||
import org.redisson.client.protocol.RedisCommand;
|
|
||||||
import org.redisson.misc.RPromise;
|
|
||||||
|
|
||||||
import io.netty.util.concurrent.Future;
|
|
||||||
import io.netty.util.concurrent.FutureListener;
|
|
||||||
|
|
||||||
public class FutureConnectionListener<T extends RedisConnection> implements FutureListener<Object> {
|
|
||||||
|
|
||||||
private final AtomicInteger commandsCounter = new AtomicInteger();
|
|
||||||
|
|
||||||
private final RPromise<T> connectionPromise;
|
|
||||||
private final T connection;
|
|
||||||
private final List<Runnable> commands = new ArrayList<Runnable>(4);
|
|
||||||
|
|
||||||
public FutureConnectionListener(RPromise<T> connectionFuture, T connection) {
|
|
||||||
super();
|
|
||||||
this.connectionPromise = connectionFuture;
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addCommand(final RedisCommand<?> command, final Object ... params) {
|
|
||||||
commandsCounter.incrementAndGet();
|
|
||||||
commands.add(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
RFuture<Object> future = connection.async(command, params);
|
|
||||||
future.addListener(FutureConnectionListener.this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void executeCommands() {
|
|
||||||
if (commands.isEmpty()) {
|
|
||||||
connectionPromise.trySuccess(connection);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Runnable command : commands) {
|
|
||||||
command.run();
|
|
||||||
}
|
|
||||||
commands.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void operationComplete(Future<Object> future) throws Exception {
|
|
||||||
if (!future.isSuccess()) {
|
|
||||||
connection.closeAsync();
|
|
||||||
connectionPromise.tryFailure(future.cause());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (commandsCounter.decrementAndGet() == 0) {
|
|
||||||
connectionPromise.trySuccess(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,463 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018 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.pubsub;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.redisson.api.RFuture;
|
||||||
|
import org.redisson.client.BaseRedisPubSubListener;
|
||||||
|
import org.redisson.client.RedisNodeNotFoundException;
|
||||||
|
import org.redisson.client.RedisPubSubConnection;
|
||||||
|
import org.redisson.client.RedisPubSubListener;
|
||||||
|
import org.redisson.client.RedisTimeoutException;
|
||||||
|
import org.redisson.client.SubscribeListener;
|
||||||
|
import org.redisson.client.codec.Codec;
|
||||||
|
import org.redisson.client.protocol.pubsub.PubSubType;
|
||||||
|
import org.redisson.config.MasterSlaveServersConfig;
|
||||||
|
import org.redisson.connection.ConnectionManager;
|
||||||
|
import org.redisson.connection.MasterSlaveEntry;
|
||||||
|
import org.redisson.connection.PubSubConnectionEntry;
|
||||||
|
import org.redisson.misc.RPromise;
|
||||||
|
import org.redisson.misc.RedissonPromise;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import io.netty.util.Timeout;
|
||||||
|
import io.netty.util.TimerTask;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.FutureListener;
|
||||||
|
import io.netty.util.internal.PlatformDependent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Nikita Koksharov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class PublishSubscribeService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(PublishSubscribeService.class);
|
||||||
|
|
||||||
|
private final ConnectionManager connectionManager;
|
||||||
|
|
||||||
|
private final MasterSlaveServersConfig config;
|
||||||
|
|
||||||
|
private final AsyncSemaphore[] locks = new AsyncSemaphore[50];
|
||||||
|
|
||||||
|
private final AsyncSemaphore freePubSubLock = new AsyncSemaphore(1);
|
||||||
|
|
||||||
|
protected final ConcurrentMap<String, PubSubConnectionEntry> name2PubSubConnection = PlatformDependent.newConcurrentHashMap();
|
||||||
|
|
||||||
|
protected final Queue<PubSubConnectionEntry> freePubSubConnections = new ConcurrentLinkedQueue<PubSubConnectionEntry>();
|
||||||
|
|
||||||
|
public PublishSubscribeService(ConnectionManager connectionManager, MasterSlaveServersConfig config) {
|
||||||
|
super();
|
||||||
|
this.connectionManager = connectionManager;
|
||||||
|
this.config = config;
|
||||||
|
for (int i = 0; i < locks.length; i++) {
|
||||||
|
locks[i] = new AsyncSemaphore(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PubSubConnectionEntry getPubSubEntry(String channelName) {
|
||||||
|
return name2PubSubConnection.get(channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFuture<PubSubConnectionEntry> psubscribe(String channelName, Codec codec, RedisPubSubListener<?>... listeners) {
|
||||||
|
return subscribe(PubSubType.PSUBSCRIBE, codec, channelName, new RedissonPromise<PubSubConnectionEntry>(), listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFuture<PubSubConnectionEntry> psubscribe(String channelName, Codec codec, AsyncSemaphore semaphore, RedisPubSubListener<?>... listeners) {
|
||||||
|
RPromise<PubSubConnectionEntry> promise = new RedissonPromise<PubSubConnectionEntry>();
|
||||||
|
subscribe(codec, channelName, promise, PubSubType.PSUBSCRIBE, semaphore, listeners);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFuture<PubSubConnectionEntry> subscribe(Codec codec, String channelName, RedisPubSubListener<?>... listeners) {
|
||||||
|
return subscribe(PubSubType.SUBSCRIBE, codec, channelName, new RedissonPromise<PubSubConnectionEntry>(), listeners);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RFuture<PubSubConnectionEntry> subscribe(final PubSubType type, final Codec codec, final String channelName,
|
||||||
|
final RPromise<PubSubConnectionEntry> promise, final RedisPubSubListener<?>... listeners) {
|
||||||
|
final AsyncSemaphore lock = getSemaphore(channelName);
|
||||||
|
lock.acquire(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (promise.isDone()) {
|
||||||
|
lock.release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final RPromise<PubSubConnectionEntry> result = new RedissonPromise<PubSubConnectionEntry>();
|
||||||
|
result.addListener(new FutureListener<PubSubConnectionEntry>() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<PubSubConnectionEntry> future) throws Exception {
|
||||||
|
if (!future.isSuccess()) {
|
||||||
|
subscribe(type, codec, channelName, promise, listeners);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.trySuccess(result.getNow());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
subscribe(codec, channelName, result, type, lock, listeners);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFuture<PubSubConnectionEntry> subscribe(Codec codec, String channelName, AsyncSemaphore semaphore, RedisPubSubListener<?>... listeners) {
|
||||||
|
RPromise<PubSubConnectionEntry> promise = new RedissonPromise<PubSubConnectionEntry>();
|
||||||
|
subscribe(codec, channelName, promise, PubSubType.SUBSCRIBE, semaphore, listeners);
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncSemaphore getSemaphore(String channelName) {
|
||||||
|
return locks[Math.abs(channelName.hashCode() % locks.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void subscribe(final Codec codec, final String channelName,
|
||||||
|
final RPromise<PubSubConnectionEntry> promise, final PubSubType type, final AsyncSemaphore lock, final RedisPubSubListener<?>... listeners) {
|
||||||
|
final PubSubConnectionEntry connEntry = name2PubSubConnection.get(channelName);
|
||||||
|
if (connEntry != null) {
|
||||||
|
subscribe(channelName, promise, type, lock, connEntry, listeners);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
freePubSubLock.acquire(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (promise.isDone()) {
|
||||||
|
lock.release();
|
||||||
|
freePubSubLock.release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PubSubConnectionEntry freeEntry = freePubSubConnections.peek();
|
||||||
|
if (freeEntry == null) {
|
||||||
|
connect(codec, channelName, promise, type, lock, listeners);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int remainFreeAmount = freeEntry.tryAcquire();
|
||||||
|
if (remainFreeAmount == -1) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
final PubSubConnectionEntry oldEntry = name2PubSubConnection.putIfAbsent(channelName, freeEntry);
|
||||||
|
if (oldEntry != null) {
|
||||||
|
freeEntry.release();
|
||||||
|
freePubSubLock.release();
|
||||||
|
|
||||||
|
subscribe(channelName, promise, type, lock, oldEntry, listeners);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remainFreeAmount == 0) {
|
||||||
|
freePubSubConnections.poll();
|
||||||
|
}
|
||||||
|
freePubSubLock.release();
|
||||||
|
|
||||||
|
subscribe(channelName, promise, type, lock, freeEntry, listeners);
|
||||||
|
|
||||||
|
if (PubSubType.PSUBSCRIBE == type) {
|
||||||
|
freeEntry.psubscribe(codec, channelName);
|
||||||
|
} else {
|
||||||
|
freeEntry.subscribe(codec, channelName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void subscribe(final String channelName, final RPromise<PubSubConnectionEntry> promise,
|
||||||
|
final PubSubType type, final AsyncSemaphore lock, final PubSubConnectionEntry connEntry,
|
||||||
|
final RedisPubSubListener<?>... listeners) {
|
||||||
|
for (RedisPubSubListener<?> listener : listeners) {
|
||||||
|
connEntry.addListener(channelName, listener);
|
||||||
|
}
|
||||||
|
SubscribeListener listener = connEntry.getSubscribeFuture(channelName, type);
|
||||||
|
final Future<Void> subscribeFuture = listener.getSuccessFuture();
|
||||||
|
|
||||||
|
subscribeFuture.addListener(new FutureListener<Void>() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<Void> future) throws Exception {
|
||||||
|
if (!promise.trySuccess(connEntry)) {
|
||||||
|
for (RedisPubSubListener<?> listener : listeners) {
|
||||||
|
connEntry.removeListener(channelName, listener);
|
||||||
|
}
|
||||||
|
if (!connEntry.hasListeners(channelName)) {
|
||||||
|
unsubscribe(channelName, lock);
|
||||||
|
} else {
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lock.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
connectionManager.newTimeout(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run(Timeout timeout) throws Exception {
|
||||||
|
if (promise.tryFailure(new RedisTimeoutException())) {
|
||||||
|
subscribeFuture.cancel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, config.getRetryInterval(), TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseSubscribeConnection(int slot, PubSubConnectionEntry pubSubEntry) {
|
||||||
|
MasterSlaveEntry entry = connectionManager.getEntry(slot);
|
||||||
|
if (entry == null) {
|
||||||
|
log.error("Node for slot: " + slot + " can't be found");
|
||||||
|
} else {
|
||||||
|
entry.returnPubSubConnection(pubSubEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private RFuture<RedisPubSubConnection> nextPubSubConnection(int slot) {
|
||||||
|
MasterSlaveEntry entry = connectionManager.getEntry(slot);
|
||||||
|
if (entry == null) {
|
||||||
|
RedisNodeNotFoundException ex = new RedisNodeNotFoundException("Node for slot: " + slot + " hasn't been discovered yet");
|
||||||
|
return RedissonPromise.newFailedFuture(ex);
|
||||||
|
}
|
||||||
|
return entry.nextPubSubConnection();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connect(final Codec codec, final String channelName,
|
||||||
|
final RPromise<PubSubConnectionEntry> promise, final PubSubType type, final AsyncSemaphore lock, final RedisPubSubListener<?>... listeners) {
|
||||||
|
final int slot = connectionManager.calcSlot(channelName);
|
||||||
|
RFuture<RedisPubSubConnection> connFuture = nextPubSubConnection(slot);
|
||||||
|
connFuture.addListener(new FutureListener<RedisPubSubConnection>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<RedisPubSubConnection> future) throws Exception {
|
||||||
|
if (!future.isSuccess()) {
|
||||||
|
freePubSubLock.release();
|
||||||
|
lock.release();
|
||||||
|
promise.tryFailure(future.cause());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RedisPubSubConnection conn = future.getNow();
|
||||||
|
|
||||||
|
final PubSubConnectionEntry entry = new PubSubConnectionEntry(conn, config.getSubscriptionsPerConnection());
|
||||||
|
entry.tryAcquire();
|
||||||
|
|
||||||
|
final PubSubConnectionEntry oldEntry = name2PubSubConnection.putIfAbsent(channelName, entry);
|
||||||
|
if (oldEntry != null) {
|
||||||
|
releaseSubscribeConnection(slot, entry);
|
||||||
|
|
||||||
|
freePubSubLock.release();
|
||||||
|
|
||||||
|
subscribe(channelName, promise, type, lock, oldEntry, listeners);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
freePubSubConnections.add(entry);
|
||||||
|
freePubSubLock.release();
|
||||||
|
|
||||||
|
subscribe(channelName, promise, type, lock, entry, listeners);
|
||||||
|
|
||||||
|
if (PubSubType.PSUBSCRIBE == type) {
|
||||||
|
entry.psubscribe(codec, channelName);
|
||||||
|
} else {
|
||||||
|
entry.subscribe(codec, channelName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsubscribe(final String channelName, final AsyncSemaphore lock) {
|
||||||
|
final PubSubConnectionEntry entry = name2PubSubConnection.remove(channelName);
|
||||||
|
if (entry == null) {
|
||||||
|
lock.release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.unsubscribe(channelName, new BaseRedisPubSubListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStatus(PubSubType type, String channel) {
|
||||||
|
if (type == PubSubType.UNSUBSCRIBE && channel.equals(channelName)) {
|
||||||
|
|
||||||
|
if (entry.release() == 1) {
|
||||||
|
freePubSubConnections.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public RFuture<Codec> unsubscribe(final String channelName, final PubSubType topicType) {
|
||||||
|
final RPromise<Codec> result = new RedissonPromise<Codec>();
|
||||||
|
final AsyncSemaphore lock = getSemaphore(channelName);
|
||||||
|
lock.acquire(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
final PubSubConnectionEntry entry = name2PubSubConnection.remove(channelName);
|
||||||
|
if (entry == null) {
|
||||||
|
lock.release();
|
||||||
|
result.trySuccess(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
freePubSubLock.acquire(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
freePubSubConnections.remove(entry);
|
||||||
|
freePubSubLock.release();
|
||||||
|
|
||||||
|
final Codec entryCodec = entry.getConnection().getChannels().get(channelName);
|
||||||
|
RedisPubSubListener<Object> listener = new BaseRedisPubSubListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStatus(PubSubType type, String channel) {
|
||||||
|
if (type == topicType && channel.equals(channelName)) {
|
||||||
|
lock.release();
|
||||||
|
result.trySuccess(entryCodec);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
if (topicType == PubSubType.PUNSUBSCRIBE) {
|
||||||
|
entry.punsubscribe(channelName, listener);
|
||||||
|
} else {
|
||||||
|
entry.unsubscribe(channelName, listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void punsubscribe(final String channelName, final AsyncSemaphore lock) {
|
||||||
|
final PubSubConnectionEntry entry = name2PubSubConnection.remove(channelName);
|
||||||
|
if (entry == null) {
|
||||||
|
lock.release();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.punsubscribe(channelName, new BaseRedisPubSubListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStatus(PubSubType type, String channel) {
|
||||||
|
if (type == PubSubType.PUNSUBSCRIBE && channel.equals(channelName)) {
|
||||||
|
|
||||||
|
if (entry.release() == 1) {
|
||||||
|
freePubSubConnections.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock.release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reattachPubSub(RedisPubSubConnection redisPubSubConnection) {
|
||||||
|
for (String channelName : redisPubSubConnection.getChannels().keySet()) {
|
||||||
|
PubSubConnectionEntry pubSubEntry = getPubSubEntry(channelName);
|
||||||
|
Collection<RedisPubSubListener<?>> listeners = pubSubEntry.getListeners(channelName);
|
||||||
|
reattachPubSubListeners(channelName, listeners, PubSubType.UNSUBSCRIBE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String channelName : redisPubSubConnection.getPatternChannels().keySet()) {
|
||||||
|
PubSubConnectionEntry pubSubEntry = getPubSubEntry(channelName);
|
||||||
|
Collection<RedisPubSubListener<?>> listeners = pubSubEntry.getListeners(channelName);
|
||||||
|
reattachPubSubListeners(channelName, listeners, PubSubType.PUNSUBSCRIBE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reattachPubSubListeners(final String channelName, final Collection<RedisPubSubListener<?>> listeners, final PubSubType topicType) {
|
||||||
|
RFuture<Codec> subscribeCodec = unsubscribe(channelName, topicType);
|
||||||
|
if (listeners.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeCodec.addListener(new FutureListener<Codec>() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<Codec> future) throws Exception {
|
||||||
|
if (future.get() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Codec subscribeCodec = future.get();
|
||||||
|
if (topicType == PubSubType.PUNSUBSCRIBE) {
|
||||||
|
psubscribe(channelName, listeners, subscribeCodec);
|
||||||
|
} else {
|
||||||
|
subscribe(channelName, listeners, subscribeCodec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void subscribe(final String channelName, final Collection<RedisPubSubListener<?>> listeners,
|
||||||
|
final Codec subscribeCodec) {
|
||||||
|
RFuture<PubSubConnectionEntry> subscribeFuture = subscribe(subscribeCodec, channelName, listeners.toArray(new RedisPubSubListener[listeners.size()]));
|
||||||
|
subscribeFuture.addListener(new FutureListener<PubSubConnectionEntry>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<PubSubConnectionEntry> future)
|
||||||
|
throws Exception {
|
||||||
|
if (!future.isSuccess()) {
|
||||||
|
subscribe(channelName, listeners, subscribeCodec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("listeners of '{}' channel to '{}' have been resubscribed", channelName, future.getNow().getConnection().getRedisClient());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void psubscribe(final String channelName, final Collection<RedisPubSubListener<?>> listeners,
|
||||||
|
final Codec subscribeCodec) {
|
||||||
|
RFuture<PubSubConnectionEntry> subscribeFuture = psubscribe(channelName, subscribeCodec, listeners.toArray(new RedisPubSubListener[listeners.size()]));
|
||||||
|
subscribeFuture.addListener(new FutureListener<PubSubConnectionEntry>() {
|
||||||
|
@Override
|
||||||
|
public void operationComplete(Future<PubSubConnectionEntry> future)
|
||||||
|
throws Exception {
|
||||||
|
if (!future.isSuccess()) {
|
||||||
|
psubscribe(channelName, listeners, subscribeCodec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.debug("resubscribed listeners for '{}' channel-pattern to '{}'", channelName, future.getNow().getConnection().getRedisClient());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue