├── .gitignore ├── LICENSE ├── README.md ├── pom.xml └── src ├── main └── java │ └── com │ └── moilioncircle │ └── redis │ └── cluster │ └── watchdog │ ├── AbstractClusterWatchdog.java │ ├── ClusterConfigInfo.java │ ├── ClusterConfigListener.java │ ├── ClusterConfiguration.java │ ├── ClusterConfigurationException.java │ ├── ClusterConstants.java │ ├── ClusterNodeInfo.java │ ├── ClusterNodeListener.java │ ├── ClusterState.java │ ├── ClusterStateListener.java │ ├── ClusterWatchdog.java │ ├── RedisClusterWatchdog.java │ ├── ReplicationListener.java │ ├── Resourcable.java │ ├── ThinGossip.java │ ├── ThinServer.java │ ├── Version.java │ ├── codec │ ├── ClusterMessageDecoder.java │ ├── ClusterMessageEncoder.java │ ├── RedisDecoder.java │ └── RedisEncoder.java │ ├── command │ ├── AbstractCommandHandler.java │ ├── CommandHandler.java │ ├── ConfigCommandHandler.java │ ├── DBSizeCommandHandler.java │ ├── DefaultCommandHandler.java │ ├── DumpCommandHandler.java │ ├── InfoCommandHandler.java │ ├── PingCommandHandler.java │ ├── ReadWriteCommandHandler.java │ ├── ReadonlyCommandHandler.java │ ├── RestoreCommandHandler.java │ ├── SelectCommandHandler.java │ ├── ShutdownCommandHandler.java │ └── cluster │ │ ├── ClusterAddSlotsCommandHandler.java │ │ ├── ClusterBumpEpochCommandHandler.java │ │ ├── ClusterCommandHandler.java │ │ ├── ClusterCountFailureReportsCommandHandler.java │ │ ├── ClusterCountKeysInSlotCommandHandler.java │ │ ├── ClusterDelSlotsCommandHandler.java │ │ ├── ClusterFlushSlotsCommandHandler.java │ │ ├── ClusterForgetCommandHandler.java │ │ ├── ClusterGetKeysInSlotCommandHandler.java │ │ ├── ClusterInfoCommandHandler.java │ │ ├── ClusterKeySlotCommandHandler.java │ │ ├── ClusterMeetCommandHandler.java │ │ ├── ClusterMyIDCommandHandler.java │ │ ├── ClusterNodesCommandHandler.java │ │ ├── ClusterReplicateCommandHandler.java │ │ ├── ClusterResetCommandHandler.java │ │ ├── ClusterSaveConfigCommandHandler.java │ │ ├── ClusterSetConfigEpochCommandHandler.java │ │ ├── ClusterSetSlotCommandHandler.java │ │ ├── ClusterSlavesCommandHandler.java │ │ └── ClusterSlotsCommandHandler.java │ ├── manager │ ├── ClusterBlacklistManager.java │ ├── ClusterCommandHandlerManager.java │ ├── ClusterConfigManager.java │ ├── ClusterConnectionManager.java │ ├── ClusterFailoverManager.java │ ├── ClusterManagers.java │ ├── ClusterMessageHandlerManager.java │ ├── ClusterMessageManager.java │ ├── ClusterNodeManager.java │ ├── ClusterSlotManager.java │ ├── ClusterStateManager.java │ └── ReplicationManager.java │ ├── message │ ├── ClusterMessage.java │ ├── ClusterMessageData.java │ ├── ClusterMessageDataFail.java │ ├── ClusterMessageDataGossip.java │ ├── ClusterMessageDataPublish.java │ ├── ClusterMessageDataUpdate.java │ ├── RCmbMessage.java │ └── handler │ │ ├── AbstractClusterMessageHandler.java │ │ ├── ClusterMessageFailHandler.java │ │ ├── ClusterMessageFailoverAuthAckHandler.java │ │ ├── ClusterMessageFailoverAuthRequestHandler.java │ │ ├── ClusterMessageHandler.java │ │ ├── ClusterMessageMFStartHandler.java │ │ ├── ClusterMessageMeetHandler.java │ │ ├── ClusterMessagePingHandler.java │ │ ├── ClusterMessagePongHandler.java │ │ ├── ClusterMessagePublishHandler.java │ │ └── ClusterMessageUpdateHandler.java │ ├── state │ ├── ClusterLink.java │ ├── ClusterNode.java │ ├── ClusterNodeFailReport.java │ ├── ClusterState.java │ ├── NodeStates.java │ └── ServerState.java │ ├── storage │ ├── DefaultStorageEngine.java │ └── StorageEngine.java │ └── util │ ├── CRC16.java │ ├── CRC64.java │ ├── Iterators.java │ ├── Tuples.java │ ├── collection │ └── ByteMap.java │ ├── concurrent │ ├── executor │ │ ├── ExecutorListener.java │ │ ├── ListenableExecutors.java │ │ ├── ListenableScheduledThreadPoolExecutor.java │ │ └── ListenableThreadPoolExecutor.java │ └── future │ │ ├── AbstractCompletableFuture.java │ │ ├── CompletableFuture.java │ │ ├── FutureListener.java │ │ ├── ListenableChannelFuture.java │ │ ├── ListenableFuture.java │ │ ├── ListenableRunnableFuture.java │ │ └── ListenableScheduledFuture.java │ ├── net │ ├── AbstractNioBootstrap.java │ ├── ConnectionStatus.java │ ├── NetworkConfiguration.java │ ├── NioAcceptor.java │ ├── NioBootstrap.java │ ├── NioBootstrapImpl.java │ ├── NioInitiator.java │ ├── exceptions │ │ ├── OverloadException.java │ │ └── TransportException.java │ ├── session │ │ ├── DefaultSession.java │ │ └── Session.java │ └── transport │ │ ├── AbstractTransport.java │ │ ├── NioAcceptorTransport.java │ │ ├── NioInitiatorTransport.java │ │ ├── Transport.java │ │ └── TransportListener.java │ └── type │ ├── Tuple1.java │ ├── Tuple2.java │ ├── Tuple3.java │ ├── Tuple4.java │ └── Tuple5.java └── test ├── java └── com │ └── moilioncircle │ └── redis │ └── cluster │ └── watchdog │ ├── ClusterWatchdogTest.java │ ├── ReplicationTest.java │ ├── SimpleReplicationListener.java │ └── storage │ └── RedisStorageEngine.java └── resources └── log4j2.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.ear 17 | *.zip 18 | *.tar.gz 19 | *.rar 20 | 21 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 22 | hs_err_pid* 23 | /*.conf 24 | 25 | # Eclipse 26 | /.project 27 | /.classpath 28 | 29 | # Idea 30 | /redis-cluster-watchdog.iml 31 | /.idea 32 | /target 33 | 34 | *.conf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Redis-cluster-watchdog 2 | 3 | `redis-cluster-watchdog` can pretend as a redis cluster node which accept `RCmb(Redis Cluster message bus)` message and play with redis cluster. 4 | 5 | # Run an example 6 | 7 | ## Create a redis cluster 8 | 9 | ```java 10 | 11 | $wget https://github.com/antirez/redis/archive/4.0.0.tar.gz 12 | $tar -xvzf 4.0.0.tar.gz 13 | $cd redis-4.0.0 14 | $make MALLOC=libc 15 | $cp src/redis-trib.rb /usr/local/bin/ 16 | $gem install redis 17 | $cd utils/create-cluster 18 | $./create-cluster start 19 | $./create-cluster create 20 | 21 | ``` 22 | 23 | ## Run redis-cluster-watchdog 24 | 25 | ```java 26 | 27 | public static void main(String[] args) { 28 | ClusterWatchdog watchdog = new RedisClusterWatchdog(ClusterConfiguration.defaultSetting().setClusterAnnouncePort(10001)); 29 | watchdog.start(); 30 | } 31 | 32 | ``` 33 | 34 | ## Add redis-cluster-watchdog to redis cluster as a normal node 35 | 36 | ```java 37 | 38 | $cd /path/to/redis-4.0.0/src 39 | $./redis-cli -p 10001 40 | 127.0.0.1:10001>cluster meet 127.0.0.1 30001 41 | 42 | ``` 43 | 44 | ## Supported commands 45 | 46 | `CLUSTER MEET ip port ` 47 | `CLUSTER NODES` 48 | `CLUSTER MYID` 49 | `CLUSTER SLOTS` 50 | `CLUSTER BUMPEPOCH` 51 | `CLUSTER INFO` 52 | `CLUSTER SAVECONFIG` 53 | `CLUSTER KEYSLOT` 54 | `CLUSTER FORGET nodename` 55 | `CLUSTER REPLICATE nodename` 56 | `CLUSTER SLAVES nodename` 57 | `CLUSTER COUNT-FAILURE-REPORTS nodename` 58 | `CLUSTER SET-CONFIG-EPOCH epoch` 59 | `CLUSTER RESET ` 60 | `CLUSTER ADDSLOTS slot ` 61 | `CLUSTER DELSLOTS slot ` 62 | `CLUSTER SETSLOT slot MIGRATING nodename` 63 | `CLUSTER SETSLOT slot IMPORTING nodename` 64 | `CLUSTER SETSLOT slot STABLE` 65 | `CLUSTER SETSLOT slot NODE nodename` 66 | `CLUSTER GETKEYSINSLOT slot count` 67 | `CLUSTER COUNTKEYSINSLOT slot` 68 | 69 | ## Supported redis-trib.rb command 70 | 71 | `redis-trib.rb create <--replicas N> host1:port1 ... hostN:portN` 72 | `redis-trib.rb check host:port` 73 | `redis-trib.rb info host:port` 74 | `redis-trib.rb rebalance <--weight N> --auto-weights --threshold N --use-empty-masters --simulate --timeout milliseconds --pipeline N` 75 | `redis-trib.rb reshard --from arg --to nodename --slots N --yes --timeout milliseconds --pipeline N host:port` 76 | `redis-trib.rb add-node <--slave --master-id masterid> new_host:new_port existing_host:existing_port` 77 | `redis-trib.rb del-node host:port node_id` 78 | `redis-trib.rb call host:port command arg arg .. arg` 79 | `redis-trib.rb set-timeout host:port milliseconds` 80 | `redis-trib.rb import --from host:port <--copy> <--replace> host:port` 81 | `redis-trib.rb help` 82 | 83 | ## Listeners 84 | 85 | `ReplicationListener` 86 | `ClusterNodeListener` 87 | `ClusterStateListener` 88 | `ClusterConfigListener` 89 | 90 | # Have fun!! -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/AbstractClusterWatchdog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.CommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.storage.StorageEngine; 22 | 23 | /** 24 | * @author Leon Chen 25 | * @since 1.0.0 26 | */ 27 | public abstract class AbstractClusterWatchdog implements ClusterWatchdog { 28 | 29 | protected final ClusterManagers managers; 30 | protected final ClusterConfiguration configuration; 31 | 32 | protected AbstractClusterWatchdog(ClusterConfiguration configuration) { 33 | this.configuration = configuration.validate(); 34 | this.managers = new ClusterManagers(configuration, this); 35 | } 36 | 37 | @Override 38 | public void setStorageEngine(StorageEngine engine) { 39 | managers.setStorageEngine(engine); 40 | } 41 | 42 | @Override 43 | public ClusterConfiguration getClusterConfiguration() { 44 | return this.configuration; 45 | } 46 | 47 | @Override 48 | public CommandHandler addCommandHandler(String name, CommandHandler handler) { 49 | return managers.addCommandHandler(name, handler); 50 | } 51 | 52 | @Override 53 | public ClusterNodeListener setClusterNodeListener(ClusterNodeListener clusterNodeListener) { 54 | return managers.setClusterNodeListener(clusterNodeListener); 55 | } 56 | 57 | @Override 58 | public ReplicationListener setReplicationListener(ReplicationListener replicationListener) { 59 | return managers.setReplicationListener(replicationListener); 60 | } 61 | 62 | @Override 63 | public ClusterStateListener setClusterStateListener(ClusterStateListener clusterStateListener) { 64 | return managers.setClusterStateListener(clusterStateListener); 65 | } 66 | 67 | @Override 68 | public ClusterConfigListener setClusterConfigListener(ClusterConfigListener clusterConfigListener) { 69 | return managers.setClusterConfigListener(clusterConfigListener); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ClusterConfigListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public interface ClusterConfigListener { 24 | 25 | void onConfigChanged(ClusterConfigInfo info); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ClusterConfigurationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class ClusterConfigurationException extends RuntimeException { 24 | private static final long serialVersionUID = 1L; 25 | 26 | public ClusterConfigurationException() { 27 | super(); 28 | } 29 | 30 | public ClusterConfigurationException(String message) { 31 | super(message); 32 | } 33 | 34 | public ClusterConfigurationException(Throwable cause) { 35 | super(cause); 36 | } 37 | 38 | public ClusterConfigurationException(String message, Throwable c) { 39 | super(message, c); 40 | } 41 | 42 | protected ClusterConfigurationException(String message, Throwable c, boolean suppression, boolean writable) { 43 | super(message, c, suppression, writable); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ClusterConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class ClusterConstants { 24 | 25 | /** 26 | * slot 27 | */ 28 | public static final int CLUSTER_SLOTS = 16384; 29 | 30 | public static final int CLUSTER_SLOTS_BYTES = 2048; 31 | 32 | /** 33 | * node state 34 | */ 35 | public static final int CLUSTER_NODE_MASTER = 1; 36 | 37 | public static final int CLUSTER_NODE_SLAVE = 2; 38 | 39 | public static final int CLUSTER_NODE_PFAIL = 4; 40 | 41 | public static final int CLUSTER_NODE_FAIL = 8; 42 | 43 | public static final int CLUSTER_NODE_MYSELF = 16; 44 | 45 | public static final int CLUSTER_NODE_HANDSHAKE = 32; 46 | 47 | public static final int CLUSTER_NODE_NOADDR = 64; 48 | 49 | public static final int CLUSTER_NODE_MEET = 128; 50 | 51 | public static final int CLUSTER_NODE_MIGRATE_TO = 256; 52 | 53 | /** 54 | * message type 55 | */ 56 | public static final int CLUSTERMSG_TYPE_PING = 0; 57 | 58 | public static final int CLUSTERMSG_TYPE_PONG = 1; 59 | 60 | public static final int CLUSTERMSG_TYPE_MEET = 2; 61 | 62 | public static final int CLUSTERMSG_TYPE_FAIL = 3; 63 | 64 | public static final int CLUSTERMSG_TYPE_PUBLISH = 4; 65 | 66 | public static final int CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST = 5; 67 | 68 | public static final int CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK = 6; 69 | 70 | public static final int CLUSTERMSG_TYPE_UPDATE = 7; 71 | 72 | public static final int CLUSTERMSG_TYPE_MFSTART = 8; 73 | 74 | public static final int CLUSTERMSG_TYPE_COUNT = 9; 75 | 76 | /** 77 | * time related 78 | */ 79 | public static final int CLUSTER_BLACKLIST_TTL = 60000; 80 | 81 | public static final int CLUSTER_WRITABLE_DELAY = 2000; 82 | 83 | public static final int CLUSTER_MIN_REJOIN_DELAY = 500; 84 | 85 | public static final int CLUSTER_MAX_REJOIN_DELAY = 5000; 86 | 87 | public static final int CLUSTER_FAIL_UNDO_TIME_MULTI = 2; 88 | 89 | public static final int CLUSTER_SLAVE_MIGRATION_DELAY = 5000; 90 | 91 | public static final int CLUSTER_FAIL_REPORT_VALIDITY_MULTI = 2; 92 | 93 | /** 94 | * other 95 | */ 96 | public static final int CLUSTER_BROADCAST_ALL = 0; 97 | 98 | public static final int CLUSTER_PORT_INCR = 10000; 99 | 100 | public static final int CLUSTER_BROADCAST_LOCAL_SLAVES = 1; 101 | 102 | public static final int CLUSTERMSG_FLAG0_FORCEACK = (1 << 1); 103 | 104 | /** 105 | * ip name 106 | */ 107 | public static final byte[] CLUSTER_NODE_NULL_IP = new byte[46]; 108 | 109 | public static final byte[] CLUSTER_NODE_NULL_NAME = new byte[40]; 110 | 111 | public static final int CLUSTER_NAME_LEN = CLUSTER_NODE_NULL_NAME.length; 112 | 113 | public static final char[] HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; 114 | } 115 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ClusterNodeListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public interface ClusterNodeListener { 24 | 25 | void onNodeAdded(ClusterNodeInfo node); 26 | 27 | void onNodeDeleted(ClusterNodeInfo node); 28 | 29 | void onNodeFailed(ClusterNodeInfo failed); 30 | 31 | void onNodePFailed(ClusterNodeInfo pFailed); 32 | 33 | void onUnsetNodeFailed(ClusterNodeInfo failed); 34 | 35 | void onUnsetNodePFailed(ClusterNodeInfo pFailed); 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ClusterState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public enum ClusterState { 24 | CLUSTER_OK((byte) 0, "ok"), 25 | CLUSTER_FAIL((byte) 1, "fail"); 26 | 27 | private final byte state; 28 | private final String display; 29 | 30 | ClusterState(byte state, String display) { 31 | this.state = state; 32 | this.display = display; 33 | } 34 | 35 | public static ClusterState valueOf(byte state) { 36 | if (state == 0) return CLUSTER_OK; 37 | else if (state == 1) return CLUSTER_FAIL; 38 | else throw new UnsupportedOperationException(String.valueOf(state)); 39 | } 40 | 41 | public byte getState() { 42 | return state; 43 | } 44 | 45 | public String getDisplay() { 46 | return display; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ClusterStateListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public interface ClusterStateListener { 24 | 25 | void onStateChanged(ClusterState state); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ClusterWatchdog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.CommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.storage.StorageEngine; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public interface ClusterWatchdog extends Resourcable { 27 | 28 | void setStorageEngine(StorageEngine engine); 29 | 30 | ClusterConfiguration getClusterConfiguration(); 31 | 32 | CommandHandler addCommandHandler(String name, CommandHandler handler); 33 | 34 | ClusterNodeListener setClusterNodeListener(ClusterNodeListener clusterNodeListener); 35 | 36 | ReplicationListener setReplicationListener(ReplicationListener replicationListener); 37 | 38 | ClusterStateListener setClusterStateListener(ClusterStateListener clusterStateListener); 39 | 40 | ClusterConfigListener setClusterConfigListener(ClusterConfigListener clusterConfigListener); 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/RedisClusterWatchdog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * @author Leon Chen 23 | * @since 1.0.0 24 | */ 25 | public class RedisClusterWatchdog extends AbstractClusterWatchdog { 26 | 27 | protected final Resourcable server; 28 | protected final Resourcable gossip; 29 | 30 | public RedisClusterWatchdog() { 31 | this(ClusterConfiguration.defaultSetting()); 32 | } 33 | 34 | public RedisClusterWatchdog(ClusterConfiguration configuration) { 35 | super(configuration); 36 | this.server = new ThinServer(managers); 37 | this.gossip = new ThinGossip(managers); 38 | } 39 | 40 | @Override 41 | public void start() { 42 | Resourcable.startQuietly(managers); 43 | Resourcable.startQuietly(server); 44 | Resourcable.startQuietly(gossip); 45 | } 46 | 47 | @Override 48 | public void stop() { 49 | stop(0, TimeUnit.MILLISECONDS); 50 | } 51 | 52 | @Override 53 | public void stop(long timeout, TimeUnit unit) { 54 | try { 55 | managers.cron.shutdown(); 56 | managers.cron.awaitTermination(timeout, unit); 57 | } catch (InterruptedException e) { 58 | Thread.currentThread().interrupt(); 59 | } 60 | Resourcable.stopQuietly(gossip, timeout, unit); 61 | Resourcable.stopQuietly(server, timeout, unit); 62 | Resourcable.stopQuietly(managers, timeout, unit); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ReplicationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.storage.StorageEngine; 20 | 21 | /** 22 | * @author Leon Chen 23 | * @since 1.0.0 24 | */ 25 | public interface ReplicationListener { 26 | 27 | long onGetSlaveOffset(); 28 | 29 | void onUnsetReplication(StorageEngine engine); 30 | 31 | void onSetReplication(String ip, int port, StorageEngine engine); 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/Resourcable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | import java.util.concurrent.TimeUnit; 20 | 21 | /** 22 | * @author Leon Chen 23 | * @since 1.0.0 24 | */ 25 | public interface Resourcable { 26 | 27 | static void startQuietly(Resourcable resource) { 28 | try { 29 | resource.start(); 30 | } catch (Throwable e) { 31 | } 32 | } 33 | 34 | static void stopQuietly(Resourcable resource) { 35 | stopQuietly(resource, 0, TimeUnit.MILLISECONDS); 36 | } 37 | 38 | static void stopQuietly(Resourcable resource, long timeout, TimeUnit unit) { 39 | try { 40 | resource.stop(timeout, unit); 41 | } catch (Throwable e) { 42 | } 43 | } 44 | 45 | void start(); 46 | 47 | void stop(); 48 | 49 | void stop(long timeout, TimeUnit unit); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/ThinServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.codec.RedisDecoder; 20 | import com.moilioncircle.redis.cluster.watchdog.codec.RedisEncoder; 21 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.NioBootstrapImpl; 23 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 24 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.TransportListener; 25 | import org.apache.commons.logging.Log; 26 | import org.apache.commons.logging.LogFactory; 27 | 28 | import java.util.concurrent.ExecutionException; 29 | import java.util.concurrent.TimeUnit; 30 | import java.util.concurrent.TimeoutException; 31 | 32 | /** 33 | * @author Leon Chen 34 | * @since 1.0.0 35 | */ 36 | public class ThinServer implements Resourcable { 37 | 38 | private static final Log logger = LogFactory.getLog(ThinServer.class); 39 | 40 | private ClusterManagers managers; 41 | private ClusterConfiguration configuration; 42 | private volatile NioBootstrapImpl acceptor; 43 | 44 | public ThinServer(ClusterManagers managers) { 45 | this.managers = managers; 46 | this.configuration = managers.configuration; 47 | } 48 | 49 | @Override 50 | public void start() { 51 | acceptor = new NioBootstrapImpl<>(); 52 | acceptor.setEncoder(RedisEncoder::new); 53 | acceptor.setDecoder(RedisDecoder::new); 54 | acceptor.setup(); 55 | acceptor.setTransportListener(new RedisTransportListener()); 56 | try { 57 | acceptor.connect(null, configuration.getClusterAnnouncePort()).get(); 58 | } catch (InterruptedException | ExecutionException e) { 59 | if (e instanceof InterruptedException) Thread.currentThread().interrupt(); 60 | else throw new UnsupportedOperationException(e.getCause()); 61 | } 62 | } 63 | 64 | @Override 65 | public void stop() { 66 | stop(0, TimeUnit.MILLISECONDS); 67 | } 68 | 69 | @Override 70 | public void stop(long timeout, TimeUnit unit) { 71 | NioBootstrapImpl acceptor = this.acceptor; 72 | try { 73 | if (acceptor != null) acceptor.shutdown().get(timeout, unit); 74 | } catch (InterruptedException e) { 75 | Thread.currentThread().interrupt(); 76 | } catch (ExecutionException e) { 77 | logger.error("unexpected error", e.getCause()); 78 | } catch (TimeoutException e) { 79 | logger.error("stop timeout error", e); 80 | } 81 | } 82 | 83 | private class RedisTransportListener extends TransportListener.Adaptor { 84 | @Override 85 | public void onConnected(Transport t) { 86 | if (configuration.isVerbose()) logger.info("[acceptor] > " + t); 87 | } 88 | 89 | @Override 90 | public void onMessage(Transport t, byte[][] message) { 91 | managers.commands.handleCommand(t, message); 92 | } 93 | 94 | @Override 95 | public void onDisconnected(Transport t, Throwable cause) { 96 | if (configuration.isVerbose()) logger.info("[acceptor] < " + t); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/Version.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public enum Version { 24 | 25 | /** 26 | * redis-3.0 ~ redis-3.2. 27 | */ 28 | PROTOCOL_V0(0), 29 | 30 | /** 31 | * redis-4.0 or greater. 32 | */ 33 | PROTOCOL_V1(1); 34 | 35 | private int version; 36 | 37 | Version(int version) { 38 | this.version = version; 39 | } 40 | 41 | public static Version valueOf(int version) { 42 | if (version == 0) return PROTOCOL_V0; 43 | else if (version == 1) return PROTOCOL_V1; 44 | else throw new UnsupportedOperationException("version: " + version); 45 | } 46 | 47 | public int getVersion() { 48 | return version; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/codec/RedisEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.codec; 18 | 19 | import io.netty.buffer.ByteBuf; 20 | import io.netty.channel.ChannelHandlerContext; 21 | import io.netty.handler.codec.MessageToByteEncoder; 22 | 23 | /** 24 | * @author Leon Chen 25 | * @since 1.0.0 26 | */ 27 | public class RedisEncoder extends MessageToByteEncoder { 28 | @Override 29 | protected void encode(ChannelHandlerContext ctx, byte[] cmd, ByteBuf out) throws Exception { 30 | out.writeBytes(cmd); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/AbstractCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.ClusterConfiguration; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ServerState; 22 | import com.moilioncircle.redis.cluster.watchdog.storage.StorageEngine; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public abstract class AbstractCommandHandler extends CommandHandler.Adaptor { 29 | 30 | protected final ServerState server; 31 | protected final ClusterManagers managers; 32 | 33 | public AbstractCommandHandler(ClusterManagers managers) { 34 | this.managers = managers; 35 | this.server = managers.server; 36 | } 37 | 38 | @Override 39 | public StorageEngine getStorageEngine() { 40 | return managers.engine; 41 | } 42 | 43 | @Override 44 | public void setStorageEngine(StorageEngine storageEngine) { 45 | throw new UnsupportedOperationException(); 46 | } 47 | 48 | @Override 49 | public ClusterConfiguration getConfiguration() { 50 | return managers.configuration; 51 | } 52 | 53 | @Override 54 | public void setConfiguration(ClusterConfiguration configuration) { 55 | throw new UnsupportedOperationException(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/CommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.ClusterConfiguration; 20 | import com.moilioncircle.redis.cluster.watchdog.storage.StorageEngine; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | /** 24 | * @author Leon Chen 25 | * @since 1.0.0 26 | */ 27 | public interface CommandHandler { 28 | StorageEngine getStorageEngine(); 29 | 30 | void setStorageEngine(StorageEngine engine); 31 | 32 | ClusterConfiguration getConfiguration(); 33 | 34 | void setConfiguration(ClusterConfiguration configuration); 35 | 36 | void handle(Transport t, String[] message, byte[][] rawMessage); 37 | 38 | abstract class Adaptor implements CommandHandler { 39 | protected StorageEngine storageEngine; 40 | protected ClusterConfiguration configuration; 41 | 42 | @Override 43 | public StorageEngine getStorageEngine() { 44 | return storageEngine; 45 | } 46 | 47 | @Override 48 | public void setStorageEngine(StorageEngine storageEngine) { 49 | this.storageEngine = storageEngine; 50 | } 51 | 52 | @Override 53 | public ClusterConfiguration getConfiguration() { 54 | return configuration; 55 | } 56 | 57 | @Override 58 | public void setConfiguration(ClusterConfiguration configuration) { 59 | this.configuration = configuration; 60 | } 61 | 62 | /** 63 | * 64 | */ 65 | protected void reply(Transport t, String message) { 66 | reply(t, message.getBytes()); 67 | } 68 | 69 | protected void reply(Transport t, byte[] message) { 70 | t.write("+".getBytes(), false); 71 | t.write(message, false); 72 | t.write("\r\n".getBytes(), true); 73 | } 74 | 75 | protected void replyNumber(Transport t, long number) { 76 | t.write((":" + number + "\r\n").getBytes(), true); 77 | } 78 | 79 | protected void replyBulk(Transport t, String message) { 80 | replyBulk(t, message.getBytes()); 81 | } 82 | 83 | protected void replyBulk(Transport t, byte[] message) { 84 | t.write(("$" + message.length + "\r\n").getBytes(), false); 85 | t.write(message, false); 86 | t.write("\r\n".getBytes(), true); 87 | } 88 | 89 | protected void replyError(Transport t, String message) { 90 | replyError(t, message.getBytes()); 91 | } 92 | 93 | protected void replyError(Transport t, byte[] message) { 94 | t.write("-".getBytes(), false); 95 | t.write(message, false); 96 | t.write("\r\n".getBytes(), true); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/ConfigCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | import static java.lang.Long.parseLong; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class ConfigCommandHandler extends AbstractCommandHandler { 29 | 30 | public ConfigCommandHandler(ClusterManagers managers) { 31 | super(managers); 32 | } 33 | 34 | @Override 35 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 36 | if (message.length == 4 && message[1] != null && message[1].equalsIgnoreCase("set")) { 37 | if (message[2] == null || !message[2].equalsIgnoreCase("cluster-node-timeout")) { 38 | replyError(t, "ERR wrong number of arguments for 'config' command"); 39 | return; 40 | } 41 | if (message[3] == null) { 42 | replyError(t, "ERR wrong number of arguments for 'config' command"); 43 | return; 44 | } 45 | try { 46 | long timeout = parseLong(message[3]); 47 | managers.configuration.setClusterNodeTimeout(timeout); 48 | reply(t, "OK"); 49 | } catch (Exception e) { 50 | replyError(t, "ERR wrong number of arguments for 'config' command"); 51 | } 52 | } else if (message.length == 2 && message[1] != null && message[1].equalsIgnoreCase("rewrite")) { 53 | reply(t, "OK"); 54 | } else { 55 | replyError(t, "ERR wrong number of arguments for 'config' command"); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/DBSizeCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class DBSizeCommandHandler extends AbstractCommandHandler { 27 | 28 | public DBSizeCommandHandler(ClusterManagers managers) { 29 | super(managers); 30 | } 31 | 32 | @Override 33 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 34 | if (message.length == 1) replyNumber(t, managers.engine.size()); 35 | else replyError(t, "ERR wrong number of arguments for 'dbsize' command"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/DefaultCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.cluster.ClusterCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | import java.util.Map; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class DefaultCommandHandler extends AbstractCommandHandler { 31 | 32 | private Map handlers = new ConcurrentHashMap<>(); 33 | 34 | public DefaultCommandHandler(ClusterManagers managers) { 35 | super(managers); 36 | addCommandHandler("ping", new PingCommandHandler(managers)); 37 | addCommandHandler("info", new InfoCommandHandler(managers)); 38 | addCommandHandler("dump", new DumpCommandHandler(managers)); 39 | addCommandHandler("dbsize", new DBSizeCommandHandler(managers)); 40 | addCommandHandler("config", new ConfigCommandHandler(managers)); 41 | addCommandHandler("select", new SelectCommandHandler(managers)); 42 | addCommandHandler("cluster", new ClusterCommandHandler(managers)); 43 | addCommandHandler("restore", new RestoreCommandHandler(managers)); 44 | addCommandHandler("shutdown", new ShutdownCommandHandler(managers)); 45 | addCommandHandler("readonly", new ReadonlyCommandHandler(managers)); 46 | addCommandHandler("readwrite", new ReadWriteCommandHandler(managers)); 47 | addCommandHandler("restore-asking", new RestoreCommandHandler(managers)); 48 | } 49 | 50 | public CommandHandler get(String name) { 51 | return handlers.get(name.toLowerCase()); 52 | } 53 | 54 | public CommandHandler addCommandHandler(String name, CommandHandler handler) { 55 | return handlers.put(name.toLowerCase(), handler); 56 | } 57 | 58 | @Override 59 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 60 | if (message.length <= 0 || message[0] == null) { 61 | replyError(t, "ERR Unsupported COMMAND"); 62 | return; 63 | } 64 | CommandHandler handler = get(message[0]); 65 | if (handler == null) { 66 | replyError(t, "ERR Unsupported COMMAND"); 67 | return; 68 | } 69 | handler.handle(t, message, rawMessage); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/DumpCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class DumpCommandHandler extends AbstractCommandHandler { 27 | 28 | public DumpCommandHandler(ClusterManagers managers) { 29 | super(managers); 30 | } 31 | 32 | @Override 33 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 34 | if (rawMessage.length != 2) { 35 | replyError(t, "ERR wrong number of arguments for 'dump' command"); 36 | return; 37 | } 38 | 39 | byte[] key = rawMessage[1]; 40 | if (key == null) { 41 | replyError(t, "ERR Invalid key: null"); 42 | return; 43 | } 44 | replyBulk(t, managers.engine.dump(key)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/InfoCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class InfoCommandHandler extends AbstractCommandHandler { 27 | 28 | public InfoCommandHandler(ClusterManagers managers) { 29 | super(managers); 30 | } 31 | 32 | @Override 33 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 34 | if (message.length == 1 || message.length == 2) { 35 | replyBulk(t, "cluster_enabled:1\r\n"); 36 | } else { 37 | replyError(t, "ERR wrong number of arguments for 'info' command"); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/PingCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class PingCommandHandler extends AbstractCommandHandler { 27 | 28 | public PingCommandHandler(ClusterManagers managers) { 29 | super(managers); 30 | } 31 | 32 | @Override 33 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 34 | if (message.length == 1) { 35 | reply(t, "PONG"); 36 | } else if (message.length == 2 && message[1] != null) { 37 | reply(t, message[1]); 38 | } else { 39 | replyError(t, "ERR wrong number of arguments for 'ping' command"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/ReadWriteCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class ReadWriteCommandHandler extends AbstractCommandHandler { 27 | 28 | public ReadWriteCommandHandler(ClusterManagers managers) { 29 | super(managers); 30 | } 31 | 32 | @Override 33 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 34 | if (message.length != 1) { 35 | replyError(t, "ERR wrong number of arguments for 'readwrite' command"); 36 | return; 37 | } 38 | managers.engine.readonly(false); 39 | reply(t, "OK"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/ReadonlyCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class ReadonlyCommandHandler extends AbstractCommandHandler { 27 | 28 | public ReadonlyCommandHandler(ClusterManagers managers) { 29 | super(managers); 30 | } 31 | 32 | @Override 33 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 34 | if (message.length != 1) { 35 | replyError(t, "ERR wrong number of arguments for 'readonly' command"); 36 | return; 37 | } 38 | managers.engine.readonly(true); 39 | reply(t, "OK"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/RestoreCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | import static java.lang.Long.parseLong; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class RestoreCommandHandler extends AbstractCommandHandler { 29 | 30 | public RestoreCommandHandler(ClusterManagers managers) { 31 | super(managers); 32 | } 33 | 34 | @Override 35 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 36 | if (rawMessage.length != 4 && rawMessage.length != 5) { 37 | replyError(t, "ERR wrong number of arguments for 'restore' command"); 38 | return; 39 | } 40 | 41 | byte[] key = rawMessage[1]; 42 | if (key == null) { 43 | replyError(t, "ERR Invalid key: null"); 44 | return; 45 | } 46 | 47 | long ttl; 48 | try { 49 | ttl = parseLong(message[2]); 50 | } catch (Exception e) { 51 | replyError(t, "ERR Invalid ttl: " + message[2]); 52 | return; 53 | } 54 | if (ttl < 0) { 55 | replyError(t, "ERR Invalid ttl: " + ttl); 56 | return; 57 | } 58 | 59 | byte[] serialized = rawMessage[3]; 60 | if (serialized == null) { 61 | replyError(t, "ERR Invalid serialized-value: null"); 62 | return; 63 | } 64 | 65 | boolean replace; 66 | if (rawMessage.length == 5) { 67 | if (message[4] != null && message[4].equalsIgnoreCase("replace")) { 68 | replace = true; 69 | } else { 70 | replyError(t, "ERR wrong number of arguments for 'restore' command"); 71 | return; 72 | } 73 | } else replace = false; 74 | 75 | long expire = ttl == 0 ? 0 : System.currentTimeMillis() + ttl; 76 | 77 | managers.engine.restore(key, serialized, expire, replace); 78 | reply(t, "OK"); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/SelectCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | import static java.lang.Integer.parseInt; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class SelectCommandHandler extends AbstractCommandHandler { 29 | 30 | public SelectCommandHandler(ClusterManagers managers) { 31 | super(managers); 32 | } 33 | 34 | @Override 35 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 36 | if (message.length != 2) { 37 | replyError(t, "ERR wrong number of arguments for 'select' command"); 38 | return; 39 | } 40 | 41 | try { 42 | int db = parseInt(message[1]); 43 | if (db < 0 || db >= 16) { 44 | replyError(t, "ERR DB index is out of range"); 45 | return; 46 | } 47 | if (db != 0) { 48 | replyError(t, "ERR SELECT is not allowed in cluster mode"); 49 | return; 50 | } 51 | reply(t, "OK"); 52 | } catch (Exception e) { 53 | replyError(t, "ERR Invalid db number:" + message[1]); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/ShutdownCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | import java.util.concurrent.TimeUnit; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class ShutdownCommandHandler extends AbstractCommandHandler { 29 | 30 | public ShutdownCommandHandler(ClusterManagers managers) { 31 | super(managers); 32 | } 33 | 34 | @Override 35 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 36 | if (message.length == 1) { 37 | reply(t, "OK"); 38 | managers.cron.execute(() -> managers.watchdog.stop(10, TimeUnit.SECONDS)); 39 | } else if (message.length == 2 && message[1] != null && (message[1].equalsIgnoreCase("nosave") || message[1].equalsIgnoreCase("save"))) { 40 | reply(t, "OK"); 41 | managers.cron.execute(() -> managers.watchdog.stop(10, TimeUnit.SECONDS)); 42 | } else { 43 | replyError(t, "ERR wrong number of arguments for 'shutdown' command"); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterAddSlotsCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS; 24 | import static java.lang.Integer.parseInt; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class ClusterAddSlotsCommandHandler extends AbstractCommandHandler { 31 | 32 | public ClusterAddSlotsCommandHandler(ClusterManagers managers) { 33 | super(managers); 34 | } 35 | 36 | @Override 37 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 38 | if (message.length < 3) { 39 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 40 | return; 41 | } 42 | byte[] slots = new byte[CLUSTER_SLOTS]; 43 | for (int i = 2; i < message.length; i++) { 44 | try { 45 | int slot = parseInt(message[i]); 46 | if (slot < 0 || slot > CLUSTER_SLOTS) { 47 | replyError(t, "ERR Invalid slot:" + slot); 48 | return; 49 | } 50 | if (server.cluster.slots[slot] != null) { 51 | replyError(t, "ERR Slot " + slot + " is already busy"); 52 | return; 53 | } 54 | if (slots[slot]++ == 1) { 55 | replyError(t, "ERR Slot " + slot + " specified multiple times"); 56 | return; 57 | } 58 | } catch (Exception e) { 59 | replyError(t, "ERR Invalid slot:" + message[i]); 60 | return; 61 | } 62 | } 63 | for (int i = 0; i < CLUSTER_SLOTS; i++) { 64 | if (slots[i] == 0) continue; 65 | if (server.cluster.importing[i] != null) server.cluster.importing[i] = null; 66 | managers.slots.clusterAddSlot(managers.server.myself, i); 67 | } 68 | managers.states.clusterUpdateState(); 69 | reply(t, "OK"); 70 | } 71 | } -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterBumpEpochCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | /** 24 | * @author Leon Chen 25 | * @since 1.0.0 26 | */ 27 | public class ClusterBumpEpochCommandHandler extends AbstractCommandHandler { 28 | 29 | public ClusterBumpEpochCommandHandler(ClusterManagers managers) { 30 | super(managers); 31 | } 32 | 33 | @Override 34 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 35 | if (message.length != 2) { 36 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 37 | return; 38 | } 39 | boolean bumped = managers.states.clusterBumpConfigEpochWithoutConsensus(); 40 | reply(t, (bumped ? "BUMPED" : "STILL") + " " + server.myself.configEpoch); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterCountFailureReportsCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class ClusterCountFailureReportsCommandHandler extends AbstractCommandHandler { 29 | 30 | public ClusterCountFailureReportsCommandHandler(ClusterManagers managers) { 31 | super(managers); 32 | } 33 | 34 | @Override 35 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 36 | if (message.length != 3) { 37 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 38 | return; 39 | } 40 | 41 | ClusterNode node = managers.nodes.clusterLookupNode(message[2]); 42 | if (node == null) { 43 | replyError(t, "ERR Unknown node " + message[2]); 44 | } else replyNumber(t, managers.nodes.clusterNodeFailureReportsCount(node)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterCountKeysInSlotCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | import static java.lang.Integer.parseInt; 24 | 25 | /** 26 | * @author Leon Chen 27 | * @since 1.0.0 28 | */ 29 | public class ClusterCountKeysInSlotCommandHandler extends AbstractCommandHandler { 30 | 31 | public ClusterCountKeysInSlotCommandHandler(ClusterManagers managers) { 32 | super(managers); 33 | } 34 | 35 | @Override 36 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 37 | if (message.length != 3) { 38 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 39 | return; 40 | } 41 | 42 | try { 43 | int slot = parseInt(message[2]); 44 | if (slot <= 0 || slot > 65535) { 45 | replyError(t, "-ERR Invalid slot:" + slot); 46 | return; 47 | } 48 | replyNumber(t, managers.slots.countKeysInSlot(slot)); 49 | } catch (Exception e) { 50 | replyError(t, "ERR Invalid slot:" + message[2]); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterDelSlotsCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS; 24 | import static java.lang.Integer.parseInt; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class ClusterDelSlotsCommandHandler extends AbstractCommandHandler { 31 | 32 | public ClusterDelSlotsCommandHandler(ClusterManagers managers) { 33 | super(managers); 34 | } 35 | 36 | @Override 37 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 38 | if (message.length < 3) { 39 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 40 | return; 41 | } 42 | 43 | byte[] slots = new byte[CLUSTER_SLOTS]; 44 | for (int i = 2; i < message.length; i++) { 45 | try { 46 | int slot = parseInt(message[i]); 47 | if (slot < 0 || slot > CLUSTER_SLOTS) { 48 | replyError(t, "-ERR Invalid slot:" + slot); 49 | return; 50 | } 51 | if (server.cluster.slots[slot] == null) { 52 | replyError(t, "ERR Slot " + slot + " is already unassigned"); 53 | return; 54 | } 55 | if (slots[slot]++ == 1) { 56 | replyError(t, "ERR Slot " + slot + " specified multiple times"); 57 | return; 58 | } 59 | } catch (Exception e) { 60 | replyError(t, "ERR Invalid slot:" + message[i]); 61 | return; 62 | } 63 | } 64 | for (int i = 0; i < CLUSTER_SLOTS; i++) { 65 | if (slots[i] == 0) continue; 66 | if (server.cluster.importing[i] != null) server.cluster.importing[i] = null; 67 | managers.slots.clusterDelSlot(i); 68 | } 69 | managers.states.clusterUpdateState(); 70 | reply(t, "OK"); 71 | } 72 | } -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterFlushSlotsCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | /** 24 | * @author Leon Chen 25 | * @since 1.0.0 26 | */ 27 | public class ClusterFlushSlotsCommandHandler extends AbstractCommandHandler { 28 | 29 | public ClusterFlushSlotsCommandHandler(ClusterManagers managers) { 30 | super(managers); 31 | } 32 | 33 | @Override 34 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 35 | if (message.length != 2) { 36 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 37 | return; 38 | } 39 | 40 | managers.slots.clusterDelNodeSlots(server.myself); 41 | managers.states.clusterUpdateState(); 42 | reply(t, "OK"); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterForgetCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 23 | 24 | import java.util.Objects; 25 | 26 | import static com.moilioncircle.redis.cluster.watchdog.ClusterNodeInfo.valueOf; 27 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsSlave; 28 | 29 | /** 30 | * @author Leon Chen 31 | * @since 1.0.0 32 | */ 33 | public class ClusterForgetCommandHandler extends AbstractCommandHandler { 34 | 35 | public ClusterForgetCommandHandler(ClusterManagers managers) { 36 | super(managers); 37 | } 38 | 39 | @Override 40 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 41 | if (message.length != 3) { 42 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 43 | return; 44 | } 45 | 46 | ClusterNode node = managers.nodes.clusterLookupNode(message[2]); 47 | 48 | if (node == null) { 49 | replyError(t, "ERR Unknown node " + message[2]); 50 | return; 51 | } else if (Objects.equals(node, server.myself)) { 52 | replyError(t, "ERR I tried hard but I can't forget myself..."); 53 | return; 54 | } else if (nodeIsSlave(server.myself) && Objects.equals(server.myself.master, node)) { 55 | replyError(t, "ERR Can't forget my master!"); 56 | return; 57 | } 58 | managers.blacklists.clusterBlacklistAddNode(node); 59 | managers.nodes.clusterDelNode(node); 60 | managers.notifyNodeDeleted(valueOf(node, server.myself)); 61 | managers.states.clusterUpdateState(); 62 | reply(t, "OK"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterGetKeysInSlotCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | import java.util.Iterator; 24 | 25 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS; 26 | import static java.lang.Integer.parseInt; 27 | import static java.lang.Long.parseLong; 28 | 29 | /** 30 | * @author Leon Chen 31 | * @since 1.0.0 32 | */ 33 | public class ClusterGetKeysInSlotCommandHandler extends AbstractCommandHandler { 34 | 35 | public ClusterGetKeysInSlotCommandHandler(ClusterManagers managers) { 36 | super(managers); 37 | } 38 | 39 | @Override 40 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 41 | if (message.length != 4) { 42 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 43 | return; 44 | } 45 | 46 | int slot; 47 | try { 48 | slot = parseInt(message[2]); 49 | } catch (Exception e) { 50 | replyError(t, "ERR Invalid slot:" + message[2]); 51 | return; 52 | } 53 | if (slot < 0 || slot > CLUSTER_SLOTS) { 54 | replyError(t, "ERR Invalid slot:" + slot); 55 | return; 56 | } 57 | 58 | long max; 59 | try { 60 | max = parseLong(message[3]); 61 | } catch (Exception e) { 62 | replyError(t, "ERR Invalid number of keys:" + message[3]); 63 | return; 64 | } 65 | if (max < 0) { 66 | replyError(t, "ERR Invalid number of keys:" + max); 67 | return; 68 | } 69 | 70 | max = Math.min(managers.slots.countKeysInSlot(slot), max); 71 | Iterator it = managers.slots.getKeysInSlot(slot); 72 | if (!it.hasNext()) { 73 | t.write("*0\r\n".getBytes(), true); 74 | return; 75 | } else 76 | t.write(("*" + max + "\r\n").getBytes(), false); 77 | int idx = 0; 78 | while (it.hasNext() && idx++ < max) { 79 | byte[] key = it.next(); 80 | t.write(("$" + key.length + "\r\n").getBytes(), false); 81 | t.write(key, false); 82 | if (it.hasNext()) 83 | t.write("\r\n".getBytes(), false); 84 | else 85 | t.write("\r\n".getBytes(), true); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterKeySlotCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | import static com.moilioncircle.redis.cluster.watchdog.storage.StorageEngine.calcSlot; 24 | 25 | /** 26 | * @author Leon Chen 27 | * @since 1.0.0 28 | */ 29 | public class ClusterKeySlotCommandHandler extends AbstractCommandHandler { 30 | 31 | public ClusterKeySlotCommandHandler(ClusterManagers managers) { 32 | super(managers); 33 | } 34 | 35 | @Override 36 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 37 | if (message.length != 3) { 38 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 39 | return; 40 | } 41 | 42 | replyNumber(t, calcSlot(rawMessage[2])); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterMeetCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | 25 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_PORT_INCR; 26 | import static com.moilioncircle.redis.cluster.watchdog.Version.PROTOCOL_V0; 27 | import static java.lang.Integer.parseInt; 28 | 29 | /** 30 | * @author Leon Chen 31 | * @since 1.0.0 32 | */ 33 | public class ClusterMeetCommandHandler extends AbstractCommandHandler { 34 | 35 | private static final Log logger = LogFactory.getLog(ClusterMeetCommandHandler.class); 36 | 37 | public ClusterMeetCommandHandler(ClusterManagers managers) { 38 | super(managers); 39 | } 40 | 41 | @Override 42 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 43 | if (message.length != 4 && message.length != 5) { 44 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 45 | return; 46 | } 47 | 48 | int port, busPort; 49 | try { 50 | port = parseInt(message[3]); 51 | } catch (Exception e) { 52 | replyError(t, "ERR Invalid port:" + message[3]); 53 | return; 54 | } 55 | 56 | if (message.length == 5) { 57 | try { 58 | busPort = parseInt(message[4]); 59 | } catch (Exception e) { 60 | replyError(t, "ERR Invalid bus port:" + message[4]); 61 | return; 62 | } 63 | } else busPort = port + CLUSTER_PORT_INCR; 64 | 65 | if (managers.configuration.getVersion() == PROTOCOL_V0) { 66 | busPort = port + CLUSTER_PORT_INCR; 67 | logger.warn("bus port force set to " + busPort + ", cause cluster protocol version is 0."); 68 | } 69 | 70 | if (port <= 0 || port > 65535) { 71 | replyError(t, "ERR Invalid port:" + port); 72 | return; 73 | } 74 | if (busPort <= 0 || busPort > 65535) { 75 | replyError(t, "ERR Invalid bus port:" + busPort); 76 | return; 77 | } 78 | if (message[2] == null || message[2].length() == 0) { 79 | replyError(t, "ERR Invalid address:" + message[2]); 80 | return; 81 | } 82 | 83 | if (managers.nodes.clusterStartHandshake(message[2], port, busPort)) reply(t, "OK"); 84 | else replyError(t, "ERR Invalid node address specified:" + message[2] + ":" + message[3]); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterMyIDCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | /** 24 | * @author Leon Chen 25 | * @since 1.0.0 26 | */ 27 | public class ClusterMyIDCommandHandler extends AbstractCommandHandler { 28 | 29 | public ClusterMyIDCommandHandler(ClusterManagers managers) { 30 | super(managers); 31 | } 32 | 33 | @Override 34 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 35 | if (message.length != 2) { 36 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 37 | return; 38 | } 39 | 40 | reply(t, server.myself.name); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterNodesCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConfigInfo.valueOf; 24 | import static com.moilioncircle.redis.cluster.watchdog.manager.ClusterConfigManager.clusterGenNodesDescription; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class ClusterNodesCommandHandler extends AbstractCommandHandler { 31 | 32 | public ClusterNodesCommandHandler(ClusterManagers managers) { 33 | super(managers); 34 | } 35 | 36 | @Override 37 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 38 | if (message.length != 2) { 39 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 40 | return; 41 | } 42 | replyBulk(t, clusterGenNodesDescription(valueOf(server.cluster), 0, managers.configuration.getVersion())); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterReplicateCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 23 | 24 | import java.util.Objects; 25 | 26 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsMaster; 27 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsSlave; 28 | 29 | /** 30 | * @author Leon Chen 31 | * @since 1.0.0 32 | */ 33 | public class ClusterReplicateCommandHandler extends AbstractCommandHandler { 34 | 35 | public ClusterReplicateCommandHandler(ClusterManagers managers) { 36 | super(managers); 37 | } 38 | 39 | @Override 40 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 41 | if (message.length != 3) { 42 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 43 | return; 44 | } 45 | 46 | ClusterNode node = managers.nodes.clusterLookupNode(message[2]); 47 | if (node == null) { 48 | replyError(t, "ERR Unknown node " + message[2]); 49 | return; 50 | } 51 | if (Objects.equals(node, server.myself)) { 52 | replyError(t, "ERR Can't replicate myself"); 53 | return; 54 | } 55 | if (nodeIsSlave(node)) { 56 | replyError(t, "ERR I can only replicate a master, not a slave."); 57 | return; 58 | } 59 | if (nodeIsMaster(server.myself) && (server.myself.assignedSlots != 0)) { 60 | replyError(t, "ERR To set a master the node must be empty and without assigned slots."); 61 | return; 62 | } 63 | 64 | managers.nodes.clusterSetMyMasterTo(node); 65 | managers.states.clusterUpdateState(); 66 | reply(t, "OK"); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterResetCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | import java.util.ArrayList; 27 | import java.util.List; 28 | import java.util.Objects; 29 | 30 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS; 31 | import static com.moilioncircle.redis.cluster.watchdog.ClusterNodeInfo.valueOf; 32 | import static com.moilioncircle.redis.cluster.watchdog.manager.ClusterNodeManager.getRandomHexChars; 33 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsSlave; 34 | 35 | /** 36 | * @author Leon Chen 37 | * @since 1.0.0 38 | */ 39 | public class ClusterResetCommandHandler extends AbstractCommandHandler { 40 | 41 | private static final Log logger = LogFactory.getLog(ClusterResetCommandHandler.class); 42 | 43 | public ClusterResetCommandHandler(ClusterManagers managers) { 44 | super(managers); 45 | } 46 | 47 | @Override 48 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 49 | if (message.length != 2 && message.length != 3) { 50 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 51 | return; 52 | } 53 | 54 | boolean hard = false; 55 | if (message.length == 3) { 56 | if (message[2] != null && message[2].equalsIgnoreCase("hard")) hard = true; 57 | else if (message[2] != null && message[2].equalsIgnoreCase("soft")) hard = false; 58 | else { 59 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 60 | return; 61 | } 62 | } 63 | clusterReset(hard); 64 | reply(t, "OK"); 65 | } 66 | 67 | public void clusterReset(boolean hard) { 68 | if (nodeIsSlave(server.myself)) { 69 | managers.nodes.clusterSetNodeAsMaster(server.myself); 70 | managers.replications.replicationUnsetMaster(); 71 | } 72 | managers.slots.clusterCloseAllSlots(); 73 | for (int i = 0; i < CLUSTER_SLOTS; i++) 74 | managers.slots.clusterDelSlot(i); 75 | 76 | List nodes = new ArrayList<>(server.cluster.nodes.values()); 77 | nodes.stream().filter(e -> !Objects.equals(e, server.myself)).forEach(e -> { 78 | managers.nodes.clusterDelNode(e); 79 | managers.notifyNodeDeleted(valueOf(e, server.myself)); 80 | }); 81 | 82 | if (!hard) return; 83 | server.myself.configEpoch = 0; 84 | server.cluster.currentEpoch = 0; 85 | server.cluster.lastVoteEpoch = 0; 86 | managers.notifyNodeDeleted(valueOf(server.myself, server.myself)); 87 | managers.nodes.clusterRenameNode(server.myself, getRandomHexChars()); 88 | logger.info("Node hard reset, now I'm " + server.myself.name); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterSaveConfigCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | 23 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConfigInfo.valueOf; 24 | 25 | /** 26 | * @author Leon Chen 27 | * @since 1.0.0 28 | */ 29 | public class ClusterSaveConfigCommandHandler extends AbstractCommandHandler { 30 | 31 | public ClusterSaveConfigCommandHandler(ClusterManagers managers) { 32 | super(managers); 33 | } 34 | 35 | @Override 36 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 37 | if (message.length != 2) { 38 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 39 | return; 40 | } 41 | if (!managers.configs.clusterSaveConfig(valueOf(server.cluster), true)) { 42 | replyError(t, "ERR saving the cluster node config"); 43 | return; 44 | } 45 | reply(t, "OK"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterSetConfigEpochCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | 25 | import static java.lang.Long.parseLong; 26 | 27 | /** 28 | * @author Leon Chen 29 | * @since 1.0.0 30 | */ 31 | public class ClusterSetConfigEpochCommandHandler extends AbstractCommandHandler { 32 | 33 | private static final Log logger = LogFactory.getLog(ClusterSetConfigEpochCommandHandler.class); 34 | 35 | public ClusterSetConfigEpochCommandHandler(ClusterManagers managers) { 36 | super(managers); 37 | } 38 | 39 | @Override 40 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 41 | if (message.length != 3) { 42 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 43 | return; 44 | } 45 | 46 | long epoch; 47 | try { 48 | epoch = parseLong(message[2]); 49 | } catch (Exception e) { 50 | replyError(t, "ERR Invalid config epoch specified: " + message[2]); 51 | return; 52 | } 53 | 54 | if (epoch < 0) { 55 | replyError(t, "ERR Invalid config epoch specified: " + epoch); 56 | } else if (server.myself.configEpoch != 0) { 57 | replyError(t, "ERR Node config epoch is already non-zero"); 58 | } else if (server.cluster.nodes.size() > 1) { 59 | replyError(t, "ERR The user can assign a config epoch only when the node does not know any other node."); 60 | } else { 61 | server.myself.configEpoch = epoch; 62 | logger.info("configEpoch set to " + server.myself.configEpoch + " via CLUSTER SET-CONFIG-EPOCH"); 63 | if (server.cluster.currentEpoch < epoch) server.cluster.currentEpoch = epoch; 64 | managers.states.clusterUpdateState(); 65 | reply(t, "OK"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/command/cluster/ClusterSlavesCommandHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.command.cluster; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.ClusterConfigInfo; 20 | import com.moilioncircle.redis.cluster.watchdog.ClusterNodeInfo; 21 | import com.moilioncircle.redis.cluster.watchdog.Version; 22 | import com.moilioncircle.redis.cluster.watchdog.command.AbstractCommandHandler; 23 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 24 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 25 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 26 | 27 | import static com.moilioncircle.redis.cluster.watchdog.manager.ClusterConfigManager.clusterGenNodeDescription; 28 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsSlave; 29 | 30 | /** 31 | * @author Leon Chen 32 | * @since 1.0.0 33 | */ 34 | public class ClusterSlavesCommandHandler extends AbstractCommandHandler { 35 | 36 | public ClusterSlavesCommandHandler(ClusterManagers managers) { 37 | super(managers); 38 | } 39 | 40 | @Override 41 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 42 | if (message.length != 3) { 43 | replyError(t, "ERR Wrong CLUSTER subcommand or number of arguments"); 44 | return; 45 | } 46 | 47 | ClusterNode node = managers.nodes.clusterLookupNode(message[2]); 48 | if (node == null) { 49 | replyError(t, "ERR Unknown node " + message[2]); 50 | return; 51 | } 52 | 53 | if (nodeIsSlave(node)) { 54 | replyError(t, "ERR The specified node is not a master"); 55 | return; 56 | } 57 | 58 | StringBuilder builder = new StringBuilder(); 59 | builder.append("*").append(node.slaves.size()).append("\r\n"); 60 | for (ClusterNode slave : node.slaves) { 61 | Version version = managers.configuration.getVersion(); 62 | ClusterConfigInfo configInfo = ClusterConfigInfo.valueOf(server.cluster); 63 | ClusterNodeInfo nodeInfo = ClusterNodeInfo.valueOf(slave, server.cluster.myself); 64 | String nodeDescription = clusterGenNodeDescription(configInfo, nodeInfo, version); 65 | builder.append("$").append(nodeDescription.length()).append("\r\n").append(nodeDescription).append("\r\n"); 66 | } 67 | t.write(builder.toString().getBytes(), true); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/manager/ClusterBlacklistManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.manager; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 20 | import com.moilioncircle.redis.cluster.watchdog.state.ServerState; 21 | import com.moilioncircle.redis.cluster.watchdog.util.type.Tuple2; 22 | 23 | import java.util.Map; 24 | 25 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_BLACKLIST_TTL; 26 | import static com.moilioncircle.redis.cluster.watchdog.util.Tuples.of; 27 | 28 | /** 29 | * @author Leon Chen 30 | * @since 1.0.0 31 | */ 32 | public class ClusterBlacklistManager { 33 | 34 | private ServerState server; 35 | 36 | public ClusterBlacklistManager(ClusterManagers managers) { 37 | this.server = managers.server; 38 | } 39 | 40 | public boolean clusterBlacklistExists(String name) { 41 | long now = System.currentTimeMillis(); 42 | Map> map = server.cluster.blacklist; 43 | map.values().removeIf(e -> e.getV1() < now); 44 | return map.containsKey(name); 45 | } 46 | 47 | public void clusterBlacklistAddNode(ClusterNode node) { 48 | long now = System.currentTimeMillis(); 49 | server.cluster.blacklist.values().removeIf(e -> e.getV1() < now); 50 | server.cluster.blacklist.put(node.name, of(now + CLUSTER_BLACKLIST_TTL, node)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/manager/ClusterCommandHandlerManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.manager; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.CommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.command.DefaultCommandHandler; 21 | import com.moilioncircle.redis.cluster.watchdog.storage.DefaultStorageEngine; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | import static java.util.Arrays.stream; 27 | 28 | /** 29 | * @author Leon Chen 30 | * @since 1.0.0 31 | */ 32 | public class ClusterCommandHandlerManager { 33 | 34 | private static final Log logger = LogFactory.getLog(ClusterCommandHandlerManager.class); 35 | 36 | private ClusterManagers managers; 37 | private DefaultCommandHandler handler; 38 | 39 | public ClusterCommandHandlerManager(ClusterManagers managers) { 40 | this.managers = managers; 41 | this.handler = new DefaultCommandHandler(managers); 42 | } 43 | 44 | public CommandHandler addCommandHandler(String name, CommandHandler handler) { 45 | if (managers.engine instanceof DefaultStorageEngine) { 46 | logger.warn("Using default storage engine. [ ClusterWatchdog.setStorageEngine(engine); ] first"); 47 | } 48 | handler.setStorageEngine(managers.engine); 49 | handler.setConfiguration(managers.configuration); 50 | return this.handler.addCommandHandler(name, handler); 51 | } 52 | 53 | public void handleCommand(Transport t, byte[][] raw) { 54 | this.handler.handle(t, stream(raw).map(String::new).toArray(String[]::new), raw); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/manager/ClusterConnectionManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.manager; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterLink; 20 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class ClusterConnectionManager { 27 | 28 | public synchronized void freeClusterLink(ClusterLink link) { 29 | if (link == null) return; 30 | if (link.node != null) link.node.link = null; 31 | if (link.fd != null) link.fd.disconnect(null); 32 | } 33 | 34 | public synchronized ClusterLink createClusterLink(ClusterNode node) { 35 | ClusterLink c = new ClusterLink(); 36 | c.node = node; 37 | return c; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/manager/ReplicationManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.manager; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 20 | import com.moilioncircle.redis.cluster.watchdog.state.ServerState; 21 | import org.apache.commons.logging.Log; 22 | import org.apache.commons.logging.LogFactory; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class ReplicationManager { 29 | 30 | private static final Log logger = LogFactory.getLog(ReplicationManager.class); 31 | 32 | private ServerState server; 33 | private ClusterManagers managers; 34 | 35 | public ReplicationManager(ClusterManagers managers) { 36 | this.managers = managers; 37 | this.server = managers.server; 38 | } 39 | 40 | public long replicationGetSlaveOffset() { 41 | return managers.notifyReplicationGetSlaveOffset(); 42 | } 43 | 44 | public void replicationSetMaster(ClusterNode node) { 45 | logger.info("replication set [" + node.ip + ":" + node.port + "]"); 46 | this.server.masterHost = node.ip; 47 | this.server.masterPort = node.port; 48 | managers.notifySetReplication(node.ip, node.port, this.managers.engine); 49 | } 50 | 51 | public void replicationUnsetMaster() { 52 | logger.info("replication unset [" + server.masterHost + ":" + server.masterPort + "]"); 53 | managers.notifyUnsetReplication(managers.engine); 54 | server.masterHost = null; 55 | server.masterPort = 0; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/ClusterMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.ClusterState; 20 | import com.moilioncircle.redis.cluster.watchdog.Version; 21 | 22 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS_BYTES; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class ClusterMessage implements RCmbMessage { 29 | public int type; 30 | public int flags; 31 | public String name; 32 | public String ip; 33 | public int port; 34 | public int busPort; 35 | public int count; 36 | public int length; 37 | public long offset; 38 | public String master; 39 | public Version version; 40 | public String signature; 41 | public long configEpoch; 42 | public long currentEpoch; 43 | public ClusterState state; 44 | public byte[] messageFlags = new byte[3]; 45 | public byte[] slots = new byte[CLUSTER_SLOTS_BYTES]; 46 | public ClusterMessageData data = new ClusterMessageData(); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/ClusterMessageData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class ClusterMessageData { 27 | public List gossips = new ArrayList<>(); 28 | public ClusterMessageDataFail fail = new ClusterMessageDataFail(); 29 | public ClusterMessageDataUpdate config = new ClusterMessageDataUpdate(); 30 | public ClusterMessageDataPublish publish = new ClusterMessageDataPublish(); 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/ClusterMessageDataFail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class ClusterMessageDataFail { 24 | public String name; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/ClusterMessageDataGossip.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class ClusterMessageDataGossip { 24 | public int flags; 25 | public String name; 26 | public long pingTime; 27 | public long pongTime; 28 | public String ip; 29 | public int port; 30 | public int busPort; 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/ClusterMessageDataPublish.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class ClusterMessageDataPublish { 24 | public byte[] bulkData = new byte[8]; 25 | public int channelLength; 26 | public int messageLength; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/ClusterMessageDataUpdate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message; 18 | 19 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS_BYTES; 20 | 21 | /** 22 | * @author Leon Chen 23 | * @since 1.0.0 24 | */ 25 | public class ClusterMessageDataUpdate { 26 | public String name; 27 | public long configEpoch; 28 | public byte[] slots = new byte[CLUSTER_SLOTS_BYTES]; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/RCmbMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public interface RCmbMessage { 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/handler/ClusterMessageFailHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message.handler; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.message.ClusterMessage; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterLink; 22 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_FAIL; 27 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_PFAIL; 28 | import static com.moilioncircle.redis.cluster.watchdog.ClusterNodeInfo.valueOf; 29 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeFailed; 30 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsMyself; 31 | 32 | /** 33 | * @author Leon Chen 34 | * @since 1.0.0 35 | */ 36 | public class ClusterMessageFailHandler extends AbstractClusterMessageHandler { 37 | 38 | private static final Log logger = LogFactory.getLog(ClusterMessageFailHandler.class); 39 | 40 | public ClusterMessageFailHandler(ClusterManagers managers) { 41 | super(managers); 42 | } 43 | 44 | @Override 45 | public boolean handle(ClusterNode sender, ClusterLink link, ClusterMessage hdr) { 46 | logger.debug("Fail packet received: node:" + (link.node == null ? "(nil)" : link.node.name)); 47 | 48 | if (sender == null) return true; 49 | ClusterNode failing = managers.nodes.clusterLookupNode(hdr.data.fail.name); 50 | if (failing != null && !nodeIsMyself(failing.flags) && !nodeFailed(failing.flags)) { 51 | logger.info("FAIL message received from " + hdr.name + " fail " + hdr.data.fail.name); 52 | failing.flags |= CLUSTER_NODE_FAIL; 53 | failing.failTime = System.currentTimeMillis(); //fail time 54 | failing.flags &= ~CLUSTER_NODE_PFAIL; 55 | managers.notifyNodeFailed(valueOf(failing, server.myself)); 56 | } 57 | return true; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/handler/ClusterMessageFailoverAuthAckHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message.handler; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.message.ClusterMessage; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterLink; 22 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsMaster; 27 | 28 | /** 29 | * @author Leon Chen 30 | * @since 1.0.0 31 | */ 32 | public class ClusterMessageFailoverAuthAckHandler extends AbstractClusterMessageHandler { 33 | 34 | private static final Log logger = LogFactory.getLog(ClusterMessageFailoverAuthAckHandler.class); 35 | 36 | public ClusterMessageFailoverAuthAckHandler(ClusterManagers managers) { 37 | super(managers); 38 | } 39 | 40 | @Override 41 | public boolean handle(ClusterNode sender, ClusterLink link, ClusterMessage hdr) { 42 | logger.debug("Failover auth ack packet received: node:" + (link.node == null ? "(nil)" : link.node.name)); 43 | 44 | if (sender == null) return true; 45 | if (nodeIsMaster(sender) && sender.assignedSlots > 0 && hdr.currentEpoch >= server.cluster.failoverAuthEpoch) { 46 | server.cluster.failoverAuthCount++; 47 | managers.failovers.clusterHandleSlaveFailover(); 48 | } 49 | return true; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/handler/ClusterMessageFailoverAuthRequestHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message.handler; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.message.ClusterMessage; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterLink; 22 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTERMSG_FLAG0_FORCEACK; 27 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS; 28 | import static com.moilioncircle.redis.cluster.watchdog.manager.ClusterSlotManager.bitmapTestBit; 29 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeFailed; 30 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsMaster; 31 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsSlave; 32 | 33 | /** 34 | * @author Leon Chen 35 | * @since 1.0.0 36 | */ 37 | public class ClusterMessageFailoverAuthRequestHandler extends AbstractClusterMessageHandler { 38 | 39 | private static final Log logger = LogFactory.getLog(ClusterMessageFailoverAuthRequestHandler.class); 40 | 41 | public ClusterMessageFailoverAuthRequestHandler(ClusterManagers managers) { 42 | super(managers); 43 | } 44 | 45 | @Override 46 | public boolean handle(ClusterNode sender, ClusterLink link, ClusterMessage hdr) { 47 | logger.debug("Failover auth request packet received: node:" + (link.node == null ? "(nil)" : link.node.name)); 48 | if (sender == null) return true; 49 | clusterSendFailoverAuthIfNeeded(sender, hdr); 50 | return true; 51 | } 52 | 53 | public void clusterSendFailoverAuthIfNeeded(ClusterNode node, ClusterMessage hdr) { 54 | ClusterNode master = node.master; 55 | long now = System.currentTimeMillis(); 56 | boolean force = (hdr.messageFlags[0] & CLUSTERMSG_FLAG0_FORCEACK) != 0; 57 | // 58 | if (nodeIsSlave(server.myself)) return; 59 | if (server.myself.assignedSlots == 0) return; 60 | if (hdr.currentEpoch < server.cluster.currentEpoch) return; 61 | if (server.cluster.lastVoteEpoch == server.cluster.currentEpoch) return; 62 | if (nodeIsMaster(node) || master == null || (!nodeFailed(master) && !force)) return; 63 | if (now - master.votedTime < managers.configuration.getClusterNodeTimeout() * 2) return; 64 | 65 | for (int i = 0; i < CLUSTER_SLOTS; i++) { 66 | if (!bitmapTestBit(hdr.slots, i)) continue; 67 | if (server.cluster.slots[i] == null) continue; 68 | if (server.cluster.slots[i].configEpoch <= hdr.configEpoch) continue; 69 | return; 70 | } 71 | 72 | managers.messages.clusterSendFailoverAuth(node); 73 | node.master.votedTime = System.currentTimeMillis(); 74 | server.cluster.lastVoteEpoch = server.cluster.currentEpoch; 75 | logger.info("Failover auth granted to " + node.name + " for epoch " + server.cluster.currentEpoch); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/handler/ClusterMessageHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message.handler; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.message.ClusterMessage; 20 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterLink; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public interface ClusterMessageHandler { 27 | 28 | boolean handle(ClusterLink link, ClusterMessage hdr); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/handler/ClusterMessageMFStartHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message.handler; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.message.ClusterMessage; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterLink; 22 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class ClusterMessageMFStartHandler extends AbstractClusterMessageHandler { 31 | 32 | private static final Log logger = LogFactory.getLog(ClusterMessageMFStartHandler.class); 33 | 34 | public ClusterMessageMFStartHandler(ClusterManagers managers) { 35 | super(managers); 36 | } 37 | 38 | @Override 39 | public boolean handle(ClusterNode sender, ClusterLink link, ClusterMessage hdr) { 40 | logger.debug("MFStart packet received: node:" + (link.node == null ? "(nil)" : link.node.name)); 41 | return true; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/handler/ClusterMessagePublishHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message.handler; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.message.ClusterMessage; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterLink; 22 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class ClusterMessagePublishHandler extends AbstractClusterMessageHandler { 31 | 32 | private static final Log logger = LogFactory.getLog(ClusterMessagePublishHandler.class); 33 | 34 | public ClusterMessagePublishHandler(ClusterManagers managers) { 35 | super(managers); 36 | } 37 | 38 | @Override 39 | public boolean handle(ClusterNode sender, ClusterLink link, ClusterMessage hdr) { 40 | logger.debug("Publish packet received: node:" + (link.node == null ? "(nil)" : link.node.name)); 41 | return true; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/message/handler/ClusterMessageUpdateHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.message.handler; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.manager.ClusterManagers; 20 | import com.moilioncircle.redis.cluster.watchdog.message.ClusterMessage; 21 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterLink; 22 | import com.moilioncircle.redis.cluster.watchdog.state.ClusterNode; 23 | import org.apache.commons.logging.Log; 24 | import org.apache.commons.logging.LogFactory; 25 | 26 | import static com.moilioncircle.redis.cluster.watchdog.state.NodeStates.nodeIsSlave; 27 | 28 | /** 29 | * @author Leon Chen 30 | * @since 1.0.0 31 | */ 32 | public class ClusterMessageUpdateHandler extends AbstractClusterMessageHandler { 33 | 34 | private static final Log logger = LogFactory.getLog(ClusterMessageUpdateHandler.class); 35 | 36 | public ClusterMessageUpdateHandler(ClusterManagers managers) { 37 | super(managers); 38 | } 39 | 40 | @Override 41 | public boolean handle(ClusterNode sender, ClusterLink link, ClusterMessage hdr) { 42 | logger.debug("Update packet received: node:" + (link.node == null ? "(nil)" : link.node.name)); 43 | if (sender == null) return true; 44 | String name = hdr.data.config.name; 45 | long epoch = hdr.data.config.configEpoch; 46 | ClusterNode node = managers.nodes.clusterLookupNode(name); 47 | if (node == null || node.configEpoch >= epoch) return true; 48 | if (nodeIsSlave(node)) managers.nodes.clusterSetNodeAsMaster(node); 49 | clusterUpdateSlotsConfigWith(node, node.configEpoch = epoch, hdr.data.config.slots); 50 | return true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/state/ClusterLink.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.state; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.message.RCmbMessage; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.session.Session; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class ClusterLink { 27 | public volatile long createTime; 28 | public volatile ClusterNode node; 29 | public volatile Session fd; 30 | 31 | public ClusterLink() { 32 | this.createTime = System.currentTimeMillis(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/state/ClusterNode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.state; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS_BYTES; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class ClusterNode { 29 | public long failTime; 30 | public long pingTime; 31 | public long pongTime; 32 | public long votedTime; 33 | public long createTime; 34 | public long isolatedTime; 35 | public long configEpoch; 36 | public int assignedSlots; 37 | public byte[] slots = new byte[CLUSTER_SLOTS_BYTES]; 38 | public List slaves = new ArrayList<>(); 39 | public String ip; 40 | public int port; 41 | public int busPort; 42 | public int flags; 43 | public String name; 44 | public long offset; 45 | public ClusterNode master; 46 | public volatile ClusterLink link; 47 | public List failReports = new ArrayList<>(); 48 | 49 | public ClusterNode() { 50 | this.createTime = System.currentTimeMillis(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/state/ClusterNodeFailReport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.state; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class ClusterNodeFailReport { 24 | public ClusterNode node; 25 | public long createTime = System.currentTimeMillis(); 26 | 27 | public ClusterNodeFailReport(ClusterNode node) { 28 | this.node = node; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/state/ClusterState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.state; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.type.Tuple2; 20 | 21 | import java.util.LinkedHashMap; 22 | import java.util.Map; 23 | 24 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTERMSG_TYPE_COUNT; 25 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS; 26 | import static com.moilioncircle.redis.cluster.watchdog.ClusterState.CLUSTER_FAIL; 27 | 28 | /** 29 | * @author Leon Chen 30 | * @since 1.0.0 31 | */ 32 | public class ClusterState { 33 | public int size = 1; 34 | public long pFailNodes = 0; 35 | public long currentEpoch = 0; 36 | public long lastVoteEpoch = 0; 37 | public ClusterNode[] slots = new ClusterNode[CLUSTER_SLOTS]; 38 | public long[] messagesSent = new long[CLUSTERMSG_TYPE_COUNT]; 39 | public Map nodes = new LinkedHashMap<>(); 40 | public ClusterNode[] migrating = new ClusterNode[CLUSTER_SLOTS]; 41 | public ClusterNode[] importing = new ClusterNode[CLUSTER_SLOTS]; 42 | public long[] messagesReceived = new long[CLUSTERMSG_TYPE_COUNT]; 43 | public int failoverAuthRank = 0; 44 | public ClusterNode myself = null; 45 | public long failoverAuthTime = 0; 46 | public int failoverAuthCount = 0; 47 | public long failoverAuthEpoch = 0; 48 | public boolean failoverAuthSent = false; 49 | public Map> blacklist = new LinkedHashMap<>(); 50 | public com.moilioncircle.redis.cluster.watchdog.ClusterState state = CLUSTER_FAIL; 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/state/NodeStates.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.state; 18 | 19 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_FAIL; 20 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_HANDSHAKE; 21 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_MASTER; 22 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_MEET; 23 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_MIGRATE_TO; 24 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_MYSELF; 25 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_NOADDR; 26 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_PFAIL; 27 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_NODE_SLAVE; 28 | 29 | /** 30 | * @author Leon Chen 31 | * @since 1.0.0 32 | */ 33 | public class NodeStates { 34 | 35 | /** 36 | * 37 | */ 38 | public static boolean nodeFailed(ClusterNode n) { 39 | return nodeFailed(n.flags); 40 | } 41 | 42 | public static boolean nodeIsSlave(ClusterNode n) { 43 | return nodeIsSlave(n.flags); 44 | } 45 | 46 | public static boolean nodePFailed(ClusterNode n) { 47 | return nodePFailed(n.flags); 48 | } 49 | 50 | public static boolean nodeHasAddr(ClusterNode n) { 51 | return nodeHasAddr(n.flags); 52 | } 53 | 54 | public static boolean nodeIsMaster(ClusterNode n) { 55 | return nodeIsMaster(n.flags); 56 | } 57 | 58 | public static boolean nodeInHandshake(ClusterNode n) { 59 | return nodeInHandshake(n.flags); 60 | } 61 | 62 | public static boolean nodeWithoutAddr(ClusterNode n) { 63 | return nodeWithoutAddr(n.flags); 64 | } 65 | 66 | /** 67 | * 68 | */ 69 | public static boolean nodeFailed(int flags) { 70 | return (flags & CLUSTER_NODE_FAIL) != 0; 71 | } 72 | 73 | public static boolean nodeInMeet(int flags) { 74 | return (flags & CLUSTER_NODE_MEET) != 0; 75 | } 76 | 77 | public static boolean nodePFailed(int flags) { 78 | return (flags & CLUSTER_NODE_PFAIL) != 0; 79 | } 80 | 81 | public static boolean nodeIsSlave(int flags) { 82 | return (flags & CLUSTER_NODE_SLAVE) != 0; 83 | } 84 | 85 | public static boolean nodeHasAddr(int flags) { 86 | return (flags & CLUSTER_NODE_NOADDR) == 0; 87 | } 88 | 89 | public static boolean nodeIsMaster(int flags) { 90 | return (flags & CLUSTER_NODE_MASTER) != 0; 91 | } 92 | 93 | public static boolean nodeIsMyself(int flags) { 94 | return (flags & CLUSTER_NODE_MYSELF) != 0; 95 | } 96 | 97 | public static boolean nodeWithoutAddr(int flags) { 98 | return (flags & CLUSTER_NODE_NOADDR) != 0; 99 | } 100 | 101 | public static boolean nodeInMigrate(int flags) { 102 | return (flags & CLUSTER_NODE_MIGRATE_TO) != 0; 103 | } 104 | 105 | public static boolean nodeInHandshake(int flags) { 106 | return (flags & CLUSTER_NODE_HANDSHAKE) != 0; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/state/ServerState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.state; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.message.RCmbMessage; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | 22 | import java.util.Map; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | 25 | /** 26 | * @author Leon Chen 27 | * @since 1.0.0 28 | */ 29 | public class ServerState { 30 | public int masterPort; 31 | public String masterHost; 32 | public ClusterNode myself; 33 | public ClusterState cluster; 34 | public long iteration = 0; 35 | public String previousAddress; 36 | public long stateSaveTime = 0; 37 | public long amongMinorityTime = 0; 38 | public Map, ClusterLink> cfd = new ConcurrentHashMap<>(); 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/storage/DefaultStorageEngine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.storage; 18 | 19 | import java.util.Iterator; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class DefaultStorageEngine implements StorageEngine { 27 | private static Iterator EMPTY = new Iterator() { 28 | @Override 29 | public boolean hasNext() { 30 | return false; 31 | } 32 | 33 | @Override 34 | public byte[] next() { 35 | return new byte[0]; 36 | } 37 | }; 38 | 39 | @Override 40 | public long size() { 41 | return 0L; 42 | } 43 | 44 | @Override 45 | public long clear() { 46 | return 0L; 47 | } 48 | 49 | @Override 50 | public void persist() { 51 | } 52 | 53 | @Override 54 | public boolean readonly() { 55 | return false; 56 | } 57 | 58 | @Override 59 | public long size(int slot) { 60 | return 0L; 61 | } 62 | 63 | @Override 64 | public long clear(int slot) { 65 | return 0L; 66 | } 67 | 68 | @Override 69 | public void readonly(boolean r) { 70 | } 71 | 72 | @Override 73 | public Iterator keys() { 74 | return EMPTY; 75 | } 76 | 77 | @Override 78 | public Iterator keys(int slot) { 79 | return EMPTY; 80 | } 81 | 82 | @Override 83 | public long ttl(byte[] key) { 84 | return 0L; 85 | } 86 | 87 | @Override 88 | public boolean delete(byte[] key) { 89 | return false; 90 | } 91 | 92 | @Override 93 | public Object load(byte[] key) { 94 | return null; 95 | } 96 | 97 | @Override 98 | public boolean exist(byte[] key) { 99 | return false; 100 | } 101 | 102 | @Override 103 | public Class type(byte[] key) { 104 | return null; 105 | } 106 | 107 | @Override 108 | public boolean save(byte[] key, Object value, long expire, boolean force) { 109 | return false; 110 | } 111 | 112 | @Override 113 | public byte[] dump(byte[] key) { 114 | return null; 115 | } 116 | 117 | @Override 118 | public boolean restore(byte[] key, byte[] serialized, long expire, boolean force) { 119 | return false; 120 | } 121 | 122 | @Override 123 | public void start() { 124 | } 125 | 126 | @Override 127 | public void stop() { 128 | stop(0, TimeUnit.MILLISECONDS); 129 | } 130 | 131 | @Override 132 | public void stop(long timeout, TimeUnit unit) { 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/storage/StorageEngine.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.storage; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.Resourcable; 20 | 21 | import javax.annotation.concurrent.ThreadSafe; 22 | import java.util.Iterator; 23 | 24 | import static com.moilioncircle.redis.cluster.watchdog.ClusterConstants.CLUSTER_SLOTS; 25 | import static com.moilioncircle.redis.cluster.watchdog.util.CRC16.crc16; 26 | 27 | /** 28 | * @author Leon Chen 29 | * @since 1.0.0 30 | */ 31 | @ThreadSafe 32 | public interface StorageEngine extends Resourcable { 33 | 34 | /** 35 | * 36 | */ 37 | static int calcSlot(byte[] key) { 38 | if (key == null) return 0; 39 | int st = -1, ed = -1; 40 | for (int i = 0, len = key.length; i < len; i++) { 41 | if (key[i] == '{' && st == -1) st = i; 42 | if (key[i] == '}' && st >= 0) { 43 | ed = i; 44 | break; 45 | } 46 | } 47 | if (st >= 0 && ed >= 0 && ed > st + 1) 48 | return crc16(key, st + 1, ed) & (CLUSTER_SLOTS - 1); 49 | return crc16(key) & (CLUSTER_SLOTS - 1); 50 | } 51 | 52 | long size(); 53 | 54 | long clear(); 55 | 56 | void persist(); 57 | 58 | long size(int slot); 59 | 60 | long clear(int slot); 61 | 62 | Iterator keys(); 63 | 64 | Iterator keys(int slot); 65 | 66 | /** 67 | * 68 | */ 69 | long ttl(byte[] key); 70 | 71 | Object load(byte[] key); 72 | 73 | boolean exist(byte[] key); 74 | 75 | Class type(byte[] key); 76 | 77 | boolean delete(byte[] key); 78 | 79 | boolean save(byte[] key, Object value, long expire, boolean force); 80 | 81 | /** 82 | * 83 | */ 84 | byte[] dump(byte[] key); 85 | 86 | boolean restore(byte[] key, byte[] serialized, long expire, boolean force); 87 | 88 | /** 89 | * 90 | */ 91 | boolean readonly(); 92 | 93 | void readonly(boolean r); 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/CRC16.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class CRC16 { 24 | public static final int[] LOOKUP_TABLE = { 25 | 0X0000, 0X1021, 0X2042, 0X3063, 0X4084, 0X50A5, 0X60C6, 0X70E7, 26 | 0X8108, 0X9129, 0XA14A, 0XB16B, 0XC18C, 0XD1AD, 0XE1CE, 0XF1EF, 27 | 0X1231, 0X0210, 0X3273, 0X2252, 0X52B5, 0X4294, 0X72F7, 0X62D6, 28 | 0X9339, 0X8318, 0XB37B, 0XA35A, 0XD3BD, 0XC39C, 0XF3FF, 0XE3DE, 29 | 0X2462, 0X3443, 0X0420, 0X1401, 0X64E6, 0X74C7, 0X44A4, 0X5485, 30 | 0XA56A, 0XB54B, 0X8528, 0X9509, 0XE5EE, 0XF5CF, 0XC5AC, 0XD58D, 31 | 0X3653, 0X2672, 0X1611, 0X0630, 0X76D7, 0X66F6, 0X5695, 0X46B4, 32 | 0XB75B, 0XA77A, 0X9719, 0X8738, 0XF7DF, 0XE7FE, 0XD79D, 0XC7BC, 33 | 0X48C4, 0X58E5, 0X6886, 0X78A7, 0X0840, 0X1861, 0X2802, 0X3823, 34 | 0XC9CC, 0XD9ED, 0XE98E, 0XF9AF, 0X8948, 0X9969, 0XA90A, 0XB92B, 35 | 0X5AF5, 0X4AD4, 0X7AB7, 0X6A96, 0X1A71, 0X0A50, 0X3A33, 0X2A12, 36 | 0XDBFD, 0XCBDC, 0XFBBF, 0XEB9E, 0X9B79, 0X8B58, 0XBB3B, 0XAB1A, 37 | 0X6CA6, 0X7C87, 0X4CE4, 0X5CC5, 0X2C22, 0X3C03, 0X0C60, 0X1C41, 38 | 0XEDAE, 0XFD8F, 0XCDEC, 0XDDCD, 0XAD2A, 0XBD0B, 0X8D68, 0X9D49, 39 | 0X7E97, 0X6EB6, 0X5ED5, 0X4EF4, 0X3E13, 0X2E32, 0X1E51, 0X0E70, 40 | 0XFF9F, 0XEFBE, 0XDFDD, 0XCFFC, 0XBF1B, 0XAF3A, 0X9F59, 0X8F78, 41 | 0X9188, 0X81A9, 0XB1CA, 0XA1EB, 0XD10C, 0XC12D, 0XF14E, 0XE16F, 42 | 0X1080, 0X00A1, 0X30C2, 0X20E3, 0X5004, 0X4025, 0X7046, 0X6067, 43 | 0X83B9, 0X9398, 0XA3FB, 0XB3DA, 0XC33D, 0XD31C, 0XE37F, 0XF35E, 44 | 0X02B1, 0X1290, 0X22F3, 0X32D2, 0X4235, 0X5214, 0X6277, 0X7256, 45 | 0XB5EA, 0XA5CB, 0X95A8, 0X8589, 0XF56E, 0XE54F, 0XD52C, 0XC50D, 46 | 0X34E2, 0X24C3, 0X14A0, 0X0481, 0X7466, 0X6447, 0X5424, 0X4405, 47 | 0XA7DB, 0XB7FA, 0X8799, 0X97B8, 0XE75F, 0XF77E, 0XC71D, 0XD73C, 48 | 0X26D3, 0X36F2, 0X0691, 0X16B0, 0X6657, 0X7676, 0X4615, 0X5634, 49 | 0XD94C, 0XC96D, 0XF90E, 0XE92F, 0X99C8, 0X89E9, 0XB98A, 0XA9AB, 50 | 0X5844, 0X4865, 0X7806, 0X6827, 0X18C0, 0X08E1, 0X3882, 0X28A3, 51 | 0XCB7D, 0XDB5C, 0XEB3F, 0XFB1E, 0X8BF9, 0X9BD8, 0XABBB, 0XBB9A, 52 | 0X4A75, 0X5A54, 0X6A37, 0X7A16, 0X0AF1, 0X1AD0, 0X2AB3, 0X3A92, 53 | 0XFD2E, 0XED0F, 0XDD6C, 0XCD4D, 0XBDAA, 0XAD8B, 0X9DE8, 0X8DC9, 54 | 0X7C26, 0X6C07, 0X5C64, 0X4C45, 0X3CA2, 0X2C83, 0X1CE0, 0X0CC1, 55 | 0XEF1F, 0XFF3E, 0XCF5D, 0XDF7C, 0XAF9B, 0XBFBA, 0X8FD9, 0X9FF8, 56 | 0X6E17, 0X7E36, 0X4E55, 0X5E74, 0X2E93, 0X3EB2, 0X0ED1, 0X1EF0 57 | }; 58 | 59 | public static int crc16(byte[] bytes) { 60 | return crc16(bytes, 0, bytes.length); 61 | } 62 | 63 | public static int crc16(byte[] bytes, int start, int length) { 64 | int crc = 0x0000; 65 | for (int i = start; i < length; i++) 66 | crc = ((crc << 8) ^ LOOKUP_TABLE[((crc >>> 8) ^ (bytes[i] & 0xFF)) & 0xFF]); 67 | return crc & 0xFFFF; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/Iterators.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util; 18 | 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class Iterators { 27 | 28 | public static Iterator iterator(T t) { 29 | return iterator(t); 30 | } 31 | 32 | @SafeVarargs 33 | public static Iterator iterator(final T... t) { 34 | class Iter implements Iterator { 35 | private int idx = 0; 36 | 37 | @Override 38 | public boolean hasNext() { 39 | return idx < t.length; 40 | } 41 | 42 | @Override 43 | public T next() { 44 | if (!hasNext()) throw new NoSuchElementException(); 45 | return t[idx++]; 46 | } 47 | } 48 | return t == null ? null : new Iter(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/Tuples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.type.Tuple2; 20 | import com.moilioncircle.redis.cluster.watchdog.util.type.Tuple3; 21 | import com.moilioncircle.redis.cluster.watchdog.util.type.Tuple4; 22 | import com.moilioncircle.redis.cluster.watchdog.util.type.Tuple5; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public class Tuples { 29 | 30 | public static Tuple2 of(T1 t1, T2 t2) { 31 | return new Tuple2<>(t1, t2); 32 | } 33 | 34 | public static Tuple3 of(T1 t1, T2 t2, T3 t3) { 35 | return new Tuple3<>(t1, t2, t3); 36 | } 37 | 38 | public static Tuple4 of(T1 t1, T2 t2, T3 t3, T4 t4) { 39 | return new Tuple4<>(t1, t2, t3, t4); 40 | } 41 | 42 | public static Tuple5 of(T1 t1, T2 t2, T3 t3, T4 t4, T5 t5) { 43 | return new Tuple5<>(t1, t2, t3, t4, t5); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/concurrent/executor/ExecutorListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.concurrent.executor; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.ListenableRunnableFuture; 20 | 21 | import java.util.concurrent.ExecutorService; 22 | 23 | /** 24 | * @author Leon Chen 25 | * @since 1.0.0 26 | */ 27 | public interface ExecutorListener { 28 | void onTerminated(ExecutorService executor); 29 | 30 | void beforeExecute(ExecutorService executor, ListenableRunnableFuture future); 31 | 32 | void afterExecute(ExecutorService executor, ListenableRunnableFuture future, Throwable tx); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/concurrent/executor/ListenableExecutors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.concurrent.executor; 18 | 19 | import java.util.concurrent.BlockingQueue; 20 | import java.util.concurrent.LinkedBlockingQueue; 21 | import java.util.concurrent.RejectedExecutionHandler; 22 | import java.util.concurrent.SynchronousQueue; 23 | import java.util.concurrent.ThreadFactory; 24 | import java.util.concurrent.TimeUnit; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class ListenableExecutors { 31 | 32 | public static ListenableThreadPoolExecutor newThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { 33 | return new ListenableThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); 34 | } 35 | 36 | public static ListenableThreadPoolExecutor newFixedThreadPool(int nThreads) { 37 | return new ListenableThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); 38 | } 39 | 40 | public static ListenableThreadPoolExecutor newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { 41 | return new ListenableThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), threadFactory); 42 | } 43 | 44 | public static ListenableThreadPoolExecutor newSingleThreadExecutor() { 45 | return new ListenableThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); 46 | } 47 | 48 | public static ListenableThreadPoolExecutor newSingleThreadExecutor(ThreadFactory threadFactory) { 49 | return new ListenableThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), threadFactory); 50 | } 51 | 52 | public static ListenableThreadPoolExecutor newCachedThreadPool() { 53 | return new ListenableThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>()); 54 | } 55 | 56 | public static ListenableThreadPoolExecutor newCachedThreadPool(ThreadFactory threadFactory) { 57 | return new ListenableThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), threadFactory); 58 | } 59 | 60 | public static ListenableScheduledThreadPoolExecutor newSingleThreadScheduledExecutor() { 61 | return new ListenableScheduledThreadPoolExecutor(1); 62 | } 63 | 64 | public static ListenableScheduledThreadPoolExecutor newSingleThreadScheduledExecutor(ThreadFactory threadFactory) { 65 | return new ListenableScheduledThreadPoolExecutor(1, threadFactory); 66 | } 67 | 68 | public static ListenableScheduledThreadPoolExecutor newScheduledThreadPool(int corePoolSize) { 69 | return new ListenableScheduledThreadPoolExecutor(corePoolSize); 70 | } 71 | 72 | public static ListenableScheduledThreadPoolExecutor newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) { 73 | return new ListenableScheduledThreadPoolExecutor(corePoolSize, threadFactory); 74 | } 75 | 76 | public static ListenableScheduledThreadPoolExecutor newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory, RejectedExecutionHandler handler) { 77 | return new ListenableScheduledThreadPoolExecutor(corePoolSize, threadFactory, handler); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/concurrent/future/AbstractCompletableFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.concurrent.future; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public abstract class AbstractCompletableFuture implements CompletableFuture { 24 | 25 | protected volatile FutureListener listener; 26 | 27 | @Override 28 | public synchronized FutureListener setListener(FutureListener listener) { 29 | FutureListener r = this.listener; 30 | this.listener = listener; 31 | if (isDone() && listener != null) listener.onComplete(this); 32 | return r; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/concurrent/future/CompletableFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.concurrent.future; 18 | 19 | import java.util.concurrent.ExecutionException; 20 | import java.util.concurrent.Future; 21 | import java.util.function.Function; 22 | 23 | /** 24 | * @author Leon Chen 25 | * @since 1.0.0 26 | */ 27 | public interface CompletableFuture extends Future { 28 | 29 | FutureListener setListener(FutureListener listener); 30 | 31 | default boolean isSuccess() { 32 | if (!isDone()) return false; 33 | try { 34 | get(); 35 | return true; 36 | } catch (ExecutionException e) { 37 | return false; 38 | } catch (InterruptedException e) { 39 | Thread.currentThread().interrupt(); 40 | return false; 41 | } 42 | } 43 | 44 | default Throwable cause() { 45 | if (!isDone()) return null; 46 | try { 47 | get(); 48 | return null; 49 | } catch (ExecutionException e) { 50 | return e.getCause(); 51 | } catch (InterruptedException e) { 52 | Thread.currentThread().interrupt(); 53 | return null; 54 | } 55 | } 56 | 57 | default boolean success(T value) { 58 | throw new UnsupportedOperationException(); 59 | } 60 | 61 | default boolean failure(Throwable cause) { 62 | throw new UnsupportedOperationException(); 63 | } 64 | 65 | default CompletableFuture map(Function function) { 66 | CompletableFuture r = new ListenableFuture<>(); 67 | FutureListener old = this.setListener(null); 68 | this.setListener(f -> { 69 | if (old != null) old.onComplete(this); 70 | try { 71 | r.success(function.apply(f.get())); 72 | } catch (ExecutionException e) { 73 | r.failure(e); 74 | } catch (InterruptedException e) { 75 | Thread.currentThread().interrupt(); 76 | } 77 | }); 78 | return r; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/concurrent/future/FutureListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.concurrent.future; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | @FunctionalInterface 24 | public interface FutureListener { 25 | void onComplete(CompletableFuture future); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/concurrent/future/ListenableChannelFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.concurrent.future; 18 | 19 | import io.netty.util.concurrent.Future; 20 | import io.netty.util.concurrent.GenericFutureListener; 21 | 22 | import java.util.concurrent.ExecutionException; 23 | import java.util.concurrent.TimeUnit; 24 | import java.util.concurrent.TimeoutException; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class ListenableChannelFuture extends AbstractCompletableFuture implements GenericFutureListener> { 31 | 32 | protected final Future future; 33 | 34 | public ListenableChannelFuture(Future future) { 35 | this.future = future; 36 | this.future.addListener(this); 37 | } 38 | 39 | @Override 40 | public boolean cancel(boolean mayInterruptIfRunning) { 41 | return future.cancel(mayInterruptIfRunning); 42 | } 43 | 44 | @Override 45 | public boolean isCancelled() { 46 | return future.isCancelled(); 47 | } 48 | 49 | @Override 50 | public boolean isDone() { 51 | return future.isDone(); 52 | } 53 | 54 | @Override 55 | public T get() throws InterruptedException, ExecutionException { 56 | return future.get(); 57 | } 58 | 59 | @Override 60 | public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 61 | return future.get(timeout, unit); 62 | } 63 | 64 | @Override 65 | public void operationComplete(Future future) throws Exception { 66 | listener.onComplete(this); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/concurrent/future/ListenableRunnableFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.concurrent.future; 18 | 19 | import java.util.concurrent.Callable; 20 | import java.util.concurrent.FutureTask; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public class ListenableRunnableFuture extends FutureTask implements CompletableFuture { 27 | 28 | protected volatile FutureListener listener; 29 | 30 | public ListenableRunnableFuture(Callable callable) { 31 | super(callable); 32 | } 33 | 34 | public ListenableRunnableFuture(Runnable runnable, T result) { 35 | super(runnable, result); 36 | } 37 | 38 | @Override 39 | protected void done() { 40 | listener.onComplete(this); 41 | } 42 | 43 | @Override 44 | public FutureListener setListener(FutureListener listener) { 45 | FutureListener r = this.listener; 46 | this.listener = listener; 47 | if (isDone() && listener != null) listener.onComplete(this); 48 | return r; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/concurrent/future/ListenableScheduledFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.concurrent.future; 18 | 19 | import java.util.concurrent.Callable; 20 | import java.util.concurrent.Delayed; 21 | import java.util.concurrent.RunnableScheduledFuture; 22 | import java.util.concurrent.TimeUnit; 23 | import java.util.concurrent.atomic.AtomicLong; 24 | 25 | /** 26 | * @author Leon Chen 27 | * @since 1.0.0 28 | */ 29 | public class ListenableScheduledFuture extends ListenableRunnableFuture implements RunnableScheduledFuture { 30 | 31 | protected static final AtomicLong sequencer = new AtomicLong(); 32 | protected final long sequenceNumber; 33 | protected final RunnableScheduledFuture future; 34 | 35 | public ListenableScheduledFuture(Runnable runnable, RunnableScheduledFuture future) { 36 | super(runnable, null); 37 | this.future = future; 38 | this.sequenceNumber = sequencer.getAndIncrement(); 39 | } 40 | 41 | public ListenableScheduledFuture(Callable callable, RunnableScheduledFuture future) { 42 | super(callable); 43 | this.future = future; 44 | this.sequenceNumber = sequencer.getAndIncrement(); 45 | } 46 | 47 | @Override 48 | public boolean cancel(boolean mayInterruptIfRunning) { 49 | return future.cancel(mayInterruptIfRunning); 50 | } 51 | 52 | @Override 53 | public void run() { 54 | future.run(); 55 | } 56 | 57 | @Override 58 | public boolean isPeriodic() { 59 | return future.isPeriodic(); 60 | } 61 | 62 | @Override 63 | public long getDelay(TimeUnit unit) { 64 | return future.getDelay(unit); 65 | } 66 | 67 | @Override 68 | public int compareTo(Delayed o) { 69 | int c = future.compareTo(o); 70 | if (c == 0 && o instanceof ListenableScheduledFuture) { 71 | ListenableScheduledFuture x = (ListenableScheduledFuture) o; 72 | return sequenceNumber < x.sequenceNumber ? -1 : 1; 73 | } 74 | return c; 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/AbstractNioBootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.TransportListener; 21 | import io.netty.channel.ChannelHandler; 22 | import org.apache.commons.logging.Log; 23 | import org.apache.commons.logging.LogFactory; 24 | 25 | import java.util.function.Supplier; 26 | 27 | /** 28 | * @author Leon Chen 29 | * @since 1.0.0 30 | */ 31 | public abstract class AbstractNioBootstrap implements NioBootstrap { 32 | protected static final Log logger = LogFactory.getLog(AbstractNioBootstrap.class); 33 | protected final NetworkConfiguration configuration; 34 | protected Supplier encoder; 35 | protected Supplier decoder; 36 | protected volatile TransportListener listener; 37 | 38 | protected AbstractNioBootstrap(NetworkConfiguration configuration) { 39 | this.configuration = configuration; 40 | } 41 | 42 | @Override 43 | public void onMessage(Transport transport, T message) { 44 | TransportListener listener = this.listener; 45 | if (listener != null) listener.onMessage(transport, message); 46 | } 47 | 48 | @Override 49 | public void onException(Transport transport, Throwable throwable) { 50 | TransportListener listener = this.listener; 51 | if (listener != null) listener.onException(transport, throwable); 52 | } 53 | 54 | @Override 55 | public void onConnected(Transport transport) { 56 | TransportListener listener = this.listener; 57 | if (listener != null) listener.onConnected(transport); 58 | } 59 | 60 | @Override 61 | public void onDisconnected(Transport transport, Throwable cause) { 62 | TransportListener listener = this.listener; 63 | if (listener != null) listener.onDisconnected(transport, cause); 64 | } 65 | 66 | @Override 67 | public TransportListener setTransportListener(TransportListener listener) { 68 | TransportListener r = this.listener; 69 | this.listener = listener; 70 | return r; 71 | } 72 | 73 | public Supplier getEncoder() { 74 | return encoder; 75 | } 76 | 77 | @Override 78 | public void setEncoder(Supplier encoder) { 79 | this.encoder = encoder; 80 | } 81 | 82 | public Supplier getDecoder() { 83 | return decoder; 84 | } 85 | 86 | @Override 87 | public void setDecoder(Supplier decoder) { 88 | this.decoder = decoder; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/ConnectionStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public enum ConnectionStatus { 24 | DISCONNECTED, CONNECTING, CONNECTED, DISCONNECTING 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/NioBootstrap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.CompletableFuture; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.TransportListener; 22 | import io.netty.channel.ChannelHandler; 23 | 24 | import java.util.function.Supplier; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public interface NioBootstrap extends TransportListener { 31 | 32 | void setup(); 33 | 34 | Transport getTransport(); 35 | 36 | CompletableFuture shutdown(); 37 | 38 | void setEncoder(Supplier encoder); 39 | 40 | void setDecoder(Supplier decoder); 41 | 42 | CompletableFuture connect(String host, int port); 43 | 44 | TransportListener setTransportListener(TransportListener listener); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/NioBootstrapImpl.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.CompletableFuture; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.TransportListener; 22 | import io.netty.channel.ChannelHandler; 23 | 24 | import java.util.function.Supplier; 25 | 26 | /** 27 | * @author Leon Chen 28 | * @since 1.0.0 29 | */ 30 | public class NioBootstrapImpl implements NioBootstrap { 31 | 32 | private NioBootstrap wrapper; 33 | 34 | public NioBootstrapImpl() { 35 | this(true); 36 | } 37 | 38 | public NioBootstrapImpl(boolean server) { 39 | this(server, NetworkConfiguration.defaultSetting()); 40 | } 41 | 42 | public NioBootstrapImpl(boolean server, NetworkConfiguration configuration) { 43 | if (server) wrapper = new NioAcceptor<>(configuration); 44 | else wrapper = new NioInitiator<>(configuration); 45 | } 46 | 47 | @Override 48 | public void onConnected(Transport transport) { 49 | wrapper.onConnected(transport); 50 | } 51 | 52 | @Override 53 | public void onMessage(Transport transport, T message) { 54 | wrapper.onMessage(transport, message); 55 | } 56 | 57 | @Override 58 | public void onException(Transport transport, Throwable cause) { 59 | wrapper.onException(transport, cause); 60 | } 61 | 62 | @Override 63 | public void onDisconnected(Transport transport, Throwable cause) { 64 | wrapper.onDisconnected(transport, cause); 65 | } 66 | 67 | @Override 68 | public void setup() { 69 | wrapper.setup(); 70 | } 71 | 72 | @Override 73 | public Transport getTransport() { 74 | return wrapper.getTransport(); 75 | } 76 | 77 | @Override 78 | public CompletableFuture shutdown() { 79 | return wrapper.shutdown(); 80 | } 81 | 82 | @Override 83 | public void setEncoder(Supplier encoder) { 84 | wrapper.setEncoder(encoder); 85 | } 86 | 87 | @Override 88 | public void setDecoder(Supplier decoder) { 89 | wrapper.setDecoder(decoder); 90 | } 91 | 92 | @Override 93 | public CompletableFuture connect(String host, int port) { 94 | return wrapper.connect(host, port); 95 | } 96 | 97 | @Override 98 | public TransportListener setTransportListener(TransportListener listener) { 99 | return wrapper.setTransportListener(listener); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/exceptions/OverloadException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.exceptions; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class OverloadException extends RuntimeException { 24 | private static final long serialVersionUID = 1L; 25 | 26 | public OverloadException() { 27 | super((String) null); 28 | } 29 | 30 | public OverloadException(String message) { 31 | super(message); 32 | } 33 | 34 | public OverloadException(String message, Throwable cause) { 35 | super(message, cause); 36 | } 37 | 38 | public OverloadException(Throwable cause) { 39 | super(cause); 40 | } 41 | 42 | public OverloadException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 43 | super(message, cause, enableSuppression, writableStackTrace); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/exceptions/TransportException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.exceptions; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public class TransportException extends RuntimeException { 24 | private static final long serialVersionUID = 1L; 25 | 26 | public TransportException() { 27 | super((String) null); 28 | } 29 | 30 | public TransportException(String message) { 31 | super(message); 32 | } 33 | 34 | public TransportException(String message, Throwable cause) { 35 | super(message, cause); 36 | } 37 | 38 | public TransportException(Throwable cause) { 39 | super(cause); 40 | } 41 | 42 | public TransportException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 43 | super(message, cause, enableSuppression, writableStackTrace); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/session/DefaultSession.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.session; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.CompletableFuture; 20 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.ListenableFuture; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.exceptions.TransportException; 23 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 24 | 25 | import java.net.InetSocketAddress; 26 | 27 | /** 28 | * @author Leon Chen 29 | * @since 1.0.0 30 | */ 31 | public class DefaultSession implements Session { 32 | 33 | protected final Transport transport; 34 | 35 | public DefaultSession(Transport transport) { 36 | this.transport = transport; 37 | } 38 | 39 | @Override 40 | public long getId() { 41 | return transport.getId(); 42 | } 43 | 44 | @Override 45 | public ConnectionStatus getStatus() { 46 | return this.transport.getStatus(); 47 | } 48 | 49 | @Override 50 | public String getLocalAddress(String value) { 51 | if (value != null) return value; 52 | return ((InetSocketAddress) transport.getLocalAddress()).getAddress().getHostAddress(); 53 | } 54 | 55 | @Override 56 | public String getRemoteAddress(String value) { 57 | if (value != null) return value; 58 | return ((InetSocketAddress) transport.getRemoteAddress()).getAddress().getHostAddress(); 59 | } 60 | 61 | @Override 62 | public CompletableFuture send(T message) { 63 | if (transport.getStatus() == ConnectionStatus.CONNECTED) { 64 | return transport.write(message, true); 65 | } else { 66 | CompletableFuture r = new ListenableFuture<>(); 67 | r.failure(new TransportException("connection disconnected: " + toString())); 68 | return r; 69 | } 70 | } 71 | 72 | @Override 73 | public CompletableFuture disconnect(Throwable cause) { 74 | return transport.disconnect(cause); 75 | } 76 | 77 | @Override 78 | public String toString() { 79 | return transport.toString(); 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/session/Session.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.session; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.CompletableFuture; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus; 21 | 22 | /** 23 | * @author Leon Chen 24 | * @since 1.0.0 25 | */ 26 | public interface Session { 27 | 28 | long getId(); 29 | 30 | ConnectionStatus getStatus(); 31 | 32 | String getLocalAddress(String value); 33 | 34 | String getRemoteAddress(String value); 35 | 36 | CompletableFuture send(T message); 37 | 38 | CompletableFuture disconnect(Throwable cause); 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/transport/AbstractTransport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.transport; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.net.AbstractNioBootstrap; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.exceptions.OverloadException; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.exceptions.TransportException; 22 | import io.netty.channel.ChannelHandlerContext; 23 | import io.netty.channel.SimpleChannelInboundHandler; 24 | 25 | import java.io.IOException; 26 | import java.util.concurrent.atomic.AtomicInteger; 27 | 28 | /** 29 | * @author Leon Chen 30 | * @since 1.0.0 31 | */ 32 | @SuppressWarnings("unchecked") 33 | public abstract class AbstractTransport extends SimpleChannelInboundHandler implements Transport { 34 | 35 | private static AtomicInteger acc = new AtomicInteger(); 36 | protected volatile TransportListener listener; 37 | private long id; 38 | 39 | protected AbstractTransport(AbstractNioBootstrap listener) { 40 | super((Class) Object.class); 41 | this.listener = listener; 42 | this.id = acc.incrementAndGet(); 43 | } 44 | 45 | @Override 46 | public boolean acceptInboundMessage(Object msg) throws Exception { 47 | return true; 48 | } 49 | 50 | @Override 51 | public long getId() { 52 | return id; 53 | } 54 | 55 | @Override 56 | public synchronized TransportListener setTransportListener(TransportListener listener) { 57 | TransportListener r = this.listener; 58 | this.listener = listener; 59 | return r; 60 | } 61 | 62 | @Override 63 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 64 | TransportListener listener = this.listener; 65 | if (listener != null) listener.onConnected(this); 66 | } 67 | 68 | @Override 69 | public void channelInactive(ChannelHandlerContext ctx) throws Exception { 70 | TransportListener listener = this.listener; 71 | if (listener != null) listener.onDisconnected(this, null); 72 | } 73 | 74 | @Override 75 | protected void channelRead0(ChannelHandlerContext ctx, T message) throws Exception { 76 | TransportListener listener = this.listener; 77 | if (listener != null) listener.onMessage(this, message); 78 | } 79 | 80 | @Override 81 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 82 | if (cause instanceof IOException) cause = new TransportException(toString(), cause); 83 | TransportListener listener = this.listener; 84 | if (listener != null) listener.onException(this, cause); 85 | } 86 | 87 | @Override 88 | public final void channelWritabilityChanged(final ChannelHandlerContext ctx) throws Exception { 89 | if (ctx.channel().isWritable()) return; 90 | TransportListener listener = this.listener; 91 | if (listener != null) listener.onException(this, new OverloadException("overload")); 92 | } 93 | 94 | @Override 95 | public String toString() { 96 | return "[" + "id=" + id + 97 | ",la=" + getLocalAddress() + 98 | ",ra=" + getRemoteAddress() + "]"; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/transport/NioAcceptorTransport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.transport; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.CompletableFuture; 20 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.ListenableChannelFuture; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.AbstractNioBootstrap; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus; 23 | import io.netty.channel.ChannelHandlerContext; 24 | 25 | import java.net.SocketAddress; 26 | 27 | import static com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus.CONNECTED; 28 | import static com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus.DISCONNECTED; 29 | 30 | /** 31 | * @author Leon Chen 32 | * @since 1.0.0 33 | */ 34 | public class NioAcceptorTransport extends AbstractTransport { 35 | 36 | private volatile ChannelHandlerContext context; 37 | 38 | public NioAcceptorTransport(AbstractNioBootstrap listener) { 39 | super(listener); 40 | } 41 | 42 | @Override 43 | public ConnectionStatus getStatus() { 44 | if (this.context == null) return DISCONNECTED; 45 | return this.context.channel().isActive() ? CONNECTED : DISCONNECTED; 46 | } 47 | 48 | @Override 49 | public SocketAddress getLocalAddress() { 50 | if (this.context == null) return null; 51 | return this.context.channel().localAddress(); 52 | } 53 | 54 | @Override 55 | public SocketAddress getRemoteAddress() { 56 | if (this.context == null) return null; 57 | return this.context.channel().remoteAddress(); 58 | } 59 | 60 | @Override 61 | public CompletableFuture disconnect(Throwable cause) { 62 | return new ListenableChannelFuture<>(this.context.close()); 63 | } 64 | 65 | @Override 66 | public CompletableFuture write(V message, boolean flush) { 67 | if (!flush) { 68 | return new ListenableChannelFuture<>(context.write(message)); 69 | } else { 70 | return new ListenableChannelFuture<>(context.writeAndFlush(message)); 71 | } 72 | } 73 | 74 | @Override 75 | public void channelActive(ChannelHandlerContext ctx) throws Exception { 76 | super.channelActive(this.context = ctx); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/transport/NioInitiatorTransport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.transport; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.CompletableFuture; 20 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.ListenableChannelFuture; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.AbstractNioBootstrap; 22 | import com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus; 23 | import io.netty.channel.Channel; 24 | 25 | import java.net.SocketAddress; 26 | 27 | import static com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus.CONNECTED; 28 | import static com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus.DISCONNECTED; 29 | 30 | /** 31 | * @author Leon Chen 32 | * @since 1.0.0 33 | */ 34 | public class NioInitiatorTransport extends AbstractTransport { 35 | 36 | private volatile Channel channel; 37 | 38 | public NioInitiatorTransport(AbstractNioBootstrap listener) { 39 | super(listener); 40 | } 41 | 42 | @Override 43 | public ConnectionStatus getStatus() { 44 | if (this.channel == null) return DISCONNECTED; 45 | return this.channel.isActive() ? CONNECTED : DISCONNECTED; 46 | } 47 | 48 | @Override 49 | public SocketAddress getLocalAddress() { 50 | if (this.channel == null) return null; 51 | return this.channel.localAddress(); 52 | } 53 | 54 | @Override 55 | public SocketAddress getRemoteAddress() { 56 | if (this.channel == null) return null; 57 | return this.channel.remoteAddress(); 58 | } 59 | 60 | @Override 61 | public CompletableFuture disconnect(Throwable cause) { 62 | return new ListenableChannelFuture<>(channel.close()); 63 | } 64 | 65 | @Override 66 | public CompletableFuture write(V message, boolean flush) { 67 | if (!flush) { 68 | return new ListenableChannelFuture<>(channel.write(message)); 69 | } else { 70 | return new ListenableChannelFuture<>(channel.writeAndFlush(message)); 71 | } 72 | } 73 | 74 | public void setChannel(Channel channel) { 75 | this.channel = channel; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/transport/Transport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.transport; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.concurrent.future.CompletableFuture; 20 | import com.moilioncircle.redis.cluster.watchdog.util.net.ConnectionStatus; 21 | 22 | import java.net.SocketAddress; 23 | 24 | /** 25 | * @author Leon Chen 26 | * @since 1.0.0 27 | */ 28 | public interface Transport { 29 | 30 | long getId(); 31 | 32 | ConnectionStatus getStatus(); 33 | 34 | SocketAddress getLocalAddress(); 35 | 36 | SocketAddress getRemoteAddress(); 37 | 38 | CompletableFuture disconnect(Throwable cause); 39 | 40 | CompletableFuture write(V message, boolean flush); 41 | 42 | TransportListener setTransportListener(TransportListener listener); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/net/transport/TransportListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.net.transport; 18 | 19 | /** 20 | * @author Leon Chen 21 | * @since 1.0.0 22 | */ 23 | public interface TransportListener { 24 | 25 | void onConnected(Transport transport); 26 | 27 | void onMessage(Transport transport, T message); 28 | 29 | void onException(Transport transport, Throwable cause); 30 | 31 | void onDisconnected(Transport transport, Throwable cause); 32 | 33 | abstract class Adaptor implements TransportListener { 34 | 35 | public void onConnected(Transport transport) { 36 | } 37 | 38 | public void onMessage(Transport transport, T message) { 39 | } 40 | 41 | public void onException(Transport transport, Throwable cause) { 42 | } 43 | 44 | public void onDisconnected(Transport transport, Throwable cause) { 45 | } 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/type/Tuple1.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.type; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.Iterators; 20 | 21 | import java.lang.reflect.Array; 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | import java.util.Iterator; 25 | import java.util.List; 26 | import java.util.function.Function; 27 | 28 | /** 29 | * @author Leon Chen 30 | * @since 1.0.0 31 | */ 32 | @SuppressWarnings("unchecked") 33 | public class Tuple1 implements Iterable { 34 | private final T1 v1; 35 | 36 | public Tuple1(T1 v1) { 37 | this.v1 = v1; 38 | } 39 | 40 | public Tuple1(Tuple1 rhs) { 41 | this.v1 = rhs.getV1(); 42 | } 43 | 44 | public static Tuple1 from(V... ary) { 45 | if (ary == null || ary.length != 1) throw new IllegalArgumentException(); 46 | return new Tuple1<>(ary[0]); 47 | } 48 | 49 | public static Tuple1 from(Iterator iterator) { 50 | List list = new ArrayList<>(); 51 | while (iterator.hasNext()) { 52 | list.add(iterator.next()); 53 | } 54 | return from(list.toArray((V[]) new Object[list.size()])); 55 | } 56 | 57 | public static Tuple1 from(Iterable iterable) { 58 | return from(iterable.iterator()); 59 | } 60 | 61 | public static Tuple1 from(Collection collection) { 62 | return from((Iterable) collection); 63 | } 64 | 65 | public T1 getV1() { 66 | return v1; 67 | } 68 | 69 | public Tuple1 map(Function, Tuple1> function) { 70 | return function.apply(this); 71 | } 72 | 73 | @Override 74 | public boolean equals(Object o) { 75 | if (this == o) return true; 76 | if (o == null || getClass() != o.getClass()) return false; 77 | 78 | Tuple1 tuple1 = (Tuple1) o; 79 | 80 | return v1 != null ? v1.equals(tuple1.v1) : tuple1.v1 == null; 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | return v1 != null ? v1.hashCode() : 0; 86 | } 87 | 88 | @Override 89 | public Iterator iterator() { 90 | return Iterators.iterator(getV1()); 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return "[" + v1 + "]"; 96 | } 97 | 98 | public Object[] toArray() { 99 | return new Object[]{getV1()}; 100 | } 101 | 102 | public T[] toArray(Class clazz) { 103 | T[] ary = (T[]) Array.newInstance(clazz, 5); 104 | if (!clazz.isInstance(getV1())) throw new UnsupportedOperationException(); 105 | ary[0] = (T) getV1(); 106 | return ary; 107 | } 108 | 109 | public T toObject(Function, T> func) { 110 | return func.apply(this); 111 | } 112 | } -------------------------------------------------------------------------------- /src/main/java/com/moilioncircle/redis/cluster/watchdog/util/type/Tuple2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog.util.type; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.util.Iterators; 20 | 21 | import java.lang.reflect.Array; 22 | import java.util.ArrayList; 23 | import java.util.Collection; 24 | import java.util.Iterator; 25 | import java.util.List; 26 | import java.util.function.Function; 27 | 28 | /** 29 | * @author Leon Chen 30 | * @since 1.0.0 31 | */ 32 | @SuppressWarnings("unchecked") 33 | public class Tuple2 implements Iterable { 34 | private final T1 v1; 35 | private final T2 v2; 36 | 37 | public Tuple2(T1 v1, T2 v2) { 38 | this.v1 = v1; 39 | this.v2 = v2; 40 | } 41 | 42 | public Tuple2(Tuple2 rhs) { 43 | this.v1 = rhs.getV1(); 44 | this.v2 = rhs.getV2(); 45 | } 46 | 47 | public static Tuple2 from(V... ary) { 48 | if (ary == null || ary.length != 2) throw new IllegalArgumentException(); 49 | return new Tuple2<>(ary[0], ary[1]); 50 | } 51 | 52 | public static Tuple2 from(Iterator iterator) { 53 | List list = new ArrayList<>(); 54 | while (iterator.hasNext()) { 55 | list.add(iterator.next()); 56 | } 57 | return from(list.toArray((V[]) new Object[list.size()])); 58 | } 59 | 60 | public static Tuple2 from(Iterable iterable) { 61 | return from(iterable.iterator()); 62 | } 63 | 64 | public static Tuple2 from(Collection collection) { 65 | return from((Iterable) collection); 66 | } 67 | 68 | public T1 getV1() { 69 | return v1; 70 | } 71 | 72 | public T2 getV2() { 73 | return v2; 74 | } 75 | 76 | public Tuple2 map(Function, Tuple2> function) { 77 | return function.apply(this); 78 | } 79 | 80 | @Override 81 | public boolean equals(Object o) { 82 | if (this == o) return true; 83 | if (o == null || getClass() != o.getClass()) return false; 84 | 85 | Tuple2 tuple2 = (Tuple2) o; 86 | 87 | if (v1 != null ? !v1.equals(tuple2.v1) : tuple2.v1 != null) return false; 88 | return v2 != null ? v2.equals(tuple2.v2) : tuple2.v2 == null; 89 | } 90 | 91 | @Override 92 | public int hashCode() { 93 | int result = v1 != null ? v1.hashCode() : 0; 94 | result = 31 * result + (v2 != null ? v2.hashCode() : 0); 95 | return result; 96 | } 97 | 98 | @Override 99 | public Iterator iterator() { 100 | return Iterators.iterator(getV1(), getV2()); 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "[" + v1 + ", " + v2 + "]"; 106 | } 107 | 108 | public Object[] toArray() { 109 | return new Object[]{getV1(), getV2()}; 110 | } 111 | 112 | public T[] toArray(Class clazz) { 113 | T[] ary = (T[]) Array.newInstance(clazz, 5); 114 | if (!clazz.isInstance(getV1())) throw new UnsupportedOperationException(); 115 | ary[0] = (T) getV1(); 116 | if (!clazz.isInstance(getV2())) throw new UnsupportedOperationException(); 117 | ary[1] = (T) getV2(); 118 | return ary; 119 | } 120 | 121 | public T toObject(Function, T> func) { 122 | return func.apply(this); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/test/java/com/moilioncircle/redis/cluster/watchdog/ClusterWatchdogTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | import java.io.IOException; 20 | 21 | /** 22 | * @author Leon Chen 23 | * @since 1.0.0 24 | */ 25 | public class ClusterWatchdogTest { 26 | public static void main(String[] args) throws IOException { 27 | for (int i = 1; i <= 10; i++) { 28 | final int j = i; 29 | new Thread(() -> { 30 | ClusterConfiguration c = ClusterConfiguration.defaultSetting(); 31 | c.setFailover(true).setVersion(Version.PROTOCOL_V1).setClusterAnnouncePort(10000 + j); 32 | 33 | ClusterWatchdog watchdog = new RedisClusterWatchdog(c); 34 | watchdog.start(); 35 | }).start(); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/com/moilioncircle/redis/cluster/watchdog/ReplicationTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2018 Leon Chen 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.moilioncircle.redis.cluster.watchdog; 18 | 19 | import com.moilioncircle.redis.cluster.watchdog.command.CommandHandler; 20 | import com.moilioncircle.redis.cluster.watchdog.storage.RedisStorageEngine; 21 | import com.moilioncircle.redis.cluster.watchdog.util.net.transport.Transport; 22 | import org.junit.Test; 23 | import redis.clients.jedis.Jedis; 24 | 25 | import java.util.concurrent.TimeUnit; 26 | 27 | import static junit.framework.TestCase.assertEquals; 28 | 29 | /** 30 | * @author Leon Chen 31 | * @since 1.0.0 32 | */ 33 | public class ReplicationTest { 34 | 35 | @Test 36 | public void test() { 37 | ClusterConfiguration configuration = ClusterConfiguration.defaultSetting(); 38 | configuration.setFailover(true).setVersion(Version.PROTOCOL_V0).setClusterAnnouncePort(20000); 39 | 40 | ClusterWatchdog watchdog = new RedisClusterWatchdog(configuration); 41 | watchdog.setStorageEngine(new RedisStorageEngine()); 42 | watchdog.addCommandHandler("set", new CommandHandler.Adaptor() { 43 | @Override 44 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 45 | getStorageEngine().save(rawMessage[1], rawMessage[2], 0, true); 46 | reply(t, "OK"); 47 | } 48 | }); 49 | watchdog.addCommandHandler("get", new CommandHandler.Adaptor() { 50 | @Override 51 | public void handle(Transport t, String[] message, byte[][] rawMessage) { 52 | byte[] value = (byte[]) getStorageEngine().load(rawMessage[1]); 53 | replyBulk(t, value); 54 | } 55 | }); 56 | watchdog.setClusterConfigListener(System.out::println); 57 | watchdog.setReplicationListener(new SimpleReplicationListener()); 58 | watchdog.start(); 59 | 60 | Jedis jedis = new Jedis("127.0.0.1", 20000); 61 | assertEquals("OK", jedis.set("key", "value")); 62 | assertEquals(1L, jedis.dbSize().longValue()); 63 | assertEquals("value", jedis.get("key")); 64 | jedis.close(); 65 | watchdog.stop(5, TimeUnit.SECONDS); 66 | } 67 | } -------------------------------------------------------------------------------- /src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %p %c{1.}:%L - %m%n 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | --------------------------------------------------------------------------------