码迷,mamicode.com
首页 > 其他好文 > 详细

RPC框架实现 - 框架篇

时间:2015-05-01 00:37:36      阅读:291      评论:0      收藏:0      [点我收藏+]

标签:

RPC(Remote Procedure Call,远程过程调用)框架是分布式服务的基石,实现RPC框架需要考虑方方面面。其对业务隐藏了底层通信过程(TCP/UDP、打包/解包、序列化/反序列化),使上层专注于功能实现;框架层面,提供各类可选架构(多进程/多线程/协程);应对设备故障(高负载/死机)、网络故障(拥塞/网络分化),提供相应容灾措施。

 

关于服务端框架设计和实现,很多文章都有介绍,最常见的是epoll + prefork(进程池/线程池)实现方式,近几年业界又兴起了协程(coroutine)的讨论,协程的使用提升了程序性能,在生产环境应用中取得了较好的效果。

 

prefork模型

prefork模型(进程池/线程池)是服务端框架实现最常见的模型,事先拉起一定数量的进程/线程,当Server接收到请求时,分配一个进程/线程处理,框架具体实现如下:

技术分享

Epoll线程、Worker进程等各部分的职责分别是:

Accept进程:负责处理Client的连接,对新建连接的fd以轮询的方式派发给各个Epoll线程

Epoll线程:监听fd(epoll相关函数),如果有请求包则置入Inqueue;监听Outqueue(轮询Outqueue队列是否为空),如果有应答包则对相应fd作回包

Inqueue/Outqueue:分别用于存储请求包、应答包的共享内存

Worker进程:监听Inqueue(轮询Inqueue队列是否为空),进行业务逻辑处理,处理完成后将回包写入Outqueue

Master进程:负责拉起Worker进程,Master进程是Worker进程的父进程,运行过程中监听Worker进程的状态(waitpid),如果一个Worker进程在运行时异常退出则重新拉起一个Worker

 

以上各进程/线程按功能分主要有三块,如上图虚线分隔,分别是处理连接的Accept进程、监听fd的Epoll线程和处理业务请求的Worker进程。Aceept进程的逻辑很简单,Epoll线程和Worker进程则相对复杂,Epoll线程既要监听fd、将请求包置入Inqueue,又要从Outqueue取应答包并给Client回包,Worker进程的逻辑与业务相关,在框架层面不了解上层业务逻辑和行为。

 

分布式服务中存在大量RPC调用,按以上实现模型,在RPC调用期间Epoll线程所在的CPU核将进入IOWait状态,Worker进程在处理业务逻辑时也会让所在CPU核进入IOWait(如写log)。Epoll线程、Worker进程有可能让出CPU,有可能一直占用CPU直到IO操作完成,这取决于内核调度。是否可以进一步榨取CPU,让原本浪费在IOWait的计算资源用于真正的业务逻辑处理?

 

为解决这个问题,就要用到协程,由我们自己替代内核完成服务调度。

 

协程

协程在遇到IO操作时对fd设置事件监听,挂起并让出CPU,适用于处理一个个独立的任务。我们可以对以上Worker进程、Epoll线程的功能进一步拆解,分不同协程处理。 

Worker进程:Worker进程只负责业务逻辑处理,相应于Worker协程

Epoll线程:Epoll线程负责将请求包置入Inqueue、从Outqueue中取应答包并回包、与后端建立Socket连接,相应地交由EpollIO协程ClientIO协程ClientConn协程处理

 

Worker进程和Epoll线程的工作细分给协程之后,框架变为下图所示:

技术分享

各个协程任务对应一个主循环(上下文),如EpollIO协程循环监听fd,ClientIO协程循环查看Outqueue中是否有回包,ClientConn协程循环查看EndPointLink中是否有EndPoint(IP/port)需要建立Socket连接,Worker协程循环查看Inqueue中是否有请求包。

 

那为什么说协程的使用可以提高程序性能呢?举个栗子,ClientIO协程在处理Client回包时将挂起,让出CPU,这时另一个ClientIO协程可从Outqueue中取回包继续处理。相同时间内,协程较线程/进程处理了更多的请求。

 

虽然协程能提升程序性能,但也只在一定的场景中适用。各个种类的协程数量有限,因而协程不能被挂起太久,如果IO操作很重引起协程长时间挂起(如存储类服务),逐渐地空闲的协程将耗尽,新的请求将得不到处理。从编码实现协程的角度,也有一些需要注意的事项,相比多进程同步处理的环境,协程跑在异步环境下,一个worker进程会同时处理多个请求,里面会有多个类实例重叠,如果这些类原来用到全局变量,那么在协程中这些全局变量将变得不可靠。同样的,__thread变量、static变量也有类似情况,协程改造时需要将这些变量改为局部变量。

 

协程的实现方法请见《协程及libco介绍》。

 

小结

本文介绍了最常见的服务端框架实现模型 - prefork模型,另介绍了协程以及使用协程时需要注意的事项。协程并非适用于所有类型的程序,具有短时IO操作(如RPC调用、写log)的CPU消耗型服务(如纯逻辑服务),使用协程可以带来较大的程序性能提升。

 

Reference:《服务端程序设计和实现总结

 

 

RPC框架实现 - 框架篇

标签:

原文地址:http://www.cnblogs.com/bangerlee/p/4470125.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!