负载均衡重构: 子网感知选择 + 节点健康监控 + 节点注册上报API
- 新增 subnet-aware 负载策略: 根据客户端IP匹配最近/16子网节点 - 节点注册端点 (POST /api/nodes/register): 边缘节点自动注册 - 节点心跳端点 (POST /api/nodes/heartbeat): 上报CPU/RAM/带宽/连接数 - 健康汇总 (GET /api/nodes/health): 服务端汇总所有节点状态 - 配置节点新增subnet/is_origin字段 - 前端NodeManager: 实时健康状态/CPU/RAM进度条/带宽/连接数 - X-Forwarded-For/X-Real-IP透传支持客户端IP
This commit is contained in:
1 parent
5f19fc6aa5
commit
980b91f41f
7 files changed
+319
-87
No files matched your search
@@ -1,4 +1,4 @@
|
||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||
from fastapi import APIRouter, HTTPException, Depends, Query, Request
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
@@ -26,6 +26,18 @@ class StreamUpdate(BaseModel):
|
||||
scheduled_at: Optional[str] = None
|
||||
|
||||
|
||||
def _get_client_ip(request: Request) -> str | None:
|
||||
forwarded = request.headers.get("X-Forwarded-For")
|
||||
if forwarded:
|
||||
return forwarded.split(",")[0].strip()
|
||||
client = request.headers.get("X-Real-IP")
|
||||
if client:
|
||||
return client
|
||||
if request.client:
|
||||
return request.client.host
|
||||
return None
|
||||
|
||||
|
||||
@router.get("")
|
||||
async def list_streams(db: AsyncSession = Depends(get_db)):
|
||||
svc = StreamService(db)
|
||||
@@ -114,17 +126,19 @@ async def delete_stream(stream_id: int, db: AsyncSession = Depends(get_db),
|
||||
|
||||
|
||||
@router.get("/{stream_id}/watch")
|
||||
async def get_watch_urls(stream_id: int, db: AsyncSession = Depends(get_db)):
|
||||
async def get_watch_urls(stream_id: int, request: Request, db: AsyncSession = Depends(get_db)):
|
||||
svc = StreamService(db)
|
||||
stream = await svc.get_stream(stream_id)
|
||||
if not stream:
|
||||
raise HTTPException(status_code=404, detail="Stream not found")
|
||||
urls = await svc.get_watch_urls(stream.stream_key)
|
||||
client_ip = _get_client_ip(request)
|
||||
urls = await svc.get_watch_urls(stream.stream_key, client_ip)
|
||||
return {
|
||||
"stream_id": stream.id,
|
||||
"title": stream.title,
|
||||
"status": stream.status.value,
|
||||
"multicast_available": urls["multicast_available"],
|
||||
"selected_node": urls.get("selected_node"),
|
||||
"lines": urls["lines"],
|
||||
"qualities": urls["qualities"],
|
||||
}
|
||||
@@ -153,12 +167,13 @@ async def stream_end(stream_id: int, db: AsyncSession = Depends(get_db),
|
||||
|
||||
|
||||
@router.get("/detail/{stream_key}")
|
||||
async def get_stream_by_key(stream_key: str, db: AsyncSession = Depends(get_db)):
|
||||
async def get_stream_by_key(stream_key: str, request: Request, db: AsyncSession = Depends(get_db)):
|
||||
svc = StreamService(db)
|
||||
s = await svc.get_stream_by_key(stream_key)
|
||||
if not s:
|
||||
raise HTTPException(status_code=404, detail="Stream not found")
|
||||
urls = await svc.get_watch_urls(stream_key)
|
||||
client_ip = _get_client_ip(request)
|
||||
urls = await svc.get_watch_urls(stream_key, client_ip)
|
||||
return {
|
||||
"stream_id": s.id,
|
||||
"title": s.title,
|
||||
|
||||
Reference in new issue
Block a user