All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nl.topicus.jdbc.shaded.io.grpc.netty.NettyChannelBuilder Maven / Gradle / Ivy

There is a newer version: 1.1.6
Show newest version
/*
 * Copyright 2014, 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 nl.topicus.jdbc.shaded.io.grpc.nl.topicus.jdbc.shaded.net.y;

import static nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.base.Preconditions.checkArgument;
import static nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.base.Preconditions.checkNotNull;
import static nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.base.Preconditions.checkState;
import static nl.topicus.jdbc.shaded.io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
import static nl.topicus.jdbc.shaded.io.grpc.internal.GrpcUtil.DEFAULT_KEEPALIVE_TIME_NANOS;
import static nl.topicus.jdbc.shaded.io.grpc.internal.GrpcUtil.KEEPALIVE_TIME_NANOS_DISABLED;

import nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.annotations.VisibleForTesting;
import nl.topicus.jdbc.shaded.com.google.nl.topicus.jdbc.shaded.com.on.base.Preconditions;
import nl.topicus.jdbc.shaded.com.google.errorprone.annotations.CanIgnoreReturnValue;
import nl.topicus.jdbc.shaded.io.grpc.Attributes;
import nl.topicus.jdbc.shaded.io.grpc.ExperimentalApi;
import nl.topicus.jdbc.shaded.io.grpc.Internal;
import nl.topicus.jdbc.shaded.io.grpc.NameResolver;
import nl.topicus.jdbc.shaded.io.grpc.internal.AbstractManagedChannelImplBuilder;
import nl.topicus.jdbc.shaded.io.grpc.internal.AtomicBackoff;
import nl.topicus.jdbc.shaded.io.grpc.internal.ClientTransportFactory;
import nl.topicus.jdbc.shaded.io.grpc.internal.ConnectionClientTransport;
import nl.topicus.jdbc.shaded.io.grpc.internal.GrpcUtil;
import nl.topicus.jdbc.shaded.io.grpc.internal.KeepAliveManager;
import nl.topicus.jdbc.shaded.io.grpc.internal.SharedResourceHolder;
import nl.topicus.jdbc.shaded.io.nl.topicus.jdbc.shaded.net.y.channel.Channel;
import nl.topicus.jdbc.shaded.io.nl.topicus.jdbc.shaded.net.y.channel.ChannelOption;
import nl.topicus.jdbc.shaded.io.nl.topicus.jdbc.shaded.net.y.channel.EventLoopGroup;
import nl.topicus.jdbc.shaded.io.nl.topicus.jdbc.shaded.net.y.channel.socket.nio.NioSocketChannel;
import nl.topicus.jdbc.shaded.io.nl.topicus.jdbc.shaded.net.y.handler.ssl.SslContext;
import java.nl.topicus.jdbc.shaded.net.InetSocketAddress;
import java.nl.topicus.jdbc.shaded.net.SocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import nl.topicus.jdbc.shaded.javax.annotation.CheckReturnValue;
import nl.topicus.jdbc.shaded.javax.annotation.Nullable;
import javax.nl.topicus.jdbc.shaded.net.ssl.SSLException;

/**
 * A builder to help simplify construction of channels using the Netty transport.
 */
@ExperimentalApi("https://github.nl.topicus.jdbc.shaded.com.grpc/grpc-java/issues/1784")
@CanIgnoreReturnValue
public final class NettyChannelBuilder
    extends AbstractManagedChannelImplBuilder {
  public static final int DEFAULT_FLOW_CONTROL_WINDOW = 1048576; // 1MiB

  private static final long AS_LARGE_AS_INFINITE = TimeUnit.DAYS.toNanos(1000L);

  private final Map, Object> channelOptions =
      new HashMap, Object>();

  private NegotiationType negotiationType = NegotiationType.TLS;
  private OverrideAuthorityChecker authorityChecker;
  private Class channelType = NioSocketChannel.class;

  @Nullable
  private EventLoopGroup eventLoopGroup;
  private SslContext sslContext;
  private int flowControlWindow = DEFAULT_FLOW_CONTROL_WINDOW;
  private int maxHeaderListSize = GrpcUtil.DEFAULT_MAX_HEADER_LIST_SIZE;
  private long keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED;
  private long keepAliveTimeoutNanos = DEFAULT_KEEPALIVE_TIMEOUT_NANOS;
  private boolean keepAliveWithoutCalls;
  private TransportCreationParamsFilterFactory dynamicParamsFactory;

  /**
   * Creates a new builder with the given server address. This factory method is primarily intended
   * for using Netty Channel types other than SocketChannel. {@link #forAddress(String, int)} should
   * generally be preferred over this method, since that API permits delaying DNS lookups and
   * noticing changes to DNS.
   */
  @CheckReturnValue
  public static NettyChannelBuilder forAddress(SocketAddress serverAddress) {
    return new NettyChannelBuilder(serverAddress);
  }

  /**
   * Creates a new builder with the given host and port.
   */
  @CheckReturnValue
  public static NettyChannelBuilder forAddress(String host, int port) {
    return new NettyChannelBuilder(host, port);
  }

  /**
   * Creates a new builder with the given target string that will be resolved by
   * {@link nl.topicus.jdbc.shaded.io.grpc.NameResolver}.
   */
  @CheckReturnValue
  public static NettyChannelBuilder forTarget(String target) {
    return new NettyChannelBuilder(target);
  }

  @CheckReturnValue
  NettyChannelBuilder(String host, int port) {
    this(GrpcUtil.authorityFromHostAndPort(host, port));
  }

  @CheckReturnValue
  NettyChannelBuilder(String target) {
    super(target);
  }

  @CheckReturnValue
  NettyChannelBuilder(SocketAddress address) {
    super(address, getAuthorityFromAddress(address));
  }

  @CheckReturnValue
  private static String getAuthorityFromAddress(SocketAddress address) {
    if (address instanceof InetSocketAddress) {
      InetSocketAddress inetAddress = (InetSocketAddress) address;
      return GrpcUtil.authorityFromHostAndPort(inetAddress.getHostString(), inetAddress.getPort());
    } else {
      return address.toString();
    }
  }

  /**
   * Specifies the channel type to use, by default we use {@link NioSocketChannel}.
   */
  public NettyChannelBuilder channelType(Class channelType) {
    this.channelType = Preconditions.checkNotNull(channelType, "channelType");
    return this;
  }

  /**
   * Specifies a channel option. As the underlying channel as well as nl.topicus.jdbc.shaded.net.ork implementation may
   * ignore this value applications should consider it a hint.
   */
  public  NettyChannelBuilder withOption(ChannelOption option, T value) {
    channelOptions.put(option, value);
    return this;
  }

  /**
   * Sets the negotiation type for the HTTP/2 connection.
   *
   * 

Default: TLS */ public NettyChannelBuilder negotiationType(NegotiationType type) { negotiationType = type; return this; } /** * Provides an EventGroupLoop to be used by the nl.topicus.jdbc.shaded.net.y transport. * *

It's an optional parameter. If the user has not provided an EventGroupLoop when the channel * is built, the builder will use the default one which is static. * *

The channel won't take ownership of the given EventLoopGroup. It's caller's responsibility * to shut it down when it's desired. */ public NettyChannelBuilder eventLoopGroup(@Nullable EventLoopGroup eventLoopGroup) { this.eventLoopGroup = eventLoopGroup; return this; } /** * SSL/TLS context to use instead of the system default. It must have been configured with {@link * GrpcSslContexts}, but options could have been overridden. */ public NettyChannelBuilder sslContext(SslContext sslContext) { if (sslContext != null) { checkArgument(sslContext.isClient(), "Server SSL context can not be used for client channel"); GrpcSslContexts.ensureAlpnAndH2Enabled(sslContext.applicationProtocolNegotiator()); } this.sslContext = sslContext; return this; } /** * Sets the flow control window in bytes. If not called, the default value * is {@link #DEFAULT_FLOW_CONTROL_WINDOW}). */ public NettyChannelBuilder flowControlWindow(int flowControlWindow) { checkArgument(flowControlWindow > 0, "flowControlWindow must be positive"); this.flowControlWindow = flowControlWindow; return this; } /** * Sets the max message size. * * @deprecated Use {@link #maxInboundMessageSize} instead */ @Deprecated public NettyChannelBuilder maxMessageSize(int maxMessageSize) { maxInboundMessageSize(maxMessageSize); return this; } /** * Sets the maximum size of header list allowed to be received on the channel. If not called, * defaults to {@link GrpcUtil#DEFAULT_MAX_HEADER_LIST_SIZE}. */ public NettyChannelBuilder maxHeaderListSize(int maxHeaderListSize) { checkArgument(maxHeaderListSize > 0, "maxHeaderListSize must be > 0"); this.maxHeaderListSize = maxHeaderListSize; return this; } /** * Equivalent to using {@link #negotiationType(NegotiationType)} with {@code PLAINTEXT} or * {@code PLAINTEXT_UPGRADE}. */ @Override public NettyChannelBuilder usePlaintext(boolean skipNegotiation) { if (skipNegotiation) { negotiationType(NegotiationType.PLAINTEXT); } else { negotiationType(NegotiationType.PLAINTEXT_UPGRADE); } return this; } /** * Enable keepalive with default delay and timeout. * * @deprecated Please use {@link #keepAliveTime} and {@link #keepAliveTimeout} instead */ @Deprecated public final NettyChannelBuilder enableKeepAlive(boolean enable) { if (enable) { return keepAliveTime(DEFAULT_KEEPALIVE_TIME_NANOS, TimeUnit.NANOSECONDS); } return keepAliveTime(KEEPALIVE_TIME_NANOS_DISABLED, TimeUnit.NANOSECONDS); } /** * Enable keepalive with custom delay and timeout. * * @deprecated Please use {@link #keepAliveTime} and {@link #keepAliveTimeout} instead */ @Deprecated public final NettyChannelBuilder enableKeepAlive(boolean enable, long keepAliveTime, TimeUnit delayUnit, long keepAliveTimeout, TimeUnit timeoutUnit) { if (enable) { return keepAliveTime(keepAliveTime, delayUnit) .keepAliveTimeout(keepAliveTimeout, timeoutUnit); } return keepAliveTime(KEEPALIVE_TIME_NANOS_DISABLED, TimeUnit.NANOSECONDS); } /** * Sets the time without read activity before sending a keepalive ping. An unreasonably small * value might be increased, and {@code Long.MAX_VALUE} nano seconds or an unreasonably large * value will disable keepalive. Defaults to infinite. * *

Clients must receive permission from the service owner before enabling this option. * Keepalives can increase the load on services and are nl.topicus.jdbc.shaded.com.only "invisible" making it hard to * notice when they are causing excessive load. Clients are strongly encouraged to use only as * small of a value as necessary. * * @since 1.3.0 */ public NettyChannelBuilder keepAliveTime(long keepAliveTime, TimeUnit timeUnit) { checkArgument(keepAliveTime > 0L, "keepalive time must be positive"); keepAliveTimeNanos = timeUnit.toNanos(keepAliveTime); keepAliveTimeNanos = KeepAliveManager.clampKeepAliveTimeInNanos(keepAliveTimeNanos); if (keepAliveTimeNanos >= AS_LARGE_AS_INFINITE) { // Bump keepalive time to infinite. This disables keepalive. keepAliveTimeNanos = KEEPALIVE_TIME_NANOS_DISABLED; } return this; } /** * Sets the time waiting for read activity after sending a keepalive ping. If the time expires * without any read activity on the connection, the connection is considered dead. An unreasonably * small value might be increased. Defaults to 20 seconds. * *

This value should be at least multiple times the RTT to allow for lost packets. * * @since 1.3.0 */ public NettyChannelBuilder keepAliveTimeout(long keepAliveTimeout, TimeUnit timeUnit) { checkArgument(keepAliveTimeout > 0L, "keepalive timeout must be positive"); keepAliveTimeoutNanos = timeUnit.toNanos(keepAliveTimeout); keepAliveTimeoutNanos = KeepAliveManager.clampKeepAliveTimeoutInNanos(keepAliveTimeoutNanos); return this; } /** * Sets whether keepalive will be performed when there are no outstanding RPC on a connection. * Defaults to {@code false}. * *

Clients must receive permission from the service owner before enabling this option. * Keepalives on unused connections can easilly accidentally consume a considerable amount of * bandwidth and CPU. * * @since 1.3.0 * @see #keepAliveTime(long, TimeUnit) */ public NettyChannelBuilder keepAliveWithoutCalls(boolean enable) { keepAliveWithoutCalls = enable; return this; } @Override @CheckReturnValue @Internal protected ClientTransportFactory buildTransportFactory() { return new NettyTransportFactory(dynamicParamsFactory, channelType, channelOptions, negotiationType, sslContext, eventLoopGroup, flowControlWindow, maxInboundMessageSize(), maxHeaderListSize, keepAliveTimeNanos, keepAliveTimeoutNanos, keepAliveWithoutCalls); } @Override @CheckReturnValue protected Attributes getNameResolverParams() { int defaultPort; switch (negotiationType) { case PLAINTEXT: case PLAINTEXT_UPGRADE: defaultPort = GrpcUtil.DEFAULT_PORT_PLAINTEXT; break; case TLS: defaultPort = GrpcUtil.DEFAULT_PORT_SSL; break; default: throw new AssertionError(negotiationType + " not handled"); } return Attributes.newBuilder() .set(NameResolver.Factory.PARAMS_DEFAULT_PORT, defaultPort).build(); } void overrideAuthorityChecker(@Nullable OverrideAuthorityChecker authorityChecker) { this.authorityChecker = authorityChecker; } @VisibleForTesting @CheckReturnValue static ProtocolNegotiator createProtocolNegotiator( String authority, NegotiationType negotiationType, SslContext sslContext) { ProtocolNegotiator negotiator = createProtocolNegotiatorByType(authority, negotiationType, sslContext); String proxy = System.getenv("GRPC_PROXY_EXP"); if (proxy != null) { String[] parts = proxy.split(":", 2); int port = 80; if (parts.length > 1) { port = Integer.parseInt(parts[1]); } InetSocketAddress proxyAddress = new InetSocketAddress(parts[0], port); negotiator = ProtocolNegotiators.httpProxy(proxyAddress, null, null, negotiator); } return negotiator; } @CheckReturnValue private static ProtocolNegotiator createProtocolNegotiatorByType( String authority, NegotiationType negotiationType, SslContext sslContext) { switch (negotiationType) { case PLAINTEXT: return ProtocolNegotiators.plaintext(); case PLAINTEXT_UPGRADE: return ProtocolNegotiators.plaintextUpgrade(); case TLS: return ProtocolNegotiators.tls(sslContext, authority); default: throw new IllegalArgumentException("Unsupported negotiationType: " + negotiationType); } } @CheckReturnValue interface OverrideAuthorityChecker { String checkAuthority(String authority); } @Override @CheckReturnValue @Internal protected String checkAuthority(String authority) { if (authorityChecker != null) { return authorityChecker.checkAuthority(authority); } return super.checkAuthority(authority); } void setDynamicParamsFactory(TransportCreationParamsFilterFactory factory) { this.dynamicParamsFactory = checkNotNull(factory, "factory"); } interface TransportCreationParamsFilterFactory { @CheckReturnValue TransportCreationParamsFilter create( SocketAddress targetServerAddress, String authority, @Nullable String userAgent); } @CheckReturnValue interface TransportCreationParamsFilter { SocketAddress getTargetServerAddress(); String getAuthority(); @Nullable String getUserAgent(); ProtocolNegotiator getProtocolNegotiator(); } /** * Creates Netty transports. Exposed for internal use, as it should be private. */ @CheckReturnValue private static final class NettyTransportFactory implements ClientTransportFactory { private final TransportCreationParamsFilterFactory transportCreationParamsFilterFactory; private final Class channelType; private final Map, ?> channelOptions; private final NegotiationType negotiationType; private final SslContext sslContext; private final EventLoopGroup group; private final boolean usingSharedGroup; private final int flowControlWindow; private final int maxMessageSize; private final int maxHeaderListSize; private final AtomicBackoff keepAliveTimeNanos; private final long keepAliveTimeoutNanos; private final boolean keepAliveWithoutCalls; private boolean closed; NettyTransportFactory(TransportCreationParamsFilterFactory transportCreationParamsFilterFactory, Class channelType, Map, ?> channelOptions, NegotiationType negotiationType, SslContext sslContext, EventLoopGroup group, int flowControlWindow, int maxMessageSize, int maxHeaderListSize, long keepAliveTimeNanos, long keepAliveTimeoutNanos, boolean keepAliveWithoutCalls) { this.channelType = channelType; this.negotiationType = negotiationType; this.channelOptions = new HashMap, Object>(channelOptions); if (negotiationType == NegotiationType.TLS && sslContext == null) { try { sslContext = GrpcSslContexts.forClient().build(); } catch (SSLException ex) { throw new RuntimeException(ex); } } this.sslContext = sslContext; if (transportCreationParamsFilterFactory == null) { transportCreationParamsFilterFactory = new TransportCreationParamsFilterFactory() { @Override public TransportCreationParamsFilter create( SocketAddress targetServerAddress, String authority, String userAgent) { return new DynamicNettyTransportParams(targetServerAddress, authority, userAgent); } }; } this.transportCreationParamsFilterFactory = transportCreationParamsFilterFactory; this.flowControlWindow = flowControlWindow; this.maxMessageSize = maxMessageSize; this.maxHeaderListSize = maxHeaderListSize; this.keepAliveTimeNanos = new AtomicBackoff("keepalive time nanos", keepAliveTimeNanos); this.keepAliveTimeoutNanos = keepAliveTimeoutNanos; this.keepAliveWithoutCalls = keepAliveWithoutCalls; usingSharedGroup = group == null; if (usingSharedGroup) { // The group was unspecified, using the shared group. this.group = SharedResourceHolder.get(Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP); } else { this.group = group; } } @Override public ConnectionClientTransport newClientTransport( SocketAddress serverAddress, String authority, @Nullable String userAgent) { checkState(!closed, "The transport factory is closed."); TransportCreationParamsFilter dparams = transportCreationParamsFilterFactory.create(serverAddress, authority, userAgent); final AtomicBackoff.State keepAliveTimeNanosState = keepAliveTimeNanos.getState(); Runnable tooManyPingsRunnable = new Runnable() { @Override public void run() { keepAliveTimeNanosState.backoff(); } }; NettyClientTransport transport = new NettyClientTransport( dparams.getTargetServerAddress(), channelType, channelOptions, group, dparams.getProtocolNegotiator(), flowControlWindow, maxMessageSize, maxHeaderListSize, keepAliveTimeNanosState.get(), keepAliveTimeoutNanos, keepAliveWithoutCalls, dparams.getAuthority(), dparams.getUserAgent(), tooManyPingsRunnable); return transport; } @Override public void close() { if (closed) { return; } closed = true; if (usingSharedGroup) { SharedResourceHolder.release(Utils.DEFAULT_WORKER_EVENT_LOOP_GROUP, group); } } @CheckReturnValue private final class DynamicNettyTransportParams implements TransportCreationParamsFilter { private final SocketAddress targetServerAddress; private final String authority; @Nullable private final String userAgent; private DynamicNettyTransportParams( SocketAddress targetServerAddress, String authority, String userAgent) { this.targetServerAddress = targetServerAddress; this.authority = authority; this.userAgent = userAgent; } @Override public SocketAddress getTargetServerAddress() { return targetServerAddress; } @Override public String getAuthority() { return authority; } @Override public String getUserAgent() { return userAgent; } @Override public ProtocolNegotiator getProtocolNegotiator() { return createProtocolNegotiator(authority, negotiationType, sslContext); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy