前言
大家好,我是老马。很高兴遇到你。
我们为 java 开发者实现了 java 版本的 nginx
如果你想知道 servlet 如何处理的,可以参考我的另一个项目:
手写从零实现简易版 tomcat minicat
手写 nginx 系列
如果你对 nginx 原理感兴趣,可以阅读:
从零手写实现 nginx-01-为什么不能有 java 版本的 nginx?
从零手写实现 nginx-03-nginx 基于 Netty 实现
从零手写实现 nginx-04-基于 netty http 出入参优化处理
从零手写实现 nginx-05-MIME类型(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展类型)
从零手写实现 nginx-12-keep-alive 连接复用
从零手写实现 nginx-13-nginx.conf 配置文件介绍
从零手写实现 nginx-14-nginx.conf 和 hocon 格式有关系吗?
从零手写实现 nginx-15-nginx.conf 如何通过 java 解析处理?
从零手写实现 nginx-16-nginx 支持配置多个 server
从零手写实现 nginx-18-nginx 请求头+响应头操作
一个 nginx.conf 的例子
# nginx.conf
# 定义运行Nginx的用户和组
user nginx;
# 主进程的PID文件存放位置
pid /var/run/nginx.pid;
# 事件模块配置
events {
worker_connections 1024; # 每个工作进程的最大连接数
}
# HTTP模块配置
http {
include /etc/nginx/mime.types; # MIME类型配置文件
default_type application/octet-stream; # 默认的MIME类型
# 访问日志配置
access_log /var/log/nginx/access.log; # 访问日志文件路径
# 错误日志配置
error_log /var/log/nginx/error.log; # 错误日志文件路径
# 文件传输设置
sendfile on; # 开启高效文件传输
tcp_nopush on; # 防止网络拥塞
# Keepalive超时设置
keepalive_timeout 65;
# 定义服务器块
server {
listen 80; # 监听80端口
server_name example.com; # 服务器域名
# 静态文件的根目录
root /usr/share/nginx/html; # 静态文件存放的根目录
index index.html index.htm; # 默认首页
# 定义location块,处理对根目录的请求
location / {
try_files $uri $uri/ =404; # 尝试提供请求的文件,如果不存在则404
}
}
server {
listen 443 ssl;
server_name secure-example.com;
ssl_certificate /etc/nginx/ssl/secure-example.com.crt;
ssl_certificate_key /etc/nginx/ssl/secure-example.com.key;
location / {
root /var/www/secure-example.com;
index index.html index.htm;
}
}
}
自己解析
思路
我们可以自己写一堆代码,然后解析这个配置文件。
伪代码
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class NginxConfigParser {
public static void main(String[] args) {
Map<String, String> configMap = parseNginxConfig("/path/to/nginx.conf");
System.out.println("Nginx configuration settings:");
for (Map.Entry<String, String> entry : configMap.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
}
public static Map<String, String> parseNginxConfig(String filePath) {
Map<String, String> configMap = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
String currentBlock = "";
Pattern pattern = Pattern.compile("^\\s*([^#\\s][^\\s]*)\\s*([^#]+)?");
while ((line = reader.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String directive = matcher.group(1);
String value = matcher.group(2);
if (value != null) {
value = value.trim();
if (value.endsWith(";")) {
value = value.substring(0, value.length() - 1).trim();
}
}
if (directive.equals("server")) {
currentBlock = "server";
} else if (directive.equals("location")) {
currentBlock = "location";
}
if (!directive.isEmpty()) {
configMap.put(currentBlock + "." + directive, value);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return configMap;
}
}
实际效果
上面的内容,如果用这个方法解析,实际上并不太准确。
Nginx configuration settings:
.events = {
location.} = null
server.listen = 80
.error_log = /var/log/nginx/error.log
server.server = {
location.try_files = $uri $uri/ =404
.include = /etc/nginx/mime.types
.keepalive_timeout = 65
.user = nginx
.tcp_nopush = on
.pid = /var/run/nginx.pid
server.server_name = example.com
.} = null
.http = {
.default_type = application/octet-stream
.worker_connections = 1024
location.location = / {
server.root = /usr/share/nginx/html
server.index = index.html index.htm
.access_log = /var/log/nginx/access.log
.sendfile = on
优缺点
自己实现,可控性相对比较强。
但是缺点是比较麻烦,可能还会引入一堆问题。
三方库解析
第二种是利用三方库。
比如 https://github.com/odiszapc/nginx-java-parser
Nginx配置Java解析器
这个库帮助分析Nginx Web服务器配置文件,查找指定的参数、块、正则表达式或注释。
然后AST可以被修改并转换回纯文本文件。
maven 依赖
<dependency>
<groupId>com.github.odiszapc</groupId>
<artifactId>nginxparser</artifactId>
<version>0.9.3</version>
</dependency>
解析例子
package com.github.houbb.nginx4j.config;
import com.github.odiszapc.nginxparser.NgxBlock;
import com.github.odiszapc.nginxparser.NgxConfig;
import com.github.odiszapc.nginxparser.NgxEntry;
import com.github.odiszapc.nginxparser.NgxParam;
import java.io.IOException;
import java.util.List;
public class NginxConfigParserTest {
public static void main(String[] args) throws IOException {
NgxConfig conf = NgxConfig.read("D:\\github\\nginx4j\\src\\test\\resources\\nginx-demo.conf");
// 基本信息
NgxParam pidParam = conf.findParam("pid");
System.out.println(pidParam.getValue());;
NgxParam worker_connectionsParam = conf.findParam("events", "worker_connections");
System.out.println(worker_connectionsParam.getValue());
// 模块下多级
NgxParam listen = conf.findParam("http", "server", "listen"); // 示例2
System.out.println(listen.getValue()); // "8889"
// 首先获取 block
List<NgxEntry> servers = conf.findAll(NgxConfig.BLOCK, "http", "server"); // 示例3
for (NgxEntry entry : servers) {
NgxBlock ngxBlock = (NgxBlock) entry;
String name = ngxBlock.getName();
// value
String value = ngxBlock.findParam("listen").getValue(); // 第一次迭代返回"on",第二次迭代返回"off"
System.out.println(name + "---" + value);
}
}
}
测试日志
/var/run/nginx.pid
1024
80
server---80
server---443 ssl
转储器
NgxConfig conf = NgxConfig.read("/etc/nginx/nginx.conf");
// ...
NgxDumper dumper = new NgxDumper(conf);
return dumper.dump(System.out);
自定义的解析类
思路
我们首先进行一层封装,方便后续的接口替换。
目前底层使用 nginxparser 来统一实现。
效果
/var/run/nginx.pid
1024
80
server---80
server---443 ssl
小结
本文介绍了 nginx 配置文件的例子,和自己解析的思路。
不过还是推荐使用三方标准库来处理,这样很多情况解决的比较充分。
我是老马,期待与你的下次重逢。
开源地址
为了便于大家学习,已经将 nginx 开源