Search in sources :

Example 11 with NodeDescriptor

use of services.moleculer.transporter.tcp.NodeDescriptor in project moleculer-java by moleculer-java.

the class TcpTransporter method sendGossipRequest.

/**
 * Create and send a Gossip request packet.
 */
protected Tree sendGossipRequest() {
    try {
        // Update CPU
        NodeDescriptor descriptor = getDescriptor();
        int cpu = monitor.getTotalCpuPercent();
        descriptor.writeLock.lock();
        try {
            descriptor.updateCpu(cpu);
        } finally {
            descriptor.writeLock.unlock();
        }
        // Are we alone?
        if (nodes.isEmpty()) {
            return null;
        }
        // Add "online" and "offline" blocks
        Collection<NodeDescriptor> descriptors = nodes.values();
        int size = nodes.size() + 32;
        FastBuildTree online = new FastBuildTree(size);
        FastBuildTree offline = new FastBuildTree(size);
        // Add current node
        descriptor.readLock.lock();
        try {
            ArrayList<Object> array = new ArrayList<>(3);
            array.add(descriptor.seq);
            array.add(descriptor.cpuSeq);
            array.add(descriptor.cpu);
            online.putUnsafe(nodeID, array);
        } finally {
            descriptor.readLock.unlock();
        }
        // Separate online and offline nodes
        String[] liveEndpoints = new String[size];
        String[] unreachableEndpoints = new String[size];
        int liveEndpointCount = 0;
        int unreachableEndpointCount = 0;
        // Loop on registered nodes
        for (NodeDescriptor node : descriptors) {
            node.readLock.lock();
            try {
                if (node.offlineSince > 0) {
                    // Offline
                    if (unreachableEndpointCount < unreachableEndpoints.length) {
                        unreachableEndpoints[unreachableEndpointCount++] = node.nodeID;
                    }
                    if (node.seq > 0) {
                        offline.put(node.nodeID, node.seq);
                    }
                } else {
                    if (!node.local) {
                        // Online
                        if (liveEndpointCount < liveEndpoints.length) {
                            liveEndpoints[liveEndpointCount++] = node.nodeID;
                        }
                        if (node.seq > 0) {
                            ArrayList<Object> array = new ArrayList<>(3);
                            array.add(node.seq);
                            array.add(node.cpuSeq);
                            array.add(node.cpu);
                            online.putUnsafe(node.nodeID, array);
                        }
                    }
                }
            } finally {
                node.readLock.unlock();
            }
        }
        // Create gossip request
        FastBuildTree root = new FastBuildTree(4);
        root.putUnsafe("ver", ServiceBroker.PROTOCOL_VERSION);
        root.putUnsafe("sender", nodeID);
        root.putUnsafe("online", online.asObject());
        if (!offline.isEmpty()) {
            root.putUnsafe("offline", offline.asObject());
        }
        // Serialize gossip packet (JSON, MessagePack, etc.)
        byte[] packet = serialize(PACKET_GOSSIP_REQ_ID, root);
        // Do gossiping with a live endpoint
        if (liveEndpointCount > 0) {
            sendGossipToRandomEndpoint(liveEndpoints, liveEndpointCount, packet, root);
        }
        // Do gossiping with a unreachable endpoint
        if (unreachableEndpointCount > 0) {
            // 10 nodes:
            // 1 offline / (9 online + 1) = 0.10
            // 3 offline / (7 online + 1) = 0.37
            // 5 offline / (5 online + 1) = 0.83
            // 9 offline / (1 online + 1) = 4.50
            double ratio = (double) unreachableEndpointCount / ((double) liveEndpointCount + 1);
            // Random number between 0.0 and 1.0
            double random = rnd.nextDouble();
            if (random < ratio) {
                sendGossipToRandomEndpoint(unreachableEndpoints, unreachableEndpointCount, packet, root);
            }
        }
        // For unit testing
        return root;
    } catch (Exception cause) {
        logger.error("Unable to send gossip message to peer!", cause);
    }
    return null;
}
Also used : FastBuildTree(services.moleculer.util.FastBuildTree) NodeDescriptor(services.moleculer.transporter.tcp.NodeDescriptor) ArrayList(java.util.ArrayList)

Example 12 with NodeDescriptor

use of services.moleculer.transporter.tcp.NodeDescriptor in project moleculer-java by moleculer-java.

the class TcpTransporter method connect.

@Override
public void connect() {
    try {
        // Create reader and writer
        disconnect();
        reader = new TcpReader(this);
        writer = new TcpWriter(this);
        // Disable offline timeout when use host list
        if (urls != null && urls.length > 0) {
            offlineTimeout = 0;
            nodes.clear();
            for (String url : urls) {
                int i = url.indexOf("://");
                if (i > -1 && i < url.length() - 4) {
                    url = url.substring(i + 3);
                }
                url = url.replace('/', ':');
                String[] parts = url.split(":");
                if (parts.length < 3) {
                    logger.warn("Invalid URL format (" + url + ")! Valid syntax is \"tcp://host:port/nodeID\" or \"host:port/nodeID\"!");
                    continue;
                }
                int port;
                try {
                    port = Integer.parseInt(parts[1]);
                } catch (Exception e) {
                    logger.warn("Invalid URL format (" + url + ")! Valid syntax is \"tcp://host:port/nodeID\" or \"host:port/nodeID\"!");
                    continue;
                }
                String sender = parts[2];
                if (sender.equals(nodeID)) {
                    // TCP server's port (port in URL list)
                    this.port = port;
                    continue;
                }
                String host = parts[0];
                nodes.put(sender, new NodeDescriptor(sender, useHostname, host, port));
            }
        } else if (offlineTimeout > 0 && offlineTimeout < 15) {
            offlineTimeout = 15;
        }
        // Process basic properties (eg. "prefix")
        heartbeatTimeout = 0;
        heartbeatInterval = 0;
        // Start TCP server
        reader.connect();
        currentPort = reader.getCurrentPort();
        // Create descriptor of current node
        Tree info = registry.getDescriptor();
        info.put("port", currentPort);
        info.put("seq", "0");
        cachedDescriptor = new NodeDescriptor(nodeID, useHostname, true, info);
        // Start data writer (TCP client)
        writer.connect();
        // TCP + UDP mode ("zero config")
        if (urls == null || urls.length == 0) {
            locator = new UDPLocator(nodeID, this, scheduler);
            locator.connect();
        }
        // Start gossiper
        gossiperTimer = scheduler.scheduleWithFixedDelay(this::sendGossipRequest, gossipPeriod, gossipPeriod, TimeUnit.SECONDS);
        // Start timeout checker's timer
        if (checkTimeoutTimer == null && offlineTimeout > 0) {
            int period = Math.max(offlineTimeout / 3, 10);
            checkTimeoutTimer = scheduler.scheduleAtFixedRate(this::checkTimeouts, period, period, TimeUnit.SECONDS);
        }
        // Ok, transporter started
        logger.info("Message receiver started on tcp://" + getHostName() + ':' + currentPort + ".");
    } catch (Exception cause) {
        String msg = cause.getMessage();
        if (msg == null || msg.isEmpty()) {
            msg = "Unable to start TCP transporter!";
        } else if (!msg.endsWith("!") && !msg.endsWith(".")) {
            msg += "!";
        }
        logger.warn(msg);
        reconnect();
    }
}
Also used : TcpWriter(services.moleculer.transporter.tcp.TcpWriter) UDPLocator(services.moleculer.transporter.tcp.UDPLocator) TcpReader(services.moleculer.transporter.tcp.TcpReader) NodeDescriptor(services.moleculer.transporter.tcp.NodeDescriptor) CommonUtils.readTree(services.moleculer.util.CommonUtils.readTree) FastBuildTree(services.moleculer.util.FastBuildTree) Tree(io.datatree.Tree)

Example 13 with NodeDescriptor

use of services.moleculer.transporter.tcp.NodeDescriptor in project moleculer-java by moleculer-java.

the class TcpTransporter method processGossipResponse.

// --- GOSSIP RESPONSE MESSAGE RECEIVED ---
protected void processGossipResponse(Tree data) throws Exception {
    // Debug
    if (debug) {
        String sender = data.get("sender", (String) null);
        logger.info("Gossip response received from \"" + sender + "\" node:\r\n" + data);
    }
    // Online / offline nodes in responnse
    Tree online = data.get("online");
    Tree offline = data.get("offline");
    // Process "online" block
    if (online != null) {
        for (Tree row : online) {
            // Get nodeID
            String nodeID = row.getName();
            if (this.nodeID.equals(nodeID)) {
                continue;
            }
            int size = row.size();
            if (!row.isEnumeration() || size < 1 || size > 3) {
                logger.warn("Invalid \"offline\" block: " + row);
                continue;
            }
            // Get parameters from input
            Tree info = null;
            long cpuSeq = 0;
            int cpu = 0;
            if (row.size() == 1) {
                info = row.get(0);
            } else if (row.size() == 2) {
                cpuSeq = row.get(0).asLong();
                cpu = row.get(1).asInteger();
            } else if (row.size() == 3) {
                info = row.get(0);
                cpuSeq = row.get(1).asLong();
                cpu = row.get(2).asInteger();
            } else {
                logger.warn("Invalid \"online\" block: " + row.toString(false));
                continue;
            }
            if (info != null) {
                // Update "info" block,
                // send updated, connected or reconnected event
                updateNodeInfo(nodeID, info);
            }
            if (cpuSeq > 0) {
                // We update our CPU info
                NodeDescriptor node = nodes.get(nodeID);
                if (node != null) {
                    node.writeLock.lock();
                    try {
                        node.updateCpu(cpuSeq, cpu);
                    } finally {
                        node.writeLock.unlock();
                    }
                }
            }
        }
    }
    // Process "offline" block
    if (offline != null) {
        for (Tree row : offline) {
            String nodeID = row.getName();
            NodeDescriptor node;
            if (this.nodeID.equals(nodeID)) {
                long seq = row.asLong();
                node = getDescriptor();
                node.writeLock.lock();
                try {
                    long newSeq = Math.max(node.seq, seq + 1);
                    if (node.seq < newSeq) {
                        node.seq = newSeq;
                        node.info.put("seq", newSeq);
                    }
                } finally {
                    node.writeLock.unlock();
                }
                continue;
            }
            node = nodes.get(nodeID);
            if (node == null) {
                return;
            }
            if (!row.isPrimitive()) {
                logger.warn("Invalid \"offline\" block: " + row);
                continue;
            }
            // Get parameters from input
            boolean disconnected = false;
            node.writeLock.lock();
            try {
                long seq = row.asLong();
                if (node.seq < seq && node.markAsOffline(seq)) {
                    // We know it is online, so we change it to offline
                    // Remove remote actions and listeners
                    registry.removeActions(node.nodeID);
                    eventbus.removeListeners(node.nodeID);
                    writer.close(node.nodeID);
                    disconnected = true;
                }
            } finally {
                node.writeLock.unlock();
            }
            if (node != null && disconnected) {
                // Notify listeners (not unexpected disconnection)
                logger.info("Node \"" + node.nodeID + "\" disconnected.");
                broadcastNodeDisconnected(node.info, false);
            }
        }
    }
}
Also used : NodeDescriptor(services.moleculer.transporter.tcp.NodeDescriptor) CommonUtils.readTree(services.moleculer.util.CommonUtils.readTree) FastBuildTree(services.moleculer.util.FastBuildTree) Tree(io.datatree.Tree)

Example 14 with NodeDescriptor

use of services.moleculer.transporter.tcp.NodeDescriptor in project moleculer-java by moleculer-java.

the class TcpTransporter method registerAsNewNode.

protected void registerAsNewNode(String sender, String host, int port) {
    // Check node
    if (sender == null || sender.isEmpty()) {
        throw new IllegalArgumentException("Empty sender field!");
    }
    if (host == null || host.isEmpty()) {
        throw new IllegalArgumentException("Empty host field!");
    }
    if (port < 1) {
        throw new IllegalArgumentException("Invalid port value (" + port + ")!");
    }
    if (nodeID.equalsIgnoreCase(sender)) {
        return;
    }
    NodeDescriptor node = nodes.get(sender);
    if (node == null) {
        // Add as new, offline node
        try {
            nodes.put(sender, new NodeDescriptor(sender, useHostname, host, port));
            logger.info("Node \"" + sender + "\" registered.");
        } catch (Exception cause) {
            logger.warn("Unable to register new node!", cause);
        }
    } else {
        node.writeLock.lock();
        try {
            // Host or port number changed
            if (!node.host.equalsIgnoreCase(host) || node.port != port) {
                node.host = host;
                node.port = port;
                if (node.info != null) {
                    if (useHostname) {
                        node.info.put("hostname", host);
                    } else {
                        Tree ipList = node.info.get("ipList");
                        if (ipList == null) {
                            ipList = node.info.putList("ipList");
                        } else {
                            ipList.clear();
                        }
                        ipList.add(host);
                    }
                    node.info.put("port", port);
                }
                writer.close(sender);
            }
        } finally {
            node.writeLock.unlock();
        }
    }
}
Also used : NodeDescriptor(services.moleculer.transporter.tcp.NodeDescriptor) CommonUtils.readTree(services.moleculer.util.CommonUtils.readTree) FastBuildTree(services.moleculer.util.FastBuildTree) Tree(io.datatree.Tree)

Example 15 with NodeDescriptor

use of services.moleculer.transporter.tcp.NodeDescriptor in project moleculer-java by moleculer-java.

the class Transporter method checkTimeouts.

// --- TIMEOUT PROCESS ---
protected void checkTimeouts() {
    // Compute timeout limits
    long now = System.currentTimeMillis();
    Iterator<NodeDescriptor> i;
    NodeDescriptor node;
    // Check offline timeout
    if (offlineTimeout > 0) {
        i = nodes.values().iterator();
        long offlineTimeoutMillis = offlineTimeout * 1000L;
        while (i.hasNext()) {
            node = i.next();
            node.readLock.lock();
            try {
                if (node.offlineSince > 0 && now - node.offlineSince > offlineTimeoutMillis) {
                    // Remove node from Map
                    i.remove();
                    logger.info("Node \"" + nodeID + "\" is no longer registered because it was inactive for " + offlineTimeout + " seconds.");
                }
            } finally {
                node.readLock.unlock();
            }
        }
    }
    // Check heartbeat timeout
    if (heartbeatTimeout > 0) {
        long heartbeatTimeoutMillis = heartbeatTimeout * 1000L;
        i = nodes.values().iterator();
        LinkedList<NodeDescriptor> disconnectedNodes = new LinkedList<>();
        while (i.hasNext()) {
            node = i.next();
            node.writeLock.lock();
            try {
                if (node.cpuWhen > 0 && now - node.cpuWhen > heartbeatTimeoutMillis && node.markAsOffline()) {
                    // Remove services and listeners
                    registry.removeActions(node.nodeID);
                    eventbus.removeListeners(node.nodeID);
                    disconnectedNodes.add(node);
                }
            } finally {
                node.writeLock.unlock();
            }
        }
        i = disconnectedNodes.iterator();
        while (i.hasNext()) {
            node = i.next();
            // Notify listeners
            logger.info("Node \"" + node.nodeID + "\" is no longer available because it hasn't submitted heartbeat signal for " + heartbeatTimeout + " seconds.");
            broadcastNodeDisconnected(node.info, true);
        }
    }
}
Also used : NodeDescriptor(services.moleculer.transporter.tcp.NodeDescriptor) LinkedList(java.util.LinkedList)

Aggregations

NodeDescriptor (services.moleculer.transporter.tcp.NodeDescriptor)17 Tree (io.datatree.Tree)12 FastBuildTree (services.moleculer.util.FastBuildTree)9 CommonUtils.readTree (services.moleculer.util.CommonUtils.readTree)5 ArrayList (java.util.ArrayList)2 LinkedList (java.util.LinkedList)2 Color (java.awt.Color)1 PrintWriter (java.io.PrintWriter)1 StringWriter (java.io.StringWriter)1 HashMap (java.util.HashMap)1 ExecutorService (java.util.concurrent.ExecutorService)1 ScheduledExecutorService (java.util.concurrent.ScheduledExecutorService)1 Test (org.junit.Test)1 ServiceBroker (services.moleculer.ServiceBroker)1 ServiceBrokerConfig (services.moleculer.config.ServiceBrokerConfig)1 RemoteAddress (services.moleculer.transporter.tcp.RemoteAddress)1 TcpReader (services.moleculer.transporter.tcp.TcpReader)1 TcpWriter (services.moleculer.transporter.tcp.TcpWriter)1 UDPLocator (services.moleculer.transporter.tcp.UDPLocator)1