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

文章目录

  • [go实现定时检查大量的 CLOSE_WAIT 连接](#go实现定时检查大量的 CLOSE_WAIT 连接)
    • [背景:为什么监控指定端口上的 CLOSE_WAIT 连接数量原因:](#背景:为什么监控指定端口上的 CLOSE_WAIT 连接数量原因:)
    • 什么是CLOSE_WAIT
    • [go实现定时检查大量的 CLOSE_WAIT 连接](#go实现定时检查大量的 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

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

相关推荐
EasyCVR1 小时前
萤石设备视频接入平台EasyCVR多品牌摄像机视频平台海康ehome平台(ISUP)接入EasyCVR不在线如何排查?
运维·服务器·网络·人工智能·ffmpeg·音视频
明月看潮生1 小时前
青少年编程与数学 02-003 Go语言网络编程 09课题、Cookie
青少年编程·golang·网络编程·编程与数学
明月看潮生2 小时前
青少年编程与数学 02-003 Go语言网络编程 15课题、Go语言URL编程
开发语言·网络·青少年编程·golang·编程与数学
明月看潮生2 小时前
青少年编程与数学 02-003 Go语言网络编程 14课题、Go语言Udp编程
青少年编程·golang·网络编程·编程与数学
龙哥说跨境2 小时前
如何利用指纹浏览器爬虫绕过Cloudflare的防护?
服务器·网络·python·网络爬虫
懒大王就是我3 小时前
C语言网络编程 -- TCP/iP协议
c语言·网络·tcp/ip
Elaine2023913 小时前
06 网络编程基础
java·网络
hlsd#3 小时前
go 集成go-redis 缓存操作
redis·缓存·golang
海绵波波1074 小时前
Webserver(4.3)TCP通信实现
服务器·网络·tcp/ip
热爱跑步的恒川7 小时前
【论文复现】基于图卷积网络的轻量化推荐模型
网络·人工智能·开源·aigc·ai编程