Bagian 3: Menambahkan Dukungan Pub/Sub
Sumber:
https://github.com/kzeiter/build-redis-in-go

Di bagian seri klon Redis ini, kami akan menambahkan dukungan untuk fungsionalitas Pub/Sub (Terbitkan/Berlangganan). Pub/Sub memungkinkan klien untuk berlangganan saluran dan menerima pesan yang dipublikasikan ke saluran tersebut. Fitur ini biasa digunakan pada aplikasi real-time, seperti aplikasi chatting dan update real-time.

Kami akan menerapkan dua perintah baru untuk Pub/Sub:

SUBSCRIBE channel
PUBLISH   channel message

Perintah SUBSCRIBE memungkinkan klien untuk berlangganan saluran, sedangkan perintah PUBLISH memungkinkan klien untuk mempublikasikan pesan ke saluran. Ketika sebuah pesan dipublikasikan ke suatu saluran, semua klien yang berlangganan akan menerima pesan tersebut.

Menerapkan Pub/Sub

Untuk mengimplementasikan Pub/Sub, kita perlu memodifikasi store struct kita untuk melacak pelanggan setiap saluran. Kita akan menggunakan peta yang kuncinya adalah nama saluran dan nilainya adalah potongan client struct. Struct client akan mewakili klien yang berlangganan suatu saluran dan akan berisi referensi ke objek net.Conn klien, yang akan kita gunakan untuk mengirim pesan ke klien.

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)
      }
   }
}

Kita juga perlu mendefinisikan client struct:

type client struct {
    conn net.Conn
}

Saat klien berlangganan suatu saluran, kami akan membuat struct client baru dan menambahkannya ke daftar pelanggan saluran tersebut. Saat pesan dipublikasikan ke saluran, kami akan menelusuri daftar pelanggan saluran tersebut dan mengirimkan pesan ke masing-masing saluran.

Berikut adalah handleCommand yang diperbarui dengan fungsi Pub/Sub baru:

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"
    }
}

Kami telah menambahkan dua kasus baru ke pernyataan switch dalam metode handleCommand untuk menangani perintah SUBSCRIBE dan PUBLISH. Kami juga telah menambahkan metode subscribe dan publish untuk masing-masing menangani langganan saluran dan penerbitan pesan ke saluran.

Dalam metode subscribe, kita membuat struct client baru dengan objek net.Conn klien dan menambahkannya ke daftar pelanggan untuk saluran yang ditentukan.

Dalam metode publish, pertama-tama kita periksa apakah ada pelanggan untuk saluran yang ditentukan. Jika ada, kami mengulangi daftar pelanggan dan mengirim pesan ke masing-masing pelanggan menggunakan objek net.Conn klien.

Kesimpulan

Di bagian seri kloning Redis ini, kami telah menambahkan dukungan untuk fungsi Pub/Sub. Kami telah menerapkan perintah SUBSCRIBE dan PUBLISH dan memperbarui struct store kami untuk menyertakan peta subs, yang menyimpan daftar pelanggan untuk setiap saluran.

Pub/Sub adalah pola perpesanan canggih yang memungkinkan penerbit mengirim pesan ke banyak pelanggan tanpa mengetahui apa pun tentangnya. Hal ini menjadikannya ideal untuk membangun aplikasi real-time, seperti ruang obrolan atau game online.

Di bagian selanjutnya dari seri kloning Redis, kami akan menambahkan dukungan untuk persistensi dengan mengimplementasikan sistem penyimpanan berbasis disk yang sederhana. Pantau terus!

Sumber:
"https://github.com/kzeiter/build-redis-in-go"

Bagian 1: Membangun Penyimpanan Nilai-Kunci Dasar:
https://khalidzeiter.medium.com/building-a-simple-redis-clone-in-golang-part-1 -7f816f2f61bd

Bagian 2: Menambahkan Dukungan untuk Angka, Daftar, dan Kumpulan:
https://khalidzeiter.medium.com/building-a-simple-redis-clone-in-golang-part -2-9130c0c3ef22

Bagian 4: Menambahkan Persistensi dengan Penyimpanan Disk:
https://khalidzeiter.medium.com/building-a-simple-redis-clone-in-golang-part-4-bf4ff349dad3

Kesimpulan dan Langkah Selanjutnya:
https://khalidzeiter.medium.com/building-a-simple-redis-clone-in-golang-conclusion-and-next-steps-128b0503a55b