Giter Site home page Giter Site logo

pinguo / php-msf Goto Github PK

View Code? Open in Web Editor NEW
1.8K 1.8K 318.0 3.37 MB

PHP微服务框架即Micro Service Framework For PHP

License: GNU General Public License v2.0

PHP 99.93% HTML 0.07%
microservice microservice-framework mvc php php-framework php-msf swoole swoole-framework

php-msf's People

Contributors

kashin-j avatar pinguo-lipengcheng avatar pinguo-tonganping avatar pinguo-xiaoshiyong avatar pinguo-xudianyang avatar pinguo-zengzhiqiang avatar pinguo-zhanglu avatar shellvon avatar sunny5156 avatar syyongx avatar toxmc avatar viaweb3 avatar wiseker avatar xudianyang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

php-msf's Issues

有个小提议。怎么调试db sql语句?

Minner.php
public $debug = false;
public function debug($debug = true){
    $this->debug = $debug;
    return $this;
}

public function go($bindId = null, $sql = null)
    {
        if ($sql == null) {
            $sql = $this->getStatement(false);
        }
        if($this->debug){
            writeln($sql);//打印sql
        }
       ..................
}

类加载重复。

app/Controllers/Lists.php
   use \App\Server\Goods.php
     actionIndex(){
             this->getObject(Goods.php);//这里会报Goods.php已重复加载。
     }

app/Models/Goods.php
app/Server/Goods.php

default

docker 镜像问题

1、使用alpinelinux 进行重新打 (内核4.x了,但是无论是编译安装swoole 还是通过pcel安装swoole 都回出现找不到 libaio 事事上已经安装了)
2、使用centos7 进行制作(1.1 G )

getObject通过对象池生成对象

不是很理解它的价值,因为
class BaseModel extends Model{
public function __constrance(){
echo "BaseModel";
}
}

Controller里getObject(BaseModel::class,);两次,都会打印BaseModel,也就是说,第一次生成的object,第二次还是生成object.

模版渲染异常时文案提示可能是错的

目前渲染的逻辑是当所有目录遍历完之后如果found=false则会抛出异常:

php-msf/src/Base/Output.php

Lines 265 to 278 in a21350e

foreach (getInstance()->viewResolvePaths as $basePath) {
try {
$template = getInstance()->templateEngine->setDirectory($basePath)->make($view);
$responseBody = $template->render($data);
$found = true;
$template = null;
break;
} catch (\Throwable $e) {
// pass.
}
}
if (!$found) {
throw new Exception(sprintf('A template named %s was not found in any folder, please check again', $view));
}

但是如果当渲染时出现一些其他的逻辑错误时,比如模版内语法错误/调用的方法不存在等情况时,会在267/268行抛出异常,从而错误的认为是模版没有找到。

解决思路:
在render之前判断模版是否存在,不存在则跳过当前目录。不再在此处捕获异常(Plates内部爆出的异常都是LogicException,无法根据异常类型区分是模版没找到还是其他错误,因此最佳方式就是把模版内的错误直接冒泡出来)。

worker 进程死掉

worker 进程死掉,查看日志,有大量如下记录

WORKER Error {"worker_id":2,"worker_pid":22443,"exit_code":0,"message":null}

貌似一直重启一直死。

根据 swoole 的文档:

$exit_code 退出的状态码,范围是 1 ~255

exit_code 为0,message 为空,咋回事呢?

Mi.php context = null

/**
     * 通过对象池生成对象
     *
     * @param array $args
     * @return \stdClass|mixed
     */
    public function getObject(...$args)
    {
       //请求间隔久时,这里会出现this->context = null的情况。Miner过来的getObject(Mysql::class,...)
        return $this->context->getObjectPool()->get(...$args);
    }

miner.php bug

$pool = this->getMysqlPool();
$pool->where('name', 100);
$pool->from($table);
$pool->go();

必现name = 100问题。因为
name改为group,(group为table表的字段)也会报错。

后面有没有考虑添加服务注册发现,熔断等特性?

看了下整个项目的思路:

  1. 使用swoole异步IO提升效率
  2. 使用协程提升回调效率,避免callback hell
  3. 使用对象池减少内存占用

收益良多,感谢~

但感觉目前没有服务注册发现&熔断等特性,如果直接写死ip的话,感觉不利于维护和扩展,后面有考虑加上这一块吗?

模版渲染引擎使用官方维护的repo

将目前使用的目标渲染引擎pinguo/plates 修改为官方维护的hephpleague/plates

替换为官方repo的主要原因是:

  • 目前pinguo维护的plates变更是支持多级目录做出的变更其实是多余的,因为原plates可以完成子目录渲染。
  • 为了这一点,我们不得不花费精力去维护一个fork的repo.比如当官方的plates提交了一个新的features或者bugfixed,我们fork的repo需要保持同步,于此同时需要确保官方的变更与我们的变更不要产生冲突。 与其这样,还不如花费更多的精力将msf做的更好。
  • 目前的多级视图渲染如果需要使用fetch/layout等内置函数时有BUG(无法正确找到视图文件)

另外,根据官方文档关于视图加载策略的描述:

默认情况下框架会根据请求的控制器名和方法名自动加载视图文件,比如:

http://127.0.0.1:8000/Demo/TplView

这样的URL会自动首先加载的视图文件为app/Views/Demo/TplView.php,如果失败,会继续加载php-msf/src/Views/Demo/TplView.php,如果还是失败,则会抛出异常。

即优先加载用户@app/Views目录下视图,然后尝试加载框架目录下视图。

  1. app/Views
  2. php-msf/src/Views

将视图渲染引擎修改为官方repo之后,仍然可以保证以上策略的完美执行且支持更灵活的策略配置。

为了做到这一点,框架需要修改两个地方:

  • HTTPServer增加需要加载的视图目录(为了更加灵活,可以支持配置):
/**
 * 视图文件存储路径,您可以指定多个路径以便让框架载入.
 * 数组顺序即加载顺序,当然,框架会自动将框架视图目录放在最后resolve.
 *
 * @var array
 */
public $viewResolvePaths;
/**
 * HttpServer constructor.
 */
public function __construct()
{
    parent::__construct();
    $this->viewResolvePaths = $this->config->get('http_server.view_paths', [
        APP_DIR.'/Views'
    ]);
    foreach ($this->viewResolvePaths as $path) {
      // check  dir exists and so on.
    }
    // 框架自带的视图目录.
    $this->viewResolvePaths[] = $this->MSFSrcDir;
}
  • 当有了需要遍历的视图目录时,只需要在src/Base/Output.php中将加载视图的策略代码稍微修改一下:
$responseBody = null;
$found = false;
foreach (getInstance()->viewResolvePaths as $basePath) {
    try {
       $template = getInstance()->templateEngine->setDirectory($basePath)->make($view);
       $responseBody = $template->render($data);
       $found = true;
       break;
    } catch (\Throwable $e) {
        // pass.
    }
}
if (!$found) {
    throw new Exception("A template named: {$view} was not found in any folder, please check again");
}
$this->end($responseBody);

现在即可正常使用多级渲染和多目录加载策略了。
比如$this->outputView(["data" => "someData"], "path/to/file"); 框架会自动加载

  1. app/Views/path/to/file.php 如果没有加载此文件,尝试第二步,有则渲染之。
  2. php-msf/src/Views/ path/to/file.php 如果此文件没有,则报错,有则渲染之。

Call to undefined method swoole_http_response::gzip()

/Base/Output.php;296:Call to undefined method swoole_http_response::gzip()

php --ri swoole :报以上错误
swoole

swoole support => enabled
Version => 1.9.23
Author => tianfeng.han[email: [email protected]]
epoll => enabled
eventfd => enabled
timerfd => enabled
signalfd => enabled
cpu affinity => enabled
spinlock => enabled
rwlock => enabled
async redis client => enabled
async http/websocket client => enabled
Linux Native AIO => enabled
pcre => enabled
mutex_timedlock => enabled
pthread_barrier => enabled
futex => enabled

Directive => Local Value => Master Value
swoole.aio_thread_num => 2 => 2
swoole.display_errors => On => On
swoole.use_namespace => Off => Off
swoole.fast_serialize => Off => Off
swoole.unixsock_buffer_size => 8388608 => 8388608
[root@localhost etc]#

getObject()//第二参数的传值。是怎么实现的。

Pool.php类的get()//没见到this->getObject(Class, Params);//没见到Params的传值。
$reflector = new \ReflectionClass($poolName);
$obj = $reflector->newInstanceWithoutConstructor();
$obj->__useCount = 0;
$obj->__genTime = time();
$obj->__isContruct = false;
$obj->__DSLevel = Marco::DS_PUBLIC;

通过composer 安装新的依赖和msf本身的依赖版本冲突,如何解决

msf依赖monolog的2.0dev版本,但我要安装的overtrue/wechat 依赖monolog的release 1.X的版本,通过composer安装overtrue/wechat 时候报错。请问如何解决?
Conclusion: don't install overtrue/wechat 4.0.1
- Conclusion: don't install overtrue/wechat 4.0.0
- Conclusion: don't install overtrue/wechat 4.0.0-beta.4
- Conclusion: don't install overtrue/wechat 4.0.0-beta.3
- Conclusion: don't install overtrue/wechat 4.0.0-beta.2
- Conclusion: don't install overtrue/wechat 4.0.0-beta.1
- Conclusion: don't install overtrue/wechat 4.0.0-alpha.2
- Conclusion: don't install overtrue/wechat 4.0.0-alpha.1
- Conclusion: remove monolog/monolog 2.0.x-dev
- Installation request for overtrue/wechat ~4.0 -> satisfiable by overtrue/wechat[4.0.0, 4.0.0-alpha.1, 4.0.0-alpha.2, 4.0.0-beta.1, 4.0.0-beta.2, 4.0.0-beta.3, 4.0.0-beta.4, 4.0.1, 4.0.10, 4.0.11, 4.0.12, 4.0.13, 4.0.14, 4.0.15, 4.0.16, 4.0.17, 4.0.18, 4.0.19, 4.0.2, 4.0.20, 4.0.21, 4.0.22, 4.0.3, 4.0.4, 4.0.5, 4.0.6, 4.0.7, 4.0.8, 4.0.9, 4.0.x-dev].
- Conclusion: don't install monolog/monolog 2.0.x-dev
- overtrue/wechat 4.0.x-dev requires monolog/monolog ^1.22 -> satisfiable by monolog/monolog[1.22.0, 1.22.1, 1.23.0, 1.x-dev].
- Can only install one of: monolog/monolog[1.22.0, 2.0.x-dev].
- Can only install one of: monolog/monolog[1.22.1, 2.0.x-dev].
- Can only install one of: monolog/monolog[1.23.0, 2.0.x-dev].
- Can only install one of: monolog/monolog[1.x-dev, 2.0.x-dev].
- Installation request for monolog/monolog (locked at 2.0.x-dev) -> satisfiable by monolog/monolog[2.0.x-dev].

很多常用的库都依赖monolog,并且都是release版本,现在不知道该怎么办了,求解。。

Redis 命令rpoplpush,brpoplpush第二个参数值始终为2

src/Pools/CoroutineRedisProxy.php中第194行:
$arguments[2] = $this->generateUniqueKey(2);
按照generateUniqueKey函数定义,第二个参数始终为2,导致以上命令无法正常使用。
1507708689.923708 [0 127.0.0.1:37236] "RPOPLPUSH" "sourceMQS" "2"

controller yield Exception问题,严重!

class C extends Controller {
 public function actionIndex(){
          $user = $this->getObject(User::class);//User class
           $result = yield $user->login();//这里有throw new LoginFailException();的情况。
           $this->outputJson($result);
 }
public function onExceptionHandle(\Throwable $e)
    {
        if($e instanceof LoginFailException){//这里没有catch到上面的异常。
            $this->outputJson(['message'=>$e->getMessage()], 401);
            return;
        }
      parent::onExceptionHandle($e);
 }
}

当login throw LoginFailException会报如下错:
[2017-10-12 19:46:12 *29162.0] ERROR zm_deactivate_swoole (ERROR 503): Fatal error: Uncaught App\Exceptions\LoginFailException: 账号或密码错误:0,剩除次数:0 in /opt/rondaful-b2c/msf-server/app/Service/User.php:36
Stack trace:
#0 [internal function]: App\Service\User->login('admin1', 'admin', 10)
#1 /opt/rondaful-b2c/msf-server/vendor/pinguo/php-msf/src/Coroutine/Task.php(159): Generator->send(NULL)
#2 /opt/rondaful-b2c/msf-server/vendor/pinguo/php-msf/src/Coroutine/Scheduler.php(99): PG\MSF\Coroutine\Task->run()
#3 /opt/rondaful
[2017-10-12 19:46:12 $29159.0] WARNING swManager_check_exit_status: worker#0 abnormal exit, status=255, signal=0
[2017-10-12 19:46:12] 服务器进程异常退出 WORKER Error {"worker_id":0,"worker_pid":29162,"exit_code":255,"message":null}

关于对象的生命周期问题?

class Test extens Controller{
     private $server;
     public function __construct($controllerName, $methodName){
            parent::__construct($controllerName, $methodName);
            $this->server = $this->server ?? $this->getObject(Server::class);
     }
}
class Server extens Core{
    private $mode;
    public function __construct(){
          $this->model = $this->getObject(MyModel::class);
    }
   public function destroy()
    {
        parent::destroy(); // TODO: Change the autogenerated stub
        $this->model = null;
        echo "destory\n";
    }
    public function __destruct()
    {
        echo "__destruct\n";
    }
}

多worker时=4,
前4次请求会Test 调用:?? $this->getObject(Server::class);//因为每个worker都会初始一次server
这4次请求是很正常的。
第5次请求时,?? $this->getObject(Server::class);不会调用了。因为private $server被前4次设值了。

导至Server的__construct不会调用到。
那么为什么前4次明明private $server是非public的,而它的Server实例下的destroy会被调用到。
而且__destruct析构函数也调用了。

第五次没有?? $this->getObject(Server::class);,那$server中的$model就不会this->getObject(MyModel::class);//那这时的$model一直是null了。。。

Demo 项目不生成日志文件、协程无限重启

按照 运行代码 一步步来的。

supervisor> status

除了 msf:php-msf-demo RUNNING 之外,还有 nginx RUNNING,为什么还要启动 Nginx?好奇。


Demo示例项目配置了HTTP索引页,直接浏览器访问http://127.0.0.1:8000

此处展示提示:Api not found controller(Index),但 /welcome 是正常的


查看服务日志 $>cat ~/data/msf-php-msf-demo/server.log

此处 Shell 提示:open(/home/worker/data/www/runtime/demo/server.log) failed. Error: No such file or directory[2]


协程 - 示例代码:http://127.0.0.1:8000/CoroutineTest/CoroutineMode

此页面无限重启,加载的一些 .js 文件都是 404。

layout加载完文件不继续执行代码的问题

views目录结构如下:
public
----Header.php
----- Footer.php
index
---- Index.php

Header.php具体内容如下:

<title>KoolTubeeew666666666 this is a test</title>

Index.php具体内容如下:

layout('Public/Header') ?>
Welcome to KOOLTUBE!

重启server.php后,在浏览器上显示结果如下:

<title>KoolTubeeew666666666 this is a test</title> 

Index.php中layout下面的内容没执行。

mysql

[2017-11-17 11:33:05] 服务器发生严重错误 WORKER Error swoole_mysql::query(): mysql client is waiting response, cannot send new sql query

$mysql = $this->getMysqlPool('master');
$id = yield $mysql->goBegin();
$a = $mysql->insert('a')->set(['a'=>'a'])->go($id);
$b = $mysql->insert('a')->set(['a'=>'a'])->go($id);
$c = yield $a;
$d = yield $b;
yield $mysql->goCommit($id);

sendfile一个大文件,会不会导致这个request请求被一直占用着?

protected function outputFile($filename, $headers = [])
    {
        $output = $this->getContext()->getOutput();
        foreach ($headers as $header=>$value){
            $output->setHeader($header, $value);
        }
        $output->response->sendfile($filename);
        $output->__isEnd = true;
    }

$filename有30m,要传1分钟,这一分钟里,会一直占用这条请求资源吗?
如果worker只有4(4个请求资源),那不就没法正常走其它业务了吗?疑问~~

同步任务返回值错乱 BUG

情景:两个同步任务,Demo1阻塞1秒,Demo2阻塞2秒。

<?php
/**
 * Demo1
 */
namespace App\Tasks;

use \PG\MSF\Tasks\Task;

class Demo1 extends Task
{
    public function test()
    {
        sleep(1);
        return 'Demo1 test';
    }
}
<?php
/**
 * DEMO2
 */
namespace App\Tasks;

use \PG\MSF\Tasks\Task;

class Demo2 extends Task
{
    public function test()
    {
        sleep(2);
        return 'Demo2 test';
    }
}
<?php
/**
 * 控制器调用
 */

namespace App\Controllers;

use PG\MSF\Controllers\Controller;

class Welcome extends Controller
{
    public function actionDemo1()
    {
        // Demo1任务使用break
        $this->getObject(\App\Tasks\Demo1::class)->test()->break();
        $this->output('demo1 run');
    }

    public function actionDemo2()
    {
        // Demo2任务使用yield
        $result = yield $this->getObject(\App\Tasks\Demo2::class)->test();
        $this->output('demo2 run,result:'.$result);
    }
}
<?php
// 测试
$a = file_get_contents("http://192.168.53.10/welcome/demo1");
$b = file_get_contents("http://192.168.53.10/welcome/demo2");
echo "demo1:".$a."<br >"."demo2:".$b;

结果:
demo1:demo1 run
demo2:demo2 run,result:Demo1 test

demo2任务返回值为Demo1的返回值。

worker Reload时增加自定义回调机制

swoole_event_add($this->inotifyFd, function ($inotifyFd) use (&$monitorFiles) {
            $events = inotify_read($inotifyFd);
            $flag = true;
            foreach ($events as $ev) {
                if (pathinfo($ev['name'], PATHINFO_EXTENSION) != 'php') {
                    //创建目录添加监听
                    if ($ev['mask'] == 1073742080) {
                        $path = $monitorFiles[$ev['wd']] .'/'. $ev['name'];

                        $wd = inotify_add_watch($inotifyFd, $path, IN_MODIFY | IN_CREATE | IN_IGNORED | IN_DELETE);
                        $monitorFiles[$wd] = $path;
                    }
                    $flag = false;
                    continue;
                }
                writeln('RELOAD ' . $monitorFiles[$ev['wd']] .'/'. $ev['name'] . ' update');
            }
            if ($flag == true) {
                $this->MSFServer->server->reload();
            }
        }, null, SWOOLE_EVENT_READ);

改为

swoole_event_add($this->inotifyFd, function ($inotifyFd) use (&$monitorFiles) {
            $events = inotify_read($inotifyFd);
            $flag = true;
            $changes = [];//record change
            foreach ($events as $ev) {
                if (pathinfo($ev['name'], PATHINFO_EXTENSION) != 'php') {
                    //创建目录添加监听
                    if ($ev['mask'] == 1073742080) {
                        $path = $monitorFiles[$ev['wd']] .'/'. $ev['name'];

                        $wd = inotify_add_watch($inotifyFd, $path, IN_MODIFY | IN_CREATE | IN_IGNORED | IN_DELETE);
                        $monitorFiles[$wd] = $path;
                    }
                    $flag = false;
                    continue;
                }
                writeln('RELOAD ' . $monitorFiles[$ev['wd']] .'/'. $ev['name'] . ' update');
                $changes[$monitorFiles[$ev['wd']] .'/'. $ev['name']] = 'update';
            }
            if ($flag == true) {
                $callback = $this->config->get('reload.callback');//reload callback
                if($callback && is_callable($callback)){
                    $callback($changes);//call back
                }
                $this->MSFServer->server->reload();
            }
        }, null, SWOOLE_EVENT_READ);

没有中间件

是否有计划在框架中集成基于PSR-15规范的中间件?目前有没有替代方案实现类似中间件的功能?

要实现在启动时自动加载数据表结构。

onWorkerStart
        $poolName = 'master';
        $instnce = getInstance();
        $activePoolName = $poolName;
        $poolName       = MysqlAsynPool::ASYN_NAME . $poolName;
        $pool = $instnce->getAsynPool($poolName);
        if (!$pool) {
            $pool = new MysqlAsynPool($instnce->config, $activePoolName);
            getInstance()->addAsynPool($poolName, $pool, true);
        }
        $tables = yield $pool->go(null, 'show create table config_channel');
        print_r($tables);

没有打印print_r($tables);

yield 使用临时表时出现问题

当我使用 yield 创建临时表时,返回结果应该创建成功并且有记录的,但是查询临时表时报错,提示 临时表不存在!

以下示例代码:

    {
        // 删除临时表
        $db =  yield $this->getMysqlPool('master');
        $tableName = 'tmp_user_info';

        $dropSql = 'DROP TABLE IF EXISTS ' . $tableName .';';

        yield $db->go(null,$dropSql);

        $querySql = "SELECT * FROM user_info WHERE create_at = '1514691308'";

        $createTmpTblSql = "CREATE TEMPORARY TABLE  {$tableName}  ({$querySql}) ;";

        $res1 = yield $db->go(null,$createTmpTblSql);

        dump($res1);
        /* 输出结果
         [
            'client_id' => 0
            'result' => true
            'affected_rows' => 8
            'insert_id' => 0
         ]
         */

        $res2 = yield $db->select('*')->from($tableName)->limit(1)->go();
        dump($res2);
        /*
         * [mysql]:Table 'demo_user.tmp_user_info' doesn't exist[sql]:SELECT * FROM tmp_user_info LIMIT 1
         */
        return $res2;
    }`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.