首页
Preview

使用 Go 语言构建负载均衡器

什么是负载均衡器?

负载均衡器是一种常见的代理服务器,它在一个或多个后端服务器之间分配客户端请求并进行实际工作。

代理是另一个实体的替身,它可以像另一个实体一样行事或代替另一个实体。在(Web)软件中,我们经常将代理视为网络中的一个节点,它在客户端和后端服务器之间转发请求和响应。如果我去朋友家并通过他们的Ring门铃与他们交谈,那么这个门铃就是我们之间的代理。同样,代理服务器提供了客户端(我)和某个后端服务器(我的朋友)之间的网关。就像门铃一样,它充当中间人。我可以向服务器发出请求(与我的朋友交谈),而不需要获得该服务器资源的特权(进入房子)。

代理服务器可以为互联网系统提供增加的安全性、性能和可观察性。

今天,我们将使用Go构建一个负载均衡代理服务器!

构建服务器

我们将从定义LoadBalancer开始 - 此对象将包含有关服务器的基本信息,包括将处理代理请求的后端服务器以及负载均衡器将侦听的端口。

type LoadBalancer struct {
	port            string
	roundRobinCount int
	servers         []Server
}

func NewLoadBalancer(port string, servers []Server) *LoadBalancer {
	return &LoadBalancer{
		port:            port,
		roundRobinCount: 0,
		servers:         servers,
	}
}

通过包括一个Server列表 - 代表一组函数的接口 - 而不是硬编码类型,我们可以使用这个负载均衡器与实现基本Server接口的各种对象一起使用,而不必更改底层代码。为了清晰起见,我们还包括了一个构造函数(NewLoadBalancer),但我们同样可以直接构造LoadBalancer

现在我们需要一个实现Server接口的对象:

type Server interface {
	// Address returns the address with which to access the server
	Address() string

	// IsAlive returns true if the server is alive and able to serve requests
	IsAlive() bool

	// Serve uses this server to process the request
	Serve(rw http.ResponseWriter, req *http.Request)
}

type simpleServer struct {
	addr  string
	proxy *httputil.ReverseProxy
}

func (s *simpleServer) Address() string { return s.addr }

func (s *simpleServer) IsAlive() bool { return true }

func (s *simpleServer) Serve(rw http.ResponseWriter, req *http.Request) {
	s.proxy.ServeHTTP(rw, req)
}

没有错误处理的代码有什么用呢?对于这个例子,我们将使用一种非常简单的形式 - 如果我们看到错误,就退出!

// handleErr prints the error and exits the program
// Note: this is not how one would want to handle errors in production, but
// serves well for demonstration purposes.
func handleErr(err error) {
	if err != nil {
		fmt.Printf("error: %v\n", err)
		os.Exit(1)
	}
}

接下来,我们将编写一个帮助程序来实例化我们的服务器。此代码将采用地址并创建一个新的simpleServer实例,该实例具有一个代理。我们将使用代理来处理我们希望发送到此服务器的任何请求。

func newSimpleServer(addr string) *simpleServer {
	serverUrl, err := url.Parse(addr)
	handleErr(err)

	return &simpleServer{
		addr:  addr,
		proxy: httputil.NewSingleHostReverseProxy(serverUrl),
	}
}

现在,让我们设置我们的main函数来启动负载均衡器。我们将配置它来处理任何请求,而且我们将侦听此LoadBalancer定义的端口。我们将定义几个simpleServer实例。addr是我们将重定向请求的URL。

func main() {
	servers := []Server{
		newSimpleServer("https://www.google.com"),
		newSimpleServer("https://www.bing.com"),
		newSimpleServer("https://www.duckduckgo.com"),
	}

	lb := NewLoadBalancer("8000", servers)
	handleRedirect := func(rw http.ResponseWriter, req *http.Request) {
		lb.serveProxy(rw, req)
	}

	// register a proxy handler to handle all requests
	http.HandleFunc("/", handleRedirect)

	fmt.Printf("serving requests at 'localhost:%s'\n", lb.port)
	http.ListenAndServe(":"+lb.port, nil)
}

好了,所以我们已经配置了服务器的基本结构以及大部分需要的部分。最后一步是定义LoadBalancer接收请求时发生的情况。

// getNextServerAddr returns the address of the next available server to send a
// request to, using a simple round-robin algorithm
func (lb *LoadBalancer) getNextAvailableServer() Server {
	server := lb.servers[lb.roundRobinCount%len(lb.servers)]
	for !server.IsAlive() {
		lb.roundRobinCount++
		server = lb.servers[lb.roundRobinCount%len(lb.servers)]
	}
	lb.roundRobinCount++

	return server
}

func (lb *LoadBalancer) serveProxy(rw http.ResponseWriter, req *http.Request) {
	targetServer := lb.getNextAvailableServer()

	// could optionally log stuff about the request here!
	fmt.Printf("forwarding request to address %q\n", targetServer.Address())

	// could delete pre-existing X-Forwarded-For header to prevent IP spoofing
	targetServer.Serve(rw, req)
}

我们使用getNextAvailableServer()来确定哪个后端服务器最适合处理我们的请求,通过某种负载均衡算法(在这种情况下,我们使用循环轮询与健康检查相配合)。一旦我们知道将请求转发到哪个服务器,我们将在该服务器的代理上发出请求。代理将将客户端的请求路由到目标,并将目标的响应返回给客户端。

现在,唯一剩下的就是运行服务器并发出请求!

运行服务器

你可以从我的GitHub克隆完整代码,或通过按照本教程操作来生成代码。

一旦你拥有服务器代码,请在终端窗口中导航到该目录并键入go run src/main.go。你应该会看到终端输出,指示服务器正在运行。

现在,你可以在浏览器中转到localhost:8000,然后你应该被重定向到servers中定义的地址之一。当你刷新时,负载均衡代理服务器将你重定向到不同的URL。

请注意,你的浏览器可能会记住localhost:8000和其中一个给定的服务器地址之间的身份,因此最好使用隐身窗口。还要注意,定义的链接可能会从它们各自的主机中给出错误(例如,在https://google.com中前往/会产生错误)。

祝愉快!

译自:https://betterprogramming.pub/building-a-load-balancer-in-go-3da3c7c46f30

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
菜鸟一只
你就是个黄焖鸡,又黄又闷又垃圾。

评论(0)

添加评论