← Назад к вопросам

На какие два потока прокси сервер разделяет запрос?

1.8 Middle🔥 141 комментариев
#DevOps и инфраструктура#Асинхронность и многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Краткий ответ

Прокси сервер разделяет обработку запроса на два основных потока (или задачи):

  1. Входящий поток (Inbound) - обработка запроса от клиента
  2. Исходящий поток (Outbound) - отправка запроса на целевой сервер и получение ответа

Детальное объяснение

Поток 1: Входящий (Inbound) - от клиента к прокси

Этот поток отвечает за:

  • Приём HTTP запроса от клиента
  • Парсинг заголовков и тела запроса
  • Валидация запроса
  • Применение входящих правил (filtering, rate limiting, authentication)
  • Буферизация данных запроса если необходимо
  • Трансформация запроса (изменение заголовков, URL переписывание)
import asyncio
from aiohttp import web

class ProxyServer:
    async def handle_inbound(self, request):
        # Поток 1: Входящий
        print(f'Received request: {request.method} {request.path}')
        print(f'Headers: {dict(request.headers)}')
        
        # Валидация и обработка входящего запроса
        if not self.is_valid_request(request):
            return web.Response(status=400, text='Invalid request')
        
        # Преобразование запроса если нужно
        modified_request = self.transform_request(request)
        
        return modified_request

Поток 2: Исходящий (Outbound) - от прокси к целевому серверу

Этот поток отвечает за:

  • Отправку модифицированного запроса на целевой сервер
  • Получение ответа от целевого сервера
  • Обработка ошибок соединения (retry, timeout)
  • Кэширование ответа если нужно
  • Применение исходящих правил (compression, response modification)
  • Отправку ответа обратно клиенту
import aiohttp
import asyncio

class ProxyServer:
    async def handle_outbound(self, request, target_url):
        # Поток 2: Исходящий
        try:
            async with aiohttp.ClientSession() as session:
                # Отправляем запрос на целевой сервер
                async with session.request(
                    method=request.method,
                    url=target_url,
                    headers=request.headers,
                    data=await request.read(),
                    timeout=aiohttp.ClientTimeout(total=30)
                ) as response:
                    # Получаем ответ
                    response_data = await response.read()
                    
                    # Применяем исходящие правила
                    modified_response = self.transform_response(
                        response.status,
                        response.headers,
                        response_data
                    )
                    
                    return modified_response
        
        except asyncio.TimeoutError:
            return web.Response(status=504, text='Gateway Timeout')
        except aiohttp.ClientError as e:
            return web.Response(status=502, text=f'Bad Gateway: {e}')

Полный пример асинхронного прокси

import aiohttp
from aiohttp import web
import asyncio
from typing import Tuple

class AsyncProxy:
    def __init__(self, target_base_url: str):
        self.target_base_url = target_base_url
    
    async def handle_request(self, request: web.Request) -> web.StreamResponse:
        """
        Главный обработчик который координирует оба потока
        """
        # Поток 1: Входящий (Inbound)
        inbound_result = await self._handle_inbound(request)
        if isinstance(inbound_result, web.Response):
            return inbound_result  # Ошибка валидации
        
        modified_request = inbound_result
        
        # Поток 2: Исходящий (Outbound)
        return await self._handle_outbound(modified_request, request)
    
    async def _handle_inbound(self, request: web.Request):
        """
        Поток 1: Входящий - обработка от клиента
        """
        print(f'[INBOUND] {request.method} {request.path}')
        
        # Валидация
        if len(request.path) > 2000:
            return web.Response(status=414, text='URI Too Long')
        
        # Rate limiting (пример)
        if not self._check_rate_limit(request.remote):
            return web.Response(status=429, text='Too Many Requests')
        
        # Логирование
        print(f'[INBOUND] Headers: {dict(request.headers)}')
        
        # Готовим данные для исходящего потока
        inbound_data = {
            'method': request.method,
            'path': request.path,
            'query': request.query_string,
            'headers': dict(request.headers),
            'body': await request.read()
        }
        
        return inbound_data
    
    async def _handle_outbound(self, inbound_data: dict, 
                              original_request: web.Request) -> web.StreamResponse:
        """
        Поток 2: Исходящий - отправка на целевой сервер
        """
        target_url = f"{self.target_base_url}{inbound_data['path']}"
        
        if inbound_data['query']:
            target_url += f"?{inbound_data['query']}"
        
        print(f'[OUTBOUND] Forwarding to {target_url}')
        
        try:
            async with aiohttp.ClientSession() as session:
                # Отправляем на целевой сервер
                async with session.request(
                    method=inbound_data['method'],
                    url=target_url,
                    headers=inbound_data['headers'],
                    data=inbound_data['body'],
                    timeout=aiohttp.ClientTimeout(total=30),
                    ssl=False  # Для самоподписанных сертификатов
                ) as response:
                    print(f'[OUTBOUND] Response status: {response.status}')
                    
                    # Читаем ответ
                    response_body = await response.read()
                    
                    # Создаём ответ для клиента
                    return web.Response(
                        status=response.status,
                        body=response_body,
                        headers=response.headers,
                        content_type=response.content_type
                    )
        
        except asyncio.TimeoutError:
            print('[OUTBOUND] Timeout connecting to target')
            return web.Response(status=504, text='Gateway Timeout')
        
        except aiohttp.ClientConnectorError as e:
            print(f'[OUTBOUND] Connection error: {e}')
            return web.Response(status=502, text='Bad Gateway')
    
    def _check_rate_limit(self, client_ip: str) -> bool:
        # Простая реализация rate limiting
        return True

# Запуск
if __name__ == '__main__':
    app = web.Application()
    proxy = AsyncProxy('http://example.com')
    
    app.router.add_route('*', '/{path_info:.*}', proxy.handle_request)
    
    web.run_app(app, host='127.0.0.1', port=8080)

Синхронный пример (с использованием threading)

import requests
from flask import Flask, request, Response
import threading

app = Flask(__name__)
TARGET_URL = 'http://example.com'

@app.route('/', defaults={'path': ''}, methods=['GET', 'POST', 'PUT', 'DELETE'])
@app.route('/<path:path>', methods=['GET', 'POST', 'PUT', 'DELETE'])
def proxy(path):
    
    # Поток 1: Входящий (Inbound)
    def handle_inbound():
        print(f'[INBOUND] {request.method} {request.path}')
        return {
            'method': request.method,
            'path': path,
            'headers': dict(request.headers),
            'data': request.get_data()
        }
    
    # Поток 2: Исходящий (Outbound)
    def handle_outbound(inbound_data):
        url = f"{TARGET_URL}/{inbound_data['path']}"
        print(f'[OUTBOUND] Forwarding to {url}')
        
        try:
            response = requests.request(
                method=inbound_data['method'],
                url=url,
                headers=inbound_data['headers'],
                data=inbound_data['data'],
                timeout=30,
                allow_redirects=True
            )
            
            return Response(
                response.content,
                status=response.status_code,
                headers=dict(response.headers)
            )
        
        except requests.Timeout:
            return Response('Gateway Timeout', status=504)
        except requests.RequestException as e:
            return Response(f'Bad Gateway: {e}', status=502)
    
    inbound_data = handle_inbound()
    return handle_outbound(inbound_data)

if __name__ == '__main__':
    app.run(host='127.0.0.1', port=8080)

Ключевые различия между потоками

ХарактеристикаВходящий (Inbound)Исходящий (Outbound)
ИсточникКлиентПрокси сервер
НазначениеПрокси серверЦелевой сервер
ЗадачиВалидация, трансформацияОтправка, получение, кэширование
НаправлениеКлиент → ПроксиПрокси → Сервер
Обработка ошибок4xx ошибки5xx ошибки

Практическое применение

Этот паттерн используется в:

  • nginx/Apache - веб прокси
  • API Gateway - API прокси
  • Load Balancer - распределение нагрузки
  • Reverse Proxy - кэширование и ускорение
  • VPN/HTTP Proxy - анонимизация

Вывод: Прокси сервер разделяет обработку на входящий поток (от клиента) и исходящий поток (к целевому серверу), что позволяет независимо обрабатывать, трансформировать и оптимизировать каждую сторону соединения.