|
|
@ -15,18 +15,19 @@
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
package org.redisson.client.handler;
|
|
|
|
package org.redisson.client.handler;
|
|
|
|
|
|
|
|
|
|
|
|
import io.netty.channel.*;
|
|
|
|
import io.netty.channel.ChannelDuplexHandler;
|
|
|
|
|
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
|
|
|
|
|
|
|
import io.netty.channel.ChannelPromise;
|
|
|
|
import io.netty.util.AttributeKey;
|
|
|
|
import io.netty.util.AttributeKey;
|
|
|
|
import org.redisson.client.ChannelName;
|
|
|
|
|
|
|
|
import org.redisson.client.WriteRedisConnectionException;
|
|
|
|
import org.redisson.client.WriteRedisConnectionException;
|
|
|
|
import org.redisson.client.protocol.CommandData;
|
|
|
|
import org.redisson.client.protocol.*;
|
|
|
|
import org.redisson.client.protocol.QueueCommand;
|
|
|
|
|
|
|
|
import org.redisson.client.protocol.QueueCommandHolder;
|
|
|
|
|
|
|
|
import org.redisson.misc.LogHelper;
|
|
|
|
import org.redisson.misc.LogHelper;
|
|
|
|
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
import java.net.SocketAddress;
|
|
|
|
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.Queue;
|
|
|
|
import java.util.Queue;
|
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
*
|
|
|
@ -36,41 +37,30 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public class CommandsQueue extends ChannelDuplexHandler {
|
|
|
|
public class CommandsQueue extends ChannelDuplexHandler {
|
|
|
|
|
|
|
|
|
|
|
|
public static final AttributeKey<QueueCommand> CURRENT_COMMAND = AttributeKey.valueOf("promise");
|
|
|
|
public static final AttributeKey<Queue<QueueCommandHolder>> COMMANDS_QUEUE = AttributeKey.valueOf("COMMANDS_QUEUE");
|
|
|
|
|
|
|
|
|
|
|
|
private final Queue<QueueCommandHolder> queue = new ConcurrentLinkedQueue<>();
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
|
|
|
|
private final ChannelFutureListener listener = future -> {
|
|
|
|
super.connect(ctx, remoteAddress, localAddress, promise);
|
|
|
|
if (!future.isSuccess() && future.channel().isActive()) {
|
|
|
|
|
|
|
|
sendNextCommand(future.channel());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void sendNextCommand(Channel channel) {
|
|
|
|
ctx.channel().attr(COMMANDS_QUEUE).set(new ConcurrentLinkedQueue<>());
|
|
|
|
QueueCommand command = channel.attr(CommandsQueue.CURRENT_COMMAND).getAndSet(null);
|
|
|
|
|
|
|
|
if (command != null) {
|
|
|
|
|
|
|
|
queue.poll();
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
QueueCommandHolder c = queue.peek();
|
|
|
|
|
|
|
|
if (c != null) {
|
|
|
|
|
|
|
|
QueueCommand data = c.getCommand();
|
|
|
|
|
|
|
|
List<CommandData<Object, Object>> pubSubOps = data.getPubSubOperations();
|
|
|
|
|
|
|
|
if (!pubSubOps.isEmpty()) {
|
|
|
|
|
|
|
|
queue.poll();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
sendData(channel);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
while (true) {
|
|
|
|
Queue<QueueCommandHolder> queue = ctx.channel().attr(COMMANDS_QUEUE).get();
|
|
|
|
QueueCommandHolder command = queue.poll();
|
|
|
|
Iterator<QueueCommandHolder> iterator = queue.iterator();
|
|
|
|
if (command == null) {
|
|
|
|
while (iterator.hasNext()) {
|
|
|
|
break;
|
|
|
|
QueueCommandHolder command = iterator.next();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CommandData cc = (CommandData) command.getCommand();
|
|
|
|
|
|
|
|
RedisCommand cmd = cc.getCommand();
|
|
|
|
|
|
|
|
if (RedisCommands.BLOCKING_COMMAND_NAMES.contains(cmd.getName())
|
|
|
|
|
|
|
|
|| RedisCommands.BLOCKING_COMMANDS.contains(cmd)) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
iterator.remove();
|
|
|
|
command.getChannelPromise().tryFailure(
|
|
|
|
command.getChannelPromise().tryFailure(
|
|
|
|
new WriteRedisConnectionException("Channel has been closed! Can't write command: "
|
|
|
|
new WriteRedisConnectionException("Channel has been closed! Can't write command: "
|
|
|
|
+ LogHelper.toString(command.getCommand()) + " to channel: " + ctx.channel()));
|
|
|
|
+ LogHelper.toString(command.getCommand()) + " to channel: " + ctx.channel()));
|
|
|
@ -79,39 +69,29 @@ public class CommandsQueue extends ChannelDuplexHandler {
|
|
|
|
super.channelInactive(ctx);
|
|
|
|
super.channelInactive(ctx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private final AtomicBoolean lock = new AtomicBoolean();
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
|
|
|
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
|
|
|
if (msg instanceof QueueCommand) {
|
|
|
|
if (msg instanceof QueueCommand) {
|
|
|
|
QueueCommand data = (QueueCommand) msg;
|
|
|
|
QueueCommand data = (QueueCommand) msg;
|
|
|
|
QueueCommandHolder holder = queue.peek();
|
|
|
|
QueueCommandHolder holder = new QueueCommandHolder(data, promise);
|
|
|
|
if (holder != null && holder.getCommand() == data) {
|
|
|
|
|
|
|
|
super.write(ctx, msg, promise);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
queue.add(new QueueCommandHolder(data, promise));
|
|
|
|
|
|
|
|
sendData(ctx.channel());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
super.write(ctx, msg, promise);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void sendData(Channel ch) {
|
|
|
|
Queue<QueueCommandHolder> queue = ctx.channel().attr(COMMANDS_QUEUE).get();
|
|
|
|
QueueCommandHolder command = queue.peek();
|
|
|
|
|
|
|
|
if (command != null && command.trySend()) {
|
|
|
|
while (true) {
|
|
|
|
QueueCommand data = command.getCommand();
|
|
|
|
if (lock.compareAndSet(false, true)) {
|
|
|
|
List<CommandData<Object, Object>> pubSubOps = data.getPubSubOperations();
|
|
|
|
try {
|
|
|
|
if (!pubSubOps.isEmpty()) {
|
|
|
|
queue.add(holder);
|
|
|
|
for (CommandData<Object, Object> cd : pubSubOps) {
|
|
|
|
ctx.writeAndFlush(data, holder.getChannelPromise());
|
|
|
|
for (Object channel : cd.getParams()) {
|
|
|
|
} finally {
|
|
|
|
ch.pipeline().get(CommandPubSubDecoder.class).addPubSubCommand((ChannelName) channel, cd);
|
|
|
|
lock.set(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
|
|
|
|
ch.attr(CURRENT_COMMAND).set(data);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
command.getChannelPromise().addListener(listener);
|
|
|
|
super.write(ctx, msg, promise);
|
|
|
|
ch.writeAndFlush(data, command.getChannelPromise());
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|