Software Development

Microservices for Java Developers: The Java / JVM Landscape

1. Introduction

In the previous part of the tutorial we have covered a broad range of communication styles widely used while building microservices. It is time to put this knowledge into practical perspective by talking about most popular and battle-tested Java libraries and frameworks which may serve as the foundation of your microservice architecture implementation.

Although there are quite a few old enough to remember the SOAP era, many of the frameworks we are going to discuss shortly are fairly young, and often quite opinionated. The choice of which one is right for you is probably the most important decision you are going to make early on. Beside the JAX-RS specification (and more generic Servlet specification), there are no industry-wide standards to guarantee the interoperability between different frameworks and libraries on JVM platform, so make the call wisely.F3.1

There will not be any comparison to promote one framework or library over another since each has own goals, philosophy, community, release cycles, roadmaps, integrations, scalability and performance characteristics. There are just too many factors to account for, taking into the context the application and organization specifics.

However, a couple of valuable resources could be of a great help. The Awesome Microservices repository is a terrific curated list of microservice architecture related principles and technologies. In the same vein, the TechEmpower’s Web Framework Benchmarks provides a number of interesting and useful insights regarding the performance of several web application platforms and frameworks, and not only the JVMs ones.

2. Staying RESTy

The most crowded space is occupied by the frameworks and libraries which promote REST architectural style over HTTP protocol. Nearly all the nominees in this category are very matured and well-established brands, deployed in productions for years.

2.1. JAX-RS: RESTful Java in the Enterprise

The JAX-RS specification, also known as JSR-370 (and previously outlined in the JSR-339 and JSR-311), defines a set of Java APIs for the development of web services built according to the REST architectural style. It is fairly successful effort with many implementations available to select from and, arguably, the number one preference in the enterprise world.

The JAX-RS APIs are driven by Java annotations and generally could be ported from one framework to another quite smoothly. In addition, there is tight integration with other Java platform specifications, like Contexts and Dependency Injection for Java (JSR-365), Bean Validation (JSR-380), Java API for JSON Processing (JSR-374) to name a few.

Getting back to the imaginable library management web APIs we have talked about in the previous part of the tutorial, the typical (but very simplified) JAX-RS web service implementation may look like this:

@Path("/library")
public class LibraryRestService {
    @Inject private LibraryService libraryService;

    @GET
    @Path("/books")
    @Produces(MediaType.APPLICATION_JSON)
    public Collection<Book> getAll() {
        return libraryService.getBooks();
    }
    
    @POST
    @Path("/books")
    @Produces(MediaType.APPLICATION_JSON)
    public Response addBook(@Context UriInfo uriInfo, Book payload) {
        final Book book = libraryService.addBook(payload);
        
        return Response
            .created(
                uriInfo
                    .getRequestUriBuilder()
                    .path(book.getIsbn())
                    .build())
            .entity(book)
            .build();    }
    
    @GET
    @Path("/books/{isbn}")
    @Produces(MediaType.APPLICATION_JSON)
    public Book findBook(@PathParam("isbn") String isbn) {
        return libraryService
            .findBook(isbn)
            .orElseThrow(() -> new NotFoundException("No book found for ISBN: " + isbn));
    }
}

It should work on any framework which is fully compliant with the latest JAX-RS 2.1 specification (JSR-370) so let us take it from there.

2.2. Apache CXF

Apache CXF, an open source services framework, celebrates its 10th anniversary this year! Apache CXF helps to build and develop services using frontend programming APIs, like JAX-WS and JAX-RS, which can speak a variety of protocols such as SOAP, REST, SSE (even CORBA) and work over a variety of transports such as  HTTP, JMS or JBI.

What makes Apache CXF a great fit for microservices is the fact that it has outstanding integrations with many other projects, notably: OpenAPI for contract-driven development, Brave / OpenTracing / Apache HTrace for distributed tracing, JOSE / OAuth2 / OpenID Connect for security.

2.3. Apache Meecrowave

Meecrowave is a very lightweight, easy to use microservices framework, built exclusively on top of other great Apache projects: Apache OpenWebBeans (CDI 2.0), Apache CXF (JAX-RS 2.1), and Apache Johnzon (JSON-P). It significantly reduces the development time since all the necessary pieces are already wired together.

2.4. RESTEasy

RESTEasy from RedHat / JBoss is another fully certified and portable implementation of the JAX-RS 2.1 specification. One of the advantages of the RESTEasy framework is tighter integration with WildFly Application Server, in case it might be important in your context.

2.5. Jersey

Jersey is an open source, production quality framework for developing RESTful web services in Java. In fact, it serves as a JAX-RS (JSR-370, JSR-339 and JSR-311) reference implementation. Similarly to other frameworks, Jersey goes way beyond just being JAX-RS implementation and provides additional features, extensions and utilities to further simplify the development of RESTful web APIs and clients.

2.6. Dropwizard

Dropwizard is yet another great Java framework for developing RESTful web services and APIs with the emphasis on the operational friendliness and high performance. It is built on top of Jersey framework and combines together best of breed libraries from the entire Java ecosystem. In that sense, it is quite opinionated, but at the same time known to be simple, stable, mature and light-weight.

Dropwizard has out-of-the-box support for sophisticated configuration, application metrics, logging, operational tools, and much more, allowing you and your team to ship a production-quality web service in the shortest time possible. – https://www.dropwizard.io/1.3.5/docs

It is worth noting that Dropwizard truly established the instrumentation and monitoring baseline for modern Java applications (you may have heard about Metrics library born from it) and is really good choice for building microservices.

2.7. Eclipse Microprofile: thinking in microservices from the get-go

While talking about microservices in Java universe it is impossible not to mention very recent initiative undertaken by Eclipse Foundation, known as MicroProfile.

The MicroProfile is a baseline platform definition that optimizes Enterprise Java for a microservices architecture and delivers application portability across multiple MicroProfile runtimes. The initially planned baseline is JAX-RS + CDI + JSON-P, with the intent of community having an active role in the MicroProfile definition and roadmap. – https://microprofile.io/faq

The primary goal of MicroProfile is to accelerate the pace of innovation in the space of the enterprise Java, which traditionally is suffering from very slow processes. It is certainly worth keeping an eye on.

2.8. Spring WebMvc / WebFlux

Long time ago Spring Framework, initially the implementation of the inversion of control (IoC) design principle, had shaken the world of the enterprise Java, filled with monstrous frameworks and specifications.  It provided an easy and straightforward way to use dependency injection capabilities in Java applications.

The IoC ­­­and dependency injection are still at the core of Spring Framework but there are so many projects under its umbrella that nowadays it is more like a platform than anything else. As for this section, the one we are interested in is Spring Web MVC, the original web framework built on the Servlet API and widely used for traditional RESTful web services, like our library management APIs for example.

@RestController
@RequestMapping("/library")
public class LibraryController {
    @Autowired private LibraryService libraryService;
    
    @RequestMapping(path = "/books/{isbn}", method = GET, produces = APPLICATION_JSON_VALUE)
    public ResponseEntity<Book> findBook(@PathVariable String isbn) {
        return libraryService
            .findBook(isbn)
            .map(ResponseEntity::ok)
            .orElseGet(ResponseEntity.notFound()::build);
    }
    
    @RequestMapping(path = "/books", method = GET, produces = APPLICATION_JSON_VALUE)
    public Collection<Book> getAll() {
        return libraryService.getBooks();
    }

    @RequestMapping(path = "/books", method = POST, consumes = APPLICATION_JSON_VALUE)
    public ResponseEntity<Book> addBook(@RequestBody Book payload) {
        final Book book = libraryService.addBook(payload);
        
        return ResponseEntity
            .created(linkTo(methodOn(LibraryController.class).findBook(book.getIsbn())).toUri())
            .body(book);
    }
}

It would be unfair not to mention a relatively new Spring WebFlux project, the counterpart of the Spring Web MVC, built on top of the reactive stack and non-blocking I/O.

Innovative, productive and hugely successful, Spring Framework is the number one choice for Java developers these days, particularly with respect to microservice architecture.

2.9. Spark Java

Spark, also often referred as Spark-Java to eliminate the confusion with similarly named hyper-popular data processing framework, is a micro framework for creating web applications in Java with a minimal effort. It is heavily relying on its expressive and simple DSL, designed by leveraging the power of Java 8 lambda expressions. It indeed leads to quite compact and clean code. For example, here is a sneak peak on our library management APIs definition:

path("/library", () -> {
    get("/books", 
        (req, res) -> libraryService.getBooks(),
        json()
    );
            
    get("/books/:isbn", 
        (req, res) -> libraryService
            .findBook(req.params(":isbn"))
            .orElseGet(() -> {
                res.status(404);
                return null;
            }), 
        json()
    );
            
    post("/books", 
        (req, res) -> libraryService
            .addBook(JsonbBuilder.create().fromJson(req.body(), Book.class)),
        json()
    );
});

Beside just Java, Spark-Java has first class Kotlin support and although it is mainly used for creating REST APIs, it integrates with a multitude of template engines.

2.10. Restlet

Restlet framework aims to help Java developers build better web APIs that follow REST architectural style. It provides pretty powerful routing and filtering capabilities along with offering numerous extensions. It has quite unique way to structure and bundle things together.

public class BookResource extends ServerResource {
    @Inject private LibraryService libraryService;
    
    @Get
    public Book findBook() {
        final String isbn = (String) getRequest().getAttributes().get("isbn");
        return libraryService.findBook(isbn).orElse(null);
    }
}

public class BooksResource extends ServerResource  {
    @Inject private LibraryService libraryService;
    
    @Get
    public Collection<Book> getAll() {
        return libraryService.getBooks();
    }
    
    @Post("json")
    public Representation addBook(Book payload) throws IOException {
        return toRepresentation(libraryService.addBook(payload));
    }
}

And here are the bindings between the resources and their URI, basically the typical router.

final Router router = new Router(getContext());
router.attach("/library/books/{isbn}", BookResource.class);
router.attach("/library/books", BooksResource.class);

Interestingly, Restlet is one of the few open-source frameworks which was able to grew up from a simple library to a full-fledged RESTful APIs development platform.

2.11. Vert.x

The Vert.x project from Eclipse Foundation is an open-source toolkit for building reactive applications on the JVM platform. It follows the reactive paradigm and is designed from the ground up to be event-driven and non-blocking.

It has tons of different components. One of them is Vert.x-Web, designated for writing sophisticated modern web applications and HTTP-based microservices. The snippet below showcases our library management web API implemented on top of Vert.x-Web.

final LibraryService libraryService = ...;

final Vertx vertx = Vertx.vertx();
final HttpServer server = vertx.createHttpServer();
final Router router = Router.router(vertx);
        
router
    .get("/library/books")
    .produces("application/json")
    .handler(context ->
        context
            .response()
            .putHeader("Content-Type", "application/json")
            .end(Json.encodePrettily(libraryService.getBooks()))
    );
            
router
    .get("/library/books/:isbn")
    .produces("application/json")
    .handler(context ->
        libraryService
            .findBook(context.request().getParam("isbn"))
            .ifPresentOrElse(
                book -> context
                        .response()
                        .putHeader("Content-Type", "application/json")
                        .end(Json.encodePrettily(book)),
                () -> context
                        .response()
                        .setStatusCode(204)
                        .putHeader("Content-Type", "application/json")
                        .end()
            )
    );
        
router
    .post("/library/books")
    .consumes("application/json")
    .produces("application/json")
    .handler(BodyHandler.create())
    .handler(context -> {
        final Book book = libraryService
            .addBook(context.getBodyAsJson().mapTo(Book.class));
                
        context
            .response()
            .putHeader("Content-Type", "application/json")
            .end(Json.encodePrettily(book));
    });
        
server.requestHandler(router::accept);
server.listen(4567);

Some of the notable characteristics of the Vert.x are high performance and modular design. More to that, it is pretty lightweight, scales really well and natively supports Java, JavaScript, Groovy, Ruby, Ceylon, Scala and Kotlin.

2.12. Play Framework

Play is high velocity, hyper-productive web framework for Java and Scala. It is based on a lightweight, stateless, web-friendly architecture and is built on top of Akka Toolkit. Although Play is full-fledged framework with exceptionally powerful templating engine, it is very well suited for RESTful web services development as well. Our library management web APIs could be easily designed in Play.

GET    /library/books           controllers.LibraryController.list
GET    /library/books/:isbn     controllers.LibraryController.show(isbn: String)
POST   /library/books           controllers.LibraryController.add
public class LibraryController extends Controller {
    private LibraryService libraryService;

    @Inject
    public LibraryController(LibraryService libraryService) {
        this.libraryService = libraryService;
    }
    
    
    public Result list() {
        return ok(Json.toJson(libraryService.getBooks()));
    }

    public Result show(String isbn) {
        return  libraryService
            .findBook(isbn)
            .map(resource -> ok(Json.toJson(resource)))
            .orElseGet(() -> notFound());
    }
    
    public Result add() {
        JsonNode json = request().body().asJson();
        final Book book = Json.fromJson(json, Book.class);
        return created(Json.toJson(libraryService.addBook(book)));
    }
}

The ability of Play to serve both backend (RESTful) and frontend (using for example Angular, React, Vue.js) endpoints, in other words going full-stack,  might be an attractive offering with respect to implementing microservices using a single framework (although such decisions should be taken with a great care).

2.13. Akka HTTP

Akka HTTP, the part of amazing Akka Toolkit family, provides a full server-side and client-side HTTP stack implemented on top of actor model. It’s not a full-fledged web framework (like Play, for example) but crafted specifically to manage HTTP-based services. Similarly to other frameworks, Akka HTTP has own DSL to elegantly define RESTful web API endpoints. The best way to give it a try is to create our library management API definitions.

public class LibraryRoutes extends AllDirectives {
    private final LibraryService libraryService;

    // ...    
    
    public Route routes() {
        return route(
            pathPrefix("library", () ->
                pathPrefix("books", () ->
                    route(
                        pathEndOrSingleSlash(() ->
                            route(
                                get(() ->
                                    complete(StatusCodes.OK, libraryService.getBooks(), Jackson.marshaller())
                                ),
                                post(() ->
                                    entity(
                                        Jackson.unmarshaller(Book.class),
                                        payload -> complete(StatusCodes.OK, libraryService.addBook(payload), Jackson.marshaller())
                                    )
                                )
                            )
                        ),
                        path(PathMatchers.segment(), isbn -> 
                            route(
                                get(() -> 
                                    libraryService
                                        .findBook(isbn)
                                        .map(book -> (Route)complete(StatusCodes.OK, book, Jackson.marshaller()))
                                        .orElseGet(() -> complete(StatusCodes.NOT_FOUND))
                                )
                            )
                        )
                    )
                )
            )
        );
    }
}

Akka HTTP has first-class support of Java and Scala and is an excellent choice for building RESTful web services and scalable microservice architecture in general.

2.14. Micronaut

Micronaut is a modern, JVM-based, full-stack framework for building modular, easily testable microservice applications. It is truly polyglot (in JVM sense) and offers support for Java, Groovy, and Kotlin out of the box.

Micronaut’s focus is a first-class support of reactive programming paradigm and compile-time dependency injection. Here is the skeleton of our library management web APIs declaration in Micronaut.

@Controller("/library")
public class LibraryController {
    @Inject private LibraryService libraryService;
    
    @Get("/books/{isbn}")
    @Produces(MediaType.APPLICATION_JSON)
    public Optional<Book> findBook(String isbn) {
        return libraryService.findBook(isbn);
    }
    
    @Get("/books")
    @Produces(MediaType.APPLICATION_JSON)
    public Observable<Book> getAll() {
        return Observable.fromIterable(libraryService.getBooks());
    }
    
    @Post("/books")
    @Consumes(MediaType.APPLICATION_JSON)
    public HttpResponse<Book> addBook(Book payload) {
        return HttpResponse.created(libraryService.addBook(payload));
    }
}

Comparing to others, Micronaut is very young but promising framework, focused on modern programming. It is a fresh start without the baggage accumulated over the years.

3. GraphQL, the New Force

The popularity of the GraphQL is slowly but steadily growing. It has proven to be an indispensable tool to address a wide range of the problems although there are not many choices available for Java developers. Along this section we are going to use the same GraphQL schema we have discussed in the previous part of the tutorial, repeated below just for convenience.

schema {
  query: Query
  mutation: Mutation
}

type Book {
  isbn: ID!
  title: String!
  year: Int
}

type Query {
  books: [Book]
  book(isbn: ID!): Book
}

type Mutation {
  addBook(isbn: ID!, title: String!, year: Int): Book
  updateBook(isbn: ID!, title: String, year: Int): Book
  removeBook(isbn: ID!): Boolean
}


 

3.1. Sangria

Sangria is the GraphQL implementation in Scala. This is a terrific framework with a vibrant community and seamless integration with Akka HTTP and/or Play Framework. Although it does not provide Java-friendly APIs at the moment, it is worth mentioning nonetheless. It takes a bit different approach by defining the schema along the resolvers in the code, for example.

object SchemaDefinition {
  val BookType = ObjectType(
    "Book", "A book.", fields[LibraryService, Book](
      Field("isbn", StringType, Some("The book's ISBN."), resolve = _.value.isbn),
      Field("title", StringType, Some("The book's title."), resolve = _.value.title),
      Field("year", IntType, Some("The book's year."), resolve = _.value.year)
    ))
    
  val ISBN = Argument("isbn", StringType, description = "ISBN")
  val Title = Argument("title", StringType, description = "Book's title")
  val Year = Argument("year", IntType, description = "Book's year")

  val Query = ObjectType(
    "Query", fields[LibraryService, Unit](
      Field("book", OptionType(BookType),
        arguments = ISBN :: Nil,
        resolve = ctx ⇒ ctx.ctx.findBook(ctx arg ISBN)),
      Field("books", ListType(BookType),
        resolve = ctx ⇒  ctx.ctx.getBooks())
    ))

  val Mutation = ObjectType(
    "Mutation", fields[LibraryService, Unit](
      Field("addBook", BookType,
        arguments = ISBN :: Title :: Year :: Nil,
        resolve = ctx ⇒ ctx.ctx.addBook(Book(ctx arg ISBN, ctx arg Title, ctx arg Year))),
      Field("updateBook", BookType,
        arguments = ISBN :: Title :: Year :: Nil,
        resolve = ctx ⇒ ctx.ctx.updateBook(ctx arg ISBN, Book(ctx arg ISBN, ctx arg Title, ctx arg Year))),
      Field("removeBook", BooleanType,
        arguments = ISBN :: Nil,
        resolve = ctx ⇒ ctx.ctx.removeBook(ctx arg ISBN))
    ))
    
   val LibrarySchema = Schema(Query, Some(Mutation))
}

Although there are some pros and cons to the code-first schema development, it may be a good solution in certain microservice architecture implementations.

3.2. graphql-java

Easy to guess, graphql-java is the GraphQL implementation in Java. It has pretty good integration with Spring Framework as well as any other Servlet-compatible framework or container. In the case of such a simple GraphQL schema as ours, the implementation is just a matter of defining resolvers.

public class Query implements GraphQLQueryResolver {
    private final LibraryService libraryService;
    
    public Query(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
    
    public Optional<Book> book(String isbn) {
        return libraryService.findBook(isbn);
    }
    
    public List<Book> books() {
        return new ArrayList<>(libraryService.getBooks());
    }
}

public class Mutation implements GraphQLMutationResolver {
    private final LibraryService libraryService;
    
    public Mutation(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
        
    public Book addBook(String isbn, String title, int year) {
        return libraryService.addBook(new Book(isbn, title, year));
    }
    
    public Book updateBook(String isbn, String title, int year) {
        return libraryService.updateBook(isbn, new Book(isbn, title, year));
    }
    
    public boolean removeBook(String isbn) {
        return libraryService.removeBook(isbn);
    }
}

And this is literally it. If you are considering using GraphQL in some or across all services in your microservice architecture, the graphql-java could be a robust foundation to build upon.

4. The RPC Style

Comparing to RESTful or GraphQL, the efficiency of RPC conversations, specifically for service-to-service communication, is really hard to beat. The gRPC framework from Google is taking a lead here but this is not the only player.

As we are going to see, many RPC systems are built on the idea of defining a service contract: in Java it is typically an annotated interface definition. The server side implements this interface and exposes it over the wire, whereas on the client side this interface is used to get a proxy or a stub.

4.1. java-grpc

We have briefly glanced through gRPC generic concepts in the previous part of the tutorial, but in this section we are going to talk about its Java implementation – java-grpc. Since mostly everything is generated for you from the Protocol Buffers service definition, the only thing left to the developers is to provide the relevant service implementations. Here is the gRPC version of our library management service.

static class LibraryImpl extends LibraryGrpc.LibraryImplBase {
    private final LibraryService libraryService;
        
    public LibraryImpl(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
        
    @Override
    public void addBook(AddBookRequest request, StreamObserver<Book> responseObserver) {
        final Book book = Book.newBuilder()
            .setIsbn(request.getIsbn())
            .setTitle(request.getTitle())
            .setYear(request.getYear())
            .build();

        responseObserver.onNext(libraryService.addBook(book));
        responseObserver.onCompleted();
    }
        
    @Override
    public void getBooks(Filter request, StreamObserver<BookList> responseObserver) {
        final BookList bookList = BookList
            .newBuilder()
            .addAllBooks(libraryService.getBooks())
            .build();
        responseObserver.onNext(bookList);
        responseObserver.onCompleted();
    }
        
    @Override
    public void updateBook(UpdateBookRequest request, StreamObserver<Book> responseObserver) {
        responseObserver.onNext(libraryService.updateBook(request.getIsbn(), request.getBook()));
        responseObserver.onCompleted();
    }
        
    @Override
    public void removeBook(RemoveBookRequest request, StreamObserver<Empty> responseObserver) {
        libraryService.removeBook(request.getIsbn());
        responseObserver.onCompleted();
    }
}

It is worth mentioning that Protocol Buffers is the default but not the only serialization mechanism, gRPC could be used with JSON encoding as well. By and large, gRPC works amazingly well, and is certainly a safe bet with respect to implementing service-to-service communication in the microservice architecture. But more to come, stay tuned, grpc-web is around the corner.

4.2. Reactive gRPC

In the recent years reactive programming is steadily making its way to the mainstream. The Reactive gRPC is a suite of libraries to augment gRPC to work with Reactive Streams implementations. In the nutshell, it just generates alternative gRPC bindings, with respect to the library of your choice (RxJava 2 and  Spring Reactor as of now), everything else stays pretty much unchanged. To prove it, let us take a look on the LibraryImpl implementation using Reactive Streams APIs.

static class LibraryImpl extends RxLibraryGrpc.LibraryImplBase {
    private final LibraryService libraryService;
        
    public LibraryImpl(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
        
    @Override
    public Single<Book> addBook(Single<AddBookRequest> request) {
        return request
            .map(r -> 
                Book
                    .newBuilder()
                    .setIsbn(r.getIsbn())
                    .setTitle(r.getTitle())
                    .setYear(r.getYear())
                    .build())
            .map(libraryService::addBook);
    }
        
    @Override
    public Single<BookList> getBooks(Single<Filter> request) {
        return request
            .map(r -> 
                BookList
                    .newBuilder()
                    .addAllBooks(libraryService.getBooks())
                    .build());
    }
        
    @Override
    public Single<Book> updateBook(Single<UpdateBookRequest> request) {
        return request
            .map(r -> libraryService.updateBook(r.getIsbn(), r.getBook()));
    }
        
    @Override
    public Single<Empty> removeBook(Single<RemoveBookRequest> request) {
        return request
            .map(r -> {
                libraryService.removeBook(r.getIsbn());
                return Empty.newBuilder().build();
            });
    }
}

To be fair, Reactive gRPC is not the full-fledged gRPC implementation but rather an excellent addition to the java-grpc.

4.3. Akka gRPC

Yet another great tool from Akka Toolkit box, Akka gRPC provides support for building streaming gRPC servers and clients on top of Akka Streams (and Akka Toolkit in general). The Java-based implementation is relying on CompletionStage from the standard library and is quite straightforward to use.

public class LibraryImpl implements Library {
    private final LibraryService libraryService;
    
    public LibraryImpl(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
    
    @Override
    public CompletionStage<Book> addBook(AddBookRequest in) {
        final Book book = Book
            .newBuilder()
            .setIsbn(in.getIsbn())
            .setTitle(in.getTitle())
            .setYear(in.getYear())
            .build();
        
        return CompletableFuture.completedFuture(libraryService.addBook(book));
    }

    @Override
    public CompletionStage<BookList> getBooks(Filter in) {
        return CompletableFuture.completedFuture(
            BookList
                .newBuilder()
                .addAllBooks(libraryService.getBooks())
                .build());
    }
    
    @Override
    public CompletionStage<Book> updateBook(UpdateBookRequest in) {
        return CompletableFuture.completedFuture(libraryService.updateBook(in.getIsbn(), in.getBook()));
    }
    
    @Override
    public CompletionStage<Empty> removeBook(RemoveBookRequest in) {
        libraryService.removeBook(in.getIsbn());
        return CompletableFuture.completedFuture(Empty.newBuilder().build());
    }
}

Akka gRPC is quite a new member of the Akka Toolkit and is currently in the preview mode. It could be used already today but certainly expect some changes in the future.

4.4. Apache Dubbo

Apache Dubbo, currently under the incubation within Apache Software Foundation but initially developed at Alibaba, is a high-performance, Java-based, open source RPC framework. Our library service could be up and running just in a few lines of code.

final ServiceConfig serviceConfig = new ServiceConfig();
serviceConfig.setApplication(new ApplicationConfig("library-provider"));
serviceConfig.setRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
serviceConfig.setInterface(LibraryService.class);
serviceConfig.setRef(new LibraryServiceImpl());
serviceConfig.export();

It is purely Java-oriented so if you are aiming to build the polyglot microservice architecture, Apache Dubbo might not help you there natively but through additional integrations, like for example RPC over REST or RPC over HTTP.

4.5. Finatra and Finagle

Finagle, the RPC library, and Finatra, the services framework built on top of it, were born at Twitter and open-sourced shortly after.

Finagle is an extensible RPC system for the JVM, used to construct high-concurrency servers. Finagle implements uniform client and server APIs for several protocols, and is designed for high performance and concurrency. – https://github.com/twitter/finagle

Finatra is a lightweight framework for building fast, testable, scala applications on top of TwitterServer and Finagle.https://github.com/twitter/finatra

They are both Scala-based and are actively used in production. Finagle was one of the first libraries to use Apache Thrift for service generation and binary serialization. Let us grab the library service IDL from the previous part of the tutorial and implement it as a Finatra service (as usual, most of the scaffolding code is generated on our behalf).

@Singleton
class LibraryController @Inject()(libraryService: LibraryService) extends Controller with Library.BaseServiceIface {
  override val addBook = handle(AddBook) { args: AddBook.Args =>
    Future.value(libraryService.addBook(args.book))
  }
  
  override val getBooks = handle(GetBooks) { args: GetBooks.Args =>
    Future.value(libraryService.getBooks())
  }
  
  override val removeBook = handle(RemoveBook) { args: RemoveBook.Args =>
    Future.value(libraryService.removeBook(args.isbn))
  }
  
  override val updateBook = handle(UpdateBook) { args: UpdateBook.Args =>
    Future.value(libraryService.updateBook(args.isbn, args.book))
  }
}

One of the distinguishing features of the Finagle is the out of the box support of distributed tracing and statistics for monitoring and diagnostics, invaluable insights for operating the microservices in production.

5. Messaging and Eventing

In microservice architecture, and generally in many loosely coupled distributed systems, some form of message and event passing is one of the basic building blocks. Along this section we are going to talk about few frameworks and outline a number of messaging solutions you may use independently of the framework of your choice.

5.1. Axon Framework

Axon is a lightweight, open-source Java framework to build scalable, extensible event-driven applications. It is one of the pioneers to employ the sound architectural principles of domain-driven design (DDD) and Command and Query Responsibility Segregation (CQRS) in practice.

5.2. Lagom

Lagom is an opinionated, open source framework for building reactive microservice systems in Java or Scala. Lagom stands on the shoulders of giants, Akka Toolkit and Play! Framework, two proven technologies that are battle-tested in production in many of the most demanding applications. Its designed after the principles of the domain-driven design (DDD), Event Sourcing and Command and Query Responsibility Segregation (CQRS) and strongly encourages usage of these patterns.

5.3. Akka

Akka is a toolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala. As we have seen, Akka serves as a foundation for a several other high-level frameworks (like for example Akka HTTP and Play Framework), however it is by itself is a great way to build microservices which could be decomposed into independent actors. The library management actor is an example of such a solution.

public class LibraryActor extends AbstractActor {
    private final LibraryService libraryService;
    
    public LibraryActor(final LibraryService libraryService) {
        this.libraryService = libraryService;
    }
    
    @Override
    public Receive createReceive() {
        return receiveBuilder()
            .match(GetBooks.class, e -> 
                getSender().tell(libraryService.getBooks(), self()))
            .match(AddBook.class, e -> { 
                final Book book = new Book(e.getIsbn(), e.getTitle());
                getSender().tell(libraryService.addBook(book), self());
            })
            .match(FindBook.class, e -> 
                getSender().tell(libraryService.findBook(e.getIsbn()), self()))
            .matchAny(o -> log.info("received unknown message"))
            .build();
    }
}

Communication between Akka actors is very efficient and is not based on HTTP protocol (one of the preferred transports is Aeron, which we have briefly talked about in the previous part of the tutorial). The Akka Persistence module enables stateful actors to persist their internal state and recover it later. There are certain complexities you may run into while implementing microservice architecture using Akka but overall it is a solid and trusted choice.

5.4. ZeroMQ

ZeroMQ is not a typical messaging middleware. It is completely brokerless (originally the zero prefix in ZeroMQ was meant as “zero broker”).

ZeroMQ (also known as ØMQ, 0MQ, or zmq) looks like an embeddable networking library but acts like a concurrency framework. It gives you sockets that carry atomic messages across various transports like in-process, inter-process, TCP, and multicast. You can connect sockets N-to-N with patterns like fan-out, pub-sub, task distribution, and request-reply. It’s fast enough to be the fabric for clustered products. Its asynchronous I/O model gives you scalable multicore applications, built as asynchronous message-processing tasks. It has a score of language APIs and runs on most operating systems. – http://zguide.zeromq.org/page:all#ZeroMQ-in-a-Hundred-Words

It is widely used in the applications and services where achieving the low latency is a must. Those are the spots in microservice architecture where ZeroMQ might be of great help.

5.5. Apache Kafka

Apache Kafka, started from the idea of building the distributed log system, has expanded way beyond that into a distributed streaming platform. It is horizontally scalable, fault-tolerant, wicked fast, and is able to digest insanely massive volumes of messages (or events).

The hyper-popularity of the Apache Kafka (and for good reasons) had an effect that many other message brokers became forgotten and undeservedly are fading away. It is highly unlikely that Apache Kafka will not be able to keep up with the demand of your microservice architecture implementation, but more often than not a simpler alternative could be an answer.

5.6. RabbitMQ and Apache Qpid

RabbitMQ and Apache Qpid are classical examples of the message brokers which speak AMQP protocol. Not much to say beside that RabbitMQ is most known for the fact it is written in Erlang. Both are open source and good choices to serve as the messaging backbone between your microservices.

5.7. Apache ActiveMQ

Apache ActiveMQ is one of the oldest and most powerful open source messaging solutions out there. It supports a wide range of the protocols (including AMQP, STOMP, MQTT) while being fully compliant with Java Messaging Service (JMS 1.1) specification.

Interestingly, there are quite a few different message brokers hidden under Apache ActiveMQ umbrella. One of those is ActiveMQ Artemis with a goal to be a multi-protocol, embeddable, very high performance, clustered, asynchronous messaging system.

Another one is ActiveMQ Apollo, a development effort to come up with a faster, more reliable and easier to maintain messaging broker. It was built from the foundations of the original Apache ActiveMQ with radically different threading and message dispatching architecture. Although very promising, it seems to be abandoned.

It is very likely that Apache ActiveMQ has every feature you need in order for your microservices to communicate the messages reliably. Also, the JMS support might be an important benefit in the enterprise world.

5.8. Apache RocketMQ

Apache RocketMQ is an open-source distributed messaging and streaming data platform (yet another contribution from Alibaba). It aims for extremely low latency, high availability and massive message capacity.

5.9. NATS

NATS is a simple, high performance open source messaging system for cloud-native applications, IoT messaging, and microservice architectures. It implements a highly scalable and elegant publish-subscribe message distribution model.

5.10. NSQ

NSQ is an open-source realtime distributed messaging platform, designed to operate at scale and handle billions of messages per day. It also follows a broker-less model and as such has no single point of failure, supports high-availability and horizontal scalability.

6. Get It All

There is a certain class of libraries which have a sole intention to seamlessly bridge many different communication channels together. Collectively, they are known as integration frameworks and are heavily inspired by terrific Enterprise Integration Patters book.

6.1. Apache Camel

Apache Camel is a powerful and mature open source integration framework. It is a surprisingly small library with a minimal set of dependencies and easily embeddable in any Java application. It abstracts away the kind of transports used behind a concise API layer which allows the interaction with more than 300 components provided out of the box.

6.2. Spring Integration

Spring Integration, another great member of the Spring projects portfolio, enables lightweight messaging and integration with external systems. It is primarily used within Spring-based applications and services, providing outstanding interoperability with all other Spring projects.

Spring Integration’s primary goal is to provide a simple model for building enterprise integration solutions while maintaining the separation of concerns that is essential for producing maintainable, testable code. – https://spring.io/projects/spring-integration

If your microservices are built on top of Spring Framework, the Spring Integration is a logical choice to make (in case its need is justified and it fits into the overall architecture).

7. What about Cloud?

Along this part of the tutorial we have talked about open-source libraries and frameworks, which could be used either in on-premise deployments or in the cloud. They are generally agnostic to the vendor but many cloud providers have own biases towards one framework or another. More to that, besides offering own managed services, specifically in the messaging and data streaming space, the cloud vendors are heavily gravitating towards serverless computing, primarily in the shape of the function as a service.

8. But There Are a Lot More …

Fairly speaking, there are too many different libraries and frameworks to talk about. We have been discussing the most widely used ones but there are much, much more. Let us just mention some of them briefly.

OSGi and its distributed counterpart, DOSGi, were known to be the only true way to build modular system and platforms in Java. Although it is not a simple one to deal with, it could be very well suited for implementing microservices.

RxNetty is a pretty low-level reactive extension adaptor for Netty, quite helpful if you need an engine with very low overhead.

Rest.li (from Linkedin) is a framework for building robust, scalable RESTful web services architectures using dynamic discovery and simple asynchronous APIs.

Apache Pulsar is a multi-tenant, high performance, very low latency solution for server-to-server messaging which was originally developed by Yahoo.

9. Java / JVM Landscape – Conclusions

In this section of the tutorial we paged through tons of the different libraries and frameworks which are used today to build microservices in Java (and JVM in general). Every one of them has own niche but it does not make the decision process any easier. Importantly, the today’s architecture may not reflect the tomorrow’s reality: you have to pick the stack which could scale with your microservices for years to come.

10. What’s next

In the next section of the tutorial we are going to talk about monoglot versus polyglot microservices and outline the reference application to serve as a playground for our future topics.

The complete set of sample projects is available for download.

Andrey Redko

Andriy is a well-grounded software developer with more then 12 years of practical experience using Java/EE, C#/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Shilpa
4 years ago

Great..this is exactly what I was looking for :-)
Thanks for sharing !

Back to top button