1、Dubbo基础
一、Dubbo基础架构
整个发布-订阅的过程简单描述如下:
启动容器,加载,运行服务提供者。
服务提供者在启动时,在注册中心发布注册自己提供的服务。
服务消费者在启动时,在注册中心订阅自己所需的服务。
如果考虑失败或变更的情况,就需要考虑下面的过程:
注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
1.1、开源RPC框架对比(仅为参考)
二、Dubbo基本应用
2.1、项目准备
dubbo父工程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>com.zender</groupId>
<artifactId>dubbo</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>provider</module>
<module>interface</module>
<module>consumer</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<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>interface工程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"> <parent> <artifactId>dubbo</artifactId> <groupId>com.zender</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>interface</artifactId> </project>
consumer工程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">
<parent>
<artifactId>dubbo</artifactId>
<groupId>com.zender</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>consumer</artifactId>
<dependencies>
<dependency>
<groupId>com.zender</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>配置文件application.yml
spring: application: name: dubbo-consumer-demo server: port: 8082 dubbo: registry: address: zookeeper://127.0.0.1:2181
provider工程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">
<parent>
<artifactId>dubbo</artifactId>
<groupId>com.zender</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>provider</artifactId>
<dependencies>
<dependency>
<groupId>com.zender</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>配置文件application.properties
# Spring boot application
spring.application.name=dubbo-provider-demo
server.port=8081
# 扫描包路径
dubbo.scan.base-packages=com.zender.provider.service.impl
# 应用名称
dubbo.application.name=${spring.application.name}
## 注册中心地址
dubbo.registry.address=zookeeper://127.0.0.1:2181
# Dubbo协议端口号
dubbo.protocol.name=dubbo
dubbo.protocol.port=208802.2、DefaultDemo
public interface DemoService {
String sayHello(String name);
}provider工程(服务提供者),实现DemoService接口
package com.zender.provider.service.impl;
import com.zender.service.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 DefaultDemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
System.out.println("执行了服务:" + name);
URL url = RpcContext.getContext().getUrl();
return String.format("%s:%s, Hello, %s", url.getProtocol(), url.getPort(), name);
}
}启动类
//服务提供者
@SpringBootApplication
public class ProviderStart {
public static void main(String[] args) {
SpringApplication.run(ProviderStart.class, args);
}
}
//服务消费者
@SpringBootApplication
public class ConsumerStart implements WebMvcConfigurer {
@Reference(version = "default")
private DemoService demoService;
public static void main(String[] args) throws IOException {
ConfigurableApplicationContext context = SpringApplication.run(ConsumerStart.class);
DemoService demoService = context.getBean(DemoService.class);
System.out.println((demoService.sayHello("Dubbo")));
}
}2.3、负载均衡
//定义服务的时候配置 @Service(version = "default", loadbalance = "roundrobin") //消费者,这里会按消费者配置的为主(通常配置在消费端) @Reference(version = "default", loadbalance = "roundrobin") //或者 <dubbo:service interface="..." loadbalance="roundrobin"/> //或者 <dubbo:reference interface="..." loadbalance="roundrobin"/>
随机负载均衡(Random LoadBalance)
轮询负载均衡(RoundRobin LoadBalance)
最少活跃调用数负载均衡(LeastActive LoadBalance)
消费者会缓存所调⽤服务的所有提供者,⽐如记为p1、p2、p3三个服务提供者,每个提供者内都个属性记为active,默认位0。
消费者在调⽤次服务时,如果负载均衡策略是leastactive。
消费者端会判断缓存的所有服务提供者的active,选择最⼩的,如果都相同,则随机。
选出某⼀个服务提供者后,假设位p2,Dubbo就会对p2.active+1。
然后真正发出请求调⽤该服务,消费端收到响应结果后,对p2.active-1。
这样就完成了对某个服务提供者当前活跃调⽤数进⾏了统计并且并不影响服务调⽤的。
一致性Hash算法(ConsistentHash LoadBalance)
<dubbo:parameter key="hash.arguments" value="0,1" /> <dubbo:parameter key="hash.nodes" value="320" />
2.4、服务超时
消费者发送请求(⽹络传输)。
服务端执⾏服务。
服务端返回响应(⽹络传输)。
服务执⾏为5s。
消费端timeout=3s。
服务端timeout=6s。
@Reference(version = "timeout", timeout = 3000) //或者 @Service(version = "timeout", timeout = 4000)
2.4、集群容错
各节点关系:
这里的
Invoker是Provider的一个可调用Service的抽象,Invoker封装了Provider地址及Service接口信息。Directory代表多个Invoker,可以把它看成List<Invoker>,但与List不同的是,它的值可能是动态变化的,比如注册中心推送变更。Cluster将Directory中的多个Invoker伪装成一个Invoker,对上层透明,伪装过程包含了容错逻辑,调用失败后,重试另一个。Router负责从多个Invoker中按路由规则选出子集,比如读写分离,应用隔离等。LoadBalance负责从多个Invoker中选出具体的一个用于本次调用,选的过程包含了负载均衡算法,调用失败后,需要重选。
Failover Cluster
失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。
重试次数配置如下:
<dubbo:service retries="2" /> 或者 <dubbo:reference retries="2" /> 或者 <dubbo:reference> <dubbo:method name="findFoo" retries="2" /> </dubbo:reference>
Failfast Cluster
Failsafe Cluster
Failback Cluster
Forking Cluster
Broadcast Cluster
@reference(cluster = "broadcast", parameters = {"broadcast.fail.percent", "20"})2.5、服务降级
//调用出错了,直接返回123 @Reference(version = "timeout", timeout = 1000, mock = "fail: return 123") //调用直接返回123 @Reference(version = "timeout", timeout = 1000, mock = "force: return 123")
2.6、本地存根
@Reference(version = "timeout", timeout = 1000, stub = "true") private DemoService demoService;
stub对应的DemoServiceStub类
public class DemoServiceStub implements DemoService {
private final DemoService demoService;
// 构造函数传入真正的远程代理对象
public DemoServiceStub(DemoService demoService){
this.demoService = demoService;
}
@Override
public String sayHello(String name) {
// 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
try {
return demoService.sayHello(name); // safe null
} catch (Exception e) {
// 你可以容错,可以做任何AOP拦截事项
return "容错数据";
}
}
}2.7、本地伪装
2.8、参数回调
public interface CallbackService {
String getName(String name);
//回调方法
default String getName(String name,String key,CallbackServiceListener callbackServiceListener){
return null;
}
}
public interface CallbackServiceListener {
void changed(String data);
}
public class CallbackServiceListenerImpl implements CallbackServiceListener {
@Override
public void changed(String data) {
System.out.println("changed:"+data);
}
}provider工程添加服务
@Service(version = "callback",
methods = {@Method(name="siteName",arguments = {@Argument(index=2,callback = true)})},
callbacks = 3)
public class CallbackServiceImpl implements CallbackService {
@Override
public String getName(String name) {
return null;
}
@Override
public String getName(String name, String key, CallbackServiceListener callbackServiceListener) {
callbackServiceListener.changed("provider data");
return "callback:key="+key+",name="+name;
}
}consumer工程
@RestController
public class CallbackController {
@Reference(version = "callback")
CallbackService callbackService;
@RequestMapping("/callback")
public String getName(@RequestParam("name") String name){
return callbackService.getName(name,"key1",new CallbackServiceListenerImpl());
}
}提供方的控制台可以看到如下信息:
版权声明
非特殊说明,本文由Zender原创或收集发布,欢迎转载。
ZENDER






发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。