Skip to content

Commit e5b32c8

Browse files
authored
Merge pull request #28 from yuuki/feature/replace-cobra-with-viper
refactor: replace cobra subcommands with pflag/viper flags
2 parents 0bb5b12 + 218f4f3 commit e5b32c8

18 files changed

+654
-469
lines changed

CLAUDE.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ go test ./...
2525

2626
## Development Workflow with GitHub
2727

28-
1. Create a new branch for [feature/bug-name] from main branch.
28+
1. Create a new branch for [feature/bug/refactor-name] from main branch.
2929
2. Coding the feature/bug.
3030
3. Add or fix tests.
3131
4. Check the code quality with testing, linting and formatting.
@@ -37,9 +37,7 @@ go test ./...
3737

3838
The project follows a simple flat structure with all Go files in the top-level directory:
3939

40-
- `main.go`: Entry point defining root Cobra command
41-
- `serve.go`: Server subcommand implementation
42-
- `connect.go`: Client subcommand implementation
40+
- `main.go`: Entry point with flag-based CLI using pflag and viper (-c for client, -s for server)
4341
- `server.go`: TCP/UDP server logic with context-based cancellation and errgroup for concurrent handling
4442
- `client.go`: Client implementation with persistent/ephemeral connection modes, rate limiting, and latency measurements
4543
- `option.go`: Default socket option implementations (non-Linux platforms)
@@ -79,7 +77,8 @@ The project includes test files (connect_test.go, serve_test.go, e2e_test.go). W
7977
## Dependencies
8078

8179
Key dependencies include:
82-
- github.com/spf13/cobra: CLI framework
80+
- github.com/spf13/pflag: POSIX-style command-line flag parsing
81+
- github.com/spf13/viper: Configuration management with flag binding
8382
- github.com/rcrowley/go-metrics: Performance metrics
8483
- go.uber.org/ratelimit: Rate limiting
8584
- golang.org/x/sync/errgroup: Concurrent error handling

README.md

Lines changed: 42 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ tcpulse is a high-performance TCP/UDP connection load generator and performance
99

1010
tcpulse is a specialized tool designed to measure and analyze the performance characteristics of network connections. It operates in two primary modes:
1111

12-
- **Server mode (`serve`)**: Acts as an echo server that accepts TCP/UDP connections and echoes back received data
13-
- **Client mode (`connect`)**: Generates configurable load against target servers and measures connection performance metrics
12+
- **Server mode (`-s/--server`)**: Acts as an echo server that accepts TCP/UDP connections and echoes back received data
13+
- **Client mode (`-c/--client`)**: Generates configurable load against target servers and measures connection performance metrics
1414

1515
## Why use tcpulse?
1616

@@ -96,49 +96,39 @@ tcpulse --help
9696

9797
```shell-session
9898
$ tcpulse --help
99-
tcpulse is a measturement tool for TCP connections in Go
100-
101-
Usage:
102-
tcpulse [command]
103-
104-
Available Commands:
105-
connect connect connects to a port where 'serve' listens
106-
help Help about any command
107-
serve serve accepts connections
108-
109-
Flags:
110-
-h, --help help for tcpulse
111-
112-
Use "tcpulse [command] --help" for more information about a command.
113-
114-
# tcpulse serve
115-
$ tcpulse serve --help
116-
serve accepts connections
117-
118-
Usage:
119-
tcpulse serve [flags]
120-
121-
Flags:
122-
-h, --help help for serve
123-
-l, --listenAddr string listening address (default "0.0.0.0:9100")
124-
125-
# tcpulse connect
126-
$ ./tcpulse connect --help
127-
connect connects to a port where 'serve' listens
128-
129-
Usage:
130-
tcpulse connect [flags]
131-
132-
Flags:
133-
-c, --connections int32 Number of connections to keep (only for 'persistent')l (default 10)
134-
-d, --duration duration measurement period (default 10s)
135-
-f, --flavor string connect behavior type 'persistent' or 'ephemeral' (default "persistent")
136-
-h, --help help for connect
137-
-i, --interval int interval for printing stats (default 5)
138-
--jsonlines output results in JSON Lines format
139-
-p, --proto string protocol (tcp or udp) (default "tcp")
140-
-r, --rate int32 New connections throughput (/s) (only for 'ephemeral') (default 100)
141-
--show-only-results print only results of measurement stats (default true)
99+
Usage: tcpulse [OPTIONS] <addresses...>
100+
101+
tcpulse is a concurrent TCP/UDP load generator that provides fine-grained, flow-level control
102+
103+
Modes:
104+
-c, --client Run in client mode (connect to servers)
105+
-s, --server Run in server mode (accept connections)
106+
107+
Options:
108+
--addrs-file [client mode] enable to pass a file including a pair of addresses and ports to an argument
109+
-c, --client run in client mode
110+
--connections int32 [client mode] Number of concurrent connections to keep (only for 'persistent') (default 10)
111+
--duration duration [client mode] measurement period (default 10s)
112+
--enable-pprof [client mode] enable pprof profiling
113+
--flavor string [client mode] connect behavior type 'persistent' or 'ephemeral' (default "persistent")
114+
--interval duration [client mode] interval for printing stats (default 5s)
115+
--jsonlines [client mode] output results in JSON Lines format
116+
--listen-addrs-file string [server mode] enable to pass a file including a pair of addresses and ports
117+
--merge-results-each-host [client mode] merge results of each host (with --show-only-results)
118+
--message-bytes int32 [client mode] TCP/UDP message size (bytes) (default 64)
119+
--pprof-addr string [client mode] pprof listening address:port (default "localhost:6060")
120+
--proto string [client mode] protocol (tcp or udp) (default "tcp")
121+
--protocol string [server mode] listening protocol ('tcp' or 'udp') (default "all")
122+
--rate int32 [client mode] New connections throughput (/s) (only for 'ephemeral') (default 100)
123+
-s, --server run in server mode
124+
--show-only-results [client mode] print only results of measurement stats
125+
--version show version information
126+
127+
Examples:
128+
tcpulse -s # Start server on default port 9100
129+
tcpulse -s 0.0.0.0:8080 # Start server on port 8080
130+
tcpulse -c localhost:9100 # Connect to server as client
131+
tcpulse -c --connections 50 host:port # Connect with 50 connections
142132
```
143133

144134
## Examples
@@ -148,29 +138,29 @@ Flags:
148138
Run as a server.
149139

150140
```shell-session
151-
$ tcpulse serve -l 127.0.0.1:9100
141+
$ tcpulse -s 127.0.0.1:9100
152142
```
153143

154144
Run as a client to put a load on the server.
155145

156146
```shell-session
157-
$ tcpulse connect --flavor ephemeral --rate 1000 --duration 15s 127.0.0.1:9100
147+
$ tcpulse -c --flavor ephemeral --rate 1000 --duration 15s 127.0.0.1:9100
158148
```
159149

160150
```shell-session
161-
$ tcpulse connect --flavor persistent --connections 1000 --duration 15s 127.0.0.1:9100
151+
$ tcpulse -c --flavor persistent --connections 1000 --duration 15s 127.0.0.1:9100
162152
```
163153

164154
Run as a UDP client.
165155

166156
```shell-session
167-
$ tcpulse connect --proto udp --rate 1000 --duration 15s 127.0.0.1:9100
157+
$ tcpulse -c --proto udp --rate 1000 --duration 15s 127.0.0.1:9100
168158
```
169159

170160
Output results in JSON Lines format for integration with monitoring tools.
171161

172162
```shell-session
173-
$ tcpulse connect --jsonlines --rate 1000 --duration 10s 127.0.0.1:9100
163+
$ tcpulse -c --jsonlines --rate 1000 --duration 10s 127.0.0.1:9100
174164
{"peer":"127.0.0.1:9100","count":9998,"latency_max_us":2156,"latency_min_us":145,"latency_mean_us":234,"latency_90p_us":289,"latency_95p_us":321,"latency_99p_us":456,"rate_per_sec":999.8,"timestamp":"2025-01-07T10:30:00Z"}
175165
```
176166

@@ -179,7 +169,7 @@ $ tcpulse connect --jsonlines --rate 1000 --duration 10s 127.0.0.1:9100
179169
#### Standard Output Format
180170

181171
```shell-session
182-
$ tcpulse connect --proto tcp --flavor ephemeral --rate 1000 --duration 15s 10.0.150.2:9200 10.0.150.2:9300
172+
$ tcpulse -c --proto tcp --flavor ephemeral --rate 1000 --duration 15s 10.0.150.2:9200 10.0.150.2:9300
183173
PEER CNT LAT_MAX(µs) LAT_MIN(µs) LAT_MEAN(µs) LAT_90p(µs) LAT_95p(µs) LAT_99p(µs) RATE(/s)
184174
10.0.150.2:9200 4996 4108 212 367 446 492 773 999.00
185175
10.0.150.2:9300 4999 10294 219 389 435 470 1595 999.40
@@ -195,7 +185,7 @@ PEER CNT LAT_MAX(µs) LAT_MIN(µs) LAT_MEAN(µs)
195185
#### JSON Lines Output Format
196186

197187
```shell-session
198-
$ tcpulse connect --jsonlines --proto tcp --flavor ephemeral --rate 1000 --duration 15s 10.0.150.2:9200 10.0.150.2:9300
188+
$ tcpulse -c --jsonlines --proto tcp --flavor ephemeral --rate 1000 --duration 15s 10.0.150.2:9200 10.0.150.2:9300
199189
{"peer":"10.0.150.2:9200","count":14799,"latency_max_us":13736,"latency_min_us":223,"latency_mean_us":530,"latency_90p_us":501,"latency_95p_us":953,"latency_99p_us":5989,"rate_per_sec":996.0,"timestamp":"2025-01-07T10:30:00Z"}
200190
{"peer":"10.0.150.2:9300","count":14809,"latency_max_us":18023,"latency_min_us":212,"latency_mean_us":542,"latency_90p_us":492,"latency_95p_us":1110,"latency_99p_us":5849,"rate_per_sec":996.47,"timestamp":"2025-01-07T10:30:00Z"}
201191
```

client.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ type JSONLinesResult struct {
361361
func printJSONLinesReport(w io.Writer, addrs []string, mergeResultsEachHost bool) {
362362
timestamp := time.Now().UTC().Format(time.RFC3339)
363363
results := []JSONLinesResult{}
364-
364+
365365
if mergeResultsEachHost {
366366
ts := getOrRegisterTimer("total.latency", "", mergeResultsEachHost)
367367
results = append(results, JSONLinesResult{
@@ -393,7 +393,7 @@ func printJSONLinesReport(w io.Writer, addrs []string, mergeResultsEachHost bool
393393
})
394394
}
395395
}
396-
396+
397397
for _, result := range results {
398398
if err := json.NewEncoder(w).Encode(result); err != nil {
399399
slog.Error("Failed to encode JSON result", "error", err)
@@ -412,4 +412,4 @@ func printReport(w io.Writer, addrs []string, mergeResultsEachHost bool) {
412412
ts := getOrRegisterTimer("total.latency", addr, mergeResultsEachHost)
413413
printStatLine(w, addr, ts)
414414
}
415-
}
415+
}

0 commit comments

Comments
 (0)