|
|
51CTO旗下网站
|
|
移动端

微信亿级在线点赞系统,用Redis如何实现?

点赞功能大家都不会陌生,像微信这样的社交产品中都有,但别看功能小,想要做好需要考虑的东西还挺多的,如海量数据的分布式存储、分布式缓存、多 IDC 的数据一致性、访问路由到机房的算法等等。

作者:solocoder来源:掘金|2019-11-28 09:54

点赞功能大家都不会陌生,像微信这样的社交产品中都有,但别看功能小,想要做好需要考虑的东西还挺多的,如海量数据的分布式存储、分布式缓存、多 IDC 的数据一致性、访问路由到机房的算法等等。


图片来 Pexels

本文介绍大型社交平台点赞系统的设计思路,基于 Spring Cloud,用户发起点赞、取消点赞后先存入 Redis 中,再每隔两小时从 Redis 读取点赞数据写入数据库中做持久化存储。

点赞、取消点赞是高频次的操作,若每次都读写数据库,大量的操作会影响数据库性能,所以需要做缓存。

至于多久从 Redis 取一次数据存到数据库中,根据项目的实际情况定吧,我是暂时设了两个小时。

项目需求需要查看都谁点赞了,所以要存储每个点赞的点赞人、被点赞人,不能简单的做计数。

文章分四部分介绍:

  • Redis 缓存设计及实现
  • 数据库设计
  • 数据库操作
  • 开启定时任务持久化存储到数据库

Redis 缓存设计及实现

Redis 安装及运行

Redis 安装请自行查阅相关教程。

说下Docker 安装运行 Redis:

  1. docker run -d -p 6379:6379 redis:4.0.8 

如果已经安装了 Redis,打开命令行,输入启动 Redis 的命令:

  1. redis-server 

Redis 与 Spring Boot 项目的整合

①在 pom.xml 中引入依赖:

  1. <dependency> 
  2.     <groupId>org.springframework.boot</groupId> 
  3.     <artifactId>spring-boot-starter-data-redis</artifactId> 
  4. </dependency> 

②在启动类上添加注释 @EnableCaching:

  1. @SpringBootApplication 
  2. @EnableDiscoveryClient 
  3. @EnableSwagger2 
  4. @EnableFeignClients(basePackages = "com.solo.coderiver.project.client"
  5. @EnableCaching 
  6. public class UserApplication { 
  7.  
  8.     public static void main(String[] args) { 
  9.         SpringApplication.run(UserApplication.class, args); 
  10.     } 

③编写 Redis 配置类 RedisConfig:

  1. import com.fasterxml.jackson.annotation.JsonAutoDetect; 
  2. import com.fasterxml.jackson.annotation.PropertyAccessor; 
  3. import com.fasterxml.jackson.databind.ObjectMapper; 
  4. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 
  5. import org.springframework.context.annotation.Bean; 
  6. import org.springframework.context.annotation.Configuration; 
  7. import org.springframework.data.redis.connection.RedisConnectionFactory; 
  8. import org.springframework.data.redis.core.RedisTemplate; 
  9. import org.springframework.data.redis.core.StringRedisTemplate; 
  10. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 
  11.  
  12. import java.net.UnknownHostException; 
  13.  
  14.  
  15. @Configuration 
  16. public class RedisConfig { 
  17.  
  18.     @Bean 
  19.     @ConditionalOnMissingBean(name = "redisTemplate"
  20.     public RedisTemplate<String, Object> redisTemplate( 
  21.             RedisConnectionFactory redisConnectionFactory) 
  22.             throws UnknownHostException { 
  23.  
  24.         Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class); 
  25.         ObjectMapper om = new ObjectMapper(); 
  26.         om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 
  27.         om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 
  28.         jackson2JsonRedisSerializer.setObjectMapper(om); 
  29.  
  30.         RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); 
  31.         template.setConnectionFactory(redisConnectionFactory); 
  32.         template.setKeySerializer(jackson2JsonRedisSerializer); 
  33.         template.setValueSerializer(jackson2JsonRedisSerializer); 
  34.         template.setHashKeySerializer(jackson2JsonRedisSerializer); 
  35.         template.setHashValueSerializer(jackson2JsonRedisSerializer); 
  36.         template.afterPropertiesSet(); 
  37.         return template; 
  38.     } 
  39.  
  40.  
  41.     @Bean 
  42.     @ConditionalOnMissingBean(StringRedisTemplate.class) 
  43.     public StringRedisTemplate stringRedisTemplate( 
  44.             RedisConnectionFactory redisConnectionFactory) 
  45.             throws UnknownHostException { 
  46.         StringRedisTemplate template = new StringRedisTemplate(); 
  47.         template.setConnectionFactory(redisConnectionFactory); 
  48.         return template; 
  49.     } 

至此 Redis 在 Spring Boot 项目中的配置已经完成,可以愉快的使用了。

Redis 的数据结构类型

Redis 可以存储键与 5 种不同数据结构类型之间的映射,这5种数据结构类型分别为 String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)。

下面来对这 5 种数据结构类型作简单的介绍:

点赞数据在 Redis 中的存储格式

用 Redis 存储两种数据:

  • 一种是记录点赞人、被点赞人、点赞状态的数据。
  • 另一种是每个用户被点赞了多少次,做个简单的计数。

由于需要记录点赞人和被点赞人,还有点赞状态(点赞、取消点赞),还要固定时间间隔取出 Redis 中所有点赞数据,分析了下 Redis 数据格式中 Hash 最合适。

因为 Hash 里的数据都是存在一个键里,可以通过这个键很方便的把所有的点赞数据都取出。

这个键里面的数据还可以存成键值对的形式,方便存入点赞人、被点赞人和点赞状态。

设点赞人的 id 为 likedPostId,被点赞人的 id 为 likedUserId ,点赞时状态为 1,取消点赞状态为 0。

将点赞人 id 和被点赞人 id 作为键,两个 id 中间用 :: 隔开,点赞状态作为值。

所以如果用户点赞,存储的键为:likedUserId::likedPostId,对应的值为 1 。

取消点赞,存储的键为:likedUserId::likedPostId,对应的值为 0 。取数据时把键用 :: 切开就得到了两个id,也很方便。

在可视化工具 RDM 中看到的是这样子:

操作 Redis

将具体操作方法封装到了 RedisService 接口里:

①RedisService.java

  1. import com.solo.coderiver.user.dataobject.UserLike; 
  2. import com.solo.coderiver.user.dto.LikedCountDTO; 
  3.  
  4. import java.util.List; 
  5.  
  6. public interface RedisService { 
  7.  
  8.     /** 
  9.      * 点赞。状态为1 
  10.      * @param likedUserId 
  11.      * @param likedPostId 
  12.      */ 
  13.     void saveLiked2Redis(String likedUserId, String likedPostId); 
  14.  
  15.     /** 
  16.      * 取消点赞。将状态改变为0 
  17.      * @param likedUserId 
  18.      * @param likedPostId 
  19.      */ 
  20.     void unlikeFromRedis(String likedUserId, String likedPostId); 
  21.  
  22.     /** 
  23.      * 从Redis中删除一条点赞数据 
  24.      * @param likedUserId 
  25.      * @param likedPostId 
  26.      */ 
  27.     void deleteLikedFromRedis(String likedUserId, String likedPostId); 
  28.  
  29.     /** 
  30.      * 该用户的点赞数加1 
  31.      * @param likedUserId 
  32.      */ 
  33.     void incrementLikedCount(String likedUserId); 
  34.  
  35.     /** 
  36.      * 该用户的点赞数减1 
  37.      * @param likedUserId 
  38.      */ 
  39.     void decrementLikedCount(String likedUserId); 
  40.  
  41.     /** 
  42.      * 获取Redis中存储的所有点赞数据 
  43.      * @return 
  44.      */ 
  45.     List<UserLike> getLikedDataFromRedis(); 
  46.  
  47.     /** 
  48.      * 获取Redis中存储的所有点赞数量 
  49.      * @return 
  50.      */ 
  51.     List<LikedCountDTO> getLikedCountFromRedis(); 
  52.  

②实现类 RedisServiceImpl.java

  1. import com.solo.coderiver.user.dataobject.UserLike; 
  2. import com.solo.coderiver.user.dto.LikedCountDTO; 
  3. import com.solo.coderiver.user.enums.LikedStatusEnum; 
  4. import com.solo.coderiver.user.service.LikedService; 
  5. import com.solo.coderiver.user.service.RedisService; 
  6. import com.solo.coderiver.user.utils.RedisKeyUtils; 
  7. import lombok.extern.slf4j.Slf4j; 
  8. import org.springframework.beans.factory.annotation.Autowired; 
  9. import org.springframework.data.redis.core.Cursor
  10. import org.springframework.data.redis.core.RedisTemplate; 
  11. import org.springframework.data.redis.core.ScanOptions; 
  12. import org.springframework.stereotype.Service; 
  13.  
  14. import java.util.ArrayList; 
  15. import java.util.List; 
  16. import java.util.Map; 
  17.  
  18. @Service 
  19. @Slf4j 
  20. public class RedisServiceImpl implements RedisService { 
  21.  
  22.     @Autowired 
  23.     RedisTemplate redisTemplate; 
  24.  
  25.     @Autowired 
  26.     LikedService likedService; 
  27.  
  28.     @Override 
  29.     public void saveLiked2Redis(String likedUserId, String likedPostId) { 
  30.         String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId); 
  31.         redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED, key, LikedStatusEnum.LIKE.getCode()); 
  32.     } 
  33.  
  34.     @Override 
  35.     public void unlikeFromRedis(String likedUserId, String likedPostId) { 
  36.         String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId); 
  37.         redisTemplate.opsForHash().put(RedisKeyUtils.MAP_KEY_USER_LIKED, key, LikedStatusEnum.UNLIKE.getCode()); 
  38.     } 
  39.  
  40.     @Override 
  41.     public void deleteLikedFromRedis(String likedUserId, String likedPostId) { 
  42.         String key = RedisKeyUtils.getLikedKey(likedUserId, likedPostId); 
  43.         redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key); 
  44.     } 
  45.  
  46.     @Override 
  47.     public void incrementLikedCount(String likedUserId) { 
  48.         redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, likedUserId, 1); 
  49.     } 
  50.  
  51.     @Override 
  52.     public void decrementLikedCount(String likedUserId) { 
  53.         redisTemplate.opsForHash().increment(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, likedUserId, -1); 
  54.     } 
  55.  
  56.     @Override 
  57.     public List<UserLike> getLikedDataFromRedis() { 
  58.         Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED, ScanOptions.NONE); 
  59.         List<UserLike> list = new ArrayList<>(); 
  60.         while (cursor.hasNext()){ 
  61.             Map.Entry<Object, Object> entry = cursor.next(); 
  62.             String key = (String) entry.getKey(); 
  63.             //分离出 likedUserId,likedPostId 
  64.             String[] split = key.split("::"); 
  65.             String likedUserId = split[0]; 
  66.             String likedPostId = split[1]; 
  67.             Integer value = (Integer) entry.getValue(); 
  68.  
  69.             //组装成 UserLike 对象 
  70.             UserLike userLike = new UserLike(likedUserId, likedPostId, value); 
  71.             list.add(userLike); 
  72.  
  73.             //存到 list 后从 Redis 中删除 
  74.             redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED, key); 
  75.         } 
  76.  
  77.         return list; 
  78.     } 
  79.  
  80.     @Override 
  81.     public List<LikedCountDTO> getLikedCountFromRedis() { 
  82.         Cursor<Map.Entry<Object, Object>> cursor = redisTemplate.opsForHash().scan(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, ScanOptions.NONE); 
  83.         List<LikedCountDTO> list = new ArrayList<>(); 
  84.         while (cursor.hasNext()){ 
  85.             Map.Entry<Object, Object> map = cursor.next(); 
  86.             //将点赞数量存储在 LikedCountDT 
  87.             String key = (String)map.getKey(); 
  88.             LikedCountDTO dto = new LikedCountDTO(key, (Integer) map.getValue()); 
  89.             list.add(dto); 
  90.             //从Redis中删除这条记录 
  91.             redisTemplate.opsForHash().delete(RedisKeyUtils.MAP_KEY_USER_LIKED_COUNT, key); 
  92.         } 
  93.         return list; 
  94.     } 

③用到的工具类和枚举类

RedisKeyUtils,用于根据一定规则生成 key:

  1. public class RedisKeyUtils { 
  2.  
  3.     //保存用户点赞数据的key 
  4.     public static final String MAP_KEY_USER_LIKED = "MAP_USER_LIKED"
  5.     //保存用户被点赞数量的key 
  6.     public static final String MAP_KEY_USER_LIKED_COUNT = "MAP_USER_LIKED_COUNT"
  7.  
  8.     /** 
  9.      * 拼接被点赞的用户id和点赞的人的id作为key。格式 222222::333333 
  10.      * @param likedUserId 被点赞的人id 
  11.      * @param likedPostId 点赞的人的id 
  12.      * @return 
  13.      */ 
  14.     public static String getLikedKey(String likedUserId, String likedPostId){ 
  15.         StringBuilder builder = new StringBuilder(); 
  16.         builder.append(likedUserId); 
  17.         builder.append("::"); 
  18.         builder.append(likedPostId); 
  19.         return builder.toString(); 
  20.     } 

LikedStatusEnum 用户点赞状态的枚举类:

  1. package com.solo.coderiver.user.enums; 
  2.  
  3. import lombok.Getter; 
  4.  
  5. /** 
  6.  * 用户点赞的状态 
  7.  */ 
  8. @Getter 
  9. public enum LikedStatusEnum { 
  10.     LIKE(1, "点赞"), 
  11.     UNLIKE(0, "取消点赞/未点赞"), 
  12.     ; 
  13.  
  14.     private Integer code; 
  15.  
  16.     private String msg; 
  17.  
  18.     LikedStatusEnum(Integer code, String msg) { 
  19.         this.code = code; 
  20.         this.msg = msg; 
  21.     } 

数据库设计

数据库表中至少要包含三个字段:被点赞用户 id,点赞用户 id,点赞状态。再加上主键 id,创建时间,修改时间就行了。

建表语句:

  1. create table `user_like`( 
  2.     `id` int not null auto_increment, 
  3.     `liked_user_id` varchar(32) not null comment '被点赞的用户id'
  4.     `liked_post_id` varchar(32) not null comment '点赞的用户id'
  5.     `status` tinyint(1) default '1' comment '点赞状态,0取消,1点赞'
  6.     `create_time` timestamp not null default current_timestamp comment '创建时间'
  7.   `update_time` timestamp not null default current_timestamp on update current_timestamp comment '修改时间'
  8.     primary key(`id`), 
  9.     INDEX `liked_user_id`(`liked_user_id`), 
  10.     INDEX `liked_post_id`(`liked_post_id`) 
  11. ) comment '用户点赞表'

对应的对象 UserLike:

  1. import com.solo.coderiver.user.enums.LikedStatusEnum; 
  2. import lombok.Data; 
  3.  
  4. import javax.persistence.Entity; 
  5. import javax.persistence.GeneratedValue; 
  6. import javax.persistence.GenerationType; 
  7. import javax.persistence.Id; 
  8.  
  9. /** 
  10.  * 用户点赞表 
  11.  */ 
  12. @Entity 
  13. @Data 
  14. public class UserLike { 
  15.  
  16.     //主键id 
  17.     @Id 
  18.     @GeneratedValue(strategy = GenerationType.IDENTITY) 
  19.     private Integer id; 
  20.  
  21.     //被点赞的用户的id 
  22.     private String likedUserId; 
  23.  
  24.     //点赞的用户的id 
  25.     private String likedPostId; 
  26.  
  27.     //点赞的状态.默认未点赞 
  28.     private Integer status = LikedStatusEnum.UNLIKE.getCode(); 
  29.  
  30.     public UserLike() { 
  31.     } 
  32.  
  33.     public UserLike(String likedUserId, String likedPostId, Integer status) { 
  34.         this.likedUserId = likedUserId; 
  35.         this.likedPostId = likedPostId; 
  36.         this.status = status; 
  37.     } 

数据库操作

操作数据库同样封装在接口中:

①LikedService

  1. import com.solo.coderiver.user.dataobject.UserLike; 
  2. import org.springframework.data.domain.Page; 
  3. import org.springframework.data.domain.Pageable; 
  4.  
  5. import java.util.List; 
  6.  
  7. public interface LikedService { 
  8.  
  9.     /** 
  10.      * 保存点赞记录 
  11.      * @param userLike 
  12.      * @return 
  13.      */ 
  14.     UserLike save(UserLike userLike); 
  15.  
  16.     /** 
  17.      * 批量保存或修改 
  18.      * @param list 
  19.      */ 
  20.     List<UserLike> saveAll(List<UserLike> list); 
  21.  
  22.  
  23.     /** 
  24.      * 根据被点赞人的id查询点赞列表(即查询都谁给这个人点赞过) 
  25.      * @param likedUserId 被点赞人的id 
  26.      * @param pageable 
  27.      * @return 
  28.      */ 
  29.     Page<UserLike> getLikedListByLikedUserId(String likedUserId, Pageable pageable); 
  30.  
  31.     /** 
  32.      * 根据点赞人的id查询点赞列表(即查询这个人都给谁点赞过) 
  33.      * @param likedPostId 
  34.      * @param pageable 
  35.      * @return 
  36.      */ 
  37.     Page<UserLike> getLikedListByLikedPostId(String likedPostId, Pageable pageable); 
  38.  
  39.     /** 
  40.      * 通过被点赞人和点赞人id查询是否存在点赞记录 
  41.      * @param likedUserId 
  42.      * @param likedPostId 
  43.      * @return 
  44.      */ 
  45.     UserLike getByLikedUserIdAndLikedPostId(String likedUserId, String likedPostId); 
  46.  
  47.     /** 
  48.      * 将Redis里的点赞数据存入数据库中 
  49.      */ 
  50.     void transLikedFromRedis2DB(); 
  51.  
  52.     /** 
  53.      * 将Redis中的点赞数量数据存入数据库 
  54.      */ 
  55.     void transLikedCountFromRedis2DB(); 
  56.  

②LikedServiceImpl 实现类

  1. import com.solo.coderiver.user.dataobject.UserInfo; 
  2. import com.solo.coderiver.user.dataobject.UserLike; 
  3. import com.solo.coderiver.user.dto.LikedCountDTO; 
  4. import com.solo.coderiver.user.enums.LikedStatusEnum; 
  5. import com.solo.coderiver.user.repository.UserLikeRepository; 
  6. import com.solo.coderiver.user.service.LikedService; 
  7. import com.solo.coderiver.user.service.RedisService; 
  8. import com.solo.coderiver.user.service.UserService; 
  9. import lombok.extern.slf4j.Slf4j; 
  10. import org.springframework.beans.factory.annotation.Autowired; 
  11. import org.springframework.data.domain.Page; 
  12. import org.springframework.data.domain.Pageable; 
  13. import org.springframework.stereotype.Service; 
  14. import org.springframework.transaction.annotation.Transactional; 
  15.  
  16. import java.util.List; 
  17.  
  18. @Service 
  19. @Slf4j 
  20. public class LikedServiceImpl implements LikedService { 
  21.  
  22.     @Autowired 
  23.     UserLikeRepository likeRepository; 
  24.  
  25.     @Autowired 
  26.     RedisService redisService; 
  27.  
  28.     @Autowired 
  29.     UserService userService; 
  30.  
  31.     @Override 
  32.     @Transactional 
  33.     public UserLike save(UserLike userLike) { 
  34.         return likeRepository.save(userLike); 
  35.     } 
  36.  
  37.     @Override 
  38.     @Transactional 
  39.     public List<UserLike> saveAll(List<UserLike> list) { 
  40.         return likeRepository.saveAll(list); 
  41.     } 
  42.  
  43.     @Override 
  44.     public Page<UserLike> getLikedListByLikedUserId(String likedUserId, Pageable pageable) { 
  45.         return likeRepository.findByLikedUserIdAndStatus(likedUserId, LikedStatusEnum.LIKE.getCode(), pageable); 
  46.     } 
  47.  
  48.     @Override 
  49.     public Page<UserLike> getLikedListByLikedPostId(String likedPostId, Pageable pageable) { 
  50.         return likeRepository.findByLikedPostIdAndStatus(likedPostId, LikedStatusEnum.LIKE.getCode(), pageable); 
  51.     } 
  52.  
  53.     @Override 
  54.     public UserLike getByLikedUserIdAndLikedPostId(String likedUserId, String likedPostId) { 
  55.         return likeRepository.findByLikedUserIdAndLikedPostId(likedUserId, likedPostId); 
  56.     } 
  57.  
  58.     @Override 
  59.     @Transactional 
  60.     public void transLikedFromRedis2DB() { 
  61.         List<UserLike> list = redisService.getLikedDataFromRedis(); 
  62.         for (UserLike like : list) { 
  63.             UserLike ul = getByLikedUserIdAndLikedPostId(like.getLikedUserId(), like.getLikedPostId()); 
  64.             if (ul == null){ 
  65.                 //没有记录,直接存入 
  66.                 save(like); 
  67.             }else
  68.                 //有记录,需要更新 
  69.                 ul.setStatus(like.getStatus()); 
  70.                 save(ul); 
  71.             } 
  72.         } 
  73.     } 
  74.  
  75.     @Override 
  76.     @Transactional 
  77.     public void transLikedCountFromRedis2DB() { 
  78.         List<LikedCountDTO> list = redisService.getLikedCountFromRedis(); 
  79.         for (LikedCountDTO dto : list) { 
  80.             UserInfo user = userService.findById(dto.getId()); 
  81.             //点赞数量属于无关紧要的操作,出错无需抛异常 
  82.             if (user != null){ 
  83.                 Integer likeNum = user.getLikeNum() + dto.getCount(); 
  84.                 user.setLikeNum(likeNum); 
  85.                 //更新点赞数量 
  86.                 userService.updateInfo(user); 
  87.             } 
  88.         } 
  89.     } 

数据库的操作就这些,主要还是增删改查。

开启定时任务持久化存储到数据库

定时任务 Quartz 很强大,就用它了。Quartz 使用步骤如下:

①添加依赖

  1. <dependency> 
  2.     <groupId>org.springframework.boot</groupId> 
  3.     <artifactId>spring-boot-starter-quartz</artifactId> 
  4. </dependency> 

②编写配置文件

  1. package com.solo.coderiver.user.config; 
  2.  
  3. import com.solo.coderiver.user.task.LikeTask; 
  4. import org.quartz.*; 
  5. import org.springframework.context.annotation.Bean; 
  6. import org.springframework.context.annotation.Configuration; 
  7.  
  8. @Configuration 
  9. public class QuartzConfig { 
  10.  
  11.     private static final String LIKE_TASK_IDENTITY = "LikeTaskQuartz"
  12.  
  13.     @Bean 
  14.     public JobDetail quartzDetail(){ 
  15.         return JobBuilder.newJob(LikeTask.class).withIdentity(LIKE_TASK_IDENTITY).storeDurably().build(); 
  16.     } 
  17.  
  18.     @Bean 
  19.     public Trigger quartzTrigger(){ 
  20.         SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule() 
  21. //                .withIntervalInSeconds(10)  //设置时间周期单位秒 
  22.                 .withIntervalInHours(2)  //两个小时执行一次 
  23.                 .repeatForever(); 
  24.         return TriggerBuilder.newTrigger().forJob(quartzDetail()) 
  25.                 .withIdentity(LIKE_TASK_IDENTITY) 
  26.                 .withSchedule(scheduleBuilder) 
  27.                 .build(); 
  28.     } 

③编写执行任务的类继承自 QuartzJobBean

  1. package com.solo.coderiver.user.task; 
  2.  
  3. import com.solo.coderiver.user.service.LikedService; 
  4. import lombok.extern.slf4j.Slf4j; 
  5. import org.apache.commons.lang.time.DateUtils; 
  6. import org.quartz.JobExecutionContext; 
  7. import org.quartz.JobExecutionException; 
  8. import org.springframework.beans.factory.annotation.Autowired; 
  9. import org.springframework.scheduling.quartz.QuartzJobBean; 
  10.  
  11. import java.text.SimpleDateFormat; 
  12. import java.util.Date
  13.  
  14. /** 
  15.  * 点赞的定时任务 
  16.  */ 
  17. @Slf4j 
  18. public class LikeTask extends QuartzJobBean { 
  19.  
  20.     @Autowired 
  21.     LikedService likedService; 
  22.  
  23.     private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
  24.  
  25.     @Override 
  26.     protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException { 
  27.  
  28.         log.info("LikeTask-------- {}", sdf.format(new Date())); 
  29.  
  30.         //将 Redis 里的点赞信息同步到数据库里 
  31.         likedService.transLikedFromRedis2DB(); 
  32.         likedService.transLikedCountFromRedis2DB(); 
  33.     } 

在定时任务中直接调用 LikedService 封装的方法完成数据同步。

以上就是点赞功能的设计与实现,不足之处还请各位大佬多多指教。如有更好的实现方案欢迎在评论区交流。

【编辑推荐】

  1. Redis为什么默认16个数据库?
  2. Redis如何轻松支撑万亿级日访问量?
  3. 详细讲解 Redis 的两种安装部署方式
  4. Redis持久化的几种方式——深入解析RDB
  5. 那些你不得不知的Redis基础类型常用操作、命令
【责任编辑:武晓燕 TEL:(010)68476606】

点赞 0
分享:
大家都在看
猜你喜欢

订阅专栏+更多

骨干网与数据中心建设案例

骨干网与数据中心建设案例

高级网工必会
共20章 | 捷哥CCIE

403人订阅学习

中间件安全防护攻略

中间件安全防护攻略

4类安全防护
共4章 | hack_man

151人订阅学习

CentOS 8 全新学习术

CentOS 8 全新学习术

CentOS 8 正式发布
共16章 | UbuntuServer

291人订阅学习

读 书 +更多

Java编程思想 第4版

本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、Java I...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊

51CTO服务号

51CTO官微