Предоставляя некоторый контекст, я реализую разветвление (потоковую передачу), используя gRPC, на сервере каждый раз, когда клиент подключается, я сохраняю поток в карту, а когда клиент отключается, я просто удаляю его из карты, чтобыЯ мог бы обновлять карту только с помощью «живых» клиентов / соединений.
Таким образом, исходная карта выглядит примерно так:
key value
client1 stream
client2 stream
client3 stream
Проблема с этой реализацией состоит в том, что, если клиент подключаетсянесколько раз только последний клиент (использующий тот же key id
) получит сообщение «широковещания», так как карта перезапишет существующий поток самым последним из подключающегося клиента, поэтому мне нужно либо использовать уникальный ключ, либочтобы найти способ сопоставления, связать клиентов и соединения.
Мне было легко просто переключить карту и использовать в качестве ключа идентификатор соединения, что-то вроде этого
conn_id client_id
conn1 client1
conn2 client2 \
conn3 client2 > 3 connections for client ID 2
conn4 client2 /
Это работает ипомогает отправить сообщение всем подключенным клиентам, но усложняет ситуацию, если я хочу отправить сообщение определенномуclient, так как мне нужно было бы ранжировать / пройти всю карту, чтобы сопоставить значение с желаемым клиентом.
В попытке исправить это я переключил карту обратно, используя client_id, но вместо того, чтобы использовать поток какзначение, я использую список срезов
key value
client1 [stream, stream, stream]
client2 [stream]
client3 [stream, stream]
Проблема не устранена, поскольку для отправки сообщения всем клиентам по-прежнему необходимо выполнить обход / диапазон для получения потоков для каждого клиента и отправкисообщение каждому.
Поэтому, не используя базу данных, задаюсь вопросом, какую структуру данных можно использовать, следуя рекомендациям относительно big O нотации ?
В основном хотелось бы найтичто-то похожее на базу данных внешние ключи , возможно, с двумя картами, одна из которых сохраняет все подключения, а другая - только состояние или количество подключений на клиента, например:
key value client_id # streams
conn1 client1 client1 1
conn2 client2 client2 3
conn3 client2
conn4 client2
Но как«атомарно» удалить клиента из карты clients
, когда больше нет соединений с этим клиентом в connections
map.
Это попытка, но интересно, можно ли ее улучшить:
https://play.golang.org/p/VEoLsWh6dbJ
package main
import (
"fmt"
"sync"
)
func main() {
clients := &sync.Map{}
for i := 0; i < 100; i++ {
clientID := fmt.Sprintf("client-%d", i%5)
connID := fmt.Sprintf("conn-%d", i)
client, ok := clients.LoadOrStore(clientID, connID)
if ok {
switch c := client.(type) {
case string:
client = []string{c, connID}
case []string:
client = append(client.([]string), connID)
}
clients.Store(clientID, client)
}
}
list := func(k, v interface{}) bool {
fmt.Printf("k: %+v v: %v\n", k, v)
return true
}
clients.Range(list)
// Get all connections
broadcast := func(k, v interface{}) bool {
fmt.Printf("To all connections from= %q\n", k)
for _, v := range v.([]string) {
fmt.Printf("msg to conn = %s\n", v)
}
return true
}
clients.Range(broadcast)
}
Здесь я пытаюсь использовать только карты (легко обрабатывать удаления):медленнее, но выделяет меньше байтов:
https://play.golang.org/p/hz5uRX-VAvH
package main
import (
"fmt"
"sync"
)
func main() {
clients := &sync.Map{}
for i := 0; i < 100; i++ {
clientID := fmt.Sprintf("client-%d", i%5)
connID := fmt.Sprintf("conn-%d", i)
conns, ok := clients.Load(clientID)
if ok {
conns.(*sync.Map).Store(connID, i)
} else {
conns := &sync.Map{}
conns.Store(connID, i)
clients.Store(clientID, conns)
}
}
list := func(k, v interface{}) bool {
fmt.Printf("k: %v\n", k)
listValue := func(j, l interface{}) bool {
fmt.Printf(" j = %+v\n", j)
return true
}
v.(*sync.Map).Range(listValue)
return true
}
clients.Range(list)
// Get all connections
broadcast := func(k, v interface{}) bool {
fmt.Printf("To all connections from= %q\n", k)
listValue := func(j, l interface{}) bool {
fmt.Printf(" msg to conn = %s -- %v\n", j, l)
return true
}
v.(*sync.Map).Range(listValue)
return true
}
clients.Range(broadcast)
}