利用TIdTCPServer接受資料並存入資料庫中發生的問題 |
尚未結案
|
smallma
一般會員 發表:8 回覆:11 積分:3 註冊:2003-03-05 發送簡訊給我 |
小弟的程式是要利用TIdTCPServer處理client端傳來的資料(有可能多筆),並insert入資料庫,程式在測試的時候run的很順,但在實際上線的情況下程式只要run個一整天就會出現各種奇怪的錯訊息並掛掉了,我發現在工作管理員中,這個程式所佔用的記憶體會隨程式執行的時間以及它處理的需求,慢慢的增加,直到最後就掛了,我懷疑是TIdTCPServer的OnExecute的thread中,一定有部份的記憶體沒有在thread結束時釋放,而造成每次OnExecute都會造成程式佔用記憶體的增加,這是我推論有可能發生問題的點,到最後的結果就是程式掛掉,希望哪位大大能幫幫我解決這個問題,程式的片斷如下: procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var recstr,sqlstr: string;
A,B,C,D: string;
Query1: TADOQuery;
begin
with AThread.Connection do begin
//接受傳來的字串
recstr := ReadLn;
//由recstr取出第一筆資料,每個欄位以空格做間隔
A := fetch(recstr,' ');
B := fetch(recstr,' ');
C := fetch(recstr,' ');
D := fetch(recstr,' ');
//設定sql指令的第一筆資料
sqlstr := 'insert into ABCD(A,B,C,D) values ('
QuotedStr(A) ',' QuotedStr(B) ','
QuotedStr(C) ',' QuotedStr(D) ')'; //如果資料不止一筆,讀資料並加入sql到讀完為止
while length(recstr) > 0 then begin
A := fetch(recstr,' ');
B := fetch(recstr,' ');
C := fetch(recstr,' ');
D := fetch(recstr,' ');
sqlstr := sqlstr ',('
QuotedStr(A) ',' QuotedStr(B) ','
QuotedStr(C) ',' QuotedStr(D) ')';
end; //設定Query元件
Query1 := TADOQuery.Create(self);
Query1.Connection := ADOConnection1; Query1.SQL.Clear;
Query1.SQL.Add(sqlstr); //執行sql指令加入資料庫並free query
try
Query1.ExecSQL;
Query1.Free;
except
on e: exception do begin
Query1.Free;
end;
end;
end;
end; ps:使用的資料庫為mysql,以ado元件連結delphi 6
|
Chance36
版主 發表:31 回覆:1033 積分:792 註冊:2002-12-31 發送簡訊給我 |
smallma 你好 請改成如下程式
....... //執行sql指令加入資料庫並free query try Query1.ExecSQL; except on e: exception do begin ....... end; end; If Assigned(Query1) Then Query1.Free; end; 若還有問題,你可以試著把Try...Except的包含範圍拉大一點到 Query1 := TADOQuery.Create(self);之前如下 Try Query1 := TADOQuery.Create(self); .... .... .... Query1.ExecSQL; Except ...... End; If Assigned(Query1) Then Query1.Free; |
smallma
一般會員 發表:8 回覆:11 積分:3 註冊:2003-03-05 發送簡訊給我 |
|
ha0009
版主 發表:16 回覆:507 積分:639 註冊:2002-03-16 發送簡訊給我 |
|
smallma
一般會員 發表:8 回覆:11 積分:3 註冊:2003-03-05 發送簡訊給我 |
目前的測試結果: (1)在thread的最後加入
if Assigned(Query1) Then FreeandNil(Query1); 這個方法經過5個小時的測試,佔用記憶體的情況有小幅改善(增加的速度變小),可是依照以往的經驗,大約這支程式會在約72小時後掛掉。 (2)除了(1)的方法之外我參考了之前的文章,在程式中放一個timer,每十分鐘做一次以下的指令:
SetProcessWorkingSetSize(GetCurrentProcess,$FFFFFF,$FFFFFF);
Sleep(100); 這對於這個程式的問題有明顯的改善,雖然不能完全的達到在一段時間後佔用記憶體的狀況完全不增加,但是已經可以接受,雖然測試時間不夠,不過這支程式應該可以穩定工作120小時以上。 (3)除了加上(2)的timer之外,所有的thread全部使用同一個adoquery,先宣告一個全域變數QueryIsUsing為TCriticalSection,並將thread的onexecte中有關使用query的部份程式碼改寫如下: QueryIsUsing.Acquire; Query1.Close;
Query1.SQL.Clear;
Query1.SQL.Add('XXXXXXXX') <--執行的指令
Query1.ExecSQL; QueryIsUsing.Release; 這樣做的好處是完全沒有create、以及free adoquery的動作,完全就是一個adoquery用到底,理論上,應該不可能造成程式佔用記憶體上昇的問題,可是實際上在使用的時候,佔用記憶體的情況仍然發生,甚至效果還比不上(2) 以下是我的結論: 利用query在新增資料的時候,也許是無可避免的,一定會有一些記憶體的片斷沒有釋放,目前我測試效果最好的方式是使用SetProcessWorkingSetSize重新調整,不過有了它可以大幅度的延長程式執行的時間,不過我想這也不是一個100分的做法,不知道大家有什麼意見,希望能多多提出來討論~~~
|
smallma
一般會員 發表:8 回覆:11 積分:3 註冊:2003-03-05 發送簡訊給我 |
補充一下另一個新的想法: 如果佔用記憶體的情況不能100%避免,另一個可能的想法就是將這支程式設定為一段時間後(也許是72小時或更久),就會自動關閉,另外由一支附帶的程式來偵測,如果主要程式被關閉,附帶程式就自動開啟一個全新的主要程式,這是一個想法。 不過這個想法會有一個問題,就是這個有可能同時有很多thread在執行的程式,如果自己呼叫self.close;常常會由於有些thread還沒有執行完,會造成出現錯誤訊息(accessviolation),或是"這個程式已經沒有回應"的messagebox,這樣的結果不但是主要程式當掉,連那個附帶的程式偵測也會因為主程式沒有"完全結束",而沒有作用,不知道有沒有大大知道怎麼樣避免這個情況呢???
|
Chance36
版主 發表:31 回覆:1033 積分:792 註冊:2002-12-31 發送簡訊給我 |
smallma 你好 使用一個query固然是省記憶體,但要注意執行緒同步的問題,會不會某個連線正在使用該Query時同時另一連線也要用而造成競爭的現象。
我倒建議使用LIst來管理連線,當OnConnect時則將該AThread加入ThreadList,而在DisConnect時將該AThread移除,同時使用另一QueryList記錄已建立的AdoQuery,當連線建立時除了加入ThreadList的管理之外,同時檢QueryList.Count是否>=ThreadList.Count,若不是則動態建加Query並加入QueryList中加以管理,所以此時每個AThread就有一個專用的Query可用,如此才不會打架,至於某個連線斷線時,只要將該AThread從ThreadList中移除即可,QueryList還是保持不要釋放。
|
microbean
初階會員 發表:1 回覆:43 積分:38 註冊:2004-04-09 發送簡訊給我 |
對於 Memory Leak 的問題 , 很有興趣 ,
我曾遇過在 MySql 上面使用 MySql ODBC , 發生嚴重的 Memory Leak 問題,
所以對於這個問題, 提供一些想法來追蹤 首先, 我假設 smallma 的程式語法都正確 ,也有正確的下釋放的指令 .
這樣可能會發生的 memory leak 來源 , 推斷有三個來源
1. MySQL ODBC
2. TAdoQuery
3. Indy 這三個裡面 , 我先排除 TAdoQuery, 機率比較小 .
接著 MySQL ODBC 是否有 Memory leak 的 bug , 我曾找過資料
都沒有證實, 但是 原則上 , 建議可以先更新到最新的版本 ,
或者反向操作 , 先用 Access ODBC 取代 MySQL ODBC , 試試. Indy 是否有 Memory Leak 呢 ?
假設 TCPServer 是一個 multi thread 的元件 , 可以觀察
工作管理員的 執行緒數目 , 以及 記憶體的成長情形
原則上, 我們可以假設 一個 Connection 佔用 一個 thread
所以當 client 斷線時 , 這個 thread 以及佔用的記憶體
應該被釋放 . thread 的數目沒有因斷線而減少 ,
那表示 , indy 的 thread 控制出了問題.
如果是這樣 , 就要從 indy thread 如何正確釋放著手. 另外 , 可以用 debug 的 trace 方法 , 針對 ServerExecute 的
每一行逐行去執行 , 並且觀察記憶體以及 thread 數目的變化
,在我們預期應該釋放的地方 , 記憶體應該產生變化 , 應該減小. 以上 提供參考. _= 沒事就 winsock 一下 =_
|
smallma
一般會員 發表:8 回覆:11 積分:3 註冊:2003-03-05 發送簡訊給我 |
有關 microbean 提出的寶貴意見,我非常的感謝,我在這裡也針對他提出的三點提供一些我的經驗做為大家的參考: (1)MySql ODBC:這是最有可能,也是我到目前為止認為最大的嫌疑犯,不過要解決這個問題,也不知道從何下手 (2)TAdoQuery:我想應該不會是它,因為我在前面已經提過了,我利用TCriticalSection變數的Acquire和Relese來控制,讓所有的thread都只使用一個TAdoQuery,但是Memory Leak的情形完全沒有改善;順便提一下,我也試著使用過BDE的TQuery來做相同的實驗,結果是一樣的。 (3)Indy:這個也不可能,因為相同的程式我有另外一支也在跑,那一支負責的是查詢(select),而那支程式我可以連續跑二個星期以上,完全都不會有任何Memory Leak的情形,同樣的程式,同樣的語法,只是一個是查詢(query.open),一個是新增(query.execsql),就有如此大的差別,而這二個程式在使用indy方面的結構是完全相同的,所以可以排除indy的因素。 經由消去法(好像在辦案),(1)應該就是我們的犯人,可是要怎麼解決呢???
|
microbean
初階會員 發表:1 回覆:43 積分:38 註冊:2004-04-09 發送簡訊給我 |
smallma 兄
你可以在 access or MSSQL 上面測試看看嗎 ??
也就是原本走 MySQL 的 odbc driver 現在走
access or MSSQL ... 就是把 db 換一下看看.... 如果真的證實是 my SQL ODBC driver
再來想辦法 ..
我之前的情形 , 我是這樣解的
由於我的 mysql 是在 unix 上 , 所以我在 apache
上面寫了一個 php 的接口程式 , 然後 我的 indy
就單純的使用 idHttp 去送 query 給 php .
這樣就解了 , 但是...安全性以及效能是需要在考量的.. 發表人 - microbean 於 2004/04/22 14:21:15 發表人 - microbean 於 2004/04/22 14:22:03
|
smallma
一般會員 發表:8 回覆:11 積分:3 註冊:2003-03-05 發送簡訊給我 |
|
smallma
一般會員 發表:8 回覆:11 積分:3 註冊:2003-03-05 發送簡訊給我 |
謝謝大家的幫忙,我把這個問題最終發現的關鍵和大家報告一下: 很奇怪的,不知道什麼原因,只要在TIdTCPServer的OnExecute的thread中有執行query的insert的動作,在程式執行久了之後,我發現一個重要的關鍵,就是該程式的執行會愈來愈多,進而造成Memory Leak,所以抱著嘗試的心理試了一行指令,在OnExecute的最後加上一行: procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var ....
begin
with AThread.Connection do begin
.......
end;
AThread.Terminate;
end; 結果出奇的好,再將之前大家討論的部份加上去,幾乎可以說是一個完美的結局,只是不知道它的原理到底是怎麼樣,還希望如果有高手在看的話,能給我們一點指示,謝謝大家
|
microbean
初階會員 發表:1 回覆:43 積分:38 註冊:2004-04-09 發送簡訊給我 |
這樣看來 , 當 client 斷線時 , Server 可能未處理 disconnect .
或者有處理 , 但是裡面仍有 thread 處在 pending 的狀況 .
這可以用 debug mode , 去 trace client 斷線以後的所有程式
(不只有要看自己寫的) , 推斷一定有一個地方 , pending 了. 把 thread 直接 teminate 掉 , 當然上面的資源就會隨之解決.
更好奇的是說 , 如果不用 indy 呢 ?? 是否就可以了..
(因為我常常遇到 indy 莫名的問題 , 我懷疑這是 indy 架構有問題) _= 沒事就 winsock 一下 =_
|
smallma
一般會員 發表:8 回覆:11 積分:3 註冊:2003-03-05 發送簡訊給我 |
|
jeffreck
高階會員 發表:247 回覆:340 積分:197 註冊:2003-01-23 發送簡訊給我 |
|
Ktop_Robot
站務副站長 發表:0 回覆:3511 積分:0 註冊:2007-04-17 發送簡訊給我 |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |