精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

使用Redis實(shí)現(xiàn)聊天記錄轉(zhuǎn)存

存儲(chǔ) 存儲(chǔ)軟件 Redis
這幾天在實(shí)現(xiàn)我開源項(xiàng)目的單聊功能,在實(shí)現(xiàn)過程中遇到了需要將聊天記錄保存至數(shù)據(jù)庫(kù)的問題,在收到消息時(shí)肯定不能直接存數(shù)據(jù)庫(kù),因?yàn)檫@樣在高并發(fā)的場(chǎng)景下,數(shù)據(jù)庫(kù)就炸了。

 [[357356]]

本文轉(zhuǎn)載自微信公眾號(hào)「 神奇的程序員K」,作者 神奇的程序員K 。轉(zhuǎn)載本文請(qǐng)聯(lián)系 神奇的程序員K公眾號(hào)。

前言

這幾天在實(shí)現(xiàn)我開源項(xiàng)目的單聊功能,在實(shí)現(xiàn)過程中遇到了需要將聊天記錄保存至數(shù)據(jù)庫(kù)的問題,在收到消息時(shí)肯定不能直接存數(shù)據(jù)庫(kù),因?yàn)檫@樣在高并發(fā)的場(chǎng)景下,數(shù)據(jù)庫(kù)就炸了。

于是,我就想到了redis這個(gè)東西,第一次聽說它是在2年前,但是一直沒時(shí)間玩他,現(xiàn)在終于遇到了需要使用它的場(chǎng)景,在用的時(shí)候?qū)W它,本文就跟大家分享下我的實(shí)現(xiàn)思路以及過程,歡迎各位感興趣的開發(fā)者閱讀本文。

環(huán)境搭建

我的項(xiàng)目是基于SpringBoot2.x搭建的,電腦已經(jīng)安裝了redis,用的maven作為jar包管理工具,所以只需要在maven中添加需要的依賴包即可,如果你用的是其他管理工具,請(qǐng)自行查閱如何添加依賴。

本文需要用到依賴:Redis 、quartz,在pom.xml文件的dependencies標(biāo)簽下添加下述代碼。

  1. <!-- Redis --> 
  2. <dependency> 
  3.     <groupId>org.springframework.boot</groupId> 
  4.     <artifactId>spring-boot-starter-data-redis</artifactId> 
  5. </dependency> 
  6. <!-- 定時(shí)任務(wù)調(diào)度 --> 
  7. <dependency> 
  8.     <groupId>org.springframework.boot</groupId> 
  9.     <artifactId>spring-boot-starter-quartz</artifactId> 
  10.     <version>2.3.7.RELEASE</version> 
  11. </dependency> 

在application.yml文件中配置相關(guān)參數(shù)。

  1. spring: 
  2. # redis配置 
  3.   redis: 
  4.     host: 127.0.0.1 # redis地址 
  5.     port: 6379 # 端口號(hào) 
  6.     password:  # 密碼 
  7.     timeout: 3000 # 連接超時(shí)時(shí)間,單位毫秒 

實(shí)現(xiàn)思路

在websocket的服務(wù)中,收到客戶端推送的消息后,我們對(duì)數(shù)據(jù)進(jìn)行解析,構(gòu)造聊天記錄實(shí)體類,將其保存至redis中,最后我們使用quartz設(shè)置定時(shí)任務(wù)將redis的數(shù)據(jù)定時(shí)寫入mysql中。

我們將上述思路進(jìn)行下整理:

  • 解析客戶端數(shù)據(jù),構(gòu)造實(shí)體類
  • 將數(shù)據(jù)保存至redis
  • 使用quartz將redis中的數(shù)據(jù)定時(shí)寫入mysql

實(shí)現(xiàn)過程

實(shí)現(xiàn)思路很簡(jiǎn)單,難在如何將實(shí)體類數(shù)據(jù)保存至redis,我們需要把redis這一塊配置好后,才能繼續(xù)實(shí)現(xiàn)我們的業(yè)務(wù)需求。

redis支持的數(shù)據(jù)結(jié)構(gòu)類型有:

  • set 集合,string類型的無序集合,元素不允許重復(fù)
  • hash 哈希表,鍵值對(duì)的集合,用于存儲(chǔ)對(duì)象
  • list 列表,鏈表結(jié)構(gòu)
  • zset有序集合
  • string 字符串,最基本的數(shù)據(jù)類型,可以包含任何數(shù)據(jù),比如一個(gè)序列化的對(duì)象,它的字符串大小上限是512MB

redis的客戶端分為jedis 和 lettuce,在SpringBoot2.x中默認(rèn)客戶端是使用lettuce實(shí)現(xiàn)的,因此我們不用做過多配置,在使用的時(shí)候通過RedisTemplate.xxx來對(duì)redis進(jìn)行操作即可。

自定義RedisTemplate

在RedisTemplate中,默認(rèn)是使用Java字符串序列化,將字符串存入redis后可讀性很差,因此,我們需要對(duì)他進(jìn)行自定義,使用Jackson 序列化,以 JSON 方式進(jìn)行存儲(chǔ)。

我們?cè)陧?xiàng)目的config包下,創(chuàng)建一個(gè)名為L(zhǎng)ettuceRedisConfig的Java文件,我們?cè)俅宋募信渲闷淠J(rèn)序列化規(guī)則,它的代碼如下:

  1. package com.lk.config; 
  2.  
  3. import org.springframework.context.annotation.Bean; 
  4. import org.springframework.context.annotation.Configuration; 
  5. import org.springframework.data.redis.connection.RedisConnectionFactory; 
  6. import org.springframework.data.redis.core.RedisTemplate; 
  7. import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; 
  8. import org.springframework.data.redis.serializer.StringRedisSerializer; 
  9.  
  10.  
  11. // 自定義RedisTemplate設(shè)置序列化器, 方便轉(zhuǎn)換redis中的數(shù)據(jù)與實(shí)體類互轉(zhuǎn) 
  12. @Configuration 
  13. public class LettuceRedisConfig { 
  14.     /** 
  15.      * Redis 序列化配置 
  16.      */ 
  17.     @Bean 
  18.     public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) { 
  19.         RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); 
  20.         redisTemplate.setConnectionFactory(connectionFactory); 
  21.         // 使用GenericJackson2JsonRedisSerializer替換默認(rèn)序列化 
  22.         GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); 
  23.         // 設(shè)置 Key 和 Value 的序列化規(guī)則 
  24.         redisTemplate.setKeySerializer(new StringRedisSerializer()); 
  25.         redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); 
  26.         redisTemplate.setHashKeySerializer(new StringRedisSerializer()); 
  27.         redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); 
  28.         // 初始化 RedisTemplate 序列化完成 
  29.         redisTemplate.afterPropertiesSet(); 
  30.         return redisTemplate; 
  31.     } 

封裝redis工具類

做完上述操作后,通過RedisTemplate存儲(chǔ)到redis中的數(shù)據(jù)就是json形式的了,接下來我們對(duì)其常用的操作封裝成工具類,方便我們?cè)陧?xiàng)目中使用。

在Utils包中創(chuàng)建一個(gè)名為RedisOperatingUtil,其代碼如下:

  1. package com.lk.utils; 
  2.  
  3. import org.springframework.data.redis.connection.DataType; 
  4. import org.springframework.data.redis.core.RedisTemplate; 
  5. import org.springframework.stereotype.Component; 
  6.  
  7. import javax.annotation.Resource; 
  8. import java.util.Arrays; 
  9. import java.util.Collections; 
  10. import java.util.List; 
  11. import java.util.Map; 
  12. import java.util.concurrent.TimeUnit; 
  13.  
  14. @Component 
  15. // Redis操作工具類 
  16. public class RedisOperatingUtil { 
  17.     @Resource 
  18.     private RedisTemplate<Object, Object> redisTemplate; 
  19.  
  20.     /** 
  21.      * 指定 key 的過期時(shí)間 
  22.      * 
  23.      * @param key  鍵 
  24.      * @param time 時(shí)間(秒) 
  25.      */ 
  26.     public void setKeyTime(String key, long time) { 
  27.         redisTemplate.expire(keytime, TimeUnit.SECONDS); 
  28.     } 
  29.  
  30.     /** 
  31.      * 根據(jù) key 獲取過期時(shí)間(-1 即為永不過期) 
  32.      * 
  33.      * @param key 鍵 
  34.      * @return 過期時(shí)間 
  35.      */ 
  36.     public Long getKeyTime(String key) { 
  37.         return redisTemplate.getExpire(key, TimeUnit.SECONDS); 
  38.     } 
  39.  
  40.     /** 
  41.      * 判斷 key 是否存在 
  42.      * 
  43.      * @param key 鍵 
  44.      * @return 如果存在 key 則返回 true,否則返回 false 
  45.      */ 
  46.     public Boolean hasKey(String key) { 
  47.         return redisTemplate.hasKey(key); 
  48.     } 
  49.  
  50.     /** 
  51.      * 刪除 key 
  52.      * 
  53.      * @param key 鍵 
  54.      */ 
  55.     public Long delKey(String... key) { 
  56.         if (key == null || key.length < 1) { 
  57.             return 0L; 
  58.         } 
  59.         return redisTemplate.delete(Arrays.asList(key)); 
  60.     } 
  61.  
  62.     /** 
  63.      * 獲取 Key 的類型 
  64.      * 
  65.      * @param key 鍵 
  66.      */ 
  67.     public String keyType(String key) { 
  68.         DataType dataType = redisTemplate.type(key); 
  69.         assert dataType != null
  70.         return dataType.code(); 
  71.     } 
  72.  
  73.     /** 
  74.      * 批量設(shè)置值 
  75.      * 
  76.      * @param map 要插入的 key value 集合 
  77.      */ 
  78.     public void barchSet(Map<String, Object> map) { 
  79.         redisTemplate.opsForValue().multiSet(map); 
  80.     } 
  81.  
  82.     /** 
  83.      * 批量獲取值 
  84.      * 
  85.      * @param list 查詢的 Key 列表 
  86.      * @return value 列表 
  87.      */ 
  88.     public List<Object> batchGet(List<String> list) { 
  89.         return redisTemplate.opsForValue().multiGet(Collections.singleton(list)); 
  90.     } 
  91.  
  92.  
  93.     /** 
  94.      * 獲取指定對(duì)象類型key的值 
  95.      * 
  96.      * @param key 鍵 
  97.      * @return 值 
  98.      */ 
  99.     public Object objectGetKey(String key) { 
  100.         return redisTemplate.opsForValue().get(key); 
  101.     } 
  102.  
  103.     /** 
  104.      * 設(shè)置對(duì)象類型的數(shù)據(jù) 
  105.      * 
  106.      * @param key   鍵 
  107.      * @param value 值 
  108.      */ 
  109.     public void objectSetValue(String key, Object value) { 
  110.         redisTemplate.opsForValue().set(key, value); 
  111.     } 
  112.  
  113.     /** 
  114.      * 向list的頭部插入一條數(shù)據(jù) 
  115.      * 
  116.      * @param key   鍵 
  117.      * @param value 值 
  118.      */ 
  119.     public Long listLeftPush(String key, Object value) { 
  120.         return redisTemplate.opsForList().leftPush(key, value); 
  121.     } 
  122.  
  123.     /** 
  124.      * 向list的末尾插入一條數(shù)據(jù) 
  125.      * 
  126.      * @param key   鍵 
  127.      * @param value 值 
  128.      */ 
  129.     public Long listRightPush(String key, Object value) { 
  130.         return redisTemplate.opsForList().rightPush(key, value); 
  131.     } 
  132.  
  133.     /** 
  134.      * 向list頭部添加list數(shù)據(jù) 
  135.      * 
  136.      * @param key   鍵 
  137.      * @param value 值 
  138.      */ 
  139.     public Long listLeftPushAll(String key, List<Object> value) { 
  140.         return redisTemplate.opsForList().leftPushAll(key, value); 
  141.     } 
  142.  
  143.     /** 
  144.      * 向list末尾添加list數(shù)據(jù) 
  145.      * 
  146.      * @param key   鍵 
  147.      * @param value 值 
  148.      */ 
  149.     public Long listRightPushAll(String key, List<Object> value) { 
  150.         return redisTemplate.opsForList().rightPushAll(key, value); 
  151.     } 
  152.  
  153.     /** 
  154.      * 通過索引設(shè)置list元素的值 
  155.      * 
  156.      * @param key   鍵 
  157.      * @param index 索引 
  158.      * @param value 值 
  159.      */ 
  160.     public void listIndexSet(String key, long index, Object value) { 
  161.         redisTemplate.opsForList().set(keyindex, value); 
  162.     } 
  163.  
  164.     /** 
  165.      * 獲取列表指定范圍內(nèi)的list元素,正數(shù)則表示正向查找,負(fù)數(shù)則倒敘查找 
  166.      * 
  167.      * @param key   鍵 
  168.      * @param start 開始 
  169.      * @param end   結(jié)束 
  170.      * @return boolean 
  171.      */ 
  172.     public Object listRange(String key, long start, long end) { 
  173.         return redisTemplate.opsForList().range(key, start, end); 
  174.     } 
  175.  
  176.     /** 
  177.      * 從列表前端開始取出數(shù)據(jù) 
  178.      * 
  179.      * @param key 鍵 
  180.      * @return 結(jié)果數(shù)組對(duì)象 
  181.      */ 
  182.     public Object listPopLeftKey(String key) { 
  183.         return redisTemplate.opsForList().leftPop(key); 
  184.     } 
  185.  
  186.     /** 
  187.      * 從列表末尾開始遍歷取出數(shù)據(jù) 
  188.      * 
  189.      * @param key 鍵 
  190.      * @return 結(jié)果數(shù)組 
  191.      */ 
  192.     public Object listPopRightKey(String key) { 
  193.         return redisTemplate.opsForList().rightPop(key); 
  194.     } 
  195.  
  196.     /** 
  197.      * 獲取list長(zhǎng)度 
  198.      * 
  199.      * @param key 鍵 
  200.      * @return 列表長(zhǎng)度 
  201.      */ 
  202.     public Long listLen(String key) { 
  203.         return redisTemplate.opsForList().size(key); 
  204.     } 
  205.  
  206.     /** 
  207.      * 通過索引獲取list中的元素 
  208.      * 
  209.      * @param key   鍵 
  210.      * @param index 索引(index>=0時(shí),0 表頭,1 第二個(gè)元素,依次類推;index<0時(shí),-1,表尾,-2倒數(shù)第二個(gè)元素,依次類推) 
  211.      * @return 列表中的元素 
  212.      */ 
  213.     public Object listIndex(String key, long index) { 
  214.         return redisTemplate.opsForList().index(keyindex); 
  215.     } 
  216.  
  217.     /** 
  218.      * 移除list元素 
  219.      * 
  220.      * @param key   鍵 
  221.      * @param count 移除數(shù)量("負(fù)數(shù)"則從列表倒敘查找刪除 count 個(gè)對(duì)應(yīng)的值; "整數(shù)"則從列表正序查找刪除 count 個(gè)對(duì)應(yīng)的值;) 
  222.      * @param value 值 
  223.      * @return 成功移除的個(gè)數(shù) 
  224.      */ 
  225.     public Long listRem(String key, long count, Object value) { 
  226.         return redisTemplate.opsForList().remove(keycount, value); 
  227.     } 
  228.  
  229.     /** 
  230.      * 截取指定范圍內(nèi)的數(shù)據(jù), 移除不是范圍內(nèi)的數(shù)據(jù) 
  231.      * @param key 操作的key 
  232.      * @param start 截取開始位置 
  233.      * @param end 截取激素位置 
  234.      */ 
  235.     public void listTrim(String key, long start, long end) { 
  236.         redisTemplate.opsForList().trim(key, start, end); 
  237.     } 

進(jìn)行單元測(cè)試

做完上述操作后,最難弄的一關(guān)我們就已經(jīng)搞定了,接下來我們來對(duì)一會(huì)需要使用的方法進(jìn)行單元測(cè)試,確保其能夠正常運(yùn)行。

創(chuàng)建一個(gè)名為RedisTest的Java文件,注入需要用到的相關(guān)類。

  • redisOperatingUtil為我們的redis工具類
  • subMessageMapper為聊天記錄表的dao層
  1. @RunWith(SpringRunner.class) 
  2. @SpringBootTest 
  3. @Slf4j 
  4. public class RedisTest { 
  5.     @Resource 
  6.     private RedisOperatingUtil redisOperatingUtil; 
  7.     @Resource 
  8.     private SubMessageMapper subMessageMapper; 

接下來,我們看下SubMessage實(shí)體類的代碼。

  1. package com.lk.entity; 
  2.  
  3. import lombok.AllArgsConstructor; 
  4. import lombok.Getter; 
  5. import lombok.NoArgsConstructor; 
  6. import lombok.Setter; 
  7.  
  8. @Getter 
  9. @Setter 
  10. @NoArgsConstructor 
  11. @AllArgsConstructor 
  12. // 聊天記錄-消息內(nèi)容 
  13. public class SubMessage { 
  14.   private Integer id; 
  15.   private String msgText; // 消息內(nèi)容 
  16.   private String createTime; // 創(chuàng)建時(shí)間 
  17.   private String userName; // 用戶名 
  18.   private String userId; // 推送方用戶id 
  19.   private String avatarSrc; // 推送方頭像 
  20.   private String msgId; // 接收方用戶id 
  21.   private Boolean status; // 消息狀態(tài) 

測(cè)試list數(shù)據(jù)的寫入與獲取

在單元測(cè)試類內(nèi)部加入下述代碼:

  1. @Test 
  2.     public void testSerializableListRedisTemplate() { 
  3.         // 構(gòu)造聊天記錄實(shí)體類數(shù)據(jù) 
  4.         SubMessage subMessage = new SubMessage(); 
  5.         subMessage.setAvatarSrc("https://www.kaisir.cn/uploads/1ece3749801d4d45933ba8b31403c685touxiang.jpeg"); 
  6.         subMessage.setUserId("1090192"); 
  7.         subMessage.setUserName("神奇的程序員"); 
  8.         subMessage.setMsgText("你好"); 
  9.         subMessage.setMsgId("2901872"); 
  10.         subMessage.setCreateTime("2020-12-12 18:54:06"); 
  11.         subMessage.setStatus(false); 
  12.         // 將聊天記錄對(duì)象保存到redis中 
  13.         redisOperatingUtil.listRightPush("subMessage", subMessage); 
  14.         // 獲取list中的數(shù)據(jù) 
  15.         Object resultObj = redisOperatingUtil.listRange("subMessage", 0, redisOperatingUtil.listLen("subMessage")); 
  16.         // 將Object安全的轉(zhuǎn)為L(zhǎng)ist 
  17.         List<SubMessage> resultList = ObjectToOtherUtil.castList(resultObj, SubMessage.class); 
  18.         // 遍歷獲取到的結(jié)果 
  19.         if (resultList != null) { 
  20.             for (SubMessage message : resultList) { 
  21.                 System.out.println(message.getUserName()); 
  22.             } 
  23.         } 
  24.     } 

在上述代碼中,我們從redis中取出的數(shù)據(jù)是Object類型的,我們要將它轉(zhuǎn)換為與之對(duì)應(yīng)的實(shí)體類,一開始我是用的類型強(qiáng)轉(zhuǎn),但是idea會(huì)報(bào)黃色警告,于是就寫了一個(gè)工具類用于將Object對(duì)象安全的轉(zhuǎn)換為與之對(duì)應(yīng)的類型,代碼如下:

  1. package com.lk.utils; 
  2.  
  3. import java.util.ArrayList; 
  4. import java.util.List; 
  5.  
  6. public class ObjectToOtherUtil { 
  7.     public static <T> List<T> castList(Object obj, Class<T> clazz) { 
  8.         List<T> result = new ArrayList<>(); 
  9.         if (obj instanceof List<?>) { 
  10.             for (Object o : (List<?>) obj) { 
  11.                 result.add(clazz.cast(o)); 
  12.             } 
  13.             return result; 
  14.         } 
  15.         return null
  16.     } 

執(zhí)行后,我們看看redis是否有保存到我們寫入的數(shù)據(jù),如下所示,已經(jīng)成功保存。

image-20201213163924700

我們?cè)賮砜纯矗a的執(zhí)行結(jié)果,看看有沒有成功獲取到數(shù)據(jù),如下圖所示,也成功取到了。

image-20201213164038308

注意:如果你的項(xiàng)目對(duì)websocket進(jìn)行了啟動(dòng)配置,可能會(huì)導(dǎo)致單元測(cè)試失敗,報(bào)錯(cuò)java.lang.IllegalStateException: Failed to load ApplicationContext,解決方案就是注釋掉websocket配置文件中的@Configuration即可。

測(cè)試list數(shù)據(jù)的取出

當(dāng)我們把redis中存儲(chǔ)的數(shù)據(jù)遷移到mysql后,需要?jiǎng)h除redis中的數(shù)據(jù),一開始我用的是它的delete方法,但是他的delete方法只能刪除與之匹配的值,不能選擇一個(gè)區(qū)間進(jìn)行刪除,于是就決定用它的pop方法進(jìn)行出棧操作。

我們來測(cè)試下工具類中的listPopLeftKey方法。

  1. @Test 
  2.     public void testListPop() { 
  3.         long item = 0; 
  4.         // 獲取存儲(chǔ)在redis中聊天記錄的條數(shù) 
  5.         long messageListSize = redisOperatingUtil.listLen("subMessage"); 
  6.         for (int i = 0; i < messageListSize; i++) { 
  7.             // 從頭向尾取出鏈表中的元素 
  8.             SubMessage messageResult = (SubMessage) redisOperatingUtil.listPopLeftKey("subMessage"); 
  9.             log.info(messageResult.getMsgText()); 
  10.             item++; 
  11.         } 
  12.         log.info(item+"條數(shù)據(jù)已成功取出"); 
  13.     } 

執(zhí)行結(jié)果如下所示,成功取出了redis中存儲(chǔ)的兩條數(shù)據(jù)。

image-20201213170726492

測(cè)試聊天記錄轉(zhuǎn)移至數(shù)據(jù)庫(kù)

接下來我們?cè)趓edis中放入三條數(shù)據(jù)用于測(cè)試

image-20201213171623890

我們測(cè)試下將redis中的數(shù)據(jù)取出,然后寫入數(shù)據(jù)庫(kù),代碼如下:

  1. // 測(cè)試聊天記錄轉(zhuǎn)移數(shù)據(jù)庫(kù) 
  2.     @Test 
  3.     public void testRedisToMysqlTask() { 
  4.         // 獲取存儲(chǔ)在redis中聊天記錄的條數(shù) 
  5.         long messageListSize = redisOperatingUtil.listLen("subMessage"); 
  6.         // 寫入數(shù)據(jù)庫(kù)的數(shù)據(jù)總條數(shù) 
  7.         long resultCount = 0; 
  8.         for (int i = 0; i < messageListSize; i++) { 
  9.             // 從頭到尾取出鏈表中的元素 
  10.             SubMessage subMessage= (SubMessage) redisOperatingUtil.listPopLeftKey("subMessage"); 
  11.             // 向數(shù)據(jù)庫(kù)寫入數(shù)據(jù) 
  12.             int result = subMessageMapper.addMessageTextInfo(subMessage); 
  13.             if (result > 0) { 
  14.                 // 寫入成功 
  15.                 resultCount++; 
  16.             } 
  17.         } 
  18.         log.info(resultCount+ "條聊天記錄,已寫入數(shù)據(jù)庫(kù)"); 
  19.     } 

執(zhí)行結(jié)果如下,數(shù)據(jù)已成功寫入數(shù)據(jù)庫(kù)且redis中的數(shù)據(jù)也被刪除。

image-20201213171834299

 

image-20201213171956311

 

image-20201213172031222

解析客戶端數(shù)據(jù)保存至redis

完成上述操作后,我們r(jià)edis那一塊的東西就搞定了,接下來就可以實(shí)現(xiàn)將客戶端的數(shù)據(jù)存到redis里了。

這里有個(gè)坑,因?yàn)閣ebsocket服務(wù)類中用到了@Component,會(huì)導(dǎo)致redis的工具類注入失敗,出現(xiàn)null的情況,解決這個(gè)問題需要將當(dāng)前類名聲明為靜態(tài)變量,然后在init中獲取賦值redis工具類,代碼如下:

  1. // 解決redis操作工具類注入為null的問題 
  2.     public static WebSocketServer webSocketServer; 
  3.     @PostConstruct 
  4.     public void init() { 
  5.         webSocketServer = this; 
  6.         webSocketServer.redisOperatingUtil = this.redisOperatingUtil; 
  7.     } 

在websocket服務(wù)的@OnMessage注解中,收到客戶端發(fā)送的消息,我們將其保存到redis中,代碼如下:

  1. /** 
  2.      * 收到客戶端消息后調(diào)用的方法 
  3.      * 
  4.      * @param message 客戶端發(fā)送過來的消息 
  5.      *                // @param session 客戶端會(huì)話 
  6.      */ 
  7.     @OnMessage 
  8.     public void onMessage(String message) { 
  9.         // 客戶端發(fā)送的消息 
  10.         JSONObject jsReply = new JSONObject(message); 
  11.         // 添加在線人數(shù) 
  12.         jsReply.put("onlineUsers", getOnlineCount()); 
  13.         if (jsReply.has("buddyId")) { 
  14.             // 獲取推送方id 
  15.             String userId = jsReply.getString("userID"); 
  16.             // 獲取被推送方id 
  17.             String buddyId = jsReply.getString("buddyId"); 
  18.             // 非測(cè)試數(shù)據(jù)則推送消息 
  19.             if (!buddyId.equals("121710f399b84322bdecc238199d6888")) { 
  20.                 // 發(fā)送消息至推送方 
  21.                 this.sendInfo(jsReply.toString(), userId); 
  22.             } 
  23.             // 構(gòu)造聊天記錄實(shí)體類數(shù)據(jù) 
  24.             SubMessage subMessage = new SubMessage(); 
  25.             subMessage.setAvatarSrc(jsReply.getString("avatarSrc")); 
  26.             subMessage.setUserId(jsReply.getString("userID")); 
  27.             subMessage.setUserName(jsReply.getString("username")); 
  28.             subMessage.setMsgText(jsReply.getString("msg")); 
  29.             subMessage.setMsgId(jsReply.getString("msgId")); 
  30.             subMessage.setCreateTime(DateUtil.getThisTime()); 
  31.             subMessage.setStatus(false); 
  32.             // 將聊天記錄對(duì)象保存到redis中 
  33.             webSocketServer.redisOperatingUtil.listRightPush("subMessage", subMessage); 
  34.             // 發(fā)送消息至被推送方 
  35.             this.sendInfo(jsReply.toString(), buddyId); 
  36.         } 
  37.     } 

做完上述操作后,收到客戶端發(fā)送的消息就會(huì)自動(dòng)寫入redis。

定時(shí)將redis的數(shù)據(jù)寫入mysql

接下來,我們使用quartz定時(shí)向mysql中寫入數(shù)據(jù),他執(zhí)行定時(shí)任務(wù)的步驟分為2步:

創(chuàng)建任務(wù)類編寫任務(wù)內(nèi)容

在QuartzConfig文件中設(shè)置定時(shí),執(zhí)行第一步創(chuàng)建的任務(wù)。

首先,創(chuàng)建quartzServer包,在其下創(chuàng)建RedisToMysqlTask.java文件,在此文件內(nèi)實(shí)現(xiàn)redis寫入mysql的代碼

  1. package com.lk.quartzServer; 
  2.  
  3. import com.lk.dao.SubMessageMapper; 
  4. import com.lk.entity.SubMessage; 
  5. import com.lk.utils.RedisOperatingUtil; 
  6. import lombok.extern.slf4j.Slf4j; 
  7. import org.quartz.JobExecutionContext; 
  8. import org.quartz.JobExecutionException; 
  9. import org.springframework.scheduling.quartz.QuartzJobBean; 
  10.  
  11. import javax.annotation.Resource; 
  12.  
  13. // 將redis數(shù)據(jù)放進(jìn)mysql中 
  14. @Slf4j 
  15. public class RedisToMysqlTask extends QuartzJobBean { 
  16.     @Resource 
  17.     private RedisOperatingUtil redisOperatingUtil; 
  18.     @Resource 
  19.     private SubMessageMapper subMessageMapper; 
  20.  
  21.     @Override 
  22.     protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { 
  23.         // 獲取存儲(chǔ)在redis中聊天記錄的條數(shù) 
  24.         long messageListSize = redisOperatingUtil.listLen("subMessage"); 
  25.         // 寫入數(shù)據(jù)庫(kù)的數(shù)據(jù)總條數(shù) 
  26.         long resultCount = 0; 
  27.         for (int i = 0; i < messageListSize; i++) { 
  28.             // 從頭到尾取出鏈表中的元素 
  29.             SubMessage subMessage= (SubMessage) redisOperatingUtil.listPopLeftKey("subMessage"); 
  30.             // 向數(shù)據(jù)庫(kù)寫入數(shù)據(jù) 
  31.             int result = subMessageMapper.addMessageTextInfo(subMessage); 
  32.             if (result > 0) { 
  33.                 // 寫入成功 
  34.                 resultCount++; 
  35.             } 
  36.         } 
  37.         log.info(resultCount+ "條聊天記錄,已寫入數(shù)據(jù)庫(kù)"); 
  38.     } 

在config包下創(chuàng)建QuartzConfig.java文件,創(chuàng)建定時(shí)任務(wù)

  1. package com.lk.config; 
  2.  
  3. import com.lk.quartzServer.RedisToMysqlTask; 
  4. import org.quartz.*; 
  5. import org.springframework.context.annotation.Bean; 
  6. import org.springframework.context.annotation.Configuration; 
  7.  
  8. /** 
  9.  * Quartz定時(shí)任務(wù)配置 
  10.  */ 
  11. @Configuration 
  12. public class QuartzConfig { 
  13.     @Bean 
  14.     public JobDetail RedisToMysqlQuartz() { 
  15.         // 執(zhí)行定時(shí)任務(wù) 
  16.         return JobBuilder.newJob(RedisToMysqlTask.class).withIdentity("CallPayQuartzTask").storeDurably().build(); 
  17.     } 
  18.  
  19.     @Bean 
  20.     public Trigger CallPayQuartzTaskTrigger() { 
  21.         //cron方式,從每月1號(hào)開始,每隔三天就執(zhí)行一次 
  22.         return TriggerBuilder.newTrigger().forJob(RedisToMysqlQuartz()) 
  23.                 .withIdentity("CallPayQuartzTask"
  24.                 .withSchedule(CronScheduleBuilder.cronSchedule("* * 4 1/3 * ?")) 
  25.                 .build(); 
  26.     } 

這里我設(shè)置的定時(shí)任務(wù)是從每月1號(hào)開始,每隔三天就執(zhí)行一次,Quartz定時(shí)任務(wù)采用的是cron表達(dá)式,自己算這個(gè)比較麻煩,這里推薦一個(gè)在線網(wǎng)站,可以很容易的生成表達(dá)式:Cron表達(dá)式生成器

實(shí)現(xiàn)效果

最后,配合Vue實(shí)現(xiàn)的瀏覽器端,跟大家展示下實(shí)現(xiàn)效果:

效果視頻:使用Vue實(shí)現(xiàn)單聊

項(xiàng)目瀏覽器端代碼地址:github/chat-system

項(xiàng)目在線體驗(yàn)地址:chat-system

 

 

責(zé)任編輯:武曉燕 來源: 神奇的程序員k
相關(guān)推薦

2022-07-15 15:11:27

SQLite微信數(shù)據(jù)庫(kù)

2021-09-08 14:54:51

微信功能備份

2023-11-09 14:40:56

大數(shù)據(jù)自動(dòng)化工具

2025-03-27 09:46:59

2023-05-11 15:12:12

2020-12-22 06:02:48

JS聚合聊天

2021-03-29 09:23:08

微信聊天記錄移動(dòng)應(yīng)用

2025-09-19 08:43:46

2025-09-02 06:38:33

ClaudeAnthropicAI

2012-05-18 14:53:25

2021-09-08 14:50:38

微信聊天記錄移動(dòng)應(yīng)用

2019-11-05 10:00:06

手機(jī)QQQQ空間QQ

2023-05-09 08:54:01

ChatGPT必應(yīng)聊天

2025-07-17 10:08:30

2019-11-04 10:40:06

手機(jī)QQ聊天記錄

2011-12-28 14:41:02

金山快盤同步MSN聊天

2021-04-06 11:03:28

WhatsAppiPhoneAndroid

2025-02-25 13:55:06

2022-03-01 15:34:48

勒索組織黑客網(wǎng)絡(luò)安全

2021-08-12 07:24:42

WhatsAppAndroidiOS
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

高清视频一区二区| 日韩高清影视在线观看| 一区二区三区四区av| 2014亚洲精品| 97免费在线观看视频| 天海翼精品一区二区三区| 欧美中文字幕一二三区视频| 免费看av软件| 四虎影院在线域名免费观看| 亚洲最大成人| 韩国一区二区在线观看| 欧美国产日韩一区二区在线观看| 国产精品久久久免费观看| 另类中文字幕国产精品| 亚洲一二三区视频在线观看| 国产精品日韩欧美大师| 全程偷拍露脸中年夫妇| 日韩美女国产精品| 欧美一区二区观看视频| 精品视频一区二区在线| 黄色网在线看| 91丨九色丨蝌蚪富婆spa| 国产欧美日韩精品在线观看| 久久久www成人免费毛片| 精品久久久久久久久久久下田 | 四虎精品欧美一区二区免费| 天堂а在线中文在线无限看推荐| 国产一区在线观看视频| 国产成人小视频在线观看| 久久久久久久福利| 婷婷综合伊人| 亚洲午夜精品久久久久久久久久久久| 动漫美女无遮挡免费| 亚洲欧美一级| 欧美日韩在线观看一区二区 | 欧美色视频一区二区三区在线观看| jizz国产精品| 日韩三级视频在线看| 亚欧洲精品在线视频免费观看| 精品人妻一区二区三区三区四区 | 99日在线视频| 日韩高清成人| 欧美性猛交xxxx偷拍洗澡 | 日本成人中文字幕在线视频| 91精品国产九九九久久久亚洲| 永久久久久久久| 久久国产成人精品| 色yeye香蕉凹凸一区二区av| 亚洲AV无码成人精品区明星换面| 天天躁日日躁狠狠躁欧美巨大小说 | 手机av免费观看| 日本欧美高清| 亚洲国产精久久久久久久| 九九九九九国产| 国产成人a视频高清在线观看| 欧美日韩精品国产| 免费 成 人 黄 色| 韩日毛片在线观看| 精品国产1区2区| 欧美日韩一道本| 日韩欧美一中文字暮专区| 久久婷婷一区二区三区| 精品国产乱码久久久久软件| 国产精品成人久久| 一区福利视频| 97精品国产97久久久久久免费| 久久久久久久久久91| 欧美午夜a级限制福利片| 欧美精品一区视频| 久久久久久三级| 三上悠亚国产精品一区二区三区| 欧美性xxxx极品hd满灌| 日韩一级在线免费观看| 久久xxx视频| 欧美美女激情18p| 九色91porny| 成人动态视频| 亚洲香蕉成人av网站在线观看| av永久免费观看| 国产韩日影视精品| 国模精品系列视频| www.国产毛片| 狠狠色综合日日| 国产精品v欧美精品v日韩| 熟妇人妻av无码一区二区三区| 91麻豆蜜桃一区二区三区| 日韩精品一区二区三区丰满| 亚洲精品成av人片天堂无码 | 精品盗摄女厕tp美女嘘嘘| 伊人一区二区三区久久精品 | 国产一区电影| 亚洲少妇中出一区| 精品欧美日韩在线| 国产免费永久在线观看| 亚洲欧洲av一区二区三区久久| 国产一区二区三区高清| 国产三级视频在线播放线观看| 国产精品毛片久久久久久| 久久天天东北熟女毛茸茸| 国产精品电影| 久久中文精品视频| 爱豆国产剧免费观看大全剧苏畅| 精品视频一区二区三区| 尤物在线观看一区| www..com日韩| 国产精品传媒麻豆hd| 欧美videofree性高清杂交| 91精品国产自产| 亚洲乱码在线| 欲色天天网综合久久| 熟女av一区二区| 午夜一级久久| 动漫一区二区在线| 中文字幕日本在线观看| 精品久久久中文| 久久久精品视频国产| 精品久久99| 日韩成人在线视频观看| 日韩国产第一页| 视频在线观看91| 国产视频在线观看一区| 国产原创视频在线观看| 色视频成人在线观看免| jjzz黄色片| 国产高清欧美| 国产精品一区二区电影| 欧美成人免费| 五月婷婷欧美视频| 亚洲欧美综合视频| 亚洲激情五月| 国产精品一区二区久久久| 户外极限露出调教在线视频| 亚洲一区二区三区小说| 爱豆国产剧免费观看大全剧苏畅| 欧美日韩国产在线观看网站| 欧美在线激情视频| 天堂网av2014| 亚洲a一区二区| 佐佐木明希电影| gogo人体一区| 久久99久国产精品黄毛片入口| 中文字幕在线2019| 国产三级一区二区三区| 欧美国产亚洲一区| 欧美日韩一本| 欧洲亚洲在线视频| 日韩一二三四| 欧美丝袜美女中出在线| 亚洲天堂2024| 夜夜夜久久久| 欧美精品尤物在线| 悠悠资源网亚洲青| 亚洲精品综合精品自拍| 特级做a爱片免费69| 久久综合中文字幕| 黄色一级一级片| 蜜桃成人av| 国产精品成人一区二区三区吃奶| 国产最新视频在线观看| 在线精品国精品国产尤物884a| 成年人在线免费看片| 蜜臀久久久久久久| 久久av秘一区二区三区| 久久精品一级| 久久免费少妇高潮久久精品99| 人妻va精品va欧美va| 天天色综合成人网| 丰满少妇高潮一区二区| 日本不卡视频在线| 美女黄色片网站| 亚洲天堂av资源在线观看| 久久久久久久久久久久久久久久久久av| 日韩视频免费观看高清| 99九九99九九九视频精品| 不卡影院一区二区| 日韩精品首页| 99精品在线直播| 日韩在线伦理| 深夜福利91大全| 精品人妻少妇嫩草av无码专区| 亚洲第一福利视频在线| 亚洲人成人无码网www国产| 美女视频网站久久| 日本三级中文字幕在线观看| 欧美韩一区二区| 国产精品久久久久久久久久久久| 色视频在线免费观看| 日韩精品资源二区在线| 好看的av在线| 综合激情成人伊人| 三级网站免费看| 一区二区三区四区五区精品视频 | 欧美精品在线视频观看| 婷婷在线免费观看| 精品视频一区三区九区| 精品在线视频观看| 久久久国产精品午夜一区ai换脸| 国产无遮挡猛进猛出免费软件| 国自产拍偷拍福利精品免费一| 日本亚洲欧洲精品| 99re8这里有精品热视频免费| 日韩免费在线视频| 性欧美1819sex性高清大胸| 亚洲欧美中文字幕在线一区| 亚洲国产成人在线观看| 在线观看免费亚洲| www..com国产| 亚洲欧美精品午睡沙发| 九色91popny| 一区在线视频| 中文字幕久久综合| 亚洲人挤奶视频| 欧美亚州一区二区三区| 精品黄色免费中文电影在线播放| 亚洲第一中文字幕在线观看| 亚洲综合一区中| 色综合久久综合| 国产一级片播放| 亚洲免费观看高清完整版在线| 三上悠亚ssⅰn939无码播放| 丁香婷婷深情五月亚洲| 日韩av加勒比| 免费在线观看精品| 2022亚洲天堂| 亚洲美女一区| 精品人妻人人做人人爽| 天天做天天爱综合| 日韩影视精品| 妖精视频一区二区三区| 国产福利久久| 77成人影视| 97se国产在线视频| 亚洲91在线| 国产免费一区二区三区在线观看| 国产伦精品一区二区三区视频金莲| 久久久欧美精品| 免费影视亚洲| 欧美黑人狂野猛交老妇| 中文字幕在线观看播放| 日韩视频一区在线| 婷婷成人激情| 中文字幕亚洲专区| 日本高清在线观看wwwww色| 国产午夜精品全部视频在线播放 | 亚洲一区 中文字幕| 欧美怡红院视频| 波多野结衣视频网址| 欧美国产国产综合| 一区二区黄色片| 国产午夜精品一区二区三区嫩草| 国产精品无码网站| 2020日本不卡一区二区视频| 国精产品一区一区三区免费视频| 97久久超碰国产精品| 黄色a一级视频| 久久久久国产免费免费| 国产伦精品一区二区三区视频女| 国产日韩欧美在线一区| 91麻豆精品国产91久久综合| 欧美国产精品一区二区三区| 久久日免费视频| 国产精品二三区| 中文字幕人妻一区二| 一区二区三区在线观看国产 | 精品日韩美女的视频高清| 日韩在线视频免费播放| 日本高清不卡在线观看| 最新中文字幕第一页| 欧美妇女性影城| 亚洲成人黄色片| 国产视频一区在线| sese一区| 欧美精品做受xxx性少妇| 鲁鲁在线中文| 国产精品九九久久久久久久| 亚洲福利影视| 成人动漫视频在线观看完整版| 国内自拍欧美| 欧美婷婷久久| 四季av在线一区二区三区| 亚洲中文字幕无码一区二区三区| 亚洲激情午夜| 男女无套免费视频网站动漫| 久久99蜜桃精品| 性囗交免费视频观看| 麻豆精品一二三| xxxx在线免费观看| 成人av网站在线观看免费| 中文字幕丰满乱子伦无码专区| 国产精品嫩草99a| 久久免费在线观看视频| 色婷婷综合久久久久中文一区二区| 国产精品久久久久久久久久久久久久久久 | www.日本久久久久com.| 成人av影院在线观看| 国产精品电影网| 成人h动漫免费观看网站| 亚洲mv在线看| 亚洲国产日本| www.久久久久久久久久久| 2023国产精品视频| 国产高潮国产高潮久久久91| 色综合一区二区三区| 性生交大片免费看女人按摩| 国产一区二区av| 不卡视频观看| 亚洲一区亚洲二区亚洲三区| 激情综合网站| 麻豆tv在线播放| 国产一区二区毛片| 国产中年熟女高潮大集合| 亚洲一区二区三区不卡国产欧美| 亚洲视频在线观看免费视频| 日韩av在线导航| 影院在线观看全集免费观看| 国产精品成人va在线观看| 精品丝袜久久| av影院在线播放| 精品一区二区日韩| 日本一级免费视频| 欧美日韩激情视频| 亚洲爱爱综合网| 久久99久久99精品免观看粉嫩| 岛国精品在线| 涩涩日韩在线| 久久久久久夜| 中文字幕5566| 午夜视频在线观看一区二区| 亚洲国产精品suv| 成年人精品视频| 91麻豆精品一二三区在线| 日日夜夜精品网站| 久久久久一区| 亚洲综合色一区| 一本久道中文字幕精品亚洲嫩| 欧美 日韩 国产 在线| 久操成人在线视频| 亚洲天堂中文字幕在线观看| 欧美三级午夜理伦三级老人| 久久精品国产99国产| 天堂资源在线视频| 欧美偷拍一区二区| 亚洲视频tv| 国产有码在线一区二区视频| 日韩在线观看电影完整版高清免费悬疑悬疑| 91av在线免费播放| 久久久久久久性| 99re国产在线| 中文字幕在线亚洲| 国产精品高潮久久| 在线亚洲美日韩| 国产乱理伦片在线观看夜一区| 国产成人综合在线视频| 欧美一级免费大片| 国产一线二线在线观看| 国产精品日韩高清| 美女毛片一区二区三区四区| 欧美日韩在线中文| 久久精品视频网| 中文字幕理论片| 日韩中文字幕在线视频播放| 亚洲日日夜夜| 99视频精品全部免费看| 国产白丝精品91爽爽久久| 亚洲一区二区91| 精品视频中文字幕| 国模冰冰炮一区二区| 一区二区三区在线视频111| 国产乱色国产精品免费视频| 久久久久久久国产视频| 亚洲国产精品专区久久| 欧美大胆成人| 亚洲午夜精品一区二区三区| 韩国三级中文字幕hd久久精品| 免费在线观看黄视频| 婷婷开心久久网| 欧美黄色小说| 国产精品尤物福利片在线观看| 亚洲h色精品| 在线看黄色的网站| 色综合天天天天做夜夜夜夜做| 色欧美激情视频在线| 国产精品乱码视频| 日韩国产精品久久久久久亚洲| 男人av资源站| 日韩精品视频在线播放| 免费在线成人激情电影| 毛片av在线播放| 久久久精品黄色| www.97av| 国产精品黄页免费高清在线观看| 综合在线一区| 五月天综合视频| 欧美sm美女调教| 69堂精品视频在线播放| 免费特级黄色片| 国产69精品久久777的优势| 久久久久久不卡| 欧美高清视频免费观看| 欧美日韩中文一区二区|