ในยุคที่ AI API กลายเป็นหัวใจสำคัญของแอปพลิเคชัน การจัดการความล้มเหลวอย่างชาญฉลาดเป็นสิ่งที่ทีมพัฒนาทุกคนต้องเผชิญ วันนี้เราจะมาเรียนรู้วิธีติดตั้ง Circuit Breaker Pattern ที่ช่วยให้ระบบของคุณทำงานได้อย่างเสถียรแม้ในสถานการณ์ที่ API ตอบสนองช้าหรือล่ม
กรณีศึกษา: ทีมสตาร์ทอัพ AI ในกรุงเทพฯ
ทีมพัฒนาแพลตฟอร์ม Chatbot อัจฉริยะแห่งหนึ่งในกรุงเทพฯ มีจุดเจ็บปวดหลักคือ ระบบต้องเรียกใช้ AI API จากหลายผู้ให้บริการ (OpenAI, Anthropic, Google) เพื่อให้บริการแชทบอทที่รองรับหลายภาษา
ปัญหาที่พบคือ เมื่อ API ตัวใดตัวหนึ่งตอบสนองช้าเกิน 5 วินาที ระบบทั้งหมดจะค้าง ทำให้ผู้ใช้งานรอนานและบางครั้งเกิด Timeout Error สูงถึง 30% ของคำขอทั้งหมด ค่าใช้จ่ายรายเดือนพุ่งสูงถึง $4,200 เพราะระบบพยายามเรียก API ที่ล่มซ้ำแล้วซ้ำเล่า
หลังจากที่ทีมได้ทดลองใช้ HolySheep AI ซึ่งรวม API ของหลายผู้ให้บริการเข้าด้วยกัน พร้อมทั้งมีฟีเจอร์ Circuit Breaker ในตัว ผลลัพธ์ที่ได้คือ ค่าเฉลี่ยความหน่วง (Latency) ลดลงจาก 420ms เหลือ 180ms และค่าใช้จ่ายรายเดือนลดลงเหลือ $680 ประหยัดได้ถึง 83%
Circuit Breaker Pattern คืออะไร
Circuit Breaker เป็น Design Pattern ที่ทำหน้าที่เหมือนฟิวส์ไฟฟ้า เมื่อระบบตรวจพบว่า API ตัวใดตอบสนองผิดพลาดติดต่อกันหลายครั้ง ระบบจะ "ตัดวงจร" ชั่วคราว ไม่เรียก API ตัวนั้นอีก แต่จะส่งต่อคำขอไปยัง API สำรองแทน เป็นการป้องกันไม่ให้ระบบทั้งหมดล่มจากปัญหาเพียงจุดเดียว
การติดตั้ง Circuit Breaker กับ HolySheep AI
HolySheep AI มี endpoint เดียวที่รวมทุก Model ไว้แล้ว ทำให้การติดตั้ง Circuit Breaker ง่ายมาก มาดูตัวอย่างการใช้งานกัน
// การติดตั้ง Circuit Breaker ด้วย Python
import requests
import time
from enum import Enum
from dataclasses import dataclass
from typing import Optional, Dict, Any
class CircuitState(Enum):
CLOSED = "closed" # ปกติ ทำงานได้
OPEN = "open" # ตัดวงจร รอการกู้คืน
HALF_OPEN = "half_open" # ทดสอบการกู้คืน
@dataclass
class CircuitBreakerConfig:
failure_threshold: int = 5 # จำนวนครั้งที่ล้มเหลวก่อนตัดวงจร
recovery_timeout: int = 30 # วินาทีที่รอก่อนลองใหม่
half_open_max_calls: int = 3 # จำนวนครั้งที่ทดสอบในโหมด half-open
success_threshold: int = 2 # ครั้งที่ต้องสำเร็จก่อนเปิดวงจร
class CircuitBreaker:
def __init__(self, config: CircuitBreakerConfig):
self.config = config
self.state = CircuitState.CLOSED
self.failure_count = 0
self.success_count = 0
self.last_failure_time: Optional[float] = None
self.half_open_calls = 0
def call(self, func, *args, **kwargs):
if self.state == CircuitState.OPEN:
if self._should_attempt_reset():
self.state = CircuitState.HALF_OPEN
self.half_open_calls = 0
else:
raise Exception("Circuit Breaker is OPEN - API temporarily unavailable")
try:
result = func(*args, **kwargs)
self._on_success()
return result
except Exception as e:
self._on_failure()
raise e
def _should_attempt_reset(self) -> bool:
if self.last_failure_time is None:
return True
return (time.time() - self.last_failure_time) >= self.config.recovery_timeout
def _on_success(self):
self.failure_count = 0
if self.state == CircuitState.HALF_OPEN:
self.success_count += 1
if self.success_count >= self.config.success_threshold:
self.state = CircuitState.CLOSED
self.success_count = 0
print("Circuit Breaker RESET to CLOSED state")
def _on_failure(self):
self.failure_count += 1
self.last_failure_time = time.time()
if self.state == CircuitState.HALF_OPEN:
self.state = CircuitState.OPEN
print("Circuit Breaker OPENED from HALF_OPEN state")
elif self.failure_count >= self.config.failure_threshold:
self.state = CircuitState.OPEN
print(f"Circuit Breaker OPENED after {self.failure_count} failures")
การใช้งานกับ HolySheep API
HOLYSHEEP_API_KEY = "YOUR_HOLYSHEEP_API_KEY"
HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"
circuit_breaker = CircuitBreaker(CircuitBreakerConfig())
def call_ai_model(model: str, prompt: str) -> Dict[str, Any]:
def _make_request():
response = requests.post(
f"{HOLYSHEEP_BASE_URL}/chat/completions",
headers={
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
},
json={
"model": model,
"messages": [{"role": "user", "content": prompt}]
},
timeout=10
)
response.raise_for_status()
return response.json()
return circuit_breaker.call(_make_request)
ตัวอย่างการใช้งาน
try:
result = call_ai_model("gpt-4.1", "อธิบายเรื่อง Circuit Breaker Pattern")
print(result)
except Exception as e:
print(f"Request failed: {e}")
# Fallback ไปยัง logic อื่น
Multi-Model Fallback Strategy
นอกจาก Circuit Breaker แล้ว การมี Fallback Strategy ที่ดีจะช่วยให้ระบบทำงานได้อย่างต่อเนื่อง ตัวอย่างด้านล่างแสดงการตั้งค่า Priority-based Fallback ที่จะเรียก Model สำรองหาก Model หลักไม่สามารถใช้งานได้
// TypeScript Implementation พร้อม Multi-Model Fallback
interface AIModelConfig {
name: string;
priority: number;
maxLatency: number; // milliseconds
}
interface CircuitBreakerState {
failures: number;
lastFailure: number;
isOpen: boolean;
}
class MultiModelRouter {
private models: AIModelConfig[] = [
{ name: "gpt-4.1", priority: 1, maxLatency: 3000 },
{ name: "claude-sonnet-4.5", priority: 2, maxLatency: 4000 },
{ name: "gemini-2.5-flash", priority: 3, maxLatency: 2000 },
{ name: "deepseek-v3.2", priority: 4, maxLatency: 2500 }
];
private circuitBreakers: Map<string, CircuitBreakerState> = new Map();
private readonly FAILURE_THRESHOLD = 5;
private readonly RECOVERY_TIMEOUT = 30000; // 30 seconds
constructor() {
// Initialize circuit breakers for each model
this.models.forEach(model => {
this.circuitBreakers.set(model.name, {
failures: 0,
lastFailure: 0,
isOpen: false
});
});
}
async chatCompletion(prompt: string): Promise<any> {
const availableModels = this.getAvailableModels();
for (const model of availableModels) {
try {
const result = await this.callWithTimeout(
model.name,
prompt,
model.maxLatency
);
this.resetCircuitBreaker(model.name);
return result;
} catch (error) {
console.error(Model ${model.name} failed:, error.message);
this.recordFailure(model.name);
if (this.isCircuitOpen(model.name)) {
console.log(Circuit breaker OPEN for ${model.name});
continue;
}
}
}
throw new Error("All models unavailable");
}
private async callWithTimeout(
modelName: string,
prompt: string,
timeout: number
): Promise<any> {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
try {
const response = await fetch("https://api.holysheep.ai/v1/chat/completions", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_HOLYSHEEP_API_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({
model: modelName,
messages: [{ role: "user", content: prompt }]
}),
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new Error(HTTP ${response.status});
}
return await response.json();
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
}
private getAvailableModels(): AIModelConfig[] {
return this.models
.filter(m => !this.isCircuitOpen(m.name))
.sort((a, b) => a.priority - b.priority);
}
private isCircuitOpen(modelName: string): boolean {
const cb = this.circuitBreakers.get(modelName);
if (!cb || !cb.isOpen) return false;
const now = Date.now();
if (now - cb.lastFailure >= this.RECOVERY_TIMEOUT) {
cb.isOpen = false;
cb.failures = 0;
return false;
}
return true;
}
private recordFailure(modelName: string): void {
const cb = this.circuitBreakers.get(modelName)!;
cb.failures++;
cb.lastFailure = Date.now();
if (cb.failures >= this.FAILURE_THRESHOLD) {
cb.isOpen = true;
console.log(Circuit breaker opened for ${modelName});
}
}
private resetCircuitBreaker(modelName: string): void {
const cb = this.circuitBreakers.get(modelName)!;
cb.failures = 0;
cb.isOpen = false;
}
getHealthStatus(): object {
const status: any = {};
this.models.forEach(model => {
const cb = this.circuitBreakers.get(model.name)!;
status[model.name] = {
failures: cb.failures,
isOpen: cb.isOpen,
available: !cb.isOpen
};
});
return status;
}
}
// การใช้งาน
const router = new MultiModelRouter();
async function main() {
try {
const result = await router.chatCompletion("สวัสดีครับ");
console.log("Response:", result);
} catch (error) {
console.error("All models failed:", error.message);
}
// ตรวจสอบสถานะสุขภาพของทุก Model
console.log("Health Status:", router.getHealthStatus());
}
main();
ข้อผิดพลาดที่พบบ่อยและวิธีแก้ไข
1. ปัญหา Timeout ไม่ทำงานตามที่ตั้งค่า
สาเหตุ: การตั้งค่า timeout ใน Python ด้วย requests.post(timeout=10) ไม่สามารถยกเลิก request ที่กำลังดำเนินอยู่ได้ทันที เมื่อ timeout ครบ แต่การเชื่อมต่อยังคงเปิดอยู่
# ❌ วิธีที่ผิด - timeout ไม่ยกเลิกการเชื่อมต่อจริง
response = requests.post(url, json=data, timeout=10)
✅ วิธีที่ถูก - ใช้ signal-based timeout หรือ session
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session_with_timeout():
session = requests.Session()
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[500, 502, 503, 504],
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
return session
หรือใช้วิธี Manual timeout control
import signal
def timeout_handler(signum, frame):
raise TimeoutError("Request took too long")
def call_with_real_timeout(url, data, timeout_seconds=10):
# ตั้งค่า signal handler
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout_seconds)
try:
response = requests.post(url, json=data)
signal.alarm(0) # ยกเลิก alarm
return response.json()
finally:
signal.alarm(0) # ทำความสะอาดในกรณี exception
หรือใช้ aiohttp สำหรับ async operations
import aiohttp
async def async_call_with_real_timeout(url, data, timeout_ms=10000):
timeout = aiohttp.ClientTimeout(total=timeout_ms / 1000)
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post(url, json=data) as response:
return await response.json()
2. ปัญหา Race Condition ใน Circuit Breaker
สาเหตุ: เมื่อมี requests หลายตัวพร้อมกันและ Circuit Breaker เปิดขึ้น state อาจไม่สอดคล้องกันระหว่าง threads
# ❌ วิธีที่ผิด - ไม่มีการล็อกเมื่อแก้ไข state
class BrokenCircuitBreaker:
def __init__(self):
self.state = "closed"
self.failure_count = 0
def record_failure(self):
self.failure_count += 1 # Race condition!
if self.failure_count >= 5:
self.state = "open"
✅ วิธีที่ถูก - ใช้ Lock สำหรับ thread safety
import threading
from enum import Enum
class CircuitState(Enum):
CLOSED = "closed"
OPEN = "open"
HALF_OPEN = "half_open"
class ThreadSafeCircuitBreaker:
def __init__(self, failure_threshold=5):
self._lock = threading.RLock()
self._state = CircuitState.CLOSED
self._failure_count = 0
self._failure_threshold = failure_threshold
self._last_failure_time = None
@property
def state(self):
with self._lock:
return self._state
def record_failure(self):
with self._lock:
self._failure_count += 1
self._last_failure_time = time.time()
if self._failure_count >= self._failure_threshold:
self._state = CircuitState.OPEN
print(f"Circuit opened after {self._failure_count} failures")
def record_success(self):
with self._lock:
if self._state == CircuitState.HALF_OPEN:
self._state = CircuitState.CLOSED
self._failure_count = 0
print("Circuit closed - service recovered")
def can_execute(self):
with self._lock:
if self._state == CircuitState.CLOSED:
return True