在这段不到3分钟的视频中,总有一款FPS游戏能让你大喊“爷青辉”
06-21
在海量并发用户的系统中,热键一直是不可避免的问题。也许是因为某些产品突然成为爆款,也许是某个店铺突然大量涌入用户,又或者是大量爬虫用户瞬间开启限时抢购。
这些无法提前感知的突发热键,对于系统来说都是潜在的巨大风险。 。
有哪些风险?主要是数据层,其次是服务层。热键对数据层的影响是显而易见的。
例如,如果数据存储在redis或mysql中,以redis为例,根据哈希规则,未知的热点数据会存在于某个redis分片上,日常使用时会从这个分片中获取。它的数据。
由于redis的性能相当不错,再加上集群模式,我们假设它可以支持每秒20万次读取,这足以支持大多数日常使用。然而,这些领先的互联网公司,比如京东,往往有一款爆款产品,每秒会瞬间引入数百万甚至数百万个请求。
当然,大部分流量会在几秒钟内消失。但仅仅几秒的热键就会瞬间导致所在的redis分片集群瘫痪。
原因也很简单。 redis作为单线程结构,所有请求到达时都会排队。
当请求量远大于自身处理能力时,后续请求就会陷入等待超时。由于redis分片完全被该key的请求填满,该分片上的所有其他数据操作都无法继续提供服务。
也就是说,热键不仅影响自身,还影响与其共享的数据。显然,在这个极短的时间窗口内,我们无法快速将redis扩容10倍以上来支持这个热点。
虽然redis已经很优秀了,但它的内心是这样的:热键对服务层的影响不可小觑。比如你本来有一台Tomcat,每一个都可以支持每秒QPS。
假设数据层稳定,服务层每秒可以处理数万个请求。然而,由于某款爆款产品的出现,或者由于一次大促销,大量机器人突然发起极其密集的请求,速度远超正常用户。
这些机器人可以以极小的成本发出普通用户一百倍的请求。量,从而极大地占用了普通用户的资源。
本来可以处理1万个请求,现在有1万个请求,其中50万个是机器人请求,导致至少1/3的正常用户无法访问,导致用户体验很差。根据以上场景,我们可以总结一下什么是有害热键。
什么是热键 1 MySQL等数据库中会被??频繁访问的热数据,如热门商品的skuId 2 Redis中被密集访问的Key,如热门商品的各种维度信息,skuId、shopId等 3 机器人、爬虫,刷用户如userId、uuid、ip等。 4 一个接口地址,如/sku/query或者更精细的维度 5 用户id+接口信息,如userId + /sku/query,代表访问频率访问接口的用户 6 服务器id + 接口信息 例如ip + /sku/query,代表某台服务器的某个接口被访问的频率。
7 用户ID+接口信息+具体产品。例如userId + /sku/query + skuId,代表某个用户访问某个产品的频率。
我们把上面的都称为风险键。请注意,我们的热键检测框架只关心键,它实际上是一个字符串。
由用户决定如何组合成这个字符串,因此该框架非常灵活。可以完成热数据检测、限流熔断、统计等多种功能。
以往如何解决热键问题,是基于redis的热键、刷用户、限流等典型场景。 Redis热键:过去的这种解决方案比较常见。
比较常见的有: 1》上传二级缓存。读取redis的key-value信息后,直接写入jvm缓存,并设置一个过期时间。
,设置淘汰策略,比如当队列满时淘汰最先加入的。特点是无脑缓存,不关心数据是否热点。
缓存的数据无法在应用集群内实现一致性。 2》重写redis源码,添加热点检测功能,有热点时推送到jvm。
主要问题是不通用,有一定的难度。 3》重写jedis、letture等redis客户端的jar,通过本地计算检测热键。
如果密钥很热,它们会被缓存在本地并通知集群中的其他机器。 4》其他刷爬虫用户:常见的有: 1》日常积累后,通过配置中心将这些黑名单推送到jvm内存中。
存在无法实时感知的滞后问题。 2》通过本地累加,实时计算,单位时间内任何超过阈值的值都算作刷子。
如果服务器很多,用户请求就会分散,本地计算无法识别刷机。 3》引入redis等其他组件进行集中累计计算,超过阈值的拉取到本地内存。
问题是需要频繁的对redis进行读写,并且redis仍然存在性能瓶颈。限流: 1》单机维度的接口限流多采用本地累加计数 2》集群维度多采用第三方中间件,如sentinel 3》在网关层,如Nginx+lua 中总结一下,我们会发现,虽然都可以归结到热键领域,但是并没有统一的解决方案。
我们希望有一个统一的框架,能够解决所有场景的热键实时感知,最好是不管什么键或者什么维度?只要我拼接这个字符串,交给框架检测,并设置判断热键的阈值(比如2秒内该字符串出现20次),那么热键就可以在毫秒内进入。到应用程序的JVM内存中,并在整个服务集群中保持一致性。
必须全部存在,也必须全部删除。进入内存后热键的优点。
热键问题最终是如何找到热键并将热键放入jvm内存中。只要密钥在内存中,我们就可以很快地对其进行逻辑处理。
内存访问和redis访问的速度不是一个数量级的。比如对于刷机用户,我们可以屏蔽、降级、限制访问速度。
对于热接口,我们可以限制电流并恢复到默认值。借助redis的热键,我们可以大大提高访问速度。
以redis access key为例,我们可以很容易的计算出性能指标。例如,如果有一台服务器,某个key所在的redis集群可以支持20万/s的访问,那么平均每台机器每秒大约可以访问该key几次。
,超出的部分将进入等待状态。由于redis的瓶颈,服务器的性能会受到很大的限制。
而如果key在本地内存中,每秒读取内存中的一个值几万次是很正常的,数据层不存在瓶颈。当然,如果增加redis集群的规模,也可以提高数据访问的上限,但问题是你事先不知道热键在哪里,而全面增加redis的规模会带来难以接受的成本增加。
热键检测的关键指标1:实时性这个很容易理解。按键往往会突然瞬间变热,而你没有机会手动将热键添加到配置中心,然后推送到 JVM。
大多数时候它是不可预测的,而且来得很快。也许某个商户有活动,瞬间就出现了热键。
如果短时间内无法访问内存,则存在redis集群被破坏的风险。因此,热键检测框架最重要的就是实时性。
最好是某个key刚刚出现热迹象,并在1秒内进入整个服务集群的内存。 1秒后,将不再密集。
访问redis。对于刷机用户来说也是如此。
当我开始刷牙时,我在1秒内就禁用了它。2 准确性 这是非常重要且容易实现的。
累加数量,确保不误检,准确检测,确保检测到的热键完全符合用户设定的阈值。 3 集群一致性 这个比较重要,尤其是在一些有删除key的场景。
删除某个key时,整个集群中的key都会被删除,避免数据错误。 4.高性能 这是核心之一。
高性能带来低成本。热键检测的目的是减轻数据层的负载,提高应用层的性能,节省服务器资源。
否则大家就只能整体扩大redis集群的规模了。理论上,在不影响实时性能的情况下完成实时热键检测所消耗的机器资源越少,经济价值就越大。
京东热键检测框架架构设计在经历了多次数据层服务被突然海量请求淹没的场景,并且始终面临爬虫、刷机机器人用户的大量请求后,我们根据现有经验,设计开发了一套通用的轻量级框架。先进的热键检测框架——JdHotkey。
它非常轻量级,既不改变redis源码,也不改变redis客户端jar包。当然,与redis无关,根本不依赖于redis。
它是一个独立的系统。部署完成后,将jar引入到服务器代码中,然后像本地HashMap一样使用。
框架本身会完成一切,包括上报要测试的按键、推送热键、缓存本地热键、过期和淘汰策略等。框架会告诉你是否是热键,其他逻辑你可以自己实现。
具有很强的实时性。默认情况下,ms可以检测要测试的按键是否为热键。
如果是热键,就会进入jvm内存中。当然,我们还提供了更快的频率设置方法。
一般情况下,如果不是极端场景,建议保持默认值。更高的频率带来更大的资源消耗。
它具有强大的性能。一台8核8G的机器在承担框架的热键检测和计算任务(即下架构图中的worker服务)时,每秒可以100%处理数千台服务器发送的数据。
测试16万个按键,8核机器的吞吐量为16万,16核机器每秒可以达到30万次以上的检测。当然,前提是CPU非常稳定。
高性能代表着低成本,所以我们只用10台机器就能完成每秒近万个关键检测任务。一旦找到热键,访问数据所花费的时间就和redis不在一个数量级了。
。如果我们添加一个redis集群呢? QPS从20万提升到1万,需要扩容多少台服务器?框架主要由4部分组成: 1.etcd集群。
作为一个高性能的配置中心,etcd 可以以最少的资源占用提供高效的监控和订阅服务。主要用于存储规则配置、各个worker的IP地址、检测到的热键、手动添加的热键等。
2 客户端jar包是添加到服务中的引用jar。介绍完后,您可以使用一种便捷的方式来判断某个按键是否为热键。
同时该jar已经完成了key上报、监控etcd中的规则变化、worker信息变化、热键变化以及热键的本地cacheine缓存。 3 Worker端集群 Worker端是一个独立部署的Java程序。
启动后会连接etcd并定期上报自己的IP信息,供客户端获取地址并进行长连接。之后的主要任务是累积并计算每个客户端发送的待测试密钥。
当达到etcd中设置的规则阈值时,热键将被推送到每个客户端。 4 仪表板控制台 控制台是一个具有可视化界面的Java程序,它也连接到etcd。
之后在控制台中设置各个APP的关键规则。比如2秒20次就算热了。
然后当worker检测到热键时,就会将该热键发送给etcd,同时dashboard也会监控到热键信息并存储到数据库中保存记录。同时,仪表板还可以手动添加和删除每个客户端的热键进行监控。
综上可见,该框架不依赖任何定制组件,与redis无关。核心是依靠netty连接,客户端发送待测试的key,然后各个worker完成分布式计算,计算出热点key。
之后直接推送到客户端,非常轻量级。该框架的工作流程一:首先构建一个etcd集群。
etcd作为一个全局共享的配置中心,将允许所有客户端读取完全相同的worker信息和规则信息。 2 启动仪表板可视化界面。
在界面上添加每个APP需要测试的规则。例如,app1 包含两条规则。
一种是以userId_开头的key,比如userId_abc。如果每 2 秒出现 20 次,则视为热键。
第二个是如果skuId_每1秒出现一次以上,则认为是热键。只有符合规则的key才会被发送给worker进行计算。
3 启动worker集群worker集群可以配置APP级隔离,也可以不配置隔离。实现隔离后,应用程序只能使用这些Worker,避免其他应用程序之间的性能资源竞争。
Worker启动后,会从etcd中读取之前配置的规则,并持续监控规则的变化。然后,worker会定期向etcd报告其IP信息。
如果一段时间内没有报告,etcd 将会删除该worker 信息。 Worker报告的IP供客户端用于长期连接。
每个客户端根据应用程序在etcd中可以使用的worker信息进行长期连接,并且将要测试的密钥进行散列并根据worker数量均匀分配给每个worker。之后,worker开始接收并计算每个客户端发送的密钥。
当某个key达到规则中设置的阈值时,会推送到APP的所有客户端jar,然后推送到etcd进行dashboard监控和记录。 4 客户端 客户端启动后,会连接etcd,获取规则,获取独享的worker IP信息,然后继续监听该信息。
获取IP信息后,会通过netty与worker建立长连接。客户端会启动一个定时任务,每隔ms(可配置)将待测试的key批量发送到对应的worker机器上。
发送规则是key的hashcode是由worker的数量调制的,所以固定的key肯定会发送给同一个worker。在这几毫秒内,待测试的密钥及其数量被收集到本地,并在过期后批量发送出去。
请注意,除非本地密钥缓存已过期,否则不会再次发送热密钥。当工作人员检测到热键时,就会将其推送到该热键。
框架使用caffeine进行本地缓存,并且会根据原来设置的规则中的过期时间进行本地过期设置。当然,如果在控制台手动添加或删除热键,客户端也会对其进行监控,并添加或删除本地caffeine。
通过这种方式,每个热键在整个客户端集群内保持一致性。 jar包提供了外部方法来判断是否是热键。
如果是热键,那么你只需要关心自己的逻辑处理,是限制它的当前流量,降级它访问的部分接口,还是给它返回一个值。它依靠自己的逻辑处理,非常灵活。
请注意,我们只关心键本身,它只是一个字符串,而不关心值。我们只检测密钥。
那么这个时候肯定有一个问题。如果是redis的热键,框架会告诉我哪一个是热键,但不会给我值。
是的,框架提供了判断是否是热键的方法。如果是redis热键,则用户需要从redis中获取值,然后调用框架的set方法设置该值。
如果不是热键,则按照原来的逻辑即可。所以你可以把框架想象成一个带有热键的HashMap,但是你需要自己维护这个值。
综上所述,该框架采用了非常轻量级的方式,实现了毫秒级的精准热键检测,与集群规模一致,适用于大量场景。它可以用于任何需要对某些字符串进行热匹配的场景。
热键检测框架的性能表现。该密钥经历了多次大促压力测试、极端场景压力测试以及大促期间的在线使用。
在此期间,许多不常见甚至奇怪的问题都得到了解决。之前也发表过相关问题的总结。
文章。这里我们只简单介绍一下它的性能。
etcd 在 etcd 端的性能非常出色。官方宣称每秒读写次数可达数万次。
在实际使用中,我们仅按下热键并监视、读写其他少量信息。负载很轻。
有数千个客户端连接,并且通常在几秒钟内生成数百个热键。 CPU占用率不超过5%,大部分时间在1%左右。
Worker端 Worker端是框架的核心部分,也是分布式计算压力最大的部分。它需要根据第二级每个客户端发送的密钥总数来分配资源。
例如,如果每秒有10000个key需要测试,那么我们需要知道单个worker的处理能力,然后决定分配多少台worker机器来平均分担这些计算任务。这也是调优的核心领域。
qps越高,成本越低。我简单列出一些之前的测试数据。
8核8G Worker单机场景负载,totalDealCount为累计计算key数量(累计完成后的数量、推送热键给客户端等),totalReceiveCount为累计接收key数量(刚刚收到但未收到)尚未参与计算) .expireCount 是自客户端发送且工作人员收到以来已超过 5 秒而未参与计算的键的数量。以上每 10 秒打印一次。
可以看到每10秒处理量约为10000次。机器的CPU占用率达到70%左右,峰值区域大多是GC造成的。
总体而言,在这个压力水平下,我们认为不能再对其进行大幅加压。换成16核16G机器后,同样数据量保持10万秒不变,16核机器轻松多了。
CPU占用率在30%以上,整体负载比较轻。增加数据源后,10秒达到10000小时,CPU上升到40%以上,说明还有继续增加压力的空间。
经过后续的极压写入,我们验证单机可以在30万QPS以上的情况下稳定工作半小时以上。但CPU负载已经很高,存在不确定性风险。
这样的表现足以应对大多数“爆款”场景。综上所述,我们可以对性能给出一个简单的结论。
采用8核Worker机器,单机每秒可处理10万级关键检测计算和推送任务。使用16核机器每秒可以轻松处理20万个处理任务。
用户可以根据这个绩效标准来分配相应数量的worker。例如,如果您的应用程序每秒有 10,000 个请求,并且您要检测的维度是 userId 和 skuId,那么您需要估计有多少个 skuId 和 userId。
如果这10000个请求来自10000个不同的用户,每个用户访问不同的sku,那么就有数千个key需要测试。所以如果你需要10个工人的话会比较安全。
该框架已在京东APP后台上线使用,并经历了多次重大推广压力测试演练和重大促销活动。
版权声明:本文内容由互联网用户自发贡献,本站不拥有所有权,不承担相关法律责任。如果发现本站有涉嫌抄袭的内容,欢迎发送邮件 举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
标签:
相关文章
06-17
06-06
06-08
06-21
06-17
最新文章
【玩转GPU】ControlNet初学者生存指南
【实战】获取小程序中用户的城市信息(附源码)
包雪雪简单介绍Vue.js:开学
Go进阶:使用Gin框架简单实现服务端渲染
线程池介绍及实际案例分享
JMeter 注释 18 - JMeter 常用配置组件介绍
基于Sentry的大数据权限解决方案
【云+社区年度征文集】GPE监控介绍及使用