Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2021-12

Improved FPC JSON-RPC support

15 messages · 7 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Michael Van Canneyt

Hello,

Thanks to the magic of RTTI and Invoke(), creating a JSON-RPC server has just become significantly easier !

Given an interface definition:

   IMyOtherInterface = interface ['{4D52BEE3-F709-44AC-BD31-870CBFF44632}']
     Function SayHello : string;
     function Echo(args : TStringArray) : String;
   end;

Creating a JSON-RPC server for this is now as simple as:

// Create a class that implements the interface
Type
   TIntf2Impl = class(TInterfacedObject, IMyOtherInterface)
   public
     function Echo(args: TStringArray): String;
     function SayHello: string;
   end;
function TIntf2Impl.Echo(args: TStringArray): String;
var
   S : String;
begin
   Result:='';
   For S in Args do
     begin
     if Result'' then
       Result:=Result+' ';
     Result:=Result+S;
     end
end;
function TIntf2Impl.SayHello: string;
begin
   Result:='Hello, World!';
end;
// Register the class using an interface factory:
Function GetMyOtherInterface(Const aName : string) : IInterface;
begin
   Result:=TIntf2Impl.Create as IInterface;
end;
initialization
   RTTIJSONRPCRegistry.Add(TypeInfo(IMyOtherInterface),@GetMyOtherInterface,'Service2');
end.

And calling it from a client program is even more simple:

var
   client: IMyOtherInterface;
   aRPCClient: TFPRPCClient;
begin
   // Register interface with name 'Service2'
   RPCServiceRegistry.Add(TypeInfo(IMyOtherInterface),'Service2');
   // Create client
   aRPCClient:=TFPRPCClient.Create(Nil);
   aRPCClient.BaseURL:='http://localhost:8080/RPC';
   // Just typecast the client to the desired interface
   Client:=aRPCClient as IMyotherInterface;
   // or explitly create using the registered service name
   // Client:=RPC.Specialize CreateService<IMyotherInterface>('Service2');
   Writeln('Sayhello: ',client.SayHello);
   Writeln('Sayhello: ',client.Echo(['This','is','Sparta']));
end.

The service can also be consumed by pas2js.

The support for various argument types is still limited, but this will improve soon. Currently simple types and arrays of simple types are improved. Proper support for records and other structured types needs extended RTTI...

An example server and client programs have been committed to the FPC repo. (packages/fcl-web/examples/jsonrpc/rtti)

With many thanks to Sven Barth for putting me on the right track...

Enjoy!

Michael.

Reply by email

Graeme Geldenhuys
to Michael Van Canneyt
Michael Van Canneyt wrote
Thanks to the magic of RTTI and Invoke(), creating a JSON-RPC server has just become significantly easier !

That's pretty neat - nicely done Michael.

Show quoted text 7 lines
// Create a class that implements the interface Type   TIntf2Impl = class(TInterfacedObject, IMyOtherInterface)   public     function Echo(args: TStringArray): String;     function SayHello: string;   end;

...snip...

Show quoted text 5 lines
// Register the class using an interface factory: Function GetMyOtherInterface(Const aName : string) : IInterface; begin   Result:=TIntf2Impl.Create as IInterface; end;

Slightly off topic, and my Object Pascal is getting a bit rusty as the years go by. <grin> ;-) In your factory above, is the "as IInterface" part needed. Can't the FPC compiler automatically cast the return type based on the function return type, and the fact that the compiler should know that TIntf2Impl type implements TInterfacedObject?

Regards,
Graeme

Reply by email

Michael Van Canneyt
to Graeme Geldenhuys
Graeme Geldenhuys wrote 6 lines
On 2021-12-28 15:25, Michael Van Canneyt via fpc-pascal wrote: > > Thanks to the magic of RTTI and Invoke(), creating a JSON-RPC server has > just become significantly easier ! That's pretty neat - nicely done Michael.

We do our best.

Show quoted text 22 lines
> // Create a class that implements the interface > Type >   TIntf2Impl = class(TInterfacedObject, IMyOtherInterface) >   public >     function Echo(args: TStringArray): String; >     function SayHello: string; >   end; ...snip... > // Register the class using an interface factory: > Function GetMyOtherInterface(Const aName : string) : IInterface; > begin >   Result:=TIntf2Impl.Create as IInterface; > end; Slightly off topic, and my Object Pascal is getting a bit rusty as the years go by. <grin> ;-) In your factory above, is the "as IInterface" part needed. Can't the FPC compiler automatically cast the return type based on the function return type, and the fact that the compiler should know that TIntf2Impl type implements TInterfacedObject?

No, theoretically it probably could, but type inference is not implemented in Pascal.

Michael.

Reply by email

Don Duvall
to Michael Van Canneyt

Has any performance tests been ran, to see what the runtime impact of utilizing RTTI is? Hopefully negligent in the grand scheme...

Thanks,
Don

Michael Van Canneyt wrote 46 lines
On Tue, 28 Dec 2021, Graeme Geldenhuys via fpc-pascal wrote: > On 2021-12-28 15:25, Michael Van Canneyt via fpc-pascal wrote: >> >> Thanks to the magic of RTTI and Invoke(), creating a JSON-RPC server >> has >> just become significantly easier ! > > That's pretty neat - nicely done Michael. We do our best. >> // Create a class that implements the interface >> Type >>    TIntf2Impl = class(TInterfacedObject, IMyOtherInterface) >>    public >>      function Echo(args: TStringArray): String; >>      function SayHello: string; >>    end; > > ...snip... > >> // Register the class using an interface factory: >> Function GetMyOtherInterface(Const aName : string) : IInterface; >> begin >>    Result:=TIntf2Impl.Create as IInterface; >> end; > > > Slightly off topic, and my Object Pascal is getting a bit rusty as > the years go by. <grin> ;-) In your factory above, is the "as > IInterface" part needed. Can't the FPC compiler automatically cast > the return type based on the function return type, and the fact that > the compiler should know that TIntf2Impl type implements > TInterfacedObject? No, theoretically it probably could, but type inference is not implemented in Pascal. Michael. _______________________________________________ fpc-pascal maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Mattias Gaertner
to Michael Van Canneyt: We do our best. No, theoretically it probably could, but type inference is not…

On Tue, 28 Dec 2021 23:11:42 +0100 (CET)
Michael Van Canneyt via fpc-pascal

[...] > Slightly off topic, and my Object Pascal is getting a bit rusty as > the years go by. <grin> ;-) In your factory above, is the "as > IInterface" part needed.

No.

Can't the FPC compiler automatically cast > the return type based on the function return type, and the fact > that the compiler should know that TIntf2Impl type implements > TInterfacedObject?

Yes.

No, theoretically it probably could, but type inference is not implemented in Pascal.

This is not type inference.
It compiles with fpc and pas2js:

Function GetMyOtherInterface(Const aName : string) : IInterface;
begin
  Result:=TIntf2Impl.Create;
end;

Mattias

Reply by email

Michael Van Canneyt
to Don Duvall: Has any performance tests been ran, to see what the runtime impact of utilizing RTTI is?…
Don Duvall wrote
Has any performance tests been ran, to see what the runtime impact of utilizing RTTI is? Hopefully negligent in the grand scheme...

No tests have been run.
I also don't know what exactly you would compare it with ?

Using some plain common sense:
The 'older' json-rpc mechanism relied on TJSONRPCHandler components on a Datamodule. Loading a datamodule and all components (one per exposed method) on it also uses RTTI, but is bound to beslower due to all the loading & parsing of the lfm file for every call.

Even so, all that will be dwarfed by the network latency & http+json protocol:

Many years ago, I did a speed comparison test of the various protocols offered by Remobjects SDK in delphi. It was published in Toolbox magazine:

https://www.freepascal.org/~michael/articles/index.html#remobjects2 (the date on the article is misleading, it is actually older, must have been 2005 or so)

They showed clearly that the used protocol (http & xml) is the largest factor. in 2013 I did many tests in a JSON REST framework: the times for streaming were dwarfed by the time spent on network & database access.

Translated to RPC: if you want speed, don't use HTTP or JSON. WST offers a binary protocol and plain TCP channel, it's bound to be much faster.

Michael.

Show quoted text 55 lines
Thanks, Don On 12/28/2021 2:11 PM, Michael Van Canneyt via fpc-pascal wrote: > > > On Tue, 28 Dec 2021, Graeme Geldenhuys via fpc-pascal wrote: > >> On 2021-12-28 15:25, Michael Van Canneyt via fpc-pascal wrote: >>> >>> Thanks to the magic of RTTI and Invoke(), creating a JSON-RPC server >>> has >>> just become significantly easier ! >> >> That's pretty neat - nicely done Michael. > > We do our best. > >>> // Create a class that implements the interface >>> Type >>>    TIntf2Impl = class(TInterfacedObject, IMyOtherInterface) >>>    public >>>      function Echo(args: TStringArray): String; >>>      function SayHello: string; >>>    end; >> >> ...snip... >> >>> // Register the class using an interface factory: >>> Function GetMyOtherInterface(Const aName : string) : IInterface; >>> begin >>>    Result:=TIntf2Impl.Create as IInterface; >>> end; >> >> >> Slightly off topic, and my Object Pascal is getting a bit rusty as >> the years go by. <grin> ;-) In your factory above, is the "as >> IInterface" part needed. Can't the FPC compiler automatically cast >> the return type based on the function return type, and the fact that >> the compiler should know that TIntf2Impl type implements >> TInterfacedObject? > > No, theoretically it probably could, but type inference is not > implemented in Pascal. > > Michael. > > _______________________________________________ > fpc-pascal maillist - > https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal _______________________________________________ fpc-pascal maillist - https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

Reply by email

Unknown author
to Michael Van Canneyt
Michael Van Canneyt wrote
Translated to RPC: if you want speed, don't use HTTP or JSON. WST offers a binary protocol and plain TCP channel, it's bound to be much faster.

i'm sorry... what is WST? googling for "wst binary protocol tcp ip channel" doesn't turn up anything informative for me and acronym searching is equally uninformative... getting old sucks :?

NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list where it belongs!*

Reply by email

Sven Barth
to Unknown author: i'm sorry... what is WST? googling for "wst binary protocol tcp ip channel" doesn't turn…
wkitty42--- wrote 8 lines
On 12/29/21 4:54 AM, Michael Van Canneyt via fpc-pascal wrote: > Translated to RPC: if you want speed, don't use HTTP or JSON. WST > offers a > binary protocol and plain TCP channel, it's bound to be much faster. i'm sorry... what is WST? googling for "wst binary protocol tcp ip channel" doesn't turn up anything informative for me and acronym searching is equally uninformative... getting old sucks :?

WST = Web Service Toolkit
https://wiki.freepascal.org/Web_Service_Toolkit

Regards,
Sven

Reply by email

Michael Van Canneyt
to Unknown author: i'm sorry... what is WST? googling for "wst binary protocol tcp ip channel" doesn't turn…
wkitty42--- wrote 7 lines
On 12/29/21 4:54 AM, Michael Van Canneyt via fpc-pascal wrote: > Translated to RPC: if you want speed, don't use HTTP or JSON. WST offers a > binary protocol and plain TCP channel, it's bound to be much faster. i'm sorry... what is WST? googling for "wst binary protocol tcp ip channel" doesn't turn up anything informative for me and acronym searching is equally uninformative... getting old sucks :?

Web Service Toolkit:

https://wiki.freepascal.org/Web_Service_Toolkit

The Lazarus online package manager can install it.

Michael.

Reply by email

Howard Page-Clark
to Unknown author: i'm sorry... what is WST? googling for "wst binary protocol tcp ip channel" doesn't turn…
wkitty42--- wrote 10 lines
On 12/29/21 4:54 AM, Michael Van Canneyt via fpc-pascal wrote: > Translated to RPC: if you want speed, don't use HTTP or JSON. WST > offers a > binary protocol and plain TCP channel, it's bound to be much faster. i'm sorry... what is WST? googling for "wst binary protocol tcp ip channel" doesn't turn up anything informative for me and acronym searching is equally uninformative... getting old sucks :?

WST stands for Web Services Toolkit, a package that offers both provision (authoring) of web services, and facilitates programming web service consumption.

A package for the framework is available in Lazarus' online package manager.

Reply by email

Graeme Geldenhuys
to Michael Van Canneyt: No tests have been run. I also don't know what exactly you would compare it with ? Using…
Michael Van Canneyt wrote
They showed clearly that the used protocol (http & xml) is the largest factor. in 2013 I did many tests in a JSON REST framework: the times for streaming were dwarfed by the time spent on network & database access.

Have you, or anybody else, ever done some benchmarks to see how "expensive" in CPU time it is to parse the exact same data in XML format vs JSON format?

I couldn't find FPC specific benchmarks, but did a basic Firefox v95.0.2 browser Network tests (with caching disabled). A 54KB JSON file vs a 121KB XML file, which represents the exact same 1000 rows of a database table. Interestingly (and surprising to me), Firefox managed to load the XML in about half the time (172ms) compared to the JSON (386ms) file. I did the load test multiple times, and the results were consistent.

Regards,
Graeme

Reply by email

Michael Van Canneyt
to Graeme Geldenhuys: Have you, or anybody else, ever done some benchmarks to see how "expensive" in CPU time…
Graeme Geldenhuys wrote 16 lines
On 2021-12-29 09:54, Michael Van Canneyt via fpc-pascal wrote: > They showed clearly that the used protocol (http & xml) is the largest > factor. > in 2013 I did many tests in a JSON REST framework: the times for > streaming were dwarfed by the time spent on network & database access. Have you, or anybody else, ever done some benchmarks to see how "expensive" in CPU time it is to parse the exact same data in XML format vs JSON format? I couldn't find FPC specific benchmarks, but did a basic Firefox v95.0.2 browser Network tests (with caching disabled). A 54KB JSON file vs a 121KB XML file, which represents the exact same 1000 rows of a database table. Interestingly (and surprising to me), Firefox managed to load the XML in about half the time (172ms) compared to the JSON (386ms) file. I did the load test multiple times, and the results were consistent.

It depends on how you load it. It's quite possible the XML uses a SAX model where the nodes are not loaded till you actually access them. So I would definitely also add a loop that runs over all rows.

I did a test once on pure FPC, and there the JSON worked faster.

Michael.

Reply by email