Openresty相关
刘梦凯 Lv3

使用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服务

1
nginx -p ./ -s stop

Nginx

nginx location

Location 块通过指定模式来与客户端请求的URI相匹配

location匹配顺序

nginx有两层指令来匹配请求 URI 。第一个层次是 server 指令,它通过域名、ip 和端口来做第一层级匹配,当找到匹配的 server 后就进入此 server 的 location 匹配。

location 的匹配并不完全按照其在配置文件中出现的顺序来匹配,请求URI 会按如下规则进行匹配:

  1. 先精准匹配 = ,精准匹配成功则会立即停止其他类型匹配;
  2. 没有精准匹配成功时,进行前缀匹配。先查找带有 ^~ 的前缀匹配,带有 ^~ 的前缀匹配成功则立即停止其他类型匹配,普通前缀匹配(不带参数 ^~ )成功则会暂存,继续查找正则匹配;
  3. =^~ 均未匹配成功前提下,查找正则匹配 ~~\* 。当同时有多个正则匹配时,按其在配置文件中出现的先后顺序优先匹配,命中则立即停止其他类型匹配;
  4. 所有正则匹配均未成功时,返回步骤 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 8m;
client_body_buffer_size 1m;
proxy_buffers 32 128k;
proxy_busy_buffers_size 1024k;
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查询就结束了。
  • Post title:Openresty相关
  • Post author:刘梦凯
  • Create time:2022-07-20 11:52:05
  • Post link:https://liumengkai.github.io/2022/07/20/Openresty相关/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.