Coder Social home page Coder Social logo

vaynedu / nginx-1.16.0 Goto Github PK

View Code? Open in Web Editor NEW
12.0 1.0 2.0 1.62 MB

学习nginx架构设计与实现,翻译nginx的源码,写nginx的测试代码, 在issue中记录nginx的精妙设计及其常见问题https://github.com/vaynedu/nginx-1.16.0/issues 。 myexercise内存池、哈希表、链表、md5、crc测试代码,mymodule中有hello自定义模块代码。通过nginx将自己整个知识体系连接起来

Home Page: https://github.com/vaynedu/nginx-1.16.0/issues

License: BSD 2-Clause "Simplified" License

Perl 0.13% HTML 0.02% C 95.92% C++ 0.80% XS 0.40% Makefile 0.10% Lua 0.07% Shell 0.08% Vim Script 2.48%
nginx nginx-module nginx-configuration

nginx-1.16.0's Introduction

nginx-1.16.0

nginx-1.16.0源码分析及其注释--记录心路历程,有问题随时沟通交流,一起学习

调试练习ngx_pool.c的代码

调试练习ngx_hash.c的代码

调试练习ngx_list.c的代码

调试练习ngx_md5.c的代码, ngx_sha1.c亦是如此

调试练习ngx_crc.c的代码

1.ngx_http_myhello_module

自己编写的一些hello模块,用来熟悉和掌握nginx

./configure --prefix=/usr/local/nginx --with-cc-opt="-O0" --add-module=./third-module/tmp/echo-nginx-module-0.61 --with-debug --add-module=./mymodule/ngx_http_myhello_module/ && make -j8 && make install

三.已翻译代码

1.HTTP模块

a) HTTP的11个阶段入口 src/http/ngx_http_core_module.c -- 特别重要

b) 待添加

2.配置相关

a) 模块上下文结构体 --- 待完成

b) 模块结构体 --待完成

c) ngx_command_t 配置指令结构体 -- 待完成

3.数据结构

a) ngx_pool_t 内存池--src/core/ngx_palloc.c、src/os/unix/ngx_alloc.c

b) ngx_hash_elt_t nginx哈希表 -- src/core/ngx_hash.c

c) ngx_array_t 动态数组 -- src/core/ngx_array.c

d) ngx_list_t 链表 -- src/core/ngx_list.c

e) ngx_md5_t && ngx_sha1_t 生成md5, 增加封装 -- src/core/ngx_md5.c src/core/ngx_sha1.c

f) ngx_crc32 crc使用 -- src/core/ngx_crc32.c

1.echo-nginx-module

nginx.conf中通过echo等指令直接输出包体, 类似于nginx的命令,方便调试配置

./configure --prefix=/data4/nginx/nginx --add-module=./third-module/echo-nginx-module-0.61 && make -j8 && make install

1. nginx

a) 《深入理解nginx》--陶辉

b) 《深入剖析nginx》

2. http

a) 《图解HTTP》

b) 《图解TCP/IP》

3.git

a) git color着色初始化脚本

4.gcc

《如何处理linux库的同名函数》

1. ab 压测工具

yum install httpd-tools

直接上手ab -n 1000 -c 5 http://1.1.1.1/1.mp4

2. stress

待添加

记录一些常用的初始化脚本

待补充

八.联系方式

2.QQ二维码

nginx-1.16.0's People

Contributors

vaynedu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar

nginx-1.16.0's Issues

nginx的server_name 竟然不支持下划线?

server_name _;

这个代表的就是无效域名,_符号可以用-或!@#代替,都可以达到相同的效果

上次配置了一个下划线的server_name, 结果nginx返回了400的错误,讲道理我server_name应该是合法的吧

SSD使用寿命评估

看到同事推送一盘文章,我觉得不合理Flash闪存颗粒和SSD知识深度解析

如果每天对SSD写入4.8GB的数据,假设SSD总容量为16GB,那么,你至少需要3.34天才能对整个SSD的每个单元擦写一次;如果此SSD为擦写次数为100K的SLC单元,那么,你至少需要3.34×100K天才能使这个SSD完全失效;3.34×100K天=913年,因此16G的SSD可以使用913年。那么,如果是MLC的话,也至少可以使用91.3年。

我觉得观点如下

16G盘 10%的预留,用来替换以后坏掉的flash。 所有可用的总量就是14.4G
现网的情况是: 270G 盘(可用240G评估), 使用900天(2年半), 每天写400多G数据(我按照ssd滚两遍,480G计算), 推算 270G的系统盘写入总数据量就是432T, 16G盘写入的总数据量就是26T(25920G),
所以, 按我的理解, 16G盘每天写4.8G,大约使用14年(5400天)

nginx cpu的亲和性worker_cpu_affinty?

CPU亲和性,简单点说就是让某一段代码或者数据尽量在指定的CPU上长时间运行。

1. CPU的亲和性有什么好处?

 提供CPU cache的命中率,提高性能

2. nginx使用下面命令来保证CPU的亲和性,具体配置子网掩码

  worker_processes 1;
   worker_cpu_affinty  01;

3. 其实也可以参考下linux命令 taskset, 也是用来指定CPU的

请教Nginx upstream 名称的长度限制

大神你好:
看不懂源码,所以向你请教下nginx upstream的名字长度限制是多少,另外zone的命名长度限制是多少
恳请大拿给解惑呀

nginx互斥锁是如何实现的?

nginx互斥锁有3中实现方式:自旋锁(原子操作)、文件锁、原子操作+信号量

一. 自旋锁(原子操作)

1. 如何判断当前操作系统是否支持原子操作?

volatile 关键字告诉C编译器不要优化,就看是否支持volatile关键字,如果不支持,nginx模拟实现支持,这个需要使用到内联汇编, 需要使用到__asm__ 关键字

2.nginx的自旋锁如何实现?

a) 其实锁很简单,也很好理解,锁只是一种同步的方式,保证资源的安全访问。怎么可以做到这一点呢,多进程环境,大家去共享内存去获取这个变量,哪个进程获取到这个变量,就相当于谁有锁, 谁就有权利访问这个资源。
b) 自旋锁: 进程获取不到锁就一直循环等待,之道获取到锁为止。nginx在自旋锁中使用了PAUSE指令用来提升CPU的性能,节省电量。
c) 这部分代码比较简单,一目了然

二. 文件锁

1.文件锁的原理就是在磁盘上创建一个文件(操作系统创建一个文件描述符),然后多个进程竞争去获取这个文件的访问权限,因此同一时刻只有一个进程能够访问临界区。
2.文件锁创造一个文件之后,会直接删除这个文件,只是用其fd (内核的信息结构),后面通过fcntl函数设置fd的属性,控制加解锁。
3.效率低下,不建议使用

三、原子操作+信号量

支持信号量只会影响阻塞进程的ngx_shmtx_lock方法持有锁的方式。当不支持信号量,就是自旋锁,支持信号量时,ngx_shmtx_lock将spin指定的一段时间内自旋等待其他CPU释放锁,如果达到spin的上限还没有获得锁,那么将会sem_wait使得当前进程进入睡眠状态,等待其他进程释放锁内核唤醒这个进程。nginx在spin上限之后如果还获得不了锁,很可能强制获得锁。 反正nginx作为高性能服务器,容不得半点阻塞,否则的很多连接就饿死了。
目前不清楚nginx到底是什么强制获得锁呢?猜测是不是特别高的优先级。疑问
备注:信号量使用不好很可能导致进程睡眠
todo: 信号量现在基本没有怎么使用,对这个不怎么清楚。

个人觉得: 原子操作 + 信号量 和 自旋锁都可以吧, nginx自旋锁很适合这种简单的场景,表现应该不会比 原子操作 + 信号量 实现方式差(虽然说自旋锁会盲目的旋转等待)

如何查看linux文件的pagecache情况-vmtouch?

vmtouch - the Virtual Memory Toucher 就是用来查看linux文件缓存(page cache)使用情况,命中率

现网是真正提升能力的地方,因为机器高负载之后,会出现各种各样的问题,包括很很多"假象". 而机器高负载的原因很多,比如内存. 之前遇到的问题就是, pagecache使用过多,导致内存不足,然后接着cpu飙升,> 严重影响业务质量.这就是为什么要搞个vmtouch的原因. 我要查看哪些文件大量使用page cache, 然后好直接 echo 3 > /proc/sys/vm/drop_caches ,暴力清除缓存

详细请看vmtouch

nginx的延迟关闭?

1. 为什么nginx会有延迟机制?

nginx延迟关闭机制,只是为了接收来自客户端的剩余数据,接收完数据后在关闭tcp连接与释放http请求。 不让正常的连接rst。 开启延迟机制,会发送rst报文,不开启延迟机制,正常四次挥手

2. nginx延迟机制的触发条件?

知道延迟关闭所要避免的就是在执行close后之后,却由于接收缓冲区中存放了客户端发来的数据,或者正在接收客户端的数据而导致发送RST复位报文异常终止连接所带来的负面影响。这个RST复位报文可能导致之前发送给客户端并且尚在网络或者客户端接收缓冲区的正常响应数据丢弃。

3 nginx的延迟关闭如何实现,并且会做什么操作?

  非常的简单,shutdown函数,请问一下shutdown和close有什么区别呢

4.nginx的延迟机制和keepalive有什么区别?

nginx延迟关闭机制,只是为了接收来自客户端的剩余数据,接收完成后还是会马上被关闭tcp连接的, 这一点还是与keepalive机制有区别的。keepalive在一个请求结束时,tcp连接并没有关闭,可以继续处理来自客户端的剩余的http请求

CDN集群突发热点怎么办?

nginx为什么会没有集群? 或者是因为集群太复杂,或者是nginx天生就用来反向代理,根本不需要集群。所以nginx注定不能用作高性能的CDN缓存服务器,即使nginx没有集群,但是也必须考虑集群突发热点。
举例来说:
一个集群4台机器,这4台机器同于同一个文件,只存储一份(集群可以理解为一个超级性能的计算机), 但是呢,如果这个文件是热点文件? 是不是存储该文件的机器会跑爆?那应该怎么处理?

集群热点功能就可以防止内网跑爆,效果非常明显
image

((sizeof(n)+sizeof(int)-1)&~(sizeof(int)-1))

原文链接
看到一个很有意思的代码,就在这边记录一下

_INTSIZEOF(n)整个做的事情就是将n的长度化为int长度的整数倍。

#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) – 1) & ~(sizeof(int) – 1)

比如n为5,二进制就是101b,int长度为4,二进制为100b,那么n化为int长度的整数倍就应该为8。(sizeof(int) – 1) )就应该为(4-1)=~(00000011b)=11111100b,这样任何数& ~(sizeof(int) – 1) )后最后两位肯定为0,就肯定是4的整数倍了。(sizeof(n) + sizeof(int) – 1)就是将大于4m但小于等于4(m+1)的数提高到大于等于4(m+1)但小于4(m+2),这样再& ~(sizeof(int) – 1) )后就正好将原长度补齐到4的倍数了

nginx中字节对齐如何实现?4字节对齐为什么低两位是0?

1. nginx 哪一块的代码使用了此技巧?

ngx_hash_wildcard_init函数

name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));

2.nginx如何实现地址对齐?

#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1))
#define ngx_align_ptr(p, a) \
     (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))

3. 为什么需要内存对齐?内存对齐有什么好处?

需要字节对齐的根本原因在于CPU访问数据的效率问题。32位系统中,数据总线宽度为32,每次能够读取4字节数据。地址总线为32,最大寻址空间为4GB。如果内存地址空间4字节对齐,那么低两位就是0,这样每两个地址之间的字节数都是4的整数倍

4. 内存不对齐有什么影响?

例如int a的地址是0x00fffff3,则其字节分布在0x00fffff3~0x00fffff6空间内,为了读取这个int,cpu必须对 0x00fffff0和0x00fffff4进行两次内存读取,并处理得出的中间结果。两次内存访问将会浪费大量的时间,因为内存访问的速度远小于CPU 处理指令的速度。

nginx的打印类型?

src/core/ngx_string.c

     * nginx 打印无符号整型和C语言不一样,这里有自定义的格式
     *
     * src/core/ngx_string.c
     *
     * %O — off_t
     * %T — time_t
     * %z — ssize_t
     * %i — ngx_int_t
     * %p — void *
     * %V — ngx_str_t *
     * %s — u_char * (null-terminated)
     * %*s — size_t + u_char *
     *
     * 大多数类型上加上前缀来使它们无符号。 要将输出转换为十六进制,请使用X或x
/*
 * supported formats:
 *    %[0][width][x][X]O        off_t
 *    %[0][width]T              time_t
 *    %[0][width][u][x|X]z      ssize_t/size_t
 *    %[0][width][u][x|X]d      int/u_int
 *    %[0][width][u][x|X]l      long
 *    %[0][width|m][u][x|X]i    ngx_int_t/ngx_uint_t                                                                                                                                         
 *    %[0][width][u][x|X]D      int32_t/uint32_t
 *    %[0][width][u][x|X]L      int64_t/uint64_t
 *    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t
 *    %[0][width][.width]f      double, max valid number fits to %18.15f
 *    %P                        ngx_pid_t
 *    %M                        ngx_msec_t
 *    %r                        rlim_t
 *    %p                        void *
 *    %V                        ngx_str_t *
 *    %v                        ngx_variable_value_t *
 *    %s                        null-terminated string
 *    %*s                       length and string
 *    %Z                        '\0'
 *    %N                        '\n'
 *    %c                        char
 *    %%                        %
 *
 *  reserved:
 *    %t                        ptrdiff_t
 *    %S                        null-terminated wchar string
 *    %C                        wchar
 */

如何设计一个屏蔽逻辑?

  1. 这里首先要考虑为什么需要屏蔽逻辑?
    服务器宕机、程序挂掉,端口不通,机房断电等多种情况, 目前不影响正常的服务
  2. 这里如何屏蔽呢,何时屏蔽呢,触发条件?
    访问端口不通,超时
    比如多长时间内,失败多少次,屏蔽多少秒

nginx为什么不使用多线程而采用多进程呢?

  1. 稳定性
    多进程隔离性好,容错性更强。单个进程过掉不会导致整个程序不可用。多线程共享进程的地址空间,一个线程奔溃会导致整个服务不可用。
  2. 复杂性
    a.多进程编程相对简单,只需要考虑IPC。而多线程同步比较复杂。
    b.多进程调试也比较容易(nginx单进程就是用来调试开发)
  3. 场景选择
    nginx 的master + worker架构, 异步非阻塞处理网络事件,无需频繁上下文切换(CPU亲和性), 也没有涉及大量IO,也没有线程间全局共享进程数据的场景,所以nginx没有必要使用多线程架构(共享变量很容易产生并发bug,加锁又影响性能,最好通过架构规避)
    目前nginx更多是用作反向代理,而不是缓存服务器。所以nginx的多进程架构非常适

ngx_strcasestrn(h->value.data, "close", 5 - 1) 为什么要设计成 5 -1 ?

设计成5-1,在编译的时候就可以确定。
如果设计n--, 运行时还要计算一次。

这应该是nginx的设计的精妙之处,提升性能的地方吧

u_char *
ngx_strcasestrn(u_char *s1, char *s2, size_t n)
{
    ngx_uint_t  c1, c2;

    c2 = (ngx_uint_t) *s2++;
    c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
                                                                                                                                                                                             
    do {
        do {
            c1 = (ngx_uint_t) *s1++;

            if (c1 == 0) {
                return NULL;
            }

            c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;

        } while (c1 != c2);

    } while (ngx_strncasecmp(s1, (u_char *) s2, n) != 0);

    return --s1;
}

nginx的keepalive机制?

TCP 的 keepalive机制 : 按照心跳包来理解非常容易, 有没有必要维持TCP连接

HTTP的keepalive机制 : TCP连接复用 , 一个TCP连接上面可以连接多个http请求。如果长时间收不到新的HTTP请求,nginx断开这个TCP长连接

1. 服务器怎么判断该请求是长连接?

在http1.0协议里,客户端通过发送connection: keep-alive的请求头来实现与服务器之间的长连接。 http1.1默认支持keepalive, 但通过请求头connection: close可明确要求不进行长连接保持
nginx的代码可搜索ngx_http_process_connectionngx_http_handler函数

2. nginx的keepalive怎么设置的?能保存多长时间?

当一个http请求完成后, 如果引用计数为0了,会释放这个http请求,但到底要不要释放tcp连接,是由keepalive机制与延迟关闭机制决定的,具体在ngx_http_finalize_connection函数中。
至于keepalive保持多长时间在nginx,conf根据业务特性配置

3.长连接的配置项

keepalive_timeout 长连接超时时间
keepalive_requests 一个连接上的最大请求数(一直对这里存在疑问)

nginx的该keepalive_requests 配置项是不是防止长连接 长时间不释放出出问题吧(内存暴涨?其他异常问题)。 本身对于业务场景来说,还是觉得nginx 长连接长时间不释放不会导致内存暴涨

nginx创建子进程的过程中,被信号打断了,此时master会如何处理,nginx会不会有异常?

信号还有好几个问题待整理 todo

比如,刚fork出worker,worker因为异常推出,master收到SIGCHLD信号,如果执行信号处理函数会导致master的进程管理结构错误初始化。

nginx创建子进程的过程分为:
1)fork出子进程。
2)初始化子进程控制结构。

1.通过sigprocmask先阻塞设置的信号,然后置空set,后面取消阻塞的时候会用到。

void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{
    ...
    sigemptyset(&set);
    sigaddset(&set, SIGCHLD);
    sigaddset(&set, SIGALRM);
    sigaddset(&set, SIGIO);
    sigaddset(&set, SIGINT);
    sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
    sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));

    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "sigprocmask() failed");
    }

    sigemptyset(&set);

    ...

2.“临界区”执行fork,设置全局初始化,防止信号中断导致出错。这里主要是在ngx_worker_process_init中执行的。

void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{
    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ngx_start_worker_processes(cycle, ccf->worker_processes,
                               NGX_PROCESS_RESPAWN);
    ngx_start_cache_manager_processes(cycle, 0);

}



static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{

    for (i = 0; i < n; i++) {
        ngx_spawn_process(cycle, ngx_worker_process_cycle,
                          (void *) (intptr_t) i, "worker process", type);
    }
}

static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
   ngx_worker_process_init(cycle, worker);
}

非常重要: nginx的worker进程初始化已经完成,直接结束信号阻塞,表示可以接受信号了
static void
ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
{
   ...
    sigemptyset(&set);

    if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "sigprocmask() failed");
    }

   ...
}


3.通过sigsuspend取消阻塞信号,等到信号到来


void
ngx_master_process_cycle(ngx_cycle_t *cycle)
{
   sigsuspend(&set);
}

ngx_hash_elt_t 设计的巧妙之处

源码src/core/ngx_hash.h

typedef struct {
    void             *value;
    u_short           len;
    u_char            name[1];   
} ngx_hash_elt_t; 

我认为这里的ngx_hash_elt_t结构体设计成name[0] 或者name[1]一样的效果。主要考虑的是C语言数组可变长(柔性数组),申请的是连续的一段空间,便于后续释放吧

nginx的reuseport?

nginx在多进程模式下,给每个进程都clone监听一个fd。 有内核机制保证平均分配连接给每个worker进程。 直接从内核层面解决了。 从而解决了当一个连接带来的惊群问题。为什么多个work进程可以监听同一个端口呢,因为都来自一个同一个父进程,共享文件描述符。

既然reuseport这么好用,为什么nginx不默认使用reuseport。这个还真不知道。没查到,正在确定一下吧

nginx竟然没有集群的概念?

1. nginx缺少集群的概念

多个nginx不能组成一个超级大的集群 直接对外服务。都需要nginx反向代理
业界构建高可用集群 nginx + keepalive,自我认为没有高端之处,只是考虑的主备容灾

nginx的channel机制?

nginx的master和worker进程如何通信,就是通过sockerpair建立一个channel,master进程接收外部信号,通过channel传递给worker进程(具体可以看ngx_channel_handler函数), 如何是直接kill系统调用肯定不经过channel。 下面这段代码浅显易懂,worker进程一直监听读chanenl,只要master发消息过来,立即接受处理

static void
ngx_channel_handler(ngx_event_t *ev)
{
    for ( ;; ) {

        n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);
        switch (ch.command) {

        case NGX_CMD_QUIT:
            ngx_quit = 1;
            break;

        case NGX_CMD_TERMINATE:
            ngx_terminate = 1;
            break;

        case NGX_CMD_REOPEN:
            ngx_reopen = 1;
            break;

        case NGX_CMD_OPEN_CHANNEL:

            ngx_processes[ch.slot].pid = ch.pid;
            ngx_processes[ch.slot].channel[0] = ch.fd;
            break;

        case NGX_CMD_CLOSE_CHANNEL:
            if (close(ngx_processes[ch.slot].channel[0]) == -1) {
                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
                              "close() channel failed");
            }
            ngx_processes[ch.slot].channel[0] = -1;
            break;
        }
    }
}

感觉nginx的事件机制还是太强大了, nginx在ngx_worker_process_init初始化的时候建立channel, 并且直接加入事件监听机制

    if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,
                              ngx_channel_handler)
        == NGX_ERROR)
    {
        /* fatal */
        exit(2);
    }

我觉得nginx channel机制也就是用作父子进程通信,并没有什么其他的作用

为什么我视频的url在浏览器中是直接下载而不是播放?

一、如果在线播放视频?

1, 需要浏览器支持(google浏览器就比较好一点)
2. 增加相应的headerContent-Type: video/mp4(如果是mp4文件)

二、如何浏览器直接下载?

Content-Disposition: attachment;
返回http头部

HTTP/1.1 200 OK
Server: NWS_Svideo
Connection: keep-alive
Date: Wed, 24 Jul 2019 12:49:14 GMT
Last-Modified: Wed, 24 Jul 2019 12:29:49 GMT
Content-Type: video/mp4
Content-Length: 13068927
X-NWS-LOG-UUID: 362d8435-d67c-4726-bb92-8b3372126606 c8598114ad6c9cf221efaf5fb2ad036e
Access-Control-Allow-Origin: *
Content-Disposition: attachment;filename=aaaa

服务器如何做到回源容灾?

upstream_domain -- 主域名
fwd_host -- 主host
backup_upstream_domain -- 备域名
backup_fwd_host --备host

  1. 什么时候出发回源容灾逻辑?这里逻辑到底是怎么进行的?
  2. nginx目前是怎么实现的?

记一个flag

hls-ts渐进式下载过程中跳包:2-3次ts GET请求返回1片ts媒体

问题: hls-ts渐进式下载过程中跳包:2-3次ts GET请求返回1片ts媒体
现象:【TCP ACKed unseen segment】 --- ACK包没有被抓到
原因 : 这个和跳包没有关系, 下载速度太快,wireshark概率性的抓不到,在我限速之后抓包一切正常,不会出现跳包

nginx的ngx_shmtx_lock()原子锁 pause指令?

1. pause指令介绍

这里不直接谈nginx原子锁如何实现,只谈pause指令。
intel的pause指令, 减少cpu的消耗,节省电量。指令的本质功能:让加锁失败时cpu睡眠30个(about)clock,从而使得读操作的频率低很多。流水线重排的代价也会小很多。

Description
Improves the performance of spin-wait loops. When executing a “spin-wait loop,” a Pentium 4 or Intel Xeon processor suffers a severe performance penalty when exiting the loop because it detects a possible memory order violation. The PAUSE instruction provides a hint to the processor that the code sequence is a spin-wait loop. The processor uses this hint to avoid the memory order violation in most situations, which greatly improves processor performance. For this reason, it is recommended that a PAUSE instruction be placed in all spin-wait loops.

PAUSE指令提升了自旋等待循环(spin-wait loop)的性能。当执行一个循环等待时,Intel P4或Intel Xeon处理器会因为检测到一个可能的内存顺序违规(memory order violation)而在退出循环时使性能大幅下降。PAUSE指令给处理器提了个醒:这段代码序列是个循环等待。处理器利用这个提示可以避免在大多数情况下的内存顺序违规,这将大幅提升性能。因为这个原因,所以推荐在循环等待中使用PAUSE指令。

PAUSE的另一个功能就是降低Intel P4在执行循环等待时的耗电量。Intel P4处理器在循环等待时会执行得非常快,这将导致处理器消耗大量的电力,而在循环中插入一个PAUSE指令会大幅降低处理器的电力消耗。

2.nginx使用pause指令?

nginx的原子锁ngx_shmtx_lock()函数中使用 ngx_cpu_pause()

#if ( __i386__ || __i386 || __amd64__ || __amd64 )
#define ngx_cpu_pause()             __asm__ ("pause") 
#else
#define ngx_cpu_pause()
#endif

3. 其他代码使用pause指令

intel的pause指令  __asm__ (".byte 0xf3, 0x90") 

#define NOP_CPU3(n)  {int i = 0; while(i++ < (n)) cpu_pause();}

#define cpu_pause()         __asm__ (".byte 0xf3, 0x90")

nginx的NGX_HTTP_FIND_CONFIG_PHASE的阶段为什么不能挂载handler?

NGX_HTTP_FIND_CONFIG_PHASE就是寻找location的阶段,nginx官方要求在该阶段不要挂载handler,就算挂载了handler的也不会处理。

我觉得NGX_HTTP_FIND_CONFIG_PHASE该阶段就很简单,本身只是查找一个location,没有必要的介入。除此之外,我觉得查找location的规则的不能被随便修改,否则不能正确的跳到下一个阶段

ngin支持多range请求?

1. 多range请求返回结果

[email protected]:~#curl "123.206.25.239" -v  -H'Range: bytes=0-50, 100-150'
* About to connect() to 123.206.25.239 port 80 (#0)
*   Trying 123.206.25.239...
* Connected to 123.206.25.239 (123.206.25.239) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 123.206.25.239
> Accept: */*
> Range: bytes=0-50, 100-150
> 
< HTTP/1.1 206 Partial Content
< Server: nginx/1.8.0
< Date: Tue, 24 Sep 2019 13:11:04 GMT
< Content-Type: multipart/byteranges; boundary=00000000000000000020
< Content-Length: 303
< Last-Modified: Mon, 28 Aug 2017 03:17:03 GMT
< Connection: keep-alive
< ETag: "59a38b2f-1197"
< 

--00000000000000000020
Content-Type: text/html
Content-Range: bytes 0-50/4503

<!doctype html>
<html>
<head>
<meta charset="utf
--00000000000000000020
Content-Type: text/html
Content-Range: bytes 100-150/4503

shortcut icon" href="images/logo.ico" type="image/x
--00000000000000000020--
* Connection #0 to host 123.206.25.239 left intact

2. 多range请求实现

源码位置:src/http/modules/ngx_http_range_filter_module.c
实现也特别简单,多range无非就通过特定的分割串标识一下就行,按照约定的格式,填充响应头和包体即可,没有难的地方。

妹子反馈: nginx的worker进程获取不到环境变量getenv("PATH")为空?

这里我肯定怀疑,肯定不相信
worker进程 被 master进程 fork出来,肯定也继承环境变量,测试代码:实践往往是最好的证明

#include <sys/types.h>
#include <sys/socket.h>

#include <stdlib.h>
#include <stdio.h>

int main()
{
    pid_t  pid;

    pid = fork();


    if (0 == pid)
    {
        pid = getpid();
        printf("[%d]-child process start, %s\n", pid,getenv("HOME"));
        
    }
    else if (0 < pid)
    {
        pid = getpid();
        printf("[%d]-parent process continue, %s\n", pid, getenv("HOME"));

    }
    else
    {
        printf("%s", "fork failed");
    }

    return 0;
}

而在nginx代码中,首先猜测肯定是某个地方修改了环境变量,不然不可能为空

static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
   ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "11111111111111111111111111[%d]-child process start, %s\n", getpid(),getenv("PATH"));

    ngx_worker_process_init(cycle, worker);

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "22222222222222222222222222[%d]-child process start \n", getpid(),getenv("PATH"));

}

日中打印结果
2019/10/12 15:53:43 [notice] 24279#0: 11111111111111111111111111[24279]-child process start, /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/bin/vim:/root/bin:/usr/local/bi    n/vim:/usr/lib/golang/bin:/data/go/bin:/usr/local/bin/vim:/usr/lib/go/bin:/data/go/bin:/usr/local/bin/vim:/usr/local/go/bin:/data/go/bin

2019/10/12 15:53:43 [notice] 24279#0: 22222222222222222222222222[24279]-child process start

nginx中获取不到PATH变量。 这个和fork、不fork没有任何关系。
我一直想说只是在nginx下,getenv获取不到环境变量。因为在ngx_set_environment重新设置环境变量了

如何产生core文件?

echo "/data/corefiles/core-%e-%p" >/proc/sys/kernel/core_pattern
ulimit -c unlimited

echo "/data/corefiles/core-%e-%p-%t" > /proc/sys/kernel/core_pattern
将会控制所产生的core文件会存放到/corefile目录下,产生的文件名为core-命令名-pid-时间戳
以下是参数列表:
%p - insert pid into filename 添加pid
%u - insert current uid into filename 添加当前uid
%g - insert current gid into filename 添加当前gid
%s - insert signal that caused the coredump into the filename 添加导致产生core的信号
%t - insert UNIX time that the coredump occurred into filename 添加core文件生成时的unix时间
%h - insert hostname where the coredump happened into filename 添加主机名
%e - insert coredumping executable name into filename 添加命令名

nginx的md5使用为什么不封装一下?

看nginx的代码,获取一个字符串的md5,需要写那么多函数? 就不能单纯封装一下?像我这样子?

// 获取md5, 封装nginx的三部操作
void ngx_get_md5(const char *data,  size_t data_len, unsigned char *md5)
{
    ngx_md5_t ctx;

    ngx_md5_init(&ctx);
    ngx_md5_update(&ctx, (const void *)data, data_len);
    ngx_md5_final(md5, &ctx);
}

md5转化成16进制也很简单,下面就列一种

   char hex[32 + 1] = {0};
   for(i = 0; i < 16; i++)
   {
      snprintf(hex + i * 2, 32, "%02x", md5[i]);
   }


详情请看,我的nginx测试代码

nginx的超时管理?

1. nginx要什么要超时管理?

回收资源、返回错误
比如nginx对客户端的连接aceppt后、收到报文头后等,这些一定要设置超时时间,否则这些连接会消耗系统资源,导致不能正常服务。如果是恶意连接,后果不堪设想

2. nginx超时管理如何实现?

nginx超时事件对象的超时检测有两种方案

1.定时检测机制,通过设置定时器(通过系统调用),每过一定时间就对红黑树管理的所有超时事件进行一次超级扫描并处理超时事件。
2. 先计算出距离当前最快发生超时的时间是多久。然后等待这个时间之后再去进行一次超时检测。(管理一颗红黑树、超时时间就按照红黑树中最小的值(马上就要发生的事件)设置)

nginx的post的机制?

post事件处理机制就是允许事件延后执行。
nginx设计了两个队列,一个是由被触发的监听连接的读事件构成的ngx_posted_accept_events队列,另一个是由普通读/写事件构成的ngx_posted_events队列

nginx的连接池?

nginx的连接池只是听起来高端一点,其实就是开辟一个大的ngx_connection_t 数组(大小按照用户配置的worker_connections)

struct ngx_cycle_s {
   ...
   ngx_connection_t         *connections;
   ngx_event_t              *read_events;
   ngx_event_t              *write_events;
   ...
}

nginx 每一个连接与读写事件是绑定在一起,读事件、写事件 、连接池是由3个大小相同的数组组成,所以根据数组序号就可将每一个连接、读事件、写事件对应起来,这个关系在ngx_event_process_init模块初始化过程就已经决定了。

//初始化事件循环
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle)
{  
 ...
    //预分配连接池
    cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
    //预分配读事件
    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
...
}

关于连接池只有两个函数

ngx_connection_t *ngx_get_connection(ngx_socket_t s,ngx_log_t *log)
void ngx_free_connection (ngx_connection_t *c)

nginx不能基于ip + host的访问吗?

一、请求nginx的时候ip写在前面,nginx会把ip作为host来处理

curl "http://100.115.138.131:8000/myhello.com/hello" -v

预期:
host : myhello.com
访问ip : 100.115.138.131:8000

结果:

> GET /myhello.com/hello HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 100.115.138.131:8000
> Accept: */*
> 
< HTTP/1.1 404 Not Found
< Server: nginx/1.16.0
< Date: Thu, 04 Jul 2019 06:27:33 GMT
< Content-Type: text/html
< Content-Length: 153
< Connection: keep-alive

二、写成代理ip就没有什么问题? 那这样岂不是浏览器不能指定ip访问host了?

只能通过类似 curl "http://myhello.com/hello" -v -x"100.115.138.131:8000"

> GET http://myhello.com/hello HTTP/1.1
> User-Agent: curl/7.29.0
> Host: myhello.com
> Accept: */*
> Proxy-Connection: Keep-Alive
> 
< HTTP/1.1 200 OK
< Server: nginx/1.16.0
< Date: Thu, 04 Jul 2019 06:31:12 GMT
< Content-Type: text/plain
< Content-Length: 38
< Connection: keep-alive
< name: vaynedu
< module: ngx_http_myhello_module
< mytime: 2019-07
< timeup: 2019-07
< mytime2: 2019070

worker进程哪些场景会挂掉?master进程怎么判断worker进程挂掉的? 如果master进程挂掉了怎么办? master拉起worker进程的可靠性怎么保证?

1. master进程挂掉了怎么办

master进程挂掉一般通过watchdog + crontab

2. master进程怎么判断worker进程挂掉的?

至于master进程判断work进程挂掉,从nginx的代码中,master进程通过信号 + 定时检测来保证
如果外界直接kill掉worker子进程,master进程会接收一个来自内核的信号,会迅速拉起子进程

void ngx_master_process_cycle(ngx_cycle_t *cycle)
{
  ...
  for ( ;; ) {
        if (delay) {
            if (ngx_sigalrm) {
                sigio = 0;
                delay *= 2;
                ngx_sigalrm = 0;
            }

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "termination cycle: %M", delay);
        
            // 定时检测部分
            itv.it_interval.tv_sec = 0;
            itv.it_interval.tv_usec = 0;
            itv.it_value.tv_sec = delay / 1000;
            itv.it_value.tv_usec = (delay % 1000 ) * 1000;

            if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                              "setitimer() failed");
            }
        }

        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");

        //master等待信号产生
        sigsuspend(&set);

        ngx_time_update();

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "wake up, sigio %i", sigio);

         // master一直检测worker进程的状态,有问题直接拉起
        if (ngx_reap) {
            ngx_reap = 0;
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");

            live = ngx_reap_children(cycle);
        }

        if (!live && (ngx_terminate || ngx_quit)) {
            ngx_master_process_exit(cycle);
        }
        ...
}

3. worker进程哪些场景会挂掉?

worker进程10s~20s没有响应 。 那什么时候worker进程会没有响应,我目前遇到在多线程服务器里面就是死锁、高负载会触发watchdog重启服务器进程

4.worker进程是如何接受信号的?

nginx启动之后,只有master进程才接受外部信号(重启、优雅退出、强制退出),至于worker进程启动时会清空信号,后面通信则使用socketpair建立的channel来进行通信(修改全局标志位控制worker进程的行为)。

nginx的四级指针 ****conf_ctx 和 *(void **)conf = ctx ?

1. void * 和 void *** ,即是再来个void **** ,到底有什么区别吗?

其实没有任何区别,也就是保存一个地址的, 占用4 or 8个字节。 既然没有区别为什么要这么写?主要还是方便理解和防止出错,void **** ,就可以知道有4个层级,写在代码的时候也能按照编译器的要求。不会出现告警

2. 如何理解 *(void **)conf = ctx ?

void *conf;  // 一级指针
void ***ctx; // 三级指针

void **是指针的指针(二级指针)。   *(void **)conf的意思是 conf先转成指针的指针,然后在解引用. 这里*(void **)conf = ctx   和 conf = ctx 效果一样,都能编译通过。

3. 其他

  1. void * 不能直接解引用,也不能直接赋值(必须转化成确定的类型)
  2. void * 可以接受任何类型的指针比如void *****,int *******************
  3. *(void **)表示void指针的指针的解引用,。就算是void *, 指针的指针解引就是合法好理解

4.nginx四级指针优秀文章推荐

https://blog.csdn.net/sniffer12345/article/details/4782127
https://zhuanlan.zhihu.com/p/81731535
https://blog.csdn.net/u012566181/article/details/41694637

5.测试代码

#include <stdio.h>
#include <stdlib.h>

int main(int argc, const char *argv[]) 
{
	  char *home = getenv("PATH");

	  printf("path is %s.\n", home);

      // 容易看到它的指向和层次
	  // 编译器在检查类型的时候,还会判断**的个数
	  void ****conf;
	  void ***ctx;

//	  conf = ctx;
//	  *conf = ctx;
//	  *(void **)conf = ctx;


     return 0;
}

采坑-如何处理linux库同名函数?

我的博客地址
记录一次采坑--如何处理Linux动态库同名函数?

1. 最简单的方案 — 修改函数名称

因为业务本身提供的静态库,用nm查看函数也不多,为了安全起见,修改每个函数的名称(增加一些业务私有的东西)。 批量修改函数名可直接使用sed

2. -fvisibility=hidden — 最安全最推荐的做法

现在C/C++这一块有几十年的历史,该踩的坑大家都应该踩过去了。gcc编译器就直接提供-fvisibility=hidden编译选项,直接让库函数global可见变成local可见, 使用__attribute__((visibility("default")))直接将需要的函数暴漏在外面就行。

3. version-script,仅仅导出要使用的符号表

nginx为什么不一次accept尽可能多的建立新连接?

ngx_event_accept用来接收来自客户端的连接请求。tcp建立后,从连接池中获取一个新连接对象(同时获取到读、写事件),并把读事件加入到epoll中。

void ngx_event_accept(ngx_event_t *ev)
{
   ...
   do{
      ....
      s = accept(lc->fd, &sa.sockaddr, &socklen);
   }while (ev->available);

}

用户配置有multi_accept on; 那么工作进程每次捕获到监听套接口上的可读事件后,反复调用accept()函数,即一次接受当前所有到达客户端的连接请求。但是这并不是默认配置。

因为如果一次尽可能多建立连接可能会导致工作进程的负载没有得到较好的均衡。比如A工作进程一次建立了10个连接, 但是B一次性建立100个连接,这就导致极大的不均衡

其实这里我有想法,退出while循环的条件可以,如果每次超过50或者100或者其他,不就行了?这样效率是不是会高一点?

ngx_log_debug设计不合理?

自己编写的代码引用nginx源文件的时候core掉,突然发现是ngx_log_debug3问题,core栈如下

(gdb) bt
  #0  ngx_memalign (alignment=alignment@entry=16, size=size@entry=1024, log=log@entry=0x0) at src/os/unix/ngx_alloc.c:84
  #1  0x0000000000400a61 in ngx_create_pool (size=1024, log=0x0) at src/core/ngx_palloc.c:23
  #2  0x0000000000403405 in main () at myngx_pool.c:58
  (gdb) f 0
  #0  ngx_memalign (alignment=alignment@entry=16, size=size@entry=1024, log=log@entry=0x0) at src/os/unix/ngx_alloc.c:84
        84          ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,
  (gdb)

分析 & 问题原因 log为空的时候就直接core掉

#define ngx_log_debug(level, log, args...)                                    \
    if ((log)->log_level & level)                                             \
        ngx_log_error_core(NGX_LOG_DEBUG, log, args)

需要改进的地方, 至少应该判断log是否为NULL再去log->xx 成员

nginx如何判断事件已过期(客户端已断开连接)?

参考连接

1.初始化时,所有连接的地址最后一位默认为0,读写事件的instance=1
2.从空闲连接列表中获取新的可用连接时,将连接地址最后一位置反。
3.注册事件时,将连接地址最后一位设为事件的instance标志位。
4.处理事件时,取连接地址最后一位,与事件的instance标志位对比,不一致说明获取过新的连接,说明旧连接已经不存在,则为过期事件。(旧连接存在的情况下,不会放回空闲连接列表)

CDN上一个文件的缓存时间是怎么样的?

min_cache_inter a
max_cache_inter b
1、如果Cache-Control 中的max-age存在,则
(1)a<=max-age<=b;则缓存时间取max-age
(2)max-age <a,则取a
(3)max-age>b,则取b
2、 若无max-age , 则设置max-age = 0 , 然后匹配1

对于设计而言,客户的header仅仅是客户的header,CDN应该对业务行为有整体的把控全。
比如一个场景: 业务header中带有Cache-Control : no-cache.标准的HTTP行为,对于此类请求不缓存。但是万一客户误带,错带,难道CDN只能傻傻不缓存文件,傻傻的看着回源率非常高。
这是不可能的,如果做到一切可控? 就是互不信任原则。 内部保护机制,强制缓存。这个**非常的好,不管别人怎么玩,我总可以控制自己的行为。可以降低风险,保护自己。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.