ลอจิกการลองใหม่ของเวบฮุก
เรียนรู้วิธีการจัดการการลองใหม่ของเวบฮุก, ใช้ความส ามารถในการเป็นปัญหาเดียวกันเพื่อจัดการการจัดส่งที่ซ้ำกัน และสร้างระบบการประมวลผลเวบฮุกที่เชื่อถือได้
ภาพรวม
การจัดส่งเวบฮุกไม่รับประกันในครั้งแรก ปัญหาเครือข่าย, ความล้มเหลวของเซิร์ฟเวอร์ หรือข้อผิดพลาดในการประมวลผลสามารถทำให้เกิดความล้มเหลว คำแนะนำนี้ครอบคลุม:
- ลำดับการลองใหม่อัตโนมัติของ Omise และ exponential backoff
- การใช้ความสามารถในการเป็นปัญหาเดียวกันเพื่อจัดการเหตุการณ์ที่ซ้ำกัน
- การบันทึกและตรวจสอบการจัดส่งเวบฮุก
- ขั้นตอนการลองใหม่ด้วยตนเอง
- แนวทางปฏิบัติที่ดีสำหรับการจัดการความล้มเหลว
ตารางการลองใหม่
วิธีการลองใหม่เวบฮุก
เมื่อการจัดส่งเวบฮุกล้มเหลว (การตอบสนอง non-2xx หรือการหมดเวลา), Omise จะลองใหม่โดยอัตโนมัติด้วย exponential backoff:
ความพยายาม 1: ทันที (การจัดส่งเริ่มต้น)
ความพยายาม 2: หลังจาก 1 นาที
ความพยายาม 3: หลังจาก 5 นาที
ความพยายาม 4: หลังจาก 15 นาที
ความพยายาม 5: หลังจาก 1 ชั่วโมง
ความพยายาม 6: หลังจาก 3 ชั่วโมง
ความพยายาม 7: หลังจาก 6 ชั่วโมง
ความพยายาม 8: หลังจาก 12 ชั่วโมง
ความพยายาม 9: หลังจาก 24 ชั่วโมง
ความพยายาม 10: หลังจาก 48 ชั่วโมง
ความพยายามสุดท้าย: ทุกๆ 48 ชั่วโมงจนกระทั่ง 7 วันผ่านไป
พฤติกรรมการลองใหม่
ตัวบ่งชี้สำหรับการลองใหม่:
- รหัสสถานะ HTTP 400-599 (ข้อผิดพลาดของไคลเอนต์และเซิร์ฟเวอร์)
- Timeout เครือข่าย (ไม่มีการตอบสนองภายใน 10 วินาที)
- ความล้มเหลวในการเชื่อมต่อ (ข้อผิดพล าด DNS, การปฏิเสธการเชื่อมต่อ)
เกณฑ์ความสำเร็จ:
- รหัสสถานะ HTTP 200-299
- การตอบสนองที่ได้รับภายใน 10 วินาที
ระยะเวลาการลองใหม่:
- การลองใหม่ดำเนินต่อไปเป็นเวลา 7 วันนับจากความพยายามเริ่มต้น
- หลังจาก 7 วัน, เวบฮุกจะถูกทำเครื่องหมายว่าล้มเหลวถาวร
ความเป็นปัญหาเดียวกัน
ความสามารถในการเป็นปัญหาเดียวกันช่วยให้การประมวลผลเวบฮุกหลายครั้งมีเอฟเฟกต์เดียวกับการประมวลผลครั้งเดียว นี่เป็นสิ่งสำคัญเพราะเวบฮุกสามารถจัดส่งหลายครั้งเนื่องจากการลองใหม่
เหตุใดความเป็นปัญหาเดียวกันจึงสำคัญ
สถานการณ์: การจัดส่งเวบฮุกล้มเหลวหลังจากประมวลผล แต่ก่อนการตอบสนอง
1. เวบฮุกได้รับ
2. ประจำถูกทำเครื่องหมายว่าจ่ายแล้ว ✓
3. อีเมลส่งให้ลูกค้า ✓
4. เซิร์ฟเวอร์ขัดข้องก่อนการตอบสนอง ✗
5. Omise ลองใหม่เวบฮุก
6. ไม่มีความสามารถในการเป็นปัญหาเดียวกัน:
- ประจำถูกทำเครื่องหมายว่าจ่ายแล้วอีกครั้ง (ซ้ำกัน)
- ลูกค้าได้รับอีเมลซ้ำ ✗
7. ด้วยความสามารถในการเป็นปัญหาเดียวกัน:
- จำหน่ายเหตุการณ์ที่ประมวลผลแล้ว
- ข้ามการประมวลผลที่ซ้ำกัน ✓
รูปแบบความสามารถในการเป็นปัญหาเดียวกัน
1. การติดตาม Event ID
// Node.js - การติดตาม Event ID ด้วย Redis
const redis = require('redis');
const client = redis.createClient();
async function processWebhookIdempotent(event) {
const eventId = event.id;
const processedKey = `webhook:processed:${eventId}`;
// ตรวจสอบว่าประมวลผลแล้ว
const isProcessed = await client.exists(processedKey);
if (isProcessed) {
console.log(`Event ${eventId} already processed, skipping`);
return { status: 'duplicate', eventId };
}
try {
// ประมวลผลเวบฮุก
await processWebhook(event);
// ทำเครื่องหมายเป็นประมวลผล (เก็บไว้ 7 วัน)
await client.setex(processedKey, 7 * 24 * 3600, JSON.stringify({
processed_at: new Date().toISOString(),
event_key: event.key
}));
return { status: 'success', eventId };
} catch (error) {
console.error(`Error processing event ${eventId}:`, error);
throw error;
}
}
2. ข้อจำกัดฐานข้อมูล
// Node.js - ความสามารถในการเป็นปัญหาเดียวกันที่ระดับฐานข้อมูลด้วย MongoDB
const mongoose = require('mongoose');
// โครงการเวบฮุกพร้อมข้อจำกัดแบบเฉพาะ
const WebhookEventSchema = new mongoose.Schema({
event_id: {
type: String,
required: true,
unique: true, // ป้องกันการประมวลผลที่ซ้ำกัน
index: true
},
event_key: { type: String, required: true },
processed_at: { type: Date, default: Date.now },
data: { type: mongoose.Schema.Types.Mixed },
status: {
type: String,
enum: ['processing', 'completed', 'failed'],
default: 'processing'
}
});
const WebhookEvent = mongoose.model('WebhookEvent', WebhookEventSchema);
async function processWebhookIdempotent(event) {
try {
// พยายามแทรกบันทึกเหตุการณ์
const webhookEvent = new WebhookEvent({
event_id: event.id,
event_key: event.key,
data: event.data
});
await webhookEvent.save();
} catch (error) {
// ข้อผิดพลาดคีย์ที่ซ้ำกัน - เหตุการณ์ประมวลผลแล้ว
if (error.code === 11000) {
console.log(`Event ${event.id} already processed`);
return { status: 'duplicate' };
}
throw error;
}
try {
// ประมวลผลเวบฮุก
await processWebhook(event);
// อัปเดตสถานะ
await WebhookEvent.updateOne(
{ event_id: event.id },
{ status: 'completed' }
);
return { status: 'success' };
} catch (error) {
// อัปเดตสถานะเป็นล้มเหลว
await WebhookEvent.updateOne(
{ event_id: event.id },
{ status: 'failed', error: error.message }
);
throw error;
}
}
การจัดการความล้มเหลว
การจัดการข้อผิดพลาดที่สง่างาม
ตอบสนองต่อเวบฮุกอย่างรวดเร็วแม้ว่าการประมวลผลจะล้มเหลว:
// Node.js - การจัดการข้อผิดพลาดที่สง่างาม
app.post('/webhooks/omise', async (req, res) => {
let event;
try {
// ตรวจสอบลายเซ็น
const signature = req.headers['x-omise-signature'];
if (!signature || !verifySignature(req.body, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
event = JSON.parse(req.body.toString());
// ตอบสนองทันที (ก่อนการประมวลผล)
res.status(200).json({ received: true });
} catch (error) {
console.error('Webhook parsing error', error);
return res.status(400).json({ error: 'Invalid request' });
}
// ประมวลผลแบบไม่ซิงโครนัสด้วยการจัดการข้อผิดพลาด
processWebhookWithRetry(event)
.then(() => {
console.log('Webhook processed successfully');
})
.catch(error => {
console.error('Webhook processing failed', error);
// เข้าคิวสำหรับการตรวจสอบด้วยตนเอง
queueForManualReview(event, error);
});
});