精华内容
下载资源
问答
  • “缓存击穿、缓存穿透、缓存雪崩及其解决方式*Redis实现分布式锁(代码实现和Jmeter压测)*RedisAPI(jedis、Lettuce)*SpringBoot里使用Redis*JAVA与Redis交互时的序列化问题”这一章全是干货,客官好好读,面试帮助大~...

    f84a94883db5e6ec70b9b2e6328e7c19.png

    缓存击穿、缓存穿透、缓存雪崩及其解决方式*Redis实现分布式锁(代码实现和Jmeter压测)*RedisAPI(jedis、Lettuce)*SpringBoot里使用Redis*JAVA与Redis交互时的序列化问题

    这一章全是干货,客官好好读,面试帮助大~

    01

    缓存击穿、缓存穿透、缓存雪崩及其解决方式

    *缓存击穿

    6138097a9e939d8c283dfcf9cbd1ac08.png

    先来解释一下这个过程。我们知道一般Redis缓存会给key一个有效时间,到期则会去掉这个key(或者LRU淘汰了这个KEY),如果这个时候恰好有一个大流量访问这个key,所有客户端请求就会落到数据库,注意,这里是一个key过期,就好像在redis这层开了一个小窟窿,大流量通过这个小窟窿流到了数据库上把数据库压垮了。这种情况是小概率事件,但是对于大数据平台,就要当心这个问题。

    那么该如何处理这个问题呢?

    我们应该在业务层做预防,1.线程去调用getKey()方法。2.如果获取不到所有线程需要去获取该key的redis锁,调用setnx()方法并设置过期时间(这里是怕该查询线程卡死),获取不到的线程则不能继续业务,每睡一段时间然后循环去get,直到get到。3.获取到锁的线程去数据库查询,将查询到的数据写回到redis,让循环中的线程继续下去。

    OK,上面是常规的处理,但是这里面有个问题,就是有些特殊情况,锁超时了以后,获取锁的线程还是没查询到,这时候后面就又会有一个线程冲进去了,一下来了两下,当然这如果在缓存查询当然是没问题的,可如果是用这种方式分布式锁,那么可能就会悲剧了,一个用户连点两下下单,居然落库了!完了你还在感叹,what?我不是加了锁为啥不行啊。(别问我为什么说的这么形象,遇到过而已)。这个时候想完美,就要另启动一个监控线程,一直去查询获取key的redis是否完成了查询,没完成要更新过期时间,直到查询到为止。

    这块代码,我们在“02 分布式锁里直接给出”

    *缓存穿透

    8d1f6533a13474ec229b36893d74b3d8.png

    这个前面在说布隆过滤器的时候已经说了,就大致说一下。前台疯狂查询缓存中没有的数据,因为缓存中没有,所以查询都落到了数据库上。布隆有个缺点就是不能进行删除(因为其算法),但是这种可以人为控制,如果数据库查询不到就在redis存个key然后值为null,这样压力也不会落到数据库上。

    *缓存雪崩

    36c1fe87166da727d30e5f49a45ba553.png

    雪崩和击穿很相似,但是问题在失效的key的数据量上,比如一个电商每天凌晨定时要更新一波缓存,这时就会有大量的key失效,这个时候不需要很大的查询流量(因为key多了),也可能会把数据库压塌。根据数据特性解决方案有2:1、时点性无关的数据,可以设置随机过期时间,防止key在同一时间过期来解决。2、时点性有关的数据,需要强依赖击穿方案,当然那个时间点的查询会很慢。

    02

    Redis实现分布式锁

    1ba1c00fc780abddd706a5f284bada57.png

    分布式锁的最佳(coding最简单)实现是zk。以后学到说。

    我们先看看一个实现

    package utils.lock;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import java.util.Calendar;import java.util.Date;import java.util.concurrent.TimeUnit;/** * redis锁 调用lock锁住资源,方法执行完后要调用unlock释放锁 * * @author oracle */@Componentpublic class RedisLockUtil {    @Autowired    RedisTemplate redisTemplate;    Logger log = LoggerFactory.getLogger(getClass());    private String prefix = "project:jicai:";//锁前缀    public RedisTemplate getRedisTemplate() {        return redisTemplate;    }    public void setRedisTemplate(RedisTemplate redisTemplate) {        this.redisTemplate = redisTemplate;    }    public RedisLockUtil() {    }    public RedisLockUtil(RedisTemplate redisTemplate) {        this.redisTemplate = redisTemplate;    }    public Boolean lock(String key) {        while (!getLock(key)) {            try {                Thread.sleep(200);// 获取不到锁等待200毫秒            } catch (InterruptedException e) {                // TODO Auto-generated catch block                e.printStackTrace();            }        }        return true;    }    private Boolean getLock(String key) {        key = prefix + key;        if (redisTemplate.boundValueOps(key).setIfAbsent(new Date())) {// 获取锁            log.info("===============lock key=" + key);            return true;        } else {            Date oldKeyTime = (Date) redisTemplate.opsForValue().get(key); // 未获取锁,检查是否死锁            if (oldKeyTime != null) {                Date now = new Date();                Calendar calendar = Calendar.getInstance();                calendar.setTime(oldKeyTime);                calendar.add(Calendar.MINUTE, 10);                Date outTime = calendar.getTime();                if (outTime.before(now)) { // 设置锁时间超过10分钟未被释放,判断为死锁                    if (redisTemplate.boundValueOps(key + ":timeOut").setIfAbsent(                            new Date())) {                        redisTemplate.delete(key); // 清除死锁                        redisTemplate.boundValueOps(key).setIfAbsent(new Date());                        redisTemplate.expire(key + ":timeOut", 5, TimeUnit.SECONDS);                        return true;                    }                }            }        }        return false;    }    public void unlock(String key) {        key = prefix + key;        try {            log.info("===============unlock key=" + key);            redisTemplate.delete(key);        } catch (Exception e) {            e.printStackTrace();        }    }    public String getPrefix() {        return prefix;    }    public void setPrefix(String prefix) {        this.prefix = prefix;    }}

    并发测试

     @Autowired    RedisLockUtil redisLockUtil;    private int a = 0;    public  void testRedisCacheBreakdown(){        CountDownLatch countDownLatch = new CountDownLatch(1000);        for (int i=0;i<1000;i++){            new Thread(()-> {                try {                    countDownLatch.countDown();                    countDownLatch.await();                    //String pw = redisTemplateUtil.getKeyWithDistributedModel("pw");                    redisLockUtil.lock("pw");                    a++;                    System.out.println(a);                    redisLockUtil.unlock("pw");//                    System.out.println("ok");                } catch (InterruptedException e) {                    e.printStackTrace();                }            },"t1"+i).start();        }    }

    结果是1000没啥问题,说明这个锁在正常情况下是好的。

    然后我们改下锁内等待时间然后改小超时时间

    0fbe23d832b8042ec3246a7d285d8d4d.png

    159ff79ab4091803d1847436974543d9.png

    851e5f6b5f48ac0be3158f1a0251f654.png

    已经出现并发问题了。

    这段代码问题在于,如果真的有超时10分钟,如果是高并发情况那么后面来的线程会同时去获取到锁,达不到分布式锁的效果,但是一般似乎也没有一个业务弄个10分钟也就是了,我理解其实这段代码说的死锁并不是死锁,而是说锁间业务超时,或者是在线程解锁的过程中线程异常,导致不能解锁,而这个代码最有问题的地方就是这个时间了,10分钟超时太长,不适用于并发,你总不能抢商品时,让用户等10分钟吧。

    OK,我们再来个程序分析下,然后用Jmeter来压测,这次直接上一般完整的秒杀扣减库存业务时使用Redis分布式锁。(这个程序面对高并发还是会有小问题)

    先上代码

    package cn.pw.studyJavaDemo.controller;import org.redisson.Redisson;import org.redisson.api.RLock;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.UUID;import java.util.concurrent.TimeUnit;@Controller@RestController@RequestMapping("redissonLock")public class TestRedissonLockController {    @Autowired    Redisson redisson;    @Autowired    StringRedisTemplate redisTemplate;    @RequestMapping("stockDecrease")    public String stockDecrease() {        String key = "ball";        String lockKey = "lockKey" + key;        String clientId = UUID.randomUUID().toString();        RLock lock = null;        try {            lock = redisson.getLock(lockKey);            if(lock != null){                boolean status = false;                try {                    status = lock.tryLock(10, TimeUnit.SECONDS);                } catch (InterruptedException e) {                    e.printStackTrace();                }                if(status){                int stock = Integer.parseInt(redisTemplate.opsForValue().get(key));                if (stock > 0) {                    int stockRest = redisTemplate.opsForValue().decrement(key).intValue();                    System.out.println(stockRest);                } else {                    System.out.println("扣减库存失败,库存不足");                    return "库存不足";                }            }            }        }  finally {            if(lock != null){                lock.unlock();            }        }    return "恭喜抢到商品";    }}

    首先,假使扣减库存有很多的逻辑,第一个线程过了锁的超时时间还在做任务,那么这个时候第二个线程就会获取锁并开始跑代码了(我们等下让线程睡眠来模拟),如果恰好这时候第一个线程跑过了,然后解了锁(注意这时这把锁已经不是第一个线程上的那把了),结果第三个线程进来了,完蛋,并发问题产生了。那么其实就是有两个问题:

    1. 怎么让每个线程只解自己的锁?

    2. 如何让锁过了超时时间,任务没执行完,会自动续费?

    1.clientId就是为了防止锁超时后,第一个线程把第二个线程的锁给解了,导致第三个线程直接就获得到锁进入程序,导致超卖,加了clientId可以防止自己去解别人上的锁。(这个上面的程序已经写出了)

    2.获取锁后开启一个线程,里面有定时器,隔1/3的超时时间去检查锁是否还存在,存在就续费超时时间重新设置为10s,如果不存在就可以关闭这个线程(也可以用线程池,复用这个线程)。但是如果抛了异常,这个线程要捕获异常,停止计时,超时时间一到锁就自然解开了。(这部分代码没写)

    8fbe683cf721957aff3d42aaed0a23c7.png

    其实这个原理图是Redisson的,没错,已经有客户端给实现了,我们拿来用就好。(但是Redisson对于Redis主备不支持,意思就是如果主坏了,从顶上来的过程中还没同步到主的锁数据,前面我们看了,哨兵切换准备时间开始很长的,这个时候锁也会失效,我去这真的是超超超级并发了)。

    然后给redission的代码

    package cn.pw.studyJavaDemo.controller;import org.redisson.Redisson;import org.redisson.api.RLock;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.UUID;import java.util.concurrent.TimeUnit;@Controller@RestController@RequestMapping("redissonLock")public class TestRedissonLockController {    @Autowired    Redisson redisson;    @Autowired    StringRedisTemplate redisTemplate;    @RequestMapping("stockDecrease")    public String stockDecrease() {        String key = "ball";        String lockKey = "lockKey" + key;        String clientId = UUID.randomUUID().toString();        RLock lock = null;        try {            lock = redisson.getLock(lockKey);            if(lock != null){                boolean status = false;                try {                    status = lock.tryLock(10, TimeUnit.SECONDS);                } catch (InterruptedException e) {                    e.printStackTrace();                }                if(status){                int stock = Integer.parseInt(redisTemplate.opsForValue().get(key));                if (stock > 0) {                    int stockRest = redisTemplate.opsForValue().decrement(key).intValue();                    System.out.println(stockRest);                } else {                    System.out.println("扣减库存失败,库存不足");                    return "库存不足";                }            }            }        }  finally {            if(lock != null){                lock.unlock();            }        }    return "恭喜抢到商品";    }}
     @Bean    public Redisson redisson(){        Config config = new Config();        config.useSingleServer().setAddress("redis://139.224.119.73:6380").setDatabase(0);        return (Redisson) Redisson.create(config);    }
      <dependency>      <groupId>org.redissongroupId>      <artifactId>redissonartifactId>      <version>3.5.5version>    dependency>

    最后我们分别对两个代码进行压测。

    这里要在本机装上nginx进行反向代理,然后用idea启动两台进行测试。

    1fe87bf4434590b6be4a5e7f0e8ab36d.png

    这样就模拟了分布式场景。

    然后我们用Jmeter来测试

    Jmeter安装:

    https://www.cnblogs.com/molao-doing/articles/7202110.html

    自己看这篇文章哈

    很简单我就直接开始用了

    89f5b589749740ea712d1bbe84083bfe.png

    200个请求瞬时并发,循环4次。

    fe784fdc369c82ce7b45d005bc040822.png

    配置请求

    这次我们现在Redis里面给100个库存。

    然后先测试我们自己用redisTemplate实现的分布式锁

    访问:http://localhost/redisLock/stockDecrease

    c81d948ad2fb9da2dc9ff048deb5d98a.png

    Emm,这种方式拦截的请求就特别多,800个请求大概只会发出去20个商品,但不会超卖。

    访问 :http://localhost/redissonLock/stockDecrease

    dcbbeebb7821177fc27f2a4d154c6823.png

    对比了日志,没有超卖的现象,但是解锁的时候如果解了别人的锁redisson会报异常,这个catch一下就好了。这种方式则尽可能多的发出了商品但是不超卖。

    但是,公认的分布式锁还是用ZK,等我们在ZK章节的时候再撸一遍。

    03

    RedisAPI

    Redis在java中的多个客户端:

    https://redis.io/clients#java

    Javaaredis           Asynchronous, pipelined client based on the Java 7 NIO Channel API   java-redis-client           A very simple yet very complete java client in less than 200 lines with 0 dependencies.   JDBC-Redis             mavcunhaJedipus           Redis Client & Command Executor.   jamespedwardsJedis           A blazingly small and sane redis java client   xetorthioJRedis             SunOf27lettuce           Advanced Redis client for thread-safe sync, async, and reactive usage. Supports Cluster, Sentinel, Pipelining, and codecs.   ar3te mp911deredis-protocol           Up to 2.6 compatible high-performance Java, Java w/Netty & Scala (finagle) client   spullaraRedisClient           redis client GUI tool   Redisson           distributed and scalable Java data structures on top of Redis server   mrnikoRJC             e_mzunguvertx-redis-client           The Vert.x Redis client provides an asynchronous API to interact with a Redis data-structure server.   pmlopesviredis           A simple and small redis client for java.

    a69687eb56d8dc72aac31c8c64205a7d.png

    最火的是标星的几个(Jedis,lettuce,Redisson)

    可以看到,只有lettuce是线程安全的,而lettuce是现在默认使用的

    *Jedis

    Jedis的github:

    https://github.com/xetorthio/jedis

    使用方式

    How do I use it?You can download the latest build at: http://github.com/xetorthio/jedis/releasesOr use it as a maven dependency:Official Releases    redis.clients    jedis    <version>3.2.0version>    <type>jartype>    <scope>compilescope>    redis.clients    jedis    <version>2.10.2version>    <type>jartype>    <scope>compilescope>Snapshots            <id>snapshots-repoid>      <url>https://oss.sonatype.org/content/repositories/snapshotsurl>      and            redis.clients      jedis      <version>3.3.0-SNAPSHOTversion>      To use it just:Jedis jedis = new Jedis("localhost");jedis.set("foo", "bar");String value = jedis.get("foo");For more usage examples check the tests.Please check the wiki. There are lots of cool things you should know, including information about connection pooling.Master branch javadocs can be found here: http://xetorthio.github.io/jedis/And you are done!Jedis ClusterRedis cluster specification is implementedSet jedisClusterNodes = new HashSet();//Jedis Cluster will attempt to discover cluster nodes automaticallyjedisClusterNodes.add(new HostAndPort("127.0.0.1", 7379));JedisCluster jc = new JedisCluster(jedisClusterNodes);jc.set("foo", "bar");String value = jc.get("foo");

    注意,Redis想远程访问要改配置

    50ccfc44903feae0df51fc525ddf9acf.png

    public class JedisTest {    public static void main(String[] args) {        Jedis jedis = new Jedis("139.224.119.73",6380);        jedis.set("pw", "hello");        String value = jedis.get("pw");        System.out.println(value);    }
    [root@iZuf6celicizfplk0e77e8Z conf]# redis-cli -p 6380127.0.0.1:6380> keys *1) "pw"127.0.0.1:6380

    29d9970d195e0c5a0e25e0f72e5ce65b.png

    Jedis的问题就在于如果多个客户端同时使用一个Jedis会造成一致性问题,一般加一个连接池去解决。

    简单说一下就好,下面讲lettuce

    *lettuce

    使用方式

    Binaries/DownloadBinaries and dependency information for Maven, Ivy, Gradle and others can be found at http://search.maven.org.Releases of lettuce are available in the Maven Central repository. Take also a look at the Releases.Example for Maven:<dependency>  <groupId>io.lettucegroupId>  <artifactId>lettuce-coreartifactId>  <version>x.y.zversion>dependency>If you'd rather like the latest snapshots of the upcoming major version, use our Maven snapshot repository and declare the appropriate dependency version.<dependency>  <groupId>io.lettucegroupId>  <artifactId>lettuce-coreartifactId>  <version>x.y.z.BUILD-SNAPSHOTversion>dependency><repositories>  <repository>    <id>sonatype-snapshotsid>    <name>Sonatype Snapshot Repositoryname>    <url>https://oss.sonatype.org/content/repositories/snapshots/url>    <snapshots>      <enabled>trueenabled>    snapshots>  repository>repositories>Basic UsageRedisClient client = RedisClient.create("redis://localhost");StatefulRedisConnection<String, String> connection = client.connect();RedisStringCommands sync = connection.sync();String value = sync.get("key");
     public static void main(String[] args) {        RedisClient client = RedisClient.create("redis://139.224.119.73:6380");        StatefulRedisConnection connection = client.connect();        RedisStringCommands sync = connection.sync();        String value = (String) sync.get("pw");        System.out.println(value);    }

    测试也木有问题。

    然后我们用Spring和SpringBoot来整合redis

    Spring

    SpringData-Redis

    https://docs.spring.io/spring-data/redis/docs/2.3.0.RELEASE/reference/html/#reference

    0722e1c2c59be6898eb7f90d15bf8d0a.png

    SpringData支持的客户端

    6743c4e5dc36084034d1d0a5c6c28109.png

    可以看到支持Lettuce 和Jedis,默认是Lettuce

    使用Lettuce如何配置

    10.4.2. Configuring the Lettuce ConnectorLettuce is a Netty-based open-source connector supported by Spring Data Redis through the org.springframework.data.redis.connection.lettuce package.Add the following to the pom.xml files dependencies element:<dependencies>    <dependency>    <groupId>io.lettucegroupId>    <artifactId>lettuce-coreartifactId>    <version>5.3.0.RELEASEversion>  dependency>dependencies>The following example shows how to create a new Lettuce connection factory:@Configurationclass AppConfig {  @Bean  public LettuceConnectionFactory redisConnectionFactory() {    return new LettuceConnectionFactory(new RedisStandaloneConfiguration("server", 6379));  }}

    使用Jedis如何配置

    10.4.3. Configuring the Jedis ConnectorJedis is a community-driven connector supported by the Spring Data Redis module through the org.springframework.data.redis.connection.jedis package.Add the following to the pom.xml files dependencies element:<dependencies>    <dependency>    <groupId>redis.clientsgroupId>    <artifactId>jedisartifactId>    <version>3.3.0version>  dependency>dependencies>In its simplest form, the Jedis configuration looks as follow:@Configurationclass AppConfig {  @Bean  public JedisConnectionFactory redisConnectionFactory() {    return new JedisConnectionFactory();  }}For production use, however, you might want to tweak settings such as the host or password, as shown in the following example:@Configurationclass RedisConfiguration {  @Bean  public JedisConnectionFactory redisConnectionFactory() {    RedisStandaloneConfiguration config = new RedisStandaloneConfiguration("server", 6379);    return new JedisConnectionFactory(config);  }}

    主从配置(主要是读写分离那里)

    10.4.4. Write to Master, Read from ReplicaThe Redis Master/Replica setup — without automatic failover (for automatic failover see: Sentinel) — not only allows data to be safely stored at more nodes. It also allows, by using Lettuce, reading data from replicas while pushing writes to the master. You can set the read/write strategy to be used by using LettuceClientConfiguration, as shown in the following example:@Configurationclass WriteToMasterReadFromReplicaConfiguration {  @Bean  public LettuceConnectionFactory redisConnectionFactory() {    LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()      .readFrom(SLAVE_PREFERRED)      .build();//是否读写分离    RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration("server", 6379);    return new LettuceConnectionFactory(serverConfig, clientConfig);  }}

    哨兵连接

    /** * Jedis */@Beanpublic RedisConnectionFactory jedisConnectionFactory() {  RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()  .master("mymaster")  .sentinel("127.0.0.1", 26379)  .sentinel("127.0.0.1", 26380);  return new JedisConnectionFactory(sentinelConfig);}/** * Lettuce */@Beanpublic RedisConnectionFactory lettuceConnectionFactory() {  RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration()  .master("mymaster")  .sentinel("127.0.0.1", 26379)  .sentinel("127.0.0.1", 26380);  return new LettuceConnectionFactory(sentinelConfig);}

    另外Spring提供了两种使用方式:

    1. RedisTemplate,高级别API,这种方式会提供很多好用的API,用户友好度高。

      8a576e4cf86b5f8c60a7b56e77098341.png

    2. redisTemplate.getConnectionFactory().getConnection(),低级别的API,但是用户可以更灵活的去使用底层指令。

      2a6564382471be78680dc87ef510803e.png

    代码使用

    public class Example {  // inject the actual template  @Autowired  private RedisTemplate<String, String> template;  // inject the template as ListOperations  @Resource(name="redisTemplate")  private ListOperations<String, String> listOps;  public void addLink(String userId, URL url) {    listOps.leftPush(userId, url.toExternalForm());  }}

    依赖添加

    <dependency>      <groupId>org.springframework.bootgroupId>      <artifactId>spring-boot-starter-data-redisartifactId>dependency>
    import cn.pw.studyJavaDemo.Redis.TestRedis;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.ConfigurableApplicationContext;@SpringBootApplicationpublic class MainApplication {  public static void main(String[] args) {    ConfigurableApplicationContext run = SpringApplication.run(MainApplication.class, args);    TestRedis redis = run.getBean(TestRedis.class);    redis.testRedis();  }}

    主程序调用

    package cn.pw.studyJavaDemo.Redis;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.data.redis.connection.Message;import org.springframework.data.redis.connection.MessageListener;import org.springframework.data.redis.connection.RedisConnection;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.hash.Jackson2HashMapper;import org.springframework.stereotype.Component;import java.util.Map;@Componentpublic class TestRedis {    @Autowired    RedisTemplate  redisTemplate;    @Autowired    @Qualifier("ooxx")    StringRedisTemplate  stringRedisTemplate;    @Autowired    ObjectMapper  objectMapper;    public void testRedis(){        stringRedisTemplate.opsForValue().set("hello01","china");        System.out.println(stringRedisTemplate.opsForValue().get("hello01"));//        RedisConnection conn = redisTemplate.getConnectionFactory().getConnection();////        conn.set("hello02".getBytes(),"mashibing".getBytes());//        System.out.println(new String(conn.get("hello02".getBytes())));//        HashOperations hash = stringRedisTemplate.opsForHash();//        hash.put("sean","name","zhouzhilei");//        hash.put("sean","age","22");////        System.out.println(hash.entries("sean"));        Person p = new Person();        p.setName("zhangsan");        p.setAge(16);//        stringRedisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer(Object.class));        Jackson2HashMapper jm = new Jackson2HashMapper(objectMapper, false);        stringRedisTemplate.opsForHash().putAll("sean01",jm.toHash(p));        Map map = stringRedisTemplate.opsForHash().entries("sean01");        Person per = objectMapper.convertValue(map, Person.class);        System.out.println(per.getName());        stringRedisTemplate.convertAndSend("ooxx","hello");        RedisConnection cc = stringRedisTemplate.getConnectionFactory().getConnection();        cc.subscribe(new MessageListener() {            @Override            public void onMessage(Message message, byte[] pattern) {                byte[] body = message.getBody();                System.out.println(new String(body));            }        }, "ooxx".getBytes());        while(true){            stringRedisTemplate.convertAndSend("ooxx","hello  from wo zi ji ");            try {                Thread.sleep(3000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

    测试代码,大家可以自己测试下,Template可以去我的代码仓库下载,都是基本用法,有String,hash,消息的使用。

    SpringBoot

    https://docs.spring.io/spring-boot/docs/2.1.8.RELEASE/reference/html/

    SpringBoot只告诉我们四种连接方式

    32.1.1 Connecting to RedisYou can inject an auto-configured RedisConnectionFactory, StringRedisTemplate, or vanilla RedisTemplate instance as you would any other Spring Bean. By default, the instance tries to connect to a Redis server at localhost:6379. The following listing shows an example of such a bean:@Componentpublic class MyBean {  private StringRedisTemplate template;  @Autowired  public MyBean(StringRedisTemplate template) {    this.template = template;  }  // ...}

    04

    JAVA与Redis交互时的序列化问题

    1629b0c97cfa48560d8e459775fcee76.png

    在设置Template的时候如果不设置这个序列化方式,JAVA传到Redis的数据在Redis客户端会乱码,这是因为Redis是二进制安全的(I/O流为字节流)。虽然在Redis客户端会乱码,但是Java程序取回的时候还是正常的,这个叫二进制安全。

    b633814fffcaebb44343cf2f4ed4d332.png

    项目源码:

    https://github.com/pengwenqq/studyDemo

    展开全文
  • 一.SpringBoot集成Redis1.pom.xml文件添加spring-boot-starter-data-redis依赖<dependencies> <dependency> <groupId>org.springframework.bootgroupId> <artifactId>sprin...

    c48d1e5ca23b58a3037ab480cade9e1e.png

    一.SpringBoot集成Redis

    1.pom.xml文件添加spring-boot-starter-data-redis依赖

    <dependencies>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-data-redisartifactId>    dependency>    <dependency>        <groupId>org.apache.commonsgroupId>        <artifactId>commons-pool2artifactId>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-webartifactId>    dependency>    <dependency>        <groupId>org.springframework.sessiongroupId>        <artifactId>spring-session-data-redisartifactId>    dependency>    <dependency>        <groupId>org.projectlombokgroupId>        <artifactId>lombokartifactId>        <optional>trueoptional>    dependency>    <dependency>        <groupId>org.springframework.bootgroupId>        <artifactId>spring-boot-starter-testartifactId>        <scope>testscope>    dependency>dependencies>

    依赖说明:

    • spring-boot-starter-data-redis:在Spring Boot 2.x 以后底层不再使用 Jedis,而是换成了Lettuce。

    • commons-pool2:用作 Redis 连接池,如不引入启动会报错。

    • spring-session-data-redis:Spring Session 引入,用作共享 Session。

    2.配置文件application.properties

    ## Redis部分# Redis数据库索引(默认为0)spring.redis.database=0# Redis服务器地址spring.redis.host=106.14.72.179# Redis服务器连接端口spring.redis.port=6379# Redis服务器连接密码(默认为空)spring.redis.password=# 连接池最大连接数(使用负值表示没有限制)spring.redis.jedis.pool.max-active=8# 连接池最大阻塞等待时间(使用负值表示没有限制)spring.redis.jedis.pool.max-wait=-1ms# 连接池中的最大空闲连接spring.redis.jedis.pool.max-idle=8# 连接池中的最小空闲连接spring.redis.jedis.pool.min-idle=0# 连接超时时间(毫秒)spring.redis.timeout=5000

    3.配置RedisConfig(加载方式,推荐使用方式一)

    方式一:使用Autoconfiguration自动加载

    因为上面引入了spring-boot-start-data-redis,所以可以使用RedisAutoConfiguration类加载properties文件的配置。因此我们只要在使用的地方注入即可。

    @AutowiredStringRedisTemplate stringRedisTemplate; //操作 k-v 字符串@AutowiredRedisTemplate redisTemplate;  //k- v 都是对象

    说明:

    Spring Boot关于Spring Data Redis的自动配置类。该自动配置类检测到包spring-boot-start-data-redis被使用时才应用。并且导入了另外两个配置类LettuceConnectionConfiguration,JedisConnectionConfiguration,这两个配置类是用于配置底层Redis连接组件RedisConnectionFactory,一种基于Lettuce Redis客户端实现,一种基于Jedis Redis客户端实现,不会同时生效。因为包spring-boot-starter-data-redis自身依赖lettuce,所以缺省情况下,LettuceConnectionConfiguration会生效,JedisConnectionConfiguration不生效。

    RedisAutoConfiguration自身主要的作用是确保以下bean存在于容器中 :

    • RedisTemplate redisTemplate – 基于容器中的redisConnectionFactory bean

    • StringRedisTemplate stringRedisTemplate – 基于容器中的redisConnectionFactory bean

    源代码(spring-boot-autoconfigure-2.1.3.RELEASE):

    package org.springframework.boot.autoconfigure.data.redis;// 省略 import 行/** * EnableAutoConfiguration Auto-configuration for Spring Data's Redis support. * */@Configuration@ConditionalOnClass(RedisOperations.class)@EnableConfigurationProperties(RedisProperties.class)@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })public class RedisAutoConfiguration {    // 定义 bean RedisTemplate redisTemplate  @Bean    // 仅在该 bean 不存在的情况下才定义  @ConditionalOnMissingBean(name = "redisTemplate")  public RedisTemplate redisTemplate(      RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {    RedisTemplate template = new RedisTemplate<>();    template.setConnectionFactory(redisConnectionFactory);    return template;  }    // 定义 bean StringRedisTemplate  stringRedisTemplate  @Bean      // 仅在该 bean 不存在的情况下才定义  @ConditionalOnMissingBean  public StringRedisTemplate stringRedisTemplate(      RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {    StringRedisTemplate template = new StringRedisTemplate();    template.setConnectionFactory(redisConnectionFactory);    return template;  }}

    方式二:自己写代码加载

    package com.guige.core.conf;import com.guige.core.ext.common.redis.MyCacheErrorHandler;import com.guige.core.ext.common.redis.MyKeyGenerator;import com.guige.core.ext.common.redis.MyRedisCacheManager;import com.guige.core.ext.common.redis.MyRedisTemplate;import org.springframework.beans.factory.annotation.Value;import org.springframework.cache.interceptor.CacheErrorHandler;import org.springframework.cache.interceptor.KeyGenerator;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;import redis.clients.jedis.JedisPoolConfig;@Configurationpublic class RedisConf {    @Value("${spring.redis.database}")    private Integer database;    @Value("${spring.redis.host}")    private String host;    @Value("${spring.redis.password}")    private String password;    @Value("${spring.redis.port}")    private Integer port;    @Value("${spring.redis.timeout}")    private Integer timeout;    @Value(value = "${spring.redis.expire:300}")    private Integer expiration;    @Value("${spring.redis.pool.maxactive}")    private int maxActive;    @Value("${spring.redis.pool.minidle}")    private int minIdle;    @Value("${spring.redis.pool.maxidle}")    private int maxIdle;    @Bean    public JedisPoolConfig jedisPoolConfig(){        JedisPoolConfig poolConfig = new JedisPoolConfig();        poolConfig.setMaxIdle(this.maxIdle);        poolConfig.setMinIdle(this.minIdle);        poolConfig.setTestOnCreate(true);        poolConfig.setTestOnBorrow(true);        poolConfig.setTestOnReturn(true);        poolConfig.setTestWhileIdle(true);        return poolConfig;    }    @Bean    public JedisConnectionFactory jedisConnectionFactory(){        JedisPoolConfig config = jedisPoolConfig();        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(config);        jedisConnectionFactory.setDatabase(this.database);        jedisConnectionFactory.setHostName(this.host);        jedisConnectionFactory.setPassword(this.password);        jedisConnectionFactory.setPort(this.port);        jedisConnectionFactory.setTimeout(this.timeout);        return jedisConnectionFactory;    }    @Bean   public  RedisTemplate redisTemplate(){        MyRedisTemplate myRedisTemplate = new MyRedisTemplate();        myRedisTemplate.setConnectionFactory(jedisConnectionFactory());        return myRedisTemplate;    }}##或者如下方式@Configurationpublic class RedisConf {    @Bean    public StringRedisTemplate stringRedisTemplate(@Autowired JedisConnectionFactory jedisConnectionFactory) {        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();        stringRedisTemplate.setConnectionFactory(jedisConnectionFactory);        return stringRedisTemplate;    }}

    4.Controller

    package com.platform.app.member.web;@RestController@RequestMapping(value = "/memberForGoddess")public class MemberForGoddessController {    private Logger logger = LoggerFactory.getLogger(MemberForGoddessController.class);    @Autowired    private RedisTemplate redisCacheTemplate;    private static final String RECEIVE_KEY = "receive:list";//缓存的key,    @PostMapping(value = "/checkSsinsureCode")    public Rst checkCodeForSsinsure(@RequestBody Map phonemap){        logger.info("100001-femaleHelp-校验验证码:入参手机号和验证码:" + phonemap);        String phone = "";        String phoneCode = "";        if (StringUtil.isEmpty(phonemap)){            return Rst.error("手机号或验证码为空");        }else {            phone = phonemap.get("phone");            phoneCode = phonemap.get("phoneCode");            Rst rst = memberForGoddessServivce.checkIPhoneCode(phone,phoneCode);            if(rst.getSts() == 1){                List list = redisCacheTemplate.opsForList().range(RECEIVE_KEY,0,-1);                if(list!=null && !list.isEmpty()){                    if(!list.contains(phone)){                        redisCacheTemplate.opsForList().rightPush(RECEIVE_KEY,phone);                    }                }else{                    redisCacheTemplate.opsForList().rightPush(RECEIVE_KEY,phone);                }            }            return rst;        }    }}

    以上就是SpringBoot集成Redis的基本方法!!!

    5.StringRedisTemplate和RedisTemplate的区别

    使用时只需使用maven依赖包spring-boot-starter-data-redis即可,然后在service中注入StringRedisTemplate或者RedisTemplate即可。

    StringRedisTemplate继承了RedisTemplate,所以两者对Redis的操作方法具有相同之处:

    • 两者的数据是不共通的

      也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。

    • 其实他们两者之间的区别主要在于他们使用的序列化类

      RedisTemplate使用的是JdkSerializationRedisSerializer 存入数据会将数据先序列化成字节数组然后在存入Redis数据库。StringRedisTemplate使用的是StringRedisSerializer,反序列化,则是一个得到 String,一个得到 Object。

    • 使用时注意事项

      当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可。但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。

    • RedisTemplate使用时常见问题

      redisTemplate 中存取数据都是字节数组。当redis中存入的数据是可读形式而非字节数组时,使用redisTemplate取值的时候会无法获取导出数据,获得的值为null。可以使用 StringRedisTemplate 试试。

    StringRedisTemplate对于Redis的操作方法:

    StringRedisTemplate.opsForValue() //操作String字符串类型StringRedisTemplate.delete(key/collection) //根据key/keys删除StringRedisTemplate.opsForList()  //操作List类型StringRedisTemplate.opsForHash()  //操作Hash类型StringRedisTemplate.opsForSet()  //操作set类型StringRedisTemplate.opsForZSet()  //操作有序set

    6.RedisTemplate类常用方法封装

    package com.example.demo.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import org.springframework.util.CollectionUtils;import java.util.Collection;import java.util.List;import java.util.Map;import java.util.Set;import java.util.concurrent.TimeUnit;@Servicepublic class RedisService {  @Autowired  private RedisTemplate redisTemplate;  // =============================common============================  /**   * 指定缓存失效时间   * @param key 键   * @param time 时间(秒)   * @return   */  public boolean expire(String key, long time) {    try {      if (time > 0) {        redisTemplate.expire(key, time, TimeUnit.SECONDS);      }      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 根据key 获取过期时间   * @param key 键 不能为null   * @return 时间(秒) 返回0代表为永久有效   */  public long getExpire(String key) {    return redisTemplate.getExpire(key, TimeUnit.SECONDS);  }  /**   * 判断key是否存在   * @param key 键   * @return true 存在 false不存在   */  public boolean hasKey(String key) {    try {      return redisTemplate.hasKey(key);    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 删除缓存   * @param key 可以传一个值 或多个   */  @SuppressWarnings("unchecked")  public void del(String... key) {    if (key != null && key.length > 0) {      if (key.length == 1) {        redisTemplate.delete(key[0]);      } else {        redisTemplate.delete(CollectionUtils.arrayToList(key));      }    }  }  /**   * 删除缓存   * @param keys 可以传一个值 或多个   */  @SuppressWarnings("unchecked")  public void del(Collection keys) {    if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(keys)) {      redisTemplate.delete(keys);    }  }  // ============================String=============================  /**   * 普通缓存获取   * @param key 键   * @return 值   */  public Object get(String key) {    return key == null ? null : redisTemplate.opsForValue().get(key);  }  /**   * 普通缓存放入   * @param key 键   * @param value 值   * @return true成功 false失败   */  public boolean set(String key, Object value) {    try {      redisTemplate.opsForValue().set(key, value);      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 普通缓存放入并设置时间   * @param key 键   * @param value 值   * @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期   * @return true成功 false 失败   */  public boolean set(String key, Object value, long time) {    try {      if (time > 0) {        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);      } else {        set(key, value);      }      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 递增   * @param key 键   * @param delta 要增加几(大于0)   * @return   */  public long incr(String key, long delta) {    if (delta < 0) {      throw new RuntimeException("递增因子必须大于0");    }    return redisTemplate.opsForValue().increment(key, delta);  }  /**   * 递减   * @param key 键   * @param delta 要减少几(小于0)   * @return   */  public long decr(String key, long delta) {    if (delta < 0) {      throw new RuntimeException("递减因子必须大于0");    }    return redisTemplate.opsForValue().increment(key, -delta);  }  // ================================Map=================================  /**   * HashGet   * @param key 键 不能为null   * @param item 项 不能为null   * @return 值   */  public Object hget(String key, String item) {    return redisTemplate.opsForHash().get(key, item);  }  /**   * 获取hashKey对应的所有键值   * @param key 键   * @return 对应的多个键值   */  public Map hmget(String key) {    return redisTemplate.opsForHash().entries(key);  }  /**   * HashSet   * @param key 键   * @param map 对应多个键值   * @return true 成功 false 失败   */  public boolean hmset(String key, Map map) {    try {      redisTemplate.opsForHash().putAll(key, map);      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * HashSet 并设置时间   * @param key 键   * @param map 对应多个键值   * @param time 时间(秒)   * @return true成功 false失败   */  public boolean hmset(String key, Map map, long time) {    try {      redisTemplate.opsForHash().putAll(key, map);      if (time > 0) {        expire(key, time);      }      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 向一张hash表中放入数据,如果不存在将创建   * @param key 键   * @param item 项   * @param value 值   * @return true 成功 false失败   */  public boolean hset(String key, String item, Object value) {    try {      redisTemplate.opsForHash().put(key, item, value);      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 向一张hash表中放入数据,如果不存在将创建   * @param key 键   * @param item 项   * @param value 值   * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间   * @return true 成功 false失败   */  public boolean hset(String key, String item, Object value, long time) {    try {      redisTemplate.opsForHash().put(key, item, value);      if (time > 0) {        expire(key, time);      }      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 删除hash表中的值   * @param key 键 不能为null   * @param item 项 可以使多个 不能为null   */  public void hdel(String key, Object... item) {    redisTemplate.opsForHash().delete(key, item);  }  /**   * 删除hash表中的值   * @param key 键 不能为null   * @param items 项 可以使多个 不能为null   */  public void hdel(String key, Collection items) {    redisTemplate.opsForHash().delete(key, items.toArray());  }  /**   * 判断hash表中是否有该项的值   * @param key 键 不能为null   * @param item 项 不能为null   * @return true 存在 false不存在   */  public boolean hHasKey(String key, String item) {    return redisTemplate.opsForHash().hasKey(key, item);  }  /**   * hash递增 如果不存在,就会创建一个 并把新增后的值返回   * @param key 键   * @param item 项   * @param delta 要增加几(大于0)   * @return   */  public double hincr(String key, String item, double delta) {    if (delta < 0) {      throw new RuntimeException("递增因子必须大于0");    }    return redisTemplate.opsForHash().increment(key, item, delta);  }  /**   * hash递减   * @param key 键   * @param item 项   * @param delta 要减少记(小于0)   * @return   */  public double hdecr(String key, String item, double delta) {    if (delta < 0) {      throw new RuntimeException("递减因子必须大于0");    }    return redisTemplate.opsForHash().increment(key, item, -delta);  }  // ============================set=============================  /**   * 根据key获取Set中的所有值   * @param key 键   * @return   */  public Set sGet(String key) {    try {      return redisTemplate.opsForSet().members(key);    } catch (Exception e) {      e.printStackTrace();      return null;    }  }  /**   * 根据value从一个set中查询,是否存在   * @param key 键   * @param value 值   * @return true 存在 false不存在   */  public boolean sHasKey(String key, Object value) {    try {      return redisTemplate.opsForSet().isMember(key, value);    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 将数据放入set缓存   * @param key 键   * @param values 值 可以是多个   * @return 成功个数   */  public long sSet(String key, Object... values) {    try {      return redisTemplate.opsForSet().add(key, values);    } catch (Exception e) {      e.printStackTrace();      return 0;    }  }  /**   * 将数据放入set缓存   * @param key 键   * @param values 值 可以是多个   * @return 成功个数   */  public long sSet(String key, Collection values) {    try {      return redisTemplate.opsForSet().add(key, values.toArray());    } catch (Exception e) {      e.printStackTrace();      return 0;    }  }  /**   * 将set数据放入缓存   * @param key 键   * @param time 时间(秒)   * @param values 值 可以是多个   * @return 成功个数   */  public long sSetAndTime(String key, long time, Object... values) {    try {      Long count = redisTemplate.opsForSet().add(key, values);      if (time > 0)      expire(key, time);      return count;    } catch (Exception e) {      e.printStackTrace();      return 0;    }  }  /**   * 获取set缓存的长度   * @param key 键   * @return   */  public long sGetSetSize(String key) {    try {      return redisTemplate.opsForSet().size(key);    } catch (Exception e) {      e.printStackTrace();      return 0;    }  }  /**   * 移除值为value的   * @param key 键   * @param values 值 可以是多个   * @return 移除的个数   */  public long setRemove(String key, Object... values) {    try {      Long count = redisTemplate.opsForSet().remove(key, values);      return count;    } catch (Exception e) {      e.printStackTrace();      return 0;    }  }  // ===============================list=================================  /**   * 获取list缓存的内容   * @param key 键   * @param start 开始   * @param end 结束 0 到 -1代表所有值   * @return   */  public List lGet(String key, long start, long end) {    try {      return redisTemplate.opsForList().range(key, start, end);    } catch (Exception e) {      e.printStackTrace();      return null;    }  }  /**   * 获取list缓存的长度   * @param key 键   * @return   */  public long lGetListSize(String key) {    try {      return redisTemplate.opsForList().size(key);    } catch (Exception e) {      e.printStackTrace();      return 0;    }  }  /**   * 通过索引 获取list中的值   * @param key 键   * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推   * @return   */  public Object lGetIndex(String key, long index) {    try {      return redisTemplate.opsForList().index(key, index);    } catch (Exception e) {      e.printStackTrace();      return null;    }  }  /**   * 将list放入缓存   * @param key 键   * @param value 值   * @return   */  public boolean lSet(String key, Object value) {    try {      redisTemplate.opsForList().rightPush(key, value);      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 将list放入缓存   * @param key 键   * @param value 值   * @param time 时间(秒)   * @return   */  public boolean lSet(String key, Object value, long time) {    try {      redisTemplate.opsForList().rightPush(key, value);      if (time > 0)      expire(key, time);      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 将list放入缓存   * @param key 键   * @param value 值   * @return   */  public boolean lSet(String key, List value) {    try {      redisTemplate.opsForList().rightPushAll(key, value);      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }  /**   * 将list放入缓存   *   * @param key 键   * @param value 值   * @param time 时间(秒)   * @return   */  public boolean lSet(String key, List value, long time) {    try {      redisTemplate.opsForList().rightPushAll(key, value);      if (time > 0)      expire(key, time);      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }    /**     * 根据索引修改list中的某条数据     * @param key 键     * @param index 索引     * @param value 值     * @return     */  public boolean lUpdateIndex(String key, long index, Object value) {    try {      redisTemplate.opsForList().set(key, index, value);      return true;    } catch (Exception e) {      e.printStackTrace();      return false;    }  }    /**     * 移除N个值为value     * @param key 键     * @param count 移除多少个     * @param value 值     * @return 移除的个数     */  public long lRemove(String key, long count, Object value) {    try {      Long remove = redisTemplate.opsForList().remove(key, count, value);      return remove;    } catch (Exception e) {      e.printStackTrace();      return 0;    }  }}

    二.SpringBoot集成Redis缓存

    1.pom.xml加入依赖

        org.springframework.boot    spring-boot-starter-data-redis    org.springframework.boot     spring-boot-starter-cache

    2.application.properties

    server:  port: 8081#数据库连接spring:  datasource:    url: jdbc:mysql://localhost:3306/mytest_springboot_cache?useUnicode=true    driver-class-name: com.mysql.jdbc.Driver    username: root    password: lzh  ## Redis 配置  redis:    ## Redis数据库索引(默认为0)    database: 0    ## Redis服务器地址    host: 192.168.126.129    ## Redis服务器连接端口    port: 6379    ## Redis服务器连接密码(默认为空)    password:    jedis:      pool:        ## 连接池最大连接数(使用负值表示没有限制)        #spring.redis.pool.max-active=8        max-active: 8        ## 连接池最大阻塞等待时间(使用负值表示没有限制)        #spring.redis.pool.max-wait=-1        max-wait: -1        ## 连接池中的最大空闲连接        #spring.redis.pool.max-idle=8        max-idle: 8        ## 连接池中的最小空闲连接        #spring.redis.pool.min-idle=0        min-idle: 0    ## 连接超时时间(毫秒)    timeout: 1200  #将themilef的默认缓存禁用,热加载生效  thymeleaf:    cache: false  #mybatis的下划线转驼峰配置  configuration:    map-underscore-to-camel-case: true    #另外一种打印语句的方式    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl#打印sql时的语句logging:  level:    com:      acong:        dao: debug  file: d:/logs/bsbdj.log

    3.配置类

    package com.example.demo2.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;import com.fasterxml.jackson.annotation.PropertyAccessor;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.cache.CacheManager;import org.springframework.cache.annotation.CachingConfigurerSupport;import org.springframework.cache.annotation.EnableCaching;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.cache.RedisCacheConfiguration;import org.springframework.data.redis.cache.RedisCacheManager;import org.springframework.data.redis.cache.RedisCacheWriter;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;import java.time.Duration;@Configuration@EnableCachingpublic class RedisConfig extends CachingConfigurerSupport {    //缓存管理器,确定使用何种缓冲    @Bean    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()                .entryTtl(Duration.ofHours(1)); // 设置缓存有效期一小时        return RedisCacheManager                .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))                .cacheDefaults(redisCacheConfiguration).build();    }    @Bean    public RedisTemplate redisTemplate(RedisConnectionFactory factory){        StringRedisTemplate template = new StringRedisTemplate(factory);        setSerializer(template);//设置序列化工具        template.afterPropertiesSet();        return template;    }    private void setSerializer(StringRedisTemplate template){        @SuppressWarnings({ "rawtypes", "unchecked" })        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);        template.setValueSerializer(jackson2JsonRedisSerializer);    }}

    说明:

    • @EnableCaching:注解是spring framework中的注解驱动的缓存管理功能。自spring版本3.1起加入了该注解。如果你使用了这个注解,那么你就不需要在XML文件中配置cache manager了,表示缓冲功能开启,可以用在启动类或配置类上。

    • @Cacheable:在方法执行前Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;没有则调用方法并将方法返回值放进缓存。用在方法上。

    • @CacheEvict:调用方法后从缓存中删除对应key的数据。用在方法上。

    • @Caching:当一个方法需要查询多个缓存或者删除多个缓存时使用。

    • @CacheConfig:抽取类中的所有@CachePut@Cacheable@CacheEvict的公共配置,配置一些共有的缓存配置。

    e7d7f2da11dc9998b2479b73f4d51d08.png

    4.Mapper持久层Dao,这里主要用注解写比较方便,也可以使用mybatis的xml配置文件写sql语句。

    @Mapperpublic interface ProductMapper {    @Select("select * from tb_product where product_id=#{id}")    Product getProductById(Long id);    @Update("update tb_product set product_name=#{productName},product_desc=#{productDesc} WHERE product_id=#{productId}")    int updateProduct(Product product);    @Delete("delete from tb_product where product_id=#{id}")    void deleteProductById(Long id);    @Select("select * from tb_product where product_name=#{productName}")    Product getProductByName(String productName);}

    5.Service

    package com.sl.cache.service;import com.sl.cache.entity.Product;import com.sl.cache.mapper.ProductMapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.annotation.CacheConfig;import org.springframework.cache.annotation.CacheEvict;import org.springframework.cache.annotation.CachePut;import org.springframework.cache.annotation.Cacheable;import org.springframework.cache.annotation.Caching;import org.springframework.stereotype.Service;@Service@CacheConfig(cacheNames = "product")public class ProductService {      @Autowired    private ProductMapper productMapper;    @Cacheable(/*cacheNames = "product",*/key = "#root.methodName+'['+#id+']'")    //@Cacheable(cacheNames = {"product","product2"})// 默认key为参数,多个参数SimpleKey [arg1,arg2]    //@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'")    //@Cacheable(cacheNames = "product",keyGenerator = "myKeyGenerator")    //@Cacheable(cacheNames = "product",key = "#root.methodName+'['+#id+']'",condition="#a0>10",unless = "#a0==11") //或者condition="#id>10")    public Product getProductById(Long id){       Product product =productMapper.getProductById(id);       System.out.println(product);       return product;    }    @CachePut(/*value="product",*/key = "#result.productId",condition = "#result!=null")    public  Product updateProduct(Product product){        int count = productMapper.updateProduct(product);        System.out.println("影响行数:"+count);        if(count>0){            return product;        }else{            return null;        }    }    //@CacheEvict(value="product",key="#id")    //@CacheEvict(value="product",allEntries = true) //清楚所有缓存    @CacheEvict(/*value="product",*/allEntries = true,beforeInvocation = true) //清楚所有缓存    public boolean deleteProductById(Long id) {        productMapper.deleteProductById(id);        return true;    }    //含有CachePut注解,所以执行这个方法时一定会查询数据库,即使有cacheable注解    @Caching(            cacheable = {@Cacheable(/*value="product",*/key="#productName")},            put = {                @CachePut(/*value="product",*/key="#result.productId"),                @CachePut(/*value="product",*/key="#result.productName")            }    )    public  Product getProductByName(String productName){        Product product =productMapper.getProductByName(productName);         return product;    }}

    6.Controller

    package com.sl.cache.controller;import com.sl.cache.entity.Product;import com.sl.cache.service.ProductService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.stereotype.Service;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class ProductController {    @Autowired    private ProductService productService;    @GetMapping("/product/{id}")    public Product getProduct(@PathVariable("id") Long id) {        Product product = productService.getProductById(id);        return product;    }    //prooduct?productid=1&productName= &    @GetMapping("/product")    public Product updateProduct(Product product) {        productService.updateProduct(product);        return product;    }    @GetMapping("/delproduct")    public String delProduct(@RequestParam(value="id") Long id) {        productService.deleteProductById(id);        return "ok";    }    @GetMapping("/product/name/{productName}")    public Product getEmpByLastName(@PathVariable("productName") String productName){        return productService.getProductByName(productName);    }}

    7.启动类

    @MapperScan("com.sl.cache.mapper")@SpringBootApplication//@EnableCachingpublic class SpringbootCacheApplication {    public static void main(String[] args) {        SpringApplication.run(SpringbootCacheApplication.class, args);    }}

    总结:以上介绍了两种使用Redis的方式,第一种是直接将数据存到Redis数据库中,第二种是基于数据库使用Redis缓冲!!!

    展开全文
  • 08-19 19:06:10.015 ERROR 9716 --- [pool-2-thread-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled taskorg.springframework.dao.QueryTimeoutException: Redis ...

    错误信息

    2020-08-19 19:06:10.015 ERROR 9716 --- [pool-2-thread-1] o.s.s.s.TaskUtils$LoggingErrorHandler : Unexpected error occurred in scheduled task

    org.springframework.dao.QueryTimeoutException: Redis command timed out; nested exception is io.lettuce.core.RedisCommandTimeoutException: Command timed out after 10 second(s)

    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:70)

    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)

    at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)

    at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)

    at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:270)

    at org.springframework.data.redis.connection.lettuce.LettuceSetCommands.convertLettuceAccessException(LettuceSetCommands.java:520)

    at org.springframework.data.redis.connection.lettuce.LettuceSetCommands.sMembers(LettuceSetCommands.java:245)

    at org.springframework.data.redis.connection.DefaultedRedisConnection.sMembers(DefaultedRedisConnection.java:729)

    at org.springframework.data.redis.core.DefaultSetOperations.lambda$members$10(DefaultSetOperations.java:214)

    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:228)

    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:188)

    at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:96)

    at org.springframework.data.redis.core.DefaultSetOperations.members(DefaultSetOperations.java:214)

    at org.springframework.data.redis.core.DefaultBoundSetOperations.members(DefaultBoundSetOperations.java:152)

    at org.springframework.session.data.redis.RedisSessionExpirationPolicy.cleanExpiredSessions(RedisSessionExpirationPolicy.java:129)

    at org.springframework.session.data.redis.RedisIndexedSessionRepository.cleanupExpiredSessions(RedisIndexedSessionRepository.java:407)

    at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)

    at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:93)

    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)

    at java.util.concurrent.FutureTask.run(FutureTask.java:266)

    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)

    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)

    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)

    at java.lang.Thread.run(Thread.java:748)

    Caused by: io.lettuce.core.RedisCommandTimeoutException: Command timed out after 10 second(s)

    at io.lettuce.core.ExceptionFactory.createTimeoutException(ExceptionFactory.java:51)

    at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:114)

    at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:69)

    at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)

    at com.sun.proxy.$Proxy115.smembers(Unknown Source)

    at org.springframework.data.redis.connection.lettuce.LettuceSetCommands.sMembers(LettuceSetCommands.java:243)

    ... 18 common frames omitted

    解决方法

    1)换掉默认的Lettuce客户端,使用jedis客户端

    redis.clients

    jedis

    org.springframework.data

    spring-data-redis

    展开全文
  • 服务的资源使用率较高,能够精确的实现超时任务的执行。 2. 减少 DB 的查询次数,能够降低数据库的压力 缺点: 1. 对于延迟队列来说本身设计比较复杂,目前没有通用的比较好过的方案。 基于 Redis 的延迟队列实现 ...

    延迟队列,顾名思义它是一种带有延迟功能的消息队列。那么,是在什么场景下我才需要这样的队列呢?1. 背景
    我们先看看以下业务场景:
    1.当订单一直处于未支付状态时,如何及时的关闭订单
    2.如何定期检查处于退款状态的订单是否已经退款成功
    3.在订单长时间没有收到下游系统的状态通知的时候,如何实现阶梯式的同步订单状态的策略
    4.在系统通知上游系统支付成功终态时,上游系统返回通知失败,如何进行异5.步通知实行分频率发送:15s 3m 10m 30m 30m 1h 2h 6h 15h

    6.对于红包场景,账户 A 对账户 B 发出红包通常在 1 天后会自动归还到原账户。

    7.对于实时支付场景,如果账户 A 对商户 S 付款 100 元,5秒后没有收到支付方回调将自动取消订单。

    2. 设计目标
    实时性:允许存在一定时间的秒级误差
    高可用性:支持单机、支持集群
    支持消息删除:业务会随时删除指定消息
    消息可靠性:保证至少被消费一次
    消息持久化:基于Redis自身的持久化特性,如果Redis数据丢失,意味着延迟消息的丢失,不过可以做主备和集群保证。这个可以考虑后续优化将消息持久化到MangoDB中

    方案一:

    采用通过定时任务采用数据库/非关系型数据库轮询方案。

    优点:

    1. 实现简单,对于项目前期这样是最容易的解决方案。

    缺点:

    1. DB 有效使用率低,需要将一部分的数据库的QPS分配给 JOB 的无效轮询。

    2. 服务资源浪费,因为轮询需要对所有的数据做一次 SCAN 扫描 JOB 服务的资源开销很大。

    方案二:

    采用延迟队列:

    优点:

    1.  服务的资源使用率较高,能够精确的实现超时任务的执行。

    2. 减少 DB 的查询次数,能够降低数据库的压力

    缺点:

    1. 对于延迟队列来说本身设计比较复杂,目前没有通用的比较好过的方案。

    基于 Redis 的延迟队列实现

    基于以上的分析,我决定通过 Redis 来实现分布式队列。

    设计思路:

     9c8b22da4699282107fe8e336645c9b8.png

    1.  第一步将需要发放的消息发送到延迟队列中。

    2. 延迟队列将数据存入 Redis 的 ZSet 有序集合中 score 为当前时间戳,member 存入需要发送的数据。

    3. 添加一个 schedule 来进行对 Redis 有序队列的轮询。

    4. 如果到达达到消息的执行时间,那么就进行业务的执行。

    5. 如果没有达到消息的执行是将,那么消息等待下轮执行。

    实现步骤:

    由于本处篇幅有限,所以只列举部分代码,完整的代码可以在本文最后访问 GitHub 获取。由于本人阅历/水平有限,如有建议/或更正欢迎留言或提问。先在此谢谢大家驻足阅读 ? ? ?。

    需要注意的问题:

    单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。

    事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

    我们可以通过 Redis 的 eval 命令来执行 lua 脚本来保证原子性实现Redis的事务。

    实现步骤如下:

    1. 延迟队列接口

    /** * 延迟队列 * * @author zhengsh * @date 2020-03-27 */public interface RedisDelayQueueextends DelayMessage> {    String META_TOPIC_WAIT = "delay:meta:topic:wait";    String META_TOPIC_ACTIVE = "delay:meta:topic:active";    String TOPIC_ACTIVE = "delay:active:9999";    /**     * 拉取消息     */    void poll();    /**     * 推送延迟消息     *     * @param e     */    void push(E e);}

    2. 延迟队列消息

    /** * 消息体 * * @author zhengsh * @date 2020-03-27 */@Setter@Getterpublic class DelayMessage {    /**     * 消息唯一标识     */    private String id;    /**     * 消息主题     */    private String topic = "default";    /**     * 具体消息 json     */    private String body;    /**     * 延时时间, 格式为时间戳: 当前时间戳 + 实际延迟毫秒数     */    private Long delayTime = System.currentTimeMillis() + 30000L;    /**     * 消息发送时间     */    private LocalDateTime createTime;}
    3. 延迟队列实现/** * 延迟队列实现 * * @author cc * @date 2020-03-27 */@Componentpublic class RedisDelayQueueImplextends DelayMessage>     private Logger logger = LoggerFactory.getLogger(getClass());    @Autowired    private StringRedisTemplate redisTemplate;    @Override    public void poll() {        // todo    }    /**     * 发送消息     *     * @param e     */    @SneakyThrows    @Override    public void push(E e) {        try {            String jsonStr = JSON.toJSONString(e);            String topic = e.getTopic();            String zkey = String.format("delay:wait:%s", topic);            String u =                    "redis.call('sadd', KEYS[1], ARGV[1])\n" +                            "redis.call('zadd', KEYS[2], ARGV[2], ARGV[3])\n" +                            "return 1";            Object[] keys = new Object[]{serialize(META_TOPIC_WAIT), serialize(zkey)};            Object[] values = new Object[]{ serialize(zkey), serialize(String.valueOf(e.getDelayTime())),serialize(jsonStr)};            Long result = redisTemplate.execute((RedisCallback) connection -> {                Object nativeConnection = connection.getNativeConnection();                if (nativeConnection instanceof RedisAsyncCommands) {                    RedisAsyncCommands commands = (RedisAsyncCommands) nativeConnection;                    return (Long) commands.getStatefulConnection().sync().eval(u, ScriptOutputType.INTEGER, keys, values);                } else if (nativeConnection instanceof RedisAdvancedClusterAsyncCommands) {                    RedisAdvancedClusterAsyncCommands commands = (RedisAdvancedClusterAsyncCommands) nativeConnection;                    return (Long) commands.getStatefulConnection().sync().eval(u, ScriptOutputType.INTEGER, keys, values);                }                return 0L;            });            logger.info("延迟队列[1],消息推送成功进入等待队列({}), topic: {}", result != null && result > 0, e.getTopic());        } catch (Throwable t) {            t.printStackTrace();        }    }    private byte[] serialize(String key) {        RedisSerializer<String> stringRedisSerializer =                (RedisSerializer<String>) redisTemplate.getKeySerializer();        //lettuce连接包下序列化键值,否则无法用默认的ByteArrayCodec解析        return stringRedisSerializer.serialize(key);    }}

    4. 定时任务

    /** * 分发任务 */@Componentpublic class DistributeTask {    private static final String LUA_SCRIPT;    private Logger logger = LoggerFactory.getLogger(getClass());    @Autowired    private StringRedisTemplate redisTemplate;    static {        StringBuilder sb = new StringBuilder(128);        sb.append("local val = redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1], 'limit', 0, 1)\n");        sb.append("if(next(val) ~= nil) then\n");        sb.append("    redis.call('sadd', KEYS[2], ARGV[2])\n");        sb.append("    redis.call('zremrangebyrank', KEYS[1], 0, #val - 1)\n");        sb.append("    for i = 1, #val, 100 do\n");        sb.append("        redis.call('rpush', KEYS[3], unpack(val, i, math.min(i+99, #val)))\n");        sb.append("    end\n");        sb.append("    return 1\n");        sb.append("end\n");        sb.append("return 0");        LUA_SCRIPT = sb.toString();    }    /**     * 2秒钟扫描一次执行队列     */    @Scheduled(cron = "0/5 * * * * ?")    public void scheduledTaskByCorn() {        try {            Set<String> members = redisTemplate.opsForSet().members(META_TOPIC_WAIT);            assert members != null;            for (String k : members) {                if (!redisTemplate.hasKey(k)) {                    // 如果 KEY 不存在元数据中删除                    redisTemplate.opsForSet().remove(META_TOPIC_WAIT, k);                    continue;                }                String lk = k.replace("delay:wait", "delay:active");                Object[] keys = new Object[]{serialize(k), serialize(META_TOPIC_ACTIVE), serialize(lk)};                Object[] values = new Object[]{serialize(String.valueOf(System.currentTimeMillis())), serialize(lk)};                Long result = redisTemplate.execute((RedisCallback) connection -> {                    Object nativeConnection = connection.getNativeConnection();                    if (nativeConnection instanceof RedisAsyncCommands) {                        RedisAsyncCommands commands = (RedisAsyncCommands) nativeConnection;                        return (Long) commands.getStatefulConnection().sync().eval(LUA_SCRIPT, ScriptOutputType.INTEGER, keys, values);                    } else if (nativeConnection instanceof RedisAdvancedClusterAsyncCommands) {                        RedisAdvancedClusterAsyncCommands commands = (RedisAdvancedClusterAsyncCommands) nativeConnection;                        return (Long) commands.getStatefulConnection().sync().eval(LUA_SCRIPT, ScriptOutputType.INTEGER, keys, values);                    }                    return 0L;                });                logger.info("延迟队列[2],消息到期进入执行队列({}): {}", result != null && result > 0, TOPIC_ACTIVE);            }        } catch (Throwable t) {            t.printStackTrace();        }    }    private byte[] serialize(String key) {        RedisSerializer<String> stringRedisSerializer =                (RedisSerializer<String>) redisTemplate.getKeySerializer();        //lettuce连接包下序列化键值,否则无法用默认的ByteArrayCodec解析        return stringRedisSerializer.serialize(key);    }}
    展开全文
  • 特点与Jedis相比,Lettuce 则完全克服了其 线程不安全的缺点:Lettuce是一个可伸缩的线程安全的Redis 客户端,支持同步、异步和响应式模式。多个线程可以共享一个连接实例,而不必担心多线程 并发问题。同步调用:...
  • SpringBoot 中除了了对常用的关系型数据库提供了优秀的自动化测试以外,对于很多 NoSQL 数据库一样提供了自动化配置的支持,包括:Redis, MongoDB, Elasticsearch, Solr 和 Cassandra。Java架构免费学习资料​docs....
  • 前言最近项目上用到redis,主要就是在SpringBoot项目中集成Redis,在做拓展开发。关于SpringBoot集成Redis开发,实际上比较简单,网上也有很多的教程,这里还是自己总结一下实践过程。工欲善其事必先利其器,不管是...
  • 原先配置是使用默认Lettuce。后来在生产环境发现会经常的超时,导致缓存不可用,而且一超时就不恢复。 后来查了些资料,网上都有这个问题,无解,都建议使用jedis。 先看下原来的配置,maven配置(项目其他配置...
  • 四 Java连接RedisJedis连接RedisLettuce连接Redis4.1 Jedis连接Redis1、创建maven项目2、导入需要的依赖包https://mvnrepository.com/<dependencies> <dependency> <groupId>redis....
  • Redis是什么Redis 是开源免费高性能的key-value数据库。性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets ...
  • linux redis搭建好了,可是我用java去连,就报错:Command timed outio.lettuce.core.RedisCommandTimeoutException:Commandtimedoutatio.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:114)~...
  • 一、pom.xm文件引入对应的包 org.springframework.boot spring-boot-starter-data-redis 二、redis配置常用client有两种:Jedis和Lettuce,spring boot框架中在1.x.x的版本时默认使用的jedis,2.x.x版本默认使用的...
  • lettuce客户端Lettuce 和 Jedis 的都是连接Redis Server的客户端程序。Jedis在实现上是直连redis server,多线程环境下非线程安全(即多个线程对一个连接实例操作,是线程不安全的),除非使用连接池,为每个Jedis...
  • 点击上方☝SpringForAll社区... 1、是什么缓存. 2、为什么使用缓存. 3、缓存的优缺点. 二、Redis 概念知识. 1、什么是 Redis. 2、为什么使用 Redis 作为缓存. 3、Redis 支持的数据类型. 三、缓存后可能遇见的问题....
  • 在使用Springcloud整合Redis作为缓存时,一段时间不去操作,再次刷新会出现连接超时的问题,具体如下: io.lettuce.core.RedisCommandTimeoutException: Command timed out after 5 second(s) 二、原因 springboot 2...
  • 问题描述 最近线上的程序经常报错,redis command timed out 报错信息如下 org.springframework.dao.QueryTimeoutException: ...nested exception is io.lettuce.core.RedisCommandTimeoutException: Command ti...
  • Redis 简介什么是 RedisRedis 是目前使用的非常广泛的免费开源内存数据库,是一个高性能的 key-value 数据库。Redis 与其他 key-value 缓存(如 Memcached )相比有以下三个特点:1.Redis 支持数据的持久化,它可以将...
  • SpringBoot2.x系列教程50--NoSQL之SpringBoot整合Redis作者:一一哥一. Spring Boot整合Redis实现1. Redis简介Redis是一个缓存,消息中间件及具有丰富特性的键值存储系统。Spring Boot为Redis的客户端Jedis提供了...
  • 频繁FullGC导致Lettuce客户端访问redis时出现命令超时
  • 最近在项目中遇到这个问题,启动项目没有问题,但是用着用着就出现redis连接超时,而且这个问题在使用了Kubernetes后,显得尤为突出。估计很多人也遇到了,排查思路如下: 1.检查配置文件链接配置,修改配置 2....
  • springboot lettuce 超时

    2019-07-26 17:37:47
    nested exception is io.lettuce.core.RedisCommandTimeoutException: io.lettuce.core.RedisCommandTimeoutException: Command timed out after 6 second(s) 2、配置 ``` <groupId>org.springframework....
  • 今天临近下班了,线上开始频繁报警,各种Redis连接超时,顿时慌的一批,因为最近在优化系统高频查询时用到了Redis作为缓存,难道要出生产事故,额~~~ 一首凉凉送给自己。。。。。。 于是马上联系下运维看下什么...
  • 背景从 SpringBoot 2.X 版本开始,Lettuce 作为 Redis Java 客户端程序集成到 Spring Data,在 Jedis 横行的年代,Lettuce 能够进入 Spring Boot 的阵营,这引起了庖丁的好奇,在对 Lettuce 认真了解后,发现这个 ...
  • 而目前比较常见的方案是通过Redis来实现分布式锁,网上关于分布式锁的实现方式有很多,早期主要是基于Redisson等客户端,但在Spring Boot2.x以上版本中使用Redis时,其客户端库已经默认使用lettuce。所以本文将直接...
  • 使用阻塞操作后,redis连接超时

    千次阅读 2019-08-06 12:50:21
    项目是springboot,在之前使用redis时一切正常(配置连接超时为1500ms),后来为了实现分布式队列,增加了redis list的brpop操作,阻塞时间为30s,就报了这个错 Caused by: io.lettuce.core....
  • 在学习纯洁的微笑的spring-boot整合redis的时候发现总是报redis连接超时 原文连接:https://www.cnblogs.com/ityouknow/p/5748830.html 最后发现是在application.properites的redis配置中的spring.redis.timeout中...

空空如也

空空如也

1 2 3 4
收藏数 78
精华内容 31
关键字:

lettuceredis超时

redis 订阅