大家好,我是小林。
這次跟大家分享一位同學面騰訊后端開發(fā)的面經,一步一步深挖計算機基礎的內容,問的問題很多,光面試時常長達 1 個小時多,再加上寫算法 20 分鐘,面試的強度還是挺大的。
雖然面試強度是比較大了一點,但是整體面下來,同學反饋還是很有收獲的,也算是對自己沒掌握的內容進行查漏補缺的過程。
計算機網絡
網絡七層模型分別是什么?
為了使得多種設備能通過網絡相互通信,和為了解決各種不同設備在網絡互聯中的兼容性問題,國際標準化組織制定了開放式系統(tǒng)互聯通信參考模型(Open System Interconnection Reference Model),也就是 OSI 網絡模型,該模型主要有 7 層,分別是應用層、表示層、會話層、傳輸層、網絡層、數據鏈路層以及物理層。
img-
應用層:應用層最接近終端用戶。大多數應用程序都位于這一層。我們從后端服務器請求數據,無需了解數據傳輸的具體細節(jié)。這一層的協(xié)議包括 HTTP、SMTP、FTP、DNS 等。
-
表示層:這一層處理數據編碼、加密和壓縮,為應用層準備數據。例如,HTTPS 利用 TLS(Transport Layer Security)實現客戶端與服務器之間的安全通信。
-
會話層:該層用于打開和關閉兩個設備之間的通信。如果數據量較大,會話層就會設置檢查點,避免從頭開始重新發(fā)送。
-
傳輸層:該層處理兩個設備之間的端到端的通信。它在發(fā)送方將數據分解成段,然后在接收方重新組裝。這一層有流量控制,以防止擁塞。這一層的主要協(xié)議是 TCP 和 UDP。
-
網絡層:這一層實現不同網絡之間的數據傳輸。它進一步將網段或數據報分解成更小的數據包,并使用 IP 地址找到通往最終目的地的最佳路由。
-
數據鏈路層:這一層允許在同一網絡的設備之間傳輸數據。數據包被分解成幀,這些幀被限制在局域網內。
-
物理層:這一層通過電纜和交換機發(fā)送比特流,因此與設備之間的物理連接密切相關。與 OSI 模型相比,TCP/IP 模型只有 4 層。在討論網絡協(xié)議的層次時,必須明確上下文。
TCP和UDP的應用場景是哪些?
TCP適用:網頁、電子郵件、遠程登錄連接、文件傳輸
UDP適用:語音通話,多播通信,DNS解析
TCP如何實現可靠傳輸?
面向連接、同步序列號、校驗和、流量控制和擁塞控制。
- 序列號與確認機制:TCP將每個數據包分配一個唯一的序列號,并且接收方會發(fā)送確認消息來確認已經接收到的數據。發(fā)送方會根據接收到的確認消息判斷是否需要重新發(fā)送丟失的數據包。
- 數據校驗和:TCP使用校驗和來驗證數據在傳輸過程中是否發(fā)生了損壞。接收方會計算校驗和并與發(fā)送方發(fā)送的校驗和進行比較,如果不一致,則說明數據包發(fā)生了損壞,需要重新發(fā)送。
- 滑動窗口機制:TCP使用滑動窗口來控制發(fā)送方發(fā)送數據的速度和接收方接收數據的速度,以避免因發(fā)送速度過快或接收速度過慢而導致的數據丟失或堵塞。
- 重傳機制:如果發(fā)送方沒有收到接收方的確認消息,或者接收方收到的數據包校驗和不一致,發(fā)送方會重新發(fā)送數據包,確保數據的可靠傳輸。
- 擁塞控制:TCP具有擁塞控制機制,可以根據網絡的擁塞情況來調整發(fā)送數據的速率,避免網絡擁塞導致的數據丟失和延遲。
第一次握手,客戶端發(fā)送SYN報后,服務端回復ACK報,那這個過程中服務端內部做了哪些工作?
服務端收到客戶端發(fā)起的 SYN 請求后,內核會把該連接存儲到半連接隊列,并向客戶端響應 SYN+ACK,接著客戶端會返回 ACK,服務端收到第三次握手的 ACK 后,內核會把連接從半連接隊列移除,然后創(chuàng)建新的完全的連接,并將其添加到 accept 隊列,等待進程調用 accept 函數時把連接取出來。
半連接隊列與全連接隊列不管是半連接隊列還是全連接隊列,都有最大長度限制,超過限制時,內核會直接丟棄,或返回 RST 包。
大量SYN包發(fā)送給服務端服務端會發(fā)生什么事情?
有可能會導致TCP 半連接隊列打滿,這樣當 TCP 半連接隊列滿了,后續(xù)再在收到 SYN 報文就會丟棄,導致客戶端無法和服務端建立連接。
避免 SYN 攻擊方式,可以有以下四種方法:
- 調大 netdev_max_backlog;
- 增大 TCP 半連接隊列;
- 開啟 tcp_syncookies;
- 減少 SYN+ACK 重傳次數
方式一:調大 netdev_max_backlog
當網卡接收數據包的速度大于內核處理的速度時,會有一個隊列保存這些數據包??刂圃撽犃械淖畲笾等缦?a target="_blank">參數,默認值是 1000,我們要適當調大該參數的值,比如設置為 10000:
net.core.netdev_max_backlog=10000
方式二:增大 TCP 半連接隊列
增大 TCP 半連接隊列,要同時增大下面這三個參數:
- 增大 net.ipv4.tcp_max_syn_backlog
- 增大 listen() 函數中的 backlog
- 增大 net.core.somaxconn
方式三:開啟 net.ipv4.tcp_syncookies
開啟 syncookies 功能就可以在不使用 SYN 半連接隊列的情況下成功建立連接,相當于繞過了 SYN 半連接來建立連接。
tcp_syncookies 應對 SYN 攻擊具體過程:
-
當 「 SYN 隊列」?jié)M之后,后續(xù)服務端收到 SYN 包,不會丟棄,而是根據算法,計算出一個
cookie
值; - 將 cookie 值放到第二次握手報文的「序列號」里,然后服務端回第二次握手給客戶端;
- 服務端接收到客戶端的應答報文時,服務端會檢查這個 ACK 包的合法性。如果合法,將該連接對象放入到「 Accept 隊列」。
-
最后應用程序通過調用
accpet()
接口,從「 Accept 隊列」取出的連接。
可以看到,當開啟了 tcp_syncookies 了,即使受到 SYN 攻擊而導致 SYN 隊列滿時,也能保證正常的連接成功建立。
net.ipv4.tcp_syncookies 參數主要有以下三個值:
- 0 值,表示關閉該功能;
- 1 值,表示僅當 SYN 半連接隊列放不下時,再啟用它;
- 2 值,表示無條件開啟功能;
那么在應對 SYN 攻擊時,只需要設置為 1 即可。
$echo1>/proc/sys/net/ipv4/tcp_syncookies
方式四:減少 SYN+ACK 重傳次數
當服務端受到 SYN 攻擊時,就會有大量處于 SYN_REVC 狀態(tài)的 TCP 連接,處于這個狀態(tài)的 TCP 會重傳 SYN+ACK ,當重傳超過次數達到上限后,就會斷開連接。
那么針對 SYN 攻擊的場景,我們可以減少 SYN-ACK 的重傳次數,以加快處于 SYN_REVC 狀態(tài)的 TCP 連接斷開。
SYN-ACK 報文的最大重傳次數由 tcp_synack_retries
內核參數決定(默認值是 5 次),比如將 tcp_synack_retries 減少到 2 次:
$echo2>/proc/sys/net/ipv4/tcp_synack_retries
服務端默認能接受多少個連接?
TCP 四元組可以唯一的確定一個連接,四元組包括如下:
- 源地址
- 源端口
- 目的地址
- 目的端口
源地址和目的地址的字段(32 位)是在 IP 頭部中,作用是通過 IP 協(xié)議發(fā)送報文給對方主機。
源端口和目的端口的字段(16 位)是在 TCP 頭部中,作用是告訴 TCP 協(xié)議應該把報文發(fā)給哪個進程。
有一個 IP 的服務端監(jiān)聽了一個端口,它的 TCP 的最大連接數是多少?
服務端通常固定在某個本地端口上監(jiān)聽,等待客戶端的連接請求。
因此,客戶端 IP 和端口是可變的,其理論值計算公式如下:
img
對 IPv4,客戶端的 IP 數最多為 2
的 32
次方,客戶端的端口數最多為 2
的 16
次方,也就是服務端單機最大 TCP 連接數,約為 2
的 48
次方。
當然,服務端最大并發(fā) TCP 連接數遠不能達到理論上限,會受以下因素影響:
-
文件描述符限制
,每個 TCP 連接都是一個文件,如果文件描述符被占滿了,會發(fā)生 Too many open files。Linux 對可打開的文件描述符的數量分別作了三個方面的限制:
-
系統(tǒng)級:當前系統(tǒng)可打開的最大數量,通過
cat /proc/sys/fs/file-max
查看; -
用戶級:指定用戶可打開的最大數量,通過
cat /etc/security/limits.conf
查看; -
進程級:單個進程可打開的最大數量,通過
cat /proc/sys/fs/nr_open
查看;
-
系統(tǒng)級:當前系統(tǒng)可打開的最大數量,通過
-
內存限制,每個 TCP 連接都要占用一定內存,操作系統(tǒng)的內存是有限的,如果內存資源被占滿后,會發(fā)生 OOM。
從輸入url到頁面顯示發(fā)生了哪些事情?
圖片- 解析URL:分析 URL 所需要使用的傳輸協(xié)議和請求的資源路徑。如果輸入的 URL 中的協(xié)議或者主機名不合法,將會把地址欄中輸入的內容傳遞給搜索引擎。如果沒有問題,瀏覽器會檢查 URL 中是否出現了非法字符,則對非法字符進行轉義后在進行下一過程。
- 緩存判斷:瀏覽器會判斷所請求的資源是否在緩存里,如果請求的資源在緩存里且沒有失效,那么就直接使用,否則向服務器發(fā)起新的請求。
- DNS解析:如果資源不在本地緩存,首先需要進行DNS解析。瀏覽器會向本地DNS服務器發(fā)送域名解析請求,本地DNS服務器會逐級查詢,最終找到對應的IP地址。
- 獲取MAC地址:當瀏覽器得到 IP 地址后,數據傳輸還需要知道目的主機 MAC 地址,因為應用層下發(fā)數據給傳輸層,TCP 協(xié)議會指定源端口號和目的端口號,然后下發(fā)給網絡層。網絡層會將本機地址作為源地址,獲取的 IP 地址作為目的地址。然后將下發(fā)給數據鏈路層,數據鏈路層的發(fā)送需要加入通信雙方的 MAC 地址,本機的 MAC 地址作為源 MAC 地址,目的 MAC 地址需要分情況處理。通過將 IP 地址與本機的子網掩碼相結合,可以判斷是否與請求主機在同一個子網里,如果在同一個子網里,可以使用 APR 協(xié)議獲取到目的主機的 MAC 地址,如果不在一個子網里,那么請求應該轉發(fā)給網關,由它代為轉發(fā),此時同樣可以通過 ARP 協(xié)議來獲取網關的 MAC 地址,此時目的主機的 MAC 地址應該為網關的地址。
- 建立TCP連接:主機將使用目標 IP地址和目標MAC地址發(fā)送一個TCP SYN包,請求建立一個TCP連接,然后交給路由器轉發(fā),等路由器轉到目標服務器后,服務器回復一個SYN-ACK包,確認連接請求。然后,主機發(fā)送一個ACK包,確認已收到服務器的確認,然后 TCP 連接建立完成。
- HTTPS 的 TLS 四次握手:如果使用的是 HTTPS 協(xié)議,在通信前還存在 TLS 的四次握手。
- 發(fā)送HTTP請求:連接建立后,瀏覽器會向服務器發(fā)送HTTP請求。請求中包含了用戶需要獲取的資源的信息,例如網頁的URL、請求方法(GET、POST等)等。
- 服務器處理請求并返回響應:服務器收到請求后,會根據請求的內容進行相應的處理。例如,如果是請求網頁,服務器會讀取相應的網頁文件,并生成HTTP響應。
拿到IP地址還要去獲取MAC地址,TCP連接需要用到MAC地址嗎?
也需要的,客戶端發(fā)送第一個 syn 報文的時候,也需要在數據鏈路層組裝 mac 地址的。
DNS解析流程?
域名解析的工作流程- 客戶端首先會發(fā)出一個 DNS 請求,問 www.server.com 的 IP 是啥,并發(fā)給本地 DNS 服務器(也就是客戶端的 TCP/IP 設置中填寫的 DNS 服務器地址)。
- 本地域名服務器收到客戶端的請求后,如果緩存里的表格能找到 www.server.com,則它直接返回 IP 地址。如果沒有,本地 DNS 會去問它的根域名服務器:“老大, 能告訴我 www.server.com 的 IP 地址嗎?” 根域名服務器是最高層次的,它不直接用于域名解析,但能指明一條道路。
- 根 DNS 收到來自本地 DNS 的請求后,發(fā)現后置是 .com,說:“www.server.com 這個域名歸 .com 區(qū)域管理”,我給你 .com 頂級域名服務器地址給你,你去問問它吧。”
- 本地 DNS 收到頂級域名服務器的地址后,發(fā)起請求問“老二, 你能告訴我 www.server.com 的 IP 地址嗎?”
- 頂級域名服務器說:“我給你負責 www.server.com 區(qū)域的權威 DNS 服務器的地址,你去問它應該能問到”。
- 本地 DNS 于是轉向問權威 DNS 服務器:“老三,www.server.com對應的IP是啥呀?” server.com 的權威 DNS 服務器,它是域名解析結果的原出處。為啥叫權威呢?就是我的域名我做主。
- 權威 DNS 服務器查詢后將對應的 IP 地址 X.X.X.X 告訴本地 DNS。
- 本地 DNS 再將 IP 地址返回客戶端,客戶端和目標建立連接。
至此,我們完成了 DNS 的解析過程?,F在總結一下,整個過程我畫成了一個圖。
網頁非常慢轉圈圈的時候,要定位問題需要從哪些角度?
最直接的辦法就是抓包,排查的思路大概有:
-
先確定是服務端的問題,還是客戶端的問題。先確認瀏覽器是否可以訪問其他網站,如果不可以,說明客戶端網絡自身的問題,然后檢查客戶端網絡配置(連接wifi正不正常,有沒有插網線);如果可以正常其他網頁,說明客戶端網絡是可以正常上網的。
-
如果客戶端網絡沒問題,就抓包確認 DNS 是否解析出了 IP 地址,如果沒有解析出來,說明域名寫錯了,如果解析出了 IP 地址,抓包確認有沒有和服務端建立三次握手,如果能成功建立三次握手,并且發(fā)出了 HTTP 請求,但是就是沒有顯示頁面,可以查看服務端返回的響應碼:
- 如果是404錯誤碼,檢查輸入的url是否正確;
- 如果是500,說明服務器此時有問題;
- 如果是200,F12看看前端代碼有問題導致瀏覽器沒有渲染出頁面。
-
如果客戶端網絡是正常的,但是訪問速度很慢,導致很久才顯示出來。這時候要看客戶端的網口流量是否太大的了,導致tcp發(fā)生丟包之類的問題。
總之就是一層一層有沒有插網線,網絡配置是否正確、DNS有沒有解析出 IP地址、TCP有沒有三次握手、HTTP返回的響應碼是什么。
推薦閱讀:網站顯示不出來,怎么排查?
HTTP默認的端口是什么?
http 是 80,https 默認是 443。
端口通不通用什么命令?
第一種方式:telnet:telnet命令用于建立與遠程主機的Telnet連接,并可以使用telnet命令測試特定端口的可訪問性。
-
示例:
telnet IP地址 端口號
用于測試指定IP地址上的指定端口是否可訪問。如果能夠建立連接,則表示端口通暢;如果連接失敗或超時,則表示端口不可訪問。
第二種方式:nc:nc命令(也稱為netcat)是一個網絡工具,可以用于創(chuàng)建各種類型的網絡連接,包括測試端口的可訪問性。
-
示例:
nc -zv IP地址 端口號
用于測試指定IP地址上的指定端口是否可訪問。如果能夠成功連接,則表示端口通暢;如果連接失敗或拒絕,則表示端口不可訪問。
linux端口狀態(tài)查詢有哪些命令?
-
netstat -tunlp
用于顯示所有TCP和UDP端口的監(jiān)聽狀態(tài)。 -
lsof -i :端口號
用于顯示指定端口的相關信息。
ping命令走的是什么協(xié)議?
ICMP 協(xié)議。
ICMP 回送消息錯誤返回碼認識哪些?
五大類 HTTP 狀態(tài)碼-
1xx
類狀態(tài)碼屬于提示信息,是協(xié)議處理中的一種中間狀態(tài),實際用到的比較少。 -
2xx
類狀態(tài)碼表示服務器成功處理了客戶端的請求,也是我們最愿意看到的狀態(tài)。 -
3xx
類狀態(tài)碼表示客戶端請求的資源發(fā)生了變動,需要客戶端用新的 URL 重新發(fā)送請求獲取資源,也就是重定向。 -
4xx
類狀態(tài)碼表示客戶端發(fā)送的報文有誤,服務器無法處理,也就是錯誤碼的含義。 -
5xx
類狀態(tài)碼表示客戶端請求報文正確,但是服務器處理時內部發(fā)生了錯誤,屬于服務器端的錯誤碼。
403代表什么含義?
「403 Forbidden」表示服務器禁止訪問資源,并不是客戶端的請求出錯。
常見的情況包括:
- 客戶端嘗試訪問需要身份驗證的資源,但未提供有效的憑據。
- 客戶端嘗試訪問受限制的目錄或文件。
- 客戶端的IP地址被服務器所禁止訪問。
重定向的作用?
重定向狀態(tài)碼如下,301 和 302 都會在響應頭里使用字段 Location
,指明后續(xù)要跳轉的 URL,瀏覽器會自動重定向新的 URL。
- 「301 Moved Permanently」表示永久重定向,說明請求的資源已經不存在了,需改用新的 URL 再次訪問。
- 「302 Found」表示臨時重定向,說明請求的資源還在,但暫時需要用另一個 URL 來訪問。
重定向是指將一個URL請求轉發(fā)到另一個URL的過程。重定向的作用包括:
-
更改URL:通過重定向,可以更改URL,使其更易于記憶、更友好或更有意義。例如,將長而復雜的URL重定向到簡潔的、易于理解的URL。
-
網站遷移:當網站進行重構、更換域名或更改URL結構時,通過重定向舊的URL到新的URL,可以讓用戶和搜索引擎正確地訪問和索引新的內容。
反向代理,那正向代理是什么?
圖片來源:ByteByteGo- 正向代理是位于用戶設備和互聯網之間的服務器。它代理的是客戶端,是站在用戶一方的。其真實客戶端對于服務器不可見。
- 反向代理是一種服務器,它接受客戶端的請求,將請求轉發(fā)給網絡服務器,然后將結果返回給客戶端,就像代理服務器處理了請求一樣。
推薦閱讀:面試官:你背一下負載均衡算法?
TCP的擁塞控制介紹一下?
擁塞控制是用于在網絡擁塞時減少網絡流量和避免網絡崩潰。
TCP的擁塞控制機制主要包括以下幾個方面:
-
慢啟動:當建立連接或恢復丟失的數據包時,TCP會以指數增加的方式逐漸增加發(fā)送窗口的大小,從而逐漸增加發(fā)送的數據量。
-
擁塞避免:在慢啟動階段,當檢測到網絡擁塞時,TCP會切換到擁塞避免模式。擁塞避免使用線性增加的方式逐漸增加發(fā)送窗口的大小,以降低網絡擁塞的風險。
-
快速重傳:當發(fā)送方連續(xù)接收到同一個確認號的重復確認時,它會認為該數據包已經丟失,并立即重新發(fā)送丟失的數據包,而不等待超時重傳。
-
快速恢復:在快速重傳后,發(fā)送方會將擁塞窗口減半,并使用擁塞避免模式逐漸增加發(fā)送窗口的大小,以便恢復正常的發(fā)送速率。
通過這些機制,TCP能夠根據網絡的擁塞情況動態(tài)調整發(fā)送窗口的大小和發(fā)送速率,以避免網絡擁塞并保證數據的可靠傳輸。擁塞控制是TCP協(xié)議的重要特性,它在保持網絡穩(wěn)定和高效運行方面起著重要作用。
HTTP層請求的類型有哪些?
-
GET:用于請求獲取指定資源,通常用于獲取數據。
-
POST:用于向服務器提交數據,通常用于提交表單數據或進行資源的創(chuàng)建。
-
PUT:用于向服務器更新指定資源,通常用于更新已存在的資源。
-
DELETE:用于請求服務器刪除指定資源。
-
HEAD:類似于GET請求,但只返回資源的頭部信息,用于獲取資源的元數據而不獲取實際內容。
GET和POST的使用場景,有哪些區(qū)別?
根據 RFC 規(guī)范,GET 的語義是從服務器獲取指定的資源,這個資源可以是靜態(tài)的文本、頁面、圖片視頻等。GET 請求的參數位置一般是寫在 URL 中,URL 規(guī)定只能支持 ASCII,所以 GET 請求的參數只允許 ASCII 字符 ,而且瀏覽器會對 URL 的長度有限制(HTTP協(xié)議本身對 URL長度并沒有做任何規(guī)定)。
比如,你打開我的文章,瀏覽器就會發(fā)送 GET 請求給服務器,服務器就會返回文章的所有文字及資源。
GET 請求根據 RFC 規(guī)范,POST 的語義是根據請求負荷(報文body)對指定的資源做出處理,具體的處理方式視資源類型而不同。POST 請求攜帶數據的位置一般是寫在報文 body 中,body 中的數據可以是任意格式的數據,只要客戶端與服務端協(xié)商好即可,而且瀏覽器不會對 body 大小做限制。
比如,你在我文章底部,敲入了留言后點擊「提交」(暗示你們留言),瀏覽器就會執(zhí)行一次 POST 請求,把你的留言文字放進了報文 body 里,然后拼接好 POST 請求頭,通過 TCP 協(xié)議發(fā)送給服務器。
POST 請求如果從 RFC 規(guī)范定義的語義來看:
- GET 方法就是安全且冪等的,因為它是「只讀」操作,無論操作多少次,服務器上的數據都是安全的,且每次的結果都是相同的。所以,可以對 GET 請求的數據做緩存,這個緩存可以做到瀏覽器本身上(徹底避免瀏覽器發(fā)請求),也可以做到代理上(如nginx),而且在瀏覽器中 GET 請求可以保存為書簽。
- POST 因為是「新增或提交數據」的操作,會修改服務器上的資源,所以是不安全的,且多次提交數據就會創(chuàng)建多個資源,所以不是冪等的。所以,瀏覽器一般不會緩存 POST 請求,也不能把 POST 請求保存為書簽。
但是實際過程中,開發(fā)者不一定會按照 RFC 規(guī)范定義的語義來實現 GET 和 POST 方法。比如:
- 可以用 GET 方法實現新增或刪除數據的請求,這樣實現的 GET 方法自然就不是安全和冪等。
- 可以用 POST 方法實現查詢數據的請求,這樣實現的 POST 方法自然就是安全和冪等。
HTTP的長連接是什么?
HTTP 協(xié)議采用的是「請求-應答」的模式,也就是客戶端發(fā)起了請求,服務端才會返回響應,一來一回這樣子。
請求-應答由于 HTTP 是基于 TCP 傳輸協(xié)議實現的,客戶端與服務端要進行 HTTP 通信前,需要先建立 TCP 連接,然后客戶端發(fā)送 HTTP 請求,服務端收到后就返回響應,至此「請求-應答」的模式就完成了,隨后就會釋放 TCP 連接。
一個 HTTP 請求如果每次請求都要經歷這樣的過程:建立 TCP -> 請求資源 -> 響應資源 -> 釋放連接,那么此方式就是 HTTP 短連接,如下圖:
HTTP 短連接這樣實在太累人了,一次連接只能請求一次資源。
能不能在第一個 HTTP 請求完后,先不斷開 TCP 連接,讓后續(xù)的 HTTP 請求繼續(xù)使用此連接?
當然可以,HTTP 的 Keep-Alive 就是實現了這個功能,可以使用同一個 TCP 連接來發(fā)送和接收多個 HTTP 請求/應答,避免了連接建立和釋放的開銷,這個方法稱為 HTTP 長連接。
HTTP 長連接HTTP 長連接的特點是,只要任意一端沒有明確提出斷開連接,則保持 TCP 連接狀態(tài)。
HTTP長連接與WebSocket有什么區(qū)別?
- 全雙工和半雙工:TCP 協(xié)議本身是全雙工的,但我們最常用的 HTTP/1.1,雖然是基于 TCP 的協(xié)議,但它是半雙工的,對于大部分需要服務器主動推送數據到客戶端的場景,都不太友好,因此我們需要使用支持全雙工的 WebSocket 協(xié)議。
- 應用場景區(qū)別:在 HTTP/1.1 里,只要客戶端不問,服務端就不答。基于這樣的特點,對于登錄頁面這樣的簡單場景,可以使用定時輪詢或者長輪詢的方式實現服務器推送(comet)的效果。對于客戶端和服務端之間需要頻繁交互的復雜場景,比如網頁游戲,都可以考慮使用 WebSocket 協(xié)議。
操作系統(tǒng)
進程跟線程的區(qū)別?
- 本質區(qū)別:進程是操作系統(tǒng)資源分配的基本單位,而線程是任務調度和執(zhí)行的基本單位
- 在開銷方面:每個進程都有獨立的代碼和數據空間(程序上下文),程序之間的切換會有較大的開銷;線程可以看做輕量級的進程,同一類線程共享代碼和數據空間,每個線程都有自己獨立的運行棧和程序計數器(PC),線程之間切換的開銷小
- 所處環(huán)境:在操作系統(tǒng)中能同時運行多個進程(程序);而在同一個進程(程序)中有多個線程同時執(zhí)行(通過CPU調度,在每個時間片中只有一個線程執(zhí)行)
- 內存分配方面:系統(tǒng)在運行的時候會為每個進程分配不同的內存空間;而對線程而言,除了CPU外,系統(tǒng)不會為線程分配內存(線程所使用的資源來自其所屬進程的資源),線程組之間只能共享資源
- 包含關系:沒有線程的進程可以看做是單線程的,如果一個進程內有多個線程,則執(zhí)行過程不是一條線的,而是多條線(線程)共同完成的;線程是進程的一部分,所以線程也被稱為輕權進程或者輕量級進程
舉個例子:進程=火車,線程=車廂
- 線程在進程下行進(單純的車廂無法運行)
- 一個進程可以包含多個線程(一輛火車可以有多個車廂)
- 不同進程間數據很難共享(一輛火車上的乘客很難換到另外一輛火車,比如站點換乘)
- 同一進程下不同線程間數據很易共享(A車廂換到B車廂很容易)
- 進程要比線程消耗更多的計算機資源(采用多列火車相比多個車廂更耗資源)
- 進程間不會相互影響,一個線程掛掉將導致整個進程掛掉(一列火車不會影響到另外一列火車,但是如果一列火車上中間的一節(jié)車廂著火了,將影響到所有車廂)
多線程是不是越多越好,太多會有什么問題?
多線程不一定越多越好,過多的線程可能會導致一些問題。
- 切換開銷:線程的創(chuàng)建和切換會消耗系統(tǒng)資源,包括內存和CPU。如果創(chuàng)建太多線程,會占用大量的系統(tǒng)資源,導致系統(tǒng)負載過高,某個線程崩潰后,可能會導致進程崩潰。
- 死鎖的問題:過多的線程可能會導致競爭條件和死鎖。競爭條件指的是多個線程同時訪問和修改共享資源,如果沒有合適的同步機制,可能會導致數據不一致或錯誤的結果。而死鎖則是指多個線程相互等待對方釋放資源,導致程序無法繼續(xù)執(zhí)行。
進程調度算法有哪些?
01 先來先服務調度算法
最簡單的一個調度算法,就是非搶占式的先來先服務(*First Come First Serve, FCFS*)算法了。
FCFS 調度算法
顧名思義,先來后到,每次從就緒隊列選擇最先進入隊列的進程,然后一直運行,直到進程退出或被阻塞,才會繼續(xù)從隊列中選擇第一個進程接著運行。
這似乎很公平,但是當一個長作業(yè)先運行了,那么后面的短作業(yè)等待的時間就會很長,不利于短作業(yè)。
FCFS 對長作業(yè)有利,適用于 CPU 繁忙型作業(yè)的系統(tǒng),而不適用于 I/O 繁忙型作業(yè)的系統(tǒng)。
02 最短作業(yè)優(yōu)先調度算法
最短作業(yè)優(yōu)先(*Shortest Job First, SJF*)調度算法同樣也是顧名思義,它會優(yōu)先選擇運行時間最短的進程來運行,這有助于提高系統(tǒng)的吞吐量。
SJF 調度算法
這顯然對長作業(yè)不利,很容易造成一種極端現象。
比如,一個長作業(yè)在就緒隊列等待運行,而這個就緒隊列有非常多的短作業(yè),那么就會使得長作業(yè)不斷的往后推,周轉時間變長,致使長作業(yè)長期不會被運行。
03 高響應比優(yōu)先調度算法
前面的「先來先服務調度算法」和「最短作業(yè)優(yōu)先調度算法」都沒有很好的權衡短作業(yè)和長作業(yè)。
那么,高響應比優(yōu)先 (*Highest Response Ratio Next, HRRN*)調度算法主要是權衡了短作業(yè)和長作業(yè)。
每次進行進程調度時,先計算「響應比優(yōu)先級」,然后把「響應比優(yōu)先級」最高的進程投入運行,「響應比優(yōu)先級」的計算公式:
從上面的公式,可以發(fā)現:
- 如果兩個進程的「等待時間」相同時,「要求的服務時間」越短,「響應比」就越高,這樣短作業(yè)的進程容易被選中運行;
- 如果兩個進程「要求的服務時間」相同時,「等待時間」越長,「響應比」就越高,這就兼顧到了長作業(yè)進程,因為進程的響應比可以隨時間等待的增加而提高,當其等待時間足夠長時,其響應比便可以升到很高,從而獲得運行的機會;
04 時間片輪轉調度算法
最古老、最簡單、最公平且使用最廣的算法就是時間片輪轉(*Round Robin, RR*)調度算法。
RR 調度算法
每個進程被分配一個時間段,稱為時間片(*Quantum*),即允許該進程在該時間段中運行。
- 如果時間片用完,進程還在運行,那么將會把此進程從 CPU 釋放出來,并把 CPU 分配給另外一個進程;
- 如果該進程在時間片結束前阻塞或結束,則 CPU 立即進行切換;
另外,時間片的長度就是一個很關鍵的點:
- 如果時間片設得太短會導致過多的進程上下文切換,降低了 CPU 效率;
- 如果設得太長又可能引起對短作業(yè)進程的響應時間變長。將
一般來說,時間片設為 20ms~50ms
通常是一個比較合理的折中值。
05 最高優(yōu)先級調度算法
前面的「時間片輪轉算法」做了個假設,即讓所有的進程同等重要,也不偏袒誰,大家的運行時間都一樣。
但是,對于多用戶計算機系統(tǒng)就有不同的看法了,它們希望調度是有優(yōu)先級的,即希望調度程序能從就緒隊列中選擇最高優(yōu)先級的進程進行運行,這稱為最高優(yōu)先級(*Highest Priority First,HPF*)調度算法。
進程的優(yōu)先級可以分為,靜態(tài)優(yōu)先級和動態(tài)優(yōu)先級:
- 靜態(tài)優(yōu)先級:創(chuàng)建進程時候,就已經確定了優(yōu)先級了,然后整個運行時間優(yōu)先級都不會變化;
- 動態(tài)優(yōu)先級:根據進程的動態(tài)變化調整優(yōu)先級,比如如果進程運行時間增加,則降低其優(yōu)先級,如果進程等待時間(就緒隊列的等待時間)增加,則升高其優(yōu)先級,也就是隨著時間的推移增加等待進程的優(yōu)先級。
該算法也有兩種處理優(yōu)先級高的方法,非搶占式和搶占式:
- 非搶占式:當就緒隊列中出現優(yōu)先級高的進程,運行完當前進程,再選擇優(yōu)先級高的進程。
- 搶占式:當就緒隊列中出現優(yōu)先級高的進程,當前進程掛起,調度優(yōu)先級高的進程運行。
但是依然有缺點,可能會導致低優(yōu)先級的進程永遠不會運行。
06 多級反饋隊列調度算法
多級反饋隊列(*Multilevel Feedback Queue*)調度算法是「時間片輪轉算法」和「最高優(yōu)先級算法」的綜合和發(fā)展。
顧名思義:
- 「多級」表示有多個隊列,每個隊列優(yōu)先級從高到低,同時優(yōu)先級越高時間片越短。
- 「反饋」表示如果有新的進程加入優(yōu)先級高的隊列時,立刻停止當前正在運行的進程,轉而去運行優(yōu)先級高的隊列;
多級反饋隊列
來看看,它是如何工作的:
- 設置了多個隊列,賦予每個隊列不同的優(yōu)先級,每個隊列優(yōu)先級從高到低,同時優(yōu)先級越高時間片越短;
- 新的進程會被放入到第一級隊列的末尾,按先來先服務的原則排隊等待被調度,如果在第一級隊列規(guī)定的時間片沒運行完成,則將其轉入到第二級隊列的末尾,以此類推,直至完成;
- 當較高優(yōu)先級的隊列為空,才調度較低優(yōu)先級的隊列中的進程運行。如果進程運行時,有新進程進入較高優(yōu)先級的隊列,則停止當前運行的進程并將其移入到原隊列末尾,接著讓較高優(yōu)先級的進程運行;
可以發(fā)現,對于短作業(yè)可能可以在第一級隊列很快被處理完。對于長作業(yè),如果在第一級隊列處理不完,可以移入下次隊列等待被執(zhí)行,雖然等待的時間變長了,但是運行時間也變更長了,所以該算法很好的兼顧了長短作業(yè),同時有較好的響應時間。
數據庫
MySQL和Redis的區(qū)別,應用場景?
MySQL 是關系型數據庫,適用于需要保持數據一致性、進行復雜的數據分析和關聯查詢的場景。
Redis是一種基于內存的數據存儲系統(tǒng),它主要用于緩存和高速數據訪問。Redis適合處理高速讀寫和緩存需求,適用于需要快速響應和高并發(fā)的場景,例如實時計數器、會話緩存、消息隊列、發(fā)布/訂閱系統(tǒng)等。
一般會用Redis 作為MySQL的緩存,主要是因為 Redis 具備「高性能」和「高并發(fā)」兩種特性。
1、Redis 具備高性能
假如用戶第一次訪問 MySQL 中的某些數據。這個過程會比較慢,因為是從硬盤上讀取的。將該用戶訪問的數據緩存在 Redis 中,這樣下一次再訪問這些數據的時候就可以直接從緩存中獲取了,操作 Redis 緩存就是直接操作內存,所以速度相當快。
img如果 MySQL 中的對應數據改變的之后,同步改變 Redis 緩存中相應的數據即可,不過這里會有 Redis 和 MySQL 雙寫一致性的問題,后面我們會提到。
2、Redis 具備高并發(fā)
單臺設備的 Redis 的 QPS(Query Per Second,每秒鐘處理完請求的次數) 是 MySQL 的 10 倍,Redis 單機的 QPS 能輕松破 10w,而 MySQL 單機的 QPS 很難破 1w。
所以,直接訪問 Redis 能夠承受的請求是遠遠大于直接訪問 MySQL 的,所以我們可以考慮把數據庫中的部分數據轉移到緩存中去,這樣用戶的一部分請求會直接到緩存這里而不用經過數據庫。
MySQL有哪些存儲引擎?有什么區(qū)別?
-
InnoDB:支持事務處理,支持外鍵,支持崩潰修復能力和并發(fā)控制。如果需要對事務的完整性要求比較高(比如銀行),要求實現并發(fā)控制(比如售票),那選擇InnoDB有很大的優(yōu)勢。如果需要頻繁的更新、刪除操作的數據庫,也可以選擇InnoDB,因為支持事務的提交(commit)和回滾(rollback)。
-
MyISAM:插入數據快,空間和內存使用比較低。如果表主要是用于插入新記錄和讀出記錄,那么選擇MyISAM能實現處理高效率。如果應用的完整性、并發(fā)性要求比 較低,也可以使用。如果數據表主要用來插入和查詢記錄,則MyISAM引擎能提供較高的處理效率
-
MEMORY:所有的數據都在內存中,數據的處理速度快,但是安全性不高。如果需要很快的讀寫速度,對數據的安全性要求較低,可以選擇MEMOEY。它對表的大小有要求,不能建立太大的表。所以,這類數據庫只使用在相對較小的數據庫表。如果只是臨時存放數據,數據量不大,并且不需要較高的數據安全性,可以選擇將數據保存在內存中的Memory引擎,MySQL中使用該引擎作為臨時表,存放查詢的中間結果
事務用來解決什么問題?
事務用于解決數據庫操作中的一致性和持久性問題。
-
一致性問題指的是在多個并發(fā)操作中,如果其中一個操作失敗或發(fā)生錯誤,可能導致數據處于不一致的狀態(tài),不符合預期的要求。
-
持久性問題指的是確保已提交的數據在數據庫系統(tǒng)
事務的四大特性主要是:
- 原子性(Atomicity):一個事務中的所有操作,要么全部完成,要么全部不完成,不會結束在中間某個環(huán)節(jié),而且事務在執(zhí)行過程中發(fā)生錯誤,會被回滾到事務開始前的狀態(tài),就像這個事務從來沒有執(zhí)行過一樣,就好比買一件商品,購買成功時,則給商家付了錢,商品到手;購買失敗時,則商品在商家手中,消費者的錢也沒花出去。
- 一致性(Consistency):是指事務操作前和操作后,數據滿足完整性約束,數據庫保持一致性狀態(tài)。比如,用戶 A 和用戶 B 在銀行分別有 800 元和 600 元,總共 1400 元,用戶 A 給用戶 B 轉賬 200 元,分為兩個步驟,從 A 的賬戶扣除 200 元和對 B 的賬戶增加 200 元。一致性就是要求上述步驟操作后,最后的結果是用戶 A 還有 600 元,用戶 B 有 800 元,總共 1400 元,而不會出現用戶 A 扣除了 200 元,但用戶 B 未增加的情況(該情況,用戶 A 和 B 均為 600 元,總共 1200 元)。
- 隔離性(Isolation):數據庫允許多個并發(fā)事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務并發(fā)執(zhí)行時由于交叉執(zhí)行而導致數據的不一致,因為多個事務同時使用相同的數據時,不會相互干擾,每個事務都有一個完整的數據空間,對其他并發(fā)事務是隔離的。也就是說,消費者購買商品這個事務,是不影響其他消費者購買的。
- 持久性(Durability):事務處理結束后,對數據的修改就是永久的,即便系統(tǒng)故障也不會丟失。
MySQL存儲的數據結構是怎么樣的?
InnoDB 存儲引擎的索引結構是 b+樹。
選擇 b+樹作為數據結構的原因是:
- B+Tree vs B Tree:B+Tree 只在葉子節(jié)點存儲數據,而 B 樹 的非葉子節(jié)點也要存儲數據,所以 B+Tree 的單個節(jié)點的數據量更小,在相同的磁盤 I/O 次數下,就能查詢更多的節(jié)點。另外,B+Tree 葉子節(jié)點采用的是雙鏈表連接,適合 MySQL 中常見的基于范圍的順序查找,而 B 樹無法做到這一點。
-
B+Tree vs 二叉樹:對于有 N 個葉子節(jié)點的 B+Tree,其搜索復雜度為
O(logdN)
,其中 d 表示節(jié)點允許的最大子節(jié)點個數為 d 個。在實際的應用當中, d 值是大于100的,這樣就保證了,即使數據達到千萬級別時,B+Tree 的高度依然維持在 3~4 層左右,也就是說一次數據查詢操作只需要做 3~4 次的磁盤 I/O 操作就能查詢到目標數據。而二叉樹的每個父節(jié)點的兒子節(jié)點個數只能是 2 個,意味著其搜索復雜度為O(logN)
,這已經比 B+Tree 高出不少,因此二叉樹檢索到目標數據所經歷的磁盤 I/O 次數要更多。 - B+Tree vs Hash:Hash 在做等值查詢的時候效率賊快,搜索復雜度為 O(1)。但是 Hash 表不適合做范圍查詢,它更適合做等值的查詢,這也是 B+Tree 索引要比 Hash 表索引有著更廣泛的適用場景的原因。
Text數據類型可以無限大嗎?
MySQL 3 種text類型的最大長度如下:
- TEXT:65,535 bytes ~64kb
- MEDIUMTEXT:16,777,215 bytes ~16Mb
- LONGTEXT:4,294,967,295 bytes ~4Gb
MySQL索引的優(yōu)缺點?
索引最大的好處是提高查詢速度,但是索引也是有缺點的,比如:
- 需要占用物理空間,數量越大,占用空間越大;
- 創(chuàng)建索引和維護索引要耗費時間,這種時間隨著數據量的增加而增大;
- 會降低表的增刪改的效率,因為每次增刪改索引,B+ 樹為了維護索引有序性,都需要進行動態(tài)維護。
所以,索引不是萬能鑰匙,它也是根據場景來使用的。
什么時候不適合用索引?
-
WHERE
條件,GROUP BY
,ORDER BY
里用不到的字段,索引的價值是快速定位,如果起不到定位的字段通常是不需要創(chuàng)建索引的,因為索引是會占用物理空間的。 - 字段中存在大量重復數據,不需要創(chuàng)建索引,比如性別字段,只有男女,如果數據庫表中,男女的記錄分布均勻,那么無論搜索哪個值都可能得到一半的數據。在這些情況下,還不如不要索引,因為 MySQL 還有一個查詢優(yōu)化器,查詢優(yōu)化器發(fā)現某個值出現在表的數據行中的百分比很高的時候,它一般會忽略索引,進行全表掃描。
- 表數據太少的時候,不需要創(chuàng)建索引;
- 經常更新的字段不用創(chuàng)建索引,比如不要對電商項目的用戶余額建立索引,因為索引字段頻繁修改,由于要維護 B+Tree的有序性,那么就需要頻繁的重建索引,這個過程是會影響數據庫性能的。
算法
- 實現 LRU算法實現(鏈表+hashmap)
#include
#include
#include
classLRUCache{
private:
intcapacity;
std::unordered_map<int,std::list<std::pair<int,int>>::iterator>cache;
std::list<std::pair<int,int>>lruList;
public:
LRUCache(intcapacity){
this->capacity=capacity;
}
intget(intkey){
if(cache.find(key)==cache.end()){
return-1;//如果key不存在,返回-1
}
//將訪問的節(jié)點移動到鏈表頭部
std::pair<int,int>keyValue=*cache[key];
lruList.erase(cache[key]);
lruList.push_front(keyValue);
cache[key]=lruList.begin();
returnkeyValue.second;
}
voidput(intkey,intvalue){
if(cache.find(key)==cache.end()){
//如果容量已滿,移除最久未使用的節(jié)點
if(lruList.size()==capacity){
std::pair<int,int>lastPair=lruList.back();
intlastKey=lastPair.first;
cache.erase(lastKey);
lruList.pop_back();
}
//將新節(jié)點添加到鏈表頭部
lruList.push_front(std::make_pair(key,value));
cache[key]=lruList.begin();
}else{
//如果key已存在,更新節(jié)點的值,并將節(jié)點移動到鏈表頭部
lruList.erase(cache[key]);
lruList.push_front(std::make_pair(key,value));
cache[key]=lruList.begin();
}
}
};
intmain(){
LRUCachecache(2);
cache.put(1,1);
cache.put(2,2);
std::cout<1)<std::endl;//輸出1
cache.put(3,3);
std::cout<2)<std::endl;//輸出-1
cache.put(4,4);
std::cout<1)<std::endl;//輸出-1
std::cout<3)<std::endl;//輸出3
std::cout<4)<std::endl;//輸出4
return0;
}
-
數據傳輸
+關注
關注
9文章
1794瀏覽量
64412 -
傳輸層
+關注
關注
0文章
29瀏覽量
10872 -
網絡模型
+關注
關注
0文章
44瀏覽量
8400
原文標題:騰訊有點頂,連環(huán)追問我基礎細節(jié)!
文章出處:【微信號:小林coding,微信公眾號:小林coding】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論