From d7701074454945cf516cb04cd3bac19433e0b7bd Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sun, 11 Oct 2026 15:51:20 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20sidecar=20=E4=B8=8E=20Info=20=E5=93=8D?= =?UTF-8?q?=E5=BA=94=E5=8A=A0=E7=9B=91=E5=90=AC=E5=9C=B0=E5=9D=80=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 40 ++++++++++++------------- Cargo.toml | 2 +- crates/oxide_cli/src/main.rs | 11 +++++-- crates/oxide_cli/src/server/cleanup.rs | 1 + crates/oxide_cli/src/server/liveness.rs | 1 + crates/oxide_cli/src/server/protocol.rs | 12 ++++++-- crates/oxide_cli/src/server/server.rs | 23 ++++++++++++-- crates/oxide_cli/src/server/sidecar.rs | 19 ++++++++++-- crates/oxide_cli/src/server/watchdog.rs | 1 + crates/oxide_cli/tests/server_cli.rs | 8 ++++- crates/oxide_cli/tests/server_client.rs | 21 ++++++++++--- 11 files changed, 104 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ee4cd6f..d3be7984 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1160,7 +1160,7 @@ dependencies = [ [[package]] name = "oxide_builtins" -version = "0.15.10" +version = "0.15.11" dependencies = [ "chrono", "fast-float", @@ -1185,7 +1185,7 @@ dependencies = [ [[package]] name = "oxide_bytecode" -version = "0.15.10" +version = "0.15.11" dependencies = [ "num-bigint", "oxide_log", @@ -1193,7 +1193,7 @@ dependencies = [ [[package]] name = "oxide_cfg" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxide_bytecode", "oxide_emit", @@ -1204,7 +1204,7 @@ dependencies = [ [[package]] name = "oxide_cli" -version = "0.15.10" +version = "0.15.11" dependencies = [ "ansi_term", "bumpalo", @@ -1228,7 +1228,7 @@ dependencies = [ [[package]] name = "oxide_code_cache" -version = "0.15.10" +version = "0.15.11" dependencies = [ "lru", "oxide_bytecode", @@ -1237,7 +1237,7 @@ dependencies = [ [[package]] name = "oxide_compiler" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1257,7 +1257,7 @@ dependencies = [ [[package]] name = "oxide_dce" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1271,7 +1271,7 @@ dependencies = [ [[package]] name = "oxide_emit" -version = "0.15.10" +version = "0.15.11" dependencies = [ "num-bigint", "oxc_allocator", @@ -1286,7 +1286,7 @@ dependencies = [ [[package]] name = "oxide_engine" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1298,7 +1298,7 @@ dependencies = [ [[package]] name = "oxide_ir" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxide_bytecode", "oxide_log", @@ -1307,7 +1307,7 @@ dependencies = [ [[package]] name = "oxide_kernel" -version = "0.15.10" +version = "0.15.11" dependencies = [ "bumpalo", "dashmap", @@ -1321,7 +1321,7 @@ dependencies = [ [[package]] name = "oxide_liveness" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1333,7 +1333,7 @@ dependencies = [ [[package]] name = "oxide_log" -version = "0.15.10" +version = "0.15.11" dependencies = [ "tracing", "tracing-appender", @@ -1342,11 +1342,11 @@ dependencies = [ [[package]] name = "oxide_net" -version = "0.15.10" +version = "0.15.11" [[package]] name = "oxide_parser" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxc_allocator", "oxc_ast", @@ -1361,7 +1361,7 @@ dependencies = [ [[package]] name = "oxide_regalloc" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxide_bytecode", "oxide_cfg", @@ -1377,7 +1377,7 @@ dependencies = [ [[package]] name = "oxide_runtime_api" -version = "0.15.10" +version = "0.15.11" dependencies = [ "num-bigint", "num-traits", @@ -1390,7 +1390,7 @@ dependencies = [ [[package]] name = "oxide_test262" -version = "0.15.10" +version = "0.15.11" dependencies = [ "oxide_bytecode", "oxide_compiler", @@ -1408,7 +1408,7 @@ dependencies = [ [[package]] name = "oxide_types" -version = "0.15.10" +version = "0.15.11" dependencies = [ "bumpalo", "dashmap", @@ -1421,7 +1421,7 @@ dependencies = [ [[package]] name = "oxide_vm" -version = "0.15.10" +version = "0.15.11" dependencies = [ "bumpalo", "chrono", diff --git a/Cargo.toml b/Cargo.toml index 17158a10..0169fe91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ members = [ ] [workspace.package] -version = "0.15.10" +version = "0.15.11" edition = "2021" license = "MIT" rust-version = "1.80" diff --git a/crates/oxide_cli/src/main.rs b/crates/oxide_cli/src/main.rs index 82400f68..39bf501b 100644 --- a/crates/oxide_cli/src/main.rs +++ b/crates/oxide_cli/src/main.rs @@ -403,9 +403,14 @@ fn server_command(command: ServerCommands) -> ExitCode { _ => None, }), ServerCommands::Info => control_arm(&ServerRequest::Info, |response| match response { - ServerResponse::Info { version, socket_path, pid } => { - Some(format!("版本:{version}\nendpoint:{socket_path}\n进程号:{pid}")) - } + ServerResponse::Info { + version, + socket_path, + pid, + listen_addr, + } => Some(format!( + "版本:{version}\nendpoint:{socket_path}\n监听地址:{listen_addr}\n进程号:{pid}" + )), _ => None, }), ServerCommands::Version => control_arm(&ServerRequest::Version, |response| match response { diff --git a/crates/oxide_cli/src/server/cleanup.rs b/crates/oxide_cli/src/server/cleanup.rs index efecf6d4..011dd68f 100644 --- a/crates/oxide_cli/src/server/cleanup.rs +++ b/crates/oxide_cli/src/server/cleanup.rs @@ -306,6 +306,7 @@ mod tests { started_at: 0, starttime, port: 0, + listen_addr: String::new(), } } diff --git a/crates/oxide_cli/src/server/liveness.rs b/crates/oxide_cli/src/server/liveness.rs index 3efc9000..01e5ba52 100644 --- a/crates/oxide_cli/src/server/liveness.rs +++ b/crates/oxide_cli/src/server/liveness.rs @@ -259,6 +259,7 @@ mod tests { started_at: 0, starttime, port: 0, + listen_addr: String::new(), } } diff --git a/crates/oxide_cli/src/server/protocol.rs b/crates/oxide_cli/src/server/protocol.rs index a84e680f..dd0b28c6 100644 --- a/crates/oxide_cli/src/server/protocol.rs +++ b/crates/oxide_cli/src/server/protocol.rs @@ -172,8 +172,15 @@ pub enum ServerResponse { }, /// 健康检查响应。 Health { healthy: bool }, - /// 信息响应:版本、socket 路径、进程号。 - Info { version: String, socket_path: String, pid: u32 }, + /// 信息响应:版本、socket 路径、进程号、监听地址。 + Info { + version: String, + socket_path: String, + pid: u32, + /// 实际监听地址(bind 后的网络端点,控制客户端经协议层发现 server + /// 可达接口;TCP 臂为 host:port,Unix 臂为 socket 路径)。 + listen_addr: String, + }, /// 关闭确认。 Shutdown, /// 交接确认:旧 server 已受理,开始排空退出。 @@ -434,6 +441,7 @@ mod tests { version: "0.8.23".into(), socket_path: "/tmp/oxide-server.sock".into(), pid: 1234, + listen_addr: "127.0.0.1:4242".into(), }, ServerResponse::Shutdown, ServerResponse::Yield, diff --git a/crates/oxide_cli/src/server/server.rs b/crates/oxide_cli/src/server/server.rs index 065609bd..1f61e5b1 100644 --- a/crates/oxide_cli/src/server/server.rs +++ b/crates/oxide_cli/src/server/server.rs @@ -153,6 +153,9 @@ struct ServerContext { pool_counters: Arc, // 监听端点文本形态(Unix 臂为路径、TCP 臂为 host:port),Info 响应用。 socket_path: String, + // 实际监听地址(bind 后取值:TCP 臂为 host:port、Unix 臂为 socket 路径), + // Info 响应用,让控制客户端经协议层发现 server 网络端点。 + listen_addr: String, } /// server 进程主体:初始化日志文件输出 → 启动时 liveness 扫描 → 认领 sidecar → @@ -240,6 +243,10 @@ pub fn run_server(config: &ServerConfig) -> Result<(), ServerError> { // 绑定监听器:stale socket 文件删一次重试,失败回滚 sidecar。 let listener = bind_listener(config)?; + // 取实际监听地址(bind 后:TCP 臂为操作系统分配的实际端口,Unix 臂为 + // socket 路径),Info 响应用,让控制客户端经协议层发现 server 网络端点。 + let listen_addr = listener.local_addr().map_err(|e| ServerError::BindFailed(e.to_string()))?; + // 建内核(标准配置)与共享聚合计数器(全体 worker 池的增减累加到同一聚合)。 let kernel = KernelCore::new(KernelConfig::standard()); let counters = PoolCounters::shared(); @@ -261,6 +268,7 @@ pub fn run_server(config: &ServerConfig) -> Result<(), ServerError> { in_flight: Arc::new(AtomicUsize::new(0)), pool_counters: counters, socket_path: config.endpoint.describe(), + listen_addr, }); // accept 循环:非阻塞 accept 加 10 毫秒轮询加原子关闭标志。 @@ -346,7 +354,10 @@ pub fn run_server_rm(config: &RmServerConfig) -> Result<(), ServerError> { shutdown: Arc::clone(&shutdown), in_flight: Arc::new(AtomicUsize::new(0)), pool_counters: counters, - socket_path, + // socket_path 与 listen_addr 同源:--rm 的 socket_path 已取 bind 后的 + // 实际地址(local_addr),Info 响应的监听地址与之一致。 + socket_path: socket_path.clone(), + listen_addr: socket_path, }); // 空闲窗口一:等首个连接;空闲超时内无连接即干净退出。 @@ -794,6 +805,7 @@ fn dispatch_control(request: &ServerRequest, ctx: &ServerContext) -> ServerRespo version: env!("CARGO_PKG_VERSION").to_string(), socket_path: ctx.socket_path.clone(), pid: std::process::id(), + listen_addr: ctx.listen_addr.clone(), }, ServerRequest::Shutdown => { // 置关闭标志;主循环至多 10 毫秒内退出 accept 循环。 @@ -1043,10 +1055,17 @@ mod tests { let mut stream = connect(&config); let response = send_and_recv(&mut stream, &ServerRequest::Info); match response { - ServerResponse::Info { version, socket_path, pid } => { + ServerResponse::Info { + version, + socket_path, + pid, + listen_addr, + } => { assert_eq!(version, env!("CARGO_PKG_VERSION"), "版本应与构建期版本一致"); assert_eq!(socket_path, config.endpoint.describe(), "socket 路径应与配置一致"); assert_eq!(pid, std::process::id(), "进程号应为当前进程号"); + // Unix 臂:实际监听地址即 socket 路径,与配置端点一致。 + assert_eq!(listen_addr, config.endpoint.describe(), "监听地址应与配置一致(Unix 臂)"); } other => panic!("应得 Info 帧,实得 {other:?}"), } diff --git a/crates/oxide_cli/src/server/sidecar.rs b/crates/oxide_cli/src/server/sidecar.rs index 910f851c..e606f0b5 100644 --- a/crates/oxide_cli/src/server/sidecar.rs +++ b/crates/oxide_cli/src/server/sidecar.rs @@ -35,6 +35,9 @@ use serde::{Deserialize, Serialize}; /// PID 复用判定用。 /// - `port`:TCP 监听端口(机器可读形态),Unix 臂为 0;旧 sidecar 无该 /// 字段时读回为 0(serde 缺省),探活裁决仍走 `socket_path` 文本。 +/// - `listen_addr`:监听地址(网络身份——server 可达的网络接口,安全边界 +/// 关注点:loopback-only 与 all-interfaces 的区分);旧 sidecar 无该字段 +/// 时读回为空串(serde 缺省)。 #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ServerIdentity { pub pid: u32, @@ -46,6 +49,11 @@ pub struct ServerIdentity { /// 读回为 0(serde 缺省),探活裁决走 socket_path 文本,行为不变。 #[serde(default)] pub port: u16, + /// 监听地址(网络身份:server 可达的网络接口,loopback-only 与 + /// all-interfaces 的区分);旧 sidecar 无该字段时读回为空串(serde + /// 缺省),探活裁决仍走 socket_path 文本,行为不变。 + #[serde(default)] + pub listen_addr: String, } impl ServerIdentity { @@ -57,6 +65,8 @@ impl ServerIdentity { /// - `socket_path` 字段记监听端点的文本形态(`endpoint.describe()`), /// 字段名与 JSON 模式不变,旧 sidecar 可读。 /// - `port` 取端点的 TCP 端口(Unix 臂无端口,记 0)。 + /// - `listen_addr` 取端点的文本形态(`endpoint.describe()`),与 + /// `socket_path` 同源同时机(认领时、bind 之前),记配置监听地址。 /// - 非 Linux 宿主(`/proc` 缺位)时 `starttime` 填 0,交叉核对退化, /// liveness 由 endpoint 探活裁决。 pub fn new(endpoint: &Endpoint, version: &str) -> Self { @@ -66,13 +76,15 @@ impl ServerIdentity { Endpoint::Tcp(addr) => addr.port(), Endpoint::Unix(_) => 0, }; + let socket_path = endpoint.describe(); ServerIdentity { pid, - socket_path: endpoint.describe(), + socket_path: socket_path.clone(), version: version.to_string(), started_at, starttime: process_starttime(pid).unwrap_or(0), port, + listen_addr: socket_path, } } } @@ -424,6 +436,7 @@ mod tests { started_at: 0, starttime: 0, port: 0, + listen_addr: String::new(), } } @@ -435,7 +448,7 @@ mod tests { let id = ServerIdentity::new(&Endpoint::Unix(PathBuf::from("/tmp/oxide-test.sock")), "0.0.0"); write_exclusive(&id, &path).expect("写 sidecar 应成功"); let read = read_identity(&path).expect("读回 sidecar 应成功"); - assert_eq!(read, id, "五字段应逐字段一致"); + assert_eq!(read, id, "六字段应逐字段一致"); } /// 端口字段:TCP 端点记端点端口(机器可读形态),Unix 臂记 0。 @@ -458,6 +471,7 @@ mod tests { let read = read_identity(&path).expect("读回 sidecar 应成功"); assert_eq!(read, id, "字段应逐字段一致"); assert_eq!(read.port, 4242, "port 字段应记端点端口"); + assert_eq!(read.listen_addr, "127.0.0.1:4242", "listen_addr 字段应记端点地址"); } /// 旧 sidecar(无 port 字段)读回:serde 缺省补 0,读回成功不判损坏。 @@ -472,6 +486,7 @@ mod tests { .expect("写旧 sidecar 应成功"); let id = read_identity(&path).expect("旧 sidecar 应可读回"); assert_eq!(id.port, 0, "无 port 字段应读回 0"); + assert_eq!(id.listen_addr, "", "无 listen_addr 字段应读回空串"); } /// O_EXCL 排他:同路径二次 create_new 必得 AlreadyExists。 diff --git a/crates/oxide_cli/src/server/watchdog.rs b/crates/oxide_cli/src/server/watchdog.rs index c3c55dcb..2cbfd8c6 100644 --- a/crates/oxide_cli/src/server/watchdog.rs +++ b/crates/oxide_cli/src/server/watchdog.rs @@ -309,6 +309,7 @@ mod tests { started_at: 0, starttime: 0, port: 0, + listen_addr: String::new(), } } diff --git a/crates/oxide_cli/tests/server_cli.rs b/crates/oxide_cli/tests/server_cli.rs index be4b7ecd..73d54ccb 100644 --- a/crates/oxide_cli/tests/server_cli.rs +++ b/crates/oxide_cli/tests/server_cli.rs @@ -715,8 +715,14 @@ fn server_start_port_e2e() { } let response = send_control_request(&ServerRequest::Info).expect("信息臂应成功"); match response { - ServerResponse::Info { version, socket_path, pid } => { + ServerResponse::Info { + version, + socket_path, + pid, + listen_addr, + } => { assert_eq!(socket_path, format!("127.0.0.1:{port}"), "Info 端点应为自定义端口"); + assert_eq!(listen_addr, format!("127.0.0.1:{port}"), "Info 监听地址应为自定义端口"); assert_eq!(version, env!("CARGO_PKG_VERSION"), "版本应与构建期版本一致"); assert_eq!(pid, id.pid, "Info 进程号应与 sidecar 一致"); } diff --git a/crates/oxide_cli/tests/server_client.rs b/crates/oxide_cli/tests/server_client.rs index 02a1f4b8..992e244f 100644 --- a/crates/oxide_cli/tests/server_client.rs +++ b/crates/oxide_cli/tests/server_client.rs @@ -80,11 +80,17 @@ fn control_client_end_to_end() { "healthy 应为真:{response:?}" ); - // 信息请求:socket 路径与配置一致、进程号为当前进程号。 + // 信息请求:socket 路径与配置一致、监听地址与配置一致、进程号为当前进程号。 let response = send_control_request_from(&config.sidecar_path, &ServerRequest::Info).expect("信息请求应成功"); match response { - ServerResponse::Info { socket_path, pid, .. } => { + ServerResponse::Info { + socket_path, + pid, + listen_addr, + .. + } => { assert_eq!(socket_path, config.endpoint.describe(), "socket 路径应与配置一致"); + assert_eq!(listen_addr, config.endpoint.describe(), "监听地址应与配置一致(Unix 臂)"); assert_eq!(pid, std::process::id(), "进程号应为当前进程号"); } other => panic!("应得 Info 帧,实得 {other:?}"), @@ -148,11 +154,18 @@ fn control_client_tcp_arm_end_to_end() { "healthy 应为真:{response:?}" ); - // 信息请求:endpoint 值与配置一致(host:port 文本形态)、进程号为当前进程号。 + // 信息请求:endpoint 值与配置一致(host:port 文本形态)、监听地址与配置一致、 + // 进程号为当前进程号。 let response = send_control_request_from(&config.sidecar_path, &ServerRequest::Info).expect("信息请求应成功"); match response { - ServerResponse::Info { socket_path, pid, .. } => { + ServerResponse::Info { + socket_path, + pid, + listen_addr, + .. + } => { assert_eq!(socket_path, config.endpoint.describe(), "endpoint 值应与配置一致"); + assert_eq!(listen_addr, config.endpoint.describe(), "监听地址应与配置一致(TCP 臂)"); assert_eq!(pid, std::process::id(), "进程号应为当前进程号"); } other => panic!("应得 Info 帧,实得 {other:?}"),