查找未使用的免费应用端口 - 找到一些算法

时间:2016-12-01 15:00:57

标签: javascript node.js algorithm random port

我在程序中使用以下API来解除自由端口并将其提供给应用程序运行

portscanner.findAPortNotInUse(3000, 65000, '127.0.0.1', function(error, port) {
  console.log('AVAILABLE PORT AT: ' + port)
})

https://github.com/baalexander/node-portscanner

这个自由端口被提供给应用程序使用和工作正常。 问题是如果我向application A提供一个空闲端口并且应用程序尚未占用它(有时需要一些时间......)并且还有其他application B并请求免费端口所以它给 APP B提供应用程序A的端口 导致问题的原因...... 是否有任何优雅的方法来解决它?

我的应用程序没有状态,因此无法保存到哪个应用程序获取哪个端口...

有一个解决方案,我们可以随机化范围,但这不健全......

在我的应用程序中,我得到应该提供自由端口运行的应用程序的URL。

更新

不能使用一些经纪人或其他可以控制这种情况的其他我需要找到一些算法(可能带有一些智能随机),这可以帮助我做到这一点内部,即我的程序就像单身,我需要一些技巧如何在50000 to 65000之间提供端口,以便减少提供给应用程序的端口的冲突量

更新2

我决定尝试下面的内容你觉得怎么样?

使用lodash https://lodash.com/docs/4.17.2#random来确定with循环之间的端口,这些循环为下面的范围提供3(或更多,如果有意义)数字

portscanner.findAPortNotInUse([50001, 60000, 600010], '127.0.0.1', function(err, port) {
    if(err) {
        console.log("error!!!-> " +err);
    }else {
        console.log('Port Not in Use ' + port);
    }

//using that in a loop 
var aa = _.random(50000, 65000); 

然后如果我在端口出现错误,即所有3个端口都被占用,请再次运行此过程3个其他随机数量的建议欢迎!!! 我试图找到一些尽可能避免碰撞的方法 ......

5 个答案:

答案 0 :(得分:2)

我只是接受这样一个事实,即在分布式系统中出现问题并重新尝试操作(即获得一个空闲端口),如果它在第一次尝试时出于任何原因失败。

幸运的是,有很多npm模块已经为你做了,例如retry

使用此模块,您可以重试异步操作,直到成功为止,并配置等待策略,以及最多应重试多少次等等...

为了提供代码示例,它基本上归结为:

const operation = retry.operation();

operation.attempt(currentAttempt => {
  findAnUnusedPortAndUseIt(err => {
    if (operation.retry(err)) {
      return;
    }

    callback(err ? operation.mainError() : null);
  });
});

此解决方案的好处是:

  • 无锁定工作,即效率很高,如果一切正常,资源使用率会很低。
  • 没有中央经纪人或其他类似的工作。
  • 适用于任何规模的分布式系统。
  • 使用可在分布式系统中重复使用的模式,以解决各种问题。
  • 使用经过实战考验的坚固npm模块,而不是手写所有这些东西。
  • 不要求您以主要方式更改代码,而只是添加几行。

希望这会有所帮助: - )

答案 1 :(得分:0)

当管理多个应用程序或多个服务器时,第一次必须正确(没有重试)时,您需要一个单一的事实来源。只要资源是“可锁定的”,同一台机器上的应用程序就可以与数据库,代理服务器甚至文件进行通信。 (服务器以类似的方式工作,但不使用本地文件)。

所以你的流程将是这样的:

  1. App A向服务发送请求锁定的请求。
  2. 确认锁定后,启动端口扫描程序
  3. 使用端口时,释放锁定。
  4. 同样,这可能是一个" PortService"你写出那些未使用的端口,或者在一些共享资源中的简单锁定,因此两个东西同时获得相同的端口。

    希望您能找到适合您应用的内容。

答案 2 :(得分:0)

我建议你寻找一种保留状态的方法。在内存中,即使是临时状态也总比没有好。这样你至少可以避免给出你已经发出的端口。因为那些很可能不再是免费的。 (这就像保存它们并重新生成一个随机端口一样简单,如果你发现你发现了一个已经发出的随机端口)。如果您不想要冲突,请将模块构建为具有状态,以便可以避免它们。如果你不想这样做,你必须接受在不需要时会发生碰撞。

如果您获得的网址是随机的,那么您可以做的最好是随机猜测。如果你可以推导出一些属性,其中URL唯一且一致地不同,你可以围绕它设计一些东西。

代码示例:

checkPortInUse

注意:

  • ls -lR /path/to/dir/*.jpg | wc -l 可能是异步的,所以你必须这样做 适应那个。
  • 你说'在50000到65000之间'。这是从50000到65000。

答案 3 :(得分:0)

如果您的应用程序可以使用SO_REUSEADDR等选项打开端口,但操作系统将列表中的端口保持在TIME_WAIT状态,则可以使用SO_REUSEADDR绑定/打开要返回的端口,立即将其关闭并将其返回给应用程序。因此对于TIME_WAIT期间(取决于操作系统,它可以是30秒,实际时间应该由实验/管理决定/设置或找到)端口列表将显示此端口为已占用。

如果您的端口查找器没有为TIME_WAIT状态的端口提供端口号,则问题通过相对昂贵的打开/关闭套接字操作解决。

答案 4 :(得分:-1)

你试过这样的事吗?

var portscanner = require('portscanner')

module.exports = function (ip, minport) {

  var pendings = [];
  var allocated = [];
  var pendingsearch = false;

  function start(){
    if (!pendingsearch && pendings.length) {
      pendingsearch = true;
      var ready = pendings.shift();
      var min = getMax();
      min = min===-1?minport:min+1;
      var max = min + 100;
      portscanner.findAPortNotInUse(min, max, ip, function(err, port) {
        if (err) {
          pendings.unshift(ready);
        } else if (allocated.indexOf(port)>-1) {
          pendings.unshift(ready);
        } else {
          allocated.push(port);
          ready(port);
        }
        restart();
      })
    }
  }
  function restart(){
    pendingsearch = false;
    setTimeout(start, 0);
  }
  function getMax(){
    var max = -1;
    allocated.forEach(function(p) {
      if (p>max) {
        max = p
      }
    })
    return max
  }

  function findPort(ready) {
    pendings.push(ready);
    start();
  };

  function updateAllocated() {
    var tocheck = [].concat(allocated);
    var todo = tocheck.length;
    tocheck.forEach(function(port, index) {
      portscanner.checkPortStatus(port, ip, function(error, status) {
        if (status==='closed') {
          allocated.splice(allocated.indexOf(port), 1);
        }
        if (index===tocheck.length-1) {
          setTimeout(updateAllocated, 1000);
        }
      })
    });
    if (tocheck.length<1) {
      setTimeout(updateAllocated, 1000);
    }
  };
  var update = setTimeout(updateAllocated, 1000);

  return {
    findPort: findPort,
    stop: function(){
      clearTimeout(update);
    },
  }
};