亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

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

z2xy / 2088人閱讀

摘要:說(shuō)明本文主要講述使用作為緩存加快頁(yè)面訪問(wèn)速度。何不用來(lái)做緩存,等到該達(dá)到一定瀏覽頁(yè)面后再刷新下,效率也很高??勺骶彺嫦到y(tǒng)隊(duì)列系統(tǒng)。

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

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

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

Redis依賴包安裝與配置

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

Redis服務(wù)端安裝

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

brew install redis
//設(shè)置電腦啟動(dòng)時(shí)也啟動(dòng)redis-server
ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents
//通過(guò)launchctl啟動(dòng)redis-server
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.redis.plist
//或者通過(guò)配置文件啟動(dòng)
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
//測(cè)試是否安裝成功,出現(xiàn)pong,輸入redis-cli進(jìn)入redis自帶的終端客戶端
redis-cli ping

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

composer require predis/predis

predis是用PHP語(yǔ)言寫的一個(gè)redis客戶端包,Laravel的Redis模塊依賴于這個(gè)包。
phpredis是C語(yǔ)言寫的一個(gè)PHP擴(kuò)展,和predis功能差不多,只不過(guò)作為擴(kuò)展效率高些,phpredis可以作為擴(kuò)展裝進(jìn)PHP語(yǔ)言中,不過(guò)這里沒(méi)用到,就不裝了。

推薦Laravel開(kāi)發(fā)插件三件套,提高開(kāi)發(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ū)動(dòng)設(shè)為redis,還有redis自身配置在config/database.php文件中:

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

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

        "cluster" => false,

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

    ],
Redis存儲(chǔ)瀏覽量字段

先做個(gè)post表,建個(gè)post遷移文件再設(shè)計(jì)表字段值,包括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("標(biāo)題");
            $table->string("slug")->unique()->index()->comment("錨點(diǎn)");
            $table->string("summary")->comment("概要");
            $table->text("content")->comment("內(nèi)容");
            $table->text("origin")->comment("文章來(lái)源");
            $table->integer("comment_count")->unsigned()->comment("評(píng)論次數(shù)");
            $table->integer("view_count")->unsigned()->comment("瀏覽次數(shù)");
            $table->integer("favorite_count")->unsigned()->comment("點(diǎn)贊次數(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");
    }
}

做一個(gè)控制器和一個(gè)路由:

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

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

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

//指令
php artisan event:generate       

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

在PostController寫上showPostCache方法:

const modelCacheExpires = 10;

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

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

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

這里Cache上文已經(jīng)配置了以redis作為驅(qū)動(dòng),這里取IP,這樣防止同一IP短時(shí)間內(nèi)刷新頁(yè)面增加瀏覽量,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;
    }

順便也把視圖簡(jiǎn)單寫下吧:


    
        
        
        
        
        Bootstrap Template
        
        
        
    
    
        

Title:{{$post->title}}

Summary:{{$post->summary}}

Content:{{$post->content}}

然后重點(diǎn)寫下事件監(jiān)聽(tīng)器邏輯:

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

    /**
     * 同一用戶瀏覽同一post過(guò)期時(shí)間
     */
    const ipExpireSec   = 300;

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

    }

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

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

        return false;
    }

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

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

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



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

It is working!!!

不知道有沒(méi)有說(shuō)清楚,有疑問(wèn)或者指正的地方請(qǐng)留言交流吧。

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

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

RightCapital招聘Laravel DevOps

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/61802.html

相關(guān)文章

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

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

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

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

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

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

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

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

    longshengwang 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<