背景
项目中使用了dubbo作为分布式服务框架的技术解决方案,dubbo提供高性能的RPC机制,满足服务之间透明的远程调用,注册中心由zookeeper插件提供,用于服务发现与治理、服务配置管理、服务调度控制等,所有需要暴露的服务接口都将注册到zookeeper中,服务的调用也通过zookeeper统一调度。
一般来说,相同的服务会部署到多台机器,从横向维度扩展来达到高并发的要求。分布式服务调用时,往往会用到dubbo的负载均衡,dubbo提供多种可选的负载均衡策略进行配置,但如何在调用的时候动态的指定某台机器直接调用呢,负载均衡策略并不能满足这个需求,解决这个问题,则需要通过动态配置路由策略来实现。
Dubbo架构介绍
节点角色说明:
Consumer:服务消费者,即服务调用方;
Provider:服务提供者,即被调用方;
Registry:注册中心,服务注册与服务发现,dubbo支持4种注册中心(multicast zookeeper redis simple),dubbo推荐使用zookeeper注册中心;
Monitor:服务监控中心,提供服务调用次数和调用时间统计;
Container:服务运行容器;
调用关系说明:
0. 服务容器负责启动,加载,运行服务提供者;
1. 服务提供者在启动时,向注册中心注册自己提供的服务;
2. 服务消费者在启动时,向注册中心订阅自己所需的服务;
3. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者;
4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用;
5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
Dubbo负载均衡
在集群负载均衡时,dubbo提供了4中均衡策略,缺省为random随机调用,具体策略如下:
Random LoadBalance
随机,按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
RoundRobin LoadBalance
轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
LeastActive LoadBalance
最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
ConsistentHash LoadBalance
一致性Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
基于以上4种策略,可以发现,并不能满足点对点调用的需求,ConsistentHash LoadBalance虽然可以将相同参数的请求总是发到同一个提供者,但前提是参数相同,灵活性和拓展性无法满足我们的需求,更别说动态更改了。
直连提供者
dubbo本身也会提供直连指定服务提供者的方式,绕过注册中心,点对点连接到提供者。在具体尝试使用该功能后发现,其实该功能并不能满足我们的需求。
1、在JVM启动参数中加入-D参数映射服务地址,如:
(key为服务名,value为服务提供者url,此配置优先级最高,1.0.15及以上版本支持)
java -Dcom.alibaba.xxx.XxxService=dubbo://localhost:20890
2、如果服务比较多,也可以用文件映射,如:
(用-Ddubbo.resolve.file指定映射文件路径,此配置优先级高于<dubbo:reference>中的配置,1.0.15及以上版本支持)
(2.0以上版本自动加载${user.home}/dubbo-resolve.properties文件,不需要配置)
java -Ddubbo.resolve.file=xxx.properties
然后在映射文件xxx.properties中加入:
(key为服务名,value为服务提供者url)
com.alibaba.xxx.XxxService=dubbo://localhost:20890
以上两种方式,dubbo强烈不推荐在复杂的线上环境使用,提供的目的仅仅是为了在开发测试环境方便调试;
3、如果是线上需求需要点对点,可在中配置url指向提供者,将绕过注册中心,多个地址用分号隔开,配置如下:(1.0.6及以上版本支持)
interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890"/>
第三种方式虽然能在线上使用,但提供者被写死在dubbo的配置文件中,和第一、二种方式一样,无法满足动态指定某个提供者的需求。
动态配置路由规则
dubbo服务的调用都是通过zookeeper注册中心完成,我们是否可以向注册中心写入一定的路由规则,达到动态调用指定提供者的需求呢?
dubbo是支持写入路由规则的,如在服务调用前,加入下面代码:
RegistryFactory registryFactory = ExtensionLoader.getExtensionLoader(RegistryFactory.class).getAdaptiveExtension();
Registry registry = registryFactory.getRegistry(URL.valueOf("zookeeper://10.20.153.10:2181"));
registry.register(URL.valueOf("condition://0.0.0.0/com.foo.BarService?category=routers&dynamic=false&rule="+ URL.encode("http://10.20.160.198/wiki/display/dubbo/host = 10.20.153.10 => host = 10.20.153.11") + "));
以上代码的规则,概括就是:对于所有调用com.foo.BarService接口的消费者,如果消费者的ip是"10.20.153.10",那么这个消费者将调用ip为"10.20.153.11"的提供者,这样,通过动态配置注册中心的路由规则,就实现了动态指定某个提供者的需求。