Laravel利用redis和定時任務實現活躍用戶統計

最近在Summer的《Laravel教程-Web開發實戰進階》學到很多東西,以前只會看文檔,大概瞭解Laravel的內容而在實際運用中確不知道該怎樣做,碰到一個需求不會立馬聯想到“這個東西可以用…實現“。
不是有句話”實踐是檢驗真理的唯一標準“ 所以學習編程還是要多實踐多寫項目,不然就像我一樣拿到東西不知道怎樣運用 廢話不多,今天分享一下利用redis和定時任務實現活躍用戶統計
如果你對redis和定時任務不瞭解建議去補一下linuxredis 以及laravel的任務調度

活躍用戶算法
我們規定系統每個小時,統計最近7天用戶所發表的帖子數和評論數。
用戶發佈帖子+4分
用戶發佈評論+1分
最後計算所有人的得分進行倒序排序 取前八個用戶顯示在主頁活躍用戶欄
類似
在這裏插入圖片描述

需求已經明確我們開始編寫代碼,不過在編寫代碼之前我們需要.env中指定緩存驅動爲redis
在這裏插入圖片描述
爲了不讓User模型顯得非常龐大,我們使用trait的方式編寫用戶統計邏輯,如果你不瞭解trait請參照PHP面向對象之trait

新鍵如下文件
app/Models/Traits/ActiveUserHelper.php
鍵入如下代碼

<?php

namespace App\Models\Traits;

use App\Models\Topic;
use App\Models\Reply;
use Carbon\Carbon;
use Cache;
use DB;
use Arr;

trait ActiveUserHelper
{
    // 用於存放臨時用戶數據
    protected $users = [];       

    // 配置信息
    protected $topic_weight = 4; // 話題權重
    protected $reply_weight = 1; // 回覆權重
    protected $pass_days = 7;    // 多少天內發表過內容
    protected $user_number = 6; // 取出來多少用戶

    // 緩存相關配置
    protected $cache_key = 'larabbs_active_users';//設置緩存key
    protected $cache_expire_in_seconds = 65 * 60;//設置緩存過期時間

    public function getActiveUsers()
    {
        // 嘗試從緩存中取出 cache_key 對應的數據。如果能取到,便直接返回數據。
        // 否則運行匿名函數中的代碼來取出活躍用戶數據,返回的同時做了緩存。
        return Cache::remember($this->cache_key, $this->cache_expire_in_seconds, function(){
            return $this->calculateActiveUsers();
        });
    }

    public function calculateAndCacheActiveUsers()
    {
        // 取得活躍用戶列表
        $active_users = $this->calculateActiveUsers();
        // 並加以緩存
        $this->cacheActiveUsers($active_users);
    }

    private function calculateActiveUsers()
    {
        $this->calculateTopicScore();//計算獲取用戶發帖權重
        $this->calculateReplyScore();//計算用戶回覆權重

        // 數組按照得分排序
        $users = Arr::sort($this->users, function ($user) {
            return $user['score'];
        });

        // 我們需要的是倒序,高分靠前,第二個參數爲保持數組的 KEY 不變
        $users = array_reverse($users, true);

        // 只獲取我們想要的數量
        $users = array_slice($users, 0, $this->user_number, true);

        // 新建一個空集合
        $active_users = collect();

        foreach ($users as $user_id => $user) {
            // 找尋下是否可以找到用戶
            $user = $this->find($user_id);

            // 如果數據庫裏有該用戶的話
            if ($user) {

                // 將此用戶實體放入集合的末尾
                $active_users->push($user);
            }
        }

        // 返回數據
        return $active_users;
    }

    private function calculateTopicScore()
    {
        // 從話題數據表裏取出限定時間範圍($pass_days)內,有發表過話題的用戶
        // 並且同時取出用戶此段時間內發佈話題的數量
        $topic_users = Topic::query()->select(DB::raw('user_id, count(*) as topic_count'))
                                     ->where('created_at', '>=', Carbon::now()->subDays($this->pass_days))
                                     ->groupBy('user_id')
                                     ->get();
        // 根據話題數量計算得分
        foreach ($topic_users as $value) {
            $this->users[$value->user_id]['score'] = $value->topic_count * $this->topic_weight;
        }
    }

    private function calculateReplyScore()
    {
        // 從回覆數據表裏取出限定時間範圍($pass_days)內,有發表過回覆的用戶
        // 並且同時取出用戶此段時間內發佈回覆的數量
        $reply_users = Reply::query()->select(DB::raw('user_id, count(*) as reply_count'))
                                     ->where('created_at', '>=', Carbon::now()->subDays($this->pass_days))
                                     ->groupBy('user_id')
                                     ->get();
        // 根據回覆數量計算得分
        foreach ($reply_users as $value) {
            $reply_score = $value->reply_count * $this->reply_weight;
            if (isset($this->users[$value->user_id])) {
                $this->users[$value->user_id]['score'] += $reply_score;
            } else {
                $this->users[$value->user_id]['score'] = $reply_score;
            }
        }
    }

    private function cacheActiveUsers($active_users)
    {
        // 將數據放入緩存中
        Cache::put($this->cache_key, $active_users, $this->cache_expire_in_seconds);
    }
}

請參照註釋理解代碼。
由於是基於User Model實現用戶活躍統計 我們在User Model trait ActiveUserHelper
在這裏插入圖片描述
這樣就相當於把 ActiveUserHelper的代碼注入到User Model中

我們要通過定時任務來實現用戶活躍統計,所以我們通過命令執行ActiveUserHelper中的邏輯。
新鍵命令

php artisan make:command CalculateActiveUser --command=larabbs:calculate-active-user

CalculateActiveUse是一個命令類,存放在app/Console/Commands/在這裏面鍵入命令相關邏輯

--command=爲我們今後要調用的命令名
php aritsan larabbs:calculate-active-user

編寫app/Console/Commands/CalculateActiveUser

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\User;

class CalculateActiveUser extends Command
{
    // 供我們調用命令
    protected $signature = 'larabbs:calculate-active-user';

    // 命令的描述
    protected $description = '生成活躍用戶';

    // 最終執行的方法
    public function handle(User $user)
    {
        // 在命令行打印一行信息
        $this->info("開始計算...");

        $user->calculateAndCacheActiveUsers();//取得活躍用戶並緩存

        $this->info("成功生成!");
    }
}

接下來我們就可以輸入 php artisan larabbs:calculate-active-user 執行 上面handle方法

定時任務
我們不可能每一個小時都手動的調用 php artisan larabbs:calculate-active-user 獲取最新的活躍用戶,所以我們利用定時任務實現
在當前下面輸入

export EDITOR=vi && crontab -e

打開如下,在文件的末尾鍵入

* * * * * php /home/vagrant/Code/larabbs/artisan schedule:run >> /dev/null 2>&1

在這裏插入圖片描述
* * * * * php /home/vagrant/Code/larabbs/art。。。。。是linux定時任務的寫法
參照下圖
在這裏插入圖片描述
*後面爲我們要執行的程序
>>表示追加內容 linux中dev/null表示黑洞的意思。
>表示覆蓋內容。
1 表示 stdout 標準輸出。
2 表示 stderr 標準錯誤。
所以 2>&1表示將所有錯誤信息覆蓋到標準輸出。
綜上所述
* * * * * php /home/vagrant/Code/larabbs/artisan schedule:run >> /dev/null 2>&1表示
將 schedule:run的結果與錯誤輸出到黑洞(不予顯示),不過爲了方便調試,你可以將其運行結果輸出到文件

* * * * *php artisan schedule:run >> /home/user/output.txt

接下來我們註冊調度任務
在app/Console/Kernel.php

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // $schedule->command('inspire')
        //          ->hourly();

        // 一小時執行一次『活躍用戶』數據生成的命令
        $schedule->command('larabbs:calculate-active-user')->hourly();
    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}

這樣每一個小時就會執行schedule裏面的命令,參照laravel任務調度瞭解更多。

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