当前位置: 首页 > news >正文

hotkey的学习

五、自动缓存热点

1、需求分析

  • 能自动的发现并缓存热点,也可管理员设置
    • 例如:
      • 高频面试题
      • 给经常访问的题库加缓存:当一个题库5秒内访问>=10次,加缓存。
  • 遇到攻击自动发现并进行封禁

2、方案设计

  • 自动缓存热门题库需要以下五个步骤:
    1、记录访问:用户每访问一次题库,统计次数+1
    2、访问统计:统计一段时间内题库的访问次数。这是最难实现的一部分。
    3、阈值判断:访问频率超过一定的阈值,变为热点数据。
    4、缓存数据:缓存热点数据
    5、获取数据:后续访问时,从缓存中获取数据
  • 自己实现,太难了,我们可以使用热点key的框架:hotkey

3、hotkey

京东的热点探测开源框架:https://gitee.com/jd-platform-opensource/hotkey

输入图片说明

4、核心组件

Etcd 集群
  • Etcd 作为一个高性能的配置中心,可以以极小的资源占用,提供高效的监听订阅服务。主要用于存放规则配置,各worker的 ip 地址,以及探测出的热 key、手工添加的热 key 等。
client端jar包
  • 判断那些key是热key,对热key进行caffeine本地缓存
worker
  • worker 端是一个独立部署的 Java 程序,启动后会连接 Etcd,并定期上报自己的ip 信息,供 client 端获取地址并进行长连
    接。之后,主要就是对各个 client 发来的待测 key 进行 累加计算,当达到 Etcd 里设定的 rule 阈值后,将热 kev 推送到各
    个 client.
dashboard
  • 控制台是一个带可视化界面的 Java 程序,也是连接到 Etcd,之后在控制台设置各个 APP 的 key 规则,譬如 2 秒出现 20
    次算热 key。然后当 worker 探测出来热 key 后,会将 key 发往 etcd,dashboard 也会监听热 key 信息,进行入库保存记
    录。同时,dashboard 也可以手工添加、删除热 key,供各个 client 端监听。

Etcd安装

  • etcd:etcd 服务本身
  • etcdctl:客户端,用于操作 etcd,比如读写数据
  • etcdutl:备份恢复工具
  • image-20250219171251593
  • 输入etcd启动
    • 2379:提供 HTTP API服务,和 etcdctl 交互
    • 2380:集群中节点间通讯

安装配置:hotkey

  • 将git源码下载下来:https://gitee.com/jd-platform-opensource/hotkey
  • 然后用idea打开
  • 先启动worker—>dashboard
启动worker
  • yml文件设置端口号为:8111
  • 启动
启动dashboard
  • 因为dashboard依赖于数据库

    • 先导入数据库数据
    • 修改数据库密码,端口好
  • 启动

    • 账号:admin
    • 密码:123456
    • image-20250220162257880
  • 添加规则

    • image-20250220162415399
    • image-20250220162642845
      • 意思是:interval-2秒内出现了threshold-10次就认为它是热key,它就会被推送到jvm内存中,并缓存60秒,prefix-true代表前缀匹配。那么在应用中,就可以把一组key,都用as__开头,用来探测。
引入客户端
  • 将打包好的客户端jar包放入主项目的lib里面

  • pom.xml文件

    • <dependency>
        <artifactId>hotkey-client</artifactId>
        <groupId>com.jd.platform.hotkey</groupId>
        <version>0.0.4-SNAPSHOT</version>
        <scope>system</scope>
        <systemPath>${project.basedir}/lib/hotkey-client-0.0.4-SNAPSHOT.jar</systemPath>
      </dependency>
      
      
  • 编写配置信息

    • yml文件

      • # 热 key 探测
        hotkey:
          app-name: lishuati
          caffeine-size: 10000
          push-period: 1000
          etcd-server: http://localhost:2379
        
    • 配置文件

      • @Configuration
        @ConfigurationProperties(prefix = "hotkey")
        @Data
        public class HotKeyConfig {
        
            /**
             * Etcd 服务器完整地址
             */
            private String etcdServer = "http://127.0.0.1:2379";
        
            /**
             * 应用名称
             */
            private String appName = "app";
        
            /**
             * 本地缓存最大数量
             */
            private int caffeineSize = 10000;
        
            /**
             * 批量推送 key 的间隔时间
             */
            private long pushPeriod = 1000L;
        
            /**
             * 初始化 hotkey
             */
            @Bean
            public void initHotkey() {
                ClientStarter.Builder builder = new ClientStarter.Builder();
                ClientStarter starter = builder.setAppName(appName)
                        .setCaffeineSize(caffeineSize)
                        .setPushPeriod(pushPeriod)
                        .setEtcdServer(etcdServer)
                        .build();
                starter.startPipeline();
            }
        }
        
        
最佳实践
  • 主要有如下4个方法可供使用

    boolean JdHotKeyStore.isHotKey(String key)

    Object JdHotKeyStore.get(String key)

    void JdHotKeyStore.smartSet(String key, Object value)

    Object JdHotKeyStore.getValue(String key)

    • boolean isHotKey(String key) ,

      • 该方法会返回该key是否是热key,如果是返回true,如果不是返回false,并且会将key上报到探测集群进行数量计算。该方法通常用于判断只需要判断key是否热、不需要缓存value的场景,如刷子用户、接口访问频率等。
    • Object get(String key),

      • 该方法返回该key本地缓存的value值,可用于判断是热key后,再去获取本地缓存的value值,通常用于redis热key缓存
    • void smartSet(String key, Object value),

      • 方法给热key赋值value,如果是热key,该方法才会赋值,非热key,什么也不做
    • Object getValue(String key),

      • 该方法是一个整合方法,相当于isHotKey和get两个方法的整合,该方法直接返回本地缓存的value。
        • 如果是热key,
          • 则存在两种情况,1是返回value,2是返回null。返回null是因为尚未给它set真正的value,返回非null说明已经调用过set方法了,本地缓存value有值了。
        • 如果不是热key,则返回null,并且将key上报到探测集群进行数量探测。
  • 最佳实践:

    • 判断用户是否是刷子
        if (JdHotKeyStore.isHotKey(“pin__” + thePin)) {
            //限流他,do your job
        } 
    
    • 判断商品id是否是热点
           Object skuInfo = JdHotKeyStore.getValue("skuId__" + skuId);
           if(skuInfo == null) {
               JdHotKeyStore.smartSet("skuId__" + skuId, theSkuInfo);
           } else {
                  //使用缓存好的value即可
            }
    
    • 或者这样:
             if (JdHotKeyStore.isHotKey(key)) {
                  //注意是get,不是getValue。getValue会获取并上报,get是纯粹的本地获取
                  Object skuInfo = JdHotKeyStore.get("skuId__" + skuId);
                  if(skuInfo == null) {
                      JdHotKeyStore.smartSet("skuId__" + skuId, theSkuInfo);
                  } else {
                      //使用缓存好的value即可
                  }
    
             }
    

改造项目

  • @GetMapping("/get/vo")
    public BaseResponse<QuestionBankVO> getQuestionBankVOById(QuestionBankQueryRequest questionBankQueryRequest, HttpServletRequest request) {
        ThrowUtils.throwIf(questionBankQueryRequest == null, ErrorCode.PARAMS_ERROR);
        Long id = questionBankQueryRequest.getId();
        ThrowUtils.throwIf(id <= 0, ErrorCode.PARAMS_ERROR);
    
        //生成热key
          String key = HotKeyConstant.HOT_KEY_PREFIX + id;
          //判断是否存在缓存
          if (JdHotKeyStore.isHotKey(key)) {
              //注意是get,不是getValue。getValue会获取并上报,get是纯粹的本地获取
              Object cacheQuestionBank = JdHotKeyStore.get(key);
              //如果缓存中存在,则直接返回
              if(cacheQuestionBank != null) {
                  return ResultUtils.success((QuestionBankVO) cacheQuestionBank);
              }
          }
    
          // 查询数据库
    
          // 写入缓存,如果不是热key,则不写入缓存,是热key,则写入缓存
          JdHotKeyStore.smartSet(key, questionBankVO);
    
          // 获取封装类
          return ResultUtils.success(questionBankVO);
    }
    
    
  • 改造完后,速度直线上升

    • image-20250220174605203
  • 3、热 key 会自动续期么?否则可能出现缓存雪崩的问题?

    • 可以先自己测试一下。比如针对某个热点 key 再多次发送请求查询缓存,发现在热 key 生效(缓存生效)期间,如果该key 仍然被不断访问,并不会刷新缓存时间,直到过期。
    • 然后看下源码,就知道为什么了。源码中的逻辑是,如果已经是热 key 则不会再 push,离过期还有2秒内的时候,会再
      次 push,这样这个 key 可能被继续设置为热 key。

相关文章:

  • 深度学习在图像识别中的应用-以花卉分类系统为例
  • Microsoft 365 Copilot中使用人数最多的是哪些应用
  • 聊聊 FocusSearch/focus_mcp_sql:Text2SQL 的新玩法
  • Word文档中插入的图片不能完整显示
  • LeetCode 热题 100_搜索二维矩阵(64_74_中等_C++)(二分查找)(暴力破解法;Z字形查找;一次二分查找)
  • 第8章:LangChain检索增强生成RAG--2.4Advanced RAG【高级RAG】
  • windows怎样查看系统信息(处理器等)
  • QSplashScreen --软件启动前的交互
  • 头像壁纸小程序源码,壁纸取图小程序源码系统
  • 【matlab代码】基于故障概率加权与多模态滤波的AUV多源融合导航
  • 如何有效利用MYSQL的连接数
  • cline通过硅基流动平台接入DeepSeek-R1模型接入指南
  • Windows 下如何对 node/vue 进行多版本管理?
  • 【后端基础】布隆过滤器原理
  • flowable适配达梦数据库
  • 二叉树的前序、中序、后序遍历(递归和非递归实现)
  • SpringBoot 自动装配原理详解
  • [答疑]领域建模:邓丽君、周杰伦和少女时代
  • 矩阵-旋转图像
  • Web - JS基础语法与表达式
  • 去年上海全市博物馆接待观众约4087万人次,同比增31.9%
  • 首次带人形机器人走科技节红毯,傅利叶顾捷:机器人行业没包袱,很多事都能从零开始
  • 广西百色“致富果”:高品质芒果直供香港,带动近五千户增收
  • 湃书单|澎湃新闻编辑们在读的14本书:后工作时代
  • 德国总理默茨发表首份政府声明:将提升国防能力,全力发展经济
  • 7月纽约举办“上海日”,上海大剧院舞剧《白蛇》连演三场