In 1973, Carl Hewitt had an idea inspired by quantum mechanics. He wanted to develop computing machines that were capable of parallel execution of tasks, communicating with each other seamlessly while containing their own local memory and processors.

Born was the actor model, and with that, a very simple concept: Everything is an actor. This allows for some great benefits: Separating business and other logic is made vastly easier. Security is easily gained because each core component of your application is separate and independent. Prototyping is accelerated due to the nature of actors and their interconnectivity.

What is Pronghorn?

However, what ties it all together is the ability to pass messages between these actors concurrently. An actor responds based on an input message; it can then send back an acknowledgment, deliver content, and designate behaviors to be used for the next time a message gets received. For example, one actor is loading image files from disk while simultaneously streaming chunks to other actors for further processing; i.e., image analysis or conversion. Another actor then takes these as inputs and writes them back to disk or logs them to the terminal. Independently, these actors alone can’t accomplish much—but together, they form an application.

Today there are many implementations of this actor model. At Object Computing, we’ve been working on a highly scalable, performant, and completely open source Java framework called Pronghorn, named after one of the world’s fastest land animals.

Pronghorn, recently released to 1.0, attempts to address a few of the shortcomings of Akka and RxJava, two popular actor frameworks for Java and Scala.

As a result, we developed Pronghorn with a comprehensive list of features in mind:

We wanted to produce as little garbage as possible. Without the Garbage Collector kicking in regularly, it is able to reach performance levels never seen before. We wanted to make sure that Pronghorn has a minimal memory footprint and is mechanical-sympathetic. Built from the ground up with performance in mind, it leverages CPU prefetch functions and caches for fastest possible throughput. Using zero copy direct access, it loads fields from schemas in nanoseconds and never stall cores, while also being non-blocking and lock-free. Pronghorn ensures that you write correct code securely. Through its APIs and contracts, and by using "software fortresses" and industry-leading encryption, Pronghorn lets you build applications that are secure and that fail safely. Debugging and testing can be stressful and annoying, especially when you need to hit a deadline. Pronghorn easily integrates with common testing frameworks and simplifies refactoring and debugging through its automatically generated and live-updating telemetry graph, fuzz testing (in work) based on existing message schemas, and warnings when certain actors are misbehaving or consuming too many resources. This helps you rapidly prototype and spend more time focusing on your business needs.

For more details, visit the Pronghorn Features list.

Why Pronghorn?

Writing concurrent and performant applications has never been easy, and we don’t promise to solve the problems entirely. However, to give you an idea of the benefits of Pronghorn and the power of its API, we wrote a small HTTP REST server and benchmarked it against common industry standards such as Node & Express and Tomcat & Spring Boot:

We encourage you to run these numbers yourself, share your results, and add your own web server.

As you can see, Pronghorn does exceptionally well in this REST example. While almost being 10x faster than conventional solutions, Pronghorn could help cut server costs (such as EC2 or Azure) in half or more through its garbage-free, statically-typed backend. HTTP requests can be parsed, and responses are generated while actors are working concurrently. The scheduling and threading are automatically handled by Pronghorn's powerful default scheduler.

As mentioned above, Pronghorn allows you to rapidly prototype and envision your project, generally by following three basic steps:

Define your data flow graph

This is a crucial first step. Pronghorn takes a data-first approach; processing large volumes of data rapidly. In your application, think about the type of data that should flow through the "pipes"—for example, if you’re building an image analysis tool, you will need actors to read, write, and analyze image files. The format of the data between actors needs also to be established; it could be schemas containing JPG MCUs or raw binary BMP files. Pick the format that works best for your application.

Define the contracts between each stage

Contracts allow you to easily define your messages using FAST, a proven protocol used by the finance industry for stock trading. These contracts are used in the testing phase to ensure implementation aligns with your message field definitions. This is a contractual approach; it must be respected for actors to communicate with each other.

Test first development by using generative testing as the graph is implemented

Schemas are code-generated for you as you develop your application. Test-driven development allows for correct and safe code, saving valuable time as you head towards release. As your program grows, the graph grows as well, describing every single interaction between actors and illustrating your message data flow on pipes between stages. Through its automatically telemetry, you can easily keep track of even the most complex applications, as shown below:

What does it look like?

You may be curious about what Pronghorn code looks like. Below is some sample code for generating the message schemas in our "Hello World" example.

To define a message, create a new XML file similar to this:

<?xml version="1.0" encoding="UTF-8"?>

<templates xmlns="http://www.fixprotocol.org/ns/fast/td/1.1">

<template name="HelloWorldMessage" id="1">

<string name="GreetingName" id="100" charset="unicode"/>

</template>

</templates>

This schema will then be used by the stages described in the Hello World example. Populating a graph in your application using this schema is even easier:

private static void populateGraph(GraphManager gm) {

Pipe<HelloWorldSchema> messagePipe =

HelloWorldSchema.instance.newPipe(10, 10_000);

new GreeterStage(gm, "Jon Snow", messagePipe);

new GuestStage(gm, messagePipe);

}

This uses the stages created in the Hello World tutorial.

We use a Maven archetype to provide you with everything you need to start building Pronghorn applications.

Start using Pronghorn

We hope this article has offered a taste of how Pronghorn can help you write performant, efficient, and secure applications in Java using Pronghorn, an alternative to Akka and RXJava. We’d love your feedback on how to make this an ideal platform for developers, managers, CFOs, and others.

Resources