panjf2000 / gnet

🚀 gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go.
https://gnet.host
Apache License 2.0
9.7k stars 1.04k forks source link

[Question]: Please tell me about the address and port occupation problem after concurrent requests. #645

Closed kwok828 closed 4 weeks ago

kwok828 commented 4 weeks ago

Actions I've taken before I'm here

Questions with details

  1. gnet版本:2.5.9
  2. go版本: go version go1.22.2 linux/amd64
  3. 问题:使用gnet处理http请求,option启用了WithMulticore、WithTCPKeepAlive、WithReusePort;项目运行在wsl(ubuntu)/centos下(两个系统都运行过),使用nodejs编写http并发请求,每次请求都建立新连接。当连接数达到1.5w的时候,出现错误:Error connect EADDRINUSE 127.0.0.1:8081 查看端口,1.5w个连接如下TIME_WAIT 状态,不会进行复用 TCP 127.0.0.1:14990 127.0.0.1:8081 TIME_WAIT 0

Code snippets (optional)

nodejs代码
import got from 'got';
import https from 'https';
import fs from 'fs';

const agent = new https.Agent({
    keepAlive : true
    // rejectUnauthorized: false
});

let count = 0;
let success = 0;
let fail = 0;
let latestCount = 0;
let latestSuccess = 0;
let latestFail = 0;
let current = 0;
async function worker() {
    while (true) {
        if (current < 100) {
            doRequest();
        } else {
            await new Promise(resolve => setTimeout(resolve, 5));
        }
    }
}

async function doRequest() {
    current++;
    try {
        count++;
        // const res = await got.post('http://localhost:9081', {
        const res = await got.post('http://127.0.0.1:8081/dispatch/device', {
            // agent: {https: agent}
        });
        // console.log(res.body);
        success++;
    } catch (e) {
        if(e.res && e.res.body){
            console.error('Error', e.res.body);
        }else{
            console.error('Error', e.message);
        }
        fail++;
    }
    current--;
}

async function monitor() {
    while (true) {
        let difCount = count - latestCount;
        let difSuccess = success - latestSuccess;
        let difFail = fail - latestFail;
        console.log(`count=${difCount} success=${difSuccess} fail=${difFail}`);

        latestCount = count;
        latestSuccess = success;
        latestFail = fail;
        await new Promise(resolve => setTimeout(resolve, 1000));
    }
}

(async () => {
    try {
        monitor();
        for (let i = 0; i < 1; i++) {
            worker();
        }
    } catch (e) {
        console.log(e.message);
    }
})();

gnet代码
package main

func main() {
    serverStart()
}

func serverStart() {

    httpPorts := appconfig.Config.App.HttpPort

    // 启动gnet监听http请求
    for _, port := range httpPorts {
        go startHTTPServer(port)
    }

    select {}
}

func startHTTPServer(port int) {
    hs := &httpserver.HttpServer{
        Addr:      fmt.Sprintf("tcp://0.0.0.0:%d", port),
        Multicore: true,
    }
    options := []gnet.Option{
        gnet.WithMulticore(hs.Multicore),
        gnet.WithTicker(true),
        gnet.WithTCPNoDelay(gnet.TCPNoDelay),
        gnet.WithReusePort(true),
        gnet.WithTCPKeepAlive(10 * time.Second),
    }
    rateLimitedIdleHandler := &handler.RateLimitedIdleHandler{
        EventHandler: hs,
        IdleTimeout:  8 * time.Second,
    }
    err := gnet.Run(rateLimitedIdleHandler, hs.Addr, options...)
    if err != nil {
        logger.ErrorLog.Panic(err)
    }
}
kwok828 commented 4 weeks ago

由于之前nodejs脚本是通过windows环境运行的,出现了该问题。 刚刚在linux下运行nodejs脚本模拟请求没问题了。

gh-translator commented 4 weeks ago

🤖 Non-English text detected, translating ...


This problem occurred because the nodejs script was previously run through the windows environment. I just ran the nodejs script under Linux to simulate the request with no problem.