Golang使用ReverseProxy实现反向代理

news/2024/12/23 5:22:31/

目录

1.源码结构体 

2.官方单机示例

3.使用示例

4.简单的http服务(用于测试)


1.源码结构体 

type ReverseProxy struct {// Rewrite 必须是一个函数,用于将请求修改为要使用 Transport 发送的新请求。然后,其响应将原封不动地复制回原始客户端。返回后,Rewrite 不得访问提供的 ProxyRequest 或其内容。// 在调用 Rewrite 之前,将从出站请求中删除 Forwarded、X-Forwarded、X-Forwarded-Host 和 X-Forwarded-Proto 标头。另请参阅 ProxyRequest.SetXForwarded 方法。// 在调用 Rewrite 之前,将从出站请求中删除不可解析的查询参数。Rewrite 函数可以将入站 URL 的 RawQuery 复制到出站 URL 以保留原始参数字符串。请注意,如果代理对查询参数的解释与下游服务器的解释不匹配,这可能会导致安全问题。// 最多可以设置 Rewrite 或 Director 中的一个。Rewrite func(*ProxyRequest)// Director 是一种将请求修改为要使用 Transport 发送的新请求的功能。然后,其响应将原封不动地复制回原始客户端。Director 在返回后不得访问提供的请求。//默认情况下,X-Forwarded-For 标头设置为客户端 IP 地址的值。如果 X-Forwarded-For 标头已存在,则客户端 IP 将附加到现有值。作为特殊情况,如果标头存在于 Request.Header 映射中,但具有 nil 值(例如由 Director func 设置时),则不会修改 X-Forwarded-For 标头。// 为防止 IP 欺骗,请务必删除来自客户端或不受信任的代理的任何预先存在的 X-Forwarded-For 标头。// 在 Director 返回后,将从请求中删除逐跳标头,这可以删除 Director 添加的标头。请改用 Rewrite 函数来确保保留对请求的修改。// 如果在 Director 返回后设置 Request.Form,则会从出站请求中删除不可解析的查询参数。// 最多可以设置 Rewrite 或 Director 中的一个。Director func(*http.Request)// 用于执行代理请求的传输。如果为 nil,则使用为 http.DefaultTransportTransport http.RoundTripper// FlushInterval 指定在复制响应正文时要刷新到客户端的刷新间隔。如果为零,则不执行定期刷新。负值表示在每次写入 Client 端后立即刷新。当 ReverseProxy 将响应识别为流式响应或其 ContentLength 为 -1 时,将忽略 FlushInterval;对于此类响应,写入会立即刷新到客户端。FlushInterval time.Duration// ErrorLog 为尝试代理请求时发生的错误指定可选记录器。如果为 nil,则通过 log 包的标准 logger 完成日志记录。ErrorLog *log.Logger// BufferPool 可以选择指定一个缓冲池,以获取 io 使用的字节切片。CopyBuffer 在复制 HTTP 响应正文时。   BufferPool BufferPool// ModifyResponse 是一个可选函数,用于修改来自后端的 Response。如果后端返回带有任何 HTTP 状态代码的响应,则调用它。如果无法访问后端,则调用可选的 ErrorHandler,而不调用 ModifyResponse。如果 ModifyResponse 返回错误,则调用 ErrorHandler 及其错误值。如果 ErrorHandler 为 nil,则使用其默认实现。ModifyResponse func(*http.Response) error// ErrorHandler 是一个可选函数,用于处理到达后端的错误或来自 ModifyResponse 的错误。如果为 nil,则默认记录提供的错误并返回 502 Status Bad Gateway 响应。    ErrorHandler func(http.ResponseWriter, *http.Request, error)
}

2.官方单机示例

NewSingleHostReverseProxy是官方给的示例,代理单机服务,如果想实现负载均衡,需自己实现。

源码:

3.使用示例

package mainimport ("log""net""net/http""net/http/httputil""net/url""os""sync""time"
)// 从Go 1.18版本开始,httputil.ReverseProxy 的 BufferPool 字段期望的是一个实现了 BufferPool 接口的对象,该接口要求有一个返回 []byte 的 Get 方法和一个接受 []byte 的 Put 方法。
// 定义一个实现了 BufferPool 接口的结构体
// 定义一个实现了 BufferPool 接口的结构体
type byteBufferPool struct {sync.Pool
}// 实现 BufferPool 接口的 Get 方法
func (b *byteBufferPool) Get() []byte {v := b.Pool.Get()if v == nil {return make([]byte, 1024*1024) // 分配 1MB 的缓冲区}return v.([]byte)
}// 实现 BufferPool 接口的 Put 方法
func (b *byteBufferPool) Put(bts []byte) {b.Pool.Put(bts)
}// 定义一个实现了 ErrorHandler 接口的函数
func customErrorHandler(w http.ResponseWriter, r *http.Request, err error) {// 记录错误log.Printf("Error occurred: %v", err)// 返回自定义的状态码和错误消息w.WriteHeader(http.StatusInternalServerError)w.Write([]byte("An internal error occurred."))
}func main() {// 目标服务器的 URLtargetURL := "http://localhost:8081"// 解析目标 URLtarget, err := url.Parse(targetURL)if err != nil {log.Fatalf("Failed to parse target URL: %v", err)}// 创建反向代理proxy := httputil.NewSingleHostReverseProxy(target)// 设置 FlushInterval// FlushInterval 是一个 time.Duration 类型的字段,它指定了代理服务器将缓冲的数据写入客户端的间隔时间。这对于实现实时应用(如实时聊天、日志流等)非常重要,因为它可以减少延迟,让客户端更快地接收到数据。proxy.FlushInterval = 100 * time.Millisecond// 设置反向代理的 Transportproxy.Transport = &http.Transport{DialContext: (&net.Dialer{Timeout:   30 * time.Second, // 建立连接的最大等待时间KeepAlive: 30 * time.Second, // 保持连接活跃的时间间隔DualStack: true,             // 尝试同时使用 IPv4 和 IPv6 地址}).DialContext,MaxIdleConns:          100,              // 最大空闲连接数IdleConnTimeout:       90 * time.Second, // 空闲连接超时时间TLSHandshakeTimeout:   10 * time.Second, // TLS握手超时时间ExpectContinueTimeout: 1 * time.Second,  // Expect: 100-continue 超时时间}// 创建自定义的日志器proxy.ErrorLog = log.New(os.Stderr, "ERROR: ", log.LstdFlags)// 设置反向代理的 BufferPool 缓冲池proxy.BufferPool = &byteBufferPool{Pool: sync.Pool{New: func() interface{} {return make([]byte, 1024*1024) // 分配 1MB 的缓冲区},},}// 设置反向代理的 ModifyResponseproxy.ModifyResponse = func(res *http.Response) error {// 修改响应头res.Header.Set("X-Modified-By", "ReverseProxy")// 修改状态码(示例)res.StatusCode = 200// 返回 nil 表示继续处理return nil}// 设置反向代理的 ErrorHandlerproxy.ErrorHandler = customErrorHandler// 创建 HTTP 服务器http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {// 调用反向代理proxy.ServeHTTP(w, r)})// 启动 HTTP 服务器log.Println("Starting server on :8080")log.Fatal(http.ListenAndServe(":8080", nil))
}

4.简单的http服务(用于测试)

package mainimport ("fmt""log""net/http"
)func helloWorldHandler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, World!")
}func main() {http.HandleFunc("/", helloWorldHandler)log.Println("Starting server on :8081")log.Fatal(http.ListenAndServe(":8081", nil))
}


http://www.ppmy.cn/news/1526626.html

相关文章

Golang 中实现动态代理

在 Go 语言中,没有像 Java 中那样直接支持的动态代理机制,因为 Go 是静态类型的编程语言,不支持像 Java 反射那样基于接口的动态代理。但我们可以通过组合使用反射(reflect 包)和高阶函数的方式,实现类似于…

神经网络通俗理解学习笔记(5) 自然语言处理

自然语言处理 词嵌入和word2vec词义搜索和句意表示预训练模型Hugging Face库介绍经典NLP数据集代码案例-电影评论情感分析 词嵌入和word2vec 词嵌入是一种 将高维的数据表示映射到低维空间的方法 word embedding 是将语言中的词编码成向量便于后续的分析和处理 词嵌入和词向量…

MFC工控项目实例之十六输入信号验证

承接专栏《MFC工控项目实例之十五定时刷新PC6325A模拟量输入》 验证选定的输入信号实时状态 在BoardTest.cpp文件中添加代码 void CBoardTest::OnButton2() {// TODO: Add your control notification handler code hereisThreadBegin true; //运行线程执行pThre…

SOMEIP_ETS_110: SD_Do_not_specify_IPv4_Adress

测试目的: 验证DUT能够拒绝未在端点选项中指定有效IPv4地址的SubscribeEventgroup消息,并以SubscribeEventgroupNAck作为响应。 描述 本测试用例旨在确保DUT遵循SOME/IP协议,当接收到未包含有效IPv4地址的SubscribeEventgroup消息时&#…

bat批量修改文件名

一、bat代码 代码如下,其中csv_file中记录了原来的文件名字和要修改为的新文件名字,folder是img所在的文件路径。 echo off setlocal enabledelayedexpansion set "csv_fileD:\img\1.csv" set "folderD:\img\" for /f "…

python3 数据量单位转换

文章目录 python安装数据量单位简介如何实现数据量单位的转换示例代码 自动选择合适的单位自动选择单位的代码 处理小数点精度小数点精度示例 扩展应用:带宽单位转换带宽转换代码示例 结论 在 Python 中,数据量单位的转换是一个常见的需求,尤…

什么是机器学习力场

机器学习力场(Machine Learning Force Fields, MLFF)方法是一类将机器学习技术应用于分子动力学(Molecular Dynamics, MD)模拟的技术。它通过使用机器学习算法拟合原子之间的相互作用能量和力场,使得在不牺牲精度的前提…

it基础软件运维管理:从操作系统到数据库,再到中间件和应用系统

在当今的信息化时代,基础软件的运维管理对于企业的稳定运营至关重要。从操作系统到数据库,再到中间件和应用系统,每一个环节都需要精细化的管理和维护。本文将深入探讨基础软件运维管理的关键方面,并结合监控易一体化运维软件&…