forked from boos/BoosAPI
feat(server): add proactive channel probes and load-aware stable routing / 服务端新增主动探活与负载感知稳定路由 (#330)
* wip: preserve local changes before upstream merge * Improve proxy routing stability and add stable-first strategy * Sync explicit-group routing strategy to eligible source routes * Fix proxy retry and runtime health review issues * fix: make proxy channel retry attempts configurable * fix: harden proxy retry recovery and bookkeeping * fix: address pr 257 review comments * feat(proxy): add sticky session channel coordination * feat(server): add proactive channel probes and load-aware stable routing * test(server): stabilize session load routing selection assertion * fix(proxy): harden codex forwarding stability * fix(router): rotate stable-first channels by healthy order * fix(server): address probe review feedback * fix(server): address probe review follow-ups * fix(ci): address CodeQL and core test failures * feat(server): refine stable-first routing recovery and observation * Finish PR 330 settings and review fixes * Fix chat site endpoint CI mock * Address latest PR 330 review comments --------- Co-authored-by: Cita <juricek.chen@gmail.com>
This commit is contained in:
@@ -34,6 +34,8 @@ import type { RouteCandidateView, RouteTokenOption } from '../helpers/routeModel
|
||||
import { SortableChannelRow } from './SortableChannelRow.js';
|
||||
import {
|
||||
getRouteRoutingStrategyLabel,
|
||||
getRouteRoutingStrategyDescription,
|
||||
getRouteRoutingStrategyHint,
|
||||
normalizeRouteRoutingStrategyValue,
|
||||
} from './routingStrategy.js';
|
||||
import {
|
||||
@@ -199,6 +201,8 @@ function RouteCardInner({
|
||||
const channelManagementDisabled = explicitGroupRoute;
|
||||
const title = resolveRouteTitle(route);
|
||||
const routingStrategy = normalizeRouteRoutingStrategyValue(route.routingStrategy);
|
||||
const routingStrategyDescription = getRouteRoutingStrategyDescription(routingStrategy);
|
||||
const routingStrategyHint = getRouteRoutingStrategyHint(routingStrategy);
|
||||
const hasCachedDecisionSnapshot = !!route.decisionSnapshot;
|
||||
const cachedDecisionTooltip = route.decisionRefreshedAt
|
||||
? `${tr('最近刷新')}: ${formatDateTimeMinuteLocal(route.decisionRefreshedAt)}`
|
||||
@@ -207,17 +211,17 @@ function RouteCardInner({
|
||||
{
|
||||
value: 'weighted',
|
||||
label: tr('权重随机'),
|
||||
description: tr('按优先级、权重和成本信号综合选择'),
|
||||
description: getRouteRoutingStrategyDescription('weighted'),
|
||||
},
|
||||
{
|
||||
value: 'round_robin',
|
||||
label: tr('轮询'),
|
||||
description: tr('按全局顺序轮流调用,忽略优先级,连续失败 3 次后进入分级冷却'),
|
||||
description: getRouteRoutingStrategyDescription('round_robin'),
|
||||
},
|
||||
{
|
||||
value: 'stable_first',
|
||||
label: tr('稳定优先'),
|
||||
description: tr('按优先级优先选择当前最稳、最快、成功率更高的通道,不做随机分流'),
|
||||
description: getRouteRoutingStrategyDescription('stable_first'),
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -359,7 +363,11 @@ function RouteCardInner({
|
||||
{tr('0 通道')}
|
||||
</span>
|
||||
) : (
|
||||
<span className="badge badge-muted" style={{ fontSize: 10, flexShrink: 0 }}>
|
||||
<span
|
||||
className="badge badge-muted"
|
||||
style={{ fontSize: 10, flexShrink: 0 }}
|
||||
data-tooltip={`${getRouteRoutingStrategyLabel(routingStrategy)}:${routingStrategyDescription}`}
|
||||
>
|
||||
{getRouteRoutingStrategyLabel(routingStrategy)}
|
||||
</span>
|
||||
)}
|
||||
@@ -545,6 +553,14 @@ function RouteCardInner({
|
||||
placeholder={tr('选择路由策略')}
|
||||
emptyLabel={tr('暂无可选策略')}
|
||||
/>
|
||||
<div style={{ marginTop: 8, display: 'flex', flexDirection: 'column', gap: 4 }}>
|
||||
<div style={{ fontSize: 12, lineHeight: 1.55, color: 'var(--color-text-secondary)' }}>
|
||||
{routingStrategyDescription}
|
||||
</div>
|
||||
<div style={{ fontSize: 11, lineHeight: 1.5, color: 'var(--color-text-muted)' }}>
|
||||
{routingStrategyHint}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -12,3 +12,25 @@ export function getRouteRoutingStrategyLabel(value?: RouteRoutingStrategy | null
|
||||
if (strategy === 'stable_first') return tr('稳定优先');
|
||||
return tr('权重随机');
|
||||
}
|
||||
|
||||
export function getRouteRoutingStrategyDescription(value?: RouteRoutingStrategy | null): string {
|
||||
const strategy = normalizeRouteRoutingStrategyValue(value);
|
||||
if (strategy === 'round_robin') {
|
||||
return tr('忽略 P 值,按全局顺序依次调用;连续失败 3 次后进入分级冷却');
|
||||
}
|
||||
if (strategy === 'stable_first') {
|
||||
return tr('先避开最近失败或不健康站点,再在稳定池里按顺序轮询;P 值表示轮询顺位');
|
||||
}
|
||||
return tr('P 值是硬优先级,只会在当前最高可用优先级内结合权重、成本和健康度随机选择');
|
||||
}
|
||||
|
||||
export function getRouteRoutingStrategyHint(value?: RouteRoutingStrategy | null): string {
|
||||
const strategy = normalizeRouteRoutingStrategyValue(value);
|
||||
if (strategy === 'round_robin') {
|
||||
return tr('当前策略不看 P 值;如果之后切回其他策略,拖拽保存的顺序仍会保留。');
|
||||
}
|
||||
if (strategy === 'stable_first') {
|
||||
return tr('当前策略下,稳定站点会按 P 顺序轮换;不稳定站点会被自动降权或临时避让。');
|
||||
}
|
||||
return tr('只要更高优先级还有可用通道,后面的通道本次就不会参与选择。');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user