![](/img/trans.png)
[英]Gin reverse proxy tests failing for interface conversion: *httptest.ResponseRecorder is not http.CloseNotifier: missing method CloseNotify
[英]Golang gin-gonic reverse proxying leads to panic “interface conversion: *http.timeoutWriter is not http.CloseNotifier: missing method CloseNotify”
我正在使用Gin Gonic框架创建反向代理端点,目标端点是通过grpc Gateway使用下面给出的代码提供的。 这类似于此处和此处为gin建议的反向代理方法
ep1 := v1.Group("/ep1")
{
ep1.GET("/ep2", reverseProxy("http://localhost:50000"))
}
func reverseProxy(target string) gin.HandlerFunc {
url, err := url.Parse(target)
if err != nil {
log.Println("Reverse Proxy target url could not be parsed:", err)
return nil
}
proxy := httputil.NewSingleHostReverseProxy(url)
return func(c *gin.Context) {
proxy.ServeHTTP(c.Writer, c.Request)
}
}
但是,当实际上向该gin端点(/ ep1 / ep2)发送请求时,会出现go紧急情况:
interface conversion: *http.timeoutWriter is not http.CloseNotifier: missing method CloseNotify
/usr/local/Cellar/go/1.8/libexec/src/runtime/panic.go:489 (0x10288df)
gopanic: reflectcall(nil, unsafe.Pointer(d.fn), deferArgs(d), uint32(d.siz), uint32(d.siz))
/usr/local/Cellar/go/1.8/libexec/src/runtime/iface.go:131 (0x100c3af)
additab: panic(&TypeAssertionError{"", typ.string(), inter.typ.string(), iname})
/usr/local/Cellar/go/1.8/libexec/src/runtime/iface.go:79 (0x100bc34)
getitab: additab(m, true, canfail)
/usr/local/Cellar/go/1.8/libexec/src/runtime/iface.go:256 (0x100cbb8)
assertI2I: r.tab = getitab(inter, tab._type, false)
/path/to/vendor/github.com/gin-gonic/gin/response_writer.go:110 (0x14de6f3)
(*responseWriter).CloseNotify: return w.ResponseWriter.(http.CloseNotifier).CloseNotify()
/usr/local/Cellar/go/1.8/libexec/src/net/http/httputil/reverseproxy.go:142 (0x14d4d12)
(*ReverseProxy).ServeHTTP: notifyChan := cn.CloseNotify()
/path/to/main.go:379 (0x16d2ead)
reverseProxy.func1: proxy.ServeHTTP(c.Writer, c.Request)
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/middlewares/locale.go:12 (0x15737d9)
getLocaleMiddleware.func1: c.Next()
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/middlewares/session_cookie.go:27 (0x1574e7c)
getSessionCookieMiddleware.func1: c.Next()
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/middlewares/affiliate_api.go:27 (0x15729a1)
getAffiliateAPIMiddleware.func1: c.Next()
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/middlewares/metrics.go:17 (0x157465b)
getMetricsMiddleware.func1: c.Next()
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/middlewares/input_validations.go:75 (0x1572dcb)
getInputValidationMiddleware.func1: c.Next()
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/middlewares/logger.go:68 (0x1573aea)
LoggerWithWriter.func1: c.Next()
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/middlewares/request_tracer.go:13 (0x1574d6c)
getTracerContext.func1: c.Next()
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/vendor/github.com/gin-gonic/gin/recovery.go:45 (0x14e4b6a)
RecoveryWithWriter.func1: c.Next()
/path/to/vendor/github.com/gin-gonic/gin/context.go:97 (0x14d657a)
(*Context).Next: c.handlers[c.index](c)
/path/to/vendor/github.com/gin-gonic/gin/gin.go:284 (0x14dc710)
(*Engine).handleHTTPRequest: context.Next()
/path/to/vendor/github.com/gin-gonic/gin/gin.go:265 (0x14dc02b)
(*Engine).ServeHTTP: engine.handleHTTPRequest(c)
/usr/local/Cellar/go/1.8/libexec/src/net/http/server.go:2967 (0x140fa53)
(*timeoutHandler).ServeHTTP.func1: h.handler.ServeHTTP(tw, r)
/usr/local/Cellar/go/1.8/libexec/src/runtime/asm_amd64.s:2197 (0x1054851)
关于为什么会发生这种情况或代码中有什么错误的任何想法?
发现发现此问题是因为代码库未直接使用gin-gonic
的Run()
方法。 相反,它在启动http服务器时使用了超时,如下所示(此处使用部分相关代码):
type H struct {
sync.Mutex
Engine *gin.Engine
listener net.Listener
running bool
}
.
.
.
var h H
s := &http.Server{
Addr: address,
Handler: http.TimeoutHandler(h.Engine, time.Duration(100000)*time.Millisecond, ""),
ReadTimeout: time.Duration(100000) * time.Millisecond,
WriteTimeout: time.Duration(100000) * time.Millisecond,
}
h.listener, err := net.Listen("tcp", s.Addr)
if err != nil {
return err
}
h.running = true
s.Serve(h.listener)
然而, http.TimeoutHandler
没有实现http.CloseNotifer
截至提到接口http://grokbase.com/t/gg/golang-dev/13796p5h1n/net-http-timeouthandler-vs-closenotify这导致了panic
与错误消息interface conversion: *http.timeoutWriter is not http.CloseNotifier: missing method CloseNotify
因此,对于这个问题的解决方法,在服务器处理程序进行了修改,可直接在杜松子酒引擎,而使用ReadTimeout
和WriteTimeout
值http.Server
超时目的。
修改后的代码不再引起混乱,并成功实现了反向代理:
type H struct {
sync.Mutex
Engine *gin.Engine
listener net.Listener
running bool
}
.
.
.
var h H
s := &http.Server{
Addr: address,
Handler: h.Engine,
ReadTimeout: time.Duration(100000) * time.Millisecond,
WriteTimeout: time.Duration(100000) * time.Millisecond,
}
h.listener, err := net.Listen("tcp", s.Addr)
if err != nil {
return err
}
h.running = true
s.Serve(h.listener)
请注意,这里仅需要修改&http.Server
的Handler
。 同样,不需要修改该问题的原始反向代理代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.