乐趣区

php框架

php 框架的功能通用的路由,autoload。服务端 mysql 封装,日志组件。前端的页面渲染(smarty 封装个)。在工作时听说别的部门换框架性能提升,所以调研了下常见的框架,包含 ci,laravel,yii,yaf,会介绍下功能,另外给出号称最快框架 yaf 和常用 yii 和裸写框架的性能差。另外想实现 rpc 并发,http 一般用过 multi_curl 可以,公司用的 thrift 没有实现并发,所以研究了下 php 协程,curl_multi,swoole 异步,rpc 中并发实现,corotine 等。

常用 php 框架提供功能

  • ci http://codeigniter.org.cn/use…
    Route.
    autoload. 载入文件正常 controller 中 load->helper(xx)。直接用 xx
    log. db. hook. 公共函数。
    代码逻辑分层。
    ui 抽象。模板。
  • laravel https://laravel-china.org/doc…
    路由
    中间件(前置后置)
    配置区分环境,本地和线上密码不放其中
    数据库 创建表,编辑,删除,迁移,回滚,软删除和恢复(标记删除位)。ORM 链式操作
    依赖注入,依赖自动发现。

    IOC 平时的 if new 这种工厂模式,IoC 模式看作工厂模式的升华,以前在工厂模式里写死了的对象,IoC 模式    改为配置 XML 文件,这就把工厂和要生成的对象两者隔离
    类(DatabaseQueue,queue,QueueContract),serviceprovider(外部调这个)=>bind(将类绑定到容器)。调用 Queue::xx。依赖注入可以直接调用 $ 类 ->method。通过门面可以类::method【https://www.cnblogs.com/shiwenhu/p/6882340.html】

    事件

    事件映射 protected $listen = [
        'App\Events\OrderShipped' => ['App\Listeners\SendShipmentNotification',],];
    写事件 OrderShipped,写监听 SendShipmentNotification 可以继承队列
    分发事件:public function ship($orderId)
        {order=Order::findOrFail(orderId);
        // 订单的发货逻辑...
        event(new OrderShipped($order));
        }

    队列,redis,db, 广播
    js 监听
    任务调度 只是 cron

  • yii https://www.yiichina.com/doc/…
    功能全面读介于 ci 和 laravel 之间,前端支持功能丰富。组件和行为是它的特色
    行为

    要定义行为,通过继承 yii\base\Behavior。覆盖其中的 events 方法,public function events() { 
        return [ActiveRecord::EVENT_BEFORE_VALIDATE => 'beforeValidate',]; 
    } 
    public function beforeValidate($event) {// 处理器方法逻辑},附加行为:lass User extends ActiveRecord {public function behaviors() { 
        return [
            ['class' => MyBehavior::className(), 
            'prop1' => 'value1', 
            'prop2' => 'value2', 
        ]]}}或者 attach 组件就可以使用行为了。

    直观感受下

  • yaf http://www.laruence.com/manua…
    php 的一个扩展, 因为是扩展,所以可以常驻内存,不用每次加载框架了、实现了基本功能
    路由,
    插件可以在几个 hook 处调用,比如自己开发一个 Log, 在这些 Hook 的地方加入回调(https://github.com/akDevelope…)
    autoload
    配置

yaf,cii, 裸写框架(autoload,路由)代码和性能

性能比较

裸写在 CPU 占用方面和 YAF 相近,略高,在内存方面更节省。在吞吐量方面和 YAF 相近,略好。
CI 框架会比 YAF 在 CPU 方面多耗费 10%。吞吐量要差 75%

  • 吞吐
    裸写优于 yaf,yaf 可以比 CI 提升 75% 左右
    在 php-fpm 固定为 10 个,看处理速度,全部打满 4000qps 压测。10s 预热,30s 压测
    我们的框架最大吞吐量是 2200/s,平均有 970,一共处理了 20 万
    ci 的最大为 1850/s。但是会有 502 和 504 且有 CPU 为 0 的时候,平均只有 400,一共处理 5 万左右,可以看出 ci 在稳定情况下最大的吞吐量可以达到 1600,共处理 4 万左右
    yaf 的最大吞吐量是 2280/s。但平均只有 670,所以一共处理 7 万
  • CPU
    php-fpm 开 2048 个,全部绑定到 cpu1 上,tsung 压直到打满 CPU,10 秒预热 +30 秒压测
    yaf 和我们的框架可以压到 600qps.CI 到 600 直接 CPU 掉底了
    500 时,我们的框架略优于 YAF.YAF 相比于 CI 要节省至少 10 个点的 CPU 使用率
    300 时,我们的框架和 YAF 基本一样,CI 还是要高出 10 个点。

代码

  • yaf

    index.php
    <?php
    define('APPLICATION_PATH', dirname(__FILE__));
    $performance = getrusage();
    $globalPerformStatics['cpu_time_start'] = $performance['ru_utime.tv_sec'] *1000000+ $performance['ru_utime.tv_usec']+$performance['ru_stime.tv_sec'] *1000000+$performance['ru_stime.tv_usec'];
    $application = new Yaf_Application(APPLICATION_PATH . "/conf/application.ini");
    $application->bootstrap()->run();
    $performance = getrusage();
    $globalPerformStatics['cpu_time_end'] = $performance['ru_utime.tv_sec'] *1000000+ $performance['ru_utime.tv_usec']+$performance['ru_stime.tv_sec'] *1000000+$performance['ru_stime.tv_usec'];
    var_dump($globalPerformStatics['cpu_time_end']-$globalPerformStatics['cpu_time_start']);
    ?>
     
    ————————————————————————————————
    Bootstrap.php
     
    <?php
    class Bootstrap extends Yaf_Bootstrap_Abstract {public function _initRoute(Yaf_Dispatcher $dispatcher) {$router = Yaf_Dispatcher::getInstance()->getRouter();
            $route = new Yaf_Route_Rewrite(
                'order/base/passenger/getinfo',
                array('controller' => 'Base_Passenger_Getinfo',)
                );
            $router->addRoute('html1asdfd', $route);
        }
        public function _initView(Yaf_Dispatcher $dispatcher) {Yaf_Dispatcher::getInstance()->autoRender(false);
    }
    }
     
    ————————————————————————————————
    创建 controller/Base/Passenger/Getinfo.php
     
    <?php
    class Base_Passenger_GetInfoController extends Yaf_Controller_Abstract {public function indexAction($name = "Stranger") {
        echo "hello";
        $orderId = $this->getRequest()->getQuery("order_id");
         $model = new SampleModel();
                $orderInfo = $model->selectSample($orderId);
        var_dump($orderInfo);
        return TRUE;
    }
    }
     
    ————————————————————————————————
    创建 Model
     
    <?php
    /**
     * @name SampleModel
     * @desc sample 数据获取类, 可以访问数据库,文件,其它系统等
     * @author */
      class SampleModel {public function __construct() { }
        public function selectSample($id) {
                    $a = $id / 5.314; // 取反正切 0-5.314 的变化区间
                            $b = 1000 / 1.520837931073;
                            return intval((100 / $a) * $b);
                                }
        public function insertSample($arrInfo) {return true;}
      }
          
  • 裸写

    index.php
    <?php
    define('FRAMEPATH', '/home/project/phputil/');
    define('APPPATH', '/home/project/order/');
    $performance = getrusage();echo 1;
    $globalPerformStatics['cpu_time_start'] = $performance['ru_utime.tv_sec'] *1000000+ $performance['ru_utime.tv_usec']+$performance['ru_stime.tv_sec'] *1000000+$performance['ru_stime.tv_usec'];
    $appNameSpace = 'Project\Order';
    $_GET = array_merge($_GET, $_POST);//fix Android 端把 $_GET 改成 $_POST 问题
    require_once(FRAMEPATH . '/autoload/autoloader.php');
    $loader = Project\Autoload\Autoloader::getLoader();
    $loader->addPsr4('Project\Order\\', APPPATH);
    require_once(FRAMEPATH . '/framework.php');
    $performance = getrusage();
    $globalPerformStatics['cpu_time_end'] = $performance['ru_utime.tv_sec'] *1000000+ $performance['ru_utime.tv_usec']+$performance['ru_stime.tv_sec'] *1000000+$performance['ru_stime.tv_usec'];
    var_dump($globalPerformStatics['cpu_time_end']-$globalPerformStatics['cpu_time_start']);
    ?>
     
    ————————————————————————————————
    config/route.php
    <?php
    namespace Project\Order\Config;
    class Route
    {
            public static $routes = array('order/(.+)' => '$1',
                    );
    }
     
    ————————————————————————————————
    helper/order.php
    <?php
    namespace Project\Order\Helper;
    class Order{public static function selectSample($id) {
              $a = $id / 5.314; // 取反正切 0-5.314 的变化区间
              $b = 1000 / 1.520837931073;
              return intval((100 / $a) * $b);
        }
    }
     
    ————————————————————————————————
    phputil
     
    <?php
    $loader->addPsr4('Project\Framework\\', FRAMEPATH . '');
    // 全局变量定义(暂时做法,限制性使用)
    $__uid   = 0;
    $_startTime = '';
    $_redisCount = 0;
    $_mysqlCount = 0;
    $_httpRpcCount = 0;
    $_thriftRpcCount = 0;
    try {$params = array('get' => $_GET, 'post' => $_POST);
        $routerConfigPath = $appNameSpace . '\Config\Route';
        $routerConfig = array();
        $errorPageConfig = false;
        if(class_exists($routerConfigPath)){if(!empty($routerConfigPath::$routes) && is_array($routerConfigPath::$routes)){$routerConfig = $routerConfigPath::$routes;}
            if(!empty($routerConfigPath::$showErrorPage)){$errorPageConfig = $routerConfigPath::$showErrorPage;}
        }
        $_startTime = microtime(true);
        $router = new \Project\Framework\Base\Router($_SERVER['REQUEST_URI'], $routerConfig,$errorPageConfig);
        $router->setRoute();
        $router->run($params);
    } catch (\InvalidArgumentException $ex) {
        $errNo = -1;
        $errMsg = strlen($ex->getMessage()) ? $ex->getMessage() : 'system error';
      var_dump(array('errno' => $errNo, 'errmsg' => $errMsg));
    } catch (\Exception $ex) {$errNo = $ex->getCode();
        $errMsg = $ex->getMessage();
     var_dump(array('errno' => $errNo, 'errmsg' => $errMsg));
    }
  • ci

    config/route.php
    CI 多级目录支持需要自己开发,粗暴的把路由直接打到 welcome.php
    $route['(.+)'] = 'welcome';
     
    ————————————————————————————————
    controller/Welcome.php
     
    <?php
    class Welcome extends CI_Controller {public function __construct()
        {parent::__construct();
            $this->load->model('SampleModel');
        }
        public function index()
        {
    echo "hello";
    $orderId=$this->input->get('order_id');
            $orderInfo = $this->SampleModel->selectSample($orderId);
     
    var_dump($orderInfo);
    return 1;
    }
    }
     
    ————————————————————————————————
    model/SampleModel.php
     
    <?php
    class SampleModel extends CI_Model {public function selectSample($id) {
                    $a = $id / 5.314; // 取反正切 0-5.314 的变化区间
                            $b = 1000 / 1.520837931073;
                            return intval((100 / $a) * $b);
                                }
        public function insertSample($arrInfo) {return true;}
    }
     
    

php 异步 rpc

php 协程

php 为何不用多线程:

pthreads v3 is restricted to operating in CLI only: I have spent many years trying to explain that threads in a web server just don’t make sense, after 1,111 commits to pthreads I have realised that, my advice is going unheeded.
So I’m promoting the advice to hard and fast fact: you can’t use pthreads safely and sensibly anywhere but CLI.
Thanks for listening ;)

  • yield 原理:
    http://note.youdao.com/notesh…
  • 用 yield 实现协程原理:
    http://note.youdao.com/notesh…

curl_mutli

设为非阻塞的 socket,调用 libcurl 的方法。也是 select 后执行,跟我们 php 协程处理方式差不多
curl_multi_init
curl_multi_add_handle
curl_multi_select
select 有结果后 curl_multi_perform // multi_runsingle. 一个一个进行,状态流转,从任何一个状态都可以继续执行
当 select 结束后 curl_multi_info_read
curl_multi_remove_handle,curl_multi_cleanup

swoole 框架异步和 corotine 实现

swoole:https://wiki.swoole.com/wiki/…
协程原理:跳堆栈的原理
异步: 客户端 /mysql/redis/http 等。这些只能在 cli 下调用。不能在 fpm 中, 异步就是设为非阻塞的 socket, 然后设置回调。epoll。一个进程就行了。

自开发并发 rpc

http 协议封装 libcurl. 其他协议 状态机 +epoll

workerman-thrift

需要分开提供发送和读取两个接口。对发送和读取两个步骤实现异步。先发送一批再读取,不是发完了等待接收再下一个。

退出移动版