23. října 2012

Verzování webových služeb, SOAP

Před časem jsem napsal lehký úvod do SOA governance. Chtěl bych se k tomuto tématu vracet a prezentovat principy, které postupně zavádíme na projektu. Momentálně nás daleko víc pálí věci, které spadají do design fáze služeb, takže řešíme věci jako verzování, reusabilita, granularita služeb apod.

Aspekt, který jsme řešili jako první (a nyní už i zavedli v praxi) je verzování služeb. Jelikož používáme řešení založené na SOAP webových službách (jak jinak v enterprise :-) podíval bych se právě na toto téma. Jestliže budu dále mluvit o webových službách (WS), mám tím vždy na mysli WS založený na SOAP.

Proč?

Otázka je, proč vlastně webové služby verzovat? Dejme tomu, že z nějakého důvodu chceme, na určitém prostředí, držet více verzí jedné služby. Tím důvodem může být např. to, že už máme nějaké stávající konzumenty dané služby a zároveň chceme touto službou poskytnout nové (business) funkcionality. Stávající klienti ovšem nemohou (nebo také odmítnou) přejít na novou verzi služby. Starou verzi služby tedy necháme funkční a zároveň nasadíme verzi novou. V případě SOA by toto mělo být definováno/podpořeno nějakou politikou, např. podpora více verzí služeb.



Kontrakt

Jak z hlediska SOA, tak z hlediska klienta se webová služba (a její konzumace) točí kolem kontraktu. Kontrakt, jako takový, se skládá z několika částí. Jednak definuje nějaké designové věci jako datové typy, zprávy a rozhraní (ve smyslu OOP). Dále definuje technologické záležitosti, tj. jaké se používají protokoly, jaké jsou endpointy služby. A konečně jsou to nefunkční požadavky.

Pro SOAPovské služby je tímto kontraktem WSDL (Web Service Definition Language). Pokud pomineme nefunkční požadavky (které stejně nejsou definovány ve WSDL namespacu, ale jinde), má WSDL následující strukturu (zeleně jsou designové části, červeně implementační):

  • types - element definující pomocí XML Schema datové typy používané ve zprávách.
  • message - abstraktní definice přenášených dat (zpráv), sestavená z typů definovaných v předešlém elementu. Každá zpráva se skládá z jedné a více logických částí (parts). Pokud je částí ve zprávě více, jde o službu založenou na RPC.
  • portType - množina abstraktních operací, víceméně odpovídá rozhraní v OOP. Každá operace (metoda) by měla odkazovat vstupní a výstupní zprávu. Operace může být jedním ze čtyř typů: Request-response (klasika), One-way (obsahuje pouze vstupní zprávu), Notification (obsahuje pouze výstupní zprávu) a Solicit-response (endpoint pošle zprávu a očekává odpověď).
  • binding - sváže definovaný portType s konkrétním protokolem (SOAP, HTTP, JMS) a formátem zpráv (např. Document/literal, RPC/encoded).
  • port - definuje endpoint služby.
  • service - množina souvisejících portů.

Sémantika verzování

Není potřeba znovu-vymýšlet-kolo. Archetypem verzování je formát <major>.<minor>.<micro>. U služeb nás micro verze nebude moc zajímat - je vyhrazená pro implementační změny, které neovlivňují vyšší řády verze a nijak se tedy nepromítají do kontraktu. minor verze je vyhrazená pro změny rozhraní, které jsou zpětně kompatibilní. No a major verze indikuje změnu rozhraní, která není zpětně kompatibilní, tj. rozbíjí kontrakt.

Změny, které jsou zpětně kompatibilní:
  • přidání nové operace do služby,
  • přidání nového XML typu do schématu.

Změny, které nejsou zpětně kompatibilní:
  • odebrání operace ze služby,
  • přejmenování operace,
  • změna XML typů a atributů zprávy,
  • změna namespace.

Jak?

Jako best-practice se uvádí, že verzování by pro konzumenty mělo být explicitní, tj. uvádět číslo verze v elementech, URL apod. Co všechno v kontraktu verzovat, může být předmětem diskuzí na konkrétním projektu a technologiích, nicméně dá se vyjít z těchnto pravidel:
  1. Vkládat major a minor verzi do názvu WSDL souboru: MyService-v1.2.wsdl.
  2. Vkládat major verzi do targetNamespace WSDL souboru:
    <definition
      targetNamespace=
          "http://sw-samuraj.cz/ws/MyService-v1"
      xmlns="http://schemas.xmlsoap.org/wsdl/">
  3. Vkládat major a minor verzi do portType elementu:
    <portType name="MyServicePort-v1.2">
  4. Vkládat major a minor verzi do service elementu:
    <service name="MyService-v1.2">
  5. Vkládat major verzi do endpointu služby:
    <soap:address
      location=
        "http://sw-samuraj.cz/myService/v1"/>
Pokud bychom si ukázali celé WSDL, vypadalo by takto:
<?xml version="1.0" encoding="UTF-8"?>
<definitions
  targetNamespace=
      "http://sw-samuraj.cz/ws/MyService-v1"
  xmlns="http://schemas.xmlsoap.org/wsdl/"
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">

  <types>
    <xsd:schema
      targetNamespace=
            "http://sw-samuraj.cz/ws/MyService-v1">
      <!-- schema omitted -->
    </xsd:schema>
  </types>

  <!-- messages omitted -->

  <portType name="MyServicePort-v1.2">
    <!-- operations omitted -->
  </portType>

  <!-- binding omitted -->

  <service name="MyService-v1.2">
    <port binding="MyServiceSOAP"
          name="MyServiceSOAP">
      <soap:address
        location="http://sw-samuraj.cz/myService/v1"/>
    </port>
  </service>

</definitions>

Závěr

Verzování poměrně úzce souvisí s dalším SOA governance aspektem a sice životním cyklem služeb (což je téma, na které se podíváme někdy příště). To že držíme na prostředí (typicky produkci) více verzí jedné služby má samozřejmě svoje náklady a ideálním stavem je, když služba běží pouze v jedné (nejaktuálnější) verzi.

Se správou více verzí nám může pomoci vhodný nástroj - SOA governance repository, která drží o konrétní verzi služby různá meta-data a mmj. také to, v jaké životní fázi se služba nachází, nebo kdo ji konzumuje (a koho musíme notifikovat o penzionování služby).

8 komentářů:

  1. Pokud jsou služby rozsáhlé, tak má WSDL vazby na XSD definice, které mají další vazby atd. Proto je vhodné namespace verzovat od začátku nejenom ve WSDL, ale i v jednotlivých XSD s definicemi. Protože verzováním vznikají i různé soubory, musí se verzovat i jména souborů na discích.
    Máte někdo zkušenosti, jak udělat "deprecated" element nebo atribut v XSD nebo WSDL tak, aby se časem nechal zrušit a uživatelé služeb o tom byli informováni?

    Petr

    OdpovědětVymazat
    Odpovědi
    1. Ano, XSD se dost často importují a i ty by měly být verzovaný - jak jejich namespace, tak soubor samotný. Asi k tomu ještě napíšu nějaké doplnění.

      Vymazat
    2. K tomu deprecated, napsal bych to do dokumentace elementu. Jsou to meta-data, podobně jako třeba v Javě (buď v Javadocu, nebo anotace @Deprecated).

      Ale hlavně, deprecated by měla být označena služba, protože pokud je deprecated element, tak to znamená, že bude nějaká nová (a zaverzovaná) verze služby a služba s deprecated elementem by měla být v lifecyclu zaplánovaná pro retirement.

      Vymazat
    3. Chtěl jsem se spíš zeptat na případ, kdy mám rozsáhlé WS a jeden element např. jmeno_prijmeni se mi rozpadne na element jmeno a element prijmeni. Jestli je lepší vytvořit novou verzi WS včetně všech souvisejících XSD, nebo by stačilo v jednom XSD nastavit element jako zastaralý (změnit verzi) a podporovat ho stejně jako v předchozí verzi s tím, že podporuji i nové dva elementy (verze WS by se neměnila). To má výhodu, že "starý" klient pracuje se službou bez změny a novější by pracoval jen s novými elementy. Jak podporovat staré klienty, které už asi nikdo neopraví a současně podporovat i nové? Jinými slovy: jak rozumně řešit zpětnou kompatibilitu u WS?

      Vymazat
    4. Verzovat by se mělo s každou změnou. Pokud přibydou nové optional elementy a zároveň ponechám původní element, tak je to zpětně kompatibilní změna. Tj. klient může používat jak starou verzi kontraktu, tak novou. Zároveň můžu starý kontrakt z endpointu odstranit a vše by mělo zůstat funkční.

      Pokud ovšem ty změny rozbijí kontrakt, např. v nové verzi ten inkriminovaný element odstraním, tak musím podporovat dvě verze služby. S tím že ta stará je označená jako deprecated, její konzumenti by o tom měli být notifikováni a potom, časem, by měla být služba penzionována a zůstala by jenom ta nová. Ono verzování, pokud držím více verzí služby, hodně souvisí s lifecyclem (chystám o tom nějaký post).

      Další věc, která tady může pomoct, je že držím dvě verze kontraktu, ale obě používají pouze jednu implementaci - u toho příkladu se jménem se to přímo nabízí. Tady je to kombinace lifecyclu + SOA/Integračních patternů virtual endpoint a message router (o tom bych taky mohl něco napsat :-)

      Vymazat
    5. Díky za informace a budu se těšit na další články!

      Vymazat
  2. "reusabilita" se česky řekne "znovupoužitelnost". Díky za jinak kvalitně napsaný příspěvek k tématu, které právě řeším. K patternům zmíněným v diskuzi stojí za doporučení kniha SOA Patterns, kterou napsal Arnon Rotem-Gal-Oz a vydal Manning.

    OdpovědětVymazat
    Odpovědi
    1. Znovupoužitelnost znám, ale někdy jdou ty anglický termíny líp od pusy (a někdy se člověk zapomene).

      SOA Patterns jsem právě dočetl a chci o tom něco napsat. Verzování se tam ale neřeší. Tady bych spíš doporučil SOA Governance in Action (taky od Manningu) nebo SOA Design Patterns od Thomase Erla.

      Vymazat