新增feign 请求头传递和response 传递
This commit is contained in:
byteblogs168 2023-01-14 20:48:11 +08:00
parent 347bc9a616
commit 3b04af96e4
19 changed files with 329 additions and 48 deletions

View File

@ -71,6 +71,12 @@
<artifactId>okhttp</artifactId>
<version>4.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>3.0.3</version>
</dependency>
</dependencies>
<build>

View File

@ -5,10 +5,13 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
//import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableFeignClients(basePackages = "com.example.client")
@EnableXRetry(group = "example_group")
@EnableAspectJAutoProxy
@EnableTransactionManagement

View File

@ -1,11 +1,7 @@
package com.example;
import com.x.retry.client.core.exception.XRetryClientException;
import com.x.retry.client.core.intercepter.RetrySiteSnapshot;
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 com.x.retry.client.core.plugin.RequestHeaderPlugins;
import com.x.retry.client.core.plugin.ResponseHeaderPlugins;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
@ -13,8 +9,7 @@ import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Map;
/**
* RestTemplate 拦截器
@ -37,33 +32,14 @@ public class ExampleClientHttpRequestInterceptor implements ClientHttpRequestInt
}
private void after(ClientHttpResponse execute) {
HttpHeaders headers = execute.getHeaders();
// 获取不重试标志
if (headers.containsKey(SystemConstants.X_RETRY_STATUS_CODE_KEY)) {
List<String> statusCode = headers.get(SystemConstants.X_RETRY_STATUS_CODE_KEY);
RetrySiteSnapshot.setRetryStatusCode(statusCode.get(0));
}
ResponseHeaderPlugins.responseHeader(execute.getHeaders());
}
private void before(HttpRequest request) {
XRetryHeaders retryHeader = RetrySiteSnapshot.getRetryHeader();
Map<String, String> header = RequestHeaderPlugins.requestHeader();
HttpHeaders headers = request.getHeaders();
header.forEach((key, value) -> headers.add(key, value));
// 传递请求头
if (Objects.nonNull(retryHeader)) {
long callRemoteTime = System.currentTimeMillis();
long entryMethodTime = RetrySiteSnapshot.getEntryMethodTime();
long transmitTime = retryHeader.getDdl() - (callRemoteTime - entryMethodTime);
LogUtils.info("RPC传递header头 entryMethodTime:[{}] - callRemoteTime:[{}] = transmitTime:[{}]", entryMethodTime, callRemoteTime, transmitTime);
if (transmitTime > 0) {
retryHeader.setDdl(transmitTime);
} else {
throw new XRetryClientException("调用链超时, 不在继续调用后面请求");
}
request.getHeaders().add(SystemConstants.X_RETRY_HEAD_KEY, JsonUtil.toJsonString(retryHeader));
}
}
}

View File

@ -0,0 +1,25 @@
package com.example;
import com.x.retry.client.core.plugin.RequestHeaderPlugins;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @author: www.byteblogs.com
* @date : 2022-05-16 15:05
*/
@Component
public class ExampleFeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
Map<String, String> header = RequestHeaderPlugins.requestHeader();
header.forEach((key, value) -> requestTemplate.header(key, value));
}
}

View File

@ -0,0 +1,17 @@
package com.example.client;
//import org.springframework.cloud.netflix.feign.FeignClient;
import com.x.retry.common.core.model.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author: www.byteblogs.com
* @date : 2022-05-16 15:32
*/
@FeignClient(name = "daemoClient", url = "http://127.0.0.1:8089")
public interface DemoClient {
@GetMapping("/school/id")
Result get();
}

View File

@ -0,0 +1,51 @@
package com.example.client;
import com.x.retry.client.core.plugin.ResponseHeaderPlugins;
import feign.FeignException;
import feign.Response;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import feign.codec.StringDecoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author: www.byteblogs.com
* @date : 2022-05-17 08:03
*/
@Configuration
public class FeignConfig {
@Bean
public Decoder decoder(@Autowired HttpMessageConverters httpMessageConverters) {
ObjectFactory<HttpMessageConverters> objectFactory = () -> httpMessageConverters;
return new SpringDecoder(objectFactory) {
@Override
public Object decode(Response response, Type type) throws IOException, DecodeException, FeignException {
Map<String, Collection<String>> headers = response.headers();
Map<String, List<String>> header = new HashMap<>();
headers.forEach((key, value)-> {
header.put(key, (List<String>) value);
});
ResponseHeaderPlugins.responseHeader(header);
return super.decode(response, type);
}
};
}
}

View File

@ -0,0 +1,44 @@
package com.example.controller;
import com.example.client.DemoClient;
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;
/**
* @author: www.byteblogs.com
* @date : 2022-05-19 08:21
*/
@RequestMapping("/ddl")
@RestController
public class TestDdlController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DemoClient demoClient;
@GetMapping("rest-template")
public Result testDDL() {
Result result = restTemplate.getForObject("http://127.0.0.1:8088/school/id", Result.class);
if (result.getStatus() == 0) {
throw new UnsupportedOperationException(result.getMessage());
}
return result;
}
@GetMapping("test-feign")
public Result feign() {
Result result = demoClient.get();
if (result.getStatus() == 0) {
throw new UnsupportedOperationException(result.getMessage());
}
return result;
}
}

View File

@ -0,0 +1,47 @@
package com.example.controller;
import com.example.client.DemoClient;
import com.x.retry.client.core.annotation.Retryable;
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;
/**
* @author: www.byteblogs.com
* @date : 2022-05-19 08:24
*/
@RequestMapping("/status-code")
@RestController
public class TestStatusCodeController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private DemoClient demoClient;
@GetMapping("rest-template")
@Retryable(scene = "testRestTemplateStatusCode")
public Result testRestTemplateStatusCode() {
Result result = restTemplate.getForObject("http://127.0.0.1:8088/school/id", Result.class);
if (result.getStatus() == 0) {
throw new UnsupportedOperationException(result.getMessage());
}
return result;
}
@GetMapping("feign")
@Retryable(scene = "testFeignStatusCode")
public Result testFeignStatusCode() {
Result result = demoClient.get();
if (result.getStatus() == 0) {
throw new UnsupportedOperationException(result.getMessage());
}
return result;
}
}

View File

@ -1,7 +1,10 @@
package com.example;
//import com.example.client.DemoClient;
import com.example.client.DemoClient;
import com.example.mapper.SchoolMapper;
import com.example.po.School;
import com.x.retry.common.core.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
@ -18,6 +21,15 @@ public class ExampleApplicationTests {
@Autowired
private RestTemplate restTemplate;
@Autowired(required = false)
private DemoClient demoClient;
@Test
public void demoClient() {
Result s = demoClient.get();
System.out.println(s);
}
@Test
public void test() {
School school = new School();

View File

@ -17,6 +17,9 @@ import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
@ -34,11 +37,12 @@ import java.util.concurrent.TimeUnit;
@Slf4j
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class NettyHttpConnectClient implements Lifecycle {
public class NettyHttpConnectClient implements Lifecycle, ApplicationContextAware {
private static final String HOST_ID = IdUtil.simpleUUID();
private static final String HOST_IP = HostUtils.getIp();
private ApplicationContext applicationContext;
private static Channel channel;
private static NioEventLoopGroup nioEventLoopGroup = new NioEventLoopGroup();
private static Bootstrap bootstrap = new Bootstrap();
@ -46,7 +50,7 @@ public class NettyHttpConnectClient implements Lifecycle {
public void start() {
try {
XRetryProperties xRetryProperties = SpringContext.getBeanByType(XRetryProperties.class);
XRetryProperties xRetryProperties = applicationContext.getBean(XRetryProperties.class);
XRetryProperties.ServerConfig server = xRetryProperties.getServer();
final NettyHttpConnectClient thisClient = this;
bootstrap.group(nioEventLoopGroup)
@ -116,4 +120,8 @@ public class NettyHttpConnectClient implements Lifecycle {
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@ -3,8 +3,8 @@ package com.x.retry.client.core.init;
import com.x.retry.client.core.Lifecycle;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.List;
@ -17,13 +17,13 @@ import java.util.List;
*/
@Component
@Slf4j
public class StartListener implements ApplicationListener<ContextRefreshedEvent> {
public class StartListener implements ApplicationRunner {
@Autowired
private List<Lifecycle> lifecycleList;
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
public void run(ApplicationArguments args) throws Exception {
log.info("X-RETRY-CLIENT-RETRY 启动");
lifecycleList.forEach(Lifecycle::start);
log.info("X-RETRY-CLIENT-RETRY 启动成功");

View File

@ -0,0 +1,51 @@
package com.x.retry.client.core.plugin;
import com.x.retry.client.core.exception.XRetryClientException;
import com.x.retry.client.core.intercepter.RetrySiteSnapshot;
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 java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* @author: www.byteblogs.com
* @date : 2022-05-17 09:01
*/
public class RequestHeaderPlugins {
private RequestHeaderPlugins() {
}
/**
* 请求头传递
*
* @return 头信息
*/
public static Map<String, String> requestHeader() {
Map<String, String> header = new HashMap<>();
XRetryHeaders retryHeader = RetrySiteSnapshot.getRetryHeader();
// 传递请求头
if (Objects.nonNull(retryHeader)) {
long callRemoteTime = System.currentTimeMillis();
long entryMethodTime = RetrySiteSnapshot.getEntryMethodTime();
long transmitTime = retryHeader.getDdl() - (callRemoteTime - entryMethodTime);
LogUtils.info("RPC传递header头 entryMethodTime:[{}] - callRemoteTime:[{}] = transmitTime:[{}]", entryMethodTime, callRemoteTime, transmitTime);
if (transmitTime > 0) {
retryHeader.setDdl(transmitTime);
} else {
throw new XRetryClientException("调用链超时, 不在继续调用后面请求");
}
header.put(SystemConstants.X_RETRY_HEAD_KEY, JsonUtil.toJsonString(retryHeader));
}
return header;
}
}

View File

@ -0,0 +1,32 @@
package com.x.retry.client.core.plugin;
import com.x.retry.client.core.intercepter.RetrySiteSnapshot;
import com.x.retry.common.core.constant.SystemConstants;
import java.util.List;
import java.util.Map;
/**
* @author: www.byteblogs.com
* @date : 2022-05-17 09:01
*/
public class ResponseHeaderPlugins {
private ResponseHeaderPlugins() {
}
/**
* 获取接口返回的响应头
*
* @param header 响应头
*/
public static void responseHeader(Map<String, List<String>> header) {
// 获取不重试标志
if (header.containsKey(SystemConstants.X_RETRY_STATUS_CODE_KEY)) {
List<String> statusCode = header.get(SystemConstants.X_RETRY_STATUS_CODE_KEY);
RetrySiteSnapshot.setRetryStatusCode(statusCode.get(0));
}
}
}

View File

@ -9,6 +9,9 @@ import com.x.retry.client.core.strategy.RetryMethod;
import com.x.retry.common.core.context.SpringContext;
import com.x.retry.common.core.log.LogUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
@ -21,7 +24,9 @@ import java.util.*;
* @date : 2022-03-03 16:55
*/
@Component
public class RetryableScanner implements Scanner {
public class RetryableScanner implements Scanner, ApplicationContextAware {
public ApplicationContext applicationContext;
@Override
public List<RetryerInfo> doScan() {
@ -31,9 +36,9 @@ public class RetryableScanner implements Scanner {
private List<RetryerInfo> scanRetryAbleMethod() {
List<RetryerInfo> retryerInfoList = new ArrayList<>();
String[] beanDefinitionNames = SpringContext.applicationContext.getBeanNamesForType(Object.class, false, true);
String[] beanDefinitionNames = applicationContext.getBeanNamesForType(Object.class, false, true);
for (String beanDefinitionName : beanDefinitionNames) {
Object bean = SpringContext.applicationContext.getBean(beanDefinitionName);
Object bean = applicationContext.getBean(beanDefinitionName);
Map<Method, Retryable> annotatedMethods = null;
try {
@ -87,4 +92,8 @@ public class RetryableScanner implements Scanner {
);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

View File

@ -31,12 +31,9 @@ import java.util.function.Function;
public class ReportHandler implements Lifecycle {
@Autowired
@Qualifier("jacksonSerializer")
@Qualifier("XRetryJacksonSerializer")
private RetryArgSerializer retryArgSerializer;
@Autowired
private XRetryProperties xRetryProperties;
private static SlidingWindow<RetryTaskDTO> slidingWindow;
/**

View File

@ -19,7 +19,7 @@ import java.util.Objects;
* @date : 2022-03-07 15:08
* @date 2022/1/5 11:14 上午
*/
@Component
@Component("XRetryHessianSerializer")
public class HessianSerializer implements RetryArgSerializer {
@Override

View File

@ -14,7 +14,7 @@ import java.lang.reflect.Type;
* @author: www.byteblogs.com
* @date : 2022-03-07 15:08
*/
@Component
@Component("XRetryJacksonSerializer")
public class JacksonSerializer implements RetryArgSerializer {
@Override

View File

@ -9,12 +9,12 @@ public class SystemConstants {
/**
* 请求头 key
*/
public static final String X_RETRY_HEAD_KEY = "X-RETRY";
public static final String X_RETRY_HEAD_KEY = "x-retry";
/**
* 异常重试码 key
*/
public static final String X_RETRY_STATUS_CODE_KEY = "X-RETRY-STATUS";
public static final String X_RETRY_STATUS_CODE_KEY = "x-retry-status";
/**
* 异常重试码

View File

@ -4,6 +4,8 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
@ -11,6 +13,7 @@ import org.springframework.stereotype.Component;
* @date : 2022-02-16 18:03
*/
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
@Slf4j
public class SpringContext implements ApplicationContextAware {