Мы переписали один существующий проект, изначально написанную на Go, на язык Rust. Использование новой платформы позволит нам достичь большего ускорения выполнения программного кода, а также использовать возможности, присущие определенным языкам для борьбы с проблемами вроде гонок.
Ваш проект состоит из нескольких частей:
main.goconfig.yml или переменных окруженияmqtt)sync_loop)Далее мы рассмотрим важной из частей кода, которые были переписаны: обработка MQTT-сообщений и работа в синхронном цикле.
Изменим наш основной модуль, добавим необходимые библиотеки для подключения:
use crate::mqtt;
use crate::sync;
#[tokio_main]
async fn main() {
/* Открываем два потока: один отвечает за MQTT, другой за синхронизацию */
let mqtt_handle = tokio::spawn(mqtt::mqtt_loop());
let sync_handle = tokio_aspawn(sync::sync_loop());
// Синхронизируем запуск
drop((mqtt_handle.await, sync_handle.await));
/* Возвращаемся к основной функции если есть какая-либо задача после того, как оба тела потоку отключатся. Например: вывід завершению, логирование ошибок и так далее.
*/
}
В коде выше mqtt_loop() ищет определенное социальное сообщение МКТТ и затем сохраняет это в локальной базе данных.
Перключение к PostgreSQL: В Go использовался sql.Open() с инициализацией параметров подключения, во время как в Rust через Diesel подключаемся вызвав функцию establish_connection(). Это позволяет более гибко обрабатывать ошибки при подключении.
Обработка ошибок: В Go используется систематическое закрытие всех открытых ресурсов с помощью оператора defer перед каждым вызовом, что предотвращает утечка памяти и блокировку файлового дексриптора. Rust реализация использует соответствующие модели типизации и обертки над системными вызовами.
В код для приложения MCKT представлен следующим образом на Rust:
use crate::settings::CONFIG;
use crate::sqlite::{InsertSensorData, establish_local_connection};
use diesel::RunQueryDsl;
use rumqttc::{Client, QoS};
pub async fn mqtt_loop() -> Result<(), ConnectionError> {
/* Эта часть кода идентична. Происходят инициализации клиента, события подписки и слушка соединений */
...
Замечается, что Rust изначально включает в себя синхронизацию данных на основе системных мутексов, обеспечивая безопасность асинхронного кода. Тем не менее из-за простоты нашего использования MQTT (подписка для одной публикиции), то можно сказать, что сложностей с нитями и трулами возникающих в Rust для этого конкретной задачи пока нет особого количества.
Хоть обновлённый подход на языке Rust добавителен в объему работы, мы в принципе не используем столько "плюшек" от асинхронного программирования.
Сравнивая приложение Rust (нампирайте на самом деле пять) с его исходным Go-аналогом, можно наблюдать выдающийся размер файла бинаря изначального кода, 3.2Mb против только 0.8Mb для программы, написанной на Rust.
Rust использует модели типизации для безопасного контроля памяти и ужём размер программки, что в свою очередь значительно влияет по производительности запуска и выполнения бинарника.
Rus тает при разработке с различными потоками изначально обрабатывает синхронность, так выдерживает параллельные задачи, что отражается в большей под нагрузкой и быстроты кода в сравнении со своим Go аналогом.
Однако Rust также требует времени присвоения новой структуры переменным и типам данных, поэтому важно следить за их управление, и это можно считать не совсем эффективно.
На основе данных из базы, данные строятся по определенной схеме в PostgreSQL (schema.rs):
var schema = `CREATE TABLE IF NOT EXISTS sensor_data(
id SERIAL PRIMARY KEY,
gateway_id INT8 UNIQUE NOT NULL,
device MACADDR8,
rssi SMALLINT NOT NULL,
datetime INT8,
Sensor VARCHAR(36)
)`
Все улучшения в использовании данных происходят через приведения на Rust с помощью Diesel ORM:
table! {
sensor_data(id) {
id -> BigInt,
gateway_id -> BigInt,
device -> Text,
rssi -> Integer,
datetime -> Integer,
sensor -> Text,
}
}
Уменьшение размер бинаря: Исходный язык Go в среднем приблизился к около трети размерам программы написаной на Rust, что снижено вдвое только без оптимизации конфигурации билдером.
Безопасность памяти и управление ей по сравненению с Go: Несмотря на то, что оба языка являются безопасными для памяти, использование Rust значительно снижает количество логических ошибок в коде, связанных с неправильной обработкой памяти через структуры и типы данных.
Интеграция библиотеки Diesel: Код организации запросов базы данных Rust и использование ORM делает чтение и поддержку кода заметно легче по сравнению с низкоуровневыми SQL-вызовами Go.
Улучшенное управление языком асинхронного кода из Golang: Вместо того, чтобы переключаться между различными системами обработки (Goroutines и Channels vs. Futures and Aysn/Await), язык Rust автоматически гарантирует безопасную синхронизацию данных, что предотвращает гонки в нитях за данными.
Можете проверить это для кода MQTT поддерживает библиотеку Diesel и управление событиями в асинхронных циклах (сборщик мусора).
Так что для использования Rust как перспективного языка, которые следует заменению Go, с учетом его сложной моделью типизации, безопасных нитей и автоматического управления памятью, а также меньшего объема бинарник в прикладке.
В конце-концов при выполнении наших требований в нашем проекте был внедрён лучшим образом языок программирования Rust.
При разработке коды на нашем сайтах вы сможете оптимизировать и улучшить существование данных. Каждое разворачивание, используя преимущества особенностей этого нового языка, добавит вам индивидуального тонкого контроля над программами.