背景
线上服务采用的是Django + Gunicorn+K8s组成的服务。
最近查看日志的时候,发现在发版时,有部分请求会在Pod开始提供服务的时候超时(超过三秒)
但线下测试时又没有超过三秒的问题。
怀疑过LB、K8s Nodeport,但都被一一排除。最终发现是Gunicorn Lazy-App导致的问题。
什么是Lazy-App
其实很简单,就是只有在第一个请求到达时,Worker才会对WSGI App进行初始化。
因为Django框架很复杂,耗时也会相对多一些
为什么Lazy-App方式会导致超时
首先需要明确个条件:高并发下
从Lazy-App的原理我们可以知道,第一个请求的处理时长一定是较长的。
在高并发下,分发到同一个Worker的请求会有阻塞级联的现象(使用协程/Sync模式,特别是第一次访问App还没有加载),即一批请求中最长的处理时间是之前所有请求处理时间之和(如果代码中没有使用异步库,Django ORM没有打异步Patch)
加之高并发下大量请求同时到达不同的Worker,Worker对系统资源的抢占更加剧了阻塞。
解决方法
其实很简单,在提供服务之前进行预热即可解决问题。
或者使用Preload参数,让App加载完成后,Workers再进行Fork操作。但这个做法会导致某些代码因跨进程出现问题(如python-kafka),所以更通用的方法还是进行预热。
预热也很简单,写个Py脚,按照并发数 = Worker + 1,总请求 = 并发数 * 5即可完成充分预热。