多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > 服務器 > SkyDNS2源碼分析

SkyDNS2源碼分析

來源:程序員人生   發布時間:2017-01-13 10:58:49 閱讀次數:3267次

SkyDNS2是SkyDNS Version 2.x的統稱,其官方文檔只有README.md,網上能找到的資料也不多,因此需要我們自行對代碼進行1定的分析,才能對其有更好的理解,這就是本文的工作,通過走讀SkyDNS的代碼,了解其內部架構及其工作原理。

說明

  • SkyDNS2的github地址: https://github.com/skynetservices/skydns
  • Version: v2.5.3a

SkyDNS架構

關于SkyDNS是甚么?…. 這些知識,請前往官網了解。

下面我直接把我瀏覽代碼后理解的SkyDNS架構貼出來:

這里寫圖片描述

SkyDNS工作原理

SkyDNS Server的工作,依賴后端Key-Value存儲的支持。當前支持etcd或etcd3作為Backend(架構圖中藍色部份),為SkyDNS提供配置和數據的管理。

通過環境變量ETCD_MACHINES進行etcd cluster的配置,如果Backend為etcd3,還需要設置etcd中/v2/keys//skydns/config/etcd3為true。SkyDNS中有etcd client模塊,負責與ETCD_MACHINES的通訊。

SkyDNS主要對應的etcd key path以下:

/v2/keys/skydns/config
/v2/keys/skydns/local/skydns/east/production/rails
/v2/keys/skydns/local/skydns/dns/stub
/v2/keys/skydns/local/skydns/...

通過以下環境變量的配置,支持prometheus監控(架構圖中棕色部份)。如果想disable prometheus監控,則配置環境變量PROMETHEUS_PORT的值為0便可。

Port      = os.Getenv("PROMETHEUS_PORT")
Path      = envOrDefault("PROMETHEUS_PATH", "/metrics")
Namespace = envOrDefault("PROMETHEUS_NAMESPACE", "skydns")
Subsystem = envOrDefault("PROMETHEUS_SUBSYSTEM", "skydns")

如果/v2/keys/skydns/config/nameservers有值,則SkyDNS解析不了的Domain,會forward到對應的這些IP:Port構成的nameservers,由它們進行解析(架構圖中綠色部份)。

參考官方文檔https://github.com/skynetservices/skydns/blob/master/README.md完成參數配置后,即可啟動SkyDNS。

SkyDNS Server的啟動進程以下:

  • 創建etcd client對象;
  • dns_addr 和 nameservers參數合法性檢查;
  • 加載啟動參數到etcd,覆蓋/v2/keys/skydns/config中原有數據;
  • 配置SkyDNS Server參數的default值,并創建SkyDNS server對象;
  • 去etcd中加載…/dns/stub//xx數據作為server的stub zones數據,并啟動對…/dns/stub/的watcher,1旦有數據更新,就加載到server的stub zones數據中;
  • 注冊SkyDNS metrics到prometheus;
  • 然后在/v2/keys/skydns/config/dns_addr配置的interface和port上開啟tcp/udp監聽服務并block住,由此開始提供DSN服務。

在github.com/skynetservices/skydns/server/server.go中的ServeDNS方法覆蓋了miekg/dns/server中的ServeMux.ServeDNS方法,由自實現的ServeDNS提供來處理DNS client的要求。

github.com/skynetservices/skydns/server/server.go

// ServeDNS is the handler for DNS requests, responsible for parsing DNS request, possibly forwarding
// it to a real dns server and returning a response.
func (s *server) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {
    ...
    // Check cache first.
    m1 := s.rcache.Hit(q, dnssec, tcp, m.Id)
    if m1 != nil {
        ...
        // Still round-robin even with hits from the cache.
        // Only shuffle A and AAAA records with each other.
        if q.Qtype == dns.TypeA || q.Qtype == dns.TypeAAAA {
            s.RoundRobin(m1.Answer)
        }

        ...
        return
    }

    for zone, ns := range *s.config.stub {
        if strings.HasSuffix(name, "." + zone) || name == zone {
            metrics.ReportRequestCount(req, metrics.Stub)

            resp := s.ServeDNSStubForward(w, req, ns)
            if resp != nil {
                s.rcache.InsertMessage(cache.Key(q, dnssec, tcp), resp)
            }

            metrics.ReportDuration(resp, start, metrics.Stub)
            metrics.ReportErrorCount(resp, metrics.Stub)
            return
        }
    }
    ...

    if name == s.config.Domain {
        if q.Qtype == dns.TypeSOA {
            m.Answer = []dns.RR{s.NewSOA()}
            return
        }
        if q.Qtype == dns.TypeDNSKEY {
            if s.config.PubKey != nil {
                m.Answer = []dns.RR{s.config.PubKey}
                return
            }
        }
    }
    if q.Qclass == dns.ClassCHAOS {
        if q.Qtype == dns.TypeTXT {
            switch name {
            case "authors.bind.":
                fallthrough
            case s.config.Domain:
                hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
                authors := []string{"Erik St. Martin", "Brian Ketelsen", "Miek Gieben", "Michael Crosby"}
                for _, a := range authors {
                    m.Answer = append(m.Answer, &dns.TXT{Hdr: hdr, Txt: []string{a}})
                }
                for j := 0; j < len(authors)*(int(dns.Id())%4+1); j++ {
                    q := int(dns.Id()) % len(authors)
                    p := int(dns.Id()) % len(authors)
                    if q == p {
                        p = (p + 1) % len(authors)
                    }
                    m.Answer[q], m.Answer[p] = m.Answer[p], m.Answer[q]
                }
                return
            case "version.bind.":
                fallthrough
            case "version.server.":
                hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
                m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{Version}}}
                return
            case "hostname.bind.":
                fallthrough
            case "id.server.":
                // TODO(miek): machine name to return
                hdr := dns.RR_Header{Name: q.Name, Rrtype: dns.TypeTXT, Class: dns.ClassCHAOS, Ttl: 0}
                m.Answer = []dns.RR{&dns.TXT{Hdr: hdr, Txt: []string{"localhost"}}}
                return
            }
        }
        // still here, fail
        m.SetReply(req)
        m.SetRcode(req, dns.RcodeServerFailure)
        return
    }

    switch q.Qtype {
    case dns.TypeNS:
        if name != s.config.Domain {
            break
        }
        // Lookup s.config.DnsDomain
        records, extra, err := s.NSRecords(q, s.config.dnsDomain)
        if isEtcdNameError(err, s) {
            m = s.NameError(req)
            return
        }
        m.Answer = append(m.Answer, records...)
        m.Extra = append(m.Extra, extra...)
    case dns.TypeA, dns.TypeAAAA:
        records, err := s.AddressRecords(q, name, nil, bufsize, dnssec, false)
        if isEtcdNameError(err, s) {
            m = s.NameError(req)
            return
        }
        m.Answer = append(m.Answer, records...)
    case dns.TypeTXT:
        records, err := s.TXTRecords(q, name)
        if isEtcdNameError(err, s) {
            m = s.NameError(req)
            return
        }
        m.Answer = append(m.Answer, records...)
    case dns.TypeCNAME:
        records, err := s.CNAMERecords(q, name)
        if isEtcdNameError(err, s) {
            m = s.NameError(req)
            return
        }
        m.Answer = append(m.Answer, records...)
    case dns.TypeMX:
        records, extra, err := s.MXRecords(q, name, bufsize, dnssec)
        if isEtcdNameError(err, s) {
            m = s.NameError(req)
            return
        }
        m.Answer = append(m.Answer, records...)
        m.Extra = append(m.Extra, extra...)
    default:
        fallthrough // also catch other types, so that they return NODATA
    case dns.TypeSRV:
        records, extra, err := s.SRVRecords(q, name, bufsize, dnssec)
        if err != nil {
            if isEtcdNameError(err, s) {
                m = s.NameError(req)
                return
            }
            logf("got error from backend: %s", err)
            if q.Qtype == dns.TypeSRV { // Otherwise NODATA
                m = s.ServerFailure(req)
                return
            }
        }
        // if we are here again, check the types, because an answer may only
        // be given for SRV. All other types should return NODATA, the
        // NXDOMAIN part is handled in the above code. TODO(miek): yes this
        // can be done in a more elegant manor.
        if q.Qtype == dns.TypeSRV {
            m.Answer = append(m.Answer, records...)
            m.Extra = append(m.Extra, extra...)
        }
    }

    if len(m.Answer) == 0 { // NODATA response
        m.Ns = []dns.RR{s.NewSOA()}
        m.Ns[0].Header().Ttl = s.config.MinTtl
    }
}

上面代碼邏輯比較復雜,細節上需要你漸漸去理解,簡短的可以總結以下:

  • 如架構圖中標注的線路1:如果在SkyDNS保護的cache中找到對應Msg,則從cache中讀取并返回Msg給DNS client;
  • 如架構圖中標注的線路2:如果在cache中沒有對應的記錄,并且是需要DNS forward的場景(比如name匹配到stub zones等),則將要求forward到對應的DNS servers進行處理;
  • 如架構圖中標注的線路3:如果在cache中沒有對應的記錄,并且Question Type為A/AAAA,SRV等類型時,就通過etcd client去etcd cluster中獲得對應的Rule,并構造Msg返回。

總結

通過走讀SkyDNS的代碼,了解其內部架構及其工作原理。

Mark

SkyDNS2 Changes since version 1:

  • Does away with Raft and uses etcd (which uses raft).
  • Makes it possible to query arbitrary domain names.
  • Is a thin layer above etcd, that translates etcd keys and values to the DNS.
  • Does DNSSEC with NSEC3 instead of NSEC.
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 最近的中文字幕视频完整 | 亚洲精品一区二区三区国产 | 中文字幕一区二区在线观看 | 性做久久久久免费观看 | 一级做a爰片久久毛片图片 一级做a爰片欧美aaaa | 欧美性性性性性色大片免费的 | 男18视频在线观看 | 欧美激情伊人 | 亚洲不卡在线视频 | 中文字幕无限乱码不卡2021 | 国产精品jizz视频 | 亚洲免费网站在线观看 | 高清中文字幕视频在线播 | www.免费视频| www.日本高清视频 | 欧美日韩亚洲精品一区二区 | 欧美区亚洲区 | 性的小视频在线观看免费 | 日韩中文字幕精品免费一区 | 黄xxxx| 91桃色观看免费高清 | 亚洲精品午夜久久aaa级久久久 | 国产区成人综合色在线 | 亚洲片在线 | 日本高清wwww免费视频 | 日本韩国在线视频 | 亚洲精品第一页中文字幕 | 波多洁野衣一二区三区| 久久se精品一区二区影院 | 亚洲香蕉一区二区三区在线观看 | 欧美一区二区影院 | 另类图片综合 | 欧美一级高清在线观看 | 成人国产在线视频在线观看 | 久久国产精品永久免费网站 | 免费一级毛片在线视频观看 | 国产成人乱码一区二区三区在线 | 国产精品久久亚洲一区二区 | 日本孕妇大胆孕交无码 | 中国毛片网 | 亚洲一区二区精品视频 |