com.moilioncircle.redis.replicator.RedisMixReplicator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redis-replicator Show documentation
Show all versions of redis-replicator Show documentation
Redis Replicator implement Redis Replication protocol written in java.
It can parse,filter,broadcast the RDB and AOF events in a real time manner.
It also can synchronize redis data to your local cache or to database.
/*
* Copyright 2016-2018 Leon Chen
*
* 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 com.moilioncircle.redis.replicator;
import static com.moilioncircle.redis.replicator.Status.CONNECTED;
import static com.moilioncircle.redis.replicator.Status.DISCONNECTED;
import static com.moilioncircle.redis.replicator.util.Strings.format;
import static com.moilioncircle.redis.replicator.util.Tuples.of;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.moilioncircle.redis.replicator.cmd.Command;
import com.moilioncircle.redis.replicator.cmd.CommandName;
import com.moilioncircle.redis.replicator.cmd.CommandParser;
import com.moilioncircle.redis.replicator.cmd.RedisCodec;
import com.moilioncircle.redis.replicator.cmd.ReplyParser;
import com.moilioncircle.redis.replicator.event.PostCommandSyncEvent;
import com.moilioncircle.redis.replicator.event.PreCommandSyncEvent;
import com.moilioncircle.redis.replicator.io.PeekableInputStream;
import com.moilioncircle.redis.replicator.io.RedisInputStream;
import com.moilioncircle.redis.replicator.rdb.RdbParser;
import com.moilioncircle.redis.replicator.util.Strings;
/**
* @author Leon Chen
* @since 2.1.0
*/
public class RedisMixReplicator extends AbstractReplicator {
protected static final Logger logger = LoggerFactory.getLogger(RedisMixReplicator.class);
protected final ReplyParser replyParser;
protected final PeekableInputStream peekable;
public RedisMixReplicator(File file, Configuration configuration) throws FileNotFoundException {
this(new FileInputStream(file), configuration);
}
public RedisMixReplicator(InputStream in, Configuration configuration) {
Objects.requireNonNull(in);
Objects.requireNonNull(configuration);
this.configuration = configuration;
if (in instanceof PeekableInputStream) {
this.peekable = (PeekableInputStream) in;
} else {
in = this.peekable = new PeekableInputStream(in);
}
this.inputStream = new RedisInputStream(in, this.configuration.getBufferSize());
this.inputStream.setRawByteListeners(this.rawByteListeners);
this.replyParser = new ReplyParser(inputStream, new RedisCodec());
builtInCommandParserRegister();
if (configuration.isUseDefaultExceptionListener())
addExceptionListener(new DefaultExceptionListener());
}
@Override
public void open() throws IOException {
super.open();
if (!compareAndSet(DISCONNECTED, CONNECTED)) return;
try {
doOpen();
} catch (UncheckedIOException e) {
if (!(e.getCause() instanceof EOFException)) throw e.getCause();
} finally {
doClose();
doCloseListener(this);
}
}
protected void doOpen() throws IOException {
configuration.setReplOffset(0L);
if (peekable.peek() == 'R') {
RdbParser parser = new RdbParser(inputStream, this);
configuration.setReplOffset(parser.parse());
}
if (getStatus() != CONNECTED) return;
submitEvent(new PreCommandSyncEvent());
try {
final long[] offset = new long[1];
while (getStatus() == CONNECTED) {
Object obj = replyParser.parse(len -> offset[0] = len);
if (obj instanceof Object[]) {
if (verbose() && logger.isDebugEnabled())
logger.debug(format((Object[]) obj));
Object[] raw = (Object[]) obj;
CommandName name = CommandName.name(Strings.toString(raw[0]));
final CommandParser extends Command> parser;
if ((parser = commands.get(name)) == null) {
logger.warn("command [{}] not register. raw command:{}", name, format(raw));
configuration.addOffset(offset[0]);
offset[0] = 0L;
continue;
}
final long st = configuration.getReplOffset();
final long ed = st + offset[0];
submitEvent(parser.parse(raw), of(st, ed));
} else {
logger.warn("unexpected redis reply:{}", obj);
}
configuration.addOffset(offset[0]);
offset[0] = 0L;
}
} catch (EOFException ignore) {
submitEvent(new PostCommandSyncEvent());
}
}
}