前言
大家好,我是老馬。很高興遇到你。
我們為 java 開發者實現了 java 版本的 nginx
https://github.com/houbb/nginx4j
如果你想知道 servlet 如何處理的,可以參考我的另一個專案:
手寫從零實現簡易版 tomcat minicat
手寫 nginx 系列
如果你對 nginx 原理感興趣,可以閱讀:
從零手寫實現 nginx-01-為什麼不能有 java 版本的 nginx?
從零手寫實現 nginx-02-nginx 的核心能力
從零手寫實現 nginx-03-nginx 基於 Netty 實現
從零手寫實現 nginx-04-基於 netty http 出入參最佳化處理
從零手寫實現 nginx-05-MIME型別(Multipurpose Internet Mail Extensions,多用途網際網路郵件擴充套件型別)
從零手寫實現 nginx-06-資料夾自動索引
從零手寫實現 nginx-07-大檔案下載
從零手寫實現 nginx-08-範圍查詢
從零手寫實現 nginx-09-檔案壓縮
從零手寫實現 nginx-10-sendfile 零複製
從零手寫實現 nginx-11-file+range 合併
從零手寫實現 nginx-12-keep-alive 連線複用
從零手寫實現 nginx-13-nginx.conf 配置檔案介紹
從零手寫實現 nginx-14-nginx.conf 和 hocon 格式有關係嗎?
從零手寫實現 nginx-15-nginx.conf 如何透過 java 解析處理?
從零手寫實現 nginx-16-nginx 支援配置多個 server
從零手寫實現 nginx-17-nginx 預設配置最佳化
從零手寫實現 nginx-18-nginx 請求頭+響應頭操作
從零手寫實現 nginx-19-nginx cors
一個 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 開源
https://github.com/houbb/nginx4j