dhRPCServer
This is the "flagship" in our Tool-Set and contains not only an XCopy-deployable Serverpart, it comes of course with an appropriate RPCClient-Part too.
The communication between the Client- and the Server-Part is done over TCP/IP-sockets under usage of a fast binary protocol (and one single, definable port only).
Basically it is the "barebone-version" of our larger dBAS-Applicationserver (mentioned here), optimized and tuned over the last years.
The current version of the RPC-Environment is 2.1.Downloads:
Server-Binaries and Binaries for a COM- and a DB-DemoClient.VB6-Sources for both of the Demo-Clients, mentioned above.
Here's a screenshot of the RPCServer, hosted on a Linux-machine and currently
"stressed" by two concurrent DB-Clients (one running on XP and one on Linux).
Features:
1.)
One goal of the developement was: simplicity (no mysterious DCOM/COM+
puzzling anymore, regarding SecurityConfig, UserRights, Registration).
Start RPCServer.exe (the RPC-Service-Controller) and from there start
dh_RPCService.exe (either as UserProcess or as Service).
Now start the ClientDemo (RPCDemoClient.exe) from inside the Client-
RPCDirectory - eh voilà - it should connect and work. Now test Cross-
Machine Calls in your LAN. Simply copy both, the Server- and Client-
Folder to the new Host, start the Server there, start the Client and
try to Cross-Connect from both Machines (using the appropriate HostIPs/
HostNames - it should work too. No MS-Management-Console, no DCOMConfig,
no Proxy-/Interface-Creation/Registration.
Just look at the Sources - The Code for wrapping basic SQLite-Read/Write-
functionality needs ca. 50 lines in the server-class (DBServer.Dll) and ca.
30 lines in the clientside Wrapper-Class. We have builtin support for (clientside)
Debugging of the whole RPC-Roundtrip - simply step through all of your Client-
and Server-side Code-Lines. As soon as your Server-Code is stable, you can
compile your Server.Dll and simply put it into the \RPCDlls-Folder in your
Server-Directory (no need to register anything), switch off DebugMode at the
Clientside and you can access your Dll at the "real server".
2.)
As already mentioned above, (and that's not only working in our Demo,
it will work for your ClientCalls and your ServerDlls too), there's no
need, to register Interfaces for our COM-Calls across Machine-Boundaries
(neither at the client-side, nor at the server-side) anymore - we use
LoadLibrary + LoadTypeLibEx + DllGetClassObject, to instantiate from
the Filesystem directly.
Wrapper-Classes at the client-side have to be written by hand, but
they need very small Implementation-Effort (look at the Demo-Source)
and can be Private Classes.
3.)
All Types, that are accepted by a (VB-)Variant-ParamArray can be transported
(the whole bunch of VBs "simple" Types and their Arrays).
Objects (altough not Object-Arrays) are supported too, as long as they
support the IPersistStream-Interface (most interesting in this regard
are ADO-Recordsets, wich allow this "Out-Of-The-Box").
No ANSI-Conversion is performed for String-Types, so we avoid problems
with different locales (the RPC-Requests offer full UniCode-Transport).
Full support for ByRef-Params - non-changed Byref-Params (especially
Arrays of simple Types and Recordsets) are detected by our Serializer
automatically, and do not waste bandwidth regarding their unnecessary
backtransport to the Client.
4.)
Fast transfers using an efficient binary Socket-Protocol, consuming only
one single Port (DCOM uses a complete Port-Range). Port 80 (http) can be
used also, because the server responses to http-Gets and -Posts correctly.
Our ZLib-based Compression-Feature becomes very usefull in Low-Bandwidth-
-Scenarios (Internet-Connections).
Strong-Encryption is also possible at Protocol-Level. We use Diffie-
Hellman-Authentication and avoid man-in-the-middle-attacks per Challenge/
Response using Public-/Private KeyPairs. After the DH-KeyExchange/Auth.
the serialized Packets are encrypted using 320Bit ArcFour-Encryption,
wich is very fast and generates not much Call-Overhead. But if you want
to work over your own VPNs or SSH-Channels, you can simply switch it off.
I've also tested RPCs succesfully in a Putty-initiated (Open-)SSH-Session
per DSL and all was working fine.
5.)
Authorization is possible at RPC-Level (or better: Connection-Level -
for RPC-Level-Impersonation-Switches you would have to reconnect before
each Call with another ServerKnown-Account). This feature allows, to use
server-side resources (NetWork-Shares, SQL-Servers) with the appropriate
Win-User-Rights for each Client-Connect (respective Request) independently.
The Server supports a ForceAuthentication-Switch, so that unauthorized
Connection-Attempts are blocked (disconnected instantly). It is also
possible, to restrict the Windows-Authentication to the Members of a self-
defined Group(name) only.
6.)
TimeOuts at Method-Call-Level. TimeOuts are handled properly, even if
the bandwidth is limited (on slower internet-connections there will be
a packetbased "TimeOut-Retriggering"), so that you can define your TOuts
very narrow to the duration of the pure server-side Method-Call, no need
to take latencies or slow transfers due to low bandwidth into respect.
7.)
The Server is storing requests in a Job-Queue and gives them from there
to a WorkerThread-Pool, wich finally does the processing. The PoolSize
can be changed dynamically whilst the server is running. The Server hardly
terminates a running request (on its WorkerThread) after RPC-Timeout + 0.5
sec without losing stability, a terminated Thread is replaced with a fresh
WorkerThread-Instance.
8.)
Stateful Objects: Altough Objects in the "Server-Layer" normally should
be called in a stateless manner, there are often scenarios, where you
want to "pin" values between requests (complex transactions come to mind,
or the requirements of enhanced session-management), or you simply want to
let a serverside resource opened between the calls (e.g. COM-Ports, etc.).
For this purpose we allow to instantiate two types of Singletons - public
reachable Singletons (X-Thread and X-Process over the ROT per GetObject)
and RPC-internal Singletons, not visible to the outside. These Objects can
be created from your own server-side DLL-Classes. They are instantiated on
separate threads beside the WorkerThread-Pool (also done without using the
registry - as usual here). They allow marshaled "In-Call-Order-Access"
from the "normal" RPCs (wich run on their own WorkerThreads). The COM-
Marshaling takes care, that parallel requests from different WorkerThreads
are queued. But look at our Demo, you will find commented examples there.
************************** Limitations ***************************
Max Client-Connects: 1024
Max ThreadPool-Size: 64
Max Client-Req-Size: 2,000,000 Bytes (see comments in the COM-Demo)
Max Servr-Resp-Size: 4,000,000 Bytes (see comments in the COM-Demo)
Performance:
Performance is very good, the server can process around 5000 small COM-
Requests per second. (measured under heavy load on a "normal" PIV 3.0GHz
Machine without HyperThreading, using our Demo-Client and its String-
Reflection-Loop from multiple ClientMachines in a 100MBit-LAN).
The SMP-Values for a AthlonX2 (2x2000MHz) look even better of course:
7000 simple ComReq/s with a ProcessorLoad of 50% (10000 with 95% Load).
Performance for a single connected Client is around 500 COMReq/sec, so a
simple Calls Roundtrip-Time calculates (as a consequence) to around 2ms.
Combined with dhSQLite (look at the Demos from the Download-Links above) the
Server reaches outstanding Multi-User-Results.
You can expect ca. 120 Responses/sec, serving 'Select * from Orders' or ca. 500 Responses/sec serving 'Select * from Customers' - stresstested from multiple Clients against a "Standard-NWind.db' on a simple Dual-Athlon-CPU (2GHz - SATA 7200/min,
512 MB Ram, 100MBit-LAN, Protocol-Compression and -Encryption enabled).
One additional note:
I've just tested the whole thing (DemoClient-App and Server) on an actual
Linux-Box (Debian-based) with the current Wine-Version 0.935, and there's
absolutely nothing (regarding the RPC-Calls), that doesn't work as expected
(no Server-Crashes, no Memory-Leaking, nothing) - even the ADO-Recordset-
Calls finished succesfully (tested Linux->Linux and XP->Linux scenarios).
So hosting your '*.mdb', your SQLite-DBs (the Server works best with SQLite)
or a "real" SQL-Server like PostGres (wich has its own OleDB-ADOProvider -
Firebird also) together with the server-side Logic on an inexpensive
Linux-based Internet-Hoster becomes possible now.
Linux was faster for Remote-Calls with large Content (not much, but
faster ;-) - probably due to the very good Wine-Translation into the
Linux-Socket-Stack). As expected, it has larger COM-Overhead regarding
Class-Instantiation and Method-Invoking-ByName - so our Stressing-Loop
from different DemoClients (small Com-Calls with very few Data)
sumed up to around 3500 Server-Responses per second under heavy load (where
the XP-Box had 10000). But the more realistic ADO-Recordset-Call was finished
after 8msec (XP 7ms) - so this relativates the Call-Overhead (factor 3) to
some factor 1.2 for usual (DB-)calls.
Despite the COM-Overhead, these are very good values - I was impressed.
Thanks to the Wine-folks, who made this possible!