资料索引
整体介绍
主要特性
- 基于NC原JVM的缓存服务发布:无需外挂中间件;
- 支持惰性缓存淘汰:即失效缓存不会主动淘汰,而是在下次访问时才淘汰;
- 支持分布式缓存服务访问:was环境下,通过指定缓存服务器,增加缓存命中率;
- 预置JDBC使用方法:以JDBC的形式使用,查询前先查看缓存;
- 预置自定义使用方法:以类Map的形式使用,自主决定如何获取/设置缓存;
- 预置统计API:调用接口,获取缓存统计信息;
类图
功能实现
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 | 设置缓存 |