Laravel Transformer 重复查询的问题

之前一直觉得Laravel慢,确实比ThinkPHP之类的慢,用了Transformer以后,发现更慢。但是之前一直做项目,也没有去深究,隐隐约约中感觉,如果是一个列表,Transformer中的查询会重复查询。今天打印了一下数据库,确实是。但这功能用的人这么多,理论上不应该出现这样重大的性能消耗啊,百度了一波,发现确实提这个问题的人也很少。so,研究了一下,用到关系查询的时候,应该在查询时直接用With

同一个Transformer

//Transformer
public function transform(Administrator $model)
    {
        return [
            'id'         => (int) $model->id,
            'username'=>$model->username,
            'name'=>$model->name,
            /* place your other model properties here */
            'roles'=>$model->permissions,
            'created_at' => $model->created_at->toDateString(),
            'updated_at' => $model->updated_at->toDateString()
        ];
    }

使用with后只会产生两条查询

//控制器
$user = Administrator::with(['permissions'])->get();
return response()->json(Translate::collection($user,new UserTransformer()));
select * from `admin_users`
select `admin_permissions`.*, `admin_user_permissions`.`user_id` as `pivot_user_id`, `admin_user_permissions`.`permission_id` as `pivot_permission_id` from `admin_permissions` inner join `admin_user_permissions` on `admin_permissions`.`id` = `admin_user_permissions`.`permission_id` where `admin_user_permissions`.`user_id` in (1, 2, 3)

不使用with会产生N条查询

//控制器
$user = Administrator::get();
return response()->json(Translate::collection($user,new UserTransformer()));
select * from `admin_users`
select `admin_permissions`.*, `admin_user_permissions`.`user_id` as `pivot_user_id`, `admin_user_permissions`.`permission_id` as `pivot_permission_id` from `admin_permissions` inner join `admin_user_permissions` on `admin_permissions`.`id` = `admin_user_permissions`.`permission_id` where `admin_user_permissions`.`user_id` = 1
select `admin_permissions`.*, `admin_user_permissions`.`user_id` as `pivot_user_id`, `admin_user_permissions`.`permission_id` as `pivot_permission_id` from `admin_permissions` inner join `admin_user_permissions` on `admin_permissions`.`id` = `admin_user_permissions`.`permission_id` where `admin_user_permissions`.`user_id` = 2 
select `admin_permissions`.*, `admin_user_permissions`.`user_id` as `pivot_user_id`, `admin_user_permissions`.`permission_id` as `pivot_permission_id` from `admin_permissions` inner join `admin_user_permissions` on `admin_permissions`.`id` = `admin_user_permissions`.`permission_id` where `admin_user_permissions`.`user_id` = 3 

Nginx+Lua进行访问控制

背景

前阵子帮朋友做了个服务端,用来给他APP做一些基本的注册、付费等功能。但是他APP被盗版了,话说这盗版也是厉害,有时候注册量远远高于正版注册量,昨天盗版一天注册6000人,正版才2000人。于是乎服务器扛不住了,注册接口超时,客户端自动发起重连,又超时,又重连,如此往复,最后正常用户都没法访问了。

处理方案

要保证现有客户的能正常使用,还要限制盗版,那必须得知道盗版跟正版有啥区别了,检查过后,发现盗版应用在接口中有一个参数跟正版不一致。那当然得从参数上做限制。
两个方案:

  1. PHP接收到数据后,判断然后剔除。
  2. 使用Lua在Nginx层直接剔除

不用看,当然能在Web服务剔除最好了。为啥呢?如果在应用层提出,一样会进入PHPFPM消耗资源。

具体实施

安装Lua过程不表,可以参考https://segmentfault.com/a/1190000018641801

lua_need_request_body on;
location ~ \.php {
    access_by_lua '
        local uri = ngx.var.request_uri
        if string.find(uri,"/api/register") ~= nil then
            local body = ngx.req.get_body_data()    
            if string.find(body,"20200101") or string.find(body,"20200102") or string.find(body,"9.9.9") or string.find(body,"8.8.8") then
                ngx.exit(ngx.HTTP_NOT_FOUND)
            end
        end
    ';
    # 接着往下继续执行php,正常请求
}

这里提一个点access_by_lua用来进行访问控制,之前一直用content_by_lua_block,匹配成功以后,就不会继续执行php了。一定要用access_by_lua

网上是这么说的

access_by_lua 在请求访问阶段处理,用于访问控制,适用于 http、server、location、location if

content_by_lua 是内容处理器,接受请求并输出响应,适用于 location、location if

https://github.com/openresty/lua-nginx-module


2020年04月08日简单优化一下。随着盗版越来越多,盗版的关键字丢数组里

lua_need_request_body on;
location / {  
  access_by_lua '
    local uri = ngx.var.request_uri
    if string.find(uri,"/api/register") ~= nil then
        local body = ngx.req.get_body_data()
        local denyList = {"20200101","20200102","9.9.9","8.8.8"}
        for i = 1,#denyList do
            if string.find(body,denyList[i]) then
                ngx.header.content_type = "application/json";
                ngx.say(\'{"toast":"您正在使用盗版软件"}\');       
                ngx.exit(ngx.HTTP_OK);
            end
        end
    end
';
}  

细心的朋友会发现,上面的for循环中,i是从1开始的,没毛病,在很多语言中长度是从0开始,但是在lua里面,长度居然是从1开始。


2020年04月10日在优化一波,将所有处理失败的请求全部记录下来

在处理这个功能的时候,发现一个神奇的bug

在lua中,比如匹配8.8.8里面的居然会匹配成任意字符,也就是说,8d8c8也会被匹配成功,导致有一部分用户被误判。需要用%p来表示

将错误信息,写入文件中。

file = io.open("/tmp/lua.txt", "a")
io.output(file)
io.write(date)
io.write(body)
io.close(file)

以下是将lua脚本单独放在一个文件中的完整代码

lua_need_request_body on;
access_by_lua_file /usr/local/nginx/conf/lua.lua
local uri = ngx.var.request_uri
    if string.find(uri,"/api/register") ~= nil then
        local body = ngx.req.get_body_data()
        local denyList = {"20200101","20200102","9%p9%p9"}
        for i = 1,#denyList do
            if string.find(body,denyList[i]) ~= nil then
            local time = os.date("%Y-%m-%d %H:%M:%S")
                    local date = os.date("%Y-%m-%d")
                    local fileName = "/tmp/lua"..date..".txt"
            file = io.open(fileName, "a")
          io.output(file)
          io.write(time)
          io.write(body)
          io.write("\n")
          io.close(file)
                    ngx.header.content_type = "application/json";
          ngx.say(\'{"toast":"您正在使用盗版软件"}\');       
          ngx.exit(ngx.HTTP_OK);
            end
        end
    end

给自己打个气

八月了,今年一整年都不是很顺利,无论是工作还是生活,最近一直都状态也不是很好。一切都是有因果的,今天的一切都是昨天的造成的,所以我也在想这些年以来,我有没有哪一步是走错了,才导致今天这样的结果。想了一下,我觉得有两点

没有认识到工作的天花板

说白了就是年少不想事。从我大学毕业的时候,三叔就一直劝我叫我做生意,不要上班打工。而我一直喜欢写代码,一直都在老老实实写代码,从来没想过未来怎么办,那时候也觉得未来很远,还有这么长时间,够我挥霍。到现在年近三十,结婚即将生子,已经没有再挥霍的资本,才发现一切都已经晚了。
那么工作的天花板在哪里呢?其实就是收入,大部分人工作就是普普通通的一份工作而已,我们不去讨论那些大企业总裁级别的,岗位收入的天花板一直都会在那里,当你走到一定高度的时候,就是需要走出来,离开这个岗位,才可能达到另一个层次。就好像在南昌,程序员一万多的工资已经差不多到天花板了。而随着年龄的增长,需要花钱的地方也越来越多,一万的收入已经完全不足以支撑日常开支。

没有在一件事上坚持做下去

是的,没有任何一件事情,我虽然这两年尝试了很多事,但是没有任何一件事情,是花了80%的力气去做的,顶多当时做kindle推送、Youtube搬运的时候花了一点心思,那是能看到实实在在的钱嘛。而我没有在其他任何一件事情上,付出同样的力气,总是在做一些以为随便花点力气就能有不错回报的尝试,却从来不够深入。

人生总是起起伏伏,也会有不同的阶段,我一直记得当年在沃尔玛的时候,当时店总东哥说过一句话,销售不好的是就做标准。现在看这句话,就是在劝我们,脚踏实地,稳扎稳打。越是在低谷的时候,越是需要打起精神,不要让自己走错一步。人生啊,坚持做对的事情,哪怕只是一个很小的选择,坚持下去,一定会让一切走入正轨。

Centos intall Redis


wget http://download.redis.io/releases/redis-5.0.5.tar.gz
tar zxvf redis-5.0.5.tar.gz
mv redis-5.0.5 /usr/local/
cd /usr/local/redis
make
make test
make install
mkdir bin
cd src/
mv mkreleasehdr.sh redis-benchmark redis-check-aof redis-check-rdb redis-cli redis-server /usr/local/redis/bin/
mkdir ../conf
mv redis.conf conf/

├── bin
    ├── dump.rdb
    ├── mkreleasehdr.sh
    ├── redis-benchmark
    ├── redis-check-aof
    ├── redis-check-rdb
    ├── redis-cli
    └── redis-server
├── BUGS
├── conf
    ├── redis.conf
├── CONTRIBUTING
├── COPYING
├── sentinel.conf
├── src
├── tests
└── utils


cd conf/
vim redis.conf 
/usr/local/redis/bin/redis-server /usr/local/redis/conf/redis.conf