Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Проблемы с потоками при закачке страниц


Страницы:   1 2 3 4 5

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]

вызов от этого не меняется.
но по хорошему не нужно создавать отдельный поток на каждый урл, а использовать фиксированное (или динамическое, но с ограничением) число тредов, каждый из которых обрабатывает урлы по очереди


Страницы:   1 2 3 4 5

Наверх

Титульная страница Поиск, карта сайта Написать письмо