From 6394a8b3816944009d18bd105a64c5508e8e2b61 Mon Sep 17 00:00:00 2001 From: Shangming Cai Date: Mon, 8 Jun 2026 19:08:29 +0800 Subject: [PATCH] [EPD] Dynamic encoder registration cleanup (#27542) --- .../srt/disaggregation/encode_receiver.py | 3 +- .../srt/disaggregation/encode_server.py | 41 ++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/python/sglang/srt/disaggregation/encode_receiver.py b/python/sglang/srt/disaggregation/encode_receiver.py index b83cd6dc0..2f43b7c50 100644 --- a/python/sglang/srt/disaggregation/encode_receiver.py +++ b/python/sglang/srt/disaggregation/encode_receiver.py @@ -153,9 +153,9 @@ class EncoderBootstrapServer: def register(self, url: str) -> bool: """Add *url* if not already present. Returns True if added.""" with self._lock: + self._consecutive_failures.pop(url, None) if url not in self._urls: self._urls.append(url) - self._consecutive_failures.pop(url, None) logger.info(f"Registered encoder URL: {url}") return True logger.debug(f"Encoder URL already registered: {url}") @@ -166,6 +166,7 @@ class EncoderBootstrapServer: with self._lock: if url in self._urls: self._urls.remove(url) + self._consecutive_failures.pop(url, None) logger.info(f"Unregistered encoder URL: {url}") return True return False diff --git a/python/sglang/srt/disaggregation/encode_server.py b/python/sglang/srt/disaggregation/encode_server.py index 9e3435f0d..7f5bce2b7 100644 --- a/python/sglang/srt/disaggregation/encode_server.py +++ b/python/sglang/srt/disaggregation/encode_server.py @@ -3141,7 +3141,11 @@ def _register_encoder_url_with_bootstrap(server_args: ServerArgs): instead of serialising sleeps in a single thread. """ - encoder_url = server_args.url() + host = server_args.host + if not host or host in ("0.0.0.0", "::"): + host = get_local_ip_auto(server_args.host) + scheme = "https" if server_args.ssl_certfile else "http" + encoder_url = NetworkAddress(host, server_args.port).to_url(scheme) payload = {"url": encoder_url} bootstrap_urls = list(server_args.encoder_register_urls) if not bootstrap_urls: @@ -3197,6 +3201,35 @@ def _register_encoder_url_with_bootstrap(server_args: ServerArgs): ).start() +def _unregister_encoder_url_from_bootstrap(server_args: ServerArgs): + host = server_args.host + if not host or host in ("0.0.0.0", "::"): + host = get_local_ip_auto(server_args.host) + scheme = "https" if server_args.ssl_certfile else "http" + encoder_url = NetworkAddress(host, server_args.port).to_url(scheme) + payload = {"url": encoder_url} + + for bootstrap_url in server_args.encoder_register_urls: + try: + resp = http_requests.delete( + f"{bootstrap_url}/unregister_encoder_url", + json=payload, + timeout=2.0, + ) + if resp.status_code == 200: + logger.info( + f"Unregistered encoder URL '{encoder_url}' from " + f"bootstrap at {bootstrap_url}" + ) + else: + logger.warning( + f"Bootstrap {bootstrap_url} returned " + f"{resp.status_code} on unregister: {resp.text}" + ) + except Exception as e: + logger.debug(f"Unregister from {bootstrap_url} failed: {e}") + + def launch_server(server_args: ServerArgs): configure_logger(server_args, prefix=" encode_server") if server_args.dp_size > 1: @@ -3229,7 +3262,10 @@ def launch_server(server_args: ServerArgs): # Register this encoder's URL with prefill server(s) if configured. if server_args.encoder_register_urls: + import atexit + _register_encoder_url_with_bootstrap(server_args) + atexit.register(_unregister_encoder_url_from_bootstrap, server_args) uvicorn.run(app, host=server_args.host, port=server_args.port) @@ -3305,7 +3341,10 @@ def _launch_server_dp(server_args: ServerArgs): # Register this encoder's URL with prefill server(s) if configured. if server_args.encoder_register_urls: + import atexit + _register_encoder_url_with_bootstrap(server_args) + atexit.register(_unregister_encoder_url_from_bootstrap, server_args) uvicorn.run(app, host=server_args.host, port=server_args.port)