![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Проблемы с потоками при закачке страниц
Pcrepair © (2012-12-27 11:20:00)Добрый день.
Есть функция вида function LoadPage(const Url:string):string; для закачки страниц на базе idHHP.Get, которая обеспечивает:
- закачку страниц с фреймами
- закачку страниц по HTTPs
- смену Request.UserAgent (доп. функция) по Random из списка агентов
- подавление исключений в коде idHHP.Get с записью в лог причин
- вывод сообщений о утечке памяти при закрытии Арр
Сама по себе функция работает нормально, загружает все страницы и без утечки памяти.
Есть код запуска этой функции в многопоточном режиме:
unit Main;
....
implementation
{$R *.dfm}
uses uLoadPage,uGlobalVar;
(*нажимает эту кнопку и понеслось*)
procedure TMainForm.Btn_StartLoadURLsClick(Sender: TObject);
var
UrlList:TStringList; (*эквивалент передаваемого массива строк из ПМ*)
i:integer;
Url:string; (*строка = УРЛ из массива*)
begin
UrlList:=TStringList.Create;
UrlList.Text:=Mm_InsSMUrls.Text; (*ввод массива строк УРЛ*)
(*----ЦИКЛ мульти закачки страниц по числу УРЛ в Mm_InsSMUrls---*)
for i := 0 to UrlList.Count-1 do
begin
Url:=UrlList[i]; (*передача УРЛ*)
Loader(Url); (*вызов процедуры запуска потока закачки страницы*)
Sleep(10); (*страховка от перегрева процессора а также от толпы потоков, которые мешают друг другу?*)
end;
FreeAndNil(UrlList);
end;
unit uLoadPage;
interface
uses SysUtils, uThreadLoader, Classes;
Procedure Loader(Url:string);
implementation
Procedure Loader(Url:string);
var ThreadLdr: TLoader;
begin
ThreadLdr:=Tloader.Create(True);
ThreadLdr.Priority:=tpNormal;
ThreadLdr.FreeOnTerminate:=True;
ThreadLdr.Url:=Url;
ThreadLdr.Start;
end;
end.
unit uThreadLoader;
interface
uses
System.Classes, idHTTP, SysUtils, Dialogs;
type
TLoader = class(TThread)
private
FUrl:string;
FHtml:string;
{ Private declarations }
protected
procedure Execute; override;
procedure CallStore;
function LoadHtmlCode(const Url:string):string;
public
property Url: string read FUrl write FUrl; (*свойство класа, доступное для всей программы*)
property Html: string read FHtml write FHtml;
end;
implementation
uses uPageLoader, uLoadPage, uGlobalVar;
function TLoader.LoadHtmlCode(const Url:string):string;
begin
Result:=LoadPage(Url); (*вызываем функцию загрузки страницы из uPageLoader*)
end;
procedure TLoader.CallStore;
begin
gStore.Add(Html); (*ввод новой строки в ГлобПерем*)
end;
procedure TLoader.Execute;
begin
Html:=LoadHtmlCode(Url); (*загрузка страницы в Поле Класса(свойство)*)
CS.Enter; (*вход в КС*)
CallStore; (*передаем результат в ГП*)
Cs.Leave; (*выход из КС*)
end;
end.
unit uPageLoader;
interface
uses Classes, SysUtils, idHTTP, StrUtils, IdSSL, IdSSLOpenSSL;
function LoadPage(const Url:string):string; (*Основная функция Юнита*)
procedure ErrorLog(Url:string);
function LoadHtmlPage(const PageUrl:string):string;
function DelJS(const Code:string):string;
function RestoreFrame(const Code:string):string;
function RestoreFullUrl(const Code:string; const RootUrl:string):string;
function GetRandomUserAgent: string;
var
Loader:TidHTTP;
SSL:TIdSSLIOHandlerSocketOpenSSL;
implementation
тут много чего, но все работает, смотри в начало
end.
unit uGlobalVar;
interface
uses Classes, SysUtils, SyncObjs;
var
gStore: TStringList=nil; (*ГП для хранения закачанных страниц*)
CS:TCriticalSection; (*используем КС для передачи результата в основной поток*)
implementation
initialization
gStore:=TStringList.Create;
gStore.Duplicates:=dupAccept;
gStore.Sorted:=False;
Cs:=TCriticalSection.Create;
finalization
FreeAndNil(gStore);
FreeAndNil(CS);
end.
Результат:
В режиме отладки видно что возникает множество самых различных ошибок:
- First chance exception at $00407466. Exception class $C0000005 with message 'access violation at 0x00407466: read of address 0x000000a4'. Process Loader_VER06.exe (2012)
- First chance exception at $7C812AFB. Exception class EIdHTTPProtocolException with message 'Set-Cookie: dle_password=deleted; expires=Tue, 27-Dec-2011 20:37:50 GMT; path=/; domain=.freeseller.ru; httponly'. Process Loader_VER06.exe (2012)
First chance exception at $00629F8C. Exception class $C0000005 with message 'access violation at 0x00629f8c: read of address 0x00000148'. Process Loader_VER06.exe (2012)
- First chance exception at $7C812AFB. Exception class EOSError with message
'System Error. Code: 123. Синтаксическая ошибка в имени файла, имени папки или метке тома'.
Process Loader_VER06.exe (2012)
- First chance exception at $7C812AFB. Exception class EIdNotASocket with message
'Socket Error # 10038
Socket operation on non-socket.'.
Process Loader_VER06.exe (2012)
В рабочем режиме, за счет подавления исключений, все это конечно не вылазит, но:
- закачиваются не все страницы(хотя все УРЛ валидные и функцией закачки закачиваются без вопросов)
- после закрытия Арр выводится сообщение о многочисленных маленьких утечках памяти
По теории потоки не должны пересекаться. каждый имеет свою память. вывод результата через КС. такое впечетление что потоки как то мешают друг другу
Внимание!!! Вопрос: в чем может быть причина появления исключений и утечки памяти именно в режиме многопотоковой закачки?
Pcrepair (2012-12-28 19:22:00) [61]а если вот так:
type
TLoader = class(TThread) (*создаем свой класс на базе ПОТОК*)
private
FUrl:string; (*поле класса типа СТРОКА для чтения -записи данных*)
FIdHttp:TidHTTP;
FSSL:TIdSSLIOHandlerSocketOpenSSL;
protected
procedure Execute; override;
function LoadHtmlCode(const Url: String):string; (*ФУНК загрузки страницы*)
public
property Url: string read FUrl write FUrl; (*свойство класа, доступное для всей программы*)
constructor Create;
destructor Destroy; override;
end;
implementation
uses Unit1, uLoadPage, uGlobalVar;
constructor TLoader.Create;
begin
inherited Create(True); (*Поток создаем в состоянии «Приостановлен»*)
FIdHttp := TIdHTTP.Create(nil);
FSSL := TIdSSLIOHandlerSocketOpenSSL.Create;
FreeOnTerminate := True; (* Поток освободит ресурсы при окончании работы*)
Self.Priority := tpNormal;
end;
destructor TLoader.Destroy;
begin
FreeAndNil(FIdHttp);
FreeAndNil(FSSl);
end;
(* 28.12.2012 функция загрузки страницы прямо в коде потока*)
function TLoader.LoadHtmlCode(const Url: String):string;
begin
FIdHttp.IOHandler:=FSSL;
FIdHttp.HandleRedirects:=True;
Result:=FIdHttp.Get(Url);
end;
procedure TLoader.Execute;
var Code:string;
begin
Code:=LoadHtmlCode(Url);
begin
CS.Enter; (*вход в КС*)
try
gStore.Add(Code); (*передаем результат в ГП*)
finally
Cs.Leave; (*выход из КС*)
end;
end;
end;
end.
то есть функция загрузки с idHTTP прямо в потоке. потом тут же дописать обертку (небольшой цикл по закачке фреймов) а функции обработки кода страницы вообще вызывать в поток из другого модуля
или еще лучще тут их написать но не как метод потока а сверху так сказать. или все же лучше сделать все функции методами класса??
Pcrepair (2012-12-28 16:30:00) [62]ну раз ты предпочитаешь ICS, тогда поведай миру о своем опыте, конкретно, есть ли в ICS:
- встроенная обработка перенаправления 301 и 302
- правильная кодировка кирилицы(так называемые кракрзябры вместо правльных букаф)
- возможность закачки HTTPs
- возможность установки нужного значения юзер-агент
- управление временем подключения к серверу и работы с сервером. ограничение в смысле
как видишь, если у тебя есть опыт работы, сказать много что вполне возможно
можно и примерный код выставить, или хотя бы дать ссылки
ждемс
Германн (2012-12-28 01:44:00) [63]
> Pcrepair © (28.12.12 00:05) [36]
>
> а может попробовать в качестве загрузчика ICS
Попытка - не пытка. Попробуй.
Лично я предпочитаю всегда ICS. Но я не занимался разработкой "поисковой машины". Так что мне больше нечего сказать.
bems (2012-12-28 00:11:00) [64]
> некоторые утверждают что SSL:TIdSSLIOHandlerSocketOpenSSL;
> тоже получается как и idHTTP общее для всех потоков. и
> при отладке выскакивают исключения, которые указывают на
> модуль IdSSLOpenSSL
общий, но ты ж добавил в конструктор треда FSSL := TIdSSLIOHandlerSocketOpenSSL.Create;
вот остальные ssl убирай, и присваивай FidHttp.IoHandler еще перед вызовом LoadPage
ну и в uPageLoader передавай IdHttp везде через параметр
Pcrepair (2012-12-28 00:05:00) [65]а может попробовать в качестве загрузчика ICS
Pcrepair (2012-12-28 00:04:00) [66]
> ssl на момент вызова уже по идее должен быть присвоен в
> iohandler, поэтому ssl вообще можно убрать
некоторые утверждают что SSL:TIdSSLIOHandlerSocketOpenSSL; тоже получается как и idHTTP общее для всех потоков. и при отладке выскакивают исключения, которые указывают на модуль IdSSLOpenSSL(смотри[8])
в общем прийдется модуль переписать, под многопоточность
bems (2012-12-27 23:49:00) [67]передавай через параметр как и в LoadPage.
ssl на момент вызова уже по идее должен быть присвоен в iohandler, поэтому ssl вообще можно убрать
Сергей М. (2012-12-27 23:43:00) [68]
> чтоб провыдЫр не за так деньги получал
О как сурово)
Pcrepair (2012-12-27 23:36:00) [69]
> в варианте без общего использования одного idHttp ошибки
> остаются?
этот вариант еще не попробовал и вот почему: на самом деле функция в 19
+++++++++++++++другой модуль++++++++++++++++++++
function LoadPage(const Url: String; IdHttp: TIdHttp): String;
begin
Result := AnsiLowerCase(IdHttp.Get(Url))
end;
IdHttp не содержит. LoadPage состоит из нескольких функций
unit uPageLoader;
interface
uses Classes, SysUtils, idHTTP, StrUtils, IdSSL, IdSSLOpenSSL;
function LoadPage(const Url:string):string; (*Основная функция Юнита*)
procedure ErrorLog(Url:string);
function LoadHtmlPage(const PageUrl:string):string;
function DelJS(const Code:string):string;
function RestoreFrame(const Code:string):string;
function RestoreFullUrl(const Code:string; const RootUrl:string):string;
function GetRandomUserAgent: string;
var
Loader:TidHTTP;
SSL:TIdSSLIOHandlerSocketOpenSSL;
в том числе function LoadHtmlPage(const PageUrl:string):string; в которой как и есть idHTTP
Пока не совсем понятно как вызывать свой экземпляр idHTTP и SSL
Никаких идей нет?
Pcrepair (2012-12-27 23:31:00) [70]для полного использования трафика. в среднем скорость выдачи у серверов небольшая. если трафик к примеру 5000 кбит\сек, при средней скорости отдачи серверов 50 кбит\сек можно запускать до 100 потоков одновременно, ну чтоб провыдЫр не за так деньги получал
bems (2012-12-27 23:31:00) [71]
> в начальный момент может одновременно запуститься практически
> от 50 до 200 потоков
если у тебя есть много урлов еще до старта первого потока, то можно сразу отдать каждому треду пачку урлов и пусть обрабатывает их в цикле. это легко, потому что не нужно делать добавление задания на закачку в уже существующий поток
> реально
> складывается впечетление(по ошибке 10038 и 10056) что где
> то есть узкое место, всем потокам не хватает трафика и некоторые
> получают отказ
в варианте без общего использования одного idHttp ошибки остаются?
Сергей М. (2012-12-27 23:19:00) [72]
> есть решения поумнее?
Объясни накой тебе аж 50 и более потоков ?
Pcrepair (2012-12-27 23:16:00) [73]не будем спотыкаться об уже пройденное, лучше продолжим:
Сама структура программы в принципе правильная?
- получаем список строк(УРЛ)
- запускаем конвейер потоков, передаем каждому свой УРЛ
- по закачке страницы, поток передает результат в ГлобПерем
- поток самоликвидируется
начинай сначала
получается что в начальный момент может одновременно запуститься практически от 50 до 200 потоков реально
складывается впечетление(по ошибке 10038 и 10056) что где то есть узкое место, всем потокам не хватает трафика и некоторые получают отказ. непонятно только как, временные интервалы в idHTTP для подключения к серверу и работы с сервером установлены в 50 сек.
или есть решения поумнее?
Сергей М. (2012-12-27 23:00:00) [74]
> в чем тут безобразие?
см. [26]
bems (2012-12-27 22:32:00) [75]в том что это не скомпилируется
Pcrepair (2012-12-27 22:22:00) [76]
> в сущности не имеет отношения к ООП
ага, разве TThread это не объект? со всеми втекающими и вытекающими
и в чем тут безобразие? поканкратнее пжалуйста
Сергей М. (2012-12-27 22:13:00) [77]
> в ООП пока не сильно
Вот это безобразие
> Create(FIdHttp :=чегототам);
в сущности не имеет отношения к ООП
bems (2012-12-27 22:13:00) [78]аа, не, после того как ты добавил конструктор без параметров, вызвать Create(True) уже нельзя
создавай просто ThreadLdr := Tloader.Create; а остальное у тебя уже внутри конструктора
Pcrepair (2012-12-27 22:04:00) [79]да, это многие советуют - создать пул потоков и давать им задания по закачке страниц. но это следующий этап. пока нужно с этим вариантом разобраться, в частности замечено что на
SSL:=TIdSSLIOHandlerSocketOpenSSL.Create;
Loader.IOHandler:=SSL;
идут исключения в соответствующем модуле ИНДИ.
видимо надо усложнить:
constructor TLoader.Create;
begin
inherited Create(True); (*Поток создаем в состоянии «Приостановлен»*)
FIdHttp := TIdHTTP.Create(nil);
FSSL := TIdSSLIOHandlerSocketOpenSSL.Create;
FreeOnTerminate := True; (* Поток освободит ресурсы при окончании работы*)
Self.Priority := tpNormal;
end;
что касается вызова, там ведь тоже есть
ThreadLdr:=Tloader.Create(True); (*создаем поток, но не запускаем*)
ThreadLdr.Priority:=tpNormal; (*задать приортет нормальный*)
ThreadLdr.FreeOnTerminate:=True; (*самовыгрузка в конце потока*)
(*передача в поток данных*)
ThreadLdr.Url:=Url; (*передаем УРЛ в Поток*)
(*запуск поток на выполнение*)
ThreadLdr.Start;
получается свой конструктор просто дополняет родительский класс новыми свойствами?
в ООП пока не сильно
bems (2012-12-27 21:49:00) [80]вызов от этого не меняется.
но по хорошему не нужно создавать отдельный поток на каждый урл, а использовать фиксированное (или динамическое, но с ограничением) число тредов, каждый из которых обрабатывает урлы по очереди