☎️ ☎️ ☎️ 已开源基于SpringBoot+Mybatisplus+Layui+SnakerFlow前后端分离轻量级工作流引擎的脚手架项目 easy-admin
背景
在我的开源项目EasyAdmin中,我希望能直接在管理后台开发个简易页面去编辑nginx配置,重启、热加载等操作,很便于运维哦。
nginxparser介绍
nginxparser就是个nginx配置文件解析器,把其中的每个属性、块解释成Java类,以便于进行操作。
Github地址:https://github.com/odiszapc/nginx-java-parser
pom.xml依赖
<dependency>
<groupId>com.github.odiszapc</groupId>
<artifactId>nginxparser</artifactId>
<version>0.9.6</version>
</dependency>
官方示例
读取配置
NgxConfig conf = NgxConfig.read("/etc/nginx/nginx.conf");
NgxParam workers = conf.findParam("worker_processes"); // Ex.1
workers.getValue(); // "1"
NgxParam listen = conf.findParam("http", "server", "listen"); // Ex.2
listen.getValue(); // "8889"
List<NgxEntry> rtmpServers = conf.findAll(NgxConfig.BLOCK, "rtmp", "server"); // Ex.3
for (NgxEntry entry : rtmpServers) {
((NgxBlock)entry).getName(); // "server"
((NgxBlock)entry).findParam("application", "live"); // "on" for the first iter, "off" for the second one
}
etc/nginx/nginx.conf
worker_processes 1; # <- Ex.1
http {
server {
listen 8889; # <- Ex.2
server_name localhost;
}
}
rtmp {
server { # <- Ex.3 (first)
listen 1935;
application myapp {
live on;
}
}
server { # <- Ex.3 (second)
listen 1936;
application myapp2 {
live off;
}
}
}
NgxConfig conf = NgxConfig.read("/etc/nginx/nginx.conf");
// ...
NgxDumper dumper = new NgxDumper(conf); // 序列化配置
return dumper.dump(System.out);
结构分析
以下面这个常见的配置来分析其对应的描述。
worker_processes 1;
http {
server {
# 端口号
listen 80;
server_name www.nginx.cn;
location / {
proxy_pass http://www.nginx.cn;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 81;
server_name localhost;
location / {
proxy_pass http://192.168.0.1;
}
}
}
块描述NgxBlock
:http {…}、server {…}、location / {…},这种名称+{}
单行参数描述NgxParam
: listen 80,server_name www.nginx.cn,这种简单的文本key value
注释描述NgxComment
:# 注释哦
操作列表
加载配置
可以通过路径或者流的方式加载配置。
NgxConfig conf = NgxConfig.read(fileName);
---
NgxConfig conf = NgxConfig.read("/etc/nginx/nginx.conf");
查询指定配置项
查找块BLOCK
以查找server块,配置使用上面的为示例
方式一:单个块精确查询普通
NgxBlock httpBlock = conf.findBlock("http");
NgxBlock serverBlock = httpBlock.findBlock("server"); // 这里只能查找到第一个server 端口80的那个
方式二:单个块精确查询高级
NgxBlock serverBlock2 = conf.findBlock("http","server"); // 这里只能查找到第一个server 端口80的那个
这里效果与上面方式一相同,但是更简单有效,建议选这个。
方式三:获取所有块
List<NgxEntry> serverBlockList = conf.findAll(NgxConfig.BLOCK, "http", "server");
for (NgxEntry serverBlockEntry : serverBlockList) {
NgxBlock serverBlock = (NgxBlock) serverBlockEntry;
serverBlock.getName();
}
这里可以一次获取所有块。
查找单行参数Param
以查找listen配置项为例
基本操作与查找块类似,也支持单次普通,高级,以及获取所有
NgxParam listen = serverBlock.findParam("listen");// 普通单个 listen 80;
NgxParam listen = conf.findParam("http", "server", "listen"); // 高级单个 listen 80;
List<NgxEntry> listenS = conf.findAll(NgxConfig.PARAM, "http", "server","listen"); // 获取所有listen 80;listen 81;
for (NgxEntry listen : listenS) {
NgxParam listenParam = (NgxParam) listen;
listenParam.getName();
}
查找具体的Key Value
String state = listenParam.getName(); // listen
String state = listenParam.getValue(); // 80
添加配置
添加块Block
NgxBlock serverBlock = new NgxBlock();
serverBlock.addValue("server");
httpBlock.addEntry(serverBlock);
上面结果为:
...
http {
...
server {
listen 81;
server_name localhost;
location / {
proxy_pass http://192.168.0.1;
}
}
server { // 这里是新增的 server 块
}
}
添加单行参数Param
方式一:简单版
NgxParam ngxParam = new NgxParam();
ngxParam.addValue("proxy_pass http://192.168.0.1");
locationBlock.addEntry(ngxParam);
方式二:高级版
NgxParam ngxParam = new NgxParam();
ngxParam.addValue("proxy_pass");
ngxParam.addValue("http://192.168.0.1");
locationBlock.addEntry(ngxParam);
方式三:牛逼版
NgxParam ngxParam = new NgxParam();
param.addValue(" proxy_pass http://www.nginx.cn;\n" +
" proxy_set_header Host $host;\n" +
" proxy_set_header X-Real-IP $remote_addr;\n" +
" proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;");
locationBlock.addEntry(ngxParam);
这种形式可以一把塞一坨。
添加一大坨文本配置
原生态Api没办法一下添加一大坨文本配置,例如直接添加一整个server块,或者是整个nginx配置
server {
listen 81;
server_name localhost;
location / {
proxy_pass http://192.168.0.1;
}
}
尝试了NgxParam方式
NgxParam xxx = new NgxParam();
xxx.addValue(" server {\n" +
" listen 81;\n" +
" server_name localhost;\n" +
" location / {\n" +
" proxy_pass http://192.168.0.1;\n" +
" }\n" +
" }");
conf.findBlock("http").addEntry(xxx);
String content = new NgxDumper(conf).dump();
System.out.println(content);
结果是最后拼接会多个;
号,
server {
listen 81;
server_name localhost;
location / {
proxy_pass http://192.168.0.1;
}
}; //这里多了个;
可以看下源码NgxParam.java
public class NgxParam extends NgxAbstractEntry {
public String toString() {
String s = super.toString();
if (s.isEmpty())
return s;
return s + ";";
}
}
同理NgxBlock、NgxComment,会多个**{}、#** 。
然后骚东西来了,第一行搞2个空格加点东西然后换行符即可
NgxComment xxx = new NgxComment(" 注释\n" + //这里2个空格 + \n
" server {\n" +
" listen 81;\n" +
" server_name localhost;\n" +
" location / {\n" +
" proxy_pass http://192.168.0.1;\n" +
" }\n" +
" }");
conf.findBlock("http").addEntry(xxx);
String content = new NgxDumper(conf).dump();
System.out.println(content);
结果
http{
# 注释
server {
listen 81;
server_name localhost;
location / {
proxy_pass http://192.168.0.1;
}
}
}
nice、nice、nice
常用示例
添加server
NgxBlock serverBlock = new NgxBlock();
serverBlock.addValue("server"); //新建 server块
NgxParam ngxParam = new NgxParam();
ngxParam.addValue("listen 80"); // 新建 listen 参数
serverBlock.addEntry(ngxParam); // 添加 listen 80 参数
ngxParam = new NgxParam();
ngxParam.addValue("server_name localhost"); // 新建 server_name参数
serverBlock.addEntry(ngxParam); // 添加 server_name localhost 参数
httpBlock.addEntry(serverBlock); // 添加server块到http块
结果如下:
...
http {
...
server {
listen 80;
server_name localhost;
}
}
添加location
NgxBlock locatioBlock = new NgxBlock();
locatioBlock.addValue("location /");
// locatioBlock.addValue("/"); // 或者这样处理
NgxParam ngxParam = new NgxParam();
ngxParam.addValue("proxy_pass http://192.168.0.1");
locatioBlock.addEntry(ngxParam);
ngxParam = new NgxParam();
ngxParam.addValue("proxy_set_header Host $host");
locatioBlock.addEntry(ngxParam);
serverBlock.addEntry(locatioBlock);
结果如下:
...
http {
...
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://192.168.0.1;
proxy_set_header Host $host;
}
}
}
删除配置
confg.remove(xxx)
示例
删除listen 81
的server块
NgxBlock ngxBlockHttp = conf.findBlock("http");
List<NgxEntry> serverList = ngxBlockHttp.findAll(NgxConfig.BLOCK,"server");
for (NgxEntry ngxEntry : serverList) {
NgxBlock ngxBlock = (NgxBlock) ngxEntry;
NgxParam ngxParam = ngxBlock.findParam("listen");
if ("81".equals(ngxParam.getValue())) {
ngxBlockHttp.remove(ngxBlock);
}
}
修改配置
没有这个接口,只能先删除再新增。
获取配置内容
String content = new NgxDumper(conf).dump();
log.info("{}", content);
读出来的内容就是上面的nginx.conf整体内容。
worker_processes 1;
http {
...
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://192.168.0.1;
proxy_set_header Host $host;
}
}
}
Nginx常见操作
启动
nginx -c /usr/local/nginx/conf/nginx.conf
- 说明:-c 参数指定运行nginx系统的自定义配置文件。
- 若加:使用自定义配置文件。
- 若不加:使用默认的nginx.conf(一般位于/usr/local/conf/nginx.conf,具体以实际情况为准)
停止
当nginx启动后,可以使用“-s”
参数向nginx管理进程(即master进程)发送信号来控制nginx:nginx -s signal
其中,signal可以是以下值:
- stop:快速关闭
- quit:安全关闭
- reload:重载配置文件
- reopen:重新打开一个log文件,主要用于日志切割
quit信号:通知nginx等待worker进程处理完当前的请求后退出,此命令只能由启动nginx的linux账户来执行。
reload信号:通知nginx重新载入配置文件nginx.conf。 除了使用reload外,nginx只会在启动时载入一次配置文件,之后对配置文件的修改不会实时对已经运行的nginx进程生效。
当运行这个命令时,master进程会尝试读取配置文件
- 如果配置文件没有问题
- master进程会启动新的worker进程来运行新的配置文件并处理请求,同时会通知老的worker进程不再处理新的请求并在处理完当前任务后退出。
- 如果配置文件存在问题
- master进程会回退老配置文件继续工作,不会导致nginx进程整个异常退出。
linux的kill命令也可以达到相同的作用,假设nginx的master进程号(pid)是123456,那么 kill -s QUIT 123456 和 kill -s HUP 123456 这两条命令和前面quit、reload的作用相同。
关闭nginx命令:
nginx -s quit
重载配置
nginx -s reload
校验配置
为什么要验证配置呢?
试想一个场景,线上nginx系统正在正常运行。
现因某种原因,需重新加载一下配置文件,但是,在加载之前,你不确定刚编辑完成的配置文件是否正确(例如:语法等等规范性)
只有配置文件语法等等正确,加载后才会被系统重新读取并利用,否则,系统会回退原配置文件继续运行!!
因此,在加载配置文件之前,最好先验证一下配置文件的正确性。这样可以确保加载更有把握,更稳妥。
验证默认配置文件
nginx -t
验证自定义配置文件
nginx -t -c /home/test/conf/nginx.conf