Cache dan batas batas dengan redis dan next.js – Beragampengetahuan
Sebagai seseorang yang suka mengangkut produk dan alat untuk bereksperimen dan digunakan orang, itu hanya masalah waktu, itu hanya masalah skalabilitas yang saya miliki. Saya selalu tahu apa artinya penskalaan dan bagaimana skala secara teoritis. Saya akrab dengan penskalaan vertikal dan horizontal dan kompleksitas setiap penskalaan, tetapi secara teoritis. Sebenarnya, saya tidak perlu menerapkan salah satu dari mereka karena basis pengguna saya biasanya kecil.
Sampai saya membangun keran yang melebihi ekspektasi penggunaan. Namun, traksi besar ini telah menyebabkan serangkaian masalahnya sendiri. Masalahnya adalah bahwa meskipun saya membatasi frekuensi dana alamat dompet sekali sehari, pengguna dapat dengan mudah memutar alamat baru dan terus mengklaim. Ini menyebabkan keran saya sering mengalir.
Untuk memperbaikinya, saya harus memperkenalkan tingkat batas untuk membatasi alamat IP setiap penyalahgunaan pengguna dari keran. Sekali lagi, di platform LMS yang saya bangun, saya harus memperkenalkan cache untuk mengurangi jumlah panggilan ke database.
Dalam tutorial ini, saya akan menunjukkan kepada Anda cara mengimplementasikan pembatasan dan caching tingkat dalam aplikasi Anda menggunakan Redis.
Contents
Mengapa Redis?
Redis seperti teman yang dapat diandalkan itu, dia pandai dalam segala hal. Apakah Anda memerlukan pengambilan data petir? Redis. Apakah Anda perlu berkoordinasi antara beberapa server? Redis. Perlu membatasi seberapa sering pengguna dapat melakukan operasi? Anda dapat menebaknya dengan benar – Reddy.
Kami dapat mensembakan respons API, batas laju lacak, dan sering membuat aplikasi kami lebih tangguh. Bagian terbaik? Setelah Anda memahami polanya, implementasinya sebenarnya sangat sederhana. Setelah mengatakan itu, mari kita mulai membangun!
Prasyarat
- Siapkan akun cloud redis dan dapatkan URL publik
- Akrab dengan next.js
Apa yang kita bangun?

Kami akan membuat aplikasi Next.js menggunakan metode berikut
- API Katalog Produk dengan Cache Repower
- Sistem pembatas laju laju fleksibel menggunakan redis
- Halaman demo interaktif untuk memvisualisasikan dua teknologi
Berikut adalah pratinjau penampilan struktur proyek kami:
/src
/lib
- redis.ts # Redis client and caching utilities
- edge-rate-limiter.ts # Edge-compatible rate limiting
- product.ts # Product data service
/app
/api
/products # Product data API endpoints
/cache/invalidate # Cache management endpoint
/tests # Test endpoints for rate limiting
/demo # Demo UI pages
/caching # Redis caching demo
/rate-limiting # Rate limiting demo
- middleware.ts # Rate limiting middleware
Langkah 1: Siapkan lingkungan kita
Pertama -tama mari kita buat proyek Next.js baru.
npx create-next-app redis-scaling-demo
cd redis-scaling-demo
Untuk prompt yang menyertainya, pilih opsi default dan klik ENTER. Selanjutnya, kami menginstal dependensi dan jenis Redis.
npm install ioredis @upstash/ratelimit @upstash/redis @types/ioredis --save-dev
Jika Anda belum memiliki akun cloud redis, kunjungi Redisbuat akun baru dan pilih database gratis.

Cari tombol di atas dan klik untuk memilih opsi CLI. Salin perintah yang berisi host dan port Anda.
redis://default:password@host:port
Ini harus terlihat seperti format di atas (Catatan: Abaikan “redis -cli -u” dalam perintah). Akhirnya, ganti kata sandi (tanda bintang dalam perintah) dengan kata sandi Anda yang sebenarnya, yang bisa Anda dapatkan dengan pergi ke bagian keamanan dan menyalin kata sandi.
Akhirnya, buat a .env File dalam file (dengan asumsi Anda telah membukanya di terminal kode) dan menempelkannya ke dalam url redis:
REDIS_URL=redis://default:password@host:port
Langkah 2: Buat Layanan
ada src Folder, buat folder baru yang disebut lib Lalu ada file yang dipanggil redis.ts. Tempel dalam kode di bawah ini.
import Redis from "ioredis";
let redisClient: Redis | null = null;
export function getRedisClient()
if (!redisClient)
redisClient = new Redis(process.env.REDIS_URL
return redisClient;
function createCacheKey(prefix: string, key: string): string
return `$prefix$key`;
async function getCachedData<T>(
redis: Redis,
prefix: string,
key: string
): Promise<T | null>
const cachedData = await redis.get(createCacheKey(prefix, key));
if (!cachedData)
return null;
try
return JSON.parse(cachedData) as T;
catch (error)
console.error(`Error parsing cached data for key $key:`, error);
return null;
async function setCachedData<T>(
redis: Redis,
prefix: string,
key: string,
data: T,
ttl: number
): Promise<void>
await redis.set(createCacheKey(prefix, key), JSON.stringify(data), "EX", ttl);
async function deleteCachedData(
redis: Redis,
prefix: string,
key: string
): Promise<void>
await redis.del(createCacheKey(prefix, key));
async function invalidateCachePattern(
redis: Redis,
prefix: string,
pattern: string
): Promise<number>
const keys = await redis.keys(`$prefix$pattern*`);
if (keys.length === 0)
return 0;
return await redis.del(...keys);
export function getCacheInstance(
prefix: string = "cache:",
defaultTTL: number = 60 * 5
)
const redis = getRedisClient();
return defaultTTL),
delete: (key: string) => deleteCachedData(redis, prefix, key),
invalidatePattern: (pattern: string) =>
invalidateCachePattern(redis, prefix, pattern),
;
Next.js Middleware berjalan pada runtime Edge, dan memiliki keterbatasan dibandingkan dengan Node.js. Kita perlu membuat laju pembatas yang bekerja di lingkungan ini, jadi lib folder, buat yang lain dipanggil edge-rate-limiter.ts.
import NextRequest, NextResponse from "next/server";
// Simple in-memory store (note: this won't work in a multi-instance setup)
const ipRequests = new Map<string, count: number; timestamp: number >();
export function createEdgeRateLimiter(options:
limit: number;
timeWindow: number;
)
return function rateLimit(request: NextRequest) count: 0, timestamp: now ;
if (currentData.timestamp < windowStart)
currentData.count = 0;
currentData.timestamp = now;
currentData.count++;
ipRequests.set(ip, currentData);
const response = NextResponse.next();
response.headers.set("X-RateLimit-Limit", options.limit.toString());
response.headers.set(
"X-RateLimit-Remaining",
Math.max(0, options.limit - currentData.count).toString()
);
if (currentData.count > options.limit)
return NextResponse.json( error: "Too many requests" , status: 429 );
return response;
;
Untuk membatasi tarif, kami membuat a rate-limiter.ts File dalam file lib Map.
import NextRequest, NextResponse from "next/server";
import getRedisClient from "./redis";
import Redis from "ioredis";
export interface RateLimitConfig
maxRequests: number;
windowSizeInSeconds: number;
prefix: string;
function getRedisKey(prefix: string, identifier: string): string
return `$prefix:$identifier`;
export async function checkRateLimit(
redis: Redis,
config: RateLimitConfig,
identifier: string
): Promise<
success: boolean;
limit: number;
remaining: number;
reset: number;
>
export function getClientIdentifier(req: NextRequest): string forwardedFor
export function createRateLimitMiddleware(config: RateLimitConfig) {
return async function rateLimitMiddleware(
req: NextRequest
): Promise<NextResponse> {
try
const redis = getRedisClient();
const identifier = getClientIdentifier(req);
const success, limit, remaining, reset = await checkRateLimit(
redis,
config,
identifier
);
const headers =
"X-RateLimit-Limit": limit.toString(),
"X-RateLimit-Remaining": remaining.toString(),
"X-RateLimit-Reset": reset.toString(),
;
if (!success)
return NextResponse.json(
error: "Too Many Requests",
message: `Rate limit of $limit requests per $config.windowSizeInSeconds seconds exceeded.`,
,
status: 429,
headers:
...headers,
"Retry-After": Math.ceil(
reset - Math.floor(Date.now() / 1000)
).toString(),
,
);
const response = NextResponse.next();
Object.entries(headers).forEach(([key, value]) =>
response.headers.set(key, value);
);
return response;
catch (error)
console.error("Rate limit error:", error);
return NextResponse.next();
};
}
export const rateLimiters =
default: createRateLimitMiddleware(
maxRequests: 60,
windowSizeInSeconds: 60,
prefix: "ratelimit:default",
),
auth: createRateLimitMiddleware(
maxRequests: 10,
windowSizeInSeconds: 60,
prefix: "ratelimit:auth",
),
sensitive: createRateLimitMiddleware(
maxRequests: 3,
windowSizeInSeconds: 60,
prefix: "ratelimit:sensitive",
),
;
Akhirnya, kami membuat layanan produk untuk mensimulasikan database. Jadi, buat a product.ts File dalam file lib Map.
export interface Product
id: string;
name: string;
description: string;
price: number;
category: string;
image: string;
stock: number;
const productData: Product[] = [
id: "prod_001",
name: "Ergonomic Office Chair",
description: "A comfortable chair designed for long working hours",
price: 299.99,
category: "furniture",
image: "/images/chair.jpg",
stock: 25,
,
id: "prod_002",
name: "Mechanical Keyboard",
description: "Mechanical keyboard with RGB lighting and Cherry MX switches",
price: 129.99,
category: "electronics",
image: "/images/keyboard.jpg",
stock: 40,
,
id: "prod_003",
name: "Wireless Mouse",
description: "High-precision wireless mouse with long battery life",
price: 49.99,
category: "electronics",
image: "/images/mouse.jpg",
stock: 75,
,
id: "prod_004",
name: "Adjustable Standing Desk",
description: "Electric adjustable standing desk for better ergonomics",
price: 499.99,
category: "furniture",
image: "/images/desk.jpg",
stock: 15,
,
id: "prod_005",
name: "Monitor Stand",
description: "Adjustable monitor stand with cable management",
price: 79.99,
category: "accessories",
image: "/images/monitor-stand.jpg",
stock: 30,
,
id: "prod_006",
name: "USB-C Hub",
description: "7-in-1 USB-C hub with HDMI, SD card reader, and USB ports",
price: 39.99,
category: "accessories",
image: "/images/usb-hub.jpg",
stock: 50,
,
id: "prod_007",
name: "Noise-Cancelling Headphones",
description: "Over-ear headphones with active noise cancellation",
price: 199.99,
category: "electronics",
image: "/images/headphones.jpg",
stock: 20,
,
id: "prod_008",
name: "Ergonomic Mouse Pad",
description: "Mouse pad with wrist support for ergonomic comfort",
price: 19.99,
category: "accessories",
image: "/images/mousepad.jpg",
stock: 100,
,
];
export async function getAllProducts(): Promise<Product[]>
await new Promise((resolve) => setTimeout(resolve, 300));
return [...productData];
export async function getProductById(id: string): Promise<Product | null>
export async function getProductsByCategory(
category: string
): Promise<Product[]>
await new Promise((resolve) => setTimeout(resolve, 300));
return productData.filter((product) => product.category === category);
export async function searchProducts(query: string): Promise<Product[]>
await new Promise((resolve) => setTimeout(resolve, 400));
const searchTerm = query.toLowerCase();
return productData.filter(
(product) =>
product.name.toLowerCase().includes(searchTerm)
export const productService =
getAllProducts,
getProductById,
getProductsByCategory,
searchProducts,
;
Langkah 3: Batas Batas – Middleware dan Tes API
Kami akan membuat middleware untuk pembatasan suku bunga, yang pada dasarnya akan melakukan tiga hal untuk kami:
- Buat tiga pembatas berbeda dengan batas yang berbeda
- Terapkan mereka sesuai dengan pola jalur URL
- Gunakan pencocokan untuk menjalankan rute API saja
Buat a middleware.ts mengajukan src folder dan tempel dalam kode di bawah ini.
import NextRequest, NextResponse from "next/server";
import createEdgeRateLimiter from "./lib/edge-rate-limiter";
// Create rate limiters with different settings
const rateLimiters =
default: createEdgeRateLimiter( limit: 60, timeWindow: 60 ), // 60 requests per minute
auth: createEdgeRateLimiter( limit: 10, timeWindow: 60 ), // 10 requests per minute
sensitive: createEdgeRateLimiter( limit: 3, timeWindow: 60 ), // 3 requests per minute
;
export function middleware(request: NextRequest)
const path = request.nextUrl.pathname;
// Apply different rate limits based on path
if (path.startsWith("/api/tests/admin"))
return rateLimiters.sensitive(request);
else if (path.startsWith("/api/tests/auth"))
return rateLimiters.auth(request);
else if (path.startsWith("/api/"))
return rateLimiters.default(request);
return NextResponse.next();
export const config =
matcher: ["/api/:path*"],
;
Untuk titik akhir uji untuk menunjukkan batas laju yang berbeda, buat folder yang disebut api ada app Daftar isi. Kemudian buat folder lain yang disebut tests. Di sinilah titik akhir tes kami berada. Di folder tes, buat tiga subfolder: admin,,,,, auth Dan basic masing -masing. Untuk masing -masing subfolder ini, buat a route.ts dokumen.
ada app/api/tests/admin/route.ts File, tempel kode berikut:
import NextRequest, NextResponse from "next/server";
export function GET(request: NextRequest)
return NextResponse.json(
message: "Admin rate limited endpoint (very strict)",
timestamp: new Date().toISOString(),
path: "/api/tests/admin",
);
Demikian pula, di auth Daftar isi:
import NextRequest, NextResponse from "next/server";
export function GET(request: NextRequest)
return NextResponse.json(
message: "Auth rate limited endpoint (moderate)",
timestamp: new Date().toISOString(),
path: "/api/tests/auth",
);
Akhirnya, di basic Daftar isi:
import NextRequest, NextResponse from "next/server";
export function GET(request: NextRequest)
return NextResponse.json(
message: "Default rate limited endpoint",
timestamp: new Date().toISOString(),
path: "/api/tests/basic",
);
Langkah 4: Cache – Bangun API Produk
Sekarang, kita dapat terus membuat rute API untuk caching. ada app/api Folder, buat folder baru yang disebut products. Di folder ini, buat subfolder bernama categories dan file yang dipanggil route.ts. Di file routing, tempel dalam kode berikut
import NextRequest, NextResponse from "next/server";
import productService from "@/lib/product";
import getCacheInstance from "@/lib/redis";
// GET /api/products - List products with optional filtering
export async function GET(request: NextRequest)
try
// Parse query parameters
const searchParams = request.nextUrl.searchParams;
const category = searchParams.get("category");
const query = searchParams.get("q");
let cacheKey = "products";
if (category) cacheKey += `:category:$category`;
if (query) cacheKey += `:search:$query`;
const cache = getCacheInstance("api:");
const cachedData = await cache.get(cacheKey);
if (cachedData)
console.log(`Cache hit for $cacheKey`);
const response = NextResponse.json(cachedData);
response.headers.set("X-Cache-Status", "HIT");
return response;
console.log(`Cache miss for $cacheKey, fetching from database...`);
let products;
if (query)
products = await productService.searchProducts(query);
else if (category)
products = await productService.getProductsByCategory(category);
else
products = await productService.getAllProducts();
const responseData =
products,
count: products.length,
timestamp: new Date().toISOString(),
;
await cache.set(cacheKey, responseData, 60 * 5);
return NextResponse.json(responseData);
catch (error)
console.error("Error fetching products:", error);
return NextResponse.json(
error: "Failed to fetch products" ,
status: 500
);
Di subfolder categoriesbuat satu lagi route.ts File dan tempel berikut:
import NextRequest, NextResponse from "next/server";
import productService from "@/lib/product";
import getCacheInstance from "@/lib/redis";
import Product from "@/lib/product";
export async function GET(request: NextRequest)
try
const cacheKey = "product:categories";
const cache = getCacheInstance("api:");
const cachedCategories = await cache.get(cacheKey);
if (cachedCategories)
console.log(`Cache hit for $cacheKey`);
return NextResponse.json(cachedCategories);
console.log(`Cache miss for $cacheKey, computing categories...`);
const products = await productService.getAllProducts();
const categories = Array.from(
new Set(products.map((product: Product) => product.category))
);
const responseData =
categories,
count: categories.length,
timestamp: new Date().toISOString(),
;
await cache.set(cacheKey, responseData, 60 * 60);
return NextResponse.json(responseData);
catch (error)
console.error("Error fetching categories:", error);
return NextResponse.json(
error: "Failed to fetch categories" ,
status: 500
);
Selanjutnya, kami api Panggilan direktori cache Di dalamnya, subfolder disebut invalidate Dan route.ts dokumen.
import NextRequest, NextResponse from "next/server";
import getCacheInstance from "@/lib/redis";
export async function POST(request: NextRequest)
try
const pattern = await request.json();
if (!pattern)
return NextResponse.json(
error: "Pattern is required" ,
status: 400
);
const cache = getCacheInstance("api:");
const deletedCount = await cache.invalidatePattern(pattern);
return NextResponse.json(
success: true,
message: `Invalidated $deletedCount cache entries`,
pattern,
);
catch (error)
console.error("Error invalidating cache:", error);
return NextResponse.json(
error: "Failed to invalidate cache" ,
status: 500
);
Langkah 5: UI – Bangun demonstrasi tingkat batas UI
ada app Direktori, buat folder bernama demo dan dua subfolder caching Dan rate-limiting masing -masing. Di subfolder pembatas tingkat, buat a page.tsx File dan tempel di bawah ini.
"use client";
import useState from "react";
import Link from "next/link";
export default function RateLimitingDemo() {
const [requests, setRequests] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [requestCount, setRequestCount] = useState(5);
const [endpoint, setEndpoint] = useState("/api/tests/basic");
async function makeRequests() {
setLoading(true);
setRequests([]);
const newRequests = [];
const promises = Array.from( length: requestCount , (_, i) => {
return fetch(endpoint)
.then((response) =>
const headers =
status: response.status,
limit: response.headers.get("X-RateLimit-Limit"),
remaining: response.headers.get("X-RateLimit-Remaining"),
reset: response.headers.get("X-RateLimit-Reset"),
;
return response
.json()
.then((data) =>
return id: i + 1, headers, data, success: response.ok ;
)
.catch(() =>
return
id: i + 1,
headers,
data: error: "Failed to parse JSON" ,
success: false,
;
);
)
.catch((error) =>
return
id: i + 1,
headers: ,
data: error: error.message ,
success: false,
;
);
});
const results = await Promise.all(promises);
setRequests(results);
setLoading(false);
}
return (
<div className="min-h-screen p-8 bg-black text-black ">
<div className="max-w-5xl mx-auto">
<div className="mb-6">
<Link href=" className="text-blue-600 hover:underline">
← Back to home
</Link>
</div>
<h1 className="text-3xl font-bold mb-6">Redis Rate Limiting Demo</h1>
<div className="bg-white p-6 rounded-lg shadow-md mb-8">
<h2 className="text-xl font-semibold mb-4">How It Works</h2>
<p className="mb-4">
This demo shows Redis-based rate limiting in action. We've
configured the API to limit requests based on client IP address.
</p>
<p>
Try making multiple requests at once to see how the rate limiter
kicks in after exceeding the allowed limit.
</p>
</div>
<div className="bg-white p-6 rounded-lg shadow-md mb-8">
<h2 className="text-xl font-semibold mb-4">Test Rate Limiting</h2>
<div className="flex flex-col md:flex-row gap-4 mb-6">
<div className="flex-1">
<label
htmlFor="requestCount"
className="block text-sm font-medium text-gray-700 mb-1"
>
Number of Requests
</label>
<input
type="number"
id="requestCount"
min="1"
max="20"
value=requestCount
onChange=(e) => setRequestCount(parseInt(e.target.value))
className="w-full p-2 border border-gray-300 rounded-md"
/>
</div>
<div className="flex-1">
<label
htmlFor="endpoint"
className="block text-sm font-medium text-gray-700 mb-1"
>
API Endpoint
</label>
<select
id="endpoint"
value=endpoint
onChange=(e) => setEndpoint(e.target.value)
className="w-full p-2 border border-gray-300 rounded-md"
>
<option value="/api/tests/basic">
Default Rate Limit (60/min)
</option>
<option value="/api/tests/auth">
Auth Rate Limit (10/min)
</option>
<option value="/api/tests/admin">
Admin Rate Limit (3/min)
</option>
</select>
</div>
<div className="flex-1 flex items-end">
<button
onClick=makeRequests
disabled=loading
className="w-full bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700 disabled:bg-blue-300"
>
loading ? "Sending Requests..." : "Send Requests"
</button>
</div>
</div>
</div>
requests.length > 0 && (
<div className="bg-white p-6 rounded-lg shadow-md">
<h2 className="text-xl font-semibold mb-4">Request Results</h2>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
#
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Rate Limit
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Remaining
</th>
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Response
</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
requests.map((request) => (
<tr key=request.id>
<td className="px-6 py-4 whitespace-nowrap">
request.id
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span
className=`px-2 inline-flex text-xs leading-5 font-semibold rounded-full $
request.success
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
`
>
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap">
</td>
<td className="px-6 py-4 whitespace-nowrap">
"-"
</td>
<td className="px-6 py-4">
<pre className="text-xs bg-gray-50 p-2 rounded overflow-x-auto">
JSON.stringify(request.data, null, 2)
</pre>
</td>
</tr>
))
</tbody>
</table>
</div>
</div>
)
</div>
</div>
);
}
Langkah 6: UI – Bangun Demo Cache UI
ada demo/caching Folder, buat a page.tsx dan salin kode berikut.
"use client";
import useState, useEffect from "react";
import Link from "next/link";
import Product from "@/lib/product";
export default function CachingDemo()
const [products, setProducts] = useState<Product[]>([]);
const [loading, setLoading] = useState(false);
const [responseTime, setResponseTime] = useState(0);
const [cacheStatus, setCacheStatus] = useState("");
const [category, setCategory] = useState("");
const [categories, setCategories] = useState<string[]>([]);
useEffect(() =>
async function fetchCategories()
const response = await fetch("/api/products/categories");
const data = await response.json();
setCategories(data.categories);
fetchCategories();
, []);
async function fetchProducts()
setLoading(true);
setCacheStatus("");
const startTime = performance.now();
let url = "/api/products";
if (category)
url += `?category=$category`;
const response = await fetch(url);
const data = await response.json();
const endTime = performance.now();
setProducts(data.products);
setResponseTime(endTime - startTime);
setCacheStatus(endTime - startTime < 500 ? "HIT" : "MISS");
setLoading(false);
async function invalidateCache()
setLoading(true);
const response = await fetch("/api/cache/invalidate",
method: "POST",
headers:
"Content-Type": "application/json",
,
body: JSON.stringify(
pattern: "products",
),
);
const data = await response.json();
alert(`Cache invalidated: $data.message`);
setLoading(false);
fetchProducts();
return (
<div className="min-h-screen p-8 bg-black text-black">
<div className="max-w-5xl mx-auto">
<div className="mb-6">
<Link href=" className="text-blue-600 hover:underline">
← Back to home
</Link>
</div>
<h1 className="text-3xl font-bold mb-6">Redis Caching Demo</h1>
<div className="bg-white p-6 rounded-lg shadow-md mb-8">
<h2 className="text-xl font-semibold mb-4">How It Works</h2>
<p className="mb-4">
This demo shows Redis caching in action. When you fetch products,
the first request will be slow as it goes to the database.
Subsequent requests will be fast as they come from the Redis cache.
</p>
<p>
Try fetching products multiple times to see the difference in
response time between cache misses and hits.
</p>
</div>
<div className="bg-white p-6 rounded-lg shadow-md mb-8">
<h2 className="text-xl font-semibold mb-4">Test Caching</h2>
<div className="flex flex-col md:flex-row gap-4 mb-6">
<div className="flex-1">
<label
htmlFor="category"
className="block text-sm font-medium text-gray-700 mb-1"
>
Filter by Category
</label>
<select
id="category"
value=category
onChange=(e) => setCategory(e.target.value)
className="w-full p-2 border border-gray-300 rounded-md"
>
<option value="">All Categories</option>
categories.map((cat) => (
<option key=cat value=cat>
cat.charAt(0).toUpperCase() + cat.slice(1)
</option>
))
</select>
</div>
<div className="flex-1 flex items-end">
<button
onClick=fetchProducts
disabled=loading
className="w-full bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700 disabled:bg-blue-300"
>
loading ? "Loading..." : "Fetch Products"
</button>
</div>
<div className="flex-1 flex items-end">
<button
onClick=invalidateCache
disabled=loading
className="w-full bg-red-600 text-white py-2 px-4 rounded hover:bg-red-700 disabled:bg-red-300"
>
Invalidate Cache
</button>
</div>
</div>
responseTime > 0 && (
<div className="mb-4 p-4 bg-gray-100 rounded-md">
<div className="flex justify-between items-center">
<p className="text-lg">
Response Time:" "
<span className="font-bold">responseTime.toFixed(2)ms</span>
</p>
<p className="text-lg">
Cache:" "
<span
className=`font-bold $
cacheStatus === "HIT"
? "text-green-600"
: "text-yellow-600"
`
>
cacheStatus
</span>
</p>
</div>
</div>
)
</div>
products.length > 0 && (
<div className="bg-white p-6 rounded-lg shadow-md">
<h2 className="text-xl font-semibold mb-4">
Products (products.length)
</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
products.map((product) => (
<div key=product.id className="border rounded-md p-4">
<h3 className="font-medium text-lg mb-1">product.name</h3>
<p className="text-gray-500 text-sm mb-2">
product.category
</p>
<p className="font-bold text-lg mb-2">
$product.price.toFixed(2)
</p>
<p className="text-sm text-gray-700">product.description</p>
</div>
))
</div>
</div>
)
</div>
</div>
);
Uji aplikasi kami
PH! Itu banyak kode, tetapi kita bisa mengujinya pada akhirnya. Hal pertama adalah menjalankan server pengembangan kami.
Fungsi Uji:
1. Demo cache
- Klik Demo Cache.
- Pilih kategori dan klik dapatkan produk.
- Perhatikan waktu respons.
- Klik Dapatkan Produk lagi dan amati waktu respons yang lebih cepat, serta klik cache dan kehilangan.
- Coba klik cache yang tidak valid dan dapatkan produk lagi.

Waktu respons awal adalah 2004,50 ms (tanpa cache).

Waktu respons cache – 285.50ms!
2. Demonstrasi batas kecepatan
- Klik pada demo batas kecepatan.
- Pilih titik akhir batas tingkat manajemen.
- Tetapkan jumlah permintaan menjadi lima.
- Klik Kirim Permintaan.
- Amati bagaimana tiga permintaan pertama berhasil, tetapi dua permintaan berikutnya gagal dengan kesalahan 429.
- Coba gunakan titik akhir yang berbeda dengan batas laju yang berbeda.

Batas pengeluaran administrasi hanya memungkinkan satu permintaan per menit. Anda dapat melihat bahwa tiga permintaan pertama berhasil, sementara dua lainnya gagal.

Ketika kami membuat permintaan lagi dalam menit yang sama, kami menemukan bahwa semua permintaan gagal.
Bagaimana semuanya bekerja
Mari kita tinjau bagaimana komponen yang berbeda dari suatu aplikasi berinteraksi satu sama lain:
Aliran cache
- Pengguna memilih kategori dan mengklik “Dapatkan Produk” di UI.
- Klien mengirimkan permintaan ke /API /Produk? Kategori = xyz.
- Routing API menggunakan getCacheInstance () untuk memeriksa apakah Redis memiliki respons yang di -cache.
- Jika hit cache terjadi:
- Data yang di-cache memiliki status x-cache: judul hit
- Respons yang sangat cepat (biasanya di bawah 50ms)
- Jika kerusakan cache terjadi:
- Data diperoleh dari layanan produk
- Data disimpan di Redis dengan 5 menit TTL
- Respons pengembalian (lebih lambat, biasanya 300-1000ms)
- UI menampilkan produk dan menampilkan waktu respons
Aliran batas tingkat
- Pengguna memilih titik akhir dan jumlah permintaan dan klik Kirim Permintaan.
- Klien mengirimkan beberapa permintaan ke titik akhir yang dipilih
- Untuk setiap permintaan, middleware mencegat permintaan dan memanggil limiter tarif yang sesuai
- Tingkat limiter:
- Melacak jumlah permintaan alamat IP klien
- Tambahkan header batas tingkat ke respons
- Jika batas terlampaui, itu akan mengembalikan kesalahan 429
- UI menampilkan hasil, menampilkan kode status, permintaan yang tersisa dan data respons
sebagai kesimpulan
Berikut ini adalah demonstrasi tentang cara menerapkan cache dan laju batas menggunakan Redis. Ini adalah tutorial yang panjang, tetapi tujuannya adalah menjadi sedetail mungkin, jadi mudah dimengerti. Anda juga dapat menemukan proyek lengkap di sini. Terima kasih telah membaca!
rencana pengembangan website
metode pengembangan website
jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website
#Cache #dan #batas #batas #dengan #redis #dan #next.js