Назад | Перейти на главную страницу

Прокси-сервер для поддержания соединения с API

На одном сервере у меня около 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: Примеры сокетов