golang快速入門(四)
提示:本系列文章適合有其他語音基礎並對Go有持續衝動的讀者
一、golang獲取HTTP請求
1.在golang標準庫中提供了net包來處理網路連接,通過http.Get創建http請求並返回伺服器響應流。再通過ReadAll讀取response全部內容。
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
)
func main() {
for _, arg := range os.Args[1:] {
res, err := http.Get(arg)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
b, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("%s", b)
}
}
以訪問360為例
超時會當成錯誤被捕獲
二、練習
1.函數調用io.Copy(dst, src)
會從src中讀取內容,並將讀到的結果寫入到dst中,使用這個函數替代掉例子中的ioutil.ReadAll來拷貝響應結構體到os.Stdout,避免申請一個緩衝區(例子中的b)來存儲。記得處理io.Copy返回結果中的錯誤。
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"os"
)
func main() {
for _, arg := range os.Args[1:] {
res, err := http.Get(arg)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
out, err := os.Create("/tmp/buf_file2.txt")
// 初始化一個 io.Writer
wt := bufio.NewWriter(out)
result, err := io.Copy(wt, res.Body)
defer res.Body.Close()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(result)
wt.Flush()
}
}
2.如果輸入的url參數沒有 //
前綴的話,為這個url加上該前綴。你可能會用到strings.HasPrefix
這個函數。
strings.Hasprefix(s, prefix)
可以識別字元串的開頭,如s字元串以prefix開頭則返回true,否則為false,函數原型如下:
func HasPrefix(s, prefix string) bool {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix //通過切片處理
}
我們只需對參數做判斷即可。
package main
import (
"bufio"
"fmt"
"io"
"net/http"
"os"
"strings"
)
func main() {
for _, arg := range os.Args[1:] {
if strings.HasPrefix(arg, "//") {
fmt.Println(arg)
get_txt(arg)
} else {
arg = "//" + arg
get_txt(arg)
fmt.Println(arg)
}
}
}
func get_txt(arg string) {
res, err := http.Get(arg)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
out, err := os.Create("/tmp/buf_file2.txt")
// 初始化一個 io.Writer
wt := bufio.NewWriter(out)
result, err := io.Copy(wt, res.Body)
defer res.Body.Close()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Println(result)
wt.Flush()
}
3.h列印出HTTP協議的狀態碼,可以從resp.Status
變數得到該狀態碼。
我們增加一個get_code函數如下,在main將get_txt替換為get_code即可:
func get_code(arg string) {
res, err := http.Get(arg)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
status := res.Status
fmt.Println("Http Code :", status)
}
結果如下:
三、初嘗golang並發(擴展)
輕鬆創建高並發應用是go的特點之一,在我們請求多個url時也可以通過goroutime
(我理解為協程)來創建並發,再通過信道來傳遞協程中的數據。我們來創建一個同時請求所有url的應用。
新創建一個new_routime(arg string, ch chan<- string
goroutime,傳入命令行參數、行道。該函數中包含start_time
和end_time
用於計算此協程運行的時間。
package main
import (
"bufio"
"fmt"
"io"
"io/ioutil"
"net/http"
"os"
"strings"
"time"
)
func get_txt(arg string) {
// ...
}
func get_code(arg string) {
// ...
}
func main() {
start := time.Now() //記錄開始時間
ch := make(chan string) //創建一個字元信道
for _, arg := range os.Args[1:] {
//根據參數開啟協程
if strings.HasPrefix(arg, "//") {
go new_routime(arg, ch)
} else {
arg = "//" + arg
go new_routime(arg, ch)
}
}
//讀取信道數據
for range os.Args[1:] {
fmt.Println(<-ch)
}
end := time.Since(start).Seconds() //結束時間
fmt.Printf("time used :%.2fs\n", end)
}
func new_routime(arg string, ch chan<- string) {
start_time := time.Now()
res, err := http.Get(arg)
if err != nil {
ch <- fmt.Sprintf("err:", err)
return
}
size_bytes, err := io.Copy(ioutil.Discard, res.Body)
res.Body.Close()
if err != nil {
ch <- fmt.Sprintf("reading usl:%v", err)
return
}
end_time := time.Since(start_time).Seconds()
ch <- fmt.Sprintf("%.2fs %10d %s", end_time, size_bytes, arg) //執行時間、請求大小、url
}
Sprintf
將數據輸出到字元串或信道對象中。
ioutil.Discard
是一個臨時垃圾回收站,可以將不關注數據輸出到此。
執行後結果如下:
書籍參考:Go語言聖經
文章有不足的地方歡迎在評論區指出。
歡迎收藏、點贊、提問。關注頂級飲水機管理員,除了管燒熱水,有時還做點別的。