Биллинговая система Nodeny
26 Ноября 2024, 05:59:45 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
Новости: Прекращена поддержка версии Nodeny 49
 
   Начало   Помощь Поиск Войти Регистрация  
Страниц: [1]
  Печать  
Автор Тема: мой Comet-сервер  (Прочитано 9287 раз)
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« : 19 Июня 2011, 16:36:08 »

В прикрепленном архиве файлы разработанного мною comet-сервера. О comet-технологии можете почитать в инете. Коротко это работает так:

Суть в обеспечении обратной связи, когда web-клиент принимает запросы от сервера, а не наоборот как
в стандартном протоколе http. Технически это реализуется так:

Web-клиент посылает ajax http-запрос на порт, который слушает текущий скрипт. Скрипт принимает соединение,
но не закрывает и не отвечает на запрос! Когда появляется необходимое событие - скрипт посылает ответ и
закрывает соединение. Клиент, приняв ответ, снова делает запрос. Если определенное время не происходит
никаких событий - клиент закрывает соединение и пересоздает снова.

Comet-сервера обычно работают поверх стандартных web-серверов, настроенных на большие таймауты. В моем случае (а я писал под реальную задачу) нельзя было использовать стандартный, поэтому я написал свой небольшой веб-серверочек noHttpd.pm. Можете использовать не только для comet-сервера, с ним очень просто работать, я ниже напишу как.

Этот http-сервер не многопоточный, а мультиплексный. Т.е. его можно применять исключительно в случае, когда обработка запросов не требует обращения  к каким-либо внешним ресурсам, которые имеют хотя бы минимальный таймаут. В моем случае обработка примитивная, поэтому я не изъябывался с потоками (их всего 2 - один для вебсервера и один для основного скрипта).

Как вы уже поняли у нас есть 3 стороны сего процесса:
- браузер клиента
- comet-сервер
- web-сервер (который обрабатывает не comet-запросы)

Коротко процесс выглядит так. Клиент запрашивает страничку у web-сервера, например со списком платежей такого-то клиента (это реальная задача, которую я делал, только речь не идет о NoDeny). Web-сервер связывается с comet-сервером и грит ему: вот такой-то клиент создает канал такой-то (тут сгенерированное имя канала)  и хочет получать обновления когда будут такие условия (а в условии сказано "когда такие-то платежи будут меняться"). Web-сервер отдает страничку со списком платежей в браузер клиента. В этой страничке присутствует реально небольшой фрагмент на javascript который использует noComet.js для общаения с comet-сервером noComet.pl по заданному имени канала. Если параллельно какой-то админ изменяет статус платежа, который попадает в условие, то комет-сервер сигнализирует текущему клиенту "произошло событие такое-то, id=xxx". Клиент выполняет ajax-запрос к web-серверу и просто перечитывает тукущий платеж. В моем примере он просто перерисовывал на экране строку с данными платежа (там администраторы параллельно захватывают платежи на обработку и нужно чтобы было видно, что платеж, который ты собираешься захватить уже забрал кто-то)

Теперь технические детали.

Вам понадобятся (в архиве):

Debug.pm - охуенный модуль дебага, я его вылизывал почти год (это не значит, что он сложный, просто я сделал его простым и удобным). Он предназначен для ведения логов и дебагов в консоли/файле/вебе. Особенно удобно ведет он себя в вебе, когда каждый шаг отображается в виде строчки таблицы, в которой есть время выполнения от начала создания объекта дебага, полный путь к текущей операции (разворачивается по плюсику), ну и др. вкусности. Вы можете в него заглянуть там есть примеры, хотя и не все. Например, в случае краша, в одном из проектов, я сделал чтоб вся дебажная инфа (как объект) серилизовалась (методом этого же модуля) и записывалась в бд. Потом админ извлекает конкретный снимок и видит вселенную в первозданном виде полный дебаг краша как будто он произошел при нем. Кстати, не обязательно краша. Например, клиент не может выполнить какую-то операцию, берем ставим его на карандаш - все дебаги пишутся в БД (в серилизованном виде как я уже грил). Мы извлекаем эти дебаги и видим, что этот лось вводит сумму меньше нуля.

Еще нам понадобится StartMod.pm - это маленький модуль сделал совсем недавно для обработки ключей командной строки и сигналов TERM и INT. Просто у меня скопилось много однотипных скриптов, которые постоянно висят демонами, я их заставил принимать ключи этим модулем. Ключей мало:
-v - ведем дебаг
-vv - ведем более информативный дебаг
-f=файл - лог и дебаг (если ведется) идут в файл "файл"

Например:

noComet.pl -vv

P.S. Ессно я напишу продолжение, чуть позже. Но сейчас я хочу сказать, что пока не придумал по какой лицензии распространять мои файлы, я пока временно ставлю запрет на их распространение. Вы можете только тестировать эти файлы для личных целей, не изменяя их в случае если кто-то еще попросит их потестировать. Это временно, пока я не придумал под чем лицензировать)
« Последнее редактирование: 19 Июня 2011, 16:40:53 от Efendy » Записан
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« Ответ #1 : 19 Июня 2011, 16:56:45 »

После запуска noComet.pl, он присядет на порт 8000 и будет слушать как клиентов так и web-сервер. Последний создает каналы, поэтому ему нужен пароль, который задается в самом noComet.pl, без труда найдете $cfg::comet_pass = 'hardpass';

Вот пример кода, для создания канала:
Код:
use IO::Socket;
use JSON;
use Debug;

$cfg::comet_pass = 'hardpass';
$cfg::comet_client_url = 'http://my.site.com:8000';
$cfg::comet_server_url = 'http://127.0.0.1:8000';


my $chanal = 'pays_'.(join '', map{ int(rand()*10**5) } (0..2));
comet_Send_to_server(
                act     => 'create',
                chanal  => $chanal,
                return  => 'id',
                filter  => "event eq pay && id >= 10 && id <= 20"
);
$self->{template}{comet_chanal} = $chanal;
$self->{template}{comet_client_url} = $cfg::comet_client_url;

sub url_encoding
{
    local $_ = shift;
    s/([^a-zA-z0-9])/sprintf('%%%02X',ord($1))/eg;
    return $_;
}

sub comet_Send_to_server
{
    my(%data) = @_;
    $_ = $cfg::comet_server_url;
    # уберем префикс http://, httpd:// ...
    $_ =~ s|^ *\w+://||;
    my($addr,$query) = split /\//, $_, 2;
    my $port = ($addr =~ s|^([^:]+):(\d+).*|$1|)? $2 : 80;
    debug('pre',"Посылаем на $addr:$port данные:",\%data);
    my $sock = new IO::Socket::INET(
        PeerAddr => $addr,
        PeerPort => $port,
        Proto    => 'tcp',
        Timeout  => '1',
    );
    if( !$sock )
    {
        debug('warn','не отправлен:',$!);
        return;
    }
    debug('Отправлен успешно');
    $data{pass} = $cfg::comet_pass;
    $query ||= '/';
    $query .= '?';
    $query .= join '&', map{ url_encoding($_).'='.url_encoding($data{$_})} keys %data;
    my $pkt = "GET $query HTTP/1.1\r\n\r\n";
    print $sock $pkt;
    close $sock;
}

Большой кусочек кода получился... но там нет ничего сложного, основной размер - это подпрограмма comet_Send_to_server, которая эмулирует Http-запрос на comet-сервер. Я решил не выполнять настоящий http-запрос, ибо стандартный модуль ожидал бы ответ от comet-cервера, а это время. Я просто посылаю один tcp-пакет с http-заголовком, кстати урезанным, но комет-серверу этого будет достаточно.

Код:
$self->{template}{comet_chanal} = $chanal;
$self->{template}{comet_client_url} = $cfg::comet_client_url;
этот код вставляет в результирующую html-страничку имя канала, и урл по которому браузер клиента будет соединяться с комет сервером. Естественно я не знаю как у вас будет формироваться хтмл (через шаблоны или динамически), поэтому привел условно...

Вот шаблон html-страницы:

Код:
<script type='text/javascript' src='<tmpl_var img_dir>/noComet.js'></script>
<script type='text/javascript' src='<tmpl_var img_dir>/jquery-1.5.2.min.js'></script>
<script language='JavaScript'>
$(document).ready(function()
    {
        var CometObj = new noComet({
            url     : '<tmpl_var comet_client_url>',
            chanal  : '<tmpl_var comet_chanal>',
            latency : 500,
            callback: pays_changed
        });
        CometObj.start();
    }
 );

 function pays_changed(answer)
 {
    var ids = '';
    var comma = '';
    for( var i in answer )
    {
        var id = answer[i].id;
        var comment = $('#comment_'+id).val() || '';
         ids += comma + id;
         comma = ',';
    }
    ajax_now({'do':'ajax_pay_show','act':'replace','id':ids});
 }
</script>


Хм. Я тут подумал, что проще будет сделал пример в виде работающего минипроекта, так что ждите...
 
« Последнее редактирование: 19 Июня 2011, 17:06:51 от Efendy » Записан
VitalVas
NoDeny
Спец
*

Карма: 60
Offline Offline

Сообщений: 991



Просмотр профиля WWW
« Ответ #2 : 19 Июня 2011, 18:28:14 »

а не проще сделать все на full ajax
был один у меня проект у которого была 1-а html-ка + jquery + бекенд на питоне(web.py) (жаль что он однопоточный)
(( идея с переключения между страницами была нагло сворована у твиттера ))
Записан
0xbad0c0d3
гуру nodeny )
NoDeny
Спец
*

Карма: 116
Offline Offline

Сообщений: 1059



Просмотр профиля
« Ответ #3 : 19 Июня 2011, 19:54:17 »

так, а чего ж не флеш?Непонимающий Все тупо на флеше, чтобы переливалось и блестело. )))
P.S. Лично я против аяксов и напичканности скриптами.
Записан
versus
Администратор
Спец
*****

Карма: 21
Offline Offline

Сообщений: 845


44306843
Просмотр профиля WWW Email
« Ответ #4 : 19 Июня 2011, 22:03:15 »

а не проще сделать все на full ajax
был один у меня проект у которого была 1-а html-ка + jquery + бекенд на питоне(web.py) (жаль что он однопоточный)
(( идея с переключения между страницами была нагло сворована у твиттера ))

Гугл как бы подсказывает разницу между аяксом и кометой
Записан
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« Ответ #5 : 20 Июня 2011, 09:44:09 »

так, а чего ж не флеш?Непонимающий Все тупо на флеше, чтобы переливалось и блестело. )))
P.S. Лично я против аяксов и напичканности скриптами.
тянуть флеш ради одной небольшой задачи? Задача очень простая - сервер должен иметь возможность сообщить клиенту, что произошло какое-то событие. Причем то, что я предлагаю - кода реально очень мало, что javascript, что серверного. Если потратить часок на изучение принципа работы, то можно клепать на comet-что угодно, он чатов до онлайновых игр. А что касается аякса - я тоже раньше избегал, но это хорошая вещь, когда я понял, что операторы делают выборки под 1000 записей, по не проматывают и 2х экранов. Сделал чтоб платежи подгружались когда скролинг подходи к концу- посмотри как в вконтакте реализован бесконечный скролинг. Очень удобно, кстати.

На самом деле jquery клевая вещь, позволяющая программить на js даже не зная его) Обычно когда изучаешь что-то новое, то приходится изучать много новых сущностей, а в случае jquery я удивился, что изучать ничего не надо - вместо изучения тонкостей js достаточно онлайновой документации по jquery. Ессно,  js надо учить, но я запарился подбирать валидные конструкции для реализации простейших задач, а то, что ни задача - какие-то тонкости и лезь изучай инет. Это я к тому, что не надо бояться юзать проверенные решения или бояться напичканности скриптами. Я сам сторонник чтоб логика концентрировалась на сервере, но не все можно реализовать на сервере, я же предложил реально несложный вариант, например:

Код:
 var CometObj = new noComet({
            url     : '<tmpl_var comet_client_url>',
            chanal  : '<tmpl_var comet_chanal>',
            latency : 500,
            callback: pays_changed
});
CometObj.start();

это разве много кода для выполнения такой задачи как "запускать функцию pays_changed когда появится событие в канале <tmpl_var comet_chanal>"? По-моему код очень даже лаконичный.

Мне comet-технология очень понравилась т.к. давно были мысли чтобы сервер посылал инфу о том, что делает какой оператор, какие критические события произошли в данный момент. Представьте, у оператора открыто окно и не обновляя станицу ему приходит инфа: столько-то сообщений от клиентов в очереди. Он смотрит, что их уже много скопилось - бежит отвечать. Ну вы поняли
Записан
stix
NoDeny
Спец
*

Карма: 72
Offline Offline

Сообщений: 1872


Nodeny Support Team

205539
Просмотр профиля
« Ответ #6 : 20 Июня 2011, 10:16:17 »

новая версия nodeny будет с использованием comet?  Смеющийся
Записан
ser970
NoDeny
Спец
*

Карма: 70
Offline Offline

Сообщений: 1323

262462619
Просмотр профиля Email
« Ответ #7 : 27 Января 2013, 01:51:59 »


Хм. Я тут подумал, что проще будет сделал пример в виде работающего минипроекта, так что ждите...
 

а примерчик можно?
Записан
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« Ответ #8 : 27 Января 2013, 02:57:31 »


Хм. Я тут подумал, что проще будет сделал пример в виде работающего минипроекта, так что ждите...
 

а примерчик можно?
Это уже неактуально,  скоро вебсокеты будут  во всех современных браузерах, а комет это все таки костыль как ни крути. В Привате в одном из внутренних проектов работает, но код ессно привести не могу
Записан
Andrey Zentavr
NoDeny
Старожил
*

Карма: 29
Offline Offline

Сообщений: 301



Просмотр профиля
« Ответ #9 : 28 Января 2013, 01:54:56 »

Это уже неактуально,  скоро вебсокеты будут  во всех современных браузерах, а комет это все таки костыль как ни крути. В Привате в одном из внутренних проектов работает, но код ессно привести не могу

Оффтоп: Стас, а ты программишь на Приватбанк?
Записан
Efendy
Администратор
Спец
*****

Карма: 138
Offline Offline

Сообщений: 4790



Просмотр профиля
« Ответ #10 : 28 Января 2013, 15:53:49 »

Оффтоп: Стас, а ты программишь на Приватбанк?
2 года назад оттуда ушел. Приват24 и остальные каки делал не я, так что помидорами не кидайтесь. Я делал админку для Liqpay
Записан
Страниц: [1]
  Печать  
 
Перейти в:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.20 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!