Maison  >  Article  >  développement back-end  >  Événements envoyés par le serveur Golang à chaque utilisateur

Événements envoyés par le serveur Golang à chaque utilisateur

PHPz
PHPzavant
2024-02-06 11:39:141064parcourir

Golang 服务器向每个用户发送的事件

Contenu de la question

J'utilise Go depuis un certain temps mais je n'ai jamais utilisé SSE auparavant. J'ai un problème, quelqu'un peut-il fournir un exemple fonctionnel d'un événement envoyé par le serveur qui n'est envoyé qu'à un utilisateur spécifique (connexion) ?

J'utilise gorilla - session pour l'authentification et je souhaite utiliser UserID pour séparer les connexions.

Ou dois-je utiliser un sondage de 5 secondes via Ajax ?

Merci beaucoup

Voici ce que j'ai trouvé et essayé :

  1. https://gist.github.com/ismasan/3fb75381cd2deb6bfa9c Il ne sera pas envoyé aux utilisateurs individuels et la fonction go ne s'arrêtera pas si la connexion est fermée

  2. https://github.com/striversity/gotr/blob/master/010-server-sent-event-part-2/main.go C'est exactement ce dont j'ai besoin, mais une fois la connexion supprimée. Alors maintenant, une fois que vous fermez et ouvrez le navigateur dans une fenêtre privée, cela ne fonctionne plus du tout. De plus, comme mentionné ci-dessus, la routine Go continue.


Bonne réponse


Créez un "broker" pour distribuer les messages aux utilisateurs connectés :

type broker struct {
    // users is a map where the key is the user id
    // and the value is a slice of channels to connections
    // for that user id
    users map[string][]chan []byte

    // actions is a channel of functions to call
    // in the broker's goroutine. the broker executes
    // everything in that single goroutine to avoid
    // data races.
    actions chan func()
}

// run executes in a goroutine. it simply gets and 
// calls functions.
func (b *broker) run() {
    for a := range b.actions {
        a()
    }
}

func newbroker() *broker {
    b := &broker{
        users:   make(map[string][]chan []byte),
        actions: make(chan func()),
    }
    go b.run()
    return b
}

// adduserchan adds a channel for user with given id.
func (b *broker) adduserchan(id string, ch chan []byte) {
    b.actions <- func() {
        b.users[id] = append(b.users[id], ch)
    }
}

// removeuserchan removes a channel for a user with the given id.
func (b *broker) removeuserchan(id string, ch chan []byte) {
    // the broker may be trying to send to 
    // ch, but nothing is receiving. pump ch
    // to prevent broker from getting stuck.
    go func() { for range ch {} }()

    b.actions <- func() {
        chs := b.users[id]
        i := 0
        for _, c := range chs {
            if c != ch {
                chs[i] = c
                i = i + 1
            }
        }
        if i == 0 {
            delete(b.users, id)
        } else {
            b.users[id] = chs[:i]
        }
        // close channel to break loop at beginning
        // of removeuserchan.
        // this must be done in broker goroutine
        // to ensure that broker does not send to
        // closed goroutine.
        close(ch)
    }
}

// sendtouser sends a message to all channels for the given user id.
func (b *broker) sendtouser(id string, data []byte) {
    b.actions <- func() {
        for _, ch := range b.users[id] {
            ch <- data
        }
    }
}

Déclarez les variables au niveau du package à l'aide de proxys :

var broker = newbroker()

Écrivez le point de terminaison sse à l'aide d'un proxy :

func sseEndpoint(w http.ResponseWriter, r *http.Request) {
    // I assume that user id is in query string for this example,
    // You should use your authentication code to get the id.
    id := r.FormValue("id")

    // Do the usual SSE setup.
    flusher := w.(http.Flusher)
    w.Header().Set("Content-Type", "text/event-stream")
    w.Header().Set("Cache-Control", "no-cache")
    w.Header().Set("Connection", "keep-alive")

    // Create channel to receive messages for this connection.  
    // Register that channel with the broker.
    // On return from the function, remove the channel
    // from the broker.
    ch := make(chan []byte)
    broker.addUserChan(id, ch)
    defer broker.removeUserChan(id, ch)
    for {
        select {
        case <-r.Context().Done():
            // User closed the connection. We are out of here.
            return
        case m := <-ch:
            // We got a message. Do the usual SSE stuff.
            fmt.Fprintf(w, "data: %s\n\n", m)
            flusher.Flush()
        }
    }
}

Ajoutez du code à votre application pour appeler broker.sendtouser.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer