Tutorial on Microservices with Kotlin, SpringBoot, Akka and Docker – part 5

Intro

I left you in the previous post with the promise of add Akka services to our architecture; and that’s what will do in this and the next post. As always you may download the full code from the repository:

 git clone https://github.com/gitgabrio/container-microservices-tutorial-5.git

actorserver-service

Let’s begin adding the module declaration to the parent pom:

...
   </parent>
      <modules>
        <module>servicecommon</module>
        <module>registrationservice</module>
        <module>persistenceservice</module>
        <module>timeconsumingservice</module>
        <module>configurationservice</module>
        <module>actorserverservice</module>
        <module>docker</module>
    </modules>
    <properties>
...

This is the actor-service pom:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>net.microservices.tutorial</groupId>
        <artifactId>container-microservice</artifactId>
        <version>0.1</version>
        <relativePath>../</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>actorserverservice</artifactId>
    <properties>
        <akka.version>2.4.2</akka.version>
        <!-- CONFIGURATIONS -->
        <start-class>net.microservices.tutorial.actorserverservice.ActorServerServer</start-class>
        <finalName>actorserverservice-${project.version}</finalName>
        <service.port>5555</service.port>
        <rs.port>1111</rs.port>
        <rs.ip>localhost</rs.ip>
        <akka.port>12552</akka.port>
        <container.ip>localhost</container.ip>
    </properties>
    <build>
        <finalName>${finalName}</finalName>
    </build>
    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>servicecommon</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_2.11</artifactId>
            <version>${akka.version}</version>
        </dependency>
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-remote_2.11</artifactId>
            <version>${akka.version}</version>
        </dependency>
    </dependencies>

</project>

Here you may notice the akka dependencies and a couple of akkas-specific properties:

  1. akka.port: this is the port where the Akka actor is listening
  2. container.ip: this is the public ip server, used when not injected by the docker plugin

code

In this module we will inject some properties in the eureka metadata map, to make them available for eventual “clients”, namely, the port:

net/microservices/tutorial/actorserverservice/resources/application.yml:

...
eureka:
  client:
    ...
  instance:
    ...
    metadata-map:
      port: ${AKKA_PORT:@akka.port@}
...

We will use this map in the next part of the tutorial; anyway, just note that the value could be populated by the module pom or by the docker plugin.

net/microservices/tutorial/actorserverservice/resources/application.conf:

akka {
  actor {
    provider = "akka.remote.RemoteActorRefProvider"
    serializers {
        proto = "akka.remote.serialization.ProtobufSerializer"
    }
  }
  remote {
    netty.tcp {
      hostname = localhost
      port = 4324
    }
 }
 log-sent-messages = on
 log-received-messages = on
 }

This is a bare-bone akka configuration. Unfortunately Akka does not read YAML files; it could be possible (see here for an example) with some extra code, but I do not think it is important, here, so I provided one as base configuration. Please note that the netty parameters (hostname and port) will be overridden at startup.
The important details are in the configuration class:

net/microservices/tutorial/actorserverservice/configurations/ActorServerConfiguration.kt:

...
     @Value("\${eureka.instance.metadata-map.port}")
    var akkaPort: Int = 0

    @Value("\${akka.remote.netty.tcp.hostname}")
    var akkaHostName: String = ""



    @Bean
    open fun actorSystem(): ActorSystem {
        val defaultApplication: Config = ConfigFactory.defaultApplication()
                .withValue("akka.remote.netty.tcp.hostname", ConfigValueFactory.fromAnyRef(akkaHostName))
                .withValue("akka.remote.netty.tcp.port", ConfigValueFactory.fromAnyRef(akkaPort))
        val system = ActorSystem.create("RemoteWorkerSystem", defaultApplication)
        system.actorOf(Props.create(ServerActor::class.java), "serverActor")
        return system
    }
...

First, there are two properties injected by Spring, namely

  1. eureka.instance.metadata-map.port -> akkaPort
  2. akka.remote.netty.tcp.hostname -> akkaHostName

Then, in the ActorSystem instantiation, we define its configuration

defaultApplication: Config = ConfigFactory.defaultApplication()

we are loading the default configuration, that will read application.conf;

.withValue("akka.remote.netty.tcp.hostname", ConfigValueFactory.fromAnyRef(akkaHostName))

we are overriding the akka.remote.netty.tcp.hostname value;

.withValue("akka.remote.netty.tcp.port", ConfigValueFactory.fromAnyRef(akkaPort))

we are overriding the akka.remote.netty.tcp.port value;

val system = ActorSystem.create("RemoteWorkerSystem", defaultApplication)

we instantiate the ActorSystem whose “akka-address” will be RemoteWorkerSystem

system.actorOf(Props.create(ServerActor::class.java), "serverActor")

we instantiate the ServerActor that will listen and respond to incoming messages.
And that’s is the bare-minimum akka implementation, for now.

image creation

Let’s update the docker configuration.
docker/pom.xml:

...
 <properties>
      ...
        <!-- services properties -->
        <registration.service.port>1111</registration.service.port>
        <persistence.service.port>2222</persistence.service.port>
        <timeconsuming.service.port>3333</timeconsuming.service.port>
        <configuration.service.port>4444</configuration.service.port>
        <actorserver.service.port>5555</actorserver.service.port>
    </properties>
</properties>

    <dependencies>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>registrationservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>persistenceservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>timeconsumingservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>configurationservice</artifactId>
            <version>0.1</version>
        </dependency>
        <dependency>
            <groupId>net.microservices.tutorial</groupId>
            <artifactId>actorserverservice</artifactId>
            <version>0.1</version>
        </dependency>
    </dependencies>
...

but this time we will introduce some modifications. As you may have experienced, each docker container take a lot of resources, even if the actual code inside it is pretty small, as in this tutorial. There is a way to build and start only a subset of the images actually configured, with the docker plugin configuration tag filter; here you may define the image or the comma-separated list of images to manage.

...
<configuration>
   <filter>(IMAGES-TO-INCLUDE)</filter>
....

.
Now, to have a flexible build configuration, I have defined a global images property:

...
   <actorserver.service.port>5555</actorserver.service.port>
        <!-- Images to build and run - default to all-->
        <images>registrationservice, persistenceservice, timeconsumingservice, configurationservice,
            actorserverservice
        </images>
    </properties>
...

and a couple of profiles to eventually override it at build-time:

...
<profiles>
        <profile>
            <id>configuration</id>
            <properties>
                <images>registrationservice, persistenceservice, timeconsumingservice, configurationservice
                </images>
            </properties>
        </profile>
        <profile>
            <id>akka</id>
            <properties>
                <images>registrationservice, actorserverservice
                </images>
            </properties>
        </profile>
    </profiles>
...

Last, I use the property in the plugin configuration:

...
<build>
        <plugins>
            <!-- DOCKERIZE WITH FABRIC8 -->
            <plugin>
                <!-- The Docker Maven plugin is used to create docker image with the fat jar -->
                <groupId>io.fabric8</groupId>
                <artifactId>docker-maven-plugin</artifactId>
                <version>${docker.maven.plugin.fabric8.version}</version>
                <configuration>
                    <logDate>default</logDate>
                    <autoPull>true</autoPull>
                    <filter>${images}</filter>
                    <images>
...

Now, depending on the profile (or lack of) used for the docker goal, we could decide which image(s) build and run.

After installing the actorserverservice artifact and starting docker with the akka profile, we should have the actorserver service registered:

and the image running here
.
In the logs, you will see the akka instance listening:

Conclusion

Well, you will find the image definition (and all the other missing stuff) in the project:

git clone https://github.com/gitgabrio/container-microservices-tutorial-5.git

In the next part we will add the Akka Client service so… stay tuned!!!

Any comment and suggestion will be greatly appreciated!!!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s