Python CLI กับ Webhook ผ่าน ngrok เพิ่มความทนทานให้งานสำคัญ
สวัสดีครับ! โปรแกรมเมอร์ทุกคน
วันนี้ผมอยากจะพาเพื่อนๆ มาดูเทคนิคที่เราใช้กันบ่อยมากๆ เวลาต้องทำงานกับ Webhook ในเครื่องเราเนี่ยแหละครับ นั่นก็คือการใช้ ngrok นะครับ แถมยังจะมาเพิ่มความทนทาน หรือ Resilience ให้กับโปรแกรม Python CLI ของเรา เวลาที่ต้องรับมือกับข้อมูล Webhook ด้วยกันครับ
ทำไมต้อง ngrok? ก็เพราะว่าเวลาเราพัฒนา Webhook หรือ API ที่ต้องให้ภายนอกเรียกเข้ามาที่เครื่องเราตรงๆ เนี่ย มันทำไม่ได้เลยใช่ไหมครับ แต่ ngrok มันช่วยสร้าง Public URL ที่ชี้เข้ามาที่ Localhost ของเราได้เลย ง่ายมากๆ ครับ
แล้วทำไมต้อง Resilience? ก็เพราะว่าการสื่อสารผ่านเครือข่ายมันไม่แน่นอนครับ ข้อมูลอาจจะมาไม่ครบ, เน็ตหลุด, หรือแม้แต่ปลายทางที่เราจะไปเรียกต่อเกิดล่มขึ้นมา ก็จะทำให้โปรแกรมของเราหยุดทำงานได้เลยใช่ไหมครับ การเพิ่ม Resilience จะช่วยให้โปรแกรมเราทำงานต่อได้ ถึงแม้จะมีปัญหาบางอย่างเกิดขึ้นนั่นเองครับ
มาดูกันเลยครับว่าจะทำยังไงกันบ้าง
1. เตรียม ngrok ให้พร้อมใช้งาน
ก่อนอื่นเลย เพื่อนๆ ต้องไปสมัครบัญชี ngrok ที่เว็บไซต์ของเค้าก่อนนะครับ แล้วก็ทำตามขั้นตอนการติดตั้งให้เรียบร้อย จะมีให้เชื่อม Account Token ด้วยนะครับ หลังจากนั้นก็พร้อมใช้งานแล้วครับ
# สมัครบัญชี ngrok และติดตั้งตามขั้นตอนในเว็บนะครับ
# สำหรับ macOS/Linux:
# brew install ngrok/ngrok/ngrok
# หรือดาวน์โหลดจาก https://ngrok.com/download
# จากนั้นเชื่อม Account Token (ดูได้จากหน้า Dashboard ของ ngrok)
ngrok authtoken <YOUR_AUTH_TOKEN>
2. สร้าง Python CLI ง่ายๆ เพื่อรับ Webhook (ด้วย Flask)
ผมจะใช้ Flask เป็นตัวอย่างนะครับ เพราะว่ามันง่ายและรวดเร็วดีครับ ให้เราสร้างไฟล์ชื่อ app.py ขึ้นมา แล้วใส่โค้ดนี้ลงไปเลยครับ
# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def handle_webhook():
data = request.json
print(f"ได้รับ Webhook แล้วครับ: {data}")
# ตรงนี้เราสามารถเอาข้อมูลที่ได้ไปประมวลผลต่อได้เลยครับ
return jsonify({"status": "success", "message": "ได้รับข้อมูลแล้วครับ"}), 200
if __name__ == '__main__':
app.run(port=8000, debug=True)
จากนั้นก็รันโปรแกรม Python ของเราที่พอร์ต 8000 ได้เลยครับ
python app.py
ตอนนี้โปรแกรม Flask ของเราพร้อมรับ Webhook ที่พอร์ต 8000 แล้วนะครับ
3. เปิด Tunnel ด้วย ngrok
คราวนี้เราจะใช้ ngrok เปิดช่องทางให้ Public URL ชี้เข้ามาที่พอร์ต 8000 ของเราครับ
ngrok http 8000
พอรันคำสั่งนี้เสร็จ ngrok จะแสดง URL มาให้เรานะครับ เป็นแบบ https://xxxx-xxxx-xxxx-ngrok-free.app/ ประมาณนี้ครับ URL นี้แหละที่เราจะเอาไว้ให้ภายนอกส่ง Webhook มาได้เลย
4. เพิ่ม Resilience ให้ Python CLI ของเรา
มาถึงส่วนสำคัญกันแล้วครับ เราจะมาเพิ่มความทนทานให้กับโปรแกรมของเรากัน ในกรณีที่ตอนเราได้รับ Webhook แล้วต้องเอาข้อมูลไปประมวลผลต่อ เช่น ไปเรียก External API หรือ Save ลง Database ที่อาจจะมีปัญหาได้ เราจะใช้ try-except และ retry เข้ามาช่วยครับ
ลองปรับโค้ดในไฟล์ app.py ของเราแบบนี้ดูนะครับ
# app.py (เพิ่มส่วนของ Resilience)
import time
import requests
from flask import Flask, request, jsonify
app = Flask(__name__)
# จำลองฟังก์ชันที่อาจจะล่มได้ หรือต้อง retry
def process_data_with_retry(data, retries=3, delay=2):
for i in range(retries):
try:
print(f"พยายามประมวลผลข้อมูลครั้งที่ {i+1}...")
# สมมติว่าเราเรียก external service หรือทำอะไรที่อาจจะเกิด error
# เช่น response = requests.post(\\"http://some-external-api.com/process\\", json=data, timeout=5)
# response.raise_for_status() # ตรวจสอบ HTTP errors ถ้ามี
# ตรงนี้เป็นส่วนจำลองให้เกิดความผิดพลาด เพื่อทดสอบ retry
if i < 2 and data.get("error_trigger"):
raise requests.exceptions.ConnectionError(\\"จำลองการเชื่อมต่อล้มเหลวครับ\\")
print(f"ประมวลผลข้อมูลสำเร็จแล้วครับ: {data}")
return True
except requests.exceptions.RequestException as e:
print(f"เกิดข้อผิดพลาดในการเรียก External Service: {e}")
if i < retries - 1:
print(f"จะลองใหม่ในอีก {delay} วินาทีนะครับ...")
time.sleep(delay)
else:
print(\\"ลองหลายครั้งแล้วไม่สำเร็จครับ\\")
return False
except Exception as e:
print(f"เกิดข้อผิดพลาดที่ไม่คาดคิด: {e}")
return False
return False
@app.route('/webhook', methods=['POST'])
def handle_webhook():
try:
data = request.json
if data is None:
return jsonify({\\"status\\": \\"error\\", \\"message\\": \\"ไม่ได้ส่ง JSON มาให้ผมเลยครับ\\"}), 400
print(f\\"ได้รับ Webhook แล้วครับ: {data}\\")
# เรียกใช้ฟังก์ชันที่มี Resilience
if process_data_with_retry(data):
return jsonify({\\"status\\": \\"success\\", \\"message\\": \\"ได้รับและประมวลผลข้อมูลแล้วครับ\\"}), 200
else:
return jsonify({\\"status\\": \\"error\\", \\"message\\": \\"ประมวลผลข้อมูลไม่สำเร็จครับ\\"}), 500
except Exception as e:
print(f\\"เกิดข้อผิดพลาดใน Webhook Handler: {e}\\")
return jsonify({\\"status\\": \\"error\\", \\"message\\": f\\"มีบางอย่างผิดพลาดครับ: {e}\\"}), 500
if __name__ == '__main__':
app.run(port=8000, debug=True)
ในโค้ดตัวอย่าง process_data_with_retry จะพยายามทำงานซ้ำ 3 ครั้ง (retries=3) ถ้าเกิดปัญหาขึ้นมา โดยจะรอ 2 วินาที (delay=2) ก่อนจะลองใหม่ครับ และถ้าใน data มี error_trigger เป็น true ก็จะจำลองให้เกิด error ขึ้น 2 ครั้งแรก เพื่อให้เราเห็นว่ามันพยายาม retry จริงๆ นะครับ
5. ลองทดสอบส่ง Webhook
ตอนนี้เราก็สามารถลองส่ง Webhook เข้ามาทดสอบได้เลยครับ โดยใช้ curl หรือเครื่องมืออื่นๆ ก็ได้ครับ อย่าลืมเปลี่ยน <YOUR_NGROK_URL> เป็น URL ที่ ngrok ให้มานะครับ
ทดสอบส่งข้อมูลปกติ:
curl -X POST -H "Content-Type: application/json" -d '{\\"name\\": \\"นายทดสอบ\\", \\"action\\": \\"ซื้อสินค้า\\"}' <YOUR_NGROK_URL>/webhook
ทดสอบส่งข้อมูลที่จำลองให้เกิด error (เพื่อดู Resilience):
curl -X POST -H "Content-Type: application/json" -d '{\\"name\\": \\"นางสาวปัญหา\\", \\"action\\": \\"อัปเดตสถานะ\\", \\"error_trigger\\": true}' <YOUR_NGROK_URL>/webhook
ลองส่งแบบมี error_trigger ดูนะครับ จะเห็นว่าโปรแกรมของเราจะพยายามประมวลผลซ้ำหลายครั้ง ก่อนจะบอกว่าไม่สำเร็จครับ ซึ่งถ้าเป็นเหตุการณ์จริงที่เกิดจากปัญหาชั่วคราว เช่น เน็ตกระตุก หรือ External API ล่มไปแวบนึงเนี่ย ระบบเราก็จะรอดจากการล่มไปเลยนะครับ
เป็นไงบ้างครับ เทคนิคนี้ช่วยให้เราพัฒนาและทดสอบ Webhook ได้ง่ายขึ้นเยอะเลยใช่ไหมครับ แถมยังทำให้โปรแกรมเราอึดขึ้นอีกด้วย ลองเอาไปปรับใช้ในโปรเจกต์ของเพื่อนๆ ดูนะครับ
สวัสดีครับ
อ้างอิง: * ngrok Official Website: https://ngrok.com/ * Flask Documentation: https://flask.palletsprojects.com/ * Requests Library: https://requests.readthedocs.io/