200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > Redis实现微博好友功能微服务(关注 取关 共同关注)

Redis实现微博好友功能微服务(关注 取关 共同关注)

时间:2023-04-11 12:23:56

相关推荐

Redis实现微博好友功能微服务(关注 取关 共同关注)

点击关注公众号,利用碎片时间学习

需求分析

好友功能是目前社交场景的必备功能之一,一般好友相关的功能包含有:关注/取关、我(他)的关注、我(他)的粉丝、共同关注、我关注的人也关注他等这样一些功能。

类似于这样的功能我们如果采用数据库做的话只是单纯得到用户的一些粉丝或者关注列表的话是很简单也很容易实现, 但是如果我想要查出两个甚至多个用户共同关注了哪些人或者想要查询两个或者多个用户的共同粉丝的话就会很麻烦,效率也不会很高。

但是如果你用redis去做的话就会相当的简单而且效率很高。原因是redis自己本身带有专门针对于这种集合的交集、并集、差集的一些操作。

设计思路

总体思路我们采用MySQL + Redis的方式结合完成。MySQL主要是保存落地数据,而利用Redis的Sets数据类型进行集合操作。

Sets拥有去重(我们不能多次关注同一用户)功能。一个用户我们存贮两个集合:一个是保存用户关注的人 另一个是保存关注用户的人。

SADD 添加成员:命令格式:SADD key member [member …]----- 关注

SREM 移除某个成员:命令格式:SREM key member [member …]-------取关

SCARD 统计集合内的成员数:命令格式:SCARD key-------关注/粉丝个数

SISMEMBER 判断是否是集合成员:命令格式:SISMEMBER key member---------判断是否关注(如果关注那么只可以点击取关)

SMEMBERS 查询集合内的成员:命令格式:SMEMBERS key-------列表使用(关注列表和粉丝列表)

SINTER 查询集合的交集:命令格式:SINTER key [key …]--------共同关注、我关注的人关注了他

数据库表设计

这个数据库表的结构比较简单,主要记录了用户id、用户关注的id和关注状态。

CREATETABLE`t_follow`(`id`int(11)NOTNULLAUTO_INCREMENT,`user_id`int(11)DEFAULTNULLCOMMENT'当前登录用户的id',`follow_user_id`int(11)DEFAULTNULLCOMMENT'当前登录用户关注的用户的id',`is_valid`tinyint(1)DEFAULTNULLCOMMENT'关注状态,0-没有关注,1-关注了',`create_date`datetimeDEFAULTNULL,`update_date`datetimeDEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8ROW_FORMAT=COMPACTCOMMENT='用户和用户关注表';

新建好友功能微服务

添加依赖和配置

pom依赖如下:

<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="/POM/4.0.0"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0/xsd/maven-4.0.0.xsd"><parent><artifactId>redis-seckill</artifactId><groupId>com.zjq</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>ms-follow</artifactId><dependencies><!--eurekaclient--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><!--springweb--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--springdataredis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--mybatis--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId></dependency><!--commons公共项目--><dependency><groupId>com.zjq</groupId><artifactId>commons</artifactId><version>1.0-SNAPSHOT</version></dependency><!--swagger--><dependency><groupId>com.battcn</groupId><artifactId>swagger-spring-boot-starter</artifactId></dependency></dependencies></project>

springboot配置如下:

server:port:7004#端口spring:application:name:ms-follow#应用名#数据库datasource:driver-class-name:com.mysql.cj.jdbc.Driverusername:rootpassword:rooturl:jdbc:mysql://127.0.0.1:3306/seckill?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false#Redisredis:port:6379host:localhosttimeout:3000password:123456database:2#Swaggerswagger:base-package:com.zjq.followtitle:好用功能微服务API接口文档#配置EurekaServer注册中心eureka:instance:prefer-ip-address:trueinstance-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:'%d{HH:mm:ss}[%thread]%-5level%logger{50}-%msg%n'

添加配置类

redis配置类:

packagecom.zjq.seckill.config;importcom.fasterxml.jackson.annotation.JsonAutoDetect;importcom.fasterxml.jackson.annotation.PropertyAccessor;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.core.io.ClassPathResource;importorg.springframework.data.redis.connection.RedisConnectionFactory;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.data.redis.core.script.DefaultRedisScript;importorg.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;importorg.springframework.data.redis.serializer.StringRedisSerializer;/***RedisTemplate配置类*@authorzjq*/@ConfigurationpublicclassRedisTemplateConfiguration{/***redisTemplate序列化使用的jdkSerializeable,存储二进制字节码,所以自定义序列化类**@paramredisConnectionFactory*@return*/@BeanpublicRedisTemplate<Object,Object>redisTemplate(RedisConnectionFactoryredisConnectionFactory){RedisTemplate<Object,Object>redisTemplate=newRedisTemplate<>();redisTemplate.setConnectionFactory(redisConnectionFactory);//使用Jackson2JsonRedisSerialize替换默认序列化Jackson2JsonRedisSerializerjackson2JsonRedisSerializer=newJackson2JsonRedisSerializer(Object.class);ObjectMapperobjectMapper=newObjectMapper();objectMapper.setVisibility(PropertyAccessor.ALL,JsonAutoDetect.Visibility.ANY);jackson2JsonRedisSerializer.setObjectMapper(objectMapper);//设置key和value的序列化规则redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setKeySerializer(newStringRedisSerializer());redisTemplate.setHashKeySerializer(newStringRedisSerializer());redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);redisTemplate.afterPropertiesSet();returnredisTemplate;}}

REST配置类:

关注/取关实现

业务逻辑

Mapper实现

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

Service层实现

packagecom.zjq.seckill.service;importcn.hutool.core.bean.BeanUtil;mons.constant.ApiConstant;mons.constant.RedisKeyConstant;mons.exception.ParameterException;mons.model.domain.ResultInfo;mons.model.pojo.Follow;mons.model.vo.SignInUserInfo;mons.utils.AssertUtil;mons.utils.ResultInfoUtil;importcom.zjq.seckill.mapper.FollowMapper;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Service;importorg.springframework.web.client.RestTemplate;importjavax.annotation.Resource;importjava.util.LinkedHashMap;/***关注/取关业务逻辑层*@authorzjq*/@ServicepublicclassFollowService{@Value("${service.name.ms-oauth-server}")privateStringoauthServerName;@Value("${service.name.ms-diners-server}")privateStringdinersServerName;@ResourceprivateRestTemplaterestTemplate;@ResourceprivateFollowMapperfollowMapper;@ResourceprivateRedisTemplateredisTemplate;/***关注/取关**@paramfollowUserId关注的食客ID*@paramisFollowed是否关注1=关注0=取关*@paramaccessToken登录用户token*@parampath访问地址*@return*/publicResultInfofollow(IntegerfollowUserId,intisFollowed,StringaccessToken,Stringpath){//是否选择了关注对象AssertUtil.isTrue(followUserId==null||followUserId<1,"请选择要关注的人");//获取登录用户信息(封装方法)SignInUserInfodinerInfo=loadSignInDinerInfo(accessToken);//获取当前登录用户与需要关注用户的关注信息Followfollow=followMapper.selectFollow(dinerInfo.getId(),followUserId);//如果没有关注信息,且要进行关注操作--添加关注if(follow==null&&isFollowed==1){//添加关注信息intcount=followMapper.save(dinerInfo.getId(),followUserId);//添加关注列表到Redisif(count==1){addToRedisSet(dinerInfo.getId(),followUserId);}returnResultInfoUtil.build(ApiConstant.SUCCESS_CODE,"关注成功",path,"关注成功");}//如果有关注信息,且目前处于关注状态,且要进行取关操作--取关关注if(follow!=null&&follow.getIsValid()==1&&isFollowed==0){//取关intcount=followMapper.update(follow.getId(),isFollowed);//移除Redis关注列表if(count==1){removeFromRedisSet(dinerInfo.getId(),followUserId);}returnResultInfoUtil.build(ApiConstant.SUCCESS_CODE,"成功取关",path,"成功取关");}//如果有关注信息,且目前处于取关状态,且要进行关注操作--重新关注if(follow!=null&&follow.getIsValid()==0&&isFollowed==1){//重新关注intcount=followMapper.update(follow.getId(),isFollowed);//添加关注列表到Redisif(count==1){addToRedisSet(dinerInfo.getId(),followUserId);}returnResultInfoUtil.build(ApiConstant.SUCCESS_CODE,"关注成功",path,"关注成功");}returnResultInfoUtil.buildSuccess(path,"操作成功");}/***添加关注列表到Redis**@paramdinerId*@paramfollowUserId*/privatevoidaddToRedisSet(IntegerdinerId,IntegerfollowUserId){redisTemplate.opsForSet().add(RedisKeyConstant.following.getKey()+dinerId,followUserId);redisTemplate.opsForSet().add(RedisKeyConstant.followers.getKey()+followUserId,dinerId);}/***移除Redis关注列表**@paramdinerId*@paramfollowUserId*/privatevoidremoveFromRedisSet(IntegerdinerId,IntegerfollowUserId){redisTemplate.opsForSet().remove(RedisKeyConstant.following.getKey()+dinerId,followUserId);redisTemplate.opsForSet().remove(RedisKeyConstant.followers.getKey()+followUserId,dinerId);}/***获取登录用户信息**@paramaccessToken*@return*/privateSignInUserInfoloadSignInDinerInfo(StringaccessToken){//必须登录AssertUtil.mustLogin(accessToken);Stringurl=oauthServerName+"user/me?access_token={accessToken}";ResultInforesultInfo=restTemplate.getForObject(url,ResultInfo.class,accessToken);if(resultInfo.getCode()!=ApiConstant.SUCCESS_CODE){thrownewParameterException(resultInfo.getMessage());}SignInUserInfodinerInfo=BeanUtil.fillBeanWithMap((LinkedHashMap)resultInfo.getData(),newSignInUserInfo(),false);returndinerInfo;}}

Controller实现

packagecom.zjq.seckill.controller;mons.model.domain.ResultInfo;importcom.zjq.seckill.service.FollowService;importorg.springframework.web.bind.annotation.*;importjavax.annotation.Resource;importjavax.servlet.http.HttpServletRequest;/***关注/取关控制层*@authorzjq*/@RestControllerpublicclassFollowController{@ResourceprivateFollowServicefollowService;@ResourceprivateHttpServletRequestrequest;/***关注/取关**@paramfollowUserId关注的用户ID*@paramisFollowed是否关注1=关注0=取消*@paramaccess_token登录用户token*@return*/@PostMapping("/{followUserId}")publicResultInfofollow(@PathVariableIntegerfollowUserId,@RequestParamintisFollowed,Stringaccess_token){ResultInforesultInfo=followService.follow(followUserId,isFollowed,access_token,request.getServletPath());returnresultInfo;}}

网关配置路由规则

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

测试验证

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

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

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

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

共同关注列表

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

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

Controller添加方法

/***共同关注列表**@paramuserId*@paramaccess_token*@return*/@GetMapping("commons/{userId}")publicResultInfofindCommonsFriends(@PathVariableIntegeruserId,Stringaccess_token){returnfollowService.findCommonsFriends(userId,access_token,request.getServletPath());}

Service添加方法

/***共同关注列表**@paramuserId*@paramaccessToken*@parampath*@return*/@Transactional(rollbackFor=Exception.class)publicResultInfofindCommonsFriends(IntegeruserId,StringaccessToken,Stringpath){//是否选择了查看对象AssertUtil.isTrue(userId==null||userId<1,"请选择要查看的人");//获取登录用户信息SignInUserInfouserInfo=loadSignInuserInfo(accessToken);//获取登录用户的关注信息StringloginuserKey=RedisKeyConstant.following.getKey()+userInfo.getId();//获取登录用户查看对象的关注信息StringuserKey=RedisKeyConstant.following.getKey()+userId;//计算交集Set<Integer>userIds=redisTemplate.opsForSet().intersect(loginuserKey,userKey);//没有if(userIds==null||userIds.isEmpty()){returnResultInfoUtil.buildSuccess(path,newArrayList<ShortUserInfo>());}//调用食客服务根据ids查询食客信息ResultInforesultInfo=restTemplate.getForObject(usersServerName+"findByIds?access_token={accessToken}&ids={ids}",ResultInfo.class,accessToken,StrUtil.join(",",userIds));if(resultInfo.getCode()!=ApiConstant.SUCCESS_CODE){resultInfo.setPath(path);returnresultInfo;}//处理结果集List<LinkedHashMap>dinnerInfoMaps=(ArrayList)resultInfo.getData();List<ShortUserInfo>userInfos=dinnerInfoMaps.stream().map(user->BeanUtil.fillBeanWithMap(user,newShortUserInfo(),true)).collect(Collectors.toList());returnResultInfoUtil.buildSuccess(path,userInfos);}

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

Controller:

/***根据ids查询用户信息**@paramids*@return*/@GetMapping("findByIds")publicResultInfo<List<ShortUserInfo>>findByIds(Stringids){List<ShortUserInfo>dinerInfos=userService.findByIds(ids);returnResultInfoUtil.buildSuccess(request.getServletPath(),dinerInfos);}

Service:

/***根据ids查询食客信息**@paramids主键id,多个以逗号分隔,逗号之间不用空格*@return*/publicList<ShortUserInfo>findByIds(Stringids){AssertUtil.isNotEmpty(ids);String[]idArr=ids.split(",");List<ShortUserInfo>dinerInfos=usersMapper.findByIds(idArr);returndinerInfos;}

Mapper:

/***根据ID集合查询多个食客信息*@paramids*@return*/@Select("<script>"+"selectid,nickname,avatar_urlfromt_diners"+"whereis_valid=1andidin"+"<foreachitem=\"id\"collection=\"ids\"open=\"(\"separator=\",\"close=\")\">"+"#{id}"+"</foreach>"+"</script>")List<ShortUserInfo>findByIds(@Param("ids")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./article/details/128208047

推荐:最全的java面试题库PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我们吧!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。