Простой вебсервис, на Perl, но не CGI

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

Почему именно на Perl — потому что на примере минимального дистрибутива Debian, где Perl уже установлен по умолчанию, и для создания вебсервиса потребуются минимальные усилия.
Ну и заодно — как делаются библиотеки функций и автотесты.

Итак, сам Perl установлен в дистрибутиве.
Устанавливаем дополнительно фреймворк Mojolicious:

apt install libmojolicious-perl

Он займет дополнительно около 70 мегабайт места, но значительно сэкономит время «разработки» данного вебсервиса.

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

Но это пока еще не веб-приложение, это просто пара функций. Чтобы не забивать себе голову, куда и как их добавить — вынесем их в отдельный модуль, который назовем MyStat.pm

#!/usr/bin/perl

# название модуля должно соответствовать имени файла MyStat.pm
package MyStat;

# Получение информации о свободном месте на диске
sub get_disk_space {
    my $df_output = `df -h /`;  # Получаем информацию о корневом разделе
    my @lines = split("\n", $df_output);
    my @fields = split(/\s+/, $lines[1]);
    return $fields[3];  # Возвращаем доступное пространство
}

# Получение информации о доступной памяти
sub get_memory {
    my $free_output = `free -h`;  # Получаем информацию о памяти
    my @lines = split("\n", $free_output);
    my @fields = split(/\s+/, $lines[1]);
    return $fields[6];  # Возвращаем доступную память
}

# модуль всегда завершается возвратом 1
1;

Теперь в любой программе достаточно будет подключить этот модуль и вызвать функции, примерно так:

#!/usr/bin/perl

# эта строчка нужна чтобы указать, где лежит модуль, 
# если он не находится в системных каталогах типа /usr/share/perl...
# в данном случае он лежит в каталоге lib в домашнем каталоге пользователя
use lib $ENV{HOME}.'/lib';

# подключаем его
use MyStat;

# получаем данные
my $df = MyStat::get_disk_space;
my $mem = MyStat::get_memory;
....

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

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

#!/usr/bin/perl

use lib $ENV{HOME}.'/lib';
use MyStat;
use Test::More;

my $df = MyStat::get_disk_space;
ok(defined $df, "disk space is $df");


my $mem = MyStat::get_memory;
ok(defined $mem, "free memory is $mem");

done_testing();

По сути — это обычный скрипт, который просто вызывает функции, а команда ok проверяет, то ли мы получили что хотели или нет.
Если выполнить команду perl MyStat.t — получим примерно следующее:

ok 1 — disk space is 70G
ok 2 — free memory is 3.3Gi
1…2

Первый тест ОК, результат »70G», второй тест ОК, результат 3.3Gi.
Оба теста успешно отработали, вернули значения.
Модуль исправен.

Теперь делаем собственно вебсервис: он будет по запросу выдавать JSON с данными о наличии свободного места.
Для этого выполняем команду:

mojo generate lite-app MyApp

У нас получилась заготовка мини-приложения MyApp.pl

#!/usr/bin/env perl
use Mojolicious::Lite -signatures;

get '/' => sub ($c) {
  $c->render(template => 'index');
};

app->start;
__DATA__

@@ index.html.ep
% layout 'default';
% title 'Welcome';

Welcome to the Mojolicious real-time web framework!

@@ layouts/default.html.ep <%= title %> <%= content %>

В данный момент приложение содержит один единственный метод GET »/», который отрисовывает шаблон index, причем сам шаблон определен в этом же файле, в секции __DATA__ , и состоит из двух частей: шаблона самой страницы, @@ index.html.ep, и шаблона макета default.html.ep.

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

Но сейчас и всего этого не нужно, рисовать отдельные страницы мы не будем, просто выведем JSON.

#!/usr/bin/env perl
use Mojolicious::Lite -signatures;
use Mojolicious::Static;

# ==============================================
use lib $ENV{HOME}.'/lib';
use MyStat;

get '/getstat' => sub ($c) {

  my $mem = MyStat::get_memory;
  $mem =~ s/([\d\.]+).*/$1/;
  my $drive = MyStat::get_disk_space;
  $drive =~ s/([\d\.]+).*/$1/;

  my $t = {
    mem => $mem,
    drive => $drive,
  };

  $c->res->headers->header('Access-Control-Allow-Origin' => '*');
  $c->render(json => $t);
};
# ==============================================

get '/' => sub ($c) {
  $c->render(template => 'index');
};

app->start;
__DATA__
........

Добавлен метод GET »/getstat» который выводит строку наподобие {«mem»:2.6, «drive»:102.4}
Для этого он вызывает ранее созданные функции из модуля, получает данные, отрезает от них буквы и формирует JSON.
Его можно вызвать из внешней системы, чтобы получить данные о ресурсах на этом сервере.

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

А теперь добавим на «главную страницу» простейшие графики, чтобы можно было сразу в графическом виде смотреть, что оно там нам выдает.
Для этого создаем обыкновенную html-страницу с javascript, вызывающим созданный ранее метод и подставляющим данные в график. А потом внедряем ее в скрипт:

__DATA__
@@ layouts/default.html.ep



    
    
    Memory and Disk Usage
    
    

<%= content %>


@@ index.html.ep
% layout 'default';
% title 'Welcome';

System Monitoring

Графики загруженности памяти и занятости диска

По сути, в данном случае при запросе к »/» сервер просто выдаст страничку, которая начнет запрашивать »/getstat» и заполнять графики.

А теперь всё это запустим, например так:

morbo -l http://*:8080 MyApp.pl

При подключении браузером к порту 8080 получим примерно такую страницу:

5e8f7fd39c0e079e626bf9bf5329e483.png

В принципе ничего не мешает сделать страницу красивой, настроить Nginx frontend — и получится небольшое веб-приложение, возможно с десятком страниц и методов. Для большего лучше создавать приложение чуть по другому, но это другая история.
Ну, а для нужд внутреннего мониторинга можно оставить и так.

Писать этот текст было дольше…

Habrahabr.ru прочитано 808 раз