@@ -38,6 +38,10 @@ import (
38
38
_ "google.golang.org/grpc/xds"
39
39
)
40
40
41
+ func init () {
42
+ rpcCfgs .Store ([]* rpcConfig {{typ : unaryCall }})
43
+ }
44
+
41
45
type statsWatcherKey struct {
42
46
startID int32
43
47
endID int32
@@ -73,21 +77,84 @@ func (watcher *statsWatcher) buildResp() *testpb.LoadBalancerStatsResponse {
73
77
}
74
78
}
75
79
80
+ type accumulatedStats struct {
81
+ mu sync.Mutex
82
+ numRpcsStartedByMethod map [string ]int32
83
+ numRpcsSucceededByMethod map [string ]int32
84
+ numRpcsFailedByMethod map [string ]int32
85
+ }
86
+
87
+ // copyStatsMap makes a copy of the map, and also replaces the RPC type string
88
+ // to the proto string. E.g. "UnaryCall" -> "UNARY_CALL".
89
+ func copyStatsMap (originalMap map [string ]int32 ) (newMap map [string ]int32 ) {
90
+ newMap = make (map [string ]int32 )
91
+ for k , v := range originalMap {
92
+ var kk string
93
+ switch k {
94
+ case unaryCall :
95
+ kk = testpb .ClientConfigureRequest_UNARY_CALL .String ()
96
+ case emptyCall :
97
+ kk = testpb .ClientConfigureRequest_EMPTY_CALL .String ()
98
+ default :
99
+ logger .Warningf ("unrecognized rpc type: %s" , k )
100
+ }
101
+ if kk == "" {
102
+ continue
103
+ }
104
+ newMap [kk ] = v
105
+ }
106
+ return newMap
107
+ }
108
+
109
+ func (as * accumulatedStats ) buildResp () * testpb.LoadBalancerAccumulatedStatsResponse {
110
+ as .mu .Lock ()
111
+ defer as .mu .Unlock ()
112
+ return & testpb.LoadBalancerAccumulatedStatsResponse {
113
+ NumRpcsStartedByMethod : copyStatsMap (as .numRpcsStartedByMethod ),
114
+ NumRpcsSucceededByMethod : copyStatsMap (as .numRpcsSucceededByMethod ),
115
+ NumRpcsFailedByMethod : copyStatsMap (as .numRpcsFailedByMethod ),
116
+ }
117
+ }
118
+
119
+ func (as * accumulatedStats ) startRPC (rpcType string ) {
120
+ as .mu .Lock ()
121
+ defer as .mu .Unlock ()
122
+ as .numRpcsStartedByMethod [rpcType ]++
123
+ }
124
+
125
+ func (as * accumulatedStats ) finishRPC (rpcType string , failed bool ) {
126
+ as .mu .Lock ()
127
+ defer as .mu .Unlock ()
128
+ if failed {
129
+ as .numRpcsFailedByMethod [rpcType ]++
130
+ return
131
+ }
132
+ as .numRpcsSucceededByMethod [rpcType ]++
133
+ }
134
+
76
135
var (
77
136
failOnFailedRPC = flag .Bool ("fail_on_failed_rpc" , false , "Fail client if any RPCs fail after first success" )
78
137
numChannels = flag .Int ("num_channels" , 1 , "Num of channels" )
79
138
printResponse = flag .Bool ("print_response" , false , "Write RPC response to stdout" )
80
139
qps = flag .Int ("qps" , 1 , "QPS per channel, for each type of RPC" )
81
- rpc = flag .String ("rpc" , "UnaryCall" , "Types of RPCs to make, ',' separated string. RPCs can be EmptyCall or UnaryCall" )
82
- rpcMetadata = flag .String ("metadata" , "" , "The metadata to send with RPC, in format EmptyCall:key1:value1,UnaryCall:key2:value2" )
140
+ rpc = flag .String ("rpc" , "UnaryCall" , "Types of RPCs to make, ',' separated string. RPCs can be EmptyCall or UnaryCall. Deprecated: Use Configure RPC to XdsUpdateClientConfigureServiceServer instead. " )
141
+ rpcMetadata = flag .String ("metadata" , "" , "The metadata to send with RPC, in format EmptyCall:key1:value1,UnaryCall:key2:value2. Deprecated: Use Configure RPC to XdsUpdateClientConfigureServiceServer instead. " )
83
142
rpcTimeout = flag .Duration ("rpc_timeout" , 20 * time .Second , "Per RPC timeout" )
84
143
server = flag .String ("server" , "localhost:8080" , "Address of server to connect to" )
85
144
statsPort = flag .Int ("stats_port" , 8081 , "Port to expose peer distribution stats service" )
86
145
146
+ rpcCfgs atomic.Value
147
+
87
148
mu sync.Mutex
88
149
currentRequestID int32
89
150
watchers = make (map [statsWatcherKey ]* statsWatcher )
90
151
152
+ accStats = accumulatedStats {
153
+ numRpcsStartedByMethod : make (map [string ]int32 ),
154
+ numRpcsSucceededByMethod : make (map [string ]int32 ),
155
+ numRpcsFailedByMethod : make (map [string ]int32 ),
156
+ }
157
+
91
158
// 0 or 1 representing an RPC has succeeded. Use hasRPCSucceeded and
92
159
// setRPCSucceeded to access in a safe manner.
93
160
rpcSucceeded uint32
@@ -163,6 +230,47 @@ func (s *statsService) GetClientStats(ctx context.Context, in *testpb.LoadBalanc
163
230
}
164
231
}
165
232
233
+ func (s * statsService ) GetClientAccumulatedStats (ctx context.Context , in * testpb.LoadBalancerAccumulatedStatsRequest ) (* testpb.LoadBalancerAccumulatedStatsResponse , error ) {
234
+ return accStats .buildResp (), nil
235
+ }
236
+
237
+ type configureService struct {
238
+ testpb.UnimplementedXdsUpdateClientConfigureServiceServer
239
+ }
240
+
241
+ func (s * configureService ) Configure (ctx context.Context , in * testpb.ClientConfigureRequest ) (* testpb.ClientConfigureResponse , error ) {
242
+ rpcsToMD := make (map [testpb.ClientConfigureRequest_RpcType ][]string )
243
+ for _ , typ := range in .GetTypes () {
244
+ rpcsToMD [typ ] = nil
245
+ }
246
+ for _ , md := range in .GetMetadata () {
247
+ typ := md .GetType ()
248
+ strs , ok := rpcsToMD [typ ]
249
+ if ! ok {
250
+ continue
251
+ }
252
+ rpcsToMD [typ ] = append (strs , md .GetKey (), md .GetValue ())
253
+ }
254
+ cfgs := make ([]* rpcConfig , 0 , len (rpcsToMD ))
255
+ for typ , md := range rpcsToMD {
256
+ var rpcType string
257
+ switch typ {
258
+ case testpb .ClientConfigureRequest_UNARY_CALL :
259
+ rpcType = unaryCall
260
+ case testpb .ClientConfigureRequest_EMPTY_CALL :
261
+ rpcType = emptyCall
262
+ default :
263
+ return nil , fmt .Errorf ("unsupported RPC type: %v" , typ )
264
+ }
265
+ cfgs = append (cfgs , & rpcConfig {
266
+ typ : rpcType ,
267
+ md : metadata .Pairs (md ... ),
268
+ })
269
+ }
270
+ rpcCfgs .Store (cfgs )
271
+ return & testpb.ClientConfigureResponse {}, nil
272
+ }
273
+
166
274
const (
167
275
unaryCall string = "UnaryCall"
168
276
emptyCall string = "EmptyCall"
@@ -218,7 +326,7 @@ func parseRPCMetadata(rpcMetadataStr string, rpcs []string) []*rpcConfig {
218
326
219
327
func main () {
220
328
flag .Parse ()
221
- rpcCfgs := parseRPCMetadata (* rpcMetadata , parseRPCTypes (* rpc ))
329
+ rpcCfgs . Store ( parseRPCMetadata (* rpcMetadata , parseRPCTypes (* rpc ) ))
222
330
223
331
lis , err := net .Listen ("tcp" , fmt .Sprintf (":%d" , * statsPort ))
224
332
if err != nil {
@@ -227,6 +335,7 @@ func main() {
227
335
s := grpc .NewServer ()
228
336
defer s .Stop ()
229
337
testpb .RegisterLoadBalancerStatsServiceServer (s , & statsService {})
338
+ testpb .RegisterXdsUpdateClientConfigureServiceServer (s , & configureService {})
230
339
go s .Serve (lis )
231
340
232
341
clients := make ([]testpb.TestServiceClient , * numChannels )
@@ -240,7 +349,7 @@ func main() {
240
349
}
241
350
ticker := time .NewTicker (time .Second / time .Duration (* qps * * numChannels ))
242
351
defer ticker .Stop ()
243
- sendRPCs (clients , rpcCfgs , ticker )
352
+ sendRPCs (clients , ticker )
244
353
}
245
354
246
355
func makeOneRPC (c testpb.TestServiceClient , cfg * rpcConfig ) (* peer.Peer , * rpcInfo , error ) {
@@ -257,6 +366,7 @@ func makeOneRPC(c testpb.TestServiceClient, cfg *rpcConfig) (*peer.Peer, *rpcInf
257
366
header metadata.MD
258
367
err error
259
368
)
369
+ accStats .startRPC (cfg .typ )
260
370
switch cfg .typ {
261
371
case unaryCall :
262
372
var resp * testpb.SimpleResponse
@@ -270,8 +380,10 @@ func makeOneRPC(c testpb.TestServiceClient, cfg *rpcConfig) (*peer.Peer, *rpcInf
270
380
_ , err = c .EmptyCall (ctx , & testpb.Empty {}, grpc .Peer (& p ), grpc .Header (& header ))
271
381
}
272
382
if err != nil {
383
+ accStats .finishRPC (cfg .typ , true )
273
384
return nil , nil , err
274
385
}
386
+ accStats .finishRPC (cfg .typ , false )
275
387
276
388
hosts := header ["hostname" ]
277
389
if len (hosts ) > 0 {
@@ -280,26 +392,28 @@ func makeOneRPC(c testpb.TestServiceClient, cfg *rpcConfig) (*peer.Peer, *rpcInf
280
392
return & p , & info , err
281
393
}
282
394
283
- func sendRPCs (clients []testpb.TestServiceClient , cfgs [] * rpcConfig , ticker * time.Ticker ) {
395
+ func sendRPCs (clients []testpb.TestServiceClient , ticker * time.Ticker ) {
284
396
var i int
285
397
for range ticker .C {
286
- go func (i int ) {
287
- // Get and increment request ID, and save a list of watchers that
288
- // are interested in this RPC.
289
- mu .Lock ()
290
- savedRequestID := currentRequestID
291
- currentRequestID ++
292
- savedWatchers := []* statsWatcher {}
293
- for key , value := range watchers {
294
- if key .startID <= savedRequestID && savedRequestID < key .endID {
295
- savedWatchers = append (savedWatchers , value )
296
- }
398
+ // Get and increment request ID, and save a list of watchers that are
399
+ // interested in this RPC.
400
+ mu .Lock ()
401
+ savedRequestID := currentRequestID
402
+ currentRequestID ++
403
+ savedWatchers := []* statsWatcher {}
404
+ for key , value := range watchers {
405
+ if key .startID <= savedRequestID && savedRequestID < key .endID {
406
+ savedWatchers = append (savedWatchers , value )
297
407
}
298
- mu .Unlock ()
408
+ }
409
+ mu .Unlock ()
299
410
300
- c := clients [i ]
411
+ // Get the RPC metadata configurations from the Configure RPC.
412
+ cfgs := rpcCfgs .Load ().([]* rpcConfig )
301
413
302
- for _ , cfg := range cfgs {
414
+ c := clients [i ]
415
+ for _ , cfg := range cfgs {
416
+ go func (cfg * rpcConfig ) {
303
417
p , info , err := makeOneRPC (c , cfg )
304
418
305
419
for _ , watcher := range savedWatchers {
@@ -325,8 +439,8 @@ func sendRPCs(clients []testpb.TestServiceClient, cfgs []*rpcConfig, ticker *tim
325
439
fmt .Printf ("RPC %q, failed with %v\n " , cfg .typ , err )
326
440
}
327
441
}
328
- }
329
- }( i )
442
+ }( cfg )
443
+ }
330
444
i = (i + 1 ) % len (clients )
331
445
}
332
446
}
0 commit comments