Home > Blog > tech

Microservices Communication Patterns คืออะไร? Sync vs Async, API Gateway, Service Mesh 2026

microservices communication patterns guide
Microservices Communication Patterns Guide 2026
2026-04-10 | tech | 4500 words

ในสถาปัตยกรรม Microservices แต่ละ Service ทำงานเป็นอิสระ แต่ต้องสื่อสารกันเพื่อให้ระบบทำงานครบถ้วน การเลือก Communication Pattern ที่ถูกต้องเป็นหัวใจสำคัญที่กำหนดทั้ง Performance, Reliability, Scalability และ Maintainability ของระบบทั้งหมด

บทความนี้จะอธิบาย Communication Patterns ทั้งแบบ Synchronous และ Asynchronous, API Gateway, Service Mesh, Resilience Patterns และวิธีเลือก Pattern ที่เหมาะสม

Synchronous Communication

Synchronous คือ Service A เรียก Service B แล้วรอคำตอบ เหมาะกับกรณีที่ต้องการ Response ทันที

1. REST (HTTP/JSON)

รูปแบบพื้นฐานที่นิยมที่สุด ใช้ HTTP Methods (GET, POST, PUT, DELETE) กับ JSON Payload

// Order Service เรียก User Service
// GET /api/users/123
const response = await fetch('http://user-service:3000/api/users/123');
const user = await response.json();

// ข้อดี: เรียบง่าย, ทุกภาษา Support, Debug ง่าย
// ข้อเสีย: Latency สูง (HTTP overhead), Tight Coupling, ไม่ Efficient สำหรับ Binary Data

2. gRPC

High-performance RPC Framework จาก Google ใช้ Protocol Buffers (Protobuf) เป็น Interface Definition Language

// user.proto — Schema Definition
syntax = "proto3";

service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
  rpc ListUsers (ListRequest) returns (stream UserResponse);  // Server Streaming
}

message UserRequest {
  string id = 1;
}

message UserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
}

// Go Client
conn, _ := grpc.Dial("user-service:50051", grpc.WithInsecure())
client := pb.NewUserServiceClient(conn)
user, err := client.GetUser(ctx, &pb.UserRequest{Id: "123"})
FeatureRESTgRPC
ProtocolHTTP/1.1 or 2HTTP/2
FormatJSON (text)Protobuf (binary)
Performanceปานกลางสูงมาก (10x faster)
Streamingไม่มี nativeBi-directional
Code Generationต้อง manualAuto จาก .proto
Browser Supportดีต้องใช้ gRPC-Web

3. GraphQL

# Query — ขอเฉพาะ Field ที่ต้องการ
query {
  user(id: "123") {
    name
    email
    orders {
      id
      total
    }
  }
}

# ข้อดี: ไม่ Over-fetch, Single Endpoint, Strong Typing
# ข้อเสีย: ซับซ้อนกว่า REST, N+1 Problem, Caching ยากกว่า

Asynchronous Communication

Asynchronous คือ Service A ส่ง Message แล้วไม่ต้องรอ Response เหมาะกับ Background Processing, Event-Driven Workflows และ Decoupling

1. Message Queue

Producer ส่ง Message เข้า Queue, Consumer ดึงไปประมวลผล เช่น RabbitMQ, Amazon SQS, Redis Queue

// RabbitMQ — Producer (Order Service)
const amqp = require('amqplib');
const conn = await amqp.connect('amqp://rabbitmq:5672');
const channel = await conn.createChannel();

await channel.assertQueue('order-processing');
channel.sendToQueue('order-processing', Buffer.from(JSON.stringify({
  orderId: 'ORD-001',
  userId: '123',
  items: [{ productId: 'P1', qty: 2 }],
  action: 'process'
})));

// Consumer (Inventory Service)
channel.consume('order-processing', (msg) => {
  const order = JSON.parse(msg.content.toString());
  // ตัดสต็อกสินค้า
  updateInventory(order.items);
  channel.ack(msg);  // Acknowledge
});

2. Event Streaming (Apache Kafka)

Kafka เหมาะกับ High-throughput, Event Sourcing และ Real-time Data Pipeline

// Kafka Producer
const { Kafka } = require('kafkajs');
const kafka = new Kafka({ brokers: ['kafka:9092'] });
const producer = kafka.producer();

await producer.send({
  topic: 'order-events',
  messages: [{
    key: 'ORD-001',
    value: JSON.stringify({
      type: 'ORDER_CREATED',
      data: { orderId: 'ORD-001', total: 1500 },
      timestamp: Date.now()
    })
  }]
});

// Kafka Consumer (Multiple Consumer Groups)
const consumer = kafka.consumer({ groupId: 'payment-service' });
await consumer.subscribe({ topic: 'order-events' });
await consumer.run({
  eachMessage: async ({ message }) => {
    const event = JSON.parse(message.value.toString());
    if (event.type === 'ORDER_CREATED') {
      await processPayment(event.data);
    }
  }
});
FeatureMessage Queue (RabbitMQ)Event Streaming (Kafka)
ModelPush (Broker ส่งให้ Consumer)Pull (Consumer ดึงเอง)
Retentionลบหลัง Consumeเก็บตาม Retention Period
Replayไม่ได้ได้ (Rewind Offset)
Throughputปานกลางสูงมาก (ล้าน msg/s)
Use CaseTask Queue, RPCEvent Sourcing, Analytics

Publish-Subscribe Pattern

// Publisher ส่ง Event โดยไม่รู้ว่าใคร Subscribe
// Service หลายตัว Subscribe Topic เดียวกัน

// Order Service (Publisher)
channel.publish('order-exchange', 'order.created', Buffer.from(JSON.stringify(order)));

// Email Service (Subscriber 1)
channel.bindQueue('email-queue', 'order-exchange', 'order.created');
// Inventory Service (Subscriber 2)
channel.bindQueue('inventory-queue', 'order-exchange', 'order.created');
// Analytics Service (Subscriber 3)
channel.bindQueue('analytics-queue', 'order-exchange', 'order.*');

API Gateway Pattern

API Gateway เป็น Single Entry Point สำหรับ Client ทำหน้าที่ Route Request ไปยัง Service ที่ถูกต้อง พร้อมจัดการ Cross-cutting Concerns

หน้าที่ของ API Gateway

# Kong API Gateway Configuration (YAML)
services:
  - name: user-service
    url: http://user-service:3000
    routes:
      - name: user-routes
        paths:
          - /api/users
        methods:
          - GET
          - POST
    plugins:
      - name: rate-limiting
        config:
          minute: 100
      - name: jwt
      - name: cors

  - name: order-service
    url: http://order-service:3001
    routes:
      - name: order-routes
        paths:
          - /api/orders

API Gateway Tools

Toolประเภทจุดเด่น
KongOpen SourcePlugin ecosystem, Lua scripting
AWS API GatewayManagedLambda integration, Auto-scaling
Envoy ProxyOpen SourceHigh performance, gRPC support
NGINXOpen SourceMature, Simple config
TraefikOpen SourceAuto-discovery, Docker native

BFF — Backend for Frontend

สร้าง API Gateway แยกสำหรับแต่ละ Client Type:

// Mobile BFF — ส่งเฉพาะ Field ที่ Mobile ต้องการ
app.get('/mobile/orders/:id', async (req, res) => {
  const order = await orderService.getOrder(req.params.id);
  const user = await userService.getUser(order.userId);
  // Compact response สำหรับ Mobile
  res.json({
    id: order.id,
    total: order.total,
    status: order.status,
    userName: user.name
  });
});

Service Mesh

Service Mesh จัดการ Service-to-Service Communication โดยไม่ต้องแก้ Code ทำงานผ่าน Sidecar Proxy ที่ Inject เข้าไปข้าง Service

Istio

# Istio VirtualService — Traffic Management
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: user-service
spec:
  hosts:
    - user-service
  http:
    - match:
        - headers:
            x-version:
              exact: "v2"
      route:
        - destination:
            host: user-service
            subset: v2
    - route:
        - destination:
            host: user-service
            subset: v1
          weight: 90
        - destination:
            host: user-service
            subset: v2
          weight: 10  # Canary 10%

---
# Circuit Breaker
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: user-service
spec:
  host: user-service
  trafficPolicy:
    connectionPool:
      http:
        h2UpgradePolicy: DEFAULT
        http1MaxPendingRequests: 100
        http2MaxRequests: 1000
    outlierDetection:
      consecutive5xxErrors: 5
      interval: 10s
      baseEjectionTime: 30s

Service Mesh Features

FeatureIstioLinkerd
Complexityสูงต่ำ
Resource Usageสูงต่ำ
Featuresครบเน้น Essential
Learning Curveสูงปานกลาง
เหมาะกับEnterprise ขนาดใหญ่ทีมขนาดกลาง

Resilience Patterns

Circuit Breaker Pattern

ป้องกัน Cascading Failure เมื่อ Service ปลายทางล่ม หยุดส่ง Request ชั่วคราวแทนที่จะรอ Timeout

// Resilience4j (Java/Spring Boot)
@CircuitBreaker(name = "userService", fallbackMethod = "fallbackUser")
@Retry(name = "userService")
@TimeLimiter(name = "userService")
public CompletableFuture<User> getUser(String id) {
    return CompletableFuture.supplyAsync(
        () -> userClient.getUser(id)
    );
}

public CompletableFuture<User> fallbackUser(String id, Exception ex) {
    return CompletableFuture.completedFuture(
        new User(id, "Unknown", "N/A")  // Cached/Default response
    );
}

// application.yml
resilience4j:
  circuitbreaker:
    instances:
      userService:
        slidingWindowSize: 10
        failureRateThreshold: 50
        waitDurationInOpenState: 30s
        permittedNumberOfCallsInHalfOpenState: 3

Circuit Breaker มี 3 สถานะ:

Retry & Timeout Patterns

// Exponential Backoff with Jitter
async function retryWithBackoff(fn, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries) throw error;
      const delay = Math.min(1000 * Math.pow(2, attempt), 10000);
      const jitter = delay * 0.5 * Math.random();
      await sleep(delay + jitter);
    }
  }
}

// Timeout
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 5000);
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timeout);

Bulkhead Pattern

แยก Resource Pool ของแต่ละ Service เพื่อป้องกันไม่ให้ Service ที่ช้าดึง Resource ของ Service อื่น:

// แยก Connection Pool
const userServicePool = new ConnectionPool({ maxConnections: 20 });
const orderServicePool = new ConnectionPool({ maxConnections: 30 });

// ถ้า User Service ช้า → ใช้ได้แค่ 20 Connections
// Order Service ไม่ได้รับผลกระทบ

Saga Pattern — Distributed Transactions

ในระบบ Microservices ไม่สามารถใช้ Database Transaction ข้าม Service ได้ Saga Pattern แก้ปัญหานี้โดยแบ่ง Transaction เป็นหลาย Local Transactions พร้อม Compensating Actions

Choreography Saga

แต่ละ Service Publish Event หลังทำงานเสร็จ Service ถัดไป React ต่อ:

// ลำดับ:
// 1. Order Service → OrderCreated event
// 2. Payment Service (ฟัง OrderCreated) → PaymentProcessed event
// 3. Inventory Service (ฟัง PaymentProcessed) → StockReserved event
// 4. Shipping Service (ฟัง StockReserved) → ShipmentCreated event

// ถ้า Payment Failed:
// Payment Service → PaymentFailed event
// Order Service (ฟัง PaymentFailed) → Cancel Order (Compensate)

Orchestration Saga

มี Orchestrator (Saga Coordinator) ควบคุม Flow ทั้งหมด:

// Saga Orchestrator
class OrderSaga {
  async execute(orderData) {
    try {
      // Step 1: Create Order
      const order = await orderService.create(orderData);
      // Step 2: Process Payment
      const payment = await paymentService.charge(order.total);
      // Step 3: Reserve Inventory
      await inventoryService.reserve(order.items);
      // Step 4: Create Shipment
      await shippingService.create(order.id);
      return { success: true, orderId: order.id };
    } catch (error) {
      // Compensating Actions (Rollback)
      await this.compensate(error.failedStep);
    }
  }

  async compensate(failedStep) {
    switch(failedStep) {
      case 'shipping':
        await inventoryService.release(order.items);
      case 'inventory':
        await paymentService.refund(payment.id);
      case 'payment':
        await orderService.cancel(order.id);
    }
  }
}
AspectChoreographyOrchestration
CouplingLooseCentral Coordinator
Complexityซับซ้อนเมื่อ Service เยอะเข้าใจง่ายกว่า
Single Point of Failureไม่มีOrchestrator
Debuggingยาก (Distributed)ง่ายกว่า (Central)
เหมาะกับ2-4 Services5+ Services

CQRS (Command Query Responsibility Segregation)

// แยก Write Model (Command) กับ Read Model (Query)

// Command Side — Write to Primary DB
app.post('/orders', async (req, res) => {
  const order = await orderRepository.save(req.body);
  await eventBus.publish('OrderCreated', order);
  res.json(order);
});

// Event Handler — Update Read Model
eventBus.subscribe('OrderCreated', async (order) => {
  // Write to Read-optimized DB (Elasticsearch, Redis, etc.)
  await readStore.index('orders', {
    ...order,
    userName: await userService.getName(order.userId),
    productNames: await productService.getNames(order.items)
  });
});

// Query Side — Read from Optimized Store
app.get('/orders/search', async (req, res) => {
  const results = await readStore.search('orders', req.query);
  res.json(results);  // Fast! ไม่ต้อง Join
});

API Versioning Strategies

// 1. URL Path Versioning
GET /api/v1/users
GET /api/v2/users

// 2. Header Versioning
GET /api/users
Accept: application/vnd.myapp.v2+json

// 3. Query Parameter
GET /api/users?version=2

// 4. Content Negotiation
Accept: application/json; version=2
Best Practice: ใช้ URL Path Versioning สำหรับ Major Changes (v1 → v2) เพราะชัดเจนที่สุด และใช้ Header Versioning สำหรับ Minor Changes เพื่อไม่สร้าง URL ใหม่

Service Discovery

Client-Side Discovery

// Service ค้นหา Instance จาก Registry เอง
const instances = await consul.health.service('user-service');
const target = instances[Math.floor(Math.random() * instances.length)];
const response = await fetch(`http://${target.address}:${target.port}/api/users`);

Server-Side Discovery (Kubernetes DNS)

# Kubernetes Service — Auto DNS Resolution
apiVersion: v1
kind: Service
metadata:
  name: user-service
spec:
  selector:
    app: user-service
  ports:
    - port: 3000

# เรียกผ่าน DNS
# http://user-service:3000/api/users
# http://user-service.namespace.svc.cluster.local:3000/api/users

Service Discovery Tools

ToolTypeFeature
Consul (HashiCorp)DedicatedHealth Check, KV Store, Multi-DC
Eureka (Netflix)DedicatedJava ecosystem, Spring Cloud
Kubernetes DNSBuilt-inAuto discovery ใน K8s
etcdKV StoreConsistent, Distributed

Health Checks & Readiness

// Express.js Health Check Endpoints
app.get('/health/live', (req, res) => {
  // Liveness: Service ยัง Run อยู่ไหม?
  res.status(200).json({ status: 'UP' });
});

app.get('/health/ready', async (req, res) => {
  // Readiness: พร้อมรับ Traffic ไหม?
  try {
    await db.query('SELECT 1');
    await redis.ping();
    res.status(200).json({ status: 'READY', db: 'UP', cache: 'UP' });
  } catch (err) {
    res.status(503).json({ status: 'NOT_READY', error: err.message });
  }
});

# Kubernetes Probes
spec:
  containers:
    - name: user-service
      livenessProbe:
        httpGet:
          path: /health/live
          port: 3000
        initialDelaySeconds: 15
        periodSeconds: 10
      readinessProbe:
        httpGet:
          path: /health/ready
          port: 3000
        initialDelaySeconds: 5
        periodSeconds: 5

Inter-Service Authentication

// 1. mTLS (Service Mesh จัดการให้)
// Istio inject Sidecar Proxy → Automatic mTLS

// 2. JWT Token Propagation
// API Gateway ตรวจ User Token
// แล้วส่ง Service Token ไปยัง Internal Services
app.use(async (req, res, next) => {
  const userToken = req.headers.authorization;
  // Verify User Token
  const user = await verifyJWT(userToken);
  // Create Internal Service Token
  req.serviceToken = await createServiceJWT({
    service: 'order-service',
    user: user.id,
    permissions: user.roles
  });
  next();
});

// 3. API Key (Simple)
// Internal services use pre-shared API keys
fetch('http://user-service/api/users', {
  headers: { 'X-Service-Key': process.env.INTERNAL_API_KEY }
});

Idempotency in Microservices

เมื่อ Retry Request ต้องมั่นใจว่าทำซ้ำแล้วไม่เกิดผลข้างเคียง (เช่น ตัดเงิน 2 ครั้ง):

// Idempotency Key
app.post('/payments', async (req, res) => {
  const idempotencyKey = req.headers['idempotency-key'];

  // ตรวจว่าเคยประมวลผล Key นี้แล้วหรือยัง
  const existing = await redis.get(`payment:${idempotencyKey}`);
  if (existing) {
    return res.json(JSON.parse(existing));  // Return cached result
  }

  // ประมวลผลครั้งแรก
  const payment = await processPayment(req.body);
  await redis.set(`payment:${idempotencyKey}`, JSON.stringify(payment), 'EX', 86400);
  res.json(payment);
});

// Client
fetch('/payments', {
  method: 'POST',
  headers: {
    'Idempotency-Key': crypto.randomUUID(),
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ amount: 1500, currency: 'THB' })
});

เลือก Communication Pattern อย่างไร?

สถานการณ์Pattern ที่เหมาะเหตุผล
Query ข้อมูลทันทีREST / gRPC (Sync)ต้องการ Response ทันที
High-performance InternalgRPCProtobuf binary, HTTP/2
Background ProcessingMessage Queueไม่ต้องรอ, Retry ได้
Event NotificationPub/SubDecouple, Multiple Consumers
High-throughput EventsKafkaReplay, Event Sourcing
Distributed TransactionSagaCompensating Actions
Flexible Client QueriesGraphQLลด Over/Under-fetch
Multi-client APIBFF + API GatewayOptimize per client
หลักการทั่วไป: ใช้ Synchronous สำหรับ Query (อ่านข้อมูล) และ Asynchronous สำหรับ Command (เปลี่ยนแปลงข้อมูล) ที่ไม่จำเป็นต้อง Real-time เสมอ Prefer Async เมื่อทำได้ เพราะ Decoupled มากกว่า

สรุป

การเลือก Communication Pattern ที่ถูกต้องใน Microservices เป็นการตัดสินใจสำคัญที่ส่งผลกระทบต่อทุกด้านของระบบ ไม่มี Pattern ใดที่ดีที่สุดสำหรับทุกกรณี แต่การเข้าใจข้อดีข้อเสียของแต่ละ Pattern จะช่วยให้ตัดสินใจได้ดีขึ้น

เริ่มจาก Synchronous (REST/gRPC) สำหรับ Simple Use Cases แล้วค่อยเพิ่ม Asynchronous (Message Queue/Kafka) เมื่อต้องการ Decoupling ใช้ API Gateway เป็น Single Entry Point ใช้ Circuit Breaker ป้องกัน Cascading Failure และใช้ Saga เมื่อต้องจัดการ Distributed Transactions ระบบที่ดีคือระบบที่ใช้ Pattern ที่เหมาะสมในแต่ละจุด ไม่ใช่ Pattern เดียวทั้งระบบ


Back to Blog | iCafe Forex | SiamLanCard | Siam2R