1. 首页
  2. 后端

JD-hotkey框架处理热key实践

  JD-hotkey框架处理热key实践

===================

事前准备

1、下载jd-hotkey源码:gitee.com/jd-platform…

2、在etcd下载页面下载对应操作系统的etcd,github.com/etcd-io/etc… 使用3.4.x以上。

一、安装etcd

详细安装地址可以参考:tianyalei.blog.csdn.net/article/det…

1、这里以windows系统,单机启动为例,下载后,解压运行 etcd.exe 即可。

image.png

2、运行后出现 ready to serve client requests 字眼,表示启动成功

image.png

二、启动worker(集群)

jar 运行方式

下载并编译好代码,将worker打包为jar,启动即可。如:

命令:nohup java -jar worker-0.0.4-SNAPSHOT.jar –etcd.server=${etcdServer} 2>&1 &

这里指定jar运行参数可以参考 worker模块下的 application.yml

idea运行

修改worker模块下的 application.yml的配置,我这里只修改了etcd地址和workrt的路径

etcd:
  server: http://127.0.0.1:2379 #etcd的地址,重要!!!
  workerPath: test_hot_key #该worker放到哪个path下,譬如放/app1下,则该worker只能被app1使用,不会为其他client提供服务

然后找到启动类,运行即可

三、启动控制台

下载并编译好dashboard项目,创建数据库并导入resource下db.sql文件。 配置一下application.yml里的数据库相关和etcdServer地址。 启动dashboard项目,访问ip:8081,即可看到界面。

⽤户名/密码:admin/123456 (SQL初始导入)

这里也是jar 或者 idea运行都可以,自行选择。

image.png

这里可以看到已经运行的worker节点信息

image.png

四、规则配置

新建用户

在用户管理菜单中,添加一个新用户,设置他的APP名字,如test_hot_key。之后新添加的这个用户就可以登录dashboard给自己的APP设置规则了。

image.png

新建规则

选中所属APP,然后配置相应规则

[{
    "key": "userId__", # key 名称, 结合 prefix 实现精准匹配或者模糊匹配
    "prefix": true, # 是否前缀 
    "interval": "10", # 间隔时间(秒)
    "threshold": "5", # 阈值 指在间隔时间内,访问次数超过阈值就会标记为热key
    "duration": "300" # 生成热key 后 缓存时间(秒),默认60
}]

image.png

五、搭建客户端验证

搭建脚手架

主要引入springboot + mybatis,可以正常访问 mysql数据库 (也可以用redis,验证框架是否可用,对数据源不限制),脚手架地址:start.aliyun.com/?spm=a2c6h.…

hotkey 客户端使用

1、引入依赖

<dependency>
    <groupId>com.jd.platform.hotkey</groupId>
    <artifactId>hotkey-client</artifactId>
    <version>${version}</version>
</dependency>

2、初始化HotKey

// appname 要跟规则对应上
@PostConstruct
public void initHotkey() {

    ClientStarter.Builder builder = new ClientStarter.Builder();
    ClientStarter starter = builder.setAppName("test_hot_key").setEtcdServer("http://127.0.0.1:2379").build();
    starter.startPipeline();
}

3、使用方式

主要有如下4个方法可供使用

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

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

3 void smartSet(String key, Object value),方法给热key赋值value,如果是热key,该方法才会赋值,非热key,什么也不做
void JdHotKeyStore.smartSet(String key, Object value)

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

3、验证demo

@Controller
public class PathVariableController {

    @Resource
    private UserMapper userMapper;

    public static volatile AtomicInteger count = new AtomicInteger(0);

    @RequestMapping(value = "/insert/{password}", method = RequestMethod.GET)
    @ResponseBody
    public String testInsert(@PathVariable("password") String password) {
        UserDO user = new UserDO().setUsername(UUID.randomUUID().toString())
                .setPassword(password).setCreateTime(new Date());
        userMapper.insert(user);
        return user.getId().toString();
    }

    @RequestMapping(value = "/selectById/{userId}", method = RequestMethod.GET)
    @ResponseBody
    public UserDO testSelectById(@PathVariable("userId") Integer userId) {
        System.out.println("累计请求多少次 count = " + count.incrementAndGet());
        Object userInfo = JdHotKeyStore.getValue("userId__" + userId);
        if (userInfo == null) {
            System.out.println("通过数据库查询:" + count.get());
            UserDO theUserInfo = userMapper.selectById(userId);
            JdHotKeyStore.smartSet("userId__" + userId, theUserInfo);
            return theUserInfo;
        } else {
            System.out.println("热key探测查询:" + count.get());
            return (UserDO) userInfo;
        }
    }

}

4、验证结果

image.png

可以看到第七次请求的时候,已经直接从jvm缓存中获取。

image.png

缓存过期后,重新查询,又开始从数据源获取源数据。

image.png

六、官方最佳实践

1 判断用户是否是刷子

if (JdHotKeyStore.isHotKey(“pin__” + thePin)) {
    //限流他,do your job
} 

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即可
      }

 }

七、疑问

为什么不是超过阈值就直接从缓存获取?

hotkey统计是采用 批量推送的,间隔时间默认500ms,该值越小,上报热key越频繁,相应越及时,建议根据实际情况调整,如单机每秒qps10个,那么0.5秒上报一次即可,否则是空跑。该值最小为1,即1ms上报一次

1、每次调用方法时,仅进行累计。

image.png

2、应用启动时初始化hotkey,会启动一个定时调度
com.jd.platform.hotkey.client.ClientStarter#startPipeline

image.png

com.jd.platform.hotkey.client.core.key.PushSchedulerStarter#startPusher
本质是使用 ScheduledExecutorService 周期线程池,根据设置的周期,进行数据上送

image.png

原文链接: https://juejin.cn/post/7352075813259411506

文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17908.html

QR code