本人爲了學習租用了一臺國外(日本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後,就須要針對異常現象進行全方面的排查了,在這裏若是有發現某框架的一些蛛絲馬跡,就須要對框架代碼進排查了,下源碼,反編譯,用各類手段看到框架的邏輯代碼,有沒有問題天然就一清二楚了。