golang实现定时监控 CLOSE_WAIT 连接的数量

go实现定时检查大量的 CLOSE_WAIT 连接

监控指定端口的连接状态,特别是关注 CLOSE_WAIT 连接的数量。CLOSE_WAIT 是指 TCP 连接关闭时,连接的一端等待关闭的另一端发送最后的确认信号。如果存在大量的 CLOSE_WAIT 连接,可能意味着网络连接没有正常关闭,可能会导致资源泄漏或其他问题。

背景:为什么监控指定端口上的 CLOSE_WAIT 连接数量原因:

  1. 资源泄漏检测:大量的 CLOSE_WAIT 连接可能是由于网络连接没有正常关闭导致的资源泄漏。通过监控 CLOSE_WAIT 连接数量,可以及时发现这些连接,从而识别和解决资源泄漏问题。
  2. 网络连接管理:CLOSE_WAIT 连接可能会占用系统资源,如文件描述符等。通过监控连接数量,可以更好地管理和优化网络连接,确保连接的正常关闭和释放。
  3. 故障排查:CLOSE_WAIT 连接可能是网络故障或应用程序错误的指示。通过监控连接数量,可以定位和解决潜在的网络问题,加快故障排查的速度。
  4. 安全性:异常的 CLOSE_WAIT 连接可能是一种恶意行为的指示,如拒绝服务攻击等。通过监控连接数量,可以及时发现可疑连接,采取相应的安全措施。

什么是CLOSE_WAIT

客户端主动关闭连接,服务器接收到客户端的FIN,但是还没有发送自己的FIN,此时的状态为close_wait状态,大量的close_wait状态拖累服务器性能。

在这里插入图片描述

主动关闭的一方发出 FIN 包,被动关闭的一方响应 ACK 包,此时,被动关闭的一方就进入了 CLOSE_WAIT 状态。 如果一切正常,稍后被动关闭的一方也会发出 FIN 包,然后迁移到 LAST_ACK 状态。

通常,CLOSE_WAIT 状态在服务器停留时间很短**,如果你发现大量的 CLOSE_WAIT 状态,那么就意味着被动关闭的一方没有及时发出 FIN 包**,一般有如下几种可能:

  • 程序问题:如果代码层面忘记了 close 相应的 socket 连接,那么自然不会发出 FIN 包,从而导致 CLOSE_WAIT 累积;或者代码不严谨,出现死循环之类的问题,导致即便后面写了 close 也永远执行不到。
  • 响应太慢或者超时设置过小:如果连接双方不和谐,一方不耐烦直接 timeout,另一方却还在忙于耗时逻辑,就会导致 close 被延后。响应太慢是首要问题,不过换个角度看,也可能是 timeout 设置过小。

go实现定时检查大量的 CLOSE_WAIT 连接

通过定期执行 netstat 命令并记录结果,该程序可以提供一种简单的方式来监控 CLOSE_WAIT 连接的数量,并将结果写入日志文件进行进一步分析和处理。

代码位置:https://gitcode.net/inthat/mymonitor

main.go

package main

import (
	"context"
	"fmt"
	lcli "mymonitor/cli"
	socketmonitorlog "mymonitor/lib/socketmonitorlog"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"io"
	"os"

	logging "github.com/ipfs/go-log/v2"
	"github.com/urfave/cli/v2"
)

var log = logging.Logger("socket-go-monitor")

func init() {

}

func exitHandle(exitChan chan os.Signal) {

	for {
		select {
		case sig := <-exitChan:
			fmt.Println("接受到来自系统的信号:", sig)
			os.Exit(1) //如果ctrl+c 关不掉程序,使用os.Exit强行关掉
		}
	}

}

func main() {
	socketmonitorlog.SetupLogLevels()

	exitChan := make(chan os.Signal)
	signal.Notify(exitChan, os.Interrupt, os.Kill, syscall.SIGTERM)
	go exitHandle(exitChan)

	app := &cli.App{
		Name:  "socket-go-monitor",
		Usage: "Start socket monitor",
		Flags: []cli.Flag{
			&cli.StringFlag{
				Name:    "port",
				Aliases: []string{"p"},
				Usage:   "specify monitor port ",
			},
			&cli.StringFlag{
				Name:    "threshold",
				Aliases: []string{"t"},
				Usage:   "specify socket threshold num ",
			},
			&cli.BoolFlag{
				Name:    "cmd",
				Aliases: []string{"s"},
				Value:   false,
				Usage:   "do cmd",
			},
		},
		Action: func(cctx *cli.Context) error {
			log.Info("Starting socket monitor")

			ctx := lcli.ReqContext(cctx)
			ctx, cancel := context.WithCancel(ctx)
			defer cancel()

			//get options
			port := cctx.String("port") // 获取命令行参数中的端口号
			// 获取阈值
			threshold := cctx.Int("threshold")

			var (
				cmd    *exec.Cmd
				output []byte
				err    error
			)

			filename := "socket_monitor.txt"
			file, err := os.Create(filename)
			if err != nil {
				fmt.Println(err)
			}
			defer file.Close()

			var wg sync.WaitGroup

			//创建定时器,每隔600秒后,定时器就会给channel发送一个事件(当前时间)
			ticker := time.NewTicker(time.Second * 600)
			defer ticker.Stop()

			i := 0
			wg.Add(1)
			go func(t *time.Ticker) {
				defer wg.Done()
				for { //循环
					<-t.C
					i++
					fmt.Println("i = ", i)
					// 生成Cmd
					cmd = exec.Command("/bin/bash", "-c", fmt.Sprintf("netstat -an|grep %s|grep CLOSE_WAIT|wc -l\n", port))
					// 执行了命令, 捕获了子进程的输出( pipe )
					if output, err = cmd.CombinedOutput(); err != nil {
						fmt.Println(err)
						return
					}
					//打印子进程的输出
					fmt.Println(string(output))
					// Parse the output as an integer
					closeWaitCount, err := strconv.Atoi(strings.TrimSpace(string(output)))
					if err != nil {
						fmt.Println(err)
						return
					}

					// Check if the count is greater than the threshold and print if it is
					if closeWaitCount > threshold {
						var nowtime = time.Unix(time.Now().Unix(), 0).Format("2006-01-02 15:04:05")
						str := fmt.Sprintf("%s CLOSE_WAIT COUNT: %d \n", nowtime, closeWaitCount)
						fmt.Println(str)

						n, err := io.WriteString(file, str)
						if err != nil {
							fmt.Println(n, err)
						}
					}

					// if i == 10000000 {
					// 	t.Stop() //停止定时器
					// 	return
					// }
				}
			}(ticker)

			wg.Wait()

			return nil
		},
	}
	app.Setup()

	//os.Args启动程序
	if err := app.Run(os.Args); err != nil {
		log.Warnf("%+v", err)
		return
	}
	fmt.Println("ends")
}

参考

浅谈CLOSE_WAIT
参考URL: https://cloud.tencent.com/developer/article/1918110

最近更新

  1. TCP协议是安全的吗?

    2024-04-10 15:06:03       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-10 15:06:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-10 15:06:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-10 15:06:03       20 阅读

热门阅读

  1. LeetCode-热题100:23. 合并 K 个升序链表

    2024-04-10 15:06:03       11 阅读
  2. 计算机网络---第四天

    2024-04-10 15:06:03       15 阅读
  3. react类组件转化为函数组件

    2024-04-10 15:06:03       12 阅读
  4. docker 的常用命令

    2024-04-10 15:06:03       16 阅读
  5. torch.mean()的使用方法

    2024-04-10 15:06:03       13 阅读
  6. 卷积神经网络介绍和实例

    2024-04-10 15:06:03       15 阅读
  7. 建造者模式(生成器模式)

    2024-04-10 15:06:03       15 阅读