Mikroservislar arxitekturasi


ULASHISH

Mikroservislarga kirish

Mikroservislar - bu arxitektura uslubi bo‘lib, dasturni kichik, erkin bog‘langan xizmatlar to‘plami sifatida tuzadi. Har bir xizmat:

  • Yagona biznes qobiliyatiga qaratilgan
  • Mustaqil ravishda joylashtirilishi mumkin
  • Mustaqil ravishda kengaytirilishi mumkin
  • Potentsial turli dasturlash tillarida yozilgan
  • Turli xil ma’lumotlarni saqlash texnologiyalaridan potentsial foydalanish

Mikroservislar arxitekturasi an’anaviy monolitik ilovalarga nisbatan tezroq rivojlanish davrlarini, yaxshi miqyoslilikni va yaxshilangan moslashuvchanlikni ta’minlaydi.


Monolitlar va mikroservislar

Aspekt Monolitik arxitektura Mikroservislar arxitekturasi
Tuzilishi Yagona, birlashtirilgan kod bazasi Bir nechta kichik xizmatlar
Joylashtirish To‘liq dastur bir vaqtning o‘zida joylashtiriladi Xizmatlar mustaqil ravishda amalga oshiriladi
Masshtablash Butun dastur birgalikda miqyoslanishi kerak Individual xizmatlarni mustaqil ravishda kengaytirish mumkin
Rivojlanish Yagona texnologiya to‘plami Har bir xizmat uchun potentsial turli texnologiyalar
Jamoa tuzilishi Ko‘pincha bitta jamoa Bir nechta jamoalar, ularning har biri o‘ziga xos xizmatlarga ega
Murakkablik Oddiyroq arxitektura, murakkab kod bazasi Murakkab arxitektura, oddiy individual kod bazalari


Asosiy tamoyillar

  • Yagona mas’uliyat - Har bir mikroservis bitta narsani yaxshi bajarishga - bitta biznes qobiliyatini amalga oshirishga e’tibor qaratishi kerak.
  • Markazsizlashtirish - Hamma narsani markazsizlashtiring: boshqaruv, ma’lumotlarni boshqarish va arxitektura qarorlari.
  • Avtonom xizmatlar - Xizmatlar boshqalarga ta’sir qilmasdan mustaqil ravishda o‘zgarishi va tarqatilishi mumkin bo‘lishi kerak.
  • Domenga asoslangan dizayn - Texnik funksiyalardan ko‘ra biznes domenlari atrofida dizayn xizmatlari.
  • Mustaqillik - Xizmatlar boshqa xizmatlarning ishlamay qolishi uchun mo‘ljallangan bo‘lishi kerak.
  • Kuzatilishi - Xizmatlar bo‘ylab keng qamrovli monitoring, ro‘yxatga olish va kuzatishni amalga oshirish.

Eng yaxshi amaliyot: Ilovani mikroservislarga bo‘lishdan oldin aniq domen modelidan boshlang va cheklangan kontekstlarni aniqlang.


Mikroservislar uchun Node.js

Node.js bir necha sabablarga ko‘ra mikroservislar arxitekturasi uchun juda mos keladi:

  • Yengil va tez - Node.js kichik hajmga ega va tez ishga tushadi, bu esa tez o‘lchamga muhtoj bo‘lgan mikroservislar uchun ideal qiladi.
  • Asinxron va hodisalarga asoslangan - Node.js-ning bloklanmaydigan kiritish-chiqarish modeli xizmatlar o‘rtasidagi ko‘plab bir vaqtda ulanishlarni boshqarish uchun samarali qiladi.
  • JSON Support - Birinchi darajali JSON yordami mikroservislar o‘rtasida ma’lumotlar almashinuvini osonlashtiradi.
  • NPM ekotizim - Keng paketli ekotizim xizmatlarni aniqlash, API shlyuzlari, monitoring va boshqalar uchun kutubxonalarni taqdim etadi.

Misol: Oddiy Node.js Microservice

// user-service.js
const express = require('express');
const app = express();

app.use(express.json());

// In-memory user database for demonstration
const users = [
  { id: 1, name: 'John Doe', email: 'john@example.com' },
  { id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];

// Get all users
app.get('/users', (req, res) => {
  res.json(users);
});

// Get user by ID
app.get('/users/:id', (req, res) => {
  const user = users.find(u => u.id === parseInt(req.params.id));
  if (!user) return res.status(404).json({ message: 'User not found' });
  res.json(user);
});

// Create a new user
app.post('/users', (req, res) => {
  const newUser = {
    id: users.length + 1,
    name: req.body.name,
    email: req.body.email
  };
  users.push(newUser);
  res.status(201).json(newUser);
});

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
  console.log(`User service running on port ${PORT}`);
});

Xizmat aloqasi

Mikroservislar bir-biri bilan aloqa qilish usullariga muhtoj.

Ikkita asosiy yondashuv mavjud:

Sinxron aloqa

Xizmatlar real vaqtda so‘rov-javob oqimini yaratib, bir-birining API-larini bevosita chaqiradi:

  • REST: Oddiy, keng qo‘llaniladigan, stateless (ichki state saqlamaydigan) (stateless) aloqa
  • GraphQL: Bitta so‘nggi nuqtaga ega moslashuvchan so‘rovlar
  • gRPC: Protokol buferlaridan foydalangan holda yuqori samarali RPC frameworki

Misol: Xizmatlar orasidagi REST aloqasi

// order-service.js calling the user-service
const axios = require('axios');

async function getUserDetails(userId) {
  try {
    const response = await axios.get(`http://user-service:3001/users/${userId}`);
    return response.data;
  } catch (error) {
    console.error(`Error fetching user ${userId}:`, error.message);
    throw new Error('User service unavailable');
  }
}

// Route handler in order service
app.post('/orders', async (req, res) => {
  const { userId, products } = req.body;
  
  try {
    // Get user data from user service
    const user = await getUserDetails(userId);
    
    // Check product availability from product service
    const productStatus = await checkProductAvailability(products);
    
    if (!productStatus.allAvailable) {
      return res.status(400).json({ error: 'Some products are unavailable' });
    }
    
    // Create the order
    const order = await createOrder(userId, products, user.shippingAddress);
    
    res.status(201).json(order);
  } catch (error) {
    console.error('Order creation failed:', error);
    res.status(500).json({ error: 'Failed to create order' });
  }
});

Eslatma: Sinxron aloqa xizmatlar o‘rtasida bevosita bog‘liqlikni yaratadi.

Agar chaqirilgan xizmat ishlamay qolsa yoki sekin ishlasa, bu uni chaqirgan xizmatga ham ta’sir qiladi, bu esa kaskadli nosozliklarni keltirib chiqarishi mumkin.

Asinxron aloqa

Xizmatlar zudlik bilan javoblarni kutmasdan xabar brokerlari yoki voqea avtobuslari orqali muloqot qiladi:

  • Xabar navbatlari: RabbitMQ, nuqtadan nuqtaga xabar almashish uchun ActiveMQ
  • Pub/Sub: Kafka, Redis Pub/Sub bir nechta obunachilarga xabarlarni nashr qilish uchun
  • Voqealar oqimi: Kafka, ma’lumotlar oqimini boshqarish uchun AWS Kinesis

Misol: Voqealar avtobusi bilan voqealarga asoslangan aloqa

// order-service.js publishing an event
const axios = require('axios');

async function publishEvent(eventType, data) {
  try {
    await axios.post('http://event-bus:3100/events', {
      type: eventType,
      data: data,
      source: 'order-service',
      timestamp: new Date().toISOString()
    });
    console.log(`Published event: ${eventType}`);
  } catch (error) {
    console.error(`Failed to publish event ${eventType}:`, error.message);
    // Store failed events for retry
    storeFailedEvent(eventType, data, error);
  }
}

// Create an order and publish event
app.post('/orders', async (req, res) => {
  try {
    const order = await createOrder(req.body);
    
    // Publish event for other services
    await publishEvent('order.created', order);
    
    res.status(201).json(order);
  } catch (error) {
    res.status(500).json({ error: 'Order creation failed' });
  }
});

Xizmatdagi nosozliklarni ko‘rib chiqish

Mikroservislarda sizga aloqa nosozliklarini hal qilish strategiyalari kerak bo‘ladi:

Naqsh Tavsif Qachon foydalanish kerak
O‘chirish to‘xtatuvchisi Kaskadli nosozliklarning oldini olib, muvaffaqiyatsiz xizmatlarga so‘rovlarni vaqtincha to‘xtatadi Xizmatlar noto‘g‘ri bog‘liqliklardan himoyaga muhtoj bo‘lganda
Orqaga qaytish bilan qayta urinib ko‘ring Muvaffaqiyatsiz so‘rovlarni kechikishlar bilan avtomatik ravishda qaytadan urinib ko‘radi Tez hal qilinishi mumkin bo‘lgan vaqtinchalik nosozliklar uchun
Vaqt tugashi namunasi Javoblarni kutish uchun maksimal vaqtni belgilaydi Sekin xizmatlarda mavzularni bloklashni oldini olish uchun
Qopqoq naqsh Barcha resurslarni iste’mol qilishiga yo‘l qo‘ymaslik uchun nosozliklarni ajratib turadi Komponentlar ichidagi nosozliklarni saqlash uchun
Qayta tiklash namunasi Xizmat ishlamay qolganda muqobil javob beradi Muvaffaqiyatsizliklar paytida asosiy funksionallikni saqlab qolish uchun

Misol: O‘chirish to‘xtatuvchisini amalga oshirish

const CircuitBreaker = require('opossum');

// Configure the circuit breaker
const options = {
  failureThreshold: 50,        // Open after 50% of requests fail
  resetTimeout: 10000,         // Try again after 10 seconds
  timeout: 8080,               // Time before request is considered failed
  errorThresholdPercentage: 50 // Error percentage to open circuit
};

// Create a circuit breaker for the user service
const getUserDetailsBreaker = new CircuitBreaker(getUserDetails, options);

// Add listeners for circuit state changes
getUserDetailsBreaker.on('open', () => {
  console.log('Circuit OPEN - User service appears to be down');
});

getUserDetailsBreaker.on('halfOpen', () => {
  console.log('Circuit HALF-OPEN - Testing user service');
});

getUserDetailsBreaker.on('close', () => {
  console.log('Circuit CLOSED - User service restored');
});

// Use the circuit breaker in the route handler
app.get('/orders/:orderId', async (req, res) => {
  const orderId = req.params.orderId;
  const order = await getOrderById(orderId);
  
  try {
    // Call the user service through the circuit breaker
    const user = await getUserDetailsBreaker.fire(order.userId);
    res.json({ order, user });
  } catch (error) {
    // If the circuit is open or the call fails, return fallback data
    console.error('Could not fetch user details:', error.message);
    res.json({
      order,
      user: { id: order.userId, name: 'User details unavailable' }
    });
  }
});
  try {
    const response = await axios.get(`http://user-service:8080/users/${userId}`);
    return response.data;
  } catch (error) {
    console.error('Error fetching user details:', error.message);
    throw new Error('User service unavailable');
  }
}

// Process an order
app.post('/orders', async (req, res) => {
  try {
    const { userId, products } = req.body;
    
    // Get user details from the user service
    const user = await getUserDetails(userId);
    
    // Create the order
    const order = {
      id: generateOrderId(),
      userId: userId,
      userEmail: user.email,
      products: products,
      total: calculateTotal(products),
      createdAt: new Date()
    };
    
    // Save order (simplified)
    saveOrder(order);
    
    res.status(201).json(order);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

Asinxron aloqa

Xizmatlar xabar brokerlari yoki voqea avtobuslari orqali bog‘lanadi:

  • Xabar navbatlari: RabbitMQ, ActiveMQ
  • Oqimli platformalar: Apache Kafka, AWS Kinesis
  • Tadbir avtobuslari: Redis Pub/Sub, NATS

Misol: RabbitMQ bilan asinxron aloqa

// order-service.js publishing an event
const amqp = require('amqplib');

async function publishOrderCreated(order) {
  try {
    const connection = await amqp.connect('amqp://localhost');
    const channel = await connection.createChannel();
    
    const exchange = 'order_events';
    await channel.assertExchange(exchange, 'topic', { durable: true });
    
    const routingKey = 'order.created';
    const message = JSON.stringify(order);
    
    channel.publish(exchange, routingKey, Buffer.from(message));
    console.log(`Published order created event for order ${order.id}`);
    
    setTimeout(() => connection.close(), 500);
  } catch (error) {
    console.error('Error publishing event:', error);
  }
}

// notification-service.js consuming the event
async function setupOrderCreatedConsumer() {
  const connection = await amqp.connect('amqp://localhost');
  const channel = await connection.createChannel();
  
  const exchange = 'order_events';
  await channel.assertExchange(exchange, 'topic', { durable: true });
  
  const queue = 'notification_service_orders';
  await channel.assertQueue(queue, { durable: true });
  await channel.bindQueue(queue, exchange, 'order.created');
  
  channel.consume(queue, (msg) => {
    if (msg) {
      const order = JSON.parse(msg.content.toString());
      console.log(`Sending order confirmation email for order ${order.id}`);
      sendOrderConfirmationEmail(order);
      channel.ack(msg);
    }
  });
}

Eng yaxshi amaliyot: Shoshilinch javob talab qilmaydigan operatsiyalar uchun moslashuvchanlikni yaxshilash va xizmatlar o‘rtasidagi bog‘lanishni kamaytirish uchun asinxron xabarlardan foydalaning.


API shlyuz namunasi

API shlyuzi mikroservislar arxitekturasiga barcha mijoz so‘rovlari uchun yagona kirish nuqtasi vazifasini bajaradi.

API shlyuzining mas’uliyati

  • So‘rov marshruti: Mijoz so‘rovlarini tegishli xizmatlarga yo‘naltiradi
  • API tarkibi: Bir nechta xizmatlardan javoblarni jamlaydi
  • Protokol tarjimasi: Protokollarni o‘zgartiradi (masalan, HTTP dan gRPC ga)
  • Autentifikatsiya va avtorizatsiya: Xavfsizlik muammolarini hal qiladi
  • So‘rovlarni cheklash (Rate limiting): APIdan suiiste’mol qilinishining oldini oladi
  • Monitoring va jurnallar: API foydalanish ko‘rinishini ta’minlaydi

Misol: API Gateway Implementation

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');

const app = express();
const PORT = 8080;

// Add security headers
app.use(helmet());

// Apply rate limiting
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100,                // limit each IP to 100 requests per windowMs
  message: 'Too many requests from this IP, please try again later'
});
app.use('/api/', apiLimiter);

// Authentication middleware
function authenticate(req, res, next) {
  const token = req.headers.authorization;
  if (!token) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  // Verify token logic would go here
  next();
}

// Service registry (hardcoded for simplicity)
const serviceRegistry = {
  userService: 'http://localhost:3001',
  productService: 'http://localhost:3002',
  orderService: 'http://localhost:3003'
};

// Define proxy middleware for each service
const userServiceProxy = createProxyMiddleware({
  target: serviceRegistry.userService,
  changeOrigin: true,
  pathRewrite: { '^/api/users': '/users' }
});

const productServiceProxy = createProxyMiddleware({
  target: serviceRegistry.productService,
  changeOrigin: true,
  pathRewrite: { '^/api/products': '/products' }
});

const orderServiceProxy = createProxyMiddleware({
  target: serviceRegistry.orderService,
  changeOrigin: true,
  pathRewrite: { '^/api/orders': '/orders' }
});

// Route requests to appropriate services
app.use('/api/users', authenticate, userServiceProxy);
app.use('/api/products', productServiceProxy);
app.use('/api/orders', authenticate, orderServiceProxy);

app.listen(PORT, () => console.log(`API Gateway running on port ${PORT}`));
Misolni ishga tushirish »

Eng yaxshi amaliyot: O‘zingizni yaratish o‘rniga, ishlab chiqarish muhitida Kong, Netflix Zuul kabi maxsus API Gateway yoki AWS API Gateway kabi bulutli yechimlardan foydalaning.


Xizmat kashfiyoti

Xizmatni ochish mikroservislarga qattiq kodlangan so‘nggi nuqtalarsiz dinamik ravishda bir-birlarini topish va muloqot qilish imkonini beradi.

Xizmatni aniqlash usullari

Metod Tavsif
Mijoz tomonini aniqlash Mijozlar xizmat ko‘rsatish joylarini topish va balans so‘rovlarini o‘zlari yuklash uchun xizmat registrini so‘rashadi
Server tomonida kashf qilish Mijozlar xizmat nusxalarini aniqlash bilan shug‘ullanadigan marshrutizator/yuk balanslagichiga murojaat qiladi
DNS-ga asoslangan kashfiyot Xizmatlar DNS SRV yozuvlari yoki shunga o‘xshash texnologiyalar orqali topiladi

Misol: Mijoz tomonidan xizmat ko‘rsatishni aniqlash

const axios = require('axios');

// Simple service registry client
class ServiceRegistry {
  constructor(registryUrl) {
    this.registryUrl = registryUrl;
    this.servicesCache = {};
    this.cacheTimeout = 60000; // 1 minute
  }

  async getService(name) {
    // Check cache first
    const cachedService = this.servicesCache[name];
    if (cachedService && cachedService.expiresAt > Date.now()) {
      return this._selectInstance(cachedService.instances);
    }

    // Fetch from registry if not in cache or expired
    try {
      const response = await axios.get(`${this.registryUrl}/services/${name}`);
      const instances = response.data.instances;

      if (!instances || instances.length === 0) {
        throw new Error(`No instances found for service: ${name}`);
      }

      // Update cache
      this.servicesCache[name] = {
        instances,
        expiresAt: Date.now() + this.cacheTimeout
      };

      return this._selectInstance(instances);
    } catch (error) {
      console.error(`Error fetching service ${name}:`, error.message);
      throw new Error(`Service discovery failed for ${name}`);
    }
  }

  // Simple round-robin load balancing
  _selectInstance(instances) {
    if (!instances._lastIndex) {
      instances._lastIndex = 0;
    } else {
      instances._lastIndex = (instances._lastIndex + 1) % instances.length;
    }

    return instances[instances._lastIndex];
  }
}

// Usage example
const serviceRegistry = new ServiceRegistry('http://registry:8500/v1');

async function callUserService(userId) {
  try {
    const serviceInstance = await serviceRegistry.getService('user-service');
    const response = await axios.get(`${serviceInstance.url}/users/${userId}`);
    return response.data;
  } catch (error) {
    console.error('Error calling user service:', error.message);
    throw error;
  }
}

Mashhur xizmatlarni aniqlash vositalari

  • Konsul: Xizmatni aniqlash va sozlash
  • va hokazo: taqsimlangan kalit-qiymatlar ombori
  • ZooKeeper: Konfiguratsiya va sinxronizatsiya uchun markazlashtirilgan xizmat
  • Evrika: AWS buluti uchun REST asosidagi xizmat kashfiyoti
  • Kubernetes Service Discovery: Kubernetes uchun o‘rnatilgan xizmat kashfiyoti

Ma’lumotlarni boshqarish strategiyalari

Mikroservislar arxitekturasida ma’lumotlarni boshqarish monolit ilovalarga qaraganda boshqacha yondashuvlarni talab qiladi.

Har bir xizmat uchun ma’lumotlar bazasi

Har bir mikroservis o‘zining maxsus ma’lumotlar bazasiga ega bo‘lib, bo‘sh ulanish va mustaqil masshtabni ta’minlaydi.

Eslatma: Har bir xizmat uchun ma’lumotlar bazasi sxemasi har bir xizmatga o‘z ehtiyojlari uchun eng mos ma’lumotlar bazasi texnologiyasini tanlash imkonini beradi (SQL, NoSQL, Graph DB va boshqalar).

Taqsimlangan operatsiyalar

ACID tranzaksiyalarisiz xizmatlar bo‘ylab ma’lumotlarning izchilligini ta’minlash maxsus naqshlarni talab qiladi:

Saga naqsh

Har bir tranzaksiya bitta xizmat doirasidagi ma’lumotlarni yangilaydigan mahalliy tranzaktsiyalar ketma-ketligi. Har bir mahalliy tranzaksiya keyingi tranzaksiyani boshlaydigan voqeani nashr etadi.

Misol: Saga Pattern Implementation

// In order-service.js

async function createOrder(orderData) {
  try {
    // Start the saga - create order
    const order = await orderRepository.create(orderData);

    // Publish event to trigger the next step in the saga
    await eventBus.publish('order.created', { orderId: order.id, ...orderData });

    return order;
  } catch (error) {
    console.error('Failed to create order:', error);
    throw error;
  }
}

// In payment-service.js
async function processPayment(event) {
  const { orderId, userId, amount } = event.data;

  try {
    // Process payment
    const payment = await paymentProcessor.charge(userId, amount, `Order ${orderId}`);

    // Publish success event
    await eventBus.publish('payment.succeeded', {
      orderId,
      paymentId: payment.id
    });
  } catch (error) {
    // Publish failure event to trigger compensation
    await eventBus.publish('payment.failed', {
      orderId,
      reason: error.message
    });
  }
}

// Compensating transaction in order-service.js
async function handlePaymentFailure(event) {
  const { orderId, reason } = event.data;

  // Update order status to 'payment-failed'
  await orderRepository.updateStatus(orderId, 'payment-failed', reason);

  // Notify customer about payment failure
  const order = await orderRepository.findById(orderId);
  await notificationService.notifyCustomer(order.userId, `Payment failed for order ${orderId}: ${reason}`);
}

Voqea manbalari va CQRS

Event Sourcing ilova holatidagi barcha o‘zgarishlarni hodisalar ketma-ketligi sifatida saqlaydi. Command Query Responsibility Segregation (CQRS) o‘qish va yozish amallarini bir-biridan ajratadi.

Misol: Voqealar manbasi

// Event store
class EventStore {
  constructor() {
    this.events = [];
  }

  append(aggregateId, eventType, eventData) {
    const event = {
      id: this.events.length + 1,
      timestamp: new Date().toISOString(),
      aggregateId,
      type: eventType,
      data: eventData
    };

    this.events.push(event);
    this.publishEvent(event);
    return event;
  }

  getEventsForAggregate(aggregateId) {
    return this.events.filter(event => event.aggregateId === aggregateId);
  }

  publishEvent(event) {
    // Publish to subscribers/event bus
    console.log(`Event published: ${event.type}`);
  }
}

// Order aggregate
class Order {
  constructor(eventStore) {
    this.eventStore = eventStore;
  }

  createOrder(orderId, userId, items) {
    this.eventStore.append(orderId, 'OrderCreated', {
      userId,
      items,
      status: 'created'
    });
  }

  addItem(orderId, item) {
    this.eventStore.append(orderId, 'ItemAdded', { item });
  }

  removeItem(orderId, itemId) {
    this.eventStore.append(orderId, 'ItemRemoved', { itemId });
  }

  submitOrder(orderId) {
    this.eventStore.append(orderId, 'OrderSubmitted', {
      status: 'submitted',
      submittedAt: new Date().toISOString()
    });
  }

  // Rebuild the current state from events
  getOrder(orderId) {
    const events = this.eventStore.getEventsForAggregate(orderId);
    if (events.length === 0) return null;

    let order = { id: orderId, items: [] };

    for (const event of events) {
      switch (event.type) {
        case 'OrderCreated':
          order = { ...order, ...event.data };
          break;
        case 'ItemAdded':
          order.items.push(event.data.item);
          break;
        case 'ItemRemoved':
          order.items = order.items.filter(item => item.id !== event.data.itemId);
          break;
        case 'OrderSubmitted':
          order.status = event.data.status;
          order.submittedAt = event.data.submittedAt;
          break;
      }
    }

    return order;
  }
}

Mikroservis namunalari

Bir nechta dizayn naqshlari mikroservislar arxitekturasidagi umumiy muammolarni hal qilishga yordam beradi:

API shlyuzi

Tegishli xizmatlarga yo‘naltirilgan barcha mijoz so‘rovlari uchun yagona kirish nuqtasi.

// Basic API Gateway with Express
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');

const app = express();

// Authentication middleware
app.use('/api', (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    return res.status(401).json({ message: 'Authentication required' });
  }
  // Validate token (simplified)
  next();
});

// Route to services
app.use('/api/users', createProxyMiddleware({
  target: 'http://user-service:8080',
  pathRewrite: { '^/api/users': '/users' }
}));

app.use('/api/orders', createProxyMiddleware({
  target: 'http://order-service:3001',
  pathRewrite: { '^/api/orders': '/orders' }
}));

app.listen(8000, () => {
  console.log('API Gateway running on port 8000');
});

O‘chirish to‘xtatuvchisi

Xizmat javob bermayotganda tez ishdan chiqish orqali kaskadli nosozliklarni oldini oladi.

Xizmat kashfiyoti

Xizmatlarga qattiq kodlangan joylarsiz bir-birlarini topish va muloqot qilish imkonini beradi.

Saga naqsh

Bir nechta xizmatlarda taqsimlangan tranzaktsiyalarni boshqaradi.

CQRS (buyruq va so‘rov mas’uliyatlarini ajratish)

Yaxshiroq ishlash va kengaytirilishi uchun o‘qish va yozish operatsiyalarini ajratadi.

Qopqoq naqsh

Tizim bo‘ylab kaskad bo‘lishining oldini olish uchun nosozliklarni izolyatsiya qiladi.

Kengaytirilgan maslahat: Xizmatdan xizmatga, jumladan, trafikni boshqarish, xavfsizlik va kuzatuvchanlik kabi aloqalarni boshqarish uchun Istio yoki Linkerd kabi servis tarmog‘idan foydalanishni o‘ylab ko‘ring.


Joylashtirish strategiyalari

Mikroservislar zamonaviy joylashtirish yondashuvlaridan foyda ko‘radi:

Konteynerlash

Docker konteynerlari har bir mikroservis uchun izchil muhitni ta’minlaydi.

Node.js Microservice uchun Dockerfile misoli

FROM node:16-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 8080

CMD ["node", "user-service.js"]

Orkestratsiya

Kubernetes kabi vositalar konteynerlashtirilgan xizmatlarni joylashtirish, masshtablash va boshqarishni avtomatlashtiradi.

Misol Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  template:
    metadata:
      labels:
        app: user-service
    spec:
      containers:
      - name: user-service
        image: my-registry/user-service:latest
        ports:
        - containerPort: 8080
        env:
        - name: DB_HOST
          value: mongodb-service
        resources:
          limits:
            cpu: "0.5"
            memory: "512Mi"
          requests:
            cpu: "0.2"
            memory: "256Mi"

Uzluksiz joylashtirish

CI/CD payplar (pipes)i individual xizmatlarni sinovdan o‘tkazish va tarqatishni avtomatlashtiradi.

Infratuzilma kod sifatida

Terraform yoki AWS CloudFormation kabi vositalar infratuzilmani deklarativ tarzda belgilaydi.

Eng yaxshi amaliyot: Mikroservislarni yangilashda uzilishlar va xavflarni minimallashtirish uchun ko‘k-yashil yoki kanareykalarni joylashtirish strategiyalaridan foydalaning.


Kengaytirilgan mikroservis namunalari

1. O‘chirish to‘xtatuvchisi namunasi

Xizmatlar ishlamay qolganda kaskadli nosozliklarni oldini oling:

// circuit-breaker.js
class CircuitBreaker {
  constructor(request, options = {}) {
    this.request = request;
    this.state = 'CLOSED';
    this.failureCount = 0;
    this.successCount = 0;
    this.nextAttempt = Date.now();

    // Configurable thresholds
    this.failureThreshold = options.failureThreshold || 5;
    this.successThreshold = options.successThreshold || 2;
    this.timeout = options.timeout || 10000; // 10 seconds
  }

  async fire() {
    if (this.state === 'OPEN') {
      if (this.nextAttempt <= Date.now()) {
        this.state = 'HALF';
      } else {
        throw new Error('Circuit is OPEN');
      }
    }

    try {
      const response = await this.request();
      return this.success(response);
    } catch (err) {
      return this.fail(err);
    }
  }

  success(response) {
    if (this.state === 'HALF') {
      this.successCount++;
      if (this.successCount > this.successThreshold) {
        this.close();
      }
    }
    this.failureCount = 0;
    return response;
  }

  fail(err) {
    this.failureCount++;
    if (this.failureCount >= this.failureThreshold) {
      this.open();
    }
    return err;
  }

  open() {
    this.state = 'OPEN';
    this.nextAttempt = Date.now() + this.timeout;
  }

  close() {
    this.state = 'CLOSED';
    this.failureCount = 0;
    this.successCount = 0;
    this.nextAttempt = 0;
  }
}

module.exports = CircuitBreaker;

2. Saga naqsh

Mikroservislar bo‘ylab taqsimlangan tranzaktsiyalarni boshqarish:

// order-saga.js
class OrderSaga {
  constructor(orderId) {
    this.orderId = orderId;
    this.steps = [];
    this.compensations = [];
  }

  addStep(execute, compensate) {
    this.steps.push(execute);
    this.compensations.unshift(compensate);
    return this;
  }

  async execute() {
    const executedSteps = [];

    try {
      for (const [index, step] of this.steps.entries()) {
        await step();
        executedSteps.push(index);
      }
      return { success: true };
    } catch (error) {
      console.error('Saga execution failed, compensating...', error);
      await this.compensate(executedSteps);
      return { success: false, error };
    }
  }

  async compensate(executedSteps) {
    for (const stepIndex of executedSteps) {
      try {
        await this.compensations[stepIndex]();
      } catch (compError) {
        console.error('Compensation failed:', compError);
      }
    }
  }
}

// Example usage
const orderSaga = new OrderSaga('order-123')
  .addStep(
    () => orderService.createOrder({ id: 'order-123', items: ['item1', 'item2'] }),
    () => orderService.cancelOrder('order-123')
  )
  .addStep(
    () => paymentService.processPayment('order-123', 100.00),
    () => paymentService.refundPayment('order-123')
  );

orderSaga.execute();

Mikroservislar xavfsizligi

1. Xizmatdan xizmatga autentifikatsiya

// auth-middleware.js
const jwt = require('jsonwebtoken');
const authenticateService = (req, res, next) => {
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    return res.status(401).json({ message: 'No token provided' });
  }

  const token = authHeader.split(' ')[1];

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    if (decoded.iss !== 'auth-service') {
      return res.status(403).json({ message: 'Invalid token issuer' });
    }

    // Attach service info to request
    req.service = {
      id: decoded.sub,
      name: decoded.serviceName,
      permissions: decoded.permissions || []
    };

    next();
  } catch (error) {
    return res.status(401).json({ message: 'Invalid or expired token' });
  }
};

module.exports = authenticateService;

2. Tarifni cheklash

// rate-limiter.js
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const { createClient } = require('redis');
// Create Redis client

const redisClient = createClient({
  url: process.env.REDIS_URL
});

// Initialize rate limiter
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each IP to 100 requests per window
  standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers
  store: new RedisStore({
    sendCommand: (...args) => redisClient.sendCommand(args)
  }),
  handler: (req, res) => {
    res.status(429).json({
      message: 'Too many requests, please try again later.'
    });
  }
});

module.exports = apiLimiter;

Kuzatish va kuzatish

1. OpenTelemetry bilan taqsimlangan kuzatish

// tracing.js
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const { BatchSpanProcessor } = require('@opentelemetry/sdk-trace-base');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');
const { ExpressInstrumentation } = require('@opentelemetry/instrumentation-express');


// Configure the tracer provider
const provider = new NodeTracerProvider({
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]: 'user-service',
    'service.version': '1.0.0',
  }),
});

// Configure Jaeger exporter
const exporter = new JaegerExporter({
  endpoint: process.env.JAEGER_ENDPOINT || 'http://localhost:14268/api/traces',
});

// Add the exporter to the provider
provider.addSpanProcessor(new BatchSpanProcessor(exporter));

// Initialize the OpenTelemetry APIs to use the NodeTracerProvider
provider.register();

// Register instrumentations
registerInstrumentations({
  instrumentations: [
    new HttpInstrumentation(),
    new ExpressInstrumentation(),
  ],
  tracerProvider: provider,
});

console.log('Tracing initialized');

2. Strukturaviy ro‘yxatga olish

// logger.js
const winston = require('winston');
const { combine, timestamp, json } = winston.format;

const logger = winston.createLogger({
  level: process.env.LOG_LEVEL || 'info',
  format: combine(
    timestamp(),
    json()
  ),
  defaultMeta: {
    service: 'user-service',
    environment: process.env.NODE_ENV || 'development',
  },
  transports: [
    new winston.transports.Console(),
    // Add other transports like file, ELK, etc.
  ],
});

// Add request ID to logs
logger.child = function(opts) {
  return new Proxy(logger, {
    get(target, property, receiver) {
      if (property === 'write') {
        return (...args) => {
          const originalMeta = args[args.length - 1] || {};
          args[args.length - 1] = { ...opts, ...originalMeta };
          return logger[property](...args);
        };
      }
      return Reflect.get(target, property, receiver);
    },
  });
};

module.exports = logger;



W3Schools Pathfinder

Yutuqlaringizni kuzating – bu bepul!