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

com.aerospike.client.proxy.grpc.GrpcClientPolicy Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2012-2023 Aerospike, Inc.
 *
 * Portions may be licensed to Aerospike, Inc. under one or more contributor
 * license agreements WHICH ARE COMPATIBLE WITH THE APACHE LICENSE, VERSION 2.0.
 *
 * 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.
 */
package com.aerospike.client.proxy.grpc;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import javax.annotation.Nullable;

import com.aerospike.client.policy.ClientPolicy;
import com.aerospike.client.policy.TlsPolicy;

import io.grpc.CallOptions;
import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;

/**
 * gRPC Aerospike proxy client policy. All the knobs and configs that
 * affect the working of the gRPC proxy client are combined into this policy
 * object.
 */
public class GrpcClientPolicy {
	/**
	 * The event loops to process the gRPC HTTP/2 requests.
	 */
	public final List eventLoops;

	/**
	 * The type of the eventLoops.
	 */
	public final Class channelType;

	/**
	 * Should the event loops be closed in close.
	 */
	public final boolean closeEventLoops;

	/**
	 * Maximum number of HTTP/2 channels (connections) to open to the Aerospike
	 * gRPC proxy server.
	 * 

* Generally HTTP/2 based gRPC recommends a single channel to be * sufficient for most purposes. In our performance experiments we have * found that any number greater than 8 yields no extra * performance gains. */ public final int maxChannels; /** * Maximum number of concurrent HTTP/2 streams to have in-flight per HTTP/2 * channel (connection). *

* Generally HTTP/2 servers restrict the number of concurrent HTTP/2 streams * to about a 100 on a channel (connection). */ public final int maxConcurrentStreamsPerChannel; /** * Maximum number of concurrent requests that are in-flight per streaming * HTTP/2 call. *

* The Aerospike gRPC proxy server implements streaming for unary calls * like Aerospike get, put, operate, etc to improve latency and throughput. * maxConcurrentRequestsPerStream specifies the number of * concurrent requests that can be sent on a single unary call based stream. *

* NOTE: This policy does not apply to queries, scans, etc. */ public final int maxConcurrentRequestsPerStream; /** * Total number of HTTP/2 requests that are sent on a stream, after which * the stream is closed. *

* The Aerospike gRPC proxy server implements streaming for unary calls * like Aerospike get, put, operate, etc to improve latency and throughput. * totalRequestsPerStream specifies the total number of * requests that are sent on the stream, after which the stream is closed. *

* Requests to the Aerospike gRPC proxy server will be routed through a * HTTP/2 load balancer over the public internet. HTTP/2 load balancer * splits requests on a single HTTP/2 channel (connection) across the proxy * servers, but it will send all requests on a HTTP/2 stream to a single * gRPC Aerospike proxy server. This policy ensures that the requests are * evenly load balanced across the gRPC Aerospike proxy servers. *

* NOTE: This policy does not apply to queries, scans, etc. */ public final int totalRequestsPerStream; /** * The connection timeout in milliseconds when creating a new HTTP/2 * channel (connection) to a gRPC Aerospike proxy server. */ public final int connectTimeoutMillis; /** * See {@link ClientPolicy#closeTimeout}. */ public final int closeTimeout; /** * Strategy to select a channel for a gRPC request. */ public final GrpcChannelSelector grpcChannelSelector; /** * Strategy to select a stream for a gRPC request. */ public final GrpcStreamSelector grpcStreamSelector; /** * Call options. */ public final CallOptions callOptions; /** * The TLS policy to connect to the gRPC Aerospike proxy server. *

* NOTE: The channel (connection) will be non-encrypted if this policy is null. */ @Nullable public final TlsPolicy tlsPolicy; /** * Milliseconds to wait for termination of the channels. Should be * greater than the deadlines. The implementation is best-effort, its * possible termination takes more time than this. */ public final long terminationWaitMillis; /** * Index to get the next event loop. */ private final AtomicInteger eventLoopIndex = new AtomicInteger(0); private GrpcClientPolicy( int maxChannels, int maxConcurrentStreamsPerChannel, int maxConcurrentRequestsPerStream, int totalRequestsPerStream, int connectTimeoutMillis, long terminationWaitMillis, int closeTimeout, GrpcChannelSelector grpcChannelSelector, GrpcStreamSelector grpcStreamSelector, CallOptions callOptions, List eventLoops, Class channelType, boolean closeEventLoops, @Nullable TlsPolicy tlsPolicy ) { this.maxChannels = maxChannels; this.maxConcurrentStreamsPerChannel = maxConcurrentStreamsPerChannel; this.maxConcurrentRequestsPerStream = maxConcurrentRequestsPerStream; this.totalRequestsPerStream = totalRequestsPerStream; this.connectTimeoutMillis = connectTimeoutMillis; this.terminationWaitMillis = terminationWaitMillis; this.closeTimeout = closeTimeout; this.grpcChannelSelector = grpcChannelSelector; this.grpcStreamSelector = grpcStreamSelector; this.callOptions = callOptions; this.eventLoops = eventLoops; this.channelType = channelType; this.closeEventLoops = closeEventLoops; this.tlsPolicy = tlsPolicy; } public static Builder newBuilder( @Nullable List eventLoops, @Nullable Class channelType ) { Builder builder = new Builder(); if (eventLoops == null || channelType == null) { builder.closeEventLoops = true; DefaultThreadFactory tf = new DefaultThreadFactory("aerospike-proxy", true /*daemon */); // TODO: select number of event loop threads? EventLoopGroup eventLoopGroup; if (Epoll.isAvailable()) { eventLoopGroup = new EpollEventLoopGroup(0, tf); builder.channelType = EpollSocketChannel.class; } else { eventLoopGroup = new NioEventLoopGroup(0, tf); builder.channelType = NioSocketChannel.class; } builder.eventLoops = StreamSupport.stream(eventLoopGroup.spliterator(), false) .map(eventExecutor -> (EventLoop)eventExecutor) .collect(Collectors.toList()); } else { builder.channelType = channelType; builder.eventLoops = eventLoops; builder.closeEventLoops = false; } // TODO: justify defaults. builder.maxChannels = 8; // Multiple requests should be sent on a stream at once to enhance // performance. So `maxConcurrentRequestsPerStream` should be the // ideal batch size of the requests. // TODO: maybe these parameters depend on the payload size? builder.maxConcurrentStreamsPerChannel = 8; builder.maxConcurrentRequestsPerStream = builder.totalRequestsPerStream = 128; builder.connectTimeoutMillis = 5000; builder.terminationWaitMillis = 30000; builder.tlsPolicy = null; return builder; } public EventLoop nextEventLoop() { int i = eventLoopIndex.getAndIncrement() % eventLoops.size(); return eventLoops.get(i); } public static class Builder { private List eventLoops; private Class channelType; private boolean closeEventLoops; private int maxChannels; private int maxConcurrentStreamsPerChannel; private int maxConcurrentRequestsPerStream; private int totalRequestsPerStream; private int connectTimeoutMillis; @Nullable private TlsPolicy tlsPolicy; @Nullable private GrpcChannelSelector grpcChannelSelector; @Nullable private GrpcStreamSelector grpcStreamSelector; @Nullable private CallOptions callOptions; private long terminationWaitMillis; private int closeTimeout; private Builder() { } public GrpcClientPolicy build() { if (grpcChannelSelector == null) { // TODO: how should low and high water mark be selected? int hwm = maxConcurrentStreamsPerChannel * maxConcurrentRequestsPerStream; int lwm = Math.max(16, (int)(0.8 * hwm)); grpcChannelSelector = new DefaultGrpcChannelSelector(lwm, hwm); } if (grpcStreamSelector == null) { grpcStreamSelector = new DefaultGrpcStreamSelector(maxConcurrentStreamsPerChannel, maxConcurrentRequestsPerStream, totalRequestsPerStream); } if (callOptions == null) { callOptions = CallOptions.DEFAULT; } return new GrpcClientPolicy(maxChannels, maxConcurrentStreamsPerChannel, maxConcurrentRequestsPerStream, totalRequestsPerStream, connectTimeoutMillis, terminationWaitMillis, closeTimeout, grpcChannelSelector, grpcStreamSelector, callOptions, eventLoops, channelType, closeEventLoops, tlsPolicy); } public Builder maxChannels(int maxChannels) { if (maxChannels < 1) { throw new IllegalArgumentException(String.format( "maxChannels=%d < 1", maxChannels )); } this.maxChannels = maxChannels; return this; } public Builder maxConcurrentStreamsPerChannel(int maxConcurrentStreamsPerChannel) { if (maxConcurrentStreamsPerChannel < 1) { throw new IllegalArgumentException(String.format( "maxConcurrentStreamsPerChannel=%d < 1", maxConcurrentStreamsPerChannel )); } this.maxConcurrentStreamsPerChannel = maxConcurrentStreamsPerChannel; return this; } public Builder maxConcurrentRequestsPerStream(int maxConcurrentRequestsPerStream) { if (maxConcurrentRequestsPerStream < 1) { throw new IllegalArgumentException(String.format( "maxConcurrentRequestsPerStream=%d < 1", maxConcurrentRequestsPerStream )); } this.maxConcurrentRequestsPerStream = maxConcurrentRequestsPerStream; return this; } public Builder totalRequestsPerStream(int totalRequestsPerStream) { if (totalRequestsPerStream < 0) { throw new IllegalArgumentException(String.format( "totalRequestsPerStream=%d < 0", totalRequestsPerStream )); } this.totalRequestsPerStream = totalRequestsPerStream; return this; } public Builder connectTimeoutMillis(int connectTimeoutMillis) { if (connectTimeoutMillis < 0) { throw new IllegalArgumentException(String.format( "connectTimeoutMillis=%d < 0", connectTimeoutMillis )); } this.connectTimeoutMillis = connectTimeoutMillis; return this; } public Builder closeTimeout(int closeTimeout) { this.closeTimeout = closeTimeout; return this; } public Builder tlsPolicy(@Nullable TlsPolicy tlsPolicy) { this.tlsPolicy = tlsPolicy; return this; } public Builder grpcChannelSelector(GrpcChannelSelector grpcChannelSelector) { this.grpcChannelSelector = grpcChannelSelector; return this; } public Builder grpcStreamSelector(GrpcStreamSelector grpcStreamSelector) { this.grpcStreamSelector = grpcStreamSelector; return this; } public Builder callOptions(@Nullable CallOptions callOptions) { this.callOptions = callOptions; return this; } public Builder terminationWaitMillis(long terminationWaitMillis) { if (terminationWaitMillis < 0) { throw new IllegalArgumentException(String.format( "terminationWaitMillis=%d < 0", terminationWaitMillis )); } this.terminationWaitMillis = terminationWaitMillis; return this; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy