HotKey学习总结

开源地址:https://gitee.com/jd-platform-opensource/hotkey

1.它用来解决啥问题?

​ 对任意突发性的无法预先感知的热点数据,包括并不限于热点数据(如突发大量请求同一个商品)、热用户(如恶意爬虫刷子)、热接口(突发海量请求同一个接口)等,进行毫秒级精准探测到。然后对这些热数据、热用户等,推送到所有服务端JVM内存中,以大幅减轻对后端数据存储层的冲击,并可以由使用者决定如何分配、使用这些热key(譬如对热商品做本地缓存、对热用户进行拒绝访问、对热接口进行熔断或返回默认值)。

​ 总结一句话:热点缓存,防止穿透

2.它是怎么做的?

​ 它通过客户端主动上报数据到计算节点(wroker),通过获取配置中心的规则配置最终计算出热点数据。通过计算节点回推给所有的客户端,本地JVM缓存起来。

3.架构又是如何?

官网的一份图很清晰明了。如下:

docker集群:代表用户的客户端

worker集群:计算集群

etcd集群:配置集群,存储规则,worker节点,client节点,dashboard节点信息。

流程介绍:

​ 客户端通过引用hotkey的client包,在启动的时候上报自己的信息给worker和worker之间建立长连接。定时拉取配置集群上面的规则信息和worker集群信息。

​ 客户端调用hotkey的ishot的方法来首先匹配规则,然后统计是不是热key;通过定时任务把热key数据上传到worker节点。worker集群在收取到所有关于这个key的数据以后(因为通过hash来决定key 上传到哪个worker的,所以同一个key只会在同一个worker节点上),和定义的规则匹配判断是不是热key,然后推送给客户端,完成缓存

4.简单使用

//判断rekey
public Object a(String s) {if (JdHotKeyStore.isHotKey(”store_" + s)) {logger.error("isHot");} else {logger.error("noHot");}return 1;}//获取热key value
public String get(String key) {Object object = JdHotKeyStore.getValue(key);//如果已经缓存过了if (object != null) {System.out.println("is hot key");return object.toString();} else {//底层获取String value = getFromRedis(key);//判断热key,塞入值JdHotKeyStore.smartSet(key, value);return value;}}

client仅仅需要调用JdHotKeyStore.isHotKey来判断是不是热key,后面都会做好所有的初始化工作,几乎0接入门槛。

get的方法演示了他是如何获取热key缓存的,当缓存失效的时候,我们还可以通过多种方式处理。

5.原理又是如何?

介绍一下最重要的几个过程

5.1 client启动过程调用

client启动,从etcd中获取所有关于我这个APP的worker集群。通过Netty和所有的worker节点保持长连接,发送信息。

worker接受到client的信息,上报client的信息和client的APP信息,在etcd里面保存。

PS:client启动的时候不止是会做这个,还会启动很多定时任务。通过定时任务的方式上传判断的热key和统计数据,还会和etcd保持长连接来监听数据的变化,来更新本地的缓存信息

5.2 client 热key的过程

上面就是热key的调用过程,比较清晰,不用文字了。

5.3 上传热key消息

 @Overridepublic void collect(HotKeyModel hotKeyModel) {String key = hotKeyModel.getKey();if (StrUtil.isEmpty(key)) {return;}if (atomicLong.get() % 2 == 0) {//不存在时返回null并将key-value放入,已有相同key时,返回该key对应的value,并且不覆盖HotKeyModel model = map0.putIfAbsent(key, hotKeyModel);if (model != null) {model.setCount(model.getCount() + hotKeyModel.getCount());}} else {HotKeyModel model = map1.putIfAbsent(key, hotKeyModel);if (model != null) {model.setCount(model.getCount() + hotKeyModel.getCount());}}}

​ 热key上传的时候用到了一个ping-pong buffer的概念。通过一个状态量来切换两个数据map,当一个数据需要上传的时候,就切换一次,然后使用其中一个map,另一个map接受数据。

6.涉及到JAVA的知识体系

Hot Key用了很多开源的中间件,也达到了很好的效果。

6.1 设计模式使用

工厂

KeyHandlerFactory

调用链

worker:AppNameFilter

​ worker:HeartBeatFilter

worker:HotKeyFilter

worker:KeyCounterFilter

单例

CaffeineCacheHolder

6.2 滑动窗口

对应的类:SlidingWindow

通过滑动窗口使用,减少统计的时间片;来减少毛刺现象。

附上类实现:

package com.jd.platform.hotkey.worker.tool;import cn.hutool.core.date.SystemClock;import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.atomic.AtomicInteger;public class SlidingWindow {/*** 循环队列,就是装多个窗口用,该数量是windowSize的2倍*/private AtomicInteger[] timeSlices;/*** 队列的总长度*/private int timeSliceSize;/*** 每个时间片的时长,以毫秒为单位*/private int timeMillisPerSlice;/*** 共有多少个时间片(即窗口长度)*/private int windowSize;/*** 在一个完整窗口期内允许通过的最大阈值*/private int threshold;/*** 该滑窗的起始创建时间,也就是第一个数据*/private long beginTimestamp;/*** 最后一个数据的时间戳*/private long lastAddTimestamp;public static void main(String[] args) {//1秒一个时间片,窗口共5个SlidingWindow window = new SlidingWindow(2, 20);CyclicBarrier cyclicBarrier = new CyclicBarrier(10);for (int i = 0; i < 10; i++) {new Thread(new Runnable() {@Overridepublic void run() {try {cyclicBarrier.await();} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}boolean hot = window.addCount(2);System.out.println(hot);}}).start();}}public SlidingWindow(int duration, int threshold) {//超过10分钟的按10分钟if (duration > 600) {duration = 600;}//要求5秒内探测出来的,if (duration <= 5) {this.windowSize = 5;this.timeMillisPerSlice = duration * 200;} else {this.windowSize = 10;this.timeMillisPerSlice = duration * 100;}this.threshold = threshold;// 保证存储在至少两个windowthis.timeSliceSize = windowSize * 2;reset();}public SlidingWindow(int timeMillisPerSlice, int windowSize, int threshold) {this.timeMillisPerSlice = timeMillisPerSlice;this.windowSize = windowSize;this.threshold = threshold;// 保证存储在至少两个windowthis.timeSliceSize = windowSize * 2;reset();}/*** 初始化*/private void reset() {beginTimestamp = SystemClock.now();//窗口个数AtomicInteger[] localTimeSlices = new AtomicInteger[timeSliceSize];for (int i = 0; i < timeSliceSize; i++) {localTimeSlices[i] = new AtomicInteger(0);}timeSlices = localTimeSlices;}private void print() {for (AtomicInteger integer : timeSlices) {System.out.print(integer + "-");}}/*** 计算当前所在的时间片的位置*/private int locationIndex() {long now = SystemClock.now();//如果当前的key已经超出一整个时间片了,那么就直接初始化就行了,不用去计算了if (now - lastAddTimestamp > timeMillisPerSlice * windowSize) {reset();}int index = (int) (((now - beginTimestamp) / timeMillisPerSlice) % timeSliceSize);if (index < 0) {return 0;}return index;}/*** 增加count个数量*/public synchronized boolean addCount(int count) {//当前自己所在的位置,是哪个小时间窗int index = locationIndex();
//        System.out.println("index:" + index);//然后清空自己前面windowSize到2*windowSize之间的数据格的数据//譬如1秒分4个窗口,那么数组共计8个窗口//当前index为5时,就清空6、7、8、1。然后把2、3、4、5的加起来就是该窗口内的总和clearFromIndex(index);int sum = 0;// 在当前时间片里继续+1sum += timeSlices[index].addAndGet(count);//加上前面几个时间片for (int i = 1; i < windowSize; i++) {sum += timeSlices[(index - i + timeSliceSize) % timeSliceSize].get();}lastAddTimestamp = SystemClock.now();return sum >= threshold;}private void clearFromIndex(int index) {for (int i = 1; i <= windowSize; i++) {int j = index + i;if (j >= windowSize * 2) {j -= windowSize * 2;}timeSlices[j].set(0);}}}

开源作者有关于时间窗口的文章:https://blog.csdn.net/tianyaleixiaowu/article/details/102861254

6.3 Netty

netty本次主要用到的几个方法:

userEventTriggered同步ping-pong消息

channelActive同步client的信息接口

channelActive删除client的信息接口

channelRead0接受过来的消息同步接口

HotKey学习总结相关推荐

  1. 自学python推荐书籍 知乎-你一定不能错过的翻译学习资源!

    原标题:你一定不能错过的翻译学习资源! 1 翻译学习 同声传译的日常工作内容有哪些? http://www.zhihu.com/question/20109720 民国时期的学者是如何做到精通各国语言 ...

  2. 学习 | 雷军 1994 年写的代码,不服不行

    点上方蓝字计算机视觉联盟获取更多干货 在右上方 ··· 设为星标 ★,与你不见不散 仅作分享,不代表本公众号立场,侵权联系删除 转载于:程序员的那些事 AI博士笔记系列推荐 周志华<机器学习&g ...

  3. python自动化框架学习-pyautogui

    一.适用平台:PC(windows和mac均可用) 二.下载安装: 推荐使用命令行下载(因为会自动安装依赖库): pip install PyAutoGUI 该框架的依赖库还是蛮多的,第一次用的同学耐 ...

  4. 批处理脚本学习笔记——程序猿版

    批处理脚本学习笔记 原创作品.同意转载,转载时请务必以超链接形式标明文章原始出处.作者信息和本声明.否则将追究法律责任.http://blog.csdn.net/taotaoyouarebaby/ar ...

  5. 分享Silverlight/WPF/Windows Phone/HTML5一周学习导读(1月16日-2月5日)

    分享Silverlight/WPF/Windows Phone/HTML5一周学习导读(1月16日-2月5日) 本周Silverlight学习资源更新 WIn2003部署Silverlight coo ...

  6. HTML+CSS零基础学习笔记(五)— 模拟淘宝首页(静态)

    内容概览 模拟淘宝首页(静态) 一.项目重难点 二.效果图及源码 模拟淘宝首页(静态) 一.项目重难点 样式重置:在实际开发过程中,我们往往会新建一个单独的CSS文件(reset.css),用于对应H ...

  7. ICC学习——LAB0A

    ICC学习--LAB0A 文章目录 ICC学习--LAB0A Task1 启动ICC Task2 layout视图导航 Task3 控制图层的可见性 Task4 选择和查询对象 Task5 得到关于命 ...

  8. ICC 图文学习——LAB0:ICC图形界面

      解决了库的问题,开始上手跑lab了.在做lab的过程中,会补充一些相关的知识点和自己的学习笔记,加深理解,防止遗忘. LAB0:熟悉ICC图形界面 LAB 0A 1. 启动ICC 2. Layou ...

  9. 您可能不需要Twitter客户端,只需在New Twitter中学习热键

    It's probably not unfair to call me a Twitter Power User. I use it a lot, it's my favorite Social Ne ...

  10. 批处理脚本学习笔记——程序员版

    批处理脚本学习笔记 原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处.作者信息和本声明.否则将追究法律责任.http://blog.csdn.net/taotaoyouarebaby/ar ...

最新文章

  1. golang 切片删除指定内容
  2. ICCV 2019《Zero-Shot Grounding of Objects from Natural Language Queries》论文笔记
  3. 巧用定时任务监控第三方组件是否正常
  4. 用rem来做响应式开发
  5. jsp+servlet+java 实现统计在线人数
  6. python eval简介
  7. 在a标签中写ajax,ajax请求后的数据渲染到页面中,a链接失效
  8. 将叶节点连接成一个链表☆
  9. mysql只update不做修改_长沙一学校另类考试走红网络:只做游戏不做题
  10. docker logstash_Mysql 同步数据到 Elasticserach(使用logstash-input-jdbc)
  11. SQL window身份登陆 SQL server不能登陆
  12. Kolmogorov 的数学观与业绩
  13. 网易邮箱大师代收gmail
  14. IEEE latex 模板 部分文字变色 (变蓝,变成浅蓝色)暂时的解决方案
  15. 图像算法研究---背景虚化算法
  16. iOS TCP UDP通信
  17. CPT101计算机系统概念(存储系统部分)
  18. 悉尼大学经济学荣誉升学及就业情况情况
  19. Problem  L 求m到n之和 C语言函数
  20. ARIMA实现(亲测可用)

热门文章

  1. python调用rf关键字_RobotFramework之关键字
  2. 2018.9.10 工作日志 猎宝行动
  3. uniapp实现复制功能
  4. 巧用RoaringBitMap处理海量数据内存diff问题
  5. 解决Layui表格头部工具栏事件绑定失效,上传文件按钮失效问题
  6. bert常用基准数据集:GLUE数据集介绍以及数据集资源
  7. Bash命令光标移动和编辑
  8. unity学习之路——什么是unity(附学习路线)
  9. windows c++ 获取本地ip地址
  10. Windows上安装ubantu