Dubbo(服务治理框架)
RPC
各服务都要实现rpc协议,才能实现服务间的调用
rpc:远程过程调用协议,是一种通过网络从远程计算机程序上请求服务
-
rpc原理:就是对象的序列化,反序列化以及序列化后数据的传输
和Feign的区别:微服务中远程调用Dubbo与Feign对比 - 悬铃木pp - 博客园 (cnblogs.com)
-
注册中心的作用
消费者根据我服务的名字调用服务就好,具体服务是放在哪里的不用关心
方便做集群和迁移,哪一天我们服务的ip地址要变也之前项目中的调用也不受影响
-
注册中心的原理
首先各个服务会把自己服务的名称和ip地址,注册到注册中心去
如果某个服务有集群的话,那么在注册中心就是一个服务名字对应多个ip地址
服务消费方会向服务中心去订阅消息,一旦注册中心的服务列表有变化,那么注册中心就把最新的服务列表发给服务消费方
服务消费方会把拉取到的最新服务列表保存在本地,调用服务的时候是去保存在本地的服务列表里面找到对应的服务和ip地址的
当项目运行的过程中注册中心挂了,是不影响现有的服务调用的,因为服务消费方用的都是本地的服务列表,但是会影响服务注册方,新的服务无法注册了,服务消费方也拿不到最新的服务列表,也就不知道有哪些服务有变化了
-
dubbo使用
-
原生生产者
其中的IUserService和User类是在一个member-api服务中,member-server服务包含了member-api服务,在member-server服务中把要给别人调用的接口IUserService暴露出去(给到注册中心),同时也把具体的实现类(UserServiceImpl)给到注册中心,一一对应
2.原生的消费者
-
原生生产者
消费者把自己的名字设置好,设置好注册中心的地址,设置好要消费的是哪一个接口,当前端发来请求调用IUserService里面的方法(比如getId)时,那么dubbo会给IUserService对应的远程UserServiceImpl实现类创建一个动态代理类(也就是IUserService的一个实现类),在这个代理类里面是可以拿到服务提供者(member-server)暴露出来的信息(我的推测是从注册中心拿到的),那么我们就可以在代理类里面根据具体的url和UserServiceImpl实现类来执行远程的调用了。
-
常见问题
启动时检查:在dubbo中消费者必须在生产者之后启动才不会报错,因为消费者会根据接口名去zookeeper中去找对应的远程服务路径,当生产者没启动时是找不到路径的,就会报错,那如果a,b两个服务之间互相消费,那么就怎么都启动不了了,此时就要关闭启动时检查:<bubbo:reference interface="cn.sushen.BarService" check="false">,将check设为false
超时设置:由于服务的性能是有限的,当我们的消费者去调用生产者暴露的接口时,如果一下子来了太多请求,而生产者对应接口中处理的速度又比较慢,那么就很可能导致生产者服务挂掉,如果各服务关联比较紧密的话,就可能导致服务器雪崩,那么此时就要设置一个超时时间,比如消费者发送请求如果2秒没有响应,就判断超时了,要断开连接了,如果我们想延长下超时时间,可以在<bubbo:reference设置timeout="3000",这就表示3秒没用反应才超时,超时时间一般不要设置超过2秒,否则可能导致服务器雪崩
上述2中设置的超时时间是针对整个BarService接口而言,如果我接口中有多个方法,我们只想给某些特定的方法设置超时时间,那么既可以在<bubbo:reference中配置:<dubbo:method name="getById" time="3000"/>这就表示只给BarService接口中的getById方法设置了3秒的超时时间,并且方法级别配置的超时时间比直接陪在类上面的更高,如果还单独设置了超时时间<dubbo:consumer timeout="4000"/>那么这个单独设置的全局超时时间是没有针对接口设置的超时时间优先级高的(如:<dubbo: id="my_barservice" interface="cn.sushen.BarService" check="false" timeout="1000"/>)
在生产者的spring中也是可以配置超时时间的,和消费者做对比时,优先级是怎样的呢?1.第一先看超时的类型(方法(最高),类(次之,这个类指的是暴露的接口类,对应上述的<dubbo: id="my_barservice" interface="cn.sushen.BarService" check="false" timeout="1000"/>这种),全局(最低)),不管是在生产者中还是在消费者中,比如在生产者中配置的是具体方法上的超时时间1秒,在消费者中配置的是类上的超时时间2秒,那么消费者消费的最终超时时间还是1秒,而不是2秒,2.第二,如果消费者和生产者配置的类型时一样的,比如都是在类上做的配置,那么是以消费者的配置为准(作为消费的超时时间)
重试次数:消费者调用服务时,如果失败后会默认重试2次(加上第一次调用,总共就是3次),那么如果每个方法每个请求都这样设置的话,一旦网络波动或其他原因导致生产者服务器变慢了,就可能导致雪崩,所以对于明确不需要的重试的方法,我们要将重试的次数设为0,retries=0(可以在配置接口类interface的时候设置,也可以单独设置,优先级也是类覆盖全局)
dubbo有重试的话,我们就要考虑幂等性问题,对于不是幂等性的方法,就不能配置重试操作,或者自己做好幂等性逻辑处理
-
dubbo多版本:用来进行灰度发布的,就是多个服务可以实现一个一个来升级,生产者可以发布多个版本,通过version来控制,消费者(可以配置version)也可以选择要消费的版本来达到不同的消费者可以消费不同的生产者,从而实现灰度发布
-
本地存根:作用是先检验本地的参数,当参数不合法时就不调用远程服务,减少远程服务的压力,第二个作用:是可以做容错处理,如果调用远程服务失败时,可以返回一个空对象出去
负载均衡策略:1.默认是缺省为random随机调用(随机是支持权重的,可以在dubbo监控台设置倍权和半权来改变权重),2.轮询就是轮着来,3.最少活跃调用数就是dubbo每次调用服务时记录下集群服务各服务的时间,花的时间最少,就证明效率最高,下次就让更多的请求去到那台服务器,直到发现下次发现其他更低响应时间的服务,就往更低的那台服务器发送请求,4.一致性hash,对请求进行hash算法,求余看落到哪一个服务器上,缺点就是可能导致某一台服务器压力过大,而其他服务器没用上;在<dubbo:refrence中配置loadbalance=“roundrobin"就表示轮询
集群容错:默认是已经处理了(自动切换),当调用集群中一台不成功时,就会调用集群中其他的服务器,也可以设置不同的集群容错处理:1.快速失败,只发起一次调用,失败立即报错,一般用来处理非幂等性的写操作,2.失败安全,出现异常时直接忽略,一般用于写日志,3.失败自动恢复,后台记录失败请求,定时重发,一般用于消息通知,4.并行调用多个服务器,只要一个成功即返回,一般用于实时性要求高的读操作,但是需要浪费更多服务资源,可通过fork=2来设置最大并行数,5.广播调用,逐个调用所有的服务,任意一台报错则报错,一般用于通知所有提供者更新缓存或日志等本地资源信息
服务降级:在服务器压力剧增时,对一些服务和页面有策略的不处理或换种简单的方式处理,从而释放服务器资源以保证服务器核心交易正常或高效运行
-
本地存根和远程代理的关系
当我们的消费者调用dubbo提供的远程服务时,会在客户端生成一个远程动态代理类,我们知道动态代理里面是有一个属性拥有真实类的(通过注册中心拿到具体的地址和实现类),在代理类中把我们要传入的对象进行序列化后,再通过真正的远程地址和实现类进行调用,本地存根是干什么用的呢?本地存根也是我们要调用的接口的一个实现类,dubbo会把动态代理类传入到本地存根里来,我们可以在本地存根里面做自己的逻辑处理,如果参数异常我们就放弃通过远程动态代理调用服务,减少生产者的压力,同时也可以做容错处理,使用try catch,当调用远程方法失败时,返回一个空数据给到前端然后做下友好界面的处理