Golang http: panic serving: close of closed channel

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

Golang http: panic serving: close of closed channel

问题

尝试清理我的代码,GetDecisListeners() 函数中的代码以前是在 LiveDecision() 函数内部而没有恐慌错误的。

// GetDecisListener 返回订阅频道的监听器
func GetDecisListener(dStructs map[string]map[string]interface{}) (chan interface{}, error) {
    listener := make(chan interface{})
    for r, _ := range dStructs {
        DecisChannels[r].Register(listener)
        key := r
        defer func() {
            Decision(key).Unregister(listener)
            close(listener)
        }()
        fmt.Printf("Done %v\n", r)
    }
    fmt.Print("Done inside\n")
    return listener, nil
}

func LiveDecision(ctx *gin.Context) {
    ...
    listener, err := GetDecisListener(dStructs)
    fmt.Print("Done outside\n")    // 无法到达此处
    if err != nil {return}

    ctx.Stream(func(w io.Writer) bool {
        select {
        case msg := <-listener:
            return true
            ...
    }}
}

dStructs 只是一个二维接口映射;DecisionDecisChannels 是从这里引用的。

var DecisChannels = make(map[string]broadcast.Broadcaster)
var DecisCols = make(map[string]string)

func OpenListener(decisid string) chan interface{} {
    ite := make(chan interface{})
    Decision(decisid).Register(ite)
    return ite
}

func CloseListener(decisid string, ite chan interface{}) {
    Decision(decisid).Unregister(ite)
    close(ite)
}

func Decision(decisid string) broadcast.Broadcaster {
    b, ok := DecisChannels[decisid]
    if !ok {
        b = broadcast.NewBroadcaster(10)
        DecisChannels[decisid] = b
    }
    return b
}

日志显示在 GetDecisListener() 之后没有到达 Done outside,而是显示了 http:panic 错误。

Done item1
Done item2
Done item3
Done item4
Done inside
2021/09/14 14:50:45 http: panic serving 127.0.0.1:39450: close of closed channel
goroutine 40 [running]:
net/http.(*conn).serve.func1(0xc0000c4280)
        /usr/lib/go-1.13/src/net/http/server.go:1767 +0x139
panic(0xd271c0, 0xf5c2b0)
        /usr/lib/go-1.13/src/runtime/panic.go:679 +0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8290, 0xe, 0xc0003660c0)
        elsewhere/internal/app/route/client/client.go:42 +0x68
panic(0xd271c0, 0xf5c2b0)
        /usr/lib/go-1.13/src/runtime/panic.go:679 +0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8300, 0xd, 0xc0003660c0)
        elsewhere/internal/app/route/client/client.go:42 +0x68
panic(0xd271c0, 0xf5c2b0)
        /usr/lib/go-1.13/src/runtime/panic.go:679 +0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8390, 0xd, 0xc0003660c0)
        elsewhere/internal/app/route/client/client.go:42 +0x68
somewhere/internal/app/route/client.GetDecisListener(0xc0001228a0, 0xc0003660c0, 0x0, 0x0)
        elsewhere/internal/app/route/client/client.go:47 +0x27f
somewhere/internal/app/route/client.LiveDecision(0xc0000f8200)
        elsewhere/internal/app/route/client/client.go:77 +0xb0
github.com/gin-gonic/gin.(*Context).Next(0xc0000f8200)
        /home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go:165 +0x3b
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000470000, 0xc0000f8200)
        /home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go:489 +0x614
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000470000, 0xf7cdc0, 0xc0001020e0, 0xc0000f8100)
        /home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go:445 +0x15d
net/http.serverHandler.ServeHTTP(0xc00046e0e0, 0xf7cdc0, 0xc0001020e0, 0xc0000f8100)
        /usr/lib/go-1.13/src/net/http/server.go:2802 +0xa4
net/http.(*conn).serve(0xc0000c4280, 0xf7ff00, 0xc00004a040)
        /usr/lib/go-1.13/src/net/http/server.go:1890 +0x875
created by net/http.(*Server).Serve
        /usr/lib/go-1.13/src/net/http/server.go:2928 +0x384

我怀疑 Decision(key)defer func() 导致了 panic 错误。如果是这种情况,我不知道应该把它放在哪里。

英文:

Trying to clean up my code, the code in GetDecisListeners() was previously inside LiveDecision() without panic error.

//GetDecisListener return listener subscribed to channels
func GetDecisListener(dStructs map[string]map[string]interface{}) (chan interface{}, error) {
	listener := make(chan interface{})
	for r, _ := range dStructs {
		DecisChannels[r].Register(listener)
		key := r
		defer func() {
			Decision(key).Unregister(listener)
			close(listener)
		}()
		fmt.Printf(&quot;Done %v\n&quot;, r)
	}
	fmt.Print(&quot;Done inside\n&quot;)
	return listener, nil
}
func LiveDecision(ctx *gin.Context) {
    ...
	listener, err := GetDecisListener(dStructs)
	fmt.Print(&quot;Done outside\n&quot;)    // cannot reach here
	if err != nil {return}

	ctx.Stream(func(w io.Writer) bool {
		select {
		case msg := &lt;-listener:
            return true
            ...
    }}
}

dStructs is just an 2D interface map; Decision and DecisChannels are referred from here.

var DecisChannels = make(map[string]broadcast.Broadcaster)
var DecisCols = make(map[string]string)

func OpenListener(decisid string) chan interface{} {
	ite := make(chan interface{})
	Decision(decisid).Register(ite)
	return ite
}

func CloseListener(decisid string, ite chan interface{}) {
	Decision(decisid).Unregister(ite)
	close(ite)
}

func Decision(decisid string) broadcast.Broadcaster {
	b, ok := DecisChannels[decisid]
	if !ok {
		b = broadcast.NewBroadcaster(10)
		DecisChannels[decisid] = b
	}
	return b
}

The log shows it hadn't reach Done outside but shows http:panic after the GetDecisListener().

Done item1
Done item2
Done item3
Done item4
Done inside
2021/09/14 14:50:45 http: panic serving 127.0.0.1:39450: close of closed channel
goroutine 40 [running]:
net/http.(*conn).serve.func1(0xc0000c4280)
        /usr/lib/go-1.13/src/net/http/server.go:1767 +0x139
panic(0xd271c0, 0xf5c2b0)
        /usr/lib/go-1.13/src/runtime/panic.go:679 +0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8290, 0xe, 0xc0003660c0)
        elsewhere/internal/app/route/client/client.go:42 +0x68
panic(0xd271c0, 0xf5c2b0)
        /usr/lib/go-1.13/src/runtime/panic.go:679 +0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8300, 0xd, 0xc0003660c0)
        elsewhere/internal/app/route/client/client.go:42 +0x68
panic(0xd271c0, 0xf5c2b0)
        /usr/lib/go-1.13/src/runtime/panic.go:679 +0x1b2
somewhere/internal/app/route/client.GetDecisListener.func1(0xc0001d8390, 0xd, 0xc0003660c0)
        elsewhere/internal/app/route/client/client.go:42 +0x68
somewhere/internal/app/route/client.GetDecisListener(0xc0001228a0, 0xc0003660c0, 0x0, 0x0)
        elsewhere/internal/app/route/client/client.go:47 +0x27f
somewhere/internal/app/route/client.LiveDecision(0xc0000f8200)
        elsewhere/internal/app/route/client/client.go:77 +0xb0
github.com/gin-gonic/gin.(*Context).Next(0xc0000f8200)
        /home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go:165 +0x3b
github.com/gin-gonic/gin.(*Engine).handleHTTPRequest(0xc000470000, 0xc0000f8200)
        /home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go:489 +0x614
github.com/gin-gonic/gin.(*Engine).ServeHTTP(0xc000470000, 0xf7cdc0, 0xc0001020e0, 0xc0000f8100)
        /home/simon/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go:445 +0x15d
net/http.serverHandler.ServeHTTP(0xc00046e0e0, 0xf7cdc0, 0xc0001020e0, 0xc0000f8100)
        /usr/lib/go-1.13/src/net/http/server.go:2802 +0xa4
net/http.(*conn).serve(0xc0000c4280, 0xf7ff00, 0xc00004a040)
        /usr/lib/go-1.13/src/net/http/server.go:1890 +0x875
created by net/http.(*Server).Serve
        /usr/lib/go-1.13/src/net/http/server.go:2928 +0x384

I suspect defer func() of the Decision(key) causing the panic. I would have no idea where to put it if this is the case.

答案1

得分: 1

你不能两次关闭一个通道,否则会引发 panic。

listener := make(chan interface{})
for r, _ := range dStructs {
    DecisChannels[r].Register(listener)
    key := r
    defer func() {
        Decision(key).Unregister(listener)
        close(listener)
    }()
    fmt.Printf("Done %v\n", r)
}

在这段代码中,如果 len(dStructs) > 1,两个延迟调用将尝试关闭 listener

也许你可以这样做:

listener := make(chan interface{})
shouldClose := false
for r, _ := range dStructs {
    DecisChannels[r].Register(listener)
    key := r
    defer func() {
        Decision(key).Unregister(listener)
    }()
    shouldClose = true
    fmt.Printf("Done %v\n", r)
}
if shouldClose {
    close(listener)    
}
英文:

You cannot close a channel twice: it will panic.

listener := make(chan interface{})
for r, _ := range dStructs {
    DecisChannels[r].Register(listener)
    key := r
    defer func() {
        Decision(key).Unregister(listener)
        close(listener)
    }()
    fmt.Printf(&quot;Done %v\n&quot;, r)
}

Here if len(dStructs) &gt; 1, two defered call will try to close listener

Maybe you can do something like:

listener := make(chan interface{})
shouldClose := false
for r, _ := range dStructs {
    DecisChannels[r].Register(listener)
    key := r
    defer func() {
        Decision(key).Unregister(listener)
    }()
    shouldClose = true
    fmt.Printf(&quot;Done %v\n&quot;, r)
}
if shouldClose {
    close(listener)    
}

huangapple
  • 本文由 发表于 2021年9月14日 15:35:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/69173707.html
匿名

发表评论

匿名网友

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

确定