高并发下jmeter性能压测及性能提升解决方案(六)查询优化之多级缓存:nginx lua(脏读忍受)

nginx lua

lua协程机制:

lua协程机制和nginx协程类似:线程空间站内的一个执行单元,类似于线程,有自己独立的运行空间,其空间是基于用户态模拟出来的一个独立的运行空间,依托于线程,也要像线程一样被CPU去执行。好处就是在编写代码时候不用考虑异步方式,完全同步的去编写。一旦协程遇到阻塞,比如IO调用,它会主动到nginx的epoll模型上注册异步回调的句柄,放弃自己的执行权限。然后当epoll模型接收到了IO请求阻塞调用返回之后,再将对应的协程唤醒,返回时即获得CPU执行权限。

nginx lua插载点:

nginx留给我们许多lua开发的挂载点。能在nginx不同生命周期阶段来完成一些lua脚本插件式的加载,并且执行lua程序。

 lua脚本可以挂载的点:

init_by_lua:系统启动时调用

init_worker_by_lua:worker进程启动时调用

set_by_lua:变量用复杂lua retrurn

rewrite_by_lua:重写url规则

access_by_lua:权限验证阶段

content_by_lua:内容输出节点

OpenResty框架:

OpenResty框架将nginx和nginx lua打包在一起,并提供了nginx lua的Redis,MemoryCache数据库等等这样的lua脚本库文件的封装。

协程机制:

依附于线程的内存模型,切换开销小,不会像线程切换一样耗CPU,内存

遇到阻塞及归还执行权,代码以同步方式来模拟一种异步的调用来编程

无需加锁

lua协程脚本:

function foo (a)
    print("foo 函数输出",a)
    return coroutine.yield(2*a) --返回2*a的值  yield:协程放弃权限,将权限归还给调用它的函数
end

co = coroutine.create(function(a,b)  //create:创建协程
    print("第一次协同程序执行输出",a,b)  --co-body 1 10
    lcoal r = foo(a+1)

    print("第二次协同程序执行输出",r)
    local r,s = coroutine.yield(a+b,a-b) --a,b的值为第一次调用协同程序时传入

    print("第三次协同程序执行输出",r,s)
    return b,"结束协同程序"  --b的值为第二次调用协同程序时传入
end)

print("main",coroutine.resume(co,1,10)) --true,4  resume:恢复协程
print("--分割线--")
print("main",coroutine.resume(co,"r")) --true 11 -9
print("--分割线--")
print("main",coroutine.resume(co,"x","y")) --true 10 end
print("--分割线--")
print("main",coroutine.resume(co,"x","y")) --cannot resume dead coroutine
print("--分割线--")

nginx协程:

1.nginx的每一个Worker进程都是在epoll或kqueue这种事件模型之上,封装成协程。当某一个socket句柄接收到httpRequest请求时,epoll上的这个socket就会被唤醒,这时nginx会new出一个协程,这个协程将会去处理这个http请求的完整的生命周期,也就是将http请求做一些urlLocation的路由,accessController的鉴权,UpStream的反向代理等操作,然后在这些操作内,如果没有遇到IO阻塞的情况,这个协程处理完成就会返回一个httpResponse给nginx的epoll模型做socket的write的输出,一旦在这个过程中遇到block操作,对应协程就会放弃执行权限,并且将对应的和后端反向代理服务器的scoket连接注册到对应的epoll事件里,等待这个事件被response唤醒,一旦response被唤醒,nginx的Worker进程又会重新new一个协程出来,做之后的事情,因此,每一个操作其实都不会block对应的Worker进程自身的操作,都是依赖于epoll操作去做的,但是在整个串行链路上看上去就是几个协程共同串行在一起,完成一个请求的处理过程

2.每一个请求都有一个协程进行处理。

3.即使nginx_lua必须要运行Lua,相对C有一定的开销,但依旧能保证高并发能力。

nginx协程机制:

1.nginx每个工作进程创建一个lua虚拟机,用来跑lua的脚本文件

2.工作进程内的所有协程共享同一个lua的虚拟机。

3.每一个外部请求由一个lua协程处理,之间数据隔离。

4.lua代码调用io等异步接口时,协程被挂起,上下文数据保持不变。一个httpRequest的请求进来后,被一个lua的协程代理,然后去执行相关操作,遇到io的阻塞,往epoll模型上注册完,自己挂起,等待epoll模型收到数据返回之后唤醒自己,执行下面操作,因此他们之间的数据是隔离的

5.自动保存,不阻塞工作进程

6.io异步操作完成后还原协程上下文,代码继续执行。

nginx协程机制和java多线程Servlet模型的区别:

一个httpRequest的请求进来后,被一个lua的协程代理,然后去执行相关操作,遇到io的阻塞,往epoll模型上注册完,自己挂起,等待epoll模型收到数据返回之后唤醒自己,执行下面操作,因此他们之间的数据是隔离的这也就是nginx处理http请求和javaServlet处理http请求完全不一样的模型。javaServlet是一个请求对应一个线程去处理的,但是nginx内是单Worker进程内是一个单线程的模型,因此他对每一个http请求完全是基于协程的方式串行处理,一旦遇到阻塞,通过io多路复用的epoll模型去解决,但是java内部遇到阻塞就被动在等,因为本身就是多线程的模型,因此相对对于这些模型来说,nginx协程机制是比java虚拟机多线程Servlet模型机制更加高效

nginx处理阶段:

NGX_HTTP_POST_READ_PHASE = 0  //读取请求头

NGX_HTTP_SERVER_REWRITE_PHASE  //执行rewrite -》rewrite_handler

NGX_HTTP_FIND_CONFIG_PHASE  //根据uri替换location

NGX_HTTP_REWRITE_PHASE  //根据替换结果继续执行rewrite -》rewrite_handler

NGX_HTTP_POST_REWRITE_PHASE  //执行rewrite后处理

NGX_HTTP_PREACCESS_PHASE  //认证预处理 请求限制,连接限制 -》limit_conn_handerlimit_req_handler

NGX_HTTP_ACCESS_PHASE  //认证处理 -》auth_basic_handler,access_handler

NGX_HTTP_POST_ACCESS_PHASE  //认证后处理,认证不通过,丢包

NGX_HTTP_TRY_FILES_PHASE  //尝试try标签

NGX_HTTP_CONTENT_PHASE  //内容处理- 》static_handler

NGX_HTTP_LOG_PHASE  //日志处理 -》 log_handler

nginx lua实战

在openresty文件下创建lua文件夹,把所有的lua脚本放入这个文件夹内。我们一般使用content_by_lua这个挂载点,来修改对应location某一个固定的url所产生的应用的lua编程:

修改nginx配置文件:

vim conf/nginx.conf

//加入新的location,配置staticitem/get
location /staticitem/get{
    default_type "text/html";  //不指定格式会以文件形式输出
    content_by_lua_file ../lua/staticitem.lua;  //配置文件地址
}

进入lua文件夹:

vim staticitem.lua

ngx.say("heelo static item lua");  //以httpResponse方式返回这句

重启nginx配置:

sbin/nginx -s reload

 测试:

openResty

openResty由Nginx核心加很多第三方模块组成,默认集成了Lua开发环境,使得Nginx可以作为一个Web Server使用。

借助于Nginx的事件驱动模型和非阻塞IO,可以实现高性能的Web应用程序。

openResty提供了大量组件如Mysql,Redis,Memcached等等。使在Nginx上开发Web应用简单。

OpenResty实战

1.openresty hello world

进入openresty/lua,编写helleworld文件:

vim helloworld.lua

ngx.exec("/item/get?id=6");  //商品详情页的访问

 进入openresty/nginx,修改conf文件:

vim conf/nginx.conf

location /helloworld{
    content_by_lua_file ../lua/helloworld.lua;
}

重启nginx配置:

sbin/nginx -s reload

 测试访问helloworld: 

2.shared dic:共享内存字典,所有的worker进程可见,lru淘汰(类似与guava cache)

在nginx配置文件中配置shared dic:

vim conf/nginx.conf

lua_shared_dict my_cache 128m;  //指定名称和开辟大小

 在lua文件中编写itemSharedic文件:

vim itemsharedic.lua

function get_from_cache(key)
        local cache_ngx = ngx.shared.my_cache  //定义local变量取自my_cache中
        local value = cache_ngx:get(key)  //获取value
        return value
end

function set_tocache(key,value,exptime)
        if not exptime then 
                exptime = 0;
        end
        local cache_ngx = ngx.shared.my_cache
        local succ,err,forcible = cache_ngx:set(jey,value,exptime)  //定义返回值
        retrun succ
end

local args = ngx.req.get_uri_args()  //可以拿到对应Request请求上的一个参数
local id = args["id"]
local item_model = get_from_cache("item_"..id)  //".."相当于拼接字符串
if item_model == uil the n
        local resp = ngx.location.capture("/item/get?id="..id)  //缓存请求不到时转发给后台
        item_model = resp.body
        set_to_cache("item_"..id,item_model,1*60)  //缓存1min            
end
ngx.say(item_model)

 编辑nginx.conf文件:

vim conf/nginx.conf

server{
    location /luaitem/get{
        default_type "applicatoin/json";
        content_by_lua_file ../lua/itemsharedic.lua  //将luaitem/get的请求方式代理到了../lua/itemsharedic.lua这个lua脚本上
    }
}

重启nginx配置:

sbin/nginx -s reload

查看启动日志:

 进入ngixn的logs目录执行:

tail -f error.log

测试:

验证nginx收到请求:

进入nginxlogs文件下执行:

tail -f access.log

可以看到请求 

但是在项目服务器中的tomcat文件中查看日志:

tail -f access_log.yyyy-mm-dd.log

可以看到没有请求到达。

进行jmeter压测:

以之前设置的并发参数请求,可以看出TPS可达3700,平均响应页很低

使用top -H也可以看到压力主要在nginx服务器上,程序服务器在1min内压力并不大。

3.openresty redis支持

架构图:

 可以避免部分脏读的影响。

进入lua目录:

vim itemredis.lua

local args = ngx.req.get_uri_args()
local id = args["id"]
local redis = require "resty.redis"
local cache = redis.new()
local ok,err = cache:connect("ip",port)
local item_model = cache:get("item_"..id)
if item_model == ngx.null or item_model == nil then 
    local resp = ngx.location.capture("/item/get?id="..id)
    item_model = resp.body
end

返回上层修改nginx/conf/nginx,conf

server{
    location /luaitem/get{
        default_type "application/json";
        content_by_lua_life ../lua/itemredis.lua;
    }
}

重启nginx配置:

sbin/nginx -s reload

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值