Kısaca: Yoklama (Polling) güncellemeleri periyodik olarak kontrol eder (basit ama verimsiz). Web kancaları (Webhooks) güncellemeleri gerçek zamanlı olarak iletir (verimli ama karmaşık). Nadir kontroller için yoklama, gerçek zamanlı güncellemeler için web kancaları kullanın. Modern PetstoreAPI, güvenilir web kancası teslimatı ile her iki modeli de destekler.
Farkı Anlamak
Yoklama (Polling): İstemci tekrar tekrar "Herhangi bir güncelleme var mı?" diye sorar. Web Kancaları (Webhooks): Sunucu, bir şey olduğunda "İşte bir güncelleme!" der.
Benzetme:
- Yoklama = Posta kutunuzu her saat başı kontrol etmek
- Web Kancaları = Postacı, posta geldiğinde kapı zilini çalar
Yoklama (Polling): Nasıl Çalışır?
İstemci, değişiklikleri kontrol etmek için periyodik istekler yapar.
// Her 30 saniyede bir yokla
setInterval(async () => {
const response = await fetch('https://petstoreapi.com/api/v1/orders/123');
const order = await response.json();
if (order.status === 'completed') {
console.log('Sipariş tamamlandı!', order);
clearInterval(pollInterval);
}
}, 30000);
Yoklama modelleri:
Basit yoklama:
GET /api/v1/orders/123
# Mevcut sipariş durumunu döndürür
Koşullu yoklama (ETag):
GET /api/v1/orders/123
If-None-Match: "abc123"
# Değişmediyse 304 Not Modified döndürür
# Değiştiyse yeni verilerle 200 döndürür
Zamana dayalı yoklama (Since-based polling):
GET /api/v1/orders/123/events?since=1710331200
# Belirtilen zaman damgasından bu yana olayları döndürür
Web Kancaları (Webhooks): Nasıl Çalışır?
Sunucu, olaylar meydana geldiğinde uç noktanıza bir HTTP POST isteği gönderir.
Kurulum akışı:
// 1. Web kancası uç noktasını kaydet
POST /api/v1/webhooks
{
"url": "https://myapp.com/webhooks/petstore",
"events": ["order.created", "order.completed"],
"secret": "whsec_abc123"
}
// 2. Sunucu, olay meydana geldiğinde web kancasını gönderir
POST https://myapp.com/webhooks/petstore
{
"id": "evt_123",
"type": "order.completed",
"created": 1710331200,
"data": {
"orderId": "123",
"status": "completed",
"completedAt": "2024-01-01T12:00:00Z"
}
}
// 3. Web kancasını doğrula ve işle
// 200 OK ile yanıt ver
Yoklama (Polling) Ne Zaman Kullanılmalı?
Şunlar için iyidir:
- Sık olmayan kontroller (saatte bir kez)
- Az sayıda kaynak
- Basit uygulamalar
- İstemciyi siz kontrol ettiğinizde
- Test ve hata ayıklama
Örnekler:
- Günlük rapor durumunu kontrol etme
- Kişileri birkaç dakikada bir senkronize etme
- Sunucu sağlığını izleme
- Ödeme durumunu kontrol etme (sık olmayan)
Yoklama şu durumlarda uygundur:
- Güncellemeler nadirdir
- Ufak bir gecikme kabul edilebilir
- Basit bir uygulama istiyorsunuz
- Kaynak küçüktür
Web Kancaları (Webhooks) Ne Zaman Kullanılmalı?
Şunlar için iyidir:
- Gerçek zamanlı güncellemeler
- İzlenecek çok sayıda kaynak
- Zaman açısından kritik olaylar
- Üçüncü taraf entegrasyonları
- Yüksek frekanslı güncellemeler
Örnekler:
- Ödeme onayları
- Sohbet mesajları
- Borsa fiyatı uyarıları
- Sipariş durumu değişiklikleri
- CI/CD bildirimleri
Web kancaları şu durumlarda daha iyidir:
- Güncellemelerin anında olması gerektiğinde
- Yoklama verimsiz olacağında
- Aynı kaynağı izleyen çok sayıda istemci olduğunda
- Sunucu yükünü azaltmak istediğinizde
Karşılaştırma Tablosu
| Faktör | Yoklama (Polling) | Web Kancaları (Webhooks) |
|---|---|---|
| Gecikme | Yoklama aralığına kadar | Gerçek zamanlı |
| Sunucu yükü | Yüksek (çok sayıda boş istek) | Düşük (sadece gerçek olaylar) |
| Karmaşıklık | Basit | Karmaşık |
| Güvenilirlik | Yüksek (istemci yeniden denemeyi kontrol eder) | Orta (yeniden deneme mantığı gerekir) |
| Kurulum | Yok | Uç nokta kaydı |
| Güvenlik duvarı sorunları | Yok (yalnızca giden) | Beyaz listeye alma gerekebilir |
| Maliyet | Daha yüksek (daha fazla istek) | Daha düşük (daha az istek) |
| En iyi kullanım | Sık olmayan kontroller | Gerçek zamanlı güncellemeler |
Yoklama (Polling) Uygulaması
Temel Yoklama
async function pollOrderStatus(orderId, callback) {
let lastStatus = null;
const poll = async () => {
try {
const response = await fetch(`https://petstoreapi.com/api/v1/orders/${orderId}`);
const order = await response.json();
// Yalnızca durum değişirse geri çağır
if (order.status !== lastStatus) {
lastStatus = order.status;
callback(order);
}
// Terminal duruma ulaştığında yoklamayı durdur
if (['completed', 'cancelled'].includes(order.status)) {
return;
}
// Yoklamaya devam et
setTimeout(poll, 5000);
} catch (error) {
console.error('Yoklama hatası:', error);
setTimeout(poll, 30000); // Hatada bekleme süresini uzat
}
};
poll();
}
// Kullanım
pollOrderStatus('order-123', (order) => {
console.log(`Sipariş durumu: ${order.status}`);
});
Akıllı Yoklama (Üstel Geri Çekilme)
async function smartPoll(url, callback, options = {}) {
const {
maxRetries = 10,
initialInterval = 1000,
maxInterval = 60000,
stopCondition = () => false
} = options;
let retries = 0;
let interval = initialInterval;
let lastData = null;
const poll = async () => {
try {
const response = await fetch(url);
const data = await response.json();
// Veriler değiştiyse geri çağır
if (JSON.stringify(data) !== JSON.stringify(lastData)) {
lastData = data;
callback(data);
}
// Koşul karşılanırsa durdur
if (stopCondition(data)) {
return;
}
// Başarılı istekte aralığı sıfırla
interval = initialInterval;
} catch (error) {
retries++;
if (retries >= maxRetries) {
throw new Error('Maksimum yeniden deneme aşıldı');
}
}
// Üstel geri çekilme ile sonraki yoklamayı planla
setTimeout(poll, interval);
interval = Math.min(interval * 2, maxInterval);
};
poll();
}
// Kullanım: Tamamlanana kadar siparişi yokla
smartPoll('https://petstoreapi.com/api/v1/orders/123',
(order) => console.log('Sipariş:', order),
{
stopCondition: (order) => ['completed', 'cancelled'].includes(order.status),
initialInterval: 2000,
maxInterval: 30000
}
);
ETag ile Yoklama
async function pollWithEtag(url, callback) {
let etag = null;
const poll = async () => {
const headers = {};
if (etag) {
headers['If-None-Match'] = etag;
}
const response = await fetch(url, { headers });
if (response.status === 304) {
// Değişmedi, yoklamaya devam et
setTimeout(poll, 30000);
return;
}
const data = await response.json();
etag = response.headers.get('etag');
callback(data);
setTimeout(poll, 30000);
};
poll();
}
Web Kancaları (Webhooks) Uygulaması
Web Kancalarını Kaydetme
// Web kancası uç noktasını kaydet
async function registerWebhook(url, events) {
const response = await fetch('https://petstoreapi.com/api/v1/webhooks', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
url,
events,
secret: generateSecret()
})
});
return response.json();
}
function generateSecret() {
return 'whsec_' + crypto.randomBytes(32).toString('hex');
}
Web Kancalarını Alma
const express = require('express');
const crypto = require('crypto');
const app = express();
// İmza doğrulaması için ham gövde ayrıştırıcı
app.use('/webhooks', express.raw({ type: 'application/json' }));
app.post('/webhooks/petstore', async (req, res) => {
const signature = req.headers['x-petstore-signature'];
const body = req.body;
// İmzayı doğrula
const isValid = verifySignature(body, signature, process.env.WEBHOOK_SECRET);
if (!isValid) {
return res.status(401).json({ error: 'Geçersiz imza' });
}
const event = JSON.parse(body.toString());
// Olayı işle
switch (event.type) {
case 'order.created':
await handleOrderCreated(event.data);
break;
case 'order.completed':
await handleOrderCompleted(event.data);
break;
case 'order.cancelled':
await handleOrderCancelled(event.data);
break;
}
// Alındığını onayla
res.status(200).json({ received: true });
});
function verifySignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
Yerel Olarak Web Kancalarını Test Etme
# Yerel uç noktayı açığa çıkarmak için ngrok kullanın
ngrok http 3000
# ngrok URL'sini web kancası uç noktası olarak kaydet
curl -X POST https://petstoreapi.com/api/v1/webhooks \
-H "Authorization: Bearer $TOKEN" \
-d '{
"url": "https://abc123.ngrok.io/webhooks/petstore",
"events": ["order.created", "order.completed"]
}'
Güvenilir Web Kancası Teslimatı
Web kancaları başarısız olabilir. Yeniden deneme mantığı uygulayın.
Gönderici Tarafı (Sunucu)
// Teslimat için web kancalarını sıraya al
const webhookQueue = [];
async function sendWebhook(event) {
const webhooks = await db.webhooks.findMany({
where: { events: { contains: event.type } }
});
for (const webhook of webhooks) {
webhookQueue.push({
webhook,
event,
attempts: 0,
nextAttempt: Date.now()
});
}
processQueue();
}
async function processQueue() {
const now = Date.now();
for (const item of webhookQueue) {
if (item.nextAttempt > now) continue;
try {
await deliverWebhook(item);
// Başarılı olursa sıradan kaldır
webhookQueue.splice(webhookQueue.indexOf(item), 1);
} catch (error) {
// Üstel geri çekilmeyle yeniden denemeyi planla
item.attempts++;
item.nextAttempt = now + getBackoff(item.attempts);
if (item.attempts >= 5) {
// 5 denemeden sonra başarısız olarak işaretle
await markWebhookFailed(item);
webhookQueue.splice(webhookQueue.indexOf(item), 1);
}
}
}
setTimeout(processQueue, 5000);
}
function getBackoff(attempt) {
// 1dk, 5dk, 15dk, 1sa, 4sa
const delays = [60000, 300000, 900000, 3600000, 14400000];
return delays[attempt - 1] || delays[delays.length - 1];
}
async function deliverWebhook({ webhook, event }) {
const signature = generateSignature(event, webhook.secret);
const response = await fetch(webhook.url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Petstore-Signature': signature,
'X-Petstore-Event': event.type
},
body: JSON.stringify(event),
timeout: 10000
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
}
Alıcı Tarafı (İstemci)
// Idempotent web kancası işleme
const processedEvents = new Set();
app.post('/webhooks/petstore', async (req, res) => {
const event = JSON.parse(req.body.toString());
// Zaten işlenmişse atla (idempotency)
if (processedEvents.has(event.id)) {
return res.status(200).json({ received: true });
}
try {
await processEvent(event);
processedEvents.add(event.id);
// Eski olay kimliklerini temizle (son 1000'i tut)
if (processedEvents.size > 1000) {
const arr = Array.from(processedEvents);
arr.slice(0, arr.length - 1000).forEach(id => processedEvents.delete(id));
}
res.status(200).json({ received: true });
} catch (error) {
console.error('Web kancası işleme hatası:', error);
// Yeniden denemeyi tetiklemek için 5xx döndür
res.status(500).json({ error: 'İşleme başarısız oldu' });
}
});
async function processEvent(event) {
// Olayı işle
switch (event.type) {
case 'order.created':
await handleOrderCreated(event.data);
break;
// ... diğer olayları işle
}
}
Hibrit Yaklaşım
Kritik güncellemeler için hem yoklama hem de web kancalarını kullanın.
class OrderMonitor {
constructor(orderId, callback) {
this.orderId = orderId;
this.callback = callback;
this.pollInterval = null;
}
async start() {
// Anında geri bildirim için yoklama ile başla
this.startPolling();
// Gerçek zamanlı güncelleme için web kancasını kaydet
await this.registerWebhook();
}
startPolling() {
this.pollInterval = setInterval(async () => {
const order = await this.fetchOrder();
this.callback(order);
if (['completed', 'cancelled'].includes(order.status)) {
this.stop();
}
}, 10000);
}
async registerWebhook() {
const response = await fetch('https://petstoreapi.com/api/v1/webhooks', {
method: 'POST',
headers: { 'Authorization': `Bearer ${TOKEN}` },
body: JSON.stringify({
url: 'https://myapp.com/webhooks/petstore',
events: [`order.${this.orderId}`],
oneTime: true // İlk teslimattan sonra otomatik sil
})
});
this.webhookId = (await response.json()).id;
}
stop() {
if (this.pollInterval) {
clearInterval(this.pollInterval);
}
if (this.webhookId) {
fetch(`https://petstoreapi.com/api/v1/webhooks/${this.webhookId}`, {
method: 'DELETE'
});
}
}
}
Sıkça Sorulan Sorular
S: Ne sıklıkla yoklama yapmalıyım?Aciliyetine bağlıdır. Gerçek zamanlıya yakın durumlar için 30 saniye. Acil olmayan durumlar için 5 dakika. Güncelliği sunucu yüküyle dengeleyin.
S: Web kancası uç noktam kapalıysa ne olur?İyi web kancası sağlayıcıları, üstel geri çekilme ile yeniden dener. Yinelenen teslimatları yönetmek için idempotency (tekrarlanan işlemlerin aynı sonucu vermesi) uygulayın.
S: Web kancalarını nasıl güvence altına alırım?Paylaşılan gizli anahtarları kullanarak imzaları doğrulayın. Yalnızca HTTPS kullanın. Olay verilerini doğrulayın.
S: Web kancalarını geçmiş veriler için kullanabilir miyim?Hayır. Web kancaları yalnızca yeni olaylar içindir. Geçmiş veriler için yoklama veya toplu API'leri kullanın.
S: Mobil uygulamalar için yoklama mı yoksa web kancaları mı kullanmalıyım?Yoklama mobil için daha basittir. Web kancaları, aracı olarak anlık bildirimler gerektirir.
S: Web kancası sorunlarını nasıl ayıklayabilirim?Test için webhook.site gibi araçları kullanın. Tüm web kancası teslimatlarını günlüğe kaydedin. API'nizde web kancası olay geçmişi sağlayın.
Modern PetstoreAPI hem yoklamayı hem de web kancalarını destekler. Uygulama detayları için web kancaları kılavuzuna bakın. Web kancası entegrasyonlarını Apidog ile test edin.
