org.apache.eventmesh.meta.raft.RaftMetaService Maven / Gradle / Ivy
/*
* 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 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 findEventMeshInfoByCluster(String clusterName) throws MetaException {
List listEventMeshDataInfo = new ArrayList<>();
RequestResponse req = RequestResponse.newBuilder().setValue(MetaRaftConstants.GET).build();
boolean result = false;
try {
CompletableFuture future = commit(req, EventClosure.createDefaultEventClosure());
RequestResponse requestResponse = future.get(3000, TimeUnit.MILLISECONDS);
if (requestResponse != null) {
result = requestResponse.getSuccess();
if (result) {
Map infoMap = requestResponse.getInfoMap();
for (Entry 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 findAllEventMeshInfo() throws MetaException {
return findEventMeshInfoByCluster(null);
}
@Override
public void registerMetadata(Map metadataMap) {
for (Map.Entry eventMeshRegisterInfo : eventMeshRegisterInfoMap.entrySet()) {
EventMeshRegisterInfo registerInfo = eventMeshRegisterInfo.getValue();
registerInfo.setMetadata(metadataMap);
this.register(registerInfo);
}
}
@Override
public Map getMetaData(String key, boolean fuzzyEnabled) {
Map resultMap = new HashMap<>();
RequestResponse req = RequestResponse.newBuilder().setValue(MetaRaftConstants.GET).build();
boolean result = false;
try {
CompletableFuture future = commit(req, EventClosure.createDefaultEventClosure());
RequestResponse requestResponse = future.get(3000, TimeUnit.MILLISECONDS);
if (requestResponse != null) {
result = requestResponse.getSuccess();
if (result) {
Map infoMap = requestResponse.getInfoMap();
resultMap.putAll(infoMap);
}
}
} catch (Exception e) {
throw new MetaException("fail to get meta data ", e);
}
if (fuzzyEnabled) {
// todo
} else {
Map 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 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 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 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 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 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 commit(RequestResponse requestResponse, EventClosure eventClosure)
throws RemotingException, InterruptedException {
CompletableFuture 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 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();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy