利用redis实现点赞功能的几种 *** (看看如何用 Redis实现微博好

生活常识 2023-04-16 17:34生活常识www.baidianfengw.cn

新浪微博如何批量取消关注,看如何用Redis(关注、访问、共同关注)实现微博好友

需求分析

的好友功能是目前社交场景中必备的功能之一。一般的好友相关功能包括关注/下车,我(他)的关注,我(他)的粉丝,共同关注,我关注的人也关注他。

如果只是利用数据库获取用户的一些粉丝或者关注列表,实现类似这样的功能是非常简单容易的,如果我想找出两个或两个以上的用户关注谁,或者想查询两个或两个以上用户的共同粉丝,就会非常麻烦,效率很低。

如果用redis来做,会相当简单高效。原因是redis本身就有一些专门针对这类 的交、并、差集的运算。

设计思路

整体思路由MySQL Redis完成。MySQL主要保存登陆数据,使用Redis的Sets数据类型进行 操作。

set具有去重功能(我们可以 不要多次关注同一个用户)。对于一个用户,我们存储两套一套是保存被用户关注的人,一套是保存被用户关注的人。

SADD 添加成员指挥格式3360 add key member[member…]-关注SREM 移除某个成员指挥格式 SREM key member [member …]。-SCARD 统计 内的成员数命令格式 scardkey -关注者/粉丝数SI EMBER 判断是否是 成员.命令格式s sis会员关键会员-判断是否关注(如果关注,只能点击 起飞 ) EMBERS 查询 内的成员命令格式s成员key-list使用(关注列表和粉丝列表)SINTER 查询 的交集.命令格式 starter key [key …] -数据库表

数据库表设计

结构比较简单,主要记录用户id、用户 相关id和相关状态。

create table ` t _ follow `(` id ` int(11)not null auto _ increment,` user _ id ` int(11)default null comment ;当前登录用户的 Follow _ user _ id ` int(11)default null comment ;当前登录用户所关注的用户的 ` is _ valid ` tinyint(1)default null comment ;关注状态,0-不关注,1-关注 Create _ date datetimedefaultnull,` update _ date datetimedefaultnull,primary key(` id `))engine=innodb defaultcharset=utf8 row _ format=compact comment= ;用户和用户关注表

新建好友功能微服务

pom取决于以下因素

?xml版本= 1.0 编码= UTF-8 ;xmlns项目= http://maven.apache.org/POM/4.0.0xmlns:xsi= http://www.w3.org/2001/XMLSchema-instancexsi:schemaLocation= http://maven.apache.org/POM/4 . 0 . 0 http://maven.apache.org/xsd/maven-4.0.0.xsd父artifactId redis-sec kill/artifactId groupIdcom.zjq/groupId版本1.0-快照/版本/父模型版本4 . 0 . 0/模型版本artifactIdms-follow/artifactId依赖项!-尤里卡客户端依赖groupIdorg.springframework.cloud/groupId artifactId

gtspring-cloud-starter-netflix-eureka-clientlt/artifactIdgt lt/dependencygt lt!-- spring web --gt ltdependencygt ltgroupIdgtorg.springframework.bootlt/groupIdgt ltartifactIdgtspring-boot-starter-weblt/artifactIdgt lt/dependencygt lt!-- mysql --gt ltdependencygt ltgroupIdgtmysqllt/groupIdgt ltartifactIdgtmysql-connector-javalt/artifactIdgt lt/dependencygt lt!-- spring data redis --gt ltdependencygt ltgroupIdgtorg.springframework.bootlt/groupIdgt ltartifactIdgtspring-boot-starter-data-redislt/artifactIdgt lt/dependencygt lt!-- mybatis --gt ltdependencygt ltgroupIdgtorg.mybatis.spring.bootlt/groupIdgt ltartifactIdgtmybatis-spring-boot-starterlt/artifactIdgt lt/dependencygt lt!-- commons 公共项目 --gt ltdependencygt ltgroupIdgtcom.zjqlt/groupIdgt ltartifactIdgtcommonslt/artifactIdgt ltversiongt1.0-SNAPSHOTlt/versiongt lt/dependencygt lt!-- swagger --gt ltdependencygt ltgroupIdgtcom.battcnlt/groupIdgt ltartifactIdgtswagger-spring-boot-starterlt/artifactIdgt lt/dependencygt lt/dependenciesgtlt/projectgt

springboot配置如下

server: port: 7004 # 端口spring: application: name: ms-follow # 应用名 # 数据库 datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root url: jdbc:mysql://127.0.0.1:3306/seckill?serverTimezone=Asia/ShanghaicharacterEncoding=utf8useUnicode=trueuseSSL=false # Redis redis: port: 6379 host: localhost timeout: 3000 password: 123456 database: 2 # Swagger swagger: base-package: com.zjq.follow title: 好用功能微服务API接口文档# 配置 Eureka Server 注册中心eureka: instance: prefer-ip-address: true instance-id: ${spring.cloud.client.ip-address}:${server.port} client: service-url: defaultZone: http://localhost:7000/eureka/service: name: ms-oauth-server: http://ms-oauth2-server/ ms-diners-server: http://ms-users/mybatis: configuration: map-underscore-to-camel-case: true # 开启驼峰映射logging: pattern: console: #39%d{HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n#39

添加配置类

redis配置类

package com.zjq.seckill.configimport com.fasterxml.jackson.annotation.JsonAutoDetectimport com.fasterxml.jackson.annotation.PropertyAccessorimport com.fasterxml.jackson.databind.ObjectMapperimport org.springframework.context.annotation.Beanimport org.springframework.context.annotation.Configurationimport org.springframework.core.io.ClassPathResourceimport org.springframework.data.redis.connection.RedisConnectionFactoryimport org.springframework.data.redis.core.RedisTemplateimport org.springframework.data.redis.core.script.DefaultRedisScriptimport org.springframework.data.redis.serializer.Jackson2JsonRedisSerializerimport org.springframework.data.redis.serializer.StringRedisSerializer/ RedisTemplate配置类 @author zjq /@Configurationpublic class RedisTemplateConfiguration { / redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类 @param redisConnectionFactory @return / @Bean public RedisTemplateltObject, Objectgt redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplateltObject, Objectgt redisTemplate = new RedisTemplateltgt() redisTemplate.setConnectionFactory(redisConnectionFactory) // 使用Jackson2JsonRedisSerialize 替换默认序列化 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class) ObjectMapper objectMapper = new ObjectMapper() objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY) jackson2JsonRedisSerializer.setObjectMapper(objectMapper) // 设置key和value的序列化规则 redisTemplate.setValueSerializer(jackson2JsonRedisSerializer) redisTemplate.setKeySerializer(new StringRedisSerializer()) redisTemplate.setHashKeySerializer(new StringRedisSerializer()) redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer) redisTemplate.afterPropertiesSet() return redisTemplate } }

REST配置类

关注/取关实现

业务逻辑

Mapper实现

Mapper比较简单主要是查询关注信息、添加关注信息、取关或者关注。

Service层实现

package com.zjq.seckill.serviceimport cn.hutool.core.bean.BeanUtilimport com.zjq.commons.constant.ApiConstantimport com.zjq.commons.constant.RedisKeyConstantimport com.zjq.commons.exception.ParameterExceptionimport com.zjq.commons.model.domain.ResultInfoimport com.zjq.commons.model.pojo.Followimport com.zjq.commons.model.vo.SignInUserInfoimport com.zjq.commons.utils.AssertUtilimport com.zjq.commons.utils.ResultInfoUtilimport com.zjq.seckill.mapper.FollowMapperimport org.springframework.beans.factory.annotation.Valueimport org.springframework.data.redis.core.RedisTemplateimport org.springframework.stereotype.Serviceimport org.springframework.web.client.RestTemplateimport javax.annotation.Resourceimport java.util.LinkedHashMap/ 关注/取关业务逻辑层 @author zjq /@Servicepublic class FollowService { @Value(#34${service.name.ms-oauth-server}#34) private String oauthServerName @Value(#34${service.name.ms-diners-server}#34) private String dinersServerName @Resource private RestTemplate restTemplate @Resource private FollowMapper followMapper @Resource private RedisTemplate redisTemplate / 关注/取关 @param followUserId 关注的食客ID @param isFollowed 是否关注 1=关注 0=取关 @param accessToken 登录用户token @param path 访问地址 @return / public ResultInfo follow(Integer followUserId, int isFollowed, String accessToken, String path) { // 是否选择了关注对象 AssertUtil.isTrue(followUserId == null || followUserId lt 1, #34请选择要关注的人#34) // 获取登录用户信息 (封装 ) SignInUserInfo dinerInfo = loadSignInDinerInfo(accessToken) // 获取当前登录用户与需要关注用户的关注信息 Follow follow = followMapper.selectFollow(dinerInfo.getId(), followUserId) // 如果没有关注信息,且要进行关注操作 -- 添加关注 if (follow == null isFollowed == 1) { // 添加关注信息 int count = followMapper.save(dinerInfo.getId(), followUserId) // 添加关注列表到 Redis if (count == 1) { addToRedisSet(dinerInfo.getId(), followUserId) } return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, #34关注成功#34, path, #34关注成功#34) } // 如果有关注信息,且目前处于关注状态,且要进行取关操作 -- 取关关注 if (follow != null follow.getIsValid() == 1 isFollowed == 0) { // 取关 int count = followMapper.update(follow.getId(), isFollowed) // 移除 Redis 关注列表 if (count == 1) { removeFromRedisSet(dinerInfo.getId(), followUserId) } return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, #34成功取关#34, path, #34成功取关#34) } // 如果有关注信息,且目前处于取关状态,且要进行关注操作 -- 重新关注 if (follow != null follow.getIsValid() == 0 isFollowed == 1) { // 重新关注 int count = followMapper.update(follow.getId(), isFollowed) // 添加关注列表到 Redis if (count == 1) { addToRedisSet(dinerInfo.getId(), followUserId) } return ResultInfoUtil.build(ApiConstant.SUCCESS_CODE, #34关注成功#34, path, #34关注成功#34) } return ResultInfoUtil.buildSuccess(path, #34操作成功#34) } / 添加关注列表到 Redis @param dinerId @param followUserId / private void addToRedisSet(Integer dinerId, Integer followUserId) { redisTemplate.opsForSet().add(RedisKeyConstant.following.getKey() + dinerId, followUserId) redisTemplate.opsForSet().add(RedisKeyConstant.followers.getKey() + followUserId, dinerId) } / 移除 Redis 关注列表 @param dinerId @param followUserId / private void removeFromRedisSet(Integer dinerId, Integer followUserId) { redisTemplate.opsForSet().remove(RedisKeyConstant.following.getKey() + dinerId, followUserId) redisTemplate.opsForSet().remove(RedisKeyConstant.followers.getKey() + followUserId, dinerId) } / 获取登录用户信息 @param accessToken @return / private SignInUserInfo loadSignInDinerInfo(String accessToken) { // 必须登录 AssertUtil.mustLogin(accessToken) String url = oauthServerName + #34user/me?access_token={accessToken}#34 ResultInfo resultInfo = restTemplate.getForObject(url, ResultInfo.class, accessToken) if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) { throw new ParameterException(resultInfo.getMessage()) } SignInUserInfo dinerInfo = BeanUtil.fillBeanWithMap((LinkedHashMap) resultInfo.getData(), new SignInUserInfo(), false) return dinerInfo }}

Controller实现

package com.zjq.seckill.controllerimport com.zjq.commons.model.domain.ResultInfoimport com.zjq.seckill.service.FollowServiceimport org.springframework.web.bind.annotation.import javax.annotation.Resourceimport javax.servlet.http.HttpServletRequest/ 关注/取关控制层 @author zjq /@RestControllerpublic class FollowController { @Resource private FollowService followService @Resource private HttpServletRequest request / 关注/取关 @param followUserId 关注的用户ID @param isFollowed 是否关注 1=关注 0=取消 @param access_token 登录用户token @return / @PostMapping(#34/{followUserId}#34) public ResultInfo follow(@PathVariable Integer followUserId, @RequestParam int isFollowed, String access_token) { ResultInfo resultInfo = followService.follow(followUserId, isFollowed, access_token, request.getServletPath()) return resultInfo }}

网关配置路由规则

spring: application: name: ms-gateway cloud: gateway: discovery: locator: enabled: true # 开启配置注册中心进行路由功能 lower-case-service-id: true # 将服务名称转小写 routes: # 好友功能微服务 - id: ms-follow uri: lb://ms-follow predicates: - Path=/follow/ filters: - StripPrefix=1

测试验证

依次启动,注册中心、网关、认证中心、好友功能微服务。

测试id为5的用户,关注id为1的用户。

查看redis可以看到有两个 ,一个粉丝 ,一个关注 。

查看数据库,id为5的用户关注了id为1的用户

共同关注列表

从Redis中读取登录用户的关注列表与查看用户的关注列表,然后进行交集操作,获取共同关注的用户id

然后通过用户服务传入用户id数据获取用户基本信息

Controller添加

/ 共同关注列表 @param userId @param access_token @return / @GetMapping(#34commons/{userId}#34) public ResultInfo findCommonsFriends(@PathVariable Integer userId, String access_token) { return followService.findCommonsFriends(userId, access_token, request.getServletPath()) }

Service添加

/ 共同关注列表 @param userId @param accessToken @param path @return / @Transactional(rollbackFor = Exception.class) public ResultInfo findCommonsFriends(Integer userId, String accessToken, String path) { // 是否选择了查看对象 AssertUtil.isTrue(userId == null || userId lt 1, #34请选择要查看的人#34) // 获取登录用户信息 SignInUserInfo userInfo = loadSignInuserInfo(accessToken) // 获取登录用户的关注信息 String loginuserKey = RedisKeyConstant.following.getKey() + userInfo.getId() // 获取登录用户查看对象的关注信息 String userKey = RedisKeyConstant.following.getKey() + userId // 计算交集 SetltIntegergt userIds = redisTemplate.opsForSet().intersect(loginuserKey, userKey) // 没有 if (userIds == null || userIds.isEmpty()) { return ResultInfoUtil.buildSuccess(path, new ArrayListltShortUserInfogt()) } // 调用食 务根据 ids 查询食客信息 ResultInfo resultInfo = restTemplate.getForObject(usersServerName + #34findByIds?access_token={accessToken}ids={ids}#34, ResultInfo.class, accessToken, StrUtil.join(#34,#34, userIds)) if (resultInfo.getCode() != ApiConstant.SUCCESS_CODE) { resultInfo.setPath(path) return resultInfo } // 处理结果集 ListltLinkedHashMapgt dinnerInfoMaps = (ArrayList) resultInfo.getData() ListltShortUserInfogt userInfos = dinnerInfoMaps.stream() .map(user -gt BeanUtil.fillBeanWithMap(user, new ShortUserInfo(), true)) .collect(Collectors.toList()) return ResultInfoUtil.buildSuccess(path, userInfos) }

用户服务新增根据ids查询用户

Controller:

/ 根据 ids 查询用户信息 @param ids @return / @GetMapping(#34findByIds#34) public ResultInfoltListltShortUserInfogtgt findByIds(String ids) { ListltShortUserInfogt dinerInfos = userService.findByIds(ids) return ResultInfoUtil.buildSuccess(request.getServletPath(), dinerInfos) }

Service

/ 根据 ids 查询食客信息 @param ids 主键 id,多个以逗号分隔,逗号之间不用空格 @return / public ListltShortUserInfogt findByIds(String ids) { AssertUtil.isNotEmpty(ids) String[] idArr = ids.split(#34,#34) ListltShortUserInfogt dinerInfos = usersMapper.findByIds(idArr) return dinerInfos }

Mapper:

/ 根据 ID 查询多个食客信息 @param ids @return / @Select(#34ltscriptgt #34 + #34 select id, nickname, avatar_url from t_diners #34 + #34 where is_valid = 1 and id in #34 + #34 ltforeach item=\#34id\#34 collection=\#34ids\#34 open=\#34(\#34 separator=\#34,\#34 close=\#34)\#34gt #34 + #34 #{id} #34 + #34 lt/foreachgt #34 + #34 lt/scriptgt#34) ListltShortUserInfogt findByIds(@Param(#34ids#34) String[] ids)

上面测试已经让id5和7的用户关注了id为1的用户,我们继续让id5的用户关注id为3的用户,让id5、6、7的用户关注了id为2的用户。

redis和数据库信息如下

测试验证

查询当前登录用户id为5和id为7的共同关注信息

查询当前登录用户id为6和id为7的共同关注信息

可以看出来5和7共同关注了1和2,6和7只共同关注了2,符合预期。

感谢阅读,希望对你有所帮助 :)

来源zhanjq.blog.csdn.net/article/details/128208047

redis 是如何实现点赞取消点赞的 redis 实现共同关注

Copyright@2015-2025 白癜风网版板所有