WSGI (Web Server Gateway Interface) es una especificación que define cómo interactúan los servidores web con las aplicaciones Python. No se trata de un servidor, módulo o framework, sino de un estándar documentado en el PEP 3333 que establece la comunicación entre el servidor y la aplicación. Frameworks como Flask, Django y Bottle operan sobre esta especificación.
Por otro lado, uwsgi es un protocolo de comunicación propio del servidor uWSGI, diseñado para definir el tipo de información transmitida. Cada paquete uwsgi incluye en sus primeros 4 bytes el descrpitor del tipo de datos. Este protocolo es distinto a WSGI y ofrece un rendimiento significativamente superior al protocolo FastCGI.
uWSGI es un servidor web que soporta múltiples protocolos incluyendo WSGI, uwsgi y HTTP, diseñado para despliegues de aplicaciones distribuidas con alta escalabilidad.
Arquitectura WSGI
El protocolo WSGI se compone de dos elementos principales:
- Servidor WSGI: Recibe las peticiones HTTP de los clientes, las reenvía a la aplicación y devuelve la respuesta generada al cliente.
- Aplicación WSGI: Procesa las peticiones provenientes del servidor y genera las respuestas. Puede incluir middleware que actúa como intermediario, funcionando como aplicación frente al servidor y como servidor frente a la aplicación.
Esta separación permite combinar libremente servidores (como Gunicorn o uWSGI) con frameworks (como Django o Flask) según las necesidades del proyecto.
Implementación de la Aplicación WSGI
La aplicación WSGI debe ser un objeto invocable que acepte dos parámetros: un diccionario con el contexto de la petición (típicamente llamado environ) y una función callback para enviar el estado HTTP y las cabeceras de respuesta.
A continuación se muestra una implementación simplificada inspirada en Django:
class WSGIApplicationHandler(BaseRequestHandler):
def __init__(self):
self._middleware_chain = None
self._sync_lock = threading.Lock()
def __call__(self, request_env, response_callback):
self._ensure_middleware_loaded()
self._configure_script_prefix(request_env)
try:
parsed_request = self._build_request(request_env)
result = self._process_request(parsed_request)
except UnicodeDecodeError:
result = self._build_bad_request_response()
status_line = f"{result.status_code} {result.reason_phrase}"
headers = [(str(key), str(val)) for key, val in result.items()]
for cookie in result.cookies.values():
headers.append(('Set-Cookie', cookie.output(header='')))
response_callback(status_line, headers)
if hasattr(result, 'file_to_stream') and result.file_to_stream:
if request_env.get('wsgi.file_wrapper'):
return request_env['wsgi.file_wrapper'](result.file_to_stream)
return result
def _ensure_middleware_loaded(self):
if self._middleware_chain is None:
with self._sync_lock:
if self._middleware_chain is None:
self._middleware_chain = self._load_middleware_stack()
def _process_request(self, req):
return self._middleware_chain(req)
Implementación del Servidor WSGI
El servidor WSGI gestiona las conexiones HTTP y delega el procesamiento a la aplicación. Una implementación básica se estructuraría de la siguiente manera:
def launch_wsgi_server(host_addr, port_num, app_callable, use_threads=False):
bind_address = (host_addr, port_num)
if use_threads:
server_impl = type('ThreadedWSGIServer',
(socketserver.ThreadingMixIn, BaseWSGIServer),
{})
server_impl.daemon_threads = True
else:
server_impl = BaseWSGIServer
http_server = server_impl(bind_address, DefaultRequestHandler)
http_server.register_application(app_callable)
http_server.run_forever()
class BaseWSGIServer(socketserver.HTTPServer):
def __init__(self, server_addr, request_handler_cls, ipv6_support=False):
self._app_instance = None
super().__init__(server_addr, request_handler_cls)
def register_application(self, app):
self._app_instance = app
def fetch_application(self):
return self._app_instance
def handle_request(self):
raw_request, client_info = self.socket.accept()
handler = self.request_handler_cls(raw_request, client_info, self)
handler.process_request()
class RequestProcessor:
def __init__(self, raw_socket, client_addr, server_ref):
self._socket = raw_socket
self._client = client_addr
self._server = server_ref
def process_request(self):
environ_dict = self._parse_http_request()
app = self._server.fetch_application()
def send_response_header(status, headers):
self._write_headers(status, headers)
response_body = app(environ_dict, send_response_header)
self._stream_response(response_body)
def _parse_http_request(self):
# Parsea el socket y construye el diccionario environ
pass
def _write_headers(self, status, headers):
# Escribe las cabeceras HTTP al socket
pass
def _stream_response(self, body_chunks):
for chunk in body_chunks:
self._socket.sendall(chunk)
Servidor de Demostración
Un ejemplo completo y funcional que muestra la interacción entre servidor y aplicación WSGI:
from io import StringIO
def create_server(host, port, application, server_cls=BaseWSGIServer, handler_cls=DefaultRequestHandler):
instance = server_cls((host, port), handler_cls)
instance.register_application(application)
return instance
def sample_application(env, start_response_fn):
output_buffer = StringIO()
output_buffer.write("Respuesta del servidor WSGI\n\n")
for key in sorted(env.keys()):
output_buffer.write(f"{key} = {env[key]!r}\n")
start_response_fn("200 OK", [("Content-Type", "text/plain; charset=utf-8")])
return [output_buffer.getvalue().encode("utf-8")]
if __name__ == '__main__':
http_server = create_server('', 8000, sample_application)
socket_info = http_server.socket.getsockname()
print(f"Servidor activo en {socket_info[0]}:{socket_info[1]}")
http_server.handle_request()
http_server.server_close()
Características de uWSGI
El servidor uWSGI ofrece capacidades avanzadas para despliegues en producción:
- Alto rendimiento con baja latencia
- Consumo eficiente de memoria
- Gestión de múltiples aplicaciones simultáneas
- Sistema de logging detallado para análisis de rendimiento
- Configuración flexible con opciones de reinicio automático y límites de recursos
uWSGI implementa nativamente el protocolo uwsgi, por lo que solo es necesario configurar la ruta del módulo de la aplicación en su archivo de configuración para establecer la comunicación directa con el WSGI application del framework elegido.