Redis在Laravel项目中的应用实例详解


Posted in PHP onAugust 11, 2017

前言

本文主要给大家介绍了关于Redis在Laravel项目中的应用实例,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:

在初步了解Redis在Laravel中的应用 那么我们试想这样的一个应用场景 一个文章或者帖子的浏览次数的统计 如果只是每次增加一个浏览量

就到数据库新增一个数据 如果请求来那个太大这对数据库的消耗也就不言而喻了吧 那我们是不是可以有其他的解决方案

这里的解决方案就是 即使你的网站的请求量很大 那么每次增加一个访问量就在缓存中去进行更改 至于刷新Mysql数据库可以自定义为

多少分钟进行刷新一次或者访问量达到一定数量再去刷新数据库 这样数据也是准确的 效率也比直接每次刷新数据库要高出许多了

既然给出了相应的解决方案 我们就开始实施

我们以一篇帖子的浏览为例 我们先去创建对应的控制器

$ php artisan make:controller PostController

再去生成需要用到的 Model

$ php artisan make:model Post -m

填写posts的迁移表的字段内容

Schema::create('posts', function (Blueprint $table) {
 $table->increments('id');
 $table->string("title");
 $table->string("content");
 $table->integer('view_count')->unsigned();
 $table->timestamps();
});

还有就是我们测试的数据的Seeder填充数据

$factory->define(App\Post::class, function (Faker\Generator $faker) {
 return [
 'title' => $faker->sentence,
 'content' => $faker->paragraph,
 'view_count' => 0
 ];
});

定义帖子的访问路由

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

当然我们还是需要去写我们访问也就是浏览事件的(在app/providers/EventServiceProvider中定义)

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

执行事件生成监听

$ php artisan event:generate

之前定义了相关的路由方法 现在去实现一下:

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

 //获取客户端请求的IP
 $ip = $request->ip();
 
 //触发浏览次数统计时间
 event(new PostViewEvent($post, $ip));

 return view('posts.show', compact('post'));
}

这里看的出来就是以Redis作为缓存驱动 同样的 会获取获取的ip目的是防止同一个ip多次刷新来增加浏览量

同样的每次浏览会触发我们之前定义的事件 传入我们的post和id参数

Redis的key的命名以:分割 这样可以理解为一个层级目录 在可视化工具里就可以看的很明显了

接下来就是给出我们的posts.show的视图文件

<html lang="en">
<head>
 <meta charset="utf-8">
 <meta http-equiv="X-UA-Compatible" content="IE=edge">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <title>Bootstrap Template</title>
 <!-- 新 Bootstrap 核心 CSS 文件 -->
 <link rel="stylesheet" href="//cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="external nofollow" >
 <style>
 html,body{
  width: 100%;
  height: 100%;
 }
 *{
  margin: 0;
  border: 0;
 }
 .jumbotron{
  margin-top: 10%;
 }
 .jumbotron>span{
  margin: 10px;
 }
 </style>
</head>
<body>
<div class="container">
 <div class="row">
 <div class="col-xs-12 col-md-12">
  <div class="jumbotron">
  <h1>Title:{{$post->title}}</h1>
  <span class="glyphicon glyphicon-eye-open" aria-hidden="true"> {{$post->view_count}} views</span>
  <p>Content:{{$post->content}}</p>
  </div>
 </div>
 </div>
</div>

<!-- jQuery文件-->
<script src="//cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<script>

</script>
</body>
</html>

初始化我们的事件就是接收一下这些参数即可

class PostViewEvent
{
 use Dispatchable, InteractsWithSockets, SerializesModels;

 public $ip;
 public $post;


 /**
 * PostViewEvent constructor.
 * @param Post $post
 * @param $ip
 */
 public function __construct(Post $post, $ip)
 {
 $this->post = $post;
 $this->ip = $ip;
 }

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

最主要的还是编写我们的监听事件:

class PostEventListener
{
 /**
 * 一个帖子的最大访问数
 */
 const postViewLimit = 20;

 /**
 * 同一用户浏览同一个帖子的过期时间
 */
 const ipExpireSec = 200;

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

 }


 /**
 * @param PostViewEvent $event
 */
 public function handle(PostViewEvent $event)
 {
 $post = $event->post;
 $ip = $event->ip;
 $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']);
 }
 }
}

最后可以通过我们的工具查看具体效果

Redis在Laravel项目中的应用实例详解

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对三水点靠木的支持。

相关链接

PHP 相关文章推荐
PHP4和PHP5共存于一系统
Nov 17 PHP
PHP的面试题集,附我的答案和分析(一)
Nov 19 PHP
php面向对象全攻略 (九)访问类型
Sep 30 PHP
PHP的autoload自动加载机制使用说明
Dec 28 PHP
ThinkPHP表单自动验证实例
Oct 13 PHP
ecshop实现smtp发送邮件
Feb 03 PHP
详解配置 Apache 服务器支持 PHP 文件的解析
Feb 15 PHP
CI(CodeIgniter)框架视图中加载视图的方法
Mar 24 PHP
使用WAMP搭建PHP本地开发环境
May 10 PHP
PHP实现求连续子数组最大和问题2种解决方法
Dec 26 PHP
php通过header发送自定义数据方法
Jan 18 PHP
TP5(thinkPHP5框架)基于bootstrap实现的单图上传插件用法示例
May 29 PHP
PHP验证码无法显示的原因及解决办法
Aug 11 #PHP
php readfile()修改文件上传大小设置
Aug 11 #PHP
浅谈Laravel中的一个后期静态绑定
Aug 11 #PHP
浅谈PHP中new self()和new static()的区别
Aug 11 #PHP
php使用 readfile() 函数设置文件大小大小的方法
Aug 11 #PHP
详解PHP使用日期时间处理器Carbon人性化显示时间
Aug 10 #PHP
PHP弱类型语言中类型判断操作实例详解
Aug 10 #PHP
You might like
PHP错误Allowed memory size of 67108864 bytes exhausted的3种解决办法
2014/07/28 PHP
Yii中Model(模型)的创建及使用方法
2015/12/28 PHP
php登录超时检测功能实例详解
2017/03/21 PHP
Alliance vs AM BO3 第二场2.13
2021/03/10 DOTA
checkbox 多选框 联动实现代码
2008/10/22 Javascript
js 删除数组的几种方法小结
2014/02/21 Javascript
js截取中英文字符串、标点符号无乱码示例解读
2014/04/17 Javascript
JQuery中使用Ajax赋值给全局变量失败异常的解决方法
2014/08/18 Javascript
用JavaScript显示浏览器客户端信息的超相近教程
2015/06/18 Javascript
JS封装cookie操作函数实例(设置、读取、删除)
2015/11/17 Javascript
js实现图片无缝滚动特效
2020/03/19 Javascript
AngularJS路由实现页面跳转实例
2017/03/03 Javascript
详解Puppeteer 入门教程
2018/05/09 Javascript
解决vue动态为数据添加新属性遇到的问题
2018/09/18 Javascript
vue实现在线学生录入系统
2020/05/30 Javascript
解决vue 使用axios.all()方法发起多个请求控制台报错的问题
2020/11/09 Javascript
Python 访问限制 private public的详细介绍
2018/10/16 Python
python使用tkinter库实现五子棋游戏
2019/06/18 Python
pandas数据处理进阶详解
2019/10/11 Python
关于torch.optim的灵活使用详解(包括重写SGD,加上L1正则)
2020/02/20 Python
python实现ftp文件传输系统(案例分析)
2020/03/20 Python
python怎么自定义捕获错误
2020/06/29 Python
python 基于selectors库实现文件上传与下载
2020/12/31 Python
使用Django的JsonResponse返回数据的实现
2021/01/15 Python
浅析Python模块之间的相互引用问题
2021/02/26 Python
中国第一家杂志折扣订阅网:杂志铺
2016/08/30 全球购物
法国二手手袋、手表和奢侈珠宝购物网站:Collector Square
2018/07/05 全球购物
美国综合购物商城:UnbeatableSale.com
2018/11/28 全球购物
手工制作的音乐盒:Music Box Attic
2019/09/05 全球购物
服务员岗位职责
2014/01/29 职场文书
纪律教育学习月活动总结
2014/08/27 职场文书
2014年乡镇安全生产工作总结
2014/12/02 职场文书
幼儿园大班毕业评语
2014/12/31 职场文书
教学工作总结范文5篇
2019/08/19 职场文书
Python作用域和名称空间的详细介绍
2022/04/13 Python
Java 使用类型为Object的变量指向任意类型的对象
2022/04/13 Java/Android