Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Базы";
Текущий архив: 2002.08.15;
Скачать: [xml.tar.bz2];

Вниз

Почему может виснуть многопоточный сервер приложений?   Найти похожие ветки 

 
Дмитрий Жуков   (2002-07-23 11:59) [0]

На D6 написан сервер приложений, к которому подключаются
несколько клиентов через DCOMConnection или SocketConnection.
Каждый клиент работает со своим RemoteDataModulem, на котором
есть свой Tdatabase и свой TSession. Базой данных является
InterBase 6.0. Также на сервере есть общий DataModul со своим
TDatabase, доступ к TQuery которого для всех потоков реализован
через критическиую секцию.
И вот вся эта конструкция периодически виснет в самые произвольные моменты работы с обхектами типа TQuery. Подскажите, пожалуйста, из-за чего это может происходить?


 
Alexandr   (2002-07-23 12:07) [1]

а строка подключения TdataBase к базе данных какая?


 
Дмитрий Жуков   (2002-07-23 12:25) [2]

tDatabase подключается к базе через алиас. В алиасе
Server Name=ntserver:e:\d\ibserver\ansi\search4.gdb


 
Alexandr   (2002-07-23 12:30) [3]

значит проблема не в том...


 
kaif   (2002-07-23 12:32) [4]

Может, какие-то глобальные переменные или другие ресурсы незащищены... Не помню точно, но по-моему к каждому Query в потоке нужно создавать свой Session...


 
Дмитрий Жуков   (2002-07-23 13:44) [5]

Каждый TDatabase имеет собственный TSession


 
Proton   (2002-07-23 13:54) [6]

железку бенчмарками гонять пробывали - может проблемма аппаратная ?


 
Дмитрий Жуков   (2002-07-23 16:24) [7]

к сожалению правда жизни такова, что виснет на разных железяках...


 
Digitman   (2002-07-23 17:23) [8]

Кто девушку обедает , тот ее и танцует)

Связка объектов session + database должна быть индивидуальна для каждого потока. Недопустимо открывать сессию в одном потоке, а открывать базу (с привязкой к этой сессии) и/или обращаться к ее объектам - в другом потоке. Крит.секции и прочие сист.объекты синхронизации не устраняют ограничений данной особенности


 
Дмитрий Жуков   (2002-07-23 18:23) [9]

Не могли бы Вы указать источник этой информации. Или Вы с этой проблемой воевали сами? А могу ли я в таком случае для работы с общими данными использовать TClientDataset вместо TQuery и модифицировать там данные потоками через критические секции? Ведь TClientDataset насколько я знаю к BDE никаким боком не относится. Или в нем будут такие же проблемы, как и в BDE?


 
Digitman   (2002-07-23 18:55) [10]

Опустив на время из рассмотрения BDE (как промежуточное звено) можно сказать, что источник инф-ции - офиц.документация по ПО InterBase.

Касаемо же BDE и VCL-объектов для доступа к БД через BDE - те же ограничения. Вот они - в хэлпе :

If your application may be accessing the same database multiple times simultaneously, you must use multiple sessions to isolate these uses of the database. Failure to do so will disrupt the logic governing transactions on that database (including transactions created for you automatically). Applications risk simultaneous access when running concurrent queries or when using multiple threads. For more information about using multiple sessions, see Managing multiple sessions.

Тема : Using Sessions

TclientDataSet так же не является thread-safe и применять его в AppServer"e нежелательно да и неразумно - это класс для реализации чисто пользовательских ф-ций в "тонком клиенте"


 
Набережных С.   (2002-07-23 19:12) [11]

Можно организовать через BDE, но заморочек будет больше, чем выигрыш. Вкратце так:
С базой общается один поток, например, основной или специально выделенный. Назовем его базовым.
На каждого клиента запускаешь отдельный поток, а лучше по одному на несколько клиентов.
Клиентские потоки ставят свои запросы в очередь базового, тот по мере обработки ставит результаты в очередь соотв. клиентского потока, а тот уже возвращает их клиенту после требуемой обработки.

Все это может что-то дать при, например, медленном транспорте.
Главный недостаток - большое кол-во переключений потоковых контекстов. В принципе, надо оценивать предполагаемую "плотность" общения отдельного клиента с сервером.
P.S. Это придумано прямо сейчас и на практике не проверялось.



 
Polevi   (2002-07-23 23:19) [12]

а еще лучше ограничить количество потоков - сделать так чтобы несколько клиентов работали в одном потоке, но для этого надо писать свой BSS


 
Набережных С.   (2002-07-23 23:52) [13]


> Polevi © (23.07.02 23:19)

С сокетами я плоховато знаком. Но вообще достаточно написать свой аналог TApartmentThread и подправить фабрику классов, в основном метод CreateInstance... ну и некоторые сопутствующие мелочи. Мне кажется, что и с сокетами этого будет достаточно, хотя вполне вероятно, что я ошибаюсь.


 
Polevi   (2002-07-24 00:02) [14]

2 Набережных С.
Не совсем понял что подразумевается под TApartmentThread..

BSS для каждого клиента создает отдельный поток - это не есть гуд, сейчас сам пытаюсь реализовать вышесказанное мной с помощью портов завершения ввода вывода, есть один нюансик - скорее всего придется писать модули, использующие Free модель, если кому интересно - пишите serg@finco.spb.ru, буду рад пообщаться на эту тему


 
Набережных С.   (2002-07-24 01:03) [15]


> Polevi © (24.07.02 00:02)

TApartmentThread - класс, реализующий поток при использовании апартаментной модели потоков в MIDAS. Описан в модуле VCLCom, как и соответствующая фабрика классов. И реализация нескольких клиентов в одном потоке + динамическая оптимизация числа клиентов на поток + пул потоков честно говоря не составляют особого труда. Как я уже упоминал, я плохо знаком со связкой мидас+сокеты, однако запуском потоков занимается фабрика классов, собственно поток - это TApartmentThread.Execute, и, основываясь на своих представлениях о СОМ и маршаллинге, я сильно сомневаюсь, что при SocetConnection что-то происходит иначе. Хотя, кто не ошибается? Да, и кстати, порт завершения здесь не всегда лучшее решение. Все-таки он делался несколько для других задач и здесь его применение снижает гибкость управления, на мой взгляд.


 
Digitman   (2002-07-24 08:16) [16]

>Polevi

>>BSS для каждого клиента создает отдельный поток - это не есть гуд

Почему же ? Поясни ....

Это ж - архитектура, на которой базируется любой мало-мальски серьезный SuperServer


 
Polevi   (2002-07-24 09:37) [17]

2Набережных С.
Вот как создается объект в BSS

function TDataBlockInterpreter.CreateObject(const Name: string): OleVariant;
var
ClassID: TGUID;
begin
if (Name[1] = "{") and (Name[Length(Name)] = "}") then
ClassID := StringToGUID(Name) else
ClassID := ProgIDToClassID(Name);
if CanCreateObject(ClassID) then
Result := InternalCreateObject(ClassID) else
raise Exception.CreateResFmt(@SObjectNotAvailable, [GuidToString(ClassID)]);
end;

function TDataBlockInterpreter.InternalCreateObject(const ClassID: TGUID): OleVariant;
var
Unk: IUnknown;
begin
OleCheck(CoCreateInstance(ClassID, nil, CLSCTX_INPROC_SERVER or
CLSCTX_LOCAL_SERVER or CLSCTX_REMOTE_SERVER, IUnknown, Unk));
Result := Unk as IDispatch;
end;

2Digitman ©
Много переключений контекстов, достаточно 4-6 потоков в спуле (зависит от количества процессоров). К тому же гибель потока автоматически означает отключение клиента от сервера, если есть спул - данные приходящие в сокет просто станут обрабатываться другим потоком.


 
Digitman   (2002-07-24 09:57) [18]

>Polevi

pooling кодовых потоков хорош лишь в тех случаях, когда не требуется хранить в АП AppServer"а промежуточные результаты контекста логически связанных последовательных запросов одного и того же клиента. Кр.того, невозможно обеспечить гибкость управления транзакциями


 
Polevi   (2002-07-24 10:09) [19]

2Digitman ©
Вот какие изменения внесены мной в SConnect - возможно тут больше 20 строк, но не намного

unit SConnect;
...

const

{ Action Signatures }
...
asSetCallbackInterface=$13;
...

TSocketConnection = class(TStreamedConnection)
...
public
CallbackInterface:IDispatch;
...

procedure TSocketConnection.DoConnect;
begin
...
inherited DoConnect;
SendCallbackInterface;
...
end


procedure TSocketConnection.SendCallbackInterface;
var
Data: IDataBlock;
begin
Data := TDataBlock.Create as IDataBlock;
FInterpreter.WriteVariant(CallbackInterface, Data);
Data.Signature := CallSig or asSetCallbackInterface;
Data := FInterpreter.FSendDataBlock.Send(Data, False);
end;



TDataBlockInterpreter = class
...
public
FCallbackObject:IClientCallbackDisp;
FAppServerModule:IAppServerModule;
...


procedure TDataBlockInterpreter.DoCreateObject(const Data: IDataBlock);
var
V: OleVariant;
VarFlags: TVarFlags;
I: Integer;
begin
V := CreateObject(ReadVariant(VarFlags, Data));
FAppServerModule:=IUnknown(V) as IAppServerModule;
...
end

procedure TDataBlockInterpreter.InterpretData(const Data: IDataBlock);
begin
...
asSetCallbackInterface: DoSetCallbackInterface(Data);
...
end


procedure TDataBlockInterpreter.DoSetCallbackInterface(
const Data: IDataBlock);
var
TmpFlags: TVarFlags;
begin
FCallbackObject:=IClientCallbackDisp(IDispatch(ReadVariant(TmpFlags,Data)));
FAppServerModule.SetClientCallback(FCallbackObject);
end;





 
Polevi   (2002-07-24 10:12) [20]

сорри, не туда сделал пост


 
Polevi   (2002-07-24 10:23) [21]

2Digitman ©
Согласен, просто разные архитектуры


 
Дмитрий Жуков   (2002-07-24 10:54) [22]

2Digitman
А можно поподробнее насчет "что источник инф-ции - офиц.документация по ПО InterBase". Вы хотите сказать, что скажем под MS SQL или Oraclom это будет работать нормально?


 
Набережных С.   (2002-07-24 11:03) [23]


> Polevi © (24.07.02 09:37)

Может, я по-старости окончательно поглупел, однако, по-моему, это не совсем то. Метод CoCreateInstance - стандартный метод библиотеки СОМ.
Он производит поиск и, если не найдет, создание необходимой фабрики классов(опускаем несущественные в нашем случае нюансы), после чего вызывается ее метод CreateInstance. Далее в действие вступает (в случае стандартной реализации Borland и при применении TRemoteDataModule) класс TComponentFactory, точнее, его метод CreateInstance, который собственно и создает СОМ-объект в соответствии с выбранной потоковой моделью и типом сервера, и возвращает интерфейс обратно по цепочке. Если объект реализован в DLL, то он создается в том потоке, который вызвал метод CoCreateInstance. Созданный COM-объект, в свою очередь, создает нашего потомка TRemoteDataModule и является для него контейнером. Т.е., с точки зрения СОМ, TDataBlockInterpreter.CreateObject - это клиентский конец цепи. Как-то даже странно... это ведь основы основ. Или где?


 
Digitman   (2002-07-24 11:22) [24]

>Дмитрий Жуков

Извини, но я понятия не имею о "нормальности" или "ненормальности" чего-то там в Oracle и MSSQL.. На практике я не работал с этими сиквэл-серверами, поэтому судить не берусь. Но интуитивно могу предположить, что и в этих системах (как, впрочем, и в иных сиквэл-серверных системах в арх-ре SuperServer) эти особенности так же имеют место быть...

Вот то, что я поимел из офиц. и неофиц. источников по ПО IB и попробовал собственными руками - о том и смею говорить.


 
Polevi   (2002-07-24 11:27) [25]

2Набережных С.
Да сорри, это основы основ - я понял это заглянув в VCLCom
Буду думать, спасибо


 
Digitman   (2002-07-24 11:29) [26]

>Набережных С. (24.07.02 11:03)

Все правильно. Так это и работает. С той лишь деталью (смею подчеркнуть), что никто и ничто не мешает реализовать фабрику класса AppServer"а как фабрику internal-объектов. Обидно то, что Борланд не предусматривает это, обращаясь (при реализации всей этой цепочки) только к обязательно зарегистрированным в реестре фабрикам классов и к станд.механизму поиска фабрики и ее инициализации (COM-механизму самой ОС)


 
Набережных С.   (2002-07-24 11:55) [27]


> Polevi © (24.07.02 11:27)
Да сорри,

За что?!!!!

> Digitman © (24.07.02 11:29)

Ну разумеется, я разве спорю? Я и сам немало "поизвращался" в этой области :)Я говорил(смею подчеркнуть:)) о стандартной схеме.


 
Дмитрий Жуков   (2002-07-24 13:45) [28]

Господа!
А как объяснить следующий факт?
Наваял я примерчик сервера с RemoteDatamodulem, в котором
есть несколько ф-ций, дергающих базу. У каждого TDatabase свой TSession, созданный этим же потоком. Т.е. все выполнено по правилам. Так вот при обращении нескольких клиентов на сервере через какое-то время при выполнении TQuery.open и TQuery.execSQL вылетает ошибка:
Error reading data from connection
По этой ошибке постепенно отваливают все клиенты, пока не
остается один, которому больше всех повезло - теперь в одиночестве он может работать до бесконечности.
А теперь внимание: при работе с MS SQL Server 2000 или
Oracle 9i никаких ошибок и подвисаний не возникает даже
если они работают с общими TQuery через критические секции!
Получается, проблемы именно в InterBase!




 
Digitman   (2002-07-24 14:01) [29]

>Дмитрий Жуков

Интересная ассоциация получается : ты чего-то там "дергаешь", а IB-сервер крайним остается) ....


 
Дмитрий Жуков   (2002-07-24 15:58) [30]

вот что я дергаю:

procedure TTestRDM.Delete;
var q: TQuery;
i: integer;
begin
q := TQuery.Create(Self);
try
q.SessionName := Session1.SessionName;
q.DatabaseName := Database1.DatabaseName;
i := Random(1000000);
q.SQL.Text := "DELETE FROM TEST123 WHERE F1 = "+ IntToStr(i);
q.ExecSQL;
finally
q.Free;
end;
end;

procedure TTestRDM.Update;
var q: TQuery;
i: integer;
begin
q := TQuery.Create(Self);
try
q.SessionName := Session1.SessionName;
q.DatabaseName := Database1.DatabaseName;
i := Random(1000000);
q.SQL.Text := "UPDATE TEST123 SET F2 = ""UPDATED"" WHERE F1 = "+ IntToStr(i);
q.ExecSQL;
finally
q.Free;
end;
end;

procedure TTestRDM.ClientSelect;
var i, j, f1: integer;
wst: string;
dat1: TDateTime;
q: TQuery;
begin
q := TQuery.Create(Self);
try
q.SessionName := Session1.SessionName;
q.DatabaseName := Database1.DatabaseName;
with q do
begin
SQL.Text := "SELECT * FROM TEST123";
i := Random(200);
j := 0;
Close;
Open;
while (not eof) and (j < i) do
begin
inc(j);
f1 := Fieldbyname("F1").AsInteger;
wst := Fieldbyname("F2").AsString;
dat1 := Fieldbyname("F3").AsDateTime;
next;
end;
locate("F1", i, []);
end;
finally
q.Free;
end;
end;

больше там ничего нет, кроме стандартных

class procedure TTestRDM.UpdateRegistry(Register: Boolean; const ClassID, ProgID: string);
begin
if Register then
begin
inherited UpdateRegistry(Register, ClassID, ProgID);
EnableSocketTransport(ClassID);
EnableWebTransport(ClassID);
end else
begin
DisableSocketTransport(ClassID);
DisableWebTransport(ClassID);
inherited UpdateRegistry(Register, ClassID, ProgID);
end;
end;

initialization
TComponentFactory.Create(ComServer, TTestRDM,
Class_TestRDM, ciMultiInstance, tmApartment);
end.




 
Дмитрий Жуков   (2002-07-24 16:04) [31]

а под ms sql и Oracle этот пример работает, даже если туда добавить код, использующий общий для всех потоков TDatabase и TSession.


 
Случайный прохожий   (2002-07-24 17:28) [32]

А Session1.AutoSessionName установлено в true?


 
Дмитрий Жуков   (2002-07-24 17:33) [33]

да


 
Набережных С.   (2002-07-24 17:40) [34]

Ну тады упс Не моя тема, извиняй:)


 
Дмитрий Жуков   (2002-07-24 17:47) [35]

Да ладно... Я так чувствую, что и не моя тоже...:-(

Людииии!!!!!! Аууууууу!!!!!! Тут есть спецы по BDE и InterBasу?


 
Дмитрий Жуков   (2002-07-25 10:16) [36]

Для тех, кому интересно.
Данный пример заработал только после того, как я ВСЕ
действия, связанные с TQuery, заключил в критическую секцию.
Какие "гении" этот InterBase писали?!!!!


 
erik   (2002-07-25 11:56) [37]

Скорее всего Interbase тут непричем, это BDE дайвер для Interbase глючит. Его еще линком называют.


 
Digitman   (2002-07-25 12:53) [38]

>Дмитрий Жуков

Это - BDE. Понимаю, что лишишься универсальности при проведении подобных экспериментов, но все же - рискни обойтись без BDE, используюя либо непосредственно API либо компоненты прямого доступа к тем сиквел-серверам, которые ты сравниваешь при тестировании на предмет "глюков". Это все же будет гораздо более "чистый" эксперимент


 
Дмитрий Жуков   (2002-07-25 13:55) [39]

А чем тогда обяснить следующий эксперимент:
я переписал этот пример (по совету одного
"специалиста" из Borland.ru) на dbExpress. Теперь
сервер тоже вылетает, но уже с ошибкой
Error writing data to connection
вместо
Error reading data from connection
которая была на BDE. Как говориться, те же яйца,
только в профиль.
В критических секциях тоже все работает.
А на Oracle работает при любых условиях. Под MS SQL
проверить не смог, т.к. его dbExpress просто не
поддерживает.
На InterBase API я писать, к сожалению, не могу,
т.к. универсальность мне просто необходима. Похоже,
придется при работе с InterBase каждому клиенту
ставить сервер приложений.
Это просто праздник какой-то...




 
Digitman   (2002-07-25 14:01) [40]

Как это - "при работе с InterBase каждому клиенту
ставить сервер приложений" ? Поясни ... Зачем клиенту что-то ставить кроме кл.части ? ты что используешь - TSocketConnection ? Или ?


 
Дмитрий Жуков   (2002-07-25 15:55) [41]

Имеется ввиду, что для каждого клиента будет запускаться свой
сервер приложений на машине клиента - тогда никакого
многопоточного доступа не будет, => не будет и таких проблем.
Согласен, способ хреновый, то пускать на сервере все потоки через
очередь к одному Tdatabase еще хуже. Так хоть на остальных
СУБД все будет работать по-человечески.



Страницы: 1 2 вся ветка

Форум: "Базы";
Текущий архив: 2002.08.15;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.56 MB
Время: 0.008 c
4-48370
ATLANTIDO
2002-06-03 16:12
2002.08.15
priority


8-48208
Mr.Ice
2002-04-09 07:39
2002.08.15
скины


1-48032
Sour
2002-08-03 11:21
2002.08.15
Drag&Drop StringGrid..


1-48060
Axill
2002-08-02 21:17
2002.08.15
Функция PlaySound не пашет, почему?


14-48295
Slavka
2002-07-21 22:45
2002.08.15
Движок!!





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский