o
To test the capacity of the time server, you need a program to
generate requests. One could cobble something together using a
script that makes multiple calls to
curl
but that is, at best, imprecise. Furthermore, since the overhead of
creating a new process for each request is so high, this
approach would be limited in its effectiveness.
Write a Go program
loadgen
that takes the following flags:
--rate
:
average rate of requests (per second)
--burst
:
number of concurrent requests to issue
--timeout-ms
:
max time to wait for response
--runtime
:
number of seconds to process
--url
:
URL to sample
The
rate
parameter determines the average number of requests (per second) to
issue. The requests will be issued concurrently and will last for
up to
timeout-ms
milliseconds.
At any given moment there will be some number of active requests
depending on the server's responsiveness.
For this proof-of-concept, we don't have to care about the number of inflight requests. Assume the system has enough resouces to handle it.
To issue requests at the given
rate
one could issue a request and then wait for a period of
1 / rate
,
but realistic traffic is more random, arriving in clusters or
bursts.
To simulate bursts at the given request
rate
,
use
time.Tick()
to create a ticker with a period of
burst / rate
.
You will also need to convert the period to microseconds (multiply
by 1,000,000, so the period calculation will be
time.Duration(burst * 1000000 / rate) * time.Microsecond)
On each tick, spawn off
burst
goroutines, with each routine making one
get request.
Keep a tally of the toatal number of requests issued and counts of status codes returned by century (100s, 200s, 300s, 400s, 500s). Also keep count of the number of timeout errors.
The basic operations of a counter are to reset the value, increment the value, and emit a map of all counter/value pairs. The map, of course, should be a copy.
An easy way to implement a counter is by using a
map[string] int
.
The counter "variable" is a string, not a Go variable.
Counters do need to be thread-safe. If you use a map internally, the dump operation must return a copy of the map.
Design an appropriate package interface and implement the package.
Implement unit tests for your counter package using the testing package. Hint: your test should be concurrent.
Implement the load generation function as an infinite loop.
The main program should call the
load
function (or whatever you want to call it)
in a goroutine and then sleep for
runtime
seconds before collecting the statistics and terminating the
program.
Run loadgen targeting your timeserver. If you run the system with parameters like this:
./bin/authserver --log=etc/auth-log.xml &
./bin/timeserver --log=etc/log-01.xml --port=8081 --max-inflight=80 --avg-response-ms=500 --response-deviation-ms=300 &
./bin/loadgen --url='http://localhost:8081/time' --runtime=10 --rate=200 --burst=20 --timeout-ms=1000
Your output should look something like this:
Total : 2000
100s: 0
200s: 1341
300s: 0
400s: 0
500s: 524
Errors: 66
In addition to the usual hand-in requirements (source code and build script or Makefile), include text file(s) containing 3 sample runs of the the load generator running against timeserver. Experiment with different combinations of timeserver and loadgen parameters.