Pengantar Dubbo

1. Perkenalan

Dubbo adalah RPC open-source dan kerangka kerja layanan mikro dari Alibaba.

Antara lain, ini membantu meningkatkan tata kelola layanan dan memungkinkan aplikasi monolit tradisional untuk direfraktor dengan lancar ke arsitektur terdistribusi yang dapat diskalakan.

Pada artikel ini, kami akan memperkenalkan Dubbo dan fitur-fiturnya yang paling penting.

2. Arsitektur

Dubbo membedakan beberapa peran:

  1. Penyedia - di mana layanan diekspos; penyedia akan mendaftarkan layanannya ke registri
  2. Penampung - tempat layanan dimulai, dimuat, dan dijalankan
  3. Konsumen - yang meminta layanan jarak jauh; konsumen akan berlangganan layanan yang diperlukan dalam registri
  4. Registri - tempat layanan akan didaftarkan dan ditemukan
  5. Monitor - merekam statistik untuk layanan, misalnya, frekuensi permintaan layanan dalam interval waktu tertentu

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

Koneksi antara penyedia, konsumen, dan registri tetap ada, jadi setiap kali penyedia layanan tidak aktif, registri dapat mendeteksi kegagalan dan memberi tahu konsumen.

Registri dan monitor bersifat opsional. Konsumen dapat terhubung langsung ke penyedia layanan, tetapi stabilitas keseluruhan sistem akan terpengaruh.

3. Ketergantungan Maven

Sebelum kita menyelami, mari tambahkan ketergantungan berikut ke pom.xml kita :

 com.alibaba dubbo 2.5.7 

Versi terbaru dapat ditemukan di sini.

4. Bootstrap

Sekarang mari kita coba fitur dasar Dubbo.

Ini adalah kerangka kerja invasif minimal, dan banyak fiturnya bergantung pada konfigurasi atau anotasi eksternal.

Secara resmi disarankan agar kita menggunakan file konfigurasi XML karena bergantung pada wadah Spring (saat ini Spring 4.3.10).

Kami akan mendemonstrasikan sebagian besar fiturnya menggunakan konfigurasi XML.

4.1. Multicast Registry - Penyedia Layanan

Sebagai permulaan cepat, kita hanya membutuhkan penyedia layanan, konsumen, dan registri "tak terlihat". Registri tidak terlihat karena kami menggunakan jaringan multicast.

Dalam contoh berikut, penyedia hanya mengatakan "hai" kepada konsumennya:

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

Untuk melakukan panggilan prosedur jarak jauh, konsumen harus berbagi antarmuka yang sama dengan penyedia layanan, sehingga antarmuka GreetingsService harus dibagikan dengan konsumen.

4.2. Multicast Registry - Pendaftaran Layanan

Sekarang mari mendaftarkan GreetingsService ke registri. Cara yang sangat mudah adalah dengan menggunakan registri multicast jika penyedia dan konsumen berada di jaringan lokal yang sama:

Dengan konfigurasi beans di atas, kita baru saja mengekspos GreetingsService ke url di bawah dubbo: //127.0.0.1: 20880 dan mendaftarkan layanan ke alamat multicast yang ditentukan di.

Dalam konfigurasi penyedia, kami juga mendeklarasikan metadata aplikasi kami, antarmuka yang akan diterbitkan dan implementasinya masing-masing , dan .

The dubbo protokol adalah salah satu dari banyak protokol mendukung framework. Itu dibangun di atas fitur non-pemblokiran Java NIO dan itu adalah protokol default yang digunakan.

Kami akan membahasnya lebih detail nanti di artikel ini.

4.3. Multicast Registry - Konsumen Layanan

Umumnya, konsumen perlu menentukan antarmuka yang akan dipanggil dan alamat layanan jarak jauh, dan itulah yang dibutuhkan oleh konsumen:

Sekarang semuanya sudah siap, mari kita lihat cara kerjanya:

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

Ketika remoteContext penyedia dimulai, Dubbo akan secara otomatis memuat GreetingsService dan mendaftarkannya ke registri tertentu. Dalam hal ini, ini adalah registri multicast.

Konsumen berlangganan ke registri multicast dan membuat proxy GreetingsService dalam konteksnya. Ketika klien lokal kami memanggil metode sayHi , itu secara transparan memanggil layanan jarak jauh.

Kami menyebutkan bahwa registri bersifat opsional, artinya konsumen dapat terhubung langsung ke penyedia, melalui port yang terbuka:

Pada dasarnya, prosedurnya mirip dengan layanan web tradisional, tetapi Dubbo membuatnya sederhana, sederhana dan ringan.

4.4. Registry Sederhana

Perhatikan bahwa saat menggunakan registri multicast "tidak terlihat", layanan registri tidak berdiri sendiri. Namun, ini hanya berlaku untuk jaringan lokal yang dibatasi.

Untuk secara eksplisit mengatur registri yang dapat dikelola, kita dapat menggunakan SimpleRegistryService .

Setelah memuat konfigurasi kacang berikut ke dalam konteks Spring, layanan registri sederhana dimulai:

Perhatikan bahwa kelas SimpleRegistryService tidak terdapat dalam artefak, jadi kami menyalin kode sumber langsung dari repositori Github.

Then we shall adjust the registry configuration of the provider and consumer:

SimpleRegistryService can be used as a standalone registry when testing, but it is not advised to be used in production environment.

4.5. Java Configuration

Configuration via Java API, property file, and annotations are also supported. However, property file and annotations are only applicable if our architecture isn't very complex.

Let's see how our previous XML configurations for multicast registry can be translated into API configuration. First, the provider is set up as follows:

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

Now that the service is already exposed via the multicast registry, let's consume it in a local client:

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

Though the snippet above works like a charm as the previous XML configuration example, it is a little more trivial. For the time being, XML configuration should be the first choice if we intend to make full use of Dubbo.

5. Protocol Support

The framework supports multiple protocols, including dubbo, RMI, hessian, HTTP, web service, thrift, memcached and redis. Most of the protocols looks familiar, except for dubbo. Let's see what's new in this protocol.

The dubbo protocol keeps a persistent connection between providers and consumers. The long connection and NIO non-blocking network communication result in a fairly great performance while transmitting small-scale data packets (<100K).

There are several configurable properties, such as port, number of connections per consumer, maximum accepted connections, etc.

Dubbo also supports exposing services via different protocols all at once:

And yes, we can expose different services using different protocols, as shown in the snippet above. The underlying transporters, serialization implementations and other common properties relating to networking are configurable as well.

6. Result Caching

Natively remote result caching is supported to speed up access to hot data. It's as simple as adding a cache attribute to the bean reference:

Here we configured a least-recently-used cache. To verify the caching behavior, we'll change a bit in the previous standard implementation (let's call it “special implementation”):

public class GreetingsServiceSpecialImpl implements GreetingsService { @Override public String sayHi(String name) { try { SECONDS.sleep(5); } catch (Exception ignored) { } return "hi, " + name; } }

After starting up provider, we can verify on the consumer's side, that the result is cached when invoking more than once:

@Test public void givenProvider_whenConsumerSaysHi_thenGotResponse() { ClassPathXmlApplicationContext localContext = new ClassPathXmlApplicationContext("multicast/consumer-app.xml"); localContext.start(); GreetingsService greetingsService = (GreetingsService) localContext.getBean("greetingsService"); long before = System.currentTimeMillis(); String hiMessage = greetingsService.sayHi("baeldung"); long timeElapsed = System.currentTimeMillis() - before; assertTrue(timeElapsed > 5000); assertNotNull(hiMessage); assertEquals("hi, baeldung", hiMessage); before = System.currentTimeMillis(); hiMessage = greetingsService.sayHi("baeldung"); timeElapsed = System.currentTimeMillis() - before; assertTrue(timeElapsed < 1000); assertNotNull(hiMessage); assertEquals("hi, baeldung", hiMessage); }

Here the consumer is invoking the special service implementation, so it took more than 5 seconds for the invocation to complete the first time. When we invoke again, the sayHi method completes almost immediately, as the result is returned from the cache.

Note that thread-local cache and JCache are also supported.

7. Cluster Support

Dubbo helps us scale up our services freely with its ability of load balancing and several fault tolerance strategies. Here, let's assume we have Zookeeper as our registry to manage services in a cluster. Providers can register their services in Zookeeper like this:

Note that we need these additional dependencies in the POM:

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

The latest versions of zookeeper dependency and zkclient can be found here and here.

7.1. Load Balancing

Currently, the framework supports a few load-balancing strategies:

  • random
  • round-robin
  • least-active
  • consistent-hash.

In the following example, we have two service implementations as providers in a cluster. The requests are routed using the round-robin approach.

First, let's set up service providers:

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

Now we have a standard “fast provider” that responds immediately, and a special “slow provider” who sleeps for 5 seconds on every request.

After running 6 times with the round-robin strategy, we expect the average response time to be at least 2.5 seconds:

@Test public void givenProviderCluster_whenConsumerSaysHi_thenResponseBalanced() { ClassPathXmlApplicationContext localContext = new ClassPathXmlApplicationContext("cluster/consumer-app-lb.xml"); localContext.start(); GreetingsService greetingsService = (GreetingsService) localContext.getBean("greetingsService"); List elapseList = new ArrayList(6); for (int i = 0; i  e) .average(); assertTrue(avgElapse.isPresent()); assertTrue(avgElapse.getAsDouble() > 2500.0); }

Moreover, dynamic load balancing is adopted. The next example demonstrates that, with round-robin strategy, the consumer automatically chooses the new service provider as a candidate when the new provider comes online.

The “slow provider” is registered 2 seconds later after the system starts:

@Before public void 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 = new ClassPathXmlApplicationContext("cluster/provider-app-special.xml"); backupRemoteContext.start(); return null; }); }

The consumer invokes the remote service once per second. After running 6 times, we expect the average response time to be greater than 1.6 seconds:

@Test public void givenProviderCluster_whenConsumerSaysHi_thenResponseBalanced() throws InterruptedException { ClassPathXmlApplicationContext localContext = new ClassPathXmlApplicationContext("cluster/consumer-app-lb.xml"); localContext.start(); GreetingsService greetingsService = (GreetingsService) localContext.getBean("greetingsService"); List elapseList = new ArrayList(6); for (int i = 0; i  e) .average(); assertTrue(avgElapse.isPresent()); assertTrue(avgElapse.getAsDouble() > 1666.0); }

Note that the load balancer can be configured both on the consumer's side and on the provider's side. Here's an example of consumer-side configuration:

7.2. Fault Tolerance

Several fault tolerance strategies are supported in Dubbo, including:

  • fail-over
  • fail-safe
  • fail-fast
  • fail-back
  • forking.

In the case of fail-over, when one provider fails, the consumer can try with some other service providers in the cluster.

The fault tolerance strategies are configured like the following for service providers:

To demonstrate service fail-over in action, let's create a fail-over implementation of GreetingsService:

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

We can recall that our special service implementation GreetingsServiceSpecialImpl sleeps 5 seconds for each request.

When any response that takes more than 2 seconds is seen as a request failure for the consumer, we have a fail-over scenario:

After starting two providers, we can verify the fail-over behavior with the following snippet:

@Test public void whenConsumerSaysHi_thenGotFailoverResponse() { ClassPathXmlApplicationContext localContext = new 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. Summary

Dalam tutorial ini, kami mencoba Dubbo. Sebagian besar pengguna tertarik dengan kesederhanaan dan fitur-fiturnya yang kaya dan kuat.

Selain dari apa yang kami perkenalkan di artikel ini, kerangka kerja memiliki sejumlah fitur yang belum dieksplorasi, seperti validasi parameter, notifikasi dan panggilan balik, implementasi dan referensi umum, pengelompokan dan penggabungan hasil jarak jauh, peningkatan layanan dan kompatibilitas mundur, dan lain-lain. Beberapa.

Seperti biasa, implementasi penuh dapat ditemukan di Github.