Часть 3. Добавление поддержки Pub/Sub
Источник: https://github.com/kzeiter/build-redis-in-go
В этой части нашей серии клонов Redis мы добавим поддержку функции Pub/Sub (публикация/подписка). Pub/Sub позволяет клиентам подписываться на каналы и получать сообщения, публикуемые на этих каналах. Эта функция обычно используется в приложениях реального времени, таких как чаты и обновления в реальном времени.
Мы будем реализовывать две новые команды для Pub/Sub:
SUBSCRIBE channel PUBLISH channel message
Команда SUBSCRIBE
позволяет клиентам подписываться на канал, а команда PUBLISH
позволяет клиентам публиковать сообщения в канале. Когда сообщение публикуется в канале, все подписанные клиенты получат это сообщение.
Внедрение публикации/подписки
Чтобы реализовать Pub/Sub, нам нужно будет изменить нашу структуру store
, чтобы отслеживать подписчиков для каждого канала. Мы будем использовать карту, где ключи — это имена каналов, а значения — срезы client
структур. Структура client
будет представлять клиента, который подписан на канал, и будет содержать ссылку на объект net.Conn
клиента, который мы будем использовать для отправки сообщений клиенту.
type store struct { data map[string]string list map[string][]string sets map[string]map[string]bool subs map[string][]client } func (s *store) subscribe(channel string, conn net.Conn) string { client := client{conn: conn} s.subs[channel] = append(s.subs[channel], client) return "SUBSCRIBED" } func (s *store) publish(channel string, message string) { subs, ok := s.subs[channel] if ok { for _, subscriber := range subs { fmt.Fprintf(subscriber.conn, "+%s\n", message) } } }
Нам также нужно определить структуру client
:
type client struct { conn net.Conn }
Когда клиент подписывается на канал, мы создадим новую структуру client
и добавим ее в список подписчиков канала. Когда сообщение публикуется на канале, мы проходим по списку подписчиков этого канала и отправляем сообщение каждому из них.
Вот обновленный handleCommand
с новой функциональностью Pub/Sub:
func (s *store) handleCommand(command string, args []string) string { switch command { case "SET": // Using Join to save the rest of the received data return s.set(args[0], strings.Join(args[1:], " ")) case "GET": return s.get(args[0]) case "DEL": s.del(args[0]) return "DELETED" case "INCR": return fmt.Sprintf("%v", s.incr(args[0])) case "INCRBY": return fmt.Sprintf("%v", s.incrBy(args[0], args[1])) case "DECR": return fmt.Sprintf("%v", s.decr(args[0])) case "DECRBY": return fmt.Sprintf("%v", s.decrBy(args[0], args[1])) case "LPUSH": return fmt.Sprintf("%v", s.lPush(args[0], args[1])) case "RPUSH": return fmt.Sprintf("%v", s.rPush(args[0], args[1])) case "LPOP": return s.lPop(args[0]) case "RPOP": return s.rPop(args[0]) case "LLEN": return fmt.Sprintf("%v", s.lLen(args[0])) case "LINDEX": index, _ := strconv.Atoi(args[1]) return s.lIndex(args[0], index) case "SADD": return fmt.Sprintf("%v", s.sadd(args[0], args[1])) case "SREM": return fmt.Sprintf("%v", s.srem(args[0], args[1])) case "SMEMBERS": members := s.smembers(args[0]) result := "" for _, member := range members { result += fmt.Sprintf("%v ", member) } return strings.TrimSpace(result) case "SISMEMBER": return fmt.Sprintf("%v", s.sismember(args[0], args[1])) case "SUBSCRIBE": return s.subscribe(args[0], conn) case "PUBLISH": s.publish(args[0], strings.Join(args[1:], " ")) return "PUBLISHED" default: return "ERROR" } }
Мы добавили два новых случая в оператор switch в методе handleCommand
для обработки команд SUBSCRIBE
и PUBLISH
. Мы также добавили методы subscribe
и publish
для обработки подписки на каналы и публикации сообщений в каналах соответственно.
В методе subscribe
мы создаем новую структуру client
с объектом net.Conn
клиента и добавляем ее в список подписчиков для указанного канала.
В методе publish
мы сначала проверяем, есть ли подписчики на указанный канал. Если они есть, мы перебираем список подписчиков и отправляем сообщение каждому из них, используя клиентский объект net.Conn
.
Заключение
В этой части нашей серии клонов Redis мы добавили поддержку функций Pub/Sub. Мы реализовали команды SUBSCRIBE
и PUBLISH
и обновили нашу структуру store
, включив в нее карту subs
, в которой хранится список подписчиков для каждого канала.
Pub/Sub — это мощный шаблон обмена сообщениями, который позволяет издателям отправлять сообщения нескольким подписчикам, ничего о них не зная. Это делает его идеальным для создания приложений реального времени, таких как чаты или онлайн-игры.
В следующей части нашей серии клонов Redis мы добавим поддержку сохраняемости, реализуя простую дисковую систему хранения. Следите за обновлениями!
Источник:
https://github.com/kzeiter/build-redis-in-go
Часть 1. Создание базового хранилища ключей и значений:
https://khalidzeiter.medium.com/building-a-simple-redis-clone-in-golang-part-1 -7f816f2f61bd
Часть 2. Добавление поддержки чисел, списков и наборов:
https://khalidzeiter.medium.com/building-a-simple-redis-clone-in-golang-part -2-9130c0c3ef22
Часть 4. Добавление сохраняемости с помощью дискового хранилища:
https://khalidzeiter.medium.com/building-a-simple-redis-clone-in-golang-part-4-bf4ff349dad3
Заключение и следующие шаги:
https://khalidzeiter.medium.com/building-a-simple-redis-clone-in-golang-conclusion-and-next-steps-128b0503a55b