0x00 среда
Я установил версию 1.5 apisix из исходного кода на macos, etcd и другие службы развернуты с помощью docker-compose, у apisix есть руководство по развертыванию докеров. Развертывание среды здесь не описывается.
исходный код apix v1.5:GitHub.com/Apache/API…
Код отладки, который я использовал во время анализа:GitHub.com/Тайчжоу Сангграсу…Эта часть кода делит процесс запуска на несколько шагов и подготавливает среду отладки для облегчения пошаговой отладки и уточнения процесса запуска.
0x01 начало
Среда установки apisix находится в/usr/local/Cellar/apisix/apache-apisix-1.5
, команда запуска./bin/apisix restart
Команда запуска соответствует/usr/local/Cellar/apisix/apache-apisix-1.5/bin/apisix
скрипт, первая строка кода скрипта
#!/usr/bin/env lua
В Unix-подобных операционных системах добавьте первую строку скрипта#!(это называется шебанг или фунт бенг), за которым следует абсолютный путь интерпретатора, используемого для интерпретации скрипта, но, очевидно, за ним не следует абсолютный путь.
env — это исполняемая команда, которая сообщает операционной системе о необходимости поиска в переменной окружения $PATH текущей системы.
#查看PATH环境变量
echo $PATH
#输出
/usr/local/opt/openssl@1.1/bin:/usr/local/Cellar/openresty@1.15.8.3/1.15.8.3/openresty/luajit/bin:/usr/local/Cellar/apisix/apache-apisix-1.5/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/Applications/Wireshark.app/Contents/MacOS
env lua
: относится к поиску интерпретатора (инструкции) с именем lua по пути Path, настроенному в текущей среде env.
#查看lua指令位置
which lua
#输出
/usr/local/Cellar/apisix/apache-apisix-1.5
Как видите, lua находится в переменной окружения $PATH. Если интерпретатор lua в этом пути удален, а в других путях нет интерпретатора lua, то при запуске apisix будет выдано сообщение об ошибке
env: lua: No such file or directory
Основное преимущество такого способа записи состоит в том, чтобы сделать этот сценарий применимым к разным системам, разным средам и разным разрешениям пользователей, если программа-интерпретатор правильно записывает свой собственный путь в переменную окружения $PATH.
0x02 start
Функция, соответствующая команде запуска
function _M.start(...)
-- check running
local pid_path = apisix_home .. "/logs/nginx.pid"
local pid, err = read_file(pid_path) -- 读取/usr/local/apisix/logs/nginx.pid
……
end
Приведенный выше код включаетapisix_home
Он был определен в другом месте скрипта, поэтому причина размещенияapisix_home
Переменная вынесена отдельно, так как эта переменная является текущим каталогом установки apisix.Получить это значение громоздко, так как в разных системах и разных методах установки каталог установки apisix будет неизвестен, поэтому его нужно извлечь и отдельно записать кусок кода для определения этого значения.
apisix_home: определить каталог установки apisix.
local apisix_home = "/usr/local/apisix" --linux环境中默认的安装地址
/usr/local/apisix
Это каталог установки по умолчанию для среды Linux, но он не является абсолютным, и он также может быть установлен пользователем в других местах.Например, моя локальная установка находится не в этом каталоге.
Так что аписикс правapisix_home
После выполнения обработки необходимо вызвать функцию для выполненияpwd
команда, чтобы получить абсолютный путь, где находится скрипт, процесс выглядит следующим образом
--我的启动命令是./bin/apisix start
--如果启动命令以'./'开头
if script_path:sub(1, 2) == './' then
--调用系统的cmd命令,查看当前所在路径
apisix_home = trim(excute_cmd("pwd"))
if not apisix_home then
error("failed to fetch current path")
end
--判断是否安装在root跟路径下
if string.match(apisix_home, '^/[root][^/]+') then
is_root_path = true
end
end
excute_cmd
Функция выглядит следующим образом
-- 注意:`excute_cmd`返回值的末尾会有换行符,建议使用`trim`函数处理返回值。
local function excute_cmd(cmd)
--使用io.popen函数来执行命令(和直接在命令行界面执行命令的结果上一样的,不过使用函数这种方式的结果保存在文件中),在这里执行的就是下面的pwd命令
local t, err = io.popen(cmd) --这里返回的t其实是一个文件描述符
if not t then
return nil, "failed to execute command: " .. cmd .. ", error info:" .. err
end
local data = t:read("*all") --执行read命令读取文件
t:close()
return data --在我的机器上,模拟apisix脚本所在位置,执行pwd命令,返回如下
--/usr/local/Cellar/apisix/apache-apisix-1.5/bin\n
--结尾的\n是换行符
end
Используйте подключаемый модуль IDEA+EmmyLua для создания среды отладки lua-скриптов, поместитеexcute_cmd
Функция подхвачена и отлажена следующим образом
- Прототип: io.popen ([прог [ режим]])
- Объяснение: Запустить программу в дополнительном процессе
prog
, и возвращает дескриптор файла для prog. С точки зрения непрофессионала, эта функция может использоваться для вызова команды (программы) и возврата дескриптора файла, связанного с программой, обычно результат вывода вызванной функции.Режим открытия файла определяется параметромmode
Хорошо, есть значение"r"
а также"w"
Существует два типа, соответственно указывающих, что они открываются в режимах чтения и записи, а по умолчанию установлен режим чтения.
После вышеуказанной обработкиapisix_home
уже правильноapisix
каталог установки. В моем окружении,apisix_home
=/usr/local/Cellar/apisix/apache-apisix-1.5
, продолжайте смотреть на функцию запуска.
read_file: Прочитайте nginx.pid и оцените, запущен ли apisix
function _M.start(...)
-- check running
local pid_path = apisix_home .. "/logs/nginx.pid"
local pid, err = read_file(pid_path) -- 读取/usr/local/apisix/logs/nginx.pid
--如果nginx的进程id存在
if pid then
local hd = io.popen("lsof -p " .. pid)
local res = hd:read("*a")
if res and res ~= "" then
print("APISIX is running...")
return nil
end
end
--启动apisix
init(...)
--启动etcd
init_etcd(...)
local cmd = openresty_args
-- print(cmd)
os.execute(cmd)
end
nginx.pid
На самом деле начинаяnginxАвтоматически генерируется, в котором хранится текущийnginxИдентификационный номер процесса, то есть pid. Мой локальный apisix запустился, посмотритеnginx.pid
В строке 4 нужно найти каталог среды выполнения apisix.
Здесь используется функция io.read(), и переданные параметры"*a"
а также"*all"
Во-вторых, я отлаживаю и сравниваю результаты, чтобы увидеть, что эти два параметра должны иметь одинаковое значение, начиная с текущей позиции, читать весь файл. Если в конце файла или если файл пуст, вызов вернет пустую строку. В конце файла возвращается пустая строка.
"*all"
Он написан на lua5.1, см.:woohoo.rua.org/bulk/21.1.contract…
"*a"
Он написан на lua5.2, см.:woohoo.rua.org/manual/5.2/…
В строке 13 нужно определить, запущен ли nginx, если да, распечатать лог и выйти.
init: запустить apix
Функция start показывает, что вызывается функция init, а три точки (...) в параметрах указывают на то, что функция принимает другое количество аргументов.
local function init()
--判断apisix的安装目录是否是根路径
if is_root_path then
print('Warning! Running apisix under /root is only suitable for development environments'
.. ' and it is dangerous to do so. It is recommended to run APISIX in a directory other than /root.')
end
-- read_yaml_conf
--读取配置文件
local yaml_conf, err = read_yaml_conf()
if not yaml_conf then
error("failed to read local yaml config of apisix: " .. err)
end
-- print("etcd: ", yaml_conf.etcd.host)
……
end
Первый взглядread_yaml_conf()
функция
local function read_yaml_conf()
--调用profile模块
local profile = require("apisix.core.profile")
--给profile模块的元表apisix_home属性赋值,其实就是让profile模块知道apisix的安装路径
profile.apisix_home = apisix_home .. "/"
--profile模块yaml_path函数就是用
--apisix_home .. "conf/" .. "config" .. ".yaml"
--进行拼接,在我的环境下,这个拼接结果就是一个字符串:
--"/usr/local/Cellar/apisix/apache-apisix-1.5/conf/config.yaml"
--即apisix的配置文件所在位置
local local_conf_path = profile:yaml_path("config")
--读取配置文件
local ymal_conf, err = read_file(local_conf_path)
if not ymal_conf then
return nil, err
end
-- 解析tinyyaml模块来yaml配置文件
return yaml.parse(ymal_conf)
end
Этот раздел в основном основан на пути установки аписикса, прошивке конфигурационного файлаconfig.yaml
Абсолютный путь io, затем прочитайте файл io, дайтеtinyyaml
модуль для анализа конфигурации yaml, и в результате конфигурация yaml анализируется как значение типа table,yaml_conf
Информация о стеке выглядит следующим образом, век должен быть поставленconfig.yaml
Преобразован в табличную форму.
продолжать вниз
local function init()
--查看openresty编译信息
local or_ver = excute_cmd("openresty -V 2>&1")
local with_module_status = true
--校验是否安装了http_stub_status_module模块
if or_ver and not or_ver:find("http_stub_status_module", 1, true) then
io.stderr:write("'http_stub_status_module' module is missing in ",
"your openresty, please check it out. Without this ",
"module, there will be fewer monitoring indicators.\n")
with_module_status = false
end
……
end
Этот абзац эквивалентен выполнениюopenresty -V 2>&1 | grep http_stub_status_module
Команда в основном предназначена для проверки того, компилируется ли компиляция openresty во время компиляции.http_stub_status_module
Если модуль не скомпилирован, показатели мониторинга будут снижены.
Продолжайте, далее следует подготовить системный контекст, этот абзац в основном предназначен дляyaml_conf
Выполните проверку и преобразование параметров и получите информацию, относящуюся к среде выполнения системы, и, наконец, получите полную табличную форму.sys_conf
local function init()
……
-- Using template.render
--准备上下文信息
local sys_conf = {
lua_path = pkg_path_org,
lua_cpath = pkg_cpath_org,
--获取当前系统相关信息
os_name = trim(excute_cmd("uname")),
apisix_lua_home = apisix_home,
with_module_status = with_module_status,
error_log = {level = "warn"},
}
--一系列校验
if not yaml_conf.apisix then
error("failed to read `apisix` field from yaml file")
end
if not yaml_conf.nginx_config then
error("failed to read `nginx_config` field from yaml file")
end
--校验是否是32位机器
if is_32bit_arch() then
--worker_rlimit_core: nginx的配置,设置每个worker最大能打开的核心文件数,用于在不重启主进程的情况下增加限制。
sys_conf["worker_rlimit_core"] = "4G"
else
sys_conf["worker_rlimit_core"] = "16G"
end
--yaml_conf中的配置信息转移到sys_conf
for k,v in pairs(yaml_conf.apisix) do
sys_conf[k] = v
end
for k,v in pairs(yaml_conf.nginx_config) do
sys_conf[k] = v
end
--参数校验&优化参数
local wrn = sys_conf["worker_rlimit_nofile"]
local wc = sys_conf["event"]["worker_connections"]
if not wrn or wrn <= wc then
-- ensure the number of fds is slightly larger than the number of conn
sys_conf["worker_rlimit_nofile"] = wc + 128
end
--是否开启dev模式
if(sys_conf["enable_dev_mode"] == true) then
--开启dev模式的话,设置worker数量
sys_conf["worker_processes"] = 1
sys_conf["enable_reuseport"] = false
else
sys_conf["worker_processes"] = "auto"
end
--是否配置外置dns解析
local dns_resolver = sys_conf["dns_resolver"]
if not dns_resolver or #dns_resolver == 0 then
--如果没有配置dns解析,则使用默认的解析
local dns_addrs, err = local_dns_resolver("/etc/resolv.conf")
if not dns_addrs then
error("failed to import local DNS: " .. err)
end
if #dns_addrs == 0 then
error("local DNS is empty")
end
sys_conf["dns_resolver"] = dns_addrs
end
--到这里,sys_conf信息基本准备完毕
……
end
Хотя эта часть выглядит большим количеством кода, она не сложна.Часть ее состоит в проверке параметров, а часть – в получении информации о сервере во время выполнения.
В основном все дело в подготовкеsys_conf
продолжать.
yaml_conf
Информация из пользовательской конфигурации, даsys_conf
часть этого, в соответствии с параметрами конфигурации пользователя, например, включать ли режим разработки и т. д.sys_conf
настройка некоторых параметров.
Так что здесь в основном на основеyaml_conf
, среда операционной системы, информация о компиляции openresty,apisix
Среда установки, переменная среды PATH lua и другие параметры, сборкаsys_conf
,а такжеsys_conf
будет использоваться для определенияnginx
поведения, т. е. превращается вnginx.conf
конфигурационный файл.
завершенныйsys_conf
Информация о стеке выглядит следующим образом
продолжайте, следующее должно положитьsys_conf
Преобразован в файл конфигурации nginx.conf с использованием библиотеки функций.lua-resty-template
, справочник по использованию шаблонаGitHub.com/Не грусти/Руа-…
local function init()
……
--获取模板渲染引擎,ngx_tpl是在代码中定义的nginx-template模板,太长了,在这里就不展现了
local conf_render = template.compile(ngx_tpl)
--执行模板渲染,其实就是把sys_conf中的参数渲染进ngxconf中
local ngxconf = conf_render(sys_conf)
--把ngxconf配置写入文件,即nginx.conf
local ok, err = write_file(apisix_home .. "/conf/nginx.conf", ngxconf)
if not ok then
error("failed to update nginx.conf: " .. err)
end
--获取openresty版本号,调用的是local 函数,其实就是执行cmd命令
local op_ver = get_openresty_version()
--在我的环境中,op_ver = 1.15.8.3
if op_ver == nil then
io.stderr:write("can not find openresty\n")
return
end
local need_ver = "1.15.8"
--校验openresty版本号,校验的方式就是把op_ver和need_ver用 . 分割成数组,然后挨个比较大小
if not check_or_version(op_ver, need_ver) then
io.stderr:write("openresty version must >=", need_ver, " current ", op_ver, "\n")
return
end
end
Этот абзац, в том числе и предыдущий абзац, использует некоторые локальные функции,get_openresty_version()
,check_or_version
И так далее, по сути, аналогично тем функциям, которые были введены ранее, это все операции io и cmd.
ngxconf – это результат, отображаемый механизмом шаблонов. Это значение типа string. Содержимое выглядит следующим образом.
Затем содержимое выводится в файл, а окончательный сгенерированныйnginx.conf
Файл выглядит следующим образом
# Configuration File - Nginx Server Configs
# This is a read-only file, do not try to modify it.
master_process on;
worker_processes auto;
error_log logs/error.log warn;
pid logs/nginx.pid;
worker_rlimit_nofile 20480;
events {
accept_mutex off;
worker_connections 10620;
}
worker_rlimit_core 16G;
worker_shutdown_timeout 240s;
env APISIX_PROFILE;
http {
lua_package_path "$prefix/deps/share/lua/5.1/?.lua;$prefix/deps/share/lua/5.1/?/init.lua;/usr/local/Cellar/apisix/apache-apisix-1.5/?.lua;/usr/local/Cellar/apisix/apache-apisix-1.5/?/init.lua;;./?.lua;/usr/local/Cellar/openresty@1.15.8.3/1.15.8.3/openresty/luajit/share/luajit-2.1.0-beta3/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/Cellar/openresty@1.15.8.3/1.15.8.3/openresty/luajit/share/lua/5.1/?.lua;/usr/local/Cellar/openresty@1.15.8.3/1.15.8.3/openresty/luajit/share/lua/5.1/?/init.lua;/Users/tuzhengsong/Library/Application Support/JetBrains/IntelliJIdea2020.2/plugins/intellij-emmylua/classes/debugger/mobdebug/?.lua;/Users/tuzhengsong/IdeaProjects/apisix-learning/src/?.lua;/Users/tuzhengsong/IdeaProjects/apisix-learning/?.lua;;/usr/local/Cellar/apisix/apache-apisix-1.5/deps/share/lua/5.1/?.lua;/usr/local/Cellar/apisix/apache-apisix-1.5/deps/share/lua/5.1/?/?.lua;/usr/local/Cellar/apisix/apache-apisix-1.5/deps/share/lua/5.1/?.lua;";
lua_package_cpath "$prefix/deps/lib64/lua/5.1/?.so;$prefix/deps/lib/lua/5.1/?.so;;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/Cellar/openresty@1.15.8.3/1.15.8.3/openresty/luajit/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so;";
lua_shared_dict plugin-limit-req 10m;
lua_shared_dict plugin-limit-count 10m;
lua_shared_dict prometheus-metrics 10m;
lua_shared_dict plugin-limit-conn 10m;
lua_shared_dict upstream-healthcheck 10m;
lua_shared_dict worker-events 10m;
lua_shared_dict lrucache-lock 10m;
lua_shared_dict skywalking-tracing-buffer 100m;
# for openid-connect plugin
lua_shared_dict discovery 1m; # cache for discovery metadata documents
lua_shared_dict jwks 1m; # cache for JWKs
lua_shared_dict introspection 10m; # cache for JWT verification results
# for custom shared dict
# for proxy cache
proxy_cache_path /tmp/disk_cache_one levels=1:2 keys_zone=disk_cache_one:50m inactive=1d max_size=1G;
# for proxy cache
map $upstream_cache_zone $upstream_cache_zone_info {
disk_cache_one /tmp/disk_cache_one,1:2;
}
lua_ssl_verify_depth 5;
ssl_session_timeout 86400;
underscores_in_headers on;
lua_socket_log_errors off;
resolver 192.168.1.1 192.168.0.1 valid=30;
resolver_timeout 5;
lua_http10_buffering off;
lua_regex_match_limit 100000;
lua_regex_cache_max_entries 8192;
log_format main '$remote_addr - $remote_user [$time_local] $http_host "$request" $status $body_bytes_sent $request_time "$http_referer" "$http_user_agent" $upstream_addr $upstream_status $upstream_response_time';
access_log logs/access.log main buffer=16384 flush=3;
open_file_cache max=1000 inactive=60;
client_max_body_size 0;
keepalive_timeout 60s;
client_header_timeout 60s;
client_body_timeout 60s;
send_timeout 10s;
server_tokens off;
more_set_headers 'Server: APISIX web server';
include mime.types;
charset utf-8;
real_ip_header X-Real-IP;
set_real_ip_from 127.0.0.1;
set_real_ip_from unix:;
upstream apisix_backend {
server 0.0.0.1;
balancer_by_lua_block {
apisix.http_balancer_phase()
}
keepalive 320;
}
init_by_lua_block {
require "resty.core"
apisix = require("apisix")
local dns_resolver = { "192.168.1.1", "192.168.0.1", }
local args = {
dns_resolver = dns_resolver,
}
apisix.http_init(args)
}
init_worker_by_lua_block {
apisix.http_init_worker()
}
server {
listen 9080 reuseport;
listen 9443 ssl http2 reuseport;
listen [::]:9080 reuseport;
listen [::]:9443 ssl http2 reuseport;
ssl_certificate cert/apisix.crt;
ssl_certificate_key cert/apisix.key;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
location = /apisix/nginx_status {
allow 127.0.0.0/24;
deny all;
access_log off;
stub_status;
}
location /apisix/admin {
allow 127.0.0.0/24;
deny all;
content_by_lua_block {
apisix.http_admin()
}
}
location /apisix/dashboard {
allow 127.0.0.0/24;
deny all;
alias dashboard/;
try_files $uri $uri/index.html /index.html =404;
}
ssl_certificate_by_lua_block {
apisix.http_ssl_phase()
}
location / {
set $upstream_mirror_host '';
set $upstream_scheme 'http';
set $upstream_host $host;
set $upstream_upgrade '';
set $upstream_connection '';
set $upstream_uri '';
access_by_lua_block {
apisix.http_access_phase()
}
proxy_http_version 1.1;
proxy_set_header Host $upstream_host;
proxy_set_header Upgrade $upstream_upgrade;
proxy_set_header Connection $upstream_connection;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass_header Server;
proxy_pass_header Date;
### the following x-forwarded-* headers is to send to upstream server
set $var_x_forwarded_for $remote_addr;
set $var_x_forwarded_proto $scheme;
set $var_x_forwarded_host $host;
set $var_x_forwarded_port $server_port;
if ($http_x_forwarded_for != "") {
set $var_x_forwarded_for "${http_x_forwarded_for}, ${realip_remote_addr}";
}
if ($http_x_forwarded_proto != "") {
set $var_x_forwarded_proto $http_x_forwarded_proto;
}
if ($http_x_forwarded_host != "") {
set $var_x_forwarded_host $http_x_forwarded_host;
}
if ($http_x_forwarded_port != "") {
set $var_x_forwarded_port $http_x_forwarded_port;
}
proxy_set_header X-Forwarded-For $var_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $var_x_forwarded_proto;
proxy_set_header X-Forwarded-Host $var_x_forwarded_host;
proxy_set_header X-Forwarded-Port $var_x_forwarded_port;
### the following configuration is to cache response content from upstream server
set $upstream_cache_zone off;
set $upstream_cache_key '';
set $upstream_cache_bypass '';
set $upstream_no_cache '';
set $upstream_hdr_expires '';
set $upstream_hdr_cache_control '';
proxy_cache $upstream_cache_zone;
proxy_cache_valid any 10s;
proxy_cache_min_uses 1;
proxy_cache_methods GET HEAD;
proxy_cache_lock_timeout 5s;
proxy_cache_use_stale off;
proxy_cache_key $upstream_cache_key;
proxy_no_cache $upstream_no_cache;
proxy_cache_bypass $upstream_cache_bypass;
proxy_hide_header Cache-Control;
proxy_hide_header Expires;
add_header Cache-Control $upstream_hdr_cache_control;
add_header Expires $upstream_hdr_expires;
add_header Apisix-Cache-Status $upstream_cache_status always;
proxy_pass $upstream_scheme://apisix_backend$upstream_uri;
mirror /proxy_mirror;
header_filter_by_lua_block {
apisix.http_header_filter_phase()
}
body_filter_by_lua_block {
apisix.http_body_filter_phase()
}
log_by_lua_block {
apisix.http_log_phase()
}
}
location @grpc_pass {
access_by_lua_block {
apisix.grpc_access_phase()
}
grpc_set_header Content-Type application/grpc;
grpc_socket_keepalive on;
grpc_pass grpc://apisix_backend;
header_filter_by_lua_block {
apisix.http_header_filter_phase()
}
body_filter_by_lua_block {
apisix.http_body_filter_phase()
}
log_by_lua_block {
apisix.http_log_phase()
}
}
location = /proxy_mirror {
internal;
if ($upstream_mirror_host = "") {
return 200;
}
proxy_pass $upstream_mirror_host$request_uri;
}
}
}
На этом процесс запуска не закончен, он просто генерирует требуемыйnginx.conf
конфигурационный файл.
init_etcd: запустить etcd
Следующее, чтобы начать etcd, этот раздел в основном для чтенияyaml_conf
в настройках, связанных с etcd.
local function init_etcd(show_output)
-- 读取yaml_conf
local yaml_conf, err = read_yaml_conf()
--一系列的参数校验
if not yaml_conf then
error("failed to read local yaml config of apisix: " .. err)
end
if not yaml_conf.apisix then
error("failed to read `apisix` field from yaml file when init etcd")
end
if yaml_conf.apisix.config_center ~= "etcd" then
return true
end
if not yaml_conf.etcd then
error("failed to read `etcd` field from yaml file when init etcd")
end
--最终拿到根etcd相关的参数
local etcd_conf = yaml_conf.etcd
……
end
Информация о стеке конфигурации etcd_conf выглядит следующим образом.
Продолжать
local function init_etcd(show_output)
……
local timeout = etcd_conf.timeout or 3
local uri
--将旧的单个etcd配置转换为多个etcd配置
if type(yaml_conf.etcd.host) == "string" then
yaml_conf.etcd.host = {yaml_conf.etcd.host}
end
--获取配置的etcd集群的多个地址
local host_count = #(yaml_conf.etcd.host)
-- 检查用户是否已启用etcd v2协议
for index, host in ipairs(yaml_conf.etcd.host) do
uri = host .. "/v2/keys"
--这里就是拼接cmd命令了,在我的环境中,拼接出来的命令就是
--curl -i -m 60 -o /dev/null -s -w %{http_code} http://127.0.0.1:2379/v2/keys
--这个命令相当于获取curl访问结果的http状态码
local cmd = "curl -i -m ".. timeout * 2 .. " -o /dev/null -s -w %{http_code} " .. uri
--执行命令
local res = excute_cmd(cmd)
--校验返回结果
if res == "404" then
--io.stderr:标准错误输出
io.stderr:write(string.format("failed: please make sure that you have enabled the v2 protocol of etcd on %s.\n", host))
return
end
end
……
end
Здесь, в соответствии с информацией о хосте настроенного кластера etcd (их может быть несколько), используйте команду curl для доступа, получите код состояния результата доступа и проверьте, включил ли пользователь протокол v2 etcd.
эквивалентно выполнениюcurl -i -m 60 -o /dev/null -s -w %{http_code} http://127.0.0.1:2379/v2/keys
Эффект следующий
Продолжать
local function init_etcd(show_output)
……
local etcd_ok = false
--遍历etcd集群的主机地址
for index, host in ipairs(yaml_conf.etcd.host) do
local is_success = true
--host --> http://127.0.0.1:2379
--etcd_conf.prefix --> /apisix
uri = host .. "/v2/keys" .. (etcd_conf.prefix or "")
--准备在etcd中创建一些目录
for _, dir_name in ipairs({"/routes", "/upstreams", "/services",
"/plugins", "/consumers", "/node_status",
"/ssl", "/global_rules", "/stream_routes",
"/proto"}) do
--拼接cmd命令
local cmd = "curl " .. uri .. dir_name
.. "?prev_exist=false -X PUT -d dir=true "
.. "--connect-timeout " .. timeout
.. " --max-time " .. timeout * 2 .. " --retry 1 2>&1"
--cmd示例: curl http://127.0.0.1:2379/v2/keys/apisix/routes?prev_exist=false -X PUT -d dir=true --connect-timeout 30 --max-time 60 --retry 1 2>&1
local res = excute_cmd(cmd)
--curl命令的返回结果中进行字符串匹配,找到有没有index或者createdIndex的关键词
--这个判断条件的意思是:在返回结果中即没有匹配上createdIndex(创建成功时返回的res中含有的关键词),也没有匹配上index(重复创建时返回的关键词)
if not res:find("index", 1, true)
and not res:find("createdIndex", 1, true) then
--如果进入这里,说明本次循环(目录,routers之类的)中,当前要创建的资源实际上无法创建了,因为这个返回的结果既不是创建成功的结果,也不是重复创建的提示,那么这个资源无法创建了
is_success = false
--如果当前已经遍历到了配置的最后一个etcd host地址,那么直接报错,因为没有机会再次在其他host上创建这个资源了
if (index == host_count) then
error(cmd .. "\n" .. res)
end
--如果是否的话,则退出当前循环,继续创建目录中的其他资源
break
end
--是否输出cmd和res
if show_output then
print(cmd)
print(res)
end
end
--如果在一个host上创建成功,则退出循环(host),标识etcd_ok资源初始化成功
if is_success then
etcd_ok = true
break
end
end
--虽然可能会配置了etcd集群的多个host地址,但是只要有一个执行成功了初始化,就算完成了
if not etcd_ok then
error("none of the configured etcd works well")
end
end
Выведенная команда cmd:
curl http://127.0.0.1:2379/v2/keys/apisix/routes?prev_exist=false -X PUT -d dir=true --connect-timeout 30 --max-time 60 --retry 1 2>&1
Следующие параметры в этой команде — это команды, полученные API etcd v2.
- prev_exist: проверить, существует ли ключ: если prev_exist равен true, это запрос на обновление; если prev_exist равен false, это запрос на создание
- dir=true: создать каталог
Команда cmd выражается как: создать ресурс каталога на API, После успешного создания соответствующая конфигурация в etcd выглядит следующим образом
Эффект выполнения cmd следующийВозвращается, когда создание успешно
{"action":"set","node":{"key":"/apiseven/routes","dir":true,"modifiedIndex":91,"createdIndex":91}}
Возвращает, если такое же значение уже было создано
{"errorCode":102,"message":"Not a file","cause":"/apiseven/upstreams","index":89}
Поэтому при проверке результата выполнения cmd он будет соответствоватьindex
а такжеcreatedIndex
, если он соответствует одному из них, это означает, что каталог был успешно создан.
Логика двухуровневого цикла в этом процессе немного коварна.Утверждается, что все ресурсы каталога должны быть успешно созданы в сконфигурированном кластере etcd.Хотя питомник не создается на том же хосте, при условии, что он создан успешно , Тогда etcd гарантирует возможную согласованность.
На данный момент две важные ссылки в процессе запуска apisix
- Создайте nginx.conf
- Инициализировать ресурсы каталога etcd
Завершено.
openresty_args: запустить openresty
Последняя часть стартовой функции выглядит следующим образом:
local openresty_args = [[openresty -p ]] .. apisix_home .. [[ -c ]]
.. apisix_home .. [[/conf/nginx.conf]]
function _M.start(...)
local cmd = openresty_args
-- print(cmd)
os.execute(cmd)
end
По сути, это запуск опенрестов, аписикс — это приложение, построенное на опенресах
Команда cmd склеивается следующим образом:
openresty -p /usr/local/Cellar/apisix/apache-apisix-1.5 -c /usr/local/Cellar/apisix/apache-apisix-1.5/conf/nginx.conf
- -p указывает каталог проекта
- -c указывает файл конфигурации
0x03 end
Если есть ошибки, указывайте на них, обсуждайте и учитесь вместе.