stoneworld

stoneworld 查看完整档案

北京编辑大连海洋大学  |  计算机专业 编辑Perfect  |  Python开发工程师 编辑 github.com 编辑
编辑

流水当歌醉,落叶亦芳菲。

个人动态

stoneworld 赞了文章 · 2019-07-03

Nginx 日志自定义记录及启用日志缓冲区

原文链接: 何晓东 博客

如果想统计网站的访问来源信息,可以用 php 获取信息,记录到数据库的形式,也可以直接使用 nginx 提供的访问日志,来记录网站的访问详情,管理员可以通过分析 nginx 的访问日志,来分析用户的访问来源,访问行为详情,网站页面访问热度等。此外,nginx 自身也有错误日志,方便运维人员调试 nginx。对于记录日志的行为,如果每次进行磁盘操作,将会较多的耗费资源,基于这个情况可以开启 nginx 日志缓冲区,缓冲区满或者定时写入的时间到了,再一次写入日志。

访问日志

nginx 在处理请求后立即在访问日志中写入有关客户端请求的信息。默认情况下,访问日志位 logs/access.log 中,信息以预定义的组合格式写入日志。
想要精确记录访问信息,就需要自定义一个更加完整的访问日志格式,如下所示:

http {
    log_format geoproxy
    '[$time_local] $remote_addr '
    '$realip_remote_addr $remote_user '
    '$request_method $server_protocol '
    '$scheme $server_name $uri $status '
    '$request_time $body_bytes_sent '
    '$geoip_city_country_code3 $geoip_region '
    '"$geoip_city" $http_x_forwarded_for '
    '$upstream_status $upstream_response_time '
    '"$http_referer" "$http_user_agent"';
    ...
}

这个日志配置被命名为 geoproxy,它使用许多 nginx 变量来演示 nginx 日志记录功能。详细讲解配置选项中各个变量的具体含义:

当用户发起请求时,会记录服务器时间 $time_local, $remote_user 值为通过基本授权的用户名称;

用于 nginx 处理 geoip_proxyrealip_header 指令的打开连接的 IP 地址和客户端 IP 地址;

之后记录 HTTP 请求方法 $request_method、协议 $server_protocol 和 HTTP 方法 $scheme:httphttps

当然还有服务器名称 $server_name、请求的 URI 和响应状态码;

除基本信息外,还有一些统计的结果数据:包括请求处理的毫秒级时间 $request_time、服务器响应的数据块大小 $body_bytes_sent
此外,客户端所在国家 $geoip_city_country_code3、地区 $geoip_region 和城市信息 $geoip_city 也被记录在内;

变量 $http_x_forwarded_for 用于记录由其它代理服务器发起的请求的 X-Forwarded-For 头消息;

upstream 模块中一些数据也被记录到日志里:被代理服务器的响应状态码 $upstream_status 、建立链接和从上游服务器接收响应主体最后一个字节的时间 $upstream_response_time、 建立和上游服务器的链接时间 $upstream_connect_time、建立链接和从上游响应头的第一个字节的时间 $upstream_header_time

请求来源 $http_referer 和用户代理 $http_user_agent 也可以被记录在日志里;

nginx 的日志记录功能非常强大和灵活的,需要注意的是:用于定义日志格式的 log_format 指令仅适用于 http 块级指令内,所有时间值均以毫秒为单位,以毫秒分辨率进行测量。

这个格式的日志配置将产生如下类型的日志:

[25/Feb/2019:16:20:42 +0000] 10.0.1.16 192.168.0.122 Derek
GET HTTP/1.1 http www.example.com / 200 0.001 370 USA MI
"Ann Arbor" - 200 0.001 "-" "curl/7.47.0"

如果需要使用这个日志配置,需要结合使用 access_log 指令,access_log指令接收一个日志目录和使用的配置名作为参数:

server {
    access_log /var/log/nginx/access.log geoproxy;
    ...
}

access_log 能在多个上下文使用,每个上下文中可以定义各自的日志目录和日志记录格式。

结论: nginx 中的日志模块允许为不同的场景配置日志格式,以便查看不同的日志文件。

在实际运用中,为不同上下文配置不同的日志会非常有用,记录的日志内容可以简单的信息,也可以详细记录所有必要信息。不仅如此,日志内容除了支持文本
也能记录 json 格式和 xml 格式数据。实际上 nginx 日志有助于您了解服务器流量、客户端使用情况和客户端来源等信息。此外,访问日志还可以帮助您定位与上游服务器或特定 uri 相关的响应和问题;对于测试来讲,访问日志同样有用,它可以用于分析流量情况,模拟真实的用户交互场景。日志在故障排除、调试、应用分析及业务调整中作用是不可或缺的。

错误日志

为了精确定位 nginx 的错误日志,使用自带的 error_log 指令定义错误日志目录及记录错误日志的等级,配置如下:

error_log /var/log/nginx/error.log warn;

error_log 指令配置时需要一个必选的日志目录和一个可选的错误等级选项。
if 指令外,error_log 指令能在所有的上下文中使用。错误日志等级包括:
debug、info、notice、warn、error、crit、alert 和 emerg。给出的日志
等级顺序就是记录最小到最严谨的日志等级顺序。需要注意的是 debug 日志
需要在编译 nginx 服务器时,带上 --with-debug 标识才能使用。

当服务器配置出错时,首先需要查看错误日志以定位问题。错误日志
也是定位应用服务器(如 FastCGI 服务)的利器。通过错误日志,我们可以调试 worker 进程连接错误、内存分配、客户端 IP 和 应用服务器等问题。错误日志格式不支持自定义日志格式;但他同样记录当前时间、日志等级和具体信息等数据。

注意: 错误日志的默认设置适用于全局。要覆盖它,请将 error_log 指令放在 main(顶级)配置上下文中。error_log 在开源 NGINX 1.5.2 版中添加了在同一配置级别指定多个指令的功能。

通过 syslog 将日志发送到统一服务器

既然不再需要将日志写到磁盘的某个目录,而是发送到统一的日志服务器,则将原有的目录部分替换为服务器 ip 即可,配置如下:

error_log syslog:server=10.0.1.42 debug;
access_log syslog:server=10.0.1.42,tag=nginx,severity=info geoproxy;

#error_log server=unix:/var/log/nginx.sock debug;
#access_log syslog:server=[2001:db8::1]:1234,facility=local7,tag=nginx,severity=info;

error_logaccess_log 指令的 syslog 参数紧跟冒号 : 和一些参数选项。包括:必选的 server 标记表示需要连接的 IP、DNS 名称或 UNIX 套接字;
可以使用如上注释的高端玩。

可选参数有 facilityseveritytag:

server 参数接收带端口的 IP 地址或 DNS 名称;默认是 UDP 514 端口。

facility 参数设置syslog 的类型 facility,值是 syslog RFC 标准定义的 23 个值中一个,默认值为 local7。其他可能的值是:authauthprivdaemoncronftplprkernmailnewssysloguseruucplocal0 ... local7

tag 参数表示日志文件中显示时候的标题,默认值是 nginx

severity 设置消息严重程度,默认是 info 级别日志。

日志缓冲区

当系统处于负载状态时,启用日志缓冲区以降低 nginx worker 进程阻塞。大量的磁盘读写和 cpu 资源使用对于服务器资源也是一种巨大消耗。将日志数据缓冲到内存中可能是很小的一个优化手段,buffer 参数意义是缓冲区的大小,功能是当缓冲区已经写满时,日志会被写入文件中;flush 参数意义是缓冲区内日志在缓冲区内存中保存的最长时间,功能即当缓存中的日志超过最大缓存时间,也会被写入到文件中,不足的地方即写入到日志文件的日志有些许延迟,即时调试中应当关闭日志缓冲。。配置如下:

http {
    access_log /var/log/nginx/access.log main buffer=32k flush=1m;
}

参考链接:

  1. nginx cook book
  2. nginx 配置日志

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券

查看原文

赞 7 收藏 2 评论 0

stoneworld 赞了文章 · 2019-07-03

分布式系统全局发号器的几点思考

原文链接:何晓东 博客

文章起源于 康神交流群的 panda大佬和boss li关于发号器的一些交流,特此感谢让我们学到了新知识。

为什么需要发号器

在分布式系统中,经常需要对大量的数据、消息、http 请求等进行唯一标识,例如:对于分布式系统,服务间相互调用需要唯一标识,调用链路分析,日志追踪的时候需要使用这个唯一标识。此时需要一个全局唯一的 ID。

需要什么样子的发号器

持久化

要满足长期全局唯一,持久化是必须的,肯定不能让已经使用的再次产生一遍,同时需要强一致性。可用选择存储在 Redis 或者 Etcd 中。

高可用

这个时候需要提供发号器服务的机器主从同步,能够在主服务器宕机的时候,自动选择从服务器,切换过程中,发号器生成的 ID 可能不连续,服务正常就可以。

其他特性

主要是看具体业务了,需要认证和权限控制都是可选的,可用在请求层限制来源 IP,只允许固定的 IP 访问。

发号器的几种常用方案

UUID

UUID 是 Universally Unique Identifier 的缩写,它是在一定的范围内(从特定的名字空间到全球)唯一的机器生成的标识符,UUID 是16字节128位长的数字,通常以36字节的字符串表示,比如:3F2504E0-4F89-11D3-9A0C-0305E82C3301。

UUID经由一定的算法机器生成,为了保证 UUID 的唯一性,规范定义了包括网卡 MAC 地址、时间戳、名字空间(Namespace)、随机或伪随机数、时序等元素,以及从这些元素生成 UUID 的算法。UUID 的复杂特性在保证了其唯一性的同时,意味着只能由计算机生成。

优缺点: 本地生成,性能高,延迟低,位数长,不适用当作索引字段,同时是无序的,难以根据特征分析趋势。

类snowflake算法

snowflake是twitter开源的分布式ID生成算法,其核心思想为,一个long型的ID:

41bit作为毫秒数 - 10bit作为机器编号 - 12bit作为毫秒内序列号

算法单机每秒内理论上最多可以生成1000*(2^12),也就是400W的ID,

优缺点: 整个 ID 都是自增的,这个非常适合查看趋势,同时生成不依赖于第三方系统,可靠性很高,可调整性也很高。缺点就是严重依赖机器时钟。

基于MySQL的发号器

字段设置 auto_increment_incrementauto_increment_offset 来保证 ID 自增,每次业务使用下列 SQL 读写 MySQL 得到 ID。

begin;
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
commit;

为了保证高可用,需要有多台 MySQL 机器,也需要为每台机器设置不同的自增起始值和步长,例如:

# TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1

# TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2

优缺点: 简单可靠,成本很小,由专业 DBA 进行维护就可以的。ID 单调递增,有合适的业务是最好的。缺点就是强依赖于数据库,修改起点和步长优点困难,同时也需要额外保证数据库的稳定可用。每次请求都去额外读写数据库的情况下,造成数据库压力很大,需要消耗很多资源。

以上就是最常用的三种全局唯一 ID 生成方案,采用较多的是类 snowflake 的方案,如果请求数列不是很高,可以调低时间的精度等,灵活性很高。生成的 ID 时间趋势自增,甚至可以用来做索引字段,占用空间较小。后期对数据进行分析的时候也很方便。

生产环境唯一ID的使用

类 snowflake 的方案,会采用请求时生成的方式,无法提前生成。

基于 MySQL 的发号器,或者 uuid 模式的,可用提前生成一大批数据,根据业务去定生成数量,放到内存,MQ 或者 Redis List中,业务申请唯一 ID 的时候,直接从其中弹一个出来。同时加一个异步定时任务,固定时间计算一下队列中剩余的唯一 ID 数量,数量不足时及时的再次生成一大批,加入到备选队列。

Go snowflake的demo:
Go snowflake的demo

参考文章:

  1. 有赞技术团队文章 - 发号器
  2. CSDN文章
  3. 美团分布式ID生成
  4. Go snowflake包

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券

查看原文

赞 5 收藏 1 评论 0

stoneworld 赞了文章 · 2019-07-03

Nginx 命令行参数

  • -? | -h — 打印命令行参数的帮助
  • -c file — 使用一个文件作为配置而不是默认配置文件
  • -g directives — 设置全局配置的指令,例如
nginx -g "pid /var/run/nginx.pid; worker_processes sysctl -n hw.ncpu;"
  • -p prefix — 设置 nginx 路径前缀,即保存服务器文件的目录 (默认值为 /usr/local/nginx)
  • -q — 在配置测试期间抑制非错误消息
  • -s signal — 向 master 进程发送信号,参数信号可以是以下几种:

    • stop — 立即关闭
    • quit — 优雅的关闭,即退出前完成已经接收的连接请求
    • reload — 重载配置文件,使用新配置开启心 worker 进程,正常关闭旧 worker 进程
    • reopen — 重新打开日志文件
  • -t — 测试配置文件: nginx 会检查配置是否有正确的语法,然后尝试打开配置中引入的文件
  • -T — 和 -t 一样, 但另外将配置文件转储到标准输出 (1.9.2)
  • -v — 打印 nginx 版本
  • -V — 打印 nginx 版本, 编译器版本及配置参数

参考链接:nginx command line parameters

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券

查看原文

赞 6 收藏 5 评论 0

stoneworld 赞了文章 · 2019-07-03

Nginx 层面配置基础用户验证

原文链接: 何晓东 博客

应用场景:大概是在内部网站需要外部用户访问到,同时不能给访问者网站账号权限,所以在 nginx 层面进行限制。例如外包项目,内部员工有账号进行文档的操作,外包员工没有内部账号,但需要让他们能够看到文档,所以在 nginx 层面设置用户验证是最佳最简单的选择,多数情况下雇主方不会给外包员工开一个基础访问权限的账号。

在 nginx 层面进行进行用户认证的前置条件:需要有对应的密码创建程序,如apache2-utils(Debian,Ubuntu)或 httpd-tools(RHEL / CentOS / Oracle Linux),不同操作系统是不同的软件。

创建账户密码文件

  1. 使用命令 sudo htpasswd -c /etc/apache2/.htpasswd user1 创建第一个账户,然后按下 Enter 键输入密码,同样的命令,没有 -c 参数创建第二个用户及密码, -c 参数为创建文件,在第二次及以后的命令中不需要再次创建文件。
  2. 确认一下文件及账号信息生成成功,使用命令 cat /etc/apache2/.htpasswd 查看文件内容,应该为账号及加密后的密码,如:user1:$apr1$/woC1jnP$KAh0SsVn5qeSMjTtn0E9Q0 等。

配置 nginx 进行 http 基础用户验证

使用 auth_basic 指令指定设置受保护区域的名称,此名称会显示在账号密码弹窗上,使用 auth_basic_user_file 指令设置带有账户密码信息的 .htpasswd 路径。例如配置:

location /api {
    auth_basic           "Administrator's Area";
    auth_basic_user_file /etc/apache2/.htpasswd; 
}

此外,如果某个区块不想继承整个认证体系,可以在区块内设置 auth_basic off,即用户认证关闭状态。例如配置:

server {
    ...
    auth_basic           "Administrator's Area";
    auth_basic_user_file conf/htpasswd;

    location /public/ {
        auth_basic off;
    }
}

通过 ip 地址将认证与访问限制相结合

HTTP基本认证可以通过IP地址有效地与访问限制相结合。您可以实现至少两种方案:

  • 用户需要通过身份验证并且具有 ip 访问权限
  • 用户需要通过身份验证或者具有 ip 访问权限

1.使用 allowdeny 指令来允许或限制指定 ip 的访问,例如配置:

location /api {
    #...
    deny  192.168.1.2;
    allow 192.168.1.1/24;
    allow 127.0.0.1;
    deny  all;
}

192.168.1.2 地址以外的网络,仅授予 192.168.1.1/24 的访问权限。注意:allowdeny 指令将按照定义的顺序应用。

2.通过ip 和 http 身份验证将限制与 satisfy 指令相结合。如果将指令设置为all,则在客户端满足这两个条件时授予访问权限。如果将指令设置为 any,则如果客户端满足至少一个条件,则授予访问权限,例如配置:

location /api {
    #...
    satisfy all;    

    deny  192.168.1.2;
    allow 192.168.1.1/24;
    allow 127.0.0.1;
    deny  all;

    auth_basic           "Administrator's Area";
    auth_basic_user_file conf/htpasswd;
}

综上可以组织成一个完整的例子:

http {
    server {
        listen 192.168.1.23:8080;
        root   /usr/share/nginx/html;

        location /api {
            api;
            satisfy all;

            deny  192.168.1.2;
            allow 192.168.1.1/24;
            allow 127.0.0.1;
            deny  all;

            auth_basic           "Administrator's area";
            auth_basic_user_file /etc/apache2/.htpasswd; 
        }
    }
}

最终效果如图:nginx 基础用户验证

© 原创文章,翻译自 官方文档,部分做了语义调整。

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券

查看原文

赞 11 收藏 7 评论 0

stoneworld 收藏了文章 · 2019-06-14

关于用户关注粉丝表设计方案的思考

方案一

  • follow(关注关系表)
字段名类型索引注解
idprimaryKey()
user_idinteger()->unsigned()->notNull()normal用户
followed_userinteger()->unsigned()->notNull()关注的人的id
statussmallInteger()->unsigned()->defaultValue(1)关注状态:是否取消关注等
created_atinteger()->unsigned()->notNull()normal
updated_atinteger()->unsigned()->notNull()normal
用户每关注一个人,都会在表中添加一条数据

优点

1.设计简单,方便查询
2.可以区分关注的每一个人进行特殊处理(例如,不同人的关注事件,是否互粉,特别关注等),方便扩展
3.好写代码。

缺点

当用户量大时表数据量会非常庞大,因此必需要采用水平分表的方式将用户分散到多个表。

例如,有10万用户,ID为1~10000的用户放在表1(follow_1), ID为10001~20000的用户放在表2(follow_2), 以此类推。

然而分表后又会面临另一个问题,当被关注者依照多个表反查自己的粉丝时将会非常麻烦。因此需要再建一个粉丝表:

  • fans(粉丝表)
字段名类型索引注解
idprimaryKey()
user_idinteger()->unsigned()->notNull()normal用户
followerinteger()->unsigned()->notNull()粉丝
statussmallInteger()->unsigned()->defaultValue(1)关注状态:是否取消关注等
created_atinteger()->unsigned()->notNull()normal
updated_atinteger()->unsigned()->notNull()normal

此数据表依然需要做水平分表处理。

方案二

  • follow(关注关系表)
字段名类型索引注解
idprimaryKey()
user_idinteger()->unsigned()->notNull()唯一用户
followed_usertext关注的人
followertext粉丝
created_atinteger()->unsigned()->notNull()normal
updated_atinteger()->unsigned()->notNull()normal
以json格式记录每个用户关注的人和粉丝

优点

1.每个用户只有一条记录
2.方便查询

缺点

1.当粉丝数或关注的人数过大时,followed_user 和 follower 字段的数据长度会非常大,当用户关注的人或者粉丝数达到十万级别时,一条数据的数据量将会达到 兆 级别,将会极大地降低mysql的查询和php数据处理的效率。

2.每一次使用该表时都要将整条数据取出进行计算,对资源耗费太过严重。

方案三

使用redis的Hash数据类型

Redis hash是一个string类型的field和value的映射表。
Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。

每个用户分一张hash表,表名为用户id(可加前缀或后缀)

用户每关注一个人,便在hash表中添加一条数据

field: 关注用户的id

value:关注时间
  • user_1
fieldvalue
21483423443
31483423445
131483423440
......
  • user_2
fieldvalue
11483423443
51483423445
101483423440
......

......

优点

1.查询处理速度快。

缺点

1.消耗服务器内存和CPU。最好使用一台单独的服务器来运行 Redis

2.数据查询,处理不如关系型数据库灵活。

3.开发步骤复杂,学习成本高。

参考

新浪微博开放平台中的Redis实践(演讲视频)

微博关系服务与Redis的故事

一个微博数据库设计带来的简单思考(使用mysql)

论坛相关讨论

用户中心,关注用户动态的功能,数据库结构是如何设计的?

新浪微博的“关注功能”数据库是如何设计的?

微博关注是根据什么来知道你关注我,我关注你了?数据库怎么设计?

用户中心,关注用户动态的功能,数据库结构是如何设计的?

SNS,微博 好友关注和推送功能的数据库设计是怎么实现的底层设计?

微信朋友圈数据库模式如何设计的?

PPT等资源下载

新浪微博开放平台中的Redis实践_大数据时代feed架构_微博消息系统架构演进_互联网公司技术架构资料.新浪微博.微博架构与平台安全_构建高性能的微博系统

查看原文

stoneworld 赞了文章 · 2019-05-28

Nginx 操作响应头信息

原文链接:何晓东 博客

前置条件:需要编译 ngx_http_headers_module 模块,才支持 header 头信息操作

add_header

意思为将自定义的头信息的添加到响应头,指令为 add_header name value [always];,可以用在 http {}, server {}, location {}, if in location {} 上下文中,
只有当响应状态码等于 200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), 或 308 (1.13.0) 时生效,如果 always 参数指定了,头信息将无视状态码,在所有响应中强制返回。

注意: 可以声明多个 add_header 指令,当且仅当 当前区块没有 add_header 的时候,才会继承上级声明的 add_header 信息

例如:

server {
    add_header a 123;

    location / {
        root /path;
    }

    location /download/ {
        add_header b 321;
    }
}

如上的例子,在访问 /download/ 路由的时候,将返回 b 321 这样的头信息,而访问 / 路由时,将返回上级指定的 a 123 头信息。

add_trailer

意思为将自定义的头信息添加到响应头的末尾,指令为 add_trailer name value [always];,可以用在 http {}, server {}, location {}, if in location {} 上下文中,只有当响应状态码等于 200, 201, 206, 301, 302, 303, 307, 或 308 时生效,如果 always 参数指定了,头信息将无视状态码,在所有响应中强制返回。

注意: 可以声明多个 add_trailer 指令,当且仅当 当前区块没有 add_trailer 的时候,才会继承上级声明的 add_trailer 信息

举例参考 add_header 的例子。

expires

启用或禁用,添加或修改响应头中的 "Expires" 和 "Cache-Control" 字段,指令为 expires [modified] time; 或者 expires epoch | max | off; 当响应状态码等于200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), 或 308 (1.13.0) 时生效,time 参数可以为正时间或者负时间。"Expires" 字段中的时间计算为当前时间和在 time 中指定的时间总和。如果使用该 modified 参数(0.7.0,0.6.32),则将时间计算为文件修改时间与 time 指令中指定的时间之和。

此外,可以使用 "@" 前缀(0.7.9,0.6.34)指定一天中的时间:expires @15h30m;

"Cache-Control" 字段的内容取决于指定时间的符号:

  • 时间为负数 - "Cache-Control: no-cache"
  • 时间为正数或者0 - "Cache-Control: max-age=t" t 是指令中指定的时间,以秒为单位

epoch 参数将 "Expires" 设置为值 "Thu, 01 Jan 1970 00:00:01 GMT",将"Cache-Control" 设置为 "no-cache"。

max 参数将 "Expires" 设置为值 "Thu, 31 Dec 2037 23:55:55 GMT",将"Cache-Control" 设置为 10 年。

off 参数禁用添加或修改 "Expires" 和 "Cache-Control" 响应头字段。

最后一个参数值可以包含变量(1.7.9): 例如:

map $sent_http_content_type $expires {
    default         off;
    application/pdf 42d;
    ~image/         max;
}

expires $expires;

© 主体内容翻译自 Module ngx_http_headers_module docs, 做了一些调整和示例

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券

查看原文

赞 8 收藏 4 评论 0

stoneworld 赞了文章 · 2019-05-28

Nginx 操作响应头信息

原文链接:何晓东 博客

前置条件:需要编译 ngx_http_headers_module 模块,才支持 header 头信息操作

add_header

意思为将自定义的头信息的添加到响应头,指令为 add_header name value [always];,可以用在 http {}, server {}, location {}, if in location {} 上下文中,
只有当响应状态码等于 200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), 或 308 (1.13.0) 时生效,如果 always 参数指定了,头信息将无视状态码,在所有响应中强制返回。

注意: 可以声明多个 add_header 指令,当且仅当 当前区块没有 add_header 的时候,才会继承上级声明的 add_header 信息

例如:

server {
    add_header a 123;

    location / {
        root /path;
    }

    location /download/ {
        add_header b 321;
    }
}

如上的例子,在访问 /download/ 路由的时候,将返回 b 321 这样的头信息,而访问 / 路由时,将返回上级指定的 a 123 头信息。

add_trailer

意思为将自定义的头信息添加到响应头的末尾,指令为 add_trailer name value [always];,可以用在 http {}, server {}, location {}, if in location {} 上下文中,只有当响应状态码等于 200, 201, 206, 301, 302, 303, 307, 或 308 时生效,如果 always 参数指定了,头信息将无视状态码,在所有响应中强制返回。

注意: 可以声明多个 add_trailer 指令,当且仅当 当前区块没有 add_trailer 的时候,才会继承上级声明的 add_trailer 信息

举例参考 add_header 的例子。

expires

启用或禁用,添加或修改响应头中的 "Expires" 和 "Cache-Control" 字段,指令为 expires [modified] time; 或者 expires epoch | max | off; 当响应状态码等于200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), 或 308 (1.13.0) 时生效,time 参数可以为正时间或者负时间。"Expires" 字段中的时间计算为当前时间和在 time 中指定的时间总和。如果使用该 modified 参数(0.7.0,0.6.32),则将时间计算为文件修改时间与 time 指令中指定的时间之和。

此外,可以使用 "@" 前缀(0.7.9,0.6.34)指定一天中的时间:expires @15h30m;

"Cache-Control" 字段的内容取决于指定时间的符号:

  • 时间为负数 - "Cache-Control: no-cache"
  • 时间为正数或者0 - "Cache-Control: max-age=t" t 是指令中指定的时间,以秒为单位

epoch 参数将 "Expires" 设置为值 "Thu, 01 Jan 1970 00:00:01 GMT",将"Cache-Control" 设置为 "no-cache"。

max 参数将 "Expires" 设置为值 "Thu, 31 Dec 2037 23:55:55 GMT",将"Cache-Control" 设置为 10 年。

off 参数禁用添加或修改 "Expires" 和 "Cache-Control" 响应头字段。

最后一个参数值可以包含变量(1.7.9): 例如:

map $sent_http_content_type $expires {
    default         off;
    application/pdf 42d;
    ~image/         max;
}

expires $expires;

© 主体内容翻译自 Module ngx_http_headers_module docs, 做了一些调整和示例

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券

查看原文

赞 8 收藏 4 评论 0

stoneworld 赞了文章 · 2019-05-28

Nginx 操作响应头信息

原文链接:何晓东 博客

前置条件:需要编译 ngx_http_headers_module 模块,才支持 header 头信息操作

add_header

意思为将自定义的头信息的添加到响应头,指令为 add_header name value [always];,可以用在 http {}, server {}, location {}, if in location {} 上下文中,
只有当响应状态码等于 200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), 或 308 (1.13.0) 时生效,如果 always 参数指定了,头信息将无视状态码,在所有响应中强制返回。

注意: 可以声明多个 add_header 指令,当且仅当 当前区块没有 add_header 的时候,才会继承上级声明的 add_header 信息

例如:

server {
    add_header a 123;

    location / {
        root /path;
    }

    location /download/ {
        add_header b 321;
    }
}

如上的例子,在访问 /download/ 路由的时候,将返回 b 321 这样的头信息,而访问 / 路由时,将返回上级指定的 a 123 头信息。

add_trailer

意思为将自定义的头信息添加到响应头的末尾,指令为 add_trailer name value [always];,可以用在 http {}, server {}, location {}, if in location {} 上下文中,只有当响应状态码等于 200, 201, 206, 301, 302, 303, 307, 或 308 时生效,如果 always 参数指定了,头信息将无视状态码,在所有响应中强制返回。

注意: 可以声明多个 add_trailer 指令,当且仅当 当前区块没有 add_trailer 的时候,才会继承上级声明的 add_trailer 信息

举例参考 add_header 的例子。

expires

启用或禁用,添加或修改响应头中的 "Expires" 和 "Cache-Control" 字段,指令为 expires [modified] time; 或者 expires epoch | max | off; 当响应状态码等于200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), 或 308 (1.13.0) 时生效,time 参数可以为正时间或者负时间。"Expires" 字段中的时间计算为当前时间和在 time 中指定的时间总和。如果使用该 modified 参数(0.7.0,0.6.32),则将时间计算为文件修改时间与 time 指令中指定的时间之和。

此外,可以使用 "@" 前缀(0.7.9,0.6.34)指定一天中的时间:expires @15h30m;

"Cache-Control" 字段的内容取决于指定时间的符号:

  • 时间为负数 - "Cache-Control: no-cache"
  • 时间为正数或者0 - "Cache-Control: max-age=t" t 是指令中指定的时间,以秒为单位

epoch 参数将 "Expires" 设置为值 "Thu, 01 Jan 1970 00:00:01 GMT",将"Cache-Control" 设置为 "no-cache"。

max 参数将 "Expires" 设置为值 "Thu, 31 Dec 2037 23:55:55 GMT",将"Cache-Control" 设置为 10 年。

off 参数禁用添加或修改 "Expires" 和 "Cache-Control" 响应头字段。

最后一个参数值可以包含变量(1.7.9): 例如:

map $sent_http_content_type $expires {
    default         off;
    application/pdf 42d;
    ~image/         max;
}

expires $expires;

© 主体内容翻译自 Module ngx_http_headers_module docs, 做了一些调整和示例

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券

查看原文

赞 8 收藏 4 评论 0

stoneworld 赞了文章 · 2019-05-23

Nginx 静态文件服务配置及优化

原文链接: 何晓东 博客

根目录和索引文件

root 指令指定将用于搜索文件的根目录。 为了获取所请求文件的路径,NGINX 将请求 URI 附加到 root 指令指定的路径。该指令可以放在 http {}server {}location {} 上下文中的任何级别。在下面的示例中,为虚拟服务器定义了 root 指令。 它适用于未包含根指令的所有location {} 块,以显式重新定义根:

server {
    root /www/data;

    location / {
    }

    location /images/ {
    }

    location ~ \.(mp3|mp4) {
        root /www/media;
    }
}

在这里,NGINX 针对 /images/ 开头的 URI 将在文件系统的 /www/ data/images/ 目录中搜索相应文件。 如果 URI 以 .mp3.mp4 扩展名结尾,则 NGINX 会在 /www/media/ 目录中搜索该文件,因为它是在匹配的位置块中定义的。

如果请求以 / 结尾,则 NGINX 将其视为对目录的请求,并尝试在目录中查找索引文件。index 指令定义索引文件的名称(默认值为 index.html)。要继续该示例,如果请求 URI 是 /images/some/path/,则 NGINX 会返回文件 /www/data/images/some/path/index.html(如果存在)。如果没有,NGINX 默认返回 HTTP 404 错误(未找到)。要配置 NGINX 以返回自动生成的目录列表,请在 autoindex 指令中包含 on 参数:

location /images/ {
    autoindex on;
}

你可以在 index 指令中列出多个文件名。 NGINX按指定的顺序搜索文件并返回它找到的第一个文件。

location / {
    index index.$geo.html index.htm index.html;
}

这里使用的 $geo 变量是通过 geo 指令设置的自定义变量。变量的值取决于客户端的 IP 地址。

要返回索引文件,NGINX 会检查它是否存在,然后对通过将索引文件的名称附加到基础 URI 上获得的新 URI 进行内部重定向。内部重定向导致对位置的新搜索,并且可能最终位于另一个位置,如以下示例所示:

location / {
    root /data;
    index index.html index.php;
}

location ~ \.php {
    fastcgi_pass localhost:8000;
    #...

}

这里,如果请求中的 URI 是 /path/,并且 /data/path/index.html 不存在但 /data/path/index.php 存在,则内部重定向到 /path/index.php 将映射到第二个位置。结果,请求被代理。

尝试几种选择

try_files 指令可用于检查指定的文件或目录是否存在; NGINX 会进行内部重定向,如果没有,则返回指定的状态代码。例如,要检查对应于请求 URI 的文件是否存在,请使用 try_files指令和 $uri 变量,如下所示:

server {
    root /www/data;

    location /images/ {
        try_files $uri /images/default.gif;
    }
}

该文件以 URI 的形式指定,使用在当前位置或虚拟服务器的上下文中设置的根或别名指令进行处理。在这种情况下,如果对应于原始 URI 的文件不存在,NGINX 会将内部重定向到最后一个参数指定的 URI,并返回 /www/data/images/default.gif

最后一个参数也可以是状态代码(直接以等号开头)或位置名称。 在以下示例中,如果 try_files 指令的所有参数都不会解析为现有文件或目录,则会返回 404 错误。

location / {
    try_files $uri $uri/ $uri.html =404;
}

在下一个示例中,如果原始 URI 和带有附加尾部斜杠的 URI 都不会解析为现有文件或目录,则会将请求重定向到指定位置,并将其传递给代理服务器。

location / {
    try_files $uri $uri/ @backend;
}

location @backend {
    proxy_pass http://backend.example.com;
}

有关更多信息,请观看内容缓存网络研讨会,了解如何显着提高网站性能,并深入了解 NGINX 的缓存功能。

优化服务内容的性能

加载速度是提供任何内容的关键因素。 对 NGINX 配置进行微小优化可以提高生产力并帮助实现最佳性能。

启用 sendfile

默认情况下,NGINX 会自行处理文件传输,并在发送之前将文件复制到缓冲区中。 启用 sendfile 指令消除了将数据复制到缓冲区的步骤,并允许将数据从一个文件描述符直接复制到另一个文件描述符。或者,为了防止一个快速连接完全占用工作进程,可以使用 sendfile_max_chunk 指令限制单个sendfile() 调用中传输的数据量(在本例中为1 MB):

location /mp3 {
    sendfile           on;
    sendfile_max_chunk 1m;
    #...

}

启用 tcp_nopush

tcp_nopush 指令与 sendfile on; 指令一起使用。这使得 NGINX 可以在 sendfile() 获取数据块之后立即在一个数据包中发送 HTTP 响应头。

location /mp3 {
    sendfile   on;
    tcp_nopush on;
    #...

}

启用 tcp_nodelay

tcp_nodelay 指令允许覆盖 Nagle 的算法,该算法最初设计用于解决慢速网络中小数据包的问题。该算法将许多小数据包合并为一个较大的数据包,并以 200 毫秒的延迟发送数据包。如今,在提供大型静态文件时,无论数据包大小如何,都可以立即发送数据。延迟也会影响在线应用程序(ssh,在线游戏,在线交易等)。默认情况下,tcp_nodelay 指令设置为 on,这意味着禁用了 Nagle的算法。此指令仅用于 keepalive 连接:

location /mp3  {
    tcp_nodelay       on;
    keepalive_timeout 65;
    #...
    
}

优化积压队列

其中一个重要因素是 NGINX 可以多快地处理传入连接。一般规则是在建立连接时,将其放入侦听套接字的 "listen" (监听)队列中。在正常负载下,队列很小或根本没有队列。但是在高负载下,队列会急剧增长,导致性能不均匀,连接中断,延迟增加。

显示积压队列

使用命令 netstat -Lan 来显示当前监听队列。输出可能如下所示,它显示在端口 80上的监听队列中,有 10 个未接受的连接,这些连接针对配置的最多 128 个排队连接。这种情况很正常。

Current listen queue sizes (qlen/incqlen/maxqlen)
Listen         Local Address         
0/0/128        *.12345            
10/0/128        *.80       
0/0/128        *.8080

相反,在以下命令中,未接受的连接数(192)超过了 128 的限制。当网站流量很大时,这种情况很常见。要获得最佳性能,需要在操作系统和 NGINX 配置中增加可以排队等待 NGINX 接受的最大连接数。

Current listen queue sizes (qlen/incqlen/maxqlen)
Listen         Local Address         
0/0/128        *.12345            
192/0/128        *.80       
0/0/128        *.8080

调整操作系统

net.core.somaxconn 内核参数的值从其默认值(128)增加到足以容纳大量流量的值。在这个例子中,它增加到 4096。

  • FreeBSD 的命令为 sudo sysctl kern.ipc.somaxconn=4096
  • Linux 的命令为 1. sudo sysctl -w net.core.somaxconn=4096 2. 将 net.core.somaxconn = 4096 加入到 /etc/sysctl.conf 文件中。

调整 NGINX

如果将 somaxconn 内核参数设置为大于 512 的值,请将 backlog 参数增加在 NGINX listen 指令以匹配修改:

server {
    listen 80 backlog=4096;
    # ...

}

© 文章翻译自 Nginx Serving Static Content,部分做了语义调整。

最后恰饭 阿里云全系列产品/短信包特惠购买 中小企业上云最佳选择 阿里云内部优惠券

查看原文

赞 20 收藏 11 评论 3

stoneworld 关注了专栏 · 2019-04-08

swoole 源码分析

解析 swoole php扩展

关注 85

认证与成就

  • 获得 126 次点赞
  • 获得 18 枚徽章 获得 0 枚金徽章, 获得 4 枚银徽章, 获得 14 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

  • Wechat

    微信公众平台企业号 SDK

注册于 2015-04-24
个人主页被 1.9k 人浏览