Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

是否可以增加一个重新路由的方法?我现在是swoole+yaf,然后在路由这关碰壁了 #191

Open
guoerwei opened this issue Sep 1, 2015 · 7 comments

Comments

@guoerwei
Copy link

guoerwei commented Sep 1, 2015

我用Swoole的HttpServer来搭配Yaf。
PHP版本是5.6.6
Yaf版本是2.3.4

参考的是https://github.com/LinkedDestiny/swoole-yaf这个。

在HttpServer启动的时候,会创建出几个worker进程。
在理想状况下,每个worker进程保存一份Yaf_Application的实例,用户访问网站的时候,触发onRequest事件,由Swoole来分配到其中的一个worker进程里执行。
这边的执行目前看来确实是用Yaf_Dispatcher::dispatch(new Yaf_Request_Http($request_uri))来分发请求最合适,但问题来了,它并不会重新路由。对于多module的模式就很致命了。

例子:
第一次访问是http://domain/firstModule/indexController/indexAction
dispatch之后正常访问到FirstModule->IndexController->indexAction
但因为无法重新触发路由,第二次访问时是http://domain/anotherModule/indexController/indexAction
结果仍然访问到的是FirstModule->IndexController->indexAction

单module的话应该是没问题的,使用dispatch可以正常指向,问题在于多module,moduleName在第一次dispatch之后就不会再变化了。即使自己再自定义Route时setModuleName,虽然会设置成功,但完全不影响变化。

由于Yaf_Application只能实例化一次,预先创建好的这几个worker进程不退出的话,不能再次实例化。
这两个机制的存在,导致Swoole+Yaf搭配很不愉快。

如果可以增加一个方法,让Yaf_Application重新路由,重新当成一个新的请求来处理。我想Swoole+Yaf真心绝配啊

@laruence
Copy link
Owner

laruence commented Sep 1, 2015

那么Swoole接受到请求以后, 会调用Yaf的什么呢? 我是说, 第二次请求从哪里进入Yaf?

@guoerwei
Copy link
Author

guoerwei commented Sep 1, 2015

worker创建之后,Yaf_Application就实例化,然后一直在那边等待。
每一次Swoole接受请求的时候,会在对应的worker里面使用
application->getDispatcher()->dispatch(new Yaf_Request_Http( $request_uri ));
这样来重新dispatch一个新的request_uri来表示一个新请求。
但根据文档上写的,dispatch的时候,路由只发生一次,多次调用的时候,路由不会再变化了吧,所以后续的请求都会被指到第一次dispatch时的module

@laruence
Copy link
Owner

laruence commented Sep 1, 2015

不应该啊, 你每次都是new的request对象, 不应该出现这个问题.

@guoerwei
Copy link
Author

guoerwei commented Sep 1, 2015

我是根据https://github.com/LinkedDestiny/swoole-yaf这个来测试的
每次onRequest里面都是new Yaf_Request_Http
try {
$yaf_request = new Yaf_Request_Http(HttpServer::$server['request_uri']);
$this->application->getDispatcher()->dispatch($yaf_request);
} catch ( Yaf_Exception $e ) {
var_dump( $e );
}
因为他的源代码new Application之后就马上run()了,所以后续所有的dispatch就都是defaultModule了
而如果尝试在new Application之后只bootstrap(),不立即run()。这样第一次web访问可以到一个正确的module,但是后续也就全部都到首次判断到的module了。
之前尝试了几种方法,结果最后都撞在这边,今天才发现应该是这个问题。
群里有问过别人,据说也是swoole+yaf时尝试多module失败,然后就一直用单module了。

@guoerwei
Copy link
Author

guoerwei commented Sep 2, 2015

https://github.com/LinkedDestiny/swoole-yaf这个例子里面,他swoole配置的时候是开了16个worker,并且dispatch_mode=1,使用的是worker轮循,所以问题不是一下子就会暴露出来,但多刷新几次,等worker轮循完一轮之后问题就出现了。每个worker被固定住了一个module,导致错乱。

@laruence
Copy link
Owner

laruence commented Sep 6, 2015

@guoerwei 如果是每次都是New一个Request 不应该出现不能路由的情况, 这个我能确认.

或者是你贴的代码片段有误, 是重用了一个request对象, 如果是这样, 你可以调用Yaf_Request->setRouted(FALSE)使它的路由信息失效, 强制重新路由.

@guoerwei
Copy link
Author

guoerwei commented Sep 6, 2015

我大概知道是为什么了,大概是因为我在多个module里使用了同名的controller,估计因此引发冲突。这是我的测试过程

我试着脱离了Swoole来重现了一下这个问题,直接使用了Hello world时使用的代码,要重现也比较容易
目录结构是

/controllers/
    Index.php
    Mmmm.php
    Nnnn.php
/modules/
    First/
        controllers/
            Index.php
            Mmmm.php
            Nnnn.php
    Second/
        controllers/
            Index.php
            Mmmm.php
            Nnnn.php

每个Controller里面都有着3个action:indexAction, aaaaAction, bbbbAction,访问到每个action的时候,页面打印moduleName - controllerName - actionName

比如这个是First/Nnnn.php的内容

<?php
class NnnnController extends Yaf_Controller_Abstract {  // 感觉可能是这个类名的定义引起的问题,因为Second/Nnnn.php的定义也是NnnnController

    public function indexAction(){
        echo 'first - Nnnn - index<br />';
    }

    public function aaaaAction(){
        echo 'first - Nnnn - aaaa<br />';
    }

    public function bbbbAction(){
        echo 'first - Nnnn - bbbb<br />';
    }

}

之后使用下面这个index.php来引导

<?php
$app        = new Yaf_Application(APP_PATH.'app.ini', '');
$dispatcher = $app->getDispatcher();
$app->bootstrap();

$request    = new Yaf_Request_Http('/index/index/aaaa');
$dispatcher->dispatch($request);

$request2   = new Yaf_Request_Http('/index/mmmm/aaaa');
$dispatcher->dispatch($request2);

// $app->getDispatcher()->getRequest()->setRouted(FALSE);

$request3   = new Yaf_Request_Http('/first/mmmm/aaaa');
$dispatcher->dispatch($request3);

$request4   = new Yaf_Request_Http('/first/index/aaaa');
$dispatcher->dispatch($request4);

这种访问方式应该会和Swoole很类似,单个进程里,多次调用dispatch,代码我是直接从环境里复制过来的,这是直接运行的代码,而得到的结果是这样的

index - index - aaaa
index - mmmm - aaaa
index - mmmm - aaaa
index - index - aaaa

前面2次是正常的,到第三次换module的时候,没有正常换过来。

我修改了一下

$request    = new Yaf_Request_Http('/first/index/aaaa');
$dispatcher->dispatch($request);

$request2   = new Yaf_Request_Http('/index/index/aaaa');
$dispatcher->dispatch($request2);

// $app->getDispatcher()->getRequest()->setRouted(FALSE);

$request3   = new Yaf_Request_Http('/second/mmmm/aaaa');
$dispatcher->dispatch($request3);

$request4   = new Yaf_Request_Http('/second/index/bbbb');
$dispatcher->dispatch($request4);

这次得到的结果是

first - index - aaaa
first - index - aaaa
second - mmmm - aaaa
first - index - bbbb

第二次错了,第三次恢复正常,第四次又错了

setRouted(FALSE)我是注释的,加上的话也没有效果。即使是自行写一个Route,并在里面使用setModuleName这些来控制,这样虽然getModuleName时是正常的,但实际上访问到的并不是对应的module

后面我又做了几次试验,总结下来是这么一个规律,当某次dispatch的时候,controller的值在之前有出现过了,则module会直接用之前那次的值。再来一个例子描述一下

$request    = new Yaf_Request_Http('/index/mmmm/aaaa');
$dispatcher->dispatch($request);

$request2   = new Yaf_Request_Http('/first/index/aaaa');
$dispatcher->dispatch($request2);

$request3   = new Yaf_Request_Http('/second/oooo/aaaa');
$dispatcher->dispatch($request3);

$request4   = new Yaf_Request_Http('/index/oooo/bbbb');
$dispatcher->dispatch($request4);

$request5   = new Yaf_Request_Http('/index/index/aaaa');
$dispatcher->dispatch($request5);

$request6   = new Yaf_Request_Http('/first/mmmm/aaaa');
$dispatcher->dispatch($request6);

得到的值是

index - mmmm - aaaa
first - index - aaaa
second - oooo - aaaa
second - oooo - bbbb
first - index - aaaa
index - mmmm - aaaa

根据总结出来的规律:
$request4重复了$request3的controllerName,都是oooo,所以$request4得到的moduleName就是$request3的second
$request5重复了$request2的controllerName,都是index,所以$request5得到的moduleName就是$request2的first
$request6重复了$request的controllerName,都是mmmm,所以$request6得到的moduleName就是$request的index

对于问题的猜测是
因为controller重名了,无论是写在哪个module里,它们的声明都一样叫作NameController,第一次访问的时候,实例化成功,第二次访问另外一个module的同名controller时,直接返回前一次声明的结果了。
或者是说,多module开发的时候,不应该出现同名controller么?但我感觉比如网站前台有一个/index/user的controller,在后台有一个/admin/user的管理面板也是挺正常的。或者是module里的controller命名需要改变?我目前还没有看到相关的文档……

开启命名空间后,应该也只是Yaf\Application自己的命名空间吧,controller重名好像还是无解的,这边有看到一篇文章http://blog.csdn.net/jrlapple/article/details/9341581,是通过修改源码的方法来处理的。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants