# Day09 标准库net/http与context
Go语言内置的net/http包提供了HTTP客户端和服务端的实现
# HTTP 客户端
- GET请求示例
func getDemo() {
resp, err := http.Get("https://httpbin.org/get")
if err != nil {
fmt.Println(err)
return
}
// 注意点:程序在使用完response后必须关闭回复的主体
defer resp.Body.Close()
// 借助特定工具 ioutil.ReadAll
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%s\n", b)
}
resp, err := http.Get("http://example.com/")
resp, err := http.Post("http://example.com/upload", "image/jpeg", &buf)
resp, err := http.PostForm("http://example.com/form", url.Values{"key": {"Value"}, "id": {"123"}})
- 带参数的GET请求示例
func getByParamDemo() {
apiUrl := "https://httpbin.org/get"
param := url.Values{}
param.Set("name", "linda")
param.Set("age", "18")
u, err := url.ParseRequestURI(apiUrl)
if err != nil {
fmt.Println("ParseRequestURI Err:", err)
return
}
u.RawQuery = param.Encode() // URL encode age=18&name=linda
fmt.Println("Request Info:", u.String())
resp, err := http.Get(u.String())
if err != nil {
fmt.Println("Get Err:", err)
return
}
// resp *Response 延时关闭
defer resp.Body.Close()
// 借助特定工具 ioutil.ReadAll
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%s\n", b)
}
- POST请求示例
func postDemo() {
url := "https://httpbin.org/post"
// 表单数据
//contentType := "application/x-www-form-urlencoded"
//data := "name=linda&age=18"
// json 数据
contentType := "application/json"
data := `{"key": "value"}`
resp, err := http.Post(url, contentType, strings.NewReader(data))
if err != nil {
fmt.Println(err)
return
}
// resp *Response 延时关闭
defer resp.Body.Close()
// 借助特定工具 ioutil.ReadAll
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%s\n", b)
}
- 自定义Client
func client_demo() {
tr := &http.Transport{
DisableCompression: true,
}
client := &http.Client{
Transport: tr,
Timeout: 5 * time.Second,
}
resp, err := client.Get("https://httpbin.org/get")
if err != nil {
fmt.Println(err)
return
}
// resp *Response 延时关闭
defer resp.Body.Close()
// 借助特定工具 ioutil.ReadAll
b, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%s\n", b)
}
# HTTP 服务端
ListenAndServe使用指定的监听地址和处理器启动一个HTTP服务端
func sayHello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World!")
}
func sayIndex(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello Index!")
}
func server_demo() {
http.HandleFunc("/", sayHello)
http.HandleFunc("/index", sayIndex)
err := http.ListenAndServe(":8090", nil)
if err != nil {
fmt.Printf("http server failed, err:%v\n", err)
return
}
}
# Context 背景介绍
- 基本示例
var wg sync.WaitGroup
func worker() {
for {
fmt.Println("worker")
time.Sleep(time.Second)
}
// 如何接收外部命令实现退出
wg.Done()
}
func main() {
wg.Add(1)
go worker()
// 如何优雅的实现结束子goroutine
wg.Wait()
fmt.Println("Done")
}
- 全局变量方式
var wg sync.WaitGroup
var exit bool
// 全局变量方式存在的问题:
// 1. 使用全局变量在跨包调用时不容易统一;
// 2. 如果worker中再启动goroutine,就不太好控制;
func worker() {
for {
fmt.Println("worker")
time.Sleep(time.Second)
if exit {
break
}
}
wg.Done()
}
func main() {
wg.Add(1)
go worker()
time.Sleep(3 * time.Second) // sleep3秒以免程序过快退出
exit = true // 修改全局变量实现子goroutine的退出
wg.Wait()
fmt.Println("Done")
}
- 通道方式
var wg sync.WaitGroup
// 管道方式存在的问题:
// 1. 使用全局变量在跨包调用时不容易实现规范和统一,需要维护一个共用的channel
func worker(exitChan chan struct{}) {
LOOP:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-exitChan: // 等待接收上级通知
break LOOP
default:
}
}
wg.Done()
}
func main() {
var exitChan = make(chan struct{})
wg.Add(1)
go worker(exitChan)
time.Sleep(3 * time.Second) // sleep3秒以免程序过快退出
exitChan <- struct{}{} // 修改全局变量实现子goroutine的退出
close(exitChan)
wg.Wait()
fmt.Println("Done")
}
- 官方版本
var wg sync.WaitGroup
func worker(ctx context.Context) {
LOOP:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-ctx.Done(): // 等待接收上级通知
break LOOP
default:
}
}
wg.Done()
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
go worker(ctx)
time.Sleep(3 * time.Second) // sleep3秒以免程序过快退出
cancel() // 通知子goroutine结束
wg.Wait()
fmt.Println("Done")
}
当子goroutine又开启另外一个goroutine时,只需要将ctx传入即可:
var wg sync.WaitGroup
func worker(ctx context.Context) {
go worker2(ctx)
LOOP:
for {
fmt.Println("worker")
time.Sleep(time.Second)
select {
case <-ctx.Done(): // 等待上级通知
break LOOP
default:
}
}
wg.Done()
}
func worker2(ctx context.Context) {
LOOP:
for {
fmt.Println("worker2")
time.Sleep(time.Second)
select {
case <-ctx.Done(): // 等待上级通知
break LOOP
default:
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
wg.Add(1)
go worker(ctx)
time.Sleep(time.Second * 3)
cancel() // 通知子goroutine结束
wg.Wait()
fmt.Println("over")
}