Dubbo學習1-Hello world


前言

互聯網技術到今天已經非常成熟和穩定了,其中為了解決高並發、大規模的服務請求,出現了微服務、RPC這樣的分布式架構。今天就從頭開始學習RPC框架dubbo。

為什么要學Dubbo

關於分布式的解決方案有很多,光RPC框架就有很多種,甚至有人比較了RPC框架性能, 結果顯示dubbo墊底,以及spring cloud這種推崇Rest Http請求代替RPC, 那為什么還要學習RPC?

首先,RPC框架的發展並沒有因為Spring Cloud的出現而變的衰弱。其次,歷史悠久的市場上大部分公司都會使用RPC來解決分布式問題。

那么,為啥要選Dubbo? 只能說Dubbo在國內的流行程度遠超過gRPC、thrift等,而且中文資料很多,在國內的技術圈混則必須要掌握這個技能。當然,缺點也不少,比如由於官方很長時間沒有維護導致Dubbo各種落后。令人驚喜的是,Alibaba已經開始專注開源項目了,dubbo重新開啟維護,進入Apache Incubating。然而,很多人也因此選擇了其他RPC來代替dubbo。

Dubbo發音問題: da bou; |ˈdʌbəʊ|

什么是RPC

RPC(Remote Procedure Call)表示遠程過程調用。

兩台機器A和B。A部署了首頁的服務,想要獲取訂單信息,訂單服務部署在機器B上。這樣,A通過遠程調用B上的函數(function)來得到信息就屬於遠程過程調用。

以下來自
誰能用通俗的語言解釋一下什么是 RPC 框架?

1.首先,要解決通訊的問題。主要是通過在客戶端和服務器之間建立TCP連接,遠程過程調用的所有交換的數據都在這個連接里傳輸。連接可以是按需連接,調用結束后就斷掉,也可以是長連接,多個遠程調用共享同一個連接。
2.第二,要解決尋址問題。也就是說,A服務器上的應用怎么告訴底層的RPC框架,如何連接到B服務器(如主機或IP地址)以及特定的端口,方法的名稱是什么,或者是從UDDI服務上查找。如果是RMI調用的話,還需要一個RMI Registry來注冊服務的地址。
3.第三,當A服務器上的應用發起遠程過程調用時,方法的參數需要通過底層的網絡協議如TCP傳遞到B服務器,由於網絡協議是基於二進制的,內存中的參數的值要序列化成二進制的形式,也就是序列化(Serialize)或編組(marshal), 通過尋址和傳輸將序列化的二進制發送給B服務器。
4.第四,B服務器收到請求后,需要對參數進行反序列化(序列化的逆操作),恢復為內存中的表達方式,然后找到對應的方法(尋址的一部分)進行本地調用,然后得到返回值。
5.第五,返回值還要發送回服務器A上的應用,也要經過序列化的方式發送,服務器A接到后,再反序列化,恢復為內存中的表達方式,交給A服務器上的應用。

Dubbo的產生背景

以下來自Dubbo官方文檔

隨着互聯網的發展,網站應用的規模不斷擴大,常規的垂直應用架構已無法應對,分布式服務架構以及流動計算架構勢在必行,亟需一個治理系統確保架構有條不紊的演進。

單一應用架構

當網站流量很小時,只需一個應用,將所有功能都部署在一起,以減少部署節點和成本。此時,用於簡化增刪改查工作量的數據訪問框架(ORM)是關鍵。

垂直應用架構

當訪問量逐漸增大,單一應用增加機器帶來的加速度越來越小,將應用拆成互不相干的幾個應用,以提升效率。此時,用於加速前端頁面開發的Web框架(MVC)是關鍵。

分布式服務架構

當垂直應用越來越多,應用之間交互不可避免,將核心業務抽取出來,作為獨立的服務,逐漸形成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。此時,用於提高業務復用及整合的分布式服務框架(RPC)是關鍵。

流動計算架構

當服務越來越多,容量的評估,小服務資源的浪費等問題逐漸顯現,此時需增加一個調度中心基於訪問壓力實時管理集群容量,提高集群利用率。此時,用於提高機器利用率的資源調度和治理中心(SOA)是關鍵。

Dubbo面臨的需求

在大規模服務化之前,應用可能只是通過 RMI 或 Hessian 等工具,簡單的暴露和引用遠程服務,通過配置服務的URL地址進行調用,通過 F5 等硬件進行負載均衡。

當服務越來越多時,服務 URL 配置管理變得非常困難,F5 硬件負載均衡器的單點壓力也越來越大。 此時需要一個服務注冊中心,動態的注冊和發現服務,使服務的位置透明。並通過在消費方獲取服務提供方地址列表,實現軟負載均衡和 Failover,降低對 F5 硬件負載均衡器的依賴,也能減少部分成本。

當進一步發展,服務間依賴關系變得錯蹤復雜,甚至分不清哪個應用要在哪個應用之前啟動,架構師都不能完整的描述應用的架構關系。 這時,需要自動畫出應用間的依賴關系圖,以幫助架構師理清理關系。

接着,服務的調用量越來越大,服務的容量問題就暴露出來,這個服務需要多少機器支撐?什么時候該加機器? 為了解決這些問題,第一步,要將服務現在每天的調用量,響應時間,都統計出來,作為容量規划的參考指標。其次,要可以動態調整權重,在線上,將某台機器的權重一直加大,並在加大的過程中記錄響應時間的變化,直到響應時間到達閥值,記錄此時的訪問量,再以此訪問量乘以機器數反推總容量。

以上是 Dubbo 最基本的幾個需求。

Dubbo的架構

節點角色說明

節點 角色說明
Provider 暴露服務的服務提供方
Consumer 調用遠程服務的服務消費方
Registry 服務注冊與發現的注冊中心
Monitor 統計服務的調用次數和調用時間的監控中心
Container 服務運行容器

調用關系說明

1.服務容器負責啟動,加載,運行服務提供者。
2.服務提供者在啟動時,向注冊中心注冊自己提供的服務。
3.服務消費者在啟動時,向注冊中心訂閱自己所需的服務。
4.注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基於長連接推送變更數據給消費者。
5.服務消費者,從提供者地址列表中,基於軟負載均衡算法,選一台提供者進行調用,如果調用失敗,再選另一台調用。
6.服務消費者和提供者,在內存中累計調用次數和調用時間,定時每分鍾發送一次統計數據到監控中心。

Dubbo 架構具有以下幾個特點,分別是連通性、健壯性、伸縮性、以及向未來架構的升級性。

Demo time

首先,確認本次的目標。學習一項技術首先要了解背景以及意義。前文摘自官網的內容已經給出了答案。然后就是一個Hello World, 給學習者一個信心:這東西很容易上手的。So,接下來的demo依舊來自官方,特別簡單的一個模擬運行。后面再繼續深入用法。

由於Maven在之前極盛行,demo仍舊用maven當作構建工具,當然,后面熟悉后可切換為gradle。

本次demo見 https://github.com/Ryan-Miao/dubbo-hello-demo

新建一個maven項目

在idea里新建空的maven項目。

File->new->project->maven

填寫group和artifact。


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.test</groupId>
    <artifactId>dubbo-hello-demo</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <repositories>
        <repository>
            <id>central</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <name>aliyun</name>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>central</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <name>aliyun</name>
        </pluginRepository>
    </pluginRepositories>

</project>

提供服務的API

通過上述的介紹也能理解,消費者需要知道提供者的接口,那么就一定要共享接口和參數以及響應類。所以,我們先抽離一個單獨的API項目來專門聲明API。

在項目名上右鍵,new -> moudle, 選擇maven,新建一個新的空maven項目。

然后,創建一個用戶服務接口com.test.hello.api.IUserService

package com.test.hello.api;

public interface IUserService {
    String sayHi(String name);
}

這次的主角是dubbo,所以就不填充參數和包結構整理了。簡單傳入參數String和返回String就好。

提供服務的provider

剛才聲明了一個服務的接口,我們還需要一個實現,來提供真實的服務。而這個服務則是一個獨立的項目。我們可以部署在某個機器上。

在項目名上右鍵,new -> module, 選擇Spring Initializr, 然后只選擇web就可以了。最終生成的pom如下

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.test</groupId>
	<artifactId>hello-provider</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>hello-provider</name>
	<description>Demo for dubbo provider hello world</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<dubbo.version>2.6.1</dubbo.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>dubbo</artifactId>
			<version>${dubbo.version}</version>
		</dependency>

		<dependency>
			<groupId>com.test</groupId>
			<artifactId>hello-api</artifactId>
			<version>1.0-SNAPSHOT</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

當然,手動添加dubbo依賴

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>dubbo</artifactId>
	<version>${dubbo.version}</version>
</dependency>

還要引入我們剛才定義的共享接口定義項目:

<dependency>
	<groupId>com.test</groupId>
	<artifactId>hello-api</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>

接下來,就是實現這個接口了。創建實現類com.test.helloprovider.service.impl.UserService

package com.test.helloprovider.service.impl;


import com.test.hello.api.IUserService;

import java.util.Date;

public class UserService implements IUserService {
    @Override
    public String sayHi(String name) {
        return "Hi " + name + ", current date is: " + new Date();
    }
}

然后,只要把這個服務發布出去就行了。在spring cloud里,發布一個服務就是發布web項目,啟動tomcat來提供一個rest api。而在這里,我們不需要。我們要做的是把自己注冊到dubbo里,等消費者來拿。

在resource下新建provider.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 提供方應用信息,用於計算依賴關系 -->
    <dubbo:application name="hello-provider"  />

    <!-- 使用multicast廣播注冊中心暴露服務地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234" />

    <!-- 用dubbo協議在20880端口暴露服務 -->
    <dubbo:protocol name="dubbo" port="20880" />

    <!-- 聲明需要暴露的服務接口 -->
    <dubbo:service interface="com.test.hello.api.IUserService" ref="userService" />

    <!-- 和本地bean一樣實現服務 -->
    <bean id="userService" class="com.test.helloprovider.service.impl.UserService" />
</beans>

然后,我們只要啟動Spring容器就行了,我們的bean還是聲明在spring里的。

創建一個啟動類com.test.helloprovider.HelloProviderApplication

package com.test.helloprovider;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.io.IOException;

public class HelloProviderApplication {

	public static void main(String[] args) throws IOException {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
				new String[] {"provider.xml"});
		context.start();
		System.in.read(); // 按任意鍵退出
	}
}

運行main方法即可。

這里的注冊中心只是簡單的multicast組播。這樣,我們就發布了一個用戶服務,提供sayHi方法。接下來只要給消費者調用就行。

消費服務的consumer

同樣的步驟,我們來建立一個consumer。這個consumer是另一個獨立的項目,這個項目也可以部署在另一個機器上。這個項目需要調用provider的項目來獲得用戶信息。

和provider不同的是,項目的artifact不一樣。

新建好之后就可以在spring容器里遠程調用了。同樣在resource下新建一個consumer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <!-- 消費方應用名,用於計算依賴關系,不是匹配條件,不要與提供方一樣 -->
    <dubbo:application name="hello-consumer"  />

    <!-- 使用multicast廣播注冊中心暴露發現服務地址 -->
    <dubbo:registry address="multicast://224.5.6.7:1234" />

    <!-- 生成遠程服務代理,可以和本地bean一樣使用demoService -->
    <dubbo:reference id="userService" interface="com.test.hello.api.IUserService" />
</beans>

然后,調用我們的bean完成消費。創建啟動類


public class HelloConsumerApplication {

    public static void main(String[] args) {
        final ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                new String[]{"consumer.xml"});
        context.start();
        final IUserService userService = context.getBean("userService", IUserService.class); // 獲取遠程服務代理
        String hello = userService.sayHi("world"); // 執行遠程方法
        System.out.println("===================================");
        System.out.println(hello); // 顯示調用結果
        System.out.println("===================================");
    }
}

運行main方法可以看到控制台打印內容


22:43:38.034 [main] INFO com.alibaba.dubbo.config.AbstractConfig -  [DUBBO] Refer dubbo service com.test.hello.api.IUserService from url multicast://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?anyhost=true&application=consumer-of-helloworld-app&check=false&dubbo=2.6.1&generic=false&interface=com.test.hello.api.IUserService&methods=sayHi&pid=6204&register.ip=192.168.0.108&remote.timestamp=1521902609769&side=consumer&timestamp=1521902617561, dubbo version: 2.6.1, current host: 192.168.0.108
22:43:38.206 [DubboClientHandler-192.168.0.108:20880-thread-1] DEBUG com.alibaba.dubbo.remoting.transport.DecodeHandler -  [DUBBO] Decode decodeable message com.alibaba.dubbo.rpc.protocol.dubbo.DecodeableRpcResult, dubbo version: 2.6.1, current host: 192.168.0.108
===================================
Hi world, current date is: Sat Mar 24 22:43:38 CST 2018
===================================
22:43:38.207 [DubboShutdownHook] INFO com.alibaba.dubbo.config.AbstractConfig -  [DUBBO] Run shutdown hook now., dubbo version: 2.6.1, current host: 192.168.0.108
22:43:38.208 [DubboShutdownHook] INFO com.alibaba.dubbo.registry.support.AbstractRegistryFactory -  [DUBBO] Close all registries [multicast://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?application=consumer-of-helloworld-app&dubbo=2.6.1&interface=com.alibaba.dubbo.registry.RegistryService&pid=6204&timestamp=1521902617639], dubbo version: 2.6.1, current host: 192.168.0.108
22:43:38.208 [DubboShutdownHook] INFO com.alibaba.dubbo.registry.multicast.MulticastRegistry -  [DUBBO] Destroy registry:multicast://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?application=consumer-of-helloworld-app&dubbo=2.6.1&interface=com.alibaba.dubbo.registry.RegistryService&pid=6204&timestamp=1521902617639, dubbo version: 2.6.1, current host: 192.168.0.108
22:43:38.208 [DubboShutdownHook] INFO com.alibaba.dubbo.registry.multicast.MulticastRegistry -  [DUBBO] Unregister: consumer://192.168.0.108/com.test.hello.api.IUserService?application=consumer-of-helloworld-app&category=consumers&check=false&dubbo=2.6.1&interface=com.test.hello.api.IUserService&methods=sayHi&pid=6204&side=consumer&timestamp=1521902617561, dubbo version: 2.6.1, current host: 192.168.0.108
22:43:38.208 [DubboShutdownHook] INFO com.alibaba.dubbo.registry.multicast.MulticastRegistry -  [DUBBO] Send broadcast message: unregister consumer://192.168.0.108/com.test.hello.api.IUserService?application=consumer-of-helloworld-app&category=consumers&check=false&dubbo=2.6.1&interface=com.test.hello.api.IUserService&methods=sayHi&pid=6204&side=consumer&timestamp=1521902617561 to /224.5.6.7:1234, dubbo version: 2.6.1, current host: 192.168.0.108

如此,hello world 完成。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM