package main import ( "embed" "fmt" "html/template" "log" "net/http" "time" "github.com/ebosas/microservices/config" "github.com/gorilla/websocket" "github.com/streadway/amqp" ) //go:embed template.html var files embed.FS //go:embed static var static embed.FS var ( conf = config.New() upgrader = websocket.Upgrader{} // use default options ) func main() { log.SetFlags(0) conn, err := amqp.Dial(conf.RabbitURL) if err != nil { log.Fatalf("Failed to connect to RabbitMQ: %s", err) } defer conn.Close() ch, err := conn.Channel() if err != nil { log.Fatalf("Failed to open a channel: %s", err) } defer ch.Close() err = ch.ExchangeDeclare( conf.Exchange, // name "topic", // type true, // durable false, // auto-deleted false, // internal false, // no-wait nil, // arguments ) if err != nil { log.Fatalf("Failed to declare an exchange: %s", err) } _, err = ch.QueueDeclare( conf.QueueBack, // name true, // durable false, // delete when unused false, // exclusive false, // no-wait nil, // arguments ) if err != nil { log.Fatalf("Failed to declare a backend queue: %s", err) } err = ch.QueueBind( conf.QueueBack, // queue name fmt.Sprintf("#.%s.#", conf.KeyBack), // routing key conf.Exchange, // exchange false, // no-wait nil, // arguments ) if err != nil { log.Fatalf("Failed to bind a backend queue: %s", err) } // files := http.FileServer(http.Dir("./static")) // http.Handle("/static/", http.StripPrefix("/static/", files)) // http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(static)))) http.Handle("/static/", http.FileServer(http.FS(static))) http.HandleFunc("/", handleHome) http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) { handleWs(w, r, conn) }) log.Fatal(http.ListenAndServe(conf.ServerAddr, nil)) } func handleHome(w http.ResponseWriter, r *http.Request) { // t, _ := template.ParseFiles("template.html") t, _ := template.ParseFS(files, "template.html") t.Execute(w, nil) } func handleWs(w http.ResponseWriter, r *http.Request, c *amqp.Connection) { ws, err := upgrader.Upgrade(w, r, nil) if err != nil { log.Printf("Failed to upgrade WebSocket: %s", err) return } defer ws.Close() done := make(chan bool) go wsWriter(ws, c, done) go wsReader(ws, c, done) <-done } // wsWriter reads messages from a Rabbit exchange // and writes to a websocket func wsWriter(ws *websocket.Conn, c *amqp.Connection, done chan bool) { defer func() { done <- true }() ch, err := c.Channel() if err != nil { log.Printf("Failed to open a channel: %s", err) return } defer ch.Close() q, err := ch.QueueDeclare( "", // name false, // durable true, // delete when unused true, // exclusive false, // no-wait nil, // arguments ) if err != nil { log.Printf("Failed to create a frontend queue: %s", err) return } err = ch.QueueBind( q.Name, // queue name fmt.Sprintf("#.%s.#", conf.KeyFront), // routing key conf.Exchange, // exchange false, // no-wait nil, // arguments ) if err != nil { log.Printf("Failed to bind a frontend queue: %s", err) return } msgs, err := ch.Consume( q.Name, // queue name "", // consumer false, // auto-ack false, // exclusive false, // no-local false, // no-wait nil, // args ) if err != nil { log.Printf("Failed to register a consumer: %s", err) return } for msg := range msgs { ws.SetWriteDeadline(time.Now().Add(10 * time.Second)) err = ws.WriteMessage(websocket.TextMessage, []byte(msg.Body)) if err != nil { log.Printf("Failed to write to WebSocket: %s", err) break } msg.Ack(false) } } // wsReader reads messages from a websocket and writes // to a Rabbit exchange func wsReader(ws *websocket.Conn, c *amqp.Connection, done chan bool) { defer func() { done <- true }() ch, err := c.Channel() if err != nil { log.Printf("Failed to open a channel: %s", err) return } defer ch.Close() for { _, message, err := ws.ReadMessage() if err != nil { log.Printf("Failed to read a message: %s", err) break } err = ch.Publish( conf.Exchange, // exchane name conf.KeyBack+"."+conf.KeyDB, // routing key false, // mandatory false, // immediate amqp.Publishing{ Timestamp: time.Now(), ContentType: "text/plain", Body: []byte(message), }, ) if err != nil { log.Printf("Failed to publish a message: %s", err) break } } }