关注
HarmonyOS技术社区
,回复
【鸿蒙】
送
定制T恤
(礼品不多,先到先得)
,还可以
免费下载
鸿蒙
入门资料
!
👇
扫码
立刻关注
👇
专注开源技术,共建鸿蒙生态
一位七牛的资深架构师曾经说过这样一句话:“Nginx+业务逻辑层+数据库+缓存层+消息队列,这种模型几乎能适配绝大部分的业务场景。
图片来自 Pexels
这么多年过去了,这句话或深或浅地影响了我的技术选择,以至于后来我花了很多时间去重点学习缓存相关的技术。
我在 10 年前开始使用缓存,从本地缓存、到分布式缓存、再到多级缓存,踩过很多坑。下面我结合自己使用缓存的历程,谈谈我对缓存的认识。
我使用缓存的时间很早,2010 年左右使用过 OSCache,当时主要用在 JSP 页面中用于实现页面级缓存。
<cache:cache key="foobar" scope="session">
some jsp content
cache:cache>
中间的那段 JSP 代码将会以 key="foobar" 缓存在 session 中,这样其他页面就能共享这段缓存内容。
在使用 JSP 这种远古技术的场景下,通过引入 OSCache 之后 ,页面的加载速度确实提升很快。
但随着前后端分离以及分布式缓存的兴起,服务端的页面级缓存已经很少使用了。但是在前端领域,页面级缓存仍然很流行。
2011 年左右,开源中国的红薯哥写了很多篇关于缓存的文章。他提到:开源中国每天百万的动态请求,只用 1 台 4 Core 8G 的服务器就扛住了,得益于缓存框架 Ehcache。
这让我非常神往,一个简单的框架竟能将单机性能做到如此这般,让我欲欲跃试。
于是,我参考红薯哥的示例代码,在公司的余额提现服务上第一次使用了 Ehcache。
逻辑也很简单,就是将成功或者失败状态的订单缓存起来,这样下次查询的时候,不用再查询支付宝服务了。
添加缓存之后,优化的效果很明显 , 任务耗时从原来的 40 分钟减少到了 5~10 分钟。
上面这个示例就是典型的「对象缓存」,它是本地缓存最常见的应用场景。相比页面缓存,它的粒度更细、更灵活,常用来缓存很少变化的数据,比如:全局配置、状态已完结的订单等,用于提升整体的查询速度。
2018 年,我和我的小伙伴自研了配置中心,为了让客户端以最快的速度读取配置, 本地缓存使用了 Guava。
那本地缓存是如何更新的呢?有两种机制:
后来我阅读了 Soul 网关的源码,它的本地缓存更新机制如下图所示,
共支持 3 种策略:
Zookeeper Watch 机制:
soul-admin 在启动的时候,会将数据全量写入 Zookeeper,后续数据发生变更时,会增量更新 Zookeeper 的节点。
与此同时,soul-web 会监听配置信息的节点,一旦有信息变更时,会更新本地缓存。
Websocket 机制:
Websocket 和 Zookeeper 机制有点类似,当网关与 admin 首次建立好 websocket 连接时,admin 会推送一次全量数据,后续如果配置数据发生变更,则将增量数据通过 websocket 主动推送给 soul-web。
Http 长轮询机制
:Http 请求到达服务端后,并不是马上响应,而是利用 Servlet 3.0 的异步机制响应数据。
当配置发生变化时,服务端会挨个移除队列中的长轮询请求,告知是哪个 Group 的数据发生了变更,网关收到响应后,再次请求该 Group 的配置数据。
不知道大家发现了没?
长轮询是一个有意思的话题 , 这种模式在 RocketMQ 的消费者模型也同样被使用,接近准实时,并且可以减少服务端的压力。
关于分布式缓存, Memcached 和 Redis 应该是最常用的技术选型。相信程序员朋友都非常熟悉了,我这里分享两个案例。
2013 年,我服务一家彩票公司,我们的比分直播模块也用到了分布式缓存。当时,遇到了一个 Young GC 频繁的线上问题,通过 jstat 工具排查后,发现新生代每隔两秒就被占满了。
进一步定位分析,原来是某些 key 缓存的 value 太大了,平均在 300K 左右,最大的达到了 500K。这样在高并发下,就很容易导致 GC 频繁。
找到了根本原因后,具体怎么改呢?我当时也没有清晰的思路。于是,我去同行的网站上研究他们是怎么实现相同功能的,包括:360 彩票,澳客网。
我发现了两点:
再回到我的问题上,最终是用什么方案解决的呢?当时,我们的比分直播模块缓存格式是 JSON 数组,每个数组元素包含 20 多个键值对, 下面的 JSON 示例我仅仅列了其中 4 个属性。