关于Go利用Linux内核实现负载均衡
  DEmt2n5ZOrEK 2023年11月02日 60 0

概述

一般情况下,对某个HTTP服务的进程忘记关闭了。而重新尝试启动一个新的服务进程,会遇到以下的错误信息:

$ go run main.go
listen tcp :8000: bind: address already in use

这是由于默认情况下,操作系统不允许我们打开具有相同源地址和端口的socket。但如果我们想开启多个服务进程去监听同一个端口,可以使用以下的操作步骤实现。


Linux SO_REUSEPORT

为了满足复用端口的需求,Linux3.9内核引入了SO_REUSEPORT选项(实际在此之前有一个类似的选项SO_REUSEADDR,但它没有做到真正的端口复用)。

SO_REUSEPORT支持多个进程或者线程绑定到同一端口,用于提高服务器程序的性能。它的特性包含以下几点:

1.允许多个套接字bind同一个TCP/UDP 端口;

①每一个线程拥有自己的服务器套接字

②在服务器套接字上没有了锁的竞争

2.内核层面实现负载均衡;

3.安全层面,监听同一个端口的套接字只能位于同一个用户下(same effective UID)。


有了SO_RESUEPORT后,每个进程可以bind相同的地址和端口,各自是独立平等的。

让多进程监听同一个端口,各个进程中accept socket fd不一样,有新连接建立时,内核只会调度一个进程来accept,并且保证调度的均衡性。

示意图如下:

关于Go利用Linux内核实现负载均衡_GO

有了SO_REUSEADDR的支持,我们不仅可以创建多个具有相同IP:PORT的套接字能力,而且我们还得到了一种内核模式下的负载均衡能力。


Go 如何设置SO_REUSEPORT

Linux经典的设计哲学:一切皆文件。socket 也不例外,它也是一种文件。

如果想在Go程序中,利用上linux的SO_REUSEPORT选项,那就需要有修改内核 socket连接选项的接口,可以依赖于golang.org/x/sys/unix库来实现,具体就在以下这个方法。

import “"golang.org/x/sys/unix"”
...
unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)

因此,一个持有SO_REUSEPORT特性的完整Go服务代码如下:

package main
import (
"context"
"fmt"
"golang.org/x/sys/unix"
"net"
"net/http"
"os"
"syscall"
)
var lc = net.ListenConfig{
Control: func(network, address string, c syscall.RawConn) error {
var socketErr error
if err := c.Control(func(fd uintptr) {
socketErr = unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
}); err != nil {
return err
}
return socketErr
},
}
func main() {
pid := os.Getpid()
listen, err := lc.Listen(context.Background(), "tcp", "127.0.0.1:8080")
if err != nil {
panic(err)
}
server := &http.Server{}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
_, _ = fmt.Fprintf(w, "The Client [%s] Received Message from Server PID: [%d] \n", r.RemoteAddr, pid)
})
fmt.Printf("Server with PID: [%d] is running \n", pid)
_ = server.Serve(listen)
}

我们将其编译为Linux可执行文件main

$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go

在Linux主机上开启三个同时监听8080端口的进程,我们可以看到三个服务进程的 PID 分别是1569 、2419 和 3124。

~ $ ./main
Server with PID: [1569] is running
~ $ ./main
Server with PID: [2419] is running
~ $ ./main
Server with PID: [3124] is running

通过curl命令,模拟多次http客户端请求

# for i in {1..20}; do curl localhost:8080; done
The Client [127.0.0.1:19958] Received Message from Server PID: [1569]
The Client [127.0.0.1:19962] Received Message from Server PID: [2419]
The Client [127.0.0.1:19966] Received Message from Server PID: [3124]
The Client [127.0.0.1:19970] Received Message from Server PID: [2419]
The Client [127.0.0.1:19974] Received Message from Server PID: [3124]
The Client [127.0.0.1:19978] Received Message from Server PID: [2419]
The Client [127.0.0.1:19982] Received Message from Server PID: [2419]
The Client [127.0.0.1:19986] Received Message from Server PID: [2419]
The Client [127.0.0.1:19990] Received Message from Server PID: [3124]
The Client [127.0.0.1:19994] Received Message from Server PID: [3124]
The Client [127.0.0.1:19998] Received Message from Server PID: [2419]
The Client [127.0.0.1:20002] Received Message from Server PID: [3124]
The Client [127.0.0.1:20006] Received Message from Server PID: [1569]
The Client [127.0.0.1:20010] Received Message from Server PID: [1569]
The Client [127.0.0.1:20014] Received Message from Server PID: [2419]
The Client [127.0.0.1:20018] Received Message from Server PID: [2419]
The Client [127.0.0.1:20022] Received Message from Server PID: [2419]
The Client [127.0.0.1:20026] Received Message from Server PID: [2419]
The Client [127.0.0.1:20030] Received Message from Server PID: [2419]
The Client [127.0.0.1:20034] Received Message from Server PID: [1569]

可以看到,20 个客户端请求都均衡地在这三个服务进程上。


总结

linux内核自3.9提供的SO_REUSEPORT选项,可以让多进程监听同一个端口。

这种机制有以下优点:

1.提高服务器程序的吞吐性能:我们可以运行N个应用程序实例,充分利用多核CPU资源,避免出现单核在处理数据,其他核却闲着的问题。

2.内核级的负载均衡:我们不需要在多个实例前面添加一层服务代理,因为内核已经提供了简单的负载均衡。

以上就是今天的内容,如果大家有疑问或者新的想法,欢迎联系我沟通交流。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  ehrZuhofWJiC   2024年04月26日   38   0   0 内核linux
  ehrZuhofWJiC   2024年05月17日   36   0   0 linuxsvn
  ehrZuhofWJiC   2024年05月17日   40   0   0 KVMlinux
  ehrZuhofWJiC   2024年05月17日   39   0   0 服务器linux
DEmt2n5ZOrEK
作者其他文章 更多