目录
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没有进入