在一台服务器上,我有大约 30 个 PHP 站点在 Apache 下运行。所有这些站点都使用相同的 (HTTP) API 来获取一些数据。该 API 托管在其他地方(在我的控制之下)
API 使用具有 keep-alive 功能的 Nginx,而 PHP 站点使用 CURL 发出 API 请求。
访问这 30 个站点中的 1 个站点的访问者会生成一个 API 调用,并且一旦 HTML 传递给访问者,apache/PHP 就会关闭与 API 的连接。
我正在寻找类似于 API 的本地代理的东西,它能够维持与 API 的连接,以便 PHP 网站可以从 keepalive 中获利。
无论如何都要完成这个任务?
答案1
Nginx 配置为反向代理可以轻松做到这一点:
http {
upstream remoteserver {
# here you add your remote server's IPs or hostnames
server 54.175.222.246; # for example here we use HTTPBin's address
keepalive 10; # maintain a maximum of 10 open connections
}
server {
listen 80;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # passing the client's IP to the remote server, on a local machine it doesn't do much though
proxy_set_header Host $http_host; # passing the Host header as requested by the client, otherwise this will default to the pool's name, "remoteserver" in this case
proxy_pass http://remoteserver; # sends the request off to the pool defined above
}
}
}
现在您可以将脚本指向本地服务器而不是远程服务器,这里有一个演示curl
:
$ curl http://localhost/get -H "Host: host header is passed normally"
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "host header is passed normally",
"User-Agent": "curl/7.29.0"
},
"origin": "127.0.0.1, 1.2.3.4",
"url": "http://host header is passed normally/get"
}
如您所见,甚至连 Host 标头都是按原样传递的。
或者,您可以通过将远程主机名指向本地计算机(在/etc/hosts
DNS 解析器的配置中)来实现无缝转换。在这种情况下,请确保在 Nginx 配置中的池定义中仅使用 IP 地址而不是主机名,否则代理也会循环回到自身,这将导致一些灾难。
一旦主机文件被相应地更改,代理就是无缝的:
$ curl http://httpbin.org/get -v
* About to connect() to httpbin.org port 80 (#0)
* Trying 127.0.0.1...
* Connected to httpbin.org (127.0.0.1) port 80 (#0)
> GET /get HTTP/1.1
> User-Agent: curl/7.29.0
> Host: httpbin.org
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.6.2
< Date: Sun, 15 Mar 2015 00:41:54 GMT
< Content-Type: application/json
< Content-Length: 198
< Connection: keep-alive
< Access-Control-Allow-Origin: *
< Access-Control-Allow-Credentials: true
<
{
"args": {},
"headers": {
"Accept": "*/*",
"Host": "httpbin.org",
"User-Agent": "curl/7.29.0"
},
"origin": "127.0.0.1, 1.2.3.4",
"url": "http://httpbin.org/get"
}
如您所见,我们的本地服务器的行为与远程服务器一样,任何尝试访问远程主机名的程序实际上都会连接到我们的本地服务器。
请注意,这可能需要对基于 HTTPS 的主机进行额外的配置。
答案2
PHP 的套接字函数可能是最简单的方法。套接字创建将处理 IPv4、IPv6 和 UNIX 连接。一个简单的例子
$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
$address = '127.0.0.1';
$port = 80;
socket_connect($socket,$address,$port);
// Sending data
socket_write('Your API commands here');
// Reading data
while ($buffer = socket_read($socket,1024,PHP_NORMAL_READ)) {
if(trim($buffer) == 'END') {
break;
}
}
socket_close($socket);
php.net 上有更多示例:套接字示例
答案3
您可能需要编写一个 php 脚本,该脚本将从命令行启动并妖魔化,打开 curl 句柄并将其重用于每个后续请求,因此使用保持活动功能。此脚本应提供使用消息队列的 API(查看 beanstalkd / rabbitmq)。一旦队列中有新消息,脚本就应该向外部 API 发出请求并将结果推回到消息队列中。或者提供使用套接字的 API(但这可能非常棘手,因为它应该是多线程的,PHP 中的多线程可以使用 fork 实现,而且我不确定如果您尝试同时在多个子进程中使用它,curl 句柄会如何表现)。使用它还可能会对性能造成影响,因此如果您有许多用户,您可能应该创建多个同时运行的守护进程。