平滑加权轮询负载均衡的底层逻辑
馗奶还胃什么是 WebSocket,它如何工作?在深入代码之前,让我们先了解一下 WebSocket 以及它与传统 HTTP 通信的区别。WebSocket vs. HTTP:关键区别HTTP 是无状态协议。每次客户端发送请求(例如,当你加载网页或发送消息时),它都会与服务器建立新连接,处理请求,并在数据发送完成后关闭连接。WebSocket 则支持全双工通信。这意味着一旦建立 WebSocket 连接,服务器和客户端就可以相互发送消息,而无需反复打开和关闭连接。WebSocket 非常适合需要实时数据交换的应用,如聊天应用、在线游戏和股票行情更新。WebSocket 的工作原理握手(Handshake):客户端(浏览器)通过 HTTP 向服务器发送 WebSocket 握手请求。如果服务器支持 WebSocket,它会响应并升级到 WebSocket 协议。持久连接:握手完成后,建立持久连接,允许客户端和服务器随时发送数据。数据交换:连接打开后,数据可以以小数据包的形式发送,最大限度地减少延迟。关闭连接:客户端或服务器都可以在不再需要时发起关闭 WebSocket 连接。设置环境:工具和要求在本指南中,我们将使用以下技术:PHP:用于构建服务端逻辑Ratchet:一个用于 WebSocket 的 PHP 库Composer:用于管理 PHP 依赖JavaScript(客户端):与 WebSocket 服务器交互并实时更新 UI前置要求PHP 和 JavaScript 的基础知识机器上安装了 PHP 7.4安装了 Composer 用于管理依赖你可以按照 Composer 官方网站上的说明安装 Composer。安装 RatchetRatchet 是一个 PHP WebSocket 库,可以轻松使用 WebSocket。要安装它,打开终端并运行以下命令:composer require cboden/ratchet这将安装 Ratchet 及其依赖项,包括必要的 WebSocket 服务器功能。构建 WebSocket 服务器:PHP 和 Ratchet步骤 1:创建 WebSocket 服务器我们来创建一个基本的 WebSocket 服务器来处理客户端连接和消息。1. 创建 WebSocket 服务器脚本在项目目录中创建一个名为 chat-server.php 的文件。require dirname(__DIR__) . /vendor/autoload.php; // 自动加载 Composer 依赖use Ratchet\MessageComponentInterface;use Ratchet\ConnectionInterface;class ChatServer implements MessageComponentInterface {protected $clients;public function __construct() {$this-clients new \SplObjectStorage;}public function onOpen(ConnectionInterface $conn) {// 当新客户端连接时,将其添加到客户端列表$this-clients-attach($conn);echo New connection: . $conn-resourceId . \n;}public function onClose(ConnectionInterface $conn) {// 当客户端断开连接时,从客户端列表中移除$this-clients-detach($conn);echo Connection closed: . $conn-resourceId . \n;}public function onMessage(ConnectionInterface $from, $msg) {// 将传入的消息广播给所有连接的客户端foreach ($this-clients as $client) {if ($from ! $client) {// 将消息发送给除发送者之外的所有客户端$client-send($msg);}}}public function onError(ConnectionInterface $conn, \Exception $e) {// 在这里处理错误echo Error: . $e-getMessage() . \n;$conn-close();}}这是 WebSocket 服务器的核心。当新客户端连接时,它会被添加到客户端列表中,当任何客户端发送消息时,该消息会广播给所有连接的客户端。2. 启动 WebSocket 服务器现在我们有了服务器脚本,需要创建一个命令来启动 WebSocket 服务器。创建一个名为 server.php 的新文件:require dirname(__DIR__) . /vendor/autoload.php;use Ratchet\Server\IoServer;use Ratchet\WebSocket\WsServer;use Ratchet\HTTP\Router;use Ratchet\WebSocket\WsProtocol;$server IoServer::factory(new WsServer(new ChatServer()),8080 // WebSocket 服务器端口);echo WebSocket server running on ws://localhost:8080\n;$server-run();要启动服务器,只需运行:php server.php你的 WebSocket 服务器现在将在 ws://localhost:8080 上运行。构建客户端:连接到 WebSocket 服务器现在我们已经设置好了服务器,接下来创建客户端逻辑来与它交互。我们将使用 JavaScript 打开 WebSocket 连接并发送/接收消息。步骤 2:创建前端创建一个名为 index.html 的 HTML 文件作为聊天 UI。Real-Time ChatSend这个基本的前端连接到 WebSocket 服务器并允许用户发送消息。消息显示在 #chat div 中。身份验证:实现用户认证以支持私信和用户识别虽然基本的聊天应用可以很好地向所有连接的用户广播消息,但它还没有处理用户身份验证。在实际应用中,用户身份验证对于验证用户身份和确保私密对话的安全至关重要。我们来看看如何在 PHP WebSocket 聊天应用中实现身份验证。步骤 1:用户身份验证概述在这个实现中,我们将使用 JWT(JSON Web Token)进行身份验证。JWT 是一种紧凑且自包含的方式,可以在各方之间安全地传输信息。它将帮助我们验证用户并为他们创建会话以发送和接收私信。步骤 2:在 PHP 中设置 JWT 身份验证1. 安装 JWT PHP 库要处理 JWT 的创建和验证,我们将使用 firebase/php-jwt 库。通过 Composer 安装:composer require firebase/php-jwt2. 创建身份验证服务器脚本登录脚本(login.php):此脚本将验证用户(使用硬编码凭据的简单示例)并发送 JWT。require_once vendor/autoload.php;use \Firebase\JWT\JWT;$secretKey your_secret_key;$issuedAt time();$expirationTime $issuedAt 3600; // jwt 从签发时间起 1 小时内有效$issuer localhost;// 演示用的硬编码用户凭据(你可以用真实数据库替换)$validUsername user;$validPassword password;if ($_POST[username] $validUsername $_POST[password] $validPassword) {// 生成 JWT token$payload array(iat $issuedAt,exp $expirationTime,iss $issuer,user $validUsername);$jwt JWT::encode($payload, $secretKey);echo json_encode(array(token $jwt));} else {echo json_encode(array(error Invalid credentials));}3. WebSocket 服务器身份验证(chat-server.php)修改 WebSocket 服务器,在 WebSocket 连接过程中使用从客户端发送的 JWT token 验证用户。// 在 chat-server.php 中修改 onOpen 方法private function validateToken($token) {try {$decoded JWT::decode($token, $this-secretKey, array(HS256));return true;} catch (Exception $e) {return false;}}4. 客户端身份验证连接到 WebSocket 服务器时,客户端将 JWT token 作为查询参数包含进来。const token your-jwt-token-here; // 登录后获取const socket new WebSocket(ws://localhost:8080?token token);持久化:在数据库中存储聊天消息为了使聊天应用更加实用,存储聊天消息至关重要,这样用户可以稍后查看或检索旧消息。为此,我们将使用 MySQL 在数据库中持久化聊天消息。步骤 1:创建 MySQL 数据库和表创建一个数据库和一个用于存储聊天消息的表:CREATE DATABASE chat_app;USE chat_app;CREATE TABLE messages (id INT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(255) NOT NULL,message TEXT NOT NULL,timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP);步骤 2:修改 WebSocket 服务器以保存消息修改 WebSocket 服务器脚本,将每条传入的消息存储在数据库中。我们将使用 PDO 与 MySQL 数据库交互。// 在 chat-server.php 顶部添加以下内容$pdo new PDO(mysql:hostlocalhost;dbnamechat_app, root, ); // 替换为你的数据库凭据// 修改 onMessage 方法以将消息存储在数据库中public function onMessage(ConnectionInterface $from, $msg) {$username user; // 你可以从 token 或其他来源检索用户名$stmt $pdo-prepare(INSERT INTO messages (username, message) VALUES (:username, :message));$stmt-bindParam(:username, $username);$stmt-bindParam(:message, $msg);$stmt-execute();// 将消息广播给所有客户端foreach ($this-clients as $client) {if ($from ! $client) {$client-send($msg);}}}步骤 3:检索消息为了在用户连接时显示之前的消息,我们可以在打开新连接时查询数据库,并将过去的消息发送给客户端。public function onOpen(ConnectionInterface $conn) {// 检索最近 10 条消息$stmt $pdo-query(SELECT username, message FROM messages ORDER BY timestamp DESC LIMIT 10);$messages $stmt-fetchAll(PDO::FETCH_ASSOC);// 将消息发送给新客户端foreach ($messages as $message) {$conn-send($message[username] . : . $message[message]);}$this-clients-attach($conn);echo New connection: . $conn-resourceId . \n;}可扩展性:使用 Redis Pub/Sub 扩展应用随着实时聊天应用的增长和更多用户的加入,单个 WebSocket 服务器可能不够用。为了扩展应用,我们可以使用 Redis Pub/Sub(发布/订阅)来管理多个 WebSocket 服务器之间的消息分发。步骤 1:安装 Redis 和 PHP 的 Redis 客户端你需要在服务器上安装 Redis,以及 Redis PHP 客户端:composer require predis/predis步骤 2:在 WebSocket 服务器中实现 Redis Pub/Sub在分布式系统中,你可以在不同机器上运行多个 WebSocket 服务器。Redis 将充当消息代理,允许服务器向所有连接的客户端广播消息。修改 WebSocket 服务器以发布和订阅 Redis 频道:// 添加 Redis 配置$redis new Predis\Client();// 订阅 Redis 频道$redis-connect();$redis-subscribe([chat_channel], function ($message) {// 将来自 Redis 的消息广播给所有连接的客户端foreach ($this-clients as $client) {$client-send($message);}});// 收到新消息时将消息发布到 Redis 频道public function onMessage(ConnectionInterface $from, $msg) {// 将消息发布到 Redis$redis-publish(chat_channel, $msg);}总结:整合所有内容恭喜!你已经学会了如何使用 PHP 和 WebSocket 构建一个功能完整的实时聊天应用。你还学会了如何实现用户身份验证、在数据库中存储聊天消息,以及使用 Redis Pub/Sub 扩展应用。构建实时应用既具有挑战性又令人满足,通过本文介绍的概念,你已经具备了进一步推进项目的能力。无论你是添加私信、媒体分享还是视频通话等功能,可能性都是无限的。借助 PHP 和 WebSocket,你拥有了创建可扩展、实时应用的基础,这些应用可以提供无缝的用户体验。