分布式服务治理框架Dubbo的前世今生及应用实战

分布式服务治理框架Dubbo的前世今生及应用实战
文章图片

Dubbo的出现背景 Dubbo从开源到现在,已经出现了接近10年时间,在国内各大企业被广泛应用。 它到底有什么魔力值得大家去追捧呢?本篇文章给大家做一个详细的说明。
大规模服务化对于服务治理的要求 当企业开始大规模的服务化以后,远程通信带来的弊端就越来越明显了。比如说

  1. 服务链路变长了,如何实现对服务链路的跟踪和监控呢?
  2. 服务的大规模集群使得服务之间需要依赖第三方注册中心来解决服务的发现和服务的感知问题
  3. 服务通信之间的异常,需要有一种保护机制防止一个节点故障引发大规模的系统故障,所以要有容错机制
  4. 服务大规模集群会是的客户端需要引入负载均衡机制实现请求分发
而这些对于服务治理的要求,传统的RPC技术在这样的场景中显得有点力不从心,因此很多企业开始研发自己的RPC框架,比如阿里的HSF、Dubbo;京东的JSF框架、当当的dubbox、新浪的motan、蚂蚁金服的sofa等等
有技术输出能力的公司,都会研发适合自己场景的rpc框架,要么是从0到1开发,要么是基于现有的思想结合公司业务特色进行改造。而没有技术输出能力的公司,遇到服务治理的需求时,会优先选择那些比较成熟的开源框架。而Dubbo就是其中一个
dubbo主要是一个分布式服务治理解决方案,那么什么是服务治理?服务治理主要是针对大规模服务化以后,服务之间的路由、负载均衡、容错机制、服务降级这些问题的解决方案,而Dubbo实现的不仅仅是远程服务通信,并且还解决了服务路由、负载、降级、容错等功能。
Dubbo的发展历史 Dubbo是阿里巴巴内部使用的一个分布式服务治理框架,2012年开源,因为Dubbo在公司内部经过了很多的验证相对来说比较成熟,所以在很短的的还是件就被很多互联网公司使用,再加上阿里出来的很多技术大牛进入各个创业公司担任技术架构以后,都以Dubbo作为主推的RPC框架使得dubbo很快成为了很多互联网公司的首要选择。并且很多公司在应用dubbo时,会基于自身业务特性进行优化和改进,所以也衍生了很多版本,比如京东的JSF、比如新浪的Motan、比如当当的dubbox.
在2014年10月份,Dubbo停止了维护。后来在2017年的9月份,阿里宣布重启Dubbo,并且对于Dubbo做好了长期投入的准备,并且在这段时间Dubbo进行了非常多的更新,目前的版本已经到了2.7.
2018年1月8日,Dubbo创始人之一梁飞在Dubbo交流群里透露了Dubbo 3.0正在动工的消息。Dubbo 3.0内核与Dubbo2.0完全不同,但兼容Dubbo 2.0。Dubbo 3.0将支持可选Service Mesh
2018年2月份, Dubbo捐给了Apache。另外,阿里巴巴对于Spring Cloud Alibaba生态的完善,以及Spring Cloud团队对于alibaba整个服务治理生态的支持,所以Dubbo未来依然是国内绝大部分公司的首要选择。
Dubbo的整体架构 分布式服务治理框架Dubbo的前世今生及应用实战
文章图片

Dubbo的使用 首先,构建两个maven项目
  • user-service
    • user-service-api
    • user-service-provider
  • user-service-consumer
user-service-api user-service提供服务的公共契约,里面提供了user-service对外的服务。
public interface ILoginService {String login(String username,String password); }

user-service-provider 在user-service-provider服务中,提供ILoginService的实现
public class LoginServiceImpl implements ILoginService{ @Override public String login(String username, String password) { if(username.equals("admin")&&password.equals("admin")){ return "SUCCESS"; } return "FAILED"; } }

user-service-consumer
public class App { public static void main( String[] args ){ ILoginService loginService=null; System.out.println(loginService.login("admin","admin")); } }

问题来了,现在user-service-consumer作为服务消费者,如何去调用远程服务user-service-provider呢?
按照前面对于服务远程通信的原理来说,服务提供方必然需要将服务发布到网络上,并且提供对应的访问协议。而服务消费端必然需要基于这个协议来进行访问。
这个时候,dubbo这个中间件就派上用场了,它的最基本作用就是提供服务的发布和服务的远程访问。
引入Dubbo发布服务
  • 引入dubbo依赖包
    org.apache.dubbo dubbo 2.7.8

  • 在/src/main/resource/META-INF/spring目录下添加application.xml文件

  • 启动服务
    public class App { public static void main( String[] args ){ Main.main(args); } }

  • 启动成功后,会在控制台看到如下日志
    信息:[DUBBO] Export dubbo service com.gupaoedu.demo.ILoginService to url dubbo://192.168.1.104:20880/com.gupaoedu.demo.ILoginService?anyhost=true&application=user-service&bind.ip=192.168.1.104&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.gupaoedu.demo.ILoginService&methods=login&pid=24280&release=2.7.8&side=provider×tamp=1596550697070, dubbo version: 2.7.8, current host: 192.168.152.1 八月 04, 2020 10:18:17 下午 org.apache.dubbo.remoting.transport.AbstractServer info 信息:[DUBBO] Start NettyServer bind /0.0.0.0:20880, export /192.168.1.104:20880, dubbo version: 2.7.8, current host: 192.168.152.1

通过上述步骤,就表示ILoginService已经发布到了网络上,基于NettyServer的形式,默认监听20880端口
服务消费者引入dubbo
  • 添加jar包依赖
    org.apache.dubbo dubbo 2.7.8

  • 在/src/main/resources/META-INF/spring目录下添加application.xml文件

  • 修改main方法
    • 通过ApplicationContext加载spring的配置文件
    • 从容器中获得一个ILoginService的bean
    public class App { public static void main( String[] args ){ ILoginService loginService=null; ApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:META-INF/spring/application.xml"); loginService=applicationContext.getBean(ILoginService.class); System.out.println(loginService.login("admin","admin")); } }

指定服务提供端的url 在上述的配置完成之后,运行项目后发现会提示如下错误
IllegalStateException: No such any registry to reference com.gupaoedu.demo.ILoginService on the consumer 192.168.152.1 use dubbo version 2.7.8, please config to your spring config.

原因是,我们配置的dubbo:registry指定的注册中心是N/A,表示没有配置注册中心。
其次,我们也没有明确的指明服务提供者在什么位置。因此解决这个问题的方法有两种
  • 指向服务提供者的地址
  • 配置服务注册中心,把服务提供者注册到注册中心,然后服务消费者指向注册中心从注册中心获取服务地址
修改方式如下,修改服务消费者中application.xml中的dubbo:reference。

总结 【分布式服务治理框架Dubbo的前世今生及应用实战】简单总结一下上面的整个过程,其实不难发现,Dubbo这个中间件为我们提供了服务远程通信的解决方案。通过dubbo这个框架,可以开发者快速高效的构建微服务架构下的远程通信实现。
不知道大家是否发现,我们在使用dubbo发布服务,或者消费服务的时候,全程都是采用spring的配置来完成的,这样的好处是我们在学习或者使用dubbo时,如果你用过spring这个框架,那么对于它的学习难度会大大的降低。而且我们也可以看到,dubbo是完全集成Spring 的,因此后续我们去分析dubbo的源码时,还是会有一些和spring有关的内容。
而且如果大家之前学习过我手写RPC的那节课,也基本能猜测到它的整个实现结构,大家不妨大胆的去猜测dubbo的一些实现细节,以助于后续在深度学习dubbo时更好的理解。
引入注册中心 Dubbo并不仅仅只是一个RPC框架,他还是一个服务治理框架,它提供了对服务的统一管理、以及服务的路由等功能。
在上面的案例中,我们只是掩饰了Dubbo作为RPC通信的点对点服务,但是就像咱们前面在学习spring cloud的内容一样,服务多了以后,如何管理和维护,以及动态发现呢?
而且,从Dubbo的架构图中可以看到,Dubbo天然就支持服务注册与发现,官方最早推荐的服务注册中心是zookeeper,当然,目前dubbo能够支持的注册中心已经非常多了,比如
consul、etcd、nacos、sofa、zookeeper、eureka、redis等等,很显然,Dubbo已经在往一个独立微服务解决方案的生态在发展。
集成Zookeeper作为服务注册中心
  • 添加zookeeper的jar包依赖
    org.apache.dubbo dubbo-dependencies-zookeeper 2.7.8

  • 修改服务提供者和服务消费者的配置

集成Nacos作为服务注册中心
  • 启动nacos
    docker run --name nacos -d -p 8848:8848 --privileged=true --restart=always -e JVM_XMS=512m -e JVM_XMX=2048m -e MODE=standalone -e PREFER_HOST_MODE=hostname -v /home/nacos/logs:/home/nacos/logs nacos/nacos-server

    • privileged: 使用该参数,container内的root拥有真正的root权限。否则,container内的root只是外部的一个普通用户权限。
    • 当 Docker 重启时,容器自动重启
    • PREFER_HOST_MODE: ip #如果支持主机名可以使用hostname,否则使用ip,默认也是ip
  • 添加依赖
    com.alibaba.nacos nacos-client 1.2.1

  • 修改配置

Dubbo Spring Cloud 既然我们讲的是Spring Cloud Alibaba这个专题,那么我们就有必要去了解一下Dubbo是如何集成Spring Cloud去使用的。
Dubbo Spring Cloud是构建在原生的Spring Cloud之上,涵盖了Spring Cloud原生特性,而且相对于Spring Cloud原生治理来说,Dubbo Spring Cloud提供了更加稳定和成熟的实现。
具体的特性对比如下:
![image-20200804224645852](E:\教研-课件\vip课程\第四轮\分布式微服务\5 第五章 Spring Cloud Alibaba微服务生态\01 第一节 微服务治理之Dubbo的基本认识\第一节 微服务治理之Dubbo的基本认识.assets\image-20200804224645852.png)
为什么叫Dubbo Spring Cloud,而不是Spring Cloud Dubbo呢,在我看来,Dubbo本身是自成一个生态体系,并且在本身的服务治理以及成熟度上要比Spring cloud 更加突出。
所以实际上Dubbo整合Spring Cloud,是Dubbo这个成熟的生态去拥抱spring cloud的标准体系。
Dubbo Spring Cloud 基于 Dubbo Spring Boot 2.7.1[1] 和 Spring Cloud 2.x 开发,无论开发人员是 Dubbo 用户还是 Spring Cloud 用户, 都能轻松地驾驭,并以接近“零”成本的代价使应用向上迁移
从 2.7.0 开始,Dubbo Spring Boot 与 Dubbo 在版本上保持一致
接下来,我们可以去利用Dubbo Spring Cloud来做一个简单的案例实现
创建一个项目
  • 创建一个spring-cloud-dubbo-example的maven工程
  • 分别添加三个模块
    • spring-cloud-dubbo-sample-api
    • spring-cloud-dubbo-sample-provider
    • spring-cloud-dubbo-sample-consumer
其中后面两个模块都是spring boot的应用。
修改spring-cloud-dubbo-sample-provider这个模块中。
  • 将dependencyManagement部分的依赖移动到parent pom.xml
  • 修改spring-cloud-dubbo-sample-provider中的pom.xml,增加parent模块的依赖
    com.gupaoedu.dubbo spring-cloud-dubbo-example 1.0-SNAPSHOT

  • 添加maven依赖
    com.alibaba.cloud spring-cloud-starter-dubbo com.gupaoedu.dubbo 1.0-SNAPSHOT spring-cloud-dubbo-sample-api com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery

定义服务接口 在spring-boot-dubbo-sample-api模块中,定义接口
public interface IHelloService {String sayHello(); }

实现服务 在spring-boot-dubbo-sample-provider中,实现IHelloService接口
public class HelloServiceImpl implements IHelloService{@Override public String sayHello() { return "Hello GuPao"; } }

添加@EnableDiscoveryClient注解
@EnableDiscoveryClient @SpringBootApplication public class SpringCloudDubboSampleProviderApplication {public static void main(String[] args) { SpringApplication.run(SpringCloudDubboSampleProviderApplication.class, args); }}

配置dubbo服务发布
  • 在服务实现类中添加@Service注解
    @Service public class HelloServiceImpl implements IHelloService{@Override public String sayHello() { return "Hello GuPao"; } }

  • 配置dubbo提供方信息
    # dubbo 服务扫描基础包路径 dubbo.scan.base-packages=com.gupaoedu.dubbo.springclouddubbosampleproviderdubbo.protocol.id=dubbo # Dubbo 服务暴露的协议配置,其中子属性 name 为协议名称,port 为协议端口( -1 表示自增端口,从 20880 开始) dubbo.protocol.name=dubbo dubbo.protocol.port=-1spring.cloud.nacos.discovery.server-addr=192.168.216.128:8848

    • dubbo.scan.base-packages : 指定 Dubbo 服务实现类的扫描基准包
    • dubbo.protocol : Dubbo 服务暴露的协议配置,其中子属性 name 为协议名称,port 为协议端口( -1 表示自增端口,从 20880 开始)
    • dubbo.registry : Dubbo 服务注册中心配置,其中子属性 address 的值 "spring-cloud://localhost",说明挂载到 Spring Cloud 注册中心
    • spring.cloud.nacos.discovery : Nacos 服务发现与注册配置,其中子属性 server-addr 指定 Nacos 服务器主机和端口
版本规范 项目的版本号格式为 x.x.x 的形式,其中 x 的数值类型为数字,从 0 开始取值,且不限于 0~9 这个范围。项目处于孵化器阶段时,第一位版本号固定使用 0,即版本号为 0.x.x 的格式。
由于 Spring Boot 1 和 Spring Boot 2 在 Actuator 模块的接口和注解有很大的变更,且 spring-cloud-commons 从 1.x.x 版本升级到 2.0.0 版本也有较大的变更,因此我们采取跟 SpringBoot 版本号一致的版本:
  • 1.5.x 版本适用于 Spring Boot 1.5.x
  • 2.0.x 版本适用于 Spring Boot 2.0.x
  • 2.1.x 版本适用于 Spring Boot 2.1.x
  • 2.2.x 版本适用于 Spring Boot 2.2.x
构建服务消费者
  • 添加jar包依赖
    com.alibaba.cloud spring-cloud-starter-dubbo org.springframework.boot spring-boot-actuator com.gupaoedu.dubbo 1.0-SNAPSHOT spring-cloud-dubbo-sample-api com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-web

  • 添加配置文件
    spring.application.name=spring-cloud-dubbo-sample-consumer dubbo.application.name=spring-cloud-dubbo-sample-consumerdubbo.cloud.subscribed-services=spring-cloud-dubbo-sample-provider spring.cloud.nacos.discovery.server-addr=192.168.216.128:8848

    除应用名称 spring.application.name 存在差异外,spring-cloud-dubbo-client-sample 新增了属性 dubbo.cloud.subscribed-services 的设置。并且该值为服务提供方应用 "spring-cloud-dubbo-sample-provider"。
    它的主要作用是服务消费方订阅服务提供方的应用名称的列表,若需订阅多应用,使用 "," 分割。 不推荐使用默认值为 "*",它将订阅所有应用。
  • 编写测试代码
    @RestController @EnableDiscoveryClient @SpringBootApplication public class SpringCloudDubboSampleConsumerApplication {public static void main(String[] args) { SpringApplication.run(SpringCloudDubboSampleConsumerApplication.class, args); } @Reference IHelloService helloService; @GetMapping("/say") public String say(){ return helloService.sayHello(); }}

多注册中心的支持 dubbo相对于spring cloud来说,它的强大之处在于,提供了很多不同场景的功能支持,比如多注册中心的支持。
所谓的多注册中心,就是指dubbo可以同时配置多个注册中心的地址,然后针对于不同类型的服务注册到不同的注册中心上。
Dubbo多注册中心可以支持几种场景
一个服务部署到多个注册中心 基于spring cloud的配置方式
  • 添加jar包依赖
    org.apache.dubbo dubbo-dependencies-zookeeper 2.7.8 org.slf4j slf4j-log4j12 log4j log4j

  • 修改application配置
    dubbo.registries.registry1.address=nacos://192.168.216.128:8848 dubbo.registries.registry1.timeout=10000 dubbo.registries.registry2.address=zookeeper://192.168.216.128:2181 dubbo.registries.registry2.timeout=10000#spring.cloud.nacos.discovery.server-addr=192.168.216.128:8848 spring.cloud.nacos.discovery.register-enabled=false spring.cloud.nacos.discovery.watch.enabled=false spring.cloud.service-registry.auto-registration.enabled=false

    • spring.cloud.service-registry.auto-registration.enabled 关闭spring cloud的自动注册
    • spring.cloud.nacos.discovery.watch.enabled/spring.cloud.nacos.discovery.register-enabled关闭nacos的服务注册和监听
    这么做的目的是,规避spring cloud本身的服务注册发现机制,走dubbo本身的服务注册与发现
  • 修改服务配置
    @Service(registry = {"registry1","registry2"}) public class HelloServiceImpl implements IHelloService{@Override public String sayHello() { return "Hello GuPao"; } }

多注册中心的引用
  • 修改消费端的application.properties
    dubbo.registries.registry1.address=nacos://192.168.216.128:8848 dubbo.registries.registry1.timeout=10000 dubbo.registries.registry2.address=zookeeper://192.168.216.128:2181 dubbo.registries.registry2.timeout=10000spring.cloud.nacos.discovery.register-enabled=false spring.cloud.nacos.discovery.watch.enabled=false spring.cloud.service-registry.auto-registration.enabled=false

  • 添加jar包依赖
    org.apache.dubbo dubbo-dependencies-zookeeper 2.7.8 org.slf4j slf4j-log4j12 log4j log4j

基于spring boot集成Dubbo方式 实际上,在dubbo spring cloud的使用方式中,对于配置多个服务注册中心不是很友好而且还有一些潜在的问题, 毕竟dubbo和spring cloud两个本质上是属于完全不同的生态耦合在一起,必然会导致一些兼容问题。比如刚刚我们去配置的这些多注册中心的支持,它需要去关闭spring cloud本身的服务自动注册和发现的支持,本质上就是在两个生态中选择其中一个生态作为主要方式来使用。
所以,如果是在spring cloud的生态中,可以尽量减少对于dubbo本身灵活性的使用,拥抱spring cloud的标准生态,当然如果希望以dubbo作为独立的生态来使用,大家可以采用spring boot+Dubbo来集成,
这里同样也给大家快速构建一下。
另外,dubbo集成到spring boot中还有一个好处,就是它可以继承spring boot本身的特性
  • 自动装配(注解驱动、自动装配)
  • production-ready(安全机制、健康检测、外部化配置)
创建项目结构
创建基础的项目结构
  • spring-boot-dubbo-example [maven]
    • spring-boot-dubbo-sample-api [maven]
    • spring-boot-dubbo-sample-provider [spring boot]
    • spring-boot-dubbo-sample-consumerp [spring-boot]
添加jar包依赖
从2.7开始,dubbo的版本和dubbo-spring-boot的版本是保持一致的,所以大家不用再去担心版本的问题。
org.apache.dubbo dubbo-spring-boot-starter 2.7.7 com.alibaba.nacos nacos-client 1.2.1

添加服务以及发布
@DubboService public class SayHelloServiceImpl implements ISayHelloService{ @Override public String sayHello() { return "Hello GuPaoEdu.com"; } }

spring.application.name=spring-boot-dubbo-sample-providerdubbo.registry.address=nacos://192.168.216.128:8848 dubbo.scan.base-packages=com.gupaoedu.springboot.dubbo.springbootdubbosampleprovider.servicedubbo.protocol.name=dubbo dubbo.protocol.port=-1

编写服务引用代码
  • 添加jar包依赖
    org.springframework.boot spring-boot-starter-web com.gupaoedu.com 1.0-SNAPSHOT spring-boot-dubbo-sample-api org.apache.dubbo dubbo-spring-boot-starter 2.7.7 com.alibaba.nacos nacos-client 1.2.1

  • 添加web测试类
    @DubboReference ISayHelloService sayHelloService; @GetMapping("/get") public String get(){ return sayHelloService.sayHello(); }

    dubbo.registry.address=nacos://192.168.216.128:8848

不同服务注册到不同的注册中心 从上面的配置可以发现,我们开可以针对不同的服务配置到不同的注册中心,这个就不再浪费时间去演示了。
多个注册中心的集群 如果一个服务消费者引用了多个注册中心,那么这个时候服务消费者首先要做的就是先针对注册中心的负载均衡,然后得到一个目标注册中心之后,再从目标注册中心上获得服务提供者的地址列表再进行集群访问,实现原理如下图所示
分布式服务治理框架Dubbo的前世今生及应用实战
文章图片

当然,有三种方式来指定注册中心的负载均衡配置
  • 指定优先级

  • 同zone优先

  • 权重轮询

接口多版本支持 平时我们在开发接口的时候,可能会面临到一个接口的修改,但是这个时候因为线上会有一些项目正在使用这个接口,如果直接修改,很可能会对线上的服务造成比较大的影响。
因此对于这种情况,dubbo提供了接口版本的支持。
具体的配置方式
  • 服务端针对同一个接口提供不同版本的实现
  • 并在dubboservice注解中配置版本的声明
    @DubboService(registry = {"registry1","registry2"},version = "1.0")

  • 服务消费端指定消费版本号
    @DubboReference(registry = {"registry1","registry2"},version = "2.0") ISayHelloService sayHelloService;

多协议的支持 当公司原本采用其他的rpc框架,这个时候如果想迁移到dubbo这个框架上来,那么Dubbo提供的多协议支持就能够提供几乎零成本的迁移。
对于一个服务,可以同时发布多种不同协议的接口,也可以针对不同的接口发布不同的协议类型。并且从2.7开始,dubbo对于一些主流的协议做了支持,目前已经支持的协议有
dubbo协议、hessian协议、http协议、thrift、rmi、webservice、grpc、rest等。初次之外,dubbo还提供了非常灵活的可扩展性机制,对于有定制化需求或者目前正在使用的协议,dubbo不支持的公司,是可以自己去进行扩展。
整体的灵活性以及可插拔性的特性,相比spring cloud来说,更加强大。
JAX-RS协议说明 Dubbo中的REST(表述性资源转移)支持,是基于JAX-RS2.0(Java API for RESTful Web Services)来实现的。
REST是一种架构风格,简单来说就是对于api接口的约束,基于URL定位资源,使用http动词(GET/POST/DELETE)来描述操作
REST很早就提出来了,在早期开发人员为了实现REST,会使用各种工具来实现,比如Servlets就经常用来开发RESTful的程序。随着REST被越来越多的开发人员采用,所以JCP(Java community process)提出了JAX-RS规范,并且提供了一种新的基于注解的方式来开发RESTful服务。有了这样的一个规范,使得开发人员不需要关心通讯层的东西,只需要关注资源以以及数据对象。
JAX-RS规范的实现有:Apache CXF、Jersey(由Sun公司提供的JAX-RS的参考实现)、RESTEasy(jboss实现)等。
而Dubbo里面实现的REST就是基于Jboss提供的RESTEasy框架来实现的
SpringMVC中的RESTful实现我们用得比较多,它也是JAX-RS规范的一种实现
添加REST支持
  • 添加jar包依赖
    org.jboss.resteasy resteasy-jaxrs 3.13.0.Final org.jboss.resteasy resteasy-client 3.13.0.Final org.eclipse.jetty jetty-server 9.4.19.v20190610 org.eclipse.jetty jetty-servlet 9.4.19.v20190610

  • 修改配置文件
    dubbo.protocols.dubbo.name=dubbo dubbo.protocols.dubbo.port=-1dubbo.protocols.rest.name=rest dubbo.protocols.rest.port=8888 dubbo.protocols.rest.server=jetty

  • 修改api的接口定义
    @Path("/") public interface ISayHelloService {@GET @Path("say") String sayHello(); }

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Mic带你学架构
如果本篇文章对您有帮助,还请帮忙点个关注和赞,您的坚持是我不断创作的动力。欢迎关注「跟着Mic学架构」公众号公众号获取更多技术干货!
分布式服务治理框架Dubbo的前世今生及应用实战
文章图片

    推荐阅读