guawa 之本地缓存的运用

    业务场景是这样的,我要给前端提供一个接口,这个接口是从数据库查出数据根据业务逻辑拼装数据返回。这样是可以完成的,但是这个接口调用量比较大,于是就加了一个redis,把数据存到缓存中提高吞吐量。但是在系统压测的时候发现,没分钟访问量在3W左右总是上不去。后来查了资料才发现,redis有热点数据,因为我存redis中的数据就有5个key,这些key都是固定的,redis的存储机制是相同的key都会落到相同的分片,redis的每个分片就支持3W左右的吞吐量。

     那么问题来了,怎样继续把吞吐量搞上去呢,于是就用到了本地缓存 guawa。下面先定义一个本地缓存的接口,然后再加实现类。guawa的get方法是先从本地缓存里面取,如果拿不到就去load方法里面取。想要深入了解可以看一下里面的源码。

    在做本地缓存的时候有一个坑,就是spring和spring mvc初始化扫包的时候注意是不是只扫了一遍service,如果扫了两遍会初始化两个本地缓存对象,由于缓存不是static的类,会导致别人调用你的接口时拿不到最新的,而你本地跑的时候可以拿到最新的。

     

public interface CacheRpc {

    void set(String key, String value) throws Exception;

    /**
     * @param key
     * @param value
     * @param timeout
     *            单位毫秒
     */
    void set(String key, String value, long timeout) throws Exception;

    String get(String key) throws Exception;

    void delete(String key) throws Exception;

}
 

    

public class R2MCacheRpcImpl implements CacheRpc {

    @Resource(name = "cacheClusterClient")
   private CacheClusterClient cacheClusterClient;

   private static final Logger logger = LogManager.getLogger(R2MCacheRpcImpl.class);

   private ListeningExecutorService executor = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(1));

   //添加本地缓存,优先查询
   private final LoadingCache cache = CacheBuilder.newBuilder()
         .expireAfterWrite(5, TimeUnit.MINUTES)
         .refreshAfterWrite(1, TimeUnit.MINUTES)
         .build(new CacheLoader() {
            @Override
            public String load(String accountsId) {
               logger.debug("重新加载本地缓存");
               return cacheClusterClient.get(accountsId) == null ? "":cacheClusterClient.get(accountsId);
            }
            @Override
            public ListenableFuture reload(final String accountsId, String oldValue) throws Exception {
               return executor.submit(new Callable() {
                  @Override
                  public String call() throws Exception {
                     return cacheClusterClient.get(accountsId);
                  }
               });
            }
         });


   @Override
   public void set(String key, String value) throws Exception {
      try {
         cacheClusterClient.set(key, value);
         cache.put(key,value);
      } catch (Exception e) {
         logger.error("r2m set error,key=" + key + ",value=" + value, e);
         throw e;
      }
   }

   @Override
   public void set(String key, String value, long timeout) throws Exception {
      try {
         cacheClusterClient.psetex(key, timeout, value);
         cache.put(key,value);
      } catch (Exception e) {
         logger.error("r2m psetex error,key=" + key + ",value=" + value, e);
         throw e;
      }
   }

   @Override
   public String get(String key) throws Exception {
      try {
         //优先从本地缓存取活动数据
         return cache.getUnchecked(key);
      } catch (Exception e) {
         logger.error("r2m get error,key=" + key, e);
         return null;
      }
   }

 
@Override
public void delete(String key) throws Exception {
   try {
      cache.invalidateAll();//清除所有key
      //cache.refresh(key);//清除当前key
   } catch (Exception e) {
      logger.error("r2m del error,key=" + key, e);
      throw e;
   }
}
 

}

运行逻辑:spring初始化的时候会初始化一个本地缓存对象,调用本地缓存做set操作的时候,guawa会把数据放到本地缓存里面,同时放置两个时间,一个是过期时间,一个是刷新时间。用户在调本地缓存get的时候先看数据有没有过期,如果没有过期,就会取本地缓存里的,如果过期的话,调用load方法取数据放到本地缓存里面。 如果过期了,请求来的时候会触发reload方法,先返回旧数据,然后开启一个线程去取数据然后放到本地缓存。

你可能感兴趣的:(本地缓存)