使用 Simpy 模拟具有设置/开启时间的资源系统

问题描述

我想创建一个带有服务器的系统,这些服务器需要时间进行设置才能提供服务。由于服务器的数量会随着时间的推移而变化,我认为 Container 资源可能会起作用。每当有客户到达队列时,就会建立一个服务器,较早到来的客户将占用较早开启的服务器,如下所示。

  1. 客户 1 到达并请求服务器。
  2. 服务器 1 在 t1 秒内完成设置。
  3. 客户 2 到达并请求服务器。
  4. 服务器 2 在 t2 秒内设置完毕。
  5. 服务器 2 已开启。
  6. 客户 1 占用服务器 2。

我想知道如何才能使上述过程真正起作用。如果我按如下方式安排事件,每个循环似乎都在 yield req 之后卡住(不yield req 将此请求放入队列并在稍后打开准备好的服务器时立即完成请求在yield servers.server.put(1)?)

with servers.computer.get(1) as req: 
      yield req                       
      yield env.timeout(switch_on())    #switch on time
      yield servers.server.put(1) 

"""0.09948 Job0 arrives
0.25648 Job1 arrives
0.37188 Job2 arrives
0.47028 Job3 arrives
0.53916 Job4 arrives
0.66893 Job5 arrives
"""

如果我按照下面的顺序改变顺序,那么无论服务器打开的顺序如何,Customer-i 肯定会占用 Server-i。

with servers.computer.get(1) as req: 
      yield env.timeout(switch_on())    #switch on time
      yield servers.server.put(1)
      yield req 

这是完整的代码

import simpy

LAM = 8  #arival rate of jobs
MU = 2  #service rate
ALPHA = 12  #set up rate
NUM_SERVERS = 5
MAX_NUM_JOB = 10000000000
UNTIL = 10

def generate_interarrival():
  return np.random.exponential(1/LAM)

def generate_service():
  return np.random.exponential(1/MU)

def switch_on():
  return np.random.exponential(1/ALPHA)

class Generate_Job(): 
    def arriving_job(env,servers):
        global  num_current_jobs,num_server_on,leaving_time_list
        for i in range(MAX_NUM_JOB):
            job = Job(name="Job%01d" % (i))
            yield env.timeout(generate_interarrival())
            print('{0:.5f}'.format(env.Now),job.name,"arrives")

            env.process(job.handling(env,servers))

class Room:                             # A room containing servers (resource)
    def __init__(self,env):
        self.computer = simpy.Container(env,capacity = 10000,init = 0)


class Job(object):
    def __init__(self,name):
        self.name = name

    def handling(self,env,servers):
        with servers.computer.get(1) as req: 
            yield req                       
            yield env.timeout(switch_on())    #switch on time
            yield servers.server.put(1)            
            print('{0:.5f}'.format(env.Now),self.name,"occupies a server--")        
            yield env.timeout(generate_service())    #service time
            print('{0:.5f}'.format(env.Now),"leaves")


np.random.seed(0)
env = simpy.Environment()
servers  = Room(env)
env.process(Generate_Job.arriving_job(env,servers))
env.run(until = UNTIL) 


解决方法

需要改变两件事

您的资源池一开始是空的,这意味着您对资源的第一次请求将永远等待并且永远不会到达添加资源的代码。

我在请求前添加了一个检查,如果需要,将添加资源。由于添加资源存在延迟,因此资源请求在资源“启动”时仍然会等待。不要使用 yield 调用此检查,否则请求将等待添加新资源,如果由于另一个添加而添加资源,或者如果另一个作业完成并在添加新资源之前返回资源,则请求将丢失

此外,容器不会在“with”语句的末尾返回资源,只有资源会这样做。

这是固定代码,让我知道你的想法

"""
Simulation of a dynamic server pool

Server pool starts empty and servers are added as needed,but there is a delay 
simulating start up time before the server is available to fulfill a resource request

Programmer: Matt
    Wrote original version

Programmer: Michael R. Gibbs
    Added server check for dynamicaly adding servers
    Fixed return of resorces to pool
"""

import simpy
import numpy as np

LAM = 8  #arival rate of jobs
MU = 2  #service rate
ALPHA = 12  #set up rate
NUM_SERVERS = 5
MAX_NUM_JOB = 10000000000
UNTIL = 10

def generate_interarrival():
  return np.random.exponential(1/LAM)

def generate_service():
  return np.random.exponential(1/MU)

def switch_on():
  return np.random.exponential(1/ALPHA)

def check_servers(env,servers):
    """
    Checks the server pool to see if the pool has any avalable servers
    if not then add a server,(there will be a delay before added server becomes available)

    Call this without a yield so it does not block if a server is added
    """

    print('{0:.5f}'.format(env.now),"checking server pool","requests:",len(servers.get_queue),"servers:",servers.level)
    
    if len(servers.get_queue) >= servers.level:
        # will need another server
        d = switch_on()
        print('{0:.5f}'.format(env.now),"adding a server at " + '{0:.5f}'.format(env.now + d) + " --")
        yield env.timeout(d)    #switch on time
        yield servers.put(1)      
        print('{0:.5f}'.format(env.now),"added a server--")

class Generate_Job(): 
    def arriving_job(env,servers):
        global  num_current_jobs,num_server_on,leaving_time_list
        for i in range(MAX_NUM_JOB):
            job = Job(name="Job%01d" % (i))
            yield env.timeout(generate_interarrival())
            print('{0:.5f}'.format(env.now),job.name,"arrives")

            env.process(job.handling(env,servers))

class Room:                             # A room containing servers (resource)
    def __init__(self,env):
        self.computer = simpy.Container(env,capacity = 10000,init = 0)


class Job(object):
    def __init__(self,name):
        self.name = name

    def handling(self,env,servers):

        # added a check to see if a resource pool needs another server.
        env.process(check_servers(env,servers.computer))
        print('{0:.5f}'.format(env.now),self.name,"requesting a server--") 

        with servers.computer.get(1) as req: 
            
            yield req 
            # if the queue is empty then the req is never filled and the next lines are never called
            # need to do this before the rescource requests
            #                       
            # yield env.timeout(switch_on())    #switch on time
            # yield servers.server.put(1)            
            print('{0:.5f}'.format(env.now),"occupies a server--")        
            yield env.timeout(generate_service())    #service time
            print('{0:.5f}'.format(env.now),"leaves")

            # containers do not return a resouce at the end of a "with"
            # added a put
            yield servers.computer.put(1)


np.random.seed(0)
env = simpy.Environment()
servers  = Room(env)
env.process(Generate_Job.arriving_job(env,servers))
env.run(until = UNTIL)