Пишем cron микросервис с AWS Lambda и Serverless

Недавно мы столкнулись с ситуацией, когда нам понадобилось создать задание cron присоединять всех из нашей базы данных пользователей находящихся в конце своего триального периода, в базу данных customer.io. Задание cron написать легко, сложно его по уму настроить.
Вариантов несколько:

  • можно редактировать /etc/crontab на сервере;
  • если используете heroku, то можно сделать это с помощью их планировщика(Scheduler);
  • или вы можете использовать реализации cron на предпочтительном вам языке программирования.

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

Использовать AWS Lambda/Serverless

AWS Lambda это облачная вычислительная платформа, которая позволяет загрузить какой либо код, и исполнять его в облачном окружении. Прелесть сего метода в том, что платите вы только за время когда ваш код работает(с шагом 100 мс).
В сочетании с АМС ScheduledEvents это стало идеальным решением для нас.

Serverless это фреймворк и утилита командной строки которая упрощает создание функций AWS Lambda через API более удобное, чем официальный модуль узла aws-sdk. Он имеет хорошую документацию для установки, и подробную инструкцию, как дать ему доступ к учетной записи AWS: Инструкция.

Микросервис создается следующими командами (необходимо также установить AWS):
npm i serverless -g
serverless create --template aws-nodejs --path users-ending-trial
cd users-ending-trial

Затем берём файл serverless.yml и добавим опционально время в которое вы намерены выполнять его:
service: users-ending-trial
provider:
name: aws
runtime: nodejs4.3
functions:
fetchTrialUsers:
handler: handler.fetchTrialUsers
events:
# 10am every morning
- schedule: cron(0 10 * * ? *)

Пропишем в handler.js следующее:
'use strict';

const mongo = require('mongodb').MongoClient;

const CUSTOMER_IO_KEY = process.env.CUSTOMER_IO_KEY;
const CUSTOMER_IO_SECRET = process.env.CUSTOMER_IO_SECRET;
const MONGO_READ_URL = process.env.MONGO_READ_URL;

const CustomerIo = require('customerio-node');

const cio = new CustomerIo(CUSTOMER_IO_KEY, CUSTOMER_IO_SECRET);

function getRelativeDate(offset) {
return new Date(new Date().setDate(new Date().getDate() + offset));
}

module.exports.fetchTrialUsers = (event, context, callback) => {
mongo.connect(MONGO_READ_URL, (err, db) => {
db.collection('projects').find({ 'trial.trialEndsAt': { $gt: getRelativeDate(2), $lt: getRelativeDate(4) }, plan: 'free' }).toArray((err, projects) => {
if (err) {
console.error('Error reading projects from database');
return callback(err);
}

Promise.all(projects.map((project) => {
return cio.track(project.owner, { name: 'threeDaysLeft', data: { project } });
})).then(() => {
callback(null, projects.length);
}, (e) => {
console.error('Error tracking customers in customer.io', e);
return callback(e);
});

db.close();
});
});
};

Чтобы тестировать работу локально выполняем:
serverless invoke local -f fetchTrialUsers
Чтобы развернуть в продакшн:
serverless deploy -f fetchTrialUsers
Для запуска используйте ту же команду что и для локального теста, только без local:
serverless invoke -f fetchTrialUsers
И вуаля! Теперь это должно запускать ваш исполняемый код на AWS Lambda каждый день в назначенное время. Этот простой, но очень мощный способ работы, который позволяет сосредоточиться на коде, а не на инфраструктуре, которая требуется для его запуска.

Источник на английском: https://blog.readme.io/

Leave a Comment