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!!!
- 点赞
- 收藏
- 分享
- 文章举报
相关文章推荐
- Redis系列文章总结:ASP.Net Core 中如何借助CSRedis实现一个安全高效的分布式锁
- 如何作证书并注册控件,并实现打包下载注册的功能(接前两片文章)(4)
- 如何实现在线数据的离线浏览和修改
- 如何实现分享网站文章到微信朋友圈时显示指定缩略图或LOGO
- 如何作证书并注册控件,并实现打包下载注册的功能(接前两片文章)(3)
- 如何正确实现PHP显示文章发布时间
- 每条新闻的浏览次数统计,该如何实现呢?
- 如何将Oracle中同一列的多行记录拼接成一个字符串 十一__262 | 浏览 8183 次 推荐于2016-01-25 17:34:18 最佳答案 需要用wm_concat函数来实现。
- 如何实现某一页面只让特定的用户浏览?
- 如何实现Wordpress文章分页
- 启用Nginx目录浏览功能如何实现
- spring结合redis如何实现数据的缓存
- 如何实现自动采集微信公众号文章
- 使用redis如何实现分布式锁
- 解读Laravel,看PHP如何实现Facade?
- 如何在vba中实现目录浏览对话框
- 如何实现在文章模块LIST页面調用文章内容摘要
- 如何实现“浏览过该页面的人还浏览过...”功能(数据库版)
- windows8下如何设置不保存本地文件浏览记录(通过注册表实现)
- 如何用程序实现向博客写入文章