Всем привет! Пишу свою первую статью - не судите строго :-)
Сегодня хочу рассказать вам о своём проекте, а точнее показать его в деле. Надеюсь, вам будет интересно!
Введение
Netter - инструмент для быстрого и лёгкого поднятия серверов. Он поддерживает следующий функционал:
Запуск HTTP / HTTPS серверов;
Тонкая настройка логики обработки на каждый маршрут с помощью язык описания маршрутов (RDL). Используется собственный интерпретатор;
Поддержка кастомных плагинов для их последующего использования в RDL. Плагины пишутся на Rust с использованием специального крейта для облегчения разработки
Проект создан для разработчиков, которым надоело писать большой код для запуска простых/средних по сложности серверов
Route Definition Language
Route Definition Language (RDL) - интерпретируемый язык для описания маршрутов и серверной логики.
Синтаксис
config {
type = "http"; // тип сервера
host = "127.0.0.1"; // ip адрес, по которому будет доступен сервер
port = 8080; // порт сервера
}; // после блоков ';' обязательна
import "absolute/path/to/file.dll" as plugin_alias;
// absolute/path/to/file.dll - абсолютный путь до динамической библиотеки .dll или .so
// plugin_alias - алиас для плагина. Будет использован для доступа к функциям из плагина
global_error_handler(e) { // глоабльный обработчик ошибок.
// здесь будут все ошибки, которые перехватил '?',
// но локальный обработчик не был указан
Response.body("Global error: "+ e);
Response.status(500);
Response.send();
};
route "/" GET { // ждём запросы на "/" по запросу GET
val body = Request.body()?; // получаем тело запроса
// '?' перехватывает ошибки
if (body == "empty") {
Response.body("Body is empty!"); // ставим тело ответа
Response.status(400); // ставим статус ответа
Response.send(); // отправляем ответ
};
Response.body(body + "!empty"); // конкатенация строк
Response.send(); // 200 OK
} onError(e) { // локальный обработчик ошибок. Если Request.body()? завершится ошибкой,
// кодд перейдёт сюда
// локальные обработчики имеют приоритет над глобальными
Response.status(500);
Response.body(e); // ставим тело ответа с текстом ошибки
Response.send();
};
Ключевые слова
Язык имеет следующие ключевые слова:
route
- объявление маршрута;GET
,POST
,PUT
и т.д. - тип запроса;global_error_handler
- объявление глобального обработчика ошибок;onError
- объявление локального обработчика
Более подробно с документацией языка (объекты, функции, ключевые слова, обработчики ошибок...) можно в репозитории.
Обработка ошибок
Обработка ошибок - ключевой элемент написания кода. Язык предоставляет возможность обработать ошибки:
Знак
?
перехватывает все ошибки, которые может вернуть функция;!!
игнорирует ошибки, но код завершится паникой, если ошибка всё же будет.
Существует два обработчика ошибок: локальный и глобальный. Локальный всегда имеет приоритет над глобальным, т.е. все ошибки, перехваченные ?
, перейдут в локальный обработчик, если он есть, иначе в глобальный.
Плагины
Netter предоставляет крейт netter_plugger
, который поможет вам в создании более сложных функций, которые RDL сделать не позволит.
Пример простой функции:
use netter_plugger::{netter_plugin, generate_dispatch_func};
generate_dispatch_func!();
#[netter_plugin]
fn something(
path: String,
) -> Result<String, String> {
let result = "heey".to_string();
if path == "error" {
Err("err from plugin".to_string())
} else {
Ok(format!("{result}: {path}"))
}
}
Макрос
generate_dispatch_func!();
обязательно должен находиться наверху файла. Он инициализирует входную точку в плагинАтрибут
#[netter_plugin]
помечает функции вызываемыми из RDL
Как это работает?
Весь ваш код из функции переводится в C-совместимый код, так что готовьтесь к unsafe extern "C"
, хотя в базовых случаях проблем быть не должно.
После импорта вашей динамической библиотеки (которую вы ранее собрали из плагина на Rust), интерпретатор ищет и открывает её, выполняет код из либы и возвращает результат функции обратно в RDL.
Служба (демон)
Служба (или демон) помогает запускать сервера в фоне и улучшить их устойчивость.
Всё общение между клиентом (CLI) и сервисом происходит по протоколу IPC. Каждый запуск демона / службы логируется в новый файл (т.е. создаётся новый файл логов, в который записываются все логи).
Развитие проекта
Главное направление, которое я стараюсь сохранять - легкость в освоении и довольно высокая скорость разработки. Со временем проект будет постоянно развиваться в соответствии с моим виденьем и вашими запросами
Планы по развитию:
Графический UI на десктоп и мобильные устройства для визуализации разработки, упрощения использования инструмента и ускорения описания серверной логики;
Поддержка режима обратного прокси;
Создание балансировки нагрузок;
Реализация других протоколов;
Пакетный менеджер для плагинов (динамических библиотек).
Заключение
Это мой первый крупный проект. Не судите строго :-)
Спасибо всем, кто прочитал "это", делится своим мнением о проекте, его проблемах, вносит вклад в его развитие!
Комментарии (10)
Dimonogen
11.05.2025 17:39Не очень понял. Имеется в виду web сервер, а называется просто сервер? Для меня сервер - это куча железа, на нём поднимается гипервизор и там виртуалки, на которых только поднимаются web сервера и web сервисы. Но у автора что-то с терминологией. Плюс вообще не понял, чем этот проект на rust лучше asp net core на c#, который я использую сейчас. Так что очень интересно, но не понятно.
bjfssd757 Автор
11.05.2025 17:39Да, я имел ввиду именно веб сервер, извиняюсь)
Плюс вообще не понял, чем этот проект на rust лучше asp net core на c#, который я использую сейчас
Вообще цель проекта - ускорение разработки. Я очень стараюсь сделать написание кода минимальным. Да, своей ценой - изучение нового "языка" (условный, конечно, язык, но не суть), но он должен уменьшить количество этих самых строк кода и не требовать глубокого понимая принципов работы веб серверов, увеличивая абстракцию и скрывая от пользователя детали работы. Т.е. пользователю нужно только объявить маршруты и что веб серверу надо делать при запросе на этот маршрут.
Раст здесь (для пользователя. Понятное дело, что весь проект на нём) нужен только для тех, кому требуются более экзотические задачи или просто те (задачи), которые не даёт решить сам RDL. Сейчас я делаю на этом же проекте веб сервер, который будет выступать в роли "ракетного менеджера" для RDL и позволять публиковать/скачивать плагины. Если всё будет реализовано по плану, это позволит большинству пользователей не писать платины самостоятельно, т.к., вероятно, под их задачу уже будет всё написано ранее (и опубликовано)
Dimonogen
11.05.2025 17:39Так... Тогда другой вопрос, а ускорение разработки чего вы хотите добиться? У нас есть разработка разных бэкенд приложений, которые представляют собой веб-серверы. Я вот к примеру работаю в области бизнес систем, там очень много бизнес логики, что объект а нужно обработать таким образом и бла-бла-бла. И вот обвязка веб сервера там не такая и большая, по сравнению с бизнес логикой. Того, что вы упростите написание веб части, почти ничего не поменяет в плане бизнес задачи.
Или речь про то, что вы хотите разработать некоторый аналог nginx, который маршрутизирует некоторый контент и маршрутизирует запросы между разными серверами? Для задачи того, что на сервер А приходят запросы от 3-х разных сайтов и их нужно перенаправить на нужные сервера в режиме прокси - с этим прекрасно справляется nginx, и там просто нужно чуть-чуть написать конфига. Для этого я не буду писать каждый раз новый веб-сервер на c#.bjfssd757 Автор
11.05.2025 17:39Моя задача в первую очередь была набраться опыта в создании нечто большего, чем обычный пет проект (видимо так себе получилось):)
Проект не рассчитан на бизнес и решение его проблем) конечно, для задач бизнес можно и даже нужно писать веб сервера самостоятельно. Я же создаю этот проект для менее... обеспеченной(?) навыками, знаниями или людьми стартапов или просто для обычных людей, которые не хотят тратить время на изучение, но веб сервер им почему-то нужен. В целом и проекты другие, которым нужен не слишком сложный бэкенд, могут воспользоваться этим.
NGINX безусловно хорошее решение, но это другая ниша. Я с ним и не планировал конкурировать. Писать саму серверную логику в nginx не очень то и удобно (хотя обратный прокси на нём замечательно работает), а у моего предложения как раз на неё идёт упор. Для сложный сценариев я добавил возможность интегрировать плагины, но даже с ними бизнесу (крупному.... хотя кто его знает...) вряд ли потребуется это решение
Siemargl
11.05.2025 17:39Статья написана плохо, не структурированно, сумбурно. Не описаны нормально ни предмет разработки, ни цель, ни процесс. В итоге, вообще непонятно о чем речь и зачем она (статья) нужна.
bjfssd757 Автор
11.05.2025 17:39Да, я это заметил. В голове есть структура более правильная. Сейчас планирую написать её заново.
Статья реально плоха, к сожалению(
ky0
Не надо тащить HTTPS внутрь приложений, ну пожалуйста. Каждый раз это дополнительный головняк для эксплуатации, а пользы никакой. То оно ломается при обновлении крпитографических либ в контейнере, то оно сертификаты не умеет менять без перезапуска... зачем?
bjfssd757 Автор
Добавлял как опция) https не обязателен и по умолчанию всё работает на стандартном http.
loncothad
учитывая, что это Rust, "при обновлении криптографических либ" ничего ломаться не должно, а вот по поводу "сертификатов без перезапуска" это уже надо посмотреть доки rustls...