Dubbo高级应用-服务治理

  • Post author:
  • Post category:其他



目录


1、dubbo-admin-2.7.x版本安装部署


1.1 下载源码


1.2 部署访问


2、路由规则


2.1 Dubbo API配置


2.2 管理控制台配置


3、规则动态配置


3.1 应用粒度


3.2 服务粒度


4、服务降级


5、集群容错


5.1 集群容错模式


5.2 集群模式配置


6、黑白名单


6.1 应用粒度


6.2 服务粒度


7、权重调整


7.1 应用粒度


7.2 服务粒度


8、负载均衡


8.1 官方提供


8.2 自定义策略


9、优雅停机


9.1 优雅停机原理


9.2 设置方式


10、在线运维Qos


11、简化注册中心URL


11.1 为什么要简化


11.2 简化配置


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 服务粒度

uploading.4e448015.gif
转存失败

重新上传


取消

打开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没有进入



版权声明:本文为huxiang19851114原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。