golang快速入門(四)

頂級飲水機管理員發表於2021-05-30

提示:本系列文章適合有其他語音基礎並對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為例

image-20210530131408230

超時會當成錯誤被捕獲

image-20210530131617770


二、練習

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引數沒有 http:// 字首的話,為這個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, "http://") {
			fmt.Println(arg)
			get_txt(arg)
		} else {
			arg = "http://" + 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)
}

結果如下:

image-20210530152453343


三、初嘗golang併發(擴充套件)

輕鬆建立高併發應用是go的特點之一,在我們請求多個url時也可以通過goroutime(我理解為協程)來建立併發,再通過通道來傳遞協程中的資料。我們來建立一個同時請求所有url的應用。

新建立一個new_routime(arg string, ch chan<- string goroutime,傳入命令列引數、行道。該函式中包含start_timeend_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, "http://") {
			go new_routime(arg, ch)
		} else {
			arg = "http://" + 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是一個臨時垃圾回收站,可以將不關注資料輸出到此。

執行後結果如下:

image-20210530165514957


書籍參考:Go語言聖經

文章有不足的地方歡迎在評論區指出。

歡迎收藏、點贊、提問。關注頂級飲水機管理員,除了管燒熱水,有時還做點別的。

相關文章