java 项目做多级缓存_【开源项目系列】如何基于 Spring Cache 实现多级缓存(同时整合本地缓存 Ehcache 和分布式缓存 Redis)...

一、缓存

当系统的并发量上来了,如果我们频繁地去访问数据库,那么会使数据库的压力不断增大,在高峰时甚至可以出现数据库崩溃的现象。所以一般我们会使用缓存来解决这个数据库并发访问问题,用户访问进来,会先从缓存里查询,如果存在则返回,如果不存在再从数据库里查询,最后添加到缓存里,然后返回给用户,当然了,接下来又能使用缓存来提供查询功能。

而缓存,一般我们可以分为本地缓存和分布式缓存。

常用的本地缓存有 ehcache、guava cache,而我们一般都是使用 ehcache,毕竟他是纯 Java 的,出现问题我们还可以根据源码解决,并且还能自己进行二次开发来扩展功能。

常用的分布式缓存当然就是 Redis 了,Redis 是基于内存和单线程的,执行效率非常的高。

二、Spring Cache

相信如果要整合缓存到项目中,大家都会使用到 Spring Cache,它不但整合了多种缓存框架(ehcache、jcache等等),还可以基于注解来使用,是相当的方便。

缓存框架的整合在 spring-context-support 中:

cfbc05e67f6befa4120910a0f3853021.png

缓存注解在 spring-context 中:

d32f58d5e8d7705e8c58f1d3d69787e4.png

当然了,在 Spring 的 context 中没有整合 Redis,但是我们可以在 spring-data-redis 中找到。

0a2ebd666f4d3f635e393dae7ea89508.png

但是我们都知道,不管是在 Spring 项目 还是 Spring Boot 中,我们都只能整合一种缓存,不能同时整合多种缓存。

在 Spring Boot 中,我们一般是利用 spring.cache.type 来指定使用哪种缓存,然后填写相关配置信息来完成自动配置。

f46a7b0489ba6798da78bdccfc33f050.png

CacheType 的源码:我们可以看到,Spring 是支持非常多种缓存框架的。

package org.springframework.boot.autoconfigure.cache;

public enum CacheType {

GENERIC,

JCACHE,

EHCACHE,

HAZELCAST,

INFINISPAN,

COUCHBASE,

REDIS,

CAFFEINE,

SIMPLE,

NONE;

private CacheType() {

}

}

那么如果我们就是有这么一种需求,要整合两种缓存框架:例如一个本地缓存 Ehcache,一个分布式缓存 Redis,

那能整么?

能是能,但是 Spring 可不提供这种多级缓存,而是需要你自己动手来整了。

三、h2cache-spring-boot-starter

1、什么是 h2cache-spring-boot-starter?

在微服务中,每个服务都是无状态的,服务之间需要经过 HTTP 或者 RPC 来进行通信。而每个服务都拥有自己对应的数据库,所以说如果服务A 需要获取服务B 的某个表的数据,那么就需要一次 HTTP 或 RPC 通信,那如果高峰期每秒需要调用100次,那岂不是需要100次 HTTP 或 RPC 通信,这是相当耗费接口性能的。

那怎么解决呢?

本地缓存那是肯定不是的,因为一般不同服务都是部署在不同的机器上面的,所以此时我们需要的是分布式缓存,例如 Redis;但是,访问量高的的服务当然还是需要本地缓存了。所以最后,我们不但需要本地缓存,还需要分布式缓存,但是 Spring Boot 却不能提供这种多级缓存的功能,所以需要我们自己来整合。

不用怕,我已经自己整了一个 Spring Boot Starter了,就是h2cache-spring-boot-starter,我们只需要在配置文件配置上对应的信息,就可以启用这个多级缓存的功能了。

2、开始使用

添加依赖:

大家正常引入下面依赖即可,因为我已经将此项目发布到 Maven 中央仓库了~

com.github.howinfun

h2cache-spring-boot-starter

0.0.1

在 Spring Boot properties 启用服务,并且加上对应的配置:

开启多级缓存服务:

# Enable L2 cache or not

h2cache.enabled=true

配置 Ehcache:

# Ehcache Config

## the path of ehcache.xml (We can put it directly under Resources)

h2cache.ehcache.filePath=ehcache.xml

#Set whether the EhCache CacheManager should be shared (as a singleton at the ClassLoader level) or independent (typically local within the application).Default is "false", creating an independent local instance.

h2cache.ehcache.shared=true

配置 Redis:主要包括默认的缓存配置和自定义缓存配置

要注意一点的是:h2cache-spring-boot-starter 同时引入了 Lettuce 和 Jedis 客户端,而 Spring Boot 默认使用 Lettuce 客户端,所以如果我们需要使用 Jedis 客户端,需要将 Lettuce 依赖去除掉。

# Redis Config

## default Config (expire)

h2cache.redis.default-config.ttl=200

### Disable caching {@literal null} values.Default is "false"

h2cache.redis.default-config.disable-null-values=true

### Disable using cache key prefixes.Default is "true"

h2cache.redis.default-config.use-prefix=true

## Custom Config list

### cacheName -> @CacheConfig#cacheNames @Cacheable#cacheNames and other comments, etc

h2cache.redis.config-list[0].cache-name=userCache

h2cache.redis.config-list[0].ttl=60

h2cache.redis.config-list[0].use-prefix=true

h2cache.redis.config-list[0].disable-null-values=true

h2cache.redis.config-list[1].cache-name=bookCache

h2cache.redis.config-list[1].ttl=60

h2cache.redis.config-list[1].use-prefix=true

#Redis

spring.redis.host=10.111.0.111

spring.redis.password=

spring.redis.port=6379

spring.redis.database=15

# 连接池最大连接数(使用负值表示没有限制)

spring.redis.jedis.pool.max-active=8

# 连接池中的最小空闲连接

spring.redis.jedis.pool.min-idle=0

# 连接池中的最大空闲连接

spring.redis.jedis.pool.max-idle=8

# 连接池最大阻塞等待时间(使用负值表示没有限制)

spring.redis.jedis.pool.max-wait=30

如何使用缓存注解

我们只要像之前一样使用 Spring Cache 的注解即可。

for example:

代码里的持久层,我使用的是: mybatis-plus.

package com.hyf.testDemo.redis;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import org.springframework.cache.annotation.CacheConfig;

import org.springframework.cache.annotation.CacheEvict;

import org.springframework.cache.annotation.CachePut;

import org.springframework.cache.annotation.Cacheable;

import org.springframework.cache.annotation.Caching;

import org.springframework.stereotype.Repository;

/**

* @author Howinfun

* @desc

* @date 2020/3/25

*/

@Repository

// Global cache config,We usually set the cacheName

@CacheConfig(cacheNames = {"userCache"})

public interface UserMapper extends BaseMapper {

/**

* put the data to cache(Ehcache & Redis)

* @param id

* @return

*/

@Cacheable(key = "#id",unless = "#result == null")

User selectById(Long id);

/**

* put the data to cache After method execution

* @param user

* @return

*/

@CachePut(key = "#user.id", condition = "#user.name != null and #user.name != ''")

default User insert0(User user) {

this.insert(user);

return user;

}

/**

* evict the data from cache

* @param id

* @return

*/

@CacheEvict(key = "#id")

int deleteById(Long id);

/**

* Using cache annotations in combination

* @param user

* @return

*/

@Caching(

evict = {@CacheEvict(key = "#user.id", beforeInvocation = true)},

put = {@CachePut(key = "#user.id")}

)

default User updateUser0(User user){

this.updateById(user);

return user;

}

}

测试一下:

查询:我们可以看到,在数据库查询到结果后,会将数据添加到 Ehcache 和 Redis 缓存中;接着之后的查询都将会先从 Ehcache 或者 Redis 里查询。

2020-04-03 09:55:09.691 INFO 5920 --- [nio-8080-exec-7] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...

2020-04-03 09:55:10.044 INFO 5920 --- [nio-8080-exec-7] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.

2020-04-03 09:55:10.051 DEBUG 5920 --- [nio-8080-exec-7] c.h.t.redis.BookMapper2.selectById : ==> Preparing: SELECT id,create_time,update_time,read_frequency,version,book_name FROM book WHERE id=?

2020-04-03 09:55:10.068 DEBUG 5920 --- [nio-8080-exec-7] c.h.t.redis.BookMapper2.selectById : ==> Parameters: 51(Long)

2020-04-03 09:55:10.107 DEBUG 5920 --- [nio-8080-exec-7] c.h.t.redis.BookMapper2.selectById : <== Total: 1

2020-04-03 09:55:10.113 INFO 5920 --- [nio-8080-exec-7] c.hyf.cache.cachetemplate.H2CacheCache : insert into ehcache,key:51,value:Book2(id=51, bookName=微服务架构, readFrequency=1, createTime=2020-03-20T16:10:13, updateTime=2020-03-27T09:14:44, version=1)

2020-04-03 09:55:10.118 INFO 5920 --- [nio-8080-exec-7] c.hyf.cache.cachetemplate.H2CacheCache : insert into redis,key:51,value:Book2(id=51, bookName=微服务架构, readFrequency=1, createTime=2020-03-20T16:10:13, updateTime=2020-03-27T09:14:44, version=1)

2020-04-03 09:55:31.864 INFO 5920 --- [nio-8080-exec-2] c.hyf.cache.cachetemplate.H2CacheCache : select from ehcache,key:51

删除:删除数据库中的数据后,也会删除 Ehcache 和 Redis 中对应的缓存数据。

2020-04-03 10:05:18.704 DEBUG 5920 --- [nio-8080-exec-3] c.h.t.redis.BookMapper2.deleteById : ==> Preparing: DELETE FROM book WHERE id=?

2020-04-03 10:05:18.704 DEBUG 5920 --- [nio-8080-exec-3] c.h.t.redis.BookMapper2.deleteById : ==> Parameters: 51(Long)

2020-04-03 10:05:18.731 DEBUG 5920 --- [nio-8080-exec-3] c.h.t.redis.BookMapper2.deleteById : <== Updates: 1

2020-04-03 10:05:18.732 INFO 5920 --- [nio-8080-exec-3] c.hyf.cache.cachetemplate.H2CacheCache : delete from ehcache,key:51

2020-04-03 10:05:18.844 INFO 5920 --- [nio-8080-exec-3] c.hyf.cache.cachetemplate.H2CacheCache : delete from redis,key:51

其他的就不用演示了...

四、最后

当然啦,这个 starter 还是比较简单的,如果大家感兴趣,可以去看看源码是如何基于 Spring Cache 实现多级缓存的~

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

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

相关文章

Spring MVC:带有CNVR卷的REST应用程序。 3

这是带有CNVR的Spring MVC REST教程的最后一部分。 在这里&#xff0c;我将演示所有这些东西如何工作&#xff0c;这是我在前两部分中开发的。 对于每种类型的CRUD操作&#xff0c;这将分为四个部分&#xff1a;CREATE&#xff0c;READ&#xff0c;UPDATE&#xff0c;DELETE。 …

java 中io的删除文件_总结删除文件或文件夹的7种方法-JAVA IO基础总结第4篇

本文是Java IO总结系列篇的第4篇&#xff0c;前篇的访问地址如下&#xff1a;如果您阅读完成&#xff0c;觉得此文对您有帮助&#xff0c;请给我点个赞&#xff0c;您的支持是我不竭的创作动力。为了方便大家理解&#xff0c;我特意制作了本文对应的视频&#xff1a;总结删除文…

实现小程序canvas拖拽功能

组件地址 https://github.com/jasondu/wx-comp-canvas-drag 实现效果 如何实现 使用canvas使用movable-view标签 由于movable-view无法实现旋转&#xff0c;所以选择使用canvas 需要解决的问题 如何将多个元素渲染到canvas上如何知道手指在元素上、如果多个元素重叠如何知…

H5页面滚动阻尼效果实现

功能描述 要求 页面分为AB两个区域 当手机可视区的底部接触到 “阻尼带” 的时候&#xff0c;有个上拉弹性过程 当上拉到一定阈值程度就直接把B区顶部弹到手机可视区的顶部&#xff0c;让可视区从B区开始显示当上拉程度未到阈值&#xff0c;就回弹复原 当手机可视区从B区向上…

web 前端 html

1&#xff0c;什么是web 在网络中&#xff0c;大量的数据需要有一个载体&#xff0c;而很多人都能够访问这个载体&#xff0c;利用浏览器的这个窗口链接一个有一个载体&#xff0c;这个载体就是网站也就是web的前身。  1&#xff0c;web标准&#xff1a;结构标准&#xff0c;表…

再谈前后端分离

前段时间我针对手头上的项目前端配置进行了反思以及总结并且写了两篇文章: webpack传统后端渲染的项目前端配置, webpack配置之前后端不分离, 很显然这些配置能满足一时的需求, 但是也有不足. 今天继续总结, 这里应该不涉及到具体后端语言, 只对前端配置进行描述. 毕竟配置工程…

Python - day1 借鉴洪卫

一、了解开发语言 1、高级语言&#xff1a;Python&#xff0c;Java&#xff0c;C&#xff0c;C#&#xff0c;PHP&#xff0c;JS&#xff0c;Go&#xff0c;Ruby&#xff0c;SQL&#xff0c;Swift&#xff0c;Perl&#xff0c;Objective-C&#xff0c;R等等&#xff1b; 2、低级…

返回一个二维整数数组最大子数组的和

要求&#xff1a; 1&#xff0c;输入一个二维整形数组&#xff0c;数组里有正数也有负数。 2&#xff0c;二维数组中连续的一个子矩阵组成一个子数组&#xff0c;每个子数组都有一个和, 3&#xff0c;求所有子数组的和的最大值。 设计思路&#xff1a; 参照一维整数数组求解最大…

基于React的表单开发的分析(上)

本文主要讲解后台系统与表单相关的页面开发&#xff0c;并分析如何才能更好地、高效地开发。 技术栈 ReactAntd 背景 Antd 以下我都将Ant Design 简称为 Antd Ant Design是个服务于企业级产品的UI框架&#xff0c;主要可以用于中后台系统,它有基于React、Vue和Angular的实现…

50个Servlet面试问答

Servlet是Java EE的一个非常重要的主题&#xff0c;所有Web应用程序框架&#xff08;例如Spring和Struts&#xff09;都建立在它之上。 这使servlet成为Java访谈中的热门话题。 在这里&#xff0c;我提供了50个servlet面试问题的列表&#xff0c;并提供了答案&#xff0c;以帮…

深入浅出的webpack4构建工具--webpack4+react构建环境(二十)

下面我们来配置下webpack4react的开发环境&#xff0c;之前都是针对webpack4vue的。下面我们也是在之前项目结构的基础之上进行配置下。 首先看下如下是我为 webpack4react 基本的项目结构如下&#xff1a; ### 目录结构如下&#xff1a; demo1 …

Webpack 4进阶--从前的日色变得慢 ,一下午只够打一次包

从前的日色变得慢&#xff0c;车&#xff0c;马&#xff0c;邮件都慢&#xff0c;一生只够爱一个人 -- 《从前慢》 近期在团队项目里把Webpack升级到4.4.1&#xff0c;过程中发现现存的升级文档十分有限&#xff0c;踩了不少坑&#xff0c;好在升级之后提升还算显著&#xff0c…

编码Java时的10个微妙的最佳实践

这是10个最佳实践的列表&#xff0c;这些最佳实践比您的平均Josh Bloch有效Java规则要微妙得多。 尽管Josh Bloch的列表很容易学习&#xff0c;并且涉及日常情况&#xff0c;但此处的列表包含了涉及API / SPI设计的较不常见的情况&#xff0c;但可能会产生很大的影响。 我在编…

Vue 实现微信 jssdk 扫码, 上传图片

流程 1: 配置微信公众号JS域名 2:前端发送URL后台获取JSSDK配置, 后台Service代码如下, 修改2处位置: WeixinUtil.APPID > 当前公众号APPID WeixinUtil.getAccessToken() > 当前公众号access_token public interface IWxJssdkService {Map<String, Object> getJssd…

使用Storm进行可扩展的实时状态更新

在本文中&#xff0c;我将说明如何借助Storm框架以可扩展且无锁定的方式在数据库中维护实时事件驱动流程的当前状态。 Storm是基于事件的数据处理引擎。 它的模型依赖于基本原语&#xff0c;例如事件转换&#xff0c;过滤&#xff0c;聚合……&#xff0c;我们将它们组合成拓扑…

【干货】十分钟读懂浏览器渲染流程

在之前写过的一篇《"天龙八步"细说浏览器输入URL后发生了什么》一文中&#xff0c;和大家分享了从在浏览器中输入网址URL到最终页面展示的整个过程。部分读者向我反馈对于最后的浏览器渲染布局这块不是很清晰&#xff0c;所以本文就浏览器渲染流程单独开篇讲解&#…

控制台资费管理主菜单java_java毕业设计_springboot框架的高速公路收费管理系统...

今天介绍一个java毕设题目, 题目内容为springboot框架的高速公路收费管理系统, 是一个采用b/s结构的javaweb项目, 采用java语言编写开发工具eclipse, 项目框架jspspringbootmybatis, 高速公路收费管理系统的信息存储于mysql中, 并基于mybatis进行了orm封装, 该高速公路收费管理…

在Amazon EMR上运行Hadoop MapReduce作业

不久前&#xff0c;我发布了如何使用CLI设置EMR群集的信息。 在本文中&#xff0c;我将展示如何使用适用于AWS的Java SDK来设置集群。 展示如何使用Java AWS开发工具包执行此操作的最佳方法是展示完整的示例&#xff0c;因此&#xff0c;让我们开始吧。 设置一个新的Maven项目…

[JSConf EU 2018] 大脑控制 Javascript

先解释&#xff0c;本人为前端菜鸟&#xff0c;之前也未参加过类似的活动&#xff0c;没有翻译过什么文章&#xff0c;此次是好奇心使然&#xff0c;也是想尝试下&#xff0c;学习学习&#xff0c;英文很烂&#xff0c;全靠有道&#xff0c;但是视频整个看下来&#xff0c;还是…

JavaScript中不得不说的断言?

断言主要应用于“调试”与“测试” 一、前端中的断言 仔细地查找一下JavaScript中的API&#xff0c;实际上并没有多少关于断言的方法。唯一一个就是console.assert&#xff1a; // console.assert(condition, message)const a 1console.assert(typeof a number, a should be…