laravel 中workman的应用

由于自己写了一个博客,一直对workman有想法的我,突发奇想的想在博客里面弄个聊天室,因此就研究了一下workman,看了下workman的手册,然后了解了一下,workman,发现他是通过socket进行通讯,由于socket是tcp层的,因此可以进行长链接,接着双向通讯,而不像php的普通情况,通过http或者https进行通讯,因为http或者https每次通讯需要三次握手,四次挥手,而且服务器始终是处于被动状态,无法主动和客户端进行交流,因此若是客户端不进行轮询的情况下,几乎是做不到实时通讯。

而workman可以做到,workman是用PHP开发的,他就是用来调用socket来进行通讯的。此外还需要补充一点就是,workman他存在的一个缺陷,由于他本身是属于单线程的,如果你的服务器是多线程的,那workman是始终无法占满cpu的,所以对于多线程的服务器来说,workman的工作效率的偏低的,因此考虑到多线程的问题,设计workman的大佬们,又设计了一款workman的升级版,gateway,它是属于多线程的,如果用户量大的话,只要CPU处理的过来,他就完全能继续扛着,其实gateway的设计原理就是:你有几个线程,我就开几个work满。

废话不多说,接着开始说workman.

下载和安装workman就不说了,直接说如何使用吧。

由于workman的启动是需要命令框的,因此在laravel中,有专门用来生成自定义命令框的一个模块,commond,具体的话,可以去laravel中查看。

php artisan make:command  WorkermanCommand  (创建自己的 laravel Artisan Console);

接着就在handle()方法中写上,你的命令逻辑。


命令逻辑


自定义的命令逻辑

我自己定义的命令逻辑是 wk  {action}  :action 就是你要执行的方法。

例如,我需要执行start ,就可以写成: php artisan wk start

从handle()方法中看到,当action为start 的时候,调用chat_room()这个方法。

chat_room方法()

可以看到执行的逻辑就是:

1.先实例化workman.

2.开启1个进程,至于为什么不开启多个进程,那是因为,当开启多个进程以后,如果在不同进程中,用户和用户之间是不能通讯得,因此就只开一个进程就ok了,如果无法理解,就举个例子,现在有4个房间,然后一堆人站在房间前面,每一个人只能随机选择一个房间进入,当他们进入房间后,同一个房间的人能相互交流,不同房间的人只能干瞪眼,没法交流。(这就是为什么开一个进程的原因)

3.设置一个变量,用来存放用户信息。以便于对用户进行通讯发送消息。

4.当用户连接的时候,发送信息的时候,这时候获取到用户的信息,并且将他存放到定义好的变量中,然后将他发送的信息遍历给其他的每个一用户,做到信息的实时通讯。

5.当用户断开连接的时候,为了减少workman推送消息的资源,首先吧断开连接的用户给剔除了,然后广播通知其他用户,该用户已经退出聊天。

6.设置一个心跳,看了官方文档,主要作用就是为了防止长链接长时间不通讯导致被路由节点强制断开,说白了就是需要定期的和路由节点说一声:兄弟,我和他还活着,别我们T了。 他这边的心跳功能,就是一个定时器,客户端长时间不发信息给服务器,但是服务器又怕被断开,咋办呢,那服务器只能,定时的给客户端发个消息。这样就能保持不被断开了。心跳间隔建议值:建议客户端发送心跳间隔小于60秒,比如55秒。(这是官方建议,我设置了59秒)

这就是PHP这块的代码。

在cmd命令框执行:php artisan wk start              ---这样workman 就启动了。

下面贴一下前端的js代码。


html

这块是html.就是一个聊天框.


js代码

前端需要处理的很少,只需要发送信息和接受消息。

下面看一下,成果


这是模拟的两个用户的测试结果。

当然你也可以到www.youngxs.com/boke/boke_chat来测试玩一下。一个浏览器同时开两个就可以了。

有不懂的可以咨询我:601902897  欢迎来骚扰

下面就是贴下代码:

<?php

namespace App\Console\Commands;

use Workerman\Worker;

use \Workerman\Lib\Timer;

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;  //$argv 在ArgvInput类中被定义了,就是$_SERVER['argv'];

        $arg = $this->argument('action');

        $argv[1] = $argv[2];

        $argv[2] = isset($argv[3]) ? "-{$argv[3]}" : '';

        switch ($arg) {

            case 'start':

                $this->chat_room();

                break;

        }

}

    private function chat_room()

{

        //建立socket连接

        $http_worker = new Worker('websocket://0.0.0.0:2001');

        // 启动4个进程对外提供服务

        $http_worker->count = 1;

        //定义一个属性,用来存放用户的连接信息,$connection

        $http_worker->uidConnections = array();

        //socket事件类中的,链接属性,调用闭包函数

        $http_worker->onConnect = function($connection)use($http_worker)

{

            // 设置连接的onMessage回调

            $connection->onMessage = function($connection, $data)use($http_worker)

{

                //获取用户传输过来的数据,并且进行json_decode解析

                $data=json_decode($data,true);

                //获取用户ID接着将用户信息$connection,存储在$http_worker->uidConnections中,

                $http_worker->uidConnections[$data['id']]=$connection;

                //给其他用户发送,该用户发送的信息,为了保持数据的实时性。

                foreach ($http_worker->uidConnections as $k =>$v){

                    if($k != $data['id']){

                        if(isset($data['content'])){

                            $v->send(json_encode(['status'=>0,'msg'=>$data['content']]));

                        }

}

}

            };

            //设置用户断开连接的回调。

            $connection->onClose = function($connection)use($http_worker)

{

                foreach ($http_worker->uidConnections as $k=>$v){

                    if($v==$connection){

                        unset( $http_worker->uidConnections[$k]);

                    }else{

                        $v->send(json_encode(['status'=>0,'msg'=>$k.'下线了']));

                    }

}

            };

            /*设置心跳,为防止长链接断开            * 原理:就是设置一个定时器,定时的向客户端发送数据            * $time_interval 时间间隔* */

            $time_interval = 59;

            Timer::add($time_interval, function()use($http_worker)

{

                if(count($http_worker->uidConnections)>0){

                    foreach ($http_worker->uidConnections as $connection) {

                        $connection->send(json_encode(['status'=>1,'smg'=>'socket_hot']));

                    }

}

            });

        };

        Worker::runAll();

    }

}


下面的是JS代码

    var id='youngxs_name'+ Date.parse(new Date());

    $('.italic').html('游客:'+id)

var url='ws://127.0.01:2001'

    var ws =new WebSocket(url);

    ws.onopen =function() {

var data={'id':id}

data=JSON.stringify(data);

        ws.send(data);

        $('#chat_sure').click(function () {

var content=$('#chat_send').val();

            var str=id+'说:'+content;

            var data={'id':id,'content':str}

data=JSON.stringify(data);

            ws.send(data);

            $('#chat_room').append('<p>您说:'+content+'</p>');

            $('#chat_send').val('')

})

$("#chat_send").keypress(function (e) {

if (e.which ==13) {

var content=$('#chat_send').val();

                var str=id+'说:'+content;

                var data={'id':id,'content':str}

var $chatRoom =$('#chat_room')

data=JSON.stringify(data);

                ws.send(data);

                $('#chat_room').append('<p>您说:'+content+'</p>')

$('#chat_send').val('')

$chatRoom[0].scrollTop = $chatRoom[0].scrollHeight-$chatRoom[0].clientHeight;

            }

});

    };

    ws.onmessage =function(e) {

var msg=JSON.parse(e.data);

          if(msg.status==0){

$('#chat_room').append('<p>'+msg.msg+'</p>');

              var $chatRoom =$('#chat_room')

$chatRoom[0].scrollTop = $chatRoom[0].scrollHeight-$chatRoom[0].clientHeight;

          }

};

</script>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容