diff --git a/example/src/main/java/com/example/ExampleClientHttpRequestInterceptor.java b/example/src/main/java/com/example/ExampleClientHttpRequestInterceptor.java
new file mode 100644
index 00000000..3216a164
--- /dev/null
+++ b/example/src/main/java/com/example/ExampleClientHttpRequestInterceptor.java
@@ -0,0 +1,30 @@
+package com.example;
+
+import com.x.retry.client.core.intercepter.RetrySiteSnapshot;
+import com.x.retry.common.core.constant.SystemConstants;
+import com.x.retry.common.core.model.XRetryHeaders;
+import com.x.retry.common.core.util.JsonUtil;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * @author: shuguang.zhang
+ * @date : 2022-04-17 15:22
+ */
+public class ExampleClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
+
+ @Override
+ public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
+ XRetryHeaders retryHeader = RetrySiteSnapshot.getRetryHeader();
+ if (Objects.nonNull(retryHeader)) {
+ request.getHeaders().add(SystemConstants.X_RETRY_HEAD, JsonUtil.toJsonString(retryHeader));
+ }
+
+ return execution.execute(request, body);
+ }
+}
diff --git a/example/src/main/java/com/example/config/RestTemplateConfig.java b/example/src/main/java/com/example/config/RestTemplateConfig.java
new file mode 100644
index 00000000..343fee1a
--- /dev/null
+++ b/example/src/main/java/com/example/config/RestTemplateConfig.java
@@ -0,0 +1,37 @@
+package com.example.config;
+
+import com.example.ExampleClientHttpRequestInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Collections;
+
+/**
+ * @author: www.byteblogs.com
+ * @date : 2022-03-09 14:19
+ */
+@Configuration
+public class RestTemplateConfig {
+
+ @Bean
+ public RestTemplate restTemplate(ClientHttpRequestFactory factory){
+ RestTemplate restTemplate = new RestTemplate(factory);
+ restTemplate.setInterceptors(Collections.singletonList(new ExampleClientHttpRequestInterceptor()));
+ return restTemplate;
+ }
+
+ @Bean
+ public ClientHttpRequestFactory okHttp3ClientHttpRequestFactory(){
+ OkHttp3ClientHttpRequestFactory factory = new OkHttp3ClientHttpRequestFactory();
+ factory.setReadTimeout(5000);
+ factory.setConnectTimeout(5000);
+ factory.setConnectTimeout(3000);
+ factory.setWriteTimeout(5000);
+ return factory;
+ }
+
+
+}
diff --git a/example/src/main/java/com/example/controller/SchoolController.java b/example/src/main/java/com/example/controller/SchoolController.java
index 152b079e..f47c80a5 100644
--- a/example/src/main/java/com/example/controller/SchoolController.java
+++ b/example/src/main/java/com/example/controller/SchoolController.java
@@ -1,9 +1,14 @@
package com.example.controller;
-
+import com.x.retry.common.core.constant.SystemConstants;
+import com.x.retry.common.core.model.Result;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
-import org.springframework.stereotype.Controller;
+import javax.servlet.http.HttpServletRequest;
/**
*
@@ -13,8 +18,15 @@ import org.springframework.stereotype.Controller;
* @author www.byteblogs.com
* @since 2022-03-24
*/
-@Controller
+@RestController
@RequestMapping("/school")
public class SchoolController {
+ @GetMapping("/id")
+ public Result getSchool(HttpServletRequest request) {
+ String header = request.getHeader(SystemConstants.X_RETRY_HEAD);
+ System.out.println(header);
+ return new Result("school");
+ }
+
}
diff --git a/example/src/main/java/com/example/demo/RemoteService.java b/example/src/main/java/com/example/demo/RemoteService.java
index 8a5f0c8e..2fe656d9 100644
--- a/example/src/main/java/com/example/demo/RemoteService.java
+++ b/example/src/main/java/com/example/demo/RemoteService.java
@@ -1,7 +1,9 @@
package com.example.demo;
import com.x.retry.common.core.model.Result;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
/**
* @author: www.byteblogs.com
@@ -10,7 +12,11 @@ import org.springframework.stereotype.Component;
@Component
public class RemoteService {
+ @Autowired
+ private RestTemplate restTemplate;
+
public Result call() {
- return new Result();
+// restTemplate.getForObject("http://127.0.0.1:8088/school/id", Result.class);
+ return new Result();
}
}
diff --git a/example/src/test/java/com/example/ExampleApplicationTests.java b/example/src/test/java/com/example/ExampleApplicationTests.java
index 094aff3f..7f00934c 100644
--- a/example/src/test/java/com/example/ExampleApplicationTests.java
+++ b/example/src/test/java/com/example/ExampleApplicationTests.java
@@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.web.client.RestTemplate;
@SpringBootTest
@Slf4j
@@ -14,6 +15,9 @@ public class ExampleApplicationTests {
@Autowired
private SchoolMapper schoolMapper;
+ @Autowired
+ private RestTemplate restTemplate;
+
@Test
public void test() {
School school = new School();
@@ -22,5 +26,12 @@ public class ExampleApplicationTests {
schoolMapper.insert(school);
}
+ @Test
+ public void test1() {
+ String template = restTemplate.getForObject("http://127.0.0.1:8088/school/id", String.class);
+ System.out.println(template);
+ }
+
+
}
diff --git a/example/src/test/java/com/example/ExistsTransactionalRetryServiceTest.java b/example/src/test/java/com/example/ExistsTransactionalRetryServiceTest.java
index 2fdc40e0..1bdc59b6 100644
--- a/example/src/test/java/com/example/ExistsTransactionalRetryServiceTest.java
+++ b/example/src/test/java/com/example/ExistsTransactionalRetryServiceTest.java
@@ -89,7 +89,7 @@ public class ExistsTransactionalRetryServiceTest {
.thenReturn(new Result(0, "5"))
;
try {
- for (int i = 0; i < 300; i++) {
+ for (int i = 0; i < 100; i++) {
threadPoolExecutor.execute(() -> testExistsTransactionalRetryService.testSimpleInsert(UUID.randomUUID().toString()));
}
} catch (Exception e) {
diff --git a/pom.xml b/pom.xml
index db2461b6..6b708759 100644
--- a/pom.xml
+++ b/pom.xml
@@ -21,7 +21,7 @@
1.8
1.8
1.8
- 0.0.1.0
+ 0.0.1.0-SNAPSHOT
1.0.0
11.7
5.4.2.Final
diff --git a/x-retry-client-core/src/main/java/com/x/retry/client/core/config/XRetryWebMvcConfigurerAdapter.java b/x-retry-client-core/src/main/java/com/x/retry/client/core/config/XRetryWebMvcConfigurerAdapter.java
new file mode 100644
index 00000000..a8a57d57
--- /dev/null
+++ b/x-retry-client-core/src/main/java/com/x/retry/client/core/config/XRetryWebMvcConfigurerAdapter.java
@@ -0,0 +1,27 @@
+package com.x.retry.client.core.config;
+
+import com.x.retry.client.core.intercepter.HeadersInterceptor;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @author www.byteblogs.com
+ * @date 2022-03-06
+ * @since 2.0
+ */
+@Configuration
+public class XRetryWebMvcConfigurerAdapter implements WebMvcConfigurer {
+
+ @Autowired
+ private HeadersInterceptor headersInterceptor;
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 注册拦截器
+ registry.addInterceptor(headersInterceptor).addPathPatterns("/retry/**");
+
+ }
+
+}
diff --git a/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/HeadersInterceptor.java b/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/HeadersInterceptor.java
new file mode 100644
index 00000000..a909df68
--- /dev/null
+++ b/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/HeadersInterceptor.java
@@ -0,0 +1,46 @@
+package com.x.retry.client.core.intercepter;
+
+import com.x.retry.common.core.constant.SystemConstants;
+import com.x.retry.common.core.log.LogUtils;
+import com.x.retry.common.core.model.XRetryHeaders;
+import com.x.retry.common.core.util.JsonUtil;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Objects;
+
+/**
+ * @Author:byteblogs
+ * @Date:2018/09/27 12:52
+ */
+@Component
+public class HeadersInterceptor implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
+ String xRetry = httpServletRequest.getHeader(SystemConstants.X_RETRY_HEAD);
+ if (Objects.nonNull(xRetry)) {
+ LogUtils.info("x-retry 拦截器 xRetry:[{}]", xRetry);
+ RetrySiteSnapshot.setRetryHeader(JsonUtil.parseObject(xRetry, XRetryHeaders.class));
+ }
+
+ return true;
+ }
+
+ @Override
+ public void postHandle(HttpServletRequest httpServletRequest,
+ HttpServletResponse httpServletResponse,
+ Object o, ModelAndView modelAndView) throws Exception {
+
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest httpServletRequest,
+ HttpServletResponse httpServletResponse,
+ Object o, Exception e) throws Exception {
+ }
+}
diff --git a/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/RetryAspect.java b/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/RetryAspect.java
index 6a6d0d3f..adc72799 100644
--- a/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/RetryAspect.java
+++ b/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/RetryAspect.java
@@ -35,8 +35,6 @@ public class RetryAspect {
@Autowired
@Qualifier("localRetryStrategies")
private RetryStrategy retryStrategy;
- @Autowired(required = false)
- private TransactionTemplate transactionTemplate;
@Around("@annotation(com.x.retry.client.core.annotation.Retryable)")
public Object around(ProceedingJoinPoint point) throws Throwable {
@@ -74,7 +72,7 @@ public class RetryAspect {
private void doHandlerRetry(ProceedingJoinPoint point, String traceId, Retryable retryable, String executorClassName, String methodEntrance, Throwable throwable) {
- if (!RetrySiteSnapshot.isMethodEntrance(methodEntrance)) {
+ if (!RetrySiteSnapshot.isMethodEntrance(methodEntrance) || RetrySiteSnapshot.isRunning()) {
return;
}
@@ -97,7 +95,7 @@ public class RetryAspect {
private void openRetry(ProceedingJoinPoint point, String traceId, Retryable retryable, String executorClassName, Throwable throwable) {
try {
- if (Objects.isNull(throwable) || RetrySiteSnapshot.isRunning()) {
+ if (Objects.isNull(throwable)) {
return;
}
diff --git a/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/RetrySiteSnapshot.java b/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/RetrySiteSnapshot.java
index 89e95b82..07cf4f10 100644
--- a/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/RetrySiteSnapshot.java
+++ b/x-retry-client-core/src/main/java/com/x/retry/client/core/intercepter/RetrySiteSnapshot.java
@@ -1,7 +1,10 @@
package com.x.retry.client.core.intercepter;
+import com.x.retry.common.core.model.XRetryHeaders;
import lombok.Getter;
+import java.util.Objects;
+
/**
* 重试现场记录器
*
@@ -25,6 +28,11 @@ public class RetrySiteSnapshot {
*/
private static final ThreadLocal RETRY_STATUS = ThreadLocal.withInitial(EnumStatus.COMPLETE::getStatus);
+ /**
+ * 重试请求头
+ */
+ private static final ThreadLocal RETRY_HEADER = new ThreadLocal<>();
+
public static Integer getStage() {
return RETRY_STAGE.get();
}
@@ -57,6 +65,26 @@ public class RetrySiteSnapshot {
return EnumStatus.RUNNING.status == getStatus();
}
+ public static XRetryHeaders getRetryHeader() {
+ return RETRY_HEADER.get();
+ }
+
+ public static void setRetryHeader(XRetryHeaders headers) {
+ RETRY_HEADER.set(headers);
+ }
+
+ /**
+ * 是否是重试流量
+ */
+ public static boolean isRetryFlow(XRetryHeaders headers) {
+ XRetryHeaders retryHeader = getRetryHeader();
+ if (Objects.nonNull(retryHeader)) {
+ return retryHeader.isXRetry();
+ }
+
+ return false;
+ }
+
public static void removeAll() {
RETRY_STATUS.remove();
RETRY_CLASS_METHOD_ENTRANCE.remove();
diff --git a/x-retry-common/x-retry-common-core/src/main/java/com/x/retry/common/core/constant/SystemConstants.java b/x-retry-common/x-retry-common-core/src/main/java/com/x/retry/common/core/constant/SystemConstants.java
new file mode 100644
index 00000000..922300ca
--- /dev/null
+++ b/x-retry-common/x-retry-common-core/src/main/java/com/x/retry/common/core/constant/SystemConstants.java
@@ -0,0 +1,14 @@
+package com.x.retry.common.core.constant;
+
+/**
+ * @author: www.byteblogs.com
+ * @date : 2022-04-17 10:47
+ */
+public class SystemConstants {
+
+ /**
+ * 请求头
+ */
+ public static final String X_RETRY_HEAD = "X-RETRY";
+
+}
diff --git a/x-retry-common/x-retry-common-core/src/main/java/com/x/retry/common/core/model/XRetryHeaders.java b/x-retry-common/x-retry-common-core/src/main/java/com/x/retry/common/core/model/XRetryHeaders.java
new file mode 100644
index 00000000..abce0cb4
--- /dev/null
+++ b/x-retry-common/x-retry-common-core/src/main/java/com/x/retry/common/core/model/XRetryHeaders.java
@@ -0,0 +1,16 @@
+package com.x.retry.common.core.model;
+
+import lombok.Data;
+
+/**
+ * @author: shuguang.zhang
+ * @date : 2022-04-16 22:20
+ */
+@Data
+public class XRetryHeaders {
+
+ /**
+ * 是否是重试流量
+ */
+ private boolean xRetry;
+}
diff --git a/x-retry-server/src/main/java/com/x/retry/server/support/dispatch/actor/exec/ExecUnitActor.java b/x-retry-server/src/main/java/com/x/retry/server/support/dispatch/actor/exec/ExecUnitActor.java
index 20f0a5b2..2a237aea 100644
--- a/x-retry-server/src/main/java/com/x/retry/server/support/dispatch/actor/exec/ExecUnitActor.java
+++ b/x-retry-server/src/main/java/com/x/retry/server/support/dispatch/actor/exec/ExecUnitActor.java
@@ -1,26 +1,20 @@
package com.x.retry.server.support.dispatch.actor.exec;
import akka.actor.AbstractActor;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.google.common.util.concurrent.RateLimiter;
import com.x.retry.client.model.DispatchRetryDTO;
import com.x.retry.client.model.DispatchRetryResultDTO;
+import com.x.retry.common.core.constant.SystemConstants;
import com.x.retry.common.core.log.LogUtils;
import com.x.retry.common.core.model.Result;
+import com.x.retry.common.core.model.XRetryHeaders;
import com.x.retry.common.core.util.Assert;
import com.x.retry.common.core.util.JsonUtil;
import com.x.retry.server.exception.XRetryServerException;
import com.x.retry.server.persistence.mybatis.mapper.RetryTaskLogMapper;
-import com.x.retry.server.persistence.mybatis.mapper.ServerNodeMapper;
-import com.x.retry.server.persistence.mybatis.po.GroupConfig;
import com.x.retry.server.persistence.mybatis.po.RetryTask;
import com.x.retry.server.persistence.mybatis.po.RetryTaskLog;
import com.x.retry.server.persistence.mybatis.po.ServerNode;
-import com.x.retry.server.persistence.support.ConfigAccess;
-import com.x.retry.server.support.ClientLoadBalance;
import com.x.retry.server.support.IdempotentStrategy;
-import com.x.retry.server.support.allocate.client.ClientLoadBalanceManager;
-import com.x.retry.server.support.cache.CacheGroupRateLimiter;
import com.x.retry.server.support.context.MaxAttemptsPersistenceRetryContext;
import com.x.retry.server.support.retry.RetryExecutor;
import org.apache.commons.lang.StringUtils;
@@ -29,18 +23,15 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
-import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;
import java.text.MessageFormat;
import java.time.LocalDateTime;
-import java.util.List;
import java.util.Objects;
-import java.util.TreeSet;
import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
/**
* 重试结果执行器
@@ -117,8 +108,16 @@ public class ExecUnitActor extends AbstractActor {
dispatchRetryDTO.setExecutorName(retryTask.getExecutorName());
dispatchRetryDTO.setArgsStr(retryTask.getArgsStr());
+ // 设置header
+ HttpHeaders requestHeaders = new HttpHeaders();
+ XRetryHeaders xRetryHeaders = new XRetryHeaders();
+ xRetryHeaders.setXRetry(Boolean.TRUE);
+ requestHeaders.add(SystemConstants.X_RETRY_HEAD, JsonUtil.toJsonString(xRetryHeaders));
+
+ HttpEntity requestEntity = new HttpEntity<>(dispatchRetryDTO, requestHeaders);
+
String format = MessageFormat.format(URL, serverNode.getHostIp(), serverNode.getHostPort().toString());
- Result result = restTemplate.postForObject(format, dispatchRetryDTO, Result.class);
+ Result result = restTemplate.postForObject(format, requestEntity, Result.class);
if (1 != result.getStatus() && StringUtils.isNotBlank(result.getMessage())) {
retryTaskLog.setErrorMessage(result.getMessage());