Laravel+Redis 数据缓存

我们试想这样的一个应用场景 一个文章或者帖子的浏览次数的统计 如果只是每次增加一个浏览量
,当浏览量暴增时对数据库的消耗就不言而喻了,那我们是不是可以有其他的解决方案

  • 我们使用redis缓存数据库来处理,即使你的网站的请求量很大,那么每次增加一个访问量就在缓存中去进行更改,至于刷新数据库可以自定义为达到某个值时才去更新数据库

首先我们创建一个新的项目

composer create-project laravel/laravel redis_article --prefer-dist

在.env文件下配置mysql以及redis

mysql

Redis

然后创建控制器 模型

php artisan make:controller ArticleController

php artisan make:model Article -m
// 此命令会在batabase/migrations里生成迁移文件

// 迁移文件
public function up()
{
    Schema::create('articles', function (Blueprint $table) {
        $table->increments('id');

        $table->string("title");

        $table->text("content");

        $table->integer('page_view')->unsigned();

        $table->timestamps();
    });
}

// 使用laravel 模型工厂添加测试数据
php artisan make:seeder ArticleSeeder
php artisan make:factory ArticleFactory
    // 运行完会在batabase下的seeder和factory产生两个文件
    
// ArticleSeeder写法如下 生成50篇文章
public function run()
{
    Article::factory()
        ->count(50)
        ->create();
}

// ArticleFactory写法如下 默认title,content,page_view数据否则报错
public function definition()
{
    return [
        'title' => $this->faker->sentence,

        'content' => $this->faker->paragraph,

        'page_view' => 0
    ];
}

// 后运行命令
    php artisan migrate --path=/database/migrations/迁移文件名.php
    
    php artisan db:seed --class=ArticleSeeder

// 至此测试数据完成

在routes/api.php添加文章路由

Route::get('/articls/{id}', [\App\Http\Controllers\ArticleController::class,'showPost']);

创建浏览器监听事件

​ 在app\EventServiceProvider.php下的$listen添加一个数组

'App\Events\ArticlsViewEvent' => [
    'App\Listeners\ArticlsEventListener',
]
    
    // 生成 app\Events\ArticlsViewEvent.php 、 app\Listeners\ArticlsEventListener.php

接着我们书写ArticleController.php控制器

<?php

namespace App\Http\Controllers;

use App\Events\ArticlsViewEvent;
use App\Models\Article;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;

class ArticleController extends Controller
{
    public function __construct()
    {
        $this->cacheExpires = 5;
    }

    public function showPost(Request $request,$id)
    {
        //Redis缓存中没有该post,则从数据库中取值,并存入Redis中,该键值key='post:cache'.$id生命时间5分钟
        // $Articls = Cache::remember('Article:cache:'.$id, $this->cacheExpires, function () use ($id) {
        //
        //     return Article::whereId($id)->first();
        //
        // });

        $Articls = Article::whereId($id)->first();

        //获取客户端请求的IP

        $ip = $request->ip();

        //触发浏览次数统计时间 并把文章缓存和$ip传递过去
        event(new ArticlsViewEvent($Articls, $ip));

        return view('blog.show', compact('Articls'));

    }
}

app\Events\ArticlsViewEvent.php

<?php

namespace App\Events;

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

class ArticlsViewEvent
{
    use Dispatchable, InteractsWithSockets, SerializesModels;


    public function __construct(Article $article, $ip)
    {
        // 在构造函数中把参数保存在$this中让ArticlsEventListener接收
        $this->article = $article;
        //
        $this->ip = $ip;
    }


    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

app\Listeners\ArticlsEventListener.php

<?php

namespace App\Listeners;

use App\Events\ArticlsViewEvent;
use App\Models\Article;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;

class ArticlsEventListener
{
    /**

     * 限定redis最大存储数,达到这个数后更新数据库

     */

    const ArticlsViewLimit = 5;

    /**

     * 同一用户浏览同一个帖子的过期时间-秒

     */

    const ArticlsExpireTime = 30;


    // const

    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  \App\Events\ArticlsViewEvent  $event
     * @return void
     */
    public function handle(ArticlsViewEvent $event)
    {
        $articls = $event->article;

        $ip = $event->ip;

        $id = $articls->id;

        // 如果一个用户在ArticlsExpireTime时间内访问多次则只记录一次
        if($this->ArticlsRedisTime($ip,$id))
        {
            var_dump('浏览有效,记录');
            $this->updateCacheViewCount($id, $ip);
        }
    }

    public function ArticlsRedisTime($ip,$id)
    {
        $key = "Articls:Ip:time:$id:$ip";

        //  Redis命令SISMEMBER检查集合类型Set中有没有该键,Set集合类型中值都是唯一
        $rulest = Redis::command('SISMEMBER',[$key,$ip]);
        var_dump("存在则说明访问频繁不记录,不存在则真实有效");
        var_dump("存在?$rulest,");

        // 如果不存在则创建
        if(!$rulest)
        {
            Redis::command('SADD',[$key,$ip]);

            //并给该键设置生命时间,这里设置ArticlsExpireTime,ArticlsExpireTime秒后同一IP访问就当做是新的浏览量了

            Redis::command('EXPIRE', [$key, self::ArticlsExpireTime]);

            return true;
        }

        return false;
    }

    /**
        * 达到要求更新数据库的浏览量
     */

    public function updateCacheViewCount($id,$ip)
    {
        $key =  "Articls:View";

        // 如果这个key存在 +1
        if(Redis::command('HEXISTS',[$key,$id]))
        {
            // 存在+1 返回总数
            $save_count = Redis::command('HINCRBY', [$key, $id, 1]);

            var_dump($save_count);

            if($save_count == self::ArticlsViewLimit)
            {
                // 达到更新数据库的条件,更新数据库
                var_dump("达到更新数据库的条件,更新数据库");
                $this->updataMysql($id,$save_count);

                var_dump("更新数据库后,清除redis && 清除laravel的缓存");
                // 更新数据库后,清除redis && 清除laravel的缓存
                Redis::command('HDEL', [$key, $id]);

                Cache::pull('Article:cache:'.$id);
            }
        }

        else{

            Redis::command('HSET', [$key, $id, '1']);

        }
    }

    public function updataMysql($id,$save_count)
    {
        $data = Article::find($id);

        $data->page_view = bcadd($data->page_view,$save_count);

        $data->save();
        var_dump("更新数据库成功");
    }
}

最后视图resources\views\blog\show.blade.php

<div style="text-align: center">
    <h1>{{$Articls->title}}</h1>

    <br>
    浏览量:{{$Articls->page_view}}
    <br>

    内容:Content:{{$Articls->content}}
    <br>
</div>

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值