一、业务需求分析
(一)通用点赞系统特性
- 通用性:点赞业务在设计时不应与特定业务系统过度耦合,要能够同时支持不同业务场景下的点赞操作。比如在社交平台、电商平台以及学习平台等,点赞功能都应能无缝接入,且不受业务差异的影响。
- 独立性:点赞功能需作为独立系统存在,不依赖其他服务,以此确保具备良好的可迁移性。无论是从一个技术架构迁移到另一个,还是在不同的项目中复用,都能够轻松实现。
- 高并发支持:热点业务可能会面临大量的点赞请求,系统必须能够高效处理高并发情况,确保在高流量下点赞操作的稳定性和及时性。
- 安全性:要通过有效的并发安全控制机制,防止用户重复点赞,保证点赞数据的准确性和公正性。
(二)点赞功能具体需求
- 新增点赞与取消点赞:用户点击点赞按钮时,首次点击实现点赞功能,按钮状态变为高亮;再次点击则取消点赞,按钮恢复灰色。
- 查询点赞状态:前端在展示内容时,需要根据用户的点赞状态进行不同效果的渲染。例如,在展示回答或评论列表、笔记列表时,要对用户已点赞的内容进行高亮显示。这就要求系统能够支持批量查询多个业务 ID 的点赞状态,以便准确判断哪些内容已被当前用户点赞。
二、实现思路
(一)数据存储策略
- 为保证安全并避免重复点赞,需完整记录每一次点赞行为。因此设计点赞记录表,存储点赞用户、点赞业务 ID、点赞业务类型以及点赞时间等信息。同时,由于业务方常需根据点赞数量进行排序展示,所以在与业务关联紧密的数据表(如互动问答的回答表、学员笔记表等)中记录点赞数。
- 优化数据查询性能,考虑使用缓存技术。例如,将点赞数量等高频查询数据缓存起来,减少对数据库的直接查询操作,提高系统响应速度。
(二)系统架构设计
- 采用微服务架构模式,将点赞系统拆分为独立的微服务,使其职责单一、易于维护和扩展。各个微服务之间通过轻量级的通信机制(如 RESTful API)进行协作,实现点赞功能的各项业务逻辑。
- 引入消息队列(如 RabbitMQ)实现异步通信和解耦。在点赞或取消点赞操作后,通过发送消息通知相关业务方更新点赞数,避免同步调用带来的性能瓶颈,提高系统的整体吞吐量。
(三)并发安全控制
- 在数据库层面,利用唯一约束(如在点赞记录表中,对用户 ID 和业务 ID 的组合设置唯一索引)来防止重复点赞记录的插入。同时,合理使用数据库事务,确保点赞操作的原子性,即点赞或取消点赞操作要么全部成功,要么全部失败。
- 在代码实现中,通过加锁机制(如分布式锁)来控制并发访问。当多个请求同时对同一业务进行点赞操作时,确保只有一个请求能够成功获取锁并执行点赞逻辑,其他请求等待锁释放后再进行处理
二、系统设计
(一)架构设计
- 采用微服务架构,将点赞系统拆分为多个独立的服务,如点赞服务、计数服务、存储服务等。每个服务职责单一,便于开发、维护和扩展。
- 利用消息队列(如 RabbitMQ)实现异步通信,解耦点赞操作与其他业务逻辑,提高系统的响应速度和吞吐量。
(二)数据库设计
- 设计点赞记录表,存储点赞用户、点赞目标、点赞时间等信息。
- 建立目标内容表,包含内容 ID、内容类型、点赞数等字段。
-
id userid biz_id biz_type create_time update_time bigint bigint bigint VARCHAR(16) datetime datetime 主键 id,自增长 用户 id 点赞的业务 id 点赞的业务类型,取值为 qa-回答
或note-
笔记创建时间,默认为当前时间 更新时间,每次更新记录时自动更新为当前时间 主键,自动增长,起始值为 8,步长为 1 非空 非空 非空 非空,默认值为当前时间 非空,默认值为当前时间,且在记录更新时自动更新 -
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`) )
id content_type like_count 内容 ID 内容类型 点赞数 非空 qa-回答 note-笔记 -- 创建目标内容表 CREATE TABLE target_content ( id VARCHAR(50) PRIMARY KEY, content_type VARCHAR(20) NOT NULL, like_count INT DEFAULT 0 );
(三)接口设计
- 提供点赞接口,接收用户的点赞请求,参数包括用户 ID、目标内容 ID 等。
- 设计获取点赞数量接口,根据目标内容 ID 返回相应的点赞数。
- 创建获取用户点赞列表接口,用于查询指定用户点赞过的内容。
三、技术实现
(一)开发语言与框架
- 后端采用 Java 语言,结合 Spring Boot 框架进行开发,利用其强大的生态和便捷的开发特性,快速构建系统的核心功能。
- 前端可根据实际情况选择合适的技术栈,如 Vue.js 或 React,为用户提供良好的交互体验。
(二)高并发处理
- 使用 Redis 作为缓存数据库,将点赞数量等热点数据缓存起来,减少对数据库的频繁查询,提高系统性能。
- 在点赞操作时,通过加锁(如分布式锁)机制确保同一时间只有一个请求能够对同一目标进行点赞,避免并发冲突。
(三)安全实现
- 在用户登录或认证后,为其颁发 Token,用户点赞时需携带 Token 进行身份验证,确保操作的合法性。
- 在点赞接口中,对用户的点赞行为进行幂等性校验,防止重复点赞。可通过在数据库中记录点赞操作的唯一标识或使用 Redis 缓存来实现。
四、代码实现
(一)创建微服务
- 模块创建:在项目中创建专门的点赞服务模块,将与点赞功能相关的代码和资源集中管理,确保模块的独立性和可维护性。
- 依赖管理(pom.xml)
- 引入必要的依赖项,如
auth-sdk
用于认证授权相关功能,确保点赞操作的安全性和合法性;tj-api
作为项目内部的公共接口模块,方便与其他服务进行接口调用和数据交互;spring-boot-starter-web
构建基于 Spring Boot 的 Web 服务,提供 HTTP 接口的开发能力;mybatis-plus-boot-starter
简化数据库访问操作,提高开发效率;mysql-connector-java
用于连接 MySQL 数据库,实现数据的持久化存储;spring-boot-starter-data-redis
集成 Redis 缓存,优化数据查询性能;spring-cloud-starter-alibaba-nacos-discovery
和spring-cloud-starter-alibaba-nacos-config
用于服务注册与发现以及配置管理,实现微服务架构中的服务治理和动态配置功能;spring-boot-starter-amqp
接入 RabbitMQ 消息队列,实现异步通信和解耦;xxl-job-core
用于实现定时任务功能,如定时同步点赞数据等;spring-cloud-starter-loadbalancer
提供负载均衡能力,确保服务在高并发环境下的稳定性和可靠性。
- 引入必要的依赖项,如
- 启动类配置
- 创建启动类(如
RemarkApplication
),通过@SpringBootApplication
注解标识这是一个 Spring Boot 应用的入口类。 - 使用
@EnableScheduling
注解开启定时任务功能,以便后续能够定时执行如同步点赞数据等任务。 - 通过
@MapperScan
注解指定 MyBatis Mapper 接口的扫描路径,确保数据库访问层的正确配置和使用。 - 在启动类的
main
方法中,完成 Spring 应用上下文的构建和启动,并在启动成功后打印应用的访问地址和相关信息,方便开发者进行调试和监控。
- 创建启动类(如
(二)新增点赞功能实现
- 接口定义与控制器(LikedRecordController)
- 定义点赞接口(如
POST /likes
),接收包含点赞业务 ID 和点赞状态(点赞或取消点赞)等信息的请求参数(LikeRecordFormDTO
)。 - 在控制器方法(
addLikeRecord
)中,调用点赞服务层的方法来处理点赞逻辑。
- 定义点赞接口(如
- 服务接口(ILikedRecordService)
- 定义点赞或取消点赞的业务方法(
addLikeRecord
),明确服务层的功能职责,为控制器与服务实现类之间提供统一的接口契约。
- 定义点赞或取消点赞的业务方法(
- 服务实现类(LikedRecordServiceImpl)
- 在点赞服务实现类中,首先根据传入的点赞状态(点赞或取消点赞)分别调用对应的处理方法(
liked
或unliked
)。 - 在
liked
方法中,先查询当前用户对指定业务是否已存在点赞记录。若已存在,则返回点赞失败;若不存在,则创建新的点赞记录并保存到数据库中。 - 在
unliked
方法中,根据用户 ID 和业务 ID 删除对应的点赞记录。 - 无论点赞或取消点赞操作成功与否,若成功则统计当前业务的点赞数量,并通过 RabbitMQ 发送消息通知相关业务方点赞数发生变化。消息内容包含业务 ID 和点赞数量,消息的路由键根据业务类型进行设置(如
{bizType}.times.changed
),以便不同业务方能够准确接收并处理对应的点赞数变更通知。
- 在点赞服务实现类中,首先根据传入的点赞状态(点赞或取消点赞)分别调用对应的处理方法(
(三)查询点赞功能实现
- 接口定义与控制器(LikedRecordController)
- 定义批量查询点赞状态的接口(如
GET /likes/list
),接收包含多个业务 ID 的请求参数(bizIds
)。 - 在控制器方法(
getLikedIds
)中,调用点赞服务层的方法来查询当前用户对这些业务 ID 的点赞状态。
- 定义批量查询点赞状态的接口(如
- 服务接口(ILikedRecordService)
- 定义批量查询点赞状态的业务方法(
getLikedIds
),接收业务 ID 列表作为参数,返回当前用户已点赞的业务 ID 集合。
- 定义批量查询点赞状态的业务方法(
- 服务实现类(LikedRecordServiceImpl)
- 在服务实现类中,根据当前用户 ID 和传入的业务 ID 列表,查询点赞记录表,找出用户已点赞的业务 ID,并将其封装成集合返回给控制器。
- Feign 客户端定义(tj-api 模块)
- 在
tj-api
模块中定义点赞服务的 Feign 客户端接口(RemarkClient
),用于其他服务远程调用点赞服务的查询点赞状态接口。 - 为 Feign 客户端接口定义降级处理类(
RemarkClientFallback
),当远程调用出现异常时,返回默认值或执行备用逻辑,以确保系统的稳定性和可靠性。通过在FallbackConfig
类中创建RemarkClientFallback
的 Bean 实例,并将其注册到 Spring 容器中,使 Feign 客户端在发生异常时能够正确使用降级处理逻辑。
- 在
五、测试与优化
- 问题分析:原有点赞功能实现中存在多次数据库读写操作,在高并发场景下可能对数据库造成巨大压力,影响系统性能和稳定性。主要问题包括高并发读操作对数据库的频繁查询以及高并发写操作对数据库的频繁插入和更新。
- 改进思路
- 高并发读优化:采用缓存技术(如 Redis),将点赞数量等高频查询数据缓存起来,减少对数据库的直接查询。同时,优化查询 SQL 和代码逻辑,提高查询效率。
- 高并发写优化:将同步写操作转变为异步写操作,通过消息队列(如 RabbitMQ)将点赞操作记录异步保存到数据库,降低对数据库的实时写压力。此外,合并写请求,减少不必要的重复写操作,提高数据库写操作的效率。
- 代码改造
- 点赞或取消点赞逻辑调整(LikedRecordRedisServiceImpl)
- 注入
StringRedisTemplate
,用于操作 Redis 缓存。 - 在点赞方法(
liked
)中,直接向 Redis 的集合(Set)中添加点赞用户 ID,若添加成功则返回点赞成功,否则返回失败。 - 在取消点赞方法(
unliked
)中,从 Redis 集合中删除点赞用户 ID,根据删除结果返回相应的操作成功或失败状态。 - 点赞或取消点赞成功后,更新 Redis 中记录点赞次数的有序集合(SortedSet),确保点赞次数的准确性。
- 注入
- 批量查询点赞状态优化(LikedRecordRedisServiceImpl)
- 使用 Redis 的管道(Pipeline)技术,一次性发送多个
sIsMember
命令,查询多个业务 ID 的点赞状态,减少网络开销和查询延迟。 - 根据查询结果,返回当前用户已点赞的业务 ID 集合。
- 使用 Redis 的管道(Pipeline)技术,一次性发送多个
- 定时任务实现(LikedTimesTask)
- 创建定时任务类(
LikedTimesTask
),使用@XxlJob
注解定义定时任务方法(checkLikedTime
)。 - 在定时任务方法中,调用点赞服务的方法(
readLikedTimesAndSendMQ
),实现定时从 Redis 中读取点赞次数数据,并通过 RabbitMQ 发送给相关业务方。
- 创建定时任务类(
- 点赞服务数据同步方法(ILikedRecordService 和 LikedRecordRedisServiceImpl)
- 在点赞服务接口(
ILikedRecordService
)中定义数据同步方法(readLikedTimesAndSendMQ
)。 - 在服务实现类(
LikedRecordRedisServiceImpl
)中实现该方法,遍历需要同步的业务类型(如QA
、NOTE
等),从 Redis 中获取点赞次数数据,并进行数据转换和消息发送。每次获取的数据量可通过配置参数(如MAX_SIZE
)进行控制,避免一次性读取过多数据对系统造成压力。
- 在点赞服务接口(
- 点赞或取消点赞逻辑调整(LikedRecordRedisServiceImpl)
- 定时任务配置与监听
- 在配置文件(如
bootstrap.yml
)中配置xxl-job
的相关参数,包括访问令牌(accessToken
)、管理端地址(admin.address
)以及执行器相关配置(如端口、应用名称等)。 - 在
xxl-job-admin
管理界面配置执行器,确保执行器能够正确连接到任务调度中心。 - 配置定时任务,设置任务的执行表达式(如每隔一段时间执行一次)、任务描述等信息,使定时任务能够按照预定计划执行点赞数据同步操作。
- 在相关业务模块(如
tj-learning
)中添加消息监听器(如LikeTimesChangeListener
),监听点赞数变更的消息队列,接收从点赞服务发送过来的点赞次数更新消息,并根据消息内容更新本地数据库中的点赞数量,确保业务数据与点赞数据的一致性。
- 在配置文件(如
六、总结
通过以上对点赞系统的详细设计与实现过程,我们构建了一个满足通用、独立、高并发和安全特性的点赞系统。在实际应用中,该系统能够为不同业务场景提供稳定、高效的点赞服务,有效提升用户参与度和互动体验。同时,通过不断优化和改进,点赞系统能够更好地适应业务发展和高并发场景的挑战,为整个应用平台的发展提供有力支持。