[BUGCASE]Phantom服务代码不健壮导致无法发送报表邮件
一、问题描述
广告平台中的发报表邮件功能偶尔会出现发送失败的情况,重启phantom服务之后就好了
查看phantom服务的日志发现,在2017-12-12 03:29:45
有访问记录,并且参数是异常的,queryJSON是%257B
,经过url decode之后发现是:{

并且之后再进行发邮件操作,phantom不再打日志
不想看分析过程的话,可直接看结论
或解决方案
二、原因分析
1.导火线
日志中的url是后台传给phantom服务的,为什么会传一个异常参数的url过来?
很明显不是人为点击发邮件触发的,而且时间发生在凌晨,会不会是安平扫描
?
经过和后台同事确认,确实如此!
原来后台没有做session校验,所以被扫描了,导致了这个问题
2.一些猜测
可是这似乎不是根本原因
为什么安平扫描会导致phantom服务停止打日志?
以下是一些猜测:
(1)phantom服务崩溃
一开始猜测是phantom服务接收到异常参数,内部报错,导致服务崩溃
这个想法立即被导师否决了,因为phantom服务的进程还在,并没有崩溃
(2)运维自动重启服务的锅
因为phantom服务接了运维这边的自动重启服务,如果进程挂了会自动拉起
会不会进程拉起的过程有问题,初始的phantom进程不再工作,又拉起了一个新进程,存在两个进程,导致无法发邮件?
(3)进程僵死
会不会是进程僵死了,导致进程虽然还在,实际上却不工作?
(4)进程打开的文件句柄数超出限制
会不会是进程打开的句柄数超过了限制?
由于当时急着把服务重启了,没法获取出错时的上下文环境信息
为了验证这些猜测,需要重现下这个问题,怎么重现呢?
似乎没法真正地模拟安平的扫描过程
当时有同事提供了一个解决方案:
写一个脚本定时给phantom服务发包,然后看phantom服务有没有回包,没有回包说明phantom已经停止工作了,这时把phantom的进程杀死。运维的自动拉起服务这时会拉起phantom服务,如此便可解决这个问题
听起来很有道理,怎么实施呢?
打算先试着找到根本原因吧,实在没法重现这个问题,找不到根本原因再考虑发包的方案
3.分析代码
于是试着从phantom的代码入手
phantom服务拿到后台传过来的url之后,会做一下事情:
- 1.取url中的queryString
- 2.将queryString转化成JSON格式,并取其中的queryJSON参数的值
- 3.将queryJSON的值进行url解码(decodeURIComponent)
- 4.将解码后的queryJSON转化成JSON格式(JSON.parse)JSON.parse
- 5.取其中的platform参数的值
- 6.通过判断platform拼接出不同的targetURL
- 7.用phantom这个headless browser打开这个targetURL,并取其中的HTML内容
- 8.将这个内容传给后台服务
用安平扫描的异常参数模拟一遍上面的流程,发现第4步报错,JSON.parse方法没法解析{
左大括号
这个报错会导致程序不再往下执行,但并不会导致phantom服务不可用,下一次传入正确的参数依然可以发邮件
安平扫描有可能会连续扫描多次,会不会是这个原因呢?
4.问题重现
于是打算写一个脚本给phantom服务发多次带错误参数的请求:
for ((i=1;i<=100;i++));
do curl -i "http://ip:port/generateAdsReport?sessionid=&queryJSON=%7B";
done
ip:port是phantom服务的套接字
果然重现了!
5.查看报错环境
这时就可以看上下文环境信息
(1)查看进程情况:
ps -ef | grep phantom
并没有出现之前猜测的两个进程的情况
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
也没有变成僵尸进程
(2)查看文件句柄数
查看系统默认最大句柄数(单个进程最多允许打开的最大句柄数,默认是1024)
ulimit -n
查看总句柄数
lsof|awk '{print $2}'|wc -l
查看当前进程占用的句柄数
lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr|more
根据ID产看进程名
ps aef|grep 24204
发现并没有哪个进程超过句柄限制
(3)查看TCP连接数
netstat -nat | grep 10021
问题慢慢浮出水面!!!
6.根本原因
有很多状态为CLOSE_WAIT
的TCP连接
CLOSE_WAIT
表示等待从本地用户发来的连接中断请求
也就是说有很多TCP连接没有关闭,对照之前的phantom代码,出现报错之后,确实没有在执行任何关闭连接的代码
所以根本原因还是:自己写的代码不健壮,没有捕捉异常,报错了没有及时关闭TCP连接
一句话总结问题的原因:
由于安平的扫描,phantom服务内部将字符串转成JSON时报错,不往下执行后面的代码,也就不会关闭已经建立的TCP连接。未关闭的TCP连接积累到一定的量,超出系统的最大限制,导致phantom服务不再接收和处理之后的请求,最终导致无法发送报表邮件。
三、解决方案
知道问题的根本原因,要解决就容易多了,使用try/catch语句将可能出错的代码包裹起来,如果报错就关闭TCP连接。
try {
...
var jsonParams = JSON.parse(decodedParams);//可能报错的语句
var platform = jsonParams.platform;
}catch(err) {
response.close();//关闭TCP连接
}
------------------------------------------------20171219 更新------------------------------------------------
为了弄清楚TCP连接数的限制,在开发环境又测试了下
状态为CLOSE_WAIT
的TCP连接大概到了25个时,phantom服务就不再工作
此时phantom占用的文件句柄数是61个
实际上,phantom的最大并发连接数是10个,参考phantom官网
以下是来自phantom官网的截图:
以下是在开发环境上测试出来的TCP连接的边界值
之前的一句话原因总结改为:
结论
由于安平的扫描,phantom服务内部将字符串转成JSON时报错,不往下执行后面的代码,也就不会关闭已经建立的TCP连接。未关闭的TCP连接积累到一定的量,超出
phantom的最大并发连接数10个
(之前写的是系统的最大限制
),导致phantom服务不再接收和处理之后的请求,最终导致无法发送报表邮件。
[BUGCASE]Phantom服务代码不健壮导致无法发送报表邮件的更多相关文章
- strcpy之代码的健壮性与可维护性
strcpy 函数的原型是: char * strcpy(char * strDest,const char * strSrc); 功能:把从strSrc地址开始且含有NULL结束符的字符串 ...
- RabbitMQ服务主机名更改导致消息队列无法连接
RabbitMQ服务主机名更改导致消息队列无法连接 在多节点环境中,RabbitMQ服务使用一个独立节点部署.在此环境下,如果修改了RabbitMQ节点的主机名,则需要更新RabbitMQ用户才能保证 ...
- SpringCloud发现服务代码(EurekaClient,DiscoveryClient)
1.说明 本文介绍SpringCloud发现服务代码的开发, 通过使用EurekaClient,DiscoveryClient来发现注册中心的服务等, 从而可以自定义客户端对注册中心的高级用法. 2. ...
- Devops 开发运维高级篇之微服务代码上传和代码检查
Devops 开发运维高级篇之微服务代码上传和代码检查 微服务持续集成(1)-项目代码上传到Gitlab 微服务持续集成(2)-从Gitlab拉取项目源码 微服务持续集成(3)-提交到SonarQub ...
- service层代码相互调用, 导致spring循环依赖,设计上的优化
管理员创建用户需要发送激活邮件, 而发送激活邮件的时候需要判断发件人是不是合法的用户, 因此设计到一个循环依赖的问题 //UserService @Service class UserService{ ...
- java代码如何发送QQ邮件
近来想写一个qq之间互相发送邮件的工具.奈何一直报错服务错误: org.apache.commons.mail.EmailException: Sending the email to the fol ...
- EXT3文件系统误删除导致文件系统中的邮件丢失恢复方法
一.故障描述 由8块盘组成的RAID5, 上层是EXT3文件系统,由于误删除导致文件系统中的邮件丢失 二.镜像磁盘为防止数据恢复过程中由于误操作对原始磁盘造成二次破坏, 使用winhex软件为每块磁盘 ...
- 使用Zabbix服务端本地邮箱账号发送报警邮件及指定报警邮件操作记录
邮件报警有两种情况:1)Zabbix服务端只是单纯的发送报警邮件到指定邮箱,发送报警邮件的这个邮箱账号是Zabbix服务端的本地邮箱账号(例如:root@localhost.localdomain), ...
- 使用Power BI Desktop 制作并发布到Power BI 服务,使用Power BI Mobile查询报表
上节内容中,我们介绍了Power BI的基本概念,本节我们分享以下一个简单报表从使用Power BI Desktop制作,到发布到Power BI 服务,到从Power BI Mobile上查阅报表的 ...
随机推荐
- mysql 事务的日志
事务的日志 1.redo log redo:"重做",记录的是,内存数据页的变化过程 1)作用 在事务ACID过程中,实现的是 "D" 持久化的作用. 2)工作 ...
- 《Head First 设计模式》:迭代器模式
正文 一.定义 迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示. 要点: 迭代器模式把在元素之间游走的责任交给迭代器,而不是聚合对象.这样简化了聚合的接口和实现,也让责 ...
- NB-IOT基站的优势和特点
NB-IOT基站是什么 NB-IOT基站的主要目的是完成移动通信网和UE之间的通信和管理功能,在移动通信中是组成蜂窝小区最基本的单元.只有在基站信号的覆盖范围之内通过运营商网络连接的NB ...
- [Luogu P1829] [国家集训队]Crash的数字表格 / JZPTAB (莫比乌斯反演)
题面 传送门:洛咕 Solution 调到自闭,我好菜啊 为了方便讨论,以下式子\(m>=n\) 为了方便书写,以下式子中的除号均为向下取整 我们来颓柿子吧qwq 显然,题目让我们求: \(\l ...
- Linux的进程、线程、文件描述符是什么
说到进程,恐怕面试中最常见的问题就是线程和进程的关系了,那么先说一下答案:在 Linux 系统中,进程和线程几乎没有区别. Linux 中的进程就是一个数据结构,看明白就可以理解文件描述符.重定向.管 ...
- 三十二张图告诉你,Jenkins构建Spring Boot 有多简单~
持续原创输出,点击上方蓝字关注我 目录 前言 如何安装Jenkins? 环境准备 开始安装Jenkins 初始化配置 访问首页 输入管理员密码 安装插件 创建管理员 实例配置 配置完成 构建Sprin ...
- 在pgsql库用触发器自动触发PostgreSQL的存储过程,实现插入。
需求:在对表A 执行 insert操作时,筛选符合条件的数据 insert到表B中,编写为存储过程(postgreSQL数据库) [筛选条件]:1. dd !="A" 或是 dd为 ...
- Python优点与缺点
优点 简单 -- Python 是一种代表简单主义思想的语言.阅读一个良好的 Python 程序就感觉像是在读英语一样,尽管这个英语的要求非常严格!Python 的这种伪代码本质是它最大的优点之一.它 ...
- MySql中指定符号分割并分行展示
1.涉及到的函数三个: 1.1 REPLACE('value','str1','str2') 用法规则:使用str2替换掉value中的所有的str1; SELECT REPLACE('我来了','来 ...
- .NET操作Excel
一.读取Excel数据,并显示 1.配置文件 <configuration> <system.web> <compilation debug="true&quo ...