Golang TCP服务器在多个客户端连接时性能下降问题探讨

Golang TCP服务器在多个客户端连接时性能下降问题探讨 你好,我编写了一个简单的TCP服务器和TCP客户端应用程序,但在性能方面得到了不同的输出。以下是我的场景:

TCP服务器应用程序:主要工作是监听套接字,并为每个连接的客户端启动一个新的协程。

TCP客户端应用程序:主要工作是打开一个或多个到TCP服务器的连接,并为每个连接发送/接收一定数量的数据包,然后计算耗时。

问题是,当我启动两个不同的TCP客户端应用程序,每个都使用单个连接连接到TCP服务器时,性能几乎下降了一半。但是,当我只启动一个TCP客户端应用程序,但让它建立两个不同的连接到TCP服务器时,性能却表现良好。有人能告诉我这是什么情况吗?

如果问题出在我的TCP服务器应用程序上,那么当我执行TCP客户端并让它建立两个不同的连接到TCP服务器时,我的性能也应该下降。但实际情况并非如此。

我希望我已经把问题说清楚了。 提前感谢。


更多关于Golang TCP服务器在多个客户端连接时性能下降问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

时间上的实际差异是多少?我们是在讨论4毫秒与8毫秒的差别,还是几秒甚至几分钟以上的差别?

更多关于Golang TCP服务器在多个客户端连接时性能下降问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


从你的描述来看,这很可能与操作系统级别的网络连接管理和Go的调度机制有关。当两个独立的客户端进程各自建立单个连接时,每个连接可能被分配到不同的CPU核心或网络处理队列,而单个客户端进程建立两个连接时,这些连接可能共享相同的底层资源,减少了上下文切换开销。此外,Go的协程调度在单个进程内可能更高效。

以下是一个简单的TCP服务器示例,它使用连接池和协程池来优化多客户端连接的性能:

package main

import (
    "fmt"
    "net"
    "sync"
    "time"
)

type connPool struct {
    pool sync.Pool
}

func newConnPool() *connPool {
    return &connPool{
        pool: sync.Pool{
            New: func() interface{} {
                return &net.TCPConn{}
            },
        },
    }
}

func (p *connPool) get() *net.TCPConn {
    return p.pool.Get().(*net.TCPConn)
}

func (p *connPool) put(conn *net.TCPConn) {
    p.pool.Put(conn)
}

func handleConnection(conn net.Conn, pool *connPool) {
    defer func() {
        if tcpConn, ok := conn.(*net.TCPConn); ok {
            pool.put(tcpConn)
        }
        conn.Close()
    }()

    buffer := make([]byte, 1024)
    for {
        n, err := conn.Read(buffer)
        if err != nil {
            break
        }
        conn.Write(buffer[:n])
    }
}

func main() {
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println("Error listening:", err)
        return
    }
    defer listener.Close()

    pool := newConnPool()
    var wg sync.WaitGroup
    sem := make(chan struct{}, 100) // 限制并发协程数

    fmt.Println("TCP Server started on :8080")
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting connection:", err)
            continue
        }

        wg.Add(1)
        sem <- struct{}{}
        go func(c net.Conn) {
            defer wg.Done()
            handleConnection(c, pool)
            <-sem
        }(conn)
    }
}

这个服务器使用了连接池来重用TCP连接对象,并通过缓冲通道限制并发协程数量,以减少资源竞争和调度开销。对于客户端,确保使用连接池并复用连接:

package main

import (
    "fmt"
    "net"
    "sync"
    "time"
)

func clientWorker(addr string, packets int, wg *sync.WaitGroup) {
    defer wg.Done()
    conn, err := net.Dial("tcp", addr)
    if err != nil {
        fmt.Println("Error connecting:", err)
        return
    }
    defer conn.Close()

    start := time.Now()
    for i := 0; i < packets; i++ {
        conn.Write([]byte("test packet"))
        buffer := make([]byte, 1024)
        conn.Read(buffer)
    }
    elapsed := time.Since(start)
    fmt.Printf("Connection completed in %v\n", elapsed)
}

func main() {
    addr := "localhost:8080"
    packets := 1000
    var wg sync.WaitGroup

    // 模拟单个客户端多个连接
    for i := 0; i < 2; i++ {
        wg.Add(1)
        go clientWorker(addr, packets, &wg)
    }
    wg.Wait()
}

性能差异可能源于操作系统对来自不同进程的连接处理方式不同,例如网络中断分配、CPU亲和性等。使用netstatss命令检查连接状态,并通过Go的pprof进行性能分析:

import _ "net/http/pprof"

在服务器代码中添加:

go func() {
    http.ListenAndServe("localhost:6060", nil)
}()

然后使用go tool pprof分析CPU和内存使用情况。调整服务器和客户端的资源分配,如使用runtime.GOMAXPROCS()设置CPU核心数,或通过syscall.Setpriority调整进程优先级,可能有助于平衡性能。

回到顶部