关于Web编程异步模型的白日梦

早上刷牙,处于半睡状态。突然想起昨天晚上看到的那个 go-lang 的 MVC 框架,若使用 go func() 方式异步获取数据,应当是不错的。窃喜……梦醒……

在地铁上被前前后后那些特种男女逼到车角,无奈。又想起早上那个白日梦,遂上网搜索了一番。得老赵的佳作一篇《F# 与ASP.NET(1):基于事件的异步模式与异步Action》。之前看过,由于对微软无爱,未能细品。今日一读,如醍醐灌顶,豁然开朗。

遂整理思路如下,以待后用。

在说异步模型之前,先说说最常见的同步模型吧。例如下面的 PHP 代码:

<br />
// 获取数据<br />
$userInfo = getUserInfo();<br />
$newsList = getNewsList();<br />
$topRateNewsList = getNewsList('DESC `rate`');<br />
// 创建模板,绑定变量<br />
$tmp = CreateTemplate();<br />
$tmp-&gt;bind('userInfo', $userInfo);<br />
$tmp-&gt;bind('newsList', $newsList);<br />
$tmp-&gt;bind('topRateNewsList', $topRateNewsList);<br />
// 渲染模板,输出<br />
$tmp-&gt;render();<br />
echo $tmp;<br />

在这段代码中,所有调用都是顺序的。也就是说,如果 getUserInfo($userId) 没有返回用户信息的话,getNewsList(5) 永远都不会被调用。实际情况,可能是用户信息是从用户库取数据,新闻是从新闻库取数据。假设新闻库运行良好,而由于某种原因用户库宕机了,那么这次请求也就废掉了。故障也从一个库的宕机扩散到整个站点。

现在,我白日做梦的创建一种新语言 go-php,引入 go-lang 的关键字 go 到 php 中:

<br />
// 创建一个数据通道<br />
$chan = CreateChan();<br />
// 获取数据,并放到数据通道上去<br />
go getUserInfo($chan);<br />
go getNewsList($chan);<br />
$params = array('DESC `rate`');<br />
go getNewsList($chan, $params);<br />
// 创建模板<br />
$tmp = CreateTemplate();<br />
// 从通道上取数据<br />
while($d = $chan-&gt;read()) {<br />
    if ($d['error']) {<br />
        // 数据有错误 ……处理一下吧<br />
    } else {<br />
        $tmp-&gt;bind($d['key'], $d['data']);<br />
    }<br />
}<br />
// 渲染模板,输出<br />
$tmp-&gt;render();<br />
echo $tmp;<br />

好了,这其实不是什么新奇创造,这只是一个二段式异步调用(Begin/End)。这有点像大宗采购,采购商并不一件一件的商品进行采购,而是拿着清单说:“好了,兄弟,这是我要的货,你们帮我找齐,放到码头406号仓库去……”,然后他就在 406 号仓库等着点货了。这段代码还可以改进,就像采购清单一样,将这个清单推到数据层,数据层把数据返回到数据通道上去。恩,应该不少人在自己的应用中使用 MQ,Memcache 甚至 pipe 实现了这种异步了吧。

再来看另外一段 go-php 代码:

<br />
/**<br />
 * 回调函数<br />
 * @param $tmp 模板<br />
 * @param $d 返回的数据<br />
 */<br />
function callbackBind($tmp, $d) {<br />
    if ($d['error']) {<br />
        // 数据有错误 ……处理一下吧<br />
    } else {<br />
        $tmp-&gt;bind($d['key'], $d['data']);<br />
    }<br />
}<br />
// 创建模板<br />
$tmp = CreateTemplate();<br />
// 获取数据,并设置数据回调<br />
go getUserInfo(callbackBind, $tmp);<br />
go getNewsList(callbackBind, $tmp);<br />
$params = array('DESC `rate`');<br />
go getNewsList(callbackBind, $tmp, $params);<br />
// 如果不是所有数据都回调了,则阻塞<br />
$tmp-&gt;wait();<br />
// 渲染模板,输出<br />
$tmp-&gt;render();<br />
echo $tmp;<br />

这就是老赵的事件回调的异步处理的 go-php 版本。这有点像渠道商订货(或者淘宝上的无货代理?):“我需要XXXX,你帮我送到XXXX去”。然后坐等,所有的内容都送到了,就收钱走人。

对于数据读取的错误,只要处理得当也不是致命的。最多在渲染页面的时候,少某块数据,用户只会奇怪:“这次怎么打开没有新闻那部分的内容了呢……刷新一下看看……”。而不会说:“烂网站,又打不开了……”用户体验直线上升啊!

好了,梦就做到这里。相信这两种方式其实已经有实际案例了。我比较孤陋寡闻一些,了解的不多。而且有的东西,未经许可也不好多说……大家私下打听吧。

总之呢,两种异步模型各自有各自的好处。并行的数据存取,提高 I/O 利用率是其本质。王道啊……

4 thoughts on “关于Web编程异步模型的白日梦”

  1. 构想不错,实现了吗?如果并发量大,某个请求时间太长,会导致php进程堵塞,进而导致Nginx 504。已经屡屡遇到这样的问题了。仍未有好的解决方案,望赐教。

Leave a Reply

Your email address will not be published. Required fields are marked *