当一个任务失败时,终止所有的例行程序。

huangapple go评论77阅读模式
英文:

Killing all routines when one failed

问题

我需要创建一个程序,每个程序在不同的端口上启动多个服务器,并将每个程序分配给不同的队列。所有服务器和程序都必须在运行中,或者如果至少有一个失败,则其他服务应该关闭。

目前,我正在监听一个通道,如果发生错误,就执行log.Fatal,它会执行os.Exit

  • 这种方式是否适合在一个失败时终止所有程序,而不会出现任何意外行为?
  • 是否有更好的解决方案来解决这个问题?

我的简化程序如下所示:

package main

import (
	"log"
	"net/http"
)

func startWorker(queue string) error {
    // 其他可能返回错误的逻辑在这里

    // 模拟工作程序的工作
    for {
        // 从`queue`获取项目
        // 并对其进行处理
        continue
    }
}

func startServer(port string) error {
    // 其他可能返回错误的逻辑在这里
	return http.ListenAndServe(port, nil)
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
	channel := make(chan error)

	go func() {
		channel <- startServer(":3000")
	}()

	go func() {
		channel <- startServer(":3001")
	}()

	go func() {
		channel <- startWorker("first")
	}()

    go func() {
		channel <- startWorker("second")
	}()

	if err := <-channel; err != nil {
		log.Fatal(err)
	}
}

请注意,我只翻译了代码部分,其他内容不包括在内。

英文:

I need to create a program which starts multiple servers each on different ports and workers each to different queue. All servers and workers has be running or in case if at least one fails other services should be shutted down

Currently I'm listening on a channel and if error would occur just execute log.Fatal which would execute os.Exit

  • Is it proper way of killing all routines when one failed which doesn't occur any unexpected behaviours?
  • Is there a better solution for this problem?

My simplified program looks like this:

package main

import (
	&quot;log&quot;
	&quot;net/http&quot;
)

func startWorker(queue string) error {
    // other logic which 
    // could return an 
    // error goes here
	
    // simulate worker work
    for {
        // get items from `queue`
        // and do something with it
        continue
    }
}

func startServer(port string) error {
    // other logic which 
    // could return an 
    // error goes here
	return http.ListenAndServe(port, nil)
}

func main() {
	http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
	channel := make(chan error)

	go func() {
		channel &lt;- startServer(&quot;:3000&quot;)
	}()

	go func() {
		channel &lt;- startServer(&quot;:3001&quot;)
	}()

	go func() {
		channel &lt;- startWorker(&quot;first&quot;)
	}()

    go func() {
		channel &lt;- startWorker(&quot;second&quot;)
	}()

	if err := &lt;-channel; err != nil {
		log.Fatal(err)
	}
}

答案1

得分: 1

在我看来,os.Exit()log.Fatal()应该只用于开发目的或极其意外的错误。

有一种方法可以优雅地处理服务器错误:

package main

import (
	"context"
	"fmt"
	"net/http"
	"time"
)

func startServer(server *http.Server) error {
	// 其他可能返回错误的逻辑在这里
	return server.ListenAndServe()
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })

	server3000 := &http.Server{Addr: ":3000", Handler: nil}
	server3001 := &http.Server{Addr: ":3001", Handler: nil}

	closeAll := func() {
		server3000.Shutdown(context.Background())
		server3001.Shutdown(context.Background())
	}

	go func() {
		err := startServer(server3000)
		fmt.Printf("发生错误:%s\n", err)
		if err != nil {
			closeAll()
		}
	}()

	go func() {
		err := startServer(server3001)
		fmt.Printf("发生错误:%s\n", err)
		if err != nil {
			closeAll()
		}
	}()

	timer := time.NewTimer(time.Second)
	<-timer.C
	server3000.Close() // 关闭其中一个服务器

	timer2 := time.NewTimer(time.Second)
	<-timer2.C // 等待两个服务器关闭
}

我省略了工作线程的关闭,因为这取决于它们的实现,但它们也可以在closeAll()函数中处理。

希望这对你有帮助。

英文:

In my opinion os.Exit() and log.Fatal() should only be used for development purposes or extremely unexpected errors.

There is a way to handle server errors gracefully:

package main
import (
&quot;context&quot;
&quot;fmt&quot;
&quot;net/http&quot;
&quot;time&quot;
)
func startServer(server *http.Server) error {
// other logic which
// could return an
// error goes here
return server.ListenAndServe()
}
func main() {
http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
server3000 := &amp;http.Server{Addr: &quot;:3000&quot;, Handler: nil}
server3001 := &amp;http.Server{Addr: &quot;:3001&quot;, Handler: nil}
closeAll := func() {
server3000.Shutdown(context.Background())
server3001.Shutdown(context.Background())
}
go func() {
err := startServer(server3000)
fmt.Printf(&quot;Got error: %s\n&quot;, err)
if err != nil {
closeAll()
}
}()
go func() {
err := startServer(server3001)
fmt.Printf(&quot;Got error: %s\n&quot;, err)
if err != nil {
closeAll()
}
}()
timer := time.NewTimer(time.Second)
&lt;-timer.C
server3000.Close() // close one of the servers
timer2 := time.NewTimer(time.Second)
&lt;-timer2.C // wait for both servers to shutdown
}

I omitted the closing of the workers because it depends on their implementation but they can also be handled in the closeAll() function.

Hope this helps

答案2

得分: 1

尝试在goroutine之间进行通信。这是Golang并发的一个强大工具。
你可以将一个通道发送给goroutine,并在任何goroutine中等待发送错误。
我修复了你的代码,你会在终端中看到"queue",然后在2秒后应用程序因为一个错误而停止。

package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"
)

func startWorker(queue string, ch chan error) error {
	// 其他可能返回错误的逻辑在这里

	// 模拟工作
	for {
		// 从`queue`获取项目并进行处理
		fmt.Println("queue")
		continue
	}
	return nil
}

func startServer(port string, ch chan error) error {
	// 其他可能返回错误的逻辑在这里
	if port == ":3000" {
		time.Sleep(2 * time.Second)
		ch <- errors.New("error")
	}

	return http.ListenAndServe(port, nil)
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
	channel := make(chan error)
	ch := make(chan error)

	go func() {
		channel <- startServer(":3000", ch)
	}()

	go func() {
		channel <- startServer(":3001", ch)
	}()

	go startWorker("first", ch)

	go startWorker("second", ch)

	if err := <-ch; err != nil {
		log.Fatal(err)
	}
}
英文:

try to communicate between goroutines. it's a powerful tool for Golang concurrency.
you can send a channel to goroutines and change wait until sending an error form any goroutine.
I fix your code and you see "queue" in the terminal and after 2 seconds app stopped by an error.

package main
import (
&quot;errors&quot;
&quot;fmt&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;time&quot;
)
func startWorker(queue string, ch chan error) error {
// other logic which
// could return an
// error goes here
// simulate worker work
for {
// get items from `queue`
// and do something with it
fmt.Println(&quot;queue&quot;)
continue
}
return nil
}
func startServer(port string, ch chan error) error {
// other logic which
// could return an
// error goes here
if port == &quot;:3000&quot; {
time.Sleep(2 * time.Second)
ch &lt;- errors.New(&quot;error&quot;)
}
return http.ListenAndServe(port, nil)
}
func main() {
http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
channel := make(chan error)
ch := make(chan error)
go func() {
channel &lt;- startServer(&quot;:3000&quot;, ch)
}()
go func() {
channel &lt;- startServer(&quot;:3001&quot;, ch)
}()
go startWorker(&quot;first&quot;, ch)
go startWorker(&quot;second&quot;, ch)
if err := &lt;-ch; err != nil {
log.Fatal(err)
}
}

huangapple
  • 本文由 发表于 2022年8月11日 21:41:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/73321724.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定