ActiveJ Overview. New Full-stack Java Framework

Share
  • November 26, 2020

What is ActiveJ?

ActiveJ is a brand-new full-stack Java framework that can be used in diverse web, cloud, and microservices high-load scenarios.

ActiveJ is a natively high-performance solution. According to the ActiveJ website, it was originally developed from the ground up as a solution for an RTB advertising platform that processes more than 100 billion inbound requests daily.

“The idea behind ActiveJ is to revolutionize the Java industry with ground-breaking technologies.”

To match this idea, ActiveJ is not only extremely efficient but also provides an alternative approach towards development. It has minimal third-party dependencies, which empowers high performance, consistency and streamlined development process. ActiveJ brings the application business logic over framework specifications and limitations.

Let’s overview the core components of the framework!

SEE ALSO: JavaFX 15: “The cross-platform capabilities of JavaFX should not be underestimated”

ActiveJ Components

ActiveJ has a set of loosely-coupled components that cover the full application stack. Some of the components have no dependencies and can be used as stand-alone libraries.

Asynchronous core

ActiveJ has its own high-performance asynchronous I/O core: Eventloop, Promise, and Net. It also features asynchronous data stream processing: CSP and Datastream.

ActiveInject

Fast and lightweight dependency injection library. It is multi-thread friendly, feature-rich, and can boast lightning-fast start-up time and runtime. It provides support for nested scopes, singletons and transient bindings, modules, programmatic binding generation, introspections of dependencies graphs, transformations, automatic generation of missing bindings, and modification of the already existing bindings. It has no third-party dependencies and can be used independently of the ActiveJ platform. You can find some examples of its usage on ActiveInject’s website.

ActiveJ HTTP

High-performance async HTTP client and server. According to the benchmarks, in some use cases, it is 15% faster than multi-threaded Vert.x HTTP server, even on a single core and with 50% less overall CPU load. The full size of minimal ActiveJ web application with an embedded HTTP server is only 1.4MB with launch time of only 0.65 sec, compared to 17MB and 14 sec for Spring.

Let’s create a simple server using the HTTP component. It features a predefined HTTP client and server implementations with significant performance. We’ll also use the ActiveJ Launcher module. Launcher is a highly-generalized abstraction over the main method that takes care of application lifecycle, dependencies, and logging.

public final class HttpHelloWorldExample extends HttpServerLauncher {
    @Provides
    AsyncServlet servlet() {
         return request -> HttpResponse.ok200().withPlainText("Hello World!");
    }

    public static void main(String[] args) throws Exception {
        Launcher launcher = new HttpHelloWorldExample();
        launcher.launch(args);
    }
}

We’ve extended the HttpServerLauncher class from the Launcher module.

ActiveInject @Provides annotation creates an AsyncServlet. It asynchronously receives an HTTP request from client and sends an HTTP response. To start the server, just use the Launcher.launch method, and that’s it. Go to localhost:8080 to check your server out. Thanks to ActiveInject, you can easily extend the example to make it more complex.

You can find the source code of this example on GitHub or find out more in ActiveJ HTTP docs.

ActiveCodegen

Dynamic bytecode generator that encapsulates the complexity of direct bytecode manipulations with a streamlined and concise API. It compiles Lisp-like AST tree expressions directly into bytecode, using automatic type inference with essentially zero overhead. Here’s a small example of implementing sayHello method of the Example interface.

Class<Example> example = ClassBuilder 
// DefiningClassLoader represents a loader for defining dynamically generated classes    	.create(DefiningClassLoader.create(Thread.currentThread().getContextClassLoader()), Example.class)
	.withMethod("sayHello", call(staticField(System.class, "out"), "println", value("Hello world")))
	.build();
Example instance = example.getDeclaredConstructor().newInstance();
instance.sayHello();

ActiveSerializer

ActiveSerializer is implemented using ActiveCodegen and introduces a schema-less approach for the best performance of serialization process. It features a full support of Java sub-classes, collections (including graphs with circular references and also specialized collections like HPPC). Moreover, it supports plugins, extensions, versioning, can be fine-tuned using Java annotations, and much more. According to the benchmarks on ActiveSerializer website, it’s the fastest JVM-based serializer in the world. Here’s a small example of its use case:

public static class Person {
    public Person(@Deserialize("age") int age,
                  @Deserialize("name") String name) {
        this.age = age;
        this.name = name;
    }

    @Serialize(order = 0)
    public int age;

    @Serialize(order = 1)
    public final String name;
}

You can find more examples on GitHub or in ActiveSerializer docs. ActiveSerializer can be used independently of the framework as a stand-alone library.

ActiveSpecializer

A unique technology that automatically speeds up your code by rewriting the bytecode in runtime. Unlike traditional compiler optimization tools, it uses the runtime information of classes instances: all class fields are transformed into static class fields, and all virtual methods calls are devirtualized and replaced with static method calls. According to the benchmarks, in some use cases, ActiveSpecializer can make your code up to 7 times faster.

For example, let’s see how a typical AST expressions tree f(x) = ((x + 5) – 5) * (-1) can be optimized with ActiveSpecializer. The expression will look as follows:

static IntUnaryOperator INT_UNARY_OPERATOR =
    new IntUnaryOperatorProduct(
        new IntUnaryOperatorSum(
            new IntUnaryOperatorSum(
                 new IntUnaryOperatorIdentity(),
                 new IntUnaryOperatorConst(5)),
            new IntUnaryOperatorConst(-5)),
         new IntUnaryOperatorConst(-1));

//And it is equivalent to this manually optimized equation:
static IntUnaryOperator INT_UNARY_OPERATOR_OPTIMIZED_MANUALLY =
    new IntUnaryOperator() {
        @Override
        public int applyAsInt(int x) {
            return -x;
        }
    };

Let’s see how ActiveSpecializer will automatically cope with the first expression:

static IntUnaryOperator INT_UNARY_OPERATOR_OPTIMIZED_AUTOMATICALLY =
    SPECIALIZER.specialize(INT_UNARY_OPERATOR);

According to the benchmarks, the original equation was operated in 69.938 ns, while the manually and automatically specialized took only 26.533 ns and 26.691 ns respectively.

Yet, ActiveSpecializer goes far beyond the arithmetical equations. You can find ActiveSpecializer examples on GitHub or in ActiveSpecializer docs. It can be used independently of the framework as a stand-alone library.

ActiveRPC

A lightning-fast binary protocol for developing distributed applications and microservices solutions. In order to minimize the overheads, ActiveRPC doesn’t use HTTP with JSON or XML encoding. Instead, it is powered by lightning-fast ActiveSerializer, runs on TCP, and has a custom high-performance binary streaming protocol.

ActiveRPC features highly optimized server and client implementations along with predefined cloud strategies that help to manage requests arrangement between servers or shards of servers. These strategies include first available, round-robin, rendezvous hashing, etc. You can combine the strategies. For example, let’s use the first available and round-robin strategies to manage a pool of 4 connections:

RpcStrategy strategy = roundRobin(
    firstAvailable(servers(ADDRESS_1, ADDRESS_2)),
    firstAvailable(servers(ADDRESS_3, ADDRESS_4)));

You can change the amount of connections, apply different strategies, and your application will be able to handle high load scenarios natively:

RpcStrategy strategy = rendezvousHashing(hashFunction)
    .withShard(1, firstAvailable(servers(ADDRESS_1, ADDRESS_2)))
    .withShard(2, firstAvailable(servers(ADDRESS_3, ADDRESS_4)))
    .withShard(3, server(ADDRESS_5));

You can find ActiveRPC examples on GitHub or in ActiveRPC docs.

SEE ALSO: Type-safe Unit Expressions for Java (and you)

ActiveFS

Provides a tiny asynchronous abstraction with upload, download, append, list, copy, move, delete, and other methods for operating with local, remote, or distributed storage. It features a simple FTP-like protocol with zero-overhead streaming and supports data redundancy, rebalancing, and resharding.

private RemoteFsClusterClient client;

    ...

Eventloop eventloop = Eventloop.create();

Map<Object, FsClient> clients = ...;

client = RemoteFsClusterClient.create(eventloop, clients)
    .withReplicationCount(N / 2)
    .withServerSelector(RENDEZVOUS_HASH_SHARDER);

Specialized databases

ActiveJ features a list of specialized databases implementations like Operational Transformation database, CRDT server database, and OLAP database.

Utils

Includes application bootstrapping, lifecycle, and management components like Launcher, Service Graph, JMX, Triggers.

Conclusions

ActiveJ is a radically new development platform for creating diverse modern applications of any complexity with a lightweight and natively scalable async architecture that squeezes the last bits of performance from your hardware.

You can explore ActiveJ source code on the GitHub repository. Documentation and Examples on ActiveJ can be found on the official website.

The post ActiveJ Overview. New Full-stack Java Framework appeared first on JAXenter.

Source : JAXenter