从零开始手写 redis(四)监听器的实现
前言
java从零手写实现redis(一)如何实现固定大小的缓存?
java从零手写实现redis(三)redis expire 过期原理
java从零手写实现redis(三)内存数据如何重启不丢失?
本节,让我们来一起学习一下如何实现类似 guava-cache 中的 removeListener 删除监听器,和类似 redis 中的慢日志监控的 slowListener。
删除监听器
说明
我们在两种场景下删除数据是对用户透明的:
(1)size 满了之后,进行数据淘汰。
(2)expire 过期时,清除数据。
这两个特性对用户本来应该是无感的,不过用户如果关心的话,也可以通过添加删除监听器来获取到相关的变更信息。
实现思路
为了实现删除的监听,我们需要找到删除的位置,然后调用监听器即可。
evict 驱除的场景
每次 put 数据时,都会校验 size 是否达到最大的限制,如果达到,则进行 evict 淘汰。
expire 过期的场景
用户指定 expire 时间之后,回后台异步执行刷新。
也存在惰性删除的场景。
接口定义
为了统一,我们将所有的删除都定义统一的接口:
/**
* 删除监听器接口
*
* @author binbin.hou
* @since 0.0.6
* @param <K> key
* @param <V> value
*/
public interface ICacheRemoveListener<K,V> {
/**
* 监听
* @param context 上下文
* @since 0.0.6
*/
void listen(final ICacheRemoveListenerContext<K,V> context);
}
内置实现
系统内置的实现如下:
public class CacheRemoveListener<K,V> implements ICacheRemoveListener<K,V> {
private static final Log log = LogFactory.getLog(CacheRemoveListener.class);
@Override
public void listen(ICacheRemoveListenerContext<K, V> context) {
log.debug("Remove key: {}, value: {}, type: {}",
context.key(), context.value(), context.type());
}
}
这个监听器是默认开启的,暂时无法关闭。
自定义
用户可以自己的需要,进行自定义实现:
public class MyRemoveListener<K,V> implements ICacheRemoveListener<K,V> {
@Override
public void listen(ICacheRemoveListenerContext<K, V> context) {
System.out.println("【删除提示】可恶,我竟然被删除了!" + context.key());
}
}
测试
ICache<String, String> cache = CacheBs.<String,String>newInstance()
.size(1)
.addRemoveListener(new MyRemoveListener<String, String>())
.build();
cache.put("1", "1");
cache.put("2", "2");
我们指定 cache 的大小为1,设置我们自定义的删除监听器。
这里的删除监听器可以添加多个。
日志
测试日志如下:
[DEBUG] [2020-09-30 19:32:54.617] [main] [c.g.h.c.c.s.l.r.CacheRemoveListener.listen] - Remove key: 2, value: 2, type: evict
【删除提示】可恶,我竟然被删除了!2
慢操作监听器
说明
redis 中会存储慢操作的相关日志信息,主要是由两个参数构成:
(1)slowlog-log-slower-than 预设阈值,它的单位是毫秒(1秒=1000000微秒)默认值是10000
(2)slowlog-max-len 最多存储多少条的慢日志记录
不过 redis 是直接存储到内存中,而且有长度限制。
根据实际工作体验,如果我们可以添加慢日志的监听,然后有对应的存储或者报警,这样更加方便问题的分析和快速反馈。
所以我们引入类似于删除的监听器。
实现思路
我们处理所有的 cache 操作,并且记录对应的操作耗时。
如果耗时操作用户设置的时间阈值,则调用慢操作监听器。
接口定义
为了保证接口的灵活性,每一个实现都可以定义自己的慢操作阈值,这样便于分级处理。
比如超过 100ms,用户可以选择输出 warn 日志;超过 1s,可能影响到业务了,可以直接接入报警系统。
public interface ICacheSlowListener {
/**
* 监听
* @param context 上下文
* @since 0.0.6
*/
void listen(final ICacheSlowListenerContext context);
/**
* 慢日志的阈值
* @return 慢日志的阈值
* @since 0.0.9
*/
long slowerThanMills();
}
自定义监听器
实现接口 ICacheSlowListener
这里每一个监听器都可以指定自己的慢日志阈值,便于分级处理。
public class MySlowListener implements ICacheSlowListener {
@Override
public void listen(ICacheSlowListenerContext context) {
System.out.println("【慢日志】name: " + context.methodName());
}
@Override
public long slowerThanMills() {
return 0;
}
}
使用
ICache<String, String> cache = CacheBs.<String,String>newInstance()
.addSlowListener(new MySlowListener())
.build();
cache.put("1", "2");
cache.get("1");
- 测试效果
[DEBUG] [2020-09-30 17:40:11.547] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.before] - Cost start, method: put
[DEBUG] [2020-09-30 17:40:11.551] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.after] - Cost end, method: put, cost: 10ms
【慢日志】name: put
[DEBUG] [2020-09-30 17:40:11.554] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.before] - Cost start, method: get
[DEBUG] [2020-09-30 17:40:11.554] [main] [c.g.h.c.c.s.i.c.CacheInterceptorCost.after] - Cost end, method: get, cost: 1ms
【慢日志】name: get
实际工作中,我们可以针对慢日志数据存储,便于后期分析。
也可以直接接入报警系统,及时反馈问题。
小结
监听器实现起来比较简单,但是对于使用者的作用是比较大的。
文中主要讲述了思路,实现部分因为篇幅限制,没有全部贴出来。
觉得本文对你有帮助的话,欢迎点赞评论收藏关注一波~
你的鼓励,是我最大的动力~
从零开始手写 redis(四)监听器的实现的更多相关文章
- java 从零开始手写 RPC (03) 如何实现客户端调用服务端?
说明 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 写完了客户端和服务端,那么如何实现客户端和服务端的 ...
- java 从零开始手写 RPC (04) -序列化
序列化 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何实 ...
- java 从零开始手写 RPC (05) reflect 反射实现通用调用之服务端
通用调用 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RPC (02)-netty4 实现客户端和服务端 java 从零开始手写 RPC (03) 如何 ...
- java 从零开始手写 RPC (07)-timeout 超时处理
<过时不候> 最漫长的莫过于等待 我们不可能永远等一个人 就像请求 永远等待响应 超时处理 java 从零开始手写 RPC (01) 基于 socket 实现 java 从零开始手写 RP ...
- C基础 带你手写 redis sds
前言 - Simple Dynamic Strings antirez 想统一 Redis,Disque,Hiredis 项目中 SDS 代码, 因此构建了这个项目 https://github.c ...
- C基础 带你手写 redis adlist 双向链表
引言 - 导航栏目 有些朋友可能对 redis 充满着数不尽的求知欲, 也许是 redis 属于工作, 交流(面试)的大头戏, 不得不 ... 而自己当下对于 redis 只是停留在会用层面, 细节层 ...
- 用C、python手写redis客户端,兼容redis集群 (-MOVED和-ASK),快速搭建redis集群
想没想过,自己写一个redis客户端,是不是很难呢? 其实,并不是特别难. 首先,要知道redis服务端用的通信协议,建议直接去官网看,博客啥的其实也是从官网摘抄的,或者从其他博客抄的(忽略). 协议 ...
- C基础 带你手写 redis ae 事件驱动模型
引言 - 整体认识 redis ae 事件驱动模型, 网上聊得很多. 但当你仔细看完一篇又一篇之后, 可能你看的很舒服, 但对于 作者为什么要这么写, 出发点, 好处, 缺点 ... 可能还是好模糊, ...
- 从零开始手写Cartographer(1): 开端
写在前面的话 我做SLAM已经三年了.读书时初学SLAM,一开始无从下手,直到读了高博士的博客,茅塞顿开,渐入佳境.后来又买了他的<视觉SLAM十四讲>,常伴手边,直至毕业.几个月前找工作 ...
- 手写redis的docker文件,通过docker-compose配置redis
在前面一遍随笔,配置的是mysql主从的docker-compose配置.今天我们来学习配置编排容器redis. 准备环境: docker 18.06.1-ce docker-compose 1.23 ...
随机推荐
- Flutter 3 发布了(文末推荐一个免费的在线Flutter学习教程)
翻译自 Tim Sneath 2022年5月12日的文章 <Introducing Flutter 3> 作者 : Tim Sneath 翻译 : 沙漠尽头的狼(谷歌翻译加持) 链接 : ...
- 基于AHB_BUS的eflash控制器设计-02
基于AHB-BUS的eflash控制器设计 1.Flash Spec 1.1 地址映射 XADR是10bit?因为一共有1024行 每一行128byte容量,每次读取的粒度就是一个double wor ...
- 在Vue中可以使用方括号法获得想要的对象数据吗?
1.问题 Document {{message}} {{school.name}}{{school[mobile]}} 在这里 {{school.name}}{{school[mobile]}} 不可 ...
- 【MMC子系统】 一、MMC/SD/SDIO介绍
1.前言 该节学习Linux Kernel的MMC子系统,也称为块设备驱动,正如其名,与字符驱动相比,MMC子系统以块为单位进行操作. 同时,由于MMC Card.SD Card.SDIO Card等 ...
- css : object-fit 兼容 ie 的解决方案
通过 github 搜索 object-fit ie , 借鉴大佬兼容 ie 的经验. 下载解压到文件夹 , 打开测试目录 , 查看 demo 使用 ie 打开demo , 查看显示效果 : 代码 ...
- 买彩票能中大奖?用Java盘点常见的概率悖论 | 京东云技术团队
引言 <双色球头奖概率与被雷劈中的概率哪个高?> <3人轮流射击,枪法最差的反而更容易活下来?> 让我们用Java来探索ta们! 悖论1:著名的三门问题 规则描述:你正在参加一 ...
- Gitlab使用说明
零.gitlab简介 Gitlab是一个成熟的代码管理工具.为企业和组织提供内部的源代码的存储和管理功能. 一.gitlab角色总览 gitlab中的角色分管理员和 ...
- jenkins 安装与构建过程中的系列问题
一.插件安装遇到的依赖问题 插件安装分为在线安装和离线安装 1.在线安装 搜索要安装的插件,然后进行安装即可 2.离线安装hpi文件 使用该方法安装插件每次只能安装一个插件,且如果插件之间存在依赖性则 ...
- 开启想象翅膀:轻松实现文本生成模型的创作应用,支持LLaMA、ChatGLM、UDA、GPT2等模型,开箱即用
开启想象翅膀:轻松实现文本生成模型的创作应用,支持LLaMA.ChatGLM.UDA.GPT2等模型,开箱即用 1.介绍 TextGen实现了多种文本生成模型,包括:LLaMA.ChatGLM.UDA ...
- 14.7 Socket 循环结构体传输
在上述内容中笔者通过一个简单的案例给大家介绍了在套接字编程中如何传递结构体数据,本章将继续延申结构体传输,在某些时候例如我们需要传输一些当前系统的进程列表信息,或者是当前主机中的目录文件,此时就需要使 ...