了解Javascript模块化开发

小A是某个创业团队的前端工程师,负责编写项目的Javascript程序。

全局变量冲突

根据自己的经验,小A先把一些常用的功能抽出来,写成函数放到一个公用文件base.js中:

var _ = {
    $: function(id) { return document.getElementById(id); },
    getCookie: function(key) { ... },
    setCookie: function(key, value) { ... }
};

小A把这些函数都放在_对象内,以防过多的全局变量造成冲突。他告诉团队的其他成员,如果谁想使用这些函数,只要引入base.js就可以了。

小C是小A的同事,他向小A反映:自己的页面引入了一个叫做underscore.js的类库,而且,这个类库也会占用_这个全局变量,这样一来就会跟base.js中的_冲突了。小A心想,underscore.js是第三方类库,估计不好改,但是base.js已经在很多页面铺开,不可能改。最后小A只好无奈地把underscore.js占用的全局变量改了。

此时,小A发现,把函数都放在一个名字空间内,可以减少全局变量冲突的概率,却没有解决全局变量冲突这个问题。

依赖

随着业务的发展,小A又编写了一系列的函数库和UI组件,比方说标签切换组件tabs.js,此组件需调用base.js以及util.js中的函数。

有一天,新同事小D跟小A反映,自己已经在页面中引用了tabs.js,功能却不正常。小A一看就发现问题了,原来小D不知道tabs.js依赖于base.js以及util.js,他并没有添加这两个文件的引用。于是,他马上进行修改:

<script src="tabs.js"></script>

<script src="base.js"></script>

<script src="util.js"></script>

然而,功能还是不正常,此时小A教训小D说:“都说是依赖,那被依赖方肯定要放在依赖方之前啊”。原来小D把base.js和util.js放到tabs.js之后了。

小A心想,他是作者,自然知道组件的依赖情况,但是别人就难说了,特别是新人。

过了一段时间,小A给标签切换组件增加了功能,为了实现这个功能,tabs.js还需要调用ui.js中的函数。这时,小A发现了一个严重的问题,他需要在所有调用了tabs.js的页面上增加ui.js的引用!!!

又过了一段时间,小A优化了tabs.js,这个组件已经不再依赖于util.js,所以他在所有用到tabs.js的页面中移除了util.js的引用,以提高性能。他这一修改,出大事了,测试组MM告诉他,有些页面不正常了。小A一看,恍然大悟,原来某些页面的其他功能用到了util.js中的函数,他把这个文件的引用去掉导致出错了。为了保证功能正常,他又把代码恢复了。

小A又想,有没有办法在修改依赖的同时不用逐一修改页面,也不影响其他功能呢?

模块化

小A逛互联网的时候,无意中发现了一种新奇的模块化编码方式,可以把它之前遇到的问题全部解决。

在模块化编程方式下,每个文件都是一个模块。每个模块都由一个名为define的函数创建。例如,把base.js改造成一个模块后,代码会变成这样:

define(function(require, exports, module) {
    exports.$ = function(id) { return document.getElementById(id); };
    exports.getCookie = function(key) { ... };
    exports.setCookie = function(key, value) { ... };
});

base.js向外提供的接口都被添加到exports这个对象。而exports是一个局部变量,整个模块的代码都没有占用半个全局变量。

那如何调用某个模块提供的接口呢?以tabs.js为例,它要依赖于base.js和util.js:

define(function(require, exports, module) {
    var _ = require('base.js'), util = require('util.js');
    var div_tabs = _.$('tabs');
    // .... 其他代码
});

一个模块可以通过局部函数require获取其他模块的接口。此时,变量_和util都是局部变量,并且,变量名完全是受开发者控制的,如果你不喜欢_,那也可以用base:

define(function(require, exports, module) {
    var base = require('base.js'), util = require('util.js');
    var div_tabs = base.$('tabs');
    // .... 其他代码
});

一旦要移除util.js、添加ui.js,那只要修改tabs.js就可以了:

define(function(require, exports, module) {
    var base = require('base.js'), ui = require('ui.js');
    var div_tabs = base.$('tabs');
    // .... 其他代码
});

加载器

由于缺乏浏览器的原生支持,如果我们要用模块化的方式编码,就必须借助于一个叫做加载器(loader)的东西。

目前加载器的实现有很多,比如require.js、seajs。而JRaiser类库也有自己的加载器。

 

本文摘自:http://heeroluo.net/article/detail/111

利用PHP调用BizMail OpenApi(腾讯企业邮箱)进行接口开发

以下引自《腾讯企业邮箱OpenApi协议v1.0

腾讯企业邮箱开放协议,包括面向第三方合作应用和面向企业邮用户两类。其中,面向
企业邮用户的开放协议, 将提供给企业邮用户丰富的应用接口, 用户可以根据这些接口定制
自己统一的企业解决方案。
通过协议接口,企业用户可以实现:
1) 单点登录
可以从公司 OA 系统、网站一键进入企业邮箱,免去登录过程。
2) 新邮件提醒
新邮件将即时在 OA 等办公系统提醒你。
3) 数据同步
数据同步可以帮助你同步部门成员信息, 你还可以创建、 删除、 修改帐号, 同步部门信息等。

我一直想用PHP来实现这些功能的调用,把腾讯官方提供的这份简单的文档研究了下,终于弄出来了。做个笔记,如下:
在使用OpenApi接口之前,要先准备好开通腾讯企业邮箱的管理员ID,接口Key(这个可以去企业邮箱管理后台获取)和需要调用信息的邮箱名,然后就可以进行相关调用了,PHP代码如下:

 

// 首先获取AccessToken
 
// 企业邮箱的管理员ID
$cTMailID = 'mymailid';
// 在管理后台获取的接口Key
$cTMailSecret = 'a1234567890b1234567890c123456789';
// 需要调用信息的邮箱名
$cTMailAlias = 'mailalias';
// 获取Token的地址
$cTMailGetAccessTokenURL = 'https://exmail.qq.com/cgi-bin/token';
$cTMailContentData = 'grant_type=client_credentials&client_id=' . $cTMailID . '&client_secret=' . $cTMailSecret;
 
// 获取access_token
// 不要问我为什么这么写,早就忘记了!-_-
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailGetAccessTokenURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
$cTMailAccessToken = $json_obj['access_token'];
 
// 打印获取的AccessToken
echo $cTMailAccessToken . '
';
 
 
 
// 获取auth_key
$cTMailGetAuthKeyURL = 'http://openapi.exmail.qq.com:12211/openapi/mail/authkey';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&alias=' . $cTMailAlias;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailGetAuthKeyURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
$cTMailAuthKey = $json_obj['auth_key'];
 
 
// 一键登录
$cURL = 'https://exmail.qq.com/cgi-bin/login?fun=bizopenssologin&method=bizauth&agent=' . $cTMailID . '&user=' . $cTMailAlias . '&ticket=' . $cTMailAuthKey;
echo '一键登录';
 
 
// 获取用户信息
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/user/get';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&alias=' . $cTMailAlias;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 75);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
print_r($json_obj);
echo '
';
echo 'Email地址:' . $json_obj['Alias'] . '
';
echo '姓名:' . $json_obj['Name'] . '
';
echo '性别:' . $json_obj['Gender'] . '
';
echo '职位:' . $json_obj['Position'] . '
';
echo '电话:' . $json_obj['Tel'] . '
';
echo '手机号码:' . $json_obj['Mobile'] . '
';
echo '员工编号:' . $json_obj['ExtId'] . '
';
 
 
 
// 获取用户未读邮件数量
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/mail/newcount';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&alias=' . $cTMailAlias;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 75);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
// 打印获取的信息
print_r($json_obj);
 
 
// 客户端维持长连接
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/listen';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&alias=' . $cTMailAlias;
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_HEADER,0);
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
curl_setopt($ch,CURLOPT_CONNECTTIMEOUT, 75);
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
print_r($json_obj);
 
 
 
// 获取子部门列表
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/party/list';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&partypath=';
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
echo '
获取的子部门列表:';
print_r($json_obj);
 
 
 
// 获取部门下成员列表
$cTMailPostURL = 'http://openapi.exmail.qq.com:12211/openapi/partyuser/list';
$cTMailContentData = 'access_token=' . $cTMailAccessToken . '&partypath=XX公司/财务部';
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,$cTMailPostURL);
curl_setopt($ch,CURLOPT_POSTFIELDS,$cTMailContentData);
curl_setopt($ch,CURLOPT_ENCODING,'UTF-8');
curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1 );
$res = curl_exec($ch);
curl_close($ch);
$json_obj = json_decode($res,true);
echo '
获取的子部门成员列表:';
print_r($json_obj);

原文参考:http://www.barhe.org/archives/561
官方手册:http://cdn.markdream.com/ref/mat/%E8%85%BE%E8%AE%AF%E4%BC%81%E4%B8%9A%E9%82%AE%E7%AE%B1OpenApi%E5%8D%8F%E8%AE%AEv1.0.pdf

OSX使用技巧记录

用OSX不是很久,有很多小技巧还没能熟练掌握,这里我把我用到的一些知识放到这里算是一个集合吧,有需要的朋友可以相互学习。

1、显示\隐藏“隐藏文件或文件夹”

打开“终端”,输入如下命令,重启Finder即可。

$ defaults write com.apple.finder AppleShowAllFiles -bool true  #显示

$ defaults write com.apple.finder AppleShowAllFiles -bool false #隐藏

2、强制关机(暴力,邪恶)

  1. 直接按住电源键不放,五秒之后电脑就会强行切断电源。不过它有个坏处,就是有可能损坏系统文件,所以不到万不得已的时候不要使用这种方法。
  2. 同时按住 control + command + 电源键,这样也会强制关闭电脑,但好处是不会损坏系统文件。

3、强制关闭程序

打开强制退出程序窗口(使用快捷键:Command+Option+Esc)来打开“强制退出应用程序”的窗口,然后选中你需要退出的程序,再点右下方的“强制退出”即可。

4、关闭Dashboard

OSX 里一项功能叫作 Dashboard,透过 Dashboard 所提供的各种小工具 Widget ,让我们可以很方便地取得特定的资讯。不过笔者我似乎很少使用这个方便的功能,感觉太累赘所以想出来这么把它们关闭掉。

1)命令关闭(键盘君最爱)

#关闭
$ defaults write com.apple.dashboard mcx-disabled -boolean YES
$ killall Dock

#重新开启
$ defaults write com.apple.dashboard mcx-disabled -boolean NO
$ killall Dock

2)常规关闭(推荐)

“系统偏好设置”——>“Mission Control”里面,将“Dash Board”设置为“关闭”即可。

4E3834D3-65F1-4C15-B03B-6D5AD17EA72E

 

5、OSX找不到WiFi信号

苹果电脑突然找不到wifi信号了,其他的电脑或者手机(包括iPhone)均能看到Wifi信号,就是苹果电脑看不到,这种情况是OSX系统无法识别Wifi频道为12的信号段导致的,一般我们的路由器默认采用“自动模式”,所以有可能根据你所在的wifi环境下路由器自动更换频道所致,一般情况下我们可以手工指定一个频段即可(只要不是12频段即可)设置完毕后重启你的路由器即可解决该问题。

0EB6ADA4-54F8-45C3-AA4D-9C3D4C245E1C

6、Spotlight搜索文件怎么在Finder中打开?

我们输入“信息”,这时候我们不要点回车了,我们按住Command再点回车,发现搜索的结果在finder中显示了。

最近更新2015年06月08日

基于rsync+sersync的服务器文件同步实战

N年前的业务,现在发现使用FTP远程发布变得相当的低效率,性能差安全性又不是很高。网络上不乏存在许多关于数据同步的文档,其中一篇《rsync+inotify实现服务器之间文件实时同步》文章吸引我的注意,按照这篇文档的思路进行业务上的扩展,后面再发现金山一位工程师开发了一款《sersync》深受欢迎和鼓舞,利用前人制造的好的轮子对现有业务进行拆分和实施。

现有业务上的服务器列表

服务器A(主服务器)IP:192.168.28.1
服务器B(从服务器/备份服务器)IP:192.168.28.88
……

业务的要求就是在A服务器上操作一个文件(增删改),实时同步到B服务器中。也就是保证B服务器要与A服务器上面的文件要时刻保持一致。
为了达到上述业务要求,按照正常测试逻辑我们还是先对服务器B进行相关配置,因为服务器B是作为备份服务器所以在B服务器上我们只需要安装rsync软件即可。

在服务器B上安装rsync

cd /opt/src
wget  http://rsync.samba.org/ftp/rsync/src/rsync-3.1.0.tar.gz
tar zxf rsync-3.1.0.tar.gz
cd rsync-3.1.0
./configure
make && make install

至此你已经在服务器B上成功安装rsync,可以在试着输入下rsync命令。

在服务器B上配置rsync的认证密码

现在你该配置rsync服务了。

#创建rsync认证文件  markdream是用户名 123456是密码 注意中间以“:”分割
echo "markdream:123456" > /opt/authz/rsync.pas
#将密码文件设置600 避免报错
chmod 600 /opt/authz/rsync.pas

设置rsync的配置文件

vi /etc/rsyncd.conf

服务器B上的rsyncd.conf文件内容

uid=root
gid=root
max connections=36000
use chroot=no
log file=/var/log/rsyncd.log
pid file=/var/run/rsyncd.pid
lock file=/var/run/rsyncd.lock

#website同步模块
[website]
uid=root
gid=root
path=/home/website #同步到B服务器的文件存放的路径
comment  = markdream website  #注释
ignore errors = yes #忽略错误
read only = no # 只读
hosts allow =  192.168.28.1/24 #只允许IP 192.168.28.1进行同步
hosts deny = *
auth users = chnpony  #同步的用户名
secrets file = /opt/authz/rsync.pas   #密码文件

启动服务器B上的rsync服务

rsync --daemon"

最后一步我们把rsync服务器设置开机启动

echo "/usr/local/bin/rsync --daemon" >> /etc/rc.local

到这里服务器B上面的rsync配置完毕,下面开始服务器A(主服务器的配置)

服务器A(主服务器)配置

由于是主服务器,用到的不只是rsync还需要安装inotify-tools软件再加上sersync软件,总体来说比从服务器需要的软件要多。
我们还是先在服务器A(主服务器)上安装rsync,可以直接按找上面的方式进行安装即可。

服务器A:安装inotify-tools

inotify-tools是一个 Linux特性,它监控文件系统操作,比如读取、写入和创建。Inotify 反应灵敏,用法非常简单,并且比 cron 任务的繁忙轮询高效得多。学习如何将 inotify 集成到您的应用程序中,并发现一组可用来进一步自动化系统治理的命令行工具。

cd /opt/src
wget https://github.com/rvoicilas/inotify-tools/archive/master.zip -O inotify-tools-master.zip
cd inotify-tools-master
./autogen.sh
 ./configure
 make && make install

到这里你已经安装完毕inotify-tools,下面我们再来安装下这个sersync软件

服务器A:安装sersync

sersync主要用于服务器同步,web镜像等功能。基于boost1.43.0,inotify api,rsync command.开发。本项目优点是:
sersync是使用c++编写,而且对linux系统文件系统产生的临时文件和重复的文件操作进行过滤(详细见附录,这个过滤脚本程序没有实现),所以在结合rsync同步的时候,节省了运行时耗和网络资源。因此更快。

cd /opt/src
wget https://sersync.googlecode.com/files/sersync2.5.4_64bit_binary_stable_final.tar.gz
mv /opt/src/GNU-Linux-x86/ /opt/sersync
cd /opt/sersync
#配置下密码文件,因为这个密码是要访问服务器B需要的密码 和上面服务器B的密码必须一致
echo "123456" > /opt/sersync/user.pas
#同样也要设置600
chmod 600 /opt/sersync/user.pas
#我们创建一个服务器A上要同步到B服务器的路径
mkdir /home/webroot

在/opt/sersync/目录里面有个叫confxml.conf文件,我们把他们修改成这样的话就可以使用了。

服务器A:配置confxml.conf文件

<?xml version="1.0" encoding="ISO-8859-1"?>
<head version="2.5">
 <host hostip="localhost" port="8008"></host>
 <debug start="true"/>
 <fileSystem xfs="false"/>
 <filter start="false">
 <exclude expression="(.*)\.php"></exclude>
 <exclude expression="^data/*"></exclude>
 </filter>
 <inotify>
 <delete start="true"/>
 <createFolder start="true"/>
 <createFile start="false"/>
 <closeWrite start="true"/>
 <moveFrom start="true"/>
 <moveTo start="true"/>
 <attrib start="false"/>
 <modify start="false"/>
 </inotify>

 <sersync>
 <localpath watch="/mnt/webroot"> <!-- 这里填写服务器A要同步的文件夹路径-->
 <remote ip="192.168.28.88" name="website"/> <!-- 这里填写服务器B的IP地址和模块名-->
 <!--<remote ip="192.168.28.39" name="tongbu"/>-->
 <!--<remote ip="192.168.28.40" name="tongbu"/>-->
 </localpath>
 <rsync>
 <commonParams params="-artuz"/> 
 <auth start="true" users="markdream" passwordfile="/opt/sersync/user.pas"/> <!-- markdream+密码文件 这里填写服务器B的认证信息-->
 <userDefinedPort start="false" port="874"/><!-- port=874 -->
 <timeout start="false" time="100"/><!-- timeout=100 -->
 <ssh start="false"/>
 </rsync>
 <failLog path="/tmp/rsync_fail_log.sh" timeToExecute="60"/><!--default every 60mins execute once-->
 <crontab start="false" schedule="600"><!--600mins-->
 <crontabfilter start="false">
 <exclude expression="*.php"></exclude>
 <exclude expression="info/*"></exclude>
 </crontabfilter>
 </crontab>
 <plugin start="false" name="command"/>
 </sersync>

 <!-- 下面这些有关于插件你可以忽略了 -->
 <plugin name="command">
 <param prefix="/bin/sh" suffix="" ignoreError="true"/> <!--prefix /opt/tongbu/mmm.sh suffix-->
 <filter start="false">
 <include expression="(.*)\.php"/>
 <include expression="(.*)\.sh"/>
 </filter>
 </plugin>

 <plugin name="socket">
 <localpath watch="/home/demo">
 <deshost ip="210.36.158.xxx" port="8009"/>
 </localpath>
 </plugin>
 <plugin name="refreshCDN">
 <localpath watch="/data0/htdocs/cdn.markdream.com/site/">
 <cdninfo domainname="cdn.chinacache.com" port="80" username="xxxx" passwd="xxxx"/>
 <sendurl base="http://cdn.markdream.com/cms"/>
 <regexurl regex="false" match="cdn.markdream.com/site([/a-zA-Z0-9]*).cdn.markdream.com/images"/>
 </localpath>
 </plugin>
</head>

好到这里你已经配置好sersync软件了,我们来运行它

cd /opt/sersync
./sersync2 -r -d

你现在可以去服务器A的/home/webroot目录下面进行添加文件,看看效果,为了保证能够正常通信,请先关闭你的防火墙(主要是服务器B)!!!

service iptables stop

我们还可以在A服务器上设置sersync开机启动

echo "/opt/sersync/sersync2 -r -d -o /opt/sersync/confxml.xml" >> /etc/rc.local

效果展示

sersync

相关参考

http://dl528888.blog.51cto.com/2382721/771533/

http://blog.johntechinfo.com/sersyncguild

30多年程序员生涯经验总结

30 years of career experience summary programmer

在我30多年的程序员生涯里,我学到了不少有用的东西。下面是我这些年积累的经验精华。我常常想,如果以前能有人在这些经验上指点一二,我相信我现在会站得更高。

1.客户在接触到产品之后,才会真正明白自己的需求。

这是我在我的第一份工作上面学来的。只有当我们给客户展示产品的时候,他们才会意识到哪些是必须的。给出一个功能性原型设计远远比一张长长的文字表格要好。

2.只要有充足的时间,所有安全防御系统都将失败。

安全防御现如今是全世界都在关注的大课题、大挑战。我们必须时时刻刻积极完善它,因为黑客只要有一次成功,就可以彻底打败你。

3.安全防御是否失败取决于及早规划。

假设有黑客会彻底破坏你的防御系统,那你就得提前做好准备。这样即便真的让他们侵入了系统,也盗取不了任何有价值的东西,因为你已经对服务器做了安全设置,比如对数据库中的内容进行了加密,并且对每台有可能遭受攻击的服务器进行了隔离。

记住,再强大的防御都会有它的薄弱之处,关键是要有备无患。

4.良好的安全防御系统不要在乎它的费用,因为这是战略投资;不及格的安全防御才是被浪费的资源。

在我的职业生涯中,经常听到有人抱怨说安全防御是多么多么的复杂和昂贵,他们没有意识到的是,如果防御失败,公司将损失的可能不止几十亿美元。为了节约几块钱而导致企业破产,这种做法毋庸置疑是非常愚蠢的。

5.将复杂的东西整理成简单的,是很难的,但是要是把复杂的捣鼓成更加复杂的,那就简单了。

这一条适用于编程、设计和几乎所有的创造领域中。我一直以来都希望自己的代码能越易于理解越好。如果你的代码过于复杂和晦涩,那十之八九它正常工作的可能性很低。我曾非常有幸地见识到有些程序员费劲千辛万苦,反而让代码更加难以捉摸了。

6.成功源自于失败中的学习;失败则是因为容忍错误的横行。

有很多程序员总是在辩解,说什么“程序这么难,犯错误很正常了,软件变得糟糕也在所难免了”。这种理由听得多了,于是,大家也逐渐接受了这些扯淡的借口。但是我们作为程序员真的不应该让这些借口阻碍我们的进步,应该谨记,错误只能犯一次,要吸取教训。话说是程序员都会希望自己下一次就能一次性搞定代码。但是没有人是完美的,不过至少我们是在朝着这个方向前进的路上。

7.唯一不变的是变化本身,这是谁都无法改变的法则。

计划永远赶不上变化,以为明天的世界和今天一样,这种想法本身就是愚不可及的。尤其是在编程世界里,没什么是永恒的。人不能两次踏进同一条河里。

8.永远不要停止学习,一旦你停下来,技术的浪头就会狠狠将你拍死在沙滩上。

作为程序员立于不败之地唯一方法就是,不断学习、不断进步。因为一旦你松懈下来,你的所有优势都将随风而逝。

9.整个软件行业建立在“百家争鸣”的思想上。

在我的职业生涯中,我看到过很多程序员会对各种事情较真:预估完成时间上较真,规模大小上面较真等等。而且有的人还屡错屡战。有些以前被批判为“行不通”的技术,现在却已经牢牢占据了人们生活的一席之地,并且现今正向着另一个高潮冲刺。

10.适合你的不一定适合他。

在软件项目中我们可做的选择很多很多。有的英明,有的糟糕。但是适合你和你当前情况的选择可能一点都不适用于其他人。我们经常能听到别人说自己又在干什么伟大的创举,但是如果他们说什么这是唯一的好方法时,我会对此嗤之以鼻。

11.在这个不断变化的世界中,评估是最为重要的技能。

这一点有些人可能并不知道。但是如果你愿意认识新事物,看得到他人的努力,比较做事方法之后再择优使用,那么不但是你自己,还有你的团队、你的项目、你的公司,都将受益无穷。但是很多人对此都不擅长,而很多负责人甚至在这方面表现得非常糟糕。照着别人说得做,以及看别人做什么自己也做什么,是非常容易的。但是如果要全方位地看问题然后再基于自己的需要选择对应的最优方向,这就很难很难了。在软件行业中做抉择是必须的,但是如果当你在不得不评价分析的时候头脑一片茫然,那最终的结果只能是随机挑一个或者是盲从随大流。

12.不管黑猫白猫,能抓到老鼠就是好猫。

只要你的软件能实现客户指定的功能,他们才不会关心需要解决哪些问题。系统出问题了,异常情况发生了,硬件坏了,程序猿被女朋友甩了,黑客盗号了:用户永远不会对这些发生兴趣。如果发生意外情况,最好能坦诚说出来,但是你最好要能确保这种情况不会持久,因为你总给将最终的产品交给客户。

13.客户的意见决定质量。

无论你设置了多少指标,检查过多少表单,审核了多少代码,写了多少测试:这都不是关键,除非客户自己亲眼目睹软件运作正常。关于代码质量、性能、设计和可用性,客户的意见才是决定质量的唯一要素。

14.对某方面的无知可能会让你一败涂地,因为你在这方面毫无经验。

即使到了今天我依旧在不断惊叹,有的同行竟然仍然没有收集足够的日志、崩溃报告和使用信息来掌控自己的软件。那些对这方面信息不屑一顾的家伙,大多会高估产品的质量。因为如果你不采取措施和记录结果,浑浑噩噩地混日子,终将会导致你对当前情况一无所知,包括你的客户。我一直反复强调,详细而有用的日志记录、程序崩溃跟踪、评论和意见,反正各种只要能让我尽快了解发生了什么问题的途径和方法,都是可行的。不过,我也知道有很多人认为“这种事和程序员有一毛钱的关系吗?”。

14.总有更好的办法,但是时间不允许。

评估中最难把握的节点是什么时候应该停止头脑风暴开始开工。或许我们会错过那个更好的方法,但是如果要耗费很长时间,那就不值得了。但是这是很难界定的,不过有时候今天的一个小小的选择可能会打败明年那个更佳的选项。Who knows?

下面两点引用自一名销售人员,他是我很早以前的同事。有些东西我并不是完全同意,不过也能给予我们不同的角度看问题。

15.客户要找愚蠢的。

这是我最喜欢的一句话,这个销售人员就职于一家咨询公司。他认为,要找那种不懂技术但是有足够资本挥霍的金主。聪明的人总是会问很多问题;没钱的人无力购买我们的服务。我很庆幸我是一名程序员,哈哈!

16.我的工作是欺骗客户,而你的工作则是支持我。

第二句话来自于同一个销售人员。他总是喜欢不断地承诺一些不可能的任务,然后当我们终于呕心沥血加班加点赶出来了,他就来收获我们成功的果实。挑战的确让人exciting,但是每次都是这种不可能的任务未免太痛苦。我的建议是,换一个更好的销售人员!

参考

http://www.codeceo.com/article/a-lifetime-programmer.html

nginx使用XSendfile模块让php更快下载文件实战

引言

最近有点闲暇时间了,看了@风雪之隅的一篇的文章《让PHP更快的提供文件下载》后,利用实际项目中的业务场景觉得有必须要去优化附件下载功能了。鸟哥的文章里面主要介绍的基于apache来做XSendfile讲解的,我有点强迫症,我对我的生产环境《顶岗实习管理系统》进行升级改造,我的webserver是nginx,所以基于nginx官方网站的XSendfile说明特做此改造笔记。

我的webroot目录在/var/vhost/demo,demo目录中存在一个uploadfiles文件夹,这个文件夹是存放用户上传的文件。比如一个用户上传一个hello.docx文件,那么它对应的物理路径应该是/var/vhost/demo/xsendfile/uploadfiles/hello.docx,如果我们以直接路径“http://demo.markdream.com/xsendfile/uploadfiles/hello.docx”来访问的话,那么我想对这个文件进行下载统计或者隐藏实体路径的话就只能使用php readfile()方法来把文件装载到内存中,在转发给客户端,如果文件超大的话,你的服务器估计很悬。所以我们要改进php下载方式,就是引言所阐述的利用nginx的XSendfile方式来传送文件给客户端,nginx默认已经包含了senfile模块了,你如果安装完nginx你会在nginx.conf文件会看到“sendfile on;”,OK说明你可以直接使用了不像apache那么复杂还要重新加载组件编译等等。好了我贴下我的配置文件和代码。

我的nginx虚拟主机配置文件

server {
        listen 80;
        server_name demo.markdream.com;

        root /var/vhost/demo;
        index  index.php;

		# 这个是定义读取你的文件的目录的url开头  直接访问是不可以的 只能通过
        location /protected {
                internal;
                alias   /var/vhost/demo/uploadfiles;
        }

        location ~ \.php$ {
           fastcgi_pass   127.0.0.1:9000;
           fastcgi_index  index.php;
           fastcgi_param  SCRIPT_FILENAME  $document_root/$fastcgi_script_name;
           include        fastcgi_params;
        }
}

一个简单的php下载文件脚本,downloads.php

<?php

//eg: http://demo.markdream.com/xsendfile/downloads.php?filename=hello.docx

// 获取文件名
$filename = $_GET["filename"];

// 你可以在这里写下你的查询数据库等你所想的功能 ……

header ( "Content-type: application/octet-stream" );
// 处理中文文件名
$ua = $_SERVER ["HTTP_USER_AGENT"];
if (preg_match ( "/MSIE/", $ua )) {
 $encoded_filename = rawurlencode ( $filename );
 header ( 'Content-Disposition: attachment; filename="' . $encoded_filename . '"' );
} else if (preg_match ( "/Firefox/", $ua )) {
 header ( "Content-Disposition: attachment; filename*=\"utf8''" . $filename . '"' );
} else {
 header ( 'Content-Disposition: attachment; filename="' . $filename . '"' );
}

// 就这么简单一句话搞定 注意“protected”是和nginx配置文件的 protected要一致
header("X-Accel-Redirect: /protected/" . $filename);

?>

愉悦的demo

http://demo.markdream.com/xsendfile/downloads.php?filename=hello.docx

参考

  1. http://wiki.nginx.org/XSendfile
  2. http://www.laruence.com/2012/05/02/2613.html
  3. http://kovyrin.net/2006/11/01/nginx-x-accel-redirect-php-rails/