I tried using Dapr in Java to facilitate microservice development

Introduction

Looking at Microsoft Build 2020, Dapr of Distributed Application Runtime seemed to be interesting. Tutorial was Node.js, so I made a sample using Dapr from Quarkus while referring to it.

See below for code https://github.com/koduki/example-dapr/tree/v01/api-with-java

What is Dapr?

Dapr is a framework developed by Microsoft that simplifies the implementation of microservices by implementing non-functional requirements such as inter-service invocation, state management, and inter-service messaging with a sidecar (Proxy). Since it is developed by OSS, it can be used from the following.

I thought that it was the same as a service mesh such as Istio because it was a sidecar, but when I listened to the session, it felt a little different, and abstracted file and state management (data persistence), or queuing such as Kafka. There seemed to be a role to play. It's more like Java EE's JNDI, DataSource, or JCA (Java Connector Architecture). ~~ If you're an uncle ~~ It's a chance to say, "This is the one I did at Shinkenzemi!"

If it is a Java EE container such as Weblogic, I think that this area is in the same memory space and talks with T3 in the first place, but Dapr and each application talk with HTTP or gRPC.

Let's move Dapr for the time being

Let's move it for the time being before doing the actual tutorial. This time gRPC is a hassle, so we'll implement REST. For the time being, create a Quarkus template with the mvn command and execute it.

$ mvn io.quarkus:quarkus-maven-plugin:1.4.2.Final:create \
    -DprojectGroupId=dev.nklab \
    -DprojectArtifactId=dapr-app \
    -DprojectVersion=1.0.0-SNAPSHOT \
    -DclassName="dev.nklab.example.dapr.HelloResource"
$ cd dapr-app
$ ./mvnw quarkus:dev 

Try accessing with curl from another terminal.

$  curl http://localhost:8080/hello                           
hello

Install Dapr when you can confirm the operation of the application. k8s works without any special, but Docker seems to need to be installed in advance.

$ curl -fsSL https://raw.githubusercontent.com/dapr/cli/master/install/install.sh | /bin/bash
$ dapr init

The installation is now complete. If you get the following error, you probably forgot dapr init.

exec: "daprd": executable file not found in $PATH

Next, wrap the Quarkus application created earlier with Dapr with the following command and execute it.

$ dapr run --app-id javaapp --app-port 8080 --port 3500 ./mvnw quarkus:dev 
...
ℹ️  Updating metadata for app command: ./mvnw quarkus:dev
✅  You're up and running! Both Dapr and your app logs will appear here.

--app-port is the Quarkus port number and --port is the Dapr port number. Now let's access Dapr with curl.

$ curl http://localhost:3500/v1.0/invoke/javaapp/method/hello                                
hello

Since Dapr is acting as a sidecar, that is, a Proxy, you can see that you could access the backside app with 3500 instead of 8080. The javaapp part is the ʻaap-id specified at runtime earlier. It seems that it will be a process that links with the application on the back side with ʻinvoke.

State management using Redis

Application specifications

Now, perform Steps 1 to 5 of Hello World. The application to be implemented this time has the following structure.

image.png

A user sends a request to Application (Java) via Dapr, hits Dapr's API from the application and writes to Redis via Dapr. It's interesting not to go directly through Redis for data persistence, as the application only talks to Dapr.

As a specification, the user sends the following POST request to Application.

{
  "data": {
    "orderId": "42"
  } 
}

This data is stored in Redis in the following format:

[{
  key: "order",
  value:Store orderId here
}]

Also, when a GET request is sent to Application, the current orderId is returned.

Application implementation

Now let's implement the application. Since we will use JSON this time, add the library to Quarkus.

$ ./mvnw quarkus:add-extension -Dextensions="quarkus-resteasy-jsonb"
$ ./mvnw quarkus:add-extension -Dextensions="quarkus-resteasy-jackson"

Then modify the application as follows:

HelloResource.java


@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class HelloResource {

    @ConfigProperty(name = "daprapp.daprport")
    String daprPort;

    String stateStoreName = "statestore";

    @GET
    @Path("/order")
    public Map<String, Object> order() throws IOException, InterruptedException {
        return Map.of("orderId", get(stateUrl() + "/order").body());
    }

    @POST
    @Path("/neworder")
    public HttpResponse neworder(Map<String, Map<String, Object>> data) throws IOException, InterruptedException {
        System.out.println("orderId: " + data.get("data").get("orderId"));

        var items = List.of(Map.of("key", "order", "value", data.get("data").get("orderId")));
        return post(stateUrl(), items);
    }

    private String stateUrl() {
        return "http://localhost:" + daprPort + "/v1.0/state/" + stateStoreName;
    }

    private HttpResponse<String> post(String url, List<Map<String, Object>> items) throws IOException, InterruptedException, JsonProcessingException {
        var mapper = new ObjectMapper();
        var client = HttpClient.newHttpClient();
        var request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(items)))
                .setHeader("Content-Type", "application/json")
                .build();
        return client.send(request, HttpResponse.BodyHandlers.ofString());
    }

    private HttpResponse<String> get(String url) throws InterruptedException, IOException {
        var client = HttpClient.newHttpClient();
        var request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .GET()
                .setHeader("Content-Type", "application/json")
                .build();
        return client.send(request, HttpResponse.BodyHandlers.ofString());
    }
}

I haven't done anything special as JAX-RS, so I won't go into details, but new order is the endpoint for registration and ʻorder` is the endpoint for reference.

You are accessing http: // localhost: 3500 / v1.0 / state / statestore in each method. This is the endpoint of Dapr's state management API. This time, the substance of this state management API is Redis. The request and response of the state management API will be JSON as shown below.

[{
  key:value,
  value:value
}]

Set up Redis in your state management implementation

Then configure Redis for your state management implementation. However, it has already been set, so just check it. Actually, a directory called components is created at the timing of dapr run. It seems that the statestore.yaml here describes which store to connect to.

components/statestore.yaml


apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: ""
  - name: actorStateStore
    value: "true"

Perhaps if you rewrite this to RDB etc., it will be another implementation.

test

Now that the implementation is complete, let's check the operation. First, start Dapr.

$ dapr run --app-id javaapp --app-port 8080 --port 3500 ./mvnw quarkus:dev

Then make a request.

$ curl -X POST -H "Content-Type: application/json" -d '{"data": { "orderId": "41" } }' http://localhost:3500/v1.0/invoke/javaapp/method/neworder
{}
$ curl http://localhost:3500/v1.0/invoke/javaapp/method/order
{"orderId":"\"41\""}

I think you can confirm that the requested value you threw is stored and you can get it. You can also write POST with the Dapr command as follows.

$ dapr invoke --app-id javaapp --method neworder --payload '{"data": { "orderId": "41" } }'

Summary

For the time being, I tried using Dapr from Java. Since it is REST, I was able to implement it without any problems. Dapr has a Jakarta EE feeling more than Istio, and apart from Dapr itself, this idea itself seems to be in the right direction from the viewpoint of ease of development. It is a basic idea to hide the substance of services and data including DAO patterns, and non-functionals should be integrated into the infrastructure side as much as possible.

On the other hand, if the persistence layer also goes through Proxy, it seems that a certain amount of overhead is inevitable even if gRPC is used. I think that it will be required in the future how to deal with this area by design.

Dapr itself is still in its infancy and there are many rough edges, but I would like to touch it a little more in the future.

Then Happy Hacking!

Reference link

Recommended Posts

I tried using Dapr in Java to facilitate microservice development
I tried using JWT in Java
I tried to implement deep learning in Java
I tried to create Alexa skill in Java
I tried to make a talk application in Java using AI "A3RT"
I tried using Java REPL
I tried metaprogramming in Java
I tried using a database connection in Android development
I tried to implement Firebase push notification in Java
I tried using Google Cloud Vision API in Java
I tried to operate SQS using AWS Java SDK
# 2 [Note] I tried to calculate multiplication tables in Java.
I tried to create a Clova skill in Java
I tried to make a login function in Java
I tried using an extended for statement in Java
I tried to implement the Euclidean algorithm in Java
~ I tried to learn functional programming in Java now ~
I tried to find out what changed in Java 9
I tried to create a java8 development environment with Chocolatey
I tried using Java8 Stream API
I tried to convert a string to a LocalDate type in Java
I tried to summarize Java learning (1)
I tried to make a client of RESAS-API in Java
I tried to summarize Java 8 now
I tried using Java memo LocalDate
I tried using GoogleHttpClient of Java
I tried setting Java beginners to use shortcut keys in eclipse
I tried to display the calendar on the Eclipse console using Java.
I tried to summarize Java lambda expressions
I tried using OpenCV with Java + Tomcat
I tried to build a Firebase application development environment with Docker in 2020
[For beginners] I tried using DBUnit in Eclipse
I tried to make Basic authentication with Java
I tried to implement polymorphic related in Nogizaka.
[For beginners] I tried using JUnit 5 in Eclipse
I want to send an email in Java.
I tried to organize the session in Rails
java I tried to break a simple block
rsync4j --I want to touch rsync in Java.
I tried to build Micra mackerel in 1 hour!
I tried to develop an application in 2 languages
I tried to implement a server using Netty
I tried to break a block with java (1)
[Note] Execute java program in the integrated development environment Eclipse-I tried using git
[Java] I tried to connect using a connection pool with Servlet (tomcat) & MySQL & Java
I tried using Gson
I tried using TestNG
I tried using Galasa
I tried to organize the cases used in programming
I want to do something like "cls" in Java
I tried to implement TCP / IP + BIO with JAVA
I want to use ES2015 in Java too! → (´ ・ ω ・ `)
Things to watch out for in future Java development
I tried using Log4j2 on a Java EE server
I tried to implement Stalin sort with Java Collector
[Java] I tried to implement Yahoo API product search
I tried passing Java Silver in 2 weeks without knowing Java
Try to build a Java development environment using Docker
I tried scraping a stock chart using Java (Jsoup)
I tried to build an environment using Docker (beginner)
I tried to explain Effective Java 3rd edition "almost all chapters" in "easy-to-read Japanese".