Golang使用ReverseProxy实现反向代理的方法

 更新时间:2024年09月13日 14:30:39   作者:os-lee  
本文介绍了如何使用Golang的ReverseProxy实现反向代理,包括源码结构解析和官方单机示例NewSingleHostReverseProxy,同时指出,若要实现负载均衡,需要自行开发,还提供了一个简单的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.DefaultTransport
    Transport 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 main
import (
	"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() {
	// 目标服务器的 URL
	targetURL := "http://localhost:8081"
	// 解析目标 URL
	target, 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
	// 设置反向代理的 Transport
	proxy.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 的缓冲区
			},
		},
	}
	// 设置反向代理的 ModifyResponse
	proxy.ModifyResponse = func(res *http.Response) error {
		// 修改响应头
		res.Header.Set("X-Modified-By", "ReverseProxy")
		// 修改状态码(示例)
		res.StatusCode = 200
		// 返回 nil 表示继续处理
		return nil
	}
	// 设置反向代理的 ErrorHandler
	proxy.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 main
import (
	"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))
}

到此这篇关于Golang使用ReverseProxy实现反向代理的文章就介绍到这了,更多相关Golang ReverseProxy反向代理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • golang 各种排序大比拼实例

    golang 各种排序大比拼实例

    这篇文章主要介绍了golang 各种排序大比拼实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • Goland配置leetcode的实现示例

    Goland配置leetcode的实现示例

    本文主要介绍了Goland配置leetcode的实现示例,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-06-06
  • 对Go语言中的context包源码分析

    对Go语言中的context包源码分析

    这篇文章主要对Go语言中的context包源码进行分析,context包析是1.15,context包定义了一个Context类型过这个Context接口类型, 就可以跨api边界/跨进程传递一些值,下面我们就来对context包源码进行分析,需要的小伙伴可以参考一下
    2022-02-02
  • go zero微服务实战系服务拆分

    go zero微服务实战系服务拆分

    这篇文章主要为大家介绍了go zero微服务实战系服务拆分的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • golang中使用proto3协议导致的空值字段不显示的问题处理方案

    golang中使用proto3协议导致的空值字段不显示的问题处理方案

    这篇文章主要介绍了golang中使用proto3协议导致的空值字段不显示的问题处理方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Golang使用gorm实现分页功能的示例代码

    Golang使用gorm实现分页功能的示例代码

    在提供列表接口时一般要用到分页,对于存储在某些数据库中的数据进行分页起来非常的方便,下文给出一个通过gorm进行分页并通过http返回数据的例子,感兴趣的小伙帮跟着小编一起来看看吧
    2024-10-10
  • 详解Golang五种原子性操作的用法

    详解Golang五种原子性操作的用法

    本文主要介绍了详解Golang五种原子性操作的用法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • Go语言规范context 类型的key用法示例解析

    Go语言规范context 类型的key用法示例解析

    这篇文章主要为大家介绍了Go语言规范context 类型的key用法示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • Go并发与锁的两种方式该如何提效详解

    Go并发与锁的两种方式该如何提效详解

    如果没有锁,在我们的项目中,可能会存在多个goroutine同时操作一个资源(临界区),这种情况会发生竞态问题(数据竞态),下面这篇文章主要给大家介绍了关于Go并发与锁的两种方式该如何提效的相关资料,需要的朋友可以参考下
    2022-12-12
  • 构建Golang应用最小Docker镜像的实现

    构建Golang应用最小Docker镜像的实现

    这篇文章主要介绍了构建Golang应用最小Docker镜像的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05

最新评论