You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言基础负载均衡器404问题:请求未进入处理函数求助

Go负载均衡器请求未进入处理函数导致404问题排查

问题现象

运行程序后终端输出:

1
2
7
8
server serving at 'http://localhost:3000'

访问localhost:3000/时出现404错误,终端无额外输出,服务器未崩溃,且请求从未进入handleRedirect()函数。

用户代码

package main

import (
    "fmt"
    "net/http"
    "net/http/httputil"
    "net/url"
    "os"
)

type Server interface {
    Address() string
    IsAlive() bool
    Serve(rw http.ResponseWriter, r *http.Request)
}

type SimpleServer struct {
    address string
    proxy   *httputil.ReverseProxy
}

func newSimpleServer(address string) *SimpleServer {
    serverURL, err := url.Parse(address)
    handleError(err)

    return &SimpleServer{
        address: address,
        proxy:   httputil.NewSingleHostReverseProxy(serverURL),
    }
}

type LoadBalancer struct {
    port            string
    roundRobinCount int
    servers         []Server
}

func newLoadBalancer(port string, servers []Server) *LoadBalancer {
    return &LoadBalancer{
        port:            port,
        roundRobinCount: 0,
        servers:         servers,
    }
}

func (s *SimpleServer) Address() string { return s.address }
func (s *SimpleServer) IsAlive() bool   { return true }
func (s *SimpleServer) Serve(rw http.ResponseWriter, r *http.Request) {
    fmt.Println(6)
    s.proxy.ServeHTTP(rw, r)
}

func (lb *LoadBalancer) getNextAvailableServer() Server {
    server := lb.servers[lb.roundRobinCount%len(lb.servers)]
    for !server.IsAlive() {
        lb.roundRobinCount++
        server = lb.servers[lb.roundRobinCount%len(lb.servers)]
    }
    lb.roundRobinCount++
    return server
}

func (lb *LoadBalancer) ServeProxy(rw http.ResponseWriter, r *http.Request) {
    fmt.Println(4)
    targetServer := lb.getNextAvailableServer()
    fmt.Printf("forwarding reques to %q\n", targetServer.Address())
    fmt.Println(5)
    targetServer.Serve(rw, r)
}

func main() {
    fmt.Println(1)
    servers := []Server{
        newSimpleServer("https://www.fast.com/"),
        newSimpleServer("https://www.theverge.com/"),
        newSimpleServer("https://www.canva.com/"),
    }

    fmt.Println(2)
    lb := newLoadBalancer("3000", servers)
    handleRedirect := func(rw http.ResponseWriter, req *http.Request) {
        fmt.Println(3)
        lb.ServeProxy(rw, req)
    }
    
    fmt.Println(7)
    http.HandleFunc("/", handleRedirect)
    
    fmt.Println(8)
    fmt.Printf("server serving at 'http://localhost:%s'\n", lb.port)
    http.ListenAndServe(":"+lb.port, nil)
}

func handleError(err error) {
    if err != nil {
        fmt.Printf("you've got an error fix it asap %v\n", err)
        os.Exit(1)
    }
}

解决方案

1. 修复路由未触发问题

默认全局http.DefaultServeMux可能被隐式操作修改,导致路由注册失效。替换为自定义多路复用器即可解决:

修改main函数:

func main() {
    fmt.Println(1)
    servers := []Server{
        newSimpleServer("https://www.fast.com/"),
        newSimpleServer("https://www.theverge.com/"),
        newSimpleServer("https://www.canva.com/"),
    }

    fmt.Println(2)
    lb := newLoadBalancer("3000", servers)
    handleRedirect := func(rw http.ResponseWriter, req *http.Request) {
        fmt.Println(3)
        lb.ServeProxy(rw, req)
    }
    
    fmt.Println(7)
    // 创建自定义多路复用器,避免默认Mux的潜在问题
    mux := http.NewServeMux()
    mux.HandleFunc("/", handleRedirect)
    
    fmt.Println(8)
    fmt.Printf("server serving at 'http://localhost:%s'\n", lb.port)
    // 使用自定义Mux启动服务,并检查启动错误
    err := http.ListenAndServe(":"+lb.port, mux)
    handleError(err)
}

2. 修复反向代理导致的404问题

默认反向代理会保留原请求的Host头(localhost:3000),目标服务器无法识别该Host,因此返回404。需修改代理的Director函数替换Host头:

修改newSimpleServer函数:

func newSimpleServer(address string) *SimpleServer {
    serverURL, err := url.Parse(address)
    handleError(err)

    proxy := httputil.NewSingleHostReverseProxy(serverURL)
    // 自定义Director,修改请求Host头为目标服务器的Host
    originalDirector := proxy.Director
    proxy.Director = func(req *http.Request) {
        originalDirector(req)
        req.Host = serverURL.Host
    }

    return &SimpleServer{
        address: address,
        proxy:   proxy,
    }
}

额外排查建议

  • 用netstat类命令确认3000端口确实被你的程序监听
  • 直接访问http://localhost:3000/(确保路径带末尾斜杠),避免浏览器自动跳转影响
  • 检查防火墙设置,确保本地请求能到达3000端口

内容的提问来源于stack exchange,提问作者zxcvbnm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 18:40:38