diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilter.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilter.java new file mode 100644 index 0000000000..e6c7e2d982 --- /dev/null +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilter.java @@ -0,0 +1,76 @@ +/* + * 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.hugegraph.api.filter; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.net.URI; +import java.net.URISyntaxException; + +import jakarta.ws.rs.NameBinding; +import jakarta.ws.rs.container.ContainerRequestContext; +import jakarta.ws.rs.container.ContainerRequestFilter; +import jakarta.ws.rs.core.Response; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.client.utils.URIBuilder; +import org.apache.hugegraph.election.GlobalMasterInfo; +import org.apache.hugegraph.util.Log; +import org.slf4j.Logger; + +public class RedirectFilter implements ContainerRequestFilter { + + private static final Logger LOG = Log.logger(RedirectFilter.class); + + @Override + public void filter(ContainerRequestContext requestContext) throws IOException { + GlobalMasterInfo instance = GlobalMasterInfo.instance(); + if (!instance.isFeatureSupport()) { + return; + } + + String url = ""; + synchronized (instance) { + if (instance.isMaster() || StringUtils.isEmpty(instance.url())) { + return; + } + url = instance.url(); + } + + URI redirectUri = null; + try { + URIBuilder redirectURIBuilder = new URIBuilder(requestContext.getUriInfo().getAbsolutePath()); + String[] host = url.split(":"); + redirectURIBuilder.setHost(host[0]); + if (host.length == 2 && StringUtils.isNotEmpty(host[1].trim())) { + redirectURIBuilder.setPort(Integer.parseInt(host[1].trim())); + } + + redirectUri = redirectURIBuilder.build(); + } catch (URISyntaxException e) { + LOG.error("Redirect request exception occurred", e); + return; + } + requestContext.abortWith(Response.temporaryRedirect(redirectUri).build()); + } + + @NameBinding + @Retention(RetentionPolicy.RUNTIME) + public @interface RedirectMasterRole { + } +} diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilterDynamicFeature.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilterDynamicFeature.java new file mode 100644 index 0000000000..0b62693583 --- /dev/null +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/filter/RedirectFilterDynamicFeature.java @@ -0,0 +1,34 @@ +/* + * 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.hugegraph.api.filter; + +import jakarta.ws.rs.container.DynamicFeature; +import jakarta.ws.rs.container.ResourceInfo; +import jakarta.ws.rs.core.FeatureContext; +import jakarta.ws.rs.ext.Provider; + +@Provider +public class RedirectFilterDynamicFeature implements DynamicFeature { + + @Override + public void configure(ResourceInfo resourceInfo, FeatureContext context) { + if (resourceInfo.getResourceMethod().isAnnotationPresent(RedirectFilter.RedirectMasterRole.class)) { + context.register(RedirectFilter.class); + } + } +} diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/AlgorithmAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/AlgorithmAPI.java index c9b8dbf9b1..ebb172a9e9 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/AlgorithmAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/AlgorithmAPI.java @@ -19,6 +19,7 @@ import java.util.Map; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.core.GraphManager; import org.apache.hugegraph.server.RestServer; import org.slf4j.Logger; @@ -56,6 +57,7 @@ public class AlgorithmAPI extends API { @Status(Status.CREATED) @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) + @RedirectFilter.RedirectMasterRole public Map post(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String algorithm, diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/ComputerAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/ComputerAPI.java index 42b79a67ad..88b229bdf4 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/ComputerAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/ComputerAPI.java @@ -29,6 +29,7 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.Context; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.core.GraphManager; import org.slf4j.Logger; @@ -58,6 +59,7 @@ public class ComputerAPI extends API { @Status(Status.CREATED) @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) + @RedirectFilter.RedirectMasterRole public Map post(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String computer, diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/GremlinAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/GremlinAPI.java index 6a47ec5ad9..c343d2df2a 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/GremlinAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/GremlinAPI.java @@ -35,6 +35,7 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.Context; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.core.GraphManager; import org.apache.hugegraph.define.Checkable; import org.apache.hugegraph.metrics.MetricsUtil; @@ -58,6 +59,7 @@ @Path("graphs/{graph}/jobs/gremlin") @Singleton @Tag(name = "GremlinAPI") +@RedirectFilter.RedirectMasterRole public class GremlinAPI extends API { private static final Logger LOG = Log.logger(GremlinAPI.class); diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/RebuildAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/RebuildAPI.java index 354a729450..0ac93811ef 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/RebuildAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/RebuildAPI.java @@ -28,6 +28,7 @@ import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.Context; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.api.filter.StatusFilter.Status; import org.apache.hugegraph.core.GraphManager; import org.slf4j.Logger; @@ -42,6 +43,7 @@ @Path("graphs/{graph}/jobs/rebuild") @Singleton @Tag(name = "RebuildAPI") +@RedirectFilter.RedirectMasterRole public class RebuildAPI extends API { private static final Logger LOG = Log.logger(RebuildAPI.class); diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/TaskAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/TaskAPI.java index 67a445e2c6..30f8d3bdd3 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/TaskAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/job/TaskAPI.java @@ -39,6 +39,7 @@ import jakarta.ws.rs.core.Context; import org.apache.groovy.util.Maps; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.api.filter.StatusFilter.Status; import org.apache.hugegraph.core.GraphManager; import org.slf4j.Logger; @@ -132,6 +133,7 @@ public Map get(@Context GraphManager manager, @DELETE @Timed @Path("{id}") + @RedirectFilter.RedirectMasterRole public void delete(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("id") long id) { @@ -147,6 +149,7 @@ public void delete(@Context GraphManager manager, @Path("{id}") @Status(Status.ACCEPTED) @Produces(APPLICATION_JSON_WITH_CHARSET) + @RedirectFilter.RedirectMasterRole public Map update(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("id") long id, diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/EdgeLabelAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/EdgeLabelAPI.java index 02ce5a6f1e..5ec2785e2a 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/EdgeLabelAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/EdgeLabelAPI.java @@ -36,6 +36,7 @@ import jakarta.ws.rs.core.Context; import org.apache.commons.collections.CollectionUtils; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.core.GraphManager; import org.apache.hugegraph.define.Checkable; import org.slf4j.Logger; @@ -68,6 +69,7 @@ public class EdgeLabelAPI extends API { @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=edge_label_write"}) + @RedirectFilter.RedirectMasterRole public String create(@Context GraphManager manager, @PathParam("graph") String graph, JsonEdgeLabel jsonEdgeLabel) { @@ -86,6 +88,7 @@ public String create(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=edge_label_write"}) + @RedirectFilter.RedirectMasterRole public String update(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name, @@ -156,6 +159,7 @@ public String get(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=edge_label_delete"}) + @RedirectFilter.RedirectMasterRole public Map delete(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name) { diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/IndexLabelAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/IndexLabelAPI.java index 33710bd4c3..16be06e513 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/IndexLabelAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/IndexLabelAPI.java @@ -36,6 +36,7 @@ import jakarta.ws.rs.core.Context; import org.apache.commons.collections.CollectionUtils; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.core.GraphManager; import org.apache.hugegraph.define.Checkable; import org.slf4j.Logger; @@ -70,6 +71,7 @@ public class IndexLabelAPI extends API { @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=index_label_write"}) + @RedirectFilter.RedirectMasterRole public String create(@Context GraphManager manager, @PathParam("graph") String graph, JsonIndexLabel jsonIndexLabel) { @@ -88,6 +90,7 @@ public String create(@Context GraphManager manager, @Path("{name}") @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) + @RedirectFilter.RedirectMasterRole public String update(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name, @@ -157,6 +160,7 @@ public String get(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=index_label_delete"}) + @RedirectFilter.RedirectMasterRole public Map delete(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name) { diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/PropertyKeyAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/PropertyKeyAPI.java index c7ddd66655..893dcd9c69 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/PropertyKeyAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/PropertyKeyAPI.java @@ -36,6 +36,7 @@ import jakarta.ws.rs.core.Context; import org.apache.commons.collections.CollectionUtils; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.core.GraphManager; import org.apache.hugegraph.define.Checkable; import org.slf4j.Logger; @@ -73,6 +74,7 @@ public class PropertyKeyAPI extends API { @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=property_key_write"}) + @RedirectFilter.RedirectMasterRole public String create(@Context GraphManager manager, @PathParam("graph") String graph, JsonPropertyKey jsonPropertyKey) { @@ -93,6 +95,7 @@ public String create(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=property_key_write"}) + @RedirectFilter.RedirectMasterRole public String update(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name, @@ -178,6 +181,7 @@ public String get(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=property_key_delete"}) + @RedirectFilter.RedirectMasterRole public Map delete(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name) { diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/VertexLabelAPI.java b/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/VertexLabelAPI.java index 8c62490abb..0ad5a5d8e3 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/VertexLabelAPI.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/api/schema/VertexLabelAPI.java @@ -36,6 +36,7 @@ import jakarta.ws.rs.core.Context; import org.apache.commons.collections.CollectionUtils; +import org.apache.hugegraph.api.filter.RedirectFilter; import org.apache.hugegraph.core.GraphManager; import org.apache.hugegraph.define.Checkable; import org.slf4j.Logger; @@ -68,6 +69,7 @@ public class VertexLabelAPI extends API { @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=vertex_label_write"}) + @RedirectFilter.RedirectMasterRole public String create(@Context GraphManager manager, @PathParam("graph") String graph, JsonVertexLabel jsonVertexLabel) { @@ -87,6 +89,7 @@ public String create(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=vertex_label_write"}) + @RedirectFilter.RedirectMasterRole public String update(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name, @@ -159,6 +162,7 @@ public String get(@Context GraphManager manager, @Consumes(APPLICATION_JSON) @Produces(APPLICATION_JSON_WITH_CHARSET) @RolesAllowed({"admin", "$owner=$graph $action=vertex_label_delete"}) + @RedirectFilter.RedirectMasterRole public Map delete(@Context GraphManager manager, @PathParam("graph") String graph, @PathParam("name") String name) { diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java index 9a7a8dc134..6e60e6fd24 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/auth/StandardAuthenticator.java @@ -126,6 +126,7 @@ public void setup(HugeConfig config) { String raftGroupPeers = config.get(ServerOptions.RAFT_GROUP_PEERS); graphConfig.addProperty(ServerOptions.RAFT_GROUP_PEERS.name(), raftGroupPeers); + this.addRoleWorkerConfig(graphConfig, config); this.graph = (HugeGraph) GraphFactory.open(graphConfig); @@ -137,6 +138,21 @@ public void setup(HugeConfig config) { } } + private void addRoleWorkerConfig(HugeConfig graphConfig, HugeConfig config) { + graphConfig.addProperty(CoreOptions.NODE_EXTERNAL_URL.name(), + config.get(CoreOptions.NODE_EXTERNAL_URL)); + graphConfig.addProperty(CoreOptions.BASE_TIMEOUT_MILLISECOND.name(), + config.get(CoreOptions.BASE_TIMEOUT_MILLISECOND)); + graphConfig.addProperty(CoreOptions.EXCEEDS_FAIL_COUNT.name(), + config.get(CoreOptions.EXCEEDS_FAIL_COUNT)); + graphConfig.addProperty(CoreOptions.RANDOM_TIMEOUT_MILLISECOND.name(), + config.get(CoreOptions.RANDOM_TIMEOUT_MILLISECOND)); + graphConfig.addProperty(CoreOptions.HEARTBEAT_INTERVAL_SECOUND.name(), + config.get(CoreOptions.HEARTBEAT_INTERVAL_SECOUND)); + graphConfig.addProperty(CoreOptions.EXCEEDS_WORKER_COUNT.name(), + config.get(CoreOptions.EXCEEDS_WORKER_COUNT)); + } + /** * Verify if a user is legal * @param username the username for authentication diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java b/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java index 912687bc10..3f8cfe6ebc 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/core/GraphManager.java @@ -369,6 +369,7 @@ private void loadGraph(String name, String graphConfPath) { String raftGroupPeers = this.conf.get(ServerOptions.RAFT_GROUP_PEERS); config.addProperty(ServerOptions.RAFT_GROUP_PEERS.name(), raftGroupPeers); + this.addRoleWorkerConfig(config); Graph graph = GraphFactory.open(config); this.graphs.put(name, graph); @@ -386,6 +387,21 @@ private void loadGraph(String name, String graphConfPath) { } } + private void addRoleWorkerConfig(HugeConfig config) { + config.addProperty(CoreOptions.NODE_EXTERNAL_URL.name(), + this.conf.get(CoreOptions.NODE_EXTERNAL_URL)); + config.addProperty(CoreOptions.BASE_TIMEOUT_MILLISECOND.name(), + this.conf.get(CoreOptions.BASE_TIMEOUT_MILLISECOND)); + config.addProperty(CoreOptions.EXCEEDS_FAIL_COUNT.name(), + this.conf.get(CoreOptions.EXCEEDS_FAIL_COUNT)); + config.addProperty(CoreOptions.RANDOM_TIMEOUT_MILLISECOND.name(), + this.conf.get(CoreOptions.RANDOM_TIMEOUT_MILLISECOND)); + config.addProperty(CoreOptions.HEARTBEAT_INTERVAL_SECOUND.name(), + this.conf.get(CoreOptions.HEARTBEAT_INTERVAL_SECOUND)); + config.addProperty(CoreOptions.EXCEEDS_WORKER_COUNT.name(), + this.conf.get(CoreOptions.EXCEEDS_WORKER_COUNT)); + } + private void waitGraphsReady() { if (!this.rpcServer.enabled()) { LOG.info("RpcServer is not enabled, skip wait graphs ready"); diff --git a/hugegraph-api/src/main/java/org/apache/hugegraph/server/ApplicationConfig.java b/hugegraph-api/src/main/java/org/apache/hugegraph/server/ApplicationConfig.java index 3bbe3048cd..838e4b8105 100644 --- a/hugegraph-api/src/main/java/org/apache/hugegraph/server/ApplicationConfig.java +++ b/hugegraph-api/src/main/java/org/apache/hugegraph/server/ApplicationConfig.java @@ -17,6 +17,7 @@ package org.apache.hugegraph.server; +import org.apache.hugegraph.api.filter.RedirectFilterDynamicFeature; import org.apache.tinkerpop.gremlin.server.util.MetricManager; import org.glassfish.hk2.api.Factory; import org.glassfish.hk2.api.MultiException; @@ -67,6 +68,8 @@ public ApplicationConfig(HugeConfig conf, EventHub hub) { // Register to use the jsr250 annotations @RolesAllowed register(RolesAllowedDynamicFeature.class); + register(RedirectFilterDynamicFeature.class); + // Register HugeConfig to context register(new ConfFactory(conf)); diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java b/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java index 688b5ba8ef..6153c77918 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/StandardHugeGraph.java @@ -288,6 +288,7 @@ public void serverStarted(Id serverId, NodeRole serverRole) { private void initRoleStateWorker(Id serverId) { Config roleStateMachineConfig = new HugeRoleStateMachineConfig(serverId.toString(), + this.configuration.get(CoreOptions.NODE_EXTERNAL_URL), this.configuration.get(CoreOptions.EXCEEDS_FAIL_COUNT), this.configuration.get(CoreOptions.RANDOM_TIMEOUT_MILLISECOND), this.configuration.get(CoreOptions.HEARTBEAT_INTERVAL_SECOUND), diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/config/CoreOptions.java b/hugegraph-core/src/main/java/org/apache/hugegraph/config/CoreOptions.java index 862cabb454..d6cfcf0bc9 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/config/CoreOptions.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/config/CoreOptions.java @@ -635,6 +635,14 @@ public static synchronized CoreOptions instance() { 2 ); + public static final ConfigOption NODE_EXTERNAL_URL = + new ConfigOption<>( + "server.role.node_external_url", + "The url of external accessibility", + disallowEmpty(), + "127.0.0.1:8080" + ); + public static final ConfigOption RANDOM_TIMEOUT_MILLISECOND = new ConfigOption<>( "server.role.random_timeout", diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/Config.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/Config.java index 8afa3084bb..1618c1b193 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/election/Config.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/Config.java @@ -21,6 +21,8 @@ public interface Config { String node(); + String url(); + int exceedsFailCount(); long randomTimeoutMillisecond(); diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/GlobalMasterInfo.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/GlobalMasterInfo.java new file mode 100644 index 0000000000..2ecb1e7439 --- /dev/null +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/GlobalMasterInfo.java @@ -0,0 +1,57 @@ +/* + * 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.hugegraph.election; + +public class GlobalMasterInfo { + + private boolean isMaster; + private String url; + + private volatile boolean featureSupport; + + private static GlobalMasterInfo instance = new GlobalMasterInfo(); + + public static GlobalMasterInfo instance() { + return instance; + } + + private GlobalMasterInfo() { + this.featureSupport = false; + } + + public synchronized void set(boolean isMaster, String url) { + this.isMaster = isMaster; + this.url = url; + } + + public synchronized boolean isMaster() { + return this.isMaster; + } + + public synchronized String url() { + return this.url; + } + + public void isFeatureSupport(boolean featureSupport) { + this.featureSupport = featureSupport; + } + + public boolean isFeatureSupport() { + return this.featureSupport; + } +} diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/HugeRoleStateMachineConfig.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/HugeRoleStateMachineConfig.java index 322c9b7477..d99ce967a4 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/election/HugeRoleStateMachineConfig.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/HugeRoleStateMachineConfig.java @@ -20,16 +20,18 @@ public class HugeRoleStateMachineConfig implements Config { private String node; + private String url; private int exceedsFailCount; private long randomTimeoutMillisecond; private long heartBeatIntervalSecond; private int exceedsWorkerCount; private long baseTimeoutMillisecond; - public HugeRoleStateMachineConfig(String node, int exceedsFailCount, + public HugeRoleStateMachineConfig(String node, String url, int exceedsFailCount, long randomTimeoutMillisecond, long heartBeatIntervalSecond, int exceedsWorkerCount, long baseTimeoutMillisecond) { this.node = node; + this.url = url; this.exceedsFailCount = exceedsFailCount; this.randomTimeoutMillisecond = randomTimeoutMillisecond; this.heartBeatIntervalSecond = heartBeatIntervalSecond; @@ -43,6 +45,11 @@ public String node() { return this.node; } + @Override + public String url() { + return this.url; + } + @Override public int exceedsFailCount() { return this.exceedsFailCount; diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/RoleTypeData.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/RoleTypeData.java index 66f47e7921..a8bb768f6c 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/election/RoleTypeData.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/RoleTypeData.java @@ -25,12 +25,15 @@ public class RoleTypeData { private long clock; private int epoch; - public RoleTypeData(String node, int epoch) { - this(node, epoch, 1); + private String url; + + public RoleTypeData(String node, String url, int epoch) { + this(node, url, epoch, 1); } - public RoleTypeData(String node, int epoch, long clock) { + public RoleTypeData(String node, String url, int epoch, long clock) { this.node = node; + this.url = url; this.epoch = epoch; this.clock = clock; } @@ -59,6 +62,10 @@ public String node() { return this.node; } + public String url() { + return this.url; + } + @Override public boolean equals(Object obj) { if (this == obj) { diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardRoleElectionStateMachine.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardRoleElectionStateMachine.java index bc66d79182..0af2483bd3 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardRoleElectionStateMachine.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardRoleElectionStateMachine.java @@ -120,6 +120,8 @@ public RoleState transform(StateMachineContext context) { } context.epoch(roleTypeData.epoch()); + context.master(new StateMachineContextImpl.MasterServerInfoImpl( + roleTypeData.node(), roleTypeData.url())); if (roleTypeData.isMaster(context.node())) { return new MasterState(roleTypeData); } else { @@ -143,6 +145,7 @@ public AbdicationState(Integer epoch) { @Override public RoleState transform(StateMachineContext context) { + context.master(null); RoleState.heartBeatPark(context); return new UnknownState(this.epoch).transform(context); } @@ -241,10 +244,13 @@ public CandidateState(Integer epoch) { public RoleState transform(StateMachineContext context) { RoleState.randomPark(context); int epoch = this.epoch == null ? 1 : this.epoch; - RoleTypeData roleTypeData = new RoleTypeData(context.config().node(), epoch); + RoleTypeData roleTypeData = new RoleTypeData(context.config().node(), + context.config().url(), epoch); //failover to master success context.epoch(roleTypeData.epoch()); if (context.adapter().updateIfNodePresent(roleTypeData)) { + context.master(new StateMachineContextImpl.MasterServerInfoImpl( + roleTypeData.node(), roleTypeData.url())); return new MasterState(roleTypeData); } else { return new UnknownState(epoch).transform(context); @@ -263,11 +269,18 @@ private static class StateMachineContextImpl implements StateMachineContext { private final String node; private final StandardRoleElectionStateMachine machine; + private MasterServerInfo masterServerInfo; + public StateMachineContextImpl(StandardRoleElectionStateMachine machine) { this.node = machine.config.node(); this.machine = machine; } + @Override + public void master(MasterServerInfo info) { + this.masterServerInfo = info; + } + @Override public Integer epoch() { return this.epoch; @@ -293,6 +306,11 @@ public Config config() { return this.machine.config; } + @Override + public MasterServerInfo master() { + return this.masterServerInfo; + } + @Override public RoleElectionStateMachine stateMachine() { return this.machine; @@ -302,6 +320,27 @@ public RoleElectionStateMachine stateMachine() { public void reset() { this.epoch = null; } + + private static class MasterServerInfoImpl implements MasterServerInfo { + + private final String node; + private final String url; + + public MasterServerInfoImpl(String node, String url) { + this.node = node; + this.url = url; + } + + @Override + public String url() { + return this.url; + } + + @Override + public String node() { + return this.node; + } + } } protected RoleTypeDataAdapter adapter() { diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardRoleTypeDataAdapter.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardRoleTypeDataAdapter.java index 4b917f9f11..7fde15b965 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardRoleTypeDataAdapter.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardRoleTypeDataAdapter.java @@ -88,6 +88,9 @@ private BackendEntry constructEntry(RoleTypeData stateData) { list.add(P.NODE); list.add(stateData.node()); + list.add(P.URL); + list.add(stateData.url()); + list.add(P.CLOCK); list.add(stateData.clock()); @@ -111,10 +114,11 @@ public Optional query() { private RoleTypeData from(Vertex vertex) { String node = (String) vertex.property(P.NODE).value(); + String url = (String) vertex.property(P.URL).value(); Long clock = (Long) vertex.property(P.CLOCK).value(); Integer epoch = (Integer) vertex.property(P.EPOCH).value(); - RoleTypeData roleTypeData = new RoleTypeData(node, epoch, clock); + RoleTypeData roleTypeData = new RoleTypeData(node, url, epoch, clock); return roleTypeData; } @@ -146,6 +150,8 @@ public static final class P { public static final String EPOCH = Graph.Hidden.hide("role_epoch"); + public static final String URL = Graph.Hidden.hide("role_url"); + public static final String TYPE = Graph.Hidden.hide("role_type"); } @@ -177,6 +183,7 @@ private String[] initProperties() { List props = new ArrayList<>(); props.add(createPropertyKey(P.NODE, DataType.TEXT)); + props.add(createPropertyKey(P.URL, DataType.TEXT)); props.add(createPropertyKey(P.CLOCK, DataType.LONG)); props.add(createPropertyKey(P.EPOCH, DataType.INT)); props.add(createPropertyKey(P.TYPE, DataType.TEXT)); diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardStateMachineCallback.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardStateMachineCallback.java index 3db938d00c..a572ffd08c 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardStateMachineCallback.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StandardStateMachineCallback.java @@ -21,6 +21,8 @@ import org.apache.hugegraph.util.Log; import org.slf4j.Logger; +import java.util.Objects; + public class StandardStateMachineCallback implements StateMachineCallback { private static final Logger LOG = Log.logger(StandardStateMachineCallback.class); @@ -32,12 +34,14 @@ public class StandardStateMachineCallback implements StateMachineCallback { public StandardStateMachineCallback(TaskManager taskManager) { this.taskManager = taskManager; this.taskManager.enableRoleElected(true); + GlobalMasterInfo.instance().isFeatureSupport(true); } @Override public void onAsRoleMaster(StateMachineContext context) { if (!isMaster) { this.taskManager.onAsRoleMaster(); + this.initGlobalMasterInfo(context); LOG.info("Server {} change to master role", context.config().node()); } this.isMaster = true; @@ -47,6 +51,7 @@ public void onAsRoleMaster(StateMachineContext context) { public void onAsRoleWorker(StateMachineContext context) { if (isMaster) { this.taskManager.onAsRoleWorker(); + this.initGlobalMasterInfo(context); LOG.info("Server {} change to worker role", context.config().node()); } @@ -57,6 +62,7 @@ public void onAsRoleWorker(StateMachineContext context) { public void onAsRoleCandidate(StateMachineContext context) { if (isMaster) { this.taskManager.onAsRoleWorker(); + this.initGlobalMasterInfo(context); LOG.info("Server {} change to worker role", context.config().node()); } @@ -67,6 +73,7 @@ public void onAsRoleCandidate(StateMachineContext context) { public void unknown(StateMachineContext context) { if (isMaster) { this.taskManager.onAsRoleWorker(); + this.initGlobalMasterInfo(context); LOG.info("Server {} change to worker role", context.config().node()); } @@ -77,6 +84,7 @@ public void unknown(StateMachineContext context) { public void onAsRoleAbdication(StateMachineContext context) { if (isMaster) { this.taskManager.onAsRoleWorker(); + this.initGlobalMasterInfo(context); LOG.info("Server {} change to worker role", context.config().node()); } @@ -87,4 +95,16 @@ public void onAsRoleAbdication(StateMachineContext context) { public void error(StateMachineContext context, Throwable e) { LOG.error("Server {} exception occurred", context.config().node(), e); } + + public void initGlobalMasterInfo(StateMachineContext context) { + StateMachineContext.MasterServerInfo master = context.master(); + if (master == null) { + GlobalMasterInfo.instance().set(false, null); + return; + } + + boolean isMaster = Objects.equals(context.node(), master.node()); + String url = master.url(); + GlobalMasterInfo.instance().set(isMaster, url); + } } diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StateMachineContext.java b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StateMachineContext.java index e314a352c2..cd9ac57f3d 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/election/StateMachineContext.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/election/StateMachineContext.java @@ -29,7 +29,17 @@ public interface StateMachineContext { Config config(); + MasterServerInfo master(); + + void master(MasterServerInfo info); + RoleTypeDataAdapter adapter(); void reset(); + + interface MasterServerInfo { + + String url(); + String node(); + } } diff --git a/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskManager.java b/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskManager.java index a18c9aa4d4..524a1f7593 100644 --- a/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskManager.java +++ b/hugegraph-core/src/main/java/org/apache/hugegraph/task/TaskManager.java @@ -266,6 +266,7 @@ public void onAsRoleMaster() { } } catch (Throwable e) { LOG.error("Exception occurred when change to master role", e); + throw e; } } @@ -278,6 +279,7 @@ public void onAsRoleWorker() { } } catch (Throwable e) { LOG.error("Exception occurred when change to worker role", e); + throw e; } } diff --git a/hugegraph-dist/src/assembly/travis/conf-raft1/rest-server.properties b/hugegraph-dist/src/assembly/travis/conf-raft1/rest-server.properties index ac9806192b..5afc6c9c29 100644 --- a/hugegraph-dist/src/assembly/travis/conf-raft1/rest-server.properties +++ b/hugegraph-dist/src/assembly/travis/conf-raft1/rest-server.properties @@ -16,6 +16,7 @@ # restserver.url=http://127.0.0.1:8080 +server.role.node_external_url=127.0.0.1:8080 gremlinserver.url=http://127.0.0.1:8181 graphs=conf/graphs auth.authenticator=org.apache.hugegraph.auth.StandardAuthenticator diff --git a/hugegraph-dist/src/assembly/travis/conf-raft2/rest-server.properties b/hugegraph-dist/src/assembly/travis/conf-raft2/rest-server.properties index b5ffe91148..305800df51 100644 --- a/hugegraph-dist/src/assembly/travis/conf-raft2/rest-server.properties +++ b/hugegraph-dist/src/assembly/travis/conf-raft2/rest-server.properties @@ -16,6 +16,7 @@ # restserver.url=http://127.0.0.1:8082 +server.role.node_external_url=127.0.0.1:8082 gremlinserver.url=http://127.0.0.1:8182 graphs=conf/graphs auth.authenticator=org.apache.hugegraph.auth.StandardAuthenticator diff --git a/hugegraph-dist/src/assembly/travis/conf-raft3/rest-server.properties b/hugegraph-dist/src/assembly/travis/conf-raft3/rest-server.properties index 89a10f6bf0..d54f1430d7 100644 --- a/hugegraph-dist/src/assembly/travis/conf-raft3/rest-server.properties +++ b/hugegraph-dist/src/assembly/travis/conf-raft3/rest-server.properties @@ -16,6 +16,7 @@ # restserver.url=http://127.0.0.1:8083 +server.role.node_external_url=127.0.0.1:8083 gremlinserver.url=http://127.0.0.1:8183 graphs=conf/graphs auth.authenticator=org.apache.hugegraph.auth.StandardAuthenticator diff --git a/hugegraph-test/src/main/java/org/apache/hugegraph/core/RoleElectionStateMachineTest.java b/hugegraph-test/src/main/java/org/apache/hugegraph/core/RoleElectionStateMachineTest.java index 7366081067..7253d09e83 100644 --- a/hugegraph-test/src/main/java/org/apache/hugegraph/core/RoleElectionStateMachineTest.java +++ b/hugegraph-test/src/main/java/org/apache/hugegraph/core/RoleElectionStateMachineTest.java @@ -106,6 +106,11 @@ public String node() { return this.node; } + @Override + public String url() { + return "http://127.0.0.1:8080"; + } + @Override public int exceedsFailCount() { return 2; @@ -211,7 +216,8 @@ RoleTypeData copy(RoleTypeData stateData) { if (stateData == null) { return null; } - return new RoleTypeData(stateData.node(), stateData.epoch(), stateData.clock()); + return new RoleTypeData(stateData.node(), stateData.url(), + stateData.epoch(), stateData.clock()); } @Override