R:在队列中:根据队列长度和持续时间来建模服务器容量的变化

时间:2019-03-26 18:50:31

标签: r event-simulation

我正在尝试对系统进行如下建模:

到达根据预定义的时间表生成,并具有数据帧提供的已知处理时间。在模拟开始时,有一台服务器的容量等于min_daemons。到目前为止很简单,但是nxt部分却很棘手:根据以下算法,整个模拟过程中的间隔[min_daemons,max_daemons]可能会有所不同:

如果在模拟过程中的任何时间,队列长度达到或超过了incr_count的值,并且对于incr_delay保持等于或大于此级别,则将附加容量单元添加到主服务器。只要容量不超过max_daemons,就可以随时发生。

反之亦然。如果在任何时候队列长度小于decr_count,并且对于decr_delay保持在此级别或以下,则将删除一个容量单位,有可能降至min_daemons级别。

我为所有到达的服务器创建了一条轨迹,当满足上述更改服务器容量的条件时,这些分支就会分支。问题在于,容量的变化总是与到达事件有关。我真正想要的是一个独立于到达轨迹的过程,该过程始终监视队列长度并进行适当的容量更改。

我考虑用某种虚拟的到达过程来完成此操作,例如在模拟的每一秒钟,但是我不确定是否可以防止虚拟的到达与服务器容量的真实到达竞争。

#instantiate simulation environment
env <- simmer("queues") %>% add_resource("daemon",1) %>% add_global("incr_start",99999) %>% add_global("decr_start",99999)

run <-  trajectory() %>% 
  branch(option = function() if (get_queue_count(env,"daemon") >=  incr_count) {1}
                             else if (get_queue_count(env,"daemon") <= decr_count) {2}
                             else {3}
         ,
         continue = c(T, T, T)
         ,
         trajectory("increment?")
         %>% branch(option = function() if (now(env) - get_global(env,"incr_start") >= incr_delay
                                            & get_capacity(env,"daemon") < max_daemons) {1}
                                        else if (get_global(env,"incr_start")==99999) {2}
                                        else {3}
                    ,
                    continue = c(T, T, T)
                    ,
                    trajectory("increment")
                    %>% log_(function () {paste("Queue size is: ",get_queue_count(env,"daemon"))})
                    %>% log_(function () 
                      {paste("Queue has exceeded count for ",now(env)-get_global(env,"incr_start")," seconds.")})
                    %>% set_capacity(resource = "daemon", value = 1, mod="+")
                    ,
                    trajectory("update incr start")
                    %>% set_global("incr_start",now(env))
                    %>% log_("Queue count is now above increment count. Starting increment timer.")
                    ,
                    trajectory("do nothing")
                    %>% log_("Did not meet increment criteria. Doing nothing.")
         )
         ,
         trajectory("decrement?")
         %>% branch(option = function() if (now(env) - get_global(env,"decr_start") >= decr_delay
                                            & get_capacity(env,"daemon") > min_daemons) {1}
                                        else if (get_global(env,"decr_start")==99999) {2}
                                        else {3}
                    ,
                    continue = c(T, T, T)
                    ,
                    trajectory("decrement")
                    %>% log_(function () {paste("Queue size is: ",get_queue_count(env,"daemon"))})
                    %>% log_(function () 
                      {paste("Queue has been less than count for ",now(env)-get_global(env,"incr_start")," seconds.")})
                    %>% set_capacity(resource = "daemon", value = -1, mod="+")
                    ,
                    trajectory("update decr start")
                    %>% set_global("decr_start",now(env))
                    %>% log_("Queue count is now below decrement count. Starting decrement timer.")
                    ,
                    trajectory("do nothing")
                    %>% log_("Did not meet decrement criteria. Doing nothing.")
         )
         ,
         trajectory("reset_timer")
         %>% log_("Did not meet criteria to increment or decrement. Resetting timers.")
         %>% set_global("decr_start", values = 99999)
         %>% set_global("decr_start", values = 99999)
  ) %>%
  seize("daemon") %>% 
  log_("Now running") %>%
  log_(function () {paste(get_queue_count(env,"daemon")," runs in the queue.")}) %>%
  timeout_from_attribute("service") %>% 
  release("daemon") %>% 
  log_("Run complete")

env %>% 
  add_dataframe("run", run, arr,time="absolute") %>% 
  run(200)

我需要进行更多调试,以验证仿真是否按预期工作,但我完全理解该模型是错误的。我希望设计不会对它的有效性造成太大的影响,但我也想获得有关如何设计更真实的东西的反馈。

1 个答案:

答案 0 :(得分:0)

定期检查状态会破坏具有离散事件的整个目的。我们这里有一个异步过程,所以对此建模的正确方法是使用信号。

我们需要自问:什么时候可以排队...

  1. 增加?到达seize()活动时。因此,在尝试抢占资源之前,我们需要检查入队到达的人数并采取相应措施:

    • 如果它等于decr_count,则必须发送一个信号来取消任何降低服务器容量的尝试。
    • 如果等于incr_count - 1,则必须发送信号以请求增加服务器的容量。
    • 不执行其他操作。
  2. 减少?服务到达后(即继续进行seize()之后的下一个活动。因此,在占用资源之后,我们还需要检查已排队到达的人数:

    • 如果它等于incr_count - 1,则必须发送一个信号来取消任何增加服务器容量的尝试。
    • 如果它等于decr_count,则必须发送一个信号来请求减少服务器的容量。
    • 不执行其他操作。

检查排队到达的数量并确定需要哪种信号(如果有)的过程可以捆绑在可重用的函数中(我们将其称为check_queue),如下所示:

library(simmer)

env <- simmer()

check_queue <- function(.trj, resource, mod, lim_queue, lim_server) {
  .trj %>% branch(
    function() {
      if (get_queue_count(env, resource) == lim_queue[1])
        return(1)
      if (get_queue_count(env, resource) == lim_queue[2] &&
          get_capacity(env, resource)    != lim_server)
        return(2)
      0 # pass
    },
    continue = c(TRUE, TRUE),
    trajectory() %>% send(paste("cancel", mod[1])),
    trajectory() %>% send(mod[2])
  )
}

main <- trajectory() %>%
  check_queue("resource", c("-", "+"), c(decr_count, incr_count-1), max_daemons) %>%
  seize("resource") %>%
  check_queue("resource", c("+", "-"), c(incr_count-1, decr_count), min_daemons) %>%
  timeout_from_attribute("service") %>%
  release("resource")

这样,主要轨迹非常简单。然后,我们需要几个过程来接收此类信号并在延迟一段时间后增加/减少容量:

change_capacity <- function(resource, mod, delay, limit) {
  trajectory() %>%
    untrap(paste("cancel", mod)) %>%
    trap(mod) %>%
    wait() %>%
    # signal received
    untrap(mod) %>%
    trap(paste("cancel", mod),
         handler = trajectory() %>%
           # cancelled! start from the beginning
           rollback(Inf)) %>%
    timeout(delay) %>%
    set_capacity(resource, as.numeric(paste0(mod, 1)), mod="+") %>%
    # do we need to keep changing the capacity?
    rollback(2, check=function() get_capacity(env, resource) != limit) %>%
    # start from the beginning
    rollback(Inf)
}

incr_capacity <- change_capacity("resource", "+", incr_delay, max_daemons)
decr_capacity <- change_capacity("resource", "-", decr_delay, min_daemons)

最后,我们将资源,我们的过程和数据添加到仿真环境中:

env %>%
  add_resource("resource", min_daemons) %>%
  add_generator("incr", incr_capacity, at(0)) %>%
  add_generator("decr", decr_capacity, at(0)) %>%
  add_dataframe("arrival", main, data)

请注意,我没有检查此代码。可能需要进行一些调整,但是总的思路就在那里。