NAME
Patro - proxy access to remote objects
VERSION
0.10
SYNOPSIS
# on machine 1 (server)
use Patro;
my $obj = ...
$config = patronize($obj);
open my $fh, '>config_file'; print $fh $config; close $fh;
# on machines 2 through n (clients)
use Patro;
open my $fh, '<config_file'; my $config=<$fh>; close $fh;
my ($proxy) = Patro->new($config)->getProxies;
...
$proxy->{key} = $val; # updates $obj->{key} for obj on server
$val = $proxy->method(@args); # calls $obj->method for obj on server
DESCRIPTION
Patro
is a mechanism for making any Perl reference in one Perl program accessible is other processes, even processes running on different hosts. The "proxy" references have the same look and feel as the native references in the original process, and any manipulation of the proxy reference will have an effect on the original reference.
Some important features:
Hash members and array elements
Accessing or updating hash values or array values on a remote reference is done with the same syntax as with the local reference:
# host 1 use Patro; my $hash1 = { abc => 123, def => [ 456, { ghi => "jkl" }, "mno" ] }; my $config = patronize($hash1); ... # host 2 use Patro; my $hash2 = Patro->new($config)->getProxies; print $hash2->{abc}; # "123" $hash2->{def}[2] = "pqr"; # updates $hash1 on host 1 print delete $hash2->{def}[1]{ghi}; # "jkl", updates $hash1 on host1
Remote method calls
Method calls on the proxy object are propagated to the original object, affecting the remote object and returning the result of the call.
# host 1 use Patro; sub Foofie::new { bless \$_[1],'Foofie' } sub Foofie::blerp { my $self=shift; wantarray ? (5,6,7,$$self) : ++$$self } $config = patronize(Foofie->new(17)); ... # host 2 use Patro; my $foo = Patro->new($config)->getProxies; my @x = $foo->blerp; # (5,6,7,17) my $x = $foo->blerp; # 18
Overloaded operators
Any overloaded operations on the original object are supported on the remote object.
# host 1 use Patro; my $obj = Barfie->new(2,5); $config = patronize($obj); package Barfie; use overload '+=' => sub { $_ += $_[1] for @{$_[0]->{vals}};$_[0] }, fallback => 1; sub new { my $pkg = shift; bless { vals => [ @_ ] }, $pkg; } sub prod { my $self = shift; my $z=1; $z*=$_ for @{$_[0]->{vals}}; $z } # host 2 use Patro;' my $proxy = getProxies($config); print $proxy->prod; # calls Barfie::prod($obj) on host1, 2 * 5 => 10 $proxy += 4; # calls Barfie '+=' sub on host1 print $proxy->prod; # 6 * 9 => 54
FUNCTIONS
patronize
CONFIG = patronize(@REFS)
Creates a server on the local machine that provides proxy access to the given list of references. It returns a string (some `Data::Dumper` output) with information about how to connect to the server. The output can be used as input to the `getProxies()` function to retrieve proxies to the shared references.
getProxies
PROXIES = getProxies(CONFIG)
Connects to a server on another machine, specified in the CONFIG
string, and returns proxies to the list of references that are served. In scalar context, returns a proxy to the first reference that is served.
See the "PROXIES" section below for what you can do with the output of this function.
ref
TYPE = Patro::ref(PROXY)
For the given proxy object, returns the ref type of the remote object being served. If the input is not a proxy, returns undef
. See also "reftype".
reftype
TYPE = Patro::reftype(PROXY)
Returns the simple reference type (e.g., ARRAY
) of the remote object associated with the given proxy, as if we were calling Scalar::Util::reftype
on the remote object. Returns undef
if the input is not a proxy object.
client
CLIENT = Patro::client(PROXY)
Returns the IPC client object used by the given proxy to communicate with the remote object server. The client object contains information about how to communicate with the server and other connection configuration.
PROXIES
Proxy references, as returned by the "getProxies" function above, or sometimes returned in other calls to the server, are designed to look and feel as much as possible as the real references on the remote server that they provide access to, so any operation or expression with the proxy on the local machine should evaluate to the same value(s) as the same operation or expression with the real object/reference on the remote server. When the server if using threads and is sharing the served objects between threads, an update to the proxy object will affect the remote object, and vice versa.
Example 1: network file synchronization
Network file systems are notoriously flaky when it comes to synchronizing files that are being written to by processes on many different hosts [citation needed]. Patro
provides a workaround, in that every machine can hold to a proxy to an object that writes to a file, with the object running on a single machine.
# master
package SyncWriter;
use Fcntl qw(:flock SEEK_END);
sub new {
my ($pkg,$filename) = @_;
open my $fh, '>', $filename;
bless { fh => $fh }, $pkg;
}
sub print {
my $self = shift;
flock $self->{fh}, LOCK_EX;
seek $self->{fh}, 0, SEEK_END;
print {$self->{fh}} @_;
flock $self->{fh}, LOCK_UN;
}
sub printf { ... }
use Patro;
my $writer = SyncWriter->new("file.log");
my $cfg = patronize($writer);
open my $fh,'>','/network/accessible/file';
print $fh $cfg;
close $fh;
...
# slaves
use Patro;
open my $fh, '<', '/network/accessible/file';
my $cfg = <$fh>;
close $fh;
my $writer = Patro->new($cfg)->getProxies;
...
# $writer->print with a proxy $writer
# invokes $writer->print on the host. Since all
# the file operations are done on a single machine,
# there are no network synchronization issues
$writer->print("a log message\n");
...
Example 2: Distributed queue
A program that distributes tasks to several threads or several child processes can be extended to distribute tasks to several machines.
# master
use Patro;
my $queue = [ 'job 1', 'job 2', ... ];
open my $fh, '>', '/network/accessible/file';
print $fh patronize($queue);
close $fh;
...
# slaves
use Patro;
open my $fh, '<', '/network/accessible/file';
my $queue = Patro->new(<$fh>)->getProxies;
close $fh;
while (my $task = shift @$queue) {
... do task ...
}
(This example will not work without threads. For a more robust network-safe queue that will run with forks, see Forks::Queue)
Example 3: Keep your code secret
If you distribute your Perl code for others to use, it is very difficult to keep others from being able to see (and potentially steal) your code. Obfuscators are penetrable by any determined reverse engineer. Most other suggestions for keeping your code secret revolve around running your code on a server, and having your clients send input and receive output through a network service.
The Patro
framework can make this service model easier to use. Design a small set of objects that can execute your code, provide your clients with a public API for those objects, and make proxies to your objects available through Patro
.
# code to run on client machine
use Patro;
my $cfg = ... # provided by you
my ($obj1,$obj2) = Patro->new($cfg)->getProxies;
$result = $obj1->method($arg1,$arg2);
...
In this model, the client can use the objects and methods of your code, and inspect the members of your objects through the proxies, but the client cannot see the source code.
ENVIRONMENT
Patro
pays attention to the following environment variables.
PATRO_THREADS
If the environment variable PATRO_THREADS
is set, Patro
will use it to determine whether to use a forked server or a threaded server to provide proxy access to objects. If this variable is not set, Patro
will use threads if the threads module can be loaded.
LICENSE AND COPYRIGHT
MIT License
Copyright (c) 2017, Marty O'Brien
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.