Cron — это просто демон планирования, который выполняет задачу через заданный интервал времени. Обычно мы используем Cron для резервного копирования данных, создания отчетов, синхронизации настроек в фоновом режиме и т.д.
Что мы узнаем : —
- Настройка Actix-web.
- Добавление длительности для периодических задач.
- Добавление парсера cron для гибкости планирования.
- Выполнение операций с БД с помощью cronjobs.
Необходимые библиотеки :-
- actix-web :- мощный, прагматичный и чрезвычайно быстрый веб-фреймворк для Rust
- reqwest :- клиентская библиотека HTTP более высокого уровня
- actix-rt :- однопоточная асинхронная среда выполнения на базе Tokio для экосистемы Actix
- cron :- парсер выражений cron и проводник расписаний.
- chrono :- Библиотека даты и времени для Rust
1. Настройка Actix-web
Инициализация проекта Rust
Запустите новый проект со следующим cargo new <file-name>
.
Реализация базового actix-сервера
Давайте клонируем пример кода из официальной документации actix.
Добавьте зависимость в cargo.toml
....
[dependencies]
actix-web = "4"
....
Напишем код для сервера actix.
use actix_web::{web, App, HttpServer};
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.route("/hello", web::get().to(|| async { "Hello World!" }))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
2. Добавление длительности для периодических задач.
use std::{collections::HashMap, time::Duration};
use reqwest;
use actix_rt::time;
// async function to get data from url using reqwest library
async fn get_ips() -> HashMap<String, String> {
let resp = reqwest::get("https://httpbin.org/ip")
.await.unwrap()
.json::<HashMap<String, String>>()
.await.unwrap();
resp
}
async fn main() -> std::io::Result<()> {
actix_rt::spawn(async {
let mut interval = time::interval(Duration::from_secs(20));
loop {
interval.tick().await;
let result = get_ips().await;
println!("20 sec {:?}",result);
}
});
....
}
Запутались?
Нет, не нужно путаться. Давайте попробуем разобраться в пошаговом коде.
actix_rt ()
Это однопоточная асинхронная среда выполнения на основе tokio для системы actix.
Однопоточность означает, что за один раз выполняется только одна команда.
actix_rt::spawn : Порождает будущее на текущем потоке как новую задачу. Если задача не ожидается немедленно, она может быть отменена с помощью JoinHandle::abort (при отмене отсоединяет текущий поток).
actix_rt::spawn(async move {}) : move позволит вам захватить переменную окружения закрытия.
Закрытия — это простые анонимные функции
которые могут храниться в переменной и не требуют аннотирования типов.
tokio time::interval : Создает новый интервал, который выдает интервал длительности. Первый тик завершается немедленно.
- interval.tick() : Завершается при достижении следующего мгновения в интервале.
Duration::from_secs(20) : Создает новую Duration с введенным целым числом в секундах.
3. Добавление парсера cron для гибкости планирования.
use std::{collections::HashMap, time::Duration, str::FromStr};
use chrono::{Local, FixedOffset};
use cron::Schedule;
use reqwest;
use actix_rt::{self, time};
use actix_web::{web, App, HttpServer};
async fn get_ips() -> HashMap<String, String> {
let resp = reqwest::get("https://httpbin.org/ip")
.await.unwrap()
.json::<HashMap<String, String>>()
.await.unwrap();
resp
}
#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {
actix_rt::spawn(async move {
let expression = "1/50 * * * * * *";
let schedule = Schedule::from_str(expression).unwrap();
let offset = Some(FixedOffset::east(0)).unwrap();
loop {
let mut upcoming = schedule.upcoming(offset).take(1);
actix_rt::time::sleep(Duration::from_millis(500)).await;
let local = &Local::now();
if let Some(datetime) = upcoming.next() {
if datetime.timestamp() <= local.timestamp() {
let result = get_ips().await;
println!("{:?}",result);
}
}
}
});
HttpServer::new(|| {
App::new()
.route("/hello", web::get().to(|| async { "Hello World!" }))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
Давайте попробуем понять, что нового здесь происходит
выражение cron : следуйте шаблону cron. Если вы хотите узнать об этом больше, то здесь.
// sec min hour day of month month day of week year
let expression = "0 30 9,12,15 1,15 May-Aug Mon,Wed,Fri 2018/2";
Schedule::from_str : IT будет разбирать значение из строки.
FixedOffset : Фиксированное смещение используется для создания экземпляра даты. Положительные секунды означают Северное полушарие, а отрицательные — Западное полушарие.
schedule.upcoming(offset).take(1) : upcoming берет экземпляр смещения и возвращает итератор объекта DateTime, который соответствует расписанию.
- take : возвращает первый элемент в итераторе.
thread::sleep : необходимо заснуть перед проверкой условий, потому что проверка условий в каждую миллисекунду не идеальна, так как в нашем случае используются секунды или больше. Если вам нужен контроль минутных миллисекунд над cron, вы можете попробовать и меньшее время.
Local::now() : Возвращает текущее время в зависимости от местного часового пояса.
if let Some(datetime) = upcoming.next() {if datetime.timestamp() <= local.timestamp() {} } : Если мы найдем объект datetime в итераторе и локальное время даты (текущее) >= времени итератора, то будет запущена функция.
4. Выполнение операций с БД с помощью cronjobs.
Это довольно похоже, как и два предыдущих. Я постараюсь дать вам представление и пример кода, чтобы вы могли понять сами.
Мы будем использовать clousure(move) над функцией для получения пула БД и логгера.
Пожалуйста, найдите код здесь, actix-question-bank-stackoverflow.
Особая благодарность cronjob. Он вдохновил меня на написание простого планировщика cron.
Не стесняйтесь задавать вопросы и делать pull request для изменений и предложений на GitHub.
Github — исходный код
Счастливого хакинга
Rustaceans!