目录
    
   
    1、dubbo-admin-2.7.x版本安装部署
   
    1.1 下载源码
   
dubbo-admin-2.5.x版本是现阶段官方推荐应用到生产的,但是缺陷太多,特别是对于服务治理这一块,2.7.x版本完善了很多,但是官方并没有推荐生产使用,不过我觉得能不能用于生产,根据自己的情况来,够用就行(bug很多,但是留有对应的后门API)
    先下载dubbo-admin-2.7.x源码:
    
     https://github.com/apache/dubbo-admin
    
   
    官网地址:
    
     http://dubbo.apache.org/zh-cn/docs/2.7/user/quick-start/
    
   
    
   
2.7版本作者基于springboot开发的,而且做了前后端分离!
    1.2 部署访问
   
修改zookeeper地址配置,找到\dubbo-admin-develop\dubbo-admin-server\src\main\resources\application.properties:
admin.registry.address=zookeeper://192.168.223.128:2181
admin.config-center=zookeeper://192.168.223.128:2181
admin.metadata-report.address=zookeeper://192.168.223.128:2181
admin.root.user.name=root
admin.root.user.password=root
在主目录dubbo-admin-develop目录下,执行mvn clean package -Dmaven.test.skip=true,第一次会比较慢,因为前端使用了vue.js和node.js,所以你本地没有安装npm的会自定下载安装,慢慢等:
    
   
因为使用了springboot,所以不再依赖tomcat容器,有两种方式启动
一种方法启动 :
mvn --projects dubbo-admin-server spring-boot:run 
二种方法启动:
cd dubbo-admin-distribution/target
java -jar dubbo-admin-0.1.jar
    
   
    访问测试:
    
     http://localhost:8080
    
   
    
   
    2、路由规则
   
    2.1 Dubbo API配置
   
    
     需求背景:
    
   
    我们现在有一个
    
     服务A
    
    , 需要暴露在同一个zookeeper上面, 但是注册到zookeeper的地址需要有公网和内网两种, 但是我又不想在代码中做修改.
   
    
     需求说明:
    
   
    同一个服务需要提供内网和公网地址的原因是: 我们有
    
     服务B
    
    所在的机器只能通过外网去访问
    
     服务A
    
    , 但是外网访问会受到带宽的限制. 但是
    
     服务A
    
    的请求量又很大, 因此, 我就希望除了
    
     服务B
    
    以外的机器都通过内网去访问服务A, 这样就
    
     解决了带宽上面的限制
    
    .
   
    但是, 服务A的集群是在同一个zookeeper下面的. 所以, 我就必须指定
    
     服务B
    
    去访问服务A的外网地址的dubbo服务.
   
    这里有一个方案: 就是我使用两个不同的zookeeper, 外网地址的
    
     服务A
    
    注册到zookeeper-1上面, 内网地址的注册到zookeeper-2上面. 这样也是可以解决上面指定
    
     服务B
    
    访问
    
     服务A
    
    的问题.
   
但是, 我们上了监控, 监控是监控一个zookeeper地址的服务, 所以, 我就不能有多个zookeeper, 这样会加大复杂度. 这个时候, 路由规则是一个好东西
路由规则在发起一次RPC调用前起到过滤目标服务器地址的作用,过滤后的地址列表,将作为消费端最终发起RPC调用的备选地址。
- 
条件路由。支持以服务或Consumer应用为粒度配置路由规则。
 - 
标签路由。以Provider应用为粒度配置路由规则。
 
@Test
public void test() throws UnknownHostException {
    RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
    Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://192.168.223.128:2181"));
    registry.register(URL.valueOf("condition://0.0.0.0/com.ydt.dubbo.service.LoadBalanceService?category=routers&dynamic=false&rule="
            + URL.encode("host = 192.168.223.* => host = 192.168.224.*")));
}
其中:
- 
route://
表示路由规则的类型,支持条件路由规则和脚本路由规则,可扩展,
必填
。 - 
0.0.0.0
表示对所有 IP 地址生效,如果只想对某个 IP 的生效,请填入具体 IP,
必填
。 - 
com.ydt.dubbo.service.LoadBalanceService
表示只对指定服务生效,
必填
。 - 
group=loadbalance
对指定服务的指定group生效,不填表示对未配置group的指定服务生效 - 
version=1.0
对指定服务的指定version生效,不填表示对未配置version的指定服务生效 - 
category=routers
表示该数据为动态配置类型,
必填
。 - 
dynamic=false
表示该数据为持久数据,当注册方退出时,数据依然保存在注册中心,
必填
。 - 
enabled=true
覆盖规则是否生效,可不填,缺省生效。 - 
force=false
当路由结果为空时,是否强制执行,如果不强制执行,路由结果为空的路由规则将自动失效,可不填,缺省为
false
。 - 
runtime=false
是否在每次调用时执行路由规则,否则只在提供者地址列表变更时预先执行并缓存结果,调用时直接从缓存中获取路由结果。如果用了参数路由,必须设为
true
,需要注意设置会影响调用的性能,可不填,缺省为
false
。 - 
priority=1
路由规则的优先级,用于排序,优先级越大越靠前执行,可不填,缺省为
0
。 - 
rule=URL.encode("host = 192.168.223.* => host = 192.168.224.*")
表示路由规则的内容,
必填
。 
    
     条件路由规则
    
   
    基于条件表达式的路由规则,如:
    
     host = 192.168.223.* => host = 192.168.224.*
    
    
     规则:
    
   
- 
=>
之前的为消费者匹配条件,所有参数和消费者的 URL 进行对比,当消费者满足匹配条件时,对该消费者执行后面的过滤规则。 - 
=>
之后为提供者地址列表的过滤条件,所有参数和提供者的 URL 进行对比,消费者最终只拿到过滤后的地址列表。 - 
如果匹配条件为空,表示对所有消费方应用,如:
=> host = 192.168.224.*
 - 
如果过滤条件为空,表示禁止访问,如:
host = 192.168.223.* =>
 
    2.2 管理控制台配置
   
使用API进行配置非常不好理解,而且也做不到动态的修改,所以实际生产中大多时候还是通过管理控制台进行动态配置,特别是对于运维人员来说
2.2.1 条件路由
在Dubbo2.6及更早版本中,所有的服务治理规则都只针对服务粒度,如果要把某条规则作用到应用粒度上,需要为应用下的所有服务配合相同的规则,变更,删除的时候也需要对应的操作,这样的操作很不友好,因此Dubbo2.7版本中增加了应用粒度的服务治理操作,对于条件路由(包括黑白名单),动态配置(包括权重,负载均衡)都可以做应用级别的配置
    
     1)、应用粒度
    
   
# 消费者dubbo-order只能消费所有端口为20881的服务实例
enabled: true
force: true
runtime: true
conditions:
  - 'application=dubbo-order => address=*:20881'
    
   
怎么测试也只会调用dubbo服务端口为20883的服务实例:
    
   
    
     2)、服务粒度
    
   
#com.ydt.dubbo.service.LoadBalanceService的sayHello方法只能消费所有端口为20880的服务实例
enabled: true
force: true
runtime: true
key: com.ydt.dubbo.service.LoadBalanceService
conditions:
  - 'method=sayHello => address=*:20880'
    
   
2.2.2 标签路由
    标签路由是Dubbo2.7引入的新功能,配置以
    
     应用作为维度
    
    ,给不同的服务器打上不同名字的标签,标签路由通过将某一个或多个服务的提供者划分到同一个分组,约束流量只在指定分组中流转,从而实现流量隔离的目的,可以作为蓝绿发布、灰度发布等场景的能力基础。
   
enabled: true
force: true
runtime: true
tags:
    #在消费者处设置一个全局tag="tag1"
  - name: tag1 
    addresses:
    #如果是本机,请使用四星或者四个0标识IP来测试,我觉得是dubbo的bug
      - '*.*.*.*:20881'
<!--消费者全局配置-->
<dubbo:consumer timeout="6000" retries="3" check="false" tag="tag1"/>
    
   
无论你怎么访问都是如下结果,只会消费到dubbo服务端口为20881的实例:
    
   
    3、规则动态配置
   
    动态覆盖规则是Dubbo设计的在无需重启应用的情况下,动态调整RPC调用行为的一种能力。2.7.0版本开始,支持从
    
     服务
    
    和
    
     应用
    
    两个粒度来调整动态配置。
   
    3.1 应用粒度
   
# 将应用dubbo-pay(key:dubbo-pay)在20881端口上提供(side:provider)的所有服务(scope:application)的权重修改为1000(weight:1000,默认都是100)。
configVersion: v2.7
enabled: true
key: dubbo-pay
configs:
  - side: provider
    addresses:
      - '0.0.0.0:20881'
    parameters:
      weight: 1000
    
   
测试,你会明显的发现20881所在服务实例调用的次数要多得多(因为我这里一个工程多个端口启动,所以权重默认都是一样的)!
    3.2 服务粒度
   
# 所有消费(side:consumer)LoadBalanceService服务(key:com.ydt.dubbo.service.LoadBalanceService)的应用实例(addresses:[0.0.0.0]),超时时间修改为6000ms。
configVersion: v2.7
enabled: true
key: com.ydt.dubbo.service.LoadBalanceService
configs:
  - side: consumer
    addresses:
      - 0.0.0.0
    parameters:
      timeout: 6000
    
   
    4、服务降级
   
可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略,向注册中心写入动态配置覆盖规则!
@Test
public void test2(){
    RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
    Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://192.168.223.128:2181"));
    registry.register(URL.valueOf("override://0.0.0.0/com.ydt.dubbo.service.LoadBalanceService?category=configurators&dynamic=false&application=dubbo-order&mock=force:return+error"));
}
    
   
暂时dubbo-admin不支持服务mock的处理:
    
   
    5、集群容错
   
在集群调用失败时,Dubbo 提供了多种容错方案,缺省为 failover 重试。
    
     5.1 集群容错模式
    
   
    
     Failover Cluster
    
   
    失败自动切换,当出现失败,重试其它服务器。通常用于读操作,但重试会带来更长延迟。可通过
    
     retries="2"
    
    来设置重试次数(不含第一次,默认也是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
    
   
    并行调用多个服务器,只要一个成功即返回。通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过
    
     forks="2"
    
    来设置最大并行数。
   
    
     Broadcast Cluster
    
   
广播调用所有提供者,逐个调用,任意一台报错则报错。通常用于通知所有提供者更新缓存或日志等本地资源信息。
    
     5.2 集群模式配置
    
   
按照以下示例在服务提供方和消费方配置集群模式
<dubbo:service cluster="failsafe" />
或
<dubbo:reference cluster="failsafe" />
    6、黑白名单
   
    在Dubbo Admin服务治菜单下有黑白名单
    
     ,其功能是在应用级别,服务级别针对
    
    IP`设置访问限制
   
注意:
1、创建黑白名单的时候,可以设置应用级别,也可以设置服务级别,但是不能同时设置
2、吐槽一下,黑白名单貌似没什么卵用,只要你配置了消费者应用或者服务端接口,你就用不了了,我还配个*线,这也是为什么官方暂时提醒不适用于生产的原因吧!
    6.1 应用粒度
   
    
   
打开zookeeper管理控制台可以看到:
    
   
现在测试dubbo-order消费者接口访问:
    
   
    6.2 服务粒度
   
    
   
打开zookeeper管理控制台:
    
   
测试:
    
   
    7、权重调整
   
实现应用和服务级别的负载均衡权重动态配置,因为只能配置IP级别,本地开发环境配置过于麻烦就不演示了!
    7.1 应用粒度
   
    
   
    7.2 服务粒度
   
    
   
    8、负载均衡
   
    8.1 官方提供
   
    在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为
    
     Random
    
    随机调用。
   
    
     负载均衡策略
    
   
    
     Random LoadBalance
    
    (random)
   
- 
随机
,按权重设置随机概率。 - 
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
 
    
     RoundRobin LoadBalance
    
    (roundrobin)
   
- 
轮询
,按公约后的权重设置轮询比率。 - 
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
 
    
     LeastActive LoadBalance
    
    (leastactive)
   
- 
最少活跃调用数
,相同活跃数的随机,活跃数指调用前后计数差。 - 
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
 
    
     ConsistentHash LoadBalance
    
    (consistenthash)
   
- 
一致性 Hash
,相同参数的请求总是发到同一提供者。 - 
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
 - 
缺省只对第一个参数 Hash,如果要修改,请配置
<dubbo:parameter key="hash.arguments" value="0,1" />
 - 
缺省用 160 份虚拟节点,如果要修改,请配置
<dubbo:parameter key="hash.nodes" value="320" />
 
    
     ShortestResponse LoadBalance
    
    (shortestresponse)
   
- 
最短响应时间策略,筛选成功调用响应时间最短的调用程序,并计算这些调用程序的权重和数量
 - 
如果只有一个调用器,则直接使用该调用器
 - 
如果有多个调用器,则按随机策略来玩
 
测试方法:
1、服务提供者开启三台,设置不同的tomcat和dubbo服务端口
    
   
2、服务消费者配置不同的全局负载均衡策略分别进行测试:
<!--消费者全局配置-->
<dubbo:consumer timeout="6000" retries="3" check="false" loadbalance="roundrobin"/>
    
   
    8.2 自定义策略
   
    1、创建一个负载均衡策略类,实现
    
     LoadBalance
    
    接口
   
package com.ydt.dubbo.loadbalance;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.cluster.LoadBalance;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.RpcException;
import java.util.List;
public class MyLoadBalance implements LoadBalance {
    @Override
    public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) 
            throws RpcException {
        //这里根据你的业务需要实现,我这里仅仅只是为了测试,就取了第一个执行机器
        return invokers.get(0);
    }
}
2、在resources/META-INF/dubbo/internal下创建纯文本文件,文件名:org.apache.dubbo.rpc.cluster.LoadBalance,配置负载均衡类索引映射
    
   
3、配置自定义负载均衡器的key值:
<!--消费者全局配置-->
<dubbo:consumer timeout="6000" retries="3" check="false" loadbalance="mybalance"/>
再次测试,永远都只会路由到一个服务器上了!
    9、优雅停机
   
    Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果用户使用
    
     kill -9 PID
    
    等强制关闭指令,是不会执行优雅停机的,只有通过
    
     kill PID
    
    时,才会执行。
   
    9.1 优雅停机原理
   
    
     服务提供方
    
   
- 
停止时,先标记为不接收新请求,新请求过来时直接报错,让客户端重试其它机器。
 - 
然后,检测线程池中的线程是否正在运行,如果有,等待所有线程执行完成,除非超时,则强制关闭。
 
    
     服务消费方
    
   
- 
停止时,不再发起新的调用请求,所有新的调用在客户端即报错。
 - 
然后,检测有没有请求的响应还没有返回,等待响应返回,除非超时,则强制关闭。
 
    9.2 设置方式
   
设置优雅停机超时时间,缺省超时时间是 10 秒,如果超时则强制关闭。
<!--应用名,用于计算依赖关系,不是匹配条件,不要与其他应用名一样 -->
    <dubbo:application name="dubbo-pay">
        <dubbo:parameter key="shutdown.timeout" value="60000" /> <!-- 单位毫秒 -->
    </dubbo:application>
    如果 ShutdownHook 不能生效,可以自行调用DubboShutdownHook关闭,
    
     使用tomcat等容器部署的场景,建议通过扩展ApplicationListener等自行调用以下代码实现优雅停机
    
    :
   
DubboShutdownHook.destroyAll();
现状测试(因为本地开发要模拟kill -pid不方便,使用如下方式,实际开发中直接加入以下第一步监听器实例即可):
1、新建一个监听器,实现Spring ApplicationListener
package com.ydt.dubbo.listener;
import org.apache.dubbo.config.DubboShutdownHook;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
public class ShutdownHookListener implements ApplicationListener {
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ContextClosedEvent) {
            System.out.println("优雅下线啦!");
            DubboShutdownHook shutdownHook = DubboShutdownHook.getDubboShutdownHook();
            shutdownHook.destroyAll();
        }
    }
}
2、编写一个测试类,
@Test
public void test() throws IOException, InterruptedException {
    ClassPathXmlApplicationContext context
            = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"});
    context.start();
    System.out.println("容器加载完成....");
    final DemoService demoService = (DemoService)context.getBean("demoService"); // 获取远程服务代理
    final long start = System.currentTimeMillis();
    new Thread(
            new Runnable() {
                public void run() {
                    System.out.println("别停,哥已经在调用了呢!");
                    System.out.println(demoService.sayHello("laohu"));
                    System.out.println("调用返回总时间为:" + (System.currentTimeMillis()-start));
                }
            }
    ).start();
    Thread.sleep(1000);//这个地方主要是让demoService调用线程run起来,如果没获取到cpu资源,是不会优雅的
    System.out.println("截止到调用容器销毁时间为:" + (System.currentTimeMillis()-start));
    context.close();
}
3、测试效果
    
   
    10、在线运维Qos
   
QoS的英文全称为”Quality of Service”,中文名为”服务质量”。在dubbo 2.5.8 新版本增加了 QOS 模块,提供了新的 telnet 命令支持。dubbo管它叫在线运维命令,我们可以通过它能够看到服务提供者状态,服务调用者状态,现在dubbo提供了 ls , online,offline,help ,quit命令。
    
   
我们可以在dubbo的配置文件配置参数:
<!--应用名,用于计算依赖关系,不是匹配条件,不要与其他应用名一样 -->
<dubbo:application name="dubbo-pay">
    <!--开启QOS(在线运维命令,可以对服务进行动态的配置、控制及查询)-->
    <dubbo:parameter key="qos.enable" value="true"/>
    <dubbo:parameter key="qos.accept.foreign.ip" value="false"/>
    <dubbo:parameter key="qos.port" value="8108"/>
</dubbo:application>
<!--
qos.enable :表示是否开始Qos
qos.accept.foreign.ip: 允许访问的ip,缺省就是false,表示不接受任何ip
qos.port: Qos提供服务的端口
-->
通过终端访问qos:telnet 127.0.0.1 8108 注意了,如果不是使用本地终端,一些命令是禁止使用的,这也是dubbo的一种保护模式
    
   
可以看到服务提供服务与调用服务的状态 这个 Provider Service Name 就是服务提供者名字 , PUB 就是状态 N是未注册,就是没有注册到注册中心,其实服务下线功能就是从注册中心unregister ,Y表示服务在线,就是注册到注册中心了。
其实这个和dubbo-monitor非常相似:
    
   
其实都可以在dubbo-monitor上找到原型,其实也就是不想单独部署dubbo-monitor时使用简单的操作:
    
   
    11、简化注册中心URL
   
    11.1 为什么要简化
   
    dubbo provider中的服务配置项有接近
    
     30个配置项
    
    。 排除注册中心服务治理需要之外,很大一部分配置项是provider自己使用,不需要透传给消费者。这部分数据不需要进入注册中心,而只需要以key-value形式持久化存储。
   
    dubbo consumer中的配置项也有
    
     20+个配置项
    
    。在注册中心之中,服务消费者列表中只需要关注application,version,group,ip,dubbo版本等少量配置,其他配置也可以以key-value形式持久化存储。
   
这些数据是以服务为维度注册进入注册中心,导致了数据量的膨胀,进而引发注册中心(如zookeeper)的网络开销增大,性能降低。
简化注册中心的配置,只在2.7之后的版本中进行支持。 开启provider或者consumer简化配置之后,默认保留的配置项如下:
provider:
| Constant Key | Key | remark | 
|---|---|---|
| APPLICATION_KEY | application | |
| CODEC_KEY | codec | |
| EXCHANGER_KEY | exchanger | |
| SERIALIZATION_KEY | serialization | |
| CLUSTER_KEY | cluster | |
| CONNECTIONS_KEY | connections | |
| DEPRECATED_KEY | deprecated | |
| GROUP_KEY | group | |
| LOADBALANCE_KEY | loadbalance | |
| MOCK_KEY | mock | |
| PATH_KEY | path | |
| TIMEOUT_KEY | timeout | |
| TOKEN_KEY | token | |
| VERSION_KEY | version | |
| WARMUP_KEY | warmup | |
| WEIGHT_KEY | weight | |
| TIMESTAMP_KEY | timestamp | |
| DUBBO_VERSION_KEY | dubbo | |
| SPECIFICATION_VERSION_KEY | 
        specVersion  | 
新增,用于表述dubbo版本,如2.7.0 | 
consumer:
| Constant Key | Key | remark | 
|---|---|---|
| APPLICATION_KEY | application | |
| VERSION_KEY | version | |
| GROUP_KEY | group | |
| DUBBO_VERSION_KEY | dubbo | |
| SPECIFICATION_VERSION_KEY | 
        specVersion  | 
新增,用于表述dubbo版本,如2.7.0 | 
    11.2 简化配置
   
resources目录下新建dubbo.properties,dubbo框架会默认加载:
    
   
配置如下:
#是否开启简化
dubbo.registry.simplified=true 
#是否有简化后的拓展
dubbo.registry.extra-keys=retries,owner
spring核心配置加载一个服务接口发布到注册中心:
<bean id="loadBalanceService" class="com.ydt.dubbo.service.LoadBalanceServiceImpl"/>
<dubbo:service async="true" interface="com.ydt.dubbo.service.LoadBalanceService"
               version="1.2.3" group="dubbo-simple" ref="loadBalanceService" executes="4500" retries="7" owner="vict"
               timeout="5300"/>
启动后上zookeeper管理控制台查看,会发现executes并没有注册到注册中心:
    
   
原因:
配置了dubbo.registry.simplified=true, 默认情况下,timeout在默认的配置项列表,所以还是会进入注册中心;
配置了:dubbo.registry.extra-keys=retries,owner , 所以retries,owner也会进入注册中心。
总结:timeout,retries,owner进入了注册中心,而executes没有进入
 
