diff --git a/zcli/service.go b/zcli/service.go index 11e86f6..c2ff747 100644 --- a/zcli/service.go +++ b/zcli/service.go @@ -135,21 +135,18 @@ func LaunchServiceRun(name string, description string, fn func(), config ...*dae // LaunchService Launch Service func LaunchService(name string, description string, fn func(), config ...*daemon.Config) (daemon.ServiceIface, error) { - once.Do(func() { - var daemonConfig *daemon.Config + daemonConfig := &daemon.Config{ + Name: name, + Description: description, + Options: map[string]interface{}{ + "UserService": true, + }, + } if len(config) > 0 { daemonConfig = config[0] daemonConfig.Name = name daemonConfig.Description = description - } else { - daemonConfig = &daemon.Config{ - Name: name, - Description: description, - Options: map[string]interface{}{ - // "UserService": true, - }, - } } // The file path is redirected to the current execution file path diff --git a/zcli/utils_test.go b/zcli/utils_test.go index 6a2bcac..5e52b9a 100644 --- a/zcli/utils_test.go +++ b/zcli/utils_test.go @@ -17,8 +17,8 @@ type testCmd struct { flag1 *bool flag2 *int flag3 *string - run bool tt *zls.TestUtil + run bool } func (cmd *testCmd) Flags(sub *Subcommand) { diff --git a/zhttp/http.go b/zhttp/http.go index 7f610b1..f1bb3a4 100644 --- a/zhttp/http.go +++ b/zhttp/http.go @@ -524,7 +524,6 @@ func (m *multipartHelper) upload(req *http.Request, upload func(io.Writer, io.Re func (m *multipartHelper) Upload(req *http.Request) { bodyBuf := zutil.GetBuff(1048576) - defer zutil.PutBuff(bodyBuf) bodyWriter := multipart.NewWriter(bodyBuf) m.upload(req, nil, bodyWriter) @@ -532,6 +531,9 @@ func (m *multipartHelper) Upload(req *http.Request) { req.Header.Set(textContentType, bodyWriter.FormDataContentType()) b := bytes.NewReader(bodyBuf.Bytes()) + + zutil.PutBuff(bodyBuf) + req.Body = ioutil.NopCloser(b) req.ContentLength = int64(b.Len()) } diff --git a/znet/handler.go b/znet/handler.go index e6e6185..a46d4d4 100644 --- a/znet/handler.go +++ b/znet/handler.go @@ -64,13 +64,13 @@ func requestLog(c *Context) { var status string end := time.Now() statusCode := zutil.GetBuff() - defer zutil.PutBuff(statusCode) latency := end.Sub(c.startTime) code := c.prevData.Code.Load() statusCode.WriteString(" ") statusCode.WriteString(strconv.FormatInt(int64(code), 10)) statusCode.WriteString(" ") s := statusCode.String() + zutil.PutBuff(statusCode) switch { case code >= 200 && code <= 299: status = c.Log.ColorBackgroundWrap(zlog.ColorBlack, zlog.ColorGreen, s) diff --git a/znet/utils.go b/znet/utils.go index 4dd1d86..b71ad6c 100644 --- a/znet/utils.go +++ b/znet/utils.go @@ -27,7 +27,7 @@ var Utils = utils{ } const ( - defaultPattern = `[^\/.]+` + defaultPattern = `[^\/]+` idPattern = `[\d]+` idKey = `id` allPattern = `.*` diff --git a/znet/utils_test.go b/znet/utils_test.go index 92e88b7..3c4851d 100644 --- a/znet/utils_test.go +++ b/znet/utils_test.go @@ -39,10 +39,16 @@ func TestURLMatchAndParse(t *testing.T) { tt.EqualTrue(ok) tt.Equal(1, len(match)) + match, ok = Utils.URLMatchAndParse("/hi/1.1/123", "/:name/:code/:id") + t.Log(match) + tt.EqualTrue(ok) + tt.Equal(3, len(match)) + match, ok = Utils.URLMatchAndParse("/aaa/hi", "/:name/:*") t.Log(match) tt.EqualTrue(ok) tt.Equal(2, len(match)) + } func Test_parsPattern(t *testing.T) { @@ -58,6 +64,11 @@ func Test_parsPattern(t *testing.T) { tt.EqualExit("([\\w\\d-]+)", p) tt.EqualExit([]string{"name"}, s) + p, s = parsePattern([]string{":name", ":id"}, "/") + tt.Log(p, s) + tt.EqualExit("/([^\\/]+)/([\\d]+)", p) + tt.EqualExit([]string{"name", "id"}, s) + p, s = parsePattern([]string{"{p:[\\w\\d-]+}.pth"}, "") tt.Log(p, s) tt.EqualExit("([\\w\\d-]+).pth", p) diff --git a/zpool/pool.go b/zpool/pool.go index 5d16536..6302dbb 100644 --- a/zpool/pool.go +++ b/zpool/pool.go @@ -59,9 +59,9 @@ func New(size int, max ...int) *WorkPool { } maxIdle := minIdle if len(max) > 0 && max[0] > 0 { - max := uint(max[0]) - if max > maxIdle { - maxIdle = max + m := uint(max[0]) + if m > maxIdle { + maxIdle = m } } @@ -103,10 +103,10 @@ func (wp *WorkPool) PanicFunc(handler PanicFunc) { } func (wp *WorkPool) do(cxt context.Context, fn taskfn, param []interface{}) error { - wp.activeNum.Add(1) if wp.IsClosed() { return ErrPoolClosed } + wp.activeNum.Add(1) wp.mu.Lock() run := func(w *worker) { if fn != nil { @@ -131,29 +131,22 @@ func (wp *WorkPool) do(cxt context.Context, fn taskfn, param []interface{}) erro default: switch { case uint(wp.usedNum.Load()) >= wp.minIdle: - wp.mu.Unlock() - // todo 超时处理 - // 需要启动队列功能了 - select { - case <-cxt.Done(): - wp.mu.Lock() - if uint(wp.usedNum.Load()) >= wp.maxIdle { - wp.mu.Unlock() - return ErrWaitTimeout - } + if uint(wp.usedNum.Load()) < wp.maxIdle { w := add() run(w) return nil - // 尝试扩大容量? + } + wp.mu.Unlock() + select { + case <-cxt.Done(): + wp.activeNum.Sub(1) + return ErrWaitTimeout case w := <-wp.queue: if w != nil { run(w) } else { return ErrPoolClosed } - // default: - // todo 进入队列 - // return nil } case uint(wp.usedNum.Load()) < wp.minIdle: w := add() diff --git a/zpool/pool_test.go b/zpool/pool_test.go index 07c9f59..6683bd6 100644 --- a/zpool/pool_test.go +++ b/zpool/pool_test.go @@ -126,14 +126,10 @@ func TestPoolCap(t *testing.T) { tt.EqualExit(uint(0), p.Cap()) newSize := 5 - go func() { - tt.EqualExit(uint(0), p.Cap()) - time.Sleep(time.Second) - p.Continue(newSize) - tt.EqualExit(uint(newSize), p.Cap()) - }() + p.Continue(newSize) + tt.EqualExit(uint(newSize), p.Cap()) - restarSum := 6 + restarSum := 7 g.Add(restarSum) for i := 0; i < restarSum; i++ { @@ -149,13 +145,13 @@ func TestPoolCap(t *testing.T) { }() } g.Wait() - tt.EqualExit(uint(newSize), p.Cap()) + tt.EqualExit(uint(restarSum), p.Cap()) p.Continue(1000) tt.EqualExit(uint(maxsize), p.Cap()) p.Close() - p.Continue(1000) + tt.EqualExit(uint(0), p.Cap()) } func TestPoolPanicFunc(t *testing.T) { @@ -223,12 +219,13 @@ func TestPoolTimeout(t *testing.T) { }, time.Second/3) t.Log(err) if v > 0 { - tt.EqualTrue(err == zpool.ErrWaitTimeout) + tt.Equal(err, zpool.ErrWaitTimeout) } if err == zpool.ErrWaitTimeout { t.Log(v) } } + p.Wait() } func TestPoolAuto(t *testing.T) { diff --git a/zreflect/utils_test.go b/zreflect/utils_test.go index f4015e9..af4685c 100644 --- a/zreflect/utils_test.go +++ b/zreflect/utils_test.go @@ -19,14 +19,15 @@ type ( DemoSt struct { Date2 time.Time + Any interface{} + any interface{} Child3 *DemoChildSt Remark string `json:"remark"` note string - Any interface{} - any interface{} Name string `json:"username"` - Slice [][]string + pri string Hobby []string + Slice [][]string Child struct { Title string `json:"child_user_title"` DemoChild2 Child2 `json:"demo_child_2"` @@ -37,7 +38,6 @@ type ( child4 DemoChildSt Age uint Lovely bool - pri string } TestSt struct { Name string diff --git a/ztype/conv.go b/ztype/conv.go index b162826..3f70405 100644 --- a/ztype/conv.go +++ b/ztype/conv.go @@ -12,12 +12,12 @@ import ( type Conver struct { MatchName func(mapKey, fieldName string) bool + ConvHook func(i reflect.Value, o reflect.Type) (reflect.Value, error) TagName string ZeroFields bool Squash bool Deep bool Merge bool - ConvHook func(i reflect.Value, o reflect.Type) (reflect.Value, error) } var conv = Conver{TagName: tagName, Squash: true, MatchName: strings.EqualFold} diff --git a/ztype/to_test.go b/ztype/to_test.go index 0bea321..2b69bc6 100644 --- a/ztype/to_test.go +++ b/ztype/to_test.go @@ -24,15 +24,15 @@ type ( } JsonTime time.Time type2 struct { - E *uint - G map[string]int `z:"gg"` - S2 *type1 - F []string `json:"fs"` - type1 - S1 type1 - D bool Date time.Time `z:"date_time"` JDate JsonTime `z:"j_date"` + E *uint + G map[string]int `z:"gg"` + S2 *type1 + F []string `json:"fs"` + type1 + S1 type1 + D bool } ) diff --git a/zutil/args.go b/zutil/args.go index 825471d..6f8d74e 100644 --- a/zutil/args.go +++ b/zutil/args.go @@ -137,7 +137,6 @@ func (args *Args) CompileString(format string, initialValue ...interface{}) stri // Compile compiles builder's format to standard sql and returns associated args func (args *Args) Compile(format string, initialValue ...interface{}) (query string, values []interface{}) { buf := GetBuff(256) - defer PutBuff(buf) idx := strings.IndexRune(format, '$') offset := 0 values = initialValue @@ -175,6 +174,8 @@ func (args *Args) Compile(format string, initialValue ...interface{}) (query str query = buf.String() + PutBuff(buf) + if len(args.sqlNamedArgs) > 0 { ints := make([]int, 0, len(args.sqlNamedArgs)) for _, p := range args.sqlNamedArgs { diff --git a/zutil/daemon/daemon_darwin.go b/zutil/daemon/daemon_darwin.go index aa1dc25..93f9efe 100644 --- a/zutil/daemon/daemon_darwin.go +++ b/zutil/daemon/daemon_darwin.go @@ -124,10 +124,7 @@ func (s *darwinLaunchdService) Install() error { if v, ok := s.Options[optionKeepAlive]; ok { keepAlive, _ = v.(bool) } - load := optionRunAtLoadDefault - if v, ok := s.Options[optionRunAtLoad]; ok { - load, _ = v.(bool) - } + load := isServiceRestart(s.Config) sessionCreate := optionSessionCreateDefault if v, ok := s.Options[optionSessionCreate]; ok { sessionCreate, _ = v.(bool) diff --git a/zutil/daemon/daemon_freebsd.go b/zutil/daemon/daemon_freebsd.go index 356b4ee..229cf24 100644 --- a/zutil/daemon/daemon_freebsd.go +++ b/zutil/daemon/daemon_freebsd.go @@ -103,10 +103,7 @@ func (s *freebsdRcdService) Install() error { if v, ok := s.Options[optionKeepAlive]; ok { keepAlive, _ = v.(bool) } - load := optionRunAtLoadDefault - if v, ok := s.Options[optionRunAtLoad]; ok { - load, _ = v.(bool) - } + load := isServiceRestart(s.Config) sessionCreate := optionSessionCreateDefault if v, ok := s.Options[optionSessionCreate]; ok { sessionCreate, _ = v.(bool) diff --git a/zutil/daemon/daemon_windows.go b/zutil/daemon/daemon_windows.go index 101c0de..732cba2 100644 --- a/zutil/daemon/daemon_windows.go +++ b/zutil/daemon/daemon_windows.go @@ -2,18 +2,16 @@ package daemon import ( "fmt" - "os" "strconv" "strings" "sync" "time" + "github.com/sohaha/zlsgo/zshell" "golang.org/x/sys/windows/registry" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/eventlog" "golang.org/x/sys/windows/svc/mgr" - - "github.com/sohaha/zlsgo/zshell" ) type ( @@ -71,6 +69,7 @@ func (w *windowsService) setError(err error) { defer w.errSync.Unlock() w.stopStartErr = err } + func (w *windowsService) getError() error { w.errSync.Lock() defer w.errSync.Unlock() @@ -140,11 +139,8 @@ func (w *windowsService) Install() error { _ = s.Delete() return fmt.Errorf("installAsEventCreate() failed: %s", err) } - load := optionRunAtLoadDefault - if l, ok := w.Options[optionRunAtLoad]; ok { - load, _ = l.(bool) - } - if load { + + if isServiceRestart(w.Config) { _ = s.SetRecoveryActions([]mgr.RecoveryAction{ { Type: mgr.ServiceRestart, @@ -162,27 +158,25 @@ func (w *windowsService) Uninstall() error { return err } defer m.Disconnect() + s, err := m.OpenService(w.Name) if err != nil { return fmt.Errorf("service %s is not installed", w.Name) } defer s.Close() - _ = s.SetRecoveryActions([]mgr.RecoveryAction{ - { - Type: mgr.NoAction, - Delay: 0, - }, - }, 0) - _ = zshell.BgRun("taskkill /F /pid " + strconv.Itoa(os.Getpid())) + _ = w.Stop() - if err = s.Delete(); err != nil { + + err = s.Delete() + if err != nil { return err } + err = eventlog.Remove(w.Name) if err != nil { return fmt.Errorf("removeEventLogSource() failed: %s", err) } - // log.Warn("after uninstalling, you need to restart the system to take effect") + return nil } @@ -220,6 +214,16 @@ func (w *windowsService) Start() error { return err } defer s.Close() + + if isServiceRestart(w.Config) { + _ = s.SetRecoveryActions([]mgr.RecoveryAction{ + { + Type: mgr.ServiceRestart, + Delay: 0, + }, + }, 0) + } + return s.Start() } @@ -229,30 +233,31 @@ func (w *windowsService) Stop() error { return err } defer m.Disconnect() + s, err := m.OpenService(w.Name) if err != nil { return err } defer s.Close() + if isServiceRestart(w.Config) { + _ = s.SetRecoveryActions([]mgr.RecoveryAction{ + { + Type: mgr.NoAction, + Delay: 0, + }, + }, 0) + } + return w.stopWait(s) } func (w *windowsService) Restart() error { - m, err := connect() - if err != nil { - return err - } - defer m.Disconnect() - s, err := m.OpenService(w.Name) + err := w.Stop() if err != nil { return err } - defer s.Close() - - _ = w.stopWait(s) - - return s.Start() + return w.Start() } func (w *windowsService) Status() string { @@ -275,20 +280,35 @@ func (w *windowsService) Status() string { return "Running" case svc.StopPending: return "StopPending" + case svc.Stopped: + return "Stop" } return strconv.Itoa(int(q.State)) } +func (w *windowsService) forceKeep(processId uint32) error { + ss := "taskkill /F /pid " + strconv.Itoa(int(processId)) + _, _, _, err := zshell.Run(ss) + return err +} + func (w *windowsService) stopWait(s *mgr.Service) error { status, err := s.Control(svc.Stop) if err != nil { - return err + if !strings.Contains(err.Error(), "not valid") { + return err + } + status, err = s.Query() + if err != nil { + return err + } + _ = w.forceKeep(status.ProcessId) } - timeDuration := time.Millisecond * 50 + + timeDuration := time.Millisecond * 100 timeout := time.After(getStopTimeout() + (timeDuration * 2)) tick := time.NewTicker(timeDuration) defer tick.Stop() - for status.State != svc.Stopped { select { case <-tick.C: @@ -297,6 +317,7 @@ func (w *windowsService) stopWait(s *mgr.Service) error { return err } case <-timeout: + _ = w.forceKeep(status.ProcessId) break } } diff --git a/zutil/daemon/utils.go b/zutil/daemon/utils.go index f9a4f3e..9803c40 100644 --- a/zutil/daemon/utils.go +++ b/zutil/daemon/utils.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/sohaha/zlsgo/zshell" + "github.com/sohaha/zlsgo/ztype" ) func (c *Config) execPath() (path string) { @@ -77,3 +78,11 @@ func runcmd(commands []string, in *bytes.Reader, out, outErr *bytes.Buffer) erro } return err } + +func isServiceRestart(c *Config) bool { + load := optionRunAtLoadDefault + if l, ok := c.Options[optionRunAtLoad]; ok { + load = ztype.ToBool(l) + } + return load +}