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

software.amazon.awssdk.http.nio.netty.internal.http2.Http2MultiplexedChannelPool Maven / Gradle / Ivy

Go to download

A single bundled dependency that includes all service and dependent JARs with third-party libraries relocated to different namespaces.

There is a newer version: 2.5.20
Show newest version
/*
 * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License").
 * You may not use this file except in compliance with the License.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. This file 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 software.amazon.awssdk.http.nio.netty.internal.http2;

import static software.amazon.awssdk.http.nio.netty.internal.ChannelAttributeKey.CHANNEL_POOL_RECORD;
import static software.amazon.awssdk.http.nio.netty.internal.utils.NettyUtils.doInEventLoop;

import io.netty.channel.Channel;
import io.netty.channel.EventLoop;
import io.netty.channel.pool.ChannelPool;
import io.netty.handler.codec.http2.Http2StreamChannelBootstrap;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.util.ArrayList;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.http.nio.netty.internal.utils.BetterFixedChannelPool;

/**
 * {@link ChannelPool} implementation that handles multiplexed streams. Child channels are created
 * for each HTTP/2 stream using {@link Http2StreamChannelBootstrap} with the parent channel being
 * the actual socket channel. This implementation assumes that all connections have the same setting
 * for MAX_CONCURRENT_STREAMS. Concurrent requests are load balanced across all available connections,
 * when the max concurrency for a connection is reached then a new connection will be opened.
 *
 * 

* Note: This enforces no max concurrency. Relies on being wrapped with a {@link BetterFixedChannelPool} * to enforce max concurrency which gives a bunch of other good features like timeouts, max pending acquires, etc. *

*/ @SdkInternalApi public class Http2MultiplexedChannelPool implements ChannelPool { private final EventLoop eventLoop; private final ChannelPool connectionPool; private final long maxConcurrencyPerConnection; private final ArrayList connections; /** * @param connectionPool Connection pool for parent channels (i.e. the socket channel). * @param eventLoop Event loop to run all tasks in. * @param maxConcurrencyPerConnection Max concurrent streams per HTTP/2 connection. */ Http2MultiplexedChannelPool(ChannelPool connectionPool, EventLoop eventLoop, long maxConcurrencyPerConnection) { this.connectionPool = connectionPool; this.eventLoop = eventLoop; this.maxConcurrencyPerConnection = maxConcurrencyPerConnection; // Customers that want an unbounded connection pool may set max concurrency to something like // Long.MAX_VALUE so we just stick with the initial ArrayList capacity and grow from there. this.connections = new ArrayList<>(); } @Override public Future acquire() { return acquire(new DefaultPromise<>(eventLoop)); } @Override public Future acquire(Promise promise) { doInEventLoop(eventLoop, () -> acquire0(promise), promise); return promise; } private Future acquire0(Promise promise) { for (MultiplexedChannelRecord connection : connections) { if (connection.availableStreams() > 0) { connection.acquire(promise); return promise; } } // No available streams, establish new connection and add it to list connections.add(new MultiplexedChannelRecord(connectionPool.acquire(), maxConcurrencyPerConnection, this::releaseParentChannel) .acquire(promise)); return promise; } /** * Releases parent channel on failure and cleans up record from connections list. * * @param parentChannel Channel to release. May be null if no channel is established. * @param record Record to cleanup. */ private void releaseParentChannel(Channel parentChannel, MultiplexedChannelRecord record) { doInEventLoop(eventLoop, () -> releaseParentChannel0(parentChannel, record)); } private void releaseParentChannel0(Channel parentChannel, MultiplexedChannelRecord record) { if (parentChannel != null) { try { parentChannel.close(); } finally { connectionPool.release(parentChannel); } } connections.remove(record); } @Override public Future release(Channel childChannel) { return release(childChannel, new DefaultPromise<>(eventLoop)); } @Override public Future release(Channel channel, Promise promise) { doInEventLoop(eventLoop, () -> release0(channel, promise), promise); return promise; } private void release0(Channel channel, Promise promise) { if (channel.parent() == null) { // This is the socket channel, close and release from underlying connection pool try { releaseParentChannel(channel); } finally { // This channel doesn't technically belong to this pool as it was never acquired directly promise.setFailure(new IllegalArgumentException("Channel does not belong to this pool")); } } else { Channel parentChannel = channel.parent(); MultiplexedChannelRecord channelRecord = parentChannel.attr(CHANNEL_POOL_RECORD).get(); channelRecord.release(channel); channel.close(); promise.setSuccess(null); } } private void releaseParentChannel(Channel parentChannel) { MultiplexedChannelRecord channelRecord = parentChannel.attr(CHANNEL_POOL_RECORD).get(); connections.remove(channelRecord); parentChannel.close(); connectionPool.release(parentChannel); } @Override public void close() { doInEventLoop(eventLoop, connectionPool::close); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy