~/Ali GÖREN

Golang ile Basit Bir Redis Client Yazmak — 1

Ali Goren · · 5 dk okuma


Herkese selamlar. 2 parçadan oluşacak bu yazıda Golang ile Redis için SET işlemi yapabilen bir client yazacağız.

  1. Yazı

Golang ile Basit Bir Redis Client Yazmak — 2

Yazının bu kısmında Redis Protokolü hakkında bilgi vermeye çalışacağım.

En baştan belirteyim, bu client go-redis ayarında bir profesyonelliğe sahip olmayacak. Sadece TCP bağlantısı ile Redis için işlemlerin nasıl çalışacağını göstereceğim. Ancak önce temel bilgileri alalım.

Redis Protokolü (RESP)

Bir Redis Client, Redis Server ile iletişime geçmek için RESP adı verilen protokolü kullanılır. Bunun açılımı ise Redis Serialization Protocol şeklindedir. Bağlantıyı TCP üzerinden sağlarız.

Oldukça basit bir protokol olduğu için şunları söylemek yanlış olmayacaktır;

  • Kolay implemente edilebilir
  • Hızlı parse edilebilir
  • Human readable yani insanlarca anlaşılırdır.

Ayrıca şunları bilmek de iyi olacaktır;

  • RESP, integer, string ve array gibi veri türlerini serialize edebilir.
  • RESP binary-safe’tir. Aktarılan veriler prefixed-length yani ön tanımlı uzunluğa sahip olduğundan tekrar tekrar işlenmelerine gerek yoktur

Yukarıdaki maddeler, Redis Cluster kullanıldığında değişebilir. Çünkü Redis Cluster farklı bir binary protokolü kullanmaktadır.

RESP Hakkında Detaylar

RESP simple strings, errors, integers, arrays ve bulk strings gibi türleri destekler.

Request-Response için akış ise şöyledir

  • Client’ın server’a isteği bir RESP Array olarak gönderir.
  • Server ise komuta göre (Örneğin SET) RESP türlerinden birisiyle yanıt verir.

RESP’te dönen veriyi anlamlandırabilmemiz için bazı detaylar sunulmuştur.

  • Simple Strings için cevabın ilk byte’ı “+” şeklindedir.
  • Errors için cevabın ilk byte’ı ise “-” şeklindedir.
  • Integers için cevabın ilk byte’ı “:” şeklindedir.
  • Bulk Strings için cevabın ilk byte’ı “$” şeklindedir.
  • Arrays için ise cevabın ilk byte’ı “*” şeklindedir.

Bu aşamadan sonra en azından gelen cevaplara göre bir client yazacak olursak, neleri nasıl parse edeceğimizi anlayabiliriz.

Unutmadan ekleyeyim. RESP’te protokolün her bir bölümü \r\n ile sonlandırılır.

Örneklerle RESP Türleri

Yazının bu kısmında örneklerle RESP türleri hakkında açıklamalara değineceğim.

RESP Simple Strings

Simple strings + karakteri ile başlar. Bildiğiniz basit bir artı. Bu string \r\n içeremez ancak \r\n ile sonlanır.

Çok yük oluşturmamak adına bu türde veriler non binary-safe olarak iletilirler. Eğer binary-safe iletim isteniyor ise Bulk Strings kullanılabilir.

Dönen değer 5 byte ve “+OK\r\n” şeklindedir.

RESP Errors

Resp, hata mesajları için özel bir veri türüne sahiptir. Hata mesajları sadece bir şeyler yolunda gitmediği anda üretilirler. Örneğin bir hatalı bir veri türü ya da olmayan bir redis komutu hata üretir.

Redis hataları “-” ile başlar. Yani eksi işareti diyebiliriz. Örneğin şöyle hatalar görebiliriz

  • -ERR unknown command “falanca”
  • -WRONGTYPE Operation against …..

İlk maddedeki ERR generic bir hata iken, ikinci maddede yer alan hata mesajı daha çok adreslenmiş bir hata türetir. Client yani library yazan kişi bu şekilde hatayı anlamlandırabilir. Exception fırlatılacak ise bu şekilde fırlatılabilir.

RESP Integers

Bu tür sadece \r\n ile sonlanan bir stringtir. Ancak başlangıcı “:” ile olur. Örnekler şöyle çeşitlendirilebilir;

  • :1\r\n
  • :256\r\n

Çoğu Redis komutu INCR, LLEN ya da LASTSAVE gibi RESP Integers değerleri dönebilir.

Burada dönen INCR özel bir anlama sahip değildir. Sadece incremental bir integer değerdir. LASTSAVE ise bir unix time’dır.

Dönen değerlerin signed 64-bit integer olduğu ise garanti edilmiştir.

Redis integer işlemleri için yine farklı komutlara sahiptir ancak bu yazıda bunu aktarmayacağım.

RESP Bulk Strings

Bulk Strings, 512 MB’a kadar olan single binary-safe string’i temsil etmek için kullanılır.

Bu veri türünü kullanırken ilk byte $ ile başlar. Yani dolar işareti. Daha sonra gidecek olan string’in length değerini içerir. Yani örnek olarak şöyle söylenebilir.

$LEN(STRING_VALUE)\r\nSTRING_VALUE\r\n

Yukarıdaki örnek yapı biraz karışık görünüyor. O zaman şöyle daha anlamlı bir şey yazalım.

$3\r\nAli\r\n

Evet bu daha anlamlı bir hale geldi. Eğer bir empty string kullanılacak ise şöyle tanımlama yapılabilir

$0\r\n\r\n

Diyelim ki null değerler ile çalışıyorsunuz. O zaman length değeri -1 olarak verilir. Yani şöyle;

$-1\r\n

Bu değer null veriyi temsil etmektedir. Redis içerisinde bu değere “Null Bulk String” de denmektedir.

Redis, server’dan bu tarz bir reply aldıysanız, empty string yerine kullandığını dile göre nil, null vb. gibi değerleri dönmenizi öneriyor.

RESP Arrays

Bir client, RESP Array’leri kullanarak server’a komutları yollayabilir. Aynı şekilde birden fazla değer içeren cevaplar için de RESP Arrays kullanılır.

RESP Arrays için spesifikasyonlar ise şöyle tanımlanabilir;

  • Oluşturulan formatın ilk byte’ı “*” yani yıldız ile başlar.
  • İkinci byte ise array’in kaç karakter içerdiği bilgisini tutar. Örnek; *2 için 2 boyutlu bir dizi denebilir.
  • Diğer işlemler, bir üstte tanımlanan RESP türleri şeklindedir.

Bir örneği şöyle verebiliriz;

Yukarıdaki komut başlangıçta anlam ifade etmiyor olabilir. Ancak temelde şunu dedik;

AUTH 12345

Bu, 2 boyutlu bir arraydir. İlk element 4 karakterden oluşan AUTH, diğer element ise 5 karakterden oluşan 12345 şeklindedir.

Her elementin kendi kurallarına göre eklenebildiğini bildiğimize göre belki de nested array sizin bir ödeviniz olabilir.

Okuduğunuz için teşekkür ederim. Bu, yazının ilk kısmıydı. Kodlamayı bu aşamaya sığdırmak isterdim. Ancak bu aşamaya kodlama sığdırmak demek okunamaz halde bir içerik sunmak demektir.

Okuduğunuz için teşekkür ederim.

Bu yazıyla ilgili sorularınızı bana aşağıdaki kaynaklardan ulaşarak sorabilirsiniz;

Kaynaklar

Wingie / Enuygun’un büyüyen ekibinin bir parçası olmak isterseniz buradan açık pozisyonlarımıza göz atabilirsiniz. Tech ekibimize başvurmak için ise CV’nizi kariyer@enuygun.com’a iletebilirsiniz.