swoole task MySQL连接池

参考 https://blog.csdn.net/ldy3243942/article/details/40596547

上一章中我简单讲解了如何开启和使用Task功能。这一节,我将提供一个Task的高级用法。

在PHP中,访问MySQL数据库往往是性能提升的瓶颈。而MySQL连接池我想大家都不陌生,这是一个很好的提升数据库访问性能的方式。传统的MySQL连接池,是预先申请一定数量的连接,每一个新的请求都会占用其中一个连接,请求结束后再将连接放回池中,如果所有连接都被占用,新来的连接则会进入等待状态。
知道了MySQL连接池的实现原理,那我们来看如何使用Swoole实现一个连接池。
首先,Swoole允许开启一定量的Task Worker进程,我们可以让每个进程都拥有一个MySQL连接,并保持这个连接,这样,我们就创建了一个连接池。
其次,设置swoole的dispatch_mode为抢占模式(主进程会根据Worker的忙闲状态选择投递,只会投递给处于闲置状态的Worker)。这样,每个task都会被投递给闲置的Task Worker。这样,我们保证了每个新的task都会被闲置的Task Worker处理,如果全部Task Worker都被占用,则会进入等待队列。

下面直接上关键代码:

public function onWorkerStart( $serv , $worker_id) {
    echo "onWorkerStart\n";
    // 判定是否为Task Worker进程
    if( $worker_id >= $serv->setting['worker_num'] ) {
        $this->pdo = new PDO(
            "mysql:host=localhost;port=3306;dbname=Test", 
            "root", 
            "123456", 
            array(
                PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8';",
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_PERSISTENT => true
            )
        );
    }
}

首先,在每个Task Worker进程中,创建一个MySQL连接。这里我选用了PDO扩展。

public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
    $sql = array(
        'sql'=>'select * from Test where pid > ?',
        'param' => array(
            0
        ),
        'fd' => $fd
    );
    $serv->task( json_encode($sql) );
}

其次,在需要的时候,通过task函数投递一个任务(也就是发起一次SQL请求)

public function onTask($serv,$task_id,$from_id, $data) {
    $sql = json_decode( $data , true );

    $statement = $this->pdo->prepare($sql['sql']);
    $statement->execute($sql['param']);     

    $result = $statement->fetchAll(PDO::FETCH_ASSOC);
    $serv->send( $sql['fd'],json_encode($result));
    return true;
}

最后,在onTask回调中,根据请求过来的SQL语句以及相应的参数,发起一次MySQL请求,并将获取到的结果通过send发送给客户端(或者通过return返回给Worker进程)。而且,这样的一次MySQL请求还不会阻塞Worker进程,Worker进程可以继续处理其他的逻辑。

可以看到,简单十几行代码,就实现了一个高效的异步MySQL连接池。
通过测试,单个客户端一共发起1W次select请求,共耗时9s;
1W次insert请求,共耗时21s。

(客户端会在每次收到前一个请求的结果后才会发起下一次请求,而不是并发)。


上面是原文 下面是代码

<?php
class MySQLPool
{
        public function __construct() {
                $this->serv = new swoole_server("0.0.0.0", 9501);
        $this->serv->set(array(
            'worker_num' => 4,
            'daemonize' => false,
            'max_request' => 10000,
            'dispatch_mode' => 3,//这个模式下无法收到onclose这样的事件
            'debug_mode'=> 1 ,
            'task_worker_num' => 4
        ));
/*
dispatch_mode
数据包分发策略。可以选择3种类型,默认为2

1,轮循模式,收到会轮循分配给每一个worker进程
2,固定模式,根据连接的文件描述符分配worker。这样可以保证同一个连接发来的数据只会被同一个worker处理
3,抢占模式,主进程会根据Worker的忙闲状态选择投递,只会投递给处于闲置状态的Worker
使用建议
无状态Server可以使用1或3,同步阻塞Server使用3,异步非阻塞Server使用1
有状态使用2、4、5
dispatch_mode 4,5两种模式,在1.7.8以上版本可用
非请求响应式的服务器程序,请不要使用模式1或3

UDP协议
dispatch_mode=1/3时随机分配到不同的worker进程
BASE模式
*/

        $this->serv->on('WorkerStart', array($this, 'onWorkerStart'));
        $this->serv->on('Connect', array($this, 'onConnect'));
        $this->serv->on('Receive', array($this, 'onReceive'));
        $this->serv->on('Close', array($this, 'onClose'));
                // bind callback
        $this->serv->on('Task', array($this, 'onTask'));
        $this->serv->on('Finish', array($this, 'onFinish'));
        $this->serv->start();
        }
        public function onWorkerStart( $serv , $worker_id) {
        echo "onWorkerStart\n";
        // 判定是否为Task Worker进程
        if( $worker_id >= $serv->setting['worker_num'] ) {
                $this->pdo = new PDO(
                        "mysql:host=localhost;port=3306;dbname=testxcx",
                        "root",
                        "111111",
                        array(
                        PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'UTF8';",
                        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                        PDO::ATTR_PERSISTENT => true
                )
            );
        }
    }
    public function onConnect( $serv, $fd, $from_id ) {
        echo "Client {$fd} connect\n";
    }
    public function onReceive( swoole_server $serv, $fd, $from_id, $data ) {
        $sql = array(
                'sql'=>'insert into test values(?,?)',
                'param' => array(
                        0 ,
                        "name"
                ),
                'fd' => $fd
        );
        $serv->task( json_encode($sql) );
    }
    public function onClose( $serv, $fd, $from_id ) {
        echo "a\n";
        echo "Client {$fd} close connection\n";
    }
        public function onTask($serv,$task_id,$from_id, $data) {
        try{
            $sql = json_decode( $data , true );

            //print_r($sql);
/*
Array
(
    [sql] => insert into test values(?,?)
    [param] => Array
        (
            [0] => 0
            [1] => name
        )

    [fd] => 1
)

*/
            $statement = $this->pdo->prepare($sql['sql']);
           // $statement->execute($sql['param'][0],$sql['param'][1]);
            $statement->execute($sql['param']);
            $serv->send( $sql['fd'],"Insert");
            return true;
        } catch( PDOException $e ) {
            var_dump( $e );
            return false;
        }
    }
    public function onFinish($serv,$task_id, $data) {
    }
}
new MySQLPool();


上面是server 下面是client

<?php
class Client
{
        private $client;
  private $i = 0;
  private $time;

        public function __construct() {
    $this->client = new swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_ASYNC);
    $this->client->on('Connect', array($this, 'onConnect'));
    $this->client->on('Receive', array($this, 'onReceive'));
    $this->client->on('Close', array($this, 'onClose'));
    $this->client->on('Error', array($this, 'onError'));
        }

        public function connect() {
                $fp = $this->client->connect("127.0.0.1", 9501 , 1);
                if( !$fp ) {
                        echo "Error: {$fp->errMsg}[{$fp->errCode}]\n";
                        return;
                }
        }
        public function onReceive( $cli, $data ) {
    $this->i ++;
    if( $this->i >= 10000 ) {
      echo "Use Time: " . ( time() - $this->time);
      exit(0);
    }
    else {
       $cli->send("Get");
    }
  }
  public function onConnect( $cli) {
    $cli->send("Get");
    $this->time = time();
  }
  public function onClose( $cli) {
      echo "Client close connection\n";
  }
  public function onError() {
  }
  public function send($data) {
        $this->client->send( $data );
  }
  public function isConnected() {
        return $this->client->isConnected();
  }
}
$cli = new Client();
$cli->connect();


上面代码略有修改 运行截图如下

server


client


数据库



猜你喜欢

转载自blog.csdn.net/ljwy1234/article/details/80107319