公司的一个 php 项目经常调用众多的三方平台的 api ,有 http 协议和 https 协议;由于 php-fpm 模式无法复用链接,所以每次进行 api 调用时需要经过:tcp 握手、( tls 握手)、http 报文交互、连接关闭几个阶段;比较明显的就是接口延迟高,业务量大时出现大量的 TIME_WAIT 。
使用 go 语言开发了一个网络请求代理中间件,接管 php 的网络请求,在代理程序中保持连接的复用;经测试能极大的降低网络延迟,目前已在生产环境使用。
如果项目采用微服务架构,且包括由 PHP 开发的微服务,而这些微服务之间通过 HTTP 进行调用,那么可以使用 Fastcar 作为 PHP 的网络请求中间件。能有效地维持长连接,降低资源开销,以及减少网络延迟。
1
vibbow 2023-09-05 23:01:01 +08:00
更正一个错误:php-fpm 模式下是可以复用链接的
|
3
vibbow 2023-09-05 23:09:14 +08:00
@pretty66 https://www.php.net/pfsockopen
在我的配置下,单个 php-fpm 进程可以处理 1w 个请求,同一个 php-fpm 进程处理的请求是可以复用链接的。 通常一个 web 实例也不会有太多 php-fpm 进程,所以不跨进程共享问题也不大。 |
4
vibbow 2023-09-05 23:10:24 +08:00
实际 pfsockopen 是底层一些的用法,一般情况下用 stream_socket_client + STREAM_CLIENT_PERSISTENT flag 去启用这个功能。
像 redis 的长连接,也是用这个实现的。 |
6
vibbow 2023-09-05 23:16:35 +08:00
@pretty66 你可以看下 composer 的代码,composer 是用 stream_socket_client 创建 http 连接的。
|
7
pretty66 OP @vibbow 粗略的看了下 pfsockopen 和 stream_socket_client 这两个函数都是偏网络底层的,工作在 tcp4 层,如果处理 7 层的 http 协议需要基于这些函数自己实现协议的封装;相当于自己实现 http client 复杂度有点高;目前没有发现成熟开源库。简单的 http 请求用这些底层函数封装下还行,如果涉及到复杂的 http 请求不知道能否胜任。例如:服务端异常主动关闭连接的,http 的 chunked 数据响应,http2 的服务 不知道这几种情况能否很好的处理
|
9
vibbow 2023-09-06 00:39:04 +08:00
https://www.php.net/manual/en/context.http.php
构建一个 context 丢给 stream_socket_client 就行 |
10
changz 2023-09-06 08:08:43 +08:00 via Android
业务侵入性太大,不如 hook dns 然后做个代理服务靠谱些
|
11
louisxxx 2023-09-06 08:28:33 +08:00 via iPhone
没看懂你这个和 nginx 的反向代理 API 域名做连接保持区别在哪里? stream_socket_client 是很常用的方案啊
|
12
rekulas 2023-09-06 08:37:07 +08:00 1
你的需求用 webman 似乎就可以了
|
13
demoshengxw 2023-09-06 08:49:58 +08:00 via iPhone
我们公司是 java 和 php 还有 go 乱七八糟一堆异构系统,对于 php 这类链接 redis ,mysql 这种短链接,是直接在 k8s 内注入 sidercar 容器做代理,常链接都由 sidercar 代理去维护。
|
14
kingofzihua 2023-09-06 08:57:17 +08:00
这样算是复用连接吗?
``` curl_setopt($ch, CURLOPT_FRESH_CONNECT, false); curl_setopt($ch, CURLOPT_FORBID_REUSE, false); ``` |
15
pretty66 OP @changz 如果项目代码封装比较好,只需要在请求调用的函数增加仅仅三行代码,对正常的业务无任何影响;做 hook dns 再做代理,代理你使用正向代理吗,正向代理你怎么保持连接复用
|
16
fenglangjuxu 2023-09-06 09:29:13 +08:00
有考虑过 fastcar 重启的问题么
它挂掉了 是不是请求就中断了 |
17
pretty66 OP @demoshengxw 能用 sidecar 是很好的方案,你们公司业务规模肯定不小;一般的小公司的技术是用不起来这一套的,fastcar 也类似一个 sidecar 的程序内部维护连接池,只专注于解决 php http 请求的问题,比较适合小业务;只需要在程序调用的地方增加三行 curl 设置就能保持长连接。当然如果有实力使用 service mesh 架构肯定是极力推荐 sidecar 方案,我们公司也是使用的 service mesh 架构
|
18
pretty66 OP @kingofzihua chatgpt 亲自答:`CURLOPT_FORBID_REUSE` 和 `CURLOPT_FRESH_CONNECT` 是 cURL 中的两个参数,用于控制连接的复用和重新连接。它们的作用如下:
1. `CURLOPT_FORBID_REUSE`:设置为 `true`(或 `1`)时,表示禁止复用连接。这意味着在请求之间不会重用现有的连接,而是每次请求都会创建一个新的连接。默认情况下,cURL 是允许复用连接的。 2. `CURLOPT_FRESH_CONNECT`:设置为 `true`(或 `1`)时,表示强制每次请求都创建一个新的连接,即使之前的连接可复用。默认情况下,cURL 会尝试复用现有连接,以提高性能。 在 PHP-FPM 不同的生命周期中,这两个参数的设置通常不会影响连接的复用。PHP-FPM 是一个进程管理器,它会在请求到达时启动一个 PHP 进程来处理请求,处理完请求后,该 PHP 进程会继续存在一段时间等待下一个请求。连接的复用通常是在同一 PHP-FPM 进程内进行的,而不是在不同 PHP-FPM 进程之间。 如果你希望在不同的 PHP-FPM 进程之间共享连接池,你需要使用连接池管理工具或者设置共享内存等机制,这超出了 cURL 的 `CURLOPT_FORBID_REUSE` 和 `CURLOPT_FRESH_CONNECT` 参数的作用范围。 因此,`CURLOPT_FORBID_REUSE` 和 `CURLOPT_FRESH_CONNECT` 主要用于在单个 PHP-FPM 进程内的不同请求之间控制连接的行为,而不会跨不同 PHP-FPM 进程。如果需要在不同 PHP-FPM 进程之间实现连接的共享和复用,需要考虑其他方法,如使用连接池工具或者共享内存。 |
19
codersdp1 2023-09-06 10:59:16 +08:00
@vibbow #8 请教下,stream_socket_client 复用链接的话。stream_socket_client 创建的 fd,该保存在那里,在 fpm 下每个请求都会创建新的 php 解释实例,上个 php 实例创建的 fd 应该被销毁掉了。
|
20
vibbow 2023-09-06 14:10:48 +08:00 2
@codersdp1 fastcgi 模式下,一个 PHP 进程实例会处理很多请求(我这里是处理 10000 请求数后才会销毁),而不是处理一个请求新建一个进程的模式。
所以 fd 是由 PHP-CGI 进程自己保存的。 FD 的生命周期是跟着 PHP-CGI 进程的实例生命周期,而不是请求的周期了。 |
21
mrpzx001 2023-09-06 15:35:50 +08:00
workerman/swoole 能干这事吗?
|
23
changz 2023-09-06 18:24:24 +08:00 via Android
@pretty66 三行代码是少,耐不住服务数量多啊,你一个一个改?还不如在出口网关做代理,内网 tcp 握手效率一般还没到需要优化的时候
|
24
pretty66 OP @changz 出口网关? hook dns 流量到网关,然后如何做代理转发流量,你在代理这一层针对 https 类型的地址,你如何复用连接,是否会涉及到自签证书体系的管理,如果引入这一套东西这个架构的复杂度提升了不是一星半点。如果不包含自建证书体系,是否可以详细说说你的架构流程
|
25
learningman 2023-09-06 19:48:00 +08:00 via Android
确实是玩具
|
27
pretty66 OP @changz service mesh 体系小公司用起来也费劲的,envoy 和阿里的 mosn 这种 sidecar 主要用来解决的是微服务间的调用对 http 的支持很友好,针对调用第三方平台 api 特别是 https 是没办法支持连接复用的,可以看下 istio 的说明: https://istio.io/v1.8/zh/docs/tasks/traffic-management/egress/egress-control/#access-an-external-https-service
|
28
vibbow 2023-09-07 09:50:43 +08:00 via Android
@demoshengxw php 连接 redis/mysql
地址前面加 p: 就是长连接模式 |
29
demoshengxw 2023-09-07 22:58:11 +08:00 via iPhone
@vibbow 但是 fpm 模式下,长链接意义不大。执行完毕还是会释放,所以找个代理维持和 redis 的链接是个好的方案。
|
30
vibbow 2023-09-08 03:57:06 +08:00
@demoshengxw 大哥,你不看回帖的么...
fpm 模式下,长链接 是跨请求存在的 |
33
shoaly 2023-09-13 08:45:03 +08:00
@pretty66 其实老哥这个项目的尴尬是 , php 和 go 一般人都是 2 选 1, 不喜欢缝合, 我个人倒是也喜欢缝合在一起用, php 的效率和随意 + go 的一些网络请求的优势
|
34
pretty66 OP @shoaly 是的,多语言借助各自的优势相互配合能极大的提高效率;这个只是给有需要的人,还有一种针对老项目需要优化但是又不想重构的用这个比较方便。这个目前我们已经上线使用了,效果很不错,而且从开发到上线只用了一天
|
36
dandankele 327 天前
@vibbow 有个问题,长连接是不是也需要服务端的支持?如果服务端在每次处理完成请求之后主动断开连接(例如对方服务端接口也是没使用长连接的常规的 php 实现的接口),即使客户端支持长连接,也无法维持这个长连接是吗?
|
37
vibbow 327 天前
@dandankele 对于服务器端,长连接实际上不是 PHP 处理的,而是 apache/nginx 这些处理的,一般都是支持的。
|