Practical Go. Amit Saha

Чтение книги онлайн.

Читать онлайн книгу Practical Go - Amit Saha страница 23

Автор:
Жанр:
Серия:
Издательство:
Practical Go - Amit Saha

Скачать книгу

function.

      2 Set up a signal handler that will create a handler for the SIGINT and SIGTERM signal. When one of the signals is received, the signal handling code will manually call the cancellation function returned in step 1.

      3 Execute the external program using the CommandContext() function using the context created in step 1.

      Step 1 is implemented in a function, createContextWithTimeout() :

      func createContextWithTimeout(d time.Duration) (context.Context, context.CancelFunc) { ctx, cancel := context.WithTimeout(context.Background(), d) return ctx, cancel }

      The WithTimeout() function from the context package is called to create a context that is canceled when a specified unit of time, d, expires. The first parameter is an empty non- nil context created via a call to the context.Background() function. The context, ctx, and the cancellation function, cancel, are returned. We do not call the cancellation function here since we need the context to be around for the lifetime of the program.

      Step 2 is implemented in the setupSignalHandler() function:

      func setupSignalHandler(w io.Writer, cancelFunc context.CancelFunc) { c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) go func() { s := <-c fmt.Fprintf(w, "Got signal:%v\n", s) cancelFunc() }() }

      Step 3 is implemented by the following function:

      func executeCommand(ctx context.Context, command string, arg string) error { return exec.CommandContext(ctx, command, arg).Run() }

      // chap2/user-signal/main.go package main import ( "context" "fmt" "io" "os" "os/exec" "os/signal" "time" ) // TODO Insert definition of createContextWithTimeout() as above // TODO Insert definition of setupSignalHandler() as above // TODO Insert definition of executeCommand as above func main() { if len(os.Args) != 3 { fmt.Fprintf(os.Stdout, "Usage: %s <command> <argument>\n", os.Args[0]) os.Exit(1) } command := os.Args[1] arg := os.Args[2] // Implement Step 1 cmdTimeout := 30 * time.Second ctx, cancel := createContextWithTimeout(cmdTimeout) defer cancel() // Implement Step 2 setupSignalHandler(os.Stdout, cancel) // Implement Step 3 err := executeCommand(ctx, command, arg) if err != nil { fmt.Fprintln(os.Stdout, err) os.Exit(1) } }

      The main() function starts by checking to see if the expected number of arguments have been specified. Here we implement a basic user interface and expect the application to be executed as ./application sleep 60 where sleep is the command to be executed and 60 is the argument to the command. Then we store the command to be executed and the argument to it in two string variables: – command and arg. The createContextWithTimeout() function is then called with a duration object specifying a 30-second time-out. The function returns a context, ctx, and a context cancellation function, cancel. In the next statement, we call the function in a deferred call.

      We then call the setupSignalHandler() function, passing it two parameters: – os.Stdout and the context's cancellation function, cancel .

      Finally, we call the executeCommand() function with the context object created, ctx ; the command to execute, command ; and the argument to the command, arg. If there is an error returned, it is printed.

      Create a new directory, chap2/user-signal, and initialize a module inside it:

      $ mkdir -p chap2/user-signal $ cd chap2/user-signal $ go mod init github.com/username/user-signal

      Next, save Listing 2.8 as a new file, main.go, and build it:

      $ go build -o application

      Considering that the time-out is set to 30 seconds, let's try executing the sleep command with a value for the time to sleep:

      We ask the sleep command to sleep for 60 seconds but manually abort it by pressing Ctrl+C. The error message tells us how the command was aborted.

      Next, we sleep for 10 seconds:

      As 10 seconds is lower than the context time-out for 30 seconds, it exits cleanly. Finally, let's execute the sleep command for 31 seconds:

      % ./listing7 sleep 31 signal: killed

      Now we can see that the time-out context kicks in and kills the process.

      In this chapter, you learned about patterns for implementing scalable command-line applications. You learned how to implement a sub-command-based interface for your application, and you built upon it to design a scalable architecture for applications with sub-commands. Then you learned to use the context package to implement certain control over the runtime behavior of your applications. Finally, you used goroutines and channels to allow the user to interrupt the application using contexts and signals.

      In the next chapter, we will continue our exploration into the world of writing command-line applications as you learn about writing HTTP clients. You will do so as you build out the HTTP client implementation for which we laid the foundation in this chapter.

      Конец ознакомительного фрагмента.

      Текст предоставлен ООО «ЛитРес».

      Прочитайте эту книгу целиком, купив полную легальную версию на ЛитРес.

      Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона,

Скачать книгу