perfkitbenchmarker/linux_benchmarks/tomcat_wrk_benchmark.py (136 lines of code) (raw):

# Copyright 2015 PerfKitBenchmarker Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Run wrk against a simple Tomcat web server. This is close to HTTP-RR: * Connections are reused. * The server does very little work. Doubles connections up to a fixed count, reports single connection latency and maximum error-free throughput. `wrk` is a scalable web load generator. `tomcat` is a popular Java web server. """ import functools import logging import operator from absl import flags from perfkitbenchmarker import background_tasks from perfkitbenchmarker import configs from perfkitbenchmarker.linux_packages import tomcat from perfkitbenchmarker.linux_packages import wrk import six import six.moves.urllib.parse flags.DEFINE_integer( 'tomcat_wrk_test_length', 120, 'Length of time, in seconds, to run wrk for each connction count', lower_bound=1, ) flags.DEFINE_integer( 'tomcat_wrk_max_connections', 128, 'Maximum number of simultaneous connections to attempt', lower_bound=1, ) flags.DEFINE_boolean( 'tomcat_wrk_report_all_samples', False, 'If true, report throughput/latency at all connection ' 'counts. If false (the default), report only the ' 'connection counts with lowest p50 latency and highest ' 'throughput.', ) # Stop when >= 1% of requests have errors MAX_ERROR_RATE = 0.01 FLAGS = flags.FLAGS BENCHMARK_NAME = 'tomcat_wrk' BENCHMARK_CONFIG = """ tomcat_wrk: description: Run wrk against tomcat. vm_groups: server: vm_spec: *default_dual_core client: vm_spec: *default_dual_core """ MAX_OPEN_FILES = 65536 WARM_UP_DURATION = 30 # Target: simple sample page that generates an SVG. SAMPLE_PAGE_PATH = 'examples/jsp/jsp2/jspx/textRotate.jspx?name=JSPX' NOFILE_LIMIT_CONF = '/etc/security/limits.d/pkb-tomcat.conf' def GetConfig(user_config): return configs.LoadConfig(BENCHMARK_CONFIG, user_config, BENCHMARK_NAME) def _IncreaseMaxOpenFiles(vm): vm.RemoteCommand( ('echo "{0} soft nofile {1}\n{0} hard nofile {1}" | sudo tee {2}').format( vm.user_name, MAX_OPEN_FILES, NOFILE_LIMIT_CONF ) ) def _RemoveOpenFileLimit(vm): vm.RemoteCommand('sudo rm -f {}'.format(NOFILE_LIMIT_CONF)) def _PrepareServer(vm): """Installs tomcat on the server.""" vm.Install('tomcat') _IncreaseMaxOpenFiles(vm) tomcat.Start(vm) def _PrepareClient(vm): """Install wrk on the client VM.""" _IncreaseMaxOpenFiles(vm) vm.Install('curl') vm.Install('wrk') def Prepare(benchmark_spec): """Install tomcat on one VM and wrk on another. Args: benchmark_spec: The benchmark specification. Contains all data that is required to run the benchmark. """ tomcat_vm = benchmark_spec.vm_groups['server'][0] wrk_vm = benchmark_spec.vm_groups['client'][0] tomcat_vm.AllowPort(tomcat.TOMCAT_HTTP_PORT) background_tasks.RunThreaded( (lambda f: f()), [ functools.partial(_PrepareServer, tomcat_vm), functools.partial(_PrepareClient, wrk_vm), ], ) def Run(benchmark_spec): """Run wrk against tomcat. Args: benchmark_spec: The benchmark specification. Contains all data that is required to run the benchmark. Returns: A list of sample.Sample objects. """ tomcat_vm = benchmark_spec.vm_groups['server'][0] wrk_vm = benchmark_spec.vm_groups['client'][0] samples = [] errors = 0 connections = 1 duration = FLAGS.tomcat_wrk_test_length max_connections = FLAGS.tomcat_wrk_max_connections target = six.moves.urllib.parse.urljoin( 'http://{}:{}'.format(tomcat_vm.ip_address, tomcat.TOMCAT_HTTP_PORT), SAMPLE_PAGE_PATH, ) logging.info('Warming up for %ds', WARM_UP_DURATION) list(wrk.Run(wrk_vm, connections=1, target=target, duration=WARM_UP_DURATION)) all_by_metric = [] while connections <= max_connections: run_samples = list( wrk.Run( wrk_vm, connections=connections, target=target, duration=duration ) ) by_metric = {i.metric: i for i in run_samples} errors = by_metric['errors'].value requests = by_metric['requests'].value throughput = by_metric['throughput'].value if requests < 1: logging.warning('No requests issued for %d connections.', connections) error_rate = 1.0 else: error_rate = float(errors) / requests if error_rate <= MAX_ERROR_RATE: all_by_metric.append(by_metric) else: logging.warning( 'Error rate exceeded maximum (%g > %g)', error_rate, MAX_ERROR_RATE ) logging.info( 'Ran with %d connections; %.2f%% errors, %.2f req/s', connections, error_rate, throughput, ) # Retry with double the connections connections *= 2 if not all_by_metric: raise ValueError('No requests succeeded.') # Annotate the sample with the best throughput max_throughput = max(all_by_metric, key=lambda x: x['throughput'].value) for sample in max_throughput.values(): sample.metadata.update(best_throughput=True) # ...and best 50th percentile latency min_p50 = min(all_by_metric, key=lambda x: x['p50 latency'].value) for sample in min_p50.values(): sample.metadata.update(best_p50=True) sort_key = operator.attrgetter('metric') if FLAGS.tomcat_wrk_report_all_samples: samples = [ sample for d in all_by_metric for sample in sorted(d.values(), key=sort_key) ] else: samples = sorted(min_p50.values(), key=sort_key) + sorted( max_throughput.values(), key=sort_key ) for sample in samples: sample.metadata.update(ip_type='external', runtime_in_seconds=duration) return samples def Cleanup(benchmark_spec): """Remove tomcat and wrk. Args: benchmark_spec: The benchmark specification. Contains all data that is required to run the benchmark. """ tomcat_vm = benchmark_spec.vm_groups['server'][0] tomcat.Stop(tomcat_vm) background_tasks.RunThreaded(_RemoveOpenFileLimit, benchmark_spec.vms)