国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Laravel學(xué)習(xí)筆記之Redis保存頁面瀏覽量

z2xy / 2019人閱讀

摘要:說明本文主要講述使用作為緩存加快頁面訪問速度。何不用來做緩存,等到該達到一定瀏覽頁面后再刷新下,效率也很高。可作緩存系統(tǒng)隊列系統(tǒng)。

說明:本文主要講述使用Redis作為緩存加快頁面訪問速度。同時,作者會將開發(fā)過程中的一些截圖和代碼黏上去,提高閱讀效率。

備注:作者最近在學(xué)習(xí)github上別人的源碼時,發(fā)現(xiàn)好多在計算一篇博客頁面訪問量view_count時都是這么做的:利用Laravel的事件監(jiān)聽器監(jiān)聽IP訪問該post,然后頁面每訪問一次,都刷新一次MySQL(假設(shè)MySQL)中post表的view_count字段,如果短時間內(nèi)大量的IP來訪問,那效率就不是很高了。何不用Redis來做緩存,等到該post達到一定瀏覽頁面后再刷新下MySQL,效率也很高。

開發(fā)環(huán)境:Laravel5.1+MAMP+PHP7+MySQL5.5

Redis依賴包安裝與配置

Redis就和MySQL一樣,都是數(shù)據(jù)庫,只不過MySQL是磁盤數(shù)據(jù)庫,數(shù)據(jù)存儲在磁盤里,而Redis是內(nèi)存數(shù)據(jù)庫,數(shù)據(jù)存儲在內(nèi)存里,不持久化的話服務(wù)器斷電數(shù)據(jù)就被抹掉了。Redis數(shù)據(jù)存儲類型比較多,包括:字符串類型、哈希類型、列表類型集合類型有序集合類型,而不像MySQL主要只有三類:字符串類型、數(shù)字類型日期類型。Redis可作緩存系統(tǒng)、隊列系統(tǒng)。

Redis服務(wù)端安裝

首先得在主機上裝下Redis服務(wù)端,以MAC為例,Windows/Linux安裝也很多教程:

brew install redis
//設(shè)置電腦啟動時也啟動redis-server
ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents
//通過launchctl啟動redis-server
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist
//或者通過配置文件啟動
redis-server /usr/local/etc/redis.conf
//停止redis-server
launchctl unload ~/Library/LaunchAgents/homebrew.mxcl.redis.plist
//卸載redis-server
$ brew uninstall redis
$ rm ~/Library/LaunchAgents/homebrew.mxcl.redis.plist
//測試是否安裝成功,出現(xiàn)pong,輸入redis-cli進入redis自帶的終端客戶端
redis-cli ping

主機安裝完,就可以在Laravel環(huán)境安裝下PHP的Redis客戶端依賴包:

composer require predis/predis

predis是用PHP語言寫的一個redis客戶端包,Laravel的Redis模塊依賴于這個包。
phpredis是C語言寫的一個PHP擴展,和predis功能差不多,只不過作為擴展效率高些,phpredis可以作為擴展裝進PHP語言中,不過這里沒用到,就不裝了。

推薦Laravel開發(fā)插件三件套,提高開發(fā)效率,可以參考作者寫的Laravel學(xué)習(xí)筆記之Seeder填充數(shù)據(jù)小技巧:

composer require barryvdh/laravel-debugbar --dev
composer require barryvdh/laravel-ide-helper --dev
composer require mpociot/laravel-test-factory-helper --dev

//config/app.php
        /**
         *Develop Plugin
        */
        BarryvdhDebugbarServiceProvider::class,
        MpociotLaravelTestFactoryHelperTestFactoryHelperServiceProvider::class,
        BarryvdhLaravelIdeHelperIdeHelperServiceProvider::class,

配置下config/cache.php文件把緩存驅(qū)動設(shè)為redis,還有redis自身配置在config/database.php文件中:

//config/cache.php
//"default" => "redis",
"default" => env("CACHE_DRIVER", "file"),//或者改下.env文件
"redis" => [
            "driver"     => "redis",
            "connection" => "default",//改為連接的實例,就默認連接"default"實例
        ],

//config/database.php
"redis" => [

        "cluster" => false,

        //就做一個實例,名為"default"實例
        "default" => [
            "host"     => env("REDIS_HOST", "localhost"),
            "password" => env("REDIS_PASSWORD", null),
            "port"     => env("REDIS_PORT", 6379),
            "database" => 0,
        ],

    ],
Redis存儲瀏覽量字段

先做個post表,建個post遷移文件再設(shè)計表字段值,包括seeder填充假數(shù)據(jù),可以參考下這篇文章Laravel學(xué)習(xí)筆記之Seeder填充數(shù)據(jù)小技巧,總之表字段如下:

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create("posts", function (Blueprint $table) {
            $table->increments("id");
            $table->integer("category_id")->unsigned()->comment("外鍵");
            $table->string("title")->comment("標題");
            $table->string("slug")->unique()->index()->comment("錨點");
            $table->string("summary")->comment("概要");
            $table->text("content")->comment("內(nèi)容");
            $table->text("origin")->comment("文章來源");
            $table->integer("comment_count")->unsigned()->comment("評論次數(shù)");
            $table->integer("view_count")->unsigned()->comment("瀏覽次數(shù)");
            $table->integer("favorite_count")->unsigned()->comment("點贊次數(shù)");
            $table->boolean("published")->comment("文章是否發(fā)布");
            $table->timestamps();
            $table->foreign("category_id")
                  ->references("id")
                  ->on("categories")
                  ->onUpdate("cascade")
                  ->onDelete("cascade");
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table("posts", function(Blueprint $tabel){
            $tabel->dropForeign("posts_category_id_foreign");
        });
        Schema::drop("posts");
    }
}

做一個控制器和一個路由:

php artisan make:controller PostController
Route::get("post/{id}", "PostController@showPostCache");

利用Laravel的事件模塊,來定義一個IP訪問事件類,然后在事件監(jiān)聽器類里做一些邏輯處理如把訪問量存儲在Redis里。Laravel的事件監(jiān)聽這么做:在EventServiceProvider里定義事件和對應(yīng)的監(jiān)聽器,然后輸入指令:

//app/Providers/EventServiceProvider.php
protected $listen = [
        "AppEventsPostViewCount" => [
            "AppListenersPostEventListener",
        ],
       ] 

//指令
php artisan event:generate       

在app/Event和app/Listeners會生成事件類和監(jiān)聽器類。

在PostController寫上showPostCache方法:

const modelCacheExpires = 10;

public function showPostCache(Request $request, $id)
    {
        //Redis緩存中沒有該post,則從數(shù)據(jù)庫中取值,并存入Redis中,該鍵值key="post:cache".$id生命時間10分鐘
        $post = Cache::remember("post:cache:".$id, self::modelCacheExpires, function () use ($id) {
            return Post::whereId($id)->first();
        });

        //獲取客戶端IP
        $ip = $request->ip();
        //觸發(fā)瀏覽量計數(shù)器事件
        event(new PostViewCount($post, $ip));

        return view("browse.post", compact("post"));
    }

這里Cache上文已經(jīng)配置了以redis作為驅(qū)動,這里取IP,這樣防止同一IP短時間內(nèi)刷新頁面增加瀏覽量,event()或Event::fire()觸發(fā)事件,把$post和$ip作為參數(shù)傳入,然后再定義事件類:

//app/Events/PostViewCount.php
/**
     * @var Post
     */
    public $post;

    /**
     * @var string
     */
    public $ip;

    /**
     * Create a new event instance.
     *
     * @param Post $post
     * @param string $ip
     */
    public function __construct(Post $post, $ip)
    {
        $this->post = $post;
        $this->ip   = $ip;
    }

順便也把視圖簡單寫下吧:


    
        
        
        
        
        Bootstrap Template
        
        
        
    
    
        

Title:{{$post->title}}

Summary:{{$post->summary}}

Content:{{$post->content}}

然后重點寫下事件監(jiān)聽器邏輯:

class PostEventListener
{
    /**
     * 同一post最大訪問次數(shù),再刷新數(shù)據(jù)庫
     */
    const postViewLimit = 30;

    /**
     * 同一用戶瀏覽同一post過期時間
     */
    const ipExpireSec   = 300;

    /**
     * Create the event listener.
     *
     */
    public function __construct()
    {

    }

    /**
     * Handle the event.
     * 監(jiān)聽用戶瀏覽事件
     * @param  PostViewCount  $event
     * @return void
     */
    public function handle(PostViewCount $event)
    {
        $post = $event->post;
        $ip   = $event->ip;
        $id   = $post->id;
        //首先判斷下ipExpireSec = 300秒時間內(nèi),同一IP訪問多次,僅僅作為1次訪問量
        if($this->ipViewLimit($id, $ip)){
            //一個IP在300秒時間內(nèi)訪問第一次時,刷新下該篇post的瀏覽量
            $this->updateCacheViewCount($id, $ip);
        }
    }

    /**
     * 一段時間內(nèi),限制同一IP訪問,防止增加無效瀏覽次數(shù)
     * @param $id
     * @param $ip
     * @return bool
     */
    public function ipViewLimit($id, $ip)
    {
//        $ip = "1.1.1.6";
        //redis中鍵值分割都以:來做,可以理解為PHP的命名空間namespace一樣
        $ipPostViewKey    = "post:ip:limit:".$id;
        //Redis命令SISMEMBER檢查集合類型Set中有沒有該鍵,該指令時間復(fù)雜度O(1),Set集合類型中值都是唯一
        $existsInRedisSet = Redis::command("SISMEMBER", [$ipPostViewKey, $ip]);
        if(!$existsInRedisSet){
            //SADD,集合類型指令,向ipPostViewKey鍵中加一個值ip
            Redis::command("SADD", [$ipPostViewKey, $ip]);
            //并給該鍵設(shè)置生命時間,這里設(shè)置300秒,300秒后同一IP訪問就當做是新的瀏覽量了
            Redis::command("EXPIRE", [$ipPostViewKey, self::ipExpireSec]);
            return true;
        }

        return false;
    }

    /**
     * 更新DB中post瀏覽次數(shù)
     * @param $id
     * @param $count
     */
    public function updateModelViewCount($id, $count)
    {
        //訪問量達到300,再進行一次SQL更新
        $postModel              = Post::find($id);
        $postModel->view_count += $count;
        $postModel->save();
    }

    /**
     * 不同用戶訪問,更新緩存中瀏覽次數(shù)
     * @param $id
     * @param $ip
     */
    public function updateCacheViewCount($id, $ip)
    {
        $cacheKey        = "post:view:".$id;
        //這里以Redis哈希類型存儲鍵,就和數(shù)組類似,$cacheKey就類似數(shù)組名,$ip為$key.HEXISTS指令判斷$key是否存在$cacheKey中
        if(Redis::command("HEXISTS", [$cacheKey, $ip])){
            //哈希類型指令HINCRBY,就是給$cacheKey[$ip]加上一個值,這里一次訪問就是1
            $incre_count = Redis::command("HINCRBY", [$cacheKey, $ip, 1]);
            //redis中這個存儲瀏覽量的值達到30后,就往MySQL里刷下,這樣就不需要每一次瀏覽,來一次query,效率不高
            if($incre_count == self::postViewLimit){
                $this->updateModelViewCount($id, $incre_count);
                //本篇post,redis中瀏覽量刷進MySQL后,把該篇post的瀏覽量鍵抹掉,等著下一次請求重新開始計數(shù)
                Redis::command("HDEL", [$cacheKey, $ip]);
                //同時,抹掉post內(nèi)容的緩存鍵,這樣就不用等10分鐘后再更新view_count了,
                //如該篇post在100秒內(nèi)就達到了30訪問量,就在3分鐘時更新下MySQL,并把緩存抹掉,下一次請求就從MySQL中請求到最新的view_count,
                //當然,100秒內(nèi)view_count還是緩存的舊數(shù)據(jù),極端情況300秒內(nèi)都是舊數(shù)據(jù),而緩存里已經(jīng)有了29個新增訪問量
                //實際上也可以這樣做:在緩存post的時候,可以把view_count多帶帶拿出來存入鍵值里如single_view_count,每一次都是給這個值加1,然后把這個值傳入視圖里
                //或者平衡設(shè)置下postViewLimit和ipExpireSec這兩個參數(shù),對于view_count這種實時性要求不高的可以這樣做來著
                //加上laravel前綴,因為Cache::remember會自動在每一個key前加上laravel前綴,可以看cache.php中這個字段:"prefix" => "laravel"
                Redis::command("DEL", ["laravel:post:cache:".$id]);
            }
        }else{
            //哈希類型指令HSET,和數(shù)組類似,就像$cacheKey[$ip] = 1;
            Redis::command("HSET", [$cacheKey, $ip, "1"]);
        }
    }
}

這里推薦下一本Redis入門書《Redis入門指南》(作者也是咱北航的,軟件學(xué)院的,居然比我小一屆,慚愧。。不過俺們也參與寫過書,哈哈,只是參與,呵呵),快的話看個一兩天就能看完,也就基本入門了。還推薦一個Redis客戶端:Redis Desktop Manager,可以在客戶端里看下各個鍵值:



頁面視圖中可以利用上面推薦的barryvdh/laravel-debugbar插件觀察下請求過程產(chǎn)生的數(shù)據(jù)。第一次請求時會有一次query,然后從緩存里取值沒有query了,直到把緩存中view_count刷到MySQL里再有一次query:

It is working!!!

不知道有沒有說清楚,有疑問或者指正的地方請留言交流吧。

總結(jié):研究Redis和Cache模塊的時候,還看到可以利用Model Observer模型觀察器來監(jiān)聽事件自動刷新緩存,晚上在研究下吧,這兩天也順便把Redis數(shù)據(jù)存儲類型總結(jié)下,到時見。

歡迎關(guān)注Laravel-China。

RightCapital招聘Laravel DevOps

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/61802.html

相關(guān)文章

  • Laravel學(xué)習(xí)筆記Model Observer模型觀察者

    摘要:說明本文主要學(xué)習(xí)下的模型觀察者,把一點點經(jīng)驗分享出來希望對別人能有幫助。模型觀察者這個功能能做很多事情,比如模型更新時發(fā)個通知??偨Y(jié)本篇文章主要學(xué)了下的模型觀察者,發(fā)現(xiàn)這個功能也能使代碼結(jié)構(gòu)更清晰,覺得挺好的。 說明:本文主要學(xué)習(xí)下Laravel的Model Observer模型觀察者,把一點點經(jīng)驗分享出來希望對別人能有幫助。同時,作者會將開發(fā)過程中的一些截圖和代碼黏上去,提高閱讀效率...

    Crazy_Coder 評論0 收藏0
  • Laravel學(xué)習(xí)筆記Session源碼解析(下)

    摘要:實際上,在中關(guān)閉主要包括兩個過程保存當前到介質(zhì)中在中存入。,學(xué)習(xí)下關(guān)閉的源碼吧先??傊?,關(guān)閉的第二件事就是給添加。通過對的源碼分析可看出共分為三大步啟動操作關(guān)閉??偨Y(jié)本小系列主要學(xué)習(xí)了的源碼,學(xué)習(xí)了的三大步。 說明:在中篇中學(xué)習(xí)了session的CRUD增刪改查操作,本篇主要學(xué)習(xí)關(guān)閉session的相關(guān)源碼。實際上,在Laravel5.3中關(guān)閉session主要包括兩個過程:保存當前U...

    Awbeci 評論0 收藏0
  • Laravel學(xué)習(xí)筆記Session源碼解析(上)

    摘要:然后中間件使用方法來啟動獲取實例,使用類來管理主要分為兩步獲取實例,主要步驟是通過該實例從存儲介質(zhì)中讀取該次請求所需要的數(shù)據(jù),主要步驟是。 說明:本文主要通過學(xué)習(xí)Laravel的session源碼學(xué)習(xí)Laravel是如何設(shè)計session的,將自己的學(xué)習(xí)心得分享出來,希望對別人有所幫助。Laravel在web middleware中定義了session中間件IlluminateSess...

    NervosNetwork 評論0 收藏0
  • Laravel學(xué)習(xí)筆記Session源碼解析(中)

    摘要:說明在上篇中學(xué)習(xí)了的啟動過程,主要分為兩步,一是的實例化,即的實例化二是從存儲介質(zhì)中讀取的數(shù)據(jù)。第二步就是操作,包括對數(shù)據(jù)的增刪改查操作,本文也主要聊下相關(guān)操作源碼。下篇再學(xué)習(xí)下關(guān)閉,到時見。 說明:在上篇中學(xué)習(xí)了session的啟動過程,主要分為兩步,一是session的實例化,即IlluminateSessionStore的實例化;二是從session存儲介質(zhì)redis中讀取id ...

    longshengwang 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<