0.前言
在出统计数据时,一个页面有10多个图表同时并发向同一个接口发起请求,返回不同的数据。如果不做任何处理,想象一下如果10个人同时访问页面那就是100次的数据库访问,这将会导致接口响应异常的慢。加上特殊的情况,数据库并发连接数只支持50次,也就是所如果连接数超过50个需要排队等待。在这样的情况下,做了如下几步处理来缓解这样的访问压力。
1.使用信号量进行访问控流
1.1什么是信号量
一个资源有多个副本可供同时使用,比如打印机房有多个打印机、厕所有多个坑可供同时使用,这种情况下,Java 提供了另外的并发访问控制 – 资源的多副本的并发访问控制,信号量 Semaphore 即是其中的一种。
1.2 Semaphore原理
Semaphore 是用来保护一个或者多个共享资源的访问,Semaphore 内部维护了一个计数器,其值为可以访问的共享资源的个数。一个线程要访问共享资源,先获得信号量,如果信号量的计数器值大于 1,意味着有共享资源可以访问,则使其计数器值减去 1,再访问共享资源。如果计数器值为 0, 线程进入休眠。当某个线程使用完共享资源后,释放信号量,并将信号量内部的计数器加 1,之前进入休眠的线程将被唤醒并再次试图获得信号量。
1.3 Semaphore的使用
Semaphore semaphore = new Semaphore(10,true);
semaphore.acquire();
//do something here
semaphore.release();
2.使用本地缓存和Redis缓存
先访问本地缓存有值直接返回,没有访问Redis缓存,读到把值放进本地缓存返回。
线程池的创建看文章:线程池的应用~~
private Cache<String, SoftReference<Object>> localCache = CacheBuilder.newBuilder()
//设置cache的初始大小为10,要合理设置该值
.initialCapacity(50)
//设置并发数为5,即同一时间最多只能有5个线程往cache执行写入操作
.concurrencyLevel(5)
//设置cache中的数据在写入之后的存活时间为3
.expireAfterWrite(60, TimeUnit.SECONDS)
//构建cache实例
.build();
/**
* 获取本地缓存
*/
private Object getCache(String longKey) {
Object o = null;
// 返回与此缓存中的key关联的值,如果key没有缓存值,则返回null 。
SoftReference<Object> objectSoftReference = localCache.getIfPresent(longKey);
if(objectSoftReference != null && objectSoftReference.get() != null){
o = objectSoftReference.get();
if(o == null){
o = toolRedis.get(longKey);
if(o != null) {
localCache.put(longKey, new SoftReference<>(o));
}
}else {
//异步写日志,这里使用了线程池,executorService使用Bean对象引入
executorService.submit(() -> log.info("命中本地缓存,当前本地缓存个数:{},缓存key:{}",
localCache.size(),localCache.asMap().keySet()));
}
}else{
o = toolRedis.get(longKey);
if(o != null) {
localCache.put(longKey, new SoftReference<>(o));
}
}
return o;
}
3.使用数据库访问
这是数据的出处,如果调用getCache(cacheKey)方法返回为空则去访问数据库。
文文的博客~