OpenResty单端口多协议支持研究
需求背景
443端口需要提供2种协议。HTTPS和一种额外的私有协议。私有协议还需要通过sendmsg将fd发送给专门处理私有协议的服务。
方案1
一开始,想在openresty的balancer模块中实现。但发现不行,因为balancer的上下文中没办法预计一部分数据。
有人提了issue,官方还没回应。https://github.com/openresty/lua-nginx-module/issues/2330
方案2
利用preread_by_lua_* gateway能力,它可以通过cosocket:peek预读一部分tcp数据。
还要可以获取到底层连接的FD。
社区有人提了PR,https://github.com/openresty/lua-nginx-module/pull/1147/files。但官方回复说这会破坏底层的IO封装,建议通过ngx.semaphore + lua_shared_dict + ngx.sleep来实现。
一开始没加思考完全照抄了其中推荐的ffi函数 ngx_http_lua_ffi_socket_tcp_sslhandshake。但测的时候发现获取的fd并不对。不知道原因。
调试了很久,反应过来了:不应该访问http的接口,因为此时连接刚建立,处于stream的上下文中,fd.c中按照http请求的结构体定义不对,应该按照stream的。
找了半天没找到ngx 头文件中的对应结构体,后来发现openresty项目 https://github.com/openresty/stream-lua-nginx-module/blob/master/src/ngx_stream_lua_request.h#L22中有定义。直接修改下,就拿到正常的fd。
local ngx = require 'ngx'
local base = require "resty.core.base"
base.allows_subsystem('http', 'stream')
local r = base.get_request()
local ffi = require 'ffi'
local C = ffi.C
local lib = ffi.load('/tmp/fd.so')
ffi.cdef[[
long ngx_http_lua_ffi_socket_fd(void *r);
int lua_ffi_socket_fd(void *r);
]]
-- local sock = ngx.req.socket()
-- local tcp_sock = sock[1]
-- local fd = C.ngx_http_lua_ffi_socket_fd(r)
local fd = lib.lua_ffi_socket_fd(r)
-- ngx.log(1, "hi", tcp_sock)
ngx.log(1, string.format("fd is: %d", fd))
fd.so的源码
fd.c
#include "header.h"
#include <stddef.h>
int lua_ffi_socket_fd(ngx_http_request_t *r)
{
if (r == NULL)
return -1;
return r->connection->fd;
}
header.h
typedef struct {
void* data;
void* data1;
void* data2;
int fd;
} ngx_connection_t;
typedef struct {
ngx_connection_t *connection;
} ngx_http_request_t;
这样是为了不修改openresty源码,只要结构体的成员偏移量与openresty中保持一致,即可获取到正确的fd。
最终完整的方案如下。剩下的就是在test.lua中,识别是私有协议,把连接转走即可。具体可以参考社区的好心人提供的示例:https://github.com/gustavosbarreto/websocket-fd-handoff
stream {
server {
listen 1080;
proxy_pass 127.0.0.1:80;
preread_by_lua_file test.lua;
}
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
...
}

浙公网安备 33010602011771号