学习swoole笔记

swoole是什么

swoole是用来补缺php异步处理缺陷的新技术,支持协程,用户也可以完全抛开原来的fpm模式调用,而直接使用php+swoole开发项目,能更加处理网站的高并发,大数据问题;它是php异步网络通讯引擎;异步多线程;

swoole用来做什么

在线直播,聊天室,游戏行业

swoole如何用

安装php,swoole就是php的一个扩展,可以用在http,websocket等请求。

swoole课程1

swoole课程2-php源码编译安装

  1. configure (shell脚本):主要用于编译安装源代码库和软件,对源代码库进行一些设置(需要gcc,autoconfig);eg:./configure --prefix=/home/usr/local/php (安装路径)
  2. make -j (构建\编译)
  3. make install (安装)

知识点

  • php的启动,是在bin目录下,启动命令 ./bin/php test.php (执行php)
  • 设置当前用户的快捷启动操作:安装目录下 vi ~/.bash_profile export PATH 处,增加 alias php=/home/usr/local/php 然后source ~./bash_profile
  • souce filename 和sh filename的区别: source 不会生成新的子shell,只是可能脚本修改需要重新读取执行了这个shell脚本。而sh 则会产生新的子shell,不改变父级shell的进程和变量
  • 注意php.ini的默认文件需要自己复制php.ini-development,然后修改成php.ini,源码安装没设置安装路径会默认放在lib文件夹下,但是人们习惯放在etc下,这两个目录下寻找
  • php -i | grep php.ini 查找php.ini 生效的目录

swoole课程3-swoole编译安装

  1. 下载: git clone / 下载上传压缩包,直接解压
  2. 需要先用phpize 先生成configure 文件,phpize是用来安装php扩展模块的。在swoole的文件目录下,/home/usr/lcoal/bin/phpize 会自动生成 configure 脚本文件
  3. swoole目录下 ./configure --with-php0-config=/home/usr/local/php/bin/php-config (check操作) ; make &&make install;最后会生成一个so的扩展文件(会自动显示该so扩展文件的路径,一般在lib/php/extensions/ 的目录下);在lib/php.ini 下,增加 extension=swoole;
  4. 查看php的模块 php -m

swoole课程4-网络通讯

tcp基本四步走

  1. 创建server对象
  2. 监听连接进入事件
  3. 监听数据接收事件; 发送客户端, s e r v − > s e n d ( serv->send( serv>send(fd, "Server: ".$data);
  4. 监听关闭事件
  5. 启动服务器
$serv = new Swoole\Server("127.0.0.1", 9501);
$serv->set([
    'worker_num' => 4,    //worker process num
    'max_request'=>10000
]);

//监听连接进入事件
$serv->on('Connect', function ($serv, $fd) {
    echo "Client: Connect.\n";
});

//监听数据接收事件
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
    $serv->send($fd, "Server: ".$data);
});

//监听连接关闭事件
$serv->on('Close', function ($serv, $fd) {
    echo "Client: Close.\n";
});

//启动服务器
$serv->start();

udp基本三步走

  1. 创建server对象
  2. UDP服务器与TCP服务器不同,UDP没有连接的概念。启动Server后,客户端无需Connect,直接可以向Server监听的9502端口发送数据包;里边传输数据用 s e r v e r − > s e n d t o ( server->sendto( server>sendto(address, p o r t , port, port,data).
  3. 启动服务器
$serv = new Swoole\Server("127.0.0.1",9502,SWOOLE_PROCESS,SWOOLE_SOCK_UDP);
$serv->set([
    'worker_num' => 4,    //worker process num
    'max_request'=>10000
]);
//UDP服务器与TCP服务器不同,UDP没有连接的概念。启动Server后,客户端无需Connect,直接可以向Server监听的9502端口发送数据包。对应的事件为onPacket
$serv->on('Packet',function($serv,$data,$clientInfo){
    $serv->sendto($clientInfo['address'], $clientInfo['port'], "Server ".$data);
    var_dump($clientInfo);
    var_dump($data);
});

//启动服务器
$serv->start();

http服务基本三步走

  1. 创建对象
  2. 设置静态资源目录
  3. 接收前端数据,响应返回数据
  4. 开启http服务器
$http = new swoole_http_server("0.0.0.0", 9501);
// 配置 这里设置了该项,浏览器路由加了,index.html ,代码就不会执行西下边的输出
$http->set([
            'enable_static_handler'=>true,
            'document_root'=>'/home/swoole-mooc/demo/data',
]);
$http->on('request',function($request,$response){
    print_r($request->get);
    $response->cookie('login','cook-name',time()+3600);//给当前服务设置一个cookie
    $response->header("Content-Type", "text/html; charset=utf-8");
    $response->end("

Hello Swoole. #".rand(1000, 9999)."

"
); }); $http->start();

上边如果请求找不到对应的html文件,就会走到request中的end方法,找到就不会

websockt

什么是websocket?

websocket协议是基于TCP的一种新的网络协议。它实现了浏览器和服务器全双工通信----允许服务器主动发送信息给客户端。

为什么需要websocket?

http的通信只能由客户端发起,需要不断发送消息需要轮询发送请求

websocket的特点?
  • 建立在TCP协议之上
  • 性能开销小通信搞笑
  • 客户端可以和任意服务器通信
  • 协议标识符ws,wss
  • 持久化网络通信协议
websocket\server
  1. 创建对象
  2. 监听websocket连接打开事件
  3. 监听ws消息事件:(onMessage),必须实现的事件,里边推送数据用 s e r v e r − > p u s h ( server->push( server>push(fd,$data);
  4. 关闭ws消息事件
  5. 开启服务
$server = new Swoole\WebSocket\Server("0.0.0.0", 9502);
//监听websocket连接打开事件
$server->on('open', function (Swoole\WebSocket\Server $server, $request) {
    echo "server: handshake success with fd{$request->fd}\n";
});
//监听ws消息事件
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {
    echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n";
    $server->push($frame->fd, "this is server");
});
//关闭ws消息事件
$server->on('close', function ($ser, $fd) {
    echo "client {$fd} closed\n";
});
//启动websocket服务器
$server->start();

websocket前端代码:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
  </head>
  <body>
    <h1>Websocket的测试111</h1>
    <h1>2222</h1>
    <script type="text/javascript">
      var wsurl = "ws://xx.xx.xx.xx:9502";
      var websocket = new WebSocket(wsurl);
      websocket.onopen=function(evt){
        websocket.send("hello world");
        console.log("链接成功");
      }
      websocket.onmessage=function(evt){
        console.log("后端数据:"+evt.data);
      }
      websocket.onclose=function(evt){
        console.log("close");
      }
    </script>
  </body>
</html>
task任务

使用场景:执行耗时操作,比如发送邮件,广播
实现要求:

  • 需要实现onTask()、onFinish(),并且设置task_worker_num.。
  • 注意:onTask事件仅在task进程中发生,onFinish事件仅在worker进程中发生
    function onTask(swoole_server $serv, int $task_id, int $src_worker_id, mixed $data){};
    function onFinish(swoole_server $serv, int $task_id, string d a t a ) ; 这 里 的 data){};这里的 data);data是onTask返回的值。

swoole课程5-异步非阻塞IO场景

swoole课程6-进程、内存、协程

什么是进程?

进程就是正在运行的程序的一个实例。
进程概念:主进程-》子进程-》管理进程-》work进程
进程使用场景:比如需要耗时读取多个文件里的内容,执行速度慢,这是可以开启多个进程,互不影响读取速度加快。
进程间的通讯,通过管道通讯。

echo date("Ymd H:i:s").PHP_EOL;
$workers=[];
$urls=array("aaa,bbb,ccc,ddd,eee,fff");
for ($i=0; $i <6 ; $i++) {
    // 创建子进程
    $process= new swoole_process(function(swoole_process $work) use($i,$urls){
        //匿名函数调用函数外部变量值的方法,使用use
        $content = curlData($urls[$i]);
        $work->write($content.PHP_EOL);//数据写入管道中
    },true);
    $pid = $process->start();
    $workers[$pid] = $process;
}
foreach ($workers as $process) {
    echo $process->read();//从管道获取数据
}
function curlData($url){
  sleep(1);
  return $url.PHP_EOL;
}
echo date("Ymd H:i:s").PHP_EOL;
内存模块

Lock、Buffer、Table、Mmap、Atomic。。。。。。

swoole_table(内存表)

swoole_table(内存表)是一个基于共享内存的锁实现的超高性能,并发数据结构。

//内存。类似数据库能力
use Swoole\Table;
$table = new Swoole\Table(1024);
//内存增加一列
$table->column('id',Table::TYPE_INT,4);
$table->column('name',Table::TYPE_STRING,16);
$table->column('age',Table::TYPE_INT,3);
$table->create();
//表中增加一条数据 ,hello为key
$table->set("hello",[
    "id"=>1,
    "name"=>"syf",
    "age"=>30
]);
//原子自增信息,只针对 TYPE_INT生效
$table->incr("hello","age",12);
//内存中获取数据
print_r($table->get("hello"));

协程 Coroutine

协程可以理解为纯用户态的线程,通过协作而不是抢占来进行切换。应用层可以使用同步编程方式,底层自动实现异步IO。
优势:

  1. 开发者可以再无感知情况下使用同步编程方式实现异步操作,避免了异步回调带来离散代码逻辑和多重调用的无法维护障碍
  2. 相较于传统的php,开发者不需要使用yield关键词来标识一个协程IO操作,不在需要对每一级调用都修改yield,提高开发效率。
$http = new swoole_http_server("0.0.0.0", 9501);
$http->on("request",function($request,$response){
    $redis = new Swoole\Coroutine\Redis();
    $redis->connect("127.0.0.1",6379);
    $val = $redis->get("hello");//获得redis中key=hello值,输出在页面中
    // $response->header("Content-type","text/html");
    $response->header("Content-type","text/plain");
    $response->end($val);
});
$http->start();

swoole课程7-赛事直播

开发思想学习

  1. 配置项多处使用时建立配置文件,eg:redis.php
return [
  'host'=>'127.0.0.1',
  'port'=>'6379',
  'out_time'=>120,
  'timeout'=>5,
  'test_code'=>6789,
  'live_game_key'=>'clientKey',
];

  1. 封装对象,美化代码,是面向过程的开发转为面向对象
class Http
{
    CONST HOST = "0.0.0.0";
    CONST PORT = 9501;
    public $http = null;
    public function __construct()
    {
        $this->http = new swoole_http_server(self::HOST,self::PORT);
        $this->http->set([
                    'enable_static_handler'=>true,
                    'document_root'=>'/home/swoole-mooc/thinkphp51/public/static',
                    'work_num'=>5,
                    'task_worker_num'=>2,
        ]);
        $this->http->on('workerstart',[$this,'onWorkerStart']);
        $this->http->on('request',[$this,'onRequest']);
        $this->http->on('task',[$this,'onTask']);
        $this->http->on('finish',[$this,'onFinish']);
        $this->http->on('close',[$this,'onClose']);
        $this->http->start();
    }
    public function onWorkerStart(){
        define('APP_PATH', __DIR__ . '/../application/');
        // 加载框架引导文件
        require __DIR__ . '/../thinkphp/start.php';
    }
    public function onRequest($request,$response){
        $_GET = [];
        $_POST = [];
        // $_SERVER = [];
        if(isset($request->get)){
            foreach ($request->get as $k=> $v) {
                $_GET[$k]=$v;
            }
        }
        if(isset($request->post)){
            foreach ($request->post as $k => $v) {
                $_POST[$k]=$v;
            }
        }
        if(isset($request->header)){
            foreach ($request->header as $k => $v) {
                $_SERVER[strtoupper($k)]=$v;
            }
        }
        if(isset($request->server)){
            foreach ($request->server as $k => $v) {
                $_SERVER[strtoupper($k)]=$v;
            }
        }
        $_POST['http_server'] = $this->http;
        ob_start();
        try{
            // 执行应用并响应
            think\Facade::make('app', [APP_PATH])
                ->run()
                ->send();
        } catch (\Exception $e){
            echo "有错误".$e->getMessage().PHP_EOL;
        }
        $res = ob_get_contents();
        ob_end_clean();
        // var_dump($_SERVER);
        $response->header("Content-Type", "text/html; charset=utf-8");
        $response->end($res);
    }
    public function onTask($http,$task_id,$workId,$data){
        //分发任务,会进入 Task.php 来找到相应方法,来进行任务。
        $obj = new app\common\lib\task\Task();
        $method = $data['method'];
        $flag = $obj->$method($data['data']);
        return  $flag;


    }
    public function onFinish($http,$task_id,$data){
        echo "task is finish".PHP_EOL;
    }
    public function onClose($http,$fd){
        echo "client is shutdown".$fd.PHP_EOL;
    }


}
new Http();
  1. 一个对象需要多次连接,消耗资源时,可以使用单例模式,仅仅实例化一次,魔术方法_call()使用,简化代码
/**
 * 设置同步redis,这里的重写,是为了不让redis每次都重新连接,造成内存资源的浪费
 */
class Predis
{
  public  $redis = '';
  private static $_instance = null;
  //采用单例模式设计,
  public static function getInstance()
  {
    if(empty(self::$_instance)){
      self::$_instance = new self();
    }
    return self::$_instance;
  }
/**/
  public function __construct()
  {
    $this->redis = new \Redis();
    $result = $this->redis->connect(config('redis.host'),
        config('redis.port'),config('redis.timeout'));
    if($result===false){
      throw new \Exception("redis connect error");
    }
  }
/*
  redis ->set()
 */
  public function set($key,$value,$time=0)
  {
    if (!$key) {
      return '';
    }
    if(is_array($value)){
      $value = json_encode($value);
    }
    if(!$time){
      $this->redis->set($key,$value);
    }
    return $this->redis->setex($key,$time,$value);
  }
/*
redis->get
 */
  public function get($key)
  {
    if(!$key){
      return '';
    }
    return $this->redis->get($key);
  }
/*
redis->add
 */
  // public function sAdd($key,$value)
  // {
  //   return $this->redis->sadd($key,$value);
  // }
/*
redis->rem
 */
  // public function sRem($key,$value)
  // {
  //   return $this->redis->srem($key,$value);
  // }
/*
redis->sMember
 */
  public function sMembers($key)
  {
    return $this->redis->sMembers($key);
  }
  public function __call($name,$arguments)
  {
    if(count($arguments)==2){
        return $this->redis->$name($arguments[0],$arguments[1]);
    } else {
        return "不等于2";
    }
  }
  1. swoole中task任务和work进程的使用,task任务的统一封装执行
class Task
{

    /**
 	 * 异步发送验证码
 	 *
 	 * @param type
 	 * @return void
	 */

    public function sms($value,$serv)
    {

        try {
            $respond = SMS::sendSms($value['phone'],$value['code']);
        } catch (\Exception $e) {
            echo $e->getMessage();
        }
        if($respond){
         //这里不能用异步redis来存储,
          // $redis = new \Swoole\Coroutine\Redis();
          // $redis->connect(config('redis.host'),config('redis.port'));
          // $val = $redis->set(Redis::setRedisKey($phone),config('redis.test_code'),config('redis.out_time'));

          //方法一:同步redis
          // $val = \Cache::set('key1',123456);
          // $getval = \Cache::get('key1');

         // 方法二:同步redis
          $val = Predis::getInstance()->set($value['phone'],6789);
          // $getval = Predis::getInstance()->get($phone);
        }else{
          return false;
        }
        return true;

    }
    public function pushLive($data,$serv)
    {
        $clients = Predis::getInstance()->sMembers(config('redis.live_game_key'));
        foreach ($clients as $fd) {
            $serv->push($fd,json_encode($data));
        }
    }
}

你可能感兴趣的:(php)