1 /* 2 * Copyright 2015 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.benchmarks.qps; 18 19 import static io.grpc.benchmarks.Utils.parseBoolean; 20 import static java.lang.Integer.parseInt; 21 import static java.util.Arrays.asList; 22 23 import io.grpc.ManagedChannel; 24 import io.grpc.benchmarks.Transport; 25 import io.grpc.benchmarks.Utils; 26 import io.grpc.benchmarks.proto.Control.RpcType; 27 import io.grpc.benchmarks.proto.Messages; 28 import io.grpc.benchmarks.proto.Messages.PayloadType; 29 import io.grpc.internal.testing.TestUtils; 30 import java.io.IOException; 31 import java.util.Collection; 32 import java.util.Collections; 33 import java.util.LinkedHashSet; 34 import java.util.Set; 35 36 /** 37 * Configuration options for benchmark clients. 38 */ 39 public class ClientConfiguration implements Configuration { 40 private static final ClientConfiguration DEFAULT = new ClientConfiguration(); 41 42 Transport transport = Transport.NETTY_NIO; 43 boolean tls; 44 boolean testca; 45 String authorityOverride = TestUtils.TEST_SERVER_HOST; 46 boolean useDefaultCiphers; 47 boolean directExecutor; 48 String target; 49 int channels = 4; 50 int outstandingRpcsPerChannel = 10; 51 int serverPayload; 52 int clientPayload; 53 int flowControlWindow = Utils.DEFAULT_FLOW_CONTROL_WINDOW; 54 // seconds 55 int duration = 60; 56 // seconds 57 int warmupDuration = 10; 58 int targetQps; 59 String histogramFile; 60 RpcType rpcType = RpcType.UNARY; 61 PayloadType payloadType = PayloadType.COMPRESSABLE; 62 ClientConfiguration()63 private ClientConfiguration() { 64 } 65 newChannel()66 public ManagedChannel newChannel() throws IOException { 67 return Utils.newClientChannel(transport, target, tls, testca, authorityOverride, 68 flowControlWindow, directExecutor); 69 } 70 newRequest()71 public Messages.SimpleRequest newRequest() { 72 return Utils.makeRequest(payloadType, clientPayload, serverPayload); 73 } 74 75 /** 76 * Constructs a builder for configuring a client application with supported parameters. If no 77 * parameters are provided, all parameters are assumed to be supported. 78 */ newBuilder(ClientParam... supportedParams)79 static Builder newBuilder(ClientParam... supportedParams) { 80 return new Builder(supportedParams); 81 } 82 83 static final class Builder extends AbstractConfigurationBuilder<ClientConfiguration> { 84 private final Collection<Param> supportedParams; 85 Builder(ClientParam... supportedParams)86 private Builder(ClientParam... supportedParams) { 87 this.supportedParams = supportedOptionsSet(supportedParams); 88 } 89 90 @Override newConfiguration()91 protected ClientConfiguration newConfiguration() { 92 return new ClientConfiguration(); 93 } 94 95 @Override getParams()96 protected Collection<Param> getParams() { 97 return supportedParams; 98 } 99 100 @Override build0(ClientConfiguration config)101 protected ClientConfiguration build0(ClientConfiguration config) { 102 if (config.tls) { 103 if (!config.transport.tlsSupported) { 104 throw new IllegalArgumentException( 105 "Transport " + config.transport.name().toLowerCase() + " does not support TLS."); 106 } 107 } 108 109 // Verify that the address type is correct for the transport type. 110 config.transport.validateSocketAddress(config.target); 111 112 return config; 113 } 114 supportedOptionsSet(ClientParam... supportedParams)115 private static Set<Param> supportedOptionsSet(ClientParam... supportedParams) { 116 if (supportedParams.length == 0) { 117 // If no options are supplied, default to including all options. 118 supportedParams = ClientParam.values(); 119 } 120 return Collections.unmodifiableSet(new LinkedHashSet<Param>(asList(supportedParams))); 121 } 122 } 123 124 enum ClientParam implements AbstractConfigurationBuilder.Param { 125 ADDRESS("STR", "Socket address (host:port) or Unix Domain Socket file name " 126 + "(unix:///path/to/file), depending on the transport selected.", null, true) { 127 @Override setClientValue(ClientConfiguration config, String value)128 protected void setClientValue(ClientConfiguration config, String value) { 129 config.target = value; 130 } 131 }, 132 CHANNELS("INT", "Number of Channels.", "" + DEFAULT.channels) { 133 @Override setClientValue(ClientConfiguration config, String value)134 protected void setClientValue(ClientConfiguration config, String value) { 135 config.channels = parseInt(value); 136 } 137 }, 138 OUTSTANDING_RPCS("INT", "Number of outstanding RPCs per Channel.", 139 "" + DEFAULT.outstandingRpcsPerChannel) { 140 @Override setClientValue(ClientConfiguration config, String value)141 protected void setClientValue(ClientConfiguration config, String value) { 142 config.outstandingRpcsPerChannel = parseInt(value); 143 } 144 }, 145 CLIENT_PAYLOAD("BYTES", "Payload Size of the Request.", "" + DEFAULT.clientPayload) { 146 @Override setClientValue(ClientConfiguration config, String value)147 protected void setClientValue(ClientConfiguration config, String value) { 148 config.clientPayload = parseInt(value); 149 } 150 }, 151 SERVER_PAYLOAD("BYTES", "Payload Size of the Response.", "" + DEFAULT.serverPayload) { 152 @Override setClientValue(ClientConfiguration config, String value)153 protected void setClientValue(ClientConfiguration config, String value) { 154 config.serverPayload = parseInt(value); 155 } 156 }, 157 TLS("", "Enable TLS.", "" + DEFAULT.tls) { 158 @Override setClientValue(ClientConfiguration config, String value)159 protected void setClientValue(ClientConfiguration config, String value) { 160 config.tls = parseBoolean(value); 161 } 162 }, 163 TESTCA("", "Use the provided Test Certificate for TLS.", "" + DEFAULT.testca) { 164 @Override setClientValue(ClientConfiguration config, String value)165 protected void setClientValue(ClientConfiguration config, String value) { 166 config.testca = parseBoolean(value); 167 } 168 }, 169 TRANSPORT("STR", Transport.getDescriptionString(), DEFAULT.transport.name().toLowerCase()) { 170 @Override setClientValue(ClientConfiguration config, String value)171 protected void setClientValue(ClientConfiguration config, String value) { 172 config.transport = Transport.valueOf(value.toUpperCase()); 173 } 174 }, 175 DURATION("SECONDS", "Duration of the benchmark.", "" + DEFAULT.duration) { 176 @Override setClientValue(ClientConfiguration config, String value)177 protected void setClientValue(ClientConfiguration config, String value) { 178 config.duration = parseInt(value); 179 } 180 }, 181 WARMUP_DURATION("SECONDS", "Warmup Duration of the benchmark.", "" + DEFAULT.warmupDuration) { 182 @Override setClientValue(ClientConfiguration config, String value)183 protected void setClientValue(ClientConfiguration config, String value) { 184 config.warmupDuration = parseInt(value); 185 } 186 }, 187 DIRECTEXECUTOR("", 188 "Don't use a threadpool for RPC calls, instead execute calls directly " 189 + "in the transport thread.", "" + DEFAULT.directExecutor) { 190 @Override setClientValue(ClientConfiguration config, String value)191 protected void setClientValue(ClientConfiguration config, String value) { 192 config.directExecutor = parseBoolean(value); 193 } 194 }, 195 SAVE_HISTOGRAM("FILE", "Write the histogram with the latency recordings to file.", null) { 196 @Override setClientValue(ClientConfiguration config, String value)197 protected void setClientValue(ClientConfiguration config, String value) { 198 config.histogramFile = value; 199 } 200 }, 201 STREAMING_RPCS("", "Use Streaming RPCs.", "false") { 202 @Override setClientValue(ClientConfiguration config, String value)203 protected void setClientValue(ClientConfiguration config, String value) { 204 config.rpcType = RpcType.STREAMING; 205 } 206 }, 207 FLOW_CONTROL_WINDOW("BYTES", "The HTTP/2 flow control window.", 208 "" + DEFAULT.flowControlWindow) { 209 @Override setClientValue(ClientConfiguration config, String value)210 protected void setClientValue(ClientConfiguration config, String value) { 211 config.flowControlWindow = parseInt(value); 212 } 213 }, 214 TARGET_QPS("INT", "Average number of QPS to shoot for.", "" + DEFAULT.targetQps, true) { 215 @Override setClientValue(ClientConfiguration config, String value)216 protected void setClientValue(ClientConfiguration config, String value) { 217 config.targetQps = parseInt(value); 218 } 219 }; 220 221 private final String type; 222 private final String description; 223 private final String defaultValue; 224 private final boolean required; 225 ClientParam(String type, String description, String defaultValue)226 ClientParam(String type, String description, String defaultValue) { 227 this(type, description, defaultValue, false); 228 } 229 ClientParam(String type, String description, String defaultValue, boolean required)230 ClientParam(String type, String description, String defaultValue, boolean required) { 231 this.type = type; 232 this.description = description; 233 this.defaultValue = defaultValue; 234 this.required = required; 235 } 236 237 @Override getName()238 public String getName() { 239 return name().toLowerCase(); 240 } 241 242 @Override getType()243 public String getType() { 244 return type; 245 } 246 247 @Override getDescription()248 public String getDescription() { 249 return description; 250 } 251 252 @Override getDefaultValue()253 public String getDefaultValue() { 254 return defaultValue; 255 } 256 257 @Override isRequired()258 public boolean isRequired() { 259 return required; 260 } 261 262 @Override setValue(Configuration config, String value)263 public void setValue(Configuration config, String value) { 264 setClientValue((ClientConfiguration) config, value); 265 } 266 setClientValue(ClientConfiguration config, String value)267 protected abstract void setClientValue(ClientConfiguration config, String value); 268 } 269 } 270