dubbo使用和配置講解


 

1. 分布式系統中相關概念

1.1 互聯網項目特點及目標

1.1.1 特點:

  • 用戶多

  • 流量大、並發高

  • 海量數據

  • 易受攻擊

  • 功能繁瑣

  • 變更快

 

1.1.2 指標及相關目標

互聯網項目三高目標:高並發、高可用(99.999%)、高可拓展

其他可伸縮安全性敏捷性

1.2 集群&分布式

集群:很多人一起干,干一樣的事。一個業務模塊,部署在多個服務器上。是一個橫向概念。

分布式:很多人一起干,干不一樣的事。這些不一樣的事,合起來是一件大事。

一個大的業務系統,拆分為多個小的不同的業務模塊,分別部署在各個機器上。是一個縱向的概念。

 

集群和分布式能初步實現互聯網項目的三高目標。

  • 生活中的分布式和集群

  • 開發中的分布式和集群

1.3. 軟件架構的演進過程

 

 

 

 

 

 

1.3.1 工作中如何選用軟件架構

如果要使用並實現微服務的架構,整體的技術成本、人力成本、硬件成本、維護成本都會很高。

  • 公司/項目非常小,非常適合使用單體架構

  • 中型的公司/項目,可以采用垂直/SOA架構

  • 大型的公司/項目(25+人維護開發一個項目),考慮微服務系統架構

 

1.3.2 Dubbo&SpringCloud

Dubbo主要是為了解決SOA架構下,服務治理的中間件。

SpringCloud主要的應用場景就是在微服務的架構之下。

 

 

2. Dubbo簡單概述

2.1 概念

webservice拆分出來分別部署到不同的服務器上,dubbo為了解決遠程調用時候的各種問題的,底層是基於長連接的方式實現RPC。

Dubbo是一個基於Spring開發的小項目,可以和Spring無縫整合。

 

Dubbo是阿里巴巴公司開源的一個高性能、輕量級的 Java RPC 框架。

• 致力於提供高性能和透明化的 RPC 遠程服務調用方案,以及 SOA 服務治理方案。

了解:

2011年,阿里巴巴開源了自己的SOA服務化治理方案的核心框架Dubbo,SOA架構開始在國內流行。做的是橫向拆分。

在開源之前已經在阿里巴巴內部廣泛使用,開源之后,由於他出色的表現,很快被很多大公司使用,eg:當當、網易考拉等等。

dubbo的前身是基於Spring開發的一個項目,所以可以和Spring無縫銜接,中間停更過一段時間,后來又開始更新並捐給了Apache。

最重要的,他有中文版本的文檔,特別友好。通過快速入門體驗dubbo的特點,通過API 文檔可以輕松的學習dubbo中所有的內容。

 

 

2.2 Zookeeper注冊中心

2.2.1 簡單介紹

Zookeeper 是 Apache Hadoop 的子項目,是一個樹型的目錄服務,支持變更推送,適合作為 Dubbo 服務的注冊中心,工業強度較高,可用於生產環境,並推薦使用。對應官網圍擋說明位置:

文檔/Dubbo2.7/用戶文檔/參考手冊/注冊中心參考手冊

 

 

2.2.2 安裝

linuxwindows使用的是同一個安裝包,綠色,解壓可用。

windows下安裝
  1. 解壓,存放在開發軟件目錄(路徑中不要用中文、空格等特殊符號)

  2. conf目錄,復制zoo_sample.cfgzoo.cfg

  3. 修改配置文件中如下兩點:

    # 數據保存路徑,建議在安裝目錄新建一個數據文件夾,並在此 指定
    dataDir=D:\Develop\soft\apache-zookeeper-3.5.6-bin\zkData

    # netty port
    # netty服務(web容器)的端口,建議修改為非8080端口
    admin.serverPort=8888
  4. 雙擊bin目錄下 zkServer.cmd即可啟動,出現如下內容即為啟動成功。默認端口2181。

2.3 Dubbo架構及執行流程

 

 

3 Dubbo快速入門

 

3.1 分模塊編寫單體架構項目

分模塊開發項目,兩個模塊中分別包含web層和service

dubbo-service

dubbo-web

 

3.1.1dubbo-service

pom.xml

  <properties>
       <spring.version>5.1.9.RELEASE</spring.version>
   </properties>

   <dependencies>
       <!-- servlet3.0規范的坐標 -->
       <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>javax.servlet-api</artifactId>
           <version>3.1.0</version>
           <scope>provided</scope>
       </dependency>
       <!--spring的坐標-->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>${spring.version}</version>
       </dependency>
       <!--springmvc的坐標-->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-webmvc</artifactId>
           <version>${spring.version}</version>
       </dependency>

       <!--日志-->
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>1.7.21</version>
       </dependency>
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
           <version>1.7.21</version>
       </dependency>
   </dependencies>

 

applicationcontext.xml

<context:component-scan base-package="com.yuyy.service" />

 

日志配置文件log4j.properties

# 略

 

 

UserServiceImpl及其接口

public interface UserService {
   public String sayHello();
}

@Service
public class UserServiceImpl implements UserService {

   public String sayHello() {
       return "hello dubbo!~";
  }
}

 

3.1.1dubbo-web

pom.xml

    <groupId>com.yuyy</groupId>
   <artifactId>dubbo-web</artifactId>
   <version>1.0-SNAPSHOT</version>
   <packaging>war</packaging>


   <properties>
       <spring.version>5.1.9.RELEASE</spring.version>
   </properties>

   <dependencies>
       <!-- servlet3.0規范的坐標 -->
       <dependency>
           <groupId>javax.servlet</groupId>
           <artifactId>javax.servlet-api</artifactId>
           <version>3.1.0</version>
           <scope>provided</scope>
       </dependency>
       <!--spring的坐標-->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-context</artifactId>
           <version>${spring.version}</version>
       </dependency>
       <!--springmvc的坐標-->
       <dependency>
           <groupId>org.springframework</groupId>
           <artifactId>spring-webmvc</artifactId>
           <version>${spring.version}</version>
       </dependency>

       <!--日志-->
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-api</artifactId>
           <version>1.7.21</version>
       </dependency>
       <dependency>
           <groupId>org.slf4j</groupId>
           <artifactId>slf4j-log4j12</artifactId>
           <version>1.7.21</version>
       </dependency>

       <!--依賴service模塊-->
       <dependency>
           <groupId>com.yuyy</groupId>
           <artifactId>dubbo-service</artifactId>
           <version>1.0-SNAPSHOT</version>
       </dependency>

   </dependencies>


   <build>
       <plugins>
           <!--tomcat插件-->
           <plugin>
               <groupId>org.apache.tomcat.maven</groupId>
               <artifactId>tomcat7-maven-plugin</artifactId>
               <version>2.1</version>
               <configuration>
                   <port>8000</port>
                   <path>/</path>
               </configuration>
           </plugin>
       </plugins>
   </build>

 

springmvc.xml

<mvc:annotation-driven/>
<context:component-scan base-package="com.yuyy.controller"/>

<!--dubbo的配置-->
<!--配置項目的名稱-->
<dubbo:application name="dubbo_web">
       <dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
<!--配置注冊中心的地址-->
<dubbo:registry address="zookeeper://192.168.23.190:2181"/>
<!--配置dubbo包掃描-->
<dubbo:annotation package="com.yuyy.controller"/>

 

web.xml

<servlet>
   <servlet-name>springmvc</servlet-name>
   <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
   <!-- 指定加載的配置文件 ,通過參數contextConfigLocation加載-->
   <init-param>
       <param-name>contextConfigLocation</param-name>
       <param-value>classpath:spring/springmvc.xml</param-value>
   </init-param>
</servlet>

<servlet-mapping>
   <servlet-name>springmvc</servlet-name>
   <url-pattern>*.do</url-pattern>
</servlet-mapping>

UserController.java

@RestController
@RequestMapping("/user")
public class UserController {

    //注入Service
    //@Autowired//本地注入
    private UserService userService;


    @RequestMapping("/sayHello")
    public String sayHello(){
        return userService.sayHello();
    }

}

 

 

 

3.2 改造service成為服務提供者

3.2.1 添加依賴

<properties>
    <dubbo.version>2.7.4.1</dubbo.version>
    <zookeeper.version>4.0.0</zookeeper.version>
</properties>

<!--Dubbo的起步依賴,版本2.7之后統一為org.apache.dubbo -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>${dubbo.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>${zookeeper.version}</version>
</dependency>
-->
<!--ZooKeeper客戶端實現 -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>${zookeeper.version}</version>
</dependency>

 

3.2.2 修改成web項目

修改pompackaing打包方式為war,添加tomcat插件

<packaging>war</packaging>

<!--tomcat插件-->
<plugin>
    <groupId>org.apache.tomcat.maven</groupId>
    <artifactId>tomcat7-maven-plugin</artifactId>
    <version>2.1</version>
    <configuration>
       <!-- 端口一定要和web模塊的端口不同,避免端口沖突 -->
        <port>9000</port>
        <path>/</path>
    </configuration>
</plugin>

 

3.2.3 發布服務

service層服務實現類上修改如下(注意service注解的包,是dubbo的)

import org.apache.dubbo.config.annotation.Service;

@Service//將這個類提供的方法(服務)對外發布。將訪問的地址 ip,端口,路徑注冊到注冊中心中
public class UserServiceImpl implements UserService {

    public String sayHello() {
        return "hello dubbo hello!~";
    }
}

 

 

3.2.4 配置dubbo服務相關配置

spring核心配置文件中配置。命名空間可以讓idea提示引入,但是要注意不要引錯。

<!--<context:component-scan base-package="com.yuyy.service" />-->

<!--dubbo的配置-->
<!--1.配置項目的名稱,唯一-->
<dubbo:application name="dubbo-service"/>
<!--2.配置注冊中心的地址-->
<dubbo:registry address="zookeeper://localhost:2181"/>
<!--3.配置dubbo組件掃描-->
<dubbo:annotation package="com.yuyy.service.impl" />

 

 

 

3.3 改造web成為服務消費者

3.3.1 添加依賴

<properties>
    <dubbo.version>2.7.4.1</dubbo.version>
    <zookeeper.version>4.0.0</zookeeper.version>
</properties>

<!--Dubbo的起步依賴,版本2.7之后統一為rg.apache.dubbo -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>${dubbo.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-framework</artifactId>
    <version>${zookeeper.version}</version>
</dependency>
<!--ZooKeeper客戶端實現 -->
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>${zookeeper.version}</version>
</dependency>

 

3.3.2 添加service層接口

解除了web模塊對本地service模塊的依賴,UserController中還是用到了UserService類型的對象。

所以臨時解決方案是在當前模塊中添加了一個和service模塊中一樣的一個UserService接口:

public interface UserService {
    public String sayHello();
}

 

 

3.3.3 尋找服務

web層服務實現類controller類上修改如下(注意@Reference注解的包,是dubbo的)

@RestController
@RequestMapping("/user")
public class UserController {

    //注入Service
    //@Autowired//本地注入

    /*
        1. 從zookeeper注冊中心獲取userService的訪問url
        2. 進行遠程調用RPC
        3. 將結果封裝為一個代理對象。給變量賦值

     */

    @Reference//遠程注入
    private UserService userService;


    @RequestMapping("/sayHello")
    public String sayHello(){
        return userService.sayHello();
    }

}

 

 

3.3.4 配置dubbo服務相關配置

命名空間可以讓idea提示引入,但是要注意不要引錯。

<!--原有的組件掃描和注解驅動保持不變-->
<mvc:annotation-driven/>
<context:component-scan base-package="com.yuyy.controller"/>

<!--dubbo的配置-->
<!--1.配置項目的名稱,唯一-->
<dubbo:application name="dubbo-web" >
    <dubbo:parameter key="qos.port" value="33333"/>
</dubbo:application>
<!--2.配置注冊中心的地址-->
<dubbo:registry address="zookeeper://192.168.149.135:2181"/>
<!--3.配置dubbo包掃描-->
<dubbo:annotation package="com.yuyy.controller" />

 

3.2.5 QOS服務端口沖突

同一台電腦上啟動多個Dubbo服務(無論是服務提供者,還是消費者),都會啟動一個QOS(quality of Server)服務,占用相同端口22222,修改任意一個即可。

 

 

3.4 抽取共性代碼

服務提供者模塊和服務消費者模塊都使用到了UserService,那就抽取到一個單獨的模塊,並把UserService接口在該模塊中定義,該模塊打包方式是jar(默認值)。

兩個模塊都引入接口模塊的依賴,直接使用其中的接口即可。

 

使用

Dubbo占用的端口

服務端口:20880

qos默認端口:22222(如果啟動了多個dubbo項目,需要修改成不一樣)

zookeeper默認端口:2181

zookeeper中web容器netty的默認端口:8080(建議修改成其他的)

 

 

4. Dubbo-admin

4.1 安裝及啟動

按照文檔安裝即可,網絡不好的情況下,可以使用安裝好的jar包,直接啟動,雙擊bat即可啟動,使用8080端口訪問。

4.2 使用

4.2.1 元數據

我們需要打開我們的提供者provider配置文件加入下面配置

    <!-- 元數據配置 -->
    <dubbo:metadata-report address="zookeeper://localhost:2181" />

重新啟動生產者,再次打開Dubbo-Admin

4.2.2 默認賬密

登錄地址:http://localhost:8080,默認賬密:root/root

 

5. 高級特性

5.1 序列化

對象想要存儲到硬盤持久化保存,或者通過網絡在多個項目間傳輸,需要實現序列化接口(Serializable

/**
 * 注意!!!
 *  將來所有的pojo類都需要實現Serializable接口
 */
public class User implements Serializable {
    private int id;
    private String username;
    private String password;
}

 

服務提供者,添加方法查詢到一個User對象:

@Service//將這個類提供的方法(服務)對外發布。將訪問的地址 ip,端口,路徑注冊到注冊中心中
public class UserServiceImpl implements UserService {

    public User findUserById(int id) {
        //查詢User對象
        User user = new User(1,"zhangsan","123");
        return user;
    }
    public String sayHello() {
        return "hello dubbo hello!~";
    }
}

 

服務消費者,添加一個方法,調用服務消費者對象獲取查詢到的User對象

@RestController
@RequestMapping("/user")
public class UserController {

    @Reference//遠程注入
    private UserService userService;

    @RequestMapping("/sayHello")
    public String sayHello(){
        return userService.sayHello();
    }

    /**
     * 根據id查詢用戶信息
     * @param id
     * @return
     */
    @RequestMapping("/find")
    public User find(int id){
        return userService.findUserById(id);
    }
}

 

問題:SpringMVC響應對象到瀏覽器的時候,User對象為什么不需要序列化?

 

5.2 地址緩存

面試題

如果項目啟動了然后注冊中心掛掉了,會影響整個項目的運行嗎?

不會影響已有的正常服務的調用,但是無法發現並調用新的服務。

 

原理是什么呢?底層原理就是因為地址緩存。

Dubbo服務消費者在第一次調用服務生產者的時候,會從注冊中心獲取服務提供者地址等信息,保存到本地,以后調用則不需要訪問再次注冊中心;但是當原有服務提供者更新信息或者新的服務將無法完成注冊或更新。

 

5.3 超時

本質為了降低consumer服務器壓力,避免帶來非常嚴重的雪崩問題。

要根據實際情況,設置的盡量短一些;根據業務情況盡量控制在5s內。

  1. consumer性能消耗完畢之后可能會產生雪崩。因為消費者請求線程不釋放(不設超時時間,且provider出問題不能給出響應),大量占用。

  2. provider只要接受某個請求之后,就必須把流程執行完,或者過了超時時間記錄一個warn級別的錯誤日志。消費端撤銷了請求也不會影響服務端的執行。所以要定期查看warn日志,避免大量錯誤堆積。

  3. consumer超時時間盡量在5s以內,否則消費端服務器線程壓力會很大,具體實現要根據服務端響應的平均時長設置。

 

@Service或@Reference注解的timeout屬性可以設置超時時間,單位是毫秒,默認值是1000ms。建議在服務提供者端設置超時時間。

retries retry

 

5.4 重試

consumer的請求超時之后,為了保證QOS會重試多次,盡量得到響應數據;默認為2次,最多共訪問3次。

provider出現阻塞響應慢的時候,重試機制會造成provider端請求壓力倍增;而且provider只要接受一個請求之后,就必須把流程執行完,會造成當前服務器壓力倍增,極易造成雪崩。

所以在dubbo優化策略中往往會優化設置重試次數為0;

要多關注前台響應情況和后台providerwarn級別的日志。

//@Service//將該類的對象創建出來,放到Spring的IOC容器中  bean定義
//將這個類提供的方法(服務)對外發布。將訪問的地址 ip,端口,路徑注冊到注冊中心中
@Service(timeout = 3000,retries = 2)//當前服務3秒超時,超時后最多重試2次,一共3次
public class UserServiceImpl implements UserService {

    int i = 1;
    public String sayHello() {
        return "hello dubbo hello!~";
    }


    public User findUserById(int id) {
        System.out.println("服務被調用了:"+i++);
        //查詢User對象
        User user = new User(1,"zhangsan","123");
        //數據庫查詢很慢,查了5秒

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return user;
    }
}

 

 

5.5 多版本管理

灰度發布:當provider發布新的版本,會讓一部分用戶先使用新功能,用戶反饋沒問時,再將所有用戶遷移到新功能。

dubbo 中使用version 屬性來設置和調用同一個接口的不同版本。

A消費者

public class UserController1 {
	@Reference(version="1.0")
    private UserService userService;
}

 

B消費者

public class UserController2 {
	@Reference(version="2.0")
    private UserService userService;
}

 

生產者版本1

@Service(version = "v1.0")
public class UserServiceImpl implements UserService {

    public String sayHello() {}

    public User findUserById(int id) {}
}

 

生產者版本2

@Service(version = "v2.0")
public class UserServiceImpl implements UserService {

    public String sayHello() {}

    public User findUserById(int id) {}
}

 

 

 

 

5.6 負載均衡

當服務提供者provider以集群的方式提供服務時;某個請求到達后,到底要哪個provider提供服務,就要根據負載均衡策略分配。

配置方式,在消費者遠程注入的注解上,通過loadbalance屬性配置(策略名稱在AbstractLoadBance的實現類中有定義):

public class UserController {
	@Refrence(loadbalance="random|roundRobin")
    private UserService userService;
}

負載均衡策略共有4個:

  1. random:默認值,按照權重隨機分配。權重默認值為100。但有隨機性。

  2. RoundRobin:按權限輪訓,1/2/3/2。按照順序調用,受權重影響。

  3. LeastActive:最少活躍調用數,相同活躍數隨機。當前負載最低的提供者優先調用。

    某個請求被當前provider調用開始前,計數器+1,調用結束后計數器-1。會優先調用當前計數器的值中最小的provider為本次請求提供服務。

  4. ConsistentHash:一直性hash,相同參數的請求總是發到同一提供者 user/1 user/1000

 

5.7 集群容錯

當請求到達服務提供者provider集群時,如果請求超時失敗了,該請求如何處理的策略。

  1. 默認failover,失敗重試,根據設定的重試次數重試其他機器,默認兩次,每次失敗換個機器。但是之前的超時會報錯。

    通常用於讀操作,但重試會帶來更長延遲。

  2. Failfast:快速失敗,只發起一次調用,立即報錯。通常用於寫操作,比如新增記錄。

  3. Failsafe:安全失敗,失敗時忽略。通常用於寫入審計日志等不重要的操作。

  4. Failback:失敗自動回復,后台記錄失敗請求,定時重發。通常用於必須成功的重要內容,例如消息通知等。

  5. Forking:並發調用多個服務,只要一個成功即返回。通常用於實時性要求較高的讀操作,但需要浪費更多服務資源。可通過 forks="2" 來設置最大並行數。

  6. Broadcast:廣播調用所有提供者,逐個調用,任意一台報錯則報錯。通常用於通知所有提供者更新緩存或日志等本地資源信息。

在服務消費者注入提供者的地方@Reference(cluster="failover")

找到Cluster接口,實現類中有每個方案的名字。

public class UserController {
    @Reference(cluster = FailoverCluster.NAME)//遠程注入,通過類常量的方式避免不必要的錯誤
    private UserService userService;
}

 

5.8 服務降級

可以通過服務降級功能臨時屏蔽某個出錯的非關鍵服務。

 

mock="force:return null":消費方對該服務的調用直接返回null,即不發起遠程調用。用來屏蔽不重要服務不可用時對調用方的影響。

mock="fail:return null":表示消費方對該服務的方法調用在失敗后,再返回 null 值,不拋異常。用來容忍不重要服務不穩定時對調用方的影響。

 

在遠程注入注解上添加屬性實現

public class UserController {
    @Reference(mock="force:return null")//遠程注入
    private UserService userService;
}

 


免責聲明!

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



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