如何用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
相关产品推荐
相关产品推荐

