社交网络平台技术架构与实现指南

社交网络平台技术架构与实现指南

【免费下载链接】WebFundamentals Former git repo for WebFundamentals on developers.google.com 【免费下载链接】WebFundamentals 项目地址: https://gitcode.com/gh_mirrors/we/WebFundamentals

1. 社交网络系统架构概述

现代社交网络平台需要支持千万级用户并发访问、实时数据传输和复杂社交关系管理。基于WebFundamentals开源项目,我们构建了一套完整的社交网络技术栈,涵盖实时通信、数据存储、内容分发和安全防护等核心模块。

1.1 系统架构分层

mermaid

1.2 核心技术栈

  • 前端框架:React + Redux(组件化开发,状态管理)
  • 后端技术:Node.js + Express(高性能API服务)
  • 实时通信:WebSocket + Socket.IO(双向实时通信)
  • 数据库:PostgreSQL(关系型数据)+ Redis(缓存)+ MongoDB(非结构化内容)
  • 搜索引擎:Elasticsearch(全文搜索与内容索引)
  • 部署环境:Docker + Kubernetes(容器化部署与编排)

2. 实时通信系统实现

社交网络的核心在于用户间的实时互动,我们采用WebSocket技术构建低延迟的实时通信系统。

2.1 WebSocket服务端实现

// WebSocket服务器集群实现
const WebSocket = require('ws');
const redis = require('redis');
const jwt = require('jsonwebtoken');

// 连接到Redis用于跨服务器通信
const pubClient = redis.createClient({ host: 'redis-server' });
const subClient = redis.createClient({ host: 'redis-server' });

// 创建WebSocket服务器集群
class WebSocketServer {
  constructor() {
    this.servers = new Map(); // 存储各服务器实例
    this.connectionCount = 0;
    this.redisPubSub = new RedisPubSub(pubClient, subClient);
    
    // 初始化WebSocket服务器
    this.init();
  }
  
  init() {
    // 创建主WebSocket服务器
    this.mainServer = new WebSocket.Server({
      port: process.env.WS_PORT || 8080,
      maxPayload: 1024 * 100, // 100KB最大消息大小
      perMessageDeflate: { threshold: 1024 } // 压缩阈值
    });
    
    // 处理新连接
    this.mainServer.on('connection', (ws, req) => {
      this.handleConnection(ws, req);
    });
    
    // 集群间消息广播
    this.redisPubSub.on('message', (channel, message) => {
      const data = JSON.parse(message);
      // 向除发送者外的其他用户广播消息
      if (data.userId) {
        this.broadcastToUser(data.userId, data.type, data.message);
      } else {
        this.broadcastToChannel(data.channel, data.type, data.message);
      }
    });
    
    console.log(`WebSocket server started on port ${process.env.WS_PORT || 8080}`);
  }
  
  // 处理新连接
  async handleConnection(ws, req) {
    this.connectionCount++;
    console.log(`New WebSocket connection. Total: ${this.connectionCount}`);
    
    // 从请求URL获取认证令牌
    const token = new URL(req.url, `http://${req.headers.host}`).searchParams.get('token');
    let userId = null;
    
    try {
      // 验证JWT令牌
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      userId = decoded.userId;
      
      // 存储用户连接信息
      this.storeUserConnection(userId, ws);
      
      // 处理消息
      ws.on('message', (data) => this.handleMessage(ws, userId, data));
      
      // 处理断开连接
      ws.on('close', () => {
        this.connectionCount--;
        this.removeUserConnection(userId, ws);
        console.log(`WebSocket connection closed. Total: ${this.connectionCount}`);
      });
      
      // 发送连接成功消息
      ws.send(JSON.stringify({
        type: 'connection_established',
        userId,
        timestamp: Date.now()
      }));
      
    } catch (error) {
      console.error('WebSocket authentication failed:', error);
      ws.close(4001, 'Authentication failed');
    }
  }
  
  // 处理接收到的消息
  handleMessage(ws, userId, data) {
    try {
      const message = JSON.parse(data.toString());
      console.log(`Received message from user ${userId}:`, message);
      
      // 根据消息类型处理
      switch (message.type) {
        case 'direct_message':
          this.handleDirectMessage(userId, message.recipientId, message.content);
          break;
        case 'typing_status':
          this.broadcastToUser(message.recipientId, 'typing', { 
            userId, 
            status: message.status 
          });
          break;
        case 'presence_status':
          this.broadcastToUser(message.recipientId, 'presence', { 
            userId, 
            status: message.status 
          });
          break;
        default:
          console.warn(`Unknown message type: ${message.type}`);
      }
    } catch (error) {
      console.error('Error processing message:', error);
      ws.send(JSON.stringify({
        type: 'error',
        message: 'Invalid message format',
        timestamp: Date.now()
      }));
    }
  }
  
  // 存储用户连接
  storeUserConnection(userId, ws) {
    // 这里可以使用Redis存储活跃连接,实现跨服务器共享
    this.redisPubSub.publish(`user:${userId}:connections`, JSON.stringify({
      type: 'add',
      wsId: ws._ultron.id,
      timestamp: Date.now()
    }));
  }
  
  // 广播消息到用户
  async broadcastToUser(userId, type, message) {
    const connections = await this.redisPubSub.get(`user:${userId}:connections`);
    if (connections.length > 0) {
      connections.forEach(wsId => {
        const ws = this.servers.get(wsId);
        if (ws && ws.readyState === WebSocket.OPEN) {
          ws.send(JSON.stringify({
            type,
            message,
            timestamp: Date.now()
          }));
        }
      });
    }
  }
  
  // 广播消息到频道
  async broadcastToChannel(channel, type, message) {
    const subscribers = await this.redisPubSub.get(`channel:${channel}:subscribers`);
    if (subscribers.length > 0) {
      subscribers.forEach(wsId => {
        const ws = this.servers.get(wsId);
        if (ws && ws.readyState === WebSocket.OPEN) {
          ws.send(JSON.stringify({
            type,
            message,
            channel,
            timestamp: Date.now()
          }));
        }
      });
    }
  }
  
  // 关闭服务器
  close() {
    this.mainServer.close(() => {
      console.log('WebSocket server closed');
    });
  }
}

// 主应用入口
const wss = new WebSocketServer();

// 处理进程退出
process.on('SIGINT', () => {
  wss.close();
  process.exit(0);
});

2.2 前端WebSocket客户端实现

// React组件中的WebSocket客户端
import React, { useState, useEffect, useRef } from 'react';
import io from 'socket.io-client';

const ChatComponent = ({ userId, recipientId }) => {
  const [messages, setMessages] = useState([]);
  const [newMessage, setNewMessage] = useState('');
  const [typingStatus, setTypingStatus] = useState('');
  const [isConnected, setIsConnected] = useState(false);
  const [socket, setSocket] = useState(null);
  const messagesEndRef = useRef(null);
  
  // 连接WebSocket服务器
  useEffect(() => {
    const token = localStorage.getItem('auth_token');
    const socket = io(`/ws?token=${token}`, {
      reconnection: true,
      reconnectionAttempts: 5,
      reconnectionDelay: 1000,
      path: '/ws/socket.io'
    });
    
    setSocket(socket);
    
    // 连接状态
    socket.on('connect', () => {
      console.log('Connected to WebSocket server');
      setIsConnected(true);
      // 加入用户一对一聊天频道
      socket.emit('join_channel', { 
        channel: `chat:${userId}:${recipientId}`,
        user: userId
      });
    });
    
    socket.on('disconnect', () => {
      console.log('Disconnected from WebSocket server');
      setIsConnected(false);
    });
    
    socket.on('error', (error) => {
      console.error('WebSocket error:', error);
      // 处理错误,可尝试自动重连
    });
    
    // 接收消息
    socket.on('direct_message', (data) => {
      if (data.message && data.message.userId === recipientId) {
        setMessages(prev => [...prev, {
          id: data.message.id,
          userId: data.message.userId,
          content: data.message.content,
          timestamp: data.message.timestamp,
          type: data.message.type || 'text'
        }]);
      }
    });
    
    // 接收用户状态更新
    socket.on('typing', (data) => {
      if (data.userId === recipientId) {
        setTypingStatus(data.status === 'typing' ? data.userId : '');
      }
    });
    
    // 清理函数
    return () => {
      socket.disconnect();
      socket.off('connect');
      socket.off('disconnect');
      socket.off('direct_message');
      socket.off('typing');
    };
  }, [userId, recipientId]);
  
  // 自动滚动到底部
  useEffect(() => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  }, [messages]);
  
  // 发送消息
  const handleSendMessage = () => {
    if (!newMessage.trim() || !socket) return;
    
    socket.emit('send_message', {
      type: 'direct_message',
      recipientId,
      content: {
        id: Date.now().toString(),
        userId,
        content: newMessage,
        timestamp: Date.now(),
        type: 'text'
      }
    });
    
    setNewMessage('');
  };
  
  // 监听输入状态
  const handleInputChange = (e) => {
    setNewMessage(e.target.value);
    
    // 发送输入状态
    if (e.target.value.length > 0) {
      socket.emit('typing_status', {
        recipientId,
        status: 'typing'
      });
    } else {
      socket.emit('typing_status', {
        recipientId,
        status: 'idle'
      });
    }
  };
  
  return (
    <div className="chat-container">
      <div className="chat-header">
        <h3>与 {getUserName(recipientId)} 的聊天</h3>
        <div className="connection-status">
          {isConnected ? (
            <span className="status-indicator online"></span> 在线
          ) : (
            <span className="status-indicator offline"></span> 离线
          )}
        </div>
      </div>
      
      <div className="chat-messages">
        {messages.map(message => (
          <div 
            key={message.id} 
            className={`message ${message.userId === userId ? 'sent' : 'received'}`}
          >
            <div className="message-content">
              {message.content}
            </div>
            <div className="message-time">
              {formatTime(message.timestamp)}
            </div>
          </div>
        ))}
        {typingStatus && (
          <div className="typing-indicator">
            {getUserName(typingStatus)} 正在输入...
          </div>
        )}
        <div ref={messagesEndRef} />
      </div>
      
      <div className="chat-input">
        <input
          type="text"
          value={newMessage}
          onChange={handleInputChange}
          placeholder="输入消息..."
          onKeyDown={(e) => {
            if (e.key === 'Enter' && !e.shiftKey) {
              e.preventDefault();
              handleSendMessage();
            }
          }}
        />
        <button onClick={handleSendMessage}>发送</button>
      </div>
    </div>
  );
};

2.3 WebSocket集群通信优化

为支持千万级用户连接,我们采用Redis Pub/Sub实现WebSocket服务器间的消息广播,避免单点瓶颈:

  1. Redis Pub/Sub实现跨服务器通信
  2. 连接分片:按用户ID哈希路由到不同服务器
  3. 心跳检测与自动重连
  4. 连接池化与资源复用
  5. 断线重连机制与会话恢复

3. 社交关系与图谱系统

社交网络的核心是用户间的连接关系,我们设计了高效的社交图谱数据模型和查询算法。

3.1 社交关系数据模型

// 社交关系数据模型设计
class SocialGraphService {
  constructor() {
    this.userRepository = new UserRepository();
    this.connectionRepository = new ConnectionRepository();
    this.neo4jClient = neo4j.driver(
      process.env.NEO4J_URI,
      neo4j.auth.basic(process.env.NEO4J_USER, process.env.NEO4J_PASSWORD)
    );
  }
  
  // 添加好友关系
  async addFriendship(userId, friendId, options = {}) {
    const session = this.neo4jClient.session();
    
    try {
      // 使用事务确保数据一致性
      await session.run(
        `MATCH (u1:User {id: $userId}), (u2:User {id: $friendId})
         MERGE (u1)-[:FRIENDS_WITH {status: 'accepted', since: timestamp()}]->(u2)
         MERGE (u2)-[:FRIENDS_WITH {status: 'accepted', since: timestamp()}]->(u1)
         RETURN u1, u2`,
        { userId, friendId }
      );
      
      // 记录关系创建事件
      await this.connectionRepository.createConnection({
        userId,
        targetId: friendId,
        type: 'friend',
        status: 'accepted',
        createdAt: new Date()
      });
      
      // 通知双方
      this.notificationService.sendFriendshipNotification(userId, friendId);
      
      return { success: true, message: 'Friendship established' };
    } catch (error) {
      console.error('Error creating friendship:', error);
      throw error;
    } finally {
      session.close();
    }
  }
  
  // 获取用户好友列表(带分页)
  async getUserFriends(userId, options = {}) {
    const { page = 1, limit = 20, sort = 'recent' } = options;
    const offset = (page - 1) * limit;
    
    const session = this.neo4jClient.session();
    
    try {
      const query = `
        MATCH (u:User {id: $userId})-[:FRIENDS_WITH]-(friend:User)
        RETURN friend.id AS id, friend.name AS name, friend.avatar AS avatar, 
               friend.bio AS bio, friend.friendCount AS friendCount,
               friend.followingCount AS followingCount, friend.followerCount AS followerCount,
               (friend.lastActive > timestamp() - 86400000 * 7) AS isOnline,
               relationship.status AS status, 
               relationship.since AS since
        ORDER BY 
          CASE $sort 
            WHEN 'recent' THEN relationship.since 
            WHEN 'popular' THEN friend.friendCount 
            ELSE friend.name 
          END DESC
        SKIP $offset
        LIMIT $limit
      `;
      
      const result = await session.run(query, { 
        userId, 
        sort, 
        offset, 
        limit 
      });
      
      const friends = result.records.map(record => {
        const node = record.get('friend');
        const rel = record.get('relationship');
        
        return {
          id: node.get('id'),
          name: node.get('name'),
          avatar: node.get('avatar'),
          bio: node.get('bio'),
          friendCount: node.get('friendCount'),
          followingCount: node.get('followingCount'),
          followerCount: node.get('followerCount'),
          isOnline: node.get('isOnline'),
          status: rel.get('status'),
          since: new Date(rel.get('since'))
        };
      });
      
      // 获取总数
      const countResult = await session.run(
        `MATCH (u:User {id: $userId})-[:FRIENDS_WITH]-(friend:User)
         RETURN COUNT(friend) AS total`,
        { userId }
      );
      
      const total = countResult.records[0].get('total');
      
      return {
        friends,
        pagination: {
          total,
          page,
          limit,
          pages: Math.ceil(total / limit)
        }
      };
    } catch (error) {
      console.error('Error fetching friends:', error);
      throw error;
    } finally {
      session.close();
    }
  }
  
  // 获取用户推荐好友(二度连接)
  async getFriendRecommendations(userId, limit = 10) {
    const session = this.neo4jClient.session();
    
    try {
      const query = `
        MATCH (u:User {id: $userId})-[:FRIENDS_WITH]-(friend:User),
              (friend)-[:FRIENDS_WITH]-(recommendation:User)
        WHERE recommendation.id NOT IN 
              [ (u)-[:FRIENDS_WITH]-(x:User) | x.id ]
        AND recommendation.id <> $userId
        WITH friend, recommendation, COUNT(*) AS commonFriends
        ORDER BY commonFriends DESC
        LIMIT $limit
        RETURN recommendation.id AS id, recommendation.name AS name, 
               recommendation.avatar AS avatar, recommendation.friendCount AS friendCount,
               recommendation.bio AS bio, commonFriends
      `;
      
      const result = await session.run(query, { 
        userId, 
        limit 
      });
      
      return result.records.map(record => ({
        id: record.get('id'),
        name: record.get('name'),
        avatar: record.get('avatar'),
        friendCount: record.get('friendCount'),
        bio: record.get('bio'),
        commonFriends: record.get('commonFriends')
      }));
    } catch (error) {
      console.error('Error fetching recommendations:', error);
      throw error;
    } finally {
      session.close();
    }
  }
  
  // 检测用户关系状态
  async checkRelationshipStatus(userId, targetId) {
    const session = this.neo4jClient.session();
    
    try {
      const query = `
        MATCH (u:User {id: $userId})-[r:FRIENDS_WITH]-(v:User {id: $targetId})
        RETURN type(r) AS type, r.status AS status, r.since AS since
      `;
      
      const result = await session.run(query, { 
        userId, 
        targetId 
      });
      
      if (result.records.length > 0) {
        const rel = result.records[0].get('r');
        return {
          exists: true,
          status: rel.get('status'),
          since: new Date(rel.get('since'))
        };
      }
      
      // 检查是否有未处理的好友请求
      const requestResult = await session.run(
        `MATCH (u:User {id: $userId})-[:PENDING_REQUEST]->(v:User {id: $targetId})
         RETURN 'pending' AS status`,
        { userId, targetId }
      );
      
      if (requestResult.records.length > 0) {
        return { exists: true, status: 'pending' };
      }
      
      return { exists: false };
    } catch (error) {
      console.error('Error checking relationship status:', error);
      throw error;
    } finally {
      session.close();
    }
  }
}

3.2 社交图谱查询优化

  1. 使用Neo4j图数据库存储社交关系,实现高效路径查询
  2. 关系索引与缓存,热门用户关系预先计算
  3. 分页与延迟加载,避免大数据集一次性加载
  4. 推荐算法优化,基于共同好友、兴趣标签等多维度推荐
  5. 异步关系更新,避免阻塞主流程

4. 内容分享与互动系统

内容是社交网络的核心驱动力,我们构建了完整的内容发布、存储和互动系统。

4.1 内容发布与处理流程

// 内容服务核心实现
class ContentService {
  constructor() {
    this.contentRepository = new ContentRepository();
    this.storageService = new StorageService();
    this.analyticsService = new AnalyticsService();
    this.searchIndexer = new SearchIndexer();
    this.notificationService = new NotificationService();
  }
  
  // 创建内容
  async createContent(userId, contentData) {
    const { 
      type, 
      title, 
      text, 
      mediaUrls, 
      tags, 
      location, 
      privacy = 'public' 
    } = contentData;
    
    // 1. 验证内容
    if (!this.validateContent(type, text, mediaUrls)) {
      throw new Error('Invalid content data');
    }
    
    // 2. 处理媒体文件
    const processedMedia = await this.processMedia(mediaUrls, type);
    
    // 3. 创建内容记录
    const content = await this.contentRepository.create({
      userId,
      type,
      title,
      text,
      media: processedMedia,
      tags: tags || [],
      location,
      privacy,
      status: 'pending' // 审核状态
    });
    
    // 4. 创建搜索索引
    if (content.type !== 'video') { // 视频内容由专门服务处理
      this.searchIndexer.indexContent(content);
    }
    
    // 5. 记录内容创建事件
    this.analyticsService.trackContentCreation(userId, content.id, type);
    
    // 6. 通知关注者
    if (privacy !== 'private') {
      this.notificationService.sendContentNotification(userId, content.id);
    }
    
    return content;
  }
  
  // 处理媒体文件
  async processMedia(mediaUrls, type) {
    if (!mediaUrls || mediaUrls.length === 0) return [];
    
    const processedMedias = [];
    
    for (const url of mediaUrls) {
      try {
        // 根据内容类型处理媒体
        let processedUrl, metadata;
        
        switch (type) {
          case 'image':
            // 图片处理:生成缩略图、压缩、格式转换
            const [thumbnailUrl, optimizedUrl] = await this.storageService.processImage(url);
            metadata = {
              originalUrl: url,
              thumbnailUrl,
              optimizedUrl,
              dimensions: await this.storageService.getImageDimensions(url),
              size: await this.storageService.getFileSize(url)
            };
            processedUrl = optimizedUrl;
            break;
            
          case 'video':
            // 视频处理:转码、生成缩略图、提取元数据
            const [transcodedUrl, thumbnailUrl, metadata] = await this.storageService.processVideo(url);
            processedUrl = transcodedUrl;
            break;
            
          case 'text':
          case 'link':
          default:
            processedUrl = url;
            break;
        }
        
        processedMedias.push({
          url: processedUrl,
          type: type,
          metadata
        });
        
      } catch (error) {
        console.error(`Error processing media ${url}:`, error);
        // 记录错误但继续处理其他媒体
      }
    }
    
    return processedMedias;
  }
  
  // 获取用户内容流
  async getUserFeed(userId, options = {}) {
    const { 
      page = 1, 
      limit = 20, 
      type = 'all', 
      withFollowing = true 
    } = options;
    
    const offset = (page - 1) * limit;
    
    // 1. 获取关注的用户
    let followingIds = [];
    if (withFollowing) {
      const followingResult = await this.socialGraphService.getFollowingIds(userId);
      followingIds = followingResult.followingIds;
    }
    
    // 2. 获取用户内容(包括关注的用户和自己的内容)
    const contentResult = await this.contentRepository.getFeedContent({
      userIds: followingIds,
      userId,
      type,
      limit,
      offset
    });
    
    // 3. 丰富内容数据(互动信息、用户信息)
    const enrichedContent = await this.enrichContentData(contentResult.contents);
    
    // 4. 计算分页信息
    const total = contentResult.total;
    
    return {
      contents: enrichedContent,
      pagination: {
        total,
        page,
        limit,
        pages: Math.ceil(total / limit)
      }
    };
  }
  
  // 互动功能:点赞
  async likeContent(userId, contentId) {
    // 1. 检查内容是否存在
    const content = await this.contentRepository.getById(contentId);
    if (!content) {
      throw new Error('Content not found');
    }
    
    // 2. 检查是否已点赞
    const existingLike = await this.interactionRepository.getInteraction({
      userId,
      contentId,
      type: 'like'
    });
    
    if (existingLike) {
      // 取消点赞
      await this.interactionRepository.deleteInteraction(existingLike.id);
      await this.contentRepository.decrementLikes(contentId);
      return { success: true, action: 'unlike', count: content.likes - 1 };
    } else {
      // 添加点赞
      const like = await this.interactionRepository.createInteraction({
        userId,
        contentId,
        type: 'like'
      });
      await this.contentRepository.incrementLikes(contentId);
      
      // 通知内容创建者
      if (content.userId !== userId) {
        this.notificationService.sendLikeNotification(
          userId, 
          content.userId, 
          contentId
        );
      }
      
      return { success: true, action: 'like', count: content.likes + 1 };
    }
  }
  
  // 互动功能:评论
  async commentOnContent(userId, contentId, commentData) {
    const { text, parentCommentId } = commentData;
    
    // 1. 验证评论内容
    if (!text || text.trim().length === 0) {
      throw new Error('Comment text cannot be empty');
    }
    
    // 2. 创建评论
    const comment = await this.interactionRepository.createInteraction({
      userId,
      contentId,
      type: 'comment',
      parentId: parentCommentId,
      text
    });
    
    // 3. 更新内容评论计数
    await this.contentRepository.incrementComments(contentId);
    
    // 4. 通知内容创建者和回复的用户
    const content = await this.contentRepository.getById(contentId);
    if (content.userId !== userId) {
      this.notificationService.sendCommentNotification(
        userId, 
        content.userId, 
        contentId, 
        comment.id
      );
    }
    
    if (parentCommentId) {
      const parentComment = await this.interactionRepository.getById(parentCommentId);
      if (parentComment.userId !== userId) {
        this.notificationService.sendReplyNotification(
          userId, 
          parentComment.userId, 
          contentId, 
          comment.id
        );
      }
    }
    
    return comment;
  }
}

4.2 内容Feed系统实现

// Feed算法实现
class FeedAlgorithm {
  constructor() {
    this.userRepository = new UserRepository();
    this.contentRepository = new ContentRepository();
    this.socialGraphService = new SocialGraphService();
    this.algorithmConfig = {
      // 权重配置
      followingWeight: 0.7,
      recencyWeight: 0.5,
      engagementWeight: 0.3,
      contentQualityWeight: 0.4,
      // 算法参数
      maxContentPerUser: 50,
      maxSuggestedContent: 20,
      minEngagementScore: 10
    };
  }
  
  // 生成个性化Feed
  async generatePersonalizedFeed(userId, options = {}) {
    const { 
      page = 1, 
      limit = 20, 
      includeRecommended = true 
    } = options;
    
    const offset = (page - 1) * limit;
    
    // 1. 获取用户数据
    const user = await this.userRepository.getById(userId);
    if (!user) throw new Error('User not found');
    
    // 2. 获取用户兴趣标签
    const userInterests = await this.userInterestService.getUserInterests(userId);
    
    // 3. 获取关注的用户
    const { followingIds } = await this.socialGraphService.getFollowingIds(userId);
    
    // 4. 获取关注用户的内容
    const followingFeed = await this.getFollowingFeed(followingIds, {
      limit: this.algorithmConfig.maxContentPerUser,
      userInterests
    });
    
    // 5. 获取推荐内容
    const recommendedFeed = includeRecommended 
      ? await this.getRecommendedFeed(userId, userInterests, followingIds)
      : [];
    
    // 6. 合并并排序内容
    const combinedFeed = [...followingFeed, ...recommendedFeed];
    const rankedFeed = this.rankFeedItems(combinedFeed, userId);
    
    // 7. 分页并返回
    const paginatedFeed = rankedFeed.slice(offset, offset + limit);
    
    return {
      contents: paginatedFeed,
      pagination: {
        total: rankedFeed.length,
        page,
        limit,
        pages: Math.ceil(rankedFeed.length / limit)
      }
    };
  }
  
  // 获取关注用户的内容
  async getFollowingFeed(followingIds, options = {}) {
    const { limit = 50, userInterests } = options;
    
    if (!followingIds || followingIds.length === 0) {
      return [];
    }
    
    // 获取关注用户的内容
    const contentResult = await this.contentRepository.getContentByUsers(
      followingIds,
      {
        limit,
        sort: 'created_at',
        order: 'desc'
      }
    );
    
    // 过滤不感兴趣的内容
    const filteredContent = this.filterByInterests(
      contentResult.contents,
      userInterests
    );
    
    return filteredContent;
  }
  
  // 获取推荐内容
  async getRecommendedFeed(userId, userInterests, followingIds) {
    const { maxSuggestedContent = 20 } = this.algorithmConfig;
    
    // 1. 基于用户兴趣推荐
    const interestBasedRecommendations = await this.recommendationService.getByInterests(
      userInterests,
      {
        excludeUserIds: [userId, ...followingIds],
        limit: maxSuggestedContent
      }
    );
    
    // 2. 基于热门内容推荐
    const trendingRecommendations = await this.recommendationService.getTrending(
      {
        excludeUserIds: [userId, ...followingIds],
        limit: maxSuggestedContent - interestBasedRecommendations.length
      }
    );
    
    // 合并推荐内容
    return [...interestBasedRecommendations, ...trendingRecommendations];
  }
  
  // 对Feed项进行排序
  rankFeedItems(contents, userId) {
    // 获取用户互动历史
    const userInteractions = this.interactionRepository.getUserInteractions(userId);
    
    // 对每个内容项计算分数
    const scoredContents = contents.map(content => {
      // 1. 基础分数
      let score = 0;
      
      // 2. 关注用户权重
      if (content.isFollowing) {
        score += this.algorithmConfig.followingWeight * 10;
      }
      
      // 3. 时效性权重(时间衰减)
      const ageScore = this.calculateAgeScore(content.createdAt);
      score += ageScore * this.algorithmConfig.recencyWeight;
      
      // 4. 互动权重(基于用户偏好)
      const engagementScore = this.calculateEngagementScore(
        content, 
        userInteractions
      );
      score += engagementScore * this.algorithmConfig.engagementWeight;
      
      // 5. 内容质量权重(基于平台指标)
      const qualityScore = this.calculateQualityScore(content);
      score += qualityScore * this.algorithmConfig.contentQualityWeight;
      
      return { ...content, score };
    });
    
    // 按分数排序
    return scoredContents.sort((a, b) => b.score - a.score);
  }
  
  // 计算内容年龄分数(时间衰减)
  calculateAgeScore(createdAt) {
    const ageInHours = (Date.now() - createdAt) / (1000 * 60 * 60);
    // 内容越新分数越高,24小时后分数衰减至0.5,72小时后衰减至0.25
    return Math.max(0.1, Math.min(1, Math.pow(0.8, ageInHours / 24)));
  }
  
  // 计算互动分数(基于用户兴趣和历史互动)
  calculateEngagementScore(content, userInteractions) {
    // 基础分数
    let score = 1;
    
    // 互动类型权重:评论 > 分享 > 点赞 > 保存
    const interactionWeights = {
      like: 1,
      comment: 3,
      share: 5,
      save: 2
    };
    
    // 计算用户互动分数
    for (const interaction of userInteractions) {
      if (interaction.contentId === content.id) {
        score += interactionWeights[interaction.type] || 0;
      }
    }
    
    // 内容互动热度(基于全局数据)
    const globalEngagementScore = this.calculateGlobalEngagementScore(content);
    score += globalEngagementScore;
    
    // 确保分数不为负且不超过最大值
    return Math.max(1, Math.min(10, score));
  }
  
  // 过滤用户不感兴趣的内容
  filterByInterests(contents, userInterests) {
    if (!userInterests || userInterests.length === 0) {
      return contents;
    }
    
    // 对每个内容,检查是否与用户兴趣匹配
    return contents.filter(content => {
      // 内容标签匹配
      const tagMatch = content.tags.some(tag => 
        userInterests.includes(tag.toLowerCase())
      );
      
      // 内容作者匹配(关注的用户)
      const authorMatch = content.author.isFollowing;
      
      return tagMatch || authorMatch;
    });
  }
}

5. 安全与性能优化

5.1 安全防护策略

  1. 用户认证与授权

    • JWT令牌基于角色的访问控制(RBAC)
    • 密码加盐哈希存储(bcrypt)
    • 多因素认证(MFA)支持
    • 会话超时与自动登出
  2. 数据安全

    • 敏感数据加密存储(AES-256)
    • HTTPS/TLS 1.3加密传输
    • 数据脱敏与最小化原则
    • 定期安全审计与漏洞扫描
  3. 内容安全

    • 基于AI的内容过滤系统
    • 图片/视频内容自动审核
    • XSS/CSRF防护措施
    • 内容举报与人工审核流程

5.2 性能优化实现

  1. 多级缓存策略

    // 实现Redis+内存多级缓存
    class MultiLevelCache {
      constructor() {
        this.memoryCache = new Map();
        this.redisClient = redis.createClient({
          host: process.env.REDIS_HOST,
          port: process.env.REDIS_PORT
        });
        this.memoryCacheTTL = new Map(); // 内存缓存过期时间
      }
    
      // 获取缓存数据
      async get(key, options = {}) {
        const { ignoreMemory = false, ignoreRedis = false } = options;
    
        // 1. 尝试从内存缓存获取
        if (!ignoreMemory) {
          const now = Date.now();
          const ttl = this.memoryCacheTTL.get(key);
    
          if (this.memoryCache.has(key) && (!ttl || ttl > now)) {
            return this.memoryCache.get(key);
          } else if (ttl && ttl <= now) {
            // 内存缓存已过期,清除
            this.memoryCache.delete(key);
            this.memoryCacheTTL.delete(key);
          }
        }
    
        // 2. 尝试从Redis获取
        if (!ignoreRedis) {
          try {
            const data = await this.redisClient.get(`cache:${key}`);
            if (data) {
              const parsedData = JSON.parse(data);
    
              // 同步到内存缓存
              if (!ignoreMemory) {
                this.set(key, parsedData, { localOnly: true });
              }
    
              return parsedData;
            }
          } catch (error) {
            console.error('Redis get error:', error);
            // Redis出错时降级到只使用内存缓存
          }
        }
    
        // 缓存未命中
        return null;
      }
    
      // 设置缓存数据
      async set(key, value, options = {}) {
        const { ttl = 3600, localOnly = false, redisOnly = false } = options;
        const expiry = ttl ? Date.now() + (ttl * 1000) : null;
    
        // 1. 设置内存缓存
        if (!redisOnly) {
          this.memoryCache.set(key, value);
          if (ttl) {
            this.memoryCacheTTL.set(key, expiry);
    
            // 设置内存缓存自动过期
            setTimeout(() => {
              if (this.memoryCacheTTL.get(key) === expiry) {
                this.memoryCache.delete(key);
                this.memoryCacheTTL.delete(key);
              }
            }, ttl * 1000);
          }
        }
    
        // 2. 设置Redis缓存
        if (!localOnly) {
          try {
            await this.redisClient.set(
              `cache:${key}`, 
              JSON.stringify(value), 
              ttl ? 'EX' : undefined, 
              ttl
            );
          } catch (error) {
            console.error('Redis set error:', error);
            // Redis出错时记录但不中断
          }
        }
      }
    
      // 清除缓存
      async invalidate(key, options = {}) {
        const { localOnly = false, redisOnly = false } = options;
    
        // 清除内存缓存
        if (!redisOnly) {
          this.memoryCache.delete(key);
          this.memoryCacheTTL.delete(key);
        }
    
        // 清除Redis缓存
        if (!localOnly) {
          try {
            await this.redisClient.del(`cache:${key}`);
          } catch (error) {
            console.error('Redis delete error:', error);
          }
        }
      }
    }
    
  2. 数据库优化

    • 读写分离:主库写入,从库读取
    • 分库分表:按用户ID哈希分片
    • 索引优化:针对高频查询创建复合索引
    • 查询优化:分页查询、延迟加载、投影查询
  3. 前端性能优化

    • 代码分割与懒加载
    • 图片优化:WebP格式、响应式图片、渐进式加载
    • 静态资源CDN分发
    • 客户端缓存策略:Service Worker离线缓存

6. 总结与未来展望

社交网络平台是一个复杂的系统工程,需要综合考虑实时通信、数据存储、内容分发和安全防护等多个方面。通过WebFundamentals开源项目,我们构建了一套完整的社交网络技术栈,实现了从实时通信到内容互动的全流程解决方案。

关键技术亮点

  1. 实时通信系统:基于WebSocket的实时双向通信,支持千万级用户连接
  2. 社交图谱:Neo4j图数据库实现高效关系存储与查询,支持复杂社交关系
  3. Feed算法:个性化内容推荐,结合用户兴趣与社交关系
  4. 安全防护:多层次安全策略,保障用户数据安全
  5. 性能优化:多级缓存与数据库优化,支持高并发访问

未来发展方向

  1. AI驱动社交体验

    • 智能内容推荐系统
    • 自动化内容生成与编辑
    • 虚拟助手与聊天机器人
  2. 沉浸式社交体验

    • AR/VR社交空间
    • 元宇宙概念应用
    • 3D互动与空间音频
  3. 去中心化社交

    • 区块链技术保障数据主权
    • 分布式身份认证
    • P2P通信降低中心化依赖

通过持续优化和创新,社交网络平台将不断提升用户体验,创造更丰富的社交互动方式。

【免费下载链接】WebFundamentals Former git repo for WebFundamentals on developers.google.com 【免费下载链接】WebFundamentals 项目地址: https://gitcode.com/gh_mirrors/we/WebFundamentals

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值