?原站地址:Go語言核心36講_Golang_Go語言-極客時間
一、io包中的接口和工具
1.?strings.Builder、strings.Reader 和 bytes.Buffer?這些類型實現了 io 包的很多接口,目的是什么?
? ? 是為了提高不同程序實體之間的互操作性。?程序實體是指比如網絡和文件。
? ? 比如?io.Copy:??func Copy(dst Writer, src Reader) (written int64, err error)
? ? 里面的?Writer 和 Reader 可以是網絡,也可以是文件,只要實現了io.Reader接口和io.Writer接口就都可以了。可以把不同的實體抽象成一個統一的實體。
2. 擴展接口和實現類型的區別是什么?
? ?實現類型是一個結構體; 擴展接口只是通過結構體的方式,嵌入了其他數據類型,達到擴展原有數據類型功能的目的,
? ?擴展接口是多個接口的集合。要實現擴展接口,需要實現多個接口。
? ?實現類型是把接口實現了,而擴展接口并沒有把接口實現,它只是多個接口的集合。
3.?在io包中,io.Reader的擴展接口和實現類型都有哪些? (提升對io包的了解程度)
? ??io.Reader的擴展接口有下面幾種:
(1)?io.ReadWriter:包含字節序列讀取方法Read,和寫入方法Write。
(2)?io.ReadCloser:包含字節序列讀取方法Read,和關閉方法Close。Close用于關閉數據讀寫的通路。這個接口是io.Reader和io.Closer的組合。
(3)?io.ReadWriteCloser:io.Reader、io.Write 和io.Closer的組合。
(4)?io.ReadSeeker:可以根據給定的偏移量去尋找新的位置,作為下一次讀的起始索引。包含了尋找讀寫位置的基本方法Seek。io.Reader 和io.Seeker的組合。
(5)?io.ReadWriteSeeker:io.Reader、io.Writer和io.Seeker的組合。
? ?io.Reader接口的實現類型有下面幾種:
(1)?*io.LimitedReader:方法Read返回的總數據量會受到限制,無論被調用多少次。
(2)?*io.SectionReader:Read方法只能夠讀取原始數據中的某一個部分。與切片類似,只暴露在窗口之中的數據。
(3)?*io.teeReader:接受io.Reader和io.Writer兩個類型的參數,把Reader讀到的數據,通過字節切片中轉的方式,寫入io.Writer處。通常使用在數據流的處理中,比如計算下載速度。
(4)?*io.multiReader:接受多個io.Reader類型的參數,并從中順序地讀取數據。
(5)?*io.pipe:同步內存管道的核心實現
(6) * io.PipeReader:同步內存管道的讀取端
4.?io包中的接口都有哪些?
(1) 核心接口:io.Reader、io.Writer和io.Closer
(2)?io.ByteReader:讀取一個單一的字節。 strings.Reader和bytes.Buffer 是它的實現類型。
? ? ?io.RuneReader:讀取一個單一的Unicode 字符。strings.Reader和bytes.Buffer 是其實現類型
? ? ?io.ByteScanner:讀取和讀回退單個字節
? ? ?io.RuneScanner:讀取和讀回退單個Unicode 字符
? ? ?io.ReaderAt:只讀取數據,不修改已讀計數的值。
? ? ?io.ReaderFrom:從一個 Reader 中讀取數據
? ? ?io.WriteTo:將數據寫入到一個 Writer 中
? ? ?io.ReadWriter:讀和寫,?*io.pipe 是其實現類型。
? ? ?io.ReadWriteCloser:讀寫和關閉管道,net包有它的實現類型。
? ? ?io.ByteWriter和io.WriterAt:功能和上面對應。實現類型是 *os.File
? ? ?io.Seeker:尋找并設定下一次讀取或寫入時的起始索引位置。strings.Reader和io.SectionReader都是其實現類型。
? ??io.Closer:關閉管道。io.PipeReader和io.PipeWriter 是其實現類型。
(3) io包中的簡單接口共有 11 個。讀取操作相關的5 個,寫入操作相關的 4 個,關閉操作有關的1 個,讀寫位置設定相關的一個。此外,還包含了 9 個基于這些簡單接口的擴展接口。
二、bufio包中的數據類型
1.?bufio 包的程序實體,是在包裝簡單 I/O 接口類型值的基礎上,添加了緩沖區。
2.?bufio包中的數據類型主要有:(1) Reader (2) Scanner (3) Writer 和 ReadWriter。
3.?bufio.Reader類型值中的緩沖區起著怎樣的作用?
????????Reader值會預先從底層讀取器里讀出一部分數據,暫存于緩沖區之中。當Reader值下次讀取數據時,先從緩沖區中讀取。 減少了底層讀取器的使用次數,降低讀取的執行時間。?
????????雖然,讀取時會增加填充緩沖區的操作,但從總體上看,平均執行時間會有大幅度的縮短。
4. bufio.Reader類型通過私有的 fill方法 來整理緩沖區,把已讀的空間騰出來,提供給底層讀取器存放數據。
? ? 整理方法就是把 [已讀計數, 已寫計數) 范圍內的數據往最前面搬運,如下圖所示:
5.?bufio.Reader類型讀取方法分別有哪些?
(1)?Peek方法:讀取緩沖區中的n個未讀字節,n大于緩沖區長度的話,轉而直接從底層讀取器中讀出數據。
??????會從已讀計數代表的索引位置開始讀,讀完不更改已讀計數。?
(2)?Read方法:其他和Peek方法一樣,但會更改已讀計數。?
(3)?ReadSlice方法:持續地讀取數據,直至遇到調用方給定的分隔符為止。
? ? ?如果緩沖區滿了仍然找不到分隔符,會把整個緩沖區作為第一個結果值,緩沖區已滿錯誤作為第二個結果值 返回。
(4)?ReadBytes方法: 其他和ReadSlice方法一樣,但緩沖區滿了仍然找不到分隔符的話,會再次調用ReadSlice方法,整理緩沖區之后繼續從底層讀取器中讀出數據,直至少找到分隔符或者讀完全部數據。
6.?內容泄露:Peek方法、ReadSlice方法和ReadLine方法都有可能會造成內容泄露,因為他們都是返回直接基于緩沖區的字節切片。 只有Read方法不會內容泄露。
三、使用os包中的API
1.?os代碼包中的 API,是對操作系統的某方面功能的高層次抽象,使我們可以用統一的方式,操縱不同的操作系統。
? ??最有代表性的就是數據類型 os.File,它實現了io包3 個核心接口io.Reader、io.Writer和io.Closer,3 個簡單接口,io.ReaderAt、io.Seeker和io.WriterAt,以及9 個擴展接口中的 7 個。
? ? 所以除 文本文件、二進制文件、壓縮文件、目錄這些常見的形式之外,還有符號鏈接、各種物理設備、命名管道,以及套接字(socket)都可以被視為文件。
2.?怎樣才能獲得一個os.File類型的指針值(File值)??
(1)?os.Create函數:根據給定的路徑創建一個新的文件。?
? ? ? 會返回一個File值和一個錯誤值。可以通過File值進行讀寫,路徑不存在的話會返回錯誤。
(2) os.Open函數:打開一個文件并返回包裝了該文件的File值。
? ? ? 只能從該File值中讀取內容,而不能寫入內容。
(3)?os.NewFile函數:依據已經存在的文件描述符,新建包裝了該文件的File值。
(4)?os.OpenFile函數:新建或打開文件。
? ? ?可讀可寫。函數有 3 個參數,為name(文件路徑)、flag(操作模式)和perm(權限模式)。
? ? ?可以視為這是基礎函數,上面3個函數只是這個基礎函數的參數組合。
3.?文件描述符,作為某個文件的一個標識存在。由 I/O 相關的系統調用返回,是很小的非負整數。
? ??任何文件的I/O 操作都需要這個文件描述符,它被存儲在File值中。
4.?File值的操作模式都有哪些?
? ??os.O_APPEND:追加模式寫入內容。
????os.O_CREATE:路徑不存在時創建文件。
? ? os.O_SYNC:在打開的文件上實施同步 I/O,保證讀寫的內容總會與硬盤上的數據同步。
? ??os.O_TRUNC:文件已存在時清空文件內容。
? ? 多個操作模式可以通過按位或操作符 " | "?組合起來的。
5. File值的權限模式都有哪些?
? ??權限模式參數 是uint32類型的再定義類型,包含了 32 個比特位,每個比特位都有特定含義:
(1)?最高比特位,1 代表 目錄。
(2)?第 26 個比特位,1代表?命名管道
(3)?最低的 9 個比特位才用于文件的權限,分別是 文件所有者、用戶組、其他用戶 對該文件的訪問權限(讀、寫和執行)。
四、訪問網絡服務
1. 進程間通信,稱為 IPC。主要方法包括:系統信號(signal)、管道(pipe)、套接字(socket)、文件鎖、消息隊列、信號量(semaphore)等。 socket 是最為通用和靈活的一種。
2. socket實例相關的API,是由一個名為 socket系統調用 代表的,它是連接應用程序和操作系統內核的橋梁。
? ??syscall代碼包中,有一個與這個 socket系統調用 相對應的函數。函數本身是平臺不相關的。在其底層,Go 為每個操作系統都做了適配,所以無論在哪個平臺上總是有效的。
? ??net代碼包中的很多程序實體,都會直接或間接地使用到syscall.Socket函數,比如net.Dial函數
3.?net.Dial函數的第一個參數network有哪些可選值?
(1) TCP、TCP4、TCP6:代表 TCP 協議,自適應、第四版、第六版
(2)?UDP、UDP4、UDP6:代表 UDP 協議,自適應、第四版、第六版
(3)?unix、unixgram、unixpacket:代表 Unix 通信域下的內部 socket 協議,socket 類型分別為SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET。
4.?有消息邊界,是指內核程序在發送或接收數據時,是以消息為單位的。
? ??有邏輯連接,是指通信雙方在收發數據之前必須先建立網絡連接。
5. TCP 沒有消息邊界,有邏輯連接。 好處:保證可靠性,有序,雙向傳輸。壞處:速度慢。
? ? UDP 有消息邊界,沒有邏輯連接。好處:速度快。壞處:不保證可靠,無需,只能單向傳輸。
6.?net.DialTimeout函數 調用時給定的超時時間意味著什么?
? ?給定的超時時間,意味著函數為網絡建立連接,可以等待的最長時間。
? ?調用net.DialTimeout函數之后,時間主要花費在 “解析參數network和address的值”,以及“創建 socket 實例并建立網絡連接”這兩件事情上。
五、基于HTTP協議的網絡服務
1. net/http代碼包 可以使用 基于http協議的網絡服務。比如?http.Get函數。
? ? 實例:? resp, err := http.Get(url)? ? 返回兩個值:resp 和?err
? ? resp:數據類型是*http.Response,響應內容的結構體。
? ? err:數據類型是error,創建、發送請求、接收和解析、響應 全過程中,可能發生的錯誤。
2.?http.Get函數
? ? http.Get函數會在內部使用?缺省HTTP客戶端,它是 net/http包中的公開變量DefaultClient代表的,其類型是 *http.Client, 它是開箱即用的。
3.?http.Client類型中的 Transport字段?代表著什么?
(1)?Transport字段 的數據類型是?*http.Transport,會在內部使用net.Dialer類型值,實現 連接 和 超時 的功能。
(2)?http.Transport類型的值(以下簡稱Transport值)會對每一個網絡服務的空閑連接的總數做出限定。 一個網絡服務由網絡地址、網絡協議、代理 三方面來鑒定。
? ? ?在默認情況下,空閑連接總數最大為100,而每個網絡服務的最大空閑連接數為2。
(3)?*http.Transport 是?http.RoundTripper接口 的實現類型。RoundTripper 內部有一個DefaultTransport的缺省值。 前面提到的?缺省HTTP客戶端?DefaultClient ,其實也就是使用這個DefaultTransport。
(4) 總的來說 Transport字段代表著:向網絡服務發送 HTTP 請求,并從網絡服務接收 HTTP 響應的整個操作過程。
4.?http.Server類型的ListenAndServe方法都做了哪些事情?
(1) 對一個基于 TCP 協議的網絡地址進行監聽(net.Listen),并對接收到的 HTTP 請求進行處理。
(2)?默認開啟針對網絡連接的存活探測機制,以保證連接是持久的。
(3)?該方法會一直執行,直到有嚴重的錯誤發生或者被外界關掉。
5.?net.Listen函數都做了哪些事情?
(1)?解析參數值中網絡地址包含的 IP 地址和端口號;
(2)?根據給定的網絡協議,確定監聽的方法,并開始進行監聽。
6.?http.Server類型的Serve方法是怎樣接受和處理 HTTP 請求的?
(1) listen之后,進入一個for循環。
(2) for循環中,Accept方法會被不斷地調用,該方法返回兩個值:
? ? ?net.Conn類型:代表包含了新到來的 HTTP 請求的網絡連接
?????error類型:代表是否發生了錯誤。暫時性的錯誤的話for會繼續執行,否則會for循環都會被終止
(3) 把?net.Conn類型的結果值包裝成一個*http.conn類型,并啟用新的 goroutine ,去調用這個conn值的serve方法,來對當前的 HTTP 請求進行處理。
六、程序性能分析基礎
1.?性能分析 API 在這三個代碼包中:
(1) runtime/pprof
(2) net/http/pprof
(3) runtime/trace
2.?概要文件(Profile)
? ?runtime 代碼包中包含更底層的 API,用來收集程序運行過程中的一些關鍵指標,并生成概要文件,提供分析使用。
? ??go test 命令也可以在測試完成后生成?概要文件(Profile)。
3.?概要文件采樣時刻的內容
? ? ? 分析程序性能的概要文件有CPU、內存、阻塞三種概要文件。文件里每段概要信息都記錄著,某個采樣時刻的內容分別是:
(1) CPU 概要文件:CPU 上正在執行的 Go 代碼。
(2) 內存概要文件:內存的使用情況,已分配和已釋放的字節數量和對象數量。
(3) 阻塞概要文件:goroutine 阻塞事件
4.?概要文件內容格式
? ? 這些概要文件是以二進制存儲的,是通過 protocol buffers 生成的二進制字節流。
? ? 可以使用?go tool pprof 工具查看。
5.?怎樣讓程序對 CPU 概要信息進行采樣?
(1) 進行采樣調用StartCPUProfile函數;停止采樣調用StopCPUProfile函數。
(2)?StartCPUProfile函數,會設定 CPU 概要信息的采樣頻率,并在單獨的 goroutine 中進行收集和輸出。 CPU采樣頻率總是固定100赫茲的,經過大量實驗證明最優。
(3)?StopCPUProfile函數,會采樣頻率設為0,采樣工作停止。
6.?怎樣設定內存概要信息的采樣頻率?
(1)?為 runtime.MemProfileRate變量賦值即可。?含義是,每分配多少個字節,就對堆內存的使用情況進行一次采樣。缺省值是512 KB。
(2) 越早設定越好,避免運行時造成不良影響。最好只在main函數的開始處設定一次。
(3)?想獲取內存概要信息的時候, 調用WriteHeapProfile函數。但它并非實時數據,是在最近一次的內存垃圾收集工作完成時產生的。
(4)?可以調用runtime.ReadMemStats函數,獲得實時數據。不過該函數會引起 Go 語言調度器的短暫停頓。
7.?怎樣獲取到阻塞概要信息?
(1) 調用runtime包中的SetBlockProfileRate函數,可對阻塞概要信息的采樣頻率進行設定。
(2) 函數有一個名叫rate的參數,int類型。含義是,只要發現一個阻塞事件的持續時間達到了多少納秒,就對其進行采樣。
(3) 在runtime包中,有一個名叫blockprofilerate的私有變量,uint64類型。含義是,只要發現一個阻塞事件的持續時間跨越了多少個 CPU 時鐘周期,就對其進行采樣。和(2)的區別僅僅在于單位不同。?
(4)?缺省值是0,所以默認情況下并不會記錄任何阻塞事件。
(5)?需要獲取阻塞概要信息的時候,做兩步操作:
? ? 調用runtime/pprof包中的 Lookup函數 并傳入參數值"block",得到*runtime/pprof.Profile類型的值? ? ?
? ? 調用Profile值的 WriteTo方法,把概要信息寫進指定的寫入器中。
8.?runtime/pprof.Lookup函數的正確調用方式是什么?
(1)?Lookup函數的功能是,提供 給定的名稱?相對應的概要信息。
(2)?給定的名稱 包括:
goroutine:收集當前正在使用的所有 goroutine 堆棧跟蹤信息。會引起 Go 調度器的短暫停頓。
heap:收集與堆內存的分配和釋放有關的采樣信息。也就是前面的內存概要信息。
allocs:與heap大致一樣,但?allocs 收集到的是已分配空間(已分配不管有否釋放),heap是在用空間(已分配未釋放)。
threadcreate:收集堆棧跟蹤信息,描繪出代碼調用鏈。
block:在代碼同步競爭中被阻塞的代碼的堆棧跟蹤信息。就是前面的阻塞概要信息。
mutex:在代碼同步競爭中,獲得過執行的代碼的堆棧跟蹤信息。
9.?如何為基于 HTTP 協議的網絡服務添加性能分析接口?
(1)?在程序中導入net/http/pprof代碼包。
? ? ?import _ "net/http/pprof"
(2)?啟動網絡服務并開始監聽。
? ? ?log.Println(http.ListenAndServe("localhost:8082", nil))
(3)?在瀏覽器中訪問? http://localhost:8082/debug/pprof? 看到一個簡約的網頁
(4)?在/debug/pprof/ 這個URL路徑下還有很多可用的子路徑,包括:profile,trace,allocs、block、goroutine、heap、mutex、threadcreate。
(5) 返回二進制內容時,需要使用??go tool pprof 工具去查看。
? ? go tool pprof http://localhost:6060/debug/pprof/profile?seconds=60