顶级饮水机管理员

顶级饮水机管理员

提示:本系列文章适合有其他语音基础并对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为例

golang快速入门(四)-LMLPHP

超时会当成错误被捕获

golang快速入门(四)-LMLPHP


二、练习

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)
}

结果如下:

golang快速入门(四)-LMLPHP


三、初尝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是一个临时垃圾回收站,可以将不关注数据输出到此。

执行后结果如下:

golang快速入门(四)-LMLPHP


书籍参考:Go语言圣经

文章有不足的地方欢迎在评论区指出。

欢迎收藏、点赞、提问。关注顶级饮水机管理员,除了管烧热水,有时还做点别的。

05-31 03:36