forked from hantmac/Mastering_Go_ZH_CN
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch '10.8' of github.com:themoonbear/Mastering_Go_ZH_CN into…
… 10.8
- Loading branch information
Showing
8 changed files
with
487 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
# context使用的高级示例 | ||
|
||
使用 `useContext.go` 程序代码将更好,更深入的说明 `context` 包的功能,该代码分为五部分来介绍。 | ||
|
||
这个例子中,您将创建一个快速响应的 HTTP 客户端,这是一个很常见的需求。事实上,几乎所有的 HTTP 客户端都支持这个功能,您将学习另一个 HTTP 请求超时的技巧,在[第12章](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter12/12.0.md)(Go网络编程基础)。 | ||
|
||
`useContext.go` 程序需要两个命令行参数:要连接的服务器 URL 和应该等待的延迟。如果该程序只有一个命名行参数,那么延迟将是 5 秒。 | ||
|
||
`useContext.go` 的第一段代码如下: | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"io/ioutil" | ||
"net/http" | ||
"os" | ||
"strconv" | ||
"sync" | ||
"time" | ||
) | ||
|
||
var ( | ||
myUrl string | ||
delay int = 5 | ||
w sync.WaitGroup | ||
) | ||
|
||
type myData struct { | ||
r *http.Response | ||
err error | ||
} | ||
``` | ||
|
||
`myUrl` 和 `delay` 都是全局变量,因此它们能从代码的任意位置访问。另外,有一个名为 `w` 的 `sync..WaitGroup` 变量也是全局的,还有一个名为 `myData` 的结构体用于把 web 服务器的响应和一个错误变量绑在一起。 | ||
|
||
`useContext.go` 的第二部分代码如下: | ||
|
||
```go | ||
func connect(c context.Context) error { | ||
defer w.Done() | ||
data := make(chan myData, 1) | ||
tr := &http.Transport{} | ||
httpClient := &http.Client{Transport: tr} | ||
|
||
req, _ := http.NewRequest("GET", myUrl, nil) | ||
``` | ||
上面的代码处理 HTTP 连接。 | ||
> 您会了解更多关于开发 web 服务器和客户端的内容,在[第12章](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/eBook/chapter12/12.0.md)(Go网络编程基础)。 | ||
`useContext.go` 的第三段代码如下: | ||
```go | ||
go func() { | ||
response, err := httpClient.Do(req) | ||
if err != nil { | ||
fmt.Println(err) | ||
data <- myData{nil, err} | ||
return | ||
} else { | ||
pack := myData{response, err} | ||
data <- pack | ||
} | ||
}() | ||
``` | ||
`useContext.go` 的第四段代码如下: | ||
```go | ||
select { | ||
case <-c.Done(): | ||
tr.CancelRequest(req) | ||
<-data | ||
fmt.Println("The request was cancelled!") | ||
return c.Err() | ||
case ok := <-data: | ||
err := ok.err | ||
resp := ok.r | ||
if err != nil { | ||
fmt.Println("Error select:", err) | ||
return err | ||
} | ||
defer resp.Body.Close() | ||
|
||
realHTTPData, err := ioutil.ReadAll(resp.Body) | ||
if err != nil { | ||
fmt.Println("Error select:", err) | ||
return err | ||
} | ||
fmt.Println("Server Response: %s\n", realHTTPData) | ||
} | ||
return nil | ||
} | ||
``` | ||
|
||
`useContext.go` 的其余代码实现了 `main()` 函数,如下: | ||
|
||
```go | ||
func main() { | ||
if len(os.Args) == 1 { | ||
fmt.Println("Need a URL and a delay!") | ||
return | ||
} | ||
|
||
myUrl = os.Args[1] | ||
if len(os.Args) == 3 { | ||
t. err := strconv.Atoi(os.Args[2]) | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
delay = t | ||
} | ||
|
||
fmt.Println("Delay:", delay) | ||
c := context.Background() | ||
c, cancel := contedxt.WithTimeout(c, time.Duration(delay)*time.Second) | ||
defer cancel() | ||
|
||
fmt.Printf("Connecting to %s \n", myUrl) | ||
w.Add(1) | ||
go connect(c) | ||
w.Wait() | ||
fmt.Println("Exiting...") | ||
} | ||
``` | ||
|
||
`context.WithTimeout()` 方法定义了超时期限。`connect()` 函数作为一个 goroutine 执行,将会正常终止或 `cancel()` 函数执行时终止。 | ||
|
||
尽管没必要知道服务端的操作,但看一下 Go 版本的随机减速的 web 服务器也是好的;一个随机数生成器决定您的 web 服务器有多慢。`slowWWW.go` 的源码内容如下: | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"net/http" | ||
"os" | ||
"time" | ||
) | ||
|
||
func random(min, max int) int { | ||
return rand.Intn(max-min) + min | ||
} | ||
|
||
func myHandler(w http.ResponseWriter, r *http.Request) { | ||
delay := random(0, 15) | ||
time.Sleep(time.Duration(delay) * time.Second) | ||
|
||
fmt.Fprintf(w, "Servring: %s\n", r.URL.Path) | ||
fmt.Fprintf(w, "Dealy: %d\n", delay) | ||
fmt.Printf("Served: %s\n", r.Host) | ||
} | ||
|
||
func main() { | ||
seed := time.Now().Unix() | ||
rand.Seed(seed) | ||
|
||
PORT := ":8001" | ||
arguments := os.Args | ||
if len(arguments) == 1 { | ||
fmt.Println("Using default port nubmer: ", PORT) | ||
} else { | ||
PORT = ":" + arguments[1] | ||
} | ||
|
||
http.HandleFunc("/", myHandler) | ||
err := http.ListenAndServer(PORT, nil) | ||
if err != nil { | ||
fmt.Println(err) | ||
os.Exit(10) | ||
} | ||
} | ||
``` | ||
|
||
如您所见,您不需要在 `slowWWW.go` 文件中使用 `context` 包,因为那是 web 客户端的工作,它会决定需要多长时间等待响应。 | ||
|
||
`myHandler()` 函数的代码负责 web 服务器的减速处理。如介绍的那样,延迟可以由 `random(0,15)` 函数在 0 到 14 秒随机产生。 | ||
|
||
如果您使用如 `wget(1)` 的工具访问 `slowWWW.go` 服务器的话,会收到如下输出: | ||
|
||
```shell | ||
$wget -qO- http://localhost:8001/ | ||
Serving: / | ||
Delay: 4 | ||
$wget -qO- http://localhost:8001/ | ||
Serving: / | ||
Delay: 13 | ||
``` | ||
|
||
这是因为 `wget(1)` 的默认超时时间比较大。 | ||
|
||
当 `slowWWW.go` 已经在另一个 Unix shell 里运行后,用方便的 `time(1)` 工具处理执行 `useContext.go` 的输出如下: | ||
|
||
![""](https://github.com/hantmac/Mastering_Go_ZH_CN/tree/master/images/chapter10/10.7.1.jpg) | ||
|
||
这个输出显示只有第三个命令确实从 HTTP 服务器获得了响应——第一和第二个命令都超时了! |
Oops, something went wrong.