Package radish is a stateless asynchronous task queue and handler framework. Radish is
designed to maximize the resources of a single node by being able to flexibly increase
and decrease the number of worker go routines that handle tasks. A radish server allows
users to scale the number of workers that can handle generic tasks, add tasks to the
queue, and reports metrics to prometheus for easy tracking and management. Radish also
provides a CLI program for interacting with servers that are running the radish service.
Radish is intended to be used as a framework to create asynchronous task handling
services that do not rely on an intermediate message broker like RabbitMQ or Redis. The
statelessness of Radish makes it much simpler to use, but also does not guarantee fault
tolerance in task handling. It is up to the application using Radish to determine how to
handle task scheduling and timeouts as well as success and failure callbacks. The way
applications do this is by defining tasks handlers that implement the Task interface and
registering them with the radish server. Tasks can then be queued using the Delay method
or by submitting a Queue request to the API server. On success or failure, the worker
will call one of the handlers callback methods then move on to the next task.
A task handler is implemented by defining a struct that implements the Task interface
and registering it with the Radish task queue. Custom tasks must specify a Name method
that uniquely identifies the type of task it is (which is also used when queueing tasks)
as well as a Handle method. The Handle method must accept a uuid, which describes the
future being handled (in case the application wants to implement statefulness) as well
as generic parameters as a byte slice. We have chosen byte for parameters so that
applications can define any serialization format they choose, e.g. json or protobuf.
Task handlers may also implement two callbacks: Success and Failure. Both of these
callbacks take parameters that are specific to those methods and must be provided with
the task being queued. The Failure method will additionally be passed the error that
caused the task to fail.
Once we have defined our custom task handlers, we can register them and begin delaying
tasks for asynchronous processing. If we have two task handlers, SendEmail and
DailyReport whose names are "sendEmail" and "dailyReport" respectively, then the
simplest way we can get started is as follows:
When the task queue is created, it immediately launches workers (1 per CPU on the
machine) to start handling tasks. You can then delay tasks, which will return the unique
id of the future of the task (which you can use for book keeping in success or failure).
In this example, the tasks are submited with an email and an address, but no parameters
for success or failure handling.
More detailed configuration and registration is possible with radish. In the quick start
example we submitted a nil configuration as the first argument to New - this allowed us
to set reasonable defaults for the radish queue. We can configure it more specifically
using the Config object:
The config is validated when it is created and any invalid configurations will return an
error when the queue is created. We can also manually register tasks with the queue (and
register tasks at runtime) as follows:
This allows the queue to be dynamic and handle different tasks at different times. It
is also possible to scale the number of workers at runtime:
The queue can also be scaled and tasks delayed using the Radish service.
Radish implements a gRPC API so that remote clients can connect and get the queue
status, delay tasks, and scale the number of workers. The simplest way to run this
service is as follows:
This wil serve on the address and port specified in the configuration and block until
an interrupt signal is received from the OS, which will shutdown the queue. Applications
can also manually call:
To gracefully shutdown the queue, completing any tasks that are in flight and not
accepting new tasks if they run the listener in its own go routine. Applications that
need to specify their own services using gRPC or http servers can manually run the
service as follows:
The radish CLI command can then be used to access the service and submit tasks.
Radish also serves a metrics endpoint that can be polled by Prometheus. Radish keeps
track of the following metrics associated with the task queue:
Coming soon: If you have your own Prometheus endpoint, you will be able to register
Radish metrics manually without serving them in Radish.
The radish CLI utility is found in `cmd/radish` and can be installed as follows:
This utility allows you to interact with any radish server and can be used to manage
your task queue services out of the box. You can view the commands and options using
the --help flag. In order to connect to a radish server you need to specify options as
This connects radish to a server on port 5356 on the local host without TLS (the -U
stands for "unsecure"). Note that you can also use the $RADISH_ENDPOINT and
$RADISH_UNSECURE environment variables. The misspelling of "unsecure" is a joke, radish
is not insecure it's just not connecting with encryption.
After the connection options are specified you can use a command to interact with the
server. For example to set the number of workers you can use the scale command:
To get the status of the server and the currently registered tasks you can use the
Finally, once you know the names of the tasks that the radish server is handling, you
can queue tasks as follows:
The CLI interface is meant to help you get quickly started with Radish task queues
without having to write your own interfaces or servers.