feat(go): add load simulator hold mode
This commit is contained in:
@@ -84,7 +84,8 @@ go run ./cmd/load-sim \
|
|||||||
-connect-rate 100 \
|
-connect-rate 100 \
|
||||||
-send-interval 10s \
|
-send-interval 10s \
|
||||||
-duration 2m \
|
-duration 2m \
|
||||||
-template 0200
|
-template 0200 \
|
||||||
|
-send=false
|
||||||
```
|
```
|
||||||
|
|
||||||
1. 100 连接 smoke test。
|
1. 100 连接 smoke test。
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ go run ./cmd/load-sim \
|
|||||||
-connect-rate 100 \
|
-connect-rate 100 \
|
||||||
-send-interval 10s \
|
-send-interval 10s \
|
||||||
-duration 2m \
|
-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:
|
Expected CLI:
|
||||||
|
|
||||||
```bash
|
```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**
|
- [ ] **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:
|
Run a small smoke first:
|
||||||
|
|
||||||
```bash
|
```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.
|
Then run 10K only after smoke succeeds.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ type Config struct {
|
|||||||
SendInterval time.Duration
|
SendInterval time.Duration
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
Template string
|
Template string
|
||||||
|
SendFrames bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type FlagConfig struct {
|
type FlagConfig struct {
|
||||||
@@ -34,6 +35,7 @@ type FlagConfig struct {
|
|||||||
sendInterval time.Duration
|
sendInterval time.Duration
|
||||||
duration time.Duration
|
duration time.Duration
|
||||||
template string
|
template string
|
||||||
|
sendFrames bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterFlags(fs *flag.FlagSet) *FlagConfig {
|
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.sendInterval, "send-interval", 10*time.Second, "frame send interval per connection")
|
||||||
fs.DurationVar(&cfg.duration, "duration", 10*time.Minute, "load test duration")
|
fs.DurationVar(&cfg.duration, "duration", 10*time.Minute, "load test duration")
|
||||||
fs.StringVar(&cfg.template, "template", "", "frame template name")
|
fs.StringVar(&cfg.template, "template", "", "frame template name")
|
||||||
|
fs.BoolVar(&cfg.sendFrames, "send", true, "send protocol frames while connections are open")
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,5 +81,6 @@ func (c *FlagConfig) Build() (Config, error) {
|
|||||||
SendInterval: c.sendInterval,
|
SendInterval: c.sendInterval,
|
||||||
Duration: c.duration,
|
Duration: c.duration,
|
||||||
Template: strings.TrimSpace(c.template),
|
Template: strings.TrimSpace(c.template),
|
||||||
|
SendFrames: c.sendFrames,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ func TestConfigFromFlagsParsesCapacityKnobs(t *testing.T) {
|
|||||||
"-send-interval", "5s",
|
"-send-interval", "5s",
|
||||||
"-duration", "30m",
|
"-duration", "30m",
|
||||||
"-template", "0200",
|
"-template", "0200",
|
||||||
|
"-send=false",
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("parse flags: %v", err)
|
t.Fatalf("parse flags: %v", err)
|
||||||
@@ -49,6 +50,9 @@ func TestConfigFromFlagsParsesCapacityKnobs(t *testing.T) {
|
|||||||
if got.Template != "0200" {
|
if got.Template != "0200" {
|
||||||
t.Fatalf("Template = %q", got.Template)
|
t.Fatalf("Template = %q", got.Template)
|
||||||
}
|
}
|
||||||
|
if got.SendFrames {
|
||||||
|
t.Fatal("SendFrames = true, want false")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestConfigFromFlagsRejectsUnsafeValues(t *testing.T) {
|
func TestConfigFromFlagsRejectsUnsafeValues(t *testing.T) {
|
||||||
|
|||||||
@@ -37,6 +37,9 @@ func (r Runner) Run(ctx context.Context, cfg Config) (Stats, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return Stats{}, err
|
return Stats{}, err
|
||||||
}
|
}
|
||||||
|
if !cfg.SendFrames {
|
||||||
|
payload = nil
|
||||||
|
}
|
||||||
dial := r.Dial
|
dial := r.Dial
|
||||||
if dial == nil {
|
if dial == nil {
|
||||||
dialer := &net.Dialer{Timeout: 5 * time.Second, KeepAlive: 30 * time.Second}
|
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() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer conn.Close()
|
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,
|
SendInterval: time.Millisecond,
|
||||||
Duration: 5 * time.Millisecond,
|
Duration: 5 * time.Millisecond,
|
||||||
Template: "0200",
|
Template: "0200",
|
||||||
|
SendFrames: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Run() error = %v", err)
|
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 {
|
type recordingConn struct {
|
||||||
write func([]byte) (int, error)
|
write func([]byte) (int, error)
|
||||||
close func() error
|
close func() error
|
||||||
|
|||||||
Reference in New Issue
Block a user