Pull to refresh

Многопоточный сервер на Perl? Легко!

Perl *
Иногда возникает необходимость написать какой-то простенький TCP либо UDP сервер. Например, сейчас у меня вполне успешно в production трудится собственная реализация DHCP (существующие не подходят из-за специфики). Обычно это делается просто — один цикл, слушаем сокет — и вуаля! Но не всегда «тупой» подход оправдан (нужно что-то более сложное — работа в несколько потоков, например), а использование тяжелой артиллерии слишком затратно.

Интересно? Добро пожаловать под кат.

Подготовка, или что нам понадобится


В первую очередь нужен, конечно же, сам Perl. Подойдет любая более-менее современная версия. Сразу оговорюсь — как это делать под Windows, я не знаю, код я не проверял, но он должен работать.
Как оказалось, для подобных задач в CPAN уже есть готовый модуль Net::Server. Скачиваем и устанавливаем его средствами своего пакетного менеджера (libnet-server-perl в Debian/Ubuntu) либо же прямо cpan (на любителя):
cpan Net::Server

Теория


Net::Server предоставляет несколько моделей работы с мультипоточностью: Single (один поток), Fork (новый дочерний процесс для каждого подключения), PreFork (похоже, но умнее), INET (для использования с inetd), MultiType (выбор одной из моделей — из конфигурации или автоматически) и так далее. Лично мне импонирует PreFork (довольно простой и устойчивый, но эффективный), его я и буду использовать. В реальности лучше использовать MultiType — он умеет fallback на другую модель в случае, если нужная нам недоступна.
Всё, что предоставляет этот пакет (модуль, класс, как хотите) — это база. Его нужно унаследовать, переопределить несколько функций (ну ладно, ладно, хотя бы одну!), дать ему конфиг, после чего запустить. Фактически, все, что нужно, нам предоставляет базовый класс. Нужно дописать всего чуть-чуть. Базовый вариант, описанный в synopsis (который, впрочем, не лишён некоторой Perl-магии — например, наследование без объявления класса-наследника), выглядит так:
#!/usr/bin/perl

use Net::Server::PreFork;
@ISA = qw(Net::Server::PreFork);

sub process_request {
   #...code...
}

__PACKAGE__->run();

Работает. Просто. Сильно. Эффективно. И нифига не понятно (что, в общем-то, тоже плюс — можно кому-нибудь показывать). На практике мы приведем это все в более человеческий вид.

Практика


Для программы лучше выделить где-нибудь папку. У нас будет 3 файла (пакет, конфигурация и сам исполняемый файл). Без лишних слов, код:
lib/Net/Server/Hello.pm
#!/usr/bin/perl

package Net::Server::Hello; # Объявляем свой пакет

use strict; # Так в книжке написано ;)
use warnings;
use base qw(Net::Server::PreFork); # Наследуем

sub process_request {   # Собственно, здесь и выполняется вся работа с запросом
   my $self = shift;    # Получаем ссылку на себя. Это Perl ООП-магия
   while (<STDIN>) {    # Net::Server дает нам сокет как STDIN + STDOUT!
      print "Hello!\n"; # Отвечаем. Просто отвечаем на любой запрос клиента.
   }
}

1; # Perl-магия: пакет должен иметь return

etc/myserver.cfg
# Формат - ключ значение
port 9999
proto tcp


bin/myserver.pl
#!/usr/bin/perl

use strict;
use warnings;
use FindBin;

use lib "$FindBin::Bin/../lib"; # Хороший тон: держим исполняемый файл в bin,
                                # а библиотеки (пакеты) - в lib, и т.д.
use Net::Server::Hello;

# Создаем экземпляр своего сервера; параметры, кстати, можно задавать в командной строке
our $server = Net::Server::Hello->new(conf_file => "$FindBin::Bin/../etc/myserver.cfg"); 

$server->run(); # Поехали!

Если теперь это все запустить, подключиться к нему telnet-ом и что-нибудь спросить, получится примерно следующее:
$ telnet 127.0.0.1 9999
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
Привет!
Hello!
Hi!
Hello!
Терморектальный криптоанализ
Hello!
^]quit

telnet> quit
Connection closed.


Что дальше?


Использование Net::Server далеко не ограничено написанием простеньких сетевых «Hello world» или написанием статей на хабре. Например, у меня уже есть рабочий прототип RADIUS-сервера, который я собираюсь использовать для собственного VPN-сервиса (Kebrum, привет!). Также, на CPAN есть тонны кода, который так или иначе использует Net::Server — например, HTTP-сервера. Читайте документацию — она рулит. И да прибудет с вами сила.

Картинка из xkcd.
Если что, это мой первый хабратопик, посему просьба тухлые яйца бросать сразу мне в хабрапочту.
Tags:
Hubs:
Total votes 69: ↑56 and ↓13 +43
Views 14K
Comments Comments 62