京东云罗玉杰:OpenResty 在直播场景中的应用

2019 年 3 月 23 日,OpenResty 社区联合又拍云,举办 OpenResty × Open Talk 全国巡回沙龙·北京站,京东云技术专家罗玉杰在活动上做了《 OpenResty 在直播场景中的应用 》的分享。OpenResty x Open Talk 全国巡回沙龙是由 OpenResty 社区、又拍云发起,邀请业内资深的 OpenResty 技术专家,分享 OpenResty 实战经验,增进 OpenResty 使用者的交流与学习,推动 OpenResty 开源项目的发展。活动已经在深圳、北京两地举办,未来将陆续在武汉、上海、杭州、成都等城市巡回举办。罗玉杰,京东云技术专家,10 余年 CDN、流媒体行业从业经验,热衷于开源软件的开发与研究,对 OpenResty、Nginx 模块开发有较深入的研究,熟悉 CDN 架构和主流流媒体协议。以下是分享全文:大家下午好,我是来自京东云的罗玉杰,今天给大家分享的主题是 《OpenResty 在直播场景中的应用》。项目需求京东云前期的服务是基于 Nginx 二次开发的,之后因为要对接上云的需求,于是新做了两个服务,一个是对接云存储的上传服务,另一个是偏业务层的直播时移回看服务。项目的需求是做视频数据上云,主要是视频的相关数据对接云存储,需求的开发周期很紧,基本上是以周为单位。我们之前的服务用 C 、C++ 开发,但 C 和 C++ 的开发周期很长。我们发现这个项目基于 OpenResty 开发是非常适合的,可以极大地缩短开发周期,同时提高运行效率,并且 OpenResty 对运维非常友好,能提供很多的配置项,让运维根据线上动态修改一些配置,甚至运维都可以看懂代码的主流程。项目体系结构上图是一个直播服务的主流体系结构,先是主播基于 RTMP 协议推到 CDN 边缘,接着到视频源站接入层,然后把 RTMP 流推送到切片上传服务器,上面有两个服务:一个是切片服务,把流式的视频流进行切片存储到本地,生成 TS 视频文件和 M3U8 文本文件,每形成一个小切片都会通知上传服务,后者将这些 TS 文件和 M3U8 文件基于 AWS S3 协议上传到云存储服务,我们的云存储兼容 AWS S3 协议。在此基础上,我们用 OpenResty 做了一个直播时移回看服务,用户基于 HLS 协议看视频,请求参数里带上时间段信息,比如几天之前或者几个小时之前的信息,此服务从云存储上下载 M3U8 信息进行裁剪,再返回给用户,用户就可以看到视频了。HLS 协议的应用面、支持面很广,各大厂商、终端支持得都非常好,而且对 HTTP 和 CDN 原有的技术栈、体系非常友好,可以充分地利用原来的一些积累。有的播放是基于 RTMP,HDL(HTTP + FLV)协议的,需要播放器的支持。项目功能1、基于 s3 PUT 协议将 TS 文件上传至云存储。2、S3 multi 分片上传大文件,支持断点续传。这个服务重度依赖于 Redis,用 Redis 实现任务队列、存储任务元数据、点播 M3U8。3、基于 Redis 实现任务队列的同时做了 Nginx Worker 的负载调度。在此基础上做了对于后端服务的保护,连接和请求量控制,防止被短时间内特别大的突发流量把后端的云服务直接打垮。实现任务队列之后,对后端的链接数是固定的,而且请求处理看的是后端服务的能力,简单地说,它处理得多快就请求得多快。4、为了保证云和服务的高可靠性,我们做了失败重试和异常处理、降低策略。其中,任务失败是不可避免的,现在也遇到了大量的任务失败,包括链接失败、后端服务异常等,需要把失败的任务进行重试,降级。把它在失败队列里面,进行一些指数退避。还有一些降级策略,我这个服务依赖于后面的 Redis 服务,和后端的云存储服务,如果它们失败之后,我们需要做一些功能的降级,保证我们的服务高可用。在后端 Redis 服务恢复的时候再把数据同步过去,保证数据不会丢失。5、还有就是生成直播、点播 M38,为后续的服务提供一些基础数据。如直播时移回看服务。AWS S3 协议AWS S3 比较复杂的就是鉴权,主要用它的两个协议,一个是 PUT,一个是 MULTI PART。AWS S3 的鉴权和 Nginx 中的 Secure Link 模块比较相似,将请求相关信息用私钥做一个散列,这个散列的内容会放到 HTTP 头 authorization 里面,服务端收到请求后,会有同样的方式和同样的私钥来计算这个内容,计算出的内容是相同的就会通过,不相同的话会认为是一个非法请求。它主要分三步骤,第一步是创建任务,创建任务之后会返回一个 ID 当做任务的 Session ID,用 POST 和 REST 规范实现的协议。初始化任务之后,可以传各种分片了,然后还是用 PUT 传小片,加上 Session ID,每一片都是这样。上传任务成功之后,会发一个 Complete 消息,然后文件就认为是成功了,成功之后就会合并成一个新的文件,对外生成一个可用的大文件。HLS 协议HLS 协议,全称是 HTTP LIVE STREAMING 协议,是由苹果推出的,可读性很强。里面的每一个片都是一个 HTTP 请求,整个文本协议就是一个索引。上图是每一个视频段的时长,这个是 8 秒是视频的最大长度。直播的应用中会有一个 Sequence 从零开始递增的,如果有一个新片,就会把旧片去掉,把新的加上去,并增加 Sequence。任务队列、均衡、流控下面再介绍一下具体的功能实现,任务收到请求之后不是直接处理,而是异步处理的。先把请求分发到各个 Worker 的私有队列,分发算法是用的 crc32,因为 crc32 足够快、足够轻量,基于一个 key 视频流会有域名、App、stream,再加上 TS 的文件名称。这样分发可以很好地做一次负载均衡。基于这个任务队列,可以处理大量的突发请求,如果突然有了数倍的请求,可以把这些消息发到 Redis 里,由 Redis 存储这些请求。每个 Worker 会同步进行处理,把 TS 片上传,上传完之后再生成 M3U8 文件。我们现在对后端固定了连接数,一个 woker 一个链接,因为存储集群的连接数量是有限的,现在采取一个简单策略,后端能处理请求多快,就发送多快,处理完之后可以马上发送下一个。因任务队列是同步处理,是同步非阻塞的,不会发送超过后端的处理能力。我们未来准备进行优化的方向就是把任务队列分成多个优先级,高优先级的先处理,低优先级的降级处理。比如我们线上遇到的一些视频流,它不太正常会大量的切小,比如正常视频 10 秒一片,而它 10 毫秒就一片,这样我们会把它的优先级降低,防止异常任务导致正常任务不能合理地处理。以后就是要实现可以动态调解链接数、请求速率和流量。如果后端的处理能力很强,可以动态增长一些链接数和请求速率,一旦遇到瓶颈后可以动态收缩。任务分发比较简单,主要就是上面的三行代码,每一个 Worker 拿到一个任务后,把任务分发给相应的 Worker ,它的算法是拿到总 Worker 数然后基于 crc32 和 key ,得到正确的 Worker ID,把它加到任务队列里。这样的做法好处是每个任务分发是非单点的,每一个 Worker 都在做分发,把请求的任务发到任务队列里,请求的元信息放入 Redis 里面,还有一个就是任务拉取消费的协程,拉取任务并执行。失败重试、降级、高可靠如果数据量大会有很多失败的任务,失败任务需要放入失败队列,进行指数退避重试。重试成功后再进行后续处理,比如添加进点播 m3u8、分片 complete。分片 complete 是如果原来有 100 个任务会同时执行,但是现在有 3 个失败了,我们可以判断一下它是不是最后一个,如果是最后一个的分片就要调一下 complete,然后完成这个分片,完成整个事务。同时我们做了一个 Redis 失败时的方案,Redis 失败后需要把 Redis 的数据降级存到本地,一部分存到 share dict,另一部分用 LRU cache,TS 对应 m3u8 的索引信息会用 share dict 做缓存。LRU 主要是存一些 m3u8 的 key,存储哪些信息和流做了降级,Redis 恢复后会把这些信息同步到 Redis。因为存在于各个 Worker 里面数据量会比较大,有些任务会重复执行,我们下一步工作就想基于 share dict,加一个按照指定值来排序的功能,这样就可以优先处理最近的任务,将历史任务推后处理。我们还有一些 M3U8 的列表数据存储在 Redis,因为线上的第一版本是单实例的,存储空间比较有限,但是现在对接的流量越来越多,单实例内存空间不足,于是我们做了支持 Redis 集群的工作,实现 Reids 高可用,突破内存限制。还有一个比较兜底的策略:定期磁盘巡检,重新处理失败任务。事务可能是在任何的时点失败的,但是只要我们能够重做整个任务,业务流程就是完整的。遇到的问题和优化方案第一版的时候是全局的单一任务队列,基于 resty lock 的锁取保护这个队列,每一个 woker 争用锁,获取任务,锁冲突比较严重,CPU 消耗也高,因为那个锁是轮询锁,优化后我们去掉了一个锁实现了无锁,每一个 Worker一个任务队列, 每个 Worker 基于 CRC_32 分发任务。旧版一个 TS 更新一次 M3U8,一次生成一个哈希表,数量较多的情况下 CPU 开销比较大。我们进行了优化,做了一些定时触发的机制,进行定期更新,因为点播 M3U8 对时间是不敏感的,可以定期地更新,减少开销。当然直播的 还是实时生产的,因为要保证直播的实时性。直播方面如果异常切片太多,用户也不能很好观看,会进行主动丢片,主要是基于 Redis 锁去实现;对于 Redis 内存消耗高的问题我们搭建了 Redis 集群。直播时移回看服务我们开发了一个直播时移回看服务,根据用户请求的时间去后台下载相应的 M3U8 的数据进行裁剪拼接返回给用户。这一块的 M3U8 信息不是很大,非常适合用 MLCACHE 保存,它是一个开源的两级缓存,Worker 一级的和共享内存一级,因为共享内存缓存有锁冲突,MLCACHE 会把一些热点数据缓存到 Worker 级别,这样是无锁的,使用后效果非常好,虽然文件不大,但是运行时间建连,网络IO耗时很大,经过缓存之后可以大大提高处理效率,节省时间。时移的时候每一个用户会也一个 Session 记录上次返回的 M3U8 位置,因为直播流会有中断,不是 24 小时都有流的,用户遇到了一个断洞,可以跳过看后面的视频,时移不需要等待,并且用户网络短暂异常时不会跳片。点击观看演讲视频和 PPT~OpenResty 在直播场景中的应用 ...

April 18, 2019 · 2 min · jiezi

Nginx https 配置

Nginx 证书部署转载地址:腾讯云Nginx证书部署注意:https 需要申请证书,需自行申请证书安装使用 “WinSCP” 工具,登录 Nginx 服务器。将已获取到的1_www.domain.com_bundle.crt 证书文件和 2_www.domain.com.key 私钥文件拷贝到 Nginx 服务器的 /usr/local/nginx/conf 目录下。说明:若无 /usr/local/nginx/conf 目录,可新建。关闭 WinSCP 界面。使用 “PuTTY” 工具,登录 Nginx 服务器。更新 Nginx 根目录下的 conf/nginx.conf 文件。修改内容如下: server { listen 443; server_name www.domain.com; #填写绑定证书的域名 ssl on; ssl_certificate 1_www.domain.com_bundle.crt; ssl_certificate_key 2_www.domain.com.key; ssl_session_timeout 5m; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置 ssl_prefer_server_ciphers on; location / { root html; #站点目录 index index.html index.htm; } }配置文件的主要参数说明如下:listen 443:SSL 访问端口号为 443ssl on:启用 SSL 功能ssl_certificate:证书文件ssl_certificate_key:私钥文件ssl_protocols:使用的协议ssl_ciphers:配置加密套件,写法遵循 openssl 标准执行以下命令,检验配置是否有误。 bin/nginx –t是,请重新配置。否,重启 Nginx。即可使用 https://www.domain.com 进行访问。使用全站加密,HTTP 自动跳转 HTTPS(可选)对于用户不知道网站可以通过 HTTPS 方式访问的情况,我们可以通过配置服务器,让其自动将 HTTP 的请求重定向到 HTTPS。您可以在页面中添加 JS 脚本,也可以在后端程序中添加重定向,还可以通过 Web 服务器实现跳转。若您在编译时没有去掉 pcre,Nginx 支持 rewrite 功能。您可在 HTTP 的 server 中增加 rewrite ^(.*) https://$host$1 permanent;,即可将 80 端口的请求重定向为 HTTPS。 ...

April 16, 2019 · 1 min · jiezi

Python3基础知识

Python3基础知识 | 基础语法 Python3基础知识 | 编程第一步 Python3基础知识 | 基本数据类型Python3基础知识 | 解释器 Python3基础知识 | 注释 Python3基础知识 | 运算符Python3基础知识 | 数字 Python3基础知识 | 字符串 Python3基础知识 | 列表Python3基础知识 | 元组 Python3基础知识 | 字典 Python3基础知识 | 条件控制Python3基础知识 | 循环 Python3基础知识 | 迭代器与生成器 Python3基础知识 | 函数Python3基础知识 | 数据结构 Python3基础知识 | 模块 Python3基础知识 | 输入和输出Python3基础知识 | File 方法 Python3基础知识 | OS 文件目录方法 Python3基础知识 | 错误和异常Python3基础知识 | 标准库概览 Python3基础知识 | 日期和时间 Python3基础知识 | 正则表达式Python3基础知识 | XML解析 Python3基础知识 | JSON 数据解析 Python3基础知识 | MySQL 数据库连接Python3基础知识 | 多线程 Python3基础知识 | 面向对象 ...

April 16, 2019 · 1 min · jiezi

关于如何快速调教Nginx的几点总结

关于如何快速调教Nginx的几点总结关于Nginx的好与坏,我觉得没有必要去介绍了,在这里主要分享一下我在实际的项目部署中是如何快速的调教Nginx的。其中分享的源码大家可以作为模板代码,根据自身项目的实际情况,酌情使用。这里简单的说一说我为什么要写这篇文章,网上有很多大而全的文章在介绍Nginx是什么,如何入门等等,玩了很多的文字游戏,反正我接触Nginx的时候,去查阅文档给我的是这种感觉,大而全,但是很乱。这里我要讲的不是Nginx的理论知识,而是一些能够快速的应用到项目中的实际技巧。废话就说这么多,开始本次分享的主体。调教一:开启GZIP,提高页面加载速度http:{ … gzip on; gzip_min_length 10; gzip_comp_level 4; gzip_disable “MSIE [1-10] .”; gzip_types text/plain appliaton/x-javascript text/css application/xml image/jpeg image/gif image/png image/svg+xml; …}gzip on开启gzip压缩功能gzip_min_lenght 10压缩临界值,大于10KB的文件才压缩gzip_com_level 4设置压缩级别[0-10],数字越大,压缩比越好,但消耗的时间越长gzip_desable “MSIE [1-10].“对IE浏览器不采用压缩,[1-10]表示浏览器版本范围gzip_types需要进行文件压缩的类型,根据自身情况酌情添加一般情况下,关于gzip的配置,设置以上几个参数就可以了调教二:无www的域名跳转到带www的域名server{ listen 80; server_name http://youdomain.com; return 301 http://www.youdomain.com$request_uri;}针对自己的域名,配置一个全局的server,对裸域名的请求进行转发,注意要加上“$request_uri”网上有关这个问题提供了另外一种解决办法,代码如下:server{ listen 80; server_name www.youdomain.com; if ( $host !=‘www.youdomain.com’){ rewrite ^/(.)$ http://www.youdomain.com/$1 permanent; } rewrite ^/(.)$ http://$host$1 permanent;}我在自己的项目中使用第二种方式进行配置,貌似没有生效,所以改为了第一种配置方式调教三:配置https关于如何配置server(http)这里不再介绍,网上相关文档很多,这里主要分享如何在Nginx中配置HTTPS,配置代码如下:server{ listen 443 ssl; server_name www.youdomain.com; access_log logs/com_youdomain_logs.log; ssl_certificate c:/sslfile/cert.crt; ssl_certificate_key c:/sslfile/cert.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; location /{ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; proxy_set_header X-real-IP $remote_addr; proxy_set_header X-Forwarded-proto $scheme; proxy_connect_timeout 240; proxy_send_timeout 240; proxy_read_timeout 240; proxy_pass http://localhost:8080; proxy_redirect ^http://([^:]+)(:\d+)?(.*)$ https://$1$3; }}这里需要注意几个地方:监听的端口由原来的80或者其他(通常是80端口)改为 443 ssl 。ssl_certificate配置HTTPS证书放置的路径,ssl_certificate_key 放置HTTPS证书的秘钥路径。ssl_session_cache配置HTTPS的缓存,ssl_session_timeout配置HTTPS缓存的生命周期。在location配置中,proxy_set_header部分的代码是一个固定用法,不进行介绍。proxy_connect_timeout,proxy_send_timeout和proxy_read_timeout主要配置在HTTPS下建立请求连接、发送数据和读取数据的时间上线(超时处理)proxy_pass设置Nginx需要代理的请求对象,如http://localhost:8080 ,这里需要web容器配置,在接下来会单独介绍proxy_redirect设置代理后的请求转发重定向:^http://(1+)(:d+)?(.*)$ https://$1$3; 将http请求重定向到https上。要实现https加密请求,还需要web容器的配合,在这里以Apache Tomcat配置为例进行介绍。调教三:开启tomcat对https请求的支持在上一小节中,我们对server的代理做了如下的配置:server{ … location /{ … proxy_pass http://localhost:8080; … } …}首先,我们需要将tomcat的连接器(Connector)的端口设置为8080,将转发重定向的端口(redirectPort)和代理端口(proxyPort)设置为443。具体的配置代码如下:…<Connector prot=“8080” protocol=“HTTP/1.1” connectionTimeout=“20000” redirectPort=“443” proxyPort=“443”/>…然后,需要在Host配置中设置remoteIpHeader、protocolHeader和protocolHeaderHttpsValue这三个属性的值,详细配置如下:… <Host name=“localhost” appBase=“webapps” ….> <Value className=“org.apache.catalina.values.RemoteIpValue” remoteIpHeader=“X-Forwarded-For” protocolHeader=“X-Forwarded-Proto” protocolHeaderHttpsValue=“https”/> … … <Context docBase=”” path="" reloadable=“false”></Context> </Host>…以上就是Nginx+tomcat的组合方式开启https请求的调教过程。结束语本文是我在实际项目开发过程中认为比较常用其重要的几个调教点技巧,希望本次分享能够帮到你。此次文章主要分享关于Nginx小而精的一些常用配置技巧,更多的配置如分布式下一服多实例的配置我会单独些一篇文章进行分享,今天的内容就到这里结束了;再次感谢你的拜读,拜拜~~原文:关于如何快速调教Nginx的几点总结: ↩ ...

April 14, 2019 · 1 min · jiezi

Web前端必备-Nginx知识汇总

一、Nginx简介Nginx是一个高性能、轻量级的Web和反向代理服务器, 其特点是占有内存及资源少、抗并发能力强。Nginx安装简单、配置简洁、启动快速便捷、支持热部署、支持 SSL、拥有高度模块化的设计。Nginx的主要功能有:Web服务器反向代理负载均衡二、运行和控制Nginx备注: 以下命令中的 /usr/local/nginx 是nginx二进制文件的绝对路径,需根据自己实际的安装路径而定。1.启动/usr/local/nginx/sbin/nginx2.重新打开日志文件/usr/local/nginx/sbin/nginx -s reopen3.重新载入配置文件/usr/local/nginx/sbin/nginx -s reload4.停止/usr/local/nginx/sbin/nginx -s stop5.从容停止(1) 查看进程号ps -ef|grep nginx(2) 杀死进程kill -QUIT <进程号> 或 kill -TERM <进程号>6.强制停止pkill -9 nginx三、Nginx作为Web服务器Nginx作为Web服务器, 需要定义server虚拟主机,让这些虚拟主机去处理对于特定域名或IP地址的请求。每个server虚拟主机都定义了 location 指令,location 定义了对于指定的一组 URI 是如何匹配和进行处理的。1.web服务器基本实例server { listen 80; server_name www.example.com; location / { root /usr/local/www; index index.html; } }参数说明:server 代表1个虚拟主机,可以有多个server_name 匹配请求的指定域名或IP地址location 配置请求的路由,匹配对应的URIroot 查找资源的路径(文件夹目录)index 默认查找2.location匹配规则(请求过滤)(1) 语法server { location 表达式 { }}(2) location表达式的类型@ 它定义一个命名的 location,使用在内部定向时,例如 error_page, try_files/ 通用匹配,任何请求都会匹配到= 开头, 表示精确匹配, 只有请求的url路径与=后面的字符串完全相等才会匹配到(优先级最高)^~ 表示普通字符匹配。使用前缀匹配。如果匹配成功,则不再匹配其他location~ 开头表示区分大小写的正则匹配~* 开头表示不区分大小写的正则匹配(3) location表达式的优先级= 的优先级最高。一旦匹配成功,则不再查找其他匹配项。^ 类型表达式。一旦匹配成功,则不再查找其他匹配项。 和 ~ 的优先级次之。如果有多个location的正则能匹配的话,则使用正则表达式最长的那个。常规字符串匹配类型。按前缀匹配。3.URL重写URL重写是指: 当请求的URL满足事先定义好的规则时, 将跳转/定向到某个规则,比如常见的伪静态、301重定向、浏览器定向等。(1) 语法server { rewrite 规则 定向路径 重写类型;}rewrite参数说明:规则:字符串或者正则来表示想匹配的目标url定向路径:匹配到规则后要定向的路径,如果规则里有正则,则可以使用$index来表示正则里的捕获分组重写类型:last :表示完成rewrite,浏览器地址栏URL地址不变break;本条规则匹配完成后,终止匹配,不再匹配后面的规则,浏览器地址栏URL地址不变redirect:返回302临时重定向,浏览器地址会显示跳转后的URL地址permanent:返回301永久重定向,浏览器地址栏会显示跳转后的URL地址(2) 示例域名跳转: 访问 www.aaa.com 跳转到 www.bbb.comserver { listen 80; server_name www.aaa.com; location / { rewrite ^/$ www.bbb.com permanent ; }}4.try_filestry_files是指: 按顺序检查文件是否存在,返回第一个找到的文件。如果所有的文件都找不到,会进行一个内部重定向到最后一个参数.(1) 语法try_files file1 files2 … uri参数说明:最后一个参数是回退URI, 且必须存在,否则将会出现内部500错误。只有最后一个参数可以引起一个内部重定向,之前的参数只设置内部URI的指向。最后一个参数也可以是一个命名的location。最后一个参数如果不是命名的location那么$args不会自动保留,如果你想保留$args,必须在最后一个参数里明确声明。示例为:try_files $uri $uri/ /index.php?q=$uri&$args;(2) 示例跳转到文件当访问:www.example.com/test 时会依次查找,若 1.html,2.html 都不存在,最终返回 3.htmlserver { listen 80; server_name www.example.com; root html; index index.html; location /test { try_files /1.html /2.html /3.html; }}跳转到变量当访问:www.example.com/test 时会依次查找,若 1.html,2.html 都不存在,则跳转到命名为abc的locationserver { listen 80; server_name www.example.com; root html; index index.html; location /test { try_files /1.html /2.html @abc; } location @abc{ rewrite ^/(.)$ http://www.example2.com; }}vue-router设置HTML5 History 模式时, nginx的配置如下:location / { # URL 匹配不到任何静态资源,返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。 try_files $uri $uri/ /index.html;}5.Gzip配置server { # 开启gzip 压缩 gzip on; # 设置gzip所需的http协议最低版本 (HTTP/1.1, HTTP/1.0) gzip_http_version 1.1; # 设置压缩级别(1-9), 值越大压缩率越高,同时消耗cpu资源也越多,建议设置在4左右 gzip_comp_level 4; # 设置压缩的最小字节数, 页面Content-Length获取 gzip_min_length 1000; # 设置压缩文件的类型 (text/html), 不建议压缩图片(如jpg、png本身已压缩) gzip_types text/plain application/javascript text/css; #配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持) gzip_disable “MSIE [1-6].”;}6.https配置http { # 配置共享会话缓存大小,视站点访问情况设定 ssl_session_cache shared:SSL:10m; # 配置会话超时时间 ssl_session_timeout 10m; server { listen 443; server_name www.example.com; ssl on; # 设置长连接 keepalive_timeout 70; # HSTS策略 add_header Strict-Transport-Security “max-age=31536000; includeSubDomains; preload” always; # 证书文件 ssl_certificate www.example.com.crt; # 私钥文件 ssl_certificate_key www.example.com.key; # 优先采取服务器算法 ssl_prefer_server_ciphers on; # 指定SSL协议 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 定义算法 ssl_ciphers “EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4”; # 减少点击劫持 add_header X-Frame-Options DENY; # 禁止服务器自动解析资源类型 add_header X-Content-Type-Options nosniff; # 防XSS攻擊 add_header X-Xss-Protection 1; }}四、Nginx作为反向代理服务器server { listen 80; server_name www.example.com; root html; index index.html; location /test { # 请求host proxy_set_header Host $http_host; # 请求ip proxy_set_header X-Real-IP $remote_addr; # 请求协议 proxy_set_header X-Scheme $scheme; # 代理服务器 proxy_pass http://localhost:3000; }}当访问http://www.example.com/test时, nginx会将请求转发到http://localhost:3000上。五、Nginx作为负载均衡1.负载均衡的介绍在服务器集群中,Nginx起到一个代理服务器的角色(即反向代理),为了避免单独一个服务器压力过大,将来自用户的请求转发给不同的服务器。负载均衡用于从 “upstream” 模块定义的后端服务器列表中选取一台服务器接受用户的请求。2.负载均衡的基本实例(1) upstream模块一个最基本的upstream模块如下:#动态服务器组, server是后端服务器,my_server是自定义的服务器组名称。upstream my_server { server localhost:8001; server localhost:8002; server localhost:8003;}(2) 反向代理在upstream模块配置完成后,要让指定的访问反向代理到服务器组。server { listen 80; server_name www.example.com; root html; index index.html; location / { # 反向代理到定义好的服务器组my_server proxy_pass my_server; }}(3)完整配置http { upstream my_server { server localhost:8001; server localhost:8002; server localhost:8003; } server { listen 80; server_name www.example.com; root html; index index.html; location / { # 反向代理到定义好的服务器组my_server proxy_pass my_server; } }}3. 负载均衡策略(1) 轮询(默认方式)表示每个请求按时间顺序逐一分配到不同的后端服务器。upstream my_server { server localhost:8001; server localhost:8002;}(2) weight(权重方式)表示在轮询策略的基础上指定轮询的服务器的权重,默认为1,权重越高分配到需要处理的请求越多。upstream my_server { server localhost:8001 weight=1; server localhost:8002 weight=2;}(3) ip_hash表示指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题。upstream my_server { ip_hash; server localhost:8001; server localhost:8002;}备注:在nginx版本1.3.1之前,不能在ip_hash中使用权重(weight)。ip_hash不能与backup同时使用。此策略适合有状态服务,比如session。当有服务器需要剔除,必须手动down掉。(4) least_conn表示把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。upstream my_server { least_conn; server localhost:8001; server localhost:8002;}(5) down表示当前的server暂时不参与负载均衡。upstream my_server { server localhost:8001 down; server localhost:8002; server localhost:8003;}(6) backup表示预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候,才会请求backup机器,因 此这台机器的压力最轻。upstream my_server { server localhost:8001 backup; server localhost:8002; server localhost:8003;} ...

April 13, 2019 · 3 min · jiezi

一天一点linux(19):配置https

Nginx配置https#腾讯云证书文档https://cloud.tencent.com/document/product/400/4143备注,手动编译安装需要安加入 SSL 模块(http_ssl_module)示例server { # 开启https端口 listen 443 ssl; # 填写绑定证书的域名 server_name xxx.com; # nginx的错误日志 access_log /var/log/nginx/sd-access.log; error_log /var/log/nginx/sd-error.log; # 默认网站根目录 root /data/releases/websites/sd/htdocs; # 将以下路由下放到前台 location ~ (^/taskm)|(^/perm)|(^/data) { try_files $uri /index.html; } location / { # 入口文件,注意这里有先后顺序 index index.html index.php index.htm; if (!-e $request_filename) { rewrite . /index.php last; } } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # https证书配置 # 开启ssl ssl on; # 证书文件 ssl_certificate /usr/local/nginx/conf/ssl/xxxx.crt; # 私钥文件 ssl_certificate_key /usr/local/nginx/conf/ssl/xxxx.key; # 配置会话超时时间 ssl_session_timeout 5m; # 配置共享会话缓存大小 ssl_session_cache shared:SSL:10m; # ssl_protocols 和 ssl_ciphers 用来限制连接只包含 SSL/TLS 的加強版本和算法 # 配置协议 ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 配置加密套件 ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; # 优先采取服务器算法 ssl_prefer_server_ciphers on; # Fastcgi服务器和程序(PHP,Python)沟通的协议. location ~ .php$ { # 设置监听端口 fastcgi_pass 127.0.0.1:9000; # 设置nginx的默认首页文件,上面index设置过 fastcgi_index index.php; # 设置脚本文件请求的路径 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; # 引入fastcgi的配置文件 include fastcgi_params; fastcgi_param SERVER_NAME $http_host; fastcgi_ignore_client_abort on; fastcgi_connect_timeout 600s; fastcgi_send_timeout 600s; fastcgi_read_timeout 600s; } # 对图片缓存时间设置 location ~ ..(gif|jpg|jpeg|png|bmp|swf|flv|ico)$ { expires 30d; access_log off; } # 对JS和CSS缓存时间设置 location ~ ..(js|css)?$ { expires 7d; access_log off; }}# 使用全站加密,HTTP 自动跳转 HTTPS, 注意配置这里上面就不能再监听80端口server { # 80端口访问 listen 80; # 配置访问域名 不包含协议 server_name xxx.com; # 使用url重写模块重写url 访问非https的url重定向到http上去 rewrite ^/(.*) https://$host/$1 permanent;} ...

April 12, 2019 · 1 min · jiezi

各大API网关性能比较

用于实现API网关的技术有很多,大致分为这么几类:通用反向代理:Nginx、Haproxy、……网络编程框架:Netty、Servlet、……API网关框架:Spring Cloud Gateway、Zuul、Zuul2、……API网关最基本的功能就是反向代理,所以在对API网关做技术选型的时候需要着重考察其性能表现,本文对Nginx、Haproxy、Netty、Spring Cloud Gateway、Zuul2做了性能测试,测试代码可以在github获得。测试方法准备了三台2CPU 4G内存的服务器,分别运行Tomcat、API Gateway、Gatling(压测工具)先对Tomcat做压测,取Tomcat充分预热后的压测结果作为基准。压的是Tomcat自带的example:/examples/jsp/jsp2/simpletag/book.jsp在对Netty、Zuul2、Spring Cloud Gateway做压测时候也是先压个几轮做预热。被测的API网关都没有添加额外业务,只做反向代理吞吐量下图是吞吐量的情况,可以看到Netty、Nginx、Haproxy均比直压Tomcat低一点点,而Spring Cloud Gateway和Zuul2则要低得多。下面这张图可以更明显的看到吞吐量比较,Tomcat为100%因为它是基准值,Netty、Nginx、Haproxy的只比基准值低8%,而Spring Cloud Gateway和Zuul2则只是基准值的35%和34%(难兄难弟)。平均响应时间下图可以看到Netty、Nginx、Haproxy的平均响应时间与Tomcat差不多。但是Spring Cloud Gateway和Zuul2则是Tomcat的3倍多,不出所料。下图同样是以Tomcat作为基准值的比较:响应时间分布光看平均响应时间是不够的,我们还得看P50、P90、P99、P99.9以及Max响应时间(可惜Gatling只能设置4个百分位,否则我还想看看P99.99的响应时间)。为何要观察P99.9的响应时间?光看P90不够吗?理由有两个:1)观察P99、P99.9、P99.99的响应时间可以观察系统的在高压情况下的稳定性,如果这三个时间的增长比较平滑那么说明该系统在高压力情况下比较稳定,如果这个曲线非常陡峭则说明不稳定。2)观察P99、P99.9、P99.99的响应时间能够帮助你估算用户体验。假设你有一个页面会发出5次请求,那么这5次请求均落在P90以内概率是多少?90%^5=59%,至少会经历一次 > P90响应时间的概率是 100%-59%=41%,如果你的P90=10s,那么就意味着用户有41%的概率会在加载页面的时候超过10s,是不是很惊人?如果你的P99=10s,那么用户只有5%的概率会在访问页面的时候超过10s。如果P99.9=10s,则有0.4%的概率。关于如何正确测量系统可以看 “How NOT to Measure Latency” by Gil Tene下面同样是把结果与Tomcat基准值做对比:可以看到几个很有趣的现象:Haproxy、Nginx的P50、P90、P99、P99.9、Max都是逐渐递增的。Netty的P50、P90、P99、P99.9是很平坦的,Max则为基准值的207%。Spring Cloud Gateway和Zuul2则是相反的,它们的平面呈现下降趋势。Spring Cloud Gateway的Max甚至还比基准值低了一点点(94%),我相信这只是一个随机出现的数字,不要太在意。结论Nginx、Haproxy、Netty三者的表现均很不错,其对于吞吐量和响应时间的性能损耗很低,可以忽略不计。但是目前最为火热的Spring Cloud Gateway和Zuul2则表现得比较糟糕,因我没有写额外的业务逻辑这,可以推测这和它们的内置逻辑有关,那么大致有这么几种可能:内置逻辑比较多内置逻辑算法存在问题,占用了太多CPU时间内置逻辑存在阻塞内置逻辑没有用正确姿势使用Netty(两者皆基于Netty)不管是上面的哪一种都需要再后续分析。不过话说回来考虑选用那种作为API网关(的基础技术)不光要看性能,还要看:是否易于扩展自己的业务逻辑API使用的便利性代码的可维护性文档是否齐全…性能只是我们手里的一个筹码,当我们知道这个东西性能到底几何后,才可以与上面的这些做交换(trade-off)。比如Nginx和Haproxy的可扩展性很差,那么我们可以使用Netty。如果你觉得Netty的API太底层了太难用了,那么可以考虑Spring Cloud Gateway或Zuul2。前提是你知道你会失去多少性能。

April 12, 2019 · 1 min · jiezi

不同品牌云服务器价格各异,如何更好的选择

不同品牌服务商的云服务器价格各有千秋,群英把影响服务器价格的因素概述如下几点:1.服务器的配置,配置越高,功能越强大,价格相对越高;2.服务器的宽带,宽带的大小直接影响网站的访问速度,访问量大的网站选择宽带要大一些,宽带还分独享和共享,独享宽带稳定性较好,共享是跟其他用户共享资源,速度有限制;3.机房的位置,不同的机房环境、软硬件设施、线路等都有一定的区别,对价格有一定影响;4.服务商的品牌,不同品牌口碑、信誉度和规模都有各异,一般来说,规模越大,运营时间越长,口碑越佳的服务商价格会高一点,因为他们所提供的服务器质量、售后服务更有保障;5.是否搞活动:部分信誉度较好的服务商为了提高新用户的购买率,会不定期举办促销活动,此时价格要比同行稍微低一点,但服务器质量还是不错的。因此,价格高的服务器可能更有保障,但价格低的并不代表服务器质量差,要根据实际情况来对比考虑。

April 11, 2019 · 1 min · jiezi

nginx配置多个tomcat负载均衡

nginx, tomcat的下载安装就不多说了nginx(1.15.x): http://nginx.org/en/download....tomcat(8.5.x): https://tomcat.apache.org/dow...1. 配置多个tomcat这里介绍是一台服务器配置多个tomcat,更改端口的方法。如果是多台服务器各配一个tomcat,那可以直接使用默认的配置。1.1 复制多个复制多个tomcat。假定下载解压后的tomcat为apache-tomcat-8.5.8,建议复制并更改名称为.apache-tomcat-8.5.8-8081apache-tomcat-8.5.8-8082apache-tomcat-8.5.8-80831.2 配置端口修改每个tomcat里面的配置文件。如apache-tomcat-8.5.8-8081/conf/server.xml,修改 3个地方 的端口号8443端口,若有需要再进行修改。apache-tomcat-8.5.8-8081<?xml version=“1.0” encoding=“UTF-8”?><!– 1 默认8005改成8006 –><Server port=“8006” shutdown=“SHUTDOWN”> <!– 2 Http默认8080 改成9081 –> <Connector port=“9081” protocol=“HTTP/1.1” connectionTimeout=“20000” redirectPort=“8443” /> <!– 3 AJP默认8009 改成8010 –> <Connector port=“8010” protocol=“AJP/1.3” redirectPort=“8443” /> apache-tomcat-8.5.8-8082<?xml version=“1.0” encoding=“UTF-8”?><!– 1 默认8005改成8007 –><Server port=“8007” shutdown=“SHUTDOWN”> <!– 2 Http默认8080 改成9082 –> <Connector port=“9082” protocol=“HTTP/1.1” connectionTimeout=“20000” redirectPort=“8443” /> <!– 3 AJP默认8009 改成8011 –> <Connector port=“8011” protocol=“AJP/1.3” redirectPort=“8443” /> apache-tomcat-8.5.8-8083<?xml version=“1.0” encoding=“UTF-8”?><!– 1 默认8005改成8008 –><Server port=“8008” shutdown=“SHUTDOWN”> <!– 2 Http默认8080 改成9083 –> <Connector port=“9083” protocol=“HTTP/1.1” connectionTimeout=“20000” redirectPort=“8443” /> <!– 3 AJP默认8009 改成8011 –> <Connector port=“8011” protocol=“AJP/1.3” redirectPort=“8443” /> 1.3运行3个tomcat# 进入tomcat目录cd /相对路径/apache-tomcat-8.5.8-8081# 运行tomcat/bin/startup.sh其他2个tomcat同理运行。运行成功后,可以访问浏览器2. 配置Nginx ...

April 11, 2019 · 1 min · jiezi

OpenResty下使用Apache Ant Path匹配库

OpenResty下使用Apache Ant Path匹配库一、简介 OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,而lua相对于编译型语言性能比较差,所以我们使用编写sharedobject库的方式集成到OpenResty项目中去。luajit使用ffi调用libcgoantpath.so来实现pattern匹配。 基于以上思路我们实现了一个符合Apache Ant Path标准的动态共享库,Git地址:go-antpath v1.1,为了方大家使用我们还封装了lua版本的lua-antpath v1.0.1,欢迎大家多多指导,共同进步。二、参考http://ant.apache.org/manual/api/org/apache/tools/ant/https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/PathMatcher.htmlgo-antpathlua-antpathhttps://github.com/golang/go/wiki/cgohttps://golang.org/cmd/cgo/https://groups.google.com/forum/#!topic/golang-nuts/Nb-nfVdAyF0三、编译及运行环境3.1 编译环境GNU Make 4.1 golang 1.9.2+3.2 运行环境luajit 2.1 antpath.go (执行make的时候自动下载)lua2go v1.0 (执行make的时候自动下载) cjson (OpenResty自带优良库)四、使用

April 10, 2019 · 1 min · jiezi

RPC架构之SOA服务化架构学习(一)

传统垂直应用架构背景:传统垂直MVC项目简单分为展示层.业务逻辑层.数据访问层缺点:如1.复杂应用的开发维护成本变高,部署效率逐渐降低 2.团队协作效率差,部分公共功能重复开发,代码重复率居高不下 3.系统可靠性变差。随着业务的发展,访问量逐渐攀升,网络流量、负载均衡、数据库连接等都面临着巨大的压力.走向:当垂直引用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。同时将公共能力API抽取出来,作为独立的公共服务供其他调用者消费,以实现服务的共享和重用,降低开发和运维成本。应用拆分之后会按照模块独立部署,接口调用由本地API演进成跨进程的远程方法调用,此时RPC框架应运而生具体可参考《分布式服务框架原理与实践》集群管理,负载均衡负载均衡有F5硬件负载均衡和软负载均衡.这里我简单讲下软负载均衡,nginx的反向代理服务很好的实现了集群管理,负载均衡.反向代理就是根据客户端的请求,从其关系的一组或多组后端服务器上获取资源,然后再将这些资源返回给客户端,客户端只会得知反向代理的IP地址,而不知道在代理服务器后面的服务器簇的存在.session失效: nginx默认算法是轮询服务器,这有一个问题session会失效解决办法1:upstream里设置ip_hash即采用哈希算法则可以解决这个问题,某个用户请求了A服务器,接下来该用户只会请求A服务器,除非A挂了,则会请求转入别的服务器,这时候还是会存在session失效的问题.解决办法2:session共享,比如2个tomcat来说,session共享需要发生网络通信也就是会建立连接,如果集群有多个,多个请求同时到每个不同tomcat,比如100个请求到100个不同tomcat,则会把100个的session共享到另外99个tomcat,则此时连接就100了,集群越多性能反而大大降低了.因此nginx自身session共享不建议,轮询算法中可通过别的方法,如redis共享session.初步学习分布式,理解较为浅,后续还会改动~~~

April 10, 2019 · 1 min · jiezi

Nginx修复漏洞打补丁过程

漏洞报告最近收到安全部门的安全扫描报告。内容如下:nginx 安全漏洞(CVE-2018-16845) 中危 nginx类 nginx是由俄罗斯的程序设计师Igor Sysoev所开发的一款轻量级Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。 Nginx 1.15.5及之前的版本和1.14.1版本中的ngx_http_mp4_module组件存在内存泄露漏洞,该漏洞源于程序没有正确处理MP4文件。远程攻击者可利用该漏洞获取敏感信息或造成拒绝服务。 厂商补丁: 目前厂商已发布升级补丁以修复漏洞,补丁获取链接: http://mailman.nginx.org/pipermail/nginx-announce/2018/000221.html一个高危漏洞,赶紧网上查询下资料这准备修复。修复过程去补丁地址获取补丁,可以看到这个内容:Patch for the issue can be found here:http://nginx.org/download/patch.2018.mp4.txt点击获取查看补丁信息:— src/http/modules/ngx_http_mp4_module.c+++ src/http/modules/ngx_http_mp4_module.c@@ -942,6 +942,13 @@ ngx_http_mp4_read_atom(ngx_http_mp4_file atom_size = ngx_mp4_get_64value(atom_header + 8); atom_header_size = sizeof(ngx_mp4_atom_header64_t); + if (atom_size < sizeof(ngx_mp4_atom_header64_t)) {+ ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,+ “"%s" mp4 atom is too small:%uL”,+ mp4->file.name.data, atom_size);+ return NGX_ERROR;+ }+ } else { ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0, “"%s" mp4 atom is too small:%uL”,第一行和第二行表示漏洞发生的文需要修改的文件第三行表示修复前的漏洞位置在942行的后6行,942,13为补丁添加的位置到第13行真正需要添加的部分为+号部分,复制定漏洞文件需要删除+号(+表示新增)接着去nginx的启动文件夹,查看编译参数信息:./nginx -V得到如下信息:nginx version: nginx/1.11.5 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) built with OpenSSL 1.0.1c 10 May 2012 TLS SNI support enabled configure arguments: –prefix=/app/nginx/nginx –with-pcre=/app/nginx/soft/pcre-8.35 –with-zlib=/app/nginx/soft/zlib-1.2.8 –with-openssl=/app/nginx/soft/openssl-1.0.1c –with-http_ssl_module –with-http_realip_module需要用到的内容为configure arguments:后的内容去nginx源码目录编译cd nginx-1.11.5 && ./configure –prefix=/app/nginx/nginx –with-pcre=/app/nginx/soft/pcre-8.35 –with-zlib=/app/nginx/soft/zlib-1.2.8 –with-openssl=/app/nginx/soft/openssl-1.0.1c –with-http_ssl_module –with-http_realip_module && make 注意:不要make install,不然会覆盖现有的等待编译成功后会生成一个objs目录,进入目录cd objs复制编译生成的可执行文件到原先的nginx的sbin目录cp nginx /app/nginx/nginx/sbin注意,复制前建议先备份原有的sbin文件切换进程:make upgrade或者去替换的sbin目录,./nginx -s reload ...

April 8, 2019 · 1 min · jiezi

Centos 7 配置Nginx-Aliyun ECS

Centos 7 配置Nginx,常用的nginx操作,启动/停止,代理,反向代理设置以及https ssl 443配置Linux查询nginx主进程号ps -ef | grep nginx启动/重启## 在nginx/sbin下执行命令 . (查看是否在 /usr/local/nginx/sbin)## 启动./nginx -c /usr/local/nginx/conf/nginx.conf## 重启./nginx -s reload停止## 从容停止Nginx:kill -QUIT 主进程号 ## 例如:kill -QUIT 16391## 快速停止Nginx:kill -TERM 主进程号 ## 强制停止Nginx:kill -9 主进程号 ## 停止nginxnginx -s stop代理/请求转发http { server { ### … listen 4000; server_name localhost; location / { root /Users/zhangguoye/Documents/Porject/Gitee/searchWX/src/main/internetapp; index index.html index.htm; } location /oauth/ { proxy_pass http://localhost:8080/oauth/; } location /api/ { proxy_pass http://localhost:8080/api/; } ### … }}443/SSL/未开启SSL模块安装模块切换到源码包:cd /usr/local/src/nginx-1.11.3查看nginx原有的模块/usr/local/nginx/sbin/nginx -V在configure arguments:后面显示的原有的configure参数如下:–prefix=/usr/local/nginx –with-http_stub_status_module那么我们的新配置信息就应该这样写:./configure –prefix=/usr/local/nginx –with-http_stub_status_module –with-http_ssl_module运行上面的命令即可,等配置完配置完成后,运行命令makes然后备份原有已安装好的nginxcp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak然后将刚刚编译好的nginx覆盖掉原有的nginx(这个时候nginx要停止状态)cp ./objs/nginx /usr/local/nginx/sbin/然后启动nginx,仍可以通过命令查看是否已经加入成功/usr/local/nginx/sbin/nginx -V配置Http和Https共存把ssl on;这行去掉,ssl写在443端口后面。这样http和https的链接都可以用server { listen 80 default backlog=2048; listen 443 ssl; server_name wosign.com; root /var/www/html; ssl_certificate /usr/local/Tengine/sslcrt/ wosign.com.crt; ssl_certificate_key /usr/local/Tengine/sslcrt/ wosign.com .Key; }配置SSL安全证书重启避免输入密码可以用私钥来做这件事。生成一个解密的key文件,替代原来key文件。openssl rsa -in server.key -out server.key.unsecureSSL性能调优ssl_protocols TLSv1 TLSv1.1 TLSv1.2;ssl_ciphers ECDHE-RSA-AES256-SHA384:AES256-SHA256:RC4:HIGH:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!AESGCM;ssl_prefer_server_ciphers on;ssl_session_cache shared:SSL:10m;ssl_session_timeout 10m;MAC (Brew Nginx)mac 使用homebrew安装Nginx,Nginx的位置与启动## 在mac上安装完nginx后的提示信息==> nginxDocroot is: /usr/local/var/wwwThe default port has been set in /usr/local/etc/nginx/nginx.conf to 8080 so thatnginx can run without sudo.nginx will load all files in /usr/local/etc/nginx/servers/.To have launchd start nginx now and restart at login: brew services start nginxOr, if you don’t want/need a background service you can just run: nginx## 查看nginx版本nginx -v## 启动nginx服务brew services start nginx## 关闭nginx服务brew services stop nginx## 重新加载nginxnginx -s reload## 停止nginxnginx -s stop ...

April 8, 2019 · 1 min · jiezi

Centos 7 安装Nginx-yum方式

前言Nginx (engine x) 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 服务器。。 本例演示 CentOS 7 下安装和配置 Nginx 的基本步骤。环境说明CentOS 7(Minimal Install)$ cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core)步骤步骤 1: 添加 yum 源Nginx 不在默认的 yum 源中,可以使用 epel 或者官网的 yum 源,本例使用官网的 yum 源。$ sudo rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm安装完 yum 源之后,可以查看一下。$ sudo yum repolistLoaded plugins: fastestmirror, langpacksLoading mirror speeds from cached hostfile * base: mirrors.aliyun.com * extras: mirrors.aliyun.com * updates: mirrors.aliyun.comrepo id repo name statusbase/7/x86_64 CentOS-7 - Base 9,911extras/7/x86_64 CentOS-7 - Extras 368nginx/x86_64 nginx repo 108updates/7/x86_64 CentOS-7 - Updates 1,041repolist: 11,428可以发现 nginx repo 已经安装到本机了。步骤 2: 安装yum 安装 Nginx,非常简单,一条命令。$ sudo yum install nginx步骤 3: 启动配置 Nginx 服务设置开机启动$ sudo systemctl enable nginx启动服务$ sudo systemctl start nginx停止服务$ sudo systemctl restart nginx重新加载,因为一般重新配置之后,不希望重启服务,这时可以使用重新加载。$ sudo systemctl reload nginx配置### nginx默认配置位置/etc/nginx/conf.d/default.conf## 默认wwwroot位置/usr/share/nginx/html步骤 4: 打开防火墙端口默认 CentOS7 使用的防火墙 firewalld 是关闭 http 服务的(打开 80 端口)。$ sudo firewall-cmd –zone=public –permanent –add-service=httpsuccess$ sudo firewall-cmd –reloadsuccess打开之后,可以查看一下防火墙打开的所有的服务$ sudo sudo firewall-cmd –list-servicessh dhcpv6-client http可以看到,系统已经打开了 http 服务。步骤 5: 反向代理Nginx 是一个很方便的反向代理,配置反向代理可以参考 Module ngx_http_proxy_module 。本文不做累述。需要指出的是 CentOS 7 的 SELinux,使用反向代理需要打开网络访问权限。$ sudo setsebool httpd_can_network_connect 1打开网络权限之后,反向代理可以使用了。结论本文演示了 CentOS 7 下 yum 安装 Nginx,配置服务等。参考资料Install Nginx Binary ReleasesModule ngx_http_proxy_module ...

April 8, 2019 · 1 min · jiezi

Nginx 外的另一选择,轻量级开源 Web 服务器 Tengine 发布新版本

新版发布近日,轻量级开源 Web 服务器 Tengine 发布了2.3.0版本,新增如下特性:ngx_http_proxy_connect_module,该模块让 Tengine 可以用于正向代理场景,支持对 CONNECT 方法请求的处理;HTTP2 Server粒度控制 新增 HTTP2指令,可针对 listen 相同端口的 server 进行个性化开启与关闭 HTTP2;Stream模块支持 server_name 指令,可在 SSL 场景下,基于 SNI 识别出域名,让四层SSL 转发支持特定的 server 块配置;加强 limit_req 模块功能,可以基于请求粒度动态设置限速大小,更多详细变更日志请参考limit_req 变更日志;注意事项需要注意的是,本次 Tengine 升级 core 代码至 Nginx 官方的1.15.9版本(2019年2月26日发布),由于 Tengine 的部分功能 Nginx 官方已经实现,所以 Tengine 2.3.0 弃用了自身实现的部分配置指令,由此带来的不兼容性,列举如下:废弃 Tengine 自身实现的 reuse_port 指令,使用 Nginx官方 的reuseport。升级方法:将events 配置块里面的 reuse_port on|off 注释掉,在对应的监听端口后面加 reuseport 参数,详细的操作文档,请参考limit_req 变更日志 。废弃 Tengine 的 dso_tool 工具以及 dso 配置指令。若之前有使用 Tengine 的 dso 功能,则可以切换到 Nginx官方 的 load_module 指令,详细操作文档,请参考Nginx 官方文档1和Nginx 官方文档2。移除 Tengine 加强版 slice 模块到 modules,默认使用 Nginx 官方的 slice 功能。如果依然需要使用 Tengine 的 slice,那么编译slice时请使用–add-module=modules/ngx_http_slice_module,否则使用 –with-http_slice_module 编译参数;Tengine 自身实现的模块,已全部剥离到 modules 目录下。如果需要使用那个模块,请使用 –add-module=modules/ 的方式进行编译。limit_req 的请求计数逻辑和官方保持一致,去除 limit_req_zone 中任何一个变量值为空,跳过请求计数的逻辑。关于 TengineTengine 是基于 Nginx 开发的轻量级开源 Web 服务器,作为阿里巴巴七层流量入口的核心系统,支撑着阿里巴巴双11等大促活动的平稳度过,并提供了智能的流量转发策略、HTTPS 加速、安全防攻击、链路追踪等众多高级特性,同时秉着软硬件结合的的性能优化思路,在高性能、高并发方面取得了重大突破。自开源以来,Tengine 已获得来自67位 contributors 的1390个 commits,他们分别来自淘宝、搜狗,美团、Nginx 等企业。据不完全统计,目前已有 200多家企业在通过 Tengine 来实现 Web 服务、负载均衡、代理服务、防攻击和访问限制等功能,包括傲世堂、小米网、聚美优品、河狸家、旺旺集团、杭州思华、中国博客联盟、SuperID、联想网盘、华兴资本、猿题库、蓝奏网盘、HoukeYun、云智慧等。目前,Tengine 正通过 Ingress Controller 和 K8s 打通,这让 Tengine 具备了动态感知某个服务整个生命周期的能力。未来,Tengine 将定期开源内部通用组件功能模块,并同步 Nginx 官方的最新代码,丰富开发者们的开源 Web 服务器选项。本文作者:王发康(花名:毅松) GitHub ID @wangfakang ,Tengine 开源项目 maintainer,阿里巴巴技术专家,负责阿里巴巴WEB统一接入层的开发及维护。本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 4, 2019 · 1 min · jiezi

Docker安装与应用

一、docker安装1、快捷安装快捷安装参考:https://get.daocloud.io/#inst…curl -sSL https://get.daocloud.io/docker | sh2、手动安装1.)先查看内核,更新yum包docker要求CentOS系统的内核版本>3.10$ uname -r$ sudo yum update2.)安装依赖包$ sudo yum install -y yum-utils device-mapper-persistent-data lvm23.)设置国内docker镜像源$ sudo yum-config-manager –add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo4.)如果安装过,卸载旧版本$ sudo yum remove docker docker-common docker-selinux docker-engine5.)查看仓库中所有docker,安装$ yum list docker-ce –showduplicates | sort -r$ sudo yum install docker-ce$ docker -v # 测试3、启动/关闭docker服务**$ sudo systemctl start docker$ sudo systemctl stop docker# 加入随机启动$ sudo systemctl enable docker二、docker常用命令每次使用docker命令都需要sudo,比较麻烦,可以通过以下命令添加当前用户到docker附属组:$ sudo usermod -aG docker 当前用户名 # 需要注销后登录生效1.)查看容器/镜像$ docker ps # 查看本地容器$ docker images # 查看本地镜像# 查看容器详情$ docker inspect xxx# 查看容器top进程$ docker top xxx# 查找远程镜像$ docker search xxx2.)新建容器最少参数的创建# -d:表示后台运行,-it:表示以交互的方式创建,可视化时可通过console打开$ docker run -d -it –name 容器名 –restart always 镜像名更多参数:映射端口、dns、持久存储卷、初始化进程防容器退出# -p:映射端口,–dns:有些容器默认dns解析服务配置不对(/etc/resolv.conf),-v:映射路径(可重复多个),/bin/sh:保持一个进程运行,否则容器会退出$ docker run -dit -p 宿主端口:容器端口 –dns=8.8.8.8 –name 容器名 -v 宿主路径:容器路径 –restart always 镜像:标签 /bin/sh3.)启动/停止容器$ docker start | restart xxx # 启动/重启$ docker stop xxx # 停止容器$ docker kill xxx # 强行终止,关闭进程4.)进入容器# 多窗口同时进入,会同步显示,容易窗口阻塞,适合本地开发$ docker attach xxx# 或者,docker在1.3.X版本,新命令$ docker exec -it xxx /bin/bash5.)删除容器/镜像# 删除容器前,先停止$ docker stop xxx $ docker rm xxx# 删除镜像$ docker rmi xxx # -f 强制删除6.)镜像改名$ docker tag 原镜像 新镜像 # 会生成一个新名,镜像id一样$ docker rmi 原镜像三、镜像构建1、手动构建镜像用基础镜像创建一个容器,手动安装好一切,然后用容器生成镜像:$ docker commit 容器名 新镜像名2、自动构建镜像:Dockerfile1.)Dockerfile配置新建Dockerfile并配置相关内容,下面以配置一个基于alpine的pm2安装并运行node应用为例:# 基础镜像源FROM alpine# 创建者信息MAINTAINER hoby <w.hoby@qq.com># RUN命令:构建过程中执行,常用于安装软件包RUN echo ’nameserver 8.8.8.8’ >> /etc/resolv.conf \ && apk update \ && apk add bash \ && apk add nodejs && apk add npm \ && npm config set registry https://registry.npm.taobao.org \ && npm i -g pm2# 指定工作目录,用绝对WORKDIR /app# 从宿主机copy到容器#COPY ./www.js /app# 与COPY类似,但ADD自带解压功能#ADD ./x.tar.xz /app# 定义环境变量ENV NODE_ENV=production# 配置entrypoint入口脚本RUN echo ‘console.log(“this is node web!”)’ > ./www.js \ && echo ‘#!/bin/bash’ > ./entrypoint.sh \ && echo ‘pm2 start /app/www.js’ >> ./entrypoint.sh \ && echo ‘/bin/sh’ >> ./entrypoint.sh \ && chmod a+x ./entrypoint.sh# 容器启动后执行的命令,且不可被docker run提供的参数覆盖ENTRYPOINT ["/bin/sh", “./entrypoint.sh”]# 容器启动后默认执行的命令,可被docker run后面的参数代替#CMD ["/bin/sh"]# 暴露端口EXPOSE 80运行构建命令:$ docker build -t myimage:latest . # 镜像名需小写# 新建并启动容器$ docker run -dit -p 8000:80 –dns=8.8.8.8 –name 容器名 –restart always myimage:latest2.)Dockerfile构建总结a. 构建时下载不了软件包,说明容器dns不对,需修改/etc/resolv.conf b. alpine镜像默认sh终端,需安装bash c. 建议一个容器只运行单个应用,多个应用见下文compose部署 d. Docker镜像构建是分层,将多个RUN指令合并 e. -v持久化路径时,若宿主机路径是新建的,容器路径内容会被清空 f. 当ENTRYPOINT与CMD使用exec参数时需双引号g. 添加.dockerignore,提高编译速度:.git/node_modules/四、多个应用容器部署Docker Compose是一个管理多容器应用的工具1、docker-compose安装Compose下载地址: https://get.daocloud.io/#inst...curl -L https://get.daocloud.io/docker/compose/releases/download/1.24.0/docker-compose-`uname -s-uname -m` > /usr/local/bin/docker-composechmod +x /usr/local/bin/docker-compose2、docker-compose文件配置新建docker-compose.yml,下面以nginx单容器配置为例:version: “3"services: web: image: nginx volumes: - ./nginx.conf:/etc/nginx/conf.d/actor.conf - ./actor:/app/actor container_name: actor ports: - “18000:8000” command: [“nginx”,"-g”,“daemon off;”] redis: image: “redis:alpine"在docker-compose.yml同目录下,创建启动/更新容器:$ docker-compose up -d # -d后台运行3、docker-compose常用命令在docker-compose.yml同一目录下# 创建并启动容器$ docker-compose up -d# 重启容器$ docker-compose restart# 查看yml配置$ docker-compose config# 停止容器$ docker-compose stop# 停止并移除容器$ docker-compose down五、可视化容器管理工具Portainer是一个轻量级的Docker环境UI界面管理系统1、快速部署$ docker volume create portainer_data # 在宿主机创建持久化目录$ docker run -d -p 9000:9000 –name portainer –restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer2、相关问题1.) 可视化创建容器时,Advanced container settings -> Console 记得勾选 Interactive & TTY (-i -t),否则无法使用控制台2.) portainer中volume默认在/var/lib/docker/volumes下,貌似不能自定义目录,可通过手动命令行创建时-v指定 ...

April 3, 2019 · 2 min · jiezi

PHP面试知识点梳理

思维导图地址:https://www.processon.com/vie…前段时间复习的时候总结的,在此分享给大家。

April 3, 2019 · 1 min · jiezi

Laravel 路由研究之domain 解决多域名问题

材料准备一份干净的laravel两份Nginx配置文件,主要配置如下:server_name *.amor_laravel_test_1.amor;root /var/www/amor_laravel_test/public;index index.php index.html index.htm;server_name *.amor_laravel_test.amor;root /var/www/amor_laravel_test/public;index index.php index.html index.htm;将域名分割为参数Route::domain(’{account}.{webname}.{suffix}’)->group(function () { Route::get(‘user/{id}’, function ($account, $webname, $suffix, $id) { // 可以在请求中接收到被分割的参数,可能的使用场景:在单独路由中需要根据不同的域名处理不同的需求 dd($account, $webname, $suffix, $id); });});注意: 若account不固定,可以将Nginx Server Name 配置为泛型: *.example.com关于多域名配置两个不同的域名如下:server_name *.amor_laravel_test.amor;server_name *.amor_laravel_test_1.amor;如何让Laravel匹配不同的域名?方式1:直接在 route/web.php中使用domain区分Route::domain(’{account}.amor_laravel_test.amor’)->group(function () { Route::get(‘user/{id}’, function ($account, $id) { // dd($account, $id); });});Route::domain(’{account}.amor_laravel_test_1.amor’)->group(function () { Route::get(‘user/{id}’, function ($account, $id) { // dd(111, $account, $id); });});方式2:通过设置 RouteServiceProvider 区分添加方法: protected function mapSelfRoutes() { Route::domain(’{account}.amor_laravel_test_1.amor’) ->middleware(‘web’) ->namespace($this->namespace) ->group(base_path(‘routes/self.php’)); }注册 public function map() { $this->mapApiRoutes(); $this->mapWebRoutes(); $this->mapSelfRoutes(); // }添加路由文件Route::get(’/user’, function ($account) { dd($account);});注意: 必须全部设置domain,如果只设置了self 那么在相同请求路径下,未设置domain的将会首先匹配到。总结:推荐第二种方式来区分域名,优点在于路由分离 ,结构清晰,domain不仅仅可以作为区分子域名来使用,也可以做参数分割,不同域名区分等注意Laravel的路由匹配顺序,希望大家能认真的做一遍,体验一下,做到心中有数既然已经区分开域名,那么就可以绑定到不同的控制器,或者绑定不同的模型,大家灵活应用 ...

April 2, 2019 · 1 min · jiezi

基于docker, 快速搭建Nginx+Php+https本地开发环境, 免于手动安装PHP扩展

NginxPhpDocker是什么, 主要解决什么问题基于docker, 快速搭建Nginx+Php本地开发环境(已含常用PHP扩展), nginx、php配置文件,日志文件和php工程代码都在宿主机上, 方便修改.可以解决:新人加入团队, 配置LNMP麻烦, 而且是重复劳动prod, staging, local开发环境不一致, local没问题, 上了staging,prod出现各种问题php扩展安装有问题, 比如: mac电脑, 本地多个php版本, 扩展不一致(eg: memcache, memcached)项目新功能需要安装新扩展, 所有开发者都要安装一遍NginxPhpDocker github url1. 如何使用呢?1.1 download codegit clone git@github.com:weiwenwang/NginxPhpDocker.gitcd NginxPhpDocker1.2 启动php容器docker run -it -d --name myphp -v $PWD/www/php:/www/php -v $PWD/www/example:/www/example --privileged=true \wangnan188/nginx-php-docker:v7.2-v11.3 启动nginx容器docker run -it -d -p 80:80 -p 443:443 -v $PWD/nginx-conf/conf.d:/etc/nginx/conf.d -v $PWD/nginx-conf/nginx.conf:/etc/nginx/nginx.conf -v $PWD/www/html:/www/html -v $PWD/www/example:/www/example -v $PWD/ssl/server.crt:/etc/nginx/ssl/server.crt -v $PWD/ssl/server.key:/etc/nginx/ssl/server.key -v $PWD/log/nginx:/var/log/nginx/ --link=myphp:myphp_alias --privileged=true --name=mynginx nginx1.4 注意事项, 非常重要1.2, 1.3的两个指令必须在NginxPhpDocker目录下执行PHP代码的文件夹, 必须挂在到PHP容器里面, 有小伙伴使用的时候挂到nginx容器里面了, nginx和PHP俩容器是隔离的, php只会按地址在他们自己的容器里面找文件, 和nginx只是通过fastcgi通信, nginx告诉php用户请求的文件地址, php在自己的容器去找对应的文件1.5 执行1.2、1.3之后效果是什么样子的呢?3. 如何把现有的项目跑起来呢?这里我举例个例子, 假如我们现在的项目(thinkphp_3.2.3_full)就是thinkphp框架写的, 我如何把它运行起来呢? 第一步: 把代码放在www/example/目录下 第二步: 添加配置文件nginx-conf/conf.d/example-thinkphp.conf, 剩下的就是单纯的nginx配置问题了. 本地做一个host绑定: “127.0.0.1 thinkphp-full.com” 浏览器访问: http://thinkphp-full.com/inde...4. wangnan188/nginx-php-docker现在包含了哪些extension呢?extensionstatusremark-extensionstatusremark- CoreYES–redisYES–ctypeYES–gdYES–curlYES–xdebugYES–dateYES–mongodbYES–domYES–swooleYES–fileinfoYES–memcachedYES–filterYES–memcacheNO–ftpYES– hashYES– iconvYES– jsonYES– libxmlYES– mbstringYES– mysqlndYES– opensslYES– pcreYES– PDOYES– pdo_sqliteYES– PharYES– posixYES– readlineYES– ReflectionYES– sessionYES– SimpleXMLYES– sodiumYES– SPLYES– sqlite3YES– standardYES– tokenizerYES– xmlYES– xmlreaderYES– xmlwriterYES– zlibYES– 5. 其他后续php extensions有补充, 可能不能及时更新此文章, 最新版本请移步: github ...

April 1, 2019 · 1 min · jiezi

Django后台 + Wordpress主题快速搭建个人博客

既然学习了Python Web怎么能没有自己的一个小站呢?没有自己精心打造的一个小站怎么敢说自己学习过 Python Web呢?说的再多不如直接干,我的个人网站也已经部署上线。Django后台 + Wordpress主题,只要自己看上的主题都可以让它变成自己的为什么要选择 Wordpress 主题呢?自己在刚开始学习Python Web时最大的困惑就是:Django后台开发没什么难度,但是想搭起自己的一个站点却难的让我无从下手,什么 HTML、CSS、Jquery、JS、AJAX的前端知识太多,听起来就头大,即使学会了前端技术,你能写出一个自己满意的前端页面吗?没有一点审美和设计能力,好像并不大行。当我遇到 Wordpress 时,这一切都变得如此简单,Wordpress社区有丰富的主题,可以挑出自己喜欢的随意摆弄,只需能看懂前端代码即可,加上 Django 类似API式的视图和前端模板语法,简直完美结合。当初学习 Django 一心想搭起一个自己满意的小站,但始终未完成心愿,了解到 Wordpress 后让我打开了新思路,我的个人网站,是 Django1.11 + 崔庆才个人博客 Wordpress 欲思主题搭建:https://www.stormsha.com/初学Python web时对前端不熟练是其实是最难的点,为了让更多的人少走弯路,自己写了一下个人博客搭建的教程,这个教程我希望靠每个浏览过的人都能给出更多建议把它完善,所以希望更多的人看到,提出意见,帮助更多的人

April 1, 2019 · 1 min · jiezi

nginx: [emerg] unknown connection_upgrade variable解决与思考

问题一天更新完主分支后启动nginx,结果报错:nginx: [emerg] unknown “connection_upgrade” variable解决方法网上查,发现是nginx配置文件出了问题,将下面map代码块补上即可。 http { map $http_upgrade $connection_upgrade { default upgrade; ’’ close; } server { location / { #… proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; } }}思考虽然问题解决了,但后来我想知道为什么会出现这种情况,以及解决方法的真正原理。在这篇博客可以知道问题出在了nginx代理websocket上。什么是websocket传统的http通讯模式是:客户端发起请求,服务端接收请求并作出响应。而websocket协议复用了http的握手通道,具体指的是,客户端通过HTTP请求与WebSocket服务端协商升级协议。第一步,建立连接,客户端使用http报文的格式发起协议升级的请求,服务端响应协议升级。第二步,交换数据,客户端与服务端可以使用websocket协议进行双向通讯。nginx反向代理websocket首先,客户端发起协议升级的请求,而nginx在拦截时需要识别出这是一个协议升级(upgrade)的请求,所以必须显式设置升级(Upgrade head)和连接头(Connection head),如下: location /sockjs-node/ { proxy_pass http://127.0.0.1:4200/sockjs-node/; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade;}完成后,nginx将其作为WebSocket连接处理。接着,服务器响应升级请求,由nginx做代理进行处理,这时,需要进行如下配置:http { map $http_upgrade $connection_upgrade { default upgrade; ’’ close; } server { … }}这时就出现了一开始我所修改的地方,结合上面那段的内容,我大概可以猜出来map 代码段该作用主要是根据客户端请求中 $http_upgrade 的值,来构造改变 $connection_upgrade 的值,即根据变量 $http_upgrade 的值创建新的变量 $connection_upgrade。由于我没有进行map映射,它不知道connection_upgrade是什么,所以就会出现unknown “connection_upgrade” variable错误。总结即使是小小的一点改动,背后也会隐藏庞大的信息。如果止步于解决问题,而不是探索问题,就永远不会有进步。本人水平有限,欢迎各位在评论区指出不足,你们的反馈就是我的进步动力!参考文档:https://www.nginx.com/blog/we…https://www.cnblogs.com/chyin…

March 30, 2019 · 1 min · jiezi

文件服务器的两种实现方式

简介 在日常的工作项目中,我们常常会需要用到文件服务器,即在网页端就能访问到本地的电脑中某个文件夹下的所有文件,示例界面如下: 本文将介绍笔者接触到的两种实现文件服务器的方法,它们所使用的工具如下:NginxPython本文以Linux系统(Ubuntu系统)为例,具体介绍如何实现文件服务器。Nginx实现文件服务器Nginx的简介与安装 Nginx是一个高性能的HTTP和反向代理服务,也是一个IMAP/POP3/SMTP服务。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点开发的,第一个公开版本0.1.0发布于2004年10月4日。 Ubuntu系统安装Nginx的命令如下:sudo apt install nginx配置文件 安装Nginx之后,切换至/etc/nginx/conf.d文件夹,创建default.conf文件,内容如下:autoindex on; # 显示目录autoindex_exact_size on; # 显示文件大小autoindex_localtime on; # 显示文件时间charset utf-8,gbk; # 字符集server { listen 8080 default_server; listen [::]:8080 default_server; server_name _; #root /usr/share/nginx/html; root /home/vagrant; #需要展示的目录}启动服务 如需启动文件服务,只需运行以下命令即可:sudo nginx这样在浏览器中输入localhost:8080,就会出现示例界面。Python实现文件服务器 如果使用Python实现文件服务器,那么命令就会简单很多,只需一行命令即可:python -m SimpleHTTPServer 8080这样在浏览器中输入localhost:8080,就会出现示例界面。总结 也许还有更多实现文件服务器的办法,希望大家能多多指教~注意:本人现已开通微信公众号: Python爬虫与算法(微信号为:easy_web_scrape), 欢迎大家关注哦~~

March 30, 2019 · 1 min · jiezi

nginx 修改 max open files limits

注意:修改 nginx 的 max open files 有个前提,就是你已经修改好了系统的 max open files.先查看 nginx 的 ulimits: grep ‘open files’ /proc/$( cat /var/run/nginx.pid )/limits修改 nginx.servicesudo vi /lib/systemd/system/nginx.service # (仅适用于 ubuntu)添加:[Service]LimitNOFILE=100000重启服务:sudo systemctl daemon-reload修改 nginx.conf,添加:worker_rlimit_nofile 90000; # (has to be smaller or equal to LimitNOFILE set above)重启 nginx:sudo systemctl restart nginx上面是网上流传的教程,但是还是不够,你这样改了之后,nginx 的并发能力反而会下降,所以还需要改一个关键的参数:修改 nginx.conf添加:events { worker_connections 90000;}重启 nginx:sudo systemctl restart nginx

March 29, 2019 · 1 min · jiezi

一篇读懂分布式架构下的负载均衡

一篇读懂分布式架构下的负载均衡微信公众号:IT一刻钟大型现实非严肃主义现场一刻钟与你分享优质技术架构与见闻,做一个有剧情的程序员关注可第一时间了解更多精彩内容,定期有福利相送哟。什么是负载均衡?百度词条里的解释是:负载均衡,英文叫Load Balance,意思就是将请求或者数据分摊到多个操作单元上进行执行,共同完成工作任务。它的目的就通过调度集群,达到最佳化资源使用,最大化吞吐率,最小化响应时间,避免单点过载的问题。负载均衡分类负载均衡可以根据网络协议的层数进行分类,我们这里以ISO模型为准,从下到上分为:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。当客户端发起请求,会经过层层的封装,发给服务器,服务器收到请求后经过层层的解析,获取到对应的内容。二层负载均衡二层负债均衡是基于数据链路层的负债均衡,即让负债均衡服务器和业务服务器绑定同一个虚拟IP(即VIP),客户端直接通过这个VIP进行请求,那么如何区分相同IP下的不同机器呢?没错,通过MAC物理地址,每台机器的MAC物理地址都不一样,当负载均衡服务器接收到请求之后,通过改写HTTP报文中以太网首部的MAC地址,按照某种算法将请求转发到目标机器上,实现负载均衡。这种方式负载方式虽然控制粒度比较粗,但是优点是负载均衡服务器的压力会比较小,负载均衡服务器只负责请求的进入,不负责请求的响应(响应是有后端业务服务器直接响应给客户端),吞吐量会比较高。三层负载均衡三层负载均衡是基于网络层的负载均衡,通俗的说就是按照不同机器不同IP地址进行转发请求到不同的机器上。这种方式虽然比二层负载多了一层,但从控制的颗粒度上看,并没有比二层负载均衡更有优势,并且,由于请求的进出都要经过负载均衡服务器,会对其造成比较大的压力,性能也比二层负载均衡要差。四层负载均衡四层负载均衡是基于传输层的负载均衡,传输层的代表协议就是TCP/UDP协议,除了包含IP之外,还有区分了端口号,通俗的说就是基于IP+端口号进行请求的转发。相对于上面两种,控制力度缩小到了端口,可以针对同一机器上的不用服务进行负载。这一层以LVS为代表。七层负载均衡七层负载均衡是基于应用层的负载均衡,应用层的代表协议有HTTP,DNS等,可以根据请求的url进行转发负载,比起四层负载,会更加的灵活,所控制到的粒度也是最细的,使得整个网络更"智能化"。例如访问一个网站的用户流量,可以通过七层的方式,将对图片类的请求转发到特定的图片服务器并可以使用缓存技术;将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术。可以说功能是非常强大的负载。这一层以Nginx为代表。在普通的应用架构中,使用Nginx完全可以满足需求,对于一些大型应用,一般会采用DNS+LVS+Nginx的方式进行多层次负债均衡,以上这些说明都是基于软件层面的负载均衡,在一些超大型的应用中,还会在前面多加一层物理负载均衡,比如知名的F5。负载均衡算法负载均衡算法分为两类:一种是静态负载均衡,一种是动态负载均衡。静态均衡算法:1、轮询法将请求按顺序轮流地分配到每个节点上,不关心每个节点实际的连接数和当前的系统负载。优点:简单高效,易于水平扩展,每个节点满足字面意义上的均衡;缺点:没有考虑机器的性能问题,根据木桶最短木板理论,集群性能瓶颈更多的会受性能差的服务器影响。2、随机法将请求随机分配到各个节点。由概率统计理论得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配,也就是轮询的结果。优缺点和轮询相似。3、源地址哈希法源地址哈希的思想是根据客户端的IP地址,通过哈希函数计算得到一个数值,用该数值对服务器节点数进行取模,得到的结果便是要访问节点序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会落到到同一台服务器进行访问。优点:相同的IP每次落在同一个节点,可以人为干预客户端请求方向,例如灰度发布;缺点:如果某个节点出现故障,会导致这个节点上的客户端无法使用,无法保证高可用。当某一用户成为热点用户,那么会有巨大的流量涌向这个节点,导致冷热分布不均衡,无法有效利用起集群的性能。所以当热点事件出现时,一般会将源地址哈希法切换成轮询法。4、加权轮询法不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。加权轮询算法要生成一个服务器序列,该序列中包含n个服务器。n是所有服务器的权重之和。在该序列中,每个服务器的出现的次数,等于其权重值。并且,生成的序列中,服务器的分布应该尽可能的均匀。比如序列{a, a, a, a, a, b, c}中,前五个请求都会分配给服务器a,这就是一种不均匀的分配方法,更好的序列应该是:{a, a, b, a, c, a, a}。优点:可以将不同机器的性能问题纳入到考量范围,集群性能最优最大化;缺点:生产环境复杂多变,服务器抗压能力也无法精确估算,静态算法导致无法实时动态调整节点权重,只能粗糙优化。5、加权随机法与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。6、键值范围法根据键的范围进行负债,比如0到10万的用户请求走第一个节点服务器,10万到20万的用户请求走第二个节点服务器……以此类推。优点:容易水平扩展,随着用户量增加,可以增加节点而不影响旧数据;缺点:容易负债不均衡,比如新注册的用户活跃度高,旧用户活跃度低,那么压力就全在新增的服务节点上,旧服务节点性能浪费。而且也容易单点故障,无法满足高可用。(注:以上所提到的单点故障,都可以用主从方式来解决,从节点监听主节点心跳,当发现主节点死亡,从节点切换成主节点顶替上去。这里可以思考一个问题,怎么设计集群主从可以最大程度上降低成本)动态负债均衡算法:1、最小连接数法根据每个节点当前的连接情况,动态地选取其中当前积压连接数最少的一个节点处理当前请求,尽可能地提高后端服务的利用效率,将请求合理地分流到每一台服务器。俗称闲的人不能闲着,大家一起动起来。优点:动态,根据节点状况实时变化;缺点:提高了复杂度,每次连接断开需要进行计数;实现:将连接数的倒数当权重值。2、最快响应速度法根据请求的响应时间,来动态调整每个节点的权重,将响应速度快的服务节点分配更多的请求,响应速度慢的服务节点分配更少的请求,俗称能者多劳,扶贫救弱。优点:动态,实时变化,控制的粒度更细,跟灵敏;缺点:复杂度更高,每次需要计算请求的响应速度;实现:可以根据响应时间进行打分,计算权重。3、观察模式法观察者模式是综合了最小连接数和最快响应度,同时考量这两个指标数,进行一个权重的分配。说在后面话还有哪些负载均衡的算法,或者有更好的想法或问题,欢迎留言交流!

March 29, 2019 · 1 min · jiezi

我眼中的 Nginx(五):Nginx — 子请求设计之道

张超:又拍云系统开发高级工程师,负责又拍云 CDN 平台相关组件的更新及维护。Github ID: tokers,活跃于 OpenResty 社区和 Nginx 邮件列表等开源社区,专注于服务端技术的研究;曾为 ngx_lua 贡献源码,在 Nginx、ngx_lua、CDN 性能优化、日志优化方面有较为深入的研究。子请求、父请求和主请求Nginx 所处理的大部分请求,都是在接收到客户端发来的 HTTP 请求报文后创建的,这些请求直接与客户端打交道,称之为主请求;与之相对的则是子请求,顾名思义,子请求是由另外的请求创建的,比如主请求(当然子请求本身也可以创建子请求),当一个请求创建一个子请求后,它就成了该子请求的父请求。从源码层面来说,当前请求的主请求通过 r->main 指针获取,父请求则通过 r->parent 指针获取。使用子请求机制的意义在于,它能够分散原本集中在单个请求里的处理逻辑,简化任务,大大降低请求的复杂度。例如当既需要访问一个 MySQL 集群,又需要访问一个 Redis 集群时,我们就可以分别创建一个子请求负责和 MySQL 的交互,另外一个负责和 Redis 的交互,简化主请求的业务复杂度。而且创建子请求的过程不涉及任何的网络 I/O,仅仅是一些内存的分配,其代价非常可控,因此在笔者看来,子请求机制是 Nginx 里最为巧妙的设计之一。子请求创建与驱动通常需要创建子请求时,模块开发者们可以调用函数 ngx_http_subrequest 来实现,默认情况下,子请求会共享父请求的内存池,变量缓存,下游连接和 HTTP 请求头等数据。当子请求创建完毕后,它会被挂到 r->main->posted_requests 链表上,这个链表用以保存需要延迟处理的请求(不局限于子请求)。因此子请求会在父请求本地调度完毕后得到运行的机会,这通常是子请求获得首次运行机会的手段。我们知道 Nginx 针对一个 HTTP 请求,将其处理逻辑分别划分到了 11 个不同的阶段。当一个子请求被创建出来后,它首先运行的是 find config 阶段,即寻找一个合适的 location,然后开始后续的逻辑处理。通常,如果一个子请求不涉及任何的网络 I/O 操作,或者定时器处理,一次调度即可完成当前的子请求;而如果子请求需要处理一些网络、定时器事件,那么后续该子请求的调度,都会由这些事件来驱动,这使得它的调度和普通的主请求变得无差别。既然除第一次外,子请求的驱动可能是由网络事件来驱动的,那么子请求的调度就是乱序的了。假设当前主请求需要向后端请求一个大小 2MB 的资源,我们通过产生两个子请求,分别获取 0-1MB 和 1MB - 2MB 的部分,然后发往下游,因为网络的不确定性,很有可能后者(1MB - 2MB)先获取到并往下游传输。那么此时下游所得到的数据就成了脏数据了。为了解决这个问题,Nginx 为子请求机制引入了另外一个称为 postpone_filter 的模块。该模块的目的在于,判断当前准备发送数据的请求,是否是“活跃的”,如果当前请求不是“活跃”的,则它期望发送的数据会被暂时保存起来,直到某一刻它“活跃”了,才能将这些数据发往下游。怎么判断一个请求是否是“活跃”的?我们需要先了解父、子请求之间的保存形式。对于当前请求,它的子请求以链表的方式被维护起来,而前面提到,子请求也可以创建子请求,因此这些请求间完整的保存形式可以理解成一颗分层树,如下图所示。上图中,每个红圈表示一个请求,每一层的请求分别是上一层请求的子请求。从树遍历的角度讲,在这样一棵树上,哪个节点应该最先被处理?结合子请求机制的实际意义来分析,子请求是为了分摊父请求的处理逻辑,降低业务复杂度。换而言之,父请求是依赖于子请求的。很大程度上父请求可能需要等到当前子请求运行完毕后根据子请求反馈的结果来做一些收尾工作。所以需要采用的是类似后序遍历的规则。即上图最右下角的请求是第一个“活跃”的请求。从源码层面来说,这颗分层树的保存用到了两个数据结构,r->postponed 和 r->parent这两个指针,遍历 r->postponed 来按序访问当前请求的子请求(树中同层的兄弟节点);遍历 r->parent 访问到父请求(树中上一层的父节点)。postpone_filter 模块会判断当前请求是否“活跃”,如果不“活跃”,则把将要发送的数据临时拦截到它自己的 r->postponed链表上(所以这个链表上其实既有数据也有请求);如果是活跃的,则遍历它的 r->postponed 链表,要么把被临时拦截下来的数据发送出去,要么找到第一个子请求,将其标记为 “活跃”,然后返回。等到该子请求处理结束,重新将其父请求标记为“活跃”,这样一来,当父请求再一次运行到 postpone_filter 模块的时候,又可以遍历 r->postponed 链表,循环往复直到所有请求或者数据处理完毕。感兴趣的同学可以自行阅读相关源码(http://hg.nginx.org/nginx/file/tip/src/http/ngx_http_postpone_filter_module.c)。使用了子请求机制的模块目前整个 Nginx 生态圈,有很多使用子请求的例子,最著名的便是 ngx_lua 的子请求和 Nginx 官方的 slice_filter 模块了。ngx_lua 提供给用户的 API (ngx.location.capture)灵活性非常大。 包括针对是否共享变量也可自行选择。特别地,ngx_lua 的子请求运行时,会阻塞父请求(挂起其对应的 Lua 协程)。直到子请求运行完毕,子请求的响应头、响应体(所以如果响应体比较大,则会消耗很多内存)等信息都会返回给父请求。ngx_lua 的子请求是不经过 postpone_filter模块的,它在一个较早的 filter 模块(ngx_http_lua_capture_filter) 里就完成了对子请求响应体的拦截。Nginx 官方提供的 slice_filter模块,可以将一个资源下载,拆分成若干个 HTTP Range 请求,这样做最大的好处是分散热点。这个模块允许我们设置一个指令 slice_size,用以设置后续 Range 请求的区间大小。该模块会陆续创建子请求(在前一个完成后),直到所需资源下载完毕。另外, Nginx/1.13.1 也引入了一个称为 Background subrequests 的机制(用以更新缓存)。基于这个机制,Nginx/1.13.4 引入了一个 mirror 模块,通过创建子请求,可以让用户自定义一些后台任务。比如预热一些资源,直接将它们放入 Nginx 自身的 proxy_cache 缓存中。陷阱与缺陷前文说到,子请求创建出来时,复用了父请求的一些数据,这无形中引入了一些坑点。比如变量缓存,如果在子请求中访问并缓存了某个变量,当后续在父请求中使用时,我们就会得到之前的缓存数据,这可能造成工程师们花费大量的时间和精力去调试这个问题。另外笔者认为一个非常重大的缺陷是,子请求复用了父请求的内存池,以 slice_filter 模块举例,它将一个 HTTP 请求划分成若干个的子请求,每个子请求向后端发起 HTTP Range 请求,在资源非常大 ,而配置的 slice_size 相对比较小的时候,会造成有大量的子请求的创建,整个资源下载过程可能会持续很长一段时间,这导致父请求的内存池在一段时间内没有释放,加之如果并发数比较大,可能会造成进程内存使用率变得很高,严重时可能会 OOM,影响到服务。因此在考虑使用的时候,需要权衡这些问题,有必要的话可能需要自行修改源码,以满足业务上的需要。虽然一些缺点是在所难免的,但是子请求机制很大程度上简化了请求的处理逻辑,它分而治之的处理思想非常值得我们去学习和借鉴,无论如何,子请求机制也将是后续进行系统设计时的一大参考范例。《我眼中的 Nginx》系列:我眼中的 Nginx(一):Nginx 和位运算我眼中的 Nginx(二):HTTP/2 dynamic table size update我眼中的 Nginx(三):Nginx 变量和变量插值我眼中的 Nginx(四):是什么让你的 Nginx 服务退出这么慢? ...

March 27, 2019 · 1 min · jiezi

Prometheus 监控 Nginx

前言Nginx官网有介绍各种监控方案,以前我们常用stub_status和Log日志来实现Nginx监控。本文主要介绍基于Prometheus的2种监控方案nginx-lua-prometheus和nginx-vts-exporter,在真实的生产环境中你可能需要研究和选择哪种方法才是适合你的,F5收购Nginx后的未来让我们一起拭目以待。Prometheus 监控 Nginx更新历史2019年03月25日 - 初稿阅读原文 - https://wsgzao.github.io/post…扩展阅读Monitoring NGINX - https://www.nginx.com/blog/mo...Nginx监控官网介绍的监控方案 - https://www.nginx.com/blog/mo...Prometheus 集成的 HTTP exporter 方案 - https://prometheus.io/docs/in…聊聊 Nginx 的监控 - https://zhuanlan.zhihu.com/p/…使用rpmbuild制作Nginx的RPM包 - https://wsgzao.github.io/post...Prometheus 监控 Nginxnginx-lua-prometheusNginx 需要添加 Lua 扩展https://github.com/knyar/ngin…# 下载redis_exporterhttps://github.com/knyar/nginx-lua-prometheus/releaseswget https://github.com/knyar/nginx-lua-prometheus/archive/0.20181120.tar.gztar xf 0.20181120.tar.gzcd nginx-lua-prometheus-0.20181120# 创建prometheus.lua目录mkdir -p /etc/nginx/lua/cp prometheus.lua /etc/nginx/lua/# 编辑nginx配置文件修改,注意修改lua_package_path “/etc/nginx/lua/prometheus.lua”;vim /etc/nginx/nginx.conflua_shared_dict prometheus_metrics 10M;lua_package_path “/etc/nginx/lua/prometheus.lua”;init_by_lua ’ prometheus = require(“prometheus”).init(“prometheus_metrics”) metric_requests = prometheus:counter( “nginx_http_requests_total”, “Number of HTTP requests”, {“host”, “status”}) metric_latency = prometheus:histogram( “nginx_http_request_duration_seconds”, “HTTP request latency”, {“host”}) metric_connections = prometheus:gauge( “nginx_http_connections”, “Number of HTTP connections”, {“state”})’;log_by_lua ’ metric_requests:inc(1, {ngx.var.server_name, ngx.var.status}) metric_latency:observe(tonumber(ngx.var.request_time), {ngx.var.server_name})’;# 创建nginx-lua-prometheusvim /etc/nginx/sites-available/nginx-lua-prometheusserver { listen 9145; location /metrics { content_by_lua ’ metric_connections:set(ngx.var.connections_reading, {“reading”}) metric_connections:set(ngx.var.connections_waiting, {“waiting”}) metric_connections:set(ngx.var.connections_writing, {“writing”}) prometheus:collect() ‘; }}# 创建软链接cd /etc/nginx/sites-enabled/ln -s ../sites-available/prometheus# 测试Nginx语法并reload测试metricsnginx -tnginx -s reloadcurl http://127.0.0.1:9145/metrics# iptables rule for Prometheus Nginx -A INPUT -s xxx -p tcp –dport 9145 -j ACCEPTnginx-vts-exporterhttps://github.com/hnlq715/ng…对方正在输入中Grafananginx-lua-prometheushttps://grafana.com/dashboard…nginx-vts-exporterhttps://grafana.com/dashboard…参考文献https://prometheus.io/docs/in… ...

March 26, 2019 · 1 min · jiezi

Vue项目部署(阿里云+Nginx代理+PM2)

最近部署一个Vue项目到阿里云ECS上,因为项目涉及一些跨域请求,所以采用了Nginx代理请求本地的node服务(利用pm2做进程管理)。node服务借助axios设置headers的referer、host转发请求,解决跨域请求问题。先交代下在阿里云ECS里安装的部署环境:phpstudy(php调试运行大礼包,里面包含nginx、mysql等,还能监控端口占用情况)、pm2(node服务进程管理工具)、node、git等等。部署过程拉去GitHub项目代码至root目录下(找到安装phpstudy的WWW目录),构建项目修改nginx-conf的代理配置、root项配置(指向项目项目的dist目录下)启动pm2(项目中,事先已写好服务端逻辑prod.server.js)启动phpstudy访问项目构建项目构建项目前,要修改项目confing目录下的index.js,主要是build部分的端口、目录,具体如下:build: { port: 9001, // 因为我是用PHPStudy做web服务器的,此时php.cgi会占9000端口,所以改用9001 // Template for index.html index: path.resolve(__dirname, ‘../dist/index.html’), // Paths assetsRoot: path.resolve(__dirname, ‘../dist’), assetsSubDirectory: ‘static’, assetsPublicPath: ‘’, …….}主要有两部分修改端口,跟代理端口一致port: 9001修改assetsPublicPathassetsPublicPath: ‘’nginx-conf配置利用phpstudy,可以很方便的进行nginx相关设置、host修改、端口监控等等。先来说说nginx-conf的配置。先找到nginx-conf配置入口修改ngin-confupstream my_project { server 127.0.0.1:9001; keepalive 64;}server { listen 80; server_name my_project; #charset koi8-r; #access_log logs/host.access.log main; root “C:/phpStudy/PHPTutorial/WWW/project/dist”; location / { index index.php index.html index.htm; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection “upgrade”; proxy_max_temp_file_size 0; proxy_pass http://my_project/; proxy_redirect off; proxy_read_timeout 240s; }}注意事项root配置,指向构建后目录 root “C:/phpStudy/PHPTutorial/WWW/project/dist”;设置代理端口时,避免端口占用!!upstream my_project { server 127.0.0.1:9001; keepalive 64;}开始部署时,使用的是9000端口,一直运行不起来,后面发现phpstudy启动是php-cgi.exe默认就使用了9000端口。关于端口使用情况,phpstudy也提供了工具。启动pm2关于pm2的介绍、常规使用自行了解。在项目目录下事先已经写好了转发请求的逻辑。pro.server.jsvar axios = require(‘axios’)const bodyParser = require(‘body-parser’)var config = require(’./config/index’)var express = require(’express’)var app = express()var apiRoutes = express.Router()apiRoutes.get(’/api/getdata’, function(req, res) { var url = ‘https://a.com’ axios.get(url, { headers: { referer: ‘https://b.com’, host: ‘b.com’ }, params: req.query }).then((response) => { …. }).catch((e) => { console.log(e) })})app.use(’/’, apiRoutes)app.use(express.static(’./dist’))var port = process.env.PORT || config.build.portmodule.exports = app.listen(port, function (err) { if (err) { console.log(err) return } console.log(‘Listening at http://localhost:’ + port + ‘\n’)})这里只是简单的借助axios可以设置referer和host,实现代理转发的功能。运行prod.server.jsprod.server.js是在根目录下,所以运行命令如下:pm2 start prod.server.js进程列表:pm2 start list查看进程详情pm2 show 0总结整个部署过程涉及比较多的知识点,nginx代理,node开发部署,端口管理等等。在端口占用这个点上卡了一段时间。不过目前只是实现构建部署、访问。但维护成本还是比较高,先得从GitHub拉取代码,本地构建。项目完成以后,研究下持续性集成流程。 ...

March 26, 2019 · 1 min · jiezi

nginx,作为前端的你会多少?

–现在阅读的你,如果是个FE,相信你不是个纯切图仔。反之,如果是,该进阶了,老铁!前端的我们,已经不仅仅是做页面,写样式了,我们还需要会做相关的服务器部署。废话不多说,下面就从前端的角度来讲以下nginx的相关使用。给我们的静态资源启一个web 服务给我们的nodejs 的项目设置反向代理,80端口访问给我们的接口做转发设置跨域请求配置https服务的请求接口登录云服务器首先你得有一台linux服务器(用你电脑起个本地服务也OK,这里不做这个介绍,我们用的是云服务器),如果没有,你可以上相关的云服务实验室开 1、2个小时的服务器玩玩也行,这里推荐 阿里云的 https://edu.aliyun.com/lab/ ,和腾讯云的 https://cloud.tencent.com/dev… 。下面是取阿里云开放实验室的服务器演示:登录安装nginx (源码编译安装)安装nginx 的相关依赖yum -y install gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel下载nginx包wget http://nginx.org/download/ngi…解压:tar -zxvf nginx-1.15.8.tar.gz编译安装配置nginx安装选项./configure –prefix=/usr/local/nginx编译安装make && make install启动、查看进程/usr/local/nginx/sbin/nginx ps -ef | grep nginx查看网页,nginx 启动成功。补充命令: /usr/local/nginx/sbin/nginx -t// 查看nginx 配置文件是否语法正确/usr/local/nginx/sbin/nginx -s reload// 重新加载nginx 配置/usr/local/nginx/sbin/nginx -s stop// 停止nginx我们要修改nginx 的基本配置, 做以下步骤:cd /usr/local/nginxmkdir vhostscd vhostsvim active.conf按 esc 再按 :wq 保存并退出修改nginx.confvim /usr/local/nginx/nginx.conf在倒数第二行添加 include vhosts/*.conf⬆️⬆️⬆️⬆️⬆️⬆️⬆️⬆️⬆️⬆️⬆️⬆️⬆️⬆️⬆️(以上nginx 的安装路径,可以自己自由选择)注意:下面具体的演示案例,是我个人的服务器,使用的是域名访问, 上面的实验服务器的时长限制,没办法做很多的业务操作。到这里基本上配置好nginx 的使用和扩展,下面就是我们要利用 nginx 实现一些功能了。使用nginx1、给我们的静态资源启一个web 服务vim /usr/local/nginx/vhosts/active.conf将server 模块写进去,server { listen 8008; server_name localhost; root /usr/myfile/dist; index index.html;}访问:2、接口转发, 跨域请求server { server_name vue.wtodd.wang; charset utf-8; location /nodejsapi/ { proxy_pass http://localhost:5000/; }}实际请求 http://localhost:5000/ 的接口,被代理到请求该域名de /nodejsapi/ 下访问:3、给我们其他端口启动的nodejs 项目设置反向代理到80端口访问server { listen 80; server_name csa.scampus.cn; location / { proxy_pass http://127.0.0.1:8000; }}页面访问:实际项目访问地址:4、配置https服务的请求接口这里涉及到了https 证书的配置,这里不牵涉这个话题,这里 https://help.aliyun.com/docum… 是阿里云的免费https 证书,可参照该步骤。 有人说,前端为什么还要https 的服务? 微信小程序的服务接口,需要走https 的哇!我们做demo,不要自己会配置一下吗,省得找后台哇???? server { listen 80; server_name api.scampus.cn; rewrite ^ https://$http_host$request_uri? permanent; } server { listen 443; ssl_certificate /etc/nginx/ssl/alyca.pem; ssl_certificate_key /etc/nginx/ssl/alyca.key; server_name api.scampus.cn; ssl on; ssl_session_timeout 5m; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_prefer_server_ciphers on; location / { proxy_pass http://localhost:4000/; } }访问:原先真实请求地址配置 https 访问的地址:总结:这里nginx 在前端的使用只是很少的一部分,比如做请求拦截、api版本控制等,但这一些应用也是受到前端处理范围的局限,使得我们运用的也不多,相信以后随着“大前端“的发展,我们会更多的使用nginx功能或类nginx 服务功能。欢迎品阅和指正! ...

March 25, 2019 · 1 min · jiezi

在阿里云上单机使用Nginx负载均衡发布网站

恕我见识短浅,不知道Nginx有Windows版本,可以运行在Windows系统下,当初遇到Socket并发压力增长,学习负载均衡的时候,找的是LVS的4层负载均衡,没有考虑7层负载均衡,所以一直不了解Nginx的Windows版本。今天写此教程,就是为了告诉大家怎么在Windows下配置Nginx。首先说,Nginx正常用法应当是在 网站发布在多机器上,实现网站压力大的时候,增加网站的负载能力和提高可用性能。本文选择单机发布多站点,首先是为了学习下Nginx的配置,其次是可以这样来实现网站的“高可用”。对于正式业务,可以选购阿里云的负载均衡SLB产品,不应当使用这种单机多站点的发布方法。本文将使用阿里云的ECS作为测试站点,使用条件:ECS有公网IP,使用Windows系统并装有IIS服务,安全组和防火墙提前放行80端口。【准备工作】首先在IIS里,建立几个的80之外的端口的临时网站(不要占用80端口,因为一会要把80端口给Nginx用):只有一个首页,内容分别是编号 web01、web02、web03……发布端口分别设置为81、82、83……在阿里云ECS内部使用浏览器检查下(http://127.0.0.1:81、http://127.0.0.1:82、http://127.0.0.1:83……),能分别看到各自的内容表示演示站点搭建无误【下载Nginx 1.15.9(windows版本)】官网地址:http://nginx.org/download/ngi… (需要其他版本请访问 http://nginx.org/en/download…. 自行寻找)【配置过程】使用远程桌面连接登录到阿里云服务器上,将Nginx文件下载到在服务器上解压缩,我的路径是 C:webnginx-1.15.9找到配置文件 C:webnginx-1.15.9conf 目录下的 nginx.conf 使用记事本(推荐notepad++)打开,这里就是配置文件,需要对新手说明一下的是,前面带有#的表示注释。1,添加配置,把前面准备好的几个站点放进去这里是自己添加的,本文单机演示,同ip不同端口,实际应用环境应该是不同内网ip,相同端口upstream linuxidc {server 127.0.0.1:81; server 127.0.0.1:82; server 127.0.0.1:83; #如果还有其他站点(机器),在这里添加即可}2,找到 location 配置节点,添加 proxy_pass 节点,内容配置为 http://linuxidc; (linuxidc是upstream的节点名称)location / {root html;index index.html index.htm;#添加转发配置,这里的 linuxidc是upstream的节点名称proxy_pass http://linuxidc;}然后访问下,在远程桌面里,打开系统自带的浏览器,反复刷新访问下Nginx的端口 127.0.0.1:80 ,会看到不同的站点的内容,说明Nginx配置成功3,最后一步,在自己其他的设备上,使用浏览器访问阿里云ECS的公网IP,看下是否跟第二步的结果相同,能正常打开表示网站发布完成!【写在最后】本文只做了最简单的入门教程,教大家使用Nginx实现网站的负载均衡最基础的部分,通过配置文件,可以看到其他配置信息:设置错误页面,设置文件访问权限,绑定SSL证书,配置不同节点的权重等等,这些还需要进一步试验学习原文地址: https://www.opengps.cn/Blog/V… 文章的更新编辑依此链接为准。欢迎关注源站原创文章!

March 25, 2019 · 1 min · jiezi

docker搭建php+nginx+swoole+mysql+redis环境

操作系统:阿里云esc实例centos7.4软件:docker-ce version 18.09.3, docker-compose version 1.23.2一.创建带有swoole-redis-pdo_mysql-gd扩展的docker image1.创建dockerfile文件vim dockerfile2.在dockerfile文件写入From php:7.1-fpmRUN apt-get update && apt-get install -y \ libfreetype6-dev \ libjpeg62-turbo-dev \ libpng-dev &amp;& docker-php-ext-install -j$(nproc) iconv &amp;& docker-php-ext-configure gd –with-freetype-dir=/usr/include/ –with-jpeg-dir=/usr/include/ &amp;& docker-php-ext-install -j$(nproc) gd &amp;& docker-php-ext-configure pdo_mysql &amp;& docker-php-ext-install pdo_mysql &amp;& pecl install redis-4.3.0 &amp;& pecl install swoole &amp;& docker-php-ext-enable redis swoole3.创建自定义的php镜像,主要不要漏了最后的 ‘.’,是指定当前目录构建镜像docker build -t myphp4 .运行指令,由于网络问题等,需要等比较长的时间,成功后会出现类似下面的代码…Build process completed successfullyInstalling ‘/usr/local/include/php/ext/swoole/config.h’Installing ‘/usr/local/lib/php/extensions/no-debug-non-zts-20160303/swoole.so’install ok: channel://pecl.php.net/swoole-4.3.1configuration option “php_ini” is not set to php.ini locationYou should add “extension=swoole.so” to php.iniRemoving intermediate container ad1420f7554f —> 2f2f332d73ceSuccessfully built 2f2f332d73ceSuccessfully tagged myphp4:latest至此docker 的自定义myphp4 image创建成功!二.创建docker-compose.yml文件mkdir pnsmrcd pnsmrvim docker-compose.yml写入下面代码version: ‘3.0’services:nginx: image: “nginx:latest” ports: - “10000:80” volumes: - /var/www/html:/usr/share/nginx/htmlphp-fpm: image: “myphp4” volumes: - /var/www/html:/usr/share/nginx/htmlmysql: image: “mysql:latest"redis: image: “redis:4.0"运行指令docker-compose up -d成功可以看到WARNING: The Docker Engine you’re using is running in swarm mode.Compose does not use swarm mode to deploy services to multiple nodes in a swarm. All containers will be scheduled on the current node.To deploy your application across the swarm, use docker stack deploy.Creating network “pnsmr_default” with the default driverCreating pnsmr_php-fpm_1 … doneCreating pnsmr_redis_1 … doneCreating pnsmr_mysql_1 … doneCreating pnsmr_nginx_1 … done至此,已开启nginx mysql redis php 服务三.修改各服务配置文件 1.浏览器输入 127.0.0.1:9998 #此处应输入你的服务器ip地址,可以看到下图 2.接下来要修改容器里nginx的配置文件,先使用指令查看各容器的docker IP地址docker inspect -f ‘{{.Name}} - {{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}’ $(docker ps -aq)此指令可以查看所有用docker-compose 开启的容器的ip,结果类似下图,可以用对应的ip地址进行内部通讯3.复制nginx容器的配置文件出来,并修改替换,使nginx能解析phpdocker cp pnsmr_nginx_1:/etc/nginx/conf.d/default.conf nginx.confvim nginx.conf修改为下列代码server { listen 80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ .php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ .php$ { root html; fastcgi_pass 172.24.0.3:9000;#此处需要填写你的php容器的docker内部通讯ip fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/$fastcgi_script_name; include fastcgi_params; } # deny access to .htaccess files, if Apache’s document root # concurs with nginx’s one # #location ~ /.ht { # deny all; #}}docker cp nginx.conf pnsmr_nginx_1:/etc/nginx/conf.d/default.conf #将修改好的配置文件拷贝到容器里docker container stop pnsmr_nginx_1 docker container start pnsmr_nginx_1 #重启nginx容器使配置文件生效vim /var/www/html/index.php #在服务器本地目录新建 index.php 文件,输入<?php phpinfo(); 并保存vim /var/www/html/index.html #在服务器本地目录新建 index.html 文件,输出helloworld访问127.0.0.1:9998, html文件解析正常 访问127.0.0.1:9998/index.php,php文件解析正常 4.测试mysql,redis是否生效vim /var/www/html/redis.php #用于测试redis是否配置成功<?php$redis = new Redis();$redis->connect(“172.24.0.4”,6379);$redis->set(’test’,’this is a test for redis’);echo $redis->get(’test’);访问127.0.0.1:9998/redis.php,redis已生效进入mysql容器docker exec -it pnsmr_mysql_1 bash进入mysql并更改root用户密码创建测试文件vim /var/www/html/mysql.php<?php$pdo = new PDO(‘mysql:host=172.24.0.2;dbname=mysql;port=3306’,‘root’,‘root123’);var_dump($pdo);访问127.0.0.1:9998/mysql.php,mysql已生效四.总结 虽然环境是配置成功了,并可以用docker-compose up 指令一键生成,但是还要改各容器的配置文件,仍然不够方便,需要优化;另外docker的集群,堆栈功能也没用上,后面再继续学习. ...

March 25, 2019 · 2 min · jiezi

Lua在Nginx的应用

首发于 樊浩柏科学院当 Nginx 标准模块和配置不能灵活地适应系统要求时,就可以考虑使用 Lua 扩展和定制 Nginx 服务。OpenResty 集成了大量精良的 Lua 库、第三方模块,可以方便地搭建能够处理超高并发、扩展性极高的 Web 服务,所以这里选择 OpenResty 提供的 lua-nginx-module 方案。安装Lua环境lua-nginx-module 依赖于 LuaJIT 和 ngx_devel_kit。LuaJIT 需要安装,ngx_devel_kit 只需下载源码包,在 Nginx 编译时指定 ngx_devel_kit 目录。系统依赖库首先确保系统已安装如下依赖库。$ yum install readline-devel pcre-devel openssl-devel gcc安装LuaJIT首先,安装 LuaJIT 环境,如下所示:$ wget http://luajit.org/download/LuaJIT-2.0.5.tar.gz$ tar zxvf LuaJIT-2.0.5.tar.gz$ cd LuaJIT-2.0.5$ make install# 安装成功==== Successfully installed LuaJIT 2.0.5 to /usr/local ====设置 LuaJIT 有关的环境变量。$ export LUAJIT_LIB=/usr/local/lib$ export LUAJIT_INC=/usr/local/include/luajit-2.0$ echo “/usr/local/lib” > /etc/ld.so.conf.d/usr_local_lib.conf$ ldconfig下载相关模块下载 ngx_devel_kit 源码包,如下:$ wget https://github.com/simpl/ngx_devel_kit/archive/v0.3.0.tar.gz$ tar zxvf v0.3.0.tar.gz# 解压缩后目录名ngx_devel_kit-0.3.0接下来,下载 Lua 模块 lua-nginx-module 源码包,为 Nginx 编译作准备。$ wget https://github.com/openresty/lua-nginx-module/archive/v0.10.10.tar.gz$ tar zxvf v0.10.10.tar.gz# 解压缩后目录名lua-nginx-module-0.10.10加载Lua模块Nginx 1.9 版本后可以动态加载模块,但这里由于版本太低只能重新编译安装 Nginx。下载 Nginx 源码包并解压:$ wget http://nginx.org/download/nginx-1.13.5.tar.gz$ tar zxvf nginx-1.13.5.tar.gz编译并重新安装 Nginx:$ cd nginx-1.13.5# 增加–add-module=/usr/src/lua-nginx-module-0.10.10 –add-module=/usr/src/ngx_devel_kit-0.3.0$ ./configure –prefix=/usr/local/nginx –with-http_ssl_module –with-http_v2_module –with-http_stub_status_module –with-pcre –add-module=/usr/src/lua-nginx-module-0.10.10 –add-module=/usr/src/ngx_devel_kit-0.3.0$ make$ make install# 查看是否安装成功$ nginx -v配置Nginx环境现在只需配置 Nginx,即可嵌入 Lua 脚本。首先,在 http 部分配置 Lua 模块和第三方库路径:# 第三方库(cjson)地址luajit-2.0/liblua_package_path ‘/home/www/lua/?.lua;;’;lua_package_cpath ‘/usr/local/include/luajit-2.0/lib/?.so;;’;接着,配置一个 Lua 脚本服务:# hello world测试server { location /lua_content { # 定义MIME类型 default_type ’text/plain’; content_by_lua_block { ngx.say(‘Hello,world!’) } }}测试安装和配置是否正常:$ service nginx test$ service nginx reload# 访问地址/lua_content输出Hello,world!Lua调用Nginxlua-nginx-module 模块中已经为 Lua 提供了丰富的 Nginx 调用 API,每个 API 都有各自的作用环境,详细描述见 Nginx API for Lua。这里只列举基本 API 的使用 。先配一个 Lua 脚本服务,配置文件如下:location ~ /lua_api { # 示例用的Nginx变量 set $name $host; default_type “text/html”; # 通过Lua文件进行内容处理 content_by_lua_file /home/www/nginx-api.lua; }请求部分ngx.var可以通过ngx.var.var_name形式获取或设置 Nginx 变量值,例如 request_uri、host、request 等。– ngx.say打印内容ngx.say(ngx.var.request_uri)ngx.var.name = ‘www.fanhaobai.com’ngx.req.get_headers()该方法会以表的形式返回当前请求的头信息。查看请求的头信息:ngx.say(‘Host : ‘, ngx.req.get_headers().host, ‘<br>’)for k,v in pairs(ngx.req.get_headers()) do if type(v) == “table” then ngx.say(k, “ : ”, table.concat(v, “,”), ‘<br>’) else ngx.say(k," : ", v, ‘<br>’) endend当然,通过 ngx.req.set_header() 也可以设置头信息。ngx.req.set_header(“Content-Type”, “text/html”)ngx.req.get_uri_args()该方法以表形式返回当前请求的所有 GET 参数。查看请求 query 为?name=fhb的 GET 参数:ngx.say(’name : ‘, ngx.req.get_uri_args().name, ‘<br>’)for k,v in pairs(ngx.req.get_uri_args()) do if type(v) == “table” then ngx.say(k, “ : ”, table.concat(v, “,”), ‘<br>’) else ngx.say(k," : ", v, ‘<br>’) endend同样,可以通过 ngx.req.set_uri_args() 设置请求的所有 GET 参数。ngx.req.set_uri_args({name=‘fhb’}) –{name=‘fhb’}可以为query形式name=fhbget_post_args()该方法以表形式返回当前请求的所有 POST 参数,POST 数据必须是 application/x-www-form-urlencoded 类型。查看请求curl –data ’name=fhb’ localhost/lua_api的 POST 参数:–必须先读取body体ngx.req.read_body()ngx.say(’name : ‘, ngx.req.get_post_args().name, ‘<br>’)for k,v in pairs(ngx.req.get_post_args()) do if type(v) == “table” then ngx.say(k, “ : ”, table.concat(v, “,”), ‘<br>’) else ngx.say(k," : ", v, ‘<br>’) endend通过 ngx.req.get_body_data() 方法可以获取未解析的请求 body 体内容字符串。ngx.req.get_method()获取请求的大写字母形式的请求方式,通过 ngx.req.set_method() 可以设置请求方式。例如:ngx.say(ngx.req.get_method())响应部分ngx.header通过ngx.header.header_name的形式获取或设置响应头信息。如下:ngx.say(ngx.header.content_type)ngx.header.content_type = ’text/plain’ngx.print()ngx.print() 方法会填充指定内容到响应 body 中。如下所示:ngx.print(ngx.header.content_type)ngx.say()如上述使用,ngx.say() 方法同 ngx.print() 方法,只是会在后追加一个换行符。ngx.exit()以某个状态码返回响应内容,状态码常量对应关系见 HTTP status constants 部分,也支持数字形式的状态码。ngx.exit(403)ngx.redirect()重定向当前请求到新的 url,响应状态码可选列表为 301、302(默认)、303、307。ngx.redirect(‘http://www.fanhaobai.com’)其他ngx.re.match该方法提供了正则表达式匹配方法。请求?name=fhb&age=24匹配 GET 参数中的数字:local m, err = ngx.re.match(ngx.req.set_uri_args, “[0-9]+")if m then ngx.say(m[0])else ngx.say(“match not found”)endngx.log()通过该方法可以将内容写入 Nginx 日志文件,日志文件级别需同 log 级别一致。ngx.md5() | ngx.encode_base64() | ngx.decode_base64()它们都是字符串编码方式。ngx.md5() 可以对字符串进行 md5 加密处理,而 ngx.encode_base64() 是对字符串 base64 编码, ngx.decode_base64() 为 base64 解码。Nginx中嵌入Lua上面讲述了怎么在 Lua 中调用 Nginx 的 API 来扩展或定制 Nginx 的功能,那么编写好的 Lua 脚本怎么在 Nginx 中得到执行呢?其实,Nginx 是通过模块指令形式在其 11 个处理阶段做插入式处理,指令覆盖 http、server、server if、location、location if 这几个范围。模块指令列表这里只列举基本的 Lua 模块指令,更多信息参考 Directives 部分。指令所在阶段使用范围说明init_by_luainit_by_lua_file加载配置文件http可以用于初始化全局配置set_by_luaset_by_lua_filerewriteserverlocationlocation if复杂逻辑的变量赋值,注意是阻塞的rewrite_by_luarewrite_by_lua_filerewritehttpserverlocationlocation if实现复杂逻辑的转发或重定向content_by_luacontent_by_lua_filecontentlocationlocation if处理请求并输出响应header_filter_by_luaheader_filter_by_lua_file响应头信息过滤httpserverlocationlocation if设置响应头信息body_filter_by_luabody_filter_by_lua_file输出过滤httpserverlocationlocation if对输出进行过滤或修改使用指令注意到,每个指令都会有*_lua和*_lua_file两个指令,_lua指令后为 Lua 代码块,而_lua_file指令后为 Lua 脚本文件路径。下面将只对*_lua指令进行说明。init_by_lua该指令会在 Nginx 的 Master 进程加载配置时执行,所以可以完成 Lua 模块初始化工作,Worker 进程同样会继承这些。nginx.conf配置文件中的 http 部分添加如下代码:– 所有worker共享的全局变量lua_shared_dict shared_data 1m; init_by_lua_file /usr/example/lua/init.lua;init.lua初始化脚本为:local cjson = require ‘cjson’local redis = require ‘resty.redis’local shared_data = ngx.shared.shared_dataset_by_lua我们直接使用 set 指令很难实现很复杂的变量赋值逻辑,而 set_by_lua 模块指令就可以解决这个问题。nginx.conf配置文件 location 部分内容为:location /lua { set_by_lua_file $num /home/www/set.lua; default_type ’text/html’; echo $num;}set.lua脚本内容为:local uri_args = ngx.req.get_uri_args()local i = uri_args.a or 0local j = uri_args.b or 0return i + j上述赋值逻辑,请求 query 为?a=10&b=2时响应内容为 12。rewrite_by_lua可以实现内部 URL 重写或者外部重定向。nginx.conf配置如下:location /lua { default_type “text/html”; rewrite_by_lua_file /home/www/rewrite.lua;}rewrite.lua脚本内容:if ngx.req.get_uri_args()[“type”] == “app” then ngx.req.set_uri("/m_h5”, false);endaccess_by_lua用于访问权限控制。例如,只允许带有身份标识用户访问,nginx.conf配置为:location /lua { default_type “text/html”; access_by_lua_file /home/www/access.lua;} access.lua脚本内容为:if ngx.req.get_uri_args()[“token”] == “fanhb” then return ngx.exit(403)endcontent_by_lua该指令在 Lua 调用 Nginx 部分已经使用过了,用于输出响应内容。案例访问权限控制使用 Lua 模块对本站的 ES 服务做受信操作控制,即非受信 IP 只能查询操作。nginx.conf配置如下:location / { set $allowed ‘115.171.226.212’; access_by_lua_block { if ngx.re.match(ngx.req.get_method(), “PUT|POST|DELETE”) and not ngx.re.match(ngx.var.request_uri, “_search”) then start, _ = string.find(ngx.var.allowed, ngx.var.remote_addr) if not start then ngx.exit(403) end end } proxy_pass http://127.0.0.1:9200$request_uri;}访问频率控制在 Nginx 配置文件的 location 部分配置 Lua 脚本基本参数,并配置 Lua 模块指令:default_type “text/html”;set rate_per 300access_by_lua_file /home/www/access.lua;Lua 脚本实现频率控制逻辑,使用 Redis 对单位时间内的访问次数做缓存,key 为访问 uri 拼接 token 后的 md5 值。具体内容如下:local redis = require “resty.redis"local red = redis:new()local limit = tonumber(ngx.var.rate_per) or 200local expire_time = tonumber(ngx.var.rate_expire) or 1000local key = “rate.limit:string:“red:set_timeout(500)local ok, err = red:connect(“www.fanhaobai.com”, 6379)if not ok then ngx.log(ngx.ERR, “failed to connect redis: " .. err) returnendkey = key .. ngx.md5(ngx.var.request_uri .. (ngx.req.get_uri_args()[’token’] or ngx.req.get_post_args()[’token’]))local times, err = red:incr(key)if not times then ngx.log(ngx.ERR, “failed to exec incr: " .. err) returnelseif times == 1 then ok, err = red:expire(key, expire_time) if not ok then ngx.log(ngx.ERR, “failed to exec expire: " .. err) return endendif times > limit then return ngx.exit(403)endreturn相关文章 »进入Lua的世界(2017-09-03)Lua在Redis的应用(2017-09-04) ...

March 25, 2019 · 3 min · jiezi

Xtrabackup数据库备份

wget -O /etc/yum.repos.d /epel.repo http://mirrors.aliyun.com/repo/epel-6.repo #配置epel源yum -y install perl perl-devel libaio libaio-devel perl-Time-HiRes perl-DBD-MySQL #安装Xtrabackup软件需要的基础环境包wget https://www.percona.com/downloads/XtraBackup/PerconaXtraBackup-2.4.4/binary/redhat/6/x86_64/percona-xtrabackup-24-2.4.4-1.el6.x86_64.rpmls -l percona-xtrabackup-24-2.4.4-1.el6.x86_64.rpmyum -y install percona-xtrabackup-24-2.4.4-1.el6.x86_64.rpmls -l which xtrabackup innobackupexmkdir /application/mysql/logs -pchown -R mysql.mysql /application/mysql/logsegrep -v “#|^$” /etc/my.cnf 向 /etc/my.cnf加入两行log_bin=/application/mysql/logs/bin-log #mysql5.6之前是log-bin之后是log_binexpire_logs_days=7Xtrabackup命令,专门用于对InnoDB和XtraDB等事务引擎的数据库热备份的工具,不能用于备份myIsam等其他类型的引擎数据,它的主要特点是备份数据时完全不用锁表。Innobackupex命令,将上述Xtrabackup命令使用perl脚本进行二次封装的工具,除了可以用于InnoDB和XtraDB等引擎之外,还可以备份MyISAM及多种引擎混合使用场景,它的主要特点是备份事务引擎数据而不用锁表,可以备份非事务引擎数据,但要锁表。mkdir /application/mysql/data #创建一个备份目录innobackupex –defaults-file=/etc/my.cnf –user=root –password=123456 –socket=/var/lib/mysql/mysql.sock –no-timestamp /application/mysql/data/full #全备最终结果如下:总用量 18468drwxr-x— 2 root root 12288 3月 20 14:33 3ideapc-rw-r—– 1 root root 417 3月 20 14:33 backup-my.cnf #配置文件备份-rw-r—– 1 root root 18874368 3月 20 14:32 ibdata1 #共享表空间备份drwxr-x— 2 root root 4096 3月 20 14:32 mysql drwxr-x— 2 root root 4096 3月 20 14:32 test-rw-r—– 1 root root 113 3月 20 14:33 xtrabackup_checkpoints #checkpoints信息-rw-r—– 1 root root 515 3月 20 14:33 xtrabackup_info #xtrabackup信息-rw-r—– 1 root root 2560 3月 20 14:33 xtrabackup_logfile #xtrabackup日志文件 ...

March 20, 2019 · 1 min · jiezi

nginx中access日志如何做到按时间完美切割

nginx web服务器中access日志,默认是不能按时间分隔的,每次日志都是打在access.log上,这样久而久之这个日志文件就特别的大,也不利于清理和管理,故此我们肯定是需要做时间上的切割的,那么如何做到完美的切割的呢?我们采取的方案是利用shell脚本和crontab定时任务来做比如新建一个nginx_time_log.sh脚本,里面的内容如下(当然也可以使用linux中的logrotate来做日志切割)!/bin/bashlocal_path=/home/work/tp/log/webserver #找到您服务器中存放access日志的目录cd $local_path #进入这个目录mv access_log $local_path/access_log date +%Y%m%d%H #把当前的access_log挪到这个时期下,其实就是相当于日志的切分nginx_pid=ps -ef |grep -v grep |grep “nginx: master process “|awk -F” ” ‘{print $2}’ #找到您nginx的进程 kill -USR1 $nginx_pid #执行usr1这样就会先把access_log 移动到一个access_log.时间 的日志文件,并且会新生产一个access_log文件最后通过定时任务来让这个nginx_time.sh脚本按每小时来进行切分crontab命令如下:0 /1 sh /xxx(您这个脚本的存放命令)/nginx.sh 按每小时切割

March 19, 2019 · 1 min · jiezi

现在最不缺的应该就是码农了,缺的是技术过硬又精通业务的工程师

昨天,一位分析界的老前辈对我很无奈地摇摇头,“这帮程序员,不食人间烟火哪!”我也深有感触,全世界的码农都一个鸟样。这让我想起了,同样也是他,在多年之前,对我提了警醒——要重视业务。从那之后,我一直狂奔在技术+业务的双修道路上。放在以前,码农这个族群一定是稀罕动物。但在今天,这个世界最不缺的应该就是码农了,未来最廉价的也将是码农。仅有泛泛一技,在未来并不吃香,因为那是要被机器人所取代的。这个世界,缺的是技术过硬又精通业务的工程师,缺的是真正能解决实际业务问题的人,缺的是复合型的人才。码农不是工程师,码农只是会写代码,只会明确需求和逻辑的情况下写代码。工程师则不一样,懂得用技术怎么解决实际业务问题,用技术驱动业务的发展。什么叫业务?先来明确这个问题。业务是一个很实在的东西,看得见感受得到,接地气儿。业务就是我们所能理解和感受的世界,就是这个世界或者某个行业的运转逻辑、流程与现状,是结果表象,是能够被看见和感受的,也是内在本质,是能够被洞察和感知的。业务就是这个世界发生了什么,什么时候,谁参与,怎么发生,结果如何。业务就是什么时候,谁在哪里,买了什么东西,花了多少钱,用什么支付。业务就是这个行业怎么发展起来的,现状如何,未来趋势如何,用了什么技术,有什么企业,商业模式如何,盈利能力如何,目前主要面临什么问题,消费者有什么特点,等等。世界很复杂,单个细分行业的业务也很复杂。为什么要了解业务?谈到这个,码农们一定有所不悦,“熟悉业务是需求分析师做的事,跟我们没有关系。”打个不恰当的比喻。有10个人经过一栋写字楼,突然从楼上掉下来几块砖头,砸中了9个人,其中就有7个码农,3个硕士,1个博士(原谅我又犯职业病,拿数据说话了)。而没被砸到的那个人,恰好因为了解到之前经常发生这样的事而绕道行走。如果你只会写代码,你不是不可替代的,而是可有可无的。因为这年头,会JAVA、C、Python的程序员,在大街上一抓一大把。现在已经开始提倡,编程从娃娃抓起了。10后都开始跟你抢饭碗了,你怕不怕?但话也不是那么极端,除非你的技术很牛逼,在国内或者某个行业内能够排上号的。但技术牛逼的人,也不是只是技术超群,还常常因为能够利用手中的技术解决某方面的业务问题,做了哪些突出的贡献。我们出来混,也是要拿成果说话的,做过什么项目,有什么价值。这种价值往往就是针对业务而说的。IT研发与业务需求方常常是一对冤家,常常因为一个业务功能实现争辩得耳红面赤。研发觉得这个功能很low,没什么技术含量,业务方却认为这个功能却很有用,需要花功夫做细做深做好。现实情况是,功能做出来了,却很难用,或者经常用不了,或者数据不对。研发想做点高大上的功能,业务方却认为太虚了,没什么用。(IT与业务方那点事就不多说了,大家都心知肚明~~)多年经验反复告诫我,鉴定一个功能是不是好功能,唯一的标准是看它能否支撑业务、改善业务、推动业务,也即应用效果。一个产品,只要有30%的功能,让业务用户用起来很爽,感觉帮助很大,就已经是一个不错的产品了。我们都认同,技术驱动业务。但我们不一定明白,正是由于业务的某些强烈需求,才推动技术的发展与落地。说这些,我是想说,作为技术人员,我们既要仰望星空,也要脚踏实地,既要追逐腾飞的技术,也要重视落地的业务。如果一个业务人员很懂技术,那将很可能是技术人员的灾难。因为那样的话,业务人员会很强势,又或者那样就没有技术人员什么事了。当然,也不难想象,一个真正懂看数据的测试人员,就好比一个真正懂用算法的业务人员一样难得。业务与数据的关系真实(而不是杜撰、模拟、伪造)、可量化、可被记录的数据一定会反映真实世界某方面的业务情形。而现实当中很多业务场景都可由数据体现出来。零售是业务场景最繁多且最贴近我们生活的行业,可以从中找到很多方便理解的例子。当你在一个酷热难耐的夏天上午10点,走进位于公司附近的全家便利店,使用微信支付,花了3.5元,买了一瓶无糖330ml摩登罐的可乐,而且刷会员卡攒了100积分,而收银员MM返回给了你一张POS单据,这时你所发生的这一切都已经通过收银记录在了全家的数据库里。更糟糕的是,店里的摄像头也已经把你在店里的一举一动录了下来了,转化成为一帧帧图像数据。这就是,业务数据化。店长通过数据分析发现,最近3.5元330ml摩登罐可乐的销量比上月增长了20%,而消费者中75%是20-35岁的男性,相比之下,300ml塑料瓶装的可乐销量却下滑40%。店长权衡比较了一下,300ml塑料瓶装可乐利润低,330ml摩登罐可乐目前更受年轻人欢迎,考虑到日渐增长的租金压力,做了一个大胆的决定——下架300ml塑料瓶装可乐,增加330ml摩登罐可乐的商品。(又拿数据说话了。)这就是,数据业务化。或者,数据驱动业务。当我开始接触一个行业时,我通常会花2-3周的时间去了解这个行业的业务,然后就大致清楚这个行业有什么样的数据,可以做哪方面的分析,解决什么问题。当遇到不好理解的分析结果时,我经常使用业务联想法,设身处地去体会结果所反映的业务场景是什么样的。如何了解业务?这个说大了,就是如何看这个世界。每个人有每个人的方法论,每个人有每个人的世界观,每个人有每个人的逻辑思维。我们都知道,观念的转变是最难的,也有很多不确定性。有些人可能因为自己的切身体会一天就改变了之前几十年根深蒂固的看法,有些人任由三姑六婆苦口婆心地劝说就是不肯改变自己的择偶观,却有可能因为自己年岁渐大不断降低自己的标准。但最好也及早要形成科学的思考方法,帮助正确地理解这个世界。以“面-线-点”的方式可以较为全面、系统、深入地了解一个行业,然后是某个垂直领域,最后再到具体业务场景。佛系文化的流行,使得年轻一代降低对这个世界的关注度,一切都无所谓,一切都漠不关心。这个世界从来没有变好过,但我们每个人都是这个世界的匆匆过客,都是行走在自己的人生路上不断领略这个世界的美与丑。这世间的风景,这世间的悲欢离合,如果我们积极地探索与领悟,也不枉来这世间走一遭。保持好奇心,可以驱动我们的思考,强化我们的认知,丰富我们的内在。这是我想说的第二个方面。怀有好奇心,就会渐渐地敏锐观察这个世界,多问自己一些为什么。我家附近原来有个沃尔玛超市,现在地产商将它装修一番,引入了不少餐厅,刚开张不久,我就去那里吃饭,吃的是烤鸭,一个多两个月后,再去那里吃饭,发现有一半的餐厅已经关门了。在去地铁站的那条路上,每天人流如梭,一点点,即使到了深夜,依然有很多人在门口排队买奶茶。然而,仅仅隔了一个店铺的喜茶,做不下去,关门了。两三个月前又换成粉店,路转粉。每天下班路过时,发现店里顾客不到10个,门可罗雀。为什么每家一点点奶茶店门口,不管是什么时候都是很多人,他们是托儿还是真的顾客?因为喜欢新鲜,不喜欢在冰箱里存太多菜,且附近没有菜市场,所以常去买菜的还是附近的钱大妈。但我却没怎么去更近的一家生活超市,店面比较大,除果肉蔬菜外,也卖油盐酱醋,还有生活用品,但奇怪的是顾客却不到钱大妈的1/10。为什么几乎所有潮州牛肉店都很多人,有很多甚至在门口排了很长的队?观察到这些,常常会陷入思考,为什么会发生这些,新零售到底改变了什么?再举个例子。去年拿保温杯泡着枸杞的中年男火了。关于这个,我又问了自己几个问题:拿着保温杯泡着枸杞的是不是都是中年男?如果是,这个特征能否被数据量化?可否考虑加入到算法模型当中,加以应用起来?虽然很多问题,我没有找到答案,但多问自己问题,会引发自己不断深入思考,不断激发自己好奇心,不断去研究。很多业务知识都是零散的,不可能在短时间内完全了解,可以在日常不断积累。关于日常积累业务知识,可以经常询问懂业务的人。这是我想说的第三个方面。刚进公司的时候,我以为业务很简单。很快,我就发现里面的坑不少。加上所在团队的成员也是刚入职不久的,问问题没处可问。过了一个月之后,我发现隔壁团队有两个十年左右的老员工,业务很熟,而且人特好。于是,我几乎一遇到业务问题,就跑过去“骚扰”他们,他们也很乐意解答,如果他们不清楚,他们也会告诉我应该去找谁了解。大约半年之后,我基本摸透了顺丰的数据和业务情况。我也和那两位老员工建立了不错的友谊,即使后来换了部门,我也经常过去找他们。跟懂业务的人搞好关系,遇到业务问题,多咨询他们,这是最有效最接地气的办法。多看书,这是我想说的第四个方面。比如说,从事新零售领域方面的工作,总得先了解新零售是怎么回事。你可以去听专家们忽悠,但这样的机会很少,而且时间也有限,说不定成本还很高。读书则不一样。读书,意味着主动了解,主动去构建自己的知识体系。读书的重要性,这里不多言了。如果您读这篇文章的时候,您恰好也是一位数据人。我还想告诫一句:我们不能脱离业务去看数据,而是要时刻从业务角度去理解数据。我们不敢期望可以完全理解这个世界,但也憧憬着我们不单可以在代码的世界里畅快驰骋,论剑江湖,也可以放下身段洞察芸芸众生之百态,领悟人间世俗之真情。如果真的可以的话,就没有需求分析师什么事了。硬实力这里说的硬实力,也就是技术上的真实积累。怎么来体现你的技术实力?我总的分为:技术深度和技术广度这两方面。技术广度通俗的讲,就是你熟悉该技术点的使用以及基本原理。一般面试官在面试首轮会问很多技术点,来考核你是否能正确使用。准备不充分的面试,完全是浪费时间,更是对自己的不负责(如果title很高,当我没说)。今天给大家分享下在跳槽时需要准备的Java面试大纲,其中大部分都是面试过程中的面试题,可以对照这查漏补缺,当然了,这里所列的肯定不可能覆盖全部方式。软实力软实力在面试过程中也尤为重要(有时候真的要更重要),主要是指和面试官的沟通,对一个问题的阐述方式和表达方式,逻辑思维能力等。面试过程全程微笑,项目描述需要严谨的表述,个人的优缺点基本要做到随口而出..等这些其实就是软实力的体现。知己知彼、百战不殆,面试也是如此,针对于上面的面试问到的知识点我总结出了互联网公司java程序员在面试中涉及到的绝大部分架构面试题及答案做成了文档和架构视频资料免费分享给大家(包括Dubbo、Redis、Netty、zookeeper、Spring cloud、分布式、高并发等架构技术资料),希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!资料领取方式:关注+转发后,私信关键词 【架构资料】即可获取!重要的事情说三遍,转发、转发、转发后再发私信,才可以拿到哦!

March 18, 2019 · 1 min · jiezi

Nginx(2)-创建具有缓存功能的反向代理服务器

承接上一篇文章,在本文中,将上文中的静态资源服务器作为上游服务器,另外搭建一台 Nginx 服务器,作为反向代理服务器。配置反向代理服务器上游服务器处理的业务逻辑相对复杂,而且强调开发效率,所以它的性能并不优秀,使用 nginx 作为反向代理后,可以将请求将根据负载均衡算法,分散到多台上游(后端)服务器,这样就实现了架构上的水平扩展,让用户无感知的情况下,添加更多的服务器,来提升性能,即使后端的服务器出现问题,nginx反向代理服务器会转交给正常工作的服务器。一般情况下,上游服务器不对外提供访问,修改的方法是,将 server 配置块中的 listen 配置项修改为内部网络地址,修改配置文件后,重启nginx 进程,目的是防止之前打开的端口仍然可以使用。Nginx实现反向代理的功能由 ngx_http_proxy_module 实现,下面是配置示例:location / { proxy_pass http://localhost:8080; proxy_set_header Host $host; #当后端还有虚拟主机时,应该返回正确的网页,而不是用户请求不用的 host 都返回相同的内容 proxy_set_header X-Real-IP $remote_addr;}当用户请求"/“的所有 URL请求,都转交配置文件中proxy_pass指定的后端服务器,同时还设置了向后端生成请求报文时新的 header,如定义Host 将用户请求的 host 定义在 header 中,定义 X-Real-IP客户端的 IP 地址。…upstream webdlib{ #定义上游服务器群组,并自定义名称为 webdlib server 172.16.240.140:8080; #上游服务器群组的服务器列表,多台服务器可以选择负载均衡算法 }server { listen 80 server_name _; … … location / { proxy_pass http://172.16.240.140:8080; #设置上游服务器地址 proxy_set_header Host $host; #添加请求首部 host 名称,由上游服务器处理 host 请求 proxy_set_header X-Real-IP $remote_addr; #添加客户端真实 IP 地址 proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; }配置缓存服务器通常只有动态请求,也就是不同的用户访问同一个 url内容不相同时,请求才会交由上游处理,在页面中,一部分内容在一段时间不会发生变化,为了减轻上游服务器的压力,将上游服务器返回的内容,缓存在反向代理服务器中保存一段时间,如几个小时或一天,在缓存时间内,即使上游服务器内容发生变化,也会被忽视,将缓存的内容向浏览器发送。使用缓存会提供站点的响应性能。首先要在 http 配置块下,使用proxy_cache_path定义缓存文件的路径、文件命名方式、命名共享内存及共享内存的空间大小等信息,如proxy_cache_path /tmp/nginxcache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;缓存的使用方法则是,在需要进行缓存url 路径下,添加 proxy_cache、proxy_cache_key、proxy_cache_valid。proxy_cache my_cache:指定缓存共享内存的命名proxy_cahce_key $host$uri$is_args$args:在共享内存中设置的 key 的值,这里将 host,uri 等作为 key 值Proxy_cache_valid 200 304 302 1d :指定的响应不返回缓存下面是关于缓存的配置文件节选:…http { … proxy_cache_path /tmp/nginxcache levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off; … server { … location / { proxy_pass http://172.16.240.140:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; proxy_cache my_cache; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 304 302 1d; } }}总结首先配置反向代理服务器,需要使用 proxy_pass设置上游服务地址、使用 proxy_set_header设置向后端发送请求的 header诸如客户端的 IP 地址、请求的 host。配置缓存服务器,首先要设置缓存的名称,内存空间名称等信息,然后在需要进行缓存的 URL 路径下,启用缓存,进行缓存的设置诸如缓存的名称、缓存的 key 等。 ...

March 18, 2019 · 1 min · jiezi

nginx的网络配置

vi /etc/networksdefault 0.0.0.0loopback 127.0.0.0link-local 169.254.0.0vi /etc/sysconfig/network-scripts/ifcfg-enp0s8DEVICE=enp0s8BOOTPROTO=dhcpONBOOT=yesNAME=enp0s8UUID=。。。。。。。

March 18, 2019 · 1 min · jiezi

nginx配置laravel项目

server{ listen 443; server_name 10.0.3.15 ssl on; ssl_certificate /etc/nginx/conf.d/3i.crt; ssl_certificate /etc/nginx/conf.d/3i_nopass.key; root /var/www/html/3i-mobile/public; index index.php; location /{ try_files $uri $uri/ /index.php?$query_string; } #error_page 404 /404.html error_page 500 502 503 504 /50x.html; location = /50x.html{ root /usr/share/nginx/html; } location ~ .php${ root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /var/www/html/3i-mobile/public/$fastcgi_script_name; include fastcgi_params; }}对应的Php配置文件user = daemongroup = daemonlisten = 127.0.0.1:9000listen = allowed_clients = 127.0.0.1pm = dynamicpm.max_children = 80pm.start_servers = 5pm.min_spare_servers = 5pm.max_spare_servers = 35slowlog = /var/log/php-fpm/www-slow.logphp_admin_value[error_log] = /var/log/php-fpm/www-error.logphp_admin_flag[log_errors] = onphp_value[session.save_handler] = filesphp_value[session.save_path] = /var/lib/php/sessionphp_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache ...

March 18, 2019 · 1 min · jiezi

Nginx(1)

初识NginxNginx 的三个主要应用场景:静态资源服务:通过本地文件系统提供服务反向代理服务:缓存加速与负载均衡API服务:OpenRestyNginx 背景和优点:互联网的数据量快速增长、摩尔定律的性能提升以及低效的 Apache(一个连接对应一个进程)促使 Nginx 的出现。Nginx 的优点高并发、高性能:32核64G 内存可以处理数千万并发连接可扩展性:得益于模块化设计高可靠性:持续不间断运行数年热部署:不停止服务情况下,升级 NginxBSD 许可证Nginx 的组成Nginx 二进制可执行文件:由各模块源码编译出的一个文件Nginx.conf 配置文件:控制 nginx 的行为access.log 访问日志:记录每一条 http 请求信息error.log 错误日志:定位问题Nginx 的版本发布开源版本的 Nginx 中,奇数版本的 Nginx 如1.15.x 是 Mainline 主干版本,它会有新的功能,但可能并不稳定,偶数版本如1.14.x,是Stable 稳定版本。每个版本的 CHANGES 中,包含三部分:Feature新增功能、Bugfix 修复 bug、Change重构。编译 Nginx安装 Nginx 的方法主要用两种:编译 Nginx 或者直接安装二进制 nginx。二进制格式的 Nginx 中模块已经编译,但并不是每一个模块(功能)都开启(被编译),一些第三方模块,同样包含强大的功能就无法编译到二进制 Nginx 中。自主编译 nginx 可以根据自己的需求合理开启需要编译的模块。下载 Nginx下载你需要的 Nginx 版本,并解压缩。解压缩 Nginx 的源码包后,包含6个目录和5个文件auto:有四个子目录,用于编译的 cc、lib库目录 lib、判断操作系统的 os、types 和19个文件用于辅助 configure判断当前系统支持哪些模块以及特性conf:示例文件,安装成功后,会将此目录中的内容拷贝到安装目录configure:执行编译前的必备动作,生成中间文件contrib:提供两个 perl 脚本和一个vim 配置文件用于高亮显示 nginx 的配置文件,文件放置于~/.vim/中:cp -r contrib/vim/* ~/.vim/html:提供两个标准 html 文件 index.html 和50X.htmlsrc:源码目录Configure获取支持的的参数使用./configure –help,编译时支持的参数有三类:指定 Nginx 各种文件的安装位置;确定开启和关闭的模块。使用 with 开头的需要手动开启,使用 without 的默认会编译到 nginx 中,指定即可取消编译该模块;指定 nginx 的安装过程中使用的特殊参数,如 c 编译器的路径等。执行./configure –prefix=/usr/local/nginx检查系统环境是否符合满足安装要求,并将定义好的安装配置信息和系统环境信息写入Makefile文件中。 Makefile 文件包含了如何编译、启用哪些功能、安装路径等信息。在预编译过程中,可以出现的错误大部分是因为软件包的缺失。编译 Nginx 需要依赖的软件包关系有:yum install -y gcc gcc-c++ autoconf automake make /yum install -y pcre pcre-devel /yum install -y zlib zlib-devel预编译后,会生成 objs 目录和 Makefile 文件编译执行make命令进行编译。make命令会根据Makefile文件进行编译。编译工作主要是调用编译器(如gcc)将源码编译为可执行文件,通常需要一些函数库才能产生一个完整的可执行文件。编译生成的二进制文件存在于 src 目录中,如果使用了动态模块,编译产生的.so 文件,会存在于 objs 目录中。安装上面之所以没有使用 make && make install,是因为如果是升级 nginx,则需要将二进制文件拷贝到安装目录中。首次安装 nginx时,可以使用make install将编译的文件复制到指定目录中。 创建软链接:sudo ln -s /usr/nginx-1.14.2/sbin/nginx /usr/bin/nginxNginx 的配置文件Nginx 的配置文件中,nginx.conf 为主配置文件,配置文件中,以#开始的行,或者是前面有若干空格或者 TAB 键,然后再跟#的行,都会被认为是注释。Nginx 的配置文件是以 block(块)形式组织,每个 block 都是以一个块名称和一对大括号“{}”组成。block 分为几个层级,整个配置文件为 main 级别,即最大层级;在 main 级别之下会有 event、http、mail 等层级,而 http 中又会包含 server block,server block 中可以包含 location block。即块之间是可以嵌套的,内层 block 继承外层 block。最基本的配置项语法格式是“配置项名 配置项值1 配置项值2 配置项值3 … ”;每个层级可以有自己的指令(Directive),例如 worker_processes 是一个main层级指令,它指定 Nginx 服务的 Worker 进程数量。有的指令只能在一个层级中配置,如worker_processes 只能存在于 main 中,而有的指令可以存在于多个层级,在这种情况下,子 block 会继承 父 block 的配置,同时如果子block配置了与父block不同的指令,则会覆盖掉父 block 的配置。指令的格式是“指令名 参数1 参数2 … 参数N;”,注意参数间可用任意数量空格分隔,最后要加分号。下图是 Nginx 配置文件的一般结构:总结一下 Nginx 的配置语法:配置文件由指令块和指令组成每条指令以“;”结尾,指令与参数间以空格分隔指令块以{}将多条指令组织在一起include 语句允许组合多个配置文件以提升可维护性使用“#”添加注释,以提高可读性“$”使用变量部分指令的参数支持正则表达式Nginx 中时间参数的单位时间单位描述msmillisecondsssecondsmminuteshhoursddayswweeksMmonths(30 days)yyears(365 days)Nginx 中空间参数的单位空间单位描述不加任何单位bytesk/KKilobytesm/MMeagbytesg/GGigabytesNginx 服务运行时,需要加载几个核心模块和一个事件模块,这些模块运行时所支持的配置项称为基本配置;基本配置项大概分为四类:用于调试、定位的配置项正常运行的必备配置项优化性能的配置项事件类配置项操作 Nginx 命令行功能命令行一般格式nginx -s reload获取帮助-?-h使用指定的配置文件-c指定配置指令-g执行运行的目录-p发送信号-s立刻停止服务 stop优雅停止服务 quit重载配置文件 reload 重新开始记录日志文件 reopen测试配置文件语法-t -T打印 nginx 的版本和编译信息-v -v这里需要说明的是:在修改配置文件后,使用nginx -s reload,在不停止服务的情况下,运行修改后的配置文件。热部署 nginx(升级)备份低版本的 nginx 二进制文件cp ngnx nginx.old将编译好的新版本 nginx 二进制文件拷贝替换正在运行的 nginx 进程所使用的二进制文件发送信号给旧 nginx master 进程,让其优雅关闭进程kill -WINCH [旧 ngix mater 进程号]。当前旧 nginx master还存在的原因是,如果新版本 nginx有问题,可以进行版本回退。日志切割将当前的日志文件复制到系统的另外一个位置执行命令nginx -s reopen,nginx 会重新生成同名的日志文件实际上日志切割会定期进行,应该使用 bash 脚本来实现,并有 crond 定时执行。#!/bin/bash#Rotate the Nginx logs to prevent a single logfile from consuming too much disk space.LOGS_PATH=/usr/local/nginx/logs/historyCUR_LOGS_PATH=/usr/local/nginx/logsYESTERDAY=$(date -d “yesterday” +%Y-%m-%d)mv ${CUR_LOGS_PATH}/access.log ${LOGS_PATH}/access_${YESTERDAY}.logmv ${CUR_LOGS_PATH}/error.log ${LOGS_PATH}/error_${YESTERDAY}.log#向 Nginx 主进程发送 USR1 信号。USR1 信号试重新打开日志文件kill -USR1 $(cat /usr/local/nginx/logs/nginx.pid)使用 Nginx 搭建一个静态服务器将静态网站部署在服务器中对外提供服务,在 nginx 安装目录中创建一个 dlib 目录用于存放静态网页配置 nginx,修改配置文件中的 server 中的监听端口listen为80或8080,location 后面定义的"/“表示所有请求,指定 url的后缀与服务端文件系统路径一一对应,有两种用法,一种是使用 root,一种是使用 alias,如使用 alias dlib/就表示 url/后面的路径与 dlib/后面的路径是一一对应的。节选配置文件:user simon;…http{ gzip on; … server{ listen 8080; … location / { alias dlib/; } }}优化:开启压缩gzip on; #开启 gzip 压缩gzip_min_length 1; #压缩的最小单位,小于1字节不压缩gzip_comp_level 2; #压缩级别gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpep image/gif image/png; #针对指定类型进行压缩压缩前:压缩后:功能:将文件系统通过web 网页展示通过 ngx_http_autoindex_module ,可以将以“/”结尾的 url 对应到相应文件系统目录中,并列出。在 location 块中加入“autoindex on;”即可开启。这里注意的是,当打开的是首页依然是一个web 页面,只有 url 中继续定义了查看静态网站文件系统目录中的某一目录时,才会将文件列出。优化:高并发时,限制大文件的传输速率在 location 块中,设置“set $limit_rate 1k(http 模块的内置变量);”意思是限制 nginx 每秒传输1字节到浏览器中,定义 nginx access 日志格式定义日志格式需要命名,因为会根据不同的场景记录不同的日志信息,nginx 默认的日志记录名称为 main,记录了诸如客户端 IP、客户端名称、时间、状态码等。定义日志,在 http 配置块下,使用 log_format,后面定义日志的名称,然后使用nginx 中指定的变量定义需要记录的日志格式。定义日志存储位置,使用 access_log,定义 access_log的位置决定了记录日志的范围,也就是说,当在 server 配置块中定义了 access_log,那么发往server 指定的端口或者域名的请求,都会使用 access_log定义的级别和位置进行记录。总结如何编译、安装、升级 Nginx部署一个静态网站常用的流程,以及 Nginx 的配置文件中相关指令的配置参考Nginx 配置文件NGINX systemd service filesystemd 添加自定义系统服务附录预编译参数mkdir -p /var/cache/nginx./configure --prefix=/usr/local/nginx --sbin-path=/usr/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nobody --group=nobody --with-pcre --with-http_v2_module --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_stub_status_module --with-http_auth_request_module --with-mail --with-mail_ssl_module --with-file-aio --with-ipv6 --with-http_v2_module --with-threads --with-stream --with-stream_ssl_moduleNGINX systemd service filesystem 的有系统和用户的区分:系统(/lib/systemd/system/)、用户(/etc/lib/systemd/system/),一般系统管理员手工创建的单元文件建议存放在/etc/lib/systemd/system/目录下。在/lib/systemd/system/目录下创建服务文件nginx.service[Unit]Description=The NGINX HTTP and reverse proxy server #服务的简单描述Documentation=http://nginx.org/en/docs/ #服务文档,可有可无After=syslog.target network.target remote-fs.target nss-lookup.target#定义启动顺序,After表示本服务在指定的服务之后启动,另外类似的还有 Before、Requires本单元启动,它需要的单元也会被启动;它需要的单元停止了,这个单元也停止了、wants推荐使用,这个单元启动了,它需要的单元也会被启动;它需要的单元被停止了,对本单元无影响。[Service]Type=forking #systemd认为当该服务进程fork,且父进程退出后服务启动成功。对于常规的守护进程(daemon),除非你确定此启动方式无法满足需求,使用此类型启动即可。使用此启动类型应同时指定 PIDFile=,以便systemd能够跟踪服务的主进程。PIDFile=/usr/nginx-1.14.2/logs/nginx.pidExecStartPre=/usr/bin/nginx -tExecStart=/usr/bin/nginxExecReload=/usr/sbin/nginx -s reloadExecStop=/bin/kill -s QUIT $MAINPIDPrivateTmp=true[Install]WantedBy=multi-user.target ...

March 18, 2019 · 2 min · jiezi

linux创建swap分区

创建swap分区(内存超过2G,可不配置)创建1G的swap,可以根据你的服务器配置来调整大小dd if=/dev/zero of=/mnt/swap bs=1M count=1024 设置交换分区文件mkswap /mnt/swap启动swapswapon /mnt/swap设置开机时自启用 swap 分区需要修改文件 /etc/fstab 中的 swap 行,添加/mnt/swap swap swap defaults 0 0

March 18, 2019 · 1 min · jiezi

Java到底要做到什么程度才能适应市场的需求(本人的面试经历)

前言:从过年前就萌生出要跳槽的想法,到过年来公司从月初提出离职到~~号正式离职,上班的时间也出去面试过几家公司,后来总觉的在职找工作总是得请假,便决心离职后找工作。到3月10号找到了一家互联网公司成功应聘上,中间也经历了很多公司,有外包的、创业的、互联网的等等各种类型,也收到了很多offer,也有面试不顺序的…今天来记录一下自己面试中的问题,围绕着java到底应该具备什么样的水平才能适应现在市场的要求的主题来谈一谈。本篇文章目录:一:面试中的问题二: 面试中要注意的问题三:关于最后的选择四:两年java到底应该具备什么样的水平一:面试中的问题java集合框架:1:介绍一下java的集合框架2:HashMap遇见哈希冲突会如何怎么办?HashMap是线程安全的吗?HashMap在高并发下会有什么问题?然后引入ConcurrentHashMap的原理?3:Hahtable和concurrentHashMap的区别?4:数组和ArrayList的区别?Arraylist是如何扩容的?5:线程池中的阻塞队列一般会选择哪种队列?为什么?6:RetreenLock的原理?AQS的原理?7:HashMap的容量为什么推荐是2的幂次方?框架类:1:mybatis的二级缓存有什么问题?2:mybaits中的mapper的#{}和${}有什么区别?哪种可以防止sql注入?2:我们知道mybatis的mapper和接口之间是没有对象的,那么它是如何映射的?4:说说springmvc的注解有哪些?他们的原理是什么?5:springmvc的控制器是单例的吗?是线程安全的吗?6:struts1和struts2的区别?是线程安全的吗?7:spring如何解析它的xml文件?8:spring的核心是什么?Aop的原理是什么?redis相关:1:redis数据类型有哪些?2:zset数据类型是如何排序的?3:redis如何做项目的中间缓存层?4:redis的Hash的时间复杂度是多少?数据库:1:数据库索引分为哪几种?组合索引有什么要注意的问题?2:什么是悲观锁 什么是乐观锁?如何实现悲观锁?3: 数据库关键字的执行顺序是什么?4:如何进行sql优化?5:有没有进行过分库分表操作?分库之后如何保持事务一致?分布式和微服务:1:微服务要克服那些问题?微服务系统是怎样通信的?2:分布式环境下如何解决session不一致的问题?3:分布式下如何保证id一致?4:你在dubbo的使用过程中遇到什么问题?5: zookeeper的负载均衡算法有哪些?jdk源码相关1:synchronized的原理?它该怎么用?如何一个方法是synchronized的,其他的非synchronzied线程能进入吗?2:cvs中的ABA问题如何解决?3:volatile的原理是什么?volatile一定是线程安全的吗?4:ThreadLocal是什么?它的原理是什么?5:CountDowanLatch有没有用过?适合在什么样的场景下用?设计模式相关:1:实现两种单例模式2:讲一下观察者模式3:spring中都用到哪些设计模式?4:动态代理模式是如何实现的?5:你在项目中用到哪些设计模式了?讲解一下业务场景算法相关:1:快速排序的时间复杂度?手写快速排序(注意递归式和非递归式的实现方式)2:手写二分查找3:手写堆排序4:一个int数组如何进行奇数和偶数分离?5:用算法实现String转doublejvm相关:1: jvm的垃圾回收算法有哪些?分别解释一下?2: 新生代为什么要设置两个survior区?3:如何通过一个.class文件获取它的jdk版本?4:jvm的内存模型?哪些是线程私有的?哪些是公共的?关于自己的项目(问的时间最长)1:简述一下自己的项目?你在其中主要是做什么的?2:你在项目中都遇到了哪些难题?最后都是怎么解决的?3:项目有多大规模?周期多久(这个很多都问到的)4:讲一下某一模块的具体实现方式?然后从中挑刺5:如何解决某一时刻的高并发请求?6:如何解决订单支付回调的超时问题?轮询应该怎么写?其他:1:秒杀场景如何削峰?2:http和udp的区别是什么?3:ajax的跨域问题4:nio与io的区别?什么情况下适合用nio5: 说说常见的linux命令,linux查看内存的命令是什么?7:git遇见代码冲突了怎么办?8:说几个常见的maven命令,maven如何排除一个jar包的冲突?二: 面试中要注意的问题2.1:一定要有自己的实际项目经验按照我这么多面试经验?其实有的公司会侧重于问自己做的项目经验,有的公司侧重于问问题,一般互联网公司会对技术要求比较高,既要求项目经验又要要求技术水平2.2:可以适当渲染,但是不要夸大其词面试的过程中最忌讳的就是夸夸其谈,高屋建瓴很厉害,但是一到实际细节都不知所云了,在技术总监面前,其实你吹牛或者是真的会他是一目了然的。不懂装懂,有的面试官又给你台阶下,不然你就卡带了,这很容易造成面试的不好印象2.3:要会自我介绍面试的时候一般的话都会让你做一个自我介绍,这个要分对象,是技术官还是Hr,如果是技术官侧重于综述一下自己的项目的实际技术栈和技术路线,如果是Hr的话不要用过多的技术语言,而要说一些自己的实际工作经历或者自己上家公司的运营情况2.4:关于简历简历切记不可太啰嗦,但是不可太简单,作为技术的简历一般起码得在3页,不然HR会觉得你的求职态度不怎么好,不管如何求职结果如何,一个良好的简历会给人留下好的第一印象(有简历模板)三:关于最后的选择说实话也接受到很多HR的offer邀请,但是我一般会选择说考虑一下一天以后再给回复,切不可直接把话说死,不然后面就尴尬了。实际提供的offer的有一家外包公司,三家创业公司,两家互联网公司,最终选择了一家互联网公司,虽然实际上班地点有点远(下了地铁还得座公交,后来还是选择骑单车了),但是互联网公司会给你快的成长速度,并且互联网技术栈都比较新..相比于传统企业会有更多的技术挑战。而外包公司的话,可能环境不怎么好,我记得自己当初还是个小白的时候,去了外包,那里的优点就是会有不断的活,新人进去的话收获还是挺多的,但是作为已经有两年经验的我,外包很显然不适合我的后期职业发展。缺点:技术更新迭代的太慢,没有归属感,最后的选择我个人的意见是选择技术优先,毕竟以后软件路还长,技术才是王道四:两年java到底应该具备什么样的水平两年java的面试过程中遇到了很多挑战,也遇到了一些不谈技术的公司,从上面的面试题可以看出,目前对于java的要求越来越高,水涨船高,毕竟这个行业的人数越来越多,而保持自己的竞争力的唯一方法就是找对方向,不断学习,注意这里我提到的第一点是方向,然后才是学习。给自己制定一个职业规划,按照这个路线往前走,我其实还在想分布式微服务这块以后再深入学习,可是按照市场要求,现在已经刻不容缓了,一些技术架构比如:springcloud、duboo都得保持学习,这样才能有竞争力!作为一名两年的javaSir,你必须具备以下技能1:阅读源码的能力,多用Intelj idea这个开发工具,而不是eclipse。它是直接支持反编译class文件的,多读jdk源码,吸收优秀的源码并加以复用2:做到能够手写常见的排序算法,比如快速排序和堆排序、冒泡排序、选择排序、二分查找这些都是必须的3:对java的框架有很深入的认识,现在基本流行的ssm框架很多人都会,可是知道一些原理的人就不多了,得不断研究这些框架本身,它们都是经过无数次锤炼 出来的优秀框架4:多用redismongodb,传统的关系型数据库已经无法市场需求了,这些东西也是面试中的一部分,虽不是重点,但也是加分的选项5:对于微服务和分布式,这个是有一定难度的,我在面试人人车的时候,一面很顺利,二面被技术总监给pass了,问题就是分布式不是特别熟悉!要想进入好的互联网公司,分布式和微服务是很必须的6:jvm的底层,这里要推荐的书就是周志明的《深入jvm虚拟机》这本书了,我总在闲暇时间读它,所以jvm的问题还是信手拈来的最后送上福利:腾讯课堂的的Java工程化、高性能及分布式、高性能、高架构、性能调优、Spring、MyBatis、Netty源码分析视频、java高并发处理视频(在腾讯买得花1800块左右)如果有谁想要,可以私信我,免费分享哦,我花了些钱买的

March 17, 2019 · 1 min · jiezi

使用Docker部署Nginx+Flask+Mongo的应用

使用Docker部署Nginx+Flask+Mongo的应用Nginx做为服务器,Mongo为数据库支持,Flask为Python语言的Web框架,利用Docker的容器特性,可以简单地部署在linux服务器上项目准备项目主要目录如下__ project-name |__ docker-file |__ ningx |__ Dockerfile |__ conf |__ nginx.conf |__ flask |__ Dockerfile |__ requirements.txt |__ mongo |__ Dockerfile |__ setup.sh |__ docker-compose.yml |__ src |__ app |__ … |__ run.py简要说明docker-file目录为docker部署的配置文件src目录为flask应用的python代码Docker的详细配置docker-compose配置version: ‘2.2’services: mongo: build: ./mongo volumes: - “./mongo/db:/data/db” restart: always ports: - “27017:27017” environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: 123456 flask: build: ./flask links: - mongo ports: - “5000:5000” expose: - “5000” volumes: - ../src:/home/web nginx: build: ./nginx links: - flask volumes: - “./nginx/log:/var/log/nginx” - “../:/usr/share/nginx/html” ports: - “80:80” - “8080:8080” - “443:443” restart: alwaysMongoDB的配置/mongo/Dockerfile的内容如下FROM mongo:3.6# 设置时区ENV TZ=Asia/ShanghaiRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone# 设置工作目录ENV WORKDIR /usr/local/workENV AUTO_RUN_DIR /docker-entrypoint-initdb.dENV INSTALL_MONGO_SHELL setup.shRUN mkdir -p $WORKDIR# 复制数据库的初始化命令COPY ./$INSTALL_MONGO_SHELL $AUTO_RUN_DIR/RUN chmod +x $AUTO_RUN_DIR/$INSTALL_MONGO_SHELL/mongo/setup.sh的内容如下该文件的目的是,启动MongoDB后创建一个密码为test的用户test,并赋予它数据库test的读写操作#!/bin/bashmongo <<EOFuse admin;db.auth(‘root’, ‘123456’);use dmx_aluminum;db.createUser({user:’test’,pwd:’test’,roles:[{role:‘readWrite’,db:’test’}]});db.createCollection(“user”);EOFFlask应用的配置/flask/Dockerfile的内容如下FROM python:3.6# 设置时区ENV TZ=Asia/ShanghaiRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone# 设置工作区RUN mkdir -p /home/webWORKDIR /home/web# 添加依赖ADD requirements.txt /home/web/requirements.txtRUN pip3 install -i https://pypi.douban.com/simple/ -r requirements.txt# 使用gunicorn启动应用CMD gunicorn -w 4 -b 0.0.0.0:5000 run:app/src/app/run.py的代码此处注释了调试用的 app.run(),发布时用gunicorn启动from app import create_appapp = create_app(‘default’)app.debug=False# if name == ‘main’:# app.run()Nginx的配置/nginx/Dockerfile的内容如下FROM nginx:1.14# 设置时区ENV TZ=Asia/ShanghaiRUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone# 复制配置COPY conf/nginx.conf /etc/nginx/nginx.conf/nignx/conf/nginx.conf的内容如下user nginx;worker_processes 1;error_log /var/log/nginx/error.log warn;pid /var/run/nginx.pid;events { worker_connections 1024;}http { include /etc/nginx/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 /var/log/nginx/access.log main; sendfile on; #tcp_nopush on; keepalive_timeout 65; # 开启gzip gzip on; gzip_min_length 1k; gzip_buffers 4 16k; #gzip_http_version 1.0; gzip_comp_level 1; gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; gzip_vary off; gzip_disable “MSIE [1-6].”; server { listen 80; server_name localhost; keepalive_timeout 5; root /usr/share/nginx/html; location /static/ { alias /usr/share/nginx/html/src/app/static/; } location / { # checks for static file, if not found proxy to app try_files $uri @flask_app; } location @flask_app { proxy_pass http://192.168.0.2:5000; # 发布在阿里云上,应填写内网IP proxy_redirect off; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; #proxy_buffers 8 32k; #proxy_buffer_size 64k; } }}启动部署进入docker-flie目录 cd docker-flie启动docker docker-compose up查看容器状态 docker ps本地部署浏览器输入 127.0.0.1即可最后出现类似docker_file_nginx_1,docker_file_mongo_1, docker_file_flask_1的3个容器,说明成功!!!踩坑吐槽1 mongol容器中的初始化文件需要放在 docker-entrypoint-initdb.d 目录下本人做过如下尝试,会显示 mongdb未启动。ADD setup.sh /data/setup.shRUN chmod +x /data/setup.shCMD ["/data/setup.sh"]2 flask应用无法连接mongo,本文使用link方式。在数据库的配置应相应写成:MONGODB_SETTINGS = { ‘db’: ’test’, ‘host’: ‘mongo’, # 127.0.0.1 host地址一定要写你配置的–link的名字 ‘username’: ’test’, ‘password’: ’test’, ‘port’: 27017 }本地测试时改回127.0.0.13 nginx中配置使用的代理模式,其中执行flask应用的IP,应为内网IP ...

March 17, 2019 · 2 min · jiezi

Nginx-负载均衡

轮询配置语法:Syntax: upstream name {…}Default: -Context httpSyntax: server address [parameters];Default: -Context: upstream{ upstream imooc { server 192.168.8.1:8081; server 192.168.8.1:8082; server 192.168.8.1:8083; } // 轮询使用3个 server。 server { listen 80; server_name localhost; location / { proxy_pass http://xxx; include proxy_params; } … }}upstream 参数说明down当前的 erver 暂时不参与负载均衡backup预留的备份服务器max_fails允许请求失败的次数fail_timeout经过 max_fails 失败后,服务暂停时间max_conns限制最大的接收的连接数。调度算法方法说明轮询按时间顺序注意分配到不同的后端服务器加权轮询weight 值越大,分配到的访问几率越高。ip_hash每个请求按访问 IP 的 hash 结果分配,这样来自同一个 IP 的固定访问一个后端服务器。url_hash每个请求按访问 URL 的 hash 结果分配,这样来自同一个 IP 的固定访问一个后端服务器。least_conn最少连接数,那个机器连接数少就分发hash 关键数值hash 自定义的 key

March 16, 2019 · 1 min · jiezi

nginx 与后台端口冲突

问题:在起alice管理系统的开发环境的时候,发现后台所有的接口在第一次请求的时候全部产生404错误,但第二次请求成功定位问题查看nginx 报错日志发现如下报错,因此错误的认为错误发生在html的文件夹权限不够导致的文件无法写入,于是开放权限之后发现还是不行,在Google一番查找还是没找到解决方案。暂时搁置,第二天重新找错时,无意的点开8081端口,当你访问localhost:8081与127.0.0.1:8081的内容竟然不同。当时发觉是不是端口冲突了,于是打开文件下面是nginx的config文件和我们的项目配置文件,发现产生端口冲突,当你访问后台时发现请求会有专到nginx的静态文件,因此产生404报错。server { listen 8081; server_name 127.0.0.1; #charset koi8-r; #access_log logs/host.access.log main; location / { root html; index index.html index.htm; }server { # 平台端口 listen 8100; server_name localhost; location / { proxy_pass http://localhost:4200/; } location /api/ { proxy_pass http://localhost:8081/; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; }}解决方法更改后台端口,与项目未 nginx 的配置。遗留问题:在后台启动时,访问localhost:8081与127.0.0.1:8081的内容不同后台关闭时相同原因可能如下:localhost:系统带的本机当前用户的权限去访问127.0.0.1:等于本机是通过网络再去访问本机当nginx 与后台冲突时为什么奇数次访问nginx内部文件,偶数次访问后台(为什么能占用同一个接口) ...

March 15, 2019 · 1 min · jiezi

常用的Nginx/Apache引擎服务器配置HTTPS SSL证书示范

昨天我们看到百度发布"百度烽火算法升级",提到网站如果被劫持或者拦截可能会降低网站的权重和排名等问题。这使得我们网站需要使用HTTPS SSL证书来减少被拦截劫持的风险。其实在早些时候我们已经看到很多浏览器都强制要求网站使用SSL证书,如果不使用的会被提示不安全网站URL。这次机遇百度文章发布之后,可以看到大家应该还在犹豫的话会新增SSL证书实现HTTPS访问。目前,大部分网站都会采用的是NGINX、APACHE等WEB架构,所以我们只需要学习这两个架构技术就可以。这里我们需要注意的,SSL证书的安装分两种, 一种是我们服务器自带的一键安装免费SSL证书。比如let’s encrypt证书是免费的。对于个人网站或者小型网站是可以使用的,而且大部分浏览器也是支持的。但是作为商业网站,我们总不能去使用免费证书吧。所以如果是购买第三方的付费证书,我们需要做的就是配置到服务器中。在这篇文章中,笔者将会分享在常规的NGINX、APCHE中如何配置SSL证书。其实配置SSL证书不难的,只要我们认真看下过程就可以。如果我们个人网站可以使用免费证书,一般LAMP或者LNMP脚本都是自带免费证书的。具体可以参考:1、LNMP环境一键安装免费Let’s Encrypt SSL证书2、宝塔面板不同PHP版本、伪静态设置且一键免费安装Let’s Encrypt但是,如果我们是购买的付费证书则不可以这么自动化安装SSL,我们需要手工安装。第一、NGINX WEB引擎安装SSL证书在当前网站下服务器配置文件的.conf文件中添加。server {listen 443;server_name www.domain.com; #填写绑定证书的域名ssl on;ssl_certificate 1_www.domain.com_bundle.crt;ssl_certificate_key 2_www.domain.com.key;ssl_session_timeout 5m;ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #按照这个协议配置ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;#按照这个套件配置ssl_prefer_server_ciphers on;location / {root html; #站点目录index index.html index.htm;}}安装完毕之后,我们需要命令检查是否生效。nginx -t检测没有报错重启NGINX。service nginx reload重启NGINX后生效。第二、Apache WEB引擎安装SSL我们需要先在Apache根目录下 conf/httpd.conf 文件,找到 #LoadModule ssl_module modules/mod_ssl.so 和 #Include conf/extra/httpd-ssl.conf,去掉前面的#号注释;编辑Apache根目录下 conf/extra/httpd-ssl.conf 文件。修改如下内容:<VirtualHost www.domain.com:443>DocumentRoot “/var/www/html"ServerName www.domain.comSSLEngine onSSLCertificateFile /usr/local/apache/conf/2_www.domain.com_cert.crtSSLCertificateKeyFile /usr/local/apache/conf/3_www.domain.com.keySSLCertificateChainFile /usr/local/apache/conf/1_root_bundle.crt</VirtualHost>配置完成后,重新启动 Apache 就可以使用HTTPS网址访问。相对来说好像NGINX比较简单一些。第三、实例参考安装SSL过程1、购买证书后合并证书我们在购买证书后会看到.crt和.ca-bundle文件,需要将这2个文件合并到一个文件.crt中。2、上传证书我们需要将上面的CRT文件和KEY文件上传到网站服务器目录中,这个具体放到哪里没事。因为我们可以在引用的时候调用对应自己的路径。3、引用CONF文件路径listen 443 ssl http2; ssl_certificate /usr/local/nginx/conf/ssl/网站域名.crt; ssl_certificate_key /usr/local/nginx/conf/ssl/网站域名.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; ssl_prefer_server_ciphers on; ssl_session_timeout 10m; ssl_session_cache builtin:1000 shared:SSL:10m; ssl_buffer_size 1400; add_header Strict-Transport-Security max-age=15768000; ssl_stapling on; ssl_stapling_verify on;这里我们将配置文件贴到conf文件中,然后检测Nginx,后没有问题就重启生效。4、收尾工作如果我们需要强制HTTP跳转至HTTPS,则需要在conf配置文件中设置301强制跳转。这样,我们才可以完成整个的SSL证书安装,是不是也不是很难?本文可以参考的文献:1、申请腾讯云免费DV SSL证书及Apache/Nginx/IIS/Tomcat证书安装2、手动配置安装SSL证书实现HTTPS加密网址 ...

March 15, 2019 · 1 min · jiezi

深入 Nginx 之配置篇

常用配置项在工作中,我们与 Nginx 打交道更多的是通过其配置文件来进行。那么掌握这些配置项各自的作用就很有必要了。首先,nginx.conf 的内容通常是这样的:… … #核心摸块events { #事件模块 …}http { # http 模块 server { # server块 location [PATTERN] { # location块 … } location [PATTERN] { … } } server { … }}mail { # mail 模块 server { # server块 … }}我们依次看一下每个模块一般有哪些配置项:核心模块user admin; #配置用户或者组。worker_processes 4; #允许生成的进程数,默认为1 pid /nginx/pid/nginx.pid; #指定 nginx 进程运行文件存放地址 error_log log/error.log debug; #错误日志路径,级别。事件模块events { accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off use epoll; #事件驱动模型select|poll|kqueue|epoll|resig worker_connections 1024; #最大连接数,默认为512}http 模块http { include mime.types; #文件扩展名与文件类型映射表 default_type application/octet-stream; #默认文件类型,默认为text/plain access_log off; #取消服务日志 sendfile on; #允许 sendfile 方式传输文件,默认为off,可以在http块,server块,location块。 sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。 keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块。 server { keepalive_requests 120; #单连接请求上限次数。 listen 80; #监听端口 server_name 127.0.0.1; #监听地址 index index.html index.htm index.php; root your_path; #根目录 location ~ .php$ { fastcgi_pass unix:/var/run/php/php7.1-fpm.sock; #fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; } }}配置项解析worker_processesworker_processes 用来设置 Nginx 服务的进程数。该值推荐使用 CPU 内核数。worker_cpu_affinityworker_cpu_affinity 用来为每个进程分配CPU的工作内核,参数有多个二进制值表示,每一组代表一个进程,每组中的每一位代表该进程使用CPU的情况,1代表使用,0代表不使用。所以我们使用 worker_cpu_affinity 0001 0010 0100 1000;来让进程分别绑定不同的核上。默认情况下worker进程不绑定在任何一个CPU上。worker_rlimit_nofile设置毎个进程的最大文件打开数。如果不设的话上限就是系统的 ulimit –n的数字,一般为65535。worker_connections设置一个进程理论允许的最大连接数,理论上越大越好,但不可以超过 worker_rlimit_nofile 的值。use epoll设置事件驱动模型使用 epoll。epoll 是 Nginx 支持的高性能事件驱动库之一。是公认的非 常优秀的事件驱动模型。accept_mutex off关闭网络连接序列化,当其设置为开启的时候,将会对多个 Nginx 进程接受连接进行序列化,防止多个进程对连接的争抢。当服务器连接数不多时,开启这个参数会让负载有一定程度的降低。但是当服务器的吞吐量很大时,为了效率,请关闭这个参数;并且关闭这个参数的时候也可以让请求在多个 worker 间的分配更均衡。所以我们设置 accept_mutex off;multi_accept on设置一个进程可同时接受多个网络连接Sendfile onSendfile是 Linux2.0 以后的推出的一个系统调用,它能简化网络传输过程中的步骤,提高服务器性能。不用 sendfile的传统网络传输过程:硬盘 >> kernel buffer >> user buffer >> kernel socket buffer >> 协议栈用 sendfile()来进行网络传输的过程:硬盘 >> kernel buffer (快速拷贝到 kernelsocket buffer) >>协议栈tcp_nopush on;设置数据包会累积一下再一起传输,可以提高一些传输效率。 tcp_nopush 必须和 sendfile 搭配使用。tcp_nodelay on;小的数据包不等待直接传输。默认为on。看上去是和 tcp_nopush 相反的功能,但是两边都为 on 时 nginx 也可以平衡这两个功能的使用。keepalive_timeoutHTTP 连接的持续时间。设的太长会使无用的线程变的太多。这个根据服务器访问数量、处理速度以及网络状况方面考虑。send_timeout设置 Nginx 服务器响应客户端的超时时间,这个超时时间只针对两个客户端和服务器建立连接后,某次活动之间的时间,如果这个时间后,客户端没有任何活动,Nginx服务器将关闭连接gzip on启用 gzip,对响应数据进行在线实时压缩,减少数据传输量。gzip_disable “msie6"Nginx服务器在响应这些种类的客户端请求时,不使用 Gzip 功能缓存应用数据,gzip_disable “msie6”对IE6浏览器的数据不进行 GZIP 压缩。常用的配置项大致这些,对于不同的业务场景,有的需要额外的其他配置项,这里不做展开。其他http 配置里有 location 这一项,它是用来根据请求中的 uri 来为其匹配相应的处理规则。location 查找规则location = / { # 精确匹配 / ,主机名后面不能带任何字符串 [ config A ]}location / { # 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求 # 但是正则和最长字符串会优先匹配 [ config B ]}location /documents/ { # 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索 # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条 [ config C ]}location ~ /documents/Abc { # 匹配任何以 /documents/Abc 开头的地址,匹配符合以后,还要继续往下搜索 # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条 [ config CC ]}location ^~ /images/ { # 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。 [ config D ]}location * .(gif|jpg|jpeg)$ { # 匹配所有以 gif,jpg或jpeg 结尾的请求 # 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^ 到达不了这一条正则 [ config E ]}location /images/ { # 字符匹配到 /images/,继续往下,会发现 ^~ 存在 [ config F ]}location /images/abc { # 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在 # F与G的放置顺序是没有关系的 [ config G ]}location ~ /images/abc/ { # 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用 [ config H ]}正则查找优先级从高到低依次如下:“ = ” 开头表示精确匹配,如 A 中只匹配根目录结尾的请求,后面不能带任何字符串。“ ^~ ” 开头表示uri以某个常规字符串开头,不是正则匹配“ ~ ” 开头表示区分大小写的正则匹配;“ ~* ”开头表示不区分大小写的正则匹配“ / ” 通用匹配, 如果没有其它匹配,任何请求都会匹配到负载均衡配置Nginx 的负载均衡需要用到 upstream 模块,可通过以下配置来实现:upstream test-upstream { ip_hash; # 使用 ip_hash 算法分配 server 192.168.1.1; # 要分配的 ip server 192.168.1.2;}server { location / { proxy_pass http://test-upstream; }}上面的例子定义了一个 test-upstream 的负载均衡配置,通过 proxy_pass 反向代理指令将请求转发给该模块进行分配处理。 ...

March 14, 2019 · 2 min · jiezi

Nginx访问控制与参数调优

Nginx全局变量Nginx中有很多的全局变量,可以通过$变量名来使用。下面列举一些常用的全局变量:变量说明$args请求中的参数,如www.123.com/1.php?a=1&b=2的$args就是a=1&b=2$content_lengthHTTP请求信息里的”Content-Length”$conten_typeHTTP请求信息里的”Content-Type”$document_rootnginx虚拟主机配置文件中的root参数对应的值$document_uri当前请求中不包含指令的URI,如www.123.com/1.php?a=1&b=2的$document_uri就是1.php,不包含后面的参数$host主机头,也就是域名$http_user_agent客户端的详细信息,也就是浏览器的标识,用curl -A可以指定$http_cookie客户端的cookie信息$limit_rate如果nginx服务器使用limit_rate配置了显示网络速率,则会显示,如果没有设置, 则显示0$remote_addr客户端的公网ip$remote_port客户端的port$remote_user如果nginx有配置认证,该变量代表客户端认证的用户名$request_body_file做反向代理时发给后端服务器的本地资源的名称$request_method请求资源的方式,GET/PUT/DELETE等$request_filename当前请求的资源文件的路径名称,相当于是$document_root/$document_uri的组合$request_uri请求的链接,包括$document_uri和$args$scheme请求的协议,如ftp,http,https$server_protocol客户端请求资源使用的协议的版本,如HTTP/1.0,HTTP/1.1,HTTP/2.0等$server_addr服务器IP地址$server_name服务器的主机名$server_port服务器的端口号$uri和$document_uri相同$http_referer客户端请求时的referer,通俗讲就是该请求是通过哪个链接跳过来的,用curl -e可以指定Nginx locationlocation作用location指令的作用是根据用户请求的URI来执行不同的应用。即根据用户请求的网站地址URL进行匹配,匹配成功就进行相应的操作。语法location的语法规则:location [=|||^] /uri/ { … }location匹配的变量是$uri关于几种字符的说明字符描述=表示精准匹配表示区分大小写的正则匹配~表示不区分大小写的正则匹配^表示uri以指定字符或字符串开头/通用匹配,任何请求都会匹配到规则优先级= 高于 ^ 高于 ~ 等于 ~ 高于 /示例1location = “/12.jpg” { … }如:www.syushin.com/12.jpg 匹配www.syushin.com/abc/12.jpg 不匹配location ^~ “/abc/” { … }如:www.syushin.com/abc/123.html 匹配www.syushin.com/a/abc/123.jpg 不匹配location ~ “png” { … }如:www.syushin.com/aaa/bbb/ccc/123.png 匹配www.syushin.com/aaa/png/123.html 匹配location ~ “png” { … }如:www.syushin.com/aaa/bbb/ccc/123.PNG 匹配www.syushin.com/aaa/png/123.html 匹配location /admin/ { … }如:www.syushin.com/admin/aaa/1.php 匹配www.syushin.com/123/admin/1.php 不匹配注意:有些资料上介绍location支持不匹配 !如: location ! ‘png’{ … }这是错误的,location不支持 !如果有这样的需求,可以通过if(location优先级小于if )来实现,如: if ($uri ! ‘png’) { … }访问控制web2.0时代,很多网站都是以用户为中心,网站允许用户发布内容到服务器。由于为用户开放了上传功能,因此有很大的安全风险,比如黑客上传木马程序等等。因此,访问控制就很有必要配置了。deny与allow字面上很容易理解就是拒绝和允许。Nginx的deny和allow指令是由ngx_http_access_module模块提供,Nginx安装默认内置了该模块。语法语法:allow/deny address | CIDR | unix: | all它表示,允许/拒绝某个ip或者一个ip段访问.如果指定unix:,那将允许socket的访问。注意:unix在1.5.1中新加入的功能。在nginx中,allow和deny的规则是按顺序执行的。 示例1:location /{ allow 192.168.0.0/24; allow 127.0.0.1; deny all;}说明:这段配置值允许192.168.0.0/24网段和127.0.0.1的请求,其他来源IP全部拒绝。示例2:location ~ “admin”{ allow 192.168.30.7; deny all}说明:访问的uri中包含admin的请求,只允许192.168.30.7这个IP的请求。基于location的访问控制日常上,访问控制基本是配合location来做配置的,直接例子吧。示例1:location /blog/{ deny all;}说明:针对/blog/目录,全部禁止访问,这里的deny all;可以改为return 403;.示例2location ~ “.bak|.ht”{ return 403;}说明:访问的uri中包含.bak字样的或者包含.ht的直接返回403状态码。测试链接举例:www.syushin.com/abc.bakwww.syushin.com/blog/123/.htalskdjf如果用户输入的URL是上面其中之一都会返回403。示例3location ~ (data|cache|tmp|image|attachment)..php${ deny all;}说明:请求的uri中包含data、cache、tmp、image、attachment并且以.php结尾的,全部禁止访问。测试链接举例:www.xxxxxx.com/aming/cache/1.phpwww.xxxxxxx.com/image/123.phpswww.xxxxxx.com/aming/datas/1.php基于$document_uri的访问控制前面介绍了内置变量$document_uri含义是当前请求中不包含指令的URI。如www.123.com/1.php?a=1&b=2的$document_uri就是1.php,不包含后面的参数。我们可以针对这个变量做访问控制。示例1if ($document_uri ~ “/admin/”){ return 403;}说明:当请求的uri中包含/admin/时,直接返回403.注意:if结构中不支持使用allow和deny。测试链接:1. www.xxxxx.com/123/admin/1.html 匹配2. www.xxxxx.com/admin123/1.html 不匹配3. www.xxxxx.com/admin.php 不匹配示例2if ($document_uri = /admin.php){ return 403;}说明:请求的uri为/admin.php时返回403状态码。测试链接:1. www.xxxxx.com/admin.php # 匹配2. www.xxxxx.com/123/admin.php # 不匹配示例3if ($document_uri ~ ‘/data/|/cache/..php$’){ return 403;}说明:请求的uri包含data或者cache目录,并且是php时,返回403状态码。测试链接:1. www.xxxxx.com/data/123.php # 匹配2. www.xxxxx.com/cache1/123.php # 不匹配基于$request_uri访问控制$request_uri比$docuemnt_uri多了请求的参数。主要是针对请求的uri中的参数进行控制。示例if ($request_uri ~ “gid=\d{9,12}”){ return 403;}说明:\d{9,12}是正则表达式,表示9到12个数字,例如gid=1234567890就符号要求。测试链接:1. www.xxxxx.com/index.php?gid=1234567890&pid=111 匹配2. www.xxxxx.com/gid=123 不匹配背景知识:曾经有一个客户的网站cc攻击,对方发起太多类似这样的请求:/read-123405150-1-1.html实际上,这样的请求并不是正常的请求,网站会抛出一个页面,提示帖子不存在。所以,可以直接针对这样的请求,return 403状态码。基于$http_user_agent的访问控制(反爬虫)user_agent可以简单理解成浏览器标识,包括一些蜘蛛爬虫都可以通过user_agent来辨识。假如观察访问日志,发现一些搜索引擎的蜘蛛对网站访问特别频繁,它们并不友好。为了减少服务器的压力,其实可以把除主流搜索引擎蜘蛛外的其他蜘蛛爬虫全部封掉。示例if ($user_agent ~ ‘YisouSpider|MJ12bot/v1.4.2|YoudaoBot|Tomato’){ return 403;}说明:user_agent包含以上关键词的请求,全部返回403状态码。测试:1. curl -A “123YisouSpider1.0"2. curl -A “MJ12bot/v1.4.1"基于$http_referer的访问控制$http_referer除了可以实现防盗链的功能外,还可以做一些特殊的需求。比如:网站被黑挂马,搜索引擎收录的网页是有问题的,当通过搜索引擎点击到网站时,却显示一个博彩网站。由于查找木马需要时间,不能马上解决,为了不影响用户体验,可以针对此类请求做一个特殊操作。比如,可以把从百度访问的链接直接返回404状态码,或者返回一段html代码。示例if ($http_referer ~ ‘baidu.com’){ return 404;}或者if ($http_referer ~ ‘baidu.com’){ return 200 “<html><script>window.location.href=’//$host$request_uri’;</script></html>”;}Nginx参数优化Nginx作为高性能web服务器,即使不特意调整配置参数也可以处理大量的并发请求。当然,配置调优会使Nginx性能更加强悍,配置参数需要结合服务器硬件性能等做参考。worker进程优化worker_processes num;该参数表示启动几个工作进程,建议和本机CPU核数保持一致,每一核CPU处理一个进程,num表示数字。worker_rlimit_nofile它表示Nginx最大可用的文件描述符个数,需要配合系统的最大描述符,建议设置为102400。还需要在系统里执行ulimit -n 102400才可以。也可以直接修改配置文件/etc/security/limits.conf修改增加:#* soft nofile 655350 (去掉前面的#)#* hard nofile 655350 (去掉前面的#)worker_connections该参数用来配置每个Nginx worker进程最大处理的连接数,这个参数也决定了该Nginx服务器最多能处理多少客户端请求(worker_processes * worker_connections)建议把该参数设置为10240,不建议太大。http/tcp连接数优化use epoll使用epoll模式的事件驱动模型,该模型为Linux系统下最优方式。multi_accept on使每个worker进程可以同时处理多个客户端请求。sendfile on使用内核的FD文件传输功能,可以减少user mode和kernel mode的切换,从而提升服务器性能。tcp_nopush on当tcp_nopush设置为on时,会调用tcp_cork方法进行数据传输。使用该方法会产生这样的效果:当应用程序产生数据时,内核不会立马封装包,而是当数据量积累到一定量时才会封装,然后传输。tcp_nodelay on不缓存data-sends(关闭 Nagle 算法),这个能够提高高频发送小数据报文的实时性。(关于Nagle算法)【假如需要频繁的发送一些小包数据,比如说1个字节,以IPv4为例的话,则每个包都要附带40字节的头,也就是说,总计41个字节的数据里,其中只有1个字节是我们需要的数据。为了解决这个问题,出现了Nagle算法。它规定:如果包的大小满足MSS,那么可以立即发送,否则数据会被放到缓冲区,等到已经发送的包被确认了之后才能继续发送。通过这样的规定,可以降低网络里小包的数量,从而提升网络性能。keepalive_timeout定义长连接的超时时间,建议30s,太短或者太长都不一定合适,当然,最好是根据业务自身的情况来动态地调整该参数。keepalive_requests定义当客户端和服务端处于长连接的情况下,每个客户端最多可以请求多少次,可以设置很大,比如50000.reset_timeout_connection on设置为on的话,当客户端不再向服务端发送请求时,允许服务端关闭该连接。client_body_timeout客户端如果在该指定时间内没有加载完body数据,则断开连接,单位是秒,默认60,可以设置为10。send_timeout这个超时时间是发送响应的超时时间,即Nginx服务器向客户端发送了数据包,但客户端一直没有去接收这个数据包。如果某个连接超过send_timeout定义的超时时间,那么Nginx将会关闭这个连接。单位是秒,可以设置为3。压缩对于纯文本的内容,Nginx是可以使用gzip压缩的。使用压缩技术可以减少对带宽的消耗。由ngx_http_gzip_module模块支持配置如下:gzip on; //开启gzip功能gzip_min_length 1024; //设置请求资源超过该数值才进行压缩,单位字节gzip_buffers 16 8k; //设置压缩使用的buffer大小,第一个数字为数量,第二个为每个buffer的大小gzip_comp_level 6; //设置压缩级别,范围1-9,9压缩级别最高,也最耗费CPU资源gzip_types text/plain application/x-javascript text/css application/xml image/jpeg image/gif image/png; //指定哪些类型的文件需要压缩gzip_disable “MSIE 6.”; //IE6浏览器不启用压缩测试:curl -I -H “Accept-Encoding: gzip, deflate” http://www.xxxxx.com/1.css日志错误日志级别调高,比如crit级别,尽量少记录无关紧要的日志。对于访问日志,如果不要求记录日志,可以关闭,静态资源的访问日志关闭静态文件过期对于静态文件,需要设置一个过期时间,这样可以让这些资源缓存到客户端浏览器,在缓存未失效前,客户端不再向服务期请求相同的资源,从而节省带宽和资源消耗。配置示例如下:location ~* ^.+.(gif|jpg|png|css|js)$ { expires 1d; //1d表示1天,也可以用24h表示一天。}访问控制和参数调优只记录其中一些部分,有些可能会在工作中用到,SSL的配置后续再作笔记吧,春招笔试好难呀,努力学习吧… ...

March 14, 2019 · 2 min · jiezi

阿里巴巴基于 Nacos 实现环境隔离的实践

随着Nacos 0.9版本的发布,Nacos 离正式生产版本(GA)又近了一步,其实已经有不少企业已经上了生产,例如虎牙直播。本周三(今天),晚上 19:00~21:00 将会在 Nacos 钉钉群(群号:21708933)直播 Nacos 1.0.0 所有发布特性的预览以及升级和使用上的指导。Nacos环境隔离通常,企业研发的流程是这样的:先在测试环境开发和测试功能,然后灰度,最后发布到生产环境。并且,为了生产环境的稳定,需要将测试环境和生产环境进行隔离,此时,必然会遇到问题是多环境问题,即:多个环境的数据如何隔离?如何优雅的隔离?(不需要用户做任何改动)本文将就 Nacos 环境隔离,向大家介绍阿里在这方面的实践经验。什么是环境?说到环境隔离,首先应该定义好什么是环境。环境这个词目前还没有一个比较统一的定义,有些公司叫环境,在阿里云上叫 region,在 Kubernetes 架构中叫 namespace。本文认为,环境是逻辑上或物理上独立的一整套系统,这套系统中包含了处理用户请求的全部组件,例如网关、服务框架、微服务注册中心、配置中心、消息系统、缓存、数据库等,可以处理指定类别的请求。举个例子,很多网站都会有用户 ID 的概念,可以按照用户 ID 划分,用户 ID 以偶数结尾的请求全部由一套系统处理,而奇数结尾的请求由另一套系统处理。如下图所示。 我们这里说的环境隔离是指物理隔离,即不同环境是指不同的机器集群。环境隔离有什么用上一节定义了环境的概念,即一套包含了处理用户请求全部必要组件的系统,用来处理指定类别的请求。本节跟大家讨论一下环境隔离有哪些好处。从概念的定义可以看出,环境隔离至少有三个方面的好处:故障隔离、故障恢复、灰度测试;故障隔离首先,因为环境是能够处理用户请求的独立组件单元,也就是说用户请求的处理链路有多长,都不会跳出指定的机器集群。即使这部分机器故障了,也只是会影响部分用户,从而把故障隔离在指定的范围内。如果我们按照用户id把全部机器分为十个环境,那么一个环境出问题,对用户的影响会降低为十分之一,大大提高系统可用性。故障恢复环境隔离的另一个重要优势是可以快速恢复故障。当某个环境的服务出现问题之后,可以快速通过下发配置,改变用户请求的路由方向,把请求路由到另一套环境,实现秒级故障恢复。当然,这需要一个强大的分布式系统支持,尤其是一个强大的配置中心(如Nacos),需要快速把路由规则配置数据推送到全网的应用进程。灰度测试灰度测试是研发流程中不可或缺的一个环节。传统的研发流程中,测试和灰度环节,需要测试同学做各种各样的配置,如绑定host、配置jvm参数、环境变量等等,比较麻烦。经过多年的实践,阿里巴巴内部的测试和灰度对开发和测试非常友好,通过环境隔离功能来保证请求在指定的机器集群处理,开发和测试不需要做任何做任何配置,大大提高了研发效率。Nacos如何做环境隔离前两节讲到了环境的概念和环境隔离的作用,本节介绍如何基于 Nacos,实现环境的隔离。Nacos 脱胎于阿里巴巴中间件部门的软负载小组,在环境隔离的实践过程中,我们是基于 Nacos 去隔离多个物理集群的,同时,在 Nacos 客户端不需要做任何代码改动的情况下,就可以实现环境的自动路由。开始前,我们先做一些约束:一台机器上部署的应用都在一个环境内;一个应用进程内默认情况下只连一个环境的 Nacos;通过某种手段可以拿到客户端所在机器 IP;用户对机器的网段有规划;基本原理是:网络中 32 位的 IPV4 可以划分为很多网段,如192.168.1.0/24,并且一般中大型的企业都会有网段规划,按照一定的用途划分网段。我们可以利用这个原理做环境隔离,即不同网段的 IP 属于不同的环境,如192.168.1.0/24属于环境A, 192.168.2.0/24属于环境B等。Nacos 有两种方式初始化客户端实例,一种是直接告诉客户端 Nacos 服务端的IP;另一种是告诉客户端一个 Endpoint,客户端通过 HTTP 请求到 Endpoint,查询 Nacos 服务端的 IP 列表。这里,我们利用第二种方式进行初始化。增强 Endpoint 的功能。在 Endpoint 端配置网段和环境的映射关系,Endpoint 在接收到客户端的请求后,根据客户端的来源 IP 所属网段,计算出该客户端的所属环境,然后找到对应环境的 IP 列表返回给客户端。如下图一个环境隔离server的示例上面讲了基于IP段做环境隔离的约束和基本原理,那么如何实现一个地址服务器呢。最简单的方法是基于nginx实现,利用nginx的geo模块,做IP端和环境的映射,然后利用nginx返回静态文件内容。安装nginx http://nginx.org/en/docs/install.html在nginx-proxy.conf中配置geo映射,参考这里geo $env { default “”; 192.168.1.0/24 -env-a; 192.168.2.0/24 -env-b;}配置nginx根路径及转发规则,这里只需要简单的返回静态文件的内容;# 在http模块中配置根路径root /tmp/htdocs;# 在server模块中配置location / { rewrite ^(.*)$ /$1$env break;}配置Nacos服务端IP列表配置文件,在/tmp/hotdocs/nacos目录下配置以环境名结尾的文件,文件内容为IP,一行一个$ll /tmp/hotdocs/nacos/total 0-rw-r–r– 1 user1 users 0 Mar 5 08:53 serverlist-rw-r–r– 1 user1 users 0 Mar 5 08:53 serverlist-env-a-rw-r–r– 1 user1 users 0 Mar 5 08:53 serverlist-env-b$cat /tmp/hotdocs/nacos/serverlist192.168.1.2192.168.1.3验证curl ’localhost:8080/nacos/serverlist'192.168.1.2192.168.1.3至此, 一个简单的根据IP网段做环境隔离的示例已经可以工作了,不同网段的nacos客户端会自动获取到不同的Nacos服务端IP列表,实现环境隔离。这种方法的好处是用户不需要配置任何参数,各个环境的代码和配置是一样的,但需要提供底层服务的同学做好网络规划和相关配置。总结本文简单介绍了环境隔离的概念,环境隔离的三个好处以及 Nacos 如何基于网段做环境隔离。最后,给出了一个基于 Nginx 做 Endpoint 服务端的环境隔离配置示例。需要注意的是,本文只是列出了一种可行的方法,不排除有更优雅的实现方法,如果大家有更好的方法,欢迎到Nacos 社区或官网贡献方案。本文作者:正己,GitHub ID @jianweiwang,负责 Nacos 的开发和社区维护,阿里巴巴高级开发工程师。本文作者:中间件小哥阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 14, 2019 · 1 min · jiezi

Nginx反向代理与负载均衡

反向代理反向代理指的是以代理服务器接收用户的的访问请求,代理用户向内部服务器重新发起请求,最后把内部服务器的响应信息返回给用户。这样,代理服务器对外就表现为一台服务器,而访问内部服务器的客户端用的就是代理服务器,而不是真实网站访问用户。为什么使用反向代理可以起到保护网站安全的作用,因为任何来自Internet的请求都必须先经过代理服务器。通过缓存静态资源,加速Web请求。实现负载均衡反向代理例子环境说明假如有AB两个服务器。A服务器提供web资源,并且只给内网访问。B服务器有两块网卡,一块与A服务器在一个内网,以块是外网。此时,用户C想直接访问A服务器是行不通的。这时就可以通过B服务器代理用户C的请求去访问A服务器了。hostname网卡IP说明moli-04ens33192.168.30.6内网IP,代理服务器moli-04ens37192.168.93.129外网IP,代理服务器moli-05ens33192.168.30.7内网服务器两台机器都安装nginxmoli-05服务器访问是wordpress博客,域名blog.syushin.org虚拟机实验环境,就都关闭防火墙了配置虚拟主机moli-04机器上编辑虚拟主机配置文件,内容如下:[root@moli-04 extra]$ cat blog.syushin.org.conf server{ listen 80; server_name blog.syushin.org; location / { proxy_pass http://192.168.30.7; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }更改hosts文件windows上修改hosts文件,添加配置192.168.93.129 blog.syushin.org浏览器测试访问地址是192.168.93.129,出现的界面的05机器的页面,配置成功。负载均衡负载均衡的功能对用户的访问请求进行调度管理对用户的访问请求进行压力分担负载均衡集群在运行时,一般是通过一个或者多个前端负载均衡器将客户访问请求发到后端的一组服务器上。Nginx负载均衡严格来说,Nginx仅仅是作为Nginx Proxy反向代理的使用的,但是因为这个反向代理功能表现的效果是负载均衡机器的效果,因此nginx负载均衡是特殊的反向代理。实现Nginx负载均衡的主要组件:Nginx模块说明ngx_http_proxy_moduleproxy代理模块,用于把请求发送给服务器节点或upstream服务器池ngx_http_upstream_module负载均衡模块,可以实现网站的负载均衡功能及节点的健康检查upstream模块介绍ngx_http_upstream_module模块支持的代理方式有proxy_pass,fastcgi_pass等,主要使用proxy_pass。upstream模块允许nginx定义一组或多组节点服务器组,使用时通过proxy_pass代理把网站的请求发送到定义好的对应的节点组中。示例:创建节点服务器池upstream blog { server 192.168.30.5:80 weight=5; server 192.168.30.6:81 weight=10; server 192.168.30.7:82 weight=15;}upstream:创建节点服务器组的关键字,必须有;blog:节点服务器组的名字,必须有,可自定义名字;server:关键字,后面可加IP或者域名或者IP:端口,不指定端口默认80;weight:权重,数值越大被分配的请求越多。默认为1设置节点服务器的状态值除了weight之外,还有:max_fails:允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误.fail_timeout:max_fails次失败后,暂停的时间。down:表示当前的节点服务器不参与负载,标志机器永远不可用,可配合iP_hash使用backup:其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。使用域名的upstreamupstream blog2{ server www.syushin.com weight=5; server blog.syushin.org down; server blog.syushin.cc backup;}调度算法rr轮询(默认调度算法,静态调度算法)按客户端请求顺序把客户端的请求逐一分配到不同的后端节点服务器。wrr(权重轮询,静态调度算法)在rr轮询的基础上加上权重,使用该算法的时候,权重和用户访问成正比,权重值越大,被转发的请求就越多。比如有30个请求,2台服务器A(10.0.0.1)和B(10.0.0.2),如果希望A处理10个请求,B处理20个请求,可以这样定义:upstream pools{ server 10.0.0.1 weight=1; server 10.0.0.2 weight=2;}ip_hash(静态调度算法)每个请求按客户端IP的hash结果分配,当新的请求到达,先将客户端IP通过哈希算法哈希出一个值,在随后的分配客户端请求中,客户IP的哈希值只要相同,就会被分配到同一台服务器。upstream blog_pool{ ip_hash; server 192.168.30.5:80; server 192.168.30.6:8090;}注意:当使用ip_hash时,不能有weight和backup。least_conn算法least_conn算法会根据后端服务器的连接数来觉得分配情况,哪台服务器连接数最少就分发多的请求。调度算法除了上面所列的(常用)还有很多,就不一一列举了。http_proxy_module模块http_proxy_module可以将请求转发到另外一台服务器,在反向代理中,会通过location功能匹配指定的URI,然后把收到符合匹配的URI的请求通过proxy_pass抛给定义好的upstream节点池。http_proxy模块参数参数说明proxy_set_header设置http请求header项传给后端服务器节点,例如:可实现让代理后端的服务器节点获取访问客户端用户的真实IP地址client_body_buffer_size用于指定客户端请求主体缓冲区大小proxy_connect_timeout表示反向代理后端节点服务器连接的超时时间,即发起握手等候响应的超时时间proxy_send_timeout表示代理后端服务器的数据回传时间,即在规定时间内后端服务器必须传完所有数据,否则nginx将断开这个连接proxy_read_timeout设置nginx从代理的后端服务器获取信息的时间,表示连接建立成功后,nginx等待后端服务器的响应时间,其实是nginx已经进入后端的排队之中等候处理的时间proxy_buffer_size设置缓冲区大小,默认该缓冲区大小等于指令proxy_buffers设置的大小proxy_buffers设置缓冲区的数量和大小,nginx从代理的后端服务器获取的响应信息,会设置到缓冲区proxy_busy_buffers_size用于设置相同很忙时可以使用的proxy_buffers大小,官方推荐的大小为 proxy_buffers * 2proxy_trmp_file_write_size指定proxy缓存临时文件的大小

March 13, 2019 · 1 min · jiezi

vue代理模式 nginx配置

前言前端使用vue.js开发,后端使用tornado框架提供restful API, vue.js使用代理;如 当前端js请求http://192.168.9.62:9000/api/orders时候后端通过nginx配置去请求http://192.168.9.62:9000/v1/orders需求nginx需要将前端js请求http://192.168.9.62:9000/api/orders 转发 http://192.168.9.62:9000/v1/ordersnginx配置upstream svrs { # 负载均衡的servers server 127.0.0.1:8001; server 127.0.0.1:8002; server 127.0.0.1:8003; server 127.0.0.1:8004; server 127.0.0.1:8005; server 127.0.0.1:8006; server 127.0.0.1:8007; server 127.0.0.1:8008; server 127.0.0.1:8009; server 127.0.0.1:8010;}server { listen 9000; server_name _; location /v1 { # 直接访问 http://192.168.9.62:9000/v1/orders 的配置 proxy_pass_header Server; proxy_redirect off; proxy_set_header Host $http_host; proxy_set_header x-forwarded-for $remote_addr; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 120; proxy_pass http://svrs; } location /api { # 访问http://192.168.9.62:9000/api/orders 的配置 # 重写 api/ –> v1/ rewrite ^.+api/?(.*)$ /v1/$1 break; # 后端的API服务器 proxy_pass http://svrs; } location / { # 前端打包的静态文件 root /home/xx/xx_web; index index.html; }} ...

March 13, 2019 · 1 min · jiezi

【每日学习记录】使用录像设备记录每天的学习

在这里使用学而思网校的录像设备,记录每天学习的内容:2019-03-07 ~ 2019-03-0803-07内存管理一 by 陈雷03-08内存管理二 by 陈雷2019-03-11 ~ 2019-03-1503-11内存管理三 by 陈雷03-12基本变量 by 陈雷03-13字符串 by 景罗

March 13, 2019 · 1 min · jiezi

2小时快速搭建后端接口报警系统(基于阿里云日志服务分析nginx访问日志)

目标后端任一接口一分钟内5xx响应超过一定的量,马上收到报警提示报警及慢接口有详细列表可以查看低成本。几年前公司的日志报警系统是自研的,开发成本比较高,也没有达到阿里云日志服务这种产品化程度机器部署情况阿里云EC服务器功能概述阿里云日志服务,采集并分析nginx访问日志;写日志分析SQL,每分钟调度执行,符合条件就触发报警;根据响应状态码提供:接口5xx响应报警、接口4xx响应报警;报警通知方式为钉钉群机器人,5xx跟4xx响应分别通知到专用的后端跟前端同学群;修改日志分析SQL,在专用dashboard展示相关报警请求的详细信息列表根据响应时间提供:慢响应请求列表,同样放到dashboard效果图钉钉群报警【c是符合条件的个数,st是响应状态码】阿里云日志服务仪表盘-5xx报警接口详情操作步骤配置日志采集新建Project;新建Logstore;配置nginx日志采集;日志路径:/path_to_logs/**/access.log模式:nginx配置;从线上nginx.conf文件里拷贝 log_format main,配置到页面;Topic生成方式:文件路径正则;自定义正则:/path_to_logs/([^/]+)/access.log,正好把域名提取出来。可参考生成主题Logtail机器组:配置nginx机器内网IPnginx机器安装Logtail采集器;参考文档五分钟快速入门分析Nginx日志日志服务(SLS)用户手册配置日志分析SQL及报警日志库》查询分析》查询,可以写SQL实时查询/分析,然后另存为告警配置告警条件配置告警通知。一个告警可配置多个通知列表,可以同时通知到钉钉群跟短信5xx报警SQL为了方便查看具体的错误接口,基于uri分组统计并报警,报警内容里包含uri信息为了方便确认严重程度,报警内容里包含响应状态码__topic__:www.xyz.com and status in [500 600) | select count(1) as c, avg(status) as st, case when strpos(request_uri, ‘?’) > 0 then split_part(request_uri, ‘?’, 1) else request_uri end as uri group by uri having count(1)>=5 order by count(1) desc分析SQL的写法可参考告警-实时监控Nginx访问日志实时分析简介。支持的SQL语法及计算函数都有告警条件配置告警通知配置上面的配置图可能会变,这个产品一直在进化,18年12月的时候发现有一次大的改版。dashboard相关报警请求的详细信息列表SQL:topic:www.xyz.com and status in [500 600) | select time_local, status, upstream_addr, topic as vhost, case when strpos(request_uri, ‘?’) > 0 then split_part(request_uri, ‘?’, 1) else request_uri end as uri order by time_local descdashboard慢响应分析SQL:topic:www.xyz.com and request_time > 0.3 | select count(1) as count, avg(request_time) as avg_request_time, min(topic) as vhost, case when strpos(request_uri, ‘?’) > 0 then split_part(request_uri, ‘?’, 1) else request_uri end as uri group by uri order by avg_request_time desc ...

March 13, 2019 · 1 min · jiezi

HTTP、PHP-FPM、与握手协议

HTTP请求的流程梳理用户输入url如http:www.baidu.com到浏览器,浏览器如chrom需要将其解析为ip地址才知道需要到哪里去访问哪个服务器。浏览器解析DNS步骤如下搜索浏览器自身的dns缓存,这个缓存缓存时间短,缓存数目有限。搜索操作系统的dns缓存读取host文件的dns映射(一般做本地开发映射都是修改这个文件来达到拦截浏览器请求到本地服务器的目的,从而使本地可以成功映射服务器地址)先本地网卡配置里的dns服务器发起域名解析请求,这里好像还有一套运营商的处理流程就不在展开了。下面好像还有一些流程,由于基本不会执行到这一步,一般所以dns运营商的dns服务器都会搞定的。解析失败,以上任何一步成功都会返回一个成功的ip地址浏览器以一个随机的端口享这个ip地址的特定端口(默认80)发起著名的TCP3次握手。关于一个http请求是如何到达nginx服务的流程大致如下:握手完成后的浏览器和服务器就可以愉快地发送http请求了,具体在nginx上流程如下:PHP-FPM在服务端出来请求中扮演了什么角色PHP、nginx与CGI协议对于一个PHP的web程序来说,web服务器(如:nginx)要想与它通信需要通过CGI协议。当一个web请求触达web服务器时,web服务器会创建一个CGI进程,CGI进程将web的请求按照固定的格式进行解析,然后写入标准输入(STDIN)和环境变量中,然后PHP启动的CGI解析器会从标准输入(STDIN)和环境变量中读取http请求的数据,所以$_SERVER才会有数据,然后做出相应的逻辑处理,然后将处理结果放入标准输出(STDOUT),CGI进程从STDOUT中读取响应数据然后传输给浏览器,这样服务端就完成了一次http请求。上面是CGI的实现流程,但是使用CGI协议的服务器在用户每次访问服务器的时候都需要fork/销毁CGI进程,必然照成多余的系统开销,所以FASTCGI就是为了解决这个问题的。什么是FastCGI协议FastCGI会创建一个常驻的master进程和多个worker进程,master进程负责管理和为worker进程反派任务,worker进程负责内部嵌入了CGI解析器用于解释php代码。PHP-FPM是一个FastCGI进程管理器,在LNMP体系中就是由它来实现FastCGI协议的。同样,它也会创建一个常驻的master进程和多个worker进程,master进程负责监听端口和接收来自nginx的请求,指派任务给worker进程。worker进程的负责解释php代码。PHP-FPM可以通过配置预先启动一定数量的worker进程,这样当http请求触达时就可以更快速的响应。Nginx关于FastCGI的配置nginx与PHP-FPM之间的通信一般通过其ngx_http_fastcgi_module模块来实现。其中fastcgi_pass用于设置fastcgi服务器的IP地址;fastcgi_param设置传入fastcgi服务器的参数。这个模块出现的配置问题一般集中在这一块。相对于并发状态下出现的问题,一般也都集中在fastcgi服务器上,具体表现为fastcgi服务器为了应对大量的http请求必须不停的fork新的worker进程,这时就需要考虑服务器可支持的最大链接数和最大打开文件数(可通过ulimit -n查看)以及php-fpm配置里的最低开启worker数和最高开启worker数的限制。高性能服务器可以在这个方向上调优。HTTP协议三次握手四次挥手的细节协议过程中客服端与服务端的状态图TCP的标志位说明标志位英文说明SYNsynchronous建立联机ACKacknowledgement确认PSHpush传送FINfinish结束RSTreset重置URGurgent紧急Sequence numbe-顺序号码Acknowledge number-确认号码TCP状态说明状态说明LISTEN侦听状态SYN_SEND发送连接请求[SYN=J]后等待匹配连接请求SYN_RECEIVED收到连接请求[SYN=J]后发送连接确认包[SYN=k,ack=J+1]后等待收到确认包[Ack=k+1]状态ESTABLISHED打开连接后,可以开始传输数据FIN_WAIT_1发起连接中断请求[FIN=M]后等待远程TCP确认时[Ack=M+1]状态FIN_WAIT_2收到远程中断确认[Ack=M+1]后,等待远程中断请求[FIN=N]CLOSE_WAIT收到连接中断请求[FIN=M]后未发送出中断确认包[Ack=M=1]状态TIME_WAIT发送确认远程中断请求[Ack=N+1]包后,进入等待状态,用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制大量TIME_WAIT的原因「常见性能异常」在四次挥手断开连接中,发起socket主动关闭的一方 socket将进入TIME_WAIT状态,TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),TIME_WAIT状态下的socket不能被回收使用.具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务.TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证。一般产生的原因是系统没有主动关闭连接,如mysql连接资源没有关闭关于网络链路中追踪异常用到的运维命令(以下显示的IP和端口均为假数据)Linux中查看socket的状态cat /proc/net/sockstat参数说明sockets:used已使用的所有协议套接字总量TCP:inuse正在使用(正在侦听)的TCP套接字数量。其值≤ netstat –lntgrep ^tcpwc –lTCP:orphan无主(不属于任何进程)的TCP连接数(无用、待销毁的TCP socket数)TCP:tw等待关闭的TCP连接数。其值等于netstat –antgrep TIME_WAITwc –lTCP:alloc已分配(已建立、已申请到sk_buff)的TCP套接字数量。其值等于netstat –antgrep ^tcpwc –lTCP:mem套接字缓冲区使用量UDP:inuse正在使用的UDP套接字数量FRAG使用的IP段数量查看当前tcp链接情况netstat -na | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}‘参数说明LISTEN正在监听状态CLOSE_WAIT对方主动关闭连接或者网络异常导致连接中断,这时我方的状态会变成CLOSE_WAIT 此时我方要调用close()来使得连接正确关闭ESTABLISHED建立连接,正在通信TIME_WAIT我方主动调用close()断开连接,收到对方确认后状态变为TIME_WAITtcp工具抓取网络请求包tcpdump -n port 3306mysql 主动断开链接11:38:45.693382 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [F.], seq 123, ack 144, win 227, options [nop,nop,TS val 3000355 ecr 2997359], length 0 # MySQL发送fin包给我11:38:45.740958 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 124, win 229, options [nop,nop,TS val 3000360 ecr 3000355], length 0 # 我回复ack给它11:38:45.740960 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [F.], ack 125, win 231, options [nop,nop,TS val 3000360 ecr 3000355], length 0 # MySQL发送fin包给客户端11:38:45.740965 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 125, win 229, options [nop,nop,TS val 3000360 ecr 3000355], length 0 # 客户端回复ack给我……src > dst: flags data-seqno ack window urgent options# 发生了 3次握手11:38:15.679863 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [S], seq 4065722321, win 29200, options [mss 1460,sackOK,TS val 2997352 ecr 0,nop,wscale 7], length 011:38:15.679923 IP 172.18.0.3.3306 > 172.18.0.5.38822: Flags [S.], seq 780487619, ack 4065722322, win 28960, options [mss 1460,sackOK,TS val 2997352 ecr 2997352,nop,wscale 7], length 011:38:15.679936 IP 172.18.0.5.38822 > 172.18.0.3.3306: Flags [.], ack 1, win 229, options [nop,nop,TS val 2997352 ecr 2997352], length 0参数说明src > dst表明从源地址到目的地址flags是TCP包中的标志信息,S 是SYN标志, F(FIN), P(PUSH) , R(RST) “."(没有标记)data-seqno是数据包中的数据的顺序号ack是下次期望的顺序号window是接收缓存的窗口大小urgent表明数据包中是否有紧急指针options是选项 ...

March 12, 2019 · 1 min · jiezi

PHP-FPM 与 Nginx 的通信机制总结

PHP-FPM 介绍CGI 协议与 FastCGI 协议每种动态语言( PHP,Python 等)的代码文件需要通过对应的解析器才能被服务器识别,而 CGI 协议就是用来使解释器与服务器可以互相通信。PHP 文件在服务器上的解析需要用到 PHP 解释器,再加上对应的 CGI 协议,从而使服务器可以解析到 PHP 文件。由于 CGI 的机制是每处理一个请求需要 fork 一个 CGI 进程,请求结束再kill掉这个进程,在实际应用上比较浪费资源,于是就出现了CGI 的改良版本 FastCGI,FastCGI 在请求处理完后,不会 kill 掉进程,而是继续处理多个请求,这样就大大提高了效率。PHP-FPM 是什么PHP-FPM 即 PHP-FastCGI Process Manager, 它是 FastCGI 的实现,并提供了进程管理的功能。进程包含 master 进程和 worker 进程两种;master 进程只有一个,负责监听端口,接收来自服务器的请求,而 worker 进程则一般有多个(具体数量根据实际需要进行配置),每个进程内部都会嵌入一个 PHP 解释器,是代码真正执行的地方。Nginx 与 php-fpm 通信机制当我们访问一个网站(如 www.test.com)的时候,处理流程是这样的:www.test.com | | Nginx | |路由到 www.test.com/index.php | |加载 nginx 的 fast-cgi 模块 | |fast-cgi 监听 127.0.0.1:9000 地址 | |www.test.com/index.php 请求到达 127.0.0.1:9000 | | 等待处理…Nginx 与 php-fpm 的结合在 Linux 上,nginx 与 php-fpm 的通信有 tcp socket 和 unix socket 两种方式。tcp socket 的优点是可以跨服务器,当 nginx 和 php-fpm 不在同一台机器上时,只能使用这种方式。Unix socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信,这种方式需要在 nginx配置文件中填写 php-fpm 的 socket 文件位置。两种方式的数据传输过程如下图所示:二者的不同:由于 Unix socket 不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。所以其效率比 tcp socket 的方式要高,可减少不必要的 tcp 开销。不过,unix socket 高并发时不稳定,连接数爆发时,会产生大量的长时缓存,在没有面向连接协议的支撑下,大数据包可能会直接出错不返回异常。而 tcp 这样的面向连接的协议,可以更好的保证通信的正确性和完整性。Nginx 与 php-fpm 结合只需要在各自的配置文件中做设置即可:1、 Nginx 中的配置以 tcp socket通信为例server { listen 80; #监听 80 端口,接收http请求 server_name www.test.com; #就是网站地址 root /usr/local/etc/nginx/www/huxintong_admin; # 准备存放代码工程的路径 #路由到网站根目录 www.test.com 时候的处理 location / { index index.php; #跳转到 www.test.com/index.php autoindex on; } #当请求网站下 php 文件的时候,反向代理到 php-fpm location ~ .php$ { include /usr/local/etc/nginx/fastcgi.conf; #加载 nginx 的 fastcgi 模块 fastcgi_intercept_errors on; fastcgi_pass 127.0.0.1:9000; # tcp 方式,php-fpm 监听的 IP 地址和端口 # fasrcgi_pass /usr/run/php-fpm.sock # unix socket 连接方式 }}2、 php-fpm 的配置listen = 127.0.0.1:9000# 或者下面这样listen = /var/run/php-fpm.sock注意,在使用 unix socket 方式连接时,由于 socket 文件本质上是一个文件,存在权限控制的问题,所以需要注意 nginx进程的权限与 php-fpm 的权限问题,不然会提示无权限访问。(在各自的配置文件里设置用户)通过以上配置即可完成 php-fpm 与 nginx 的通信。在应用中的选择如果是在同一台服务器上运行的 nginx 和 php-fpm,且并发量不高(不超过1000),选择unix socket,以提高 nginx 和 php-fpm 的通信效率。如果是面临高并发业务,则考虑选择使用更可靠的 tcp socket,以负载均衡、内核优化等运维手段维持效率。若并发较高但仍想用 unix socket 时,可通过以下方式提高 unix socket 的稳定性。1)将sock文件放在 /dev/shm 目录下,此目录下将 sock 文件放在内存里面,内存的读写更快。2)提高 backlogbacklog 默认位 128,1024 这个值换成自己正常的 QPS,配置如下。nginx.conf 文件中server { listen 80 default backlog = 1024; }php-fpm.conf 文件中listen.backlog = 10243)增加 sock 文件和 php-fpm 实例在 /dev/shm 新建一个 sock 文件,在 nginx 中通过 upstream 模块将请求负载均衡到两个 sock 文件,并且将两个 sock 文件分别对应到两套 php-fpm 实例上。 ...

March 11, 2019 · 2 min · jiezi

网关 rate limit 网络速率限制方案

网关 rate limit 网络速率限制方案一、网络限流算法 在计算机领域中,限流技术(time limiting)被用来控制网络接口收发通讯数据的速率。用这个方法来优化性能、较少延迟和提高带宽等。 在互联网领域中也借鉴了这个概念,用来控制网络请求的速率,在高并发,大流量的场景中,比如双十一秒杀、抢购、抢票、抢单等场景。 网络限流主流的算法有两种,分别是漏桶算法和令牌桶算法。接下来我们一一为大家介绍:1. 漏桶算法描述:漏桶算法思路很简单,水(数据或者请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。实现逻辑: 控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。 漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。 优缺点:在某些情况下,漏桶算法不能够有效地使用网络资源。因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使某一个单独的流突发到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。而令牌桶算法则能够满足这些具有突发特性的流量。通常,漏桶算法与令牌桶算法可以结合起来为网络流量提供更大的控制。2. 令牌桶算法实现逻辑:令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。 令牌桶的另外一个好处是可以方便的改变速度。 一旦需要提高速率,则按需提高放入桶中的令牌的速率。 一般会定时(比如100毫秒)往桶中增加一定数量的令牌, 有些变种算法则实时的计算应该增加的令牌的数量, 比如华为的专利"采用令牌漏桶进行报文限流的方法"(CN 1536815 A),提供了一种动态计算可用令牌数的方法, 相比其它定时增加令牌的方法, 它只在收到一个报文后,计算该报文与前一报文到来的时间间隔内向令牌漏桶内注入的令牌数, 并计算判断桶内的令牌数是否满足传送该报文的要求。二、常见的 Rate limiting 实现方式通常意义上的限速,其实可以分为以下三种:limit_rate 限制响应速度limit_conn 限制连接数limit_req 限制请求数1. Nginx 模块 (漏桶)参考地址:limit_req_modulengx_http_limit_req_module模块(0.7.21)用于限制每个定义键的请求处理速度,特别是来自单个IP地址的请求的处理速度。1.1 Example Configurationhttp { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; … server { … location /search/ { limit_req zone=one burst=5; }1.2 使用规则语法: limit_req zone=name [burst=number] [nodelay | delay=number];默认: —作用范围: http, server, location参数说明zone 设置内存名称和内存大小。burst 漏桶的突发大小。当大于突发值是请求被延迟。nodelay|delay delay参数(1.15.7)指定了过度请求延迟的限制。默认值为零,即所有过量的请求都被延迟。设置共享内存区域和请求的最大突发大小。如果请求速率超过为区域配置的速率,则延迟处理请求,以便以定义的速率处理请求。过多的请求会被延迟,直到它们的数量超过最大突发大小,在这种情况下,请求会因错误而终止。默认情况下,最大突发大小等于零。limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;server { location /search/ { limit_req zone=one burst=5; }描述:平均每秒不允许超过一个请求,突发请求不超过5个。—–参数使用说明—–如果不希望在请求受到限制时延迟过多的请求,则应使用参数nodelay:limit_req zone=one burst=5 nodelay;可以有几个limit_req指令。例如,下面的配置将限制来自单个IP地址的请求的处理速度,同时限制虚拟服务器的请求处理速度:limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s;limit_req_zone $server_name zone=perserver:10m rate=10r/s;server { … limit_req zone=perip burst=5 nodelay; limit_req zone=perserver burst=10;}当且仅当当前级别上没有limit_req指令时,这些指令从上一级继承。1.3 围绕limit_req_zone的相关配置语法: limit_req_log_level info | notice | warn | error;默认: limit_req_log_level error;作用范围: http, server, locationThis directive appeared in version 0.8.18.设置所需的日志记录级别,用于服务器因速率超过或延迟请求处理而拒绝处理请求的情况。延迟日志记录级别比拒绝日志记录级别低1点;例如,如果指定了“limit_req_log_level通知”,则使用info级别记录延迟。错误状态语法: limit_req_status code;默认: limit_req_status 503;作用范围: http, server, locationThis directive appeared in version 1.3.15.设置状态代码以响应被拒绝的请求。语法: limit_req_zone key zone=name:size rate=rate [sync];默认: —作用范围: http设置共享内存区域的参数,该区域将保存各种键的状态。特别是,状态存储当前过多请求的数量。键可以包含文本、变量及其组合。键值为空的请求不被计算。Prior to version 1.7.6, a key could contain exactly one variable.例如:limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;说明:在这里,状态保存在一个10mb的区域“1”中,该区域的平均请求处理速度不能超过每秒1个请求。总结:客户端IP地址作为密钥。注意,这里使用的是$binary_remote_addr变量,而不是$remote_addr。$binary_remote_addr变量的大小对于IPv4地址总是4个字节,对于IPv6地址总是16个字节。存储状态在32位平台上总是占用64字节,在64位平台上占用128字节。一个兆字节区域可以保存大约16000个64字节的状态,或者大约8000个128字节的状态。如果区域存储耗尽,则删除最近最少使用的状态。即使在此之后无法创建新状态,请求也会因错误而终止。速率以每秒请求数(r/s)指定。如果需要每秒少于一个请求的速率,则在每分钟请求(r/m)中指定。例如,每秒半请求是30r/m。2. Openresty 模块参考地址:lua-resty-limit-traffic参考地址:openresty常用限速2.1 限制接口总并发数按照 ip 限制其并发连接数lua_shared_dict my_limit_conn_store 100m;…location /hello { access_by_lua_block { local limit_conn = require “resty.limit.conn” – 限制一个 ip 客户端最大 1 个并发请求 – burst 设置为 0,如果超过最大的并发请求数,则直接返回503, – 如果此处要允许突增的并发数,可以修改 burst 的值(漏桶的桶容量) – 最后一个参数其实是你要预估这些并发(或者说单个请求)要处理多久,以便于对桶里面的请求应用漏桶算法 local lim, err = limit_conn.new(“my_limit_conn_store”, 1, 0, 0.5) if not lim then ngx.log(ngx.ERR, “failed to instantiate a resty.limit.conn object: “, err) return ngx.exit(500) end local key = ngx.var.binary_remote_addr – commit 为true 代表要更新shared dict中key的值, – false 代表只是查看当前请求要处理的延时情况和前面还未被处理的请求数 local delay, err = lim:incoming(key, true) if not delay then if err == “rejected” then return ngx.exit(503) end ngx.log(ngx.ERR, “failed to limit req: “, err) return ngx.exit(500) end – 如果请求连接计数等信息被加到shared dict中,则在ctx中记录下, – 因为后面要告知连接断开,以处理其他连接 if lim:is_committed() then local ctx = ngx.ctx ctx.limit_conn = lim ctx.limit_conn_key = key ctx.limit_conn_delay = delay end local conn = err – 其实这里的 delay 肯定是上面说的并发处理时间的整数倍, – 举个例子,每秒处理100并发,桶容量200个,当时同时来500个并发,则200个拒掉 – 100个在被处理,然后200个进入桶中暂存,被暂存的这200个连接中,0-100个连接其实应该延后0.5秒处理, – 101-200个则应该延后0.5*2=1秒处理(0.5是上面预估的并发处理时间) if delay >= 0.001 then ngx.sleep(delay) end } log_by_lua_block { local ctx = ngx.ctx local lim = ctx.limit_conn if lim then local key = ctx.limit_conn_key – 这个连接处理完后应该告知一下,更新shared dict中的值,让后续连接可以接入进来处理 – 此处可以动态更新你之前的预估时间,但是别忘了把limit_conn.new这个方法抽出去写, – 要不每次请求进来又会重置 local conn, err = lim:leaving(key, 0.5) if not conn then ngx.log(ngx.ERR, “failed to record the connection leaving “, “request: “, err) return end end } proxy_pass http://10.100.157.198:6112; proxy_set_header Host $host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 60; proxy_read_timeout 600; proxy_send_timeout 600;}说明:其实此处没有设置 burst 的值,就是单纯的限制最大并发数,如果设置了 burst 的值,并且做了延时处理,其实就是对并发数使用了漏桶算法,但是如果不做延时处理,其实就是使用的令牌桶算法。参考下面对请求数使用漏桶令牌桶的部分,并发数的漏桶令牌桶实现与之相似2.2 限制接口时间窗请求数限制 ip 每分钟只能调用 120 次 /hello 接口(允许在时间段开始的时候一次性放过120个请求)lua_shared_dict my_limit_count_store 100m;…init_by_lua_block { require “resty.core”}….location /hello { access_by_lua_block { local limit_count = require “resty.limit.count” – rate: 10/min local lim, err = limit_count.new(“my_limit_count_store”, 120, 60) if not lim then ngx.log(ngx.ERR, “failed to instantiate a resty.limit.count object: “, err) return ngx.exit(500) end local key = ngx.var.binary_remote_addr local delay, err = lim:incoming(key, true) – 如果请求数在限制范围内,则当前请求被处理的延迟(这种场景下始终为0,因为要么被处理要么被拒绝)和将被处理的请求的剩余数 if not delay then if err == “rejected” then return ngx.exit(503) end ngx.log(ngx.ERR, “failed to limit count: “, err) return ngx.exit(500) end } proxy_pass http://10.100.157.198:6112; proxy_set_header Host $host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 60; proxy_read_timeout 600; proxy_send_timeout 600;}2.3 平滑限制接口请求数限制 ip 每分钟只能调用 120 次 /hello 接口(平滑处理请求,即每秒放过2个请求)lua_shared_dict my_limit_req_store 100m;….location /hello { access_by_lua_block { local limit_req = require “resty.limit.req” – 这里设置rate=2/s,漏桶桶容量设置为0,(也就是来多少水就留多少水) – 因为resty.limit.req代码中控制粒度为毫秒级别,所以可以做到毫秒级别的平滑处理 local lim, err = limit_req.new(“my_limit_req_store”, 2, 0) if not lim then ngx.log(ngx.ERR, “failed to instantiate a resty.limit.req object: “, err) return ngx.exit(500) end local key = ngx.var.binary_remote_addr local delay, err = lim:incoming(key, true) if not delay then if err == “rejected” then return ngx.exit(503) end ngx.log(ngx.ERR, “failed to limit req: “, err) return ngx.exit(500) end } proxy_pass http://10.100.157.198:6112; proxy_set_header Host $host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 60; proxy_read_timeout 600; proxy_send_timeout 600;}2.4 漏桶算法限流限制 ip 每分钟只能调用 120 次 /hello 接口(平滑处理请求,即每秒放过2个请求),超过部分进入桶中等待,(桶容量为60),如果桶也满了,则进行限流lua_shared_dict my_limit_req_store 100m;….location /hello { access_by_lua_block { local limit_req = require “resty.limit.req” – 这里设置rate=2/s,漏桶桶容量设置为0,(也就是来多少水就留多少水) – 因为resty.limit.req代码中控制粒度为毫秒级别,所以可以做到毫秒级别的平滑处理 local lim, err = limit_req.new(“my_limit_req_store”, 2, 60) if not lim then ngx.log(ngx.ERR, “failed to instantiate a resty.limit.req object: “, err) return ngx.exit(500) end local key = ngx.var.binary_remote_addr local delay, err = lim:incoming(key, true) if not delay then if err == “rejected” then return ngx.exit(503) end ngx.log(ngx.ERR, “failed to limit req: “, err) return ngx.exit(500) end – 此方法返回,当前请求需要delay秒后才会被处理,和他前面对请求数 – 所以此处对桶中请求进行延时处理,让其排队等待,就是应用了漏桶算法 – 此处也是与令牌桶的主要区别既 if delay >= 0.001 then ngx.sleep(delay) end } proxy_pass http://10.100.157.198:6112; proxy_set_header Host $host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 60; proxy_read_timeout 600; proxy_send_timeout 600;}3.5 令牌桶算法限流限制 ip 每分钟只能调用 120 次 /hello 接口(平滑处理请求,即每秒放过2个请求),但是允许一定的突发流量(突发的流量,就是桶的容量(桶容量为60),超过桶容量直接拒绝lua_shared_dict my_limit_req_store 100m;….location /hello { access_by_lua_block { local limit_req = require “resty.limit.req” local lim, err = limit_req.new(“my_limit_req_store”, 2, 0) if not lim then ngx.log(ngx.ERR, “failed to instantiate a resty.limit.req object: “, err) return ngx.exit(500) end local key = ngx.var.binary_remote_addr local delay, err = lim:incoming(key, true) if not delay then if err == “rejected” then return ngx.exit(503) end ngx.log(ngx.ERR, “failed to limit req: “, err) return ngx.exit(500) end – 此方法返回,当前请求需要delay秒后才会被处理,和他前面对请求数 – 此处忽略桶中请求所需要的延时处理,让其直接返送到后端服务器, – 其实这就是允许桶中请求作为突发流量 也就是令牌桶桶的原理所在 if delay >= 0.001 then – ngx.sleep(delay) end } proxy_pass http://10.100.157.198:6112; proxy_set_header Host $host; proxy_redirect off; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 60; proxy_read_timeout 600; proxy_send_timeout 600;}说明:其实nginx的ngx_http_limit_req_module 这个模块中的delay和nodelay也就是类似此处对桶中请求是否做延迟处理的两种方案,也就是分别对应的漏桶和令牌桶两种算法注意:resty.limit.traffic 模块说明 This library is already usable though still highly experimental. 意思是说目前这个模块虽然可以使用了,但是还处在高度实验性阶段,所以目前(2019-03-11)放弃使用resty.limit.traffic模块。3. kong 插件参考地址:Rate Limiting Advanced (企业版)参考地址:request-termination参考地址:rate-limiting 请求限速参考地址:request-size-limiting (官方建议开启此插件,防止DOS(拒绝服务)攻击)参考地址:response-ratelimiting 响应限速参考地址:kong-response-size-limiting (非官方提供)3.1 rate-limiting速率限制开发人员在给定的几秒、几分钟、几小时、几天、几个月或几年时间内可以发出多少HTTP请求。如果底层服务/路由(或废弃的API实体)没有身份验证层,那么将使用客户机IP地址,否则,如果配置了身份验证插件,将使用使用者。在一个Service上启用该插件$ curl -X POST http://kong:8001/services/{service}/plugins \ –data “name=rate-limiting” \ –data “config.second=5” \ –data “config.hour=10000"在一个router上启用该插件$ curl -X POST http://kong:8001/routes/{route_id}/plugins \ –data “name=rate-limiting” \ –data “config.second=5” \ –data “config.hour=10000"在一个consumer上启动该插件$ curl -X POST http://kong:8001/plugins \ –data “name=rate-limiting” \ –data “consumer_id={consumer_id}” \ –data “config.second=5” \ –data “config.hour=10000"rate-limiting支持三个策略,它们分别拥有自己的优缺点策略优点缺点cluster准确,没有额外的组件来支持相对而言,性能影响最大的是,每个请求都强制对底层数据存储执行读和写操作。redis准确,比集群策略对性能的影响更小额外的redis安装要求,比本地策略更大的性能影响local最小的性能影响不太准确,除非在Kong前面使用一致哈希负载均衡器,否则在扩展节点数量时它会发散3.2 response-ratelimiting此插件允许您根据上游服务返回的自定义响应头限制开发人员可以发出的请求数量。您可以任意设置任意数量的限速对象(或配额),并指示Kong按任意数量增加或减少它们。每个自定义速率限制对象都可以限制每秒、分钟、小时、天、月或年的入站请求。在一个Service上启用该插件$ curl -X POST http://kong:8001/services/{service}/plugins \ –data “name=response-ratelimiting” \ –data “config.limits.{limit_name}=” \ –data “config.limits.{limit_name}.minute=10"在一个router上启用该插件$ curl -X POST http://kong:8001/routes/{route_id}/plugins \ –data “name=response-ratelimiting” \ –data “config.limits.{limit_name}=” \ –data “config.limits.{limit_name}.minute=10"在一个consumer上启动该插件$ curl -X POST http://kong:8001/plugins \ –data “name=response-ratelimiting” \ –data “consumer_id={consumer_id}” \ –data “config.limits.{limit_name}=” \ –data “config.limits.{limit_name}.minute=10"在api上启用该插件$ curl -X POST http://kong:8001/apis/{api}/plugins \ –data “name=response-ratelimiting” \ –data “config.limits.{limit_name}=” \ –data “config.limits.{limit_name}.minute=10"3.3 request-size-limiting阻塞体大于特定大小(以兆为单位)的传入请求。在一个Service上启用该插件$ curl -X POST http://kong:8001/services/{service}/plugins \ –data “name=request-size-limiting” \ –data “config.allowed_payload_size=128"在一个router上启用该插件$ curl -X POST http://kong:8001/routes/{route_id}/plugins \ –data “name=request-size-limiting” \ –data “config.allowed_payload_size=128"在一个consumer上启动该插件$ curl -X POST http://kong:8001/plugins \ –data “name=request-size-limiting” \ –data “consumer_id={consumer_id}” \ –data “config.allowed_payload_size=128"3.4 request-termination此插件使用指定的状态代码和消息终止传入的请求。这允许(暂时)停止服务或路由上的通信,甚至阻塞消费者。在一个Service上启用该插件$ curl -X POST http://kong:8001/services/{service}/plugins \ –data “name=request-termination” \ –data “config.status_code=403” \ –data “config.message=So long and thanks for all the fish!“在一个router上启用该插件$ curl -X POST http://kong:8001/routes/{route_id}/plugins \ –data “name=request-termination” \ –data “config.status_code=403” \ –data “config.message=So long and thanks for all the fish!“在一个consumer上启动该插件$ curl -X POST http://kong:8001/plugins \ –data “name=request-termination” \ –data “consumer_id={consumer_id}” \ –data “config.status_code=403” \ –data “config.message=So long and thanks for all the fish!“4. 基于redis - INCR key参考地址:pattern-rate-limiter(翻墙)使用redis的INCR key,它的意思是将存储在key上的值加1。如果key不存在,在操作之前将值设置为0。如果键包含错误类型的值或包含不能表示为整数的字符串,则返回错误。此操作仅限于64位带符号整数。return value Integer reply: the value of key after the incrementexamplesredis> SET mykey “10"“OK"redis> INCR mykey(integer) 11redis> GET mykey"11"redis> INCR key 有两种用法:计数器(counter),比如文章浏览总量、分布式数据分页、游戏得分等;限速器(rate limiter),速率限制器模式是一种特殊的计数器,用于限制操作的执行速率,比如:限制可以针对公共API执行的请求数量;本方案的重点是使用redis实现一个限速器,我们使用INCR提供了该模式的两种实现,其中我们假设要解决的问题是将API调用的数量限制在每IP地址每秒最多10个请求:第一种方式,基本上每个IP都有一个计数器,每个不同的秒都有一个计数器FUNCTION LIMIT_API_CALL(ip)ts = CURRENT_UNIX_TIME()keyname = ip+”:"+tscurrent = GET(keyname)IF current != NULL AND current > 10 THEN ERROR “too many requests per second"ELSE MULTI INCR(keyname,1) EXPIRE(keyname,10) EXEC PERFORM_API_CALL()END优点:使用ip+ts的方式,确保了每秒的缓存都是不同的key,将每一秒产生的redisobject隔离开。没有使用过期时间强制限制redis过期时效。缺点:会产生大量的redis-key,虽然都写入了过期时间,但是对于redis-key的清理也是一种负担。有可能会影响redis的读性能。第二种方式,创建计数器的方式是,从当前秒中执行的第一个请求开始,它只能存活一秒钟。如果在同一秒内有超过10个请求,计数器将达到一个大于10的值,否则它将过期并重新从0开始。FUNCTION LIMIT_API_CALL(ip):current = GET(ip)IF current != NULL AND current > 10 THEN ERROR “too many requests per second"ELSE value = INCR(ip) IF value == 1 THEN EXPIRE(ip,1) END PERFORM_API_CALL()END优点:相对于方案一种占用空间更小,执行效率更高。缺点:INCR命令和EXPIRE命令不是原子操作,存在一个竞态条件。如果由于某种原因客户端执行INCR命令,但没有执行过期,密钥将被泄露,直到我们再次看到相同的IP地址。修复方案:将带有可选过期的INCR转换为使用EVAL命令发送的Lua脚本(只有在Redis 2.6版本中才可用)。使用lua局部变量来解决,保证每次都能设置过期时间。local currentcurrent = redis.call(“incr”,KEYS[1])if tonumber(current) == 1 then redis.call(“expire”,KEYS[1],1)end三、最终实现方案根据几种常见的实现方案和场景以及优缺点最终采用的是使用kong的插件 rate-limiting ,如果不符合要求进行二次开发。直接开发kong插件使用令牌桶+redis实现限流 ...

March 11, 2019 · 6 min · jiezi

nginx常用命令

nginx 常用命令nginx 常用命令nginx -s quit //优雅停止nginx,有连接时会等连接请求完成再杀死worker进程 nginx -s reload //优雅重启,并重新载入配置文件nginx.confnginx -s reopen //重新打开日志文件,一般用于切割日志nginx -v //查看版本 nginx -t //检查nginx的配置文件nginx -h //查看帮助信息nginx -V //详细版本信息,包括编译参数 nginx -c filename //指定配置文件注:后来发现使配置文件生效不需要重启系统只需要输入 source /etc/profile //让配置文件重新生效一下即可nginx 跨域//直接请求nginx也是会报跨域错误的这里设置允许跨域;如果代理地址已经允许跨域则不需要这些, 否则报错(虽然这样nginx跨域就没意义了)add_header Access-Control-Allow-Origin *; add_header Access-Control-Allow-Headers X-Requested-With; add_header Access-Control-Allow-Methods GET,POST,OPTIONS;

March 11, 2019 · 1 min · jiezi

linux运维之路之nginx

软件介绍与 Apache软件类似, Nginx ( “engme x")是一个开源的,支持高性能、高并发的 WWW服务器和代理服务软件。它是由俄罗斯人 Igor Sysoev开发的,最初被应用在勘罗斯的大型网站 www.rambler.ru 上,后来作者将源代码以类BSD许可证的形式开源出来供全球使用。Nginx因具有高并发(特别是静态资源)、占用系统资源少等特性,且功能丰富而逐渐流行起来。在功能应用发面,Nginx不但是一个优秀的Web服务软件,还具有反向代理负载均衡功能和缓存服务功能。在反向代理负载均衡功能方面,它类似于大名鼎鼎的LVS负载均衡及Haproxy等专业代理软件,但是Nginx部署起来更为简单、方便;在缓存服务功能方面,它又类似于Squid等专业的缓存服务软件。Nginx 的官方介绍见 http://nginx.org/ennginx软件特性HTTP服务器的特色及优点:支持高并发:能支持几万并发连接(特别是静态小文件业务环境)资源消耗少:在3万并发连接下,开启10个Nginx线程消耗的内存不到200MB可以做HTTP反向代理及加速缓存、即负载均衡功能,内置对RS节点服务器健康检查功能这相当于专业的Haproxy软件或LVS的功能。具备Squid等专业缓存软件等的缓存功能。支持异步网络I/O事件横型epoll(Linux2.6+)nginx的安装nginx的安装可参考上一篇博文,这里不再啰嗦了。介绍几个nginx管理的命令吧。启动 $ /usr/local/nginx/sbin/nginx 重新加载 $ /usr/local/nginx/sbin/nginx -s reload检查语法$ /usr/local/nginx/sbin/nginx -t 杀死进程 $ pkill nginx 或者 $ killall nginx # 如果现实没有killall命令,执行:yum install -y psmiscnginx配置文件详解nginx.conf配置文件全局结构大致如下:全局配置(user,worker_process,error_log,pid)events(网络连接部分,worker_connections)http(最重要的部分,大部分功能都在这里)server(虚拟主机相关,在http块里)location(server里面)全局配置部分user nobody;# 定义运行nginx服务的用户,还可以加上组,如 user nobody nobody;worker_processes 1;# 定义nginx子进程数量,即提供服务的进程数量,该数值建议和服务cpu核数保持一致。# 除了可以定义数字外,还可以定义为auto,表示让系统自动调整。error_log logs/error.log;# 定义错误日志的路径,可以是相对路径(相对prefix路径的),也可以是绝对路径。# 该配置可以在此处定义,也可以定义到http、server、location里。error_log logs/error.log notice;# 定义错误日志路径以及日志级别.# 错误日志级别:常见的错误日志级别有[debug|info|notice|warn|error|crit|alert|emerg],级别越高记录的信息越少。# 如果不定义默认是errorpid logs/nginx.pid;# 定义nginx进程pid文件所在路径,可以是相对路径,也可以是绝对路径。worker_rlimit_nofile 100000;# 定义nginx最多打开文件数限制。如果没设置的话,这个值为操作系统(ulimit -n)的限制保持一致。# 把这个值设高,nginx就不会有“too many open files”问题了。events配置部分worker_connections 1024;定义每个work_process同时开启的最大连接数,即允许最多只能有这么多连接。accept_mutex on;当某一个时刻只有一个网络连接请求服务器时,服务器上有多个睡眠的进程会被同时叫醒,这样会损耗一定的服务器性能。Nginx中的accept_mutex设置为on,将会对多个Nginx进程(worker processer)接收连接时进行序列化,防止多个进程争抢资源。 默认就是on。multi_accept on;nginx worker processer可以做到同时接收多个新到达的网络连接,前提是把该参数设置为on。默认为off,即每个worker process一次只能接收一个新到达的网络连接。use epoll;Nginx服务器提供了多个事件驱动器模型来处理网络消息。其支持的类型有:select、poll、kqueue、epoll、rtsing、/dev/poll以及eventport。select:只能在Windows下使用,这个事件模型不建议在高负载的系统使用poll:Nginx默认首选,但不是在所有系统下都可用kqueue:这种方式在FreeBSD 4.1+, OpenBSD2.9+, NetBSD 2.0, 和 MacOS X系统中是最高效的epoll: 这种方式是在Linux 2.6+内核中最高效的方式rtsig:实时信号,可用在Linux 2.2.19的内核中,但不适用在高流量的系统中/dev/poll: Solaris 7 11/99+,HP/UX 11.22+, IRIX 6.5.15+, and Tru64 UNIX 5.1A+操作系统最高效的方式eventport: Solaris 10最高效的方式http配置部分官方文档 http://nginx.org/en/docs/参考链接: https://segmentfault.com/a/11…参考链接: https://segmentfault.com/a/11…参考链接:http的header https://kb.cnblogs.com/page/9…常见配置:include mime.types; //cat conf/mime.types定义nginx能识别的网络资源媒体类型(如,文本、html、js、css、流媒体等)default_type application/octet-stream;定义默认的type,如果不定义改行,默认为text/plain.log_format main ‘$remote_addr - $remote_user [$time_local] “$request” ’ ‘$status $body_bytes_sent “$http_referer” ’ ‘"$http_user_agent" “$http_x_forwarded_for”’;定义nginx日志格式,其中main为日志格式的名字,后面的为nginx的内部变量组成的一串字符串。access_log logs/access.log main; 定义日志的路径以及采用的日志格式,该参数可以在server配置块中定义。sendfile on;是否调用sendfile函数传输文件,默认为off,使用sendfile函数传输,可以减少user mode和kernel mode的切换,从而提升服务器性能。对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。sendfile_max_chunk 128k;该参数限定Nginx worker process每次调用sendfile()函数传输数据的最大值,默认值为0,如果设置为0则无限制。tcp_nopush on;当tcp_nopush设置为on时,会调用tcp_cork方法进行数据传输。使用该方法会产生这样的效果:当应用程序产生数据时,内核不会立马封装包,而是当数据量积累到一定量时才会封装,然后传输。这样有助于解决网络堵塞问题。默认值为on。举例:快递员收快递、发快递,包裹累积到一定量才会发,节省运输成本。keepalive_timeout 65 60;该参数有两个值,第一个值设置nginx服务器与客户端会话结束后仍旧保持连接的最长时间,单位是秒,默认为75s。第二个值可以省略,它是针对客户端的浏览器来设置的,可以通过curl -I看到header信息中有一项Keep-Alive: timeout=60,如果不设置就没有这一项。第二个数值设置后,浏览器就会根据这个数值决定何时主动关闭连接,Nginx服务器就不操心了。但有的浏览器并不认可该参数。send_timeout这个超时时间是发送响应的超时时间,即Nginx服务器向客户端发送了数据包,但客户端一直没有去接收这个数据包。如果某个连接超过send_timeout定义的超时时间,那么Nginx将会关闭这个连接。client_max_body_size 10m;浏览器在发送含有较大HTTP包体的请求时,其头部会有一个Content-Length字段,client_max_body_size是用来限制Content-Length所示值的大小的。这个限制包体的配置不用等Nginx接收完所有的HTTP包体,就可以告诉用户请求过大不被接受。会返回413状态码。例如,用户试图上传一个1GB的文件,Nginx在收完包头后,发现Content-Length超过client_max_body_size定义的值,就直接发送413(Request Entity Too Large)响应给客户端。gzip on;是否开启gzip压缩。gzip_min_length 1k;设置允许压缩的页面最小字节数,页面字节数从header头得content-length中进行获取。默认值是20。建议设置成大于1k的字节数,小于1k可能会越压越大。gzip_buffers 4 16k;设置系统获取几个单位的buffer用于存储gzip的压缩结果数据流。4 16k代表分配4个16k的buffer。gzip_http_version 1.1;用于识别 http 协议的版本,早期的浏览器不支持 Gzip 压缩,用户会看到乱码,所以为了支持前期版本加上了这个选项。如果你用了Nginx反向代理并期望也启用Gzip压缩的话,由于末端通信是http/1.1,故请设置为 1.1。gzip_comp_level 6;gzip压缩比,1压缩比最小处理速度最快,9压缩比最大但处理速度最慢(传输快但比较消耗cpu)gzip_types mime-type … ;匹配mime类型进行压缩,无论是否指定,”text/html”类型总是会被压缩的。在conf/mime.conf里查看对应的type。示例:gzip_types text/plain application/x-javascript text/css text/html application/xml;gzip_vary on;和http头有关系,会在响应头加个 Vary: Accept-Encoding ,可以让前端的缓存服务器缓存经过gzip压缩的页面,例如,用Squid缓存经过Nginx压缩的数据。gzip_proxied any;Nginx作为反向代理的时候启用,决定开启或者关闭后端服务器返回的结果是否压缩,匹配的前提是后端服务器必须要返回包含”Via”的 header头。 以下为可用的值:off - 关闭所有的代理结果数据的压缩expired - 启用压缩,如果header头中包含 “Expires” 头信息no-cache - 启用压缩,如果header头中包含 “Cache-Control:no-cache” 头信息no-store - 启用压缩,如果header头中包含 “Cache-Control:no-store” 头信息private - 启用压缩,如果header头中包含 “Cache-Control:private” 头信息 no_last_modified - 启用压缩,如果header头中不包含 “Last-Modified” 头信息no_etag - 启用压缩 ,如果header头中不包含 “ETag” 头信息auth - 启用压缩 , 如果header头中包含 “Authorization” 头信息 any - 无条件启用压缩nginx服务优化虚拟主机1、虚拟主机概念虚拟主机指的是,在web服务里是一个独立的网站站点,这个站点对应独立的域名,也可能是IP或端口,具有独立的程序及资源目录,可以独立地对外提供服务供用户访问。Nginx软件是使用一个server{}标签来标识一个虚拟主机的。一个web服务里可以有多个虚拟主机标签对,即可同时支持多个虚拟主机站点。2、虚拟主机类型常见的虚拟主机类型分三种基于域名的虚拟主机基于端口的虚拟主机基于IP的虚拟主机基于域名的虚拟主机是通过不同域名区分不同的虚拟主机,是企业应用最广泛的虚拟主机类型。基于端口的虚拟主机是通过不同端口区分不同的虚拟主机,常用于公司内部的网站,比如不希望用户访问的网站后台等。基于IP地址的虚拟主机是通过不同IP地址区分不同的虚拟主机,不常用很少见。3、基于域名的虚拟主机配置第一、编辑nginx配置文件nginx.conf,找到http{}块,在http{}块里面添加server{}标签,一个server{}代表一个虚拟主机。默认nginx.conf中有一个server{},这里修改一下,并配置两个基于域名的虚拟主机。配置内容如下:server { listen 80; server_name www.syushin.com; location / { root html/com; index index.html index.htm; } }server { listen 80; server_name www.syushin.org; location / { root html/org; index index.html index.htm; } } 可以看到基于域名的虚拟是在server_name中定义。第二、创建域名对应的站点目录及文件。$ mkdir ../html/com ../html/org$ echo “I am syushin.com” >> ../html/com/index.html$ echo “I am syushin.org” >> ../html/org/index.html第三、配置域名解析linux下echo “192.168.30.7 www.syushin.com www.syushin.org” >> /etc/hostswindows下找到域名解析文件,通常在C:\Windows\System32\drivers\etc\hosts添加内容:192.168.30.7 www.syushin.com www.syushin.org第四、检查nginx语法,重新加载$ /usr/local/nginx/sbin/nginx -t$ /usr/local/nginx/sbin/nginx -s reload第五、测试linux下用curl命令测试[root@lnmp conf]$ curl www.syushin.comI am syushin.com[root@lnmp conf]$ curl www.syushin.orgI am syushin.orgwindows下用浏览器访问这样就可以实现基于域名的虚拟主机了,用户输入不同的域名,访问不同的站点内容。而基于端口的虚拟主机是在server{}标签里的listen处修改监听的端口,比如listen 8080;或者listen 8090;基于IP地址也是在listen里修改,格式如下:192.168.30.7:80;或者192.168.30.8:81都很简单,就不再实际操作了。规范优化nginx配置文件nginx主配置文件是nginx.conf,可以将主配置文件包含所有虚拟主机的子配置文件统一放入extra目录里。使用include参数实现,它可以放在nginx配置文件的任何地方,用法示例如下:$ include extra/web01.conf下面是我的使用方案:$ vim nginx.conf在http{}删除默认的server{}标签,使用include extra/.conf;http{ include mime.types; default_type application/octet-stream; …(这里省略) include extra/.conf; #就是这一行}创建虚拟主机配置保存的目录$ mkdir /usr/local/nginx/conf/extra进入extra目录,创建虚拟主机配置文件。$ vim web01.conf# 内容如下server { listen 80; server_name localhost; index index.html index.htm ; root html; }这样通过主配置文件加上include包含的配置使得Nginx配置更加简单,清晰,规范。域名重定向在一个虚拟主机上有多个域名的时候,可以针对这多个域名做域名重定向。即访问A域名,浏览器会跳转到B域名。server { listen 80; server_name www.syushin.com blog.syushin.cc; if ($host = ‘blog.syushin.cc’ ) { rewrite /(.) http://syushin.com/$1 permanent; } index index.html index.htm index.php; root html/web01;}虚拟主机配置如上:其中if语句就是域名重定向的配置了。$host变量就是域名,当访问域名是blog.syushin.cc的时候,就将域名跳转到www.syushin.com。测试:[root@lnmp ~]$ curl -x127.0.0.1:80 www.syushin.com -IHTTP/1.1 200 OK # 正常访问状态码200Server: nginx/1.14.2Date: Sun, 10 Mar 2019 13:00:16 GMTContent-Type: text/htmlContent-Length: 12Last-Modified: Sun, 10 Mar 2019 12:38:09 GMTConnection: keep-aliveETag: “5c850531-c"Accept-Ranges: bytes[root@lnmp ~]$ curl -x127.0.0.1:80 blog.syushin.cc -I HTTP/1.1 301 Moved Permanently # 状态码301Server: nginx/1.14.2Date: Sun, 10 Mar 2019 13:00:25 GMTContent-Type: text/htmlContent-Length: 185Connection: keep-aliveLocation: http://www.syushin.com/ # 重定向域名用户认证nginx访问日志nginx访问日志就是用户访问网站的记录。可以针对不同格式记录日志。配置nginx访问日志格式(这里只使用其中一部分): # 在nginx主配置文件中的http{}块里定义 log_format main ‘$remote_addr $http_x_forwarded_for [$time_local]’ ’ $host “$request_uri” $status’ ’ “$http_referer” “$http_user_agent”’;参数说明:变量描述$remote_addr客户端IP(公网IP)$http_x_forwarded_for代理服务器的IP$time_local服务器本地时间$host访问主机名(域名)$request_url访问的url地址$status状态码$http_refererreferer$http_user_agentuser_agent虚拟主机配置文件爬配置nginx访问日志保存目录以及使用主配置文件里的日志格式:server { listen 80; server_name www.syushin.com blog.syushin.cc; if ($host = ‘blog.syushin.cc’ ) { rewrite /(.) http://www.syushin.com/$1 permanent; } index index.html index.htm index.php; root html/web01; location ~ .php$ { include fastcgi_params; fastcgi_pass unix:/tmp/php-fcgi.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /usr/local/nginx/html$fastcgi_script_name; } access_log /var/log/nginx/syushin_access.log main; # 这一行就是配置访问日志}创建日志文件目录$ mkdir -p /var/log/nginx检查语法、重新加载[root@lnmp conf]$ $ /usr/local/nginx/sbin/nginx -tnginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is oknginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful[root@lnmp conf]$ /usr/local/nginx/sbin/nginx -s reload测试,浏览器访问一次域名和在本地curl一下,查看是否有日志生成。[root@lnmp nginx]$ curl -x127.0.0.1:80 blog.syushin.cc <html><head><title>301 Moved Permanently</title></head><body bgcolor=“white”><center><h1>301 Moved Permanently</h1></center><hr><center>nginx/1.14.2</center></body></html>[root@lnmp nginx]$ cat syushin_access.log 192.168.30.1 - [11/Mar/2019:12:55:01 +0800] www.syushin.com “/” 304 “-” “Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko"127.0.0.1 - [11/Mar/2019:12:56:32 +0800] blog.syushin.cc “/” 301 “-” “curl/7.29.0"这样配置访问日志就成功了。日志切割生产环境下,访问日志的量是很大的,如果把访问日志都放在一个文件里是会有很大影响的。因此将nginx访问日志进行切割是很有必要的。如何切割日志?Linux有个日志切割工具logrotate。它的配置文件在/etc/logrotate.conf查看配置文件# see “man logrotate” for details# rotate log files weeklyweekly# keep 4 weeks worth of backlogsrotate 4# create new (empty) log files after rotating old onescreate# use date as a suffix of the rotated filedateext# uncomment this if you want your log files compressed#compress# RPM packages drop log rotation information into this directoryinclude /etc/logrotate.d# no packages own wtmp and btmp – we’ll rotate them here/var/log/wtmp { monthly create 0664 root utmp minsize 1M rotate 1}/var/log/btmp { missingok monthly create 0600 root utmp rotate 1}其中weekly表示按周切割;rotate 4表示一次存储4个归档文件,对于第5个归档文件,时间最久的文档会被覆盖;create以指定的权限创建全新的日志文件,同时logrotate也会重命名原始日志文件。dataext表示以日期为格式命令新的日志文件/var/log/wtmp { monthly create 0664 root utmp minsize 1M rotate 1 }上面代码块表示针对/var/log/wtmp这个目录下做日志切割,时间为一个月,只对文件大小大于1M文件进行切割。对于nginx的日志切割如果是yum安装的nginx,logrotate已经有默认的切割策略了/etc/logrotate.d/nginx。而编译安装的nginx就没有,需要手动定义,也可以用这个日志配置$ cat /etc/logrotate.d/nginx /var/log/nginx/.log { daily rotate 5 missingok notifempty create 644 www www postrotate if [ -f /application/nginx/logs/nginx.pid ]; then kill -USR1 cat /application/nginx/logs/nginx.pid fiendscript}logrotate工具对于防止因庞大的日志文件而耗尽存储空间是十分有用的。配置完毕后,进程是全自动的,可以长时间在不需要人为干预下运行。除了样例之外,自己还可以定制不同的样式满足需求。访问日志不记录静态文件在访问日志里,过滤掉一些图片、js、css等的请求日志。因为这样的日志没有多大意义,而且会占用很大的磁盘空间。配置,编辑虚拟主机配置文件,添加location:location ~ .(png|jpeg|jpg|gif|js|css|bmp)$ { access_log off; }上面规则表示凡是匹配到以.png|jpeg|jpg|gif|js|css|bmp结尾的文件,访问日志功能就关闭,即不记录访问静态文件的访问记录。*号表示不区分大小写,.号前面需要只用转义字符,|表示或者。测试在虚拟主机站点目录里添加资源文件test.png,然后curl访问一下、再查看日志是否有记录这条访问记录,如果没有则表示配置成功。清空日志$ > /var/log/nginx/syushin_access.log 创建以png结尾的文件$ touch /usr/local/nginx/html/web01/test.png 访问# 成功访问[root@lnmp web01]$ curl -x127.0.0.1:80 www.syushin.com/test.png -IHTTP/1.1 200 OKServer: nginx/1.14.2Date: Mon, 11 Mar 2019 05:19:58 GMTContent-Type: image/pngContent-Length: 0Last-Modified: Mon, 11 Mar 2019 05:16:14 GMTConnection: keep-aliveETag: “5c85ef1e-0"Accept-Ranges: bytes查看日志[root@lnmp web01]# cat /var/log/nginx/syushin_access.log [root@lnmp web01]# 无日志记录,说明配置成功。防盗链盗链是指服务提供商自己不提供服务的内容,通过技术手段绕过其它有利益的最终用户界面(如广告),直接在自己的网站上向最终用户提供其它服务提供商的服务内容,骗取最终用户的浏览和点击率。受益者不提供资源或提供很少的资源,而真正的服务提供商却得不到任何的收益。防盗链就是防止上面情况的。常见的盗链是图片盗链,音频盗链,文件盗链。Referer 防盗链Referer在HTTP协议里有特殊的用途,当浏览器向服务器发送请求时,一般会带上Referer头,告知服务器该请求是从哪个页面链接过来的。Referer经常被用于页面访问统计、图片防盗链等。配置图片防盗链location ~ .(png|gif|jpeg|bmp|mp3|mp4|flv)$ { valid_referers none blocked server_name .syushin.com; if ($invalid_referer){ return 403; } }valid referers指的是白名单内的域名可以引用站点图片none表示空referer,即直接打开站点的图片,而不是从其他网站打开本站点的图片文件,因此对直接访问站点内容的不做限制。blocked指的是非法链接server_name就是虚拟主机域名后面的.syushin.com就是白名单域名这样配置后,当从其他网站引用本站的图片的时候,就引用不了了。测试:使用curl -e选项测试,-e指定referer$ curl -I -e “http://www.aaa.com/1.txt" http://www.syushin.com/test.png出现403说明配置成功访问控制负载均衡 ...

March 11, 2019 · 4 min · jiezi

前端开发者必备的Nginx知识

nginx在应用程序中的作用解决跨域请求过滤配置gzip负载均衡静态资源服务器nginx是一个高性能的HTTP和反向代理服务器,也是一个通用的TCP/UDP代理服务器,最初由俄罗斯人Igor Sysoev编写。nginx现在几乎是众多大型网站的必用技术,大多数情况下,我们不需要亲自去配置它,但是了解它在应用程序中所担任的角色,以及如何解决这些问题是非常必要的。下面我将从nginx在企业中的真实应用来解释nginx在应用程序中起到的作用。为了便于理解,首先先来了解一下一些基础知识,nginx是一个高性能的反向代理服务器那么什么是反向代理呢?正向代理与反向代理代理是在服务器和客户端之间假设的一层服务器,代理将接收客户端的请求并将它转发给服务器,然后将服务端的响应转发给客户端。不管是正向代理还是反向代理,实现的都是上面的功能。正向代理正向代理,意思是一个位于客户端和原始服务器(origin server)之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。正向代理是为我们服务的,即为客户端服务的,客户端可以根据正向代理访问到它本身无法访问到的服务器资源。正向代理对我们是透明的,对服务端是非透明的,即服务端并不知道自己收到的是来自代理的访问还是来自真实客户端的访问。反向代理 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。反向代理是为服务端服务的,反向代理可以帮助服务器接收来自客户端的请求,帮助服务器做请求转发,负载均衡等。反向代理对服务端是透明的,对我们是非透明的,即我们并不知道自己访问的是代理服务器,而服务器知道反向代理在为他服务。基本配置配置结构下面是一个nginx配置文件的基本结构:events { }http { server { location path { … } location path { … } } server { … }}main:nginx的全局配置,对全局生效。events:配置影响nginx服务器或与用户的网络连接。http:可以嵌套多个server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。server:配置虚拟主机的相关参数,一个http中可以有多个server。location:配置请求的路由,以及各种页面的处理情况。upstream:配置后端服务器具体地址,负载均衡配置不可或缺的部分。内置变量下面是nginx一些配置中常用的内置全局变量,你可以在配置的任何位置使用它们。| 变量名 | 功能 | | —— | —— | | $host| 请求信息中的Host,如果请求中没有Host行,则等于设置的服务器名 || $request_method | 客户端请求类型,如GET、POST| $remote_addr | 客户端的IP地址 ||$args | 请求中的参数 ||$content_length| 请求头中的Content-length字段 ||$http_user_agent | 客户端agent信息 ||$http_cookie | 客户端cookie信息 ||$remote_addr | 客户端的IP地址 ||$remote_port | 客户端的端口 ||$server_protocol | 请求使用的协议,如HTTP/1.0、·HTTP/1.1` ||$server_addr | 服务器地址 ||$server_name| 服务器名称||$server_port|服务器的端口号|解决跨域先追本溯源以下,跨域究竟是怎么回事。跨域的定义同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。通常不允许不同源间的读操作。同源的定义如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。nginx解决跨域的原理例如:前端server的域名为:fe.server.com后端服务的域名为:dev.server.com现在我在fe.server.com对dev.server.com发起请求一定会出现跨域。现在我们只需要启动一个nginx服务器,将server_name设置为fe.server.com,然后设置相应的location以拦截前端需要跨域的请求,最后将请求代理回dev.server.com。如下面的配置:server { listen 80; server_name fe.server.com; location / { proxy_pass dev.server.com; }}这样可以完美绕过浏览器的同源策略:fe.server.com访问nginx的fe.server.com属于同源访问,而nginx对服务端转发的请求不会触发浏览器的同源策略。请求过滤根据状态码过滤error_page 500 501 502 503 504 506 /50x.html; location = /50x.html { #将跟路径改编为存放html的路径。 root /root/static/html; }根据URL名称过滤,精准匹配URL,不匹配的URL全部重定向到主页。location / { rewrite ^.$ /index.html redirect;}根据请求类型过滤。if ( $request_method !~ ^(GET|POST|HEAD)$ ) { return 403; }配置gzipGZIP是规定的三种标准HTTP压缩格式之一。目前绝大多数的网站都在使用 GZIP 传输 HTML、CSS、JavaScript 等资源文件。对于文本文件,GZip 的效果非常明显,开启后传输所需流量大约会降至 1/4 ~ 1/3。并不是每个浏览器都支持gzip的,如何知道客户端是否支持gzip呢,请求头中的Accept-Encoding来标识对压缩的支持。启用gzip同时需要客户端和服务端的支持,如果客户端支持gzip的解析,那么只要服务端能够返回gzip的文件就可以启用gzip了,我们可以通过nginx的配置来让服务端支持gzip。下面的respone中content-encoding:gzip,指服务端开启了gzip的压缩方式。 gzip on; gzip_http_version 1.1; gzip_comp_level 5; gzip_min_length 1000; gzip_types text/csv text/xml text/css text/plain text/javascript application/javascript application/x-javascript application/json application/xml;gzip开启或者关闭gzip模块默认值为 off可配置为 on / offgzip_http_version启用 GZip 所需的 HTTP 最低版本默认值为 HTTP/1.1这里为什么默认版本不是1.0呢?HTTP 运行在 TCP 连接之上,自然也有着跟 TCP 一样的三次握手、慢启动等特性。启用持久连接情况下,服务器发出响应后让TCP连接继续打开着。同一对客户/服务器之间的后续请求和响应可以通过这个连接发送。为了尽可能的提高 HTTP 性能,使用持久连接就显得尤为重要了。HTTP/1.1 默认支持 TCP 持久连接,HTTP/1.0 也可以通过显式指定 Connection: keep-alive 来启用持久连接。对于 TCP 持久连接上的 HTTP 报文,客户端需要一种机制来准确判断结束位置,而在 HTTP/1.0 中,这种机制只有 Content-Length。而在HTTP/1.1 中新增的 Transfer-Encoding: chunked 所对应的分块传输机制可以完美解决这类问题。nginx同样有着配置chunked的属性chunked_transfer_encoding,这个属性是默认开启的。Nginx 在启用了GZip的情况下,不会等文件 GZip 完成再返回响应,而是边压缩边响应,这样可以显著提高 TTFB(Time To First Byte,首字节时间,WEB 性能优化重要指标)。这样唯一的问题是,Nginx 开始返回响应时,它无法知道将要传输的文件最终有多大,也就是无法给出 Content-Length 这个响应头部。所以,在HTTP1.0中如果利用Nginx 启用了GZip,是无法获得 Content-Length 的,这导致HTTP1.0中开启持久链接和使用GZip只能二选一,所以在这里gzip_http_version默认设置为1.1。gzip_comp_level压缩级别,级别越高压缩率越大,当然压缩时间也就越长(传输快但比较消耗cpu)。默认值为 1压缩级别取值为1-9gzip_min_length设置允许压缩的页面最小字节数,Content-Length小于该值的请求将不会被压缩默认值:0当设置的值较小时,压缩后的长度可能比原文件大,建议设置1000以上gzip_types要采用gzip压缩的文件类型(MIME类型)默认值:text/html(默认不压缩js/css)负载均衡什么是负载均衡如上面的图,前面是众多的服务窗口,下面有很多用户需要服务,我们需要一个工具或策略来帮助我们将如此多的用户分配到每个窗口,来达到资源的充分利用以及更少的排队时间。把前面的服务窗口想像成我们的后端服务器,而后面终端的人则是无数个客户端正在发起请求。负载均衡就是用来帮助我们将众多的客户端请求合理的分配到各个服务器,以达到服务端资源的充分利用和更少的请求时间。nginx如何实现负载均衡Upstream指定后端服务器地址列表upstream balanceServer { server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}在server中拦截响应请求,并将请求转发到Upstream中配置的服务器列表。 server { server_name fe.server.com; listen 80; location /api { proxy_pass http://balanceServer; } }上面的配置只是指定了nginx需要转发的服务端列表,并没有指定分配策略。nginx实现负载均衡的策略轮询策略默认情况下采用的策略,将所有客户端请求轮询分配给服务端。这种策略是可以正常工作的,但是如果其中某一台服务器压力太大,出现延迟,会影响所有分配在这台服务器下的用户。upstream balanceServer { server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}最小连接数策略将请求优先分配给压力较小的服务器,它可以平衡每个队列的长度,并避免向压力大的服务器添加更多的请求。upstream balanceServer { least_conn; server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}最快响应时间策略依赖于NGINX Plus,优先分配给响应时间最短的服务器。upstream balanceServer { fair; server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}客户端ip绑定来自同一个ip的请求永远只分配一台服务器,有效解决了动态网页存在的session共享问题。upstream balanceServer { ip_hash; server 10.1.22.33:12345; server 10.1.22.34:12345; server 10.1.22.35:12345;}静态资源服务器location ~ .(png|gif|jpg|jpeg)$ { root /root/static/; autoindex on; access_log off; expires 10h;# 设置过期时间为10小时 }匹配以png|gif|jpg|jpeg为结尾的请求,并将请求转发到本地路径,root中指定的路径即nginx本地路径。同时也可以进行一些缓存的设置。小结nginx的功能非常强大,还有很多需要探索,上面的一些配置都是公司配置的真实应用(精简过了),如果您有什么意见或者建议,欢迎在下方留言… ...

March 11, 2019 · 2 min · jiezi

PHP统计Nginx日志的User Agent数据

转载请注明文章出处:https://tlanyan.me/stat-user-…即将用到爬虫,于是打算收集一下User Agent(UA)数据。接着马上想到自己网站的访问日志不就是现成的优质数据源吗?于是愉快的决定写个脚本统计一下Nginx访问日志中的UA信息。这类简单操作,用脚本语言就足够,毫无疑问肯定要用最熟悉的PHP。打开vim就开撸,十几分钟下来,功能简单的统计脚本就搞定了。脚本目前有三个功能:1. 找出所有的UA信息并排序; 2. 统计操作系统数据; 3. 统计浏览器数据。程序运行截图如下:UA信息操作系统信息浏览器用脚本统计最近一个月的访问日志,得到以下结果:搜索引擎爬虫比较频繁,每天有好几千次数据访问;Windows仍是份额最大的操作系统,Linux桌面依然份额很小;Chrome目前是浏览器领域的霸主,其次是Firefox,Opera已经很小众了。最后附上PHP脚本的代码,也可以从本人的Github里找到:https://github.com/tlanyan/Sc…#!/usr/bin/php&lt;?php/** * @brief stat UA in access log * * @author tlanyan&lt;tlanyan@hotmail.com&gt; * @link http://tlanyan.me // vim: set ts=4; set sw=4; set ss=4; set expandtab; /function getFileList(string $path) : array { return glob(rtrim($path, “/”) . “/access.log”);}function statFiles(array $files) : array { $stat = []; echo PHP_EOL, “start to read files…”, PHP_EOL; foreach ($files as $file) { echo “read file: $file …”, PHP_EOL; $contents = getFileContent($file); foreach ($contents as $line) { $ua = getUA($line); if (isset($stat[$ua])) { $stat[$ua] += 1; } else { $stat[$ua] = 1; } } } echo “stat all files done!”, PHP_EOL, PHP_EOL; return $stat;}function getFileContent(string $file) : array { if (substr($file, -3, 3) === “.gz”) { return gzfile($file); } return file($file);}function getUA(string $line) : ?string { // important! Nginx log format determins the UA location in the line! // You may have to refactor following codes to get the right result // UA starts from fifth double quote $count = 0; $offset = 0; while ($count &lt; 5) { $pos = strpos($line, ‘"’, $offset); if ($pos === false) { echo “Error! Unknown line: $line”, PHP_EOL; return null; } $count ++; $offset = $pos + 1; } $end = strpos($line, ‘"’, $offset); return substr($line, $offset, $end - $offset);}function usage() { echo “Usage: php statUA.php [option] [dir]”, PHP_EOL; echo " options:", PHP_EOL; echo " -h: show this help", PHP_EOL; echo " -v: verbose mode", PHP_EOL; echo “-n NUM: UA list number”, PHP_EOL; echo " dir: directory to the log files", PHP_EOL; echo PHP_EOL;}function filterUA(array& $stat, array $UAFilters) { $filterCount = 0; foreach ($UAFilters as $filter) { foreach ($stat as $ua =&gt; $count) { if (stripos($ua, $filter) !== false) { $filterCount += $count; unset($stat[$ua]); } } } echo “filter $filterCount records!”, PHP_EOL;}function printCount(array $stat) { $sum = array_sum($stat); foreach ($stat as $key =&gt; $count) { echo $key, " : “, $count, “, percent: “, sprintf(”%.2f”, 100$count/$sum), PHP_EOL; }}function statOS(array $UAs) : array { global $debug; echo PHP_EOL, “stat OS…”, PHP_EOL; $os = [“Windows”, “MacOS”, “Linux”, “Android”, “iOS”, “other”]; $stat = array_fill_keys($os, 0); foreach ($UAs as $key =&gt; $count) { if (strpos($key, “Windows”) !== false) { $stat[“Windows”] += $count; } else if (strpos($key, “Macintosh”) !== false) { $stat[“MacOS”] += $count; // must deal Android first, then Linux } else if (strpos($key, “Android”) !== false) { $stat[“Android”] += $count; } else if (strpos($key, “Linux”) !== false) { $stat[“Linux”] += $count; } else if (strpos($key, “iPhone”) !== false || strpos($key, “iOS”) !== false || strpos($key, “like Mac OS”) !== false || strpos($key, “Darwin”) !== false) { $stat[“iOS”] += $count; } else { if ($debug) { echo “other: $key, count: $count”, PHP_EOL; } $stat[“other”] += $count; } } return $stat;}function statBrowser(array $UAs) : array { global $debug; echo PHP_EOL, “stat brwoser…”, PHP_EOL; $browsers = [“Chrome”, “Firefox”, “IE”, “Safari”, “Edge”, “Opera”, “other”]; $stat = array_fill_keys($browsers, 0); foreach ($UAs as $key =&gt; $count) { if (strpos($key, “MSIE”) !== false) { $stat[“IE”] += $count; } else if (strpos($key, “Edge”) !== false) { $stat[“Edge”] += $count; } else if (strpos($key, “Firefox”) !== false) { $stat[“Firefox”] += $count; } else if (strpos($key, “OPR”) !== false) { $stat[“Opera”] += $count; // first Chrome, then Safari } else if (strpos($key, “Chrome”) !== false) { $stat[“Chrome”] += $count; } else if (strpos($key, “Safari”) !== false) { $stat[“Safari”] += $count; } else { if ($debug) { echo “other: $key, count: $count”, PHP_EOL; } $stat[“other”] += $count; } } return $stat;}function parseCmd() { global $debug, $num, $path, $argc, $argv; $optind = null; $options = getopt(“hvn:”, [], $optind); if ($argc &gt; 2 && empty($options)) { usage(); exit(1); } if (isset($options[‘h’])) { usage(); exit(0); } if (isset($options[‘v’])) { $debug = true; } if (isset($options[’n’])) { $num = intval($options[’n’]); if ($num &lt;= 0) { $num = 10; } } if ($argc === 2 && empty($options)) { $path = $argv[1]; } if ($argc &gt; $optind) { $path = $argv[$optind]; } if (!is_dir($path)) { echo “invalid directory: $path”, PHP_EOL; exit(1); } if ($debug) { echo “num: $num”, PHP_EOL; echo “verbose: “, var_export($debug, true), PHP_EOL; echo “path: $path”, PHP_EOL; }}if (version_compare(PHP_VERSION, “7.1”) &lt; 0) { exit(“scripts require PHP &gt;=7.1”);}$path = “.”;$debug = false;$num = 10;$UAFilters = [ “spider”, “bot”, “wget”, “curl”,];parseCmd();$files = getFileList($path);if (empty($files)) { echo ‘”’ . realpath($path) . ‘” does not contain access log files.’, PHP_EOL; exit(0);}$allUA = statFiles($files);if (empty($allUA)) { echo “no data”, PHP_EOL; exit(0);}filterUA($allUA, $UAFilters);// sort array with countuasort($allUA, function ($a, $b) { return $b - $a;});if ($debug) { print_r($allUA);}echo PHP_EOL, “—- top $num UA —-”, PHP_EOL;printCount(array_slice($allUA, 0, $num));echo “——————-”, PHP_EOL;$os = statOS($allUA);echo PHP_EOL, “os count:”, PHP_EOL;printCount($os);$browser = statBrowser($allUA);echo PHP_EOL, “browser count:”, PHP_EOL;printCount($browser); ...

March 10, 2019 · 4 min · jiezi

CentOS7安装LNMP(nginx1.14.2、mariadb10.3.13、php7.3.3)

前言系统环境[root@lnmp mysql]# cat /etc/redhat-release CentOS Linux release 7.3.1611 (Core) [root@lnmp mysql]# uname -r3.10.0-514.el7.x86_64官方下载地址mariadb数据库phpngixn安装mariadb10.3.131. 下载mariadb二进制包由于源码包编译时间过长就不选用了,而选用已经编译过的二进制包$ wget https://mirrors.shu.edu.cn/mariadb//mariadb-10.3.13/bintar-linux-x86_64/mariadb-10.3.13-linux-x86_64.tar.gz2. 解压$ tar -zxvf mariadb-10.3.13-linux-x86_64.tar.gz3. 将解压后的二进制包放置软件目录这里我习惯放置在/usr/local下,并改名mysql$ mv mariadb-10.3.13-linux-x86_64 /usr/local/mysql4. 创建数据存储目录与管理用户mysql$ groupadd mysql$ useradd -s /sbin/nologin -M mysql$ cd /usr/local/mysql$ chown -R mysql:mysql .5.初始化$ ./scripts/mysql_install_db –user=mysql –basedir=/usr/local/mysql –datadir=/usr/local/mysql/data这一步如果报错:/usr/local/mariadb/bin/mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory 解决:yum install -y libaio-devel6.检查是否安装成功在初始化命令之后使用$ ech $?,查看返回结果是否为0,为0表示成功,不为0表示失败。 7.拷贝启动脚本,并用chkconfig工具管理$ cd /usr/local/mysql$ cp support-files/mysql.server /etc/init.d/mysqld# 定义启动脚本内容,修改datadir和basedir$ vim /etc/init.d/mysqld# 修改basedir=/usr/local/mysql # 软件的目录datadir==/usr/local/mysql/data # 存放数据目录# 修改完,设置开机自启动$ chkconfig –add mysqld$ chkconfig mysqld on8.设置mariadb配置文件/etc/my.cnf$ vim /etc/my.cnf# 修改配置内容为datadir=/usr/local/mysql/datasocket=/tmp/mysql.socklog-error=/usr/local/mysql/data/mariadb.logpid-file=/usr/local/mysql/data/mariadb.pid保存退出。9. 启动mariadb服务$ /etc/init.d/mysqld start #启动mariadb10. 查看端口进程[root@lnmp mysql]$ netstat -lntp | grep mysqltcp6 0 0 :::3306 :::* LISTEN 4225/mysqld [root@lnmp mysql]$ ps aux | grep mysqlroot 4140 0.0 0.0 115436 1756 ? S 13:31 0:00 /bin/sh /usr/local/mysql/bin/mysqld_safe –datadir=/usr/local/mysql/data –pid-file=/usr/local/mysql/data/lnmp.pidmysql 4225 0.0 4.8 1713716 90736 ? Sl 13:31 0:00 /usr/local/mysql/bin/mysqld –basedir=/usr/local/mysql –datadir=/usr/local/mysql/data –plugin-dir=/usr/local/mysql/lib/plugin –user=mysql –log-error=/usr/local/mysql/data/mariadb.log –pid-file=/usr/local/mysql/data/lnmp.pid –socket=/tmp/mysql.sockroot 4280 0.0 0.0 112728 964 pts/1 S+ 13:43 0:00 grep –color=auto mysql 11. 进入mariadb数据库 $ /usr/local/mysql/bin/mysql Welcome to the MariaDB monitor. Commands end with ; or \g. Your MariaDB connection id is 10 Server version: 10.3.13-MariaDB MariaDB Server Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement. MariaDB [(none)]> 12. 设置软连接$ ln -s /usr/local/mysql/bin/mysql /usr/bin $ ln -s /usr/local/mysql/bin/mysqladmin /usr/bin13. 设置root密码$ mysqladmin -u root -p ‘!@#123qwe'14. root用户登录mysql[root@lnmp mysql]# mysql -u root -pEnter password: Welcome to the MariaDB monitor. Commands end with ; or \g.Your MariaDB connection id is 16Server version: 10.3.13-MariaDB MariaDB ServerCopyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.MariaDB [(none)]> 至此mariadb安装完毕。安装php7.3.31. 下载软件包下载地址:http://php.net/get/php-7.3.3.tar.bz2/from/a/mirror选择中国镜像站点下载,或者复制链接地址wget下载也可以。这里我是先下载到我的window再上传到linux。2. 解压$ tar -jxvf php-7.3.3.tar.bz2 # 如果没有安装bzip2工具会报错$ yum install -y bzip23. 安装依赖包$ yum install -y gcc libxml2-devel zlib-devel openssl-devel bzip2-devel libjpeg-devel libpng-devel epel-release libmcrypt-devel curl-devel freetype-devel4. 编译参数$ ./configure –prefix=/usr/local/php-fpm –with-config-file-path=/usr/local/php/etc –enable-fpm –with-fpm-user=php-fpm –with-fpm-group=php-fpm –with-mysql=mysqlnd –with-mysqli=mysqlnd –with-pdo-mysql=mysqlnd –with-mysql-sock=/tmp/mysql.sock –with-libxml-dir –with-gd –with-jpeg-dir –with-png-dir –with-freetype-dir –with-iconv-dir –with-zlib-dir –with-mcrypt –enable-soap –enable-gd-native-ttf –enable-ftp –enable-mbstring –enable-exif –with-pear –with-curl –with-openssl5. 安装make && make install6. 拷贝配置文件$ cp /usr/local/src/php-7.3.3/php.ini-development /usr/local/php-fpm/etc/php.ini$ cp /usr/local/php-fpm/etc/php-fpm.conf.default /usr/local/php-fpm/etc/php-fpm.conf7. 配置启动脚本设置开机启动$ cp /usr/local/src/php-7.3.3/sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm$ chmod +x php-fpm$ chkconfig –add php-fpm$ chkconfig php-fom on8. 创建用户,配置pool$ useradd php-fpm$ cp /usr/local/php-fpm/etc/php-fpm.d/www.conf.default /usr/local/php-fpm/etc/php-fpm.d/www.conf9.启动服务,查看进程[root@lnmp ~]$ service php-fpm startStarting php-fpm done[root@lnmp ~]$ ps aux | grep php-fpmroot 18747 0.0 0.3 115992 6284 ? Ss 13:27 0:00 php-fpm: master process (/usr/local/php-fpm/etc/php-fpm.conf)php-fpm 18748 0.0 0.3 115992 5836 ? S 13:27 0:00 php-fpm: pool wwwphp-fpm 18749 0.0 0.3 115992 5836 ? S 13:27 0:00 php-fpm: pool wwwroot 18751 0.0 0.0 112724 988 pts/0 S+ 13:27 0:00 grep –color=auto php-fpm[root@lnmp ~]$ netstat -lntp | grep php-fpmtcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 18747/php-fpm: mast php安装完毕。编译安装nginx1.14.21. 下载软件包$ cd /usr/local/src$ wget http://nginx.org/download/nginx-1.14.2.tar.gz2. 解压$ tar -zxvf nginx-1.14.2.tar.gz 3. 编译安装$ cd nginx-1.14.2$ ./configure –prefix=/usr/local/nginx –with-http_stub_status_module –with-http_ssl_module$ make && make install4. 添加nginx启动脚本$ vim /etc/init.d/nginx内容如下:#!/bin/bash# chkconfig: - 30 21# description: http service.# Source Function Library. /etc/init.d/functions# Nginx SettingsNGINX_SBIN="/usr/local/nginx/sbin/nginx"NGINX_CONF="/usr/local/nginx/conf/nginx.conf"NGINX_PID="/usr/local/nginx/logs/nginx.pid"RETVAL=0prog=“Nginx"start() { echo -n $“Starting $prog: " mkdir -p /dev/shm/nginx_temp daemon $NGINX_SBIN -c $NGINX_CONF RETVAL=$? echo return $RETVAL}stop() { echo -n $“Stopping $prog: " killproc -p $NGINX_PID $NGINX_SBIN -TERM rm -rf /dev/shm/nginx_temp RETVAL=$? echo return $RETVAL}reload(){ echo -n $“Reloading $prog: " killproc -p $NGINX_PID $NGINX_SBIN -HUP RETVAL=$? echo return $RETVAL}restart(){ stop start}configtest(){ $NGINX_SBIN -c $NGINX_CONF -t return 0}case “$1” in start) start ;; stop) stop ;; reload) reload ;; restart) restart ;; configtest) configtest ;; ) echo $“Usage: $0 {start|stop|reload|restart|configtest}” RETVAL=1esacexit $RETVAL5. 设置nginx开机启动$ chmod +x /etc/init.d/nginx$ chkconfig –add nginx$ chkconfig nginx on6. 启动nignx,查看进程端口[root@lnmp nginx-1.14.2]$ /etc/init.d/nginx startStarting nginx (via systemctl): [ 确定 ][root@lnmp nginx-1.14.2]$ ps aux | grep nginxroot 21486 0.0 0.0 45948 1120 ? Ss 13:50 0:00 nginx: master process /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.confnobody 21487 0.0 0.1 46384 1900 ? S 13:50 0:00 nginx: worker processroot 21489 0.0 0.0 112724 988 pts/0 S+ 13:50 0:00 grep –color=auto nginx[root@lnmp nginx-1.14.2]$ netstat -lntp | grep nginxtcp 0 0 0.0.0.0:80 0.0.0.0: LISTEN 21486/nginx: master 至此nginx安装完毕。 ...

March 10, 2019 · 3 min · jiezi

nginx配置文件之location匹配详解

作为一名PHPer,配置nginx是我们必会的技能项之一。但是当用户请求到达nginx之后,是如何匹配到对应的配置项的呢?location语法规则首先我们先了解下location的语法规则,location语法规则大致可以分为两类一类是指定URL地址:location [ = | ~ | * | ^ ] uri { … }另一类则是指定自定义名称:location @name { … }@name是对location设置的一个自定义名称,由@+自定义命名组成,一般用于try_files后面的内部请求,且自定义命名location中不可再嵌套自定义命名location举例:location @xuzheng{ …}location修饰符含义上文我们提到location语法规则有一类是指定URL地址,其中运用了一些修饰符,下面我们看下这些修饰符的含义1、 = 表示精确匹配。只有请求的url路径与后面的字符串完全相等时,才会命中。举例:location = / { …}2、 ~ 表示该规则是使用正则定义的,区分大小写。举例:location ~ .php$ { …}3、 * 表示该规则是使用正则定义的,不区分大小写。举例:location * .php$ { …}4、^ 是前缀匹配的一种,但是如果该符号后面的字符被匹配上,则被默认为最佳匹配,即采用该规则,不再进行后续的查找。举例:location ^ /blogs { …}location匹配优先级最后,我们了解下location的查找规则。下面列出的location查找,从上到下的优先级为从高到低1、等号类型,精确匹配2、^~类型,前缀匹配,不支持正则,如果该符号后面的字符匹配被匹配上,则被默认为最佳匹配,不再继续往下查找3、和*类型,正则匹配,区分大小写,*不区分大小写4、前缀匹配类型,如location / {}(表示任何以/开头的URL都匹配)或location /user {},只不过找到合适了还会继续往下找,直到找到最长匹配注:使用正则匹配的规则时,顺序很重要,location只要找到第一个符合条件的配置规则就会停止查找,即使下面有更匹配的配置总结location匹配过程:1、首先先检查使用前缀匹配(即字符串匹配)定义的location,选择最长匹配的项并记录下来2、如果找到了精确匹配的location,也就是使用了=修饰符的location,结束查找,使用它的配置。如果没有找到合适精确匹配,则继续往下查找3、如果找到了包含^~的前缀匹配,则停止查找,使用它定义的配置。如果没有找到合适的带^~符号的前缀匹配,则继续往下查找4、如果找到了使用正则定义的location,则停止查找,使用它定义的配置。如果没有找到合适的正则匹配,则继续往下查找5、使用前面记录的最长匹配前缀字符location。

March 10, 2019 · 1 min · jiezi

加载JavaScript文件时nginx报::ERR_CONTENT_LENGTH_MISMATCH错误解决

背景使用nginx做反向代理,使用8013端口代理4200端口的angular,问题出现的原因是在开发过程中一次清除浏览器缓存,之后8013端口产生::ERR_CONTENT_LENGTH_MISMATCH错误,但原4200端口正常,图片如下所示系统:Mac OS 10.13.5访问8013时访问4200时定位问题因为在8013端口已经映射出加载界面,所以开始找问题的方向一直在angular,一直没有找到,后来找到喜硕,分析着4200端口访问是成功的所以感觉问题还是处在nginx上,看报错的意思发现内容太长,于是试着删了几个模块发现居然好了,再到后来在google上查了一下这个问题,发现大概时nginx的缓存方向的问题。打开nginx的日志文件,运行以下指令: tail -f /usr/local/var/log/nginx/error.log,发现时文件权限禁止,failed (13: Permission denied) while reading upstream翻译:失败(13: 权限被拒绝)同时读取文件xyz的上游解决方法之后用 如下命令开放权限之后,发现就好了sudo nginx -s stopsudo chmod -R 777 /usr/local/var/run/nginx/*nginx原因:nginx会缓存大文件到proxy_temp目录中,然而对这个目录没有读写权限,nginx 的工作进程对大文件做了缓存,这个缓存在 %nginx%/proxy_temp 目录下,主进程在读取缓存的时候由于权限问题而无法访问。大概原因应该是在未清理浏览器缓存的时候,nginx只是提供部分内容,所以未产生大文件缓存,当你清除浏览器缓存的时候,nginx就会提供所有的内容,因此将产生缓存。

March 9, 2019 · 1 min · jiezi

记录一次windows server上,反向代理服务器的配置和使用

背景我司的软件在一个客户处测试功能和性能,这个客户比较特殊:他们客户端是很旧的java代码,且要求不能改动,客户端的主要业务简单说就是上传下载文件他们提供了客户端demo,http请求是用裸socket手动加http头,写死了http1.1,但又不带’host’这个http header客户要求中间必须经过一台windows server服务器代理后端的实际服务器是linux系统,用的是nginxhost header问题(此时先直连后端服务,不考虑代理)最开始是请求直接返400,nginx access log可以看到400但是没有更多信息,error log则没有任何信息打印,一开始另一位同事负责定位,我跟着一起用wireshark抓包看了很久,没得出结论。后来我又看了一下,nginx error log默认的日志级别是crit,那么直接改到debug,看到这个信息:那么问题就一目了然了,查了一下,这个header是http1.1要求必须带的且nginx严格遵守该协议,没有提供可配置的方式忽略这个头,按理说其实服务端不应该强行处理这种问题,但客户要求最高,没办法这时候好在,正好我们需要一层反向代理,那么,看看能不能找一个允许不带该header的反向代理服务器实现,接下来可以暂时忽略这个问题,我们使用压测工具测性能,压测工具在这个header上是没有问题的代理布署及性能问题windows server系统的服务器,一开始我们直接用Nginx,但简单测后就知道性能很差,搜了一下基本是说nginx和apache在windows上性能都没啥希望,首推的基本还是微软自家的IIS,于是我们配起了IIS,果然性能与直连后端相比基本没有损耗,但是经过很长时间查资料,找不到配置忽略host header的方法,而且IIS的确不好用,配置比较难看懂,大家都不熟悉windows平台,接下来只能另辟蹊径python:老本行,用twisted写了个4行的反向代理,性能比nginx倒是好一点,但大概也只有直连的1/6,时间紧迫,没来得及分析,继续尝试其他语言nodejs:在github找到个redbird库,代码也是只需要两行,但性能跟python半斤八两java:捡了个undertow的框架,折腾挺久终于搞起来,倒是基本没有性能损耗,于是就要解决header问题,但是这个版本的400返回没带提示信息,java框架源码要反编译看,这个倒还好,但是没有错误信息,不好直接搜代码,只能看流程,比较难看go:代码倒是长一些,要二三十行,性能不出意外也是等于直连,惊喜的来了,它的400带了个错误信息提示:missing required Host header,当时那个幸福感。。。无法形容,直接对go产生了满分好感于是直接打开源码目录全局搜,找到src/net/http/server.go,如下一段// hosts, haveHost := req.Header["Host"]isH2Upgrade := req.isH2Upgrade()// if req.ProtoAtLeast(1, 1) &amp;&amp; (!haveHost || len(hosts) == 0) &amp;&amp; !isH2Upgrade &amp;&amp; req.Method != "CONNECT" {// return nil, badRequestError("missing required Host header")// }// if len(hosts) &gt; 1 {// return nil, badRequestError("too many Host headers")// }// if len(hosts) == 1 &amp;&amp; !httpguts.ValidHostHeader(hosts[0]) {// return nil, badRequestError("malformed Host header")// }果断注释掉了上面这些,跑起来没有问题。但是到这还没结束,压测跑了一会儿发现请求全部失败,看了一下报错,socket爆了,在cmd里netstat查了一下,果然go进程接近2w的socket消耗,全部是TIME_WAIT状态查一下就知道,本端主动关闭的socket,就会进入该状态,被对方关闭是CLOSE_WAIT。那么查一下,有说设置注册表的[HKEY_LOCAL_MACHINESYSTEMCurrentControlSetservicesTcpipParameters]“TcpTimedWaitDelay”=dword:0000001e感觉并不是解决办法?实测也不能解决问题。想想就知道,关键应该是找到为什么go会频繁关闭socket,那么google的关键字就应该是这样了:go socket time_wait(而不是windows socket time_wait)搜索结果第一条stackoverflow的就是答案:https://stackoverflow.com/que…在main函数里加上配置:http.DefaultTransport.(*http.Transport).MaxIdleConns = 8192http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 8192这个值的含义见链接,简单说就是允许接收的最大并发数,我们压测工具跑的是100并发,实际只需要这个值是200就够了 ...

March 8, 2019 · 1 min · jiezi

nginx 二级目录

需求:使用一台服务器的80端口部署api+web+接收微信 接口配置信息 的GET请求前端开发使用JS-SDK来调用腾讯地图的功能并在公众号正常使用.开发时只有一台腾讯云的服务器,需要同时配置微信公众号的 接口配置信息, JS接口安全域名.[见微信公众号配置]nginx配置[80端口的server]:# Default server configuration#server { listen 80 default_server; listen [::]:80 default_server; # SSL configuration # # listen 443 ssl default_server; # listen [::]:443 ssl default_server; # # Note: You should disable gzip for SSL traffic. # See: https://bugs.debian.org/773332 # # Read up on ssl_ciphers to ensure a secure configuration. # See: https://bugs.debian.org/765782 # # Self signed certs generated by the ssl-cert package # Don’t use them in a production server! # # include snippets/snakeoil.conf; #root /var/www/html; # Add index.php to the list if you are using PHP #index index.html index.htm index.nginx-debian.html; server_name _; location / { # 此处配置是接收微信公众号 接口配置信息 的请求,微信服务器需要发送HTTP GET请求给当前server的80端口,只能是80端口. # 后端使用的flask+uwsgi,此处配置保证转发了HTTP请求,对应后端的flask api可用. include uwsgi_params; uwsgi_pass 127.0.0.1:5000; } #location /v1/api/ { # 此处配置的是项目的api接口服务,此处配置和上面的location / 合一起了. # include uwsgi_params; # uwsgi_pass 127.0.0.1:5000; #} # 此处的需求: # 访问http://IP/sites/index.html 能正常访问index.html页面,同时保证api可用. location /sites { alias /root/sites; # web项目的目录 index index.html; # index.html try_files $uri @rewriteweb; } location @rewriteweb { rewrite ^/sites/(.*)$ /sites/index.html break; # 注意此处的break }} ...

March 8, 2019 · 1 min · jiezi

通过阿里云K8S Ingress Controller实现路由配置的动态更新

摘要: 本文主要描述了阿里云Kubernetes集群Ingress Controller如何通过动态更新的方式来极大地缓解转发平面Nginx频繁Reload带来的影响。简介在Kubernetes集群中,Ingress作为集群内服务对外暴露的访问接入点,其几乎承载着集群内服务访问的所有流量。我们知道,Nginx Ingress Controller是Kubernetes社区很重要的一个子项目,其内部主要依托于高性能的负载均衡软件Nginx,将Kubernetes Ingress资源对象实时地自动化转换为Nginx配置规则来对外提供期望的授权访问入口。现实问题当随着Kubernetes集群中部署的微服务越来越多,对外暴露的路由规则越来越复杂,服务后端Endpoint变化的越来越频繁,那么对应地都会引起Nginx Ingress Controller组件内Nginx配置文件的变化越来越频繁;而我们知道,任何一行Nginx配置的变化,都需要Reload Nginx才能生效,这在变化频率较低的场景下索性还能接受,但在高频率变化的场景下就会引起Nginx的频繁Reload。而Nginx频繁Reload带来的问题,这已是一个老生常谈的话题了,其问题本质主要还是源于Nginx本身最初的架构设计模型:一般在Linux服务器中,我们会配置使用Nginx的EPOLL多进程模式;当我们修改了Nginx配置文件后,需要通过nginx -s reload命令来重新热加载新的Nginx配置规则;当Nginx Master进程接收到reload signal后,其会从指定路径重新加载新的Nginx配置文件内容,并校验配置规则的有效性,若检验为有效的配置文件,则会依据新的配置文件中的worker_processes值fork出指定数量的新的Nginx Worker进程,此时新fork出来的子进程完全继承了父进程的内存数据ngx_cycle(其包含了新的解析后的Nginx配置规则),同时将配置中的每一个Listen Socket FD注册到内核的EPOLL事件监听中,此时这些新的Nginx Worker进程可以接收处理来自客户端的请求;同时Nginx Master进程会发送QUIT signal通知老的Nginx Worker进程平滑退出,当老的Nginx Worker进程接收到QTUI信号后,将其之前注册到EPOLL中的监听Event移除,至此不再接收处理新的客户端请求,并依据老配置文件中设置的worker_shutdown_timeout值来设置定时器,然后继续处理完已接收的客户端请求;若在worker_shutdown_timeout之前处理完已有的客户端请求,则自动退出,若未处理完,则被强制Kill退出,此时就会导致该客户端请求响应异常。因此,对于在高频率变化的场景下,Nginx频繁Reload会带来较明显的请求访问问题:造成一定的QPS抖动和访问失败情况对于长连接服务会被频繁断掉造成大量的处于shutting down的Nginx Worker进程,进而引起内存膨胀动态更新为缓解Nginx频繁Reload带来的影响,我们需要通过动态更新的方式来加载Nginx配置规则,即在不Fork新Nginx Worker进程的情况下来实时更新已加载到内存中的Nginx配置规则。首先我们看下Nginx的配置文件样式,主要包含下面几部分配置章节:# 1. main configurationdaemon off;worker_processes 4;events { # 2. event configuration multi_accept on; worker_connections 1024; use epoll;}http { # 3. http main configuration access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; upstream { # 4. upstream configuration server 0.0.0.1; } server { # 5. server configuration server_name _ ; listen 80 default_server; location / { # 6. location configuration proxy_pass http://upstream_balancer; }}而在Kubernetes集群中,一个Ingress资源对象主要被解析映射到Nginx的HTTP Main Block、Server Block、Upstream Block和Location Block等章节的配置规则上,因此我们可以将这几部分频繁变化的配置内容以Shared Memory的方式统一维持在内存中,同时在Ingress Controller内部暴露出管控端口,通过API的方式来实时管理Nginx路由规则配置:当K8S Ingress Controller监控到集群内Ingress及相关联的资源发生变化时,均可通过Internal API将最新的Nginx配置规则推送到统一的共享内存配置中,而不用再通过Reload Nginx的方式来使新配置生效,至此当Nginx处理任何新接收的客户端请求时,都可以基于最新的共享内存中的配置进行规则匹配和路由转发;配置说明1、目前阿里云容器服务Kubernetes集群中最新版本的Nginx Ingress Controller组件默认已开启Upstream的动态更新,同时支持应用服务的灰度发布和蓝绿发布功能,具体配置说明可参考这里;我们可以通过如下命令来查看当前共享内存中的Nginx Upstream的配置列表:kubectl -n kube-system exec -it <NGINX-INGRESS-CONOTROLLER-POD-NAME> – curl http://127.0.0.1:18080/configuration/backends2、同时也支持HTTPS证书的动态更新,可通过修改nginx-ingress-controller deployment的如下参数配置来开启Nginx Ingress Controller的证书动态更新:- args: - /nginx-ingress-controller - –configmap=$(POD_NAMESPACE)/nginx-configuration - –tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - –udp-services-configmap=$(POD_NAMESPACE)/udp-services - –annotations-prefix=nginx.ingress.kubernetes.io - –publish-service=$(POD_NAMESPACE)/nginx-ingress-lb - –enable-dynamic-certificates=true ### 添加该配置 - –v=2当开启HTTPS证书的动态更新后,Ingress的TLS证书都统一维护在Nginx的共享内存中,我们可通过如下命令来查看当前共享内存中配置的证书列表:kubectl -n kube-system exec -it <NGINX-INGRESS-CONOTROLLER-POD-NAME> – curl http://127.0.0.1:18080/configuration/certs3、进一步地我们也支持Nginx Server和Location配置的动态更新,可通过修改nginx-ingress-controller deployment的如下参数配置来开启Nginx Ingress Controller的Server和Location的动态更新:- args: - /nginx-ingress-controller - –configmap=$(POD_NAMESPACE)/nginx-configuration - –tcp-services-configmap=$(POD_NAMESPACE)/tcp-services - –udp-services-configmap=$(POD_NAMESPACE)/udp-services - –annotations-prefix=nginx.ingress.kubernetes.io - –publish-service=$(POD_NAMESPACE)/nginx-ingress-lb - –enable-dynamic-certificates=true ### 添加该配置 - –enable-dynamic-servers=true ### 添加该配置,同时也要enable-dynamic-certificates - –v=2同样地,当我们开启了Nginx Ingress Controller的Server动态更新后,所有Nginx Server和Location的配置都统一维护在共享内存中,我们同样可以通过如下命令来查看当前共享内存中的Server配置列表:kubectl -n kube-system exec -it <NGINX-INGRESS-CONOTROLLER-POD-NAME> – curl http://127.0.0.1:18080/configuration/servers注意说明:当开启Server的动态更新特性后,部分Ingress Annotation配置暂不支持,正在逐步优化支持中,相应地您可直接通过ConfigMap方式来进行配置;本文作者:chenqz阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 8, 2019 · 1 min · jiezi

使用idea调试lua代码-Openresty

使用idea调试lua代码Openresty是基于nginx与lua的高性能web框架,继承了大量的高质量的lua库、第三方模块以及大多数依赖项。目前对于lua主流开发工具有vscode+lua插件、IntelliJ IDEA+EmmyLua、ZeroBrane Studio、还有其他的一些文本编辑软件等。lua作为一种脚本语言,除了开发简洁,性能优越之外,还应该具备调试功能,对于开发者才能算得上更加友好。本文将使用IntelliJ IDEA+EmmyLua使用远程调试和本地调试。IntelliJ IDEA 2018.2.1Lua 5.1EmmyLua 1.2.6-IDEA182MobDebug 0.70项目目录结构源码位置often-script一、远程调试1、打开idea中调试配置,使用Lua Remote(Mobdebug),如下图:2、配置调试名称和远程调试端口;3、在需要调试的位置加上调试代码;— 启动调试local mobdebug = require(“src.initial.mobdebug”);mobdebug.start();4、启动Openresty项目,然后打开debug模式;5、启动openresty项目;# 进入到工作目录cd /Users/xiaoyueya/projects/vscode/often-script/lua/project# 启动nginxsudo nginx -p ./ -c nginx-debug.conf -s reload6、刷新浏览器;7、断点位置和lua栈信息;8、执行结果;二、本地调试1、打开idea中调试配置,使用lua application ,如下图:2、配置工作目录和执行文件入口;3、编写调试名称为main.lua,然后点击OK,进入主编辑页,找到调试按钮;4、开始本地调试;5、查看堆栈信息;6、查看执行结果

March 8, 2019 · 1 min · jiezi

openresty/nginx升级http2

http2需要https支持,假设你已经有https证书从源码编译安装openresty解压:tar -xzvf openresty-VERSION.tar.gzcd openresty-VERSION/三连:./configuremakesudo make install此时安装的openresty配置http2会报错 the “http2” parameter requires ngx_http_v2_module重新编译:./configure –with-http_v2_module编译完成后make (不要make insall ,会覆盖安装)编译出的nginx在 openresty-1.13.6.2/build/nginx-1.13.6/objs/里我们之拥替换此二进制文件就好了nginx -s stop先停止nginx将openresty-1.13.6.2/build/nginx-1.13.6/objs/nginx 复制到/usr/local/openresty/nginx/sbin不先停止nginx的话会出现 cp: 无法创建普通文件’./nginx’: 文本文件忙然后将listen的http2配置上启动nginx应该就可以看到如果替换了新编译的nginx二进制文件还是使用http1.1,检查一下openssl版本和编译的模块,openssl版本需大于1.0.1才能支持http2

March 8, 2019 · 1 min · jiezi

nginx日志自动每日切割脚本

1、脚本内容#!/bin/bash#日志文件存放目录logs_path="/var/log/nginx/e/"# pid文件pid_path="/run/nginx.pid"#重命名日志文件mv ${logs_path}access.log ${logs_path}access_$(date -d “yesterday” +"%Y%m%d").log#向nginx主进程发送信号以重新打开日志kill -USR1 cat ${pid_path}2、Crontab任务配置0 0 * sh /home/ubuntu/www/Pikachu/shell/cutAccessLogs.sh

March 7, 2019 · 1 min · jiezi

已安装nginx新增模块(以新增headers-more-nginx-module模块为例)

需求场景编译好的nginx需要添加headers-more-nginx-module模块来自定义响应头nginx其它模块同样适用环境nginx包目录:/app/nginx-1.12.2nginx安装目录:/app/nginx112插件目录:/app/tools/headers-more-nginx-module-0.33系统环境centOs7资源地址headers-more-nginx-module-0.33github地址查看插件支持的nginx版本github地址headers-more-nginx-module下载地址nginx下载地址下载# 举例目录/app/toolscd /app/tools/#下载插件wget https://github.com/openresty/headers-more-nginx-module/archive/v0.33.tar.gz#解压tar -zxvf v0.33.tar.gz加载模块# 查看安装参数命令(取出:configure arguments:)/app/nginx/sbin/nginx -V# 在nginx资源目录编译cd /app/nginx-1.12.2/# 将上面取出的configure arguments后面追加 –add-module=/app/tools/headers-more-nginx-module-0.33./configure –prefix=/app/nginx112 –add-module=/app/tools/headers-more-nginx-module-0.33# 编辑,切记没有make installmake# 备份cp /app/nginx112/sbin/nginx /app/nginx112/sbin/nginx.bak # 覆盖(覆盖提示输入y)cp -f /app/nginx-1.12.2/objs/nginx /app/nginx112/sbin/nginx修改配置vim /app/nginx112/conf/nginx.conf# 添加配置(在http模块)more_clear_headers ‘Server’;上面配置只是将http响应头中的Server:nginx/1.12.2清楚,详细使用方案可阅读参考文档,支持添加·修改·清除响应头的操作,重启nginx/app/nginx112/sbin/nginx -s stop/app/nginx112/sbin/nginx直接使用reload可能会无效

March 7, 2019 · 1 min · jiezi

Dockerfile构建PHP开发镜像:Alpine+Nginx+PHP7+Supervisor+Crontab+Laravel

一、配置目录结构1.1 crontabs# do daily/weekly/monthly maintenance# min hour day month weekday command# * * * * * /usr/bin/php /usr/share/nginx/html/artisan schedule:run >> /dev/null 2>&1Tips:一般在生产环境都是负载均衡,定时任务是单独在一个容器执行,故此是注释掉的,需要单独在定时任务容器中打开即可。2.1 nginx与php配置文件Tips:由于php配置文件是监听的sock,默认情况下监听端口 9000。PHP-FPM使用Unix套接字,这避免了TCP的开销。3.1 php二、Dockerfile详解#1.Base ImageFROM alpine# ensure www-data user exists#RUN set -x # && addgroup -g 82 -S www-data # && adduser -u 82 -D -S -G www-data www-data# EnvironmentsENV TIMEZONE Asia/ShanghaiENV PHP_MEMORY_LIMIT 512MENV MAX_UPLOAD 50MENV PHP_MAX_FILE_UPLOAD 200ENV PHP_MAX_POST 100MENV COMPOSER_ALLOW_SUPERUSER 1#2.ADD-PHP-FPM# Mirror mirror switch to Alpine Linux - http://dl-4.alpinelinux.org/alpine/RUN apk update \ && apk upgrade \ && apk add \ curl \ tzdata \ php7-fpm\ php7 \ php7-dev \ php7-apcu \ php7-bcmath \ php7-xmlwriter \ php7-ctype \ php7-curl \ php7-exif \ php7-iconv \ php7-intl \ php7-json \ php7-mbstring\ php7-opcache \ php7-openssl \ php7-pcntl \ php7-pdo \ php7-mysqlnd \ php7-mysqli \ php7-pdo_mysql \ php7-pdo_pgsql \ php7-phar \ php7-posix \ php7-session \ php7-xml \ php7-simplexml \ php7-mcrypt \ php7-xsl \ php7-zip \ php7-zlib \ php7-dom \ php7-redis\ php7-tokenizer \ php7-gd \ php7-mongodb\ php7-fileinfo \ php7-zmq \ php7-memcached \ php7-xmlreader \ && cp /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \ && echo “${TIMEZONE}” > /etc/timezone \ && apk del tzdata \ && rm -rf /var/cache/apk/# https://github.com/docker-library/php/issues/240# https://gist.github.com/guillemcanal/be3db96d3caa315b4e2b8259cab7d07e# https://forum.alpinelinux.org/forum/installation/php-iconv-issueRUN mkdir -p /usr/local/var/log/php7/RUN mkdir -p /usr/local/var/run/COPY ./php/php-fpm.conf /etc/php7/COPY ./php/www.conf /etc/php7/php-fpm.d/RUN apk add –no-cache –repository http://dl-3.alpinelinux.org/alpine/edge/testing gnu-libiconvENV LD_PRELOAD /usr/lib/preloadable_libiconv.so phpRUN rm -rf /var/cache/apk/# Set environmentsRUN sed -i “s|;date.timezone =.|date.timezone = ${TIMEZONE}|i” /etc/php7/php.ini && \ sed -i “s|;memory_limit =.|memory_limit = ${PHP_MEMORY_LIMIT}|i” /etc/php7/php.ini && \ sed -i “s|;upload_max_filesize =.|upload_max_filesize = ${MAX_UPLOAD}|i” /etc/php7/php.ini && \ sed -i “s|;max_file_uploads =.|max_file_uploads = ${PHP_MAX_FILE_UPLOAD}|i” /etc/php7/php.ini && \ sed -i “s|;post_max_size =.|post_max_size = ${PHP_MAX_POST}|i” /etc/php7/php.ini && \ sed -i “s|;cgi.fix_pathinfo=.|cgi.fix_pathinfo= 0|i” /etc/php7/php.ini#3.Install-ComposerRUN curl -sS https://getcomposer.org/installer | \ php – –install-dir=/usr/bin/ –filename=composer#4.ADD-NGINXRUN apk add nginxCOPY ./nginx/conf.d/default.conf /etc/nginx/conf.d/COPY ./nginx/nginx.conf /etc/nginx/COPY ./nginx/cert/ /etc/nginx/cert/RUN mkdir -p /usr/share/nginx/html/public/COPY ./php/index.php /usr/share/nginx/html/public/#RUN mkdir -p /run/nginx#RUN touch /run/nginx/nginx.pid# Expose volumesVOLUME ["/usr/share/nginx/html", “/usr/local/var/log/php7”, “/var/run/"]WORKDIR /usr/share/nginx/html#5.ADD-SUPERVISORRUN apk add supervisor \ && rm -rf /var/cache/apk/*# Define mountable directories.VOLUME ["/etc/supervisor/conf.d”, “/var/log/supervisor/"]COPY ./supervisor/conf.d/ /etc/supervisor/conf.d/#6.ADD-CRONTABSCOPY ./crontabs/default /var/spool/cron/crontabs/RUN cat /var/spool/cron/crontabs/default >> /var/spool/cron/crontabs/rootRUN mkdir -p /var/log/cron \ && touch /var/log/cron/cron.logVOLUME /var/log/cron#8.添加启动脚本# Define working directory.WORKDIR /usr/share/nginx/htmlCOPY ./entrypoint.sh /usr/share/nginx/html/RUN chmod +x /usr/share/nginx/html/entrypoint.sh#CMD [“supervisord”, “–nodaemon”, “–configuration”, “/etc/supervisor/conf.d/supervisord.conf”]ENTRYPOINT [”./entrypoint.sh"]传送门:Dockerfile、Docker-Compose基本命令与介绍三、镜像制作与启动3.1 根据Dockerfile制作镜像3.2 查看制作的镜像3.3 启动镜像3.4 浏览器查看是否成功四、镜像发布4.1 镜像发布一定要登录4.2 自动构建当关联的github代码发生改变是,自动触发构建。GitHub地址:https://github.com/tcyfree/anpscDocker Hub镜像地址:https://cloud.docker.com/repo… ...

March 7, 2019 · 2 min · jiezi

Ubuntu18.04搭建LNMP

在csdn上发布了一遍,然后又在这里发布一遍,哈哈,以下是csdn地址:https://blog.csdn.net/sinat_3… 最近闲来无事,遂下载了vmware,尝试搭建下lnmp,之前有试过搭建宝塔,lamp等,但最后结果都不怎么好,这次再次尝试搭建linux下的php开发环境,借鉴了很多网友的文章,历经千辛,搭建成功,遂将过程总结总结,分享出来。 这里是主要参考的两篇博客:快速搭建lnmp https://www.cnblogs.com/zhangbobo/p/9597446.html修改数据库密码 https://www.cnblogs.com/super-zhangkun/p/9435974.html开始 更新源 sudo apt-get update安装nginx sudo apt-get install nginx安装mysql sudo apt-get install mysql-server mysql-client这里会有个问题,有的文章说,安装过程会提示设置密码,然而。。。,这样,你将无法登录mysql,那么就这样放弃了吗?如下: cd /etc/mysql sudo cat debian.cnf 可以看到账号密码 使用这个账号密码登录mysql mysql -u :user -p :password use mysql; update user set authentication_string=PASSWORD(“密码”) where user=‘root’; update user set plugin=“mysql_native_password”; flush privileges; quit; 重启mysql /etc/init.d/mysql restart OJBK 什么?找不到debian?可以试一下find命令或者查看mysql安装位置find / debianps -ef|grep mysql 还是找不到?额,我也不知道。安装PHP 我安装的php7.2,安装前把已安装的php版本禁掉,怎么禁?百度或谷歌!!!至于想安装其他版本的PHP?看完我下面的命令,只要不是白痴应该都知道。 安装 apt install php7.2-cli 下载需要的扩展 apt install php7.2-fpm 将最后的fpm改成需要的扩展就行了 配置fpm cd /etc/php/7.2/fpm/pool.d sudo vim www.conf 找到 listen = /run/php/php7.2-fpm.sock 大概在36行,取消注释,或者修改为 listen = 127.0.0.1:9000 重启fpm sudo service php7.2-fpm restart 测试php-fpm sudo php-fpm7.2 -t // 出现successful就行了配置nginx 有点懒,直接把我写在subline里面的 Ctrl + C出来吧cd /etc/nginx/sites-enabled sudo vim default 将第一个service的 #location ~ .php$ { #} 前面的#去掉(去掉这两个就行了,其余的需要用到时再去掉) 添加( 或修改:去掉# )fastcgi_pass unix:/run/php/php7.2-fpm.sock;(与之前在fpm里面的listen一致) 坑:这时重启nginx:sudo service nginx restart 然后在 /var/www/html 里面添加 phpinfo.php 输出phpinfo();页面一片空白 做以下修改: 在fastcgi_pass 后面添加: fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_script_name; include fastcgi_params; 重启nginx 在此步骤中,如果之前步骤没出错,但是重启nginx失败,请仔细检查自己的配置文件是否缺少’;’,或者 配置的开闭 {} 之前的 # 号去除的不对 再者检查php-fpm监听路径是否正确,如果是127.0.0.1:9000,还可以通过 netstat -tlnp 检查运行情况写在最后 以上就是我搭建lnmp的全过程,比我之前搭建的宝塔和lamp要顺利点,但也是有点曲折的,凡事还是要自己亲身体验体验,才能理解里面的艰辛。这篇教程,不说多么详细吧,但是还是可以避免一些坑的产生!!!如果看了之后还是搭建不成功,我建议多看看别人的博客,了解了解linux的基础命令再来重新搭建一次,或者,使用windows和mac!最后,Enjoy It ! ...

March 6, 2019 · 1 min · jiezi

深入 Nginx 之配置篇

常用配置项在工作中,我们与 Nginx 打交道更多的是通过其配置文件来进行。那么掌握这些配置项各自的作用就很有必要了。首先,nginx.conf 的内容通常是这样的:… … #核心摸块events { #事件模块 …}http { # http 模块 server { # server块 location [PATTERN] { # location块 … } location [PATTERN] { … } } server { … } }mail { # mail 模块 server { # server块 … }}我们依次看一下每个模块一般有哪些配置项:核心模块user admin; #配置用户或者组。worker_processes 4; #允许生成的进程数,默认为1 pid /nginx/pid/nginx.pid; #指定 nginx 进程运行文件存放地址 error_log log/error.log debug; #错误日志路径,级别。事件模块events { accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off use epoll; #事件驱动模型select|poll|kqueue|epoll|resig worker_connections 1024; #最大连接数,默认为512}http 模块http { include mime.types; #文件扩展名与文件类型映射表 default_type application/octet-stream; #默认文件类型,默认为text/plain access_log off; #取消服务日志 sendfile on; #允许 sendfile 方式传输文件,默认为off,可以在http块,server块,location块。 sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。 keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块。 server { keepalive_requests 120; #单连接请求上限次数。 listen 80; #监听端口 server_name 127.0.0.1; #监听地址 index index.html index.htm index.php; root your_path; #根目录 location ~ .php$ { fastcgi_pass unix:/var/run/php/php7.1-fpm.sock; #fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi_params; } }}配置项解析worker_processesworker_processes 用来设置 Nginx 服务的进程数。该值推荐使用 CPU 内核数。worker_cpu_affinityworker_cpu_affinity 用来为每个进程分配CPU的工作内核,参数有多个二进制值表示,每一组代表一个进程,每组中的每一位代表该进程使用CPU的情况,1代表使用,0代表不使用。所以我们使用 worker_cpu_affinity 0001 0010 0100 1000;来让进程分别绑定不同的核上。默认情况下worker进程不绑定在任何一个CPU上。worker_rlimit_nofile设置毎个进程的最大文件打开数。如果不设的话上限就是系统的 ulimit –n的数字,一般为65535。worker_connections设置一个进程理论允许的最大连接数,理论上越大越好,但不可以超过 worker_rlimit_nofile 的值。use epoll设置事件驱动模型使用 epoll。epoll 是 Nginx 支持的高性能事件驱动库之一。是公认的非 常优秀的事件驱动模型。accept_mutex off关闭网络连接序列化,当其设置为开启的时候,将会对多个 Nginx 进程接受连接进行序列化,防止多个进程对连接的争抢。当服务器连接数不多时,开启这个参数会让负载有一定程度的降低。但是当服务器的吞吐量很大时,为了效率,请关闭这个参数;并且关闭这个参数的时候也可以让请求在多个 worker 间的分配更均衡。所以我们设置 accept_mutex off;multi_accept on 设置一个进程可同时接受多个网络连接Sendfile onSendfile是 Linux2.0 以后的推出的一个系统调用,它能简化网络传输过程中的步骤,提高服务器性能。不用 sendfile的传统网络传输过程:硬盘 >> kernel buffer >> user buffer >> kernel socket buffer >> 协议栈用 sendfile()来进行网络传输的过程:硬盘 >> kernel buffer (快速拷贝到 kernelsocket buffer) >>协议栈tcp_nopush on;设置数据包会累积一下再一起传输,可以提高一些传输效率。 tcp_nopush 必须和 sendfile 搭配使用。tcp_nodelay on;小的数据包不等待直接传输。默认为on。
看上去是和 tcp_nopush 相反的功能,但是两边都为 on 时 nginx 也可以平衡这两个功能的使用。keepalive_timeoutHTTP 连接的持续时间。设的太长会使无用的线程变的太多。这个根据服务器访问数量、处理速度以及网络状况方面考虑。send_timeout设置 Nginx 服务器响应客户端的超时时间,这个超时时间只针对两个客户端和服务器建立连接后,某次活动之间的时间,如果这个时间后,客户端没有任何活动,Nginx服务器将关闭连接gzip on启用 gzip,对响应数据进行在线实时压缩,减少数据传输量。gzip_disable “msie6"Nginx服务器在响应这些种类的客户端请求时,不使用 Gzip 功能缓存应用数据,gzip_disable “msie6”对IE6浏览器的数据不进行 GZIP 压缩。常用的配置项大致这些,对于不同的业务场景,有的需要额外的其他配置项,这里不做展开。其他http 配置里有 location 这一项,它是用来根据请求中的 uri 来为其匹配相应的处理规则。location 查找规则location = / { # 精确匹配 / ,主机名后面不能带任何字符串 [ config A ]}location / { # 因为所有的地址都以 / 开头,所以这条规则将匹配到所有请求 # 但是正则和最长字符串会优先匹配 [ config B ]}location /documents/ { # 匹配任何以 /documents/ 开头的地址,匹配符合以后,还要继续往下搜索 # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条 [ config C ]}location ~ /documents/Abc { # 匹配任何以 /documents/Abc 开头的地址,匹配符合以后,还要继续往下搜索 # 只有后面的正则表达式没有匹配到时,这一条才会采用这一条 [ config CC ]}location ^~ /images/ { # 匹配任何以 /images/ 开头的地址,匹配符合以后,停止往下搜索正则,采用这一条。 [ config D ]}location * .(gif|jpg|jpeg)$ { # 匹配所有以 gif,jpg或jpeg 结尾的请求 # 然而,所有请求 /images/ 下的图片会被 config D 处理,因为 ^ 到达不了这一条正则 [ config E ]}location /images/ { # 字符匹配到 /images/,继续往下,会发现 ^~ 存在 [ config F ]}location /images/abc { # 最长字符匹配到 /images/abc,继续往下,会发现 ^~ 存在 # F与G的放置顺序是没有关系的 [ config G ]}location ~ /images/abc/ { # 只有去掉 config D 才有效:先最长匹配 config G 开头的地址,继续往下搜索,匹配到这一条正则,采用 [ config H ]}正则查找优先级从高到低依次如下:“ = ” 开头表示精确匹配,如 A 中只匹配根目录结尾的请求,后面不能带任何字符串。“ ^~ ” 开头表示uri以某个常规字符串开头,不是正则匹配“ ~ ” 开头表示区分大小写的正则匹配;“ ~* ”开头表示不区分大小写的正则匹配“ / ” 通用匹配, 如果没有其它匹配,任何请求都会匹配到负载均衡配置Nginx 的负载均衡需要用到 upstream 模块,可通过以下配置来实现:upstream test-upstream { ip_hash; # 使用 ip_hash 算法分配 server 192.168.1.1; # 要分配的 ip server 192.168.1.2;}server { location / { proxy_pass http://test-upstream; } }上面的例子定义了一个 test-upstream 的负载均衡配置,通过 proxy_pass 反向代理指令将请求转发给该模块进行分配处理。 ...

March 1, 2019 · 2 min · jiezi

CentOS 7 将 Nginx 添加系统服务

导语经过编译安装以及解决问题,Nginx 已经运行正常,但是此时 Nginx 并没有添加进系统服务。接下来会将 Nginx 添加进系统服务并且设置开机启动。查看服务首先查看 Nginx 的服务状态,输入 systemctl status nginx,结果如下没有找到相关的服务,下一步就是添加系统服务。添加系统服务在 /usr/lib/systemd/system 目录中添加 nginx.service,根据实际情况进行修改,详细解析可查看下方参考资料中的文章。内容如下[Unit]Description=nginx - high performance web serverDocumentation=http://nginx.org/en/docs/After=network.target remote-fs.target nss-lookup.target [Service]Type=forkingPIDFile=/usr/local/nginx/logs/nginx.pidExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.confExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.confExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s QUIT $MAINPIDPrivateTmp=true [Install]WantedBy=multi-user.target添加完成后再来看下设置开机自动启动首先测试一下,重启然后来查看 Nginx 服务确实没有启动。输入 systemctl start nginx 启动可以使用 systemctl 启动,说明之前添加的 nginx.service 没有问题。然后输入 systemctl enable nginx 设置开机启动最后重启检查下是否设置成功没有问题,到此关于 Nginx 的编译安装完成,接下来是 PHP 的安装。参考资料:CentOS 7 systemd添加自定义系统服务、

February 28, 2019 · 1 min · jiezi

使用logrotate完成日志自动切分并轮转

部署网络应用时,会对请求进行日志保存,用于数据统计分析以及故障排除,但对于高并发请求的服务器,日志文件会迅速增长,快速的消耗磁盘空间,同时,分析一个大文件来排查问题也会非常慢。因此,我们通常需要将日志按照天级别进行存储,并对过旧的日志进行压缩转存或删除,方便节省磁盘空间和进行脚本分析。当我第一次有这种需求的时候,最先想到的是crontab脚本定时执行日志清理脚本。就是先编写一个cleanLog.sh,然后让crontab定期的来执行它。这个方法可行,但是比较麻烦费事,此时一个Linux内置的工具就比较有用了:logrotate。logrotate: Linux日志文件总管logrotate(日志轮转工具)可以自动对日志文件提供截断、压缩以及轮转的功能。logrotate工具默认安装在linux机器上,全局命令在/usr/sbin/logrotate,另外还包含两个配置文件:// 全局配置文件,存储的为公用的默认配置项/etc/logrotate.conf// 子项配置文件夹,该文件夹下提供一些系统级别的日志配置,你的自定义配置也需要配置在这里/etc/logrotate.d/这个工具能做到自动执行的功能,其实还是依赖于crontab工具,只不过这些设定系统自动完成了,我们可以查看crontab系统级别的日运行脚本:$ vim /etc/cron.daily/logrotate#!/bin/sh/usr/sbin/logrotate /etc/logrotate.conf >/dev/null 2>&1EXITVALUE=$?if [ $EXITVALUE != 0 ]; then /usr/bin/logger -t logrotate “ALERT exited abnormally with [$EXITVALUE]“fiexit 0可以看到在crontab的日级别配置文件目录下有一个logrotate子项,它会每天执行logrotate命令,调用的配置为/etc/logrotate.conf。在实际执行中,logrotate命令会先读取/etc/logrotate.conf的配置作为默认项,然后再依次读取/etc/logrotate.d/目录下的各文件配置来覆盖默认项,并执行日志轮转功能。logrotate命令语法:logrotate [OPTION…] <configfile>参数说明:-d, –debug :debug模式,测试配置文件是否有错误。-f, –force :强制转储文件。-m, –mail=command :压缩日志后,发送日志到指定邮箱。-s, –state=statefile :使用指定的状态文件。-v, –verbose :显示转储过程。logrotate使用假设我们现在有一个日志文件存储在/home/work/log/nginx.access.log,需要对其每日进行切分为新旧两个日志文件,并删除7天前的旧日志。首先我们创建新的日志轮转配置:$vim /etc/logrotate.d/nginxAccessLog# 指定需要轮转处理的日志文件/home/work/log/nginx.access.log { # 日志文件轮转周期,可用值为: daily/weekly/yearly daily # 新日志文件的权限 create 0664 work work # 轮转次数,即最多存储7个归档日志,会删除最久的归档日志 rotate 7 # 以当前日期作为命名格式 dateext # 轮循结束后,已归档日志使用gzip进行压缩 compress # 与compress共用,最近的一次归档不要压缩 delaycompress # 忽略错误信息 missingok # 日志文件为空,轮循不会继续执行 notifempty # 当日志文件大于指定大小时,才继续执行,单位为bytes(默认)/k/M/G size = 100M # 将日志文件转储后执行的命令,以endscript结尾,命令需要单独成行 postrotate # 重启nginx日志服务,写入到新的文件中去,否则会依然写入重命名后的文件中 /bin/kill -USR1 cat /home/work/run/nginx.pid 2&gt; /dev/null 2> /dev/null || true endscript}在使用前,我们先演练一下,也就是debug模式,此时,不用实际轮循,而是模拟并输出,使用强制执行是因为还没到轮循周期:$logrotate -d -f /etc/logrotate.d/nginxAccessLog reading config file /etc/logrotate.d/nginxAccessLogreading config info for /home/work/log/nginx.access.logHandling 1 logsrotating pattern: /home/work/log/nginx.access.log forced from command line (7 rotations)empty log files are rotated, old logs are removedconsidering log /home/work/log/nginx.access.log log needs rotatingrotating log /home/work/log/nginx.access.log, log->rotateCount is 7dateext suffix ‘-20190228’glob pattern ‘-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]‘glob finding old rotated logs failedrenaming /home/work/log/nginx.access.log to /home/work/log/nginx.access.log-20190228creating new /home/work/log/nginx.access.log mode = 0664 uid = 500 gid = 501running postrotate scriptrunning script with arg /home/work/log/nginx.access.log: " /bin/kill -USR1 cat /home/work/run/nginx.pid 2&gt; /dev/null 2> /dev/null || true"可以看到整个执行流程,以及没有什么报错信息,此时我们直接继续执行:$logrotate -f /etc/logrotate.d/nginxAccessLog$ll /home/work/lnmp/log/ -rw-r–r– 1 work work 0 Feb 28 13:40 nginx.access.log-rw-r–r– 1 work work 5379846 Feb 28 13:37 nginx.access.log-20190228可以看到我们已经对日志进行了切分,最新的日志文件大小为0。以后系统就会对该日志进行自动的轮转管理。参考资料Linux日志文件总管——logrotate:https://linux.cn/article-4126…logrotate-(8) manual page:https://linuxconfig.org/logro…运维中的日志切割操作梳理:https://www.cnblogs.com/kevin… ...

February 28, 2019 · 1 min · jiezi

使用阿里云发布分布式网站,开发时候应该注意什么?

虽然之前写过关于负载均衡的文章,但是似乎大家都对负载均衡这个标题很陌生。今天就换个角度,从分布式网站发布角度说一下首先,网站发布一定离不开服务器,就是阿里云的云服务器ECS。最近发现,老用户也有机会购买特价服务器,参考阿里云天猫旗舰店。分布式网站架构,在开发环节需要处理号几个地方,才能更轻松的扩展:1,数据库单独运行这个最简单,相信只要用到了数据库的网站,大部分都是已经可以轻松实现网站和数据库分离的模式。之所以强调这点,就是因为,网站真的到了访问量大了,需要由单机发布改为分布式发布时候。首先就是要把数据库单独出来,使用单独一台ECS或者使用专门的RDS运行。关于使用ECS自行搭建数据库,要说的无非就是,数据库文件一定要放在SSD硬盘上。因为云硬盘本质都是虚拟硬盘,读写指标iops会损失的很严重。当然业务量大了之后,我更建议选购RDS,RDS阿里云专门的数据库服务产品,mysql,sqlserver等多款分支,说实话,用惯了ECS自建可能会感觉这个产品挺贵的。但是实际上RDS已经针对硬盘读写,高可用,轻松拓展读写分离等提供了巨大的便利。因此大部分分布式网站都是选用了RDS等产品。2,session存储大部分网站都是有登录需求或者其他session存储需求的。其实真正服务某一个用户的服务器只有一个,然而分布式网站是在负载均衡的规则下转发到后台服务器,如果负载均衡没有开启回话保持功能,那么很有可能用户登录后,其他访问很有可能到达不同的后端服务器处理,而实现不同服务器同样能支持服务的办法就是,session得共享起来。这样就做到了让用户不需要反复登录。session共享有多重方法,一种是什么都不用做,session存储在收到请求的那台ECS本地,这种情况就需要负载均衡开启“会话保持”功能。这么做可以实现大部分分布式发布需求,但是如果有使用session统计在线量的时候就会发现只统计到某台ECS登录的用户量。还有一种session共享的办法是使用单独的服务存储session,例如专门拿出来一台ECS做session服务。这里也有人跟倾向于单独用一个redis服务共享存储session,也是非常好的办法。3,用户上传的文件很多网站存在用户上传的情况,但是分布式发布后,显然用户上传的时候传入了服务器A,等以后下载的时候使用的是服务器B,这时候如果没做共享,显然用户下载的文件会报错404,因为服务器B路径下显然没有用户传到A服务器里的文件。解决办法很简单,就是讲所有的上传文件,传入到服务器之后,都转存到“共享文件服务器”,这个共享文件服务器,可以考虑自己搭建,但我建议的是使用阿里云提供的“对象存储OSS”服务。这样用户所有的下载,都走的时候OSS的访问地址,就不会报错404了。而且OSS是个独立的服务,下载文件使用的是OSS的流量,不占用发布网站的ECS的带宽。这就可以做到1M服务器带宽撑起峰值几十人甚至上百人的并发的原因。如果文件较多,另外建议将OSS搭配CDN使用4,网站的日志文件很多网站开发时候都是留有日志功能的,这些日志,就不再适合继续使用网站目录下某个路径存储了。原因跟上一条一样:文件存储在单台服务器里,要拿到全部日志文件需要每台服务器去获取一遍,显然不合适!解决办法跟上一条很相似:使用共享文件服务器存储日志。这里需要注意避免日志文件重名,常见的年月日明明,在这里就会冲突,因为多台服务器都会产生相同文件名称的日志。但是我在这里,更建议大家把需要收集的日志文件,直接放入数据库RDS中。这样获取日志就不用每台服务器去汇总了。直接从数据库查出来就好。5,发布网站文件一致发布文件一致,就是为了保证更好的扩展。将来服务器压力太大,只需要直接从现有服务器环境拷贝一份即可。甚至搭配阿里云的“弹性伸缩ESS服务”。可以根据设置好的伸缩规则,自动按照某个服务器模板增加或者减少WEB服务器数量,实现弹性扩容。综上所述,一个功能较全的分布式网站发布在阿里云,公共需要这么几个服务:ECS多台,SLB一台,RDS一台,Redis或有,ESS或有。通过ECS+SLB+RDS+Redis+ESS的搭配,就可以轻松应对更多的访问量了。附录:阿里云相关产品的帮助文档:《云服务器ECS帮助文档》《负载均衡SLB帮助文档》《对象存储OSS帮助文档》《弹性伸缩ESS帮助文档》《CDN帮助文档》原文地址: https://www.opengps.cn/Blog/V… 文章的更新编辑依此链接为准。欢迎关注源站原创文章!

February 28, 2019 · 1 min · jiezi

使用 nginx 同域名下部署多个 vue 项目,并使用反向代理

花了 3 天时间,趁着我还没有忘记,先记录下来效果目前有 2 个项目(project1, project2),还有一个 nginx 自带的 index.html,我添加了对应的链接代码(稍后粘贴出来),为了统一管理子项目的路由。我期望实现下面的效果(假设 ip: localhost,port: 8080):http://localhost:8080/ 进入最外层的 index.htmlhttp://localhost:8080/project1 进入项目一http://localhost:8080/project2 进入项目二废话不多说,开始配置Vue 的配置本人使用的是 vue-cli2 搭建的项目,所以对应的需要修改一些 vue 的配置参数。config 文件夹下的 index.js,因为是打包,所以我们需要在 build.assetsPublicPath 更改对应项目名,譬如// project1module.exports = { dev: {}, build: { assetsPublicPath: ‘/project1/’ // 注意前后的 ‘/’ }}// project2module.exports = { dev: {}, build: { assetsPublicPath: ‘/project2/’ // 注意前后的 ‘/’ }}config 文件夹下的 prod.env.js 修改成这样:// project1module.exports = { NODE_ENV: ‘“production”’, BASE_API: ‘"/api/pro1"’ // 这里待会与 nginx 配置对应}// project2module.exports = { NODE_ENV: ‘“production”’, BASE_API: ‘"/api/pro2"’ // 这里待会与 nginx 配置对应}[注意] 因为我在项目中使用到了 BASE_API 作为代理的前缀,如果你的不在这边,你需要找到你自己的代理配置因为每个人的 vue-router 文件配置不一样,你需要找到你的 router 配置文件,内部修改为:// 我采用了 history 模式,hash 模式我没有测试,感觉应该是一样的效果// project1export default new Router({ base: ‘/project1/’, // 注意更改你子项目名,这个对应你的 build.assetsPublicPath mode: ‘history’, scrollBehavior: () => ({ y: 0 }), routes: []})// project2export default new Router({ base: ‘/project2/’, // 注意更改你子项目名,这个对应你的 build.assetsPublicPath mode: ‘history’, scrollBehavior: () => ({ y: 0 }), routes: []})[注意] 在 npm run build 可能会报错:.tap(*) 之类的,那是因为打包中的 html-webpack-plugin 版本出现了问题,可以执行下面的语句# 这个版本就是你的 package.json 中的版本,只不过你需要重新再指定这个版本$ npm i html-webpack-plugin@4.0.0-alpha -DNginx 的配置首先我的目录是这样的,无关文件全部以 … 展示.├─conf│ ├─… # 其他文件│ └─nginx.conf│├─html # 只看这里,其他暂时我没用到 │ ├─project1│ │ └─static│ │ ├─css│ │ ├─fonts│ │ └─js│ │ ├─g│ │ └─V│ ├─project2│ │ └─static│ │ ├─css│ │ ├─fonts│ │ └─js│ │ ├─g│ │ └─V│ ├─index.html│ └─50x.html└─… # 其他文件[解释] 我的 nginx 目录就是原生的,内部包含了一个 html 文件夹,为了省事,我直接使用这个,当然你也可以指定其他的目录,但是目前还请和我一样的配置,后面可以自己定制化。现在我们开始配置在 conf 文件夹下的 nginx.conf 文件我是直接在原始文件上修改的,而修改的配置都是在 http 模块中,所以其他的不需要的代码我直接用 … 代替。# …# 反向代理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”’; sendfile on; keepalive_timeout 65; client_max_body_size 20M; client_body_buffer_size 10M; large_client_header_buffers 4 128k; # 这里可以做集群 upstream p1_server { server localhost:8081; } # 这里可以做集群 upstream p2_server { server localhost:8082; } server { listen 8080; server_name localhost; charset utf-8; proxy_connect_timeout 180; proxy_send_timeout 180; proxy_read_timeout 180; proxy_set_header Host $host; proxy_set_header X-Forwarder-For $remote_addr; root html; # 这里指定刚刚我们的文件夹 # 总的项目路由,我偷懒直接写在了同一个文件 # 如果有很多可以在配置多个 conf 文件,使用 include 关联进来 location / { try_files $uri $uri/ /index.html; # 这里可以理解指定到 html 文件夹下的 index.html } # project1 # 这里就是刚刚我们在 vue 项目中 config/index.js 的配置 build.assetsPublicPath, # 也是 vue 项目中配置的 router 中的 base location ^~ /project1 { try_files $uri $uri/ /project1/index.html; # 这里可以理解指定到 html 文件夹下 project1 文件夹 的 index.html } # project2 # 这里是项目二的配置 location ^~ /project2 { # try_files $uri $uri/ /project2/index.html; # 这里可以理解指定到 html 文件夹下 project2 文件夹 的 index.html } # 这里是 project1 配置需要调用的接口 location /api/pro1 { # 这里就是在 vue 项目中 prod.env.js 的配置 BASE_API proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://p1_server; # 此处的 p1_server 对应的上面的配置 upstream p1_server {},这里可以做集群,我用不到,就简单配置了 } # 这里是 project1 配置需要调用的接口 location /api/pro2 { # 这里就是在 vue 项目中 prod.env.js 的配置 BASE_API proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://p2_server; # 此处的 p2_server 对应的上面的配置 upstream p2_server {},这里可以做集群,我用不到,就简单配置了 } # … } # …}最后贴出我修改的 index.html 的代码因为我是追加的,所以直接贴出我追加的代码,其他的采用 ……<p><em>Thank you for using nginx.</em></p> <!– 为了展示位置 –><!– start: 追加–><hr><a href="/project1">项目一</a> | <a href="/project2">项目二</a><!– end: 追加–></body> <!– 为了展示位置 –>最后的调试所有的配置完成,我们就可以启动 nginx 了,这个不会的请自行解决了。启动成功,我们在浏览器输入 http://localhost:8080 我们就可以看到,点击项目一,我们可以看到链接变为 http://localhost:8080/project1点击项目二,链接变为 http://localhost:8080/project2,完全符合我们的期望,那就成功了。[强行解释一下玄学] 那天配置好了,一启动就报错,弄的我最后放弃了。但是第二天,准备在检查下,一启动竟然全好了,我都一脸懵逼啊! 如果你也遇到和我一样的问题,先放放,说不定隔天就好了。 ...

February 27, 2019 · 3 min · jiezi

正向代理与反向代理

什么是代理服务器(Proxy Serve)? 提供代理服务的电脑系统或其它类型的网络终端,代替网络用户去取得网络信息。为什么使用代理服务器?提高访问速度。由于目标主机返回的数据会存放在代理服务器的硬盘中,因此下一次客户再访问相同的站点数据时,会直接从代理服务器的硬盘中读取,起到了缓存的作用,尤其对于热门网站能明显提高访问速度。防火墙作用 由于所有的客户机请求都必须通过代理服务器访问远程站点,因此可以在代理服务器上设限,过滤掉某些不安全信息。同时正向代理中上网者可以隐藏自己的IP,免受攻击。突破访问限制 互联网上有许多开发的代理服务器,客户机在访问受限时,可通过不受限的代理服务器访问目标站点,通俗说,我们使用的翻墙浏览器就是利用了代理服务器,可以直接访问外网。正向代理 正向代理(forward proxy) ,一个位于客户端和原始服务器之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并制定目标(原始服务器),然后代理向原始服务器转发请求并将获得的内容返回给客户端,客户端才能使用正向代理。我们平时说的代理就是指正向代理。 简单一点:A向C借钱,由于一些情况不能直接向C借钱,于是A想了一个办法,他让B去向C借钱,这样B就代替A向C借钱,A就得到了C的钱,C并不知道A的存在,B就充当了A的代理人的角色。反向代理 反向代理(Reverse Proxy),以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求的客户端,此时代理服务器对外表现为一个反向代理服务器。 理解起来有些抽象,可以这么说:A向B借钱,B没有拿自己的钱,而是悄悄地向C借钱,拿到钱之后再交给A,A以为是B的钱,他并不知道C的存在。正向代理和反向代理的区别位置不同: 正向代理,架设在客户机和目标主机之间; 反向代理,架设在服务器端; 代理对象不同: 正向代理,代理客户端,服务端不知道实际发起请求的客户端; 反向代理,代理服务端,客户端不知道实际提供服务的服务端; 备注:正向代理–HTTP代理为多个人提供翻墙服务;反向代理–百度外卖为多个商户提供平台给某个用户提供外卖服务。 用途不同 正向代理,为在防火墙内的局域网客户端提供访问Internet的途径; 反向代理,将防火墙后面的服务器提供给Internet访问; 安全性不同 正向代理允许客户端通过它访问任意网站并且隐藏客户端自身,因此必须采取安全措施以确保仅为授权的客户端提供服务; 反向代理都对外都是透明的,访问者并不知道自己访问的是哪一个代理。正向代理的应用 1. 访问原来无法访问的资源 2. 用作缓存,加速访问速度 3. 对客户端访问授权,上网进行认证 4. 代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息反向代理的应用 1. 保护内网安全 2. 负载均衡 3. 缓存,减少服务器的压力 Nginx作为最近较火的反向代理服务器,安装在目的主机端,主要用于转发客户机请求,后台有多个http服务器提供服务,nginx的功能就是把请求转发给后台的服务器,决定哪台目标主机来处理当前请求。总结 正向代理是从客户端的角度出发,服务于特定用户(比如说一个局域网内的客户)以访问非特定的服务;反向代理正好与此相反,从服务端的角度出发,服务于非特定用户(通常是所有用户),已访问特定的服务。

February 27, 2019 · 1 min · jiezi

CentOS 7 解决丢失 nginx.pid

导语上一篇文章中,已经将 Nginx 编译安装完成。重启服务器之后,再次开启 Nginx 服务的时候出错了,错误信息如下解决错误出现错误就要解决错误。从上图中可以看出,错误原因是缺少 nginx.pid 这个文件,这个文件中的内容只有一行,记录的是相应进程的 pid,即进程号。解决的方法是输入 ./nginx -c /usr/local/nginx/conf/nginx.conf 重新设置配置文件

February 25, 2019 · 1 min · jiezi

尝试 Docker + Nginx 部署单页应用

开发到部署,亲力亲为当我们开发一个单页面应用时,执行完构建后npm run build会生成一个 index.html 在 dist 目录,那怎么把这个 index.html 部署到服务器上呢?目录结构dist/:前端构建完的静态文件docker/:镜像所需的配置文件配置 Nginx挑几点配置讲讲,先是 Gzip 压缩资源,以节省带宽和提高浏览器加载速度虽然 Webpack 已经支持在构建时就生成 .gz 压缩包,但也可以通过 Nginx 来启用gzip on;gzip_disable “msie6”;# 0-9 等级,级别越高,压缩包越小,但对服务器性能要求也高gzip_comp_level 9;gzip_min_length 100;# Gzip 不支持压缩图片,我们只需要压缩前端资源gzip_types text/css application/javascript;再就是服务端口的配置,将 API 反向代理到后端服务server { listen 8080; server_name www.frontend.com; root /usr/share/nginx/html/; location / { index index.html index.htm; try_files $uri $uri/ /index.html; # 禁止缓存 HTML,以保证引用最新的 CSS 和 JS 资源 expires -1; } location /api/v1 { proxy_pass http://backend.com; }}完整配置长这样worker_processes 1;events { worker_connections 1024; }http { ## # Basic Settings ## sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /etc/nginx/mime.types; default_type application/octet-stream; ## # Logging Settings ## access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; ## # Gzip Settings ## gzip on; gzip_disable “msie6”; gzip_comp_level 9; gzip_min_length 100; gzip_types text/css application/javascript; server { listen 8080; server_name www.frontend.com; root /usr/share/nginx/html/; location / { index index.html index.htm; try_files $uri $uri/ /index.html; expires -1; } location /api/v1 { proxy_pass http://backend.com; } }}配置 Docker这里简单一点,基于基础镜像,拷贝我们写好的 nginx.conf 和 index.html 到镜像内FROM nginx:alpineCOPY nginx.conf /etc/nginx/nginx.confCOPY dist /usr/share/nginx/html编写 Makefile完成了上面的准备,就可以编写命令来执行镜像的打包了先给镜像取个名称和端口号APP_NAME = spa_nginx_dockerPORT = 8080通过 build 来打包镜像build: cp docker/Dockerfile . cp docker/nginx.conf . docker build -t $(APP_NAME) . rm Dockerfile rm nginx.conf通过 deploy 来启动镜像deploy: docker run -d -it -p=$(PORT):$(PORT) –name="$(APP_NAME)" $(APP_NAME)最后还有个 stop 来停止和清理镜像stop: docker stop $(APP_NAME) docker rm $(APP_NAME) docker rmi $(APP_NAME)完整配置长这样APP_NAME = spa_nginx_dockerPORT = 8080build: cp docker/Dockerfile . cp docker/nginx.conf . docker build -t $(APP_NAME) . rm Dockerfile rm nginx.confdeploy: docker run -d -it -p=$(PORT):$(PORT) –name="$(APP_NAME)" $(APP_NAME)stop: docker stop $(APP_NAME) docker rm $(APP_NAME) docker rmi $(APP_NAME)完整命令长这样# 静态资源构建npm run build# 镜像打包make build# 停止并删除旧镜像(首次可忽略)make stop# 镜像启动make deploy总结目前的部署方法相对简单,后续会加入基础镜像和镜像仓库的使用,先去前面探探路 ...

February 25, 2019 · 2 min · jiezi

Nginx 安装与配置规则入门

Nginx 安装与配置规则入门nginx 安装与运行 (Mac OS环境)nginx 规则配置入门一些命令行的配置一、nginx 安装与运行 (Mac OS环境)1. 安装 nginx可通过 Homebrew 可直接安装:$brew install nginx安装好后,默认首页的文件在 /usr/local/var/www 文件夹下 默认的配置文件地址在 /usr/local/etc/nginx/nginx.confnginx 默认用的 8080 端口,如果发现端口被占用了(通过 $lsof -i:8080查看端口占用情况),可以杀掉使用该端口的进程($kill 进程PID)。或者修改 nginx 的默认端口(/usr/local/etc/nginx/nginx.conf )2. 启动 nginx$brew services start nginx或者进入到目录 /usr/local/bin 下$./nginx启动成功后,浏览器访问http://localhost:8080/,就可以看到 nginx 服务器返回的静态资源了(默认是资源/usr/local/var/www/index.html)3. 停止 nginx$nginx -s stop4. 重启 nginx$nginx -s reload5. 查看 nginx 配置路径信息$brew info nginx二、nginx 规则配置更多配置可查看https://www.nginx.com/resourc…http://nginx.org/en/docs/http://www.nginx.cn/doc/1. locationlocation 语法文章2. root 与 aliasnginx 中可通过 root 和 alias 指定资源的访问路径。1)root:location / { root /usr/local/var/www/; index index.html index.htm;}上面这个规则:请求 http://localhost:8080/index.html 这个地址时,访问的资源是: /usr/local/var/www/index.html.请求 http://localhost:8080/test/a.png 这个地址时,访问的资源是: /usr/local/var/www/test/a.png.也就是说,访问的资源地址其实是 root 指定的路径 + location 匹配到的路径。2)alias:alias 即别名,与 root 的匹配规则稍有不同。location /a/ { alias /usr/local/var/www/b/;}上面这个规则:请求 http://localhost:8080/a/ 这个地址时,访问的资源是: /usr/local/var/www/b/index.html.请求 http://localhost:8080/a/1.gif 这个地址时,访问的资源是: /usr/local/var/www/b/1.gif.也就是说,访问的资源地址就是 alias 指定的路径,与 location 匹配到的路径无关(会把 location 匹配到的路径丢掉)。3)root 与 alias 的区别:alias 只能作用在 location 中,而 root 可以存在 server、http 和 location 中。alias 后面必须要用 “/” 结束,否则会找不到文件,而 root 则对 “/” 可有可无。3. try_filelocation /test/ { try_files $uri $uri/ /a/1.png;}try_files 去尝试到网站目录读取用户访问的文件,如果第一个变量存在,就直接返回;不存在则继续读取第二个变量,如果存在,直接返回;不存在则跳转到第三个参数上。$uri 是 nginx 的一个变量,存放着用户访问的地址。比如访问http://www.xxx.com/index.html,$uri就是 /index.html.$uri/ 代表访问的是一个目录,比如:http://www.xxx.com/hello/test/ ,那么$uri/ 就是 /hello/test/.例如上面这条规则:请求 http://localhost:8080/test/2.png 这个地址时,try_files 会判断他是文件,还是一个目录,结果发现他是文件,与第一个参数 $uri 变量匹配。然后去到网站目录下去查找 test/2.png 文件是否存在,如果存在直接读取返回。如果不存在则跳转到第三个参数,即返回网站根目录 + /a/1.png 文件(/usr/local/var/www/a/1.png)。更多用法:https://www.hi-linux.com/post…4. rewriterewrite 语法rewrite 功能就是实现 url 重写以及重定向。语法rewrite regex replacement [flag];rewrite只能放在server{},location{},if{}中,并且只能对域名后边的除去传递的参数外的字符串起作用,例如 http://www.xxx.com/a/b/index.html?param=1&u=str 只对 /a/b/index.html 重写。rewrite 的执行顺序:执行server块的rewrite指令执行location匹配执行选定的location中的rewrite指令flag 标志位:last : 相当于Apache的[L]标记,表示完成rewritebreak : 停止执行当前虚拟主机的后续 rewrite 指令集redirect : 返回302临时重定向,地址栏会显示跳转后的地址permanent : 返回301永久重定向,地址栏会显示跳转后的地址location /home/ { rewrite ^/home/test/ http://www.baidu.com;}上面这个规则:访问 http://localhost:8080/home/test/ 这个地址时,页面会重定向到 http://www.baidu.com。一些小tips:如何 nginx 重定向 url,但不改变浏览器中 url 的显示?proxy_pass 可指定反向代理更多用法:https://my.oschina.net/foreve…三、一些命令行的配置(mac OS)1. 如何在命令行用 vscode 打开文件cd /usr/local/bin/ln -s “/Applications/Visual Studio Code.app/Contents/MacOS/Electron” vscode其中 /Applications/Visual Studio Code.app/Contents/MacOS/Electron 为 vscode 的可执行文件,ln -s 命令就是将其通过软连接的方式放到 /usr/local/bin/ 目录下。这样就可以在命令行的其他地方通过 vscode 命令打开文件了。更多博客:https://github.com/Lmagic16/blog ...

February 24, 2019 · 1 min · jiezi

Nginx 通过 Lua + Redis 实现动态封禁 IP

背景为了封禁某些爬虫或者恶意用户对服务器的请求,我们需要建立一个动态的 IP 黑名单。对于黑名单之内的 IP ,拒绝提供服务。架构实现 IP 黑名单的功能有很多途径:1、在操作系统层面,配置 iptables,拒绝指定 IP 的网络请求;2、在 Web Server 层面,通过 Nginx 自身的 deny 选项 或者 lua 插件 配置 IP 黑名单;3、在应用层面,在请求服务之前检查一遍客户端 IP 是否在黑名单。为了方便管理和共享,我们选择通过 Nginx+Lua+Redis 的架构实现 IP 黑名单的功能,架构图如下:实现1、安装 Nginx+Lua模块,推荐使用 OpenResty,这是一个集成了各种 Lua 模块的 Nginx 服务器:2、安装并启动 Redis 服务器;3、配置 Nginx 示例:Nginx 配置其中lua_shared_dict ip_blacklist 1m;由 Nginx 进程分配一块 1M 大小的共享内存空间,用来缓存 IP 黑名单。access_by_lua_file lua/ip_blacklist.lua;指定 lua 脚本位置。4、配置 lua 脚本,定期从 Redis 获取最新的 IP 黑名单。5、在 Redis 服务器上新建 Set 类型的数据 ip_blacklist,并加入最新的 IP 黑名单。完成以上步骤后,重新加载 nginx,配置便开始生效了。这时访问服务器,如果你的 IP 地址在黑名单内的话,将出现拒绝访问,如下图:总结以上,便是 Nginx+Lua+Redis 实现的 IP 黑名单功能,具有如下优点:1、配置简单、轻量,几乎对服务器性能不产生影响;2、多台服务器可以通过Redis实例共享黑名单;3、动态配置,可以手工或者通过某种自动化的方式设置 Redis 中的黑名单。 ...

February 24, 2019 · 1 min · jiezi

nginx+php执行请求的工作原理

php工作原理首先先了解下常听说的cgi,php-cgi,fastcgi,php-fpm到底是什么关系,帮助了解php的工作原理cgi协议cgi协议用来确定webserver(例如nginx),也就是内容分发服务器传递过来什么数据,什么样格式的数据php-cgi进程解释器php-cgi是php的cgi协议进程解释器,每次启动时,需要经历加载php.ini文件->初始化执行环境->处理请求->返回内容给webserver->php-cgi进程退出的流程fastcgi协议fastcgi协议是对cgi协议效率提升的补充,主要是针对每次请求过来时都需要启动一个cgi解释器进程的优化,不再需要cgi解释器进程每次收到webserver请求后都需要重新加载php.ini文件和初始化执行环境php-fpm进程管理器php-fpm是对fastcgi协议的实现,是进程管理器,启动时包括master和worker进程俩部分,master进程监听端口,接收来自webserver请求,worker进程一般具有多个,每个worker进程都有一个cgi进程解释器,用来执行php代码php启动和工作原理启动phpfpm时,会启动master进程,加载php.ini文件,初始化执行环境,并启动多个worker进程。每次请求来时会将请求传递给worker进程进行处理php平滑重启原理每次修改完php.ini配置并重启后,会启动新的worker进程加载新的配置,而之前已经存在的进程会在工作完成之后销毁,因此实现平滑重启nginx工作原理如果想弄明白nginx和php配合的原理,还需要先了解nginx的配置文件中的server部分server { listen 80; #监听80端口,接收http请求 server_name www.example.com; #一般存放网址,表示配置的哪个项目 root /home/wwwroot/zensmall/public/; # 存放代码的根目录地址或代码启动入口 index index.php index.html; #网站默认首页 #当请求网站的url进行location的前缀匹配且最长匹配字符串是该配置项时,按顺序检查文件是否存在,并返回第一个找到的文件 location / { #try_files,按顺序检查文件是否存在,返回第一个找到的文件 #$uri代表不带请求参数的当前地址 #$query_string代表请求携带的参数 try_files $uri $uri/ /index.php?$query_string; #按顺序检查$uri文件,$uri地址是否存在,如果存在,返回第一个找到的文件;如果都不存在,发起访问/index.php?$query_string的内部请求,该请求会重新匹配到下面的location请求 } #当请求网站的php文件的时候,反向代理到php-fpm去处理 location ~ .php$ { include fastcgi_params; #引入fastcgi的配置文件 fastcgi_pass 127.0.0.1:9000; #设置php fastcgi进程监听的IP地址和端口 fastcgi_index index.php; #设置首页文件 fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #设置脚本文件请求的路径 }}上面server配置的整体含义是:每次nginx监听到80端口的url请求,会对url进行location匹配。如果匹配到/规则时,会进行内部请求重定向,发起/index.php?$query_string的内部请求,而对应的location配置规则会将请求发送给监听9000端口的php-fpm的master进程总结下面总结下最简单的用户请求流程:用户访问域名->域名进行DNS解析->请求到对应IP服务器和端口->nginx监听到对应端口的请求->nginx对url进行location匹配->执行匹配location下的规则->nginx转发请求给php->php-fpm的master进程监听到nginx请求->master进程将请求分配给其中一个闲置的worker进程->worker进程执行请求->worker进程返回执行结果给nginx->nginx返回结果给用户

February 24, 2019 · 1 min · jiezi

深入 Nginx 之架构篇

前言最近在读 Nginx 相关的书籍,做一下读书笔记。Nginx 作为业界知名的高性能服务器,被广泛的应用。它的高性能正是由于其优秀的架构设计,其架构主要包括这几点:模块化设计、事件驱动架构、请求的多阶段异步处理、管理进程与多工作进程设计、内存池的设计,以下内容依次进行说明。模块化设计高度模块化的设计是 Nginx 的架构基础。在 Nginx 中,除了少量的核心代码,其他一切皆为模块。所有模块间是分层次、分类别的,Nginx 官方共有五大类型的模块:核心模块、配置模块、事件模块、HTTP 模块、mail 模块。它们之间的关系如下:在这 5 种模块中,配置模块和核心模块是与 Nginx 框架密切相关的。而事件模块则是 HTTP 模块和 mail 模块的基础。HTTP 模块和 mail 模块的“地位”类似,它们都是更关注于应用层面。事件驱动架构事件驱动架构,简单的说就是由一些事件发生源来产生事件,由事件收集器来收集、分发事件,然后由事件处理器来处理这些事件(事件处理器需要先在事件收集器里注册自己想处理的事件)。对于 Nginx 服务器而言,一般由网卡、磁盘产生事件,Nginx 中的事件模块将负责事件的收集、分发操作;而所有的模块都可能是事件消费者,它们首先需要向事件模块注册感兴趣的事件类型,这样,在有事件产生时,事件模块会把事件分发到相应的模块中进行处理。对于传统 web 服务器(如 Apache)而言,采用的所谓事件驱动往往局限在 TCP 连接建立、关闭事件上,一个连接建立以后,在其关闭之前的所有操作都不再是事件驱动,这时会退化成按顺序执行每个操作的批处理模式,这样每个请求在连接建立后都将始终占用着系统资源,直到关闭才会释放资源。这种请求占用着服务器资源等待处理的模式会造成服务器资源极大的浪费。如下图所示,传统 web 服务器往往把一个进程或线程作为时间消费者,当一个请求产生的事件被该进程处理时,直到这个请求处理结束时,进程资源都将被这一请求所占用。比较典型的例子如 Apache 同步阻塞的多进程模式就是这样的。传统 web 服务器处理事件的简单模型(矩形代表进程):Nginx 采用事件驱动架构处理业务的方式与传统的 web 服务器是不同的。它不使用进程或者线程来作为事件消费者,所谓的事件消费者只能是某个模块。只有事件收集、分发器才有资格占用进程资源,它们会在分发某个事件时调用事件消费模块使用当前占用的进程资源,如下图所示,该图中列出了 5 个不同的事件,在事件收集、分发者进程的一次处理过程中,这 5 个事件按照顺序被收集后,将开始使用当前进程分发事件,从而调用相应的事件消费者来处理事件。当然,这种分发、调用也是有序的。Nginx 处理事件的简单模型:由上图可以看出,处理请求事件时,Nginx 的事件消费者只是被事件分发者进程短期调用而已,这种设计使得网络性能、用户感知的请求时延都得到了提升,每个用户的请求所产生的事件会及时响应,整个服务器的网络吞吐量都会由于事件的及时响应而增大。当然,这也带来一定的要求,即每个事件消费者都不能有阻塞行为,否则将会由于长时间占用事件分发者进程而导致其他事件得不到及时响应,Nginx 的非阻塞特性就是由于它的模块都是满足这个要求的。请求的多阶段异步处理多阶段异步处理请求与事件驱动架构是密切相关的,也就是说,请求的多阶段异步处理只能基于事件驱动架构实现。多阶段异步处理就是把一个请求的处理过程按照事件的触发方式划分为多个阶段,每个阶段都可以由事件收集、分发器来触发。处理获取静态文件的 HTTP 请求时切分的阶段及各阶段的触发事件如下所示:这个例子中,该请求大致分为 7 个阶段,这些阶段是可以重复发生的,因此,一个下载静态资源请求可能会由于请求数据过大,网速不稳定等因素而被分解为成百上千个上图所列出的阶段。异步处理和多阶段是相辅相成的,只有把请求分为多个阶段,才有所谓的异步处理。当一个时间被分发到事件消费者中进行处理时,事件消费者处理完这个事件只相当于处理完 1 个请求的阶段。什么时候可以处理下一个阶段呢?这只能等待内核的通知,即当下一次事件出现时,epoll 等事件分发器将会获取到通知,然后去调用事件消费者进行处理。管理进程、多工作进程设计Nginx 在启动后,会有一个 master 进程和多个 worker 进程。master 进程主要用来管理worker 进程,包括接收来自外界的信号,向各 worker 进程发送信号,监控 worker 进程的运行状态以及启动 worker 进程。 worker 进程是用来处理来自客户端的请求事件。多个 worker 进程之间是对等的,它们同等竞争来自客户端的请求,各进程互相独立,一个请求只能在一个 worker 进程中处理。worker 进程的个数是可以设置的,一般会设置与机器 CPU 核数一致,这里面的原因与事件处理模型有关。Nginx 的进程模型,可由下图来表示:在服务器上查看 Nginx 进程:这种设计带来以下优点:1) 利用多核系统的并发处理能力现代操作系统已经支持多核 CPU 架构,这使得多个进程可以分别占用不同的 CPU 核心来工作。Nginx 中所有的 worker 工作进程都是完全平等的。这提高了网络性能、降低了请求的时延。2) 负载均衡多个 worker 工作进程通过进程间通信来实现负载均衡,即一个请求到来时更容易被分配到负载较轻的 worker 工作进程中处理。这也在一定程度上提高了网络性能、降低了请求的时延。3) 管理进程会负责监控工作进程的状态,并负责管理其行为管理进程不会占用多少系统资源,它只是用来启动、停止、监控或使用其他行为来控制工作进程。首先,这提高了系统的可靠性,当 worker 进程出现问题时,管理进程可以启动新的工作进程来避免系统性能的下降。其次,管理进程支持 Nginx 服务运行中的程序升级、配置项修改等操作,这种设计使得动态可扩展性、动态定制性较容易实现。内存池的设计为了避免出现内存碎片,减少向操作系统申请内存的次数、降低各个模块的开发复杂度,Nginx 设计了简单的内存池,它的作用主要是把多次向系统申请内存的操作整合成一次,这大大减少了 CPU 资源的消耗,同时减少了内存碎片。因此,通常每一个请求都有一个简易的独立内存池(如每个 TCP 连接都分配了一个内存池),而在请求结束时则会销毁整个内存池,把曾经分配的内存一次性归还给操作系统。这种设计大大提高了模块开发的简单些,因为在模块申请内存后不用关心它的释放问题;而且因为分配内存次数的减少使得请求执行的时延得到了降低。同时,通过减少内存碎片,提高了内存的有效利用率和系统可处理的并发连接数,从而增强了网络性能。 ...

February 23, 2019 · 1 min · jiezi

tcpdump查看Nginx长连接还是短连接

tcpdump用法-i eth0 表示网卡-A 表示转为ascii码-n 表示不要转域名,用ip就好host 后面加IP地址查看nginx是长连接还是短连接:[root@nginx01 ~]# tcpdump -i eth0 -A host 192.168.156.44 -n | grep HTTP tcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes.oJ&…ePOST /v2/story/comic/xxx/top HTTP/1.1…}.oJ&HTTP/1.1 200 OK.oN…..POST /v1/story/xxx HTTP/1.1…..oN.HTTP/1.1 200 OK.oN….:POST /v1/story/xxx HTTP/1.1…[.oN.HTTP/1.1 200 OK.oR….BPOST /v2/story/comic/xxx/top HTTP/1.1.oS….MPOST /v1/story/xxx HTTP/1.1…`.oR.HTTP/1.1 200 OK结果:HTTP/1.1 实锤

February 23, 2019 · 1 min · jiezi

PHP_SELF变量解析和重复路径解决

最近升级PHP到PHP7版本,并重新部署了新的Nginx,启动的时候发现了一个问题,全局变量$_SERVER[‘PHP_SELF’]的值发生了改变,从而影响到代码的功能。因此我们来了解下$_SERVER全局变量中的PHP_SELF/PATH_INFO/SCRIPT_NAME等参数以及其关系。CGI 1.1规范之前的文章 [ php-fpm进程数管理 ] 已经简单说过CGI的内容,这里我们再详细讲一下。CGI是Common Gateway Interface(通用网管协议),用于让交互程序和Web服务器通信的协议。它负责处理URL的请求,启动一个进程,将客户端发送的数据作为输入,由Web服务器收集程序的输出并加上合适的头部,再发送回客户端。FastCGI是基于CGI的增强版本的协议,不同于创建新的进程来服务请求,使用持续的进程和创建的子进程来处理一连串的进程,这些进程由FastCGI服务器管理,开销更小,效率更高。CGI诞生于1993年美国国家计算机中心,目的是为不同的动态页面处理语言(php/python/java)在不同的服务器下(apache/nginx)提供一致的接口规范,提供会话环境变量、会话客户端等信息。在RFC-CGI1.1文档中包含了协议的全部内容,我们现在只关注它的 4.1节:Request Meta-Variables 。标准中定义了处理请求应该实现的17个属性和如何自定义新属性,比如:SERVER_PROTOCOL :信息协议的名字和修订版。格式为protocol/reVision 。SERVER_PORT :发送请求的端口号。REQUEST_METHOD :请求的方法。对于HTTP,有"GET"、 “HEAD”、 “POST"等等。PATH_INFO :额外的路径信息,由客户端给出的。换句话说,脚本可以由他们的虚拟路径名来访问,在这个路径的末尾附带额外的信息。这个额外信息被作为PATH_INFO发送。这个信息如果在传递给CGI脚本之前来自URL就可以由服务器来解码。PATH_TRANSLATED :服务器提供了一个PATH_INFO的转换版本,它需要路径并且为它做虚拟到物理的映射。SCRIPT_NAME :将要执行的脚本的一个虚拟路径。QUERY_STRING :在引用脚本的URL中紧跟在?之后的信息。这是一个查询信息。它不能以任何方式来解码。这个变量总是可以在有查询信息的时候被设置,而不管命令行解码。REMOTE_HOST :产生请求的主机名。如果服务器没有这个信息,它应该设置REMOTE_ADDR 并且让这个为未设置状态。REMOTE_ADDR :产生请求的远程主机的IP地址。AUTH_TYPE :如果服务器支持用户验证,脚本就受保护。这是一个协议规范授权方法,用于验证用户。REMOTE_USER :如果服务器支持用户验证,脚本就受保护。这是他们授权的用户名。REMOTE_IDENT :如果HTTP服务器支持RFC931认证,这个变量将被设置为从服务器取出的远程用户名。这个变量的用法应该只限制在登陆的时候。CONTENT_TYPE :对于哪些已经附上信息的请求,比如 HTTP POST和PUT,这是数据的内容类型。CONTENT_LENGTH :客户端给的数据内容的长度。这些变量需要各个语言和服务器进行自己的实现,同时他们也会有自己定义的一些变量。如我们今天要说的PHP语言中的$_SERVER[‘PHP_SELF’]变量。PHP的超全局变量$_SERVER$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或者提供一些没有在这里列举出来的项目。这也就意味着大量的此类变量都会在» CGI 1.1 规范中说明,所以应该仔细研究一下。FILE 常量包含当前(例如包含)文件的完整路径和文件名。与此相关的,我们这里主要关注的几个变量是:PHP_SELF: 当前执行脚本的文件名,与 document root 有关。例如,在地址为 http://example.com/foo/bar.php 的脚本中值为 /foo/bar.php。SCRIPT_NAME: 包含当前脚本的路径。这在页面需要指向自己时非常有用。PATH_INFO: 包含由客户端提供的、跟在真实脚本名称之后并且在查询语句(query string)之前的路径信息,如果存在的话。例如,如果当前脚本是通过 URL http://www.example.com/php/path_info.php/some/stuff?foo=bar 被访问,那么值为 /some/stuff。文档里表述的Web服务器,在我的环境里指代的是Nginx。在Apache中,当不加配置的时候对于PHP脚本, AcceptPathInfo是默认接受的。而对于Nginx下, 是不支持PATH INFO的, 也就是它不会默认设置PATH_INFO.因此,对于一个Nginx架构的常规请求来说,这几个字段的值分别是:# http://www.baidu.com:8080/odp/index.php?r=updatePHP_SELF: /odp/index.phpSCRIPT_NAME: /odp/index.phpPATH_INFO: null问题:PHP_SELF中出现重复路径在我部署完成新的Nginx服务后,得到的上面三个字段的值为:# http://www.baidu.com:8080/odp/index.php?r=updatePHP_SELF: /odp/index.php/odp/index.phpSCRIPT_NAME: /odp/index.phpPATH_INFO: /odp/index.php注意这里的PHP_SELF字段存在重复的路径,而PATH_INFO也存在了值,此时的nginx.conf配置为:fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;fastcgi_param QUERY_STRING $query_string;fastcgi_param REQUEST_METHOD $request_method;fastcgi_param CONTENT_TYPE $content_type;fastcgi_param CONTENT_LENGTH $content_length;# 注意这一行,我们配置了PATH_INFO字段fastcgi_param PATH_INFO $fastcgi_script_name;fastcgi_param SCRIPT_NAME $fastcgi_script_name;fastcgi_param REQUEST_URI $request_uri;fastcgi_param DOCUMENT_URI $document_uri;fastcgi_param DOCUMENT_ROOT $document_root;fastcgi_param SERVER_PROTOCOL $server_protocol;fastcgi_param HTTPS $https if_not_empty;fastcgi_param GATEWAY_INTERFACE CGI/1.1;fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;那么我们为什么配置了PATH_INFO就会影响PHP_SELF的值了呢?这一点,我们首先会想到PHP_SELF这个自定义属性的来源是什么,然而,我并没有找到任何的文档说明。但我们可以通过重命名的方式,来探究一下它的定义:fastcgi_param PATH_INFO PATH_INFO;# fastcgi_param PATH_INFO $fastcgi_script_name;fastcgi_param SCRIPT_NAME SCRIPT_NAME;# fastcgi_param SCRIPT_NAME $fastcgi_script_name;变更这两行,我们将其重命名为指定字符串,而不是请求传入的变量,nginx reload后,此时的结果是:# http://www.baidu.com:8080/odp/index.php?r=updatePHP_SELF: SCRIPT_NAMEPATH_INFOSCRIPT_NAME: SCRIPT_NAMEPATH_INFO: PATH_INFO而其他变量均正常,因此我们可以进一步理解:PHP_SELF = SCRIPT_NAME + PATH_INFO自定义变量:PHP_SELF那么PHP为什么要自定义这个属性呢?在官方文档里有这么一个url请求,此时:# http://www.example.com/php/path_info.php/some/stuff?foo=bar PHP_SELF: /php/path_info.php/some/stuffSCRIPT_NAME: /php/path_info.phpPATH_INFO: /some/stuff所以,在这种场景下,只有PHP_SELF才能拿到完整的当前执行脚本的文件或路径。总结为了不同服务器、不同语言之间的请求通信,于是有了CGI协议规范,这个规范在不同的服务器和语言中有自己的实现,在Web Server: Nginx的配置文件中,可以设置不同变量的值,解析后传递给PHP-FPM(PHP-FastCGI Process Manager),再进一步传递给负责响应请求的PHP子进程,而PHP中也定义了关于请求通信的全局变量$_SERVER,用于解析请求和处理逻辑。这就是整个关于解析请求信息的流程。由于PHP中$_SERVER中的这几个变量的定义有一定混淆,也依赖于不同的实现和Server环境,如PATH_INFO在Nginx/Apache中的不同默认状态,因此,如果需要页面指向自己时,除非如上面示例中的那种url,建议使用SCRIPT_NAME变量即可。参考资料segmentfault-php-fpm进程数管理: https://segmentfault.com/a/11...RFC-CGI1.1: https://tools.ietf.org/html/r...CGI规范及其历史:http://www.voidcn.com/article…php关于$_SERVER中一些和环境有关的参数详解: https://www.jianshu.com/p/fea...PHP文档-$_SERVER:http://php.net/manual/zh/rese… ...

February 21, 2019 · 1 min · jiezi

NGINX HTTP2 处理流程

本文通过一个小例子串一遍nginx处理http2的流程。主要涉及到http2的协议以及nginx的处理流程。http2简介http2比较http1.1主要有如下五个方面的不同:二进制协议 http1.1请求行和请求头部都是纯文本编码,即可以直接按ascii字符解释,而http2是有自己的编码格式。并且nginx中http2必须建立在ssl协议之上。头部压缩 举个例子,HTTP1.1传一个header <method: GET>,需要11个字符.http2中有一个静态索引表,客户端传索引键,例如1,nginx通过查表能知道1代表method: GET.nginx中除了该静态表,还会有一个动态表,保存例如host这种变化的头部多路复用 http1.1一个连接上只能传输一个请求,当一个请求结束之后才能传输下一个请求。所以对http1.1协议的服务发起请求时,一般浏览器会建立6条连接,并行的去请求不同的资源。而http2的二进制协议中有一个frame的概念,每个frame有自己的id,所以一个连接上可以同时多路复用传输多个不同id的frame主动push http1.1是请求-响应模型,而http2可以主动给客户端推送资源优先级 既然多路复用,所有数据跑在了一条通道上,必然会有优先级的需求本文的例子主要通过解析报文说明头三个特性配置环境NGINX配置如下: server { listen 8443 ssl http2; access_log logs/host_server2.access.log main; ssl_certificate /home/xiaoju/nginx-2/nginx-selfsigned.crt; ssl_certificate_key /home/xiaoju/nginx-2/nginx-selfsigned.key; ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5; location / { root html; index index.html index.htm /abc.html; access_log logs/host_location3.access.log main; http2_push /favicon.ico; http2_push /nginx.png; } }客户端按如下方式发起请求:curl -k -I -L https://IP:8443HTTP/2 200 //可以看到,返回是http/2server: nginx/1.14.0date: Tue, 11 Dec 2018 09:20:33 GMTcontent-type: text/htmlcontent-length: 664last-modified: Tue, 11 Dec 2018 04:19:32 GMTetag: “5c0f3ad4-298"accept-ranges: bytes请求解析客户端请求问题先思考一个问题,上文配置中使用curl发送请求时,为何直接返回的是http/2,而不是http/1.1(虽然服务端配置了使用http2,但万一客户端未支持http2协议,直接返回http2客户端会解析不了)因为nginx中http2必须在ssl之上,所以我们首先通过在nginx代码中的ssl握手部分打断点gdb跟一下.(gdb) b ngx_ssl_handshake_handler //ssl握手函数Breakpoint 1 at 0x47ddb5: file src/event/ngx_event_openssl.c, line 1373.(gdb) cContinuing.Breakpoint 1, ngx_ssl_handshake_handler (ev=0x16141f0) at src/event/ngx_event_openssl.c:13731373 {1390 c->ssl->handler(c); //实际处理逻辑位于ngx_http_ssl_handshake_handler(gdb) sngx_http_ssl_handshake_handler (c=0x15da400) at src/http/ngx_http_request.c:782782 {(gdb) n805 if (hc->addr_conf->http2) { //配置http2后hc->addr_conf->http2标志位为1(gdb) n808 SSL_get0_alpn_selected(c->ssl->connection, &data, &len);//从ssl协议中取出alpn(gdb) n820 if (len == 2 && data[0] == ‘h’ && data[1] == ‘2’) { //如果为h2,说明客户端支持升级到http2协议(gdb) n821 ngx_http_v2_init(c->read);//开始进入http2的初始化阶段简单说就是通过ssl协议握手阶段获取一个alpn相关的配置,如果是h2,就进入http2的处理流程。我们通过wireshark抓包可以更直观的看出这个流程如上图,在ssl握手中的Client Hello 阶段有一个协议扩展alpnhttp2报文格式http2 以一个preface开头,接着是一个个的frame,其中每个frame都有一个header,如下:其中length代表frame内容的长度,type表明frame的类型,flag给frame做一些特殊的标记,sid代表的就是frame的id.其中 frame有如下10种类型#define NGX_HTTP_V2_DATA_FRAME 0x0 //body数据#define NGX_HTTP_V2_HEADERS_FRAME 0x1 //header数据#define NGX_HTTP_V2_PRIORITY_FRAME 0x2 //优先级设置#define NGX_HTTP_V2_RST_STREAM_FRAME 0x3 //重置一个stream#define NGX_HTTP_V2_SETTINGS_FRAME 0x4 //其他设置项,例如是否开启push,同时能够处理的stream数量等#define NGX_HTTP_V2_PUSH_PROMISE_FRAME 0x5 //push#define NGX_HTTP_V2_PING_FRAME 0x6 //ping#define NGX_HTTP_V2_GOAWAY_FRAME 0x7 //goaway.发送此frame后会重新建立连接#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME 0x8 //窗口更新 流控使用#define NGX_HTTP_V2_CONTINUATION_FRAME 0x9 //当一个frame发送不完数据时,可以按continuation格式继续发送frame ID在客户端按奇数递增,例如1,3,5,偶数型id留给服务端推送push时使用,设置连接属性相关的frame id都为0flags有如下定义:#define NGX_HTTP_V2_NO_FLAG 0x00 //未设置#define NGX_HTTP_V2_ACK_FLAG 0x01 //ack flag#define NGX_HTTP_V2_END_STREAM_FLAG 0x01 //结束stream#define NGX_HTTP_V2_END_HEADERS_FLAG 0x04 //结束headers#define NGX_HTTP_V2_PADDED_FLAG 0x08 //填充flag#define NGX_HTTP_V2_PRIORITY_FLAG 0x20 //优先级设置flag如下是一个http头类型frame具体的内容格式:padded和priority由上文头部的flag决定是否有这两字段。接下来占8bit的flag决定header是否需要索引,如果需要,索引号是多少。huff(1)表明该字段是否使用了huffman编码。header_value_len(7)和header_value是具体头字段的value值如下是一个设置相关的frame如下是一个窗口更新的frame下边我们看一个具体的例子http2报文解析新版本的curl有一个–http2参数,可以直接指明使用http2进行通讯。我们将客户端命令修改如下:curl –http2 -k -I -L https://10.96.79.14:8443通过上边的gdb跟踪,我们看到http2初始化入口函数为ngx_http_v2_init,直接在此处打断点,继续跟踪代码.跟踪过程不再详细描述,当把报文读取进缓存之后,我们直接在gdb中bt查看调用路径,如下:#0 ngx_http_v2_state_preface (h2c=0x15a9310, pos=0x164b0b0 “PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n”, end=0x164b11e “”) at src/http/v2/ngx_http_v2.c:713#1 0x00000000004bca20 in ngx_http_v2_read_handler (rev=0x16141f0) at src/http/v2/ngx_http_v2.c:415#2 0x00000000004bcf8a in ngx_http_v2_init (rev=0x16141f0) at src/http/v2/ngx_http_v2.c:328#3 0x0000000000490a13 in ngx_http_ssl_handshake_handler (c=0x15da400) at src/http/ngx_http_request.c:821#4 0x000000000047de24 in ngx_ssl_handshake_handler (ev=0x16141f0) at src/event/ngx_event_openssl.c:1390#5 0x0000000000479637 in ngx_epoll_process_events (cycle=0x1597e30, timer=<optimized out>, flags=<optimized out>) at src/event/modules/ngx_epoll_module.c:902#6 0x000000000046f9db in ngx_process_events_and_timers (cycle=0x1597e30) at src/event/ngx_event.c:242#7 0x000000000047761c in ngx_worker_process_cycle (cycle=0x1597e30, data=<optimized out>) at src/os/unix/ngx_process_cycle.c:750#8 0x0000000000475c50 in ngx_spawn_process (cycle=0x1597e30, proc=0x477589 <ngx_worker_process_cycle>, data=0x0, name=0x684922 “worker process”, respawn=-3) at src/os/unix/ngx_process.c:199#9 0x00000000004769aa in ngx_start_worker_processes (cycle=0x1597e30, n=1, type=-3) at src/os/unix/ngx_process_cycle.c:359#10 0x0000000000477cb0 in ngx_master_process_cycle (cycle=0x1597e30) at src/os/unix/ngx_process_cycle.c:131#11 0x0000000000450ea4 in main (argc=<optimized out>, argv=<optimized out>) at src/core/nginx.c:382调用到ngx_http_v2_state_preface这个函数之后,开始处理http2请求,我们将请求内容打印出来看一下:(gdb) p end-pos$1 = 110(gdb) p pos@110$2 = “PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n\000\000\022\004\000\000\000\000\000\000\003\000\000\000d\000\004@\000\000\000\000\002\000\000\000\000\000\000\004\b\000\000\000\000\000?\377\000\001\000\000%\001\005\000\000\000\001B\004HEAD\204\207A\214\b\027}\305\335}p\265q\346\232gz\210%\266Pë\266\322\340S\003/*“nginx接下来开始处理http2请求,处理方法可以按上述方法继续跟踪,我们直接按http2协议将上述报文解析一下,如下所示:注意gdb打印出来的是八进制格式http push抓包注意上文nginx配置中配置了两条http2_push指令,即服务端会在请求index.html时主动将favicon.ico和nginx.png两个图片push下去。wireshark中抓包如下:服务端首先发送一个push_promise报文,报文中会包括push的文件路径和frame id.第二个和第三个红框即开始push具体的信息,frame id分别为2和4我们从浏览器端看一下push的请求:不主动push请求如下:浏览器必须首先将index.html加载之后才会知道接着去请求哪些资源,于是favicon.ico和nginx.png就会延迟加载。Q&AHTTP2如果在服务端动态索引header,会使http变成有状态的服务,集群之间如何解决header头缓存的问题?静态资源文件首次请求后会在浏览器端缓存,push如何保证只推送一次(即只有首次请求时才push)?参考资料1.https://www.nginx.com/blog/ht…2.https://httpwg.org/specs/rfc7540 ...

February 20, 2019 · 2 min · jiezi

NGINX 4xx 5xx 状态码构造

nginx配置worker_processes 1;events { worker_connections 1024;}http { include mime.types; default_type application/octet-stream; limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; sendfile on; keepalive_timeout 65; server { listen 8070; server_name 10.96.79.14; limit_req zone=one; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } location = /abc.html { root html; auth_basic “opened site”; auth_basic_user_file conf/htpasswd; } location ~ .php$ { root /home/xiaoju/nginx-1.14.0/html; fastcgi_index index.php; fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME /home/xiaoju/nginx-1.14.0/html$fastcgi_script_name; include fastcgi.conf; fastcgi_connect_timeout 300; fastcgi_send_timeout 300; fastcgi_read_timeout 300; } }}index.php <?phpecho “124”;4xx系列400NGX_HTTP_BAD_REQUESTHost头不合法 curl localhost:8070 -H ‘Host:123/com’<html><head><title>400 Bad Request</title></head><body bgcolor=“white”><center><h1>400 Bad Request</h1></center><hr><center>nginx/1.14.0</center></body></html> Content-Length头重复curl localhost:8070 -H ‘Content-Length:1’ -H ‘Content-Length:2’<html><head><title>400 Bad Request</title></head><body bgcolor=“white”><center><h1>400 Bad Request</h1></center><hr><center>nginx/1.14.0</center></body></html>401NGX_HTTP_UNAUTHORIZED参考如上nginx配置,访问abc.html需要认证 curl localhost:8070/abc.html<html><head><title>401 Authorization Required</title></head><body bgcolor=“white”><center><h1>401 Authorization Required</h1></center><hr><center>nginx/1.14.0</center></body></html>403NGX_HTTP_FORBIDDENchmod 222 index.html将index.html设置为不可读 curl localhost:8070<html><head><title>403 Forbidden</title></head><body bgcolor=“white”><center><h1>403 Forbidden</h1></center><hr><center>nginx/1.14.0</center></body></html>404NGX_HTTP_NOT_FOUNDcurl localhost:8070/cde.html<html><head><title>404 Not Found</title></head><body bgcolor=“white”><center><h1>404 Not Found</h1></center><hr><center>nginx/1.14.0</center></body></html>405NGX_HTTP_NOT_ALLOWED使用非GET/POST/HEAD方法访问一个静态文件curl -X DELETE localhost:8070/index.html -IHTTP/1.1 405 Not AllowedServer: nginx/1.14.0Date: Tue, 18 Sep 2018 10:02:22 GMTContent-Type: text/htmlContent-Length: 173Connection: keep-alive5xx系列500NGX_HTTP_INTERNAL_SERVER_ERROR修改index.php为<?phpecho “124” 缺少引号,语法错误curl localhost:8070/index.php -IHTTP/1.1 500 Internal Server ErrorServer: nginx/1.14.0Date: Tue, 18 Sep 2018 11:29:19 GMTContent-Type: text/html; charset=UTF-8Connection: keep-aliveSet-Cookie: PHPSESSID=aoesvcuvbh1nh95kdkp152r9e1; path=/Expires: Thu, 19 Nov 1981 08:52:00 GMTCache-Control: no-store, no-cache, must-revalidatePragma: no-cache501NGX_HTTP_NOT_IMPLEMENTEDnginx的transfer-encoding现在只支持chunked,如果客户端随意设置这个值,会报501 curl localhost:8070 -H ‘Transfer-Encoding:1’<html><head><title>501 Not Implemented</title></head><body bgcolor=“white”><center><h1>501 Not Implemented</h1></center><hr><center>nginx/1.14.0</center></body></html>502NGX_HTTP_BAD_GATEWAY修改nginx配置为fastcgi_pass 127.0.0.1:8000;指向一个未监听的端口curl localhost:8070/index.php -IHTTP/1.1 502 Bad GatewayServer: nginx/1.14.0Date: Tue, 18 Sep 2018 11:28:17 GMTContent-Type: text/htmlContent-Length: 537Connection: keep-aliveETag: “5ad6113c-219"503NGX_HTTP_SERVICE_UNAVAILABLE修改nginx配置,限速为每分钟10个请求 limit_req_zone $binary_remote_addr zone=one:10m rate=10r/m;limit_req zone=one;连续发送两个请求,第二请求会报503curl localhost:8070/index.php -IHTTP/1.1 503 Service Temporarily UnavailableServer: nginx/1.14.0Date: Tue, 18 Sep 2018 11:31:43 GMTContent-Type: text/htmlContent-Length: 537Connection: keep-aliveETag: “5ad6113c-219"504NGX_HTTP_GATEWAY_TIME_OUT修改index.php为<?phpecho “124”;sleep(5);休息5秒钟 修改nginx配置为三秒钟读超时fastcgi_read_timeout 3;curl localhost:8070/index.php -IHTTP/1.1 504 Gateway Time-outServer: nginx/1.14.0Date: Tue, 18 Sep 2018 12:17:57 GMTContent-Type: text/htmlContent-Length: 537Connection: keep-aliveETag: “5ad6113c-219"505NGX_HTTP_VERSION_NOT_SUPPORTEDtelnet8070端口,输入GET /index.html HTTP/2.1不支持http/2.1,会报505 $telnet localhost 8070Trying 127.0.0.1…Connected to localhost.Escape character is ‘^]’.GET /index.html HTTP/2.1HTTP/1.1 505 HTTP Version Not SupportedServer: nginx/1.14.0Date: Tue, 18 Sep 2018 12:26:35 GMTContent-Type: text/htmlContent-Length: 203Connection: close<html><head><title>505 HTTP Version Not Supported</title></head><body bgcolor=“white”><center><h1>505 HTTP Version Not Supported</h1></center><hr><center>nginx/1.14.0</center></body></html> ...

February 20, 2019 · 2 min · jiezi

如何在Kubernetes集群动态使用 NAS 持久卷

介绍:本文介绍的动态生成NAS存储卷的方案:在一个已有文件系统上,自动生成一个目录,这个目录定义为目标存储卷;镜像地址:registry.cn-hangzhou.aliyuncs.com/acs/alicloud-nas-controller:v1.11.5.4-433631d-aliyun默认生成资源:生成的PV名字为:pvc-${pvc-uid}生成目录的名字:namespace-pvcname-pvname可以再pvc的annotations中如下声明,自定义名字:生成的pv、目录名字为下面定义的名字。 annotations: pv-name-created: replace-user-id2. 部署NAS Controller创建alicloud-nas-controller,实现动态provider nas pv;创建alicloud-nas storageclass,为nas pv provision 提供模板;apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: alicloud-nasprovisioner: alicloud/nasreclaimPolicy: Deleteparameters: drivertype: flexvolume nfsversion: “4.0” options: “”—kind: DeploymentapiVersion: extensions/v1beta1metadata: name: alicloud-nas-controller namespace: kube-systemspec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: alicloud-nas-controller spec: tolerations: - effect: NoSchedule operator: Exists key: node-role.kubernetes.io/master - effect: NoSchedule operator: Exists key: node.cloudprovider.kubernetes.io/uninitialized serviceAccount: admin containers: - name: alicloud-nas-controller image: registry.cn-hangzhou.aliyuncs.com/acs/alicloud-nas-controller:v1.11.5.4-433631d-aliyun imagePullPolicy: Always volumeMounts: - mountPath: /persistentvolumes name: nfs-client-root env: - name: NFS_SERVER value: 154154b095-.cn-beijing.nas.aliyuncs.com - name: NFS_PATH value: / volumes: - name: nfs-client-root flexVolume: driver: alicloud/nas options: path: / server: 154154b095-.cn-beijing.nas.aliyuncs.com vers: “4.0"StorageClass使用说明:drivertype: 用来表示生成pv存储类型,可选nfs, flexvolume. nfs: 默认选项,表示使用k8s原生NFS驱动挂载; flexvolume: 表示使用阿里云提供的Flexvolume NAS驱动挂载;nfsversion: 挂载nfs使用的版本,支持3,4.0.默认为4.0; drivertype为flexvolume的时候在这里配置; 为nfs的时候通过mountOptions 配置;options:为挂载nfs的可选项配置; drivertype为flexvolume的时候在这里配置; 为nfs的时候通过mountOptions 配置;StorageClass举例:## 使用kubernetes提供的NFS驱动,并配置mountOptions,reclaimPolicy为Delete;apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: alicloud-nas-nfsmountOptions:- vers=4.0- noresvportprovisioner: alicloud/nasreclaimPolicy: Delete## 使用阿里云提供的Flexvolume NAS驱动,配置nfs版本、options;apiVersion: storage.k8s.io/v1kind: StorageClassmetadata: name: alicloud-nas-flexprovisioner: alicloud/nasreclaimPolicy: Deleteparameters: drivertype: flexvolume nfsversion: “3” options: “noresvport"3. 创建应用-Deployment:kind: PersistentVolumeClaimapiVersion: v1metadata: name: replace-user-id annotations: pv-name-created: replace-user-idspec: storageClassName: alicloud-nas accessModes: - ReadWriteMany resources: requests: storage: 5Gi—apiVersion: extensions/v1beta1kind: Deploymentmetadata: name: “deploy-nas"spec: replicas: 1 strategy: type: Recreate template: metadata: labels: app: deploy-nas spec: containers: - name: “nginx” image: “nginx” volumeMounts: - name: pvc-nas mountPath: “/data” volumes: - name: pvc-nas persistentVolumeClaim: claimName: replace-user-id执行:# userID=“hello-123”# cat deploy.yaml | sed “s/replace-user-id/"$userID"/g” | kubectl create -f -# kubectl get pod | grep deploy-nasdeploy-nas-85696b6bfc-t5dmh 1/1 Running 0 28m# kubectl get pvc | grep hellhello-123 Bound hello-123 5Gi RWX alicloud-nas-flex 28m# kubectl get pv | grep hellhello-123 5Gi RWX Delete Bound default/hello-123 alicloud-nas-flex 28m# Nas目录下查看生成目录:# ls -l | grep hellodrwxrwxrwx 2 root root 4096 2月 19 09:58 hello-1234. 创建应用-StatefulSet:使用volumeTemplateClaim不支持使用pv-name-created配置pv名字;apiVersion: v1kind: Servicemetadata: name: nginx labels: app: nginxspec: ports: - port: 80 name: web clusterIP: None selector: app: nginx—apiVersion: apps/v1beta1kind: StatefulSetmetadata: name: webspec: replicas: 2 serviceName: “nginx” template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:alpine volumeMounts: - mountPath: “/data” name: pvc-sts volumeClaimTemplates: - metadata: name: pvc-sts spec: accessModes: - ReadWriteOnce storageClassName: alicloud-nas-flex resources: requests: storage: 2Gi 创建后查看:# kubectl get pod | grep webweb-0 1/1 Running 0 7sweb-1 1/1 Running 0 4s# kubectl get pvc | grep webpvc-sts-web-0 Bound pvc-65ab251a-33ec-11e9-a151-00163e066784 2Gi RWO alicloud-nas-flex 13mpvc-sts-web-1 Bound pvc-8437c50e-33ed-11e9-a151-00163e066784 2Gi RWO alicloud-nas-flex 5m# kubectl get pv | grep webpvc-65ab251a-33ec-11e9-a151-00163e066784 2Gi RWO Delete Bound default/pvc-sts-web-0 alicloud-nas-flex 13mpvc-8437c50e-33ed-11e9-a151-00163e066784 2Gi RWO Delete Bound default/pvc-sts-web-1 alicloud-nas-flex 5m# Nas目录下查看生成目录:# ls -l | grep stsdrwxrwxrwx 2 root root 4096 2月 19 10:16 default-pvc-sts-web-0-pvc-65ab251a-33ec-11e9-a151-00163e066784drwxrwxrwx 2 root root 4096 2月 19 10:24 default-pvc-sts-web-1-pvc-8437c50e-33ed-11e9-a151-00163e0667845. 创建应用-Pod:kind: PersistentVolumeClaimapiVersion: v1metadata: name: replace-user-id annotations: pv-name-created: replace-user-idspec: storageClassName: alicloud-nas-flex accessModes: - ReadWriteMany resources: requests: storage: 5Gi—apiVersion: v1kind: Podmetadata: name: “nas-pod"spec: containers: - name: “nginx” image: “nginx” volumeMounts: - name: pvc-nas mountPath: “/data” volumes: - name: pvc-nas persistentVolumeClaim: claimName: replace-user-id # userID=“pod-123”# cat pod.yaml | sed “s/replace-user-id/"$userID"/g” | kubectl create -f -# kubectl get pod | grep podnas-pod 1/1 Running 0 32s# kubectl get pvc | grep podpod-123 Bound pod-123 5Gi RWX alicloud-nas-flex 44s# kubectl get pv | grep podpod-123 5Gi RWX Delete Bound default/pod-123 alicloud-nas-flex 48s# ls -l | grep poddrwxrwxrwx 2 root root 4096 2月 19 10:54 pod-123本文作者:kanjunbao阅读原文本文为云栖社区原创内容,未经允许不得转载。

February 20, 2019 · 3 min · jiezi

如何在 CentOS 7 上生成 SSL 证书为 Nginx 加密

本文首发:开发指南:如何在 CentOS 7 上安装 NginxLet’s Encrypt 是由 Internet Security Research Group (ISRG) 开发的一个自由、自动化和开放的证书颁发机构。目前几乎所有的现代浏览器都信任由 Let’s Encrypt 颁发的证书。这个教程,将会一步一步的教你如何在 CentOS 7 上通过 Certbot 来生成 SSL 安全证书,并配置到 Nginx 上。开始前的准备在继续此教程之前,请确保你已经满足了以下两个条件:请确保你已经拥有了一个属于你的域名,并且已经解析到了你的服务器 IP 上,在接下来的教程中,我将会用 kaifazhinan.com 作为本教程的域名。请确保你已经启用了 EPEL 仓库,并且已经安装了 Nginx,如果你还没有安装 Nginx,你可以先阅读 如何在 CentOS 7 上安装 Nginx 这篇文章来安装 Nginx。安装 CertbotCertbot 是一个非常简单方便的工具,它可以帮助我们生成 SSL 证书,自动更新 SSL 证书,并且将证书配置到 Web 服务上。可以运行以下命令,从 EPEL 仓库中安装 Certbot:sudo yum install certbot生成 Dh (Diffie-Hellman) 组Diffie–Hellman 密匙交换是一种可以在不安全的通信信道上安全交换密钥的方法。现在运行以下命令,可以来生成一个新的 2048 位的 DH 参数:sudo openssl dhparam -out /etc/ssl/certs/dhparam.pem 20482048 位,生成时间大概 3-5 分钟左右。当然,如果您愿意也可以将大小改为 4096 位,但是这样的话,可能生成的时间至少需要花费 30 分钟,此操作具体时长取决于系统熵。生成 SSL 证书要生成域名的 SSL 证书,我们将使用 Webroot 插件在 ${webroot-path}/.well-known/acme-challenge 目录中创建临时文件来验证请求的域名。Let’s Encrypt 服务器会向临时文件发出 HTTP 请求,以验证请求的域名是否被正确的解析到了正在运行 Certbot 的服务器。为了简便,我们将把所有访问 .well-known/acme-challenge 的 HTTP 请求都映射到 /var/lib/letsencrypt 这个目录中。下面的命令将会创建这个目录,并且使 Nginx 对它拥有读写的权限。sudo mkdir -p /var/lib/letsencrypt/.well-knownsudo chgrp nginx /var/lib/letsencryptsudo chmod g+s /var/lib/letsencrypt创建代码片段为了避免 Nginx 配置文件中存在重复的代码,请创建以下两个代码片段(里面是 Nginx 的配置代码),我们将在相关的 Nginx 配置文件中包含这些片段:1、首先,创建一个目录,用于存放 Nginx 配置的代码片段文件:sudo mkdir /etc/nginx/snippets2、创建第一个片段文件, letsencrypt.conf,其全路径为: /etc/nginx/snippets/letsencrypt.conflocation ^~ /.well-known/acme-challenge/ { allow all; root /var/lib/letsencrypt/; default_type “text/plain”; try_files $uri =404;}3、创建第二个片段文件,ssl.conf,其全路径为: /etc/nginx/snippets/ssl.confssl_dhparam /etc/ssl/certs/dhparam.pem;ssl_session_timeout 1d;ssl_session_cache shared:SSL:50m;ssl_session_tickets off;ssl_protocols TLSv1 TLSv1.1 TLSv1.2;ssl_ciphers ‘ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS’;ssl_prefer_server_ciphers on;ssl_stapling on;ssl_stapling_verify on;resolver 8.8.8.8 8.8.4.4 valid=300s;resolver_timeout 30s;add_header Strict-Transport-Security “max-age=15768000; includeSubdomains; preload”;add_header X-Frame-Options SAMEORIGIN;add_header X-Content-Type-Options nosniff;上面的代码中包含 Mozilla 的推荐部分。 支持 OCSP Stapling,HTTP 严格传输安全(HSTS)并强制执行几个以安全为中心的 HTTP 头。加载 letsencrypt.conf代码片段创建完成之后,就可以打开 Nginx 的域名独立配置文件,将 letsencrypt.conf 文件引入。在这里,我们的域名是 kaifazhinan.com ,所以我们的配置文件为 kaifazhinan.com.conf, 文件的全路径为 /etc/nginx/conf.d/kaifazhinan.com.conf。server { listen 80; server_name kaifazhinan.com www.kaifazhinan.com; include snippets/letsencrypt.conf;}注意: 我们建议针对不同的域名,创建不同的独立配置文件。这样会比较清晰,便于管理和查找对应的配置。Nginx 的主配置文件中有一行代码是 include /etc/nginx/conf.d/*.conf,这行代码的意思就是加载 /etc/nginx/conf.d/ 目录下所有以 .conf 结尾的配置文件,所以我们直接将独立的配置文件保存在 /etc/nginx/conf.d/ 目录下就会自动引入。生成证书重新加载 Nginx 配置使更改生效:sudo systemctl reload nginx你现在可以运行 Certbot 使用 Webroot 插件,为你的域名生成 SSL 证书:sudo certbot certonly –agree-tos –email admin@kaifazhinan.com –webroot -w /var/lib/letsencrypt/ -d kaifazhinan.com -d www.kaifazhinan.com注意: 记得将 admin@kaifazhinan.com 换成你自己的邮箱,还有 kaifazhinan.com 和 www.kaifazhinan.com 换成你的域名。如果成功的生成了 SSL 证书,那么 Certbot 将打印类似以下的内容:IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/kaifazhinan.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/kaifazhinan.com/privkey.pem Your cert will expire on 2019-02-11. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew all of your certificates, run “certbot renew” - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let’s Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le配置 Nginx现在你已经成功生成了 SSL 证书,现在可以修改 Nginx 的域名配置了,这里我们的域名是配置文件是 kaifazhinan.com.conf,文件的全路径是 /etc/nginx/conf.d/kaifazhinan.com.conf:server { listen 80; server_name www.kaifazhinan.com kaifazhinan.com; include snippets/letsencrypt.conf; return 301 https://$host$request_uri;}server { listen 443 ssl http2; server_name www.kaifazhinan.com; ssl_certificate /etc/letsencrypt/live/kaifazhinan.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/kaifazhinan.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/kaifazhinan.com/chain.pem; include snippets/ssl.conf; include snippets/letsencrypt.conf; return 301 https://kaifazhinan.com$request_uri;}server { listen 443 ssl http2; server_name kaifazhinan.com; ssl_certificate /etc/letsencrypt/live/kaifazhinan.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/kaifazhinan.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/kaifazhinan.com/chain.pem; include snippets/ssl.conf; include snippets/letsencrypt.conf; # 如果有补充的配置,可以写在这里}上面的代码,我们将 HTTP 的请求重定向到了 HTTPS,将 www.kaifazhinan.com 重定向到了 kaifazhinan.com 上。最后,通过下面的命令,重新加载 Nginx,使上面的配置生效:sudo systemctl reload nginx自动更新 Let’s Encrypt SSL 证书Let’s Encrypt 颁发的 SSL 证书有效时间是 90 天。我们需要在证书过期之前自动续订证书,这里将创建一个每天运行两次的定时任务 ,并在证书到期前 30 天自动续订。通过运行 crontab 命令,来创建一个定时任务:sudo crontab -e上面的命令,会自动创建一个文件,并自动进入编辑状态,所以直接复制下面的内容粘贴到里面即可:0 */12 * * * root test -x /usr/bin/certbot -a ! -d /run/systemd/system && perl -e ‘sleep int(rand(3600))’ && certbot -q renew –renew-hook “systemctl reload nginx"保存并关闭文件。如果要测试是否能够正常更新证书,你可以在 certbot 命令后面添加 –dry-run 这个参数来主动触发更新命令。sudo certbot renew –dry-run如果没有输出错误,则表示 SSL 证书更新成功。总结通过此教程,你学会了:如何使用 Let’s Encrypt 客户端 Certbot 为你的域名创建了 SSL 证书;也通过创建 Nginx 的代码片段,来避免 Nginx 配置文件中的代码冗余,并且将 SSL 证书配置到了 Nginx 服务中;最后,你还创建了一个定时任务,来自动更新你的 SSL 证书,保证它不会过期。如果你想了解 Certbot 的更多信息,可以参考它的官方文档。期待下次与你相见 : )本文首发:开发指南:如何在 CentOS 7 上安装 Nginx ...

February 19, 2019 · 3 min · jiezi

搭建gitbook 和 访问权限认证

相信大家都或多或少的都接触过gitbook。gitbook 首先是一个软件,正如上面定义的那样,它使用 Git 和 Markdown 来编排书本,如果用户没有听过 Git 和 Markdown,那么 gitbook 可能不适合你。废话不多说,干起来。1 gitbook安装1.1 安装npm包 $ npm install gitbook -g1.2 初始化项目 $ mkdir gitbook 新建目录 $ cd gitbook $ gitbook init 目录 gitbook/ ├── README.md └── SUMMARY.md1.3 起服务 $ gitbook serve1.4 打开浏览器 可以用浏览器打开 http://127.0.0.1:40001.5 生产文件 $ gitbook build2 登录权限认证搭建就完成了,但是有一下内部文档,不想公布出去,怎么办,这个网上没有答案,但是方法总是有的,那就是nginx2.1 用到nginx认证模块server { listen 80; server_name www.host.com ; # 域名注意不要加协议 location / { root html/blog; #根 静态文件目录 index index.html index.htm; auth_basic “pleas you password”; # nginx 认证用户和密码 auth_basic_user_file htpasswd; # nginx认证文件目录 可以随意指定 }2.2 因为要用到密码,而且是加密的,所有引入httpd模块 $ yum -y install httpd $ htpasswd -bc /applocation/nginx/conf/htpasswd qiyun 123456 #生产密码文件,如果不能写入,就创建好文件,在执行命令2.3 重新检测 $ nginx -t2.4 重启 $ nginx -S reload3 案例url: http://gitbook.beastxw.wang/name: aaapwd: 1234 图片gitbook登录认证5 博客url: http://blog.beastxw.wang/2019… ...

February 18, 2019 · 1 min · jiezi

centos6.8 安装nginx

centos6.8 安装nginx需要编译nginx源码,不过没啥大不了的,按照步骤来,肯定能成功。1.安装必要的依赖:yum install -y gcc-c++yum install -y pcre pcre-develyum install -y zlib zlib-develyum install -y openssl openssl-devel2.下载nginx源码并编译:nginx下载地址可以取官网找。我这是当前的最新版,1.13.wget http://nginx.org/download/nginx-1.13.5.tar.gz tar zxvf nginx-1.13.5.tar.gzcd nginx-1.13.5./configure –prefix=/usr/local/nginx –with-http_ssl_module –with-http_stub_status_module –with-pcremake && make installiptables -F(不然无法远程访问)3.编译完成后,我们去启动nginx通过命令 whereis nginx ,查看nginx的安装目录,可以看到路径是nginx: /usr/local/nginx然后cd /usr/local/nginx/sbin 执行命令 ./nginx这样就把nginx启动起来了。然后在浏览器里直接访问localhost,或者访问你的服务器ip。就可以看到nginx的欢迎页了。关闭nginx,执行命令 ./nginx -s stop重启nginx,执行命令 ./nginx -s reload

February 18, 2019 · 1 min · jiezi

Centos7.x下Nginx安装及SSL配置与常用命令

一、安装 采用yum方式安装##自动安装yum nginx##启动nginx 二、SSL及默认端口配置 泛解析配置server{ listen 443; server_name *.banacoo.cn; ssl on; ssl_certificate /etc/nginx/conf.d/1_banacoo.cn_bundle.crt; ssl_certificate_key /etc/nginx/conf.d/2_banacoo.cn.key; gzip on; gzip_buffers 16 8k; gzip_comp_level 6; gzip_min_length 200; gzip_types text/css text/xml application/javascript text/javascript application/x-javascript text/plan image/jpeg image/png image/gif; location /room/static { alias /home/room/; } location /statics { alias /home/quanyou/; } location / { proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto https; if ($request_method = ‘OPTIONS’) { add_header ‘Access-Control-Allow-Credentials’ true; add_header ‘Access-Control-Allow-Origin’ “$http_origin”; add_header ‘Access-Control-Allow-Methods’ ‘GET, POST, OPTIONS’; add_header ‘Access-Control-Allow-Headers’ ‘DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range’; add_header ‘Access-Control-Max-Age’ 1728000; return 204; } if ($host ~ ^(uatapi).banacoo.cn$){ proxy_pass http://127.0.0.1:8033; } if ($host ~ ^(uatapp).banacoo.cn$){ proxy_pass http://127.0.0.1:8022; } if ($host ~ ^(uatai).banacoo.cn$){ proxy_pass http://127.0.0.1:8011; } if ($host ~ ^(uatui).banacoo.cn$){ proxy_pass http://127.0.0.1:9080; } if ($host ~ ^(uatmarket).banacoo.cn$){ proxy_pass http://127.0.0.1:8088; } if ($host ~ ^(uatmarketui).banacoo.cn$){ proxy_pass http://127.0.0.1:9089; } if ($host ~ ^(uateasyjoy).banacoo.cn$){ proxy_pass http://127.0.0.1:8036; } } access_log logs/banacoo.cn.access.log;}server{ listen 80; server_name uatmarketui.banacoo.cn; charset utf-8; gzip on; gzip_buffers 16 8k; gzip_comp_level 6; gzip_min_length 200; gzip_types text/css text/xml application/javascript text/javascript application/x-javascript text/plan image/jpeg image/png image/gif; location / { proxy_pass http://127.0.0.1:9089; } autoindex on; autoindex_exact_size off; autoindex_localtime on; access_log logs/uatmarketui.banacoo.cn.access.log;}server{ listen 80; server_name uatmarket.banacoo.cn; charset utf-8; gzip on; gzip_buffers 16 8k; gzip_comp_level 6; gzip_min_length 200; gzip_types text/css text/xml application/javascript text/javascript application/x-javascript text/plan image/jpeg image/png image/gif; location / { proxy_pass http://127.0.0.1:8088; } autoindex on; autoindex_exact_size off; autoindex_localtime on; access_log logs/uatmarket.banacoo.cn.access.log;} 三、常用命令 ##重新加载配置nginx -s reload##停止服务nginx -s stop ...

February 18, 2019 · 2 min · jiezi

本地电脑搭建Nginx服务器

(1)Nginx官网下载(http://nginx.org/en/download....(2)打开文件nginx.conf文件 ,做出以下修改.server { # 启动后的端口 listen 8880; # 启动时的地址 server_name localhost; # 启动后,地址栏输入: localhost:8880, 默认会在html文件夹下找 index.html文件 location / { root html; index index.html; } # 404页面配置,页面同样在html文件夹中 error_page 404 /404.html; location = /404.html { root html; } # 其他错误码页面配置 error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # 配置代理。由于项目是在本地起动的,而我们的request需要请求其他ip地址。如果你的request链接为localhost:8880/abc/login?name=12345,那么下面配的就是location /abc location /api { proxy_pass http://192.168.0.0:80; } # 一把前端不管用vue,还是react等框架,默认都是单页面的,如果你的项目是多页面的,则需要用到下面的配置。 # 因为此时你的浏览器的url不是localhost:8880/#/login,而是 localhost:8880/a.html/#/login # 所以我们需要将路径中a.html指向具体的html文件夹中的文件,因为默认是index.html location /a.html { alias html; index a.html; } location /b.html{ alias html; index b.html; }}(3)将编译文件放入html文件夹里面(4)启动服务器 $start nginx 关闭 $nginx -s stop 浏览器打开localhost:7777 或者通过任务管理器关闭. ...

February 17, 2019 · 1 min · jiezi

Nginx服务系列——缓存

proxy_cache_path path[levels=1:2] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size]……(http)proxy_cache_path /opt/app/cache levels=1:2 keys_zone=jim_cache:10m max_size=10g(目录最大存储大小) inactive=60m(一小时内没有访问过缓存文件就被清理) use_temp_path=off;proxy_cache zone | off;(默认off)(httpserverlcation)proxy_cache_valid [code …] time;(httpserverlocation)缓存周期配置proxy_cache_valid 200 12h;#表示200的12h过期proxy_cache_valid any 10m;#表示其他请求都是10分钟过期proxy_cache_key 方式表示已什么方式为key来缓存proxy_cache_key $host$url$is_args$args;proxy_no_cache string不用缓存的urlproxy_no_cache string;(http\server\location)proxy_next_upstream filed filed;发生错误和超时就请求下一台服务器proxy_next_upstream error timeoutslice分片请求slice size(http\server\location)默认size=0;

February 16, 2019 · 1 min · jiezi

Nginx服务系列——代理

代理【反向代理配置】proxy_pass url;(location\if in location\limit_except)【正向代理配置】resolver 114.114.114.114;(DNS解析地址)在代理的上一级配置proxy_pass http://$http_host$request_uri; $http_host => 服务器域名 $request_uri => 路由地址缓冲区proxy_buffering on | off;默认打开(http\server\location)proxy_buffer_sizeproxy_buffers 4 128k;proxy_busy_buffers_size 256k;proxy_max_temp_file_size 256k;跳转重定向【常用默认proxy_redirect default | off | redirect replacement;(http\server\location)头信息proxy_set_header field value;(http\server\location)【常需要的配置 Host:$http_host X-Real-IP:$remote_addr;proxy_hide_headerproxy_set_body超时proxy_connect_timeout time;默认60s(http\server\location)proxy_read_timeoutproxy_send_timeout

February 16, 2019 · 1 min · jiezi

Nginx服务系列——负载均衡

轮询(默认方式)实现简单,不考虑每台服务器处理能力(weight默认值为1)upstream backserver{server www.jim.com:8080;server www.jim.com:9080;}权重考虑了每台服务器处理能力的不同upstream backserver{server www.jim.com:8080 weight=15;server www.jim.com:9080weight=10;}ip hash能实现同一个用户访问同一个服务器,ip hash不一定平均upstream backserver{ip_hash;server www.jim.com:8080;server www.jim.com:9080;}url hash(第三方)能实现同一个服务访问同一个服务器upstream backserver{server www.jim.com:8080;server www.jim.com:9080;hash $request_uri;}least_conn 最少连接数,那个机器连接数少就分发fair(第三方)按后端服务器响应时间upstream backserver{server www.jim.com:8080;server www.jim.com:9080;fair;}负载均衡参数讲解扩展知识点upstream backserver{ip_hash;server 地址:8080 down;down当前的server不参与负载server 地址:9080 backup;其他所有非backup的机器忙的时候请求此server,一般状态下有存活的就不访问此server}类似down的参数max_fails 允许请求失败的最大次数 fail_timeout 经过max_fails请求失败后服务的暂停时间默认为10s max_conns 限制最大的连接数

February 16, 2019 · 1 min · jiezi

CentOS 7 安装 Nginx

导语下面会用 yum 和编译两种方式来安装 Nginx。yum 安装使用 yum 命令,是相对简单的,输入 yum install -y nginx 显示如上界面,既是安装成功。接下来开启 Nginx 服务配置文件在 /etc/nginx/nginx.conf, 代码文件地址在 /usr/share/nginx/html。其他相关的内容可以在配置文件中查看,或者使用 find 命令进行查找。在上一篇文章中介绍了如何开启防火墙,这里就不在重复了,在浏览器中输入 IP 地址就可以看到 Nginx 的界面了到此为止,yum 安装 Nginx 完成。最后 yum remove -y nginx 移除安装,以便下面使用编译安装。编译安装编译安装要比使用 yum 复杂一些,它的好处是可以自选版本、根据需求自定义参数,更加自由。ok,说完了优点,下面开始进行需要先安装编译工具、依赖包,这一步使用 yum 进行即可,重点是编译安装 Nginx。输入 yum -y install gcc gcc-c++ autoconf automake zlib zlib-devel openssl openssl-devel pcre-devel 进行安装,可以根据提示信息进行调整使用 wget 来下载 Nginx 的安装包,根据自己的需求去网站下载合适的包很快就下载完成了,接下来是解压,使用 tar 命令 tar -zxvf nginx-1.14.1.tar.gz然后就是配置并且编译,Nginx 的配置参数可以查看下方参考资料中的文章,也可以使用 ./configure –help 进行查看。以下修改了一些配置配置成功的话,会看到下面的内容输入 make && make install 进行编译安装,成功后显示如下界面。第一次使用 user 账号没有编译成功,切换到 root 后成功启动服务进入到 /usr/local/nginx/sbin 目录下,输入 ./nginx 即可开启服务。尴尬的是没有启动成功,然后重新安装了一次才成功。在浏览器中输入 IP 地址可以看到如下页面参考资料:Nginx、yum 命令、nginx 编译安装详解、 CentOS7.0下编译安装Nginx 1.10.0。 ...

February 16, 2019 · 1 min · jiezi

Nginx服务系列——静态资源web服务

传输sendfile on | off;默认off(http\server\location\if in location)tcp_nopush on | off;默认off(http\server\location)sendfile开启的情况下,提高网络包的传输效率tcp_nodelay on | off;默认on(http\server\location)keepalive连接下,提高网络传输的实时性压缩文件gzip on | off;默认off(http\server\location\if in location) 压缩传输gzip_comp_level level;默认level=1(http\server\location) 压缩等级gzip_http_version 1.0 | 1.1;默认1.1(http\server\location)gzip_types text/plain被压缩的格式扩展nginx压缩模块http_gzip_static_modele 预读gzip功能(通过文件名调用相应的gz压缩包)gzip_static on;浏览器进行缓存校验(ETag/Last-Modified)验证会返回304expires: 24h;在响应头中添加Cache-control:max-age=86400并返回expires头跨站:add_header name value(http\server\location\if in location)如:add_header Access-Control-Allow-Origin 域名;防盗链:(防止资源被盗用)http_refervalid_referers none | blocked | server_names | string …;(server\location)如:valid_referers none blocked 域名;域名可以使用匹配的方式设置,~if($invalid_referer){ return 403;}

February 16, 2019 · 1 min · jiezi

Nginx基础笔记

压力测试工具:abab -n 请求数 -c 并发数 请求urlNginx:Nginx (engine x) 是一个高性能的HTTP和反向代理服务,也是一个IMAP/POP3/SMTP服务。特点:IO多路复用epoll轻量级CPU亲和(affinity):把每个worker进程固定在一个cpu上执行,减少切换cpu的cache miss,获得更好的性能sendfile:静态文件不经过用户空间,直接通过内核进行传输版本分类Mainline version 开发版(有更新的功能,但不一定稳定)Stable version 稳定版(经过测试,有更好的稳定性)Legacy version 历史版本命令nginx -V 查看nginx版本及编译的模块信息nginx -t -c .conf文件路径 检查配置文件语法是否正确nginx -s reload -c .conf文件路径 重启配置文件配置语法nginx.conf#运行用户user nobody;#启动进程,通常设置成和cpu的数量相等worker_processes 1; #全局错误日志及PID文件#error_log logs/error.log;#error_log logs/error.log notice;#error_log logs/error.log info; #pid logs/nginx.pid; #工作模式及连接数上限events { #epoll是多路复用IO(I/O Multiplexing)中的一种方式, #仅用于linux2.6以上内核,可以大大提高nginx的性能 use epoll; #单个后台worker process进程的最大并发链接数 worker_connections 1024; # 并发总数是 worker_processes 和 worker_connections 的乘积 # 即 max_clients = worker_processes * worker_connections # 在设置了反向代理的情况下,max_clients = worker_processes * worker_connections / 4 为什么 # 为什么上面反向代理要除以4,应该说是一个经验值 # 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 = 32000 # worker_connections 值的设置跟物理内存大小有关 # 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数 # 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的文件数大约是10万左右 # 我们来看看360M内存的VPS可以打开的文件句柄数是多少: # $ cat /proc/sys/fs/file-max # 输出 34336 # 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操作系统可以承受的范围之内 # 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置 # 使得并发总数小于操作系统可以打开的最大文件数目 # 其实质也就是根据主机的物理CPU和内存进行配置 # 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。 # ulimit -SHn 65535 } http { #设定mime类型,类型由mime.type文件定义 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; #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件, #对于普通应用,必须设为 on, #如果用来进行下载等应用磁盘IO重负载应用,可设置为 off, #以平衡磁盘与网络I/O处理速度,降低系统的uptime. sendfile on; #tcp_nopush on; #连接超时时间 #keepalive_timeout 0; keepalive_timeout 65; tcp_nodelay on; #开启gzip压缩 gzip on; gzip_disable “MSIE [1-6].”; #设定请求缓冲 client_header_buffer_size 128k; large_client_header_buffers 4 128k; #设定虚拟主机配置 server { #侦听80端口 listen 80; #定义使用 www.nginx.cn访问 server_name www.nginx.cn; #定义服务器的默认网站根目录位置 root html; #设定本虚拟主机的访问日志 access_log logs/nginx.access.log main; #默认请求 location / { #定义首页索引文件的名称 index index.html index.htm; } # 定义错误提示页面 error_page 500 502 503 504 /50x.html; location = /50x.html { } #静态文件,nginx自己处理 location ~ ^/(images|javascript|js|css|flash|media|static)/ { #过期30天,静态文件不怎么更新,过期可以设大一点, #如果频繁更新,则可以设置得小一点。 expires 30d; } #禁止访问 .htxxx 文件 location ~ /.ht { deny all; } }}日志类型error.log 格式:error_log log_file level; 错误日志,保存nginx运行中的错误信息access.log格式:access_log log_file log_format定义名称(固定输出格式); 访问日志,保存访问信息变量http请求变量:arg_PARAMETER http_HEADER(如:http_user_agent)sent_http_HEADER内置变量自定义变量常用模块http_sub_status_module nginx客户端的状态(可以配置在server和location下)stub_status;具体使用结果:https://blog.csdn.net/echizao…http_sub_module HTTP内容替换(http|server|location)sub_filter string(要替换的内容) replacement(替换的内容);sub_filter_once off;默认为on 默认匹配所有内容,on只匹配一次sub_filter_last_modified on;默认为off 判断服务器文件是否发生过变更,不变更则不重新加载http_random_index_module 目录中选择一个随机主页(配置在location中)random_index on;默认为offlimit_conn_module 连接频率限制limit_conn_zone key zone=name:size;(http)limit_conn zone number;(http|server|location)limit_req_modele 请求频率限制limit_req_zone key zone=name:size rate=rate;(http)如:limit_req_zone $binary_remote_addr zone=req_zone:1m rate=1r/s;limit_req zone=name burst=number [nodelay];(http\server\location)http_access_module 基于ip的控制访问(通过$remote_addr实现,代理模式就失去了作用,x-forword-for,geo模块,自定义变量解决)allow address | CIDR(网段) | unix: | all;(http\server\location\limit_except)deny address | CIDR | unix: | all;(http\server\location\limit_except)http_auth_basic_module 基于用户的信任登录(弊端解决:1结合lua实现高效验证,2利用nginx-auth-ldap模块,实现nginx和ldap打通)auth_basic string | off;默认off(http\server\location\limit_except);auth_basic_user_file file;(http\server\location\limit_except); file为一个用户名密码文件 file生成方式:htpasswd -c 文件名 用户名 然后就会让输入两次密码 命令在httpd-tools中 ...

February 16, 2019 · 2 min · jiezi

前端如何通过Nginx代理做到跨域访问API接口

跨域是指a页面想获取b页面资源,如果a、b页面的协议、域名、端口、子域名不同,或是a页面为ip地址,b页面为域名地址,所进行的访问行动都是跨域的,而浏览器为了安全问题一般都限制了跨域访问,也就是不允许跨域请求资源Nginx作为反向代理服务器,就是把http请求转发到另一个或者一些服务器上。通过把本地一个url前缀映射到要跨域访问的web服务器上,就可以实现跨域访问。对于浏览器来说,访问的就是同源服务器上的一个url。而nginx通过检测url前缀,把http请求转发到后面真实的物理服务器一.配置Nginx废话不多说,我们直接打开nginx.conf文件server { listen 8888; server_name 127.0.0.1; location / { proxy_pass http://127.0.0.1:5500; } location /api{ proxy_pass http://ip.taobao.com/; } }配置解释:我们在浏览器中输入 127.0.0.1:8888 自动会转发到 http://127.0.0.1:5500http://127.0.0.1:5500 是本地所指向的地址,类似于vue开的的代理npm run dev 启动的一个地址http://ip.taobao.com/ 是我们要访问的接口地址(淘宝检测ip地址来源的接口)前端ajax的url地址 这样写 http://127.0.0.1:8888/api/service/getIpInfo.php?ip=117.89.35.51,访问的url中有api nginx会自动换到所对应的location前端实列代码://新建一个html文件把以下代码放入script标签中$.ajax({ //请求淘宝检测ip地址来源的接口 url:‘http://127.0.0.1:8888/api/service/getIpInfo.php?ip=117.89.35.51’, type:‘get’, success:function(res){ console.log(res) }, error:function(err){ console.log(err) }})启动服务:我是通过vsCode的Go live插件启动了一个127.0.0.1:5500的服务,有很多同学是通过node开启的代理,都一样,然后我们在浏览器中输入127.0.0.1:8888上面nginx所配置打开浏览器network数据返回如下,说明跨域访问成功二.其它跨域解决方案1.jsonp 需要目标服务器配合一个callback函数。2.window.name+iframe 需要目标服务器响应window.name。3.html5的 postMessage+ifrme 这个也是需要目标服务器或者说是目标页面写一个postMessage,主要侧重于前端通讯。4.node.js开启本地代理,类似于vue-cli中的devServer模式,阔以方便开启代理5.CORS 需要服务器设置header :Access-Control-Allow-Origin。6.Nginx反向代理,可以不用目标服务器配合,需要Nginx服务器,用于请求转发。我个人认为4 、5 、6解决跨域问题在实际开发过程中显得更为重要三.Nginx工具以及参考资料Nginx在线配置生成工具(需要翻墙)如何提高Nginx的性能Nginx常用命令Nginx 配置简述(小胡子哥)

February 15, 2019 · 1 min · jiezi

Nginx 禁止某 IP 访问

导语总有一些不怀好意的人来访问我的网站,而且频率还很高,所以就用简单的方式禁止访问,就用 Nginx 来实现。创建黑名单在 /usr/local/nginx/conf 目录下创建 blocksip.conf 文件,如下格式输入禁止访问的 IPdeny 127.0.0.1;deny 127.0.0.1;修改配置文件接下来修改 nginx.conf 配置文件,添加 include blocksip.conf 语句,如下修改完成后记得 reload 。结语测试后是没有问题的。想要添加黑名单,只要在 blocksip.conf 中添加 IP ,然后 reload 即可。当然这个是最简单、效率低的方法,更高阶的方法可以查看下方参考资料的文章。参考资料:nginx 禁止某个IP访问、坏人还是有的(Nginx 拒绝指定IP访问)。

February 12, 2019 · 1 min · jiezi