خطای دروازه از کدام بخش مسیر می‌آید؟

خطای ۵۰۲ یعنی واسط یا پراکسی از سرویس بالادستی پاسخ معتبر نگرفته است. این پیام به‌تنهایی مشخص نمی‌کند مشکل از خاموشی برنامه، درگاه اشتباه، ناسازگاری پروتکل یا قطع ارتباط است. پیش از افزایش زمان انتظار، مسیر درخواست را روی کاغذ بنویسید: مرورگر، شبکهٔ توزیع محتوا، پراکسی و برنامه. باید بدانید کدام لایه پاسخ خطا را ساخته است تا تغییر شما به همان نقطه مربوط باشد.

اول سرویس مقصد را از همان محیط شبکه‌ای پراکسی آزمایش کنید. پاسخ موفق از لپ‌تاپ مدیر ثابت نمی‌کند کانتینر پراکسی به همان نشانی دسترسی دارد. نشانی محلی داخل هر کانتینر به خودش اشاره می‌کند. در نمونهٔ این مقاله، هر دو فرایند روی یک میزبان آزمایش اجرا می‌شوند؛ برای انتقال به کانتینر باید نام سرویس، درگاه داخلی و سیاست شبکه را مطابق معماری واقعی تغییر دهید.

آزمایش مستقل مقصد و ثبت شواهد درخواست

ثبت شناسهٔ درخواست و زمان اتصال کمک می‌کند رخداد را در لاگ پراکسی و برنامه کنار هم ببینید. خطای اتصال ردشده با تأخیر طولانی دریافت سرآیند یکسان نیست و نسخهٔ عمومی یک تنظیم برای هر دو مناسب نیست. نمونه زمان اتصال و دریافت را محدود نگه می‌دارد. افزایش کورکورانهٔ این زمان‌ها ممکن است فقط صف درخواست و مصرف اتصال را بیشتر کند، بدون اینکه علت خرابی برنامه رفع شود.

در پوشهٔ جداگانه، python upstream.py را اجرا کنید. برای انجین‌اکس، ابتدا پیکربندی نمونه را با گزینهٔ آزمون تنظیمات بررسی و سپس همان فایل را با مسیر مطلق بارگذاری کنید؛ این مثال برای لینوکس و لاگ استاندارد طراحی شده است. درگاه‌های ۸۰۹۰ و ۸۰۹۱ باید آزاد باشند. ابزار python diagnose.py وضعیت دروازه و سرویس مستقیم را جداگانه گزارش می‌کند و هیچ سرویس یا تنظیمی را تغییر نمی‌دهد.

با python -m unittest test_diagnose.py آزمون تماس واقعی با یک سرور محلی و تصمیم‌های اصلی ابزار را اجرا کنید. بعد از پاسخ موفق، فقط در محیط آزمایش فرایند مقصد را متوقف و نتیجه را دوباره ثبت کنید. ابزار گزارش می‌دهد مقصد از محل اجرای بررسی در دسترس نیست؛ این نتیجه را با لاگ همان لحظه مقایسه کنید. آزمون خودکار پایتون، صحت پیکربندی انجین‌اکس نصب‌شده روی سرور شما را اثبات نمی‌کند.

کدهای کامل و قابل ذخیره

چهار فایل زیر را در یک پوشه ذخیره کنید. دستورهای اجرا و بررسی در متن آمده‌اند؛ هیچ کد با سه‌نقطه جایگزین نشده است.

1. سرویس مقصد کوچک برای آزمایش

نام فایل: upstream.py؛ همهٔ سطرها را با همین نام ذخیره کنید.

from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        body = b'upstream ready\n'
        self.send_response(200)
        self.send_header('Content-Type', 'text/plain')
        self.send_header('Content-Length', str(len(body)))
        self.end_headers()
        self.wfile.write(body)


if __name__ == '__main__':
    ThreadingHTTPServer(('127.0.0.1', 8091), Handler).serve_forever()

این فایل بخشی از مثال کامل بالاست؛ ترتیب اجرا، پیش‌نیازها و محدودیت‌های نتیجه در متن مقاله توضیح داده شده است.

2. پراکسی محلی با زمان‌های قابل مشاهده

نام فایل: nginx.conf؛ همهٔ سطرها را با همین نام ذخیره کنید.

worker_processes 1;
pid nginx-demo.pid;
error_log stderr notice;
events { worker_connections 64; }
http {
  log_format upstream_timing '$request_id $status upstream=$upstream_status '
    'connect=$upstream_connect_time header=$upstream_header_time total=$request_time';
  access_log /dev/stdout upstream_timing;
  server {
    listen 127.0.0.1:8090;
    location / {
      proxy_pass http://127.0.0.1:8091;
      proxy_set_header Host $host;
      proxy_set_header X-Request-ID $request_id;
      proxy_connect_timeout 2s;
      proxy_read_timeout 5s;
    }
  }
}

این فایل بخشی از مثال کامل بالاست؛ ترتیب اجرا، پیش‌نیازها و محدودیت‌های نتیجه در متن مقاله توضیح داده شده است.

3. بررسی مستقل دو بخش مسیر

نام فایل: diagnose.py؛ همهٔ سطرها را با همین نام ذخیره کنید.

import argparse
import json
import urllib.error
import urllib.request


def probe(url):
    try:
        with urllib.request.urlopen(url, timeout=7) as response:
            return {'status': response.status, 'error': None}
    except urllib.error.HTTPError as error:
        return {'status': error.code, 'error': 'http'}
    except OSError:
        return {'status': None, 'error': 'connection-or-timeout'}


def classify(gateway, upstream):
    if upstream['status'] is None:
        return 'upstream-unreachable-from-this-client'
    if gateway['status'] == 502 and upstream['status'] == 200:
        return 'inspect-proxy-route-protocol-and-network-namespace'
    if gateway['status'] == upstream['status'] == 200:
        return 'healthy-at-check-time'
    return 'inspect-status-and-correlated-server-logs'


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('--gateway', default='http://127.0.0.1:8090/')
    parser.add_argument('--upstream', default='http://127.0.0.1:8091/')
    args = parser.parse_args()
    gateway, upstream = probe(args.gateway), probe(args.upstream)
    print(json.dumps({'gateway': gateway, 'upstream': upstream, 'assessment': classify(gateway, upstream)}))

این فایل بخشی از مثال کامل بالاست؛ ترتیب اجرا، پیش‌نیازها و محدودیت‌های نتیجه در متن مقاله توضیح داده شده است.

4. آزمون تماس واقعی و تصمیم ابزار

نام فایل: test_diagnose.py؛ همهٔ سطرها را با همین نام ذخیره کنید.

import threading
import unittest
from http.server import ThreadingHTTPServer
from diagnose import classify, probe
from upstream import Handler


class DiagnosisTests(unittest.TestCase):
    def test_real_upstream(self):
        server = ThreadingHTTPServer(('127.0.0.1', 0), Handler)
        worker = threading.Thread(target=server.serve_forever, daemon=True)
        worker.start()
        try:
            result = probe(f'http://127.0.0.1:{server.server_port}/')
            self.assertEqual(result['status'], 200)
            self.assertEqual(classify(result, result), 'healthy-at-check-time')
        finally:
            server.shutdown()
            server.server_close()
            worker.join()

    def test_proxy_only_failure(self):
        self.assertIn('proxy-route', classify({'status': 502}, {'status': 200}))

    def test_connection_failure_is_not_http_status(self):
        self.assertIn('unreachable', classify({'status': 502}, {'status': None}))


if __name__ == '__main__':
    unittest.main()

این فایل بخشی از مثال کامل بالاست؛ ترتیب اجرا، پیش‌نیازها و محدودیت‌های نتیجه در متن مقاله توضیح داده شده است.

اجرای محلی و تشخیص بدون تغییر بی‌دلیل تنظیمات

اگر سرویس مستقیم سالم و مسیر پراکسی ناموفق بود، نشانی مقصد، پروتکل، فضای شبکه، مجوز سوکت و خطای ثبت‌شدهٔ همان درخواست را بررسی کنید. تنظیمات سرویس اصلی را پس از بازتولید علت و آزمون پیکربندی تغییر دهید. اطلاعات حساس درخواست را در گزارش عمومی قرار ندهید. نتیجهٔ خوب یک راه‌حل قابل تکرار با شواهد قبل و بعد است، نه صرفاً ناپدیدشدن موقت خطا پس از راه‌اندازی دوباره.

چک‌لیست اجرای عملی

  • مقصد از فضای شبکهٔ خود پراکسی بررسی شود.
  • شناسهٔ درخواست و زمان‌های بالادستی ثبت شود.
  • تنظیمات پیش از بارگذاری آزمون شود.
  • خطای واقعی با لاگ همان لحظه تطبیق داده شود.

منابع

تدوین و راهنمای عملی: دانشنامه لیان.