- 前言
- Go 与操作系统
- Go 内部机制
- Go 基本数据类型
- 4 组合类型的使用
- 5 数据结构
- 6 Go package 中不为人知的知识
- 7 反射和接口
- 8 Go UNIX 系统编程
- 08.1 关于 UNIX 进程
- 08.2 flag 包
- 8.2 flag 包
- 08.3 io.Reader 和 io.Writer 接口
- 08.4 bufio 包
- 08.5 读取文本文件
- 08.6 从文件中读取所需的数据量
- 08.7 为什么我们使用二进制格式
- 08.8 读取 CSV 文件
- 08.9 写入文件
- 08.10 从磁盘加载和保存数据
- 08.11 再看strings包
- 08.12 关于bytes包
- 08.13 文件权限
- 08.14 处理 Unix 信号
- 08.15 Unix 管道编程
- 08.16 遍历目录树
- 08.17 使用 ePBF
- 08.18 关于 syscall.PtraceRegs
- 08.19 跟踪系统调用
- 08.20 User ID 和 group ID
- 08.21 其他资源
- 08.22 练习
- 08.23 总结
- 9 并发 Goroutines、Channel 和 Pipeline
- 10 Go 并发-进阶讨论
- 11 代码测试、优化及分析
- 12 Go 网络编程基础
- 13 网络编程 - 构建服务器与客户端
10.7.1 context 使用的高级示例
使用 useContext.go
程序代码将更好,更深入的说明 context
包的功能,该代码分为五部分来介绍。
这个例子中,您将创建一个快速响应的 HTTP 客户端,这是一个很常见的需求。事实上,几乎所有的 HTTP 客户端都支持这个功能,您将学习另一个 HTTP 请求超时的技巧,在第12章(Go网络编程基础)。
useContext.go
程序需要两个命令行参数:要连接的服务器 URL 和应该等待的延迟。如果该程序只有一个命名行参数,那么延迟将是 5 秒。
useContext.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
的第二部分代码如下:
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章(Go网络编程基础)。
useContext.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
的第四段代码如下:
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.Printf("Server Response: %s\n", realHTTPData)
}
return nil
}
useContext.go
的其余代码实现了 main()
函数,如下:
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 := context.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
的源码内容如下:
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
服务器的话,会收到如下输出:
$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
的输出如下:
这个输出显示只有第三个命令确实从 HTTP 服务器获得了响应——第一和第二个命令都超时了!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论