@@ -69,41 +69,42 @@ Debug 调试技巧
69
69
- 类型错误。在动态语言和弱类型语言当中比较常见的一种错误(动态语言确实更容易出 bug),可以借助类型强转,type hint 工具。
70
70
- 资源没有关闭。打开的文件/IO流/连接等资源一定要关闭,防止资源泄露。go 的 defer 和 python 的 with 最好用上
71
71
- 深浅拷贝问题。不同语言可能又不同的拷贝模型,确定你的参数是深拷贝还是浅拷贝,能否修改,修改了之后是否有副作用。
72
- - 数组越界错误。注意涉及到数组的时候使用的下标是否会越界。越界了 python 抛出异常,go 直接 panic 掉。
72
+ - 数组越界错误。注意涉及到数组的时候使用的下标是否会越界。越界了 python 抛出异常,go 直接 panic 掉,并且 go 不支持负数下标
73
73
- 数值截断错误。注意强制类型转换是否会发生截断,损失精度,结果是否符合期望。
74
74
- 参数校验。一般来自用户的输入都要假设参数可能是错误甚至是恶意参数,后台必须要进行类型、大小、范围、长度、边界、空值等进行检查
75
75
- 参数单位是否匹配。比如 go 需要时间的参数 time.Duration 有没有乘以对应的 time.Second/MilliSecond 等。
76
76
- 路径错误。编写一些脚本需要处理文件的时候,推荐使用绝对路径比较不容易出错。
77
- - 空值错误。比如直接赋值一个 go 里边的 map 会 panic,你需要先给 map make 一个值,很多 go 新手会重复犯这个错(go slice 却可以直接声明之后 append)
77
+ - 空值错误。比如直接赋值一个 go 里边声明的 map 会 panic,你需要先给 map make 一个值,很多 go 新手会重复犯这个错(go slice 却可以直接声明之后 append)
78
78
- 零值和空值。有时候我们根据业务来区分零值(一个类型的初始化值)和空值 (None/nil等),注意处理上的细微区别。
79
79
- 闭包问题。循环里闭包引用的是最后一个循环变量的值,需要注意一下,很多语言都有类似问题,可以通过临时变量或者传参的方式避免
80
80
- 遍历修改列表问题。一边遍历,一边修改可能会使得迭代器失效而出错,最好不要遍历的时候修改列表。
81
81
- 遍历修改元素值问题。这一点 go 和 python 表现不同,go 比如你去循环一个 []Struct 是无法修改每个元素的,go 会拷贝每一个元素值,需要通过下标或者指针修改
82
- - 影子变量。很多语言同名的局部作用于变量会隐藏外部作用域变量 ,最好不要命名重复。
82
+ - 影子变量。很多语言同名的局部作用域变量会隐藏外部作用域变量 ,最好不要命名重复,否则可能不是期望结果
83
83
84
84
网络问题
85
85
~~~~~~~~~~~~~~~~~~~~~~
86
86
- 请求超时。网络请求的 client(http/rpc) 是否有设置超时,比如有些 go 的 client 需要显式自己传进去超时参数,否则可能导致 block
87
- - 连接池打满。连接池应该是服务共享的(单例),而不是每个请求都要去创建连接池导致打满连接池
87
+ - 连接池打满。连接池应该是服务共享的(单例),而不是每个请求都要去创建连接池导致打满连接池。
88
+ - 长短连接。注意有些需要长连接的场景,可以避免频繁建立 tcp 握手的开销。(http keepalive)
88
89
89
90
RPC/Web 框架
90
91
~~~~~~~~~~~~~~~~~~~~~~
91
92
- 请求参数限制。比如一般 rpc 请求会限制每次请求的最大的参数个数,如果一次性请求太多可能需要分批并发请求
93
+ - debug 模式。注意线上一定要关闭掉 debug 方式防止泄露关键信息
92
94
93
95
数据库问题
94
96
~~~~~~~~~~~~~~~~~~~~~~
95
- - 查询参数非法。查询数据库的时候可能因为一些不合理参数导致数据库慢查询,比如过量查询导致慢查询 。可以在入口处做一下限制。比如限制limit 大小
97
+ - 查询参数非法。查询数据库的时候可能因为一些不合理参数导致数据库慢查询,比如一次查询太多导致慢查询 。可以在入口处做一下限制。比如限制limit 大小
96
98
- 查询参数类型不匹配。注意如果传入类型不对,可能导致数据库没法利用索引导致慢查询,注意查询的参数类型和数据库类型匹配
97
99
- 连接池跳涨。除了不当使用连接池之外,如果是启动了大量的服务容器也可能有这个问题,注意限制单服务连接池的大小
98
- - 连接池过大。连接池数量设置太大效率反而可能降低,应该根据实际压测结果设置一个比较合理的值
100
+ - 连接池过大。连接池数量设置太大效率反而可能降低,应该根据实际压测结果设置一个比较合理的值,并非越大越好
99
101
- 主写从读。很多采用最终一致性模型,但是对于一些对时延敏感的场景要考虑是否会有主从延迟问题
100
102
101
103
并发问题
102
104
~~~~~~~~~~~~~~~~~~~~~~
103
105
- 线程安全。如果不是线程安全的操作(原子操作),应该通过加锁等方式做数据同步。比如 go 里边如果多个 goroutine 并发读写 map 程序会出错(lock/sync.Map)。利用好 race detector。
104
106
但是有些语言有 GIL 可以保证内部数据结构的一些原子操作,这个时候可以不用加锁,所以要区分不同编程语言决定。
105
- - 分布式锁。分布式服务对于需要数据同步的操作可以使用分布式锁
106
- - goroutine泄露。确保你的 goroutine 可以完成退出
107
+ - goroutine泄露。确保你的 goroutine 可以完成退出,防止大量未执行结束的 goroutine 堆积
107
108
108
109
依赖库问题
109
110
~~~~~~~~~~~~~~~~~~~~~~
@@ -113,8 +114,8 @@ RPC/Web 框架
113
114
日志错误
114
115
~~~~~~~~~~~~~~~~~~~~~~
115
116
- 日志级别错误。线上使用了 debug 级别,可能导致日志打满,如果没有滚动日志可能会导致服务器磁盘打满。一定要注意不同环境日志级别,推荐集中式日志收集系统
116
- - 日志参数错误。日志语句对应的占位符要和传参的个数一致。
117
- - 缺少足够信息 。如果是为了 debug 加上的日志一定要有足够的上下文信息帮助排查问题。
117
+ - 日志参数错误。日志语句对应的占位符要和传参的个数一致,类型要匹配,比如本来是数字的使用了 ` "%s" `
118
+ - 缺少必要信息 。如果是为了 debug 加上的日志一定要有足够的上下文信息帮助排查问题,同时也要注意不要泄露敏感数据(密码等)
118
119
119
120
错误/异常处理
120
121
~~~~~~~~~~~~~~~~~~~~~~
@@ -131,5 +132,11 @@ RPC/Web 框架
131
132
- 比对字符串。单元测试的时候注意比对的字符串可能因为多了空格的问题没法直接比对。注意可以去掉空格之后对比,笔者曾经因为不
132
133
同字符串就多了一个空格比对失败,被坑过
133
134
135
+ 分布式系统问题
136
+ ~~~~~~~~~~~~~~~~~~~~~~
137
+ - 分布式锁。分布式服务对于需要数据同步的操作可以使用分布式锁,注意分布式锁的超时问题
138
+ - 时钟漂移。如果代码强依赖时间戳在不同的服务器上可能因为时钟差距导致问题,可以采用适当取整对齐时钟。
134
139
140
+ 参考
141
+ --------------------------------------
135
142
* `50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs <http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in-go-golang/ >`_
0 commit comments