英文:
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 (
"log"
"net/http"
)
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("/", 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)
}
}
答案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 (
"context"
"fmt"
"net/http"
"time"
)
func startServer(server *http.Server) error {
// other logic which
// could return an
// error goes here
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("Got error: %s\n", err)
if err != nil {
closeAll()
}
}()
go func() {
err := startServer(server3001)
fmt.Printf("Got error: %s\n", err)
if err != nil {
closeAll()
}
}()
timer := time.NewTimer(time.Second)
<-timer.C
server3000.Close() // close one of the servers
timer2 := time.NewTimer(time.Second)
<-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 (
"errors"
"fmt"
"log"
"net/http"
"time"
)
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("queue")
continue
}
return nil
}
func startServer(port string, ch chan error) error {
// other logic which
// could return an
// error goes here
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)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论