Микросервисы на JavaScript: архитектура и практика от А до Я

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение в микросервисную архитектуру на JavaScript

Микросервисная архитектура — это подход к разработке программного обеспечения, при котором приложение строится как набор небольших, независимо развертываемых сервисов. Каждый сервис реализует одну бизнес-функцию и взаимодействует с другими через легковесные протоколы (обычно HTTP/REST или очереди сообщений). JavaScript, а именно среда выполнения Node.js, идеально подходит для создания микросервисов благодаря асинхронной модели I/O, огромному экосистеме пакетов (npm) и высокой скорости разработки.

В этой статье мы разберем ключевые аспекты создания микросервисов на JavaScript: от проектирования архитектуры до развертывания в контейнерах. Вы узнаете, как организовать обмен данными, обеспечить отказоустойчивость и тестировать распределенную систему.

1. Проектирование архитектуры микросервисов

Первый шаг — декомпозиция монолитного приложения на сервисы. Каждый микросервис должен владеть собственной базой данных (database per service) и быть полностью автономным. Рассмотрим пример интернет-магазина:

  • Service Users — регистрация, аутентификация, профили.
  • Service Products — каталог товаров, цены, остатки.
  • Service Orders — создание и обработка заказов.
  • Service Payments — интеграция с платежными шлюзами.
  • Service Notifications — отправка email/SMS.

Для взаимодействия между сервисами используем два подхода: синхронный (REST/gRPC) для запросов, требующих немедленного ответа, и асинхронный (очереди сообщений) для событий, которые не требуют блокировки.

2. Создание микросервиса на Node.js с Express

Начнем с простого микросервиса для управления пользователями. Установим Express и библиотеку для работы с UUID:

npm init -y
npm install express uuid

Создадим файл users-service.js:

const express = require('express');
const { v4: uuidv4 } = require('uuid');
const app = express();
app.use(express.json());

const users = [];

// Создание пользователя
app.post('/users', (req, res) => {
    const { name, email } = req.body;
    const user = { id: uuidv4(), name, email };
    users.push(user);
    res.status(201).json(user);
});

// Получение списка пользователей
app.get('/users', (req, res) => {
    res.json(users);
});

// Получение пользователя по ID
app.get('/users/:id', (req, res) => {
    const user = users.find(u => u.id === req.params.id);
    if (!user) return res.status(404).json({ error: 'User not found' });
    res.json(user);
});

app.listen(3001, () => console.log('Users service running on port 3001'));

Запустите сервис: node users-service.js. Теперь у вас есть работающий микросервис с REST API. Аналогично создаются сервисы для продуктов и заказов (на портах 3002 и 3003 соответственно).

3. Асинхронное взаимодействие через RabbitMQ

Для событий, таких как "создан заказ" или "пользователь зарегистрирован", используем очереди сообщений. Установим клиент для RabbitMQ:

npm install amqplib

Создадим утилиту для публикации сообщений (event-bus.js):

const amqp = require('amqplib');
let channel;

async function connect() {
    const connection = await amqp.connect('amqp://localhost');
    channel = await connection.createChannel();
    await channel.assertExchange('order_events', 'topic', { durable: true });
}

async function publishEvent(routingKey, data) {
    if (!channel) await connect();
    channel.publish('order_events', routingKey, Buffer.from(JSON.stringify(data)));
    console.log(`Event published: ${routingKey}`);
}

module.exports = { publishEvent };

Теперь в сервисе заказов (orders-service.js) при создании заказа публикуем событие:

const { publishEvent } = require('./event-bus');

app.post('/orders', async (req, res) => {
    const order = { id: uuidv4(), userId: req.body.userId, items: req.body.items };
    orders.push(order);
    // Публикуем событие
    await publishEvent('order.created', order);
    res.status(201).json(order);
});

Сервис уведомлений (Notifications) подписывается на событие order.created и отправляет email пользователю:

const amqp = require('amqplib');

async function consume() {
    const connection = await amqp.connect('amqp://localhost');
    const channel = await connection.createChannel();
    await channel.assertExchange('order_events', 'topic', { durable: true });
    const q = await channel.assertQueue('', { exclusive: true });
    await channel.bindQueue(q.queue, 'order_events', 'order.created');
    channel.consume(q.queue, msg => {
        const order = JSON.parse(msg.content.toString());
        console.log(`Sending notification for order ${order.id} to user ${order.userId}`);
        // Здесь вызов email-сервиса
        channel.ack(msg);
    });
}

consume();

4. Контейнеризация и оркестрация с Docker

Для изоляции и упрощения развертывания каждый микросервис упаковывается в Docker-контейнер. Создадим Dockerfile для сервиса пользователей:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
EXPOSE 3001
CMD ["node", "users-service.js"]

Соберите образ: docker build -t users-service .. Для запуска всех сервисов используем Docker Compose (docker-compose.yml):

version: '3'
services:
  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "5672:5672"
      - "15672:15672"

  users-service:
    build: ./users-service
    ports:
      - "3001:3001"
    depends_on:
      - rabbitmq

  orders-service:
    build: ./orders-service
    ports:
      - "3002:3002"
    depends_on:
      - rabbitmq

  notifications-service:
    build: ./notifications-service
    depends_on:
      - rabbitmq

Запустите всю систему командой docker-compose up. Теперь все сервисы работают в изолированных контейнерах и общаются через RabbitMQ.

5. Тестирование и мониторинг микросервисов

Для тестирования микрос

Блоги

Рекомендаций по книгам