Swoole-Demo(2) Swoole对象池原理

1,179 阅读2分钟
原文链接: zhuanlan.zhihu.com

在继续上一章讲完了Tcp服务端对讲之后,本章介绍一个使用swoole_http_server的对象池demo.

优点如下:

  • 对象常驻内存,不必每次new去实例化一个对象.
  • 定时清除对象池用不到的对象。
  • 对小白友好的表达面向对象式Swoole编程原理.

开始:

根据 swoole wiki介绍, swoole_http_server是继承自 swoole_server .

简单服务器构造如下:

class Server {
    protected $server ;
    protected $config ;

    public function __construct($config)
    {
        $this->config = $config ;
        $this->server = new swoole_http_server($config['host'], $config['port']);
        $this->setConfig();
    }

    protected function setConfig()
    {
        $this->server->set($this->config['server']);
    }

    public function run()
    {
        $this->server->on('request',[$this, 'onRequest']);
    }

    public function onRequest($request ,$response)
    {
        // todo
    }
}

$config = ['server' => ['worker_num' => 4 , "task_worker_num" => "20" , "dispatch_mode" => 3 ] , 'host' => '0.0.0.0' , 'port' => 9501];
$server = new Server($config);
$server->run();

onRequest 方法就是我们要处理请求的地方了.

  • 传统方法
在onRequest里边根据请求的request_uri计算出路由,根据路由实例化控制器. 
    public function onRequest($request ,$response)
    {
        $router = new Router($request);
        $controller = $router->getController();// 控制器
        $action = $router->getAction(); //方法
        $resp = $controller->{$action}();
        $response->end($resp);
    }

可以看出这种做法对象复用率不好。

  • 对象池的做法:

对象池原理:

namespace Factory;

class ControllerFactory
{
    /**
     * @var \Factory\ControllerFactory
     */
    public static $instance ;

    private $pool ;

    public function __construct( )
    {
        self::$instance = $this ;
    }

    public static function getInstance()
    {
        if(is_null(self::$instance))
        {
            new ControllerFactory();
        }
        return self::$instance;
    }

    /**
     * 从对象池获取一个对象,
     * @param $class
     * @return \Controller\Controller
     */
    public function getController($class)
    {
        if (isset($this->pool[$class]))
        {
            if(count($this->pool[$class]) > 0)
            {
                $controller = $this->pool[$class]->shift();
                $controller->init();
                $controller->poolName = $class ;
                return $controller ;
            }
        }
        else{
            $this->pool [$class] = new \SplQueue();
        }
        if (!class_exists($class)){
            throw new \RuntimeException("class not found :: {$class}");
        }
        $object = new $class;
        return $object ;
    }

    /**
     * 返回对象给对象池
     * @param $controller
     */
    public function giveBack($controller)
    {
        if ($controller->destory())
        {
            $this->pool[$controller->poolName]->push($controller);
        }
    }


    /**
     * 用于打印输出对象池的对象以及个数
     */
    public function count()
    {
        if(is_null($this->pool))
        {
            return ;
        }
        echo "========对象池计算开始=======" , "\n";
        echo "对象类型总数:" . count($this->pool) , "\n";
        foreach ($this->pool as $key => $object) {
            echo "对象:{$key} , " . "个数:" . count($object) . "\n";
        }
        echo "========对象池计算结束=======" , "\n";
    }

}

上下文对象是对请求以及响应的封装

use Factory\ControllerFactory;

class Context
{
    private $request ;

    private $response ;

    private $hasOver ;

    private $controller ;

    public function __construct($request , $response,$controller)
    {
        $this->request = $request ;
        $this->response = $response ;
        $this->hasOver = false ;
        $this->controller = $controller ;
    }

    public function send($string)
    {
        if($this->over())
        {
            return ;
        }
        $this->setResponseStatus(200);
        $this->hasOver = true ;
        $this->response->end($string);
        ControllerFactory::getInstance()->giveBack($this->controller);
    }

    public function setResponseStatus(int $status)
    {
        if($this->over())
        {
            return ;
        }
        $this->response->status($status);
    }

    public function setResponseHeader($key,$value)
    {
        if($this->over())
        {
            return ;
        }
        $this->response->header($key , $value);
    }

    public function over()
    {
        return $this->hasOver ;
    }

}

请求过程:

function onRequest($request, $response)
    {
        // 解析路由 .
        $router = trim($request->server ['request_uri'] , '/')  ;
        try{
            if($router == 'favicon.ico')
            {
                $response->end('');
                return ;
            }
            if($router == '' )
            {
                // todo 首页地址 .
                $response->end('index ...');
                return ;
            }
            $routerArr = explode('/' ,$router);
            if(count($routerArr) == 1)
            {
                $controller = ucfirst($routerArr [0]) ;
                $method = "index";
            }
            else{
                $controller = ucfirst($routerArr [0]);
                $method = $routerArr [1];
            }
            // todo 大于 2段 / 的地址
            // 组装controller & router .
            $controllerNamespace = "Controller\\{$controller}Controller";
            $controllerObject = ControllerFactory::getInstance()->getController($controllerNamespace);
            $controllerObject->setContext(new Context($request,$response,$controllerObject));
            if(!method_exists($controllerObject , $method))
            {
                throw new \RuntimeException("method not found :: {$controllerNamespace}::$method()");
            }
            $controllerObject->{$method}();
        }
        catch (\Exception $exception) {
            $response->status(500);
            $response->end('server error ::' . $exception->getMessage());
        }
        return ;
    }

如有错误,敬请纠正!

本代码已托管到github: clearcodecn/swoole-demo

另外:clearcode.cn是本人正在建设中的一个社区,旨在讲解服务器端技术与原理,提倡开源精神,授人以鱼不如授人以渔,由于时间不足,导致进展缓慢,希望有兴趣的朋友一起加入.

QQ群: 139348611

转载请申明来源!

路人张:Swoole-Demo(1) TCP服务端简单实现zhuanlan.zhihu.com图标