Modern dağıtık sistemler sıkı sözleşme zorunluluğu gerektirir. REST'in yeri olsa da, JSON-RPC özellikle mikro hizmet mimarilerinde hizmetler arası iletişim için hafif ve verimli bir alternatif sunar. Ancak doğrulama olmadan ham RPC çağrıları tehlikeli olabilir. Bu yazı, veri bütünlüğü ve güvenliği sağlamak için sıkı JSON Şema doğrulamasını entegre ederek Go'da sağlam ve yüksek performanslı bir JSON-RPC sunucusunun nasıl oluşturulacağını inceler.
Neden JSON-RPC ve Şema Doğrulaması?
JSON-RPC, iç içe nesneler olmadan, JSON-RPC 1.0'a benzer ancak ondan farklı olan, stateless (durumsuz) ve hafif bir uzak yordam çağrısı protokolüdür. POST yöntemini kullanır ve istemcinin gövdede JSON kodlu parametreleri iletebilmesini sağlar. Temel avantajı sadeliği ve verimliliğidir. Ancak, istek gövdelerinde "her şeyin serbest" olması, hatalı verilerin çalışma zamanı paniklerine veya mantık hatalarına neden olma potansiyeli nedeniyle dezavantajdır.
JSON Şema doğrulamasının entegre edilmesi bir güvenlik bariyeri görevi görür. Gelen JSON yükünü işlemeden önce önceden tanımlanmış bir şemaya göre doğrulayarak şunları sağlarız:
- Gerekli alanların mevcut olması.
- Veri türlerinin beklentilerle eşleşmesi.
- Kısıtlamaların (örneğin dize uzunluğu veya sayı aralıkları) uygulanması.
Doğru Kütüphanelerin Seçimi
Bu uygulama için RPC çerçevesi olarak kararlı ve yaygın kullanılan gorilla/rpc kullanacağız. Doğrulama için go-playground/validator endüstri standardıdır. Ancak, sıkı JSON Şema uyumluluğu (RFC 7159) için github.com/xeipuuv/gojsonschema veya github.com/santhosh-tekuri/jsonschema gibi modern bir alternatif kullanacağız. Yüksek performans için, hızlı doğrulama için şemaları derleyen jsonschema paketinden yararlanacağız.
Sunucunun Uygulanması
Kullanıcı oluşturma isteği kabul eden bir sunucu oluşturalım. Kullanıcı verilerinin yapısını belirleyen bir JSON Şema tanımlayacağız.
Öncelikle gerekli bağımlılıklara sahip olduğunuzdan emin olun:
go get github.com/gorilla/rpc/v2
go get github.com/gorilla/rpc/v2/json2
go get github.com/santhosh-tekuri/jsonschema/v3
Şimdi, işleyiciyi tanımlayalım. Burada yüksek performansın anahtarı, JSON şemasını her istekte değil, başlangıçta bir kez derlemektir.
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/rpc/v2"
"github.com/gorilla/rpc/v2/json2"
"github.com/santhosh-tekuri/jsonschema/v3"
)
// UserRequest gelen JSON-RPC parametrelerini temsil eder
type UserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
// Doğrulama için JSON Şeması
const userSchema = `{
"type": "object",
"properties": {
"name": {"type": "string", "minLength": 1},
"email": {"type": "string", "format": "email"},
"age": {"type": "integer", "minimum": 0, "maximum": 120}
},
"required": ["name", "email", "age"],
"additionalProperties": false
}`
func main() {
// Performans için şemayı bir kez derle
compiler := jsonschema.NewCompiler()
if err := compiler.AddResource("schema.json", strings.NewReader(userSchema)); err != nil {
log.Fatal(err)
}
schema, err := compiler.Compile("schema.json")
if err != nil {
log.Fatal(err)
}
// RPC sunucusu oluştur
server := rpc.NewServer()
server.RegisterCodec(json2.NewCodec(), "application/json")
server.RegisterService(new(UserService), "")
// Servisi tanımla
type UserService struct{}
// Yöntem işleyicisi
userService := &UserService{}
// Doğrulama için özel işleyici sarmalayıcısı
http.Handle("/rpc", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var params json.RawMessage
// Gerçek senaryoda, parametreleri çıkarmak için JSON-RPC zarfını ayrıştır
// Bu, doğrulama mantığına odaklanan basitleştirilmiş bir örnektir
// Gövdede ham JSON yüküne sahip olduğumuzu varsayalım
if err := json.NewDecoder(r.Body).Decode(¶ms); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Şemaya göre doğrula
var loadedSchema jsonschema.Loader
loadedSchema = jsonschema.NewGoLoader(map[string]interface{}{}) // Basitleştirilmiş yükleme
// Not: Üretim ortamında, daha kolay Go yapı eşlemesi veya sıkı JSON doğrulaması için gojsonschema kullanın
// Burada şema zorunluluğu kavramını gösteriyoruz
service := new(UserService)
server.ServeHTTP(w, r)
}))
log.Println("Sunucu 8080 portunda dinliyor")
http.ListenAndServe(":8080", nil)
}
Performans İçin En İyi Uygulamalar
Aktarımı en üst düzeye çıkarmak için aşağıdaki optimizasyonları göz önünde bulundurun:
- Şemaları Bir Kez Derleyin: JSON şema derleme işlemi CPU yoğun olabilir. Şemaları her zaman sunucu başlatma sırasında derleyin.
- json.RawMessage Kullanın: RPC isteklerini ayrıştırırken, doğrulamadan önce gereksiz serileştirme/serileştirme işlemlerini önlemek için
json.RawMessagekullanın. - Bağlantı Havuzu: RPC istemcileriniz iç hizmetlerse, el sıkışma yükünü azaltmak için HTTP/2 bağlantı havuzu kullanın.
- Arabellek Havuzları: Aşırı yüksek aktarım kapasiteli sistemler için, GC baskısını azaltmak üzere istek/yanıt arabellekleri için
sync.Poolkullanmayı düşünün.
Sonuç
Go'da bir JSON-RPC sunucusu oluşturmak basittir, ancak JSON Şema doğrulaması eklemek onu bir oyun projesinden üretim seviyesinde bir hizmete yükseltir. Girdileri sınırda doğrulayarak, iş mantığınızı geçersiz durumlar korursunuz ve işleyicilerinizdeki bilişsel yükü azaltırsınız. Şema derlemesi ve bellek yönetimine dikkat ederek hem sıkılığı hem de yüksek performansı elde edebilirsiniz.