更新报告

This commit is contained in:
zhaocy 2022-12-13 08:13:44 +08:00
parent 885df1e8f3
commit 497282c53b
7 changed files with 173 additions and 54 deletions

View File

@ -5,33 +5,31 @@ import (
"fmt" "fmt"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"legu.airobot/storage" "legu.airobot/storage"
) )
type myAI struct { type myAI struct {
robots []*Robot robots []*Robot
scenes []*scene scenes []*scene
iscenes []IScene iscenes []IScene
tickets Tickets //票池 tickets Tickets //票池
countTotal uint32 //机器人总数 useCount uint32 //计数(压入的用户数)
useCount uint32 //使用数量 lock sync.Mutex //
lock sync.Mutex // config *storage.Config //配置
config *storage.Config //配置
} }
func NewAI(aip AIParam) (*myAI, error) { func NewAI(aip AIParam) (*myAI, error) {
logrus.Debug("创建AI")
if err := aip.Check(); err != nil { if err := aip.Check(); err != nil {
return nil, err return nil, err
} }
ai := &myAI{ ai := &myAI{
scenes: make([]*scene, 0), scenes: make([]*scene, 0),
countTotal: aip.RobotCount, config: aip.Config,
config: aip.Config, iscenes: aip.Scenes,
iscenes: aip.Scenes,
} }
if err := ai.init(); err != nil { if err := ai.init(); err != nil {
@ -45,14 +43,15 @@ func (m *myAI) init() error {
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("初始化AI") buf.WriteString("初始化AI")
tickets, err := NewTickets(m.countTotal) uct := m.config.Global.UserCountTotal
tickets, err := NewTickets(uct)
if err != nil { if err != nil {
return err return err
} }
m.tickets = tickets m.tickets = tickets
buf.WriteString(fmt.Sprintf("完成 机器人数量:%d", m.countTotal)) buf.WriteString(fmt.Sprintf("完成 用户数量:%d", uct))
logrus.Debug(buf.String()) logrus.Debug(buf.String())
return nil return nil
} }
@ -72,8 +71,12 @@ func (m *myAI) Start() bool {
go func() { go func() {
for { for {
m.tickets.Take() m.tickets.Take()
atomic.AddUint32(&m.useCount, 1) if m.useCount >= uint32(m.config.Global.UserCount) {
atomic.StoreUint32(&m.useCount, 0)
time.Sleep(time.Duration(m.config.Global.IntervalS) * time.Second)
}
go func() { go func() {
atomic.AddUint32(&m.useCount, 1)
robot := NewRobot(m.config) robot := NewRobot(m.config)
robot.SetScenes(m.iscenes) robot.SetScenes(m.iscenes)
m.appendRobot(robot) m.appendRobot(robot)

View File

@ -26,12 +26,19 @@ type SceneInfo struct {
} }
type CallResult struct { type CallResult struct {
ID int64 // ID。 ID int64 // ID。
Req RawReq // 原生请求。 MainType string
Resp RawResp // 原生响应。 SubType string
Code RetCode // 响应代码。 Elapse time.Duration // 耗时。
Msg string // 结果成因的简述。 }
Elapse time.Duration // 耗时。
type Statistics struct {
ElapseTotal time.Duration //总耗时
MaxElapse time.Duration //最大耗时
MinElapse time.Duration //最小耗时
AvgElapse time.Duration //平均耗时
CallCount int64 //调用次数
Route string //协议名称
} }
const ( const (

View File

@ -13,8 +13,6 @@ import (
type AIParam struct { type AIParam struct {
Config *storage.Config Config *storage.Config
Scenes []IScene Scenes []IScene
// 机器人数量
RobotCount uint32
} }
type SceneParam struct { type SceneParam struct {
Name string Name string
@ -23,21 +21,33 @@ type SceneParam struct {
func (a *AIParam) Check() error { func (a *AIParam) Check() error {
var errMsgs []string var errMsgs []string
if a.RobotCount == 0 { if a.Config.Global.UserCountTotal == 0 {
errMsgs = append(errMsgs, "机器人数量至少1个") errMsgs = append(errMsgs, "机器人数量至少1个")
} }
if a.Config.Global.UserCount == 0 {
errMsgs = append(errMsgs, "每次压入的用户数至少是1")
}
if a.Config.Global.SId == "" {
errMsgs = append(errMsgs, "缺少区服ID")
}
if a.Config.Global.IntervalS < 0 {
errMsgs = append(errMsgs, "压入用户的间隔时间应该是0+")
}
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteString("AI 参数校验") buf.WriteString("AI 参数校验")
if errMsgs != nil { if errMsgs != nil {
errMsg := strings.Join(errMsgs, " ") errMsg := strings.Join(errMsgs, " ")
buf.WriteString(fmt.Sprintf("未通过 (%s)", errMsg)) buf.WriteString(fmt.Sprintf("未通过 (%s)", errMsg))
logrus.Debug(buf.String()) logrus.Error(buf.String())
return errors.New(errMsg) return errors.New(errMsg)
} }
buf.WriteString( buf.WriteString(
fmt.Sprintf("通过 机器人数量:%v", a.RobotCount)) fmt.Sprintf("通过 机器人数量:%v", a.Config.Global.UserCountTotal))
logrus.Debug(buf.String()) logrus.Debug(buf.String())
return nil return nil
} }

View File

@ -1,6 +1,7 @@
package lib package lib
import ( import (
"fmt"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@ -8,6 +9,7 @@ import (
"time" "time"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/nacos-group/nacos-sdk-go/common/logger"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
"legu.airobot/pb" "legu.airobot/pb"
@ -27,23 +29,25 @@ type IRobot interface {
type Robot struct { type Robot struct {
IStore IStore
account string account string
sid string sid string
conn *websocket.Conn conn *websocket.Conn
data map[string]interface{} //机器人缓存数据 data map[string]interface{} //机器人缓存数据
status uint32 //状态 status uint32 //状态
lock sync.Mutex // lock sync.Mutex //
sceneQueue *Queue[IScene] //场景队列 sceneQueue *Queue[IScene] //场景队列
config *storage.Config //配置 config *storage.Config //配置
resultCh chan *CallResult resultCh chan *CallResult //请求结果通道
sceneResultCh chan *CallResult //场景结果通道
} }
func NewRobot(config *storage.Config) *Robot { func NewRobot(config *storage.Config) *Robot {
robot := &Robot{ robot := &Robot{
data: make(map[string]interface{}), data: make(map[string]interface{}),
sceneQueue: NewQueue[IScene](), sceneQueue: NewQueue[IScene](),
config: config, config: config,
resultCh: make(chan *CallResult, 50), resultCh: make(chan *CallResult, 100),
sceneResultCh: make(chan *CallResult, 50),
} }
robot.Store("sid", config.Global.SId) robot.Store("sid", config.Global.SId)
return robot return robot
@ -69,6 +73,12 @@ func (r *Robot) SendMsg(mainType, subType string, req proto.Message, rsp proto.M
defer func() { defer func() {
t := time.Since(start) t := time.Since(start)
logrus.WithFields(logrus.Fields{"MainType": mainType, "SubType": subType, "rsp": rsp, "since": t.String()}).Debug("接收消息") logrus.WithFields(logrus.Fields{"MainType": mainType, "SubType": subType, "rsp": rsp, "since": t.String()}).Debug("接收消息")
// 发送请求结果
r.SendResult(&CallResult{
MainType: mainType,
SubType: subType,
Elapse: t,
})
}() }()
head := &pb.UserMessage{MainType: mainType, SubType: subType} head := &pb.UserMessage{MainType: mainType, SubType: subType}
@ -186,9 +196,99 @@ func (m *Robot) syncCall() {
elapsedTime := time.Since(start) elapsedTime := time.Since(start)
info := scene.Info() info := scene.Info()
logrus.WithField("t", elapsedTime.String()).Debug("场景【" + info.Name + "】执行完毕耗时统计") logrus.WithField("t", elapsedTime.String()).Debug("场景【" + info.Name + "】执行完毕耗时统计")
//结束
m.prepareToStop()
//显示场景结果
} }
} }
func (m *Robot) prepareToStop() {
atomic.CompareAndSwapUint32(&m.status, STATUS_STARTED, STATUS_STOPPING)
logger.Infof("Closing result channel...")
close(m.resultCh)
atomic.StoreUint32(&m.status, STATUS_STOPPED)
}
func (m *Robot) SendResult(result *CallResult) bool {
if atomic.LoadUint32(&m.status) != STATUS_STARTED {
m.printIgnoredResult(result, "stopped load generator")
return false
}
select {
case m.resultCh <- result:
fmt.Printf("准备发送结果: %v\n", result)
return true
default:
m.printIgnoredResult(result, "full result channel")
return false
}
}
func (m *Robot) ShowResult() {
// statistics := &Statistics{}
// max := statistics.MaxElapse
// min := statistics.MinElapse
routes := make(map[string]*Statistics)
for r := range m.resultCh {
head := fmt.Sprintf("%s.%s", r.MainType, r.SubType)
if len(routes) == 0 {
statis := &Statistics{
Route: head,
ElapseTotal: r.Elapse,
MaxElapse: r.Elapse,
MinElapse: r.Elapse,
CallCount: 1,
}
avg := (statis.MaxElapse + statis.MinElapse) / 2
statis.AvgElapse = avg
routes[head] = statis
} else {
if route, ok := routes[head]; ok {
max := route.MaxElapse
min := route.MinElapse
if r.Elapse > max {
max = r.Elapse
}
if r.Elapse < min {
min = r.Elapse
}
statis := &Statistics{
Route: head,
ElapseTotal: route.ElapseTotal + r.Elapse,
MaxElapse: max,
MinElapse: min,
CallCount: route.CallCount + 1,
}
avg := (statis.MaxElapse + statis.MinElapse) / 2
statis.AvgElapse = avg
routes[head] = statis
} else {
statis := &Statistics{
Route: head,
ElapseTotal: r.Elapse,
MaxElapse: r.Elapse,
MinElapse: r.Elapse,
CallCount: 1,
}
avg := (statis.MaxElapse + statis.MinElapse) / 2
statis.AvgElapse = avg
routes[head] = statis
}
}
}
//TODO 将统计结果写入文件
}
func (m *Robot) printIgnoredResult(result *CallResult, cause string) {
resultMsg := fmt.Sprintf(
"MainType=%s, SubType=%s, Elapse=%v",
result.MainType, result.SubType, result.Elapse)
logrus.Warnf("Ignored result: %s. (cause: %s)\n", resultMsg, cause)
}
// Deprecated // Deprecated
func (m *Robot) checkResp(data []byte, rsp proto.Message) bool { func (m *Robot) checkResp(data []byte, rsp proto.Message) bool {
msg := &pb.UserMessage{} msg := &pb.UserMessage{}

View File

@ -11,11 +11,12 @@ type Config struct {
} }
type Global struct { type Global struct {
UserCount int32 `json:"UserCount,omitempty"` //用户数 UserCount uint32 `json:"UserCount,omitempty"` //用户数(每次压入的数量)
SId string `json:"sid,omitempty"` //区服ID UserCountTotal uint32 `json:"UserCountTotal,omitempty"` //用户总数(压入的总数)
WsAddr string `json:"wsAddr,omitempty"` //websocket addr SId string `json:"sid,omitempty"` //区服ID
IntervalS int32 `json:"intervalS,omitempty"` //间隔时间s WsAddr string `json:"wsAddr,omitempty"` //websocket addr
TimeoutMs int32 `json:"timeoutMs,omitempty"` //超时时间 IntervalS int32 `json:"intervalS,omitempty"` //间隔时间s每次压入用户的间隔时间
TimeoutMs int32 `json:"timeoutMs,omitempty"` //超时时间 (请求超时时间)
} }
type Scene struct { type Scene struct {

View File

@ -9,9 +9,7 @@ import (
) )
func TestAction(t *testing.T) { func TestAction(t *testing.T) {
aip := lib.AIParam{ aip := lib.AIParam{}
RobotCount: 1,
}
ai, _ := lib.NewAI(aip) ai, _ := lib.NewAI(aip)
// 创建场景 // 创建场景

View File

@ -102,9 +102,8 @@ func (mw *MainWindow) startContainer() {
startBtn.Enable() startBtn.Enable()
startBtn.OnTapped = func() { startBtn.OnTapped = func() {
param := lib.AIParam{ param := lib.AIParam{
Scenes: mw.UIImpl.scenes, Scenes: mw.UIImpl.scenes,
Config: mw.UIImpl.config, Config: mw.UIImpl.config,
RobotCount: uint32(config.Global.UserCount),
} }
ai, err := lib.NewAI(param) ai, err := lib.NewAI(param)
if err != nil { if err != nil {
@ -124,10 +123,11 @@ func (mw *MainWindow) configContainer() {
wsAddrEntry := widget.NewEntry() wsAddrEntry := widget.NewEntry()
sidEntry := widget.NewEntry() sidEntry := widget.NewEntry()
userCountEntry := widget.NewEntry() userCountEntry := widget.NewEntry()
userCountEntry.PlaceHolder = "每次压入的用户数"
userTotalEntry := widget.NewEntry() userTotalEntry := widget.NewEntry()
userTotalEntry.PlaceHolder = "用户数上限" userTotalEntry.PlaceHolder = "总共需要压入的最大用户数"
timeoutEntry := widget.NewEntry() timeoutEntry := widget.NewEntry()
timeoutEntry.PlaceHolder = "请求超时" timeoutEntry.PlaceHolder = "请求超时时间"
intervalEntry := widget.NewEntry() intervalEntry := widget.NewEntry()
intervalEntry.PlaceHolder = "每次压入用户间隔时间" intervalEntry.PlaceHolder = "每次压入用户间隔时间"
@ -157,7 +157,7 @@ func (mw *MainWindow) configContainer() {
global := &storage.Global{ global := &storage.Global{
WsAddr: wsAddrEntry.Text, WsAddr: wsAddrEntry.Text,
SId: sidEntry.Text, SId: sidEntry.Text,
UserCount: cast.ToInt32(userCountEntry.Text), UserCount: cast.ToUint32(userCountEntry.Text),
TimeoutMs: cast.ToInt32(timeoutEntry.Text), TimeoutMs: cast.ToInt32(timeoutEntry.Text),
IntervalS: cast.ToInt32(intervalEntry.Text), IntervalS: cast.ToInt32(intervalEntry.Text),
} }