-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
329 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
<?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"> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<groupId>ru.zhenik.akka</groupId> | ||
<artifactId>example</artifactId> | ||
<version>1.0-SNAPSHOT</version> | ||
|
||
|
||
<properties> | ||
<maven.compiler.target>1.8</maven.compiler.target> | ||
<maven.compiler.source>1.8</maven.compiler.source> | ||
<config.version>1.3.3</config.version> | ||
<consul.client.version>1.2.1</consul.client.version> | ||
<akka.http.version>10.1.3</akka.http.version> | ||
<akka.actor.version>2.5.14</akka.actor.version> | ||
</properties> | ||
|
||
<dependencies> | ||
|
||
<!--Config--> | ||
<dependency> | ||
<groupId>com.typesafe</groupId> | ||
<artifactId>config</artifactId> | ||
<version>${config.version}</version> | ||
</dependency> | ||
|
||
|
||
|
||
<!-- AKKA START --> | ||
<!--HTTP--> | ||
<dependency> | ||
<groupId>com.typesafe.akka</groupId> | ||
<artifactId>akka-http_2.12</artifactId> | ||
<version>${akka.http.version}</version> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.typesafe.akka</groupId> | ||
<artifactId>akka-http-core_2.12</artifactId> | ||
<version>${akka.http.version}</version> | ||
</dependency> | ||
<!--Actor Model--> | ||
<dependency> | ||
<groupId>com.typesafe.akka</groupId> | ||
<artifactId>akka-actor_2.12</artifactId> | ||
<version>${akka.actor.version}</version> | ||
</dependency> | ||
<!--Streams--> | ||
<dependency> | ||
<groupId>com.typesafe.akka</groupId> | ||
<artifactId>akka-stream_2.12</artifactId> | ||
<version>${akka.actor.version}</version> | ||
</dependency> | ||
<!-- AKKA END --> | ||
|
||
<!-- HashiCorp Consul client --> | ||
<!-- https://github.com/rickfast/consul-client --> | ||
<dependency> | ||
<groupId>com.orbitz.consul</groupId> | ||
<artifactId>consul-client</artifactId> | ||
<version>${consul.client.version}</version> | ||
</dependency> | ||
<!-- HashiCorp END --> | ||
|
||
</dependencies> | ||
</project> |
44 changes: 44 additions & 0 deletions
44
example/src/main/java/ru/zhenik/akka/example/AppConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package ru.zhenik.akka.example; | ||
|
||
import com.typesafe.config.Config; | ||
import com.typesafe.config.ConfigFactory; | ||
|
||
public class AppConfiguration { | ||
public final String appId; | ||
public final String serviceName; | ||
public final String host; | ||
public final Integer port; | ||
public final ServiceDiscoveryConfiguration serviceDiscoveryConfiguration; | ||
|
||
private AppConfiguration(final String appId){ | ||
// load application.conf file | ||
Config config = ConfigFactory.load(); | ||
|
||
this.appId = appId; | ||
this.serviceName = config.getString("service.name"); | ||
this.host = config.getString("application.host"); | ||
this.port = config.getInt("application.port"); | ||
this.serviceDiscoveryConfiguration = | ||
new ServiceDiscoveryConfiguration( | ||
config.getString("discovery.host"), | ||
config.getInt("discovery.port"), | ||
config.getLong("discovery.healthcheck-timeout") | ||
); | ||
|
||
} | ||
|
||
public static AppConfiguration loadConfig(final String appId) { | ||
return new AppConfiguration(appId); | ||
} | ||
|
||
public final static class ServiceDiscoveryConfiguration { | ||
public final String host; | ||
public final int port; | ||
public final long healthCheckTimeout; | ||
private ServiceDiscoveryConfiguration(String host, int port, long healthCheckTimeout) { | ||
this.host = host; | ||
this.port = port; | ||
this.healthCheckTimeout = healthCheckTimeout; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package ru.zhenik.akka.example; | ||
|
||
import akka.NotUsed; | ||
import akka.actor.ActorRef; | ||
import akka.actor.ActorSystem; | ||
import akka.http.javadsl.ConnectHttp; | ||
import akka.http.javadsl.Http; | ||
import akka.http.javadsl.ServerBinding; | ||
import akka.http.javadsl.model.HttpRequest; | ||
import akka.http.javadsl.model.HttpResponse; | ||
import akka.stream.ActorMaterializer; | ||
import akka.stream.Materializer; | ||
import akka.stream.javadsl.Flow; | ||
import java.util.UUID; | ||
import java.util.concurrent.CompletionStage; | ||
import ru.zhenik.akka.example.infrastructure.discovery.DiscoveryAgentActor; | ||
import ru.zhenik.akka.example.interfaces.rest.AppResource; | ||
|
||
public class Boot { | ||
|
||
public static void main(String[] args) { | ||
// config | ||
final String appId = UUID.randomUUID().toString(); | ||
final AppConfiguration appConfig = AppConfiguration.loadConfig(appId); | ||
|
||
// actor system init | ||
final ActorSystem system = ActorSystem.create(); | ||
final Materializer materializer = ActorMaterializer.create(system); | ||
|
||
// service discovery actor | ||
final ActorRef serviceDiscoveryActor = system.actorOf(DiscoveryAgentActor.props(appConfig), "example-app-consul-service"); | ||
|
||
// http init | ||
final Flow<HttpRequest, HttpResponse, NotUsed> routeFlow = new AppResource(appConfig).routes().flow(system, materializer); | ||
final CompletionStage<ServerBinding> binding = Http | ||
.get(system) | ||
.bindAndHandle( | ||
routeFlow, | ||
ConnectHttp.toHost(appConfig.host, appConfig.port), | ||
materializer | ||
); | ||
|
||
// exception handling | ||
binding.exceptionally(failure -> { | ||
System.err.println("Something very bad happened! " + failure.getMessage()); | ||
system.terminate(); | ||
return null; | ||
}); | ||
|
||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
...le/src/main/java/ru/zhenik/akka/example/infrastructure/discovery/DiscoveryAgentActor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package ru.zhenik.akka.example.infrastructure.discovery; | ||
|
||
import akka.actor.AbstractActor; | ||
import akka.actor.ActorRef; | ||
import akka.actor.Props; | ||
import com.google.common.net.HostAndPort; | ||
import com.orbitz.consul.AgentClient; | ||
import com.orbitz.consul.Consul; | ||
import com.orbitz.consul.NotRegisteredException; | ||
import com.orbitz.consul.model.agent.ImmutableRegistration; | ||
import com.orbitz.consul.model.agent.Registration; | ||
import java.util.Collections; | ||
import java.util.concurrent.TimeUnit; | ||
import ru.zhenik.akka.example.AppConfiguration; | ||
import scala.concurrent.duration.FiniteDuration; | ||
|
||
/** | ||
* | ||
* */ | ||
public class DiscoveryAgentActor extends AbstractActor { | ||
|
||
public static Props props(AppConfiguration config) { | ||
return Props.create(DiscoveryAgentActor.class, () -> new DiscoveryAgentActor(config)); | ||
} | ||
|
||
private final AppConfiguration configuration; | ||
private final Consul consul; | ||
private final AgentClient agentClient; | ||
private final FiniteDuration SCHEDULED_WORK_DELAY; | ||
|
||
public DiscoveryAgentActor(AppConfiguration configuration){ | ||
this.configuration = configuration; | ||
this.SCHEDULED_WORK_DELAY = new FiniteDuration(configuration.serviceDiscoveryConfiguration.healthCheckTimeout, TimeUnit.SECONDS); | ||
|
||
// todo: terminate system if error occur while connecting to consul | ||
// get consul connection | ||
this.consul = Consul | ||
.builder() | ||
.withHostAndPort( | ||
HostAndPort.fromParts( | ||
configuration.serviceDiscoveryConfiguration.host, | ||
configuration.serviceDiscoveryConfiguration.port) | ||
) | ||
.build(); | ||
|
||
// get agent | ||
agentClient = consul.agentClient(); | ||
// set registration config | ||
Registration service = ImmutableRegistration.builder() | ||
.id(configuration.appId) | ||
.name(configuration.serviceName) | ||
.port(configuration.port) | ||
.address(configuration.host) | ||
.check(Registration.RegCheck.ttl(configuration.serviceDiscoveryConfiguration.healthCheckTimeout)) | ||
.tags(Collections.singletonList("tag1")) | ||
.meta(Collections.singletonMap("version", "1.0")) | ||
.build(); | ||
|
||
// register service | ||
agentClient.register(service); | ||
// check in with Consul, serviceId required only. client will prepend "service:" for service level checks. | ||
// Note that you need to continually check in before the TTL expires, otherwise your service's state will be marked as "critical". | ||
} | ||
|
||
@Override | ||
public void preStart() { | ||
getSelf().tell("Do Scheduled Work", ActorRef.noSender()); | ||
} | ||
|
||
@Override | ||
public Receive createReceive() { | ||
return receiveBuilder() | ||
.matchEquals("Do Scheduled Work", work -> { | ||
sendHealthCheck(); | ||
context().system() | ||
// send each (10seconds default) health-check to consul | ||
.scheduler() | ||
.schedule( | ||
new FiniteDuration(5, TimeUnit.SECONDS), | ||
SCHEDULED_WORK_DELAY, | ||
healthCheck(), | ||
getContext().dispatcher() | ||
); | ||
}) | ||
.build(); | ||
} | ||
|
||
private void sendHealthCheck() { | ||
try { | ||
agentClient.pass(configuration.appId, configuration.serviceName +" alive and reachable"); | ||
} catch (NotRegisteredException e) { | ||
e.printStackTrace(); | ||
getContext().getSystem().terminate(); | ||
} | ||
System.out.println("Health check has been sent"); | ||
} | ||
|
||
private Runnable healthCheck() { | ||
return this::sendHealthCheck; | ||
} | ||
|
||
} |
43 changes: 43 additions & 0 deletions
43
example/src/main/java/ru/zhenik/akka/example/interfaces/rest/AppResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package ru.zhenik.akka.example.interfaces.rest; | ||
|
||
import static akka.http.javadsl.server.Directives.complete; | ||
import static akka.http.javadsl.server.Directives.get; | ||
import static akka.http.javadsl.server.Directives.path; | ||
import static akka.http.javadsl.server.Directives.post; | ||
import static akka.http.javadsl.server.Directives.route; | ||
|
||
import akka.http.javadsl.model.ContentTypes; | ||
import akka.http.javadsl.model.HttpEntities; | ||
import akka.http.javadsl.model.StatusCodes; | ||
import akka.http.javadsl.server.Route; | ||
import ru.zhenik.akka.example.AppConfiguration; | ||
|
||
public final class AppResource { | ||
|
||
public final AppConfiguration config; | ||
public final String healthResponse; | ||
|
||
public AppResource(AppConfiguration config) { | ||
this.config = config; | ||
this.healthResponse = String | ||
.format("ok \nservice-name:[%s]\napp-id:[%s]\nhost&port:[%s]", config.serviceName, | ||
config.appId, config.host + ":" + config.port); | ||
} | ||
|
||
public Route routes() { | ||
|
||
Route managementRoutes = path("health", () -> | ||
get(() -> | ||
complete(HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, healthResponse)) | ||
) | ||
); | ||
Route appRoutes = path("app", () -> | ||
post(() -> | ||
complete(StatusCodes.OK) | ||
) | ||
); | ||
|
||
return route(managementRoutes, appRoutes); | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
application { | ||
// use 0.0.0.0 instead localhost | ||
// https://github.com/moby/moby/issues/23082 | ||
host = "0.0.0.0" | ||
host = ${?APPLICATION_HOST} | ||
port = 3000 | ||
port = ${?APPLICATION_PORT} | ||
} | ||
// will be use when register in consul | ||
service.name = "example-app" | ||
|
||
// self registration pattern | ||
// https://microservices.io/patterns/self-registration.html | ||
discovery { | ||
host = "0.0.0.0" | ||
host = ${?DISCOVERY_HOST} | ||
port = 8500 | ||
port = ${?DISCOVERY_PORT} | ||
healthcheck-timeout = 10 | ||
healthcheck-timeout = ${?DISCOVERY_HEALTH_TIMEOUT} | ||
} |