使用Openresty实现druid访问字段热度分析
安装Openresty 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 brew install openresty/brew/openresty # 出现下面的提示安装成功 You can find the configuration files for openresty under /opt/homebrew/etc/openresty/. To start openresty/brew/openresty now and restart at login: brew services start openresty/brew/openresty Or, if you don't want/need a background service you can just run: openresty ==> Summary 🍺 /opt/homebrew/Cellar/openresty/1.21.4.1_1: 307 files, 7.1MB, built in 40 seconds ==> Running `brew cleanup openresty`... Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP. Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`). ==> Caveats ==> openresty You can find the configuration files for openresty under /opt/homebrew/etc/openresty/. To start openresty/brew/openresty now and restart at login: brew services start openresty/brew/openresty Or, if you don't want/need a background service you can just run: openresty
配置Openresty环境 1 2 3 4 5 6 7 8 9 10 11 编辑 ~/.zshrc 增加以下内容 需要注意的是如果你原先安装过nginx,那export openresty下的nginx环境的时候需要将它写到 $PATH 前面 这样nginx才用的是openresty中的 # openresty OPENRESTY_HOME=/opt/homebrew/Cellar/openresty/1.21.4.1_1 export PATH=$PATH:$OPENRESTY_HOME/bin export PATH=$OPENRESTY_HOME/nginx/sbin:$PATH (base) ➜ which nginx /opt/homebrew/Cellar/openresty/1.21.4.1_1/nginx/sbin/nginx
测试openresty 创建本地项目目录,这里假设为operesty
1 2 3 mkdir /Users/liu/Documents/openresty mkdir /Users/liu/Documents/openresty/conf mkdir /Users/liu/Documents/openresty/log
conf下创建nginx.conf文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 worker_processes 1; error_log log/error.log; events { worker_connections 1024; } http { server { listen 8080; location / { default_type text/html; content_by_lua_block { ngx.say("<h2>hello</h2>") } } } }
使用下面的命令在后台启动nginx服务,启动后使用ps命令可以查看nginx的master和worker进程:
1 2 3 4 5 6 cd openresty nginx -p ./ -c conf/nginx.conf ps -ef | grep nginx nginx: master process nginx -p ./ -c conf/nginx.conf nginx: worker process
在本机上访问8080端口
测试之后停止nginx服务
Nginx nginx location Location 块通过指定模式来与客户端请求的URI相匹配
location匹配顺序 nginx有两层指令来匹配请求 URI 。第一个层次是 server 指令,它通过域名、ip 和端口来做第一层级匹配,当找到匹配的 server 后就进入此 server 的 location 匹配。
location 的匹配并不完全按照其在配置文件中出现的顺序来匹配,请求URI 会按如下规则进行匹配:
先精准匹配 =
,精准匹配成功则会立即停止其他类型匹配;
没有精准匹配成功时,进行前缀匹配。先查找带有 ^~
的前缀匹配,带有 ^~
的前缀匹配成功则立即停止其他类型匹配,普通前缀匹配(不带参数 ^~
)成功则会暂存,继续查找正则匹配;
=
和 ^~
均未匹配成功前提下,查找正则匹配 ~
和 ~\*
。当同时有多个正则匹配时,按其在配置文件中出现的先后顺序优先匹配,命中则立即停止其他类型匹配;
所有正则匹配均未成功时,返回步骤 2 中暂存的普通前缀匹配(不带参数 ^~
)结果
1 2 3 4 5 6 1. location = 2. location ^~ 3. location ~ 4. location ~* 5. location /a 6. location /
nginx mirror 利用 mirror 模块,业务可以将线上实时访问流量拷贝至其他环境,基于这些流量可以做一些关于流量的测试或者其他操作,这里我们将流量复制出来,然后解析其中的字段,核心配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 server { ... location / { mirror /mirror; # 将流量复制到/mirror下 mirror_request_body on; proxy_pass http://druid_broker; # 将流量复制完毕之后继续传递给主服务提供查询 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location = /mirror { internal; # 内部服务 外部访问会出现404 proxy_pass http://127.0.0.1:28082$request_uri; # 镜像服务 操作镜像流量 proxy_pass_request_body on; proxy_set_header X-Original-URI $request_uri; } ... }
如何引用第三方resty库 在Openresy中引用第三方lua库非常简单,只需要将对应的lua文件拷贝到指定目录。
resty.http 我们使用openresty发起http请求的时候需要额外的resty.http包,需要将这个连接 下 lua-resty-http/lib/resty/
目录下的 http.lua 和 http_headers.lua 两个文件拷贝到openresty安装目录下{OPENRESTY_HOME}/lualib/resty
目录下即可
include mime.types;报错 参考这个连接 解决了这个问题,如果我们在启动nginx的时候出现这个问题是因为,我们openresty自带的nginx中是没有mime.types
这个文件,从github上将这个文件薅下来即可
Nginx中upstream 目前我理解的nginx中的upstream是为了分散请求压力,并且如果一台机器的服务挂了,nginx会自动检测到并且将需求全部转到另一台服务器,做到一定的高可用,但是如果nginx都挂了,这时候keepalived可以保证nginx的高可用(我还没用过)。
网上查阅nginx upstrem相关资料的时候发现除了最基本的轮询,还可以根据weight(权重),ip_hash,fair(公平地按照后端服务器的响应时间来分配请求,响应时间短即rt小的后端服务器优先分配请求),url_hash(也可解决session问题,但需要注意,使用这种方式后,server语句中不能写入weight等其他参数)
1 2 3 4 5 6 7 8 9 10 11 upstream backend { server 192.168.1.128:8081 weight=1; // 如果这个服务器挂掉了请求会自动传递给下面那个 server 192.168.1.128:8082 weight=2; } server { listen 80; server_name 0.0.0.0; location / { proxy_pass http://backend; } }
Nginx + Openresty 监控 druid查询热度 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 server { listen 80 ; client_max_body_size 8 m; client_body_buffer_size 1 m; proxy_buffers 32 128 k; proxy_busy_buffers_size 1024 k; access_log logs/druid_broker.log main; lua_need_request_body on; location / { mirror /mirror; mirror_request_body on; proxy_pass http://druid_broker; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 需要 resty.http mo location = /check { content_by_lua_block { local httpc = require ("resty.http" ).new() local res, err = httpc:request_uri("http://127.0.0.1:8082/druid/v2/sql/" , { method = "POST" , body = '{"query": "SELECT CURRENT_TIMESTAMP"}' , headers = { ["Content-Type" ] = "application/json" } }) if err then ngx.status = 500 else ngx.status = 200 end ngx.header.content_type = 'application/json' ngx.say(res.body) ngx.eof() } } location = /mirror { # 这块是将上面复制到/mirror中的流量发送到28082 端口,下面server会处理这个端口接收到的数据 internal; proxy_pass http://127.0 .0 .1 :28082 $request_uri; proxy_pass_request_body on; proxy_set_header X-Original-URI $request_uri; } } server { listen 127.0 .0 .1 :28082 ; lua_need_request_body on; location / { content_by_lua_block { local dataSource='' ; local request_uri = ngx.var.request_uri local data = ngx.req.get_body_data() if string .find (request_uri, "^/druid/v2/sql" ) then # 如果url是sql结尾的 ,那么需要进行解析 并且将字段发送到kafka中 data = string .lower (data) data = string .gsub (data, '\\n' , ' ' ) data = string .gsub (data, '/[*].*[*]/' , ' ' ) fromIndex, endIndex = string .find (data, "from +[%a_]+[ ]" ) if fromIndex ~= nil then dataSource = string .gsub (string .sub (data, fromIndex+4 , endIndex), '%s' , '' ) end else local json = require "cjson.safe" jsonBody = json.decode(data) if jsonBody.dataSource ~= nil then dataSource = jsonBody.dataSource end end local producer = require "resty.kafka.producer" local broker_list = { { host = "kfk01" , port = 9092 }, { host = "kfk02" , port = 9092 }, { host = "kfk03" , port = 9092 } } if dataSource ~= '' then local bp = producer:new(broker_list, { producer_type = "async" , flush_time= '5000' }) local ok, err = bp:send("druid_monitor" , nil , '{"ts":' .. ngx.time () ..',"datasource":"' ..dataSource..'"}' ) # 这行将数据源发送到kafka中 topic名称为druid_monitor if not ok then ngx.log (ngx.ERR, 'kafka send err:' , err) end end ngx.say('ok' ) } } } upstream druid_broker { server adt-bd-c1-druid-broker01.adtiming.int:8082 max_fails=3 fail_timeout=60 ; server adt-bd-c1-druid-broker02.adtiming.int:8082 max_fails=3 fail_timeout=60 ; } 到此为止openresty监控druid查询就结束了。