Linux

一、网络
1.流量监控
iftop
安装地址 http://www.ex-parrot.com/~pdw/iftop/
2.限速
    限速:/sbin/tc qdisc add dev eth0 root tbf rate 102400kbit latency 50ms burst 102400

    解除:/sbin/tc qdisc del dev eth0 root 

注:5120kbit表示限制宽带为5M,burst表示峰值为5M

Repo

Gogs 是一款极易搭建的自助 Git 服务。但不同于gitlab + gitlab ci, gogs不提供官方CI/CD工具, Drone在不多可选的CI工作中以部署容易、配置简单等特点上脱颖而出,下文中是我在某个项目中的配置。

一、Gogs 安装

推荐使用docker方式
具体参照 https://gogs.io/docs/installation/install_from_binary
gogs 服务绑定host https://git.example.com

二、Drone 安装

  1. 安装 docker docker-compose
    yum install docker docker-compose
  2. 编辑 docker-compose.yml
    自行替换docker-comose.yml 中变量

    version: '2'
    services:
    drone-server:
    image: drone/drone:latest
    ports:
       #drone host port
      - 3866:8000
       #drone api port
      - 8002:9000
    volumes:
      - /data/server/drone/data:/var/lib/drone
    restart: always
    privileged: true
    environment:
      - DRONE_OPEN=true
      - DRONE_SECRET=1234567890
      - DRONE_HOST=${drone_host}
      - DRONE_PORT=${drone_port}
      - DRONE_TLS_AUTOCERT=false
      - DRONE_GIT_ALWAYS_AUTH=false
      - DRONE_GOGS=true
      - DRONE_GOGS_URL=https://git.example.com
      - DRONE_DATABASE_DRIVER=mysql
      - DRONE_DATABASE_DATASOURCE=${mysql_user}:${mysql_pwd}@tcp(${mysql_host})/drone?parseTime=true
    drone-agent:
    image: drone/agent:latest
    command: agent
    restart: always
    depends_on:
      - drone-server
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - DRONE_SERVER=drone-server:9000
      - DRONE_SECRET=1234567890
  3. 启动Drone
    docker-compose up -d
  4. 配置Drone
    登录drone http://drone.example.com:3866
    用户名密码为 gogs 账号。
    查看仓库列表,如果能看到如下内容,表明配置成功


    在对应的gogs仓库中,会自动配置webhooker

  5. 项目中的 Drone 配置,以php为例

    与gitlab-ci 类型,pipeline会启动一个新的docker容器,本例采用scp部署非编译项目。
    配置中[secrets]来自drone项目中的secrets 添加,具体可参照drone 官方文档。

    pipeline:
    slack:
    image: plugins/slack
    channel: prod
    when:
      branch: master
      event: [tag, release]
      branch: master
    scp:
    image: appleboy/drone-scp
    host:
      - 192.168.99.100
      - 192.168.99.101
    user: root
    port: 22
    secrets: [ ssh_key ]
    target: /data/www/api.example.com-${DRONE_TAG}
    source:
    - ./
    ssh:
    image: appleboy/drone-ssh
    host:
     - 192.168.99.100
     - 192.168.99.101
    username: root
    secrets: [ ssh_key ]
    port: 22
    script:
      - /usr/bin/rm -rf /data/www/api.example.com
      - /usr/bin/ln -s /data/www/api.example.com-${DRONE_TAG} /data/www/api.example.com
      - /usr/bin/ln -s /data/sdk/api /data/www/api.example.com/vendor
      - chown -R www:www /data/www/api.example.com/
  6. 清理过期部署文件,为了方便回滚本例保留最近五个版本程序的部署。

    ssh 192.168.99.100
    cd /data/
    touch webclean.sh

    webclean.sh 内容

        #!/bin/sh
        echo "clean api.example.com"
        cd /data/www
    
        ls /data/www -lt | grep api.example.com- | awk '{if(NR>=5){print $9}}' | xargs rm -rf
        echo "clean success"

    添加crontab,

    crontab -e 
    0 0 * * * /data/webclean.sh

    三、Drone 部署测试

    git clone git@git.example.com:dsxt/api.example.com.git
    git tag v0.0.1
    git push --tags

总结

Drone CI 是Gogs平台下的最佳选择,在项目运行中,部署成功过react+webpack 项目、php项目,php偶发部署失败,如果有足够的计算资源建议采用 gitlab ce + gitlab ci 方式。

一、子进程使用

1)基本使用

$pid = pcntl_fork();
if ($pid == -1) {
    die('could not fork');
} else if ($pid) {
    // we are the parent
    pcntl_wait($status); //Protect against Zombie children
} else {
    // we are the child
}

2)多子进程使用

//最大进程数
define("MAXPROCESS", 50);
//计数器
$execute++;

for($i = 0; $i < 50 ; $i++){
    $pid = pcntl_fork();
    if ($pid == -1) {
        //处理fork失败的情况,一般不需要处理
    }elseif($pid){
        if ($execute>=MAXPROCESS){
            pcntl_wait($status);
            $execute--;
        }
    }else{
        //子进程逻辑
    }
}

这是子进程创建的基本方式,如果只需要怎么创建子进程上述已经满足你的需求。

然而上面代码有些问题。。。

二、僵尸进程 & 孤儿进程

pcntlfork直接调用linux系统接口函数fork。所以讨论php的子进程可以直接参照系统的fork函数。更多关于fork的描述参照维基百科 https://zh.wikipedia.org/wiki/Fork(%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8) ,php实现代码如下:

/* {{{ proto int pcntl_fork(void)
       Forks the currently running process following the same behavior as the UNIX fork() system call*/
    PHP_FUNCTION(pcntl_fork)
    {
    pid_t id;

    id = fork();
    if (id == -1) {
    PCNTL_G(last_error) = errno;
    php_error_docref(NULL, E_WARNING, "Error %d", errno);
    }

    RETURN_LONG((zend_long) id);
    }

子进程与主进程随机交互执行,默认情况(下文解释一般处理方案)下两个进程会先后执行完, 就会产生下面两种情况:

主进程先与子进程执行完:子进程会托孤给init(pid = 0),就是上文提到的进程必须存在父进程。

子进程先于主进程执行完:主进程需要捕获子进程的退出状态,否则子进程就会成为僵尸进程。(zombie),linux 进程状态为Z、z。

孤儿进程: 
Linux 进程管理有一个基本规则,除了PID为1的init进程外,所有的进程必须存在父进程,也就是说所有的进程都是被其他进程fork出来的。孤儿进程只是描述进程的一种状态,与僵尸进程不同,它没有任何问题。

僵尸进程:
释放所有资源,不可被调度,僵尸进程会存在进程列表中。详细描述参见  LINUX 的僵尸(ZOMBIE)进程https://coolshell.cn/articles/656.html。

僵尸进程的防止有多重方案,最常用的就是就是pcntl_wait。关于此函数PHP手册描述如下:

wait函数刮起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。 如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将 被释放。关于wait在您系统上工作的详细规范请查看您系统的wait(2)手册。

文章开头第一种fork方式就是采用wait防止僵尸状态出现,但是这种方案会产生新的问题:

阻塞主进程:在子进程发出结束信号之前主进程没办法再次执行其他任务。第二方式极限情况下子进程进入后直接退出或者执行时间较短也会成为zombie。为了解决这个问题可以在主进程中安装信号量signal(SIGCHLD, SIG_IGN),通知内核忽略子进程状态。

三、子进程可以完整使用主进程资源?

答案是否定的,下面两段代码可以给出验证过程。

 1、mysqlclient 子进程不可使用。

$mysqliClient = new mysqli("localhost","root","123456","assets");
$result = $mysqliClient->query("select 1");
echo sprintf("fork前主进程访问数据库: %s\n\n",

print_r($mysqliClient->error,true));

sleep(5);

$pid = pcntl_fork();
if ($pid == -1) {
    die("fork error");
} elseif ($pid) {
    echo "已经fork子进程 " . $pid . "\n";
    $result = $mysqliClient->query("select 1");
    echo sprintf("fork后 主进程访问数据库, ErrNo %d , Err: %s\n\n",
    $mysqliClient->errno, $mysqliClient->error);
    sleep(10);
} else {
    sleep(20);
    $result = $mysqliClient->query("select 1");
    echo sprintf("fork后 子进程访问数据库, ErrNo %d , Err: %s\n\n", 
    $mysqliClient->errno, $mysqliClient->error);
}

2、tcp客户端子进程可用

$msg = "i am parent";
$len = strlen($msg);

$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$handler = socket_connect($socket, '127.0.0.1', 22);

echo sprintf("fork前, 主进程 socket 连接状态 %s \n",  
socket_send($socket, $msg, $len, 1));

$pid = pcntl_fork();
if ($pid == -1) {
    die("fork error");
} elseif($pid){
    sleep(5);
    echo sprintf("主进程 socket 发送字节 %d, 当前错误 %s \n",  
    socket_send($socket, $msg, $len, 1), socket_last_error($socket));
    sleep(100);
}else{
    sleep(10);
    echo sprintf("子进程 socket 发送字节 %d, 当前错误 %s \n",  
    socket_send($socket, $msg, $len, 1), socket_last_error($socket));
    sleep(100);
}

ssh私钥登用处服务器之间互信,免密码登陆,提升安全可以去掉所有用户密码,用来放置密码泄露。

操作方法

1、生成密钥

2、上传公钥至服务器

  scp .id_rsa.pub user@yourdomain.com:~/.ssh/

登陆服务器 公钥推入 权限集合

 cat id_rsa.pub >>authorized_keys

3、配置本地环境,登陆免密码登陆远端,

Windows推荐使用xshell ,不做赘述,使用过任意一个linux服务器的同学都应该知道知道怎么配,这是基本素养。

unix-like操作系统配置方案

Host aliyun #别名,域名缩写
HostName yourdomain.com
Port 22
User user #用户名
PreferredAuthentications publickey #优先公钥校验
IdentityFile ~/.ssh/id_rsa #私钥路径 

4 、测试登陆

ssh aliyun

短暂等待登陆成功。

5、修改或者删除用户密码 (个人强烈这样做)

个人建议把密码修改成一个极其变态密码或者干脆删掉

最后的话,如果你按照上面的做了,但是登陆失败,分两步解决问题。

1、检查文件权限是否正确
  chmod 700 ~/.ssh
  chmod 600 ~/.ssh/authorized_keys  
2、目录权限(所有者)是否正确
/home/user
/home/user/.ssh 目录所有者必须为当前用户
修改方式如下:
chown user:group /home/user 
chown user:group home/user/.ssh

定时任务|Cron

简述:定时任务,顾名思义定时执行的任务,windows一般称为“计划任务”,windows7-》程序-》附件-》计划任务程序也能实现这么一个功能,但是貌似没杀人用,windows server有没有人用就不太清楚了,Linux定时任务是一个非常强大的东东,一般用来服务器日常备份或者实现某些服务器程序逻辑,比如游戏中00:00某些数据归零,或者每月清理僵尸号等等。

言归正传,我们现在讲述基础任务即cron这个玩意怎么用。

/etc/目录下有几个有关于定时任务相关的文件和目录

cron.d/       cron.daily/   cron.deny     cron.hourly/  cron.monthly/ crontab       cron.weekly/

先说crontab

在/etc目录下有一个crontab文件,这里存放有系统运行的一些调度程序。每个用户可以建立自己的调度crontab。

我们可以用crontab -e 添加要执行的命令。请记住,每个用户使用单独的crontab也就是每个用户的定时任务不同。
添加的命令必须以如下格式:

* * * * * /command path

前五个字段可以取整数值,指定何时开始工作,第六个域是字符串,即命令字段,其中包括了crontab调度执行的命令。 各个字段之间用spaces和tabs分割。

前5个字段分别表示:

   分钟:0-59
   小时:1-23
   日期:1-31
   月份:1-12
   星期:0-6(0表示周日)

还可以用一些特殊符号:

    *: 表示任何时刻
    ,: 表示分割
  -:表示一个段,如第二端里: 1-5,就表示1到5点
    /n : 表示每个n的单位执行一次,如第二段里,*/1, 就表示每隔1个小时执行一次命令。也可以写成1-23/1.

一些示例:

43 21 * * *         21:43 执行
15 05 * * *       05:15 执行
0 17 * * *          17:00 执行
0 17 * * 1          每周一的 17:00 执行
0,10 17 * * 0,2,3   每周日,周二,周三的 17:00和 17:10 执行
0-10 17 1 * *       毎月1日从 17:00到7:10 毎隔1分钟 执行
0 0 1,15 * 1        毎月1日和 15日和 一日的 0:00 执行
42 4 1 * *        毎月1日的 4:42分 执行
0 21 * * 1-6      周一到周六 21:00 执行
0,10,20,30,40,50 * * * * 每隔10分 执行
*/10 * * * *        每隔10分 执行
* 1 * * *         从1:0到1:59 每隔1分钟 执行
0 1 * * *         1:00 执行
0 */1 * * *        毎时0分 每隔1小时 执行
0 * * * *         毎时0分 每隔1小时 执行
2 8-20/3 * * *      8:02,11:02,14:02,17:02,20:02 执行
30 5 1,15 * *       1日 和 15日的 5:30 执行

& 后台执行命令

当在前台运行某个作业时,终端被该作业占据;而在后台运行作业时,它不会占据终端。可以使用&命令把作业放到后台执行。

如:

30 2 * * * /data/app/scripts/hotbackup/hot_database_backup.sh &

在后台运行作业时要当心:需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。不过,作业在后台运行一样会将结果输出到屏幕上,干扰你的工作。如果放在后台运行的作业会产生大量的输出,最好使用下面的方法把它的输出重定向到某个文件中:

如:

command >out.file 2>&1 &

在这个例子中,2>&1表示所有的标准输出和错误输出都将被重定向到一个叫做out.file 的文件中。

2>&1 含义

先看一个例子:

0 2 * * * /u01/test.sh >/dev/null 2>&1 &

这句话的意思就是在后台执行这条命令,并将错误输出2重定向到标准输出1,然后将标准输出1全部放到/dev/null 文件,也就是清空。

在这里有有几个数字的意思:

0表示键盘输入
1表示标准输出
2表示错误输出.

我们也可以这样写:

0 2 * * * /u01/test.sh  >/u01/out.file &  --这里没写,默认是1
0 2 * * * /u01/test.sh  1>/u01/out.file &
0 2 * * * /u01/test.sh  2>/u01/out.file &
0 2 * * * /u01/test.sh  2>/u01/out.file  2>&1 &

将tesh.sh 命令输出重定向到out.file, 即输出内容不打印到屏幕上,而是输出到out.file文件中。
2>&1 是将错误输出重定向到标准输出。 然后将标准输入重定向到文件out.file。
&1 表示的是文件描述1,表示标准输出,如果这里少了&就成了数字1,就表示重定向到文件1。

基本原则

封装变化Encapsulate what varies.

面向接口变成而不是实现 Code to an interface rather than to an implementation.

优先使用组合而非继承 Favor Composition Over Inheritance

SRP: The single responsibility principle 单一职责

系统中的每一个对象都应该只有一个单独的职责,而所有对象所关注的就是自身职责的完成。 Every object in your system should have a single responsibility ,and all the object s services should be focused on carrying out that single responsibility .

  1.     每一个职责都是一个设计的变因,需求变化的时候,需求变化反映为类职责的变化。当你系统里面的对象都只有一个变化的原因的时候,你就已经很好的遵循了SRP原则。 
    
  2.     如果一个类承担的职责过多,就等于把这些职责耦合在了一起。一个职责的变化就可能削弱或者抑制这个类其它职责的能力。这种设计会导致脆弱的设计。当变化发生的时候,设计会遭到意想不到的破坏。 
    
  3.     SRP 让这个系统更容易管理维护,因为不是所有的问题都搅在一起。 
    
  4.     内聚Cohesion 其实是SRP原则的另外一个名字.你写了高内聚的软件其实就是说你很好的应用了SRP原则。 
    
  5.     怎么判断一个职责是不是一个对象的呢?你试着让这个对象自己来完成这个职责,比如:“书自己阅读内容”,阅读的职责显然不是书自己的。 
    
  6.     仅当变化发生时,变化的轴线才具有实际的意义,如果没有征兆,那么应用SRP或者任何其它的原则都是不明智的。 
    

DRY : Don't repeat yourself Principle

通过抽取公共部分放置在一个地方避免代码重复.

Avoid duplicate code by abstracting out things that are common and placing those thing in a single location .

  1.     DRY 很简单,但却是确保我们代码容易维护和复用的关键。 
    
  2.     你尽力避免重复代码候实际上在做一件什么事情呢?是在确保每一个需求和功能在你的系统中只实现一次,否则就存在浪费!系统用例不存在交集,所以我们的代码更不应该重复,从这个角度看DRY可就不只是在说代码了。 
    
  3.     DRY 关注的是系统内的信息和行为都放在一个单一的,明显的位置。就像你可以猜到正则表达式在.net中的位置一样,因为合理所以可以猜到。 
    
  4.     DRY 原则:如何对系统职能进行良好的分割!职责清晰的界限一定程度上保证了代码的单一性。 
    

OCP : Open-Close Principle开闭原则

类应该对修改关闭,对扩展打开;

Classes should be open for extension ,and closed for modification .

  1.     OCP 关注的是灵活性,改动是通过增加代码进行的,而不是改动现有的代码; 
    
  2.     OCP的应用限定在可能会发生的变化上,通过创建抽象来隔离以后发生的同类变化 
    
  3.     OCP原则传递出来这样一个思想:一旦你写出来了可以工作的代码,就要努力保证这段代码一直可以工作。这可以说是一个底线。稍微提高一点要求,一旦我们的代码质量到了一个水平,我们要尽最大努力保证代码质量不回退。这样的要求使我们面对一个问题的时候不会使用凑活的方法来解决,或者说是放任自流的方式来解决一个问题;比如代码添加了无数对特定数据的处理,特化的代码越来越多,代码意图开始含混不清,开始退化。 
    
  4.     OCP 背后的机制:封装和抽象;封闭是建立在抽象基础上的,使用抽象获得显示的封闭;继承是OCP最简单的例子。除了子类化和方法重载我们还有一些更优雅的方法来实现比如组合; 
    

怎样在不改变源代码(关闭修改)的情况下更改它的行为呢?答案就是抽象,OCP背后的机制就是抽象和多态

  1.     没有一个可以适应所有情况的贴切的模型!一定会有变化,不可能完全封闭.对程序中的每一个部分都肆意的抽象不是一个好主意,正确的做法是开发人员仅仅对频繁变化的部分做出抽象。拒绝不成熟的抽象和抽象本身一样重要。 
    
  2.     OCP是OOD很多说法的核心,如果这个原则有效应用,我们就可以获更强的可维护性可重用 灵活性 健壮性 LSP是OCP成为可能的主要原则之一 
    

LSP: The Liskov substitution principle

子类必须能够替换基类。

Subtypes must be substitutable for their base types.

  1.     LSP关注的是怎样良好的使用继承. 
    
  2.     必须要清楚是使用一个Method还是要扩展它,但是绝对不是改变它。 
    
  3.     LSP清晰的指出,OOD的IS-A关系是就行为方式而言,行为方式是可以进行合理假设的,是客户程序所依赖的。 
    
  4.     LSP让我们得出一个重要的结论:一个模型如果孤立的看,并不具有真正意义的有效性。模型的有效性只能通过它的客户程序来表现。必须根据设计的使用者做出的合理假设来审视它。而假设是难以预测的,直到设计臭味出现的时候才处理它们。 
    
  5.     对于LSP的违反也潜在的违反了OCP 
    

DIP:依赖倒置原则

高层模块不应该依赖于底层模块二者都应该依赖于抽象

抽象不应该依赖于细节细节应该依赖于抽象

  1.     什么是高层模块?高层模块包含了应用程序中重要的策略选择和业务模型。这些高层模块使其所在的应用程序区别于其它。 
    
  2.     如果高层模块依赖于底层模块,那么在不同的上下文中重用高层模块就会变得十分困难。然而,如果高层模块独立于底层模块,那么高层模块就可以非常容易的被重用。该原则就是框架设计的核心原则。 
    
  3.     这里的倒置不仅仅是依赖关系的倒置也是接口所有权的倒置。应用了DIP我们会发现往往是客户拥有抽象的接口,而服务者从这些抽象接口派生。 
    
  4.     这就是著名的Hollywood原则:"Don't call us we'll call you."底层模块实现了在高层模块声明并被高层模块调用的接口。 
    
  5.     通过倒置我们创建了更灵活 更持久更容易改变的结构 
    
  6.     DIP的简单的启发规则:依赖于抽象;这是一个简单的陈述,该规则建议不应该依赖于具体的类,也就是说程序汇总所有的依赖都应该种植于抽象类或者接口。 
    
  7.     如果一个类很稳定,那么依赖于它不会造成伤害。然而我们自己的具体类大多是不稳定的,通过把他们隐藏在抽象接口后面可以隔离不稳定性。 
    
  8.     依赖倒置可以应用于任何存在一个类向另一个类发送消息的地方 
    
  9.     依赖倒置原则是实现许多面向对象技术多宣称的好处的基本底层机制,是面向对象的标志所在。 
    

ISP:接口隔离原则

不应该强迫客户程序依赖它们不需要的使用的方法。

  1.     接口不是高内聚的,一个接口可以分成N组方法,那么这个接口就需要使用ISP处理一下。 
    
  2.     接口的划分是由使用它的客户程序决定的,客户程序是分离的接口也应该是分离的。 
    
  3.     一个接口中包含太多行为时候,导致它们的客户程序之间产生不正常的依赖关系,我们要做的就是分离接口,实现解耦。 
    
  4.     应用了ISP之后,客户程序看到的是多个内聚的接口。
    

Apache的.htaccess文件是服务器的心脏,控制着网站访问的各种规则。这里提供了10个不错的.htaccess片段能够帮助你优化你的网站,包括重定向、性能、可用性等等!

强制后缀反斜杠

在URL的尾部加上反斜杠似乎对SEO有利 :)

    <IfModule mod_rewrite.c>
     RewriteCond %{REQUEST_URI} /+[^\.]+$
     RewriteRule ^(.+[^/])$ %{REQUEST_URI}/ [R=301,L]
    </IfModule>

防盗链

节省你宝贵的带宽吧!

    RewriteEngine On
    #Replace ?mysite\.com/ with your blog url
    RewriteCond %{HTTP_REFERER} !^http://(.+\.)?mysite\.com/ [NC]
    RewriteCond %{HTTP_REFERER} !^$
    #Replace /images/nohotlink.jpg with your "don't hotlink" image url
    RewriteRule .*\.(jpe?g|gif|bmp|png)$ /images/nohotlink.jpg [L]

重定向移动设备

加入你的网站支持移动设备访问的话,最好还是重定向移动设备的访问到专门定制的页面

    RewriteEngine On
    RewriteCond %{REQUEST_URI} !^/m/.*$
    RewriteCond %{HTTP_ACCEPT} "text/vnd.wap.wml|application/vnd.wap.xhtml+xml" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} "acs|alav|alca|amoi|audi|aste|avan|benq|bird|blac|blaz|brew|cell|cldc|cmd-" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} "dang|doco|eric|hipt|inno|ipaq|java|jigs|kddi|keji|leno|lg-c|lg-d|lg-g|lge-" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT}  "maui|maxo|midp|mits|mmef|mobi|mot-|moto|mwbp|nec-|newt|noki|opwv" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} "palm|pana|pant|pdxg|phil|play|pluc|port|prox|qtek|qwap|sage|sams|sany" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} "sch-|sec-|send|seri|sgh-|shar|sie-|siem|smal|smar|sony|sph-|symb|t-mo" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} "teli|tim-|tosh|tsm-|upg1|upsi|vk-v|voda|w3cs|wap-|wapa|wapi" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} "wapp|wapr|webc|winw|winw|xda|xda-" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} "up.browser|up.link|windowssce|iemobile|mini|mmp" [NC,OR]
    RewriteCond %{HTTP_USER_AGENT} "symbian|midp|wap|phone|pocket|mobile|pda|psp" [NC]
    #------------- The line below excludes the iPad
    RewriteCond %{HTTP_USER_AGENT} !^.*iPad.*$
    #-------------
    RewriteCond %{HTTP_USER_AGENT} !macintosh [NC] #*SEE NOTE BELOW
    RewriteRule ^(.*)$ /m/ [L,R=302]

强制浏览器下载指定的文件类型

你可以强制浏览器下载某些类型的文件,而不是读取并打开这些文件,例如MP3、XLS。

<Files *.xls>
  ForceType application/octet-stream
  Header set Content-Disposition attachment
</Files>
<Files *.eps>
  ForceType application/octet-stream
  Header set Content-Disposition attachment
</Files>

火狐的跨域名字体嵌入

火狐不允许嵌入一个外站的字体,下面的.htaccess片段可以绕过这个限制

<FilesMatch "\.(ttf|otf|eot|woff)$">
<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "http://yourdomain.com"
</IfModule>
</FilesMatch>

使用.htaccess缓存 给网站提速

恐怕这个是最有用的代码片段了。这段代码能帮你极大的提高网站的速度!

    # 1 YEAR
    <FilesMatch "\.(ico|pdf|flv)$">
    Header set Cache-Control "max-age=29030400, public"
    </FilesMatch>
    # 1 WEEK
    <FilesMatch "\.(jpg|jpeg|png|gif|swf)$">
    Header set Cache-Control "max-age=604800, public"
    </FilesMatch>
    # 2 DAYS
    <FilesMatch "\.(xml|txt|css|js)$">
    Header set Cache-Control "max-age=172800, proxy-revalidate"
    </FilesMatch>
    # 1 MIN
    <FilesMatch "\.(html|htm|php)$">
    Header set Cache-Control "max-age=60, private, proxy-revalidate"
    </FilesMatch>

阻止WordPress博客的垃圾评论

还在为垃圾评论头疼吗?你可以用Akismet插件来解决这个问题,但是.htaccess文件来的更直接:阻止垃圾评论机器人访问wp-comments-post.php文件。

    <IfModule mod_rewrite.c>
        RewriteEngine On
        RewriteCond %{REQUEST_METHOD} POST
        RewriteCond %{REQUEST_URI} .wp-comments-post\.php*
        RewriteCond %{HTTP_REFERER} !.*yourdomainname.* [OR]
        RewriteCond %{HTTP_USER_AGENT} ^$
        RewriteRule (.*) ^http://%{REMOTE_ADDR}/$ [R=301,L]
    </IfModule>

重定向不同的feed格式到统一的格式

很多年前,有很多不同的feed格式,例如RSS、Atom、RDF等等。但是现在RSS已经占了绝对的主导地位。下面这段代码可以让你重定向不同的feed格式到同一个feed。这段代码可以直接在WordPress博客上使用。

<IfModule mod_alias.c>
 RedirectMatch 301 /feed/(atom|rdf|rss|rss2)/?$ http://example.com/feed/
 RedirectMatch 301 /comments/feed/(atom|rdf|rss|rss2)/?$ http://example.com/comments/feed/
</IfModule>

记录PHP错误

# display no errs to user
    php_flag display_startup_errors off
    php_flag display_errors off
    php_flag html_errors off
    # log to file
    php_flag log_errors on
    php_value error_log /location/to/php_error.log

需求使用.htassess构建虚拟主机.

众所周知虚拟主机能绑定多个域名,但是这个似乎也没啥用,因为它不能分目录绑定,那么如果屌丝想用虚拟主机建立多个网站怎么办呢?.htaccesss 实现。

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^it.hansk.org$
    RewriteCond %{REQUEST_URI} !^/it/
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule ^(.*)$ /it/$1
    RewriteCond %{HTTP_HOST} ^it.hansk.org$
    RewriteRule ^(.)?$ /it/index.php [L]
</IfModule>

对上面的虚拟化控制命令非常好用,那么如果我想批量绑定呢?
下面是未验证命令

RewriteCond %{HTTP_HOST} ^(bbs|blog|download).domain.com$
RewriteRule ^(.*)$ /%1/$1 [L, NC]

这样,会自动把bbs对应到bbs目录,blog对应到blog目录,download对应到download,要加新的域名时,只需要在上面那行按格式添加即可。

对于一个写Java的来说注入这个概念再熟悉不过了。Java Spring框架IOC机制被一个蛋疼的东西简单粗暴的借鉴到了actionscript中,这就是Robotlegs。所以就有了形如下面的应用:

//someplace in your application where mapping/configuration occurs
var myClassInstance:MyClass = new MyClass();
injector.mapValue(MyClass, myClassInstance);
//in the class to receive injections
[Inject]
public var myClassInstance:MyClass

当某个Class需要使用某个类实例的时候直接在声明的变量上面添加[Inject]元标签即可被框架自动赋值,这个方式大大的减少了开发工作量,作为一个深受As3蹂躏的客户端程序这时想到的是通过这一方式结束煎熬的素材引用问题。

当使用AS3开发网页游戏的时候,我们常常会用到很多Swf,这时SWF中的类属性(元件)引用就是一件麻烦的事儿。比如

A.swf 存在 链接名为A的显示对象,一般我们使用的时候常常会做如下操作

domain=new ApplicationDomain();  
domain = evt.currentTarget.applicationDomain;  
var cls:Class=domain.getDefinition(name) as Class;  
var a:A=new cls();
a.displayobj;   

我们做了很多操作仅仅是为了拿到dispalyobj对象,通过Robotlegs的来的灵感,通过一个小技巧来减少我们的工作量。

具体就是使用describeType,关于上面这个方法不再赘述,点击链接http://help.adobe.com/zh_CN/FlashPlatform/reference/actionscript/3/flash/utils/package.html。

另外一个知识点 元数据,这个东西简单点说就是Flash内部对于AS3类描述的一种数据结构,具体的查看天地会关于元数据的描述

http://wiki.9ria.com/index.php/%E5%9C%A8Flash%E4%B8%AD%E5%B5%8C%E5%85%A5%E5%85%83%E6%95%B0%E6%8D%AE%E6%A0%87%E7%AD%BE

现在开始是我们这道菜的重点,怎么实现自定义类与SWF中的现实对象关联。先上代码瞧瞧


package com.hans.core.utils { import flash.display.DisplayObject; import flash.display.DisplayObjectContainer; import flash.utils.Dictionary; import flash.utils.describeType; import avmplus.getQualifiedClassName; /** *ReflectUtil.as: *<p>反射工具</p> *@author:Hans *@date:2012-4-15 */ public class ReflectUtil { /** *描述XML */ private var descriptXML:XML; /** *cache容器 */ private var reflectCache:Dictionary; /** * ReflectUtil实例 */ private static var _instance:ReflectUtil; /** * 构造反射实例 * */ public function ReflectUtil() { reflectCache = new Dictionary(); } /** * 映射素材 * */ public static function map2Res(reflectInstance:DisplayObject,resouce:DisplayObjectContainer):void { if(!_instance) { _instance = new ReflectUtil(); } _instance.reflect(reflectInstance,resouce); } /** * 将实际素材映射到目标Sprite上 * @param desc * @param orgiRes * */ private function reflect(target:Object,orgiRes:DisplayObjectContainer):void { this.descriptXML = describeTypeCache(target); var list : XMLList = descriptXML.*; var item : XML; //缓存容器 var propMap:Dictionary = new Dictionary(); for each (item in list) { var itemName : String = item.name().toString(); switch(itemName) { case "variable": propMap[item.@name.toString()] = item.@type.toString(); break; case "accessor": var access : String = item.@access; if((access == "readwrite") || (access == "writeonly")) { propMap[item.@name.toString()] = item.@type.toString(); } break; } } for(var prop:String in propMap) { var obj:Object = this.map(prop,orgiRes); target[prop] = obj!=null ? obj : target[prop]; } } /** * 映射素材元件 * @param property * @param resouce * */ private function map(property:String,resouce:DisplayObjectContainer):DisplayObject { var res:DisplayObject = resouce.getChildByName(property); return res; } /** * 获取xml描述 * @param desc * @return * */ private function describeTypeCache(desc:Object):XML { var name:String = getQualifiedClassName(desc); if(!this.reflectCache[name]) { this.reflectCache[name] = describeType(desc); } return this.reflectCache[name]; } } }