更新报告
This commit is contained in:
parent
885df1e8f3
commit
497282c53b
35
lib/ai.go
35
lib/ai.go
@ -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)
|
||||||
|
19
lib/base.go
19
lib/base.go
@ -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 (
|
||||||
|
20
lib/param.go
20
lib/param.go
@ -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
|
||||||
}
|
}
|
||||||
|
126
lib/robot.go
126
lib/robot.go
@ -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{}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
|
||||||
// 创建场景
|
// 创建场景
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user