Various implementations of Spring Boot operating Redis

1. The difference between Jedis, Redisson, and Lettuce

What they have in common: Both provide Java APIs based on Redis operations, but the degree of encapsulation and specific implementation are slightly different.

difference:

1.1、Jedis

is a client for the Java implementation of Redis. Support basic data types such as: String, Hash, List, Set, Sorted Set.

Features: Use blocking I/O, method calls are synchronous, the program flow needs to wait until the socket finishes processing the I/O to execute, and does not support asynchronous operations. Jedis client instances are not thread-safe and need to use Jedis through a connection pool.

1.2、Redisson

Advantages: Distributed locks, distributed collections, support delay queues through Redis.

1.3、 Lettuce

For thread-safe synchronous, asynchronous and reactive usage, with support for clusters, Sentinel, pipes and encoders.

An event-driven communication layer based on the Netty framework, whose method calls are asynchronous. Lettuce’s API is thread-safe, so you can operate a single Lettuce connection to complete various operations.

2. RedisTemplate

2.1, use configuration

Maven configuration import, (to add version number, I am here because Parent has declared)

<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 # Specify the sub-library of redis (a total of 16 0 to 15)

2.2, usage examples

@Resource
 private StringRedisTemplate stringRedisTemplate;
 
    @Override
    public CustomersEntity findById(Integer id) {
        // need cache
        // All involved caches need to be deleted, or updated
        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();
        }
        // When the cache is empty, check first, then cache 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, extension

2.3.1, spring-boot-starter-data-redis dependency package

b282ab1a246145a6b9294cd67a2fd0ce.png

3.3.2, stringRedisTemplate API (partial display)
  • opsForHash –> hash operation

  • opsForList –> list operation

  • opsForSet –> set operation

  • opsForValue –> string operation

  • opsForZSet –> Zset operation

8d90a62c2f93b0994d48082b995cebcb.png

3.3.3 StringRedisTemplate default serialization mechanism
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);
 }
 }

3. RedissonClient operation example

3.1 Basic Configuration

3.1.1, Maven pom introduction
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

    org.redisson
    redisson
    3.8.2
    true


    org.redisson
    redisson-spring-boot-starter
    LATEST
3.1.2, add configuration file Yaml or json format

redisson-config.yml

# Redisson configuration
singleServerConfig:
  address: "redis://192.168.1.140:6379"
  password: null
  clientName: null
  database: 15 #choose which database to use 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"

Alternatively, configure 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, read configuration

New read configuration class

@Configuration
public class RedissonConfig {

    @Bean
    public RedissonClient redisson() throws IOException {

        // Two reading methods, Config.fromYAML and 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);
    }
}

Or, configure it in application.yml as follows

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

3.2 Example of use

@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) {
        // set the string
        RBucket<String> keyObj = redissonClient.getBucket(key);
        keyObj.set(key + "1-v1");
        return key;
    }

    @GetMapping("/get/{key}")
    public String g1(@PathVariable String key) {
        // set the string
        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);
      // Store 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 query
        RMap<String, Ur> ss = redissonClient.getMap("UR");
        Ur ur = ss. get(id);
        return ur.toString();
    }

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

    // ======================================== Read-write lock test ============ ==================

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

    // subtraction operation
    @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("================ thread has ended ========================== ==========" + s);

        return s;
    }

}

4.3 Extension

4.3.1 Rich jar support, especially for Netty NIO framework

4.3.2 Rich selection of configuration mechanisms, here is the detailed configuration instructions

https://github.com/redisson/redisson/wiki/2.-Configuration

Regarding the serialization mechanism, there are many

15fa3c161454bb2182175318b26fc3c9.png
445825f7ee13d13fca7e45a8f7b9fb39.png

4.3.3 API support (partial display), specific Redis –> RedissonClient, you can check here

https://github.com/redisson/redisson/wiki/11.-Redis-commands-mapping

e76524de47c1d0027809eca0eb1489af.png

4.3.4 Implementation of Portable and Rich Locking Mechanism

  • lock

  • Fair Lock

  • MultiLock

  • Red Lock

  • ReadWriteLock

  • Semaphore

  • PermitExpirableSemaphore

  • CountDownLatch

4. Redis cache based on annotation

4.1 Maven and YML configuration

Refer to RedisTemplate configuration. In addition, additional configuration classes are required

// todo defines serialization to solve the problem of garbled characters
@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);

        // Solve the problem of query cache conversion exception
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // Configure serialization (to solve the problem of garbled characters)
        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 Example of use

@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";

    // ================================================== ======================= Use Spring cahce annotations to implement caching
    // ====================================== Single operation

    @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 custom redis cache 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 is cached in redis here, and the response page is String (with a lot of escape characters \,), not in Json format
    @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 caches json, no garbled characters have been processed, adjust serialization and deserialization
    @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);
    }



    // ========================================================================================= all cache
    @Override
    @CacheEvict(value = "cache:customer", key = "'cacheOne5' + '.' + #id")
    public Object del(Integer id) {
        // Logic after deleting the cache
        return null;
    }

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

    }

    @CacheEvict(value = "cache:all",allEntries = true)
    public void delall() {

    }
    // ===================List operation

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

    // todo queries the cache first, then checks whether it is consistent, and then updates the operation, which is more practical. It is necessary to know the data format of the cache (clear business and cache model data)
    @Override
    @CachePut(value = "cache:all",unless = "null == #result",key = "#root.methodName")
    public List<CustomersEntity> cacheList2() {
        List<CustomersEntity> all = customerRepo. findAll();
        return all;
    }

}

4.3 Extension

Implementation based on spring cache

99e3bb768ac7204a12d6e2412041ef12.png


Source: blog.csdn.net/qq_42105629/article/details/102589319



······················································



Data link

Self-taught Linux Notes from Tsinghua Senior Sister, Ceiling Level! The new version of "Illustrated Java" summarized by Mr. Ali's private kitchen information is popular, and the full version of PDF is open for download!
Alibaba is officially launched! SpringBoot + SpringCloud full-color guide The strongest SpringBoot + Vue full-stack project ceiling in China, no rebuttals are accepted! 

Welcome to add personal WeChat itcodexy into the fan group or watch the circle of friends

f657920dfe1467873e4ee2364e24ff3d.gif