RPC之总体架构
Posted lovejune
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RPC之总体架构相关的知识,希望对你有一定的参考价值。
要完成一个高可用、高性能的RPC框架,需要对其架构的设计进行梳理,这里参考xxl-rpc框架,对整个项目进行梳理。
以上就是项目的整个构架,分为四个部分;
第一个是服务发布与引入,基于JDK动态代理以及Spring实现;包括invoker和provider。
第二个部分是服务注册,基于zookeeper实现;
第三部分是远程调用,基于并发编程基础知识实现;这里最为复杂,使用了分层的模式。
第四部分是远程通信,基于Netty实现。
服务引入
服务引入的实质就是输入请求,然后得到请求回来的对象。
创建一个处理引入的Bean类,属性是请求时需要的数据,包括:
1.网络传输的组件:Netty等,比如MiNA等 2.序列化方式,HESSIAN等,比如JACKSON 3.调用的方式,一共有四种,同步、异步、回调、单一长连接 4.负载均衡的方式,一共5种,RANDOM、ROUND、LRU、LFU、CONSISTENT_HASH 5.接口名称 6.超时时间,若超时,则请求报错 7.请求地址:ip+prot 8.进入token 9.是否使用回调 10.调用工厂,如果没有,默认实例化工厂。
返回的数据当然就是客户端所需要的对象。
这里请求和返回的操作,是该类的getObject方法执行。
分析:getObject方法是一个动态代理的过程,即给定一个接口,将接口实现。因此,使用动态代理来获取代理的对象。
简单来说,当得到了接口的所有信息:接口名,方法名,方法类型以及方法属性值。那么就可以去对服务端发起请求了。
这里需要注意几点,客户端发起请求不一定会将那些属性都配置上,因此,需要考虑各种配置不同的情况,应该做的操作
动态代理的意义是:当我们在客户端调用哪个方法,哪个方法被代理,传入Proxy函数的就是哪个方法,而不是代理整个类。
步骤1:接口名是一定要配置的,否则根本不知道请求什么,
若类名是一个通用的业务类,则转向得到这一接口中方法的特定请求(比如在通用业务类接口方法中定义了所要调用的业务的类名,方法和参数等),
若类名是一个Object类,则需要抛出异常,这种类不能当做业务类。
步骤2:有可能只配置了接口名,但没有配置请求地址,但不代表一定会出错,这里就是使用注册服务的好处,注册服务可以管理我们的各种请求地址,和请求的类;
若没有请求地址,则看看注册服务中有没有现在请求的类(一般会通过类名和版本号按照一定的规则形成一个key值存在注册服务中),若找到这个key,就能够得到所请求类的请求地址。
但请求地址可能没有,抛异常;可能有一个,就把这个当做请求地址;若有多个,就要调用负载均衡,让它来决定请求哪个。
步骤3:将请求封装起来,这里需要一个请求的封装类,
需要封装的内容包括:请求的id(通过该id可以在返回响应时获得同步的响应)、请求创建时间、请求的进入token、请求的类名、方法名、方法属性类型、方法属性。
步骤4:可以发送请求了,可以选择四种不同的调用方式,同步、异步、回调、单一长连接
同步方式
由于netty的NIO是异步模型,在调用的同时,并不会阻塞获取调用结果;因此需要实现sync-over-async(在异步中同步),其过程如下:
调用XxlRpcFutureResponse类的get方法
该类继承了Future类,并实现了其方法,可以使用其get方法来实现同步,即,如果done不为true,一直使线程处于阻塞状态,然后当达到超时时间后,主动唤醒该线程,然后就可以得到响应对象了。
具体来说,当客户端netty的nio线程组发送的请求后,netty的服务端会异步返回response,然后该响应会被传入一个调用工厂的唤醒调用的方法中,在该方法中,取出XxlRpcFutureResponse类,并将其设置response,然后唤醒线程,此时就可以通过get方法来得到response,这样通过唯一的resquesId就可以同步唯一的得到response对象了。
为啥说实现了同步,即在发送请求以后,线程阻塞,无法接收到其他所有的响应,只有这个响应收到或抛异常后,才能收到其他的响应。如果是采用异步方式,则会出现请求得到的是同一个响应的情况。
异步方式
在发送了请求之后,不马上得到返回的结果,而是在需要的时候再取,这时候就需要用异步的方式,所谓异步,就是利用一个InvokerFuture对象,来包装已经得到响应的respnsefuture对象,然后将InvokerFuture放到ThreadLocal里面,等需要的时候,取出来使用。
callback方式
这种方式也是异步的,而且是需要等待请求返回,因此,会造成异步的请求返回有多个,请求不及时。
过程是,设置一个ConcurrentMap集合,先将callback和request存入futureresponse中,然后将futureresponse放入ConcurrentMap集合中,这样,当我们发送了请求之后,请求会从handle类中,找到read方法,然后read方法会调用notify函数,将返回的response对象取到,同时,notify函数还会把futureresponse从ConcurrentMap集合中取出来,然后把callback从futureresponse中取出,最后调用callback中的方法。这样就会准确的执行callback函数了。
oneway方式
暂时不清楚
以上就是rpc的四种连接方式。
以上是关于RPC之总体架构的主要内容,如果未能解决你的问题,请参考以下文章