上周的系统迁移,进了一个坑,可谓真是坑,从问题的定位,到问题的解决,有很多值得借鉴、学习的,可以算是一次非常有价值的故障排查,
介绍一下背景,这是一套Tuxedo的服务,下图所示,服务A调用服务B,其中服务A的功能是执行一定的逻辑生成个文件,服务B的功能是将文件推送至客户FTP(Windows)服务器的指定路径中,
这次搬迁的目的,是因为客户的FTP服务器需要迁移升级,换了一台机器,但是IP不变,同时更新了FTP账号的密码,推送系统(服务A和B)未做任何变更,理论上,对推送端来说,是次很简单的配合操作。
凡事不能乱立flag,越简单的操作,可能蕴藏着你所不知的问题。当晚启动新机器,推送端系统改了配置,执行测试,发现文件推送出现了问题,从服务A的日志看,文件生成成功,异步调用服务B,未出现任何错误,但是服务B的日志,未找到这次文件推送的请求,换句话说,从现象上看,服务A生成的文件,在调用下层服务B的过程中消失了?
一开始,没找着头绪,以至于尝试了屡试不爽的重启大法,还是无效。
其实,从ULOG日志中,还是看得出一些端倪,他提示了服务B出现过server被kill,自动重启的现象,
这个476的错误,从说明了解,应该就是server被kill,导致重启,
476 WARN: Server groupid/serverid: client process processid: lost message Description A server died and the specified server (with group groupid and server identifier serverid) is recovering on its behalf. A message from the client with the specified processid has been lost. Additional information will be printed in subsequent messages Action No action required. See Also See Messages 477 and 478 below. 477 WARN: SERVICE=servicename MSG_ID=msgid REASON=server died Description A server died (see message 476) while processing service servicename. The client from which the message was sent is still active so a response message will be sent indicating the failure. Action No action required. 478 WARN: SERVICE=servicename MSG_ID=msgid REASON=server and client died Description A server died (see message 476) while processing service servicename. The client from which the message was sent is no longer active either so that a response message cannot be sent indicating the failure. Action No action required.
那么现在的问题就是什么原因导致服务B的server被kill进而重启?
通过看出现问题的二进制文件,发现卡在了一个叫做putfile的函数上,程序用的FTP底层库,有这段的逻辑,意思是在执行FTP的put文件指令时,会调用pasv函数,他会执行PASV指令,
代码语言:javascript复制int pasv(char *ip){
if(command("PASVrn") <= 0
...
}
int putfile(char *name, char *ip){
if(t > 0) close(t);
t = pasv(ip);
...
}
这时,问题有些豁然开朗了,究其原因,如果了解FTP的朋友,就可能猜到,他可能和FTP的传输模式有关。
FTP是一种基于TCP的应用层协议,他不支持UDP协议。FTP工作在一种特殊的服务机制上,他使用两个端口,一个“数据”端口和一个“命令”端口(也称为控制端口)。通常情况下,端口21用作命令端口,端口20用作数据端口。然而,数据端口有时候并不是在端口20上时。因此,FTP的传输模式,可以分为两种,主动模式,被动模式。
1. 主动模式
在主动模式的FTP中,客户端从一个随机的非系统端口(N>1023)连接到FTP服务器的命令端口端口21。然后,客户端开始监听端口N 1,并将FTP命令端口N 1告诉FTP服务器,“请把数据发送给我的N 1端口”。然后,服务器将从本地数据端口(端口20连接回客户端的数据端口,也就是N 1端口。
因为服务器防火墙的隔离作用,我们应该确保服务器FTP到客户端的一下几个通道的畅通:
FTP服务器端口21(接受全部客户端)
FTP服务器端口21到>1023的端口(服务器响应客户端控制端口)
FTP服务器端口20到>1023的端口(服务器发起到客户端的数据端口的连接)从>1023的端口到FTP服务器端口20(客户端发送ack到服务器的数据端口)
用图来表示这些通道:
第1步,客户端的命令端口与服务器的命令端口连接并发送命令端口1027。然后,服务器在第2步时将一个ACK发送回客户端的命令端口。第3步,服务器在其本地数据端口上启动连接,连接到前面指定的客户端的数据端口。最后,客户端返回ACK,如第4步所示。
主动模式的FTP主要问题实际上落在客户端。FTP的客户端并不会主动连接到服务器的数据端口,而是是告诉服务器他正在监听哪个端口,然后服务器发起连接到客户端上指定的端口。但是,这样的连接有时候会被客户端的防火墙阻止。
2. 被动模式
为了解决服务器主动发起到客户端连接会被阻止的问题,另一种更完善的工作模式出现了,他就是FTP的被动模式,缩写作PASV,他工作的前提是客户端明确告知FTP服务器他使用被动模式。
在被动模式的FTP中,客户端启动到服务器的两个连接,解决了防火墙阻止从服务器到客户端的传入数据端口连接的问题。FTP连接建立后,客户端在本地打开两个随机的非系统端口N和N 1(N>1023)。第一个端口连接服务器上的21端口,但是客户端这次将会发出PASV命令,也就是不允许服务器连接回其数据端口。这样,服务器随后会打开一个随机的非系统端口P(P>1023),并将P发送给客户端作为PASV命令的响应。然后客户端启动从端口N 1到端口P的连接来传输数据。
在被动模式中,要保持一下通道的畅通:
FTP服务器的21端口(接受所有客户端)
FTP服务器的21端口到>1023的远程端口(服务器响应客户端控制端口)
FTP服务器>1023的端口(接受所有客户端发起的连接到服务器指定的随机端口)
FTP服务器>1023的端口到>1023的远程端口(服务器发送ack和数据到客户端数据端口)
被动模式用图表示:
第1步,客户端在命令端口上与服务器连接,并发出PASV命令。然后,服务器在第2步时使用端口2024进行响应,告诉客户端他正在监听的数据连接端口。第3步,客户端启动从其数据端口到指定服务器数据端口的数据连接。最后,服务器在第4步将ACK发送回客户端的数据端口。
服务器防火墙需要给FTP的被动模式开放一个端口范围允许所有客户端连接,比如5000 - 6000。
因此,从现象以及代码,服务B的卡顿,确实可能和被动模式有关,通过服务B进行FTP传输,首先设置了PASV,然后hang住,说明可能当前的环境,不支持被动模式。另一个角度,通过命令行验证,在客户端服务器,执行ftp命令,不输入任何参数的情况下,put文件正常,但是当执行了pasv,此时命令行hang了。
因为服务端的FTP是通过Windows自带FTP功能搭建的,并不是通过一些常见的FTP工具做的,Windows服务器的设置,自己不是很熟,网上搜了下,有的说是在服务器管理器-FTP防火墙支持中,设置数据通道端口范围,就可以配置被动的模式,
但是,客户主机工程师通过如下配置,设置被动模式,不觉明历,
1. sc sidtype ftpsvc unrestricted(将ftp服务的注册卸载) 2.net stop ftpsvc & net start ftpsvc(重启ftp服务) 3.netsh advfirewall firewall add rule name="FTP for IIS7" service=ftpsvc action=allow protocol=TCP div=in(开启所有ftp端口监听) 4.netsh advfirewall set global Statefulftp disable(使防火墙不拦截所有ftp服务的访问)
执行测试,此时服务A和B,能配合推送FTP文件,这才算是从坑中跳出来。
对开放系统来说,一个问题的解决,往往蕴涵着很多关联的知识,一方面能找到问题的突破口(通过日志、pstack、gdb等指令定位问题),另一方面能由点及面的武装自己的知识库(FTP的传输模式有何区别、如何设置不同的传输模式),这些可能都是需要在日常工作和学习中积累和沉淀的。
其实,对个人来说,碰到问题是好事,但前提是能积累这些问题,无论是谁的经验,能融会贯通,转成自己的知识,同一个坑,不要进入两次,就很有价值了。