前言
workerman--极简、稳定、高性能、分布式
workerman是什么
workerman是一款开源高性能PHP应用容器,它大大突破了传统PHP应用范围,被广泛的用于互联网、即时通讯、APP开发、硬件通讯、智能家居、物联网等领域的开发。他是纯php实现的,跟swoole不一样,Swoole 是一个使用 C++
语言编写的基于异步事件驱动和协程的并行网络通信引擎,对比来看workerman对PHPer更加友好,入门门槛更低,而且跨平台性更好,和已有的项目的对接更简单,更快速。因为它本身是PHP编写的,所以只要服务器支持php运行几乎就可以支持workerman的使用,无需更换运行环境或者对代码或者框架进行大幅的修改。集成到常见的框架比如thinkphp、laravel也很简单,兼容性好,特别是当服务器使用windows系统环境的时候swoole的支持不是很友好,这时候workerman就更显优势。不过事物都有两面性,有优点肯定也有缺点,最明显的就是workerman没有swoole强大,毕竟workerman是PHP实现的至少在运行效率上swoole略胜一筹。除了性能,在高并发上swoole也有较大的优势。
Workerman不是重复造轮子,它不是一个MVC框架,而是一个更底层更通用的服务框架,你可以用它开发tcp代理、梯子代理、做游戏服务器、邮件服务器、ftp服务器、甚至开发一个php版本的redis、php版本的数据库、php版本的nginx、php版本的php-fpm等等。Workerman可以说是PHP领域的一次创新,让开发者彻底摆脱了PHP只能做WEB的束缚。
实际上Workerman类似一个PHP版本的nginx,核心也是多进程+Epoll+非阻塞IO。Workerman每个进程能维持上万并发连接。由于本身常驻内存,不依赖Apache、nginx、php-fpm这些容器,拥有超高的性能。同时支持TCP、UDP、UNIXSOCKET,支持长连接,支持Websocket、HTTP、WSS、HTTPS等通讯协议以及各种自定义协议。拥有定时器、异步socket客户端、异步Redis、异步Http、异步消息队列等众多高性能组件。
为什么选择workerman
不管使用任何框架,都不会达到最完美的,因为世界本就没有完美的东西。而是要根据自己的需求,有所取舍地去选择合适的框架或者架构,就比如:我们现在要做一个小网站,流量很小,如果我们不做分析,一开始直接就选择分布式架构,考虑高并发,高可用,高性能等三高方案全部考虑进去,对于小网站来说,无非就是入不敷出,付出远远大于回报,完全是一种浪费大量成本,吃力不讨好的操作。所以这时候先选择单机架构,预留扩展的空间即可,等流量起来了再一步一步扩展架构,而不是想着一步到位。因此,选择合适的更重要,不但能解决问题还能节约成本,最重要的是选择从简单架构开始,更容易实现,更快速上线,对应快速占领市场很有优势,如果选择复杂的架构,可能还没上线风都停了。workerman有以下优势:
1.性能提升
基于常驻内存、epoll高性能事件循环库、高性能协议解析,workerman可将基于php-fpm的架构应用性能提升十倍甚至近百倍
2.稳定性
经过多年的不断打磨及完善,workerman早已具备企业级的稳定性,已经被众多公司用在生产环境上
3.兼容性
兼容现有composer生态。即将推出的workerman v5版本将支持PHP自带的Fiber协程以及Swoole、ReactPHP、AmPHP等协程库
4.易用性
少既是多,workerman只提供必要的功能接口,在保证workerman简约的同时,你会发现它使用真的很简单
5.应用场景广泛
Workerman不同于传统MVC框架,Workerman不仅可以用于Web开发,同时还有更广阔的应用领域,例如即时通讯类、物联网、游戏、服务治理、其它服务器或者中间件
我们上面简单对比了workerman和swoole的优缺点.简单分析了我们为什么选择workerman.接下来我们看看worker怎么使用.
安装
workerman从3.5.3版本开始同时支持linux系统和windows系统,不再区分linux版本和windows版本
环境准备
需要PHP>=5.4,并配置好PHP的环境变量,最新版本的workerman需要PHP>=7.0
本文的环境以PHP7.2为主
配置环境变量:
workerman在windows和linux的区别参考:https://www.workerman.net/windows
workerman的在Windows下与Linux下区别
1、win版本单个进程只支持200+个连接
2、win版本count属性无效,全部为单进程
3、不支持start stop reload restart status命令
4、cmd命令行启动,后面可接多个文件,例如 php start_web.php start_gateway.php start_worker.php
5、无法守护进程,cmd窗口关掉后服务即停止
6、每个启动文件只能实例化一个容器(Worker/WebServer/Gateway/BusinessWorker),需要实例化多个容器时需要拆成多个文件,例如 start_web.php start_gateway.php start_worker.php 分别初始化web gateway worker
Windows版本workerman的启动与停止
cmd命令行中运行 php your_file.php(注意后面可以接多个文件)注意windows版本没有stop、reload、restart、status命令,启动时直接运行 php 文件.php 即可,停止运行按ctrl+c
安装很简单,除了php的环境配置,跟其他的php第三方库的安装一样简单.只需要通过composer命令安装即可,或者下载源码放到项目中就可使用
项目源码地址:https://gitee.com/walkor/workerman
composer:https://www.phpcomposer.com(不熟悉composer的童鞋可以点击链接跳转了解)
composer安装命令如下:
composer require workerman/workerman
日常生产的流程主要分以下三种情况:
流程一:新项目流程 创建 composer.json,并添加依赖到的扩展包; 运行 composer install,安装扩展包并生成 composer.lock; 提交 composer.lock 到代码版本控制器中,如:git
流程二:项目协作者安装现有项目 克隆项目后,根目录下直接运行 composer install 从 composer.lock 中安装 指定版本 的扩展包以及其依赖
此流程适用于生产环境代码的部署。
流程三:为项目添加新扩展包
使用 composer require vendor/package 添加扩展包; 提交更新后的 composer.json 和 composer.lock 到代码版本控制器中,如:git
composer install - 如有 composer.lock 文件,直接安装,否则从 composer.json 安装最新扩展包和依赖
- composer update - 从 composer.json 安装最新扩展包和依赖
- composer update vendor/package - 从 composer.json 或者对应包的配置,并更新到最新
- composer require new/package - 添加安装 new/package, 可以指定版本,如: composer require new/package ~2.7
安装步骤:
- 新建项目文件夹wmtest
网络异常,图片无法展示| - 进入项目根目录
- 初始化项目
- 执行上面的命令或者composer init
新建的文件还是空的:
方法1:
我们这个是新项目执行composer init
对项目进行初始化,执行操作如下图:
一直下一步等待安装完成即可
方法2:
进入项目目录执行composer require workerman/workerman
执行过程如下:
安装完成之后项目目录会增加三个文件,如下图
vendor就是composer安装所有依赖的文件夹.现在里面有我们刚安装的workerman依赖:
从composer.json的内容中也可以看到安装了哪些依赖
从上图可以看出,我们只安装了workerman4.0的依赖
安装完接下来就是使用了
workerman的简单使用
WorkerMan开发与普通PHP开发的不同之处
- 普通PHP开发一般是基于HTTP应用层协议,WebServer已经帮开发者完成了协议的解析
- WorkerMan支持各种协议,目前内置了HTTP、WebSocket等协议。WorkerMan推荐开发者使用更简单的自定义协议通讯
- PHP在Web应用中一次请求过后会释放所有的变量与资源
- WorkerMan开发的应用程序在第一次载入解析后便常驻内存,使得类的定义、全局对象、类的静态成员不会释放,便于后续重复利用
- 由于WorkerMan会缓存编译后的PHP文件,所以要避免多次require/include相同的类或者常量的定义文件。建议使用require_once/include_once加载文件。
- 由于WorkerMan是常驻内存的,php类即函数的定义加载一次后便常驻内存,不会再次读取磁盘加载,所以每次修改完业务代码需要重启才能生效。
- WorkerMan运行在PHP命令行模式下,当调用exit、die退出语句时,会导致当前进程退出。虽然子进程退出后会立刻重新创建一个的相同的子进程继续服务,但是还是可能对业务产生影响。
- 由于WorkerMan不会在每次请求后释放全局对象及类的静态成员,在数据库等单例模式中,往往会将数据库实例(内部包含了一个数据库socket连接)保存在数据库静态成员中,使得WorkerMan在进程生命周期内都复用这个数据库socket连接。需要注意的是当数据库服务器发现某个连接在一定时间内没有活动后可能会主动关闭socket连接,这时再次使用这个数据库实例时会报错,(错误信息类似mysql gone away)。WorkerMan提供了数据库类,有断开重连的功能,开发者可以直接使用。
注意事项:有必要注意下代码是运行在主进程还是子进程,一般来说在Worker::runAll();
调用前运行的代码都是在主进程运行的,onXXX回调运行的代码都属于子进程。注意写在Worker::runAll();
后面的代码永远不会被执行
下面我们使用workerman编写一些简单的例子
利用workerman实现Http server
文件:test.php
useWorkerman\Worker; useWorkerman\Connection\TcpConnection; useWorkerman\Protocols\Http\Request; require_once__DIR__ . '/vendor/autoload.php'; // 创建一个Worker监听2345端口,使用http协议通讯$http_worker=newWorker("http://0.0.0.0:2345"); // 启动4个进程对外提供服务$http_worker->count=4; // 接收到浏览器发送的数据时回复hello world给浏览器$http_worker->onMessage=function(TcpConnection$connection, Request$request) { // 向浏览器发送hello world$connection->send('你好,欢迎使用workerman~'); }; // 运行workerWorker::runAll();
打开命令行,执行以下命令然后访问http://127.0.0.1:2345/
php test.php start
执行效果如下:
利用workerman实现WebSocket
文件:ws_test.php
useWorkerman\Worker; useWorkerman\Connection\TcpConnection; require_once__DIR__ . '/vendor/autoload.php'; // 注意:这里与上个例子不同,使用的是websocket协议$ws_worker=newWorker("websocket://0.0.0.0:2000"); // 启动4个进程对外提供服务$ws_worker->count=4; // 当收到客户端发来的数据后返回hello $data给客户端$ws_worker->onMessage=function(TcpConnection$connection, $data) { // 向客户端发送hello $data$connection->send('你好,欢迎你找我聊天哦! ' . $data); }; // 运行workerWorker::runAll();
通过前端进行请求.创建testws.html
<html><head><title>测试Websocket</title><style>body { margin: 0; } canvas { width: 100%; height: 100% } </style></head><body><script>ws=newWebSocket("ws://127.0.0.1:2000"); ws.onopen=function() { console.log("连接成功"); ws.send('李白'); console.log("给服务端发送一个字符串:你好!我来了"); }; ws.onmessage=function(e) { console.log("收到服务端的消息:"+e.data); }; </script></body></html>
执行结果:
前端返回结果:
利用workerman实现直接使用TCP传输数据
文件:tcp_test.php
useWorkerman\Worker; useWorkerman\Connection\TcpConnection; require_once__DIR__ . '/vendor/autoload.php'; // 创建一个Worker监听2347端口,不使用任何应用层协议$tcp_worker=newWorker("tcp://0.0.0.0:2347"); // 启动4个进程对外提供服务$tcp_worker->count=4; // 当客户端发来数据时$tcp_worker->onMessage=function(TcpConnection$connection, $data) { // 向客户端发送hello $data$connection->send('你好,数据传输成功' . $data); }; // 运行workerWorker::runAll();
执行结果:
启动tcp服务
测试tcp服务
telnet127.0.0.1 2347
执行结果:
这里只是利用简单的例子抛转引玉,workman还有更多更好玩,更强大的用法等你探索.需要掌握workerman支持的各种通讯协议的用法.workerman目前已经支持HTTP、websocket、text协议、frame协议,ws协议,需要基于这些协议通讯时可以直接使用,使用方法为:在初始化Worker时指定协议,比如使用$tcp_worker = new Worker("tcp://0.0.0.0:2347");
创建tcp服务.当workerman自带的通讯协议满足不了开发需求时,还可以可以根据需求定制自己的通讯协议.所以要想玩好workerman一定要熟悉掌握各种通讯协议的使用