公司网站制作商,软件工程师培训机构排名,中国建筑室内设计网,html5网站报价明细文章目录 1. 为什么需要ngx_http_slice_module2. 配置指令3. 加载模块4. 源码分析4.1 指令分析4.2 模块初始化4.3 slice模块的上下文4.2 $slice_range字段值获取4.3 http header过滤处理4.4 http body过滤处理5 测试和验证 1. 为什么需要ngx_http_slice_module
顾名思义#… 文章目录 1. 为什么需要ngx_http_slice_module2. 配置指令3. 加载模块4. 源码分析4.1 指令分析4.2 模块初始化4.3 slice模块的上下文4.2 $slice_range字段值获取4.3 http header过滤处理4.4 http body过滤处理5 测试和验证 1. 为什么需要ngx_http_slice_module
顾名思义nginx的slice模块的功能是在proxy代理的时候会将代理到上游服务器的请求转换为若干个分片的子请求最后将响应内容逐个返回给用户。
那么为什么要搞那么麻烦将一个大文件切片成小的碎片文件来处理呢原因有以下三点
1. 大文件在整个下载的过程中持续的时间比较长nginx和上游服务器被代理的后端服务器长时间建立连接可能因为各种原因引起连接中断的概率大幅度上升。
2. 更重要的原因是大文件不利于cdn缓存。譬如著名的开源缓存服务器ats和squid一般都需要等文件下载完全后才能将内容缓存到cache里面如果用户下载了一半不下载了或者因为和上游服务器连接故障都会导致文件不能完整地被cache服务器下载下来而导致该文件不能被缓存引起反复下载降低内容的命中率。
3. 大文件在cdn架构中不容易平衡cache节点的负载导致cache节点负载不平衡而影响用户体验。而nginx slice模块的出现将大文件化整为零很好地解决了以上这些问题。下面列出了一个CDN cache系统的典型架构具体不做详述后面可以另行撰文说明。2. 配置指令
slice size; 其中size是切片的大小单位可以是K(千字节M兆字节G吉字节单位大小写均可。 slice_size指令可以配置在http, “server”, “location” 块中定义。 但是真正要启用slice功能还要设置两条指令 proxy_cache_key $uri$is_args$args$slice_range;proxy_set_header Range $slice_range;第一条指令表示如果使用nginx的自带缓存功能那么nginx会以切片为单位进行缓存那么缓存的时候需要对同一个文件的不同分片进行区分所以需要将cache_key和每个切片的标识进行关联这里使用了$slice_range变量。
第二条指令表示如果向上游服务器进行请求的时候需要增加的HTTP Range头该头的内容就是$slice_range变量的值。
附带说明一下
$slice_range变量本身是由ngx_http_slice_module来定义并赋值的, 值的内容如bytes0-1048575。3. 加载模块
在configure的时候需要添加ngx_http_slice_module来将其编译进来命令如下
./configure --with-http_slice_module
然后在nginx.conf 中添加以下配置如location / {slice 1m;proxy_cache cache;proxy_cache_key $uri$is_args$args$slice_range;proxy_cache_valid 200 206 1h;proxy_set_header Range $slice_range;proxy_pass http://localhost:8000;
}当然如果不使用cache功能只是单纯使用slice功能那么proxy_cache、proxy_cache_key和proxy_cache_valid这些指令都不需要写了。4. 源码分析
4.1 指令分析
static ngx_command_t ngx_http_slice_filter_commands[] {{ ngx_string(slice),NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,ngx_conf_set_size_slot,NGX_HTTP_LOC_CONF_OFFSET,offsetof(ngx_http_slice_loc_conf_t, size),NULL },ngx_null_command
};
从以上代码知道slice指令可以在http server location块里面进行配置一旦nginx发现slice指令就调用ngx_conf_set_size_slot函数进行配置解析。ngx_conf_set_size_slot本身还是非常好理解的, 它是nginx在配置解析阶段用来解析大小的通用函数解析的结果存放在ngx_http_slice_loc_conf_t的size字段中如果size字段为0,标识不开启切片功能。当然需要说明一下ngx_http_slice_loc_conf_t的实例会由ngx_http_slice_create_loc_conf来创建并由ngx_http_slice_merge_loc_conf来合并这方面的代码逻辑不再赘述。4.2 模块初始化
static ngx_http_module_t ngx_http_slice_filter_module_ctx {ngx_http_slice_add_variables, /* preconfiguration */ngx_http_slice_init, /* postconfiguration */NULL, /* create main configuration */NULL, /* init main configuration */NULL, /* create server configuration */NULL, /* merge server configuration */ngx_http_slice_create_loc_conf, /* create location configuration */ngx_http_slice_merge_loc_conf /* merge location configuration */
};从以上代码知道slice模块是作为一个nginx的filter模块参与切片工作的同时ngx_http_slice_init是模块的初始化函数而ngx_http_slice_add_variables是在preconfiguration阶段也就是在初始化函数之前执行的函数用来向nginx http框架添加$slice_range变量供处理http请求的时候使用。先看一下ngx_http_slice_add_variables函数static ngx_int_t
ngx_http_slice_add_variables(ngx_conf_t *cf)
{ngx_http_variable_t *var;var ngx_http_add_variable(cf, ngx_http_slice_range_name, 0);if (var NULL) {return NGX_ERROR;}var-get_handler ngx_http_slice_range_variable;return NGX_OK;
}以上代码非常简单就是调用ngx_http_add_variable添加$slice_range变量,变量名存放在全局静态变量ngx_http_slice_range_name中如下static ngx_str_t ngx_http_slice_range_name ngx_string(slice_range);添加变量的时候还设置了获取该变量的回调函数为ngx_http_slice_range_variable$slice_range变量只有get没有set,所以是一个只读类型的变量其内容只能由slice模块内部进行更新,其他模块是不能对其进行更新操作的。再看slice模块的初始化函数static ngx_int_t
ngx_http_slice_init(ngx_conf_t *cf)
{ngx_http_next_header_filter ngx_http_top_header_filter;ngx_http_top_header_filter ngx_http_slice_header_filter;ngx_http_next_body_filter ngx_http_top_body_filter;ngx_http_top_body_filter ngx_http_slice_body_filter;return NGX_OK;
}显然这就是典型的filter函数的初始化过程就是将ngx_http_slice_header_filter和ngx_http_slice_body_filter分别以挂钩函数的方式挂入filter模块的两条调用链中即header过滤器调用链和body过滤器调用链。4.3 slice模块的上下文
在正式介绍请求处理逻辑之前需要先了解一下ngx_http_slice_module模块的请求上下文具体如下typedef struct {off_t start; /* 当前切片的起始偏移量 */off_t end; /* 请求内容的结束偏移量不是指一个切片的结束偏移量而是当前请求客户端需要的内容的结束偏移量 */ngx_str_t range; /* 存储$slice_range变量的字符串值 */ngx_str_t etag; /* 上游服务器响应的内容etag值用来比对多个切片请求是否属于同一个切片 */unsigned last:1; /* 第一个切片请求是否已经完成了最后一个buf的处理 */unsigned active:1; /* 当前的切片请求响应处理过程执行中 */ngx_http_request_t *sr; /* 当前活跃中的子请求 */
} ngx_http_slice_ctx_t;4.2 $slice_range字段值获取
为什么从$slice_range字段值的获取开始说的因为这个字段值的获取在nginx向上游服务器发起请求前组织HTTP请求头的时候就会被调用了执行顺序上面来说是放在执行http header和http body过滤函数的前面的。而且获取这个字段值的时候会创建slice模块的请求上下文ngx_http_slice_ctx_t, 另外需要明确的一点是每发起一个向上游服务器的新的切片的请求前都会重新获取这个字段值来组织新的请求头所以在处理的过程中这个变量的值是随着完成的切片的情况而需要不断更新的。
以下是字段值获取的回调函数的实现static ngx_int_t
ngx_http_slice_range_variable(ngx_http_request_t *r,ngx_http_variable_value_t *v, uintptr_t data)
{u_char *p;ngx_http_slice_ctx_t *ctx;ngx_http_slice_loc_conf_t *slcf;/* 获取当前请求本filter模块的上下文信息*/ctx ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);/* 如果为空表示上下文还没有创建则需要创建一个新的上下文,这当然是在主请求上才会有这个情况子请求不会出现这个情况除非当前filter是disable了如果disable状态当然返回当前$slice_range变量没有找到了 */if (ctx NULL) { if (r ! r-main || r-headers_out.status) {v-not_found 1;return NGX_OK;}/* 获取本filter模块的配置信息 */slcf ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);/* 如果配置的切片size为0表示切片功能禁用了所以返回$slice_range变量找不到的错误信息 */if (slcf-size 0) {v-not_found 1;return NGX_OK;}/* 创建一个新的上下文并保存到当前request中 */ctx ngx_pcalloc(r-pool, sizeof(ngx_http_slice_ctx_t));if (ctx NULL) {return NGX_ERROR;}ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);/* 分配一块内存用于保存$slice_range的值*/p ngx_pnalloc(r-pool, sizeof(bytes-) - 1 2 * NGX_OFF_T_LEN);if (p NULL) {return NGX_ERROR;}/* 设置本次需要向上游服务器发起的起始位置详见下文*/ctx-start slcf-size * (ngx_http_slice_get_start(r) / slcf-size);ctx-range.data p;ctx-range.len ngx_sprintf(p, bytes%O-%O, ctx-start,ctx-start (off_t) slcf-size - 1)- p;}/* 设置将返回的变量的信息 */v-data ctx-range.data;v-valid 1; /* 标识变量可用标记 */v-not_found 0; /* 标识变量找到标记 */v-no_cacheable 1; /* 标识变量不可缓存标记 */v-len ctx-range.len;return NGX_OK;
}这里需要再稍微解释一下下面这个语句ctx-start slcf-size * (ngx_http_slice_get_start(r) / slcf-size); ngx_http_slice_get_start的函数调用是会去判断客户端的请求是否是range请求如果不是range请求那么很简单就是从头获取文件的完整内容所以必然是从0字节开始请求否则需要解析当前客户端请求的HTTP Range头的信息从而得到客户端希望的文件起始偏移量。得到的客户端实际希望的文件起始偏移量以后需要按照切片大小进行对齐后设置到ctx-start变量中最后写入向上游服务器请求头中的HTTP Range字段。
那么为什么需要按照slice切片大小进行对齐向上游服务器请求呢因为这样不会由于不同客户端请求的起始位置不同导致产生大量的不同切片引起缓存miss。对齐操作完全就是为了提升缓存的命中率。虽然在本次请求的时候向后端服务器多请求了一些内容但是比起缓存hit带来的好处还是非常非常值得的。下面是ngx_http_slice_get_start的实现代码static off_t
ngx_http_slice_get_start(ngx_http_request_t *r)
{off_t start, cutoff, cutlim;u_char *p;ngx_table_elt_t *h;/* 不是range请求直接返回0表示向上游服务器从头开始请求*/if (r-headers_in.if_range) {return 0;}/* 解析HTTP Range请求头获取起始偏移量 */h r-headers_in.range;if (h NULL|| h-value.len 7|| ngx_strncasecmp(h-value.data, (u_char *) bytes, 6) ! 0){return 0;}p h-value.data 6;if (ngx_strchr(p, ,)) {return 0;}while (*p ) { p; }if (*p -) {return 0;}cutoff NGX_MAX_OFF_T_VALUE / 10;cutlim NGX_MAX_OFF_T_VALUE % 10;start 0;while (*p 0 *p 9) {if (start cutoff (start cutoff || *p - 0 cutlim)) {return 0;}start start * 10 (*p - 0);}return start;
}4.3 http header过滤处理
static ngx_int_t
ngx_http_slice_header_filter(ngx_http_request_t *r)
{off_t end;ngx_int_t rc;ngx_table_elt_t *h;ngx_http_slice_ctx_t *ctx;ngx_http_slice_loc_conf_t *slcf;ngx_http_slice_content_range_t cr;/* 获取当前请求的slice模块的上下文 ctx上下文是在4.2节中描述的ngx_http_slice_range_variable中创建的没有创建就会返回NULL说明本次请求没有启用slice过滤模块那么直接调用ngx_http_next_header_filter执行filter链中的后续模块的处理函数。*/ctx ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);if (ctx NULL) {return ngx_http_next_header_filter(r);}/* 调用本处理函数的时候上游服务器发出的响应响应头已经被本nginx获取到了。如果响应的内容不是206,并且当前是第一个切片的请求第一个切片请求只能是主请求发起不是子请求)说明上游服务器不支持Range请求则禁用切片功能。如果是子请求而上游服务器已经响应了非206,那么第一个切片和后续的切片响应前后不一致只能报错了。*/if (r-headers_out.status ! NGX_HTTP_PARTIAL_CONTENT) {if (r r-main) {ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);return ngx_http_next_header_filter(r);}ngx_log_error(NGX_LOG_ERR, r-connection-log, 0,unexpected status code %ui in slice response,r-headers_out.status);return NGX_ERROR;}/* 检查主请求和自请求中响应内容的etag是否一致如果不一致则认为不是一个内容也只能报错了。*/h r-headers_out.etag;if (ctx-etag.len) {if (h NULL|| h-value.len ! ctx-etag.len|| ngx_strncmp(h-value.data, ctx-etag.data, ctx-etag.len)! 0){ngx_log_error(NGX_LOG_ERR, r-connection-log, 0,etag mismatch in slice response);return NGX_ERROR;}}/* 在上下文中存储当前的etag信息用于下一个子请求header处理的时候进行比对 */if (h) {ctx-etag h-value; }/* 分析上游服务器的响应头中的Content-Range头中的信息 */if (ngx_http_slice_parse_content_range(r, cr) ! NGX_OK) {ngx_log_error(NGX_LOG_ERR, r-connection-log, 0,invalid range in slice response);return NGX_ERROR;}/* 如果Content-Range头中没有整个内容的长度信息那么不能进行切片处理只能报错 */if (cr.complete_length -1) {ngx_log_error(NGX_LOG_ERR, r-connection-log, 0,no complete length in slice response);return NGX_ERROR;}ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r-connection-log, 0,http slice response range: %O-%O/%O,cr.start, cr.end, cr.complete_length);/* 获取slice模块的配置信息 */slcf ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);/* 计算当前切片的结束偏移位置也就是下一个切片的起始位置 */end ngx_min(cr.start (off_t) slcf-size, cr.complete_length);/* 判断希望请求的切片起止位置和实际上游服务器响应的起止位置是否一致 */if (cr.start ! ctx-start || cr.end ! end) {ngx_log_error(NGX_LOG_ERR, r-connection-log, 0,unexpected range in slice response: %O-%O,cr.start, cr.end);return NGX_ERROR;}ctx-start end; /* 设置下一个切片的开始位置 */ctx-active 1; /* 设置当前的切片请求的响应进入活跃状态中 *//* 设置客户端响应的响应头信息包括响应状态需要从206改成200, 内容大小改成完整的大小而不是本次切片请求上游服务器返回的切片大小 */r-headers_out.status NGX_HTTP_OK;r-headers_out.status_line.len 0;r-headers_out.content_length_n cr.complete_length;r-headers_out.content_offset cr.start;r-headers_out.content_range-hash 0;r-headers_out.content_range NULL;/* 向客户端响应的时候需要清理掉Accept-Ranges头*/if (r-headers_out.accept_ranges) {r-headers_out.accept_ranges-hash 0;r-headers_out.accept_ranges NULL;}r-allow_ranges 1; /*设置允许ngx_http_range_filter_module执行Range处理*/r-subrequest_ranges 1;/*本参数和allow_ranges的值一致的可以忽略*/r-single_range 1; /*设置ngx_http_range_filter_module仅支持单个Range模式不支持多Range模式 *//* 继续调用header filter链的下一个模块的处理函数后续模块可能包括ngx_http_range_filter_module */rc ngx_http_next_header_filter(r);if (r ! r-main) { /* 如果不相等表示是子请求 */return rc; /* 如果是子请求就直接返回不执行后面的代码了 */*}/* 以下代码近在主请求中只会被执行1次子请求中则不会进入到以下代码 *//* preserve_body字段的作用就是控制在转发请求时是否保留请求体。当preserve_body字段设置为1时Nginx将会保留请求体数据并将其传递给上游服务器。当preserve_body字段设置为0时默认值Nginx会在转发请求时丢弃请求体数据只传递请求头部和其他元数据。*/r-preserve_body 1;/* 如果经过header filter的调用链处理后ngx_http_range_filter_module处理了客户端发送来的Range请求这个时候真正发送给客户端的状态是206响应,而不是前面设置的200。因为客户端的请求是Range请求而当前处理的分片的起始范围在客户端请求要求的内容的起始偏移量前面那么需要重新根据content_offset指定的偏移量调整向后端服务器请求的分片起始位置而结束位置为客户端请求的结束位置偏移量。*/if (r-headers_out.status NGX_HTTP_PARTIAL_CONTENT) {if (ctx-start (off_t) slcf-size r-headers_out.content_offset) {ctx-start slcf-size* (r-headers_out.content_offset / slcf-size);}ctx-end r-headers_out.content_offset r-headers_out.content_length_n;} else {ctx-end cr.complete_length;}return rc;
}4.4 http body过滤处理
static ngx_int_t
ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{ngx_int_t rc;ngx_chain_t *cl;ngx_http_slice_ctx_t *ctx;ngx_http_slice_loc_conf_t *slcf;/* 获取当前请求的slice模块的上下文如果为空说明本次请求没有启用slice过滤模块那么直接调用ngx_http_next_header_filter执行filter链中的后续模块的处理函数。*/ctx ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);if (ctx NULL || r ! r-main) { /* 如果是子请求直接进入后续的调用链 */return ngx_http_next_body_filter(r, in);}/* 以下都是在主请求中处理如果last_buf此字段为1表明这是最后一个buf但是对于整个请求来说只是一个切片的最后一块不是整个请求的最后一个buf块所以需要重新调整为0,并设置last_in_chain1用于表明是本chain的最后一个buf块 */for (cl in; cl; cl cl-next) {if (cl-buf-last_buf) { /* 当前子请求的最后一个buf并不是响应给客户端的最后一个buf所以需要重新调整这个标记 */cl-buf-last_buf 0; /* 用于标识是否是最后一个缓冲区 */cl-buf-last_in_chain 1; /* 表示是否是链表中的最后一个缓冲区 */cl-buf-sync 1; /* 表示是否需要执行同步操作 */ctx-last 1; /* 第一个切片的最后一个buf以及获取到 */}}/* 调用body filter链后续filter模块的处理函数第一个切片请求是在主请求中发生的这时in里面带有待发送到客户端的数据之后的切片请求是在子请求中发生的这个时候子请求的调用链已经将数据发送到客户端了到子请求把当前的切片发送完毕后会通过发送一个inNULL空的包重新激活主请求这时主请求可以知道子请求已经完成了从而可以根据需要开启一个新的子请求或者结束请求的处理。*/rc ngx_http_next_body_filter(r, in);if (rc NGX_ERROR || !ctx-last) {return rc;}/* 当前的子请求还没有处理完毕返回nginx http框架继续处理 */if (ctx-sr !ctx-sr-done) {return rc;}/* ctx-active1是在处理子请求头部信息即ngx_http_slice_header_filter函数中设置的如果0, 则表示当前切片请求的响应还没有活跃状态但是却需要发送body应该是出了什么问题 */if (!ctx-active) {ngx_log_error(NGX_LOG_ERR, r-connection-log, 0,missing slice response);return NGX_ERROR;}/* 所有内容已经全部响应给客户端结束处理 */if (ctx-start ctx-end) {/* 因为内容已经发送完毕上下文信息可以清理掉了ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);/* 这里会通知nginx框架发送一个last_buf设置为1的ngx_buf_t缓冲区表示内容发送完毕 */ngx_http_send_special(r, NGX_HTTP_LAST); return rc;}/* buffered 字段是一个标志位用于指示请求是否有未处理的请求体数据。当客户端发送一个带有请求体的 HTTP 请求时请求体数据可能会被分成多个数据块chunks进行传输。buffered 字段用于跟踪这些请求体数据的处理状态。如果这个标记为1, 就暂时不能启动一个新的子请求 */if (r-buffered) {return rc;}/* 当前切片已全部响应给客户端还有新的切片需要处理开启一个新的子请求来获取新的切片 */if (ngx_http_subrequest(r, r-uri, r-args, ctx-sr, NULL,NGX_HTTP_SUBREQUEST_CLONE)! NGX_OK){return NGX_ERROR;}ngx_http_set_ctx(ctx-sr, ctx, ngx_http_slice_filter_module);slcf ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);/* 设置下一个切片的$slice_range的字符串值*/ctx-range.len ngx_sprintf(ctx-range.data, bytes%O-%O, ctx-start,ctx-start (off_t) slcf-size - 1)- ctx-range.data;/* 设置当前切片请求响应已经结束 */ctx-active 0;ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r-connection-log, 0,http slice subrequest: \%V\, ctx-range);return rc;
}5 测试和验证
为了测试slice模块的效果我们需要两个nginx服务第一个nginx服务作为前端代理服务器第二个nginx服务作为后端源服务器为了简单起见将这两个nginx服务都搭建在一台物理服务器上面。为了能够一目了然看清楚前端代理服务器确实向后端发送了切片请求需要在后端nginx服务器的access日志上添加$http_range变量的输出。下面先列出后端nginx的配置文件nginx.confuser nobody;
worker_processes 1; error_log logs/error.log;
pid logs/nginx.pid;events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for Range: $http_range;access_log logs/access.log main;gzip off;server {listen 8888;location / { root html;} }
}
这里需要特别注意的就是log_format 这个指令中添加了Range: $http_range然后设置前端代理nginx的e配置文件nginx.confuser nobody;
worker_processes 1; error_log logs/error.log;
pid logs/nginx.pid;events {worker_connections 1024;
}http {include mime.types;default_type application/octet-stream;log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for;access_log logs/access.log main;server {listen 9080;location / {slice 1m;proxy_set_header Range $slice_range;proxy_buffering off;proxy_pass http://127.0.0.1:8888;}}
}
主要需要注意的是location / { } 中的设置。
然后启动两个nginx服务通过curl来验证测试用例1, 完整文件请求如
curl http://127.0.0.1:9080/a.pdf /dev/null查看第二个nginx的access.log日志如下:127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes0-1048575
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes1048576-2097151
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes2097152-3145727
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes3145728-4194303
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes4194304-5242879
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes5242880-6291455
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes6291456-7340031
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes7340032-8388607
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes8388608-9437183
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes9437184-10485759
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes10485760-11534335
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes11534336-12582911
127.0.0.1 - - [04/Feb/2024:16:20:17 0800] GET /a.pdf HTTP/1.0 206 1015274 - curl/7.81.0 - Range: bytes12582912-13631487
一个客户端的http请求在第二个后端源nginx上收到了若干个响应为206的HTTP请求表明前端nginx的切片功能已经正常开启了。测试用例2, Range请求如
curl http://127.0.0.1:9080/a.pdf -HRange: bytes1048577-5788888 /dev/null查看第二个nginx的access.log日志如下:127.0.0.1 - - [04/Feb/2024:16:30:29 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes1048576-2097151
127.0.0.1 - - [04/Feb/2024:16:30:29 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes2097152-3145727
127.0.0.1 - - [04/Feb/2024:16:30:29 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes3145728-4194303
127.0.0.1 - - [04/Feb/2024:16:30:29 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes4194304-5242879
127.0.0.1 - - [04/Feb/2024:16:30:29 0800] GET /a.pdf HTTP/1.0 206 1048576 - curl/7.81.0 - Range: bytes5242880-6291455这次可以看到第二个nginx收到的第一个请求的Range范围是1048576-2097151正好对应第二个切片的范围虽然我们请求要求的起始位置是1048577同时最后一个切片请求的结束位置是6291455,而这个正好是第五个切片的最后一个字节的偏移量。这样子验证了nginx slice功能的切片功能是按照切片对齐的方式向上游服务器发送请求的。