diff --git a/cmd/v2/.DS_Store b/cmd/v2/.DS_Store new file mode 100644 index 000000000..f0f0bfbdd Binary files /dev/null and b/cmd/v2/.DS_Store differ diff --git a/cmd/v2/lib/assistant.go b/cmd/v2/lib/assistant.go index 2e4f48f61..e19c1e3e6 100644 --- a/cmd/v2/lib/assistant.go +++ b/cmd/v2/lib/assistant.go @@ -126,6 +126,9 @@ func (a *assistant) asyncCall() { }() req := a.caller.BuildReq() + if req.ID == 0 { + return + } //调用状态 0未调用 1调用结束 2调用超时 var callStatus uint32 @@ -180,7 +183,7 @@ func (a *assistant) prepareStop(ctxErr error) { } // 发送请求即调用接口 -func (a *assistant) handleReq(tick <-chan time.Time) { +func (a *assistant) handleReq(throttle <-chan time.Time) { for { select { case <-a.ctx.Done(): @@ -191,7 +194,7 @@ func (a *assistant) handleReq(tick <-chan time.Time) { a.asyncCall() if a.lps > 0 { select { - case <-tick: + case <-throttle: case <-a.ctx.Done(): a.prepareStop(a.ctx.Err()) return @@ -226,12 +229,12 @@ func (a *assistant) Start() bool { logrus.Debug("AI助手启动") // 节流 周期性向目标发送 - var ticker <-chan time.Time + var throttle <-chan time.Time if a.lps > 0 { //间隔时间 interval := time.Duration(1e9 / a.lps) logrus.Debugf("启动节流控制 间隔: %v", interval) - ticker = time.Tick(interval) + throttle = time.Tick(interval) } // 初始化上下文和设置取消函数 @@ -245,7 +248,7 @@ func (a *assistant) Start() bool { go func() { logrus.Debug("请求处理中...") - a.handleReq(ticker) + a.handleReq(throttle) logrus.Infof("停止 调用次数:%d", a.callCount) }() diff --git a/cmd/v2/lib/common/lang.go b/cmd/v2/lib/common/lang.go index 435f0b922..5f325b8d8 100644 --- a/cmd/v2/lib/common/lang.go +++ b/cmd/v2/lib/common/lang.go @@ -101,6 +101,7 @@ const ( TOOLBAR_PERF_CONF = "配置" TOOLBAR_PERF_LOGIN = "登陆" TOOLBAR_PERF_CREATE = "创角" + TOOLBAR_PERF_CHOOSE = "选项" TOOLBAR_PERF_PB = "协议" TOOLBAR_PERF_RES = "结果" diff --git a/cmd/v2/lib/handler.go b/cmd/v2/lib/handler.go index 7fb8a950f..1aa54ac65 100644 --- a/cmd/v2/lib/handler.go +++ b/cmd/v2/lib/handler.go @@ -1,16 +1,21 @@ package lib -import "time" +import ( + "sync" + "time" +) // 处理器接口 type Handler interface { - SetReq(req []byte) + SetReq(req []byte, flag bool) // 处理请求 BuildReq() RawReq //调用 Call(req []byte) ([]byte, error) // 检查响应 Check(req RawReq, rsp RawResp) *CallResult + + GetConnMap() sync.Map } // 调用结果 @@ -24,10 +29,10 @@ type CallResult struct { } type Statistics struct { - ElapseTotal float64 //总耗时 - MaxElapse float64 //最大耗时 - MinElapse float64 //最小耗时 - AvgElapse float64 //平均耗时 - CallCount int64 //调用次数 - Route string //协议名称 + ElapseTotal float64 //总耗时 + MaxElapse float64 //最大耗时 + MinElapse float64 //最小耗时 + AvgElapse float64 //平均耗时 + CallCount int64 //调用次数 + Route string //协议名称 } diff --git a/cmd/v2/lib/quen.go b/cmd/v2/lib/quen.go new file mode 100644 index 000000000..b494dbddb --- /dev/null +++ b/cmd/v2/lib/quen.go @@ -0,0 +1,56 @@ +package lib + +import ( + "fmt" + "sync" +) + +type Queue[T any] struct { + data []T + lock sync.RWMutex +} + +func NewQueue[T any]() *Queue[T] { + return &Queue[T]{data: make([]T, 0)} +} + +func (this *Queue[T]) Add(obj T) { + this.lock.Lock() + defer this.lock.Unlock() + this.data = append(this.data, obj) +} + +func (this *Queue[T]) List() []T { + this.lock.RLock() + defer this.lock.RUnlock() + list := make([]T, 0, len(this.data)) + for _, v := range this.data { + list = append(list, v) + } + return list +} + +func (this *Queue[T]) Get(index int) (T, bool) { + this.lock.RLock() + defer this.lock.RUnlock() + + var ret T + if index < 0 || index >= len(this.data) { + return ret, false + } + + return this.data[index], true +} + +func (this *Queue[T]) Pop() (T, error) { + this.lock.Lock() + defer this.lock.Unlock() + var ret T + if len(this.data) == 0 { + return ret, fmt.Errorf("not found") + } + + pop := this.data[0] + this.data = this.data[1:] + return pop, nil +} diff --git a/cmd/v2/main.go b/cmd/v2/main.go index 2f528f882..6e774e6c6 100644 --- a/cmd/v2/main.go +++ b/cmd/v2/main.go @@ -164,7 +164,7 @@ func setupLogger() (err error) { // FullTimestamp: true, }) - logrus.SetLevel(logrus.InfoLevel) + logrus.SetLevel(logrus.DebugLevel) logrus.SetOutput(os.Stdout) //设置output,默认为stderr,可以为任何io.Writer,比如文件*os.File diff --git a/cmd/v2/resources/config.yaml b/cmd/v2/resources/config.yaml index a1f10234a..13ef87505 100644 --- a/cmd/v2/resources/config.yaml +++ b/cmd/v2/resources/config.yaml @@ -1,26 +1,26 @@ upgradeUrl: http://10.0.0.9:8080/ services: - - service: - sid: "df01" - name: 外网 - url: ws://119.3.89.14:9891/gateway - - service: - sid: "dfz" - name: 赵长远 - url: ws://10.0.0.238:7891/gateway - - service: - sid: "df01" - name: 内网 - url: ws://10.0.0.9:7891/gateway - - service: - sid: "dfmxf" - name: 梅雄风 - url: ws://10.0.0.85:7891/gateway - - service: - sid: "dflw" - name: 李伟 - url: ws://10.0.0.85:7891/gateway - - service: - sid: "0" - name: 广告位 - url: \ No newline at end of file + - service: + sid: "df01" + name: 外网 + url: ws://119.3.89.14:9891/gateway + - service: + sid: "dfz" + name: 赵长远 + url: ws://10.0.0.238:7891/gateway + - service: + sid: "df01" + name: 内网 + url: ws://10.0.0.9:7891/gateway + - service: + sid: "dfmxf" + name: 梅雄风 + url: ws://10.0.0.85:7891/gateway + - service: + sid: "dflw" + name: 李伟 + url: ws://10.0.0.85:7891/gateway + - service: + sid: "df01" + name: 压测服 + url: ws://106.54.189.74:7891/gateway diff --git a/cmd/v2/service/assistant_test.go b/cmd/v2/service/assistant_test.go index e716ea73d..b7ac51a1d 100644 --- a/cmd/v2/service/assistant_test.go +++ b/cmd/v2/service/assistant_test.go @@ -42,7 +42,7 @@ func TestStart(t *testing.T) { if err != nil { t.Fatal(err) } - h.SetReq(b) + h.SetReq(b, false) param := lib.ParamMgr{ Caller: h, Timeout: 50 * time.Millisecond, diff --git a/cmd/v2/service/wsCli.go b/cmd/v2/service/wsCli.go index 37c1f51e4..980595c6d 100644 --- a/cmd/v2/service/wsCli.go +++ b/cmd/v2/service/wsCli.go @@ -1,33 +1,48 @@ package service import ( + "encoding/base64" "go_dreamfactory/cmd/v2/lib" "go_dreamfactory/cmd/v2/lib/common" "go_dreamfactory/comm" + "go_dreamfactory/lego/sys/log" "go_dreamfactory/pb" "strings" + "sync" "time" "github.com/gorilla/websocket" "github.com/sirupsen/logrus" + "github.com/tidwall/gjson" "google.golang.org/protobuf/proto" ) type WsCli struct { - addr string - conn *websocket.Conn - reqData []byte //请求数据 - uid string + addr string + conn *websocket.Conn + // reqData []byte //请求数据 + uid string + reqDatas *lib.Queue[[]byte] + conns sync.Map + flag bool + reqData []byte } func NewWsCli(addr string, timeout time.Duration) (lib.Handler, error) { - cli := &WsCli{addr: addr} - if err := cli.connect(timeout); err != nil { - return nil, err + cli := &WsCli{ + addr: addr, + reqDatas: lib.NewQueue[[]byte](), } + // if err := cli.connect(timeout); err != nil { + // return nil, err + // } return cli, nil } +func (cli *WsCli) GetConnMap() sync.Map { + return cli.conns +} + func (cli *WsCli) connect(timeout time.Duration) error { if cli.conn == nil { dialer := &websocket.Dialer{ @@ -55,7 +70,7 @@ func (cli *WsCli) connect(timeout time.Duration) error { return nil } -// 检查登录相应 +// 检查相应 func (cli *WsCli) checkResp(data []byte) bool { msg := &pb.UserMessage{} if err := proto.Unmarshal(data, msg); err != nil { @@ -97,7 +112,12 @@ func (cli *WsCli) checkPush(data []byte) bool { if strings.HasSuffix(methodName, "Push") { if methodName == "NotifyErrorNotifyPush" { - logrus.WithField("methodName", methodName).Debug("收到错误码") + push := &pb.NotifyErrorNotifyPush{} + if !comm.ProtoUnmarshal(msg, push) { + logrus.Error("unmarsh err") + return false + } + logrus.WithField("methodName", methodName).WithField("code", push.Code).Debug("收到错误码") } else { logrus.WithField("methodName", methodName).Debug("收到推送") } @@ -106,35 +126,88 @@ func (cli *WsCli) checkPush(data []byte) bool { return false } -func (cli *WsCli) SetReq(data []byte) { - cli.reqData = data +func (cli *WsCli) SetReq(data []byte, flag bool) { + cli.flag = flag + if flag { + cli.reqData = data + } + cli.reqDatas.Add(data) } func (cli *WsCli) BuildReq() lib.RawReq { id := time.Now().UnixNano() - rawReq := lib.RawReq{ID: id, Req: cli.reqData} + var ( + data []byte + err error + ) + + data, err = cli.reqDatas.Pop() + if err != nil { + if cli.flag { + data = cli.reqData + } else { + return lib.RawReq{} + } + } + + rawReq := lib.RawReq{ID: id, Req: data} return rawReq } func (cli *WsCli) Call(req []byte) ([]byte, error) { + msg := &pb.UserMessage{} + if err := proto.Unmarshal(req, msg); err != nil { + logrus.Error("结果解析失败") + return nil, err + } + + base64Str := msg.Sec + dec, err := base64.StdEncoding.DecodeString(base64Str[35:]) + if err != nil { + log.Errorf("base64 decode err %v", err) + return nil, err + } + jsonRet := gjson.Parse(string(dec)) + + account := jsonRet.Get("account").String() + + dialer := &websocket.Dialer{ + HandshakeTimeout: 2 * time.Second, + } + var conn *websocket.Conn + if c, ok := cli.conns.Load(account); ok { + conn = c.(*websocket.Conn) + } else { + conn, _, err = dialer.Dial(cli.addr, nil) + if err != nil { + logrus.Errorf("websocket conn err:%v", err) + return nil, err + } + } + + if msg.MainType == "user" && msg.SubType == "login" { + cli.conns.Store(account, conn) + } // 向连接写数据 - cli.conn.WriteMessage(websocket.BinaryMessage, req) + conn.WriteMessage(websocket.BinaryMessage, req) // 读数据 var res []byte for { - _, data, err := cli.conn.ReadMessage() + _, data, err := conn.ReadMessage() if err != nil { logrus.Errorf("readMessage err:%v", err) break } - if cli.checkResp(data) { + // if cli.checkResp(data) { + // return data, nil + // } else { + if !cli.checkPush(data) { return data, nil - } else { - cli.checkPush(data) } + // } } return res, nil @@ -145,7 +218,7 @@ func (cli *WsCli) Check(req lib.RawReq, resp lib.RawResp) *lib.CallResult { result.Id = resp.ID result.Req = req result.Resp = resp - + msg := &pb.UserMessage{} if err := proto.Unmarshal(resp.Resp, msg); err != nil { logrus.Error("结果解析失败") diff --git a/cmd/v2/ui/app_interface.go b/cmd/v2/ui/app_interface.go index 2d47cfcbe..19e9f378d 100644 --- a/cmd/v2/ui/app_interface.go +++ b/cmd/v2/ui/app_interface.go @@ -39,6 +39,7 @@ var ( &perfConf{}, &perfPb{}, &perfResult{}, + &perfChoose{}, } ) diff --git a/cmd/v2/ui/perf_choose.go b/cmd/v2/ui/perf_choose.go new file mode 100644 index 000000000..db5e520a7 --- /dev/null +++ b/cmd/v2/ui/perf_choose.go @@ -0,0 +1,226 @@ +package ui + +import ( + cfg "go_dreamfactory/cmd/v2/configure/structs" + "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" + "go_dreamfactory/comm" + "go_dreamfactory/pb" + "strings" + "time" + + "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/Pallinder/go-randomdata" + "github.com/sirupsen/logrus" + "google.golang.org/protobuf/proto" +) + +type perfChoose struct { + appAdapter + + obs observer.Observer + conf *storage.Config + binduids []string //账号 + handler lib.Handler +} + +func (app *perfChoose) LazyInit(ptService service.PttService, obs observer.Observer) error { + app.obs = obs + app.conf = perfWin.UIImpl.config + + app.tabItem = container.NewTabItemWithIcon(common.TOOLBAR_PERF_CHOOSE, theme.ContentCopyIcon(), nil) + content := container.NewMax() + content.Objects = []fyne.CanvasObject{} + + loginTestBtn := widget.NewButton("登陆(注册)", nil) + loginTestBtn.OnTapped = func() { + closeApp3(perfWin.tabs, common.TOOLBAR_PERF_CHOOSE) + openApp3(perfWin.tabs, common.TOOLBAR_PERF_RES) + var err error + + app.handler, err = service.NewWsCli(app.conf.WsAddr, 2*time.Second) + if err != nil { + panic(err) + } + + userCount := app.conf.UserCount + for i := int32(0); i < userCount; i++ { + account := randomdata.SillyName() + app.binduids = append(app.binduids, account) + // 登录 + rqLogin := ReqParams{ + Sid: app.conf.SId, + Account: account, + PbReq: &pb.UserLoginReq{Sid: app.conf.SId, Account: account}, + MainType: "user", + SubType: "login", + } + b, err := app.buildReq(rqLogin) + if err != nil { + logrus.Error(err) + return + } + app.handler.SetReq(b, false) + + } + + param := lib.ParamMgr{ + Caller: app.handler, + Timeout: time.Duration(app.conf.Pressure.TimeoutMs) * time.Millisecond, + Lps: uint32(app.conf.Pressure.Concurrency), + Duration: time.Duration(app.conf.Pressure.DurationS) * time.Second, + ResultCh: make(chan *lib.CallResult, 50), + } + a, err := lib.NewAssistant(obs, param) + if err != nil { + logrus.Error(err) + } + + a.Start() + + a.ShowResult() + } + + createTestBtn := widget.NewButton("创角", func() { + closeApp3(perfWin.tabs, common.TOOLBAR_PERF_CHOOSE) + openApp3(perfWin.tabs, common.TOOLBAR_PERF_RES) + + for _, account := range app.binduids { + rq := ReqParams{ + Sid: app.conf.SId, + Account: account, + PbReq: &pb.UserCreateReq{NickName: account}, + MainType: "user", + SubType: "create", + } + b, err := app.buildReq(rq) + if err != nil { + logrus.Error(err) + return + } + app.handler.SetReq(b, false) + } + + param := lib.ParamMgr{ + Caller: app.handler, + Timeout: time.Duration(app.conf.Pressure.TimeoutMs) * time.Millisecond, + Lps: uint32(app.conf.Pressure.Concurrency), + Duration: time.Duration(app.conf.Pressure.DurationS) * time.Second, + ResultCh: make(chan *lib.CallResult, 50), + } + a, err := lib.NewAssistant(obs, param) + if err != nil { + logrus.Error(err) + } + + a.Start() + + a.ShowResult() + }) + + //场景 + scenBtn := widget.NewButton("其它场景", nil) + scenBtn.OnTapped = func() { + // defer openApp3(perfWin.tabs, common.TOOLBAR_PERF_PB) + // closeApp3(perfWin.tabs, common.TOOLBAR_PERF_CHOOSE) + closeApp3(perfWin.tabs, common.TOOLBAR_PERF_CHOOSE) + openApp3(perfWin.tabs, common.TOOLBAR_PERF_RES) + + if tables, err := cfg.NewTables(common.Loader); err != nil { + println(err.Error()) + } else { + for _, v := range tables.TestFlow.GetDataList() { + p, ok := pbMap[v.Route] + if !ok { + logrus.WithField("route", v.Route).Debug("未注册") + continue + } + routeStr := strings.SplitN(v.Route, ".", 2) + for _, account := range app.binduids { + rq := ReqParams{ + Sid: app.conf.SId, + Account: account, + PbReq: p, + MainType: routeStr[0], + SubType: routeStr[1], + } + b, err := app.buildReq(rq) + if err != nil { + logrus.Error(err) + return + } + app.handler.SetReq(b, true) + } + + param := lib.ParamMgr{ + Caller: app.handler, + Timeout: time.Duration(app.conf.Pressure.TimeoutMs) * time.Millisecond, + Lps: uint32(app.conf.Pressure.Concurrency), + Duration: time.Duration(app.conf.Pressure.DurationS) * time.Second, + ResultCh: make(chan *lib.CallResult, 50), + } + a, err := lib.NewAssistant(obs, param) + if err != nil { + logrus.Error(err) + } + + a.Start() + + a.ShowResult() + } + } + } + + //上一步 + preBtn := widget.NewButtonWithIcon("上一步", theme.NavigateBackIcon(), nil) + preBtn.OnTapped = func() { + defer openApp3(perfWin.tabs, common.TOOLBAR_PERF_CONF) + closeApp3(perfWin.tabs, common.TOOLBAR_PERF_CHOOSE) + } + btns := container.NewVBox(loginTestBtn, createTestBtn, scenBtn) + c := container.NewBorder(nil, container.NewHBox(layout.NewSpacer(), preBtn), nil, nil, btns) + content.Objects = append(content.Objects, c) + app.tabItem.Content = content + return nil +} + +func (a *perfChoose) GetAppName() string { + return common.TOOLBAR_PERF_CHOOSE +} + +func (a *perfChoose) OnClose() bool { + return false +} + +func (a *perfChoose) OnDestroy() bool { + return true +} + +type ReqParams struct { + Sid string + Account string + PbReq proto.Message + MainType string + SubType string +} + +func (a *perfChoose) buildReq(rp ReqParams) ([]byte, error) { + head := &pb.UserMessage{MainType: rp.MainType, SubType: rp.SubType} + head.Sec = common.BuildSecStr(rp.Sid, rp.Account) + if comm.ProtoMarshal(rp.PbReq, head) { + data, err := proto.Marshal(head) + if err != nil { + return nil, err + } + + return data, nil + } + return nil, nil +} diff --git a/cmd/v2/ui/perf_conf.go b/cmd/v2/ui/perf_conf.go index ffd6806d8..e4153f22e 100644 --- a/cmd/v2/ui/perf_conf.go +++ b/cmd/v2/ui/perf_conf.go @@ -115,7 +115,7 @@ func (app *perfConf) LazyInit(ptService service.PttService, obs observer.Observe //next defer closeApp3(perfWin.tabs, common.TOOLBAR_PERF_CONF) - openApp3(perfWin.tabs, common.TOOLBAR_PERF_PB) + openApp3(perfWin.tabs, common.TOOLBAR_PERF_CHOOSE) } resetBtn := widget.NewButtonWithIcon("重置", theme.ContentRedoIcon(), nil) diff --git a/cmd/v2/ui/perf_pb.go b/cmd/v2/ui/perf_pb.go index 82f8680f4..015525272 100644 --- a/cmd/v2/ui/perf_pb.go +++ b/cmd/v2/ui/perf_pb.go @@ -84,7 +84,7 @@ func (app *perfPb) LazyInit(ptService service.PttService, obs observer.Observer) } var login *UserLogin login, err = app.loginReq(perfWin.config.SId) - handler.SetReq(login.req) + handler.SetReq(login.req, false) assist := app.createAssistantWithoutConf(handler) assist.Start() assist.ShowResult() @@ -127,7 +127,7 @@ func (app *perfPb) LazyInit(ptService service.PttService, obs observer.Observer) } // } - handler.SetReq(reqData) + handler.SetReq(reqData, false) assist := app.createAssistant(handler) assist.Start() assist.ShowResult() diff --git a/cmd/v2/ui/perf_result.go b/cmd/v2/ui/perf_result.go index 904be05f3..d2b1072f1 100644 --- a/cmd/v2/ui/perf_result.go +++ b/cmd/v2/ui/perf_result.go @@ -48,13 +48,21 @@ func (app *perfResult) LazyInit(ptService service.PttService, obs observer.Obser closeApp3(perfWin.tabs, common.TOOLBAR_PERF_RES) } + // 返回 + returnBtn := widget.NewButtonWithIcon("返回", theme.NavigateBackIcon(), nil) + returnBtn.OnTapped = func() { + defer openApp3(perfWin.tabs, common.TOOLBAR_PERF_CHOOSE) + 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) + c := container.NewBorder(app.report, container.NewHBox(layout.NewSpacer(), app.resetBtn, returnBtn), nil, nil, app.itemList.ItemList) content.Objects = append(content.Objects, c) app.tabItem.Content = content app.listen()