func httpGet() {
resp, err := http.Get("http://www.01happy.com/demo/accept.php?id=1")
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
func httpPost() {
resp, err := http.Post("http://www.01happy.com/demo/accept.php",
"application/x-www-form-urlencoded",
strings.NewReader("name=cjb"))
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
使用这个方法,第二个参数(contentType)必须设置为"application/x-www-form-urlencoded"
,否则post参数无法传递
func httpPostForm() {
resp, err := http.PostForm("http://www.01happy.com/demo/accept.php",
url.Values{"key": {"Value"}, "id": {"123"}})
if err != nil {
// handle error
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
func httpDo() {
client := &http.Client{}
req, err := http.NewRequest("POST", "http://www.01happy.com/demo/accept.php", strings.NewReader("name=cjb"))
if err != nil {
// handle error
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Cookie", "name=anny")
resp, err := client.Do(req)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// handle error
}
fmt.Println(string(body))
}
++transport.go:++
type Transport struct {
idleMu sync.Mutex
wantIdle bool // user has requested to close all idle conns
idleConn map[connectMethodKey][]*persistConn
idleConnCh map[connectMethodKey]chan *persistConn
reqMu sync.Mutex
reqCanceler map[*Request]func()
altMu sync.RWMutex
altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
//Dial获取一个tcp 连接,也就是net.Conn结构,你就记住可以往里面写request
//然后从里面搞到response就行了
Dial func(network, addr string) (net.Conn, error)
}
==连接池:==
func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) {
...
pconn, err := t.getConn(req, cm)
if err != nil {
t.setReqCanceler(req, nil)
req.closeBody()
return nil, err
}
return pconn.roundTrip(treq)
}
省略前面对参数的检查部分,主要有两步:
pconn, err := t.getConn(req, cm)
func (t *Transport) getConn(req *Request, cm connectMethod) (*persistConn, error) {
...
type dialRes struct {
pc *persistConn
err error
}
dialc := make(chan dialRes)
//定义了一个发送 persistConn的channel
...
// 启动了一个goroutine, 这个goroutine 获取里面调用dialConn搞到
// persistConn, 然后发送到上面建立的channel dialc里面,
go func() {
pc, err := t.dialConn(cm)
dialc <- dialRes{pc, err}
}()
idleConnCh := t.getIdleConnCh(cm)
select {
case v := <-dialc:
// dialc 我们的 dial 方法先搞到通过 dialc通道发过来了
return v.pc, v.err
case pc := <-idleConnCh:
// 这里代表其他的http请求用完了归还的persistConn通过idleConnCh这个
// channel发送来的
handlePendingDial()
return pc, nil
case <-req.Cancel:
handlePendingDial()
return nil, errors.New("net/http: request canceled while waiting for connection")
case <-cancelc:
handlePendingDial()
return nil, errors.New("net/http: request canceled while waiting for connection")
}
}
三个goroutine通过channel互相协作的过程,
1. 主goroutine ->requestAndChan -> 读循环goroutine:读循环goroutine 通过channel requestAndChan 接受主goroutine发送的request(rc := <-pc.reqch), 并从tcp输出流中读取response, 然后反序列化到结构体中, 最后通过channel 返给主goroutine (rc.ch <- responseAndError{resp, err} )
2. 主goroutine ->writeRequest-> 写循环goroutine:select channel中主gouroutine的request,然后写入tcp输入流,如果出错了,channel 通知调用者
3. 主goroutine 通过select 监听各个channel上的数据, 比如请求取消, timeout,长连接挂了,写流出错,读流出错, 都是其他goroutine 发送过来的, 跟中断一样,然后相应处理
细粒度:细粒度只对于单次连接起作用
http.Client的默认超时时限是0,不超时,可以设置。
实际上是一个连接池,全局复用。初始化Transport,然后复用