Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)

前言:刚完成的Spring基础专题本想更新源码的,但是发现分布式非常火,而我喜欢玩这个,所以今年我希望把我的知识可以分享给正在奋斗中的互

联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个互联网职场小白到一个沪漂湿人,一路让我知道分享是一件多么重要的事

情,总之不对的地方,多多指出,我们一起徜徉代码的海洋!

我这里做每个章节去说的前提,不是一定很标准的套用一些官方名词,目的是为了让大家可以理解更加清楚,如果形容的不恰当,可以留言出来,万

分感激!

1、Dubbo整合SpringBoot

在上一节我们用传统的Spring案例,结合官网,简单介绍了Dubbo对应不同的服务怎么进行发布,以及注册中心Zookeeper的使用,当然我这里是为了演示效果,根据官网的极力推荐,使用的注册中心是Zookeeper,实际上Zookeeper的功能远远不止这些,而在

市面上的注册中心有很多种,比如Zookeeper,Nacos,Simple,Multicast和Redis等等,你没想过Redis可以做注册中心吧,当然市面上用的比较少,还有Netflix系的SpringCloud Eureka等等,其实这些都是为了做服务治理的第一步。

那么本章开始,带你走进SpringBoot微服务化的Dubbo案例,里面很多干货,都是在实战中会用到的,里面不会涉及到很多业务代码,微服务的本质是运维!!说三遍,运维,所以应该更注重服务之间的关系,和遇到各种问题应该具备的解决方案!在实战中

都值得一试!

准备环境:

我们设定两个服务:

  • user-service-provider
  • order-service-consumer

一个api接口层

  • api-service:这个用上一节的项目

新建一个Springboot项目,我就很简单过两个图,不会的要百度下了。

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第1张图片

然后

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第2张图片

下一步后

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第3张图片

好了,我把上个项目中的代码都拷过来,创建两个服务的最终效果是这样的

两个服务的pom文件别忘记加上这个api-service,之前一定要install到本地!!


    com.chenxin.dubbo
    api-service
    1.0-SNAPSHOT

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第4张图片

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第5张图片

那么此时我们两个服务就创建好了,那么我们要改造下,既然是Springboot项目,就需要有Controller请求层,模拟一个请求去请求user-service-provider的业务方法。

新建一个OrderController,负责调用用户服务,返回用户id对应的地址。

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第6张图片

是不是发现少了什么,对,pom文件里的依赖没进来,上节我们的依赖是纯Dubbo,是这样的

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第7张图片

但是我们已经转成了Springboot项目了,场景驱动器会帮我们做自动装配的,所以我们只需要引入dubbo和springboot的starter就可以

注意下自己的Springboot版本,我的是2.x版本的,对照下图看看

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第8张图片

    
            com.alibaba.boot
            dubbo-spring-boot-starter
            0.2.0
        

其实你导入这个依赖你会发现一个东西,这个starter已经把上节我们需要的zk依赖,监控等依赖都带进来了。

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第9张图片

这样是不是就可以启动了呢?当然不是,我们上一节的配置文件,怎么办,怎么整合到Springboot项目中来呢?

当然有办法,看官网:

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第10张图片

取而代之的是用application.properties里面的key,value来替代xml的标签,这个我在讲Spring的时候经常提到

所以我们在user-service-provider中的properties文件重要这么配置,我特地做了对比,可以看看其实和配置文件没什么区别。

dubbo.application.name=user-service-provider
    
    

dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper
    
    


dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
    
    

那么对于暴露的服务,我们xml中是这么写的

    
    

如果很多接口需要暴露的话,这个量就上来了,所以dubbo为我们提供了注解,叫做@Service

不再是Spring的注解@Service

import org.springframework.stereotype.Service;

而是

import com.alibaba.dubbo.config.annotation.Service;

这里要注意!

所以暴露的接口上需要加上@Service注解

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第11张图片

然后我们要启动前,在主启动类上加上一个@EnableDubbo这个注解,表示开启基于注解的Dubbo

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第12张图片

记得要本地启动zk和dubbo-admin,不然你看不到效果,本地访问localhost:7001就可以了。此时,生产者我们已经成功注册到zk上了,接下来消费者order-service也要注册上去并且去订阅生产者

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第13张图片

 消费者也是一样,这么几步:

1、依赖引进来

    
            com.alibaba.boot
            dubbo-spring-boot-starter
            0.2.0
        

2、配置properties文件,用户服务默认走8080,所以你这里要声明下端口别冲突了

server.port=8081
dubbo.application.name=order-service-consumer
dubbo.registry.address=127.0.0.1:2181
dubbo.registry.protocol=zookeeper

dubbo.protocol.name=dubbo
dubbo.protocol.port=20880

3、引用的地方从@Autowired改成@Reference,@Service改成Dubbo的包

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第14张图片

4、主启动类上加上注解@EnableDubbo

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第15张图片

来测试下接口localhost:8081/initOrder/1,很明显已经调用成功了。

到这里,其实整合Springboot已经完成,我这边要详细的介绍一些注意事项:

1、对于覆盖策略

dubbo官网给出了启动时候的覆盖策略:

启动时候的参数  覆盖  外部配置的参数,这个外部配置,你理解为目的是实现配置的集中式管理:比如目前有很多主流的配置中心,

这部分业界已经有很多成熟的专业配置系统如 Apollo, Nacos 等,Dubbo 所做的主要是保证能配合这些系统正常工作。

外部化配置和其他本地配置在内容和格式上并无区别,可以简单理解为 dubbo.properties 的外部化存储,配置中心更适合将一些公共配置如注册中心、元数据中心配置等抽取以便做集中管理。

而外部配置参数  覆盖  代码的api设定的参数

代码的api设定参数 覆盖  本地的properties配置文件,这个是很重要的,优先级是从上到下

这个场景会经常使用到,尤其是上配置中心的时候,是非常有用的。

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第16张图片

2、启动时检查

Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,以便上线时,能及早发现问题,默认 check="true"

比如你生产者在启动了很久一段时间,然后启动消费者,这个时候偶发的会爆出消费者找不到服务,或者生产者没有启动,直接启动消费者,也会报错,我演示下只启动order-service-consumer这个服务

为什么要说这个呢,因为目前我演示的是消费者调用生产者,是单向的,但是在实际项目中,双向调用是很正常的事情,那就涉及到循环依赖,比如我用户服务要调用订单服务的接口,而订单服务也要调用用户服务的接口

这样就产生了双向依赖关系,在任何一方启动都会先检查另一方有没有正常启动。所以都会抛出异常,这样的场景,为了不想这些异常信息不断发生影响服务的判断,我可以设定启动的时候关闭检查,设定check的值为false;

这样启动的时候就不会报错,而在调用具体的服务的时候,才会去检查是否有相关接口被暴露在注册中心上,从而再抛出异常。

还有一点,如果你的 Spring 容器是懒加载的,或者通过 API 编程延迟引用服务,请关闭 check,也就是设定check=false,启动的时候不检查,因为Spring容器都已经懒加载了,不会在创建工厂的时候初始化bean,创建bean对象;

如果你设定为true,会导致本服务临时不可用时,因为启动的时候检查,检查啥呢,Spring容器都没初始化对象出来,所以你的服务会抛出异常,拿到 null 引用;

如果 check="false",总是会返回引用,不为空,当服务恢复时,能自动连上。

所以对于某个接口来说的话,我们基于配置文件的方式,对于消费者方,在启动的时候先不检查userService是否可用,可以这么设置

那么注解版的话,直接在@Reference注解里有个属性叫做check,置位false即可!!

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第17张图片

如果我想全局设置统一规则,只需要在你的properties设置这个

dubbo.consumer.check=false 表示消费者在启动的时候,都不去检查生产者是否可用,防止循环依赖的影响。

此外,我们只是说了启动的时候不检查,那么如果消费者启动的时候,检查注册中心是否存在呢?消费者一定也是要订阅注册中心的,先存在注册中心,进而去检查服务是否存在,所以对于注册中心也是一样

如果不希望在服务启动时候,因为注册中心没启动而报错,我们可以设置这个属性

dubbo.registry.check=false

说明启动的时候不检查注册中心,等注册中心服务启动的时候,再去订阅。

3、超时策略

超时策略指的是服务的消费方在引用服务的提供方时,可能因为网络的原因,或者服务的提供者执行一个方法要很长的时间,很长时间没有返回,会导致大量的线程被阻塞在调用服务方上,会出现一些性能异常。

为了防止这个问题的不断发生,我可以指定调用这个方法的超时时间,还是以上面的为例子,订单服务要调用用户服务,假设UserService数据返回的时间是3s,那么就需要在调用的时候,配置上这个属性time=xxxxx,单位是毫秒

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第18张图片

超时是有个默认值,这个缺省值是1000ms,有人说你怎么知道,还是看官网:

默认使用的是dubbo consumer的timeout,再看看dubbo consumer这个标签

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第19张图片

很明显,的确是1000ms

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第20张图片

所以可以试验下,我在提供方线程休眠个4s,而调用方给个3s后超时,看看会不会有异常。

很明显我在生产者线程休眠了4s后,消费者3s超时,结果报错超时,现象在生产中,会经常出现这个问题,合理选择一个好的超时时间是有效的。

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第21张图片

但是在实际生产中,我会发现有很多的老项目用dubbo的时候,更多会采用配置文件的方式,那么可能这里设置了一个超时,那边又设置了一个方法级别的超时,或者全局超时,哪里的会被覆盖,或者是优先生效呢?

后面的例子我以配置文件的方式演示,不以Springboot的方式,原因很简单,xml你懂了,注解的方式你自然就很明确!

比如说,我在消费端设置了UserService的调用超时时间,为5s,而在提供方我设置线程休眠4s,很明显这个是调用成功的对吧。

但是dubbo是可以支持接口方法级别的超时时间:


    

此时我设置方法getUserAddressList的超时时间为1s,那么是外层5s生效呢,还是内层方法1s的生效呢?

很明显报错了,思考下也知道,肯定是方法级别优先,接口次之,因为都已经精确到方法了,你接口生效的话,方法设了还有啥用吗,对吧。

看官网的解释:

  • 方法级别优先,接口次之,全局再次之
  • 级别一样的话,消费者优先,提供者次之

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第22张图片

那级别一样,消费者优先是什么意思呢?

举个例子,提供者提供接口,设定超时时间是2s,而同级别的消费者消费这个接口,超时设定5s,是哪边生效?

运行看下,很明显是消费者优先生效!!!

那上面两种情况我都已经做了解释,下面我设定一种情况,你们看看是什么优先。

假设我提供者设定了方法级别的超时时间,而消费者设定了接口级别的超时时间

这是提供方暴露接口,方法级别的超时时间为2s

这是消费方,接口级别的超时,5s

运行看下,结果其实是提供者方法级别的超时时间优先。

因为此时级别不一样,级别一样的时候,消费者才优先,级别不一样,那么还是生产者优先

所以我再总结下:

  • 方法级别优先,接口次之,全局再次之
  • 级别一样的话,消费者优先,提供者次之
  • 级别不一样,提供者优先,消费者次之

感兴趣下去可以验证下!!

4、重试次数

当我们在开发中某个服务由于各种原因,比如网络不佳,或者运行缓慢,导致了超时,消费者远程调用方法失败,我们可以通过调整重试次数,让消费者多试几次

这个重试次数,不包含第一次,比如我基于上述的代码,在消费者这边写了个重试次数retries=3,那么会额外重试3次,直到成功,所以最多会试4次!!!

提供方代码是:因为提供方超时设置优先,所以这个一定会超时,我们看下重试的验证

结果发现,日志里面重试了4次!!!

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第23张图片

在实际的生产环境中,如果你的提供方有多台服务在不同的机子上,那么会重试轮询不同的机器,我模拟下:

1、改提供方的端口20881,并且添加实现类的日志,表明模拟调用不同的服务,然后启动main函数

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第24张图片

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第25张图片

2、改提供方的端口20882,并且添加实现类的日志,表明模拟调用不同的服务,然后启动main函数

然后idea启动三个main函数,等于模拟了三个服务提供方,看看监控中心,已经启动三个服务。

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第26张图片

启动消费端试试

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第27张图片

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第28张图片

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第29张图片

所以一共是4次,多个服务会以轮询的方式进行重试。

先是UserServiceImpl.....1....发现超时,然后开始轮巡UserServiceImpl....2....发现也是超时,于是轮巡UserServiceImpl...3....也超时,于是最后一次又回来从UserServiceImpl....1....开始,以此下去,所以解释为什么UserServiceImpl...1....会执行2次了吧。

这里要注意下,我们设置重试次数一定要在接口幂等的情况下设置重试次数

什么是接口幂等性,意思就是方法无论执行多少次,结果都是一样的,比如查询,我带个条件查,无论查多少次,结果都是一样的,比如删除,我删除一次了,删除成功,删除第二次也是一样返回删除成功。因为第一次已经删除过了。

比如修改,修改一次,和修改多次,结果都是一样的,因为你每次都是执行同一个方法,带同样的参数。

但是非幂等性的话,就不能设置重试次数了,比如插入数据,每次插入都会产生新的效果。如果第一次超时了 ,虽然是超时,但是数据库已经拿到这个新增的请求了,那么就会去新增数据,而消费方等不及了,开始重试,又插入一条数据;

所以以后在设计分布式系统的时候,这接口的幂等性要注意。

所以对于新增,我们不想做重试,就应该设置retries=0!!!

5、多版本

当一个接口实现,出现不兼容升级时,可以用版本号过渡,版本号不同的服务相互间不引用。

什么意思呢,就是你某个接口实现修改了,也就是某些业务修改了,那么生怕上线的时候,万一这程序员写的bug太多,影响了整体的功能,这就很操蛋,于是我们可以让用户使用一部分先上线的版本功能,老的不变,具体来说

可以按照以下的步骤进行版本迁移:

  1. 在低压力时间段,先升级一半的提供者为新版本,让一半的新版本先试用看看有没有问题
  2. 然后过段时间,再将所有消费者升级为新版本,开始全部调用新的实现类,新的功能
  3. 最后将剩下的一半提供者升级为新版本

这样就有效的预防级联的问题。

现在我把所有的服务都停掉,我多写一个UserServiceImpl2,其实和UserServiceImpl1一样,只是输出不一样

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第30张图片

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第31张图片

提供方端口恢复成20880,并且暴露服务添加版本号

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第32张图片

这时候消费者指定版本号为1.0.0

然后启动提供者和消费者后,日志打印

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第33张图片

说明此时消费者调用的是老版本。同样你version设定新版本,那么就会调用新版本的接口。

其实这就是灰度发布!!!

6、本地存根

用官网的图:因为接口的实现都在服务端,有时候不一定所有的代码都一定要通过远程调用去执行,有时候也希望本地可以先做点事情,再选择去调不调用远程的服务,比如我要参数验证通过了,再去调用远程服务。于是本地存根就有用武之地了。

我们要在UserService接口写个消费端的实现类叫做UserServiceStub,实现了UserService,与此同时,Dubbo会在消费端会生产一个代理的实例对象,这个代理对象我们是看不到的(基于动态代理创建的),假设叫做UserServiceProxy,把这个代理对象,通

过UserServiceStub的构造方法传入进来,进行你的参数校验等等操作,验证是否成功了后,你再选择去不去调用远程的服务UserServiceImpl。

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第34张图片

所以我们在消费端写个类,比如我要先本地判断下参数是否合法!

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第35张图片

然后写完后记得要配置消费端,先走下本地存根。

然后启动消费者看看本地存根有没有被调用成功!很明显已经调用了。

Dubbo基础专题——第三章(Dubbo整合SpringBoot分析细节点)_第36张图片

那么本节就整理了通过xml的方式,进行Dubbo的很多基础点的配置,下一节我们继续分享Dubbo是如何暴露本地服务以及高可用的过程的!!!敬请期待。。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

你可能感兴趣的:(分布式专题,java)