WebSocket主从服务器架构完整教程

发布于:2025-07-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

1. 前言:为什么要学习WebSocket主从架构

第一章:基础知识准备

2.1 什么是WebSocket

生活中的例子

技术特点

2.2 WebSocket vs HTTP

什么时候用WebSocket?

2.3 什么是主从架构

生活中的例子

技术架构图

2.4 环境准备

需要的软件

项目结构

第二章:WebSocket基础入门

3.1 第一个WebSocket服务器

3.2 第一个WebSocket客户端

3.3 消息通信基础

消息格式设计

常见消息类型

完整的消息处理示例

第三章:主从架构设计原理

4.1 架构设计思路

为什么需要主从架构?

架构演进过程

4.2 核心组件说明

1. 主服务器(Master Server)

2. 从服务器(Slave Server)

3. 客户端(Client)

4.3 通信协议设计

通信流程详解

消息协议规范

第四章:主服务器详细实现

5.1 主服务器架构

核心类设计

5.2 从服务器管理

从服务器信息结构

从服务器注册流程

5.3 负载均衡实现

多种负载均衡策略

5.4 完整代码实现

第五章:从服务器详细实现

6.1 从服务器架构

核心类设计

6.2 与主服务器通信

连接管理

6.3 客户端管理

客户端连接处理

6.4 完整代码实现

第六章:客户端详细实现

7.1 客户端连接流程

连接流程图

7.2 自动重连机制

重连策略


1. 前言:为什么要学习WebSocket主从架构

想象一下,你正在开发一个实时在线游戏,有成千上万的玩家同时在线。如果只用一台服务器,会发生什么?

  • 服务器压力巨大,可能随时崩溃
  • 玩家体验差,延迟高
  • 无法扩展,玩家数量受限

这就是为什么我们需要学习WebSocket主从架构!它能帮助我们:

  1. 分散压力:把玩家分配到不同的服务器
  2. 提高性能:每个服务器只处理部分玩家
  3. 易于扩展:需要时随时增加服务器
  4. 高可用性:一台服务器挂了,其他的继续工作

本教程将带你从零开始,一步步掌握这个强大的架构!


第一章:基础知识准备

2.1 什么是WebSocket

生活中的例子

想象两种通信方式:

  1. 写信(HTTP)

    • 你写一封信(请求)
    • 寄给朋友
    • 等待回信(响应)
    • 想再说话?再写一封信
  2. 打电话(WebSocket)

    • 拨通电话(建立连接)
    • 随时可以说话(发送消息)
    • 对方也能随时回话(接收消息)
    • 直到挂断电话(断开连接)

WebSocket就像打电话,一旦连接建立,双方可以随时通信!

技术特点
// HTTP方式(传统)
客户端: "服务器,现在几点?"
服务器: "下午3点"
// 连接断开

客户端: "服务器,现在几点?" // 需要重新连接
服务器: "下午3点01分"
// 连接又断开

// WebSocket方式
客户端 <--持续连接--> 服务器
客户端: "现在几点?"
服务器: "下午3点"
客户端: "天气如何?"
服务器: "晴天"
服务器: "对了,有新消息!" // 服务器主动推送
// 连接一直保持

2.2 WebSocket vs HTTP

特性 HTTP WebSocket
通信方式 请求-响应 全双工
连接状态 短连接 长连接
服务器推送 不支持 支持
实时性 低(需要轮询)
资源消耗 高(频繁建立连接) 低(保持连接)
适用场景 普通网页 实时应用
什么时候用WebSocket?

适合使用WebSocket的场景

  • 在线聊天
  • 实时游戏
  • 股票行情
  • 协同编辑
  • 直播弹幕
  • IoT实时数据

不适合使用WebSocket的场景

  • 静态网页
  • RESTful API
  • 文件下载
  • 表单提交

2.3 什么是主从架构

生活中的例子

想象一个大型餐厅的厨房:

     总厨师长(主服务器)
         |
    分配任务和协调
         |
   +-----+-----+-----+
   |     |     |     |
 厨师1 厨师2 厨师3 厨师4(从服务器)
   |     |     |     |
 菜品1 菜品2 菜品3 菜品4(处理客户请求)
  • 总厨师长:不做菜,只负责分配订单给各个厨师
  • 厨师们:实际做菜,每个厨师负责一部分订单
  • 优势:效率高,一个厨师累了还有其他的

这就是主从架构的核心思想!

技术架构图
                    ┌─────────────────┐
                    │   主服务器       │
                    │   (Master)      │
                    │                 │
                    │ · 管理从服务器   │
                    │ · 分配客户端     │
                    │ · 负载均衡       │
                    │ · 健康检查       │
                    └────────┬────────┘
                             │
        ┌────────────────────┼────────────────────┐
        │                    │                    │
   ┌────▼─────┐        ┌────▼─────┐        ┌────▼─────┐
   │从服务器1  │        │从服务器2  │        │从服务器3  │
   │(Slave 1) │        │(Slave 2) │        │(Slave 3) │
   │          │        │          │        │          │
   │·处理业务  │        │·处理业务  │        │·处理业务  │
   │·管理客户端│        │·管理客户端│        │·管理客户端│
   └────┬─────┘        └────┬─────┘        └────┬─────┘
        │                    │                    │
   客户端1,2,3          客户端4,5,6          客户端7,8,9

2.4 环境准备

需要的软件
  1. Node.js(版本 14.0 以上)

    # 检查是否安装
    node --version
    
    # 如果没有安装,去官网下载:https://nodejs.org
    
  2. 代码编辑器(推荐 VS Code)

    • 下载地址:https://code.visualstudio.com
  3. 创建项目目录

    # 创建项目文件夹
    mkdir websocket-master-slave
    cd websocket-master-slave
    
    # 初始化项目
    npm init -y
    
    # 安装依赖
    npm install ws
    
项目结构
websocket-master-slave/
├── package.json          # 项目配置文件
├── master-server.js      # 主服务器
├── slave-server.js       # 从服务器
├── client.js            # 客户端
├── config/              # 配置文件夹
│   ├── master.json      # 主服务器配置
│   └── slave.json       # 从服务器配置
├── logs/                # 日志文件夹
├── test/                # 测试文件夹
└── README.md            # 项目说明

第二章:WebSocket基础入门

在深入主从架构之前,我们先来掌握WebSocket的基础知识。

3.1 第一个WebSocket服务器

让我们从最简单的WebSocket服务器开始:

// simple-server.js
const WebSocket = require('ws');

// 创建WebSocket服务器,监听8080端口
const wss = new WebSocket.Server({ port: 8080 });

console.log('WebSocket服务器启动在端口 8080');

// 当有客户端连接时
wss.on('connection', function connection(ws) {
    console.log('新客户端连接了!');
    
    // 向客户端发送欢迎消息
    ws.send('欢迎连接到WebSocket服务器!');
    
    // 当收到客户端消息时
    ws.on('message', function incoming(message) {
        console.log('收到消息:', message.toString());
        
        // 回复客户端
        ws.send(`服务器收到了你的消息: ${message}`);
    });
    
    // 当客户端断开连接时
    ws.on('close', function close() {
        console.log('客户端断开连接');
    });
    
    // 错误处理
    ws.on('error', function error(err) {
        console.error('WebSocket错误:', err);
    });
});

// 每5秒向所有连接的客户端发送时间
setInterval(() => {
    wss.clients.forEach((client) => {
        if (client.readyState === WebSocket.OPEN) {
            client.send(`当前时间: ${new Date().toLocaleTimeString()}`);
        }
    });
}, 5000);

代码解释

  1. new WebSocket.Server({ port: 8080 }) - 创建服务器
  2. wss.on('connection', ...) - 监听新连接
  3. ws.on('message', ...) - 监听消息
  4. ws.send(...) - 发送消息
  5. wss.clients - 所有连接的客户端

3.2 第一个WebSocket客户端

// simple-client.js
const WebSocket = require('ws');

// 连接到服务器
const ws = new WebSocket('ws://localhost:8080');

// 连接成功时
ws.on('open', function open() {
    console.log('成功连接到服务器!');
    
    // 发送消息给服务器
    ws.send('你好,服务器!');
    
    // 每3秒发送一次消息
    setInterval(() => {
        if (ws.readyState === WebSocket.OPEN) {
            ws.send(`客户端时间: ${new Date().toLocaleTimeString()}`);
        }
    }, 3000);
});

// 收到服务器消息时
ws.on('message', function incoming(data) {
    console.log('收到服务器消息:', data.toString());
});

// 连接关闭时
ws.on('close', function close() {
    console.log('与服务器断开连接');
});

// 错误处理
ws.on('error', function error(err) {
    console.error('连接错误:', err);
});

// 处理进程退出
process.on('SIGINT', () => {
    console.log('\n正在关闭连接...');
    ws.close();
    process.exit();
});

3.3 消息通信基础

消息格式设计

在实际应用中,我们通常使用JSON格式传递消息:

// 消息格式示例
const message = {
    type: 'chat',        // 消息类型
    data: {              // 消息数据
        user: '张三',
        text: '大家好!',
        time: Date.now()
    }
};

// 发送时转换为字符串
ws.send(JSON.stringify(message));

// 接收时解析JSON
ws.on('message', (data) => {
    const message = JSON.parse(data);
    console.log(`${message.data.user}: ${message.data.text}`);
});
常见消息类型
// 定义消息类型常量
const MessageTypes = {
    // 系统消息
    CONNECT: 'connect',           // 连接
    DISCONNECT: 'disconnect',     // 断开
    HEARTBEAT: 'heartbeat',       // 心跳
    ERROR: 'error',              // 错误
    
    // 业务消息
    CHAT: 'chat',                // 聊天
    JOIN_ROOM: 'join_room',      // 加入房间
    LEAVE_ROOM: 'leave_room',    // 离开房间
    USER_LIST: 'user_list',      // 用户列表
    
    // 主从通信
    REGISTER: 'register',         // 注册
    SYNC: 'sync',                // 同步
    BROADCAST: 'broadcast'       // 广播
};
完整的消息处理示例
// message-handler.js
class MessageHandler {
    constructor(ws) {
        this.ws = ws;
        this.handlers = new Map();
        this.registerHandlers();
    }
    
    // 注册消息处理器
    registerHandlers() {
        this.handlers.set('chat', this.handleChat.bind(this));
        this.handlers.set('join_room', this.handleJoinRoom.bind(this));
        this.handlers.set('heartbeat', this.handleHeartbeat.bind(this));
    }
    
    // 处理收到的消息
    handleMessage(data) {
        try {
            const message = JSON.parse(data);
            const handler = this.handlers.get(message.type);
            
            if (handler) {
                handler(message.data);
            } else {
                console.log('未知消息类型:', message.type);
            }
        } catch (error) {
            console.error('消息解析错误:', error);
        }
    }
    
    // 处理聊天消息
    handleChat(data) {
        console.log(`[聊天] ${data.user}: ${data.text}`);
        // 广播给其他用户
        this.broadcast({
            type: 'chat',
            data: data
        });
    }
    
    // 处理加入房间
    handleJoinRoom(data) {
        console.log(`${data.user} 加入了房间 ${data.room}`);
        // 处理加入房间的逻辑
    }
    
    // 处理心跳
    handleHeartbeat(data) {
        // 回复心跳
        this.send({
            type: 'heartbeat',
            data: { timestamp: Date.now() }
        });
    }
    
    // 发送消息
    send(message) {
        if (this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(message));
        }
    }
    
    // 广播消息(需要访问所有客户端)
    broadcast(message) {
        // 这里需要服务器支持
    }
}

第三章:主从架构设计原理

4.1 架构设计思路

为什么需要主从架构?

让我们通过一个故事来理解:

小明开了一家网络游戏公司,最初只有100个玩家,一台服务器轻松应对。随着游戏火爆,玩家增长到10000人,服务器开始卡顿。小明买了更好的服务器,但玩家继续增长到100000人,再好的单台服务器也扛不住了。

这时,小明想到:为什么不用多台服务器分担压力呢?

这就是主从架构的起源!

架构演进过程
  1. 单服务器阶段

    所有客户端 --> 单一服务器
    
    优点:简单
    缺点:性能瓶颈、单点故障
    
  2. 简单集群阶段

    客户端 --> 负载均衡器 --> 多个独立服务器
    
    优点:负载分散
    缺点:服务器间无法通信
    
  3. 主从架构阶段

    客户端 --> 从服务器 <--> 主服务器 <--> 从服务器 <-- 客户端
    
    优点:负载分散、统一管理、服务器间可通信
    缺点:架构复杂度增加
    

4.2 核心组件说明

1. 主服务器(Master Server)

职责

  • 🎯 从服务器的注册和管理
  • ⚖️ 负载均衡(决定客户端连接哪个从服务器)
  • 💓 健康检查(监控从服务器状态)
  • 📢 消息路由(跨服务器消息转发)
  • 📊 全局状态管理

类比:就像交通指挥中心,不直接运送乘客,但协调所有公交车的调度。

2. 从服务器(Slave Server)

职责

  • 👥 处理客户端连接
  • 💼 执行具体业务逻辑
  • 📨 向主服务器报告状态
  • 🔄 与其他从服务器同步数据

类比:就像具体的公交车,实际运送乘客,并向调度中心报告位置。

3. 客户端(Client)

职责

  • 🔗 向主服务器请求分配
  • 🎮 连接到指定的从服务器
  • 📱 执行应用功能

类比:就像乘客,先问调度中心该上哪辆车,然后上车。

4.3 通信协议设计

通信流程详解
1. 从服务器启动并注册
   Slave --> Master: {type: "register", name: "slave-1", capacity: 100}
   Master --> Slave: {type: "register_success", slaveId: "s1"}

2. 心跳保活
   Slave --> Master: {type: "heartbeat", slaveId: "s1"}
   Master --> Slave: {type: "heartbeat_ack"}

3. 客户端请求分配
   Client --> Master: {type: "request_server"}
   Master --> Client: {type: "server_assigned", host: "192.168.1.2", port: 8081}

4. 客户端连接从服务器
   Client --> Slave: {type: "connect", userId: "u123"}
   Slave --> Client: {type: "connect_success"}

5. 跨服务器消息
   Slave1 --> Master: {type: "forward", target: "slave2", data: {...}}
   Master --> Slave2: {type: "forwarded", from: "slave1", data: {...}}
消息协议规范
// 基础消息结构
{
    id: "唯一消息ID",
    type: "消息类型",
    timestamp: "时间戳",
    data: {
        // 具体数据
    }
}

// 主从通信消息类型
const MasterSlaveProtocol = {
    // 从服务器 -> 主服务器
    SLAVE_REGISTER: 'slave:register',        // 注册
    SLAVE_HEARTBEAT: 'slave:heartbeat',      // 心跳
    SLAVE_STATUS: 'slave:status',            // 状态报告
    SLAVE_SYNC: 'slave:sync',                // 数据同步
    
    // 主服务器 -> 从服务器
    MASTER_REGISTER_ACK: 'master:register_ack',   // 注册确认
    MASTER_HEARTBEAT_ACK: 'master:heartbeat_ack', // 心跳确认
    MASTER_COMMAND: 'master:command',             // 命令下发
    MASTER_BROADCAST: 'master:broadcast',         // 广播消息
    
    // 客户端 -> 主服务器
    CLIENT_REQUEST: 'client:request',        // 请求分配
    
    // 主服务器 -> 客户端
    MASTER_ASSIGN: 'master:assign',          // 分配服务器
    MASTER_REJECT: 'master:reject'           // 拒绝请求
};

第四章:主服务器详细实现

5.1 主服务器架构

主服务器是整个系统的大脑,让我们详细实现它的每个功能。

核心类设计
// master-server-core.js
class MasterServer {
    constructor(config) {
        // 配置
        this.config = {
            port: config.port || 8080,
            heartbeatInterval: config.heartbeatInterval || 10000,
            heartbeatTimeout: config.heartbeatTimeout || 30000,
            ...config
        };
        
        // 核心数据结构
        this.slaves = new Map();        // 从服务器集合
        this.clients = new Map();       // 客户端分配记录
        this.stats = {                  // 统计信息
            totalConnections: 0,
            totalMessages: 0,
            startTime: Date.now()
        };
        
        // 状态
        this.slaveIdCounter = 0;        // 从服务器ID计数器
        this.isRunning = false;         // 运行状态
    }
}

5.2 从服务器管理

从服务器信息结构
class SlaveInfo {
    constructor(ws, registerData) {
        this.id = null;                      // 由主服务器分配
        this.ws = ws;                        // WebSocket连接
        this.name = registerData.name;       // 从服务器名称
        this.host = registerData.host;       // 主机地址
        this.port = registerData.port;       // 端口号
        this.capacity = registerData.capacity || 100;  // 容量
        this.currentLoad = 0;                // 当前负载
        this.status = 'active';              // 状态
        this.lastHeartbeat = Date.now();     // 最后心跳时间
        this.metadata = registerData.metadata || {};   // 额外信息
        this.performance = {                 // 性能指标
            cpu: 0,
            memory: 0,
            responseTime: 0
        };
    }
    
    // 获取负载率
    getLoadRate() {
        return this.currentLoad / this.capacity;
    }
    
    // 是否可用
    isAvailable() {
        return this.status === 'active' && this.getLoadRate() < 0.9;
    }
    
    // 更新心跳
    updateHeartbeat() {
        this.lastHeartbeat = Date.now();
    }
    
    // 转换为客户端可见信息
    toClientInfo() {
        return {
            id: this.id,
            host: this.host,
            port: this.port,
            name: this.name
        };
    }
}
从服务器注册流程
// 在 MasterServer 类中
registerSlave(ws, data) {
    // 验证注册信息
    if (!this.validateSlaveData(data)) {
        this.sendToWebSocket(ws, {
            type: 'register_error',
            error: '注册信息不完整'
        });
        return;
    }
    
    // 检查是否重复注册
    const existingSlave = this.findSlaveByHostPort(data.host, data.port);
    if (existingSlave) {
        console.log(`从服务器 ${data.name} 重新连接`);
        // 更新现有连接
        existingSlave.ws = ws;
        existingSlave.status = 'active';
        existingSlave.updateHeartbeat();
        ws.slaveId = existingSlave.id;
        
        this.sendToWebSocket(ws, {
            type: 'register_success',
            slaveId: existingSlave.id,
            message: '重新连接成功'
        });
        return;
    }
    
    // 创建新的从服务器记录
    const slaveInfo = new SlaveInfo(ws, data);
    slaveInfo.id = ++this.slaveIdCounter;
    
    // 保存到集合
    this.slaves.set(slaveInfo.id, slaveInfo);
    ws.slaveId = slaveInfo.id;
    
    // 发送注册成功消息
    this.sendToWebSocket(ws, {
        type: 'register_success',
        slaveId: slaveInfo.id,
        config: {
            heartbeatInterval: this.config.heartbeatInterval
        }
    });
    
    console.log(`✅ 从服务器 ${slaveInfo.name} (ID: ${slaveInfo.id}) 注册成功`);
    
    // 通知其他从服务器
    this.broadcastToSlaves({
        type: 'slave_joined',
        slave: slaveInfo.toClientInfo()
    }, slaveInfo.id);
    
    // 触发事件
    this.emit('slave:registered', slaveInfo);
}

// 验证从服务器数据
validateSlaveData(data) {
    return data.name && data.host && data.port;
}

// 根据主机和端口查找从服务器
findSlaveByHostPort(host, port) {
    for (const slave of this.slaves.values()) {
        if (slave.host === host && slave.port === port) {
            return slave;
        }
    }
    return null;
}

5.3 负载均衡实现

多种负载均衡策略
// load-balancer.js
class LoadBalancer {
    constructor() {
        this.strategies = new Map();
        this.currentStrategy = 'least_connections';
        this.roundRobinIndex = 0;
        
        // 注册所有策略
        this.registerStrategies();
    }
    
    registerStrategies() {
     

网站公告

今日签到

点亮在社区的每一天
去签到