作者归档:admin

胖子的乱看乱想笔记四

1、当说某一件事没有时间做时,表示你认为他不重要,或者有更重要的事要做

2、并不是所有值得一做的事情都值得认真去做 - 《新机器的灵魂》

3、每颗珍珠原本都是一粒沙子,但并不是每一粒沙子都能成为一颗珍珠。 - 《毕业5年决定你的一生》

4、不在于你知道多少,而在于你不知道自己不知道多少 - 鸟哥

5、真正聪明的人宁愿让人们需要,也不要让人们感激。﹣《历史中的阴谋阳谋》

6、以大多数人的努力程度之低,根本轮不到去拼天赋。 - 网络

7、责权利对等

8、KISS,小而美,一次只做一件事,一次做好!

9、对外发布的接口等都需要建立版本的概念

10、当发布的内容包括客户端和服务端,并且客户端与服务端之间有交互时,测试需要覆盖到交互的流量和频率。

11、三角形比直线会稳定,当然三角恋不算。

12、当一个主从搞不定时,那就再加一个主从吧,可能是树形也可能还是直线。

13、只有剂量能决定一东西没有毒 ﹣ 帕拉斯尔萨斯

14、在URI中避免出现文件扩展名 - 《RESTful Web Services Cookbook》

15、大多数情况下,创建URI的过程属于服务器,而非客户端。 - 《RESTful Web Services Cookbook》

16、商品化意味着美学品位的竞争。﹣ 程序员的思维修炼

17、”我不知道 “是一个好答案,但不要就此止步。﹣ 程序员的思维修炼

18、主从数据库,主就是主,从就是从。

19、问下自己,这个项目/需求/任务能解决什么问题,解决问题后能带来什么好处。

20、冗余不做,日子甭过!备份不做,十恶不赦! - ZQ大妈的签名,正在被现实深刻教育中。

21、目光聚集的地方,金钱必将随之而来 ﹣ ≪技术元素≫

22、决策靠定量分析,将词语和它的所指分离,凡事没有好坏之分但是有积极和消极的态度之分,找到自己的优势然后尽全力放大它,尽量完整自身的知识体系,对习惯思维勇敢的质疑,物极必反,不受他人评价的影响,欲速则不达,承认自己在大部分方面无能。- 天下网商许维

23、架构可以看作是战略上的设计,而详细设计可以看作是战术上的设计﹣ ≪架构实践≫

24、这个世界上有很多人的身份和称谓,都是在描述“未来的自己”,而不是现在的自己。- meditic

Apache源码阅读笔记一:Content-MD5字段

Apache源码阅读笔记一:Content-MD5字段

通常在提供下载服务时,服务器都会预先提供一个MD5校验和,用户下载完文件以后,用MD5算法计算下载文件的MD5校验和,然后通过检查这两个校验和是否一致,就能判断下载的文件是否出错。而Content-MD5是HTTP协议中一个有类似功能的字段。

Content-MD5在RFC2616中的说明是用来提供实体主体(entity-body)的 MD5 摘要(digest),为的是提供 end-to-end 消息完整性检测(MIC,可以用来检测实体主体传输过程中的偶然性变动,但不一定能防范恶意攻击)。只有源服务器或客户端可生成 Content-MD5 头域;不得由代理和网关生成,否则会有悖于其作为端到端完整性检验的价值。

任何实体主体的接收者,包括代理和网关,都可以检查此头域里的摘要值与接收到的实体主体的摘要值是否相符。但是这个字段不能保证消息没有被篡改,所以不要将它作为一种安全手段,修改正文的人同样可以修改字段的内容。

在 Apache 中我们可以通过设置 ContentDigest On 打开 Content-MD5 的输出,详细说明猛击这里

那么,在 Apache 中是如何通过设置 ContentDigest 来开启 Content-MD5 字段的输出,此字段生成的算法是怎样的?

控制 Content-MD5 字段

前面我们有说过在配置文件中增加 ContentDigest On 可以打开 Apache 的 Content-MD5 的输出,在 Apache 内核中这个配置项是如何加载的?在生成内容时是根据哪些变量控制 Content-MD5 的输出?

我们知道 Apache 的模块中有一种叫预加载模块。这些模块是 Apache 运行非常重要的模块,我们今天所说的 Content-MD5 字段就包含这些预加载模块中的一个模块 core_module 中。虽然 Apache 针对不同的操作系统有不同的预加载模块列表,但是 core_module 都会作为第一个加载的模块放在列表的最前面。

Apache 在通过 ap_setup_prelinked_modules 加载完这些预加载的模块后,其运行的基本条件已经具备。在各种池初始化后, Apache 会进行配置文件解析,并针对配置文件中每一个有效项进行循环遍历,判断这些配置项与现有模块的指令(directive)是否匹配,如果匹配并且其参数设置为在读取配置时执行(EXEC_ON_READ,Content-MD5的此参数设置为OR_OPTIONS),则执行此字段的执行函数并将此项添加到配置项的指令集中。如果没有匹配,则直接将此节点添加到指令集中。

待所有的参数加载完后,Apache 内核会执行配置树(ap_process_config_tree)的所有执行函数。根据不同的指令参数,Apache 会调用每个指令的func(不同的参数使用不同的宏,虽然现在最终都是调用 func)。回到我们关注的内容,Content-MD5字段对应的是 set_content_md5 。此函数去掉验证输入,错误处理,就剩下一句:

 
 d->content_md5 = arg != 0;

这里的arg就是我们在配置文件中的 Off 和 On,Off的值为0, On的值为1。即当 ContentDigest On 时,d->content_md5的值为1。

生成Content-MD5字段的内容

前面有说提到 Content-MD5 字段最终是由 d->content_md5 控制。除此之外,此参数的输出还与输出过滤器相关,如果输出的过滤类型不是 AP_FTYPE_RESOURCE,则不会输出 Content-MD5 字段。

如果真输出 Content-MD5 字段,则 Apache 内核会调用 ap_md5digest(/server/util_md5.c文件) 函数。 Apache 实现的 MD5 算法与标准的算法步骤有一些出入。标准算法是按照如下5个步骤进行:

  1. Append Padding Bits: 信息计算前先要进行位补位
  2. Append Length
  3. Initialize MD Buffer: 用一个四个字的缓冲器(A,B,C,D)来计算报文摘要,A,B,C,D分别是32位的寄存器,初始化使用的是十六进制表示的数字。
  4. Process Message in 16-Word Blocks
  5. Output: 报文摘要的产生后的形式为:A,B,C,D。也就是低位字节A开始,高位字节D结束。

因为在大多数情况下我们都无法或很难提前计算出输入信息的长度。因此在具体实现时Append Padding Bits和Append Length这两步会放在后面,如下代码:

 
    AP_DECLARE(char *) ap_md5digest(apr_pool_t *p, apr_file_t *infile)
    {
        apr_md5_ctx_t context;
        unsigned char buf[4096]; /* keep this a multiple of 64 */
        apr_size_t nbytes;
        apr_off_t offset = 0L;
 
        apr_md5_init(&context);
        nbytes = sizeof(buf);
        while (apr_file_read(infile, buf, &nbytes) == APR_SUCCESS) {
            apr_md5_update(&context, buf, nbytes);
            nbytes = sizeof(buf);
        }
        apr_file_seek(infile, APR_SET, &offset);
        return ap_md5contextTo64(p, &context);
    }

apr_md5_init函数执行标准算法的第三步,初始化MD缓存,而标准算法的第一步、第二步和第四步都在 apr_md5_update 中体现。最后一步输出对应 ap_md5contextTo64 。

关于MD5算法的详细算法在后续的文章中介绍。

PHP脚本运行超时管理机制

PHP脚本运行超时管理机制

在我们平常的开发中,也许曾经都遇到过PHP脚本运行超时的情况,此时PHP会显示错误说: “Fatal error: Maximum execution time of XXX seconds exceeded in XXX”,并终止脚本的运行。当遇到这种情况我们经常会通过使用 set_time_limit(非安全模式),或修改配置文件并重启服务器,或者修改程序减少程序的执行时间,使其在允许的范围之内,以解决此问题。但是,这些都是在应用层上我们可以看到的的表象,在PHP内核中有一套这样的机制支撑这样一个表象。

这是PHP为防止某些业务脚本长时间执行而阻塞其它脚本的处理或耗尽服务器资源,从而实现的脚本运行的超时管理机制。其本质上是PHP通过针对不同的平台实现定时器,依赖运行时的超时全局变量(EG(timeout_seconds))管理并控制定时器的运行。所有对脚本运行时长的管理,包括接口函数和配置文件对于最大运行时长的配置,最终都是通过管理超时全局变量并重启定时器来实现的。

初始化和超时配置项

在PHP内核的核心层文件/main/main.c文件中,定义了PHP的核心配置项以及每个配置项对应的on_modify方法。在模块初始化(php_module_startup)时,PHP内核会调用ini配置的注册函数,将定义的核心配置项添加到ini配置的指令集中,并且会调用每个配置项对应的on_modify方法。

用于定义脚本运行最长时间的max_execution_time配置项也是这些核心配置项的一员,它的默认值为30秒,对应的on_modify方法是OnUpdateTimeout。当注册这些核心配置项时,max_execution_time的on_modify方法将被调用,此时配置项的值将传递给超时全局变量:EG(timeout_seconds),并通过zend_set_timeout方法启动定时器。

针对WIN平台和类unix平台,PHP内核实现了不同的定时器。 Win32平台的定时器是在WM_TIME的基础上封装了一个计时器。通过创建一个独立线程控制计时器,并创建一个消息环,WaitForSingleObject用来阻塞zend_init_timeout_thread 返回。当接收到WM_REGISTER_ZEND_TIMEOUT时开始计时,实际上此时计时的任务是SetTimer(timeout_window, wParam, lParam*1000, NULL); 系统会在 seconds * 1000 后发个 WM_TIMER,这个时候就结束计时,中间可以被 WM_UNREGISTER_ZEND_TIMEOUT 打断。

类unix平台使用Linux的API函数setitimer,指定SIGPROF信号为超时处理信号,对应超时处理函数zend_timeout,当发生超时时,会发送此信号并触发函数zend_timeout显示错误信息并中止程序。

如果需要取消定时器,Win平台通过PostThreadMessage发送WM_UNREGISTER_ZEND_TIMEOUT给线程即可,类unix平台会重置定时器的时长为0。

超时管理

超时机制的管理非常灵活,有三种修改运行时长的方法。

1、 修改配置项。默认情况下PHP脚本的最长运行时长为30s。如果需要调整此项,可以通过修改php.ini文件中的max_execution_time项并重启动服务器达到修改最长运行时长的目的。此种方法适用于最开始的默认配置修改,或在其它方法无效的情况下使用。

2、 使用set_time_limit接口函数。此函数的作用是设置脚本最大执行时间。当此函数被调用时,set_time_limit()会从零开始重新启动超时计数器。比如,每一次设置是5秒,待脚本运行4秒后,脚本中又设置了5秒,那么,脚本在超时之前可运行总共时间为10秒。如下脚本示例:

    <?php
    set_time_limit(5);
    for ($i = 0; $i < 4; $i++) {
        sleep(1);
        echo $i, "<br />";
    }
 
    set_time_limit(5);
 
    for ($i = 0; $i < 4; $i++) {
        sleep(1);
        echo $i, "<br />";
    }

如上的代码,程序会执行完两个循环,都输出0,1,2,3。如果我们注释掉中间的set_time_limit(5),程序再运行一次,此时就会在第二个循环输出0后报错。

在安全模式下,无法通过set_time_limit和ini_set重新设置max_execution_time,只有关闭安全模式或改变php.ini中的时间限制才能达到修改此参数的目的。

3、 通过ini_set修改max_execution_time参数。

以上的三种方法,其实现过程基本类似,前一种是在初始化时调用on_modify指针函数。后两种在处理了参数后,调用zend_alter_ini_entry_ex函数,触发on_modify函数。于是,管理超时机制的所有操作最终都汇集到OnUpdateTimeout函数。在此函数中,通过zend_set_timeout重新设置脚本的超时时间。