在php代码里写sql句子,我喜欢用sprintf,这样看起来比较清晰,但是今天就遇到了一个隐藏很深的bug。

有这样一段代码:

        public function update_catogery_ad($id, array $info){/*{{{*/
                if (!$id || !$info){
                        return false;
                }

                $sql = null;
                foreach($info as $a){
                        list($type, $key, $val) = $a;
                        switch($type){
                                case 'int':
                                        $val = intval($val);
                                        break;
                                case 'string':
                                        $val = "'"._es($val)."'";
                                        break;
                        }
                        $sql .= sprintf(" `%s` = $val,", _es($key)); #1
                }
                $sql = substr($sql, 0, -1);

                $sql = sprintf("update %s set $sql where id = %d", $this->_get_catogery_ad(), intval($id)); #2
                return $this->db->update($sql);
        }/*}}}*/

它执行的是,根据传入的info数组中的字段,更新某表。在测试环境,运行的很好,但是上线之后,有同事就反映无法更新数据,也不报错。

出问题的info数组是:

$info = array(

‘a’ => ‘bbbb’,

‘c’ => ‘<area target=”_blank” alt=”" href=”http%3A%2F%2Fitry.try8.info%2Ftaobao%2Ftry%2F%3Fcatogery%3D%E4%BC%98%E8%B4%A8%E8%89%AF%E5%93%81″ coords=”0,100,180,170″ shape=”rect”>’,

);

这里字段a可以被正常更新,但是字段c就维持原样。

仔细观察这段数据,可以看到字段c的value中含有%!在sprintf里,它会被当作特殊字符,于是第一条sql句子会报warning:Too few arguments,由于页面上display_errors关闭了,所以没有看到任何提示,这段代码会默默的执行下去,完成整个功能,看起来就好像c字段被忽略掉一样。

于是动手修改第一条sql句子为:$sql .= sprintf(” `%s` = %s,”, _es($key), $val);

再次运行,直接报sql句子为空!这时发现第二条sql句子也有同样的问题。于是将第二条句子修改为:$sql = sprintf(“update %s set %s where id = %d”, $this->_get_catogery_ad(), $sql, intval($id));

这些人为的bug会隐藏的很深!得多加小心,所以,编程习惯很重要!在使用sprintf的时候,如果变量中可能带有特殊字符,则一定要放在arg里,不能直接写入format里。

tokyo cabinet与tyrant一起,组成了一个可用的NoSQL数据库,cabinet是lib,tyrant是server。其性能和应用性毋庸置疑,希望可以学习,同时也对其稳定性持怀疑态度,希望可以看看是我用的不对,还是其源码有可改进的地方。

从tyrant/ttserver.c的main函数入手:

main(){
    ……
    g_serv = ttservnew();
    int rv = proc(dbname, host, port, thnum, tout, dmn, pidpath, kl, logpath,
                  ulogpath, ulim, uas, sid, mhost, mport, rtspath, ropts,
                  skelpath, mulnum, extpath, extpcs, mask);
    ttservdel(g_serv);
    if(extpcs) tclistdel(extpcs);
    return rv;
  }
}

省略的部分是命令行参数处理,ttservnew函数对TTSERV进行初始化,其中初始化了4个锁:

ttservnew(){
    ……
    if(pthread_mutex_init(&serv->qmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed");
    if(pthread_cond_init(&serv->qcnd, NULL) != 0) tcmyfatal("pthread_cond_init failed");
    if(pthread_mutex_init(&serv->tmtx, NULL) != 0) tcmyfatal("pthread_mutex_init failed");
    if(pthread_cond_init(&serv->tcnd, NULL) != 0) tcmyfatal("pthread_cond_init failed");
    ……
}
typedef struct _TTSERV {
    ……
    pthread_mutex_t qmtx;                  /* mutex for the queue */
    pthread_cond_t qcnd;                   /* condition variable for the queue */
    pthread_mutex_t tmtx;                  /* mutex for the timer */
    pthread_cond_t tcnd;                   /* condition variable for the timer */
    ……
}

这里就牵涉到两个重要的概念,timer线程和worker线程,稍候会介绍。qmtx和qcnd主要是供主线程和worker线程使用,tmtx和tcnd主要供主线程和timer线程使用。

可以看出,真正的处理逻辑是在proc函数里。这个函数里进行了大量的稳定、log、容错等处理,先都忽略(但是这些才保证了一份好代码的存在!)。

proc函数中主要进行了以下工作:

  1. 如果以daemon模式运行,则调用ttdaemonize函数,建立可运行的子子进程,父进程和子进程都退出
  2. tcadbopen函数,根据数据库文件,为TCADB对象赋值,对于hash类型的db而言,会将数据库文件mmap到共享内存中
  3. 调用ttservaddtimedhandler函数,建立一个tserv的timer对象,其handler是do_slave
  4. 如果有外部脚本相关的,则再调用ttservaddtimedhandler函数,建立N个timer对象,其handler是do_extpc
  5. 调用ttservsettaskhandler函数,将tserv的do_task赋值为do_task函数
  6. 调用ttservsettermhandler函数,将tserv的do_term赋值为do_term函数,处理退出
  7. do{ ttservstart(); }while(g_restart); 之类的g_restart值可以有signal改变,从而使ttserver可以再次执行ttservstart,以达到重启、reload的效果
  8. 在ttservstart函数中,是真正的业务逻辑

以下简单看看ttservstart函数:

  1. 根据host和port,建立socket,可以是unix原始套接口,也可以是TCP套接口
  2. 新建timer个数个线程,等待执行timer->do_timer函数
  3. 新建thnum个worker线程,以空的reqs初始化,等待执行timer->do_task函数
  4. epoll_wait:
    1. 如果是请求建立连接,则accept连接,并将accept的socket端口加入到epoll的监听队列里
    2. 如果是其他请求,则请求qmtx锁,将该端口push到serv->queue里,释放qmtx锁,并发送qcnd信号,触发worker线程

worker线程的主体就是do_task函数,其读取socket中的数据,如果以magic word打头,则是二进制格式的命令;否则就是memcached兼容的命令格式。根据请求内容,再分别调用如do_put、do_get等函数进行处理。

以do_get函数为例,主要是调用tcadbget函数,读取到内容,再通过socket返回给客户端。这里的tcadbget函数,会根据数据库的类型,调用其get函数进行读取,比如hash database,就使用tchdbget方法。tchdbget中,首先请求HDBLOCKMETHOD 的read lock(允许其他读,但是不允许写),对整个db加锁,然后调用tchdbbidx函数计算出key对应在hash数组中的位置,和二次hash的key(为了解决hash碰撞,对于第一次hash key相同的元素,使用tree再进行存储)。调用tchdbgetimpl函数,进行真正的获取数据。

tchdbgetimpl函数中:由于hash database使用了on-memory hash database作为缓存,所以,如果cache不为空,则先会执行tcmdbget在cache中查找,如果找到则返回,否则继续在真正的hash database里查找。首先找到bucket的偏移位置,对于hash database来说,一部分数据会被map到共享内存,如果bucket属于这部分数据,则从map中读取;否则从数据文件读取(可能存在bucket的一部分数据在map,另一部分在数据文件的情况)。

bucket的header数据结构为:

magic word | hash | left child off | right child off | padding size | size of key | size of value |

buckey里的数据是以树存储的,用上面计算出的二次hash的key,与bucket key比较,若二次key较小,则向左子树找;若较大则向右子树找;否则相等时,比较关键字,若相等,则找到结果,返回;如果虽然二次hash的值相等,但是比较关键字不等,则需要解决二次 hash的碰撞。

二次hash碰撞的解决方法,是沿着当前节点的左子树或者右子树下行直至找到二次hash值等、关键字也等的节点,或者找不到匹配的节点,则返回失败。

———————————- 小分割 ——————————————

2012-1-19

今天想解决3个问题:

  1. 复制数据库文件,与tcrmgr copy的区别;后者对锁的使用情况
  2. B+tree数据文件格式,为什么异常退出会导致数据丢失?
  3. 尝试用c代码读取ulog和数据文件的内容
tcrmgr copy的奥秘

tcrmgr copy的响应函数是proccopy,首先tcrdbnew数据库,并init了一个mutex锁,继而调用tcrdbcopy函数,先请求这个mutex锁,再进行具体的copy操作。

这里请求mutex锁的tcrdblockmethod函数,在多处被调用,比如get、put等等。这里是简单的CLI模式,每次数据库都是new的,mutex锁也是新建的,但是可以猜想,在多线程模式下,线程间的读写操作都是互斥的。(这里存疑,为什么用互斥锁,而非条件变量呢?)

tcrdbcopyimpl组装socket的copy包,通过tcrdbsend发送给ttserver。并通过ttsockgetc获取返回值。

可以看出,copy的奥秘不在tcrmgr这里,而是在ttserver怎样处理copy请求里。

ttserver.c的do_copy函数具体处理copy请求,针对B+tree,其实际处理函数为tcbdbcopy。

首先对数据库加写锁,排除一切的读写操作,从cache里读取叶节点和非叶节点,分别存储到lids和nids里。解锁。由于cache里的数据还未写入到数据文件里,所以针对每个节点,调用对应的save函数,写入到数据文件中(写入每个节点的前后加写锁、解锁)。

调用tcbdbtranbegin启动事务,加读锁,调用tchdbcopy函数(据说B+tree的底层存储其实是hash格式)将数据写到目的文件中,解锁。

实际执行复制操作的是tchdbcopyimpl,这里封装了两种操作,一种是直接调用tccopyfile进行复制,另一种是当目的文件以@开头,则执行命令。仅关注前者,很简单,就是将数据库文件中的内容读取出来,写入到目标文件。

那么tcrmgr copy和直接copy有哪些区别呢?

  • tcrmgr copy会先将cache中的数据写回到文件中;而直接copy不会,所以cache中的数据就丢失了
  • tcrmgr copy写备份文件的时候,会加读锁,排斥写,保证备份文件的数据格式是正确的;而直接copy时没有锁,如果正好遇到cache中的数据同步到数据库文件,则备份文件的格式可能是损坏的
B+tree数据库异常情况分析

从上面的copy过程,猜想,异常退出导致文件格式损坏的原因可能有:

  • 数据写在cache里,没有存入文件,cache里的新数据丢失
  • 存入文件过程中,异常退出,导致文件格式损坏,可能很多数据甚至整个文件都会丢失

具体需要看tcbdbputimpl函数。在该函数中,首先从history里查找key对应的叶节点的pid,如果找不到则从根节点开始二分查找,直至找到或者新建了一个leaf节点的pid(细节没看)。然后将新内容作为rec写入pid对应的leaf位置。之后如果需要,会对整个树进行调整

如果该节点所在的recs数目超过了启动ttserver时的预设值lmemb,或者该节点的的size超过了lsmax,则调整。调整的具体过程忽略,可以参考算法中的B+ tree部分。

否则,如果该节点的recs数目<1,则说明put失败,需要调用tcbdbleafkill函数切断该leaf节点与树的联系(疑问,这个leaf里应该还有其他旧数据,岂不是也丢失了?)。

如果不在事务里,则将数据写入cache。如果页节点的cache数目大于设定值lcnum,则需要将叶节点的cache内容写入到数据文件(又调用了hash的put函数,不清楚那边是否也有cache缓存),并清除叶节点cache的内容。同样处理非叶节点。那么,如果非正常退出,cache中的数据就有可能会丢失。

在将数据写入数据文件的过程中,注意到其rbuf的大小是BDBPAGEBUFSIZ,即32768,这就意味着,一个leaf的最大字节数是32768(数据部分应该是比这个要小)。

留个问题,cache里的数据难道不存在于数据文件中吗?

在csdn上看到有人问:mysql_connect后是不是非要mysql_close,是不是自动释放?

大家伙们,PHP数据库连接是不是在页面执行完之后自己释放,即使不用mysql_close()也可以释放掉?
同理PDO连接到数据库后,是不是非要将PDO实例赋值为NULL,才能将PDO连接断掉?

在N久以前我查过资料,但己经模糊不清了,具说PHP处理每一个被请求的页面时,里面有MYSQL连接的,当页面处理完就立刻释放掉所有用到的资料。如:mysql_connect后的连接。这个说法对吗?PDO同理吗?

第一反应是CGI模式下,mysql_connect是当前请求结束自动释放,而mysql_pconnect是可以被同一web进程(线程)中的多个请求重复使用的。但是为什么呢?

查看php.net上的帮助:

mysql_pconnect() acts very much like mysql_connect() with two major differences.

First, when connecting, the function would first try to find a (persistent) link that’s already open with the same host, username and password. If one is found, an identifier for it will be returned instead of opening a new connection.

Second, the connection to the SQL server will not be closed when the execution of the script ends. Instead, the link will remain open for future use (mysql_close() will not close links established by mysql_pconnect()).

This type of link is therefore called ‘persistent’.

首先,mysql_pconnect建立的链接不会在script结束后释放,那么对于CLI模式呢?从道理和实践代码中检测,都是会释放的。因为进程退出时,所有的资源都会被自动回收。通过以下的代码可以简单的验证:

$db = mysql_pconnect(‘localhost’, ‘mysql’, ‘pwd’);
sleep(100);
var_dump($db);
exit;
同时在mysql中执行:
mysql> show processlist;  # sleep期间
+—-+——-+—————–+——–+———+——+——-+——————+
| Id | User  | Host            | db     | Command | Time | State | Info             |
+—-+——-+—————–+——–+———+——+——-+——————+
|  1 | mysql | localhost:63950 | tshare | Sleep   |    1 |       | NULL             |
| 68 | mysql | localhost       | NULL   | Query   |    0 | NULL  | show processlist |
| 73 | mysql | localhost       | NULL   | Sleep   |   20 |       | NULL |
+—-+——-+—————–+——–+———+——+——-+——————+
3 rows in set (0.01 sec)
mysql> show processlist;  # 进程结束
+—-+——-+—————–+——–+———+——+——-+——————+
| Id | User  | Host            | db     | Command | Time | State | Info             |
+—-+——-+—————–+——–+———+——+——-+——————+
|  1 | mysql | localhost:63950 | tshare | Sleep   |    0 |       | NULL             |
| 68 | mysql | localhost       | NULL   | Query   |    0 | NULL  | show processlist |
+—-+——-+—————–+——–+———+——+——-+——————+
2 rows in set (0.00 sec)

那么CLI模式呢?将上面这段代码cp至一个web页面中,通过浏览器访问结束后,show processlist仍然可以看到有连接存在!这就使得多个web请求可以复用同一mysql链接,从而节省了建立连接的过程。但是这样也可能造成问题,就是当允许的web进程(线程)数目超过mysql的max_connections时,会造成web进程连接mysql服务器的阻塞,因为每个web进程都不释放自己的mysql连接,从而可能造成too many connections的mysql错误。可以参考我之前的blog:http://flykobe.com/index.php/2011/01/20/mysql-too-many-connections%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/

那么它会存在多久呢?与mysqld的wait_timeout相关。如果一直没有被复用,那么它会存在wait_timeout时长,单位是秒。

mysql> show variables like ‘wait_timeout’;
+—————+——-+
| Variable_name | Value |
+—————+——-+
| wait_timeout  | 28800 |
+—————+——-+
1 row in set (0.00 sec)
当然,如果web server的进程(线程)结束,mysql_pconnect建立的连接也会结束。

今天很有趣,我需要把一张mysql600多万数据、读写频繁的表拆分,趁着导测试数据的时间,总结一下我的2011.

2011年,最大的收获是亲近了佛法,完成了北京佛教研究所与北京广化寺开办的佛教初级和中级研修班的学习,并在正信、正见的道路上迈下了坚实的一步。

性格方面有了一些改变,之前我是个自认为蛮聪明、暴躁、急性子的人。而现在,走在路上,会突然警醒自己正在埋着头、飞快的迈着步子,于是刻意的放缓脚步、抬起头看看远方,发现生活中、道路上的种种美好。

在与人相处中,之前认为非黑即白,不可相容。而其实,善和恶,在一个短时间内是很难看得清楚的,强行的把我对善恶的观点加诸他人,其实不是因为我有多美好,而是我有多专制。

对于事业成功的定义,也有所变化。以金钱来衡量成功,其实很虚无。今年的高薪到了明年,可能就变成中下水平,而工作的愉悦程度、自身能力是否 稳步提高、企业文化才是略微真实和可把握的。

2011年是创业的一年。还行,公司能够保持正的资金流和一个相对稳定的发展态势。

2011年,我的技术进步可能稍慢,这一点比较遗憾。涉猎的范围比较杂乱, 部署了多台服务器,从apache转移到lighttpd,又转移到nginx;用了用NoSQL的cabinet存储,提高了性能,但是也遇到一些问题;做了mysql的主从备份,但是备份机没有发挥大的用途。做的最多的,还是业务层面的开发,年初对设计模式比较感兴趣,年末发现有些是过度设计,于是重构;遇到过性能瓶颈,从服务器、数据库和代码的角度进行了一些调优,但是也暴露出我经验不足的劣势。

2011年,家里又添了一只猫,现在有3个小家伙:噜噜、团团和咪宝。噜噜、团团仍然向肥胖不懈努力,咪宝在和病魔抗争,希望2012他可以健健康康!搬到了顺义,继续喂养小区里的流浪猫。也跟随公司在帮助几个流浪动物基地。

2011年,我的生活还不错,从空虚和追求享受,到学会从奉献和佛法中获取安定和快乐。

2012年,希望在修行的道路上能有进展,希望生活平静幸福,也希望技术方面可以有所突破。

休息十分钟,写写自己的感悟。

早晨看到一个朋友在工作群里感慨印度的见闻,落后、欺骗、不准时、贫穷等等,她的出发点是好的,描述这些情况,印证因果不爽,然后教导别人向善。

我作为被教育者之一,稍稍有些诧异。因为在我的观念里,印度是伟大佛陀的故乡,有很多久负盛名的禅修中心;当代的印度计算机业十分发达,有很多优秀的工程师;当然,也少不了《3 idiots》和《贫民窟的百万富翁》之类电影的熏陶。所以,我对印度还是充满了向往。而朋友言语描述的印度,充满了反差,也稍稍感觉有一些绝对。

恰逢最近在修《弟子规》功过格,自然而然的自省,我是否也有这样的以说教的口吻对待别人呢,并认为自己的看法是绝对正确、希望他人接受的呢?答案是肯定的!

《坛经》里说,若真修道人,不闻他人过。而我经常看到他人的过错,并揪着不放。比如挤公交车,我一般是始发站上车,都有位子,车子开到中途很挤的时候,很多人上车了就挤门口也不往里面走,外面的人就挤不上来。我看到这种情况,就会很愤慨的谴责别人,不为他人着想,只顾自己。甚至还为此与别人争吵过几次。事后,也都觉得自己是有理的,是善的,是清高的。而昨天,坐了另外一辆车,短途的,没几站我就要下了,于是我也不大愿意往后面走。当这个念头闪现的时候,就发觉,其实自己和他们没有区别。

当我指摘别人的时候,如果换位思考一下,也许结论就是不同的了。当然,坐公交车,礼让他人是肯定需要的,但是我指责的态度和做法都是非常有错误的!!我并不是代表着善,只是出于自私、我慢等心理在做这些事情。

经论和师父都说过,言语是苍白的,行动才是有力的。仍然以公交车为例,同学也说过,如果人多的时候,我先给年长者让个座位,然后自己走到最后面去,其实不需要在意其他人怎样。这些其实都是世间法,都是虚幻的,站在哪里、是否挤上这趟车、早晚,在广袤的时间和空间里都是没有意义的,因缘法皆空,重要的是,能否从这生活的点滴中,发掘出佛法的智慧?!

第一义谛中,没有善恶,只需要以清净平等心行住坐卧即可。

2011-12-31  开发中,积分系统;builder模式改造抽奖领奖流程

2011-12-31  待上线,修改代码,catch异常,即使tokyo崩溃,项目也可运行

2011-12-30  13:30人为操作导致tokyo server崩溃,数据丢失;15点左右,恢复一部分,使网页可运行;17点,全部恢复

安装

http://sourceforge.net/projects/clustershell/files/clustershell/1.5.1/ 下载源码安装。指定安装目录:

python setup.py install

新建配置文件:

mkdir /etc/clustershell

cp conf/* /etc/clustershell

新增配置文件groups:

[admin@v080049 apps]$ cat /etc/clustershell/groups
web: 192.168.80.49 192.168.80.105 192.168.80.106
db: 192.168.80.103 192.168.80.104
需要配置ssh的免密码登录。
使用

简单测试:clush -g web ‘ls’

部署代码:clush -g web “cd ~/prepare/happy_itry_v2/; echo ‘yes’ | ./taobao_push.sh “

双网卡,双IP配置,加静态路由

张映 发表于 2011-12-23

分类目录: 服务器相关

北网通,南电信的问题是很让人郁闷的一件,这也是河蟹社会的一种特色吧。为了解决这个问题,我考虑过三种方案:

1,双网卡,双IP;或者单网卡,双IP。

这种方案,成本低,但是维护挺麻烦,并且速度比后面二个要慢。

2,BGP双线机房。

BGP的费用要比第一种方案要高,但是全国真正是BGP机房的到底有多少,应当就那么几家。其他假的比较多。用这种方案就不用在搞双IP了,一个IP就OK。

3,CDN加速

CDN的价格是最高,买的是dell r410的服务器,拖管在机房,带宽160元/m/月,还是熟人才拿到这价格。我和chinacache的客户经理当面谈过,刚开始的价格是400元/m/月,后来我说是我朋友推荐的,直接降到200元/m/月,这价格降的真是离谱。我有一个linux运维的QQ群,在里面问chinacache的CDN多少钱一个月一M,有得说100多元/m/月,有的说900多元/m/月,卧槽,这差别太大。感觉不规范,所以没用,现在和朋友一起创业,能省就省,所以我选择了第一种方案。扯了这么多,进入正题。

一,服务器服务安排

服务器,我准备了三台dell r410的机器,

1,web服务器

2,mysql服务器

3,文件服务器

每台服务器有二个网卡,eth1走内网,eth0走外网,并且网通和电信都走eth0,这样的方式我个人觉得比一个网卡走网通,一个网卡走电信要快。在交换机上设置二个vlan一个走内网,一个走外网,交换机设置就不在这儿多说了。

二,网络配置

1,cd /etc/sysconfig/network-scripts

2,修改 ifcfg-eth1

  1. [root@localhost network-scripts]# cat ifcfg-eth1
  2. DEVICE=”eth1″
  3. NM_CONTROLLED=”yes”
  4. ONBOOT=”yes”
  5. TYPE=Ethernet
  6. BOOTPROTO=none
  7. IPADDR=192.168.1.2
  8. PREFIX=24
  9. DEFROUTE=yes
  10. IPV4_FAILURE_FATAL=yes
  11. IPV6INIT=no
  12. NAME=”System eth1″
  13. UUID=5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03
  14. HWADDR=78:2B:CB:57:28:E5

3,修改ifcfg-eth0

  1. [root@localhost network-scripts]# cat ifcfg-eth0
  2. DEVICE=”eth0″
  3. NM_CONTROLLED=”yes”
  4. ONBOOT=”yes”
  5. TYPE=Ethernet
  6. BOOTPROTO=none
  7. IPADDR=222.121.121.121
  8. PREFIX=24
  9. GATEWAY=222.121.121.1
  10. NETMASK=255.255.255.128
  11. DEFROUTE=yes
  12. IPV4_FAILURE_FATAL=yes
  13. IPV6INIT=no
  14. NAME=”System eth0″
  15. UUID=9c92fad9-6ecb-3e6c-eb4d-8a47c6f50c04
  16. HWADDR=78:2B:CB:57:28:E6

4,cp ifcfg-eth0 ifcfg-eth0:0

  1. [root@localhost network-scripts]# cat ifcfg-eth0:0
  2. DEVICE=”eth0:0″
  3. NM_CONTROLLED=”yes”
  4. ONBOOT=”yes”
  5. TYPE=Ethernet
  6. BOOTPROTO=none
  7. IPADDR=112.121.121.121
  8. PREFIX=24
  9. NETMASK=255.255.255.0
  10. DEFROUTE=yes
  11. IPV4_FAILURE_FATAL=yes
  12. IPV6INIT=no
  13. NAME=”System eth0″
  14. UUID=9c92fad9-6ecb-3e6c-eb4d-8a47c6f50c04
  15. HWADDR=78:2B:CB:57:28:E6

5,重起网络/etc/init.d/network restart

到这儿,双网卡,双IP基本上就配置好,但是网通和电信都是走的电信的网关,这样的话丢包会比较严重的。解决这个问题,有二个比较好的方法,一是加静态路由,一是加策略路由。我用的方法是加静态路由。

三,添加静态路由

route add -net 1.24.0.0 netmask 255.248.0.0 gw 60.12.105.145 dev eth0:0
route add -net 1.56.0.0 netmask 255.248.0.0 gw 60.12.105.145 dev eth0:0

上面是通过命令来加的,如果是双网卡的要加上dev的。因为静态路由有很多条,所以还是一起加比较好。

1,vim route.sh

2,把route add全部加到 route.sh里面

3,chmod +x route.sh

4,开机启动echo “sh /路径/route.sh” >> /etc/rc.local

下载网通路由,下载下来后,根据实际情况后修改

zz from:http://blog.51yip.com/server/1358.html

我们在淘宝开放平台上有一款店铺模块,顾名思义,其分为前台和后台模块两部分。前台模块展现在淘宝卖家的店铺页面里,比如店铺首页、列表页、宝贝详情页等。后台模块仅供购买了该APP的商家使用。

根据淘宝开放平台的规则,前台模块任何人可见,后台模块仅购买该APP的商家可见。这里的可见,即授权成功,由淘宝开放平台控制,仅当授权成功后,才会将控制权交到我们的APP上。

这里比较特殊的一点是,如果用子帐号,如“开心赚宝:开心分享”,登录淘宝,店铺后台模块是无法授权成功的。而同时,其访问店铺页面,又会以edit的模式来请求店铺后台模块!这样就导致了BUG:

父帐号未授权或应用不支持子帐号
 错误码:122

针对这个bug,个人认为目前开发者是没有办法的,只能依赖淘宝平台修复。

可能还是对前端理解不深入,总是觉得css、html是靠死记硬背,而非理解之后使用的。所以老记不住其中的一些东西。今天下决心要搞明白浮动和清理,以及浏览器兼容的各方面事情!

浮动

在w3c的文档里,如此描述浮动:

浮动的框可以向左或向右移动,直到它的外边缘碰到包含框或另一个浮动框的边框为止。由于浮动框不在文档的普通流中,所以文档的普通流中的块框表现得就像浮动框不存在一样。

这个导致的效果是,如果不清除浮动,则:

  • 若父元素里仅包含浮动的子元素,则父元素的宽、高会为0
  • 若子元素A为浮动,B不浮动,则B与A会重叠
  • 若子元素A、B都浮动,则顺序排列

一个div类型的块状元素,本来会占满100%的宽度,浮动后,就会自适应,仅占所需的区域了。

这样的代码:

<div>
       <div style="float:left">left</div>
       <div style="float:right">right</div>
       <div>non float</div>
</div>

产生的效果为:

可见第三个div占满了整个宽度,但是内容还是贴着左浮动的右边框了。

这时可以先清除浮动。

浮动的清理
<div>
    <div style="float:left">left</div>
    <div style="float:right">right</div>
    <div style="clear:both"></div>
    <div style="border:1px solid red;">non float</div>
</div>
产生的效果为:
浮动与块元素
所有被定义为浮动的元素都会被自动变为“块状元素”,也就是代表它可以定义宽度和高度了。
我遇到过的问题

1、多个浮动元素超出父元素宽度时,会折行显示,即超出部分会在下一行显示

2、最好在每组浮动结束后都清除掉浮动,否则会出现莫名其妙的问题。如果在firebug中看到某个父元素的height、width为0,就要注意了

———————————————

绝对定位的问题

子元素A一旦要做绝对定位,就需要将其父元素的postion显示指定,否则在不同浏览器下,会有不同的效果。

这时,需要注意父元素的overflow属性,这将决定着子元素若超出父元素的范围,是否可以显示。可以结合z-index一起使用。