Нуждаюсь в помощи, совете, пояснениях.
Написал примитивный rest сервер для работы Android приложения с базой на MS SQL
(предыдущая тема http://www.freepascal.ru/forum/viewtopic.php?f=5&t=42810 )
служба rest сервера работает.
Но при небольшом увеличении нагрузки, служба виснет. Один раз повисла так, что процесс не удалялся никаким средствами, а для taskkill не определялся PID процесса, пришлось перезапускать винду.
Никаких потоков специально не оформлял, вроде как TIdHTTPServer и так многопоточная компонента.
Предполагаю, что дело именно в этом.
Помогите пожалуйста, как отслеживать отслеживать ошибку/ понять причину запвисания?
Как правильно оформлять работу с файлами и с sql сервером в службе?
---
Работа строго в локалке.
сервер получает 3 get запроса и запускает скрипт с параметрами в котором используются хранимые процедура и табличная функция.
Процедура определяет девайс по параметру "мак-адрес" и записывает в таблицу лога параметры запроса.
Процедура возвращает флаг 0/1
в зависимости от которого (=0) скрипт возвращает сообщение об ошибке, или вызывает функцию, и возвращает ее результат
- Код: Выделить всё
declare @hid int, @devsn varchar(20), @jsontxt varchar(max), @ident int
set @hid = isnull( :prmhid , -1)
set @devsn = isnull( :prmDeviceSN, '''' )
set @ident = 0
set @jsontxt= ''сообщение об ошибке''
exec dbo.sp_rest_hInfo_LOG @hid, @devsn, @identified=@ident output
if @ident=1 select top 1 @jsontxt=jsontxt from dbo.sp_rest_hInfo(@hid)
select ( @jsontxt ) jsontxt
Все три скрипта (для 3х get запросов) похожи на этот, различие в именах функций и количестве дополнительных параметров.
Все процедуры и функции службы я расместил по init-ам
в unit daemon_unit_main
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleShutDown(Sender: TCustomDaemon);
procedure DataModuleStart(Sender: TCustomDaemon; var OK: Boolean);
procedure DataModuleStop(Sender: TCustomDaemon; var OK: Boolean);
procedure ServerRestCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
Вот текст основного unit
- Код: Выделить всё
unit daemon_unit_main;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, IdHTTPServer, IdSocketHandle, Interfaces, ExtCtrls, DaemonApp, LazUTF8, IdCustomHTTPServer, IdContext, IdGlobal, IdCoder, IdCoderMIME;
type
{ TDM }
TDM = class(TDaemon)
IdHTTPServerRest: TIdHTTPServer;
procedure DataModuleCreate(Sender: TObject);
procedure DataModuleShutDown(Sender: TCustomDaemon);
procedure DataModuleStart(Sender: TCustomDaemon; var OK: Boolean);
procedure DataModuleStop(Sender: TCustomDaemon; var OK: Boolean);
procedure ServerRestCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
private
public
end;
var DM: TDM;
prm0:string;
IdHTTPServerRest:TIdHTTPServer;
Binding:TIdSocketHandle;
rest_port, IntChKey:integer;
ver, err_msg:string;
implementation
uses myProc, readini, unit_sql, writelog, lpudata;
procedure RegisterDaemon;
begin
RegisterDaemonClass(TDM)
end;
{$R *.lfm}
{ TDM }
{ запоминание пути запуска при старте }
procedure TDM.DataModuleCreate(Sender: TObject);
begin
prm0:=ParamStr(0);
end;
{ Запуск службы, чтение параметров, подключение к базе }
procedure TDM.DataModuleStart(Sender: TCustomDaemon; var OK: Boolean);
var automediINI, automediVER:string;
begin
ver:=myProc.ProgramVersion();
optionsINI:=readini.GetIni(prm0+'options.ini');
readini.GetParametersFromIni(optionsINI);
writelog.CreateLogFile();
unit_sql.MSSQLServCreate();
IdHTTPServerRest:=TIdHTTPServer.Create;
IdHTTPServerRest.OnCommandGet:=@ServerRestCommandGet;
Binding := IdHTTPServerRest.Bindings.Add;
Binding.Port := rest_port;
IdHTTPServerRest.Active := True;
end;
{ отключение от sql сервера при остановке службы}
procedure TDM.DataModuleStop(Sender: TCustomDaemon; var OK: Boolean);
begin
//DM.IdHTTPServerRest.Active:=false;
//unit_sql.MSSQLServActClose();
writelog.CloseLogFile();
end;
procedure TDM.DataModuleShutDown(Sender: TCustomDaemon);
begin
writelog.CloseLogFile();
end;
//
// web часть
//
procedure TDM.ServerRestCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var rCommand, rDocument, rRemoteIP, rParams: string;
hid, actID:integer; deviceSN, respTxt:string;
begin
AResponseInfo.AuthRealm:='';
AResponseInfo.ContentLanguage:='ru';
AResponseInfo.CharSet:='UTF-8';
AResponseInfo.ContentType:='application/json';
if unit_sql.MSSQLServActConnect() then
begin
rCommand := ARequestInfo.Command;
rDocument := StringReplace( AnsiLowerCase(ARequestInfo.Document) , '/', '', [rfReplaceAll, rfIgnoreCase]);
rRemoteIP := ARequestInfo.RemoteIP;
rParams := ARequestInfo.Params.Text;
hid:=-1;
actID:=-1;
if lpuData.CheckKey() then --сверка ключа
begin
if rDocument = 'hinfo' then
begin
hid:= StrToInt( ARequestInfo.Params.Values['hid'] );
deviceSN:=ARequestInfo.Params.Values['deviceSN'];
writelog.WrLog( 'get_рInfo // hID = '+inttostr(hid)+' // deviceSN = '+deviceSN);
respTxt:=unit_sql.GetPatInfo(hid,deviceSN); --процедура выполнения sql скрипта
end;
if rDocument = 'act' then
begin
hid:= StrToInt( ARequestInfo.Params.Values['hid'] );
deviceSN:=ARequestInfo.Params.Values['deviceSN'];
writelog.WrLog( 'get_Act // hID = '+inttostr(hid)+' // deviceSN = '+deviceSN);
respTxt:=unit_sql.GetActы(hid, deviceSN);
end;
if rDocument = 'writeact' then
begin
hid:= StrToInt( ARequestInfo.Params.Values['hid'] );
deviceSN:=ARequestInfo.Params.Values['deviceSN'];
actID:=StrToInt( ARequestInfo.Params.Values['ActID'] );
writelog.WrLog( 'get_WriteAct // hID = '+inttostr(hid)+' // deviceSN = '+deviceSN+' // ActID = '+inttostr(actID) );
respTxt:=unit_sql.GetInsertAct(hid, deviceSN, actID);
end;
unit_sql.MSSQLServActClose();
AResponseInfo.ResponseNo:=200;
end
else
begin
respTxt:='Обратитесь к системному администратору.';
AResponseInfo.ResponseNo:=451;
end;
end
else
begin
respTxt:='База не доступна, попробуйте позже.';
AResponseInfo.ResponseNo:=404;
end;
AResponseInfo.ContentText:= UTF8ToWinCP(respTxt);
AResponseInfo.ContentLength := length(respTxt);
end;
initialization
RegisterDaemon;
end.
в unit writelog - функция создания текстового файла лога, и функции записи в него. Файл не держится открытым постоянно, а открывается для очередной записи процедурой Append
в unit readini - чтение параметров сервера из ini файла. Чтение осуществляется 1 раз в обработчике DataModuleStart
в unit unit_sql - находятся процедуры создания объектов TMSSQLConnection, TSQLQuery, TSQLTransaction , функция коннекта, функции выполнения 3 скриптов
в unit lpudata - функция проверки лиц.ключа прграммы, ключ хранится в базе и вынимается запросом через отдельный коннект специальных экземпляров объектов TMSSQLConnection, TSQLQuery