diff --git a/cmd/v2/lib/assistant.go b/cmd/v2/lib/assistant.go index 186c4b575..2e4f48f61 100644 --- a/cmd/v2/lib/assistant.go +++ b/cmd/v2/lib/assistant.go @@ -4,6 +4,8 @@ import ( "context" "errors" "fmt" + "go_dreamfactory/cmd/v2/lib/common" + "go_dreamfactory/cmd/v2/service/observer" "go_dreamfactory/pb" "math" "sync/atomic" @@ -27,9 +29,10 @@ type assistant struct { caller Handler //处理器 status uint32 //状态 resultCh chan *CallResult //调用结果 + obs observer.Observer } -func NewAssistant(pm ParamMgr) (Aiassistant, error) { +func NewAssistant(obs observer.Observer, pm ParamMgr) (Aiassistant, error) { if err := pm.Check(); err != nil { return nil, err @@ -42,6 +45,7 @@ func NewAssistant(pm ParamMgr) (Aiassistant, error) { caller: pm.Caller, status: STATUS_ORIGINAL, resultCh: pm.ResultCh, + obs: obs, } if err := a.init(); err != nil { return nil, err @@ -50,7 +54,7 @@ func NewAssistant(pm ParamMgr) (Aiassistant, error) { } func (a *assistant) init() error { - logrus.Info("AI助手初始化") + logrus.Debug("AI助手初始化") //并发量的计算 //并发量 ≈ 超时时间 / 发送的间隔时间 var total = int64(a.timeout)/int64(1e9/a.lps) + 1 @@ -65,12 +69,13 @@ func (a *assistant) init() error { } a.goPool = gp - logrus.WithField("并发量", a.concurrency).Info("AI助手初始化完成 并发量 ") + logrus.WithField("并发量", a.concurrency).Debug("AI助手初始化完成 ") return nil } func (a *assistant) callOne(req *RawReq) *RawResp { atomic.AddInt64(&a.callCount, 1) + logrus.WithField("count", &a.callCount).WithField("Len", len(req.Req)).Debug("调用协议") if req == nil { return &RawResp{ID: -1, Err: errors.New("无效的请求")} } @@ -101,6 +106,22 @@ func (a *assistant) asyncCall() { a.goPool.Take() go func() { defer func() { + if p := recover(); p != nil { + err, ok := interface{}(p).(error) + var errMsg string + if ok { + errMsg = fmt.Sprintf("Async Call Panic! (error: %s)", err) + } else { + errMsg = fmt.Sprintf("Async Call Panic! (clue: %#v)", p) + } + logrus.Errorln(errMsg) + result := &CallResult{ + Id: -1, + Code: RES_CODE_FATAL_CALL, + Message: errMsg, + } + a.sendResult(result) + } a.goPool.Return() }() @@ -118,12 +139,13 @@ func (a *assistant) asyncCall() { Id: req.ID, Req: req, Code: RES_CODE_CALL_TIMEOUT, - Message: fmt.Sprintf("超时,期望< %v", a.timeout), + Message: fmt.Sprintf("超时,期望小于 %v", a.timeout), Elapse: a.timeout, } a.sendResult(result) }) resp := a.callOne(&req) + logrus.WithField("耗时", resp.Elapse).Debug("实际耗时") if !atomic.CompareAndSwapUint32(&callStatus, 0, 1) { return @@ -146,14 +168,13 @@ func (a *assistant) asyncCall() { } a.sendResult(result) }() - } // 停止发送 func (a *assistant) prepareStop(ctxErr error) { logrus.WithField("cause", ctxErr).Info("准备停止") atomic.CompareAndSwapUint32(&a.status, STATUS_STARTED, STATUS_STOPPING) - logrus.Info("关闭结果通道") + logrus.Debug("关闭结果通道") close(a.resultCh) atomic.StoreUint32(&a.status, STATUS_STOPPED) } @@ -181,36 +202,35 @@ func (a *assistant) handleReq(tick <-chan time.Time) { func (a *assistant) sendResult(result *CallResult) bool { if atomic.LoadUint32(&a.status) != STATUS_STARTED { + a.printResult(result, "已停止") return false } select { case a.resultCh <- result: return true default: + a.printResult(result, "结果通道已满") return false } } -//注册账号 -func (a *assistant) registUser() { - -} - -//登录账号 -func (a *assistant) login() { - +func (a *assistant) printResult(result *CallResult, cause string) { + resultMsg := fmt.Sprintf( + "Id:%d,Code=%d,Msg=%s,Elapse=%v", + result.Id, result.Code, result.Message, result.Elapse) + logrus.Warnf("result:%s (cause:%s)", resultMsg, cause) } // 启动AI助手 func (a *assistant) Start() bool { - logrus.Infoln("AI助手启动") + logrus.Debug("AI助手启动") // 节流 周期性向目标发送 var ticker <-chan time.Time if a.lps > 0 { //间隔时间 interval := time.Duration(1e9 / a.lps) - logrus.Infof("启动节流控制 间隔: %v", interval) + logrus.Debugf("启动节流控制 间隔: %v", interval) ticker = time.Tick(interval) } @@ -224,7 +244,7 @@ func (a *assistant) Start() bool { atomic.StoreUint32(&a.status, STATUS_STARTED) go func() { - logrus.Infoln("请求处理...") + logrus.Debug("请求处理中...") a.handleReq(ticker) logrus.Infof("停止 调用次数:%d", a.callCount) }() @@ -238,6 +258,9 @@ func (a *assistant) Stop() error { } func (a *assistant) ShowResult() { + statistics := &Statistics{} + max := statistics.MaxElapse + min := statistics.MinElapse for r := range a.resultCh { if r.Code != RES_CODE_SUCCESS { @@ -250,6 +273,28 @@ func (a *assistant) ShowResult() { logrus.Error("结果解析失败") continue } - logrus.WithFields(logrus.Fields{"mainType": msg.MainType, "subType": msg.SubType}).Debug("读取结果") + // 协议名 + statistics.Route = fmt.Sprintf("%s.%s", msg.MainType, msg.SubType) + // 总耗时 + statistics.ElapseTotal += float64(r.Elapse.Nanoseconds()) + + if float64(r.Elapse.Nanoseconds()) > max { + max = float64(r.Elapse.Nanoseconds()) + } else { + min = float64(r.Elapse.Nanoseconds()) + } + logrus.WithFields(logrus.Fields{"mainType": msg.MainType, "subType": msg.SubType, "耗时": r.Elapse}).Info("结果") } + if a.callCount == 1 { + min = max + } + // 调用次数 + statistics.CallCount = a.callCount + statistics.ElapseTotal = common.FormatFloatCommon(statistics.ElapseTotal / 1e6) + statistics.MaxElapse = common.FormatFloatCommon(max / 1e6) + statistics.MinElapse = common.FormatFloatCommon(min / 1e6) + //平均耗时=总耗时/调用次数 + statistics.AvgElapse = common.FormatFloatCommon(statistics.ElapseTotal / float64(statistics.CallCount)) + + a.obs.Notify(observer.EVENT_RESULT, statistics) } diff --git a/cmd/v2/lib/common/lang.go b/cmd/v2/lib/common/lang.go index 462fe4eec..435f0b922 100644 --- a/cmd/v2/lib/common/lang.go +++ b/cmd/v2/lib/common/lang.go @@ -97,9 +97,12 @@ const ( TOOLBAR_PB = "protobuf" TOOLBAR_AUTO = "自动化" - TOOLBAR_PERF_TIP = "使用说明" - TOOLBAR_PERF_CONF = "配置" - TOOLBAR_PERF_PB = "协议" + TOOLBAR_PERF_TIP = "开始" + TOOLBAR_PERF_CONF = "配置" + TOOLBAR_PERF_LOGIN = "登陆" + TOOLBAR_PERF_CREATE = "创角" + TOOLBAR_PERF_PB = "协议" + TOOLBAR_PERF_RES = "结果" //monitor APP_MONITOR_TITLE_ID = "编号" diff --git a/cmd/v2/lib/common/utils.go b/cmd/v2/lib/common/utils.go index 15e779eca..c5b59d918 100644 --- a/cmd/v2/lib/common/utils.go +++ b/cmd/v2/lib/common/utils.go @@ -3,6 +3,7 @@ package common import ( "bytes" "encoding/json" + "fmt" "io" "io/ioutil" "math" @@ -172,11 +173,63 @@ func Loader(file string) ([]map[string]interface{}, error) { } } -func Json2Pb(jsonString string, pb proto.Message) { +func Json2Pb(jsonString string, pb proto.Message) error { m := pb.ProtoReflect().Interface() - protojson.Unmarshal([]byte(jsonString), m) + if err := protojson.Unmarshal([]byte(jsonString), m); err != nil { + return err + } + return nil } func Pb2Json(pb proto.Message) string { return protojson.Format(pb.ProtoReflect().Interface()) } + +// 保留两位小数 通用 +func FormatFloatCommon(num float64) float64 { + value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", num), 64) + return value +} + +// 保留两位小数,舍弃尾数,无进位运算 +// 主要逻辑就是先乘,trunc之后再除回去,就达到了保留N位小数的效果 +func FormatFloat(num float64, decimal int) (float64, error) { + // 默认乘1 + d := float64(1) + if decimal > 0 { + // 10的N次方 + d = math.Pow10(decimal) + } + // math.trunc作用就是返回浮点数的整数部分 + // 再除回去,小数点后无效的0也就不存在了 + res := strconv.FormatFloat(math.Trunc(num*d)/d, 'f', -1, 64) + return strconv.ParseFloat(res, 64) +} + +// 强制舍弃尾数 +func FormatFloatFloor(num float64, decimal int) (float64, error) { + // 默认乘1 + d := float64(1) + if decimal > 0 { + // 10的N次方 + d = math.Pow10(decimal) + } + // math.trunc作用就是返回浮点数的整数部分 + // 再除回去,小数点后无效的0也就不存在了 + res := strconv.FormatFloat(math.Floor(num*d)/d, 'f', -1, 64) + return strconv.ParseFloat(res, 64) +} + +// 舍弃的尾数不为0,强制进位 +func FormatFloatCeil(num float64, decimal int) (float64, error) { + // 默认乘1 + d := float64(1) + if decimal > 0 { + // 10的N次方 + d = math.Pow10(decimal) + } + // math.trunc作用就是返回浮点数的整数部分 + // 再除回去,小数点后无效的0也就不存在了 + res := strconv.FormatFloat(math.Ceil(num*d)/d, 'f', -1, 64) + return strconv.ParseFloat(res, 64) +} diff --git a/cmd/v2/lib/common/utils_test.go b/cmd/v2/lib/common/utils_test.go index fabe6f840..aacb1f40e 100644 --- a/cmd/v2/lib/common/utils_test.go +++ b/cmd/v2/lib/common/utils_test.go @@ -4,8 +4,9 @@ import ( "fmt" "go_dreamfactory/pb" "reflect" - "strings" + "strconv" "testing" + "time" ) func TestSubStr(t *testing.T) { @@ -33,15 +34,24 @@ func TestJsonToPb(t *testing.T) { } func TestSpli(t *testing.T) { - pbName := "UserLoginReq" - route := "user.login" - if strings.HasSuffix(pbName, "Req") { - s := strings.SplitN(route, ".", 2) - low := strings.ToLower(pbName) - if strings.Contains(low, s[0]) && - strings.Contains(low, s[1]) { - - } - } - + now := time.Now() + fmt.Println(now.UnixNano()) + fmt.Println(now.UnixNano() / 1e6) //将纳秒转换为毫秒 +} + +func TestDiv(t *testing.T) { + numF := float64(32766600) + // 保留两位小数, 通用 + value, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", numF/1e6), 64) + fmt.Println(reflect.TypeOf(value), value) + fmt.Println(FormatFloatCommon(numF / 1e6)) + + num, _ := FormatFloat(numF, 2) + fmt.Println(reflect.TypeOf(num), num) + // 舍弃的尾数不为0,强制进位 + num, _ = FormatFloatCeil(0.2205, 2) + fmt.Println(reflect.TypeOf(num), num) + // 强制舍弃尾数 + num, _ = FormatFloatFloor(0.2295, 2) + fmt.Println(reflect.TypeOf(num), num) } diff --git a/cmd/v2/lib/handler.go b/cmd/v2/lib/handler.go index 373a9b6bf..7fb8a950f 100644 --- a/cmd/v2/lib/handler.go +++ b/cmd/v2/lib/handler.go @@ -22,3 +22,12 @@ type CallResult struct { Message string Elapse time.Duration } + +type Statistics struct { + ElapseTotal float64 //总耗时 + MaxElapse float64 //最大耗时 + MinElapse float64 //最小耗时 + AvgElapse float64 //平均耗时 + CallCount int64 //调用次数 + Route string //协议名称 +} diff --git a/cmd/v2/lib/param.go b/cmd/v2/lib/param.go index 0c91d40a9..5a9aa8c0a 100644 --- a/cmd/v2/lib/param.go +++ b/cmd/v2/lib/param.go @@ -52,6 +52,6 @@ func (param *ParamMgr) Check() error { buf.WriteString(fmt.Sprintf("通过. (timeoutMS=%s, lps=%d, durationS=%s)", param.Timeout, param.Lps, param.Duration)) - logrus.Infoln(buf.String()) + logrus.Debugln(buf.String()) return nil } diff --git a/cmd/v2/lib/storage/config.go b/cmd/v2/lib/storage/config.go index 759770ce0..8a7eda7c9 100644 --- a/cmd/v2/lib/storage/config.go +++ b/cmd/v2/lib/storage/config.go @@ -15,7 +15,9 @@ func newDefaultConfig() *Config { type Config struct { Pressure PressureConfig `json:"Pressure,omitempty"` UserCount int32 `json:"UserCount,omitempty"` //用户数 + SId string `json:"sid,omitempty"` //区服ID WsAddr string `json:"wsAddr,omitempty"` //websocket addr + IntervalS int32 `json:"intervalS,omitempty"` //间隔时间s } //压测配置 diff --git a/cmd/v2/main.go b/cmd/v2/main.go index feaf4ba7d..2f528f882 100644 --- a/cmd/v2/main.go +++ b/cmd/v2/main.go @@ -99,7 +99,7 @@ func main() { }), widget.NewButton("自动化测试", func() { perfWindow := ui.NewPerfWindow(appUI, w) - perfWindow.CreateWindow(common.APP_NAME, 600, 800, true) + perfWindow.CreateWindow(common.APP_NAME, 800, 600, true) w.Hide() }), )) diff --git a/cmd/v2/service/assistant_test.go b/cmd/v2/service/assistant_test.go index b34b50418..e716ea73d 100644 --- a/cmd/v2/service/assistant_test.go +++ b/cmd/v2/service/assistant_test.go @@ -2,26 +2,60 @@ package service import ( "go_dreamfactory/cmd/v2/lib" + "go_dreamfactory/cmd/v2/lib/common" + "go_dreamfactory/comm" + "go_dreamfactory/pb" "testing" "time" + + "github.com/Pallinder/go-randomdata" + "google.golang.org/protobuf/proto" ) +func loginReq() ([]byte, error) { + head := &pb.UserMessage{MainType: "user", SubType: "login"} + sid := "dfz" + account := randomdata.SillyName() + head.Sec = common.BuildSecStr(sid, account) + if comm.ProtoMarshal(&pb.UserLoginReq{ + Sid: sid, + Account: account, + }, head) { + data, err := proto.Marshal(head) + if err != nil { + return nil, err + } + + return data, nil + } + return nil, nil +} + func TestStart(t *testing.T) { - wsAddr := "ws://10.0.5.215:7891/gateway" + wsAddr := "ws://10.0.0.238:7891/gateway" + h, err := NewWsCli(wsAddr, 2*time.Second) + if err != nil { + panic(err) + } + + b, err := loginReq() + if err != nil { + t.Fatal(err) + } + h.SetReq(b) param := lib.ParamMgr{ - Caller: NewWsCli(wsAddr, 2*time.Second), - Timeout: 2 * time.Second, - Lps: uint32(10), - Duration: 3 * time.Second, + Caller: h, + Timeout: 50 * time.Millisecond, + Lps: uint32(1), + Duration: 5 * time.Second, ResultCh: make(chan *lib.CallResult, 50), } - a, err := lib.NewAssistant(param) + a, err := lib.NewAssistant(nil, param) if err != nil { t.Fatalf("AI助手初始化错误: %v", err) t.FailNow() } - t.Log("AI助手启动...") a.Start() a.ShowResult() diff --git a/cmd/v2/service/observer/event.go b/cmd/v2/service/observer/event.go index 686c4a8d9..e723fc363 100644 --- a/cmd/v2/service/observer/event.go +++ b/cmd/v2/service/observer/event.go @@ -14,4 +14,8 @@ const ( EVENT_UI_CLEAN Event = "uiclean" // 请求响应计时 EVENT_RST = "ctime" + + // 测试结果 + EVENT_RESULT = "result" + EVENT_FINISH = "finish" ) diff --git a/cmd/v2/service/wsCli.go b/cmd/v2/service/wsCli.go index 19d936384..37c1f51e4 100644 --- a/cmd/v2/service/wsCli.go +++ b/cmd/v2/service/wsCli.go @@ -8,7 +8,6 @@ import ( "strings" "time" - "github.com/Pallinder/go-randomdata" "github.com/gorilla/websocket" "github.com/sirupsen/logrus" "google.golang.org/protobuf/proto" @@ -16,64 +15,48 @@ import ( type WsCli struct { addr string - ws *websocket.Conn + conn *websocket.Conn reqData []byte //请求数据 + uid string } -func NewWsCli(addr string, timeout time.Duration) lib.Handler { +func NewWsCli(addr string, timeout time.Duration) (lib.Handler, error) { cli := &WsCli{addr: addr} - cli.connect(timeout) - return cli + if err := cli.connect(timeout); err != nil { + return nil, err + } + return cli, nil } func (cli *WsCli) connect(timeout time.Duration) error { - if cli.ws == nil { + if cli.conn == nil { dialer := &websocket.Dialer{ - HandshakeTimeout: timeout, + HandshakeTimeout: 2 * time.Second, } conn, _, err := dialer.Dial(cli.addr, nil) if err != nil { logrus.Errorf("websocket conn err:%v", err) return err } - cli.ws = conn + cli.conn = conn } // ping - go func() { - timer := time.NewTimer(2 * time.Second) - for { - timer.Reset(2 * time.Second) - <-timer.C - if err := cli.ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { - break - } - } - }() + // go func() { + // timer := time.NewTimer(2 * time.Second) + // for { + // timer.Reset(2 * time.Second) + // <-timer.C + // if err := cli.ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { + // break + // } + // } + // }() return nil } -func (cli *WsCli) loginReq() ([]byte, error) { - head := &pb.UserMessage{MainType: "user", SubType: "login"} - sid := "dfz" - account := randomdata.SillyName() - head.Sec = common.BuildSecStr(sid, account) - if comm.ProtoMarshal(&pb.UserLoginReq{ - Sid: sid, - Account: account, - }, head) { - data, err := proto.Marshal(head) - if err != nil { - return nil, err - } - - return data, nil - } - return nil, nil -} - // 检查登录相应 -func (cli *WsCli) checkLoginResp(data []byte) bool { +func (cli *WsCli) checkResp(data []byte) bool { msg := &pb.UserMessage{} if err := proto.Unmarshal(data, msg); err != nil { logrus.Error("结果解析失败") @@ -89,10 +72,15 @@ func (cli *WsCli) checkLoginResp(data []byte) bool { if rsp.Data != nil { if rsp.Data.Uid != "" { logrus.WithField("uid", rsp.Data.Uid).Debug("登录响应") + cli.uid = rsp.Data.Uid return true } } return true + } else { + if cli.uid != "" { + return true + } } return false } @@ -131,23 +119,21 @@ func (cli *WsCli) BuildReq() lib.RawReq { func (cli *WsCli) Call(req []byte) ([]byte, error) { // 向连接写数据 - cli.ws.WriteMessage(websocket.BinaryMessage, req) + cli.conn.WriteMessage(websocket.BinaryMessage, req) // 读数据 var res []byte for { - _, data, err := cli.ws.ReadMessage() + _, data, err := cli.conn.ReadMessage() if err != nil { logrus.Errorf("readMessage err:%v", err) break } - if cli.checkLoginResp(data) { + if cli.checkResp(data) { return data, nil } else { - if !cli.checkPush(data) { - logrus.Debug("登录失败") - } + cli.checkPush(data) } } @@ -159,12 +145,12 @@ func (cli *WsCli) Check(req lib.RawReq, resp lib.RawResp) *lib.CallResult { result.Id = resp.ID result.Req = req result.Resp = resp - //TODO 解析结果 + msg := &pb.UserMessage{} if err := proto.Unmarshal(resp.Resp, msg); err != nil { logrus.Error("结果解析失败") return &result } - logrus.WithFields(logrus.Fields{"msg": msg}).Debug("检查结果") + // logrus.WithFields(logrus.Fields{"msg": msg}).Debug("检查结果") return &result } diff --git a/cmd/v2/ui/app_interface.go b/cmd/v2/ui/app_interface.go index 4c31fcd59..2d47cfcbe 100644 --- a/cmd/v2/ui/app_interface.go +++ b/cmd/v2/ui/app_interface.go @@ -38,6 +38,7 @@ var ( &perfWelcome{}, &perfConf{}, &perfPb{}, + &perfResult{}, } ) diff --git a/cmd/v2/ui/perf_conf.go b/cmd/v2/ui/perf_conf.go index 7534b3e29..ffd6806d8 100644 --- a/cmd/v2/ui/perf_conf.go +++ b/cmd/v2/ui/perf_conf.go @@ -8,6 +8,7 @@ import ( "fyne.io/fyne/v2" "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" "github.com/sirupsen/logrus" @@ -50,15 +51,37 @@ func (app *perfConf) LazyInit(ptService service.PttService, obs observer.Observe userCountEntry.PlaceHolder = "自动创建的用户数" userCountEntry.Text = cast.ToString(app.conf.UserCount) + sidEntry := widget.NewEntry() + sidEntry.PlaceHolder = "区服ID" + sidEntry.Text = app.conf.SId + + intervalEntry := widget.NewEntry() + intervalEntry.PlaceHolder = "间隔时间(s)" + intervalEntry.Text = cast.ToString(app.conf.IntervalS) + form := widget.NewForm( + widget.NewFormItem("服务地址", wsAddrEntry), + widget.NewFormItem("区服", sidEntry), + widget.NewFormItem("用户数", userCountEntry), widget.NewFormItem("超时(ms)", timeoutEntry), widget.NewFormItem("并发量", lpsEntry), widget.NewFormItem("持续时间(s)", durationEntry), - widget.NewFormItem("用户数", userCountEntry), - widget.NewFormItem("服务地址", wsAddrEntry), + widget.NewFormItem("间隔时间(s)", intervalEntry), ) - form.OnSubmit = func() { + // btn + nextBtn := widget.NewButtonWithIcon("下一步", theme.NavigateNextIcon(), nil) + nextBtn.OnTapped = func() { //校验表单数据 + if wsAddrEntry.Text == "" { + common.ShowTip("服务地址必填") + return + } + + if sidEntry.Text == "" { + common.ShowTip("区服ID必填") + return + } + if timeoutEntry.Text == "" { common.ShowTip("超时时间必填") return @@ -85,25 +108,35 @@ func (app *perfConf) LazyInit(ptService service.PttService, obs observer.Observe app.conf.Pressure = pressure app.conf.UserCount = cast.ToInt32(userCountEntry.Text) app.conf.WsAddr = wsAddrEntry.Text + app.conf.SId = sidEntry.Text if err := perfWin.UIImpl.storage.StoreConfig(app.conf); err != nil { logrus.Error(err) } //next + defer closeApp3(perfWin.tabs, common.TOOLBAR_PERF_CONF) openApp3(perfWin.tabs, common.TOOLBAR_PERF_PB) } - form.SubmitText = "下一步" - form.OnCancel = func() { + + resetBtn := widget.NewButtonWithIcon("重置", theme.ContentRedoIcon(), nil) + resetBtn.OnTapped = func() { timeoutEntry.Text = "" lpsEntry.Text = "" durationEntry.Text = "" userCountEntry.Text = "" wsAddrEntry.Text = "" + sidEntry.Text = "" form.Refresh() } - form.CancelText = "重置" - content.Objects = append(content.Objects, form) + preBtn := widget.NewButtonWithIcon("上一步", theme.NavigateBackIcon(), nil) + preBtn.OnTapped = func() { + defer closeApp3(perfWin.tabs, common.TOOLBAR_PERF_CONF) + openApp3(perfWin.tabs, common.TOOLBAR_PERF_TIP) + } + + c := container.NewBorder(nil, container.NewHBox(layout.NewSpacer(), preBtn, resetBtn, nextBtn), nil, nil, form) + content.Objects = append(content.Objects, c) app.tabItem.Content = content return nil } @@ -111,3 +144,11 @@ func (app *perfConf) LazyInit(ptService service.PttService, obs observer.Observe func (a *perfConf) GetAppName() string { return common.TOOLBAR_PERF_CONF } + +func (a *perfConf) OnClose() bool { + return false +} + +func (a *perfConf) OnDestroy() bool { + return true +} diff --git a/cmd/v2/ui/perf_create.go b/cmd/v2/ui/perf_create.go new file mode 100644 index 000000000..bc2450c52 --- /dev/null +++ b/cmd/v2/ui/perf_create.go @@ -0,0 +1,38 @@ +package ui + +import ( + "go_dreamfactory/cmd/v2/lib/common" + "go_dreamfactory/cmd/v2/lib/storage" + "go_dreamfactory/cmd/v2/service" + "go_dreamfactory/cmd/v2/service/observer" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" +) + +type perfCreate struct { + appAdapter + + obs observer.Observer + conf *storage.Config +} + +func (app *perfCreate) LazyInit(ptService service.PttService, obs observer.Observer) error { + app.obs = obs + app.conf = perfWin.UIImpl.config + + app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_PERF_CONF, theme.ContentCopyIcon(), nil) + content := container.NewMax() + content.Objects = []fyne.CanvasObject{} + + return nil +} + +func (a *perfCreate) GetAppName() string { + return common.TOOLBAR_PERF_CREATE +} + +func (a *perfCreate) OnClose() bool { + return false +} diff --git a/cmd/v2/ui/perf_login.go b/cmd/v2/ui/perf_login.go new file mode 100644 index 000000000..0a6715907 --- /dev/null +++ b/cmd/v2/ui/perf_login.go @@ -0,0 +1,49 @@ +package ui + +import ( + "go_dreamfactory/cmd/v2/lib/common" + "go_dreamfactory/cmd/v2/lib/storage" + "go_dreamfactory/cmd/v2/service" + "go_dreamfactory/cmd/v2/service/observer" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" +) + +type perfLogin struct { + appAdapter + + obs observer.Observer + conf *storage.Config +} + +func (app *perfLogin) LazyInit(ptService service.PttService, obs observer.Observer) error { + app.obs = obs + app.conf = perfWin.UIImpl.config + + app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_PERF_CONF, theme.ContentCopyIcon(), nil) + content := container.NewMax() + content.Objects = []fyne.CanvasObject{} + + loginTestBtn := widget.NewButton("登陆/注册", func() { + + }) + + createTestBtn := widget.NewButton("创角", func() {}) + + btns := container.NewHBox(loginTestBtn, createTestBtn) + c := container.NewBorder(btns, nil, nil, nil) + content.Objects = append(content.Objects, c) + app.tabItem.Content = content + return nil +} + +func (a *perfLogin) GetAppName() string { + return common.TOOLBAR_PERF_LOGIN +} + +func (a *perfLogin) OnClose() bool { + return false +} diff --git a/cmd/v2/ui/perf_pb.go b/cmd/v2/ui/perf_pb.go index d5695b280..82f8680f4 100644 --- a/cmd/v2/ui/perf_pb.go +++ b/cmd/v2/ui/perf_pb.go @@ -10,6 +10,7 @@ import ( "go_dreamfactory/cmd/v2/service/observer" "go_dreamfactory/comm" "go_dreamfactory/pb" + "strings" "time" "fyne.io/fyne/v2" @@ -68,53 +69,86 @@ func (app *perfPb) LazyInit(ptService service.PttService, obs observer.Observer) }) // next按钮 - nextBtn := widget.NewButtonWithIcon("执行", theme.ConfirmIcon(), func() { + nextBtn := widget.NewButtonWithIcon("下一步", theme.NavigateNextIcon(), nil) + nextBtn.OnTapped = func() { + defer closeApp3(perfWin.tabs, common.TOOLBAR_PERF_PB) + openApp3(perfWin.tabs, common.TOOLBAR_PERF_RES) // 根据填写的用户数创建用户 userCount := perfWin.config.UserCount // 遍历时,通过sleep 控制增加的用户数 for i := int32(0); i < userCount; i++ { - handler := service.NewWsCli(perfWin.config.WsAddr, time.Duration(perfWin.config.Pressure.TimeoutMs)) + handler, err := service.NewWsCli(perfWin.config.WsAddr, time.Duration(perfWin.config.Pressure.TimeoutMs)*time.Millisecond) + if err != nil { + continue + } + var login *UserLogin + login, err = app.loginReq(perfWin.config.SId) + handler.SetReq(login.req) + assist := app.createAssistantWithoutConf(handler) + assist.Start() + assist.ShowResult() // 遍历测试的协议 for _, item := range app.itemList.CachedList.Items { if data, ok := item.Data.(*cfg.GameTestFlowData); ok { - logrus.Infof("%v %v", data.Route, data.Params) + logrus.Debugf("%v %v", data.Route, data.Params) var ( reqData []byte err error ) - if data.Route == "user.login" { - reqData, err = app.loginReq() - if err != nil { + // if data.Route == "user.login" { + // login, err = app.loginReq() + // if err != nil { + // logrus.Error(err) + // continue + // } + // reqData = login.req + // } else { + if login == nil { + continue + } + if v, ok := pbMap[data.Route]; ok { + routeStr := strings.SplitN(data.Route, ".", 2) + head := &pb.UserMessage{MainType: routeStr[0], SubType: routeStr[1]} + head.Sec = common.BuildSecStr(login.sid, login.account) + if err := common.Json2Pb(data.Params, v); err != nil { logrus.Error(err) continue } - - } else { - if pb, ok := pbMap[data.Route]; ok { - common.Json2Pb(data.Params, pb) - reqData, err = proto.Marshal(pb) + if comm.ProtoMarshal(v, head) { + reqData, err = proto.Marshal(head) if err != nil { logrus.Error(err) continue } } + } + // } + handler.SetReq(reqData) assist := app.createAssistant(handler) assist.Start() assist.ShowResult() + } } // time.Sleep(time.Second) } + logrus.Info("所有用户执行完毕...") + obs.Notify(observer.EVENT_FINISH, true) + } - }) + preBtn := widget.NewButtonWithIcon("上一步", theme.NavigateBackIcon(), nil) + preBtn.OnTapped = func() { + defer closeApp3(perfWin.tabs, common.TOOLBAR_PERF_PB) + openApp3(perfWin.tabs, common.TOOLBAR_PERF_CONF) + } //layout - c := container.NewBorder(container.NewHBox(refeshBtn), container.NewHBox(layout.NewSpacer(), nextBtn), nil, nil, app.itemList.ItemList) + c := container.NewBorder(container.NewHBox(refeshBtn), container.NewHBox(layout.NewSpacer(), preBtn, nextBtn), nil, nil, app.itemList.ItemList) content.Objects = append(content.Objects, c) app.tabItem.Content = content return nil @@ -124,6 +158,27 @@ func (a *perfPb) GetAppName() string { return common.TOOLBAR_PERF_PB } +func (a *perfPb) OnClose() bool { + return false +} + +func (a *perfPb) createAssistantWithoutConf(handler lib.Handler) lib.Aiassistant { + + param := lib.ParamMgr{ + Caller: handler, + Timeout: time.Duration(a.conf.Pressure.TimeoutMs) * time.Millisecond, + Lps: 1, + Duration: 1, + ResultCh: make(chan *lib.CallResult, 50), + } + assist, err := lib.NewAssistant(a.obs, param) + if err != nil { + logrus.Errorf("AI助手初始化错误: %v", err) + return nil + } + return assist +} + // func (a *perfPb) createAssistant(handler lib.Handler) lib.Aiassistant { @@ -134,7 +189,7 @@ func (a *perfPb) createAssistant(handler lib.Handler) lib.Aiassistant { Duration: time.Duration(a.conf.Pressure.DurationS) * time.Second, ResultCh: make(chan *lib.CallResult, 50), } - assist, err := lib.NewAssistant(param) + assist, err := lib.NewAssistant(a.obs, param) if err != nil { logrus.Errorf("AI助手初始化错误: %v", err) return nil @@ -142,21 +197,30 @@ func (a *perfPb) createAssistant(handler lib.Handler) lib.Aiassistant { return assist } -func (a *perfPb) loginReq() ([]byte, error) { +type UserLogin struct { + req []byte + sid string + account string +} + +func (a *perfPb) loginReq(sid string) (*UserLogin, error) { + login := &UserLogin{sid: sid} head := &pb.UserMessage{MainType: "user", SubType: "login"} - sid := "dfz" account := randomdata.SillyName() - head.Sec = common.BuildSecStr(sid, account) + login.account = account + head.Sec = common.BuildSecStr(login.sid, login.account) if comm.ProtoMarshal(&pb.UserLoginReq{ - Sid: sid, - Account: account, + Sid: login.sid, + Account: login.account, }, head) { + logrus.WithField("账号", login.account).Info("登录") data, err := proto.Marshal(head) if err != nil { return nil, err } + login.req = data - return data, nil + return login, nil } return nil, nil } diff --git a/cmd/v2/ui/perf_result.go b/cmd/v2/ui/perf_result.go new file mode 100644 index 000000000..904be05f3 --- /dev/null +++ b/cmd/v2/ui/perf_result.go @@ -0,0 +1,103 @@ +package ui + +import ( + "fmt" + "go_dreamfactory/cmd/v2/lib" + "go_dreamfactory/cmd/v2/lib/common" + "go_dreamfactory/cmd/v2/lib/storage" + "go_dreamfactory/cmd/v2/service" + "go_dreamfactory/cmd/v2/service/observer" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/layout" + "fyne.io/fyne/v2/theme" + "fyne.io/fyne/v2/widget" +) + +type perfResult struct { + appAdapter + + obs observer.Observer + conf *storage.Config + + itemList common.ItemList + + resultList func() //结果列表 + resetBtn *widget.Button + report *widget.Card +} + +func (app *perfResult) LazyInit(ptService service.PttService, obs observer.Observer) error { + app.obs = obs + app.conf = perfWin.UIImpl.config + + app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_PERF_RES, theme.ContentCopyIcon(), nil) + content := container.NewMax() + content.Objects = []fyne.CanvasObject{} + + app.itemList = *common.NewItemList() + app.itemList.ItemList = app.itemList.CreateList() + + //重新开始 + app.resetBtn = widget.NewButtonWithIcon("再来一次", theme.ContentRedoIcon(), nil) + app.resetBtn.Hide() + app.resetBtn.OnTapped = func() { + defer openApp3(perfWin.tabs, common.TOOLBAR_PERF_TIP) + app.itemList.Reset() + closeApp3(perfWin.tabs, common.TOOLBAR_PERF_RES) + } + + //统计Panel + app.report = widget.NewCard("测试报告", "登录/创角", container.NewVBox( + // widget.NewLabel("结果:"), + )) + app.report.Hide() + //layout + c := container.NewBorder(app.report, container.NewHBox(layout.NewSpacer(), app.resetBtn), nil, nil, app.itemList.ItemList) + content.Objects = append(content.Objects, c) + app.tabItem.Content = content + app.listen() + return nil +} + +func (app *perfResult) listen() { + app.obs.AddListener(observer.EVENT_RESULT, observer.Listener{ + OnNotify: func(data interface{}, args ...interface{}) { + res, ok := data.(*lib.Statistics) + if !ok { + return + } + item := common.Item{ + Text: fmt.Sprintf("协议名称:%s, 调用次数:%d, 总耗时:%vms, 平均耗时:%vms, 最大耗时:%vms, 最小耗时:%vms", + res.Route, res.CallCount, res.ElapseTotal, res.AvgElapse, res.MaxElapse, res.MinElapse), + Data: res, + } + app.itemList.AddItem(item) + }, + }) + + app.obs.AddListener(observer.EVENT_FINISH, observer.Listener{ + OnNotify: func(data interface{}, args ...interface{}) { + finish, ok := data.(bool) + if !ok { + return + } + + if finish { + app.resetBtn.Show() + app.resetBtn.Refresh() + app.report.Show() + app.report.Refresh() + } + }, + }) +} + +func (app *perfResult) GetAppName() string { + return common.TOOLBAR_PERF_RES +} + +func (a *perfResult) OnClose() bool { + return false +} diff --git a/cmd/v2/ui/perf_welcome.go b/cmd/v2/ui/perf_welcome.go index 64521ce14..ce6741293 100644 --- a/cmd/v2/ui/perf_welcome.go +++ b/cmd/v2/ui/perf_welcome.go @@ -34,9 +34,11 @@ func (app *perfWelcome) LazyInit(service service.PttService, obs observer.Observ } } - goBtn := widget.NewButton("开始测试 >>", func() { + goBtn := widget.NewButton("开始测试 >>", nil) + goBtn.OnTapped = func() { + defer closeApp3(perfWin.tabs, common.TOOLBAR_PERF_TIP) openApp3(perfWin.tabs, common.TOOLBAR_PERF_CONF) - }) + } app.tabItem.Content = container.NewCenter( container.NewVBox( wel, @@ -56,3 +58,7 @@ func (a *perfWelcome) GetAppName() string { func (a *perfWelcome) OnClose() bool { return false } + +func (a *perfWelcome) OnDestroy() bool { + return true +} diff --git a/cmd/v2/ui/perfwindow.go b/cmd/v2/ui/perfwindow.go index f84ef16c7..22c5b344e 100644 --- a/cmd/v2/ui/perfwindow.go +++ b/cmd/v2/ui/perfwindow.go @@ -24,11 +24,11 @@ type PerfWindow interface { type PerfWindowImpl struct { UIImpl - parent fyne.Window - w fyne.Window - statusbar *statusBar //状态栏 - tabs *appContainer //tabs - toolbar *toolBar //工具条 + parent fyne.Window + w fyne.Window + statusbar *statusBar //状态栏 + tabs *appContainer //tabs + toolbar *toolBar //工具条 } func NewPerfWindow(ui *UIImpl, parent fyne.Window) PerfWindow { @@ -102,6 +102,6 @@ func (ui *PerfWindowImpl) registerPb(route string, pb proto.Message) { func (ui *PerfWindowImpl) initPb() { ui.registerPb("user.login", &pb.UserLoginReq{}) - ui.registerPb("user.login", &pb.UserCreateReq{}) + ui.registerPb("user.create", &pb.UserCreateReq{}) ui.registerPb("sys.funclist", &pb.SysFuncListReq{}) } diff --git a/cmd/v2/ui/tool_bar.go b/cmd/v2/ui/tool_bar.go index c4e10db48..1387cfd0f 100644 --- a/cmd/v2/ui/tool_bar.go +++ b/cmd/v2/ui/tool_bar.go @@ -42,8 +42,6 @@ func openApp2(ac *appContainer, name string) { } } - - func openApp3(ac *appContainer, name string) { for _, app := range perfRegister { if app.GetAppName() == name { @@ -55,4 +53,10 @@ func openApp3(ac *appContainer, name string) { } } - +func closeApp3(ac *appContainer, name string) { + for _, appItem := range ac.Items { + if appItem.Text == name { + ac.Remove(appItem) + } + } +}