关于java:Spring-Boot-Redis-实现各种操作写得太好了吧

38次阅读

共计 10842 个字符,预计需要花费 28 分钟才能阅读完成。

起源:blog.csdn.net/qq_42105629/article/details/102589319

一、Jedis,Redisson,Lettuce 三者的区别

共同点:都提供了基于 Redis 操作的 Java API,只是封装水平,具体实现稍有不同。

不同点:

1.1、Jedis

是 Redis 的 Java 实现的客户端。反对根本的数据类型如:String、Hash、List、Set、Sorted Set。

特点:应用阻塞的 I /O,办法调用同步,程序流须要等到 socket 解决完 I / O 能力执行,不反对异步操作。Jedis 客户端实例不是线程平安的,须要通过连接池来应用 Jedis。

1.2、Redisson

长处点:分布式锁,分布式汇合,可通过 Redis 反对提早队列。

1.3、Lettuce

用于线程平安同步,异步和响应应用,反对集群,Sentinel,管道和编码器。

基于 Netty 框架的事件驱动的通信层,其办法调用是异步的。Lettuce 的 API 是线程平安的,所以能够操作单个 Lettuce 连贯来实现各种操作。

二、RedisTemplate

2.1、应用配置

maven 配置引入,(要加上版本号,我这里是因为 Parent 已申明)

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

application-dev.yml

spring:
  redis:
    host: 192.168.1.140
    port: 6379
    password:
    database: 15 # 指定 redis 的分库(共 16 个 0 到 15)

Spring Boot 根底就不介绍了,举荐下这个实战教程:
https://github.com/javastacks…

2.2、应用示例

 @Resource
 private StringRedisTemplate stringRedisTemplate;
 
    @Override
    public CustomersEntity findById(Integer id) {
        // 须要缓存
        // 所有波及的缓存都须要删除,或者更新
        try {String toString = stringRedisTemplate.opsForHash().get(REDIS_CUSTOMERS_ONE, id + "").toString();
            if (toString != null) {return JSONUtil.toBean(toString, CustomersEntity.class);
            }
        } catch (Exception e) {e.printStackTrace();
        }
        // 缓存为空的时候,先查, 而后缓存 redis
        Optional<CustomersEntity> byId = customerRepo.findById(id);
        if (byId.isPresent()) {CustomersEntity customersEntity = byId.get();
            try {stringRedisTemplate.opsForHash().put(REDIS_CUSTOMERS_ONE, id + "", JSONUtil.toJsonStr(customersEntity));
            } catch (Exception e) {e.printStackTrace();
            }
            return customersEntity;
        }
        return null;
    }

2.3、扩大

2.3.1、spring-boot-starter-data-redis 的依赖包

3.3.2、stringRedisTemplate API(局部展现)
  • opsForHash –> hash 操作
  • opsForList –> list 操作
  • opsForSet –> set 操作
  • opsForValue –> string 操作
  • opsForZSet –> Zset 操作

3.3.3 StringRedisTemplate 默认序列化机制
public class StringRedisTemplate extends RedisTemplate<String, String> {

     /**
      * Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)}
      * and {@link #afterPropertiesSet()} still need to be called.
      */
     public StringRedisTemplate() {RedisSerializer<String> stringSerializer = new StringRedisSerializer();
      setKeySerializer(stringSerializer);
      setValueSerializer(stringSerializer);
      setHashKeySerializer(stringSerializer);
      setHashValueSerializer(stringSerializer);
     }
 }

三、RedissonClient 操作示例

3.1 根本配置

3.1.1、Maven pom 引入
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.8.2</version>
    <optional>true</optional>
</dependency>
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>LATEST</version>
</dependency>
3.1.2、增加配置文件 Yaml 或者 json 格局

redisson-config.yml

# Redisson 配置
singleServerConfig:
  address: "redis://192.168.1.140:6379"
  password: null
  clientName: null
  database: 15 #抉择应用哪个数据库 0~15
  idleConnectionTimeout: 10000
  pingTimeout: 1000
  connectTimeout: 10000
  timeout: 3000
  retryAttempts: 3
  retryInterval: 1500
  reconnectionTimeout: 3000
  failedAttempts: 3
  subscriptionsPerConnection: 5
  subscriptionConnectionMinimumIdleSize: 1
  subscriptionConnectionPoolSize: 50
  connectionMinimumIdleSize: 32
  connectionPoolSize: 64
  dnsMonitoringInterval: 5000
  #dnsMonitoring: false

threads: 0
nettyThreads: 0
codec:
  class: "org.redisson.codec.JsonJacksonCodec"
transportMode: "NIO"

或者,配置 redisson-config.json

{
  "singleServerConfig": {
    "idleConnectionTimeout": 10000,
    "pingTimeout": 1000,
    "connectTimeout": 10000,
    "timeout": 3000,
    "retryAttempts": 3,
    "retryInterval": 1500,
    "reconnectionTimeout": 3000,
    "failedAttempts": 3,
    "password": null,
    "subscriptionsPerConnection": 5,
    "clientName": null,
    "address": "redis://192.168.1.140:6379",
    "subscriptionConnectionMinimumIdleSize": 1,
    "subscriptionConnectionPoolSize": 50,
    "connectionMinimumIdleSize": 10,
    "connectionPoolSize": 64,
    "database": 0,
    "dnsMonitoring": false,
    "dnsMonitoringInterval": 5000
  },
  "threads": 0,
  "nettyThreads": 0,
  "codec": null,
  "useLinuxNativeEpoll": false
}
3.1.3、读取配置

新建读取配置类

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redisson() throws IOException {

        // 两种读取形式,Config.fromYAML 和 Config.fromJSON
//        Config config = Config.fromJSON(RedissonConfig.class.getClassLoader().getResource("redisson-config.json"));
        Config config = Config.fromYAML(RedissonConfig.class.getClassLoader().getResource("redisson-config.yml"));
        return Redisson.create(config);
    }
}

或者,在 application.yml 中配置如下

spring:
  redis:
    redisson:
      config: classpath:redisson-config.yaml

3.2 应用示例

@RestController
@RequestMapping("/")
public class TeController {

    @Autowired
    private RedissonClient redissonClient;

    static long i = 20;
    static long sum = 300;

//    ========================== String =======================
    @GetMapping("/set/{key}")
    public String s1(@PathVariable String key) {
        // 设置字符串
        RBucket<String> keyObj = redissonClient.getBucket(key);
        keyObj.set(key + "1-v1");
        return key;
    }

    @GetMapping("/get/{key}")
    public String g1(@PathVariable String key) {
        // 设置字符串
        RBucket<String> keyObj = redissonClient.getBucket(key);
        String s = keyObj.get();
        return s;
    }

    //    ========================== hash =======================-=

    @GetMapping("/hset/{key}")
    public String h1(@PathVariable String key) {Ur ur = new Ur();
        ur.setId(MathUtil.randomLong(1,20));
        ur.setName(key);
      // 寄存 Hash
        RMap<String, Ur> ss = redissonClient.getMap("UR");
        ss.put(ur.getId().toString(), ur);
        return ur.toString();}

    @GetMapping("/hget/{id}")
    public String h2(@PathVariable String id) {
        // hash 查问
        RMap<String, Ur> ss = redissonClient.getMap("UR");
        Ur ur = ss.get(id);
        return ur.toString();}

    // 查问所有的 keys
    @GetMapping("/all")
    public String all(){RKeys keys = redissonClient.getKeys();
        Iterable<String> keys1 = keys.getKeys();
        keys1.forEach(System.out::println);
        return keys.toString();}

    // ================== ============== 读写锁测试 =============================

    @GetMapping("/rw/set/{key}")
    public void rw_set(){
//        RedissonLock.
        RBucket<String> ls_count = redissonClient.getBucket("LS_COUNT");
        ls_count.set("300",360000000l, TimeUnit.SECONDS);
    }

    // 减法运算
    @GetMapping("/jf")
    public void jf(){

        String key = "S_COUNT";

//        RAtomicLong atomicLong = redissonClient.getAtomicLong(key);
//        atomicLong.set(sum);
//        long l = atomicLong.decrementAndGet();
//        System.out.println(l);

        RAtomicLong atomicLong = redissonClient.getAtomicLong(key);
        if (!atomicLong.isExists()) {atomicLong.set(300l);
        }

        while (i == 0) {if (atomicLong.get() > 0) {long l = atomicLong.getAndDecrement();
                        try {Thread.sleep(1000l);
                        } catch (InterruptedException e) {e.printStackTrace();
                        }
                i --;
                System.out.println(Thread.currentThread().getName() + "->" + i + "->" + l);
            }
        }


    }

    @GetMapping("/rw/get")
    public String rw_get(){

        String key = "S_COUNT";
        Runnable r = new Runnable() {
            @Override
            public void run() {RAtomicLong atomicLong = redissonClient.getAtomicLong(key);
                if (!atomicLong.isExists()) {atomicLong.set(300l);
                }
                if (atomicLong.get() > 0) {long l = atomicLong.getAndDecrement();
                    i --;
                    System.out.println(Thread.currentThread().getName() + "->" + i + "->" + l);
                }
            }
        };

        while (i != 0) {new Thread(r).start();
//            new Thread(r).run();
//            new Thread(r).run();
//            new Thread(r).run();
//            new Thread(r).run();}


        RBucket<String> bucket = redissonClient.getBucket(key);
        String s = bucket.get();
        System.out.println("================ 线程已完结 ================================" + s);

        return s;
    }

}

4.3 扩大

4.3.1 丰盛的 jar 反对,尤其是对 Netty NIO 框架

4.3.2 丰盛的配置机制抉择,这里是具体的配置阐明

https://github.com/redisson/r…

对于序列化机制中,就有很多

4.3.3 API 反对(局部展现),具体的 Redis –> RedissonClient , 可查看这里

https://github.com/redisson/r…

4.3.4 轻便的丰盛的锁机制的实现

  • Lock
  • Fair Lock
  • MultiLock
  • RedLock
  • ReadWriteLock
  • Semaphore
  • PermitExpirableSemaphore
  • CountDownLatch

四、基于注解实现的 Redis 缓存

4.1 Maven 和 YML 配置

参考 RedisTemplate 配置。另外,还须要额定的配置类

// todo 定义序列化,解决乱码问题
@EnableCaching
@Configuration
@ConfigurationProperties(prefix = "spring.cache.redis")
public class RedisCacheConfig {

    private Duration timeToLive = Duration.ZERO;

    public void setTimeToLive(Duration timeToLive) {this.timeToLive = timeToLive;}

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        // 解决查问缓存转换异样的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 配置序列化(解决乱码的问题)RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(timeToLive)
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }

}

4.2 应用示例

@Transactional
@Service
public class ReImpl implements RedisService {

    @Resource
    private CustomerRepo customerRepo;
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    public static final String REDIS_CUSTOMERS_ONE = "Customers";

    public static final String REDIS_CUSTOMERS_ALL = "allList";

    // ===================================================================== 应用 Spring cahce 注解形式实现缓存
    // ================================== 单个操作

    @Override
    @Cacheable(value = "cache:customer", unless = "null == #result",key = "#id")
    public CustomersEntity cacheOne(Integer id) {final Optional<CustomersEntity> byId = customerRepo.findById(id);
        return byId.isPresent() ? byId.get() : null;
    }

    @Override
    @Cacheable(value = "cache:customer", unless = "null == #result", key = "#id")
    public CustomersEntity cacheOne2(Integer id) {final Optional<CustomersEntity> byId = customerRepo.findById(id);
        return byId.isPresent() ? byId.get() : null;
    }

     // todo 自定义 redis 缓存的 key,
    @Override
    @Cacheable(value = "cache:customer", unless = "null == #result", key = "#root.methodName +'.'+ #id")
    public CustomersEntity cacheOne3(Integer id) {final Optional<CustomersEntity> byId = customerRepo.findById(id);
        return byId.isPresent() ? byId.get() : null;
    }

    // todo 这里缓存到 redis,还有响应页面是 String(加了很多本义符 \,),不是 Json 格局
    @Override
    @Cacheable(value = "cache:customer", unless = "null == #result", key = "#root.methodName +'.'+ #id")
    public String cacheOne4(Integer id) {final Optional<CustomersEntity> byId = customerRepo.findById(id);
        return byId.map(JSONUtil::toJsonStr).orElse(null);
    }

     // todo 缓存 json,不乱码已解决好, 调整序列化和反序列化
    @Override
    @Cacheable(value = "cache:customer", unless = "null == #result", key = "#root.methodName +'.'+ #id")
    public CustomersEntity cacheOne5(Integer id) {Optional<CustomersEntity> byId = customerRepo.findById(id);
        return byId.filter(obj -> !StrUtil.isBlankIfStr(obj)).orElse(null);
    }



    // ================================== 删除缓存
    @Override
    @CacheEvict(value = "cache:customer", key = "'cacheOne5' + '.' + #id")
    public Object del(Integer id) {
        // 删除缓存后的逻辑
        return null;
    }

    @Override
    @CacheEvict(value = "cache:customer",allEntries = true)
    public void del() {}

    @CacheEvict(value = "cache:all",allEntries = true)
    public void delall() {}
    // ==================List 操作

    @Override
    @Cacheable(value = "cache:all")
    public List<CustomersEntity> cacheList() {List<CustomersEntity> all = customerRepo.findAll();
        return all;
    }

    // todo 先查问缓存,再校验是否统一,而后更新操作,比拟实用,要分明缓存的数据格式(明确业务和缓存模型数据)@Override
    @CachePut(value = "cache:all",unless = "null == #result",key = "#root.methodName")
    public List<CustomersEntity> cacheList2() {List<CustomersEntity> all = customerRepo.findAll();
        return all;
    }

}

4.3 扩大

基于 spring 缓存实现

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿 (2021 最新版)

2. 别在再满屏的 if/ else 了,试试策略模式,真香!!

3. 卧槽!Java 中的 xx ≠ null 是什么新语法?

4.Spring Boot 2.5 重磅公布,光明模式太炸了!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0