«phpClub» — архив тем ("тредов"), посвящённых изучению PHP и веб-технологий.
Аноним 2018/05/13 12:30:27  №1189148 1
Если PHP обращается к приложению в консоле (через proc_open/close), то это приложение будет использовать системные переменные окружения или те что заданны в PHP в переменной $_ENV?

К примеру, если я в системе создам переменную окружения FOO='foo', а в PHP задам $_ENV['FOO']='bar'; то какое значение получит консольное приложение запущенное с помощью PHP?
Ответы: >>1189281 >>1190032
Аноним 2018/05/13 16:06:49  №1189281 2
>>1189148
Нашел ответ проверив экспериментально:

Изменение переменной $_ENV - никак не отразилось на изменение переменных окружения.
Изменение глобальных переменных с помощью putenv("FOO=bar"); передалось и в консольное приложение, которое мы вызываем с помощь PHP.

В мануале по этой функции сказано:
>Переменная будет существовать только на время выполнения текущего запроса. По его завершении переменная вернется в изначальное состояние.

Из чего можно сделать вывод, что у каждого "консольного клиента", в том числе и PHP, свой набор переменных окружения.
Ответы: >>1190032
chat someApprentice 2018/05/13 16:57:00  №1189311 3
https://github.com/someApprentice/chat

В моем приложении есть серьёзная проблема.

Обновление сообщений и контактов происходит вызовом скрипта c определённым интервалом и возвратом времени когда этот скрипт был вызван, чтобы получать из БД только новые на текущий момент сообщения. Если сообщения были отправлены как раз в этот интервал (т.е. между вызовами скрипта), то эти сообщения не обновятся.

К тому же, ещё есть большая проблема с потреблением памяти, со временем, если оставить приложение открытом, то, спустя какое-то время, вызов скриптов загрузит всю память. У меня дошло до ~1.4Гб за менее чем пол часа.


В популярной соц.сете, эта проблема решается следующим образом, добавляется параметр wait и ответ поступает только спустя это время. Но, если собеседник отправляет сообщение, то этот скрипт вызывается мгновенно.
Я не понимал как это возможно, пока не написал это сообщение, и мне пришла идея, что когда кто-то отправляет сообщение, то сервер перестает ждать и отправляет ответ.

Как можно добиться подобного поведения в PHP? Мне приходит на ум, что это асинхронное получение результатов из БД... То есть, если в БД появились новые сообщения, то скрипт триггерется.
Ответы: >>1189376 >>1190031
Аноним 2018/05/13 17:47:46  №1189376 4
>>1189311
Пхп не предназначен для постоянной работы.
Сделай чтобы при запросе сообщений передавалось не время ответа а время последнего полученного сообщения. Тогда ты сможешь отдать все сообщения после последнего отданного.
Ответы: >>1189406 >>1190032
someApprentice 2018/05/13 18:35:37  №1189406 5
Ответы: >>1190032
Аноним 2018/05/14 23:26:48  №1190031 6
>>1189311

> К тому же, ещё есть большая проблема с потреблением памяти, со временем, если оставить приложение открытом, то, спустя какое-то время, вызов скриптов загрузит всю память. У меня дошло до ~1.4Гб за менее чем пол часа.

Нужно искать место утечки памяти. В идеале, должен быть какой-то инструмент, который меряет число выделений/освобождений памяти, находит неосвобожденные куски и пишет стектрейсы, где они были выделены.

Гугление находит:

1) https://github.com/BitOne/php-meminfo

2) Использовать режим trace в xdebug, который пишет в лог каждый вызов функции и потребление памяти в этот момент. Как-то проанализировать лог в поисках функций, при вызове которых потребление только растет.

Что-то еще я нагуглил тут: https://stackoverflow.com/questions/16068301/how-to-find-which-php-script-is-leaking-memory/36288686#36288686

> Обновление сообщений и контактов происходит вызовом скрипта c определённым интервалом и возвратом времени когда этот скрипт был вызван, чтобы получать из БД только новые на текущий момент сообщения.

Скрипт на сервере должен возвращать время для следующего запроса. Тут стоит еще помнить, что время может быть разным на клиенте/сервере и лучше использовать в запросе полученное от сервера же время.

> В популярной соц.сете, эта проблема решается следующим образом, добавляется параметр wait и ответ поступает только спустя это время. Но, если собеседник отправляет сообщение, то этот скрипт вызывается мгновенно.
> Я не понимал как это возможно, пока не написал это сообщение, и мне пришла идея, что когда кто-то отправляет сообщение, то сервер перестает ждать и отправляет ответ.

Это так называемый long polling - когда скрипт "подвисает" до наступления события (или до таймаута). Это вариант для случаев, когда недоступен или не хочется использовать вебсокет. Возможно соцсеть использует его потому, что этот метод более устойчив в случае разрывов связи, но я не уверен, это надо делать тесты. Или может, потому что он кросс-браузерней и не требует вебсокета. Или потому, что не все прокси (в организациях) пропускают вебсокет.

Таймаут нужен, так как браузер, а также промежуточные прокси или NAT-сервера могут прибивать "подвисшие" соединения, по которым ничего не передается. Потому обычно его ставят в пределах 20-60 секунд.

Опишем задачу: у нас есть один процесс (или поток) на сервере, который обрабатывает соединение от клиента (wait.php) и "висит" в ожидании уведомлений для него или в ожидании таймаута - subscriber. И есть второй процесс/поток, который добавляет новое сообщение в БД и хочет как-то уведомить все ждущие потоки первого типа о появлении нового сообщения - publisher. Такая архитектура еще называется publish/subscribe (pub/sub) - есть те, кто "публикует" сообщения, и есть те, кто "подписывается" на их получение.

Чтобы понять, кто какое уведомление ждет, введем "идентификатор канала" - channelId. Подписчик подписывается на сообщения с определенным channelId. Наша система лишь уведомляет об изменениях, она не передает их содержание, подписчик должен сам извлечь новые сообщения из базы данных.

channelId может, например, просто равняться userId пользователя, которому предназначено сообщение.

Реализуется это в общем за счет межпроцессного (межпоточного) взаимодействия, IPC: https://ru.wikipedia.org/wiki/%D0%9C%D0%B5%D0%B6%D0%BF%D1%80%D0%BE%D1%86%D0%B5%D1%81%D1%81%D0%BD%D0%BE%D0%B5_%D0%B2%D0%B7%D0%B0%D0%B8%D0%BC%D0%BE%D0%B4%D0%B5%D0%B9%D1%81%D1%82%D0%B2%D0%B8%D0%B5

Это набор "примитивов", которые реализуются по-разному в зависимости от ситуации, используемой ОС и тд. При этом не очень принципиально, о чем идет речь - о потоках, о "зеленых" тредах (асинхронное программирование), о процессах или даже о запущенных на разных машинах процессах. Я буду писать просто "поток".

Если посмотреть на примитивы IPC, то для нашей задачи подошли бы, например, семафоры ( https://ru.wikipedia.org/wiki/%D0%A1%D0%B5%D0%BC%D0%B0%D1%84%D0%BE%D1%80_(%D0%B8%D0%BD%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82%D0%B8%D0%BA%D0%B0) ). Семафор умеет блокироваться до того, как его "отпустят". Вот, как может выглядеть код:

// Создаем доступную всем потокам таблицу семафоров. Она имеет структуру
//
// {
// channel1: [ semaphore1, semaphore2, semaphore3 ],
// channel2: [ semaphore ],
// ....
// }
//
// Каждый подписчик создает и кладет в таблицу свой семафор под нужным ему channelId.
// На одном канале может быть несколько подписчиков.
//
var semaphores = {};

// Функция для потока-подписчика, она ждет публикации
// сообщения с данным channelId неограниченное время
function waitForMessages(channelId) {

// создаем семафор, который блокируется при попытке захвата
var semaphore = initSemaphore(0);

// добавляем его в список
if (!semaphores[channelId]) {
semaphores[channelId] = [];
}

semaphores[channelId].push(semaphore);

// блокируемся до появления события
// (так как семафор имеет значение 0)
semaphore.enter();

// получено новое сообщение, удаляем не нужный более семафор
// из массива
removeFromArray(semaphores[channelId], semaphore);
semaphore.destroy();
}

// Функция для издателя, "будит" все потоки, ждущие сообщения
// с данным channelId
function publishMessage(channelId) {
// Находим все ждущие семафоры и "отпускаем" их
if (!semaphores[channelId]) {
return;
}

semaphores[channelId].forEach(function (semaphore) {
// Отпускаем семафор и позволяем ждущему на нем потоку
// захватить его в enter()
semaphore.leave();
});
}

В этом коде я использовал особенность Node.JS, что она однопоточная и функция выполняется целиком, потому нам не надо беспокоиться о гонках между потоками (например, в процессе выполнения forEach никто не добавляет или удаляет элементы из массива). В других языках могут потребоваться дополнительные блокировки во время работы с разделяемым массивом semaphores.

Также, я не реализовал таймаут ожидания уведомления - оставим это как домашнее задание читателю, например, можно "отпускать" семафор в подписчике примерно таким кодом:

setTimeout(function () {
semaphore.leave();
}, timeout);

Это усложнит код, так как при срабатывании семафора до таймаута желательно уничтожать таймаут, чтобы освободить ресурсы.

Также, приведенный выше код требует, чтобы из всех потоков была доступна переменная semaphores и потому работает только внутри одного процесса Node.JS. Это лишь демонстрация архитектуры pub/sub.

Если ты хочешь реализовать pubsub между разными процессами, или тем более, разными машинами, этот подход не подойдет и проще использовать брокер (брокер = посредник при сделке) - специальный процесс, реализующий паттерн pubsub. Брокер является сервером (например, TCP-сервером), к нему подсоединяются клиенты-подписчики, ждущие новых сообщений, и клиенты-издатели, желающие уведомлять подписчиков.

Например, есть такие варианты:

- pubsub встроен в сервер redis: https://redis.io/topics/pubsub который может служить брокером
- самописный брокер на ReactPHP, в котором есть поддержка WAMP (Websocket App. Messaging Protocol), в котором есть в том числе и подписки, а также поддержка и websocket, и long polling. Это работает внутри одного процесса PHP.
- написать свой брокер на Node.JS с использованием готовых библиотек
- написать на Го
- сервер очередей RabbitMQ: https://ru.wikipedia.org/wiki/RabbitMQ - он, кстати, поддерживает HTTP и возможно, подписчик может подсоединяться напрямую с клиента
- сервер очередей ZeroMQ

Есть и другие брокеры. Есть так же более-менее стандартные протоколы MQTT и AMPQ, чтобы для разных брокеров можно было использовать общие клиентские библиотеки.

Ты можешь использовать pubsub внутри одного процесса (например, иметь единый процесс на ReactPHP, который обслуживает все соединения и который внутри содержит брокер, например, встроенный в WAMP) или использовать отдельный, внешний брокер. В этом варианте подписчики соединяются с брокером в ожидании уведомлений, а издатели подсоединяются к брокеру для публикации уведомления.

В случае внешнего брокера, есть опять же 2 варианта:

- браузер подсоеиняется к процессу PHP, который соединяется с брокером
- браузер напрямую подсоединяется к брокеру, если он поддерживает HTTP и такой режим доступа

Браузер может использовать long polling или websocket в зависимости от условий и реализации. Если используется websocket и протокол WAMP, то в него уже встроена возможность подписываться на каналы, и клиентская JS-библиотека с нужными функциями - сервер должен ее только реализовать. Вот пример такой клиентской библиотеки: https://github.com/crossbario/autobahn-js

В плане масштабирования есть такие варианты:

- использовать единый внешний брокер сообщений (готовый или самописный) - это годится для маленьких масштабов.
- использовать много брокеров, составить соответствие между channelId и IP/портом брокера и подсоединяться к нужному - это неограниченно масштабируемая схема.

Надеюсь, я не слишком запутал? Задавай уточняющие вопросы тогда.
Ответы: >>1190295 >>1196856
Аноним 2018/05/14 23:29:30  №1190032 7
>>1189376

Вполне себе предназначен - ReactPHP.

>>1189406

Я бы не советовал вообще вычислять время на сервере PHP, чтобы не сталкиваться с проблемами расхождения времени на клиенте, сервере, особенно если их несколько. Лучше пусть клиент присылает время последнего доступного ему сообщения, или, может, id (чтобы он был уникальным).

>>1189281

Массив $_ENV это лишь копия переменных, доступных в момент запуска PHP скрипта. Его изменение ни на что не влияет. Ты можешь изменить переменные окружения процесса (для текущего процесса, которые будут наследоваться запущенными из него процессами) с помощью putenv(). А получить текущие значения с помощью getenv().

Кстати, в некоторых конфигурациях $_ENV вообще пуст. Его заполнение включается где-то в php.ini.

> Из чего можно сделать вывод, что у каждого "консольного клиента", в том числе и PHP, свой набор переменных окружения.

У каждого процесса в ОС своя копия переменных. Она копируется от родителя в момент создания процесса, но это именно независимая копия.

>>1189148

Она унаследует переменные процесса PHP в момент запуска внешней программы. $_ENV ни на что не влияет.


Ответы: >>1190042 >>1190295
someApprentice 2018/05/15 11:15:22  №1190295 8
>>1190031
>> К тому же, ещё есть большая проблема с потреблением памяти, со временем, если оставить приложение открытом, то, спустя какое-то время, вызов скриптов загрузит всю память. У меня дошло до ~1.4Гб за менее чем пол часа.
>
>Нужно искать место утечки памяти. В идеале, должен быть какой-то инструмент, который меряет число выделений/освобождений памяти, находит неосвобожденные куски и пишет стектрейсы, где они были выделены.
>
>Гугление находит:
>
>1) https://github.com/BitOne/php-meminfo
>
>2) Использовать режим trace в xdebug, который пишет в лог каждый вызов функции и потребление памяти в этот момент. Как-то проанализировать лог в поисках функций, при вызове которых потребление только растет.
>
>Что-то еще я нагуглил тут: https://stackoverflow.com/questions/16068301/how-to-find-which-php-script-is-leaking-memory/36288686#36288686
Надо было уточнить, что потребление памяти со стороны клиента, т.е. браузера. Которая происходит, как я предполагаю, потому что, с поступлением новой информации из постоянных обновлений, растёт и объем памяти в браузере, который их зачем-то туда сохраняет.


>> Обновление сообщений и контактов происходит вызовом скрипта c определённым интервалом и возвратом времени когда этот скрипт был вызван, чтобы получать из БД только новые на текущий момент сообщения.
>
>Скрипт на сервере должен возвращать время для следующего запроса. Тут стоит еще помнить, что время может быть разным на клиенте/сервере и лучше использовать в запросе полученное от сервера же время.
Или таймстамп. Но зачем возвращать время для следующего запроса, в клиенте можно же самому выставить интервал?


>> В популярной соц.сете, эта проблема решается следующим образом, добавляется параметр wait и ответ поступает только спустя это время. Но, если собеседник отправляет сообщение, то этот скрипт вызывается мгновенно.
>> Я не понимал как это возможно, пока не написал это сообщение, и мне пришла идея, что когда кто-то отправляет сообщение, то сервер перестает ждать и отправляет ответ.
>
>Это так называемый long polling - когда скрипт "подвисает" до наступления события (или до таймаута). Это вариант для случаев, когда недоступен или не хочется использовать вебсокет. Возможно соцсеть использует его потому, что этот метод более устойчив в случае разрывов связи, но я не уверен, это надо делать тесты. Или может, потому что он кросс-браузерней и не требует вебсокета. Или потому, что не все прокси (в организациях) пропускают вебсокет.
Значит нужно поскорей переходить на вебсокет...

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

Виртуальный сервер к которому приходят запросы это один единственный процесс или на каждый запрос создается свой собственный?

Если виртуальный сервер, это один единственный процесс, то я могу предположить, что значит уже сам PHP создает "дерево" из нескольких процессов, это так?

В вашей задаче вы описали два процесса publisher и subscriber. Соответственно, эти два процесса должны быть как-то описаны - быть сущностью или, проще говоря, скриптовым файлом. Я представлял, что приложение по цепочке вызывает все зависимости и в итоге становиться одним целым, т.е. я не думал что на каждую зависимость создаётся свой процесс. Неужели это действительно так?

Чтобы понять как работает паттерн pub/sub, я заглянул на вики и там обнаружил, что
>Шаблон издатель-подписчик представляет собой расширение шаблона наблюдатель, в который добавлено описание канала событий (англ. event channel), специально предназначенного для оповещения о событиях.
То есть описание канала событий это обёртка над методами межпроцессных взаимодействиями, в нашем случае семафором?


>>1190032
>Я бы не советовал вообще вычислять время на сервере PHP, чтобы не сталкиваться с проблемами расхождения времени на клиенте, сервере, особенно если их несколько. Лучше пусть клиент присылает время последнего доступного ему сообщения, или, может, id (чтобы он был уникальным).
А клиент не пользуется этим временем, он просто пересылает его обратно PHP. Однако, я вижу аргумент за использование ID вместо даты - так надёжнее.
Ответы: >>1190415
Аноним 2018/05/15 14:09:09  №1190415 9
>>1190295

Если в браузере - попробуй использовать средства разработчика, вкладка Profiles, опции Take Heap Snapshot и Record Heap Allocations. Такое потребление абсолютно ненормально и скорее всего вызвано ошибками в коде.

Также, чтобы проблема была заметнее, можно попробовать специально надобавлять очень много сообщений.

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

Тут имеется в виду, не время, когда сделать запрос, а время, начиная с которого искать новые сообщения. Ну то есть в начале клиент например посылает запрос

/new-messages?from=0

получает какие-то сообщения и новое значение для from (или вычисляет его сам из максимального времени у последнего сообщения). И использует это значение в следующем запросе.

> Значит нужно поскорей переходить на вебсокет...

Тут лучше сделать сравнение. Кто знает, может при неустойчивой связи как раз long polling надежнее работает.

> Прежде чем разобраться межпроцессным взаимодействием, нужно разобраться как работают процессы.

Процесс - это, условно, говоря абстрактная сущность, которая соответствует выполняющейся программе и под которую ОС выделяет ресурсы, в первую очередь память и процессорное время. Ты можешь увидеть список процессов командами вроде ps lax или top.

Процесс в ядре имеет уникальный номер (PID = process id, а также TGID = thread group id), который ты можешь увидеть в выводе команды top или ps lax в колонке с заголовком PID. Ресурсы - в первую очередь это изолированная область памяти (плюс еще кое-что по мелочи: набор переменных окружения, таблица открытых файлов и тд). Когда ты запускаешь какую-то команду (например, ls), ядро ОС создает новый "процесс", добавляет его в список процессов, выделяет кусок памяти, мапит (отображает) туда исполняемый файл /bin/ls и создает внутри процесса "поток", который будет выполнять двоичный код из этого файла.

"Поток" (тред, поток выполнения кода) это абстрактная штука, которая может выполнять код. В каждом живом процессе есть минимум один поток. В ядре ОС есть планировщик потоков, который постоянно переключает имеющиеся процессоры между потоками, создавая иллюзию параллельного выполнения всех потоков. Код может порождать новые потоки внутри того же процесса, также, потоки могут завершаться, если они выполнили свой код и больше не нужны. В линуксе потоки еще называются Light Weight Processes, они идентифицируются уникальным номером TID (thread id). Чтобы увидеть отдельные потоки, надо добавить к команде ps lax опцию -T, она выводит идентификатор потока (тот, что TID) под заголовком SPID, а идентификатор процесса под заголовком PID. Путаница вызвана историческими причинами, в первую очередь тем, что современные потоки появлились где-то в районе linux 2.4, а до этого была другая система ( https://unix.stackexchange.com/questions/364660/are-threads-implemented-as-processes-on-linux ).

Мануал по функции clone(), порождающей процессы и потоки в Линуксе, описывает названия TID, PID и TGID: http://man7.org/linux/man-pages/man2/clone.2.html Можно считать, что PID/TGID совпадает с TID самого первого потока в процессе.

> Виртуальный сервер к которому приходят запросы это один единственный процесс или на каждый запрос создается свой собственный?

Ты про HTTP сервер? Это зависит от используемого веб-сервера и ОС. Если ты используешь mod_php, как плагин к Апачу, то по умолчанию под Линуксом Апач создает N процессов-рабочих и 1 процесс-диспетчер, который принимает и раскидывает приходящие HTTP-запросы между рабочими. Он также следит за рабочими, создает новые при росте нагрузки, или при их аварийном завершении, или прибивает ненужные. Код интерпретатора PHP подгружается как библиотека внутрь процесса-рабочего Апача и выполняется внутри него. У Апача есть специальное API, ты пишешь код на Си, компилируешь в библиотеку, подключаешь ее в конфиге и Апач вызывает твой код при обращении к определенному типу файла. Разработчики PHP поставляют интерпретатор в виде плагина к Апачу. Соответственно, при обращении к php-файлу Апач вызывает функцию из плагина, которая выполняет файл как PHP код.

Если ты используешь php-fpm, то там похожая схема, есть главный процесс и несколько процессов-рабочих. Диспетчер раздает запросы рабочим, и они выполняют указанный в запросе PHP-скрипт. Но бывают и другие варианты, например, когда создается несколько рабочих тредов внутри одного процесса.

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

Такого, что на каждый входящий запрос запускается новый процесс, обычно не делают, это неэффективно. Так когда-то давно было в протоколе CGI - веб-сервер при поступлении запроса просто запускал приложение и оно выводило на стандартный вывод HTML-код, который сервер оборачивал в HTTP-заголовки и отправлял клиенту.

Хотя процесс интепретатора PHP обычно не завершается, но контекст, в котором выполняется PHP-скрипт (список созданных PHP-переменных, функций, классов) обычно очищается после обработки одного HTTP-запроса. То есть приходит HTTP-запрос, создается новый пустой "контекст", php-скрипт работает, подгружает файлы, создает переменные, потом завершается и "контекст" уничтожается и следующий PHP-скрипт запускается в чистом, пустом окружении, как будто предыдущего скрипта и не было.

Проще говоря, в классической схеме твои PHP-скрипты - это лишь обработчики, которые сервер вызывает, когда ему нужно, в таком окружении, которое он настроил.

Если тебе это не нравится, ты можешь написать консольное PHP-приложение, которое само будет веб-сервером: открывает порт, принимает HTTP-запросы, обрабатывает их и отправляет ответы. В такой ситуации "контекст" не будет очищаться и ты можешь из обработчика одного запроса создать переменную, которая будет доступна в обработчике другого. То есть там может быть примерно такой код:

// Открываем TCP-порт
$socket = createListeningSocket(9001);
while (true) {
// Принимаем новое соединение от клиента
$connection = acceptConnection($socket);
handleConnection($connection);
}

Но это будет довольно неэффективно, так как если клиент тупит с отправкой или приемом данных, то твой сервер ждет его и ничего не делает. Ну и такое приложение не способно использовать более 1 ядра процессора. Если ты хочешь писать свой веб-сервер, лучше задействовать ReactPHP, который реализует асинхронную обработку событий.

> В вашей задаче вы описали два процесса publisher и subscriber. Соответственно, эти два процесса должны быть как-то описаны - быть сущностью или, проще говоря, скриптовым файлом

Издателем тут будет скрипт, который добавляет в базу новое сообщение, а подписчиком - скрипт, который ждет новых сообщений в схеме с long polling либо поток, обслуживающий websocket-соединение.

В общем случае это могут быть процессы даже на разных машинах (если у нас много серверов). Ну например, пусть у нас при отправке сообщения вызывается скрипт /send.php, он может выглядеть так:

...
$msg = new Message();
...
saveMessageToDb($msg);
notifyUser($toUser); // эта функция должна как-то вызвать функционал издателя

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

То, что ты описываешь, получится, если ты запускаешь свое приложение с помощью встроенного в PHP веб-сервера. Он однопоточный и выполняет все приходящие HTTP-запросы внутри одного процесса по очереди. Но это невыгодно, так как один процесс, если он однопоточный, может использовать максимум одно ядро процессора. А при высоких нагрузках, очевидно, одного ядра недостаточно.

Я описывал архитектуры серверов (не только веб-, а вообще любых работающих по схеме клиент-сервер приложений) вот тут вот: https://gist.github.com/codedokode/ffd520440a970c07c1c6 - есть самые разные варианты.

> То есть описание канала событий это обёртка над методами межпроцессных взаимодействиями, в нашем случае семафором?

Да. Семафор - это примитив, низкоуровневая сущность. Во многих ОС есть системные вызовы для создания и управления семафорами. pub/sub это штука, которая гораздо на более высоком уровне и она реализуется поверх примитивов ОС. То есть обычно для pub/sub нужна или библиотека или отдельное приложение-брокер. Отдельное приложение-брокер 100% нужно для распределенных систем, так как примитивы IPC работают только внутри одной машины.
Ответы: >>1196856
Аноним 2018/05/25 12:46:26  №1196711 10
Господа, есть регулярка находящая одну латинскую букву в окружении двух возможных на кириллице, которых может не быть.

/[а-яёА-ЯЁ]?[a-zA-Z][а-яёА-ЯЁ]?/ui

Как сделать, чтобы она находила латинскую букву с обязательной кириллической буквой рядом или с обоих сторон?
Ответы: >>1196769
Аноним 2018/05/25 14:14:22  №1196769 11
>>1196711
/([А-Яа-я]([A-Za-z]))|(([A-Za-z])[А-Яа-я])|([А-Яа-я]([A-Za-z])[А-Яа-я])/ui

Но это не точно.
Ответы: >>1196778 >>1198634
Аноним 2018/05/25 15:07:02  №1196796 12
стоит ли сильно задрачивать чистый пхп и его технологии,если есть фреймворки?
в Б меня обоссали за попытку пилить сайт на чистом языке
Ответы: >>1196798 >>1196799
Аноним 2018/05/25 15:11:51  №1196798 13
>>1196796
Как ты поймешь фреймворки не зная основ языка на котором они написаны?
Ответы: >>1196805
Аноним 2018/05/25 15:17:05  №1196805 14
>>1196798
мне сказали,что там высокая абстракция ,и сильные знания чистого не нужны
Ответы: >>1196865 >>1198634
someApprentice 2018/05/25 17:23:20  №1196856 15
>>1190031
>>1190415
Прошу прощения за столь долгий ответ - пришлось набираться сил чтобы погрузитья глубже в изучение, но, каким-то чудом, я, вроде, всё понял. Даже вопросов нет, почти.

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

Технологии, которые я буду использовать:
Redis Pub/Sub https://redis.io/topics/pubsub
phpredis https://github.com/phpredis/phpredis
ReactPHP EventLoop (для реализации таймаута ожидания) https://reactphp.org/event-loop/

subscribe.php
<?php

$redis = new Redis();
$redis->open(...); // соеденяемся

// Каналами может быть всё что угодно - id переписок, пользователей, всё на что мы хотим подписаться
// В нашем случае можно опустить только до сообщений для простоты
$channels = [...];

$message = new Message();

$loop = React\EventLoop\Factory::create();

$loop->addTimer($interval = 30, function () use ($redis) {

//unsubscribe
$redis->close();

//возвращаем пустое сообщение
});

$loop->run();


$message = $redis->subscribe($channels, function($r, $c, $m) use ($message) {

return $message->set($m);

});

//возвращаем получившиеся сообщение (либо пустое, либо то что вернул брокер)
return $message;

?>

publish.php
<?php

$database = new Database(...);

$redis = new Redis();
$redis->open(...);

$channels = [...];

$message = new Message($_POST['message']);

$message = $database->add($message);

foreach ($channels as $channel) {

$redis->publish($channel, $message); // перевод объекта Сообщения в строку опустим для простоты

}

// возвращаем сообщение назад в случае успеха
return $message;

?>

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

Однако, у меня, всё же, появились вопросы пока я писал этот пост, и я вынжден попрасить вас ответить на них.

https://github.com/phpredis/phpredis#subscribe
Я не знаю языка C, поэтому я не могу разобраться, ассинхронная ли это функция? Должно быть, ассинхронная. То есть, её возврат и присваиванее в переменную $message, в моём коде, не произойдет, если никакое сообщение так и не прийдёт?
Если это так, то можно сначала подписаться, а потом уже поставить таймайут ожидания.

https://github.com/phpredis/phpredis#publish
Опять же, из-за незнания C, я не знаю, что эта функция вернет? Как всегда, true/false в случае успеха/неудачи?

Приемлимо для обмена сообщений между клиентами Redis использовать строку в виде данных json? Или есть какая-то другая договрённость для обмена большого количества информации?
К примеру, в моём коде, я хотел опубликовать весь объект Сообщения, включая id, само сообщение, дату и так далее.

Как я и раньше, брал в пример поведение одной популярной соц.сети, так и сейчас хотелось бы рассмотреть одно из её поведений: Когда происходит одно из уведомлений (не важно, приходит ли сообщение или собеседник набирает сообщение, или кто-то подписывается) всегда выполняется один и тот же запрос.
Мне кажется, это эффективно - меньше дёрганий на обращение к БД на обнавление сообщений/контактов/проверки набора сообщений.
А что вы думаете?

И кстати, в моём примере, при подписке, я не где не обращаюсь к БД, а жду что ко мне придёт уже готовое сообщение. В этом нет ничего плохого?



Спасибо большое.
Ответы: >>1198634
Аноним 2018/05/29 03:02:01  №1198634 16
>>1196769

Упрощение:

(рус)(лат)(рус)?|(лат)(рус)

>>1196805

Это неправда. Фреймворки нельзя изучать, не зная языка, в том числе концепций ООП.

>>1196856

> https://github.com/phpredis/phpredis
Обрати внимание, что эта библиотека не использует исключения и ты должен проверять результат каждого вызова, при ошибке сам получать подробности ошибки. Игнорировать ошибки, естественно, не дело.

Насчет метода subscribe - я не уверен, что тут подойдет ReactPHP. Разве метод subscribe() работает асинхронно? Увы, документация по phpredis это никак не освещает (если у тебя есть время, можешь допилить ее), примеров использования я тоже в репозитории не нашел. Как я понимаю, клиент redis при выдаче команды SUBSCRIBE начинает просто ждать сообщений от redis, блокируя тред. То есть вызов $redis->subscribe() блокирующий. И до $loop->run выполнение просто не дойдет.

Для таймаута, судя по https://github.com/phpredis/phpredis/issues/634 можно использовать опцию задания таймаута.

Возможно, правильнее использовать pubsub не для передачи самих сообщений, а для передачи уведомления о том, что что-то изменилось. А за обновлениями лезть в БД.

Также, нужна защита от ошибок. Что, если твой PHP скрипт получил из redis сообщение и умер? Клиент отправляет новый запрос, но сообщение-то уже забрано из redis. Защититься от этого можно такой логикой:

- проверить изменения в БД, если есть, вернуть клиенту
- ждать событий в redis с таймаутом
- проверить изменения в БД, если есть, вернуть клиенту

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

> https://github.com/phpredis/phpredis#subscribe
> Я не знаю языка C, поэтому я не могу разобраться, ассинхронная ли это функция?
Думаю, нет. Вот ее код:

- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis.c#L2491
- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis_commands.c#L755 (генерирует текст команды subscribe)
- https://github.com/phpredis/phpredis/blob/2828c2f187900a5ddc450ce857b467d9e0b483ac/library.c#L272 (обрабатывает ответ на нее)

Код не особо понятный, но я вижу там

> / Multibulk response, {[pattern], type, channel, payload } /
> while(1) {
> if (!redis_sock_read_multibulk_reply_zval(
> INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)) break;
> ....
> if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC)
> ==FAILURE) ...
> }

То есть блокирующий цикл.

> https://github.com/phpredis/phpredis#publish
> Опять же, из-за незнания C, я не знаю, что эта функция вернет? Как всегда, true/false в случае успеха/неудачи?

Судя по коду

- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis.c#L2477
- https://github.com/phpredis/phpredis/blob/2828c2f187900a5ddc450ce857b467d9e0b483ac/library.c#L1040

> if(ret > LONG_MAX) { / overflow /
> RETVAL_STRINGL(response + 1, response_len - 1);
> } else {
> RETVAL_LONG((long)ret);
> }
> ...
> if (IS_ATOMIC(redis_sock)) {
> RETVAL_FALSE;

Есть такие варианты:

- целое число (long - это 32-битное целое)
- строка с числом, если оно большое
- false

Что это за число - надо смотреть документацию по PUBLISH в redis, что она вернет.

> Приемлимо для обмена сообщений между клиентами Redis использовать строку в виде данных json? Или есть какая-то другая договрённость для обмена большого количества информации?
Приемлемо. Есть и много других форматов конечно - BSON, Protocol Buffers и тд.

> К примеру, в моём коде, я хотел опубликовать весь объект Сообщения, включая id, само сообщение, дату и так далее.

Да, только к моменту получения оно может устареть в сравнении с копией в базе.

> Мне кажется, это эффективно - меньше дёрганий на обращение к БД на обнавление сообщений/контактов/проверки набора сообщений.
> А что вы думаете?

Не знаю. Зависит от ситуации наверно.

> И кстати, в моём примере, при подписке, я не где не обращаюсь к БД, а жду что ко мне придёт уже готовое сообщение. В этом нет ничего плохого?

Надо учитывать возможность устаревания данных или их потерь.
Ответы: >>1199240
someApprentice 2018/05/30 03:05:45  №1199240 17
>>1198634
То что phpredis работает не асинхронно, это вызывает сомнения в использовании этой библиотеки. Да и ваши справедливые замечания, по поводу документации и обработки ошибок, подливают масла в огонь. Слишком много у неё недостатков. Дальше только будет больше проблем с ней.
Подумав над этим, я решил написать что-то подобное на ReactPHP, без участия Redis:

subscribe.php
<?php

// Каналы опустим для простоты (если они вообще нужны)

$database = new Database(...);

$message = new Message();

// Проверяем изменения
$message = $database->check(...);

// Если сообщение пустое ждем событий
if (empty(array_filter(get_object_vars($object)))) {

$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server('127.0.0.1:8080', $loop);

$socket->on('connection', function (ConnectionInterface $c) {

$c->on('data', function ($data) use ($c) {
// пришло уведомление, проверяем сообщение в БД и возвращаем его
$message = $database->check(...);
$c->close();
});

});

$loop->addTimer($interval = 30, function () use ($socket) {
$socket->close(); // unsubscribe
});

$loop->run()

}

return $message; // дойдет ли выполнение досюда до окончания таймера?

?>


publish.php
<?php

$database = new Database(...);

$message = new Message($_POST['message']);

$message = $database->add($message);

$loop = React\EventLoop\Factory::create();
$connector = new Connector($loop);

// обработка Reject опущена для простоты
$connector->connect('127.0.0.1:8080')->then(function (ConnectionInterface $c) {

$c->write(...); // передаем либо уведомление, либо само сообщение целиком
$c->end();

});

$loop->run();

// возвращаем сообщение назад в случае успеха
return $message;

?>

Пара очень быстрых вопросов:

Когда создается сервер, ему передается адрес, который он будет слушать, и при соединении конектора, передается адрес, по которому оно создается. Т.е. для нескольких серверов, сначала нужно иметь роутер(?), который будет распределять соединения, как это делается при шардинге БД?
И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?

>- самописный брокер на ReactPHP, в котором есть поддержка WAMP (Websocket App. Messaging Protocol), в котором есть в том числе и подписки, а также поддержка и websocket, и long polling. Это работает внутри одного процесса PHP.
Такой брокер уже написан кем-то другим и указан на странице ReactPHP - https://github.com/voryx/Thruway . Только документация совсем не написана. Может попробовать придумать что-нибудь на нём?
Ответы: >>1199241 >>1199376
Аноним 2018/05/30 10:54:00  №1199376 18
>>1199240

Я погуглил "redis reactphp" и вижу, что есть асинхронные клиенты для редиса. Ты их не смотрел?

Ты, как я вижу, попытался написать свою реализауию pubsub, но она явно не работающая. Ты в subscribe.php открываешь порт на прослушивание на 30 секунд. Но порт в один момент времени может слушать только один процесс (есть возможность открыть порт нескольким процессам, если указать опцию расшаривания порта в linux, но тогда соединение получит один случайно выбранный из этих процессов, а не все).

То есть у тебя не может быть больше одного подписчика. Это не pubsub.

Как я понимаю, реализовать pubsub напрямую на примитивах ОС для IPC (вроде сокетов, которые ты пытался использовать) сложно, если вообще возможно. Проще реализовать его внутри процесса-брокера, и сделать возможность присоединения клиентов-издателей и подписчиков к нему. Я где-то в предыдущем посте предлагал даже пример кода на семафорах: https://phpclub.tech/pr/res/1174695.html#1190031 - сейчас я подумал, что в асинхронном однопоточном коде (Node.JS или ReactPHP) семафоры ведь и не нужны (так как один поток), можно без них это сделать, примерно так (пример сильно упрощен, убрана обработка сетевых ошибок, отсоединений клиентов итд):

// Мы делаем словарь, в котором индексом является номер канала,
// а содержимым - массив сокетов, заинтересованных в сообщениях.
// При поступлении сообщения в канал оно ретранслируется всем
// ждущим его сокетам клиентов.
//
// channelId: [ socket1, socket2, socket3 ]
//
// При отсоединении клиента надо удалять его сокет, это не сделано.

var listeners = {};

// Вызывается из клиента-подписчика, добавляет коллбек в массив коллбеков
// при появлении сообщения в канале оно посылается по TCP соединению
// подписчику
function subscribeForMessages(clientSocket, channelId) {
listeners[channelId].push(clientSocket);
}

// Вызывается из клиента-издателя, отправляет сообщение всем
// подписчикам канала
function publishMessage(channelId, message) {
var clients = sockets[channelId] || [];

// делаем копию для защиты от добавления/удаления
// сокетов в массив в процессе расылки
clients = clients.slice();
clients.forEach(function (socket) {
// В теории, если клиент не принимает сообщения, то они
// буферизуются и буфер будет расти неограниченно. Защита
// от этого не предусмотрена.
socket.write(message);
});
}

// Поднимаем TCP-сервер. Когда приходит соединение, мы читаем из
// него команду в текстовом виде. Подписчик посылает команду вида
// subscribe 1234, а издатель - publish 1234 message
//
// Если подписчик подписался на канал, ему в сокет приходят сообщения
// от издателей как строки текста. Отписка не предусмотрена.
//
// Нагло стырено с https://gist.github.com/creationix/707146

var net = require('net');
var serverSocket = net.createServer(function (socket) {

var lineReader = carrier.carry(socket);
lineReader.on('line', function(line) {

// Пришла строка от клиента
var [_, command, channelId, _, message] = /^(\w+)\s+(\d+)(\s+\S.*)?$/.exec(line);
if (command == 'subscribe') {
subscribeForMessages(socket, channelId);
} elseif (command == 'publish') {
publishMessage(channelId, message);
} else {
console.log("Invalid command: " + line);
}
});

socket.on('error', ...);
socket.on('close', ...);
});

serverSocket.on('error', ...);
serverSocket.listen(9001, '0.0.0.0');

Если ты так хочешь написать свой pubSub брокер, можешь переписать этот пример на PHP и допилить до работающего состояния. В качестве клиента используется telnet или netcat.

Тут разбирается реализация брокера в redis: https://making.pusher.com/redis-pubsub-under-the-hood/

> // дойдет ли выполнение досюда до окончания таймера?
Не знаю, вроде как event loop должен завершиться при закрытии всех сокетов и истечению всех таймаутов. Если нет, то можно (надежнее так) в таймауте явно вызвать команду завершения event loop.

> Т.е. для нескольких серверов, сначала нужно иметь роутер(?), который будет распределять соединения, как это делается при шардинге БД?

Да, если серверов несколько, то нужно "балансировать" клиентов между ними. Это можно сделать несколькими способами:

- на клиенте: клиент имеет массив адресов серверов и выбирает один случайно для соединения
- на сервере: делаем прокси, к которому подсоединяется клиент и который далее проксирует его соединение к одному из серверов

> И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?

Так сервер это один процесс, а клиент - другой. Как клиент узнает, к кому подсоединяться? Твой вопрос звучит как "а зачем в браузере вводить адрес сайта в адресной строке, разве не достаточно что где-то есть сервер и он слушает порт".

> Такой брокер уже написан кем-то другим и указан на странице ReactPHP - https://github.com/voryx/Thruway . Только документация совсем не написана. Может попробовать придумать что-нибудь на нём?

Можно.
Ответы: >>1199822
someApprentice 2018/05/31 04:20:04  №1199822 19
>>1199376
>Я погуглил "redis reactphp" и вижу, что есть асинхронные клиенты для редиса. Ты их не смотрел?
Я смотрел некоторые клиенты предлагаемые самим редисом (https://redis.io/clients#php) и предлагаемый ReactPHP (https://github.com/nrk/predis-async). Все они плохо задокументированы. Я разрываюсь между изучением их кода и написанием своего собственного брокера.
Изучу и то и то, так буду точно уверен что из этого выбрать.

Библиотеки на которые я обращу внимание:
https://github.com/clue/php-redis-react
https://github.com/shumkov/rediska //может быть не асинхронная
https://github.com/nrk/predis-async // заброшена с 2016
https://github.com/voryx/Thruway


>Ты, как я вижу, попытался написать свою реализауию pubsub, но она явно не работающая. Ты в subscribe.php открываешь порт на прослушивание на 30 секунд. Но порт в один момент времени может слушать только один процесс (есть возможность открыть порт нескольким процессам, если указать опцию расшаривания порта в linux, но тогда соединение получит один случайно выбранный из этих процессов, а не все).
Да, я предполагал, что поторопившись с ответом я могу допустить ошибки, но очень уж хотелось побыстрей вам ответить.
Больше не буду торопиться и буду больше думать над кодом.

>Если ты так хочешь написать свой pubSub брокер, можешь переписать этот пример на PHP и допилить до работающего состояния. В качестве клиента используется telnet или netcat.
>В качестве клиента используется telnet или netcat.
Это в рамках примера или действительно лучше использовать консольные приложения? В рабочем приложении я бы написал свой клиент.

>> // дойдет ли выполнение досюда до окончания таймера?
>Не знаю, вроде как event loop должен завершиться при закрытии всех сокетов и истечению всех таймаутов. Если нет, то можно (надежнее так) в таймауте явно вызвать команду завершения event loop.
Я имел ввиду, что при задавании таймера сначала выполнится код под ним, а потом, по истечению самого таймера, его код. То есть:

setTimeout($t = 3, function() {echo "TIMEOUT"});

echo "AFTER TIMEOUT";

Выдаст результат сначала "AFTER TIMOUT", а потом уже "TIMEOUT".

Т.е. если мы вызовем return после таймаута, то он вернет что-то до его истечения?

>> И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?
>Так сервер это один процесс, а клиент - другой. Как клиент узнает, к кому подсоединяться? Твой вопрос звучит как "а зачем в браузере вводить адрес сайта в адресной строке, разве не достаточно что где-то есть сервер и он слушает порт".
Я плохо знаю низкоуровневые устройства сети, но разве сервер слушает все адреса в интернете?

https://reactphp.org/socket/#server
>As above, the $uri parameter can consist of only a port, in which case the server will default to listening on the localhost address 127.0.0.1, which means it will not be reachable from outside of this system.
>which means it will not be reachable from outside of this system.
То есть, если я задам, например, $uri = "google.com:8080", то сервер будет до бесконечности слушать этот адрес, пока google сам не создаст соединение по адресу сервера (что никогда не произойдет). В чем смысл слушать адрес, а не только свой порт, если от клиента зависит создастся ли соединение или нет?

Или это нужно чтобы соединение устанавливалось только когда указанные адреса в сервере и клиенте совпадают? Если подумать, то это имеет смысл, чтобы злоумышленник не создал свое соединение, и не стал присылать свои уведомления... Всё равно ничего не понятно.


Спасибо
Ответы: >>1199824 >>1199830
someApprentice 2018/05/31 04:22:46  №1199824 20
Опять не заметил что предыдущий тред уже закрыт и ответил в него >>1199822
Ответы: >>1199830
Аноним 2018/05/31 04:45:49  №1199830 21
>>1199822
>>1199824

> Все они плохо задокументированы. Я разрываюсь между изучением их кода и написанием своего собственного брокера.

Изучить код наверно будет быстрее. Там ведь не только надо брокер написать, а еще протокол связи с ним придумать или реализовать готовый. Готовое будет взять гораздо быстрее, а так твоя задача может затянуться до бесконечности.

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

> Больше не буду торопиться и буду больше думать над кодом.

Если ты не знаешь что писать, лучше может быть спросить, чтобы зря время не терять.

> Это в рамках примера или действительно лучше использовать консольные приложения? В рабочем приложении я бы написал свой клиент.

Это в рамках примера.

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

Да.

> Т.е. если мы вызовем return после таймаута, то он вернет что-то до его истечения?

Я не очень понял вопрос, но попробую кратко объяснить принцип работы event loop. Тебе стоит глянуть код EventLoop, чтобы понять, как он вообще работает: https://github.com/reactphp/event-loop/blob/master/src/StreamSelectLoop.php

Этот класс предназначен для отслеживания асинхронных операций. Ты можешь, например, добавить в него таймер, который будет вызван через заданное время, или, например, поток ввода-вывода (открытый файл, сетевой сокет), на котором ты ждешь появления данных (или сигнала об ошибке, или о закрытии потока, или сигнала о готовности принять данные для записи в поток). То есть ты указываешь событие, в котором заинтересован, и коллбек, который надо вызвать при его наступлении.

Если посмотреть на реализацию, то addTimer() просто добавляет объект-таймер в коллекцию таймеров. $loop->run() в цикле проверяет, есть ли какие-то события, которые мы ждем в будущем (срабатывание таймера, события на потоках), и пока они есть, ждет их возникновения. Если есть потоки ввода/вывода - используется функция stream_select (она блокируется до возникновения события на потоке или истечения таймаута), если мы наблюдаем за потоками, но есть таймеры - просто вызывается usleep().

Соответственно, addTimer() лишь добавляет таймер в очередь, а вызван он будет из $loop->run(), когда придет его время.

Функция $loop->run() завершается и возвращает управление только в 2 случаях: если не осталось событий, которые мы ждем, либо если была вызвана функция явного останова цикла $loop->stop().

> Я плохо знаю низкоуровневые устройства сети, но разве сервер слушает все адреса в интернете?

А, я не так понял вопрос. Я думал, ты спрашивал, зачем указывать адрес в клиенте при установлении соединения с сервером. А вопрос был, зачем указывать адрес для серверного сокета.

Этот адрес указывает, на каком сетевом интерфейсе (сетевой карте или виртуальном интерфейсе) надо открыть порт. Указывается IP адрес желаемого интерфейса. Кроме того, можно указать значение 0.0.0.0 - что значит, открыть порт на всех сетевых интерфейсах. Например, если ты укажешь 127.0.0.1 - то порт будет открыт на внутреннем loopback интерфейсе и к нему нельзя будет подсоединиться снаружи.

Список сетевых интерфейсов в Windows выводится командой ipconfig, в линуксе ifconfig (или /sbin/ifconfig), или ip link show ( статья: https://www.cyberciti.biz/faq/linux-list-network-interfaces-names-command/ ).

В современном linux, кстати, на 1 сетевой интерфейс можно назначить несколько IP. Это будет выглядеть, как будто у тебя есть несколько сетевых карт, подсоединенных к одной сети.

Если ты хочешь лучше разбираться в сетях, можно погуглить и почитать про протоколы IP, TCP, и сокеты Беркли.

> То есть, если я задам, например, $uri = "google.com:8080", то сервер будет до бесконечности слушать этот адрес, пока google сам не создаст соединение по адресу сервера

Будет ошибка, из-за того что в системе нет сетевого интерфейса с таким адресом.