1. 问题表现:
接收到告警,部分接口异常。
2. 问题追查:
首先,机器日志中忽然出现很多的 flow_lock Faild to create semaphore 的错误。
追查系统中的代码实现后,发现是 sem_get 函数引起,该函数用于根据一个 key 值来获取一个系统 System V 信号量的引用。
代码位置:
我单独在指定机器上执行上述语句,发现了 Warning 信息: No space lelf on device ,原本以为是磁盘或者内存的空间不足,实际检查后才发现是信号量被耗尽了。
信号量是一种有限资源,通过 Linux 命令,查看系统信号量的限制数目是 1024 个。
而 Web 机器实际已经使用掉的信号量数目已经达到 1024 个,也就是说,可用的信号量已经被用完了。原因,已经比较明显了,就是 信号量被申请使用之后,没有被释放 。
于是我们追查代码源头,发现确实如此,这里的 sem_release 是类似解锁的意思,并非释放这个信号量,真正释放的函数是 sem_remove :
sem_release 的官方文档说明:
至此,找到问题所在。这个问题潜伏了很久,系统经常很长时间,累计申请了信号量而没有释放,慢慢达到 1024 个,最终触发该问题。
3. 问题解决方式:
( 1 )使用 root 权限手动释放信号量,如果有多台机器,每一台都要执行。
for semid in `ipcs -s | cut -f2 -d""`; do ipcrm -s $semid; done
( 2 )重启 Apache 服务,之后,信号量的使用情况,恢复到正常的数目。
关键代码位置:
函数 ams_sem_remove 会执行 sem_remove 的释放操作。
4. 问题小结:
Sem_get 系列函数,通过信号量来达到类似 Linux 的互斥量( mutex )锁的效果,对一个内存共享对象加锁和解锁的方式,来控制并发请求数量(类似线程安全)。出问题的原因,是长期运行下,申请占用了很多个信号量,并且缺少 sem_remove 的释放操作。