创立缓存服务

创立缓存服务接口我的项目

  • 创立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.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.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@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-providerredis:    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.implapplication:  id: com.oxford.myshop.service.redis.provider.api  name: com.oxford.myshop.service.redis.provider.api  qos-port: 22224  qos-enable: trueprotocal:  id: dubbo  name: dubbo  port: 20883  status: server  serialization: kryoregitry:  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: truehealth:  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-providerdatasource:  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.Driverredis:    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: truetype-aliases-package: com.oxford.myshop.commons.domainmapper-location: classpath:mapper/*.xmlservices:version:    redis:      v1: 1.0.0  user:    v1: 1.0.0dubbo:scan:  basePackages: com.oxford.myshop.service.user.provider.api.implapplication:  id: com.oxford.myshop.service.user.provider.api  name: com.oxford.myshop.service.user.provider.api  qos-port: 22222  qos-enable: trueprotocal:  id: dubbo  name: dubbo  port: 20001  status: server  serialization: kryoregitry:  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: truehealth:  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
  • 应用GenerateSerialVersionUID插件生成,装置完插件后在实现了序列化接口的类中
  • 应用快捷键Alt+Insert即可呼出生成菜单,即可主动生成序列号

  • 在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)