feat(go): add load simulator hold mode
This commit is contained in:
@@ -84,7 +84,8 @@ go run ./cmd/load-sim \
|
||||
-connect-rate 100 \
|
||||
-send-interval 10s \
|
||||
-duration 2m \
|
||||
-template 0200
|
||||
-template 0200 \
|
||||
-send=false
|
||||
```
|
||||
|
||||
1. 100 连接 smoke test。
|
||||
|
||||
@@ -104,7 +104,8 @@ go run ./cmd/load-sim \
|
||||
-connect-rate 100 \
|
||||
-send-interval 10s \
|
||||
-duration 2m \
|
||||
-template 0200
|
||||
-template 0200 \
|
||||
-send=false
|
||||
```
|
||||
|
||||
## 告警阈值建议
|
||||
|
||||
@@ -488,7 +488,7 @@ Extend load-sim to open N TCP connections and keep them open for `duration`, wit
|
||||
Expected CLI:
|
||||
|
||||
```bash
|
||||
go run ./cmd/load-sim -protocol jt808 -addr 115.29.187.205:808 -connections 10000 -send-interval 30s -duration 5m
|
||||
go run ./cmd/load-sim -protocol jt808 -addr 115.29.187.205:808 -connections 10000 -send-interval 30s -duration 5m -send=false
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run from non-production client**
|
||||
@@ -496,7 +496,7 @@ go run ./cmd/load-sim -protocol jt808 -addr 115.29.187.205:808 -connections 1000
|
||||
Run a small smoke first:
|
||||
|
||||
```bash
|
||||
go run ./cmd/load-sim -protocol jt808 -addr 115.29.187.205:808 -connections 100 -duration 30s
|
||||
go run ./cmd/load-sim -protocol jt808 -addr 115.29.187.205:808 -connections 100 -duration 30s -send=false
|
||||
```
|
||||
|
||||
Then run 10K only after smoke succeeds.
|
||||
|
||||
@@ -24,6 +24,7 @@ type Config struct {
|
||||
SendInterval time.Duration
|
||||
Duration time.Duration
|
||||
Template string
|
||||
SendFrames bool
|
||||
}
|
||||
|
||||
type FlagConfig struct {
|
||||
@@ -34,6 +35,7 @@ type FlagConfig struct {
|
||||
sendInterval time.Duration
|
||||
duration time.Duration
|
||||
template string
|
||||
sendFrames bool
|
||||
}
|
||||
|
||||
func RegisterFlags(fs *flag.FlagSet) *FlagConfig {
|
||||
@@ -45,6 +47,7 @@ func RegisterFlags(fs *flag.FlagSet) *FlagConfig {
|
||||
fs.DurationVar(&cfg.sendInterval, "send-interval", 10*time.Second, "frame send interval per connection")
|
||||
fs.DurationVar(&cfg.duration, "duration", 10*time.Minute, "load test duration")
|
||||
fs.StringVar(&cfg.template, "template", "", "frame template name")
|
||||
fs.BoolVar(&cfg.sendFrames, "send", true, "send protocol frames while connections are open")
|
||||
return cfg
|
||||
}
|
||||
|
||||
@@ -78,5 +81,6 @@ func (c *FlagConfig) Build() (Config, error) {
|
||||
SendInterval: c.sendInterval,
|
||||
Duration: c.duration,
|
||||
Template: strings.TrimSpace(c.template),
|
||||
SendFrames: c.sendFrames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ func TestConfigFromFlagsParsesCapacityKnobs(t *testing.T) {
|
||||
"-send-interval", "5s",
|
||||
"-duration", "30m",
|
||||
"-template", "0200",
|
||||
"-send=false",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("parse flags: %v", err)
|
||||
@@ -49,6 +50,9 @@ func TestConfigFromFlagsParsesCapacityKnobs(t *testing.T) {
|
||||
if got.Template != "0200" {
|
||||
t.Fatalf("Template = %q", got.Template)
|
||||
}
|
||||
if got.SendFrames {
|
||||
t.Fatal("SendFrames = true, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigFromFlagsRejectsUnsafeValues(t *testing.T) {
|
||||
|
||||
@@ -37,6 +37,9 @@ func (r Runner) Run(ctx context.Context, cfg Config) (Stats, error) {
|
||||
if err != nil {
|
||||
return Stats{}, err
|
||||
}
|
||||
if !cfg.SendFrames {
|
||||
payload = nil
|
||||
}
|
||||
dial := r.Dial
|
||||
if dial == nil {
|
||||
dialer := &net.Dialer{Timeout: 5 * time.Second, KeepAlive: 30 * time.Second}
|
||||
@@ -68,7 +71,11 @@ func (r Runner) Run(ctx context.Context, cfg Config) (Stats, error) {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
defer conn.Close()
|
||||
writeLoop(runCtx, conn, payload, cfg.SendInterval, &stats)
|
||||
if cfg.SendFrames {
|
||||
writeLoop(runCtx, conn, payload, cfg.SendInterval, &stats)
|
||||
return
|
||||
}
|
||||
<-runCtx.Done()
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ func TestRunnerDialsTargetConnectionsAndWritesFrames(t *testing.T) {
|
||||
SendInterval: time.Millisecond,
|
||||
Duration: 5 * time.Millisecond,
|
||||
Template: "0200",
|
||||
SendFrames: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
@@ -53,6 +54,41 @@ func TestRunnerDialsTargetConnectionsAndWritesFrames(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerCanHoldConnectionsWithoutWritingFrames(t *testing.T) {
|
||||
var writes atomic.Int64
|
||||
runner := Runner{
|
||||
Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return &recordingConn{
|
||||
write: func(p []byte) (int, error) {
|
||||
writes.Add(1)
|
||||
return len(p), nil
|
||||
},
|
||||
close: func() error { return nil },
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
stats, err := runner.Run(context.Background(), Config{
|
||||
Protocol: ProtocolJT808,
|
||||
Addr: "127.0.0.1:808",
|
||||
Connections: 2,
|
||||
ConnectRatePerSecond: 1000,
|
||||
SendInterval: time.Millisecond,
|
||||
Duration: 5 * time.Millisecond,
|
||||
Template: "0200",
|
||||
SendFrames: false,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
if stats.ConnectionsOpened != 2 {
|
||||
t.Fatalf("ConnectionsOpened = %d, want 2", stats.ConnectionsOpened)
|
||||
}
|
||||
if stats.FramesWritten != 0 || writes.Load() != 0 {
|
||||
t.Fatalf("frames written stats=%d writes=%d, want 0", stats.FramesWritten, writes.Load())
|
||||
}
|
||||
}
|
||||
|
||||
type recordingConn struct {
|
||||
write func([]byte) (int, error)
|
||||
close func() error
|
||||
|
||||
Reference in New Issue
Block a user