Inleiding tot Dubbo

1. Inleiding

Dubbo is een open-source RPC- en microserviceframework van Alibaba.

Het helpt onder meer de service governance te verbeteren en maakt het mogelijk dat traditionele monoliettoepassingen soepel worden geherstructureerd naar een schaalbare gedistribueerde architectuur.

In dit artikel geven we een inleiding tot Dubbo en de belangrijkste features.

2. Architectuur

Dubbo onderscheidt een aantal rollen:

  1. Provider - waar service wordt getoond; een provider registreert zijn service in het register
  2. Container - waar de service wordt gestart, geladen en uitgevoerd
  3. Consument - die een beroep doet op diensten op afstand; een consument zal zich abonneren op de service die nodig is in het register
  4. Register - waar de service wordt geregistreerd en ontdekt
  5. Monitor - registreer statistieken voor services, bijvoorbeeld de frequentie van het aanroepen van services in een bepaald tijdsinterval

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

Verbindingen tussen een provider, een consument en een register zijn blijvend, dus wanneer een serviceprovider niet beschikbaar is, kan het register de storing detecteren en de consumenten op de hoogte stellen.

Het register en de monitor zijn optioneel. Consumenten zouden rechtstreeks verbinding kunnen maken met dienstverleners, maar de stabiliteit van het hele systeem zou worden aangetast.

3. Maven Afhankelijkheid

Voordat we erin duiken, voegen we de volgende afhankelijkheid toe aan onze pom.xml:

 com.alibaba dubbo 2.5.7 

De laatste versie vind je hier.

4. Bootstrapping

Laten we nu de basisfuncties van Dubbo uitproberen.

Dit is een minimaal invasief raamwerk en veel van de functies zijn afhankelijk van externe configuraties of annotaties.

Officieel wordt aangeraden om een ​​XML-configuratiebestand te gebruiken omdat dit afhankelijk is van een Spring-container (momenteel Spring 4.3.10).

We zullen de meeste functies demonstreren met behulp van XML-configuratie.

4.1. Multicast-register - Serviceprovider

Om snel te beginnen hebben we alleen een serviceprovider, een consument en een "onzichtbaar" register nodig. Het register is onzichtbaar omdat we een multicast-netwerk gebruiken.

In het volgende voorbeeld zegt de provider alleen "hallo" tegen zijn consumenten:

openbare interface GreetingsService {String sayHi (String naam); } public class GreetingsServiceImpl implementeert GreetingsService {@Override public String sayHi (String name) {return "hi," + name; }}

Om een ​​procedure-oproep op afstand te plaatsen, moet de consument een gemeenschappelijke interface delen met de serviceprovider, dus de interface GroetenService moet worden gedeeld met de consument.

4.2. Multicast-register - Serviceregistratie

Laten we ons nu registreren GroetenService naar het register. Een erg handige manier is om een ​​multicast-registry te gebruiken als zowel providers als consumenten zich op hetzelfde lokale netwerk bevinden:

Met de bovenstaande bonenconfiguratie hebben we zojuist onze GroetenService naar een url onder dubbo: //127.0.0.1: 20880 en registreerde de service op een multicast-adres gespecificeerd in .

In de configuratie van de provider hebben we ook de metadata van onze applicatie, de interface die moet worden gepubliceerd en de implementatie ervan aangegeven door respectievelijk , en .

De dubbo protocol is een van de vele protocollen die het framework ondersteunt. Het is gebouwd bovenop de niet-blokkerende functie van Java NIO en het is het standaard gebruikte protocol.

We zullen het later in dit artikel in meer detail bespreken.

4.3. Multicast-register - Serviceconsument

Over het algemeen moet de consument de aan te roepen interface en het adres van de service op afstand specificeren, en dat is precies wat een consument nodig heeft:

Nu is alles ingesteld, laten we eens kijken hoe ze in actie werken:

openbare klasse MulticastRegistryTest {@Before public void initRemote () {ClassPathXmlApplicationContext remoteContext = nieuwe ClassPathXmlApplicationContext ("multicast / provider-app.xml"); remoteContext.start (); } @Test openbare ongeldig gegevenProvider_whenConsumerSaysHi_thenGotResponse () {ClassPathXmlApplicationContext localContext = nieuwe ClassPathXmlApplicationContext ("multicast / consumer-app.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); String hiMessage = greetingsService.sayHi ("baeldung"); assertNotNull (hiMessage); assertEquals ("hi, baeldung", hiMessage); }}

Wanneer de provider remoteContext start, wordt Dubbo automatisch geladen GroetenService en registreer het in een bepaald register. In dit geval is het een multicast-register.

De consument abonneert zich op het multicast-register en maakt een proxy van GroetenService in de context. Wanneer onze lokale klant het zeg gedag methode, het op transparante wijze een service op afstand aanroept.

We hebben vermeld dat het register optioneel is, wat betekent dat de consument rechtstreeks verbinding kan maken met de provider, via de blootgestelde poort:

In principe is de procedure vergelijkbaar met traditionele webservices, maar Dubbo maakt het eenvoudig, eenvoudig en lichtgewicht.

4.4. Eenvoudig register

Merk op dat wanneer u een "onzichtbaar" multicast-register gebruikt, de registerservice niet op zichzelf staat. Het is echter alleen van toepassing op een beperkt lokaal netwerk.

Om expliciet een beheersbaar register op te zetten, kunnen we een SimpleRegistryService.

Nadat de volgende bonenconfiguratie in Spring-context is geladen, wordt een eenvoudige registerservice gestart:

Merk op dat de SimpleRegistryService class is niet opgenomen in het artefact, dus hebben we de broncode rechtstreeks uit de Github-repository gekopieerd.

Vervolgens passen we de registerconfiguratie van de provider en consument aan:

SimpleRegistryService kan worden gebruikt als een op zichzelf staand register tijdens het testen, maar het wordt niet aanbevolen om te worden gebruikt in een productieomgeving.

4.5. Java-configuratie

Configuratie via Java API, eigenschappenbestand en annotaties worden ook ondersteund. Eigenschappenbestand en annotaties zijn echter alleen van toepassing als onze architectuur niet erg complex is.

Laten we eens kijken hoe onze eerdere XML-configuraties voor multicast-registry kunnen worden vertaald in API-configuratie. Ten eerste is de provider als volgt opgezet:

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

Nu de service al beschikbaar is via het multicast-register, kunnen we deze gebruiken in een lokale client:

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

Hoewel het bovenstaande fragment als een charme werkt als het vorige XML-configuratievoorbeeld, is het iets trivialer. XML-configuratie zou voorlopig de eerste keuze moeten zijn als we van plan zijn Dubbo volledig te benutten.

5. Protocolondersteuning

Het framework ondersteunt meerdere protocollen, waaronder dubbo, RMI, jute, HTTP, webservice, spaarzaamheid, memcached en redis. De meeste protocollen zien er bekend uit, behalve dubbo. Laten we eens kijken wat er nieuw is in dit protocol.

De dubbo protocol zorgt voor een blijvende verbinding tussen providers en consumenten. De lange verbinding en de niet-blokkerende NIO-netwerkcommunicatie resulteren in redelijk goede prestaties bij het verzenden van kleinschalige datapakketten (<100K).

Er zijn verschillende configureerbare eigenschappen, zoals poort, aantal verbindingen per gebruiker, maximaal geaccepteerde verbindingen, etc.

Dubbo ondersteunt ook het aanbieden van services via verschillende protocollen tegelijk:

En ja, we kunnen verschillende services tonen met verschillende protocollen, zoals weergegeven in het bovenstaande fragment. De onderliggende transporters, serialisatie-implementaties en andere algemene eigenschappen met betrekking tot netwerken zijn ook configureerbaar.

6. Resultaat caching

Native caching van resultaten op afstand wordt ondersteund om de toegang tot hot data te versnellen. Het is net zo eenvoudig als het toevoegen van een cachekenmerk aan de bean-referentie:

Hier hebben we een minst recent gebruikte cache geconfigureerd. Om het cachegedrag te verifiëren, zullen we een beetje veranderen in de vorige standaardimplementatie (laten we het "speciale implementatie" noemen):

openbare klasse GreetingsServiceSpecialImpl implementeert GreetingsService {@Override openbare tekenreeks sayHi (tekenreeksnaam) {probeer {SECONDS.sleep (5); } catch (uitzondering genegeerd) {} ​​return "hi", + naam; }}

Na het opstarten van de provider kunnen we aan de kant van de consument verifiëren dat het resultaat in de cache wordt opgeslagen bij meer dan eens aanroepen:

@Test openbare ongeldig gegevenProvider_whenConsumerSaysHi_thenGotResponse () {ClassPathXmlApplicationContext localContext = nieuwe ClassPathXmlApplicationContext ("multicast / consumer-app.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); lang ervoor = System.currentTimeMillis (); String hiMessage = greetingsService.sayHi ("baeldung"); long timeElapsed = System.currentTimeMillis () - eerder; assertTrue (timeElapsed> 5000); assertNotNull (hiMessage); assertEquals ("hi, baeldung", hiMessage); before = System.currentTimeMillis (); hiMessage = greetingsService.sayHi ("baeldung"); timeElapsed = System.currentTimeMillis () - voor; assertTrue (timeElapsed <1000); assertNotNull (hiMessage); assertEquals ("hi, baeldung", hiMessage); }

Hier roept de consument de implementatie van de speciale service aan, dus het duurde meer dan 5 seconden voordat de aanroep de eerste keer was voltooid. Wanneer we opnieuw een beroep doen, wordt de zeg gedag methode wordt bijna onmiddellijk voltooid, omdat het resultaat wordt geretourneerd uit de cache.

Merk op dat thread-local cache en JCache ook worden ondersteund.

7. Clusterondersteuning

Dubbo helpt ons onze services vrijelijk op te schalen met de mogelijkheid van load balancing en verschillende fouttolerantiestrategieën. Laten we hier aannemen dat we Zookeeper als ons register hebben om services in een cluster te beheren. Providers kunnen hun diensten als volgt in Zookeeper registreren:

Merk op dat we deze aanvullende afhankelijkheden nodig hebben in het POM:

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

De nieuwste versies van dierentuinmedewerker afhankelijkheid en zkclient vind je hier en hier.

7.1. Load Balancing

Momenteel ondersteunt het framework een aantal load-balancing-strategieën:

  • willekeurig
  • round-robin
  • minst actief
  • consistente hash.

In het volgende voorbeeld hebben we twee service-implementaties als providers in een cluster. De verzoeken worden gerouteerd met behulp van de round-robin-benadering.

Laten we eerst serviceproviders instellen:

@Before public void initRemote () {ExecutorService executorService = Executors.newFixedThreadPool (2); executorService.submit (() -> {ClassPathXmlApplicationContext remoteContext = nieuwe ClassPathXmlApplicationContext ("cluster / provider-app-default.xml"); remoteContext.start ();}); executorService.submit (() -> {ClassPathXmlApplicationContext backupRemoteContext = nieuw ClassPathXmlApplicationContext ("cluster / provider-app-special.xml"); backupRemoteContext.start ();}); }

Nu hebben we een standaard "snelle provider" die direct reageert, en een speciale "trage provider" die op elk verzoek 5 seconden slaapt.

Na 6 keer te hebben gelopen met de round-robin-strategie, verwachten we dat de gemiddelde responstijd minimaal 2,5 seconden zal zijn:

@Test openbare ongeldig gegevenProviderCluster_whenConsumerSaysHi_thenResponseBalanced () {ClassPathXmlApplicationContext localContext = nieuwe ClassPathXmlApplicationContext ("cluster / consumer-app-lb.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); Lijst elapseList = nieuwe ArrayList (6); voor (int i = 0; i e) .gemiddelde (); assertTrue (avgElapse.isPresent ()); assertTrue (avgElapse.getAsDouble ()> 2500.0); }

Bovendien wordt dynamische load balancing toegepast. Het volgende voorbeeld laat zien dat de consument bij round-robin-strategie automatisch de nieuwe dienstverlener als kandidaat kiest wanneer de nieuwe aanbieder online komt.

De "langzame provider" wordt 2 seconden later geregistreerd nadat het systeem is opgestart:

@Before public void initRemote () {ExecutorService executorService = Executors.newFixedThreadPool (2); executorService.submit (() -> {ClassPathXmlApplicationContext remoteContext = nieuwe ClassPathXmlApplicationContext ("cluster / provider-app-default.xml"); remoteContext.start ();}); executorService.submit (() -> {SECONDS.sleep (2); ClassPathXmlApplicationContext backupRemoteContext = nieuw ClassPathXmlApplicationContext ("cluster / provider-app-special.xml"); backupRemoteContext.start (); retourneer null;}); }

De consument roept de service op afstand eenmaal per seconde op. Na 6 keer hardlopen verwachten we dat de gemiddelde responstijd meer dan 1,6 seconden zal zijn:

@Test openbare leegte gegevenProviderCluster_whenConsumerSaysHi_thenResponseBalanced () gooit InterruptedException {ClassPathXmlApplicationContext localContext = nieuwe ClassPathXmlApplicationContext ("cluster / consumer-app-lb.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); Lijst elapseList = nieuwe ArrayList (6); voor (int i = 0; i e) .gemiddelde (); assertTrue (avgElapse.isPresent ()); assertTrue (avgElapse.getAsDouble ()> 1666.0); }

Merk op dat de load balancer zowel aan de kant van de consument als aan de kant van de provider kan worden geconfigureerd. Hier is een voorbeeld van een configuratie aan de consumentzijde:

7.2. Fouttolerantie

Verschillende fouttolerantiestrategieën worden ondersteund in Dubbo, waaronder:

  • fail-over
  • faalveilig
  • falen snel
  • fail-back
  • vorken.

In het geval van een fail-over, wanneer een provider uitvalt, kan de consument het proberen met een aantal andere serviceproviders in het cluster.

De fouttolerantiestrategieën zijn als volgt geconfigureerd voor serviceproviders:

Laten we, om service-fail-over in actie te demonstreren, een fail-overimplementatie maken van GroetenService:

public class GreetingsFailoverServiceImpl implementeert GreetingsService {@Override public String sayHi (String naam) {return "hi, failover" + naam; }}

We kunnen ons herinneren dat onze speciale service-implementatie GroetenServiceSpecialImpl slaapt 5 seconden voor elk verzoek.

Wanneer een reactie die meer dan 2 seconden duurt, wordt gezien als een mislukte aanvraag voor de consument, hebben we een failover-scenario:

Nadat we twee providers hebben gestart, kunnen we het failover-gedrag verifiëren met het volgende fragment:

@Test public void whenConsumerSaysHi_thenGotFailoverResponse () {ClassPathXmlApplicationContext localContext = nieuw ClassPathXmlApplicationContext ("cluster / consumer-app-failtest.xml"); localContext.start (); GreetingsService greetingsService = (GreetingsService) localContext.getBean ("greetingsService"); String hiMessage = greetingsService.sayHi ("baeldung"); assertNotNull (hiMessage); assertEquals ("hi, failover baeldung", hiMessage); }

8. Samenvatting

In deze tutorial hebben we een klein hapje Dubbo genomen. De meeste gebruikers worden aangetrokken door zijn eenvoud en rijke en krachtige functies.

Afgezien van wat we in dit artikel hebben geïntroduceerd, heeft het raamwerk een aantal functies die nog moeten worden onderzocht, zoals parametervalidatie, melding en terugbellen, algemene implementatie en verwijzing, groeperen en samenvoegen van resultaten op afstand, service-upgrade en achterwaartse compatibiliteit, om maar te noemen een paar.

Zoals altijd is de volledige implementatie te vinden op Github.