Swoole整合ThinkPHP3.2系列教程二_一个不靠谱的程序员-CSDN博客

mikel阅读(850)

来源: Swoole整合ThinkPHP3.2系列教程二_一个不靠谱的程序员-CSDN博客

swoole和ThinkPHP的整合
先上一份在我们系统内部的swoole整合的架构预览

├─Application 应用目录
│ │
│ ├─Cli Cli模块
│ │ ├─Controller 制器类
│ │ │ ├─StartController TP框架加载时默认加载的控制器
│ │ │ ├─SwooleController 我们的业务逻辑写在这里面
│ │ └─ …
├─Swoole
│ │
│ ├─log swoole运行日志
│ ├─Server.php swoole的服务代码
│ ├─swoole.php 用于cli模式下启动和软重启swoole服务
1
2
3
4
5
6
7
8
9
10
11
12
最早测试的时候不是这样搭建的,而是把Server.php里的关于服务的东西放在了TP控制器里,在cli模式下调用

php index.php(入口文件) Swoole/start (控制器/方法)
1
这种模式是把swoole套在了TP里运行,也是可行的,但是总觉得启动个swoole服务为毛还要告诉TP一声?

我们想要的一种模式是业务服务器独立运行,swoole服务作为守护进程常驻内存,当浏览器需要运行比较耗时的操作时,需要跟swoole服务进程建立长连接,当耗时的任务执行完毕时会通知浏览器已经完毕。

整个过程下来,swoole服务和业务服务不应该耦合在一起的。最完美的状态是swoole独立运行(使用swoole框架重新写耗时操作的业务逻辑代码),独立连接数据库。当浏览器执行这些任务时,就不再找业务服务器了,而是直接跟swoole服务打交道。

可是我们没有那么多的时间和精力,我们只能在swoole服务的进程里调用TP框架的东西,来执行我们在TP里写的代码。最后参考了网上的一些方案,选择了这样与TP结合。

代码逻辑
/Swoole/Server.php
关于swoole服务的一些配置,比如监听地址,端口号(默认为9501),和一些基础配置

一些回调函数,里面的代码注释很完整,可以直接看代码

<?php
// +———————————————————————-
// 2017-8-23 09:03:40
// 此次修改为只作为websocket的服务端
// +———————————————————————-
class Server{
protected $swoole;
// 监听所有地址
protected $host = ‘0.0.0.0’;
// 监听 9501 端口
protected $port = 9501;
// 配置项
protected $option = [
//设置启动的worker进程数
‘worker_num’ => 2,
//task进程的数量
‘task_worker_num’ => 4,
//指定task任务使用消息队列模式,3表示完全争抢模式,task进程会争抢队列,无法使用定向投递
‘task_ipc_mode’ => 3,
//task进程的最大任务数
‘task_max_request’ => 1000,
// 守护进程化
‘daemonize’ => false,
// 监听队列的长度
‘backlog’ => 128,
//绑定uid时用
‘dispatch_mode’ => 5,
//设置日志路径
‘log_file’ => SWOOLE_LOG_PATH,
];
protected function init(){
//异步非阻塞多进程的websocket
$this->swoole = new swoole_websocket_server($this->host, $this->port);
$eventList = [‘Open’, ‘Message’, ‘Close’, ‘HandShake’ , ‘Task’, ‘Finish’ , ‘WorkerStart’ , ‘Receive’];

// 设置参数
if (!empty($this->option)) {
$this->swoole->set($this->option);
}
// 设置回调
foreach ($eventList as $event) {
if (method_exists($this, ‘on’ . $event)) {
$this->swoole->on($event, [$this, ‘on’ . $event]);
}
}
}
public function start(){
$this->init();
$this->swoole->start();
}
public function getHost(){
return $this->host;
}
public function getPort(){
return $this->port;
}
/**
* [onOpen 建立连接时的回调函数]
* @method onOpen
* @param swoole_server $serv [description]
* @param swoole_http_request $req [description]
* @return [type] [description]
*/
public function onOpen(swoole_server $serv, swoole_http_request $req){
//将连接绑定到uid上面
if(!empty($req->get)&&$req->get[‘uid’]){
$serv->bind($req->fd , $req->get[‘uid’]);
}
}
/**
* [onMessage 接收到socket客户端发送数据的回调函数]
* @method onMessage
* @param swoole_server $serv [description]
* @param swoole_websocket_frame $frame [description]
* @return [type] [description]
*/
public function onMessage(swoole_server $serv, swoole_websocket_frame $frame){
//收到数据时处理数据
//根据收到的cmd名字去调用指定的方法
$receive = json_decode($frame->data,true);
//为了避免数据处理量过大阻塞当前进程,导致服务响应变慢,我们把耗时的操作扔到TaskWorker进程池中执行
//当要执行的方法存在并且已经在swoole_log表里备案过的可以丢到task进程池
$swooleLog = D(‘SwooleLog’);
$swooleController = A(‘Swoole’);
//$receive[‘args’][‘id’]是业务服务器那边数据库SwooleLog表里的id
if (method_exists($swooleController, $receive[‘cmd’]) && $receive[‘args’][‘id’]) {
$task_id = $serv->task($receive);
//记录task_id信息
//…其他你想要做的精细化处理
}else{
if($receive[‘cmd’] === ‘reload’){
//利用Swoole提供的柔性终止/重启的机制
$rs = $serv -> reload();
$serv->push($frame->fd , $rs);
}elseif($receive[‘args’][‘id’]){
$returnData = $this->_returnStr(‘submit_error’);
$serv->push($frame->fd , $returnData);
}elseif(method_exists($this, $receive[‘cmd’])){
$this->{$receive[‘cmd’]}($serv , $frame);
}
}
}
/**
* [onTask 在task_worker进程池内被调用时的回调函数]
* @method onTask
* @param swoole_server $serv [description]
* @param int $task_id [description]
* @param int $src_worker_id [description]
* @param mixed $data [description]
* @return [type] [description]
*/
public function onTask(swoole_server $serv, $task_id, $src_worker_id, $data){
//记录任务开始执行的时间
//…自己发挥 我们项目的业务逻辑部分已经删除
//
try{
$swooleController = A(‘Swoole’);
$rs = $swooleController->{$data[‘cmd’]}($data[‘args’]);
return json_encode([‘id’=>$data[‘args’][‘id’] , ‘status’ => true , ‘other’ => $rs]);
}catch(\Exception $e){
return json_encode([‘id’=>$data[‘args’][‘id’] , ‘status’ => false , ‘other’ => $e->getMessage()]);
}
}
/**
* [onFinish 任务执行完毕时的回调函数]
* @method onFinish
* @param swoole_server $serv [description]
* @param int $task_id [description]
* @param string $data [description]
* @return [type] [description]
*/
public function onFinish(swoole_server $serv, $task_id, string $data){
$rs = json_decode($data , true);
$swooleLog = D(‘SwooleLog’);
//…
//通知用户该任务已经执行完毕了 可以来查看数据了
//检查客户端链接是否存在 如果存在的话发送消息
$returnData = $this->_returnStr(‘task_excute_success’);
foreach($serv->connections as $fd){
$conn = $serv->connection_info($fd);
//根据uid判断当前连接是活跃的
//这里是业务服务器那边能取到的用户信息,这里已经删除,保护我们的项目
$serv->push($fd , $returnData);
}
}
/**
* [onWorkerStart 为了应用能热加载 把框架的东西放到worker进程启动]
* @method onWorkerStart
* @param swoole_server $server [description]
* @param [type] $worker_id [description]
* @return [type] [description]
*/
public function onWorkerStart(swoole_server $server, $worker_id){
// 定义应用目录
define(‘APP_PATH’, ‘../Application/’);
// 定义应用模式
define(‘APP_Mode’, ‘cli’);
define(‘BIND_MODULE’,’Cli’);
// 引入ThinkPHP入口文件
require ‘../ThinkPHP/ThinkPHP.php’;
}
/**
* [_returnStr 返回给客户端的数据]
* @method _returnStr
* @param [type] $type [description]
* @return [type] [description]
*/
private function _returnStr($type){
switch ($type) {
case ‘submit_success’:
$returnData = [
‘code’ => 202,
‘info’ => ‘任务已经提交,请等待服务器计算数据!’
];
break;
case ‘submit_error’:
$returnData = [
‘code’ => 404,
‘info’ => ‘任务提交失败,请联系管理员进行处理!’
];
break;
case ‘task_excute_success’:
$returnData = [
‘code’ => 200,
‘info’ => ‘任务执行完毕!’
];
break;
}
return json_encode($returnData);
}
/**
* [clients 获取所有的客户端连接信息,返回我们的uid]
* @method clients
* @return [type] [description]
*/
public function clients(swoole_server $serv , swoole_websocket_frame $frame){
//…
$serv->push($frame->fd , json_encode($serv->connections));
}
public function __call($method, $args){
call_user_func_array([$this->swoole, $method], $args);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/Swoole/swoole.php
是一个面向cli编程的代码,主要提供了start和reload两个命令

#!/bin/env php
<?php
/**
* 定义项目根目录&swoole-task pid
*/
define(‘SWOOLE_PATH’, __DIR__);
define(‘SWOOLE_LOG_PATH’, SWOOLE_PATH . DIRECTORY_SEPARATOR . ‘log’ . DIRECTORY_SEPARATOR . ‘Swoole’ . ‘.log’);

/**
* 加载 swoole server
*/
include SWOOLE_PATH . DIRECTORY_SEPARATOR . ‘Server.php’;

//提示帮助信息
if ($argc != 2 || in_array($argv[1], array(‘–help’, ‘-help’, ‘-h’, ‘-?’))) {
echo <<<HELP
用法:php swoole.php 选项 … 可选的命令[start|reload|list]

–help|-help|-h|-? 显示本帮助说明

选项说明
start, 启动swoole服务[默认监测9501端口]
reload, 柔性重启所有workder进程
list, 查看当前所有连接的客户端数

HELP;
exit;
}

//执行命令行选项
switch($argv[1]){
case ‘start’:
start();
break;
case ‘reload’:
reload();
break;
case ‘list’:
clients();
break;
default:
exit(“输入命令有误 : {$argv[1]}, 请查看帮助文档\n”);
break;
}

//启动swoole服务
function start(){
$server = new Server();
$server->start();
}

//柔性重启swoole服务
function reload(){
echo “正在柔性重启swoole的worker进程…” . PHP_EOL;
try {
$server = new Server();
$port = $server->getPort();
$host = ‘127.0.0.1’;
$cli = new swoole_http_client($host, $port);
$cli->set([‘websocket_mask’ => true]);
$cli->on(‘message’, function ($_cli, $frame) {
if($frame->data){
exit(‘swoole重启成功!’.PHP_EOL);
}
});
$cli->upgrade(‘/’, function ($cli) {
$cli->push(json_encode([‘cmd’ => ‘reload’]));
});
} catch (Exception $e) {
exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
}
}

function clients(){
try {
$server = new Server();
$port = $server->getPort();
$host = ‘127.0.0.1’;
$cli = new swoole_http_client($host, $port);
$cli->set([‘websocket_mask’ => true]);
$cli->on(‘message’, function ($_cli, $frame) {
$users = json_decode($frame->data , true);
if(empty($users)){
echo ‘当前没有客户端连接’.PHP_EOL;
}else{
foreach($users as $user){
echo ‘—‘.$user.PHP_EOL;
}
}
exit();
});
$cli->upgrade(‘/’, function ($cli) {
$cli->push(json_encode([‘cmd’ => ‘clients’]));

});
} catch (Exception $e) {
exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
存在的问题
0.以上的代码是从项目里摘出来的,大体思路还在,业务部分代码删除了。不保证正常运行,但是问题不大,如果报错了聪明的你一定一看就知道是什么错误。

1.由于我们将TP框架的东西放在workstart回调函数里启动,这里只要启动worker进程和task进程,都会加载一次TP框架的东西到内存里,对内存的占用问题还需要仔细研究。

2.目前还不支持那些不支持HTML5 WebSocket的浏览器 我会尽快想办法解决。用flash模拟socket请求的方法没有试成功,近期重新再搞一波。
————————————————
版权声明:本文为CSDN博主「一个不靠谱的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u013705066/article/details/77679233

thinkPHP3.2.3集成swoole扩展 - 林中侠客 - 博客园

mikel阅读(717)

来源: thinkPHP3.2.3集成swoole扩展 – 林中侠客 – 博客园

swoole.php

复制代码
  1 #!/bin/env php
  2 <?php
  3 /**
  4  * 默认时区定义
  5  */
  6 date_default_timezone_set('Asia/Shanghai');
  7 
  8 /**
  9  * 设置错误报告模式
 10  */
 11 error_reporting(0);
 12 
 13 /**
 14  * 设置默认区域
 15  */
 16 setlocale(LC_ALL, "zh_CN.utf-8");
 17 
 18 /**
 19  * 检测 PDO_MYSQL
 20  */
 21 if (!extension_loaded('pdo_mysql')) {
 22     exit('PDO_MYSQL extension is not installed' . PHP_EOL);
 23 }
 24 /**
 25  * 检查exec 函数是否启用
 26  */
 27 if (!function_exists('exec')) {
 28     exit('exec function is disabled' . PHP_EOL);
 29 }
 30 /**
 31  * 检查命令 lsof 命令是否存在
 32  */
 33 exec("whereis lsof", $out);
 34 if ($out[0] == 'lsof:') {
 35     exit('lsof is not found' . PHP_EOL);
 36 }
 37 /**
 38  * 定义项目根目录&swoole-task pid
 39  */
 40 define('SWOOLE_PATH', __DIR__);
 41 define('SWOOLE_TASK_PID_PATH', SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Swoole' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . 'swoole-task.pid');
 42 define('SWOOLE_TASK_NAME_PRE', 'swooleServ');
 43 
 44 /**
 45  * 加载 swoole server
 46  */
 47 include SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Swoole' . DIRECTORY_SEPARATOR . 'SwooleServer.php';
 48 
 49 function portBind($port) {
 50     $ret = [];
 51     $cmd = "lsof -i :{$port}|awk '$1 != \"COMMAND\"  {print $1, $2, $9}'";
 52     exec($cmd, $out);
 53     if ($out) {
 54         foreach ($out as $v) {
 55             $a = explode(' ', $v);
 56             list($ip, $p) = explode(':', $a[2]);
 57             $ret[$a[1]] = [
 58                 'cmd' => $a[0],
 59                 'ip' => $ip,
 60                 'port' => $p,
 61             ];
 62         }
 63     }
 64 
 65     return $ret;
 66 }
 67 
 68 function servStart($host, $port, $daemon, $name) {
 69     echo "正在启动 swoole-task 服务" . PHP_EOL;
 70     if (!is_writable(dirname(SWOOLE_TASK_PID_PATH))) {
 71         exit("swoole-task-pid文件需要目录的写入权限:" . dirname(SWOOLE_TASK_PID_PATH) . PHP_EOL);
 72     }
 73     if (file_exists(SWOOLE_TASK_PID_PATH)) {
 74         $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
 75         $cmd = "ps ax | awk '{ print $1 }' | grep -e \"^{$pid[0]}$\"";
 76         exec($cmd, $out);
 77         if (!empty($out)) {
 78             exit("swoole-task pid文件 " . SWOOLE_TASK_PID_PATH . " 存在,swoole-task 服务器已经启动,进程pid为:{$pid[0]}" . PHP_EOL);
 79         } else {
 80             echo "警告:swoole-task pid文件 " . SWOOLE_TASK_PID_PATH . " 存在,可能swoole-task服务上次异常退出(非守护模式ctrl+c终止造成是最大可能)" . PHP_EOL;
 81             unlink(SWOOLE_TASK_PID_PATH);
 82         }
 83     }
 84     $bind = portBind($port);
 85     if ($bind) {
 86         foreach ($bind as $k => $v) {
 87             if ($v['ip'] == '*' || $v['ip'] == $host) {
 88                 exit("端口已经被占用 {$host}:$port, 占用端口进程ID {$k}" . PHP_EOL);
 89             }
 90         }
 91     }
 92     unset($_SERVER['argv']);
 93     $_SERVER['argc'] = 0;
 94     echo "启动 swoole-task 服务成功" . PHP_EOL;
 95     $server = new SwooleServer('127.0.0.1', 9501);
 96     $server->run();
 97     //确保服务器启动后swoole-task-pid文件必须生成
 98     /*if (!empty(portBind($port)) && !file_exists(SWOOLE_TASK_PID_PATH)) {
 99         exit("swoole-task pid文件生成失败( " . SWOOLE_TASK_PID_PATH . ") ,请手动关闭当前启动的swoole-task服务检查原因" . PHP_EOL);
100     }*/
101 }
102 
103 function servStop($host, $port, $isRestart = false) {
104     echo "正在停止 swoole-task 服务" . PHP_EOL;
105     if (!file_exists(SWOOLE_TASK_PID_PATH)) {
106         exit('swoole-task-pid文件:' . SWOOLE_TASK_PID_PATH . '不存在' . PHP_EOL);
107     }
108     $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
109     $bind = portBind($port);
110     if (empty($bind) || !isset($bind[$pid[0]])) {
111         exit("指定端口占用进程不存在 port:{$port}, pid:{$pid[0]}" . PHP_EOL);
112     }
113     $cmd = "kill {$pid[0]}";
114     exec($cmd);
115     do {
116         $out = [];
117         $c = "ps ax | awk '{ print $1 }' | grep -e \"^{$pid[0]}$\"";
118         exec($c, $out);
119         if (empty($out)) {
120             break;
121         }
122     } while (true);
123     //确保停止服务后swoole-task-pid文件被删除
124     if (file_exists(SWOOLE_TASK_PID_PATH)) {
125         unlink(SWOOLE_TASK_PID_PATH);
126     }
127     $msg = "执行命令 {$cmd} 成功,端口 {$host}:{$port} 进程结束" . PHP_EOL;
128     if ($isRestart) {
129         echo $msg;
130     } else {
131         exit($msg);
132     }
133 }
134 
135 function servReload($host, $port, $isRestart = false) {
136     echo "正在平滑重启 swoole-task 服务" . PHP_EOL;
137     try {
138         $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
139         $ret = $client->connect($host, $port);
140         if (empty($ret)) {
141             exit("{$host}:{$port} swoole-task服务不存在或者已经关闭" . PHP_EOL);
142         } else {
143             $client->send(json_encode(array('action' => 'reload')));
144         }
145         $msg = "执行命令reload成功,端口 {$host}:{$port} 进程重启" . PHP_EOL;
146         if ($isRestart) {
147             echo $msg;
148         } else {
149             exit($msg);
150         }
151     } catch (Exception $e) {
152         exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
153     }
154 }
155 
156 function servClose($host, $port, $isRestart = false) {
157     echo "正在关闭 swoole-task 服务" . PHP_EOL;
158     try {
159         $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
160         $ret = $client->connect($host, $port);
161         if (empty($ret)) {
162             exit("{$host}:{$port} swoole-task服务不存在或者已经关闭" . PHP_EOL);
163         } else {
164             $client->send(json_encode(array('action' => 'close')));
165         }
166         //确保停止服务后swoole-task-pid文件被删除
167         if (file_exists(SWOOLE_TASK_PID_PATH)) {
168             unlink(SWOOLE_TASK_PID_PATH);
169         }
170         $msg = "执行命令close成功,端口 {$host}:{$port} 进程结束" . PHP_EOL;
171         if ($isRestart) {
172             echo $msg;
173         } else {
174             exit($msg);
175         }
176     } catch (\Exception $e) {
177         exit($e->getMessage() . PHP_EOL . $e->getTraceAsString());
178     }
179 }
180 
181 function servStatus($host, $port) {
182     echo "swoole-task {$host}:{$port} 运行状态" . PHP_EOL;
183     $pid = explode("\n", file_get_contents(SWOOLE_TASK_PID_PATH));
184     $bind = portBind($port);
185     if (empty($bind) || !isset($bind[$pid[0]])) {
186         exit("指定端口占用进程不存在 port:{$port}, pid:{$pid[0]}" . PHP_EOL);
187     }
188     $client = new \swoole_client(SWOOLE_SOCK_TCP, SWOOLE_SOCK_SYNC);
189     $ret = $client->connect($host, $port);
190     if (empty($ret)) {
191         exit("{$host}:{$port} swoole-task服务不存在或者已经停止" . PHP_EOL);
192     } else {
193         $client->send(json_encode(array('action' => 'status')));
194         $out = $client->recv();
195         $a = json_decode($out);
196         $b = array(
197             'start_time' => '服务器启动的时间',
198             'connection_num' => '当前连接的数量',
199             'accept_count' => '接受的连接数量',
200             'close_count' => '关闭的连接数量',
201             'tasking_num' => '当前正在排队的任务数',
202             'request_count' => '请求的连接数量',
203             'worker_request_count' => 'worker连接数量',
204             'task_process_num' => '任务进程数量'
205         );
206         foreach ($a as $k1 => $v1) {
207             if ($k1 == 'start_time') {
208                 $v1 = date("Y-m-d H:i:s", $v1);
209             }
210             echo $b[$k1] . ":\t$v1" . PHP_EOL;
211         }
212     }
213     exit();
214 }
215 
216 function servList() {
217     echo "本机运行的swoole-task服务进程" . PHP_EOL;
218     $cmd = "ps aux|grep " . SWOOLE_TASK_NAME_PRE . "|grep -v grep|awk '{print $1, $2, $6, $8, $9, $11}'";
219     exec($cmd, $out);
220     if (empty($out)) {
221         exit("没有发现正在运行的swoole-task服务" . PHP_EOL);
222     }
223     echo "USER PID RSS(kb) STAT START COMMAND" . PHP_EOL;
224     foreach ($out as $v) {
225         echo $v . PHP_EOL;
226     }
227     exit();
228 }
229 
230 //可执行命令
231 $cmds = [
232     'start',
233     'stop',
234     'restart',
235     'reload',
236     'close',
237     'status',
238     'list',
239 ];
240 $shortopts = "dDh:p:n:";
241 $longopts = [
242     'help',
243     'daemon',
244     'nondaemon',
245     'host:',
246     'port:',
247     'name:',
248 ];
249 $opts = getopt($shortopts, $longopts);
250 
251 if (isset($opts['help']) || $argc < 2) {
252     echo <<<HELP
253 用法:php swoole.php 选项 ... 命令[start|stop|restart|reload|close|status|list]
254 管理swoole-task服务,确保系统 lsof 命令有效
255 如果不指定监听host或者port,使用配置参数
256 
257 参数说明
258     --help  显示本帮助说明
259     -d, --daemon    指定此参数,以守护进程模式运行,不指定则读取配置文件值
260     -D, --nondaemon 指定此参数,以非守护进程模式运行,不指定则读取配置文件值
261     -h, --host  指定监听ip,例如 php swoole.php -h127.0.0.1
262     -p, --port  指定监听端口port, 例如 php swoole.php -h127.0.0.1 -p9520
263     -n, --name  指定服务进程名称,例如 php swoole.php -ntest start, 则进程名称为SWOOLE_TASK_NAME_PRE-name
264 启动swoole-task 如果不指定 host和port,读取默认配置
265 强制关闭swoole-task 必须指定port,没有指定host,关闭的监听端口是  *:port,指定了host,关闭 host:port端口
266 平滑关闭swoole-task 必须指定port,没有指定host,关闭的监听端口是  *:port,指定了host,关闭 host:port端口
267 强制重启swoole-task 必须指定端口
268 平滑重启swoole-task 必须指定端口
269 获取swoole-task 状态,必须指定port(不指定host默认127.0.0.1), tasking_num是正在处理的任务数量(0表示没有待处理任务)
270 
271 HELP;
272     exit;
273 }
274 //参数检查
275 foreach ($opts as $k => $v) {
276     if (($k == 'h' || $k == 'host')) {
277         if (empty($v)) {
278             exit("参数 -h --host 必须指定值\n");
279         }
280     }
281     if (($k == 'p' || $k == 'port')) {
282         if (empty($v)) {
283             exit("参数 -p --port 必须指定值\n");
284         }
285     }
286     if (($k == 'n' || $k == 'name')) {
287         if (empty($v)) {
288             exit("参数 -n --name 必须指定值\n");
289         }
290     }
291 }
292 
293 //命令检查
294 $cmd = $argv[$argc - 1];
295 if (!in_array($cmd, $cmds)) {
296     exit("输入命令有误 : {$cmd}, 请查看帮助文档\n");
297 }
298 
299 //监听ip 127.0.0.1,空读取配置文件
300 $host = '127.0.0.1';
301 if (!empty($opts['h'])) {
302     $host = $opts['h'];
303     if (!filter_var($host, FILTER_VALIDATE_IP)) {
304         exit("输入host有误:{$host}");
305     }
306 }
307 if (!empty($opts['host'])) {
308     $host = $opts['host'];
309     if (!filter_var($host, FILTER_VALIDATE_IP)) {
310         exit("输入host有误:{$host}");
311     }
312 }
313 //监听端口,9501 读取配置文件
314 $port = 9501;
315 if (!empty($opts['p'])) {
316     $port = (int)$opts['p'];
317     if ($port <= 0) {
318         exit("输入port有误:{$port}");
319     }
320 }
321 if (!empty($opts['port'])) {
322     $port = (int)$opts['port'];
323     if ($port <= 0) {
324         exit("输入port有误:{$port}");
325     }
326 }
327 //进程名称 没有默认为 SWOOLE_TASK_NAME_PRE;
328 $name = SWOOLE_TASK_NAME_PRE;
329 if (!empty($opts['n'])) {
330     $name = $opts['n'];
331 }
332 if (!empty($opts['name'])) {
333     $name = $opts['n'];
334 }
335 //是否守护进程 -1 读取配置文件
336 $isdaemon = -1;
337 if (isset($opts['D']) || isset($opts['nondaemon'])) {
338     $isdaemon = 0;
339 }
340 if (isset($opts['d']) || isset($opts['daemon'])) {
341     $isdaemon = 1;
342 }
343 //启动swoole-task服务
344 if ($cmd == 'start') {
345     servStart($host, $port, $isdaemon, $name);
346 }
347 //强制停止swoole-task服务
348 if ($cmd == 'stop') {
349     if (empty($port)) {
350         exit("停止swoole-task服务必须指定port" . PHP_EOL);
351     }
352     servStop($host, $port);
353 }
354 //关闭swoole-task服务
355 if ($cmd == 'close') {
356     if (empty($port)) {
357         exit("停止swoole-task服务必须指定port" . PHP_EOL);
358     }
359     servClose($host, $port);
360 }
361 //强制重启swoole-task服务
362 if ($cmd == 'restart') {
363     if (empty($port)) {
364         exit("重启swoole-task服务必须指定port" . PHP_EOL);
365     }
366     echo "重启swoole-task服务" . PHP_EOL;
367     servStop($host, $port, true);
368     servStart($host, $port, $isdaemon, $name);
369 }
370 //平滑重启swoole-task服务
371 if ($cmd == 'reload') {
372     if (empty($port)) {
373         exit("平滑重启swoole-task服务必须指定port" . PHP_EOL);
374     }
375     echo "平滑重启swoole-task服务" . PHP_EOL;
376     servReload($host, $port, true);
377 }
378 //查看swoole-task服务状态
379 if ($cmd == 'status') {
380     if (empty($host)) {
381         $host = '127.0.0.1';
382     }
383     if (empty($port)) {
384         exit("查看swoole-task服务必须指定port(host不指定默认使用127.0.0.1)" . PHP_EOL);
385     }
386     servStatus($host, $port);
387 }
388 //查看swoole-task服务进程列表
389 if ($cmd == 'list') {
390     servList();
391 }
复制代码

 

SwooleServer.php

复制代码
  1 <?php
  2 
  3 /**
  4  * Swoole服务端
  5  */
  6 class SwooleServer {
  7 
  8     private $_serv = null;
  9     private $_setting = array();
 10 
 11     public function __construct($host = '0.0.0.0', $port = 9501) {
 12         $this->_setting = array(
 13             'host' => $host,
 14             'port' => $port,
 15             'env' => 'dev', //环境 dev|test|prod
 16             'process_name' => SWOOLE_TASK_NAME_PRE,  //swoole 进程名称
 17             'worker_num' => 4, //一般设置为服务器CPU数的1-4倍
 18             'task_worker_num' => 4,  //task进程的数量
 19             'task_ipc_mode' => 3,  //使用消息队列通信,并设置为争抢模式
 20             'task_max_request' => 10000,  //task进程的最大任务数
 21             'daemonize' => 1, //以守护进程执行
 22             'max_request' => 10000,
 23             'dispatch_mode' => 2,
 24             'log_file' => SWOOLE_PATH . DIRECTORY_SEPARATOR . 'App' . DIRECTORY_SEPARATOR . 'Runtime' . DIRECTORY_SEPARATOR . 'Logs' . DIRECTORY_SEPARATOR . 'Swoole' . date('Ymd') . '.log',  //日志
 25         );
 26     }
 27 
 28     /**
 29      * 运行swoole服务
 30      */
 31     public function run() {
 32         $this->_serv = new \swoole_server($this->_setting['host'], $this->_setting['port']);
 33         $this->_serv->set(array(
 34             'worker_num' => $this->_setting['worker_num'],
 35             'task_worker_num' => $this->_setting['task_worker_num'],
 36             'task_ipc_mode ' => $this->_setting['task_ipc_mode'],
 37             'task_max_request' => $this->_setting['task_max_request'],
 38             'daemonize' => $this->_setting['daemonize'],
 39             'max_request' => $this->_setting['max_request'],
 40             'dispatch_mode' => $this->_setting['dispatch_mode'],
 41             'log_file' => $this->_setting['log_file']
 42         ));
 43         $this->_serv->on('Start', array($this, 'onStart'));
 44         $this->_serv->on('Connect', array($this, 'onConnect'));
 45         $this->_serv->on('WorkerStart', array($this, 'onWorkerStart'));
 46         $this->_serv->on('ManagerStart', array($this, 'onManagerStart'));
 47         $this->_serv->on('WorkerStop', array($this, 'onWorkerStop'));
 48         $this->_serv->on('Receive', array($this, 'onReceive'));
 49         $this->_serv->on('Task', array($this, 'onTask'));
 50         $this->_serv->on('Finish', array($this, 'onFinish'));
 51         $this->_serv->on('Shutdown', array($this, 'onShutdown'));
 52         $this->_serv->on('Close', array($this, 'onClose'));
 53         $this->_serv->start();
 54     }
 55 
 56     /**
 57      * 设置swoole进程名称
 58      * @param string $name swoole进程名称
 59      */
 60     private function setProcessName($name) {
 61         if (function_exists('cli_set_process_title')) {
 62             cli_set_process_title($name);
 63         } else {
 64             if (function_exists('swoole_set_process_name')) {
 65                 swoole_set_process_name($name);
 66             } else {
 67                 trigger_error(__METHOD__ . " failed. require cli_set_process_title or swoole_set_process_name.");
 68             }
 69         }
 70     }
 71 
 72     /**
 73      * Server启动在主进程的主线程回调此函数
 74      * @param $serv
 75      */
 76     public function onStart($serv) {
 77         if (!$this->_setting['daemonize']) {
 78             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server master worker start\n";
 79         }
 80         $this->setProcessName($this->_setting['process_name'] . '-master');
 81         //记录进程id,脚本实现自动重启
 82         $pid = "{$serv->master_pid}\n{$serv->manager_pid}";
 83         file_put_contents(SWOOLE_TASK_PID_PATH, $pid);
 84     }
 85 
 86     /**
 87      * worker start 加载业务脚本常驻内存
 88      * @param $server
 89      * @param $workerId
 90      */
 91     public function onWorkerStart($serv, $workerId) {
 92         if ($workerId >= $this->_setting['worker_num']) {
 93             $this->setProcessName($this->_setting['process_name'] . '-task');
 94         } else {
 95             $this->setProcessName($this->_setting['process_name'] . '-event');
 96         }
 97         // 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false
 98         define('APP_DEBUG', True);
 99         // 定义应用目录
100         define('APP_PATH', SWOOLE_PATH . DIRECTORY_SEPARATOR . 'Application' . DIRECTORY_SEPARATOR);
101         // 定义应用模式
102         define('APP_MODE', 'cli');
103         // 引入ThinkPHP入口文件
104         require SWOOLE_PATH . DIRECTORY_SEPARATOR . 'ThinkPHP' . DIRECTORY_SEPARATOR . 'ThinkPHP.php';
105     }
106 
107     /**
108      * 监听连接进入事件
109      * @param $serv
110      * @param $fd
111      */
112     public function onConnect($serv, $fd) {
113         if (!$this->_setting['daemonize']) {
114             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server connect[" . $fd . "]\n";
115         }
116     }
117 
118     /**
119      * worker 进程停止
120      * @param $server
121      * @param $workerId
122      */
123     public function onWorkerStop($serv, $workerId) {
124         if (!$this->_setting['daemonize']) {
125             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server[{$serv->setting['process_name']}  worker:{$workerId} shutdown\n";
126         }
127     }
128 
129     /**
130      * 当管理进程启动时调用
131      * @param $serv
132      */
133     public function onManagerStart($serv) {
134         if (!$this->_setting['daemonize']) {
135             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server manager worker start\n";
136         }
137         $this->setProcessName($this->_setting['process_name'] . '-manager');
138     }
139 
140     /**
141      * 此事件在Server结束时发生
142      */
143     public function onShutdown($serv) {
144         if (file_exists(SWOOLE_TASK_PID_PATH)) {
145             unlink(SWOOLE_TASK_PID_PATH);
146         }
147         if (!$this->_setting['daemonize']) {
148             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server shutdown\n";
149         }
150     }
151 
152     /**
153      * 监听数据发送事件
154      * @param $serv
155      * @param $fd
156      * @param $from_id
157      * @param $data
158      */
159     public function onReceive($serv, $fd, $from_id, $data) {
160         if (!$this->_setting['daemonize']) {
161             echo "Get Message From Client {$fd}:{$data}\n\n";
162         }
163         $result = json_decode($data, true);
164         switch ($result['action']) {
165             case 'reload':  //重启
166                 $serv->reload();
167                 break;
168             case 'close':  //关闭
169                 $serv->shutdown();
170                 break;
171             case 'status':  //状态
172                 $serv->send($fd, json_encode($serv->stats()));
173                 break;         
174             default:             
175                 $serv->task($data);
176                 break;
177         }
178     }
179 
180     /**
181      * 监听连接Task事件
182      * @param $serv
183      * @param $task_id
184      * @param $from_id
185      * @param $data
186      */
187     public function onTask($serv, $task_id, $from_id, $data) {
188         $result = json_decode($data, true);
189         //用TP处理各种逻辑
190         $serv->finish($data);
191     }
192 
193     /**
194      * 监听连接Finish事件
195      * @param $serv
196      * @param $task_id
197      * @param $data
198      */
199     public function onFinish($serv, $task_id, $data) {
200         if (!$this->_setting['daemonize']) {
201             echo "Task {$task_id} finish\n\n";
202             echo "Result: {$data}\n\n";
203         }
204     }
205 
206     /**
207      * 监听连接关闭事件
208      * @param $serv
209      * @param $fd
210      */
211     public function onClose($serv, $fd) {
212         if (!$this->_setting['daemonize']) {
213             echo 'Date:' . date('Y-m-d H:i:s') . "\t swoole_server close[" . $fd . "]\n";
214         }
215     }
216 
217 
218 }
复制代码

相关命令:

1、服务启动
#启动服务,不指定绑定端口和ip,则使用默认配置
php swoole.php start
#启动服务 指定ip 和 port
php swoole.php -h127.0.0.1 -p9501 start
#启动服务 守护进程模式
php swoole.php -h127.0.0.1 -p9501 -d start
#启动服务 非守护进程模式
php swoole.php -h127.0.0.1 -p9501 -D start
#启动服务 指定进程名称(显示进程名为 swooleServ-9510-[master|manager|event|task]
php swoole.php -h127.0.0.1 -p9501 -n 9501 start

2、强制服务停止
php swoole.php stop
php swoole.php -p9501 stop
php swoole.php -h127.0.0.1 -p9501 stop

3、关闭服务
php swoole.php close
php swoole.php -p9501 close
php swoole.php -h127.0.0.1 -p9501 close

4、强制服务重启
php swoole.php restart
php swoole.php -p9501 restart
php swoole.php -h127.0.0.1 -p9501 restart

5、平滑服务重启
php swoole.php reload
php swoole.php -p9501 reload
php swoole.php -h127.0.0.1 -p9501 reload

6、服务状态
php swoole.php status
php swoole.php -h127.0.0.1 -p9501 status

7、swoole-task所有启动实例进程列表(一台服务器swoole-task可以有多个端口绑定的实例)
php swoole.php list

 

Swoole-ThinkPHP.zip ( 1.14 MB )

 

Swoole跟thinkphp5结合开发WebSocket在线聊天通讯系统 - 八重樱 - 博客园

mikel阅读(1031)

来源: Swoole跟thinkphp5结合开发WebSocket在线聊天通讯系统 – 八重樱 – 博客园

ThinkPHP使用Swoole需要安装 think-swoole Composer包,前提系统已经安装好了Swoole PECL 拓展*

tp5的项目根目录下执行composer命令安装think-swoole:

composer require topthink/think-swoole

 

话不多说,直接上代码:

新建WebSocket.php控制器

(监听端口要确认服务器放行,宝塔环境还需要添加安全组规则)

复制代码
 1 <?php
 2  
 3 namespace app\home\controller;
 4 use think\swoole\Server;
 5 class WebSocket extends Server
 6 {
 7     protected $host = '0.0.0.0'; //监听所有地址
 8     protected $port = 9501; //监听9501端口
 9     protected $serverType = 'socket';
10     protected $option = [ 
11         'worker_num'=> 4, //设置启动的Worker进程数
12         'daemonize'    => false, //守护进程化(上线改为true)
13         'backlog'    => 128, //Listen队列长度
14         'dispatch_mode' => 2, //固定模式,保证同一个连接发来的数据只会被同一个worker处理
15  
16         //心跳检测:每60秒遍历所有连接,强制关闭10分钟内没有向服务器发送任何数据的连接
17         'heartbeat_check_interval' => 60,
18         'heartbeat_idle_time' => 600
19     ];
20  
21     //建立连接时回调函数
22     public function onOpen($server,$req)
23     {
24         $fd = $req->fd;//客户端标识
25         $uid = $req->get['uid'];//客户端传递的用户id
26         $token = $req->get['token'];//客户端传递的用户登录token
27         
28         //省略token验证逻辑......
29         if (!$token) {
30             $arr = array('status'=>2,'message'=>'token已过期');
31             $server->push($fd, json_encode($arr));
32             $server->close($fd);
33             return;
34         }
35         //省略给用户绑定fd逻辑......
36         echo "用户{$uid}建立了连接,标识为{$fd}\n";
37     }
38  
39     //接收数据时回调函数
40     public function onMessage($server,$frame)
41     {
42         $fd = $frame->fd;
43         $message = $frame->data;
44  
45         //省略通过fd查询用户uid逻辑......
46         $uid = 666;
47         $data['uid'] = $uid;
48         $data['message'] = '用户'.$uid.'发送了:'.$message;
49         $data['post_time'] = date("m/d H:i",time());
50         $arr = array('status'=>1,'message'=>'success','data'=>$data);
51  
52         //仅推送给当前连接用户
53         //$server->push($fd, json_encode($arr));
54         
55         //推送给全部连接用户
56         foreach($server->connections as $fd) {
57             $server->push($fd, json_encode($arr));
58         } 
59     }
60  
61     //连接关闭时回调函数
62     public function onClose($server,$fd)
63     {
64         echo "标识{$fd}关闭了连接\n";
65     }
66 }
复制代码

 

前端演示页面:

(省略控制器判断登录状态、分配数据逻辑……)

复制代码
  1 <!DOCTYPE html>
  2 <html lang="en">
  3 <head>
  4 <meta charset="UTF-8" />
  5 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  6 <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
  7 <title>Chat</title>
  8 <link rel="stylesheet" type="text/css" href="/static/liaotian/chat.css" />
  9 <script src="/static/liaotian/js/jquery.min.js"></script>
 10 <script src="/static/liaotian/js/flexible.js"></script>
 11 </head>
 12 <body>
 13     <header class="header">
 14         <a class="back" href="javascript:history.back()"></a>
 15         <h5 class="tit">在线聊天</h5>
 16         <a href=""><div class="right">退出</div></a>
 17     </header>
 18  
 19     <!-- 聊天内容 start-->
 20     <div class="message"> </div>
 21     <!-- 聊天内容 end-->
 22  
 23     <!-- 底部 start-->
 24     <div class="footer">
 25         <img id="setbtn" src="/static/liaotian/images/hua.png" alt="" />
 26         <img src="/static/liaotian/images/xiaolian.png" alt="" />
 27         <input type="text" id="msg" value="" maxlength="300">
 28         <p style="background: rgb(17, 79, 142);" id="sendBtn">发送</p>
 29     </div>
 30     <!-- 底部 end-->
 31 </body>
 32 </html>
 33 <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
 34 <script src="https://cdn.bootcss.com/layer/3.1.0/layer.js"></script>
 35 <script type="text/javascript">
 36 $(function () {
 37     var uid = 666;//当前用户id
 38     var token = 'abcdefg';//用户token
 39  
 40     //判断浏览器是否支持WebSocket
 41     var supportsWebSockets = 'WebSocket' in window || 'MozWebSocket' in window;
 42     if (supportsWebSockets) {
 43         //建立WebSocket连接(ip地址换成自己主机ip)
 44         var ws = new WebSocket("ws://127.0.0.1:9501?uid="+uid+"&token="+token);
 45         ws.onopen = function () {
 46             layer.msg('服务器连接成功',{shade:0.1,icon:1,time:600});
 47         };
 48         ws.onerror = function () {
 49             layer.msg('服务器连接失败',{shade:0.1,icon:2,time:600});
 50         };
 51         ws.onmessage = function (evt) {
 52             var data = $.parseJSON(evt.data);
 53             //错误提示
 54             if(data.status != 1){
 55                 layer.alert(data.message,{icon:2});
 56                 return;
 57             }
 58             //消息返回
 59             if (data.status==1 && data.data.message!='') {
 60                 var html = "";
 61                 if (data.data.uid == uid) {
 62                     html += "<div style='word-break:break-all' class=\"show\"><div class=\"time\">"+data.data.post_time+"</div><div class=\"msg\"><img src=\""+data.data.head_img+"\" alt=\"\" /><p><i clas=\"msg_input\"></i>"+data.data.message+"</p></div></div>";
 63                 }else{
 64                     html += "<div style='word-break:break-all' class=\"send\"><div class=\"time\">"+data.data.post_time+"</div><div class=\"msg\"><img src=\""+data.data.head_img+"\" alt=\"\" /><p><i clas=\"msg_input\"></i>"+data.data.message+"</p></div></div>";
 65                 }
 66             }
 67             $(".message").append(html);
 68             setTimeout(function () {
 69                 ($('.message').children("div:last-child")[0]).scrollIntoView();//向上滚动
 70             },100);
 71         };
 72         ws.onclose = function (res) {
 73             
 74         };
 75         //按钮发送
 76         $("#sendBtn").click(function () {
 77             var contents = $("#msg").val().trim();
 78             if(contents == null || contents == ""){
 79                 layer.msg('内容为空',{shade:0.1,icon:2,time:600});            
 80                 return false;
 81             }else{
 82                 ws.send(contents);
 83                 $("#msg").val("");
 84             }
 85         });
 86         //回车发送
 87         $("#msg").keydown(function (evel) {
 88             var that = $(this);
 89             if (evel.keyCode == 13) {
 90                 evel.cancelBubble = true;
 91                 evel.preventDefault();
 92                 evel.stopPropagation();
 93                 var contents = that.val().trim();
 94                 if(contents == null || contents == ""){
 95                     layer.msg('内容为空',{shade:0.1,icon:2,time:600});              
 96                     return false;
 97                 }else{
 98                     ws.send(contents);
 99                     that.val("");
100                 }
101             }
102         });
103     }else{
104         layer.alert("您的浏览器不支持 WebSocket!");
105     }
106 });
107 </script>
108
复制代码

 

服务器移到项目根目录开启服务:

php public/index.php Websocket/start

 

这里的路径,是因为我绑定了home模块为默认模块,tp5默认情况是:php public/index.php index/Websocket/start)

开启成功,查看端口已经被监听:

lsof -i:9501

 

thinkphp 6.0 swoole扩展websocket使用教程-ThinkPHP-PHP中文网

mikel阅读(634)

来源: thinkphp 6.0 swoole扩展websocket使用教程-ThinkPHP-PHP中文网

下面由thinkphp框架教程栏目给大家介绍thinkphp 6.0 swoole扩展websocket使用教程(think-swoole),希望对需要的朋友有所帮助!
thinkphp 6.0 swoole扩展websocket使用教程(think-swoole)

前言
最新版本的TP-SWOOLE变化很大,该文所提供的方法已经无法使用,可以参考 https://github.com/xavieryang007/think-swoole-demo/blob/master/doc/%E6%96%87%E6%A1%A3/4.1-websocket.md

介绍
即将推出的tp6.0,已经适配swoole.并推出think-swoole 3.0,并且默认适配了socketio。和2.0版本在使用方法上面有些许不同。

Websocket 继承与Http,进行websocket连接之前需要一次HTTP请求,如果当期地址支持websocket则返回101,然后进行连接。也就是说并不是我的服务支持websocket后,请求每个连接地址都可以进行websocket连接,而是需要预先适配才可以连接。

参数配置
如果要使用websocket需要在配置中启用,将websocket下的enable设置为true

‘server’           => [

‘host’      => ‘0.0.0.0’, // 监听地址

‘port’      => 808, // 监听端口

‘mode’      => SWOOLE_PROCESS, // 运行模式 默认为SWOOLE_PROCESS

‘sock_type’ => SWOOLE_SOCK_TCP, // sock type 默认为SWOOLE_SOCK_TCP

‘options’   => [

‘pid_file’              => runtime_path() . ‘swoole.pid’,

‘log_file’              => runtime_path() . ‘swoole.log’,

‘daemonize’             => false,

// Normally this value should be 1~4 times larger according to your cpu cores.

‘reactor_num’           => swoole_cpu_num(),

‘worker_num’            => swoole_cpu_num(),

‘task_worker_num’       => 4,//swoole_cpu_num(),

‘enable_static_handler’ => true,

‘document_root’         => root_path(‘public’),

‘package_max_length’    => 20 * 1024 * 1024,

‘buffer_output_size’    => 10 * 1024 * 1024,

‘socket_buffer_size’    => 128 * 1024 * 1024,

‘max_request’           => 3000,

‘send_yield’            => true,

],

],

‘websocket’        => [

‘enabled’       => true,// 开启websocket

‘handler’       => Handler::class,  //自定义wbesocket绑定类

‘parser’        => Parser::class, //自定义解析类

‘route_file’    => base_path() . ‘websocket.php’,

‘ping_interval’ => 25000,

‘ping_timeout’  => 60000,

‘room’          => [

‘type’        => TableRoom::class,

‘room_rows’   => 4096,

‘room_size’   => 2048,

‘client_rows’ => 8192,

‘client_size’ => 2048,

],

],

‘auto_reload’      => true,

‘enable_coroutine’ => true,

‘resetters’        => [],

‘tables’           => [],

handler和parser大大方便了自定义websocket服务,默认系统集成socketio。

本文主要介绍如何使用socketio,这里假设大家有socketio有一定了解和使用基础。

socketIo默认会在请求地址后加相应的参数

同时,socketio默认情况下,会认为 http://url/socket.io/ 是支持websocket服务的地址。

而在tp-swoole3.0内部已经对该地址请求进行了处理

namespace think\swoole\websocket\socketio;

use think\Config;

use think\Cookie;

use think\Request;

class Controller

{

protected $transports = [‘polling’, ‘websocket’];

public function upgrade(Request $request, Config $config, Cookie $cookie)

{

if (!in_array($request->param(‘transport’), $this->transports)) {

return json(

[

‘code’    => 0,

‘message’ => ‘Transport unknown’,

],

400

);

}

if ($request->has(‘sid’)) {

$response = response(‘1:6’);

} else {

$sid     = base64_encode(uniqid());

$payload = json_encode(

[

‘sid’          => $sid,

‘upgrades’     => [‘websocket’],

‘pingInterval’ => $config->get(‘swoole.websocket.ping_interval’),

‘pingTimeout’  => $config->get(‘swoole.websocket.ping_timeout’),

]

);

$cookie->set(‘io’, $sid);

$response = response(’97:0′ . $payload . ‘2:40’);

}

return $response->contentType(‘text/plain’);

}

public function reject(Request $request)

{

return json(

[

‘code’    => 3,

‘message’ => ‘Bad request’,

],

400

);

}

}

TP6.0,插件注册采用了service方式进行了注册,可在tp-swoole 服务注册文件中查看路由注册信息,如果想自定义链接规则,则可以覆盖该路由。

// +———————————————————————-

// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]

// +———————————————————————-

// | Copyright (c) 2006-2018 http://thinkphp.cn All rights reserved.

// +———————————————————————-

// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )

// +———————————————————————-

// | Author: yunwuxin <448901948@qq.com>

// +———————————————————————-

namespace think\swoole;

use Swoole\Http\Server as HttpServer;

use Swoole\Websocket\Server as WebsocketServer;

use think\App;

use think\Route;

use think\swoole\command\Server as ServerCommand;

use think\swoole\facade\Server;

use think\swoole\websocket\socketio\Controller;

use think\swoole\websocket\socketio\Middleware;

class Service extends \think\Service

{

protected $isWebsocket = false;

/**

* @var HttpServer | WebsocketServer

*/

protected static $server;

public function register()

{

$this->isWebsocket = $this->app->config->get(‘swoole.websocket.enabled’, false);

$this->app->bind(Server::class, function () {

if (is_null(static::$server)) {

$this->createSwooleServer();

}

return static::$server;

});

$this->app->bind(‘swoole.server’, Server::class);

$this->app->bind(Swoole::class, function (App $app) {

return new Swoole($app);

});

$this->app->bind(‘swoole’, Swoole::class);

}

public function boot(Route $route)

{

$this->commands(ServerCommand::class);

if ($this->isWebsocket) {

$route->group(function () use ($route) {

$route->get(‘socket.io/’, ‘@upgrade’);

$route->post(‘socket.io/’, ‘@reject’);

})->prefix(Controller::class)->middleware(Middleware::class);

}

}

/**

* Create swoole server.

*/

protected function createSwooleServer()

{

$server     = $this->isWebsocket ? WebsocketServer::class : HttpServer::class;

$config     = $this->app->config;

$host       = $config->get(‘swoole.server.host’);

$port       = $config->get(‘swoole.server.port’);

$socketType = $config->get(‘swoole.server.socket_type’, SWOOLE_SOCK_TCP);

$mode       = $config->get(‘swoole.server.mode’, SWOOLE_PROCESS);

static::$server = new $server($host, $port, $mode, $socketType);

$options = $config->get(‘swoole.server.options’);

static::$server->set($options);

}

}

Socketio默认使用demo

Websocket路由配置方法
在app目录下新建websocket.php文件,其中需要注意,由于使用了反射,闭包参数名称不能随意定义,不然无法注入。第一个参数是websocket,是当前websocket的Server对象,第二个参数data是客户端发送的数据。其中socketio emit的第一个参数和Websocket::on的第一个参数一致,作为事件名称。

/**

* Author:Xavier Yang

* Date:2019/6/5

* Email:499873958@qq.com

*/

use \think\swoole\facade\Websocket;

Websocket::on(“test”, function (\think\swoole\Websocket $websocket, $data) {

//var_dump($class);

$websocket->emit(“test”, “asd”);

});

Websocket::on(“test1”, function ($websocket, $data) {

$websocket->emit(“test”, “asd”);

});

Websocket::on(“join”, function (\think\swoole\Websocket $websocket, $data) {

$websocket->join(“1”);

});

参考如上方法即可使用全新的websocket服务。当然tp-swoole3.0同样还有许多其他的新功能,这些功能需要大家去摸索尝试。
我也会在接下来的文章中,一起与大家分享我的使用过程。

以上就是thinkphp 6.0 swoole扩展websocket的使用的详细内容,更多请关注php中文网其它相关文章!

ThinkPHP5.1 安装 workerman 指导 - 云+社区 - 腾讯云

mikel阅读(616)

来源: ThinkPHP5.1 安装 workerman 指导 – 云+社区 – 腾讯云

★ 背景

  • 最近需要在自己的网站后台,设计消息推送业务,通过网上资料参考,觉得 workerman 是个很好的选择,在此记录一下ThinkPHP5.1 框架下的安装操作,希望可以帮到更多的人 …

workerman 手册

  • 环境
框架版本:ThinkPHP5.1.2
使用工具:composer
系统环境:Win10、CentOS7.2

☆ 指导步骤

首先,因为配置条件多有不同,需要根据自己的情况稍作调整的哦 (仅作为友情提示…)

①. 进入项目根目录,运行 composer 命令

  • 安装命令如下:
composer require topthink/think-worker
  • 此时,会有提示信息:
Using version ^3.0 for topthink/think-worker
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - topthink/think-worker v3.0.3 requires topthink/framework ^6.0.0 -> satisfiable by topthink/framework[6.0.x-dev, v6.0.0-rc2, v6.0.0-rc3, v6.0.0-rc4] but these conflict with your requirements or minimum-stability.
    - topthink/think-worker v3.0.2 requires topthink/framework ^6.0.0 -> satisfiable by topthink/framework[6.0.x-dev, v6.0.0-rc2, v6.0.0-rc3, v6.0.0-rc4] but these conflict with your requirements or minimum-stability.
    - topthink/think-worker v3.0.1 requires topthink/framework ^6.0.0 -> satisfiable by topthink/framework[6.0.x-dev, v6.0.0-rc2, v6.0.0-rc3, v6.0.0-rc4] but these conflict with your requirements or minimum-stability.
    - topthink/think-worker v3.0.0 requires topthink/framework ^6.0.0 -> satisfiable by topthink/framework[6.0.x-dev, v6.0.0-rc2, v6.0.0-rc3, v6.0.0-rc4] but these conflict with your requirements or minimum-stability.
    - Installation request for topthink/think-worker ^3.0 -> satisfiable by topthink/think-worker[v3.0.0, v3.0.1, v3.0.2, v3.0.3].


Installation failed, reverting ./composer.json to its original content.
  • 解释: 这是因为,在不填写所需扩展的版本号前提下,会默认下载最新版本 但是,当前的框架版本很可能不支持,比如,此时要求必须要是 ThinkPHP6.0 框架 而鄙人的版本是 ThinkPHP5.1.2
  • 解决方案:就是,直接指定所需要的扩展包版本

当然,如果你的 ThinkPHP 版本足够高,此时应该会直接顺利的安装,后面的就无需多看咯

②. 获取可选择的 workerman 版本

为了指定安装的版本,所以我们要知道,有哪些可选择的版本

  • composer.json 文件中的 "require" 数组中,先补充一条数据:"topthink/think-worker":"0.1"
  • 此时,命令行执行:composer update
  • 根据自己的 ThinkPHP 版本选择合适的扩展
> 以我为例,试着选择了 "v3.0.0" (依然版本要求太高)、 "v2.0.9" (当前较合适)
> 然后进行安装操作
> 此时可以有两种方式,个人建议选用后者
> 
> 第一种方式:
> 直接修改前面所提及的 "composer.json" 文件中的版本号:"topthink/think-worker":"v2.0.9"> 然后,再次执行一遍命令:composer update
> 
> 第二种方式:
> 删掉前面 "composer.json" 文件中 补充测试的内容:"topthink/think-worker":"0.1"
> 然后,执行命令:composer require topthink/think-worker=v2.0.9
  • 执行的打印信息,如下:
  • 【安装完成】 安装成功后,你可以发现会多出 "/vendor/topthink/think-worker""/vendor/workerman" 目录 并且,在目录 "/config" 中,会自动生成 workerman 对应的三个配置文件

附录

参考文章

  • Linux 系统检测 Linux系统可以使用以下脚本测试本机 PHP 环境是否满足 WorkerMan 运行要求
curl -Ss http://www.workerman.net/check.php | php

上面脚本如果全部显示ok,则代表满足 WorkerMan 要求

composer安装tp6引入各种包问题解决笔记! - 简书

mikel阅读(1114)

来源: composer安装tp6引入各种包问题解决笔记! – 简书

执行composer require topthink/framework,报错,原因proc_open函数被禁用

  [Symfony\Component\Process\Exception\RuntimeException]                                   
  The Process class relies on proc_open, which is not available on your PHP installation.   
解决方法:

打开php.ini,搜索disable_functions,找到proc_open并删除即可。

image.png

 

执行composer require topthink/think-image报错原因:
pcntl_signal函数被禁用,同上删除该禁用,重启PHP

  [ErrorException]                                       
  pcntl_signal() has been disabled for security reasons   
执行composer require topthink/think-worker 报错:
解决方法:安装fileinfo扩展


./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - topthink/think-worker v3.0.4 requires ext-fileinfo * -> the requested PHP extension fileinfo is missing from your system.
    - topthink/think-worker v3.0.3 requires ext-fileinfo * -> the requested PHP extension fileinfo is missing from your system.
    - topthink/think-worker v3.0.2 requires ext-fileinfo * -> the requested PHP extension fileinfo is missing from your system.
    - topthink/think-worker v3.0.1 requires ext-fileinfo * -> the requested PHP extension fileinfo is missing from your system.
    - topthink/think-worker v3.0.0 requires ext-fileinfo * -> the requested PHP extension fileinfo is missing from your system.
    - Installation request for topthink/think-worker ^3.0 -> satisfiable by topthink/think-worker[v3.0.0, v3.0.1, v3.0.2, v3.0.3, v3.0.4].

  To enable extensions, verify that they are enabled in your .ini files:
    - /www/server/php/73/etc/php.ini
  You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.

Installation failed, reverting ./composer.json to its original content.


image.png

作者:DragonersLi
链接:https://www.jianshu.com/p/17fa22374667
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

thinkphp5下使用WebSocket_gzmyh的博客-CSDN博客_thinkphp websocket

mikel阅读(747)

来源: thinkphp5下使用WebSocket_gzmyh的博客-CSDN博客_thinkphp websocket

最近有个项目需求是这样的:软件端会向服务器发送请求,获取一些信息。然后对获取过来的信息进行处理,再返回信息的id

方式一:软件端那边调用接口,一分钟请求一次。

问题出现:如果有几万个人同时用软件,就会产生大量的http请求,导致负载过高。因此需求考虑到第二种方式。

方式二:使用WebSocket,进行一次长连接,这样就减少了大量的请求。

因为对于webscoket要求不高,因此就选择了Workerman,这也是thinkphp5封装好的扩展,直接下载下来用。

参考手册:thinkphp5.1和workerman开发手册

下面开始:

使用composer下载:

composer require topthink/think-worker
下载下来后会有一些文件,重点看config里面的worker.php和worker_server.php。这两个是不同的监听配置,我使用的是worker_server.php

它的默认配置是这样的

return [
// 扩展自身需要的配置
‘protocol’ => ‘websocket’, // 协议 支持 tcp udp unix http websocket text
‘host’ => ‘0.0.0.0’, // 监听地址
‘port’ => 2346, // 监听端口
‘socket’ => ‘hd.com:2346’, // 完整监听地址
‘context’ => [], // socket 上下文选项
‘worker_class’ => ”, // 自定义Workerman服务类名 支持数组定义多个服务

// 支持workerman的所有配置参数
‘name’ => ‘thinkphp’,
‘count’ => 4,
‘daemonize’ => false,
‘pidFile’ => Env::get(‘runtime_path’) . ‘worker.pid’,

// 支持事件回调
// onWorkerStart
‘onWorkerStart’ => function ($worker) {

},
// onWorkerReload
‘onWorkerReload’ => function ($worker) {

},
// onConnect
‘onConnect’ => function ($connection) {

},
// onMessage
‘onMessage’ => function ($connection, $data) {
$connection->send(‘你成功了’);
},
// onClose
‘onClose’ => function ($connection) {

},
// onError
‘onError’ => function ($connection, $code, $msg) {
echo “error [ $code ] $msg\n”;
},
];
这时你打开命令行(cmd),切换到你项目根目录下

E:\wamp64\www\hd\v2018>E:\wamp64\bin\php\php7.0.4\php.exe think worker:server

因为我没有配置php的环境变量,所有就时候这种方式了。这时会看到这样的结果说明服务启动成功

 

打开chrome浏览器,按F12打开调试控制台,在Console一栏输入(或者把下面代码放入到html页面用js运行)

// 假设服务端ip为127.0.0.1
ws = new WebSocket(“ws://127.0.0.1:2000”);
ws.onopen = function() {
alert(“连接成功”);
};
ws.onmessage = function(e) {
alert(“收到服务端的消息:” + e.data);
};
这是你会收到“你成功了”的字样。

接下来就是真正的开发流程了。

一般情况下我们会创建一个服务类(必须要继承 think\worker\Server),然后设置属性和添加回调方法,这是我写的一个类

<?php
// +———————————————————————-
// | websocket
// +———————————————————————-
// | Author: myh
// +———————————————————————-

namespace app\worker;

use think\worker\Server;
use app\common\api\BaseApi;

class Worker extends Server
{
protected $socket = ‘http://0.0.0.0:2346’;

public function onMessage($connection,$data)
{
$data = explode(‘|’, $data);
switch ($data[0]) {
case ‘software_send_msg’:
if(!isset($data[1]) || empty($data[1])) {
$list = json_encode([‘result’=>’error’,’message’=>’参数为空’]);
}else{
$account = $data[1];
$uid = BaseApi::findData(8,2,[[‘user_login’,’=’,$account]],’id’);

if($uid){
list($data) = BaseApi::getListOne(16,[[‘user_id’,’=’,$uid],[‘status’,’=’,0]]);//16-software_send_msg
if($data){
$list = [];
foreach ($data as $k => $v) {
$list[$k][‘id’] = $v[‘id’];
$list[$k][‘type’] = $v[‘type’];
$list[$k][‘msg’] = $v[‘msg’];
}

$list = json_encode([‘result’=>’success’,’data’=>json_encode($list)]);
}else{
$list = json_encode([‘result’=>’error’, ‘message’=>’数据为空’]);
}

}else{
$list = json_encode([‘result’=>’error’, ‘message’=>’找不到该账号信息’]);
}
}

break;

case ‘software_send_msg2’:
$msg = 3333;
break;

default:
$list = json_encode([‘result’=>’error’]);
break;
}

$connection->send($list);//这里把数据发送给客户端

}

}
这时要对worker_server.php进行修改,删掉之前的配置,只要加上这个即可

return [
‘worker_class’ => ‘app\worker\Worker’,
];
控制器:

public function ws()
{
return $this->fetch(‘:ws’);
}
模板:

<!DOCTYPE html>
<html xmlns:v-on=”http://www.w3.org/1999/xhtml”>
<head>
<meta charset=”utf-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no”>
<title>ThinkCMF WebSocket Demo</title>

<script src=”__STATIC__/js/JQuery-2.0.3.min.js”></script>
</head>
<body>

<div id=”app”>
软件模拟获取最新消息:
<button class=”get-msg”>请求</button>

<div class=”return-msg”>

</div>
</div>

<script>

$(document).on(‘click’,’.get-msg’,function(){
ws = new WebSocket(“ws://127.0.0.1:2346”);

//组织参数
var param = new Array();
param.push(‘software_send_msg’);
param.push(‘wydyhdzh’);

ws.onopen = function() {
ws.send(param.join(‘|’));//向服务器返送信息
};

ws.onmessage = function(e) {//服务器返回信息
var data = JSON.parse(e.data);

if(data.result == ‘success’){
var list = JSON.parse(data.data);
$(‘.return-msg’).html(data.data);
}
};
})

</script>
</body>
</html>

 

整个流程就是这样,不明白可以留言
————————————————
版权声明:本文为CSDN博主「gzmyh」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gzmyh/article/details/89874357

ThinkPHP6 Workerman 基本使用-ThinkPHP-PHP中文网

mikel阅读(603)

来源: ThinkPHP6 Workerman 基本使用-ThinkPHP-PHP中文网

Workerman
Workerman是一款纯PHP开发的开源高性能的PHP socket 服务器框架。被广泛的用于手机app、手游服务端、网络游戏服务器、聊天室服务器、硬件通讯服务器、智能家居、车联网、物联网等领域的开发。 支持TCP长连接,支持Websocket、HTTP等协议,支持自定义协议。基于workerman开发者可以更专注于业务逻辑开发,不必再为PHP Socket底层开发而烦恼。

安装

首先通过 composer 安装

composer require topthink/think-worker

使用

使用Workerman作为HttpServer

在命令行启动服务端

php think worker

然后就可以通过浏览器直接访问当前应用

http://localhost:2346

linux下面可以支持下面指令

php think worker [start|stop|reload|restart|status]

workerman的参数可以在应用配置目录下的worker.php里面配置。

由于onWorkerStart运行的时候没有HTTP_HOST,因此最好在应用配置文件中设置app_host

SocketServer

在命令行启动服务端(需要2.0.5+版本)

php think worker:server

默认会在0.0.0.0:2345开启一个websocket服务。

如果需要自定义参数,可以在config/worker_server.php中进行配置,包括:

配置参数 描述
protocol 协议
host 监听地址
port 监听端口
socket 完整的socket地址
并且支持workerman所有的参数(包括全局静态参数)。

也支持使用闭包方式定义相关事件回调。

return [

‘socket’    =>  ‘http://127.0.0.1:8000’,

‘name’      =>  ‘thinkphp’,

‘count’     =>  4,

‘onMessage’ =>  function($connection, $data) {

$connection->send(json_encode($data));

},

];

也支持使用自定义类作为Worker服务入口文件类。例如,我们可以创建一个服务类(必须要继承 think\worker\Server),然后设置属性和添加回调方法

namespace app\http;

use think\worker\Server;

class Worker extends Server

{

protected $socket = ‘http://0.0.0.0:2346’;

public function onMessage($connection,$data)

{

$connection->send(json_encode($data));

}

}

支持workerman所有的回调方法定义(回调方法必须是public类型)

然后在worker_server.php中增加配置参数:

return [

‘worker_class’  =>  ‘app\http\Worker’,

];

定义该参数后,其它配置参数均不再有效。

在命令行启动服务端

php think worker:server

然后在浏览器里面访问

http://localhost:2346

如果在Linux下面,同样支持reload|restart|stop|status 操作

php think worker:server reload

推荐教程:《ThinkPHP》《PHP教程》《Workerman教程》

以上就是ThinkPHP6 Workerman 基本使用的详细内容,更多请关注php中文网其它相关文章!

Workerman wss 宝塔 Apache2.4 配置 - 技术小站

mikel阅读(656)

来源: Workerman wss 宝塔 Apache2.4 配置 – 技术小站

我测试的是 https://www.workerman.net/workerman-chat 项目。

Workerman 文档:http://doc.workerman.net/315297

    # Proxy Config
    SSLProxyEngine on

    ProxyRequests Off
    ProxyPass /wss ws://127.0.0.1:7272/wss
    ProxyPassReverse /wss ws://127.0.0.1:7272/wss
// 证书是会检查域名的,请使用域名连接
ws = new WebSocket("wss://guodapeng.icu/wss");

ws.onopen = function() {
    alert("连接成功");
    ws.send('tom');
    alert("给服务端发送一个字符串:tom");
};
ws.onmessage = function(e) {
    alert("收到服务端的消息:" + e.data);
};

浏览器跑一下这个js,连接成功了。