Don't like ads? PRO users don't see any ads ;-)
Guest

web server simulation in python

By: a guest on May 15th, 2012  |  syntax: Python  |  size: 5.11 KB  |  hits: 31  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #!/usr/bin/env python
  2. from SimPy.Simulation import *
  3. from itertools import cycle
  4. import csv, random, sys
  5.  
  6. SERVERS              = 2    # number of web servers
  7. WORKERS_PER_SERVER   = 25   # worker processes per web server
  8. ACCEPT_QUEUE_LENGTH  = 100  # maximum number of waiting requests
  9. ARRIVAL_RATE_MIN     = 20   # min arrival rate of web requests
  10. ARRIVAL_RATE_MAX     = 70   # max arrival rate of web requests
  11. ARRIVAL_RATE_INC     = 5    # arrival rate increment
  12. CACHE_HIT_RATIO      = 0.75 # fraction of rquests served from cache
  13. CACHE_SERVE_RATE     = 10   # rate for serving requests from cache
  14. DB_SERVE_RATE        = 0.5  # rate for serving data to the web server
  15. DB_CONNECTIONS       = 25   # number of allowed connections to the database
  16. SIMULATION_STOP      = 100  # time to stop simulation
  17. SIMULATION_RUNS      = 50   # number of simulation runs for point estimator
  18.  
  19. class LoadBalancer(object):
  20.     '''
  21.    This class represents a load balancer with a round-robin policy.
  22.    Calling assign() on an instance returns the next server resource
  23.    that a request will be assigned to.
  24.    '''
  25.     def __init__(self):
  26.         servers = [Resource(
  27.             capacity = WORKERS_PER_SERVER,
  28.             name = 'web server %d' % i,
  29.             unitName = 'worker'
  30.         ) for i in xrange(SERVERS)]
  31.         self.servers = cycle(servers)
  32.        
  33.     def assign(self):
  34.         return self.servers.next()
  35.  
  36. class Request(Process):
  37.     '''
  38.    Represents a web request.  The handle method manages the process
  39.    execution model for an arbitrary web request.
  40.    '''
  41.     def __init__(self, name, server, database, wait_monitor, serve_monitor):
  42.         super(Request, self).__init__(name=name)
  43.         self.server = server
  44.         self.database = database
  45.         self.wait_monitor = wait_monitor
  46.         self.serve_monitor = serve_monitor
  47.        
  48.     def handle(self):
  49.         # If the accept queue is full, this request is rejected
  50.         if len(self.server.waitQ) < ACCEPT_QUEUE_LENGTH:
  51.             arrival_time = now()
  52.                                
  53.             # Get assigned to a worker process
  54.             yield request, self, self.server
  55.             start_time = now()
  56.             self.wait_monitor.observe(start_time - arrival_time)
  57.                
  58.             # If the requested page isn't cached, incur a database hit.
  59.             if random.random() >= CACHE_HIT_RATIO:
  60.                 yield request, self, self.database
  61.                 yield hold, self, random.expovariate(DB_SERVE_RATE)
  62.                 yield release, self, self.database
  63.                
  64.             # Always incur the cache serve rate.  This is the time used
  65.             # to simply load and serve requested content.
  66.             yield hold, self, random.expovariate(CACHE_SERVE_RATE)
  67.                
  68.             # Release the worker process
  69.             self.serve_monitor.observe(now() - start_time)
  70.             yield release, self, self.server
  71.  
  72. class RequestGenerator(Process):
  73.     '''Generates incoming web requests with exponential inter-arrival times'''
  74.     def __init__(self, name, arrival_rate, load_balancer):
  75.         super(RequestGenerator, self).__init__(name=name)
  76.         self.arrival_rate = arrival_rate
  77.         self.load_balancer = load_balancer
  78.         self.database = Resource(
  79.             capacity = DB_CONNECTIONS,
  80.             name = 'database',
  81.             unitName = 'connection'
  82.         )
  83.        
  84.         # Monitors for data collection
  85.         self.wait_monitor = Monitor()
  86.         self.serve_monitor = Monitor()
  87.  
  88.     def generate(self):
  89.         i = 0
  90.         while True:
  91.             yield hold, self, random.expovariate(self.arrival_rate)
  92.             r = Request(
  93.                 'request %d' % i,
  94.                 self.load_balancer.assign(),
  95.                 self.database,
  96.                 self.wait_monitor,
  97.                 self.serve_monitor
  98.             )
  99.             activate(r, r.handle())
  100.             i += 1
  101.  
  102. if __name__ == '__main__':
  103.     out = csv.writer(sys.stdout, delimiter='\t')
  104.     out.writerow(['arrival rate', 'mean throughput', 'mean wait time', 'mean serve time'])
  105.        
  106.     # Run our simulation for a variety of arrival rates
  107.     for arrival_rate in xrange(
  108.         ARRIVAL_RATE_MIN,
  109.         ARRIVAL_RATE_MAX + ARRIVAL_RATE_INC,
  110.         ARRIVAL_RATE_INC
  111.     ):
  112.         throughput = []
  113.         wait_times = []
  114.         serve_time = []
  115.        
  116.         for i in xrange(SIMULATION_RUNS):
  117.             initialize()
  118.        
  119.             gen = RequestGenerator('req generator', arrival_rate, LoadBalancer())
  120.             activate(gen, gen.generate())
  121.                
  122.             simulate(until=SIMULATION_STOP)
  123.             through = gen.serve_monitor.count() / float(SIMULATION_STOP)
  124.             throughput.append(through)
  125.                
  126.             wait_times.append(gen.wait_monitor.mean())
  127.             serve_time.append(gen.serve_monitor.mean())
  128.        
  129.         # Take final means as the means of means
  130.         mean_through = sum(throughput) / len(throughput)
  131.         mean_wait = sum(wait_times) / len(wait_times)
  132.         mean_serve = sum(serve_time) / len(serve_time)
  133.        
  134.         out.writerow([arrival_rate, mean_through, mean_wait, mean_serve])