一、问题描述

广告平台中的发报表邮件功能偶尔会出现发送失败的情况,重启phantom服务之后就好了

查看phantom服务的日志发现,在2017-12-12 03:29:45有访问记录,并且参数是异常的,queryJSON是%257B,经过url decode之后发现是:{

![](https://img2018.cnblogs.com/blog/296720/201901/296720-20190117170018751-1025403433.jpg)


并且之后再进行发邮件操作,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服务代码不健壮导致无法发送报表邮件的更多相关文章

  1. strcpy之代码的健壮性与可维护性

    strcpy   函数的原型是: char * strcpy(char * strDest,const char * strSrc);    功能:把从strSrc地址开始且含有NULL结束符的字符串 ...

  2. RabbitMQ服务主机名更改导致消息队列无法连接

    RabbitMQ服务主机名更改导致消息队列无法连接 在多节点环境中,RabbitMQ服务使用一个独立节点部署.在此环境下,如果修改了RabbitMQ节点的主机名,则需要更新RabbitMQ用户才能保证 ...

  3. SpringCloud发现服务代码(EurekaClient,DiscoveryClient)

    1.说明 本文介绍SpringCloud发现服务代码的开发, 通过使用EurekaClient,DiscoveryClient来发现注册中心的服务等, 从而可以自定义客户端对注册中心的高级用法. 2. ...

  4. Devops 开发运维高级篇之微服务代码上传和代码检查

    Devops 开发运维高级篇之微服务代码上传和代码检查 微服务持续集成(1)-项目代码上传到Gitlab 微服务持续集成(2)-从Gitlab拉取项目源码 微服务持续集成(3)-提交到SonarQub ...

  5. service层代码相互调用, 导致spring循环依赖,设计上的优化

    管理员创建用户需要发送激活邮件, 而发送激活邮件的时候需要判断发件人是不是合法的用户, 因此设计到一个循环依赖的问题 //UserService @Service class UserService{ ...

  6. java代码如何发送QQ邮件

    近来想写一个qq之间互相发送邮件的工具.奈何一直报错服务错误: org.apache.commons.mail.EmailException: Sending the email to the fol ...

  7. EXT3文件系统误删除导致文件系统中的邮件丢失恢复方法

    一.故障描述 由8块盘组成的RAID5, 上层是EXT3文件系统,由于误删除导致文件系统中的邮件丢失 二.镜像磁盘为防止数据恢复过程中由于误操作对原始磁盘造成二次破坏, 使用winhex软件为每块磁盘 ...

  8. 使用Zabbix服务端本地邮箱账号发送报警邮件及指定报警邮件操作记录

    邮件报警有两种情况:1)Zabbix服务端只是单纯的发送报警邮件到指定邮箱,发送报警邮件的这个邮箱账号是Zabbix服务端的本地邮箱账号(例如:root@localhost.localdomain), ...

  9. 使用Power BI Desktop 制作并发布到Power BI 服务,使用Power BI Mobile查询报表

    上节内容中,我们介绍了Power BI的基本概念,本节我们分享以下一个简单报表从使用Power BI Desktop制作,到发布到Power BI 服务,到从Power BI Mobile上查阅报表的 ...

随机推荐

  1. ui 自动化的测试用例从哪来

    从手工测试当中选取,尽量选择 1.简单且需要反复回归 2.稳定且不会经常变化 3.优先覆盖核心功能

  2. 4G DTU是什么?

    要从任何设备(个人计算机.平板电脑或智能手机)访问Internet,需要DTU或热点.大多数宽带和移动DTU在"4G"或第四代网络系统上运行.虽然互联网连接的许多基本原则与4G D ...

  3. socket编程:recvmsg 和 sendmsg 函数

    背景 复习 socket 编程的时候发现了以前没有留意到的 2个函数:recvmsg 和 sendmsg ref : Linux编程之recvmsg和sendmsg函数 知识 先来看看函数原型: #i ...

  4. SSM实现文件上传

    1.导入上传需要的jar包 commons-fileupload-1.3.3.jar commons-io-2.6.jar 2.创建 index.jsp <%@ page contentType ...

  5. STC转STM32第一次开发

    目录 前言 项目 1. 模数转换,并通过OLED屏显示出来 需求: 实验器材: 接线: 源程序: 成品: 2. 简易频率计(0.1-10MHZ) 需求: 原理: 实验器材: 接线: 源程序: 写在结尾 ...

  6. 使用 c++ 模板显示实例化解决模板函数声明与实现分离的问题

    问题背景 开始正文之前,做一些背景铺垫,方便读者了解我的工程需求.我的项目是一个客户端消息分发中心,在连接上消息后台后,后台会不定时的给我推送一些消息,我再将它们转发给本机的其它桌面产品去做显示.后台 ...

  7. Scrapy分布式爬虫,分布式队列和布隆过滤器,一分钟搞定?

    使用Scrapy开发一个分布式爬虫?你知道最快的方法是什么吗?一分钟真的能 开发好或者修改出 一个分布式爬虫吗? 话不多说,先让我们看看怎么实践,再详细聊聊细节~ 快速上手 Step 0: 首先安装 ...

  8. C++ 设计模式 4:行为型模式

    0 行为型模式 类或对象怎样交互以及怎样分配职责,这些设计模式特别关注对象之间的通信. 1 模板模式 模板模式(Template Pattern)定义:一个抽象类公开定义了执行它的方法的方式/模板.它 ...

  9. 利用Github Action和.Net 5 自动执行米游社原神每日签到福利

    GenshinDailyHelper 原神的签到福利是需要单独下载APP进行才可以领取,并且每天需要打卡,虽然奖励并不是很可观,但有一些摩拉,食材和可观的经验书累计起来还是挺有吸引力的.可能本身不怎么 ...

  10. tcp 拥塞控制引擎&状态机

    TCP核心:流量控制   拥塞控制 流量控制:滑动窗口来实现, 防止接收方能够处理过来 拥塞控制:防止过多的包被发送到网络中,避免出现网络负载过大 说一说 拥塞控制: 拥塞控制状态机的状态有五种,分别 ...