笔者场景:
跨系统调用接口(A系统调用B),B系统的token有效时间7天,调用接口响应较慢(1s)。
分析:
A系统的调用会引发两个操作:
1. 需要携带用户信息登录B系统(因为B系统有身份鉴权,否则可以忽略)
2. 调用B系统接口/test
其中,A系统的获得B系统返回结果的耗时影响较大。–> 想要缩短响应时间,使用redis存储。( 200ms+ )
原理说明:
- 使用拦截器对该接口进行拦截:实现RequestInterceptor并重写apply(RequestTemplate requestTemplate)方法
- 如果接口为/test,则继续下一步操作
- 用户从redis获取存储的token:redis.get(key)
- 如果token不为空,那么携带这个token去调用B接口/test;否则,需要重新登录,并将token存入redis,然后携带这个token去调用B接口
- 根据B接口/test的响应结果进行分析
如果响应代码为401,说明token已过期或失效,从redis删除token,并重新调用B系统的/test接口(此时会重新执行步骤1);否则B接口/test返回成功
代码说明:
请求拦截类
@Slf4j
@Configuration
/* 1. 使用拦截器对该接口进行拦截:实现RequestInterceptor并重写apply(RequestTemplate requestTemplate)方法 */
public class FeignRequestIntercepter implements RequestInterceptor{
@Autowired
private TestService testService;
@Autowired
private RedisService redisService;
@Override
public void apply(RequestTemplate requestTemplate) {
// 添加Bearer头
HttpServletRequest request = RequestContextHolder.getRequestAttributes() != null ? ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest() : null;
if (request != null) {
if (!requestTemplate.url().equals("/test")) {
requestTemplate.header("Authorization", request.getHeader(HttpHeaders.AUTHORIZATION));
} else {
/* 2. 如果接口为/test,则继续下一步操作
接口/test需要登录B系统,此处接的是B系统的token */
String userEmail = ApiAspect.getCurrentUser();
/* 3. 用户从redis获取存储的token:redis.get(key) */
String testAccessTokenCache = redisService.get(REDIS_KEY_PREFIX_TEST + userEmail);
/* 4. 如果token不为空,那么携带这个token去调用B接口/test;否则,需要重新登录,并将token存入redis,然后携带这个token去调用B接口 */
if(StringUtils.isNotBlank(testAccessTokenCache)){
requestTemplate.header("Authorization", "Bearer " + testAccessTokenCache);
}else{
String testToken = testService.getAccessToken();
redisService.setEx(REDIS_KEY_PREFIX_TEST + userEmail, testToken, 6, TimeUnit.DAYS);
requestTemplate.header("Authorization", "Bearer " + testService.getAccessToken());
}
}
}
}
}
实现类
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class TestServiceImpl implements TestService {
@Override
public JSONObject getTestData(Long testId, String userEmail) {
Response response = null;
try {
response = testFeignClient.test(testId);
/* 5. 根据B接口/test的响应结果进行分析
如果响应代码为401,说明token已过期或失效,从redis删除token,并重新调用B系统的/test接口(此时会重新执行步骤1);否则B接口/test返回成功 */
if(response.getCode().equals(CODE_ERROR_AUTH)){
redisService.delete(REDIS_KEY_PREFIX_TSET + userEmail);
response = testFeignClient.test(TestId);
}
log.info("test返回信息 {}",response);
} catch (Exception e){
log.error("数据返回失败error {}", e);
}
JSONObject data = new JSONObject();
if(response != null) {
if(response.getData() instanceof JSONObject) {
data = (JSONObject) response.getData();
}else {
LinkedHashMap dataMap = (LinkedHashMap) response.getData();
data = JSONObject.parseObject(JSONObject.toJSONString(dataMap));
}
}
return data;
}
如果不知道testFeignClient.test(testId)中feign的配置,请跳转另一篇博文:feign配置