In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-11 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains how to use cancellation parameters to make Go net/http services more flexible. The explanation in this article is simple and clear and easy to learn and understand. Please follow the editor's train of thought to study and learn how to use cancellation parameters to make Go net/http services more flexible.
Service timeout-fundamentals
In web programming, timeouts are usually divided into client-side and server-side timeouts. I want to study this topic because I have encountered an interesting server-side timeout problem. This is why we will focus on the service-side timeout in this article.
Let's first explain the basic term: a timeout is a time interval (or boundary) used to identify a specific behavior to be completed during this period of time. If the operation is not completed within a given time frame, a timeout occurs and the operation is cancelled.
From the initialization of a net/http service, you can see some basic configuration of timeouts:
Srv: = & http.Server {ReadTimeout: 1 * time.Second, WriteTimeout: 1 * time.Second, IdleTimeout: 30 * time.Second, ReadHeaderTimeout: 2 * time.Second, TLSConfig: tlsConfig, Handler: srvMux,}
Services of type http.Server can be initialized with four different timeout:
ReadTimeout: the maximum time it takes to read the entire request, including the request body
WriteTimeout: the maximum time allowed to write the response
IdleTimetout: maximum idle time allowed when keep active (keep-alive) is turned on
ReadHeaderTimeout: the maximum length of time allowed to read the request header
The chart showing the timeout above:
Service lifecycle and timeout
Look out! Don't think these are all the timeouts you need. In addition, there are a lot of timeouts, which provide smaller granularity control that will not take effect on our continuously running HTTP processors.
Please let me explain.
Timeout and deadline
If we look at the source code of net/http, especially when we see the `conn` type [1], we will find that conn actually uses net.Conn connection, and net.Conn represents the underlying network connection:
/ / Taken from: https://github.com/golang/go/blob/bbbc658/src/net/http/server.go#L247 / / A conn represents the server-side of an HTTP connection. Type conn struct {/ / server is the server on which the connection arrived. / / Immutable; never nil. Server * Server / / * Snipped * / / rwc is the underlying network connection. / / This is never wrapped by other types and is the value given out / / to CloseNotifier callers. It is usually of type * net.TCPConn or / / * tls.Conn. Rwc net.Conn / / * Snipped *}
In other words, our HTTP request is actually based on an TCP connection. In terms of type, the TLS connection is * net.TCPConn or * tls.Conn.
The serve function [2] calls the readRequest function when processing each request [3]. ReadRequest uses the timeout value we set [4] to set the deadline of the TCP connection:
/ / Taken from: https://github.com/golang/go/blob/bbbc658/src/net/http/server.go#L936 / / Read next request from connection. Func (c * conn) readRequest (ctx context.Context) (w * response, err error) {/ / * Snipped* t0: = time.Now () if d: = c.server.readHeaderTimeout (); d! = 0 {hdrDeadline = t0.Add (d)} if d: = c.server.ReadTimeout D! = 0 {wholeReqDeadline = t0.Add (d)} c.rwc.SetReadDeadline (hdrDeadline) if d: = c.server.WriteTimeout; d! = 0 {defer func () {c.rwc.SetWriteDeadline (time.Now (). Add (d))} ()} / / * Snipped*}
From the summary above, we can see that the timeout value we set for the service ultimately shows the deadline of the TCP connection rather than the HTTP timeout.
So, what is deadline? What is the working mechanism? If our requests take too long, will they cancel our connection?
A simple way to understand deadline is to think of it as a point in time that limits the occurrence of a particular behavior acting on a connection. For example, if we set a write deadline, after that deadline, all writes to the connection will be rejected.
Although we can use deadline to simulate timeout operations, we still have no control over how long the processor takes to complete the operation. Deadline acts on connections, so our service returns (error) results only after the processor attempts to access the properties of the connection, such as writing to http.ResponseWriter.
To actually verify the above discussion, let's create a small handler that takes longer to complete the operation than the timeout we set for the service:
Package main import ("fmt"io"net/http"time") func slowHandler (w http.ResponseWriter, req * http.Request) {time.Sleep (2 * time.Second) io.WriteString (w, "I am slow!\ n")} func main () {srv: = http.Server {Addr: ": 8888", WriteTimeout: 1 * time.Second, Handler: http.HandlerFunc (slowHandler) } if err: = srv.ListenAndServe () Err! = nil {fmt.Printf ("Server failed:% s\ n", err)}}
The above service has a handler, and the handler takes two seconds to complete. On the other hand, the WriteTimeout property of http.Server is set to 1 second. Based on these configurations of the service, we speculate that handler cannot write the response to the connection.
We can use go run server.go to start the service. Use curl localhost:8888 to send a request:
$time curl localhost:8888 curl: (52) Empty reply from server curl localhost:8888 0.01s user 0.01s system 0% CPU 2.021 total
The request takes two seconds to complete, and the response returned by the service is empty. Although our service knew that we couldn't write the response after 1 second, handler took an extra 100% (2 seconds) to complete the processing.
Although this is a similar timeout process, its greater role is to prevent the service from doing more and end the request when the timeout is reached. In our example above, handler is processing the request until it is completed, even if the response write timeout (1 second) is exceeded by 100% (2 seconds).
The most fundamental question is, how should we set the timeout to be more effective for processors?
Processing timeout
Our goal is to ensure that our slowHandler is processed within 1 second. If it exceeds 1 second, our service will stop running and return the corresponding timeout error.
In Go and some other programming languages, composition is often the best way to design and develop. The standard library's `net/ http` package [5] has many compatible elements that developers can easily combine without complex design considerations.
Based on this, the net/http package provides `TimeoutHandler` [6]-which returns a handler running within a given time limit.
Function signature:
Func TimeoutHandler (h Handler, dt time.Duration, msg string) Handler
The first parameter is Handler, the second parameter is time.Duration (timeout), and the third parameter is of type string, the information returned when the timeout is reached.
To encapsulate our slowHandler with TimeoutHandler, we just need to:
Package main import ("fmt"io"net/http"time") func slowHandler (w http.ResponseWriter, req * http.Request) {time.Sleep (2 * time.Second) io.WriteString (w, "I am slow!\ n")} func main () {srv: = http.Server {Addr: ": 8888", WriteTimeout: 5 * time.Second, Handler: http.TimeoutHandler (http.HandlerFunc (slowHandler), 1*time.Second) "Timeout!\ n"),} if err: = srv.ListenAndServe () Err! = nil {fmt.Printf ("Server failed:% s\ n", err)}}
Two areas to pay attention to are:
We encapsulate slowHanlder in http.TimetoutHandler, with a timeout of 1s and a timeout message of "Timeout!".
We increased the WriteTimeout to 5s to give http.TimeoutHandler enough time to execute. If we don't, when TimeoutHandler starts execution, the deadline has passed and the response can no longer be written.
If we start the service again, when the program runs to slow handler, it will have the following output:
$time curl localhost:8888 Timeout! Curl localhost:8888 0.01s user 0.01s system 1% CPU 1.023 total
1s later, our TimeoutHandler starts to execute, preventing slowHandler from running and returning the text message "Timeout!". If we set the information to empty, handler will return the default timeout response information, as follows:
Timeout Timeout
If you ignore the output, it's neat, isn't it? Now our program won't have too much time-consuming processing; it also avoids potential DoS attacks when someone maliciously sends a request that takes a long time to process.
Although we set the timeout is a great start, it is still only a primary protection. If you are likely to face DoS attacks, you should use more advanced protection tools and techniques. (try Cloudflare [7])
Our slowHandler is just a simple demo. But what happens if our program is more complex and we can make requests to other services and resources? What if our program makes a request to a service such as S3 when it times out?
What will happen?
Unprocessed timeout and request cancellation
Let's expand on our example a little bit:
Func slowAPICall () string {d: = rand.Intn (5) select {case
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 223
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.