Description
I jus add a router with gin's method GET and it result in the program panic. But when I run the program again with do nothing, it works correctly.
please check the below error.
[panic: runtime error: index out of range [2] with length 1 github.com/gin-gonic/gin@v1.7.1/tree.go:124
How to reproduce
package logging
import (
"github.com/gin-gonic/gin"
)
// logging.go
func addLoggingRoute() {
// ...
for _, lp := range logPaths {
logName, routerPath := lp.FileName, lp.RouterPath
f, err := os.Open(logName)
if err != nil {
mylog.Ctx(context.TODO()).WithField("logName", logName).Error(err.Error())
continue
}
pushPath := filepath.ToSlash(filepath.Join(routerPath, "push"))
lc.logMap[pushPath] = f
lc.logFileNameMap[routerPath] = logName
lc.logFileNamePushMap[logName] = pushPath
engine.GET(routerPath, lc.logIndex) // here panic line number: 89
mylog.Ctx(ctx).WithFields("method", "GET", "path", routerPath).Info("gin register router")
engine.GET(pushPath, lc.logPush)
mylog.Ctx(ctx).WithFields("method", "GET", "path", pushPath).Info("gin register router")
}
}
// ...
}
Actual result
panic: runtime error: index out of range [2] with length 1
goroutine 1 [running]:
github.com/gin-gonic/gin.(*node).incrementChildPrio(0xc0003050a0, 0x2, 0x2)
/home/appops/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/tree.go:124 +0x1b2
github.com/gin-gonic/gin.(*node).addRoute(0xc0003050a0, 0xc0001ae150, 0x22, 0xc000672008, 0x1, 0x1)
/home/appops/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/tree.go:218 +0x445
github.com/gin-gonic/gin.(*Engine).addRoute(0xc0001b2b60, 0xf62f4e, 0x3, 0xc0001ae150, 0x22, 0xc000672008, 0x1, 0x1)
/home/appops/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/gin.go:289 +0x12d
github.com/gin-gonic/gin.(*RouterGroup).handle(0xc0001b2b60, 0xf62f4e, 0x3, 0xc0004d3470, 0x22, 0xc000672000, 0x1, 0x1, 0x0, 0x0)
/home/appops/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/routergroup.go:75 +0x1a7
github.com/gin-gonic/gin.(*RouterGroup).GET(...)
/home/appops/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/routergroup.go:103
g.hz.netease.com/universe/goim/pkg/logging.RegisterGin(0xc0001b2b60, 0xc0001c6e58, 0xc0004d3410, 0x21, 0xc0004d3440, 0x26, 0x0, 0x0, 0xc0007bfc08, 0x2, ...)
/data/heran/universe-im-logic-server/universe-im-logic-server-docker-cm_dev/pkg/logging/logging.go:89 +0xa16
g.hz.netease.com/universe/goim/pkg/logging.logInit(0xc0001b2b60, 0xf62bc3, 0x2, 0x0, 0x0)
/data/heran/universe-im-logic-server/universe-im-logic-server-docker-cm_dev/pkg/logging/api.go:79 +0xdb0
g.hz.netease.com/universe/goim/pkg/logging.LogInitIM(...)
/data/heran/universe-im-logic-server/universe-im-logic-server-docker-cm_dev/pkg/logging/api.go:31
main.main()
/data/heran/universe-im-logic-server/universe-im-logic-server-docker-cm_dev/cmd/logic-server/main.go:84 +0x59f
Environment
- go version: go1.17.5
- gin version (or commit ref): v1.7.1
- operating system: linux/amd64
Comment From: jincheng9
can you reproduce it?
Comment From: vito-go
can you reproduce it?
yes. I find the issue why the program will panic . I add a router with go in a goroutine, where I should do something asynchronousely with keyword go and should add router in this goroutine. When I add one router in the goroutine it just panic sometimes. But when I add many routers in the goroutine, it seem like panic is a must.
package main
import (
"strconv"
"time"
"github.com/gin-gonic/gin"
)
func main() {
engine := gin.New()
gin.SetMode(gin.ReleaseMode)
engine.GET("/universe/api/v1/im/unilog", ginAddPanicTest)
for i := 0; i < 100; i++ {
go engine.GET("/universe/api/v1/im/logging/"+strconv.FormatInt(int64(i),10), ginAddPanicTest)
}
go engine.GET("/universe/api/v1/im/logging/tid-search", ginAddPanicTest)
go engine.GET("/universe/api/v1/im/logging/im.log", ginAddPanicTest)
engine.GET("/universe/api/v1/im/logging/err-im.log", ginAddPanicTest)
time.Sleep(time.Second)
err:=engine.Run(":8181")
if err != nil {
panic(err)
}
}
func ginAddPanicTest(ctx *gin.Context) {
return
}
panic: runtime error: index out of range [0] with length 0
goroutine 94 [running]:
github.com/gin-gonic/gin.(*node).addRoute(0xc0003e8000, {0xc00048e360, 0x1e}, {0xc00048c078, 0x1, 0x1})
/home/ithink/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/tree.go:180 +0xc77
github.com/gin-gonic/gin.(*Engine).addRoute(0xc000001860, {0x968ef3, 0xc000026ce0}, {0xc00048e360, 0x1e}, {0xc00048c078, 0x1, 0x1})
/home/ithink/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/gin.go:289 +0x1e5
github.com/gin-gonic/gin.(*RouterGroup).handle(0xc000001860, {0x968ef3, 0x3}, {0xc000026ce0, 0x40e947}, {0xc00048c070, 0x1, 0x1})
/home/ithink/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/routergroup.go:75 +0x145
github.com/gin-gonic/gin.(*RouterGroup).GET(0x0, {0xc000026ce0, 0x0}, {0xc00048c070, 0x0, 0x0})
/home/ithink/go/pkg/mod/github.com/gin-gonic/gin@v1.7.1/routergroup.go:103 +0x3e
created by main.main
/home/ithink/go/src/local/win3f/main.go:15 +0xbe
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
Process finished with the exit code 2
Expectations
I hope gin support adding routers asynchronousely.
Comment From: jincheng9
- Usually the router should be set when program starts, so the best practice is not let multiple goroutines write a single engine, which is not concurrently safe.
engine.GETis an action to write the engine's members. - If you still want to use multiple goroutines to write a shared engine, you need to protect the shared engine among the goroutines. Use mutex or channel to do this.
Comment From: vito-go
- Usually the router should be set when program starts, so the best practice is not let multiple goroutines write a single engine, which is not concurrently safe.
engine.GETis an action to write the engine's members.- If you still want to use multiple goroutines to write a shared engine, you need to protect the shared engine among the goroutines. Use mutex or channel to do this.
Sometimes I want to make an distributed sevice. When the program is runing, it may receive new register and i need to add a router. In this case, it's a good idea that gin can add router asynchronousely. The standard package net/http can do this.
package main
import (
"fmt"
"net/http"
"net/http/httputil"
"sync"
)
type Server struct {
ServeMux *http.ServeMux
}
type Nginx struct {
mux *sync.Mutex
rps []*httputil.ReverseProxy
}
var nginx = Nginx{} // do something to implement.
func (n *Nginx) GetProxy(appName string) *httputil.ReverseProxy {
// to get a ReverseProxy
return nil
}
func ReverseHttp(w http.ResponseWriter, r *http.Request) {
// do something
// for example, here can do as a reverse http server.
var appName string
// e.g. r.URL.Path=="/user/hello"
for i := 1; i < len(r.URL.Path); i++ {
if r.URL.Path[i] == '/' {
appName = r.URL.Path[1:i]
break
}
}
nginx.GetProxy(appName).ServeHTTP(w, r)
}
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
appName := r.URL.Query().Get("appName")
// Important!! ServeMux is support to add router asynchronously!
s.ServeMux.HandleFunc(fmt.Sprintf("/%s/", appName), ReverseHttp)
}
func main() {
mux := http.NewServeMux()
s := Server{ServeMux: mux}
mux.Handle("/add-service", &s)
http.ListenAndServe(":9191", mux)
}