☎️ ☎️ ☎️ 已开源基于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 + ";";
    }
}

同理NgxBlockNgxComment,会多个**{}、#** 。

然后骚东西来了,第一行搞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