From 04af0ebfdcf2f19f9e4f3010b681bdfc9fb03321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8B=A5=E5=B0=98=EF=BC=88=E6=A8=B1=E3=81=AE=E6=B3=AA?= =?UTF-8?q?=EF=BC=89?= Date: Sat, 23 Feb 2019 23:12:46 +0800 Subject: [PATCH] fix: goroutine exit by panic --- cmd/test/test.go | 95 ++++++++++++++++++++----- cmd/testClient/client.go | 32 +++------ cmd/testServer/sever.go | 132 ++++++++++++++++++++++++++++++----- mem.profile | Bin 0 -> 2664 bytes proxy/server/proxy.go | 13 ++-- proxy/server/shadowsocks.go | 38 +++++----- record/record.go | 7 +- record/record_test.go | 19 ++--- testing/servers/http/http.go | 84 ++++++++++++++++++++++ utils/goroutine/goroutine.go | 12 ++++ 10 files changed, 341 insertions(+), 91 deletions(-) create mode 100644 mem.profile create mode 100644 testing/servers/http/http.go create mode 100644 utils/goroutine/goroutine.go diff --git a/cmd/test/test.go b/cmd/test/test.go index 0a35f9a..b7af3ab 100644 --- a/cmd/test/test.go +++ b/cmd/test/test.go @@ -1,26 +1,85 @@ package main import ( - "fmt" + "bufio" + "bytes" + "io" + "net" + "net/http" "time" + + "github.com/rc452860/vnet/proxy/client" + + "github.com/rc452860/vnet/common/config" + "github.com/rc452860/vnet/common/log" + "github.com/rc452860/vnet/common/pool" + "github.com/rc452860/vnet/network/conn" + "github.com/rc452860/vnet/proxy/server" + thttp "github.com/rc452860/vnet/testing/servers/http" + "github.com/rc452860/vnet/utils/datasize" ) func main() { - go func() { - defer func() { - if e := recover(); e != nil { - fmt.Printf("error %v", e) - } - }() - go func() { - defer func() { - if e := recover(); e != nil { - fmt.Printf("error %v \n", e) - } - }() - panic("this is error") - }() - }() - time.Sleep(1 * time.Second) - fmt.Println("aaa") + config.LoadConfig("config.json") + log.GetLogger("root").Level = log.INFO + proxy, err := server.NewShadowsocks("0.0.0.0", "aes-128-gcm", "killer", 1090, server.ShadowsocksArgs{ + Limit: 4 * 1024 * 1024, + ConnectTimeout: 0, + }) + proxy.Start() + if err != nil { + log.Err(err) + return + } + time.Sleep(time.Second) + thttp.StartFakeFileServer() + s, c := net.Pipe() + client := client.NewShadowsocksClient("127.0.0.1", "aes-128-gcm", "killer", 1090) + cs, err := conn.NewDefaultConn(c, "pipe") + go client.TcpProxy(cs, "localhost", 8080) + var httpRequest bytes.Buffer + httpRequest.WriteString("GET /download?size=4MB HTTP/1.1\n") + httpRequest.WriteString("Host: localhost:8080\n") + httpRequest.WriteString("Connection: keep-alive\n") + // httpRequest.WriteString("Connection: close\n") + httpRequest.WriteString("Pragma: no-cache\n") + httpRequest.WriteString("Cache-Control: no-cache\n") + httpRequest.WriteString("Upgrade-Insecure-Requests: 1\n") + httpRequest.WriteString("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36\n") + httpRequest.WriteString("DNT: 1\n\n") + data := httpRequest.Bytes() + s.Write(data) + var count int64 = 0 + buf := pool.GetBuf() + defer pool.PutBuf(buf) + size, _ := datasize.Parse("4MB") + // var buff bytes.Buffer + log.Info("%v", size) + request, err := http.ReadRequest(bufio.NewReader(bufio.NewReader(&httpRequest))) + response, err := http.ReadResponse(bufio.NewReader(s), request) + log.Info("content lenght:%v", response.ContentLength) + if err != nil { + log.Err(err) + return + } + for { + n, err := response.Body.Read(buf) + // buff.Write(buf) + count = count + int64(n) + if err != nil && err != io.EOF { + log.Err(err) + break + } + if count == response.ContentLength { + break + } + } + log.Info("count %v", count) + log.Info("upload %v", proxy.UpBytes) + // log.Info("%s", buff.Bytes()[:255]) + upspeed, _ := datasize.HumanSize(uint64(proxy.UpSpeed)) + downspeed, _ := datasize.HumanSize(uint64(proxy.DownSpeed)) + up, _ := datasize.HumanSize(proxy.UpBytes) + down, _ := datasize.HumanSize(proxy.DownBytes) + log.Info("upspeed:%s - downspeed:%s | up:%s - down:%s", upspeed, downspeed, up, down) } diff --git a/cmd/testClient/client.go b/cmd/testClient/client.go index 6d2b268..f845dd7 100644 --- a/cmd/testClient/client.go +++ b/cmd/testClient/client.go @@ -1,30 +1,16 @@ package main import ( - "crypto/rand" - "io" - "net" + _ "net/http/pprof" + "os" + "os/signal" + + thttp "github.com/rc452860/vnet/testing/servers/http" ) func main() { - listen, err := net.ListenPacket("udp", "0.0.0.0:8082") - if err != nil { - panic(err) - } - dstAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:8081") - if err != nil { - panic(err) - } - tmp := make([]byte, 4096) - if _, err = io.ReadFull(rand.Reader, tmp); err != nil { - panic(err) - } - - for i := 0; i < 100000; i++ { - _, err := listen.WriteTo(tmp, dstAddr) - if err != nil { - panic(err) - } - } - + thttp.StartFakeFileServer() + ch := make(chan os.Signal) + signal.Notify(ch, os.Kill, os.Interrupt) + <-ch } diff --git a/cmd/testServer/sever.go b/cmd/testServer/sever.go index 3263bc5..d135586 100644 --- a/cmd/testServer/sever.go +++ b/cmd/testServer/sever.go @@ -1,33 +1,131 @@ package main import ( - "fmt" + "bufio" + "bytes" + "io" "net" + "net/http" + _ "net/http/pprof" "os" "os/signal" - "syscall" + "sync" "time" + + "github.com/rc452860/vnet/common/config" + "github.com/rc452860/vnet/common/log" + "github.com/rc452860/vnet/common/pool" + "github.com/rc452860/vnet/network/conn" + "github.com/rc452860/vnet/proxy/client" + "github.com/rc452860/vnet/proxy/server" + "github.com/rc452860/vnet/service" + thttp "github.com/rc452860/vnet/testing/servers/http" ) +const number = 10 + func main() { - listen, err := net.ListenPacket("udp", "0.0.0.0:8081") - if err != nil { - panic(err) - } + config.LoadConfig("config.json") + // start pprof + go func() { + http.ListenAndServe("0.0.0.0:6060", nil) + }() - rage := time.Tick(100 * time.Millisecond) - buf := make([]byte, 4096) - for { - <-rage - _, _, err := listen.ReadFrom(buf) - fmt.Printf("%v\n", buf[0:10]) + for i := 10000; i < 10000+number; i++ { + service.CurrentShadowsocksService().Add("0.0.0.0", "aes-128-gcm", "killer", i, server.ShadowsocksArgs{ + ConnectTimeout: 3000, + Limit: 0, + TCPSwitch: "", + UDPSwitch: "", + }) + err := service.CurrentShadowsocksService().Start(i) if err != nil { - fmt.Print(err.Error()) - continue + log.Err(err) } + } + log.Info("all service is started") + + Testing() + time.Sleep(10 * time.Second) + log.Info("=====================================================================") + ch := make(chan os.Signal, 2) + + signal.Notify(ch, os.Interrupt, os.Kill) + <-ch + gw := new(sync.WaitGroup) + for i := 10000; i < 10000+number; i++ { + gw.Add(1) + go func(index int) { + gw.Done() + service.CurrentShadowsocksService().Stop(index) + }(i) + } + gw.Wait() + time.Sleep(3 * time.Second) + log.Info("all service stoped") + <-ch + log.Info("bybe~") +} +func Testing() { + log.GetLogger("root").Level = log.INFO + thttp.StartFakeFileServer() + wg := new(sync.WaitGroup) + for i := 10000; i < 20000; i++ { + wg.Add(1) + go func() { + TestingClient(int(10000 + 1)) + wg.Done() + }() + } + wg.Wait() + // runtime.GC() +} + +func TestingClient(i int) { + s, c := net.Pipe() + defer s.Close() + defer c.Close() + client := client.NewShadowsocksClient("127.0.0.1", "aes-128-gcm", "killer", i) + cs, err := conn.NewDefaultConn(c, "pipe") + go client.TcpProxy(cs, "127.0.0.1", 8080) + var httpRequest bytes.Buffer + httpRequest.WriteString("GET /download?size=4KB HTTP/1.1\n") + httpRequest.WriteString("Host: localhost:8080\n") + httpRequest.WriteString("Connection: keep-alive\n") + // httpRequest.WriteString("Connection: close\n") + httpRequest.WriteString("Pragma: no-cache\n") + httpRequest.WriteString("Cache-Control: no-cache\n") + httpRequest.WriteString("Upgrade-Insecure-Requests: 1\n") + httpRequest.WriteString("User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.80 Safari/537.36\n") + httpRequest.WriteString("DNT: 1\n\n") + data := httpRequest.Bytes() + s.SetWriteDeadline(time.Now().Add(3 * time.Second)) + _, err = s.Write(data) + if err != nil { + return + } + var count int64 = 0 + buf := pool.GetBuf() + defer pool.PutBuf(buf) + request, err := http.ReadRequest(bufio.NewReader(bufio.NewReader(&httpRequest))) + response, err := http.ReadResponse(bufio.NewReader(s), request) + log.Info("content lenght:%v", response.ContentLength) + if err != nil { + log.Err(err) + return + } + for { + n, err := response.Body.Read(buf) + // buff.Write(buf) + count = count + int64(n) + if err != nil && err != io.EOF { + log.Err(err) + break + } + if count == response.ContentLength { + break + } } - c := make(chan os.Signal) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - <-c + log.Info("done %v", i) } diff --git a/mem.profile b/mem.profile new file mode 100644 index 0000000000000000000000000000000000000000..d98a4693c42f838e15e10698100d7dc4312df31c GIT binary patch literal 2664 zcmV-u3YYaCiwFP!00004|EyMfbQI;;-<{0vzL_Q2CzE9JW;Y3w;g$rLO~w#C9?$oQ z@O{X4;Fu55p85}NHj}W;W+vU4jfeJ+&W5xmAV>m;7L^rG(MlU{lmirDK_Ey0qa|_? zN>I-+t$L)Kws>i4&-=~8F%7x=J` zVs8BGsbeG+H#}>l;s(GVmzg z5JK4j7BLt|s5GX0b$pOV!^{VR z$9|N_;f=Ru^8%l<+jH)CugP>|B~wqVyjfMkvj31soOih{_x7zF=lsN*LE`++?8ny+ zwPZTq)8QxJ`|j-@9!O+@*VB0fe9`mKU-vg6Iz{^e9fCU_z3CYJBIPo%e&XM)d|E&?8QzqzI>kO?MDR`ekszJ9)+ zXwnA-G*usz;1Z!!1O)h}UmPGb0DkMH7(f{=6Q=Tho;zv2#vJcX&2%`B4}q=9AdkLw zf7AcBoXl8jOh)vhJbdE(c}8SW9!R1K@$ec;-R6OS1);nOAs$353gsdo9`}E8o&?W_ zy)JqxAEx1H6wAl^_P$TB0yt_7O94#B(}fu#paT5!uPzX*5Vlx-DTJALrZ7wNp+dao z@0Un?KP;gx{ZN4`gi6te{CIdTEkhAhQ>X~4aFsAy^r0fW>3s^(3h^QjJCl8`J)4If zNSPhZuFe#2D38xCDD)TEbr5|BAfYh)g!;oU2hR~^iGYgnyIRM@eC-4e{=0gLa-RREb_%L7ta+W(T7U#)=gJP z)Bspo4nPQpglZ8`Dc=4TT`<-v=Sz8ReR2;w)MGAkIJoR!NvXXa>3$J?s0{DFXwwF8 z@Tb<8YudyXSX{fTrTL`|Nfcb5cSAe(9`j%Y%~-^4`;{huPGxlOL0< z;GSAg=oc(TB(-upc=a-26ohW8WkIOHHMC_S?mcs}j(j-}2UnkA2VOXI*?GonOK&r) zI8V7)e53Zn-!weDWf$>R0w=8sm4J+8;Wp8SrsM9>r7q&RUnDoV1~#RaIUHQpwCPq> zK$N@z4X)XMGL@V(og$za`0P%q&QjQGrCbWNxK=0^eP||rdp9Lt2066#WiSuV6K0Em zX5kNy+D%A0fi~gs`n`ngFN*Upq)|wyDrjzM|} zPLf>ud@;tTgWX~f0nNd`8l*e~ z=(l(Xa67)8-rjTZrIQqM!!x8PnD=h%c!9loWT>0O%bGLS6JQa81wO!|lT+4g-!bO) zCD(b!-9%%r^nJ;0cBh8> z)eYdwd0%&O2m55t(8A5;@_~*tH?lqKs3aq9WAZe+r+0|h(riacNd{p5>H#*rBl!ns z-_A#lKQ}bT9st|CTJ)i6yz@Of7czc+o_ovO!93YLG~MAq)gwuha(1}Z4WtO|F>gn! z1rldh-#6^=>sLN^zPj_r=eDOLJC2=6!T^h$cQ|E-?9$u9 z5k+=2tTGf`&I!k2T4aUR@B<}c=&~!KB~(N9hUru5WKL8~=*o>FUhA)g{eN@RSea;$ zB3f%G9;uxt-%(c`YF8B_6jvfzyiuyCS`^b7!m(wFt|j6TWvQk{4J}?N#lpIApQw864xzgXeg|=t_Zg_*40Y7%*U0cm=ZCh2g9+1Whj?rm{})6 z?es*mmT9vf9E<%MLhXv$sKrOuCaj0`7NtEJSsD7Dc11PrN$65V)yU6EsZpy>G{mC% zN;$mX&PmnzJH-kqB=sICUW4g3Ni?qmhO_Cxt z4aKxb3(5Y!CJb>aZR>4{(kM@{H6gWzW6g4DM&m6th1%j;gaiZofut&rkY_b=Va5}w zDWOJc z$=X>pvAomftz0zrEZHj2HgY6~ z9BB*3b%mbujr#kLrYdp?c~DVBc22BaiC0QXG_57kwzz(NV`E$nj&~cQIB_jxnnqPa ziK~i1OqWD;ORA_^8g8qU?l-i!A{UP(Kn{dj!?axT zgxN>5)>cgoMZ%Glid0cWzbmEs#N?GV+LP_CS(9gy+(-8~vr+cEc$-=c>HfBOR5hC9 zz*r_ttwx9<#32>c;(Ez0Z_R|vZWdBnq!^3qNl!@m_@jzRB Wq9GR5SNey6953d2pQl2 literal 0 HcmV?d00001 diff --git a/proxy/server/proxy.go b/proxy/server/proxy.go index ba26262..85c9c40 100644 --- a/proxy/server/proxy.go +++ b/proxy/server/proxy.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "strings" + "sync" "time" "github.com/rc452860/vnet/utils/addr" @@ -35,6 +36,8 @@ type ProxyService struct { Status string `json:"status"` Cancel context.CancelFunc `json:"-"` Tick time.Duration `json:"-"` + TCPLock *sync.Mutex + UDPLock *sync.Mutex } func NewProxyService() *ProxyService { @@ -105,11 +108,12 @@ func (this *ProxyService) traffic(data record.Traffic) { func (this *ProxyService) proxyRequest(data record.ConnectionProxyRequest) { eventbus.GetEventBus().Publish("record:proxyRequest", data) key := addr.GetIPFromAddr(data.ClientAddr) - if this.LastOneMinuteConnections.Get(key) == nil { + last := this.LastOneMinuteConnections.Get(key) + if last == nil { this.LastOneMinuteConnections.Put(key, []record.ConnectionProxyRequest{data}, this.Tick) } else { - last := this.LastOneMinuteConnections.Get(key).([]record.ConnectionProxyRequest) - this.LastOneMinuteConnections.Put(key, append(last, data), this.Tick) + swap := last.([]record.ConnectionProxyRequest) + this.LastOneMinuteConnections.Put(key, append(swap, data), this.Tick) } // just print tcp log @@ -146,7 +150,7 @@ func (this *ProxyService) Start() error { } func (this *ProxyService) Stop() error { - log.Info("proxy stop") + start := time.Now() this.Cancel() if this.TCP != nil { err := this.TCP.Close() @@ -161,5 +165,6 @@ func (this *ProxyService) Stop() error { } } this.Status = "stop" + log.Info("proxy stop consume %v", time.Since(start).Seconds()) return nil } diff --git a/proxy/server/shadowsocks.go b/proxy/server/shadowsocks.go index ec0c1a1..e4dd557 100644 --- a/proxy/server/shadowsocks.go +++ b/proxy/server/shadowsocks.go @@ -11,6 +11,7 @@ import ( "github.com/rc452860/vnet/component/dnsx" "github.com/rc452860/vnet/utils/addr" + "github.com/rc452860/vnet/utils/goroutine" "github.com/pkg/errors" @@ -166,8 +167,8 @@ func (s *ShadowsocksProxy) startTCP() error { } s.TCP = server - go func() { - defer server.Close() + go goroutine.Protect(func() { + defer s.clearTCP() for { select { case <-s.ProxyService.Done(): @@ -188,7 +189,7 @@ func (s *ShadowsocksProxy) startTCP() error { continue } - go func() { + go goroutine.Protect(func() { defer lcon.Close() /** 默认装饰器 */ lcd, err := conn.DefaultDecorate(lcon, conn.TCP) @@ -248,9 +249,9 @@ func (s *ShadowsocksProxy) startTCP() error { } logging.Error("relay error: %v", err) } - }() + }) } - }() + }) return nil } @@ -268,17 +269,12 @@ func relayTCP(left, right net.Conn) (int64, int64, error) { } }() - go func() { - defer func() { - if e := recover(); e != nil { - log.Error("panic in timedCopy: %v", e) - } - }() + go goroutine.Protect(func() { n, err := io.Copy(right, left) right.SetDeadline(time.Now()) // wake up the other goroutine blocking on right left.SetDeadline(time.Now()) // wake up the other goroutine blocking on left ch <- res{n, err} - }() + }) n, err := io.Copy(left, right) right.SetDeadline(time.Now()) // wake up the other goroutine blocking on right @@ -341,8 +337,8 @@ func (s *ShadowsocksProxy) startUDP() error { // logging.Info("listening UDP on %s", addr) - go func() { - defer server.Close() + go goroutine.Protect(func() { + defer s.clearUDP() for { select { case <-s.ProxyService.Done(): @@ -397,7 +393,7 @@ func (s *ShadowsocksProxy) startUDP() error { continue } } - }() + }) return nil } @@ -478,12 +474,12 @@ func (m *natmap) Del(key string) net.PacketConn { func (m *natmap) Add(peer net.Addr, dst, src net.PacketConn) { m.Set(peer.String(), src) - go func() { + go goroutine.Protect(func() { timedCopy(dst, peer, src, m.timeout) if pc := m.Del(peer.String()); pc != nil { pc.Close() } - }() + }) } // copy from src to dst at target with read timeout @@ -514,3 +510,11 @@ func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout } } } + +func (s *ShadowsocksProxy) clearUDP() { + +} + +func (s *ShadowsocksProxy) clearTCP() { + +} diff --git a/record/record.go b/record/record.go index 893ef3a..7ccb1f0 100644 --- a/record/record.go +++ b/record/record.go @@ -127,11 +127,12 @@ func (g *GlobalResourceMonitor) trafficStatistics(data Traffic) { } func (g *GlobalResourceMonitor) lastOneMinuteConnections(data ConnectionProxyRequest) { key := addr.GetIPFromAddr(data.ClientAddr) - if g.LastOneMinuteConnections.Get(key) == nil { + last := g.LastOneMinuteConnections.Get(key) + if last == nil { g.LastOneMinuteConnections.Put(key, []ConnectionProxyRequest{data}, g.tick) } else { - last := g.LastOneMinuteConnections.Get(key).([]ConnectionProxyRequest) - g.LastOneMinuteConnections.Put(key, append(last, data), g.tick) + swap := last.([]ConnectionProxyRequest) + g.LastOneMinuteConnections.Put(key, append(swap, data), g.tick) } } diff --git a/record/record_test.go b/record/record_test.go index 0910b3b..041058d 100644 --- a/record/record_test.go +++ b/record/record_test.go @@ -10,20 +10,21 @@ import ( ) func Test_GetLastOneMinuteOnlineByPort(t *testing.T) { - instance := GetGRMInstanceWithTick(100 * time.Millisecond) - for i := 0; i < 10; i++ { + GetGRMInstanceWithTick(100 * time.Millisecond) + for i := 0; i < 10000000; i++ { eventbus.GetEventBus().Publish("record:proxyRequest", ConnectionProxyRequest{ ConnectionPair: ConnectionPair{ - ProxyAddr: addr.ParseAddrFromString("tcp", "0.0.0.0:8080"), - ClientAddr: addr.ParseAddrFromString("tcp", fmt.Sprintf("192.168.1.1:%v", i)), - TargetAddr: addr.ParseAddrFromString("tcp", fmt.Sprintf("192.168.1.2:%v", i)), + ProxyAddr: addr.ParseAddrFromString("tcp", fmt.Sprintf("0.0.0.0:%v", i%100+100)), + ClientAddr: addr.ParseAddrFromString("tcp", fmt.Sprintf("192.168.1.%v:%v", i%255, 35)), + TargetAddr: addr.ParseAddrFromString("tcp", fmt.Sprintf("192.168.1.%v:%v", i%255, 35)), }, }) - time.Sleep(5 * time.Millisecond) + // time.Sleep(5 * time.Millisecond) } - if addr.GetIPFromAddr(instance.GetLastOneMinuteOnlineByPort()[8080][0]) != "192.168.1.1" { - t.FailNow() - } + // if addr.GetIPFromAddr(instance.GetLastOneMinuteOnlineByPort()[8080][0]) != "192.168.1.1" { + // t.FailNow() + // } + t.Log("aaa") } diff --git a/testing/servers/http/http.go b/testing/servers/http/http.go new file mode 100644 index 0000000..c8a77ea --- /dev/null +++ b/testing/servers/http/http.go @@ -0,0 +1,84 @@ +package http + +import ( + "crypto/rand" + "fmt" + "io" + "net/http" + "time" + + "github.com/gin-gonic/gin" + "github.com/rc452860/vnet/common/log" + "github.com/rc452860/vnet/utils/datasize" +) + +type FakeFile struct { + Size int64 + Offset int64 +} + +func NewFakeFile(size int64) *FakeFile { + return &FakeFile{ + Size: size, + Offset: 0, + } +} +func (f *FakeFile) Read(p []byte) (n int, err error) { + if f.Offset >= f.Size { + return 0, io.EOF + } + remain := f.Size - f.Offset + if int64(cap(p)) < remain { + n, err = rand.Read(p) + } else { + n, err = rand.Read(p[:int(remain)]) + } + + f.Offset = f.Offset + int64(n) + return n, err +} + +func (f *FakeFile) Seek(offset int64, whence int) (int64, error) { + if whence == io.SeekEnd { + f.Offset = f.Size + offset + + } + if whence == io.SeekStart { + f.Offset = 0 + offset + } + if whence == io.SeekCurrent { + f.Offset = f.Offset + offset + } + if f.Offset > f.Size || f.Offset < 0 { + return 0, fmt.Errorf("offset is out of bounds") + } + return f.Offset, nil +} + +func StartFakeFileServer() *http.Server { + r := gin.Default() + r.GET("download", func(c *gin.Context) { + sizeStr := c.Query("size") + size, err := datasize.Parse(sizeStr) + if err != nil { + log.Err(err) + } + file := NewFakeFile(int64(size)) + c.Writer.Header().Add("Content-Disposition", fmt.Sprintf(`attachment; filename="%s.test"`, sizeStr)) + http.ServeContent(c.Writer, c.Request, fmt.Sprintf("%s.test", sizeStr), time.Now(), file) + }) + server := &http.Server{ + Addr: ":8080", + Handler: r, + } + go func() { + if err := server.ListenAndServe(); err != nil { + if err == http.ErrServerClosed { + log.Info("FakeFileServer closed") + } else { + log.Error("Server closed unexpect") + } + } + }() + return server +} diff --git a/utils/goroutine/goroutine.go b/utils/goroutine/goroutine.go new file mode 100644 index 0000000..789dcb5 --- /dev/null +++ b/utils/goroutine/goroutine.go @@ -0,0 +1,12 @@ +package goroutine + +import "log" + +func Protect(g func()) { + defer func() { + if x := recover(); x != nil { + log.Printf("run time panic: %v", x) + } + }() + g() +}