最新要闻
- 每日短讯:12.4万买新帕纳梅拉!近600名国内网友保时捷官网疯抢:成功下单后被取消
- 【全球时快讯】奔驰获全球首家L3级自动驾驶认证:开车不用看路 出事故奔驰负责
- 【快播报】优酷回应1元会员被扣24元争议:活动规则已告知 扣钱没毛病
- 当前热文:水墨风场景惊艳!《仙剑奇侠传7》DLC《人间如梦》官宣2月发售
- 彻底扑灭一台特斯拉Model S有多难 消防员实测:用了22.7吨水
- 【环球播资讯】小鹏股价暴跌、交付量惨淡 何小鹏专访回应:未来会这么做
- 全球动态:全球首个!婴幼儿视功能损伤手机智能筛查系统面世
- 撸猫手感 绿联iPhone 12-14系列液态硅胶保护壳9.9元起
- 天天快看点丨海淘不香了!日版Xbox主机涨价将近260元
- 天天速递!全国首烧?疑似红旗E-HS9充电时起火 现场黑烟弥漫
- 瑾娘为什么要杀华裳?瑾娘为什么假扮巽芳?
- 爱在旅途大结局是什么?爱在旅途剧情介绍
- 法国属于西欧还是北欧?南欧包括哪些国家?
- 荷兰为什么被称为水之国?荷兰水之国的资料简介
- 长宽高的英文缩写分别是什么?长宽高怎么算平方?
- 环球速讯:工信部明天起优化调整微波频率 为5G/6G预留频谱资源
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
热推荐:基于Spring Cache实现Caffeine、jimDB多级缓存实战
(资料图)
作者: 京东零售 王震
背景
在早期参与涅槃氛围标签中台项目中,前台要求接口性能999要求50ms以下,通过设计Caffeine、ehcache堆外缓存、jimDB三级缓存,利用内存、堆外、jimDB缓存不同的特性提升接口性能,内存缓存采用Caffeine缓存,利用W-TinyLFU算法获得更高的内存命中率;同时利用堆外缓存降低内存缓存大小,减少GC频率,同时也减少了网络IO带来的性能消耗;利用JimDB提升接口高可用、高并发;后期通过压测及性能调优999性能<20ms
当时由于项目工期紧张,三级缓存实现较为臃肿、业务侵入性强、可读性差,在近期场景化推荐项目中,为B端商家场景化资源投放推荐,考虑到B端流量相对C端流量较小,但需保证接口性能稳定。采用SpringCache实现caffeine、jimDB多级缓存方案,实现了低侵入性、可扩展、高可用的缓存方案,极大提升了系统稳定性,保证接口性能小于100ms;
Spring Cache实现多级缓存
多级缓存实例MultilevelCache
/** * 分级缓存 * 基于Caffeine + jimDB 实现二级缓存 * @author wangzhen520 * @date 2022/12/9 */public class MultilevelCache extends AbstractValueAdaptingCache { /** * 缓存名称 */ private String name; /** * 是否开启一级缓存 */ private boolean enableFirstCache = true; /** * 一级缓存 */ private Cache firstCache; /** * 二级缓存 */ private Cache secondCache; @Override protected Object lookup(Object key) { Object value; recordCount(getUmpKey(this.getName(), UMP_GET_CACHE, UMP_ALL)); if(enableFirstCache){ //查询一级缓存 value = getWrapperValue(getForFirstCache(key)); log.info("{}#lookup getForFirstCache key={} value={}", this.getClass().getSimpleName(), key, value); if(value != null){ return value; } } value = getWrapperValue(getForSecondCache(key)); log.info("{}#lookup getForSecondCache key={} value={}", this.getClass().getSimpleName(), key, value); //二级缓存不为空,则更新一级缓存 boolean putFirstCache = (Objects.nonNull(value) || isAllowNullValues()) && enableFirstCache; if(putFirstCache){ recordCount(getUmpKey(this.getName(), UMP_FIRST_CACHE, UMP_NO_HIT)); log.info("{}#lookup put firstCache key={} value={}", this.getClass().getSimpleName(), key, value); firstCache.put(key, value); } return value; } @Override public void put(Object key, Object value) { if(enableFirstCache){ checkFirstCache(); firstCache.put(key, value); } secondCache.put(key, value); } /** * 查询一级缓存 * @param key * @return */ private ValueWrapper getForFirstCache(Object key){ checkFirstCache(); ValueWrapper valueWrapper = firstCache.get(key); if(valueWrapper == null || Objects.isNull(valueWrapper.get())){ recordCount(getUmpKey(this.getName(), UMP_FIRST_CACHE, UMP_NO_HIT)); } return valueWrapper; } /** * 查询二级缓存 * @param key * @return */ private ValueWrapper getForSecondCache(Object key){ ValueWrapper valueWrapper = secondCache.get(key); if(valueWrapper == null || Objects.isNull(valueWrapper.get())){ recordCount(getUmpKey(this.getName(), UMP_SECOND_CACHE, UMP_NO_HIT)); } return valueWrapper; } private Object getWrapperValue(ValueWrapper valueWrapper){ return Optional.ofNullable(valueWrapper).map(ValueWrapper::get).orElse(null); }}
多级缓存管理器抽象
/** * 多级缓存实现抽象类 * 一级缓存 * @see AbstractMultilevelCacheManager#getFirstCache(String) * 二级缓存 * @see AbstractMultilevelCacheManager#getSecondCache(String) * @author wangzhen520 * @date 2022/12/9 */public abstract class AbstractMultilevelCacheManager implements CacheManager { private final ConcurrentMap cacheMap = new ConcurrentHashMap<>(16); /** * 是否动态生成 * @see MultilevelCache */ protected boolean dynamic = true; /** * 默认开启一级缓存 */ protected boolean enableFirstCache = true; /** * 是否允许空值 */ protected boolean allowNullValues = true; /** * ump监控前缀 不设置不开启监控 */ private String umpKeyPrefix; protected MultilevelCache createMultilevelCache(String name) { Assert.hasLength(name, "createMultilevelCache name is not null"); MultilevelCache multilevelCache = new MultilevelCache(allowNullValues); multilevelCache.setName(name); multilevelCache.setUmpKeyPrefix(this.umpKeyPrefix); multilevelCache.setEnableFirstCache(this.enableFirstCache); multilevelCache.setFirstCache(getFirstCache(name)); multilevelCache.setSecondCache(getSecondCache(name)); return multilevelCache; } @Override public Cache getCache(String name) { MultilevelCache cache = this.cacheMap.get(name); if (cache == null && dynamic) { synchronized (this.cacheMap) { cache = this.cacheMap.get(name); if (cache == null) { cache = createMultilevelCache(name); this.cacheMap.put(name, cache); } return cache; } } return cache; } @Override public Collection getCacheNames() { return Collections.unmodifiableSet(this.cacheMap.keySet()); } /** * 一级缓存 * @param name * @return */ protected abstract Cache getFirstCache(String name); /** * 二级缓存 * @param name * @return */ protected abstract Cache getSecondCache(String name); public boolean isDynamic() { return dynamic; } public void setDynamic(boolean dynamic) { this.dynamic = dynamic; } public boolean isEnableFirstCache() { return enableFirstCache; } public void setEnableFirstCache(boolean enableFirstCache) { this.enableFirstCache = enableFirstCache; } public String getUmpKeyPrefix() { return umpKeyPrefix; } public void setUmpKeyPrefix(String umpKeyPrefix) { this.umpKeyPrefix = umpKeyPrefix; }}
基于jimDB Caffiene缓存实现多级缓存管理器
/** * 二级缓存实现 * caffeine + jimDB 二级缓存 * @author wangzhen520 * @date 2022/12/9 */public class CaffeineJimMultilevelCacheManager extends AbstractMultilevelCacheManager { private CaffeineCacheManager caffeineCacheManager; private JimCacheManager jimCacheManager; public CaffeineJimMultilevelCacheManager(CaffeineCacheManager caffeineCacheManager, JimCacheManager jimCacheManager) { this.caffeineCacheManager = caffeineCacheManager; this.jimCacheManager = jimCacheManager; caffeineCacheManager.setAllowNullValues(this.allowNullValues); } /** * 一级缓存实现 * 基于caffeine实现 * @see org.springframework.cache.caffeine.CaffeineCache * @param name * @return */ @Override protected Cache getFirstCache(String name) { if(!isEnableFirstCache()){ return null; } return caffeineCacheManager.getCache(name); } /** * 二级缓存基于jimDB实现 * @see com.jd.jim.cli.springcache.JimStringCache * @param name * @return */ @Override protected Cache getSecondCache(String name) { return jimCacheManager.getCache(name); }}
缓存配置
/** * @author wangzhen520 * @date 2022/12/9 */@Configuration@EnableCachingpublic class CacheConfiguration { /** * 基于caffeine + JimDB 多级缓存Manager * @param firstCacheManager * @param secondCacheManager * @return */ @Primary @Bean(name = "caffeineJimCacheManager") public CacheManager multilevelCacheManager(@Param("firstCacheManager") CaffeineCacheManager firstCacheManager, @Param("secondCacheManager") JimCacheManager secondCacheManager){ CaffeineJimMultilevelCacheManager cacheManager = new CaffeineJimMultilevelCacheManager(firstCacheManager, secondCacheManager); cacheManager.setUmpKeyPrefix(String.format("%s.%s", UmpConstants.Key.PREFIX, UmpConstants.SYSTEM_NAME)); cacheManager.setEnableFirstCache(true); cacheManager.setDynamic(true); return cacheManager; } /** * 一级缓存Manager * @return */ @Bean(name = "firstCacheManager") public CaffeineCacheManager firstCacheManager(){ CaffeineCacheManager firstCacheManager = new CaffeineCacheManager(); firstCacheManager.setCaffeine(Caffeine.newBuilder() .initialCapacity(firstCacheInitialCapacity) .maximumSize(firstCacheMaximumSize) .expireAfterWrite(Duration.ofSeconds(firstCacheDurationSeconds))); firstCacheManager.setAllowNullValues(true); return firstCacheManager; } /** * 初始化二级缓存Manager * @param jimClientLF * @return */ @Bean(name = "secondCacheManager") public JimCacheManager secondCacheManager(@Param("jimClientLF") Cluster jimClientLF){ JimDbCache jimDbCache = new JimDbCache<>(); jimDbCache.setJimClient(jimClientLF); jimDbCache.setKeyPrefix(MultilevelCacheConstants.SERVICE_RULE_MATCH_CACHE); jimDbCache.setEntryTimeout(secondCacheExpireSeconds); jimDbCache.setValueSerializer(new JsonStringSerializer(ServiceRuleMatchResult.class)); JimCacheManager secondCacheManager = new JimCacheManager(); secondCacheManager.setCaches(Arrays.asList(jimDbCache)); return secondCacheManager; }
接口性能压测
压测环境
廊坊4C8G * 3
压测结果
1、50并发时,未开启缓存,压测5min,TP99: 67ms,TP999: 223ms,TPS:2072.39笔/秒,此时服务引擎cpu利用率40%左右;订购履约cpu利用率70%左右,磁盘使用率4min后被打满;
2、50并发时,开启二级缓存,压测10min,TP99: 33ms,TP999: 38ms,TPS:28521.18.笔/秒,此时服务引擎cpu利用率90%左右,订购履约cpu利用率10%左右,磁盘使用率3%左右;
缓存命中分析
总调用次数:1840486/min 一级缓存命中:1822820 /min 二级缓存命中:14454/min一级缓存命中率:99.04%二级缓存命中率:81.81%
压测数据
未开启缓存
开启多级缓存
监控数据
未开启缓存
下游应用由于4分钟后磁盘打满,性能到达瓶颈
接口UMP
服务引擎系统
订购履约系统
开启缓存
上游系统CPU利用率90%左右,下游系统调用量明显减少,CPU利用率仅10%左右
接口UMP
服务引擎系统
订购履约系统:
-
热推荐:基于Spring Cache实现Caffeine、jimDB多级缓存实战
在早期参与涅槃氛围标签中台项目中,前台要求接口性能999要求50ms以下,通过设计Caffeine、ehcache堆外...
来源: -
portswigger 靶场之 XSS 篇 (下)
APPRENTICEAlllabs|WebSecurityAcademy(portswigger net)14 利用跨站点脚本窃取cookie在burp中使用BurpCo
来源: -
全球最新:【算法训练营day32】LeetCode122. 买卖股票的最佳时机II LeetCode55. 跳跃游戏 LeetCode45. 跳跃游戏II
LeetCode122 买卖股票的最佳时机II题目链接:122 买卖股票的最佳时机II独上高楼,望尽天涯没有太好的...
来源: 热推荐:基于Spring Cache实现Caffeine、jimDB多级缓存实战
portswigger 靶场之 XSS 篇 (下)
全球最新:【算法训练营day32】LeetCode122. 买卖股票的最佳时机II LeetCode55. 跳跃游戏 LeetCode45. 跳跃游戏II
部署Kubernetes Cluster
每日短讯:12.4万买新帕纳梅拉!近600名国内网友保时捷官网疯抢:成功下单后被取消
【全球时快讯】奔驰获全球首家L3级自动驾驶认证:开车不用看路 出事故奔驰负责
【快播报】优酷回应1元会员被扣24元争议:活动规则已告知 扣钱没毛病
当前热文:水墨风场景惊艳!《仙剑奇侠传7》DLC《人间如梦》官宣2月发售
彻底扑灭一台特斯拉Model S有多难 消防员实测:用了22.7吨水
天天快消息!Android 软键盘丝滑切换(一)
天天看点:视频发布失败原因不好找?火山引擎数智平台这款产品能帮忙
速看:OpenYurt v1.2 新版本深度解读(一): 聚焦边云网络优化
【环球播资讯】小鹏股价暴跌、交付量惨淡 何小鹏专访回应:未来会这么做
全球动态:全球首个!婴幼儿视功能损伤手机智能筛查系统面世
撸猫手感 绿联iPhone 12-14系列液态硅胶保护壳9.9元起
天天快看点丨海淘不香了!日版Xbox主机涨价将近260元
天天速递!全国首烧?疑似红旗E-HS9充电时起火 现场黑烟弥漫
瑾娘为什么要杀华裳?瑾娘为什么假扮巽芳?
爱在旅途大结局是什么?爱在旅途剧情介绍
法国属于西欧还是北欧?南欧包括哪些国家?
荷兰为什么被称为水之国?荷兰水之国的资料简介
长宽高的英文缩写分别是什么?长宽高怎么算平方?
oppor7手机版本低怎么升级?oppo r7手机参数
复工第一天:请马上卸载这个恶心的软件!!!
全球看热讯:python-paramiko操作的封装
无法定位序数是什么意思?无法定位序数怎么解决?
打印机驱动在电脑哪里找?如何卸载打印机驱动?
无线适配器或访问点有问题是什么意思?无线适配器或访问点有问题怎么处理?
魅族手机怎么样?魅族手机锁屏密码忘了怎么解开?
环球速讯:工信部明天起优化调整微波频率 为5G/6G预留频谱资源
【独家】美国下手真狠!沃尔沃在美被罚8.7亿元 史上最大
快消息!APP竟比线下贵一倍还多 有电影院劝说观众退订淘票票
【世界快播报】提车1周 一特斯拉高速上行驶时方向盘脱落:维修还被收费
每日速读!全球最大游戏展E3辉煌不在:微软索尼任天堂“御三家”将集体缺席
波司登云原生微服务治理探索
今日热门!元宵节将至!元宵夜将出现年度最小满月
世界实时:侄子出演叔叔 MJ传记片年内开拍
【速看料】女子抱娃人肉占车位 还移走路障为自家车开路 结局引人舒适
世界讯息:西安阿房宫站将更名西安西站:原西站不够西
天天观热点:猪肉含量≥85% 一口全是肉:亚明猪肉烤肠2斤29.9元大促
焦点要闻:读Java8函数式编程笔记06_Lambda表达式编写并发程序
【全球新要闻】全网影视免费看,最新电影、电视剧免广告免VIP观看,只要你能搜到的,统统都能看,《狂飙》、《三体》追剧神器,时刻掌握最新剧集,无需安装,使用简单,
被苹果踢出供应链两年了 欧菲光仍未缓过劲:2022巨亏40多亿
精选!疯狂玩梗!强盛集团孙红雷直播被买鱼刷屏
焦点短讯!A卡很难追 游戏开发者越来越喜欢DLSS:理由离谱 弥补D加密损失
环球即时看!2023春节档爆发:复苏满座与极端的粉黑大战
每日消息!关于桌面上一万多个图标
刘慈欣:30年前拍不成《流浪地球2》 投资人不信中国有太空电梯
世界快看:老外幸福感暴降:英国近半年轻人担心收入永远不够养家
别贪速度快!SSD选什么接口更适合你?
《敢死队2》观后感
环球快报:VUEX 使用学习六 : modules
国产奋起!26557款软件力挺飞腾CPU
最新资讯:三大航空公司2022年合计预亏逾1000亿元!三大因素、东航最惨
环球观察:三亚凤凰机场出现滚滚浓烟?机场回应:暂无影响
快消息!这次过年 网吧终于活过来了!和以前完全不一样
河南矿山回应3名员工各领500万奖金:有人销售额超3亿
【环球报资讯】每个前端程序员都应该知道的10个Chrome扩展
刘慈欣:电影《流浪地球2》是原创而非小说改编 全方位超越第一部
今日聚焦!广东一男子打球6天后发现头顶卡对手2颗牙:网友神评论
焦点热议:索尼真有你的:背后给微软捅刀子
Acw 170.加成序列
精彩看点:React组件的使用
【全球独家】理想L5车型首次公布:不是SUV 价格坚守20万以上
每日热讯!最新显卡天梯榜公布:前十NVIDIA占五席!RTX 4090断层第一
全球新动态:杭州岳庙秦桧像被砸烂9次 游客仍不解气:专家称泄愤不应暴力
世界资讯:腾讯游戏春节7天吸金超4.5亿:《王者荣耀》独占一半 稳坐第一
世界信息:一加平板来了:Star Orbit金属打造 CNC一体机身
佳能2022年营收破4万亿日元 相机收入暴增 完全不惧手机蚕食
画面有点上头!男子扛铁板狂砸秦桧雕像:《满江红》带火景区热度
赚了!科学家在南极发现罕见大陨石:7.7公斤
全球消息!【Python】爬虫实战-基于代理池的高并发爬虫
焦点快看:rust写一个im聊天服务
全球看点:打破日本垄断!OLED关键材料FMM首次国产
环球今日讯!R星今日正式入驻B站!网友“花式”催更《GTA6》
全球观天下!显卡、主板返修排行:戴尔居然完美第一!AMD极度尴尬
环球简讯:神舟十五号航天员准备首次出舱!期待“感觉良好”
天天资讯:真我GT Neo5明天官宣 网友:赢麻了
每日视点!国产PCIe 4.0硬盘天花板 致态TiPro7000 1TB到手699元
《流浪地球2》热映 张朝阳称引力弹弓真实存在:美国航天器经历过
全球视点!比亚迪2022年业绩预告出炉:净利润同比暴增超450%
世界今热点:65寸电视不到2000元 LCD跌成白菜价 面板一哥京东方2022利润大跌70%
荣耀Magic5系列定档:2月27日MWC巴塞罗那见
Java中23种设计模式介绍和应用场景总结
今头条!手机测试之-adb
环球时讯:《鹅鸭杀》爆火,一文带你了解如何实现顶流社交游戏
DevOps: 自动与手动部署语义化版本(Semantic Versioning)实操
焦点信息:手上有了这些工具,明天争取6点下班了!
全球焦点!平价神器!新iPad mini 7曝光:处理器/屏幕惊喜
女子买电影票发现仅一个普通座位 其他全是C位 工作人员也无语
每日短讯:他真的很忙!雷军站公司门口给小米员工挨个发红包
最资讯丨打工人热议今天怎么才是周一:专家科普节后综合征
【全球热闻】《无名》折戟春节档:4.9亿票房只排第4、粉黑大战尴尬
springboot整合activiti实现流程审批(支持单体、微服务融合)
环球百事通!FPGA用ROM输出正弦波
深度学习基础课:卷积层的梯度检查
Dockerfile构建镜像
每日信息:SSL 证书基本概念扫盲
【全球速看料】亏电油耗不到5升 长安UNI-V iDD将于3月上市:或15万起售
要闻:《流浪地球2》有多硬核?工业机器人登上大银幕 周边产品也火了 超额数百倍