2021-05-22

缓存架构中的服务详解!SpringBoot中二级缓存服务的实现

创建缓存服务

创建缓存服务接口项目

  • 创建myshop-service-redis-api项目,该项目只负责定义接口
  • 创建项目的pom.
<?
  • 定义数据Redis接口RedisService:
package com.oxford.myshop.service.redis.apipublic 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.
<?

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@EnableHystrixDashboardpublic 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:26381server: port: 8503services: version: redis: 	 v1: 1.0.0 user:  v1: 1.0.0dubbo: 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:2183management: 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:26381server: port: 8501# MyBatis Config propertiesmybatis: configuration: 	cache-enabled: true type-aliases-package: com.oxford.myshop.commons.domain mapper-location: classpath:mapper/*.
  • 在myshop-commons-mapper的pom.
<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;@Componentpublic 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)








原文转载:http://www.shaoqun.com/a/756806.html

跨境电商:https://www.ikjzd.com/

马莎:https://www.ikjzd.com/w/2385

ifttt:https://www.ikjzd.com/w/956


创建缓存服务创建缓存服务接口项目创建myshop-service-redis-api项目,该项目只负责定义接口创建项目的pom.<?定义数据Redis接口RedisService:packagecom.oxford.myshop.service.redis.apipublicinterfaceRedisService{ voidset(Stringkey,Objectvalue); voi
王惟:https://www.ikjzd.com/w/1744
kk馆:https://www.ikjzd.com/w/1713
盘古集团:https://www.ikjzd.com/w/1448
失业老公每晚对我性虐待:http://lady.shaoqun.com/a/272382.html
私情9年 妹妹为我老公堕胎3次:http://lady.shaoqun.com/a/271875.html
痛苦 老公每晚床上折腾害我骨瘦如柴:http://lady.shaoqun.com/a/272276.html

No comments:

Post a Comment