How Laravel5.8 achieve Posts browsing into Redis

First, demand introduction

Views of an article or post statistics, if only in increments of views on a database to add / modify a data, the amount of more than one request frequent user on the problem.

Second, the solution

1, each time a traffic increase in the cache to make changes

2, after reaching a certain number of refreshing change Mysql database so that the data is accurate and efficient than direct each refresh database to be much higher

The tutorial is a test case for the project to implement this feature. For the development of a formal project, integrated own!

Third, the functional practice

1, create a new project: laravel new testin web.phpadd the following route:

Route::get('/post/{id}', 'PostController@showPost');

2, the controller and the corresponding method create:

php artisan make:controller PostController

3, create a database testand data tables posts, and modify the .envfile:

Just plug point data into it.

4, add event listeners:

In app/providers/EventServiceProviderthe $listenadded

protected $listen = [
        'App\Events\PostViewEvent' => [
            'App\Listeners\PostEventListener',
        ],
    ];

5, generates event listeners:

php artisan event:generate

6, the PostViewEvent.phpwrite code as follows:

<?php

namespace App\Events;
use App\Post;
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;

class PostViewEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $ip;
    public $post;

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

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

7, in app\Listeners\PostEventListenerthe addition of the following code:

<?php

namespace App\Listeners;

use App\Events\PostViewEvent;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

use App\Post;
use Illuminate\Support\Facades\Redis;

class PostEventListener
{
    /**
     * 一个帖子的最大访问数
     */
    const postViewLimit = 2;
    /**
     * 同一用户浏览同一个帖子的过期时间
     */
    const ipExpireSec = 200;

    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  PostViewEvent  $event
     * @return void
     */
    public function handle(PostViewEvent $event)
    {
        $post = $event->post;
        $ip = $event->ip;
//        $ip = "127.2.1.31";
        $id = $post->id;
        //首先判断下ipExpireSec = 200秒时间内,同一IP访问多次,仅仅作为1次访问量
        if($this->ipViewLimit($id, $ip)){
            //一个IP在300秒时间内访问第一次时,刷新下该篇post的浏览量
            $this->updateCacheViewCount($id, $ip);
        }

    }

    /**
     * 限制同一IP一段时间内得访问,防止增加无效浏览次数
     * @param $id
     * @param $ip
     * @return bool
     */
    public function ipViewLimit($id, $ip)
    {
        $ipPostViewKey = 'post:ip:limit:'.$id;
        //Redis命令SISMEMBER检查集合类型Set中有没有该键,Set集合类型中值都是唯一
        $existsInRedisSet = Redis::command('SISMEMBER', [$ipPostViewKey, $ip]);
        //如果集合中不存在这个建 那么新建一个并设置过期时间
        if(!$existsInRedisSet){
            //SADD,集合类型指令,向ipPostViewKey键中加一个值ip
            Redis::command('SADD', [$ipPostViewKey, $ip]);
            //并给该键设置生命时间,这里设置300秒,300秒后同一IP访问就当做是新的浏览量了
            Redis::command('EXPIRE', [$ipPostViewKey, self::ipExpireSec]);
            return true;
        }
        return false;
    }

    /**
     * 达到要求更新数据库的浏览量
     * @param $id
     * @param $count
     */
    public function updateModelViewCount($id, $count)
    {
        //访问量达到300,再进行一次SQL更新
        $post = Post::find($id);
        $post->view_count += $count;
        $post->save();
    }

    /**
     * 不同用户访问,更新缓存中浏览次数
     * @param $id
     * @param $ip
     */
    public function updateCacheViewCount($id, $ip)
    {
        $cacheKey = 'post:view:'.$id;
        //这里以Redis哈希类型存储键,就和数组类似,$cacheKey就类似数组名 如果这个key存在
        if(Redis::command('HEXISTS', [$cacheKey, $ip])){
            //哈希类型指令HINCRBY,就是给$cacheKey[$ip]加上一个值,这里一次访问就是1
            $save_count = Redis::command('HINCRBY', [$cacheKey, $ip, 1]);
            //redis中这个存储浏览量的值达到30后,就去刷新一次数据库
            if($save_count == self::postViewLimit){
                $this->updateModelViewCount($id, $save_count);
                //本篇post,redis中浏览量刷进MySQL后,就把该篇post的浏览量清空,重新开始计数
                Redis::command('HDEL', [$cacheKey, $ip]);
                Redis::command('DEL', ['laravel:post:cache:'.$id]);
            }
        }else{
            //哈希类型指令HSET,和数组类似,就像$cacheKey[$ip] = 1;
            Redis::command('HSET', [$cacheKey, $ip, '1']);
        }
    }

}

8, the PostControlleradd the following code controller:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
use App\Post;
use App\Events\PostViewEvent;
use Illuminate\Support\Facades\Cache;
class PostController extends Controller
{
    private $cacheExpires = 60;//post文章数据缓存时间 s
    public function showPost(Request $request,$id){
        //存入缓存中,该键值key='post:cache'.$id生命时间60分钟
        $post = Cache::remember('post:cache:'.$id, $this->cacheExpires, function () use ($id) {
            return Post::whereId($id)->first();
        });

        //获取客户端请求的IP
        $ip = $request->ip();
//        $ip = "127.0.1.1";//手动更改ip 以不同ip访问,计数
        //触发浏览次数统计时间
        event(new PostViewEvent($post, $ip));
        dump("当前预览文章的名称:".$post->title);
        dump("当前预览数:".$post->view_count);
    }
}

9, restart the terminal, browser access http://127.0.0.1:8000, you will see redisan error, then execute the following code, install redisextensions.

Note: Before executing the following command, make sure that you redishave installed.

composer require predis/predis

10, start redis, execute the redis-servercommand: .
11, access the browser again, you will see the following results:

And finally: test the above code is provided 200within seconds, the same IP access many times, just as once visited, so wait 200refresh again after seconds Views plus 1.

Well Done!!!

Published 14 original articles · won praise 1 · views 96

Guess you like

Origin blog.csdn.net/huangdj321/article/details/104929405