动易网站首页制作,公司logo图片,网页设计实训心得体会500字,创建个人网站的步骤一.引言
某些热点数据#xff0c;我们提前如果能够预判到的话#xff0c;可以提前人工给数据加缓存#xff0c;也就是缓存预热#xff0c;将其缓存在本地或者Redis中#xff0c;提高访问性能同时#xff0c;减低数据库压力#xff0c;也减轻后端服务的压力。但是#…一.引言
某些热点数据我们提前如果能够预判到的话可以提前人工给数据加缓存也就是缓存预热将其缓存在本地或者Redis中提高访问性能同时减低数据库压力也减轻后端服务的压力。但是有些时候我们无法预料到哪些数据是热点比如一个冷门数据突然变成一个热点数据没来得及缓存突然被大量访问系统不就故障了吗
因此我们就需要帮助我们快速发现热点Key并且自动缓存哪就不解决太多问题了嘛因此京东的这个HotKey中间件就来了。
二.介绍
官网JDHotKey
介绍
对任意突发性的无法预先感知的热点数据包括并不限于热点数据如突发大量请求同一个商品、热用户如恶意爬虫刷子、热接口突发海量请求同一个接口等进行毫秒级精准探测到。然后对这些热数据、热用户等推送到所有服务端JVM内存中以大幅减轻对后端数据存储层的冲击并可以由使用者决定如何分配、使用这些热key譬如对热商品做本地缓存、对热用户进行拒绝访问、对热接口进行熔断或返回默认值。这些热数据在整个服务端集群内保持一致性并且业务隔离worker端性能强悍。
京东APP后台热数据探测框架历经多次高压压测和2020年京东618、双11大促考验。
在上线运行的这段时间内每天探测的key数量数十亿计精准捕获了大量爬虫、刷子用户另准确探测大量热门商品并毫秒级推送到各个服务端内存大幅降低了热数据对数据层的查询压力提升了应用性能。 核心组件 它的主要核心组件如下:1) Etcd 集群 Etcd 作为一个高性能的配置中心可以以极小的资源占用提供高效的监听订阅服务。主要用于存放规则配置各 worker的 ip 地址以及探测出的热 key、手工添加的热 key 等。Etcd 常用于配置中心和注册中心2) client端iar包 就是在服务中添加的引用jar引入后就可以便捷地去判断某 key 是否热 key。同时该 jar 完成了 key 上报、监听 Etcd 里的 rule 变化、worker 信息变化、热 key 变化对热 key 进行本地 Caffeine 缓存等。3) worker端集群 worker 端是一个独立部署的 Java 程序启动后会连接 Etcd并定期上报自己的 ip 信息供 client 端获取地址并进行长连 接。之后主要就是对各个 client 发来的待测 key 进行 累加计算当达到 Etcd 里设定的 rule 阈值后将热 key 推送到各 个client.4) dashboard 控制台 控制台是一个带可视化界面的 Java 程序也是连接到 Etcd之后在控制台设置各个 APP 的 key 规则譬如 2 秒出现,20次算热 key。然后当 worker 探测出来热 key 后会将 key 发往 etcddashboard 也会监听热 key 信息进行入库保存记录。同时dashboard 也可以手工添加、删除热 key供各个 client 端监听。 更详细的内容可见京东技术团队 官方的文章最具可信度。 三.安装
1. Etcd 安装:
在etcd下载页面下载对应操作系统的etcdhttps://github.com/etcd-io/etcd/releases 使用3.4.x以上。
下载后解压压缩包会得到3个脚本
etcd:etcd 服务本身etcdctl:客户端用于操作 etcd比如读写数据etcdutl:备份恢复工具 输入etcd 启动 执行 etcd 脚本后可以启动 etcd 服务服务默认占用 2379 和 2380 端口作用分别如下:
·2379:提供 HTTP API服务和 etcdct 交互·2380:集群中节点间通讯
2. worker安装
从 hotkey 官方仓库 下载源码 - 下载地址 注意::: JDK 的版本必须小于 17!否则会报找不到类,因为有些类jdk17已经弃用了 的错误! 项目导入 IDEA后打开 worker 模块。worker 是一个 Spring Boot 项目启动前需要先修改 applicaiton.yml 中的配置。 比如端口配置( 8111)
修改完配置后直接点击WorkerApplication 启动即可, 如下图此时 worker 就已经正常启动并且连接上 Etcd 了:
3. 启动 hotkey 控制台
接着打开 dashboard 项目执行 resource 目录下的 db.sql文件创建 dashboard 所需的库表。hotkey 依赖 MySQL 储用户账号信息、热点阈值规则等。 在执行脚本前记得先配置好 MySQL 连接并且在 SQL 脚本文件中创建和指定数据库 执行脚本过程如图
修改端口及数据库配置
server:port: 8182
spring:datasource:username: ${MYSQL_USER:root}password: ${MYSQL_PASS:1234}url: jdbc:mysql://${MYSQL_HOST:localhost}:3306/hotkey_db?useUnicodetruecharacterEncodingutf-8useSSLtrueserverTimezoneUTCuseTimezonetrueserverTimezoneGMTdriver-class-name: com.mysql.cj.jdbc.Driver
etcd:server: ${etcdServer:http://127.0.0.1:2379}dashboard 也是一个 SpringBoot 项目直接在 IDEA 内执行 DashboardApplication 启动即可 访问 http://127.0.0.1:8182端口就是你自己配置的 即可看到界面: 初始Sql的时候会有一个默认账号 admin 密码 123456 (加密了的) 登录 初次使用时需要先添加 APP。建议先在用户管理菜单中添加一个新用户设置昵称为 APP 名称、并填写所属 APP如 GCXF-App密码此处就设置为 123456。之后就可以登录这个新建的用户来给应用设置规则了(当然也可以使用 admin 账 户添加)而且系统会自动创建一个 APP。 随后在规则配置中选择对应的 APP新增对应的热点探测规则: 我这里的意思就是判断poem_开头的key如果5秒内访问 如果5秒访问 10 次就会被推送到jvm 内存中将这个热 key 缓存3 分钟。 对应的规则配置如下:
[{key:poem_,prefix:true,interval:5,threshold:10,duration:180,desc:搜索热点key}
] key(*代表任意以key为前缀) 只要到时候client端上报这样子的key于这边相互匹配就可以完成统计prefix是否前缀, interval间隔时间(秒),threshold阈值, duration-缓存时间(秒)默认60desc描述 4. 引入 hotkey client
有2 种引入 hotkey client 的方式: 1.手动源码打包 2.通过 Maven 远程仓库 引入 由于 Maven 远程仓库的包引用量过少而且不具备官方权威性所以更推荐通过 hotkey 源码手动打包。 所以选择方式 1手动将 hotkey 源码中的 client 模块通过 Maven 打成 jar 包: 在我们引入的项目中新键一个lib文件然后放入client的jar包 引入依赖; !-- hotkey --dependencyartifactIdhotkey-client/artifactIdgroupIdcom.jd.platform.hotkey/groupIdversion0.0.4-SNAPSHOT/versionscopesystem/scopesystemPath${project.basedir}/lib/hotkey-client-0.0.4-SNAPSHOT.jar/systemPath/dependency 引入依赖后在代码中编写初始化 client 的配置类会读取配置文件并执行初始化逻辑
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*/Beanpublic void initHotkey() {ClientStarter.Builder builder new ClientStarter.Builder();ClientStarter starter builder.setAppName(appName).setCaffeineSize(caffeineSize).setPushPeriod(pushPeriod).setEtcdServer(etcdServer).build();starter.startPipeline();}
}注这里spring-boot不要太高因为不允许是void的返回值的也可以用提供的接口来解决所有干脆调到3.0.2下就可以了
pom.xml # 热 key 探测
hotkey:app-name: GCXF-Appcaffeine-size: 10000push-period: 1000etcd-server: http://localhost:2379app-name: GCXF-App: 这是一个键值对其中app-name是键GCXF-App是值。这表示应用程序的名称被设置为GCXF-App。这个值可能用于标识这个配置所属的应用程序。caffeine-size: 10000: 这也是一个键值对其中caffeine-size是键10000是值。这个值可能代表某种缓存可能是指Caffeine缓存库的大小设置单位可能是条目数、字节或其他具体取决于上下文。Caffeine是一个高性能的Java缓存库。push-period: 1000: 这是一个表示时间间隔的键值对意味着这个操作每秒去Push一次 上报一次。etcd-server: http://localhost:2379: 这个值指定了etcd服务器的地址和端口客户端可以通过这个地址与etcd服务器进行通信。
这里的App-name与你先去在etcd中配置app必须一致
启动可以看到有一个客户端链接了
OK我们就可以使用了
四.使用
主要有如下4个方法可供使用
boolean JdHotKeyStore.isHotKey(String key)Object JdHotKeyStore.get(String key)void JdHotKeyStore.smartSet(String key, Object value)Object JdHotKeyStore.getValue(String key)
1 boolean isHotKey(String key) 该方法会返回该key是否是热key如果是返回true如果不是返回false并且会将key上报到探测集群进行数量计算。该方法通常用于判断只需要判断key是否热、不需要缓存value的场景如刷子用户、接口访问频率等。
2 Object get(String key)该方法返回该key本地缓存的value值可用于判断是热key后再去获取本地缓存的value值通常用于redis热key缓存
3 void smartSet(String key, Object value)方法给热key赋值value如果是热key该方法才会赋值非热key什么也不做
4 Object getValue(String key)该方法是一个整合方法相当于isHotKey和get两个方法的整合该方法直接返回本地缓存的value。 如果是热key则存在两种情况1是返回value2是返回null。返回null是因为尚未给它set真正的value返回非null说明已经调用过set方法了本地缓存value有值了。 如果不是热key则返回null并且将key上报到探测集群进行数量探测。 官网推荐的最佳实践
1 判断用户是否是刷子
if (JdHotKeyStore.isHotKey(“pin__” thePin)) {// 进行限流
}2 判断商品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即可}
}代码测试 Autowiredprivate HotKeyPoemMapper hotKeyPoemMapper;Autowiredprivate PoemNameMapper poemNameMapper;private RestHighLevelClient restHighLevelClient new RestHighLevelClient(RestClient.builder(HttpHost.create(http://192.168.184.128:9200)));/*** 查询所有古诗** param name* return*/Overridepublic PoemNameVO selectPoemByName(String name) {String key poem_ name;// 判断是否是热搜if (JdHotKeyStore.isHotKey(key)) {// 获取缓存Object poemNameVO JdHotKeyStore.get(key);if (poemNameVO ! null) {return (PoemNameVO) poemNameVO;}}PoemName poenNmae null;poenNmae poemNameMapper.selectPoemByName(name);if (poenNmae null) {throw new PoenException(MessageContast.POEM_ERROR);}PoemNameVO poemNameVO new PoemNameVO();//获取注释ListpoemExplain poemExplains poemNameMapper.selectBypoenID(Long.valueOf(poenNmae.getId()));//判断是否有该收藏古诗 1返回true null 返回faslePoemNameVO poemNameVO1 poemNameMapper.selectCollectByHeader(name);if (poemNameVO1 null) {poemNameVO.setTrue(false);} else {poemNameVO.setTrue(true);}ListString list new ArrayList();//获取全文集合String[] split poenNmae.getAllpoem().split(。);for (String s : split) {list.add(s);}poemNameVO.setAllpoem(list);list new ArrayList();//获取背景集合String[] split1 poenNmae.getPoemDrop().split(。);for (String s : split1) {list.add(s);}poemNameVO.setPoemDrops(list);//复制传参BeanUtils.copyProperties(poenNmae, poemNameVO);poemNameVO.setPoemExplain(poemExplains);if(JdHotKeyStore.isHotKey(key)){// 设置缓存JdHotKeyStore.smartSet(key, poemNameVO);//并且给热点key累加排行poemNameMapper.sumTheHotKey(name);}return poemNameVO;}
因为上面我们配置了5秒内访问10次就会变成热点Key之后我们就会把这个数据存储到本地缓存中下次访问的时候就会直接从本地缓存中去读取了并不会在去查询数据库了。
我们测试一下
通过接口5秒内访问了10次后
然后可以看间 事实热点已经有了 当我们再次访问就不会查询数据库了都是通过本地缓存来查询可以感觉到非常块
源码;
1 热 key 会自动续期吗?否则可能出现缓存雪崩的问题? public static boolean isHotKey(String key) {try {if (!inRule(key)) {return false;} else {boolean isHot isHot(key);if (!isHot) {HotKeyPusher.push(key, (KeyType)null);} else {ValueModel valueModel getValueSimple(key);if (isNearExpire(valueModel)) {HotKeyPusher.push(key, (KeyType)null);}}KeyHandlerFactory.getCounter().collect(new KeyHotModel(key, isHot));return isHot;}} catch (Exception var3) {return false;}}public static Object get(String key) {ValueModel value getValueSimple(key);if (value null) {return null;} else {Object object value.getValue();return object instanceof Integer Constant.MAGIC_NUMBER (Integer)object ? null : object;}}
分析 然后看下源码就知道为什么了。源码中的逻辑是首先会校验这个key是否在规则中如果不是当然返回fasle,然后才判断是否是热点key如果已经是热 key ,返回缓存值但是不会再 push离过期还有2秒内的时候会再次 push这样这个 key 可能被继续设置为热 key。
也就是说如果一个 key 持续被访问很有可能在过期前一直被设置为热点减少了出现雪崩问题的可能性。
2.能够和 redis 分布式缓存结合 热 key 探测 热 key 发现 本地缓存。可以只利用热 key 的判断方法来给我们判断哪些是热Key不利用热 key 的存储方法即可通过换成redis存储也是可以
方法 1.不是热 key就查数据库。对于热 key写缓存时再判断一下是否为热 key是热 key 才设置 Redis 分布式缓存。后续的热 key 就可以从分布式缓存中获取值。(缓存存储的技术或者位置变了) 2.利用热 key 探测的本地缓存将原本査数据库的逻辑改为査 RedisRedis 查不到才查询数据库形成多级缓存。 3.) 如何更新本地缓存 需要有一个入口让缓存失效进行人工干预。hotkey 提供了 JdHotKeystore.remove()方法可以手动删除本地缓存并移除热点 key。 以利用控制台手动删除: 不过一般情况下热点信息一般都是不太会变更的数据过期时间设置短一点即可。