The ‘Java on Linux’ Advantage
Having much exposure on both Windows and Linux Operating Systems, various programming languages and having worked on some of the more popular cloud platforms, my quest in this section is to provide my take on why Java on Linux on Containers are very well suited for microservices deployment in the production environment.
Since its announcement by Sun Microsystems engineer James Gosling on 23 May 1995, Java has undergone significant technical and organisational changes over the past 25 years. Moving from entirely proprietary to open source in 2006, it transitioned from Sun to Oracle in 2010 as part of Oracle’s acquisition of Sun, and, in 2017, its release cadence was accelerated.
Although there have been many rivals, Java hasn’t been displaced by an of them. It is likely to remain a cornerstone of enterprise application stacks for years to come. Among the many reasons why Java to remain widely used, is the simple fact that there is a large pool of trained Java developers already for enterprises to draw from, making it possible to maintain code years after it was initially written.
Limitations of other Languages
One of the selling points of a microservice architecture is the ability to write the microservice in any language. But limitations of the language must also be taken into account and not just whether the language is fashionable or fun.
Deciding factors such as performance, scalability limitations, tooling support, production readiness standardisation, concurrency and sharding (partitioning) capabilities, available compatible libraries and access to knowledgeable developers are also important when select a programming language for implementation.
Languages such as R and Python are essentially single threaded, which can introduce scalability and performance bottlenecks that are difficult to mitigate compared to languages such as C++, Java or Go which are built for concurrency and partitioning. You thus cannot take advantage of the full benefits of streams.
Portability. Because Java is compiled to bytecode, it can run on any computer hardware that has a JVM. C and GoLang have the disadvantage that they have to be compiled for the production computing hardware and OS. If the development computer architecture or OS is different from the production architecture, it goes against the 12 factor principles of
One code base regardless of execution environment,
Dev-Production Parity and
Deployment based on Environment based Configuration.
Execution speed. Perl, PHP, Python and Javascript are interpreted or scripting languages which are at least 10 times slower than C, Java or GoLang. They also have a larger execution footprint. Because they do not have strict type checking, type errors that should be caught at compile time on C, Java and GoLang commonly occur in production for these scripting languages.
You also need to write for a codebase that you know will be around for a long time. In many government agencies, a languange must be around and have support for at least 10 years if they are going to be used for mission-critical applications.
They should have the necessary library and tools to be tested and deployed quickly, allow monitoring, tracing and logging during execution and be deployed to to cloud easily with a small execution and memory footprint.
Lambda and Streams
The support for anonymous functions is an important software design feature for multi-threaded applications. Introduced in Java, lambda expressions, dispenses with the verbosity and readability issues surrounding anonymous inner classes when writing callbacks and event handlers. This allows developers to write code that is easier to read with the intent and focus on programming the business logic rather than the mechanics of how it is achieved.
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.out.println("button clicked.");
}
});
button.addActionListener(event -> System.out.println("button clicked"));
The code above shows one written with inner functions and the same using lambda expressions. The single line code focuses on the business intent rather than the mechanics.
Containerisation
Containerising your application allows you to nail down your run time environment and other aspects of the environment, such as how logging should be collected and redirected.
With this done, you can deploy it on any of the container platforms.
Hence, your containerised application can run on Docker swarm or Kubernetes on your development PC. Or you can run it on the cloud through managed containerisation platforms such as Amazon EKS (Kubernetes management infrastructure), supported by AWS Elastic Container Registry and AWS Key Management Service, API Gateway load balancers, etc.
Platform Options
On Premise: You’ve got the choice of deploying and running your application with options like RedHat OpenShift, Kubernetes and Helm Charts or Docker Swarm or Pivotal Cloud Foundry.
In the Cloud:
Spring Cloud for AWS: AWS provides a JDK to issue requests for AWS services with Java code.
AWS Elastic Beanstalk (PaaS): Change number of instances, monitoring using AWS’s eb console. Requires you to specify info about the application.
AWS Elastic Container Service (CaaS): Scalable container management service to run, stop and manage Docker containers in a cluster. You specify isolation policies and availability requirements.
AWS EKS (PaaS): Managed Kubernetes cluster using standard kubectl command line client.
AWS EC2 (IaaS): Where you have complete control of the execution environment. But it also means you need to handle OS and JDK maintenance.
Java Platform Integration
In order to take advantage of the sophisticated Requirements of the Platform of cloud-native platforms, the Java Spring Boot framework provides libraries to make this so much easier:
Spring Security: Authentication and Authorization
Spring Boot Actuator: Provides endpoints that let the platform monitor and manage your application’s health, liveliness, configuration and other metrics.
The Alternatives
Microsoft Windows
In my opinion, microservices should ideally be packaged through containers. The Windows OS just does not cut it to be run efficiently in a container for some of the reasons given below:
Docker supports only certain windows versions such as Windows Server 2016 and certain editions of Windows 10. With Linux, you can deploy on pretty much any version or distribution of Linux.
There are very few public windows container registries and images.
Startup and tear down takes time - not ideal for running in a container.
Larger surface of attack. Because you can’t strip down Windows like you can do with Linux, Windows provides several mandatory services and software components the provide hackers more loop holes and thus more opportunities to infiltrate the system.
Serverless
Ideal for very small applications and handling events, but anything more than that will not run well. Typically, your package size should not be more than 50MB and execution must be completed within 15 minutes - which are reasonable limits for a typical Java based microservice.
Requires that your Java code be deployed with a 3rd party, which may not be a wise decision if the code is big.
Support JDK 11 which is also a reasonable limitation.