创建缓存服务

创建缓存服务接口项目

  • 创建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中二级缓存服务的实现的更多相关文章

  1. 详解Springboot中自定义SpringMVC配置

    详解Springboot中自定义SpringMVC配置 WebMvcConfigurer接口 ​ 这个接口可以自定义拦截器,例如跨域设置.类型转化器等等.可以说此接口为开发者提前想到了很多拦截层面的需 ...

  2. CentOS7中firewall防火墙详解和配置,.xml服务配置详解

    修改防火墙配置文件之前,需要对之前防火墙做好备份 重启防火墙后,需要确认防火墙状态和防火墙规则是否加载,若重启失败或规则加载失败,则所有请求都会被防火墙 1. firewall-cmd --state ...

  3. [Android] Service服务详解以及如何使service服务不被杀死

    排版上的细节有些不好看,主要是我用的MarkDown编辑器预览和这里的不一样,在那个上面的样式很舒服.这里要改的地方太多就不想改了,将就看吧.下次写的时候注意.还有看到错误给我提啊. 本文链接:htt ...

  4. java中的finally详解(finally中没有return i,却有i=XX)

    问题分析  首先来问大家一个问题:finally 语句块一定会执行吗? 很多人都认为 finally 语句块是肯定要执行的,其中也包括一些很有经验的 Java 程序员.可惜并不像大多人所认为的那样,对 ...

  5. 【转】SSH服务详解

    [转]SSH服务详解 第1章 SSH服务 1.1 SSH服务协议说明 SSH 是 Secure Shell Protocol 的简写,由 IETF 网络工作小组(Network Working Gro ...

  6. 详解工作流框架Activiti的服务架构和组件

    摘要:通过这篇文章,可以对工作流有一个基本的认识,为后续工作流框架Activiti的学习打下坚实的基础. 本文分享自华为云社区<BPMN工作流的基本概念!详解工作流框架Activiti的服务架构 ...

  7. winxp计算机管理中服务详解

    winxp计算机管理中服务详解01 http://blog.sina.com.cn/s/blog_60f923b50100efy9.html http://blog.sina.com.cn/s/blo ...

  8. Android中Service(服务)详解

    http://blog.csdn.net/ryantang03/article/details/7770939 Android中Service(服务)详解 标签: serviceandroidappl ...

  9. WCF中队列服务详解

    WCF中队列服务详解 一.引言 在前面的WCF服务中,它都要求服务与客户端两端都必须启动并且运行,从而实现彼此间的交互.然而,还有相当多的情况希望一个面向服务的应用中拥有离线交互的能力.WCF通过服务 ...

  10. Liunx中fstab文件详解

    Liunx中fstab文件详解 /etc/fstab是用来存放文件系统的静态信息的文件.位于/etc/目录下,可以用命令less /etc/fstab 来查看,如果要修改的话,则用命令 vi /etc ...

随机推荐

  1. 《C++反汇编与逆向分析技术揭秘》--数据类型

      浮点数类型 IEEE标准从逻辑上采用一个三元组{S, E, M}来表示一个数N,它规定基数为2,符号位S用0和1分别表示正和负,尾数M用原码表示,阶码E用移码表示.根据浮点数的规格化方法,尾数域的 ...

  2. PTA 输出数组元素

    7-3 输出数组元素 (15 分)   本题要求编写程序,对顺序读入的n个整数,顺次计算后项减前项之差,并按每行三个元素的格式输出结果. 输入格式: 输入的第一行给出正整数n(1).随后一行给出n个整 ...

  3. pwnable.kr 第一题fd

    使用ssh fd@pwnable.kr -p2222连接输入密码guest 1 fd@prowl:~$ ls -al 2 total 40 3 drwxr-x--- 5 root fd 4096 Oc ...

  4. LookupError: 'hex' is not a text encoding; use codecs.decode() to handle arbitrary codecs

    问题代码: b=b'\x01\x02\x03' x=binascii.b2a_hex(b.decode('hex')[::-1].encode('hex')) python2下是不报错的,因为pyth ...

  5. OpenGL光照计算中法线矩阵原理及推到过程

    问题起源 在计算漫反射关照时,需要用到法线,通过法线和光线的点乘值,计算漫反射的产生的光线强度,所以需要从顶点着色器中将法线数据传递到片源着色器中,但是片源着色器中的顶点坐标是经过了模型矩阵变化过的世 ...

  6. 使用Jenkins + git submodule 实现自动化编译,解决代码安全性问题

    道哥的第 030 篇原创 目录 一.一个真实的代码泄漏故事 二.Jenkins 的基本使用 1. Jenkins 是什么? 2. 安装 JDK8 3. 安装 Jenkins 4. 在浏览器中配置 Je ...

  7. SIP (Session Initiation Protocol) 协议

    Session Initiation Protocol 介绍 SIP是VoIP技术最常使用的协议,它是一种应用程序层协议,可与其他应用程序层协议配合使用,以控制Internet上的多媒体通信会话. V ...

  8. 解决删除Azure Active Directory的Enterprise Applications异常

    当我们不需要使用某个Azure Active Directory(以下简称AAD)的时候,我们可以删除它,这个时候Azure会对当前的AAD包含的内容进行检查, 在所有的检查项目中有一个名叫" ...

  9. 设计Web页面(2)

    1.前面我们新建了一个空白的ASP.NET网页,那么接下来这章我们就讲一下设计Web页面 2.布局页面有两种方法,一种是通过Table表格来布局页面窗体,另一种是通过CSS+DIV来布局窗体,其中作为 ...

  10. [Fundamental of Power Electronics]-PART I-1.引言-1.1 功率处理概论

    1.1 功率处理概论 电力电子领域关注的是利用电子设备对电力进行处理[1–7].如图1.1所示,其中关键部件就是开关变换器.通常,开关变换器包含电源输入和控制输入端口以及电源输出端口.原始输入功率按控 ...