在springcloud微服务架构中,可能会经常通过 feign 组件调用其它的微服务,feign的底层其实是模拟一个http请求,通过访问接口的方式调用远程服务,要经历三次握手建立TCP连接,在项目中是一个比较“耗时”的操作。
如果经常请求一些很少变动的数据,或者在一定时间段内可容忍已过期的数据,那么则需要在调用feign之前能不能从缓存中获取,可以自定义注解,将feign返回的结果缓存到redis中一段时间。
1 - 定义一个注解标记需要缓存的方法项目中需要使用:redis,aspect
package cn.wework.backend.aspect;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FeignCache {
long expiresIn() default -1;
}
2 - 定义切面
package cn.wework.backend.aspect;
import cn.hutool.extra.spring.SpringUtil;
import cn.wework.backend.common.constant.GlobalConstant;
import cn.wework.backend.util.RedisUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Set;
@Aspect
@Component
@Slf4j
public class FeignClientAspect implements InitializingBean {
@Autowired
private RedisUtil redisUtil;
private static final String FEIGN_CLIENT_CACHE_PREFIX = "FEIGN-CLIENT-CACHE:";
@Around("@annotation(cn.wework.backend.aspect.FeignCache)")
public Object cacheAround(ProceedingJoinPoint joinPoint) throws Throwable {
String key = FEIGN_CLIENT_CACHE_PREFIX + joinPoint.getSignature().getDeclaringTypeName() + "#" + joinPoint.getSignature().getName() + "#" + SimpleKeyGenerator.generateKey(joinPoint.getArgs());
Object cache = redisUtil.get(key);
if (cache != null) {
return cache;
}
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
FeignCache annotation = signature.getMethod().getAnnotation(FeignCache.class);
// 这里可以对结果进行过滤,缓存正确的结果,视具体的业务逻辑来定
cache = joinPoint.proceed(joinPoint.getArgs());
redisUtil.set(key, cache, annotation.expiresIn());
return cache;
}
@Override
public void afterPropertiesSet() {
RedisTemplate redisTemplate = redisUtil.getRedisTemplate();
Set keys = redisTemplate.keys(FEIGN_CLIENT_CACHE_PREFIX + "*");
if (keys != null) {
Long count = redisTemplate.delete(keys);
log.info("feign cache has bean deleted, count = {}", count);
}
}
}
3 - redis操作工具类
package cn.wework.backend.util;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class RedisUtil {
private RedisTemplate redisTemplate;
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public RedisTemplate getRedisTemplate() {
return redisTemplate;
}
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
// ============================String=============================
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public Map
三、使用
以上工作做完之后,使用起来就非常方便了,直接在需要缓存的方法上使用注解即可
@GetMapping("/api/v1/city/{identity}")
@FeignCache(expiresIn = 2 * 60 * 60)
Response getCity(@PathVariable(value = "identity") String identity);



