当前位置: 首页 > news >正文

线上 udp 客户端请求服务端客户端句柄泄漏问题

本题分别从如下三个方面来分享:

  • 问题描述
  • 自定义连接池的编写
  • common_pool 的使用

问题描述

线上有一个业务,某个通服务通知 udp 客户端通过向 udp 服务端(某个硬件设备)发送 udp 包来进行用户上线操作

当同时有大量的请求打到 udp 服务端的时候,udp 服务端的回包可能会在网络环境中丢包,(udp 是不可靠的)导致 udp 客户端不能及时的收到 udp 服务端的回包,在短时间内,udp 客户端的句柄又没有得到复用或者释放,没有收到回包的句柄就一直阻塞在那里,最终导致句柄泄漏

那么可以如何解决呢?

  • 增大客户端的句柄数
  • 使用连接池并且在读取服务端响应数据时加上超时时间

显然,第一个解决方式治标不治本,改大句柄数,当请求量变大的时候,仍然会出现句柄泄漏的情况

第二种方式相对靠谱很多

  • 首先,咱们将发送 udp 包给服务端后,等待读取服务端的回包时,设置超时时间,超时后读取失败,释放或者归还句柄
  • 维护一个内部的连接池,减少每一次创建句柄消耗的资源和时间,使用的时候从池子里面获取句柄,使用完毕之后再归还句柄

自定义连接池的编写 customer_pool

那么对于连接池,我们实际上是可以自己来进行造轮子的,仅用于学习,实际使用的话,自然还是会去使用经过大众考研过的公共开源库,我们可以来基本的分析和研究一下一个连接池需要有些什么?

  • 创建池子,关闭池子,池子的关闭状态
  • 从池子中获取连接,归还连接,销毁当前连接
  • 池子中能容纳的最大连接数,最小连接数,当前连接数
  • 根据当前实际的连接数来对池子进行扩容和缩容
  • 池子中创建连接的函数具体实现

当然,我们自己来体会一下连接池以及演示上述 udp 的 demo,我们仅实现如下几个简单功能作为演示

  • 创建池子,池子的关闭状态
  • 从池子中获取连接,归还连接
  • 池子中能容纳的最大连接数,最小连接数,当前连接数
  • 池子中创建连接的函数具体实现

对于池子中具体链接的销毁,池子的关闭,池子的扩缩容,以及其他高级使用,xdm 可以进行扩展

customer_pool demo

自定义连接池,实际上咱们是使用 chan 通道来进行实现,具体源码可以查看:https://github.com/qingconglaixueit/customer_pool/blob/master/customer_pool/pool.go

  1. 定义连接池 MyConnPool 数据结构,和创建连接池

    • MyConnPool 结构中的 sync.Mutex 主要是用于控制多协程中 非 pool 成员的其他成员的互斥,我们知道 chan 内部是有锁进行控制的

image.png

image.png

  1. 获取对象的具体实现

  • 从池子中获取对象,如果获取不到则默认查看当前的池子状态是否可以创建新的连接
  • 若可以,则直接创建连接,返回对象
  • 此处在进行池子成员的变动时,需要加锁进行控制
func (conn *MyConnPool) GetObject() (interface{}, error) {return conn.getObject()
}
func (conn *MyConnPool) getObject() (interface{}, error) {if conn.isClosed {return nil, errors.New("pool is closed")}// 从通道里面读,如果通道里面没有则新建一个select {case object := <-conn.pool:return object, nildefault:}// 校验当前的连接数是否大于最大连接数,若是,则还是需要从 pool 中取// 此时使用 mutex 主要是为了锁 MyConnPool 的非通道的其他成员conn.Lock()if conn.currentConn >= conn.maxConn {object := <-conn.poolconn.Unlock()return object, nil}// 逻辑走到此处需要新建对象放到 pool 中object, err := conn.connFun()if err != nil {conn.Unlock()return nil, fmt.Errorf("create conn error : %+v", err)}// 当前 pool 已有连接数+1conn.currentConn++conn.Unlock()return object, nil
}
  1. 释放对象的具体实现

  • 使用完毕对象之后,需要归还
  • 具体归还操作,则是将具体的连接再丢回通道里面即可
func (conn *MyConnPool) ReturnObject(object interface{}) error {return conn.returnObject(object)
}
func (conn *MyConnPool) returnObject(object interface{}) error {if conn.isClosed {return errors.New("pool is closed")}conn.pool <- objectreturn nil
}
  1. 具体的应用

简单写一个 udp 服务端

  • 可以查看源码地址:https://github.com/qingconglaixueit/use_common_pool/blob/master/server/main.go
  • 代码注释部分用于测试超时的效果

使用咱们上述的自定义连接池编写客户端的 demo

具体源码地址:https://github.com/qingconglaixueit/customer_pool/blob/master/main.go

  1. 定义咱们有 udp 连接的对象
  • 定义 PoolTest 对象,并简单的将 udp 连接加入到成员中
  • 编写创建 udp 连接的函数 connectUdp
  • 初始化连接池,设置池子最大 3 个连接,最小 1 个连接,实际创建连接函数为 connectUdp()
type PoolTest struct {Conn *net.UDPConn
}var myPool *customer_pool.MyConnPoolfunc init() {myPool = customer_pool.NewMyConnPool(3, 1, func() (interface{}, error) {return connectUdp()})if myPool == nil {log.Panicln("NewMyConnPool error")return}log.Println("myPool == ", myPool)
}
// 创建连接函数
func connectUdp() (*PoolTest, error) {// 创建一个 udp 句柄log.Println(">>>>> 创建一个 udp 句柄 ... ")// 连接服务器conn, err := net.DialUDP("udp", nil, &net.UDPAddr{IP:   net.IPv4(127, 0, 0, 1),Port: 9998,})if err != nil {log.Println("Connect to udp server failed,err:", err)return nil, err}log.Printf("<<<<<< new udp connect %+v", conn)return &PoolTest{Conn: conn}, nil
}
  1. 获取到连接对象之后,咱们给 udp server 写入数据
  • GetObject() 获取具体的对象,获取到连接
  • SendMsg 进行具体消息的发送
  • ReturnObject() 将具体的对象归还到池子中
  • 其中代码被注释掉的部分,是用力验证超时效果的,感兴趣的 xdm 可以将代码打开尝试一波

image.png

  1. 效果展示

最后补充上咱们的 main 函数,就可以进行测试验证了

func main() {for i := 0; i < 10; i++ {msg := fmt.Sprintf("send udp data is %d", i)go SendMsg(msg)}time.Sleep(2 * time.Second)
}

启动咱们的 udp 客户端,和 udp 服务端,我们可以查看到如下效果

客户端效果:

同时启了 10 个协程,每一个协程都会去池子里面拿连接对象,如果池子有现成的则直接使用,如果没有现成的,那么就新建一个连接, 如果当前池子已创建连接已经等于最大值,那么只能等着池子中有连接归还的时候再进行分配

  • 总的来说,当前 demo 只会创建 3 个 udp 连接句柄

服务端效果:

可以看到服务端收到的 10 个请求,实际上只有 3 个句柄在多次请求

再一次印证了客户端实际上确实只创建了 3 次 udp 句柄

上述是自定义简单连接池的基本 demo,关于 udp 超时处理的内容就不做演示,感兴趣的 xdm 可以下载源码来进行查看效果

https://github.com/qingconglaixueit/customer_pool

使用 go-commons-pool

当然,我们大致知道连接池基本是都有哪些组成部分,可以如何玩之后,我们来应用一个 golang 通用的连接池 go-commons-pool, 源码地址为:https://github.com/jolestar/go-commons-pool

use_common_pool demo

应用 go-commons-pool 咱们的 demo 仅验证该库的通用和便捷,对于上述我们自定义的池子,咱们使用到的 udp 涉及到的代码,可以基本不用变动,直接使用 go-commons-pool 直接网上套即可

和咱们自定义池子不一样的地方

  • init 初始化池子的方法和配置不一样
  • SendMsg 方法,实现时使用的池子句柄不一样
  • 当然,go-commons-pool 会好太多

实际 demo 为:

  • 其余截图上未体现的 connectUdp(),(this *PoolTest) SendMsg(data []byte) , 和上述自定义池子实现方式完全一致

此处初始化池子配置,咱们也是一样传入具体池子最大的对象数,使用池子的默认配置,传入咱们创建连接的具体函数 connectUdp()

对于到 main 函数 和 SendMsg 函数,咱们的用法和写法基本完全一致

自然,效果也是一样的

当然对于 go-commons-pool 池子还有其他很多有意思的东西,感兴趣的可以来一起阅读以下他的源码,如下为当前池子的基本数据结构和创建池子的代码,咱们可以根据这个结构来追以下代码

代码目录如下:

./pool.go

  • 基本数据结构
  • 创建池子代码

image.png

至此,本文结束

文中涉及到的源码地址:

  • customer_pool https://github.com/qingconglaixueit/customer_pool
  • use_common_pool https://github.com/qingconglaixueit/use_common_pool
  • go-commons-pool https://github.com/jolestar/go-commons-pool

感谢阅读,欢迎交流,点个赞,关注一波 再走吧

欢迎点赞,关注,收藏

朋友们,你的支持和鼓励,是我坚持分享,提高质量的动力

好了,本次就到这里

技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。

我是阿兵云原生,欢迎点赞关注收藏,下次见~
可以进入地址进行体验和学习:https://xxetb.xet.tech/s/3lucCI

相关文章:

线上 udp 客户端请求服务端客户端句柄泄漏问题

本题分别从如下三个方面来分享&#xff1a; 问题描述 自定义连接池的编写 common_pool 的使用 问题描述 线上有一个业务&#xff0c;某个通服务通知 udp 客户端通过向 udp 服务端&#xff08;某个硬件设备&#xff09;发送 udp 包来进行用户上线操作 当同时有大量的请求打到…...

合宙Air724UG LuatOS-Air LVGL API控件-窗口 (Window)

窗口 (Window) 分 享导出pdf 示例代码 win lvgl.win_create(lvgl.scr_act(), nil) lvgl.win_set_title(win, "Window title") -- close_btn lvgl.win_add_btn_right(win, "\xef\x80\x8d") -- --lvgl.obj_set_event_cb(cl…...

80 # 图片防盗链

referer 来源&#xff0c;表示这个资源被谁引用过&#xff0c;可以用来做防盗链。 我们新建文件 no-referer.js const fs require("fs"); const path require("path"); const url require("url"); const http require("http");h…...

App自动化测试持续集成效率提高50%

持续集成是一种开发实践&#xff0c;它倡导团队成员需要频繁的集成他们的工作&#xff0c;每次集成都通过自动化构建&#xff08;包括编译、构建、自动化测试&#xff09;来验证&#xff0c;从而尽快地发现集成中的错误。让正在开发的软件始终处于可工作状态&#xff0c;让产品…...

LeetCode —— 复写零(双指针)

题目链接 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目解析 将数组中出现的每个零复写一遍&#xff0c;然后将其他元素向右平移&#xff0c;数组长度不能改变。 法一&#xff1a;使用额外空间的做法 class Solution { public:void duplica…...

【Vue篇】Vue 项目下载、介绍(详细版)

如何创建一个vue项目&#xff1f;首先要有环境&#xff0c;如下&#xff1a; nodejs vue-cli如果有以上的工具就直接跳过安装教程 【Vue篇】mac上Vue 开发环境搭建、运行Vue项目&#xff08;保姆级&#xff09; 创建vue项目 选择一个位置&#xff0c;你要存放项目的路径&…...

Python批处理(一)提取txt中数据存入excel

Python批处理&#xff08;一&#xff09;提取txt中数据存入excel 问题描述 现从冠层分析软件中保存了叶面积指数分析的结果&#xff0c;然而软件保存格式为txt&#xff0c;且在不同的文件夹中&#xff0c;每个文件夹的txt文件数量不固定&#xff0c;但是txt文件格式固定。现需…...

只考一门数据结构!安徽工程大学计算机考研

安徽工程大学 考研难度&#xff08;☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文992字&#xff0c;预计阅读&#xff1a;3分钟 2023考情概况 安徽工程大…...

Ubuntu 20.04出现蓝牙无法打开的问题(已解决)

安装Ubuntu20.04后&#xff0c;蓝牙无法打开&#xff0c;按钮开启后蓝牙仍处于关闭状态 解决方法&#xff08;四种方式&#xff09; 1.卸载并重新加载btusb内核模块&#xff08;支持蓝牙设备的内核模块&#xff09; sudo rmmod btusb sleep 1 sudo modprobe btusb2、安装蓝牙工…...

并发测试工具 apache-jmeter使用发送post请求JSON数据

目录 1 下载安装 2 汉化 3 创建高并发测试 配置线程组 创建web请求 创建监听器 结果树 汇总报告 为web请求添加token 添加Content-Type用于发送json 4 启动测试 5 查看结果 1 下载安装 官网Apache JMeter - Download Apache JMeter 解压运行 2 2 汉化 打开软件…...

牛客练习赛115 A Mountain sequence

题目&#xff1a; 样例&#xff1a; 输入 3 5 1 2 3 4 5 3 3 3 3 3 1 2 1 输出 16 1 3 思路&#xff1a; 依据题意&#xff0c;再看数据范围&#xff0c;可以知道暴力肯定是不可能了&#xff0c;然后通过题目意思&#xff0c;我们可以排列模拟一下&#xff0c;这里排列所得结…...

通过git bash激活虚拟环境遇到的问题

直接git bash后用conda activate激活一直报错 报错如下&#xff1a; CommandNotFoundError: Your shell has not been properly configured to use ‘conda activate’. If using ‘conda activate’ from a batch script, change your invocation to ‘CALL conda.bat activa…...

EasyAVFilter代码示例之将摄像机RTSP流转成RTMP推流输出

以下是一套完整的RTSP流转RTMP推流功能的开发源码&#xff0c;就简简单单几行代码&#xff0c;就可以完成原来ffmpeg很复杂的调用流程&#xff0c;而且还可以集成在自己的应用程序中调用&#xff0c;不需要再单独一个ffmpeg的进程来调用&#xff0c;方法很简单&#xff1a; #i…...

【【C语言康复训练-4】】

C语言康复训练-4 head.h #pragma once #define ROWS 11 #define COLS 11 #define ROW 9//为什么会在头文件中定义两个 因为1到9是我们想要实现的标准单元 #define COL 9 //但是对于我们幕后调控者&#xff0c;对边角上并不能和其他一样方便操作&#xff0c;所以我们向外拓展了…...

[DM8] DM-DM DBLINK DPI方式

前言 对于DM与DM之间的DBLINK&#xff0c;三种方式中&#xff0c;使用DPI方式配置上最为方便&#xff0c;ODBC方式需要安装ODBC包并配置ODBC数据源&#xff0c;dmmal方式需要设置MAL_INI数据库参数、配置dmmal.ini文件并需要重启数据库服务。 dpi类型的dblink&#xff0c;达梦…...

创建了一个名为nums_list的vector容器,其中存储了一系列的pair<int, int>

vector<pair<int, int>> nums_list;for (int i 0; i < nums.size(); i) {nums_list.emplace_back(i, nums[i]);}这段代码创建了一个名为nums_list的vector容器&#xff0c;其中存储了一系列的pair<int, int>。代码的逻辑如下&#xff1a;1. 创建一个空的…...

SpringMVC文件上传、文件下载多文件上传及jrebel的使用与配置

一.文件上传 1.导入依赖 <dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.3</version> </dependency> 2.配置文件上传解析器 在spring-mvc.xml文件中添加文件…...

Leetcode143. 重排链表

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 给定一个单链表 L 的头节点 head &#xff0c;单链表 L 表示为&#xff1a; L0 → L1 → … → Ln - 1 → Ln请将其重新排列后变为&#xff1a; L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … 不能只…...

Git 回顾小结

Git是一个免费开源&#xff0c;分布式的代码版本控制系统&#xff0c;版主开发团队维护代码 作用&#xff1a;记录代码内容&#xff0c;切换代码版本&#xff0c;多人开发时高校合并代码内容 Git常用命令 命令作用注意git -v查看Git版本git init初始化本地Git仓库git add 文件…...

响应式布局(3种) + flex计算

响应式布局 1.媒体查询2.使用百分比、rem、vw、vh等相对单位来设置元素的宽度、高度、字体大小等1.rem与em2.vw、vh、vmax、vmin 3.Flexboxflexbox计算题 响应式布局是指同一个页面在不同屏幕尺寸下有不同的布局。 1.媒体查询 媒体查询是最基础的实现响应式的方式 使用media关键…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

VB.net复制Ntag213卡写入UID

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

Redis数据倾斜问题解决

Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中&#xff0c;部分节点存储的数据量或访问量远高于其他节点&#xff0c;导致这些节点负载过高&#xff0c;影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...

NPOI Excel用OLE对象的形式插入文件附件以及插入图片

static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...

android RelativeLayout布局

<?xml version"1.0" encoding"utf-8"?> <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"android:gravity&…...

Unity中的transform.up

2025年6月8日&#xff0c;周日下午 在Unity中&#xff0c;transform.up是Transform组件的一个属性&#xff0c;表示游戏对象在世界空间中的“上”方向&#xff08;Y轴正方向&#xff09;&#xff0c;且会随对象旋转动态变化。以下是关键点解析&#xff1a; 基本定义 transfor…...