@@ -48,6 +48,7 @@ class Program
4848 private static List < int > _requestsPerConnection = null ! ;
4949 private static List < int > _errorsPerConnection = null ! ;
5050 private static List < List < double > > _latencyPerConnection = null ! ;
51+ private static int _callsStarted ;
5152 private static double _maxLatency ;
5253 private static Stopwatch _workTimer = new Stopwatch ( ) ;
5354 private static volatile bool _warmingUp ;
@@ -59,6 +60,7 @@ class Program
5960 private static ILoggerFactory ? _loggerFactory ;
6061 private static SslCredentials ? _credentials ;
6162 private static StringBuilder _errorStringBuilder = new StringBuilder ( ) ;
63+ private static CancellationTokenSource _cts = new CancellationTokenSource ( ) ;
6264
6365 public static async Task < int > Main ( string [ ] args )
6466 {
@@ -68,6 +70,7 @@ public static async Task<int> Main(string[] args)
6870 rootCommand . AddOption ( new Option < int > ( new string [ ] { "-c" , "--connections" } , ( ) => 1 , "Total number of connections to keep open" ) ) ;
6971 rootCommand . AddOption ( new Option < int > ( new string [ ] { "-w" , "--warmup" } , ( ) => 5 , "Duration of the warmup in seconds" ) ) ;
7072 rootCommand . AddOption ( new Option < int > ( new string [ ] { "-d" , "--duration" } , ( ) => 10 , "Duration of the test in seconds" ) ) ;
73+ rootCommand . AddOption ( new Option < int > ( new string [ ] { "--callCount" } , "Call count of test" ) ) ;
7174 rootCommand . AddOption ( new Option < string > ( new string [ ] { "-s" , "--scenario" } , "Scenario to run" ) { Required = true } ) ;
7275 rootCommand . AddOption ( new Option < bool > ( new string [ ] { "-l" , "--latency" } , ( ) => false , "Whether to collect detailed latency" ) ) ;
7376 rootCommand . AddOption ( new Option < string > ( new string [ ] { "-p" , "--protocol" } , "HTTP protocol" ) { Required = true } ) ;
@@ -112,17 +115,26 @@ public static async Task<int> Main(string[] args)
112115
113116 private static async Task StartScenario ( )
114117 {
115- var cts = new CancellationTokenSource ( ) ;
116- cts . CancelAfter ( TimeSpan . FromSeconds ( _options . Duration + _options . Warmup ) ) ;
118+ if ( _options . CallCount == null )
119+ {
120+ Log ( "Warm up: " + _options . Warmup ) ;
121+ Log ( "Duration: " + _options . Duration ) ;
122+
123+ _cts . CancelAfter ( TimeSpan . FromSeconds ( _options . Duration + _options . Warmup ) ) ;
117124
118- _warmingUp = true ;
119- _ = Task . Run ( async ( ) =>
125+ _warmingUp = true ;
126+ _ = Task . Run ( async ( ) =>
127+ {
128+ await Task . Delay ( TimeSpan . FromSeconds ( _options . Warmup ) ) ;
129+ _workTimer . Restart ( ) ;
130+ _warmingUp = false ;
131+ Log ( "Finished warming up." ) ;
132+ } ) ;
133+ }
134+ else
120135 {
121- await Task . Delay ( TimeSpan . FromSeconds ( _options . Warmup ) ) ;
122- _workTimer . Restart ( ) ;
123- _warmingUp = false ;
124- Log ( "Finished warming up." ) ;
125- } ) ;
136+ Log ( "Call count: " + _options . CallCount ) ;
137+ }
126138
127139 var callTasks = new List < Task > ( ) ;
128140
@@ -134,13 +146,13 @@ private static async Task StartScenario()
134146 switch ( _options . Scenario ? . ToLower ( ) )
135147 {
136148 case "unary" :
137- callFactory = ( connectionId , streamId ) => UnaryCall ( cts , connectionId , streamId ) ;
149+ callFactory = ( connectionId , streamId ) => UnaryCall ( _cts , connectionId , streamId ) ;
138150 break ;
139151 case "serverstreaming" :
140- callFactory = ( connectionId , streamId ) => ServerStreamingCall ( cts , connectionId , streamId ) ;
152+ callFactory = ( connectionId , streamId ) => ServerStreamingCall ( _cts , connectionId , streamId ) ;
141153 break ;
142154 case "pingpongstreaming" :
143- callFactory = ( connectionId , streamId ) => PingPongStreaming ( cts , connectionId , streamId ) ;
155+ callFactory = ( connectionId , streamId ) => PingPongStreaming ( _cts , connectionId , streamId ) ;
144156 break ;
145157 default :
146158 throw new Exception ( $ "Scenario '{ _options . Scenario } ' is not a known scenario.") ;
@@ -220,7 +232,7 @@ private static void CalculateStatistics()
220232 Log ( $ "Least Requests per Connection: { min } ") ;
221233 Log ( $ "Most Requests per Connection: { max } ") ;
222234
223- if ( _workTimer . ElapsedMilliseconds <= 0 )
235+ if ( _options . CallCount == null && _workTimer . ElapsedMilliseconds <= 0 )
224236 {
225237 Log ( "Job failed to run" ) ;
226238 return ;
@@ -229,6 +241,7 @@ private static void CalculateStatistics()
229241 var rps = ( double ) requestDelta / _workTimer . ElapsedMilliseconds * 1000 ;
230242 var errors = _errorsPerConnection . Sum ( ) ;
231243 Log ( $ "RPS: { rps : N0} ") ;
244+ Log ( $ "Total requests: { requestDelta } ") ;
232245 Log ( $ "Total errors: { errors } ") ;
233246
234247 BenchmarksEventSource . Log . Metadata ( "grpc/rps/max" , "max" , "sum" , "Max RPS" , "RPS: max" , "n0" ) ;
@@ -389,6 +402,9 @@ private static ChannelBase CreateChannel(string target)
389402 var address = useTls ? "https://" : "http://" ;
390403 address += target ;
391404
405+ #if NETCOREAPP3_1
406+ AppContext . SetSwitch ( "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport" , true ) ;
407+ #endif
392408 var httpClientHandler = new SocketsHttpHandler ( ) ;
393409 httpClientHandler . UseProxy = false ;
394410 httpClientHandler . AllowAutoRedirect = false ;
@@ -402,18 +418,24 @@ private static ChannelBase CreateChannel(string target)
402418 clientCertificate
403419 } ;
404420 }
421+ #if NET5_0
405422 if ( ! string . IsNullOrEmpty ( _options . UdsFileName ) )
406423 {
407424 var connectionFactory = new UnixDomainSocketConnectionFactory ( new UnixDomainSocketEndPoint ( ResolveUdsPath ( _options . UdsFileName ) ) ) ;
408425 httpClientHandler . ConnectCallback = connectionFactory . ConnectAsync ;
409426 }
427+ #endif
410428
411429 httpClientHandler . SslOptions . RemoteCertificateValidationCallback =
412430 ( object sender , X509Certificate ? certificate , X509Chain ? chain , SslPolicyErrors sslPolicyErrors ) => true ;
413431
414432 return GrpcChannel . ForAddress ( address , new GrpcChannelOptions
415433 {
434+ #if NET5_0
416435 HttpHandler = httpClientHandler ,
436+ #else
437+ HttpClient = new HttpClient ( httpClientHandler ) ,
438+ #endif
417439 LoggerFactory = _loggerFactory
418440 } ) ;
419441 }
@@ -507,6 +529,11 @@ private static async Task PingPongStreaming(CancellationTokenSource cts, int con
507529
508530 while ( ! cts . IsCancellationRequested )
509531 {
532+ if ( StartCall ( ) )
533+ {
534+ break ;
535+ }
536+
510537 try
511538 {
512539 var start = DateTime . UtcNow ;
@@ -547,6 +574,11 @@ private static async Task ServerStreamingCall(CancellationTokenSource cts, int c
547574
548575 while ( ! cts . IsCancellationRequested )
549576 {
577+ if ( StartCall ( ) )
578+ {
579+ break ;
580+ }
581+
550582 try
551583 {
552584 var start = DateTime . UtcNow ;
@@ -582,6 +614,11 @@ private static async Task UnaryCall(CancellationTokenSource cts, int connectionI
582614
583615 while ( ! cts . IsCancellationRequested )
584616 {
617+ if ( StartCall ( ) )
618+ {
619+ break ;
620+ }
621+
585622 try
586623 {
587624 var start = DateTime . UtcNow ;
@@ -600,5 +637,18 @@ private static async Task UnaryCall(CancellationTokenSource cts, int connectionI
600637
601638 Log ( connectionId , streamId , $ "Finished { _options . Scenario } ") ;
602639 }
640+
641+ private static bool StartCall ( )
642+ {
643+ Interlocked . Increment ( ref _callsStarted ) ;
644+ if ( _options . CallCount != null && _callsStarted > _options . CallCount )
645+ {
646+ Log ( $ "Reached { _options . CallCount } ") ;
647+ _cts . Cancel ( ) ;
648+ return true ;
649+ }
650+
651+ return false ;
652+ }
603653 }
604654}
0 commit comments