refactor: 拆分 gateway 单体为独立 crate,新增 systemd 部署方案

将 gateway 内部的 model-fetch、provider-transport、scheduler-core、
usage-runtime、video-tasks-core 模块提取为独立 crate;重构 gateway
内部模块结构(state/router/cache/data/query 等);移除大量遗留模块
文件;新增 systemd 二进制部署骨架及相关文档;更新前端 usage 相关
API 和组件。
This commit is contained in:
fawney19
2026-04-05 20:23:16 +08:00
parent cbc811f6ce
commit 763ff03a7b
777 changed files with 42659 additions and 21469 deletions

View File

@@ -1,18 +1,19 @@
use crate::gateway::constants::{
CONTROL_ENDPOINT_SIGNATURE_HEADER, CONTROL_EXECUTION_RUNTIME_HEADER,
use crate::audit::{emit_admin_audit, record_shadow_result_non_blocking};
use crate::constants::{
CONTROL_ENDPOINT_SIGNATURE_HEADER, CONTROL_EXECUTION_RUNTIME_HEADER, CONTROL_REQUEST_ID_HEADER,
CONTROL_ROUTE_CLASS_HEADER, CONTROL_ROUTE_FAMILY_HEADER, CONTROL_ROUTE_KIND_HEADER,
DEPENDENCY_REASON_HEADER, EXECUTION_PATH_HEADER, LOCAL_EXECUTION_RUNTIME_MISS_REASON_HEADER,
TRACE_ID_HEADER,
};
use crate::gateway::{
record_shadow_result_non_blocking, AppState, GatewayControlDecision,
GatewayPublicRequestContext,
};
use crate::control::GatewayControlDecision;
use crate::control::GatewayPublicRequestContext;
use crate::middleware::RequestLogEmitted;
use crate::AppState;
use aether_runtime::{maybe_hold_axum_response_permit, AdmissionPermit};
use axum::body::{Body, Bytes};
use axum::http::{self, header::HeaderName, header::HeaderValue, Response};
use std::time::Instant;
use tracing::info;
use tracing::{info, warn};
pub(super) fn request_wants_stream(
request_context: &GatewayPublicRequestContext,
@@ -68,27 +69,70 @@ pub(super) fn finalize_gateway_response(
.headers()
.get(DEPENDENCY_REASON_HEADER)
.and_then(|value| value.to_str().ok())
.unwrap_or("none");
.unwrap_or("none")
.to_string();
let local_execution_runtime_miss_reason = response
.headers()
.get(LOCAL_EXECUTION_RUNTIME_MISS_REASON_HEADER)
.and_then(|value| value.to_str().ok())
.unwrap_or("none");
info!(
trace_id = %trace_id,
remote_addr = %remote_addr,
method = %method,
path = %path_and_query,
route_class = control_decision
.and_then(|decision| decision.route_class.as_deref())
.unwrap_or("passthrough"),
execution_path,
dependency_reason,
local_execution_runtime_miss_reason,
status = response.status().as_u16(),
elapsed_ms,
"gateway completed request"
.unwrap_or("none")
.to_string();
let route_class = control_decision
.and_then(|decision| decision.route_class.as_deref())
.unwrap_or("passthrough");
let request_id = response
.headers()
.get(CONTROL_REQUEST_ID_HEADER)
.and_then(|value| value.to_str().ok())
.filter(|value| !value.trim().is_empty())
.unwrap_or("-")
.to_string();
let status_code = response.status().as_u16();
emit_admin_audit(
&mut response,
trace_id,
method,
path_and_query,
control_decision,
);
if response.status().is_server_error() {
warn!(
event_name = "http_request_failed",
log_type = "access",
status = "failed",
status_code,
trace_id = %trace_id,
request_id,
remote_addr = %remote_addr,
method = %method,
path = %path_and_query,
route_class,
execution_path,
dependency_reason = dependency_reason.as_str(),
local_execution_runtime_miss_reason = local_execution_runtime_miss_reason.as_str(),
elapsed_ms,
"gateway request failed"
);
} else {
info!(
event_name = "http_request_completed",
log_type = "access",
status = "completed",
status_code,
trace_id = %trace_id,
request_id,
remote_addr = %remote_addr,
method = %method,
path = %path_and_query,
route_class,
execution_path,
dependency_reason = dependency_reason.as_str(),
local_execution_runtime_miss_reason = local_execution_runtime_miss_reason.as_str(),
elapsed_ms,
"gateway completed request"
);
}
response.extensions_mut().insert(RequestLogEmitted);
record_shadow_result_non_blocking(
state.clone(),

View File

@@ -1,5 +1,6 @@
use super::super::{admin, internal, public};
use crate::gateway::{AppState, GatewayError, GatewayPublicRequestContext};
use crate::control::GatewayPublicRequestContext;
use crate::{AppState, GatewayError};
use axum::body::{Body, Bytes};
use axum::http::Response;

View File

@@ -3,14 +3,17 @@ mod local;
use self::local::{
maybe_build_local_admin_proxy_response, maybe_build_local_internal_proxy_response,
};
use super::admin::misc_helpers::{
build_admin_proxy_auth_required_response, build_unhandled_admin_proxy_response,
};
use super::internal::resolve_local_proxy_execution_path;
pub(crate) use super::public::matches_model_mapping_for_models;
use crate::gateway::ai_pipeline::{finalize as ai_finalize, runtime as ai_runtime};
use crate::gateway::api::response::{
use crate::ai_pipeline::{finalize as ai_finalize, runtime as ai_runtime};
use crate::api::response::{
build_local_auth_rejection_response, build_local_http_error_response,
build_local_overloaded_response, build_local_user_rpm_limited_response,
};
use crate::gateway::constants::{
use crate::constants::{
DEPENDENCY_REASON_HEADER, EXECUTION_PATH_CONTROL_EXECUTE_STREAM,
EXECUTION_PATH_CONTROL_EXECUTE_SYNC, EXECUTION_PATH_DISTRIBUTED_OVERLOADED,
EXECUTION_PATH_EXECUTION_RUNTIME_STREAM, EXECUTION_PATH_EXECUTION_RUNTIME_SYNC,
@@ -24,16 +27,20 @@ use crate::gateway::constants::{
TRUSTED_AUTH_BALANCE_HEADER, TRUSTED_AUTH_USER_ID_HEADER, TUNNEL_AFFINITY_FORWARDED_BY_HEADER,
TUNNEL_AFFINITY_OWNER_INSTANCE_HEADER,
};
use crate::gateway::handlers::{
use crate::control::maybe_execute_via_control;
use crate::control::GatewayControlDecision;
use crate::control::GatewayPublicRequestContext;
use crate::handlers::{
allows_control_execute_emergency, local_proxy_route_requires_buffered_body,
request_enables_control_execute, request_model_local_rejection,
should_buffer_request_for_local_auth, should_strip_forwarded_provider_credential_header,
should_strip_forwarded_trusted_admin_header, trusted_auth_local_rejection,
};
use crate::gateway::headers::{extract_or_generate_trace_id, should_skip_request_header};
use crate::gateway::{
maybe_execute_via_control, AppState, FrontdoorUserRpmOutcome, GatewayControlDecision,
GatewayError, GatewayFallbackMetricKind, GatewayFallbackReason, GatewayPublicRequestContext,
use crate::headers::{extract_or_generate_trace_id, should_skip_request_header};
use crate::router::RequestAdmissionError;
use crate::{
AppState, FrontdoorUserRpmOutcome, GatewayError, GatewayFallbackMetricKind,
GatewayFallbackReason,
};
use axum::body::{to_bytes, Body, Bytes};
use axum::extract::{ConnectInfo, Request, State};
@@ -108,7 +115,7 @@ async fn maybe_forward_public_request_to_tunnel_owner(
return Ok(None);
};
let empty_body = Bytes::new();
let Some(requested_model) = crate::gateway::extract_requested_model(
let Some(requested_model) = crate::control::extract_requested_model(
decision,
&parts.uri,
&parts.headers,
@@ -116,7 +123,7 @@ async fn maybe_forward_public_request_to_tunnel_owner(
) else {
return Ok(None);
};
let Some(target) = crate::gateway::scheduler::read_cached_scheduler_affinity_target(
let Some(target) = crate::scheduler::read_cached_scheduler_affinity_target(
state,
&auth_context.api_key_id,
api_format,
@@ -145,7 +152,7 @@ async fn maybe_forward_public_request_to_tunnel_owner(
};
let Some(proxy) =
crate::gateway::provider_transport::resolve_transport_proxy_snapshot(&transport)
crate::provider_transport::resolve_transport_proxy_snapshot(&transport)
else {
return Ok(None);
};
@@ -257,9 +264,10 @@ pub(crate) async fn proxy_request(
let started_at = Instant::now();
let mut request_permit = match state.try_acquire_request_permit().await {
Ok(permit) => permit,
Err(crate::gateway::RequestAdmissionError::Local(
aether_runtime::ConcurrencyError::Saturated { gate, limit },
)) => {
Err(RequestAdmissionError::Local(aether_runtime::ConcurrencyError::Saturated {
gate,
limit,
})) => {
let trace_id = extract_or_generate_trace_id(request.headers());
let response = build_local_overloaded_response(&trace_id, None, gate, limit)?;
return Ok(finalize_gateway_response(
@@ -279,17 +287,15 @@ pub(crate) async fn proxy_request(
None,
));
}
Err(crate::gateway::RequestAdmissionError::Local(
aether_runtime::ConcurrencyError::Closed { gate },
)) => {
Err(RequestAdmissionError::Local(aether_runtime::ConcurrencyError::Closed { gate })) => {
return Err(GatewayError::Internal(format!(
"gateway request concurrency gate {gate} is closed"
)));
}
Err(crate::gateway::RequestAdmissionError::Distributed(
Err(RequestAdmissionError::Distributed(
aether_runtime::DistributedConcurrencyError::Saturated { gate, limit },
))
| Err(crate::gateway::RequestAdmissionError::Distributed(
| Err(RequestAdmissionError::Distributed(
aether_runtime::DistributedConcurrencyError::Unavailable { gate, limit, .. },
)) => {
let trace_id = extract_or_generate_trace_id(request.headers());
@@ -311,14 +317,14 @@ pub(crate) async fn proxy_request(
None,
));
}
Err(crate::gateway::RequestAdmissionError::Distributed(
Err(RequestAdmissionError::Distributed(
aether_runtime::DistributedConcurrencyError::InvalidConfiguration(message),
)) => return Err(GatewayError::Internal(message)),
};
let (parts, body) = request.into_parts();
let trace_id = extract_or_generate_trace_id(&parts.headers);
state.clear_local_execution_runtime_miss_diagnostic(&trace_id);
let request_context = crate::gateway::control::resolve_public_request_context(
let request_context = crate::control::resolve_public_request_context(
&state,
&parts.method,
&parts.uri,
@@ -389,7 +395,7 @@ pub(crate) async fn proxy_request(
&& decision.admin_principal.is_none()
})
{
let response = super::admin::build_admin_proxy_auth_required_response(&request_context);
let response = build_admin_proxy_auth_required_response(&request_context);
return Ok(finalize_gateway_response_with_context(
&state,
response,
@@ -408,7 +414,7 @@ pub(crate) async fn proxy_request(
&& decision.admin_principal.is_some()
})
{
let response = super::admin::build_unhandled_admin_proxy_response(&request_context);
let response = build_unhandled_admin_proxy_response(&request_context);
return Ok(finalize_gateway_response_with_context(
&state,
response,
@@ -420,7 +426,7 @@ pub(crate) async fn proxy_request(
));
}
if request_context.request_path.starts_with("/api/admin/") {
let response = super::admin::build_unhandled_admin_proxy_response(&request_context);
let response = build_unhandled_admin_proxy_response(&request_context);
return Ok(finalize_gateway_response_with_context(
&state,
response,