總結:解決網站的高訪問量,有三大利器,緩存,降級,限流,我這里講解一下 常用的幾種限流手段。
單機 ,有型號量,令牌通,漏桶,
分布式,可以考慮從網關,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 測試如下 具體實現 參考我的代碼庫
