RPC(Remote Procedure Call)远程过程调用。其调用协议通常包含传输协议(TCP)和序列化协议(JSON或者XML)还包括动态代理等。
对比RPC和http的区别?
1.如果指http协议的话,http是一种网络传输协议,而RPC则包括传输协议(TCP)+序列化协议+动态代理调用+异常处理等一系列跨应用调用解决方案.
而http只是传输协议的一种,是RPC传输协议一个选项。
2.如果指的是暴露接口使用http调用(httpClient,restfull风格接口),那区别包括:
1.http优点:http方式可读性好,且可以得到防火墙的支持、跨语言的支持,现在Restful大有超过RPC趋势,比如微服务就采用Restful风格通信。
缺点:效率低(Http工作在网络七层协议的第七层,包括大量Http冗余信息)、封装调用复杂(要封装各种参数名和参数值),当大量的服务间调用时,这些缺点变得更为突出。
2.RPC采用的底层TCP协议,牺牲可读性提升效率,易用性(调用本地方法一样,不用封装参数值等操作),调用方可以像调用内部接口一样调用远程的方法。缺点:可读性差,方法不透明.有些框架如dubbo不支持跨语言。
本质上,两者是可读性和效率之间的抉择,通用性和易用性之间的抉择。这也是微服务跟RPC框架的区别
如何设计RPC框架?
主要从RPC特点易用性效率出发(调用方->被调用接口->代理方法->序列化->通信->反序列化->被调用的方法)。
1.使用动态代理(1.识别具体要调用的远程方法的IP、端口,2.将调用方法的入参进行序列化.3.通过Netty通信将请求发送到远程的方法中)
远程的服务就接收到了调用方的请求.它应该:(1.反序列化各个调用参数2.定位到实际要调用的方法,然后输入参数,执行方法3.按照调用的路径返回调用的结果)
2.通信方面采高性能的网络传输,可以采用 Netty 来实现,不重复造轮子效率高
3.序列化协议采用json
4.使用描述服务的语言,让所有的服务都用IDL定义,再由框架转换为特定编程语言的接口,这样就能跨语言.
5.基本一个简单的RPC就出来了,其次还需要考虑实现集群功能,因此的要服务发现、注册,监控机制等功能。
介绍dubbo
Dubbo是阿里巴巴开源的一个基于Java的RPC框架,可以调用方可以像调用内部接口一样调用远程的方法,简单易用。
而且提供了图形化监控中心,便于运维管理。在分布式服务应架构上,很多都采用的dubbo.
他的总体架构包括五个角色:Consumer,Registry,Provider,Container,Monitor。
大致的流程是:首先服务提供者 Provider 启动然后向注册中心注册自己所能提供的服务。服务消费者 Consumer 启动向注册中心订阅自己所需的服务。然后注册中心将提供者元信息通知给 Consumer, 之后 Consumer 因为已经从注册中心获取提供者的地址,因此可以通过负载均衡选择一个 Provider 直接调用 。之后服务提供方元数据变更的话注册中心会把变更推送给服务消费者。服务提供者和消费者都会在内存中记录着调用的次数和时间,然后定时的发送统计数据到监控中心。
原理:
服务的暴露起始于 Spring IOC 容器刷新完毕之后,会根据配置参数组装成 URL, 然后根据 URL 的参数来进行本地或者远程调用。会通过 proxyFactory.getInvoker,利用 javassist 来进行动态代理,封装真的实现类,然后再通过 URL 参数选择对应的协议来进行 protocol.export,默认是 Dubbo 协议。在第一次暴露的时候会调用 createServer 来创建 Server,默认是 NettyServer。然后将 export 得到的 exporter 存入一个 Map 中,供之后的远程调用查找,然后会向注册中心注册提供者的信息。服务的引入时机有两种,第一种是饿汉式,第二种是懒汉式(默认)。
知道SPI么:
SPI是Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的接口,它可以扩展框架和替换组件。 SPI的作用就是为这些被扩展的API寻找服务实现。约定在 Classpath 下的 META-INF/services/ 目录里创建一个以服务接口命名的文件,然后文件里面记录的是此接口提供的具体实现类的全限定名。
就可以通过接口找到对应的文件,获取具体的实现类然后加载即可,做到了灵活的替换具体的实现类。
关键类:ServiceLoader
参考文章: https://www.cnblogs.com/jy107600/p/11464985.html
优点:
不需要改动源码就可以实现扩展,解耦。
实现扩展对原来的代码几乎没有侵入性。
只需要添加配置就可以实现扩展,符合开闭原则。
缺点:
只能遍历所有的实现,并全部实例化。
配置文件中只是简单的列出了所有的扩展实现,而没有给他们命名。导致在程序中很难去准确的引用它们。
扩展如果依赖其他的扩展,做不到自动注入和装配。
扩展很难和其他的框架集成,比如扩展里面依赖了一个Spring bean,原生的Java SPI不支持。
为什么Dubbo不用JDK的SPI,而是要自己实现?
通过Java的SPI缺点回答。
因为Java的SPI在需要遍历SPI的配置实现全部实例化,假设有些实现类初始化过程比较消耗资源且耗时,但是你的代码里面又用不上它,这就产生了资源的浪费。
没有给他们命名,能以准确引用。dubbo给每个实现类配了个名字,通过名字去文件里面找到对应的实现类全限定名然后加载实例化,按需加载。
Dubbo为什么默认用Javassist动态代理?
因为快,且字节码生成方便(只需用字符串拼接就可以生成字节码,而ASM需要手工生成,成本较高,比较麻烦)