在互联网领域,尤其现在的移动互联网时代,Feed流产品是非常常见的,比如我们每天都会用到的朋友圈,微博,就是一种非常典型的Feed流产品,还有图片分享网站Pinterest,花瓣网等又是另一种形式的Feed流产品。除此之外,很多App的都会有一个模块,要么叫动态,要么叫消息广场,这些也是Feed流产品,可以说,Feed流产品是遍布天下所有的App中。
我们在讲如何设计Feed流系统之前,先来看一下Feed流中的一些概念:
Feed流系统有一些非常典型的特点,比如:
Feed流的分类有很多种,但最常见的分类有两种:
解决Feed流最核心的两个问题:一个是存储,另一个是推送 ① 存储 因为该项目中Feed比较简单,就类比于空间说说,因此可以使用MySQL关系型数据库存储,如果对于数据结构比较复杂的Feed流就要使用NoSQL数据库,这样存储更方便与高效,比如MongoDB或者HBase。 ② 推送,在推送方案里面的,有三种方案,分别是:
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;
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);
}
}
/**
* 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;
}
}
/**
* 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;
}
}
/**
* 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;
}
后续博文我会讲解如何实现添加 Feed 信息、删除 Feed 信息、关注/取关时处理用户相关Feed、分页获取关注的 Feed 数据等功能。