- 论坛徽章:
- 4
|
本帖最后由 linux_c_py_php 于 2012-10-20 23:11 编辑
我就知道会有楼上这种问题, 所以我特意在代码里注明了Plugin这个东西, 我简单说一下, 如果你真的不会设计, 我可以给一段如何异步化Mysql的例子.
这是基于我阅读Lighttpd, Nginx很久很久之后(多少思考, 多少开源代码阅读量, 一般人也许不懂), 结合工作中的类似Server框架需求的个人开发经验, 才能做到有能力设计各种Server端, 任何井底之蛙的质疑, 请注意提问方式和素质, 我不希望被人身攻击.
我只能说, 如果读者用心来思考本帖, 一定是终身受益, 如果内心抵触或者不服, 错过了精彩内容只能说遗憾了.
========================
首先, 代码里任何一个请求, 你会发现它被设置了5秒后应答, 我本意是模拟一个慢查询操作, 但我知道这个例子并不明显, 因为看起来根本没有做什么事情, 就是轮了几次而已, 等到5秒后完成应答, 在一般人看来, 肯定是这样的, 如果换成Mysql查询, 你肯定觉得我的状态机什么都没有解决, 这就是问题的核心, 你仍旧不知道怎么异步化而且不影响event loop.
我相信, 如果你是抱着谦虚的学习心态认真读代码, 至少你对状态机有了基础的认识, 并且我希望大家能够注意到的是代码里的这一句:
//模拟一个plugin的慢速处理(plugin自身已保证异步)
请注意, Plugin本身需保证异步.
我怕例子本身没有真正的去做一个plugin(比如Mysql查询), 大家仍然体会不到异步化是如何做到的, 所以特别说明: Plugin自身保证异步化, 就像Lighttpd/Nginx的Proxy Plugin, 或者Mysql基本认证等Plugin, 它们都是在ON_RESPONSE阶段针对该Client回调的, 但绝对是不会阻塞Event loop的, 如何做到这一点, 我相信是你所疑惑的.
直接说方法, 讲Mysql如何在上面代码基础上异步.
1, Server启动后, 将所有Plugin的一系列回调函数注册到Server, 之后调用所有Plugin的create callback回调以便注册每个plugin在整个Server运行过程中所需的信息与数据. 假设这里是mysql plugin, 那么要做的是创建1个线程, 创建2个队列, 创建2个pipe, 注册其中一个pipe[0]到event loop上监听可读(当然注册到event loop需要提供一个callback供libevent回调), 而线程则监听另一个pipe[0]监听可读. 有什么用呢? 继续往下读.
2, Client被Accept后, 立即为Client调用每个plugin的init callback回调为该client创建每个plugin在处理请求应答过程中需要的一些信息与数据.
3, Client进入ON_RESPONSE状态后, 开始逐个回调每个plugin的on request callback回调, 开始处理请求, 假设我们用的mysql这个plugin来获取一些数据. 什么过程呢?
先说插件的回调方式, 假设这里有2个插件, 那么除非第一个插件的on request callback返回OK, 否则是不会调用第二个plugin的 on request callback的, 这是一个串行的过程, 如果第一个插件返回NOT OK, 那么没问题, 就像上面代码没到5秒一样, 继续干其他事去, 不需要等.
再说Mysql怎么运作, 我们知道第2步为该client初始化了每个plugin的一些数据结构和信息, 所以回调on request callback的时候, mysql plugin的callback可以维护client里设置的plugin需要用到的结构体和信息, 比如里面记录着该client是否完成了本次Mysql查询, 如果完成了, 那么会设置对应的字段为OK, 并让callback返回OK, 以便框架继续调用下一个plugin, 这就是一个基本原理. 实现呢?
Client进入ON_REQUEST, 回调Mysql on_request_callback, 在callback里会先检查client->mysql_plugin_data->status是否为OK, 如果是, 那么直接返回即可, 因为这表明之前已经完成了Mysql查询. 如果是NOT_OK, 那么就需要开始Mysql请求了, 但不能在callback里就查, 会阻塞event loop, 所以前面create plugin时候创建的线程起作用了, 首先向队列1中丢一个task, 里面起码记录着task所属的client, 然后向线程监听的pipe的pipe[1](线程随便怎么监听, 与event loop框架无关)写1字节通知线程, 然后callback就可以返回NOT_OK了, event_loop继续跑其他的事.
线程里检测到pipe[0]后会读pipe[0]1字节, 然后从队列里取出一个task, 开始查询task请求的Mysql语句, 不管多慢都无所谓, 不影响event loop, 反正客户端等得起就OK.
线程查询完成, 将task包括上查询结果, 丢到通往event loop的队列里, 并向通往event loop监听的pipe[1]写1字节触发 ,这样一个Mysql请求就从event loop走到mysql thread, 最终走回了event loop.
event loop监听的pipe[0]触发可读事件, 回调注册到libevent的一个callback, 该函数读pipe[0]1字节,从队列中取出包含结果的task结构体, 取出task中引用的client对象, 设置client->mysql_plugin_data->status = FINISH; 并将查询结果也放到client->mysql_plugin_data->data里, 然后这个callback就可以返回了.
在接下来client的write事件callback中, 处于ON_RESPONSE状态的client回调到Mysql plugin时, mysql plulgin的on_request_callback会检测到finish, 所以将Mysql查询结果从clieng->mysql_plugin_data中取出来与response做结合就可以设置status=OK返回OK了, 接下来其他plugin走同样的过程, 最终response被送出, client进入新的状态.
有时间, 我把Mysql描述的这个流程写成代码给大家参考吧. |
|