当前位置: 首页 > article >正文

点赞系统设计(微服务)

点赞业务是一个常见的社交功能,它允许用户对其他用户的内容(如帖子、评论、图片等)表示喜欢或支持。在设计点赞业务时,需要考虑以下几个方面:

一、业务需求

点赞业务需要满足以下特性:

  1. 通用:点赞业务在设计的时候不要与业务系统耦合,必须同时支持不同业务的点赞功能。
  2. 独立:点赞功能是独立系统,并且不依赖其它服务,这样才具备可迁移性。
  3. 并发:一些热点业务点赞会很多,所以点赞功能必须支持高并发。
  4. 安全:要做好并发安全控制,避免重复点赞。
二、实现思路

为了保证安全,避免重复点赞,我们需要保存每一次点赞记录。同时,因为业务方经常需要根据点赞数量排序,因此每个业务的点赞数量也需要记录下来。

三、技术架构

在实现点赞业务时,主要用到了以下技术和工具:

  • 该点赞业务实现通过微服务架构、数据库存储、Redis 缓存、消息队列和定时任务等技术,实现了通用、独立、高并发和安全的点赞功能。
  • 通过 Nacos 进行服务的配置管理和服务发现,确保服务的可扩展性和配置的灵活性。
  • 采用 RabbitMQ 进行消息的异步传递,实现了点赞操作和点赞数更新的解耦,同时使用 Feign 实现服务间通信,方便不同服务调用点赞服务。
  • 利用 Redis 优化高并发读写操作,通过定时任务定期将 Redis 中的点赞数同步到数据库,确保数据的最终一致性。
四、数据库表设计

点赞的数据结构分两部分,一是点赞记录,二是与业务关联的点赞数。点赞数与具体业务表关联在一起记录,比如互动问答的点赞,就在问答表中记录点赞数。学员笔记点赞,自然是在笔记表中记录点赞数。

点赞表设计如下:

create database tj_remark;
use tj_remark;

CREATE TABLE IF NOT EXISTS `liked_record` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键id',
    `user_id` bigint NOT NULL COMMENT '用户id',
    `biz_id` bigint NOT NULL COMMENT '点赞的业务id',
    `biz_type` VARCHAR(16) NOT NULL COMMENT '点赞的业务类型: qa-回答 note-笔记',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (`id`),
    UNIQUE KEY `idx_biz_user` (`biz_id`,`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='点赞记录表';
五、微服务设计:

模块创建:创建一个独立的微服务来处理点赞业务。
依赖管理:添加必要的依赖,如数据库驱动、Redis、MQ等。
配置文件:配置服务的端口、数据库连接、缓存配置、消息队列配置等。
启动类:创建启动类,配置服务的基本信息和扫描路径。
代码生成:使用代码生成工具生成基本的代码框架和数据库操作类。

六、接口设计:
  1. 点赞/取消点赞接口
    • 接口路径/like
    • 请求方法POST
    • 请求参数
      • bizId:业务ID,标识被点赞的对象
      • bizType:业务类型,如qa表示问答,note表示笔记等
    • 响应:无返回值,200表示成功
  2. 查询是否点赞接口
    • 接口路径/isLiked
    • 请求方法GET
    • 请求参数
      • bizId:业务ID,标识被查询的对象
      • bizType:业务类型,如qa表示问答,note表示笔记等
    • 响应
      • liked:布尔值,表示用户是否已经点赞该对象

这些接口将满足用户点赞和查询点赞状态的需求。

七、业务流程

我们先梳理一下点赞业务的几点需求:

  • 用户不能重复点赞
  • 点赞就新增一条点赞记录,取消点赞就删除记录
  • 点赞数由具体的业务方保存,需要通知业务方更新点赞数

由于业务方的类型很多,比如互动问答、笔记、课程等,所以通知方式必须是低耦合的,这里建议使用MQ来实现。

当点赞或取消点赞后,点赞数发生变化,我们就发送MQ通知。整体业务流程如图:

新增点赞功能实现

● 逻辑说明:
○ 接收 LikeRecordFormDTO 作为请求参数,包含业务信息。
○ addLikeRecord 方法根据 recordDTO 的 liked 属性判断是点赞还是取消点赞操作,调用 liked 或 unliked 方法。
○ liked 方法先检查用户是否已点赞,若未点赞则保存点赞记录;unliked 方法则删除用户的点赞记录。
○ 操作成功后,通过 RabbitMqHelper 发送 MQ 消息,通知其他服务点赞数发生了变化,消息发送到指定的交换器和路由键,内容包含业务 id 和点赞次数。

监听点赞数变更

● 在相关业务服务(如 tj-learning)中添加 MQ 监听器,监听点赞数变更的消息并更新数据库。
● 逻辑说明:
○ 使用 @RabbitListener 监听特定交换器和路由键的消息。
○ 收到消息后,将消息中的点赞数更新到相应业务表中,如 InteractionReply 表。

查询点赞状态功能实现

● 逻辑说明:
○ 接收一个业务 id 列表作为参数。
○ 通过 lambdaQuery 查询用户对这些业务是否已点赞。
○ 最终将用户已点赞的业务 id 以集合形式返回。

暴露 Feign 接口

在 tj-api 模块中定义 RemarkClient 作为 Feign 客户端,用于其他微服务调用点赞服务:

@FeignClient(value = "remark-service", fallbackFactory = RemarkClientFallback.class)
public interface RemarkClient {
    //批量查询我的点赞状态
    @GetMapping("/likes/list")
    Set<Long> getLikedIds(@RequestParam("bizIds") List<Long> bizIds);
}

同时定义 RemarkClientFallback 作为服务降级处理:

@Slf4j
public class RemarkClientFallback implements FallbackFactory<RemarkClient> {
    @Override
    public RemarkClient create(Throwable cause) {
        log.error("查询点赞服务异常", cause);
        return new RemarkClient() {
            @Override
            public Set<Long> getLikedIds(List<Long> bizIds) {
                return null;
            }
        };
    }
}
调用

在查询回复或者评论的时候远程调用点赞微服务查询是否点过赞

八、优化思路

高并发读的优化:1. 优化SQL和代码 2. 添加缓存

高并发写的优化:1. 优化SQL和代码 2. 变同步写为异步写 3. 合并写请求

  • 引入 Redis 缓存 ○ 使用 Redis 存储点赞记录和点赞数,以应对高并发问题。
    ○ 存储点赞记录使用 Set 数据结构,存储点赞数使用 ZSet 数据结构。
    ○ 点赞操作(addLikeRecord)通过 Redis 的 SADD 或 SREM 命令操作点赞记录,统计点赞数使用 SCARD 命令。

  • 使用定时任务 ○ 创建定时任务类 LikedTimesTask,使用 @XxlJob 注解,调用 readLikedTimesAndSendMQ 方法。
    ○ 该方法从 Redis 中读取点赞数,转换数据后发送 MQ 消息,更新其他服务的点赞数。
    • 该方法从 Redis 中读取点赞数,转换数据后发送 MQ 消息,更新其他服务的点赞数。
@Component
@Slf4j
public class LikedTimesTask {
    @Autowired
    private ILikedRecordService likedRecordService;

    @XxlJob("checkLikedTimes")
    public ReturnT<String> checkLikedTime(String param) {
        log.info("开始同步点赞次数");
        likedRecordService.readLikedTimesAndSendMQ();
        return ReturnT.SUCCESS;
    }
}

http://www.kler.cn/a/501919.html

相关文章:

  • Jira用例自动去除summary重复用例
  • 腾讯云AI代码助手编程挑战赛-智能聊天助手
  • 三 BH1750 光感驱动调试1
  • mysql的mvcc理解
  • Copula算法原理和R语言股市收益率相依性可视化分析
  • JAVA之单例模式
  • HarmonyOS中实现TabBar(相当于Android中的TabLayout+ViewPager)
  • USA-Entrepreneur-20240708-Business/Unusual
  • Kotlin 循环语句详解
  • 数字证书管理服务
  • 浅谈云计算01 | 云计算服务的特点
  • 【MySQL基础篇】十三、用户与权限管理
  • Jmeter随机参数各种搭配
  • WebScoket-服务器客户端双向通信
  • C# MS SQL Server、Oracle和MySQL
  • C# OpenCV机器视觉:二维码识别
  • 云原生安全风险分析
  • docker的学习
  • 单元测试流程
  • 传统数据湖和数据仓库的“中心化瓶颈”
  • [人工智能自学] Python包学习-pandas
  • Nginx防止点击劫持:X-Frame-Options
  • 【IDEA版本升级JDK21报错方法引用无效 找不到符号】
  • 【Ubuntu与Linux操作系统:三、用户与组管理】
  • 【Linux】深刻理解软硬链接
  • KylinV10安装CDH6.3.1