Giter Site home page Giter Site logo

leonchen83 / redis-replicator Goto Github PK

View Code? Open in Web Editor NEW
952.0 77.0 269.0 10.35 MB

Redis replication tool. support sync, psync, psync2. can parse rdb, aof, mixed rdb and aof files. support redis-7.2

License: Apache License 2.0

Java 100.00%
redis rdb replication parser aof psync2 redis-replication

redis-replicator's Introduction

Table of Contents(中文说明)

1. Redis-replicator

Buy Me A Coffee

1.1. Brief introduction

Java CI with Maven Coverage Status Maven Central Javadocs Hex.pm LICENSE

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. The following I mentioned Command which means Writable Command(e.g. set,hmset) in Redis and excludes the Readable Command(e.g. get,hmget), Supported redis-6.2 and former redis versions.

1.2. Chat with author

Join the chat at https://gitter.im/leonchen83/redis-replicator

1.3. Contract the author

[email protected]

2. Install

2.1. Requirements

compile minimal jdk 9+
runtime minimal jdk 8+
maven-3.3.1+
redis 2.6 - 7.0

2.2. Maven dependency

    <dependency>
        <groupId>com.moilioncircle</groupId>
        <artifactId>redis-replicator</artifactId>
        <version>3.8.1</version>
    </dependency>

2.3. Install from source code

    step 1: install jdk-9.0.x for compile(or jdk-11.0.x)
    step 2: git clone https://github.com/leonchen83/redis-replicator.git
    step 3: $cd ./redis-replicator 
    step 4: $mvn clean install package -DskipTests

2.4. Select a version

redis version redis-replicator version
[2.6, 7.2.x] [3.8.0, ]
[2.6, 7.0.x] [3.6.4, 3.7.0]
[2.6, 7.0.x-RC2] [3.6.2, 3.6.3]
[2.6, 7.0.0-RC1] [3.6.0, 3.6.1]
[2.6, 6.2.x] [3.5.2, 3.5.5]
[2.6, 6.2.0-RC1] [3.5.0, 3.5.1]
[2.6, 6.0.x] [3.4.0, 3.4.4]
[2.6, 5.0.x] [2.6.1, 3.3.3]
[2.6, 4.0.x] [2.3.0, 2.5.0]
[2.6, 4.0-RC3] [2.1.0, 2.2.0]
[2.6, 3.2.x] [1.0.18](not supported)

3. Simple usage

3.1. Usage

        Replicator replicator = new RedisReplicator("redis://127.0.0.1:6379");
        replicator.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator replicator, Event event) {
                if (event instanceof KeyStringValueString) {
                    KeyStringValueString kv = (KeyStringValueString) event;
                    System.out.println(new String(kv.getKey()));
                    System.out.println(new String(kv.getValue()));
                } else {
                    ....
                }
            }
        });
        replicator.open();

3.2. Backup remote rdb snapshot

See RdbBackupExample.java

3.3. Backup remote commands

See CommandBackupExample.java

3.4. Convert rdb to dump format

We can use DumpRdbVisitor to convert rdb to redis DUMP format.

        Replicator r = new RedisReplicator("redis:///path/to/dump.rdb");
        r.setRdbVisitor(new DumpRdbVisitor(r));
        r.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator replicator, Event event) {
                if (!(event instanceof DumpKeyValuePair)) return;
                DumpKeyValuePair dkv = (DumpKeyValuePair) event;
                byte[] serialized = dkv.getValue();
                // we can use redis RESTORE command to migrate this serialized value to another redis.
            }
        });
        r.open();

3.5. Rdb check

We can use SkipRdbVisitor to check rdb's correctness.

        Replicator r = new RedisReplicator("redis:///path/to/dump.rdb");
        r.setRdbVisitor(new SkipRdbVisitor(r));
        r.open();

3.6. Scan and PSYNC

By default, redis-replicator uses PSYNC to pretend as slave to receives commands. examples like following

        Replicator r = new RedisReplicator("redis://127.0.0.1:6379");
        r.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator replicator, Event event) {
                System.out.println(event);
            }
        });
        
        r.open();

However, under some cloud services, the PSYNC command is prohibited, so we use the Scan command instead of the PSYNC command

        Replicator r = new RedisReplicator("redis://127.0.0.1:6379?enableScan=yes&scanStep=256");
        r.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator replicator, Event event) {
                System.out.println(event);
            }
        });
        
        r.open();

3.7. Other examples

See examples

4. Advanced topics

4.1. Command extension

4.1.1. Write a command

    @CommandSpec(command = "APPEND")
    public static class YourAppendCommand extends AbstractCommand {
        private final String key;
        private final String value;
    
        public YourAppendCommand(String key, String value) {
            this.key = key;
            this.value = value;
        }
        
        public String getKey() {
            return key;
        }
        
        public String getValue() {
            return value;
        }
    }

4.1.2. Write a command parser

    public class YourAppendParser implements CommandParser<YourAppendCommand> {

        @Override
        public YourAppendCommand parse(Object[] command) {
            return new YourAppendCommand(new String((byte[]) command[1], UTF_8), new String((byte[]) command[2], UTF_8));
        }
    }

4.1.3. Register this parser

    Replicator replicator = new RedisReplicator("redis://127.0.0.1:6379");
    replicator.addCommandParser(CommandName.name("APPEND"),new YourAppendParser());

4.1.4. Handle command event

    replicator.addEventListener(new EventListener() {
        @Override
        public void onEvent(Replicator replicator, Event event) {
            if(event instanceof YourAppendCommand){
                YourAppendCommand appendCommand = (YourAppendCommand)event;
                // your code goes here
            }
        }
    });

4.1.5. Put them together

See CommandExtensionExample.java

4.2. Module extension

4.2.1. Compile redis test modules

    $cd /path/to/redis-4.0-rc2/src/modules
    $make

4.2.2. Open comment in redis.conf

    loadmodule /path/to/redis-4.0-rc2/src/modules/hellotype.so

4.2.3. Write a module parser

    public class HelloTypeModuleParser implements ModuleParser<HelloTypeModule> {

        @Override
        public HelloTypeModule parse(RedisInputStream in, int version) throws IOException {
            DefaultRdbModuleParser parser = new DefaultRdbModuleParser(in);
            int elements = parser.loadUnsigned(version).intValue();
            long[] ary = new long[elements];
            int i = 0;
            while (elements-- > 0) {
                ary[i++] = parser.loadSigned(version);
            }
            return new HelloTypeModule(ary);
        }
    }

    public class HelloTypeModule implements Module {
        private final long[] value;

        public HelloTypeModule(long[] value) {
            this.value = value;
        }

        public long[] getValue() {
            return value;
        }
    }

4.2.4. Write a command parser

    public class HelloTypeParser implements CommandParser<HelloTypeCommand> {
        @Override
        public HelloTypeCommand parse(Object[] command) {
            String key = new String((byte[]) command[1], Constants.UTF_8);
            long value = Long.parseLong(new String((byte[]) command[2], Constants.UTF_8));
            return new HelloTypeCommand(key, value);
        }
    }
    
    @CommandSpec(command = "hellotype.insert")
    public class HelloTypeCommand extends AbstractCommand {
        private final String key;
        private final long value;

        public long getValue() {
            return value;
        }

        public String getKey() {
            return key;
        }

        public HelloTypeCommand(String key, long value) {
            this.key = key;
            this.value = value;
        }
    }

4.2.5. Register this module parser and command parser and handle event

    public static void main(String[] args) throws IOException {
        Replicator replicator = new RedisReplicator("redis://127.0.0.1:6379");
        replicator.addCommandParser(CommandName.name("hellotype.insert"), new HelloTypeParser());
        replicator.addModuleParser("hellotype", 0, new HelloTypeModuleParser());
        replicator.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator replicator, Event event) {
                if (event instanceof KeyStringValueModule) {
                    System.out.println(event);
                }
                
                if (event instanceof HelloTypeCommand) {
                    System.out.println(event);
                }
            }
        });
        replicator.open();
    }

4.2.6. Put them together

See ModuleExtensionExample.java

4.3. Stream

Since Redis 5.0+, Redis add a new data structure STREAM. Redis-replicator parse the STREAM like the following:

        Replicator r = new RedisReplicator("redis://127.0.0.1:6379");
        r.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator replicator, Event event) {
                if (event instanceof KeyStringValueStream) {
                    KeyStringValueStream kv = (KeyStringValueStream)event;
                    // key
                    String key = kv.getKey();
                    
                    // stream
                    Stream stream = kv.getValueAsStream();
                    // last stream id
                    stream.getLastId();
                    
                    // entries
                    NavigableMap<Stream.ID, Stream.Entry> entries = stream.getEntries();
                    
                    // optional : group
                    for (Stream.Group group : stream.getGroups()) {
                        // group PEL(pending entries list)
                        NavigableMap<Stream.ID, Stream.Nack> gpel = group.getPendingEntries();
                        
                        // consumer
                        for (Stream.Consumer consumer : group.getConsumers()) {
                            // consumer PEL(pending entries list)
                            NavigableMap<Stream.ID, Stream.Nack> cpel = consumer.getPendingEntries();
                        }
                    }
                }
            }
        });
        r.open();

4.4. Write your own rdb parser

  • Write YourRdbVisitor extends RdbVisitor
  • Register your RdbVisitor to Replicator using setRdbVisitor method.

4.5. Redis URI

Before redis-replicator-2.4.0, We construct RedisReplicator like the following:

Replicator replicator = new RedisReplicator("127.0.0.1", 6379, Configuration.defaultSetting());
Replicator replicator = new RedisReplicator(new File("/path/to/dump.rdb", FileType.RDB, Configuration.defaultSetting());
Replicator replicator = new RedisReplicator(new File("/path/to/appendonly.aof", FileType.AOF, Configuration.defaultSetting());
Replicator replicator = new RedisReplicator(new File("/path/to/appendonly.aof", FileType.MIXED, Configuration.defaultSetting());

After redis-replicator-2.4.0, We introduced a new concept(Redis URI) which simplify the construction process of RedisReplicator.

Replicator replicator = new RedisReplicator("redis://127.0.0.1:6379");
Replicator replicator = new RedisReplicator("redis:///path/to/dump.rdb");
Replicator replicator = new RedisReplicator("redis:///path/to/appendonly.aof");

// configuration setting example
Replicator replicator = new RedisReplicator("redis://127.0.0.1:6379?authPassword=foobared&readTimeout=10000&ssl=yes");
Replicator replicator = new RedisReplicator("redis:///path/to/dump.rdb?rateLimit=1000000");
Replicator replicator = new RedisReplicator("rediss://user:[email protected]:6379?rateLimit=1000000");

5. Other topics

5.1. Built-in command parser

commands commands commands commands commands commands
PING APPEND SET SETEX MSET DEL
SADD HMSET HSET LSET EXPIRE EXPIREAT
GETSET HSETNX MSETNX PSETEX SETNX SETRANGE
HDEL UNLINK SREM LPOP LPUSH LPUSHX
LRem RPOP RPUSH RPUSHX ZREM ZINTERSTORE
INCR DECR INCRBY PERSIST SELECT FLUSHALL
FLUSHDB HINCRBY ZINCRBY MOVE SMOVE BRPOPLPUSH
PFCOUNT PFMERGE SDIFFSTORE RENAMENX PEXPIREAT SINTERSTORE
ZADD BITFIELD SUNIONSTORE RESTORE LINSERT ZREMRANGEBYLEX
GEOADD PEXPIRE ZUNIONSTORE EVAL SCRIPT ZREMRANGEBYRANK
PUBLISH BITOP SETBIT SWAPDB PFADD ZREMRANGEBYSCORE
RENAME MULTI EXEC LTRIM RPOPLPUSH SORT
EVALSHA ZPOPMAX ZPOPMIN XACK XADD XCLAIM
XDEL XGROUP XTRIM XSETID COPY LMOVE
BLMOVE ZDIFFSTORE GEOSEARCHSTORE FUNCTION SPUBLISH

5.2. EOFException

  • Adjust redis server setting like the following. more details please refer to redis.conf
    client-output-buffer-limit slave 0 0 0

WARNNING: this setting may run out of memory of redis server in some cases.

5.3. Trace event log

  • Set log level to debug
  • If you are using log4j2, add logger like the following:
    <Logger name="com.moilioncircle" level="debug">
        <AppenderRef ref="YourAppender"/>
    </Logger>
    Configuration.defaultSetting().setVerbose(true);
    // redis uri
    "redis://127.0.0.1:6379?verbose=yes"

5.4. SSL connection

    System.setProperty("javax.net.ssl.keyStore", "/path/to/keystore");
    System.setProperty("javax.net.ssl.keyStorePassword", "password");
    System.setProperty("javax.net.ssl.keyStoreType", "your_type");

    System.setProperty("javax.net.ssl.trustStore", "/path/to/truststore");
    System.setProperty("javax.net.ssl.trustStorePassword", "password");
    System.setProperty("javax.net.ssl.trustStoreType", "your_type");

    Configuration.defaultSetting().setSsl(true);

    // optional setting
    Configuration.defaultSetting().setSslSocketFactory(sslSocketFactory);
    Configuration.defaultSetting().setSslParameters(sslParameters);
    Configuration.defaultSetting().setHostnameVerifier(hostnameVerifier);
    // redis uri
    "redis://127.0.0.1:6379?ssl=yes"
    "rediss://127.0.0.1:6379"

5.5. Auth

    Configuration.defaultSetting().setAuthUser("default");
    Configuration.defaultSetting().setAuthPassword("foobared");
    // redis uri
    "redis://127.0.0.1:6379?authPassword=foobared&authUser=default"
    "redis://default:[email protected]:6379"

5.6. Avoid full sync

  • Adjust redis server setting like the following
    repl-backlog-size
    repl-backlog-ttl
    repl-ping-slave-periods

repl-ping-slave-period MUST less than Configuration.getReadTimeout(), default Configuration.getReadTimeout() is 60 seconds

5.7. Lifecycle event

        Replicator replicator = new RedisReplicator("redis://127.0.0.1:6379");
        final long start = System.currentTimeMillis();
        final AtomicInteger acc = new AtomicInteger(0);
        replicator.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator replicator, Event event) {
                if(event instanceof PreRdbSyncEvent) {
                    System.out.println("pre rdb sync");
                } else if(event instanceof PostRdbSyncEvent) {
                    long end = System.currentTimeMillis();
                    System.out.println("time elapsed:" + (end - start));
                    System.out.println("rdb event count:" + acc.get());
                } else {
                    acc.incrementAndGet();
                }
            }
        });
        replicator.open();

5.8. Handle huge key value pair

According to 4.3. Write your own rdb parser, This tool built in an Iterable Rdb Parser so that handle huge key value pair.
More details please refer to:
[1] HugeKVFileExample.java
[2] HugeKVSocketExample.java

5.9. Redis6 support

5.9.1. SSL support

    $cd /path/to/redis
    $./utils/gen-test-certs.sh
    $cd tests/tls
    $openssl pkcs12 -export -CAfile ca.crt -in redis.crt -inkey redis.key -out redis.p12
    $cd /path/to/redis
    $./src/redis-server --tls-port 6379 --port 0 --tls-cert-file ./tests/tls/redis.crt \
         --tls-key-file ./tests/tls/redis.key --tls-ca-cert-file ./tests/tls/ca.crt \
         --tls-replication yes --bind 0.0.0.0 --protected-mode no

    System.setProperty("javax.net.ssl.keyStore", "/path/to/redis/tests/tls/redis.p12");
    System.setProperty("javax.net.ssl.keyStorePassword", "password");
    System.setProperty("javax.net.ssl.keyStoreType", "pkcs12");

    System.setProperty("javax.net.ssl.trustStore", "/path/to/redis/tests/tls/redis.p12");
    System.setProperty("javax.net.ssl.trustStorePassword", "password");
    System.setProperty("javax.net.ssl.trustStoreType", "pkcs12");

    Replicator replicator = new RedisReplicator("rediss://127.0.0.1:6379");

If you don't want to use System.setProperty you can programing as following

    RedisSslContextFactory factory = new RedisSslContextFactory();
    factory.setKeyStorePath("/path/to/redis/tests/tls/redis.p12");
    factory.setKeyStoreType("pkcs12");
    factory.setKeyStorePassword("password");

    factory.setTrustStorePath("/path/to/redis/tests/tls/redis.p12");
    factory.setTrustStoreType("pkcs12");
    factory.setTrustStorePassword("password");

    SslConfiguration ssl = SslConfiguration.defaultSetting().setSslContextFactory(factory);
    Replicator replicator = new RedisReplicator("rediss://127.0.0.1:6379", ssl);

5.9.2. ACL support

    Replicator replicator = new RedisReplicator("redis://user:[email protected]:6379");

5.10. Redis7 support

5.10.1. Function

Since redis 7.0 add function support. and function structure stored in rdb file. we can use following method to parse function.

    Replicator replicator = new RedisReplicator("redis://127.0.0.1:6379");
    replicator.addEventListener(new EventListener() {
        @Override
        public void onEvent(Replicator replicator, Event event) {
            if (event instanceof Function) {
                Function function = (Function) event;
                function.getCode();
                    
                // your code goes here
            }
        }
    });
    replicator.open();

you can also parse function to serialized data so that use FUNCTION RESTORE to restore serialized data to target redis

    Replicator replicator = new RedisReplicator("redis://127.0.0.1:6379");
    replicator.setRdbVisitor(new DumpRdbVisitor(replicator));
    replicator.addEventListener(new EventListener() {
        @Override
        public void onEvent(Replicator replicator, Event event) {
            if (event instanceof DumpFunction) {
                DumpFunction function = (DumpFunction) event;
                byte[] serialized = function.getSerialized();
                // your code goes here
                // you can use FUNCTION RESTORE to restore above serialized data to target redis
            }
        }
    });
    replicator.open();

6. Contributors

7 Consulting

Commercial support for redis-replicator is available. The following services are currently available:

  • Onsite consulting. $10,000 per day
  • Onsite training. $10,000 per day

You may also contact Baoyi Chen directly, mail to [email protected].

8. References

9. Supported by

9.1. 宁文君

27 January 2023, A sad day that I lost my mother 宁文君, She was encouraging and supporting me in developing this tool. Every time a company uses this tool, she got excited like a child and encouraged me to keep going. Without her I couldn't have maintained this tool for so many years. Even I didn't achieve much but she is still proud of me, R.I.P and hope God bless her.

9.2. YourKit

YourKit
YourKit is kindly supporting this open source project with its full-featured Java Profiler.
YourKit, LLC is the creator of innovative and intelligent tools for profiling
Java and .NET applications. Take a look at YourKit's leading software products:
YourKit Java Profiler and YourKit .NET Profiler.

9.3. IntelliJ IDEA

IntelliJ IDEA is a Java integrated development environment (IDE) for developing computer software.
It is developed by JetBrains (formerly known as IntelliJ), and is available as an Apache 2 Licensed community edition,
and in a proprietary commercial edition. Both can be used for commercial development.

9.4. Redisson

Redisson is Redis based In-Memory Data Grid for Java offers distributed objects and services (BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) backed by Redis server. Redisson provides more convenient and easiest way to work with Redis. Redisson objects provides a separation of concern, which allows you to keep focus on the data modeling and application logic.

redis-replicator's People

Contributors

adrianyao89 avatar dependabot[bot] avatar gitter-badger avatar leonchen83 avatar lyq946 avatar maplestoria avatar trydofor avatar xinyang-pan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

redis-replicator's Issues

Code example for writing rdb file back to redis

Any code examples for writing rdb file back to redis?

I want to copy all keys from one cluster to another frequently (redis 2.8 on both ends,).

I am thinking of using

  1. Backup local rdb snapshot
  2. Parse rdb file using RedisReplicator
  3. MSET ?

如何实现从某个点续传呢?我尝试这样实现抛出异常了

        com.moilioncircle.redis.replicator.Configuration configuration = Configuration.defaultSetting();
        configuration.setReplOffset(215);
        configuration.setReplId("a1962b36d6a7bcf6b2fe660127bc3b4efbcd10ca");
        Replicator replicator = new RedisReplicator("119.2xxxx", 7001, configuration);

        replicator.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator rep, Event event) {

异常:

Exception in thread "main" java.lang.AssertionError: expect [$,:,*,+,-] but: 
	at com.moilioncircle.redis.replicator.cmd.ReplyParser.parse(ReplyParser.java:174)
	at com.moilioncircle.redis.replicator.cmd.ReplyParser.parse(ReplyParser.java:58)
	at com.moilioncircle.redis.replicator.cmd.ReplyParser.parse(ReplyParser.java:53)
	at com.moilioncircle.redis.replicator.RedisSocketReplicator$RedisSocketReplicatorRetrier.open(RedisSocketReplicator.java:407)
	at com.moilioncircle.redis.replicator.AbstractReplicatorRetrier.retry(AbstractReplicatorRetrier.java:49)
	at com.moilioncircle.redis.replicator.RedisSocketReplicator.open(RedisSocketReplicator.java:111)
	at com.moilioncircle.redis.replicator.RedisReplicator.open(RedisReplicator.java:270)
	at com.company.Replica.main(Replica.java:76)

注释掉configuration.setReplicaID就ok,但会进行整个master库的同步

Socket error

redis-replicator version : 1.0.7
        RedisReplicator replicator = new RedisReplicator("127.0.0.1", 6379, Configuration.defaultSetting());
        replicator.open();
+FULLRESYNC 8de1787ba490483314a4d30f1c628bc5025eb761 2443808505[\n]$2443808505\r\nxxxxxxxxxxxxxxxx\r\n

定时同步支持吗?

redis定时同步到mysql,支持吗?我看到都是redis有变动,实时监听之后同步到mysql。能不能做定时同步,自定义同步的时间?

Exception in parse redis command "set a 1010 px 1000"

ERROR parser value error: set a 1010 px 1000 {} (com.huawei.dms.connector.redis.parser.AbstractCommandParser:60)
java.lang.ClassCastException: java.lang.String cannot be cast to [B
        at com.moilioncircle.redis.replicator.util.Strings.toString(Strings.java:42)
        at com.moilioncircle.redis.replicator.util.Strings.toString(Strings.java:37)
        at com.moilioncircle.redis.replicator.cmd.CommandParsers.toRune(CommandParsers.java:34)
        at xxx
        at xxx
        at xxx
        at com.moilioncircle.redis.replicator.RedisSocketReplicator$RedisSocketReplicatorRetrier.open(RedisSocketReplicator.java:480)
        at com.moilioncircle.redis.replicator.AbstractReplicatorRetrier.retry(AbstractReplicatorRetrier.java:49)
        at com.moilioncircle.redis.replicator.RedisSocketReplicator.open(RedisSocketReplicator.java:123)
        at com.moilioncircle.redis.replicator.RedisReplicator.open(RedisReplicator.java:270)
        at xxx.DefaultRedisEventProcessor.lambda$startReplicatorAsync$0(DefaultRedisEventProcessor.java:313)
        at java.lang.Thread.run(Thread.java:748)

dependencies:

        <dependency>
            <groupId>com.moilioncircle</groupId>
            <artifactId>redis-replicator</artifactId>
            <version>3.6.0</version>
        </dependency>
       <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

对比

你好,请问redis-replicator和 阿里的redis shake的区别,与优势?如何选择,谢谢。

ZIncrByParser bug

When I handle zincrby aof command, it occurred the exception.

java.lang.NumberFormatException: For input string: "1.0"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) ~[na:1.7.0_79]
	at java.lang.Integer.parseInt(Integer.java:492) ~[na:1.7.0_79]
	at java.lang.Integer.parseInt(Integer.java:527) ~[na:1.7.0_79]
	at com.moilioncircle.redis.replicator.cmd.parser.ZIncrByParser.parse(ZIncrByParser.java:31) ~[redis-replicator-2.0.0-rc2.jar:na]
	at com.moilioncircle.redis.replicator.cmd.parser.ZIncrByParser.parse(ZIncrByParser.java:25) ~[redis-replicator-2.0.0-rc2.jar:na]
	at com.moilioncircle.redis.replicator.RedisSocketReplicator.doOpen(RedisSocketReplicator.java:131) ~[redis-replicator-2.0.0-rc2.jar:na]
	at com.moilioncircle.redis.replicator.RedisSocketReplicator.open(RedisSocketReplicator.java:71) ~[redis-replicator-2.0.0-rc2.jar:na]
	at com.moilioncircle.redis.replicator.RedisReplicator.open(RedisReplicator.java:187) ~[redis-replicator-2.0.0-rc2.jar:na]

Redis version: 2.8.24
redis-replicator version: 1.X, 2.X

关于从inputStream()读取命令的代码

前辈您好,最近在学习您写的这个轮子.

请问ReplyPraser.prase()中的代码(那个大while循环),就是将Redis发送过来的命令转换成具体的byte[][]格式的代码,有没有相关协议的参考资料。

想把这一块弄清楚一点,但是网上找不到Redis命令传播阶段发送的内容是以什么格式编排的,如果可以的话希望您可以指点一下,万分感谢!!!

psync同步时 CONTINUE回复后面带有多余的"\n"

不确定是不是所有版本的redis都这样,我用redis 5.0.3测试的时候,进行psync同步时,有时CONTINUE回复后面带有多余的"\n"(不确定FULLRESYNC会不会这样,反正我测试的时候没有),这个\n导致offset计算跟redis计算不一致(在INFO命令的输出中可以看到)

需要说明的是,我在使用库的时候,做了比较多的自定义(用继承访问protected字段),所以这也有可能是我自己使用不当造成的。如果你知道"\n"跟offset直间的干扰关系,并且现有代码已经对此进行了处理,也请告知具体代码位置,我会检查是自己哪里搞错了。目前为了绕过这个问题,我使用了反射调整RedisInputStream内部结构,跳过多余的"\n",但这也不是什么好的做法,另外我也不确定自己对redis的offset计算的理解是正确的,有可能引入了新的问题,所以觉得还是向你请教一下比较好

can not register GetParser

public class GetParser implements CommandParser<GetParser.GetCommand> {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Override
    public GetCommand parse(CommandName cmdName, Object[] params) {
        log.debug("cmdName:{}", cmdName);
        log.debug("params:{}", Arrays.toString(params));
        return new GetCommand((String)params[0]);
    }

    public static class GetCommand implements Command {

        public final String key;

        public GetCommand(String key) {
            this.key = key;
        }

        @Override
        public String toString() {
            return "GetCommand{" +
                    "key='" + key + '\'' +
                    '}';
        }

        @Override
        public CommandName name() {
            return CommandName.name("GET");
        }
    }
}
RedisReplicator replicator = new RedisReplicator("172.172.210.109", 8101, Configuration.defaultSetting());
        replicator.addCommandParser(CommandName.name("GET"), new GetParser());
        replicator.addCommandListener((r, command) -> System.out.println(command.toString()));
        replicator.open();

aof support

2.0.0-release will support aof replication

流控不准确

使用以下代码测试流控,发现配置1020000时,实际流控为4000000左右,配置1024000时,基本准确

    public static void main(String[] args) {
        InputStream i = new InputStream() {
            @Override
            public int read() throws IOException {
                return '0';
            }
        };
        RateLimitInputStream inputStream = new RateLimitInputStream(i, 1020000);
        byte[] b = new byte[1024];
        long[] total = new long[1];
        long start = currentTimeMillis();
        new Thread(() -> {
            while (true) {
                System.out.printf("%d%n", total[0] * 1000 / (currentTimeMillis() - start));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
        }).start();
        while (true) {
            try {
                int len = inputStream.read(b, 0, 1024);
                total[0] += len;
            } catch (IOException e) {
            }
        }
    }

使用疑问

目前两个redis-cluster集群需要两个集群做数据同步, 集群master a b c 集群master d e f,
怎么使用,我看到代码里面是RedisURI uri = new RedisURI(str); 只能是单机,集群怎么设置。

扫描RDB文件时如何获取Redis内存信息

以前使用2.0版本时可以通过如下方式获得Redis 内存使用情况:

			replicator.addAuxFieldListener(new AuxFieldListener() {
				@Override
				public void handle(Replicator replicator, AuxField auxField) {
					String auxKey = auxField.getAuxKey();
					if (used_mem_flag.equalsIgnoreCase(auxKey)) {
						Long used_mem = Long.parseLong(auxField.getAuxValue());
						AppCache.redisUsedMems.add(used_mem);
					}

				}
			});

3.0 后如何获取此信息?

做两个集群间的持续同步复制(用于多活)是否可行,是否有考虑主备切换,offset点位错乱的问题

最近在寻找一款redis同步工具,验证了redis-shake,将集群A的数据持续同步到集群B,当集群A和B不发生状态变化的时候工作良好,但当集群A发生了主备切换,会出现两个问题,

redis-shake 无法主动获取集群A的新节点拓扑
获取到新master之后,如何与集群B 对账

我想问 redis-replicator
做两个集群间的持续同步复制(用于多活)是否可行,是否有考虑主备切换,offset点位错乱的问题

请教一个效率问题

如果要在target端,使用连接池,但是sendCommand方法为proteced属性,那链接池需要自己来实现吗

readInt bug

version 1.0.11
fixed version 1.0.12-SNAPSHOT, 1.0.12

byte[] b = byte[]{-36,-117}
in.readInt(b) return a positive number
expect -29732

解析hash dump的值发生报错java.lang.AssertionError: unexpected value type:92

我用redis客户端创建一个hash,然后dump key的值,然后将dump的字符串放在代码中解析,总是报错 java.lang.AssertionError: unexpected value type:92,不知道为什么?感觉自己使用错了,但是没找到原因
复现步骤:
环境:redis单机
操作:step1:HSET myhash field1 "Hello"
step2:DUMP myhash
生成dump值:"\r\x1a\x1a\x00\x00\x00\x12\x00\x00\x00\x02\x00\x00\x06field1\b\x05Hello\xff\x06\x00DC\x19\t\xf9\x8c\xde`"
然后我把dump的值用replicator解析:

String fieldValue = "\r\x1a\x1a\x00\x00\x00\x12\x00\x00\x00\x02\x00\x00\x06field1\b\" +
"x05Hello\xff\x06\x00DC\x19\t\xf9\x8c\xde`";
DumpValueParser parser = new DefaultDumpValueParser(new RedisReplicator("redis://127.0.0.1:6379"));
DumpKeyValuePair dkv = new DumpKeyValuePair();
dkv.setValue(fieldValue.getBytes());
KeyStringValueHash kv = (KeyStringValueHash) parser.parse(dkv);
return kv.getValue();

然后报错:java.lang.AssertionError: unexpected value type:92

at com.moilioncircle.redis.replicator.rdb.dump.parser.DefaultDumpValueParser.parse(DefaultDumpValueParser.java:104)
at com.chainup.cache.redis.core.RedisDumpParser.parseHash(RedisDumpParser.java:61)

帮忙看一下,非常感谢

redis-replicator如何添加key过期时间和LFU、LRU信息

请教一下,replicator全量rdb同步是采用什么机制的?挨个entry解析然后写入目的redis吗?如果是的话,如何处理key的过期时间、5.0添加的LFU以及LRU信息?redis本身好像不支持单个key的LFU、LRU信息的配置,key过期时间是采用同步key+expire2条命令实现的吗?

Broken pipe error

version : 1.0.10
fixed version : 1.0.11

when broken pipe exception occur. RedisSocketReplicator close connection and don't retry to connect to redis-server

监听psync报错

通过监听psync 伪装为slave 监听master的命令

        r.addEventListener(new EventListener() {
            @Override
            public void onEvent(Replicator replicator, Event event) {
                System.out.println(event);
            }
        });
        
        r.open();

在执行到r.open后报错

Exception in thread "Thread-97" java.lang.OutOfMemoryError: Java heap space
	at com.moilioncircle.redis.replicator.util.ByteArray.<init>(ByteArray.java:62)
	at com.moilioncircle.redis.replicator.util.ByteArray.<init>(ByteArray.java:47)
	at com.moilioncircle.redis.replicator.io.RedisInputStream.readBytes(RedisInputStream.java:115)
	at com.moilioncircle.redis.replicator.rdb.BaseRdbParser$StringHelper.bytes(BaseRdbParser.java:295)
	at com.moilioncircle.redis.replicator.rdb.BaseRdbParser$StringHelper.listPackEntry(BaseRdbParser.java:411)
	at com.moilioncircle.redis.replicator.rdb.DefaultRdbValueVisitor.applyListQuickList2(DefaultRdbValueVisitor.java:429)
	at com.moilioncircle.redis.replicator.rdb.DefaultRdbVisitor.applyListQuickList2(DefaultRdbVisitor.java:474)
	at com.moilioncircle.redis.replicator.rdb.RdbParser.parse(RdbParser.java:267)
	at com.moilioncircle.redis.replicator.RedisSocketReplicator$1.handle(RedisSocketReplicator.java:178)
	at com.moilioncircle.redis.replicator.cmd.ReplyParser.parse(ReplyParser.java:103)
	at com.moilioncircle.redis.replicator.RedisSocketReplicator.reply(RedisSocketReplicator.java:349)
	at com.moilioncircle.redis.replicator.RedisSocketReplicator.parseDump(RedisSocketReplicator.java:166)
	at com.moilioncircle.redis.replicator.RedisSocketReplicator.trySync(RedisSocketReplicator.java:142)
	at com.moilioncircle.redis.replicator.RedisSocketReplicator$RedisSocketReplicatorRetrier.open(RedisSocketReplicator.java:440)
	at com.moilioncircle.redis.replicator.AbstractReplicatorRetrier.retry(AbstractReplicatorRetrier.java:49)
	at com.moilioncircle.redis.replicator.RedisSocketReplicator.open(RedisSocketReplicator.java:123)

是因为什么原因? 内存不足吗

有计划支持Tendis吗?

Tendis内部主从同步没有使用sync,psync指令,请问有计划支持下Tendis的数据同步吗?

RDB EOFException

https://git.oschina.net/leonchen83/redis-replicator/issues/2

redis-replicator version : 1.0.6
RedisReplicator replicator = new RedisReplicator(new File("D:/dump.rdb"), Configuration.defaultSetting());
        replicator.addRdbFilter(kv -> kv.getKey().startsWith("mid_20000007_"));
        replicator.addRdbListener(new RdbListener.Adaptor() {
            @Override
            public void handle(Replicator replicator, KeyValuePair<?> kv) {
                System.out.println(kv);
            }
        });
        replicator.open();
Exception in thread "main" java.io.EOFException: end of file.
    at com.moilioncircle.redis.replicator.io.RedisInputStream.fill(RedisInputStream.java:191)
    at com.moilioncircle.redis.replicator.io.RedisInputStream.read(RedisInputStream.java:179)
    at com.moilioncircle.redis.replicator.io.RedisInputStream.fill(RedisInputStream.java:190)
    at com.moilioncircle.redis.replicator.io.RedisInputStream.read(RedisInputStream.java:179)
    at com.moilioncircle.redis.replicator.io.RedisInputStream.readBytes(RedisInputStream.java:90)
    at com.moilioncircle.redis.replicator.io.RedisInputStream.readString(RedisInputStream.java:162)
    at com.moilioncircle.redis.replicator.io.RedisInputStream.readString(RedisInputStream.java:158)
    at com.moilioncircle.redis.replicator.rdb.AbstractRdbParser$StringHelper.str(AbstractRdbParser.java:214)
    at com.moilioncircle.redis.replicator.rdb.AbstractRdbParser.rdbGenericLoadStringObject(AbstractRdbParser.java:162)
    at com.moilioncircle.redis.replicator.rdb.AbstractRdbParser.rdbLoadEncodedStringObject(AbstractRdbParser.java:178)
    at com.moilioncircle.redis.replicator.rdb.Rdb6Parser.rdbLoad(Rdb6Parser.java:60)
    at com.moilioncircle.redis.replicator.rdb.RdbParser.parse(RdbParser.java:104)
    at com.moilioncircle.redis.replicator.RedisFileReplicator.open(RedisFileReplicator.java:44)
    at com.moilioncircle.redis.replicator.RedisReplicator.open(RedisReplicator.java:144)

How to handle huge key?

Confuse about huge key solution. 5.8. Handle huge key value pair,what is means? why HugeKVFileExample.java HugeKVSocketExample.java can solve huge key problem? can clarify the internals?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.