Java [Peer-to-Peer]:可运行的程序意外停止/阻塞

时间:2019-03-21 13:17:24

标签: java concurrency deadlock distributed p2p

我正在开发一个简单的分布式分类帐。我希望能够在不同的端口上启动可以相互通信的节点。然后,每个程序都有一个文件,它将在其中写入新发现的节点。

首先,仅将最可靠的节点硬编码到该文件中。 程序上会发生以下情况:

1)我启动一个新节点,该节点启动一个HTTP服务器(我使用com.sun.HttpServer)。服务器具有GetAddress处理程序,该处理程序侦听转到指定URI的请求。然后,它获取IP和PORT(在URI查询参数中指定),获取 known_nodes.txt 文件的信号量,并将新接收的对等地址写入该文件(如果尚未存在) ,然后将新更新文件的内容作为json列表发送回请求者。

2)在我的Node类中(如前所述,该类在单独的线程上启动HTTPServer),我创建了ScheduledExecutorService并赋予它每隔几秒钟运行一次的运行时间,其工作是连接到URL出现在 known_nodes.txt 文件中,并询问他们的known_nodes。如果收到的已知节点文件中以前不存在节点,则将覆盖文件。

现在! 如果我启动一个节点,然后尝试从浏览器请求它,那么一切都会按计划进行-我们收到一个请求,将其写入我们的文件,然后我们的runnable将尝试连接到请求中指定的地址。如果我们捕获到一个 SocketTimeoutException ,则将从我们的known_nodes.txt文件中删除该地址。

问题出现了,当我启动两个节点时,假设在端口8001和8002上运行。请注意,每个节点都有其自己的known_nodes文件。 发生的情况是,其中一个节点将停止运行DiscoverAddresses任务,而另一个将不会。如此有效,一个节点停止了接收请求。

NB!将要停止其计划任务的节点将至少发送一个发现请求,然后死亡/阻止(?)。

以下是可运行任务的代码:

    @Override
public void run() {
    log.info("still running ");
    PeerAddressesHolder inactiveNodes = new PeerAddressesHolder();
    ApplicationConfiguration appConf = ApplicationConfiguration.getInstance();

    for (PeerAddress peerAddress : knownNodes.getAddresses()) {
        if (isSameNode(peerAddress)) {
            continue;
        }

        String urlString = String.format("http://%s:%s%s?myport=%d", peerAddress.getIP(), peerAddress.getPort(), Constants.GET_ADDRESS, myPort);
        try {
            StringBuilder result = new StringBuilder();
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();

            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");

            try (InputStream connInputStream = conn.getInputStream();
                 InputStreamReader ir = new InputStreamReader(connInputStream);
                 BufferedReader br = new BufferedReader(ir)){

                String line;
                while ((line = br.readLine()) != null) {
                    result.append(line).append('\n');
                }
            } catch (Exception e) {
                log.warn("Couldn't read from connection input stream",e);
            }


            PeerAddressesHolder peerAddressesHolder = gson.fromJson(result.toString(), PeerAddressesHolder.class);

            boolean fetchedNew = false;
            for (PeerAddress fetchedAddress : peerAddressesHolder.getAddresses()) {
                if (!isValidAddress(peerAddress)) {
                    log.warn("Peer has sent us a null-address. It will be ignored.");
                    return;
                }
                if (!knownNodes.contains(fetchedAddress)) {
                    knownNodes.addAddress(fetchedAddress);
                    fetchedNew = true;
                }
            }

            if (fetchedNew) {
                FileUtils.writeToFile(appConf.getKnownNodesFilePath(), gson.toJson(knownNodes), false);
            }


        } catch (SocketTimeoutException e) {
            if (appConf.getMostReliableNodes().contains(peerAddress)) {
                log.warn("Most reliable node not available: " + peerAddress);
            } else {
                inactiveNodes.addAddress(peerAddress);
                log.warn("Connection timeout from " + peerAddress + ". It will be removed.");

            }

        } catch (Exception e) {
            log.warn("Couldn't discover new addresses." + e);
        }
    }

    try {
        knownNodes.removeAll(inactiveNodes.getAddresses());
        FileUtils.writeToFile(appConf.getKnownNodesFilePath(), gson.toJson(knownNodes), false);
    } catch (IOException ioe) {
        log.warn("Couldn't write to file after deleting dead node", ioe);
    }
}

这是我在创建节点时启动它的方式。

public NetworkNode(int port) {
    this.appConf = ApplicationConfiguration.getInstance();
    this.port = port;

    log.info("Starting a new node on port " + port);
    try {
        this.knownNodes = FileUtils.createPeerAddressesList(appConf.getKnownNodesFilePath());
    } catch (Exception e) {
        log.error("Error while trying to construct a list of peer addresses from file content on path: " + appConf.getKnownNodesFilePath());
    }

    scheduledExecutorService = Executors.newScheduledThreadPool(4);
    scheduledExecutorService.scheduleAtFixedRate(new DiscoverAddressesTask(knownNodes, this.port), 3, 4, TimeUnit.SECONDS);

处理文件读/写的方法都是使用try-with-resources构造完成的,因此我最初的想法(即由于某些未关闭的流而导致可运行停止)可能无效。

0 个答案:

没有答案