[SCB-2330] add injectFault into governance (#3140)

diff --git a/governance/src/main/java/org/apache/servicecomb/governance/GovernanceConfiguration.java b/governance/src/main/java/org/apache/servicecomb/governance/GovernanceConfiguration.java
index 9679999..03eb521 100644
--- a/governance/src/main/java/org/apache/servicecomb/governance/GovernanceConfiguration.java
+++ b/governance/src/main/java/org/apache/servicecomb/governance/GovernanceConfiguration.java
@@ -21,6 +21,7 @@
 
 import org.apache.servicecomb.governance.handler.BulkheadHandler;
 import org.apache.servicecomb.governance.handler.CircuitBreakerHandler;
+import org.apache.servicecomb.governance.handler.FaultInjectionHandler;
 import org.apache.servicecomb.governance.handler.InstanceIsolationHandler;
 import org.apache.servicecomb.governance.handler.RateLimitingHandler;
 import org.apache.servicecomb.governance.handler.RetryHandler;
@@ -36,6 +37,7 @@
 import org.apache.servicecomb.governance.marker.operator.SuffixOperator;
 import org.apache.servicecomb.governance.properties.BulkheadProperties;
 import org.apache.servicecomb.governance.properties.CircuitBreakerProperties;
+import org.apache.servicecomb.governance.properties.FaultInjectionProperties;
 import org.apache.servicecomb.governance.properties.InstanceIsolationProperties;
 import org.apache.servicecomb.governance.properties.MatchProperties;
 import org.apache.servicecomb.governance.properties.RateLimitProperties;
@@ -81,6 +83,11 @@
     return new RetryProperties();
   }
 
+  @Bean
+  public FaultInjectionProperties faultInjectionProperties() {
+    return new FaultInjectionProperties();
+  }
+
   // handlers configuration
   @Bean
   public BulkheadHandler bulkheadHandler(BulkheadProperties bulkheadProperties) {
@@ -111,6 +118,11 @@
     return new RetryHandler(retryProperties, retryExtension);
   }
 
+  @Bean
+  public FaultInjectionHandler faultInjectionHandler(FaultInjectionProperties faultInjectionProperties) {
+    return new FaultInjectionHandler(faultInjectionProperties);
+  }
+
   // request processor
   @Bean
   public RequestProcessor requestProcessor(Map<String, MatchOperator> operatorMap) {
diff --git a/governance/src/main/java/org/apache/servicecomb/governance/handler/FaultInjectionHandler.java b/governance/src/main/java/org/apache/servicecomb/governance/handler/FaultInjectionHandler.java
new file mode 100644
index 0000000..5f5e024
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/governance/handler/FaultInjectionHandler.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.governance.handler;
+
+import org.apache.servicecomb.governance.marker.GovernanceRequest;
+import org.apache.servicecomb.governance.policy.FaultInjectionPolicy;
+import org.apache.servicecomb.governance.properties.FaultInjectionProperties;
+import org.apache.servicecomb.injection.Fault;
+import org.apache.servicecomb.injection.FaultInjectionUtil;
+
+public class FaultInjectionHandler extends AbstractGovernanceHandler<Fault, FaultInjectionPolicy> {
+
+  private final FaultInjectionProperties faultInjectionProperties;
+
+  public FaultInjectionHandler(FaultInjectionProperties faultInjectionProperties) {
+    this.faultInjectionProperties = faultInjectionProperties;
+  }
+
+  @Override
+  protected String createKey(GovernanceRequest governanceRequest, FaultInjectionPolicy policy) {
+    return FaultInjectionProperties.MATCH_FAULT_INJECTION_KEY + "." + policy.getName();
+  }
+
+  @Override
+  public FaultInjectionPolicy matchPolicy(GovernanceRequest governanceRequest) {
+    return matchersManager.match(governanceRequest, faultInjectionProperties.getParsedEntity());
+  }
+
+  @Override
+  protected Fault createProcessor(String key, GovernanceRequest governanceRequest, FaultInjectionPolicy policy) {
+    return FaultInjectionUtil.getFault(key, policy);
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/governance/policy/FaultInjectionPolicy.java b/governance/src/main/java/org/apache/servicecomb/governance/policy/FaultInjectionPolicy.java
new file mode 100644
index 0000000..07b853a
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/governance/policy/FaultInjectionPolicy.java
@@ -0,0 +1,93 @@
+/*
+ * 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.governance.policy;
+
+import java.time.Duration;
+
+import org.apache.servicecomb.injection.FaultInjectionConst;
+
+public class FaultInjectionPolicy extends AbstractPolicy {
+  public static final Duration DEFAULT_TIMEOUT_DURATION = Duration.ofMillis(0);
+
+  private String type = FaultInjectionConst.TYPE_DELAY;
+
+  private String delayTime = DEFAULT_TIMEOUT_DURATION.toString();
+
+  private int percentage = -1;
+
+  private int errorCode = -1;
+
+  public String getType() {
+    return type;
+  }
+
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  public String getDelayTime() {
+    return delayTime;
+  }
+
+  public void setDelayTime(String delayTime) {
+    this.delayTime = stringOfDuration(delayTime, Duration.ofMillis(-1));
+  }
+
+  public int getPercentage() {
+    return percentage;
+  }
+
+  public void setPercentage(int percentage) {
+    this.percentage = percentage;
+  }
+
+  public int getErrorCode() {
+    return errorCode;
+  }
+
+  public void setErrorCode(int errorCode) {
+    this.errorCode = errorCode;
+  }
+
+  public long getDelayTimeToMillis() {
+    return Duration.parse(delayTime).toMillis();
+  }
+
+  @Override
+  public boolean isValid() {
+    if (getDelayTimeToMillis() < 0 && FaultInjectionConst.TYPE_DELAY.equals(type)) {
+      return false;
+    }
+    if ((getErrorCode() < FaultInjectionConst.ERROR_CODE_MIN
+        || getErrorCode() > FaultInjectionConst.ERROR_CODE_MAX)
+        && FaultInjectionConst.TYPE_ABORT.equals(type)) {
+      return false;
+    }
+    return super.isValid();
+  }
+
+  @Override
+  public String toString() {
+    return "FaultInjectionPolicy{" +
+        "type=" + type +
+        ", delayTime=" + delayTime +
+        ", percentage=" + percentage +
+        ", errorCode=" + errorCode +
+        '}';
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/governance/properties/FaultInjectionProperties.java b/governance/src/main/java/org/apache/servicecomb/governance/properties/FaultInjectionProperties.java
new file mode 100644
index 0000000..3fe8af4
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/governance/properties/FaultInjectionProperties.java
@@ -0,0 +1,33 @@
+/*
+ * 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.governance.properties;
+
+import org.apache.servicecomb.governance.policy.FaultInjectionPolicy;
+
+public class FaultInjectionProperties extends PolicyProperties<FaultInjectionPolicy> {
+  public static final String MATCH_FAULT_INJECTION_KEY = "servicecomb.faultInjection";
+
+  public FaultInjectionProperties() {
+    super(MATCH_FAULT_INJECTION_KEY);
+  }
+
+  @Override
+  public Class<FaultInjectionPolicy> getEntityClass() {
+    return FaultInjectionPolicy.class;
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/AbortFault.java b/governance/src/main/java/org/apache/servicecomb/injection/AbortFault.java
new file mode 100644
index 0000000..dceebee
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/AbortFault.java
@@ -0,0 +1,71 @@
+/*
+ * 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.injection;
+
+import org.apache.servicecomb.governance.policy.FaultInjectionPolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AbortFault extends AbstractFault {
+
+  private static final Logger LOGGER = LoggerFactory.getLogger(AbortFault.class);
+
+  public static final String ABORTED_ERROR_MSG = "aborted by fault inject";
+
+  public AbortFault(String key, FaultInjectionPolicy policy) {
+    super(key, policy);
+  }
+
+  @Override
+  public void injectFault(FaultParam faultParam) {
+    if (!shouldAbort(faultParam, policy)) {
+      return;
+    }
+
+    // get the config values related to abort percentage.
+    int errorCode = policy.getErrorCode();
+    if (errorCode == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) {
+      LOGGER.debug("Fault injection: Abort error code is not configured");
+      return;
+    }
+
+    throw new FaultInjectionException(FaultResponse.createFail(errorCode, ABORTED_ERROR_MSG));
+  }
+
+  private boolean shouldAbort(FaultParam param, FaultInjectionPolicy policy) {
+    // get the config values related to abort.
+    int abortPercent = policy.getPercentage();
+    if (abortPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) {
+      LOGGER.debug("Fault injection: Abort percentage is not configured");
+      return false;
+    }
+
+    // check fault abort condition.
+    return FaultInjectionUtil.isFaultNeedToInject(param.getReqCount(), abortPercent);
+  }
+
+  @Override
+  public int getOrder() {
+    return 200;
+  }
+
+  @Override
+  public String getName() {
+    return FaultInjectionConst.TYPE_ABORT;
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/AbstractFault.java b/governance/src/main/java/org/apache/servicecomb/injection/AbstractFault.java
new file mode 100644
index 0000000..9fd1b9f
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/AbstractFault.java
@@ -0,0 +1,42 @@
+/*
+ * 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.injection;
+
+import org.apache.servicecomb.governance.policy.FaultInjectionPolicy;
+
+public abstract class AbstractFault implements Fault {
+  protected String key;
+
+  protected FaultInjectionPolicy policy;
+
+  public AbstractFault(String key, FaultInjectionPolicy policy) {
+    this.key = key;
+    this.policy = policy;
+  }
+
+  @Override
+  public void injectFault() {
+    FaultParam faultParam = FaultInjectionUtil.initFaultParam(key);
+    injectFault(faultParam);
+  }
+
+  @Override
+  public String getKey() {
+    return key;
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/DelayFault.java b/governance/src/main/java/org/apache/servicecomb/injection/DelayFault.java
new file mode 100644
index 0000000..d2d9d8f
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/DelayFault.java
@@ -0,0 +1,73 @@
+/*
+ * 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.injection;
+
+import org.apache.servicecomb.governance.policy.FaultInjectionPolicy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DelayFault extends AbstractFault {
+  private static final Logger LOGGER = LoggerFactory.getLogger(DelayFault.class);
+
+  public DelayFault(String key, FaultInjectionPolicy policy) {
+    super(key, policy);
+  }
+
+  @Override
+  public int getOrder() {
+    return 100;
+  }
+
+  @Override
+  public void injectFault(FaultParam faultParam) {
+    if (!shouldDelay(faultParam, policy)) {
+      return;
+    }
+
+    LOGGER.debug("Fault injection: delay is added for the request by fault inject handler");
+    long delay = policy.getDelayTimeToMillis();
+    if (delay == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) {
+      LOGGER.debug("Fault injection: delay is not configured");
+      return;
+    }
+
+    executeDelay(faultParam, delay);
+  }
+
+  private void executeDelay(FaultParam faultParam, long delay) {
+    Sleepable sleepable = faultParam.getSleepable();
+    if (sleepable != null) {
+      sleepable.sleep(delay);
+    }
+  }
+
+  private boolean shouldDelay(FaultParam param, FaultInjectionPolicy policy) {
+    int delayPercent = policy.getPercentage();
+    if (delayPercent == FaultInjectionConst.FAULT_INJECTION_DEFAULT_VALUE) {
+      LOGGER.debug("Fault injection: delay percentage is not configured");
+      return false;
+    }
+    // check fault delay condition.
+    return FaultInjectionUtil.isFaultNeedToInject(param.getReqCount(), delayPercent);
+  }
+
+  @Override
+  public String getName() {
+    return FaultInjectionConst.TYPE_DELAY;
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/Fault.java b/governance/src/main/java/org/apache/servicecomb/injection/Fault.java
new file mode 100644
index 0000000..84de7d8
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/Fault.java
@@ -0,0 +1,40 @@
+/*
+ * 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.injection;
+
+import io.vavr.CheckedFunction0;
+
+public interface Fault {
+  static <T> CheckedFunction0<T> decorateCheckedSupplier(Fault fault, CheckedFunction0<T> supplier) {
+    return () -> {
+      fault.injectFault();
+      return supplier.apply();
+    };
+  }
+
+  int getOrder();
+
+  String getName();
+
+  void injectFault();
+
+  void injectFault(FaultParam faultParam);
+
+  String getKey();
+
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionConst.java b/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionConst.java
new file mode 100644
index 0000000..bf50fc5
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionConst.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.servicecomb.injection;
+
+/**
+ * Handles the all constant values for fault injection.
+ */
+public class FaultInjectionConst {
+
+  public static final int FAULT_INJECTION_DEFAULT_VALUE = -1;
+
+  public static final String TYPE_DELAY = "delay";
+
+  public static final String TYPE_ABORT = "abort";
+
+  public static final int ERROR_CODE_MIN = 200;
+
+  public static final int ERROR_CODE_MAX = 600;
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionDecorators.java b/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionDecorators.java
new file mode 100644
index 0000000..c499f3e
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionDecorators.java
@@ -0,0 +1,45 @@
+/*
+ * 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.injection;
+
+import io.vavr.CheckedFunction0;
+
+public interface FaultInjectionDecorators {
+  static <T> FaultInjectionDecorateCheckedSupplier<T> ofCheckedSupplier(CheckedFunction0<T> supplier) {
+    return new FaultInjectionDecorateCheckedSupplier<>(supplier);
+  }
+
+  class FaultInjectionDecorateCheckedSupplier<T> {
+
+    private CheckedFunction0<T> supplier;
+
+    protected FaultInjectionDecorateCheckedSupplier(CheckedFunction0<T> supplier) {
+      this.supplier = supplier;
+    }
+
+    public FaultInjectionDecorateCheckedSupplier<T> withFaultInjection(Fault fault) {
+      supplier = Fault.decorateCheckedSupplier(fault, supplier);
+      return this;
+    }
+
+    public T get() throws Throwable {
+      return supplier.apply();
+    }
+  }
+
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionException.java b/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionException.java
new file mode 100644
index 0000000..c061c90
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionException.java
@@ -0,0 +1,33 @@
+/*
+ * 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.injection;
+
+public class FaultInjectionException extends RuntimeException {
+  private static final long serialVersionUID = 1675558351029273343L;
+
+  private final FaultResponse faultResponse;
+
+  public FaultInjectionException(FaultResponse faultResponse) {
+    super(faultResponse.getErrorMsg());
+    this.faultResponse = faultResponse;
+  }
+
+  public FaultResponse getFaultResponse() {
+    return faultResponse;
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionUtil.java b/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionUtil.java
new file mode 100644
index 0000000..d95c10f
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/FaultInjectionUtil.java
@@ -0,0 +1,104 @@
+/*
+ * 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.injection;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.servicecomb.foundation.common.concurrent.ConcurrentHashMapEx;
+import org.apache.servicecomb.governance.policy.FaultInjectionPolicy;
+
+import io.vertx.core.Context;
+import io.vertx.core.Vertx;
+
+/**
+ * Handles the count for all request based key[transport + microservice qualified name].
+ */
+public final class FaultInjectionUtil {
+
+  private FaultInjectionUtil() {
+  }
+
+  /**
+   * key is transport+operQualifiedName
+   */
+  private static final Map<String, AtomicLong> REQUEST_COUNT = new ConcurrentHashMapEx<>();
+
+  /**
+   * Returns total requests per provider for operational level.
+   *
+   * @param key
+   *            transport+operational name
+   * @return long total requests
+   */
+  public static AtomicLong getOperMetTotalReq(String key) {
+    return REQUEST_COUNT.computeIfAbsent(key, p -> new AtomicLong(1));
+  }
+
+  /**
+   * It will check the delay/abort condition based on request count and percentage
+   * received.
+   *
+   * @param reqCount total request count of the uri
+   * @param percentage the percentage of hitting fault injection
+   * @return true: delay/abort is needed. false: delay/abort is not needed.
+   */
+  public static boolean isFaultNeedToInject(long reqCount, int percentage) {
+    /*
+     * Example: delay/abort percentage configured is 10% and Get the count(suppose
+     * if it is 10th request) from map and calculate resultNew(10th request) and
+     * requestOld(9th request). Like this for every request it will calculate
+     * current request count and previous count. if both not matched need to add
+     * delay/abort otherwise no need to add.
+     */
+
+    // calculate the value with current request count.
+    long resultNew = (reqCount * percentage) / 100;
+
+    // calculate the value with previous count value.
+    long resultOld = ((reqCount - 1) * percentage) / 100;
+
+    // if both are not matching then delay/abort should be added.
+    return (resultNew != resultOld);
+  }
+
+  public static Fault getFault(String key, FaultInjectionPolicy policy) {
+    Fault fault = null;
+    if (FaultInjectionConst.TYPE_DELAY.equals(policy.getType())) {
+      fault = new DelayFault(key, policy);
+    } else if (FaultInjectionConst.TYPE_ABORT.equals(policy.getType())) {
+      fault = new AbortFault(key, policy);
+    }
+    return fault;
+  }
+
+  public static FaultParam initFaultParam(String key) {
+    AtomicLong reqCount = FaultInjectionUtil.getOperMetTotalReq(key);
+    // increment the request count here after checking the delay/abort condition.
+    long reqCountCurrent = reqCount.getAndIncrement();
+
+    FaultParam param = new FaultParam(reqCountCurrent);
+    Context currentContext = Vertx.currentContext();
+    if (currentContext != null && currentContext.owner() != null && currentContext.isEventLoopContext()) {
+      param.setSleepable(
+          (delay) -> currentContext.owner().setTimer(delay, timeId -> {}));
+    }
+    return param;
+  }
+
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/FaultParam.java b/governance/src/main/java/org/apache/servicecomb/injection/FaultParam.java
new file mode 100644
index 0000000..3e53a68
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/FaultParam.java
@@ -0,0 +1,54 @@
+/*
+ * 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.injection;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Fault injection parameters which decides the fault injection condition.
+ */
+public class FaultParam {
+  private static final Logger LOGGER = LoggerFactory.getLogger(FaultParam.class);
+
+  private final long reqCount;
+
+  private Sleepable sleepable = (delay) -> {
+    try {
+      Thread.sleep(delay);
+    } catch (InterruptedException e) {
+      LOGGER.info("Interrupted exception is received");
+    }
+  };
+
+  public long getReqCount() {
+    return reqCount;
+  }
+
+  public FaultParam(long reqCount) {
+    this.reqCount = reqCount;
+  }
+
+  public Sleepable getSleepable() {
+    return sleepable;
+  }
+
+  public void setSleepable(Sleepable sleepable) {
+    this.sleepable = sleepable;
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/FaultResponse.java b/governance/src/main/java/org/apache/servicecomb/injection/FaultResponse.java
new file mode 100644
index 0000000..603451c
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/FaultResponse.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.servicecomb.injection;
+
+public class FaultResponse {
+
+  private int errorCode;
+
+  private String errorMsg;
+
+  public static FaultResponse createFail(int errorCode, String errorMsg) {
+    FaultResponse faultResponse = new FaultResponse();
+    faultResponse.setErrorCode(errorCode);
+    faultResponse.setErrorMsg(errorMsg);
+    return faultResponse;
+  }
+
+  public int getErrorCode() {
+    return errorCode;
+  }
+
+  public void setErrorCode(int errorCode) {
+    this.errorCode = errorCode;
+  }
+
+  public String getErrorMsg() {
+    return errorMsg;
+  }
+
+  public void setErrorMsg(String errorMsg) {
+    this.errorMsg = errorMsg;
+  }
+}
diff --git a/governance/src/main/java/org/apache/servicecomb/injection/Sleepable.java b/governance/src/main/java/org/apache/servicecomb/injection/Sleepable.java
new file mode 100644
index 0000000..46b9873
--- /dev/null
+++ b/governance/src/main/java/org/apache/servicecomb/injection/Sleepable.java
@@ -0,0 +1,26 @@
+/*
+ * 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.injection;
+
+public interface Sleepable {
+  /**
+   * sleep some time
+   * @param delay time unit is millisecond
+   */
+  void sleep(long delay);
+}
diff --git a/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java b/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java
new file mode 100644
index 0000000..148b0fb
--- /dev/null
+++ b/governance/src/test/java/org/apache/servicecomb/governance/FaultInjectionTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.governance;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.servicecomb.governance.handler.FaultInjectionHandler;
+import org.apache.servicecomb.governance.marker.GovernanceRequest;
+import org.apache.servicecomb.injection.Fault;
+import org.apache.servicecomb.injection.FaultInjectionDecorators;
+import org.apache.servicecomb.injection.FaultInjectionDecorators.FaultInjectionDecorateCheckedSupplier;
+import org.apache.servicecomb.injection.FaultInjectionException;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.ContextConfiguration;
+
+@SpringBootTest
+@ContextConfiguration(classes = {GovernanceConfiguration.class, MockConfiguration.class})
+public class FaultInjectionTest {
+  private FaultInjectionHandler faultInjectionHandler;
+
+  @Autowired
+  public void setFaultInjectionHandler(FaultInjectionHandler faultInjectionHandler) {
+    this.faultInjectionHandler = faultInjectionHandler;
+  }
+
+  public FaultInjectionTest() {
+  }
+
+  @Test
+  public void test_delay_fault_injection_service_name_work() throws Throwable {
+    FaultInjectionDecorateCheckedSupplier<Object> ds =
+        FaultInjectionDecorators.ofCheckedSupplier(() -> "test");
+
+    GovernanceRequest request = new GovernanceRequest();
+    request.setUri("/faultInjectDelay");
+    request.setServiceName("srcService");
+
+    Fault fault = faultInjectionHandler.getActuator(request);
+    ds.withFaultInjection(fault);
+
+    Assertions.assertEquals("test", ds.get());
+
+    // flow control
+    CountDownLatch cd = new CountDownLatch(10);
+    AtomicBoolean expected = new AtomicBoolean(false);
+    AtomicBoolean notExpected = new AtomicBoolean(false);
+    for (int i = 0; i < 10; i++) {
+      new Thread(() -> {
+        try {
+          long startTime = System.currentTimeMillis();
+          Object result = ds.get();
+          if (!"test".equals(result)) {
+            notExpected.set(true);
+          }
+          // delayTime is 2S
+          if (System.currentTimeMillis() - startTime > 1000) {
+            expected.set(true);
+          }
+        } catch (Throwable e) {
+          notExpected.set(true);
+        }
+        cd.countDown();
+      }).start();
+    }
+    //timeout should be bigger than delayTime
+    cd.await(10, TimeUnit.SECONDS);
+    Assertions.assertFalse(notExpected.get());
+    Assertions.assertTrue(expected.get());
+  }
+
+  @Test
+  public void test_abort_fault_injection_service_name_work() throws Throwable {
+    FaultInjectionDecorateCheckedSupplier<Object> ds =
+        FaultInjectionDecorators.ofCheckedSupplier(() -> "test");
+
+    GovernanceRequest request = new GovernanceRequest();
+    request.setUri("/faultInjectAbort");
+    request.setServiceName("srcService");
+
+    Fault fault = faultInjectionHandler.getActuator(request);
+    ds.withFaultInjection(fault);
+
+    Assertions.assertEquals("test", ds.get());
+
+    // flow control
+    CountDownLatch cd = new CountDownLatch(10);
+    AtomicBoolean expected = new AtomicBoolean(false);
+    AtomicBoolean notExpected = new AtomicBoolean(false);
+    for (int i = 0; i < 10; i++) {
+      new Thread(() -> {
+        try {
+          Object result = ds.get();
+          if (!"test".equals(result)) {
+            notExpected.set(true);
+          }
+        } catch (FaultInjectionException e) {
+          expected.set(true);
+        } catch (Throwable e) {
+          notExpected.set(true);
+        }
+        cd.countDown();
+      }).start();
+    }
+    cd.await(1, TimeUnit.SECONDS);
+    Assertions.assertFalse(notExpected.get());
+    Assertions.assertTrue(expected.get());
+  }
+}
diff --git a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java
index 21135ab..5cd5b25 100644
--- a/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java
+++ b/governance/src/test/java/org/apache/servicecomb/governance/GovernancePropertiesTest.java
@@ -31,15 +31,18 @@
 import org.apache.servicecomb.governance.policy.AbstractPolicy;
 import org.apache.servicecomb.governance.policy.BulkheadPolicy;
 import org.apache.servicecomb.governance.policy.CircuitBreakerPolicy;
+import org.apache.servicecomb.governance.policy.FaultInjectionPolicy;
 import org.apache.servicecomb.governance.policy.RateLimitingPolicy;
 import org.apache.servicecomb.governance.policy.RetryPolicy;
 import org.apache.servicecomb.governance.properties.BulkheadProperties;
 import org.apache.servicecomb.governance.properties.CircuitBreakerProperties;
+import org.apache.servicecomb.governance.properties.FaultInjectionProperties;
 import org.apache.servicecomb.governance.properties.GovernanceProperties;
 import org.apache.servicecomb.governance.properties.InstanceIsolationProperties;
 import org.apache.servicecomb.governance.properties.MatchProperties;
 import org.apache.servicecomb.governance.properties.RateLimitProperties;
 import org.apache.servicecomb.governance.properties.RetryProperties;
+import org.apache.servicecomb.injection.FaultInjectionConst;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -68,6 +71,8 @@
 
   private RetryProperties retryProperties;
 
+  private FaultInjectionProperties faultInjectionProperties;
+
   private Environment environment;
 
   @Autowired
@@ -106,6 +111,12 @@
   }
 
   @Autowired
+  public void setFaultInjectionProperties(
+      FaultInjectionProperties faultInjectionProperties) {
+    this.faultInjectionProperties = faultInjectionProperties;
+  }
+
+  @Autowired
   public void setEnvironment(Environment environment) {
     this.environment = environment;
   }
@@ -153,13 +164,13 @@
 
   @Test
   public void test_all_bean_is_loaded() {
-    Assertions.assertEquals(5, propertiesList.size());
+    Assertions.assertEquals(6, propertiesList.size());
   }
 
   @Test
   public void test_match_properties_successfully_loaded() {
     Map<String, TrafficMarker> markers = matchProperties.getParsedEntity();
-    Assertions.assertEquals(7, markers.size());
+    Assertions.assertEquals(9, markers.size());
     TrafficMarker demoRateLimiting = markers.get("demo-rateLimiting");
     List<Matcher> matchers = demoRateLimiting.getMatches();
     Assertions.assertEquals(1, matchers.size());
@@ -177,17 +188,17 @@
   @Test
   public void test_match_properties_delete() {
     Map<String, TrafficMarker> markers = matchProperties.getParsedEntity();
-    Assertions.assertEquals(7, markers.size());
+    Assertions.assertEquals(9, markers.size());
     dynamicValues.put("servicecomb.matchGroup.test", "matches:\n"
         + "  - apiPath:\n"
         + "      exact: \"/hello2\"\n"
         + "    name: match0");
     GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet())));
     markers = matchProperties.getParsedEntity();
-    Assertions.assertEquals(8, markers.size());
+    Assertions.assertEquals(10, markers.size());
     tearDown();
     markers = matchProperties.getParsedEntity();
-    Assertions.assertEquals(7, markers.size());
+    Assertions.assertEquals(9, markers.size());
   }
 
   @Test
@@ -204,7 +215,7 @@
     GovernanceEventManager.post(new GovernanceConfigurationChangedEvent(new HashSet<>(dynamicValues.keySet())));
 
     Map<String, TrafficMarker> markers = matchProperties.getParsedEntity();
-    Assertions.assertEquals(8, markers.size());
+    Assertions.assertEquals(10, markers.size());
     TrafficMarker demoRateLimiting = markers.get("demo-rateLimiting");
     List<Matcher> matchers = demoRateLimiting.getMatches();
     Assertions.assertEquals(1, matchers.size());
@@ -388,4 +399,19 @@
     Assertions.assertEquals(2, policy.getMinimumNumberOfCalls());
     Assertions.assertEquals("2", policy.getSlidingWindowSize());
   }
+
+  @Test
+  public void test_fault_injection_properties_successfully_loaded() {
+    Map<String, FaultInjectionPolicy> policies = faultInjectionProperties.getParsedEntity();
+    Assertions.assertEquals(2, policies.size());
+    FaultInjectionPolicy policy = policies.get("demo-faultInjectDelay");
+    Assertions.assertEquals(FaultInjectionConst.TYPE_DELAY, policy.getType());
+    Assertions.assertEquals(2000, policy.getDelayTimeToMillis());
+    Assertions.assertEquals(100, policy.getPercentage());
+
+    policy = policies.get("demo-faultInjectAbort");
+    Assertions.assertEquals(FaultInjectionConst.TYPE_ABORT, policy.getType());
+    Assertions.assertEquals(50, policy.getPercentage());
+    Assertions.assertEquals(500, policy.getErrorCode());
+  }
 }
diff --git a/governance/src/test/resources/application.yaml b/governance/src/test/resources/application.yaml
index eee3e84..8a617a1 100644
--- a/governance/src/test/resources/application.yaml
+++ b/governance/src/test/resources/application.yaml
@@ -58,6 +58,14 @@
         - apiPath:
           exact: "/bulkhead"
       services: other:1.0
+    demo-faultInjectDelay: |
+      matches:
+        - apiPath:
+            contains: "/faultInjectDelay"
+    demo-faultInjectAbort: |
+      matches:
+        - apiPath:
+            contains: "/faultInjectAbort"
   rateLimiting:
     demo-rateLimiting: |
       rate: 1
@@ -105,3 +113,15 @@
       slidingWindowSize: 2
       slidingWindowType: COUNT_BASED
       waitDurationInOpenState: 1000
+  faultInjection:
+    demo-faultInjectDelay: |
+      delayTime: 2S
+      type: delay
+      percentage: 100
+    demo-faultInjectAbort: |
+      type: abort
+      percentage: 50
+      errorCode: 500
+    wrongIngored: |
+      delayTime: -1
+      type: ERROR