详解thinkphp+redis+队列的实现代码(逻辑理解)

详解thinkphp+redis+队列的实现代码(逻辑理解)

https://www.jb51.net/article/121729.html     核心在于 队列只有一条 不会因查询   并发。

1,安装Redis,根据自己的PHP版本安装对应的redis扩展(此步骤简单的描述一下)

1.1,安装 php_igbinary.dll,php_redis.dll扩展此处需要注意你的php版本如图:

  

1.2,php.ini文件新增 extension=php_igbinary.dll;extension=php_redis.dll两处扩展

ok此处已经完成第一步redis环境搭建完成看看phpinfo

项目中实际使用redis

2.1,第一步配置redis参数如下,redis安装的默认端口为6379: 


1
2
3
4
5
6
7
8
9
10
11
12
13
14

return array (
  'DATA_CACHE_PREFIX' => 'Redis_' , //缓存前缀
  'DATA_CACHE_TYPE' => 'Redis' , //默认动态缓存为Redis
  'DATA_CACHE_TIMEOUT' => false,
  'REDIS_RW_SEPARATE' => true, //Redis读写分离 true 开启
  'REDIS_HOST' => '127.0.0.1' , //redis服务器ip,多台用逗号隔开;读写分离开启时,第一台负责写,其它[随机]负责读;
  'REDIS_PORT' => '6379' , //端口号
  'REDIS_TIMEOUT' => '300' , //超时时间
  'REDIS_PERSISTENT' =>false, //是否长连接 false=短连接
  'REDIS_AUTH' => '' , //AUTH认证密码
);

2.2,实际函数中使用redis:

1
2
3
4
5
6
7
8
9
10
11
  private function connectRedis(){
   $redis = new \Redis();
   $redis ->connect(C( "REDIS_HOST" ),C( "REDIS_PORT" )); 
   return $redis ;
  }

2.3,秒杀的核心问题是在大并发的情况下不会超出库存的购买,这个就是处理的关键所以思路是第一步在秒杀类的先做一些基础的数据生成:


1
2
3
4
5
6
7
8
9
10
11
//现在初始化里面定义后边要使用的redis参数
public function _initialize(){
   parent::_initialize();
   $goods_id = I( "goods_id" , '0' , 'intval' ); 
   if ( $goods_id ){
    $this ->goods_id = $goods_id ;
    $this ->user_queue_key = "goods_" . $goods_id . "_user" ; //当前商品队列的用户情况
    $this ->goods_number_key = "goods" . $goods_id ; //当前商品的库存队列
   }
   $this ->user_id = $this ->user_id ? $this ->user_id : $_SESSION [ 'uid' ]; 
  }

2.4,第二步就是关键所在,用户在进入商品详情页前先将当前商品的库存进行队列存入redis如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public function _before_detail(){
  $where [ 'goods_id' ] = $this ->goods_id;
  $where [ 'start_time' ] = array ( "lt" ,time());
  $where [ 'end_time' ] = array ( "gt" ,time());
  $goods = M( "goods" )->where( $where )->field( 'goods_num,start_time,end_time' )->find();
  ! $goods && $this ->error( "当前秒杀已结束!" );
  if ( $goods [ 'goods_num' ] > $goods [ 'order_num' ]){
   $redis = $this ->connectRedis();
   $getUserRedis = $redis ->hGetAll( "{$this->user_queue_key}" );
   $gnRedis = $redis ->llen( "{$this->goods_number_key}" );
  
   if (! count ( $getUserRedis ) && ! $gnRedis ){  
    for ( $i = 0; $i < $goods [ 'goods_num' ]; $i ++) {
     $redis ->lpush( "{$this->goods_number_key}" , 1);
    }
   }
   $resetRedis = $redis ->llen( "{$this->goods_number_key}" );
   if (! $resetRedis ){
    $this ->error( "系统繁忙,请稍后抢购!" );
   }
  } else {
   $this ->error( "当前产品已经秒杀完!" );
  }
   
}

接下来要做的就是用ajax来异步的处理用户点击购买按钮进行符合条件的数据进入购买的排队队列(如果当前用户没在当前产品用户的队列就进入排队并且pop一个库存队列,如果在就抛出,):


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  public function goods_number_queue(){
   ! $this ->user_id && $this ->ajaxReturn( array ( "status" => "-1" , "msg" => "请先登录" ));
   $model = M( "flash_sale" );
   $where [ 'goods_id' ] = $this ->goods_id;
   $goods_info = $model ->where( $where )->find();
   ! $goods_info && $this ->error( "对不起当前商品不存在或已下架!" );
  
   $redis = $this ->connectRedis();
  
   $goods_number_key = $redis ->llen( "{$this->goods_number_key}" );
   if (! $redis ->hGet( "{$this->user_queue_key}" , $this ->user_id)) {
    $goods_number_key = $redis ->lpop( "{$this->goods_number_key}" );
   }
    
   if ( $goods_number_key ){
    // 判断用户是否已在队列
    if (! $redis ->hGet( "{$this->user_queue_key}" , $this ->user_id)) {
     // 插入抢购用户信息
     $userinfo = array (
      "user_id" => $this ->user_id,
      "create_time" => time()
     );   
     $redis ->hSet( "{$this->user_queue_key}" , $this ->user_id, serialize( $userinfo ));
     $this ->ajaxReturn( array ( "status" => "1" ));
    } else {
     $modelCart = M( "cart" );
     $condition [ 'user_id' ] = $this ->user_id;
     $condition [ 'goods_id' ] = $this ->goods_id;
     $condition [ 'prom_type' ] = 1;
   $cartlist = $modelCart ->where( $condition )-> count ();
     if ( $cartlist > 0){
      $this ->ajaxReturn( array ( "status" => "2" ));
     } else {
      
      $this ->ajaxReturn( array ( "status" => "1" ));
      
     }
      
    }
     
   } else {
    $this ->ajaxReturn( array ( "status" => "-1" , "msg" => "系统繁忙,请重试!" ));
   }
  }

附加一个调试的函数,删除指定队列值:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function clearRedis(){
   set_time_limit(0);
   $redis = $this ->connectRedis();
   //$Rd = $redis->del("{$this->user_queue_key}");
   $Rd = $redis ->hDel( "goods49" , '用户id' ');
   $a = $redis ->hGet( "goods_49_user" , '用户id' );
   if (! $a ){
    dump( $a );
   }
   
   if ( $Rd == 0){
    exit ( "Redis队列已释放!" );  
   }
  }
posted @ 2018-10-17 14:24 Newman·Li 阅读( ...) 评论( ...) 编辑 收藏

你可能感兴趣的:(详解thinkphp+redis+队列的实现代码(逻辑理解))