百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

gRPC-go框架中dns解析器的介绍以及实战测试

ccwgpt 2024-10-13 01:32 135 浏览 0 评论

本篇文章主要是分析一下dnsResolver类型的解析器的核心原理;并且进行实际测试;最后分析一下,当dnsResolver解析失败时,实现重试机制的原理;

1、dnsResolver解析器原理介绍

dns解析器的原理,其实底层调用的是golang自带net包中的LookupHost、LookupSRV、LookupTXT三个函数来实现解析的。通过这三个函数远程去访问dns服务器,最终将用户设置的链接地址转换成后端服务器地址列表。

直接进入grpc-go/internal/resolver/dns/dns_resolver.go中的Build方法:

1.// Build creates and starts a DNS resolver that watches the name resolution of the target.
2.// resolver_conn_wrapper.go 文件里的newCCResolverWrapper方法里调用
3.func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
4.   host, port, err := parseTarget(target.Endpoint, defaultPort)
5.   if err != nil {
6.      return nil, err
7.   }

8.   // IP address.
9.   if ipAddr, ok := formatIP(host); ok {
10.      addr := []resolver.Address{{Addr: ipAddr + ":" + port}}
11.      cc.UpdateState(resolver.State{Addresses: addr})
12.      return deadResolver{}, nil
13.   }

14.   // DNS address (non-IP).
15.   ctx, cancel := context.WithCancel(context.Background())
16.   d := &dnsResolver{
17.      host:                 host,
18.      port:                 port,
19.      ctx:                  ctx,
20.      cancel:               cancel,
21.      cc:                   cc,
22.      rn:                   make(chan struct{}, 1),
23.      disableServiceConfig: opts.DisableServiceConfig,
24.   }

25.   if target.Authority == "" {
26.      d.resolver = defaultResolver
27.   } else {
28.      d.resolver, err = customAuthorityResolver(target.Authority)
29.      if err != nil {
30.         return nil, err
31.      }
32.   }

33.   d.wg.Add(1)
34.   go d.watcher()
35.   d.ResolveNow(resolver.ResolveNowOptions{})
36.   return d, nil
37.}

主要代码说明:

  • 第4行:主要是对用户输入的target.Endpoint进行解析,如localhost:50051,sayHello:50051;
  • 第8-13行:如果用户输入的是具体的IP(如192.168.43.214),而不是域名的话,就直接更新cc的状态,最终实现向grpc服务器端发起连接
  • 第16-24行:构建dnsResolver构建结构体
  • 第25-32行:核心目的就是初始化dnsResolver结构体中的resolver,类型是netResolver接口
  • 第34行:启动一个协程,监听;去向dns服务器发起请求,获取注册到dns服务器上的后端服务器地址列表
  • 第35行:调用解析器的ResolveNow方法;

进入ResolveNow方法内部看看:

func (d *dnsResolver) ResolveNow(resolver.ResolveNowOptions) {
   select {
   case d.rn <- struct{}{}:
   default:
   }
}

使用了多路复用器,这里执行的是case分支;如果case不满足的情况下,会执行default。要结合第34行watcher方法里的第7行一起看。
从ResolveNow方法的名称,可以看出来,就是开始解析的意思;其实就是告诉wacther方法里第7行的通道d.rn发送信号,目的解除阻塞,继续执行下面的代码。
点击第34行,进入watcher方法里:

1.func (d *dnsResolver) watcher() {
2.   defer d.wg.Done()
3.   for {
4.      select {
5.      case <-d.ctx.Done():
6.         return
7.      case <-d.rn:
8.      }

9.      state, err := d.lookup()
10.      if err != nil {
11.         d.cc.ReportError(err)
12.      } else {
13.         d.cc.UpdateState(*state)
14.      }

15.      // Sleep to prevent excessive re-resolutions. Incoming resolution requests
16.      // will be queued in d.rn.
17.      t := time.NewTimer(minDNSResRate)
18.      select {
19.      case <-t.C:
20.      case <-d.ctx.Done():
21.           t.Stop()
22.           return
23.      }
24.   }
25.}

watcher方法的核心目的
??调用第9行实现dns解析,获得后端grpc服务器地址,传递给第13行,从而实现grpc客户端向grpc服务器端发起连接请求。
主要代码说明:

  • 第4-8行:是一个select实现的多路复用器;有什么作用呢?
    • 提供退出for循环的出口
    • d.ctx.Done(),如果给解析器的上下文发送结束信号,这里就可以退出for循环了;
    • 第7行:d.rn通道处于阻塞的情况时,阻止程序继续往下执行,可以防止多次dns解析ResolveNow方法里的语句case d.rn <- struct{}{}:可以解除阻塞。
  • 第9行: 调用lookup实现dns地址解析;内部调用的是golang原生自带的net包中的lookupSRV、lookupHost、lookupTXT方法,对三个方法获得的结果转换成grpc框架中的resolver.State结构体即可。这就是dns解析器最核心的原理,最终将用户输入的地址target转换成了grpc服务器地址;
  • 第13行:更新State,最终实现的是向grpc服务器端发起链接请求
  • 第17-23行:使用timer+select实现一个定时器。minDNSResRate的值就是30秒;如果定时器结束时就会执行第19行,如果解析器结束时就会执行第20行。这个定时器有什么目的呢?
    • 如果dns解析失败了的话,防止频繁的解析
    • 解析器结束时,退出for循环

??这个方法使用了for循环,两个select,目的应该是在dns解析失败的情况下,可以提供重试机制;并不是所有的解析器都有重试机制,比方说passthrough解析器里就没有。
??这里为什么会有重试机制呢?
??可能是dns解析器拿到的地址是域名,并不是后端提供服务的地址,需要向dns服务器发起请求来获得;(这里仅仅是一个猜测,或者不同的解析器可以根据实际情况来考虑要不要提供)

2、dnsResolver实际测试

2.1、测试环境说明

我这里的测试环境是在Mac物理机上启动了grpc客户端,grpc服务器端,启动虚拟机;其中虚拟机里安装了centos系统,在centos系统里以容器的方式启动了coredns服务器。具体看下图所示:

如果想了解更多,可以参考下面链接

相关推荐

VUE3前端开发入门系列教程二:使用iView框架辅助开发

1、安装iView新框架,支持VUE3npminstallview-ui-plus2、编辑src/main.js,添加以下内容,导入js和css到项目importViewUIPlusfrom...

万能前端框架uni app初探03:底部导航开发

前言本节我们使用uniapp的底部导航功能,点击不同tab会显示不同页面,这个功能在实际项目开发中几乎是必备的。一、基础知识1.tabBar如果应用是一个多tab应用,可以通过tabBar配...

Rust Web 开发框架,前端你可以选择哪个?

Rust构建一切。在如今流行的语言中,Rust可谓是将构建和高效作为自己优美的身姿在大众视野中脱颖而出。它是一门赋予每个人构建可靠且高效软件能力的语言。它有什么特性呢?高性能。Rust速度惊人且内...

连载:前端开发中纠结的Javascript框架(上)

如今,前端开发有着许许多多的框架和库。其中一些好用,一些却不尽人意。通常我们会习惯性运用某一概念,模块或句法。事实上,并没有什么万能工具。这篇文章是关于未来框架的发展趋势——那就是没有框架!我从以下几...

前端开发框架的演进架构:提升用户体验和开发效率

前端开发框架是现代Web应用开发的重要工具,它不仅可以帮助开发者构建复杂的用户界面,还能够提升用户体验和开发效率。随着Web技术的不断发展,前端开发框架也在不断演进,为开发者提供了更丰富、更高效的工具...

Google应用Mesh-TensorFlow框架,让CNN也能处理超高分辨率图像

为了要处理超高分辨率医疗图像数据,Google开发了一种空间数据分区(SpatialPartition)技术,在不牺牲图像分辨率的条件下,分析超高分辨率图像。Google使用Mesh-TensorF...

大模型安全挑战加剧:框架层漏洞成新靶心

近日,360数字安全集团发布了一份关于大模型安全漏洞的报告,揭示了当前大模型及围绕其构建的框架和应用中存在的严重安全问题。报告显示,360近期研究发现了近40个大模型相关的安全漏洞,其中既包括二进制内...

Keras 3.0正式发布:可用于TensorFlow、JAX和PyTorch

机器之心报道编辑:陈萍经过5个月的更新迭代,Keras3.0终于来了。「大新闻:我们刚刚发布了Keras3.0版本!」Keras之父FrancoisChollet在X上激动的...

TensorFlow和Keras入门必读教程(tensorflow与keras版本对应)

导读:本文对TensorFlow的框架和基本示例进行简要介绍。作者:本杰明·普朗什(BenjaminPlanche)艾略特·安德烈斯(EliotAndres)来源:华章科技01TensorFlo...

谷歌官方回应“TensorFlow遭弃”:还在投资开发,将与JAX并肩作战

鱼羊发自凹非寺量子位|公众号QbitAI终于,谷歌出面回应“TensorFlow遭弃”传闻:我们将继续致力于将TensorFlow打造为一流机器学习平台,与JAX并肩推动机器学习研究。这段时...

2025 年的PHP :现代 Web 开发的强大引擎

程序员还在吐槽PHP过时?2025年的PHP8.4直接封神了。看看最近更新的属性钩子、强类型系统,加上Laravel这些框架,老语言早就脱胎换骨。十年前说PHP弱类型容易崩代码的,现在脸疼不?联合类...

前端内卷终结者?htmx如何让开发者告别200行JS只做一个按钮

当你用React写一个点赞按钮需要引入3个状态管理库、编写80行JSX和120行钩子函数时,htmx只需要一行HTML:<buttonhx-post="/like"hx-sw...

NativePHP桌面版V1.0正式发布(元气桌面电脑版下载)

导读:各位小伙伴,使用PHP构建桌面级系统的利器,NativePHP来了。概述NativePHP是一个用于使用PHP构建桌面应用的框架。它允许PHP开发人员使用熟悉的工具和技术创建跨平台的原生应用...

PHP Laravel框架底层机制(php基本框架)

当然可以,Laravel是最受欢迎的PHP框架之一,以优雅的语法和丰富的生态而闻名。尽管开发体验非常“高端”,它的底层其实是由一系列结构清晰、职责分明的组件构成的。下面我从整体架构、核心流程、...

PHP框架之Laravel框架教程:2. 控制器、路由、视图简单介绍

2.控制器、路由、视图简单介绍我们先建立控制器,目录是:app/Http/Controllers,新建控制器Ding.php,代码如下:Ding.php:<?phpnamespaceA...

取消回复欢迎 发表评论: