【问题标题】:Implementing WebSockets , the theory and the reality in PHP用 PHP 实现 WebSockets , 理论与现实
【发布时间】:2018-03-02 14:30:14
【问题描述】:

这个问题与理论有关,就像我第一次在 (cs.stackexchange.com) 中提出的现实生活中的编程一样,因为这是理论最多的问题,而且我有指示在这里提问 (https://cs.stackexchange.com/questions/81472/question-about-implementing-websockets-theory-and-the-reality-in-php)。

我多年来一直在试验 Web 套接字和 PHP(其中一些代码已经在生产中),首先我从头开始创建了一个具有非阻塞 IO 的 WebSocket (WS) 服务器,并且一切正常,除了在现实生活中的其他应用程序所需的方法不能是非阻塞的(例如连接到数据库和查询)。然后我介绍了异步编程,这意味着 WS 服务器向服务器发起各种 PHP 请求,并在每个循环中检查这些请求是否完成了结果,以便将它们发送给客户端。这对于连接到此 WS 服务器的少数客户端用户来说效果很好,这个数字与操作有关,但不会超过 30 或 50。那是因为如果你只使用一个线程并且你有很多并发如果有完成的结果,您必须按顺序检查每个请求。

下一步是分析声称可以同时容纳和处理许多(有人说 10000 个)客户的流行方法的代码。也许他们知道我不知道的事情(我的问题不是他们是否在撒谎,问题是我是否在这里遗漏了某些东西(或者我错了))。结果令人沮丧。他们中的大多数默认情况下不使用异步,建议您不要使用阻塞方法(这在现实生活中的编程中确实是不可能的),但是即使您将模块放入它们以使它们异步,我也遇到了同样的问题。

问题不在于解决方案是什么,因为我实现了 PHP pthreads,我可以让它工作,但没有真正的好处(例如共享对象,它必须序列化反序列化所有内容),我写了几年的 C++ PHP 扩展现在,所以我正在使用一个可以有效地做到这一点的 PHP 扩展。

这里的问题是,我错过了什么吗?他们怎么能声称可以同时处理大量请求,而即使使用异步编程,他们也必须检查循环中已完成的每个请求?

提前感谢您提供任何新知识或搜索方向,您的答案可能会引导我。

【问题讨论】:

  • 不想说什么蠢话,但我一直认为线程解决方案。您只需要找到一种体面的方法来在线程之间共享内存(对 pthread 不是很有经验,但 C 线程库在传递对象方面从来没有任何问题,假设您知道如何检测关键部分和添加锁)。
  • apokryfos 这是我现在正在做的,我在 C++ 中将它创建为 PHP 扩展(我们不共享对象,因为这意味着在 PHP 端锁定/解锁,我们所做的是有几个每个都与该请求共享的序列化对象,并且在请求之后我们将该对象与该上下文的主要对象进行比较)。 PHP 核心不是一次愉快的编程冒险,如果我只是在这里遗漏了一些东西,并且仅使用异步和非阻塞 I/O WS 服务器的声明有任何意义(或事实),我想知道它。这就是我要问的问题,我是正确的还是我错过了一些重要的东西?

标签: php asynchronous websocket io nonblocking


【解决方案1】:

是的,有些项目可以使用 PHP。一个这样的项目是 Amp 及其 Aerys HTTP 和 WebSocket 服务器。是的,你不能只在同一个线程中调用阻塞函数。是的,pthreads 无济于事,它就像运行另一个 PHP 进程一样,因为 PHP 中的所有内容都没有共享任何内容。但它是如何工作的呢?

尽可能使用非阻塞实现。有些库使用非阻塞 I/O 进行数据库访问,例如 amphp/mysql

如果没有这样的库,如果您不想/无法自己实现,请询问是否可以实现类似的东西。

另一种可能性是使用诸如amphp/parallel 之类的库,它们使用持久性工作者来阻塞任务。为每个阻塞任务生成另一个工作人员效率极低,因此该库可以轻松使用工作人员池并让这些工作人员在多个任务中保持活动状态。

使用amphp/parallel 的此类库是amphp/file,当没有uveio 等扩展可用时,它使用这些工作程序进行非阻塞文件系统访问,也许你想看看它的ParallelDriver

您能够同时处理多少个连接在很大程度上取决于您的硬件以及您对这些连接所做的工作。如果您不断地将数据流式传输到每个客户端,则与大多数连接处于空闲状态并且仅在连接时间的一小部分内发送/接收某些内容的情况相比,您将能够保持打开的连接少得多。

如果您想处理超过 1000 个客户端,您可能需要扩展或重新编译 PHP,因为 FD_MAXSIZE 用于 stream_select,它已编译并将 stream_select 限制为低于 1024 的文件描述符。

【讨论】:

  • Kelunik 感谢您的回复以及您对 PHP 社区的贡献。我还没有听说过放大器,我肯定会尝试的。不过我有几个问题。您是否不需要检查已完成的每个“事件”才能继续?当您执行此检查(以及事件完成时所需的操作)时,您不能在这个主线程中继续,这不是正确的吗?如果是这样,您可以执行多少并发事件(大约)而不会显着影响您的 WS 服务器? (我在 Amp\Loop\Driver 中看到了 tick 方法,但我还没有时间仔细阅读并分析它)
  • 检查每个事件是否完成是非常低效的。对于计时器,有一个按“过期”排序的项目队列,因此下一个要执行的计时器始终位于队列的开头。 I/O 多路复用发生在stream_select 或类似的系统调用中,所以它在内核内部。 IIRC stream_select 随着传递给它的文件描述符的数量线性下降,但如果您确实需要大量客户端保持连接打开,还有其他选项可用。除非无事可做,否则这些检查不会真正阻塞主线程。
猜你喜欢
  • 2012-08-07
  • 2017-12-10
  • 1970-01-01
  • 1970-01-01
  • 2016-04-16
  • 2019-08-25
  • 2015-08-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多