Kısaca: Yoklama (Polling), güncellemeleri belirli aralıklarla kontrol eder (basit ama verimsiz). Web kancaları (Webhooks), olayları gerçek zamanlı iletir (verimli ama daha karmaşık). Seyrek kontroller için yoklama, anlık bildirimler için web kancaları tercih edilmelidir. Modern PetstoreAPI, güvenilir webhook teslimatı ile iki modeli de destekler.
Farkı Anlamak
Yoklama (Polling): İstemci belirli aralıklarla "Güncelleme var mı?" diye sorar. Web Kancaları (Webhooks): Sunucu, bir olay oluştuğunda otomatik olarak bildirir.
- Yoklama = Posta kutunuzu her saat kontrol etmek
- Web Kancası = Postacı, posta geldiğinde kapı zilini çalar
Yoklama (Polling): Nasıl Çalışır?
İstemci, değişiklikleri algılamak için periyodik HTTP istekleri gönderir.
// 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 Yaklaşımları:
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
# Değiştiyse yeni verilerle 200 OK
Zamana Dayalı Yoklama (Since-based polling):
GET /api/v1/orders/123/events?since=1710331200
# Belirtilen zamandan bu yana olayları döndürür
Web Kancaları (Webhooks): Nasıl Çalışır?
Sunucu, olaylar gerçekleştiğinde otomatik olarak belirlediğiniz uç noktaya HTTP POST gönderir.
Kurulum adımları:
// 1. Webhook uç noktasını kaydet
POST /api/v1/webhooks
{
"url": "https://myapp.com/webhooks/petstore",
"events": ["order.created", "order.completed"],
"secret": "whsec_abc123"
}
// 2. Sunucu, olayda webhook 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. Webhook'u doğrula ve işle
// 200 OK ile yanıt verin
Yoklama (Polling) Ne Zaman Kullanılmalı?
Şu durumlar için uygundur:
- Seyrek kontrol ihtiyacı (saatte bir gibi)
- Az sayıda kaynak
- Basit uygulamalar
- İstemciyi siz yönetiyorsanız
- Test ve debug amaçlı
Örnekler:
- Günlük rapor durumu sorgulama
- Kişileri birkaç dakikada bir senkronize etme
- Sunucu sağlık kontrolü
- Seyrek ödeme durumu kontrolü
Yoklama tercih edilir:
- Güncellemeler nadir ise
- Küçük gecikmeler tolere edilebiliyorsa
- Uygulama sade olacaksa
- Küçük ölçekli kaynaklarda
Web Kancaları (Webhooks) Ne Zaman Kullanılmalı?
Şu durumlar için uygundur:
- Gerçek zamanlı güncelleme ihtiyacı
- Çok sayıda kaynak izleniyorsa
- Zaman kritik olaylar
- Üçüncü parti 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ı daha iyi olur:
- Anında güncelleme gerekiyorsa
- Yoklama verimsizse
- Aynı kaynağı birçok istemci izliyorsa
- Sunucu yükünü azaltmak istiyorsanız
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();
// Sadece durum değişirse callback çalıştır
if (order.status !== lastStatus) {
lastStatus = order.status;
callback(order);
}
// Terminal duruma geldiyse yoklamayı bitir
if (['completed', 'cancelled'].includes(order.status)) {
return;
}
// Devam et
setTimeout(poll, 5000);
} catch (error) {
console.error('Yoklama hatası:', error);
setTimeout(poll, 30000); // Hata alırsa beklemeyi artır
}
};
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();
// Değişiklik varsa callback çalıştır
if (JSON.stringify(data) !== JSON.stringify(lastData)) {
lastData = data;
callback(data);
}
// Durdurma koşulu varsa bitir
if (stopCondition(data)) {
return;
}
// Başarılı istekte interval sıfırlanır
interval = initialInterval;
} catch (error) {
retries++;
if (retries >= maxRetries) {
throw new Error('Maksimum yeniden deneme aşıldı');
}
}
// Üstel geri çekilme ile devam et
setTimeout(poll, interval);
interval = Math.min(interval * 2, maxInterval);
};
poll();
}
// Kullanım: Sipariş tamamlanana kadar 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şiklik yok, 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
// Webhook 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 body kullanın
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;
}
// Başarılı yanıt
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 endpoint'i açığa çıkarmak için ngrok kullan
ngrok http 3000
# ngrok URL'sini webhook endpoint 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ının iletimi başarısız olabilir. Yeniden deneme (retry) mantığını uygulamak şarttır.
Gönderici Tarafı (Sunucu)
// Web kancalarını teslim için 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ı olduysa sıradan çıkar
webhookQueue.splice(webhookQueue.indexOf(item), 1);
} catch (error) {
// Retry için üstel bekleme
item.attempts++;
item.nextAttempt = now + getBackoff(item.attempts);
if (item.attempts >= 5) {
// 5 deneme 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 webhook işleme
const processedEvents = new Set();
app.post('/webhooks/petstore', async (req, res) => {
const event = JSON.parse(req.body.toString());
// Zaten işlendiyse atla (idempotency)
if (processedEvents.has(event.id)) {
return res.status(200).json({ received: true });
}
try {
await processEvent(event);
processedEvents.add(event.id);
// Eski event id'lerini 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('Webhook işleme hatası:', error);
// Retry için 5xx döndür
res.status(500).json({ error: 'İşleme başarısız oldu' });
}
});
async function processEvent(event) {
// Event'i işle
switch (event.type) {
case 'order.created':
await handleOrderCreated(event.data);
break;
// ... diğer eventler
}
}
Hibrit Yaklaşım
Kritik güncellemeler için hem yoklama hem de web kancalarını birlikte kullanabilirsiniz.
class OrderMonitor {
constructor(orderId, callback) {
this.orderId = orderId;
this.callback = callback;
this.pollInterval = null;
}
async start() {
// Hızlı yanıt için polling başlat
this.startPolling();
// Gerçek zamanlı güncelleme için webhook 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 tetiklemeden 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 göre değişir. Gerçek zamanlıya yakın için 30 saniye, acil değilse 5 dakika uygundur. Uygulamanın güncelliği ile sunucu yükünü dengeleyin.
S: Web kancası endpoint'im kapalıysa ne olur? Kaliteli webhook sağlayıcıları, üstel geri çekilme ile yeniden dener. Yinelenen teslimatları idempotency ile yönetin.
S: Web kancalarını nasıl güvenli hale getiririm? İmzaları paylaşılan bir gizli anahtarla doğrulayın. Sadece HTTPS kullanın. Olay verilerini doğrulayın.
S: Web kancaları geçmiş veriler için kullanılabilir mi? Hayır. Web kancaları sadece yeni olaylar içindir. Geçmiş için polling veya toplu API kullanın.
S: Mobil uygulamalarda polling mi, webhook mu kullanmalıyım? Polling mobilde daha basittir. Webhook için push bildirimi altyapısı gereklidir.
S: Web kancası sorunlarını nasıl ayıklarım? Test için webhook.site gibi araçları kullanın. Tüm webhook teslimatlarını loglayın. API'nizde webhook olay geçmişi sağlayın.
Modern PetstoreAPI, polling ve web kancalarını destekler. Detaylı uygulama için web kancası kılavuzuna göz atın. Web kancası entegrasyonlarınızı Apidog ile test edebilirsiniz.
Top comments (0)