Aracılı hizmet tasarlamaya yönelik en iyi yöntemler

StreamJsonRpc için RPC arabirimleri için belgelenen genel yönergeleri ve kısıtlamaları izleyin.

Ayrıca, aracılı hizmetler için aşağıdaki yönergeler geçerlidir.

Yöntem imzaları

Tüm yöntemler, son parametreleri olarak bir CancellationToken parametre almalıdır. Bu parametre genellikle isteğe bağlı bir parametre olmamalıdır , bu nedenle çağıranların bağımsız değişkeni yanlışlıkla atlama olasılığı daha düşüktür. Yönteminin uygulanmasının önemsiz olması beklense bile, sağlama CancellationToken , istemcinin sunucuya iletilmeden önce kendi isteğini iptal etmesine olanak tanır. Ayrıca, sunucunun uygulamasının daha sonra bir seçenek olarak iptal eklemek için yöntemini güncelleştirmek zorunda kalmadan daha pahalı bir şeye dönüşmesini sağlar.

RPC arabiriminizde aynı yöntemin birden çok aşırı yüklemesini önlemeyi göz önünde bulundurun. Aşırı yükleme çözümlemesi genellikle çalışır (ve bunu doğrulamak için testler yazılmalıdır), her aşırı yüklemenin parametre türlerine göre bağımsız değişkenleri seri durumdan çıkarmaya çalışır ve bu da aşırı yüklemeyi seçmenin normal bir parçası olarak ilk şans özel durumlarının atılmasıyla sonuçlanır. Başarı yollarında ilk şans özel durumlarının sayısını en aza indirmek istediğimizden, yalnızca belirli bir ada sahip tek bir yönteme sahip olmak tercih edilir.

Parametre ve dönüş türleri

RPC üzerinden değiştirilen tüm bağımsız değişkenlerin ve dönüş değerlerinin yalnızca veri olduğunu unutmayın. Hepsi seri hale getirilir ve kablo üzerinden gönderilir. Bu veri türlerinde tanımladığınız tüm yöntemler verilerin yalnızca bu yerel kopyası üzerinde çalışır ve bunu üreten RPC hizmetine geri iletişim kurmanın hiçbir yolu yoktur. Bu serileştirme davranışının tek istisnası, StreamJsonRpc'nin özel desteği olan egzotik türlerdir.

Daha az ayırmaya neden olduğundan ValueTask<T> yöntemlerin dönüş türü olarak over Task<T> kullanmayı ValueTask<T> göz önünde bulundurun. Genel olmayan çeşitliliği (örneğin ve Task ValueTask) kullanırken daha az önemlidir, ancak ValueTask yine de tercih edilebilir. Bu API'de belgelendiği gibi üzerindeki ValueTask<T> kullanım kısıtlamalarına dikkat edin. Bu blog gönderisi ve video , hangi türün kullanılacağına karar verirken de yararlı olabilir.

Özel veri türleri

Tüm veri türlerini sabit olacak şekilde tanımlamayı göz önünde bulundurun; bu da verilerin kopyalamadan bir işlemde daha güvenli bir şekilde paylaşılmasını sağlar ve başka bir RPC yerleştirmeden bir sorguya yanıt olarak aldıkları verileri değiştiremeyecekleri fikrini tüketicilere pekiştirmeye yardımcı olur.

Newtonsoft.Json kullanırken ServiceJsonRpcDescriptor.Formatters.UTF8kutulama maliyetinden (potansiyel olarak tekrarlanan) kaçınan veri türlerinizi kullanmak yerine struct olarak class tanımlayın. Kullanılırken ServiceJsonRpcDescriptor.Formatters.MessagePack kutulama gerçekleşmez, bu nedenle bu biçimlendiriciye bağlıysanız yapılar uygun bir seçenek olabilir.

İstemcinin IEquatable<T> başka bir zamanda alınan verilere eşit olup olmadığına bağlı olarak alınan verileri verimli bir şekilde depolamasına, karşılaştırmasına ve yeniden kullanmasına olanak tanıyan veri türlerinizde ve yöntemleri uygulamayı ve geçersiz kılmayı GetHashCode() Equals(Object) göz önünde bulundurun.

JSON kullanarak polimorfik türleri serileştirmeyi desteklemek için kullanın DiscriminatedTypeJsonConverter<TBase> .

Koleksiyonlar

Daha verimli seri durumdan çıkarma olanağı sağlayan somut türler (örneğin veya IReadOnlyList<T>T[]) yerine RPC yöntemi imzalarında (örneğin, List<T> ) salt okunur koleksiyon arabirimleri kullanın.

IEnumerable<T> ifadesinden kaçının. Özelliğin Count olmaması verimsiz koda yol açar ve rpc senaryosunda geçerli olmayan olası geç veri oluşturma anlamına gelir. Bunun yerine sıralanmamış koleksiyonlar veya IReadOnlyList<T> sıralı koleksiyonlar için kullanınIReadOnlyCollection<T>.

göz önünde bulundurun IAsyncEnumerable<T>. Diğer herhangi bir koleksiyon türü veya IEnumerable<T> tüm koleksiyonun tek bir iletide gönderilmesine neden olur. kullanarak IAsyncEnumerable<T> küçük bir ilk iletiye izin verir ve alıcıya koleksiyondan istedikleri kadar öğe alma ve zaman uyumsuz olarak numaralandırma araçları sağlar. Bu yeni desen hakkında daha fazla bilgi edinin.

Gözlemci deseni

Arabiriminizde gözlemci tasarım desenini kullanmayı göz önünde bulundurun. Bu, istemcinin sonraki bölümde açıklanan geleneksel olay modeli için geçerli olan birçok tuzak olmadan verilere abone olması için basit bir yoldur.

Gözlemci düzeni şu kadar basit olabilir:

Task<IDisposable> SubscribeAsync(IObserver<YourDataType> observer);

IDisposable Yukarıda kullanılan ve IObserver<T> türleri StreamJsonRpc'deki egzotik türlerden ikisidir, bu nedenle yalnızca veri olarak seri hale getirilmeleri yerine özel olarak sıralanmış davranışlar elde ederler.

Olaylar

Olaylar çeşitli nedenlerle RPC üzerinde sorunlu olabilir ve bunun yerine yukarıda açıklanan gözlemci desenini öneririz.

Hizmetin, hizmet ve istemci ayrı işlemlerde olduğunda istemcinin kaç olay işleyicisi eklediğinin görünürlüğünün olmadığını unutmayın. JsonRpc her zaman olayı istemciye yaymakla sorumlu olan tam olarak bir işleyici ekler. İstemcinin uzak tarafına eklenmiş sıfır veya daha fazla işleyicisi olabilir.

Çoğu RPC istemcisinde, ilk bağlandıklarında kablolu olay işleyicileri olmaz. İstemci, ilgi alanını ve olayları almaya hazır olduğunu belirtmek için arabiriminizde bir "Subscribe*" yöntemi çağırana kadar ilk olayı başlatmaktan kaçının.

Olayınız bir değişim durumunu gösteriyorsa (örneğin, koleksiyona yeni bir öğe eklendi), geçmiş tüm olayları oluşturmayı veya bir istemci olay işleme kodundan başka bir şey olmadan 'eşitlemeye' yardımcı olmak için abone olduğunda olay bağımsız değişkeninde yeni gibi tüm geçerli verileri açıklamayı düşünün.

İstemci, bu bildirimleri iletmek için gereken ağ trafiğini ve CPU'ları azaltmak için verilerin veya bildirimlerin bir alt kümesiyle ilgilenmek isteyebilirse yukarıda belirtilen "Subscribe*" yönteminde ek bağımsız değişkenler kabul etmeyi göz önünde bulundurun.

Değişiklik bildirimleri almak için bir olayı da kullanıma sunuyorsanız veya istemcilerin olayla birlikte kullanmalarını etkin bir şekilde caydırıyorsanız geçerli değeri döndüren bir yöntem sunmamayı göz önünde bulundurun. Veriler için bir olaya abone olan ve geçerli değeri almak için bir yöntem çağıran istemci, bu değerdeki değişikliklere karşı yarışıyor ve bir değişiklik olayı eksik ya da bir iş parçacığındaki bir değişiklik olayını başka bir iş parçacığında elde edilen değerle nasıl uzlaştıracaklarını bilmiyor. Bu sorun yalnızca RPC üzerinden değil tüm arabirimler için geneldir.

Adlandırma kuralları

  • Service RPC arabirimlerinde son eki ve basit I bir ön ek kullanın.
  • SDK'nızdaki sınıflar için son eki kullanmayın Service . Kitaplığınız veya RPC sarmalayıcınız, "hizmet" teriminden kaçınarak tam olarak ne yaptığını açıklayan bir ad kullanmalıdır.
  • Arabirim veya üye adlarında "uzak" teriminden kaçının. Aracılı hizmetlerin ideal olarak yerel senaryolarda uzak senaryolar kadar geçerli olduğunu unutmayın.

Sürüm uyumluluğuyla ilgili endişeler

Diğer uzantılara açık olan veya Canlı Paylaşım üzerinden kullanıma sunulan herhangi bir aracılı hizmetin ileriye ve geriye doğru uyumlu olmasını istiyoruz. Bu, bir istemcinin hizmetten daha eski veya daha yeni olabileceğini ve işlevselliğin kabaca iki geçerli sürümden daha küçük olan hizmetle eşit olması gerektiğini varsaymalıyız.

İlk olarak, hataya neden olan değişiklik terminolojisini gözden geçirelim:

  • İkili hataya neden olan değişiklik: Derlemenin önceki bir sürümünde derlenen diğer yönetilen kodun çalışma zamanında yenisine bağlanamamasına neden olan bir API değişikliği. Örnekler şunları içerir:

    • Mevcut genel üyenin imzasını değiştirme.
    • Genel üyeyi yeniden adlandırma.
    • Ortak tür kaldırılıyor.
    • Bir türe soyut üye veya arabirime herhangi bir üye ekleme.

    Ancak ikili hataya neden olan değişiklikler şunlar değildir:

    • Bir sınıfa veya yapıya soyut olmayan bir üye ekleme.
    • Var olan bir türe eksiksiz (soyut olmayan) bir arabirim uygulaması ekleme.
  • Protokole aykırı değişiklik: Uzak tarafın düzgün seri durumdan çıkaramaması ve işleyememesi için bazı veri türlerinin serileştirilmiş biçiminde veya RPC yöntemi çağrısında yapılan bir değişiklik. Örnekler şunları içerir:

    • Bir RPC yöntemine gerekli parametreleri ekleme.
    • Daha önce null olmayan olması garanti edilen bir veri türünden üye kaldırma.
    • Bir yöntem çağrısının önceden var olan diğer işlemlerde önce yerleştirilmesi gerektiğini belirten bir gereksinim ekleme.
    • Bu üyedeki verilerin serileştirilmiş adını denetleen bir alan veya özelliğe öznitelik ekleme, kaldırma veya değiştirme.
    • (MessagePack): Mevcut bir üyenin DataMemberAttribute.Order özelliğini veya KeyAttribute tamsayısını değiştirme.

    Ancak aşağıdakiler protokole aykırı değişiklikler değildir :

    • Veri türüne isteğe bağlı üye ekleme.
    • RPC arabirimlerine üye ekleme.
    • Mevcut yöntemlere isteğe bağlı parametreler ekleme.
    • Bir tamsayıyı veya kayan değeri temsil eden parametre türünü daha uzun veya duyarlıklı bir parametreyle değiştirme (örneğin, int veya long float olarak double).
    • Parametre yeniden adlandırıyor. Bu teknik olarak JSON-RPC adlı bağımsız değişkenleri kullanan istemciler için hataya neden olur, ancak varsayılan olarak konumsal bağımsız değişkenleri kullanan ServiceJsonRpcDescriptor istemciler parametre adı değişikliğinden etkilenmez. Bunun, istemci kaynak kodunun adlandırılmış bağımsız değişken söz dizimi kullanıp kullanmadığıyla hiçbir ilgisi yoktur; parametre yeniden adlandırması kaynak hataya neden olan bir değişiklik olur.
  • Davranış hatalarına neden olan değişiklik: Aracılı bir hizmetin uygulanmasında yapılan ve eski istemcilerin hatalı çalışmaması için davranış ekleyen veya davranışı değiştiren bir değişiklik. Örnekler şunları içerir:

    • Artık daha önce her zaman başlatılan bir veri türünün üyesi başlatılamıyor.
    • Daha önce başarıyla tamamlanabilen bir koşul altında özel durum oluşturma.
    • Daha önce döndürülenden farklı bir hata koduyla hata döndürme.

    Ancak aşağıdakiler davranışa neden olan hataya neden olan değişiklikler değildir :

Hataya neden olan değişiklikler gerektiğinde, yeni bir hizmet adı kaydedilerek ve talep edilerek güvenli bir şekilde yapılabilir. Bu takma ad aynı adı paylaşabilir ancak daha yüksek bir sürüm numarasına sahip olabilir. İkili hataya neden olan bir değişiklik yoksa özgün RPC arabirimi yeniden kullanılabilir. Aksi takdirde, yeni hizmet sürümü için yeni bir arabirim tanımlayın. Eski sürümü de kaydetmeye, desteklemeye ve desteklemeye devam ederek eski istemcileri bozmaktan kaçının.

RPC arabirimlerine üye ekleme dışında bu tür hataya neden olan tüm değişikliklerden kaçınmak istiyoruz.

RPC arabirimlerine üye ekleme

Bir RPC istemci geri çağırma arabirimine üye eklemeyin, çünkü birçok istemci bu arabirimi uygulayabilir ve üyelerin eklenmesi bu türler yüklendiğinde CLR oluşturmasına TypeLoadException neden olur ancak yeni arabirim üyelerini uygulamaz. RPC istemci geri çağırma hedefinde çağırmak için üye eklemeniz gerekiyorsa, yeni bir arabirim tanımlayın (özgün arabirimden türetilebilir) ve ardından aracılı hizmetinizi artımlı sürüm numarasıyla çağırmak için standart işlemi izleyin ve güncelleştirilmiş istemci arabirimi türü belirtilmiş bir tanımlayıcı sunun.

Aracılı hizmet tanımlayan RPC arabirimlerine üye ekleyebilirsiniz. Bu protokole aykırı bir değişiklik değildir ve yalnızca hizmeti uygulayanlarda ikili hataya neden olan bir değişikliktir, ancak büyük olasılıkla yeni üyeyi uygulamak için de hizmeti güncelleştiriyor olursunuz. Rehberimiz, aracılı hizmetin kendisi dışında kimsenin RPC arabirimini uygulamaması gerektiği (ve testlerin sahte çerçeveler kullanması gerektiği) olduğundan, RPC arabirimine üye ekleme işlemi kimseyi bozmamalıdır.

Bu yeni üyeler, hangi hizmet sürümünün bu üyeyi ilk eklediğini tanımlayan xml belgesi açıklamalarına sahip olmalıdır. Daha yeni bir istemci yöntemini uygulamayan eski bir hizmette yöntemini çağırırsa, bu istemci yakalayabilir RemoteMethodNotFoundException. Ancak bu istemci hatayı tahmin edebilir (ve muhtemelen tahmin etmeli) ve ilk etapta çağrıdan kaçınabilir. Mevcut hizmetlere üye eklemeye yönelik en iyi yöntemler şunlardır:

  • Hizmetinizin bir sürümündeki ilk değişiklik buysa: Üyeyi ekleyip yeni tanımlayıcıyı bildirdiğinizde hizmet adınızda ikincil sürümü çarpın.
  • Eski sürüme ek olarak yeni sürümü kaydetmek ve etkinleştirmek için hizmetinizi güncelleştirin.
  • Aracılı hizmetinizin bir istemcisi varsa, istemcinizi daha yeni sürümü istemek için güncelleştirin ve yeni sürüm null olarak geri gelirse eski sürümü istemeye geri dönün.