تصميم محدِّد معدل الطلبات وتصميم موجز Twitter
طبّق الإطار على مسألتين نموذجيتين في التصميم: تحديد معدل الطلبات باستخدام token-bucket أو sliding-window، وتصميم موجز أخبار يعتمد على fan-out-on-write مقابل fan-out-on-read.
تصميم محدِّد معدل الطلبات وتصميم موجز Twitter درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.
لماذا يُعد تحديد معدل الطلبات أساسيًا
يتحكم تحديد معدل الطلبات في عدد الطلبات التي يمكن للعميل إرسالها إلى API خلال نافذة زمنية محددة. ومن دونه، يمكن لعميل واحد يتصرف بشكل سيئ (أو لهجوم DDoS) أن يستنفد موارد الخادم، مما يضعف الخدمة لجميع المستخدمين. كما يحمي تحديد معدل الطلبات من هجمات التخمين بالقوة الغاشمة، ويمنع كشط API، ويفرض الاستخدام العادل للموارد المشتركة.
تشمل مستويات تطبيق تحديد معدل الطلبات الشائعة: حسب معرّف المستخدم، أو مفتاح API، أو عنوان IP، أو نقطة النهاية، أو مزيجًا منها. ومن الحدود المعتادة: 100 طلب في الدقيقة لكل مستخدم، و1000 طلب في الساعة لكل مفتاح API. يجب أن يكون محدِّد معدل الطلبات سريعًا (بإضافة حمل قدره <1ms) وموزعًا (ومتسقًا عبر جميع نسخ خوادم API).
# Rate limiting scenarios
use_cases = [
('API authentication endpoint', '5 attempts per 15 min per IP', 'Brute-force protection'),
('Public search API', '100 requests per minute per key', 'Fair use enforcement'),
('Email sending', '50 emails per hour per user', 'Spam prevention'),
('Payment processing', '10 transactions per second per account', 'Fraud prevention'),
('File upload', '5 uploads per minute per user', 'Resource quota'),
('Notification service', '1000 pushes per second globally', 'Cost control'),
]
print(f'{'Endpoint/Feature':35s} {'Limit':40s} {'Reason'}')
print('-'*95)
for endpoint, limit, reason in use_cases:
print(f'{endpoint:35s} {limit:40s} {reason}')خوارزمية تحديد معدل الطلبات 1: دلو الرموز
تحافظ خوارزمية دلو الرموز على دلو بسعة قصوى قدرها N من الرموز. وتُضاف الرموز بمعدل ثابت (مثلًا، 10 رموز في الثانية). يستهلك كل طلب رمزًا واحدًا. إذا كان الدلو فارغًا، يُرفض الطلب. وإذا كانت السعة غير مكتملة، يُقبل الطلب ويُستهلك الرمز.
تتيح خوارزمية دلو الرموز الدفعات (bursting): فإذا لم ترد طلبات لمدة 5 ثوانٍ، يمتلئ الدلو بما يصل إلى N رمزًا، ثم يمكن استقبال N طلبات فورًا. وهذا مناسب لواجهات API التي تكون فيها الدفعات العرضية مقبولة. والمعاملان هما السعة (حجم الدفعة) ومعدل إعادة التعبئة.
import time
class TokenBucket:
def __init__(self, capacity, refill_rate):
self.capacity = capacity # max tokens (burst size)
self.refill_rate = refill_rate # tokens added per second
self.tokens = capacity # start full
self.last_refill = time.time()
def allow(self):
now = time.time()
elapsed = now - self.last_refill
# Refill tokens based on elapsed time
self.tokens = min(self.capacity,
self.tokens + elapsed * self.refill_rate)
self.last_refill = now
if self.tokens >= 1:
self.tokens -= 1
return True # request allowed
return False # rate limited
bucket = TokenBucket(capacity=5, refill_rate=2) # 2 tokens/sec, burst=5
for i in range(8):
allowed = bucket.allow()
print(f'Request {i+1}: {"ALLOWED" if allowed else "REJECTED"} (tokens={bucket.tokens:.1f})')
time.sleep(0.1) # 0.1s between requestsخوارزمية تحديد معدل الطلبات 2: سجل النافذة المنزلقة
يخزّن سجل النافذة المنزلقة طابعًا زمنيًا لكل طلب في مجموعة مرتبة. ومع كل طلب جديد، أزل الطوابع الزمنية الأقدم من بداية النافذة، ثم تحقق مما إذا كان عدد الطوابع المتبقية أقل من الحد. إذا كان كذلك، فأضف الطابع الزمني الحالي واسمح بالطلب؛ وإلا فارفضه.
هذه الخوارزمية دقيقة — إذ تحسب بالضبط عدد الطلبات التي حدثت خلال آخر N ثوانٍ. والمقابل هو ارتفاع استخدام الذاكرة (إدخال واحد لكل طلب لكل مستخدم). وبحد قدره 1000 طلب في الدقيقة مع 100K مستخدم، يصل أسوأ احتمال إلى 100M إدخال في السجل. ولا تناسب حركة المرور العالية جدًا إلا إذا استُخدمت مع التقسيم إلى أجزاء (sharding).
import time
from collections import deque
class SlidingWindowLog:
def __init__(self, limit, window_seconds):
self.limit = limit
self.window = window_seconds
self.logs = {} # user_id -> deque of timestamps
def allow(self, user_id):
now = time.time()
if user_id not in self.logs:
self.logs[user_id] = deque()
log = self.logs[user_id]
window_start = now - self.window
# Remove expired timestamps
while log and log[0] <= window_start:
log.popleft()
# Check limit
if len(log) < self.limit:
log.append(now)
return True
return False
limiter = SlidingWindowLog(limit=3, window_seconds=10)
for i in range(5):
allowed = limiter.allow('user123')
print(f'Request {i+1}: {"ALLOWED" if allowed else "REJECTED"}')
time.sleep(0.5)خوارزمية تحديد معدل الطلبات 3: عداد النافذة المنزلقة
يقرّب عداد النافذة المنزلقة النافذة المنزلقة باستخدام دلوين — الدقيقة الحالية والدقيقة السابقة — مع ترجيحهما بحسب مقدار التقدم في الدقيقة الحالية. ويقلل ذلك الذاكرة من O(requests) إلى O(1) لكل مستخدم، مع تقريب قريب من عدد النافذة المنزلقة الدقيق.
الصيغة: estimated_count = prev_count × (1 - fraction_of_window_elapsed) + curr_count. إذا تجاوز هذا العدد المقدّر الحد، فارفض الطلب. تستخدم Cloudflare وKong هذه الخوارزمية على نطاق واسع بفضل استهلاكها O(1) من الذاكرة لكل مستخدم ودقتها العالية.
import time
import math
class SlidingWindowCounter:
def __init__(self, limit, window_seconds=60):
self.limit = limit
self.window = window_seconds
self.buckets = {} # user_id -> {prev_count, curr_count, curr_window_start}
def allow(self, user_id):
now = time.time()
window_start = int(now // self.window) * self.window
if user_id not in self.buckets or self.buckets[user_id]['window'] < window_start - self.window:
self.buckets[user_id] = {'prev': 0, 'curr': 0, 'window': window_start}
elif self.buckets[user_id]['window'] < window_start:
self.buckets[user_id] = {'prev': self.buckets[user_id]['curr'], 'curr': 0, 'window': window_start}
b = self.buckets[user_id]
fraction = (now - window_start) / self.window
estimated = b['prev'] * (1 - fraction) + b['curr']
if estimated < self.limit:
b['curr'] += 1
return True
return False
limiter = SlidingWindowCounter(limit=5, window_seconds=10)
for i in range(7):
print(f'Request {i+1}: {"OK" if limiter.allow("user1") else "RATE LIMITED"}')
time.sleep(0.3)تحديد معدل الطلبات الموزع باستخدام Redis
في نظام موزع يضم عدة خوادم تطبيق، يجب أن يكون تحديد معدل الطلبات مركزيًا — وإلا فسيتتبع كل خادم عدّه الخاص، وستتضاعف الحدود فعليًا بعدد الخوادم. ويُعد Redis مع العمليات الذرية الحل القياسي: استخدموا INCR وEXPIRE لعداد نافذة ثابتة، أو ZADD وZCOUNT لسجل نافذة منزلقة.
يجعل أسلوب برنامج Lua النصي عمليات Redis المتعددة ذرية، مما يمنع حالات التنافس التي يزيد فيها خادمان العداد في الوقت نفسه بينما يكونان أدنى من الحد بقليل. يعالج Redis برامج Lua النصية كأمر واحد، مما يضمن الذرية من دون أقفال موزعة.
# Distributed rate limiting with Redis (pseudocode / simulation)
# Fixed window counter using Redis INCR + EXPIRE
def redis_fixed_window(redis_client, user_id, limit, window_sec):
key = f'rl:{user_id}:{int(time.time() // window_sec)}'
count = redis_client.incr(key) # atomic increment
if count == 1:
redis_client.expire(key, window_sec) # set TTL on first request
return count <= limit
# Sliding window with sorted set
def redis_sliding_window(redis_client, user_id, limit, window_sec):
now = time.time()
key = f'rl:{user_id}'
# Remove old entries, count recent, add current
# Atomic with Lua: multi-step operation
lua_script = '''
local key = KEYS[1]
local now = ARGV[1]
local window = ARGV[2]
local limit = ARGV[3]
redis.call('ZREMRANGEBYSCORE', key, '-inf', now - window)
local count = redis.call('ZCARD', key)
if count < tonumber(limit) then
redis.call('ZADD', key, now, now)
redis.call('EXPIRE', key, window)
return 1 -- allowed
end
return 0 -- rejected
'''
print('Redis Lua script ensures atomicity across ZREM + ZCARD + ZADD')تصميم موجز Twitter: المتطلبات
دعونا نصمم نظام موجز أخبار شبيه بـTwitter. المتطلبات الوظيفية: يمكن للمستخدمين نشر تغريدات (بحد أقصى 280 حرفًا)، ومتابعة مستخدمين آخرين، وعرض موجز للتغريدات من الأشخاص الذين يتابعونهم، مرتبًا حسب الأحدث. أما المتطلبات غير الوظيفية فهي: 300M مستخدم نشط يوميًا، و500M تغريدة يوميًا، وتحميل الموجز في أقل من <2 seconds، ونسبة قراءة إلى كتابة تبلغ نحو 100:1.
تقديرات السعة: 500M تغريدة/يوم ÷ 86400 ≈ 5800 تغريدة/ثانية. وتبلغ القراءات ≈ 580K/ثانية. يبلغ حجم كل تغريدة نحو 300 بايت؛ لذا فإن 500M × 300B = 150 GB/يوم من مساحة التخزين الجديدة للتغريدات. ويُعد تجميع الموجز التحدي الهندسي الأساسي.
# Twitter feed requirements and estimates
reqs = {
'Functional': [
'Post tweet (text, image, video)',
'Follow/unfollow users',
'View home feed (tweets from followees, newest first)',
'View user timeline (all tweets by one user)',
'Like and retweet',
'Search tweets (basic keyword)',
],
'Non-functional': [
'300M DAU, 500M tweets/day => 5800 writes/sec',
'100:1 read:write => 580K feed reads/sec',
'Feed load < 2 seconds (p95)',
'99.99% availability',
'Tweets retained indefinitely (tweets never deleted by default)',
],
'Estimates': [
'Storage: 500M tweets * 300B = 150 GB/day, 54 TB/year',
'Media: separate object store (S3), CDN-served',
'Feed cache: 300M users * top-100-tweets * 100B = 3 TB (hot feeds in Redis)',
],
}
for category, items in reqs.items():
print(f'{category}:')
for item in items: print(f' - {item}')
print()التوزيع عند الكتابة: موجزات محسوبة مسبقًا
في التوزيع عند الكتابة (fan-out on write)، عندما ينشر المستخدم A تغريدة، يوزّعها النظام فورًا على موجز كل متابع. وعندما يطلب أحد المتابعين موجزه، يكون الموجز قد حُسب مسبقًا وخُزّن في Redis — وهي قراءة بسيطة لقائمة Redis بتعقيد O(k)، حيث k هو حجم الموجز (ويُحد عادةً بـ1000 تغريدة).
يكمن التحدي في أن المشاهير الذين لديهم ملايين المتابعين ينشئون عمليات توزيع ضخمة. فنشر Justin Bieber تغريدة يتطلب الكتابة إلى موجزات أكثر من 100M متابع في الوقت نفسه — وهي مشكلة حقيقية واجهتها Twitter وسمّتها «مشكلة المشاهير». ويجب أن تكون خدمة التوزيع عند الكتابة غير متزامنة وقائمة على قوائم الانتظار للتعامل مع هذه الارتفاعات.
# Fan-out on write (push model)
fan_out_steps = [
'1. User posts tweet => write to tweets table (source of truth)',
'2. Publish event to message queue (Kafka topic: tweet-created)',
'3. Fan-out workers consume from queue:',
' a. Fetch list of followers from follows table',
' b. For each follower: LPUSH feed:{follower_id} tweet_id',
' c. Trim feed to last 1000 tweets: LTRIM feed:{follower_id} 0 999',
'4. Feed read: LRANGE feed:{user_id} 0 99 => hydrate tweet_ids => response',
]
for step in fan_out_steps:
print(step)
print('\nPros:')
print(' - Feed reads are O(1): just read from Redis list')
print(' - Feed is always sorted by recency automatically')
print('\nCons:')
print(' - Celebrities with 100M followers => 100M Redis writes per tweet')
print(' - Fan-out lag: followers may see tweet 10-30 seconds late at peak')
print(' - Inactive users waste Redis storage for precomputed feeds')التوزيع الهجين: حل مشكلة المشاهير
يجمع النهج الهجين بين التوزيع عند الكتابة للمستخدمين العاديين والتوزيع عند القراءة للمشاهير. ويُصنّف المستخدم على أنه من المشاهير إذا تجاوز عدد متابعيه حدًا معينًا (مثلًا، مليون متابع). وبالنسبة إلى المستخدمين العاديين، تُدفَع التغريدات إلى موجزات جميع المتابعين وقت النشر. أما تغريدات المشاهير فلا تُدفَع؛ بل يجلب النظام تغريداتهم الأخيرة ويدمجها مع الموجز المحسوب مسبقًا عندما يقرأ أحد المتابعين موجزه.
يشبه هذا النموذج الهجين إلى حد كبير ما تستخدمه Twitter فعليًا. وتكون خطوة الدمج سريعة لأن المشاهير ينشرون نادرًا، ولأن تعقيد الدمج هو O(f)، حيث f هو عدد حسابات المشاهير التي يتابعها المستخدم (وهو عادةً عدد صغير).
# Hybrid fan-out implementation sketch
CELEBRITY_THRESHOLD = 1_000_000 # followers > 1M => celebrity
def on_post_tweet(user_id, tweet_id, follower_count):
if follower_count <= CELEBRITY_THRESHOLD:
# Fan-out to all followers (async via Kafka)
print(f'User {user_id}: fan-out tweet {tweet_id} to {follower_count} followers')
# => queue to fan-out workers
else:
print(f'Celebrity {user_id}: tweet {tweet_id} stored in timeline only')
# => only write to tweets table + user timeline
# => followers get it on demand when reading feed
def get_home_feed(user_id, followees):
# 1. Get precomputed feed (fan-out on write tweets)
precomputed = f'LRANGE feed:{user_id} 0 499' # up to 500 tweets
# 2. Find celebrity followees
celebrity_followees = [u for u in followees if is_celebrity(u)]
# 3. Fetch recent tweets from celebrities (fan-out on read)
celebrity_tweets = []
for celeb in celebrity_followees:
tweets = f'GET tweets WHERE user_id={celeb} ORDER BY created_at DESC LIMIT 20'
celebrity_tweets.extend(tweets)
# 4. Merge and sort by recency
combined = merge_and_sort(precomputed, celebrity_tweets)
return combined[:100]
print('on_post_tweet for regular user:')
on_post_tweet('user123', 'tweet_abc', 500)
print('on_post_tweet for celebrity:')
on_post_tweet('celebrity456', 'tweet_xyz', 50_000_000)موجز Twitter: البنية المعمارية الكاملة
تجمع البنية المعمارية الكاملة لموجز Twitter عدة أنظمة:
- خدمة التغريدات: تكتب التغريدات إلى Cassandra (إنتاجية كتابة عالية، وبيانات سلاسل زمنية)
- خدمة التوزيع: عمال غير متزامنين (مستهلكو Kafka) يدفعون معرّفات التغريدات إلى موجزات المتابعين في Redis
- خدمة الموجز: تقرأ من موجز Redis، وتحوّل معرّفات التغريدات إلى كائنات تغريدات كاملة، وتدمج تغريدات المشاهير
- خدمة المتابعة: تدير الرسم البياني الاجتماعي (من يتابع من) في قاعدة بيانات رسوم بيانية أو SQL مقسّم إلى أجزاء
- خدمة الخط الزمني: تقدّم تغريدات المستخدم نفسه (بشكل منفصل عن الموجز الرئيسي)
# Twitter architecture summary
architecture = '''
[User] --> [API Gateway + Load Balancer]
|
+-----------+-----------+
| | |
[Tweet Svc] [Feed Svc] [Follow Svc]
| | |
[Cassandra] [Redis Feeds] [Graph DB]
| |
[Kafka] <-- [Fan-out
| Workers]
[S3 + CDN] (tweet_ids
(media) => follower
feed lists)
Key design choices:
- Tweets stored in Cassandra (PRIMARY KEY (user_id, created_at))
- Feed stored in Redis as list of tweet_ids per user (LPUSH/LTRIM/LRANGE)
- Fan-out via Kafka + workers (decoupled, retryable, scalable)
- Hybrid: regular users = push; celebrities = pull-on-read
- Hydration: tweet_ids -> full tweet objects via Cassandra read
'''
print(architecture)رؤوس محدِّد معدل الطلبات واستجابات الأخطاء
يُبلغ محدِّد معدل الطلبات المصمم جيدًا العملاء بحدوده عبر رؤوس استجابة HTTP. ويتيح ذلك للعملاء تنفيذ منطق إعادة المحاولة بعد الانتظار، كما يتيح للوحات المعلومات عرض الاستخدام. الرؤوس القياسية:
X-RateLimit-Limit: الحد الأقصى للطلبات المسموح بها في النافذةX-RateLimit-Remaining: عدد الطلبات المتبقية في النافذة الحاليةX-RateLimit-Reset: الطابع الزمني لـ Unix الذي تُعاد عنده تهيئة النافذةRetry-After: عدد الثواني التي يجب الانتظارها قبل إعادة المحاولة (عند استجابة 429)
رمز حالة HTTP للاستجابات التي خضعت لتحديد معدل الطلبات هو 429 Too Many Requests.
# Rate limit response headers
def build_rate_limit_headers(limit, remaining, reset_timestamp, retry_after=None):
headers = {
'X-RateLimit-Limit': str(limit),
'X-RateLimit-Remaining': str(max(0, remaining)),
'X-RateLimit-Reset': str(int(reset_timestamp)),
}
if retry_after is not None:
headers['Retry-After'] = str(retry_after)
return headers
import time
# Simulated response for allowed request
headers = build_rate_limit_headers(
limit=100,
remaining=73,
reset_timestamp=time.time() + 45
)
print('Allowed request headers:')
for k, v in headers.items():
print(f' {k}: {v}')
# Rate limited response
headers_429 = build_rate_limit_headers(
limit=100,
remaining=0,
reset_timestamp=time.time() + 30,
retry_after=30
)
print('\n429 Too Many Requests headers:')
for k, v in headers_429.items():
print(f' {k}: {v}')مقارنة خوارزميات تحديد المعدل
مقارنة موجزة بين جميع خوارزميات تحديد المعدل لمساعدتك على الاختيار في المقابلات:
- Token Bucket: يسمح بالدفعات المفاجئة، مع معدل إعادة تعبئة سلس. وهو الأنسب لواجهات API التي تُقبل فيها الدفعات العرضية (والخيار الأكثر شيوعًا).
- Leaky Bucket: يعالج الطلبات بمعدل إخراج ثابت بغض النظر عن الدفعات المفاجئة. وهو الأنسب لتشكيل حركة المرور في صورة تدفق ثابت.
- Fixed Window Counter: الأبسط، ومساحته O(1). المشكلة: إمكانية حدوث دفعة تعادل ضعف الحد عند حدود النافذة (مثلًا، 100 طلب عند 11:59 + 100 طلب عند 12:00).
- Sliding Window Log: الأدق، ولا يسبب قفزة عند حدود النافذة. المشكلة: يحتاج إلى ذاكرة بحجم O(requests).
- Sliding Window Counter: يقرّب سلوك السجل المنزلق باستخدام مساحة O(1). وتستخدمه Cloudflare.
# Algorithm comparison matrix
comparison = [
('Token Bucket', 'Allows bursts', 'O(1)', 'Most APIs, default choice'),
('Leaky Bucket', 'Smooth output rate', 'O(1)', 'Traffic shaping, message queues'),
('Fixed Window Counter', 'Very simple', 'O(1)', 'Low-traffic, approximate OK'),
('Sliding Window Log', 'Most accurate', 'O(requests)', 'High-accuracy, low traffic'),
('Sliding Window Counter','Approximate+fast', 'O(1)', 'High-traffic, Cloudflare-style'),
]
print(f'{'Algorithm':30s} {'Burst Handling':20s} {'Memory':15s} {'Use Case'}')
print('-'*85)
for name, burst, mem, use in comparison:
print(f'{name:30s} {burst:20s} {mem:15s} {use}')اختبار سريع
اختبر مدى فهمك لمفاهيم Data Structures & Algorithms — Coding Interview Prep الواردة في هذا الدرس.
مراجعة الدرس
في هذا الدرس تعلمت أن: محددات المعدل تستخدم Token Bucket (يسمح بالدفعات المفاجئة)، أو Sliding Window Counter (ذاكرة O(1))، أو Sliding Window Log (الأدق) للتحكم في معدلات الطلبات، وأن العمليات الذرية في Redis تتيح تحديد المعدل في الأنظمة الموزعة، وأن خلاصة Twitter تستخدم fan-out on write لإنشاء خلاصات المتابعين مسبقًا في Redis من أجل قراءات سريعة، مع استخدام نموذج سحب هجين للحسابات ذات المتابعين الكثيرين لتجنب التضخيم الهائل لعمليات الكتابة. بعد ذلك ننتقل إلى قسم المشروع الختامي مع ورقة غش للتعرّف على الأنماط، تربط إشارات المشكلة بأنماط الخوارزميات التي تحلها بأسرع طريقة.
الأسئلة الشائعة
هل درس «تصميم محدِّد معدل الطلبات وتصميم موجز Twitter» مجاني؟
نعم — نص درس «تصميم محدِّد معدل الطلبات وتصميم موجز Twitter» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.
ماذا ستتعلم في «تصميم محدِّد معدل الطلبات وتصميم موجز Twitter»؟
طبّق الإطار على مسألتين نموذجيتين في التصميم: تحديد معدل الطلبات باستخدام token-bucket أو sliding-window، وتصميم موجز أخبار يعتمد على fan-out-on-write مقابل fan-out-on-read. تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟
لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.
كم من الوقت يستغرق درس «تصميم محدِّد معدل الطلبات وتصميم موجز Twitter»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟
نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- إطار مقابلة تصميم الأنظمة
- تخزين البيانات القابل للتوسّع: SQL مقابل NoSQL
- التخزين المؤقت وشبكات CDN وموازنة الحمل
- تصميم محدِّد معدل الطلبات وتصميم موجز Twitter