您的位置:首页 > 数据库 > Redis

Laravel5.8如何实现文章浏览数存入Redis

2020-03-22 18:23 1566 查看

一、需求介绍

一篇文章或者帖子的浏览次数的统计,如果只是每次增加一个浏览量 就到数据库新增/修改一个数据,请求频繁 用户量一多就出问题了。

二、解决方案

1、每次增加一个访问量就在缓存中去进行更改

2、达到一定数量后刷新改变Mysql数据库,这样数据也是准确的 效率也比直接每次刷新数据库要高出许多

本次教程是以测试项目为案例来实现此功能,如需在正式项目中开发,请自行集成!

三、功能实践

1、创建新项目:

laravel new test
,在
web.php
中添加如下路由:

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

2、创建控制器和对应方法:

php artisan make:controller PostController

3、创建数据库

test
和数据表
posts
,并修改
.env
文件:

随便插点数据进去。

4、加入事件监听:

app/providers/EventServiceProvider
中的
$listen
加入

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

5、生成事件监听:

php artisan event:generate

6、在

PostViewEvent.php
中写入如下代码:

<?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、在

app\Listeners\PostEventListener
中加入如下代码:

<?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、在

PostController
控制器中添加如下代码:

<?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、重启终端,浏览器访问

http://127.0.0.1:8000
,你会看到
redis
报错,接下来执行如下代码,安装
redis
扩展。

注意:执行下面的命令之前,请确保你的

redis
已经安装好了。

composer require predis/predis

10、启动

redis
,执行命令:
redis-server

11、再次访问浏览器,你会看到如下结果:

最后:测试,上面的代码设置的是

200
秒时间内,同一IP访问多次,仅仅作为1次访问量,所以等待
200
秒后再次刷新,浏览数加
1

Well Done!!!

  • 点赞
  • 收藏
  • 分享
  • 文章举报
Holyzq 发布了21 篇原创文章 · 获赞 2 · 访问量 298 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: