最近做项目,以openresty为基础,在upstream侧通过nginx的指令开启SSL连接复用,但实际是不生效的,通过使用原生的程序nginx、openresty进行复现,明白了问题所在,也更加理解openresty与nginx的共性和特性。
自己在服务器中利用未经修改的nginx、openresty测试,编译的时候开启debug,能够在debug中看到upstream侧save、set session的操作。
nginx
简单的配置如下,upstream侧 ssl session reuse默认开启。
upstream web_server {
server 127.0.0.1:13579;
}
server {
listen 1234;
location / {
proxy_pass https://web_server;
proxy_http_version 1.1;
}
}
通过debug观察,upstream侧连接SSL连接建立成功后,会有save session的操作。
[debug] 32028#0: *1 SSL handshake handler: 0
[debug] 32028#0: *1 SSL_do_handshake: 1
[debug] 32028#0: *1 SSL: TLSv1.2, cipher: "ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD"
[debug] 32028#0: *1 http upstream ssl handshake: "/?"
[debug] 32028#0: *1 save session: 000000000217C970
[debug] 32028#0: *1 http upstream send request
[debug] 32028#0: *1 http upstream send request body
[debug] 32028#0: *1 chain writer buf fl:1 s:92
[debug] 32028#0: *1 chain writer in: 0000000002154C78
[debug] 32028#0: *1 malloc: 00000000021EBAE0:80
openresty环境 加入balance.lua脚本 未选peer
balance.lua 只是简单的引入balance_by_lua_file的指令,lua脚本为空,让其运行
upstream webserver {
server 127.0.0.1:13579;
balancer_by_lua_file `pwd`/balance.lua;
}
通过调试观察,save session可以得到执行
[debug] 1908#0: *1 SSL handshake handler: 0
[debug] 1908#0: *1 SSL_do_handshake: 1
[debug] 1908#0: *1 SSL: TLSv1.2, cipher: "ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD"
[debug] 1908#0: *1 http upstream ssl handshake: "/?"
[debug] 1908#0: *1 lua balancer save session
[debug] 1908#0: *1 save session: 000000000197D970
[debug] 1908#0: *1 http upstream send request
[debug] 1908#0: *1 http upstream send request body
openresty环境 加入balance.lua脚本 选出peer
在上面的脚本中,强制返回选出的upstream主机
ngx.log(ngx.ERR, ip, "balance session store")
local balancer = require "ngx.balancer"
local backend="127.0.0.1"
local port=13579
local ok, err = balancer.set_current_peer(backend, port)
if not ok then
ngx.log(ngx.ERR, "failed to set the current peer: ", err)
return ngx.exit(500)
end
观察debug信息,在ssl握手完成后,没有保存ssl session的操作
[debug] 3122#0: *5 SSL handshake handler: 0
[debug] 3122#0: *5 SSL_do_handshake: 1
[debug] 3122#0: *5 SSL: TLSv1.2, cipher: "ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD"
[debug] 3122#0: *5 http upstream ssl handshake: "/?"
[debug] 3122#0: *5 lua balancer save session
[debug] 3122#0: *5 http upstream send request
[debug] 3122#0: *5 http upstream send request body
[debug] 3122#0: *5 chain writer buf fl:1 s:92
通过深入openresty中balance_by_lua的源码查看
在初始化时,会将upstream侧的session操作替换为自己的。
ngx_http_lua_balancer_init_peer() {
r->upstream->peer.set_session = ngx_http_lua_balancer_set_session;
r->upstream->peer.save_session = ngx_http_lua_balancer_save_session;
}
在处理阶段,通过balance的lua脚本,如果正确调用了balancer.set_current_peer
则会将bp中的sockaddr与socklen设置上,从而直接返回,如果没有,则走nginx原先的流程。
ngx_http_lua_balancer_get_peer()
{
rc = lscf->balancer.handler(r, lscf, L);
if (bp->sockaddr && bp->socklen) {
return NGX_OK;
}
return ngx_http_upstream_get_round_robin_peer(pc, &bp->rrp);
}
再看看替换的读取与设置ssl session的回调,如果是lua脚本选出的peer,则直接返回,其实可以在这里加入自控session reuse的代码。否则走原先nginx的代码。
ngx_http_lua_balancer_save_session
{
if (bp->sockaddr && bp->socklen) {
/* TODO */
return;
}
return ngx_http_upstream_save_round_robin_peer_session(pc, &bp->rrp);
}
为什么这么处理,算是bug还是未做完的开发?深入想想,这应该是周全的设计。
nginx中的配置都是静态的,upstream侧有多少台主机都可知,session的存储与具体的主机关联即可,但是openresty是动态的,选择出的主机与nginx的配置没有任何关系,信息的存储需要自己编写脚本代码实现。通过代码也可以看出,当balance的lua脚本没有得出主机,则继续执行nginx原有的流程。