点击上方蓝色字体,选择“设置星标”

优质文章,第一时间送达

背景

当前业务需要使用 Oauth 授权的 accessToken 调用第三方系统获取用户资料。这里简化说明完成和第三方系统的对接需要两个接口。

  1. 通过 appkey 获取 accessToken。 accessToken 2 小时过期,接口有 quota 限制。

  2. 使用 accessToken 获取用户信息,这个 token 是通用的,只要是当前 appkey 下面的用户都可以使用。

设计

  1. 考虑到有 quota 限制问题,所以一定要有缓存机制,不然获取用户信息的接口请求量大的时候 quota 就不够用。

  2. accessToken 有两个小时过期问题,所有缓存要有失效机制。

  3. 一个 appkey 有且只有一个 accessToken,并且不常变,可以考虑直接使用内存缓存。如果使用 Redis等分布式缓存系统,也会因为频繁网络请求。

下面的表格来源于 Jeff Dean的一个PPT,里面罗列了不同级别的IO时间,这正是我们评估如何设计系统的必要因素。

L1 cache reference 0.5 ns
Branch mispredict 5 ns
L2 cache reference 7 ns
Mutex lock/unlock 100 ns
Main memory reference 100 ns
Compress 1K bytes with Zippy 10,000 ns 0.01 ms
Send 1K bytes over 1 Gbps network 10,000 ns 0.01 ms
Read 1 MB sequentially from memory 250,000 ns 0.25 ms
Round trip within same datacenter 500,000 ns 0.5 ms
Disk seek 10,000,000 ns 10 ms
Read 1 MB sequentially from network 10,000,000 ns 10 ms
Read 1 MB sequentially from disk 30,000,000 ns 30 ms
Send packet CA->Netherlands->CA 150,000,000 ns 150 ms

由上面表格,我们可以清楚的看出从网络上面获取1M数据和从内存中读取1M数据的差别。为什么说到这里呢,因为随着我们的用户的增加,集群的扩展,很少的情况下是把缓存数据库或者其他缓存中间件和应用程序放在一台服务器上,大部分情况都是分布式的应用系统和缓存系统,所以避免不了的我们需要考虑网络而的开销。
回到当前话题,对于一个 accessToken 占用内存足够小,即便是分布式系统每一个系统中都存储一个也不为过,只是能解决过期更新问题就好了。

实现

正好 Guava 就提供了这个功能, GuavaCache 缓存类似于 ConcurrentMap,但不完全相同。最根本的区别是, ConcurrentMap 会持续添加到其中的所有元素,如果你不手动删除它们会一直存在。然而 GuavaCache 可以通过缓存的大小,过期时间,或者其他策略自动地移除元素,来限制其内存占用。

引入依赖

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>19.0</version>
</dependency>

编写实现类

public class UserInfoProvider {public static void main(String[] args) {for (int i =0;i<100;i++) {String accessToken = new AccessTokenProvider().getAccessToken();System.out.println(accessToken);try {Thread.sleep(1000L);} catch (InterruptedException e) {e.printStackTrace();}}}
}
class AccessTokenProvider {private final static String KEY = "key";static Cache<String, Optional<String>> cache = CacheBuilder.newBuilder().expireAfterWrite(3, TimeUnit.SECONDS).removalListener(new RemovalListener<String, Optional<String>>() {@Overridepublic void onRemoval(RemovalNotification<String, Optional<String>> notification) {System.out.println("cache expired, remove key : " + notification.getKey());}}).build();public String getAccessToken() {try {Optional<String> stringOptional = cache.get(KEY, new Callable<Optional<String>>() {@Overridepublic Optional<String> call() throws Exception {return Optional.of(getRemoteAccessToken());}});return stringOptional.or("");} catch (ExecutionException e) {return null;}}private String getRemoteAccessToken() {return "accessToken:" + new Random().nextInt(1000);}
}

简单对上面逻辑进行讲解

  1. getRemoteAccessToken 是模拟远程获取 token

  2. getAccessToken 是先从 cache 中获取,如果没有获取到,从远程获取。

  3. expireAfterWrite(3,TimeUnit.SECONDS) 是3秒过期,这是为了测试。

  4. removalListener 监听过期。

  5. UserInfoProvider,sleep 3秒看一下更新情况。

由下面日志可见执行情况

accessToken:651
accessToken:651
accessToken:651
cache expired, remove key : key
accessToken:639
accessToken:639
accessToken:639
cache expired, remove key : key
accessToken:850
accessToken:850
accessToken:850

我们使用的还只是 GuavaCache 的冰山一角,它可以支容量和时间多种策略配置回收策略,同时它是良好的 LRU 实现。如上场景比较简单,如果你考虑缓存其他需求,需要考虑 refreshAfterWritemaximumSize 等配合使用,避免缓存击穿和性能问题。

原理

GuavaCache 的设计和 ConcurrentHashMap 非常类似,使用多个 segments 方式的细粒度锁,在保证线程安全的同时,支持高并发场景需求。看过 ConcurrentHashMap 实现原理的朋友跟进代码一看了然。当然它的设计更复杂一些,跟进代码的话也比较简单,在 get 的时候他会根据不同的策略进行比对是否过期,如果过期再进行相应的通知操作。同时他巧妙的使用的 WeakReference,这样可以利用 GC 来做删除数据的通知。

推荐阅读
你亲手写的代码,正在出卖你

深夜,聊聊架构设计

深夜,分享一个Git小技巧

编程·思维·职场
欢迎扫码关注

并不是只有 Redis 才可以做缓存相关推荐

  1. 常见面试题------Redis为什么可以做缓存?

    Redis为什么可以做缓存? 1.Redis将其数据库完全保存在内存中,仅使用磁盘进行持久化.与其它键值数据存储相比,Redis有一组相对丰富的数据类型.Redis可以将数据复制到任意数量的从机中. ...

  2. 搭建redis给mysql做缓存

    安装redis的前提是lnmp或者lamp的环境已经搭建完成. 安装redis 1.安装redis(或可以选择yum安装) 1 2 3 4 5 6 7 8 9 10 11 12 [root@redis ...

  3. Redis是如何做缓存的

    前言 基于Redis的高性能特性,我们将Redis用在缓存场景非常广泛.使用起来方便,响应也是远超关系型数据库. 但我们用Redis做缓存时,也要注意各种问题的应对和措施,比如缓存失效.数据一致性问题 ...

  4. java 缓存分页_基于redis做缓存分页

    在实际业务中我们会将一些热数据缓存到redis里面,这时候数据量比较大的话,我们就要对这些热数据进行分页,分页的方式有2种: 第一:从redis拿出所有数据后,再做内存分页(不推荐),热点数据小的时候 ...

  5. 9.redis 做缓存

    全网最新相关内容 标题-链接 发布日期 简介 来源 轻松理解redis做缓存的流程_小浩丶的博客-CSDN博客_redis ... 2022-4-5 22:59:03 1. 引言 缓存有啥用?降低对数 ...

  6. SpringBoot集成Shiro前后端分离使用redis做缓存

    文章目录 一 .shiro介绍 1.基础介绍 2.基本功能点 3.基本流程图 二. 常用的权限管理表关系 2.1. 表组成 2.2. 表结构 三.实战案例 3.1. 案例介绍 3.2. 依赖 3.3. ...

  7. 22-08-06 西安 尚医通(03)EasyExcel; Spring Cache 、Redis做缓存

    EasyExcel EasyExcel:一行一行读取到内存 EasyExcel是阿里巴巴开源的一个excel处理框架,以使用简单.节省内存著称 POI:java里操作excel,读取.创建excel ...

  8. redis夺命连环问10--说说Redis是怎么做旁路缓存的?

    目录 相关前置知识文章 说说Redis是怎么做旁路缓存的? 先谈缓存大概怎么做 再谈旁路缓存两种模式 redis和mysql如何保证数据一致性? 那怎么解决缓存和数据库的数据不一致问题? 如何保证缓存 ...

  9. 如果redis哨兵宕机了怎么办_Spring集成Redis做缓存,Redis宕机时Spring处理的问题

    采用的是Spring自带的缓存管理,使用Redis做缓存,在Spring中配置如下 @Configuration @EnableCaching public class CachingConfig { ...

最新文章

  1. 这道算法题太简单?你忽略了时间复杂度的要求!
  2. EUV光刻机全球出货量达57台
  3. Exchange 2010 CAS安装之一
  4. 2021-07-15
  5. visual basic.net 2019-Visual Basic 16.0新功能
  6. figma设计_Figma与Adobe XD:我们如何选择下一个设计工具
  7. 机智云小程序启蒙:WebSocket网页控制
  8. java情书_Java情书已写好,就差妹子了!
  9. php呼叫平台,php – Twilio呼叫转发
  10. Php网站手机能wifi上不了,解决手机能连上WIFI却上不了网故障
  11. iOS常用的功能(打电话、发短信、发邮件等)
  12. 重磅 | 消灭所有马赛克,谷歌宣布机器学习图像锐化工具RAISR
  13. 白盒测试哪种测试效果好_软件测试选择哪种测试方法比较好?
  14. 已知某一点坐标、线段长度和旋转角度,求另一点坐标
  15. 显示农历天气时钟小部件下载_轻松天气安卓版下载|轻松天气app下载_v2.0
  16. Java基础练习(八)字符串常用方法的使用
  17. 一个好的Java程序员,是无码胜有码,你懂这话的意思吗?
  18. java 打折_Java 为超市打折水果编写一个购物程序
  19. 构造函数、深拷贝、浅拷贝
  20. HashMap JDK1.7和JDK1.8的区别

热门文章

  1. 蓝桥杯历届试题-蚂蚁感冒
  2. 【H.264/AVC视频编解码技术详解】一. 视频信息与压缩编码
  3. gammatone滤波器 matlab,Sleepwalking_新浪博客
  4. 整数分解成若干项之和(DFS)拓展延伸
  5. CefSharp 中断点 已达到中断点
  6. 问题 D: 扑克牌排序(结构体)
  7. 第三课:电通量和高斯定律
  8. 保密相册计算机,‎加密相册(新版) - 密码计算机照片保险箱 im App Store
  9. centOS 7.5配置虚拟机静态IP,网卡不显示IP、显示IP与设置不一致及ping不同内外网的问题解决过程
  10. mysql从库有张表不同步_mysql主从数据库不同步的2种解决方法