Web Arkitektur

Vi fremmer forståelse for webbens arkitektur
 

Å bruke web arkitektur i ikke-web-programvare

Skalerbarhet i tradisjonell softwareutvikling innebærer ofte at man gjør seg mindre avhengig av databasen, og introduserer en objektcache i minnet til en eller flere servere. Problemer oppstår når dataene forandrer seg under skjørtene på serveren. Da kan man låne noen teknikker fra webben: HTTP har nemlig særdeles gode mekanismer for å kunne skalere.

Først må man akseptere at ikke alle nodene i en server vil til enhver tid se de aller siste dataene. Hvis du lager en børsapplikasjon som skal tjene penger på day-trading er det kanskje greit å ha de siste tallene, og ikke en 60 sekunder gammel kurs. Kan man akseptere litt elde (noen sekunder) er mulighetene mange.

Ok punkt en er å putte objektene inn i cache, men det er jo ikke så spennende. Noen ganger vet man at objektet forandrer seg et spesielt klokkeslett. Slikt kan man nytte seg av: Man tar HTTP’s Cache-Control: max-age og gjør noe tilsvarende med objektene når de puttes i en cache. Hvis man vet at objektet ikke er gyldig etter et visst klokkeslett er det bare å fortelle cache’n dette.

Hvis objektene fakisk brukes til videre oppdatering mot databasen (f.eks. du forandrer en liten ting og O/R mappingen tar og lagrer hele greia) så låner man If-Unmodified-Since eller enda bedre: If-Match fra HTTP og "gjør det selv. Når man får et objekt som skal sendes ned til databasen, så sjekker man versjonsnummeret til objektet man fikk er det samme som versjonsnummeret i databasen. Versjonsnummeret må selvfølgelig ikke være noe som klientkoden kan forandre på. Modifiseringsdato funker også om man ikke liker versjonsnummer.

Hva om dataene har forandret seg? Jo, det er jo HTTP 412 Precondition Failed som får en liknende vri i koden din. Slikt kan stort sett unngås ved å gjøre noe som tilsvarer en unconditional GET—nemlig å forbigå cachen når man henter data for oppdatering. Ingen kan garantere at andre holder seg unna dataene om ikke man har en form for pessimistisk lås da. Utrolig nok har ikke Wikipedia noen entry for Pessimistic locking, bare Optimistic locking.

Hvis objektene forandrer seg av ymse årsaker, så kan man låne mnot sin lovende Cache Channels slik: Med jevne mellomrom (et par ganger i minuttet) henter cachen ut en liste med endringer fra databasen, og fjerner så utdaterte objektene fra cachen. Finnes det flere cacher vil de alle hente samme liste med jevne mellomrom, og er det virkelig mange cacher kan jo listen over endringer caches den også.

Nettoresultat:

  • Cachen kan holde på objekter i laang tid. Hvis dette er en memcached kan dette være gunstig :-)
  • Cachen fjerner objekter som går ut på tid til riktig tid
  • Cachen fjerner objekter som har forandret seg innen 30 sekunder, i snitt etter 15 sekunder (eller slik du konfigurerer)
  • Oppdateringer på “gamle” objekter nektes, så man beholder dataintegritet
  • Det skalerer: Du kan ha så mange cacher i systemet som du ønsker

Ganske stilig.

Motto? Les HTTP spec’en (igjen)!

Metadata i HTML

HTTP handler jo om hypermedia: Du mottar et dokument som har lenker til andre dokumenter. Men visste du at det er mulig å lenke til andre dokumenter uten å putte lenkene inni dokumenter?

HTTP deler jo responsen i data og metadata, nemlig i selve dokumentet og headers (“hoder”?). Header’ene beskriver selve dokumentet sett utenifra; f.eks. Last-Modified forteller når dokumentet sist ble endret. Content-Length forteller hvor mange bytes det er. Cache-Control forteller deg om hvor lenge du bør anse dokumentet som gyldig. Allow forteller deg hva du kan gjøre med dokumentet. Ad kjedsommelig. Men det finnes en HTTP header som har vært med siden 1990 men som ble tatt ut av selve HTTP spec’en fordi den ikke var særlig i bruk1. Jeg snakker om Link header’en.

HTML blir jo ofte hyllet som det ultimate hypermediaformatet, på grunn av a og link taggene. Faktisk anbefaler Sam Ruby et al at man bruker HTML så langt det lar seg gjøre hvis man skal lage en RESTful web service. Skal man lage en to-do-liste web service så burde man returnere application/xhtml med head og body og en HTML-liste for hver to-do-liste…

Men å si at dette andre dokumentet er en i en rekke av dokumenter (next/previous), eller at atom feeden til dokumentet er her: Det er jo metadata! Jeg ønsker ikke å måtte forkludre dokumentet mitt. Hva om jeg returnerer et dokument som ikke har rom for lenker? Putt dem i HTTP header’ene istedenfor i HTML header’en:

Link: <http://my/other/reference.txt>; rel="alternate"; type="text/plain"
Link: <http://mypage/me>; rev="made"; title="John Doe"

Mark Nottingham har forsøkt å beskrive Link headeren i en egen Internet Draft2, og diskusjonen fortsetter hos w3c3. Om man bruker Link header’en istedenfor lenker inni dokumentet så trenger ikke klienten forstå dokumentet. Om jeg serverer et WORD dokument eller en PDF så har de vidt forskjellig metamodeller; Jeg kunne hente ut forfatter, men da må jeg ha en WORD parser eller en PDF parser. Om de lå i header’en ville jobben min som klient være betydelig lettere, om jobben består i å finne dokumenter av en viss forfatter.

Personlig anser jeg at metadata hører hjemme i HTTP header’en, og ikke i dokumentet. Metadata i dokumentet er jo plutselig data. At HTTP og HTML begge støtter både data og metadata er litt kunstig, og speiler kanskje det faktum at webben stammer fra fil-servere som ikke hadde noen særlig rik meta-modell. Både Last-Modified, Content-Length og Allow støttes jo av ethvert filsystem, og HTML fikk jo tilogmed meta http-equiv tags som skulle erstatte HTTP headers. Det er jo helt sykt! Tiden er kanskje moden for å putte metadata der det hører hjemme, nemlig i HTTP!

1 Link Header esw.w3.org

2 Mark Nottinghams Draft

3 Diskusjon om temaet

Eksempler på Hypermedia

Den lille CSS-saken som Alexander postet her om dagen fikk meg til å tenke:

Hvorfor er det bare html som hylles som det store hypermediaformatet? CSS er jo vel så bra, og brukes i nesten like stor grad

Så her sitter jeg og skriver igjen! Hypermedia er jo egentlig bare media (tekst, bilder, video) med hyperlenker til annet media (mer tekst, bilder, video). Når man sier hypermedia er det jo HTML man tenker på: HyperText Markup Language. Naturlig, ikke sant? Men hva med f.eks. CSS? Er ikke det er hypermediaspråk? Sier man ikke i CSS at man ønsker det bakgrunnsbildet på dette elementet? Og da refererer man jo til bildet ved hjelp av en URI - altså er det en hyperlenke. I alle fall på lik linje med img eller @object@-taggene i HTML.

Og da tenker jeg: Er det andre opplagte hypermediaformat man bør vurdere når man skal gi fra seg representasjoner?

JSON blir ofte disset fordi det sies at det ikke er et hypermediaformat, og det har man jo rett i. CSS har en egen “uri” funksjon for å uttrykke hypertekstlenker. JSON har ikke det.

XML blir ofte hypet opp som et veldig bra hypermediaformat, men det er jo helt feil. XML har heller ingen “uri” funksjon for å si at dette attributtet eller denne strengen her er faktisk en URI som en UA bør bruke til noe.

Slikt må (for både XML og JSON) uttrykkes i andre dokumenter som beskriver strukturen i dokumentet. Ironisk nok gjøres dette i XML ved hjelp av URI’er:

xmlns:foo=“urn:foo”

xsi:schemaLocation=“urn:foo http://bar”
xmlns:xsi=“.”

Her sier man at dokumentet ligger i namespacet foo. Neste linje sier at namespacet foo identifiseres ved hjelp av URI’en urn:foo. Dernest sier man at namespacet urn:foo har et XML schema som ligger klart til henting på http://bar—en XML prossessor kan hente denne for å sikre seg dokumentet er gyldig. Til slutt må man si til prosessoren at prefixet xsi faktisk er XML Schema Instance namespacet, som igjen blir identifisert ved hjelp av nok en URI. XML parseren slipper å hente skjemadefinisjonen for xsi fordi parseren skal kjenne igjen denne bare på bakgrunn av URI’en.

Men spør du meg, er det mye magi bare for å gjøre XML til et überhypermediaformat. XML Schema Instance er også et lite dyr, men med litt øvelse kan man sikkert definere at et attributt er av type anyURI. Dermed kan hvemsomhelst som leser dokumentet og skjemaet ditt, kjenne igjen URIer. Men hva skal man bruke det til? Huff :-)

JSON tiltaler meg. Det er Lists og Maps i skjønn harmoni. Et rent datautvekslingsformat som ikke forsøker å spesifisere datatypen utover noen enkle primitiver. Det er ikke noe problem å uttrykke en URI i et JSON dokument; Det er jo bare en streng som alltid. Så blir det opp til spesifikasjonen av JSON-dokumentet (som kanskje ligger i prosatekst slik som hele Atom Publishing Protocol - APP har ingen XML skjema) å angi hvilke strenger som er URIer osv. JSON er da like bra hypermediaformat som XML???

Misforstått: URI

URIer er en av hjørnestenene i webbens arkitektur. Alle websider har en adresse, en oppskrift for å hente en spesifik webside. Opprinnelig het disse URLer fordi de lokaliserte websider på nettet.

F.eks. denne sonen har URI’en http://web-arkitektur.origo.no/ Det er sonens kanoniske identifikator. Jeg kan gi URIen til andre og de vil kjenne den igjen som en URL og skjønne at den peker på noe: URIen er også sonens URL—man kan skrive det inn i en nettlos, og så forsøker den å hente innholdet. Voilá.

Men arkitekturen bygger også på at man kan identifisere hva som helst - ikke bare websider.

En vanlig misforståelse er at URIer angir nettopp websider. Det gjør de ikke. De fleste URIer på webben i dag gjør det (stort sett websider, bilder, CSS og javascript), men noen URIer gjør det ikke.

Min oppfattning av URIer er at de kan identifisere hva som helst: For eksempel bygger XML Namespaces på URIer. Et XML Namespace er ikke en webside eller en skjemadefinisjon, men et abstrakt konsept om et unikt navneområde for XML tagger. Det er ingen forventning om at man skal kunne putte inn en namespace URI i en braoser og få f.eks. et skjemadokument. Namespace URIen er primært for å identifisere namespacet fra andre namespaces. Det er en identifikator.

JSP Taglib URIer har samme funksjon. De er der kun for å identifisere et tag library fra et annet tag library. Her forventes det heller ikke (strengt tatt) at man må eller kan hente noe hvis man går til et taglib URI. Taglib URIer er ikke nødvendigvis URLer.

På denne måten kan URIer brukes til å identifisere ting uten at man må sette opp ett nettsted for å servere innhold. Ting som ikke kan servere innhold. For eksempel mennesker, bygninger, produkter, lyspærer, og stoler kan i teorien gis egne URIer og refereres til.

Andre som kjenner til de samme tingene kjenner også disse tingenes URIer og kan dermed kjenne igjen URIen hvis vi ser den. XML prosessorer som kjenner igjen et xml namespace URIer. JSP prosessorer som kjenner igjen taglib URIer.

RDF er et språk som har tatt dette til sitt hjerte. RDF er et språk som lar deg uttrykke kunnskap om relasjoner mellom ressurser. URIer brukes her for å identifisere personer, firmaer, blogger og blogginnlegg, og attributter på disse: navn, overskrifter, forfattere osv. Man kan uttrykke at en person er forfatter til en bloggentry, at personen har navnet sånn og sånn, og at han kjenner disse andre personene, og at de har disse navnene. Her og der kommer det også “rdfs:seeAlso” som sier at her er en URI som forteller deg mer om denne ressursen.

Hva er poenget med URIer som ikke lar seg “hente”? For en webutvikler kan det være meningsløst. Det skjønner jeg. Men i softwareutvikling kan det brukes som en generisk måte å referere til ting. Primærnøkler? Hvorfor ikke? Sannsynligvis vil alle dataene leve med samme scheme (http har grei semantikk…) og et hostnavn hvor dataene “lever” (i kanonisk forstand) Det som gjenstår er “path” til dataene. På samme måte som på en web server er pathene dine en uendelig stor tomt som skal deles opp.

Poenget er at det gir universell adresserbarhet. Hvemsomhelst i verden kan referere til tingene dine, presist og nøyaktig.

Hvorfor velge en annen identifikator enn URIer?

The Great Specificity Swindle

SitePoint har en god artikkel om spesifisitet i CSS:

Among the pages of arcane CSS lore you’ll find something called the CSS cascade; the thing that ultimately decides what each element’s style will eventually be. It has a reputation for being difficult to understand and is often the cause of those frustrating, obscure CSS problems when what happens in the browser is nothing like what you were expecting to happen. The amount of misinformation on the web certainly doesn’t help, so this is my small effort to correct the situation: putting to rest two of the biggest myths about the CSS Cascade.

Artikkelen tar for seg misforståtte kroker av CSS, som cascading, hvilken rekkefølge CSS-regler brukes, og hvilke selektorer som får presedens.

Kule URIer!

Begrepet Cool URIs stammer fra dokumentet Cool URIs don’t change skrevet av Tim Berners Lee tilbake i 1998. Hvis du ikke vet hvem Tim er får du vel finne ut av det først.

Kort sagt: Kule URIer er de som ikke forandrer seg. De inneholder ikke spor av hvilken teknologi som er i bruk; de inneholder ikke navnet på forfatteren. De inneholder ting som klart identifiserer ressursen.

VG startet på nett på slutten av forrige århundre og brukte den gang URIer som så slik ut: http://www.vg.no/pub/vgart.hbs?artid=183653. De eksponerte en intern ID. De eksponerer filtypen “hbs” og alle artiklene serveres fra samme “script” uansett hvor de er. Hvis de skiftet ut teknologien som brukte noe annet enn den interne IDen for å identifisere ressursen måtte de hatt en tjeneste som slår opp den nye ressursen og serverer det riktige innholdet, eller sender HTTP 301 Moved Permanently. VGs artikler fra 1998 virker fortsatt og det kan jo hende at de allerede har byttet ut eller skrevet om systemet flere ganger uten at man ser det fra utsiden. Det er jo kult :-)

Dagbladets URIer har ikke vært like men man får tak i sider tilbake fra 1998.
1996: http://www.dagbladet.no/961101/spo-1.html (virker ikke)
1998: http://www.dagbladet.no/sport/1998/05/30/103525.html (virker)
2000: http://www.dagbladet.no/sport/2000/03/01/196458.html&f=dbforside&t=Vi20ff8lger20Champions20League (virker ikke om ikke du tar bort query strengen)
2001: http://www.dagbladet.no/sport/2001/03/01/244742.html (virker)

Dagbladet eksponerer fortsatt “html” extension som gir et sterkt inntrykk av de fortsatt publiserer statiske filer til en simpel webserver. HTML er jo ganske avlegs; kanskje man finner opp noe mye bedre og ønsker å gå over til det; da er man låst til at alle de gamle URIene hevder at de var HTML

Og selvfølgelig Aftenposten da:
1996: http://www.aftenposten.no/nyheter/forste/s23186.htm (virker ikke)
1998: http://www.aftenposten.no/nyheter/iriks/d21775.htm (virker ikke)
2000: http://www.aftenposten.no/cgi-bin/top.cgi?./nyheter/uriks/d157643.htm (virker ikke)
2002: http://www.aftenposten.no/nyheter/iriks/article.jhtml?articleID=301706 (virker nesten)
2004: http://www.aftenposten.no/nyheter/iriks/oslo/article.jhtml?articleID=721533 (virker nesten)
2006: http://www.aftenposten.no/nyheter/uriks/article1190511.ece (virker)

Begredelig. Jeg blir vel flamet for den siste URIen. Men for å ta mine kule URI-briller på: i 2000 forsøkte man seg på å legge inn et CGI-script med det resultat at alle URIene forandret seg. Og så byttet man publiseringssystem og alt forandret seg igjen. Jommen greide de ikke å gjøre det igjen i 2005 (men da med en manuell browserbasert redirect). Men det er verdt å legge merke til at innenriks og utenriks er to helt klare avdelinger og at de helt siden 1998 heter “iriks og "uriks”. Ergo er det rimelig å anta at slik informasjon faktisk hører hjemme i URIen til nyhetsmedier.

Historien er altså full av URIer som er laget av folk som ikke skjønner hva en URI faktisk er. Det er lett å slenge opp et CGI script og lage en PHP som henter en dings, men da sier du at URIen til dingsen din faktisk er /cgi-bin/en.php?dingsID=123… Tenk heller hvilke URIer som vil overleve systemskift etter systemskift uten sære omskrivninger i over hundre år! “/dingser/123” eller “/dingser/2007/rar-dings” eller “/2007/10/02/dingser/rar”… Så krever du at programvare du kjøper for å håndtere innholdet ditt skal holde seg til det URI skjemaet du velger.

Få URIer inn i kravspekken! Og utviklere: Tenk dere om! (Pass Opp! Stein kastet i glasshus!)

Hypermedia som motoren for applikasjonstilstand

Overskriften er en fornorskning av Fielding’s “Hypermedia as the engine of application state” og er nok det prinsippet som har størst sprik mellom utviklernes forståelse og den faktiske arkitekturen i webben.

I et hypermedium som verdensveven er så er din www-browser en applikasjon. Browseren har en tilstand som beskrives kort og konsist ved hjelp av URI linja øverst. Hvis du velger deg et bokmerke og åpner en tradisjonell webside så får du opp denne siden. Alle andre kan gjøre det samme og vil se det samme som deg.

Klikker du så på en lenke havner du gjerne på en ny side, og tilstanden til browseren er nå denne nye siden. Hypermedia driver applikasjonstilstanden din.

Alle har vært borti søk hvor man POSTer formen til serveren. Resultatet er at URIen ikke gjenspeiler tilstanden til browseren. Man kan ikke sende URIen til noen andre, og man kan ikke lage noe bokmerke. Det bryter med idéen om at applikasjonstilstand (browser) styres av hypermedia (lenker til URIer).

Eller for eksempel frames. Med en gang man lager en side med frames mister man dette aspektet ved hypermedia; det er vanskelig, om ikke umulig å komme tilbake til en side med frames, simpelthen fordi det bryter med web arkitekturen. Hypermedia er ikke lengre det som driver tilstanden; den driver flere mini-tilstander som hver har sin URI (og som gjerne snakker sammen).

Det finnes andre mindre opplagte ting som gjør det vanskelig å holde applikasjonstilstand i URIen: Cookies.

La oss si at vi ønsker en cookie som lagrer de sidene du har besøkt, og på bakgrunn av den kommer med forslag til sider du antakelig liker, som Amazon gjør. Du kan nå ikke lage noe bokmerke til en side med forslagene dine fordi andre folk har ikke denne cookien og får ikke samme tilstand som deg. Hadde man puttet historikken i URIene ville det vært no problemo fordi URIen ville inneholdt alt som trengs for å gi de samme forslagene.

Noen browsere cacher sider i historikken og lar deg browse dem ved å trykke back-knappen. Noen cacher litt vel agressivt, noen gjør en conditional GET for å verifisere at innholdet er oppdatert. Hvis man har fått satt en cookie vil back-knappen vise deg nytt innhold--ikke det gamle!

Cookies ødelegger også for proxy caching ved at responsen blir avhengig av hva man har satt i en spesiell cookie. Man kan fikse dette med Vary headere men det gjør at responsen blir avhengig av alle cookies og ikke den som forandret hvordan websiden så ut…

Det er så lett å trå feil. Både for oss utviklere som lager CMSer osv, og for malverksutviklere som jobber i PHP, Ruby, JSP osv.

Hva er nå web arkitektur?

Så går startskuddet for et storstilkt eksperiment, nemlig å få utviklere til å skjønne den arkitekturen som ligger til grunn for verdensveven. Sonen burde kanskje hete vevarkitektur for å ikke erte på seg språkrådet eller det frie språkrådet men la gå.

Jeg har ikke tenkt å kreve at alle setter seg ned og leser Roy T. Fielding sin doktoravhandling som virkelig vil kan gjerne gjøre det. Det er tungt materie men hvis du skjønner den kommer du langt.

Avhandlingen beskriver de veivalg som ble tatt da www ble laget, hvilke protokoller som kom før, og hvilke krav man hadde til et slikt distribuert system. For eksempel skulle systemet ikke dette sammen dersom en server gikk ned. Dette er jo selvfølgeligheter i dag men tilbake i 1992 fantes det ikke et client server program som var uavhengig av serveren. Tilsvarende måtte man minske antall “tur-retur” meldinger på nettet. Dette manifesterte seg i at en web samtale nå består av to meldinger: “request” (som oftest GET) og “response” (som oftest 200 OK).

Enkelthet var en annen målsetning. Innhold skulle være enkelt å skrive. Hypermedia (rik tekst med lenker til mer informasjon) ble valgt fordi det enkelt tillater uendelig komplekse vev av dokumenter. Protokollen forble tekstbasert fordi det skulle være enkelt for utviklere å lage server og browsere.

Alt innhold på web’en har også unike adresser. “Universal Resource Locator” ble omsider til “Uniform Resource Identifier” men fyller samme rolle. Alle web-sider har en URI. Du kan putte URIer i websider med a href tagger, og det funker utmerket. Du kan også skrive det på en gul lapp og henge det på kjøleskapet hjemme. en URI er universell og er lett gjenkjennelig.

Disse konseptene er det nok mange som skjønner men det som er problemet er kanskje setningen “Hypermedia as the engine of application state”. Vi får ta det senere!

Velkommen!

[Edit: title]