Pull to refresh

Comments 169

Когда я начал программировать — уже был 1.1. Всегда помнил о поддержании коннекта. Не думаю, что так много программистов не заботятся об этом.
Я привел ссылки на библиотеки гугла и страйпа. Весь стек протоколов thrift пересоздает коннекты. Любой пример использования curl обязательно закрывает соединение за собой, я никогда не видел ни одной ремарки об переиспользовании дескриптора. Почти никто об этом не заботится :(
Ну я больше свои «велосипеды» изобретал, поэтому хорошо знаю спецификацию, а не курл :) Наверно поэтому с этим проблем не было.
Освобождение ресурсов

Благодаря системе подсчета ссылок, введенной в PHP 4 Zend Engine, определение отсутствия ссылок на ресурс происходит автоматически, после чего он освобождается сборщиком мусора. Поэтому, очень редко требуется освобождать память вручную.

php.net/manual/ru/language.types.resource.php

Не оно?
я никогда не видел ни одной ремарки
> я никогда не видел ни одной ремарки об переиспользовании дескриптора

При чем тут автоматическое освобождение и переиспользование. Нет, не оно.
Поэтому, очень редко требуется освобождать память вручную.

А если не освобождать, то он будет повторно использован. Нет?
Тут нет ничего о реиспользовании. «Инициализирует новый сеанс cURL», все.
Тут еще такая штука, большинство сисадминских описаний советуют отключить keep-alive, т.к. таким образом можно выжрать все порты на сервере (просто наоткрывать кучу соединений). И многие отключают (да чего уж там, большинство у кого нет балансировщиков и ферм.)
Согласен. Сам такой.
Посему:
* не надо тупо открывать коннект на каждый запрос
* не надо тупо использовать открытый коннект
* надо тупо определить возможности сервера — а потом уж тупо #1 или тупо #2.
надо тупо определить возможности сервера — а потом уж тупо #1 или тупо #2.

Достаточно просто придерживаться спецификации, которая описывает заголовок «Connection: keep-alive/closed».
Можно по-подробней, как keep-alive выжирает порты сервера?
Ресурсы выжираются.
В БСД регулируются следующим сисконфом
kern.ipc.maxsockets: 25600
После этого система не сможет принимать коннекты.
Еще есть
kern.maxfilesperproc: 11095
kern.maxfiles: 12328

Которые делают эту цифру ЕЩЕ ниже, т.к. в BSD сокеты выжирают еще и из maxfiles.

То есть с 11096 коннектов система вообще неработоспособна станет (т.к. не сможет открыть файл, эрго, SSH не подсоединиться)

Дефолты никто не изучает и не трогает.
Ну, если все по дефолту, включая настройки самого вебсервера (сколько там MaxClients в апаче по дефолту? 200? 500? В nginx — 4 workers * 1024 connectons = 4096 ), то куда раньше упремся в недостаток воркеров/лимит коннектов на воркера.

Ну, и кроме того, сокеты/дескрипторы файлов — это не порты. В TCP в лимит портов скорее всего может упереться сторона, инициирующая соединение. Мало какие протоколы требуют открытия эфемерного порта на сервере.
Так выедаются сокеты, а не порты, это же разные вещи!

Кстати говоря, в случае Connection: close сокеты тоже выедаются, потому что они потом болтаются в TIME_WAIT 2msl (2 часа в случае линуксов). Чтобы с этим бороться в линуксе есть tcp_tw_recycle и tcp_tw_reuse.
Я имел ввиду ресурсы портов.
msl надо тоже крутить.
Порты так не выжрешь, конечно, но вот занять всех воркеров можно.
В java библиотека commons http client по умолчанию использует keep-alive. Что приятный сюрприз)
В PHP есть свои нюансы. Сценарий чтобы последовательно выполнить десять запросов к одному серверу редок, а тип resource нельзя передавать между запросами и/или процессами. Хотя, конечно, в любом случае инициализировать канал несколько раз смысла нет. А ещё есть curl_copy_handle(), которая позволяет работать псевдопарралельно — сначала выполнить все запросы, а потом смотреть что там с результатами. А ещё есть curl_multi_*
VolCh, можно вас попросить кратенько изъяснить суть обоих подходов? ну или дать ссылку, где толково написано.
В смысле разницу между curl_copy_handle() и curl_multi_*()?
А лучше напишите об этом статью. Короткую, но информативную.
О чем? О закачке нескольких файлов в пределах одного скрипта?
Да хотя бы об этом. Подозреваю, при этом вскроются какие-то важные нюансы, которые помогут чуть лучше понять работу с CURL.
Если уж писать, то не о curl конкретно, а о существующих (вернее известных мне) методах закачки нескольких файлов. Кратенько вряд ли получится.
для PHP для curl можно б было сделать аналог persistent connection как для баз, держа пул соединений в стороне, и повторно используя существующие соединения, и по мере необходимости создавая в этом пуле новые соединения.
Можно было бы. И это куда больший эффект имело бы, имхо. Поскольку большинство (субъективно) curl-запросов в PHP это обращение к какому-то конкретному внешнему серверу раз за пользовательский запрос.
UFO just landed and posted this here
Вот что интересно: то, что вы тут написали, имеет некоторый смысл и приводит к мысли, что вы разбираетесь в проблеме. Но тот бред, который вы написали выше говорит о том, что вы не только не разбираетесь в протоколе, но и вообще ничего не понимаете в программировании (по крайней мере в языках с gc).
Это же наводит на мысль, что это вы тупо нагуглили несколько минут назад.
К чему я это? Мне не совсем понятны ваши мотивы (зачем постить в качестве комментария нагугленную информацию), не могли бы вы их мне объяснить?
Вышенаписанное я не считаю бредом. Автор посетовал, что не видел рекомендаций не закрывать ресурсы (дескрипторы), я ему процитировал кусок из мануала, где говорится, что обычно их закрывать не требуется — они сами закроются, когда на них не останется ссылок. Я в чём-то не прав? Ну а дальше можно, по-моему, додуматься, что если закрывать не требуется, то не нужно и второй раз инициализировать.

Ну а задача одновременной закачки нескольких файлов по HTTP моя любимая ещё со времен PHP3.
не видел рекомендаций не закрывать ресурсы (дескрипторы), я ему процитировал кусок из мануала, где говорится, что обычно их закрывать не требуется
Эм, так файловый дескриптор в PHP — это же число, нет? Т. е. поскольку дескрипторы не являются объектами, у среды выполнения нет возможности отследить, остались ли ещё ссылки на данный ресурс.
.
Reference counting в пхп применяется для вообще всех переменных (zval-ов).
Оу. Со времён, когда я в последний раз писал на php, они int заменили на resource, который нормально обрабатывается.
Давненько же вы не писали, лет 10 :)
13 тогда уж, PHP 4 вышел в 2000
Выйти-то он вышел, но хостеры очень неторопливы. Сейчас-то ещё половина сайтов на 5.2 работает.
Нет, это отдельный тип данных resource (обёртка для числовых дескрипторов ОС), похожий на object. В частности, передается он всегда по ссылке (если не использовать что-то вроде curl_copy_handle()) и соответствующий дескриптор корректно закрывается, когда переменная освобождается gc (аналог деструктора в объектах).
Любой библиотеке, типа того же cURL, ничто не мешает удержать свой собственный ресурс внутри себя, сколько необходимо. И не обязательно «бинарной» библиотеке.
Однако вам правильно подсказывают, что про внутреннюю кухню самого PHP вы в контекст не попадаете совсем. К данной теме эта кухня отношения не имеет.
Я только про видимое поведение.
Еще раз: вы человеку на фразу «не видел ни одной ремарки об переиспользовании дескриптора» ответили цитатой о reference counting на дескрипторах(что равноценно относится вообще к zval переменным) в самом PHP. Общего у этих фраз только слово «дескриптор».

Он говорил о том, что приведено в его коде в посте. Что многие программисты, выполнив запрос(curl_init -> curl_exec), тут же закрывают соединение(curl_close), и для следующего запроса устанавливают новое, когда можно было, не закрывая коннект, сохранить дескриптор уже открытого соединения и использовать его повторно для keep-alive.

При чем здесь ARC в PHP?
Не ответил, а спросил — не считает ли он это такой ремаркой. Сценарий «открыли дескриптор, поработали, закрыли» в некоторых вдалбливают чуть ли не палками. Вплоть до требований кода типа
for () {
  $fp = fopen('data.txt', 'w');
  fwrite($fp, 'test...');
  fclose($fp);
}

Я показал ремарку, что закрывать необязательно. Из чего можно сделать вывод, что можно попытаться и переиспользовать.
Не такой уж редкий случай. Особенно если этот скрипт крутится в кроне и парсит фиды какого-нибудь сервера в базу данных сайта. Можно, конечно, делать мультивызовы, обрабатывать потом, но я такое почти нигде не встречал.
Для curl_multi_* нужен внешний ивентлуп, во всяком случае в сишной либе. В итоге код получается на порядок сложнее, чем при использовании curl_easy*.
Хм, никогда в грабе не заморачивался на Keep-Alive (работает и ладно), но, похоже Grab изначально поддерживал эту штуку (с помощью pycurl, естественно) т.к. curl-объект там создаётся именно в конструкторе класса. Щас посмотрел VERBOSE логи двух последовательных вызовов, пишется: «Re-using existing connection! (#0) with host (nil)»
В nodejs флаг Keep-Alive чуть ли не по умолчанию установлен на http запросах, в Рубях та же история, Java программисты тоже про такой флаг знают. А вот у пхпшников, как обычно, впереди много замечательных открытий! :)
UFO just landed and posted this here
В PHP точно так же по умолчанию в запросах вставлен.
«Флаг», как вы называете заголовок, уже везде выставлен как надо. Проблема не в node, php, python, а в программистах, которые их используют.

Давайте посмотрим клиент того же страйпа для node.js. Функция _request создает некий https.request. Может я чего-то не понимаю, но я запускаю вот такой код:

http.request({host: 'evernote.com', path: '/favicon.ico'},
             function(res){
                 console.log('STATUS: ' + res.statusCode);
             }).end();

Запускаю из консоли, знаю о асинхронной природе js, поэтому жду появления строчки STATUS: 200 (это значит что ответ прошел, запрос закончен) и запускаю еще раз. И каждый раз открывается новое соединение.

Все еще считаете, что вас не ждут замечательные открытия?
Вот так всегда на Хабре — если не потроллишь, то дискуссии не получится :)
Ничего не имею против пхпшников, но они ведутся обычно лучше.

Я написал «флаг», потому что на уровне приложения это так и есть — просто такая опция yes/no.

А по поводу keep-alive вот цитата:

The current HTTP Agent also defaults client requests to using Connection:keep-alive. If no pending HTTP requests are waiting on a socket to become free the socket is closed. This means that node's pool has the benefit of keep-alive when under load but still does not require developers to manually close the HTTP clients using keep-alive.
После того, как вы получаете статус 200, процесс node.js завершается, следовательно закрывается ваш сокет.
Я уже как только не пробовал.

https.request({host: 'evernote.com', path: '/favicon.ico'},
function(res){
    console.log('STATUS: ' + res.statusCode);
    https.request({host: 'evernote.com', path: '/favicon.ico'},
        function(res){
            console.log('STATUS: ' + res.statusCode);
        }).end();
}).end();

Создается 2 соединения последовательно.

for (var i = 0; i < 10; i ++) {
    https.request({host: 'evernote.com', path: '/favicon.ico'},
        function(res){
            console.log('STATUS: ' + res.statusCode);
            res.end();
        }).end();
}

Создается 5 соединений паралельно и делается только 5 запросов. Куда еще 5 делось?

Научите, как правильно это «чуть ли не по умолчанию» включить?
Относительно второго случая:

By default, the HTTP Agent will only open 5 simultaneous connections to a single host. You can change this easily: (assuming you've required the HTTP module as http)

http.globalAgent.maxSockets = 20; // or whatever

источник: stackoverflow.com/questions/12060869/why-is-node-js-only-processing-six-requests-at-a-time

Для выполнения keep-alive запросов попробуйте эту информацию:

stackoverflow.com/questions/7339640/with-node-js-http-how-does-res-end-guarantee-a-disconnection-of-the-socket

Может быть эта статья тоже будет полезна: stackoverflow.com/questions/6337328/node-js-https-request-with-keep-alive-header
В общем, у меня получилось только создав собственного агента и переиспользовав его для каждого запроса.

var agent = new http.Agent;
agent.maxSockets = 1;
function work() {
    http.request({host: 'evernote.com', path: '/favicon.ico', agent: agent},
        function(res) {
            console.log('STATUS: ' + res.statusCode);
            console.log('HEADERS: ' + JSON.stringify(res.headers));
            res.setEncoding('utf8');
            res.on('data', function (chunk) {});
            res.on('end', function () { work(); });
        }).end();
}
work();

Это явно не флаг и по сложности ничем не отличается от того, что нужно сделать в примерах из топика. А по очевидности явно уступает.
Чем глобалАгент не подошел, который подставляется сам собой по умолчанию? Он такой же в точности, только максСокетс по умолчанию стоит = 5. Я приводил выше цитату почему так сделано.
Тем, что с ним соединения закрываются. Стоит в коде выше убрать agent: agent, и под каждый запрос начнет открываться отдельное соединение. Как вам такое замечательное открытие?
Смотрел, и что? Я уже сказал как обстоят дела, проверьте сами. Скажу даже больше, если в коде выше maxSockets поменять на 2, начинают создаваться новые соединения под каждый коннект. Версия ноды v0.10.1.
В последней v0.10.12 все точно так же.
Как реквест встает в очередь можно посмотреть в методе addRequest() и в каком случае не встает, а открывает новое соединение, там же. Как сокет реюзается из пула видно в его on('free'). Почему у них так сделан keep-alive написано в цитате, которую я приводил выше.

В строке 50 написано почему не обязательно делать своего агента, а в место него так же в точности работает дефолтный глобальный. У него тоже есть maxSockets, если уж очень хочется потюнить.

Извините, но мне уже надоело выступать тут комментатором чужого когда. Посмотрите его, пожалуйста, сами.
Благодарю за подробности. Особенно обрадовала маленькая разница между переиспользованием в http и https.

Теперь, наконец-то, у меня появился ответ на возражение «Что ты сказал, пользователь? HTTPS? На моём сервере? Иди в задницу, там соединения долгие».
в HTTPS есть еще SPDY, который тоже прослойка над кипалайф, но к тому же асинхронная, что круто сказывается на скорости загрузки.
Теперь осталось ещё убедить всех, что приватность действительно нужна. Напомните, пожалуйста — SPDY по-умолчанию шифрованый? Или есть и plain версии?
Только поверх HTTPS, соответственно — шифрованный. На хабре была прекрасная статья с пояснениями на каких уровнях работают хендлеры SPDY в SSL модуле apache.
SPDY (читается как «speedy», «спиди») — протокол прикладного уровня для передачи веб-контента.
Википедия

Разве он не отдельным протоколом реализован, а просто прослойкой поверх HTTPS? Одно дело, как реализовано конкретно в апаче, другое дело — как записано в «спеках».
Additional Information

The SPDY protocol uses HTTPS, so your site needs to serve content over HTTPS in order to benefit from mod_spdy. If your site does not currently serve content over HTTPS, ask your hosting provider how to enable HTTPS for your site.

Отсюда
Проблема тоже была разжевана тут, в некоторой статье. Реализации HTTP в современном виде не имеют тех уровней абстракции, что имеет HTTPS, в которых и происходит работа над шифрованием-расшифрованием, в них же и работает SPDY. Наверняка, в будущем сие поправят, когда протокол войдет в спеку HTTP 2.0 :)
Вы ошибаетесь. SPDY не «прослойка над кипалайф». HTTP запросов там нет.
Было бы неплохо, чтобы вы пролили свет на сей вопрос: чем не повод для статьи? ;)
Насчет curl_close вы погорячились, его нельзя выкидывать. Т.к. не завершив работу с курлом мы завершаем работу с PHP, то в результате получаем ошибку неожиданного завершения программы, т.к. CURL работает отдельным потоком от лица php.
Вариант — делать свой класс-пул и закрывать в его деструкторе?
Вариантов много, просто не стоит забывать закрывать хендлер :)
Ну, вопрос был в том, что не нужно закрывать его сразу, а можно переиспользовать. Конечно, вариантов реализации такого поведения много.
Мдем. Ну почему в .NET и Mono об этом вообще не надо задумываться, т. к. фремворк сам поддерживает пул соединений (число соединений в пуле можно выставить через app.config или WebRequest.ServicePoint.ConnectionLimit), а отказаться от этой фичи можно только специально, выставив KeepAlive у HttpWebRequest в false?
Потому что там фреймворк, а тут языки и либы? Потому что в PHP скрипт заново полностью инициализируется при каждом запросе (по сути новый запрос — новый процесс) и пул держать затруднительно?
в PHP скрипт заново полностью инициализируется при каждом запросе (по сути новый запрос — новый процесс) и пул держать затруднительно
Учитывая, что PHP заточен на освобождение ресурсов после завершения скрипта, да ещё и однопоточен, не понимаю, что мешает держать по пулу на скрипт.
Толку от этого мало в распространенных юзкейсах. Вот если не освобождать дескрипторы после завершения скрипта, то было бы интересно.
В новомодных (ну как новомодных, уже лет 5 как) конфигурациях типа php-fpm это вполне реализуемый подход, если бы хоть кто-то этим занялся. Так что проблема скорее не сложности/проблематичности, а в том, что до сих пор тянется груз совместимости с запуском php как CGI или через mod_php в апаче.
Некоторым API может не нравиться keepalive
Например Amazon рекомендует:
aws.amazon.com/articles/1904?_encoding=UTF8&jiveRedirect=1
Also, don't overuse a connection. Amazon S3 will accept up to 100 requests before it closes a connection (resulting in 'connection reset'). Rather than having this happen, use a connection for 80-90 requests before closing and re-opening a new connection.

Так же была проблема с тем же amazon что повторно использовать один и тот же IP для соединений нельзя, Amazon начинает думать что вы закэшировали DNS, и начинает банить запросы (делается это для того чтобы никто не кэшировал IP на долго, и их DNS-лоадбалансер правильно работал)
Ох уж этот амазон. Непонятно что такого будет, если заоверюзать один конект. Любая библиотека легко справится с этим и создаст новый взамен закрытого. Более того, их официальный клиент для Питона бото сам использует постоянные соединения, правда через свой велосипед, а не urllib3. И этот велосипед не делает различий между закрытым соединением и другими сетевыми ошибками. Отчего при запрете ретраев фейлит запросы просто от того что соединение отвалилось по таймауту. Но ладно бы просто фейлил, дае еще и зачемто секунду ждет прежде чем пересоздать соединение.
Хоть об этом уже было сказано, но это не проблема PHP, и тем более «мы все это делаем неправильно» это больше относится к быдлокоду, или как не надо программировать. Я когда в первый раз узнавал и разбирал curl даже в мыслях не было его так насиловать.

Я почти уверен, что никто не додумается сделать например так:
for () {
  $fp = fopen('data.txt', 'w');
  fwrite($fp, 'test...');
  fclose($fp);
}

* хотя может я и оптимист конечно :) в виду массовой эйфории по поводу легкого вхождения в php
Совет в общем то один, читайте документацию, включайте голову и логику, и просто напросто не говнокодьте :)
Вы конечно Д'Араньян, но я уже отвечал на такой комментарий, он самый первый.
Не обессудьте сударь, на должность Д'Араньяна я не претендовал, и обращался я вовсе не к вам. Просто мысли вслух ;)
Обоже! Вы открыли мне глаза!
Расскажите ещё о том, что можно несколько раз использовать одно соединение с БД.
Дело в том что в сети/в жизни есть много уроков как писать слова по буквам (синтаксис языков) и графоманство (обзоры фреймворков). А между ними — пропасть — деепричастные обороты, ться (эффективное использование всех возможностей языка/ов).

То, что как бы есть в мануалах. но спрятано спрятано глубоко и чему научить(направить) может только учитель/наставник.
Но в наше время интернета этот шаг пропускается —
Быстрый старт — ознакомился с синтаксисом, скачал фреймворк — в вперед!
Да, речь пойдет о keep alive. Суть в том, что, начиная с http 1.1, клиент и сервер могут договориться не закрывать установленное tcp-соединение после завершения запроса, а переиспользовать его для следующих запросов.
На самом деле это возможно и в HTTP/1.0

% netcat nginx.com 80
GET / HTTP/1.0
Host: nginx.com
Connection: keep-alive

HTTP/1.1 200 OK
Server: nginx/1.5.1
Date: Sun, 23 Jun 2013 14:51:25 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 10655
Last-Modified: Wed, 29 May 2013 10:09:50 GMT
Connection: keep-alive
Keep-Alive: timeout=15
ETag: "51a5d3ee-299f"
Accept-Ranges: bytes

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">

<head>
<title>NGINX Enterprise Software | Nginx, Inc.</title>
[...]
</body>
</html>
GET /products.html HTTP/1.0
Host: nginx.com
Connection: keep-alive

HTTP/1.1 200 OK
Server: nginx/1.5.1
Date: Sun, 23 Jun 2013 14:52:02 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 17154
Last-Modified: Wed, 29 May 2013 10:06:26 GMT
Connection: keep-alive
Keep-Alive: timeout=15
ETag: "51a5d322-4302"
Accept-Ranges: bytes

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">

<head>

<title>NGINX SE | Nginx, Inc.</title>
[...]
</body>
</html>
^C%                                                                                                                       
Хедер «Connection:» не является частью HTTP/1.0, так что вы послали кривой запрос. И получили ответ HTTP/1.1, кстати.
1. Я не получил «ответ HTTP/1.1». Версия протокола, как в запросе, так и в ответе — это информация о наивысшей версии протокла, поддерживаемой клиентом или сервером соответственно.

2. Именно так работал persistent connection в «до HTTP/1.1» эпоху. Вы просто не в теме, в RFC этого нет.

3. Заголовок «Connection: keep-alive» в HTTP/1.1 на самом деле означает буквально: «спилить заголовок keep-alive при проксировании» — не больше, не меньше.
Цитата из документации к Apache:
The Keep-Alive extension to HTTP/1.0 and the persistent connection feature of HTTP/1.1 [...] For HTTP/1.0 clients, Keep-Alive connections will only be used if they are specifically requested by a client.
И nginx это также явно поддерживает, соответсвующая обработка присутствует. Если не послать Connection: keep-alive то для HTTP/1.0 клиента соединение будет закрыто. В то же время, отсутствие заголовка Connection: keep-alive не приводит к закрытию соединения в случае HTTP/1.1, оно там по умолчанию persistent, если не сказано обратного Connection: close.
На самом деле это возможно и в HTTP/1.0

The Keep-Alive extension to HTTP/1.0 [...]

Противоречия не видите?
Не вижу, особенно если принять во внимание значение слова «возможно». Перечитайте ещё раз цитату, на которую я отвечал. Утверждать, что такая возможность появилась только с приходом HTTP/1.1 — просто неверно.

При этом тот же HTTP/1.1 уже давным давно не один RFC, а куча дополнений и прочего. Более того, как клиент, так и сервер HTTP/1.1 не обязан поддерживать persistent connection, должен, но не обязан. Так что HTTP/1.1 не означает обязательно persistent connection, равно как и HTTP/1.0 не означает отсутствие такой возможности. Всё зависит от реализации сервера и клиента.
Утверждать, что такая возможность появилась только с приходом HTTP/1.1 — просто неверно.

Равно как и утверждать, что такая возможность была в самом HTTP/1.0
VBart не говорил что такая возможность была в HTTP/1.0.
Он утверждает, что на HTTP/1.0 это сделать возможно, не путайте.
Все что вам нужно сделать в случае использования curl — перенести вызов curl_init() из метода, который делает запрос, в конструктор класса

Как вариант — сделать Lazy Load для ресурса curl.
Скажите, а в ПХП родовая травма вообще-вообще нельзя парсить страницу пока скачивается следующая?
Вообще-вообще можно. socket_select() например.
Да и в Курле есть мультизапросы.
Видимо все забыли про виртуальный хостинг, с ограничением на время исполнения скрипта, ведь у Вас получится что один скрипт будет висеть до его уничтожения сервером, при этом пользователю будет отдаваться ошибка.
Вы про пхп? Вовсе нет. При завершении каждого запроса к пхп освобождаются все ресурсы используемые скриптом. Ресурсы курла не исключение.
Разве, если соединение не будет разрываться, скрипт будет завершаться?
А разве завершение скрипта не может завершиться с ошибкой? В компилируемых языках main всегда должна вернуть число, означающее результат
Скрипт будет завершаться по таймауту и принудительно рвать соединение со сторонним сервером.
Никак не могу понять ваш use case
Вы тут качаете 20kb файлы по 10 раз с пингом в 200 ms. Это когда может понадобится в жизни?

То есть из TCP следует, что на установку соединения надо 1 RTT. И если запрос-ответ укладывается в одно окно (второй пример), то разница должна быть в 2 раза. (2 RTT на передачу данных, и ещё 1 на закрыть, но это уже не важно)
Если не укладывается (tcp slow start, вероятно, первый пример), то и 3 RTT может быть.
То есть без keep-alive дольше, в 2-3 раза дольше.
Для HTTPS из-за обмена сертификатами может быть ещё дольше.

Вот только это не бесплатно нифига. Надо держать память в ядре (от 4 kB до 4MB в дефолтных настройках ядра), надо держать сокет в PHP user space. Надо держать пул коннектов и грамотно им управлять (использовать свободные, порождать по мере надобности)

Если вы общаетесь со своим же сервером, то всё будет ещё дороже, так как память в ядре с другого конца тоже вы оплачиваете. 1 коннект фигня, а тысячи?

А если RTT маленькое (скажем всё в пределах Москвы, или вообще в пределах одного ЦОД)? Расходы памяти те же, а выигрыш в скорости 0ms*2=0

В общем если вы бежите через океан, то да, keep-alive может помочь. Правда чтобы он помог вам надо сделать несколько вызовов (10 вызовов по 0,2s RTT это уже 2 секунды только на передачу данных), не каждый PHP-скрипт может работать секунды.

Если же вызываете близкие URL, то не забивайте память, не мучайте процессор. Открыли коннект, спросили, закрыли. Дешевле будет.
> Вы тут качаете 20kb файлы по 10 раз с пингом в 200 ms. Это когда может понадобится в жизни?
Я не качаю файлы, я замеряю время соединения и получения ответа от стороннего сервиса. Цитата: «Конечно, тут я намеренно обращаюсь к статическому файлу. В реальных условиях некоторое время займет форматирование запроса.»

> Надо держать память в ядре (от 4 kB до 4MB в дефолтных настройках ядра),
То же самое можно сказать и про обычное соединение. Если вам надо 2 раза обратиться к одному сервису, чем вам поможет, что на миллисекунду вы соединение закроете, память освободите, а потом снова откроете.

> Надо держать пул коннектов и грамотно им управлять
Все уже есть. В PHP curl, в Питоне urllib3, в других языках что-то еще, я уверен.

> 1 коннект фигня, а тысячи?
Каким образом у вас появится 1000? Я рад, что мы сошлись на том, что 1 коннект — фигня.

> В общем если вы бежите через океан, то да, keep-alive может помочь.
И на том спасибо.

> не каждый PHP-скрипт может работать секунды.
Не PHP единым.

> Если же вызываете близкие URL, то не забивайте память, не мучайте процессор
Мучать процессор — это как раз создавать новые ssl соединения каждый раз.
Заметил ошибку: вместо «форматирование» должно быть «формирование». Исправил в топике.
Помню, Сергей Аверин из Badoo про эту штуку рассказывал, про гранату в руках обезьяны.

Надо не забывать, что использование keep-alive и pconnect накладывает дополнительные требования к продукту и его разработке.
— клиент, протокол и сервер должны быть подготовлены
— в highload окружениях все это ведет себя не так, а иногда и совсем не так, как описано в мануалах.
— надо не забывать об ограничениях операционной системы, где крутится все это добро. Здесь от памяти, до сетевого стека.

Протокол надо делать stateful, например, чтобы всегда знать состояние полученного соединения и что там вообще происходит с payload. Также надо научиться сопоставлять запросы и ответы, из-за асинхронной природы сокетов. С базами данных будет тяжело, а также со всякими thrift и прочее.
Ну и почти любой инструмент у нас из коробки не готов к pconnect и к connection pooling.

В общем, не серебряная пуля, никак.
> Надо не забывать, что использование keep-alive и pconnect накладывает дополнительные требования
> к продукту и его разработке.
> — клиент, протокол и сервер должны быть подготовлены

Я не знаю, о чем вы сейчас говорите, но мне кажется, что не о http. Протокол и сервера давно подготовлены. Я написал статью о том, что и клиенты тоже, просто все их не правильно используют.

> Протокол надо делать stateful,

Не надо «делать», что за чушь. Есть stateless и stateful протоколы. Http — stateless, зачем его делать stateful?

> Ну и почти любой инструмент у нас из коробки не готов к pconnect и к connection pooling.

Бдышь. А я статью о чем написал? Все готово, пользуйтесь.

В общем, все мимо, все не про http.
Да ладно. Вы сейчас про http протокол, или про контент, который вы получили посредством такого протокола?
Я говорил про то, как это выглядит со стороны сервера.

Сервер, который обеспечивает keep-alive для клиента, он внутри тоже делами какими-то занят, нет?
— инициализация бекенда,
— бекенд лезет в базы,
— считаются и наполняются кеши,
— подсоединяются в работу различные сетевые сервисы,
— генерится контент.

Это все коннекции. Они не только на обработку веб-сервером «GET /» и разговор с браузером тратятся. Проблема в том, что веб-сервер, как таковой, имеет очень ограниченное число таких коннекций. WEB уже давно очень сложный и давно уже не визитка из серии «привет, я Вася, это моя страничка в интернет».
> Вы сейчас про http протокол, или про контент, который
> вы получили посредством такого протокола?
Что?

> Сервер, который обеспечивает keep-alive для клиента, он внутри
> тоже делами какими-то занят, нет?
Да.

> Это все коннекции.
Что?

> WEB уже давно очень сложный и давно уже не визитка из серии
> «привет, я Вася, это моя страничка в интернет».
Рад за web. И что из этого следует?

Какой-то беспорядочный набор фраз и вопросов.

> Возьмите еще highload с 2000rps и посмотрите, как у вас keep-alive
> будет работать под такой нагрузкой.
Простите, мне как клиенту, какое дело какая там нагрузка на сервере? Я лишь говорю о том, что если сервер дает такую возможность, надо пользоваться. И в большинстве случаев такая возможность есть.
Как вы так, пишите скрипты и не понимаете, как в целом они работают? Круто! Удачи вам в этом нелегком деле )
Как вы так, пишете комментарии и не можете адекватно сформулировать свои мысли? Удачи вам!
Что конкретно неадекватного? Я вам рассказал, как выглядит keep-alive со стороны сервера. Что конкретно вам не понравилось? Если вам неинтересно, не читайте )) Я понимаю, что фавикон тестить — это довольно круто и достойно статьи на хабре, но вы по-моему не представляете, сколько там вообще проблем вокруг всего этого.

И если интегрировать keep-alive в свой проект, то надо много считать и думать, иначе можно сильно опростоволоситься или попасть на бабло во всяких resource-cost хостингах ))
> Что конкретно неадекватного?
Давайте посмотрим:

> Я вам рассказал, как выглядит keep-alive со стороны сервера.
Вы не начали свой рассказ с того, что хотите рассказать, «как это выглядит со стороны сервера». Вы обозвали меня обезьяной и просто начали говорить, как будто бы по теме статьи. Хотя в ней нет ничего о сервере. В статье дано: сервер уже предоставляет постоянные соединения. Вопрос в том, использовать или нет. Если ваш сервер не справляется с постоянными соединениями, довольно глупо просить меня в комментариях на хабре не использовать их. Вам нужно просто пойти и запретить их.

> Если вам неинтересно, не читайте ))
Вы выступаете с критикой, как будто в мой адрес (хотя как оказалось, не в мой, просто вы об этом не сказали). Я не могу не читать, я вынужден ответить.

> но вы по-моему не представляете, сколько там вообще проблем вокруг всего этого.
Я и не пытаюсь представить. Дано: сервер уже предоставляет постоянные соединения. Goto 1.

> Вы сейчас про http протокол, или про контент, который вы получили посредством такого протокола?
Я серьезно не могу понять, что значит этот ваш вопрос и к чему он относится.

> Как вы так, пишите скрипты и не понимаете, как в целом они работают?
А это к чему?
Это вы подумали, что я вас обезьяной назвал. Вообще так доклад Аверина назывался. Обижаться на такое не стоит, Сергей знатный конечно тролль, но очень умный и понятно все объясняет.

Как будто по теме статьи, да.
Про контент не будем, видно, что объяснять, как устроены нынешние страницы web2.0 будет сильно долго. Как и то, что операционной системе сильно пофигу, браузер у нее висит в ESTABLISHED и смотрит страницу, или сервис, который эту страницу генерит. Зато не пофигу на количество соединений в целом в системе.
Вам тут половина посетителей в коментах пытаются рассказать, что keep-alive — это круто, но с другой стороны все довольно печально и дорого.
Вот если бы вы статью назвали «как быстро скачать фавикон», не было бы споров.
А так, может это для вас будет открытием, но сервисы которые за вебсервером и которые генерят контент, тоже умеют общаться по http между собой. И там тоже можно включить keep-alive для http. Это же не только курл и браузер.
> но с другой стороны все довольно печально и дорого.
С какой другой стороны? Вы опять пытаетесь сказать что клиенту не стоит использовать кипалайв, потому что серверу будет плохо? Плохо — запретите. Не надо в комментариях просить. Коннект — дело обоюдное.

> Про контент не будем, видно, что объяснять, как устроены
> нынешние страницы web2.0 будет сильно долго.
Большое спасибо, что сэкономили всем время. Видно, что только недалекий человек мог сморозить что-то непонятное, а потом сделать такие далеко идущие выводы.

> сервисы, которые за вебсервером и которые генерят контент,
> тоже умеют общаться по http
Так, и?

> И там тоже можно включить keep-alive для http.
Так, и?

> Это же не только курл и браузер.
Так, и?

К чему вы дело то ведете?
Вы серьезно не понимаете? Что то, как вы назвали статью, применимо к широкому кругу задач, в том числе и серверных. И если заниматься посерьезнее вещами, чем вы описали, а например писать что-то серверное, да еще и под нагрузку, то будет ошибкой думать, что кипалайв решит все проблемы. Вы этого в статье не учли. Вот ваши функциональные тесты стали более лучше. А то, что они могут стать неадекватными в один момент или напрягут админа, вас ведь не касается, да?) Главное всем показать, что фавиконка стала быстрее качаться!)

Если вы не понимаете о чем я, то почему вы решили, что я недалекий человек?)
Итак, вы все-таки утверждаете, что на стороне клиента пул постоянных соединений создаст проблемы. Внимание, вопрос: какие проблемы?

> напрягут админа
А что, все админы ленивые бездельники, которые пальцем не пошевелят, чтобы проект стал быстрее работать?
Ну вы посмотрите уже презентацию, там все про проблемы есть.
Например, когда залогиненые пользователи сдвинулись по айдишнику и смотрели чужой контент.

При чем тут безделье админа, если производительность сервера от этого ну никак не зависит? От парней, которые покодить пришли, а не продукт разрабатывать, да, легко ))

Посмотрел, спасибо. Как я и сказал в самом начале, это ни разу не про http и к http неприменимо. Мне жаль потраченного на вас времени и нервов.
База данных и очередь сообщений у меня по http работает, не не применимо ни разу )) Ну да ладно, это не про вас ))
Я вот одного не понимаю, вы вот так себя вели надменно, надмехались над моими уточнениями на ваше непонятные реплики. Все эти «вы серьезно не знаете», «вы правда не представляете», «объяснять вам как устроен веб долго». А потом оказалось, что все что вы говорили было фуфлом. И что? Вы так просто «это не про вас». Ну т.е. мне интересно, вы правда очень неумный? Или вы троль такой умелый? Как вы себе объясняете, что то, чем вы мне в лицо тыкали — было полным бредом?
Акей, я фуфло, везде чушь и вы спасли мир и пофиксили библиотеки. Мне, кстати, тоже мои мысли кажутся иногда бредом, когда не подвергаются проверкой на практике.

Кому надо, все прочитали, что хотели, не переживайте вы так. Рад, что вы хоть видео просмотрели, все скилл прокачался, может пригодится когда-нибудь ))
База данных по http? Это как?
Elasticsearch может так работать.
Не очень быстро, по сравнению с традиционными реляционными базами, но для документов подходит отлично. Она лучше, чем остальные подобные решения тем, что отлично масштабируется, скалируется в разных плоскостях, имеет встроенный полнотекстовый движок, встроенное гео и она практически неубиваемая в условиях геораспределенных данных.
Вы через CURL общаетесь?
CURL и напрямую через nginx, с помощью LUA — модули из сборки openresty.
Попробуйте в течении работы одного скрипта не закрывать соединение с базой, погоняйте тесты. Потом нам расскажите.
Уже было ))
Мы обработали не тот ответ, на который отправляли запрос. У нас залогиненые пользователи посмотрели чужой контент. Воспроизвести только сложно, нужна большая нагрузка.
Ну и конечно вы решили не разбираться в чем дело, а сделали далеко идущие выводы по поводу кипалайва. Вы большой мастер делать далеко идущие выводы. Как у же говорил, это признак недалекого человека.
Почему только я. Нас тут много. Вы все не уйметесь? Вам видео мало? Уже понятно, что вы тут самый умный, остальные несут чушь.
Щас еще скажите опять, что там в видео ни слова про http. Конечно, транспортный уровень ведь решает все проблемы keep-alive. Затестено на фавиконке, понимаю.
Вы точно несете чушь, остальных я не вижу.

> в видео ни слова про http
Все проблемы, перечисленные в видео, либо решены на уровне протокола, либо решаются грамотной реализацией пула. Вы кичитесь тем, что у вас N запросов в секунду, как будто это что-то уникальное и законы физики там действовать перестают. А надо просто головой подумать, что вы сделали не так и еще раз код посмотреть.
При этом никакой конкретики от вас с самого начала не было. «Протоколы не готовы», «все пойдет не так», «бог накажет». Одна чушь.
Да успокойтесь вы уже )) Все чушь. Я чушь. Вы молодец. Я уже признал давно, что вам еще написать надо, чтобы вы успокоились?
Это странно, потому что соединение одно на одно выполнение скрипта. Или вы делали пул курл соединений?
Делали что-то подобное

function curl_post_lookslikeasync($url, $params)
{
    foreach ($params as $key => &$val) {
      if (is_array($val)) $val = implode(',', $val);
        $post_params[] = $key.'='.urlencode($val);
    }
    $post_string = implode('&', $post_params);

    $parts=parse_url($url);

    $fp = fsockopen($parts['host'],
        isset($parts['port'])?$parts['port']:80,
        $errno, $errstr, 30);

    $out = "POST ".$parts['path']." HTTP/1.1\r\n";
    $out.= "Host: ".$parts['host']."\r\n";
    $out.= "Content-Type: application/x-www-form-urlencoded\r\n";
    $out.= "Content-Length: ".strlen($post_string)."\r\n";
    $out.= "Connection: Close\r\n\r\n";
    if (isset($post_string)) $out.= $post_string;

    fwrite($fp, $out);
    fclose($fp);
}

Пул есть, да. Вот это вроде рабочий вариант, как было я уже не вспомню сейчас.
> curl_post_lookslikeasync
Отличное название для функции, которая не curl и не async. А-ха-ха.
А, понятно, это copy-paste development. За кипалайв в хайлоаде бог накажет, а копипаста — надежное продакшен-решение!

Битвин, половину функции можно заменить на http_build_query, о великий гуру хайлоада.
Так я и не программист так-то и не говорил, что я сам такое придумал. Я вот одного не пойму, если вы такой умный, чего у вас минусов в карму столько? Не ценит никто?)
Ой… если Вы не программист, то зачем Вы спорите?
Потому, что мне это потом эксплуатировать и поддерживать и хочется видеть вокруг программистов, которые понимают, что они делают и зачем. И как этот код будет вести себя на сервере в том числе.

Я вообще не понял агрессии на простой невинный комментарий про то, что есть презентация и там про некоторые проблемы всех этих штук рассказано. Тем более лично сам встречался.

В итоге мы все равно пришли к тому, что в каментах "… либо решены на уровне протокола, либо решаются грамотной реализацией пула..", про что в статье ни слова нет вообще ) При этом я мудак и чушь, ну акей, я не обидчивый )
На «простой невинный комментарий про то, что есть презентация и там про некоторые проблемы» не было агрессии, не врите.

Я до последнего старался вести себя адекватно и не обращать внимания на ваши тупые выпады и обвинения, за которыми ничего не стояло. Тут я ошибся, надо было после этого уже не обращать на вас внимание.
Что же сразу не врите? Нормальный комент, без личностей и прочего. Краткий пересказ презентации, ни слова не придумал.
Теперь ваш выход.
— «все их не правильно используют».
— «не надо «делать», что за чушь.»
Адекватно што ппц ) В первом и втором предложении вашего первого же комента, еще и с ошибками. Быстро у вас последнее наступает ))

За сим предлагаю закончить срач и пойти своими дорогами, вы спасать мир, я нести чушь. Желаю вам мира и добра и все такое ))
Вы предложили сделать http stateful. Это несусветная чушь. Признание того, что чушь является чушью — ни агрессия ни разу. До свидания.
O_o
stateful http — несусветная чушь? Что такое, например:
— cookies
— скрытые поля в форме
— URL rewrite
— websockets, comet
— nodejs
— ajax
— тот же keep alive

Технически, протокол stateless. Фактически, уже давно нет. Вам расшифровать?

Самое главное забыли — WebForms. Очень показательный пример как из stateless делают statefull.
В который раз убеждаюсь, что чем огульнее человек обвиняет в непонимании, тем больше вероятность того, что он сам ничего не понимает.

В докладе, на который вы ссылаетесь, постоянно говорится, что stateless протоколы удобнее для постоянных соединений (что и так довольно очевидно). И тут бац, протокол нужно делать stateful.

Что касается http — это абсолютно stateless протокол. Через одно соединение вы можете послать совершенно разные запросы, без разницы что вы слали до этого. Все cookies, скрытые поля, WebForms, URL rewrite (это каким боком тут?) передаются внутри каждого http-сообщения, они не являются состоянием подключения. Необходимость передавать какие-то данные не делает эти данные состоянием.

Точто так же все куки и сессии нужно передавать при ajax-запросах. И нет никакого состояния «ajax-запросы, отправленные с авторизованной страницы», каждый ajax-запрос заново авторизуется.

Я рад что вы знаете слово nodejs, но оно тут явно лишнее. Почитайте в википедии, что это такое. Ну вообще не в тему.

С websockets все чуть интереснее. Сообщения внутри одного сокета действительно передаются без авторизации и других данных. Т.е. websockets — это действительно stateful протокол. Но дело в том, что это не протокол поверх http (как сессии, например), а вообще другой протокол. Сообщения websockets не используют http в качестве транспорта. После установки websockets соединения, это больше не http соединение. Вы не можете отправить другой http-запрос по соединению, в котором открыт websocket. Websockets и keep alive — две разные технологии для постоянных подключений, они существуют параллельно и не пересекаются.

Надеюсь, что я достаточно подробно объяснил, «как устроены нынешние страницы» и вы больше не будете так падать в грязь лицом.
А теперь, пожалуйста, напишите об этом статью с примерами. Жутко заинтересовало.
Чтобы вы опять написали, что я вам глаза открыл? Или это сообщение тоже сарказм?
Без сарказма. Интересует тема websokets, желательно с детальным разбором принципов работы. А то статей много, а по-существу написано мало.
Все cookies, скрытые поля, WebForms, URL rewrite (это каким боком тут?) передаются внутри каждого http-сообщения, они не являются состоянием подключения. Необходимость передавать какие-то данные не делает эти данные состоянием.

Но делается это во многом для того, чтобы эмулировать stateful
Не имеет значение, что построено поверх. Это не делает сам протокол stateful. В википедии есть отличный абзац на эту тему:
There can be complex interactions between stateful and stateless protocols among different protocol layers. For example, HTTP is an example of a stateless protocol layered on top of TCP, a stateful protocol, which is layered on top of IP, another stateless protocol, which is routed on a network that employs BGP, another stateful protocol, to direct the IP packets riding on the network.
О, вы книги читать начали, уже упех ))
Все эти пункты, и не только, давно позволяют эмулировать stateful, о чем я и написал. «Технически, протокол stateless. Фактически, уже давно нет». Клиент и сервер вроде вполне себе обмениваются состоянием, не?

Про Comet я еще писал, где про него справка:?)
Сессии — протокол поверх http? О_о

Идите дочитывайте )
БД по http это вообще жестоко. Чем вас tcp/ip не устроил-то?

Вы вкурсе что у http весьма приличный оверхед? Или вы картинки из БД получаете по http?
Сходить туда с фронтенда напрямую и сразу показать готовый json дешевле, чем готовить его на лету.
Elasticsearch еще и поисковый движок, что успешно заменяет такую последовательность, как, например,
nginx->phpfpm->некий нормализатор запроса->phpfpm->сфинкс->phpfpm->постгрес->phpfpm->nginx.
У вас какая-то мухоморная последовательность. Кто у вас так пишет?

сервер< — >скрипт< — >сфинкс

как запускается скрипт через mod_php, cgi, fcgi, fpm не имеет существенного значения.
«Пивко в кросноярске рядом с плашкой» — вот вам пример запроса. Сфинкс в одного тут найдет чуть больше, чем ничего ))) Нужна нормализация и непростой гео.
Да ну ладно?
Вот и делайте свою нормализацию и «непростой гео» в рамках скрипта.

Или Elasticsearch внезапно найдет больше? :D
Сказки давайте-ка будем рассказывать другим. Ок?

habrahabr.ru/company/sphinx/blog/61807/
это я оставлю вам. для самообразования
Ну вы молодец! У нас полста человек два года с релевантностью поиска справиться не могут, а ведь просто статью не читали! ) Спасибо за ссылку!
> Я говорил про то, как это выглядит со стороны сервера.
Я не со стороны сервера. не могу принудить сервер держать коннект, он делает этим добровольно. Я призываю пользоваться.
Поддержу. keep-alive — это просьба клиента серверу не рвать соединение, но он волен исполнять её или нет.
А если HTTP 1.1 и нету в ответе Connection, то что по умолчанию? А то включил Keep-alive в IIS, но в ответе Connection нету.
A significant difference between HTTP/1.1 and earlier versions of HTTP is that persistent connections are the default behavior of any HTTP connection.

Проще говоря, если не получен Connection: close, то клиент должен считать, что keep-alive. Впрочем сервера это тоже касается.
Офигеть, статья про повторное использование хэндла curl. PHP-шники, как всегда, в своем репертуаре.
Офигеть, очередной бесполезный комментарий на Хабре. PHP-хейтеры, как всегда, в своем репертуаре.
Sign up to leave a comment.

Articles