diff --git a/server/internal/proxy/passthrough.go b/server/internal/proxy/passthrough.go index 40f5472..68842aa 100644 --- a/server/internal/proxy/passthrough.go +++ b/server/internal/proxy/passthrough.go @@ -163,11 +163,29 @@ func upstreamURL(ch *store.Channel, proto, path string) string { return ch.UpstreamURL(proto, path) } -// doProxy 通用代理(M5):遍历候选渠道,按需转换;可安全重试的失败自动故障转移。 +// doProxy 通用代理(M5):加权随机选一个候选渠道;失败自动故障转移。 func (g *Gateway) doProxy(c *gin.Context, cands []channel.Candidate, clientProto string, body []byte, stream bool, sink *usageSink) { + if len(cands) == 0 { + apiError(c, http.StatusBadGateway, "upstream_error", "no available channel") + g.recordError(c, nil, nil, now(), "no_available_channel") + return + } + // 加权随机选择起始渠道 + picked := g.ch.Pick(cands) + startIdx := 0 + for i, cand := range cands { + if cand.Channel.ID == picked.ID { + startIdx = i + break + } + } + var lastStatus = http.StatusBadGateway var lastBody = []byte("all upstream channels failed") - for _, cand := range cands { + // 从选中的渠道开始遍历,到末尾后再从头遍历到选中渠道之前 + for offset := 0; offset < len(cands); offset++ { + idx := (startIdx + offset) % len(cands) + cand := cands[idx] ch := cand.Channel plan, err := prepareUpstream(ch, clientProto, body, cand.UpstreamModel) if err != nil {