惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

N
Netflix TechBlog - Medium
I
InfoQ
Engineering at Meta
Engineering at Meta
Jina AI
Jina AI
Recent Announcements
Recent Announcements
T
The Blog of Author Tim Ferriss
P
Proofpoint News Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
D
Docker
Microsoft Security Blog
Microsoft Security Blog
宝玉的分享
宝玉的分享
Last Week in AI
Last Week in AI
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
GbyAI
GbyAI
博客园 - Franky
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
博客园 - 叶小钗
酷 壳 – CoolShell
酷 壳 – CoolShell
B
Blog RSS Feed
WordPress大学
WordPress大学
MyScale Blog
MyScale Blog
月光博客
月光博客
罗磊的独立博客

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
Timeouts and Circuit Breakers: Stop One Slow API From Tak...
Mean · 2026-06-16 · via DEV Community

When you call another service over HTTP, you are inheriting its worst day. If that dependency slows to a crawl, every request you make to it piles up, holds a connection, and eventually drags your service down with it. The fix is two old, unglamorous patterns: aggressive timeouts and a circuit breaker. Here is how to implement both in Node.js with no framework.

Step 1: Never make a request without a timeout

The single most common production incident is a missing timeout. By default, most HTTP clients will wait forever. One stalled dependency is enough to exhaust your connection pool.

async function fetchWithTimeout(url, { timeoutMs = 2000, ...options } = {}) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeoutMs);
  try {
    return await fetch(url, { ...options, signal: controller.signal });
  } finally {
    clearTimeout(timer);
  }
}

AbortController gives you a hard ceiling. If the dependency hasn't answered in 2 seconds, you fail fast and free the connection instead of letting it hang.

Step 2: Stop hammering a service that's already down

Timeouts protect a single request. But if a dependency is fully down, retrying every call still wastes 2 seconds each and keeps the pressure on. A circuit breaker tracks failures and, once they cross a threshold, "opens" — rejecting calls instantly without even trying the network. After a cooldown it lets one probe request through to see if the service has recovered.

class CircuitBreaker {
  constructor({ failureThreshold = 5, cooldownMs = 10000 } = {}) {
    this.failureThreshold = failureThreshold;
    this.cooldownMs = cooldownMs;
    this.failures = 0;
    this.state = 'CLOSED';      // CLOSED | OPEN | HALF_OPEN
    this.nextAttempt = 0;
  }

  async call(fn) {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        throw new Error('Circuit is OPEN — failing fast');
      }
      this.state = 'HALF_OPEN'; // time to test the waters
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (err) {
      this.onFailure();
      throw err;
    }
  }

  onSuccess() {
    this.failures = 0;
    this.state = 'CLOSED';
  }

  onFailure() {
    this.failures += 1;
    if (this.failures >= this.failureThreshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.cooldownMs;
    }
  }
}

Step 3: Wire them together

Now combine the timeout and the breaker. The breaker wraps the timed request, so a slow dependency counts as a failure and eventually trips the circuit.

const breaker = new CircuitBreaker({ failureThreshold: 5, cooldownMs: 10000 });

async function getUser(id) {
  return breaker.call(async () => {
    const res = await fetchWithTimeout(`https://api.example.com/users/${id}`, {
      timeoutMs: 2000,
    });
    if (!res.ok) throw new Error(`Upstream returned ${res.status}`);
    return res.json();
  });
}

The three states tell the whole story:

  • CLOSED — normal operation, requests flow through and failures are counted.
  • OPEN — too many failures; calls are rejected instantly for the cooldown window.
  • HALF_OPEN — one probe request is allowed; success closes the circuit, failure re-opens it.

Step 4: Always have a fallback

Failing fast is only useful if you have something to do with the failure. Degrade gracefully instead of returning a 500.

async function getUserSafe(id) {
  try {
    return await getUser(id);
  } catch (err) {
    // Serve stale cache, a default, or a partial response
    return { id, name: 'Unknown', degraded: true };
  }
}

A few rules that keep this honest

Set timeouts based on real latency percentiles, not a guess — start near the p99 of the dependency and tune. Keep one breaker per dependency, never a global one, or a single sick service will trip calls to healthy ones. And always log state transitions: an OPEN circuit is one of the highest-signal alerts you can have, because it tells you a dependency is failing before your users start complaining.

Timeouts and circuit breakers are about ten lines of code each, and together they turn a cascading outage into a localized, recoverable blip.


Testing resilience behavior by hand is tedious — you need to simulate slow responses, forced errors, and recovery. APIKumo makes it easy to mock failing and slow endpoints, script multi-step request flows, and replay them so you can watch your timeouts and breaker actually trip before you ship them to production.