聊聊RPC之Consumer

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/WANGYAN9110/article/details/69669433

上一篇我们介绍了RPC的Provider,包括它的发布过程和在设计一个Provider时需要考虑的问题,本文将介绍做一个调用方,在这个Consumer调用过程中,RPC又帮我们做了哪些事情和在使用别人提供的Provider时需要注意哪些问题。(和上篇一样,我们仍以Dubbo为例)

Consumer的调用过程

作为Consumer调用别人提供的一个服务,一般需要如下工作

  1. 引入client包

          <dependency>
               <groupId>info.yywang.demo</groupId>
               <artifactId>hello-client</artifactId>
               <version>1.0.0-SNAPSHOT</version>
           </dependency>
  2. Spring中引用远程服务

       <dubbo:registry address="zookeeper://192.168.0.122:2181" />
    
       <dubbo:reference id="helloService" interface="info.yywang.service.HelloService" />
  3. 调用远程服务

    public class HelloTest{
    
     @Resources
     private HelloService helloService;
    
     @Test
     public void testSay(){
       helloService.say('yywang');
     }
    }

通过以上代码,我们可以看出,只需要我们在spring配置中,增加一些配置,其他的就像我们使用本地接口一样,来使用远程接口。那么在这个背后,RPC框架帮我们做了什么呢?(今天不在赘述过程,也不在针对Dubbo框架,我们从几个问题思考)

Consumer端怎么找到Provider的地址

答:从注册中心获取到Provider的地址,并且根据路由规则,比如随机、轮训等方式,最后获取到一个将要方式的Provider地址,一般Consumer端会把Provider的地址缓存到本地,当请求来的时候,从本地获取。

Consumer端怎么和Provider进行通信

答:Consumer端和Provider的通信有两种方式,一种是同步调用,一种是异步调用。

  1. 同步调用

    一个工作线程在调用远程服务时,在得到结果之前,一直处于阻塞状态。在启动后,建立一个连接池,当一个请求来了之后,首先会经过对象序列化,然后从连接池里拿到一个连接,发送请求包,请求远程的服务,当远程服务响应结果之后,把连接放回连接池。然后工作线程继续执行以下代码

  2. 异步调用

    一个工作线程,在发起调用之后,在拿到结果之前,不会阻塞。在Dubbo的dubbo协议底层,使用dubbo协议就是基于Netty的底层通信模型。过程太长暂时不说了。

请求包里有哪些内容

答:请求包里有接口名,方法名字,参数类型等,参数值,请求ID。

异步情况下,怎么把请求、响应对应起来

答:在远程调用时,首先会生成一个ID用于标记本次请求,并且把这个ID发送给服务端,同时使用这个ID作为Key,将调用信息放在一个map里。在服务端响应的时候,把这个ID带过来,然后通过这个ID直接从map里找到对应的信息

Consumer怎么知道Provider挂了

答:在Consumer端缓存了Provider的服务地址之后,Consumer会和Provider保持心跳,同时在Provider挂掉之后,ConfigServer也会通知Consumer,Consumer在本地的地址列表里清除该地址

Consumer怎么知道Provider端增加了机器

答:同上ConfigServer会通知Consumer端增加服务地址

以上可以看出ConfigServer在这里扮演了很重要的角色,下篇文章我们将揭秘ConfigServer具体怎么实现通知的

Consumer的开发

以上是对于原理的讲解,我们回到作为RPC的使用者来说,我们探讨一些几个问题

面向异常编程

在使用了RPC之后,我们不得不面对一个问题,就是网络通信。在网络通信过程中,就会有各种不确定的问题,所以在使用远程接口调用时,我们要考虑以下异常场景:

  1. 超时了怎么办

    超时一般有几种情况,一种是在调用时机器负载比较高,响应比较慢。第二种,方法本身存在性能问题。第二种,可能在调用的过程中,网络出现了抖动,可能调用方的逻辑已经执行完成了,但是由于网络原因,没有及时的响应。

    对于超时情况,通常的处理办法是重试,一旦选择了重试,就需要在实现具体的业务时,分析需不需要做幂等。需要注意对于写的操作,如果重复执行,可能出现两次写入相同的值。关于如何做幂等又是一个话题,大家可以思考

    对于方法本身的性能问题,首先可以通过提高超时时间来解决当前问题,同时需要催促服务提供方做性能改进。

  2. 调用返回了失败

    返回失败的可能性有几种,第一种是业务错误,第二种是程序错误。对于业务错误很简单,根据业务逻辑处理即可,但是当对于业务错误,需要做监控时,需要记录好用于监控的错误日志。对于程序错误,可能出现在几点,一是程序bug,对于程序bug已经要记录好错误日志。第二是网络原因等错误。这种错误可以通过重试机制来保证。

错误日志,监控日志
  1. 错误日志,是排查线上问题时的最有利的武器,没有之一。线上处理问题,所以在调用远程服务的时候,一定需要记录好错误日志,但是日志也不是越多越好,日志过多也会代码排查问题的难度。并且在记录错误日志的同时,需要记录好关键的业务字段,比如订单编号、商品Code等等
  2. 监控日志,对于线上的出现的业务错误,我们通常需要通过监控来获取,有的时候监控日志和错误日志在一块,有的专门记录一个监控日志用于监控,个人推荐对于监控专门记录日志,用于监控。另外监控日志同样需要记录好关键的监控参数。
外部系统模型和本系统模型的适配

这个问题是在系统设计层面上的问题。就是在设计时,如何做到外部系统模型和本系统模型的适配。解决这个问题很简单,增加防腐层,就是增加一层防腐层,来将其他系统的模型适配到本系统模型,这样本系统的代码将不会充斥着其他系统模型的代码,并且在防腐层还能在提供方的系统发生变化时帮助本系统保持稳定。

如何测试你的代码

当你调用了远程接口之后,你想编写单元测试代码来测试自己的代码。如果你直接调用远程接口,有可能远程接口还调用了其他的远程接口。所以你需要准备满足这些系统的测试数据,这是非常麻烦的过程,通常这时就需要使用mock的方式来模拟远程接口。这样你只需要关心自己的逻辑即可。在集成测试时,在使用真实接口。

欢迎关注我的公众号MyArtNote

MyArtNote

猜你喜欢

转载自blog.csdn.net/WANGYAN9110/article/details/69669433