thinkPHP3.2 、thinkPHP5 結合workerman + Redis 實現賬號單點登錄

  • thinkPHP3.2  與workerman 結合
  1. 首先下載wokerman,放到TP項目下面的ThinkPHP/Library目錄,並命名爲Workerman
  2. 除Constants.php以外的所有php文件改爲.class.php後綴
  3. 代碼塊

        Js代碼


        var uid = sessionStorage.getItem('userid'); // 登錄平臺用戶id, 登錄時存入
            
        var ws = new WebSocket("ws://127.0.0.1:2346");  // 連接socket

        // 初始化連接
        ws.onopen = function() {
            console.log("連接成功"); 
            if(uid != null){
                ws.send(userid);  // 發送數據到後端
            }
           
        };

        // 返回信息
        ws.onmessage = function(evt){
            var res = evt.data;
           
            if(JSON.parse(res).code == 1001){
                layer.msg(JSON.parse(res).msg,{icon:6,time:2000},function(){
                    sessionStorage.removeItem('userid');  // 清楚瀏覽器userid
                    window.location.href = 'url'; // 退出接口
                });
            }
        }

登錄成功後

ws.send(“用戶id”);

 PHP代碼塊: 新建一個WorkerController.class.php 控制器

<?php
namespace Cli\Controller;

use Think\Controller;

use Workerman\Worker;
use Workerman\Lib\Timer;

use Redis;

// 心跳間隔時間
define('HEARTBEAT_TIME', 3600); 

class WorkermanController
{
    protected $redis = '';
    protected $data = '';
    protected $msg = ['code' => 0, 'msg' => '您的賬號已在別處登錄'];
    /**
     * 構造函數
     * @access public
     */
    public function __construct()
    {
        // 實例化 Websocket 服務
        $this->worker = new \Workerman\Worker('websocket://0.0.0.0:2346');

        $this->worker->count = 4;// 設置進程數
        $this->init();//初始化
        // 設置回調
        foreach (['onWorkerStart', 'onConnect', 'onMessage', 'onClose', 'onError', 'onBufferFull', 'onBufferDrain', 'onWorkerStop', 'onWorkerReload'] as $event) {
            if (method_exists($this, $event)) {
                $this->worker->$event = [$this, $event];
            }
        }
        // Run worker
        Worker::runAll();
    }
 
    /**
     * 收到信息
     * @param $connection
     * @param $data
     */
    public function onMessage($connection, $data)
    {
        $this->data = $data;
        //登錄連接時分配一個全局唯一的uid
        $connection->uid = uniqid('xxx_');
        //追入redis中
        $this->redis->hSet('key',$connection->uid, $data);
        $expireTime = mktime(23, 59, 59, date("m"), date("d"), date("Y"));
        //設置鍵的過期時間
        $this ->redis->expireAt('hxt', $expireTime);
    }
 
    /**
     * 當連接建立時觸發的回調函數
     * @param $connection
     */
    public function onConnect($connection)
    {
        // redis 不存在則實例化Redis對象, 並連接
        if($this->redis == ''){
            $this->redis = new Redis();
            $this->redis->connect('127.0.0.1',6379);
        }
    }
 
    /**
     * 當連接斷開時觸發的回調函數

     * @param $connection
     */
    public function onClose($connection)
    {
        if(isset($connection ->uid)){
            //找出userid 對應key
            $cid = $connection ->uid;
            //清除redis中對應id
             $this ->redis ->hDel('key',$cid);
             
        }
    }
 
    /**
     * 當客戶端的連接上發生錯誤時觸發
     * @param $connection
     * @param $code
     * @param $msg
     */
    public function onError($connection, $code, $msg)
    {
        echo "error $code $msg\n";
    }
 
    /**
     * 每個進程啓動
     * @param $worker
     */
    public function onWorkerStart($worker)
    {
        //var_dump($worker->connections);
        Timer::add(1, function()use($worker){
            $time_now = time();
           
            if($this->redis == ''){
                $this->redis = new Redis();
                $this->redis->connect('127.0.0.1',6379);
            }

            //取出redis中所有連接客戶端key
            $arr = $this->redis->hGetAll('key');
            //查詢當前登錄id是否在redis中
            $true = array_search($this->data,$arr);

            $sum=$this->get_array_repeats($tarr,$this->data);

            foreach($worker->connections as $connection) {
                if(isset($connection ->uid) && $connection->uid == $true && $sum>=2){
                    $connection->send(json_encode($this->msg)); // 發送給客戶端
                    $connection->close();
                }
                

                $jicheng = $connection;
                // 有可能該connection還沒收到過消息,則lastMessageTime設置爲當前時間
             
                if (empty($jicheng->lastMessageTime)) {
                    $jicheng->lastMessageTime = $time_now;
                    continue;
                }
                // 上次通訊時間間隔大於心跳間隔,則認爲客戶端已經下線,關閉連接
                if ($time_now - $jicheng->lastMessageTime > HEARTBEAT_TIME) {
                // $connection->close();
                    echo "下線";
                }
            }
        });
    }

    //計算$string在$array(需爲數組)中重複出現的次數
    public function get_array_repeats(array $array,$string)
    {
    
        $count = array_count_values($array);
        //統計中重複元素的次數,再重組數組, 
       
        if (key_exists($string,$count)) {
            return $count[$string];
        }else{
            return 0;
        }
    }
 
    public function init(){
 
    }
}

 

  1. 開啓Redis服務  

        

  1. 開啓worker 服務

        

      到這裏就實現了 thinkPHP3.2 + Redis + workerman 就完成了賬號單點登錄

 

2. thinkPHP5 與 Redis + workerman 實現  

workerman 的安裝:thinkPHP5可以直接用composer 安裝workerman  擴展,具體方法請自行參考thinkPHP5官方文檔

js與上面的相同,自行參考

 

PHP代碼部分:

 

 

一、在項目跟目錄新建一個server.php 文件 ,代碼如下:

<?php
define('APP_PATH', __DIR__ . '/application/');
define('BIND_MODULE','push/Worker');
// 加載框架引導文件
require __DIR__ . '/thinkphp/start.php';

二、建立push模塊並建立worker.php 文件, 代碼如下:

<?php 
namespace app\push\controller;
 
use think\worker\Server;
use workerman\Workerman;
use Redis;

class Worker extends Server
{

   // 代碼塊跟tp3.2 worker文件代碼相同,不做處理


}

最後同上:開啓Redis , 在跟目錄開啓server服務

php server.php

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章