刘泉皓

没有最强的法术,只有最强的法师。

Workerman源码详解1——最简单的例子

14 Aug 2018 » memory

本篇为一个workerman最简例子。

必备知识:

  1. php的namespace,顺便附上一份PSR规范
  2. php的spl_autoload_register
  3. 附带本博客的autoload相关文章

这里先给出一个 http_test.php 的例子:

<?php
use Workerman\Worker; //引用Workerman\Worker命名空间,需要使用这个类
require_once __DIR__ . '/Workerman/Autoloader.php'; //加载Autoloader,这样只要命名规范,就不需要include之类的命令引入类文件了
$http_worker = new Worker("http://0.0.0.0:2345"); //创建一个Worker监听2345端口,使用http协议通讯
$http_worker->count = 4; //启动4个进程对外提供服务
$http_worker->onMessage = function($connection, $data) //接收到浏览器发送的数据时执行的方法
{
    $connection->send('hello world'); //向浏览器发送hello world
};
Worker::runAll(); //运行worker

再用 php 运行它,一台http服务就运行起来了:

php http_test.php start

这是一个很简单的例子,运行后它就一直监听2345端口,只要有浏览器访问就会返回 hello world

下面是Autoloader.php的代码,为了节省空间,我把注释和空行都删了,详细的你可以参考源码:

<?php
namespace Workerman; //定义当前代码在Workerman命名空间下
class Autoloader //定义Autoloader类,调用它的方法是同Workerman下自动补齐为Workerman\Autoloader,外部调用为\Workerman\Autoloader
{
    protected static $_autoloadRootPath = ''; //受保护的静态参数_autoloadRootPath,自动加在的根目录
    public static function setRootPath($root_path) //公共静态方法,用于设置根目录,Autoloader::setRootPath(xxx)
    {
        self::$_autoloadRootPath = $root_path; //设置当前类的静态属性_autoloadRootPath
    }
    public static function loadByNamespace($name) //定义自动加载方法,$name为代码中使用某类但未引入该类的代码文件时,传进来的带namespace的类名,此时$name假设为'Workerman\Lib\Timer'
    {
        $class_path = str_replace('\\', DIRECTORY_SEPARATOR, $name); //例如linux下,将'\'替换成'/',php中'\'本身有特殊意义,所以需要用'\\'转义成普通'\'。这样$name是Workerman\Lib\Timer,就会替换成Workerman/Lib/Timer,并赋值给$class_path
        if (strpos($name, 'Workerman\\') === 0) { //如果$name是'Workerman\'开头,就进入if内部执行,注:所有Workerman的代码都在这个'Workerman\'命名空间下
            $class_file = __DIR__ . substr($class_path, strlen('Workerman')) . '.php'; //先将$class_path中的'Workerman'去掉,然后用当前文件目录拼接上剩余部分,并加上'.php',此时如果当前文件路径是'/home/liuxu/test/Autoloader.php',那么此时$class_file就是: '/home/liuxu/test' . '/Lib/Timer' . '.php'
        } else { //不是'Workerman\'开头,相当于不是Workerman的代码,也就是自己写的代码
            if (self::$_autoloadRootPath) { //如果设置过了_autoloadRootPath属性就进入if内部
                $class_file = self::$_autoloadRootPath . DIRECTORY_SEPARATOR . $class_path . '.php'; //如果设置了_autoloadRootPath为'/home/liuxu/include',而调用的方法是Liuxu\Test类,那么$class_file就是: '/home/liuxu/include' . '/' . 'Liuxu/Test' . '.php';
            }
            if (empty($class_file) || !is_file($class_file)) { //如果以上判断都不是,或者检查发现上面这个if导致$class_file不是文件就执行下面的内部代码
                $class_file = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . "$class_path.php"; //Autoloader当前目录的上级目录找类文件,还是上面if的假设,此时$class_file就是: '/home/liuxu/workerman' . '/' . '..' . '/' . 'Liuxu/Test.php';
            }
        }
        if (is_file($class_file)) { //经过上面的代码,如果$class_file是文件就进入if
            require_once($class_file); //引入$name的类文件
            if (class_exists($name, false)) { //class_exists($class_name, $autoload = true),如果能够找到该类了,也就是上面require_once成功,就执行if内部的代码
                return true; // 就返回true
            }
        }
        return false; // 以上都失败了,就返回flase,那么php就会报错,会说没有找到该类
    }
}
spl_autoload_register('\Workerman\Autoloader::loadByNamespace'); //将上面\Workerman\Autoloader的loadByNamespace方法注册为自动加载函数,外部调用,所以使用'\Workerman'

好了,以上就是基本例子的代码运行原理了,接下来就是分析Worker的代码了。


知识共享许可协议    鄂ICP备 15002452号-5    鄂公网安备 42088102000048号