Programmering

J2EE-klynger, del 1

Virksomheder vælger Java 2, Enterprise Edition (J2EE) for at levere deres missionskritiske applikationer over internettet. Inden for J2EE-rammen leverer klynger missionskritiske tjenester for at sikre minimal nedetid og maksimal skalerbarhed. En klynge er en gruppe applikationsservere, der gennemsigtigt kører din J2EE-applikation, som om det var en enkelt enhed. For at skalere skal du medtage yderligere maskiner i klyngen. For at minimere nedetid skal du sørge for, at alle komponenter i klyngen er overflødige.

I denne artikel får vi en grundlæggende forståelse af klyngedannelse, klyngemetoder og vigtige klyngetjenester. Fordi klyngetilgang varierer på tværs af branchen, vil vi undersøge fordele og ulemper ved hver tilgang. Yderligere vil vi diskutere de vigtige klyngerelaterede funktioner, der skal søges i en applikationsserver.

For at anvende vores nytilegnede klyngeviden i den virkelige verden vil vi se, hvordan HP Bluestone Total-e-Server 7.2.1, Sybase Enterprise Application Server 3.6, SilverStream Application Server 3.7 og BEA WebLogic Server 6.0 hver implementerer klynger.

I del 2 af denne serie vil vi dække programmering og failover-strategier for klynger samt teste vores fire applikationsserverprodukter for at se, hvordan de skaleres og failover.

Klynger defineret

J2EE-applikationsserverleverandører definerer en klynge som en gruppe maskiner, der arbejder sammen for at levere transparente virksomhedsservices (support til JNDI, EJB, JSP, HttpSession og komponent failover osv.). De efterlader definitionen med vilje vage, fordi hver sælger implementerer klyngedannelse forskelligt. I den ene ende af spektret hviler leverandører, der placerer en afsender foran en gruppe uafhængige maskiner, hvoraf ingen har kendskab til de andre maskiner i klyngen. I dette skema modtager afsenderen en indledende anmodning fra en bruger og svarer med et HTTP-omdirigeringsoverskrift for at fastgøre klienten til en bestemt medlemsserver i klyngen. I den anden ende af spektret bor leverandører, der implementerer en sammenslutning af tæt integrerede maskiner, hvor hver maskine er helt opmærksom på de andre maskiner omkring den sammen med objekterne på disse maskiner.

Ud over maskiner kan klynger omfatte overflødige og failover-kompatible:

  • Belastningsafbalancere: Enkelt indgangspunkter i klyngen og trafikdirektører til individuelle web- eller applikationsservere
  • Webservere
  • Gateway-routere: Udgangspunkter fra et internt netværk
  • Multilayer switche: Pakke- og rammefiltre for at sikre, at hver maskine i klyngen kun modtager information, der er relevant for den pågældende maskine
  • Firewalls: Klyngebeskyttere fra hackere ved at filtrere adgang på havneniveau til klyngen og det interne netværk
  • SAN (Storage Area Networking) switche: Tilslut applikationsservere, webservere og databaser til et backend-lagringsmedium; administrere hvilken fysisk disk du vil skrive data til; og failover
  • Databaser

Uanset hvordan de implementeres, giver alle klynger to hovedfordele: skalerbarhed og høj tilgængelighed (HA).

Skalerbarhed

Skalerbarhed henviser til en applikations evne til at understøtte et stigende antal brugere. Klynger giver dig mulighed for at give ekstra kapacitet ved at tilføje ekstra servere, hvilket sikrer skalerbarhed.

Høj tilgængelighed

HA kan opsummeres i et ord: redundans. En klynge bruger mange maskiner til at servicere anmodninger. Derfor, hvis en maskine i en klynge fejler, kan en anden maskine overtage transparent.

En klynge leverer kun HA på applikationsserverniveauet. For at et websystem skal udvise ægte HA, skal det være som Noahs ark, der indeholder mindst to af alt inklusive webservere, gateway-routere, skifteinfrastrukturer osv. (For mere om HA, se HA-tjeklisten.)

Klyngetyper

J2EE-klynger findes normalt i to varianter: delte intet og delt disk. I en delt-intet-klynge har hver applikationsserver sine egne filsystemer med sin egen kopi af applikationer, der kører i klyngen. Applikationsopdateringer og forbedringer kræver opdateringer i hver node i klyngen. Med denne opsætning bliver store klynger vedligeholdelses-mareridt, når kode skubber og opdateringer frigives.

I modsætning hertil anvender en delt disk-klynge en enkelt lagerenhed, som alle applikationsservere bruger til at hente de applikationer, der kører i klyngen. Opdateringer og forbedringer forekommer i et enkelt filsystem, og alle maskiner i klyngen kan få adgang til ændringerne. Indtil for nylig var en ulempe ved denne tilgang dens eneste fiasko. SAN giver dog en enkelt logisk grænseflade til et redundant lagringsmedium for at give failover, failback og skalerbarhed. (For mere om SAN, se sidebjælken til lagringsinfrastruktur.)

Når man sammenligner J2EE-applikationsservers klyngeimplementeringer, er det vigtigt at overveje:

  • Klyngeimplementering
  • Cluster- og komponent failover-tjenester
  • HttpSession failover
  • Enkelt fejlpunkter i en klyngetopologi
  • Fleksibelt topologilayout
  • Vedligeholdelse

Senere ser vi på, hvordan fire populære applikationsservere sammenlignes inden for forskellige områder. Men lad os først undersøge hvert emne mere detaljeret.

Klyngeimplementering

J2EE-applikationsservere implementerer klyngedannelse omkring deres implementering af JNDI (Java Naming and Directory Interface). Selvom JNDI er kernetjenesten, som J2EE-applikationer er afhængige af, er det svært at implementere i en klynge, fordi den ikke kan binde flere objekter til et enkelt navn. Der findes tre generelle klyngemetoder i forhold til hver applikationsservers JNDI-implementering:

  • Uafhængig
  • Centraliseret
  • Delt globalt

Uafhængigt JNDI-træ

HP Bluestone Total-e-Server og SilverStream Application Server bruger et uafhængigt JNDI-træ til hver applikationsserver. Medlemsservere i en uafhængig JNDI-træklynge kender ikke eller interesserer sig for eksistensen af ​​andre servere i klyngen. Derfor understøttes failover enten ikke eller leveres via formidlingstjenester, der omdirigerer HTTP- eller EJB-anmodninger. Disse mellemliggende tjenester er konfigureret til at vide, hvor hver komponent i klyngen ligger, og hvordan man kommer til en alternativ komponent i tilfælde af fejl.

En fordel ved den uafhængige JNDI-træklynge: kortere klyngekonvergens og let skalering. Klyngekonvergens måler den tid, det tager for klyngen at blive fuldt opmærksom på alle maskinerne i klyngen og deres tilknyttede objekter. Konvergens er dog ikke et problem i en uafhængig JNDI-træklynge, fordi klyngen opnår konvergens, så snart to maskiner starter. En anden fordel ved den uafhængige JNDI-træklynge: skalering kræver kun tilføjelse af ekstra servere.

Der findes dog flere svagheder. For det første er failover normalt udviklerens ansvar. Det vil sige, at fordi hver applikationsservers JNDI-træ er uafhængig, er de eksterne proxies, der er hentet gennem JNDI, fastgjort til den server, som opslagene fandt sted på. Under dette scenarie, hvis et metodeopkald til en EJB mislykkes, skal udvikleren skrive ekstra kode for at oprette forbindelse til en afsender, hente adressen på en anden aktiv server, foretage et nyt JNDI-opslag og ringe til den mislykkede metode igen. Bluestone implementerer en mere kompliceret form for det uafhængige JNDI-træ ved at lade enhver anmodning gennemgå en EJB-proxytjeneste eller Proxy LBB (Load Balance Broker). EJB-proxytjenesten sikrer, at hver EJB-anmodning går til en aktiv UBS-instans. Denne ordning tilføjer ekstra ventetid til hver anmodning, men tillader automatisk failover mellem metodekald.

Centraliseret JNDI-træ

Sybase Enterprise Application Server implementerer en central JNDI-træklynge. Under denne opsætning udnytter centraliserede JNDI-træklynger CORBAs CosNaming-service til JNDI. Navneservere huser det centraliserede JNDI-træ til klyngen og holder styr på, hvilke servere der er op. Ved opstart binder hver server i klyngen sine objekter i sit JNDI-træ såvel som alle navneserverne.

At få en reference til en EJB i en central JNDI-træklynge er en totrins proces. For det første ser klienten et hjemobjekt op fra en navneserver, som returnerer en interoperabel objektreference (IOR). En IOR peger på flere aktive maskiner i klyngen, der har hjemobjektet. For det andet vælger klienten den første serverplacering i IOR og henter hjemmet og fjernbetjeningen. Hvis der er en fejl imellem indkaldelse af EJB-metoden, implementerer CORBA-stub logik for at hente et andet hjem eller fjernbetjening fra en alternativ server, der er angivet i IOR, der er returneret fra navneserveren.

Navneserverne demonstrerer selv en svaghed i den centraliserede JNDI-træklynge. Specifikt, hvis du har en klynge på 50 maskiner, hvoraf fem er navneservere, bliver klyngen ubrugelig, hvis alle fem navneservere går ned. Faktisk kunne de andre 45 maskiner være i gang, men klyngen vil ikke betjene en enkelt EJB-klient, mens navneserverne er nede.

Et andet problem opstår ved at bringe en ekstra navneserver online i tilfælde af en total fejl i klyngens originale navneservere. I dette tilfælde kræver en ny central navneserver, at hver aktiv maskine i klyngen binder sine objekter til den nye navneserver's JNDI-træ. Selvom det er muligt at begynde at modtage anmodninger, mens bindingsprocessen finder sted, anbefales det ikke, da bindingsprocessen forlænger klyngens gendannelsestid. Desuden repræsenterer hvert JNDI-opslag fra en applikation eller applet virkelig to netværksopkald. Det første opkald henter IOR for et objekt fra navneserveren, mens det andet henter det objekt, som klienten ønsker fra en server, der er specificeret i IOR.

Endelig lider centraliserede JNDI-træklynger af en øget tid til konvergens, da klyngen vokser i størrelse. Når du skalerer din klynge, skal du tilføje flere navneservere. Husk, at det generelt accepterede forhold mellem navneservermaskiner og samlede klyngemaskiner er 1:10 med et minimum antal på to navneservere. Derfor, hvis du har en 10-maskinklynge med to navneservere, er det samlede antal bindinger mellem en server og navneserver 20. I en 40-maskinklynge med fire navneservere vil der være 160 bindinger. Hver binding repræsenterer en proces, hvor en medlemsserver binder alle dens objekter i JNDI-træet på en navneserver. Med det i tankerne har den centraliserede JNDI-træklynge den værste konvergenstid blandt alle JNDI-klyngeimplementeringer.

Delt globalt JNDI-træ

Endelig implementerer BEA WebLogic et delt globalt JNDI-træ. Når en server i klyngen starter, annoncerer den med denne tilgang sin eksistens og JNDI-træ til de andre servere i klyngen via IP (Internet Protocol) multicast. Hver klyngemaskine binder sine objekter ind i det delte globale JNDI-træ såvel som sit eget lokale JNDI-træ.

At have et globalt og lokalt JNDI-træ inden for hvert medlems server gør det muligt for de genererede hjem- og fjernstubber at failover og giver hurtige JNDI-opslag i processen. Det delte globale JNDI-træ deles mellem alle maskiner i klyngen, så enhver medlemsmaskine kan kende den nøjagtige placering af alle objekter i klyngen. Hvis et objekt er tilgængeligt på mere end en server i klyngen, er et særligt hjemmeobjekt bundet til det delte globale JNDI-træ. Dette specielle hjem kender placeringen af ​​alle EJB-objekter, som det er tilknyttet, og genererer eksterne objekter, der også kender placeringen af ​​alle EJB-objekter, som det er tilknyttet.

Den fælles globale tilgangs største ulemper: den store indledende netværkstrafik, der genereres, når serverne starter, og klyngens lange konvergenstid. I modsætning hertil viser konvergens i en uafhængig JNDI-træklynge ikke at være et problem, fordi der ikke sker nogen JNDI-informationsdeling. En delt global eller centraliseret klynge kræver imidlertid tid for alle klyngens maskiner at bygge det delte globale eller centraliserede JNDI-træ. Faktisk, fordi delte globale klynger bruger multicast til at overføre JNDI-information, er den tid, der kræves til at opbygge det delte globale JNDI-træ, lineær i forhold til antallet af efterfølgende tilføjede servere.

De største fordele ved delt global sammenlignet med centraliserede JNDI-træklynger er centreret om let skalering og højere tilgængelighed. Med delt global behøver du ikke at rode med CPU'erne og RAM på en dedikeret navneserver eller indstille antallet af navneservere i klyngen. I stedet for at skalere applikationen skal du blot tilføje flere maskiner. Desuden, hvis en maskine i klyngen går ned, vil klyngen fortsat fungere korrekt. Endelig kræver hver fjernopslag et enkelt netværksopkald sammenlignet med de to netværksopkald, der kræves i den centraliserede JNDI-træklynge.

Alt dette skal tages med et saltkorn, fordi JSP'er, servlets, EJB'er og JavaBeans, der kører på applikationsserveren, kan drage fordel af at være co-placeret i EJB-serveren. De bruger altid et JNDI-opslag i processen. Husk, at hvis du kun kører applikationer på serversiden, er der lille forskel mellem de uafhængige, centraliserede eller delte globale klyngeimplementeringer. Faktisk ender hver HTTP-anmodning på en applikationsserver, der foretager et JNDI-opslag i processen for at returnere ethvert objekt, der bruges i din serversidesapplikation.

Dernæst vender vi vores opmærksomhed mod den anden vigtige overvejelse om J2EE-applikationsserver: klynge- og failover-tjenester.

Klynge- og failover-tjenester