In my outcast (the author's own weather preview App) data server, there are several data retrieval tasks to use different go routine to run, each routine within the set interval of time to wake up. One of the most complicated tasks is to download radar images. It's complicated because there are 155 radar stations in the United States that take a new photo every 120 seconds, and we're going to stitch all the radar images together to form a large mosaic. It's kind of like when we use a mobile phone to take a panorama, we stitch multiple edges with overlapping images into one big picture. When go routine is woken up to download a new image, it must perform this operation for all 155 sites as soon as possible. If not enough time, get the stitching chart will be out of sync, each radar station overlapping boundary part will be misaligned. [] (https://raw.githubusercontent.com/studygolang/gctt-images/master/ TIMER-ROUTINES-AND-GRACEFUL-SHUTDOWNS-IN-GO/RADAR-IMG-1.PNG) The radar on the left is from the Tampa Bay radar at 4:51, and as you can see, it covers most of the Florida State area, In fact, the radar station even covers the range of other radar stations, such as Miami. The radar on the right is from the Miami radar station at 4:53, and there's a two-minute difference from the right, (I call this the glare). When we overlay these two radar maps on the map, you don't notice anything wrong, but if the two images are delayed more than a few minutes ago , we can see that there is a big difference in the naked eye. [] (https://raw.githubusercontent.com/studygolang/gctt-images/master/ Timer-routines-and-graceful-shutdowns-in-go/radar-img-2.png) Blue is a radar noise, we'll filter it out, so we're left with green, red, and yellow color blocks to represent real weather conditions. The picture above is downloaded and processed at 4:46, and you can see that they are very close and can be nicely stitched together. The first implementation of our code, using a single go routine, wakes up every 10 minutes, each time this go routine wakes up, it takes 3-4 minutes to download, process, save and write 155 radar station data into MONGO. Although I will stitch up the pictures of each area as closely as possible, but the lag difference between these pictures is too great. Each radar station has a two-minute delay, and the delay of all radar stations is superimposed to make the problem stand out. For all the work, I would use a single go routine as much as possible, because it would keep things simple. In this case, however, a single go routine is not effective. I had to deal with data from multiple radar stations at the same time to reduce the difference in latency. After I added a working pool to handle the data from multiple radar stations, I was able to handle the data of 155 radar station within a minute. So far, I haven't received any complaints from the client development team. In this article, we focus on timing routine and exit codes. In the next article, I'll show you how to add a working pool for your project. I'm going to provide a complete example that can be run. It may be useful as a reference template to enable you to implement your own code. To download This example, you can open a new terminal session and enter the following command: "' BASHCD $HOMEexport gopath= $HOME/examplego get github.com/goinggo/ TIMERDESIGNPATTERNCD example/bin./timerdesignpattern ' Outcast data server is a single application designed for long-running service programs, and this type of program rarely needs to be exited. It is important for your program to gracefully exit when needed. When I was developing this type of program, I always had to make sure from the start that I could tell the application to exit with some signals and not let it hang. A program, the worst thing is that you need to force the kill process to quit. The sample program creates a single go routine and specifies that this routine be awakened every 15 seconds. When the routine wakes up, it takes about 10 seconds to operate. When the work is done, it calculates how many seconds it needs to sleep to ensure that the routine is able to keep waking up every 15 seconds. Let's try to run the program and pull it out while it's running. Then we can begin to learn how it is implemented. We can exit this program by pressing the ENTER key at any time when the program is running. The following is the output that the program exits after running for 7 seconds: "' 2013-09-04t18:58:45.505:main:main:starting Program2013-09-04t18:58:45.505:main:workmanager . startup:started2013-09-04t18:58:45.505:main:workmanager. Startup:completed2013-09-04t18:58:45.505:worktimer: _workmanager.goroutine_worktimer:started2013-09-04t18 : 58:45.505:worktimer: _workmanager.goroutine_worktimer:info:wait to Run:seconds[15]2013-09-04t18:58:52.666:main : Workmanager. Shutdown:started2013-09-04t18:58:52.666:main:workmanager. Shutdown:Info:Shutting Down2013-09-04t18:58:52.666:main:workmanager. Shutdown:Info:Shutting down work Timer2013-09-04t18:58:52.666:worktimer: _workmanager.goroutine_worktimer:shuttin G Down2013-09-04t18:58:52.666:main:workmanager. Shutdown:completed2013-09-04t18:58:52.666:main:main:program complete "It was a great first test, and when we instructed the program to exit, it gracefully exited. Next we'll try to wait for it to start working (the program will wait 15 seconds to start the first work) before attempting to exit it. "' 2013-09-04t19:14:21.312:main:main:starting Program2013-09-04t19:14:21.312:main:workmanager. Startup:started2013-09-04t19:14:21.312:main:workmanager. Startup:completed2013-09-04t19:14:21.312:worktimer: _WORKMANAGER.GOroutine_worktimer:started2013-09-04t19:14:21.313:worktimer: _workmanager.goroutine_worktimer:info:wait to Run: Seconds[15]2013-09-04t19:14:36.313:worktimer: _workmanager.goroutine_worktimer:woke Up2013-09-04T19:14:36.313: Worktimer: _workmanager.goroutine_worktimer:started2013-09-04t19:14:36.313:worktimer: _WorkManager.GoRoutine_ Worktimer:processing Images for Station:02013-09-04t19:14:36.564:worktimer: _workmanager.goroutine_worktimer:proc Essing Images for Station:12013-09-04t19:14:36.815:worktimer: _workmanager.goroutine_worktimer:processing Images Fo R Station:22013-09-04t19:14:37.065:worktimer: _workmanager.goroutine_worktimer:processing Images for station:3201 3-09-04t19:14:37.129:main:workmanager. Shutdown:started2013-09-04t19:14:37.129:main:workmanager. Shutdown:Info:Shutting Down2013-09-04t19:14:37.129:main:workmanager. Shutdown:Info:Shutting down work Timer2013-09-04t19:14:37.315:worktimer: _workmanager.goroutIne_WorkTimer:Info:Request to Shutdown2013-09-04t19:14:37.315:worktimer: _workmanager.goroutine_worktimer:info: Wait to Run:seconds[14]2013-09-04t19:14:37.315:worktimer: _workmanager.goroutine_worktimer:shutting Down2013-09-04 T19:14:37.316:main:workmanager. Shutdown:completed2013-09-04t19:14:37.316:main:main:program complete "This time I waited 15 seconds for the program to start working, and when it started working and finished the fourth picture, I meant Program exits. It also stopped working in time and gracefully withdrew. Let's take a look at the core code that implements timed routine and graceful exit: "Gofunc (WM *workmanager) Worktimer () {for {select {case <-wm. Shutdownchannel:wm. Shutdownchannel <-"Down" to return case <-time. After (Timerperiod): Break} StartTime: = time. Now () Wm. Performthework () EndTime: = time. Now () Duration: = Endtime.sub (startTime) wait = timerperiod-duration}} "in order to be more concise and readable, I removed the code for annotations and output logs. This is a classic job queue channel, and this solution is very elegant. It's a lot more elegant than the same thing that's implemented in C #. The ' Worktimer () ' function runs as a go routine: ' Gofunc Startup () {wm = Workmanager{shutdown:false, Shutdownchannel:make (Chan str ing),} go Wm. Worktimer ()} "WorkManager' is created in a single case (one of the design patterns, a reference to the [singleton mode] (Https://zh.wikipedia.org/wiki/%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F)) pattern, After it is created, it starts to start the timed routine. It has a channel responsible for closing the timed routine, and a flag indicating whether the system is exiting. The timing routine has an infinite loop inside, so it won't terminate unless we indicate that we want it to exit. Let's take a look at the section on the channel in this loop: "' goselect {case <-WM. Shutdownchannel:wm. Shutdownchannel <-"Down" returncase <-time. After (timerperiod): Break}wm. Performthework () "We used the ' SELECT ' statement. This statement is explained here in the official documentation: Http://golang.org/ref/spec#Select_statements We use the ' Select ' statement to ensure that the timing routine is awakened only when it is time to work or when an exit instruction is received. The ' Select ' statement causes the timing routine to block when all channels are not receiving a signal. Only one of the branches executes at a time, which keeps our code in sync. The ' SELECT ' statement allows us to implement atomic, "routine-safe" operations between multiple channel lines with concise code (as long as we put the channel in the same ' SELECT ' statement). In our timed routine ' SELECT ' statement there are two channel, one is responsible for exiting the routine, one is responsible for performing the task. Exit the timed routine code as follows: "' Gofunc Shutdown () {Wm. Shutdown = True Wm. Shutdownchannel <-"Down" <-wm. Shutdownchannel Close (Wm. Shutdownchannel)} ' when we need to exit, we put the ' Shutdown ' tag to ' true ' and then send the string ' down ' to ' Shutdownchannel ', and then we'll go from ' Shutdownchannel ' Inside waiting from the timing rouTine's reply. This data communication synchronizes the entire exit process between the main program and the timed routine. Very good, simple and elegant. To wake up timed routine at a fixed interval, I used a call ' time. After ' function, this function waits for a specified time and then sends the current time to the specified channel. This also wakes up ' select ', which allows the ' performthework ' function to execute. When the ' performthework ' function returns, the timed routine goes back to sleep again, unless another channel receives a new signal. Let's take a look at the ' performthework ' function: ' Gofunc (WM *_workmanager) Performthework () {for count: = 0; count <; count++ {if Wm. Shutdown = = true {return} FMT. PRINTLN ("Processing Images for Station:", count) time. Sleep (Time.millisecond * 250)}} "This function outputs a message at the console every 250 microseconds, with a total output of 40 times. This will take about 10 seconds to complete the task. In this loop, each iteration checks to see if the ' Shutdown ' tag is set to ' true '. This is important because it allows the function to end very quickly when the program exits. We do not want the manager of this program to feel that the program has been suspended while exiting the program. When the ' performthework ' function finishes, the timed routine can execute the ' SELECT ' statement again, and if the program is exiting, the ' SELECT ' statement will immediately wake up to handle the signal from ' Shutdownchannel '. Here, the timer routine again notifies the main routine that it is exiting, thus the entire program gracefully exits. This is my timing routine and graceful exit program code mode, you can also apply this mode in your program. If you download the entire example from Goinggo's code warehouse, you can see the actual code and some gadgets. Read the following article to learn how to implement a working pool that can handle multiple go routine, just as I did with the above working pool of radar images: Https://studygolang.com/articles/14481
Via:https://www.ardanlabs.com/blog/2013/09/timer-routines-and-graceful-shutdowns.html
Author: William Kennedy Translator: Alex-liutao 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
132 reads ∙1 likes