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

com.netflix.zuul.netty.connectionpool.PooledConnection Maven / Gradle / Ivy

There is a newer version: 2.5.13
Show newest version
/*
 * Copyright 2018 Netflix, Inc.
 *
 *      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.netflix.zuul.netty.connectionpool;

import com.netflix.spectator.api.Counter;
import com.netflix.zuul.discovery.DiscoveryResult;
import com.netflix.zuul.passport.CurrentPassport;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPipeline;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.util.AttributeKey;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by saroskar on 3/15/16.
 */
public class PooledConnection {

    public static final AttributeKey CHANNEL_ATTR = AttributeKey.newInstance("_pooled_connection");
    public static final String READ_TIMEOUT_HANDLER_NAME = "readTimeoutHandler";

    private final DiscoveryResult server;
    private final Channel channel;
    private final ClientChannelManager channelManager;
    private final long creationTS;
    private final Counter closeConnCounter;
    private final Counter closeWrtBusyConnCounter;

    private static final Logger LOG = LoggerFactory.getLogger(PooledConnection.class);

    /**
     * Connection State
     */
    public enum ConnectionState {
        /**
         * valid state in pool
         */
        WRITE_READY,
        /**
         * Can not be put in pool
         */
        WRITE_BUSY
    }

    private ConnectionState connectionState;
    private long usageCount = 0;
    private long reqStartTime;
    private boolean inPool = false;
    private boolean shouldClose = false;
    protected boolean released = false;

    public PooledConnection(
            final Channel channel,
            final DiscoveryResult server,
            final ClientChannelManager channelManager,
            final Counter closeConnCounter,
            final Counter closeWrtBusyConnCounter) {
        this.channel = channel;
        this.server = server;
        this.channelManager = channelManager;
        this.creationTS = System.currentTimeMillis();
        this.closeConnCounter = closeConnCounter;
        this.closeWrtBusyConnCounter = closeWrtBusyConnCounter;

        this.connectionState = ConnectionState.WRITE_READY;

        // Store ourself as an attribute on the underlying Channel.
        channel.attr(CHANNEL_ATTR).set(this);
    }

    public void setInUse() {
        this.connectionState = ConnectionState.WRITE_BUSY;
        this.released = false;
    }

    public void setConnectionState(ConnectionState state) {
        this.connectionState = state;
    }

    public static PooledConnection getFromChannel(Channel ch) {
        return ch.attr(CHANNEL_ATTR).get();
    }

    public ConnectionPoolConfig getConfig() {
        return this.channelManager.getConfig();
    }

    public DiscoveryResult getServer() {
        return server;
    }

    public Channel getChannel() {
        return channel;
    }

    public long getUsageCount() {
        return usageCount;
    }

    public void incrementUsageCount() {
        this.usageCount++;
    }

    public long getCreationTS() {
        return creationTS;
    }

    public long getAgeInMillis() {
        return System.currentTimeMillis() - creationTS;
    }

    public void startRequestTimer() {
        reqStartTime = System.nanoTime();
    }

    public long stopRequestTimer() {
        final long responseTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - reqStartTime);
        server.noteResponseTime(responseTime);
        return responseTime;
    }

    public boolean isActive() {
        return (channel.isActive() && channel.isRegistered());
    }

    public boolean isInPool() {
        return inPool;
    }

    public void setInPool(boolean inPool) {
        this.inPool = inPool;
    }

    public boolean isShouldClose() {
        return shouldClose;
    }

    public void flagShouldClose() {
        this.shouldClose = true;
    }

    public ChannelFuture close() {
        server.decrementOpenConnectionsCount();
        closeConnCounter.increment();
        return channel.close();
    }

    public void updateServerStats() {
        server.decrementOpenConnectionsCount();
        server.stopPublishingStats();
    }

    public ChannelFuture closeAndRemoveFromPool() {
        channelManager.remove(this);
        return this.close();
    }

    public boolean release() {
        if (released) {
            return true;
        }

        if (isActive()) {
            if (connectionState != ConnectionState.WRITE_READY) {
                closeWrtBusyConnCounter.increment();
            }
        }

        if (!isShouldClose() && connectionState != ConnectionState.WRITE_READY) {
            CurrentPassport passport = CurrentPassport.fromChannel(channel);
            LOG.info(
                    "Error - Attempt to put busy connection into the pool = {}, {}",
                    this.toString(),
                    String.valueOf(passport));
            this.shouldClose = true;
        }

        // reset the connectionState
        connectionState = ConnectionState.WRITE_READY;
        released = true;
        return channelManager.release(this);
    }

    public void removeReadTimeoutHandler() {
        // Remove (and therefore destroy) the readTimeoutHandler when we release the
        // channel back to the pool. As don't want it timing-out when it's not in use.
        final ChannelPipeline pipeline = getChannel().pipeline();
        removeHandlerFromPipeline(READ_TIMEOUT_HANDLER_NAME, pipeline);
    }

    private void removeHandlerFromPipeline(String handlerName, ChannelPipeline pipeline) {
        if (pipeline.get(handlerName) != null) {
            pipeline.remove(handlerName);
        }
    }

    public void startReadTimeoutHandler(Duration readTimeout) {
        getChannel()
                .pipeline()
                .addBefore(
                        DefaultOriginChannelInitializer.ORIGIN_NETTY_LOGGER,
                        READ_TIMEOUT_HANDLER_NAME,
                        new ReadTimeoutHandler(readTimeout.toMillis(), TimeUnit.MILLISECONDS));
    }

    ConnectionState getConnectionState() {
        return connectionState;
    }

    boolean isReleased() {
        return released;
    }

    @Override
    public String toString() {
        return "PooledConnection{" + "channel=" + channel + ", usageCount=" + usageCount + '}';
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy