0
点赞
收藏
分享

微信扫一扫

Dubbo相关知识点梳理

Separes 2022-01-26 阅读 116

一、Dubbo入门

1.Dubbo概念

Dubbo是一款简单易用且高性能的Java的RPC框架。Dubbo的架构图如下:

在这里插入图片描述

 

Dubbo架构具有以下特点:
(1)连通性:
  <1>注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小;
  <2>监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示;
  <3>服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销;
  <4>服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销;
  <5>注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外;
  <6>注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者;
  <7>注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表;
  <8>注册中心和监控中心都是可选的,服务消费者可以直连服务提供者。
(2)健壮性:
  <1>监控中心宕掉不影响使用,只是丢失部分采样数据;
  <2>数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务;
  <3>注册中心对等集群,任意一台宕掉后,将自动切换到另一台;
  <4>注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯;
  <5>服务提供者无状态,任意一台宕掉后,不影响使用;
  <6>服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复。
在Dubbo中,一次服务的调用流程如下:


2.Dubbo使用
Dubbo的使用步骤如下:
(1)引入dubobo相关依赖;
(2)配置dubbo框架(提供了3中配置方式);
(3)开发服务;
(4)配置服务;
(5)启动、调用。
首先引入依赖:
 

<dependencies>
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>dubbo</artifactId>
  <version>2.6.6</version>
</dependency>
  <!-- 这里我们使用netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.32.Final</version>
</dependency>
 </dependencies>

接着我们写入代码和配置:
(1)提供者端:
  <1>代码:

public class Provider {
	public static void main(String[] args) throws Exception {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("provider.xml");
		context.start();
		System.in.read(); // 按任意键退出
	}
}

<2>配置:

<?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="hello-world-app">
        <dubbo:parameter key="qos.enable" value="true"/>
        <dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
        <dubbo:parameter key="qos.port" value="33333"/>
    </dubbo:application>
    <!--<dubbo:application name="hello-world-app"/>-->
 
    <!-- 使用multicast广播注册中心暴露服务地址 -->
    <dubbo:registry address="zookeeper://192.168.70.100:2181" />
 
    <!-- 用dubbo协议在20880端口暴露服务 -->
    <dubbo:protocol name="dubbo" port="20882" />
 
    <!-- 声明需要暴露的服务接口 -->
    <dubbo:service interface="com.study.mike.dubbo.DemoService" ref="demoService" filter="dabaojian"/>
 
    <!-- 和本地bean一样实现服务(这里是本地bean的定义) -->
    <bean id="demoService" class="com.study.mike.dubbo.provider.DemoServiceImpl" />
</beans>

(2)消费者端:
  <1>代码:

public class Consumer {
	public static void main(String[] args) throws Exception {
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("consumer.xml");
		context.start();

		DemoService demoService = (DemoService) context.getBean("demoService"); // 获取远程服务代理
		String hello = demoService.sayHello("world"); // 执行远程方法
		for (int i=0;i<1000;i++){
			Thread.sleep(2000);
			System.out.println(hello); // 显示调用结果
		}
		System.out.println();
		System.out.println(demoService);
		context.close();
	}
}

<2>配置:

<?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="consumer-of-helloworld-app"  />
 
    <!-- 使用multicast广播注册中心暴露发现服务地址 -->
    <dubbo:registry address="zookeeper://192.168.70.100:2181" />

    <!--&lt;!&ndash;改成直连模式&ndash;&gt;-->
    <!--<dubbo:registry address="N/A"/>-->

    <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
    <dubbo:reference id="demoService" interface="com.study.mike.dubbo.DemoService" mock="com.study.mike.dubbo.consumer.mock.MyDemoServiceMock" filter="dabaojian"/>
</beans>

二、Dubbo注册中心

Dubbo支持多种注册中心,这里我们介绍两种:Multicast和Zookeeper。

1.Multicast注册中心

Multicast注册中心无需启动任何中心节点,只要利用路由器组播地址,即可互相发现;它的工作原理如下:

在这里插入图片描述

 流程如下:
(1)提供方启动时广播自己的地址;
(2)消费方启动时广播订阅请求;
(3)提供方收到订阅请求时,单播自己的地址给订阅者,如果设置了 unicast=false ,则广播给订阅者;
(4)消费方收到提供方地址时,连接该地址进行 RPC 调用。
【注】组播受网络结构限制,只适合小规模应用或开发阶段使用。组播地址段: 224.0.0.0 - 239.255.255.255。
配置如下:
 

<dubbo:registry address="multicast://224.5.6.7:1234" />
<!--或者-->
<dubbo:registry protocol="multicast" address="224.5.6.7:1234" />

2.Zookeeper注册中心

利用Zookeeper作为Dubbo的注册中心,工业强度较高,可用于生产环境,推荐使用;它的工作原理如下:

流程如下:
(1)服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL地址;
(2)服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL地址。并向 /dubbo/com.foo.BarService/consumers 目录下写入自己的 URL 地址;
(3)监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL地址。
配置如下:

<!--Zookeeper 单机配置-->
<dubbo:registry address="zookeeper://10.20.153.10:2181" />
<!--Zookeeper 集群配置-->
<dubbo:registry address="zookeeper://10.20.153.10:2181?
backup=10.20.153.11:2181,10.20.153.12:2181" />
1
2
3
4
5
三、Dubbo多协议
Dubbo支持多种协议,这里我们主要介绍dubbo协议并且介绍一下其他协议的特性。

1.Dubbo协议
Dubbo 缺省协议采用单一长连接和 NIO 异步通讯,适合于小数据量大并发的服务调用,以及服务消费者机器数远大于服务提供者机器数的情况。反之,Dubbo 缺省协议不适合传送大数据量的服务,比如传文件,传视频等,除非请求量很低。
Dubbo协议特性如下:
(1)连接个数:单连接;
(2)连接方式:长连接;
(3)传输协议:TCP;
(4)传输方式:NIO 异步传输;
(5)序列化:Hessian 二进制序列化;
(6)适用范围:传入传出参数数据包较小(建议小于100K),消费者比提供者个数多,单一消费者无法压满提供者,尽量不要用 dubbo 协议传输大文件或超大字符串;
(7)适用场景:常规远程服务方法调用。
Dubbo协议有以下约束:
(1)参数及返回值需实现 Serializable 接口;
(2)参数及返回值不能自定义实现 List , Map , Number , Date , Calendar 等接口,只能用JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失;
(3)Hessian 序列化,只传成员属性值和值的类型,不传方法或静态变量,兼容情况。
Dubbo协议配置如下:

<dubbo:protocol name="dubbo" port="20880" />
<!--配置协议选项如下-->
<dubbo:protocol name=“dubbo” port=“9090” server=“netty” client=“netty” codec=“dubbo” serialization=“hessian2” charset=“UTF-8” threadpool=“fixed” threads=“100” queues=“0” iothreads=“9” buffer=“8192” accepts=“1000” payload=“8388608” />
1
2
3
Dubbo 协议缺省每服务每提供者每消费者使用单一长连接,如果数据量较大,可以使用多个连接,如下:

<dubbo:service connections="1"/>
<dubbo:reference connections="1"/>
1
2
为防止被大量连接撑挂,可在服务提供方限制大接收连接数,以实现服务提供方自我保护,可如下配置:

<dubbo:protocol name="dubbo" accepts="1000" />
1
在Dubbo协议中可以使用高效的Java序列化,包括Kryo和FST,使用Kryo和FST非常简单,只需要在dubbo RPC的XML配置中添加一个属性即可:

<dubbo:protocol name="dubbo" serialization="kryo"/>
<dubbo:protocol name="dubbo" serialization="fst"/>
1
2
2.其他协议
(一)rmi协议:
特性如下:
(1)连接个数:多连接;
(2)连接方式:短连接;
(3)传输协议:TCP;
(4)传输方式:同步传输;
(5)序列化:Java 标准二进制序列化;
(6)适用范围:传入传出参数数据包大小混合,消费者与提供者个数差不多,可传文件;
(7)适用场景:常规远程服务方法调用,与原生RMI服务互操作。
约束如下:
(1)参数及返回值需实现 Serializable 接口;
(2)dubbo 配置中的超时时间对 RMI 无效,需使用 java 启动参数设置:java -Dsun.rmi.transport.tcp.responseTimeout=3000。
(二)hessian协议:
特性如下:
(1)连接个数:多连接;
(2)连接方式:短连接;
(3)传输协议:HTTP;
(4)传输方式:同步传输;
(5)序列化:Hessian二进制序列化;
(6)适用范围:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件;
(7)适用场景:页面传输,文件传输,或与原生hessian服务互操作。
约束如下:
(1)参数及返回值需实现 Serializable 接口;
(2)参数及返回值不能自定义实现 List , Map , Number , Date , Calendar等接口,只能用JDK 自带的实现,因为 hessian 会做特殊处理,自定义实现类中的属性值都会丢失。
(三)http协议:
特性如下:
(1)连接个数:多连接;
(2)连接方式:短连接;
(3)传输协议:HTTP;
(4)传输方式:同步传输;
(5)序列化:表单序列化;
(6)适用范围:传入传出参数数据包大小混合,提供者比消费者个数多,可用浏览器查看,可用表单或URL传入参数,暂不支持传文件;
(7)适用场景:需同时给应用程序和浏览器 JS 使用的服务。
约束如下:
(1)参数及返回值需符合 Bean 规范。

3.多协议使用
Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。
(1)不同服务使用不同协议:

<?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="world"  />
  <dubbo:registry id="registry" address="10.20.141.150:9090"
username="admin" password="hello1234" />
  <!-- 多协议配置 -->
  <dubbo:protocol name="dubbo" port="20880" />
  <dubbo:protocol name="rmi" port="1099" />
  <!-- 使用dubbo协议暴露服务 -->
  <dubbo:service interface="com.alibaba.hello.api.HelloService"
version="1.0.0" ref="helloService" protocol="dubbo" />
  <!-- 使用rmi协议暴露服务 -->
  <dubbo:service interface="com.alibaba.hello.api.DemoService"
version="1.0.0" ref="demoService" protocol="rmi" />
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
(2)同一个服务使用不同协议:

<?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="world"  />
  <dubbo:registry id="registry" address="10.20.141.150:9090"
username="admin" password="hello1234" />
  <!-- 多协议配置 -->
  <dubbo:protocol name="dubbo" port="20880" />
  <dubbo:protocol name="hessian" port="8080" />
  <!-- 使用多个协议暴露服务 -->
  <dubbo:service id="helloService"
interface="com.alibaba.hello.api.HelloService" version="1.0.0"
protocol="dubbo,hessian" />
</beans>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
四、Dubbo服务特性
1.本地伪装
本地伪装通常用于服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过 Mock 数据返回授权失败;当提供者端抛出RpcException异常时,客户端会调用本地伪装;按如下配置:

<!-- 设置mock为true,会调用BarServiceMock的服务(需自行准备) -->
<dubbo:reference interface="com.foo.BarService" mock="true" />
<!-- 或者指定某个服务(需自行准备)使用本地伪装 -->
<dubbo:reference interface="com.foo.BarService" mock="com.foo.BarServiceMock" />
1
2
3
4
本地伪装还有如下一些进阶用法:
(1)return:
使用 return 来返回一个字符串表示的对象,作为 Mock 的返回值。合法的字符串可以是:
  <1>empty:代表空,基本类型的默认值,或者集合类的空值;
  <2>null:null;
  <3>true:true;
  <4>false:false;
  <5>JSON格式:反序列化 JSON 所得到的对象。
(2)throw:
使用 throw 来返回一个 Exception 对象,作为 Mock 的返回值;当调用出错时,抛出一个默认的 RpcException:

<dubbo:reference interface="com.foo.BarService" mock="throw" />
1
当调用出错时,抛出指定的 Exception:

<dubbo:reference interface="com.foo.BarService" mock="throw com.foo.MockException" />
1
(3)force 和 fail:
在2.6.6以上的版本,可以开始在Spring XML配置文件中使用fail:和force:。force:代表强制使用Mock行为,在这种情况下不会走远程调用;fail:与默认行为一致,只有当远程调用发生错误时才使用Mock行为。force:和fail:都支持与throw或者return组合使用。
强制返回指定值:

<dubbo:reference interface="com.foo.BarService" mock="force:return fake" />
1
强制抛出指定异常:

<dubbo:reference interface="com.foo.BarService" mock="force:throw com.foo.MockException" />
1
(4)在方法级别配置 Mock:
Mock可以在方法级别上指定,假定com.foo.BarService上有好几个方法,我们可以单独为sayHello()方法指定Mock行为。具体配置如下所示,在本例中,只要sayHello()被调用到时,强制返回"fake":

<dubbo:reference id="demoService" check="false" interface="com.foo.BarService">
  <dubbo:parameter key="sayHello.mock" value="force:return fake"/>
</dubbo:reference>
1
2
3
2.服务降级
可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略;本地伪装就是服务降级的一种实现手段。向注册中心写入动态配置覆盖规则:

RegistryFactory registryFactory =
ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExten
sion();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("override://0.0.0.0/com.foo.BarService?category=configurators&dynamic=false&application=foo&mock=force:return+null"));
1
2
3
4
5
【注】以上写法相当于把注册中心中提供者给人为改成新的提供者了。

3.本地存根
远程服务后,客户端通常只剩下接口,而实现全在服务器端,但提供方有些时候想在客户端也执行部分逻辑,比如:做ThreadLocal缓存,提前验证参数,调用失败后伪造容错数据等等,此时就需要在API中带上Stub,客户端生成Proxy实例,会把Proxy通过构造函数传给Stub,然后把Stub暴露给用户,Stub可决定要不要去调Proxy。本地存根原理如下:
首先在spring配置文件中按以下方式配置:

<dubbo:service interface="com.foo.BarService" stub="true" />
<!-- 或者 -->
<dubbo:service interface="com.foo.BarService" stub="com.foo.BarServiceStub" />
1
2
3
在interface旁边放一个Stub实现,它实现BarService接口,并有一个传入远程BarService实例的构造函数;提供Stub的实现如下:

package com.foo;
public class BarServiceStub implements BarService {
  private final BarService barService;
  // 构造函数传入真正的远程代理对象
  public BarServiceStub(BarService barService){
    this.barService = barService;
 }
  public String sayHello(String name) {
    // 此代码在客户端执行, 你可以在客户端做ThreadLocal本地缓存,或预先验证参数是否合法,等等
    try {
      return barService.sayHello(name);
   } catch (Exception e) {
      // 你可以容错,可以做任何AOP拦截事项
      return "容错数据";
   }
 }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
4.负载均衡
在集群负载均衡时,Dubbo提供了多种均衡策略,缺省为random随机调用。原理如下:

上图中负载均衡过程如下:
(1)有多个provider提供同一种服务,它们都注册到ZK中;
(2)当consumer要调用时,去ZK中寻找provider,会得到provider的列表;
(3)接着consumer去route中从所有的provider中筛选出一部分;
(4)然后再这些provider中利用一定的负载均衡机制选出一个要调用的provider;
(5)最后consumer调用provider。
负载均衡策略如下:
(1)随机:按权重设置随机概率;
(2)轮询:按公约后的权重设置轮询比率;
(3)最少活跃调用数:相同活跃数的随机,活跃数指调用前后计数差;
(4)一致性Hash:能使相同参数的请求总是发到同一提供者。

5.集群容错
在集群调用失败时,Dubbo提供了多种容错方案,缺省为failover重试。各种集群容错模式如下:
(1)Failover Cluster:失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过retries="2"来设置重试次数(不含第一次);
(2)Failfast Cluster:快速失败,只发起一次调用,失败立即报错。通常用于非幂等性的写操作,比如新增记录;
(3)Failsafe Cluster:失败安全,出现异常时,直接忽略。通常用于写入审计日志等操作;
(4)Failback Cluster:失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作;
(5)Forking Cluster:并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过forks="2"来设置最大并行数;
(6)Broadcast Cluster:广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
按照以下示例在服务提供方和消费方配置集群模式:

<dubbo:service cluster="failsafe" />
<dubbo:reference cluster="failsafe" />
1
2
五、Dubbo其他特性
1.线程模型
Dubbo中有两个线程池的概念:
(1)I/O线程池:netty实现中,I/O线程池专门用来接收请求;
(2)业务线程池:I/O线程池接收请求后发给业务线程池处理业务相关的代码。
如果用I/O线程处理事件,又在事件处理过程中发起新的I/O请求,比如在连接事件中发起登录请求,会报“可能引发死锁”异常(但不会真死锁);因此,需要通过不同的派发策略和不同的线程池配置的组合来应对不同的场景,通过配置dispatcher来设置不同的线程分发策略,如下:

<dubbo:protocol name="dubbo" dispatcher="all" threadpool="fixed" threads="100" />
1
dispatcher有如下各种选项:
(1)all:所有消息都派发到线程池,包括请求,响应,连接事件,断开事件,心跳等;
(2)direct:所有消息都不派发到线程池,全部在I/O线程上直接执行;
(3)message:只有请求响应消息派发到线程池,其它连接断开事件,心跳等消息,直接在I/O线程上执行;
(4)execution:只请求消息派发到线程池,不含响应,响应和其它连接断开事件,心跳等消息,直接在I/O线程上执行;
(5)connection:在I/O线程上,将连接断开事件放入队列,有序逐个执行,其它消息派发到线程池。
threadpool有如下各种选项:
(1)fixed:固定大小线程池,启动时建立线程,不关闭,一直持有(缺省);
(2)cached:缓存线程池,空闲一分钟自动删除,需要时重建;
(3)limited:可伸缩线程池,但池中的线程数只会增长不会收缩;只增长不收缩的目的是为了避免收缩时突然来了大流量引起的性能问题;
(4)eager:优先创建Worker线程池。在任务数量大于corePoolSize但是小于maximumPoolSize时,优先创建Worker来处理任务。当任务数量大于maximumPoolSize时,将任务放入阻塞队列中。阻塞队列充满时抛出RejectedExecutionException(相比于cached,cached在任务数量超过maximumPoolSize时直接抛出异常而不是将任务放入阻塞队列)。

2.并发控制
见如下几个案例:
(1)限制com.foo.BarService的每个方法,服务器端并发执行(或占用线程池线程数)不能超过 10 个:

<dubbo:service interface="com.foo.BarService" executes="10" />
1
(2)限制com.foo.BarService的sayHello方法,服务器端并发执行(或占用线程池线程数)不能超过10个:

<dubbo:service interface="com.foo.BarService">
  <dubbo:method name="sayHello" executes="10" />
</dubbo:service>
1
2
3
(3)限制com.foo.BarService的每个方法,每客户端并发执行(或占用连接的请求数)不能超过10个:

<dubbo:service interface="com.foo.BarService" actives="10" />
<!-- 或者 -->
<dubbo:reference interface="com.foo.BarService" actives="10" />
1
2
3
(4)限制com.foo.BarService的sayHello方法,每客户端并发执行(或占用连接的请求数)不能超过 10 个:

<dubbo:service interface="com.foo.BarService">
  <dubbo:method name="sayHello" actives="10" />
</dubbo:service>
<!-- 或者 -->
<dubbo:reference interface="com.foo.BarService">
  <dubbo:method name="sayHello" actives="10" />
</dubbo:service>
1
2
3
4
5
6
7
3.连接控制
(1)服务端连接控制(如限制服务器端接受的连接不能超过10个):

<dubbo:provider protocol="dubbo" accepts="10" />
<!-- 或者 -->
<dubbo:protocol name="dubbo" accepts="10" />
1
2
3
(2)客户端连接控制:

<dubbo:reference interface="com.foo.BarService" connections="10" />
<!-- 或者 -->
<dubbo:service interface="com.foo.BarService" connections="10" />
1
2
3
4.日志适配
自2.2.1开始,Dubbo开始内置log4j、slf4j、jcl、jdk这些日志框架的适配,也可以通过以下方式显示配置日志输出策略:

<dubbo:application logger="log4j" />
1
5.配置加载流程
配置来源默认有四种:
(1)JVM System Properties,-D参数;
(2)Externalized Configuration,外部化配置;
(3)ServiceConfig、ReferenceConfig等编程接口采集的配置;
(4)本地配置文件dubbo.properties。

6.调用拦截扩展
服务提供方和服务消费方调用过程拦截,Dubbo本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。
扩展有如下约定:
(1)用户自定义filter默认在内置filter之后;
(2)特殊值default,表示缺省扩展点插入的位置。比如:filter=“xxx,default,yyy”,表示xxx在缺省filter之前,yyy在缺省filter之后;
(3)特殊符号-,表示剔除。比如:filter="-foo1",剔除添加缺省扩展点foo1。比如:filter="-default",剔除添加所有缺省扩展点;
(4)provider和service同时配置的filter时,累加所有filter,而不是覆盖。比如:<dubbo:provider filter="xxx,yyy"/>和<dubbo:service filter="aaa,bbb" />,则xxx、yyy、aaa和bbb均会生效。如果要覆盖,需配置<dubbo:service filter="-xxx,-yyy,aaa,bbb" />
实现自定义扩展过程如下:
(1)首先实现com.alibaba.dubbo.rpc.Filter接口:

public class MyFilter implements Filter {
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        System.out.printf("过滤器前置处理");//自定义的前置处理
        Result result = invoker.invoke(invocation);//真正执行的代码
        System.out.printf("过滤器后置处理");//自定义的后置处理
        return result;
    }
}
1
2
3
4
5
6
7
8
9
(2)然后去META-INF/dubbo文件夹下配置文件com.alibaba.dubbo.rpc.Filter并写如下内容:

过滤器名=上面实现com.alibaba.dubbo.rpc.Filter接口的包名
1
(3)最后在消费者端和提供者端都可以配置过滤器:

<!-- 消费方调用过程拦截 -->
<dubbo:reference filter="xxx,yyy" />
<!-- 消费方调用过程缺省拦截器,将拦截所有reference -->
<dubbo:consumer filter="xxx,yyy"/>
<!-- 提供方调用过程拦截 -->
<dubbo:service filter="xxx,yyy" />
<!-- 提供方调用过程缺省拦截器,将拦截所有service -->
<dubbo:provider filter="xxx,yyy"/>
1
2
3
4
5
6
7
8
六、Dubbo最佳实践
1.推荐用法
【注】推荐用法官方文档

2.服务化最佳实践
【注】服务化最佳实践官方文档

3.容量规划
容量规划可以按以下案例作参考:
(1)案例一、使用Dubbo的会员服务项目:
  <1>每天接收 4 亿次远程调用;
  <2>使用 12 台网站标配机器提供服务(8 核 CPU,8G 内存);
  <3>平均负载在 1 以下(对于 8 核 CPU 负载很低);
  <4>平均响应时间 2.3 到 2.5 毫秒,网络开销约占 1.5 到 1.6 毫秒(和数据包大小有关)。
(2)案例二、使用Dubbo的产品授权服务项目:
  <1>每天接收 3 亿次远程调用;
  <2>使用 8 台网站标配机器提供服务(8 核CPU,8G 内存);
  <3>平均负载在 1 以下(对于 8 核 CPU 负载很低);
  <4>平均响应时间 1.4 到 2.8 毫秒,网络开销约占 1.0 到 1.1 毫秒(和数据包大小有关)。

4.优雅停机
Dubbo是通过JDK的ShutdownHook来完成优雅停机的,所以如果用户使用kill -9 PID等强制关闭指令,是不会执行优雅停机的,只有通过kill PID时,才会执行。
优雅停机原理如下:
(1)服务提供者:
  <1>停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器;
  <2>然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。
(2)服务消费者:
  <1>停止时,不再发起新的调用请求,所有新的调用在客户端即报错;
  <2>然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
设置优雅停机方式如下:

dubbo.service.shutdown.wait=15000
1
七、Dubbo服务治理
1.治理行为
随着服务间依赖关系变得错综复杂,甚至分不清哪个应用在谁之前启动;服务调用量变大,加多少机器,何时加机器成为问题;所以需要服务治理。
我们可以安装dubbo-admin项目,服务的治理行为包括:
(1)应用级别的服务治理;
(2)标签路由;
(3)条件路由;
(4)黑白名单;
(5)动态配置;
(6)权重调节;
(7)负载均衡。

2.路由配置
路由规则在发起一次RPC调用前起到过滤目标服务器地址的作用,过滤后的地址列表,将作为消费端最终发起RPC调用的备选地址。
有如下的路由规则:
(一)条件路由:
(1)应用粒度:

# app1的消费者只能消费所有端口为20880的服务实例
# app2的消费者只能消费所有端口为20881的服务实例
---
scope: application
force: true
runtime: true
enabled: true
key: governance-conditionrouter-consumer
conditions:
- application=app1 => address=*:20880
- application=app2 => address=*:20881
...
1
2
3
4
5
6
7
8
9
10
11
12
(2)服务粒度:

# DemoService的sayHello方法只能消费所有端口为20880的服务实例
# DemoService的sayHi方法只能消费所有端口为20881的服务实例
---
scope: service
force: true
runtime: true
enabled: true
key: org.apache.dubbo.samples.governance.api.DemoService
conditions:
- method=sayHello => address=*:20880
- method=sayHi => address=*:20881
...
1
2
3
4
5
6
7
8
9
10
11
12
规则解释如下:
(1)scope: 表示路由规则的作用粒度,scope的取值会决定key的取值,必填;
(2)Key: 明确规则体作用在哪个服务或应用,必填;
(3)enabled: 当前路由规则是否生效,可不填,缺省生效;
(4)force: 当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为false;
(5)runtime: 是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为true,需要注意设置会影响调用的性能,可不填,缺省为false;
(6)priority: 路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为0 ;
(7)conditions: 定义具体的路由规则内容,必填。
对于conditions有如下说明:


以下是一些conditions的使用示例:
(1)排除预发布机:

=> host != 172.22.3.91
1
(2)白名单:

host != 10.20.153.10,10.20.153.11 =>
1
(3)黑名单

host = 10.20.153.10,10.20.153.11 =>
1
(4)服务寄宿在应用上,只暴露一部分的机器,防止整个集群挂掉:

=> host = 172.22.3.1*,172.22.3.2*
1
(5)为重要应用提供额外的机器:

application != kylin => host != 172.22.3.95,172.22.3.96
1
(6)读写分离:

method = find*,list*,get*,is* => host = 172.22.3.94,172.22.3.95,172.22.3.96
method != find*,list*,get*,is* => host = 172.22.3.97,172.22.3.98
1
2
(7)前后台分离:

application = bops => host = 172.22.3.91,172.22.3.92,172.22.3.93
application != bops => host = 172.22.3.94,172.22.3.95,172.22.3.96
1
2
(8)隔离不同机房网段

host != 172.22.3.* => host != 172.22.3.*
1
(9)提供者与消费者部署在同集群内,本机只访问本机的服务:

=> host = $host
1
(二)标签路由:
标签路由通过将某一个或多个服务的提供者划分到同一个分组,约束流量只在指定分组中流转,从而实现流量隔离的目的,可以作为蓝绿发布、灰度发布等场景的能力基础。
(1)提供者端:
标签主要是指对Provider端应用实例的分组,目前有两种方式可以完成实例分组,分别是动态规则打标和静态规则打标 ,其中动态规则相较于静态规则优先级更高,而当两种规则同时存在且出现冲突时,将以动态规则为准。其中:
  <1>动态规则打标:

# governance-tagrouter-provider应用增加了两个标签分组tag1和tag2
# tag1包含一个实例 127.0.0.1:20880
# tag2包含一个实例 127.0.0.1:20881
---
force: false
runtime: true
enabled: true
key: governance-tagrouter-provider
tags:
- name: tag1
addresses: ["127.0.0.1:20880"]
- name: tag2
addresses: ["127.0.0.1:20881"]
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  <2>静态规则打标:

<dubbo:provider tag="tag1"/>
<!-- 或者 -->
<dubbo:service tag="tag1"/>
1
2
3
(2)消费者端:
只需指定用哪个标签即可,示例如下:

RpcContext.getContext().setAttachment(Constants.REQUEST_TAG_KEY,"tag1");
1
八、Dubbo工作原理
Dubbo的工作原理图如下所示:

在这里插入图片描述

举报

相关推荐

MySQL相关知识点梳理

Redis知识点梳理

webpack 知识点梳理

Flutter相关知识点

MQ相关知识点

0 条评论