Introduktion til Dubbo

1. Introduktion

Dubbo er en open source RPC og mikroservice ramme fra Alibaba.

Blandt andet hjælper det med at forbedre serviceforvaltningen og gør det muligt for en traditionel monolit-applikation at blive omformet glat til en skalerbar distribueret arkitektur.

I denne artikel giver vi en introduktion til Dubbo og dens vigtigste funktioner.

2. Arkitektur

Dubbo adskiller et par roller:

  1. Udbyder - hvor service udsættes en udbyder registrerer sin tjeneste i registreringsdatabasen
  2. Container - hvor tjenesten startes, indlæses og køres
  3. Forbruger - der påberåber sig fjerntjenester; en forbruger abonnerer på den nødvendige service i registreringsdatabasen
  4. Registreringsdatabase - hvor tjeneste registreres og opdages
  5. Monitor - registrer statistik for tjenester, for eksempel hyppighed af serviceopkald i et givet tidsinterval

(kilde: //dubbo.io/images/dubbo-architecture.png)

Forbindelser mellem en udbyder, en forbruger og en registreringsdatabase er vedvarende, så når en tjenesteudbyder er nede, kan registreringsdatabasen opdage fejlen og underrette forbrugerne.

Registret og skærmen er valgfri. Forbrugerne kunne oprette forbindelse direkte til tjenesteudbydere, men hele systemets stabilitet ville blive påvirket.

3. Maven-afhængighed

Lad os tilføje følgende afhængighed, før vi dykker ind pom.xml:

 com.alibaba dubbo 2.5.7 

Den seneste version kan findes her.

4. Bootstrapping

Lad os nu prøve de grundlæggende funktioner i Dubbo.

Dette er en minimalt invasiv ramme, og mange af dens funktioner afhænger af eksterne konfigurationer eller kommentarer.

Det foreslås officielt, at vi skal bruge XML-konfigurationsfil, fordi den afhænger af en Spring-container (i øjeblikket Spring 4.3.10).

Vi viser de fleste af dens funktioner ved hjælp af XML-konfiguration.

4.1. Multicast Registry - Tjenesteudbyder

Som en hurtig start har vi kun brug for en tjenesteudbyder, en forbruger og et "usynligt" register. Registret er usynligt, fordi vi bruger et multicast-netværk.

I det følgende eksempel siger udbyderen kun “hej” til sine forbrugere:

offentlig grænseflade GreetingsService {String sayHi (String name); } offentlig klasse GreetingsServiceImpl implementerer GreetingsService {@Override public String sayHi (String name) {return "hi," + name; }}

For at foretage et fjernprocedureopkald skal forbrugeren dele en fælles grænseflade med tjenesteudbyderen og dermed grænsefladen HilsenService skal deles med forbrugeren.

4.2. Multicast Registry - Service Registrering

Lad os nu registrere HilsenService til registreringsdatabasen. En meget bekvem måde er at bruge et multicast-register, hvis både udbydere og forbrugere er på det samme lokale netværk:

Med bønnekonfigurationen ovenfor har vi lige udsat vores HilsenService til en url under dubbo: //127.0.0.1: 20880 og registrerede tjenesten til en multicast-adresse specificeret i .

I udbyderens konfiguration erklærede vi også vores applikationsmetadata, grænsefladen til henholdsvis offentliggørelse og implementering af , og .

Det dubbo protokol er en af ​​mange protokoller, som rammen understøtter. Det er bygget oven på den ikke-blokerende Java NIO-funktion, og det er den anvendte standardprotokol.

Vi diskuterer det mere detaljeret senere i denne artikel.

4.3. Multicast Registry - Serviceforbruger

Generelt skal forbrugeren angive grænsefladen, der skal påberåbes, og adressen på fjerntjenesten, og det er præcis, hvad der er nødvendigt for en forbruger:

Nu er alt klar, lad os se, hvordan de fungerer i aktion:

offentlig klasse MulticastRegistryTest {@Før offentlig ugyldig initRemote () {ClassPathXmlApplicationContext remoteContext = ny ClassPathXmlApplicationContext ("multicast / provider-app.xml"); remoteContext.start (); } @Test offentlig ugyldighed givenProvider_whenConsumerSaysHi_thenGotResponse () {ClassPathXmlApplicationContext localContext = ny ClassPathXmlApplicationContext ("multicast / consumer-app.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); String hiMessage = greetingsService.sayHi ("baeldung"); assertNotNull (hiMessage); assertEquals ("hej, baeldung", hiMessage); }}

Når udbyderen er remoteContext starter, indlæses Dubbo automatisk HilsenService og registrer det i et givet register. I dette tilfælde er det et multicast-register.

Forbrugeren abonnerer på multicast-registreringsdatabasen og opretter en proxy på HilsenService i sammenhængen. Når vores lokale klient påberåber sig sig hej metode, er det gennemsigtigt at påkalde en fjerntjeneste.

Vi nævnte, at registreringsdatabasen er valgfri, hvilket betyder, at forbrugeren kunne oprette forbindelse direkte til udbyderen via den eksponerede port:

Grundlæggende ligner proceduren den traditionelle webservice, men Dubbo gør det bare enkelt, let og let.

4.4. Simpel registreringsdatabase

Bemærk, at når du bruger et "usynligt" multicast-register, er registreringsdatabasetjenesten ikke selvstændig. Det gælder dog kun for et begrænset lokalt netværk.

For eksplicit at oprette et administrerbart register kan vi bruge en SimpleRegistryService.

Efter indlæsning af følgende bønnekonfiguration i Spring-sammenhæng startes en simpel registreringsdatabasetjeneste:

Bemærk, at SimpleRegistryService klasse er ikke indeholdt i artefakten, så vi kopierede kildekoden direkte fra Github-arkivet.

Derefter justerer vi udbyderens og forbrugerens registreringsdatabase-konfiguration:

SimpleRegistryService kan bruges som et enkeltstående register under test, men det anbefales ikke at blive brugt i produktionsmiljø.

4.5. Java-konfiguration

Konfiguration via Java API, ejendomsfil og annoteringer understøttes også. Egenskabsfil og annoteringer gælder dog kun, hvis vores arkitektur ikke er særlig kompleks.

Lad os se, hvordan vores tidligere XML-konfigurationer til multicast-registreringsdatabasen kan oversættes til API-konfiguration. For det første er udbyderen konfigureret som følger:

ApplicationConfig-applikation = ny ApplicationConfig (); application.setName ("demo-udbyder"); application.setVersion ("1.0"); RegistryConfig registryConfig = ny RegistryConfig (); registryConfig.setAddress ("multicast: //224.1.1.1: 9090"); ServiceConfig service = ny ServiceConfig (); service.setApplication (applikation); service.setRegistry (registryConfig); service.setInterface (GreetingsService.class); service.setRef (ny GreetingsServiceImpl ()); service.export ();

Nu hvor tjenesten allerede er eksponeret via multicast-registreringsdatabasen, lad os forbruge den i en lokal klient:

ApplicationConfig-applikation = ny ApplicationConfig (); application.setName ("demo-forbruger"); application.setVersion ("1.0"); RegistryConfig registryConfig = ny RegistryConfig (); registryConfig.setAddress ("multicast: //224.1.1.1: 9090"); ReferenceConfig reference = ny ReferenceConfig (); reference.setApplication (applikation); reference.setRegistry (registryConfig); reference.setInterface (GreetingsService.class); GreetingsService greetingsService = reference.get (); String hiMessage = greetingsService.sayHi ("baeldung");

Selvom uddraget ovenfor fungerer som en charme som det tidligere XML-konfigurationseksempel, er det lidt mere trivielt. Indtil videre skal XML-konfiguration være det første valg, hvis vi har til hensigt at udnytte Dubbo fuldt ud.

5. Protokolstøtte

Rammen understøtter flere protokoller, herunder dubbo, RMI, hessian, HTTP, webservice, sparsommelighed, memcached og redis. De fleste af protokollerne ser velkendte ud med undtagelse af dubbo. Lad os se, hvad der er nyt i denne protokol.

Det dubbo protokol opretholder en vedvarende forbindelse mellem udbydere og forbrugere. Den lange forbindelse og NIO-ikke-blokerende netværkskommunikation resulterer i en forholdsvis god ydeevne, mens der transmitteres mindre datapakker (<100K).

Der er flere konfigurerbare egenskaber, såsom port, antal forbindelser pr. Forbruger, maksimalt accepterede forbindelser osv.

Dubbo understøtter også eksponering af tjenester via forskellige protokoller på én gang:

Og ja, vi kan eksponere forskellige tjenester ved hjælp af forskellige protokoller, som vist i uddraget ovenfor. De underliggende transportører, implementeringer af serialisering og andre almindelige egenskaber i forbindelse med netværk kan også konfigureres.

6. Resultat caching

Nativt fjern cache-resultat understøttes for at fremskynde adgangen til hot data. Det er så simpelt som at tilføje en cache-attribut til bønnereferencen:

Her konfigurerede vi en mindst for nylig brugt cache. For at bekræfte cache-opførsel ændres vi lidt i den tidligere standardimplementering (lad os kalde det "speciel implementering"):

offentlig klasse GreetingsServiceSpecialImpl implementerer GreetingsService {@Override public String sayHi (String name) {prøv {SECONDS.sleep (5); } fange (Undtagelse ignoreret) {} returner "hej" + navn; }}

Efter at have startet udbyderen kan vi kontrollere på forbrugerens side, at resultatet caches, når man påberåber sig mere end en gang:

@Test offentlig ugyldighed givenProvider_whenConsumerSaysHi_thenGotResponse () {ClassPathXmlApplicationContext localContext = ny ClassPathXmlApplicationContext ("multicast / forbruger-app.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); længe før = System.currentTimeMillis (); String hiMessage = greetingsService.sayHi ("baeldung"); long timeElapsed = System.currentTimeMillis () - før; assertTrue (timeElapsed> 5000); assertNotNull (hiMessage); assertEquals ("hej, baeldung", hiMessage); før = System.currentTimeMillis (); hiMessage = greetingsService.sayHi ("baeldung"); timeElapsed = System.currentTimeMillis () - før; assertTrue (timeElapsed <1000); assertNotNull (hiMessage); assertEquals ("hej, baeldung", hiMessage); }

Her påkalder forbrugeren den specielle serviceimplementering, så det tog mere end 5 sekunder for påkaldelsen at gennemføre første gang. Når vi påberåber os igen, sig hej metoden fuldføres næsten med det samme, da resultatet returneres fra cachen.

Bemærk, at tråd-lokal cache og JCache også understøttes.

7. Klyngestøtte

Dubbo hjælper os med at opskalere vores tjenester frit med dets evne til belastningsafbalancering og flere fejltolerancestrategier. Lad os antage, at vi har Zookeeper som vores registreringsdatabase til at administrere tjenester i en klynge. Udbydere kan registrere deres tjenester i Zookeeper således:

Bemærk, at vi har brug for disse yderligere afhængigheder i POM:

 org.apache.zookeeper zookeeper 3.4.11 com.101tec zkclient 0.10 

De nyeste versioner af dyrepasser afhængighed og zkclient kan findes her og her.

7.1. Load Balancing

I øjeblikket understøtter rammen nogle få belastningsbalanceringsstrategier:

  • tilfældig
  • runde Robin
  • mindst aktiv
  • konsekvent hash.

I det følgende eksempel har vi to serviceimplementeringer som udbydere i en klynge. Anmodningerne dirigeres ved hjælp af round-robin-tilgangen.

Lad os først oprette tjenesteudbydere:

@Før offentlig ugyldig initRemote () {ExecutorService executorService = Executors.newFixedThreadPool (2); executorService.submit (() -> {ClassPathXmlApplicationContext remoteContext = new ClassPathXmlApplicationContext ("cluster / provider-app-default.xml"); remoteContext.start ();}); executorService.submit (() -> {ClassPathXmlApplicationContext backupRemoteContext = ny ClassPathXmlApplicationContext ("klynge / udbyder-app-special.xml"); backupRemoteContext.start ();}); }

Nu har vi en standard "hurtig udbyder", der reagerer med det samme, og en særlig "langsom udbyder", der sover i 5 sekunder på hver anmodning.

Efter at have kørt 6 gange med round-robin-strategien forventer vi, at den gennemsnitlige responstid er mindst 2,5 sekunder:

@Test offentlig ugyldighed givenProviderCluster_whenConsumerSaysHi_thenResponseBalanced () {ClassPathXmlApplicationContext localContext = ny ClassPathXmlApplicationContext ("klynge / forbruger-app-lb.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); Liste elapseList = ny ArrayList (6); for (int i = 0; i e). gennemsnit (); assertTrue (avgElapse.isPresent ()); assertTrue (avgElapse.getAsDouble ()> 2500.0); }

Desuden er dynamisk belastningsafbalancering vedtaget. Det næste eksempel viser, at forbrugeren med rund-robin-strategi automatisk vælger den nye tjenesteudbyder som kandidat, når den nye udbyder kommer online.

Den "langsomme udbyder" registreres 2 sekunder senere, efter at systemet starter:

@Før offentlig ugyldigt initRemote () {ExecutorService executorService = Executors.newFixedThreadPool (2); executorService.submit (() -> {ClassPathXmlApplicationContext remoteContext = new ClassPathXmlApplicationContext ("cluster / provider-app-default.xml"); remoteContext.start ();}); executorService.submit (() -> {SECONDS.sleep (2); ClassPathXmlApplicationContext backupRemoteContext = ny ClassPathXmlApplicationContext ("cluster / provider-app-special.xml"); backupRemoteContext.start (); return null;}); }

Forbrugeren påberåber sig fjerntjenesten en gang i sekundet. Efter at have kørt 6 gange forventer vi, at den gennemsnitlige svartid er større end 1,6 sekunder:

@Test offentlig ugyldighed givenProviderCluster_whenConsumerSaysHi_thenResponseBalanced () kaster InterruptedException {ClassPathXmlApplicationContext localContext = new ClassPathXmlApplicationContext ("cluster / consumer-app-lb.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); Liste elapseList = ny ArrayList (6); for (int i = 0; i e). gennemsnit (); assertTrue (avgElapse.isPresent ()); assertTrue (avgElapse.getAsDouble ()> 1666.0); }

Bemærk, at belastningsbalanceren kan konfigureres både på forbrugerens side og på udbyderens side. Her er et eksempel på konfiguration på forbrugersiden:

7.2. Fejltolerance

Flere fejltolerancestrategier understøttes i Dubbo, herunder:

  • fail-over
  • fejlsikker
  • mislykkes hurtigt
  • fail-back
  • gaffel.

I tilfælde af fail-over, når en udbyder fejler, kan forbrugeren prøve med nogle andre tjenesteudbydere i klyngen.

Fejltolerancestrategierne er konfigureret som følger for tjenesteudbydere:

Lad os oprette en fail-over implementering af for at demonstrere service fail-over i aktion HilsenService:

offentlig klasse GreetingsFailoverServiceImpl implementerer GreetingsService {@Override public String sayHi (String name) {return "hi, failover" + name; }}

Vi kan huske, at vores specielle serviceimplementering HilsenServiceSpecialImpl sover 5 sekunder for hver anmodning.

Når ethvert svar, der tager mere end 2 sekunder, betragtes som en anmodningsfejl for forbrugeren, har vi et fail-over-scenario:

Efter start af to udbydere kan vi bekræfte fail-over-opførslen med følgende uddrag:

@Test offentlig ugyldig nårConsumerSaysHi_thenGotFailoverResponse () {ClassPathXmlApplicationContext localContext = ny ClassPathXmlApplicationContext ("klynge / forbruger-app-failtest.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); String hiMessage = greetingsService.sayHi ("baeldung"); assertNotNull (hiMessage); assertEquals ("hej, failover baeldung", hiMessage); }

8. Resume

I denne vejledning tog vi en lille bid af Dubbo. De fleste brugere er tiltrukket af sin enkelhed og rige og kraftfulde funktioner.

Bortset fra hvad vi introducerede i denne artikel, har rammen en række funktioner, der endnu ikke skal udforskes, såsom parametervalidering, notifikation og tilbagekald, generaliseret implementering og reference, gruppering og fletning af fjernresultater, serviceopgradering og bagudkompatibilitet for blot at nævne nogle få.

Som altid kan den fulde implementering findes på Github.