Dubbo ==> 簡介

1、架構發展過程前端

首先,咱們先來看看上面的架構發展的線路圖:單一應用架構 --> 垂直應用架構 --> 分佈式服務架構 --> 流動計算架構 。web

單一應用架構算法

  在一些中小型的傳統軟件公司中,一個 產品/項目 的全部的代碼都在一個工程裏,工程下面有多個不一樣的模塊。在部署的時候,將整個工程打包而後放到服務器 Tomcat 下來運行。而後,爲了所謂的「高可用」,會使用一臺負載均衡服務器(好比Nginx),而後將應用部署到兩到三臺服務器上。至於系統的依賴可能只有一個,就是相似於 MySQL、Oracle 這類的關係型數據庫,會單獨部署在一臺服務器上,讓系統來使用。就以下面這個圖:spring

對於這種單一應用的架構,隨着業務量的增加,雖然也能夠進行擴展,可是擴展的每臺服務器上都部署了全部的功能,假如只須要擴展交易能力而其餘模塊的功能不須要擴展呢?這種單一架構的系統就沒辦法解決,因此後面就出現了垂直應用架構。 數據庫

從研發團隊人數的發展來看,這種架構模式也是愈來愈不靠譜的。若是研發團隊有幾十人甚至上百人一塊兒研發這個系統,業務邏輯複雜,功能模塊多達幾十上百個,你們都在一個工程內寫代碼,大量的衝突以及代碼的合併都會讓人崩潰。並且測試也會很是痛苦,好比某個功能模塊要上線了,就必須得把整個單塊系統全部的功能都回歸測試一遍纔敢上線。由於你們的代碼都在一個工程裏,都是耦合在一塊兒的,你修改了代碼,必須所有測試一遍才能保證系統正常。apache

垂直應用架構編程

  隨着業務量的增長,單一應用的架構就愈來愈不適合了,這時候就誕生了垂直應用架構,將整個系統拆成好幾個單獨的應用,提高了效率,擴容的時候也可以對對應的業務進行精準的擴容。緩存

這種垂直應用架構的模式雖然將每一個模塊都拆分開了,可是每一個模塊內部仍是有很是多的功能,好比說用戶中心當中可能會有:註冊、登陸、密碼校驗等等功能,密碼校驗可能會比註冊和登陸使用頻繁得多,這個時候咱們是否要考慮將每個模塊的單體功能再進行拆分,而後就變成了下面這個樣子:tomcat

當拆分得愈來愈多,達到幾十上百甚至上千個的時候,這種垂直應用架構就不能很好的支撐系統的運行,因此就出現了分佈式服務架構。springboot

分佈式服務架構


當垂直應用愈來愈多,應用之間交互不可避免,將核心業務抽取出來,做爲獨立的服務,逐漸造成穩定的服務中心,使前端應用能更快速的響應多變的市場需求。用於提升業務複用及整合的分佈式調用是關鍵。

2、分佈式&集羣&RPC

分佈式:一個電商系統,用戶模塊部署在 Server1, 訂單模塊部署在 Server2, 促銷模塊部署在 Server3, 商品模塊部署在 Server4,他們之間經過遠程rpc實現服務調用,這就叫分佈式。強調的是不一樣功能模塊,單獨部署在不一樣的 Server 上,全部 Server 加起來是一個完整的系統。

集羣:更多強調的是災備,一個電商系統,完整的部署在Server1上一個,完整的部署在Server2上一個,Server1宕機後,Server2仍然能夠正常提供請求服務,這叫集羣。一樣對於某一功能模塊,好比用戶模塊部署在 Server1上,一樣部署在 Server2上,也叫作集羣。分佈式系統的每一個功能模塊節點,均可以用多機作成集羣。

RPC:Remote Procedure Call,遠程過程調用,它是一種經過網絡從遠程計算機程序上請求服務,而不須要了解底層網絡技術的協議。舉個例子說明:兩臺服務器 A、B,分別部署不一樣的應用 a 和 b。當A服務器上的應用 a 想要調用B服務器上的應用 b 提供的方法的時候,因爲不在一個內存空間,不能直接調用,須要經過網絡來表達調用的語義傳達調用的數據,這個時候就出現了一個遠程服務調用的概念。

常見RPC框架:

  • dubbo:Alibaba開發的一個RPC框架,遠程接口基於Java Interface, 依託於Spring框架。
  • gPRC:是由Google公司開源的高性能RPC框架。是一個高性能、開源和通用的 RPC 框架,面向移動和 HTTP/2 設計。目前提供 C、Java 和 Go 語言版本
  • thrift:Apache的一個項目(http://thrift.apache.org),前身是Facebook開發的一個RPC框架,採用thrift做爲IDL (Interface description language)。
  • brpc:一個基於protobuf接口的RPC框架,在百度內部稱爲「baidu-rpc」,它囊括了百度內部全部RPC協議,並支持多種第三方協議,從目前的性能測試數據來看,brpc的性能領跑於其餘同類RPC產品。

3、Dubbo架構

Dubbo是一款高性能、輕量級的開源Java RPC框架,它提供了三大核心能力:面向接口的遠程方法調用,智能容錯和負載均衡,以及服務自動註冊和發現。 

節點角色說明

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

調用關係說明

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

Dubbo的特色:

連通性

  • 註冊中心負責服務地址的註冊與查找,至關於目錄服務,服務提供者和消費者只在啓動時與註冊中心交互,註冊中心不轉發請求,壓力較小
  • 監控中心負責統計各服務調用次數,調用時間等,統計先在內存彙總後每分鐘一次發送到監控中心服務器,並以報表展現
  • 服務提供者向註冊中心註冊其提供的服務,並彙報調用時間到監控中心,此時間不包含網絡開銷
  • 服務消費者向註冊中心獲取服務提供者地址列表,並根據負載算法直接調用提供者,同時彙報調用時間到監控中心,此時間包含網絡開銷
  • 註冊中心,服務提供者,服務消費者三者之間均爲長鏈接,監控中心除外
  • 註冊中心經過長鏈接感知服務提供者的存在,服務提供者宕機,註冊中心將當即推送事件通知消費者
  • 註冊中心和監控中心所有宕機,不影響已運行的提供者和消費者,消費者在本地緩存了提供者列表
  • 註冊中心和監控中心都是可選的,服務消費者能夠直連服務提供者

健壯性

  • 監控中心宕掉不影響使用,只是丟失部分採樣數據
  • 數據庫宕掉後,註冊中心仍能經過緩存提供服務列表查詢,但不能註冊新服務
  • 註冊中心對等集羣,任意一臺宕掉後,將自動切換到另外一臺
  • 註冊中心所有宕掉後,服務提供者和服務消費者仍能經過本地緩存通信
  • 服務提供者無狀態,任意一臺宕掉後,不影響使用
  • 服務提供者所有宕掉後,服務消費者應用將沒法使用,並沒有限次重連等待服務提供者恢復

伸縮性

  • 註冊中心爲對等集羣,可動態增長機器部署實例,全部客戶端將自動發現新的註冊中心
  • 服務提供者無狀態,可動態增長機器部署實例,註冊中心將推送新的服務提供者信息給消費者

升級性

當服務集羣規模進一步擴大,帶動IT治理結構進一步升級,須要實現動態部署,進行流動計算,現有分佈式服務架構不會帶來阻力。下圖是將來可能的一種架構:

節點角色說明

節點 角色說明
Deployer 自動部署服務的本地代理
Repository 倉庫用於存儲服務應用發佈包
Scheduler 調度中心基於訪問壓力自動增減服務提供者
Admin 統一管理控制檯
Registry 服務註冊與發現的註冊中心
Monitor 統計服務的調用次數和調用時間的監控中心

4、Zookeeper

Dubbo 將註冊中心進行抽象,使得它能夠外接不一樣的存儲媒介給註冊中心提供服務,有ZooKeeper,Memcached,Redis等。

Dubbo的註冊、訂閱、通知等功能不是由 Dubbo自己來實現的,而是藉助於這些存儲媒介來實現的。

ZooKeeper的特性

  • 是負載均衡:單註冊中心的承載能力是有限的,在流量達到必定程度的時 候就須要分流,負載均衡就是爲了分流而存在的,一個 ZooKeeper 羣配合相應的Web應用就能夠很容易達到負載均衡;
  • 資源同步:單單有負載均衡還不 夠,節點之間的數據和資源須要同步,ZooKeeper 集羣就自然具有有這樣的功能。ZooKeeper 經過心跳機制能夠檢測掛掉的機器並將掛掉機器的 IP 和服務對應關係從列表中刪除;
  • 命名服務:將樹狀結構用於維護全局的服務地址列表,服務提供者在啓動 的時候,向 ZooKeeper 上的指定節點 /dubbo/${serviceName}/providers 目錄下寫入本身的 URL地址,這個操做就完成了服務的發佈。 其餘特性還有 Mast 選舉,分佈式鎖等;

5、Dubbo環境搭建

一、Zookeeper

下載

https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/

安裝

解壓安裝包,而後將 conf 目錄下的 zoo_sample.cfg 改成 zoo.cfg 便可。

啓動

進入 Zookeeper 安裝目錄下的 bin 目錄,執行如下命令

./zkServer.sh start

鏈接

./zkCli.sh -server IP:端口

二、引入依賴

compile('com.alibaba:dubbo:2.6.6')
compile('io.netty:netty-all:4.1.34.Final')
compile('org.apache.zookeeper:zookeeper:3.4.14')
compile('org.apache.curator:curator-framework:2.13.0')
compile('org.apache.curator:curator-recipes:2.13.0')

三、建立三個工程

dubbo-order-iface

建立兩個實體類 Order 和 RegMsg,以及一個對外提供的接口 IOrderService

@Getter
@Setter
@ToString
public class Order implements Serializable {
  private String orderId;
  private Integer amount;
  private String orderInfo;
}
Order
@Getter
@Setter
@ToString
public class RegMsg implements Serializable {

  private String errorNo;
  private String errorMsg;

  public RegMsg(String errorNo, String errorMsg) {
    this.errorNo = errorNo;
    this.errorMsg = errorMsg;
  }
}
RegMsg

這兩個實體類必須實現 Serializable,由於它要在不一樣的進程之間傳輸,須要進行序列化和反序列化

public interface IOrderService {

  RegMsg creatOrder(Order order);
}
IOrderService

注意:該工程需單獨打包,發到中央倉庫,供服務提供方和消費方使用(以 jar 包的方式引入)

dubbo-order-provider(服務提供者)

服務提供者實現 IOrderService 接口

public class OrderServiceImpl implements IOrderService {

  @Override
  public RegMsg creatOrder(Order order) {
    System.out.println("OrderServiceImpl -> create order: " + order);
    return new RegMsg("200","create order success!!");
  }
}
OrderServiceImpl

用 Spring 配置聲明暴露服務

<?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://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

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

    <!-- 使用zookeeper註冊中心暴露服務地址 -->
    <dubbo:registry address="zookeeper://192.168.182.128:2181" />

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

    <!-- 和本地bean同樣實現服務 -->
    <bean id="orderService" class="com.dubbo.order.provider.service.OrderServiceImpl" />

    <!-- 聲明須要暴露的服務接口 -->
    <dubbo:service interface="com.dubbo.order.iface.service.IOrderService" ref="orderService" />

</beans>
provider.xml

加載 Spring 配置

public class ProviderApp {

  public static void main(String[] args) {

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
    context.start();

    try {
      // 按任意鍵退出
      System.in.read();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}
ProviderApp

dubbo-order-consumer(服務消費者)

經過 Spring 配置引用遠程服務

<?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://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://dubbo.apache.org/schema/dubbo
       http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

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

    <!-- 使用zookeeper註冊中心暴露發現服務地址 -->
    <dubbo:registry address="zookeeper://192.168.182.128:2181" />

    <!-- 生成遠程服務代理,能夠和本地bean同樣使用demoService -->
    <dubbo:reference id="orderService" interface="com.dubbo.order.iface.service.IOrderService" />
</beans>
consumer.xml

加載Spring配置,並調用遠程服務

public class ConsumerApp {

  public static void main(String[] args) throws Exception {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
    context.start();

    // 獲取遠程服務代理
    IOrderService orderService = context.getBean("orderService", IOrderService.class);

    Order order = new Order();
    order.setOrderId("1");
    order.setAmount(1024);
    order.setOrderInfo("dubbo create order");

    // 執行遠程方法
    RegMsg regMsg = orderService.creatOrder(order);
    // 顯示調用結果
    System.out.println("regMsg = " + regMsg);
  }
}
ConsumerApp

6、Dubbo配置

一、啓動時檢查

  • Dubbo 缺省會在啓動時檢查依賴的服務是否可用,不可用時會拋出異常,阻止 Spring 初始化完成,以便上線時,能及早發現問題,默認 check="true"
  • 能夠經過 check="false" 關閉檢查,好比,測試時,有些服務不關心,或者出現了循環依賴,必須有一方先啓動。
  • 另外,若是你的 Spring 容器是懶加載的,或者經過 API 編程延遲引用服務,請關閉 check,不然服務臨時不可用時,會拋出異常,拿到 null 引用,若是 check="false",老是會返回引用,當服務恢復時,能自動連上。

示例:經過Spring 配置文件

關閉某個服務的啓動時檢查:

<dubbo:reference id="orderService" check="false" interface="com.dubbo.order.iface.service.IOrderService" />

關閉全部服務的啓動時檢查:

<dubbo:consumer check="false" />

關閉註冊中心啓動時檢查:

<dubbo:registry check="false" />

二、超時重試

<dubbo:reference id="orderService" timeout="3000" retries="3" interface="com.dubbo.order.iface.service.IOrderService" />

上面配置了超時時間爲3秒,重試3次,若是一直超時,會請求四次。

咱們配置超時和重試前,下面這些狀況是要充分考慮的

超時:

對外提供的服務,超時時間設的比較長,實際狀況倒是:服務時間出現抖動,外部鏈接比較多,可能會把該服務給拖垮(瞬間有大量的鏈接進來,可是服務又消化不掉)。因此超時時間的設置應該充分考慮整個鏈路的時間。

重試:

對外提供的服務,是否應該支持重試,是否有必要重試。好比說:訂單業務,重試也不能形成重複下單,要作好內部惟一性校驗,要作到徹底相同的請求在數據庫的落地只有一單。

7、SpringBoot整合Dubbo

一樣是下面建立三個工程

dubbo-order-iface

這個工程和上面的同樣,沒有變化

dubbo-springboot-provider

引入依賴

dependencies {
    compile project(':dubbo-order-iface')

    // dubbo須要的starter
    compile('com.alibaba.spring.boot:dubbo-spring-boot-starter:2.0.0')
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.boot:spring-boot-starter-aop')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

服務提供者實現 IOrderService 接口

@org.springframework.stereotype.Service
@com.alibaba.dubbo.config.annotation.Service
public class OrderServiceImpl implements IOrderService {

  @Override
  public RegMsg creatOrder(Order order) {

    System.out.println("springboot OrderServiceImpl->createOrder, order=" + order);
    return new RegMsg("3000", "create success!!!");
  }
}
OrderServiceImpl

用 yml文件配置聲明暴露服務

server:
  port: 8001
  servlet:
    context-path: /provider
  tomcat:
    accesslog:
      enabled: true


dubbo:
  application:
    name: dubbo-springboot-provider
  registry:
    address: 192.168.182.128:2181
    protocol: zookeeper
  protocol:
    name: dubbo
    port: 20880
application.yml

加載 Springboot 配置

@SpringBootApplication
@EnableDubbo
public class ProviderApplication {

  public static void main(String[] args) {
    SpringApplication.run(ProviderApplication.class, args);
  }
}
ProviderApplication

dubbo-springboot-consumer

引入依賴

dependencies {
    compile project(':dubbo-order-iface')

    // dubbo須要的starter
    compile('com.alibaba.spring.boot:dubbo-spring-boot-starter:2.0.0')
    implementation('org.springframework.boot:spring-boot-starter-web')
    implementation('org.springframework.boot:spring-boot-starter-aop')
    testImplementation('org.springframework.boot:spring-boot-starter-test')
}

實現一個 Controller 調用方法

@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {

  @Reference
  private IOrderService orderService;

  @RequestMapping("/createOrder")
  public RegMsg createOrder(Integer amount) {

    log.info("createOrder start. amount={}", amount);

    Order order = new Order();

    order.setAmount(amount);
    order.setOrderInfo("time:" + LocalDateTime.now());
    order.setOrderId(System.currentTimeMillis() + "");
    return orderService.creatOrder(order);
  }
}
OrderController

經過 yml文件配置引用遠程服務

server:
  port: 8002
  servlet:
    context-path: /consumer
  tomcat:
    accesslog:
      enabled: true

dubbo:
  application:
    name: dubbo-springboot-consumer
  registry:
    address: 192.168.182.128:2181
    protocol: zookeeper
application.yml

注意:consumer的端口要和provider不同

加載Springboot配置,並調用遠程服務

@SpringBootApplication
@EnableDubbo
public class ConsumerApplication {

  public static void main(String[] args) {
    SpringApplication.run(ConsumerApplication.class, args);
  }
}
ConsumerApplication
相關文章
相關標籤/搜索