Redis整合springboot笔记

redis整合springboot学习笔记

pom引入依赖

需要同时引入spring-boot-starter-data-redis和commons-pool2这2个依赖;
spring-boot-starter-data-redis是官方封装的redis操作依赖,
commons-pool2是redis需要的连接池,不引入这个会导致启动报错.

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

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.5.0</version>
        </dependency>

配置springboot项目内的application.yml或application.properties

spring:
  redis:
    # ip
    host: localhost
    # 端口6379
    port: 6379
    #密码,没有密码则不配置这一项
    password:
    #指定使用redis 16个库中的哪一个,不配置的话,默认配置为0
    database: 0
    lettuce:
      pool:
        min-idle: 0   #连接池最新空闲时间
        max-wait: -1ms  #最大等待时间
        max-active: 8   #最大活跃时间
        max-idle: 8    #最大空闲时间
      shutdown-timeout: 100ms  #连接池关闭超时时间
    timeout: 1000ms  #redis连接超时时间

使用spring-boot-starter-data-redis内置的StringRedisTemplate

要在需要使用redis的地方注入StringRedisTemplate,如

@Slf4j
@Component
@Validated
public class RedisTool {
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    private ObjectMapper objectMapper;
     @Autowired
    public void setObjectMapper(ObjectMapper objectMapper) {
        this.objectMapper = objectMapper;
    }

}    

StringRedisTemplate 是springDataRedis提供的一个模板类,可以让我们更方便的操作key值类型为字符串的Redis Key 。

请添加图片描述

字符串类型是Redis最基础的数据结构,字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字(整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。

请添加图片描述

请添加图片描述
请添加图片描述

请添加图片描述

StringRedisTemplate 内提供的常用的方法如下。

判断key是否存在

 public boolean hasKey(@NotNull String strK) {
        Boolean hasKey = stringRedisTemplate.hasKey(strK);
        if (hasKey != null && hasKey) {
            return true;
        } else {
            log.warn("不存在redis key:{}",strK);
            return false;
        }
    }

删除key

//删除1个key
public void deleteOneKeyIfExist(@NotNull String key) {
        if (this.hasKey(key)) {
            Boolean delete = stringRedisTemplate.delete(key);
            log.info("删除了key:{},删除结果:{}",key, delete);
        }
    }

//删除多个key
public void deleteBatchKeyIfExist(Set<String> keySet) {
        for (String k : keySet) {
            this.deleteOneKeyIfExist(k);
        }
    }    

添加key

//添加固定的key
public void setKV(@NotNull String strK, String plainStrV) {
        stringRedisTemplate.opsForValue().set(strK, plainStrV);
    }
//添加带超时时间的key,超时时间到期后key会自动删除
    public void setKVTimeoutSeconds(@NotNull String strK, String plainStrV, long seconds) {
        stringRedisTemplate.opsForValue().set(strK, plainStrV, seconds, TimeUnit.SECONDS);
    }
    
    //序列化后 进行set key
    public boolean setKeyAndObjValue(@NotNull String strK, @NotNull Object objV) {
        try {
            String s = objectMapper.writeValueAsString(objV);
            log.warn("jackson序列化:{}", s);
            this.setKV(strK, s);
            return true;
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            log.error("jackson序列化失败", e);
            return false;
        }
    }


    public boolean setKeyAndObjValueByTimeoutSeconds(@NotNull String strK, @NotNull Object objV, long seconds) {
        try {
            String s = objectMapper.writeValueAsString(objV);
            log.warn("jackson序列化:{}", s);
            this.setKVTimeoutSeconds(strK, s, seconds);
            return true;
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            log.error("jackson序列化失败", e);
            return false;
        }
    }

查询key


//获取key的字符串值
public String getPlainStrV(@NotNull String srtKey) {
        if (!this.hasKey(srtKey)) {
            return null;
        }
        return stringRedisTemplate.opsForValue().get(srtKey);
    }

//获取key的对象值
    public <T> T getJavaObjByJsonCache(@NotNull String srtKey, Class<T> valueType) {
        if (!this.hasKey(srtKey)) {
            return null;
        }
        String str = stringRedisTemplate.opsForValue().get(srtKey);
        if (StrUtil.isBlank(str)) {
            return null;
        }
        try {
            log.warn("反序列化json str:{} 为Java obj type:{}", str,valueType);
            return objectMapper.readValue(str, valueType);
        } catch (IOException e) {
            e.printStackTrace();
            log.error("json str 反序列化失败", e);
            return null;
        }
    }

随机获取一个key

//随机取一个 redis key名
    public String getRandomKey(){
        return stringRedisTemplate.randomKey();
    }

加1/减1/替换-操作

stringRedisTemplate.opsForValue().set("wfew", "233");
//+1和减1操作 (key不存在时,创建这个key,并设置为指定的值;key存在时,直接设置key值加1或减1)
 //+1操作 
 stringRedisTemplate.boundValueOps("twfew").increment(1);  //key不存在,存入一个key值1
  //-1操作 
 stringRedisTemplate.boundValueOps("wfew").decrement(1); //值变为232
 //替换操作 设置新值,同时返回旧值 (key不存在依然可以操作)
  String oldVal = stringRedisTemplate.boundValueOps("kwfew").getAndSet("500");       System.out.println(oldVal);//不存在旧值,返回null

设置几秒过期

//设置几秒过期
        stringRedisTemplate.opsForValue().set("gb23f", "gwr23", 100, TimeUnit.SECONDS);

查询当前key还剩几秒过期

Long gb23f = stringRedisTemplate.getExpire("gb23f", TimeUnit.SECONDS);
System.out.println(gb23f);

覆盖key值并设置过期时间

//已存在的固定key,会被设置新值同时设置过期时间
        stringRedisTemplate.boundValueOps("wfew").set("300", 60, TimeUnit.SECONDS);

append拼接key值

stringRedisTemplate.opsForValue().set("th718", "718");
 //append拼接key值
stringRedisTemplate.boundValueOps("th718").append("23");
  //打印71823
System.out.println(stringRedisTemplate.opsForValue().get("th718"));     

获取旧值,同时set新值

//获取旧值,同时set新值,若旧值不存在会返回null
String th718 = stringRedisTemplate.opsForValue().getAndSet("th718", "202307");
//会打印 71823
System.out.println(th718);

仅在键不存在时设置key

//setIfAbsent-仅在键不存在时设置key, 相当于redis中的setnx命令
        //  key存在则不变,key不存在则执行set;
        //  返回true表示key不存在,set了一个新key
        //  返回false表示key已存在,不进行set
        //适合场景:并发环境下的分布式锁--(多个进程/线程尝试设置同一个key,set成功则表示获取到锁,set失败表示没有获取到锁)
        //setIfAbsent是一个原子性操作,分布式环境下需要注意并发问题和过期时间的处理
Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent("th720", "kwerq2");
System.err.println(aBoolean);

批量获取key值

//批量获取key值
List<String> stringList = stringRedisTemplate.opsForValue().multiGet(
                ListUtil.toList("th718", "th719", "th720","th721"));
  //某个key不存在,则值获取为null
  //[202307, 202307170, kwerq, null]
System.err.println(stringList);

操作list结构

redis list 中的元素可以重复 ,且list元素是有序的.

redis list 的结构类似于队列, 提供了lpush左入队,rpop右出队 相关的方法, 但不建议使用redis的List作为队列使用.

redis列表的使用场景包括消息队列、发布/订阅、实时排行榜、历史记录、消息通知系统、简单队列等场景

push 向list中添加元素

  
        //right push 操作 可以创建key并向右顺序插入元素,或者在已有list的末尾追加元素
        stringRedisTemplate.opsForList().rightPush("lst1", "hjew");
        stringRedisTemplate.opsForList().rightPush("lst1", "bswgrg");
        stringRedisTemplate.opsForList().rightPush("lst1", "hjew");
        stringRedisTemplate.opsForList().rightPush("lst1", "kgwefqz");
        // left push 操作, 可以创建key并向左插入元素,或者在已有list的第一个元素之前追加元素
        stringRedisTemplate.opsForList().leftPush("lst1", "gtw1e");
        stringRedisTemplate.opsForList().leftPush("lst1", "nkqwe1");

pop 弹出list中的元素

stringRedisTemplate.opsForList().rightPush("lst2", "v1");
stringRedisTemplate.opsForList().rightPush("lst2", "v2");
stringRedisTemplate.opsForList().leftPush("lst2", "zisj");
     //从 lst2 列表 中最后一个元素开始,弹出1个元素 
    //[从右往左删除元素 (或删除列表中最后一个元素)]
stringRedisTemplate.opsForList().rightPop("lst2");

//从左往右删除元素 (或删除列表中第一个元素)
stringRedisTemplate.opsForList().leftPop("lst2");

仅在键不存在时向list添加元素

// 若 lst3 这个key存在,则 向左插入1个元素 (lst3不存在则不会执行)
stringRedisTemplate.opsForList().leftPushIfPresent("lst3", "nkqwe1");
//创建lst3并向右 插入1个元素
stringRedisTemplate.opsForList().rightPush("lst3", "grt2r3");
// 若 lst3 这个key存在,则 向右 插入1个元素
stringRedisTemplate.opsForList().rightPushIfPresent("lst3", "hwerg");

根据索引来查询/更新/删除list中的元素

 // 获取lst1 列表中 索引为0的元素
System.err.println(stringRedisTemplate.opsForList().index("lst1", 0));
 // 对 lst1 列表中的 索引为 2的元素 ,设置为 e3;  (若lst1不存在,会报错 ERR no such key)
stringRedisTemplate.opsForList().set("lst1", 2, "e3");
// 仅当 列表 lst3 中 索引为1的元素 为  hwerg 时,才执行删除 这个元素
System.err.println(stringRedisTemplate.opsForList().remove("lst3", 1, "hwerg"));

对list里的元素进行范围查询

// 范围查询 ,获取 lst1 列表中 ,索引从1到4的元素
List<String> rangeList = stringRedisTemplate.opsForList().range("lst1", 1, 4);
System.err.println(rangeList);

操作set结构

redis 的Set是一个无序的、唯一的数据集合。

无序性:集合中的元素没有固定顺序,每次获取的元素顺序可能不同
唯一性:集合中的元素是唯一的,不允许重复
添加元素:sadd 命令
删除元素: srem命令
查找元素: sismember命令
获取元素的数量: scard命令
获取所有元素: smembers命令
set集合间的操作: sinter求交集 \sunion 求并集\ sdiff 求差集

set集合的实际使用场景有:

去重:把java集合放入redis set,实现去重
共同好友/共同关注 : 2个集合求交集可以实现
标签或分类:用set集合存在对象的标签或分类,方便快速查找
排他性操作:通过求差集,实现对范围外的用户进行排他性操作

在springDataRedis里提供了一下对redisSet的操作方法如下

@Test
    public void testSet() {
//创建一个无序集合,并写入数据 (set中的元素不能重复, 若values里的元素有重复的,写入时会自动去重)
stringRedisTemplate.opsForSet().add("sk1", "reg23r", "r23rf", "kuy54rt", "hn5t4t","reg23r");
        //判断set中是否存在某个元素
        System.out.println(
                stringRedisTemplate.opsForSet().isMember("sk1", "reg23r"));
        //根据key获取set中的元素
        System.out.println(
                stringRedisTemplate.opsForSet().members("sk1"));

        //从1个set中随机取3个元素
        System.out.println(stringRedisTemplate.opsForSet().distinctRandomMembers("sk1", 3));
        //从1个set中随机取1个元素
        System.out.println(stringRedisTemplate.opsForSet().randomMember("sk1"));

        //从1个set中删除 某些元素
        stringRedisTemplate.opsForSet().remove("sk1", "ga34rerw", "greg23");


        stringRedisTemplate.opsForSet().add("sk2", "plf23e", "r23rf");
        System.out.println(
                stringRedisTemplate.opsForSet().members("sk2"));
        // sk1 集合 减去 sk2 集合
        System.err.println(stringRedisTemplate.opsForSet().difference("sk1", "sk2"));
        // sk2 减 sk1
        System.err.println(stringRedisTemplate.opsForSet().difference("sk2", "sk1"));
        // 2个set集合相减,结果存到新的set中
      stringRedisTemplate.opsForSet().differenceAndStore("sk1", "sk2", "sk3");

        //两个set集合求交集
      System.err.println(stringRedisTemplate.opsForSet().intersect("sk1", "sk2"));
        //两个set集合求交集, 结果存到新的set中
      stringRedisTemplate.opsForSet().intersectAndStore("sk1", "sk2", "sk5");

        //2个set集合 求并集
      System.err.println(stringRedisTemplate.opsForSet().union("sk1", "sk2"));
        //sk1和sk2 求并集,然后结果存到sk8里
      stringRedisTemplate.opsForSet().unionAndStore("sk1", "sk2", "sk8");


        //移动1个元素 到1个新的set
        //把sk1里的元素hn5t4t,移动到sk6
      System.out.println(stringRedisTemplate.opsForSet().move("sk1", "hn5t4t", "sk6"));

        //获取1个set的元素数目
      System.err.println(stringRedisTemplate.opsForSet().size("sk1"));

    }

操作zset结构

zset是redis里的的有序集合,是一种允许存储多个元素并对每个元素设置一个score分数的数据结构

zset的特点:
排序性: zset 数据最终存储的顺序是按照score得分从低到高排序,如果score相同再按成员的字典顺序排序。
 唯一性: zset中的元素是唯一的,不允许重复
   添加元素和分数的命令: zadd
   删除元素的命令: zrem
    获取元素的分数: zscore
   统计zset元素数目: zcard
   根据分数范围和排名获取成员: zrangebyscore / zrank
  集合间的操作: zinterstore 求交集 /zunionstore 求并集
        
  zset的使用场景:
          排行榜:根据得分进行排名/获取排行
          范围查询: 根据得分进行范围查询
          任务优先级: 可以用得分来表示任务优先级 或 权重 ,进行按优先级/权重 的调度
          社交网络: 可以用zset存用户之间的关系和相关指标,如共同关注的人、好友推荐等

springDataRedis提供的常用的对zset的操作如下

@Test
    public void testZset() {        
        redisTool.deleteBatchKeyIfExist(Stream.of("zs1", "zs2", "zs3").collect(Collectors.toSet()));
        // 创建zs1 集合 , 同时写入 元素值 及其 得分;
        // (redis zset 数据最终存储的顺序是按照score得分从低到高排序)
        stringRedisTemplate.opsForZSet().add("zs1", "v01", 0.3);
        stringRedisTemplate.opsForZSet().add("zs1", "v03", 0.5);
        stringRedisTemplate.opsForZSet().add("zs1", "v07", 0.2);
        stringRedisTemplate.opsForZSet().add("zs1", "v09", 0.2);
        stringRedisTemplate.opsForZSet().add("zs1", "v12", 0.1);
        // 统计分数为0.1到0.3 的元素数目
        //这里打印结果是 4
        System.out.println(stringRedisTemplate.opsForZSet().count("zs1", 0.1, 0.3));

        // 获取 zset中 索引从 1到3 的集合
        // 由于zset 数据最终存储的顺序是按照score得分从低到高排序,
        // 所以 索引1-3的数据 是  [v07, v09, v01]
        Set<String> rangeSet = stringRedisTemplate.opsForZSet().range("zs1", 1, 3);
        System.out.println(rangeSet);

        //对索引1-3的数据 倒序排序
        Set<String> zs12 = stringRedisTemplate.opsForZSet().reverseRange("zs1", 1, 3);
        //[v01, v09, v07]
        System.err.println(zs12);


        //按score分数范围 查询元素集合
        //查询score在0.1到0.3 的元素集合
        Set<String> zs1 = stringRedisTemplate.opsForZSet().rangeByScore("zs1", 0.1, 0.3);
        //[v12, v07, v09, v01]
        System.out.println(zs1);

        //score在0.1到0.3 的元素 倒序排序
        Set<String> zs13 = stringRedisTemplate.opsForZSet().reverseRangeByScore("zs1", 0.1, 0.3);
        //[v01, v09, v07, v12]
        System.out.println(zs13);


        //查询某个元素的排行(即zet中某个元素的索引)
        Long zs11 = stringRedisTemplate.opsForZSet().rank("zs1", "v03");
        //v03的索引是4 ,这里会打印 4
        System.out.println(zs11);

        //查询某个元素排倒数第几 (即倒序索引)
        Long aLong = stringRedisTemplate.opsForZSet().reverseRank("zs1", "v03");
        System.out.println(aLong);

        //重新设置某元素的score得分
        //incrementScore 加分 (在原有基础上增加多少得分)
        Double aDouble = stringRedisTemplate.opsForZSet().incrementScore("zs1", "v09", 0.40);
        //0.2 +0.40 = 0.6
        //打印结果是 0.6
        System.out.println(aDouble);

        //再次打印zset 索引0-5的元素
        Set<String> zs14 = stringRedisTemplate.opsForZSet().range("zs1", 0, 5);
        //[v12, v07, v01, v03, v09]
        System.out.println(zs14);

}

操作hash结构

redis里的 hash用于存储 键值对的集合, 需要注意的是 hash 元素中的键和值 都是字符串类型
redis里的 hash 提供了类似于 字典或关联数组的功能,可以通过键 快速访问和修改对应的值

  redis 哈希的特点:
           存储结构: 类似于java里的map ,以键值对形式存储数据
             键的唯一性: hash中的 hashKey是唯一的,不允许重复
             添加键值对命令: hset
              获取值命令: hget
            删除键值对命令: hdel
              获取所有键值对: hgetall
             获取所有键和值: hkeys / hvals
             统计键值对数量: hlen

  redis hash结构的使用场景包括:
        对象存储:hash可以表示对象,其中对象的字段存储为hash的键值对
          用户属性存储: 可以将用户的属性存储在hash中,通过键来快速获取用户的相应属性
         数据库模型存储: 可以用hash存储关系数据库表的行记录,其中每行的字段对应hash的键值对

springDataRedis提供的常用的对hash的操作如下

@Test
    public void testhash() {       
        redisTool.deleteBatchKeyIfExist(Stream.of("htb1", "htb2", "htb3").collect(Collectors.toSet()));

        HashOperations<String, Object, Object> sooHashOperations = stringRedisTemplate.opsForHash();
        //put方法: 对某个redis key里添加键值对 ,键是hashKey ,值是value
        sooHashOperations.put("htb1", "hk001", "er21r12r");
        sooHashOperations.put("htb1", "hk002", "3rge");
        sooHashOperations.put("htb1", "hk003", "tgr");
        sooHashOperations.put("htb1", "hk004", "er21r12r");
        sooHashOperations.put("htb1", "hk005", "erg23");

        sooHashOperations.put("htb2", "hk201", "er21r12r");
        sooHashOperations.put("htb2", "hk202", "ret234t");
        sooHashOperations.put("htb2", "hk203", "gr34r");
        sooHashOperations.put("htb2", "hk204", "n818gr34r");

        //size方法: 统计hash中的键值对数量
        Long htb11 = sooHashOperations.size("htb1");
        //5
        System.out.println(htb11);

        Map<String, Object> htb3 = new HashMap<>();
        htb3.put("ewfeqf", "r32r23r");
        htb3.put("hethrt", "n1498h4");
        //putAll方法: 可以直接把java里的 HashMap 放入redis hash中缓存
        sooHashOperations.putAll("htb3", htb3);

        //hasKey 方法: 判断某个redis key里 是否存在键名为 hk001的元素
        //true
        System.err.println(sooHashOperations.hasKey("htb1", "hk001"));

        //entries方法: 查询redis key名为 htb1 的所有键值对
        Map<Object, Object> htb1 = sooHashOperations.entries("htb1");
        //{hk001=er21r12r, hk002=3rge, hk003=tgr, hk004=er21r12r, hk005=erg23}
        System.out.println(htb1);
        //false
        System.err.println(htb1.containsKey("hk203"));

        //multiGet方法: 同时查询多个hashKey
        List<Object> objects = sooHashOperations.multiGet("htb1", ListUtil.toList("hk001", "hk002"));
        //[er21r12r, 3rge]
        System.err.println(objects);


        //get方法: 获取某个redis key里的键名为 hk001的元素值
        Object o = sooHashOperations.get("htb1", "hk001");
        //er21r12r
        System.out.println(o);

        //delete方法: 删除某个redis key里的 某些键值对 , hashKeys表示可以传入多个键名
        //打印结果为2 ,表示成功删除了2个hashKey
        System.err.println(sooHashOperations.delete("htb2", "hk201", "hk202"));



        //keys方法: 获取redis key名为 htb2 的hash键值对里的 key键集合
        Set<Object> keysForhtb2 = sooHashOperations.keys("htb2");
        //[hk203, hk204]
        System.out.println(keysForhtb2);

        //values方法: 获取redis key名为 htb2 的hash键值对里的 value值集合
        List<Object> htb2 = sooHashOperations.values("htb2");
        //[gr34r, n818gr34r]
        System.out.println(htb2);

    }

操作geo数据结构

redis geo 是一种地理位置存储结构

redis 3.2版本开始有了geo这个数据结构,可以存储/查询地理位置信息

redis geo的常用命令:
  添加地理位置:
      geoadd  g1  93.21   26.32  shanghai;geoadd  g1  87.12   35.23  beijing;
  获取地理位置:
      geopos  g1  shanghai;
  计算距离:
      geodist g1 shanghai beijing km;
删除地理位置: zrem
获取某个坐标点 半径多少km的范围内的成员:
      georadius g1 88.12 30.13 1500 km;
获取某个标识点 半径多少km的范围内的成员:
      georadiusbymember g1 beijing 2000 km;

 redis geo的使用场景包括:
  附近的人:可以根据用户的地理位置信息,查询附近的其他用户
  地理位置搜索: 根据经纬度信息,搜索特定地点附近的商店、餐厅等
  路径规划: 根据地理位置信息,计算两个地点之间的距离和最短路径

springDataRedis提供的常用的对geo的操作如下

@Test
    public void testGeo() {

        redisTool.deleteBatchKeyIfExist(Stream.of("g1", "g2", "g3").collect(Collectors.toSet()));

        //添加地理位置
        //向key名为g1的geo集合里添加一个Point坐标经纬度 和对应的member地理标识名
        stringRedisTemplate.opsForGeo().add("g1", new Point(23.12, 31.23), "m1");
        stringRedisTemplate.opsForGeo().add("g1", new Point(93.21, 26.32), "shanghai");
        stringRedisTemplate.opsForGeo().add("g1", new Point(87.12, 35.23), "beijing");
        //获取多个地理位置
        List<Point> position = stringRedisTemplate.opsForGeo()
                .position("g1", "m1","shanghai");
        //[Point [x=23.120001, y=31.229999], Point [x=93.210001, y=26.320001]]
        System.out.println(position);

        //计算距离
        Distance distance = stringRedisTemplate.opsForGeo()
                .distance("g1", "shanghai", "beijing",
                RedisGeoCommands.DistanceUnit.KILOMETERS);
        if (distance!=null){
            //1148.6266km
            System.out.println(distance.getValue() + distance.getUnit());
        }

        //获取某个坐标点 半径多少km的范围内的成员
        GeoResults<RedisGeoCommands.GeoLocation<String>> g1radius = stringRedisTemplate.opsForGeo()
                .radius("g1",
                        new Circle(
                            new Point(88.12, 30.13),
                            new Distance(1500, RedisGeoCommands.DistanceUnit.KILOMETERS)
                        )
                );
        System.out.println(g1radius);



        //获取某个标识点 半径多少km的范围内的成员
        GeoResults<RedisGeoCommands.GeoLocation<String>> radius = stringRedisTemplate.opsForGeo()
                .radius("g1",
                "beijing",
                new Distance(2000, RedisGeoCommands.DistanceUnit.KILOMETERS));
        //GeoResults: [averageDistance: 0.0, results: GeoResult [content: RedisGeoCommands.GeoLocation(name=beijing, point=null), distance: 0.0, ],GeoResult [content: RedisGeoCommands.GeoLocation(name=shanghai, point=null), distance: 0.0, ]]
        System.out.println(radius);
        if (radius!=null && radius.getContent().size()>0){
            for (GeoResult<RedisGeoCommands.GeoLocation<String>> geoLocationGeoResult : radius.getContent()) {
                //beijing
                //shanghai
                System.out.println(geoLocationGeoResult.getContent().getName());
            }
        }


        //删除多个地理标识的位置
        Long count = stringRedisTemplate.opsForGeo().remove("g1", "m1","beijing");
        // 打印2 ,表示成功删除了2个地理标识的位置
        System.out.println(count);

    }

通过redis中的pub/sub实现消息发布和订阅

定义RedisPubSubCfg

package cn.test.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;


@Configuration
public class RedisPubSubCfg {

   private final RedisConnectionFactory redisConnectionFactory;
   @Autowired
   public RedisPubSubCfg(RedisConnectionFactory redisConnectionFactory) {
       this.redisConnectionFactory = redisConnectionFactory;
   }

   /**
    * 将消息监听器绑定到消息容器
    * @return RedisMessageListenerContainer
    */
   @Bean
   public RedisMessageListenerContainer messageListenerContainer(){
       RedisMessageListenerContainer container = new RedisMessageListenerContainer();
       container.setConnectionFactory(redisConnectionFactory);

       //------分开订阅监听,使用自定义的消息监听器SubscribeListener-------
       //按名称匹配
       container.addMessageListener(new CitySub(),new ChannelTopic("cityTopic"));
       //按模式匹配
       container.addMessageListener(new TestSub(),new PatternTopic("/aaa/*"));

       return container;
   }

}

自定义消息监听器

package cn.test.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;

@Slf4j
public class TestSub implements MessageListener {
  
    @Override
    public void onMessage(Message message, byte[] pattern) {

        String s = new String(pattern);
        log.info("redis  sub pattern: "+s);

        String channelReal = new String(message.getChannel());
        log.info("real sub pattern: "+channelReal);

        String body = new String(message.getBody());
        log.info("TestSub 订阅到的消息: "+body);

    }
}


package cn.test.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;

@Slf4j
public class CitySub implements MessageListener {
  
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String body = new String(message.getBody());

        String channel = new String(message.getChannel());
        System.err.println("Topic 名称: "+channel);
        String patternStr = new String(pattern);
        System.err.println("Topic 模式: "+patternStr);
        log.info("CitySub 订阅到的消息: {}",body);
    }
}

发布消息

springDataRedis中的stringRedisTemplate提供了convertAndSend方法可以发送消息。

@Test
    public void tstPublish() {
        String s = null;
        try {
            s = objectMapper.writeValueAsString(new CityInfo("aaa", 117.17, 31.52));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
//发送消息,topic为 cityTopic
        stringRedisTemplate.convertAndSend("cityTopic",
                s
        );
//发送消息,topic为 /aaa/123
        stringRedisTemplate.convertAndSend("/aaa/123",
                s
        );
    }
2023-07-19 19:32:42.003  INFO 1128 --- [erContainer-357] cn.test.redis.TestSub                    : redis  sub pattern: /aaa/*
2023-07-19 19:32:42.003  INFO 1128 --- [erContainer-357] cn.test.redis.TestSub                    : real sub pattern: /aaa/123
2023-07-19 19:32:42.003  INFO 1128 --- [erContainer-357] cn.test.redis.TestSub                    : TestSub 订阅到的消息: {"city":"hefei1689755562001","longitude":117.17,"latitude":31.52}

设置key过期监听

1.需要先配置 redis.conf ,开启key过期监听功能

notify-keyspace-events Ex

2.在springboot redis配置中,订阅key过期事件并发布pub/sub消息

@Configuration
public class RedisPubSubCfg {
    private final RedisConnectionFactory redisConnectionFactory;
    @Autowired
    public RedisPubSubCfg(RedisConnectionFactory redisConnectionFactory) {
        this.redisConnectionFactory = redisConnectionFactory;
    }
    /**
     * 将消息监听器绑定到消息容器
     * @return RedisMessageListenerContainer
     */
    @Bean
    public RedisMessageListenerContainer messageListenerContainer(){
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        //------分开订阅监听,使用自定义的消息监听器SubscribeListener-------
        //按名称匹配
        container.addMessageListener(new CitySub(),new ChannelTopic("cityTopic"));
        //按模式匹配
        container.addMessageListener(new TestSub(),new PatternTopic("/aaa/*"));


        //监听key过期事件,并发布一个消息
        //监听key过期事件 ,需要在redis.conf里开启配置 notify-keyspace-events Ex
        //然后需要在springDataRedis里订阅__keyevent@*__:expired 的模式消息
        container.addMessageListener(new ExpSub() ,new PatternTopic("__keyevent@*__:expired"));

        return container;
    }
}
package cn.test.redis.exp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
@Slf4j
public class ExpSub implements MessageListener {
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String channelReal = new String(message.getChannel());
        log.info("real sub pattern: "+channelReal);

        String body = new String(message.getBody());
        log.info("过期的redisKey名: "+body);
    }
}
2023-07-19 19:32:41.090  INFO 1128 --- [erContainer-356] cn.test.redis.exp.ExpSub                 : real sub pattern: __keyevent@0__:expired
2023-07-19 19:32:41.090  INFO 1128 --- [erContainer-356] cn.test.redis.exp.ExpSub                 : 过期的redis key名: ewr3w1689755556001

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/39292.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

17 | 从后端到前端:微服务后,前端如何设计?

微服务架构通常采用前后端分离的设计方式。作为企业级的中台&#xff0c;在完成单体应用拆分和微服务建设后&#xff0c;前端项目团队会同时面对多个中台微服务项目团队&#xff0c;这时候的前端人员就犹如维修电工一样了。 面对如此多的微服务暴露出来的 API 服务&#xff0c…

适用于 Type-C接口PD应用的智能二极管保护开关

日前&#xff0c;集设计、研发、生产和全球销售一体的著名功率半导体、芯片及数字电源产品供应商Alpha and Omega Semiconductor Limited&#xff08;AOS, 纳斯达克代码:AOSL) 推出一款采用理想二极管运作进行反向电流保护的新型Type-C PD 高压电源输入保护开关。AOZ13984DI-02…

数据库应用:MySQL数据库SQL高级语句与操作

目录 一、理论 1.克隆表与清空表 2.SQL高级语句 3.SQL函数 4.SQL高级操作 5.MySQL中6种常见的约束 二、实验 1.克隆表与清空表 2.SQL高级语句 3.SQL函数 4.SQL高级操作 5.主键表和外键表 三、总结 一、理论 1.克隆表与清空表 克隆表&#xff1a;将数据表的数据记录…

【技巧】Maven重复依赖分析查找

【技巧】Maven重复依赖分析查找 遇到奇葩的错误可以考虑是不是依赖冲突了 比如同一段代码 再这个项目中好好的 另一个项目中不能用等 idea安装插件 maven helper 打开pom文件 输入要查找的依赖 将不用的排除掉 右键排除即可

lua脚本语言学习笔记

Lua 是一种轻量小巧的脚本语言&#xff0c;用标准C语言编写并以源代码形式开放&#xff0c; 其设计目的是为了嵌入应用程序中&#xff0c;从而为应用程序提供灵活的扩展和定制功能。 因为我们使用redis的时候一般要写lua脚本&#xff0c;这篇文章就介绍一下lua脚本语言的基础用…

前端 mock 数据的几种方式

目录 接口demo Better-mock just mock koa webpack Charles 总结 具体需求开发前&#xff0c;后端往往只提供接口文档&#xff0c;对于前端&#xff0c;最简单的方式就是把想要的数据写死在代码里进行开发&#xff0c;但这样的坏处就是和后端联调前还需要再把写死的数据…

大小端模式

文章目录 一、概念二、举例三、判大小端和交换 一、概念 大端模式&#xff08;Big-endian&#xff09;&#xff0c;是一种数据存储方式&#xff0c;其中较高的字节&#xff08;最高有效字节&#xff09;存储在较低的内存地址&#xff0c;较低的字节&#xff08;最低有效字节&am…

开发跨平台APP,是用Flutter还是React Native开发框架?

随着移动互联网的飞速发展&#xff0c;对于开发人员而言&#xff0c;如何快速地开发出兼容不同平台&#xff08;iOS、Android&#xff09;的应用&#xff0c;成为了一个重要的问题。 跨平台应用程序开发框架的好处&#xff1a; 1. 一个App适用于多个设备&#xff1b; 2. 一个…

数据结构 ~ 树

什么是树 - tree 一种分层数据的抽象模型&#xff1b; 如&#xff1a;DOM、级联选择、树形控件&#xff0c;js 中没有树 可以用 Object 构建树&#xff1a; const tree {val: a,children: [{val: a-1,children: [{val: a-1-1,children: []}]},{val: a-2,children: [{val: a…

chatGPT指令大全可免费使用网站列表chatGPT4试用方案

指令列表 写作助理 &#x1f449; 最常使用的 prompt&#xff0c;用于优化文本的语法、清晰度和简洁度&#xff0c;提高可读性。作为一名中文写作改进助理&#xff0c;你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性&#xff0c;同时分解长句&#xff0c;减少…

剑指offer刷题笔记--Num51-60

1--数组中的逆序对&#xff08;51&#xff09; 主要思路&#xff1a; 基于归并排序&#xff0c;视频讲解参考&#xff1a;数组中的逆序对 #include <iostream> #include <vector>class Solution { public:int reversePairs(std::vector<int>& nums) {if(…

JavaWeb 前后端分离

AJax 1. 前端视图 ajax\src\main\webapp\ajax-register.html <html><head><meta charset"UTF-8"> </head><body><form class"form-horizontal" role"form"><div><tr><td>账号</td&…

6款好用的在线原型图设计工具推荐

在线原型图的核心功能是可视化需求&#xff0c;因此一个易于使用的在线原型图工具对原型图设计至关重要。对于熟悉的Photoshop和iIlustrator来说&#xff0c;虽然它们功能强大&#xff0c;但界面太复杂&#xff0c;初学者很难快速启动&#xff0c;面对批量调整的在线原型图&…

Allegro过孔盖油和过孔开窗设置(部分过孔开窗)

Allegro设置一部分过孔盖油&#xff0c;另一部分过孔开窗。 过孔开窗&#xff1a;过孔部分去除阻焊&#xff0c;便于调试和散热&#xff1b; 过孔盖油&#xff1a;过孔盖上阻焊油墨&#xff0c;防止过孔连锡短路。 总结 使用pad designer设计两种via pad&#xff0c;一种不开…

STM32案例学习 GY-39环境监测传感器模块

STM32案例学习 GY-39环境监测传感器模块 硬件平台 野火STM32F1系列开发板正点STM32F1系列开发板STM32F103ZET6核心板GY-39环境监测传感器模块 GY-39环境监测传感器模块 GY-39 是一款低成本&#xff0c;气压&#xff0c;温湿度&#xff0c;光强度传感器模块。工作电压 3-5v…

【JavaEE】HTTP请求的构造

目录 1、通过form表单构造HTTP请求 2、通过JS的ajax构造HTTP请求 3、Postman的安装和简单使用 常见的构造HTTP请求的方式有一下几种&#xff1a; 直接通过浏览器的地址栏&#xff0c;输入一个URL&#xff0c;就可以构造一个GET请求HTML中的一些特殊标签&#xff0c;也会触发…

网工内推 | 美图秀秀招网工,大专以上,15薪,NP认证优先

01 美图公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、美图大厦网络、分公司网络、IT相关项目的网络、办公内网服务器&#xff1b; 2、负责网络的设计、运行、管理和维护等工作&#xff1b; 3、负责远程办公环境的优化、运行、管理和维护工作&#xff1b; 4、…

二级市场负重前行?腾讯音乐的“新伤”与“旧患”

炎炎夏日的7月&#xff0c;于腾讯音乐&#xff08;NYSE:TME、HK:01698&#xff09;而言并不太平。 先是&#xff0c;在7月5日&#xff0c;企鹅FM发布官方公告称由于业务调整&#xff0c;将于9月6日正式停止运营。 仅过十二天&#xff0c;7月17日&#xff0c;腾讯音乐发布公告&…

勒索花样繁多,“Sophos Encrypt”披马甲进行勒索攻击

近日&#xff0c;网络安全供应商Sophos发表声明&#xff0c;称Sophos被一款名为“Sophos Encrypt”新型勒索软件冒充&#xff0c;该勒索软件进行攻击时会冒用Sophos品牌名称&#xff0c;并将用户重要文件进行加密以勒索赎金。 现在的勒索软件类型多样&#xff0c;令企业防不胜防…

Windows搭建Nginx实现RTMP转为HLS流

所需软件 nginx-1.7.11.3-Gryphon&#xff08;这个包含必须的RTMP模块&#xff0c;普通的Ngxin没有这个&#xff09;ffmpegVLC 配置Nginx 1为Nginx配置RTMP和HLS 这里定义了一个叫live的RTMP路径。同时设置其开启HLS功能&#xff0c;那么所有推送到这个地址的RTMP流都会自动生…