【问题标题】:PHP socket server issues (mysql connection, max connections limit)PHP 套接字服务器问题(mysql 连接,最大连接数限制)
【发布时间】:2017-03-06 07:42:18
【问题描述】:

我需要编写一个套接字服务器,它至少可以处理大约 1000 个(将来会更多)低流量的永久连接。我在 PHP 上制作了一个草稿版本用于测试目的(我们正在开发一个监控硬件,所以我们需要开发和测试一个对话协议和硬件功能),当我只有几个客户端连接时,它非常适合我。但是当连接数量增加到十个时,出现了一些关键问题。这里有一些关于服务器架构的信息:

我有一个主进程,它等待套接字连接,并在连接时使用 pcntl_fork() 创建一个子进程(从现在开始提供此连接)。我也在主进程中建立到 MySQL 的 PDO 连接。所有子进程共享同一个 PDO 对象。起初我担心在同时查询期间会遇到一些冲突,但我没有遇到它们,即使通过压力测试(10 个孩子在循环中不停地进行查询)。但是每个孩子都有usleep(500000),所以这可能是运气,尽管我已经运行了这个测试几个小时。但是即使连接了 1k 个客户端,这种负载也不应该存在,因为它们与服务器之间的对话很少。

这是我的第一个问题:对大量子进程(理想情况下大约有 1000 个)使用单个 PDO 对象是否安全?我可以为每个孩子使用单个连接,但 MySQL 几乎不支持那么多连接。

第二个问题是获取寄生 MySQL 连接。正如我之前提到的,我只有一个 PDO 对象。但是当我连接了多个客户端,并且在他们运行了一些查询之后,我在 mytop 中看到有多个 DB 连接,并且我找不到连接数量和子进程数量之间的任何相关性我有.例如,我有 3 个孩子和 5 个数据库连接。我尝试建立持久连接,但并没有改变任何东西。

第二个问题:是 PDO 与 MySQL 建立了额外的连接,还是 MySQL 驱动程序?有没有办法强迫他们使用一个连接?我不认为这可能是我的错,每次我调用创建 PDO 对象的方法时,我的代码都会向控制台打印一个警报,而且这只发生一次,在脚本开始时,在分叉之前。之后,我只使用父级的 PDO 对象从子级中运行查询。再一次,由于 MySQL 的限制,我无法承受如此多的连接。

第三个问题:一千个socket连接本身会不会是个问题?除了 CPU 和数据库负载,我的意思是。或者我应该做一些较小的服务器(例如 128 个连接),如果超过最大连接数,它将告诉客户端连接到另一个服务器?

提前感谢您的时间和可能的答案。

【问题讨论】:

  • 为什么要在应用程序和数据库之间建立一层?你听说过连接池和连接重用吗?我不想听起来粗鲁,但您的经验似乎不足以为您描述的这么大的项目构建服务。
  • 您的评论没有任何粗鲁之处,我在该领域确实没有太多经验,这基本上就是我问这个问题的原因。但我们都必须从某个地方开始,你知道的。我也不知道连接池是什么,但根据我们的计算,一个数据库连接就足以满足我们的需求(在最坏的情况下,每秒的简单查询不应该超过 2 个)。正如我现在所看到的,我的问题是对分叉的工作原理缺乏了解。

标签: php mysql sockets pdo


【解决方案1】:

目前您主要关心的应该是您的套接字服务器架构。为每个客户端分叉一个进程非常繁重。 AFAIK 一台普通的 PC 可以容忍大约 2000 个线程并且它不会快速工作。在进程之间切换意味着 CPU 应该将其状态保存在内存中,如果您有大量进程,CPU 将忙于内存 IO,几乎没有时间实际做事。

您可能想从 Apache 中寻找灵感。在 Apache 中,他们使用固定数量的工作进程/线程,每个进程/线程通过select 函数和非阻塞模式的套接字与多个客户端一起工作。这是一种更加稳健的方法。

关于数据库 IO,我会生成一个进程/线程,它是数据库连接的唯一所有者。工作进程将使用 IPC(在进程的情况下)或无锁队列(在线程的情况下)与 DB IO 进程通信。这种方法使您独立于 PDO 实现细节(如果它是线程安全的或者它是否产生连接等)。

P. S. 我怀疑您实际上是通过分叉生成新的 PDO 对象(分叉仅仅意味着复制一个进程及其内存和其中的所有内容)并且 PDO 对象根据需要创建和关闭连接。这可以解释为什么您没有看到低流量客户端和数据库连接之间的相关性。

【讨论】:

  • 你有一些措辞错误。进程分叉产生一个新的(子)进程。线程不是进程/分叉。当你有 16 个 CPU 内核时,你不应该同时启动同一个程序的 15 个以上的线程。
  • @DanFromGermany 您显然对分叉是正确的,但我认为您的评论和答案之间没有矛盾。另外,我在这里互换使用术语 threadprocess 因为大多数帖子对两者都是正确的 + 我不相信“对所有事情都使用进程”是正确的方法完成这项任务。
  • @DanFromGermany 或者 PHP 中的 fork 与 C 中的 fork 不同?在 C 中,它会创建父进程的副本,即使指令指针指向同一条指令。唯一不同的是 fork 返回值 - 它是 0 表示父级或类似这样的 smth。
  • PHP 在内部使用 C fork ;-) 它确实是进程的副本,但它被称为子进程,因为它从父进程继承了一些东西。我现在不确定出了什么问题大声笑 nvm!你是对的,fork vs thread 有内存优势/劣势。线程应该用于共享内存中一些东西的较小任务,fork 复制并且不共享 afaik。我发现 PHP 线程比 fork 更易于维护和更快,但这肯定取决于。见stackoverflow.com/questions/16354460/forking-vs-threading
  • 是的,你是绝对正确的——当你分叉一个进程时,它会复制 PDO 对象,然后它奇迹般地工作,当然重新打开连接。傻我。顺便说一句,我的服务器使用某种非阻塞套接字读取(在​​每个刻度上,我使用带有 MSG_PEEK 和 MSG_DONTWAIT 的 socket_recv(),如果套接字为空,我运行一些其他逻辑。我应该为此使用 socket_set_nonblock(),但是这个方案也在工作),所以我可以重写它以服务多个套接字,如果一个工作人员,就像 nginx 一样。
猜你喜欢
  • 2016-05-01
  • 1970-01-01
  • 2016-12-21
  • 2021-03-31
  • 2023-04-02
  • 2014-02-17
  • 1970-01-01
  • 2015-09-29
  • 1970-01-01
相关资源
最近更新 更多