Feature:Reserve HTTP server port before model loading to immediately detect port conflicts instead of failing after several minutes of model loading. (#17754)

Signed-off-by: xrwang8 <xrwang8@gmail.com>
This commit is contained in:
xrwang8
2026-03-04 03:59:18 +08:00
committed by GitHub
parent 2e1b9e2547
commit cedb86a950
2 changed files with 148 additions and 71 deletions

View File

@@ -409,6 +409,75 @@ def reserve_port(host, start=30000, end=40000):
raise RuntimeError("No free port available.")
def _prebind_listening_socket(host: str, port: int) -> socket.socket:
"""
Create and bind a server socket to reserve the port before model loading.
This prevents port conflicts that could occur if another process (e.g., a
subprocess spawned during model loading) binds to the same port before the
HTTP server starts. By binding early, we ensure the port is available and
reserved for the HTTP server.
Args:
host: The host address to bind to.
port: The port number to bind to.
Returns:
A bound socket object.
Raises:
RuntimeError: If the port is already in use or permission is denied.
"""
import errno
from sglang.srt.utils.common import is_valid_ipv6_address
# Determine socket family based on address type
if host and is_valid_ipv6_address(host):
family = socket.AF_INET6
else:
family = socket.AF_INET
sock = socket.socket(family=family, type=socket.SOCK_STREAM)
# Set socket options to allow port reuse
# SO_REUSEADDR: Allows binding to a port in TIME_WAIT state
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# SO_REUSEPORT: Allows multiple processes to bind to the same port
# This is useful for multi-worker mode
if hasattr(socket, "SO_REUSEPORT"):
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
try:
sock.bind((host or "", port))
sock.listen(128) # Put socket in listening state for uvicorn
sock.set_inheritable(True)
logger.info(
f"Successfully reserved port {port} on host '{host or ('::' if family == socket.AF_INET6 else '0.0.0.0')}'"
)
except OSError as e:
sock.close()
if e.errno == errno.EADDRINUSE:
raise RuntimeError(
f"Port {port} is already in use on host '{host or ('::' if family == socket.AF_INET6 else '0.0.0.0')}'. "
f"Please choose a different port with --port or stop the process using this port. "
f"You can find the process using: lsof -i :{port} or netstat -tlnp | grep {port}"
) from e
elif e.errno == errno.EACCES:
raise RuntimeError(
f"Permission denied when trying to bind to port {port}. "
f"Ports below 1024 require root/sudo privileges. "
f"Consider using a port >= 1024 (e.g., --port 8000) or run with sudo."
) from e
else:
raise RuntimeError(
f"Failed to bind to {host or ('::' if family == socket.AF_INET6 else '0.0.0.0')}:{port}: {e}"
) from e
return sock
def release_port(lock_socket):
"""
Release the reserved port by closing the lock socket.