ehcacheh缓存

谁践踏了优雅 2022-02-28 01:24 54阅读 0赞

redis -> nosql 数据库
-> 分布式的缓存

1. 缓存简介

斐波那契

提高效率的一种方式, 将计算结果进行缓存

  1. // key 是n value 是 fbnq(n)
  2. static Map<Integer, Integer> map = new HashMap<>();
  3. // 1 1 2 3 5 8 13 ...
  4. // 递归实现
  5. public static int fbnq(int n) {
  6. if(n==1 || n==2) {
  7. return 1;
  8. }
  9. // 获取n的 fbnq结果
  10. Integer value = map.get(n);
  11. if(value == null) {
  12. // 首次执行, 将结果放入缓存
  13. System.out.println("调用了fbnq..." + n);
  14. value = fbnq(n - 1) + fbnq(n - 2);
  15. map.put(n, value);
  16. }
  17. return value;
  18. }
  19. public static void main(String[] args) {
  20. int fbnq = fbnq(7);
  21. System.out.println(fbnq);
  22. }

2. 缓存简单实现

如何实现一个 LRU (最近最少使用 元素,会优先从缓存中踢掉) 缓存

1 2 3 4 5 key

get(1)

2 3 4 5 1 再加入 6

3 4 5 1 6
LRU 是一种缓存淘汰算法:既会考虑元素放入缓存的时间先后(先放入的先淘汰),还会考虑元素的命中次数(命中次数少的应当优先淘汰)

利用 LinkedHashMap 来实现了简单的 LRU 缓存

  1. // 默认模式: accessOrder=false 可以保持元素放入集合时的顺序
  2. // accessOrder=true 可以根据访问(get方法)调整元素在集合中的顺序, 最新访问的排在最后
  3. // 还要重写 removeEldestEntry 方法, 当方法返回为true 时, 表示要移除最老的元素
  4. LinkedHashMap<String, String> map = new LinkedHashMap(16, 0.75f, true){
  5. @Override
  6. protected boolean removeEldestEntry(Map.Entry eldest) {
  7. // 希望集合中放5个元素, 超过5个要移除最老的元素
  8. if(this.size() <= 5) {
  9. return false;
  10. } else {
  11. return true;
  12. }
  13. }
  14. };

缓存分类:

  • 对象缓存 (缓存的是一个个对象)
  • 页面缓存 (缓存的是一张张页面)

3. 对象缓存

常见的缓存实现: ehcache, oscache, jboss cache

ehcache 2.x
ehcache 3.x (*)

3.1 ehcache 缓存

  • 添加 maven 依赖


    org.ehcache
    ehcache
    3.6.3



    javax.cache
    cache-api
  • 添加配置文件-ehcache.xml

    <?xml version=”1.0” encoding=”UTF-8”?>




    java.lang.String
    java.lang.String



    200






  • 创建 cacheManager 缓存管理器

  • 获取 cache 对象(具体缓存)
  • 存、取、移除元素

    // 1. 类对象.getResource() 获取类路径上的一个资源文件(url对象)
    URL url = GunsApplication.class.getResource(“/ehcache.xml”);
    // 2. 准备配置对象
    XmlConfiguration config = new XmlConfiguration(url);
    // 3. 创建一个新的缓存管理器类
    CacheManager cacheManager = CacheManagerBuilder.newCacheManager(config);

    // 4. 初始化缓存管理器
    cacheManager.init();

    // 5. 获取缓存对象
    Cache cache = cacheManager.getCache(“cache1”, String.class, String.class);

    // 6. 存储键值
    cache.put(“beijing”, “北京”);

    // 7. 根据键获取值
    System.out.println(cache.get(“beijing”));

    // 8. 删除键
    cache.remove(“beijing”);
    System.out.println(cache.get(“beijing”));

    // 9. 清空缓存
    cache.clear();

3.2 与 springboot 整合

  1. spring:
  2. cache:
  3. jcache:
  4. config: ehcache.xml
  5. spring.cache.jcache.config=ehcache.xml
  6. @EnableCaching -- 启用缓存功能(内部就会创建 cacheManagerspring中), 并交给spring容器管理)

在其他需要用到缓存的代码中,就可以利用 cacheManager,来使用缓存,例如:

  1. @Autowired
  2. private CacheManager cacheManager;
  3. public Dept detail(@PathVariable("deptId") Long deptId) {
  4. Cache cache = cacheManager.getCache("cache1");
  5. Cache.ValueWrapper value = cache.get(String.valueOf(deptId));
  6. if(value != null) {
  7. Dept dept = (Dept) value.get();
  8. return dept;
  9. } else {
  10. Dept dept = deptService.getById(deptId);
  11. cache.put(String.valueOf(deptId), dept);
  12. return dept;
  13. }
  14. }

上面的代码虽然实现了缓存功能,但发现有太多重复代码,所以利用aop思想改造它。
spring 已经把缓存的切面类写好了,我们只需要通过一些注解标记哪些方法需要缓存

  • @Cacheable 加在需要将结果进行缓存的方法上

执行流程:

  1. /** * 1) 请求过来, 先访问的是 DeptController 的代理对象 (cglib 生成 DeptController 的子类对象作为代理对象) * 2) 代理对象重写了 detail, 在 detail 方法内, 通过 缓存管理器 找到名为 cache2 的缓存 * 3) Cache.get(deptId) 去获取 value, 第一次访问 value 为空, 放行执行真正的 DeptController 的 detail 方法 * 4) 真正的 DeptController 的 detail 方法返回结果作为 value 放入 cache2 缓存 * * 5) 第二次请求过来, 先访问的是 DeptController 的代理对象 * 6) 代理对象重写了 detail, 在 detail 方法内, 通过 缓存管理器 找到名为 cache2 的缓存 * 7) Cache.get(deptId) 去获取 value, 返回不为空, 直接返回缓存中的 value, 没有执行真正的 DeptController 的 detail 方法 */
  2. @RequestMapping(value = "/detail/{deptId}")
  3. @ResponseBody
  4. @Cacheable("cache2")
  5. public Dept detail(@PathVariable("deptId") Long deptId) {
  6. return deptService.getById(deptId);
  7. }
  • @CacheEvict - 用来删除某个key value,或者清空整个缓存, 用在执行了数据的增删改时,这三种情况下,都应该让缓存失效

    • 有时,需要将整个缓存清空, 这时使用 @CacheEvict(allEntries=true)
  • @CachePut - 也是加在增删改方法之上,执行增删改方法,把方法的返回值当做value,用它更新缓存的内容
  • key 的生成,默认使用的方法参数作为key,但容易产生key冲突问题
    cache2 /dept/detail/30 30->Dept
    cache3 /user/detail/30 30->User

    // 30 dept30
    // 29 dept_29
    // #deptId 就是在方法参数中找一个名为 deptId 的参数, 取得值后前面拼接 一个 ‘dept
    ‘ 字符串, 拼接后的结果作为 key
    // 这种写法称之为 spring 表达式, SPEL (spring expression language)
    @Cacheable(cacheNames = “cache2”, key = “‘dept_’ + #deptId”)

压测工具 jmeter(java语言编写的)

缓存的使用场景

  • 提高系统吞吐量的有效手段
  • 经常查询,但很少修改的数据,需要配置缓存,经常修改的数据,不要配置缓存,反而会影响增删改效率 (读多写少)
  • ehcache 经常用于本地缓存(单机)
  • 如果数据要被多台机器共享访问,需要用到分布式缓存 redis
  • 要注意数据一致性问题

    • 考虑性能,不用加锁,能够保证最终一致性即可(中间会有一小段时间读取到旧数据)
    • 对一致性要求高,加锁,但性能会受到影响

4. 页面静态化

把页面从动态页面,变为静态页面(静态页面会具备各种各样的缓存功能)

之前配置 springmvc.xml

  1. <beans>
  2. <!-- 视图解析器 -->
  3. <mvc:view-resolvers>
  4. <mvc:jsp prefix="" suffiex=""/>
  5. </mvc:view-resolvers>
  6. <!-- 拦截器 -->
  7. <!-- 静态资源配置 -->
  8. <mvc:default-servlet-handler/>
  9. <!-- 上传文件解析器 -->
  10. <mvc:resources mapping="url路径" location="服务器磁盘路径"/> <!-- 把一个web路径映射到服务器上的一个静态资源 -->
  11. </beans>

通过java 类配置

  1. @Configuration // 表示他是一个配置类
  2. public class MyWebConfig implements WebMvcConfigurer { // 这个接口中有一些抽象方法,分别对应xml中的各项配置
  3. // <!-- 静态资源配置 -->
  4. @Override
  5. public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
  6. configurer.enable();
  7. }
  8. // <!-- 拦截器 -->
  9. public void addInterceptors(InterceptorRegistry registry) {
  10. }
  11. ...
  12. }

4.1 浏览器缓存

// 最强,离最终使用者越近,缓存带来的性能提升越高

// 调试快捷键 f5-普通刷新 ctrl+f5-强制刷新

  1. 第一次请求,从服务器得到了一个静态页面,把静态页面放入浏览器的缓存
  2. 后续访问这个页面,不会发送请求给服务器,直接从浏览器的缓存中获取这个静态页面
  3. 点击 f5 刷新,仍然会从服务器获取
  4. html 缓存有一个 max-age的最大存活期,超过了这个时间就会绕过缓存去找服务器了

4.2 cdn 缓存

网络请求静态资源时,常常不需要真正到达最终服务器,会找到一个离自己最近的cdn服务器,由cdn服务器返回结果
cdn中缓存的大部分都是静态资源(图片,html等)

4.3 304

请求确实发送给服务器了,但服务器发现网页并没有发生变化,返回一个304状态码(告诉浏览器,这个网页没有被修改,请从自己缓存中获取网页),网页的正文并没有传输

4.4 localstorage

html5 引入的一个js对象
服务器如果返回的是json, 希望缓存这些数据的话,可以将其存入 localStorage 中,即使浏览器关闭,localStorage中的内容仍然存在

发表评论

表情:
评论列表 (有 0 条评论,54人围观)

还没有评论,来说两句吧...

相关阅读