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