Protobuf tasarım desenleri


19

Java tabanlı bir hizmet için Google Protokol Tamponlarını değerlendiriyorum (ancak dil agnostik kalıpları bekliyorum). İki sorum var:

Birincisi geniş bir genel soru:

İnsanların hangi kalıpları kullandığını görüyoruz? Sözü edilen kalıplar sınıf organizasyonu (örn. .Proto dosyası, paketleme ve dağıtım başına mesajlar) ve mesaj tanımı (örneğin tekrarlanan alanlar veya tekrarlanan kapsüllenmiş alanlar *) vb.

Google Protobuf Yardım sayfalarında ve genel bloglarda bu türden çok az bilgi bulunurken, XML gibi yerleşik protokoller için bir ton bilgi vardır.

Ayrıca aşağıdaki iki farklı örnekle ilgili özel sorularım var:

  1. .Proto dosyalarındaki mesajları temsil edin, ayrı bir kavanoz olarak paketleyin ve temel olarak tahmin ettiğim varsayılan yaklaşım olan hizmetin tüketicilerini hedefleyin.

  2. Aynı şeyi yapın, ancak en azından bu iki yöntemi destekleyen bir sözleşme uygulayan her mesajın etrafına el yapımı sargılar (alt sınıflar değil!) Ekleyin (T sarıcı sınıfıdır, V mesaj sınıfıdır (jenerikler kullanarak kısalık için basitleştirilmiş sözdizimi) :

    public V toProtobufMessage() {
        V.Builder builder = V.newBuilder();
        for (Item item : getItemList()) {
            builder.addItem(item);
        }
        return builder.setAmountPayable(getAmountPayable()).
                       setShippingAddress(getShippingAddress()).
                       build();
    }
    
    public static T fromProtobufMessage(V message_) { 
        return new T(message_.getShippingAddress(), 
                     message_.getItemList(),
                     message_.getAmountPayable());
    }
    

Ben getirdiği karmaşık uzak gizleyebilirsiniz ben (2) ile bakın bir avantajı olduğunu V.newBuilder().addField().build()ve bu şekilde bazı anlamlı yöntemleri eklemek isOpenForTrade()veya isAddressInFreeDeliveryZone()vb benim sarma. (2) ile gördüğüm ikinci avantaj, müşterilerimin değişmez nesnelerle (ambalaj sınıfında uygulayabileceğim bir şey) uğraşmalarıdır.

(2) ile gördüğüm bir dezavantajı, kodu çoğaltmak ve sarma sınıfları .proto dosyaları ile eşitlemek zorunda olmasıdır.

İki yaklaşımdan herhangi biri hakkında daha iyi teknikler veya eleştiriler var mı?


* Tekrarlanan bir alanı kapsülleyerek, bunun gibi mesajlar kastediyorum:

message ItemList {
    repeated item = 1;
}

message CustomerInvoice {
    required ShippingAddress address = 1;
    required ItemList = 2;
    required double amountPayable = 3;
}

bunun gibi mesajlar yerine:

message CustomerInvoice {
    required ShippingAddress address = 1;
    repeated Item item = 2;
    required double amountPayable = 3;
}

İkincisini seviyorum ama buna karşı tartışmaları duymaktan mutluluk duyuyorum.


Yeni etiketler oluşturmak için 12 noktaya daha ihtiyacım var ve bence protobuf bu yazı için bir etiket olmalı.
Apoorv Khurasia

Yanıtlar:


13

Çalıştığım yerde protobuf kullanımını gizleme kararı alındı. Biz dağıtmak yok .protouygulamalar arasında dosya, daha ziyade, bir protobuf arayüz içeren herhangi bir uygulama kendisine konuşabilirsiniz bir istemci kitaplığı ihraç ediyor.

Bu protobuf açığa çıkaran uygulamalardan yalnızca biri üzerinde çalıştım, ancak her protobuf mesajı, alandaki bazı kavramlara karşılık geliyor. Her konsept için normal bir Java arayüzü vardır. Daha sonra, bir uygulamanın bir örneğini alıp uygun bir ileti nesnesi oluşturabilen ve bir ileti nesnesi alabilen ve arabirimin bir uygulamasının bir örneğini oluşturabilen bir dönüştürücü sınıfı vardır (olduğu gibi, genellikle tanımlanmış basit bir anonim veya yerel sınıf dönüştürücünün içinde). Protobuf tarafından üretilen mesaj sınıfları ve dönüştürücüler birlikte, hem uygulama hem de istemci kütüphanesi tarafından kullanılan bir kütüphane oluşturur; istemci kitaplığı, bağlantıları ayarlamak, mesaj göndermek ve almak için az miktarda kod ekler.

İstemci uygulamaları daha sonra istemci kitaplığını içe aktarır ve göndermek istedikleri arabirimlerin uygulamalarını sağlar. Gerçekten, her iki taraf da ikinci şeyi yapar.

Açıklığa kavuşturmak için, bu, istemcinin bir parti daveti gönderdiği ve sunucunun bir RSVP ile yanıt verdiği bir istek-yanıt döngünüz varsa, ilgili şeylerin:

  • Yazılı PartyInvitation mesajı, .protodosyanın
  • PartyInvitationMessage tarafından oluşturulan sınıf protoc
  • PartyInvitation arayüz, paylaşılan kütüphanede tanımlandı
  • ActualPartyInvitation, PartyInvitationistemci uygulaması tarafından tanımlanan somut bir uygulama (aslında denmez!)
  • StubPartyInvitation, PartyInvitationpaylaşılan kütüphane tarafından tanımlanan basit bir uygulama
  • PartyInvitationConverterHangi bir dönüştürebilirsiniz PartyInvitationbir etmek PartyInvitationMessageve PartyInvitationMessagebir karşıStubPartyInvitation
  • RSVP mesajı, .protodosyada yazılı
  • RSVPMessage tarafından oluşturulan sınıf protoc
  • RSVP arayüz, paylaşılan kütüphanede tanımlandı
  • ActualRSVP, RSVPsunucu uygulaması tarafından tanımlanan somut bir uygulama (gerçekte denmez!)
  • StubRSVP, RSVPpaylaşılan kütüphane tarafından tanımlanan basit bir uygulama
  • RSVPConverter, bir RSVPve bir RSVPMessageve bir RSVPMessagebirStubRSVP

Gerçek ve saplama uygulamalarının ayrı olmasının nedeni, gerçek uygulamaların genellikle JPA eşlemeli varlık sınıfları olmasıdır; sunucu bunları oluşturur ve devam ettirir veya veritabanından sorgular, sonra iletilecek protobuf katmanına verir. Bağlantının alıcı tarafında bu sınıfların örneklerini oluşturmanın uygun olmadığı düşünülmemiştir, çünkü bunlar bir kalıcılık bağlamına bağlı olmayacaklardır. Ayrıca, varlıklar genellikle tel üzerinden iletilenden daha fazla veri içerir, bu nedenle alıcı tarafta sağlam nesneler oluşturmak bile mümkün olmaz. Bunun doğru bir hareket olduğuna tamamen ikna olmadım, çünkü bu bize mesaj başına başka bir sınıftan daha fazla sınıf bıraktı.

Gerçekten de, protobuf kullanmanın iyi bir fikir olduğuna tamamen ikna olmadım; düz eski RMI ve serileştirme ile sıkışırsak, neredeyse çok sayıda nesne oluşturmak zorunda kalmazdık. Birçok durumda, varlık sınıflarımızı serileştirilebilir olarak işaretleyebilirdik ve onunla devam edebiliriz.

Şimdi, tüm bunları söyledikten sonra, Google'da, modüller arasındaki iletişim için protobuf'u yoğun kullanan bir kod tabanında çalışan bir arkadaşım var. Tamamen farklı bir yaklaşım benimserler: oluşturulan mesaj sınıflarını hiç sarmazlar ve heyecanla kodlarına derin (ish) iletirler. Bu iyi bir şey olarak görülür, çünkü arayüzleri esnek tutmanın basit bir yoludur. Mesajlar geliştiğinde senkronize kalacak bir iskele kodu yoktur ve oluşturulan sınıflar hasFoo(), zaman içinde eklenen alanların varlığını veya yokluğunu tespit etmek için kod almak için gerekli tüm yöntemleri sağlar. Bununla birlikte, Google'da çalışan kişilerin (a) oldukça akıllı ve (b) biraz deli olma eğilimi olduğunu unutmayın.


Bir noktada, standart seri hale getirme için az ya da çok bırakma yerine JBoss Serialization kullanarak baktım . Çok daha hızlıydı. Protobuf kadar hızlı olmasa da.
Tom Anderson

Jackson2 kullanarak JSON serileştirmesi de oldukça hızlı. GBP hakkında nefret ettiğim şey, ana arayüz sınıflarının gereksiz kopyalanması.
Apoorv Khurasia

0

Andersons'un cevabına eklemek için, mesajları akıllıca bir diğerine yerleştirmek ve aşırıya kaçmak için ince bir çizgi var. Sorun, her mesajın sahne arkasında ve veriler için her türlü erişimci ve işleyicilerin arkasında yeni bir sınıf oluşturmasıdır. Ancak, verileri kopyalamanız veya bir değeri değiştirmeniz veya mesajları karşılaştırmanız gerekiyorsa bunun bir maliyeti vardır. Çok fazla veriye sahipseniz veya zamana bağlıysanız, bu işlemler çok yavaş ve acı verici olabilir.


2
bu daha teğetsel bir yorum gibi okunur, bkz. Nasıl Cevap
gnat

1
Peki değil: hiçbir etki alanı yok sınıflar sonunda tüm bir ifade sorunu (oh C + 'de tüm şeyler geliştiriyorum ama bu bir sorun olmamalı)
Marko Bencik
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.