初识RPC

个人对RPC的理解 在如今微服务盛行的时代,RPC最主要的用处是,解决分布式系统通信问题。个人理解是,之前我们在本地写代码,调用本地的函数等,现在由于微服务,将函数等放到了远程的服务器上,需要远程调用。因此就需要开发出一套框架,使得我们能够在调用服务器上的函数时跟调用本地函数一样,不出错,能够实现函数的功能!当然这只是粗浅的认识,对于RPC,包含很多东西,比如负载均衡、异步调用等等,目前还在进一步的学习。👊👊👊 RPC整体的流程 RPC具体的几大模块 客户端 在客户端,跟之前调用本地函数有所不同,除了必要的函数方法名,函数参数等,还需要传入请求的服务对象名。同时使用TCP传输,需要处理拆粘包问题,保证数据正确传输,因此这部分跟Http协议的请求头、响应头相似,需要自己确定一套数据消息传输格式。而在调用时,分为同步调用和异步调用。所谓异步调用,就是在进行RPC远端调用之后,不等待结果,直接运行其它不需要RPC调用结果的代码,等远端调用得到结果之后,再处理需要结果的部分。这样可以提高客户端的吞吐量。 序列化和反序列化 序列化和反序列化,有几种适合序列化的协议,如Json、protobuf、Hessian等。 这部分就涉及到序列化协议的选型问题。在选择时,需要考虑以下几点: 序列化和反序列化的性能效率以及空间消耗,这一点直接影响整体RPC框架的性能和效率。 协议的通用性与兼容性 这部分目前对于我来说可能是最重要的,因为目前作为初学者,更重要的是,先跑通整体代码,等日后有了更深入的学习,再来完善其他几点。基于此选择目前的Protobuf(用的人挺多的好像👽👽👽) 协议的安全性 Protobuf 目前项目中使用的是就是Protubuf,网上也有很多关于Protobuf的教程,这里就不赘述了。主要就是把我们要发送的消息数据序列化成二进制数据。 针对这个消息数据,我们需要自己手动定义格式,为 .proto 文件 在发送的时候,需要按照 .proto 文件定义的参数进行发送,同时也需要发送函数的参数,这时间就需要明确头信息与函数参数的分界点。 传输协议 在使用序列化获得需要发送的数据之后,需要进行传输,而传输所使用的协议,可以用HTTP2、TCP、自定义的传输协议等等,从这里可以看出RPC和HTTP的关系,它们彼此独立,又可以使用。而对于RPC,相比于HTTP协议,会有更多的特性,比如:负载均衡、服务发现等等。 服务端 在服务端,需要接受经过序列化之后的数据包,进行拆包、消息解码、反序列化等,再根据解码出来函数名调用业务逻辑处理函数。 这部分仿照web服务器的思路,类似于Reactor模式,使用IO线程处理数据包的拆解,解码和反序列化等等工作,而业务逻辑使用线程池中的工作线程。 注册中心 最后一个问题,就是通信双方怎么知道对方是谁。对于客户端来说,自己想要调用的服务方法在哪台服务器上,如何通信? 针对这个问题,我们需要对服务器上服务对象方法以及对应服务器的IP进行记录,也就是注册中心集群,而获得服务对象方法提供者地址信息的过程叫服务发现。 两个功能: 服务注册:服务提供方在正式提供服务之前需要把自己的服务对象方法注册到注册中心上,注册中心会把这个服务提供者的地址信息以及提供的服务对象名以及函数方法名保存下来。 服务订阅:在服务调用方启动时,会去注册中心找自己需要的服务对象对应的服务提供者的地址信息,然后缓存到本地。 Zookeeper 而使用的注册中心,使用的是Zookeeper。这部分就涉及到分布式系统的一些概念,比如:CAP理论和BASE理论等等,目前还在进一步的学习。Zookeeper具有强一致性,只要有一个节点更新了,就会要求其他节点一起更新,保证每一个节点的数据都是完全实时同步的,所有节点上的数据在没有完全同步之前,不处理程序。由此可知,当分布式环境提供的服务和访问的机器越多,变化越多,Zookeeper就不理想了。 Zookeeper是一个开源的分布式协调服务,设计的目标是将复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。 目前先学习Zookeeper的使用,理论部分后续加强。 存储数据 服务端的示意图(还有客户端的数据,没有展示) 注册中心的管理者会在Zookeeper下创建一个服务根路径,可以根据接口来命名(对应最左边,最上边的“/”路径),根据这个路径再创建服务提供方的目录与服务这个方法的服务器的IP:Port 服务提供方注册的时候,在发起注册时,会在服务提供方目录中创建一个临时节点node,这个节点存储服务提供方的注册信息,比如UserServiceRpc/Login节点中存的就是提供这个方法的服务器的IP端口号。 服务调用方发起订阅时,服务调用方目录会创建一个临时节点,临时节点中存储调用方信息 当服务提供方的目录中节点发起了任何变化(新增节点、移除节点、节点上数据变动等等),Zookeeper就会通知发起订阅的服务调用方 翻译过来: ZooKeeper相当于是一个特殊的文件系统,不过和普通文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据,目录节点不行。ZooKeeper内部为了保持高吞吐和低延迟,再内存中维护了一个树状的目录结构,这种特性使ZooKeeper不能存放大量数据,每个节点存放数据的上线为1M。 服务对象名在ZooKeeper中以永久性节点的形式存在,当RpcServer与ZooKeeper断开连接后,整个节点还是会存在。方法对象名则以临时性节点存在,RpcServer与ZooKeeper断开后临时节点被删除。临时节点上带着节点数据,在本项目中,节点数据就是提供该服务方法的RpcServer的通信地址(IP+Port)。 Watcher机制 对于上面的第四点,这个是Zookeeper最重要的内容。就是在服务提供方目录中节点变化之后,Zookeeper该如何通知订阅的服务调用方。–Watcher机制 对于客户端来说,保存了若干服务对象方法的服务提供者的地址信息,如果有一个服务提供者出现问题,在Zookeeper中表现为服务提供方的某一个节点消失了。此时就需要告知客户端,该节点消失,无法继续提供服务,在本地也就需要将该节点删除。 Watcher机制是在客户端进行实现的,具体作用是,在运行之前,客户端告知服务端,如果服务端某一个节点或者这些子节点发生变化,都必须通知客户端。而对于服务端来说,该如何告知客户端呢?这就要求客户端必须在Zookeeper服务端上注册对某个节点的watch事件。服务端发给客户端watch通知,不过发送的watch通知里面不会包含某一个节点数据,只是高索节点发生了设么watch事件。同时watch的触发是一次性触发,假如某一个客户端收到这个watch通知之后,如果后续还想收到相同节点的watch通知,就必须再注册一次对这个节点的watch事件(Zookeeper3....

2022-05-20 · qinzhuzhu