Code cleanup & improvements
- most notably removed some unnecessary synchronize blocks in the DefaultNetworkHandler
This commit is contained in:
		| @@ -21,7 +21,6 @@ import ch.dissem.bitmessage.InternalContext; | ||||
| import ch.dissem.bitmessage.entity.*; | ||||
| import ch.dissem.bitmessage.entity.valueobject.InventoryVector; | ||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||
| import ch.dissem.bitmessage.exception.ApplicationException; | ||||
| import ch.dissem.bitmessage.exception.InsufficientProofOfWorkException; | ||||
| import ch.dissem.bitmessage.exception.NodeException; | ||||
| import ch.dissem.bitmessage.factory.Factory; | ||||
|   | ||||
| @@ -0,0 +1,120 @@ | ||||
| /* | ||||
|  * Copyright 2016 Christian Basler | ||||
|  * | ||||
|  * 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 ch.dissem.bitmessage.networking; | ||||
|  | ||||
| import ch.dissem.bitmessage.InternalContext; | ||||
| import ch.dissem.bitmessage.entity.valueobject.NetworkAddress; | ||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | ||||
| import ch.dissem.bitmessage.utils.UnixTime; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.util.Iterator; | ||||
| import java.util.List; | ||||
|  | ||||
| import static ch.dissem.bitmessage.networking.Connection.Mode.CLIENT; | ||||
| import static ch.dissem.bitmessage.networking.DefaultNetworkHandler.NETWORK_MAGIC_NUMBER; | ||||
|  | ||||
| /** | ||||
|  * @author Christian Basler | ||||
|  */ | ||||
| public class ConnectionOrganizer implements Runnable { | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(ConnectionOrganizer.class); | ||||
|  | ||||
|     private final InternalContext ctx; | ||||
|     private final DefaultNetworkHandler networkHandler; | ||||
|     private final NetworkHandler.MessageListener listener; | ||||
|  | ||||
|     private Connection initialConnection; | ||||
|  | ||||
|     public ConnectionOrganizer(InternalContext ctx, | ||||
|                                DefaultNetworkHandler networkHandler, | ||||
|                                NetworkHandler.MessageListener listener) { | ||||
|         this.ctx = ctx; | ||||
|         this.networkHandler = networkHandler; | ||||
|         this.listener = listener; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void run() { | ||||
|         try { | ||||
|             while (networkHandler.isRunning()) { | ||||
|                 try { | ||||
|                     int active = 0; | ||||
|                     long now = UnixTime.now(); | ||||
|  | ||||
|                     int diff = networkHandler.connections.size() - ctx.getConnectionLimit(); | ||||
|                     if (diff > 0) { | ||||
|                         for (Connection c : networkHandler.connections) { | ||||
|                             c.disconnect(); | ||||
|                             diff--; | ||||
|                             if (diff == 0) break; | ||||
|                         } | ||||
|                     } | ||||
|                     boolean forcedDisconnect = false; | ||||
|                     for (Iterator<Connection> iterator = networkHandler.connections.iterator(); iterator.hasNext(); ) { | ||||
|                         Connection c = iterator.next(); | ||||
|                         // Just in case they were all created at the same time, don't disconnect | ||||
|                         // all at once. | ||||
|                         if (!forcedDisconnect && now - c.getStartTime() > ctx.getConnectionTTL()) { | ||||
|                             c.disconnect(); | ||||
|                             forcedDisconnect = true; | ||||
|                         } | ||||
|                         switch (c.getState()) { | ||||
|                             case DISCONNECTED: | ||||
|                                 iterator.remove(); | ||||
|                                 break; | ||||
|                             case ACTIVE: | ||||
|                                 active++; | ||||
|                                 break; | ||||
|                             default: | ||||
|                                 // nothing to do | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                     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) { | ||||
|                             Connection c = new Connection(ctx, CLIENT, address, listener, networkHandler.requestedObjects); | ||||
|                             if (first) { | ||||
|                                 initialConnection = c; | ||||
|                                 first = false; | ||||
|                             } | ||||
|                             networkHandler.startConnection(c); | ||||
|                         } | ||||
|                         Thread.sleep(10000); | ||||
|                     } else if (initialConnection != null) { | ||||
|                         initialConnection.disconnect(); | ||||
|                         initialConnection = null; | ||||
|                         Thread.sleep(10000); | ||||
|                     } else { | ||||
|                         Thread.sleep(30000); | ||||
|                     } | ||||
|                 } catch (InterruptedException e) { | ||||
|                     networkHandler.stop(); | ||||
|                 } catch (Exception e) { | ||||
|                     LOG.error("Error in connection manager. Ignored.", e); | ||||
|                 } | ||||
|             } | ||||
|         } finally { | ||||
|             LOG.debug("Connection manager shutting down."); | ||||
|             networkHandler.stop(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -22,25 +22,21 @@ 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; | ||||
| import ch.dissem.bitmessage.exception.ApplicationException; | ||||
| import ch.dissem.bitmessage.exception.NodeException; | ||||
| import ch.dissem.bitmessage.factory.Factory; | ||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | ||||
| import ch.dissem.bitmessage.utils.Collections; | ||||
| import ch.dissem.bitmessage.utils.Property; | ||||
| import ch.dissem.bitmessage.utils.UnixTime; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.net.InetAddress; | ||||
| import java.net.ServerSocket; | ||||
| import java.net.Socket; | ||||
| import java.util.*; | ||||
| 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.utils.DebugUtils.inc; | ||||
| @@ -54,13 +50,13 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { | ||||
|  | ||||
|     public final static int NETWORK_MAGIC_NUMBER = 8; | ||||
|  | ||||
|     private final Collection<Connection> connections = new ConcurrentLinkedQueue<>(); | ||||
|     final Collection<Connection> connections = new ConcurrentLinkedQueue<>(); | ||||
|     private final ExecutorService pool; | ||||
|     private InternalContext ctx; | ||||
|     private ServerSocket serverSocket; | ||||
|     private ServerRunnable server; | ||||
|     private volatile boolean running; | ||||
|  | ||||
|     private Set<InventoryVector> requestedObjects = newSetFromMap(new ConcurrentHashMap<InventoryVector, Boolean>(50_000)); | ||||
|     final Set<InventoryVector> requestedObjects = newSetFromMap(new ConcurrentHashMap<InventoryVector, Boolean>(50_000)); | ||||
|  | ||||
|     public DefaultNetworkHandler() { | ||||
|         pool = Executors.newCachedThreadPool(new ThreadFactory() { | ||||
| @@ -122,93 +118,9 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { | ||||
|         try { | ||||
|             running = true; | ||||
|             connections.clear(); | ||||
|             serverSocket = new ServerSocket(ctx.getPort()); | ||||
|             pool.execute(new Runnable() { | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     while (!serverSocket.isClosed()) { | ||||
|                         try { | ||||
|                             Socket socket = serverSocket.accept(); | ||||
|                             socket.setSoTimeout(Connection.READ_TIMEOUT); | ||||
|                             startConnection(new Connection(ctx, SERVER, socket, listener, requestedObjects)); | ||||
|                         } catch (IOException e) { | ||||
|                             LOG.debug(e.getMessage(), e); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             pool.execute(new Runnable() { | ||||
|                 public Connection initialConnection; | ||||
|  | ||||
|                 @Override | ||||
|                 public void run() { | ||||
|                     try { | ||||
|                         while (running) { | ||||
|                             try { | ||||
|                                 int active = 0; | ||||
|                                 long now = UnixTime.now(); | ||||
|                                 synchronized (connections) { | ||||
|                                     int diff = connections.size() - ctx.getConnectionLimit(); | ||||
|                                     if (diff > 0) { | ||||
|                                         for (Connection c : connections) { | ||||
|                                             c.disconnect(); | ||||
|                                             diff--; | ||||
|                                             if (diff == 0) break; | ||||
|                                         } | ||||
|                                     } | ||||
|                                     boolean forcedDisconnect = false; | ||||
|                                     for (Iterator<Connection> iterator = connections.iterator(); iterator.hasNext(); ) { | ||||
|                                         Connection c = iterator.next(); | ||||
|                                         // Just in case they were all created at the same time, don't disconnect | ||||
|                                         // all at once. | ||||
|                                         if (!forcedDisconnect && now - c.getStartTime() > ctx.getConnectionTTL()) { | ||||
|                                             c.disconnect(); | ||||
|                                             forcedDisconnect = true; | ||||
|                                         } | ||||
|                                         switch (c.getState()) { | ||||
|                                             case DISCONNECTED: | ||||
|                                                 iterator.remove(); | ||||
|                                                 break; | ||||
|                                             case ACTIVE: | ||||
|                                                 active++; | ||||
|                                                 break; | ||||
|                                             default: | ||||
|                                                 // nothing to do | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
|                                 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) { | ||||
|                                         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); | ||||
|                                 } | ||||
|                             } catch (InterruptedException e) { | ||||
|                                 running = false; | ||||
|                             } catch (Exception e) { | ||||
|                                 LOG.error("Error in connection manager. Ignored.", e); | ||||
|                             } | ||||
|                         } | ||||
|                     } finally { | ||||
|                         LOG.debug("Connection manager shutting down."); | ||||
|                         running = false; | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|             server = new ServerRunnable(ctx, this, listener); | ||||
|             pool.execute(server); | ||||
|             pool.execute(new ConnectionOrganizer(ctx, this, listener)); | ||||
|         } catch (IOException e) { | ||||
|             throw new ApplicationException(e); | ||||
|         } | ||||
| @@ -221,13 +133,9 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { | ||||
|  | ||||
|     @Override | ||||
|     public void stop() { | ||||
|         running = false; | ||||
|         try { | ||||
|             serverSocket.close(); | ||||
|         } catch (IOException e) { | ||||
|             LOG.debug(e.getMessage(), e); | ||||
|         } | ||||
|         server.close(); | ||||
|         synchronized (connections) { | ||||
|             running = false; | ||||
|             for (Connection c : connections) { | ||||
|                 c.disconnect(); | ||||
|             } | ||||
| @@ -235,8 +143,12 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { | ||||
|         requestedObjects.clear(); | ||||
|     } | ||||
|  | ||||
|     private void startConnection(Connection c) { | ||||
|     void startConnection(Connection c) { | ||||
|         if (!running) return; | ||||
|  | ||||
|         synchronized (connections) { | ||||
|             if (!running) return; | ||||
|  | ||||
|             // prevent connecting twice to the same node | ||||
|             if (connections.contains(c)) { | ||||
|                 return; | ||||
| @@ -250,11 +162,9 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { | ||||
|     @Override | ||||
|     public void offer(final InventoryVector iv) { | ||||
|         List<Connection> target = new LinkedList<>(); | ||||
|         synchronized (connections) { | ||||
|             for (Connection connection : connections) { | ||||
|                 if (connection.getState() == ACTIVE && !connection.knowsOf(iv)) { | ||||
|                     target.add(connection); | ||||
|                 } | ||||
|         for (Connection connection : connections) { | ||||
|             if (connection.getState() == ACTIVE && !connection.knowsOf(iv)) { | ||||
|                 target.add(connection); | ||||
|             } | ||||
|         } | ||||
|         List<Connection> randomSubset = Collections.selectRandom(NETWORK_MAGIC_NUMBER, target); | ||||
| @@ -269,16 +179,14 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { | ||||
|         TreeMap<Long, Integer> incomingConnections = new TreeMap<>(); | ||||
|         TreeMap<Long, Integer> outgoingConnections = new TreeMap<>(); | ||||
|  | ||||
|         synchronized (connections) { | ||||
|             for (Connection connection : connections) { | ||||
|                 if (connection.getState() == ACTIVE) { | ||||
|                     long stream = connection.getNode().getStream(); | ||||
|                     streams.add(stream); | ||||
|                     if (connection.getMode() == SERVER) { | ||||
|                         inc(incomingConnections, stream); | ||||
|                     } else { | ||||
|                         inc(outgoingConnections, stream); | ||||
|                     } | ||||
|         for (Connection connection : connections) { | ||||
|             if (connection.getState() == ACTIVE) { | ||||
|                 long stream = connection.getNode().getStream(); | ||||
|                 streams.add(stream); | ||||
|                 if (connection.getMode() == SERVER) { | ||||
|                     inc(incomingConnections, stream); | ||||
|                 } else { | ||||
|                     inc(outgoingConnections, stream); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -303,53 +211,47 @@ public class DefaultNetworkHandler implements NetworkHandler, ContextHolder { | ||||
|  | ||||
|     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(); | ||||
|             InventoryVector next; | ||||
|             if (iterator.hasNext()) { | ||||
|                 next = iterator.next(); | ||||
|             } else { | ||||
|                 return; | ||||
|             } | ||||
|             boolean firstRound = true; | ||||
|             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; | ||||
|                         } | ||||
|         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(); | ||||
|         if (!iterator.hasNext()) { | ||||
|             return; | ||||
|         } | ||||
|         InventoryVector next = iterator.next(); | ||||
|         Connection previous = null; | ||||
|         do { | ||||
|             for (Connection connection : distribution.keySet()) { | ||||
|                 if (connection == previous) { | ||||
|                     next = iterator.next(); | ||||
|                 } | ||||
|                 if (connection.knowsOf(next)) { | ||||
|                     List<InventoryVector> ivs = distribution.get(connection); | ||||
|                     if (ivs.size() == GetData.MAX_INVENTORY_SIZE) { | ||||
|                         connection.send(new GetData.Builder().inventory(ivs).build()); | ||||
|                         ivs.clear(); | ||||
|                     } | ||||
|                     ivs.add(next); | ||||
|                     iterator.remove(); | ||||
|  | ||||
|                     if (iterator.hasNext()) { | ||||
|                         next = iterator.next(); | ||||
|                         previous = connection; | ||||
|                     } else { | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             for (Connection connection : distribution.keySet()) { | ||||
|                 List<InventoryVector> ivs = distribution.get(connection); | ||||
|                 if (!ivs.isEmpty()) { | ||||
|                     connection.send(new GetData.Builder().inventory(ivs).build()); | ||||
|                 } | ||||
|         } while (iterator.hasNext()); | ||||
|  | ||||
|         for (Connection connection : distribution.keySet()) { | ||||
|             List<InventoryVector> ivs = distribution.get(connection); | ||||
|             if (!ivs.isEmpty()) { | ||||
|                 connection.send(new GetData.Builder().inventory(ivs).build()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -0,0 +1,70 @@ | ||||
| /* | ||||
|  * Copyright 2016 Christian Basler | ||||
|  * | ||||
|  * 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 ch.dissem.bitmessage.networking; | ||||
|  | ||||
| import ch.dissem.bitmessage.InternalContext; | ||||
| import ch.dissem.bitmessage.ports.NetworkHandler; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| import java.io.Closeable; | ||||
| import java.io.IOException; | ||||
| import java.net.ServerSocket; | ||||
| import java.net.Socket; | ||||
|  | ||||
| import static ch.dissem.bitmessage.networking.Connection.Mode.SERVER; | ||||
|  | ||||
| /** | ||||
|  * @author Christian Basler | ||||
|  */ | ||||
| public class ServerRunnable implements Runnable, Closeable { | ||||
|     private static final Logger LOG = LoggerFactory.getLogger(ServerRunnable.class); | ||||
|     private final InternalContext ctx; | ||||
|     private final ServerSocket serverSocket; | ||||
|     private final DefaultNetworkHandler networkHandler; | ||||
|     private final NetworkHandler.MessageListener listener; | ||||
|  | ||||
|     public ServerRunnable(InternalContext ctx, DefaultNetworkHandler networkHandler, NetworkHandler.MessageListener listener) throws IOException { | ||||
|         this.ctx = ctx; | ||||
|         this.networkHandler = networkHandler; | ||||
|         this.listener = listener; | ||||
|         this.serverSocket = new ServerSocket(ctx.getPort()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void run() { | ||||
|         while (!serverSocket.isClosed()) { | ||||
|             try { | ||||
|                 Socket socket = serverSocket.accept(); | ||||
|                 socket.setSoTimeout(Connection.READ_TIMEOUT); | ||||
|                 networkHandler.startConnection(new Connection(ctx, SERVER, socket, listener, | ||||
|                         networkHandler.requestedObjects)); | ||||
|             } catch (IOException e) { | ||||
|                 LOG.debug(e.getMessage(), e); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void close() { | ||||
|         try { | ||||
|             serverSocket.close(); | ||||
|         } catch (IOException e) { | ||||
|             LOG.debug(e.getMessage(), e); | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user