The blog is more about experimenting with Java Native build to achieve less RAM use.
Usually, when using Java as a runtime for Google Cloud functions (https://cloud.google.com/functions/docs/concepts/java-runtime) the average RAM footprint is about 140 MB for a simple REST API Jax-RS based endpoint with JSON (text) static response. For example, the same example in Golang (one HTTP GET method) with JSON response is around 30 MB RAM.
To try to achieve less RAM footprint with Java (e.g. to use 128 MB GCP tier), let us try to use:
- Java Socket as very simple HTTP server
- GraalVM Native build
- Docker
- Google Cloud Run to use custom Docker image as an app
Java code
Source code: https://github.com/ahndmal/http-simple-sock-native
We create ServerSocket and accept connections on port 8080:
try (ServerSocket serverSocket = new ServerSocket(8080)) {
System.out.println(">>> Server started on port 8080");
while(true) {
Socket accept = serverSocket.accept();
System.out.println("> client connected");
InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream();
...
}
We create IO streams to get requests and write data to the output (browser) as a static HTTP GET response.
PrintWriter writer = new PrintWriter(new OutputStreamWriter(outputStream));
writer.write("HTTP/1.1 200 OK\n");
writer.write("Content-Type: text/html\r\n\n");
writer.write("""
<!doctype html><html>
<head><title>Java</title></head>
<body>
<h2>Java Server</h2>
<div>Test</div>
</body>
</html>""");
We can just use any HTML file and read its contents for a response instead of this hardcoded text.
Docker image
We use 3 stages for Docker build:
- Build Jar file
- Use GraalVM findepi image to build native image from this Jar
- Use Debian slim to run the native app
# maven build
FROM maven:3.8.6-amazoncorretto-19 AS BUILD
COPY . .
RUN mvn clean package
# native build
FROM ghcr.io/graalvm/jdk:java
FROM findepi/graalvm:22.3.1-java19-native AS GRAAL
COPY --from=BUILD target/http-sock-3-0.0.2.jar /http-sock.jar
RUN gu install native-image && native-image -jar http-sock.jar
# run app
FROM debian:12-slim
#FROM debian:stable-slim
COPY --from=GRAAL /http-sock /tmp/http-sock
CMD ["/tmp/http-sock"]
Build Docker image locally:
docker build --tag java-http-simple:0.0.1 .
Tag docker image for GCP Artifact registry:
docker tag java-http-simple:0.0.1 us-east1-docker.pkg.dev/{PROJECT}/{ART_REG}/java-http-simple:0.0.2
Deploy image from Artifact Registry to Cloud Run:
gcloud run deploy <service-name> --image <image_name>
Metrics
Trigger the URL of the deployed Cloud Run service and check the results:
HTTP response:
- 120 byte data
- 160 ms response time
RAM metrics:
12% of RAM from 128MB service (~15.3 MB)
As we can see, the memory footprint is around 14MB.
Of course this is a silly app with a primitive response, but it is possible to use such an approach to create a simple REST API app with the DB data and JSON parsing with the little use of RAM.
Thank you for reading!
Top comments (0)