Closed limpo1989 closed 1 year ago
使用monkey配合plugin,实现一个简单的热修复功能会发生崩溃,刚开始patching之后一切正常,运行一会以后就会发生错误
另外: agiledragon/gomonkey/#132 存在同样的问题
测试环境:
main.go
package main import ( "fmt" "io" "net/http" "os" "plugin" "reflect" "sync/atomic" "time" "github.com/go-kiss/monkey" "gocase/gopatch/route" ) var HotfixVersion = "main" func IndexHandler() func(http.ResponseWriter, *http.Request) { //return route.Index var counter int32 return func(writer http.ResponseWriter, request *http.Request) { fmt.Fprintln(writer, "plugin handle:", atomic.AddInt32(&counter, 1)) } } func main() { http.HandleFunc("/", route.Index) // 热修复 route.Index 逻辑 { p, err := plugin.Open("http_v1.so") if nil != err { panic(err) } sym, err := p.Lookup("IndexHandler") if nil != err { panic(err) } fn := sym.(func() func(http.ResponseWriter, *http.Request)) handler := fn() fmt.Println("patching...", reflect.ValueOf(route.Index).Pointer(), " => ", reflect.ValueOf(handler).Pointer()) monkey.Patch(route.Index, handler, monkey.OptGlobal) } // 等3s后开始启动客户端访问接口 time.AfterFunc(time.Second*3, func() { fmt.Println("start client...") for i := 0; i < 3; i++ { go startClient() } }) // 启动http服务器 fmt.Println("http server listen...") err := http.ListenAndServe(":8080", nil) fmt.Println("http server listen failed: ", err) } func startClient() { for { resp, err := http.Get("http://127.0.0.1:8080/") if nil != err { fmt.Println("http.Get: ", err.Error()) return } io.Copy(os.Stdout, resp.Body) resp.Body.Close() } }
route.go
var counter int32 func Index(writer http.ResponseWriter, request *http.Request) { fmt.Fprintln(writer, "main: hello gopatch/", atomic.AddInt32(&counter, 1)) }
run_http.sh
#!/bin/bash set -e echo "build main program..." go build -gcflags=all=-l -ldflags="-X main.HotfixVersion=main" -o http_main main.go echo "please modify v1 plugin, press enter key to continue..." read input echo "build plugin v1..." go build -gcflags=all=-l -buildmode=plugin -ldflags="-X main.HotfixVersion=v1" -o http_v1.so main.go echo "run main program..." ./http_main
输出结果
patching... 8645408 => 140521878991296 http server listen... start client... plugin handle: 1 plugin handle: 3 plugin handle: 2 plugin handle: 4 plugin handle: 6 plugin handle: 7 plugin handle: 5 plugin handle: 8 plugin handle: 9 plugin handle: 11 plugin handle: 10 plugin handle: 12 plugin handle: 13 plugin handle: 14 plugin handle: 15 plugin handle: 16 plugin handle: 17 plugin handle: 18 plugin handle: 19 plugin handle: 20 plugin handle: 22 plugin handle: 21 plugin handle: 23 plugin handle: 24 plugin handle: 25 plugin handle: 26 plugin handle: 27 plugin handle: 28 plugin handle: 30 plugin handle: 29 plugin handle: 31 plugin handle: 32 plugin handle: 33 plugin handle: 34 plugin handle: 35 plugin handle: 36 plugin handle: 37 plugin handle: 38 plugin handle: 41 plugin handle: 39 plugin handle: 40 plugin handle: 12346 plugin handle: 12347 plugin handle: 12348 plugin handle: 12349 plugin handle: 12350 plugin handle: 12351 unexpected fault address 0x931cc0 fatal error: fault [signal SIGSEGV: segmentation violation code=0x2 addr=0x931cc0 pc=0x931cc0] goroutine 182 [running]: runtime.throw({0x8e39fd?, 0xc00022a000?}) /usr/local/go/src/runtime/panic.go:1047 +0x5d fp=0xc0004f4a70 sp=0xc0004f4a40 pc=0x5906dd runtime.sigpanic() /usr/local/go/src/runtime/signal_unix.go:851 +0x1e5 fp=0xc0004f4aa0 sp=0xc0004f4a70 pc=0x5a7465 net/http.HandlerFunc.ServeHTTP(0x688a65?, {0x934080?, 0xc00058a0e0?}, 0x92ceb8?) /usr/local/go/src/net/http/server.go:2123 +0x2f fp=0xc0004f4ac8 sp=0xc0004f4aa0 pc=0x80644f net/http.(*ServeMux).ServeHTTP(0x0?, {0x934080, 0xc00058a0e0}, 0xc0001d2300) /usr/local/go/src/net/http/server.go:2500 +0xc2 fp=0xc0004f4b00 sp=0xc0004f4ac8 pc=0x8079a2 net/http.serverHandler.ServeHTTP({0xc000482a80?}, {0x934080?, 0xc00058a0e0?}, 0xc0001d2300?) /usr/local/go/src/net/http/server.go:2936 +0x23c fp=0xc0004f4b98 sp=0xc0004f4b00 pc=0x808fdc net/http.(*conn).serve(0xc0004847e0, {0x934488, 0xc00011af30}) /usr/local/go/src/net/http/server.go:1995 +0xad0 fp=0xc0004f4fb8 sp=0xc0004f4b98 pc=0x8055b0 net/http.(*Server).Serve.func3() /usr/local/go/src/net/http/server.go:3089 +0x2e fp=0xc0004f4fe0 sp=0xc0004f4fb8 pc=0x80992e runtime.goexit() /usr/local/go/src/runtime/asm_amd64.s:1598 +0x1 fp=0xc0004f4fe8 sp=0xc0004f4fe0 pc=0x5c4861 created by net/http.(*Server).Serve /usr/local/go/src/net/http/server.go:3089 +0x45f ...
monkey 本质上是还修改全局函数指针。多协程同时修改会出问题,所以不能用于实现热更新之类的功能。
@taoso 可是我上面的例子是没有 “多协程同时修改” 仅在初始化阶段进行了修改,之后就没有进行修改了
切换为 gohook 后运行正常了,不会崩溃了
使用monkey配合plugin,实现一个简单的热修复功能会发生崩溃,刚开始patching之后一切正常,运行一会以后就会发生错误
测试环境:
main.go
route.go
run_http.sh
输出结果