Laravel 5.4 结合 Workerman 实现 TCP 长连接
发布时间:2019-01-02, 17:49:03 分类:PHP | 编辑 off 网址 | 辅助
正文 5348字数 2,430,493阅读
一、安装 workerman
在项目根目录执行
在项目根目录执行
composer require workerman/workerman
Run code
Cut to clipboard
二、创建自定义 artisan 命令来启动 workerman 服务
由于 laravel 不能直接在根目录下执行 php 命令,所以需要创建 artisan 命令用于后面 workerman 服务的开启。
1,生成 WorkermanCommand 文件
执行以上命令行会在 app/Console/Commands/ 目录下生成 WorkermanCommand.php 文件。
我只实现了 start 命令,其他命令童鞋们自行实现吧。
这里使用了 PHP 类方法的回调。(PHP几种回调写法)
这里我们创建了一个自定义命令 wk [action] ,通过此命令即可开启 workerman 服务。
由于 laravel 不能直接在根目录下执行 php 命令,所以需要创建 artisan 命令用于后面 workerman 服务的开启。
1,生成 WorkermanCommand 文件
php artisan make:command WorkermanCommand
Run code
Cut to clipboard
执行以上命令行会在 app/Console/Commands/ 目录下生成 WorkermanCommand.php 文件。
<?php
namespace App\Console\Commands;
use Workerman\Worker;
use Illuminate\Console\Command;
class WorkermanCommand extends Command
{
private $server;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'wk {action}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Start a Workerman server.';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
global $argv;
$arg = $this->argument('action');
$argv[1] = $argv[2];
$argv[2] = isset($argv[3]) ? "-{$argv[3]}" : '';
switch ($arg) {
case 'start':
$this->start();
break;
case 'stop':
break;
case 'restart':
break;
case 'reload':
break;
case 'status':
break;
case 'connections':
break;
}
}
private function start()
{
// 创建一个Worker监听20002端口,不使用任何应用层协议
$this->server = new Worker("tcp://0.0.0.0:20002");
// 启动4个进程对外提供服务
$this->server->count = 4;
$handler = \App::make('handlers\WorkermanHandler');
// 连接时回调
$this->server->onConnect = [$handler, 'onConnect'];
// 收到客户端信息时回调
$this->server->onMessage = [$handler, 'onMessage'];
// 进程启动后的回调
$this->server->onWorkerStart = [$handler, 'onWorkerStart'];
// 断开时触发的回调
$this->server->onClose = [$handler, 'onClose'];
// 运行worker
Worker::runAll();
}
}
Run code
Cut to clipboard
我只实现了 start 命令,其他命令童鞋们自行实现吧。
这里使用了 PHP 类方法的回调。(PHP几种回调写法)
这里我们创建了一个自定义命令 wk [action] ,通过此命令即可开启 workerman 服务。
注册命令
https://laravel-china.org/docs/laravel/5.4/artisan/1245#7ed55e
命令编写完成后,需要注册 Artisan 后才能使用。注册文件为 app/Console/Kernel.php 。在这个文件中, 你会在 commands 属性中看到一个命令列表。要注册你的命令,只需将其加到该列表中即可。当 Artisan 启动时,所有罗列在这个 属性的命令,都会被 服务容器 解析并向 Artisan 注册:
在这个自定义命令还引用了其他的类文件,如:
所以,需要创建一个 WorkermanHandler.php 的文件来处理对应的操作。
2,创建 WorkermanHandler.php
创建文件 app/handlers/WorkermanHandler.php
3,修改 composer.json 文件,让 app/handlers 文件夹下的类文件自动加载。
运行命令类文件自动加载:
至此。workman的命令定义已经完成。
使用:
如果看到以下内容,说明 workerman 服务启动正常:
https://www.jianshu.com/p/00623acb3dad
https://laravel-china.org/docs/laravel/5.4/artisan/1245#7ed55e
命令编写完成后,需要注册 Artisan 后才能使用。注册文件为 app/Console/Kernel.php 。在这个文件中, 你会在 commands 属性中看到一个命令列表。要注册你的命令,只需将其加到该列表中即可。当 Artisan 启动时,所有罗列在这个 属性的命令,都会被 服务容器 解析并向 Artisan 注册:
protected $commands = [
//
Commands\WorkermanCommand::class
];
Run code
Cut to clipboard
在这个自定义命令还引用了其他的类文件,如:
$handler = \App::make('handlers\WorkermanHandler');
Run code
Cut to clipboard
所以,需要创建一个 WorkermanHandler.php 的文件来处理对应的操作。
2,创建 WorkermanHandler.php
创建文件 app/handlers/WorkermanHandler.php
<?php
namespace handlers;
use Workerman\Lib\Timer;
// 心跳间隔10秒
define('HEARTBEAT_TIME', 10);
class WorkermanHandler
{
// 处理客户端连接
public function onConnect($connection)
{
echo "new connection from ip " . $connection->getRemoteIp() . "\n";
}
// 处理客户端消息
public function onMessage($connection, $data)
{
// 向客户端发送hello $data
$connection->send('Hello, your send message is: ' . $data);
}
// 处理客户端断开
public function onClose($connection)
{
echo "connection closed from ip {$connection->getRemoteIp()}\n";
}
public function onWorkerStart($worker)
{
Timer::add(1, function () use ($worker) {
$time_now = time();
foreach ($worker->connections as $connection) {
// 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
if (empty($connection->lastMessageTime)) {
$connection->lastMessageTime = $time_now;
continue;
}
// 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
echo "Client ip {$connection->getRemoteIp()} timeout!!!\n";
$connection->close();
}
}
});
}
}
Run code
Cut to clipboard
3,修改 composer.json 文件,让 app/handlers 文件夹下的类文件自动加载。
"autoload": {
"classmap": [
...
"app/handlers"
],
...
},
Run code
Cut to clipboard
运行命令类文件自动加载:
composer dump-autoload
Run code
Cut to clipboard
至此。workman的命令定义已经完成。
使用:
php artisan wk start
Run code
Cut to clipboard
如果看到以下内容,说明 workerman 服务启动正常:
Workerman[artisan] start in DEBUG mode
----------------------- WORKERMAN -----------------------------
Workerman version:3.5.4 PHP version:7.1.4
------------------------ WORKERS -------------------------------
user worker listen processes status
root none tcp://0.0.0.0:20002 1 [OK]
----------------------------------------------------------------
Press Ctrl+C to quit. Start success.
Run code
Cut to clipboard
https://www.jianshu.com/p/00623acb3dad
(支付宝)给作者钱财以资鼓励 (微信)→
有过 14 条评论 »
Remove the data-ride="carousel" attribute to fix JS error
Bootstrap Carousel: Uncaught TypeError: Cannot read property 'offsetWidth' of undefined
改动一下 // ListTouch计算方向 ListTouchMove(e) { this.setData({ ListTouchDirection: e.touches[0].pageX - this.data.ListTouchStart > 0 ? 'right' : 'left' }) }, 这段,改成例如 // ListTouch计算方向 ListTouchMove(e) { var offset = e.touches[0].pageX - this.data.ListTouchStart if (offset > 50) { this.setData({ ListTouchDirection: 'right' }) } else if (offset < -50) { this.setData({ ListTouchDirection: 'left' }) } } 提高触发的偏移值就可以了。
DB::connection()->enableQueryLog(); // 开启查询日志 DB::table('my_table')->insert($data); // 要查看的sql语句执行 $logs = DB::getQueryLog(); // 获取查询日志 dd($logs); // 即可查看执行的sql,传入的参数等等
Laravel 系列:orWhere 条件式
->where(function ($query) use ($keyword) { $query->where('name', 'like', "%{$keyword}%")->orWhere('barcode', 'like', "%{$keyword}%"); })
foreach()循环
要跳出本次循环继续执行下次循环 continue
终止循环
break。
<button open-type="contact" bindcontact="handleContact"></button> Page({ handleContact (e) { console.log(e.path) console.log(e.query) } })
在页面使用客服消息
需要将 button 组件 open-type 的值设置为 contact,当用户点击后就会进入客服会话,如果用户在会话中点击了小程序消息,则会返回到小程序,开发者可以通过 bindcontact 事件回调获取到用户所点消息的页面路径 path 和对应的参数 query。
path 小程序消息指定的路径
query 小程序消息指定的查询参数
修改 return $_GET["echostr"]
return回来的echostr是带双引号,而echo回来的echostr是不带双引号的。
#服务器socket连接端口9999,为了避免冲突,这里用9990反向代理到9999,同时实现了wss转ws,服务器端不需要做修改 server { listen 9990; server_name xx.xx.xx.xx; ssl on; ssl_certificate "/usr/cert/barrage.crt"; ssl_certificate_key "/usr/cert/barrage.key"; ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2; ssl_session_cache shared:SSL:1m; ssl_session_timeout 10m; ssl_prefer_server_ciphers on; location /{ #反向代理到9999端口,同时协议转换为http,这样服务器端代码就不需要做修改 proxy_pass http://120.77.222.242:9999; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; #由于服务器端源码(建议大家做好大小写匹配)只匹配了"Upgrade"字符串,所以如果这里填"upgrade"服务器端会将这条http请求当成普通的请求,导致websocket握手失败 proxy_set_header Connection "Upgrade"; proxy_set_header Remote_addr $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 600s; } }
killall -9 php php artisan workman start --d
killall -9 php ps aux|grep WorkerMan|awk '{print $2}'|xargs kill -9
php artisan workman start
//echo 'dfasfads'; //dump(count($mendian_info)); if(!count($mendian_info)){ $mendian_info=[]; $mendian_info['0']['mendian_info_num']=0; $mendian_info['0']['sttt']=$sttt; //$mendian_info->{0}=''; //$mendian_info->{0}->mendian_info_num=0;//$mendian_info_num; //$mendian_info->{0}->sttt=$sttt; }//Base::exitError('fail'); else{ //$mendian_info['0']->mendian_info_num=count($mendian_info);//$mendian_info_num; //$mendian_info['0']->sttt=$sttt; } //dump($mendian_info); Base::exitSuccess('success',$mendian_info);
在别的地方看到了答案,贴到这里让更多同学们学习一下吧 $a = new \stdClass(); $a->{0} = "test"; var_dump($a); //object(stdClass)#1 (1) { ["0"]=> string(4) "test" } echo $a->{0}; //test
obj[0]['字段名字']
$where[] = ['in'=>['tn_user_base.id'=>$medical_number_ids]];
$where[] = ['in'=>['tn_user_base.id'=>$medical_number_ids]];
//in查询应该用whereIn $condition[] =['check_doctor_uid','in',$check_doctor_id]; // 错误 // Illuminate\Database\Query\Builder关于operators定义中,并没有in public $operators = [ '=', '<', '>', '<=', '>=', '<>', '!=', 'like', 'like binary', 'not like', 'between', 'ilike', '&', '|', '^', '<<', '>>', 'rlike', 'regexp', 'not regexp', '~', '~*', '!~', '!~*', 'similar to', 'not similar to', 'not ilike', '~~*', '!~~*', ]; //->where($condition) 这种写法有问题