设为首页 收藏本站
查看: 1345|回复: 0

[经验分享] 设计模式在C语言中的应用--读nginx源码

[复制链接]

尚未签到

发表于 2016-12-27 08:46:19 | 显示全部楼层 |阅读模式
  市面上的“设计模式“书籍文章,皆针对Java/C++/C#等面向对象语言,似乎离开了面向对象的种种特性,设计模式就无法实现,没有用武之地了。
  是这样吗?设计模式的概念是从建筑领域引入的,本身从没歧视过面向过程编程语言,它只是对一类问题的普遍解决方案而已。面向对象语言因为有类、多态等特点,使得开发者们容易达到:隐藏细节、封装变化,而这与设计模式的目的比较一致,所以大师们爱把设计模式与面向对象语言二位一体的使用。然而,存在即合理,C语言直到今日仍然在大型软件工程中担纲主角,其种种设计方法其实与我们通常见到的设计模式本质是相同的。例如nginx这个纯C语言写就的的高性能WEB服务器,就有许多地方使用到了市面书籍提到的设计模式。下面通过nginx源码来看看C语言是怎么做的。当然,UML图都是我根据代码意图所画,并不准确(C语言真没法画UML),只用于方便理解,呵呵。
  strategy模式:
  该模式用于客户代码在“无知”状态下,可以使用种种不同的实现。下面我们以nginx对网络IO操作的封装部分来看看C语言的实现吧。
  设计模式就是通过封装变化来解耦,所以,我们先要找出网络IO操作的变化点来。nginx是跨平台的,它会支持linux、freebsd、solaris等操作系统,而每个操作系统的网络IO操作是不同的,这就是变化点了。
  所以,nginx首先定义了ngx_os_io_t来封装这些变化。
  typedef struct {ngx_recv_pt        recv;ngx_recv_chain_pt  recv_chain;ngx_recv_pt        udp_recv;ngx_send_pt        send;ngx_send_chain_pt  send_chain;ngx_uint_t         flags;} ngx_os_io_t;
这里有五个函数指针(*_pt都是函数指针)和一个变量,用于收发网络数据,我把它理解为OO中的abstract class(每个ngx_os_io_t定义的变量都会重新实现这五个函数)。
  拥有函数指针的struct,我通常认为它们是OO中的abstract class,实现它们的文件(一堆函数)要对应到OO上,我则喜欢把它们当做子类来看。对于void*这样的成员,要根据意图来看了,通常我会转换成聚合加继承的关系。
DSC0000.gif

  ngx_io会在相应的ngx_os_specific_init方法中,来策略性的选择到底使用哪个实现。客户代码只需要简单的调用ngx_io中的方法即可。
  adapter模式:
  这个模式用以适配接口,通常都是我们已经定义好一种接口了,有一个新的实现却有着不同的接口,接下来adapter就开始发力了。下面我们仍然以nginx对网络IO操作的封装部分来看。
  linux平台下可能存在普通的IO或者异步IO方式。我们在最初已经封装好ngx_os_io_t接口了,客户代码都是这么直接使用的。现在linux实现了异步IO,而它的调用方式与普通的读写IO接口完全不同,所以,如果要支持aoi就需要一层adapter来适配ngx_os_io_t,这就是adapter方式了。
DSC0001.gif

  上图中,ngx_os_aio适配了原生的异步IO接口,这样,用户代码仍然像以前一样,只要直接使用ngx_io中的五个接口方法,当nginx的IO部分支持linux aio后,用户代码不需要修改。
  bridge桥模式:
  桥模式用于将抽象和实现分离,各自都能独立的变化。下面以nginx的核心概念module举例,虽然有些牵强,因为nginx的代码从来没这么用过:通常都是一个抽象module context只对应着一个实现module来用,但是,毕竟这种结构下还是可以达到抽象与实现分离的目的,桥模式只好对应到这上面了。
  nginx是以module的概念贯穿始终的。它有一个基本的抽象层ngx_core_module_t(从意图上判断,context有抽象接口的功能,虽然简单从语法上看不出)。然后,nginx module有三个基本类型,分别是event(处理各种事件模型,如epoll/select等),http(处理各种http协议的事件),mail(处理mail相关的事件)。针对每种类型的module,都有许多个实现,比如event module就有9个实现,这里的每个实现其实也是个子类。
  但是,在我们理解桥模式时,这些子类暂时要被看成是event module的实例。代码中看,像ngx_epoll_module这样的子类中,还是把一些通用的细节隐藏给ngx_event_core_module来做(管理这个词更合适)了。从这个角度可以认为,通过context接口,把三个基本module实现分开了。来看看类图:
DSC0002.gif

  nginx自己用时,是以ngx_module_t中的type成员来决定使用哪个实现的。目前的nginx代码中,如果用了一种接口就一定会指定相应的type。可是实际上,这也可以用来展示桥模式。以事件module为例来看看:
DSC0003.gif

  由于UML本就是针对OO语言的,所以以上我画的类图都比较牵强,什么是继承?什么是聚合?在C语言中,往往都是通过几个函数指针,或者void*指针实现各种封装和多态。没有什么语法上的关联,我就只能从代码意图中来判断了。而代码意图这个比较虚,因为不同的角度理解出来都不一样,所以这个确实不好画。太灵活了点,我只能从一个便于说明的角度来看,例如:上面的ngx_devpoll_module其实就是一个ngx_module_t,呵呵,但是,实际上它最关心的是ngx_event_actions_t的实现,如果完全根据语法来看,根本说不通的。但从代码意图中看,这些module并不关心ngx_module_t,所以我认为,它们只是在实现ngx_event_module_t了。
  当然以上只是一家之言,不必当真,如果对nginx源码有研究的话,欢迎各位拍砖。
  客观的说,C语言确实在封装上很差,就像nginx,如果我们要开发一个处理http协议的module嵌入进nginx进程,必须了解ngx_http_module里到底做了什么,真没隐藏啥细节,module开发者们表示很郁闷。上面的这些设计模式,只是做到了代码上的解藕。如果nginx用C++写的话,我相信,现在第三方module都能数以万计了。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.iyunv.com/thread-319944-1-1.html 上篇帖子: Nginx源码分析之启动过程 下篇帖子: nginx+tomcat7+memcached做集群以及session复制
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表