feat(go): add load simulator hold mode

This commit is contained in:
lingniu
2026-07-03 18:01:40 +08:00
parent c1fe1fbfb5
commit 291bffa340
7 changed files with 58 additions and 5 deletions

View File

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

View File

@@ -104,7 +104,8 @@ go run ./cmd/load-sim \
-connect-rate 100 \
-send-interval 10s \
-duration 2m \
-template 0200
-template 0200 \
-send=false
```
## 告警阈值建议

View File

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

View File

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

View File

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

View File

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

View File

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