This is an automated email from the ASF dual-hosted git repository.
xiaoyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 16f4fdc [ISSUE #2910] Add integrated test for Resilience4j plugin
(#2943)
16f4fdc is described below
commit 16f4fdc6c5ac558661a52bded091e002350e4bd1
Author: Han Gao <[email protected]>
AuthorDate: Sun Feb 27 10:43:12 2022 +0800
[ISSUE #2910] Add integrated test for Resilience4j plugin (#2943)
* add integrated test for resilience4j plugin
* add circuit tests
* upate some configurations
* add resilience4j plugin dependency
---
.../http/controller/HttpTestController.java | 68 ++++++--
.../shenyu-integrated-test-http/pom.xml | 8 +
.../http/combination/Resilience4JPluginTest.java | 178 +++++++++++++++++++++
3 files changed, 240 insertions(+), 14 deletions(-)
diff --git
a/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
b/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
index 9f7384e..ad660e6 100644
---
a/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
+++
b/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
@@ -29,17 +29,18 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.multipart.FilePart;
-import org.springframework.web.bind.annotation.CookieValue;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestPart;
-import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.CookieValue;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@@ -61,7 +62,7 @@ import java.util.Map;
@ShenyuSpringMvcClient(path = "/test/**")
public class HttpTestController {
- private static final Logger logger =
LoggerFactory.getLogger(HttpTestController.class);
+ private static final Logger LOGGER =
LoggerFactory.getLogger(HttpTestController.class);
/**
* Post user dto.
@@ -89,6 +90,7 @@ public class HttpTestController {
* Find by user id string.
*
* @param userId the user id
+ * @param name name
* @return the string
*/
@GetMapping("/findByUserIdName")
@@ -265,7 +267,7 @@ public class HttpTestController {
* @param cookie cookie
* @param requestHeader header
* @param requestParameter parameter
- * @return
+ * @return result
*/
@PostMapping(path = "/modifyRequest")
public Map<String, Object> modifyRequest(@RequestBody final UserDTO
userDTO,
@@ -285,7 +287,7 @@ public class HttpTestController {
*
* @param body file content
* @return file
- * @throws IOException
+ * @throws IOException io exception
*/
@GetMapping(path = "/download")
public ResponseEntity<byte[]> downloadFile(@RequestParam(value = "body",
defaultValue = "") final String body) throws IOException {
@@ -304,23 +306,61 @@ public class HttpTestController {
*
* @param filePart upload file
* @return OK
- * @throws IOException
+ * @throws IOException io exception
*/
@PostMapping(path = "/upload", consumes =
MediaType.MULTIPART_FORM_DATA_VALUE)
- public String downloadFile(@RequestPart("file") FilePart filePart) throws
IOException {
- logger.info("file name: {}", filePart.filename());
+ public String downloadFile(@RequestPart("file") final FilePart filePart)
throws IOException {
+ LOGGER.info("file name: {}", filePart.filename());
Path tempFile =
Files.createTempFile(String.valueOf(System.currentTimeMillis()),
filePart.filename());
filePart.transferTo(tempFile.toFile());
try (BufferedReader bufferedReader = new BufferedReader(new
FileReader(tempFile.toFile()))) {
String line = bufferedReader.readLine();
while (line != null) {
- logger.info(line);
+ LOGGER.info(line);
line = bufferedReader.readLine();
}
}
return "OK";
}
+ /**
+ * Return bad request code.
+ *
+ * @return response. result bean
+ */
+ @GetMapping("/request/badrequest")
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ResultBean badRequest() {
+ ResultBean response = new ResultBean();
+ response.setCode(400);
+ return response;
+ }
+
+ /**
+ * Return bad request code.
+ *
+ * @return response. result bean
+ */
+ @GetMapping("/request/accepted")
+ @ResponseStatus(HttpStatus.ACCEPTED)
+ public ResultBean accepted() {
+ ResultBean response = new ResultBean();
+ response.setCode(202);
+ return response;
+ }
+
+ /**
+ * Return bad request code.
+ *
+ * @return response. result bean
+ */
+ @GetMapping("/success")
+ public ResultBean success() {
+ ResultBean response = new ResultBean();
+ response.setCode(200);
+ return response;
+ }
+
private UserDTO buildUser(final String id, final String name) {
UserDTO userDTO = new UserDTO();
userDTO.setUserId(id);
diff --git a/shenyu-integrated-test/shenyu-integrated-test-http/pom.xml
b/shenyu-integrated-test/shenyu-integrated-test-http/pom.xml
index 80de60a..330e931 100644
--- a/shenyu-integrated-test/shenyu-integrated-test-http/pom.xml
+++ b/shenyu-integrated-test/shenyu-integrated-test-http/pom.xml
@@ -122,6 +122,14 @@
</dependency>
<!-- apache shenyu request plugin end-->
+ <!-- apache shenyu resilience4j plugin start-->
+ <dependency>
+ <groupId>org.apache.shenyu</groupId>
+
<artifactId>shenyu-spring-boot-starter-plugin-resilience4j</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- apache shenyu resilience4j plugin end-->
+
<dependency>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-integrated-test-common</artifactId>
diff --git
a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/Resilience4JPluginTest.java
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/Resilience4JPluginTest.java
new file mode 100644
index 0000000..7e905d9
--- /dev/null
+++
b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/combination/Resilience4JPluginTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.shenyu.integrated.test.http.combination;
+
+import com.google.common.collect.Lists;
+import org.apache.shenyu.common.dto.ConditionData;
+import org.apache.shenyu.common.dto.convert.rule.Resilience4JHandle;
+import org.apache.shenyu.common.enums.OperatorEnum;
+import org.apache.shenyu.common.enums.ParamTypeEnum;
+import org.apache.shenyu.common.enums.PluginEnum;
+import org.apache.shenyu.common.utils.JsonUtils;
+import org.apache.shenyu.integratedtest.common.AbstractPluginDataInit;
+import org.apache.shenyu.integratedtest.common.helper.HttpHelper;
+import org.apache.shenyu.integratedtest.common.result.ResultBean;
+import org.apache.shenyu.web.controller.LocalPluginController;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public final class Resilience4JPluginTest extends AbstractPluginDataInit {
+
+ private static final String TEST_RESILIENCE4J_SUCCESS_OUT_SCOPE_PATH =
"/http/test/success";
+
+ private static final String TEST_RESILIENCE4J_SUCCESS_PATH =
"/http/test/request/accepted";
+
+ private static final String TEST_RESILIENCE4J_BAD_REQUEST_PATH =
"/http/test/request/badrequest";
+
+ @BeforeEach
+ public void setup() throws IOException {
+ String pluginResult = initPlugin(PluginEnum.RESILIENCE4J.getName(),
null);
+ assertThat(pluginResult, is("success"));
+ }
+
+ @Test
+ public void testRateLimiterPass() throws InterruptedException,
ExecutionException, IOException {
+ String selectorAndRulesResult =
+ initSelectorAndRules(PluginEnum.RESILIENCE4J.getName(), "",
+ buildSelectorConditionList(),
buildRuleLocalDataList(0, 3, null));
+ assertThat(selectorAndRulesResult, is("success"));
+
+ Set<Integer> resultSet = new HashSet<>();
+ Future<ResultBean> resp1 = this.getService().submit(() ->
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_SUCCESS_PATH,
ResultBean.class));
+ Future<ResultBean> resp2 = this.getService().submit(() ->
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_SUCCESS_PATH,
ResultBean.class));
+ Future<ResultBean> resp3 = this.getService().submit(() ->
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_SUCCESS_PATH,
ResultBean.class));
+ resultSet.add(resp1.get().getCode());
+ resultSet.add(resp2.get().getCode());
+ resultSet.add(resp3.get().getCode());
+ assertTrue(resultSet.contains(202));
+ }
+
+ @Test
+ public void testRateLimiter() throws InterruptedException,
ExecutionException, IOException {
+ String selectorAndRulesResult =
+ initSelectorAndRules(PluginEnum.RESILIENCE4J.getName(), "",
+ buildSelectorConditionList(),
buildRuleLocalDataList(0, 1, null));
+ assertThat(selectorAndRulesResult, is("success"));
+
+ Set<Integer> resultSet = new HashSet<>();
+ Future<ResultBean> resp1 = this.getService().submit(() ->
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_SUCCESS_PATH,
ResultBean.class));
+ Future<ResultBean> resp2 = this.getService().submit(() ->
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_SUCCESS_PATH,
ResultBean.class));
+ Future<ResultBean> resp3 = this.getService().submit(() ->
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_SUCCESS_PATH,
ResultBean.class));
+ resultSet.add(resp1.get().getCode());
+ resultSet.add(resp2.get().getCode());
+ resultSet.add(resp3.get().getCode());
+ assertTrue(resultSet.contains(202));
+ assertTrue(resultSet.contains(429));
+ }
+
+ @Test
+ public void testCircuitBreaker() throws IOException {
+ String selectorAndRulesResult =
+ initSelectorAndRules(PluginEnum.RESILIENCE4J.getName(), "",
+ buildSelectorConditionList(),
buildRuleLocalDataList(1, 5000, null));
+ assertThat(selectorAndRulesResult, is("success"));
+
+ List<Integer> rets = new ArrayList<>();
+ for (int i = 0; i < 2; i++) {
+ ResultBean resp =
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_SUCCESS_PATH,
ResultBean.class);
+ rets.add(resp.getCode());
+ }
+ for (int i = 0; i < 5; i++) {
+ ResultBean resp =
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_BAD_REQUEST_PATH,
ResultBean.class);
+ rets.add(resp.getCode());
+ }
+ assertTrue(rets.contains(202));
+ assertTrue(rets.contains(400));
+ assertTrue(rets.contains(-103));
+ }
+
+ @Test
+ public void testCircuitBreakerFallbackUri() throws IOException {
+ String selectorAndRulesResult =
+ initSelectorAndRules(PluginEnum.RESILIENCE4J.getName(), "",
+ buildSelectorConditionList(),
buildRuleLocalDataList(1, 5000, TEST_RESILIENCE4J_SUCCESS_OUT_SCOPE_PATH));
+ assertThat(selectorAndRulesResult, is("success"));
+
+ List<Integer> rets = new ArrayList<>();
+ for (int i = 0; i < 2; i++) {
+ ResultBean resp =
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_SUCCESS_PATH,
ResultBean.class);
+ rets.add(resp.getCode());
+ }
+ for (int i = 0; i < 5; i++) {
+ ResultBean resp =
HttpHelper.INSTANCE.getFromGateway(TEST_RESILIENCE4J_BAD_REQUEST_PATH,
ResultBean.class);
+ rets.add(resp.getCode());
+ }
+ assertTrue(rets.contains(202));
+ assertTrue(rets.contains(400));
+ assertTrue(rets.contains(200));
+ }
+
+ @AfterEach
+ public void clean() throws IOException {
+ cleanPluginData(PluginEnum.RESILIENCE4J.getName());
+ }
+
+ private static List<ConditionData> buildSelectorConditionList() {
+ ConditionData conditionData = new ConditionData();
+ conditionData.setParamType(ParamTypeEnum.URI.getName());
+ conditionData.setOperator(OperatorEnum.MATCH.getAlias());
+ conditionData.setParamValue("/http/test/**");
+ return Collections.singletonList(conditionData);
+ }
+
+ private static List<LocalPluginController.RuleLocalData>
buildRuleLocalDataList(final int circuitEnable, final int limitForPeriod, final
String fallbackUri) {
+ final LocalPluginController.RuleLocalData ruleLocalData = new
LocalPluginController.RuleLocalData();
+ Resilience4JHandle resilience4JHandle = new Resilience4JHandle();
+ // set parameters for rate limiter
+ resilience4JHandle.setTimeoutDuration(5000);
+ resilience4JHandle.setLimitRefreshPeriod(500000);
+ resilience4JHandle.setLimitForPeriod(limitForPeriod);
+
+ // set parameters for circuit breaker
+ resilience4JHandle.setCircuitEnable(circuitEnable);
+ resilience4JHandle.setFallbackUri(fallbackUri);
+ resilience4JHandle.setMinimumNumberOfCalls(1);
+ resilience4JHandle.setFailureRateThreshold(50);
+
+ ruleLocalData.setRuleHandler(JsonUtils.toJson(resilience4JHandle));
+
+ ConditionData conditionData = new ConditionData();
+ conditionData.setParamType(ParamTypeEnum.URI.getName());
+ conditionData.setOperator(OperatorEnum.MATCH.getAlias());
+ conditionData.setParamValue("/http/test/request/**");
+
+
ruleLocalData.setConditionDataList(Collections.singletonList(conditionData));
+
+ return Lists.newArrayList(ruleLocalData);
+ }
+
+}