基于Caffeine的缓存服务发布

资料索引

github:Caffeine在github中的地址
wiki:中文维基
nccdev:NCC开发者社区

整体介绍

主要特性

  • 基于NC原JVM的缓存服务发布:无需外挂中间件;
  • 支持惰性缓存淘汰:即失效缓存不会主动淘汰,而是在下次访问时才淘汰;
  • 支持分布式缓存服务访问:was环境下,通过指定缓存服务器,增加缓存命中率;
  • 预置JDBC使用方法:以JDBC的形式使用,查询前先查看缓存;
  • 预置自定义使用方法:以类Map的形式使用,自主决定如何获取/设置缓存;
  • 预置统计API:调用接口,获取缓存统计信息;

类图

caffeine缓存功能类图

功能实现

jar包依赖

点此下载
或使用maven下载

    <dependencies>
        <!--for caffeine cache-->
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>${caffeine.cache.version}</version>
        </dependency>
    </dependencies>

将下载下来的jar包放到以下目录
home\modules\自己的客开模块名\lib

缓存服务代码编写

CaffeineBO代码如下

package nc.itf.ge_ext.utils.caffeine;

import java.util.concurrent.TimeUnit;

import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import nc.bs.logging.Logger;
import nc.util.ge_ext.CommonUtil;
import nc.vo.pub.BusinessException;


/**
 * CaffeineUtil:基于Caffeine实现的缓存加载工具
 * <b>禁止直接访问!</b>如需访问请调用CaffeineUtil!
 * @author	CYQ
 * @date	2024-1-5 16:00:39
 * @version	1.0.0
*/ 
public class CaffeineBO {
	private static CaffeineBO caffeineutil = new CaffeineBO();
	private CaffeineBO() {}
	//通过该方法获得实例对象
	public static CaffeineBO getCaffeineUtil(){
		return caffeineutil;
	}	
	// cache:TODO 缓存实体	
	private static Cache<String, Object> cache = Caffeine.newBuilder()
            .maximumSize(10_000)//设置内存最大数量,10000
            .expireAfterAccess(2, TimeUnit.HOURS)//设置时间对象没有被读/写访问则对象从内存中删除,2小时过期
            .recordStats()//开启Guava Cache的统计功能
            .build();
	/**
	 * getCache:	获取缓存
	 * 创  建  人 :CYQ
	 * 创建时间:2024-1-5-16:10:55
	*/
	@Deprecated
	public static Object getCache(String key){
        return Cache(key);
    }
    /**
     * putCache:	设置缓存
     * @param graph	void	TODO(参数说明)
	     * 创  建  人 :CYQ
	     * 创建时间:2024-1-5-16:11:01
    */
	@Deprecated
    public static void putCache(String key,Object graph) {
    	if(key==null||key.isEmpty()) {
    		return;
    	}if(graph==null) {
    		return;
    	}
    	Logger.error("未加载到缓存...开始创建...");
    	cache.put(key, graph);
    }
    private static Object Cache(String key){
        // 查找一个缓存元素, 没有查找到的时候返回null
        Object graph = cache.getIfPresent(key);
        //memo by CYQ 2024年1月8日 这个缓存获取方法相当优雅,但为了提高缓存泛用性,决定不使用此方法
        // 查找缓存,如果缓存不存在则生成缓存元素,  如果无法生成则返回null
//        String graph = cache.get(key, k -> createObject(key));
        return graph;
    }
    /**
     * getCacheEentity:	获取缓存对象,用于缓存统计
     * @return	Cache<String,Object>	TODO(参数说明)
     * 创  建  人 :CYQ
     * 创建时间:2024-1-8-14:39:11
    */
    public static String cacheStats(String type) throws BusinessException {
    	CommonUtil util = CommonUtil.getCommonUtil();
    	if("cache_hitRate".equals(type)) {//命中率
			return util.initstr(cache.stats().hitRate());
		}else if("cache_evictionCount".equals(type)) {//被剔除的条目数量
			return util.initstr(cache.stats().evictionCount());
		}else if("cache_averageLoadPenalty".equals(type)) {//加载新值所花费的平均时间
			return util.initstr(cache.stats().averageLoadPenalty());
		}else if("cache_size".equals(type)) {//缓存数
			return util.initstr(cache.asMap().size());
		}else {
			throw new BusinessException("【"+type+"】不是有效的指令");
		}
    }
}

注册upm文件,并将其放到此目录下
home\modules\客开模块\META-INF

<?xml version="1.0" encoding='gb2312'?>
<module name="ge_ext">
	<public>
		<!--客开缓存服务接口-->
		<component priority="0" singleton="false" remote="true" tx="CMT" supportAlias="true">
			<interface>nc.itf.ge_ext.ICaffeine</interface>
			<implementation>nc.impl.ge_ext.CaffeineImpl</implementation>
		</component>
	</public>
</module>

配置文件
需注意,下文中的
CommonUtil.getParameter(UtilConstants.PROPERTIES_KEY.ISCACHE);
是在获取配置文件,获取配置文件的方法自行编写即可,如果不想要配置,代码写死也行
涉及的所有配置文件如下

#是否启用缓存,仅为Y时启用缓存
ISCACHE=Y
#缓存服务器名称,没有此参数时默认缓存服务器为主服务器
CACHESERVER=server

ICaffeine代码如下

package nc.itf.ge_ext;

import nc.vo.pub.BusinessException;

/**
 * ICaffeine:缓存服务访问接口,<b>禁止直接访问!</b>如需访问请调用CaffeineUtil!
 * @author	CYQ
 * @date	2024-1-8 14:14:55
 * @version	1.0.0
*/ 
public interface ICaffeine {

	/**
	 * getCache:获取缓存
	 * 创  建  人 :CYQ
	 * 创建时间:2024-1-8-14:06:34
	*/
	@Deprecated
	public Object getCache(String key);
	
	/**
	 * putCache:	设置缓存
	 * 创  建  人 :CYQ
	 * 创建时间:2024-1-8-14:06:47
	*/
	@Deprecated
	public void putCache(String key,Object graph);
	
	/**
	 * cacheStats:	调用缓存统计API
	 * 创  建  人 :CYQ
	 * 创建时间:2024-1-8-14:36:25
	*/
	@Deprecated
	public String cacheStats(String type) throws BusinessException ;
}

CaffeineImpl代码如下

package nc.impl.ge_ext;

import nc.itf.ge_ext.ICaffeine;
import nc.itf.ge_ext.utils.caffeine.CaffeineBO;
import nc.vo.pub.BusinessException;

public class CaffeineImpl implements ICaffeine {
	
	@Override
	public Object getCache(String key) {
		// TODO Auto-generated method stub
		return CaffeineBO.getCache(key);
	}

	@Override
	public void putCache(String key, Object graph) {
		// TODO Auto-generated method stub
		CaffeineBO.putCache(key, graph);
	}

	@Override
	public String cacheStats(String type) throws BusinessException {
		// TODO Auto-generated method stub
		return CaffeineBO.cacheStats(type);
	}
}

CaffeineUtil代码如下

package nc.util.ge_ext;

import java.util.Properties;

import nc.bs.framework.common.NCLocator;
import nc.bs.framework.server.ServerConfiguration;
import nc.bs.logging.Logger;
import nc.itf.ge_ext.ICaffeine;
import nc.vo.pub.BusinessException;

/**
 * CaffeineUtil:缓存服务获取工具,增加此工具的意义在于,在was场景下固定缓存服务器
 * @author	CYQ
 * @date	2024-1-8 14:17:13
 * @version	1.0.0
*/ 
public class CaffeineUtil {
	
	/**
	 * getCache:	获取缓存
	 *</br> 创  建  人 :CYQ
	 *</br> 创建时间:2024-1-8-14:19:32
	*/
	public static Object getCache(String key) {
		// TODO Auto-generated method stub

		String iscache = "";
		try {
			iscache = CommonUtil.getParameter(UtilConstants.PROPERTIES_KEY.ISCACHE);
		} catch (BusinessException e) {
			Logger.error(e.getMessage());
		}
		if(!"Y".equals(iscache)) return null;
		
		return NCLocator.getInstance(getProps()).lookup(ICaffeine.class).getCache(key);
	}

	/**
	 * putCache:	设置缓存
	 *</br> 创建人 :CYQ	
	 *</br> 创建时间:2024-1-8-14:19:37
	*/
	public static void putCache(String key, Object graph) {
		// TODO Auto-generated method stub
		String iscache = "";
		try {
			iscache = CommonUtil.getParameter(UtilConstants.PROPERTIES_KEY.ISCACHE);
		} catch (BusinessException e) {
			Logger.error(e.getMessage());
		}
		if(!"Y".equals(iscache)) return;
		NCLocator.getInstance(getProps()).lookup(ICaffeine.class).putCache(key, graph);
	}
	
	/**
	 * cacheStats:缓存统计
	 *</br> 创建人 :CYQ	
	 *</br> 创建时间:2024-1-8-14:19:37
	 * @throws BusinessException 
	*/
	public static String cacheStats(String type) throws BusinessException {
		// TODO Auto-generated method stub
		return NCLocator.getInstance(getProps()).lookup(ICaffeine.class).cacheStats(type);
	}
	
	/**
	 * 获取特定节点的props,若没有单独配置,则获取主节点缓存
	 * 创  建  人 :CYQ
	 * 创建时间:2023年4月11日-下午2:40:56
	*/
	private static Properties getProps() {
		Properties props = new Properties();
		ServerConfiguration sc = ServerConfiguration.getServerConfiguration();
		String serverName = "";
		try {
			//优先获取配置的缓存服务器,无配置时使用主服务器缓存
			serverName = CommonUtil.getParameter(UtilConstants.PROPERTIES_KEY.CACHESERVER);
		} catch (BusinessException e) {
			// TODO Auto-generated catch block
			Logger.error(e.getMessage());
		}
		if(serverName==null || serverName.isEmpty()) {
			serverName = sc.getMasterServerName();//集群环境主节点名称
		}
		String serverURL = sc.getServerEndpointURL( serverName );//对应节点名称
		props.setProperty(NCLocator.SERVICEDISPATCH_URL,serverURL );

		return props;
	}

}

到此,缓存服务代码就已经编写完毕,下面我们开始编写调用缓存服务的案例

缓存使用代码编写

场景1:jdbc缓存过滤
对jdbc查询增加缓存,以sql作为hash的key,查询结果作为hash的value,当两个小时以内遇到同样的sql时,则直接返回缓存
需注意,当认为同一条sql的返回结果会反复改变时,请不要使用此工具
例如:在sql中增加了动态函数,如时间获取函数等;或查询数据库中一个需要被反复修改的值;

	/**
	 * 单个查询条件数据库查询操作
	 */
	public static String queryColumn(String tablename, String showname,
			String key, String value) throws BusinessException {
		String sql = "select " + showname + " from " + tablename
				+ "  where isnull(dr,0) = 0 and " + key + " = '" + value + "'";
		
		//add by CYQ 2024年1月5日 增加缓存处理
		Object result = CaffeineUtil.getCache(sql);
		if(result==null) {
			result = iuap.executeQuery(sql, new ColumnProcessor());
			CaffeineUtil.putCache(sql, result);
		}
		return result == null ? "" : result.toString();
	}
		/**
	 * queryDBMap:	查询Map结果集
	 * 	创  建  人 :CYQ
	 * 	创建时间:2021-1-15-下午4:24:36
	*/
	public static List<Map<String, Object>> queryDBMap(String sql)
			throws BusinessException {
		//add by CYQ 2024年1月5日 增加缓存处理
		Object result = CaffeineUtil.getCache(sql.toString());
		if(result==null) {
			result = iuap.executeQuery(sql.toString(), new MapListProcessor());
			CaffeineUtil.putCache(sql.toString(), result);
		}
		List<Map<String, Object>> listmap = (List<Map<String, Object>>)result;
		if (listmap != null && listmap.size() != 0) {
			return listmap;
		}
		return new ArrayList<Map<String, Object>>();
	}

场景2:缓存统计API服务
由于每个项目使用的API框架不相同,案例项目里是用了一套自己封装的客开API发布框架,这里就不放完整代码了

//add by CYQ 2024年1月5日 增加缓存统计API
		if("cache_hitRate".equals(type)) {//命中率
			return util.initstr(CaffeineUtil.cacheStats(type));
		}else if("cache_evictionCount".equals(type)) {//被剔除的条目数量
			return util.initstr(CaffeineUtil.cacheStats(type));
		}else if("cache_averageLoadPenalty".equals(type)) {//加载新值所花费的平均时间
			return util.initstr(CaffeineUtil.cacheStats(type));
		}else if("cache_size".equals(type)) {//缓存数
			return util.initstr(CaffeineUtil.cacheStats(type));
		}else if("cache_get".equals(type)) {
			String key = util.initstr(datas.get("key"));
			ret = CaffeineUtil.getCache(key);
		}else if("cache_put".equals(type)) {
			String key = util.initstr(datas.get("key"));
			String value = util.initstr(datas.get("value"));
			CaffeineUtil.putCache(key,value);
		}
指令编码描述备注
help查看帮助
cache_hitRate命中率
cache_evictionCount被剔除的条目数量
cache_averageLoadPenalty加载新值所花费的平均时间
cache_size缓存数示例:{“code”:“RE_SYS_TOOL”,“data”:{“type”:“输入需要的指令编码”}}
cache_get查询缓存
cache_put设置缓存

测试截图

  • 20
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值