更新工具

This commit is contained in:
wh_zcy 2022-12-07 19:12:48 +08:00
parent 2badc32f70
commit d1caac0cba
20 changed files with 580 additions and 128 deletions

View File

@ -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)
}

View File

@ -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 = "编号"

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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 //协议名称
}

View File

@ -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
}

View File

@ -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
}
//压测配置

View File

@ -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()
}),
))

View File

@ -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()

View File

@ -14,4 +14,8 @@ const (
EVENT_UI_CLEAN Event = "uiclean"
// 请求响应计时
EVENT_RST = "ctime"
// 测试结果
EVENT_RESULT = "result"
EVENT_FINISH = "finish"
)

View File

@ -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
}

View File

@ -38,6 +38,7 @@ var (
&perfWelcome{},
&perfConf{},
&perfPb{},
&perfResult{},
}
)

View File

@ -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
}

38
cmd/v2/ui/perf_create.go Normal file
View File

@ -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
}

49
cmd/v2/ui/perf_login.go Normal file
View File

@ -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
}

View File

@ -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
}

103
cmd/v2/ui/perf_result.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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{})
}

View File

@ -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)
}
}
}