作者归档:admin

活动运营总结

背景

沉默一年,所做活动过百,所见所想,所思所虑。
活动常规化,小组年活动量1000+
人力不足,需求变更多,资源到位时间不定,

基础建设

  • 活动运营,想要快速,就需要有一个强大的运营平台的支持。
    建设:运营平台作为基础建设,需要作为重点投入,在建设过程中,强化运营支撑系统 大系统小做,不停的迭代,专门的产品跟进,与使用者沟通,将系统的开发和活动的开发交替给不同的开发,以调节和深入。另,在人力方面需要确保运营系统的投入。
  • 可扩展性:从运营支持平台的业务框架层面,在适应现有业务发展的基础上增加代码的通用性,在一定程度上保证系统的可扩展性。
  • 监控告警:运营系统的告警不能成为常态,一是可能会形成告警疲劳,从而放过真正的问题,二是告警会影响日常的工作开展,此时如果确认常态的告警是问题,则解决问题,如果是告警系统本身的问题,则优化告警系统。

代码架构

  • 前端静态页面,资源文件,活动隔离,按日期归档,CDN加速。
  • 前端公用库版本升级,不同版本间清晰隔离,不做兼容,固化业务逻辑抽象为公用组件,实现业务的可配置化开发。
  • 数据和页面分离,行为和表现分离,前端和后端分离,后端固化业务,中转接口。
  • 轻重分离,将固化的业务以更高性能的服务实现,将变化较多的业务需求以脚本类CGI实现。

团队

  • 固化业务外包,需要考虑人员的稳定性问题,这是一个矛盾点,并且外包政策需要切合公司的大的政策方向,存在风险点。
  • 新业务主力人员承担,待固化后可以给新同学或相对较新的同学实现。
  • 人员流动问题:做活动是比较重复的工作,在一般的团队,活动是给新人熟悉流程,熟悉业务的,而当做活动是常规工作,不做活动才是调节时,人才的选用育留是比较严峻的问题,留住一到两个核心人员,招聘合适的稳定的人,发展新的业务促进人员的成长。然而,人的问题往往是最根本的问题,也是最难解决的问题。人员的流动会成为一个一种常态,业务特性决定的,能减缓。
  • 新人问题:新人进来,总会踩到各种各样的坑,不管是产品还是开发,如何规避?新人手册?新人FAQ?导师审核?通过流程?不管是哪种方式,最后成长并适应下来的都是需要经过时间的洗礼。
  • 人员的分工:业务的模块化,从而引发人对这些模块的分工,人与模块对应上。比如接入新游戏,专人负责接入过程,负责接入后的文档撰写

需求管理

  • 同时以产品的维度和开发的维度组织;
  • 强跟进,变更的需求及时汇总;
  • 关注需求中提到的资源,如重构,资源单,号码包等,这往往是风险所在;

流程

  • 发布流程(活动发布过程检测,人工确认最终结果) 免测发布,版本发布,应用场景,开发自测,产品体验,外团体验
  • 后台CGI大变更灰度发布,前端大变更以大版本发布
  • 如何固化流程,如何确保上线的活动的正常可用,现在还在依赖于人工的检查和验证

以上是一年积累,有所感,有所想,无太多逻辑,且行且珍惜,只愿岁月静好。

产品驱动技术,改变的是kpi,
技术驱动产品,改变的是世界
这只是一个开发的遐想而已。
现实让我们知道疼!

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语法结构的不要用函数,能使用内置函数的不要自己写,能用函数的不要用对象