2021-02-25 17:46:40 -08:00
|
|
|
package router
|
|
|
|
|
|
|
|
import (
|
2021-10-14 10:08:39 -07:00
|
|
|
fmt "fmt"
|
|
|
|
log "log"
|
|
|
|
http "net/http"
|
|
|
|
sync "sync"
|
2021-02-25 17:46:40 -08:00
|
|
|
|
2021-09-24 10:37:32 -07:00
|
|
|
gs "git.dragonheim.net/dragonheim/gagent/internal/gstructs"
|
2021-02-25 17:46:40 -08:00
|
|
|
|
2021-10-14 10:08:39 -07:00
|
|
|
promhttp "github.com/prometheus/client_golang/prometheus/promhttp"
|
|
|
|
|
2021-02-25 17:46:40 -08:00
|
|
|
zmq "github.com/pebbe/zmq4"
|
|
|
|
)
|
|
|
|
|
2021-05-30 15:37:49 -07:00
|
|
|
// @TODO -- This was documented in the example, and I am unclear what it does
|
2021-05-21 23:03:50 +00:00
|
|
|
const (
|
|
|
|
WORKER_READY = "\001" // Signals worker is ready
|
|
|
|
)
|
2021-02-25 17:46:40 -08:00
|
|
|
|
2021-05-21 23:03:50 +00:00
|
|
|
// Main is the initiation function for a Router
|
2021-10-04 13:06:09 -07:00
|
|
|
func Main(wg *sync.WaitGroup, config gs.GagentConfig) {
|
|
|
|
defer wg.Done()
|
2021-10-15 11:27:38 -07:00
|
|
|
http.Handle("/metrics", promhttp.Handler())
|
|
|
|
http.HandleFunc("/hello", answerClient)
|
|
|
|
|
2021-05-21 23:03:50 +00:00
|
|
|
log.Printf("[INFO] Starting router\n")
|
2021-02-26 08:14:28 -08:00
|
|
|
|
2021-05-30 15:37:49 -07:00
|
|
|
clientSock, _ := zmq.NewSocket(zmq.ROUTER)
|
|
|
|
defer clientSock.Close()
|
2021-02-26 08:14:28 -08:00
|
|
|
|
2021-05-30 15:37:49 -07:00
|
|
|
workerSock, _ := zmq.NewSocket(zmq.DEALER)
|
|
|
|
defer workerSock.Close()
|
2021-10-15 11:27:38 -07:00
|
|
|
workerListener := fmt.Sprintf("tcp://%s:%d", config.ListenAddr, config.WorkerPort)
|
|
|
|
clientListener := fmt.Sprintf("%s:%d", config.ListenAddr, config.ClientPort)
|
2021-02-26 08:14:28 -08:00
|
|
|
|
2021-10-15 11:27:38 -07:00
|
|
|
go func() {
|
|
|
|
http.ListenAndServe(clientListener, nil)
|
|
|
|
}()
|
2021-10-14 10:08:39 -07:00
|
|
|
|
2021-10-15 11:27:38 -07:00
|
|
|
workerSock.Bind(workerListener)
|
2021-02-26 08:14:28 -08:00
|
|
|
|
2021-05-21 23:03:50 +00:00
|
|
|
workers := make([]string, 0)
|
2021-02-26 08:14:28 -08:00
|
|
|
|
2021-05-21 23:03:50 +00:00
|
|
|
poller1 := zmq.NewPoller()
|
2021-05-30 15:37:49 -07:00
|
|
|
poller1.Add(workerSock, zmq.POLLIN)
|
2021-02-25 17:46:40 -08:00
|
|
|
|
2021-05-21 23:03:50 +00:00
|
|
|
poller2 := zmq.NewPoller()
|
2021-05-30 15:37:49 -07:00
|
|
|
poller2.Add(workerSock, zmq.POLLIN)
|
2021-02-25 17:46:40 -08:00
|
|
|
|
2021-05-21 23:03:50 +00:00
|
|
|
LOOP:
|
|
|
|
for {
|
|
|
|
// Poll frontend only if we have available workers
|
|
|
|
var sockets []zmq.Polled
|
|
|
|
var err error
|
|
|
|
if len(workers) > 0 {
|
|
|
|
sockets, err = poller2.Poll(-1)
|
|
|
|
} else {
|
|
|
|
sockets, err = poller1.Poll(-1)
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
break // Interrupted
|
|
|
|
}
|
|
|
|
for _, socket := range sockets {
|
|
|
|
switch s := socket.Socket; s {
|
2021-10-15 11:27:38 -07:00
|
|
|
case workerSock:
|
|
|
|
// Handle worker activity on backend
|
2021-05-21 23:03:50 +00:00
|
|
|
// Use worker identity for load-balancing
|
|
|
|
msg, err := s.RecvMessage(0)
|
|
|
|
if err != nil {
|
2021-10-15 11:27:38 -07:00
|
|
|
// Interrupted
|
|
|
|
break LOOP
|
2021-05-21 23:03:50 +00:00
|
|
|
}
|
|
|
|
var identity string
|
|
|
|
identity, msg = unwrap(msg)
|
|
|
|
log.Printf("[DEBUG] Worker message received: %s", msg)
|
|
|
|
workers = append(workers, identity)
|
|
|
|
|
|
|
|
// Forward message to client if it's not a READY
|
2021-10-14 10:08:39 -07:00
|
|
|
// if msg[0] != WORKER_READY {
|
|
|
|
// clientSock.SendMessage(msg)
|
|
|
|
// }
|
2021-02-25 17:46:40 -08:00
|
|
|
|
2021-05-30 15:37:49 -07:00
|
|
|
case clientSock:
|
2021-05-21 23:03:50 +00:00
|
|
|
// Get client request, route to first available worker
|
|
|
|
msg, err := s.RecvMessage(0)
|
|
|
|
log.Printf("[DEBUG] Client message received: %s", msg)
|
|
|
|
if err == nil {
|
2021-05-30 15:37:49 -07:00
|
|
|
workerSock.SendMessage(workers[0], "", msg)
|
2021-05-21 23:03:50 +00:00
|
|
|
workers = workers[1:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-25 17:46:40 -08:00
|
|
|
}
|
2021-05-21 23:03:50 +00:00
|
|
|
}
|
2021-02-25 17:46:40 -08:00
|
|
|
|
2021-05-21 23:03:50 +00:00
|
|
|
func unwrap(msg []string) (head string, tail []string) {
|
|
|
|
head = msg[0]
|
|
|
|
if len(msg) > 1 && msg[1] == "" {
|
|
|
|
tail = msg[2:]
|
|
|
|
} else {
|
|
|
|
tail = msg[1:]
|
|
|
|
}
|
|
|
|
return
|
2021-02-25 17:46:40 -08:00
|
|
|
}
|
2021-10-14 10:08:39 -07:00
|
|
|
|
2021-10-15 11:27:38 -07:00
|
|
|
func answerClient(w http.ResponseWriter, r *http.Request) {
|
|
|
|
if r.URL.Path != "/" {
|
|
|
|
fmt.Fprintf(w, "%v\n", r)
|
|
|
|
// http.NotFound(w, r)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Common code for all requests can go here...
|
|
|
|
|
|
|
|
switch r.Method {
|
|
|
|
case http.MethodGet:
|
|
|
|
fmt.Fprintf(w, "%v\n", r)
|
|
|
|
// Handle the GET request...
|
|
|
|
|
|
|
|
case http.MethodPost:
|
|
|
|
// Handle the POST request...
|
|
|
|
|
|
|
|
case http.MethodOptions:
|
|
|
|
w.Header().Set("Allow", "GET, POST, OPTIONS")
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
|
|
|
|
default:
|
|
|
|
w.Header().Set("Allow", "GET, POST, OPTIONS")
|
|
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
|
|
}
|
|
|
|
}
|