-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathserver.go
More file actions
197 lines (157 loc) · 4.45 KB
/
server.go
File metadata and controls
197 lines (157 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
/*
Inspired by
http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang/
*/
package main
import (
"os"
"fmt"
"log"
"encoding/json"
"io"
"net/http"
"strconv"
"time"
)
const (
DEFAULT_MAX_WORKERS int = 500
DEFAULT_MAX_JOBS_IN_QUEUE int = 500
DEFAULT_MAX_LENGTH int64 = 1048576
)
var (
MaxWorker int
MaxQueue int
MaxLength int64
)
type PayloadCollection struct {
WindowsVersion string `json:"version"`
Token string `json:"token"`
Payloads []Payload `json:"data"`
}
type Payload struct {
Waza int `json:"waza"`
}
// Job represents the job to be run
type Job struct {
Payload Payload
}
func (p *Payload) UploadToS3() error {
fmt.Println("Heavy work now")
time.Sleep(time.Second)
return nil
}
// A buffered channel that we can send work requests on.
var JobQueue chan Job
// Worker represents the worker that executes the job
type Worker struct {
WorkerPool chan chan Job
JobChannel chan Job
quit chan bool
}
func NewWorker(workerPool chan chan Job) Worker {
return Worker{
WorkerPool: workerPool,
JobChannel: make(chan Job),
quit: make(chan bool)}
}
// Start method starts the run loop for the worker, listening for a quit channel in
// case we need to stop it
func (w Worker) Start() {
go func() {
for {
// register the current worker into the worker queue.
w.WorkerPool <- w.JobChannel
select {
case job := <-w.JobChannel:
// we have received a work request.
if err := job.Payload.UploadToS3(); err != nil {
log.Printf("Error uploading to S3: %s", err.Error())
}
case <-w.quit:
// we have received a signal to stop
return
}
}
}()
}
// Stop signals the worker to stop listening for work requests.
func (w Worker) Stop() {
go func() {
w.quit <- true
}()
}
type Dispatcher struct {
// A pool of workers channels that are registered with the dispatcher
WorkerPool chan chan Job
}
func NewDispatcher(maxWorkers int) *Dispatcher {
pool := make(chan chan Job, maxWorkers)
return &Dispatcher{WorkerPool: pool}
}
func (d *Dispatcher) Run() {
// starting n number of workers
for i := 0; i < MaxWorker; i++ {
worker := NewWorker(d.WorkerPool)
worker.Start()
}
go d.dispatch()
}
func (d *Dispatcher) dispatch() {
for {
select {
case job := <-JobQueue:
// a job request has been received
go func(job Job) {
// try to obtain a worker job channel that is available.
// this will block until a worker is idle
jobChannel := <-d.WorkerPool
// dispatch the job to the worker job channel
jobChannel <- job
}(job)
}
}
}
func payloadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
// Read the body into a string for json decoding
var content = &PayloadCollection{}
err := json.NewDecoder(io.LimitReader(r.Body, MaxLength)).Decode(&content)
if err != nil {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(http.StatusBadRequest)
return
}
// Go through each payload and queue items individually to be posted to S3
for _, payload := range content.Payloads {
// let's create a job with the payload
work := Job{Payload: payload}
// Push the work onto the queue.
JobQueue <- work
}
w.WriteHeader(http.StatusOK)
}
func initialize() {
var err error
if MaxWorker, err = strconv.Atoi(os.Getenv("MAX_WORKERS")); err != nil {
MaxWorker = DEFAULT_MAX_WORKERS
}
if MaxQueue, err = strconv.Atoi(os.Getenv("MAX_QUEUES")); err != nil {
MaxQueue = DEFAULT_MAX_JOBS_IN_QUEUE
}
if MaxLength, err = strconv.ParseInt(os.Getenv("MAX_LENGTH"), 10, 64); err != nil {
MaxLength = DEFAULT_MAX_LENGTH
}
JobQueue = make(chan Job, MaxQueue)
}
func main() {
initialize()
dispatcher := NewDispatcher(MaxWorker)
dispatcher.Run()
http.HandleFunc("/", payloadHandler)
err := http.ListenAndServe(":8080", nil)
log.Println("listening on localhost:8080")
fmt.Println(err)
}