惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
L
LINUX DO - 热门话题
F
Fox-IT International blog
C
Cisco Blogs
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
Malwarebytes
Malwarebytes
Latest news
Latest news
D
Darknet – Hacking Tools, Hacker News & Cyber Security
SecWiki News
SecWiki News
N
News and Events Feed by Topic
T
True Tiger Recordings
www.infosecurity-magazine.com
www.infosecurity-magazine.com
美团技术团队
P
Palo Alto Networks Blog
V
V2EX - 技术
AWS News Blog
AWS News Blog
A
About on SuperTechFans
Microsoft Azure Blog
Microsoft Azure Blog
量子位
博客园 - 【当耐特】
P
Proofpoint News Feed
N
News and Events Feed by Topic
博客园 - 司徒正美
U
Unit 42
G
Google Developers Blog
阮一峰的网络日志
阮一峰的网络日志
Schneier on Security
Schneier on Security
G
GRAHAM CLULEY
O
OpenAI News
T
The Blog of Author Tim Ferriss
F
Future of Privacy Forum
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
Blog — PlanetScale
Blog — PlanetScale
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
News | PayPal Newsroom
V
Visual Studio Blog
V
V2EX
Simon Willison's Weblog
Simon Willison's Weblog
Microsoft Security Blog
Microsoft Security Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
T
Threat Research - Cisco Blogs
Spread Privacy
Spread Privacy
N
Netflix TechBlog - Medium
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Docker
小众软件
小众软件
H
Hackread – Cybersecurity News, Data Breaches, AI and More
I
Intezer

郑文峰的博客

使用dify对接飞书多维表格 使用n8n对接飞书多维表格 服务启动时出现 OOM Bug 通缉令 一次服务升级时pg表DDL执行超时失败 Go语言高性能编程 Go语言高效IO缓冲技术详解 Go语言延迟初始化(Lazy Initialization)最佳实践 Go语言字符串拼接性能对比与优化指南 Go语言结构体内存对齐完全指南 Go语言空结构体:零内存消耗的高效编程 Go语言堆栈分配与逃逸分析深度解析 Go语言原子操作完全指南 Go语言内存预分配完全指南 Go语言不可变数据共享:无锁并发编程实践 Go语言零拷贝技术完全指南 Go语言遍历性能深度解析:从原理到优化实践 Go语言Interface Boxing原理与性能优化指南 Go协程池深度解析:原理、实现与最佳实践 基于pre-commit的Python代码规范落地实践 初识 MCP Server pulsar阻塞导致logstash无法接入日志 django-prometheus使用及源码分析 kube-proxy源码分析 kubernetes service如何通过iptables转发 tcp缓存引起的日志丢失 django-apschedule定时任务异常停止 理解calico容器网络通信方案原理 理解flannel的三种容器网络方案原理 理解Linux IPIP隧道 理解VXLAN网络 理解Linux TunTap设备 快速了解iptables kafka中listener和advertised.listeners的作用 django rest_framework 分页 django后端服务、logstash和flink接入VictoriaMetrics指标监控 python中import原理 docker容器单机网络 手动实现docker容器bridge网络模型 mysql之MVCC原理 mysql之日志 使用java开发logstash的filter插件 使用python实现单例模式的三种方式 redis之缓存 redis之分片集群 redis之哨兵机制 redis之主从库同步 redis之持久化 redis之五种基本数据类型 go中如何处理error pod中将代码与运行环境分离 友链 ddt源码分析 python装饰器的使用方法 读书笔记:如何阅读一本书 使用ddt实现unittest的参数化测试 分布式锁 使用kubeadm安装k8s 优化gin表单的错误提示信息 gin中validator模块的源码分析 go简单使用grpc python简单使用grpc k8s之PV、PVC和StorageClass k8s之StatefulSet k8s之DaemonSet k8s之Job和CronJob k8s之ConfigMap和Secret k8s之Service k8s之Pod k8s之Deployment 容器的本质 docker容器 python迭代器与生成器 python元编程 python垃圾回收机制 python上下文管理器 django rest_framework使用jwt django rest_framework异常处理 django rest_framework 自定义文档 django压缩文件下载 django rest_framework使用pytest单元测试 django restframework choice 自定义输出数据 django Filtering 使用 django viewset 和 Router 配合使用时报的错 django model的序列化 django中使用AbStractUser django.core.exceptions.ImproperlyConfigured Application labels aren't unique, duplicates users django 中 media配置 django 外键引用自身和on_delete参数 django 警告 while time zone support is active Flask使用flask_socketio实现websocket flask结合mongo tornado 文件上传 tornado 使用jwt完成用户异步认证 tornado 用户密码 bcrypt加密 tornado 结合wtforms使用表单操作 tornado finish和write区别 tornado 使用peewee-async 完成异步orm数据库操作 pyspark streaming简介 和 消费 kafka示例 使用hue创建ozzie的pyspark action workflow
使用etcd分布式锁导致的协程泄露与死锁问题
2025-05-13 · via 郑文峰的博客

# 简介

本文记录自己在工作中排查etcd应用分布式锁而导致的泄露与死锁问题,并通过分析源码找到根因,最终解决。

# 问题重现

# 现象描述

服务出现数据入库失败,且伴随内存持续增长。关键现象:

  1. 锁残留:通过etcdctl get --prefix /my-lock/可看到锁KEY长期存在
  2. 租约续期etcdctl lease timetolive显示租约TTL不断重置
  3. 资源增长:Go程序协程数随请求量线性增长(可通过pprof观测)

# 最小复现代码

func main() {
	// ... etcd client初始化代码不变...

	// 关键问题点1:缺失session关闭
	session, _ := concurrency.NewSession(cli)
	// defer session.Close() // 故意注释导致协程泄漏

	// 关键问题点2:未释放锁
	mutex := concurrency.NewMutex(session, "/my-lock/")
	ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
	_ = mutex.Lock(ctx)
	
	// ...业务逻辑代码...
	
	// 关键问题点3:阻止程序退出(仅用于demo)
	select {} 
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 根因分析

# 架构示意图

+--------------+      定期续约      +------+
|  Go routine  |----------------->| etcd |
+--------------+  (KeepAlive)     +------+
       ▲
       │ 未调用Close()
       └──────+
              |
+---------------------------+
| session.Close() 核心作用:|
| 1. 停止续约协程           |
| 2. 释放租约              |
+---------------------------+

1
2
3
4
5
6
7
8
9
10
11
12

# 源码关键路径

协程泄漏路径: NewSession() → go keepAlive协程 → client.KeepAlive() → 后台协程续约(sendKeepAliveLoop)

资源释放路径: session.Close() → Orphan() → 关闭上下文 → 触发keepAlive协程退出

# 解决方案

在创建session后,确保调用Close()方法,及时释放资源。

func main() {
	// ...初始化代码不变...

	session, err := concurrency.NewSession(cli)
	if err != nil {
		log.Fatal(err)
	}
	defer session.Close() // 新增关键修复

	mutex := concurrency.NewMutex(session, "/my-lock/")
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel() // 确保上下文取消

	if err := mutex.Lock(ctx); err != nil {
		log.Fatal(err)
	}
	defer mutex.Unlock(context.TODO()) // 双保险释放锁

	// ...业务逻辑...
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 最佳实践

  1. 资源释放三原则:
  • 对每个NewSession()必须配对defer Close()
  • 锁操作必须包裹在Lock()/Unlock()中
  • 使用带超时的上下文(建议不超过5s)

# 总结

  1. etcd特性:会话型锁的设计需要客户端主动维护生命周期
  2. 调试技巧:使用go tool pprof观察goroutine增长趋势