总结:解决网站的高访问量,有三大利器,缓存,降级,限流,我这里讲解一下 常用的几种限流手段。
单机 ,有型号量,令牌通,漏桶,
分布式,可以考虑从网关,redis ngnix, 阿里 Sentinel 等手段解决 。话不多说代码如下:
1.单机:
1.1 型号量
// 线程池
ExecutorService exec = Executors.newCachedThreadPool();
// 只能5个线程同时访问
final Semaphore semp = new Semaphore(5);
@Override
public void limit() {
// 模拟20个客户端访问
for (int index = 0; index < 20; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 获取许可 一直阻塞
semp.acquire();
// semp.tryAcquire(1, TimeUnit.MINUTES); //这个尝试 多长时间获取 到
//处理业务
log.info("Accessing: " + NO);
Thread.sleep((long) (Math.random() * 10000));
log.info("存在多少有效" + semp.availablePermits());
} catch (InterruptedException e) {
log.info("error:{}",e);
} finally {
// 访问完后,释放
semp.release();
}
}
};
exec.execute(run);
}
// 退出线程池
//exec.shutdown();
}
1.2 guava
ExecutorService exec = Executors.newCachedThreadPool();// 线程池
final RateLimiter limit = RateLimiter.create(2);//1s只能有10个
@Override
public void limit() {
// 模拟20个客户端访问
for (int index = 0; index < 20; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 获取许可
limit.acquire();
//处理业务
log.info("Accessing: " + NO);
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
log.info("error:{}", e);
}
}
};
exec.submit(run);
}
}
2.0 分布式版本
2.1 redis-spring-data
@Autowired
private RedisTemplate redisTemplate;
ExecutorService exec = Executors.newCachedThreadPool();// 线程池
@Override
public void limit() {
//这里我用 包名+类名+方法
String key = "com.example.demo.limit.redis.ResdisLimitServiceImpl.limit";
RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
for (int i = 0; i < 100; i++) {
final int no = i;
exec.submit(new Runnable() {
@Override
public void run() {
if (counter.incrementAndGet() > 10) {
//拒绝请求
log.info("拒绝请求{}", no);
}
try {
//处理请求
log.info("处理请求{}", no);
} finally {
counter.decrementAndGet();
}
try {
Thread.sleep((long) (Math.random() * 10000));
} catch (InterruptedException e) {
}
}
});
}
}
2.2 redission 版本
@Autowired
private RedissonClient redisson;
ExecutorService exec = Executors.newCachedThreadPool();// 线程池
@Override
public void limit() {
RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.trySetPermits(10);
log.info("有效:{}",semaphore.availablePermits());
for (int i = 0; i <100 ; i++) {
final int no = i;
exec.submit(new Runnable() {
@Override
public void run() {
try {
if(semaphore.tryAcquire(1, TimeUnit.SECONDS)){
try {
log.info("处理业务{}",no);
Thread.sleep((long) (Math.random() * 10000));
}finally {
semaphore.release(1);
}
}else {
log.info("拒绝业务{}",no);
}
} catch (InterruptedException e) {
log.error("error:{}",e);
}
}
});
}
}
3.0 基于注解 redis开发 限流 使用方便
使用如下:
@Limit(key = "test", period = 100, count = 10)
@PostMapping("dept/update")
public ResponseResult updateDept(String name, Boolean leader, Integer id
, Double age, BigDecimal price, ResponseResultCode em){
return ResponseResultFactory.success("添加成功");
}
jmeter 测试如下 具体实现 参考我的代码库