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