标签: ThinkPHP

  • php下用redis解决秒杀超卖问题

    php下用redis解决秒杀超卖问题

    秒杀超卖问题,就是有一个商品抢购活动,一个商品假如有100件库存,但是在抢购时有200人来抢购,这时就会并发,原本只有100的库存但是抢购的人过多,就会发生数据库里原本只有100的库存但是库存为0的时候还会有人提交成功,这就是超卖。

      今天简单的用redis的队列来解决超卖问题。因为redis有list类型,list类型其实就是一个双向链表。通过push,pop操作从链表的头部或者尾部添加删除元素。这使得list既可以用作栈,也可以用作队列。先进先出  一端进  一端出这就是队列。这里用redis就解决了并发的问题,在队列里前一个走完之后,后一个才会走。

      实现原理

       将库存循环lpush进一个redis值goods_number里去,然后在下单的时候依次rpop出来。这样就是下一个单取出来一个,然后等goods_number的值为0时,停止下单。

      首先建立三个表 store商品表  order订单表  log日志表

    CREATE TABLE `store` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `goods_id` int(11) NOT NULL,
      `sku_id` int(10) unsigned NOT NULL DEFAULT '0',
      `number` int(10) NOT NULL DEFAULT '0',
      `freez` float(11,2) NOT NULL DEFAULT '0.00' COMMENT '虚拟库存',
      `price` int(10) NOT NULL COMMENT '价格:单位为分',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='库存';
     
    CREATE TABLE `order` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `order_sn` char(32) NOT NULL,
      `user_id` int(11) NOT NULL,
      `status` int(11) NOT NULL DEFAULT '0',
      `goods_id` int(11) NOT NULL DEFAULT '0',
      `sku_id` int(11) NOT NULL DEFAULT '0',
      `number` int(11) NOT NULL,
      `price` int(10) NOT NULL COMMENT '价格:单位为分',
      `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4743 DEFAULT CHARSET=utf8 COMMENT='订单表';
     
    CREATE TABLE `order` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `order_sn` char(32) NOT NULL,
      `user_id` int(11) NOT NULL,
      `status` int(11) NOT NULL DEFAULT '0',
      `goods_id` int(11) NOT NULL DEFAULT '0',
      `sku_id` int(11) NOT NULL DEFAULT '0',
      `number` int(11) NOT NULL,
      `price` int(10) NOT NULL COMMENT '价格:单位为分',
      `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=4743 DEFAULT CHARSET=utf8 COMMENT='订单表';

    代码如下:

    <?php
    namespace Home\Controller;
    use Think\Cache\Driver\Redis;
    use Think\Controller;
    class TestController extends Controller
    {
        public function index()
        {
            $wheres = array();
            $wheres['goods_id'] = 1;
            $number = M('store')->where($wheres)->getField('number');
            $redis = new Redis();
            for ($i = 0; $i < $number; $i++) {
                $redis->lpush('goods_number', 1);
            }
            echo $redis->llen('goods_number');
        }
        //生成唯一订单号  
        function build_order_no()
        {
            return date('ymd') . substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
        }
        //记录日志  
        function insertLog($event, $type = 0)
        {
            $data['event'] = $event;
            $data['type']  = $type;
            $res = M('log')->add($data);
        }
        //模拟下单操作  
        //下单前判断redis队列库存量  
        function order()
        {
            $sku_id  = 11;  //传入已知的sku_id;
            $wheres = array();
            $wheres['sku_id'] = $sku_id;
            $good_info = M('store')->where($wheres)->find();
            $user_id  = rand(1, 200);
            $goods_id = $good_info['goods_id'];
            $price    = $good_info['price'];
            $number   = 1; //抢购时每次买一件商品
    
            $redis = new Redis();
            $count = $redis->rpop('goods_number');  //下单时做rpop 从goods_number中取出1
            if ($count == 0) {
                $this->insertLog('error:no goods_number redis');
                return;
            }
    
            if (($good_info['number'] - $number) <= 0) {
                $this->insertLog('商品售罄');  //如果库存为0写入日志 并停止下单操作
                return;
            }
    
            //生成订单    
            $order_sn = $this->build_order_no();
            $data = array();
            $data['order_sn'] = $order_sn;
            $data['user_id']  = $user_id;
            $data['goods_id'] = $goods_id;
            $data['sku_id']   = $sku_id;
            $data['number']   = $number;
            $data['price']    = $price;
            $order_rs = M('order')->add($data);
            //库存减少  
            $wheres['sku_id'] = $sku_id;
            $store_rs = M('store')->where($wheres)->setDec('number', $number);
            if ($store_rs) {
                $this->insertLog('库存减少成功');
            } else {
                $this->insertLog('库存减少失败');
            }
        }
    }
    

    先访问:/test/index 将redis的list初始化

    然后可以访问:/test/order 进行模拟

  • TP5接收参数防止sql注入设置方法

    TP5接收参数防止sql注入设置方法

    在 application/config.php 中有个配置选项
    框架默认没有设置任何过滤规则,若配置文件中设置全局的过滤规则
    则会调用这些函数 自动过滤

    设置代码如下:

    // 默认全局过滤方法 用逗号分隔多个'
    default_filter' => 'htmlspecialchars,addslashes,strip_tags', 
    //参数含义:
    //htmlspecialchars:防XSS攻击,尖括号等转义过滤
    //addslashes:防SQL注入,在每个双引号(")前添加反斜杠
    //strip_tags:剥去字符串中的 HTML 标签

    设置前接收到的参数示例:

    设置后接收到的参数示例:

  • 靖江市豚之杰食品有限公司食品追溯系统

    靖江市豚之杰食品有限公司食品追溯系统

    靖江市豚之杰食品有限公司防伪码追溯系统

    网站由OneThink 驱动,老季负责技术支持。

  • Amh 面板 Nginx php-fpm Thinkphp Rewrite

    Amh 面板 Nginx php-fpm Thinkphp Rewrite

    前面我们介绍了?Nginx 下 ThinkPHP URL 的 PATHINFO 与 REWRITE 重写 配置方法,那么我们说一下在Amh面板中的使用方法

    Thinkphp REWRITE模式 2:
    该URL模式和PATHINFO模式功能一样,除了可以不需要在URL里面写入口文件,和可以定义.htaccess 文件外。在开启了Apache的URL_REWRITE模块后,就可以启用REWRITE模式了,具体参考下面的URL重写部分。四、兼容模式: 设置URL_MODEL 为3
    兼容模式是普通模式和PATHINFO模式的结合,并且可以让应用在需要的时候直接切换到PATHINFO模式而不需要更改模板和程序,还可以和URL_WRITE模式整合。兼容模式URL可以支持任何的运行环境。
    兼容模式的效果是:
    http://serverName/appName/?s=/module/action/id/1/
    并且也可以支持参数分割符号的定义,例如在URL_PATHINFO_DEPR为~的情况下,下面的URL有效:
    http://serverName/appName/?s=module~action~id~1

    ThinkPHP提供的Nginx环境配置情况
    在Nginx低版本中,是不支持PATHINFO的,但是可以通过在Nginx.conf中配置转发规则实现:

    location / { // …..省略部分代码
        if (!-e $request_filename) {
            rewrite ^(.*)$ /index.php?s=$1 last;
            break;
        }
    }

    其实内部是转发到了ThinkPHP提供的兼容模式的URL,利用这种方式,可以解决其他不支持PATHINFO的WEB服务器环境。

    AMH面板中的操作流程:
    1、在/usr/local/nginx/conf/rewrite下面新建一个规则文件thinkphp.conf

    2、将内容修改为如下:

    location / { 
        if (!-e $request_filename) { 
            rewrite ^(.*)$ /index.php?s=$1 last; 
            break; 
        } 
    }

    3、在面板编辑主机选择Rewrite规则:thinkphp.conf 就OK啦!

  • Nginx 下 ThinkPHP URL 的 PATHINFO 与 REWRITE 重写 配置方法

    尝试了下 ThinkPHP 框架, 版本为 3.2.3。本文内容为解决 Nginx 下 ThinkPHP URL 模式中所用到的 PATHINFO 与 REWRITE 模式的配置问题。并在 Mac OS X 10.10 下 Nginx 1.8.0 + PHP 5.6.8 下测试通过。理论同样适用于各版本的 Linux 系统中。

    PATHINFO 与 REWRITE 模式在表现上的不同在于浏览器的 URL 地址栏中, 以访问 Home 模块, User 控制器, login 操作为例:

    PATHINFO 下的 URL: http://laoji.org/index.php/Home/User/login
    REWRITE 下的 URL: http://laoji.org/Home/User/login

    Nginx 开启 PATHINFO

    在这之前, 笔者也到搜索引擎上看了几篇关于 Nginx 配置 PATHINFO 的方法, 感觉有点乱, 乱的原因主要是由于 Nginx 没有给予像 Apache 那样一个参数即可开启 PATHINFO 的良好支持, 因此出现了各种 Nginx 下开启 PATHINFO 的解法。

    这里提供的是参阅 Nginx 官方文档并结合实测可用后的 PATHINFO 的方案。去除不必须的注释, Nginx 开启 PATHINFO 的 server 部分配置如下:

    server {
    listen 80;
    server_name localhost;

    location / {
    root /wwwroot/laoji.org; # 你的 TP 框架 index.php 所在目录, 记得用 / 分割路径
    index index.php index.html index.htm;
    }

    # …

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ ^(.+\.php)(.*)$ {
    fastcgi_split_path_info ^(.+\.php)(.*)$;
    fastcgi_param PATH_INFO $fastcgi_path_info;

    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME? $document_root$fastcgi_script_name;
    include fastcgi_params;
    }
    }

    这种做法的原理是当请求的访问路径中含有 .php 时, 通过正则表达式构造出 PATHINFO, 并设置到 fastcgi 的参数 PATH_INFO 中。

    代码中匹配 PATH_INFO 的正则表达式来源于 Nginx 官方文档中的写法。参见:http://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_split_path_info

    PATHINFO 模式是 ThinkPHP 默认的 URL 模式, 因此不需要修改 ThinkPHP 的默认配置即可使用http://serverName/index.php/模块/控制器/操作 方式访问。


    URL REWRITE 模式

    REWRITE 模式也称 URL重写, 可用于隐藏 PATHINFO 模式路径中的 index.php, 开启 REWRITE 模式的 Nginx 配置为:

    server {
    listen 80;
    server_name laoji.org;

    location / {
    root /wwwroot/laoji.org;
    index index.php index.html index.htm;
    try_files $uri $uri/ /index.php?s=$uri; # 核心
    }

    # …

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    fastcgi_param SCRIPT_FILENAME? $document_root$fastcgi_script_name;
    include fastcgi_params;
    }
    }

    配置完成后修改 ThinkPHP 的 URL 模式为 REWRITE, 编辑配置文件 ThinkPHP/Conf/convention.php 中修改 URL_MODEL 参数值为 2 (REWRITE 模式)即可通过 http://serverName/模块/控制器/操作 方式访问。

WeChat