凯发app官方网站-凯发k8官网下载客户端中心 | | 凯发app官方网站-凯发k8官网下载客户端中心
  • 博客访问: 85528
  • 博文数量: 165
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 1655
  • 用 户 组: 普通用户
  • 注册时间: 2022-09-26 14:37
文章分类

全部博文(165)

文章存档

2024年(2)

2023年(95)

2022年(68)

我的朋友
相关博文
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·
  • ·

分类: java

2023-01-31 10:30:51

作者: 京东零售 王震

基于spring cache实现caffeine、jimdb多级缓存实战-凯发app官方网站

在早期参与涅槃氛围标签中台项目中,前台要求接口性能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<string, multilevelcache> 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<string> 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 @enablecaching public 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 服务引擎系统 订购履约系统:
阅读(302) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
")); function link(t){ var href= $(t).attr('href'); href ="?url=" encodeuricomponent(location.href); $(t).attr('href',href); //setcookie("returnouturl", location.href, 60, "/"); }
网站地图