开发部署上线流程

袁亮,15:37 2014-2-13,开发部署上线

说明:
1、本流程主要适用于工作一年内的新人,半个小时内就能搞定的,可以不用按完整的流程来,其他的都需要遵守
2、时间不是太急的情况下,老员工也尽量按该流程来,以降低风险
3、另外,每个程序员都是这么一路犯错下来的,你的水平基本上跟你犯过的错误是成正比的(同一个问题一犯再犯那就另说),所以我们需要学会犯错之后学会去避免它,并且善于从别人的错误教训中吸取经验。

一、需求确定
1、开发之前,必须要明确本次开发的目的,以及实现的方法
a、每个人提出需求的时候,一般都会根据自己的理解、现有的水平,给出相应的解决方案,至于这个方案好还是不好,那就很难说了,所以开发之前,涉及到的相关人员必须要清楚,这次做的根本原因是什么?有没有更好的方法?
b、需求的确定决定了这次开发是否能顺利完成的80%,很多时候,一个很小的改动,可能会减少你们50%以上的工作量。

2、需求的分期完成
a、我们有太多的工作是做的差不多了没上线,或者做着做着就没音讯了,大部分的原因都是因为需求不合理,以及任务分期不合适,总想着事情能一步到位,这会导致开发成本过高,系统太复杂,不敢上线(影响太大),复杂度上升导致bug过多,或者达不到预期,从而导致一堆人花了很长时间的努力全部白费。
b、要学会将一件大的事情,分割成多个小的、可快速完成的事情来实施,快速迭代开发。
c、开发的工作日尽量不要超过3天,1个星期的工作量的就已经是很容易半途而废的了。

3、需求确定之后,开发过程中,不要随意更改、添加
a、在需求讨论的时候,可以把以后可能会扩展的尽量想到,最终的目标等都可以讨论,这个时候需要尽量的详细
b、但在一期开发的时候,只做最核心、最急的几件事情,要做的东西太多,最终只会导致什么都做不了
c、达成共识之后,请需求发起方将讨论确定下来的内容通过邮件的形式发送给相关人员,邮件标题需要有统一前缀,例如【违禁词汇后台改版】- 第一期需求

二、开发过程
1、线上项目修改,必须要有svn,并确认正式版上的相应文件是否已经提交到svn中,并在测试版进行同步
2、开发新功能,必须先在测试机上开发,严禁在正式服务器直接修改
3、需要有相应的测试版数据库,可以找运维组的同学帮忙
4、开发过程中,有不清楚的,需要及时跟需求方进行沟通、反馈
5、必须有修改记录,修改记录里需含有:
a、数据库结构变化,注释
b、新增了哪些文件,做什么
c、修改了哪些文件,分别是为什么
d、各个功能对应哪些文件
6、开发完成之后,需要发送测试确认邮件给需求方,测试之后需要通过邮件回复正式确认,例如【违禁词汇后台改版】- 测试确认

三、部署上线
1、发送邮件,请求运维组同学帮忙备份程序、数据库,并帮忙观察下监控情况,例如【违禁词汇后台改版】- 上线前备份监控
2、将上线步骤通过邮件形式发送给相关人员,没有异议之后,开始下述上线操作。【违禁词汇后台改版】- 上线部署步骤
3、备份完成之后,部署程序到正式版,所有文件更改必须通过svn来更新,不允许直接copy过去上线
4、数据库结构修改部署,先于程序,更改之后马上看下页面以及相应监控是否正常
a、新增:一般不影响旧程序,可以在程序上线之前改,特殊情况自己考虑
b、修改:改了数据库结构之后,旧程序会报错,怎么办?
c、删除:可以在程序上线运行正常之后再删
5、程序文件部署
a、确认正式版的对应文件没有人直接在服务器上修改,如果有,则将正式版的提交,然后在测试版up,再重新进行测试
b、文件新增的,可以在程序上线前更新到正式版
c、更改旧有文件的,如果是后台的,可以先更新后台的,观察没问题之后,前台的如果耦合度不高,可以一个模块一个模块的上线测试
6、上述几步没问题之后,让相关人员在正式版把各个功能都测试一下,并看看其他地方是否会受影响
7、查看相应的数据统计及监控(页面访问、51yes统计、cnzz统计、运维监控、用户反馈)

四、部署出错处理
1、出现问题之后,立即通知相关人员,特别是需要告知自己的leader,协助修复,如果预估修复的时间超过10分钟,项目较重要,则请运维组的同事帮忙进行恢复
2、恢复到旧版本之后,在测试版排查是什么原因导致
3、重新发送新的上线部署步骤邮件,再重新部署
4、没问题之后,邮件告知大家,什么原因导致部署失败,以及如何避免

五、上线后的观察、维护
1、将整个开发过程的开发记录添加到tech后台
2、上线后的一周内,都要时不时的抽时间去了解下相应情况(使用情况、效果如何、用户反馈、有什么不足等)
3、将收集到的情况做相应记录,并记录到后台,为后续开发做准备

php代码规范以及项目开发规范

袁亮,2014-08-26,php代码规范以及项目开发规范,mysql规范

一、命名
1、文件夹、文件名
1.1 只允许小写字母,数字,下划线组成,不允许出现其他字符
1.2 以统一的功能模块名加操作类型命名
1.3 比如:user_add.php,sub/user_add_sub.php
1.4 add_user.php sub/add_user_sub.php 这种写法是不友好的,不要这么写
2、类名
2.1 只允许英文字母,数字组成
2.2 驼峰规则,每个单词的首字母大写
2.3 比如:class BlogPosts{}
3、函数名
3.1 只允许英文字母,数字组成
3.2 驼峰规则,同类名,第一个单词首字母小写
3.3 比如:function postAdd();
3.4 命名以模块名+动作类型命名,比如postDetailGet();而不是getPostDetail,并且将同一个模块的函数尽量放一起
4、变量名
4.1 只允许小写字母,数字,下划线组成
4.2 单词之间以_连接,除循环中之后,不允许出现单字母变量
4.3 善于临时变量,有些变量使用1-2次之后,马上就没用的,可以使用$tmp来命名,减少想变量名的苦恼
4.4 所以的sql语句,都用$sql命名,并且最好是定义完就使用,后面不会再次用到
4.5 常用变量名:
$page:分页页码
$limit:每页显示多少条
$ip:用户ip地址
$dated:当前时间
$ms:mysqls操作类实例
$pager:存放分页的html代码
$data:当前页面主要的数据
$user_id:用户id
$username:用户名
$nickname:用户昵称
5、全局变量
5.1 命名规范同变量,但加前缀g_标示这是一个全局变量
5.2 比如:global $g_username; $g_username = 'yuanliang847';
6、常量名
6.1 只允许大写字母,下划线,数字组成
6.2 比如 define("USER_PASS_MDSTR",'D123#!@ax?DSAD');
#7、session名称

二、php最后结束符
1、不要使用短标签<??>
2、每行后面必须加;号结束
3、纯php文件,最后不要加?>结束符,防止因为最后的空行等输出导致bug

三、单引号、双引号
1、纯字符串的时候,使用单引号
2、字符串中有变量的时候,使用双引号,并且变量必须以{}包含起来,比如$show = "你好{$nickname}!";
3、数组中,非数组下标,一定要加单引号,比如$data['username'] = 'yuanliang847';
4、sql语句中的变量值,以单引号包含起来,比如$sql = "select * from `users` where `username`='{$username}' limit 1";

四、括号使用
1、所有大括号的开始部分都跟在关键字的后面,没有例外,比如:
function getRow($sql){

}
foreach($data as $k=>$v){

}
class Mysqls{

}
2、小括号跟关键词相连,不需要额外的空格
if(1 == $a){

}else if(2 == $b){

}else{

}
3、函数参数左右不需要额外空格
function test($a,$b = 1){

}

五、空格、缩进、大数组缩进
1、所有缩进,均以tab进行缩进,不要使用多个空格
2、空格使用情况:
2.1 $username = 'yuanliang';变量赋值,前后加空格
2.2 比较操作符、算术操作符、逻辑操作符,前后加空格
+= , >= , <= , ==
+ , - , * , %
&& , ||
2.3 一元运算符,不需要空格
++ , -- , ! , &
2.4 对象运算符,不需要空格
$this->test();
parent::test();
3、大数组缩进方式,前面通过tab缩进到 同一层次,=>后面的是一个空格,数组只有1-2个值的时候,不需要写成这样
$data = array(
'user_id'    => 1535917,
'username'    => 'yuanliang847',
'nickname'    => '暗夜御林',
'head'        => 'http://i.ci123.com/153/1535917.png',//这种最后一定要加逗号,否则容易出错
);
$uinfo = array('username'=>'yuanliang847','nickname'=>'暗夜御林');

四、安全
1、需要过滤的外部数据:$_GET,$_POST,$_COOKIE,$_SESSION(session因为有可能存储了用户输入的内容,从而导致危险)
2、如果接收的是整形数据,一律以intval强制转为整形
2.1 比如 $user_id = isset($_GET['user_id'])?intval($_GET['user_id']):0;
3、如果是字符型,则需要调用防止sql注入的函数,进行过滤,该函数,在通用的function.php中就有
3.1 比如 $username = isset($_POST['username'])?stripSql($_POST['username']):'';
3.2 stripSql第二个参数,默认会调用stripTags,防止xss攻击,如果内容允许html等,则给定第二个参数
3.3 如果大串用户输入的内容,是会显示在页面的,比如文字内容等,可以使用htmlspecialchars将html实体化
4、接受外部参数的地方要在页面头部,或者函数开始位置全部获取所有可能接受的外部参数,该段代码之后,不允许再直接使用外部数据
5、如果是旧项目,需要改的地方太多,可以引入360的过滤文件,对所有外部危险输入进行过滤,并记录
该文件慎用,特别是在重要项目中,容易导致误判

五、功能块

六、注释

其他:
1、不要使用复杂的异或等逻辑判断(考验运算优先级的)
2、三元表达式只用做最简单的不赋值,不要做复杂的代码
3、不要让代码读起来有歧义,尽量简洁明了
4、不要写非常“巧妙”的代码,而是要让很二的人一眼就能读懂
5、不要使用or and,使用&&和||代替,优先级不同
6、少用while循环,太容易造成死循环
7、switch case 每个环节必须有break,否则会出错
8、理解清楚return continue break的意思,不要乱用
9、包含文件,一律使用include_once
10、嵌套层次最多4层,消除嵌套方法
11、循环中不要计算数量,有些计算能放在循环外面就不要放在循环里计算
11、大括号不可省略
12、使用construct 和 destruct,不要用与类同名的函数初始化 __get __set __autoload禁用
13、只用die,不要使用exit
14、不要使用嵌套式的赋值
if($a = ($c = getName())

做事优先级

dear tech4:

一、多件事情,怎么确定优先级
1、5分钟内就能搞定的事情,优先搞定,不要拖着
2、其他事情,按事情的重要程度来安排处理
3、更上级安排的事情,优先处理,比如员工群里,程总发现的问题或者南哥直接安排的事情
4、安全漏洞以及服务器出问题,需要迁移,请务必当成最重要的事情去做

二、同一件事情的处理优先级
1、第一优先级,东西是可用的
2、第二优先级,东西是好用的,包括代码的健壮、高效、易维护等
3、最后,才是自己是搞的很清楚的
因此,做事情前,必须要先去看看别人是怎么做的,不要想着什么都从头开始做,包括我们自己网站的内部资源,其他比较好的网站是怎么做的,百度或者google找到别人的demo,然后开始去处理。
给我看东西之前,都需要做好准备,我会问你“其他人是怎么做的?”,最好是能主动给我一个满意的答案

周报要求

13:14 2014-3-13,袁亮,周报要求

时间:每周日中午12点前
标题:【2014-02-24至2014-02-28】周报-袁亮
内容:
	一、本周做了什么?
		ps:主要工作记录下来即可,不要细化到改个链接什么的
	二、最有成就感或者最不爽的事?
		ps:学到了一个比较牛逼的东西,或者做了一个有意思的东西,或者做了一件自己觉得不想干的事等等
	三、有什么不足或者需要改进的?
	ps:自己的个人疑惑,或者自己哪方面还需要加强?团队中哪方面不合理的?个人或者团队内部的都可以

	其他:
	1、周报内容写在邮件正文里,可以提供附件作为备份参考,但不允许出现只有附件,没有正文的
	2、邮件的排版,参考看下其他人的邮件是怎么写的,不要写的乱七八糟,包括换行,缩进等,归纳总结
		如果自己都不能写的有条有理,那这个总结效果必然很一般
	3、周报发送给自己的直属领导,抄送我

简单的签名技术

编    写:袁    亮
时    间:2015-01-27
说    明:基本的数字签名技术

一、为什么需要
1、网络传输的过程中,数据对别人来说是可见的,我们需要保证数据没有被篡改,以保证基本的安全
2、常见使用:
cookie信息
接口调用参数验证

二、如何实现
1、先验条件
1.1 数据生成、数据接收方都需要可控
1.2 两边统一一个相应的密钥,并保存
2、实现
2.1 生成方,在原始数据的后面,根据密钥以及原始数据(其中一部分也行,两边约定好即可),使用散列(MD5或者SHA-1)生成一个签名
2.2 接受放根据接受到的数据,将原始数据使用同样的办法,进行散列,查看与接受到的签名是否一致,如果不同,则非法
3、优点
3.1 实现简单,安全性较高
4、缺点
4.1 如果密钥泄露,更改较为麻烦,得两边同时修改

三、cookie签名demo
1、设置cookie
define("MD_STR","FDSAF$#@dsa!#@4134Eda");

$cstr = '1535917,yuanliang847,暗夜御林';//设置到cookie里的内容
$signstr = md5($cstr.MD_STR);
$cstr .= "|-|".$signstr;

setcookie("uinfo",$cstr,0,"/",'',false,true)

2、cookie解析
define("MD_STR","FDSAF$#@dsa!#@4134Eda");

$uinfo = checkLogin();

function checkLogin(){
if(!isset($_COOKIE['uinfo']) || !$_COOKIE['uinfo']){
return array();
}

$cstr = $_COOKIE['uinfo'];
$tmp = explode("|-|",$cstr);
if(md5($tmp[0].MD_STR) != $tmp[1]){//cookie非法
return array();
}

$data = explode(",",$tmp);
return array('user_id'=>$data[0],'username'=>$data[1],'nickname'=>$data[2]);
}

四、接口参数签名demo
1、client(必须是服务端的,不能是用户可见的过程,比如js)
define("MD_STR","FDSAF$#@dsa!#@4134Eda");

$uid = 1535917;
$username = 'yuanliang847';
$str = "uid={$uid}&username={$username}";//需要传输的原始数据
$http_str = $str."&signstr=".md5($str.MD_STR);//将签名与原始数据一起拼接传输

//...网络数据传输

2、server
define("MD_STR","FDSAF$#@dsa!#@4134Eda");

$uid = isset($_GET['uid'])?intval($_GET['uid']):0;
$username = isset($_GET['username'])?$_GET['username']:'';
$signstr = isset($_GET['signstr'])?$_GET['signstr']:'';

$str = "uid={$uid}&username={$username}";
if(md5($str.MD_STR) != $signstr){
die('参数错误');
}

//...正常业务逻辑处理

3、改进
以上需要对每一个客户端和服务端进行相应的编写,很麻烦
可以编写一个统一的签名函数,来进行相应的验证

五、接口层统一的函数
1、client
define("MD_STR","FDSAF$#@dsa!#@4134Eda");

$p = array(//需要传递的参数,可以是get或者post用
'user_id'    => 1535917,
'username'    => 'yuanliang847',
'nickname'    => '暗夜御林',
);
$p = addSign($p);
$url = "http://api.***.com/***.php?".http_build_query($p);

//...发送接口请求

/**
* 将需要传递的参数数组增加一个签名串
* @data 需要传递的参数数组
* #返回新的参数数组,并增加键值signstr作为签名
*/
function addSign($data){
$str = json_encode($data);
$data['signstr'] = md5($str.MD_STR);
return $data;
}

2、server
define("MD_STR","FDSAF$#@dsa!#@4134Eda");

$is_sign = checkSign($_POST);//看接口是post还是get,传入不同的数据
if(!$is_sign){
die('参数错误');
}

//... 正常业务逻辑处理

/**
* 验证签名串是否正确
* @data 需要验证的参数,包括原始数据以及签名串
* #返回true则正常,否则参数有问题
*/
function checkSign($data){
if(!isset($data['signstr']) || !$data['signstr']){//没有签名
return false;
}

$signstr = $data['signstr'];
unset($data['signstr']);
if(md5(json_encode($data).MD_STR) != $signstr){
return false;
}

return true;
}

育儿网邮寄地址插件

编    写:袁    亮
时    间:2015-01-20
说    明:育儿网邮寄地址插件使用说明

一、问题
1、收货的邮寄地址需要用户填写省市区
2、省市区等信息会变,需要统一管理
3、需要实现联动效果,不需要各个项目都做这一套

二、使用范例
1、代码(参考demo.html文件)
<form name="recvaddress_form">
<select name="province" onchange="ciarea.selcity(this.value,0);" style="width:80px;"></select> 省
<select name="city" style="width:120px;" onchange="ciarea.selarea(this.value,0);"></select> 市
<select name="area" style="width:120px;"></select> 区
</form>
<script type="text/javascript" src="http://shiyong.ci123.com/district/common.js"></script>
<script type="text/javascript">
window.onload = function(){
//ci_province=11;ci_city=1100;ci_area=41306; //编辑的时候,需要默中省市区的时候设置
ciarea.selprovince(ci_province,ci_city,ci_area);
}
</script>
2、需要注意的地方
2.1 form的name必须设置为recvaddress_form,js中有用到
2.2 三个select的name的名称是确定的这三个
2.3 两个onchange事件不能少
2.4 引用common.js
2.5 页面加载的时候,触发数据加载
2.6 编辑地址的时候,需要有默认选中的时候,则设置相应的三个js变量即可

三、参考页面
1、http://shiyong.ci123.com/user/address.php
2、http://user.ci123.com/account/EditUserInfo/detail

邮寄地址插件demo范例

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>邮寄地址插件使用范例</title>
</head>

<body>
<form name="recvaddress_form">
<select name="province" onchange="ciarea.selcity(this.value,0);" style="width:80px;"></select> 省
<select name="city" style="width:120px;" onchange="ciarea.selarea(this.value,0);"></select> 市
<select name="area" style="width:120px;"></select> 区
</form>
<script type="text/javascript" src="http://shiyong.ci123.com/district/common.js"></script>
<script type="text/javascript">
window.onload = function(){
//ci_province=11;ci_city=1100;ci_area=41306; //编辑的时候,需要默中省市区的时候设置
ciarea.selprovince(ci_province,ci_city,ci_area);
}
</script>
</body>
</html>

http协议的简单总结

编    写:袁    亮
时    间:2014-6-26
说    明:http协议的简单总结

一、基本流程
1、在浏览器输入网址
2、浏览器根据网址,封装http请求头数据包
3、数据包根据地址查找到相应服务器
4、服务器根据头信息进行相应处理并返回返回头信息以及body内容
5、浏览器接收到返回数据,展示body部分内容,并根据头消息进行相应设置,比如cookie等
6、浏览器根据内容进行相应的排版现实,并且运行其中的js交互脚本
7、一次完整的交互完成

二、查找服务器过程
1、根据域名查找本地hosts文件,看是否有对应ip
2、发送到代理,代理去请求数据
3、如果没有,则发送请求到公网dns查询该域名对应的服务器地址
4、cdn加速访问
5、前端代理转发
6、http服务器接收请求,并交给php解析运行
7、服务器将返回的数据再传输回用户的浏览器

三、http常用状态码
200:正常返回
301:永久跳转
302:临时重定向
304:资源未修改,直接使用本地缓存
403:权限不够
404:找不到文件
500:服务器错误,一般是程序语法错误等
502:代理服务器未能接受到正确响应
504:响应超时

crontab书写的一些注意事项

编	写:袁	亮
时	间:2014-03-13
说	明:crontab书写的一些注意事项

一、使用说明:
crontab 是指定时linux服务器,自动的定时去执行一个脚本
一般用于定时去进行一些数据统计,缓存更新,消息提醒等等

二、使用流程:
1、脚本编写,并执行测试(/opt/ci123/php/bin/php *.php),看功能是否正常
	a、定时脚本最好不要在浏览器中执行,因为定时脚本一般执行时间较长,如果在浏览器下执行,有死循环或者太耗时间的语句,中途停止不了,很容易因为这个把数据库等弄挂,项目不能正常访问。而在linux下直接执行,如果卡或异常,可以直接ctrl+c中止。
	b、引入外部文件最好以全路径包含:通过define("FROOT",(dirname(__FILE__)))获取当前文件路径,再通过FROOT来拼接(易于迁移,防止多重include导致相对路径计算错误等)
	c、不允许浏览器访问,防止爬虫等异常访问,加在脚本的前面
		[code]
		if(isset($_SERVER['HTTP_HOST'])){//定时脚本不允许浏览器访问
 		       die();
		}
		[/code]
	d、必须有输出,包括:脚本开始时间、结束时间,各模块的结束时间,方便后期排查

2、加入crontab
	a、crontab -e 进行crontab文件编辑,-e参数不可少,否则会导致将crontab内容清楚,切记
	b、添加一条记录,例如:
		第一行	以#开头的是注释,谁写的,什么时间,做什么用的
		第二行,前5个,分别代表分、时、日、月、周(具体数字代表固定时间;1-5,代表区间;1,4,7代表指定几个时间;*/5代表每5个单位时间执行一次;*代表任意时间;);
			source /etc/profile.d/ice-3.2.1.sh;这个是有ice扩展的时候才加;
			后面是调试时候的执行代码;
			>>/tmp/bbs_static_hour_log 是指将输出以追加的方式记录到/tmp/bbs_static_hour_log这个文件中,命名需规范
			2>&1; 代表不管是正常输出,还是错误提示都记录到日志,照抄即可

		[code]
		#袁亮-2013-12-03,统计bbs每天24小时发帖、回帖等数据
		37 2 * * * ( source /etc/profile.d/ice-3.2.1.sh; /opt/ci123/php/bin/php 		/opt/ci123/www/html/bbs/sadmin/cront/makeStatic.php >>/tmp/bbs_static_hour_log  2>&1;)
		[/code]