diff --git a/benchmarks/src/main/java/io/grpc/benchmarks/qps/AbstractConfigurationBuilder.java b/benchmarks/src/main/java/io/grpc/benchmarks/qps/AbstractConfigurationBuilder.java new file mode 100644 index 0000000000..8cc92f09f6 --- /dev/null +++ b/benchmarks/src/main/java/io/grpc/benchmarks/qps/AbstractConfigurationBuilder.java @@ -0,0 +1,258 @@ +/* + * Copyright 2015, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.grpc.benchmarks.qps; + +import static java.lang.Math.max; +import static java.lang.String.CASE_INSENSITIVE_ORDER; + +import com.google.common.base.Strings; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +/** + * Abstract base class for all {@link Configuration.Builder}s. + */ +public abstract class AbstractConfigurationBuilder + implements Configuration.Builder { + + private static final Param HELP = new Param() { + @Override + public String getName() { + return "help"; + } + + @Override + public String getType() { + return ""; + } + + @Override + public String getDescription() { + return "Print this text."; + } + + @Override + public boolean isRequired() { + return false; + } + + public String getDefaultValue() { + return null; + } + + @Override + public void setValue(Configuration config, String value) { + throw new UnsupportedOperationException(); + } + }; + + /** + * A single application parameter supported by this builder. + */ + protected interface Param { + /** + * The name of the parameter as it would appear on the command-line. + */ + String getName(); + + /** + * A string representation of the parameter type. If not applicable, just returns an empty + * string. + */ + String getType(); + + /** + * A description of this parameter used when printing usage. + */ + String getDescription(); + + /** + * The default value used when not set explicitly. Ignored if {@link #isRequired()} is {@code + * true}. + */ + String getDefaultValue(); + + /** + * Indicates whether or not this parameter is required and must therefore be set before the + * configuration can be successfully built. + */ + boolean isRequired(); + + /** + * Sets this parameter on the given configuration instance. + */ + void setValue(Configuration config, String value); + } + + @Override + public final T build(String[] args) { + T config = newConfiguration(); + Map paramMap = getParamMap(); + Set appliedParams = new TreeSet(CASE_INSENSITIVE_ORDER); + + for (String arg : args) { + if (!arg.startsWith("--")) { + throw new IllegalArgumentException("All arguments must start with '--': " + arg); + } + String[] pair = arg.substring(2).split("=", 2); + String key = pair[0]; + String value = ""; + if (pair.length == 2) { + value = pair[1]; + } + + // If help was requested, just throw now to print out the usage. + if (HELP.getName().equalsIgnoreCase(key)) { + throw new IllegalArgumentException("Help requested"); + } + + Param param = paramMap.get(key); + if (param == null) { + throw new IllegalArgumentException("Unsupported argument: " + key); + } + param.setValue(config, value); + appliedParams.add(key); + } + + // Ensure that all required options have been provided. + for (Param param : getParams()) { + if (param.isRequired() && !appliedParams.contains(param.getName())) { + throw new IllegalArgumentException("Missing required option '--" + + param.getName() + "'."); + } + } + + return build0(config); + } + + @Override + public final void printUsage() { + System.out.println("Usage: [ARGS...]"); + int column1Width = 0; + List params = new ArrayList(); + params.add(HELP); + params.addAll(getParams()); + + for (Param param : params) { + column1Width = max(commandLineFlag(param).length(), column1Width); + } + int column1Start = 2; + int column2Start = column1Start + column1Width + 2; + for (Param param : params) { + StringBuilder sb = new StringBuilder(); + sb.append(Strings.repeat(" ", column1Start)); + sb.append(commandLineFlag(param)); + sb.append(Strings.repeat(" ", column2Start - sb.length())); + String message = param.getDescription(); + sb.append(wordWrap(message, column2Start, 80)); + if (param.isRequired()) { + sb.append(Strings.repeat(" ", column2Start)); + sb.append("[Required]\n"); + } else if (param.getDefaultValue() != null && !param.getDefaultValue().isEmpty()) { + sb.append(Strings.repeat(" ", column2Start)); + sb.append("[Default=" + param.getDefaultValue() + "]\n"); + } + System.out.println(sb); + } + System.out.println(); + } + + /** + * Creates a new configuration instance which will be used as the target for command-line + * arguments. + */ + protected abstract T newConfiguration(); + + /** + * Returns the valid parameters supported by the configuration. + */ + protected abstract Collection getParams(); + + /** + * Called by {@link #build(String[])} after verifying that all required options have been set. + * Performs any final validation and modifications to the configuration. If successful, returns + * the fully built configuration. + */ + protected abstract T build0(T config); + + private Map getParamMap() { + Map map = new TreeMap(CASE_INSENSITIVE_ORDER); + for (Param param : getParams()) { + map.put(param.getName(), param); + } + return map; + } + + private static String commandLineFlag(Param param) { + String name = param.getName().toLowerCase(); + String type = (!param.getType().isEmpty() ? '=' + param.getType() : ""); + return "--" + name + type; + } + + private static String wordWrap(String text, int startPos, int maxPos) { + StringBuilder builder = new StringBuilder(); + int pos = startPos; + String[] parts = text.split("\\n"); + boolean isBulleted = parts.length > 1; + for (String part : parts) { + int lineStart = startPos; + while (!part.isEmpty()) { + if (pos < lineStart) { + builder.append(Strings.repeat(" ", lineStart - pos)); + pos = lineStart; + } + int maxLength = maxPos - pos; + int length = part.length(); + if (length > maxLength) { + length = part.lastIndexOf(' ', maxPos - pos) + 1; + if (length == 0) { + length = part.length(); + } + } + builder.append(part.substring(0, length)); + part = part.substring(length); + + // Wrap to the next line. + builder.append("\n"); + pos = 0; + lineStart = isBulleted ? startPos + 2 : startPos; + } + } + return builder.toString(); + } +} diff --git a/benchmarks/src/main/java/io/grpc/benchmarks/qps/AsyncClient.java b/benchmarks/src/main/java/io/grpc/benchmarks/qps/AsyncClient.java index a8e4e2cc03..ffc320c215 100644 --- a/benchmarks/src/main/java/io/grpc/benchmarks/qps/AsyncClient.java +++ b/benchmarks/src/main/java/io/grpc/benchmarks/qps/AsyncClient.java @@ -31,59 +31,42 @@ package io.grpc.benchmarks.qps; -import static grpc.testing.Qpstest.SimpleRequest; -import static grpc.testing.Qpstest.SimpleResponse; -import static grpc.testing.TestServiceGrpc.TestServiceStub; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.CHANNELS; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.CLIENT_PAYLOAD; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.CONNECTION_WINDOW; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.DIRECTEXECUTOR; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.DURATION; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.HOST; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.NETTY_NATIVE_TRANSPORT; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.OKHTTP; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.OUTSTANDING_RPCS; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.PORT; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.SAVE_HISTOGRAM; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.SERVER_PAYLOAD; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.STREAMING_RPCS; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.STREAM_WINDOW; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.TESTCA; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.TLS; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.WARMUP_DURATION; -import static io.grpc.benchmarks.qps.ClientConfiguration.HISTOGRAM_MAX_VALUE; -import static io.grpc.benchmarks.qps.ClientConfiguration.HISTOGRAM_PRECISION; -import static io.grpc.benchmarks.qps.ClientUtil.saveHistogram; -import static io.grpc.testing.integration.Util.loadCert; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.ADDRESS; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.CHANNELS; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.CLIENT_PAYLOAD; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.CONNECTION_WINDOW; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.DIRECTEXECUTOR; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.DURATION; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.OUTSTANDING_RPCS; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.SAVE_HISTOGRAM; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.SERVER_PAYLOAD; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.STREAMING_RPCS; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.STREAM_WINDOW; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TESTCA; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TLS; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.TRANSPORT; +import static io.grpc.benchmarks.qps.ClientConfiguration.ClientParam.WARMUP_DURATION; +import static io.grpc.benchmarks.qps.Utils.HISTOGRAM_MAX_VALUE; +import static io.grpc.benchmarks.qps.Utils.HISTOGRAM_PRECISION; +import static io.grpc.benchmarks.qps.Utils.newClientChannel; +import static io.grpc.benchmarks.qps.Utils.saveHistogram; import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; -import grpc.testing.Qpstest.Payload; -import grpc.testing.TestServiceGrpc; - import io.grpc.Channel; import io.grpc.ChannelImpl; import io.grpc.Status; import io.grpc.stub.StreamObserver; -import io.grpc.transport.netty.GrpcSslContexts; -import io.grpc.transport.netty.NegotiationType; -import io.grpc.transport.netty.NettyChannelBuilder; -import io.grpc.transport.okhttp.OkHttpChannelBuilder; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.ssl.SslContext; -import io.netty.handler.ssl.SslProvider; +import io.grpc.testing.Payload; +import io.grpc.testing.SimpleRequest; +import io.grpc.testing.SimpleResponse; +import io.grpc.testing.TestServiceGrpc; +import io.grpc.testing.TestServiceGrpc.TestServiceStub; import org.HdrHistogram.Histogram; import org.HdrHistogram.HistogramIterationValue; -import java.io.File; -import java.io.IOException; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CancellationException; @@ -115,7 +98,7 @@ public class AsyncClient { List channels = new ArrayList(config.channels); for (int i = 0; i < config.channels; i++) { - channels.add(newChannel()); + channels.add(newClientChannel(config)); } // Do a warmup first. It's the same as the actual benchmark, except that @@ -175,58 +158,6 @@ public class AsyncClient { return histograms; } - private Channel newChannel() throws IOException { - if (config.okhttp) { - if (config.tls) { - throw new IllegalStateException("TLS unsupported with okhttp"); - } - return OkHttpChannelBuilder - .forAddress(config.host, config.port) - .executor(config.directExecutor ? MoreExecutors.newDirectExecutorService() : null) - .build(); - } - SslContext context = null; - InetAddress address = InetAddress.getByName(config.host); - NegotiationType negotiationType = config.tls ? NegotiationType.TLS : NegotiationType.PLAINTEXT; - if (config.tls && config.testca) { - // Force the hostname to match the cert the server uses. - address = InetAddress.getByAddress("foo.test.google.fr", address.getAddress()); - File cert = loadCert("ca.pem"); - context = GrpcSslContexts.forClient().trustManager(cert) - .sslProvider(config.nettyNativeTransport ? SslProvider.OPENSSL : SslProvider.JDK) - .build(); - } - final EventLoopGroup group; - final Class channelType; - if (config.nettyNativeTransport) { - try { - // These classes are only available on linux. - Class groupClass = Class.forName("io.netty.channel.epoll.EpollEventLoopGroup"); - @SuppressWarnings("unchecked") - Class channelClass = - (Class) Class.forName( - "io.netty.channel.epoll.EpollSocketChannel"); - group = (EventLoopGroup) groupClass.newInstance(); - channelType = channelClass; - } catch (Exception e) { - throw new RuntimeException(e); - } - } else { - group = new NioEventLoopGroup(); - channelType = NioSocketChannel.class; - } - return NettyChannelBuilder - .forAddress(new InetSocketAddress(address, config.port)) - .eventLoopGroup(group) - .channelType(channelType) - .negotiationType(negotiationType) - .executor(config.directExecutor ? MoreExecutors.newDirectExecutorService() : null) - .sslContext(context) - .connectionWindowSize(config.connectionWindow) - .streamWindowSize(config.streamWindow) - .build(); - } - private Future doRpcs(Channel channel, SimpleRequest request, long endTime) { switch (config.rpcType) { case UNARY: @@ -393,13 +324,10 @@ public class AsyncClient { * checkstyle complains if there is no javadoc comment here. */ public static void main(String... args) throws Exception { - ClientConfiguration.Builder configBuilder = - ClientConfiguration.newBuilder() - .addOptions(PORT, HOST, CHANNELS, OUTSTANDING_RPCS) - .addOptions(CLIENT_PAYLOAD, SERVER_PAYLOAD, TLS, TESTCA) - .addOptions(OKHTTP, DURATION, WARMUP_DURATION, DIRECTEXECUTOR) - .addOptions(SAVE_HISTOGRAM, STREAMING_RPCS, CONNECTION_WINDOW) - .addOptions(STREAM_WINDOW, NETTY_NATIVE_TRANSPORT); + ClientConfiguration.Builder configBuilder = ClientConfiguration.newBuilder( + ADDRESS, CHANNELS, OUTSTANDING_RPCS, CLIENT_PAYLOAD, SERVER_PAYLOAD, + TLS, TESTCA, TRANSPORT, DURATION, WARMUP_DURATION, DIRECTEXECUTOR, + SAVE_HISTOGRAM, STREAMING_RPCS, CONNECTION_WINDOW, STREAM_WINDOW); ClientConfiguration config; try { config = configBuilder.build(args); diff --git a/benchmarks/src/main/java/io/grpc/benchmarks/qps/AsyncServer.java b/benchmarks/src/main/java/io/grpc/benchmarks/qps/AsyncServer.java index 808a8562ba..d849259d4b 100644 --- a/benchmarks/src/main/java/io/grpc/benchmarks/qps/AsyncServer.java +++ b/benchmarks/src/main/java/io/grpc/benchmarks/qps/AsyncServer.java @@ -31,21 +31,19 @@ package io.grpc.benchmarks.qps; -import static grpc.testing.Qpstest.Payload; -import static grpc.testing.Qpstest.PayloadType; -import static grpc.testing.Qpstest.SimpleRequest; -import static grpc.testing.Qpstest.SimpleResponse; import static io.grpc.testing.integration.Util.loadCert; -import static io.grpc.testing.integration.Util.pickUnusedPort; import com.google.common.util.concurrent.MoreExecutors; import com.google.protobuf.ByteString; -import grpc.testing.TestServiceGrpc; - import io.grpc.ServerImpl; import io.grpc.Status; import io.grpc.stub.StreamObserver; +import io.grpc.testing.Payload; +import io.grpc.testing.PayloadType; +import io.grpc.testing.SimpleRequest; +import io.grpc.testing.SimpleResponse; +import io.grpc.testing.TestServiceGrpc; import io.grpc.transport.netty.GrpcSslContexts; import io.grpc.transport.netty.NettyServerBuilder; import io.netty.channel.EventLoopGroup; @@ -56,6 +54,7 @@ import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslProvider; import java.io.File; +import java.io.IOException; import java.util.concurrent.TimeUnit; /** @@ -63,13 +62,6 @@ import java.util.concurrent.TimeUnit; */ public class AsyncServer { - private boolean tls; - private int port; - private int connectionWindow = NettyServerBuilder.DEFAULT_CONNECTION_WINDOW_SIZE; - private int streamWindow = NettyServerBuilder.DEFAULT_STREAM_WINDOW_SIZE; - private boolean directExecutor; - private boolean nettyNativeTransport; - /** * checkstyle complains if there is no javadoc comment here. */ @@ -79,60 +71,20 @@ public class AsyncServer { /** Equivalent of "main", but non-static. */ public void run(String[] args) throws Exception { - if (!parseArgs(args)) { + ServerConfiguration.Builder configBuilder = ServerConfiguration.newBuilder(); + ServerConfiguration config; + try { + config = configBuilder.build(args); + } catch (Exception e) { + System.out.println(e.getMessage()); + configBuilder.printUsage(); return; } - SslContext sslContext = null; - if (tls) { - System.out.println("Using fake CA for TLS certificate.\n" - + "Run the Java client with --tls --testca"); - - File cert = loadCert("server1.pem"); - File key = loadCert("server1.key"); - sslContext = GrpcSslContexts.forServer(cert, key) - .sslProvider(nettyNativeTransport ? SslProvider.OPENSSL : SslProvider.JDK).build(); - } - - if (port == 0) { - port = pickUnusedPort(); - } - - final EventLoopGroup boss; - final EventLoopGroup worker; - final Class channelType; - if (nettyNativeTransport) { - try { - // These classes are only available on linux. - Class groupClass = Class.forName("io.netty.channel.epoll.EpollEventLoopGroup"); - @SuppressWarnings("unchecked") - Class channelClass = (Class) - Class.forName("io.netty.channel.epoll.EpollServerSocketChannel"); - boss = (EventLoopGroup) groupClass.newInstance(); - worker = (EventLoopGroup) groupClass.newInstance(); - channelType = channelClass; - } catch (Exception e) { - throw new RuntimeException(e); - } - } else { - boss = new NioEventLoopGroup(); - worker = new NioEventLoopGroup(); - channelType = NioServerSocketChannel.class; - } - final ServerImpl server = NettyServerBuilder - .forPort(port) - .bossEventLoopGroup(boss) - .workerEventLoopGroup(worker) - .channelType(channelType) - .addService(TestServiceGrpc.bindService(new TestServiceImpl())) - .sslContext(sslContext) - .executor(directExecutor ? MoreExecutors.newDirectExecutorService() : null) - .connectionWindowSize(connectionWindow) - .streamWindowSize(streamWindow) - .build(); + final ServerImpl server = newServer(config); server.start(); - System.out.println("QPS Server started on port " + port); + System.out.println("QPS Server started on " + config.address); Runtime.getRuntime().addShutdownHook(new Thread() { @Override @@ -148,66 +100,77 @@ public class AsyncServer { }); } - private boolean parseArgs(String[] args) { - try { - for (String arg : args) { - if (!arg.startsWith("--")) { - System.err.println("All arguments must start with '--': " + arg); - printUsage(); - return false; - } + static ServerImpl newServer(ServerConfiguration config) throws IOException { + SslContext sslContext = null; + if (config.tls) { + System.out.println("Using fake CA for TLS certificate.\n" + + "Run the Java client with --tls --testca"); - String[] pair = arg.substring(2).split("=", 2); - String key = pair[0]; - String value = ""; - if (pair.length == 2) { - value = pair[1]; - } - - if ("help".equals(key)) { - printUsage(); - return false; - } else if ("port".equals(key)) { - port = Integer.parseInt(value); - } else if ("tls".equals(key)) { - tls = true; - } else if ("directexecutor".equals(key)) { - directExecutor = true; - } else if ("connection_window".equals(key)) { - connectionWindow = Integer.parseInt(value); - } else if ("stream_window".equals(key)) { - streamWindow = Integer.parseInt(value); - } else if ("netty_native_transport".equals(key)) { - nettyNativeTransport = true; - } else { - System.err.println("Unrecognized argument '" + key + "'."); - } - } - } catch (Exception e) { - e.printStackTrace(); - printUsage(); - return false; + File cert = loadCert("server1.pem"); + File key = loadCert("server1.key"); + boolean useJdkSsl = config.transport == ServerConfiguration.Transport.NETTY_NIO; + sslContext = GrpcSslContexts.forServer(cert, key) + .sslProvider(useJdkSsl ? SslProvider.JDK : SslProvider.OPENSSL) + .build(); } - return true; - } + final EventLoopGroup boss; + final EventLoopGroup worker; + final Class channelType; + switch (config.transport) { + case NETTY_NIO: { + boss = new NioEventLoopGroup(); + worker = new NioEventLoopGroup(); + channelType = NioServerSocketChannel.class; + break; + } + case NETTY_EPOLL: { + try { + // These classes are only available on linux. + Class groupClass = Class.forName("io.netty.channel.epoll.EpollEventLoopGroup"); + @SuppressWarnings("unchecked") + Class channelClass = (Class) + Class.forName("io.netty.channel.epoll.EpollServerSocketChannel"); + boss = (EventLoopGroup) groupClass.newInstance(); + worker = (EventLoopGroup) groupClass.newInstance(); + channelType = channelClass; + break; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + case NETTY_UNIX_DOMAIN_SOCKET: { + try { + // These classes are only available on linux. + Class groupClass = Class.forName("io.netty.channel.epoll.EpollEventLoopGroup"); + @SuppressWarnings("unchecked") + Class channelClass = (Class) + Class.forName("io.netty.channel.epoll.EpollServerDomainSocketChannel"); + boss = (EventLoopGroup) groupClass.newInstance(); + worker = (EventLoopGroup) groupClass.newInstance(); + channelType = channelClass; + break; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + default: { + // Should never get here. + throw new IllegalArgumentException("Unsupported transport: " + config.transport); + } + } - private void printUsage() { - System.out.println( - "Usage: [ARGS...]" - + "\n" - + "\n --port=INT Port of the server. Required. No default." - + "\n --tls Enable TLS. Default disabled." - + "\n --directexecutor Use a direct executor i.e. execute all RPC" - + "\n calls directly in Netty's event loop" - + "\n overhead of a thread pool." - + "\n --netty_native_transport Whether to use Netty's native transport." - + "\n Only supported on linux." - + "\n --connection_window=BYTES The HTTP/2 connection flow control window." - + "\n Default " + connectionWindow + " byte." - + "\n --stream_window=BYTES The HTTP/2 per-stream flow control window." - + "\n Default " + streamWindow + " byte." - ); + return NettyServerBuilder + .forAddress(config.address) + .bossEventLoopGroup(boss) + .workerEventLoopGroup(worker) + .channelType(channelType) + .addService(TestServiceGrpc.bindService(new TestServiceImpl())) + .sslContext(sslContext) + .executor(config.directExecutor ? MoreExecutors.newDirectExecutorService() : null) + .connectionWindowSize(config.connectionWindow) + .streamWindowSize(config.streamWindow) + .build(); } private static class TestServiceImpl implements TestServiceGrpc.TestService { @@ -243,12 +206,6 @@ public class AsyncServer { } private static SimpleResponse buildSimpleResponse(SimpleRequest request) { - if (!request.hasResponseSize()) { - throw Status.INTERNAL.augmentDescription("responseSize required").asRuntimeException(); - } - if (!request.hasResponseType()) { - throw Status.INTERNAL.augmentDescription("responseType required").asRuntimeException(); - } if (request.getResponseSize() > 0) { if (!PayloadType.COMPRESSABLE.equals(request.getResponseType())) { throw Status.INTERNAL.augmentDescription("Error creating payload.").asRuntimeException(); diff --git a/benchmarks/src/main/java/io/grpc/benchmarks/qps/ClientConfiguration.java b/benchmarks/src/main/java/io/grpc/benchmarks/qps/ClientConfiguration.java index 71a138d78e..9a131c35f9 100644 --- a/benchmarks/src/main/java/io/grpc/benchmarks/qps/ClientConfiguration.java +++ b/benchmarks/src/main/java/io/grpc/benchmarks/qps/ClientConfiguration.java @@ -31,51 +31,50 @@ package io.grpc.benchmarks.qps; -import static grpc.testing.Qpstest.RpcType.STREAMING; -import static grpc.testing.Qpstest.RpcType.UNARY; -import static io.grpc.benchmarks.qps.ClientConfiguration.Builder.Option.HELP; +import static io.grpc.benchmarks.qps.SocketAddressValidator.INET; +import static io.grpc.benchmarks.qps.SocketAddressValidator.UDS; +import static io.grpc.benchmarks.qps.Utils.parseBoolean; +import static io.grpc.testing.RpcType.STREAMING; +import static io.grpc.testing.RpcType.UNARY; import static java.lang.Integer.parseInt; -import static java.lang.Math.max; import static java.util.Arrays.asList; -import com.google.common.base.Strings; - -import grpc.testing.Qpstest.PayloadType; -import grpc.testing.Qpstest.RpcType; +import io.grpc.testing.PayloadType; +import io.grpc.testing.RpcType; import io.grpc.transport.netty.NettyChannelBuilder; -import java.util.HashSet; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.UnknownHostException; +import java.util.Collection; +import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; /** - * Configuration options for the client implementations. + * Configuration options for benchmark clients. */ -class ClientConfiguration { +class ClientConfiguration implements Configuration { + private static final String TESTCA_HOST = "foo.test.google.fr"; + private static final ClientConfiguration DEFAULT = new ClientConfiguration(); - // The histogram can record values between 1 microsecond and 1 min. - static final long HISTOGRAM_MAX_VALUE = 60000000L; - // Value quantization will be no larger than 1/10^3 = 0.1%. - static final int HISTOGRAM_PRECISION = 3; - - boolean okhttp; + Transport transport = Transport.NETTY_NIO; boolean tls; boolean testca; boolean directExecutor; - boolean nettyNativeTransport; - int port; + SocketAddress address; int channels = 4; int outstandingRpcsPerChannel = 10; int serverPayload; int clientPayload; int connectionWindow = NettyChannelBuilder.DEFAULT_CONNECTION_WINDOW_SIZE; - int streamWindow = NettyChannelBuilder.DEFAULT_CONNECTION_WINDOW_SIZE; + int streamWindow = NettyChannelBuilder.DEFAULT_STREAM_WINDOW_SIZE; // seconds int duration = 60; // seconds int warmupDuration = 10; int targetQps; - String host; String histogramFile; RpcType rpcType = UNARY; PayloadType payloadType = PayloadType.COMPRESSABLE; @@ -83,240 +82,269 @@ class ClientConfiguration { private ClientConfiguration() { } - static Builder newBuilder() { - return new Builder(); + /** + * Constructs a builder for configuring a client application with supported parameters. If no + * parameters are provided, all parameters are assumed to be supported. + */ + static Builder newBuilder(ClientParam... supportedParams) { + return new Builder(supportedParams); } - static class Builder { + static class Builder extends AbstractConfigurationBuilder { + private final Collection supportedParams; - private final Set