На одном сервере у меня около 30 PHP-сайтов, работающих под Apache. Все эти сайты используют один и тот же (HTTP) API для получения некоторых данных. API размещен в другом месте (под моим контролем)
API использует Nginx с keep-alive, а сайты PHP используют CURL для выполнения запросов API.
Посетитель 1 из 30 сайтов сгенерирует вызов API, и соединение с API будет закрыто apache / PHP, как только HTML будет доставлен посетителю.
Я ищу что-то вроде локального прокси для API, который может поддерживать соединение с ним, чтобы сайты PHP могли получать прибыль от поддержки активности.
Как бы то ни было, для этого?
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-преобразователя. В этом случае убедитесь, что вы используете только IP-адреса вместо имен хостов в определении пула в конфигурации Nginx, иначе прокси также вернется к самому себе, что приведет к небольшой катастрофе.
После того, как файл hosts был соответствующим образом изменен, проксирование станет бесшовным:
$ 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.
Возможно, вы захотите написать скрипт php, который будет запускаться из командной строки и демонизировать, открывать дескриптор curl и повторно использовать его для каждого последующего запроса, поэтому с использованием функции keep-alive. Этот сценарий должен предоставлять API, использующий очереди сообщений (см. Beanstalkd / rabbitmq). Как только в очереди появляется новое сообщение, скрипт должен сделать запрос к внешнему API и отправить результат обратно в очередь обмена сообщениями. В качестве альтернативы предоставьте API с использованием сокетов (но это может быть очень сложно, поскольку он должен быть многопоточным, многопоточность в PHP может быть достигнута с помощью fork, и я не уверен, как поведет себя дескриптор curl, если вы попытаетесь его использовать в нескольких подпроцессах одновременно). Это также может привести к снижению производительности, поэтому, если у вас много пользователей, вам, вероятно, следует создать несколько демонов, которые будут работать одновременно.
PHP функции сокета может быть самым простым подходом. Socket_create будет обрабатывать соединения 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: Примеры сокетов