点赞系统设计(微服务)

news2025/1/11 4:20:36

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

一、业务需求

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

  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.coloradmin.cn/o/2274694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C#进阶-在Ubuntu上部署ASP.NET Core Web API应用

随着云计算和容器化技术的普及&#xff0c;Linux 服务器已成为部署 Web 应用程序的主流平台之一。ASP.NET Core 作为一个跨平台、高性能的框架&#xff0c;非常适合在 Linux 环境中运行。本篇博客将详细介绍如何在 Linux 服务器上部署 ASP.NET Core Web API 应用&#xff0c;包…

设计模式-结构型-桥接模式

1. 什么是桥接模式&#xff1f; 桥接模式&#xff08;Bridge Pattern&#xff09; 是一种结构型设计模式&#xff0c;它旨在将抽象部分与实现部分分离&#xff0c;使它们可以独立变化。通过这种方式&#xff0c;系统可以在抽象和实现两方面进行扩展&#xff0c;而无需相互影响…

python学习笔记—16—数据容器之元组

1. 元组——tuple(元组是一个只读的list) (1) 元组的定义注意&#xff1a;定义单个元素的元组&#xff0c;在元素后面要加上 , (2) 元组也支持嵌套 (3) 下标索引取出元素 (4) 元组的相关操作 1. index——查看元组中某个元素在元组中的位置从左到右第一次出现的位置 t1 (&qu…

基础算法--查找

一、线性枚举 1、线性枚举定义 线性枚举指的就是遍历某个一维数组&#xff08;顺序表&#xff09;的所有元素&#xff0c;找到满足条件的那个元素并且返回&#xff0c;返回值可以是下标&#xff0c;也可以是元素本身。 由于是遍历的&#xff0c;穷举了所有情况&#xff0c;所…

G1垃圾回收器的FullGC

如何确定GarbageFirst回收器发生的是FullGC ? 必须出现FullGC字样才算是FUllGC&#xff0c;例如下图&#xff1a;因为内存分配失败&#xff08;Allocation Failure&#xff09;导致 如果不出现FullGC的字样说明它不是FUllGC&#xff0c;并不像Serial GC、ParallelGC的在老年代…

Golang的代码压缩技术应用案例分析与研究实践

Golang的代码压缩技术应用案例分析与研究实践 一、介绍 是一种具有强大性能和便捷开发特性的编程语言&#xff0c;除了其优秀的语法和标准库外&#xff0c;它还拥有很多高级特性&#xff0c;其中之一就是代码压缩技术。本文将从常见的Golang代码压缩技术应用案例出发&#xff0…

【Uniapp-Vue3】image媒体组件属性

如果我们想要在页面上展示图片就需要使用到image标签。 这部分最重要的是图片的裁剪&#xff0c;图片的裁剪和缩放属性&#xff1a; mode 图片裁剪、缩放的模式 默认值是scaleToFill 我将用两张图片对属性进行演示&#xff0c;一张是pic1.jpg&#xff08;宽更长&#xf…

【网络协议】交换机概念与配置(第一部分)

概述 本文将探讨交换机的概念以及交换机的基础配置&#xff0c;并以此引入对 VLAN 的讨论。 文章目录 概述CSMA/CD以太网通信单播&#xff08;Unicast&#xff09;多播&#xff08;Multicast&#xff09;广播&#xff08;Broadcast&#xff09; MAC 地址以太网中的双工设置半双…

oracle位运算、左移右移、标签算法等

文章目录 位运算基础与或非同或同或应用场景 异或异或应用场景 什么是真值表 oracle基础函数创建bitor(按位或)函数bitnot(按位非)函数bitxor(按位异或)函数左移函数BITSHIFT()函数(实测不可用&#xff0c;废弃掉该方案)右移函数(略&#xff0c;有此场景吗?) 实际应用资质字典…

(五)ROS通信编程——参数服务器

前言 参数服务器在ROS中主要用于实现不同节点之间的数据共享&#xff08;P2P&#xff09;。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据&#xff0c;关…

《零基础Go语言算法实战》【题目 1-18】切片的反转

《零基础Go语言算法实战》 【题目 1-18】切片的反转 请编写一个名为 reverse 的函数&#xff0c;采用整数切片并在不使用临时切片的情况下将切片反转。 【解答】 可以通过 for 循环交换切片中每个元素的值&#xff0c;使其从左向右滑动。最终&#xff0c;所有元素都将 被反转。…

Elasticsearch:搜索相关性

这里写目录标题 一、相关性的概述二、自定义评分策略1、TF-IDF算法2、BM25算法 三、自定义评分策略1、Index Boost&#xff1a;在索引层面修改相关性2、boosting&#xff1a;修改文档相关性3、negative_boost&#xff1a;降低相关性4、function_score&#xff1a;自定义评分5、…

【C++经典例题】求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a; 期待您的关注 题目描述&#xff1a; 原题链接&#xff1a; 求123...n_牛客题霸_牛客网 (nowcoder.com) 解题思路&#xff1a; …

淺談Cocos2djs逆向

前言 簡單聊一下cocos2djs手遊的逆向&#xff0c;有任何相關想法歡迎和我討論^^ 一些概念 列出一些個人認為比較有用的概念&#xff1a; Cocos遊戲的兩大開發工具分別是CocosCreator和CocosStudio&#xff0c;區別是前者是cocos2djs專用的開發工具&#xff0c;後者則是coco…

概率论与数理统计总复习

复习课本&#xff1a;中科大使用的教辅《概率论和数理统计》缪柏其、张伟平版本 目录 0.部分积分公式 1.容斥原理 2.条件概率 3.全概率公式 4.贝叶斯公式 5.独立性 6.伯努利分布&#xff08;两点分布&#xff09; 7.二项分布 8.帕斯卡分布&#xff08;负二项分布&am…

el-table自定义按钮控制扩展expand

需求&#xff1a;自定义按钮实现表格扩展内容的展开和收起&#xff0c;实现如下&#xff1a; 将type“expand”的表格列的宽度设置为width"1"&#xff0c;让该操作列不展示出来&#xff0c;然后通过ref动态调用组件的内部方法toggleRowExpansion(row, row.expanded)控…

大语言模型训练的数据集从哪里来?

继续上篇文章的内容说说大语言模型预训练的数据集从哪里来以及为什么互联网上的数据已经被耗尽这个说法并不专业&#xff0c;再谈谈大语言模型预训练数据集的优化思路。 1. GPT2使用的数据集是WebText&#xff0c;该数据集大概40GB&#xff0c;由OpenAI创建&#xff0c;主要内…

【C++习题】22.随机链表的复制

文章目录 题目&#xff1a;138. 随机链表的复制 - 力扣&#xff08;LeetCode&#xff09;代码&#xff1a; 题目&#xff1a;138. 随机链表的复制 - 力扣&#xff08;LeetCode&#xff09; 链接&#x1f517;&#xff1a;138. 随机链表的复制 - 力扣&#xff08;LeetCode&…

C# 或 .NetCore 如何使用 NPOI 导出图片到 Excel 文件

今天在本文中&#xff0c;我们将尝试使用NPOI库将图像插入到 Excel 文件的特定位置。请将以下逻辑添加到您的写作方法中&#xff0c;在 Excel 文件中添加图像&#xff08;JPEG、PNG&#xff09;,我已经有一个示例 jpeg 文件 - Read-write-excel-npoi.jpg &#xff0c;我们将尝试…

Photon最新版本PUN 2.29 PREE,在无网的局域网下,无法连接自己搭建的本地服务器

1.图1为官方解答 2.就是加上这一段段代码&#xff1a;PhotonNetwork.NetworkingClient.SerializationProtocol SerializationProtocol.GpBinaryV16; 完美解决 unity 商店最新PUN 2 插件 不能连接 &#xff08;环境为&#xff1a;本地局域网 无外网情况 &#xff09; …