接触ATS开发已经有几年了,开发过内核的模块,也从事过插件的开发.内存泄露问题一直是一个困扰大多数ATS开发者的头疼的问题,下面说说我自己的感受和思考.这里这关注ATS插件开发这个话题.源码的example和plugins目录分别给出了不同业务场景的插件实例,很多都对我们有很大地启发,但是其中也存在一些问题,特别是缓存泄露的问题,在example给出的示例插件中比较常见.
1.http头中的mime field处理的内存泄露风险
ATS插件主要是在每一个http transaction交互中的指定阶段添加HooK API,俗称"钩子".比如在下面的钩子中
为此我们为了业务逻辑的需要,免不了要处理http request header和http response header中的各种host,url,status code,mime field,比如获取,修改,增加,删除等操作,按照example中的源码, 对各种异常处理不很全面, 在生产环境中运行中会内存泄露的风险.
2.自己引入外部库时创建释放内存可能导致的内存泄露风险
我曾经在插件中想引入TSIOBuffer一类的内存池管理容器来避免直接调用TSmallac()和TSfree()来频繁释放内存,但是经过细心地调研代码,我发现TSIOBuffer提供的接口只供4096以内的小内存进行char*读出转换的处理,对一个大小为300K左右的html页面来说,使用TSIOBuffer一类的内存池管理并不恰当.我也研究了nginx的memory pool的涉及思想,发现内存池管理同样还是针对4096以内的小内存进行的.对大的内存,ATS和Nginx内部还是直接调用malloc和free来直接分配和释放的.
这样就存在一个风险, 这么大的内存,如果在异常情况下,没有释放,将会导致内存泄露的巨大风险,如何解决这个问题?
举例来说,我想对着指定规则的某几类的url的html进行正则匹配, 更改一下它内部的html代码,比如改个js的链接地址,我不可避免地需要经历如下步骤:
1.http头中的mime field处理的内存泄露风险
ATS插件主要是在每一个http transaction交互中的指定阶段添加HooK API,俗称"钩子".比如在下面的钩子中
为此我们为了业务逻辑的需要,免不了要处理http request header和http response header中的各种host,url,status code,mime field,比如获取,修改,增加,删除等操作,按照example中的源码, 对各种异常处理不很全面, 在生产环境中运行中会内存泄露的风险.
对这种风险的一个解决方法就是大量使用do{...}while(0),请看下面的两段代码的示例
2.自己引入外部库时创建释放内存可能导致的内存泄露风险
我曾经在插件中想引入TSIOBuffer一类的内存池管理容器来避免直接调用TSmallac()和TSfree()来频繁释放内存,但是经过细心地调研代码,我发现TSIOBuffer提供的接口只供4096以内的小内存进行char*读出转换的处理,对一个大小为300K左右的html页面来说,使用TSIOBuffer一类的内存池管理并不恰当.我也研究了nginx的memory pool的涉及思想,发现内存池管理同样还是针对4096以内的小内存进行的.对大的内存,ATS和Nginx内部还是直接调用malloc和free来直接分配和释放的.
这样就存在一个风险, 这么大的内存,如果在异常情况下,没有释放,将会导致内存泄露的巨大风险,如何解决这个问题?
举例来说,我想对着指定规则的某几类的url的html进行正则匹配, 更改一下它内部的html代码,比如改个js的链接地址,我不可避免地需要经历如下步骤:
从upstream获取html完整内容==>解压缩=>pcre正则查找并替换=>压缩=>更新Content-Length头域=>写入downstream
下面是插件内存调试图