拉勾教育学习-笔记分享の"卍解"Dubbo

592 阅读17分钟

【文章内容输出来源:拉勾教育Java高薪训练营】
--- 所有脑图均本人制作,未经允许请勿滥用 ---
掌握Dubbo的使用 手边请拿好 官方文档 随时查阅和深入


知命不惧,日日自新;保持热爱,奔赴山海

一、项目架构演变过程

Part 1 - 单体架构

经典的MVC结构,SSM框架。所有的业务都放在一个Tomcat中。

  • 优点:

    • 开发快、成本低(很适合小型项目)
    • 易测试易部署
  • 缺点:

    • 遇到大型项目时,耦合严重,难维护,通常有多个bug同时产生

Part 2 - 垂直架构

根据业务把项目垂直切割成多个项目,因此这种架构称之为垂直架构

  • 优点:
    • 系统拆分实现了流量分担,解决了并发问题
    • 可以针对不同系统进行优化
    • 方便水平扩展,负载均衡,容错率提高
    • 系统间相互独立,互不影响,新的业务迭代时更加高效
  • 缺点:
    • 服务系统之间接口调用硬编码
    • 搭建集群之后,实现负载均衡比较复杂
    • 服务系统接口调用监控不到位 调用方式不统一
    • 服务监控不到位
    • 数据库资源浪费,充斥慢查询,主从同步延迟大

Part 3 - 分布式架构(SOA)

Service Oriented Architecture 面向服务的架构
在垂直划分的基础上,将每个项目拆分出多个具备松耦合的服务,一个服务通常以独立的形式存在于操作系统进程中。各个服务之间通过网络调用,这使得构建在各种各样的系统中的服务可以 以一种统一和通用的方式进行交互

  1. 应用层: 距离用户最近的一层 也称之为接入层 使用 tomcat 作为 web容器 接收用户请求 使用下游的dubbo提供的接口来返回数据 并且该层禁止访问数据库
  2. 业务服务层: 根据具体的业务场景演变而来的模块
  3. 基础业务层: 业务的核心
  4. 基础服务层: 与业务无关的模块,往往是一些通用的服务
  5. 存储层: 不同的存储类型 Mysql Mongodb ES fastDFS
  • 优点
    • 服务以接口为粒度,为开发者屏蔽远程调用底层细节 使用 Dubbo 面向接口远程方法调用 屏蔽了底层调用细节
    • 业务分层以后架构更加清晰 并且每个业务模块职责单一 扩展性更强
    • 数据隔离,权限回收,数据访问都通过接口 让系统更加稳定、安全
    • 服务应用本身无状态化 这里的无状态化指的是应用本身不做内存级缓存 而是把数据存入db
    • 服务责任确定 每个服务可以确定责任人 这样更容易保证服务质量和稳定
  • 缺点
    • 粒度控制复杂 如果没有控制好服务的粒度 服务的模块就会越来越多 就会引发 超时 分布式事务等问题
    • 服务接口数量不宜控制 容易引发接口爆炸 × ===> √ 所以服务接口建议以业务场景进行单位划分 并对相近的业务做抽象 防止接口爆炸
    • 版本升级兼容困难 尽量不要删除方法 字段 枚举类型的新增字段也可能不兼容
    • 调用链路长 服务质量不可监控 调用链路变长 下游抖动可能会影响到上游业务 最终形成连锁反应 服务质量不稳定 同时链路的变成使得服务质量的监控变得困难

Part 4 - 微服务架构

是在SOA上做的升华 , 粒度更加细致,微服务架构强调的一个重点是“业务需要彻底的组件化和服务化”。

详情可以在 SpringCloud章节 中阅读

二、Dubbo架构与实战

Part 1 - 架构概述

「何为Dubbo」

Apache Dubbo 是一款高性能的Java RPC框架。
其前身是阿里巴巴公司开源的一个高性能、轻量级的开源Java RPC框架,可以和Spring框架无缝集成。

使用手册

「特性」

连通性健壮性伸缩性、以及向未来架构的升级性

Dubbo gitBook 介绍特性

「服务治理」

用来管理SOA的采用和实现的过程

Part 2 - Dubbo处理流程

调用关系说明

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。

Part 3 - 服务注册中心Zookeeper

详情可以参考 拉勾教育学习-笔记分享の"解剖"Zookeeper 章节

Part 4 - Dubbo开发实战

「Demo」

参考源码

项目结构:

「接口协定」

public interface HelloService { String sayHello(String name); }

「依赖引入」

消费者和提供者都如下:

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.curator</groupId>
    <artifactId>curator-recipes</artifactId>
    <version>4.2.0</version>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-registry-zookeeper</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-rpc-dubbo</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-remoting-netty4</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-serialization-hessian2</artifactId>
</dependency>

「提供者 Provider」

接口实现类

@Service
public class GreetServiceImpl implements GreetService {
    @Override
    public String sayHello(String name) {
        return "招呼:\t" + name;
    }
}

dubbo配置

# dubbo.application.name: 当前提供者的名称
# dubbo.protocol.name: 对外提供的时候使用的协议
# dubbo.protocol.port: 该服务对外暴露的端口是什么,在消费者使用时,则会使用这个端口并且使用指定的协议与提供者建立连接。

dubbo.application.name=service-provider
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

log4j配置(可选)

log4j.rootCategory=INFO,CONSOLE

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss.SSS} [%t] %-5p %c.%M\(%F:%L\) - %m%n

启动服务的 Main入口

public class ProviderMain {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.in.read();
    }
    
    /**
     * 启动中心 的 配置类
     */
    @Configuration
    @EnableDubbo(scanBasePackages = "com.archie.service.impl")
    @PropertySource("classpath:/dubbo-provider.properties")
    static class ProviderConfiguration {
        @Bean
        public RegistryConfig registryConfig(){
            RegistryConfig registryConfig = new RegistryConfig();
            registryConfig.setAddress("zookeeper://106.75.60.49:2181"); // 此处访问自己的 zookeeper
            return registryConfig;
        }
    }
}

运行效果

「消费者 Consumer」

dubbo配置

dubbo.application.name=service-consumer
dubbo.registry.address=zookeeper://106.75.60.49:2181

log4j配置(可选)

log4j.rootCategory=INFO,CONSOLE

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{HH:mm:ss.SSS} [%t] %-5p %c.%M\(%F:%L\) - %m%n

组件(Bean)

@Component
public class ConsumerComponent {

    @Reference
    private GreetService greetService;
    
    /**
     * 调用远程 api 提供的接口
     * @return
     */
    public String sayHello(String name) {
        return greetService.sayHello(name);
    }
    
}

调用组件的 Main入口

public class ConsumerMain {
    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
        context.start();
        // 获取消费者组件
        ConsumerComponent component = context.getBean(ConsumerComponent.class);
        while (true) {
            System.in.read();
            String myGreet = component.sayHello("Hello Dubbo!");
            System.out.println(myGreet);
        }
    }
    
    /**
     * 接口消费者启动中心 的 配置类
     */
    @Configuration
    @EnableDubbo
    @PropertySource("classpath:/dubbo-consumer.properties")
    @ComponentScan(basePackages = "com.archie.bean")
    static class ConsumerConfiguration {
    
    }
}

启动效果

交互效果

「配置方式」

  1. 基于注解 -> 快速的程序配置,无需多余的配置信息,包含提供者和消费者。
  2. XML -> 和Spring做结合,相关的Service和Reference均使用Spring集成后的
  3. 基于代码 -> 使用的比较少 (更适用于自己公司对其框架与Dubbo做深度集成)

「XML方式」

参考源码

重点区别在 provider & consumer 的XML文件

dubbo-provider.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 提供方应用信息,用于计算依赖关系 -->
    <dubbo:application name="service-provider"/>

    <!-- 使用zookeeper注册中心暴露服务地址 -->
    <dubbo:registry address="zookeeper://106.75.60.49:2181"/>

     <!-- 用dubbo协议在20882端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20882" />

    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.archie.service.GreetService" ref="greetService"/>

    <!-- 和本地bean一样实现服务 -->
    <bean id="greetService" class="com.archie.service.impl.GreetServiceImpl"/>
</beans>

dubbo-consumer.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
       xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans-4.3.xsd        http://dubbo.apache.org/schema/dubbo        http://dubbo.apache.org/schema/dubbo/dubbo.xsd">

    <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
    <dubbo:application name="service-consumer" />

    <!-- 使用zookeeper注册中心暴露发现服务地址 -->
    <dubbo:registry address="zookeeper://106.75.60.49:2181"/>

    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <dubbo:reference id="greetService" interface="com.archie.service.GreetService"/>

</beans>

运行效果

Part 5 - Dubbo管理控制台

「安装」

  1. 从git 上下载项目 github.com/apache/dubb…

  2. 修改项目下的dubbo.properties文件
    注意dubbo.registry.address对应的值需要对应当前使用的Zookeeper的ip地址和端口号

  3. 切换到项目所在的路径 使用 mvn 打包
    mvn clean package -Dmaven.test.skip=true

  4. 启动target中jar包 java -jar dubbo-admin-0.0.1-SNAPSHOT.jar

「使用」

登录 localhost:7001

配置提供者 和 消费者(使用上一个基于xml的Demo)

Part 6 - Dubbo配置项说明

「简述」

  1. <dubbo:application/>对应 org.apache.dubbo.config.ApplicationConfig 代表当前应用的信息
    • name 当前应用程序的名称
    • owner 当前应用程序的负责人
    • qosEnable 是否启动QoS 默认true
    • qosPort 启动QoS绑定的端口 默认22222
    • qosAcceptForeignIp 是否允许远程访问 默认是false
  2. <dubbo:registry/> 对应 org.apache.dubbo.config.RegistryConfig 代表该模块所使用的注册中心
    • id 当前服务中provider或者consumer中存在多个注册中心时,则使用需要增加该配置
    • address 当前注册中心的访问地址
    • protocol 当前注册中心所使用的协议是什么
    • timeout 交互延时(针对多个注册中心时需延长)
  3. <dubbo:protocol/> 对应 org.apache.dubbo.config.ProtocolConfig 指定服务在进行数据传输所使用的协议
    • id 多个协议使用时,需要指定
    • name 指定协议名称。默认使用 dubbo
  4. <dubbo:service/> 对应 org.apache.dubbo.config.ServiceConfig 指定当前需要对外暴露的服务信息
    • interface 指定当前需要进行对外暴露的接口
    • ref 具体实现对象的引用
      一般我们在生产级别都是使用Spring去进行Bean托管的,所以这里面一般也指的是Spring中的BeanId
    • version 对外暴露的版本号
  5. <dubbo:reference/> 对应 org.apache.dubbo.config.ReferenceConfig 消费者的配置
    • id 该Bean在注册到Spring中的id
    • interface 服务接口名
    • version 当前服务版本
    • registry 具体使用的注册中心地址
  6. <dubbo:method/> 对应 org.apache.dubbo.config.MethodConfig 指定具体方法级别在进行RPC操作时候的配置
    • name 指定方法名称, 用于对这个方法名称的RPC调用进行特殊配置
    • async 是否异步 默认false

「dubbo:service 和 dubbo:reference」

这两个在dubbo中是我们最为常用的部分,其中有一些我们必然会接触到的属性。

  1. mock:用于在方法调用出现错误时,当做服务降级来统一对外返回结果
  2. timeout:用于指定当前方法或者接口中所有方法的超时时间
    我们一般都会根据提供者的时长来具体规定。比如我们在进行第三方服务依赖时可能会对接口的时长做放宽,防止第三方服务不稳定导致服务受损。
  3. check:用于在启动时,检查生产者是否有该服务
    我们一般都会将这个值设置为false,不让其进行检查。因为如果出现模块之间循环引用的话,那么则可能会出现相互依赖,都进行check的话,那么这两个服务永远也启动不起来。
  4. retries:用于指定当前服务在执行时出现错误或者超时时的重试机制
    1. 注意提供者是否有幂等,否则可能出现数据一致性问题
    2. 注意提供者是否有类似缓存机制,如出现大面积错误时,可能因为不停重试导致雪崩
  5. executes:用于在提供者做配置,来确保最大的并行度。
    1. 可能导致集群功能无法充分利用或者堵塞
    2. 但是也可以启动部分对应用的保护功能
    3. 可以不做配置,结合后面的熔断限流使用

「其它配置」

参考官网

三、Dubbo高级实战

Part 1 - SPI

-- Service Provider Interface -- 是JDK内置的一种 「服务提供发现机制」
使用SPI机制的优势是实现解耦,使得第三方服务模块的装配控制逻辑与调用者的业务代码分离

「Java 中的 SPI」

Java中如果想要使用SPI功能,
----> 1. 先提供标准服务接口
----> 2. 再提供相关接口实现和调用者。
这样就可以通过SPI机制中约定好的信息进行查询相应的接口实现

【遵循约定】

  1. 当服务提供者提供了接口的一种具体实现后,在META-INF/services目录下创建一个以“接口全限定名”为命名的文件,内容为实现类的全限定名
  2. 接口实现类所在的jar包放在主程序的classpath中
  3. 主程序通过java.util.ServiceLoader动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM
  4. SPI的实现类必须携带一个无参构造方法

「Dubbo 中的 SPI」

dubbo中大量的使用了SPI来作为扩展点,通过实现同一接口的前提下,可以进行定制自己的实现类。
比如比较常见的协议负载均衡,都可以通过SPI的方式进行定制化,自己扩展。
Dubbo中已经存在的所有已经实现好的扩展点。

【Dubbo中默认提供的负载均衡策略】

「Dubbo中扩展点使用方式」

源码

== A ==> api模块创建

引入依赖

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

提供接口

import org.apache.dubbo.common.extension.SPI;

/* 注意! 此处的 SPI注解是dubbo中的而非 java 中的 */
@SPI
public interface GreetService {
    String sayHello();
}
== B ==> impl模块创建

项目依赖于 api模块

具体实现类

/**
 * @description: 人类打招呼实现
 */
public class HumanGreetServiceImpl implements GreetService {
    @Override
    public String sayHello() {
        return "Hello Dubbo!";
    }
}

SPI进行声明操作,在 resources 目录下创建目录 META-INF/dubbo 目录
创建名称为 com.lagou.dubbo.study.spi.demo.api.GreetService 的文件

human=com.archie.service.impl.HumanGreetServiceImpl
== C ==> main模块创建

项目依赖于 api模块 + impl模块

public class DubboSpiMain {
    
    public static void main(String[] args) {
        // 获取扩展加载器
        ExtensionLoader<GreetService> extensionLoader = ExtensionLoader.getExtensionLoader(GreetService.class);
        // 遍历所有支持的 扩展点(classpath://META-INF.dubbo 中的配置)
        Set<String> extensions = extensionLoader.getSupportedExtensions();
        for (String extension : extensions) {
            String result = extensionLoader.getExtension(extension).sayHello();
            System.out.println("result = " + result);
        }
    
    }
    
}
== Z ==> 运行


【dubbo自己做SPI的目的】

  1. JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加 载,会很浪费资源
  2. 如果有扩展点加载失败,则所有扩展点无法使用
  3. 提供了对扩展点包装的功能(Adaptive),并且还支持通过set的方式对其他的扩展点进行注入

「Dubbo SPI中的Adaptive功能」

Dubbo中的Adaptive功能,主要解决的问题是如何动态的选择具体的扩展点

getAdaptiveExtension 统一对指定接口对应的所有扩展点进行封装,通过URL的方式对扩展点来进行动态选择。
(dubbo中所有的注册信息都是通过URL的形式进行处理的) 这里同样采用相同的方式进行实现。

  1. 扩展接口和实现类
@SPI
public interface GreetService {
    String sayHello();
    
    @Adaptive
    String  sayHello(URL url);
}
public class HumanGreetServiceImpl implements GreetService {
    @Override
    public String sayHello() {
        return "Hello Dubbo!";
    }
    
    @Override
    public String sayHello(URL url) {
        return "Hello URL";
    }
}

public class DogGreetServiceImpl implements GreetService {
    @Override
    public String sayHello() {
        return "汪汪汪 Dubbo!";
    }
    
    @Override
    public String sayHello(URL url) {
        return "汪汪汪 URL";
    }
}
  1. 配置文件指定"key"
# human 和 dog 即为 "key"
human=com.archie.service.impl.HumanGreetServiceImpl
dog=com.archie.service.impl.DogGreetServiceImpl
  1. 测试类编写
public class DubboAdaptiveMain {
    
    public static void main(String[] args) {
        // 本临时测试中 ? 前的那些可以随意, ? 后的 「greet.service」≈> GreetService
        URL urlA = URL.valueOf("test://localhost/hello?greet.service=human"); // 指向 humanServiceImpl
        URL urlB = URL.valueOf("test://localhost/hello?greet.service=dog"); // 指向 dogServiceImpl
        ExtensionLoader<GreetService> extensionLoader = ExtensionLoader.getExtensionLoader(GreetService.class);
        GreetService adaptiveExtension = extensionLoader.getAdaptiveExtension();
        System.out.println("result = " + adaptiveExtension.sayHello(urlA));
        System.out.println("result = " + adaptiveExtension.sayHello(urlB));
    }
    
}
  1. 运行

【默认实现定义】
@SPI 中指定 e.g. @SPI("dog")

「Dubbo SPI中的Filter功能」

源码

Dubbo的Filter机制,是专门为服务提供方和服务消费方调用过程进行拦截设计的,每次远程方法执行,该拦截都会被执行。
为开发者提供了非常方便的扩展性,比如为dubbo接口实现 ip白名单功能监控功能日志记录等。

  1. 实现 org.apache.dubbo.rpc.Filter 接口
  2. 使用 org.apache.dubbo.common.extension.Activate 接口进行对类进行注册 通过group 可以指定生产端 消费端
  3. 计算方法运行时间的代码实现
  4. 在 META-INF.dubbo 中新建 org.apache.dubbo.rpc.Filter 文件,并将当前类的全名写入

【注意】
一般类似于这样的功能都是单独开发依赖的,所以再使用方的项目中只需要引入依赖,在调用接口时,该方法便会自动拦截。

  • 具体使用
    • 将我们之前使用过的 无论是基于注解还是xml的项目 依赖上 该dubbo-filter项目
    • 运行consumer和provider
    • 确保zookeeper服务启动了

Part 2 - 负载均衡策略

负载均衡策略主要用于客户端存在多个提供者时进行选择某个提供者

在集群负载均衡时,Dubbo 提供了多种均衡策略

  • 随机调用(默认)
    • 随机,按权重设置随机概率。
    • 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重
  • 轮询
    • 轮询,按公约后的权重设置轮询比率。
    • 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
  • 最少活跃调用数
    • 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
    • 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大
  • 一致性 Hash
    • 一致性 Hash,相同参数的请求总是发到同一提供者。
    • 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
    • 算法参见:en.wikipedia.org/wiki/Consis…
    • 缺省只对第一个参数 Hash,如果要修改,请配置 <dubbo:parameter key="hash.arguments" value="0,1" />
    • 缺省用 160 份虚拟节点,如果要修改,请配置 <dubbo:parameter key="hash.nodes" value="320" />

既可以在服务提供者一方配置,也可以在服务消费者一方配置

//在服务消费者一方配置负载均衡策略 
@Reference(check = false,loadbalance = "random")
//在服务提供者一方配置负载均衡 
@Service(loadbalance = "random") 
public class HelloServiceImpl implements HelloService { 
	public String sayHello(String name) { 
        return "hello " + name; 
    } 
}

使用之前的 demo 代码进行测试

默认模式下的效果:(默认是 随机访问三个服务)

轮询:

一致性hash:

「自定义负载均衡器」

可以通过实现 org.apache.dubbo.rpc.cluster.LoadBalance 来实现自定义的负载均衡规则

  1. 定义负载均衡器

源码

public class OnlyFirstLoadBalancer implements LoadBalance {
    
    @Override
    public <T> Invoker<T> select(List<Invoker<T>> list, URL url, Invocation invocation) throws RpcException {
        // 所有服务按照 ip + 端口 排序后,选择第一个
        return list.stream().sorted((i1, i2) -> {
            final int ipCompare = i1.getUrl().getIp().compareTo(i2.getUrl().getIp());
            if (ipCompare == 0) {
                return Integer.compare(i1.getUrl().getPort(), i2.getUrl().getPort());
            }
            return ipCompare;
        }).findFirst().get();
    }
    
}
  1. 配置负载均衡器
onlyFirst=com.archie.loadbalance.OnlyFirstLoadBalancer
  1. 启动多个服务
    要求他们使用同一个接口注册到同一个注册中心 但是他们的dubbo通信端口不同

  1. 在服务消费方指定自定义负载均衡器

  1. 运行

Part 3 - 异步调用

【定义】
Dubbo不只提供了堵塞式的的同步调用,同时提供了异步调用的方式。

【使用场景】
提供者接口响应耗时明显,消费者端可以利用调用接口的时间去做一些其他的接口调用

【好处】
以大大的提升消费者端的利用率

「异步调用实现」

可以参考 官网

【特殊说明】
2.5.3↓ 版本不适用以上方式,会出现异步状态传递问题。

Part 4 - 线程池

  • fix: 表示创建固定大小的线程池
    也是Dubbo默认的使用方式,默认创建的执行线程数为200,并且是没有任何等待队列的。所以再极端的情况下可能会存在问题,比如某个操作大量执行时,可能存在堵塞的情况。后面也会讲相关的处理办法。
  • cache: 创建非固定大小的线程池,当线程不足时,会自动创建新的线程。
    但是使用这种的时候需要注意,如果突然有高TPS的请求过来,方法没有及时完成,则会造成大量的线程创建,对系统的CPU和负载都是压力,执行越多反而会拖慢整个系统

【实例】: 在创建线程池后进行对其监控,并且及时作出相应处理

待跟进

Part 5 - 路由规则

待跟进

Part 6 - 服务动态降级

服务降级,当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务有策略的降低服务级别,以释放服务器资源,保证核心任务的正常运行。

服务降级 ====> 防止分布式服务发生雪崩效应
====> 高并发情况下,防止因资源耗尽而导致的宕机
====> 多米诺式的整个分布式服务都瘫痪

「方式一」

在 dubbo 管理控制台配置服务降级(屏蔽和容错)

「方式二」

指定返回简单值或者null

<dubbo:reference id="xxService" check="false" interface="com.xx.XxService" timeout="3000" mock="return null" /> 
<dubbo:reference id="xxService2" check="false" interface="com.xx.XxService2" timeout="3000" mock="return 1234" />
@Reference(mock="return null")
@Reference(mock="return 简单值")
XXXService{...}

「方式三」

使用java代码 动态写入配置中心

RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://IP:端 口")); registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));

「方式四」

合整合 hystrix

后一篇SpringCloud会做出讲解