com.caucho.network.balance.ClientNetworkState Maven / Gradle / Ivy
/*
* Copyright (c) 1998-2018 Caucho Technology -- all rights reserved
*
* This file is part of Resin(R) Open Source
*
* Each copy or derived work must preserve the copyright notice and this
* notice unmodified.
*
* Resin Open Source is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Resin Open Source is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
* of NON-INFRINGEMENT. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with Resin Open Source; if not, write to the
*
* Free Software Foundation, Inc.
* 59 Temple Place, Suite 330
* Boston, MA 02111-1307 USA
*
* @author Scott Ferguson
*/
package com.caucho.network.balance;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import com.caucho.util.CurrentTime;
/**
* State of a network connection to a target server.
*
* Fail Recover Time
*
* The fail recover time is dynamic. The first timeout is 1s. After the 1s,
* the client tries again. If that fails, the timeout is doubled until
* reaching the maximum _loadBalanceRecoverTime.
*/
public class ClientNetworkState
{
private final String _id;
private final long _recoverTimeout;
private final AtomicReference _state
= new AtomicReference(State.ACTIVE);
private AtomicLong _dynamicRecoverTimeout
= new AtomicLong();
private AtomicInteger _connectionCount = new AtomicInteger();
// load management data
private volatile long _firstFailTime;
private volatile long _lastFailTime;
private volatile long _firstSuccessTime;
public ClientNetworkState(String id,
long recoverTimeout)
{
_id = id;
_recoverTimeout = recoverTimeout;
}
/**
* Returns the user-readable id of the target server.
*/
public String getId()
{
return _id;
}
/**
* Return the max recover time.
*/
public long getRecoverTimeout()
{
return _recoverTimeout;
}
/**
* Returns the lifecycle state.
*/
public final String getState()
{
// updateWarmup();
return String.valueOf(_state);
}
/**
* Returns true if the server is active.
*/
public final boolean isActive()
{
return _state.get().isActive();
}
/**
* Return true if enabled.
*/
public boolean isEnabled()
{
return _state.get().isEnabled();
}
/**
* Returns true if the server is dead.
*/
public final boolean isDead()
{
return ! isActive();
}
//
// action callbacks
//
/**
* Enable the client.
*/
public void enable()
{
toState(State.ENABLED);
}
/**
* Disable the client.
*/
public void disable()
{
toState(State.DISABLED);
_firstSuccessTime = 0;
}
/**
* Called when the server has a successful response
*/
public void onSuccess()
{
if (_firstSuccessTime <= 0) {
_firstSuccessTime = CurrentTime.getCurrentTime();
}
// reset the connection fail recover time
_dynamicRecoverTimeout.set(1000L);
_firstFailTime = 0;
}
/**
* Called when the connection fails.
*/
public void onFail()
{
_lastFailTime = CurrentTime.getCurrentTime();
if (_firstFailTime == 0) {
_firstFailTime = _lastFailTime;
}
_firstSuccessTime = 0;
toState(State.FAIL);
long recoverTimeout = _dynamicRecoverTimeout.get();
long nextRecoverTimeout = Math.min(recoverTimeout + 1000L, _recoverTimeout);
_dynamicRecoverTimeout.compareAndSet(recoverTimeout, nextRecoverTimeout);
}
/**
* Start a new connection. Returns true if the connection can be
* started.
*/
public boolean startConnection()
{
State state = _state.get();
if (state.isActive()) {
// when active, always start a connection
_connectionCount.incrementAndGet();
return true;
}
long now = CurrentTime.getCurrentTime();
long lastFailTime = _lastFailTime;
long recoverTimeout = _dynamicRecoverTimeout.get();
if (now < lastFailTime + recoverTimeout) {
// if the fail recover hasn't timed out, return false
return false;
}
// when fail, only start a single connection
int count;
do {
count = _connectionCount.get();
if (count > 0) {
return false;
}
} while (! _connectionCount.compareAndSet(count, count + 1));
return true;
}
public void completeConnection()
{
_connectionCount.decrementAndGet();
}
/**
* Close the client
*/
public void close()
{
toState(State.CLOSED);
}
private State toState(State targetState)
{
State oldState;
State newState;
do {
oldState = _state.get();
newState = targetState.toState(oldState);
} while (! _state.compareAndSet(oldState, newState));
return _state.get();
}
@Override
public String toString()
{
return (getClass().getSimpleName() + "[" + getId() + "]");
}
enum State {
ACTIVE {
boolean isActive() { return true; }
boolean isEnabled() { return true; }
State toState(State state) { return state.toActive(); }
State toFail() { return FAIL; }
State toDisable() { return DISABLED; }
},
FAIL {
boolean isEnabled() { return true; }
State toState(State state) { return state.toFail(); }
State toActive() { return ACTIVE; }
State toDisable() { return DISABLED; }
},
DISABLED {
State toState(State state) { return state.toDisable(); }
State toActive() { return this; }
State toFail() { return this; }
State toEnable() { return State.ACTIVE; }
},
ENABLED {
State toState(State state) { return state.toEnable(); }
},
CLOSED {
boolean isClosed() { return true; }
State toState(State state) { return CLOSED; }
State toActive() { return this; }
State toFail() { return this; }
State toEnable() { return this; }
State toDisable() { return this; }
};
boolean isActive() { return false; }
boolean isEnabled() { return false; }
boolean isClosed() { return false; }
State toActive() { return this; }
State toFail() { return this; }
State toEnable() { return this; }
State toDisable() { return DISABLED; }
State toState(State state)
{
throw new UnsupportedOperationException(toString());
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy