This is a creation in Article, where the information may have evolved or changed. Locks can be used for synchronization operations. But if used improperly, it can also cause significant performance problems. A more common problem is the HTTP handlers location. In particular, it is easy to inadvertently lock network I/O. To understand this problem, we'd better look at an example. In this post, I'll use Go. To do this, we need to write a simple HTTP server to report the number of requests it receives. All the code can be obtained from [here] (Https://github.com/gobuildit/gobuildit/tree/master/lock). The service that reports the number of requests looks like this: "' gopackage main//import statements//... const (payloadbytes = 1024x768 * 1024x768) var (mu sync. Mutexcount int)//register handler and start server in main//...//Bad:don ' t do this.func root (w http. Responsewriter, R *http. Request) {mu. Lock () defer MU. Unlock () Count++msg: = []byte (Strings. Repeat (FMT. Sprintf ("%d", count), payloadbytes)) W.write (msg)} ' root ' handler was unlocked at the top with a regular lock and ' defer '. Then, during holding the lock, the value of ' count ' is increased, and the value of ' count ' is written to ' http ' by repeating ' payloadbytes ' data generated. Responsewriter '. For inexperienced people, this handler looks perfect. In fact, it can cause a noticeable performance problem. Locked during network I/O, the speed at which the handler is executed depends on the slowest client. In order to be able to see the problem directly, we need to simulate a slow read client (hereinafter referred to as the slow client). In fact, because some clients are too slow, it is necessary to set a time-out for Go HTTP clients exposed to an open network. Because the kernel has a mechanism for caching writes and reads from TCP sockets, our simulations require some tricks. Suppose we create a client that sends a ' GET ' request, but no data was read from the socket (code in [here] (Https://github.com/gobuildit/gobuildit/blob/master/lock/client/main.go)). Does this cause the service to block at ' W.write '? Because the kernel caches read-write data, we don't see any slowdown in service speeds at least until the cache fills up. To observe this slowdown, we want to make sure that every write data is filled with cache. There are two ways. 1) tune the kernel. 2) writes a large number of bytes each time. Tuning the kernel itself is a fascinating matter. You can use [proc directory] (https://twitter.com/b0rk/status/981159808832286720), with all network-related parameters [documentation] (https://www.kernel.org/doc/ Documentation/sysctl/net.txt), also [various] (https://www.cyberciti.biz/faq/linux-tcp-tuning/) [host tuning] (HTTP/ fasterdata.es.net/host-tuning/) [Tutorial] (https://www.tecmint.com/change-modify-linux-kernel-runtime-parameters/). But for us, just writing a large amount of data into the socket can fill the TCP cache of the normal Darwin (v17.4) kernel. Note that running this example, you may need to adjust the amount of data written to ensure that your cache is filled. Now we start the service and use the slow client to observe the speed at which other clients wait for slow clients. The code for the slow client is in [here] (https://github.com/gobuildit/gobuildit/blob/master/lock/client/main.go). First, confirm that a request can be processed quickly: ' ' Curl localhost:8080/# output:# numerous 1 ' without any meaningful delay ' Now we run the slow client First: ' ' # Assuming $GOPATH/github.com/gobuildit/gobuildit/lock Directorygo run client/main.go# outpuT:dialingsending get requestblocking and never reading "" After the slow client connects to the server, try running the "fast" client: "Curl localhost:8080/# hangs" We can see directly how our lock strategy inadvertently blocks the fast client. If we go back to our handler and think about how we use the lock, we will understand the problem. "' Gofunc Root (w http. Responsewriter, R *http. Request) {mu. Lock () defer MU. Unlock ()//...} "By locking at the top of the method and unlocking using ' defer ', we hold the lock object throughout the handler. This process consists of shared state operations, shared state reads, and network data writes. That is, these operations cause problems. Network I/O is [inherently unpredictable] (https://en.wikipedia.org/wiki/Fallacies_of_distributed_computing). Admittedly, we can avoid lengthy calls by configuring timeouts to protect our services, but we cannot guarantee that all network I/O can be completed in a fixed amount of time. The key to solving the problem is not locking around the I/O. In this example, locking around I/O does not make any sense. Locking around I/O can cause our programs to be affected by bad network conditions and slow clients. In fact, we also partially gave up control over the synchronization of our programs. Let's rewrite the handler to lock only the key parts. "' go//good:keep the critical section as small as possible and don ' t lock around//i/o.func Root (w http. Responsewriter, R *http. Request) {mu. Lock () Count++current: = Countmu. Unlock () msg: = []byte (Strings. Repeat (FMT. Sprintf ("%d", current), payloadbytes)) W.write (msg)} "to see the difference, try to use a slow client and a normal client. Similarly, start the slow client first: "' # assuming $GOPATH/github.com/gobuildit/gobuildit/Lock Directorygo Run Client/main.go ' Now, use ' curl ' to send a request: ' Curl localhost:8080/' to see if ' curl ' returns immediately and brings back the expected count. Admittedly, this example is too unnatural and much simpler than a typical production environment code. Also, using the [Atomics] (https://golang.org/pkg/sync/atomic/) package may be more sensible for synchronous counting. Even so, I hope this example illustrates the importance of being cautious and locking. Although there are exceptions, it is often not necessary to lock around I/O in most cases.
via:https://commandercoriander.net/blog/2018/04/10/dont-lock-around-io/
Author: Eno Translator: Alfred-zhong proofreading: polaris1119
This article by GCTT original compilation, go language Chinese network honor launches
This article was originally translated by GCTT and the Go Language Chinese network. Also want to join the ranks of translators, for open source to do some of their own contribution? Welcome to join Gctt!
Translation work and translations are published only for the purpose of learning and communication, translation work in accordance with the provisions of the CC-BY-NC-SA agreement, if our work has violated your interests, please contact us promptly.
Welcome to the CC-BY-NC-SA agreement, please mark and keep the original/translation link and author/translator information in the text.
The article only represents the author's knowledge and views, if there are different points of view, please line up downstairs to spit groove
628 Reads