背景
Dubbo是阿里巴巴开源的一个高性能优秀的服务框架现(已加入Apache项目中),使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。京东也有一个基于这样的框架做了定制和改进的JSF,那我们为什么要提出这样的一个RPC框架呢?
互联网架构演变
孟子云:“颂其诗,读其书,不知其人可乎,是以论其世也。”讲的使我们要透彻的理解一篇文章,就必须要知道作者的生平际遇。互联网架构亦是如此,经历了单一应用架构、垂直应用架构、分布式服务架构和流动计算架构。服务的粒度越来越细化(比如以前一个网站后端只需要一个tomcat运行一个Servlet程序,现在前端一个请求过来,后端会有很多个tomcat容器分别运行着不同的Servlet,他们之间相互调用,最后再由第一个Servlet返回给前端),服务治理的问题就越发的明显,所以优秀的架构师们就在思考可不可以有这么一个优秀的架构替我们解决服务之间的调用问题?当然了,如果能高可用就更好了。如果有负载均衡就最好不过了。因此,我们的Dubbo框架就呱呱坠地了。
Dubbo架构
Provider: 暴露服务的服务提供方
Consumer: 调用远程服务的服务消费方
Registry: 服务注册与发现的注册中心
Monitor: 统计服务的调用次数和调用时间的监控中心
Container: 服务运行容器
Dubbo大体的流程是这样的:先用容器Container(当下以Docker为代表的应用级虚拟化技术,为了容易理解可以粗糙的以为一个虚拟机)启动我们的服务Provider。然后将其IP(192.169.0.1)注册到注册中心Registry去(通常我们会采用ZooKeeper这样的分布式应用服务协调程序)。接着我的服务调用方Comsumer就会去注册中心去订阅Subscribe相关服务。注册中心会异步的方式将你消费者需要的服务的地址列表推送Notify给你。消费者就会将整个地址列表缓存在本地,当业务需求到来时,就会使用地址列表里的地址去请求相关的服务程序。至于Monitor顾名思义就是监视器的含义,消费者和提供者会定时将服务的调用次数和被调用的次数发送给监视器。
配置与使用
Provider:
服务接口
服务接口实现类
Provider.xml(配置文件)
服务接口实现类就是我们真正Provider要执行的逻辑,可能有人会问,为什么我们要写一个接口类?不写可不可以?这是一个好问题。如果单纯的看Provider确实没有写这么一个接口的必要,但是对于Consumer来讲意义就大不相同了。RPC(Remote Procedure Cal)远程过程调用的目的就是要即便Consumer和Provider不是运行在同一台机子上,但是对于Consumer来讲就是像调用本地方法一样去调用Provider,所以Consumer至少得有Provider的接口,这样Comsumer才能知道Provider有哪些方法,至于具体怎么怎么做后面会解释,请稍安勿躁。接着,解释一下配置文件的含义
<dubbo:application/>:用于配置当前应用信息,不管该应用是提供者还是消费者
<dubbo:registry/>:用于配置连接注册中心相关信息
<dubbo:protocol/>:用于配置提供服务的协议信息,协议由提供方指定,消费方被动接受
<dubbo:service/>:用于暴露一个服务,定义服务的元信息,一个服务可以用多个协议暴露,一个服务也可以注册到多个注册中心
Consumer:
Consumer.xml(配置文件)
Consumer的使用也很简单,引入Provider的接口包,然后用配置文件生成Provider接口类的代理对象,再交给Spring来管理,这样Consumer的代码中就可以很透明的使用demoService的对象去调用Provider中的DemoServiceImpl的具体函数。接着,解释一下配置文件的含义
<dubbo:application/>:用于配置当前应用信息,不管该应用是提供者还是消费者
<dubbo:registry/>:用于配置连接注册中心相关信息
<dubbo:reference/>:用于创建一个远程服务代理,一个引用可以指向多个注册中心
Registry提了很多遍,这是一个抽象的概念,但是要理解起来也很容易如下图一般
服务列表
模块介绍
虽然我没有看过Dubbo的源码,但是我觉得作为一个RPC框架,它至少应该有封装TCP或者HTTP这类传输报文的协议功能;消费者端能生成代理对象去访问提供者对象就应该有一个生成代理的功能;消费者在调用提供端函数时候会传入一些参数,函数结果返回的时候又是对象,那它就应该有一个对象序列化和反序列化的功能;消费者和提供者会将自己的IP地址发给注册中心,并且会在消费者本地缓存服务列表,那它就应该有一个配置中心;消费者在调用的时候不用考虑访问的到底是那个节点上的服务,那它就应该有一个负载均衡的功能…..
有了这些基本的想法以后我们就可以去Dubbo的构成部分,然后再根据自己想了解的部分进行深入了解。那让我看看它有哪些模块
dubbo-config:
配置层。解析dubbo中的xml文件生成config对象。
dubbo-proxy:
服务代理层。负责生产消费端的代理对象。
dubbo-registry:
注册中心层。负责服务注册与查询服务,以及注册服务的本地缓存。
dubbo-cluster:
路由层。负责消费端的负载均衡策略,以及在访问某个节点服务失败以后的调整策略。
dubbo-monitor:
监控层。RPC服务端被调用次数还有消费方调用时间的监控。
dubbo-rpc:
远程调用层。封将RPC调用、支持RMI 、Hessian、Http、Webservice、thrift等rpc调用方式。
dubbo-remoting:
信息交换层。比较底层的模块,是封装请求和响应,处理各种通信协议,支持netty,mina,http。默认采用netty通信。
dubbo-common:
序列化层。Common就是复用度很高的模块,主要是数据序列化和序列化线程池之类的工具。
Dubbo的模块大致如此,我个人觉得这些模块中比较重要的是RPC模块,RPC是介于应用层和传输层之间的技术,也是当初设计RPC框架时候关注的地方,如果让我自己去实现一个RPC框架的话,我会采用java中的ObjectInputStream和ObjectOutputStream流,这两个流可以把java对象转成字节流和把字节流还原成java对象,然后使用sockect编程,将字节流传输于客服端和服务端之间。具体一点就是当服务端接收到客户端传来的字节流,我们就可以在服务端反序列出接口名,方法名,参数名。然后用反射的机制去调用,最后得到的返回值再序列化的方式发送给客户端。
好了Dubbo告一段下面将介绍一下JSF,因为我在京东实习,所以对于京东改进Dubbo的原因比较好奇,下面我也将介绍京东的JSF相比于Dubbo的不同之处。
京东改进之后的JSF
JSF架构
不难看出,JSF相比于Dubbo而言多了一个注册中心寻址服务,为什么会这样呢?主要是因为2015年双十一时候注册中心挂了以后,后端容器服务重启以后之前缓存的服务地址列表丢失,服务无法调用。而且以Zookeeper为注册中心的Dubbo,会受制于Zookeeper的缺点:在Zookeeper主节点挂了以后,新的主节点被选出来之前,Zookeeper集群不可用。而且Zookeeper没有动态水平扩展的能力。由此可见注册中心是瓶颈
所以京东在这一块做了改进,我觉得就是抛弃Zookeeper集群,自己取长补短的去实现一个服务治理。
1. 引入了index概念,我的理解就是将注册中心根据不同的业务分片,不同的业务模块访问不同的注册中心以实现负载均衡,而不像以前的注册中心逻辑上就是一个整体。因此也需要注册中心寻址服务,在一开始定位到自己的注册中心。
2. 因为没了Zookeeper的主从架构来保证分布式的数据一致性,所以就得自己去实现数据的一致性,京东则是采用的数据库来实现数据的一致性。同时为了保障高可用,注册中心内有服务列表的缓存,所以即便数据库挂了,注册中心依旧可用
3. 注册中心服务既然通过数据库解决了数据的一致性问题,自然可以实现水平扩容,监控到压力大即可进行动态水平扩展
4. Zookeeper优点就是其有着订阅推送的功能来实现服务列表更新的时候会推送给客户端,京东采用的是CallBack的方式,其实也能理解,只要注册中心发现出现变化调用消费者的回调函数,消费者就可以知道服务列表的更新。
5. 如果注册中心寻址服务挂了,注册中心也就成了断线纸鸢,所以只要在消费端本地再缓存一个注册中心地址即可。
以上就是我对RPC框架Dubbo和京东改进的JSF的理解,其实这种理解是使用者角度层面,如果想要深入理解的话还是避不开阅读源码或者介绍的更加详细的文档。
作者:fancy宸