Major fixes and improvements to the network module, fixing problems where objects where requested multiple times or not at all in some situations.

This commit is contained in:
2016-01-16 08:47:50 +01:00
parent 549c8854ed
commit 8764642878
6 changed files with 202 additions and 80 deletions

View File

@ -19,6 +19,7 @@ package ch.dissem.bitmessage.networking;
import ch.dissem.bitmessage.InternalContext;
import ch.dissem.bitmessage.InternalContext.ContextHolder;
import ch.dissem.bitmessage.entity.CustomMessage;
import ch.dissem.bitmessage.entity.GetData;
import ch.dissem.bitmessage.entity.NetworkMessage;
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
@ -41,8 +42,9 @@ import java.util.concurrent.*;
import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT;
import static ch.dissem.bitmessage.networking.Connection.Mode.SERVER;
import static ch.dissem.bitmessage.networking.Connection.State.ACTIVE;
import static ch.dissem.bitmessage.networking.Connection.State.DISCONNECTED;
import static ch.dissem.bitmessage.utils.DebugUtils.inc;
import static ch.dissem.bitmessage.utils.UnixTime.MINUTE;
import static java.util.Collections.newSetFromMap;
/**
* Handles all the networky stuff.
@ -50,13 +52,14 @@ import static ch.dissem.bitmessage.utils.DebugUtils.inc;
public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
public final static int NETWORK_MAGIC_NUMBER = 8;
private final static Logger LOG = LoggerFactory.getLogger(DefaultNetworkHandler.class);
private final List<Connection> connections = new LinkedList<>();
private static final Random RANDOM = new Random();
private final Collection<Connection> connections = new ConcurrentLinkedQueue<>();
private final ExecutorService pool;
private InternalContext ctx;
private ServerSocket serverSocket;
private volatile boolean running;
private ConcurrentMap<InventoryVector, Long> requestedObjects = new ConcurrentHashMap<>();
private Set<InventoryVector> requestedObjects = newSetFromMap(new ConcurrentHashMap<InventoryVector, Boolean>(50_000));
public DefaultNetworkHandler() {
pool = Executors.newCachedThreadPool(new ThreadFactory() {
@ -134,6 +137,8 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
}
});
pool.execute(new Runnable() {
public Connection initialConnection;
@Override
public void run() {
try {
@ -152,25 +157,38 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
}
for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext(); ) {
Connection c = iterator.next();
if (now - c.getStartTime() > ctx.getConnectionTTL()) {
// Just in case they were all created at the same time, don't disconnect
// all at once.
if (now - c.getStartTime() + RANDOM.nextInt(5 * MINUTE) > ctx.getConnectionTTL()) {
c.disconnect();
}
if (c.getState() == DISCONNECTED) {
// Remove the current element from the iterator and the list.
iterator.remove();
}
if (c.getState() == ACTIVE) {
active++;
switch (c.getState()) {
case DISCONNECTED:
iterator.remove();
break;
case ACTIVE:
active++;
break;
}
}
}
if (active < NETWORK_MAGIC_NUMBER) {
List<NetworkAddress> addresses = ctx.getNodeRegistry().getKnownAddresses(
NETWORK_MAGIC_NUMBER - active, ctx.getStreams());
boolean first = active == 0 && initialConnection == null;
for (NetworkAddress address : addresses) {
startConnection(new Connection(ctx, CLIENT, address, listener, requestedObjects));
Connection c = new Connection(ctx, CLIENT, address, listener, requestedObjects);
if (first) {
initialConnection = c;
first = false;
}
startConnection(c);
}
Thread.sleep(10000);
} else if (initialConnection != null) {
initialConnection.disconnect();
initialConnection = null;
Thread.sleep(10000);
} else {
Thread.sleep(30000);
}
@ -209,6 +227,7 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
c.disconnect();
}
}
requestedObjects.clear();
}
private void startConnection(Connection c) {
@ -272,7 +291,56 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder {
}
return new Property("network", null,
new Property("connectionManager", running ? "running" : "stopped"),
new Property("connections", null, streamProperties)
new Property("connections", null, streamProperties),
new Property("requestedObjects", requestedObjects.size())
);
}
void request(Set<InventoryVector> inventoryVectors) {
if (!running || inventoryVectors.isEmpty()) return;
synchronized (connections) {
Map<Connection, List<InventoryVector>> distribution = new HashMap<>();
for (Connection connection : connections) {
if (connection.getState() == ACTIVE) {
distribution.put(connection, new LinkedList<InventoryVector>());
}
}
Iterator<InventoryVector> iterator = inventoryVectors.iterator();
boolean firstRound = true;
InventoryVector next = iterator.next();
while (firstRound || iterator.hasNext()) {
if (!firstRound) {
next = iterator.next();
firstRound = true;
} else {
firstRound = false;
}
for (Connection connection : distribution.keySet()) {
if (connection.knowsOf(next)) {
List<InventoryVector> ivs = distribution.get(connection);
if (ivs.size() == 50_000) {
connection.send(new GetData.Builder().inventory(ivs).build());
ivs.clear();
}
ivs.add(next);
iterator.remove();
if (iterator.hasNext()) {
next = iterator.next();
firstRound = true;
} else {
firstRound = false;
break;
}
}
}
}
for (Connection connection : distribution.keySet()) {
List<InventoryVector> ivs = distribution.get(connection);
if (!ivs.isEmpty()) {
connection.send(new GetData.Builder().inventory(ivs).build());
}
}
}
}
}