diff --git a/akka-actor/src/main/mima-filters/2.5.16.backwards.excludes b/akka-actor/src/main/mima-filters/2.5.16.backwards.excludes index 5d9ad3dc39..032c45ddc8 100644 --- a/akka-actor/src/main/mima-filters/2.5.16.backwards.excludes +++ b/akka-actor/src/main/mima-filters/2.5.16.backwards.excludes @@ -1,3 +1,5 @@ # #25435 - Adding maximum restart attempts to BackoffSupervisor ProblemFilters.exclude[ReversedMissingMethodProblem]("akka.pattern.BackoffOptions.withMaxNrOfRetries") +# Change of a method name with ApiMayChange +ProblemFilters.exclude[DirectMissingMethodProblem]("akka.io.dns.DnsProtocol#Resolve.create") diff --git a/akka-actor/src/main/scala/akka/io/Dns.scala b/akka-actor/src/main/scala/akka/io/Dns.scala index fa64c99985..a5a1e895ab 100644 --- a/akka-actor/src/main/scala/akka/io/Dns.scala +++ b/akka-actor/src/main/scala/akka/io/Dns.scala @@ -13,7 +13,17 @@ import com.typesafe.config.Config import scala.collection.{ breakOut, immutable } abstract class Dns { + + /** + * Lookup if a DNS resolved is cached. The exact behavior of caching will depend on + * the akka.actor.io.dns.resolver that is configured. + */ def cached(name: String): Option[Dns.Resolved] = None + + /** + * If an entry is cached return it immediately. If it is not then + * trigger a resolve and return None. + */ def resolve(name: String)(system: ActorSystem, sender: ActorRef): Option[Dns.Resolved] = { val ret = cached(name) if (ret.isEmpty) @@ -51,10 +61,18 @@ object Dns extends ExtensionId[DnsExt] with ExtensionIdProvider { } } + /** + * Lookup if a DNS resolved is cached. The exact behavior of caching will depend on + * the akka.actor.io.dns.resolver that is configured. + */ def cached(name: String)(system: ActorSystem): Option[Resolved] = { Dns(system).cache.cached(name) } + /** + * If an entry is cached return it immediately. If it is not then + * trigger a resolve and return None. + */ def resolve(name: String)(system: ActorSystem, sender: ActorRef): Option[Resolved] = { Dns(system).cache.resolve(name)(system, sender) } @@ -74,6 +92,7 @@ class DnsExt(val system: ExtendedActorSystem) extends IO.Extension { val Settings = new Settings(system.settings.config.getConfig("akka.io.dns")) class Settings private[DnsExt] (_config: Config) { + import _config._ val Dispatcher: String = getString("dispatcher") diff --git a/akka-actor/src/main/scala/akka/io/dns/DnsProtocol.scala b/akka-actor/src/main/scala/akka/io/dns/DnsProtocol.scala index 67bdf54b68..6e75fa8ba3 100644 --- a/akka-actor/src/main/scala/akka/io/dns/DnsProtocol.scala +++ b/akka-actor/src/main/scala/akka/io/dns/DnsProtocol.scala @@ -48,18 +48,18 @@ object DnsProtocol { object Resolve { def apply(name: String): Resolve = Resolve(name, Ip()) - - /** - * Java API - */ - def create(name: String): Resolve = Resolve(name, Ip()) - - /** - * Java API - */ - def create(name: String, requestType: RequestType): Resolve = Resolve(name, requestType) } + /** + * Java API + */ + def resolve(name: String): Resolve = Resolve(name, Ip()) + + /** + * Java API + */ + def resolve(name: String, requestType: RequestType): Resolve = Resolve(name, requestType) + /** * @param name of the record * @param records resource records for the query diff --git a/akka-docs/src/main/paradox/index-network.md b/akka-docs/src/main/paradox/index-network.md index 4b0c655844..f35f593501 100644 --- a/akka-docs/src/main/paradox/index-network.md +++ b/akka-docs/src/main/paradox/index-network.md @@ -10,6 +10,7 @@ * [io](io.md) * [io-tcp](io-tcp.md) * [io-udp](io-udp.md) +* [io-dns](io-dns.md) * [camel](camel.md) @@@ diff --git a/akka-docs/src/main/paradox/io-dns.md b/akka-docs/src/main/paradox/io-dns.md new file mode 100644 index 0000000000..0b02c695e5 --- /dev/null +++ b/akka-docs/src/main/paradox/io-dns.md @@ -0,0 +1,74 @@ +# DNS Extension + +@@@ note + +The `async-dns` API is marked as `ApiMayChange` as more information is expected to be added to the protocol. + +@@@ + +Akka DNS is a pluggable way to interact with DNS. Implementations much implement `akka.io.DnsProvider` and provide a configuration +block that specifies the implementation via `provider-object`. + +To select which `DnsProvider` to use set `akka.io.dns.resolver ` to the location of the configuration. + +There are currently two implementations: + +* `inet-address` - Based on the JDK's `InetAddress`. Using this will be subject to both the JVM's DNS cache and its built in one. +* `async-dns` - A native implemention of the DNS protocol that does not use any JDK classes or caches. + +`inet-address` is the default implementation as it pre-dates `async-dns`, `async-dns` will likely become the default in the next major release. + +DNS lookups can be done via the `DNS` extension: + +Scala +: @@snip [DnsCompileOnlyDocSpec.scala](/akka-docs/src/test/scala/docs/actor/io/dns/DnsCompileOnlyDocSpec.scala) { #resolve } + +Java +: @@snip [DnsCompileOnlyDocTest.java](/akka-docs/src/test/java/jdocs/actor/io/dns/DnsCompileOnlyDocTest.java) { #resolve } + +Alternatively the `IO(Dns)` actor can be interacted with directly. However this exposes the different protocols of the DNS provider. +`inet-adddress` uses `Dns.Resolved` and `Dns.Resolved` where as the `async-dns` uses `DnsProtocol.Resolve` and `DnsProtocol.Resolved`. +The reason for the difference is `inet-address` predates `async-dns` and `async-dns` exposes additional information such as SRV records +and it wasn't possible to evolve the original API in a backward compatible way. + +Inet-Address API: + +Scala +: @@snip [IODocSpec.scala](/akka-docs/src/test/scala/docs/actor/io/dns/DnsCompileOnlyDocSpec.scala) { #actor-api-inet-address } + +Java +: @@snip [DnsCompileOnlyDocTest.java](/akka-docs/src/test/java/jdocs/actor/io/dns/DnsCompileOnlyDocTest.java) { #actor-api-inet-address } + +Async-DNS API: + +Scala +: @@snip [IODocSpec.scala](/akka-docs/src/test/scala/docs/actor/io/dns/DnsCompileOnlyDocSpec.scala) { #actor-api-async } + +Java +: @@snip [DnsCompileOnlyDocTest.java](/akka-docs/src/test/java/jdocs/actor/io/dns/DnsCompileOnlyDocTest.java) { #actor-api-async } + +The Async DNS provider has the following advantages: + +* No JVM DNS caching. It is expected that future versions will expose more caching related information. +* No blocking. `InetAddress` resolving is a blocking operation. +* Exposes `SRV`, `A` and `AAAA` records. + + +## SRV Records + +To get DNS SRV records `akka.io.dns.resolver` must be set to `async-dns` and `DnsProtocol.Resolve`'s requestType +must be set to `DnsProtocol.Srv` + +Scala +: @@snip [IODocSpec.scala](/akka-docs/src/test/scala/docs/actor/io/dns/DnsCompileOnlyDocSpec.scala) { #srv } + +Java +: @@snip [DnsCompileOnlyDocTest.java](/akka-docs/src/test/java/jdocs/actor/io/dns/DnsCompileOnlyDocTest.java) { #srv } + +The `DnsProtocol.Resolved` will contain `akka.io.dns.SRVRecord`s. + + + + + + diff --git a/akka-docs/src/test/java/jdocs/actor/io/dns/DnsCompileOnlyDocTest.java b/akka-docs/src/test/java/jdocs/actor/io/dns/DnsCompileOnlyDocTest.java new file mode 100644 index 0000000000..7735257c04 --- /dev/null +++ b/akka-docs/src/test/java/jdocs/actor/io/dns/DnsCompileOnlyDocTest.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2018 Lightbend Inc. + */ + +package jdocs.actor.io.dns; + +import akka.actor.ActorRef; +import akka.actor.ActorSystem; +import akka.io.Dns; +import akka.io.IO; +import akka.io.dns.DnsProtocol; + +import static akka.pattern.PatternsCS.ask; +import static akka.pattern.PatternsCS.pipe; + +import scala.Option; + +import java.util.concurrent.CompletionStage; + + +public class DnsCompileOnlyDocTest { + public static void example() { + ActorSystem system = ActorSystem.create(); + + ActorRef actorRef = null; + long timeout = 1000; + + //#resolve + Option initial = Dns.get(system).cache().resolve("google.com", system, actorRef); + Option cached = Dns.get(system).cache().cached("google.com"); + //#resolve + + { + //#actor-api-inet-address + final ActorRef dnsManager = Dns.get(system).manager(); + CompletionStage resolved = ask(dnsManager, new Dns.Resolve("google.com"), timeout); + //#actor-api-inet-address + + } + + { + //#actor-api-async + final ActorRef dnsManager = Dns.get(system).manager(); + CompletionStage resolved = ask(dnsManager, DnsProtocol.resolve("google.com"), timeout); + //#actor-api-async + } + + { + //#srv + final ActorRef dnsManager = Dns.get(system).manager(); + CompletionStage resolved = ask(dnsManager, DnsProtocol.resolve("google.com", DnsProtocol.srvRequestType()), timeout); + //#srv + } + + + } +} diff --git a/akka-docs/src/test/scala/docs/actor/io/dns/DnsCompileOnlyDocSpec.scala b/akka-docs/src/test/scala/docs/actor/io/dns/DnsCompileOnlyDocSpec.scala new file mode 100644 index 0000000000..406a5d944d --- /dev/null +++ b/akka-docs/src/test/scala/docs/actor/io/dns/DnsCompileOnlyDocSpec.scala @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 Lightbend Inc. + */ + +package docs.actor.io.dns + +import akka.actor.{ ActorRef, ActorSystem } +import akka.io.dns.DnsProtocol +import akka.io.dns.DnsProtocol.Srv +import akka.pattern.ask +import akka.io.{ Dns, IO } +import akka.util.Timeout + +import scala.concurrent.Future +import scala.concurrent.duration._ + +object DnsCompileOnlyDocSpec { + + implicit val system = ActorSystem() + implicit val timeout = Timeout(1.second) + + val actorRef: ActorRef = ??? + //#resolve + val initial: Option[Dns.Resolved] = Dns(system).cache.resolve("google.com")(system, actorRef) + val cached: Option[Dns.Resolved] = Dns(system).cache.cached("google.com") + //#resolve + + { + //#actor-api-inet-address + val resolved: Future[Dns.Resolved] = (IO(Dns) ? Dns.Resolve("google.com")).mapTo[Dns.Resolved] + //#actor-api-inet-address + + } + + { + //#actor-api-async + val resolved: Future[DnsProtocol.Resolved] = (IO(Dns) ? DnsProtocol.Resolve("google.com")).mapTo[DnsProtocol.Resolved] + //#actor-api-async + } + + { + //#srv + val resolved: Future[DnsProtocol.Resolved] = (IO(Dns) ? DnsProtocol.Resolve("your-service", Srv)) + .mapTo[DnsProtocol.Resolved] + //#srv + } + +} +