• workerman知识清单


    Worker类:静态Worker统一管理实例worker

    $this->workerId                    = spl_object_hash($this);
    static::$_workers[$this->workerId] = $this;
    static::$_pidMap[$this->workerId]  = array();

    进程端口复用原理

    参考:https://www.jianshu.com/p/97cc8c52d47a

    惊群效应

    为了提升性能,一般的服务端程序在运行时都有多个进程(俗称 Worker)监听同一个 Socket,在没有客户端连接到来的时候,这些Worker是处于挂起状态的,不消耗CPU资源。

    只有一个 Worker 可以获得处理这个连接的机会,其他Worker在竞争失败后继续回到挂起状态。唤醒 Worker 的过程是要消耗CPU资源的,Worker 数量越多,消耗的 CPU 资源就越多,造成了资源的浪费。这就是常说的 惊群效应

    代码细节

    #Worker 类是 Workerman 里最主要的类,其中有个 listen() 函数
    protected
    function listen() { ... if (!$this->_mainSocket) { ... $this->_mainSocket = stream_socket_server(...); ... } ... }
    #当 reusePort 为 false 时,主进程在创建 Worker 之前就调用了 listen() 函数
    protected function initWorkers() { .... if (!$worker->reusePort) { $worker->listen(); } .... }
    #当 reusePort 为 true 时,情况就不同了。主进程在创建 Worker 前不会调用 listen(),而是在创建完 Worker 后由每个 Worker 自行发起 listen() 调用
    protected static function forkOneWorkerForLinux($worker) { ... $pid = pcntl_fork(); if ($pid === 0) { if ($worker->reusePort) { $worker->listen(); } ... } ... }
    #想要内核开启 reuseport 功能,需要手动设置 Socket 的 context
    if ($this->reusePort) { $context = stream_context_create(); stream_context_set_option($context, 'socket', 'so_reuseport', 1); }

    定时器

    1,定时器是用来实现定时任务的,分为持续间隔计时(类似setTimeInterval())和一次性计时(类似setTimeOut())

    2,定时器实现有两种方式:使用事件和任务数组

    //初始化
    Timer::init(static::$globalEvent); if ($event) { self::$_event = $event; return; } if (function_exists('pcntl_signal')) { pcntl_signal(SIGALRM, array('WorkermanLibTimer', 'signalHandle'), false); }
    //添加定时任务 Timer
    ::add(static::KILL_WORKER_TIMER_TIME, 'posix_kill', array($one_worker_pid, SIGKILL), false); if (self::$_event) { return self::$_event->add($time_interval, $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args); } if (!is_callable($func)) { Worker::safeEcho(new Exception("not callable")); return false; } if (empty(self::$_tasks)) { pcntl_alarm(1); } $run_time = time() + $time_interval; if (!isset(self::$_tasks[$run_time])) { self::$_tasks[$run_time] = array(); } self::$_timerId = self::$_timerId == PHP_INT_MAX ? 1 : ++self::$_timerId; self::$_status[self::$_timerId] = true; self::$_tasks[$run_time][self::$_timerId] = array($func, (array)$args, $persistent, $time_interval);
    //执行任务 Timer
    ::tick() if (self::$_event) { return self::$_event->add($time_interval, $persistent ? EventInterface::EV_TIMER : EventInterface::EV_TIMER_ONCE, $func, $args); } $time_now = time(); foreach (self::$_tasks as $run_time => $task_data) { if ($time_now >= $run_time) { foreach ($task_data as $index => $one_task) { $task_func = $one_task[0]; $task_args = $one_task[1]; $persistent = $one_task[2]; $time_interval = $one_task[3]; try { call_user_func_array($task_func, $task_args); } catch (Exception $e) { Worker::safeEcho($e); } if($persistent && !empty(self::$_status[$index])) { $new_run_time = time() + $time_interval; if(!isset(self::$_tasks[$new_run_time])) self::$_tasks[$new_run_time] = array(); self::$_tasks[$new_run_time][$index] = array($task_func, (array)$task_args, $persistent, $time_interval); } } unset(self::$_tasks[$run_time]); } }

    事件

    有3种事件:

    1,信号事件:

    $this->_eventSignal

    2,socket io事件

    $this->_allEvents

    3,定时器事件

    $this->_eventTimer
    //绑定事件
    if ($this->transport !== 'udp') {
          static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptConnection'));
    } else {
          static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptUdpConnection'));
    }
    // reinstall stop signal handler
    static::$globalEvent->add(SIGINT, EventInterface::EV_SIGNAL, $signalHandler);
    // reinstall graceful stop signal handler
    static::$globalEvent->add(SIGHUP, EventInterface::EV_SIGNAL, $signalHandler);
    // reinstall reload signal handler
    static::$globalEvent->add(SIGUSR1, EventInterface::EV_SIGNAL, $signalHandler);
    // reinstall graceful reload signal handler
    static::$globalEvent->add(SIGQUIT, EventInterface::EV_SIGNAL, $signalHandler);
    // reinstall status signal handler
    static::$globalEvent->add(SIGUSR2, EventInterface::EV_SIGNAL, $signalHandler);
    // reinstall connection status signal handler
    static::$globalEvent->add(SIGIO, EventInterface::EV_SIGNAL, $signalHandler);
    
    //事件轮询
    static::$globalEvent->loop();//系统自动触发
    
    //解绑事件
    static::$globalEvent->del($this->_mainSocket, EventInterface::EV_READ);
    
    //销毁所有事件
    static::$globalEvent->destroy();
    //统计事件
    static::$globalEvent->getTimerCount();
    
    //定时器事件方法
    WorkermanTimer::$_event->clearAllTimer();
    static::$globalEvent->getTimerCount()
  • 相关阅读:
    如何用 Gerrit 提交代码到服务器
    营收奇迹:三消游戏Candy Crush Saga成功启示录
    flash进阶之路
    Flash笔记之swf居中和swf获取页面参数游戏全屏显示
    如何做一款成功的APP应用
    演义群侠扫盲
    笔记二 air android处理用户交互
    可以准备很多字体,玩家选择性下载
    AIR中调用exe或者bat可执行文件
    很好用的工具类
  • 原文地址:https://www.cnblogs.com/tkzc2013/p/14948694.html
Copyright © 2020-2023  润新知