Dubbo的基本使用與高級應(yīng)用

本篇是Dubbo的使用篇,為后續(xù)的源碼分析打基礎(chǔ)。

1. 最簡單的使用

開篇用一個最簡單的例子,來介紹如何用Dubbo搭建一個簡單的例子。

本例包括:

  • 注冊中心(用Zookeeper代替)
  • 服務(wù)提供者(provider)
  • 服務(wù)消費者(consumer)
  • 接口(interface)

1.1 項目結(jié)構(gòu)

創(chuàng)建項目,并創(chuàng)建三個Module,項目結(jié)構(gòu)如下:


項目結(jié)構(gòu)

1.2 項目依賴

整體pom.xml為:

<?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>org.example</groupId>
    <artifactId>Study</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>consumer</module>
        <module>provider</module>
        <module>interface</module>
    </modules>

    <properties>
        <spring-boot.version>2.3.1.RELEASE</spring-boot.version>
        <dubbo.version>2.7.5</dubbo.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Apache Dubbo  -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-bom</artifactId>
                <version>${dubbo.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>javax.servlet</groupId>
                        <artifactId>servlet-api</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>

</project>

consumer的依賴為:

<?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">
    <parent>
        <artifactId>Study</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>consumer</artifactId>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>

        <dependency>
            <groupId>org.example</groupId>
            <artifactId>interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- Dubbo Spring Boot Starter -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.5</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>

        <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>

        <!-- Zookeeper dependencies -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>${dubbo.version}</version>
            <type>pom</type>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-rpc-http</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

    </dependencies>


</project>

interface的依賴為:

<?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">
    <parent>
        <artifactId>Study</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>interface</artifactId>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

provider的依賴為:

<?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">
    <parent>
        <artifactId>Study</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>provider</artifactId>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.example</groupId>
            <artifactId>interface</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!-- Dubbo Spring Boot Starter -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.5</version>
        </dependency>

        <dependency>
            <groupId>org.jboss.resteasy</groupId>
            <artifactId>resteasy-jaxrs</artifactId>
            <version>3.0.19.Final</version>
        </dependency>

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>

        <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.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>

        <!-- Zookeeper dependencies -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>${dubbo.version}</version>
            <type>pom</type>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>


        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-rpc-http</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-metadata-report-zookeeper</artifactId>
            <version>${dubbo.version}</version>
        </dependency>
    </dependencies>

</project>

1.3 具體代碼

1.3.1 服務(wù)提供者

@Service注解暴露服務(wù)

實現(xiàn)類

package com.zyz.provider.service;

import com.zyz.DemoService;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;

@Service(version = "default")
public class DefaultDemoService implements DemoService {

    @Override
    public String sayHello(String name) {
        System.out.println("執(zhí)行了服務(wù)" + name);

        URL url = RpcContext.getContext().getUrl();
        return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name);  // 正常訪問
    }
}

啟動類

package com.zyz;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DubboProviderDemo {

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

}

配置文件application.properties:

# Spring boot application
spring.application.name=dubbo-provider-demo
server.port=8081

# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service
dubbo.scan.base-packages=com.zyz.provider.service
dubbo.application.name=${spring.application.name}


## Dubbo Registry
dubbo.registry.address=zookeeper://127.0.0.1:2181

# Dubbo Protocol
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

日志文件log4j.properties:

###set log levels###
log4j.rootLogger=info, stdout
###output to the console###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[%d{dd/MM/yy HH:mm:ss:SSS z}] %t %5p %c{2}: %m%n

1.3.2 接口

接口類DemoService:

package com.zyz;

public interface DemoService {

    String sayHello(String name);

}

1.3.3 服務(wù)消費者

@Reference引入服務(wù),加入版本表示具體引入哪一個實現(xiàn)類的實例

啟動類:

package com.zyz;

import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class DubboConsumerDemo {

    @Reference(version = "default")
    private DemoService demoService;

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(DubboConsumerDemo.class);

        DemoService demoService = context.getBean(DemoService.class);
        System.out.println((demoService.sayHello("zyz")));
    }

}

配置文件application.yml:

spring:
  application:
    name: dubbo-consumer-demo

server:
  port: 8082

dubbo:
  registry:
    address: zookeeper://127.0.0.1:2181

Zookeeper的配置參考

先啟動Zookeeper,再啟動provider的啟動類,最后啟動consumer的啟動類

看到輸出:

dubbo:20880, Hello, zyz

大功告成。

2. 具體應(yīng)用

2.1 負載均衡

Dubbo提供了四種負載均衡策略,也可以自定義。

  • Random LoadBalance:隨機,按權(quán)重設(shè)置隨機概率。
  • RoundRobin LoadBalance:輪詢,按公約后的權(quán)重設(shè)置輪詢比率。
  • LeastActive LoadBalance:最少活躍調(diào)用數(shù),相同活躍數(shù)的隨機,活躍數(shù)指調(diào)用前后計數(shù)差。
  • ConsistentHash LoadBalance:一致性 Hash,相同參數(shù)的請求總是發(fā)到同一提供者。

2.2 具體代碼

修改provider的配置文件為:

# Spring boot application
spring.application.name=dubbo-provider-demo
server.port=8081

# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service
dubbo.scan.base-packages=com.zyz.provider.service
dubbo.application.name=${spring.application.name}


## Dubbo Registry
dubbo.registry.address=zookeeper://127.0.0.1:2181

# Dubbo Protocol
#dubbo.protocol.name=dubbo
#dubbo.protocol.port=20880

dubbo.protocols.p1.id=dubbo1
dubbo.protocols.p1.name=dubbo
dubbo.protocols.p1.port=20881
dubbo.protocols.p1.host=0.0.0.0

dubbo.protocols.p2.id=dubbo2
dubbo.protocols.p2.name=dubbo
dubbo.protocols.p2.port=20882
dubbo.protocols.p2.host=0.0.0.0

dubbo.protocols.p3.id=dubbo3
dubbo.protocols.p3.name=dubbo
dubbo.protocols.p3.port=20883
dubbo.protocols.p3.host=0.0.0.0

服務(wù)消費端新增類:

package com.zyz.consumer;

import com.zyz.DemoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

@EnableAutoConfiguration
public class LoadBalanceDubboConsumerDemo {

    @Reference(version = "default", loadbalance = "roundrobin")
    private DemoService demoService;

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(LoadBalanceDubboConsumerDemo.class);

        DemoService demoService = context.getBean(DemoService.class);

        roundRobin(demoService);

    }

    /**
     * 負載均衡(輪詢)
     * @param demoService
     */
    public static void roundRobin(DemoService demoService){

        for (int i = 0; i < 1000; i++) {
            System.out.println((demoService.sayHello("zyz")));
            try {
                Thread.sleep(1 * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

最終輸出為:

dubbo:20882, Hello, zyz
dubbo:20883, Hello, zyz
dubbo:20881, Hello, zyz
dubbo:20882, Hello, zyz
dubbo:20883, Hello, zyz
dubbo:20881, Hello, zyz

2.2 服務(wù)超時

2.2.1 說明

在服務(wù)提供者和服務(wù)消費者上都可以配置服務(wù)超時時間,這兩者是不?樣的。

消費者調(diào)??個服務(wù),分為三步:

  • 消費者發(fā)送請求(?絡(luò)傳輸)
  • 服務(wù)端執(zhí)?服務(wù)
  • 服務(wù)端返回響應(yīng)(?絡(luò)傳輸)

如果在服務(wù)端和消費端只在其中??配置了timeout,那么沒有歧義,表示消費端調(diào)?服務(wù)的超時時間,消 費端如果超過時間還沒有收到響應(yīng)結(jié)果,則消費端會拋超時異常,但,服務(wù)端不會拋異常,服務(wù)端在執(zhí)? 服務(wù)后,會檢查執(zhí)?該服務(wù)的時間,如果超過timeout,則會打印?個超時?志。服務(wù)會正常的執(zhí)?完。

如果在服務(wù)端和消費端各配了?個timeout,那就?較復(fù)雜了,假設(shè)

  • 服務(wù)執(zhí)?為5s
  • 消費端timeout=3s
  • 服務(wù)端timeout=6s

那么消費端調(diào)?服務(wù)時,消費端會收到超時異常(因為消費端超時了),服務(wù)端?切正常(服務(wù)端沒有超時)。

2.2.2 具體代碼

服務(wù)提供方新增兩個類:

package com.zyz.provider.service;


import com.zyz.DemoService;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;

import java.util.concurrent.TimeUnit;


@Service(version = "timeout", timeout = 6000)
public class TimeoutDemoService implements DemoService {

    @Override
    public String sayHello(String name) {
        System.out.println("執(zhí)行了timeout服務(wù)" + name);

        // 服務(wù)執(zhí)行5秒
        // 服務(wù)超時時間為3秒,但是執(zhí)行了5秒,服務(wù)端會把任務(wù)執(zhí)行完的
        // 服務(wù)的超時時間,是指如果服務(wù)執(zhí)行時間超過了指定的超時時間則會拋一個warn
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("執(zhí)行結(jié)束" + name);

        URL url = RpcContext.getContext().getUrl();
        return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name);  // 正常訪問
    }

}

package com.zyz;

import org.apache.dubbo.config.spring.ServiceBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class UpdateBeanPostProcessors implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {

        if (beanName.contains("ServiceBean")) {
            //這里設(shè)置id,否則會造成同一個Service有多個group時,只能注入第一個service
            ServiceBean serviceBean = (ServiceBean) bean;
            serviceBean.setId(beanName);
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

消費方新增一個啟動類:

package com.zyz.consumer;

import com.zyz.DemoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;

@EnableAutoConfiguration
public class TimeoutDubboConsumerDemo {


    @Reference(version = "timeout", timeout = 3000)
    private DemoService demoService;

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(TimeoutDubboConsumerDemo.class);

        DemoService demoService = context.getBean(DemoService.class);

        // 服務(wù)調(diào)用超時時間為1秒,默認為3秒
        // 如果這1秒內(nèi)沒有收到服務(wù)結(jié)果,則會報錯
        System.out.println((demoService.sayHello("周瑜"))); //xxservestub


    }

}

2.3 集群容錯

集群容錯表示:服務(wù)消費者在調(diào)?某個服務(wù)時,這個服務(wù)有多個服務(wù)提供者,在經(jīng)過負載均衡后選出其中 ?個服務(wù)提供者之后進?調(diào)?,但調(diào)?報錯后,Dubbo所采取的后續(xù)處理策略。

2.3.1 設(shè)置

  • Failover Cluster:默認配置,重試兩次,一共請求三次
  • Failfast Cluster:快速失敗,只發(fā)起一次調(diào)用,失敗立即報錯。通常用于非冪等性的寫操作,比如新增記錄
  • Failsafe Cluster:失敗安全,出現(xiàn)異常時,直接忽略。通常用于寫入審計日志等操作
  • Failback Cluster:失敗自動恢復(fù),后臺記錄失敗請求,定時重發(fā)。通常用于消息通知操作
  • Forking Cluster:并行調(diào)用多個服務(wù)器,只要一個成功即返回。通常用于實時性要求較高的讀操作,但需要浪費更多服務(wù)資源??赏ㄟ^ forks="2" 來設(shè)置最大并行數(shù)。
  • Broadcast Cluster:廣播調(diào)用所有提供者,逐個調(diào)用,任意一臺報錯則報錯。通常用于通知所有提供者更新緩存或日志等本地資源信息。

2.3.2 具體代碼

cousumer新增一個類:

package com.zyz.consumer;

import com.zyz.DemoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;

@EnableAutoConfiguration
public class ClusterDubboConsumerDemo {


    @Reference(version = "cluster", timeout = 1000, cluster = "failfast")
    private DemoService demoService;

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(ClusterDubboConsumerDemo.class);

        DemoService demoService = context.getBean(DemoService.class);

        System.out.println((demoService.sayHello("zyz")));

    }

}

provider新增一個實現(xiàn)類:

package com.zyz.provider.service;

import com.zyz.DemoService;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;

import java.util.concurrent.TimeUnit;

@Service(version = "cluster", timeout = 3000)
public class ClusterDemoService implements DemoService {

    @Override
    public String sayHello(String name) {
        System.out.println("執(zhí)行了cluster服務(wù)" + name);

        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("執(zhí)行結(jié)束" + name);
        URL url = RpcContext.getContext().getUrl();
        return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name);  // 正常訪問
    }
}

2.4 服務(wù)降級

服務(wù)降級表示:服務(wù)消費者在調(diào)?某個服務(wù)提供者時,如果該服務(wù)提供者報錯了,所采取的措施。

集群容錯和服務(wù)降級的區(qū)別在于:

  • 集群容錯是整個集群范圍內(nèi)的容錯
  • 服務(wù)降級是單個服務(wù)提供者的?身容錯

2.4.1 具體代碼

consumer新增一個類:

package com.zyz.consumer;

import com.zyz.DemoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;

@EnableAutoConfiguration
public class MockDubboConsumerDemo {


    @Reference(version = "timeout", timeout = 1000, mock = "fail: return 123")
    private DemoService demoService;

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(MockDubboConsumerDemo.class);

        DemoService demoService = context.getBean(DemoService.class);

        System.out.println((demoService.sayHello("zyz")));
    }

}

2.5 本地存根

本地存根就是?段邏輯,這段邏輯是在服務(wù)消費端執(zhí)?的, 這段邏輯?般都是由服務(wù)提供者提供,服務(wù)提供者可以利?這種機制在服務(wù)消費者遠程調(diào)?服務(wù)提供者之前或之后再做?些其他事情,?如結(jié)果緩存,請求參數(shù)驗證等等。(AOP功能)

2.5.1 具體代碼

interface新增一個類:

package com.zyz;

public class DemoServiceStub implements DemoService {

    private final DemoService demoService;

    // 構(gòu)造函數(shù)傳入真正的遠程代理對象
    public DemoServiceStub(DemoService demoService){
        this.demoService = demoService;
    }

    @Override
    public String sayHello(String name) {
        // 此代碼在客戶端執(zhí)行, 你可以在客戶端做ThreadLocal本地緩存,或預(yù)先驗證參數(shù)是否合法,等等
        try {
            System.out.println("校驗邏輯");
            return demoService.sayHello(name); // safe  null
        } catch (Exception e) {
            // 你可以容錯,可以做任何AOP攔截事項
            return "容錯數(shù)據(jù)";
        }
    }
}

consumer新增一個類:

package com.zyz.consumer;

import com.zyz.DemoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;

@EnableAutoConfiguration
public class StubDubboConsumerDemo {


    @Reference(version = "timeout", timeout = 1000, stub = "com.zyz.DemoServiceStub")
    private DemoService demoService;

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(StubDubboConsumerDemo.class);

        DemoService demoService = context.getBean(DemoService.class);

        System.out.println((demoService.sayHello("周瑜")));


    }

}

2.6 本地偽裝

就是Mock功能,Mock其實就是Dubbo中的服務(wù)容錯的解決?案。

2.7 參數(shù)回調(diào)

?先,如果當前服務(wù)?持參數(shù)回調(diào),意思就是:對于某個服務(wù)接?中的某個?法,如果想?持消費者在調(diào) ?這個?法時能設(shè)置回調(diào)邏輯,那么該?法就需要提供?個?參?來表示回調(diào)邏輯。

因為Dubbo協(xié)議是基于?連接的,所以消費端在兩次調(diào)?同?個?法時想指定不同的回調(diào)邏輯,那么就需 要在調(diào)?時在指定?定key進?區(qū)分。

2.7.1 參數(shù)回調(diào)

consumer新增兩個類

package com.zyz.consumer;


import com.zyz.DemoServiceListener;

public class DemoServiceListenerImpl implements DemoServiceListener {

    @Override
    public void changed(String msg) {
        System.out.println("被回調(diào)了:"+msg);
    }
}

package com.zyz.consumer;

import com.zyz.DemoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;

@EnableAutoConfiguration
public class CallbackDubboConsumerDemo {


    @Reference(version = "callback")
    private DemoService demoService;

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(CallbackDubboConsumerDemo.class);

        DemoService demoService = context.getBean(DemoService.class);

        // 用來進行callback
        System.out.println(demoService.sayHello("zyz", "d1", new DemoServiceListenerImpl()));
        System.out.println(demoService.sayHello("zyz", "d2", new DemoServiceListenerImpl()));
        System.out.println(demoService.sayHello("zyz", "d3", new DemoServiceListenerImpl()));
    }

}

interface新增一個類,并在DemoService中新增方法:

package com.zyz;

public interface DemoService {

    String sayHello(String name);

    // 添加回調(diào)
    default String sayHello(String name, String key, DemoServiceListener listener) {
        return null;
    };

}

package com.zyz;

public interface DemoServiceListener {
    void changed(String msg);
}

provider新增類:

package com.zyz.provider.service;

import com.zyz.DemoService;
import com.zyz.DemoServiceListener;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.annotation.Argument;
import org.apache.dubbo.config.annotation.Method;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


// DemoService的sayHello方法的index=1的參數(shù)是回調(diào)對象,服務(wù)消費者可以調(diào)用addListener方法來添加回調(diào)對象,服務(wù)提供者一旦執(zhí)行回調(diào)對象的方法就會通知給服務(wù)消費者

@Service(version = "callback", methods = {@Method(name = "sayHello", arguments = {@Argument(index = 2, callback = true)})}, callbacks = 3)
public class CallBackDemoService implements DemoService {

    private final Map<String, DemoServiceListener> listeners = new ConcurrentHashMap<String, DemoServiceListener>();

    public CallBackDemoService() {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    for (Map.Entry<String, DemoServiceListener> entry : listeners.entrySet()) {
                        entry.getValue().changed(getChanged(entry.getKey()));
                    }
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        t.start();

    }


    private String getChanged(String key) {
        return "Changed: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }

    @Override
    public String sayHello(String name) {
        return null;
    }

    @Override
    public String sayHello(String name, String key, DemoServiceListener callback) {
        System.out.println("執(zhí)行了回調(diào)服務(wù)" + name);

        listeners.put(key, callback);
        URL url = RpcContext.getContext().getUrl();
        return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name);  // 正常訪問
    }

}

2.8 異步調(diào)用

從 2.7.0 開始,Dubbo 的所有異步編程接口開始以 CompletableFuture 為基礎(chǔ)

基于 NIO 的非阻塞實現(xiàn)并行調(diào)用,客戶端不需要啟動多線程即可完成并行調(diào)用多個遠程服務(wù),相對多線程開銷較小。

2.8.1 具體代碼

consumer新增類:

package com.zyz.consumer;

import com.zyz.DemoService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;

@EnableAutoConfiguration
public class AsyncDubboConsumerDemo {

    @Reference(version = "async")
    private DemoService demoService;

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(AsyncDubboConsumerDemo.class);

        DemoService demoService = context.getBean(DemoService.class);

        // 調(diào)用直接返回CompletableFuture
        CompletableFuture<String> future = demoService.sayHelloAsync("異步調(diào)用");  // 5

        future.whenComplete((v, t) -> {
            if (t != null) {
                t.printStackTrace();
            } else {
                System.out.println("Response: " + v);
            }
        });

        System.out.println("結(jié)束了");

    }

}

interface中DemoService新增方法:

package com.zyz;

import java.util.concurrent.CompletableFuture;

public interface DemoService {

    String sayHello(String name);

    // 異步調(diào)用方法
    default CompletableFuture<String> sayHelloAsync(String name) {
        return null;
    };

    // 添加回調(diào)
    default String sayHello(String name, String key, DemoServiceListener listener) {
        return null;
    };

}

consumer新增一個類:

package com.zyz.provider.service;

import com.zyz.DemoService;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.RpcContext;

import java.util.concurrent.CompletableFuture;

@Service(version = "async")
public class AsyncDemoService implements DemoService {

    @Override
    public String sayHello(String name) {
        System.out.println("執(zhí)行了同步服務(wù)" + name);
        URL url = RpcContext.getContext().getUrl();
        return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name);  // 正常訪問
    }

    @Override
    public CompletableFuture<String> sayHelloAsync(String name) {
        System.out.println("執(zhí)行了異步服務(wù)" + name);

        return CompletableFuture.supplyAsync(() -> {
            return sayHello(name);
        });
    }
}

2.9 泛化調(diào)用

泛化調(diào)?可以?來做服務(wù)測試。

在Dubbo中,如果某個服務(wù)想要?持泛化調(diào)?,就可以將該服務(wù)的generic屬性設(shè)置為true,那對于服務(wù)消費者來說,就可以不?依賴該服務(wù)的接?,直接利?GenericService接?來進?服務(wù)調(diào)?。

2.9.1 具體代碼

consumer新增一個類:

package com.zyz.consumer;

import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;

import java.io.IOException;

@EnableAutoConfiguration
public class GenericDubboConsumerDemo {


    @Reference(id = "demoService", version = "default", interfaceName = "com.zyz.DemoService", generic = true)
    private GenericService genericService;

    public static void main(String[] args) throws IOException {
        ConfigurableApplicationContext context = SpringApplication.run(GenericDubboConsumerDemo.class);

        GenericService genericService = (GenericService) context.getBean("demoService");

        Object result = genericService.$invoke("sayHello", new String[]{"java.lang.String"}, new Object[]{"zyz"});
        System.out.println(result);


    }

}

provider新增一個類:

package com.zyz.provider.service;

import org.apache.dubbo.config.annotation.Service;
import org.apache.dubbo.rpc.service.GenericException;
import org.apache.dubbo.rpc.service.GenericService;

@Service(interfaceName = "com.zyz.DemoService", version = "generic")
public class GenericDemoService implements GenericService {
    @Override
    public Object $invoke(String s, String[] strings, Object[] objects) throws GenericException {
        System.out.println("執(zhí)行了generic服務(wù)");

        return "執(zhí)行的方法是" + s;
    }
}

3. 相關(guān)工具

Zookeeper可視化客戶端
管理臺

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容