范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Plumecache一个常见分布式缓存封装工具类库

  为什么有plumecache?
  日常项目开发中使用缓存的时候,无论选择redis、memcached,或是其它类型缓存,我们总要在做一些封装,才能很好的使用。plumecache是我个人缓存使用经验的一些总结,旨在于解决日常使用缓存时的一些问题和痛点,写出来和大家分享,也欢迎大家的star和issue Gitee地址简介
  Plumecache 是一个常见分布式缓存封装工具类库,降低相关client api学习成本,提高工作效率,当前支持reids(standalone,sentinel,cluster)、memcached
  Plumecache解决什么问题?1.用于替换项目中CacheService、CacheHelper、CacheHandler、CacheUtils等缓存工具类或胶水代码2.支持多缓存实例管理,解决既要还要的问题3.支持缓存key前缀、缓存版本的配置4.支持缓存序列化统一处理,不需要在每个调用的地方写序列化,支持自定义5.支持大缓存压缩处理,支持自定义6.支持自定义拦截器的AOP扩展,可以处理参数、返回值、异常快速使用通用接入1.maven(因域名问题,暂没有上传中央仓库),可以下载源码install到本地或者上传到私服,也可以先下载jar     org.plume     plumecache-core     1.0.0  复制代码2.配置plumecache:   instances:     - type: redis       endpoint: 127.0.0.1:6379       prefix: order 复制代码3.使用@Test public void test(){         CacheService cacheService=CacheServiceFactory.getInstance();         cacheService.set("name","anson.yin");         System.out.println(cacheService.get("name")); } 复制代码spring boot接入1.maven(因域名问题,暂没有上传中央仓库),可以下载源码install到本地或者上传到私服,也可以先下载jar      org.plume     plumecache-spring-boot-starter     1.0.0  复制代码2.配置plumecache:   instances:     - type: redis       endpoint: 127.0.0.1:6379       prefix: order 复制代码3.使用@Autowired private CacheService cacheService;  @RequestMapping(value = "/hello") public String hello(){         cacheService.set("name","anson.yin");         return"hello,".concat(cacheService.get("name")); } 复制代码功能说明配置plumecache:   instances:     - type: redis #必填       name: redis #非必填,缺省为和type一样       endpoint: 127.0.0.1:6379  #必填       prefix: order #非必填,like "prefix@key"       serializer: org.plumecache.samples.FastjsonCacheSerializer  #非必填,指定序列化类,缺省使用Gson序列化       compressor: org.plumecache.samples.NoneCacheCompressor  #非必填,指定压缩类,缺省使用Gzip压缩       exclude-interceptors: SlowCacheInterceptor,LogCacheInterceptor #非必填,排除不需要的拦截器 复制代码多实例
  配置plumecache:   instances:     - type: redis #必填       name: redis #非必填,缺省为和type一样       endpoint: 127.0.0.1:6379  #必填       prefix: order #非必填,like "prefix@key"       serializer: org.plumecache.samples.FastjsonCacheSerializer  #非必填,指定序列化类,缺省使用Gson序列化       compressor: org.plumecache.samples.NoneCacheCompressor  #非必填,指定压缩类,缺省使用Gzip压缩       exclude-interceptors: SlowCacheInterceptor,LogCacheInterceptor #非必填,排除不需要的拦截器     - type: memcached       endpoint: 127.0.0.1:11211     - type: memcached       name: memcached2       endpoint: 127.0.0.1:11222     - type: rediscluster       endpoint: 127.0.0.1:6379,127.0.0.1:6389,127.0.0.1:6399 复制代码
  说明0.上面的配置了四个缓存实例1.默认的缓存实例是第一个配置2.同一种类型多个实例,需要配置不同name来区分
  代码示例@Test public void testMultipleInstances() {         //和CacheService redis =CacheServiceFactory.getInstance("redis")效果一样         CacheService redis = CacheServiceFactory.getInstance();         redis.set("instance", "redis");         System.out.println(redis.get("instance"));          CacheService memcached = CacheServiceFactory.getInstance("memcached");         memcached.set("instance", "memcached");         System.out.println(memcached.get("instance")); } 复制代码序列化1.默认使用Gson作为序列化2.自定义序列化,需要实现CacheSerializer接口,并且在配置配置序列化类
  下面是FastjsonCacheSerializer的实现示例public class FastjsonCacheSerializer implements CacheSerializer {      @Override     public  String serialize(T value) {         return JSON.toJSONString(value);     }      @Override     public  T deserialize(String value, Class clazz) {         return JSON.parseObject(value, clazz);     } } 复制代码缓存压缩1.实体类添加 @CacheCompress 注解@Data @CacheCompress public class User {     private String name;     private Integer age;     private String address; } 复制代码2.调用方式不变    @Test     public void testCompress() {         User user = new User();         user.setAge(100);         user.setName("zhangsanfeng");         user.setAddress("zhangsanfengzhangsanfengzhangsanfengzhangsanfengzhangsanfengzhangsanfengzhangsanfengzhangsanfengzhangsanfengzhangsanfengzhangsanfeng");          cacheService.set("user", user);         User cacheUser = cacheService.get("user", User.class);                  System.out.println(cacheUser);     } 复制代码127.0.0.1:6379> get order@user " >]H4sIAAAAAAAAAKtWSkxJKUotLlayUqrKSMxLL07MS0vNSx8otpKOUmJ6qpKVoYGBjlJeYm4qmruU agHJDU9isgAAAA==" 复制代码3.自定义压缩器
  默认使用gzip压缩,也可以自定义实现,且添加配置
  参考GzipCacheCompressor.java缓存版本1.实体类添加 @CacheVersion 注解@Data @CacheVersion("2.0") public class User {     private String name;     private Integer age;     private String address; } 复制代码2.调用方式不变    @Test     public void testVersion() {         User user = new User();         user.setAge(100);         user.setName("zhangsanfeng");         user.setAddress("address");          cacheService.set("user", user);         User cacheUser = cacheService.get("user", User.class);          System.out.println(cacheUser);     } 复制代码127.0.0.1:6379> get order@user@2.0 " >5{"name":"zhangsanfeng","age":100,"address":"address"}" 复制代码拦截器
  拦截器是对缓存操作的切面处理,自定义拦截器操作如下1.实现CacheInterceptor,或者继承BaseCacheInterceptor,举例如下@Slf4j public class SlowCacheInterceptor extends BaseCacheInterceptor {      @Override     public boolean preHandle(CacheService target, Method method, Object[] args, Map context) {         Instant begin = Instant.now();         context.put("SlowCacheInstantBegin", begin);         return true;     }      @Override     public void postHandle(CacheService target, Method method, Object[] args, Map context, Object result) {         Instant begin = (Instant) context.get("SlowCacheInstantBegin");          if (Duration.between(begin, Instant.now()).toMillis() > 500) {             log.warn("[SlowCacheInterceptor]slow cache, method:{},args:{},result:{},cost:{}"                     , method.getDeclaringClass().getName() + "." + method.getName()                     , JSON.toJSONString(args)                     , JSON.toJSONString(result)                     , Duration.between(begin, Instant.now()).toMillis());              //do something others         }     } } 复制代码2.添加SPI的配置
  path:resources/services/com.plumecache.core.interceptor.CacheInterceptororg.plumecache.samples.SlowCacheInterceptor 复制代码
  或者参考core包实现LogCacheInterceptor.javaVersionCacheInterceptor.javaPrefixCacheInterceptor.java管理接口
  在spring boot stater且为web项目时生效1.list 显示实例列表curl -X GET localhost:8080/plumecache/list 复制代码[{"properties":{"name":"memcached","type":"memcached","endpoint":"127.0.0.1:11211","prefix":null,"serializer":null,"compressor":null,"excludeInterceptors":null},"statistics":{"cmd_touch":"0","moves_to_cold":"5","incr_hits":"1","get_flushed":"0","evictions":"0","touch_hits":"0","expired_unfetched":"0","pid":"1","time_in_listen_disabled_us":"0","response_obj_bytes":"65536","cas_badval":"0","cmd_flush":"0","total_items":"6","read_buf_oom":"0","round_robin_fallback":"0","slab_reassign_rescues":"0","log_watcher_skipped":"0","cas_hits":"0","accepting_conns":"1","auth_errors":"0","slab_reassign_evictions_nomem":"0","log_watcher_sent":"0","reserved_fds":"20","slab_reassign_running":"0","response_obj_oom":"0","crawler_items_checked":"12","direct_reclaims":"0","conn_yields":"0","slab_reassign_busy_deletes":"0","version":"1.6.10","read_buf_count":"8","listen_disabled_num":"0","slab_global_page_pool":"0","get_misses":"0","hash_is_expanding":"0","touch_misses":"0","get_expired":"0","auth_cmds":"0","cas_misses":"0","delete_misses":"0","cmd_meta":"0","get_hits":"4","slab_reassign_inline_reclaim":"0","malloc_fails":"0","delete_hits":"0","log_worker_written":"0","read_buf_bytes_free":"49152","lru_bumps_dropped":"0","curr_connections":"2","bytes_written":"4650","slab_reassign_busy_items":"0","hash_bytes":"524288","libevent":"2.1.8-stable","read_buf_bytes":"131072","lrutail_reflocked":"0","crawler_reclaimed":"0","decr_hits":"0","limit_maxbytes":"67108864","max_connections":"1024","decr_misses":"0","lru_crawler_running":"0","reclaimed":"0","rejected_connections":"0","cmd_get":"4","hash_power_level":"16","curr_items":"3","threads":"4","cmd_set":"5","bytes_read":"360","slab_reassign_chunk_rescues":"0","lru_crawler_starts":"27","uptime":"22253","log_worker_dropped":"0","unexpected_napi_ids":"0","total_connections":"9","evicted_active":"0","incr_misses":"1","connection_structures":"4","bytes":"221","lru_maintainer_juggles":"30247","evicted_unfetched":"0","rusage_system":"12.600134","time":"1638171110","slabs_moved":"0","moves_within_lru":"0","pointer_size":"64","moves_to_warm":"0","rusage_user":"8.090528","response_obj_count":"1"}},{"properties":{"name":"redis","type":"redis","endpoint":"127.0.0.1:6379","prefix":"order","serializer":"org.plumecache.samples.FastjsonCacheSerializer","compressor":"com.plumecache.core.compressor.NoneCacheCompressor","excludeInterceptors":["SlowCacheInterceptor","LogCacheInterceptor"]},"statistics":{"io_threaded_reads_processed":"0","tracking_clients":"0","uptime_in_seconds":"22224","cluster_connections":"0","current_cow_size":"0","maxmemory_human":"0B","aof_last_cow_size":"0","master_replid2":"0000000000000000000000000000000000000000","mem_replication_backlog":"0","aof_rewrite_scheduled":"0","total_net_input_bytes":"19507","rss_overhead_ratio":"1.58","hz":"10","current_cow_size_age":"0","redis_build_id":"69ab6eec4665acbc","aof_last_bgrewrite_status":"ok","multiplexing_api":"epoll","client_recent_max_output_buffer":"0","allocator_resident":"5181440","mem_fragmentation_bytes":"6549960","repl_backlog_first_byte_offset":"0","tracking_total_prefixes":"0","redis_mode":"standalone","cmdstat_get":"calls=10,usec=342,usec_per_call=34.20,rejected_calls=0,failed_calls=0","redis_git_dirty":"0","allocator_rss_bytes":"3031040","repl_backlog_histlen":"0","io_threads_active":"0","rss_overhead_bytes":"2990080","total_system_memory":"2082197504","loading":"0","evicted_keys":"0","maxclients":"10000","cmdstat_set":"calls=10,usec=1941,usec_per_call=194.10,rejected_calls=0,failed_calls=0","cluster_enabled":"0","redis_version":"6.2.5","repl_backlog_active":"0","mem_aof_buffer":"0","allocator_frag_bytes":"447160","io_threaded_writes_processed":"0","instantaneous_ops_per_sec":"0","used_memory_human":"1.59M","cmdstat_incr":"calls=1,usec=68,usec_per_call=68.00,rejected_calls=0,failed_calls=0","total_error_replies":"0","role":"master","maxmemory":"0","used_memory_lua":"37888","rdb_current_bgsave_time_sec":"-1","used_memory_startup":"809880","used_cpu_sys_main_thread":"146.683765","lazyfree_pending_objects":"0","used_memory_dataset_perc":"38.70%","allocator_frag_ratio":"1.26","arch_bits":"64","used_cpu_user_main_thread":"38.482678","mem_clients_normal":"512400","expired_time_cap_reached_count":"0","unexpected_error_replies":"0","mem_fragmentation_ratio":"5.04","aof_last_rewrite_time_sec":"-1","master_replid":"9c1aa9fc501b889e35727910956569cac1a2fda1","aof_rewrite_in_progress":"0","lru_clock":"10781132","maxmemory_policy":"noeviction","run_id":"d05aa7e4384c6ccd09ee83e919c9e9edcd2d8450","latest_fork_usec":"439","tracking_total_items":"0","total_commands_processed":"1349","expired_keys":"0","used_memory":"1664360","module_fork_in_progress":"0","dump_payload_sanitizations":"0","mem_clients_slaves":"0","keyspace_misses":"3","server_time_usec":"1638171084987972","executable":"/data/redis-server","lazyfreed_objects":"0","db0":"keys=232,expires=0,avg_ttl=0","used_memory_peak_human":"4.53M","keyspace_hits":"7","rdb_last_cow_size":"520192","used_memory_overhead":"1333640","active_defrag_hits":"0","tcp_port":"6379","uptime_in_days":"0","used_memory_peak_perc":"35.03%","current_save_keys_processed":"0","blocked_clients":"0","total_reads_processed":"1900","expire_cycle_cpu_milliseconds":"7066","sync_partial_err":"0","used_memory_scripts_human":"0B","aof_current_rewrite_time_sec":"-1","aof_enabled":"0","process_supervised":"no","cmdstat_info":"calls=2,usec=559,usec_per_call=279.50,rejected_calls=0,failed_calls=0","master_repl_offset":"0","used_memory_dataset":"330720","used_cpu_user":"38.497928","rdb_last_bgsave_status":"ok","tracking_total_keys":"0","cmdstat_ping":"calls=1325,usec=19580,usec_per_call=14.78,rejected_calls=0,failed_calls=0","atomicvar_api":"c11-builtin","allocator_rss_ratio":"2.41","client_recent_max_input_buffer":"16","clients_in_timeout_table":"0","aof_last_write_status":"ok","mem_allocator":"jemalloc-5.1.0","cmdstat_incrby":"calls=1,usec=21,usec_per_call=21.00,rejected_calls=0,failed_calls=0","used_memory_scripts":"0","used_memory_peak":"4751720","process_id":"1","master_failover_state":"no-failover","used_cpu_sys":"146.736539","repl_backlog_size":"1048576","connected_slaves":"0","current_save_keys_total":"0","gcc_version":"8.3.0","total_system_memory_human":"1.94G","sync_full":"0","connected_clients":"25","module_fork_last_cow_size":"0","total_writes_processed":"1349","allocator_active":"2150400","total_net_output_bytes":"18709","pubsub_channels":"0","current_fork_perc":"0.00","active_defrag_key_hits":"0","rdb_changes_since_last_save":"0","instantaneous_input_kbps":"0.00","configured_hz":"10","used_memory_rss_human":"7.79M","expired_stale_perc":"0.00","active_defrag_misses":"0","used_cpu_sys_children":"0.029570","number_of_cached_scripts":"0","sync_partial_ok":"0","used_memory_lua_human":"37.00K","rdb_last_save_time":"1638169845","pubsub_patterns":"0","slave_expires_tracked_keys":"0","redis_git_sha1":"00000000","used_memory_rss":"8171520","rdb_last_bgsave_time_sec":"0","os":"Linux 5.10.47-linuxkit x86_64","mem_not_counted_for_evict":"0","active_defrag_running":"0","rejected_connections":"0","total_forks":"3","active_defrag_key_misses":"0","allocator_allocated":"1703240","instantaneous_output_kbps":"0.00","second_repl_offset":"-1","rdb_bgsave_in_progress":"0","used_cpu_user_children":"0.012939","total_connections_received":"575","migrate_cached_sockets":"0"}}] 复制代码2.execute 执行缓存操作curl localhost:8080/plumecache/execute  -X POST  -H "Content-Type: application/json"  -d "{"command":"get","key":"name"}" 复制代码anson.yin 复制代码接口说明
  参考CacheService.javaRoadMap
  version 1.0.01.支持spring boot 项目接入, 基于spring boot starter和 enable 以及 conditional注解完成初始化 --done2.支持spring项目介入 提供初始化工具类 --done3.支持key前缀功能 --done4.支持多个cache实例 --done5.支持get、set、delete、exists、expire、ttl命令 --done6.支持dashboard api(实例列表,实例参数,命令执行)7.支持自定义拦截器 --done8.支持自定义序列化 --done9.返回具体实例(redissonclient、MemcachedClient)对象 --done10.实现hget,hset,hgetall,lock 命令 --done11.支持缓存压缩功能(大value压缩) --done12.支持版本功能(version 注解) --done
  version 2.0.00.缓存参数配置(timeout,poolsize,idlecount,attempts)1.支持线程内缓存(基于threadlocal)2.支持字段序列化(基于ObjectOutputStream)项目结构
  作者:广陵笑笑生
  链接:https://juejin.cn/post/7038850628965105672

隐藏功能揭秘,人性化功能都藏在细节里,ColorOS这些优点知道吗随着时间的推移,手机能够承载的事情越来越多,我们的生活似乎和手机逐渐捆绑在了一起,手机的易用性与否,则直接影响我们的日常生活体验。目前各大手机厂商都有着优秀的硬件表现,而在软件适配新款iPhoneSE3仅售2000多元?A15处理器5G,只为吸引安卓用户提到iPhone,大家第一印象会想到什么?好用?流畅还是高端?反正我第一时间想到的是价格,作为刚毕业不久的学生,面对动辄五六千起步的售价,虽然很喜欢但是囊中羞涩,也只能远观。其实这Apple乔布斯与库克时代2021。10。5,苹果公司前创始人一代创奇人物史蒂夫乔布斯结束了他璀璨的一生。回顾乔布斯的一生,即使这一路不是特别顺畅,他仍然没有改变他的坚持创新和追求极端的态度。在库克上任后的用了一年的华为Mate40Pro,如今换成小米12PPro,憋了很多话想说如果说现在哪款华为手机值得入手?那么我肯定会推荐华为Mate40Pro,它支持5G网络,相比现在的华为P50系列以及Nova9系列热度更高,一年前我也是把它当作主力机来使用,到现在断臂求生!滴滴的孙公司悄然注销,新项目一年亏掉200多亿2022年2月份,有细心的媒体发现深圳橙心优选科技发展有限公司发生工商变更,其状态变更为注销,这家企业是橙心优选深圳公司。前不久,滴滴公布了去年第三季度财报,企业各项业绩均出现了不冰雪消融,水鸟游弋,北京号带您云游这些观鸟打卡地SPRING二月湖水清,家家春鸟鸣。大地从寒冬中慢慢苏醒过来,风由凛冽逐渐变得轻柔,吹来阵阵暖意。春水初生,春林初盛。湖面上冰消雪融,水鸟游弋河岸边草长莺飞,天鹅展翅。每一处都透露李慕豪7中7砍15分摩尔特里空砍2519新疆三分仅22中2不敌北京北京时间3月1日,CBA常规赛,北京以10665狂胜新疆。北京队李慕豪7投全中得到15分,林书豪12分5篮板,翟晓川7分10篮板,范子铭10分9篮板6助攻,汉密尔顿17分6篮板新疆绝招最多的莎皇特鲁索娃泪洒北京冬奥会!命该如此冬奥会很多滑冰项目都是俄罗斯选手的强项,其中女子花滑项目更是俄罗斯的国粹,犹如中国的国球乒乓球一样,天才选手辈出,更新换代的速度只有9个月,内卷之严重超乎想象。很多天才级选手没到2跌至1989元,骁龙870哈曼卡顿无线充,小米10S加速清仓或许大家都知道,最近两年高通平台的表现很拉胯,从骁龙888到骁龙888Plus,然后是如今的骁龙8Gen1,纸面参数做得很豪华,但功耗跟发热一直不理想,塞进手机狭小的内部空间里,完锤子科技5000万股权被解冻,公司股权冻结信息已全部清零,罗永浩回应3月1日,雷峰网消息,近日,北京锤子数码科技有限公司新增股权解冻信息,股权冻结详情显示,股权数额为5000万元人民币,被执行人为北京锤子数码科技有限公司,冻结股权标的企业成都野望数油价调整信息今天3月1日,国内加油站调整后9295号汽油售价今天是2022年3月1日,截至国内成品油价格调整的第7个工作日,参考原油油种周期内滚动均价为96。15美元桶,原油综合变化率3。15,下一轮成品油调价窗口将于3月3日24时开启。俄
夺位!史蒂文森第十回合TKO赫林,24岁成为两个级别的世界拳王史蒂文森,24岁拿下两个级别的世界拳王北京时间2021年10月24日中午,在美国亚特兰大州立农场体育馆(StateFarmArena)举行的WBO超羽量级世界拳王争霸战落下帷幕。在过早退役的8名球员!如果能继续踢下去,或许能创造一些奇迹据报道,伊瓜因正考虑在33岁退役!自从去年转会到贝克汉姆的国际迈阿密后,他就没能给人留下深刻印象,在过去的大联盟赛季中,他在35次出场中只进了11球。这位前皇家马德里和尤文图斯前锋奥尼尔当今联盟只有4人已经证明了自己,其他人还有很多不足奥尼尔当今联盟只有4人已经证明了自己,其他人还有很多不足!最近奥尼尔在社交媒体晒出了一张现役当中最强的球星,分别是詹姆斯伦纳德字母哥和库里,同时还在图片下面配上一段文字表明这群球员高抬贵手!浦江之流请不要捧杀唐欣周页彤等年轻球员全锦赛正在如火如荼进行中,各队的表现都在我的意料之中,只是江苏女排稍显稚嫩,有点尴尬!不过年轻的江苏女排几乎清一色的零零后球员,不可能与山东女排与天津女排相提并论也是可以理解的。全欧文发文疑似坚持自我!韦德儿子加盟爵士下属球队!詹皇最新动态北京时间10月24日,NBA比赛继续进行,在已经结束的场次里,骑士爆冷击败老鹰,步行者击败拥有三巨头的迈阿密热火,总之,今天冷门不断。而在刚刚过去的一夜之间,联盟有多个重要消息传出三项命中率36038,威少成最大罪人,詹姆斯射手竟是我自己两场比赛,詹姆斯在三分线外20中10,而其他首发球员的这个数据是24中4。我们都知道,最适合詹姆斯的阵容就是一星四射,可是如今詹姆斯看到首发队友的表现不禁感叹射手竟是我自己!再看到怀疑孩子得了注意力缺乏综合症,请仔细读读这篇文章近些年来,我们都太热衷于给孩子贴标签。我建议所有和你有着相同情况的父母首先找出导致这种行为的各种可能原因。经常不听话的孩子,特别难以对付的孩子,能力发展比较慢的孩子,都是父母的心病土超劲旅盼望李盈莹加盟!李盈莹成朱婷之后,中国女排新希望关于李盈莹去海外打球的消息,最近传得沸沸扬扬,有人相信,也有人在质疑。今天土耳其媒体的一则消息,让这些争议戛然而止,看来李盈莹真的要加盟土超劲旅费内巴切了!据外媒消息,费内巴切俱乐郭艾伦赛季首秀2858阿不都43分创生涯得分新高辽宁送新疆三连败北京时间10月25日1500,20212022赛季CBA常规赛第五轮,辽宁队10999战胜新疆队送给对手三连败。本场比赛,郭艾伦首发出场,上演赛季首秀。郭艾伦技术统计辽宁队郭艾伦2北京新增京外关联本地确诊6例10月22日0时至24时,北京新增6例京外关联本地新冠肺炎确诊病例(其中病例1至病例4昨日已通报),无新增疑似病例和无症状感染者无新增境外输入确诊病例疑似病例和无症状感染者。确诊病郭艾伦刘志轩复出,外援到位!辽篮12人主力大名单更新,太豪华这几天辽宁队传来了很多好消息,郭艾伦终于可以上场打球了,福格也马上就能代表辽宁队上场根据辽宁媒体透露,刘志轩已经痊愈,即将赶往赛区。新赛季的这几场比赛,辽宁队阵容并不完整,如今郭艾