Dubbo定义
Dubbo是一个Java RPC框架,致力于分布式、高性能、透明化的远程服务调用方案。
Dubbo核心功能,主要提供了:远程方法调用、智能容错和负载均衡、提供服务自动注册、自动发现等高效服务治理功能。
为什么需要Dubbo?
比如早期一个应用Java War包,将所有功能都打包,部署在一个单机服务器,调用接口也比较方便,不涉及到任何分布式场景。
随着业务的快速发展,业务越来越多、子系统也越来越多时。比如:淘宝的交易系统、商品系统、用户系统、评价系统…上百个系统的出现。
系统变得越来越复杂,业务代码依然耦合在一起。比如最早期的淘宝denali工程,包含所有业务系统的代码,就仅打包部署都需要很长的时间。
并且,随着每个业务线的快速发展,业务代码耦合在一起,上线后出现问题急需要回滚代码,拉分支、大量的代码merge工作,这个过程极其痛苦。
这个时候,你会发现技术已经成了业务的瓶颈,急需把业务单独抽离出来,各自单独部署。
应用系统一旦涉及到拆分部署,问题就来了,急需一种高效的应用程序间的通讯手段来完成这种需求,这就会涉及到分布式远程调用。
所以,RPC的框架来了,比如:Dubbo为代表的分布式远程通信RPC框架。
Dubbo作用
1.透明化的远程方法调用
就像调用本地方法一样调用远程方法,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可。
2.软负载均衡及容错机制
可在内网替代F5等硬件,软负载均衡器的作用,降低成本,减少单点。
3. 服务自动注册与发现
不再需要写死服务提供方地址,注册中心基于接口名,查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。
Dubbo角色
1)服务提供者(Server)
对外提供后台服务,将自己的服务信息,注册到注册中心
2)注册中心(Registry)
用于服务端注册远程服务以及客户端发现服务。
目前主要的注册中心可以借由 zookeeper,eureka,consul,etcd 等开源框架实现。
比如:阿里的Dubbo就是采用zookeeper实现注册中心。
3)服务消费者(Client)
从注册中心获取远程服务的注册信息,然后进行远程过程调用。
Dubbo功能
主要就是如下3个核心功能:网络通信框架、集群服务框架、以及服务注册。
1.Remoting网络通信框架
提供对多种NIO框架抽象封装,包括:同步转异步,以及请求响应模式的信息交换方式。
2. Cluster服务框架
提供基于接口方法的透明远程过程调用,包括:多协议支持、以及软负载均衡、失败容错、地址路由、动态配置等集群支持。
3.Registry服务注册
基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。
Dubbo组件
Dubbo主要包含如下几个核心组件:
1.注册中心(registry)
在微服务时代,我们所有的服务都被劲量拆分成最小的粒度,原先所有的服务都在混在1个server里,现在就被按照功能或者对象拆分成N个服务模块。
这样做的好处是深度解耦,1个模块只负责自己的事情就好,能够实现快速的迭代更新,但是坏处就是服务的管理和控制变得异常的复杂和繁琐,人工维护难度变大。
所以,这个时候急需一个组件来管控所有的服务,注册中心就出现了。
注册中心的作用:一句话概括就是存放和调度服务,实现服务和注册中心,服务和服务之间的相互通信,注册中心可以说是微服务架构中的”通讯录“。
注册中心它记录了服务和服务地址的映射关系,在分布式架构中服务会注册到这里,当服务需要调用其它服务时,就到这里找到服务的地址进行调用。
常见的注册中心有:Zookeeper,Eureka,Consul,Etcd等,Dubbo这里就采用了Zookeeper为注册中心。
2.服务提供者(provider)
当我们需要为某个功能对外提供服务时我们可以暴露服务给使用者调用,那么这个服务就是服务提供者。
举个例子:淘宝的商品服务(SIC),提供了相关的淘宝商品的信息查询服务,那这个淘宝的商品服务(SIC)就是服务提供者。
一句话总结服务提供在:暴露服务的服务提供方,称之为”服务提供者”。
3.服务消费者(consumer)
调用远程服务的服务消费方,称之为”服务消费者”。
举个例子:比如我是淘宝的店铺团队,有一个需求需要调用商品信息,这个时候我就通过调用淘宝商品服务(SIC)提供的商品查询接口,我就是服务消费者,而商品团队就是服务提供者。
4.容器(container)
服务容器负责启动,加载,运行服务提供者,dubbo服务运行,也就是让生产服务的进程一直启动。
服务容器是一个 standalone 的启动程序,因为后台服务不需要 Tomcat 或 JBoss 等 Web 容器的功能,如果硬要用 Web 容器去加载服务提供方,增加复杂性,也浪费资源。
服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。
5.监控(Monitor)
dubbo的使用其实只需要有注册中心,消费者,提供者这三个就可以使用了。
但是并不能看到有哪些消费者和提供者,为了更好的调试,发现问题,需要监控,这就是监控中心的作用。
因此Dubbo引入了监控中心,通过 dubbo-admin 可以对消费者和提供者进行管理,也可以在 dubbo 应用部署做动态的调整和服务的管理。
Dubbo架构
Dubbo架构图,如下所示:
Dubbo整个架构包含:接口层、配置层、代理层、注册层、集群层、监控层、调用层、交换层、传输层、序列化层等10大层设计。
1. 服务接口层
服务接口层:根据服务提供方和服务消费方的业务设计对应的接口和实现。
2. 配置层
配置层:主要就是对外配置接口,可以通过Spring解析配置生成配置类。
3.服务代理层
服务代理层:主要就是生成服务的客户端代理。
4.服务注册层
服务注册层:主要就是封装服务地址的注册与发现。
5.集群层
集群层:就是Dubbo集群相关的封装,比如:多个提供者的路由、及服务负载均衡等。
6.监控层
监控层:主要就是RPC调用次数和调用时间监控。
7.远程调用层
远程调用层:主要就是封将RPC调用,比如:完成网络A服务器到B服务器的整个远程通信调用。
8. 信息交换层
信息交换层:主要就是封装请求响应模式,比如:同步转异步等。
9.网络传输层
网络传输层:主要就是解决网络传输的各种通信问题。
10.数据序列化层
数据序列化层:我们都知道数据在网络传输,不可能是对象进行传输,都是需要序列化后才能在网络两端传输,这就是会涉及到序列化。
Dubbo对比Spring Cloud
1.Dubbo和Spring Cloud
Dubbo是SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。
而 Spring Cloud诞生于微服务架构时代,考虑的是微服务治理的方方面面。
另外由于依托了 Spirng与Spirng Boot的优势之上,两个框架在开始目标就不一致,Dubbo 定位服务治理、Spirng Cloud 是一个微服务生态。
2.Dubbo和Spring Cloud区别
最大的区别:Dubbo底层是使用Netty这样的NIO框架,是基于TCP协议传输的,配合以Hession序列化完成RPC通信。
而SpringCloud是基于Http协议+Rest接口调用远程过程的通信,相对来说Http请求会有更大的报文,占的带宽也会更多。
但是REST相比RPC更为灵活,服务提供方和调用方的依赖只依靠一纸契约。
不存在代码级别的强依赖,这在强调快速演化的微服务环境下显得更为合适,至于注重通信速度还是方便灵活性,具体情况具体考虑。
Dubbo原理
Dubbo调用流程,大致分为如下11步:
1.首先服务提供者会启动服务,然后将服务注册到服务注册中心。
2.服务消费者,会定时拉取服务提供者列表。
3.生成一个动态代理对象,然后通过这个代理对象,去调用远程接口。
4.生成代理对象之后,会走到Cluster层,这里会获取服务提供者列表的数据。
5.然后,Cluster会根据指定的算法做负载均衡,选出要调用的服务提供者。
6.选择好服务提供者之后,再选择指定的协议格式。
7.Exchange会根据指定的协议格式进行请求数据封装,封装成request请求。
8.请求封装好之后,就会通过网络通信框架,将请求发送出去。
9.服务提供者那边,同样会有网络通信框架,会监听指定的端口号,当接收到请求之后会将请求进行反序列化。
10.反序列化之后,再根据Exchange根据指定协议格式将请求解析出来。
11.最后再通过动态代理对象,调用服务提供者的对应接口。
Dubbo示例
1.zookeeper下载
如果注册中心是zookeeper,需要先安装zookeeper,并且启动zookeeper服务器端。
zookeeper官方下载地址:
https://zookeeper.apache.org/releases.html
选择上图红框的版本,点击下载对应需要的zookeeper版本即可,这里我选择下载最新3.7.1稳定版本。
2.zookeeper安装
下载完最新zookeeper 3.7.1稳定版本后,解压下载的压缩包。
如下图所示:
详细的zookeeper安装步骤,请查看:Zookeeper安装教程(最新版详细图文步骤)
3.定义服务接口
public interface DemoService { String sayHello(String name); }
4.服务接口实现
public class DemoServiceImpl implements DemoService{ public String sayHello(String name) { return "Hello " + name; } }
5.用Spring配置声明暴露服务
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd "> <!-- 具体的实现bean --> <bean id="demoService" class="com.mikechen.dubbotest.provider.DemoServiceImpl" /> <!-- 提供方应用信息,用于计算依赖关系 --> <dubbo:application name="xixi_provider" /> <!-- 使用zookeeper注册中心暴露服务地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 用dubbo协议在20880端口暴露服务 --> <dubbo:protocol name="dubbo" port="20880" /> <!-- 声明需要暴露的服务接口 --> <dubbo:service interface="com.mikechen.dubbotest.provider.DemoService" ref="demoService" /> </beans>
6.启动服务
public class Provider { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"}); context.start(); System.in.read(); // 为保证服务一直开着,利用输入流的阻塞来模拟 } }
7.服务消费者
1)配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd "> <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 --> <dubbo:application name="hehe_consumer" /> <!-- 使用zookeeper注册中心暴露服务地址 --> <dubbo:registry address="zookeeper://127.0.0.1:2181" /> <!-- 生成远程服务代理,可以像使用本地bean一样使用demoService --> <dubbo:reference id="demoService" interface="com.mikechen.dubbotest.provider.DemoService" /> </beans>
2)加载Spring配置,并调用远程服务
public class Consumer { public static void main(String[] args) throws Exception { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( new String[] { "applicationContext.xml" }); context.start(); DemoService demoService = (DemoService) context.getBean("demoService"); String hello = demoService.sayHello("mikechen"); System.out.println(hello); // } }
结果:
hello mikechen
Dubbo应用
1.RPC分布式服务
当网站变大后,不可避免的需要拆分应用进行服务化,以提高开发效率,调优性能,节省关键竞争资源等。
比如:为了适用不断变化的市场需求,以及多个垂直应用之间数据交互方便,我们把公共的业务抽取出来作为独立的模块,为其他的应用提供服务,系统逐渐依赖于抽象和rpc远程服务调用。
2.配置管理
当服务越来越多时,服务的URL地址信息就会爆炸式增长,配置管理变得非常困难,F5硬件负载均衡器的单点压力也越来越大。
3.服务依赖
当进一步发展,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。
4.服务扩容
接着,服务的调用量越来越大,服务的容量问题就暴露出来,这个服务需要多少机器支撑?什么时候该加机器?等等……
在遇到这些问题时,都可以用Dubbo来解决。
陈睿mikechen
10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!
后台回复【面试】即可获取《史上最全阿里Java面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》