Delphi 使用 TIdTCPClient 设置 memcashed 值后 readln() 变慢
Delphi slow readln() after set memcashed value using TIdTCPClient
我制作了一个 class TmemcacheClass,我可以在其中设置和获取 memcached 的值。
它用于 Delphi Webbroker 应用程序,其中每个线程打开 TmemcacheClass 并在其生命周期中使用它。这意味着每个线程都打开 TIdTCPClient,并连接它并为每个请求重用。
获取值非常快,如 0 到 1 毫秒
set value 也很快像 1 ms ,但是设置后等待 readln 需要 47 ms.
想知道如何加快速度。
是等待很慢的过程store()
行:= tcp.Socket.ReadLn();
添加孔单元,也许我可以在保存后删除 readl 以加快速度?
如果我放弃 readln,我想我需要清除套接字,因为我正在重用相同的套接字。
unit unitMemcache;
interface
uses
system.sysutils,system.classes,system.JSON,idtcpclient,idGlobal;
type
TmemcacheClass = Class ( Tobject )
private
tcp : TIdTCPClient;
ConnectTimeout : integer;
Fconnected: Boolean;
Ferror: String;
Fcontent: String;
Flinecounter : Integer;
procedure Setconnected(const Value: Boolean);
procedure Seterror(const Value: String);
procedure reportError(e:string);
procedure Setcontent(const Value: String);
public
constructor create( ip : string ) ;
destructor Destroy ( ) ; override ;
procedure store(Key, Value: string);
function Lookup(Key: string ): String;
procedure flush_all;
procedure Delete(Key: string );
procedure connect;
property connected : Boolean read Fconnected write Setconnected;
property error : String read Ferror write Seterror;
property content : String read Fcontent write Setcontent;
End;
implementation
uses system.dateutils;
{ TmemcacheClass }
procedure TmemcacheClass.connect;
begin
try
if not tcp.Connected then
begin
tcp.Connect;
Fconnected:=true;
end;
except
on e: Exception do
begin
reportError(e.Message);
Fconnected:=false;
end;
end;
end;
constructor TmemcacheClass.create(ip: string );
begin
// FConnectTimeout := 4000;
Fconnected:=false;
tcp := TIdTCPClient.Create;
tcp.ConnectTimeout := 1000; // maybe to long , specially if there is a issue...
tcp.ReadTimeout := 2000;
tcp.Host := ip;
tcp.Port := 11211;
tcp.ReuseSocket := rsTrue;
end;
procedure TmemcacheClass.Delete(Key: string);
var
command : String;
stop : Boolean;
line : String;
begin
connect; // just in case
if Fconnected then
begin
command :='delete '+key;
tcp.Socket.Writeln(command);
stop:=false;
repeat
line:= tcp.Socket.ReadLn();
if Line = 'END' then Stop:=true;
if Line = 'DELETED' then Stop:=true;
if Line = 'NOT_FOUND' then Stop:=true;
if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
until Stop;
end;
end;
destructor TmemcacheClass.Destroy;
begin
tcp.Free;
inherited;
end;
procedure TmemcacheClass.flush_all;
var
command : String;
stop : Boolean;
line : String;
begin
Ferror := '';
connect; // just in case
if Fconnected then
begin
command :='flush_all';
try
tcp.Socket.Writeln(command);
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
stop:=false;
repeat
try
line:= tcp.Socket.ReadLn();
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
if Line = 'OK' then Stop:=true;
if Line = 'END' then Stop:=true;
if Line = 'STORED' then Stop:=true;
if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
until Stop;
if line <> 'STORED' then line:= tcp.Socket.ReadLn();
end;
end;
function TmemcacheClass.Lookup(Key : string): String;
var
Top : Boolean;
TopString : String;
line : String;
Data : String;
Stop : Boolean;
max_loops : Integer;
topStringValues : Tstringlist;
command : String;
size : Integer;
begin
Ferror := '';
connect; // just in case
result:='';
if Fconnected then
begin
command :='get '+key;
try
tcp.Socket.Writeln(command);
except
on e: Exception do
begin
reportError(e.Message);
exit;
result:='';
end;
end;
Top:=true;
stop := False;
data :='';
max_loops:=0;
size :=0;
repeat
inc(max_loops);
if top then
begin
tcp.Socket.MaxLineLength:=1700; // default first line :-)
try
TopString:= tcp.Socket.ReadLn();
except
on e: Exception do
begin
reportError(e.Message);
exit;
result:='';
end;
end;
top :=false;
if TopString = 'END' then Stop:=true;
if TopString = 'STORED' then Stop:=true;
if TopString = 'SERVER_ERROR' then
begin
Stop:=true;
reportError('SERVER_ERROR : '+key);
end;
if TopString = 'CLIENT_ERROR' then
begin
Stop:=true;
reportError('CLIENT_ERROR : '+key);
end;
if (TopString = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
if stop=false then
begin
// Decode top string
topStringValues := Tstringlist.Create;
try
TopString:= StringReplace(TopString,' ','*',[rfReplaceAll]);
topStringValues.Delimiter:='*';
topStringValues.DelimitedText:=TopString;
if topStringValues.Count=4 then
begin
size:=strtointdef( topStringValues[3],1700);
tcp.Socket.MaxLineLength:=size+10; // meassure length add 10 in case :-) for reading
end
else
begin
// error with header
stop:=true;
result:='';
data :='';
end;
finally
topStringValues.Clear;
topStringValues.Free;
end
end;
end
else
begin
try
Line := tcp.Socket.ReadLn(); // Can be to long need to check with a stream...
except
on e: Exception do
begin
reportError(e.Message);
exit;
result:='';
end;
end;
if Line = 'END' then Stop:=true;
if Line = 'STORED' then Stop:=true;
if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
if not stop then data := data+line;
end;
if max_loops > 5000 then
begin
stop :=true;
reportError('max_loops > 500 getting : '+key);
exit;
end;
until stop;
result:=data;
end;
end;
procedure TmemcacheClass.reportError(e: string);
begin
Ferror:=e;
tcp.Disconnect;
Fconnected:=false;
Fcontent:='';
end;
procedure TmemcacheClass.Setconnected(const Value: Boolean);
begin
Fconnected := Value;
end;
procedure TmemcacheClass.Setcontent(const Value: String);
begin
Fcontent := Value;
end;
procedure TmemcacheClass.Seterror(const Value: String);
begin
Ferror := Value;
end;
procedure TmemcacheClass.store(Key, Value: string );
var
command : String;
stop : Boolean;
line : String;
start_time : Extended ;
startLabel : String;
begin
start_time:= now;
Ferror := '';
connect; // just in case
if Fconnected then
begin
command :='set '+key+' 0 0 '+length(Value).ToString;
try
tcp.Socket.Writeln(command);
tcp.Socket.Writeln(Value);
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
stop:=false;
repeat
try
line:= tcp.Socket.ReadLn();
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
if Line = 'END' then Stop:=true;
if Line = 'STORED' then Stop:=true;
if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
until Stop;
startLabel:=(MilliSecondsBetween(Now, start_time) ).ToString;
startLabel:=startLabel;
if line <> 'STORED' then line:= tcp.Socket.ReadLn();
end;
end;
end.
我更改了存储过程。在我的命令中添加了 "noreply"。并注释掉回复readln.
现在存储新数据只需要0到1毫秒。在 memcached 中使用 fluch_all 后速度大幅提升,需要重建。
procedure TmemcacheClass.store(Key, Value: string );
var
command : String;
stop : Boolean;
line : String;
start_time : Extended ;
startLabel : String;
begin
start_time:= now;
Ferror := '';
connect; // just in case
if Fconnected then
begin
command :='set '+key+' 0 0 '+length(Value).ToString+' noreply';
try
tcp.Socket.Writeln(command);
tcp.Socket.Writeln(Value);
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
// stop:=false;
// repeat
// try
// line:= tcp.Socket.ReadLn();
// except
// on e: Exception do
// begin
// reportError(e.Message);
// exit;
// end;
// end;
//
//
//
// if Line = 'END' then Stop:=true;
// if Line = 'STORED' then Stop:=true;
// if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
// until Stop;
// startLabel:=(MilliSecondsBetween(Now, start_time) ).ToString;
// startLabel:=startLabel;
//
// if line <> 'STORED' then line:= tcp.Socket.ReadLn();
end;
end;
我制作了一个 class TmemcacheClass,我可以在其中设置和获取 memcached 的值。
它用于 Delphi Webbroker 应用程序,其中每个线程打开 TmemcacheClass 并在其生命周期中使用它。这意味着每个线程都打开 TIdTCPClient,并连接它并为每个请求重用。
获取值非常快,如 0 到 1 毫秒 set value 也很快像 1 ms ,但是设置后等待 readln 需要 47 ms.
想知道如何加快速度。
是等待很慢的过程store()
行:= tcp.Socket.ReadLn();
添加孔单元,也许我可以在保存后删除 readl 以加快速度? 如果我放弃 readln,我想我需要清除套接字,因为我正在重用相同的套接字。
unit unitMemcache;
interface
uses
system.sysutils,system.classes,system.JSON,idtcpclient,idGlobal;
type
TmemcacheClass = Class ( Tobject )
private
tcp : TIdTCPClient;
ConnectTimeout : integer;
Fconnected: Boolean;
Ferror: String;
Fcontent: String;
Flinecounter : Integer;
procedure Setconnected(const Value: Boolean);
procedure Seterror(const Value: String);
procedure reportError(e:string);
procedure Setcontent(const Value: String);
public
constructor create( ip : string ) ;
destructor Destroy ( ) ; override ;
procedure store(Key, Value: string);
function Lookup(Key: string ): String;
procedure flush_all;
procedure Delete(Key: string );
procedure connect;
property connected : Boolean read Fconnected write Setconnected;
property error : String read Ferror write Seterror;
property content : String read Fcontent write Setcontent;
End;
implementation
uses system.dateutils;
{ TmemcacheClass }
procedure TmemcacheClass.connect;
begin
try
if not tcp.Connected then
begin
tcp.Connect;
Fconnected:=true;
end;
except
on e: Exception do
begin
reportError(e.Message);
Fconnected:=false;
end;
end;
end;
constructor TmemcacheClass.create(ip: string );
begin
// FConnectTimeout := 4000;
Fconnected:=false;
tcp := TIdTCPClient.Create;
tcp.ConnectTimeout := 1000; // maybe to long , specially if there is a issue...
tcp.ReadTimeout := 2000;
tcp.Host := ip;
tcp.Port := 11211;
tcp.ReuseSocket := rsTrue;
end;
procedure TmemcacheClass.Delete(Key: string);
var
command : String;
stop : Boolean;
line : String;
begin
connect; // just in case
if Fconnected then
begin
command :='delete '+key;
tcp.Socket.Writeln(command);
stop:=false;
repeat
line:= tcp.Socket.ReadLn();
if Line = 'END' then Stop:=true;
if Line = 'DELETED' then Stop:=true;
if Line = 'NOT_FOUND' then Stop:=true;
if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
until Stop;
end;
end;
destructor TmemcacheClass.Destroy;
begin
tcp.Free;
inherited;
end;
procedure TmemcacheClass.flush_all;
var
command : String;
stop : Boolean;
line : String;
begin
Ferror := '';
connect; // just in case
if Fconnected then
begin
command :='flush_all';
try
tcp.Socket.Writeln(command);
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
stop:=false;
repeat
try
line:= tcp.Socket.ReadLn();
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
if Line = 'OK' then Stop:=true;
if Line = 'END' then Stop:=true;
if Line = 'STORED' then Stop:=true;
if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
until Stop;
if line <> 'STORED' then line:= tcp.Socket.ReadLn();
end;
end;
function TmemcacheClass.Lookup(Key : string): String;
var
Top : Boolean;
TopString : String;
line : String;
Data : String;
Stop : Boolean;
max_loops : Integer;
topStringValues : Tstringlist;
command : String;
size : Integer;
begin
Ferror := '';
connect; // just in case
result:='';
if Fconnected then
begin
command :='get '+key;
try
tcp.Socket.Writeln(command);
except
on e: Exception do
begin
reportError(e.Message);
exit;
result:='';
end;
end;
Top:=true;
stop := False;
data :='';
max_loops:=0;
size :=0;
repeat
inc(max_loops);
if top then
begin
tcp.Socket.MaxLineLength:=1700; // default first line :-)
try
TopString:= tcp.Socket.ReadLn();
except
on e: Exception do
begin
reportError(e.Message);
exit;
result:='';
end;
end;
top :=false;
if TopString = 'END' then Stop:=true;
if TopString = 'STORED' then Stop:=true;
if TopString = 'SERVER_ERROR' then
begin
Stop:=true;
reportError('SERVER_ERROR : '+key);
end;
if TopString = 'CLIENT_ERROR' then
begin
Stop:=true;
reportError('CLIENT_ERROR : '+key);
end;
if (TopString = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
if stop=false then
begin
// Decode top string
topStringValues := Tstringlist.Create;
try
TopString:= StringReplace(TopString,' ','*',[rfReplaceAll]);
topStringValues.Delimiter:='*';
topStringValues.DelimitedText:=TopString;
if topStringValues.Count=4 then
begin
size:=strtointdef( topStringValues[3],1700);
tcp.Socket.MaxLineLength:=size+10; // meassure length add 10 in case :-) for reading
end
else
begin
// error with header
stop:=true;
result:='';
data :='';
end;
finally
topStringValues.Clear;
topStringValues.Free;
end
end;
end
else
begin
try
Line := tcp.Socket.ReadLn(); // Can be to long need to check with a stream...
except
on e: Exception do
begin
reportError(e.Message);
exit;
result:='';
end;
end;
if Line = 'END' then Stop:=true;
if Line = 'STORED' then Stop:=true;
if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
if not stop then data := data+line;
end;
if max_loops > 5000 then
begin
stop :=true;
reportError('max_loops > 500 getting : '+key);
exit;
end;
until stop;
result:=data;
end;
end;
procedure TmemcacheClass.reportError(e: string);
begin
Ferror:=e;
tcp.Disconnect;
Fconnected:=false;
Fcontent:='';
end;
procedure TmemcacheClass.Setconnected(const Value: Boolean);
begin
Fconnected := Value;
end;
procedure TmemcacheClass.Setcontent(const Value: String);
begin
Fcontent := Value;
end;
procedure TmemcacheClass.Seterror(const Value: String);
begin
Ferror := Value;
end;
procedure TmemcacheClass.store(Key, Value: string );
var
command : String;
stop : Boolean;
line : String;
start_time : Extended ;
startLabel : String;
begin
start_time:= now;
Ferror := '';
connect; // just in case
if Fconnected then
begin
command :='set '+key+' 0 0 '+length(Value).ToString;
try
tcp.Socket.Writeln(command);
tcp.Socket.Writeln(Value);
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
stop:=false;
repeat
try
line:= tcp.Socket.ReadLn();
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
if Line = 'END' then Stop:=true;
if Line = 'STORED' then Stop:=true;
if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
until Stop;
startLabel:=(MilliSecondsBetween(Now, start_time) ).ToString;
startLabel:=startLabel;
if line <> 'STORED' then line:= tcp.Socket.ReadLn();
end;
end;
end.
我更改了存储过程。在我的命令中添加了 "noreply"。并注释掉回复readln.
现在存储新数据只需要0到1毫秒。在 memcached 中使用 fluch_all 后速度大幅提升,需要重建。
procedure TmemcacheClass.store(Key, Value: string );
var
command : String;
stop : Boolean;
line : String;
start_time : Extended ;
startLabel : String;
begin
start_time:= now;
Ferror := '';
connect; // just in case
if Fconnected then
begin
command :='set '+key+' 0 0 '+length(Value).ToString+' noreply';
try
tcp.Socket.Writeln(command);
tcp.Socket.Writeln(Value);
except
on e: Exception do
begin
reportError(e.Message);
exit;
end;
end;
// stop:=false;
// repeat
// try
// line:= tcp.Socket.ReadLn();
// except
// on e: Exception do
// begin
// reportError(e.Message);
// exit;
// end;
// end;
//
//
//
// if Line = 'END' then Stop:=true;
// if Line = 'STORED' then Stop:=true;
// if (Line = '') and ( not tcp.Socket.ReadLnTimedOut) then Stop:=true;
// until Stop;
// startLabel:=(MilliSecondsBetween(Now, start_time) ).ToString;
// startLabel:=startLabel;
//
// if line <> 'STORED' then line:= tcp.Socket.ReadLn();
end;
end;