use of net.dempsy.transport.Sender in project Dempsy by Dempsy.
the class OutgoingDispatcher method dispatch.
@Override
public void dispatch(final KeyedMessageWithType message) {
boolean messageSentSomewhere = false;
try {
ApplicationState tmp = outbounds.get();
// if we're in the midst of an update then we really want to wait for the new state.
while (tmp == null) {
if (!isRunning.get()) {
LOGGER.debug("Router dispatch called while stopped.");
return;
}
if (// however, if we never were ready then we're not in the midst
!isReady.get())
// of an update.
throw new IllegalStateException("Dispatch used before Router is ready.");
// let the other threads do their thing. Maybe we'll be updated sooner.
Thread.yield();
// are we updated yet?
tmp = outbounds.get();
}
final ApplicationState cur = tmp;
final Map<String, RoutingStrategy.Router[]> outboundsByMessageType = cur.outboundsByMessageType;
// =================================================================================
// For each message type, determine the set of Routers. The goal of this loop is to set
// 'containerByNodeAddress'
final Map<NodeAddress, ContainerAddress> containerByNodeAddress = new HashMap<>();
for (final String mt : message.messageTypes) {
final RoutingStrategy.Router[] routers = outboundsByMessageType.get(mt);
if (routers == null)
LOGGER.trace("No cluster that handles messages of type {}", mt);
else {
// the set of ContainerAddresses that this message will be sent to.
for (int i = 0; i < routers.length; i++) {
final ContainerAddress ca = routers[i].selectDestinationForMessage(message);
// it's possible 'ca' is null when we don't know where to send the message.
if (ca == null)
LOGGER.debug("No way to send the message {} to specific cluster for the time being", message.message);
else {
// When the message will be sent to 2 different clusters, but both clusters
// are hosted in the same node, then we send 1 message to 1 ContainerAddress
// where the 'clusters' field contains both container ids.
final ContainerAddress already = containerByNodeAddress.get(ca.node);
if (already != null) {
final int[] ia = new int[already.clusters.length + ca.clusters.length];
System.arraycopy(already.clusters, 0, ia, 0, already.clusters.length);
System.arraycopy(ca.clusters, 0, ia, already.clusters.length, ca.clusters.length);
containerByNodeAddress.put(ca.node, new ContainerAddress(ca.node, ia));
} else
containerByNodeAddress.put(ca.node, ca);
}
}
}
}
for (final Map.Entry<NodeAddress, ContainerAddress> e : containerByNodeAddress.entrySet()) {
final NodeAddress curNode = e.getKey();
final ContainerAddress curAddr = e.getValue();
final RoutedMessage toSend = new RoutedMessage(curAddr.clusters, message.key, message.message);
if (curNode.equals(thisNode)) {
// this shouldn't count since Router is an OUTGOING class
nodeReciever.feedbackLoop(toSend, false);
messageSentSomewhere = true;
} else {
final Sender sender = cur.getSender(curNode);
if (sender == null) {
// router update is probably behind the routing strategy update
if (isRunning.get())
LOGGER.error("Couldn't send message to " + curNode + " from " + thisNodeId + " because there's no " + Sender.class.getSimpleName());
} else {
sender.send(toSend);
messageSentSomewhere = true;
}
}
}
if (containerByNodeAddress.size() == 0) {
if (LOGGER.isTraceEnabled())
LOGGER.trace("There appears to be no valid destination addresses for the message {}", SafeString.objectDescription(message.message));
}
} finally {
if (!messageSentSomewhere)
statsCollector.messageNotSent();
}
}
use of net.dempsy.transport.Sender in project Dempsy by Dempsy.
the class ApplicationState method apply.
public ApplicationState apply(final ApplicationState.Update update, final TransportManager tmanager, final NodeStatsCollector statsCollector, final RoutingStrategyManager manager) {
// apply toDelete first.
final Set<NodeAddress> toDelete = update.toDelete;
if (toDelete.size() > 0) {
// just clear all senders.
for (final NodeAddress a : toDelete) {
final Sender s = senders.get(a);
if (s != null)
s.stop();
}
}
final Map<NodeAddress, NodeInformation> newCurrent = new HashMap<>();
// the one's to carry over.
final Set<NodeInformation> leaveAlone = update.leaveAlone;
for (final NodeInformation cur : leaveAlone) {
newCurrent.put(cur.nodeAddress, cur);
}
// add new senders
final Set<NodeInformation> toAdd = update.toAdd;
for (final NodeInformation cur : toAdd) {
newCurrent.put(cur.nodeAddress, cur);
}
// now flush out the remaining caches.
// collapse all clusterInfos
final Set<ClusterInformation> allCis = new HashSet<>();
newCurrent.values().forEach(ni -> allCis.addAll(ni.clusterInfoByClusterId.values()));
final Map<String, RoutingStrategy.Router> newOutboundByClusterName = new HashMap<>();
final Map<String, Set<String>> cnByType = new HashMap<>();
final Set<String> knownClusterOutbounds = new HashSet<>(outboundByClusterName_.keySet());
for (final ClusterInformation ci : allCis) {
final String clusterName = ci.clusterId.clusterName;
final RoutingStrategy.Router ob = outboundByClusterName_.get(clusterName);
knownClusterOutbounds.remove(clusterName);
if (ob != null)
newOutboundByClusterName.put(clusterName, ob);
else {
final RoutingStrategy.Factory obfactory = manager.getAssociatedInstance(ci.routingStrategyTypeId);
final RoutingStrategy.Router nob = obfactory.getStrategy(ci.clusterId);
newOutboundByClusterName.put(clusterName, nob);
}
// add all of the message types handled.
ci.messageTypesHandled.forEach(mt -> {
Set<String> entry = cnByType.get(mt);
if (entry == null) {
entry = new HashSet<>();
cnByType.put(mt, entry);
}
entry.add(clusterName);
});
}
return new ApplicationState(newOutboundByClusterName, cnByType, newCurrent, tmanager, thisNode);
}
use of net.dempsy.transport.Sender in project Dempsy by Dempsy.
the class ApplicationState method apply.
public ApplicationState apply(final ApplicationState.Update update, final TransportManager tmanager, final NodeStatsCollector statsCollector, final RoutingStrategyManager manager, final String thisNodeId) {
// apply toDelete first.
final Set<NodeAddress> toDelete = update.toDelete;
final boolean infoEnabled = LOGGER_SESSION.isInfoEnabled();
if (toDelete.size() > 0) {
// just clear all senders.
if (infoEnabled)
LOGGER_SESSION.info("[{}] Applying update to topology resulting in removing several destinations:", thisNodeId);
for (final NodeAddress a : toDelete) {
final Sender s = senders.remove(a);
if (infoEnabled)
LOGGER_SESSION.info("[{}] removing sender ({}) to {}", thisNodeId, s, a);
if (s != null)
s.stop();
}
}
final Map<NodeAddress, NodeInformation> newCurrent = new HashMap<>();
// the one's to carry over.
final Set<NodeInformation> leaveAlone = update.leaveAlone;
if (leaveAlone.size() > 0)
if (infoEnabled)
LOGGER_SESSION.info("[{}] Applying update to topology resulting in leaving several destinations:", thisNodeId);
for (final NodeInformation cur : leaveAlone) {
if (infoEnabled)
LOGGER_SESSION.info("[{}] leaving alone : {}", thisNodeId, Optional.ofNullable(cur).map(ni -> ni.nodeAddress).orElse(null));
newCurrent.put(cur.nodeAddress, cur);
}
// add new senders
final Set<NodeInformation> toAdd = update.toAdd;
if (toAdd.size() > 0)
if (infoEnabled)
LOGGER_SESSION.info("[{}] Applying update to topology resulting in adding several destinations:", thisNodeId);
for (final NodeInformation cur : toAdd) {
if (infoEnabled)
LOGGER_SESSION.info("[{}] adding : {}", thisNodeId, Optional.ofNullable(cur).map(ni -> ni.nodeAddress).orElse(null));
newCurrent.put(cur.nodeAddress, cur);
}
// now flush out the remaining caches.
// collapse all clusterInfos
final Set<ClusterInformation> allCis = new HashSet<>();
newCurrent.values().forEach(ni -> allCis.addAll(ni.clusterInfoByClusterId.values()));
final Map<String, RoutingStrategy.Router> newOutboundByClusterName = new HashMap<>();
final Map<String, Set<String>> cnByType = new HashMap<>();
final Set<String> knownClusterOutbounds = new HashSet<>(outboundByClusterName_.keySet());
for (final ClusterInformation ci : allCis) {
final String clusterName = ci.clusterId.clusterName;
final RoutingStrategy.Router ob = outboundByClusterName_.get(clusterName);
knownClusterOutbounds.remove(clusterName);
if (ob != null)
newOutboundByClusterName.put(clusterName, ob);
else {
final RoutingStrategy.Factory obfactory = manager.getAssociatedInstance(ci.routingStrategyTypeId);
final RoutingStrategy.Router nob = obfactory.getStrategy(ci.clusterId);
newOutboundByClusterName.put(clusterName, nob);
}
// add all of the message types handled.
ci.messageTypesHandled.forEach(mt -> {
Set<String> entry = cnByType.get(mt);
if (entry == null) {
entry = new HashSet<>();
cnByType.put(mt, entry);
}
entry.add(clusterName);
});
}
return new ApplicationState(newOutboundByClusterName, cnByType, newCurrent, tmanager, thisNode, senders);
}
use of net.dempsy.transport.Sender in project Dempsy by Dempsy.
the class BlockingQueueTest method testBlockingQueueOverflow.
/**
* Test overflow for a blocking Transport around a queue with depth one. While the transport will not call the, and does not even have a , overflow handler,
* every message will call the overflow handler on
* the receiver since the queue is always full.
*
* @throws Throwable
*/
@Test
public void testBlockingQueueOverflow() throws Throwable {
final AtomicReference<String> message = new AtomicReference<String>(null);
final ArrayBlockingQueue<Object> input = new ArrayBlockingQueue<>(1);
try (@SuppressWarnings("resource") SystemPropertyManager // test only works when the blocking queue blocks
props = new SystemPropertyManager().set(BlockingQueueSenderFactory.class.getPackageName() + "." + BlockingQueueSenderFactory.BLOCKING_KEY, "true");
final TestInfrastructure infra = new TestInfrastructure(new DefaultThreadingModel("BQTest-testBlockingQueueOverflow-"));
final Receiver r = new BlockingQueueReceiver(input);
final TransportManager tranMan = chain(new TransportManager(), c -> c.start(infra));
final SenderFactory sf = tranMan.getAssociatedInstance(transportTypeId)) {
final Sender sender = sf.getSender(r.getAddress(infra));
final AtomicBoolean finallySent = new AtomicBoolean(false);
final AtomicLong receiveCount = new AtomicLong();
// fill up queue
sender.send("Hello");
final Thread t = new Thread(() -> {
try {
sender.send("Hello again");
} catch (final MessageTransportException | InterruptedException e) {
throw new RuntimeException(e);
}
finallySent.set(true);
});
t.start();
Thread.sleep(100);
// the thread should be hung blocked on the send
assertFalse(finallySent.get());
// Start the receiver to read
r.start((final String msg) -> {
message.set(new String(msg));
receiveCount.incrementAndGet();
return true;
}, infra);
// 2 messages should have been read and the 2nd should be "Hello again"
assertTrue(poll(o -> "Hello again".equals(message.get())));
// The thread should shut down eventually
assertTrue(poll(o -> !t.isAlive()));
}
}
use of net.dempsy.transport.Sender in project Dempsy by Dempsy.
the class TcpTransportTest method testConnectionRecovery.
@Test
public void testConnectionRecovery() throws Exception {
try (final ServiceTracker tr = new ServiceTracker()) {
final AbstractTcpReceiver<?, ?> r = tr.track(receiver.get()).numHandlers(2).useLocalHost(true);
// can't test connection recovery here.
if (!(r instanceof DisruptableRecevier))
return;
final ThreadingModel tm = tr.track(new DefaultThreadingModel(TcpTransportTest.class.getSimpleName() + ".testConnectionRecovery"));
final Infrastructure infra = tr.track(new TestInfrastructure(tm));
final TcpAddress addr = r.getAddress(infra);
LOGGER.debug(addr.toString());
final AtomicReference<RoutedMessage> rm = new AtomicReference<>(null);
r.start((Listener<RoutedMessage>) msg -> {
rm.set(msg);
return true;
}, infra);
try (final SenderFactory sf = senderFactory.get()) {
sf.start(new TestInfrastructure(tm) {
@Override
public String getNodeId() {
return "test";
}
});
final Sender sender = sf.getSender(addr);
sender.send(new RoutedMessage(new int[] { 0 }, "Hello", "Hello"));
assertTrue(poll(o -> rm.get() != null));
assertEquals("Hello", rm.get().message);
assertTrue(((DisruptableRecevier) r).disrupt(addr));
final AtomicBoolean stop = new AtomicBoolean(false);
final RoutedMessage resetMessage = new RoutedMessage(new int[] { 0 }, "RESET", "RESET");
final Thread senderThread = new Thread(() -> {
try {
while (!stop.get()) {
sender.send(resetMessage);
dontInterrupt(() -> Thread.sleep(100));
}
} catch (final InterruptedException ie) {
if (!stop.get())
LOGGER.error("Interrupted send");
}
}, "testConnectionRecovery-sender");
senderThread.start();
try {
assertTrue(poll(o -> "RESET".equals(rm.get().message)));
} finally {
stop.set(true);
if (!poll(senderThread, t -> {
dontInterrupt(() -> t.join(10000));
return !t.isAlive();
}))
LOGGER.error("FAILED TO SHUT DOWN TEST SENDING THREAD. THREAD LEAK!");
}
}
}
}
Aggregations