Introduction
Welcome to the GObject Paranoid Pirate (GPP) documentation.
GPP is a simple GObject implementation of the Paranoid Pirate Pattern, which uses zeromq internally.
GPP is not a zeromq GLib wrapper.
It provides a GPP.Queue object, which relays requests from a GPP.Client to a GPP.Worker, and replies from the worker to the client, as simple strings.
It implements heartbeating, which means that if a worker fails in some way, the client will be able to make its request again, with a per-request retries limit.
Workers are picked on a least-recently-used basis.
One can indifferently instantiate and use all these objects in the same process or in separate ones.
It doesn’t implement client prioritization, this is up to the user.
This documentation is intended as a quick guide and API reference.
GPP.Queue
GPP.Queue routes requests from GPP.Client (s) to GPP.Worker (s).
Set up and start a queue
#include <glib-unix.h>
#include "gpp.h"
static gboolean
interrupted_cb (GMainLoop *loop)
{
g_main_loop_quit (loop);
return FALSE;
}
int main (void)
{
GPPQueue *self = gpp_queue_new ();
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
g_unix_signal_add_full (G_PRIORITY_DEFAULT, SIGINT, (GSourceFunc) interrupted_cb, loop, NULL);
gpp_queue_start (self);
g_main_loop_run (loop);
return 0;
}
It will detect if a worker stopped answering heartbeats, and inform the client it was working for if there was one.
It will pick workers on a least-recently-used basis.
Methods:
GPP.Queue.new
GPPQueue* gpp_queue_new (void);
Returns: the newly-created GPP.Queue.
Create a new GPP.Queue, which doesn’t yet listen to GPP.Worker (s). Start it with GPP.Queue.start
GPP.Queue.start
gboolean gpp_queue_start (GPPQueue* self);
@accepts(GPP.Queue)
@returns(bool)
def start(self):
# Python wrapper for gpp_queue_start()
self: A GPP.Queue to start.
Returns: TRUE if the queue was started, FALSE if it was already.
Makes self start to route requests to available workers, and check worker’s liveness.
GPP.Worker
GPP.Worker receives requests from a GPP.Queue, transmits them as a simple string to the user, and notifies the queue when the user marks the task as done.
Set up and start a simple worker, which will take a lot of time to multiply the request by 2, and sometimes inexplicably fail.
#include <glib-unix.h>
#include "gpp.h"
#define FAILURE_ODDS 4
#define GPP_TYPE_MULTIPLYING_WORKER (gpp_multiplying_worker_get_type ())
G_DECLARE_FINAL_TYPE (GPPMultiplyingWorker, gpp_multiplying_worker, GPP,
MULTIPLYING_WORKER, GPPWorker);
struct _GPPMultiplyingWorker
{
GPPWorker parent;
GRand *rand_source;
gchar *reply;
};
G_DEFINE_TYPE (GPPMultiplyingWorker, gpp_multiplying_worker, GPP_TYPE_WORKER);
static gboolean
set_task_done (GPPMultiplyingWorker * self)
{
g_print ("one task done\n");
if (g_rand_int (self->rand_source) % FAILURE_ODDS == 0) {
g_print ("Actually it didn't work sorry\n");
gpp_worker_set_task_done (GPP_WORKER (self), NULL, FALSE);
} else {
g_print ("no problem !!\n");
gpp_worker_set_task_done (GPP_WORKER (self), self->reply, TRUE);
}
return FALSE;
}
static gboolean
handle_request (GPPWorker * worker, const gchar * request)
{
GPPMultiplyingWorker *self = GPP_MULTIPLYING_WORKER (worker);
g_print ("doing one task, request is %s\n", request);
if (g_rand_int (self->rand_source) % FAILURE_ODDS == 0) {
g_print ("I can't even start\n");
return FALSE;
}
self->reply = g_strdup_printf ("Result : %d", atoi (request) * 2);
g_timeout_add (1000, (GSourceFunc) set_task_done, self);
return TRUE;
}
static void
gpp_multiplying_worker_class_init (GPPMultiplyingWorkerClass * klass)
{
GPPWorkerClass *gpp_worker_class = GPP_WORKER_CLASS (klass);
gpp_worker_class->handle_request = handle_request;
}
static void
gpp_multiplying_worker_init (GPPMultiplyingWorker * self)
{
self->rand_source = g_rand_new ();
}
static gboolean
interrupted_cb (GMainLoop * loop)
{
g_main_loop_quit (loop);
return FALSE;
}
int
main (void)
{
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
GPPMultiplyingWorker *worker =
g_object_new (GPP_TYPE_MULTIPLYING_WORKER, NULL);
g_unix_signal_add_full (G_PRIORITY_HIGH, SIGINT, (GSourceFunc) interrupted_cb,
loop, NULL);
gpp_worker_start (GPP_WORKER (worker));
g_main_loop_run (loop);
g_object_unref (worker);
return 0;
}
Methods:
GPP.Worker.new
GPPWorker* gpp_worker_new (void);
Returns: the newly-created GPP.Worker.
Create a new GPP.Worker, which doesn’t yet listen to request from the GPP.Queue. Start it with GPP.Worker.start
GPP.Worker.start
gboolean gpp_worker_start (GPPWorker* self);
@accepts(GPP.Worker)
@returns(bool)
def start(self):
# Python wrapper for gpp_worker_start()
self: A GPP.Worker that will start handling requests.
Returns: TRUE if self could be started, FALSE if it was already.
This will make self start handling requests.
GPP.Worker.set_task_done
gboolean gpp_worker_set_task_done (GPPWorker* self,
const gchar* reply,
gboolean success);
@accepts(GPP.Worker, unicode, bool)
@returns(bool)
def set_task_done(self, reply, success):
# Python wrapper for gpp_worker_set_task_done()
self: A GPP.Worker.
reply: A string that will be passed to the client.
success: Whether the task was successfully handled.
Returns: TRUE if the task was marked as done, FALSE otherwise.
Call this function when your worker has finished handling a task.
Virtual Methods:
GPP.Worker.do_handle_request
gboolean GPPWorkerClass->handle_request (GPPWorker* self,
const gchar* request);
@accepts(GPP.Worker, unicode)
@returns(bool)
def do_handle_request(self, request):
self: the GPP.Worker
request: The request to handle
Returns: TRUE if the worker can handle that request, FALSE otherwise.
Implement this method to handle requests, requests MUST be handled asynchronously as this method should not block, call GPP.Worker.set_task_done when the request has been handled.
GPP.Client
GPP.Client sends requests to a GPP.Queue, and emits a signal with the possible reply and the status of the task once it has been executed.
A per-request retry limit can be set when calling GPP.Client.send_request
Set up and start a simple client, which will send requests for numbers to be multiplied, and will not take ‘No’ for an answer.
#include <glib-unix.h>
#include "gpp.h"
static gchar *
make_new_task (void)
{
static int sequence = 0;
gchar *task = g_strdup_printf ("%d", sequence);
sequence++;
g_print ("Doing task %s\n", task);
return task;
}
static void
task_done_cb (GPPClient *client, gboolean success, const gchar *reply, gpointer unused)
{
if (!success)
g_print ("task failed\n");
else
g_print ("task succeeded : %s\n", reply);
gpp_client_send_request (client, make_new_task (), -1);
}
static gboolean
interrupted_cb (GMainLoop *loop)
{
g_main_loop_quit (loop);
return FALSE;
}
int main (void)
{
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
GPPClient *client = gpp_client_new ();
g_unix_signal_add_full (G_PRIORITY_HIGH, SIGINT, (GSourceFunc) interrupted_cb, loop, NULL);
g_signal_connect (client, "request-handled", G_CALLBACK (task_done_cb), NULL);
gpp_client_send_request (client, make_new_task(), -1);
g_main_loop_run (loop);
g_object_unref (client);
return 0;
}
Methods:
GPP.Client.new
GPPClient* gpp_client_new (void);
Returns: the newly-created GPP.Client.
Create a new GPP.Client. Use it to send requests with GPP.Client.send_request
GPP.Client.send_request
gboolean gpp_client_send_request (GPPClient* self,
const gchar* request,
gint retries);
@accepts(GPP.Client, unicode, int)
@returns(bool)
def send_request(self, request, retries):
# Python wrapper for gpp_client_send_request()
self: A GPP.Client that will send the request.
request: A simple string that will be passed to the GPP.Worker.
retries: The number of times to retry before signaling that the request was handled, -1 means retry forever.
Returns: TRUE if request was made, FALSE if one is already being made.
This will make self send request to a GPP.Queue.
Signals:
GPP.Client-request-handled
void request-handled (gboolean success,
gchar* reply);
def callback(client, success, reply, user_param1, ...)
success: Whether the request was successfully executed
reply: The reply provided by the GPP.Worker , as a simple string
Connect to this signal to be notified when the current request has been handled.