This is an automated email from the ASF dual-hosted git repository.
liubao pushed a commit to branch 2.8.x
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git
The following commit(s) were added to refs/heads/2.8.x by this push:
new 71b0003aa [SCB-2807]add new instance isolation feature based on
instance-isolation-consumer (#3932)
71b0003aa is described below
commit 71b0003aafef905daa60d3077f21591ea315ece8
Author: liubao68 <[email protected]>
AuthorDate: Thu Aug 31 18:14:19 2023 +0800
[SCB-2807]add new instance isolation feature based on
instance-isolation-consumer (#3932)
---
.../ConsumerInstanceIsolationHandler.java | 27 +++--
.../handler/governance/InstanceIsolatedEvent.java | 38 +++++++
.../InstanceIsolationDiscoveryFilter.java | 120 +++++++++++++++++++++
....servicecomb.registry.discovery.DiscoveryFilter | 1 +
.../loadbalance/event/IsolationServerEvent.java | 4 +-
...lter.java => IsolationServerListFilterExt.java} | 6 +-
...che.servicecomb.loadbalance.ServerListFilterExt | 2 +-
....java => IsolationServerListFilterExtTest.java} | 8 +-
8 files changed, 185 insertions(+), 21 deletions(-)
diff --git
a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationHandler.java
b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationHandler.java
index c15a9dd0b..2a1d2ea3f 100644
---
a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationHandler.java
+++
b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/ConsumerInstanceIsolationHandler.java
@@ -17,19 +17,21 @@
package org.apache.servicecomb.handler.governance;
+import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Supplier;
+import javax.ws.rs.core.Response.Status;
+
import org.apache.servicecomb.core.Handler;
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.governance.MatchType;
+import org.apache.servicecomb.foundation.common.event.EventManager;
import org.apache.servicecomb.foundation.common.utils.BeanUtils;
import org.apache.servicecomb.governance.handler.InstanceIsolationHandler;
import org.apache.servicecomb.governance.marker.GovernanceRequestExtractor;
-import org.apache.servicecomb.registry.api.MicroserviceKey;
-import
org.apache.servicecomb.registry.api.event.MicroserviceInstanceChangedEvent;
-import org.apache.servicecomb.registry.api.event.ServiceCenterEventBus;
+import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy;
import org.apache.servicecomb.swagger.invocation.AsyncResponse;
import org.apache.servicecomb.swagger.invocation.Response;
import org.apache.servicecomb.swagger.invocation.exception.CommonExceptionData;
@@ -57,6 +59,12 @@ public class ConsumerInstanceIsolationHandler implements
Handler {
DecorateCompletionStage<Response> dcs = Decorators.ofCompletionStage(next);
GovernanceRequestExtractor request =
MatchType.createGovHttpRequest(invocation);
+ CircuitBreakerPolicy circuitBreakerPolicy =
instanceIsolationHandler.matchPolicy(request);
+ if (circuitBreakerPolicy != null && circuitBreakerPolicy.isForceOpen()) {
+ asyncResp.consumerFail(new
InvocationException(Status.SERVICE_UNAVAILABLE,
+ "Policy " + circuitBreakerPolicy.getName() + " forced open and deny
requests"));
+ return;
+ }
addCircuitBreaker(dcs, request);
dcs.get().whenComplete((r, e) -> {
@@ -67,7 +75,7 @@ public class ConsumerInstanceIsolationHandler implements
Handler {
if (e instanceof CallNotPermittedException) {
LOGGER.warn("instance isolation circuitBreaker is open by policy :
{}", e.getMessage());
-
ServiceCenterEventBus.getEventBus().post(createMicroserviceInstanceChangedEvent(invocation));
+ EventManager.post(createInstanceIsolatedEvent(circuitBreakerPolicy,
request));
// return 503 so that consumer can retry
asyncResp.complete(
Response.failResp(new InvocationException(503, "instance isolation
circuitBreaker is open.",
@@ -78,13 +86,10 @@ public class ConsumerInstanceIsolationHandler implements
Handler {
});
}
- private Object createMicroserviceInstanceChangedEvent(Invocation invocation)
{
- MicroserviceInstanceChangedEvent event = new
MicroserviceInstanceChangedEvent();
- MicroserviceKey key = new MicroserviceKey();
- key.setAppId(invocation.getAppId());
- key.setServiceName(invocation.getMicroserviceName());
- event.setKey(key);
- return event;
+ private Object createInstanceIsolatedEvent(CircuitBreakerPolicy
circuitBreakerPolicy,
+ GovernanceRequestExtractor requestExtractor) {
+ return new InstanceIsolatedEvent(requestExtractor.instanceId(),
+ Duration.parse(circuitBreakerPolicy.getWaitDurationInOpenState()));
}
private void addCircuitBreaker(DecorateCompletionStage<Response> dcs,
GovernanceRequestExtractor request) {
diff --git
a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/InstanceIsolatedEvent.java
b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/InstanceIsolatedEvent.java
new file mode 100644
index 000000000..32be69820
--- /dev/null
+++
b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/InstanceIsolatedEvent.java
@@ -0,0 +1,38 @@
+/*
+ * 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.servicecomb.handler.governance;
+
+import java.time.Duration;
+
+public class InstanceIsolatedEvent {
+ private final String instanceId;
+
+ private final Duration waitDurationInHalfOpenState;
+
+ public InstanceIsolatedEvent(String instanceId, Duration
waitDurationInHalfOpenState) {
+ this.instanceId = instanceId;
+ this.waitDurationInHalfOpenState = waitDurationInHalfOpenState;
+ }
+
+ public String getInstanceId() {
+ return instanceId;
+ }
+
+ public Duration getWaitDurationInHalfOpenState() {
+ return waitDurationInHalfOpenState;
+ }
+}
diff --git
a/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/InstanceIsolationDiscoveryFilter.java
b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/InstanceIsolationDiscoveryFilter.java
new file mode 100644
index 000000000..1923481d6
--- /dev/null
+++
b/handlers/handler-governance/src/main/java/org/apache/servicecomb/handler/governance/InstanceIsolationDiscoveryFilter.java
@@ -0,0 +1,120 @@
+/*
+ * 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.servicecomb.handler.governance;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.apache.servicecomb.foundation.common.event.EventManager;
+import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
+import org.apache.servicecomb.registry.discovery.DiscoveryContext;
+import org.apache.servicecomb.registry.discovery.DiscoveryFilter;
+import org.apache.servicecomb.registry.discovery.DiscoveryTreeNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.eventbus.Subscribe;
+import com.netflix.config.DynamicPropertyFactory;
+
+public class InstanceIsolationDiscoveryFilter implements DiscoveryFilter {
+ private static final Logger LOGGER =
LoggerFactory.getLogger(InstanceIsolationDiscoveryFilter.class);
+
+ private static final String KEY_ISOLATED = "isolated";
+
+ private final Object lock = new Object();
+
+ private final Map<String, Long> isolatedInstances = new
ConcurrentHashMapEx<>();
+
+ public InstanceIsolationDiscoveryFilter() {
+ EventManager.register(this);
+ }
+
+ @Subscribe
+ public void onInstanceIsolatedEvent(InstanceIsolatedEvent event) {
+ synchronized (lock) {
+ for (Iterator<String> iterator = isolatedInstances.keySet().iterator();
iterator.hasNext(); ) {
+ Long duration = isolatedInstances.get(iterator.next());
+ if (System.currentTimeMillis() - duration > 0) {
+ iterator.remove();
+ }
+ }
+
+ isolatedInstances.put(event.getInstanceId(),
+ System.currentTimeMillis() +
event.getWaitDurationInHalfOpenState().toMillis());
+ LOGGER.info("isolate instance {} for {}ms", event.getInstanceId(),
+ event.getWaitDurationInHalfOpenState().toMillis());
+ }
+ }
+
+ @Override
+ public boolean enabled() {
+ return DynamicPropertyFactory.getInstance().getBooleanProperty(
+ "servicecomb.loadbalance.filter.instance.isolation.enabled",
true).get();
+ }
+
+ @Override
+ public int getOrder() {
+ return Short.MAX_VALUE - 1;
+ }
+
+ @Override
+ public boolean isGroupingFilter() {
+ return true;
+ }
+
+ @Override
+ public DiscoveryTreeNode discovery(DiscoveryContext context,
DiscoveryTreeNode parent) {
+ Map<String, MicroserviceInstance> instances = parent.data();
+ if (isolatedInstances.isEmpty() || instances.isEmpty()) {
+ return parent;
+ }
+
+ boolean changed = false;
+ Map<String, MicroserviceInstance> result = new HashMap<>(instances.size());
+ for (Entry<String, MicroserviceInstance> item : instances.entrySet()) {
+ Long duration = isolatedInstances.get(item.getKey());
+ if (duration == null) {
+ result.put(item.getKey(), item.getValue());
+ continue;
+ }
+
+ if (System.currentTimeMillis() - duration < 0) {
+ changed = true;
+ continue;
+ }
+
+ synchronized (lock) {
+ isolatedInstances.remove(item.getKey());
+ LOGGER.info("try to recover instance {}", item.getKey());
+ }
+ result.put(item.getKey(), item.getValue());
+ }
+
+ if (!changed || result.size() == 0) {
+ return parent;
+ }
+
+ // Create new child. And all later DiscoveryFilter will re-calculate based
on this result.
+ DiscoveryTreeNode child = new DiscoveryTreeNode().subName(parent,
KEY_ISOLATED).data(result);
+ parent.child(KEY_ISOLATED, child);
+ return child;
+ }
+}
diff --git
a/handlers/handler-governance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter
b/handlers/handler-governance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter
index 622297217..447d294c0 100644
---
a/handlers/handler-governance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter
+++
b/handlers/handler-governance/src/main/resources/META-INF/services/org.apache.servicecomb.registry.discovery.DiscoveryFilter
@@ -15,3 +15,4 @@
# limitations under the License.
#
+org.apache.servicecomb.handler.governance.InstanceIsolationDiscoveryFilter
diff --git
a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/event/IsolationServerEvent.java
b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/event/IsolationServerEvent.java
index 50a51d692..fa027e10f 100644
---
a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/event/IsolationServerEvent.java
+++
b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/event/IsolationServerEvent.java
@@ -20,7 +20,7 @@ import org.apache.servicecomb.core.Endpoint;
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.foundation.common.event.AlarmEvent;
import org.apache.servicecomb.loadbalance.ServiceCombServerStats;
-import org.apache.servicecomb.loadbalance.filterext.IsolationDiscoveryFilter;
+import
org.apache.servicecomb.loadbalance.filterext.IsolationServerListFilterExt;
import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
public class IsolationServerEvent extends AlarmEvent {
@@ -52,7 +52,7 @@ public class IsolationServerEvent extends AlarmEvent {
public IsolationServerEvent(Invocation invocation, MicroserviceInstance
instance,
ServiceCombServerStats serverStats,
- IsolationDiscoveryFilter.Settings settings, Type type, Endpoint
endpoint) {
+ IsolationServerListFilterExt.Settings settings, Type type, Endpoint
endpoint) {
super(type);
this.microserviceName = invocation.getMicroserviceName();
this.endpoint = endpoint;
diff --git
a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/IsolationDiscoveryFilter.java
b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/IsolationServerListFilterExt.java
similarity index 97%
rename from
handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/IsolationDiscoveryFilter.java
rename to
handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/IsolationServerListFilterExt.java
index bbbda45e4..78985aa2e 100644
---
a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/IsolationDiscoveryFilter.java
+++
b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/IsolationServerListFilterExt.java
@@ -39,9 +39,9 @@ import com.netflix.config.DynamicPropertyFactory;
/**
* Isolate instances by error metrics
*/
-public class IsolationDiscoveryFilter implements ServerListFilterExt {
+public class IsolationServerListFilterExt implements ServerListFilterExt {
- private static final Logger LOGGER =
LoggerFactory.getLogger(IsolationDiscoveryFilter.class);
+ private static final Logger LOGGER =
LoggerFactory.getLogger(IsolationServerListFilterExt.class);
private final DynamicBooleanProperty emptyProtection =
DynamicPropertyFactory.getInstance()
.getBooleanProperty(EMPTY_INSTANCE_PROTECTION, false);
@@ -65,7 +65,7 @@ public class IsolationDiscoveryFilter implements
ServerListFilterExt {
return ORDER_ISOLATION;
}
- public IsolationDiscoveryFilter() {
+ public IsolationServerListFilterExt() {
emptyProtection.addCallback(() -> {
boolean newValue = emptyProtection.get();
LOGGER.info("{} changed from {} to {}", EMPTY_INSTANCE_PROTECTION,
emptyProtection, newValue);
diff --git
a/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt
b/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt
index 9e8a08479..a355341ff 100644
---
a/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt
+++
b/handlers/handler-loadbalance/src/main/resources/META-INF/services/org.apache.servicecomb.loadbalance.ServerListFilterExt
@@ -15,5 +15,5 @@
# limitations under the License.
#
-org.apache.servicecomb.loadbalance.filterext.IsolationDiscoveryFilter
+org.apache.servicecomb.loadbalance.filterext.IsolationServerListFilterExt
org.apache.servicecomb.loadbalance.filterext.ZoneAwareDiscoveryFilter
\ No newline at end of file
diff --git
a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/IsolationDiscoveryFilterTest.java
b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/IsolationServerListFilterExtTest.java
similarity index 97%
rename from
handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/IsolationDiscoveryFilterTest.java
rename to
handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/IsolationServerListFilterExtTest.java
index 78b4d0cfd..03a701377 100644
---
a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/IsolationDiscoveryFilterTest.java
+++
b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filter/IsolationServerListFilterExtTest.java
@@ -28,7 +28,7 @@ import
org.apache.servicecomb.loadbalance.ServiceCombLoadBalancerStats;
import org.apache.servicecomb.loadbalance.ServiceCombServer;
import org.apache.servicecomb.loadbalance.ServiceCombServerStats;
import org.apache.servicecomb.loadbalance.TestServiceCombServerStats;
-import org.apache.servicecomb.loadbalance.filterext.IsolationDiscoveryFilter;
+import
org.apache.servicecomb.loadbalance.filterext.IsolationServerListFilterExt;
import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
import org.apache.servicecomb.registry.cache.CacheEndpoint;
import org.junit.jupiter.api.AfterEach;
@@ -42,9 +42,9 @@ import mockit.Deencapsulation;
import mockit.Mocked;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class IsolationDiscoveryFilterTest {
+public class IsolationServerListFilterExtTest {
- private IsolationDiscoveryFilter filter;
+ private IsolationServerListFilterExt filter;
private List<ServiceCombServer> servers;
@@ -73,7 +73,7 @@ public class IsolationDiscoveryFilterTest {
ServiceCombLoadBalancerStats.INSTANCE.getServiceCombServerStats(serviceCombServer);
}
- filter = new IsolationDiscoveryFilter();
+ filter = new IsolationServerListFilterExt();
TestServiceCombServerStats.releaseTryingChance();
}