Translation Go net/http Full Manual for timeout mechanism

Source: Internet
Author: User
Tags response write set time
This is a creation in Article, where the information may have evolved or changed.

Catalogue [−]

    1. Setdeadline
    2. Server-side Timeout settings
      1. http. Listenandserve's error
      2. About Flow
    3. Client timeout settings
    4. Cancel and Context

English Original source: The Complete Guide to Go net/http Timeouts, author: Filippo Valsorda

When you use go to write HTTP server and client, time-out processing is always the most error-prone and one of the most subtle places. Errors can come from many places, and an error can wait for a long time without results until a network failure or process hangs.

HTTP is a complex, multi-phase (multi-stage) protocol, so there is no universal timeout solution, such as a streaming service, a JSON API, and a comet service that has different requirements for timeouts, often the default value is not what you want.

In this article I will disassemble the stages that require a timeout setting and see what different ways to handle it, including the server side and the client.

Setdeadline

First, you need to know the network primitive (primitive) that the go implementation timed out: Deadline (Deadline).

net.ConnSeveral methods are available for deadline Set[Read|Write]Deadline(time.Time) . Deadline is an absolute time value, and when this time is reached, all I/O operations fail, returning a timeout (timeout) error.

deadline not timed out (timeout). Once they are set, they are permanently active (or until the next call to Setdeadline), regardless of whether the connection is being used and how it is used. So if you want to use SetDeadline the set up timeout mechanism, you have to Read/Write call it each time before the operation.

You may not want to invoke it yourself SetDeadline , but instead let it net/http call you instead, so you can call the more advanced timeout method. Keep in mind, however, that all timeouts are implemented based on deadline, so they will not reset this value every time they receive or send (so they does not reset every times data is sent or received).

The rain of Jiangnan is correct:
It should be because "deadline is an absolute time value" and not a true timeout mechanism, so the author specifically reminds you that this value is not automatically reset and needs to be set manually each time.

Server-side Timeout settings

For servers exposed to the Web, setting timeouts for client connections is critical, otherwise a giant slow or hidden client could cause the file handle to fail to release, resulting in the server having the following error:

Http:accept error:accept TCP [::]:80:accept4:too many open files; Retrying in 5ms  

http.ServerThere are two ways to set timeouts: ReadTimeout and and writetimeout '. You can set them in the display:

12345
SRV: = &http. server{      readtimeout: 5 * time. Second,    writetimeout: Ten * time. Second,}log. Println (SRV. Listenandserve ())

ReadTimeoutThe time calculation is completely read from the connection received (accept) to the request body (if you do not read the body, then the time expires until the header is read). Its internal implementation is Accept called immediately in the SetReadDeadline method (line of code).

12345678
    ... if d: = C.server.readtimeout; D! = 0 {c.rwc.setreaddeadline (time. Now (). ADD (d))}if d: = C.server.writetimeout; D! = 0 {c.rwc.setwritedeadline (time. Now (). ADD (d))}  ...

WriteTimeoutThe time calculation normally begins at the end of the read of the request header to the end of the response write (that is, the declaration period of the Servehttp method), by invoking the implementation at the readRequest end of the method SetWriteDeadline (the line of code).

12345678910111213141516
func (c *conn) readrequest (CTX context. Context) (w *response, err error) {if c.hijacked () {returnnil, errhijacked}if d: = c. Server. ReadTimeout; D! = 0 {c.rwc.setreaddeadline (time. Now (). ADD (d))}if d: = C.server.writetimeout; D! = 0 {deferfunc() {C.rwc.setwritedeadline (time). Now (). ADD (d))} ()}  ...}

However, when the connection is HTTPS, SetWriteDeadline Accept it is called immediately after (code), so its time calculation also includes the time at which the TLS handshake was written. The annoying thing is that this means (and only in this case) that WriteTimeout the set time also contains the time to read Headerd to the first byte of the body to read.

12345678
if tlsconn, OK: = C.RWC. (*tls. Conn); OK {if d: = c.server.readtimeout; d! = 0 {c.rwc.setreaddeadline (time). Now (). ADD (d))}if d: = C.server.writetimeout; D! = 0 {c.rwc.setwritedeadline (time. Now (). ADD (d))}    ...

When you are dealing with untrusted clients and networks, you should set a read-write timeout at the same time, so that the client does not hold the connection because it is slow to read or slow to write.

Finally, there is one more http.TimeoutHandler way. It is not a server parameter, but a handler wrapper function that can restrict ServeHTTP invocation. It caches response and sends 504 Gateway Timeout errors if deadline is exceeded. Note that this function has a problem in 1.6 and is corrected in 1.6.2.

http. Listenandserve's error

Incidentally, the package wraps the net/http bypass http.Server function http.ListenAndServe , http.ListenAndServeTLS and http.Serve is not suitable for implementing the Internet server. These functions let the timeout setting be not enabled by default, and you have no way to set enable timeout processing. So if you use them, you will soon find a connection leak, too many file handles. I have made this mistake at least five or six times.

Instead, you should create an http.Server example, set up ReadTimeout and WriteTimeout , as in the example above, use the appropriate method.

About Flow

The thing is, there is no way to ServeHTTP access the underlying from the net.Conn , so the stream service is forced not to set WriteTimeout (this may also be why the default value of these values is always 0). If you cannot access net.Conn it, you cannot invoke it every time Write SetWriteDeadline to implement a correct idle timeout.

Also, there is no way to cancel a blocking ResponseWriter.Write because ResponseWriter.Close there is no document stating that it can cancel a blocking concurrent write. There is no way to use a timer to create a Russian-handmade timeout cup with a streaming server that cannot be shielded from slow-read clients. I submitted a [bug] (https://github.com/golang/go/issues/16100), welcome feedback.

Editor 's note: The author's statement here is problematic and can be obtained through hijack net.conn, since it is possible to get net. Conn, we can call its Setwritedeadline method. The code example is as follows:

123456789101112131415161718192021222324252627282930313233343536
 PackageMainImport("FMT""Log""Net/http")funcMain () {http. Handlefunc ("/hijack",func(W http. Responsewriter, R *http. Request) {HJ, OK: = W. (http. Hijacker)if!ok {http. Error (W,"webserver doesn ' t support hijacking", HTTP. Statusinternalservererror)return}conn, BUFRW, err: = HJ. Hijack ()ifErr! =Nil{http. Error (W, err. Error (), HTTP. Statusinternalservererror)return}//Don ' t forget to close the connection:deferConn. Close () Conn. Setwritedeadline (time. Now (). Add(Ten* Time. Second)) Bufrw. WriteString ("Now we ' re speaking raw TCP. Say Hi: ") BUFRW. Flush () s, err: = BUFRW. ReadString (' \ n ')ifErr! =Nil{log. Printf ("Error reading string:%v", err)return}fmt. fprintf (BUFRW,"You said:%q\nbye.\n", s) bufrw. Flush ()})}

Client timeout settings

Client side of the time-out is complex and complex, said simple is simple, see how you use, the most important thing is not to have the resources to leak the situation or the program is stuck.

The simplest way is to use http.Client the Timeout field. Its time calculation includes the connection (Dial) to the response body after reading.

1234
c: = &http. client{      Timeout:  * time. SECOND,}RESP, err: = C.get ("https://blog.filippo.io/")

Just like the server side, http.GET there is no time-out setting when using the client, so it is also dangerous to use on the Internet.

There are some finer-grained timeout controls:

    • net.Dialer.TimeoutRestricting the time to establish a TCP connection
    • http.Transport.TLSHandshakeTimeoutLimit the time of the TLS handshake
    • http.Transport.ResponseHeaderTimeoutLimit the time to read response headers
    • http.Transport.ExpectContinueTimeoutRestricts the time that the client waits between sending the included Expect: 100-continue header to the response that receives the continue send body. Note Setting this value in 1.6 disables HTTP/2 ( DefaultTransport since 1.6.2 is a special case)
1234567891011
c: = &http. client{      Transport: &transport{        Dial: (&net. dialer{                Timeout:    * time. Second,                KeepAlive:  * time. Second,        }). Dial,        tlshandshaketimeout:   Ten * time. Second,        responseheadertimeout: Ten * time. Second,        expectcontinuetimeout: 1 * time. Second,    }}

As I said, there is no way to limit the time it takes to send a request. Read response body (the original is read the request body, according to understand should be read response can be manually controlled) the time spent can be manually through one time.Timer to achieve, read occurs after the call Client.do (see the next section).

Finally, in Go 1.7, add one http.Transport.IdleConnTimeout that does not control the blocking phase of the client request, but can control how long a connection in the connection pool can be idle.

Note that a client default can execute the redirect. http.Client.Timeoutcontains all redirect , while fine-grained timeout control parameters are valid only for a single request, because http.Transport it is an underlying type, not a redirect concept.

Cancel and Context

net/httpThere are two ways to cancel a client request: Request.Cancel as well as go 1.7 new plus Context .

Request.Cancelis an optional channel, when you set this value and close it, the request terminates, as if it were timed out (the actual implementation is the same, I found a 1.7 bug when writing this article, The error returned by all cancel operations is still timeout error).

We can use Request.Cancel and time.Timer to construct a fine-grained timeout control that allows you to defer deadline when reading streaming data:

123456789101112131415161718192021222324252627282930313233343536373839404142
 PackageMainImport("IO"    "Io/ioutil"    "Log"    "Net/http"    "Time")funcMain () {c: = Make(Chan struct{}) Timer: = time. Afterfunc(5*time. Second,func() {Close(c)})//Serve bytes every second.Req, Err: = http. Newrequest ("GET","http://httpbin.org/range/2048?duration=8&chunk_size=256",Nil)ifErr! =Nil{log. Fatal (Err)} req. Cancel = c log. Println ("Sending request ...") RESP, err: = http. Defaultclient.do (req)ifErr! =Nil{log. Fatal (ERR)}deferResp. Body.close () log. Println ("Reading body ...") for{Timer. Reset(2* Time. Second)//Try Instead:timer. Reset (* time.millisecond)_, err = Io. Copyn (Ioutil. Discard, resp. Body, the)ifErr = = Io. EOF { Break}Else ifErr! =Nil{log. Fatal (Err)}}}

In the example above we set a timeout of 5 seconds for the Do method execution phase, but we spent at least 8 seconds executing 8 times to read the desired body and set a 2-second timeout each time. We can do this for the streaming API to avoid the program dying there. If more than two seconds we did not read from the server to the data, Io. Copyn will return an net/http: request canceled error.

In 1.7, the context package was upgraded and entered into the standard library. The context has a lot of features to learn, but for the content described in this article, you just need to wait until it can be replaced and thrown away Request.Cancel .

To cancel the request with the context is simple, we just need to get a new context and its cancel () function, which is passed through the context. The Withcancel method gets, then creates a request and uses Request.WithContext it to bind it. When we want to cancel this request is, we call cancel() cancel this context:

12345678910
CTX, Cancel: = context. Withcancel (context. TODO ())  timer: = time. Afterfunc(5func() {      cancel ()}) req, err: = http. Newrequest ("GET""http://httpbin.org/range/2048?duration=8&chunk_size=256"nil)  ifnil {      log. Fatal (err)}req = req. Withcontext (CTX)

The benefit of the context is also that if the parent context is canceled (passed in at the time of the context.WithCancel call), the sub-context is also canceled and the command is passed.

Well, that's all I want to say, I hope I'm not over your reading deadline.

The author's company, CloudFlare, is recruiting in the UK, USA and Singapore. Cloud startups, quite well known.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.