Redis实现朋友圈,微博等Feed流功能,实现Feed流微服务(业务场景、实现思路和环境搭建)_微博feed流设计

业务场景

在互联网领域尤其现在的移动互联网时代Feed流产品是非常常见的比如我们每天都会用到的朋友圈微博就是一种非常典型的Feed流产品还有图片分享网站Pinterest花瓣网等又是另一种形式的Feed流产品。除此之外很多App的都会有一个模块要么叫动态要么叫消息广场这些也是Feed流产品可以说Feed流产品是遍布天下所有的App中。
在这里插入图片描述

Feed流相关概念

我们在讲如何设计Feed流系统之前先来看一下Feed流中的一些概念

  • FeedFeed流中的每一条状态或者消息都是Feed比如朋友圈中的一个状态就是一个Feed微博中的一条微博就是一个Feed。
  • Feed流持续更新并呈现给用户内容的信息流。每个人的朋友圈微博关注页等等都是一个Feed流。
  • TimelineTimeline其实是一种Feed流的类型微博朋友圈都是Timeline类型的Feed流但是由于Timeline类型出现最早使用最广泛最为人熟知有时候也用Timeline来表示Feed流。
  • 关注页Timeline展示其他人Feed消息的页面比如朋友圈微博的首页等。
  • 个人页Timeline展示自己发送过的Feed消息的页面比如微信中的相册微博的个人页等。
  • 总结来说Feed是将用户主动订阅的若干消息源组合在一起形成内容聚合器帮助用户持续地获取最新的订阅源内容。Feed流即持续更新并呈现给用户内容的信息流。

Feed流特征

Feed流系统有一些非常典型的特点比如

  • 多账号内容流Feed流系统中肯定会存在成千上万的账号账号之间可以关注取关加好友和拉黑等操作。只要满足这一条那么就可以当做Feed流系统来设计。
  • 非稳定的账号关系由于存在关注取关等操作所以系统中的用户之间的关系就会一直在变化是一种非稳定的状态。
  • 读写比例100:1读写严重不平衡读多写少一般读写比例在101甚至1001以上。
  • 消息必达性要求高比如发送了一条朋友圈后结果部分朋友看到了部分朋友没看到这就很有问题如果偏偏女朋友没看到那么可能会产生很严重的感情矛盾后果很严重。

Feed流分类

Feed流的分类有很多种但最常见的分类有两种

  • Timeline按发布的时间顺序排序先发布的先看到后发布的排列在最顶端类似于微信朋友圈微博等。这也是一种最常见的形式。产品如果选择Timeline类型那么就是认为Feed流中的Feed不多但是每个Feed都很重要都需要用户看到。
  • Rank按某个非时间的因子排序一般是按照用户的喜好度排序用户最喜欢的排在最前面次喜欢的排在后面。这种一般假定用户可能看到的Feed非常多而用户花费在这里的时间有限那么就为用户选择出用户最想看的Top N结果场景的应用场景有图片分享、新闻推荐类、商品推荐等。

实现思路

解决Feed流最核心的两个问题一个是存储另一个是推送
① 存储
因为该项目中Feed比较简单就类比于空间说说因此可以使用MySQL关系型数据库存储如果对于数据结构比较复杂的Feed流就要使用NoSQL数据库这样存储更方便与高效比如MongoDB或者HBase。
② 推送在推送方案里面的有三种方案分别是

  • 拉方案也称为读扩散用户主动去拉取关注人的Feed内容
  • 推方案也成为写扩散当用户添加Feed时会自动将Feed通知给关注的人优选。使用Redis Sorted Sets方便按时间排序Timeline维护粉丝的Feed集合当博主添加Feed时主动将内容推送到粉丝的Feed集合中这样用户可以很方便快速从集合中读取
  • 推拉结合比如微博大部分用户的账号关系都是几百个但是有个别用户是1000万以上。

环境搭建

数据库表结构

CREATE TABLE `t_feeds` (
	`id` INT ( 11 ) NOT NULL AUTO_INCREMENT,
	`content` VARCHAR ( 500 ) CHARACTER 
	SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '内容',
	`fk_user_id` INT ( 11 ) NULL DEFAULT NULL,
	`praise_amount` INT ( 11 ) NULL DEFAULT NULL COMMENT '点赞数量',
	`comment_amount` INT ( 11 ) NULL DEFAULT NULL COMMENT '评论数量',
	`fk_restaurant_id` INT ( 11 ) NULL DEFAULT NULL  COMMENT '餐厅外键',
	`create_date` datetime NULL DEFAULT NULL,
	`update_date` datetime NULL DEFAULT NULL,
	`is_valid` TINYINT ( 1 ) NULL DEFAULT NULL,
	PRIMARY KEY ( `id` ) 
) ENGINE = INNODB DEFAULT CHARACTER 
SET = utf8mb4 COLLATE = utf8mb4_general_ci AUTO_INCREMENT = 14 ROW_FORMAT = COMPACT;

新建Feeds功能微服务ms-feeds

pom依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/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-feeds</artifactId>

    <dependencies>
        <!-- eureka client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- spring web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- spring data redis -->
        <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: 7005 # 端口

spring:
  application:
    name: ms-feeds # 应用名
  # 数据库
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://127.0.0.1:3306/seckill?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useUnicode=true&useSSL=false
  # Redis
  redis:
    port: 6379
    host: localhost
    timeout: 3000
    password: 123456
    database: 2
  # Swagger
  swagger:
    base-package: com.zjq.feeds
    title: Feed功能微服务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-users-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'

启动类

package com.zjq.feeds;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * Feeds功能微服务
 * @author zjq
 */
@MapperScan("com.zjq.feeds.mapper")
@SpringBootApplication
public class FeedsApplication {

    public static void main(String[] args) {
        SpringApplication.run(FeedsApplication.class);
    }

}

配置类 RedisTemplateConfiguration

/**
 * RedisTemplate配置类
 *
 * @author zjq
 */
@Configuration
public class RedisTemplateConfiguration {

    /**
     * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
     *
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        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配置类 RestTemplateConfiguration

/**
 * RestTemplate 配置类
 * @author zjq
 */
@Configuration
public class RestTemplateConfiguration {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
        converter.setSupportedMediaTypes(Collections.singletonList(MediaType.TEXT_PLAIN));
        restTemplate.getMessageConverters().add(converter);
        return restTemplate;
    }

}

Feeds 实体类

/**
 * Feed信息类
 * @author zjq
 */
@Getter
@Setter
@ApiModel(description = "Feed信息类")
public class Feeds extends BaseModel {

    @ApiModelProperty("内容")
    private String content;
    @ApiModelProperty("用户id")
    private Integer fkUserId;
    @ApiModelProperty("点赞")
    private int praiseAmount;
    @ApiModelProperty("评论")
    private int commentAmount;
    @ApiModelProperty("关联的餐厅")
    private Integer fkRestaurantId;

}

FeedsVO 响应类

在这里插入图片描述

后续博文我会讲解如何实现添加 Feed 信息、删除 Feed 信息、关注/取关时处理用户相关Feed、分页获取关注的 Feed 数据等功能。

本文内容到此结束了
如有收获欢迎点赞👍收藏💖关注✔️您的鼓励是我最大的动力。
如有错误❌疑问💬欢迎各位指出。
主页共饮一杯无的博客汇总👨‍💻

保持热爱奔赴下一场山海。🏃🏃🏃

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
    标签: redis

    “Redis实现朋友圈,微博等Feed流功能,实现Feed流微服务(业务场景、实现思路和环境搭建)_微博feed流设计” 的相关文章