撸代码这么久,从之前简单的脚本,到单体应用,到最后的微服务,我们的应用总会因为各种奇奇怪怪的原因罢工,有些错误显而易见,而有些错误也会让人一时摸不到头脑。究其原因,还是需要加强自己的修养,多多总结,就能做到防患于未然。
下面老高会总结一些平时遇到的问题,以及对应的解决思路和方法,同事也欢迎大家补充!
环境类问题
环境问题是一个比较宽泛的概念,如果把一个应用比作一个人,程序的运行就比作去工作,对应人类的办公室。
最常见的办公室就是你的开发机了,可以理解为应用在在家办公。
有钱的公司会给自己的所有应用租一栋大楼,然后把应用合理规划到大楼的各个办公室,而像老高这种码农,也就租的起一个路边摊了。。。
这二者的差别在哪儿呢?打个比方,如果大楼失火了,如果是大公司,有自己的灭火团队,可能作为员工的"应用",感知到的是个别办公室暂时无法使用,被分配到别的场地办公,等火灭了,就自动回迁。如果是老高的路边摊出了问题,那我的网站你都打不开了(不会自动恢复)。
系统
系统决定了你的应用的办公舒适度,他可以为应用提供各种支持,比如大仓库(大硬盘),24小时监控、保安巡逻(监控告警) 等等。
硬盘
如果服务突然出现了应用卡顿,首先应该排查应用使用的磁盘空间是否已经所剩无几。
原因有:
- 应用都会写本地日志,如果磁盘满的情况,可能会因为所用的日志库不够健壮,导致很多失败,从而影响应用的稳定
- mysql磁盘满了,数据写不进去了,更新操作被挂起,导致服务报错
- redis配置了持久化,当无写入空间时,redis就无法对外提供写服务了
- inode如果超过配额,会导致一种特殊情况,磁盘没有满但无法写入
解决方案:
df -h
查看磁盘使用df -i
查看inode使用情况- 关注系统中的大文件
# 寻找当前目录下大于1G的文件
find . -type f -size 1G
# 寻找当前目录下大于1G的文件并排序
find . -type f -size 1G -print0 | xargs -0 du -h | sort -nr
# 查看最大深度为2的文件夹大小
du -h --max-depth=2
- 为应用预留更大的空间,并且当磁盘空间使用率达到80%需要告警
- 如果你的应用跑在docker中,那么一定要为容器配置日志限制,否则该容器可能把磁盘用日志打满,另外建议给docker服务手动配置最大日志,方法请自行google。
logging:
driver: “json-file”
options:
max-size: “3m”
- 为你的应用选择合适的硬盘,如果你使用了云服务器,一般挂盘会让你选择普通型还是高性能型,这里的衡量标准是啥?如果你的请求中80% 都是读,那么可以使用普通盘,大于20%的业务请求都是在写入,请使用高性能。
内存
说到内存,老高第一个想到的就是OOM了。。。但是想把OOM讲清楚不是本文的目的。这里就像说一个点,在编程的时候,一定要尽量节省内存,Linux虽然是无私的(Memory Overcommit),你申请的内存的时候"满口答应",但真的等到系统内存不够用的时候,别忘了OMM Killer会来嘎嘎乱杀。
举个golang的demo
代码语言:javascript复制# snippet 1
response, err = ioutil.ReadAll(resp.Body)
# snippet 2
response := bytes.NewBuffer(make([]byte, 4096))
io.Copy(response, resp.Body)
不知道大家看出来代码1和2的区别没,如果resp.Body
投毒,返回会来一个10G的文件,那么代码1会让你的内存瞬间爆掉,而代码二会更加柔性(这种情况在下载文件的时候十分明显)。
内存对齐
平台
k8s/tke
- 调度超时
- 健康检查
- 快速启动
- 不要用supervisor
- 超卖问题
- prestop
- 内核文件
服务治理
编码类问题
配置错误
配置项本身有问题
老高的业务中不同的环境有不同配置文件,有时会出现某项配置在开发环境,但是测试环境中丢失的问题。
配置编码
业务中有时候会填写DSN(数据源名称),比如:
代码语言:javascript复制mysql:
dsn://user:password@tcp(localhost:3306)/your_db?charset=xxx
redis://password@127.0.0.1:6379/0
但是密码中可能会出现 =
、?
、:
等特殊字符,这些字符可能会导致DSN无法解析,比如dsn://user:localhost:3306@tcp(localhost:3306)/your_db?charset=xxx
这种,就会比较难解析。
此处就很容易发生错误。