Integrated Kryo serializers with the communications manager and IO loop stuff.
Showing
8 changed files
with
199 additions
and
18 deletions
| ... | @@ -29,4 +29,8 @@ public interface ClusterCommunicationAdminService { | ... | @@ -29,4 +29,8 @@ public interface ClusterCommunicationAdminService { |
| 29 | */ | 29 | */ |
| 30 | void startUp(DefaultControllerNode localNode, ClusterNodesDelegate delegate); | 30 | void startUp(DefaultControllerNode localNode, ClusterNodesDelegate delegate); |
| 31 | 31 | ||
| 32 | + /** | ||
| 33 | + * Clears all nodes and streams as part of leaving the cluster. | ||
| 34 | + */ | ||
| 35 | + void clearAllNodesAndStreams(); | ||
| 32 | } | 36 | } | ... | ... |
| ... | @@ -13,6 +13,7 @@ import org.onlab.onos.cluster.DefaultControllerNode; | ... | @@ -13,6 +13,7 @@ import org.onlab.onos.cluster.DefaultControllerNode; |
| 13 | import org.onlab.onos.cluster.NodeId; | 13 | import org.onlab.onos.cluster.NodeId; |
| 14 | import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; | 14 | import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; |
| 15 | import org.onlab.onos.store.cluster.messaging.ClusterMessage; | 15 | import org.onlab.onos.store.cluster.messaging.ClusterMessage; |
| 16 | +import org.onlab.onos.store.cluster.messaging.GoodbyeMessage; | ||
| 16 | import org.onlab.onos.store.cluster.messaging.HelloMessage; | 17 | import org.onlab.onos.store.cluster.messaging.HelloMessage; |
| 17 | import org.onlab.onos.store.cluster.messaging.MessageSubject; | 18 | import org.onlab.onos.store.cluster.messaging.MessageSubject; |
| 18 | import org.onlab.onos.store.cluster.messaging.MessageSubscriber; | 19 | import org.onlab.onos.store.cluster.messaging.MessageSubscriber; |
| ... | @@ -83,9 +84,11 @@ public class ClusterCommunicationManager | ... | @@ -83,9 +84,11 @@ public class ClusterCommunicationManager |
| 83 | 84 | ||
| 84 | private final Timer timer = new Timer("onos-comm-initiator"); | 85 | private final Timer timer = new Timer("onos-comm-initiator"); |
| 85 | private final TimerTask connectionCustodian = new ConnectionCustodian(); | 86 | private final TimerTask connectionCustodian = new ConnectionCustodian(); |
| 87 | + private GoodbyeSubscriber goodbyeSubscriber = new GoodbyeSubscriber(); | ||
| 86 | 88 | ||
| 87 | @Activate | 89 | @Activate |
| 88 | public void activate() { | 90 | public void activate() { |
| 91 | + addSubscriber(MessageSubject.GOODBYE, goodbyeSubscriber); | ||
| 89 | log.info("Activated but waiting for delegate"); | 92 | log.info("Activated but waiting for delegate"); |
| 90 | } | 93 | } |
| 91 | 94 | ||
| ... | @@ -102,9 +105,20 @@ public class ClusterCommunicationManager | ... | @@ -102,9 +105,20 @@ public class ClusterCommunicationManager |
| 102 | } | 105 | } |
| 103 | 106 | ||
| 104 | @Override | 107 | @Override |
| 108 | + public boolean send(ClusterMessage message) { | ||
| 109 | + boolean ok = true; | ||
| 110 | + for (DefaultControllerNode node : nodes) { | ||
| 111 | + if (!node.equals(localNode)) { | ||
| 112 | + ok = send(message, node.id()) && ok; | ||
| 113 | + } | ||
| 114 | + } | ||
| 115 | + return ok; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + @Override | ||
| 105 | public boolean send(ClusterMessage message, NodeId toNodeId) { | 119 | public boolean send(ClusterMessage message, NodeId toNodeId) { |
| 106 | ClusterMessageStream stream = streams.get(toNodeId); | 120 | ClusterMessageStream stream = streams.get(toNodeId); |
| 107 | - if (stream != null) { | 121 | + if (stream != null && !toNodeId.equals(localNode.id())) { |
| 108 | try { | 122 | try { |
| 109 | stream.write(message); | 123 | stream.write(message); |
| 110 | return true; | 124 | return true; |
| ... | @@ -140,6 +154,7 @@ public class ClusterCommunicationManager | ... | @@ -140,6 +154,7 @@ public class ClusterCommunicationManager |
| 140 | 154 | ||
| 141 | @Override | 155 | @Override |
| 142 | public void removeNode(DefaultControllerNode node) { | 156 | public void removeNode(DefaultControllerNode node) { |
| 157 | + send(new GoodbyeMessage(node.id())); | ||
| 143 | nodes.remove(node); | 158 | nodes.remove(node); |
| 144 | ClusterMessageStream stream = streams.remove(node.id()); | 159 | ClusterMessageStream stream = streams.remove(node.id()); |
| 145 | if (stream != null) { | 160 | if (stream != null) { |
| ... | @@ -159,6 +174,16 @@ public class ClusterCommunicationManager | ... | @@ -159,6 +174,16 @@ public class ClusterCommunicationManager |
| 159 | log.info("Started"); | 174 | log.info("Started"); |
| 160 | } | 175 | } |
| 161 | 176 | ||
| 177 | + @Override | ||
| 178 | + public void clearAllNodesAndStreams() { | ||
| 179 | + nodes.clear(); | ||
| 180 | + send(new GoodbyeMessage(localNode.id())); | ||
| 181 | + for (ClusterMessageStream stream : streams.values()) { | ||
| 182 | + stream.close(); | ||
| 183 | + } | ||
| 184 | + streams.clear(); | ||
| 185 | + } | ||
| 186 | + | ||
| 162 | /** | 187 | /** |
| 163 | * Dispatches the specified message to all subscribers to its subject. | 188 | * Dispatches the specified message to all subscribers to its subject. |
| 164 | * | 189 | * |
| ... | @@ -304,4 +329,11 @@ public class ClusterCommunicationManager | ... | @@ -304,4 +329,11 @@ public class ClusterCommunicationManager |
| 304 | } | 329 | } |
| 305 | } | 330 | } |
| 306 | 331 | ||
| 332 | + private class GoodbyeSubscriber implements MessageSubscriber { | ||
| 333 | + @Override | ||
| 334 | + public void receive(ClusterMessage message, NodeId fromNodeId) { | ||
| 335 | + log.info("Received goodbye message from {}", fromNodeId); | ||
| 336 | + nodesDelegate.nodeRemoved(fromNodeId); | ||
| 337 | + } | ||
| 338 | + } | ||
| 307 | } | 339 | } | ... | ... |
| ... | @@ -27,4 +27,11 @@ public interface ClusterNodesDelegate { | ... | @@ -27,4 +27,11 @@ public interface ClusterNodesDelegate { |
| 27 | */ | 27 | */ |
| 28 | void nodeVanished(NodeId nodeId); | 28 | void nodeVanished(NodeId nodeId); |
| 29 | 29 | ||
| 30 | + /** | ||
| 31 | + * Notifies about remote request to remove node from cluster. | ||
| 32 | + * | ||
| 33 | + * @param nodeId identifier of the cluster node that was removed | ||
| 34 | + */ | ||
| 35 | + void nodeRemoved(NodeId nodeId); | ||
| 36 | + | ||
| 30 | } | 37 | } | ... | ... |
| ... | @@ -127,9 +127,17 @@ public class DistributedClusterStore | ... | @@ -127,9 +127,17 @@ public class DistributedClusterStore |
| 127 | 127 | ||
| 128 | @Override | 128 | @Override |
| 129 | public void removeNode(NodeId nodeId) { | 129 | public void removeNode(NodeId nodeId) { |
| 130 | - DefaultControllerNode node = nodes.remove(nodeId); | 130 | + if (nodeId.equals(localNode.id())) { |
| 131 | - if (node != null) { | 131 | + // FIXME: this is still broken |
| 132 | - communicationAdminService.removeNode(node); | 132 | + // We are being ejected from the cluster, so remove all other nodes. |
| 133 | + communicationAdminService.clearAllNodesAndStreams(); | ||
| 134 | + nodes.clear(); | ||
| 135 | + } else { | ||
| 136 | + // Remove the other node. | ||
| 137 | + DefaultControllerNode node = nodes.remove(nodeId); | ||
| 138 | + if (node != null) { | ||
| 139 | + communicationAdminService.removeNode(node); | ||
| 140 | + } | ||
| 133 | } | 141 | } |
| 134 | } | 142 | } |
| 135 | 143 | ||
| ... | @@ -148,6 +156,11 @@ public class DistributedClusterStore | ... | @@ -148,6 +156,11 @@ public class DistributedClusterStore |
| 148 | public void nodeVanished(NodeId nodeId) { | 156 | public void nodeVanished(NodeId nodeId) { |
| 149 | states.put(nodeId, State.INACTIVE); | 157 | states.put(nodeId, State.INACTIVE); |
| 150 | } | 158 | } |
| 159 | + | ||
| 160 | + @Override | ||
| 161 | + public void nodeRemoved(NodeId nodeId) { | ||
| 162 | + removeNode(nodeId); | ||
| 163 | + } | ||
| 151 | } | 164 | } |
| 152 | 165 | ||
| 153 | } | 166 | } | ... | ... |
| 1 | package org.onlab.onos.store.cluster.impl; | 1 | package org.onlab.onos.store.cluster.impl; |
| 2 | 2 | ||
| 3 | +import de.javakaffee.kryoserializers.URISerializer; | ||
| 4 | +import org.apache.felix.scr.annotations.Activate; | ||
| 3 | import org.apache.felix.scr.annotations.Component; | 5 | import org.apache.felix.scr.annotations.Component; |
| 6 | +import org.apache.felix.scr.annotations.Deactivate; | ||
| 4 | import org.apache.felix.scr.annotations.Service; | 7 | import org.apache.felix.scr.annotations.Service; |
| 8 | +import org.onlab.onos.cluster.ControllerNode; | ||
| 9 | +import org.onlab.onos.cluster.DefaultControllerNode; | ||
| 5 | import org.onlab.onos.cluster.NodeId; | 10 | import org.onlab.onos.cluster.NodeId; |
| 11 | +import org.onlab.onos.net.ConnectPoint; | ||
| 12 | +import org.onlab.onos.net.DefaultDevice; | ||
| 13 | +import org.onlab.onos.net.DefaultLink; | ||
| 14 | +import org.onlab.onos.net.DefaultPort; | ||
| 15 | +import org.onlab.onos.net.Device; | ||
| 16 | +import org.onlab.onos.net.DeviceId; | ||
| 17 | +import org.onlab.onos.net.Element; | ||
| 18 | +import org.onlab.onos.net.Link; | ||
| 19 | +import org.onlab.onos.net.LinkKey; | ||
| 20 | +import org.onlab.onos.net.MastershipRole; | ||
| 21 | +import org.onlab.onos.net.Port; | ||
| 22 | +import org.onlab.onos.net.PortNumber; | ||
| 23 | +import org.onlab.onos.net.provider.ProviderId; | ||
| 6 | import org.onlab.onos.store.cluster.messaging.ClusterMessage; | 24 | import org.onlab.onos.store.cluster.messaging.ClusterMessage; |
| 25 | +import org.onlab.onos.store.cluster.messaging.EchoMessage; | ||
| 26 | +import org.onlab.onos.store.cluster.messaging.GoodbyeMessage; | ||
| 7 | import org.onlab.onos.store.cluster.messaging.HelloMessage; | 27 | import org.onlab.onos.store.cluster.messaging.HelloMessage; |
| 8 | import org.onlab.onos.store.cluster.messaging.MessageSubject; | 28 | import org.onlab.onos.store.cluster.messaging.MessageSubject; |
| 9 | import org.onlab.onos.store.cluster.messaging.SerializationService; | 29 | import org.onlab.onos.store.cluster.messaging.SerializationService; |
| 30 | +import org.onlab.onos.store.serializers.ConnectPointSerializer; | ||
| 31 | +import org.onlab.onos.store.serializers.DefaultLinkSerializer; | ||
| 32 | +import org.onlab.onos.store.serializers.DefaultPortSerializer; | ||
| 33 | +import org.onlab.onos.store.serializers.DeviceIdSerializer; | ||
| 34 | +import org.onlab.onos.store.serializers.IpPrefixSerializer; | ||
| 35 | +import org.onlab.onos.store.serializers.LinkKeySerializer; | ||
| 36 | +import org.onlab.onos.store.serializers.NodeIdSerializer; | ||
| 37 | +import org.onlab.onos.store.serializers.PortNumberSerializer; | ||
| 38 | +import org.onlab.onos.store.serializers.ProviderIdSerializer; | ||
| 10 | import org.onlab.packet.IpPrefix; | 39 | import org.onlab.packet.IpPrefix; |
| 40 | +import org.onlab.util.KryoPool; | ||
| 11 | import org.slf4j.Logger; | 41 | import org.slf4j.Logger; |
| 12 | import org.slf4j.LoggerFactory; | 42 | import org.slf4j.LoggerFactory; |
| 13 | 43 | ||
| 44 | +import java.net.URI; | ||
| 14 | import java.nio.ByteBuffer; | 45 | import java.nio.ByteBuffer; |
| 46 | +import java.util.ArrayList; | ||
| 47 | +import java.util.HashMap; | ||
| 15 | 48 | ||
| 16 | import static com.google.common.base.Preconditions.checkState; | 49 | import static com.google.common.base.Preconditions.checkState; |
| 17 | 50 | ||
| ... | @@ -24,11 +57,64 @@ public class MessageSerializer implements SerializationService { | ... | @@ -24,11 +57,64 @@ public class MessageSerializer implements SerializationService { |
| 24 | 57 | ||
| 25 | private final Logger log = LoggerFactory.getLogger(getClass()); | 58 | private final Logger log = LoggerFactory.getLogger(getClass()); |
| 26 | 59 | ||
| 27 | - private static final int METADATA_LENGTH = 16; // 8 + 4 + 4 | 60 | + private static final int METADATA_LENGTH = 12; // 8 + 4 |
| 28 | - private static final int LENGTH_OFFSET = 12; | 61 | + private static final int LENGTH_OFFSET = 8; |
| 29 | 62 | ||
| 30 | private static final long MARKER = 0xfeedcafebeaddeadL; | 63 | private static final long MARKER = 0xfeedcafebeaddeadL; |
| 31 | 64 | ||
| 65 | + private KryoPool serializerPool; | ||
| 66 | + | ||
| 67 | + @Activate | ||
| 68 | + public void activate() { | ||
| 69 | + setupKryoPool(); | ||
| 70 | + log.info("Started"); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + @Deactivate | ||
| 74 | + public void deactivate() { | ||
| 75 | + log.info("Stopped"); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * Sets up the common serialzers pool. | ||
| 80 | + */ | ||
| 81 | + protected void setupKryoPool() { | ||
| 82 | + // FIXME Slice out types used in common to separate pool/namespace. | ||
| 83 | + serializerPool = KryoPool.newBuilder() | ||
| 84 | + .register(ArrayList.class, | ||
| 85 | + HashMap.class, | ||
| 86 | + | ||
| 87 | + ControllerNode.State.class, | ||
| 88 | + Device.Type.class, | ||
| 89 | + | ||
| 90 | + DefaultControllerNode.class, | ||
| 91 | + DefaultDevice.class, | ||
| 92 | + MastershipRole.class, | ||
| 93 | + Port.class, | ||
| 94 | + Element.class, | ||
| 95 | + | ||
| 96 | + Link.Type.class, | ||
| 97 | + | ||
| 98 | + MessageSubject.class, | ||
| 99 | + HelloMessage.class, | ||
| 100 | + GoodbyeMessage.class, | ||
| 101 | + EchoMessage.class | ||
| 102 | + ) | ||
| 103 | + .register(IpPrefix.class, new IpPrefixSerializer()) | ||
| 104 | + .register(URI.class, new URISerializer()) | ||
| 105 | + .register(NodeId.class, new NodeIdSerializer()) | ||
| 106 | + .register(ProviderId.class, new ProviderIdSerializer()) | ||
| 107 | + .register(DeviceId.class, new DeviceIdSerializer()) | ||
| 108 | + .register(PortNumber.class, new PortNumberSerializer()) | ||
| 109 | + .register(DefaultPort.class, new DefaultPortSerializer()) | ||
| 110 | + .register(LinkKey.class, new LinkKeySerializer()) | ||
| 111 | + .register(ConnectPoint.class, new ConnectPointSerializer()) | ||
| 112 | + .register(DefaultLink.class, new DefaultLinkSerializer()) | ||
| 113 | + .build() | ||
| 114 | + .populate(1); | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + | ||
| 32 | @Override | 118 | @Override |
| 33 | public ClusterMessage decode(ByteBuffer buffer) { | 119 | public ClusterMessage decode(ByteBuffer buffer) { |
| 34 | try { | 120 | try { |
| ... | @@ -47,18 +133,12 @@ public class MessageSerializer implements SerializationService { | ... | @@ -47,18 +133,12 @@ public class MessageSerializer implements SerializationService { |
| 47 | // At this point, we have enough data to read a complete message. | 133 | // At this point, we have enough data to read a complete message. |
| 48 | long marker = buffer.getLong(); | 134 | long marker = buffer.getLong(); |
| 49 | checkState(marker == MARKER, "Incorrect message marker"); | 135 | checkState(marker == MARKER, "Incorrect message marker"); |
| 50 | - | ||
| 51 | - int subjectOrdinal = buffer.getInt(); | ||
| 52 | - MessageSubject subject = MessageSubject.values()[subjectOrdinal]; | ||
| 53 | length = buffer.getInt(); | 136 | length = buffer.getInt(); |
| 54 | 137 | ||
| 55 | // TODO: sanity checking for length | 138 | // TODO: sanity checking for length |
| 56 | byte[] data = new byte[length - METADATA_LENGTH]; | 139 | byte[] data = new byte[length - METADATA_LENGTH]; |
| 57 | buffer.get(data); | 140 | buffer.get(data); |
| 58 | - | 141 | + return (ClusterMessage) serializerPool.deserialize(data); |
| 59 | - // TODO: add deserialization hook here; for now this hack | ||
| 60 | - String[] fields = new String(data).split(":"); | ||
| 61 | - return new HelloMessage(new NodeId(fields[0]), IpPrefix.valueOf(fields[1]), Integer.parseInt(fields[2])); | ||
| 62 | 142 | ||
| 63 | } catch (Exception e) { | 143 | } catch (Exception e) { |
| 64 | // TODO: recover from exceptions by forwarding stream to next marker | 144 | // TODO: recover from exceptions by forwarding stream to next marker |
| ... | @@ -70,12 +150,8 @@ public class MessageSerializer implements SerializationService { | ... | @@ -70,12 +150,8 @@ public class MessageSerializer implements SerializationService { |
| 70 | @Override | 150 | @Override |
| 71 | public void encode(ClusterMessage message, ByteBuffer buffer) { | 151 | public void encode(ClusterMessage message, ByteBuffer buffer) { |
| 72 | try { | 152 | try { |
| 73 | - HelloMessage helloMessage = (HelloMessage) message; | 153 | + byte[] data = serializerPool.serialize(message); |
| 74 | buffer.putLong(MARKER); | 154 | buffer.putLong(MARKER); |
| 75 | - buffer.putInt(message.subject().ordinal()); | ||
| 76 | - | ||
| 77 | - String str = helloMessage.nodeId() + ":" + helloMessage.ipAddress() + ":" + helloMessage.tcpPort(); | ||
| 78 | - byte[] data = str.getBytes(); | ||
| 79 | buffer.putInt(data.length + METADATA_LENGTH); | 155 | buffer.putInt(data.length + METADATA_LENGTH); |
| 80 | buffer.put(data); | 156 | buffer.put(data); |
| 81 | 157 | ... | ... |
| ... | @@ -10,6 +10,15 @@ import java.util.Set; | ... | @@ -10,6 +10,15 @@ import java.util.Set; |
| 10 | public interface ClusterCommunicationService { | 10 | public interface ClusterCommunicationService { |
| 11 | 11 | ||
| 12 | /** | 12 | /** |
| 13 | + * Sends a message to all controller nodes. | ||
| 14 | + * | ||
| 15 | + * @param message message to send | ||
| 16 | + * @return true if the message was sent sucessfully to all nodes; false | ||
| 17 | + * if there is no stream or if there was an error for some node | ||
| 18 | + */ | ||
| 19 | + boolean send(ClusterMessage message); | ||
| 20 | + | ||
| 21 | + /** | ||
| 13 | * Sends a message to the specified controller node. | 22 | * Sends a message to the specified controller node. |
| 14 | * | 23 | * |
| 15 | * @param message message to send | 24 | * @param message message to send | ... | ... |
| 1 | +package org.onlab.onos.store.cluster.messaging; | ||
| 2 | + | ||
| 3 | +import org.onlab.onos.cluster.NodeId; | ||
| 4 | + | ||
| 5 | +/** | ||
| 6 | + * Goodbye message that nodes use to leave the cluster for good. | ||
| 7 | + */ | ||
| 8 | +public class GoodbyeMessage extends ClusterMessage { | ||
| 9 | + | ||
| 10 | + private NodeId nodeId; | ||
| 11 | + | ||
| 12 | + // For serialization | ||
| 13 | + private GoodbyeMessage() { | ||
| 14 | + super(MessageSubject.GOODBYE); | ||
| 15 | + nodeId = null; | ||
| 16 | + } | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * Creates a new goodbye message. | ||
| 20 | + * | ||
| 21 | + * @param nodeId sending node identification | ||
| 22 | + */ | ||
| 23 | + public GoodbyeMessage(NodeId nodeId) { | ||
| 24 | + super(MessageSubject.HELLO); | ||
| 25 | + this.nodeId = nodeId; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * Returns the sending node identifer. | ||
| 30 | + * | ||
| 31 | + * @return node identifier | ||
| 32 | + */ | ||
| 33 | + public NodeId nodeId() { | ||
| 34 | + return nodeId; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | +} |
| ... | @@ -8,6 +8,9 @@ public enum MessageSubject { | ... | @@ -8,6 +8,9 @@ public enum MessageSubject { |
| 8 | /** Represents a first greeting message. */ | 8 | /** Represents a first greeting message. */ |
| 9 | HELLO, | 9 | HELLO, |
| 10 | 10 | ||
| 11 | + /** Signifies node's intent to leave the cluster. */ | ||
| 12 | + GOODBYE, | ||
| 13 | + | ||
| 11 | /** Signifies a heart-beat message. */ | 14 | /** Signifies a heart-beat message. */ |
| 12 | ECHO | 15 | ECHO |
| 13 | 16 | ... | ... |
-
Please register or login to post a comment