Scala语言如何使用Akka HTTP查询及管理A、SRV类型DNS记录
DNS记录类型区分及Akka HTTP查询SRV记录实现方案
A记录与SRV记录的核心区别
- A记录:是地址解析记录,直接将域名映射到对应的IPv4地址,你之前用的
InetAddress.getAllByName本质就是查询A记录,所以只能拿到IP列表,无法识别其他类型的DNS记录 - SRV记录:是服务定位记录,除了对应服务域名外,还会返回服务的优先级、权重、端口号,专门用于定位特定协议/端口的服务,格式为
_服务名._协议名.域名,你给出的示例_thrift._tcp.prova.geo.server.com.就是标准的SRV记录格式
基于Scala + Akka HTTP的SRV记录查询实现
首先确保你的项目引入了对应版本的Akka IO依赖,sbt配置参考如下:
// 版本号需和项目现有Akka版本保持一致 libraryDependencies += "com.typesafe.akka" %% "akka-io" % "2.8.5"
完整实现代码示例:
import akka.actor.ActorSystem import akka.io.dns.{ARecord, DnsProtocol, SRVRecord} import akka.io.{Dns, IO} import akka.pattern.ask import akka.util.Timeout import scala.concurrent.Future import scala.concurrent.duration._ // 定义SRV查询结果结构,方便业务侧使用 case class SrvTarget(priority: Int, weight: Int, port: Int, targetDomain: String, ipList: List[String]) class SrvDnsManager(implicit actorSystem: ActorSystem) { // 配置DNS查询超时时间 private implicit val queryTimeout: Timeout = 3.seconds private val dnsActor = IO(Dns)(actorSystem) import actorSystem.dispatcher /** * 查询指定SRV域名的记录,同时自动解析目标域名的A记录 * @param srvDomain 待查询的SRV域名,例如:_thrift._tcp.prova.geo.server.com * @return 解析完成的SRV目标列表异步结果 */ def getSrvRecords(srvDomain: String): Future[List[SrvTarget]] = { // 发起指定类型为SRV的DNS查询请求 val srvQuery = (dnsActor ? DnsProtocol.Resolve(srvDomain, DnsProtocol.SRV)).mapTo[DnsProtocol.Resolved] srvQuery.flatMap { resolvedResult => // 过滤提取所有SRV记录 val srvRecords = resolvedResult.records.collect { case record: SRVRecord => record } // 批量查询每个SRV记录对应目标域名的IP地址 val targetResultFutures = srvRecords.map { srvRecord => val ipQuery = (dnsActor ? DnsProtocol.Resolve(srvRecord.target)).mapTo[DnsProtocol.Resolved] ipQuery.map { ipRes => val ips = ipRes.records.collect { case aRecord: ARecord => aRecord.ip.getHostAddress }.toList SrvTarget( priority = srvRecord.priority, weight = srvRecord.weight, port = srvRecord.port, targetDomain = srvRecord.target, ipList = ips ) } } Future.sequence(targetResultFutures.toList) } } } // 调用示例 object SrvQueryDemo extends App { implicit val system: ActorSystem = ActorSystem("SrvQuerySystem") val srvManager = new SrvDnsManager() val testSrvDomain = "_thrift._tcp.prova.geo.server.com" srvManager.getSrvRecords(testSrvDomain).map { records => println(s"共查询到${records.size}条SRV记录:") records.foreach { record => println(s"优先级:${record.priority} 权重:${record.weight} 端口:${record.port} 域名:${record.targetDomain} IP列表:${record.ipList.mkString(",")}") } }.recover { case e: Exception => println(s"SRV记录查询失败:${e.getMessage}") } }
注意事项
InetAddress.getAllByName系统方法默认仅支持查询A/AAAA记录,不支持SRV等其他DNS记录类型,这是原有代码无法获取SRV记录的根本原因- Akka DNS默认使用系统配置的DNS服务器,可通过Akka配置项
akka.io.dns.resolver自定义指定DNS服务器地址 - 生产环境使用建议添加查询结果本地缓存,避免频繁发起DNS请求影响性能
内容的提问来源于stack exchange,提问作者Riccardo Califano
相关产品推荐
相关产品推荐

