从零手写实现 nginx-25-directive map 条件判断指令

前言

大家好,我是老马。很高兴遇到你。

我们为 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-20-nginx 占位符 placeholder

从零手写实现 nginx-21-nginx modules 模块信息概览

从零手写实现 nginx-22-nginx modules 分模块加载优化

从零手写实现 nginx-23-nginx cookie 的操作处理

从零手写实现 nginx-24-nginx IF 指令

从零手写实现 nginx-25-nginx map 指令

前言

大家好,我是老马。

这一节我们将配置的加载,拆分为不同的模块加载处理,便于后续拓展。

if

详细介绍一下 nginx 的 map 指令

Nginx 的 map 指令是一个强大的工具,用于根据变量的值来设置另一个变量的值。

它可以用于很多场景,比如基于请求的某些特征来动态设置变量,从而影响后续的处理逻辑。

以下是关于 map 指令的详细介绍:

语法和基本用法

map 指令的基本语法如下:

map $variable_to_test $variable_to_set {default value;key value;...
}
  • $variable_to_test:要测试的变量。
  • $variable_to_set:要设置的变量。
  • default:如果没有找到匹配的键,则使用默认值。
  • key value:键值对,根据 $variable_to_test 的值来设置 $variable_to_set

示例

假设我们想根据请求的主机名设置一个变量,进而用这个变量来决定后续的行为。

可以这样使用 map 指令:

http {map $http_host $backend_server {default         backend1.example.com;"www.example.com" backend2.example.com;"api.example.com" backend3.example.com;}server {listen 80;server_name example.com;location / {proxy_pass http://$backend_server;}}
}

在这个例子中:

  • 根据 $http_host 的值(请求头中的主机名),将 $backend_server 变量设置为不同的后端服务器。
  • 如果主机名是 www.example.com,则 $backend_server 设置为 backend2.example.com
  • 如果主机名是 api.example.com,则 $backend_server 设置为 backend3.example.com
  • 如果主机名不匹配任何键,则使用默认值 backend1.example.com

复杂匹配

map 指令支持更复杂的匹配模式,包括正则表达式。

示例如下:

http {map $request_uri $file_extension {"~*\.jpg$"  image;"~*\.png$"  image;"~*\.css$"  stylesheet;"~*\.js$"   javascript;default     other;}server {listen 80;server_name example.com;location / {set $content_type $file_extension;# 在此可以根据 $content_type 变量进行不同的处理}}
}

在这个例子中:

  • 根据请求的 URI,将 $file_extension 变量设置为不同的值。
  • 如果 URI 以 .jpg.png 结尾,则设置为 image
  • 如果 URI 以 .css 结尾,则设置为 stylesheet
  • 如果 URI 以 .js 结尾,则设置为 javascript
  • 如果 URI 不匹配任何模式,则使用默认值 other

注意事项

  • map 指令必须放在 http 块中,不能直接放在 serverlocation 块中。
  • map 指令中使用的变量必须在之前已经定义或已经存在。
  • map 指令中键的匹配是按顺序进行的,匹配到第一个符合条件的键时就会停止匹配。

实际应用

map 指令可以用于很多实际应用场景,比如:

  • 根据客户端 IP 设置访问限制或调整访问策略。
  • 根据 User-Agent 头设置不同的响应头。
  • 动态调整缓存策略。
  • 根据请求路径或参数动态选择后端服务器。

通过 map 指令,Nginx 的配置变得更加灵活和强大,可以根据实际需要进行复杂的条件判断和变量设置。

为什么 nginx 中需要 map 指令

在 Nginx 配置中,map 指令用于根据某个变量的值来动态设置另一个变量的值。这在许多情况下都非常有用,尤其是在需要根据请求的不同条件(如 URL、IP 地址、请求头等)来执行不同的配置或行为时。以下是一些具体的使用场景和map指令的详细解释:

使用场景

  1. 动态配置

    • 通过map指令,可以根据请求的特定条件(例如,客户端 IP 地址、请求路径、请求头等)来设置不同的 Nginx 配置项。
    • 例如,可以根据访问路径设置不同的后端服务器、不同的缓存策略或不同的访问控制策略。
  2. 简化配置

    • map指令可以简化复杂的条件判断逻辑,避免在配置文件中编写大量的if指令。
    • 通过集中管理映射规则,可以使配置文件更清晰、更易于维护。
  3. 负载均衡

    • 可以根据请求的属性(如 User-Agent 或 Cookie)将请求分配到不同的后端服务器,实现更灵活的负载均衡策略。

map 指令的语法和用法

map指令的基本语法如下:

map $variable_to_map $result_variable {default value;  # 设置默认值condition1 value1;  # 条件1 对应的值condition2 value2;  # 条件2 对应的值...
}
  • $variable_to_map:要根据其值进行映射的变量。
  • $result_variable:映射结果存储到的变量。
  • default value:如果没有匹配的条件,使用的默认值。
  • condition value:条件和值的对,满足条件时将值赋给$result_variable

示例

假设我们需要根据不同的主机名来设置不同的后端服务器:

http {map $host $backend {default web1.example.com;host1.example.com web2.example.com;host2.example.com web3.example.com;}server {listen 80;location / {proxy_pass http://$backend;}}
}

在这个示例中:

  • 根据请求的主机名($host),将 $backend 变量设置为不同的后端服务器。
  • 默认情况下,$backend 会被设置为 web1.example.com
  • 如果请求的主机名是 host1.example.com$backend 会被设置为 web2.example.com
  • 如果请求的主机名是 host2.example.com$backend 会被设置为 web3.example.com

结论

map指令在 Nginx 中是一个强大的工具,可以根据请求的条件动态设置变量,从而实现更灵活和可维护的配置。

通过合理使用map指令,可以简化配置文件,增强 Nginx 的功能,使其能够更好地适应各种复杂的应用场景。

java 实现

配置的解析

我们以一个比较全的配置为例

http {# 定义一个 map 指令,根据请求的主机名设置后端服务器map $host $backend {default web1.example.com;host1.example.com web2.example.com;host2.example.com web3.example.com;}# 定义另一个 map 指令,根据用户代理设置变量map $http_user_agent $mobile {default 0;"~*iphone|android" 1;}# others
}

配置加载

直接放在 http 的全局配置中,解析如下:

/*** @since 0.22.0* @author 老马啸西风*/
public class NginxUserMapConfigLoadFile implements INginxUserMapConfigLoad {//conf@Overridepublic NginxUserMapConfig load() {Map<String, String> mapping = new HashMap<>();NginxUserMapConfig config = new NginxUserMapConfig();List<String> values = mapBlock.getValues();if(values.size() != 2) {throw new Nginx4jException("map 指令的 values 必须为 2,形如 map $key1 $key2");}config.setPlaceholderMatchKey(values.get(0));config.setPlaceholderTargetKey(values.get(1));Collection<NgxEntry> entryList = mapBlock.getEntries();if(CollectionUtil.isEmpty(entryList)) {throw new Nginx4jException("map 指令的映射关系不可为空,可以配置 default xxx");}for(NgxEntry entry : entryList) {if(entry instanceof NgxParam) {NgxParam ngxParam = (NgxParam) entry;String name = ngxParam.getName();String value = ngxParam.getValue();// 对比if("default".equals(name)) {config.setDefaultVal(value);} else {mapping.put(name, value);}}}config.setMapping(mapping);return config;}}

map 指令的实现

目前实现简单的,在 dispatch 前触发 map 指令。

/*** @since 0.22.0* @author 老马啸西风*/
public class NginxMapDirectiveDefault implements NginxMapDirective {private static final Log logger = LogFactory.getLog(NginxMapDirectiveDefault.class);@Overridepublic void map(NginxRequestDispatchContext context) {Map<String, Object> placeholderMap = context.getPlaceholderMap();List<NginxUserMapConfig> mapConfigList = context.getNginxConfig().getNginxUserConfig().getMapConfigs();if(CollectionUtil.isEmpty(mapConfigList)) {// 忽略logger.info("mapConfigList 为空,忽略处理 map 指令");return;}for(NginxUserMapConfig mapConfig : mapConfigList) {processMap(mapConfig, placeholderMap);}}protected void processMap(NginxUserMapConfig mapConfig,Map<String, Object> placeholderMap) {//1. keyString matchKey = mapConfig.getPlaceholderMatchKey();String matchValue = (String) placeholderMap.get(matchKey);String targetKey = mapConfig.getPlaceholderTargetKey();// 遍历for(Map.Entry<String, String> mapEntry : mapConfig.getMapping().entrySet()) {if(matchValue == null) {logger.info("matchValue is null, ignore match");break;}String key = mapEntry.getKey();String value = mapEntry.getValue();if(key.equals(matchValue)) {// fast-returnplaceholderMap.put(targetKey, value);logger.info("命中相等 {}={}, {}={}", matchKey, matchValue, targetKey, value);return;} else if(matchValue.matches(key)) {placeholderMap.put(targetKey, value);logger.info("命中正则 {}={}, {}={}", matchKey, matchValue, targetKey, value);return;}}// 默认值placeholderMap.put(targetKey, mapConfig.getDefaultVal());logger.info("命中默认值 {}={}", targetKey, mapConfig.getDefaultVal());}}

测试验证

直接本地启用访问 http://192.168.1.13:8080/

日志:

信息: 命中默认值 $backend=web1.example.com
信息: 命中默认值 $mobile=0

小结

map 指令是 Nginx 中一个强大的工具,用于根据请求属性动态设置变量。

通过合理使用 map 指令,可以简化配置,提高性能和灵活性。

使用 Java 库 nginxparser 可以动态解析和处理 Nginx 配置文件,进一步增强配置管理的自动化和灵活性。

我们后续考虑继续学习下 rewrite try_files 等指令。

我是老马,期待与你的下次重逢。

开源地址

为了便于大家学习,已经将 nginx 开源

https://github.com/houbb/nginx4j

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/43940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在vue3中,手写父子关联,勾选子级父级关联,取消只取消当前子级,父节点不动

树形控件选择子级勾选父级&#xff0c;以及所有子级&#xff0c; 取消勾选仅取消子级 在项目中&#xff0c;可能会遇到这种场景&#xff0c;比如权限配置的时候&#xff0c;页面权限和菜单权限以tree的形式来配置&#xff0c;而且不用半选&#xff0c;菜单在页面的下面&#xf…

FTP与TFTP

1、TFTP&#xff08;简单文件传输协议&#xff09; TFTP是TCP/IP协议族中一个用来在客户机与服务器之间进行简单文件传输的协议&#xff0c;提供不复杂、开销不大的文件传输服务。 基于UDP协议 端口号&#xff1a;69 特点&#xff1a;简单、轻量级、易于实现 传输过程&…

人与机器的协同是强弱系统的互补行为

人与机器的协同可以被视作强弱系统的互补行为&#xff0c;这也强调了人类和机器之间在处理问题、执行任务或创造价值时各自的优势与角色。 人类在认知、创造力、情感和伦理等方面具有独特优势。我们能够进行高级的抽象思维、创新和复杂决策&#xff0c;能够处理不确定性和动态环…

《Windows API每日一练》9.13资源-鼠标位图和字符串

鼠标指针位图&#xff08;Mouse Cursor Bitmap&#xff09;是用于表示鼠标指针外观的图像。在 Windows 窗口编程中&#xff0c;可以使用自定义的鼠标指针位图来改变鼠标的外观&#xff0c;并提供更加个性化的用户体验。 ■以下是一些与鼠标指针位图相关的要点&#xff1a; ●…

量产工具一一业务系统(六)

目录 前言 一、代码流程框架 1.业务系统程序流程图 2.业务系统主页面流程图 3.main.c 4.main_page.c 二、处理配置文件 1.配置文件示例 2.处理配置文件 3.config.h 4.config.c 三、生成产品界面 1.计算每个按钮的范围 2.main_page.c 四、处理输入事件 1.main_…

【力扣C语言】每日一题—第121题,买卖股票的最佳时机

题目&#xff1a; 返回获取的最大利润&#xff1a; 这题可浪费时间了&#xff01;&#xff01;&#xff01; 主要是暴力求解超时&#xff01;&#xff01;&#xff01; 最后参考了一下答案 解题思路&#xff1a; 思路一&#xff1a; 暴力求解&#xff1a;两重for循环判断…

【Python实战因果推断】18_线性回归的不合理效果8

目录 Saturated Regression Model Regression as Variance Weighted Average Saturated Regression Model 还记得我在本章开头强调回归和条件平均值之间的相似性吗&#xff1f;我向你展示了使用二元干预进行回归与比较干预组和对照组的平均值是完全一样的。现在&#xff0c;由…

opencv--把cv::Mat数据转为二进制数据的保存和读取

保存 #include <opencv2/opencv.hpp> #include <iostream> #include <fstream>void saveMatToBinary(const cv::Mat& mat, const std::string& filename) {std::ofstream ofs(filename, std::ios::binary);if (!ofs.is_open()) {std::cerr <<…

Python | Leetcode Python题解之第225题用队列实现栈

题目&#xff1a; 题解&#xff1a; class MyStack:def __init__(self):"""Initialize your data structure here."""self.queue collections.deque()def push(self, x: int) -> None:"""Push element x onto stack."&…

LabVIEW机器视觉技术在产品质量检测中有哪些应用实例

LabVIEW的机器视觉技术在产品质量检测中有广泛的应用&#xff0c;通过图像采集、处理和分析&#xff0c;实现对产品缺陷的自动检测、尺寸测量和定位校准&#xff0c;提高生产效率和产品质量。 1. 电子元器件质量检测 在电子制造业中&#xff0c;电子元器件的质量检测是确保产品…

【基于深度学习方法的激光雷达点云配准系列之GeoTransformer】——粗配准

【GeoTransformer系列】——粗配准 1.coarse_matching1.1 概要1.2 功能1.3 超参1.4 input1.5 output2 coarse_target2.1 概要2.2 功能2.3 input2.4 output在模型部分有了初步了解之后, 接下来我们对后续的粗配准、精配准等部分依次进行解读. 本篇主要来看粗配准部分, 代码是在G…

websocket推送消息,模拟推送

上一篇文章&#xff1a;什么是webSocket&#xff1f;以及它的一些相关理论知识 背景&#xff1a; MQTT 的发布/订阅模式与 WebSocket 的双向通信特性相结合。 通过将 MQTT 与 WebSocket 结合使用&#xff0c;可以在 Web 应用中实现高效、实时的消息传输&#xff0c;特别适用于…

算法练习第29天|1005.K次取反后最大化的数组和

1005. K 次取反后最大化的数组和 - 力扣&#xff08;LeetCode&#xff09; 1.题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下…

24/7/10总结

flex布局 父项常见属性 justify-content:设置主轴上的子元素排列方式 flex-wrap:设置子元素是否换行 align-items:设置侧轴上的子元素的排列方式&#xff08;单行&#xff09; 拉伸要把子盒子里的高度给去掉 如果两个align-items都是center并且主轴是y轴就是这种效果…

Java | Leetcode Java题解之第225题用队列实现栈

题目&#xff1a; 题解&#xff1a; class MyStack {Queue<Integer> queue;/** Initialize your data structure here. */public MyStack() {queue new LinkedList<Integer>();}/** Push element x onto stack. */public void push(int x) {int n queue.size();…

操作系统:信号究竟是什么?如何产生?

OS信号 一、信号的概念二、信号的产生1&#xff09;终端按键产生信号1、 前台进程、后台进程2、验证终端按键是否产生信号 2&#xff09;调用系统函数向进程发信号3&#xff09;硬件异常产生信号1、浮点数溢出&#xff0c;CPU产生信号2 浮点数溢出&#xff0c;产生信号原理3. 空…

Mysql-内置函数

一.什么是函数&#xff1f; 函数是指一段可以直接被另外一段程序调用的程序或代码。 mysql内置了很多的函数,我们只需要调用即可。 二.字符串函数 MySQL中内置了很多字符串函数: 三.根据需求完成以下SQL编写 由于业务需求变更,企业员工的工号,统一为5位数,目前不足5位数的全…

深入浅出Transformer:大语言模型的核心技术

引言 随着自然语言处理&#xff08;NLP&#xff09;领域的不断发展&#xff0c;Transformer模型逐渐成为现代大语言模型的核心技术。无论是BERT、GPT系列&#xff0c;还是最近的T5和Transformer-XL&#xff0c;这些模型的背后都离不开Transformer架构。本文将详细介绍Transfor…

windows驱动开发基础-环境篇

前言 Windows上无论是用户模式下还是内核模式下&#xff0c;有关驱动的开发都有可能影响系统稳定性&#xff0c;所以我们首先要准备一个专用的测试环境&#xff0c;可以使用VM等虚拟机方便环境修复和还原 测试模式 开启测试模式&#xff1a;cmd 命令 bcdedit /set testsign…

量化机器人能否实现无缝交易?

量化机器人通过先进的算法和自动化技术&#xff0c;正在逐步实现无缝交易的目标。这种类型的交易系统设计用于最大限度地减少执行延迟&#xff0c;提高交易效率&#xff0c;并确保交易过程中的高度精确性和一致性&#xff0c;从而在金融市场中提供无缝且高效的交易体验。 无缝…