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 相关文章推荐
一周学会PHP(视频)Http下载
Dec 12 PHP
PHP优于Node.js的五大理由分享
Sep 15 PHP
linux命令之调试工具strace的深入分析
Jun 03 PHP
使用PHP获取汉字的拼音(全部与首字母)
Jun 27 PHP
本地机apache配置基于域名的虚拟主机详解
Aug 10 PHP
PHP Session 变量的使用方法详解与实例代码
Sep 11 PHP
PHP限制页面只能在微信自带浏览器访问的代码
Jan 15 PHP
ThinkPHP CURD方法之field方法详解
Jun 18 PHP
深入浅析php中sprintf与printf函数的用法及区别
Jan 08 PHP
php+ajax无刷新上传图片的实现方法
Dec 06 PHP
PHP中in_array的隐式转换的解决方法
Mar 06 PHP
laravel框架数据库操作、查询构建器、Eloquent ORM操作实例分析
Dec 20 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
关于页面优化和伪静态
2009/10/11 PHP
zend framework文件上传功能实例代码
2013/12/25 PHP
php过滤html中的其他网站链接的方法(域名白名单功能)
2014/04/24 PHP
ThinkPHP惯例配置文件详解
2014/07/14 PHP
php隐藏IP地址后两位显示为星号的方法
2014/11/21 PHP
使用php+swoole对client数据实时更新(一)
2016/01/07 PHP
自制PHP框架之设计模式
2017/05/07 PHP
Nginx实现反向代理
2017/09/20 Servers
Thinkphp 框架扩展之数据库驱动常用方法小结
2020/04/23 PHP
深入document.write()与HTML4.01的非成对标签的详解
2013/05/08 Javascript
div拖拽插件——JQ.MoveBox.js(自制JQ插件)
2013/05/17 Javascript
jQuery自定义添加&quot;$&quot;与解决&quot;$&quot;冲突的方法
2015/01/19 Javascript
jQuery实现3D文字特效的方法
2015/03/10 Javascript
JS中产生标识符方式的演变
2015/06/12 Javascript
原生Javascript插件开发实践
2017/01/18 Javascript
浅谈vue的iview列表table render函数设置DOM属性值的方法
2017/09/30 Javascript
jquery animate动画持续运动的实例
2017/11/29 jQuery
jQuery实现使用sort方法对json数据排序的方法
2018/04/17 jQuery
使用mpvue搭建一个初始小程序及项目配置方法
2018/12/03 Javascript
JavaScript大数相加相乘的实现方法实例
2020/10/18 Javascript
[58:09]Spirit vs NB Supermajor小组赛 A组败者组决赛 BO3 第三场 6.2
2018/06/03 DOTA
[01:29:42]Liquid vs VP Supermajor决赛 BO 第一场 6.10
2018/07/05 DOTA
详解Python中的装饰器、闭包和functools的教程
2015/04/02 Python
微信跳一跳python代码实现
2018/01/05 Python
python实现nao机器人手臂动作控制
2019/04/29 Python
Python实现多态、协议和鸭子类型的代码详解
2019/05/05 Python
python基于celery实现异步任务周期任务定时任务
2019/12/30 Python
MxNet预训练模型到Pytorch模型的转换方式
2020/05/25 Python
爷爷追悼会答谢词
2014/01/24 职场文书
个人贷款承诺书
2014/03/28 职场文书
献爱心捐款倡议书
2014/05/14 职场文书
办理房产证委托书
2014/09/18 职场文书
学校运动会广播稿
2014/10/11 职场文书
2016年教师寒假学习心得体会
2015/10/09 职场文书
vue项目多环境配置(.env)的实现
2021/07/21 Vue.js
CSS实现单选折叠菜单功能
2021/11/01 HTML / CSS