[已解决]Golang中ReverseProxy无法正常关闭的问题

[已解决]Golang中ReverseProxy无法正常关闭的问题 我正在尝试构建一个简单的反向代理,但在停止时遇到了问题。关闭命令已执行但代理仍在运行。有人知道该怎么办吗?

package main

import (
	"context"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	go func() {
		time.Sleep(1 * time.Second)
		err := Stop()
		if err != nil {
			log.Println(err)
		}
	}()

	err := Start("localhost:9999", "http://dockerhost:8065")
	if err != nil {
		log.Print(err)
	}
}

var reverseProxy http.Server

func Start(bind string, remote string) error {
	remoteUrl, err := url.Parse(remote)
	if err != nil {
		return err
	}

	reverseProxy := http.Server{
		Handler: proxyHandler(remoteUrl),
		Addr:    bind,
	}

	err = reverseProxy.ListenAndServe()
	if err != nil {
		return err
	}

	log.Print("Stopped proxy")

	return nil
}

func Stop() error {
	log.Print("Stopper proxy.")
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	log.Print("Stopper proxy..")
	if err := reverseProxy.Shutdown(ctx); err != nil {
		return err
	}
	log.Print("Stopper proxy...")
	return nil
}

func proxyHandler(remote *url.URL) *httputil.ReverseProxy {
	handler := func(req *http.Request) {
		req.URL.Host = remote.Host
		req.URL.Scheme = remote.Scheme
		req.ParseForm()
		log.Printf("%+v", req)
	}
	return &httputil.ReverseProxy{
		Director: handler,
	}
}

更多关于[已解决]Golang中ReverseProxy无法正常关闭的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

非常感谢,现在我终于可以安心睡觉了 😅

更多关于[已解决]Golang中ReverseProxy无法正常关闭的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


@muhaha

这里的问题是你需要将这段代码:

reverseProxy := http.Server{
	Handler: proxyHandler(remoteUrl),
	Addr:    bind,
}

改为:

reverseProxy = http.Server{
	Handler: proxyHandler(remoteUrl),
	Addr:    bind,
}

否则你创建的是一个新的反向代理对象,它没有被赋值给你的全局变量,因此 Stop 方法实际上不会执行任何操作。微笑

问题在于Start函数内部重新声明了reverseProxy变量,导致全局变量reverseProxy没有被正确初始化。在Go语言中,使用:=会在当前作用域创建一个新的局部变量,而不是使用全局变量。

以下是修复后的代码:

package main

import (
	"context"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"time"
)

func main() {
	log.SetFlags(log.LstdFlags | log.Lshortfile)
	go func() {
		time.Sleep(1 * time.Second)
		err := Stop()
		if err != nil {
			log.Println(err)
		}
	}()

	err := Start("localhost:9999", "http://dockerhost:8065")
	if err != nil {
		log.Print(err)
	}
}

var reverseProxy http.Server

func Start(bind string, remote string) error {
	remoteUrl, err := url.Parse(remote)
	if err != nil {
		return err
	}

	// 移除 := 直接赋值给全局变量
	reverseProxy = http.Server{
		Handler: proxyHandler(remoteUrl),
		Addr:    bind,
	}

	err = reverseProxy.ListenAndServe()
	if err != nil && err != http.ErrServerClosed {
		return err
	}

	log.Print("Stopped proxy")

	return nil
}

func Stop() error {
	log.Print("Stopper proxy.")
	ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
	defer cancel()

	log.Print("Stopper proxy..")
	if err := reverseProxy.Shutdown(ctx); err != nil {
		return err
	}
	log.Print("Stopper proxy...")
	return nil
}

func proxyHandler(remote *url.URL) *httputil.ReverseProxy {
	handler := func(req *http.Request) {
		req.URL.Host = remote.Host
		req.URL.Scheme = remote.Scheme
		req.ParseForm()
		log.Printf("%+v", req)
	}
	return &httputil.ReverseProxy{
		Director: handler,
	}
}

主要修改:

  1. Start函数中的reverseProxy := http.Server{改为reverseProxy = http.Server{,直接赋值给全局变量
  2. ListenAndServe的错误检查中添加了对http.ErrServerClosed的判断,因为正常关闭时服务器会返回这个错误

这样修改后,Stop函数就能正确访问到已初始化的reverseProxy实例,从而正常关闭服务器。

回到顶部