RPC
Last updated
Last updated
一次完整的RPC同步调用流程简述如下: 1)服务消费方(client)调用以本地调用方式调用服务; 2)client stub (client proxy)接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体; 3)client stub找到服务地址,并将消息发送到服务端; 4)server stub (server proxy)收到消息后进行解码; 5)server stub根据解码结果调用本地的服务; 6)本地服务执行并将结果返回给server stub; 7)server stub将返回结果打包成消息并发送至消费方; 8)client stub接收到消息,并进行解码; 9)服务消费方得到最终结果。
Client端 // int l_times_r = Call(ServerAddr, Multiply, lvalue, rvalue)
将这个调用映射为Call ID。这里假设用最简单的字符串当Call ID的方法
将Call ID,lvalue和rvalue序列化。可以直接将它们的值以二进制形式打包
把2中得到的数据包发送给ServerAddr,这需要使用网络传输层
等待服务器返回结果
如果服务器调用成功,那么就将结果反序列化,并赋给l_times_r
Server端
在本地维护一个Call ID到函数指针的映射call_id_map,可以用std::map<std::string, std::function<>>
等待请求
得到一个请求后,将其数据包反序列化,得到Call ID
通过在call_id_map中查找,得到相应的函数指针
将lvalue和rvalue反序列化后,在本地调用Multiply函数,得到结果
将结果序列化后通过网络返回给Client
1.长连接or短连接 总不能每次要调用RPC接口时都去开启一个Socket建立连接吧?是不是可以保持若干个长连接,然后每次有rpc请求时,把请求放到任务队列中,然后由线程池去消费执行?只是一个思路,后续可以参考一下Dubbo是如何实现的。
2.服务端线程池 我们现在的Server端,是单线程的,每次都要等一个请求处理完,才能去accept另一个socket的连接,这样性能肯定很差,是不是可以通过一个线程池,来实现同时处理多个RPC请求?同样只是一个思路。
3.服务注册中心 正如之前提到的,要调用服务,首先你需要一个服务注册中心,告诉你对方服务都有哪些实例。Dubbo的服务注册中心是可以配置的,官方推荐使用Zookeeper。如果使用Zookeeper的话,要怎样往上面注册实例,又要怎样获取实例,这些都是要实现的。
4.负载均衡 如何从多个实例里挑选一个出来,进行调用,这就要用到负载均衡了。负载均衡的策略肯定不只一种,要怎样把策略做成可配置的?又要如何实现这些策略?同样可以参考Dubbo,Dubbo - 负载均衡
5.结果缓存 每次调用查询接口时都要真的去Server端查询吗?是不是要考虑一下支持缓存?
6.多版本控制 服务端接口修改了,旧的接口怎么办?
7.异步调用 客户端调用完接口之后,不想等待服务端返回,想去干点别的事,可以支持不?
8.优雅停机 服务端要停机了,还没处理完的请求,怎么办?
9.集成Spring 在实现了代理对象通用化之后,下一步就可以考虑集成Spring的IOC功能了,通过Spring来创建代理对象,这一点就需要对Spring的bean初始化有一定掌握了。
10.限流熔断降级 在一个服务作为调用方去调用另外一个服务时,为了防止被调用的服务出现问题(如频繁超时)而影响到整个服务,调用方的服务也需要进行自我保护, 最有效的方式就是熔断处理。