對udp dns的一次思考

2022-06-16 03:57:12 字數 4405 閱讀 5963

目前昨天查乙個線上問題:「」dns伺服器在我們的裝置, 有大量的終端到裝置上請求解析網域名稱,但是一直是單執行緒,dns報文處理不過來」, 然而裝置是多核,dns伺服器一直不能利用多核資源,所以能不能使用多執行緒進行處理呢?

udp不像tcp那樣,udp沒有連線的概念,也就是沒有通過建立多個連線來提高對dns伺服器併發訪問,然而在多核環境下那就只能通過多執行緒來訪問乙個共享的udp socket,但是還是乙個socket , 會涉及到多執行緒搶占資源問題。

來看一下核心協議棧udp收到包**:根據以前分析tcpip協議棧文章可以知道,報文在核心協議棧流程大約如下:

也就是 報文送到那個socket是由__udp4_lib_lookup這個函式做出選擇, 選擇的依據是 ip 埠號 介面來進行處理選擇對應的socket,對於

dhcp dns伺服器來說一般不會繫結介面,所以一般就是 設定ip  udp.port, 所以核心選擇socket的時候一般也是通過對比ip port來查詢。通過找出匹配層度最高的socket作為收包sk。

如果要是允許有多個socket呢??

那麼不就是可以通過輪詢選擇或者hash選擇出對應的socket 嗎!!!!!!!!所以在linux 3.9核心版本後面增加reuseport,允許多個socket繫結同乙個ip port, 通過hash雜湊在桶裡面,

socket  bind ip port時 會呼叫get_port  計算是否存在ip port存在衝突, linux 3.9patch中對hash 以及計算方式加入reuseport,可以允許多個socket bind同樣的ip port。

同乙個客戶端的資料總是分配給同乙個 udp_sock。so!! 在寫 udp server 的時候,為了提高處理能力,可以起多個執行緒,每個執行緒讀寫自己的 udp socket

順便看一看udp 怎樣查詢port:

int udp_v4_get_port(struct sock *sk, unsigned short snum)

udp 對於porttable維護乙個是一port 進行hash  乙個是以sip +port(port=0) 進行hash。

struct udp_sock

udp 協議的主要資料結構是兩張 hash 表,指向 udp 協議控制塊 struct udp_sock。其中 hash1 以 port 為 key,

hash2 以 ip+port (port=0)為 key 但是後續會使用udp_portaddr_hash  ^port 進行hash查詢,實際上也就是ip+port表 ;

所以一開始看的udp_portaddr_hash 是以ip+port=0 進行hash計算有點懵逼!!!

1 int udp_lib_get_port(struct sock *sk, unsigned short snum,

2 int (*saddr_comp)(const struct sock *sk1,

3 const struct sock *sk2,

4bool match_wildcard),

5unsigned int hash2_nulladdr)

6 else

72if

(exist)

73goto fail_unlock;

74else

75goto found;76}

77scan_primary_hash://scan_primary_hash**段是在hash表的hslot項中查詢,只有當在hash2中查詢更費時時才會執行

78if

(udp_lib_lport_inuse(net, snum, hslot, null, sk,

79saddr_comp, 0))

80goto fail_unlock;81}

82found://執行sk的插入操作

83 inet_sk(sk)->inet_num =snum;

84 udp_sk(sk)->udp_port_hash =snum;

85 udp_sk(sk)->udp_portaddr_hash ^=snum;

86if

(sk_unhashed(sk))

9495 sk_add_node_rcu(sk, &hslot->head);

96 hslot->count++;

97 sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1);

9899 hslot2 = udp_hashslot2(udptable, udp_sk(sk)->udp_portaddr_hash);

100 spin_lock(&hslot2->lock);

101if (is_enabled(config_ipv6) && sk->sk_reuseport &&

102 sk->sk_family ==af_inet6)

103 hlist_add_tail_rcu(&udp_sk(sk)->udp_portaddr_node,

104 &hslot2->head);

105else

106 hlist_add_head_rcu(&udp_sk(sk)->udp_portaddr_node,

107 &hslot2->head);

108 hslot2->count++;

109 spin_unlock(&hslot2->lock);

110}

111sock_set_flag(sk, sock_rcu_free);

112 error =0;

113fail_unlock:

114 spin_unlock_bh(&hslot->lock);

115fail:

116return

error;

117 }

如果snum==0,即沒有繫結本地埠,此時執行if部分**段,這種情況一般發生在客戶端使用socket,此時核心會為它選擇乙個未使用的埠:

udptable中的hash公司為 jhash_1word((__force u32)saddr, net_hash_mix(net)) ^ port------>(num + net_hash_mix(net)) & mask簡寫一下, net_hash_mix

(net)返回為0

所以大約就是sip^port; 具體就不細看hash函式了。也許不是這樣的。。。。

if (!snum) while迴圈的判斷條件snum!=first和snum+=rand一起保證了所有雜湊到hslot的埠號都會被遍歷到。

如果找到了可用埠號,即跳出,執行插入sk的操作,否則++first,

查詢下乙個鍵值,直到fisrt==last,表明所有鍵值都已輪循一遍,仍沒有結果,則退出

*/do

while (snum !=first);

spin_unlock_bh(&hslot->lock

); }

while (++first !=last);

goto fail;

這部分是查詢網上分析結果!!還沒有仔細研究他的這個hash演算法

問題2:由於udp是包模式!! 每次只能copy乙個包!!!能不能copy多個!!!目前是可以的!!

可以使用recvmmsg來繼續降低系統呼叫的開銷。recvmmsg是乙個批量介面,它可以從socket裡一次讀出多個udp資料報,不像recvfrom那樣一次只能讀乙個。如果客戶端多、請求量大的話,recvmmsg的批量讀就很有優勢了。

就像hash表一樣存在衝突!!! 也就是讀取的多個包可能不是乙個客戶端發過來的資料!!!! 那怎樣區分多個客戶端發來的資料呢??在udp資料部分來區分??還是??

看核心**應該是通過rcvmmsg的控制資訊區分。。。。。讀取資料報文的時候也會帶上控制資訊這樣就可以知道對端了

對一次系統上線的思考 走出「舒適區」

今天,本來計畫是會對系統進行一次更新,將這上一周做的需求和修改的bug 發布出去,然後明天開始新的計畫,本來團隊已經對這個目標達成了一致,大家努力的測試,爭取今天能夠上線。後來有一位負責的同事說,今天要是上線更新不了,就明天吧,反正我們也沒有對需求方承諾具體的上線時間,明天再測試一天,沒問題在上線吧...

一次糟糕面試的思考

1.溝通原則之一,在溝通過程中發現對方問的問題有問題時,應在融洽的氣氛中當面指出,事後想找機會指出是無力的,並且可能沒有這樣的機會。但切記不要將這一切變成一場爭論。2.溝通原則之二,當對方與你針鋒相對時應怎麼辦呢?最明智的做法首先應指明這種狀態,希望雙方冷靜下來之後尋求新的溝通方式。若這種方法無法湊...

一次Debug過程的思考

前一段時間,部門接入了新業務,由於業務量小,架構非常簡單,採用了最簡單的lnmp架構,整個專案是交給乙個剛畢業的rd負責的,這是背景。上線前半天,服務平穩執行。下午的時候,開始收到大量報警 no host could be connected in the cluster。第一反應 mysql伺服器...