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/

Read more

PSG vs Monaco: ศึก 100 นัดเดือด ลีกเอิง และบทเรียนที่ปาร์ค เดส์ แพร็งซ์

PSG vs Monaco: ศึก 100 นัดเดือด ลีกเอิง และบทเรียนที่ปาร์ค เดส์ แพร็งซ์

สรุปผลและวิเคราะห์เกมเดือด PSG พบ Monaco ในลีกเอิงนัดที่ 25 ซึ่งเป็นการพบกันครั้งที่ 100 ในประวัติศาสตร์ลีก ความพ่ายแพ้ 1-3 คาบ้านของ PSG และบทบาทของ Akliouche พร้อมผลกระทบต่อเส้นทางแชมเปี้ยนส์ลีก

By ทีมงาน devdog
บาเยิร์นผงาดไร้เคน! ถล่มกลัดบัค 4-1 โชว์ความลึกของทีมก่อนลุยศึก UCL

บาเยิร์นผงาดไร้เคน! ถล่มกลัดบัค 4-1 โชว์ความลึกของทีมก่อนลุยศึก UCL

บาเยิร์น มิวนิค โชว์ฟอร์มแกร่ง แม้ไม่มีแฮร์รี่ เคน ถล่ม โบรุสเซีย มึนเชนกลัดบัค 4-1 ก่อนเตรียมลุยศึกแชมเปียนส์ลีกกับอตาลันต้า!

By ทีมงาน devdog
PSG: มหาอำนาจลูกหนังฝรั่งเศส กับศึกดวลเดือดโมนาโก และเป้าหมายสู่บัลลังก์ยุโรป

PSG: มหาอำนาจลูกหนังฝรั่งเศส กับศึกดวลเดือดโมนาโก และเป้าหมายสู่บัลลังก์ยุโรป

เจาะลึกเส้นทาง PSG สู่มหาอำนาจลูกหนัง วิเคราะห์สถานการณ์ลีกเอิง เตรียมพร้อมศึกใหญ่กับโมนาโก พร้อมความมุ่งมั่นสู่แชมป์ยุโรป

By ทีมงาน devdog
ลาลีกา: มนต์เสน่ห์ฟุตบอลสเปน, นวัตกรรมเรโทร, และบิ๊กแมตช์แห่งอนาคต

ลาลีกา: มนต์เสน่ห์ฟุตบอลสเปน, นวัตกรรมเรโทร, และบิ๊กแมตช์แห่งอนาคต

สำรวจลาลีกา ฟุตบอลสเปนอันทรงเสน่ห์ พร้อมไฮไลต์บิ๊กแมตช์ 2025/26 นวัตกรรมสัปดาห์เรโทร และบทบาทต่อวัฒนธรรมและเศรษฐกิจ.

By ทีมงาน devdog