使用redis实现聊天记录转存(上)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 使用redis实现聊天记录转存(上)

前言


这几天在实现我开源项目的单聊功能,在实现过程中遇到了需要将聊天记录保存至数据库的问题,在收到消息时肯定不能直接存数据库,因为这样在高并发的场景下,数据库就炸了。


于是,我就想到了redis这个东西,第一次听说它是在2年前,但是一直没时间玩他,现在终于遇到了需要使用它的场景,在用的时候学它,本文就跟大家分享下我的实现思路以及过程,欢迎各位感兴趣的开发者阅读本文。


环境搭建


我的项目是基于SpringBoot2.x搭建的,电脑已经安装了redis,用的maven作为jar包管理工具,所以只需要在maven中添加需要的依赖包即可,如果你用的是其他管理工具,请自行查阅如何添加依赖。


本文需要用到依赖:Redisquartz,在pom.xml文件的dependencies标签下添加下述代码。


<!-- Redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 定时任务调度 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
    <version>2.3.7.RELEASE</version>
</dependency>


application.yml文件中配置相关参数。


spring:
# redis配置
  redis:
    host: 127.0.0.1 # redis地址
    port: 6379 # 端口号
    password:  # 密码
    timeout: 3000 # 连接超时时间,单位毫秒


实现思路


在websocket的服务中,收到客户端推送的消息后,我们对数据进行解析,构造聊天记录实体类,将其保存至redis中,最后我们使用quartz设置定时任务将redis的数据定时写入mysql中。


我们将上述思路进行下整理:


  • 解析客户端数据,构造实体类
  • 将数据保存至redis
  • 使用quartz将redis中的数据定时写入mysql


实现过程


实现思路很简单,难在如何将实体类数据保存至redis,我们需要把redis这一块配置好后,才能继续实现我们的业务需求。


redis支持的数据结构类型有:


  • set 集合,string类型的无序集合,元素不允许重复
  • hash 哈希表,键值对的集合,用于存储对象
  • list 列表,链表结构
  • zset有序集合
  • string 字符串,最基本的数据类型,可以包含任何数据,比如一个序列化的对象,它的字符串大小上限是512MB


redis的客户端分为jedislettuce,在SpringBoot2.x中默认客户端是使用lettuce实现的,因此我们不用做过多配置,在使用的时候通过RedisTemplate.xxx来对redis进行操作即可。


自定义RedisTemplate


在RedisTemplate中,默认是使用Java字符串序列化,将字符串存入redis后可读性很差,因此,我们需要对他进行自定义,使用Jackson 序列化,以 JSON 方式进行存储。


我们在项目的config包下,创建一个名为LettuceRedisConfig的Java文件,我们再此文件中配置其默认序列化规则,它的代码如下:


package com.lk.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
// 自定义RedisTemplate设置序列化器, 方便转换redis中的数据与实体类互转
@Configuration
public class LettuceRedisConfig {
    /**
     * Redis 序列化配置
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);
        // 使用GenericJackson2JsonRedisSerializer替换默认序列化
        GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        // 设置 Key 和 Value 的序列化规则
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        // 初始化 RedisTemplate 序列化完成
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}


封装redis工具类


做完上述操作后,通过RedisTemplate存储到redis中的数据就是json形式的了,接下来我们对其常用的操作封装成工具类,方便我们在项目中使用。


在Utils包中创建一个名为RedisOperatingUtil,其代码如下:


package com.lk.utils;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@Component
// Redis操作工具类
public class RedisOperatingUtil {
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;
    /**
     * 指定 key 的过期时间
     *
     * @param key  键
     * @param time 时间(秒)
     */
    public void setKeyTime(String key, long time) {
        redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }
    /**
     * 根据 key 获取过期时间(-1 即为永不过期)
     *
     * @param key 键
     * @return 过期时间
     */
    public Long getKeyTime(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    /**
     * 判断 key 是否存在
     *
     * @param key 键
     * @return 如果存在 key 则返回 true,否则返回 false
     */
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    /**
     * 删除 key
     *
     * @param key 键
     */
    public Long delKey(String... key) {
        if (key == null || key.length < 1) {
            return 0L;
        }
        return redisTemplate.delete(Arrays.asList(key));
    }
    /**
     * 获取 Key 的类型
     *
     * @param key 键
     */
    public String keyType(String key) {
        DataType dataType = redisTemplate.type(key);
        assert dataType != null;
        return dataType.code();
    }
    /**
     * 批量设置值
     *
     * @param map 要插入的 key value 集合
     */
    public void barchSet(Map<String, Object> map) {
        redisTemplate.opsForValue().multiSet(map);
    }
    /**
     * 批量获取值
     *
     * @param list 查询的 Key 列表
     * @return value 列表
     */
    public List<Object> batchGet(List<String> list) {
        return redisTemplate.opsForValue().multiGet(Collections.singleton(list));
    }
    /**
     * 获取指定对象类型key的值
     *
     * @param key 键
     * @return 值
     */
    public Object objectGetKey(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    /**
     * 设置对象类型的数据
     *
     * @param key   键
     * @param value 值
     */
    public void objectSetValue(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 向list的头部插入一条数据
     *
     * @param key   键
     * @param value 值
     */
    public Long listLeftPush(String key, Object value) {
        return redisTemplate.opsForList().leftPush(key, value);
    }
    /**
     * 向list的末尾插入一条数据
     *
     * @param key   键
     * @param value 值
     */
    public Long listRightPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }
    /**
     * 向list头部添加list数据
     *
     * @param key   键
     * @param value 值
     */
    public Long listLeftPushAll(String key, List<Object> value) {
        return redisTemplate.opsForList().leftPushAll(key, value);
    }
    /**
     * 向list末尾添加list数据
     *
     * @param key   键
     * @param value 值
     */
    public Long listRightPushAll(String key, List<Object> value) {
        return redisTemplate.opsForList().rightPushAll(key, value);
    }
    /**
     * 通过索引设置list元素的值
     *
     * @param key   键
     * @param index 索引
     * @param value 值
     */
    public void listIndexSet(String key, long index, Object value) {
        redisTemplate.opsForList().set(key, index, value);
    }
    /**
     * 获取列表指定范围内的list元素,正数则表示正向查找,负数则倒叙查找
     *
     * @param key   键
     * @param start 开始
     * @param end   结束
     * @return boolean
     */
    public Object listRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    /**
     * 从列表前端开始取出数据
     *
     * @param key 键
     * @return 结果数组对象
     */
    public Object listPopLeftKey(String key) {
        return redisTemplate.opsForList().leftPop(key);
    }
    /**
     * 从列表末尾开始遍历取出数据
     *
     * @param key 键
     * @return 结果数组
     */
    public Object listPopRightKey(String key) {
        return redisTemplate.opsForList().rightPop(key);
    }
    /**
     * 获取list长度
     *
     * @param key 键
     * @return 列表长度
     */
    public Long listLen(String key) {
        return redisTemplate.opsForList().size(key);
    }
    /**
     * 通过索引获取list中的元素
     *
     * @param key   键
     * @param index 索引(index>=0时,0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推)
     * @return 列表中的元素
     */
    public Object listIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }
    /**
     * 移除list元素
     *
     * @param key   键
     * @param count 移除数量("负数"则从列表倒叙查找删除 count 个对应的值; "整数"则从列表正序查找删除 count 个对应的值;)
     * @param value 值
     * @return 成功移除的个数
     */
    public Long listRem(String key, long count, Object value) {
        return redisTemplate.opsForList().remove(key, count, value);
    }
    /**
     * 截取指定范围内的数据, 移除不是范围内的数据
     * @param key 操作的key
     * @param start 截取开始位置
     * @param end 截取激素位置
     */
    public void listTrim(String key, long start, long end) {
        redisTemplate.opsForList().trim(key, start, end);
    }
}


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
缓存 移动开发 NoSQL
php结合redis实现高并发下的抢购、秒杀功能的实例
php结合redis实现高并发下的抢购、秒杀功能的实例
257 0
|
NoSQL Redis
Redis学习4:List数据类型、拓展操作、实现日志等
注意点:对存储空间的顺序进行分析!
Redis学习4:List数据类型、拓展操作、实现日志等
|
存储 NoSQL Redis
Redis学习3:hash类型操作、拓展操作、实现购物等
首先可以理解成一个redis里面有一个小的redis。同时要注意引入了一个field的名字。
Redis学习3:hash类型操作、拓展操作、实现购物等
|
缓存 NoSQL 安全
2021年你还不会Shiro?----10.使用redis实现Shiro的缓存
上一篇文章已经总结了使用ehCache来实现Shiro的缓存管理,步骤也很简单,引入依赖后,直接开启Realm的缓存管理器即可。如果使用Redis来实现缓存管理其实也是一样的,我们也是需要引入redis的依赖,然后开启缓存传入自定义的redis的缓存管理器就行。区别是我们需要为自定义的redis缓存管理器提供自定义的缓存管理类。这个缓存管理类中需要使用到redisTemplate模板,这个模板我们也是需要自己定义。
270 0
2021年你还不会Shiro?----10.使用redis实现Shiro的缓存
|
NoSQL Java 关系型数据库
浅谈Redis实现分布式锁
浅谈Redis实现分布式锁
|
存储 NoSQL 关系型数据库
「Redis」事务实现机制
Redis事务实现机制
586 0
|
消息中间件 设计模式 NoSQL
异步结果通知实现——基于Redis实现,我这操作很可以
前段时间,我在内存中实现了一个简单异步通知框架。但由于没有持久化功能,应用重启就会导致数据丢失,且不支持分布式和集群。今天这篇笔记,引入了 Redis 来解决这些问题,以下是几点理由: 数据结构丰富,支持 List、Sorted Set 等 具有持久化功能,消息的可靠性能得到保证 高可用性,支持单机、主从、集群部署 项目中已使用,接入成本更低 基于 Redis 实现延时队列也有几种方法,展开详细讲讲。
|
NoSQL 前端开发 PHP
thinkphp+redis实现秒杀功能
thinkphp+redis实现秒杀功能
262 0
thinkphp+redis实现秒杀功能
|
存储 NoSQL 安全
分布式锁中-基于 Redis 的实现如何防重入
分布式锁中-基于 Redis 的实现如何防重入
258 0
分布式锁中-基于 Redis 的实现如何防重入
|
存储 消息中间件 缓存
分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇
分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇
362 0
分布式锁中-基于 Redis 的实现需避坑 - Jedis 篇
下一篇
无影云桌面