本篇是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)如下:
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
先啟動(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;
}
}