org.apache.dubbo 2.7.x 再聚首

Dubbo 版本 :

  Dubbo 社區目前主力維護的有 2.6.x 和 2.7.x 兩大版本,其中,

  • 2.6.x 主要以 bugfix 和少量 enhancements 爲主,因此能完全保證穩定性
  • 2.7.x 作爲社區的主要開發版本,得到持續更新並增加了大量新 feature 和優化,同時也帶來了一些穩定性挑戰

  版本更多信息請參考官網。相信小夥伴們對於Dubbo 都有一定的瞭解。相關基礎知識點可以參考 https://www.cnblogs.com/wuzhenzhao/p/10008824.html .

Dubbo 再聚首之自動化配置:

dubbo-spring-boot-starter(org.apache.dubbo:2.7.7):

  基於目前的 Spring Boot 自動化配置的盛行,我們在使用 Dubbo的時候不再像以前集成 spring 的時候那樣的繁瑣,需要進行很多的配置。接下來來體驗一下 dubbo-spring-boot-starter 帶來的便捷。

  本文註冊中心採用 Spring  Cloud  Alibaba  Nacos ,不熟悉的小夥伴可以參考 https://www.cnblogs.com/wuzhenzhao/category/1530796.html

項目目錄:

  springboot-dubbo-api 模塊:

1.構建服務接口,api模塊,導入 Rest 協議支持依賴:

<dependencies>
        <!--添加REST支持-->
        <!--Rest協議-->
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>3.8.1.Final</version>
        </dependency>
        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-client</artifactId>
            <version>4.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>9.4.12.RC2</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>9.4.12.RC2</version>
        </dependency>
    </dependencies>

2. 創建接口 :

@Path("/")
public interface HelloService {
    
    @GET
    @Path("/sayRest")
    String sayHello() throws Exception;
}

  springboot-dubbo-provider 模塊:

1.導入依賴:

<dependencies>
        <!--基於spring-boot-dependencies 2.3.0RELEASE 版本 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- dubbo 依賴-->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.7</version>
        </dependency>
        <!--nacos註冊中心依賴-->
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>1.2.1</version>
        </dependency>
        <!--zk註冊中心依賴-->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <artifactId>springboot-dubbo-api</artifactId>
            <groupId>com.wuzz.demo</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

3. 創建服務實現類 :

@DubboService(loadbalance = "random", // 負載均衡
        timeout = 50000, //超時
        cluster = "failsafe", // 服務容錯
        protocol = {"dubbo", "rest"}, //多協議支持
        registry = {"hangzhou", "wenzhou"} //多註冊中心
)
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello() throws Exception {
        return "Hello Dubbo";
    }
}

4. 配置文件配置

spring.application.name=springboot-dubbo

# Netty ->
dubbo.protocols.dubbo.name=dubbo
dubbo.protocols.dubbo.port=-1

# jetty (配置了rest協議)
dubbo.protocols.rest.name=rest
dubbo.protocols.rest.port=-1
dubbo.protocols.rest.server=jetty

# zk註冊中心
dubbo.registries.hangzhou.address=zookeeper://192.168.1.101:2181
dubbo.registries.hangzhou.timeout=10000
dubbo.registries.hangzhou.default=true
## 服務啓動的時候,如果註冊中心有問題,那麼服務就啓動失敗
dubbo.registries.hangzhou.check=false
# nacos 註冊中心
dubbo.registries.wenzhou.address=nacos://localhost:8848

5. 服務啓動類,配置掃描路徑

@DubboComponentScan(basePackages = "com.wuzz.demo") //dubbo服務掃描
@SpringBootApplication
public class SpringBootDubboProviderApp {

    private final static Logger log = LoggerFactory.getLogger(SpringBootDubboProviderApp.class);

    public static void main(String[] args) {
        SpringApplication.run(SpringBootDubboProviderApp.class, args);
        log.info("服務啓動成功");

    }
}

  springboot-dubbo-client 模塊:

1.導入相關依賴:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.7</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.nacos</groupId>
            <artifactId>nacos-client</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.0.1</version>
        </dependency>
        <dependency>
            <artifactId>springboot-dubbo-api</artifactId>
            <groupId>com.wuzz.demo</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

2. 創建測試類 :

@RestController
public class DubboController {
    //Dubbo提供的註解
    @DubboReference(loadbalance = "roundrobin",
              timeout = 9000, cluster = "failfast",
              mock = "com.wuzz.demo.mock.HelloServiceMock", check = false)
    HelloService helloService;

    @GetMapping("/sayhello")
    public String sayHello() throws Exception {
        return helloService.sayHello(); //我調用這個服務可能失敗,如果失敗了,我要怎麼處理
    }

    // dubbo 泛化調用 
    @DubboReference(interfaceName = "com.wuzz.demo.api.HelloService",generic = true,check = false)
    GenericService genericService;

    @GetMapping("/demo")
    public String demo(){
        return genericService.$invoke("sayHello",new String[0],null).toString();
    }
}

  mock 實現類:

public class HelloServiceMock implements HelloService {
    @Override
    public String sayHello() {
        return "服務端發生異常, 被降解了。返回兜底數據。。。";
    }
}

3.配置文件,啓動類無需任何配置

spring.application.name=springboot-dubbo-client
dubbo.registry.address=nacos://localhost:8848
server.port=8889

  然後先後啓動 服務提供者、服務消費者模塊。可以看到 註冊中心應當有兩個服務的相關注冊信息:

  然後就可以訪問對應的接口進行測試。

spring-cloud-starter-dubbo(org.apache.dubbo:2.7.6):

  與springboot 集成不同,spring-cloud-alibaba 自成生態,在多註冊中心的用法上有兼容問題。

項目目錄

  spring-cloud-alibaba-dubbo-api 模塊:

1.添加相關接口

public interface HelloService {

    String sayHello() throws Exception;
}

  spring-cloud-alibaba-dubbo-provider 模塊:

1.添加相關依賴:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <!--dubbo 依賴-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-dubbo</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <!--nacos 註冊中心依賴-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.wuzz.demo</groupId>
            <artifactId>spring-cloud-alibaba-dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

2.實現類:

@Service(loadbalance = "random",timeout = 50000,cluster = "failsafe")
public class HelloServiceImpl implements HelloService {
    @Override
    public String sayHello() throws Exception {
        return "Hello Dubbo";
    }
}

3. 配置文件配置:

spring.application.name=springboot-dubbo
dubbo.scan.base-packages=com.wuzz.demo
dubbo.protocol.port=20882
dubbo.protocol.name=dubbo
spring.cloud.nacos.discovery.server-addr=localhost:8848

4.啓動類:

@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudAlibabaDubboProviderApp {

    private final static Logger log = LoggerFactory.getLogger(SpringCloudAlibabaDubboProviderApp.class);

    public static void main(String[] args) {
        SpringApplication.run(SpringCloudAlibabaDubboProviderApp.class, args);
        log.info("服務啓動成功");

    }
}

  spring-cloud-alibaba-dubbo-client 模塊:

1.導入依賴:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-dubbo</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.wuzz.demo</groupId>
            <artifactId>spring-cloud-alibaba-dubbo-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

3.測試類編寫:

@RestController
public class DubboController {
    //Dubbo提供的註解
    @Reference(loadbalance = "roundrobin",
            timeout = 1, cluster = "failfast",
            mock = "com.wuzz.demo.mock.HelloServiceMock", check = false)
    HelloService helloService;

    @GetMapping("/sayhello")
    public String sayHello() throws Exception {
        return helloService.sayHello(); //我調用這個服務可能失敗,如果失敗了,我要怎麼處理
    }
}

  mock 實現類:

public class HelloServiceMock implements HelloService {
    @Override
    public String sayHello() {
        return "服務端發生異常, 被降解了。返回兜底數據。。。";
    }
}

4.配置文件:

spring.application.name=springboot-dubbo-client
server.port=8889
spring.cloud.nacos.discovery.server-addr=localhost:8848

5.啓動類:

@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudAlibabaDubboClientApp {

    private final static Logger log = LoggerFactory.getLogger(SpringCloudAlibabaDubboClientApp.class);

    public static void main(String[] args) {
        SpringApplication.run(SpringCloudAlibabaDubboClientApp.class, args);
        log.info("服務啓動成功");
    }
}

  先後啓動服務提供者、服務消費者進行測試即可。

Dubbo 常用功能簡介:

多協議支持:

  官網協議參考手冊 :http://dubbo.apache.org/zh-cn/docs/user/references/protocol/introduction.html

  • dubbo://  :Dubbo 缺省協議採用單一長連接和 NIO 異步通訊,適合於小數據量大併發的服務調用,以及服務消費者機器數遠大於服務提供者機器數的情況。反之,Dubbo 缺省協議不適合傳送大數據量的服務,比如傳文件,傳視頻等,除非請求量很低。
  • rmi://      : RMI 協議採用 JDK 標準的 java.rmi.* 實現,採用阻塞式短連接和 JDK 標準序列化方式。
  • hessian://  : Hessian 協議用於集成 Hessian 的服務,Hessian 底層採用 Http 通訊,採用 Servlet 暴露服務,Dubbo 缺省內嵌 Jetty 作爲服務器實現。
  • http://     :基於 HTTP 表單的遠程調用協議,採用 Spring 的 HttpInvoker 實現 
  • webservice://    :基於 WebService 的遠程調用協議,基於 Apache CXF  的 frontend-simple 和 transports-http 實現 。
  • thrift://    :當前 dubbo 支持 的 thrift 協議是對 thrift 原生協議 的擴展,在原生協議的基礎上添加了一些額外的頭信息,比如 service name,magic number 等。
  • memcached://   :基於 memcached  實現的 RPC 協議 。
  • redis://   :基於 Redis  實現的 RPC 協議 。
  • rest://    :基於標準的Java REST API——JAX-RS 2.0(Java API for RESTful Web Services的簡寫)實現的REST調用支持
  • grpc://    :Dubbo 自 2.7.5 版本開始支持 gRPC 協議,對於計劃使用 HTTP/2 通信,或者想利用 gRPC 帶來的 Stream、反壓、Reactive 編程等能力的開發者來說, 都可以考慮啓用 gRPC 協議。

Dubbo 的負載均衡:

  在集羣負載均衡時,Dubbo 提供了多種均衡策略,缺省爲 random 隨機調用。

  1.Random LoadBalance:

  • 隨機,按權重設置隨機概率。
  • 在一個截面上碰撞的概率高,但調用量越大分佈越均勻,而且按概率使用權重後也比較均勻,有利於動態調整提供者權重。

  2.RoundRobin LoadBalance:

  • 輪詢,按公約後的權重設置輪詢比率。
  • 存在慢的提供者累積請求的問題,比如:第二臺機器很慢,但沒掛,當請求調到第二臺時就卡在那,久而久之,所有請求都卡在調到第二臺上。

  3.LeastActive LoadBalance:

  • 最少活躍調用數,相同活躍數的隨機,活躍數指調用前後計數差。
  • 使慢的提供者收到更少請求,因爲越慢的提供者的調用前後計數差會越大。

  4.ConsistentHash LoadBalance:

  • 一致性 Hash,相同參數的請求總是發到同一提供者。
  • 當某一臺提供者掛時,原本發往該提供者的請求,基於虛擬節點,平攤到其它提供者,不會引起劇烈變動。
  • 算法參見:http://en.wikipedia.org/wiki/Consistent_hashing
  • 缺省只對第一個參數 Hash,如果要修改,請配置 <dubbo:parameter key="hash.arguments" value="0,1" />
  • 缺省用 160 份虛擬節點,如果要修改,請配置 <dubbo:parameter key="hash.nodes" value="320" />

  5.ShortestResponse LoadBalance:

  最短響應時間負載均衡算法,篩選成功調用響應時間最短的調用程序的數量,並計算這些調用程序的權重和數量。然後根據響應時間的長短來分配目標服務的路由權重。

集羣容錯:

  官方文檔 :http://dubbo.apache.org/zh-cn/docs/user/demos/fault-tolerent-strategy.html

  在集羣調用失敗時,Dubbo 提供了多種容錯方案,缺省爲 failover 重試。

  • Failover Cluster  :失敗自動切換,當出現失敗,重試其它服務器 。通常用於讀操作,但重試會帶來更長延遲。可通過 retries="2" 來設置重試次數(不含第一次)。

  • Failfast Cluster  :快速失敗,只發起一次調用,失敗立即報錯。通常用於非冪等性的寫操作,比如新增記錄。

  • Failsafe Cluster   :失敗安全,出現異常時,直接忽略。通常用於寫入審計日誌等操作。

  • Failback Cluster   :失敗自動恢復,後臺記錄失敗請求,定時重發。通常用於消息通知操作。

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

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

服務降級:

  dubbo的降級方式: Mock。上文代碼中已給出示例實現步驟:

  1. 在client端創建一個 HelloServiceMock 類,實現對應的接口(需要對哪個接口進行mock,就實現哪個),名稱必須以Mock結尾
  2. 在client端的服務調用的註解配置中,添加 mock 配置,增加一個mock屬性指向創建的HelloServiceMock 
  3. 模擬錯誤(設置timeout),模擬超時異常,運行測試代碼即可訪問到HelloServiceMock 這個類。當服務端故障解除以後,調用過程將恢復正常,

Dubbo泛化:

  泛化接口調用方式主要用於客戶端沒有 API 接口及模型類元的情況,參數及返回值中的所有 POJO 均用 Map 表示,通常用於框架集成,比如:實現一個通用的服務測試框架,可通過 GenericService 調用所有服務實現。 上文已給出示例。

  更多的泛化配置可以參考官網 :http://dubbo.apache.org/zh-cn/docs/user/demos/generic-reference.html

主機綁定:

  關於主機綁定的源碼實現位於  org.apache.dubbo.config.ServiceConfig#doExportUrlsFor1Protocol

String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);

  主機綁定的步驟主要有以下幾個步驟:

  •  查找環境變量中是否存在啓動參數 [DUBBO_IP_TO_BIND] =服務註冊的ip
  • 讀取配置文件, dubbo.protocols.dubbo.host= 服務註冊的ip
  • InetAddress.getLocalHost().getHostAddress() 獲得本機ip地址
  • 通過Socket去連接註冊中心,從而獲取本機IP
  • 會輪詢本機的網卡,直到找到合適的IP地址
  • 上面獲取到的ip地址是bindip,如果需要作爲服務註冊中心的ip, DUBBO_IP_TO_REGISTRY -dDUBBO_IP_TO_REGISTRY=ip

配置優先級:

  • 方法層面的配置要優先於接口層面的配置, 接口層面的配置要優先於全局配置.
  • 如果級別一樣,以客戶端的配置優先,服務端次之.

性能調優的參數:

  dubbo 提供了針對服務端/客戶端的相關參數調優,以下列舉了一些比較重要的參數。

@Configuration
public class DubboConfig {

    //服務端相關調優參數
    @Bean
    public ProviderConfig providerConfig() {
        ProviderConfig config = new ProviderConfig();
        //默認200 服務線程池大小(固定大小)
        config.setThreads(200);
        //默認CPU + 1
        //IO線程池,接收網絡讀寫中斷,以及序列化和反序列化,
        // 不處理業務,業務線程池參見threads配置,此線程池和CPU相關,不建議配置。
        config.setIothreads(Runtime.getRuntime().availableProcessors() + 1);
        //線程池類型,可選:fixed/cached/limit(2.5.3以上)/eager(2.6.x以上)
        config.setThreadpool("fixed");
        //對每個提供者的最大連接數,rmi、http、hessian
        //等短連接協議表示限制連接數,dubbo等長連接協表示建立的長連接個數
        config.setConnections(0);
        //線程池隊列大小,當線程池滿時,排隊等待執行的隊列大小,
        //建議不要設置,當線程池滿時應立即失敗,重試其它服務提供機器,
        //而不是排隊,除非有特殊需求。
        config.setQueues(0);
        //每服務消費者每服務每方法最大併發調用數
        config.setAccepts(0);
        //服務提供者每服務每方法最大可並行執行請求數
        config.setExecutes(0);
        return config;

    }
    //客戶端相關調優參數
    @Bean
    public ConsumerConfig consumerConfig() {
        ConsumerConfig config = new ConsumerConfig();
        //每個服務對每個提供者的最大連接數,
        //rmi、http、hessian等短連接協議支持此配置,dubbo協議長連接不支持此配置
        config.setConnections(100);
        //每服務消費者每服務每方法最大併發調用數
        config.setActives(0);
        return config;
    }
}

  參數調優可以參考以下dubbo的處理流程

  更多參數請參考官網:

  1. providerhttp://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-provider.html
  2. consumerhttp://dubbo.apache.org/zh-cn/docs/user/references/xml/dubbo-consumer.html

Dubbo緩存文件:

  配置服務地址的緩存,避免註冊中心掛了之後對於服務通信的影響,客戶端做以下配置 :

spring.application.name=springboot-dubbo-client
server.port=8889

# nacos 註冊中心
dubbo.registries.wenzhou.address=nacos://localhost:8848
# 配置服務地址的緩存,避免註冊中心掛了之後對於服務通信的影響
dubbo.registries.wenzhou.file=${user.home}/dubbo.cache

  然後啓動服務提供者/服務消費者,可以到用戶目錄下看到一個文件,dubbo.cache

 

  然後我們打開它:

  我們會發現服務信息已經被緩存下來了。這個時候,把註冊中心關了,再去訪問接口 ,發現也是沒問題的。

  更多特性請參考官網

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章