🛠️ Practice: Simple Blog Networking Layer
Goal: Design the networking layer for a simple blog serving 1 million monthly readers globally, handling both static images and dynamic posts.
1. System Requirements & Estimations
Before designing the architecture, let's break down the requirements:
- Traffic: 1 million monthly readers ≈ 33,000 daily active users. Assuming 3 pages per user, that's ~100,000 page views per day (roughly 1.15 requests/second average, maybe 10-20 req/sec at peak).
- Content: Static assets (images, CSS, JS) and Dynamic content (blog post text, comments).
- Audience: Global distribution (latency matters).
2. High-Level Architecture
Here is the flow of a user requesting a blog post:
3. Component Breakdown
Let's explore the 5 main layers: DNS → CDN → Load Balancer → Web Servers → Database. We will explain what they do, show how they work visually, and write simple JavaScript code to understand the concepts.
1️⃣ DNS (Domain Name System)
- What it is: The "phonebook" of the internet. It turns human words (
myblog.com) into computer numbers (IP address192.168.1.50). - Why we need it: Users can't remember IP addresses. DNS tells their browser exactly which server to connect to.
💻 JavaScript Example: Simulating how a computer asks for an IP address.
const dns = require("dns").promises;
async function lookupDomain(domain) {
try {
// Ask the DNS server for the IP address
const records = await dns.resolve4(domain);
console.log(`The IP address for ${domain} is: ${records[0]}`);
} catch (error) {
console.error("Could not find the domain!", error);
}
}
lookupDomain("google.com");2️⃣ CDN (Content Delivery Network)
- What it is: A global network of servers that stores copies of your heavy files (like images, CSS, and JS) all around the world.
- Why we need it: If our main server is in New York, a user in Tokyo will wait a long time to download an image. A CDN serves the image from a server physically located in Tokyo, making it lightning fast.
💻 JavaScript Example: How a CDN conceptually decides to serve a file.
async function handleCDNRequest(request) {
const fileUrl = request.url;
// 1. Check if the local CDN edge has the file
let file = await localEdgeCache.get(fileUrl);
if (file) {
console.log("⚡ Cache Hit! Serving fast from the local city.");
return file;
}
// 2. Cache Miss: Fetch from the faraway Main Server
console.log("🐌 Cache Miss! Fetching from the faraway Main Server...");
file = await fetchFromMainServer(fileUrl);
// 3. Save a copy in the local edge cache for the next user
await localEdgeCache.set(fileUrl, file, { expireInMinutes: 60 });
return file;
}3️⃣ Load Balancer (Reverse Proxy)
- What it is: A traffic cop that stands in front of your web servers.
- Why we need it: If 10,000 people visit at once, one server might crash. The Load Balancer spreads the traffic out evenly across multiple servers so no single server gets overwhelmed.
💻 JavaScript Example: A simple Round-Robin Load Balancer.
const servers = ["Server-A", "Server-B", "Server-C"];
let currentIndex = 0;
function routeTraffic() {
// Get the current server and move the pointer to the next one
const selectedServer = servers[currentIndex];
currentIndex = (currentIndex + 1) % servers.length; // Loop back to 0
return selectedServer;
}
console.log("Sending user to:", routeTraffic()); // Server-A
console.log("Sending user to:", routeTraffic()); // Server-B
console.log("Sending user to:", routeTraffic()); // Server-C4️⃣ Web Servers
- What it is: The brain of the operation. This is where your backend code lives (Node.js, Express, Python, etc.).
- Why we need it: It looks at what the user wants, asks the Database for the blog post text, formats it into HTML or JSON, and sends it back to the user.
💻 JavaScript Example: A simple Express.js Web Server.
const express = require("express");
const app = express();
app.get("/post/:id", async (req, res) => {
const postId = req.params.id;
try {
// The web server asks the database for the post data
const postData = await database.query(`SELECT * FROM posts WHERE id = ?`, [
postId,
]);
// Sends the data back to the user's browser
res.json({ success: true, data: postData });
} catch (error) {
res.status(500).send("Oops! Server Error.");
}
});
app.listen(3000, () => console.log("Web server is awake on port 3000"));5️⃣ Database & Cache
- What it is: The filing cabinet. It permanently stores your blog posts, user comments, and likes. (e.g., PostgreSQL or MySQL).
- Why we need it: Web servers are "stateless" (they forget everything when they restart). The database keeps data safe forever.
- Optimization: Since a blog is "read-heavy" (people read a lot more than they write), querying the database every single time is slow. We use a fast memory cache like Redis to speed up database reads.
💻 JavaScript Example: The "Cache-Aside" pattern (Read-Heavy Optimization).
const redis = require("redis");
const db = require("./my-database");
const cache = redis.createClient();
async function getBlogPost(postId) {
// 1. Fast Path: Check the ultra-fast Redis Cache first
const cachedPost = await cache.get(`post:${postId}`);
if (cachedPost) {
console.log("⚡ Retrieved from Cache!");
return JSON.parse(cachedPost);
}
// 2. Cache Miss: Query the slower SQL Database
console.log("🐌 Fetching from Database...");
const row = await db.query("SELECT title, body FROM posts WHERE id = ?", [
postId,
]);
if (row) {
// 3. Save the result in Redis for 5 minutes (300 seconds) so the next read is fast
await cache.setEx(`post:${postId}`, 300, JSON.stringify(row));
}
return row;
}🎯 Summary
By combining a CDN for global image delivery and a Load Balancer with multiple Web Servers for redundancy, this architecture easily handles 1 million monthly readers while remaining fast for users across the globe. Adding Redis ensures the database never crashes from too many people reading the same viral post.
