在 Golang 中的 net/http/httputil 包中提供了 ReverseProxy 这么一个数据结构,它是实现整个反向代理的关键。
// 反向代理type ReverseProxy struct {// Director这个函数传入的参数是新复制的一个请求,我们可以修改这个请求// 比如修改请求的请求Host或者请求URL等Director func(*http.Request)// Transport 代表底层的连接池设置,比如连接最长保持多久等// 如果不填的话,则使用默认的设置Transport http.RoundTripper// FlushInterval表示多久将下游的response的数据拷贝到proxy的responseFlushInterval time.Duration// ErrorLog 表示错误日志打印的句柄ErrorLog *log.Logger// BufferPool表示将下游response拷贝到proxy的response的时候使用的缓冲池大小BufferPool BufferPool// ModifyResponse 函数表示,如果要将下游的response内容进行修改,再传递给proxy// 的response,这个函数就可以进行设置,但是如果这个函数返回了error,则将response// 传递进入ErrorHandler,否则使用默认设置ModifyResponse func(*http.Response) error// ErrorHandler 处理ModifyResponse返回的ErrorErrorHandler func(http.ResponseWriter, *http.Request, error)}
一个请求到了,直接先请求一下后端服务,如果后端发现请求不存在,返回 404 Not Found 之后, 我们再将请求再请求到前端服务
// 重新启动一个proxy网关func (p *Proxy) newProxyReverseProxy(frontend, backend *url.URL) *httputil.ReverseProxy {...// 先创建一个后端服务的directorydirector := func(req *http.Request) {req.URL.Scheme = backend.Schemereq.URL.Host = backend.Host}// 定义一个NotFoundErrNotFoundErr := errors.New("response is 404, need to redirect")return &httputil.ReverseProxy{Director: director, // 先转发到后端服务ModifyResponse: func(response *http.Response) error {// 如果后端服务返回了404,我们返回NotFoundErr 会进入到errorHandler中if response.StatusCode == 404 {return NotFoundErr}return nil},ErrorHandler: func(writer http.ResponseWriter, request *http.Request, err error) {// 判断 Error 是否为NotFoundError, 是的话则进行前端服务的转发,重新修改writerif errors.Is(err, NotFoundErr) {httputil.NewSingleHostReverseProxy(frontend).ServeHTTP(writer, request)}}}}
其次是后端调试模式,先启动一个 Goroutine 监听后端文件,再启动一个只有后端的 proxy:
// devBackendCommand 启动后端调试模式var devBackendCommand = &cobra.Command{...RunE: func(c *cobra.Command, args []string) error {proxy := NewProxy(c.GetContainer())// 监听后端文件go proxy.monitorBackend()// 启动只有后端的proxyif err := proxy.startProxy(false, true); err != nil {return err}return nil},}
而前后端同时调试,则是先启动一个 Goroutine 监听后端文件,再同时启动监听前后端的 proxy:
var devAllCommand = &cobra.Command{...RunE: func(c *cobra.Command, args []string) error {proxy := NewProxy(c.GetContainer())// 监听后端文件go proxy.monitorBackend()// 启动前后端同时监听的proxyif err := proxy.startProxy(true, true); err != nil {return err}return nil},}
// 初始化Dev命令func initDevCommand() *cobra.Command {devCommand.AddCommand(devBackendCommand)devCommand.AddCommand(devFrontendCommand)devCommand.AddCommand(devAllCommand)return devCommand}// devCommand 为调试模式的一级命令var devCommand = &cobra.Command{Use: "dev",Short: "调试模式",RunE: func(c *cobra.Command, args []string) error {c.Help()return nil},}// devBackendCommand 启动后端调试模式var devBackendCommand = &cobra.Command{Use: "backend",Short: "启动后端调试模式",RunE: func(c *cobra.Command, args []string) error {...},}// devFrontendCommand 启动前端调试模式var devFrontendCommand = &cobra.Command{Use: "frontend",Short: "前端调试模式",RunE: func(c *cobra.Command, args []string) error {...},}// 同时启动前端和后端调试var devAllCommand = &cobra.Command{Use: "all",Short: "同时启动前端和后端调试",RunE: func(c *cobra.Command, args []string) error {...},}
