最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis 實現(xiàn)好友關(guān)注和關(guān)注推送的示例代碼

 更新時間:2025年03月11日 10:20:56   作者:快樂的小三菊  
本文介紹了使用Redis實現(xiàn)好友關(guān)注和關(guān)注推送功能,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、關(guān)注和取關(guān)

1.1 簡介

在探店圖文的詳情頁面中,可以關(guān)注發(fā)布筆記的作者:

1.2 需求描述

實現(xiàn)兩個接口:關(guān)注和取關(guān)接口、判斷是否關(guān)注的接口

1.3 代碼實現(xiàn)

涉及到的 controller 層代碼如下:

@RestController
@RequestMapping("/follow")
public class FollowController {

    @Resource
    private IFollowService followService;

    @PutMapping("/{id}/{isFollow}")
    public Result follow(@PathVariable("id") Long followUserId, @PathVariable("isFollow") Boolean isFollow) {
        return followService.follow(followUserId, isFollow);
    }

    @GetMapping("/or/not/{id}")
    public Result isFollow(@PathVariable("id") Long followUserId) {
        return followService.isFollow(followUserId);
    }
}

涉及到的 service 層代碼如下:

@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private IUserService userService;

    @Override
    public Result follow(Long followUserId, Boolean isFollow) {
        // 1.獲取登錄用戶
        Long userId = UserHolder.getUser().getId();
        String key = "follows:" + userId;
        // 1.判斷到底是關(guān)注還是取關(guān)
        if (isFollow) {
            // 2.關(guān)注,新增數(shù)據(jù)
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            boolean isSuccess = save(follow);
            if (isSuccess) {
                // 把關(guān)注用戶的id,放入redis的set集合 sadd userId followerUserId
                stringRedisTemplate.opsForSet().add(key, followUserId.toString());
            }
        } else {
            // 3.取關(guān),刪除 delete from tb_follow where user_id = ? and follow_user_id = ?
            boolean isSuccess = remove(new QueryWrapper<Follow>()
                    .eq("user_id", userId).eq("follow_user_id", followUserId));
            if (isSuccess) {
                // 把關(guān)注用戶的id從Redis集合中移除
                stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
            }
        }
        return Result.ok();
    }

    @Override
    public Result isFollow(Long followUserId) {
        // 1.獲取登錄用戶
        Long userId = UserHolder.getUser().getId();
        // 2.查詢是否關(guān)注 select count(*) from tb_follow where user_id = ? and follow_user_id = ?
        Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();
        // 3.判斷
        return Result.ok(count > 0);
    }
}

二、共同關(guān)注

2.1 簡介

點擊博主頭像,可以進(jìn)入博主首頁:

2.2 需求描述

利用 Redis 中恰當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu),實現(xiàn)共同關(guān)注功能。在博主個人頁面展示出當(dāng)前用戶與博主的共同好友。

2.3 代碼實現(xiàn)

涉及到的 controller 層代碼如下:

@RestController
@RequestMapping("/follow")
public class FollowController {

    @Resource
    private IFollowService followService;

    @GetMapping("/common/{id}")
    public Result followCommons(@PathVariable("id") Long id){
        return followService.followCommons(id);
    }
}

涉及到的 service 層代碼如下:

@Service
public class FollowServiceImpl extends ServiceImpl<FollowMapper, Follow> implements IFollowService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private IUserService userService;

    @Override
    public Result followCommons(Long id) {
        // 1.獲取當(dāng)前用戶
        Long userId = UserHolder.getUser().getId();
        String key = "follows:" + userId;
        // 2.求交集
        String key2 = "follows:" + id;
        Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);
        if (intersect == null || intersect.isEmpty()) {
            // 無交集
            return Result.ok(Collections.emptyList());
        }
        // 3.解析id集合
        List<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());
        // 4.查詢用戶
        List<UserDTO> users = userService.listByIds(ids)
                .stream()
                .map(user -> BeanUtil.copyProperties(user, UserDTO.class))
                .collect(Collectors.toList());
        return Result.ok(users);
    }
}

三、關(guān)注推送

3.1 簡介

關(guān)注推送也叫做 Feed 流,直譯為投喂。為用戶持續(xù)的提供 “沉浸式” 的體驗,通過無限下拉刷新獲取新的信息。

3.2 Feed 流的模式

3.2.1 TimeLine

不做內(nèi)容篩選,簡單的按照內(nèi)容發(fā)布時間排序,常用于好友或關(guān)注。例如朋友圈。

優(yōu)點:信息全面,不會有缺失。并且實現(xiàn)也相對簡單

缺點:信息噪音較多,用戶不一定感興趣,內(nèi)容獲取效率低

3.2.2 智能排序

利用智能算法屏蔽掉違規(guī)的、用戶不感興趣的內(nèi)容。推送用戶感興趣信息來吸引用戶。

優(yōu)點:投喂用戶感興趣信息,用戶粘度很高,容易沉迷

缺點:如果算法不精準(zhǔn),可能起到反作用

3.3 實現(xiàn)方案

本例中的個人頁面,是基于關(guān)注的好友來做 Feed 流,因此采用 Timeline 的模式。該模式的實現(xiàn)方案有三種:拉模式、推模式、推拉結(jié)合模式。

3.3.1 拉模式

拉模式也叫做讀擴(kuò)散,假設(shè)現(xiàn)在有三個 up 主,張三李四和王五,它們?nèi)齻€人將來會發(fā)一些消息,此時,給他們每個人都準(zhǔn)備一個發(fā)件箱,將來它們發(fā)送消息的時候就會發(fā)送到發(fā)件箱里面去,發(fā)送的消息除了本身以外還需要攜帶一個時間戳,因為后面要按照時間排序。

此時有一個粉絲趙六,它有一個收件箱,平常是空的,只有他要去讀消息的時候我們才會給他去拉取,即從它所關(guān)注的 up 主的發(fā)件箱去拉取消息,拉過來之后按照時間去排序,這樣就可以去讀取了。

這種模式的優(yōu)點是節(jié)省內(nèi)存空間,因為收件箱讀取完畢之后就可以清空,下次再重新拉取。缺點是每次讀消息的時候都要重新去拉取發(fā)件箱的消息,然后再做排序,這一系列的動作耗時會比較久,讀取的延遲較高。 

3.3.2 推模式

推模式也叫寫擴(kuò)散。假設(shè)此時有兩個 up 主,張三和李四,粉絲1關(guān)注了張三、粉絲2關(guān)注了張三和李四、粉絲3也關(guān)注了張三和李四,假設(shè)此時張三要發(fā)送消息,它所發(fā)送的消息會直接推送到所有粉絲的收件箱里面去,然后對收件箱里面的消息進(jìn)行排序,此時粉絲可以直接從收件箱里面讀取消息

此種模式的優(yōu)點是延遲非常的低,彌補(bǔ)了拉模式的缺點。缺點是由于沒有了發(fā)件箱,不得不把消息發(fā)送給每一個粉絲,內(nèi)存占用會很高,即一個消息要寫N份 

3.3.3 推拉結(jié)合模式

推拉結(jié)合模式也叫做讀寫混合,兼具推和拉兩種模式的優(yōu)點。假設(shè)現(xiàn)在有一個大 V 和普通人張三,還有兩個普通粉絲和一個活躍粉絲,每個粉絲都有自己的收件箱。假設(shè)此時普通人張三要發(fā)送消息,他的粉絲很少,就可以采用推模式,即直接將消息推送到每一個粉絲的收件箱里面。

而大 V 則不一樣,大 的粉絲很多,雖然粉絲多,但是粉絲存在差異,有活躍粉絲和一般粉絲。針對于活躍粉絲采用推模式,而針對于一般粉絲采取拉模式,因為大 V 需要有一個發(fā)件箱。

這種推拉結(jié)合的模式,既節(jié)省了內(nèi)存又照顧了活躍用戶的感受

3.3.4 總結(jié)

3.4 Feed 流的分頁問題

Feed 流中的數(shù)據(jù)會不斷更新,所以數(shù)據(jù)的角標(biāo)也在變化,因此不能采用傳統(tǒng)的分頁模式。接下來我們分析下原因。

以下圖為例,橫線代表時間軸,此時時間來到了 t1 時刻,收件箱里面已經(jīng)有了 10 條消息了,數(shù)字越大代表越新,讀取第一頁數(shù)據(jù)沒啥問題。

此時來到了 t2 時刻,有人插入了一條新的數(shù)據(jù),此時我們的 11 數(shù)據(jù)就跑到了最前面去了

等到來到了 t3 時刻,此時需要讀取第二頁的內(nèi)容,此時就會出現(xiàn)重復(fù)讀取的問題,分頁就出現(xiàn)了混亂。

這就是 Feed 流不能采用傳統(tǒng)的分頁模式的原因。

3.5 Feed 流的滾動分頁

此時就需要采用 Feed 流的滾動分頁,即記錄每次分頁的最后一條,下次再從這個位置開始查。第一次讀取時 lastId 設(shè)置成無窮大就可以了,如下圖:

到了 t2 時刻有人插入了新的數(shù)據(jù) 11,如下圖:

等到 t3 時刻,讀取第二頁的時候,讓 lastId = 6,向后查 條就不會出現(xiàn)問題了,如下圖:

此時的查詢是不依賴于腳標(biāo)的,所以數(shù)據(jù)不受影響。所以只能使用 zset 結(jié)構(gòu)。

3.6 需求描述

基于推模式實現(xiàn)關(guān)注推送功能:

1、修改新增探店筆記的業(yè)務(wù),在保存 blog 到數(shù)據(jù)庫的同時,推送到粉絲的收件箱

2、收件箱滿足可以根據(jù)時間戳排序,必須用 Redis 的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)

3、查詢收件箱數(shù)據(jù)時,可以實現(xiàn)分頁查詢

3.7 代碼實現(xiàn)

首先完成修改新增探店筆記的業(yè)務(wù),在保存 blog 到數(shù)據(jù)庫的同時,推送到粉絲的收件箱功能,涉及到的 controller 層代碼如下:

@RestController
@RequestMapping("/blog")
public class BlogController {

    @Resource
    private IBlogService blogService;

    @PostMapping
    public Result saveBlog(@RequestBody Blog blog) {
        return blogService.saveBlog(blog);
    }
}

涉及到的 service 層代碼如下:

@Service
public class BlogServiceImpl extends ServiceImpl<BlogMapper, Blog> implements IBlogService {

    @Resource
    private IUserService userService;

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Resource
    IFollowService followService;

    @Override
    public Result saveBlog(Blog blog) {
        // 1.獲取登錄用戶
        UserDTO user = UserHolder.getUser();
        blog.setUserId(user.getId());
        // 2.保存探店筆記
        boolean isSuccess = save(blog);
        if(!isSuccess){
            return Result.fail("新增筆記失敗!");
        }
        // 3.查詢筆記作者的所有粉絲 select * from tb_follow where follow_user_id = ?
        List<Follow> follows = followService.query().eq("follow_user_id", user.getId()).list();
        // 4.推送筆記id給所有粉絲
        for (Follow follow : follows) {
            // 4.1.獲取粉絲id
            Long userId = follow.getUserId();
            // 4.2.推送
            String key = "feed:" + userId;
            stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());
        }
        // 5.返回id
        return Result.ok(blog.getId());
    }
	
}

接下來完成剩下的兩小點需求,界面請求的參數(shù)如下:

涉及到的 controller 層代碼如下:

@RestController
@RequestMapping("/blog")
public class BlogController {

    @Resource
    private IBlogService blogService;

    @PostMapping
    public Result saveBlog(@RequestBody Blog blog) {
        return blogService.saveBlog(blog);
    }

    @GetMapping("/of/follow")
    public Result queryBlogOfFollow(
            @RequestParam("lastId") Long max, @RequestParam(value = "offset", defaultValue = "0") Integer offset){
        return blogService.queryBlogOfFollow(max, offset);
    }
}

涉及到的 service 層代碼如下:

    @Override
    public Result queryBlogOfFollow(Long max, Integer offset) {
        // 1.獲取當(dāng)前用戶
        Long userId = UserHolder.getUser().getId();
        // 2.查詢收件箱 ZREVRANGEBYSCORE key Max Min LIMIT offset count
        String key = FEED_KEY + userId;
        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeByScoreWithScores(key, 0, max, offset, 2);
        // 3.非空判斷
        if (typedTuples == null || typedTuples.isEmpty()) {
            return Result.ok();
        }
        // 4.解析數(shù)據(jù):blogId、minTime(時間戳)、offset
        List<Long> ids = new ArrayList<>(typedTuples.size());
        long minTime = 0; // 2
        int os = 1; // 2
        for (ZSetOperations.TypedTuple<String> tuple : typedTuples) { // 5 4 4 2 2
            // 4.1.獲取id
            ids.add(Long.valueOf(tuple.getValue()));
            // 4.2.獲取分?jǐn)?shù)(時間戳)
            long time = tuple.getScore().longValue();
            if(time == minTime){
                os++;
            }else{
                minTime = time;
                os = 1;
            }
        }
        // 5.根據(jù)id查詢blog
        String idStr = StrUtil.join(",", ids);
        List<Blog> blogs = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();

        for (Blog blog : blogs) {
            // 5.1.查詢blog有關(guān)的用戶
            queryBlogUser(blog);
            // 5.2.查詢blog是否被點贊
            isBlogLiked(blog);
        }

        // 6.封裝并返回
        ScrollResult r = new ScrollResult();
        r.setList(blogs);
        r.setOffset(os);
        r.setMinTime(minTime);

        return Result.ok(r);
    }

封裝返回給前端的實體類如下:

@Data
public class ScrollResult {
    private List<?> list;
    private Long minTime;
    private Integer offset;
}

到此這篇關(guān)于 Redis 實現(xiàn)好友關(guān)注和關(guān)注推送的示例代碼的文章就介紹到這了,更多相關(guān) Redis 好友關(guān)注和關(guān)注推送內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

相關(guān)文章

  • redis數(shù)據(jù)一致性的實現(xiàn)示例

    redis數(shù)據(jù)一致性的實現(xiàn)示例

    所謂的redis數(shù)據(jù)一致性即當(dāng)進(jìn)行修改或者保存、刪除之后,redis中的數(shù)據(jù)也應(yīng)該進(jìn)行相應(yīng)變化,本文主要介紹了redis數(shù)據(jù)一致性,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 如何利用Redis分布式鎖實現(xiàn)控制并發(fā)操作

    如何利用Redis分布式鎖實現(xiàn)控制并發(fā)操作

    這篇文章主要介紹了如何利用Redis分布式鎖實現(xiàn)控制并發(fā)操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 利用Redis實現(xiàn)防止接口重復(fù)提交功能

    利用Redis實現(xiàn)防止接口重復(fù)提交功能

    大家好,本篇文章主要講的是利用Redis實現(xiàn)防止接口重復(fù)提交功能,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Redis的五種基本數(shù)據(jù)類型解讀(String、List、Hash、Set、ZSet)

    Redis的五種基本數(shù)據(jù)類型解讀(String、List、Hash、Set、ZSet)

    本文詳細(xì)介紹了Redis的五種基本數(shù)據(jù)類型(String、List、Hash、Set、ZSet),包括它們的常用指令、應(yīng)用場景以及在Java中的實現(xiàn),通過實操示例,幫助讀者更好地理解和使用Redis
    2025-12-12
  • CentOS7.5使用mysql_multi方式安裝MySQL5.7.28多實例(詳解)

    CentOS7.5使用mysql_multi方式安裝MySQL5.7.28多實例(詳解)

    這篇文章主要介紹了CentOS7.5使用mysql_multi方式安裝MySQL5.7.28多實例,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-01-01
  • Redis解決緩存擊穿問題的兩種方法

    Redis解決緩存擊穿問題的兩種方法

    緩存擊穿問題也叫熱點Key問題,就是?個被高并發(fā)訪問并且緩存重建業(yè)務(wù)較復(fù)雜的key突然失效了,無數(shù)的請求訪問會在瞬間給數(shù)據(jù)庫帶來巨大的沖擊,本文給大家介紹了Redis解決緩存擊穿問題的兩種方法,需要的朋友可以參考下
    2025-03-03
  • Redis集群水平擴(kuò)展、集群中添加以及刪除節(jié)點的操作

    Redis集群水平擴(kuò)展、集群中添加以及刪除節(jié)點的操作

    這篇文章主要介紹了Redis集群水平擴(kuò)展、集群中添加以及刪除節(jié)點的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-03-03
  • caffeine_redis自定義二級緩存

    caffeine_redis自定義二級緩存

    這篇文章詳細(xì)介紹了caffeine_redis 自定義二級緩存,文中有相關(guān)的背景前提與出現(xiàn)的問題,感興趣的同學(xué)可以參考一下
    2023-04-04
  • Spring?Boot?整合Redis?實現(xiàn)優(yōu)惠卷秒殺?一人一單功能

    Spring?Boot?整合Redis?實現(xiàn)優(yōu)惠卷秒殺?一人一單功能

    這篇文章主要介紹了Spring?Boot?整合Redis?實現(xiàn)優(yōu)惠卷秒殺?一人一單,在分布式系統(tǒng)下,高并發(fā)的場景下,會出現(xiàn)此類庫存超賣問題,本篇文章介紹了采用樂觀鎖來解決,需要的朋友可以參考下
    2022-09-09
  • Redis優(yōu)惠券秒殺解決方案

    Redis優(yōu)惠券秒殺解決方案

    這篇文章主要介紹了Redis解決優(yōu)惠券秒殺應(yīng)用案例,本文先講了搶購問題,指出其中會出現(xiàn)的多線程問題,提出解決方案采用悲觀鎖和樂觀鎖兩種方式進(jìn)行實現(xiàn),然后發(fā)現(xiàn)在搶購過程中容易出現(xiàn)一人多單現(xiàn)象,需要的朋友可以參考下
    2022-12-12

最新評論

桑日县| 阿坝| 柘荣县| 志丹县| 靖江市| 屯门区| 北宁市| 郴州市| 楚雄市| 华宁县| 英吉沙县| 丽水市| 海城市| 饶阳县| 平度市| 安化县| 望奎县| 印江| 西乡县| 股票| 榆社县| 乌鲁木齐市| 苏州市| 逊克县| 略阳县| 柳江县| 枣强县| 彭阳县| 镇康县| 宁化县| 华宁县| 丰县| 永平县| 临清市| 榆社县| 西安市| 绥宁县| 德令哈市| 封开县| 固安县| 水富县|