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

mxsm pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/eventmesh.git


The following commit(s) were added to refs/heads/master by this push:
     new 5a409fdd9 [ISSUE #4852] Support Jraft algorithm as meta storage  
(#4862)
5a409fdd9 is described below

commit 5a409fdd9f9db7fef5ae7fa3890a736f3ad21a93
Author: Karson <[email protected]>
AuthorDate: Thu May 30 11:02:15 2024 +0800

    [ISSUE #4852] Support Jraft algorithm as meta storage  (#4862)
    
    * init raft function
    
    * init function
    
    * finish this function.
    
    * rename file and remove unnecessary plugin.
    
    * properties modify
    
    * properties modify
    
    * properties modify
---
 eventmesh-meta/eventmesh-meta-raft/build.gradle    |  55 +++
 .../eventmesh-meta-raft/gradle.properties          |  19 +
 .../apache/eventmesh/meta/raft/EventClosure.java   |  83 +++++
 .../apache/eventmesh/meta/raft/EventOperation.java |  68 ++++
 .../eventmesh/meta/raft/JraftMetaService.java      |  29 ++
 .../eventmesh/meta/raft/JraftMetaServiceImpl.java  |  78 ++++
 .../apache/eventmesh/meta/raft/JraftServer.java    |  95 +++++
 .../eventmesh/meta/raft/MetaStateMachine.java      | 193 ++++++++++
 .../eventmesh/meta/raft/RaftMetaService.java       | 398 +++++++++++++++++++++
 .../raft/config/RaftMetaStorageConfiguration.java  |  50 +++
 .../meta/raft/consts/MetaRaftConstants.java        |  47 +++
 .../eventmesh/meta/raft/rpc/MetaServerHelper.java  |  97 +++++
 .../eventmesh/meta/raft/rpc/RequestProcessor.java  |  52 +++
 .../meta/raft/snapshot/MetaSnapshotFile.java       |  63 ++++
 .../src/main/proto/request.proto                   |  32 ++
 .../org.apache.eventmesh.api.meta.MetaService      |  16 +
 eventmesh-runtime/conf/eventmesh.properties        |  14 +
 eventmesh-runtime/conf/log4j2.xml                  |   9 +
 settings.gradle                                    |   4 +
 19 files changed, 1402 insertions(+)

diff --git a/eventmesh-meta/eventmesh-meta-raft/build.gradle 
b/eventmesh-meta/eventmesh-meta-raft/build.gradle
new file mode 100644
index 000000000..386b5c6e7
--- /dev/null
+++ b/eventmesh-meta/eventmesh-meta-raft/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+plugins {
+    id 'com.google.protobuf' version '0.8.17'
+}
+
+def grpcVersion = '1.50.2' // CURRENT_GRPC_VERSION
+def protobufVersion = '3.21.5'
+def protocVersion = protobufVersion
+
+dependencies {
+    implementation ("io.grpc:grpc-protobuf:${grpcVersion}") {
+        exclude group: "com.google.protobuf", module: "protobuf-java"
+    }
+    implementation("com.google.protobuf:protobuf-java:${protobufVersion}")
+    implementation "io.grpc:grpc-stub:${grpcVersion}"
+    implementation "com.google.protobuf:protobuf-java-util:${protobufVersion}"
+    implementation "javax.annotation:javax.annotation-api:1.3.2"
+    compileOnly 'org.projectlombok:lombok'
+    annotationProcessor 'org.projectlombok:lombok'
+
+    implementation project(":eventmesh-meta:eventmesh-meta-api")
+    implementation project(":eventmesh-common")
+    implementation group: 'com.fasterxml.jackson.core', name: 
'jackson-databind', version: '2.0.1'
+    implementation "com.alipay.sofa:jraft-core:1.3.14"
+    implementation "com.alipay.sofa:rpc-grpc-impl:1.3.14"
+    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.0'
+}
+
+protobuf {
+    protoc { artifact = "com.google.protobuf:protoc:${protocVersion}" }
+    plugins {
+        grpc { artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}" }
+    }
+    generateProtoTasks {
+        all()*.plugins {
+            grpc {}
+        }
+    }
+}
diff --git a/eventmesh-meta/eventmesh-meta-raft/gradle.properties 
b/eventmesh-meta/eventmesh-meta-raft/gradle.properties
new file mode 100644
index 000000000..0010b2d01
--- /dev/null
+++ b/eventmesh-meta/eventmesh-meta-raft/gradle.properties
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+pluginType=metaStorage
+pluginName=raft
\ No newline at end of file
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/EventClosure.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/EventClosure.java
new file mode 100644
index 000000000..a568a801d
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/EventClosure.java
@@ -0,0 +1,83 @@
+/*
+ * 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.eventmesh.meta.raft;
+
+import org.apache.eventmesh.meta.raft.consts.MetaRaftConstants;
+import org.apache.eventmesh.meta.raft.rpc.RequestResponse;
+
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import com.alipay.sofa.jraft.Closure;
+import com.alipay.sofa.jraft.Status;
+
+
+public abstract class EventClosure implements Closure {
+
+    private CompletableFuture<RequestResponse> future;
+
+    private RequestResponse requestResponse;
+
+    private EventOperation eventOperation;
+
+    public static EventClosure createDefaultEventClosure() {
+        return new EventClosure() {
+
+            @Override
+            public void run(Status status) {
+
+            }
+        };
+    }
+
+    public void setFuture(CompletableFuture<RequestResponse> future) {
+        this.future = future;
+    }
+
+    public void setRequestResponse(RequestResponse requestResponse) {
+        this.requestResponse = requestResponse;
+        if (future != null) {
+            future.complete(getRequestResponse());
+        }
+    }
+
+    public RequestResponse getRequestResponse() {
+        return requestResponse;
+    }
+
+    public EventOperation getEventOperation() {
+        return eventOperation;
+    }
+
+    protected void failure(final String errorMsg, final String redirect) {
+        final RequestResponse response = 
RequestResponse.newBuilder().setSuccess(false).setErrorMsg(errorMsg)
+            .setRedirect(redirect).build();
+        setRequestResponse(response);
+    }
+
+    public void setEventOperation(EventOperation opreation) {
+        this.eventOperation = opreation;
+    }
+
+    protected void success(final Map<String, String> map) {
+
+        final RequestResponse response = 
RequestResponse.newBuilder().setValue(MetaRaftConstants.RESPONSE)
+            .setSuccess(true).putAllInfo(map).build();
+        setRequestResponse(response);
+    }
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/EventOperation.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/EventOperation.java
new file mode 100644
index 000000000..b8c7a6cd5
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/EventOperation.java
@@ -0,0 +1,68 @@
+/*
+ * 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.eventmesh.meta.raft;
+
+import org.apache.eventmesh.meta.raft.consts.MetaRaftConstants;
+import org.apache.eventmesh.meta.raft.rpc.RequestResponse;
+
+import java.io.Serializable;
+import java.util.Map;
+
+
+public class EventOperation implements Serializable {
+
+    private static final long serialVersionUID = -6597003954824547294L;
+
+    public static final byte PUT = 0x01;
+
+    public static final byte GET = 0x02;
+
+    public static final byte DELETE = 0x03;
+
+    private byte op;
+    private Map<String, String> data;
+
+    public static EventOperation createOpreation(RequestResponse response) {
+        if (response.getValue() == MetaRaftConstants.PUT) {
+            return new EventOperation(PUT, response.getInfoMap());
+        } else if (response.getValue() == MetaRaftConstants.GET) {
+            return new EventOperation(GET, response.getInfoMap());
+        } else if (response.getValue() == MetaRaftConstants.DELETE) {
+            return new EventOperation(DELETE, response.getInfoMap());
+
+        }
+        return null;
+    }
+
+    public EventOperation(byte op, Map<String, String> data) {
+        this.op = op;
+        this.data = data;
+    }
+
+    public byte getOp() {
+        return op;
+    }
+
+    public Map<String, String> getData() {
+        return data;
+    }
+
+    public boolean isReadOp() {
+        return GET == this.op;
+    }
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftMetaService.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftMetaService.java
new file mode 100644
index 000000000..daf636ccd
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftMetaService.java
@@ -0,0 +1,29 @@
+/*
+ * 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.eventmesh.meta.raft;
+
+
+import org.apache.eventmesh.meta.raft.rpc.RequestResponse;
+
+/**
+ * MetaService.
+ */
+public interface JraftMetaService {
+
+    void handle(RequestResponse request, EventClosure closure);
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftMetaServiceImpl.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftMetaServiceImpl.java
new file mode 100644
index 000000000..1af6d5c96
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftMetaServiceImpl.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.eventmesh.meta.raft;
+
+import org.apache.eventmesh.meta.raft.rpc.RequestResponse;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.nio.ByteBuffer;
+
+import com.alipay.remoting.exception.CodecException;
+import com.alipay.remoting.serialization.SerializerManager;
+import com.alipay.sofa.jraft.Status;
+import com.alipay.sofa.jraft.entity.Task;
+import com.alipay.sofa.jraft.error.RaftError;
+
+public class JraftMetaServiceImpl implements JraftMetaService {
+
+    JraftServer server;
+
+
+    public JraftMetaServiceImpl(JraftServer server) {
+        this.server = server;
+    }
+
+    @Override
+    public void handle(RequestResponse request, EventClosure closure) {
+        applyOperation(EventOperation.createOpreation(request), closure);
+    }
+
+    public void applyOperation(EventOperation opreation, EventClosure closure) 
{
+        if (!isLeader()) {
+            handlerNotLeaderError(closure);
+            return;
+        }
+        try {
+            closure.setEventOperation(opreation);
+            final Task task = new Task();
+            
task.setData(ByteBuffer.wrap(SerializerManager.getSerializer(SerializerManager.Hessian2).serialize(opreation)));
+            task.setDone(closure);
+            this.server.getNode().apply(task);
+        } catch (CodecException e) {
+            String errorMsg = "Fail to encode EventOperation";
+            closure.failure(errorMsg, StringUtils.EMPTY);
+            closure.run(new Status(RaftError.EINTERNAL, errorMsg));
+        }
+    }
+
+
+    private String getRedirect() {
+        return this.server.redirect().getRedirect();
+    }
+
+    private boolean isLeader() {
+        return this.server.getFsm().isLeader();
+    }
+
+
+    private void handlerNotLeaderError(final EventClosure closure) {
+        closure.failure("Not leader.", getRedirect());
+        closure.run(new Status(RaftError.EPERM, "Not leader"));
+    }
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftServer.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftServer.java
new file mode 100644
index 000000000..ad8cb14d9
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/JraftServer.java
@@ -0,0 +1,95 @@
+/*
+ * 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.eventmesh.meta.raft;
+
+import org.apache.eventmesh.meta.raft.rpc.MetaServerHelper;
+import org.apache.eventmesh.meta.raft.rpc.RequestProcessor;
+import org.apache.eventmesh.meta.raft.rpc.RequestResponse;
+
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.alipay.sofa.jraft.Node;
+import com.alipay.sofa.jraft.RaftGroupService;
+import com.alipay.sofa.jraft.entity.PeerId;
+import com.alipay.sofa.jraft.option.NodeOptions;
+import com.alipay.sofa.jraft.rpc.RaftRpcServerFactory;
+import com.alipay.sofa.jraft.rpc.RpcServer;
+
+
+public class JraftServer {
+    
+    private RaftGroupService raftGroupService;
+    
+    private Node node;
+    
+    private MetaStateMachine fsm = new MetaStateMachine();
+    
+    public MetaStateMachine getFsm() {
+        return fsm;
+    }
+
+    private JraftMetaServiceImpl metaImpl;
+    
+    public JraftServer(final String dataPath, final String groupId, final 
PeerId serverId,
+            final NodeOptions nodeOptions) throws IOException {
+        // init raft data path, it contains log,meta,snapshot
+        FileUtils.forceMkdir(new File(dataPath));
+        // here use same RPC server for raft and business. It also can be 
seperated generally
+        final RpcServer rpcServer = 
RaftRpcServerFactory.createRaftRpcServer(serverId.getEndpoint());
+        MetaServerHelper.initGRpc();
+        MetaServerHelper.setRpcServer(rpcServer);
+        // register business processor
+        metaImpl = new JraftMetaServiceImpl(this);
+        rpcServer.registerProcessor(new RequestProcessor(metaImpl));
+        nodeOptions.setFsm(this.fsm);
+        // set storage path (log,meta,snapshot)
+        // log, must
+        nodeOptions.setLogUri(dataPath + File.separator + "log");
+        // meta, must
+        nodeOptions.setRaftMetaUri(dataPath + File.separator + "raft_meta");
+        // snapshot, optional, generally recommended
+        nodeOptions.setSnapshotUri(dataPath + File.separator + "snapshot");
+        // init raft group service framework
+        this.raftGroupService = new RaftGroupService(groupId, serverId, 
nodeOptions, rpcServer);
+        // start raft node
+        this.node = this.raftGroupService.start();
+        
+    }
+
+    public RequestResponse redirect() {
+        final RequestResponse.Builder builder = 
RequestResponse.newBuilder().setSuccess(false);
+        if (this.node != null) {
+            final PeerId leader = this.node.getLeaderId();
+            if (leader != null) {
+                builder.setRedirect(leader.toString());
+            }
+        }
+        return builder.build();
+    }
+
+    public JraftMetaServiceImpl getMetaImpl() {
+        return metaImpl;
+    }
+
+    public Node getNode() {
+        return node;
+    }
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/MetaStateMachine.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/MetaStateMachine.java
new file mode 100644
index 000000000..a0607f5ab
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/MetaStateMachine.java
@@ -0,0 +1,193 @@
+/*
+ * 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.eventmesh.meta.raft;
+
+import static org.apache.eventmesh.meta.raft.EventOperation.DELETE;
+import static org.apache.eventmesh.meta.raft.EventOperation.GET;
+import static org.apache.eventmesh.meta.raft.EventOperation.PUT;
+
+import org.apache.eventmesh.meta.raft.snapshot.MetaSnapshotFile;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alipay.remoting.exception.CodecException;
+import com.alipay.remoting.serialization.SerializerManager;
+import com.alipay.sofa.jraft.Closure;
+import com.alipay.sofa.jraft.Iterator;
+import com.alipay.sofa.jraft.Status;
+import com.alipay.sofa.jraft.core.StateMachineAdapter;
+import com.alipay.sofa.jraft.error.RaftError;
+import com.alipay.sofa.jraft.storage.snapshot.SnapshotReader;
+import com.alipay.sofa.jraft.storage.snapshot.SnapshotWriter;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MetaStateMachine extends StateMachineAdapter {
+
+    private final AtomicLong leaderTerm = new AtomicLong(-1);
+
+    private static ObjectMapper objectMapper = new ObjectMapper();
+
+    private final ScheduledExecutorService executor = 
Executors.newSingleThreadScheduledExecutor();
+
+    private Map<String, String> contentTable = new ConcurrentHashMap<>();
+
+    public boolean isLeader() {
+        return this.leaderTerm.get() > 0;
+    }
+
+    @Override
+    public boolean onSnapshotLoad(final SnapshotReader reader) {
+        if (isLeader()) {
+            log.warn("Leader is not supposed to load snapshot");
+            return false;
+        }
+        if (reader.getFileMeta("data") == null) {
+            log.error("Fail to find data file in {}", reader.getPath());
+            return false;
+        }
+        final MetaSnapshotFile snapshot = new 
MetaSnapshotFile(reader.getPath() + File.separator + "data");
+        try {
+            Map<String, String> snapshotLoaded = 
objectMapper.readValue(snapshot.load(), Map.class);
+            contentTable.clear();
+            contentTable.putAll(snapshotLoaded);
+            return true;
+        } catch (final IOException e) {
+            log.error("Fail to load snapshot from {}", snapshot.getPath());
+            return false;
+        }
+
+    }
+
+    @Override
+    public void onSnapshotSave(SnapshotWriter writer, Closure done) {
+        executor.submit(() -> {
+            final MetaSnapshotFile snapshot = new 
MetaSnapshotFile(writer.getPath() + File.separator + "data");
+            try {
+                if 
(snapshot.save(objectMapper.writeValueAsString(contentTable))) {
+                    if (writer.addFile("data")) {
+                        done.run(Status.OK());
+                    } else {
+                        done.run(new Status(RaftError.EIO, "Fail to add file 
to writer"));
+                    }
+                } else {
+                    done.run(new Status(RaftError.EIO, "Fail to save snapshot 
%s", snapshot.getPath()));
+                }
+            } catch (IOException e) {
+                done.run(new Status(RaftError.EIO, "Fail to deserialize 
snapshot %s", snapshot.getPath()));
+            }
+        });
+    }
+
+    @Override
+    public void onApply(Iterator iter) {
+        while (iter.hasNext()) {
+            Exception e1 = null;
+            EventOperation eventOperation = null;
+            EventClosure closure = null;
+            if (iter.done() != null) {
+                // This task is applied by this node, get value from closure 
to avoid additional parsing.
+                closure = (EventClosure) iter.done();
+                eventOperation = closure.getEventOperation();
+            } else {
+                // Have to parse FetchAddRequest from this user log.
+                final ByteBuffer data = iter.getData();
+                try {
+                    eventOperation = 
SerializerManager.getSerializer(SerializerManager.Hessian2)
+                        .deserialize(data.array(), 
EventOperation.class.getName());
+                } catch (final CodecException e) {
+                    e.printStackTrace(System.err);
+                    e1 = e;
+
+                }
+                // follower ignore read operation
+                if (eventOperation != null && eventOperation.isReadOp()) {
+                    iter.next();
+                    continue;
+                }
+            }
+            if (eventOperation != null) {
+                switch (eventOperation.getOp()) {
+                    case GET:
+                        break;
+                    case PUT:
+                        Map<String, String> tempTable = 
eventOperation.getData();
+                        contentTable.putAll(tempTable);
+                        log.info("update MetaStateMachine successfully {}", 
contentTable);
+                        break;
+                    case DELETE:
+                        Map<String, String> tempTable2 = 
eventOperation.getData();
+                        tempTable2.forEach((key, value) -> {
+                            String remove = contentTable.remove(key);
+                            if (Objects.isNull(remove)) {
+                                log.warn("delete MetaStateMachine key: {} 
fail.", remove);
+                            } else {
+                                log.info("delete MetaStateMachine key: {} 
successfully.", remove);
+                            }
+
+                        });
+
+                        break;
+                    default:
+                        break;
+                }
+
+                if (closure != null) {
+                    if (e1 != null) {
+                        closure.failure(e1.getMessage(), StringUtils.EMPTY);
+                    } else {
+                        if (eventOperation.getOp() == PUT) {
+                            closure.success(Collections.EMPTY_MAP);
+                        } else {
+                            
closure.success(Collections.unmodifiableMap(contentTable));
+                        }
+
+                    }
+                    closure.run(Status.OK());
+                }
+            }
+            iter.next();
+        }
+    }
+
+    @Override
+    public void onLeaderStart(final long term) {
+        this.leaderTerm.set(term);
+        super.onLeaderStart(term);
+
+    }
+
+    @Override
+    public void onLeaderStop(final Status status) {
+        this.leaderTerm.set(-1);
+        super.onLeaderStop(status);
+    }
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/RaftMetaService.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/RaftMetaService.java
new file mode 100644
index 000000000..d9cf37163
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/RaftMetaService.java
@@ -0,0 +1,398 @@
+/*
+ * 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.eventmesh.meta.raft;
+
+import org.apache.eventmesh.api.exception.MetaException;
+import org.apache.eventmesh.api.meta.MetaService;
+import org.apache.eventmesh.api.meta.MetaServiceListener;
+import org.apache.eventmesh.api.meta.config.EventMeshMetaConfig;
+import org.apache.eventmesh.api.meta.dto.EventMeshDataInfo;
+import org.apache.eventmesh.api.meta.dto.EventMeshRegisterInfo;
+import org.apache.eventmesh.api.meta.dto.EventMeshUnRegisterInfo;
+import org.apache.eventmesh.common.config.ConfigService;
+import org.apache.eventmesh.common.utils.ConfigurationContextUtil;
+import org.apache.eventmesh.meta.raft.config.RaftMetaStorageConfiguration;
+import org.apache.eventmesh.meta.raft.consts.MetaRaftConstants;
+import org.apache.eventmesh.meta.raft.rpc.MetaServerHelper;
+import org.apache.eventmesh.meta.raft.rpc.RequestResponse;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.alipay.sofa.jraft.CliService;
+import com.alipay.sofa.jraft.RaftServiceFactory;
+import com.alipay.sofa.jraft.RouteTable;
+import com.alipay.sofa.jraft.conf.Configuration;
+import com.alipay.sofa.jraft.core.CliServiceImpl;
+import com.alipay.sofa.jraft.entity.PeerId;
+import com.alipay.sofa.jraft.error.RemotingException;
+import com.alipay.sofa.jraft.option.CliOptions;
+import com.alipay.sofa.jraft.option.NodeOptions;
+import com.alipay.sofa.jraft.rpc.impl.cli.CliClientServiceImpl;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class RaftMetaService implements MetaService {
+
+    private static ObjectMapper objectMapper = new ObjectMapper();
+
+    private ConcurrentMap<String, EventMeshRegisterInfo> 
eventMeshRegisterInfoMap;
+
+    private final AtomicBoolean initStatus = new AtomicBoolean(false);
+
+    private final AtomicBoolean startStatus = new AtomicBoolean(false);
+
+    RaftMetaStorageConfiguration configuration;
+
+    private JraftServer jraftServer;
+
+    private CliService cliService;
+
+    private CliClientServiceImpl cliClientService;
+
+    private PeerId leader;
+
+    private final ScheduledExecutorService scheduledExecutorService = 
Executors.newSingleThreadScheduledExecutor();
+
+
+    @Override
+    public void init() throws MetaException {
+        if (!initStatus.compareAndSet(false, true)) {
+            return;
+        }
+        eventMeshRegisterInfoMap = new 
ConcurrentHashMap<>(ConfigurationContextUtil.KEYS.size());
+        ConfigService configService = ConfigService.getInstance();
+        configuration = 
configService.buildConfigInstance(RaftMetaStorageConfiguration.class);
+    }
+
+    @Override
+    public void start() throws MetaException {
+        final String dataPath = configuration.getDataPath();
+        final String groupId = MetaRaftConstants.GROUP;
+        final String serverIdStr = configuration.getSelfIpAndPort();
+        final String initConfStr = configuration.getMembersIpAndPort();
+        final NodeOptions nodeOptions = new NodeOptions();
+        nodeOptions.setElectionTimeoutMs(configuration.getElectionTimeoutMs() 
* 1000);
+        nodeOptions.setDisableCli(false);
+        
nodeOptions.setSnapshotIntervalSecs(configuration.getSnapshotIntervalSecs());
+        final PeerId serverId = new PeerId();
+        if (!serverId.parse(serverIdStr)) {
+            throw new MetaException("Fail to parse serverId:" + serverIdStr);
+        }
+        final Configuration initConf = new Configuration();
+        if (!initConf.parse(initConfStr)) {
+            throw new MetaException("Fail to parse initConf:" + initConfStr);
+        }
+        initConf.addPeer(serverId);
+        nodeOptions.setInitialConf(initConf);
+        try {
+            jraftServer = new JraftServer(dataPath, groupId, serverId, 
nodeOptions);
+        } catch (IOException e) {
+            throw new MetaException("fail to start jraft server", e);
+        }
+        log.info("Started jraft server at port: {}", 
jraftServer.getNode().getNodeId().getPeerId().getPort());
+
+        final Configuration conf = new Configuration();
+        if (!conf.parse(serverIdStr)) {
+            throw new IllegalArgumentException("Fail to parse conf:" + 
serverIdStr);
+        }
+        RouteTable.getInstance().updateConfiguration(MetaRaftConstants.GROUP, 
conf);
+        cliService = RaftServiceFactory.createAndInitCliService(new 
CliOptions());
+        cliClientService = (CliClientServiceImpl) ((CliServiceImpl) 
this.cliService).getCliClientService();
+        while (true) {
+            try {
+                refreshleader();
+                if (this.leader != null) {
+                    break;
+                }
+            } catch (Exception e) {
+                log.warn("fail to get leader node");
+                try {
+                    Thread.sleep(3000L);
+                } catch (InterruptedException ex) {
+                    Thread.currentThread().interrupt();
+                }
+            }
+        }
+        scheduledExecutorService.scheduleAtFixedRate(() -> {
+            try {
+                RaftMetaService.this.refreshleader();
+            } catch (Exception e) {
+                log.error("fail to Refresh Leader", e);
+            }
+        }, configuration.getRefreshLeaderInterval(), 
configuration.getRefreshLeaderInterval(), TimeUnit.SECONDS);
+
+        startStatus.compareAndSet(false, true);
+    }
+
+    private void refreshleader() throws InterruptedException, TimeoutException 
{
+        if (!RouteTable.getInstance().refreshLeader(cliClientService, 
MetaRaftConstants.GROUP, 3000).isOk()) {
+            throw new IllegalStateException("Refresh leader failed");
+        }
+        this.leader = 
RouteTable.getInstance().selectLeader(MetaRaftConstants.GROUP);
+        log.info("raft Leader is {}", leader);
+    }
+
+    @Override
+    public void shutdown() throws MetaException {
+        if (!startStatus.compareAndSet(true, false)) {
+            return;
+        }
+        scheduledExecutorService.shutdown();
+        MetaServerHelper.shutDown();
+        if (cliService != null) {
+            cliService.shutdown();
+        }
+        if (cliClientService != null) {
+            cliClientService.shutdown();
+        }
+    }
+
+    @Override
+    public List<EventMeshDataInfo> findEventMeshInfoByCluster(String 
clusterName) throws MetaException {
+        List<EventMeshDataInfo> listEventMeshDataInfo = new ArrayList<>();
+        RequestResponse req = 
RequestResponse.newBuilder().setValue(MetaRaftConstants.GET).build();
+        boolean result = false;
+        try {
+            CompletableFuture<RequestResponse> future = commit(req, 
EventClosure.createDefaultEventClosure());
+            RequestResponse requestResponse = future.get(3000, 
TimeUnit.MILLISECONDS);
+            if (requestResponse != null) {
+                result = requestResponse.getSuccess();
+                if (result) {
+                    Map<String, String> infoMap = requestResponse.getInfoMap();
+                    for (Entry<String, String> entry : infoMap.entrySet()) {
+                        String key = entry.getKey();
+                        String value = entry.getValue();
+                        if (key.startsWith("eventMeshInfo@@")) {
+                            if (Objects.isNull(clusterName)) {
+                                if (!key.endsWith("@@" + clusterName)) {
+                                    continue;
+                                }
+                            }
+                            EventMeshDataInfo eventMeshDataInfo = 
objectMapper.readValue(value, EventMeshDataInfo.class);
+                            listEventMeshDataInfo.add(eventMeshDataInfo);
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            throw new MetaException("fail to get meta data ", e);
+        }
+        return listEventMeshDataInfo;
+    }
+
+    @Override
+    public List<EventMeshDataInfo> findAllEventMeshInfo() throws MetaException 
{
+        return findEventMeshInfoByCluster(null);
+    }
+
+    @Override
+    public void registerMetadata(Map<String, String> metadataMap) {
+        for (Map.Entry<String, EventMeshRegisterInfo> eventMeshRegisterInfo : 
eventMeshRegisterInfoMap.entrySet()) {
+            EventMeshRegisterInfo registerInfo = 
eventMeshRegisterInfo.getValue();
+            registerInfo.setMetadata(metadataMap);
+            this.register(registerInfo);
+        }
+    }
+
+    @Override
+    public Map<String, String> getMetaData(String key, boolean fuzzyEnabled) {
+        Map<String, String> resultMap = new HashMap<>();
+        RequestResponse req = 
RequestResponse.newBuilder().setValue(MetaRaftConstants.GET).build();
+        boolean result = false;
+        try {
+            CompletableFuture<RequestResponse> future = commit(req, 
EventClosure.createDefaultEventClosure());
+            RequestResponse requestResponse = future.get(3000, 
TimeUnit.MILLISECONDS);
+            if (requestResponse != null) {
+                result = requestResponse.getSuccess();
+                if (result) {
+                    Map<String, String> infoMap = requestResponse.getInfoMap();
+                    resultMap.putAll(infoMap);
+                }
+            }
+        } catch (Exception e) {
+            throw new MetaException("fail to get meta data ", e);
+        }
+        if (fuzzyEnabled) {
+            // todo
+        } else {
+            Map<String, String> finalResult = new HashMap<>();
+            finalResult.put(key, resultMap.get(key));
+            return finalResult;
+        }
+
+        return resultMap;
+    }
+
+    @Override
+    public void getMetaDataWithListener(MetaServiceListener 
metaServiceListener, String key) {
+        //todo
+    }
+
+    @Override
+    public void updateMetaData(Map<String, String> metadataMap) {
+        String protocol = 
metadataMap.get(EventMeshMetaConfig.EVENT_MESH_PROTO);
+        String reftDataId = "Raft" + "@@" + protocol;
+        boolean result = false;
+        try {
+            RequestResponse req =
+                
RequestResponse.newBuilder().setValue(MetaRaftConstants.PUT).putInfo(reftDataId,
 objectMapper.writeValueAsString(metadataMap))
+                    .build();
+            CompletableFuture<RequestResponse> future = commit(req, 
EventClosure.createDefaultEventClosure());
+            RequestResponse requestResponse = future.get(3000, 
TimeUnit.MILLISECONDS);
+            if (requestResponse != null) {
+                result = requestResponse.getSuccess();
+            }
+        } catch (Exception e) {
+            throw new MetaException("fail to serialize ", e);
+        }
+        if (!result) {
+            throw new MetaException("fail to updateMetaData ");
+        }
+
+    }
+
+    @Override
+    public void removeMetaData(String key) {
+        RequestResponse req = 
RequestResponse.newBuilder().setValue(MetaRaftConstants.DELETE).putInfo(key, 
StringUtils.EMPTY).build();
+
+        try {
+            CompletableFuture<RequestResponse> future = commit(req, 
EventClosure.createDefaultEventClosure());
+            RequestResponse requestResponse = future.get(3000, 
TimeUnit.MILLISECONDS);
+            if (requestResponse != null) {
+                boolean result = requestResponse.getSuccess();
+                if (result) {
+                    throw new MetaException("fail to remove MetaData");
+                }
+            }
+        } catch (Exception e) {
+            throw new MetaException("fail to remove MetaData", e);
+        }
+
+    }
+
+    @Override
+    public boolean register(EventMeshRegisterInfo eventMeshRegisterInfo) 
throws MetaException {
+        //key= 
eventMeshInfo@@eventMeshName@@IP@@PORT@@protocolType@@CLUSTER_NAME
+        String eventMeshName = eventMeshRegisterInfo.getEventMeshName();
+        String protocolType = eventMeshRegisterInfo.getProtocolType();
+        String[] ipAndPort = eventMeshRegisterInfo.getEndPoint().split(":");
+        String clusterName = eventMeshRegisterInfo.getEventMeshClusterName();
+        String key = "eventMeshInfo" + "@@" + eventMeshName + "@@" + 
ipAndPort[0] + "@@" + ipAndPort[1] + "@@" + protocolType + "@@" + clusterName;
+        InfoInner infoInner = new InfoInner(eventMeshRegisterInfo);
+        String registerInfo = null;
+        boolean result = false;
+        try {
+            registerInfo = objectMapper.writeValueAsString(infoInner);
+            RequestResponse req = 
RequestResponse.newBuilder().setValue(MetaRaftConstants.PUT).putInfo(key, 
registerInfo).build();
+            CompletableFuture<RequestResponse> future = commit(req, 
EventClosure.createDefaultEventClosure());
+            RequestResponse requestResponse = future.get(3000, 
TimeUnit.MILLISECONDS);
+            if (requestResponse != null) {
+                result = requestResponse.getSuccess();
+            }
+        } catch (Exception e) {
+            throw new MetaException("fail to serialize ", e);
+        }
+        if (result) {
+            eventMeshRegisterInfoMap.put(eventMeshName, eventMeshRegisterInfo);
+        }
+        return result;
+    }
+
+    @Override
+    public boolean unRegister(EventMeshUnRegisterInfo eventMeshUnRegisterInfo) 
throws MetaException {
+        //key= 
eventMeshInfo@@eventMeshName@@IP@@PORT@@protocolType@@CLUSTER_NAME
+        String eventMeshName = eventMeshUnRegisterInfo.getEventMeshName();
+        String protocolType = eventMeshUnRegisterInfo.getProtocolType();
+        String[] ipAndPort = eventMeshUnRegisterInfo.getEndPoint().split(":");
+        String clusterName = eventMeshUnRegisterInfo.getEventMeshClusterName();
+        String key = "eventMeshInfo" + "@@" + eventMeshName + "@@" + 
ipAndPort[0] + "@@" + ipAndPort[1] + "@@" + protocolType + "@@" + clusterName;
+        RequestResponse req = 
RequestResponse.newBuilder().setValue(MetaRaftConstants.DELETE).putInfo(key, 
StringUtils.EMPTY).build();
+        boolean result = false;
+        try {
+            CompletableFuture<RequestResponse> future = commit(req, 
EventClosure.createDefaultEventClosure());
+            RequestResponse requestResponse = future.get(3000, 
TimeUnit.MILLISECONDS);
+            if (requestResponse != null) {
+                result = requestResponse.getSuccess();
+            }
+        } catch (Exception e) {
+            throw new MetaException(e.getMessage(), e);
+        }
+        if (result) {
+            eventMeshRegisterInfoMap.remove(eventMeshName);
+        }
+        return result;
+    }
+
+    @Data
+    class InfoInner implements Serializable {
+
+        EventMeshRegisterInfo eventMeshRegisterInfo;
+
+        public InfoInner(EventMeshRegisterInfo eventMeshRegisterInfo) {
+            this.eventMeshRegisterInfo = eventMeshRegisterInfo;
+        }
+    }
+
+    public CompletableFuture<RequestResponse> commit(RequestResponse 
requestResponse, EventClosure eventClosure)
+        throws RemotingException, InterruptedException {
+        CompletableFuture<RequestResponse> future = new CompletableFuture<>();
+        eventClosure.setFuture(future);
+        if (isLeader()) {
+            this.jraftServer.getMetaImpl().handle(requestResponse, 
eventClosure);
+        } else {
+            invokeToLeader(requestResponse, future);
+        }
+        return future;
+    }
+
+    private void invokeToLeader(RequestResponse requestResponse, 
CompletableFuture<RequestResponse> future)
+        throws RemotingException, InterruptedException {
+        cliClientService.getRpcClient().invokeAsync(leader.getEndpoint(), 
requestResponse, (result, err) -> {
+            if (err != null) {
+                future.completeExceptionally(err);
+                return;
+            }
+            future.complete((RequestResponse) result);
+        }, 3000);
+    }
+
+
+    private boolean isLeader() {
+        return this.jraftServer.getFsm().isLeader();
+    }
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/config/RaftMetaStorageConfiguration.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/config/RaftMetaStorageConfiguration.java
new file mode 100644
index 000000000..d5f4a8019
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/config/RaftMetaStorageConfiguration.java
@@ -0,0 +1,50 @@
+/*
+ * 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.eventmesh.meta.raft.config;
+
+import org.apache.eventmesh.common.config.Config;
+import org.apache.eventmesh.common.config.ConfigField;
+import org.apache.eventmesh.meta.raft.consts.MetaRaftConstants;
+
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@Config(prefix = "eventMesh.metaStorage.raft")
+public class RaftMetaStorageConfiguration {
+
+    @ConfigField(field = MetaRaftConstants.DATAPATH)
+    private String dataPath;
+
+    @ConfigField(field = MetaRaftConstants.SELF)
+    private String selfIpAndPort;
+
+    @ConfigField(field = MetaRaftConstants.MEMBERS)
+    private String membersIpAndPort;
+
+    @ConfigField(field = MetaRaftConstants.ELECTIONTIMEOUT)
+    private Integer electionTimeoutMs;
+
+    @ConfigField(field = MetaRaftConstants.SNAPSHOTINTERVAL)
+    private Integer snapshotIntervalSecs;
+
+    @ConfigField(field = MetaRaftConstants.REFRESHLEADERINTERVAL)
+    private Integer refreshLeaderInterval;
+
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/consts/MetaRaftConstants.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/consts/MetaRaftConstants.java
new file mode 100644
index 000000000..6be76855e
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/consts/MetaRaftConstants.java
@@ -0,0 +1,47 @@
+/*
+ * 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.eventmesh.meta.raft.consts;
+
+/**
+ * MetaRaftConstants.
+ */
+
+public interface MetaRaftConstants {
+
+    String GROUP = "EM_META";
+
+    String SELF = "self";
+
+    String MEMBERS = "members";
+
+    String DATAPATH = "dataPath";
+
+    String ELECTIONTIMEOUT = "electionTimeout";
+
+    String SNAPSHOTINTERVAL = "snapshotInterval";
+
+    String REFRESHLEADERINTERVAL = "refreshLeaderInterval";
+
+    int PUT = 1;
+
+    int GET = 2;
+
+    int DELETE = 3;
+
+    int RESPONSE = 4;
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/rpc/MetaServerHelper.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/rpc/MetaServerHelper.java
new file mode 100644
index 000000000..abaeb58d8
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/rpc/MetaServerHelper.java
@@ -0,0 +1,97 @@
+/*
+ * 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.eventmesh.meta.raft.rpc;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.TimeUnit;
+
+import com.alipay.sofa.jraft.rpc.RpcServer;
+import com.alipay.sofa.jraft.util.RpcFactoryHelper;
+import com.google.protobuf.Message;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MetaServerHelper {
+
+    public static RpcServer rpcServer;
+
+
+    public static void setRpcServer(RpcServer rpcServer) {
+        MetaServerHelper.rpcServer = rpcServer;
+    }
+
+
+    public static void initGRpc() {
+        if ("com.alipay.sofa.jraft.rpc.impl.GrpcRaftRpcFactory".equals(
+            RpcFactoryHelper.rpcFactory().getClass().getName())) {
+            RpcFactoryHelper.rpcFactory()
+                .registerProtobufSerializer(RequestResponse.class.getName(),
+                    RequestResponse.getDefaultInstance());
+            try {
+                Class<?> clazz = 
Class.forName("com.alipay.sofa.jraft.rpc.impl.MarshallerHelper");
+                Method registerRespInstance = 
clazz.getMethod("registerRespInstance", String.class, Message.class);
+                registerRespInstance.invoke(null, 
RequestResponse.class.getName(),
+                    RequestResponse.getDefaultInstance());
+            } catch (Exception e) {
+                log.error(e.getMessage(), e);
+            }
+        }
+    }
+
+    public static void shutDown() {
+        if (rpcServer == null) {
+            return;
+        }
+        rpcServer.shutdown();
+    }
+
+    public static void blockUntilShutdown() {
+        if (rpcServer == null) {
+            return;
+        }
+        if ("com.alipay.sofa.jraft.rpc.impl.GrpcRaftRpcFactory".equals(
+            RpcFactoryHelper.rpcFactory().getClass().getName())) {
+            try {
+                Method getServer = rpcServer.getClass().getMethod("getServer");
+                Object grpcServer = getServer.invoke(rpcServer);
+
+                Method shutdown = grpcServer.getClass().getMethod("shutdown");
+                Method awaitTerminationLimit = grpcServer.getClass()
+                    .getMethod("awaitTermination", long.class, TimeUnit.class);
+
+                Runtime.getRuntime().addShutdownHook(new Thread() {
+                    @Override
+                    public void run() {
+                        try {
+                            shutdown.invoke(grpcServer);
+                            awaitTerminationLimit.invoke(grpcServer, 30, 
TimeUnit.SECONDS);
+                        } catch (Exception e) {
+                            // Use stderr here since the logger may have been 
reset by its JVM shutdown hook.
+                            log.error(e.getMessage(), e);
+                        }
+                    }
+                });
+                Method awaitTermination = 
grpcServer.getClass().getMethod("awaitTermination");
+                awaitTermination.invoke(grpcServer);
+            } catch (Exception e) {
+                log.error(e.getMessage(), e);
+            }
+        }
+    }
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/rpc/RequestProcessor.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/rpc/RequestProcessor.java
new file mode 100644
index 000000000..2fd77ec5b
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/rpc/RequestProcessor.java
@@ -0,0 +1,52 @@
+/*
+ * 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.eventmesh.meta.raft.rpc;
+
+import org.apache.eventmesh.meta.raft.EventClosure;
+import org.apache.eventmesh.meta.raft.JraftMetaService;
+
+import com.alipay.sofa.jraft.Status;
+import com.alipay.sofa.jraft.rpc.RpcContext;
+import com.alipay.sofa.jraft.rpc.RpcProcessor;
+
+
+public class RequestProcessor implements RpcProcessor<RequestResponse> {
+
+    private final JraftMetaService metaService;
+
+    public RequestProcessor(JraftMetaService metaService) {
+        super();
+        this.metaService = metaService;
+    }
+
+    @Override
+    public void handleRequest(final RpcContext rpcCtx, final RequestResponse 
request) {
+        final EventClosure closure = new EventClosure() {
+            @Override
+            public void run(Status status) {
+                rpcCtx.sendResponse(getRequestResponse());
+            }
+        };
+        this.metaService.handle(request, closure);
+    }
+
+    @Override
+    public String interest() {
+        return RequestResponse.class.getName();
+    }
+}
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/snapshot/MetaSnapshotFile.java
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/snapshot/MetaSnapshotFile.java
new file mode 100644
index 000000000..b185829a8
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/java/org/apache/eventmesh/meta/raft/snapshot/MetaSnapshotFile.java
@@ -0,0 +1,63 @@
+/*
+ * 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.eventmesh.meta.raft.snapshot;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang.StringUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class MetaSnapshotFile {
+
+    private String path;
+
+    public MetaSnapshotFile(String path) {
+        super();
+        this.path = path;
+    }
+
+    public String getPath() {
+        return this.path;
+    }
+
+    /**
+     * Save value to snapshot file.
+     */
+    public boolean save(final String str) {
+        try {
+            FileUtils.writeStringToFile(new File(path), str, 
Charset.forName("UTF-8"));
+            return true;
+        } catch (IOException e) {
+            log.error("Fail to save snapshot", e);
+            return false;
+        }
+    }
+
+    public String load() throws IOException {
+        final String s = FileUtils.readFileToString(new File(path), 
Charset.forName("UTF-8"));
+        if (!StringUtils.isBlank(s)) {
+            return s;
+        }
+        throw new IOException("Fail to load snapshot from " + path + 
",content: " + s);
+    }
+}
diff --git a/eventmesh-meta/eventmesh-meta-raft/src/main/proto/request.proto 
b/eventmesh-meta/eventmesh-meta-raft/src/main/proto/request.proto
new file mode 100644
index 000000000..686549585
--- /dev/null
+++ b/eventmesh-meta/eventmesh-meta-raft/src/main/proto/request.proto
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+
+option java_package = "org.apache.eventmesh.meta.raft.rpc";
+
+message RequestResponse {
+  int64 value = 1;
+  bool success = 2;
+  string redirect = 3;
+  string errorMsg = 4;
+  map<string, string> info = 5;
+}
+
+
diff --git 
a/eventmesh-meta/eventmesh-meta-raft/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.meta.MetaService
 
b/eventmesh-meta/eventmesh-meta-raft/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.meta.MetaService
new file mode 100644
index 000000000..4771d7fc1
--- /dev/null
+++ 
b/eventmesh-meta/eventmesh-meta-raft/src/main/resources/META-INF/eventmesh/org.apache.eventmesh.api.meta.MetaService
@@ -0,0 +1,16 @@
+# 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.
+
+raft=org.apache.eventmesh.meta.raft.RaftMetaService
\ No newline at end of file
diff --git a/eventmesh-runtime/conf/eventmesh.properties 
b/eventmesh-runtime/conf/eventmesh.properties
index 91a422fa4..4984181ca 100644
--- a/eventmesh-runtime/conf/eventmesh.properties
+++ b/eventmesh-runtime/conf/eventmesh.properties
@@ -85,6 +85,20 @@ eventMesh.metaStorage.plugin.server-addr=127.0.0.1:8848
 eventMesh.metaStorage.plugin.username=nacos
 eventMesh.metaStorage.plugin.password=nacos
 
+# metaStorage plugin: raft
+# Path to local snapshot storage for raft data
+#eventMesh.metaStorage.raft.dataPath=/tmp/eventmesh-meta-raft
+# Raft local ip and port
+#eventMesh.metaStorage.raft.self=127.0.0.1:9091
+# Members ip and port
+#eventMesh.metaStorage.raft.members=192.168.1.2:9091,192.168.1.3:9091
+# Raft leader election timeout second
+#eventMesh.metaStorage.raft.electionTimeout=5
+# Raft snapshot interval second
+#eventMesh.metaStorage.raft.snapshotInterval=30
+# Raft refresh leader interval second
+#eventMesh.metaStorage.raft.refreshLeaderInterval=3
+
 # metaStorage plugin: nacos
 #eventMesh.metaStorage.nacos.endpoint=
 #eventMesh.metaStorage.nacos.accessKey=
diff --git a/eventmesh-runtime/conf/log4j2.xml 
b/eventmesh-runtime/conf/log4j2.xml
index 3ce5ac985..6341a0e62 100644
--- a/eventmesh-runtime/conf/log4j2.xml
+++ b/eventmesh-runtime/conf/log4j2.xml
@@ -95,5 +95,14 @@
         <AsyncLogger name="subscribeLogger" level="info" additivity="false" 
includeLocation="true">
             <AppenderRef ref="console"/>
         </AsyncLogger>
+
+        <AsyncLogger name="com.alipay.sofa.jraft" level="info" 
additivity="false" includeLocation="true">
+            <AppenderRef ref="console"/>
+        </AsyncLogger>
+
+        <AsyncLogger name="io.grpc.netty" level="info" additivity="false" 
includeLocation="true">
+            <AppenderRef ref="console"/>
+        </AsyncLogger>
+
     </Loggers>
 </Configuration>
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index e56b11ce5..9388e2076 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -95,6 +95,7 @@ include 'eventmesh-meta:eventmesh-meta-nacos'
 include 'eventmesh-meta:eventmesh-meta-etcd'
 include 'eventmesh-meta:eventmesh-meta-consul'
 include 'eventmesh-meta:eventmesh-meta-zookeeper'
+include 'eventmesh-meta:eventmesh-meta-raft'
 
 include 'eventmesh-protocol-plugin'
 include 'eventmesh-protocol-plugin:eventmesh-protocol-api'
@@ -124,3 +125,6 @@ include 'eventmesh-webhook:eventmesh-webhook-receive'
 include 'eventmesh-retry'
 include 'eventmesh-retry:eventmesh-retry-api'
 include 'eventmesh-retry:eventmesh-retry-rocketmq'
+
+
+


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to