php thinkphp5.1 简单模拟redis秒杀实现

提前准备:

准备一:redis在tp框架里是做一个cache用的,所以在tp框架里只用了简单的key-value,没有封装一些稍微高级的方法,所以先要自己封装一个redis方法,你也可以直接改cache目录下的Redis类,写个简单的方法,把redis实例暴露出去,用原生的redis方法来解决问题。本案例因为秒杀里只用到了hash类型方法和list类型方法,所以简单的封装下。

①,在config里添加一些配置

进入/application/config/cache.php 添加简单的配置,因为之前做的一个测试是redis读写分离,所以配置上稍微有点奇怪,大家随意看看就好

<?php
return [
    // 驱动方式
    'type'   => 'File',
    // 缓存保存目录
    'path'   => '',
    // 缓存前缀
    'prefix' => '',
    // 缓存有效期 0表示永久缓存
    'expire' => 0,
    'REDIS_W_HOST' => '127.0.0.1',
    'REDID_W_PORT' => '6379',
    'REDIS_R_HOST' => '127.0.0.1',
    'REDID_R_PORT' => '6379',
];

②,在extend/Driver/Cache 里面创建一个Redis类,封装下原装的redis

<?php

namespace Driver\Cache;
use Config;
class Redis
{
    private static $write_handle = null;
    private static $read_handle = null;


    public static function getWrite()
    {
        $option = array(
            'host' => Config::get('cache.REDIS_W_HOST') ?? '127.0.0.1',
            'port' => Config::get('cache.REDIS_W_PORT') ?? 6379,
        );
        if (!self::$write_handle) {
            self::$write_handle = new \Redis();
            self::$write_handle->connect($option['host'], $option['port']);
        }
        return self::$write_handle;
    }

    public static function getRead()
    {
        $option = array(
            'host' => Config::get('cache.REDIS_R_HOST') ?? '127.0.0.1',
            'port' => Config::get('cache.REDIS_R_PORT') ?? 6379,
        );
        if (!self::$read_handle) {
            self::$read_handle = new \Redis();
            self::$read_handle->connect($option['host'], $option['port']);
        }
        return self::$read_handle;
    }

    /*
     * $key 键值
     * $value 值
     * $expire 过期时间 0是永久
     * */
    public static function set($key, $value, $expire = 3600)
    {
        if (!self::getWrite()) return false;
        if ($expire == 0) {
            return self::getWrite()->set($key, $value);
        } else {
            return self::getWrite()->setex($key, $expire, $value);
        }
    }

    public static function get($key)
    {
        $func = is_array($key) ? 'mget' : 'get';
        return self::getRead()->{$func}($key);
    }

    //-----Hash类型方法-------------------------------------------------------------------------------------------------------------
    /*
     * 自动递增
     * $number  步长
     * */
    public static function incr($key, $number = 1)
    {
        if (!self::getWrite()) return false;
        if ($number == 1) {
            return self::getWrite()->incr($key);
        } else {
            return self::getWrite()->incrBy($key, $number);
        }
    }

    public static function strlen($key)
    {
        return self::getRead()->strlen($key);
    }

    public static function hset($key, $field, $value)
    {
        return self::getWrite()->hSet($key, $field, $value);
    }

    public static function hget($key, $field = '')
    {
        if (empty($field)) {
            return self::getRead()->hGetAll($key);
        } else {
            return self::getRead()->hGet($key, $field);
        }
    }

    public static function hlen($key)
    {
        return self::getRead()->hLen($key);
    }

    /*
     * $number 是步长
     * */
    public static function hincrby($key, $field, $number = 1)
    {
        if (!self::getWrite()) return false;

        return self::getWrite()->hIncrBy($key, $field, $number);

    }
    //-----List类型方法-------------------------------------------------------------------------------------------------------------
    public static function lpush($key, $value)
    {
        if (!self::getWrite()) return false;
        return self::getWrite()->lPush($key, $value);

    }

    public static function lpop($key)
    {
        if (!self::getWrite()) return false;
        return self::getWrite()->lPop($key);
    }

    public static function rpop($key)
    {
        if (!self::getWrite()) return false;
        return self::getWrite()->rPop($key);
    }

    public static function llen($key)
    {
        if (!self::getWrite()) return false;
        return self::getRead()->lLen($key);
    }
}

②安装ab

测试环境是 Ubuntu

sudo apt-get install apache2-utils

方法一:

先把要秒杀的商品做成一个list,每次秒杀都删掉一个(lpop),当没有可以删掉的商品的时候,算秒杀失败

1,制作一个商品列表

    // 生成一个商品列表
    public function creat_goods_seckill()
    {
        $goods_id = 1; // 假设商品id是1
        $goods_count = 50; // 假设商品库存为50
        $cache_key = 'goods_seckill_list_' . $goods_id;
        for ($i = 1; $i <= $goods_count; $i++) {
            Redis::lpush($cache_key, $i);
        }
        return json(['info' => 'success', 'info_cn' => '秒杀设置成功', 'goods_id' => $goods_id]);
    }

查看redis,50个太长了,只看开头和结尾

可以看出确实是50个,那么商品列表正常

2,秒杀方法

public function miaosha()
    {
        $goods_id = 1; // 其实这是从外部接收的,这里偷个懒,直接写死
        $cache_key = 'goods_seckill_list_' . $goods_id;
        // 随机一个用户名,用password_hash生成,没什么意义,password_hash即使是相同的password 也会生成不同的hash值
        // 详情可以看官方文档 https://www.php.net/manual/zh/function.password-hash.php

        $uname = password_hash(time(), PASSWORD_DEFAULT); // 其实这个也是从登陆信息拿的,这里也是模拟下
        $user_goods_list_cache_key = 'user_goods_list' . $goods_id; // 秒杀成功用户列表
        $goods_top_id = Redis::lpop($cache_key);
        if ($goods_top_id>0) {
            Redis::hset($user_goods_list_cache_key,$goods_top_id,$uname);
        } else {
            Redis::incr('buy_false');
        }
    }

3,进行ab测试

ab -c 100 -n 10000 http://localhost/index/index/miaosha

 

get buy_false  

 

可以看到 失败的总数是 9950个

那成功的呢?

 

可以看到秒杀成功的是50个,那么9950 + 50 正好是10000个请求,在100并发量下(实际可能更高),redis秒杀是没有问题的 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: thinkphp5.1是一款基于PHP语言的MVC框架,它采用了Laravel框架中的一些优秀设计思想,并在此基础上进行了改进和优化,是一款颇受欢迎的PHP框架。 该框架具有非常完善的文档和教程,学习起来非常容易。它的核心思想是“约定优于配置”,通过一些默认的规则来简化代码,提高开发效率。同时,它也提供了很多实用的工具和函数,如请求对象、响应对象、验证器、缓存处理、日志记录等,使得开发者能够更加便捷地实现业务逻辑。 在源码方面,thinkphp5.1采用了比较优秀的代码结构和注释规范,代码风格清晰易读,符合现代编程的标准。同时,它也支持非常灵活的扩展机制,可以通过composer方式引入其他第三方组件来扩展功能。 总的来说,thinkphp5.1源码是一款非常优秀的PHP框架,具有清晰的代码结构、丰富的功能和非常好的可扩展性,适合开发各种规模的Web应用程序。 ### 回答2: ThinkPHP 5.1是一个基于MVC设计模式的PHP开源框架,它的源码是开放的,可以供开发者进行学习和使用。通过阅读ThinkPHP 5.1源码,开发者可以深入了解框架的核心设计思想和运行原理,从而更好地进行二次开发和定制。ThinkPHP 5.1源码的主要特点包括:使用面向对象的编程思想,提供了丰富的内置库函数和类库,支持依赖注入和控制反转,提供了强大的缓存机制,支持多种数据库操作,具有良好的安全性和可扩展性。此外,ThinkPHP 5.1还提供了详细的文档和示例代码,方便开发者学习和使用。总之,ThinkPHP 5.1源码为PHP开发者提供了强有力的支持,帮助开发者快速高效地开发出安全、稳定、高性能的web应用程序。 ### 回答3: thinkphp5.1源码是一个基于PHP的开发框架,它的思想是快速、可靠、简单、灵活,它提供了许多丰富的功能,包括模板引擎、数据库访问、缓存机制等等。 thinkphp5.1源码是一个开放源代码的框架,目前为止已经发展成为非常成熟的架构体系,拥有非常多的使用者和开发者,也得到了非常多的好评和支持。 thinkphp5.1源码的特点是快速开发、规范化开发、灵活、可扩展,它采用MVC的设计模式,在架构上很好地实现了分层开发原则,从而提高了开发效率和代码的可维护性。 此外,thinkphp5.1源码在数据库访问、缓存机制、模板引擎等方面也都有很好的支持,通过它们能够更好地管理数据、提高应用的性能和减少开发周期。 总之,thinkphp5.1源码具有很好的可用性和可扩展性,它已经成为PHP开发者中的主流框架之一,许多项目都已经采用了它的开发方式,包括PHP电商、PHP CMS等等,而且它也在不断不断地完善,使得它在未来的开发过程中仍然能保持其领先地位。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值