forked from I2P_Developers/i2p.i2p
2004-10-09 jrandom
* Added a watchdog timer to do some baseline liveliness checking to help debug some odd errors. * Added a pair of summary stats for bandwidth usage, allowing easy export with the other stats ("bw.sendBps" and "bw.receiveBps") * Trimmed another memory allocation on message reception.
This commit is contained in:
@@ -1,4 +1,11 @@
|
|||||||
$Id: history.txt,v 1.40 2004/10/08 13:38:49 jrandom Exp $
|
$Id: history.txt,v 1.41 2004/10/08 17:53:03 jrandom Exp $
|
||||||
|
|
||||||
|
2004-10-09 jrandom
|
||||||
|
* Added a watchdog timer to do some baseline liveliness checking to help
|
||||||
|
debug some odd errors.
|
||||||
|
* Added a pair of summary stats for bandwidth usage, allowing easy export
|
||||||
|
with the other stats ("bw.sendBps" and "bw.receiveBps")
|
||||||
|
* Trimmed another memory allocation on message reception.
|
||||||
|
|
||||||
2004-10-08 jrandom
|
2004-10-08 jrandom
|
||||||
* Revamp the AESInputStream so it doesn't allocate any temporary objects
|
* Revamp the AESInputStream so it doesn't allocate any temporary objects
|
||||||
|
@@ -29,11 +29,12 @@ public interface I2NPMessage extends DataStructure {
|
|||||||
* @param in stream to read from
|
* @param in stream to read from
|
||||||
* @param type I2NP message type
|
* @param type I2NP message type
|
||||||
* @param buffer scratch buffer to be used when reading and parsing
|
* @param buffer scratch buffer to be used when reading and parsing
|
||||||
|
* @return size of the message read (including headers)
|
||||||
* @throws I2NPMessageException if the stream doesn't contain a valid message
|
* @throws I2NPMessageException if the stream doesn't contain a valid message
|
||||||
* that this class can read.
|
* that this class can read.
|
||||||
* @throws IOException if there is a problem reading from the stream
|
* @throws IOException if there is a problem reading from the stream
|
||||||
*/
|
*/
|
||||||
public void readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException;
|
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the body into the data structures, after the initial type byte and
|
* Read the body into the data structures, after the initial type byte and
|
||||||
|
@@ -26,11 +26,13 @@ public class I2NPMessageHandler {
|
|||||||
private I2PAppContext _context;
|
private I2PAppContext _context;
|
||||||
private long _lastReadBegin;
|
private long _lastReadBegin;
|
||||||
private long _lastReadEnd;
|
private long _lastReadEnd;
|
||||||
|
private int _lastSize;
|
||||||
private byte _messageBuffer[];
|
private byte _messageBuffer[];
|
||||||
public I2NPMessageHandler(I2PAppContext context) {
|
public I2NPMessageHandler(I2PAppContext context) {
|
||||||
_context = context;
|
_context = context;
|
||||||
_log = context.logManager().getLog(I2NPMessageHandler.class);
|
_log = context.logManager().getLog(I2NPMessageHandler.class);
|
||||||
_messageBuffer = null;
|
_messageBuffer = null;
|
||||||
|
_lastSize = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,7 +51,7 @@ public class I2NPMessageHandler {
|
|||||||
if (msg == null)
|
if (msg == null)
|
||||||
throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message");
|
throw new I2NPMessageException("The type "+ type + " is an unknown I2NP message");
|
||||||
try {
|
try {
|
||||||
msg.readBytes(in, type, _messageBuffer);
|
_lastSize = msg.readBytes(in, type, _messageBuffer);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
throw ioe;
|
throw ioe;
|
||||||
} catch (I2NPMessageException ime) {
|
} catch (I2NPMessageException ime) {
|
||||||
@@ -67,6 +69,7 @@ public class I2NPMessageHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public long getLastReadTime() { return _lastReadEnd - _lastReadBegin; }
|
public long getLastReadTime() { return _lastReadEnd - _lastReadBegin; }
|
||||||
|
public int getLastSize() { return _lastSize; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Yes, this is fairly ugly, but its the only place it ever happens.
|
* Yes, this is fairly ugly, but its the only place it ever happens.
|
||||||
|
@@ -51,7 +51,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
throw new DataFormatException("Bad bytes", ime);
|
throw new DataFormatException("Bad bytes", ime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException {
|
public int readBytes(InputStream in, int type, byte buffer[]) throws I2NPMessageException, IOException {
|
||||||
try {
|
try {
|
||||||
if (type < 0)
|
if (type < 0)
|
||||||
type = (int)DataHelper.readLong(in, 1);
|
type = (int)DataHelper.readLong(in, 1);
|
||||||
@@ -85,6 +85,7 @@ public abstract class I2NPMessageImpl extends DataStructureImpl implements I2NPM
|
|||||||
long time = _context.clock().now() - start;
|
long time = _context.clock().now() - start;
|
||||||
if (time > 50)
|
if (time > 50)
|
||||||
_context.statManager().addRateData("i2np.readTime", time, time);
|
_context.statManager().addRateData("i2np.readTime", time, time);
|
||||||
|
return size + Hash.HASH_LENGTH + 1 + 4 + DataHelper.DATE_LENGTH;
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
throw new I2NPMessageException("Error reading the message header", dfe);
|
throw new I2NPMessageException("Error reading the message header", dfe);
|
||||||
}
|
}
|
||||||
|
@@ -81,7 +81,7 @@ public class I2NPMessageReader {
|
|||||||
* reader
|
* reader
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead);
|
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead, int bytesRead);
|
||||||
/**
|
/**
|
||||||
* Notify the listener that an exception was thrown while reading from the given
|
* Notify the listener that an exception was thrown while reading from the given
|
||||||
* reader
|
* reader
|
||||||
@@ -122,7 +122,8 @@ public class I2NPMessageReader {
|
|||||||
I2NPMessage msg = _handler.readMessage(_stream);
|
I2NPMessage msg = _handler.readMessage(_stream);
|
||||||
if (msg != null) {
|
if (msg != null) {
|
||||||
long msToRead = _handler.getLastReadTime();
|
long msToRead = _handler.getLastReadTime();
|
||||||
_listener.messageReceived(I2NPMessageReader.this, msg, msToRead);
|
int bytesRead = _handler.getLastSize();
|
||||||
|
_listener.messageReceived(I2NPMessageReader.this, msg, msToRead, bytesRead);
|
||||||
}
|
}
|
||||||
} catch (I2NPMessageException ime) {
|
} catch (I2NPMessageException ime) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
@@ -65,6 +65,8 @@ public abstract class ClientManagerFacade implements Service {
|
|||||||
|
|
||||||
public abstract void messageReceived(ClientMessage msg);
|
public abstract void messageReceived(ClientMessage msg);
|
||||||
|
|
||||||
|
public boolean verifyClientLiveliness() { return true; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the client's current config, or null if not connected
|
* Return the client's current config, or null if not connected
|
||||||
*
|
*
|
||||||
|
@@ -289,6 +289,37 @@ public class JobQueue {
|
|||||||
}
|
}
|
||||||
boolean isAlive() { return _alive; }
|
boolean isAlive() { return _alive; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When did the most recently begin job start?
|
||||||
|
*/
|
||||||
|
public long getLastJobBegin() {
|
||||||
|
long when = -1;
|
||||||
|
// not synchronized, so might b0rk if the runners are changed
|
||||||
|
for (Iterator iter = _queueRunners.values().iterator(); iter.hasNext(); ) {
|
||||||
|
long cur = ((JobQueueRunner)iter.next()).getLastBegin();
|
||||||
|
if (cur > when)
|
||||||
|
cur = when;
|
||||||
|
}
|
||||||
|
return when;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* retrieve the most recently begin and still currently active job, or null if
|
||||||
|
* no jobs are running
|
||||||
|
*/
|
||||||
|
public Job getLastJob() {
|
||||||
|
Job j = null;
|
||||||
|
long when = -1;
|
||||||
|
// not synchronized, so might b0rk if the runners are changed
|
||||||
|
for (Iterator iter = _queueRunners.values().iterator(); iter.hasNext(); ) {
|
||||||
|
JobQueueRunner cur = (JobQueueRunner)iter.next();
|
||||||
|
if (cur.getLastBegin() > when) {
|
||||||
|
j = cur.getCurrentJob();
|
||||||
|
when = cur.getLastBegin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Blocking call to retrieve the next ready job
|
* Blocking call to retrieve the next ready job
|
||||||
*
|
*
|
||||||
|
@@ -11,6 +11,7 @@ class JobQueueRunner implements Runnable {
|
|||||||
private long _numJobs;
|
private long _numJobs;
|
||||||
private Job _currentJob;
|
private Job _currentJob;
|
||||||
private Job _lastJob;
|
private Job _lastJob;
|
||||||
|
private long _lastBegin;
|
||||||
|
|
||||||
public JobQueueRunner(RouterContext context, int id) {
|
public JobQueueRunner(RouterContext context, int id) {
|
||||||
_context = context;
|
_context = context;
|
||||||
@@ -31,6 +32,7 @@ class JobQueueRunner implements Runnable {
|
|||||||
public int getRunnerId() { return _id; }
|
public int getRunnerId() { return _id; }
|
||||||
public void stopRunning() { _keepRunning = false; }
|
public void stopRunning() { _keepRunning = false; }
|
||||||
public void startRunning() { _keepRunning = true; }
|
public void startRunning() { _keepRunning = true; }
|
||||||
|
public long getLastBegin() { return _lastBegin; }
|
||||||
public void run() {
|
public void run() {
|
||||||
long lastActive = _context.clock().now();
|
long lastActive = _context.clock().now();
|
||||||
long jobNum = 0;
|
long jobNum = 0;
|
||||||
@@ -103,6 +105,7 @@ class JobQueueRunner implements Runnable {
|
|||||||
|
|
||||||
private void runCurrentJob() {
|
private void runCurrentJob() {
|
||||||
try {
|
try {
|
||||||
|
_lastBegin = _context.clock().now();
|
||||||
_currentJob.runJob();
|
_currentJob.runJob();
|
||||||
} catch (OutOfMemoryError oom) {
|
} catch (OutOfMemoryError oom) {
|
||||||
try {
|
try {
|
||||||
|
@@ -127,6 +127,11 @@ public class Router {
|
|||||||
_gracefulShutdownDetector.setName("Graceful shutdown hook");
|
_gracefulShutdownDetector.setName("Graceful shutdown hook");
|
||||||
_gracefulShutdownDetector.start();
|
_gracefulShutdownDetector.start();
|
||||||
|
|
||||||
|
I2PThread watchdog = new I2PThread(new RouterWatchdog(_context));
|
||||||
|
watchdog.setName("RouterWatchdog");
|
||||||
|
watchdog.setDaemon(true);
|
||||||
|
watchdog.start();
|
||||||
|
|
||||||
_shutdownTasks = new HashSet(0);
|
_shutdownTasks = new HashSet(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,10 +265,35 @@ public class Router {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private final class CoallesceStatsJob extends JobImpl {
|
private final class CoallesceStatsJob extends JobImpl {
|
||||||
public CoallesceStatsJob() { super(Router.this._context); }
|
public CoallesceStatsJob() {
|
||||||
|
super(Router.this._context);
|
||||||
|
Router.this._context.statManager().createRateStat("bw.receiveBps", "How fast we receive data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
|
||||||
|
Router.this._context.statManager().createRateStat("bw.sendBps", "How fast we send data", "Bandwidth", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
|
||||||
|
}
|
||||||
public String getName() { return "Coallesce stats"; }
|
public String getName() { return "Coallesce stats"; }
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
Router.this._context.statManager().coallesceStats();
|
Router.this._context.statManager().coallesceStats();
|
||||||
|
|
||||||
|
RateStat receiveRate = _context.statManager().getRate("transport.receiveMessageSize");
|
||||||
|
if (receiveRate != null) {
|
||||||
|
Rate rate = receiveRate.getRate(60*1000);
|
||||||
|
if (rate != null) {
|
||||||
|
double bytes = rate.getLastTotalValue();
|
||||||
|
double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d);
|
||||||
|
Router.this._context.statManager().addRateData("bw.receiveBps", (long)bps, 60*1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RateStat sendRate = _context.statManager().getRate("transport.sendMessageSize");
|
||||||
|
if (sendRate != null) {
|
||||||
|
Rate rate = receiveRate.getRate(60*1000);
|
||||||
|
if (rate != null) {
|
||||||
|
double bytes = rate.getLastTotalValue();
|
||||||
|
double bps = (bytes*1000.0d)/(rate.getPeriod()*1024.0d);
|
||||||
|
Router.this._context.statManager().addRateData("bw.sendBps", (long)bps, 60*1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
requeue(60*1000);
|
requeue(60*1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,9 +15,9 @@ import net.i2p.CoreVersion;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RouterVersion {
|
public class RouterVersion {
|
||||||
public final static String ID = "$Revision: 1.48 $ $Date: 2004/10/08 13:38:48 $";
|
public final static String ID = "$Revision: 1.49 $ $Date: 2004/10/08 17:53:04 $";
|
||||||
public final static String VERSION = "0.4.1.1";
|
public final static String VERSION = "0.4.1.1";
|
||||||
public final static long BUILD = 14;
|
public final static long BUILD = 15;
|
||||||
public static void main(String args[]) {
|
public static void main(String args[]) {
|
||||||
System.out.println("I2P Router version: " + VERSION);
|
System.out.println("I2P Router version: " + VERSION);
|
||||||
System.out.println("Router ID: " + RouterVersion.ID);
|
System.out.println("Router ID: " + RouterVersion.ID);
|
||||||
|
67
router/java/src/net/i2p/router/RouterWatchdog.java
Normal file
67
router/java/src/net/i2p/router/RouterWatchdog.java
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
package net.i2p.router;
|
||||||
|
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Periodically check to make sure things haven't gone totally haywire (and if
|
||||||
|
* they have, restart the JVM)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class RouterWatchdog implements Runnable {
|
||||||
|
private Log _log;
|
||||||
|
private RouterContext _context;
|
||||||
|
|
||||||
|
private static final long MAX_JOB_RUN_LAG = 60*1000;
|
||||||
|
|
||||||
|
public RouterWatchdog(RouterContext ctx) {
|
||||||
|
_context = ctx;
|
||||||
|
_log = ctx.logManager().getLog(RouterWatchdog.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verifyJobQueueLiveliness() {
|
||||||
|
long when = _context.jobQueue().getLastJobBegin();
|
||||||
|
if (when < 0)
|
||||||
|
return true;
|
||||||
|
long howLongAgo = _context.clock().now() - when;
|
||||||
|
if (howLongAgo > MAX_JOB_RUN_LAG) {
|
||||||
|
Job cur = _context.jobQueue().getLastJob();
|
||||||
|
if (cur != null) {
|
||||||
|
if (_log.shouldLog(Log.ERROR))
|
||||||
|
_log.error("Last job was queued up " + DataHelper.formatDuration(howLongAgo)
|
||||||
|
+ " ago: " + cur);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// no prob, just normal lag
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verifyClientLiveliness() {
|
||||||
|
return _context.clientManager().verifyClientLiveliness();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shutdownOnHang() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
while (true) {
|
||||||
|
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
||||||
|
monitorRouter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void monitorRouter() {
|
||||||
|
boolean ok = verifyJobQueueLiveliness();
|
||||||
|
ok = ok && verifyClientLiveliness();
|
||||||
|
|
||||||
|
if (!ok && shutdownOnHang()) {
|
||||||
|
_log.log(Log.CRIT, "Router hung! hard restart!");
|
||||||
|
System.exit(Router.EXIT_HARD_RESTART);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -234,7 +234,7 @@ public class ClientManager {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ClientConnectionRunner getRunner(Destination dest) {
|
ClientConnectionRunner getRunner(Destination dest) {
|
||||||
ClientConnectionRunner rv = null;
|
ClientConnectionRunner rv = null;
|
||||||
long beforeLock = _context.clock().now();
|
long beforeLock = _context.clock().now();
|
||||||
long inLock = 0;
|
long inLock = 0;
|
||||||
@@ -300,7 +300,7 @@ public class ClientManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set getRunnerDestinations() {
|
Set getRunnerDestinations() {
|
||||||
Set dests = new HashSet();
|
Set dests = new HashSet();
|
||||||
long beforeLock = _context.clock().now();
|
long beforeLock = _context.clock().now();
|
||||||
long inLock = 0;
|
long inLock = 0;
|
||||||
|
@@ -10,7 +10,9 @@ package net.i2p.router.client;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.LeaseSet;
|
import net.i2p.data.LeaseSet;
|
||||||
@@ -68,6 +70,27 @@ public class ClientManagerFacadeImpl extends ClientManagerFacade {
|
|||||||
startup();
|
startup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final long MAX_TIME_TO_REBUILD = 5*60*1000;
|
||||||
|
public boolean verifyClientLiveliness() {
|
||||||
|
boolean lively = true;
|
||||||
|
for (Iterator iter = _manager.getRunnerDestinations().iterator(); iter.hasNext(); ) {
|
||||||
|
Destination dest = (Destination)iter.next();
|
||||||
|
ClientConnectionRunner runner = _manager.getRunner(dest);
|
||||||
|
if ( (runner == null) || (runner.getIsDead())) continue;
|
||||||
|
LeaseSet ls = runner.getLeaseSet();
|
||||||
|
if (ls == null)
|
||||||
|
continue; // still building
|
||||||
|
long howLongAgo = _context.clock().now() - ls.getEarliestLeaseDate();
|
||||||
|
if (howLongAgo > MAX_TIME_TO_REBUILD) {
|
||||||
|
if (_log.shouldLog(Log.ERROR))
|
||||||
|
_log.error("Client " + dest.calculateHash().toBase64().substring(0,6)
|
||||||
|
+ " has a leaseSet that expired " + DataHelper.formatDuration(howLongAgo));
|
||||||
|
lively = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lively;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request that a particular client authorize the Leases contained in the
|
* Request that a particular client authorize the Leases contained in the
|
||||||
* LeaseSet, after which the onCreateJob is queued up. If that doesn't occur
|
* LeaseSet, after which the onCreateJob is queued up. If that doesn't occur
|
||||||
|
@@ -29,12 +29,12 @@ public class MessageHandler implements I2NPMessageReader.I2NPMessageEventListene
|
|||||||
_con.closeConnection();
|
_con.closeConnection();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead) {
|
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead, int size) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Just received message " + message.getUniqueId() + " from "
|
_log.debug("Just received message " + message.getUniqueId() + " from "
|
||||||
+ _identHash.toBase64().substring(0,6)
|
+ _identHash.toBase64().substring(0,6)
|
||||||
+ " readTime = " + msToRead + "ms type = " + message.getClass().getName());
|
+ " readTime = " + msToRead + "ms type = " + message.getClass().getName());
|
||||||
_transport.messageReceived(message, _ident, _identHash, msToRead, message.getMessageSize());
|
_transport.messageReceived(message, _ident, _identHash, msToRead, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readError(I2NPMessageReader reader, Exception error) {
|
public void readError(I2NPMessageReader reader, Exception error) {
|
||||||
|
@@ -69,7 +69,7 @@ class I2NPMessageReaderTest implements I2NPMessageReader.I2NPMessageEventListene
|
|||||||
_log.debug("Disconnected");
|
_log.debug("Disconnected");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead) {
|
public void messageReceived(I2NPMessageReader reader, I2NPMessage message, long msToRead, int size) {
|
||||||
_log.debug("Message received: " + message);
|
_log.debug("Message received: " + message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user