本篇是Dubbo的使用篇,為后續(xù)的源碼分析打基礎(chǔ)。
1. 最簡單的使用
開篇用一個最簡單的例子,來介紹如何用Dubbo搭建一個簡單的例子。
本例包括:
- 注冊中心(用Zookeeper代替)
- 服務(wù)提供者(provider)
- 服務(wù)消費者(consumer)
- 接口(interface)
1.1 項目結(jié)構(gòu)
創(chuàng)建項目,并創(chuàng)建三個Module,項目結(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,再啟動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;
}
}