You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by as...@apache.org on 2021/02/16 18:02:28 UTC

[ignite-3] 03/04: IGNITE-14149 wip.

This is an automated email from the ASF dual-hosted git repository.

ascherbakov pushed a commit to branch ignite-14149
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 196bd82a87b720bdd4a1dac28ba607d6564d149a
Author: Alexey Scherbakov <al...@gmail.com>
AuthorDate: Tue Feb 16 18:06:33 2021 +0300

    IGNITE-14149 wip.
---
 modules/raft-client/pom.xml                        |  18 +-
 .../org/apache/ignite/raft/ElectionPriority.java   |  37 +++
 .../main/java/org/apache/ignite/raft/PeerId.java   |  78 ++++++
 .../main/java/org/apache/ignite/raft/State.java    | 286 +++++++++++++++++++++
 .../raft/client/RaftClientCommonMessages.java      | 259 +++++++++++++++++++
 .../ignite/raft/client/RaftGroupRpcClient.java     | 147 +++++++++++
 .../client/message/AddLearnersRequestImpl.java     |  37 +++
 .../raft/client/message/AddPeerRequestImpl.java    |  34 +++
 .../raft/client/message/AddPeerResponseImpl.java   |  35 +++
 .../raft/client/message/ChangePeerRequestImpl.java |  35 +++
 .../client/message/ChangePeersResponseImpl.java    |  35 +++
 .../message/ClientMessageBuilderFactory.java       |  46 ++++
 .../client/message/CreateGetLeaderRequestImpl.java |  23 ++
 .../message/CreateGetLeaderResponseImpl.java       |  22 ++
 .../raft/client/message/GetPeersRequestImpl.java   |  32 +++
 .../raft/client/message/GetPeersResponseImpl.java  |  35 +++
 .../client/message/LearnersOpResponseImpl.java     |  35 +++
 .../raft/client/message/PingRequestImpl.java       |  21 ++
 .../RaftClientCommonMessageBuilderFactory.java     |  86 +++++++
 .../client/message/RemoveLearnersRequestImpl.java  |  35 +++
 .../raft/client/message/RemovePeerRequestImpl.java |  33 +++
 .../client/message/RemovePeerResponseImpl.java     |  35 +++
 .../client/message/ResetLearnersRequestImpl.java   |  35 +++
 .../raft/client/message/ResetPeerRequestImpl.java  |  35 +++
 .../raft/client/message/SnapshotRequestImpl.java   |  22 ++
 .../raft/client/message/StatusResponseImpl.java    |  32 +++
 .../client/message/TransferLeaderRequestImpl.java  |  27 ++
 .../org/apache/ignite/raft/rpc/InvokeCallback.java |  24 ++
 .../java/org/apache/ignite/raft/rpc/Message.java   |   4 +
 .../main/java/org/apache/ignite/raft/rpc/Node.java |   7 +
 .../java/org/apache/ignite/raft/rpc/NodeImpl.java  |  48 ++++
 .../apache/ignite/raft/rpc/RaftGroupMessage.java   |   5 +
 .../java/org/apache/ignite/raft/rpc/RpcClient.java |  59 +++++
 .../ignite/raft/client/RaftGroupRpcClientTest.java |  21 ++
 34 files changed, 1720 insertions(+), 3 deletions(-)

diff --git a/modules/raft-client/pom.xml b/modules/raft-client/pom.xml
index 9286853..d2613d0 100644
--- a/modules/raft-client/pom.xml
+++ b/modules/raft-client/pom.xml
@@ -26,14 +26,26 @@
 
     <parent>
         <groupId>org.apache.ignite</groupId>
-        <artifactId>apache-ignite</artifactId>
-        <version>3.0.0-SNAPSHOT</version>
-        <relativePath>../..</relativePath>
+        <artifactId>ignite-parent</artifactId>
+        <version>1</version>
+        <relativePath>../../parent/pom.xml</relativePath>
     </parent>
 
     <artifactId>ignite-raft-client</artifactId>
+    <version>3.0.0-SNAPSHOT</version>
 
     <dependencies>
+        <dependency>
+            <groupId>org.junit.jupiter</groupId>
+            <artifactId>junit-jupiter-engine</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
     </dependencies>
 
     <build>
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/ElectionPriority.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/ElectionPriority.java
new file mode 100644
index 0000000..376a589
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/ElectionPriority.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.raft;
+
+/**
+ * Election Priority.
+ */
+public class ElectionPriority {
+    /**
+     * Priority -1 represents this node disabled the priority election function.
+     */
+    public static final int DISABLED = -1;
+
+    /**
+     * Priority 0 is a special value so that a node will never participate in election.
+     */
+    public static final int NOT_ELECTABLE = 0;
+
+    /**
+     * Priority 1 is a minimum value for priority election.
+     */
+    public static final int MIN_VALUE = 1;
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/PeerId.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/PeerId.java
new file mode 100644
index 0000000..56d96be
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/PeerId.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.raft;
+
+import java.io.Serializable;
+import org.apache.ignite.raft.rpc.Node;
+
+/**
+ * Represents a participant in a replicating group.
+ */
+public class PeerId implements Serializable {
+    private static final long serialVersionUID = 8083529734784884641L;
+
+    /**
+     * Owning node.
+     */
+    private final Node node;
+
+    /**
+     * Node's local priority value, if node don't support priority election, this value is -1.
+     */
+    private final int priority;
+
+    public PeerId(PeerId peer) {
+        this.node = peer.getNode();
+        this.priority = peer.getPriority();
+    }
+
+    public PeerId(Node node) {
+        this(node, ElectionPriority.DISABLED);
+    }
+
+    public PeerId(final Node node, final int priority) {
+        super();
+        this.node = node;
+        this.priority = priority;
+    }
+
+    public Node getNode() {
+        return this.node;
+    }
+
+    public int getPriority() {
+        return priority;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        PeerId peerId = (PeerId) o;
+
+        if (priority != peerId.priority) return false;
+        if (!node.equals(peerId.node)) return false;
+
+        return true;
+    }
+
+    @Override public int hashCode() {
+        int result = node.hashCode();
+        result = 31 * result + priority;
+        return result;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/State.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/State.java
new file mode 100644
index 0000000..d924847
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/State.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.raft;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Raft group state.
+ */
+public class State implements Iterable<PeerId> {
+    /** Mark a leaner peer */
+    private static final String LEARNER_POSTFIX = "/learner";
+
+    private PeerId leader;
+
+    private List<PeerId> peers = new ArrayList<>();
+
+    // use LinkedHashSet to keep insertion order.
+    private List<PeerId> learners = new ArrayList<>();
+
+    public State() {
+        super();
+    }
+
+    /**
+     * Construct a configuration instance with peers.
+     *
+     * @param conf configuration
+     */
+    public State(final Iterable<PeerId> conf) {
+        this(conf, null);
+    }
+
+    /**
+     * Construct a configuration from another conf.
+     *
+     * @param conf configuration
+     */
+    public State(final State conf) {
+        this(conf.getPeers(), conf.getLearners());
+    }
+
+    /**
+     * Construct a Configuration instance with peers and learners.
+     *
+     * @param conf     peers configuration
+     * @param learners learners
+     * @since 1.3.0
+     */
+    public State(final Iterable<PeerId> conf, final Iterable<PeerId> learners) {
+        for (final PeerId peer : conf) {
+            this.peers.add(new PeerId(peer));
+        }
+        addLearners(learners);
+    }
+
+    public PeerId getLeader() {
+        return leader;
+    }
+
+    public void setLeader(PeerId leader) {
+        this.leader = leader;
+    }
+
+    public void setLearners(final List<PeerId> learners) {
+        this.learners = learners;
+    }
+
+    /**
+     * Add a learner peer.
+     *
+     * @param learner learner to add
+     * @return true when add successfully.
+     */
+    public boolean addLearner(final PeerId learner) {
+        return this.learners.add(learner);
+    }
+
+    /**
+     * Add learners in batch, returns the added count.
+     *
+     * @param learners learners to add
+     * @return the total added count
+     */
+    public int addLearners(final Iterable<PeerId> learners) {
+        int ret = 0;
+        if (learners != null) {
+            for (final PeerId peer : learners) {
+                if (this.learners.add(new PeerId(peer))) {
+                    ret++;
+                }
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Remove a learner peer.
+     *
+     * @param learner learner to remove
+     * @return true when remove successfully.
+     */
+    public boolean removeLearner(final PeerId learner) {
+        return this.learners.remove(learner);
+    }
+
+    /**
+     * Retrieve the learners set.
+     *
+     * @return learners
+     */
+    public List<PeerId> getLearners() {
+        return this.learners;
+    }
+
+    /**
+     * Retrieve the learners set copy.
+     *
+     * @return learners
+     */
+    public List<PeerId> listLearners() {
+        return new ArrayList<>(this.learners);
+    }
+
+    /**
+     * Returns true when the configuration is valid.
+     *
+     * @return true if the configuration is valid.
+     */
+    public boolean isValid() {
+        final Set<PeerId> intersection = new HashSet<>(this.peers);
+        intersection.retainAll(this.learners);
+        return !this.peers.isEmpty() && intersection.isEmpty();
+    }
+
+    public void reset() {
+        this.peers.clear();
+        this.learners.clear();
+    }
+
+    public boolean isEmpty() {
+        return this.peers.isEmpty();
+    }
+
+    /**
+     * Returns the peers total number.
+     *
+     * @return total num of peers
+     */
+    public int size() {
+        return this.peers.size();
+    }
+
+    @Override
+    public Iterator<PeerId> iterator() {
+        return this.peers.iterator();
+    }
+
+    public Set<PeerId> getPeerSet() {
+        return new HashSet<>(this.peers);
+    }
+
+    public List<PeerId> listPeers() {
+        return new ArrayList<>(this.peers);
+    }
+
+    public List<PeerId> getPeers() {
+        return this.peers;
+    }
+
+    public void setPeers(final List<PeerId> peers) {
+        this.peers.clear();
+        for (final PeerId peer : peers) {
+            this.peers.add(new PeerId(peer));
+        }
+    }
+
+    public void appendPeers(final Collection<PeerId> set) {
+        this.peers.addAll(set);
+    }
+
+    public boolean addPeer(final PeerId peer) {
+        return this.peers.add(peer);
+    }
+
+    public boolean removePeer(final PeerId peer) {
+        return this.peers.remove(peer);
+    }
+
+    public boolean contains(final PeerId peer) {
+        return this.peers.contains(peer);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((this.learners == null) ? 0 : this.learners.hashCode());
+        result = prime * result + ((this.peers == null) ? 0 : this.peers.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        State other = (State) obj;
+        if (this.learners == null) {
+            if (other.learners != null) {
+                return false;
+            }
+        } else if (!this.learners.equals(other.learners)) {
+            return false;
+        }
+        if (this.peers == null) {
+            return other.peers == null;
+        } else {
+            return this.peers.equals(other.peers);
+        }
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        final List<PeerId> peers = listPeers();
+        int i = 0;
+        int size = peers.size();
+        for (final PeerId peer : peers) {
+            sb.append(peer);
+            if (i < size - 1 || !this.learners.isEmpty()) {
+                sb.append(",");
+            }
+            i++;
+        }
+
+        size = this.learners.size();
+        i = 0;
+        for (final PeerId peer : this.learners) {
+            sb.append(peer).append(LEARNER_POSTFIX);
+            if (i < size - 1) {
+                sb.append(",");
+            }
+            i++;
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Get the difference between |*this| and |rhs|
+     * |included| would be assigned to |*this| - |rhs|
+     * |excluded| would be assigned to |rhs| - |*this|
+     */
+    public void diff(final State rhs, final State included, final State excluded) {
+        included.peers = new ArrayList<>(this.peers);
+        included.peers.removeAll(rhs.peers);
+        excluded.peers = new ArrayList<>(rhs.peers);
+        excluded.peers.removeAll(this.peers);
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/RaftClientCommonMessages.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/RaftClientCommonMessages.java
new file mode 100644
index 0000000..150fe7d
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/RaftClientCommonMessages.java
@@ -0,0 +1,259 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+// source: cli.proto
+
+package org.apache.ignite.raft.client;
+
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.rpc.Message;
+import org.apache.ignite.raft.rpc.RaftGroupMessage;
+
+/**
+ *
+ */
+public final class RaftClientCommonMessages {
+    private RaftClientCommonMessages() {
+    }
+
+    public interface StatusResponse extends Message {
+        int getStatusCode();
+
+        String getStatusMsg();
+
+        interface Builder {
+            Builder setStatusCode(int code);
+
+            Builder setStatusMsg(String msg);
+
+            StatusResponse build();
+        }
+    }
+
+    public interface PingRequest extends Message {
+        long getSendTimestamp();
+
+        interface Builder {
+            Builder setSendTimestamp(long timestamp);
+
+            PingRequest build();
+        }
+    }
+
+    public interface AddPeerRequest extends RaftGroupMessage {
+        PeerId getPeerId();
+
+        interface Builder {
+            Builder setGroupId(String groupId);
+
+            Builder setPeerId(PeerId peerId);
+
+            AddPeerRequest build();
+        }
+    }
+
+    public interface AddPeerResponse extends Message {
+        List<PeerId> getOldPeersList();
+
+        List<PeerId> getNewPeersList();
+
+        public interface Builder {
+            Builder addOldPeers(PeerId oldPeersId);
+
+            Builder addNewPeers(PeerId newPeersId);
+
+            AddPeerResponse build();
+        }
+    }
+
+    public interface RemovePeerRequest extends RaftGroupMessage {
+        PeerId getPeerId();
+
+        interface Builder {
+            Builder setGroupId(String groupId);
+
+            Builder setPeerId(PeerId peerId);
+
+            RemovePeerRequest build();
+        }
+    }
+
+    public interface RemovePeerResponse extends Message {
+        List<PeerId> getOldPeersList();
+
+        List<PeerId> getNewPeersList();
+
+        public interface Builder {
+            Builder addOldPeers(PeerId oldPeerId);
+
+            Builder addNewPeers(PeerId newPeerId);
+
+            RemovePeerResponse build();
+        }
+    }
+
+    public interface ChangePeersRequest extends RaftGroupMessage {
+        List<PeerId> getNewPeersList();
+
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            Builder addNewPeers(PeerId peerId);
+
+            ChangePeersRequest build();
+        }
+    }
+
+    public interface ChangePeersResponse extends Message {
+        List<PeerId> getOldPeersList();
+
+        List<PeerId> getNewPeersList();
+
+        public interface Builder {
+            Builder addOldPeers(PeerId oldPeerId);
+
+            Builder addNewPeers(PeerId newPeerId);
+
+            ChangePeersResponse build();
+        }
+    }
+
+    public interface SnapshotRequest extends RaftGroupMessage {
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            SnapshotRequest build();
+        }
+    }
+
+    public interface ResetPeerRequest extends RaftGroupMessage {
+        List<PeerId> getNewPeersList();
+
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            Builder addNewPeers(PeerId peerId);
+
+            ResetPeerRequest build();
+        }
+    }
+
+    public interface TransferLeaderRequest extends RaftGroupMessage {
+        PeerId getPeerId();
+
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            TransferLeaderRequest build();
+        }
+    }
+
+    public interface GetLeaderRequest extends RaftGroupMessage {
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            GetLeaderRequest build();
+        }
+    }
+
+    public interface GetLeaderResponse extends Message {
+        String getLeaderId();
+
+        public interface Builder {
+            GetLeaderResponse build();
+
+            Builder setLeaderId(String leaderId);
+        }
+    }
+
+    public interface GetPeersRequest extends RaftGroupMessage {
+        boolean getOnlyAlive();
+
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            Builder setOnlyAlive(boolean onlyGetAlive);
+
+            GetPeersRequest build();
+        }
+    }
+
+    public interface GetPeersResponse extends Message {
+        List<PeerId> getPeersList();
+
+        List<PeerId> getLearnersList();
+
+        public interface Builder {
+            Builder addPeers(PeerId peerId);
+
+            Builder addLearners(PeerId learnerId);
+
+            GetPeersResponse build();
+        }
+    }
+
+    public interface AddLearnersRequest extends RaftGroupMessage {
+        List<PeerId> getLearnersList();
+
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            Builder addLearners(PeerId learnerId);
+
+            AddLearnersRequest build();
+        }
+    }
+
+    public interface RemoveLearnersRequest extends RaftGroupMessage {
+        List<PeerId> getLearnersList();
+
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            Builder addLearners(PeerId leaderId);
+
+            RemoveLearnersRequest build();
+        }
+    }
+
+    public interface ResetLearnersRequest extends RaftGroupMessage {
+        List<PeerId> getLearnersList();
+
+        public interface Builder {
+            Builder setGroupId(String groupId);
+
+            Builder addLearners(PeerId learnerId);
+
+            ResetLearnersRequest build();
+        }
+    }
+
+    public interface LearnersOpResponse extends Message {
+        List<PeerId> getOldLearnersList();
+
+        List<PeerId> getNewLearnersList();
+
+        public interface Builder {
+            Builder addOldLearners(PeerId oldLearnersId);
+
+            Builder addNewLearners(PeerId newLearnersId);
+
+            LearnersOpResponse build();
+        }
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/RaftGroupRpcClient.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/RaftGroupRpcClient.java
new file mode 100644
index 0000000..f0910d0
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/RaftGroupRpcClient.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.raft.client;
+
+import java.util.concurrent.Future;
+import org.apache.ignite.raft.State;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.rpc.Message;
+import org.apache.ignite.raft.rpc.RaftGroupMessage;
+
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.AddLearnersRequest;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.AddPeerRequest;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.AddPeerResponse;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.ChangePeersRequest;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.ChangePeersResponse;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.LearnersOpResponse;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.RemoveLearnersRequest;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.RemovePeerRequest;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.RemovePeerResponse;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.ResetLearnersRequest;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.ResetPeerRequest;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.SnapshotRequest;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.StatusResponse;
+import static org.apache.ignite.raft.client.RaftClientCommonMessages.TransferLeaderRequest;
+
+/**
+ * Raft group RPC client.
+ */
+public interface RaftGroupRpcClient {
+    /**
+     * @param groupId Group id.
+     * @param refresh Refresh state.
+     * @return Current group state.
+     */
+    State state(String groupId, boolean refresh);
+
+    /**
+     * Adds a voring peer to the raft group.
+     *
+     * @param request   request data
+     * @return A future with the result
+     */
+    Future<AddPeerResponse> addPeer(AddPeerRequest request);
+
+    /**
+     * Removes a peer from the raft group.
+     *
+     * @param endpoint  server address
+     * @param request   request data
+     * @param done      callback
+     * @return a future with result
+     */
+    Future<RemovePeerResponse> removePeer(RemovePeerRequest request);
+
+    /**
+     * Locally resets raft group peers. Intended for recovering from a group unavailability at the price of consistency.
+     *
+     * @param peerId Node to execute the configuration reset.
+     * @param request   request data
+     * @return A future with result.
+     */
+    Future<StatusResponse> resetPeers(PeerId peerId, ResetPeerRequest request);
+
+    /**
+     * Takes a local snapshot.
+     *
+     * @param peerId  Peer id.
+     * @param request   request data
+     * @param done      callback
+     * @return a future with result
+     */
+    Future<StatusResponse> snapshot(PeerId peerId, SnapshotRequest request);
+
+    /**
+     * Change peers.
+     *
+     * @param endpoint  server address
+     * @param request   request data
+     * @param done      callback
+     * @return a future with result
+     */
+    Future<ChangePeersResponse> changePeers(ChangePeersRequest request);
+
+    /**
+     * Adds learners.
+     *
+     * @param endpoint  server address
+     * @param request   request data
+     * @param done      callback
+     * @return a future with result
+     */
+    Future<LearnersOpResponse> addLearners(AddLearnersRequest request);
+
+    /**
+     * Removes learners.
+     *
+     * @param endpoint  server address
+     * @param request   request data
+     * @param done      callback
+     * @return a future with result
+     */
+    Future<LearnersOpResponse> removeLearners(RemoveLearnersRequest request);
+
+    /**
+     * Resets learners to new set.
+     *
+     * @param endpoint  server address
+     * @param request   request data
+     * @param done      callback
+     * @return a future with result
+     */
+    Future<LearnersOpResponse> resetLearners(ResetLearnersRequest request);
+
+    /**
+     * Transfer leadership to other peer.
+     *
+     * @param endpoint  server address
+     * @param request   request data
+     * @param done      callback
+     * @return a future with result
+     */
+    Future<StatusResponse> transferLeader(TransferLeaderRequest request);
+
+    /**
+     * Performs a custom action defined by specific request on the raft group leader.
+     *
+     * @param endpoint  server address
+     * @param request   request data
+     * @param done      callback
+     * @return a future with result
+     */
+    <R extends Message> Future<R> sendCustom(RaftGroupMessage request);
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddLearnersRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddLearnersRequestImpl.java
new file mode 100644
index 0000000..cc3a763
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddLearnersRequestImpl.java
@@ -0,0 +1,37 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+public class AddLearnersRequestImpl implements RaftClientCommonMessages.AddLearnersRequest, RaftClientCommonMessages.AddLearnersRequest.Builder {
+    private String groupId;
+    private PeerId leaderId;
+    private List<PeerId> learnersList = new ArrayList<>();
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public List<PeerId> getLearnersList() {
+        return learnersList;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+
+    @Override public Builder addLearners(PeerId learnerId) {
+        learnersList.add(learnerId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.AddLearnersRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddPeerRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddPeerRequestImpl.java
new file mode 100644
index 0000000..01fb298
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddPeerRequestImpl.java
@@ -0,0 +1,34 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class AddPeerRequestImpl implements RaftClientCommonMessages.AddPeerRequest, RaftClientCommonMessages.AddPeerRequest.Builder {
+    private String groupId;
+    private PeerId leaderId;
+    private PeerId peerId;
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public PeerId getPeerId() {
+        return peerId;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public Builder setPeerId(PeerId peerId) {
+        this.peerId = peerId;
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.AddPeerRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddPeerResponseImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddPeerResponseImpl.java
new file mode 100644
index 0000000..127124a
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/AddPeerResponseImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class AddPeerResponseImpl implements RaftClientCommonMessages.AddPeerResponse, RaftClientCommonMessages.AddPeerResponse.Builder {
+    private List<PeerId> oldPeersList = new ArrayList<>();
+    private List<PeerId> newPeersList = new ArrayList<>();
+
+    @Override public List<PeerId> getOldPeersList() {
+        return oldPeersList;
+    }
+
+    @Override public List<PeerId> getNewPeersList() {
+        return newPeersList;
+    }
+
+    @Override public Builder addOldPeers(PeerId oldPeersId) {
+        oldPeersList.add(oldPeersId);
+
+        return this;
+    }
+
+    @Override public Builder addNewPeers(PeerId newPeersId) {
+        newPeersList.add(newPeersId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.AddPeerResponse build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ChangePeerRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ChangePeerRequestImpl.java
new file mode 100644
index 0000000..06570ea
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ChangePeerRequestImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class ChangePeerRequestImpl implements RaftClientCommonMessages.ChangePeersRequest, RaftClientCommonMessages.ChangePeersRequest.Builder {
+    private String groupId;
+    private List<PeerId> newPeersList = new ArrayList<>();
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public List<PeerId> getNewPeersList() {
+        return newPeersList;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public Builder addNewPeers(PeerId peerId) {
+        newPeersList.add(peerId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.ChangePeersRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ChangePeersResponseImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ChangePeersResponseImpl.java
new file mode 100644
index 0000000..d22d8f0
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ChangePeersResponseImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class ChangePeersResponseImpl implements RaftClientCommonMessages.ChangePeersResponse, RaftClientCommonMessages.ChangePeersResponse.Builder {
+    private List<PeerId> oldPeersList = new ArrayList<>();
+    private List<PeerId> newPeersList = new ArrayList<>();
+
+    @Override public List<PeerId> getOldPeersList() {
+        return oldPeersList;
+    }
+
+    @Override public List<PeerId> getNewPeersList() {
+        return newPeersList;
+    }
+
+    @Override public Builder addOldPeers(PeerId oldPeerId) {
+        oldPeersList.add(oldPeerId);
+
+        return this;
+    }
+
+    @Override public Builder addNewPeers(PeerId newPeerId) {
+        newPeersList.add(newPeerId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.ChangePeersResponse build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ClientMessageBuilderFactory.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ClientMessageBuilderFactory.java
new file mode 100644
index 0000000..952fbfd
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ClientMessageBuilderFactory.java
@@ -0,0 +1,46 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+/** */
+public interface ClientMessageBuilderFactory {
+    public static ClientMessageBuilderFactory DEFAULT = new RaftClientCommonMessageBuilderFactory();
+
+    RaftClientCommonMessages.PingRequest.Builder createPingRequest();
+
+    RaftClientCommonMessages.StatusResponse.Builder createStatusResponse();
+
+    RaftClientCommonMessages.AddPeerRequest.Builder createAddPeerRequest();
+
+    RaftClientCommonMessages.AddPeerResponse.Builder createAddPeerResponse();
+
+    RaftClientCommonMessages.RemovePeerRequest.Builder createRemovePeerRequest();
+
+    RaftClientCommonMessages.RemovePeerResponse.Builder createRemovePeerResponse();
+
+    RaftClientCommonMessages.ChangePeersRequest.Builder createChangePeerRequest();
+
+    RaftClientCommonMessages.ChangePeersResponse.Builder createChangePeerResponse();
+
+    RaftClientCommonMessages.SnapshotRequest.Builder createSnapshotRequest();
+
+    RaftClientCommonMessages.ResetPeerRequest.Builder createResetPeerRequest();
+
+    RaftClientCommonMessages.TransferLeaderRequest.Builder createTransferLeaderRequest();
+
+    RaftClientCommonMessages.GetLeaderRequest.Builder createGetLeaderRequest();
+
+    RaftClientCommonMessages.GetLeaderResponse.Builder createGetLeaderResponse();
+
+    RaftClientCommonMessages.GetPeersRequest.Builder createGetPeersRequest();
+
+    RaftClientCommonMessages.GetPeersResponse.Builder createGetPeersResponse();
+
+    RaftClientCommonMessages.AddLearnersRequest.Builder createAddLearnersRequest();
+
+    RaftClientCommonMessages.RemoveLearnersRequest.Builder createRemoveLearnersRequest();
+
+    RaftClientCommonMessages.ResetLearnersRequest.Builder createResetLearnersRequest();
+
+    RaftClientCommonMessages.LearnersOpResponse.Builder createLearnersOpResponse();
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/CreateGetLeaderRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/CreateGetLeaderRequestImpl.java
new file mode 100644
index 0000000..9c44e11
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/CreateGetLeaderRequestImpl.java
@@ -0,0 +1,23 @@
+package org.apache.ignite.raft.client.message;
+
+
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+public class CreateGetLeaderRequestImpl implements RaftClientCommonMessages.GetLeaderRequest, RaftClientCommonMessages.GetLeaderRequest.Builder {
+    private String groupId;
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.GetLeaderRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/CreateGetLeaderResponseImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/CreateGetLeaderResponseImpl.java
new file mode 100644
index 0000000..5b58d48
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/CreateGetLeaderResponseImpl.java
@@ -0,0 +1,22 @@
+package org.apache.ignite.raft.client.message;
+
+
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+public class CreateGetLeaderResponseImpl implements RaftClientCommonMessages.GetLeaderResponse, RaftClientCommonMessages.GetLeaderResponse.Builder {
+    private String leaderId;
+
+    @Override public String getLeaderId() {
+        return leaderId;
+    }
+
+    @Override public RaftClientCommonMessages.GetLeaderResponse build() {
+        return this;
+    }
+
+    @Override public Builder setLeaderId(String leaderId) {
+        this.leaderId = leaderId;
+
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/GetPeersRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/GetPeersRequestImpl.java
new file mode 100644
index 0000000..1851152
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/GetPeersRequestImpl.java
@@ -0,0 +1,32 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class GetPeersRequestImpl implements RaftClientCommonMessages.GetPeersRequest, RaftClientCommonMessages.GetPeersRequest.Builder {
+    private String groupId;
+    private boolean onlyAlive;
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public boolean getOnlyAlive() {
+        return onlyAlive;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public Builder setOnlyAlive(boolean onlyGetAlive) {
+        this.onlyAlive = onlyGetAlive;
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.GetPeersRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/GetPeersResponseImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/GetPeersResponseImpl.java
new file mode 100644
index 0000000..37a4912
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/GetPeersResponseImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class GetPeersResponseImpl implements RaftClientCommonMessages.GetPeersResponse, RaftClientCommonMessages.GetPeersResponse.Builder {
+    private List<PeerId> peersList = new ArrayList<>();
+    private List<PeerId> learnersList = new ArrayList<>();
+
+    @Override public List<PeerId> getPeersList() {
+        return peersList;
+    }
+
+    @Override public List<PeerId> getLearnersList() {
+        return learnersList;
+    }
+
+    @Override public Builder addPeers(PeerId peerId) {
+        peersList.add(peerId);
+
+        return this;
+    }
+
+    @Override public Builder addLearners(PeerId learnerId) {
+        learnersList.add(learnerId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.GetPeersResponse build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/LearnersOpResponseImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/LearnersOpResponseImpl.java
new file mode 100644
index 0000000..4c3f4c1
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/LearnersOpResponseImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class LearnersOpResponseImpl implements RaftClientCommonMessages.LearnersOpResponse, RaftClientCommonMessages.LearnersOpResponse.Builder {
+    private List<PeerId> oldLearnersList = new ArrayList<>();
+    private List<PeerId> newLearnersList = new ArrayList<>();
+
+    @Override public List<PeerId> getOldLearnersList() {
+        return oldLearnersList;
+    }
+
+    @Override public List<PeerId> getNewLearnersList() {
+        return newLearnersList;
+    }
+
+    @Override public Builder addOldLearners(PeerId oldLearnersId) {
+        oldLearnersList.add(oldLearnersId);
+
+        return this;
+    }
+
+    @Override public Builder addNewLearners(PeerId newLearnersId) {
+        newLearnersList.add(newLearnersId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.LearnersOpResponse build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/PingRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/PingRequestImpl.java
new file mode 100644
index 0000000..d4ad03d
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/PingRequestImpl.java
@@ -0,0 +1,21 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class PingRequestImpl implements RaftClientCommonMessages.PingRequest, RaftClientCommonMessages.PingRequest.Builder {
+    private long sendTimestamp;
+
+    @Override public long getSendTimestamp() {
+        return sendTimestamp;
+    }
+
+    @Override public Builder setSendTimestamp(long timestamp) {
+        this.sendTimestamp = timestamp;
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.PingRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RaftClientCommonMessageBuilderFactory.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RaftClientCommonMessageBuilderFactory.java
new file mode 100644
index 0000000..f54ec48
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RaftClientCommonMessageBuilderFactory.java
@@ -0,0 +1,86 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+/**
+ * Raft client message builders factory.
+ */
+public class RaftClientCommonMessageBuilderFactory implements ClientMessageBuilderFactory {
+    public static RaftClientCommonMessageBuilderFactory DEFAULT = new RaftClientCommonMessageBuilderFactory();
+
+    @Override public RaftClientCommonMessages.PingRequest.Builder createPingRequest() {
+        return new PingRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.StatusResponse.Builder createStatusResponse() {
+        return new StatusResponseImpl();
+    }
+
+    @Override public RaftClientCommonMessages.AddPeerRequest.Builder createAddPeerRequest() {
+        return new AddPeerRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.AddPeerResponse.Builder createAddPeerResponse() {
+        return new AddPeerResponseImpl();
+    }
+
+    @Override public RaftClientCommonMessages.RemovePeerRequest.Builder createRemovePeerRequest() {
+        return new RemovePeerRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.RemovePeerResponse.Builder createRemovePeerResponse() {
+        return new RemovePeerResponseImpl();
+    }
+
+    @Override public RaftClientCommonMessages.ChangePeersRequest.Builder createChangePeerRequest() {
+        return new ChangePeerRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.ChangePeersResponse.Builder createChangePeerResponse() {
+        return new ChangePeersResponseImpl();
+    }
+
+    @Override public RaftClientCommonMessages.SnapshotRequest.Builder createSnapshotRequest() {
+        return new SnapshotRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.ResetPeerRequest.Builder createResetPeerRequest() {
+        return new ResetPeerRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.TransferLeaderRequest.Builder createTransferLeaderRequest() {
+        return new TransferLeaderRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.GetLeaderRequest.Builder createGetLeaderRequest() {
+        return new CreateGetLeaderRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.GetLeaderResponse.Builder createGetLeaderResponse() {
+        return new CreateGetLeaderResponseImpl();
+    }
+
+    @Override public RaftClientCommonMessages.GetPeersRequest.Builder createGetPeersRequest() {
+        return new GetPeersRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.GetPeersResponse.Builder createGetPeersResponse() {
+        return new GetPeersResponseImpl();
+    }
+
+    @Override public RaftClientCommonMessages.AddLearnersRequest.Builder createAddLearnersRequest() {
+        return new AddLearnersRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.RemoveLearnersRequest.Builder createRemoveLearnersRequest() {
+        return new RemoveLearnersRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.ResetLearnersRequest.Builder createResetLearnersRequest() {
+        return new ResetLearnersRequestImpl();
+    }
+
+    @Override public RaftClientCommonMessages.LearnersOpResponse.Builder createLearnersOpResponse() {
+        return new LearnersOpResponseImpl();
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemoveLearnersRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemoveLearnersRequestImpl.java
new file mode 100644
index 0000000..de0b056
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemoveLearnersRequestImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class RemoveLearnersRequestImpl implements RaftClientCommonMessages.RemoveLearnersRequest, RaftClientCommonMessages.RemoveLearnersRequest.Builder {
+    private String groupId;
+    private List<PeerId> learnersList = new ArrayList<>();
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public List<PeerId> getLearnersList() {
+        return learnersList;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public Builder addLearners(PeerId learnerId) {
+        learnersList.add(learnerId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.RemoveLearnersRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemovePeerRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemovePeerRequestImpl.java
new file mode 100644
index 0000000..a068f1d
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemovePeerRequestImpl.java
@@ -0,0 +1,33 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class RemovePeerRequestImpl implements RaftClientCommonMessages.RemovePeerRequest, RaftClientCommonMessages.RemovePeerRequest.Builder {
+    private String groupId;
+    private PeerId peerId;
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public PeerId getPeerId() {
+        return peerId;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public Builder setPeerId(PeerId peerId) {
+        this.peerId = peerId;
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.RemovePeerRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemovePeerResponseImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemovePeerResponseImpl.java
new file mode 100644
index 0000000..35d9013
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/RemovePeerResponseImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class RemovePeerResponseImpl implements RaftClientCommonMessages.RemovePeerResponse, RaftClientCommonMessages.RemovePeerResponse.Builder {
+    private List<PeerId> oldPeersList = new ArrayList<>();
+    private List<PeerId> newPeersList = new ArrayList<>();
+
+    @Override public List<PeerId> getOldPeersList() {
+        return oldPeersList;
+    }
+
+    @Override public List<PeerId> getNewPeersList() {
+        return newPeersList;
+    }
+
+    @Override public Builder addOldPeers(PeerId oldPeerId) {
+        oldPeersList.add(oldPeerId);
+
+        return this;
+    }
+
+    @Override public Builder addNewPeers(PeerId newPeerId) {
+        newPeersList.add(newPeerId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.RemovePeerResponse build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ResetLearnersRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ResetLearnersRequestImpl.java
new file mode 100644
index 0000000..1e5188d
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ResetLearnersRequestImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class ResetLearnersRequestImpl implements RaftClientCommonMessages.ResetLearnersRequest, RaftClientCommonMessages.ResetLearnersRequest.Builder {
+    private String groupId;
+    private List<PeerId> learnersList = new ArrayList<>();
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public List<PeerId> getLearnersList() {
+        return learnersList;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public Builder addLearners(PeerId learnerId) {
+        learnersList.add(learnerId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.ResetLearnersRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ResetPeerRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ResetPeerRequestImpl.java
new file mode 100644
index 0000000..bb9c28b
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/ResetPeerRequestImpl.java
@@ -0,0 +1,35 @@
+package org.apache.ignite.raft.client.message;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class ResetPeerRequestImpl implements RaftClientCommonMessages.ResetPeerRequest, RaftClientCommonMessages.ResetPeerRequest.Builder {
+    private String groupId;
+    private List<PeerId> newPeersList = new ArrayList<>();
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public List<PeerId> getNewPeersList() {
+        return newPeersList;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public Builder addNewPeers(PeerId peerId) {
+        newPeersList.add(peerId);
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.ResetPeerRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/SnapshotRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/SnapshotRequestImpl.java
new file mode 100644
index 0000000..5f1bde1
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/SnapshotRequestImpl.java
@@ -0,0 +1,22 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class SnapshotRequestImpl implements RaftClientCommonMessages.SnapshotRequest, RaftClientCommonMessages.SnapshotRequest.Builder {
+    private String groupId;
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.SnapshotRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/StatusResponseImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/StatusResponseImpl.java
new file mode 100644
index 0000000..5afcf22
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/StatusResponseImpl.java
@@ -0,0 +1,32 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class StatusResponseImpl implements RaftClientCommonMessages.StatusResponse, RaftClientCommonMessages.StatusResponse.Builder {
+    private int errorCode;
+    private String errorMsg = "";
+
+    @Override public int getStatusCode() {
+        return errorCode;
+    }
+
+    @Override public Builder setStatusCode(int errorCode) {
+        this.errorCode = errorCode;
+
+        return this;
+    }
+
+    @Override public String getStatusMsg() {
+        return errorMsg;
+    }
+
+    @Override public Builder setStatusMsg(String errorMsg) {
+        this.errorMsg = errorMsg;
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.StatusResponse build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/TransferLeaderRequestImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/TransferLeaderRequestImpl.java
new file mode 100644
index 0000000..b125aec
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/client/message/TransferLeaderRequestImpl.java
@@ -0,0 +1,27 @@
+package org.apache.ignite.raft.client.message;
+
+import org.apache.ignite.raft.PeerId;
+import org.apache.ignite.raft.client.RaftClientCommonMessages;
+
+class TransferLeaderRequestImpl implements RaftClientCommonMessages.TransferLeaderRequest, RaftClientCommonMessages.TransferLeaderRequest.Builder {
+    private String groupId;
+    private PeerId peerId;
+
+    @Override public String getGroupId() {
+        return groupId;
+    }
+
+    @Override public PeerId getPeerId() {
+        return peerId;
+    }
+
+    @Override public Builder setGroupId(String groupId) {
+        this.groupId = groupId;
+
+        return this;
+    }
+
+    @Override public RaftClientCommonMessages.TransferLeaderRequest build() {
+        return this;
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/InvokeCallback.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/InvokeCallback.java
new file mode 100644
index 0000000..c58b5e2
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/InvokeCallback.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.raft.rpc;
+
+/**
+ *
+ */
+public interface InvokeCallback {
+    void complete(final Message response, final Throwable err);
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/Message.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/Message.java
new file mode 100644
index 0000000..175d27b
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/Message.java
@@ -0,0 +1,4 @@
+package org.apache.ignite.raft.rpc;
+
+public interface Message {
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/Node.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/Node.java
new file mode 100644
index 0000000..bfff562
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/Node.java
@@ -0,0 +1,7 @@
+package org.apache.ignite.raft.rpc;
+
+import java.io.Serializable;
+
+public interface Node extends Serializable {
+    String id();
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/NodeImpl.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/NodeImpl.java
new file mode 100644
index 0000000..63556ac
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/NodeImpl.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.raft.rpc;
+
+/**
+ * An node with an immutable id.
+ */
+public class NodeImpl implements Node {
+    private final String id;
+
+    public NodeImpl(String id) {
+        super();
+        this.id = id;
+    }
+
+    @Override public String id() {
+        return null;
+    }
+
+    @Override public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        NodeImpl node = (NodeImpl) o;
+
+        if (!id.equals(node.id)) return false;
+
+        return true;
+    }
+
+    @Override public int hashCode() {
+        return id.hashCode();
+    }
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/RaftGroupMessage.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/RaftGroupMessage.java
new file mode 100644
index 0000000..0ee4561
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/RaftGroupMessage.java
@@ -0,0 +1,5 @@
+package org.apache.ignite.raft.rpc;
+
+public interface RaftGroupMessage extends Message {
+    public String getGroupId();
+}
diff --git a/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/RpcClient.java b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/RpcClient.java
new file mode 100644
index 0000000..cdb6db7
--- /dev/null
+++ b/modules/raft-client/src/main/java/org/apache/ignite/raft/rpc/RpcClient.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.ignite.raft.rpc;
+
+import java.util.concurrent.Executor;
+
+/**
+ *
+ */
+public interface RpcClient {
+    /**
+     * Check connection for given address.
+     *
+     * @param node target address
+     * @return true if there is a connection and the connection is active and writable.
+     */
+    boolean checkConnection(final NodeImpl node);
+
+    /**
+     * Check connection for given address and async to create a new one if there is no connection.
+     *
+     * @param node       target address
+     * @param createIfAbsent create a new one if there is no connection
+     * @return true if there is a connection and the connection is active and writable.
+     */
+    boolean checkConnection(final NodeImpl node, final boolean createIfAbsent);
+
+    /**
+     * Close all connections of a address.
+     *
+     * @param node target address
+     */
+    void closeConnection(final NodeImpl node);
+
+    /**
+     * Asynchronous invocation with a callback.
+     *
+     * @param node  Target node.
+     * @param request   Request object
+     * @param callback  Invoke callback.
+     * @param executor  Executor to run invoke callback.
+     * @param timeoutMs Timeout millisecond
+     */
+    void invokeAsync(final Node node, final Message request, InvokeCallback callback, Executor executor, final long timeoutMs);
+}
diff --git a/modules/raft-client/src/test/java/org/apache/ignite/raft/client/RaftGroupRpcClientTest.java b/modules/raft-client/src/test/java/org/apache/ignite/raft/client/RaftGroupRpcClientTest.java
new file mode 100644
index 0000000..d5bd8e1
--- /dev/null
+++ b/modules/raft-client/src/test/java/org/apache/ignite/raft/client/RaftGroupRpcClientTest.java
@@ -0,0 +1,21 @@
+package org.apache.ignite.raft.client;
+
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class RaftGroupRpcClientTest {
+    @BeforeEach
+    public void beforeTest() {
+
+    }
+
+    @AfterEach
+    public void afterTest() {
+    }
+
+    @Test
+    public void test() {
+
+    }
+}