1.php使用swoole的应用场景,你知道的有哪些?

  • 与硬件设备连接通讯(定位设备)
  • IM系统(用于直播页面的聊天通讯)

场景1 - 实时收集定位数据实时输出(例 滴滴司机行驶轨迹)

说明:

需要将所有的定位设备实时的接收,将实时的轨迹记录显示在地图上

注意点:

第一点:web1服务器 连接的用户1,2,3,web1广播信息时只能广播用户1,2,3,不能广播web2连接的用户4,5,6,假设场景是聊天,用户1发送一消息,只有web1 服务器的用户能看到,web2的用户全部不能收到。

第二点:消息的频率控制,例:100个设备,100个用户, 100个设备每秒上传一条数据,需要实时广播给每个用户,就是每秒要100*100 = 1W次,所以可以汇总每秒数据广播给所有用户等等方法

场景2 - 只收集定位设备入库

说明:需要把所有的定位设备上传的数据入库,设备7个,每秒一条数据,个人使用swoole 的task 函数(投递一个异步的任务到 task_worker池中,此函数是非阻塞的, worker进程数同样可以配置) 后调用接口方式入库

服务器内存报警问题

原因: 在于swoole_server->task 函数

官方介绍task底层使用Unix Socket管道通信,是全内存的,没有IO消耗。单进程读写性能可达100万/s,不同的进程使用不同的管道通信,可以最大化利用多核。

但这任务如果是调用程序接口时,由于网络的延迟,增加的任务大于消费的任务时,内存占用会不断的增加,导致服务器的内存被占满。

解决方法:

消息针对入任务的频率控制,可以根据自己的业务场景定义这个时间与是否可延迟等情况,汇总1秒内的所有数据再调用程序接口(汇总时个人使用redis),最好能直接入库,不必调用接口

简单代码片段,不全(供初学者了解,官方网站demo相似)

function __construct($config)

{

    $this->config = $config;



    $this->serv = new Swoole\Server($config['server']['host'], $config['server']['port']);

    // 连接redis

    $this->redis = new Predis\Client($config['redis']);

    $this->storage = new Storage($this->config);



    $this->serv->set([

        'worker_num'      => $this->config['server']['workerNum'],   //工作进程数量

        'daemonize'       => $this->config['server']['daemonize'], //是否作为守护进程

        'task_worker_num' => $this->config['server']['taskWorkerNum'],

    ]);

    $this->serv->on('connect', function ($serv, $fd){

        $this->onConnect($fd, $serv);

    });

    

    $this->serv->on('receive', function ($serv, $fd, $from_id, $data)  {

        $this->onReceive($fd, $serv, $data);

    });



    $this->serv->on('Close', function($server, $fd) {

        $this->onClose($fd, $server);

        

    });

    $this->serv->on('Task', function($server, $task_id, $from_id, $data) {

        $this->onTask($server, $task_id, $from_id, $data);

        

    });

    $this->serv->on('Finish', function($server, $task_id, $data) {

        $this->onFinish($server, $task_id, $data);

        

    });



    $this->serv->start();

}



public function onTask($serv, $task_id, $from_id, $data){

    // insert 方法是通过接口入库

    $this->storage->insert($data);

}

public function onReceive($fd, $serv, $data)

{

    $this->storage->writeLog('message:'.$data);

    $data = $this->formatData($data, $fd);

    $serv->task($data);

}

public function onClose($fd, $serv)

{

    // writeLog 方法是写入log

    $this->storage->writeLog('close fd:'.$fd);

}

public function onFinish($serv, $task_id, $data)

{

    return '';

}

场景-IM系统

  • 参考官方github: webim系统.
  • 官方wiki: swoole 框架wiki

好处:

  • 封装了数据库的model类,数据库的ORM接口
  • redis的封装,可以实现多实例访问
  • 框架有一些常用的方法,像log 等等(我只用到了log)
  • webim 官方有demon,可以参考

坏处:

  • 文档特别不全,一个简单的实现会折腾半天

 

 

2.协程、进程、线程分别是什么?

进程、线程和协程是三个在多任务处理中常听到的概念,三者各有区别又相互联系。

进程

进程是一个程序在一个数据集中的一次动态执行过程,可以简单理解为“正在执行的程序”,它是CPU资源分配和调度的独立单位。 

进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。 

进程的局限是创建、撤销和切换的开销比较大。

线程

线程是在进程之后发展出来的概念。 线程也叫轻量级进程,它是一个基本的CPU执行单元,也是程序执行过程中的最小单元,由线程ID、程序计数器、寄存器集合和堆栈共同组成。一个进程可以包含多个线程。 

线程的优点是减小了程序并发执行时的开销,提高了操作系统的并发性能,缺点是线程没有自己的系统资源,只拥有在运行时必不可少的资源,但同一进程的各线程可以共享进程所拥有的系统资源,如果把进程比作一个车间,那么线程就好比是车间里面的工人。不过对于某些独占性资源存在锁机制,处理不当可能会产生“死锁”。

协程

协程是一种用户态的轻量级线程,又称微线程,英文名Coroutine,协程的调度完全由用户控制。人们通常将协程和子程序(函数)比较着理解。 

子程序调用总是一个入口,一次返回,一旦退出即完成了子程序的执行。 

协程的起始处是第一个入口点,在协程里,返回点之后是接下来的入口点。在python中,协程可以通过yield来调用其它协程。通过yield方式转移执行权的协程之间不是调用者与被调用者的关系,而是彼此对称、平等的,通过相互协作共同完成任务。

其运行的大致流程如下:

  • 第一步,协程A开始执行。
  • 第二步,协程A执行到一半,进入暂停,通过yield命令将执行权转移到协程B。
  • 第三步,(一段时间后)协程B交还执行权。
  • 第四步,协程A恢复执行。

协程的特点在于是一个线程执行,与多线程相比,其优势体现在:

协程的执行效率非常高。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

协程不需要多线程的锁机制。在协程中控制共享资源不加锁,只需要判断状态就好了。

Tips:利用多核CPU最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

 

 

3.你如何运用docker搭建php环境?

在Docker流行之前,要搭建开发环境通常有两种选择:一种是使用wamp、xampp、mamp等集成开发环境安装包,另外一种就是使用普通虚拟机来安装linux服务器,然后通过下载一键安装包(如:lnmp)或者逐个安装做需要的软件。

前者虽然简单,但太不灵活,想要安装额外的软件或者版本会很麻烦或者干脆不知道如何下手;后者除了费时费力,占用本机资源过多,可能会导致系统运行缓慢,而且如果你忘了及时生成快照,一旦失误,追悔莫及。

幸好,Docker来了!它简单易用,灵活多变,方便迅捷,扫除了以上种种弊端。如果你想稍微详细的认识下这位虚拟化界的明星,传送门。

安装Docker

平时开发的环境一般都是Mac或者windows,Linux暂时没有研究,所以接下来只针对前两者写下步骤,这两个平台,官方都推荐了两种安装方式:app和工具包(toolbox)。

注:app方式对系统版本和配置会有一定要求,而且Windows需要你安装微软虚拟化产品Hyper-V,具体见app链接页面。

APP

  • https://docs.docker.com/docke...
  • https://docs.docker.com/docke...

工具包

  • https://www.docker.com/produc...

以上方式本质上都会在你的系统中安装docker-engine、docker-machine、docker-compose和VirtualBox(除了Windows的app方式)。

因为docker高度依赖linux内核提供的cgroup,namespace 等特性和接口,所以mac和windows平台需要使用docker-machine和虚拟机在后台创建运行一个linux内核。

而我的安装方式就是直接在 Mac 上使用brew,如果你未曾安装brew,则在终端执行以下代码:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

安装docker所需要的包:

brew install docker docker-machine docker-compose virtualbox

创建一个docker-machine:

docker-machine create -d virtualbox default

注:-d 指定使用virtualbox来创建default 为machine名称

告诉主机如何找到docker-machine:

echo $(docker-machine env ) >> .zshrc

注:这里的.zshrc是我的用户配置文件,如果你用bash,可以改为.bash_profile。

启动docker-machine:

docker-machine start default Compose你的应用

docker-compose是用于定义和运行复杂Docker应用的工具。你可以在docker-compose.yml文件中定义一个多容器的应用,然后使用一条命令来启动,然后所有预先定义好的操作都会被自动完成。

为了不重复造轮子,直接使用github上的第三方包。当然你也可以亲自构建每一个你所需要的容器镜像,然后用docker-compose.yml将所有容器组织起来运行,但这需要你具备一定的linux基础和docker的运行机制和相关语法。

我的仓库目前支持的容器组合:

nginx , php-fpm , mysql , redis , mongo , apache2 , memcached , elasticsearch , workspace。

注:workspace和php-fpm一般会被其他容器所依赖,所以会自动运行,启动时不必指定。

进入到你的应用的上级目录:

git clone https://github.com/RystLee/DevDock.git

 

修改hosts

如果直接安装启动,是可以通过Docker IP : 192.168.99.100,就能直接访问nginx的,但一般而言为了方便记忆,我们会去修改hosts文件,增加一条:

192.168.99.100 laravel.dev

修改你的nginx中的站点配置文件:

在DevDock目录下找到nginx,修改sites目录下的站点配置文件,通过修改本地的hosts来自定义域名,并在nginx容器中的sites文件夹下,修改相应的域名映射。

server_name laravel.dev

安装启动应用

cd DevDock docker-compose up -d nginx mysql ... # 后面跟上你想使用的容器即可

然后,耐心地等待开发环境自动搭建完成即可,如果中途出现错误,一般是因为GFW,网络会不太通畅,重新执行一两次就好,完成之后,打开浏览器,访问: http://laravel.dev 即可。