Files
dify/api/extensions/ext_socketio.py
非法操作 e13271ba29 fix: support multi-worker workflow collaboration (#38242)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Blackoutta <hyytez@gmail.com>
Co-authored-by: Blackoutta <37723456+Blackoutta@users.noreply.github.com>
2026-07-02 08:16:03 +00:00

73 lines
2.5 KiB
Python

import ssl
from typing import Any, cast
from urllib.parse import urlparse
import socketio # type: ignore[reportMissingTypeStubs]
from configs import dify_config
from extensions.redis_names import serialize_redis_name
SOCKETIO_REDIS_CHANNEL = "socketio"
def _get_ssl_cert_reqs() -> ssl.VerifyMode:
cert_reqs_map = {
"CERT_NONE": ssl.CERT_NONE,
"CERT_OPTIONAL": ssl.CERT_OPTIONAL,
"CERT_REQUIRED": ssl.CERT_REQUIRED,
}
return cert_reqs_map.get(dify_config.REDIS_SSL_CERT_REQS, ssl.CERT_NONE)
def _build_redis_options(redis_url: str) -> dict[str, Any]:
"""Build Redis options for Socket.IO's cross-process pub/sub manager."""
options: dict[str, Any] = {
"socket_timeout": dify_config.REDIS_SOCKET_TIMEOUT,
"socket_connect_timeout": dify_config.REDIS_SOCKET_CONNECT_TIMEOUT,
"health_check_interval": dify_config.REDIS_HEALTH_CHECK_INTERVAL,
"protocol": dify_config.REDIS_SERIALIZATION_PROTOCOL,
}
if dify_config.REDIS_MAX_CONNECTIONS:
options["max_connections"] = dify_config.REDIS_MAX_CONNECTIONS
if urlparse(redis_url).scheme == "rediss":
options.update(
{
"ssl_cert_reqs": _get_ssl_cert_reqs(),
"ssl_ca_certs": dify_config.REDIS_SSL_CA_CERTS,
"ssl_certfile": dify_config.REDIS_SSL_CERTFILE,
"ssl_keyfile": dify_config.REDIS_SSL_KEYFILE,
}
)
return options
def create_socketio_client_manager() -> Any:
"""
Create the Socket.IO manager used to fan out room events across API workers.
Workflow collaboration relies on room broadcasts and direct emits to a collaborator's sid. The default in-memory
manager only reaches clients attached to the current process, so horizontal websocket workers must share a Redis
pub/sub channel. The channel name follows Dify's Redis key prefix to keep independent deployments isolated.
"""
redis_url = dify_config.normalized_pubsub_redis_url
return socketio.RedisManager(
redis_url,
channel=serialize_redis_name(SOCKETIO_REDIS_CHANNEL),
redis_options=_build_redis_options(redis_url),
)
# TODO: FIXME(chariri) - Casting to any because app_factory attaches the
# current app as the `app` attribute on this - Bad.
sio = cast(
Any,
socketio.Server(
async_mode="gevent",
client_manager=create_socketio_client_manager(),
cors_allowed_origins=dify_config.CONSOLE_CORS_ALLOW_ORIGINS,
),
)