[GH-ISSUE #318] [BUG] 用协程同时启动 asynq.Server 和 http.Server 的程序无法用 kill -TERM 结束 #1150

Closed
opened 2026-03-07 22:06:29 +03:00 by kerem · 3 comments
Owner

Originally created by @xuyang2 on GitHub (Aug 27, 2021).
Original GitHub issue: https://github.com/hibiken/asynq/issues/318

Originally assigned to: @hibiken on GitHub.

Describe the bug
用协程同时启动 asynq.Server 和 http.Server 的程序无法用 kill -TERM 结束

To Reproduce
Steps to reproduce the behavior (Code snippets if applicable):

// main.go
package main

import (
	"flag"
	"io"
	"net/http"
	"syscall"
	"time"

	"github.com/hibiken/asynq"
	"golang.org/x/sync/errgroup"
)

var flagRun bool

func init() {
	flag.BoolVar(&flagRun, "run", false, "run asynq server")
	flag.Parse()
}

func main() {
	go func() {
		time.Sleep(3 * time.Second)
		syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
		syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
	}()

	srv := asynq.NewServer(asynq.RedisClientOpt{Addr: ":6379"}, asynq.Config{LogLevel: asynq.InfoLevel})
	mux := asynq.NewServeMux()

	var eg errgroup.Group

	if flagRun {
		eg.Go(func() error {
			return srv.Run(mux)
		})
	}

	eg.Go(func() error {
		helloHandler := func(w http.ResponseWriter, req *http.Request) {
			io.WriteString(w, "Hello, world!\n")
		}
		http.HandleFunc("/hello", helloHandler)
		return http.ListenAndServe(":0", nil) // random port 
	})

	eg.Wait()
}

然后分别执行
go run main.go
go run main.go -run

Expected behavior
程序都应该能在若干秒后退出

Actual behavior

$ go run main.go 
signal: terminated

$ go run main.go -run
asynq: pid=23539 2021/08/27 10:55:19.940001 INFO: Starting processing
asynq: pid=23539 2021/08/27 10:55:19.940238 INFO: Send signal TSTP to stop processing new tasks
asynq: pid=23539 2021/08/27 10:55:19.940301 INFO: Send signal TERM or INT to terminate the process
asynq: pid=23539 2021/08/27 10:55:19.941162 WARN: recoverer: could not list deadline exceeded tasks
asynq: pid=23539 2021/08/27 10:55:19.943194 ERROR: Dequeue error: UNKNOWN: redis eval error: dial tcp :6379: connect: connection refused
asynq: pid=23539 2021/08/27 10:55:19.943465 ERROR: cannot subscribe to cancelation channel: UNKNOWN: redis pubsub receive error: dial tcp :6379: connect: connection refused
asynq: pid=23539 2021/08/27 10:55:19.943591 ERROR: could not write server state data: UNKNOWN: redis command error: SADD failed: dial tcp :6379: connect: connection refused
asynq: pid=23539 2021/08/27 10:55:22.940980 INFO: Starting graceful shutdown
asynq: pid=23539 2021/08/27 10:55:22.941085 INFO: Waiting for all workers to finish...
asynq: pid=23539 2021/08/27 10:55:22.941133 INFO: All workers have finished
asynq: pid=23539 2021/08/27 10:55:22.941193 INFO: Exiting 
<-  Then stuck and can't exit. Can only be killed with kill -9 .

Environment (please complete the following information):

Originally created by @xuyang2 on GitHub (Aug 27, 2021). Original GitHub issue: https://github.com/hibiken/asynq/issues/318 Originally assigned to: @hibiken on GitHub. **Describe the bug** 用协程同时启动 asynq.Server 和 http.Server 的程序无法用 kill -TERM 结束 **To Reproduce** Steps to reproduce the behavior (Code snippets if applicable): ``` // main.go package main import ( "flag" "io" "net/http" "syscall" "time" "github.com/hibiken/asynq" "golang.org/x/sync/errgroup" ) var flagRun bool func init() { flag.BoolVar(&flagRun, "run", false, "run asynq server") flag.Parse() } func main() { go func() { time.Sleep(3 * time.Second) syscall.Kill(syscall.Getpid(), syscall.SIGTERM) syscall.Kill(syscall.Getpid(), syscall.SIGTERM) }() srv := asynq.NewServer(asynq.RedisClientOpt{Addr: ":6379"}, asynq.Config{LogLevel: asynq.InfoLevel}) mux := asynq.NewServeMux() var eg errgroup.Group if flagRun { eg.Go(func() error { return srv.Run(mux) }) } eg.Go(func() error { helloHandler := func(w http.ResponseWriter, req *http.Request) { io.WriteString(w, "Hello, world!\n") } http.HandleFunc("/hello", helloHandler) return http.ListenAndServe(":0", nil) // random port }) eg.Wait() } ``` 然后分别执行 go run main.go go run main.go -run **Expected behavior** 程序都应该能在若干秒后退出 **Actual behavior** ``` $ go run main.go signal: terminated $ go run main.go -run asynq: pid=23539 2021/08/27 10:55:19.940001 INFO: Starting processing asynq: pid=23539 2021/08/27 10:55:19.940238 INFO: Send signal TSTP to stop processing new tasks asynq: pid=23539 2021/08/27 10:55:19.940301 INFO: Send signal TERM or INT to terminate the process asynq: pid=23539 2021/08/27 10:55:19.941162 WARN: recoverer: could not list deadline exceeded tasks asynq: pid=23539 2021/08/27 10:55:19.943194 ERROR: Dequeue error: UNKNOWN: redis eval error: dial tcp :6379: connect: connection refused asynq: pid=23539 2021/08/27 10:55:19.943465 ERROR: cannot subscribe to cancelation channel: UNKNOWN: redis pubsub receive error: dial tcp :6379: connect: connection refused asynq: pid=23539 2021/08/27 10:55:19.943591 ERROR: could not write server state data: UNKNOWN: redis command error: SADD failed: dial tcp :6379: connect: connection refused asynq: pid=23539 2021/08/27 10:55:22.940980 INFO: Starting graceful shutdown asynq: pid=23539 2021/08/27 10:55:22.941085 INFO: Waiting for all workers to finish... asynq: pid=23539 2021/08/27 10:55:22.941133 INFO: All workers have finished asynq: pid=23539 2021/08/27 10:55:22.941193 INFO: Exiting <- Then stuck and can't exit. Can only be killed with kill -9 . ``` **Environment (please complete the following information):** - OS: Linux - Version of `asynq` package: 421dc584ffa4fab4ea8ced5546b9234d2dfedd82
kerem 2026-03-07 22:06:29 +03:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

@xuyang2 commented on GitHub (Aug 27, 2021):

Server.waitForSignals 现在是

signal.Notify(sigs, unix.SIGTERM, unix.SIGINT, unix.SIGTSTP)

测试发现下面这样的程序也无法 kill -TERM

package main

import (
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"

	"golang.org/x/sync/errgroup"
)

func main() {
	var eg errgroup.Group

	eg.Go(func() error {
		c := make(chan os.Signal)
		signal.Notify(c,
			syscall.SIGTERM,
			syscall.SIGINT,
		)

		// defer signal.Stop(c) // 去掉这行的注释,就可以 kill -TERM

		s := <-c
		log.Printf("got signal: %v", s)
		return nil
	})

	eg.Go(func() error {
		return http.ListenAndServe(":0", nil)
	})

	eg.Wait()
}
<!-- gh-comment-id:907217563 --> @xuyang2 commented on GitHub (Aug 27, 2021): Server.waitForSignals 现在是 signal.Notify(sigs, unix.SIGTERM, unix.SIGINT, unix.SIGTSTP) 测试发现下面这样的程序也无法 kill -TERM ``` package main import ( "log" "net/http" "os" "os/signal" "syscall" "golang.org/x/sync/errgroup" ) func main() { var eg errgroup.Group eg.Go(func() error { c := make(chan os.Signal) signal.Notify(c, syscall.SIGTERM, syscall.SIGINT, ) // defer signal.Stop(c) // 去掉这行的注释,就可以 kill -TERM s := <-c log.Printf("got signal: %v", s) return nil }) eg.Go(func() error { return http.ListenAndServe(":0", nil) }) eg.Wait() } ```
Author
Owner

@xuyang2 commented on GitHub (Aug 27, 2021):

把 server.waitForSignals() 改成下面这样好像就可以 kill -TERM 了

	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, unix.SIGTERM, unix.SIGINT, unix.SIGTSTP)
	defer signal.Stop(sigs)
<!-- gh-comment-id:907227897 --> @xuyang2 commented on GitHub (Aug 27, 2021): 把 server.waitForSignals() 改成下面这样好像就可以 kill -TERM 了 ``` sigs := make(chan os.Signal, 1) signal.Notify(sigs, unix.SIGTERM, unix.SIGINT, unix.SIGTSTP) defer signal.Stop(sigs) ```
Author
Owner

@hibiken commented on GitHub (Nov 9, 2021):

Closing this as I'm unable to read the description (sorry!) :(

<!-- gh-comment-id:964646500 --> @hibiken commented on GitHub (Nov 9, 2021): Closing this as I'm unable to read the description (sorry!) :(
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/asynq#1150
No description provided.