URL介绍在讲解地址推送性能的具体优化之前,我们需要先了解一下与其息息相关的内容---URL。定义不谈dubbo,URL这个概念对于我们大多数人来说并不陌生。统一资源定位器(RFC1738——UniformResourceLocators(URL))应该是最著名的RFC规范了,它的定义也很简单。Internet上可用的资源可以用简单的字符串表示,本文档描述了此类字符串的语法和语义。这些字符串称为:“统一资源定位器”(URL)一个标准的URL格式最多可以包含以下部分protocol://username:password@host:port/path?key=value&key=value一些典型的URLhttp://www.facebook.com/friends?param1=value1¶m2=value2https://username:password@10.20.130.230:8080/list?version=1.0.0ftp://username:password@192.168。1.7:21/1/read.txt当然也有一些非常规的url,也归类为url192.168.1.3:20880urlprotocol=null,urlhost=192.168.1.3,port=20880,urlpath=nullfile:///home/user1/router.js?type=scripturlprotocol=file,urlhost=null,urlpath=home/user1/router.jsfile://home/user1/router.js?type=script
url协议=文件,url主机=home,url路径=user1/router.jsfile:///D:/1/router.js?type=scripturl协议=文件,url主机=null,url路径=D:/1/router.jsfile:/D:/1/router.js?type=script同上file:///D:/1/router.js?type=script/home/user1/router.js?type=scripturlprotocol=null,urlhost=null,urlpath=home/user1/router.jshome/user1/router.js?type=scripturl协议l=null,urlhost=home,urlpath=user1/router.jsDubbo中的URL也使用了dubbo中类似的URL,主要用于各个扩展点之间传递数据,形成这个URL对象的具体参数为如下:protocol:一般dubbo中的各种协议如:dubbothrifthttpzkusername/password:用户名/密码host/port:主机/端口path:接口名parameters:参数键值对一些典型的dubboURLdubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000描述了一个dubbo协议服务zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=1214&qos.port=33333×tamp=1545721981946描述了一个zookeeper注册中心consumer://30.5.120.217/org.apache.dubbo.demo.DemoService?application=demo-consumer&category=consumers&ch=false&dubbo=2.0。2&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=1209&qos.port=33333&side=consumer×tamp=1545721827784描述一个consumer可以说任何领域的一个实现都可以认为是一类url,dubbo统一使用url来描述贯穿整个框架的元数据和配置信息Dubbo2.7URL结构在Dubbo2.7中,URL结构非常简单,一个类涵盖所有内容,如下图所示。地址推送模型接下来我们看一下Dubbo2.7中地址推送模型的解决方案。主要的性能问题是由以下过程引起的。上图中的主要流程是1.用户添加/删除DemoService的特定Provider实例(一般是因为扩缩容、网络波动等)2.ZooKeeper将DemoService下的所有实例推送到Consumer端3.Consumer端根据这个方案可以看出,当Provider实例数量较少时,对Consumer端的影响比较小,但是当某个接口Provider实例数量较多时,就会出现大量不必要的URL创建过程。在Dubbo3.0中,一系列的优化主要是针对上述的推送流程,我们接下来会详细讲解。Dubbo3.0URL结构当然,地址推送模型的优化还是离不开URL的优化。下图是Dubbo3.0优化地址推送模型过程中使用的新的URL结构。根据上图我们可以看到,Dubbo2.7的URL中的几个重要属性在Dubbo3.0中已经不存在了,取而代之的是两个类,URLAddress和URLParam。原来的parameters属性被移动到URLParam中的params,其他属性被移动到URLAddress及其子类。下面介绍三个新的URL子类,其中InstanceAddressURL属于应用层接口地址,本章不再介绍。ServiceConfigURL和ServiceAddressURL的主要区别在于ServiceConfigURL是程序读取配置文件时生成的URL。而ServiceAddressURL是注册中心推送一些信息(比如提供者)时生成的URL。这里顺便提一下为什么会有DubboServiceAddressURL的子类。按照现在的结构,ServiceAddressURL只有这个子类,所以完全可以把他们两个属性都放到ServiceAddressURL里,那为什么还要这个子类呢?班级?其实Dubbo3.0在设计上就是为了兼容HSF框架,抽象了一个ServiceAddressURL,HSF框架可以继承这个类使用HSFServiceAddressURL。当然这个类目前并没有体现出来,所以我们在这里简单提一下,但多做说明。那么,我们就来讨论一下为什么Dubbo3.0要改成这个数据结构,这个结构和地址推送模型的优化有什么关系!地址推送模型的优化URL结构的优化在上一节的类图中我们可以看到,虽然原来的属性已经移到了URLAddress和URLParam,但是URL子类还是多了几个属性。这些属性自然也是为了优化而加入的,那么这些属性的作用就在这里。ServiceConfigURL:该子类中添加的attribute属性。该属性主要是为了URLParam中params的冗余。只是将value的类型从String改为Object,减少了代码中每次获取参数的格式转换的消耗。.ServiceAddressURL:overrideURL和consumerURL属性已添加到该子类及其对应的子类中。其中,consumerURL为消费者端的配置信息,overrideURL为DubboAdmin动态配置时写入的值。当我们调用URL的getParameter()方法时,优先级是overrideURL>consumerURL>urlParam。在Dubbo2.7中,动态配置属性将取代URL中的属性,当你拥有大量的URL时,这种消耗是不容忽视的,这里的overrideURL避免了这种消耗,因为所有的URL都会共享同一个对象。多级缓存缓存是Dubbo3.0的URL优化的重点,这部分也直接针对地址推送模型进行了优化,下面介绍一下多级缓存的具体实现。首先,多级缓存主要体现在CacheableFailbackRegistry类,它直接继承自FailbackRegistry。以Zookeeper为例,看看Dubbo2.7和Dubbo3.0继承结构的区别。可以看到在CacheableFailbackRegistry缓存中,我们新增了3个缓存属性stringAddress、stringParam和stringUrls。我们用下图来描述这三种缓存的具体使用场景。在这个方案下,我们使用了三纬缓存数据(URL字符串缓存、URL地址缓存、URL参数缓存),使得缓存中的数据在大多数情况下都能得到有效利用,减少Zookeeper重复通知的消耗。除了上面提到的优化之外,其实还有两个针对延迟通知的小优化。第一个是在解析URL时,可以直接使用编码后的URL字符串字节进行解析。在Dubbo2.7中,所有编码的URL字符串都需要解码才能正常解析为URL对象。这种做法也直接减少了URL解码过程的开销。二是URL变化后的通知机制增加了延迟。下图以Zookeeper为例说明实现细节。该方案中,Consumer收到Zookeeper的变更通知后,会主动休眠一段时间,休眠结束后这段时间的变更只会保留最后一次变更,Consumer会使用最后一次变更更新监控实例,以减少创建大量URL的开销。字符串复用在老版本的实现中,不同URL中具有相同属性的字符串会被存放在堆中的不同地址,比如protocol,path等,当provider数量较多时,会出现大量消费者端的堆数。字符串重复导致内存利用率低,所以这里提供另一种优化方法,即字符串复用。而且它的实现也很简单,我们来看看对应的代码片段。公共类URLItemCache{privatestaticfinalMap
