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

如何用Golang实现代理?原Nginx+Lua版本配置及逻辑参考

使用Golang实现Nginx+Lua风格的代理服务

没问题,我帮你把原来Nginx+Lua的代理逻辑用Golang完整实现出来,咱们一步步拆解对应功能:

首先明确核心需求:

  • 维持客户端与代理、代理与后端的长连接(对应Nginx的keepalive_timeout 3600s和keepalive_requests 30000)
  • 处理请求参数:添加固定的media=24、submedia=46,同时保留原请求中的os、osv、make、model、devicetype、conn参数
  • 将处理后的请求转发到目标地址http://www.example.com/bd/news/home

1. 核心实现思路

Golang标准库的net/http和httputil.ReverseProxy是实现反向代理的核心,我们需要自定义ReverseProxy的Director函数来修改请求参数,同时配置服务器和后端连接的长连接参数。

2. 完整代码实现

package main

import (
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"
)

func main() {
	// 解析目标后端地址
	targetURL, err := url.Parse("http://www.example.com/bd/news/home")
	if err != nil {
		panic("解析目标地址失败: " + err.Error())
	}

	// 创建自定义反向代理
	proxy := &httputil.ReverseProxy{
		Director: func(req *http.Request) {
			// 重写请求的目标地址
			req.URL.Scheme = targetURL.Scheme
			req.URL.Host = targetURL.Host
			req.URL.Path = singleJoiningSlash(targetURL.Path, req.URL.Path)
			req.Host = targetURL.Host

			// 处理URL参数:先获取原请求的所有参数
			query := req.URL.Query()

			// 添加固定参数(对应Lua里的media=24、submedia=46)
			query.Set("media", "24")
			query.Set("submedia", "46")

			// 保留原请求中的指定参数(如果存在的话)
			keepParams := []string{"os", "osv", "make", "model", "devicetype", "conn"}
			for _, param := range keepParams {
				if val := req.URL.Query().Get(param); val != "" {
					query.Set(param, val)
				}
			}

			// 更新请求的查询参数
			req.URL.RawQuery = query.Encode()
		},
		// 配置代理到后端的长连接(对应Nginx的keepalive配置)
		Transport: &http.Transport{
			// 对应keepalive_requests:每个后端主机的最大空闲连接数
			MaxIdleConnsPerHost: 30000,
			// 对应keepalive_timeout:空闲连接的超时时间(3600秒,转成纳秒)
			IdleConnTimeout: 3600 * 1000 * 1000 * 1000,
			// 其他优化配置
			MaxIdleConns:        100000,
			TLSHandshakeTimeout: 10 * 1000 * 1000 * 1000,
		},
	}

	// 配置HTTP服务器(处理客户端到代理的连接)
	server := &http.Server{
		Addr:        ":8080", // 代理服务监听端口,可自行修改
		Handler:     proxy,
		// 对应客户端连接的keepalive_timeout:空闲连接超时3600秒
		IdleTimeout: 3600 * 1000 * 1000 * 1000,
	}

	// 启动代理服务
	println("代理服务已启动,监听端口 :8080")
	err = server.ListenAndServe()
	if err != nil && err != http.ErrServerClosed {
		panic("服务启动失败: " + err.Error())
	}
}

// 辅助函数:处理路径拼接,避免出现重复斜杠
func singleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}

3. 代码与原Nginx配置的对应解释

  • 长连接配置:
    • 客户端→代理:通过http.Server的IdleTimeout设置为3600秒,对应Nginx的keepalive_timeout 3600s;Golang默认允许单个连接处理多个请求,若要严格限制每个连接的最大请求数(对应keepalive_requests 30000),可以看下面的可选中间件。
    • 代理→后端:通过http.Transport的MaxIdleConnsPerHost设置为30000(对应keepalive_requests),IdleConnTimeout设置为3600秒(对应后端连接的超时)。
  • 参数处理:在Director函数中完全复刻了Lua脚本的逻辑——添加固定参数、保留指定的原请求参数,最后更新请求的URL参数。
  • 请求转发:通过修改req.URL的Scheme、Host、Path,实现了Nginxproxy_pass的转发效果。

4. 可选:添加keepalive_requests限制的中间件

如果需要严格模拟Nginx的keepalive_requests 30000(限制单个客户端连接最多处理30000个请求),可以添加以下中间件:

import "context"

func keepaliveRequestsMiddleware(maxRequests int, next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// 从上下文获取当前连接的请求计数
		count := 0
		if val := r.Context().Value("request_count"); val != nil {
			count = val.(int)
		}
		count++
		// 达到上限后,告诉客户端关闭连接
		if count > maxRequests {
			w.Header().Set("Connection", "close")
		}
		// 更新上下文的计数
		ctx := context.WithValue(r.Context(), "request_count", count)
		next.ServeHTTP(w, r.WithContext(ctx))
	})
}

然后修改服务器的Handler:

server.Handler = keepaliveRequestsMiddleware(30000, proxy)

这样就完全复刻了原Nginx+Lua的代理功能啦!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:49:47