聊一聊如何做监控——cgi监控

某产品: “哎呀,这个功能怎么用不了呀”;然后某前端和某后台开始定位问题,然后发现是后台接口返回错误,某前端:“没我啥事,虚惊一场 o_O”,后面如此往复,导致某前端不经感叹:“要是有能发现是否是后台接口问题的告警就好了,一目了然,再也不用一起折腾”。笔者可以开心的告诉某前端:这正是这篇文章的主题:如何做cgi监控。cgi监控的意义保证一个项目的质量,全面的测试时必要的;但是也不能保证万无一失,上线的时候也许某些因素出现各种状况。如果能快速发现问题,对于产品的可用性会大大提高,也可以增强用户体验。越快越精确的发现问题模块,不仅可以让开发者快速解决,也可以使产品不可用时间减短。基于以上的问题,做cgi监控是必不可少的,比如:某个兑换优惠券活动发现不能兑换,那么可能导致其购买的欲望降低。越晚发现问题,影响的用户数据越多。接入cgi监控后,开发者能够第一时间发现问题、解决问题,减少故障带来的损失。实现的基本思路适用范围: 在笔者的日常开发中发送请求是使用的ajax请求,其本质是调用 XMLHttpRequest 对象实现的,对 Fetch 由于兼容性,并未涉及。服务准备 : 需要有一个能进行监控上报并且发送通知(邮件、微信)等实现步骤:1.重写 XMLHttpRequest.open 方法获取当前 xhr 实例请求的 url 相关信息初始化当前请求的时间调用 XMLHttpRequest.open 原始open方法发送请求2.重写 XMLHttpRequest.onreadystatechange判定是否需要监听时上报的 url调用工具定义的 onreadystatechange 函数,进行接口上报调用开发者自定义的 onreadystatechange,实现业务逻辑3.重写 XMLHttpRequest.abort 上报相关请求中断的错误码具体代码实现src/index.js 入口文件,定义CgiAnalysisTool类供使用src/customXhr 实现相关 XMLHttpRequest函数重写src/cgiReport 上报相关函数处理src/util.js 获取请求 url、请求环境相关信息具体代码点击 查看

March 10, 2019 · 1 min · jiezi

CGI,FASTCGI,PHP-CGI,PHP-FPM 概念

CGI(Common Gateway InterFace)既然webserver想把这个请求交给可以处理的人来做,是不是该告诉人家一些基本的,并且人家看得懂的信息信息?Webserver会传那些基本信息给后端处理请求的解析器呢?url,get请求的query_string,POST数据,Http Header等,那么CGI就规定了要传哪些参数,以及以一个什么样的格式传过去,后端处理完这个请求,又该以什么样的格式传回。概念:CGI(Common Gateway Interface)全称是“通用网关接口”,WEB 服务器与PHP应用进行“交谈”的一种工具,其程序须运行在网络服务器上。CGI可以用任何一种语言编写,只要这种语言具有标准输入、输出和环境变量。也就是说,CGI用来在web服务器与后端实现了CGI协议的程序之间提供一个沟通的桥梁,降低两者之间的耦合性。缺陷:采用了效率低下的”FE”模式,就是Fork-And-Execute,每个web请求进来之后,都会启动一个php程序,处理完请求后退出,浪费资源,高请求数下,服务器宕机。FastCGI有没有效率更高的解决办法呢,可以不可一完事先别走呢?这就衍生出了FastCGI,也可以理解为一种协议,在CGI协议上进行强化,从原来的”FE”模式进化为”LL”模式,即Long-Live模式,运行完毕之后,常驻内存。同样,它也是语言无关性的,只要你具有标准输入输出和环境变量,那么就OK。FastCGI会先启动一个Master进程,Master读取和处理基础的配置文件,初始化环境,之后依据配置生成多个worker进程来处理请求。并且可以动态调整worker的数量。PHP-CGIFast-cgi听起来很不错,有没人用用它的呀?php-cgi是实现FastCGI协议的php解释器。缺陷:更改php.ini配置后需要重启php-cgi进程,且会杀死正在运行的进程。单进程,不具备进程管理能力,效率很低。PHP-FPM概念:一个实现FastCGI多进程(进程池)管理的管理器。开启之后,会看到多个php-cgi进程,它们都由php-fpm管理。一些进步:修改配置后,平滑过渡,新的worker进程使用新的配置,正在运行的worker进程处理完当前请求后自动shutdown。多进程的效率比单进程更高。转载来源:http://lib.csdn.net/article/p…

February 28, 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

深入了解SAPI

一、SAPI比较1. SAPI服务器应用程序编程接口,就是服务器与编程语言之间交互的接口。比如Linux命令行执行一段PHP代码,其实是Linux shell通过PHP SAPI传入一组参数,zend引擎执行后返回给shell。在PHP生命周期的各个阶段,一些与服务相关的操作都是通过SAPI接口实现。php_sapi_name()可以查看当前SAPI接口的类型。 如 cli(php -r “echo php_sapi_name();")、fpm-fcgi等2. PHP运行和加载的4个阶段①Minit 模块初始化阶段,可以初始化php扩展、类库的内部变量、注册常量,定义模块使用的类等。②Rinit 请求初始化阶段,在模块初始化并激活后,会创建PHP运行环境,初始化本次请求所需的环境变量,比如 $_SERVER,$_SESSION 。③Rshutdown 请求关闭阶段,执行最后的清理工作,释放所有处理本次请求的资源(申请的变量)。请求完成可能是执行到脚本完成,也可能是调用die()或exit()函数完成④Mshutdown 模块回收阶段,用于关闭自己的内核子系统,释放没存。3. SAPI 5种运行模式①单进程模式(CLI,CGI),每次执行PHP脚本,都会执行第二部分讲的四个INT和Shutdown事件。当用户请求数量非常多时,会大量挤占系统的资源如内存,CPU时间等,造成系统开销很大②多进程模式(Apache下的prefork MPM模式),会fork很多子进程,每个子进程拥有自己独立的进程地址空间,在一个子进程中,PHP的生命周期是调用MINT启动后,执行多次请求(RINT/RSHUTDOWN),在Apache关闭或进程结束后,才会调用MSHUTDOWN进行回收阶段。 多进程模型中,每个子进程都是独立运行,没有代码和数据共享,因此一个子进程终止退出和重新生成,不会影响其他子进程的稳定。③多线程模式(Apache2的Worker MPM),在一个进程下创建多个线程,在同一个进程地址空间执行④fastCGI模式,nginx+php-fpm就是这个模式,fast-cgi是CGI的升级版本,FastCGI可以看成是一个常驻型的CGI,它可以一直执行着,运行后可以fork多个进程,不用花费时间动态Fork子进程。也不需要每次请求都调用MINT/MSHUTDOWN。 ⑤内嵌模式,允许在C/C++语言中调用PHP提供的函数,运行模式和CGI一样,执行4个阶段二、php-fpm运行原理CGI:是个协议,服务器发起请求,传给PHP解析器,传递哪些数据,以什么格式,由CGI决定fastcgi:是个协议,提高CGI性能的,不用每次都去初始化,进程不够用,会预先启动几个进程,进程空闲太多了也会停掉一些,fastCGI对进程的管理,提高性能,节约了资源php-fpm:实现fastCGI协议的程序,被PHP官方收了,也提供了进程管理功能,进程包含 master 进程和 worker 进程两种进程。 master 进程只有一个,负责监听端口分发请求,接收来自 Web Server 的请求,而 worker 进程则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。php-cgi:cgi解释器进程FastCGI的工作原理:Web Server启动时载入FastCGI进程管理器FastCGI进程管理器自身初始化,启动多个CGI解释器(可见多个php-cgi)并等待来自Web Server的连接当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web Server将CGI环境变量和标准输入发送到FastCGI子进程php-cgiFastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告知处理完成。FastCGI子进程接着等待并处理来自FastCGI继承管理器的下一个连接使用FastCGI,系统开销小。另外,对于数据库和Memcache的持续连接可以工作。数据库短连接connect:请求关闭阶段,释放请求所用的资源,数据库连接句柄也会被释放 数据库长连接pconnect:请求关闭后,PHP会收留此次连接,即使主动关闭也不会关闭而是收留,下次有打开相同连接的请求时,PHP直接把收留的句柄拿出来,省去建立连接的过程。php-fpm实现长连接也需要配合数据库一些配置,一个进程收留一个连接,数据库连接的数量就是子进程数量,所以数据库允许连接数就要大于子进程数。三、php-fpm进程管理的三种模式php-fpm支持三种运行模式,分别为static、ondemand、dynamic,默认为dynamic 。static : 静态模式,启动时分配固定的worker进程。只需要考虑max_children的数量,数量取决于cpu的个数和应用的响应时间。ondemand: 按需分配,启动时不分配任何进程,当收到用户请求时才启动进程。 master进程检查work进程的数量是否受限,是否有空闲的work进程,没有就新建work进程。在大流量的系统上master进程会变得繁忙,占用系统cpu资源,不适合大流量环境的部署。dynamic: 动态模式,启动时分配固定的进程。伴随着请求数增加,在设定的浮动范围调整worker进程。 pm = dynamic //动态进程管理,对于专用服务器,可以设置为static,静态一次性启动最大子进程数,不会变化。 pm.max_children = 50 //最大子进程数,ps aux可以查看 pm.start_servers = 20 //启动服务时会启动的进程数 pm.min_spare_servers = 5 //保证空闲子进程数的最小值,如果空闲进程小于这个值,php-fpm服务会创建新的子进程。 pm.max_spare_servers = 35 //保证空闲子进程数的最大值,如果空闲进程高于这个值,就进行清理。 pm.max_requests = 500 //定义一个子进程最多处理的请求数,达到这个值,进程自动退出。目的是为了控制内存溢出,使内存在一个可控范围内。但是如果设置的很小,有可能多个进程同时达到这个值,同时重启,就会导致PHP停止响应直到重启完毕。设置为0表示一直接受请求。 四、php-fpm慢日志如果一个php网站可以访问,就是访问速度变慢了,可以通过php-fpm的慢执行日志,清晰的了解到php的脚本哪里执行时间长,它可以定位到具体的代码行 vim /usr/local/php/etc/php-fpm.d/www.conf request_slowlog_timeout = 1 //超时时间 slowlog = /usr/local/php/var/log/www-slow.log 重启php-fpm /etc/init.d/php-fpm reload我在php文件中加了一行sleep(3);,运行之后返回结果参考文献1、https://www.jianshu.com/p/c9a… php-fpm进程管理的三种模式2、https://www.jb51.net/article/… SAPI的5种运行模式3、http://blog.51cto.com/1260661… php-fpm慢日志4、https://www.cnblogs.com/wpjam… php-fpm与mysql长连接 ...

January 30, 2019 · 1 min · jiezi