缓存架构中的服务详解!SpringBoot中二级缓存服务的实现
创建缓存服务
创建缓存服务接口项目
- 创建myshop-service-redis-api项目,该项目只负责定义接口
- 创建项目的pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.oxford</groupId>
<artifactId>myshop-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../myshop-dependencies/pom.xml</relativePath>
</parent>
<artifactId>myshop-service-redis-api</artifactId>
<packaging>jar</packaging>
</project>
- 定义数据Redis接口RedisService:
package com.oxford.myshop.service.redis.api
public interface RedisService{
void set(String key,Object value);
void set(String key,Object value,int seconds);
void del(String key);
Object get(String key);
}
创建缓存服务提供者项目
- 创建myshop-service-redis-provider项目,该项目用作缓存服务提供者
- 创建项目的pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.oxford</groupId>
<artifactId>myshop-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../myshop-dependencies/pom.xml</relativePath>
</parent>
<artifactId>myshop-service-redis-api</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Spring Boot Starter Settings-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--Common Setting-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>de.javakaffee</groupId>
<artifactId>kryo-serializers</artifactId>
</dependency>
<!--Project Settings-->
<dependency>
<groupId>com.oxford</groupId>
<artifactId>my-shop-commons-dubbo</artifactId>
<version>${Project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.oxford</groupId>
<artifactId>my-shop-service-redis-api</artifactId>
<version>${Project.parent.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.oxford.myshop.service.redis.provider.MyshopServiceRedisProviderApplication</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
Redis底层实现的Java的lettuce客户端
- 创建缓存服务接口实现类RedisServiceImpl
package com.oxford.myshop.service.redis.provider.api.impl;
@Service(version="${service.versions.redis.v1}")
public class RedisServiceImpl implements RedisService{
@Override
public void set(String key,Object value){
redisTemplate.opsForValue().set(key,value);
}
@Override
public void set(String key,Object value,int seconds){
redisTemplate.opsForValue().set(key,value,seconds,TimeUnit.SECONDS);
}
@Override
public void del(String key){
redisTemplate.delete(key);
}
@Override
public Object get(String key){
return redisTemplate.opsForValue().get(key);
}
}
- 创建启动类SpringBootApplication
package com.oxford.myshop.service.redis.provider;
import com.alibaba.dubbo.container.Main;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@EnableHystrix
@EnableHystrixDashboard
public class MyShopServiceRedisrProviderApplication {
public static void main(String[]args) {
SpringApplication.run(MyShopServiceRedisProviderApplication.class,args);
Main.main(args);
}
}
- 创建配置文件application.yml
spring:
application:
name: myshop-service-redis-provider
redis:
lettuce:
pool:
max-active: 8
max-idle: 8
max-wait: -1ms
min-idle: 0
sentinel:
master: mymaster
nodes: 192.168.32.255:26379,192.168.32.255:26380,192.168.32.255:26381
server:
port: 8503
services:
version:
redis:
v1: 1.0.0
user:
v1: 1.0.0
dubbo:
scan:
basePackages: com.oxford.myshop.service.redis.provider.api.impl
application:
id: com.oxford.myshop.service.redis.provider.api
name: com.oxford.myshop.service.redis.provider.api
qos-port: 22224
qos-enable: true
protocal:
id: dubbo
name: dubbo
port: 20883
status: server
serialization: kryo
regitry:
id: zookeeper
address: zookeeper://localhost:2181?backup=192.168.32.255:2182,192.168.32.255:2183
management:
endpoint:
dubbo:
enabled: true
dubbo-shutdown:
enabled: true
dubbo-configs:
enabled: true
dubbo-sevicies:
enabled: true
dubbo-reference:
enabled: true
dubbo-properties:
enabled: true
health:
dubbo:
status:
defaults: memory
extras: load,threadpool
创建缓存服务消费者项目
- 在pom文件中引入redis接口依赖
- 在缓存服务消费者项目的ServiceImpl中调用RedisService
@Reference(version="services.versions.redis.v1")
private RedisService redisService;
MyBatis Redis二级缓存
MyBatis缓存
- 一级缓存:
- MyBatis会在表示会话的SqlSession对象中建立一个简单的缓存: 将每次查询到的结果缓存起来,当下次查询的时候,如果判断先前有个完全一样的查询,会直接从缓存中直接将结果取出,返回给用户,不需要再进行一次数据库查询
- 一级缓存是SqlSession级别的缓存:
- 在操作数据库时需要构造SqlSession对象
- 对象中有一个(内存区域)数据结构(HashMap)用于存储缓存数据
- 不同的SqlSession之间的缓存数据区域(HashMap)互不影响,
- 一级缓存的作用域是同一个SqlSession
- 在同一个SqlSession中两次执行相同的SQL语句: 第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据,将不再从数据库查询,从而提高查询效率
- 当一个SqlSession结束后该SqlSession中的一级缓存就不存在了
- MyBatis默认开启一级缓存
- 二级缓存:
- 二级缓存是Mapper级别的缓存: 多个SqlSession去操作同一个Mapper的SQL语句,多个SqlSession去操作数据库得到数据会存在二级缓存区域,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的
- 二级缓存的作用域是mapper的同一个namespace
- 不同的SqlSession两次执行相同namespace下的SQL语句且向SQL中传递参数也相同即最终执行相同的SQL语句: 第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率
- MyBatis默认没有开启二级缓存,需要在setting全局参数中配置开启二级缓存
配置MyBatis二级缓存
SpringBoot中开启MyBatis二级缓存
- 在myshop-service-user-provider的配置文件中开启MyBatis二级缓存
spring:
application:
name: myshop-service-user-provider
datasource:
druid:
url: jdbc:mysql://localhost:3306/myshop?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: 123456
initial-size: 1
min-idle: 1
main-active: 20
test-on-borrow: true
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
lettuce:
pool:
max-active: 8
max-idle: 8
max-wait: -1ms
min-idle: 0
sentinel:
master: mymaster
nodes: 192.168.32.255:26379,192.168.32.255:26380,192.168.32.255:26381
server:
port: 8501
# MyBatis Config properties
mybatis:
configuration:
cache-enabled: true
type-aliases-package: com.oxford.myshop.commons.domain
mapper-location: classpath:mapper/*.xml
services:
version:
redis:
v1: 1.0.0
user:
v1: 1.0.0
dubbo:
scan:
basePackages: com.oxford.myshop.service.user.provider.api.impl
application:
id: com.oxford.myshop.service.user.provider.api
name: com.oxford.myshop.service.user.provider.api
qos-port: 22222
qos-enable: true
protocal:
id: dubbo
name: dubbo
port: 20001
status: server
serialization: kryo
regitry:
id: zookeeper
address: zookeeper://localhost:2181?backup=192.168.32.255:2182,192.168.32.255:2183
management:
endpoint:
dubbo:
enabled: true
dubbo-shutdown:
enabled: true
dubbo-configs:
enabled: true
dubbo-sevicies:
enabled: true
dubbo-reference:
enabled: true
dubbo-properties:
enabled: true
health:
dubbo:
status:
defaults: memory
extras: load,threadpool
- 在myshop-commons-mapper的pom.xml中增加redis依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifacted>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifacted>
</dependency>
实体类实现序列化接口并声明序列号
private static final long serialVersionUID = 82897704415244673535L
IDEA生成序列号方法:
- 使用GenerateSerialVersionUID插件生成,安装完插件后在实现了序列化接口的类中
- 使用快捷键Alt+Insert即可呼出生成菜单,即可自动生成序列号
实现Mybatis Cache接口,自定义缓存为Redis
- 在myshop-commons项目中创建ApplicationContextHolder类
package com.oxford.myshop.commons.context;
@Component
public class ApplicationContextHolder implements ApplicationContextAware,DisposableBean{
private static final Logger logger=LoggerFactory.getLogger(ApplicationContext.class);
private static ApplicationContext applicationContext;
/**
* 获取存储在静态变量中的ApplicationContext
*/
public static ApplicationContext getApplicationContext(){
assertContextInjected();
return applicationContext;
}
/**
* 从静态变量applicationContext中获取Bean,自动转型成所赋值对象的类型
*/
public static <T> T getBean(String name){
assertContextInjected();
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中获取Bean,自动转型成所赋值对象的类型
*/
public static <T> T getBean(Class<T> clazz){
assertContextInjected();
return (T) applicationContext.getBean(clazz);
}
/**
* 实现DisposableBean接口,在Context关闭时清理静态变量
*/
public void destroy() throws Exception{
logger.debug("清除 SpringContext 中的 ApplicationContext: {}",applicationContext);
applicationContext=null;
}
/**
* 实现ApplicationContextAware接口,注入Context到静态变量中
*/
public void setApplicationContext(ApplicationContext applicationContext) throws BeanException{
ApplicationContext.applicationContext=applicationContext;
}
/**
* 断言Context已经注入
*/
private static void assertContextInjected(){
Validate.validState(applicationContext !=null,"applicationContext 属性未注入,请在配置文件中配置定义ApplicationContextContext");
}
}
- 在myshop-commons-mapper项目中创建一个RedisCache的工具类
package com.oxford.myshop.commons.utils;
public class RedisCache implements Cache{
private static final Logger logger=LoggerFactory.getLogger(RedisCache.class);
private final ReadWriteLock readWriteLock=new ReentranReadWriteLock();
private final String id;
private RedisTemplate redisTemplate;
private static final long EXPIRE_TIME_IN_MINUTES=30 // redis过期时间
public RedisCache(String id){
if(id==null){
throw new IllegalArgumentException("Cache instances require an ID");
}
this.id=id;
}
@Override
public String getId(){
return id;
}
/**
* Put query result to redis
*/
@Override
public void putObject(Object key,Object value){
try{
RedisTemplate redisTemplate=getRedisTemplate();
ValueOperations opsForValue=redisTemplate.opsForValue();
opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
logger.debug("Put query result to redis");
}catch(Throwable t){
logger.error("Redis put failed",t);
}
}
/**
* Get cached query result from redis
*/
@Override
public Object getObject(Object key){
try{
RedisTemplate redisTemplate=getRedisTemplate();
ValueOperations opsForValue=redisTemplate.opsForValue();
opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
logger.debug("Get cache query result from redis");
return opsForValue.get(key);
}catch(Throwable t){
logger.error("Redis get failed, fail over to db");
return null;
}
}
/**
* Get cached query result from redis
*/
@Override
public Object getObject(Object key){
try{
RedisTemplate redisTemplate=getRedisTemplate();
ValueOperations opsForValue=redisTemplate.opsForValue();
opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
logger.debug("Get cache query result from redis");
return opsForValue.get(key);
}catch(Throwable t){
logger.error("Redis get failed, fail over to db");
return null;
}
}
/**
* Remove cached query result from redis
*/
@Override
@SuppressWarnings("unchecked")
public Object removeObject(Object key){
try{
RedisTemplate redisTemplate=getRedisTemplate();
redisTemplate.delete(key);
logger.debug("Remove cached query result from redis");
}catch(Throwable t){
logger.error("Redis remove failed");
}
return null;
}
/**
* Clear this cache instance
*/
@Override
public void clear(){
RedisTemplate redisTemplate=getRedisTemplate();
redisTemplate.execute((RedisCallback)->{
connection.flushDb();
return null;
});
logger.debug("Clear all the cached query result from redis");
}
@Override
public int getSize(){
return 0;
}
@Override
public ReadWriteLock getReadWriteLock(){
return readWriteLock;
}
private RedisTemplate getRedisTemplate(){
if(redisTemplate==null){
redisTemplate=ApplicationContextHolder.getBean("redisTemplate");
}
return redisTemplate;
}
}
Mapper接口类中标注注解
- 在Mapper接口类上标注注解,声明使用二级缓存
@CacheNamespace(implementation=RedisCache.class)
缓存架构中的服务详解!SpringBoot中二级缓存服务的实现的更多相关文章
- 详解Springboot中自定义SpringMVC配置
详解Springboot中自定义SpringMVC配置 WebMvcConfigurer接口 这个接口可以自定义拦截器,例如跨域设置.类型转化器等等.可以说此接口为开发者提前想到了很多拦截层面的需 ...
- CentOS7中firewall防火墙详解和配置,.xml服务配置详解
修改防火墙配置文件之前,需要对之前防火墙做好备份 重启防火墙后,需要确认防火墙状态和防火墙规则是否加载,若重启失败或规则加载失败,则所有请求都会被防火墙 1. firewall-cmd --state ...
- [Android] Service服务详解以及如何使service服务不被杀死
排版上的细节有些不好看,主要是我用的MarkDown编辑器预览和这里的不一样,在那个上面的样式很舒服.这里要改的地方太多就不想改了,将就看吧.下次写的时候注意.还有看到错误给我提啊. 本文链接:htt ...
- java中的finally详解(finally中没有return i,却有i=XX)
问题分析 首先来问大家一个问题:finally 语句块一定会执行吗? 很多人都认为 finally 语句块是肯定要执行的,其中也包括一些很有经验的 Java 程序员.可惜并不像大多人所认为的那样,对 ...
- 【转】SSH服务详解
[转]SSH服务详解 第1章 SSH服务 1.1 SSH服务协议说明 SSH 是 Secure Shell Protocol 的简写,由 IETF 网络工作小组(Network Working Gro ...
- 详解工作流框架Activiti的服务架构和组件
摘要:通过这篇文章,可以对工作流有一个基本的认识,为后续工作流框架Activiti的学习打下坚实的基础. 本文分享自华为云社区<BPMN工作流的基本概念!详解工作流框架Activiti的服务架构 ...
- winxp计算机管理中服务详解
winxp计算机管理中服务详解01 http://blog.sina.com.cn/s/blog_60f923b50100efy9.html http://blog.sina.com.cn/s/blo ...
- Android中Service(服务)详解
http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...
- WCF中队列服务详解
WCF中队列服务详解 一.引言 在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互.然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力.WCF通过服务 ...
- Liunx中fstab文件详解
Liunx中fstab文件详解 /etc/fstab是用来存放文件系统的静态信息的文件.位于/etc/目录下,可以用命令less /etc/fstab 来查看,如果要修改的话,则用命令 vi /etc ...
随机推荐
- 解决无法通过浏览器访问docker成功安装rabbitMQ后页面问题
我发现了问题后,花了两天的时间解决了这个问题. 一.测试在docker本机中使用curl "ip地址:端口" 查看是否能访问成功,结果是没问题,排除了docker安装失败的问题 二 ...
- 【工程应用一】 多目标多角度的快速模板匹配算法(基于NCC,效果无限接近Halcon中........)
愿意写代码的人一般都不太愿意去写文章,因为代码方面的艺术和文字中的美学往往很难兼得,两者都兼得的人通常都已经被西方极乐世界所收罗,我也是只喜欢写代码,让那些字母组成美妙的歌曲,然后自我沉浸在其中自得其 ...
- Kubernetes工作流程--<1>
Kubernetes工作流程 客户端创建pod 流程: 用户管理员创建 Pod 的请求默认是通过kubectl 客户端管理命令 api server 组件进行交互的,默认会将请求发送给 API Ser ...
- [BFS]A. 【例题1】走迷宫
A . [ 例 题 1 ] 走 迷 宫 解析 简单的BFS模板题 Code #include <bits/stdc++.h> #define N 1005 using namespace ...
- 设计原则:开闭原则(OCP)
1.什么是开闭原则 开闭原则的英文是Open Closed Principle,缩写就是OCP.其定义如下: 软件实体(模块.类.方法等)应该"对扩展开放.对修改关闭". 从定义上 ...
- 【C/C++】面相对象开发之封装
封装继承多态是面向对象程序开发的基础概念.是实现面向对象的基本要素. 封装 程序开发,最核心价值,是数据. 程序其实是读取数据,操作数据,保存数据等一系列操作. 那么经过良好组织过的数据,将使编程事半 ...
- YARP实现Dapr服务调用的反向代理
楔子 公司即将新开项目,打算用点时髦的技术,需要探探路.之前没做过微服务项目,没有技术栈方面的积(负)累(债), 干脆就上微软的分布式运行时Dapr......嗯......用来服务发现,然后等测试用 ...
- 分库分表之后,id主键如何处理?
(1)数据库自增id 这个就是说你的系统里每次得到一个id,都是往一个库的一个表里插入一条没什么业务含义的数据,然后获取一个数据库自增的一个id.拿到这个id之后再往对应的分库分表里去写入. 这个方案 ...
- JAVAEE_Servlet_22_Cookie实现十天内免登录
Cookie实现十天内免登录 代码: - CheckLogin 查看是否获取到了Cookie,如果获取到了连接数据库验证Cookie发过来的用户名和密码,如果没有获取到Cookie信息,那么就跳转到登 ...
- JDBC_13_封装JDBC工具类
封装JDBC工具类 代码: import java.sql.*; /** * JDBC工具类,简化JDBC编程 */ public class DBUtil { //工具类中的构造方法都是私有的,因为 ...