本人为了学习租用了一台国外(日本linode)机房的服务器(vps),不要问我为何,就是喜欢折腾。node
服务器做为WebServer,系统为CentOS(linux),为了跑.net网站,使用了Mono的.net环境,和jexus网站容器。系统为CentOS 7 + Mono 3.8.0 + jexus 5.6linux
AWS有个1年免费的ECS,本着无事找事,而且有便宜白占不白占的原则,也去弄了一年AWS,万般折腾下,配了windows 2008 + Sql Server Express 2005 Adv用作测试数据库服务器,数据库实例名为SQLEXPRESS2005web
公司在武汉电信机房有托管服务器,系统为Windows 2008 + IIS + SqlServer 2008,数据库实例为默认实例MSSQLSERVERsql
上述简称 日本WebServer,新加坡SqlServer,武汉WebServer,武汉SqlServer数据库
前几日,新作了个MVC4测试项目,FTP传到 日本WebServer,数据库附加到 新加坡SqlServer,登录提交时出现异常信息“sqlserver server does not exist or connection refused.”,而后各类记录log,各类重传DLL重启jexus重启系统,故障信旧,查看微软的在线帮助也没有解答。windows
而后,把网站上传到公司武汉WebServer,登录,竟然正常,进入后各类与数据库相关的操做,均正常。服务器
这可奇了怪了,怒把数据库附加到武汉SqlServer,修改web.config中的链接字符串,武汉WebServer访问依旧正常,日本Webserver竟然也正常!网络
新加坡SqlServer链接字符串部分connectionString="Data Source=52.74.251.34\SqlExpress2005,14433;框架
武汉SqlServer链接字符串部分connectionString="Data Source=61.22.65.22,14433;ssh
以下图:
当时的第一思路,新加坡SqlServer或日本WebServer是否是没法互通,或者是否各自把对方的IP加入了黑名单,但通过各类排查,发现网络和防火墙均正常。
而后怒用wireshark对武汉和新加坡的sqlServer进行抓包,发现新加坡SqlServer的数据库端口竟然没有入站报文。
网络正常,但又没有数据库入站链接,这真是机关用尽。
最后在日本WebServer使用tcpdump进行抓包,down下来一看,忽然让人眼前一亮。以下图,服务器竟然把Data source的主机部分,识别成了域名,调用了DNS进行解析,但这地址固然是不存在的,这样也就解释了为啥没法链接的问题。
问题果真如此么,此时,对日本WebServer的Web.config中的数据库链接进行测试,原武汉SqlServer的data source部分由"61.22.65.22,11433"修改成"61.22.65.22\MSSQLSERVER,11433",果真也不能成功访问。
因为SqlClient程序集,被封装在System.Data.dll中,win和Mono的此dll文件内包含的内容不尽相同,因此用win版的System.Data.dll进行替换也无济于事。
从http://download.mono-project.com/sources/mono/mono-3.8.0.tar.bz2下载mono3.8.0源码,找到目录mcs/class/System.Data/System.Data.SqlClient/,打开其中SqlConnection.cs文件,通过一番定位,终于找到了其解析数据库链接的方法ParseDataSource(),代码以下
1 private bool ParseDataSource (string theDataSource, out int thePort, out string theServerName) 2 { 3 theServerName = string.Empty; 4 string theInstanceName = string.Empty; 5 6 if (theDataSource == null) 7 throw new ArgumentException("Format of initialization string does not conform to specifications"); 8 9 thePort = DEFAULT_PORT; // default TCP port for SQL Server 10 bool success = true; 11 12 int idx = 0; 13 if ((idx = theDataSource.IndexOf (',')) > -1) { 14 theServerName = theDataSource.Substring (0, idx); 15 string p = theDataSource.Substring (idx + 1); 16 thePort = Int32.Parse (p); 17 } else if ((idx = theDataSource.IndexOf ('\\')) > -1) { 18 theServerName = theDataSource.Substring (0, idx); 19 theInstanceName = theDataSource.Substring (idx + 1); 20 21 // do port discovery via UDP port 1434 22 port = DiscoverTcpPortViaSqlMonitor (theServerName, theInstanceName); 23 if (port == -1) 24 success = false; 25 } else 26 theServerName = theDataSource; 27 28 if (theServerName.Length == 0 || theServerName == "(local)" || theServerName == ".") 29 theServerName = "localhost"; 30 31 if ((idx = theServerName.IndexOf ("tcp:")) > -1) 32 theServerName = theServerName.Substring (idx + 4); 33 34 return success; 35 }
代码中绿色背景部分,是否有问题呢,固然是有问题的,哈哈~~~
好比,上面的链接字符串,用这个方法解析出来,theServerName=52.74.251.34\SqlExpress2005,固然不会正常运行。
知道了问题,就知道解决方法啦
修改代码以下
private bool ParseDataSource(string theDataSource, out int thePort, out string theServerName)
{
theServerName = string.Empty;
string theInstanceName = string.Empty;
if (theDataSource == null)
throw new ArgumentException("Format of initialization string does not conform to specifications");
thePort = DEFAULT_PORT; // default TCP port for SQL Server
bool success = true;
theServerName = theDataSource;
int idx = 0;
if ((idx = theServerName.IndexOf(',')) > -1)
{
string p = theServerName.Substring(idx + 1);
thePort = Int32.Parse(p);
theServerName = theServerName.Substring(0, idx);
}
if ((idx = theServerName.IndexOf('\\')) > -1)
{
theInstanceName = theDataSource.Substring(idx + 1);
theServerName = theServerName.Substring(0, idx);
if (thePort <= 0)
{
// do port discovery via UDP port 1434
port = DiscoverTcpPortViaSqlMonitor(theServerName, theInstanceName);
if (port == -1)
success = false;
}
}
if (theServerName.Length == 0 || theServerName == "(local)" || theServerName == ".")
theServerName = "localhost";
if ((idx = theServerName.IndexOf("tcp:")) > -1)
theServerName = theServerName.Substring(idx + 4);
return success;
}
到此,故障排查就到此了,这样就结束了么,固然没有
因为mono是在linux编译安装,因此这个改动,还得ssh进服务器进行修改并从新编译才能生效。
使用putty,登录日本WebServer,找到mono编译的源码目录(辛亏当时编译好后没有删除),依次进入mcs->class->System.Data->System.Data.SqlClient,vi SqlConnection.cs,修改掉上述代码,保存退出。
随便到mono主目录,分别执行make和make install,漫长的等待后,mono即编译成功。
重启jexus服务,/usr/jexus/jws restart,重启成功后,再次访问,新加坡SqlServer成功访问。
据此,这个BUG被修复。
在使用了成熟团队开发的框架时,项目相关部分出现异常,第一时间必定要相信他人的代码,由于不少时候,项目跑不起来,排查到最后可能都是本身犯了某低级错误。
在肯定排除掉本身的BUG后,就须要针对异常现象进行全方面的排查了,在这里若是有发现某框架的一些蛛丝马迹,就须要对框架代码进排查了,下源码,反编译,用各类手段看到框架的逻辑代码,有没有问题天然就一清二楚了。