简介
适用于单节点,不适用分布式
限流
1、引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>25.1-jre</version>
</dependency>
2、自定义注解
/**
* 自定义注解 限流
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ServiceLimit {
/**
* 描述
*/
String description() default "";
/**
* key
*/
String key() default "";
/**
* 类型
*/
LimitType limitType() default LimitType.CUSTOMER;
enum LimitType {
/**
* 自定义key
*/
CUSTOMER,
/**
* 根据请求者IP
*/
IP
}
}
3、逻辑代码
/**
*限流AOP
*/
@Aspect
@Configuration
public class LimitAspect {
//根据IP分不同的令牌桶, 每天自动清理缓存
private static LoadingCache<String, RateLimiter> caches = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(1, TimeUnit.DAYS)
.build(new CacheLoader<String, RateLimiter>() {
@Override
public RateLimiter load(String key){
// 新的IP初始化 每秒只发出5个令牌
return RateLimiter.create(5);
}
});
//Service层切点 限流
@Pointcut("@annotation(com.itstyle.blog.common.limit.ServiceLimit)")
public void ServiceAspect() {
}
@Around("ServiceAspect()")
public Object around(ProceedingJoinPoint joinPoint) {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
ServiceLimit limitAnnotation = method.getAnnotation(ServiceLimit.class);
ServiceLimit.LimitType limitType = limitAnnotation.limitType();
String key = limitAnnotation.key();
Object obj;
try {
if(limitType.equals(ServiceLimit.LimitType.IP)){
key = IPUtils.getIpAddr();
}
RateLimiter rateLimiter = caches.get(key);
Boolean flag = rateLimiter.tryAcquire();
if(flag){
obj = joinPoint.proceed();
}else{
throw new RrException("小同志,你访问的太频繁了");
}
} catch (Throwable e) {
throw new RrException("小同志,你访问的太频繁了");
}
return obj;
}
}
缓存
1、引入依赖
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
2、逻辑代码
import com.google.common.cache.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* @Description: google guava cache工具
* @author vander
* @Date: Create in 2017/7/12 15:06
*/
public class LoadingCacheUtils {
//缓存接口这里是LoadingCache,LoadingCache在缓存项不存在时可以自动加载缓存
public static LoadingCache<String, String> cache =
//CacheBuilder的构造函数是私有的,只能通过其静态方法newBuilder()来获得CacheBuilder的实例
CacheBuilder.newBuilder()
//设置并发级别为8,并发级别是指可以同时写缓存的线程数
.concurrencyLevel(8)
//设置写缓存后30分钟过期
.expireAfterWrite(30, TimeUnit.MINUTES)
//设置缓存容器的初始容量为10
.initialCapacity(10)
//设置缓存最大容量为100,超过100之后就会按照LRU最近虽少使用算法来移除缓存项
.maximumSize(100)
//设置要统计缓存的命中率
.recordStats()
//设置缓存的移除通知
.removalListener(new RemovalListener<Object, Object>() {
public void onRemoval(RemovalNotification<Object, Object> notification) {
System.out.println(notification+"was removed, cause is "+ notification.getCause());
}
})
//build方法中可以指定CacheLoader,在缓存不存在时通过CacheLoader的实现自动加载缓存
.build(new CacheLoader<String,String>() {
public String load(String str) throws Exception {
return str;
};
}
);
/**
* ImmutableMap<K, V> getAllPresent(Iterable<?> keys) 一次获得多个键的缓存值
put和putAll方法向缓存中添加一个或者多个缓存项
invalidate 和 invalidateAll方法从缓存中移除缓存项
asMap()方法获得缓存数据的ConcurrentMap<K, V>快照
cleanUp()清空缓存
refresh(Key) 刷新缓存,即重新取缓存数据,更新缓存
* @param args
* @throws ExecutionException
*/
public static void main(String[] args) throws ExecutionException {
// System.out.println(LoadingCacheUtils.cache.get("key"));
// cache.invalidateAll();
// cache.put("key","val");
String val = cache.get("key");
System.out.println(val);
System.out.println(cache.stats().toString());//命中率等 情况
}
}