已开源!网易云信的热点探测平台实践

news2024/12/23 23:00:46

背景

对于一个互联网平台,特别是 toB 的 PaaS/SaaS 平台,热点 key 是一个绕不过去的问题。作为一个开放的系统,平台每天要承载来自大量的外部系统或者海量终端的请求,虽然所有的请求都需要满足开放平台定义好的鉴权规则,但是突发的请求或者异常的请求,却总是不期而至。

对于 toB 系统来说,这样的请求至少包括以下几种类型:

  • 客户错误使用姿势导致的异常流量

    PaaS 平台往往以 API 和 SDK 的方式对外提供服务,虽然我们会提供各种demo和解决方案来指导客户以更优雅的方式使用我们的服务,但是你永远无法预测某个客户以一种你预想不到的方式调用我们的 API。

  • 来自未知攻击者的流量

    以云信为例,我们的服务器常年受到各种四层、七层攻击。四层流量一般不会直接到后台服务器,但是对于七层流量就除了通用的 waf 防护之外,也需要在业务层能及时发现和定位来源。

  • 客户压测

    没错,客户经常会在没有事先通知的情况给我们一个大惊喜,为了保护我们的系统不被突如其来的流量打垮,我们必须快速识别这样的流量并作出合适的反馈。

  • 客户的客户,错误使用姿势导致的异常流量

    对于 toB 产品,平台直接面对的往往是开发者或者说企业,客户的客户除了 C 端客户之外,也可能是其他企业,这种复杂的关系导致不确定性大大增加。

  • 客户被攻击产生的到平台的异常流量

    我们也不时收到来自客户的请求,反馈他们被黑产刷了,希望我们帮忙提供解决方案,这些黑产往往使用各种工具,也往往伴随着大流量,PaaS/SaaS 厂商属于躺枪的角色。

为了应对上述各种突发和异常流量带来的系统稳定性风险,开放平台往往会针对租户级别设置一些接口的 QPS 限制(频控系统),从而保护系统。但是频控往往是在入口层(API)设置规则,并不能完整描述突发异常流量对系统产生压力的根本原因(比如某个 redis-key 或者某个数据库行),也就是说频控系统是面向外部视角的,而风险源却是面向内部资源的,从风险点本身看,频控系统的逻辑有时候显得有一些粗放。此外,由于各种各样的原因,频控配置的设置往往也不能覆盖完整,而且随着系统的不断演讲,相关参数也需要不断进行调整。

因此在频控系统之外,还需要一个热点探测平台,热点探测系统聚焦于产生系统稳定性风险的热点本身,从而可以精确的评估和发现风险点。

为什么要自研

我们调研了已有的热点探测的方案,比如京东的 hotkey(https://gitee.com/jd-platform-opensource/hotkey),搜狐的 hotCaffeine(https://github.com/sohutv/hotcaffeine),对内咨询了网易云音乐的 hotCaffiene 内部 fork 版本,在分析了功能特点和我们的需求后,基于以下几个原因,我们决定在参考上述方案的基础上进行自研。

  • 上述开源方案均和 etcd 强绑定,etcd 在系统中扮演了配置中心和注册中心的角色,并且无法使用其他服务替代,我们期望复用已有的配置中心和注册中心,而不需要再单独维护一套 etcd 集群。

  • 上述开源方案更多侧重于热 key 缓存,而非监控本身。

  • 我们期望框架尽可能的精简,有最小的依赖。

  • 我们希望以插件化的方式开放相关接口,从而可以更方便的对接到不同部门的内部系统中,以更方便的开发自定义业务逻辑

基于上述原因,我们决定自研 camellia-hot-key 这样的热 key 探测框架。

系统架构

架构图

 

 

架构原理

作为一个通用的热点探测平台,在设计时首先需要解决以下几个问题:

  • 怎么收集和统计热点 key 的数量?

  • 怎么定义和管理热点 key 的规则?

  • 怎么使用热点 key 的探测结果?

怎么收集和统计热点key的数量?

由于热点 key 是一个全局维度的定义,因此必然需要一个中心化的服务器来汇总,第一反应就是使用 redis 之类的中心化缓存来作为一个集中的计数器管理工具。但是面对海量的 key,redis 却存在一些很显著的性能瓶颈,即使不考虑性能问题,产生的资源开销也是巨大的,因此我们需要设计一个低资源开销的方案。

当我们重新审视热 key 探测的场景可以发现,实际上我们并不需要实时,而仅需要准实时(百 ms 级别)即可满足绝大部分场景。因此通过本地缓存和批量处理,将可以极大的降低中心化服务器的压力,而且也能通过灵活的调整缓存时长和批量大小来满足不同业务的不同需求。而对于服务器本身,参考 redis-cluster 的 slot 分片的思路,我们可以将 hot-key-server 按照一定规则进行 hash 分片,从而保证相同的 key 路由到相同的 server 节点,从而把热 key 的计算完全本地化,配合前面说的本地缓存+批量处理,最终可以以较小的代价完成大量 key 的统计和计算。

除了中心化服务器之外,在收集和统计上还需要处理一个海量 key 的问题。显然我们不能粗暴的在一个时间窗口内记录下所有 key,一个简单的思路是使用 LRU/LFU 等算法来进行内存控制。为此,我们考察了【ConcurrentLinkedHashMap/Guava/Caffeine】等开源框架(这几个是同一个作者在不同时间实现的,对作者 @Ben Manes 表示敬意),Caffeine 号称进程缓存之王,其 W-TinyLFU 算法提供了最优缓存命中率,在热点探测场景下也能让我们不会错过热 key,因此 Caffeine 无疑是我们的第一选择。但再进一步分析后,我们最终选择了混用 ConcurrentLinkedHashMap 和 Caffeine,以达到最优的性能。

经过上面的分析,基本的服务器架构就比较清晰明了了,整个系统包括 SDK 和 server 两部分:SDK 使用 Caffeine 来收集 key,并定时上报给 server;server 收到后,依然使用 Caffeine 来进行收集和计算 key。而对于不同的 namespace,因为数量可预期,则使用 ConcurrentLinkedHashMap 来进行管理(仅需要简单的 lru 进行系统保护即可)。

怎么定义和管理热点 key 的规则?

热 key 探测显然是一个需要灵活变更热 key 规则的服务,热 key 规则主要包括 2 部分:

  • 一个是什么 key 需要探测。

  • 一个是什么样的 key 算热 key。

前者我们提供了前缀匹配、完全匹配、子串包含、匹配所有等不同的 key 规则匹配模式,方便业务进行不同维度的配置,并且提供规则列表的概念,按照定义的优先级进行匹配。

对于后者,我们开放了时间窗口、热 key 阈值两个参数来定义热 key。时间窗口是一个滑动的窗口,以 1000ms 内 500 次这样的规则为例,框架内部会以 100ms 为一个滑动小窗口,取最近 10 个小窗口(100ms*10=1000ms)组成一个 1000ms 的目标窗口进行计数,这种方式一方面可以第一时间识别出热 key,另一方面对于热 key 的探测也不会存在跳跃的问题。

对于热 key 规则的使用,除了在 server 端之外,框架也会下发给 SDK,从而对于不需要的 key,可以直接在 SDK 侧丢弃,从而减少不必要的网络传输。

作为一个通用的热 key 探测服务,显然会服务于不同的业务线,即使是同一个业务线,也会有不同的业务场景。因此对于热 key 规则之外,我们定义了 namespace 的概念,每个 namespace 下可以定义 1 个或者多个规则,一个服务支持同时配置多个 namespace,各个 namespace 之间互相隔离。

怎么使用热点 key 的探测结果?

首先,框架内我们预设了一个可选的 hot-key-cache 方式,这是 SDK+server 共同完成的。针对一些热点查询场景,在检测到热 key 后,server 会自动把检测结果下发给 SDK,SDK 会自动把结果缓存起来,从而避免查询请求的穿透,保护后端的缓存/数据库服务,并且为了保证缓存的时效性,server 还会把缓存结果的更新/删除事件通知给关联的 SDK 端,从而尽可能的保证缓存值在第一时间获得更新,SDK 也会把缓存命中情况上报给 server,方便服务器进行数据统计。

除此之外,server 会主动把探测结果推送给业务定义好的回调中,业务可以自行进行自定义的处理,如报警、限流、加黑等。

在接入初期,你可能并不知道如何设定热 key 规则,设置的小了,可能会被热 key 通知给淹没,设置的大了又可能发挥不了效果,因此 server 还内置了一个热 key 的 topN 探测功能,把 namespace 下访问请求最多的 key 告知业务,业务可以据此进行故障定位,或者以此为依据设置符合业务实际的热 key 规则。

插件化和自定义扩展口

前面讲了热 key 探测框架的基本原理,而 camellia-hot-key 在设计之初就考虑到了不同业务线的不同需求,因此采用了插件化的设计原则,方便业务在不修改框架源码的前提下,可以更灵活的使用,也能更容易的和已有系统进行打通。插件化主要体现在以下几点:

  • 注册中心

不同于已有的开源热 key 服务,camellia-hot-key 不和任何注册中心进行绑定,你仅需实现相关接口,即可非常快速的和已有的注册中心进行整合,如 zk(内置)、eureka(内置)、etcd、consul、nacos 等。

  • 配置中心

camellia-hot-key 不和任何配置中心进行绑定,而是定义了 HotKeyConfigService 配置接口,你仅需实现该接口,即可非常快的把热 key 规则托管到你已有的配置中心中(camellia-hot-key 内置了本地配置文件+nacos 两种方式)。

HotKeyConfigService 配置接口定义如下:

public abstract class HotKeyConfigService {
    /**
     * 获取HotKeyConfig
     * @param namespace namespace
     * @return HotKeyConfig
     */
    public abstract HotKeyConfig get(String namespace);

    /**
     * 初始化后会调用本方法,你可以重写本方法去获取到HotKeyServerProperties中的相关配置
     * @param properties properties
     */
    public void init(HotKeyServerProperties properties) {
    }

    //回调方法
    protected final void invokeUpdate(String namespace) {
        //xxxx
    }
}

此外,在 camellia-hot-key 的设计中,配置中心仅需要和 server 进行交互,SDK 会通过 server 自动获取到配置(配置初始化+配置更新),而不需要和配置中心直连,从而尽可能的简化 SDK 的逻辑(让 SDK 更瘦)。

监控端点和自定义回调

camellia-hot-key 的 server 提供了 http 的监控端点(json 格式/promethus 格式),用于暴露服务器等基本信息(如 qps、堆积等)。

此外还提供了丰富的回调接口,包括但不限于:

  • HotKeyCallback

    热 key 探测回调,会把热 key 通过本回调实时推送给业务侧,除了推送热 key 本身以及当前计数外,还会同时把热 key 命中的规则和热 key 的来源一起回调给业务。

  • HotKeyTopNCallback

    热 key 的 topN 回调,这是一个全局维度的 topN 统计(会汇总多个 server 节点的数据),框架会定时回调给业务(默认 1 分钟)。

  • HotKeyCacheStatsCallback

    在启用热 key 缓存功能的情况下,SDK 会定时上报热 key 缓存的命中情况,服务器会通过这个回调接口把统计数据回调给业务。

友好的 SDK 接口

为了适配不同的应用场景,框架对外提供了 CamelliaHotKeyMonitorSDK 和CamelliaHotKeyCacheSDK 两种不同的 SDK。

  • CamelliaHotKeyMonitorSDK

用于单纯的热 key 统计功能,探测结果的处理由业务自行完成,探测结果的处理可以在 SDK 侧进行,也可以在 server 侧进行。核心接口只有一个:

/**
     * 推送一个key用于统计和检测热key
     * @param namespace namespace
     * @param key key
     * @param count count
     * @return Result 结果
     */
    Result push(String namespace, String key, long count);
  • CamelliaHotKeyCacheSDK

封装了热 key 缓存的功能,SDK 会自动把探测到的热 key 进行本地缓存,业务接入方仅需实现 ValueLoader 接口即可。核心接口包括以下三个:

/**
     * 获取一个key的value
     * 如果是热key,则会优先获取本地缓存中的内容,如果获取不到则会走loader穿透
     * 如果不是热key,则通过loader获取到value后返回
     *
     * 如果key有更新了,hot-key-server会广播给所有sdk去更新本地缓存,从而保证缓存值的时效性
     *
     * 如果key没有更新,sdk也会在配置的expireMillis之前尝试刷新一下(单机只会穿透一次)
     *
     * @param namespace namespace
     * @param key key
     * @param loader value loader
     * @return value
     */
    <T> T getValue(String namespace, String key, ValueLoader<T> loader);
    
    /**
     * key的value被更新了,需要调用本方法给hot-key-server,进而广播给所有人
     * @param namespace namespace
     * @param key key
     */
    void keyUpdate(String namespace, String key);
    
    /**
     * key的value被删除了,需要调用本方法给hot-key-server,进而广播给所有人
     * @param namespace namespace
     * @param key key
     */
    void keyDelete(String namespace, String key);

性能

对于 Camellia-hot-key,我们进行了大量的优化和调优,主要包括如下:

  • 传输协议

    SDK 和 server 使用长链接(基于 netty4)和服务器进行交互,并且摒弃了 json/文本等协议,转而使用了更加精简的二进制协议,从而优化了性能(为了尽可能减少外部依赖,没有使用 pb 等第三方序列化库)。

  • 无锁化设计

    SDK 到 server 有一层 hash,保证相同 key 被相同 server 处理。此外,server 在收到消息后,同样会根据 key 进行 hash 分流,从而相同 key 只会被一个线程处理,极大的简化了滑动窗口的设计,也避免了锁(全流程无锁化)。

  • JDK-17

    我们使用 jdk8 和 jdk17 分别进行测试,发现相同吞吐量的情况下,jdk17 比 jdk8 有更低的 CPU 占用。

以下是一个简单的性能测试结果:

 

我们都怎么使用

作为一个通用的热 key 探测框架,智企内部多条业务线均接入了该框架,并且通过相关自定义接口无缝的接入到了已有的系统中,以 IM 为例,我们在以下业务流程中接入了热点探测服务:

  • 对于来自 IM-SDK 的请求,根据 uid+租户 id+接口以及租户 id+接口做了两个维度的探测,从而识别异常的 C 端客户和异常的租户。

  • 对于来自 IM-openAPI 的请求,根据租户 id+接口做了探测,从而识别异常的租户和异常接口。

  • 底层数据库层面,以 db 为例。我们通过 mybatis 的 plugin 插件,做到了以业务不侵入的方式接入热点探测功能,探测的 key 我们采用如下方式进行拼装:type #租户 id#sql#param,这样做的好处,一方面可以在识出热点后,可以快速定位来源租户,另一方面也可以对 select/update/insert/delete 等不同 SQL 操作类型以及不同的租户设置不同的规则

  • 缓存方面,以 redis 为例。除了 redis-key 本身之外,为了方便定位来源,还会把租户 id 拼装到探测 key 里面,特别的,对于 dao_cache,还集成了 CamelliaHotKeyCacheSDK,从而在必要的时候开启 cache 功能保护 redis。

上面讲的是输入,至于输出,基于框架提供的自定义接口,我们对接到了内部的监控报警系统,方便第一时间感知到热点;并且也会把数据实时写入到数据平台,方便后续进行追溯。未来还会对接到频控流控系统,从而可以在感知到异常流量后第一时间自动进行屏蔽。

总结

为什么要开源

在开发 camellia-hot-key 框架时,我们就将其作为开源项目 camellia 的一部分进行设计,在不夹杂业务逻辑的情况下,精简内核,从而方便大家可以在不修改源码的情况下直接对接到各自的系统中。

我们期望有更多的人使用,而不仅仅是被网易智企一家公司使用,我们期望能发挥他的最大功能和价值。

因此欢迎各位开源爱好者积极来找茬,大家一起对 camellia 进行不断的完善和改进,从而做到真正的共赢。

体验试用

camellia 是云信的一个开源项目,除了 hot-key 之外,还有 redis-proxy、id-gen、delay-queue 等诸多被生产环境充分验证的组件,欢迎大家一键三连:点赞(Star)、关注(PR)、评论(issue)!

github 地址:https://github.com/netease-im/camellia

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/698433.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

虚幻引擎(UE5)-大世界分区WorldPartition教程(四)

文章目录 前言一、Data Layers的使用1.添加Actor到Data Layers2.运行时处理 总结 上一篇&#xff1a;虚幻引擎(UE5)-大世界分区WorldPartition教程(三) 前言 Data Layers&#xff08;UE4中叫Layers&#xff09;用于将Actor划分到不同的Layer中&#xff0c;通过在编辑器和运行…

Qt在Ubuntu下如何进行桌面软件开发?

文章目录 0.引言1.新建项目2.编写第一个程序3.在Qt外部启动程序 0.引言 笔者研究的方向涉及在ubuntu中运行代码&#xff0c;早先是直接利用控制台运行代码文件&#xff0c;在控制台中虽然设法将代码精简到一个三个文件中&#xff0c;只需要在控制台运行这三个文件即可&#xff…

应用在5W~20W无线充电器中的电机驱动芯片

随着用电设备对供电质量、安全性、可靠性、方便性、即时性、特殊场合、特殊地理环境等要求的不断提高&#xff0c;使得接触式电能传输方式越来越不能满足实际需要。无线充电器是利用电磁感应原理进行充电的设备&#xff0c;其原理和变压器相似&#xff0c;通过在发送和接收端各…

基于深度学习的高精度条形码二维码检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度条形码二维码检测识别系统可用于日常生活中或野外来检测与定位条形码二维码目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的条形码二维码目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系…

2023上半年软考系统分析师科目一整理-07

2023上半年软考系统分析师科目一整理-07 信息资源规划&#xff08;Information Resource Planning&#xff0c;IRP&#xff09;是信息化建设的基础工程&#xff0c;IRP强调将需求分析与&#xff08; &#xff09;结合起来。IRP的过程大致可以分为7个步骤&#xff0c;其中&#…

微信小程序组件间通讯

1.父传子 1.1父组件 1.1.1wxml文件 <!-- 定义的my-props组件 --> <my-props count"{{count}}"></my-props> <!-- 分割线 --> <view></view> <view>父组件的count值:{{count}}</view> <button bindtap"ad…

一步一步学OAK之五:通过OAK相机实现边缘检测

目录 边缘检测简介Setup 1: 创建文件Setup 2: 安装依赖Setup 3: 导入需要的包Setup 4: 创建pipelineSetup 5: 创建节点创建相机节点创建边缘检测节点创建XLinkOut数据交互的节点 Setup 6:设置相关属性设置彩色相机的相关属性设置左侧和右侧的单目相机的相关属性设置边缘检测器的…

【计算机网络 - 第三章】运输层

目录 一、多路复用和多路分解 1、运输层端口号 2、概述 3、原理 1、无连接的多路复用与多路分解 - UDP 2、面向连接的多路复用与多路分解 - TCP 二、无连接运输——UDP用户数据报协议 1、UDP概述 2、UDP的优点 三、可靠数据传输原理 1、概述 2、rdt1.0——可靠信道上…

【AUTOSAR】BMS开发实际项目讲解(十一)----电池管理系统相关项功能安全要求

相关项功能安全要求 SG-BMS-1 : BMS系统应防止电池单体过充导致热失控&#xff08;ASIL C&#xff09; 功能框图&#xff08;SG-BMS-1&#xff09; 功能组件说明 功能组件ID 功能组件名称 描述 ASIL等级 FSC-FC-01 Detection Cell Volt&Temp 采集表征单体电压和温度的…

Qt中QCompleter自动补全功能

在Qt中有QCompleter这个类可以和QLineEdit组合实现自动补全功能&#xff0c;类似搜索框形式的&#xff0c; 1.QCompleter类可以在输入框输入字符时&#xff0c;进行提示可以匹配上的字符 例&#xff1a;为QLineEdit设置自动补全QLineEdit* pLineEdit new QLineEdit(this);QStr…

【算法】最长递增子序列:动态规划贪心+二分查找

文章目录 最长递增子序列解法一&#xff1a;动态规划解法二&#xff1a;LIS 和 LCS 的关系解法三&#xff1a;贪心 二分查找 相关题目673. 最长递增子序列的个数 https://leetcode.cn/problems/number-of-longest-increasing-subsequence/1964. 找出到每个位置为止最长的有效障…

29.组件库 Element UI

Element UI是PC端常用的组件库&#xff0c;支持vue2与vue3&#xff0c;vue2项目使用的叫 Element UI,vue3使用的叫 Elements Plus&#xff0c;官网地址 一个 Vue 3 UI 框架 | Element Plus 我们下面的代码都是以vue3为例 目录 1 安装 2 引入 3 使用 1 安装 2 引入 完…

快消EDI:联合利华Unilever EDI需求分析

联合利华&#xff08;Unilever&#xff09;是一家跨国消费品公司&#xff0c;总部位于英国和荷兰&#xff0c;在全球范围内经营着众多知名品牌&#xff0c;涵盖了食品、饮料、清洁剂、个人护理产品等多个领域。作为一家跨国公司&#xff0c;联合利华在全球各地都有业务和生产基…

el-table表单一键展开折叠,展开部分后一键全部展开或折叠

实现功能&#xff1a; 1.表单一键展开或者一键折叠 2.表单点击展开一部分后&#xff0c;再次点击展开或折叠按钮可以全部展开或全部折叠 3.完整代码在最后 1.建立el-table的树形结构 1.ref"table"&#xff0c;用节点绑定的方式实现 2.data&#xff1a;树形结构…

Redis系列--数据过期清除策略缓存淘汰策略

一、过期策略 一、前言 Redis 所有的数据结构都可以设置过期时间&#xff0c;时间一到&#xff0c;就会自动删除。可以想象里面有一个专门删除过期数据的线程&#xff0c;数据已过期就立马删除。这个时候可以思考一下&#xff0c;会不会因为同一时间太多的 key 过期&#xff0…

windows电脑设置每天自动关机

有时候我们需要我们的笔记本或者电脑在每天固定的时间自动关机&#xff0c;但是windows本身是没有带这个设置的&#xff0c;下面记录下如何设置电脑每天自动关机&#xff0c;无需安装任何第三方软件&#xff1b; 文章目录 一、设置自动关机程序二、取消自动关机三、Windows任务…

chatgpt赋能python:Python连接表

Python连接表 Python作为一种高级编程语言&#xff0c;可以用于各种各样的任务。其中之一就是连接表格数据。连接表是在数据管理中非常重要的概念&#xff0c;因为它可以将不同表格中的数据合并在一起&#xff0c;从而使我们能够更好地分析和理解数据。在这篇文章中&#xff0…

蓝桥杯专题-试题版含答案-【猜算式】【排列序数】【还款计算】【滑动解锁】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

探索Gradio的CheckboxGroup模块:交互式多选框组件

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

详解金融机构敏捷营销落地:体系架构、关键技术和实施方法

本文根据 2023 年金融营销科技价值发现论坛中&#xff0c;神策数据金融首席架构师王仕的主题演讲整理所得&#xff0c;聚焦营销 5.0 理念下的敏捷营销&#xff0c;详细讲解金融机构落地时涉及到的体系架构、关键技术及实施方法。 根据国家发改委官网披露的数据&#xff0c;2022…