Robyn 设计文档
服务器模型
Robyn 基于多进程、多线程模型构建,结合了 Python 和 Rust 的优势。它使用一个主进程来管理多个工作进程,每个工作进程可以处理多个线程。这种设计使 Robyn 能够高效利用系统资源并处理大量并发请求。
主进程
Robyn 的主进程负责初始化服务器、管理工作进程并处理信号。它创建一个套接字,并将其传递给工作进程,允许它们接受连接。主进程使用 Python 实现,为开发者提供熟悉的接口,同时利用 Rust 在核心操作中的高性能。
216:257:robyn/init.py
def start(self, host: str = "127.0.0.1", port: int = 8080, _check_port: bool = True):
"""
启动服务器
:param host str: 服务器监听的主机地址
:param port int: 服务器监听的端口号
:param _check_port bool: 是否检查端口是否已被占用
"""
host = os.getenv("ROBYN_HOST", host)
port = int(os.getenv("ROBYN_PORT", port))
open_browser = bool(os.getenv("ROBYN_BROWSER_OPEN", self.config.open_browser))
if _check_port:
while self.is_port_in_use(port):
logger.error("端口 %s 已被占用,请使用其他端口。", port)
try:
port = int(input("Enter a different port: "))
except Exception:
logger.error("无效的端口号,请输入有效的端口号。")
continue
logger.info("Robyn 版本:%s", __version__)
logger.info("正在启动服务器,地址:http://%s:%s", host, port)
mp.allow_connection_pickling()
run_processes(
host,
port,
self.directories,
self.request_headers,
self.router.get_routes(),
self.middleware_router.get_global_middlewares(),
self.middleware_router.get_route_middlewares(),
self.web_socket_router.get_routes(),
self.event_handlers,
self.config.workers,
self.config.processes,
self.response_headers,
open_browser,
)
工作进程
Robyn 使用多个工作进程来处理传入的请求。每个工作进程能够管理多个线程,从而实现高效的并发处理。工作进程的数量可以通过 --processes
参数配置,默认值为 1。
66:116:robyn/processpool.py
def init_processpool(
directories: List[Directory],
request_headers: Headers,
routes: List[Route],
global_middlewares: List[GlobalMiddleware],
route_middlewares: List[RouteMiddleware],
web_sockets: Dict[str, WebSocket],
event_handlers: Dict[Events, FunctionInfo],
socket: SocketHeld,
workers: int,
processes: int,
response_headers: Headers,
) -> List[Process]:
process_pool = []
if sys.platform.startswith("win32") or processes == 1:
spawn_process(
directories,
request_headers,
routes,
global_middlewares,
route_middlewares,
web_sockets,
event_handlers,
socket,
workers,
response_headers,
)
return process_pool
for _ in range(processes):
copied_socket = socket.try_clone()
process = Process(
target=spawn_process,
args=(
directories,
request_headers,
routes,
global_middlewares,
route_middlewares,
web_sockets,
event_handlers,
copied_socket,
workers,
response_headers,
),
)
process.start()
process_pool.append(process)
return process_pool
工作线程
在每个工作进程中,Robyn 使用多个线程并发处理请求。工作线程的数量可以通过 --workers
参数配置。默认情况下,Robyn 每个进程使用一个工作线程。
Rust 集成
Robyn 的一个独特特性是与 Rust 的集成。核心的服务器功能,包括请求处理和路由,都是用 Rust 实现的。这使得 Robyn 在提供 Python 友好的 API 的同时,能够实现高性能。
76:107:src/server.rs
pub fn start(
&mut self,
py: Python,
socket: &PyCell<SocketHeld>,
workers: usize,
) -> PyResult<()> {
pyo3_log::init();
if STARTED
.compare_exchange(false, true, SeqCst, Relaxed)
.is_err()
{
debug!("Robyn 已经启动...");
return Ok(());
}
let raw_socket = socket.try_borrow_mut()?.get_socket();
let router = self.router.clone();
let const_router = self.const_router.clone();
let middleware_router = self.middleware_router.clone();
let web_socket_router = self.websocket_router.clone();
let global_request_headers = self.global_request_headers.clone();
let global_response_headers = self.global_response_headers.clone();
let directories = self.directories.clone();
let asyncio = py.import("asyncio")?;
let event_loop = asyncio.call_method0("new_event_loop")?;
asyncio.call_method1("set_event_loop", (event_loop,))?;
let startup_handler = self.startup_handler.clone();
let shutdown_handler = self.shutdown_handler.clone();
常量请求(Const Requests)
Robyn 引入了一个优化功能,称为“常量请求”。这个功能允许某些路由在 Rust 层进行缓存,从而减少频繁访问的端点的开销,这些端点返回常量数据。
选择工作进程和进程配置
通过调整工作进程和进程的数量,Robyn 的性能可以根据系统资源和应用程序的性质进行优化。
进程数量: 设置进程数量为 CPU 核心数(N)。这使得 Robyn 能够充分利用多核系统。
工作线程数量: 使用公式
2 * N + 1
,其中 N 是 CPU 核心数。这个配置有助于高效处理 I/O 密集型和 CPU 密集型任务。
例如,在一个 4 核的机器上:
python app.py --processes=4 --workers=9
请记住,最佳配置可能会因应用程序和服务器资源的不同而有所变化。建议进行负载测试,尝试不同的配置,以找到最适合您用例的平衡。
扩展考虑
- Robyn 的多进程模型使其能够有效地跨多个 CPU 核心扩展。
- Python 和 Rust 的结合既便于开发,又能提供高性能。
- 常量请求功能可以显著提高返回常量输出的路由的性能。
- 扩展时,考虑进程和工作线程的数量,以找到适合硬件和应用需求的最佳配置。
开发模式
Robyn 提供了一个开发模式,可以通过 --dev
参数启用。此模式旨在简化开发过程,包含热重载等功能。请注意,在开发者模式下,多进程和多工作线程配置被禁用,以确保开发过程中的一致性。
92:101:robyn/argument_parser.py
if self.dev and (self.processes != 1 or self.workers != 1):
raise Exception("--processes and --workers shouldn't be used with --dev")
if self.dev and args.log_level is None:
self.log_level = "DEBUG"
elif args.log_level is None:
self.log_level = "INFO"
else:
self.log_level = args.log_level
通过理解这些设计原则并根据需要调整配置,开发者可以利用 Robyn 的独特架构构建高性能的 Web 应用程序,充分利用系统资源。
设计图
