3 votes

Comment poster correctement un message sur un simple serveur de chat Go en utilisant l'API REST

Je suis actuellement en train de construire un serveur de chat simple qui prend en charge l'envoi de messages via une API REST.

 exemple:
 ========

 ```
 curl -X POST -H "Content-Type: application/json" --data '{"user":"alex", "text":"this is a message"}' http://localhost:8081/message

 {
   "ok": true
 }

En ce moment, je stocke simplement les messages dans un tableau de messages. Je suis assez sûr que c'est une manière inefficace. Y a-t-il donc un moyen simple et meilleur d'obtenir et de stocker les messages en utilisant des goroutines et des canaux qui rendra cela thread-safe.

Voici ce que j'ai actuellement:

type Message struct {
    Text string
    User string
    Timestamp time.Time
}

var Messages = []Message{}

func messagePost(c http.ResponseWriter, req *http.Request){
    decoder := json.NewDecoder(req.Body)
    var m Message
    err := decoder.Decode(&m)
    if err != nil {
        panic(err)
    }
    if m.Timestamp == (time.Time{}) {
        m.Timestamp = time.Now()
    }
    addUser(m.User)
    Messages = append(Messages, m)
}

Merci!

2voto

teejays Points 51

Il pourrait être rendu thread-safe en utilisant un mutex, comme @ThunderCat l'a suggéré, mais je pense que cela n'ajoute pas de concurrence. Si deux ou plusieurs requêtes sont faites simultanément, l'une devra attendre que l'autre se termine d'abord, ralentissant le serveur.

Ajout de concurrence: Vous pouvez le rendre plus rapide et gérer davantage de demandes concurrentes en utilisant une file d'attente (qui est un canal Go) et un worker qui écoute ce canal - ce sera une implémentation simple. Chaque fois qu'un message arrive via une requête Post, vous l'ajoutez à la file d'attente (ceci est instantané et la réponse HTTP peut être envoyée immédiatement). Dans une autre goroutine, lorsque vous détectez qu'un message a été ajouté à la file d'attente, vous le retirez et l'ajoutez à votre slice Messages. Pendant que vous ajoutez à Messages, les requêtes HTTP n'ont pas à attendre.

Note : Vous pouvez améliorer en ayant plusieurs goroutines écoutant la file d'attente, mais nous pouvons laisser cela pour plus tard.

Voici à quoi ressemblera plus ou moins le code :

type Message struct {
    Text string
    User string
    Timestamp time.Time
}

var Messages = []Message{}

// messageQueue est la file d'attente qui contient les nouveaux messages jusqu'à ce qu'ils soient traités
var messageQueue chan Message

func init() { // besoin de la fonction init pour initialiser le canal, et les écouteurs
    // initialiser la file d'attente, en choisissant la taille du tampon à 8 (nombre de messages que le canal peut contenir à la fois)
    messageQueue = make(chan Message, 8) 

    // démarrer une goroutine qui écoute la file d'attente/canal pour les nouveaux messages
    go listenForMessages()
}

func listenForMessages() {
    // chaque fois que nous détectons un message dans la file d'attente, l'ajouter à Messages
    for m := range messageQueue {
        Messages = append(Messages, m)
    }
}

func messagePost(c http.ResponseWriter, req *http.Request){
    decoder := json.NewDecoder(req.Body)
    var m Message
    err := decoder.Decode(&m)
    if err != nil {
        panic(err)
    }
    if m.Timestamp == (time.Time{}) {
        m.Timestamp = time.Now()
    }
    addUser(m.User)

    // ajouter le message au canal, il n'attendra que si le canal est plein
    messageQueue <- m 
}

Stockage des messages: Comme d'autres utilisateurs l'ont suggéré, stocker les messages en mémoire peut ne pas être le bon choix car les messages ne persisteront pas si l'application est redémarrée. Si vous travaillez sur un petit projet de type concept de preuve et que vous ne voulez pas configurer la DB, vous pourriez enregistrer la variable Messages en tant que fichier plat sur le serveur et ensuite lire à partir de celui-ci à chaque démarrage de l'application (*Note : cela ne devrait pas être fait sur un système de production, bien sûr, pour cela vous devriez mettre en place une base de données). Mais oui, la base de données devrait être la solution à adopter.

1voto

ThunderCat Points 45890

Utilisez un mutex pour rendre le programme thread-safe.

var Messages = []Message{}
var messageMu sync.Mutex

...

messageMu.Lock()
Messages = append(Messages, m)
messageMu.Unlock()

Il n'est pas nécessaire d'utiliser des canaux et des goroutines pour rendre le programme thread-safe.

Une base de données est probablement un meilleur choix pour stocker les messages que la tranche en mémoire utilisée dans la question. Poser une question sur l'utilisation d'une base de données pour implémenter un programme de chat est trop large pour Stack Overflow.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X