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

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

1. 最簡單的使用

開篇用一個(gè)最簡單的例子搞监,來介紹如何用Dubbo搭建一個(gè)簡單的例子。

本例包括:

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

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

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


項(xiàng)目結(jié)構(gòu)

1.2 項(xiàng)目依賴

整體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ù)

實(shí)現(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);  // 正常訪問
    }
}

啟動(dòng)類

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ù)消費(fèi)者

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

啟動(dòng)類:

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的配置參考

先啟動(dòng)Zookeeper使鹅,再啟動(dòng)provider的啟動(dòng)類,最后啟動(dòng)consumer的啟動(dòng)類

看到輸出:

dubbo:20880, Hello, zyz

大功告成昌抠。

2. 具體應(yīng)用

2.1 負(fù)載均衡

Dubbo提供了四種負(fù)載均衡策略患朱,也可以自定義。

  • Random LoadBalance:隨機(jī)炊苫,按權(quán)重設(shè)置隨機(jī)概率裁厅。
  • RoundRobin LoadBalance:輪詢冰沙,按公約后的權(quán)重設(shè)置輪詢比率。
  • LeastActive LoadBalance:最少活躍調(diào)用數(shù)执虹,相同活躍數(shù)的隨機(jī)倦淀,活躍數(shù)指調(diào)用前后計(jì)數(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ù)消費(fèi)端新增類:

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

    }

    /**
     * 負(fù)載均衡(輪詢)
     * @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ù)超時(shí)

2.2.1 說明

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

消費(fèi)者調(diào)??個(gè)服務(wù)愿棋,分為三步:

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

如果在服務(wù)端和消費(fèi)端只在其中??配置了timeout,那么沒有歧義均牢,表示消費(fèi)端調(diào)?服務(wù)的超時(shí)時(shí)間糠雨,消 費(fèi)端如果超過時(shí)間還沒有收到響應(yīng)結(jié)果,則消費(fèi)端會(huì)拋超時(shí)異常徘跪,但甘邀,服務(wù)端不會(huì)拋異常,服務(wù)端在執(zhí)? 服務(wù)后垮庐,會(huì)檢查執(zhí)?該服務(wù)的時(shí)間松邪,如果超過timeout,則會(huì)打印?個(gè)超時(shí)?志哨查。服務(wù)會(huì)正常的執(zhí)?完逗抑。

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

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

那么消費(fèi)端調(diào)?服務(wù)時(shí)邮府,消費(fèi)端會(huì)收到超時(shí)異常(因?yàn)橄M(fèi)端超時(shí)了),服務(wù)端?切正常(服務(wù)端沒有超時(shí))溉奕。

2.2.2 具體代碼

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

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ù)超時(shí)時(shí)間為3秒褂傀,但是執(zhí)行了5秒,服務(wù)端會(huì)把任務(wù)執(zhí)行完的
        // 服務(wù)的超時(shí)時(shí)間加勤,是指如果服務(wù)執(zhí)行時(shí)間超過了指定的超時(shí)時(shí)間則會(huì)拋一個(gè)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仙辟,否則會(huì)造成同一個(gè)Service有多個(gè)group時(shí),只能注入第一個(gè)service
            ServiceBean serviceBean = (ServiceBean) bean;
            serviceBean.setId(beanName);
        }
        return bean;
    }

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

消費(fèi)方新增一個(gè)啟動(dòng)類:

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)用超時(shí)時(shí)間為1秒胸竞,默認(rèn)為3秒
        // 如果這1秒內(nèi)沒有收到服務(wù)結(jié)果欺嗤,則會(huì)報(bào)錯(cuò)
        System.out.println((demoService.sayHello("周瑜"))); //xxservestub


    }

}

2.3 集群容錯(cuò)

集群容錯(cuò)表示:服務(wù)消費(fèi)者在調(diào)?某個(gè)服務(wù)時(shí),這個(gè)服務(wù)有多個(gè)服務(wù)提供者卫枝,在經(jīng)過負(fù)載均衡后選出其中 ?個(gè)服務(wù)提供者之后進(jìn)?調(diào)?煎饼,但調(diào)?報(bào)錯(cuò)后,Dubbo所采取的后續(xù)處理策略校赤。

2.3.1 設(shè)置

  • Failover Cluster:默認(rèn)配置吆玖,重試兩次筒溃,一共請求三次
  • Failfast Cluster:快速失敗,只發(fā)起一次調(diào)用沾乘,失敗立即報(bào)錯(cuò)怜奖。通常用于非冪等性的寫操作,比如新增記錄
  • Failsafe Cluster:失敗安全翅阵,出現(xiàn)異常時(shí)歪玲,直接忽略。通常用于寫入審計(jì)日志等操作
  • Failback Cluster:失敗自動(dòng)恢復(fù)掷匠,后臺記錄失敗請求滥崩,定時(shí)重發(fā)。通常用于消息通知操作
  • Forking Cluster:并行調(diào)用多個(gè)服務(wù)器讹语,只要一個(gè)成功即返回钙皮。通常用于實(shí)時(shí)性要求較高的讀操作,但需要浪費(fèi)更多服務(wù)資源顽决《烫酰可通過 forks="2" 來設(shè)置最大并行數(shù)。
  • Broadcast Cluster:廣播調(diào)用所有提供者才菠,逐個(gè)調(diào)用茸时,任意一臺報(bào)錯(cuò)則報(bào)錯(cuò)。通常用于通知所有提供者更新緩存或日志等本地資源信息鸠儿。

2.3.2 具體代碼

cousumer新增一個(gè)類:

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新增一個(gè)實(shí)現(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ù)消費(fèi)者在調(diào)?某個(gè)服務(wù)提供者時(shí)屹蚊,如果該服務(wù)提供者報(bào)錯(cuò)了,所采取的措施进每。

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

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

2.4.1 具體代碼

consumer新增一個(gè)類:

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ù)消費(fèi)端執(zhí)?的命斧, 這段邏輯?般都是由服務(wù)提供者提供田晚,服務(wù)提供者可以利?這種機(jī)制在服務(wù)消費(fèi)者遠(yuǎn)程調(diào)?服務(wù)提供者之前或之后再做?些其他事情,?如結(jié)果緩存国葬,請求參數(shù)驗(yàn)證等等贤徒。(AOP功能)

2.5.1 具體代碼

interface新增一個(gè)類:

package com.zyz;

public class DemoServiceStub implements DemoService {

    private final DemoService demoService;

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

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

consumer新增一個(gè)類:

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其實(shí)就是Dubbo中的服務(wù)容錯(cuò)的解決?案通孽。

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

?先序宦,如果當(dāng)前服務(wù)?持參數(shù)回調(diào),意思就是:對于某個(gè)服務(wù)接?中的某個(gè)?法背苦,如果想?持消費(fèi)者在調(diào) ?這個(gè)?法時(shí)能設(shè)置回調(diào)邏輯互捌,那么該?法就需要提供?個(gè)?參?來表示回調(diào)邏輯潘明。

因?yàn)镈ubbo協(xié)議是基于?連接的,所以消費(fèi)端在兩次調(diào)?同?個(gè)?法時(shí)想指定不同的回調(diào)邏輯秕噪,那么就需 要在調(diào)?時(shí)在指定?定key進(jìn)?區(qū)分钳降。

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

consumer新增兩個(gè)類

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

        // 用來進(jìn)行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新增一個(gè)類,并在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ù)消費(fèi)者可以調(diào)用addListener方法來添加回調(diào)對象遂填,服務(wù)提供者一旦執(zhí)行回調(diào)對象的方法就會(huì)通知給服務(wù)消費(fèi)者

@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 的非阻塞實(shí)現(xiàn)并行調(diào)用澈蝙,客戶端不需要啟動(dòng)多線程即可完成并行調(diào)用多個(gè)遠(yuǎn)程服務(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新增一個(gè)類:

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中凌唬,如果某個(gè)服務(wù)想要?持泛化調(diào)?,就可以將該服務(wù)的generic屬性設(shè)置為true漏麦,那對于服務(wù)消費(fèi)者來說客税,就可以不?依賴該服務(wù)的接?,直接利?GenericService接?來進(jìn)?服務(wù)調(diào)?撕贞。

2.9.1 具體代碼

consumer新增一個(gè)類:

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新增一個(gè)類:

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)系作者
  • 序言:七十年代末更耻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子捏膨,更是在濱河造成了極大的恐慌秧均,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件号涯,死亡現(xiàn)場離奇詭異目胡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)链快,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門誉己,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人域蜗,你說我怎么就攤上這事巨双。” “怎么了霉祸?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵筑累,是天一觀的道長。 經(jīng)常有香客問我丝蹭,道長慢宗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮婆廊,結(jié)果婚禮上迅细,老公的妹妹穿的比我還像新娘。我一直安慰自己淘邻,他們只是感情好茵典,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著宾舅,像睡著了一般统阿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上筹我,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天扶平,我揣著相機(jī)與錄音,去河邊找鬼蔬蕊。 笑死结澄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的麻献。 我是一名探鬼主播旅赢,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼雷绢!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宇立,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤他去,失蹤者是張志新(化名)和其女友劉穎媳搪,沒想到半個(gè)月后展运,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體精刷,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拗胜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了怒允。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片埂软。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖纫事,靈堂內(nèi)的尸體忽然破棺而出勘畔,到底是詐尸還是另有隱情,我是刑警寧澤丽惶,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布炫七,位于F島的核電站,受9級特大地震影響钾唬,放射性物質(zhì)發(fā)生泄漏万哪。R本人自食惡果不足惜侠驯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奕巍。 院中可真熱鬧吟策,春花似錦、人聲如沸的止。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽冲杀。三九已至效床,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間权谁,已是汗流浹背剩檀。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旺芽,地道東北人沪猴。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓魏蔗,卻偏偏與公主長得像焕参,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子痹扇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容