В трехуровневой системе, написанной на DELPHI 5.0, есть несколько разных серверов приложений (в виде EXE-модулей), работающих на одном физическом сервере под Windows 2000 Server и подключающихся к SQL Server 2000 через ADO, есть также несколько соединений между серверами. Сервера стартуются вручную администратором. Все COM-объекты выполняются в MTA, используют учетную запись "Текущий пользователь" (The Interactive user). К ним подключаются клиенты через DCOM, к некоторым серверам до 200 подключений.
Через некоторое время вызовы с рабочих станций начинают отваливаются с различными ошибками (Interface not supported, Сервер RPC недоступен и др.), причем не все, но ошибок пролетает много.
Также бывает другая ситуация — все вызовы к одному серверу начинают отваливаться с ошибкой "Out of memory", причем сервер не обязательно при этом "держит" много памяти (судя по Task Manager'у). После перезапуска этого сервера работает еще какое-то время..
Для исключения ошибок в реализации прикладной части написал тестовую программу, которая создает много потоков, каждый поток подключается к серверу (создает экземпляр COM-объекта), вызывает простой метод и отключается.
Некоторое время, довольно много тестов выполнялись успешно, ни одной ошибки. Затем произошла описанная ситуация, причем ошибки начали периодически возникать в разных местах. Выполнил тест с другой рабочей станции (к тому же серверу), ошибки полезли почти сразу. Такое ощущение, как-будто что-то случается с самой службой COM (или RPC?).
В связи с этим вопрос, в чем может быть проблема? Есть ли какие-либо ограничения на количество или интенсивность DCOM-соединений/вызовов? Можем ли мы неверно использовать саму технологию COM?
Здравствуйте, Tom, Вы писали:
S>>Можем ли мы неверно использовать саму технологию COM?
Tom>Можете. Приводи код.
ОК. Привожу код (
здесь лежит весь проект (6887 байтов))
Сначала COM-сервер:
VTServer.dpr
program VTServer;
uses
Forms,
MainFormUnit in 'MainFormUnit.pas' {MainForm},
VTServer_TLB in 'VTServer_TLB.pas',
ServerUnit in 'ServerUnit.pas' {SBTstVolumeTest: CoClass};
{$R *.TLB}
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.
MainFormUnit.pas, MainFormUnit.dfm
Главная форма приложения-сервера (пустая)
VTServer_TLB.pas, VTServer.tlb
Библиотека типов, сгенерированная средой DELPHI
ServerUnit.pas
Непосредственно сам COM-объект
unit ServerUnit;
interface
uses
ComObj, ActiveX, VTServer_TLB, StdVcl;
type
TSBTstVolumeTest = class(TAutoObject, ISBTstVolumeTest)
protected
procedure Test; safecall;
end;
implementation
uses
ComServ;
{ TSBTstVolumeTest }
procedure TSBTstVolumeTest.Test;
begin
end;
initialization
TAutoObjectFactory.Create(ComServer, TSBTstVolumeTest, Class_SBTstVolumeTest,
ciMultiInstance, tmFree);
end.
Теперь клиент
Client.dpr
program Client;
uses
Forms,
VTServer_TLB in '..\Server\VTServer_TLB.pas',
MainFormUnit in 'MainFormUnit.pas' {MainForm},
TestThreadUnit in 'TestThreadUnit.pas';
{$R *.RES}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.Run;
end.
MainFormUnit.pas, MainFormUnit.dfm
Главная форма клиентского приложения
unit MainFormUnit;
interface
uses
SysUtils, Classes, Graphics, Controls, Forms, StdCtrls;
type
TMainForm = class(TForm)
lblHost: TLabel;
lblThreadCount: TLabel;
txtThreadCount: TEdit;
btnExecuteThreads: TButton;
txtHost: TEdit;
procedure btnExecuteThreadsClick(Sender: TObject);
end;
var
MainForm: TMainForm;
implementation
uses TestThreadUnit;
{$R *.DFM}
procedure TMainForm.btnExecuteThreadsClick(Sender: TObject);
var
i: Integer;
begin
Randomize;
for i:= 1 to StrToInt(txtThreadCount.Text) do
TTestThread.Create(txtHost.Text);
end;
end.
TestThreadUnit.pas
Поток, вызывающий тест
unit TestThreadUnit;
interface
uses
Classes;
type
TTestThread = class(TThread)
private
FHost: string;
protected
procedure Execute; override;
public
constructor Create(const Host: string);
end;
implementation
uses
Windows, SysUtils, ActiveX, Forms, ComObj,
VTServer_TLB;
var LogCS: TRTLCriticalSection;
procedure Log(Msg: string);
const DebugLogFileName = 'errors.log';
var T: TextFile;
begin
Msg:= Format('%s %s', [FormatDateTime('hh:nn:ss:zzz', Time), Msg]);
EnterCriticalSection(LogCS);
try
AssignFile(T, DebugLogFileName);
if FileExists(DebugLogFileName) then
Append(T)
else
Rewrite(T);
Writeln(T, Msg);
Flush(T);
CloseFile(T);
finally
LeaveCriticalSection(LogCS);
end;
end;
{ TTestThread }
constructor TTestThread.Create(const Host: string);
begin
FHost:= Host;
FreeOnTerminate:= True;
inherited Create(False);
end;
procedure TTestThread.Execute;
var
IServer: ISBTstVolumeTest;
i: Integer;
begin
IServer:= nil;
OleCheck(CoInitialize(nil));
try
for i:= 1 to 10000 do
try
if Application.Terminated then
Exit;
IServer:= CoSBTstVolumeTest.CreateRemote(FHost);
IServer.Test;
IServer:= nil;
Sleep(30 + Random(1000));
Log('Выполнено успешно');
except
on E: Exception do Log(Format('ОШИБКА: %s', [E.Message]));
end;
finally
CoUninitialize;
end;
end;
initialization
InitializeCriticalSection(LogCS);
finalization
DeleteCriticalSection(LogCS);
end.
Это все.
Только что еще раз специально включил два компьютера (Win2000 и WinXP), на одном запустил VTServer.exe (не забыв переключить объект SBTstVolumeTest в DCOMCNFG на "The Interactive user"). На другом запустил Client.exe, включил 10 потоков — все работает замечательно. Включил 100 потоков — сразу посыпались ошибки

.
Пардон, не выполнил вход. Предыдущее сообщение мое.