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

news2024/10/7 14:22:53

文章目录

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1589648.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云服务器公网带宽费用全解析(不同计费模式)

阿里云服务器公网带宽怎么收费&#xff1f;北京地域服务器按固定带宽计费一个月23元/M&#xff0c;按使用流量计费0.8元/GB&#xff0c;云服务器地域不同实际带宽价格也不同&#xff0c;阿里云服务器网aliyunfuwuqi.com分享不同带宽计费模式下带宽收费价格表&#xff1a; 公网…

了解 RISC-V IOMMU

了解 RISC-V IOMMU 个人作为 IOMMU 初学者&#xff0c;从初学者的角度介绍我眼中 RISCV 的 IOMMU 如果有些描述不够专业&#xff0c;还请谅解&#xff0c;也欢迎讨论 部分内容来自 https://zhuanlan.zhihu.com/p/679957276&#xff08;对于 RISCV IOMMU 规范手册的翻译&#xf…

这是雷军的简历,落魄时卡里只有冰冷的40亿

互联网新词儿 周五了&#xff0c;来点放松的&#xff08;嗯&#xff1f;周五&#xff1f;不是上了五天班了吗&#xff0c;为什么不是周六 如果说哪一种炫富秀优越并不伤人&#xff0c;那一定是富含"幽默感"的脱口秀式的吹牛。 我印象中&#xff0c;江湖上还是有这么个…

UDP网络程序

上一章中&#xff0c;我们介绍了socket&#xff0c;以及TCP/UDP协议。这一章带大家实现几个UDP协议的网络服务。我们需要一个 服务端和一个客户端。 1.服务端实现 1.1socket函数 #include <sys/types.h> #include <sys/socket.h>int socket(int domain, in…

Vue结合el-table实现合并单元格(以及高亮单元表头和指定行)

实现效果如下&#xff1a; 思路&#xff1a; 1.首先使用动态表头表格。 2.其次实现动态计算合并单元格。&#xff08;计算规则 传递需要合并的字段&#xff09; 3.然后封装公共的计算单元格方法 export导出供多个页面使用。 4.同时需要封装成公共的组件供多个页面使用。 5…

Hi3861 OpenHarmony嵌入式应用入门--鸿蒙开发环境搭建

目录 简介 准备材料 安装开发环境 配置开发工具和sdk 新建工程 代码编译 简介 本篇将进行hi3861开发环境的搭建&#xff0c;并能够编译默认工程。 准备材料 华为集成开发环境工具DevEco Device Tool 华为集成开发环境IDE DevEco Device Tool下载 | HarmonyOS设备开发 …

书生潽语趣味 demo

创建开发机 使用 InternLM2-Chat-1.8B 模型生成 300 字的小故事 运行猪八戒模型试一下

linux使用docker实现redis主从复制和哨兵模式

目录 1. 拉取redis镜像 2.使用可视化redis工具 3. 设置从redis 4.设置哨兵模式 5. 使用docker-compose快速创建 1. 拉取redis镜像 docker pull redis 默认拉取最新的镜像。 然后pull结束后使用docker images检查镜像&#xff1a; 然后docker run创建container容器 首先…

统计 | Levene检验

Levene检验是方差齐性检验的一种&#xff0c;即检验各组样本方差是否相等的一种统计方法.它通常用于方差分析&#xff08;ANOVA&#xff09;前的一个步骤。Levene检验的零假设是各组的方差相等。 Levene检验的数学步骤如下&#xff1a; 1. 数据准备&#xff1a; 假设我们有个…

Scikit-learn 快速入门篇

Sklearn 简介 scikit-learn (sklearn) 是 Python 中用于机器学习的最流行的库之一。它提供了一系列有效的算法和工具&#xff0c;涵盖各种机器学习任务&#xff0c;包括&#xff1a; 分类回归聚类降维模型选择数据预处理 Sklearn 六大模块 分类&#xff1a;预测离散类别 算…

腾讯云向量数据库-RAG介绍

1.说明 RAG结合LLM(通用大预言模型)构件基于私有文档、专业领域知识、实时信息的charbot。 2.RAG的主要步骤 知识切片成chunk向量化chunk入库query检索知识chunk构件prompts调用llm生成回答 3.优势 快速构件demo快速理解rag社区支持 4.痛点 投入大效果差调优难 5.RAG应…

激光slam论文汇总

文章目录 2014LOAM: Lidar Odometry and Mapping in Real-time 2018LeGO-LOAM: Lightweight and Ground-Optimized Lidar Odometry and Mapping on Variable Terrain 2020LIO-SAM: Tightly-coupled Lidar Inertial Odometry via Smoothing and Mapping 2021LVI-SAM: Tightly-co…

多 线 程

1&#xff0e;什么是多线程? 有了多线程&#xff0c;我们就可以让程序同时做多件事情 2.多线程的作用? 提高效率 3&#xff0e;多线程的应用场景? 只要你想让多个事情同时运行就需要用到多线程 比如:软件中的耗时操作、所有的聊天软件、所有的服务器 1.进程和线程【理解】 …

PHP01——php快速入门 之 使用phpstudy快速搭建PHP环境

PHP01——php快速入门 之 使用phpstudy快速搭建PHP环境 0. 前言1. 下载小皮面板1.1 下载phpstudy&#xff08;小皮面板&#xff09;1.2 启动、简单访问1.2.1 启动Apache1.2.2 访问1.2.3 访问自定义文件或页面 2. 创建网站2.1 创建网站2.2 可能遇到的问题2.2.1 hosts权限问题&am…

靠谱的香港服务器有哪些(看过才后悔)_避坑血泪史

最受欢迎的外贸建站服务器就是香港服务器&#xff0c;可是很多人还不知道如何辨别香港服务器商家是否靠谱。 小编曾经也是建站新手&#xff0c;以前也碰到过很多不靠谱的服务器商家&#xff0c;各种踩坑&#xff0c;可以说都是用钱堆出来的经验。在这里小编给大家总结下&#…

ADG数据库迁移方案

一、环境说明 源库 目标库 IP 192.168.37.201 192.168.37.202 系统版本 RedHat 7.9 RedHat 7.9 数据库版本 19.3.0.0.0 19.3.0.0.0 SID pri std hostname primary standby 数据量 整个库 说明:源库已经创建数据库实例&#xff0c;目标库只有数据库软件。…

蓝桥杯— —小明的背包问题

小明的背包问题 小明的背包1 — — &#xff08;01背包&#xff09; 友情链接&#xff1a;小明的背包1 题目&#xff1a; 输入样例: 5 20 1 6 2 5 3 8 5 15 3 3 输出样例&#xff1a; 37思路&#xff1a; 对于01背包问题&#xff0c;其中一个重要的条件是每一种物品只有一个…

git查看单独某一个文件的历史修改记录

git查看单独某一个文件的历史修改记录 git log -p 文件具体路径 注意&#xff0c;Windows下默认文件路径分隔符是 \&#xff0c;在git bash 里面需要改成 /。 git基于change代码修改与提交_git change-CSDN博客文章浏览阅读361次。git cherry-pick&#xff1a;复制多个提交comm…

2023全国青少年信息素养大赛总决赛C++小学组真题

2023 全国青少年信息素养大赛总决赛C小学组真题 第一题 给定一个五位数x&#xff0c;你需要重复做以下操作: 把数的各个数位进行由大到小排序和由小到大排序&#xff0c;得到的最大值和最小值&#xff0c;进行求差后作为新的x。 可以证明&#xff0c;在经过有限次操作后&…

代码随想录算法训练营33期 第三十一天(补29) | 491. 非递减子序列、46. 全排列、47. 全排列 II

491. 非递减子序列 class Solution { public:vector<int> path;vector<vector<int>> result;void BackTracking(vector<int>& nums, int index){if(path.size()>2){result.push_back(path);}unordered_set<int> usedSet;for (int iindex…