分类目录归档:PHP

PHP源码,PHP扩展,PHP程序

技术管理者必备技能之解决问题的 3 个层次

作为一名技术管理者,面对日常工作中的各种问题和挑战,我们需要具备出色的问题解决能力。技术团队管理本身就是一项充满挑战的任务,而解决问题的能力更是推动团队向前的关键。当一个技术管理者拥有极强的解决问题的能力后,他大概能应对挑战、降低风险、提高团队绩效、增强领导力并提升个人职业发展。

在解决问题的过程中,我们可以将问题分为三个层次。了解这三个层次将帮助我们更好地应对不同场景下的问题,成为更优秀的技术管理者。

1 应急响应类

应急响应类问题的处理是通过快速反应和短期改进措施来修复问题的反应性流程。其主要作用是停止损害,防止蔓延

应急响应通常是一种反应式行为,并不研究根本性的问题以及背后的原因。应急响应不会导致理想状态的实现,但是仍然可以满足即时需求、保护客户,为更加深入地挖掘和调查重要细节赢得宝贵时间。有效的应急响应有助于企业产品获得更好的稳定性。

应急响应类问题涉及到系统或产品出现紧急故障时,需要立即采取行动以避免进一步影响。这种解决方式专注于快速应对问题,暂时稳定状况,但可能不会深入探讨问题的根本原因。例如,服务器宕机导致网站无法访问,技术管理者需要立即组织团队快速评估问题的严重性,制定并实施紧急应对措施,进行故障排查,找出问题根源并进行修复,以保障系统正常运行。

针对此类问题,常规处理流程如下:

  1. 确认问题:在故障发生时,第一步是确认问题的具体表现和影响范围。
  2. 快速定位:尽快找到故障发生的关键环节或设备。
  3. 制定应急措施:为了防止问题扩大,制定临时应对措施,如切换备用设备或临时修复。
  4. 实施解决方案:采取相应的技术手段和方法解决故障。
  5. 验证修复:确认故障已被解决,系统恢复正常运行。
  6. 总结复盘:分析故障原因,制定预防措施,避免类似问题再次发生。

以某个互联网产品的后台服务出现异常,导致用户无法正常登录。为解决该问题,我们可以采取以下步骤:

  1. 确认问题:收集用户反馈,查看日志和监控数据,确定问题的具体表现和影响范围。
  2. 快速定位:分析日志和监控数据,找出异常发生的关键服务或代码模块。
  3. 制定应急措施:为防止问题扩大,可以临时限制新用户注册,或者启用备用服务器等。
  4. 实施解决方案:针对定位到的问题,进行相应的代码修复或配置调整,然后重新部署服务。
  5. 验证修复:测试修复后的服务,确认用户可以正常登录,系统恢复正常运行。
  6. 总结复盘:分析故障原因,制定预防措施,优化代码质量和监控预警机制,避免类似问题再次发生。

在职场中,经常会出遇到此类的问题,一个技术管理者也经常需要作为发言人去回复此类问题,可能是对业务方或者上级等等。一般我们回复此类问题可以按以下逻辑来讲:

  1. 问题描述:首先,简洁明了地描述问题的现象,包括故障发生的时间、影响范围以及涉及的系统或模块。
  2. 原因分析:接下来,阐述经过团队排查后发现的问题根源,以及问题产生的原因。
  3. 解决措施:说明已经采取的解决措施以及恢复情况,包括故障处理时间以及目前系统恢复的程度。
  4. 防范措施:提出针对此次故障,团队将采取哪些预防措施,以避免类似问题再次发生。
  5. 跟进计划:最后,描述团队将如何跟进并持续关注问题的后续处理,以确保问题得到妥善解决。

示例:

问题描述:今日上午 10:00,我们的网站出现了访问故障,影响了所有用户对网站内容的访问。
原因分析: 经过团队紧急排查,我们发现问题出在流量爆涨,导致服务器负载过高,从而让部分服务无法正常响应用户请求。
解决措施: 我们迅速扩展了服务器资源,同时优化了负载均衡策略。截止目前,网站访问已恢复正常,全部用户可以正常访问。
防范措施:为防止类似问题再次发生,我们将加强服务器负载监控,提前预警潜在风险。同时,我们将对现有负载均衡策略进行评估和优化,确保系统稳定性。
跟进计划:我们将在未来一周内密切关注网站运行状况,并定期向您汇报服务器性能数据。如有任何问题,请随时联系我们。

2 深度分析类

深度分析类问题和应急响应类问题相比有一个不同点,在于速度。应急响应类的方式以一种快速而急切的方式处理紧急问题,深度分析的方式则遵循更加严谨的结构,通常包括数据收集、多方分析和深度研究,可能需要以一种更科学的方式花费几小时、几天、几周,甚至更长时间来完成。

深度分析类不会每次出现问题就触发,仅在以下场景下发生:

  1. 重复发生的问题。
  2. 对安全、质量、交付、成本、士气、生产率或者其他关键绩效指标产生负面影响,且不知道根本原因与解决方案的任何问题。

深度分析类问题的解决是通过确定一个明确的目标,以及与之对应的衡量和管理流程来实现的。深度分析类的问题解决是重复的,直到人们清楚地了解问题,解决问题,并且防止问题再次出现为止。

针对此类问题,常规处理步骤如下:

  1. 界定问题:在这个阶段,技术管理者需要充分了解问题的背景,并使用事实和数据来描述现状与期望标准之间的差距。这包括明确问题的目的、范围、影响和紧迫性。问题描述应遵循 SMART 原则(具体、可衡量、可实现、相关、时限)。
  2. 分解问题:分解问题是第一步的延续,但是更加细化,为了更好地理解问题,技术管理者需要将问题分解成更小的部分。可以采用逻辑树、鱼骨图等工具来实现问题的分解。在分解过程中,应确保各部分之间的关系符合 MECE 原则(互斥且完全穷尽)。然后,针对每个子问题进行深入的分析、量化和细化。
  3. 建立目标和成功判断:在明确了问题的具体表现和原因后,技术管理者需要设定一个清晰的目标,以便于团队集中精力解决主要问题。目标应具有明确的完成标准和时间节点,并遵循 SMART 原则。此外,管理者还需要确保目标与公司战略目标保持一致。同时,为了衡量解决方案的成功程度,需要确定一些关键成功指标。
  4. 根因分析:根因分析是指根本原因分析,技术管理者需要深入挖掘问题的根本原因,以便于制定针对性的解决方案。可以采用 5W、因果图等工具来进行根本原因分析。找到根本原因后,管理者需要验证这些原因,确保它们是问题产生的关键因素。
  5. 制定解决方案:根据根因分析的结果,技术管理者需要制定相应的解决方案。解决方案是能够防止问题再次发生的应对措施,并不是指实施你感觉正确或者你希望奏效的行动。对于任何实施措施而言,能否防止问题再次发生和达成前几个步骤所确定的目标,是验证解决方案是否有效的主要检查点。解决方案应具有可行性和可持续性,以确保长期效果。同时,解决方案应具有创新性,以提高团队的问题解决能力。
  6. 里程碑:为了确保解决方案的实施过程井然有序,技术管理者需要设定一系列里程碑。每个里程碑都应与特定的任务或目标相关联,有助于监控项目进度和实现预期结果。里程碑除了监控项目进度,还有一个作用是对外或对上的汇报,以大的时间节点同步项目的进展
  7. 工作计划:在这个阶段,技术管理者需要为团队制定详细的工作计划,包括任务分配、时间表和预期结果。工作计划应确保各个团队成员清楚自己的职责和期望,以提高执行效率。同时,管理者需要与团队成员保持密切沟通,确保计划的实施过程中能够及时调整和改进。在工作计划中预期结果一定要体现必须的交付物,让预期结果是有能落地的点。
  8. 风险判断:在实施解决方案的过程中,技术管理者需要关注可能出现的风险和问题。这包括对潜在风险进行识别、评估和分类,以便于采取适当的预防和应对措施。管理者应与团队成员共同讨论可能的风险,制定风险应对策略,确保项目的顺利进行。
  9. 未来改进:问题解决并非一次性事件,而是一个持续的过程。在解决方案实施后,技术管理者需要关注其效果,并根据实际情况进行调整和改进。同时,管理者还应从这个过程中总结经验教训,为未来解决类似问题提供借鉴。

通过以上九个步骤,技术管理者可以结构化地解决复杂问题,提高团队的问题解决能力。这种方法论强调了问题的分析和解决过程的重要性,有助于确保解决方案的有效性和可持续性。

这九个步骤可以作为深度分析类问题的规划方案文档的一级标题。

3 追求卓越类

追求卓越类问题和深度分析类问题相比,通常都以检查关键指标开始,但是有一些差别。 深度分析类是要对趋势显示出来的与已设定目标的差距进行反应,而追求卓越类的这种机制则是通过建立新的、更具挑战性的未来状态而主动发起。

深度分析类问题解决方式聚焦在澄清问题及其直接原因上,要尽可能明确和具体。其思维和流程在本质上是调查性的,通过发现与标准之间的偏差,并将关键项目恢复到正常工作状况,围绕着恢复到已知标准或者之前的绩效水平而展开。深度分析类的思维接受现有标准

相比之下,追求卓越类思维会从根本上对现状提出质疑:「理想状态是怎样的,有没有更好的状态,或者应该是怎样的?」。刚开始的时候你可能没有明确的答案,你必须构想一种改进后的目标状态或未来状态。在聚焦到具体明确的个体问题之前,追求卓越类的问题解决者要拓宽思维宽度,去思考多个备选状态和路径以实现构想。

针对此类问题,常规处理步骤如下:

  1. 背景:列出受众和参与者可能需要知道的信息。提供项目的背景信息,例如组织环境、历史、市场情况等。确保所有相关人员对项目有充分了解,为后续步骤打下坚实基础。
  2. 现状定义:以图表等可视化的方法描述现状,让受众能更好地接收信息。例如,绘制价值流图,展示当前流程的关键环节、瓶颈和效率。通过直观地呈现现状,帮助团队成员更好地理解问题所在。
  3. 现状分析:全面地检验不同要素的改善潜力,比如前置时间、服务、绩效、成本和特性等。运用数据分析、用户反馈和内部评估等手段,找出现有流程中可以改进的地方。
  4. 设定目标:明确要在什么时候完成什么,并确定改善的具体水平。设定有挑战性且可实现的目标,为后续改进提供方向。
  5. 目标状态的定义:可视化地展示改进后的新状态,通过想象图、流程图或数据等方式,形象地呈现预期的目标状态。这有助于团队成员清晰地了解改进的方向和目的。
  6. 制定执行计划:列出具体的细节,比如姓名、责任、日期和预期产出结果等。明确具体的细节,确保团队成员清楚自己的职责和期望。如果需要,可以将执行计划与其他项目计划相结合进行管理。
  7. 检查结果:检查改进后的绩效水平是否达到预期。通过定期评估和数据分析,了解实施情况及效果,确保改进措施取得实际成果。
  8. 跟进与标准化:制定行动清单,确保改进结果在长期运行中是可维持的。对改进措施进行持续跟踪,评估其有效性,确保新的标准在组织内得到广泛应用和推广。

以一个互联网 SaaS 产品在高峰时段用户体验下降,页面加载速度变慢为例来描述整个过程:

  1. 背景:我们的 SaaS 产品面向企业客户,提供在线办公协作功能。近期我们发现,用户在高峰时段访问产品时,页面加载速度减慢,影响了用户体验。
  2. 现状定义:通过监控系统收集数据,绘制访问速度和用户活跃度随时间变化的图表。在图表中标注高峰时段,突出问题所在。
  3. 现状分析:分析服务器资源、带宽、前端优化等多个方面的因素,找出可能导致页面加载速度变慢的原因。例如,检查服务器响应时间、CDN 服务情况、代码优化等。
  4. 设定目标:在高峰时段将页面加载速度提高到行业标准水平。为实现这一目标,我们将设定一个合理的实施时间,例如 3 个月。
  5. 目标状态的定义:绘制改进后的访问速度和用户活跃度随时间变化的图表,展示目标状态。同时,列出在服务器资源、带宽和前端优化等方面需要达到的具体指标。
  6. 制定执行计划:为实现目标状态,我们需要分配任务给团队成员。例如:张三负责服务器资源优化,如升级硬件、调整负载均衡策略等。李四负责带宽和 CDN 服务调整,以确保高峰时段能应对流量需求。王五负责前端优化,如代码压缩、图片资源优化等。
  7. 检查结果:在实施改进措施后,持续监控页面加载速度和用户活跃度。通过数据分析,检查改进后的绩效水平是否达到预期。
  8. 跟进与标准化:为确保改进效果的持久性,我们需要:对实施过程进行总结,提炼经验教训。将改进措施纳入团队的日常工作流程,确保新的标准得到长期执行。定期回顾和评估改进效果,以便在未来进一步优化。

4 小结

本文主要探讨了技术管理者在应对日常工作中不同类型问题时,如何运用有效的问题解决能力来提升团队绩效。文章将问题分为三个层次:应急响应类、深度分析类和追求卓越类。对于应急响应类问题,例如服务器宕机等紧急故障,技术管理者需迅速评估并实施紧急应对措施。深度分析类问题则需要更加严谨和系统的方法,如面对重复发生或对关键绩效指标产生负面影响的问题,技术管理者要深入挖掘根本原因并防止问题再次出现。而在追求卓越类问题解决过程中,技术管理者需要勇于挑战现状,设定更具挑战性的未来目标,从而实现技术团队的持续进步。

通过了解这三个层次的问题解决方式,技术管理者能更加从容应对各种问题和挑战,为团队创造一个更高效、卓越的技术环境,推动团队不断向前发展。

PHP的压缩函数实现:gzencode、gzdeflate和gzcompress

  • gzencode 默认使用ZLIB_ENCODING_GZIP编码,使用gzip压缩格式,实际上是使用defalte 算法压缩数据,然后加上文件头和adler32校验
  • gzdeflate 默认使用ZLIB_ENCODING_RAW编码方式,使用deflate数据压缩算法,实际上是先用 LZ77 压缩,然后用霍夫曼编码压缩
  • gzcompress ;默认使用ZLIB_ENCODING_DEFLATE编码,使用zlib压缩格式,实际上是用 deflate 压缩数据,然后加上 zlib 头和 CRC 校验
  • 这三个函数的比较实质上是三种压缩方法:deflate, zlib, gzip的比较。
    从性能的维度看:deflate 好于 gzip 好于 zlib
    从文本文件默认压缩率压缩后体积的维度看:deflate 好于 zlib 好于 gzip

    这三种算法中gzip 、zlib的作者都是Jean-Loup Gailly和 Mark Adler。
    这两种算法以及图形格式png,使用的压缩算法却都是deflate算法。
    deflate算法是同时使用了LZ77算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法。
    它最初是由Phil Katz为他的PKZIP归档工具第二版所定义的,后来定义在 RFC 1951规范中。

    deflate算法的压缩与解压的实现过程可以在压缩库zlib上找到。
    PHP的压缩实现依赖于zlib,zlib是一个提供了 deflate, zlib, gzip 压缩方法的函数库。
    我们所使用的上面三个函数,将参数中的encoding转为相同,压缩率设置相同,则其最终调用的是同一个函数,效果和性能一样。

    PHP的zlib实现是以扩展的方式存在于ext/zlib目录中。通过deflateInit2() + deflate() + deflateEnd()三个函数配合完成压缩功能,inflateInit2() + inflate() + inflateEnd()三个函数配合完成解压功能。压缩最终都是通过php_zlib_encode函数实现调用,除了输入的字符串,压缩率,结果的输出外,不同的入口函数调用参数不同的是其encoding。deflateInit2的第四个参数指定encoding,PHP定义了三个常量:

     #define PHP_ZLIB_ENCODING_RAW          -0xf      //deflate -15
    #define PHP_ZLIB_ENCODING_GZIP          0x1f      //gzip 15 + 16
    #define PHP_ZLIB_ENCODING_DEFLATE     0x0f      // zlib 15

    三个函数在调用过程可以直接指定encoding使用其它的算法:

    zlib:   ZLIB_ENCODING_DEFLATE 
    gzip: ZLIB_ENCODING_GZIP
    deflate: ZLIB_ENCODING_RAW

    此三个函数是三种算法的简单调用方式,以更好的命名展现。三个函数间可以通过指定相同的encoding达到相同的效果,并且PHP也提供zlib_encode函数作为通用的压缩函数。

    参考资料:

    http://www.gzip.org/zlib/rfc-deflate.html

PHP成员变量获取对比

有如下4个代码示例,你认为他们创建对象,并获得成员变量的速度排序是怎样的?

1:将成员变量设置为public,通过赋值操作给成员变量赋值,直接获取变量

	class Foo {
		public $id;
	}
 
	$data = new Foo;
	$data->id = 10;
	echo $data->id;

2:将成员变量设置为public,通过构造函数设置成员变量的值,直接获取变量

        class Foo2 {
		public $id;
		public function __construct($id) {
			$this->id = $id;
		}
	}
 
	$data = new Foo2(10);
	echo $data->id;

3:将成员变量设置为protected,通过构造函数设置成员变量的值,通过成员方法获取变量

 
     class Foo3 {
		protected $id;
		public function __construct($id) {
			$this->id = $id;
		}
 
		public function getId() {
			return $this->id;
		}
	}
	$data = new Foo3(10);
	echo $data->getId();

4:将成员变量设置为protected,通过构造函数设置成员变量的值,通过魔术方法获取变量

 
     class Foo4 {
		protected $id;
		public function __construct($id) {
			$this->id = $id;
		}
 
		public function __get($key) {
			return $this->id;
		}
	}
	$data = new Foo4(10);
	echo $data->id;

按执行速度快慢排序: 1243
咱们先看其opcode:

1:

   	1  ZEND_FETCH_CLASS	4 	:4 	'Foo'
	2  NEW      			$5	:4
	3  DO_FCALL_BY_NAME			0          
	4  ASSIGN     				!0, $5
	5  ZEND_ASSIGN_OBJ			!0, 'id'
	6  ZEND_OP_DATA				10
	7  FETCH_OBJ_R			$9	!0, 'id'
	8  ECHO        				$9

2:

	1  ZEND_FETCH_CLASS	4 	:10	'Foo2'
	2  NEW             		$11	:10
	3  SEND_VAL        			10
	4  DO_FCALL_BY_NAME		1 
	5  ASSIGN    				!1, $11
	6  FETCH_OBJ_R			$14	!1, 'id'
	7  ECHO        				$14

3:

	1  ZEND_FETCH_CLASS	4 	:15	'Foo3'
	2  NEW         			$16	:15
	3  SEND_VAL     			10
	4  DO_FCALL_BY_NAME			1          
	5  ASSIGN  	   			!2, $16
	6  ZEND_INIT_METHOD_CALL	!2, 'getId'
	7  DO_FCALL_BY_NAME		0 	$20     
	8  ECHO       				$20

4:

	1  ZEND_FETCH_CLASS	4  :21	'Foo4'
	2  NEW          		$22	:21
	3  END_VAL      			10
	4  DO_FCALL_BY_NAME		1          
	5  ASSIGN        			!3, $22
	6  FETCH_OBJ_R  		$25 !3, 'id'
	7   ECHO  				$25

根据上面的opcode,参照其在zend_vm_execute.h文件对应的opcode实现,我们可以发现什么?

一、PHP内核创建对象的过程分为三步:

  1. ZEND_FETCH_CLASS 根据类名获取存储类的变量,其实现为一个hashtalbe EG(class_table) 的查找操作
  2. NEW 初始化对象,将EX(call)->fbc指向构造函数指针。
  3. 调用构造函数,其调用和其它的函数调用是一样,都是调用zend_do_fcall_common_helper_SPEC

二、魔术方法的调用是通过条件触发的,并不是直接调用,如我们示例中的成员变量id的获取(zend_std_read_property),其步骤为:

  1. 获取对象的属性,如果存在,转第二步;如果没有相关属性,转第三步
  2. 从对象的properties查找是否存在与名称对应的属性存在,如果存在返回结果,如果不存在,转第三步
  3. 如果存在__get魔术方法,则调用此方法获取变量,如果不存在,报错

回到排序的问题:

一、第一个和第二个的区别是什么?

第二个的opcode比第一个要少,反而比第一个要慢一些,因为构造函数多了参数,多了一个参数处理的opcode。参数处理是一个比较费时的操作,当我们在做代码优化时,一些不必要的参数能去掉就去掉;当一个函数有多个参数时,可以考虑通过一个数组将其封装后传递进来。

二、为啥第三个最慢?

因为其获取参数其本质上是一次对象成员方法的调用,方法的调用成本高于变量的获取

三、为啥第四个比第三个要快?

因为第四个的操作实质上获取变量,只不过其内部实现了魔术方法的调用,相对于用户定义的方法,内部函数的调用的效率会高。因此,当我们有一些PHP内核实现的方法可以调用时就不要重复发明轮子了。

四、为啥第四个比第二个要慢?

因为在PHP的对象获取变量的过程中,当成员变量在类的定义不在在时,会去调用PHP特有的魔术方法__get,多了一次魔术方法的调用。

总结一下:

  1. 使用PHP内置函数
  2. 并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。
  3. 尽量少用魔术方法 — 除非有必要,不要用框架,因为框架都有大量的魔术方法使用。
  4. 在性能优先的应用场景中,将成员变量设置为public,不失为一种比较好的方法,当你需要用到OOP时。
  5. 能使用PHP语法结构的不要用函数,能使用内置函数的不要自己写,能用函数的不要用对象