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

Redis 實現好友關注和關注推送的示例代碼

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

一、關注和取關

1.1 簡介

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

1.2 需求描述

實現兩個接口:關注和取關接口、判斷是否關注的接口

1.3 代碼實現

涉及到的 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.判斷到底是關注還是取關
        if (isFollow) {
            // 2.關注,新增數據
            Follow follow = new Follow();
            follow.setUserId(userId);
            follow.setFollowUserId(followUserId);
            boolean isSuccess = save(follow);
            if (isSuccess) {
                // 把關注用戶的id,放入redis的set集合 sadd userId followerUserId
                stringRedisTemplate.opsForSet().add(key, followUserId.toString());
            }
        } else {
            // 3.取關,刪除 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) {
                // 把關注用戶的id從Redis集合中移除
                stringRedisTemplate.opsForSet().remove(key, followUserId.toString());
            }
        }
        return Result.ok();
    }

    @Override
    public Result isFollow(Long followUserId) {
        // 1.獲取登錄用戶
        Long userId = UserHolder.getUser().getId();
        // 2.查詢是否關注 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);
    }
}

二、共同關注

2.1 簡介

點擊博主頭像,可以進入博主首頁:

2.2 需求描述

利用 Redis 中恰當的數據結構,實現共同關注功能。在博主個人頁面展示出當前用戶與博主的共同好友。

2.3 代碼實現

涉及到的 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.獲取當前用戶
        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);
    }
}

三、關注推送

3.1 簡介

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

3.2 Feed 流的模式

3.2.1 TimeLine

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

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

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

3.2.2 智能排序

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

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

缺點:如果算法不精準,可能起到反作用

3.3 實現方案

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

3.3.1 拉模式

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

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

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

3.3.2 推模式

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

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

3.3.3 推拉結合模式

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

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

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

3.3.4 總結

3.4 Feed 流的分頁問題

Feed 流中的數據會不斷更新,所以數據的角標也在變化,因此不能采用傳統的分頁模式。接下來我們分析下原因。

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

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

等到來到了 t3 時刻,此時需要讀取第二頁的內容,此時就會出現重復讀取的問題,分頁就出現了混亂。

這就是 Feed 流不能采用傳統的分頁模式的原因。

3.5 Feed 流的滾動分頁

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

到了 t2 時刻有人插入了新的數據 11,如下圖:

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

此時的查詢是不依賴于腳標的,所以數據不受影響。所以只能使用 zset 結構。

3.6 需求描述

基于推模式實現關注推送功能:

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

2、收件箱滿足可以根據時間戳排序,必須用 Redis 的數據結構實現

3、查詢收件箱數據時,可以實現分頁查詢

3.7 代碼實現

首先完成修改新增探店筆記的業(yè)務,在保存 blog 到數據庫的同時,推送到粉絲的收件箱功能,涉及到的 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());
    }
	
}

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

涉及到的 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.獲取當前用戶
        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.解析數據: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.獲取分數(時間戳)
            long time = tuple.getScore().longValue();
            if(time == minTime){
                os++;
            }else{
                minTime = time;
                os = 1;
            }
        }
        // 5.根據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有關的用戶
            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;
}

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

相關文章

  • redis數據一致性的實現示例

    redis數據一致性的實現示例

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

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

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

    利用Redis實現防止接口重復提交功能

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

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

    本文詳細介紹了Redis的五種基本數據類型(String、List、Hash、Set、ZSet),包括它們的常用指令、應用場景以及在Java中的實現,通過實操示例,幫助讀者更好地理解和使用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è)務較復雜的key突然失效了,無數的請求訪問會在瞬間給數據庫帶來巨大的沖擊,本文給大家介紹了Redis解決緩存擊穿問題的兩種方法,需要的朋友可以參考下
    2025-03-03
  • Redis集群水平擴展、集群中添加以及刪除節(jié)點的操作

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

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

    caffeine_redis自定義二級緩存

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

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

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

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

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

最新評論

阿克| 南京市| 咸宁市| 高尔夫| 淮阳县| 永年县| 崇仁县| 秭归县| 郧西县| 广东省| 龙川县| 贵定县| 霍林郭勒市| 青神县| 榆林市| 安平县| 特克斯县| 沧州市| 察哈| 玉树县| 沂水县| 通化县| 海晏县| 湘潭市| 绥棱县| 云南省| 越西县| 桃江县| 颍上县| 丽江市| 宁安市| 贞丰县| 阿城市| 和田县| 苏州市| 远安县| 石河子市| 扎鲁特旗| 潼南县| 鞍山市| 河源市|