wenqiang li
wenqiang li
ngx http日志抽样采集。 ### 场景 官方日志不支持抽样采集,而我们使用nginx时会有这样的需求,有的状态码的日志会全量采集,有的会需要抽样采集。所以,通过官方提供的配置实现不了这样的需求。 ### 思路 为了实现这样的需求,只能修改nginx的日志模块的代码,添加功能。 需要添加如下配置:`sample=50%:200,304` 表示对状态码为200和304做抽样采集,每100条采集50条。 代码后期会放倒github上。 如果采集的条件不止是对status,那么可以把指令修改成`sample=50%$status:200,304`。status可以是其它变量,后面200,304时对应的取值。 ### ngx_http_log_module 代码分析 #### 概述 该模块通过模版和变量的支持,可订制请求日志输出格式和内容。 如下是官方文档一个例子: >> log_format compression '$remote_addr - $remote_user [$time_local] ' '"$request" $status $bytes_sent...
### 概述 http协议不具备服务端主动给客户端发消息,WebSocket的出现,使得浏览器具备了实时双向通信的能力。 WebSocket复用了HTTP的握手通道,而nginx作为http协议代理也需要处理这种变更。 #### 请求协议升级: ``` GET / HTTP/1.1 Host: www.test.com Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: xxxxxxx ``` * `Connection: Upgrade`:表示要升级协议 * `Upgrade: websocket`:表示要升级到websocket协议。 * `Sec-WebSocket-Version: 13`:表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。...
nginx Transfer-Encoding: chunked 问题排查 ### 概述 有网站接入nginx以后报浏览器有白页。 我curl的结果返回是 `curl: (52) Empty reply from server`, 从日志报表也看出什么异常的请求。 查了下对应的access日志,返回的是正常的状态码。 看错误日志,提示`upstream sent invalid chunked response while reading upstream`. 通过nc和openssl 分别对80和443端口发送请求过去,返回的确实是chunked编码的内容。 到底是返回的内容有问题,还是nginx解析chunked有问题。 ### 问题定位 我们先跟踪一下nginx代码:...
### 概述 nginx中使用文件的地方有配置文件和日志、临时文件。实现在ngx_files.h|c ngx_file.c文件中。 临时文件在nginx中用来临时保存body,在ngx_http_write_request_body函数中使用。 ### 实现 ```c // 路径,一般通过ngx_conf_set_path_slot函数设置 typedef struct { ngx_str_t name; // 路径的名字 size_t len; // 路径下子目录的长度,包括分隔符。例如/ab/cde 长度是7 size_t level[NGX_MAX_PATH_LEVEL]; // 路径下的子目录,最多3级:2 3。数组内容是每一级子目录路径长度 例如 2 3...
### 概述 nginx是一个功能强大的软件,在使用的时候有一些配置的结果和你历史的经验不一致、和你理解的预期不一致。 稍不注意就会在使用过程成中出现意外的惊喜。 ### 需要注意的指令 + 请求头允许使用下划线(underscores_in_headers) 多个server块监听同一个IP,default server没有配置`underscores_in_headers on;`,跳转的server配置了`underscores_in_headers on;`。 那么,在请求中,优先`Host` 传递的带下划线的请求头都被忽略了。 具体:https://trac.nginx.org/nginx/ticket/578#no3 + 内部location(internal) ```nginx server { listen 127.0.0.1:8080; server_name localhost; location /abc { proxy_pass http://127.0.0.1:8081; }...
### 概述 15年4月发布的ngx1.9.0版本,nginx以stream模块支持了4层代理的功能。 ### 代码分析 #### 启动阶段 stream模块定义在ngx_stream.c文件中。在ngx_init_cycle函数中会调用ngx_conf_parse解析配置文件。 stream 的block会由核心模块的ngx_stream_block函数来解析。 在函数中会分配一个ngx_stream_conf_ctx_t类型的上下文,来保存stream模块的配置结构体。 接着会调用stream类型模块的create_main_conf回调和create_srv_conf回调创建相关模块的配置结构体。 接着还会调用stream类型模块的preconfiguration回调来添加变量等。接着调用ngx_conf_parse函数解析stream模块的配置。解析完配置会调用stream模块的init_main_conf和merge_srv_conf初始化配置并合并一些配置。 解析完配置文件中stream中的配置后,调用ngx_stream_init_phases开始初始化执行阶段。 调用stream模块的postconfiguration回调函数添加执行阶段回调函数。 接着调用ngx_stream_init_phase_handlers函数初始化执行阶段。在该函数中,首先会根据stream模块安装到个阶段的所有回调的个数分配一个数组,赋值到cmcf->phase_engine.handlers,其类型为:ngx_stream_phase_handler_t ```c static ngx_int_t ngx_stream_init_phase_handlers(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf) { ngx_int_t j; ngx_uint_t i, n; ngx_stream_handler_pt...
### 概述: 在nginx做一些请求内容过滤的操作。当用户上传一个大文件时,偶然会有上传失败的情况,当关闭内容过滤时问题不再发生。 初步定位是和文件过滤有关系,在测试环境模拟了很多次都无法复现。 ### 问题追踪 + 在生产环境抓包,上传失败是因为nginx发送rst包reset和客户端的连接。 首先reset肯定不是一个正常的关闭连接。先排查是否nginx在某种情况下会发送reset。 查看了nginx代码,发现只要配置了`reset_timedout_connection`指令就会有reset连接的可能,看线上配置也没有配置该指令。 + 继续查看抓包文件,发现nginx接收窗口先满,大约60s内客户端一直再发送零窗口探测。然后nginx才发送reset。 零窗口探测难道有超时设置,查看《tcp/ip协议详解》几乎说所有的实现都没有超时,零窗口会一直探测。 + 又仔细看了一下抓包文件,发现nginx接收窗口先满,大约60s内客户端一直再发送零窗口探测,然后源站发送fin包关闭了和nginx的连接,接着才是nginx reset了客户端的连接。 猜测是源站读请求内容超时,导致源站关闭连接,发送fin包。 分析了nginx核心循环的代码,nginx在主循环里优先处理网络事件,因此可能是包过滤调用了hypherscan导致nginx进程陷入。 而源站关闭连接后,nginx也要关闭和客户端的连接,而此时接收窗口还有数据没有接收。网络层就发送rst包reset掉和客户端的连接了。 以下python代码可以复现reset包。 ```py # -*- coding: utf-8 -*- import socket import sys...
### 概述 a.com的响应里,通过js 向b.com发起了异步请求(xhr)请求。而这次请求就是跨域请求。 浏览器保护机制,跨域请求是有限制的。此时就需要跨域资源共享(CORS)来突破这种限制。 CORS需要浏览器和服务器同时支持,主要是服务器。 ### 跨域资源共享(CORS) 浏览器将跨域请求分为2类,一类是简单请求。一类是非简单请求。 同时满足一下2个条件的就是简单请求,否则就是非简单请求。 1. 请求方法 + HEAD + GET + POST 2. 请求头只能带以下头 + Accept + Accept-Language + Content-Language + Content-Type - text/plain...
# redis module 开发与解析 ## 概述 redis作者antirez前几天在自己的博客更新了一篇文章说[redis4.0支持module](http://antirez.com/news/110)。他自己写了一个神经网络的模块,也有开发者写了图表DB、二级索引、全文索引等模块。并且说这只是刚刚开始,你可以写一些有意义的不止是和缓存相关的模块。 相关的代码已经在github上开源,在[github.com/antirez](https://github.com/antirez/redis/tree/4.0)仓库redis4.0分支。 redis是通过开放的一系列[API](https://github.com/RedisLabs/RedisModulesSDK/blob/master/FUNCTIONS.md)来支持模块开发,并且在代码中给出了几个实例,在redis4.0分支的github.com/antirez/redis/src/modules路径下。 ## 模块开发示例 通过一个代码示例来说明如何开发一个模块。下面是官方helloworld.c的代码的一部分。 cat hello.c ```c // 必须包含 #include "../redismodule.h" #include #include #include #include // 返回当前选中的db int HelloSimple_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv,...
### 概述 最近我一直在做动态加载的事情,希望把常用的变更都通过lua动态设置并生效到nginx内核中。 这就包括了根据SNI动态修改tls的版本和加密套件,因为理论上在SSL的client hello后才进行版本的协商,在SNI回调函数修改ssl版本应该可以行的通的,为此我修改了openresty的源码,从增加一个函数(`ssl.set_ssl_protocols`)到增加一个阶段(`ssl_sni_by_lua_block`)都尝试过,并翻看了openssl相关代码,也没有得到解决。 通过翻看openssl的代码,大概看到通过SNI的回调函数修改版本是非常困难的一件事情。不同版本的TLS_method定义的回调函数差异还是蛮大的。因此我决定测试一下nginx的`ssl_protocols`是否生效,最后发现也不生效。于是就想要探究一下nginx是如何对待这个问题的。 ### 问题跟踪 搜了一下,已经有人提了该问题。 https://trac.nginx.org/nginx/ticket/1714 https://trac.nginx.org/nginx/ticket/844 >> The ciphers are used from the SNI-based virtual server. Protocols aren't, and this is unlikely to change, see...