免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: linux_c_py_php
打印 上一主题 下一主题

[C++] [如何用状态机思想降低Server端开发复杂度? (GIT分支提供http server的实现框架示例) [复制链接]

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
31 [报告]
发表于 2012-10-21 13:10 |只看该作者
本帖最后由 starwing83 于 2012-10-21 13:18 编辑

回复 2# linux_c_py_php


    又看了一下代码,我对libevent不太熟悉,只是看了看libuv而已。问问,你怎么保证在线程完成任务的时候通知服务器端?我能想到的是两个办法:

1. 在ON_RESPONSE中做轮询。
2. 完成以后向Server/Client(随便哪个啦)发信号。

理论上第二个办法是最好的,发信号以后可以在下个循环立即处理,但我没看到你发信号的函数。
看上去是在ON_RESPONSE轮询,轮询也就算了,虽然有效率问题但也过得去,我想说的是,你咋保证了WantWrite以后你的回调会被**定期**调用捏?libevent做保证了?

我就觉得你的结构还是设计复杂了……我个人建议减少状态的数量,参照Linux的内核设计(额,那个状态更多……),将状态和业务分离,而与Client本身的真实状态挂钩,这样能保证一般性,具体说来,就是Client是基类,刚开始在就绪队列里面,然后调用虚函数做事情,如果要睡就调用Sleep,这会把Client放到一个等待队列,并提供一个“WakeUp”或者其他名字,加好锁或者用无锁队列神马的,功能是将Client放回就绪的队列。然后主程序循环做轮询,依次处理这两个队列,如果就绪队列为空就睡觉,都空可以选择退出,也可以继续等待。

这样的好处是业务层代码设计更简单了,框架先天考虑了多线程的阻塞方案,给出的Sleep和WakeUp都是线程安全的。轮询也不需要底层保证WantWrite以后回调被定期调用。而且重要的是待处理的Client数量被分流,并且在事务完成以后是主动通知,两个措施都能提高效率。

忍不住再加一句:说到异步单线程服务器设计,我强烈推荐你去看Android源代码中binder系统,和基于binder的Service架构的设计,这个设计十分优美,基本原理是coroutine,但是实现的非常细致,可以说是Android源代码里面少见的亮点。虽然这其实不是Web服务器,但是依然值得一看。

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
32 [报告]
发表于 2012-10-21 13:19 |只看该作者
看样你还是没有理解我的描述...我的架构就是完全异步通知的, 否则也没必要拿出来说.

这是nginx以及lighttpd的原原本本的实现, 我一点也不吹, 都是自己看开源代码积累下来的认识, 只是让同学们直接去读nginx/lighttpd会让同学们脱离了架构本身, 需要掌握太多了其他东西才能读得下来.

这么简单说吧, 接受到一个请求, 开始应答, 需要对socket注册write事件保持触发以便轮询检测plugin是否完成, 那么开始应答的时候要把一个task丢到队列里传递给线程, 线程里监听pipe事件读队列取task处理完成后, 向另一个pipe写1字节把task丢到另一个队列里, 这样应答就回到了主线程了(plugin在启动时刻注册了一个pipe在libevent上监听), 主线程检测到pipe, 就从另一个队列里取出一个task, 将task里引用的client的plugin的状态设置为OK就可以了. 接下来, socket的write事件触发时, 检测到plugin的状态已经OK了, 就继续调下一个plugin了.

是这样的, 整个libevent多监听了一个pipe[0], 线程里监听另一个pipe[0], 两个队列, 一个送请求, 一个送应答, 请求和应答是一个引用了对应client的task, 最终由libevent监听的pipe[0]的event callback取出task应答, 设置task->client->plugin-data->status = OK, 最终由socket本身的event callback中检查Plugin status=OK而完成.

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
33 [报告]
发表于 2012-10-21 13:22 |只看该作者
回复 32# linux_c_py_php


    哦,我完全明白了,的确是委托给libevent了,而且还不是轮询。的确是主动通知。

而且我估计在实现上还会有队列的存在(epoll),只是交给内核了,嘿嘿。

恩,这样的设计的确更健壮一些。看上去也不觉得臃肿了。毕竟这点点代码量和自己多写两个队列比起来还是微不足道的。恩。

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
34 [报告]
发表于 2012-10-21 13:23 |只看该作者
你也说了, Server设计就这么点东西, 其他的都是与Server本身设计无关的, 想异步就要用事件通知(不是信号, 是I/O复用或者条件变量等通知手段), 阻塞的摘到线程中, 线程间使用队列通信数据, 而Client的应答状态进一步推动演变是必须要轮询的, 这样才能将业务逻辑解耦出去, 否则是没有办法适应新添加的Plugin的, 也就说没法达到Module可动态配置的框架实现.

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
35 [报告]
发表于 2012-10-21 13:25 |只看该作者
恩, 我觉得多少应该赞同状态机(以前刷ACM的时候AC自动机算法的确更有意思)这个函数对编程有简化作用, 有这个概念可以应用在很多地方.

starwing83 发表于 2012-10-21 13:22
回复 32# linux_c_py_php

论坛徽章:
5
狮子座
日期:2013-08-20 10:12:24午马
日期:2013-11-23 18:04:102015年辞旧岁徽章
日期:2015-03-03 16:54:152015亚冠之德黑兰石油
日期:2015-06-29 18:11:1115-16赛季CBA联赛之新疆
日期:2024-02-21 10:00:53
36 [报告]
发表于 2012-10-21 13:26 |只看该作者
回复 34# linux_c_py_php


    补充一点,我提到的队列方式也是一个通知方式,效率上应该差不多,除了是自己维护比较放心以外也没有什么其他的优势,就是不知道有没有服务器在用。(Lua的服务器对coroutine的处理方式很多都是通过这种队列进行的,估计是觉得拿一个fd资源做通知比较浪费吧……)

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
37 [报告]
发表于 2012-10-21 13:28 |只看该作者
恩, 这样说, 我的目的是希望让其他开发参与者只开发Plugin/Module, 制作一个so, so里要求提供一个init函数, 主框架传入plugin结构体, 其他程序员实现的Init函数要把各个回调函数注册到plugin结构体上以便主框架以后调用.(在C++里我可以提供一个Interface类, 其他程序员可以继承Interface类实现所有回调, 并new一个对象返回到我的框架, 回调的说法是C里的概念)

linux_c_py_php 发表于 2012-10-21 13:23
你也说了, Server设计就这么点东西, 其他的都是与Server本身设计无关的, 想异步就要用事件通知(不是信号, 是 ...

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
38 [报告]
发表于 2012-10-21 13:28 |只看该作者
其实这个所谓单线程服务器的思路, 随便抓十个人, 估计有7,8 个人知道。 具体不同也就是细节方面而已, 也就是这里的状态机这些东西。

不能不说, 让 plugin 自己保证异步作为前提, 如果mysql 只提供阻塞接口的话, 其实就是意味着让 plugin 自己做线程, 然后服务器不承认这是多线程, 所以才能宣称自己是单线程的框架。  

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
39 [报告]
发表于 2012-10-21 13:30 |只看该作者
可以啊, 线程挂起在条件变量上等待也可以, 是个常用的手段.

starwing83 发表于 2012-10-21 13:26
回复 34# linux_c_py_php

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
40 [报告]
发表于 2012-10-21 13:35 |只看该作者
是的,

一方面状态机值得使用, 可以看到它显著的简化效果, 以前开发Server心里没有这个概念或者还没玩透根本没法实践, 结果每次写一个项目就是为这个项目现编现造一套新的设计, 而且对于阻塞的东西, 整个架构就直接多线程了, 基于状态机实现感觉像是流水线化了, 写10个Server也许10个Server都一样, 所以可以抽象出一个通用的框架.

另一方面, 你说的完全正确, webserver其实就是自称多进程单线程的master-worker架构, 但实际上把异步化的工作交给了plugin自己去保证, 实际上就算无良程序员写了一个module是阻塞逻辑的挂到nginx下, nginx也只能自认倒霉。

zylthinking 发表于 2012-10-21 13:28
其实这个所谓单线程服务器的思路, 随便抓十个人, 估计有7,8 个人知道。 具体不同也就是细节方面而已, 也 ...
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP