go tcp 同步请求-响应模式 与ulimit
客户端 服务端 典型的同步请求-响应模型
简单的 Go 示例,展示了如何实现您描述的同步请求-响应模型。客户端会发送一个包,然后等待接收服务器的响应。服务器接收包后处理数据,然后发送处理完成的消息。客户端接收到响应后,再次发送下一个包
服务端:
package main import ( "bufio" "fmt" "net" "strings" ) func handleConnection(conn net.Conn) { defer conn.Close() reader := bufio.NewReader(conn) for { // 从客户端读取数据 message, err := reader.ReadString('\n') if err != nil { fmt.Println("Error reading:", err) return } fmt.Printf("Received: %s", message) // 处理数据(这里简单的将消息转换为大写) response := strings.ToUpper(message) // 发送处理后的数据回客户端 _, err = conn.Write([]byte(response)) if err != nil { fmt.Println("Error writing:", err) return } } } func main() { listener, err := net.Listen("tcp", ":12345") if err != nil { fmt.Println("Error starting TCP server:", err) return } defer listener.Close() fmt.Println("TCP server listening on port 12345") for { conn, err := listener.Accept() if err != nil { fmt.Println("Error accepting connection:", err) return } go handleConnection(conn) } }
客户端:
package main import ( "bufio" "fmt" "net" "os" ) func main() { conn, err := net.Dial("tcp", "localhost:12345") if err != nil { fmt.Println("Error connecting to server:", err) return } defer conn.Close() reader := bufio.NewReader(os.Stdin) serverReader := bufio.NewReader(conn) for { // 从标准输入读取数据 fmt.Print("Enter message: ") message, _ := reader.ReadString('\n') // 发送数据到服务端 _, err := conn.Write([]byte(message)) if err != nil { fmt.Println("Error writing to server:", err) return } // 等待服务端响应 response, err := serverReader.ReadString('\n') if err != nil { fmt.Println("Error reading from server:", err) return } fmt.Printf("Server response: %s", response) } }
服务端 文件数量限制 ulimit -n 需要什么时候设置:
1. 在 shell 中临时设置
您可以在启动服务端程序之前在 shell 中临时设置文件描述符限制。这种方法仅对当前的 shell 会话有效,重启后设置会失效。
ulimit -n 65536 ./server
2. 在服务管理器中设置
如果您的服务是通过 systemd 管理的,可以在 systemd 服务配置文件中设置文件描述符限制。这种方法对使用 systemd 启动的服务有效,推荐用于生产环境。
编辑 systemd 服务文件
假设您的服务文件是 /etc/systemd/system/myserver.service
:
[Unit] Description=My TCP Server [Service] ExecStart=/path/to/server LimitNOFILE=65536 [Install] WantedBy=multi-user.target
重新加载 systemd 配置并重启服务
sudo systemctl daemon-reload sudo systemctl restart myserver.service
3. 在系统级别设置
在 /etc/security/limits.conf
文件中设置。这种方法会对所有用户生效,适用于需要为所有进程设置统一限制的情况。
编辑 /etc/security/limits.conf
* soft nofile 65536 * hard nofile 65536
此配置文件中的设置在用户登录时生效。如果您的服务是由特定用户运行的,可以替换 *
为特定的用户名。
确保 PAM 模块加载了这些设置
在 /etc/pam.d/common-session
和 /etc/pam.d/common-session-noninteractive
文件中确保有以下一行:
session required pam_limits.so
示例:设置服务端文件描述符限制
下面是一个完整的步骤示例,假设您有一个名为 myserver
的服务。
-
编辑 systemd 服务文件
创建或编辑
/etc/systemd/system/myserver.service
文件:ini[Unit] Description=My TCP Server [Service] ExecStart=/usr/local/bin/myserver LimitNOFILE=65536 [Install] WantedBy=multi-user.target
-
重新加载 systemd 配置并启动服务
shsudo systemctl daemon-reload sudo systemctl enable myserver.service sudo systemctl start myserver.service
4. 在程序代码中设置
您还可以在 Go 程序代码中动态设置文件描述符限制,不过这种方法不太常见,因为它可能会受到操作系统和用户权限的限制。
Go 代码示例
package main import ( "fmt" "golang.org/x/sys/unix" ) func setRlimit() { var rLimit unix.Rlimit rLimit.Max = 65536 rLimit.Cur = 65536 if err := unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit); err != nil { fmt.Printf("Error setting rlimit: %v\n", err) } } func main() { setRlimit() // 继续初始化并启动您的服务 }
总结
为了确保您的 TCP 服务端可以处理大量并发连接,适当配置文件描述符限制非常重要。最推荐的方法是在 systemd 服务文件中设置 LimitNOFILE
参数,因为它简单且能持久生效。此外,您还可以在 shell 中临时设置、在 /etc/security/limits.conf
文件中设置系统级别的限制,或在程序代码中设置。选择合适的方法取决于您的具体使用场景和需求
https://juejin.cn/post/7224886702883733560:
参考:
https://juejin.cn/post/7224886702883733560