雅虎提升web性能23条建议

Web Performance Best Practices and Rules

Yahoo!'s Exceptional Performance team has identified 34 rules that affect web page performance. YSlow's web page analysis is based on the 23 of these 34 rules that are testable. Click each performance rule below to see the details.

  1. Minimize HTTP Requests

  2. Use a Content Delivery Network

  3. Avoid empty src or href

  4. Add an Expires or a Cache-Control Header

  5. Gzip Components

  6. Put StyleSheets at the Top

  7. Put Scripts at the Bottom

  8. Avoid CSS Expressions

  9. Make JavaScript and CSS External

  10. Reduce DNS Lookups

  11. Minify JavaScript and CSS

  12. Avoid Redirects

  13. Remove Duplicate Scripts

  14. Configure ETags

  15. Make AJAX Cacheable

  16. Use GET for AJAX Requests

  17. Reduce the Number of DOM Elements

  18. No 404s

  19. Reduce Cookie Size

  20. Use Cookie-Free Domains for Components

  21. Avoid Filters

  22. Do Not Scale Images in HTML

  23. Make favicon.ico Small and Cacheable

分布式唯一标识snowflake算法

snowflake算法核心:

这里写图片描述

如图,64位。主要由3块构成:时间戳、工作机器id、序列号。 
– 其中第一位不用,也可以理解作为正负数来使用,默认正数的。 
– 随后41位表示时间戳,在实际使用时,可以当做时间差来使用,比如现在离2017-01-01 00:00:00的时间差。这样的话,时间范围就能达到: (2^41-1)/(1000*60*60*24*365)=69.7年。 
– 中间10位用于工作机器的。可以用于 2^10-1=1023台机器。 
– 最后12位表示序列号,一个机器在一个毫秒时最大能产生 2^12-1=4095个。 
**在实际应用中,可能无需最大化的,比如时间戳只用30位就能达到要求的就无需41位,其他的同理。 
工作机器ID,可以是进程级别。机器级别的话,可以使用机器的mac地址或ip地址经过算法;如果是进程级别的话,可以使用path+进程标识;也可以混编,列如前5位标识机器,后5位标识进程。 
关于序列号有个注意点,如果一个毫秒内,序列号已经达到上限,就等到下一毫秒,同时序列号置零开始。**

具体代码:

/**
 * 
 **
 /
 public class MagicSnowFlake {
    private final static long twepoch = 1483200000000l;    //10bit(位)的工作机器id  中IP标识所占的位数 8bit(位)
    private final static long ipIdBits = 8L;    //IP标识最大值 255  即2的8次方减一。
    private final static long ipIdMax = ~ (-1L << ipIdBits);    //10bit(位)的工作机器id  中数字标识id所占的位数 2bit(位)
    private final static long dataCenterIdBits = 2L;    //数字标识id最大值 3  即2的2次方减一。
    private final static long dataCenterIdMax = ~ (-1L << dataCenterIdBits);    //序列在id中占的位数 12bit
    private final static long seqBits = 12L;    //序列最大值 4095 即2的12次方减一。
    private final static long seqMax = ~(-1L << seqBits);    // 64位的数字:首位0  随后41位表示时间戳 随后10位工作机器id(8位IP标识 + 2位数字标识) 最后12位序列号
    private final static long dataCenterIdLeftShift = seqBits;    private final static long ipIdLeftShift = seqBits + dataCenterIdBits;    private final static long timeLeftShift = seqBits  + dataCenterIdBits + ipIdLeftShift;    //IP标识(0~255)
    private long ipId;    // 数据中心ID(0~3)
    private long dataCenterId;    // 毫秒内序列(0~4095)
    private long seq = 0L;    // 上次生成ID的时间截
    private long lastTime = -1L;    
    
    public MagicSnowFlake(long ipId, long dataCenterId) {        
        if(ipId < 0 || ipId > ipIdMax) {
            System.out.println(" ---------- ipId不在正常范围内(0~"+ipIdMax +") " + ipId);
            System.exit(0);
        }        
        if(dataCenterId < 0 || dataCenterId > dataCenterIdMax) {
            System.out.println(" ---------- dataCenterId不在正常范围内(0~"+dataCenterIdMax +") " + dataCenterId);
            System.exit(0);
        }        
        this.ipId = ipId;       
        this.dataCenterId = dataCenterId;
    }    
    
    public synchronized long nextId() {        
        long nowTime = System.currentTimeMillis();        
        if(nowTime < lastTime) {
            System.out.println(" ---------- 当前时间前于上次操作时间,当前时间有误: " + nowTime);
            System.exit(0);
        }        
        
        if(nowTime == lastTime) {
            seq = (seq + 1) & seqMax;            
            if(seq == 0) {
                nowTime = getNextTimeStamp();
            }
        } else {
            seq = 0L;
        }

        lastTime = nowTime;        
        return ((nowTime - twepoch) << timeLeftShift
                | (ipId << ipIdLeftShift)
                | (dataCenterId << dataCenterIdLeftShift)
                | seq;
    }    
    
    private long getNextTimeStamp() {        
        long nowTime;
        do {
            nowTime = System.currentTimeMillis();
        } while(nowTime <= lastTime);       
        return nowTime;
    }
}

/**---------------------------------------------------------------**/

 public class ThreadSnowFlake extends Thread {

    MagicSnowFlake msf;    
    int cnt = 0;    
    public ThreadSnowFlake(MagicSnowFlake msf) {        
        this.msf = msf;
    }    
    
    public void run() {
        System.out.println();        
        if(msf != null) {            
            while(cnt < 10) {
                System.out.println(Thread.currentThread().getId() + " : " + msf.nextId());
                cnt ++;
            }
        }
    }
}

/**-----------------------------------------------------------------------**/
public class AlgorithmMain {

    public static void main(String[] args) {

        MagicSnowFlake msf = new MagicSnowFlake(196, 2);

        ThreadSnowFlake t1 = new ThreadSnowFlake(msf);
        ThreadSnowFlake t2 = new ThreadSnowFlake(msf);

        t1.start();
        t2.start();
    }
}

杭派技术发展路线

“双 11”的保留项目:拜关公

 “山外青山楼外楼,西湖歌舞几时休。暖风熏得游人醉,直把杭州作汴州。”

  千年前的南宋临安,王侯将相在宫阙庙堂里主政,文人骚客在酒肆茶舍里风流,这是天子的杭州,更是诗人的杭州;1972 年,周恩来在“楼外楼”菜馆招待了美国总统尼克松,6 年后中国改革开放,西湖畔渐渐游人如织,商铺林立,这是游客的杭州,终究是浙商的杭州。

  2018 年,杭州城的生活是这样的:萧山机场 T3 航站楼的安检员每扫描 3000 个瑞士双肩包,就有 2200 个装着写满代码的笔记本;阿里西溪园区的星巴克,销量号称亚洲 Top 3,一天至少卖出 200 杯焦糖玛奇朵;余杭区的出租车司机每天能拉到 13 个谈论 App 开发的乘客,收到的现金不会超过 100 块,还有老外举着美钞,求师傅给他的支付宝转点钱;西湖银泰的新白鹿,每卖出一盘开背虾,就有两瓶勇闯天涯被喝掉,而 10 桌客人有 3 桌会谈论物联网、大数据、AI、区块链……小到充电宝、漂流雨伞,大到无人超市、无人停车场,除了西湖轮渡只收现金,在杭州,几乎一切需求都可以扫码解决。

  在君王、诗人、游客、商贾之后,如今,杭州城最风头无二的角色,非工程师莫属。

  时代让他们站在了新一轮“历史转折”的中心:整个杭州,乃至整个中国都在进行一场技术升级。向规模要增长正转变为向技术要增长。

  在这一进程中,三代杭派工程师悉数亮相——死扛的、敢赌的、自信的,他们是被时代选中的,也是自我成就的。

  初代登场:要么扛,要么死

  杭派工程师之所以自成一派,是因为他们的成长路径和环境,在中国互联网江湖乃至全球都实属罕见。

  十几年前的杭州,几乎没有什么大型互联网和科技企业,初代杭派工程师基本都是阿里人。直至马云将阿里巴巴送上神坛,人们回过头来看,依然会惊讶于这群人出发时竟如此草根。

  现在需要过五关斩六将才能拿到阿里 offer 的工程师们很难想象,刚在前不久入围《MIT 科技评论》TR 35(“MIT 全球 35 位 35 岁以下科技创新青年”)的红雪(许寄,现任蚂蚁金服国际事业群技术负责人),2007 年入职支付宝时,只有高中文凭。他入职的原因是阿里是招聘会上唯一一家不问他学历的公司。

  在杭派工程师的江湖高人里,这样的草根背景不是个例。

  2000 年,由于互联网泡沫破灭,钱不够烧了,阿里把总部从上海又搬回到杭州。自知不在“世界中心”的阿里有独特的招人策略:不是瞄准最精英的人,而是瞄准最有成长潜力,最能自我驱动的人。曾在阿里主管人事的卫哲在一次演讲中回忆,他刚到阿里的 05、06 年,公司很少去清华招工程师,因为“清华北大,他永远有比阿里更好的职位”,而结果也证明,阿里很多最优秀的工程师来自华中科技大学、北京邮电大学……

  那时的阿里看谁都是人才,马云原话是“杭州大街上走的人都想招进来”。苗人凤(倪行军,现任支付宝事业群 CEO)是看杭州日报夹缝广告找上门来的;阿玺(胡喜,现任蚂蚁金服副 CTO)大学专业是英语;鲁肃(程立,现任蚂蚁金服 CTO,国际事业群 COO)是当年在上海交大念博士,给支付宝兼职写代码时被苗人凤连哄带骗,学也不上了,来的。这种草根氛围甚至震慑到了偶尔乱入的学霸——副总裁袁雷鸣毕业于北大法学院,许多年都不敢跟别人说自己是北大毕业的,旁人“你怎么混得那么差”的眼光扛不住。

  如果把中国工程师分为三派:西二旗是华山派,华强北是丐帮,杭派工程师则像明教。

  华山派,名门正派但苦大仇深,没在早高峰被挤掉过鞋不足以和他们谈人生;丐帮,起于草莽,顽强生长,江湖上没有他们做不出的硬件;“杭派工程师”则像明教的高手,来路野,武功高,一开始处在江湖核心之外,既不似北京靠近政治、学术中心,也不像深圳有特区政策和产业链加持,偏安一隅,却有了潜心修行的机会。

  阿里由此带着这批初代杭派工程师,在杭州缩减兵马安营扎寨,开始了和时间的漫长赛跑——跑过了濒临倒闭的危机,跑过了互联网泡沫后的凛冬,跑到了电商爆发的快车道上——终于,史诗般的“推背感”到来了。

  等到红雪入职的 2007 年,淘宝和支付宝的业务量与日俱增,他正好赶上了“推背力”最大的时期。这是“杭派工程师”淬炼技术的第一个阶段:迫在眉睫的业务逼得他们不得不熬夜通宵,并在每年的大日子怀着对风险的敬畏给关公送上旺旺雪饼。

“双 11”的保留项目:拜关公
“双 11”的保留项目:拜关公

  红雪告诉“甲子光年”,在他第一天到支付宝位于文三路华星时代广场的办公室报道时,如今已成为阿里合伙人的苗人凤、鲁肃等人都在“咔咔写代码”。

  红雪很快参与到当年 10 月启动的、由鲁肃和苗人凤主导的“账务三期”项目中。

  这是支付宝早期一次“伤筋动骨”的技术改造:由于支付宝原有系统已无法承担淘宝指数级的业务增长,整个数据和账务要搬到新的账务系统。老苗在开发期曾和人开玩笑:“如果这个项目做不好,我们都从 22 楼跳下去。”

  账务三期定在 2008 年 1 月 1 日发布,整个支付宝从 1 月 1 日零点停机,原定上午 8 点发布新系统,前 4 小时调换数据、后 4 小时核对数据。

  但在核对数据时,大家发现数据无法配平。到下午 3 点、发布时间延误 7 小时后,问题才得到解决。正当所有人松口气时,5 分钟的线上实测环节又出了 bug——数据还是对不上。

  千钧一发之际,鲁肃从房间的角落走向了查 bug 的电脑,拍了拍同事:“同学,让一让,我来。”

  办公室里随即响起键盘声,也只剩下键盘声。

  当支付宝公关部已经因为各种“携款潜逃”的谣传炸锅时,技术部这边却异常安静。到下午 5 点,鲁肃发现一个公式里的两个数值正好差了两倍,把“+”换成“-”后,数据终于配平了。

  从 2009 年开始的一年一度的大考——“双 11”,更是淬炼初代杭派工程师的最重要的战役。

  在技术实力逐渐成熟的今天,阿里和蚂蚁金服的工程师们是“喝着咖啡”过“双 11”的,淡定优雅。但在红雪的老同事,蚂蚁金服研究员俊义(陈亮)的记忆里,2009 年第一年“双 11”是在惊心动魄中结束的。直到当天午夜 12 点,支付宝技术部都回响着工程师们的大喊大叫:“系统快不行了,快帮我扩掉!扩X台!”

  “双 11”惊人的流量给系统带来了超出意料的冲击,只能靠两名产品工程师在当天手工扩容,直到 11 月 11 日 24 点,他们一直严阵以待,接受从“战场”四面八方传来的“呼救”。

  “如果那一天再多 4 秒,数据库就挂了。”俊义告诉“甲子光年”。犹如一场拳击赛,拳击手已经快倒了,这时裁判吹了终场哨。

  不过比起后面的业务量,这一年的惊险只是小儿科。

  2009 年,第一届双十一的销售额是 5000 万元;第二年,则爆炸式地增长到 9.36 亿元,翻了近 20 倍;第三年又进一步飙涨到 52 亿元……与此同时,支付宝系统需要承载的 TPS(每秒交易数)也一路达到前无古人的地步,2017 年的最新数据是 25.6 万笔/秒——世界上任何别的公司都没有经历过如此高的 TPS 场景。

2010 年到 2017 年,双十一需要处理的 TPS 峰值从 500 上升到了 25.6 万
2010 年到 2017 年,双十一需要处理的 TPS 峰值从 500 上升到了 25.6 万

  往事也都不是这些富有传奇色彩的大战,还有日复一日,琐碎而艰苦的行军。

  2004 年,淘宝刚把数据库从 MySQL 换成甲骨文时,版本内部逻辑一直不对,最快的解决方法是“重启”。淘宝的早期工程师之一子柳在《淘宝技术这十年》一书中记录了当时的情况:工程师们不得不 24 小时开着手机,不管多晚,一旦收到“SQL Relay 进程挂起”的短信报警,就得从睡梦中爬起,打开电脑,连上机房的网络,重启服务。干这事最多的是三丰(姜鹏),现在是淘宝网的总裁,子柳调侃道:“于是我们知道,任何牛B的人物,都有一段苦B的经历。”

  回头看,初代杭派工程师的成长是外界的业务驱策和自我驱动的共同结果:

  极端业务场景逼迫他们必须走入未知领域,要么扛,要么死。最初几年的情况常常是,今年的技术架构刚刚好够撑今年的“双 11”,如果不在特定时间内完成升级,明年“业务就开展不了了”。这个过程中,学历和过去的经验都没用,最终能“逆袭”的人,是关键时刻不掉链子,自我迭代能力极强的“硬汉型工程师”。

  俊义向“甲子光年”说了一句特别真实的话:“我们的挑战就是源于业务对我们的需求。不是别人不聪明,是因为他们没有遇到这样的挑战。”

  不过现在,他们说起最苦的打仗岁月,反而特别怀恋。

  “写代码是最快乐的事。”红雪告诉“甲子光年”。俊义用了一个字,“嗨”。“做账务三期时,大家写得非常嗨。老苗、鲁肃、红雪他们,在一个闭关室里,放着歌写代码,边写边这样”俊义说着模仿起大家当年摇头晃脑的样子。

  最近两年,为了找点“战斗”回忆,鲁肃、苗人凤、阿玺、红雪、俊义、玉伯等“老人”组建了一个“夕阳红战队”,以参加蚂蚁金服内部不时举办的各种技术比赛。

  2018 年 5 月中旬的一个周日,为了准备今年的蚂蚁金服“黑客马拉松”,这些已经做到 CTO、阿里合伙人、P10、P11(P是阿里内部的技术职阶,最高级是 P14 的首席科学家)级别的高管变回了一线工程师,他们从上午 10 点忙到凌晨 2 点,做出了一个众筹 APP,这个项目的名字叫“谁说夕阳红不值钱”。“不出意外的话,今年应该又是最后一名。”俊义笑了。红雪补充道:“我和俊义、鲁肃、阿玺都是‘高P低能’的集中体现。”

  以前,这帮人晚上做完发布会去万塘路的“阿三烧烤”喝酒吃串,不过这次,他们熬完夜后就尽早回家,毕竟不是过去无牵无挂的单身汉了。

  对这些曾经的一线工程师来说,主要靠业务驱动技术演进的慌乱岁月已经结束。他们的作息和他们的事业都进入了更沉稳的阶段,需要思考更宏观的技术图景和未来布局。

  这也正是阿里、蚂蚁金服和中国的许多科技公司的工程师们正在经历的蜕变。

  二代接棒:敢下注的人

  如同每个经典武侠小说情节一样,初出茅庐的愣头青并不知道自己未来会成为大侠,但每上一步,就有了一层新境界。从初代杭派工程师到二代杭派工程师的跨越就是这么发生的。

  今年春节后入职阿里的李响 2012 年从浙大毕业时,他和他的浙大同学们还不觉得阿里和支付宝(2014 年成立的蚂蚁金服的前身)是技术公司,也不认为杭州是个理想的就业城市。

  在美国读完硕士后,李响进入硅谷公司 CoreOS。吸引他留在硅谷的原因是硅谷工程师思维上的特点:创造问题、想全新解法、“一直搞到很底层”。这种思维方式在中国互联网圈并不多见,但 2010 年之后,阿里开始萌发类似转变。

  2010 年的“IT 领袖峰会”上,马云、马化腾和李彦宏有一场交锋。李彦宏说:“云计算是新瓶装旧酒。”马化腾说:“云计算是一个好概念,但真正普及需要时间。”马云说:“如果我们不做云计算,将来会死掉。”

  此前一年的 9 月 10 日,阿里云在阿里巴巴 10 周年纪念日时正式成立,当时外界并不看好阿里对云计算的投入;在内部,关于阿里云是使用现成的开源技术还是走自研路线也有争议。阿里云创始人王坚主张后者。他的目标,是做一套中国自主研发的云计算大规模操作系统,把成千上万台普通 PC 服务器连到一起,实现超级计算机的能力。这将替代主要由 IBM 小型机、Oracle 数据软件和 EMC 存储设备构成的互联网行业主流底层架构。后来阿里巴巴提的“去 IOE 化”(IOE 即指 IBM、Oracle 和 EMC)就萌芽于此。

马云在阿里云成立仪式上鸣锣
马云在阿里云成立仪式上鸣锣

  草根背景的阿里在彼时彼刻似乎并没有足够的技术说服力。最开始,阿里云一度被认为是不知天高地厚的骗子项目。据说最困难的阶段,80% 的工程师离开了阿里云。而现在,阿里云工程师已经更迭到第 5 代。

  多年后,王坚做客《朗读者》节目时有一段肺腑之言:“(阿里云是)我们的工程师拿命来填的,其实我们的客户也是拿命来填的,就像第一天用电的人是拿命来填的,因为电会电死人的,第一天坐飞机的人也是拿命来换的。”

  2014 年阿里云支撑了天猫“双 11”571 亿的交易量并实现了零漏单、零故障; 2015 年春节期间,阿里云帮助官方订票网站 12306 平稳渡过春运售票高峰。现在,“阿里云”不再被认为是“骗子项目”,还成了阿里良好的技术判断力的例证。

  阿里云让阿里实现了某种意义上的进化:“技术驱动”的比重逐渐增加,一些技术本身发展成了业务。

  正是从阿里云开始,第二代杭派工程师就接过了接力棒——“抢险队员”变成了“长跑选手”,技术,开始成为他们最看重的一张底牌——投入往往不动声色,令人后知后觉,但一旦成势,就难以撼动。

  相比快速增长、直接影响消费者的电商、交易和支付,底层技术的投入需要走入无人区的魄力和长期的坚守。

  同样赌准长期技术方向的还有蚂蚁金服的由正祥(阳振坤,蚂蚁金服高级研究员)领导开发的 OceanBase(OB)团队。他们也决定挑战“去 IOE”这个看起来不可能的任务。

  其实正祥在加入阿里之前,已在数据库领域坐了十几年冷板凳——他一心想做分布式数据库,却没有遇到有足够耐心的公司,努力多年的项目也一度被停掉。

  2010 年加入阿里后,正祥领衔 OceanBase,这是一个分布式数据库,可以使用多台普通 PC 存储、处理巨量数据,好处是成本低,难题是稳定性,所以甲骨文等厂商采用的集中式系统一直是业界主流。

  虞舜(师文汇,蚂蚁金服资深运维专家)告诉“甲子光年”,到 2012 年底,OB 团队同样面临严重的“业务价值”危机,因为更换底层架构需要技术磨合且有一定的风险,当时阿里内部并没有业务愿意在 OB 上跑。

  最难的时候,正祥把虞舜叫去聊技术理想:“不能让中国的银行一直用美国的底层。”

  转机在 2013 年,蚂蚁金服 CTO 鲁肃看到了 OB 对金融交易数据的价值,OB 从阿里来到了蚂蚁金服。鲁肃随后决定,在 2014 年的“双 11”中,让 1% 的交易库数据在 OB 上跑。

  “把最核心的交易系统放在一个自研的分布式数据库上,在世界范围内真的没人做过。”虞舜说。

  2015 年“双 11”,全部交易数据链和支付数据链都跑到了 OB 上;到了 2017 年,所有核心数据现在都跑在了 OB 上。

  2017 年,曾差点“散伙”的 OB 团队获得了蚂蚁金服 CEO 大奖,团队所有人都挂上了有金色带子的醒目工牌。

OceanBase 团队获得蚂蚁金服 CEO 大奖
OceanBase 团队获得蚂蚁金服 CEO 大奖

  阿里云和 OB,这两个以“前瞻技术布局”为出发点的典型项目现在已发展成集团的独立业务,开始对外赋能。如南京银行的互联网“鑫云”就建立在 OB 和阿里云的底层上。这和阿里最初主要从市场需求出发做电商、做支付的逻辑已十分不同。

  这也对技术人才提出了新的要求:单纯会写代码已不足够,更重要的是技术决策力,是使用、整合技术工具的大局思维。

  回顾前两代“杭派工程师”打怪升级的江湖传说,背后是整个中国互联网从市场需求驱动的牵引力,向供给驱动的内推力演变的逻辑。

  90 年代末,浙江发达的民营经济培育了阿里巴巴的电商生态,做电商要解决信任问题,就有了支付宝。电商和交易发展到一定阶段,向市场、运营要增长不那么管用了,中国互联网已走过了粗放型的高速增长阶段,必须开始向技术要增长。

  两个阶段看起来路径不同、驱动力不同,但杭派工程师们的精气神一脉相承:不问出身、敢想敢做、敢为天下先。

  这进一步造就了工程师在杭州的地位。

  眼下,阿里巴巴 36 名合伙人中,12 位是技术出身,阿里、蚂蚁的技术岗员工占比都超过了 60%,蚂蚁两个最核心事业群,支付宝事业群和国际事业群的一二把手,也是技术出身。

  阿里巴巴的多隆(蔡景现)更是一个技术传奇,淘宝搜索引擎的代码是他一个人写出来的。如今身为 P11(相当于副总裁)和阿里合伙人,他却一直没被要求带团队——阿里让多隆专注做他最擅长的事,他一个人就是一支军队。

  “现在不会有人再怀疑,阿里、蚂蚁金服是技术公司。”李响的想法已与 2012 年从浙大毕业时产生了很大变化。“硅谷做云的、做底层架构的华人工程师,如果要考虑加入国内公司,首选阿里。”

  三代进阶:成为输出者

  在前两代的工程师的积淀之后,此时此刻,杭派工程师的目标是“星辰大海”,他们正在走向世界更多角落,扩散自己的技术标准、技术价值观。

  战斗意志,是“杭派工程师”对外输出的最鲜明的东西。

  从 2015 年开始,蚂蚁金服从印度市场入手,切入南亚和东南亚的本地支付业务,曾经的“高中生”红雪现在是蚂蚁国际化事业部的技术负责人,3 年来,红雪和近千名“杭派工程师”同事们已经在东南亚打造了 9 个“本地版支付宝”。

  红雪告诉“甲子光年”,在向东南亚输出移动支付技术时,积累了技术能力的杭州工程师们发现事情并不是“降维打击”这么简单,国内的很多成功经验,在国外不是好不好的问题,而是对不对的问题。小到一个代码管理、文档管理,大到上线发布流程、应急处理预案,皆和国内不同,而且国家和国家之间还有差异。

  “我们有一句土话,丢人别丢到国外去了。但软件工程是不可能 0 Bug 的,一定会有问题。我们只能进行更多测试,把问题先暴露出来。我们还在不断改我们的系统,不要说改商业系统,改内部小二的工作系统都改死你。”红雪说。

  技术之外,不同文化下的不同工作方式是初期最困扰杭派工程师们的一点。和过去一样,最好的协调方式是“一起打仗”。

  在向印度本地支付公司 Paytm 输出技术和运营模式时,项目启动不到一年,赶上印度政府突然废除大面额纸钞,废钞后 3 个月内,Paytm 接入的本地商户数从 200 万涨到 600 万,翻了 3 倍。快速的业务增长,让本来轻松的办公室氛围变得紧张,印度的同事们也体会到了发布时的“压力山大”和终于发布后的全员欢动。

  类似的,在越南、菲律宾输出本地支付技术的过程中,也经历了几次线上促销。等红雪再去东南亚时,有本地的工程师对他说:“我一辈子都没想过我会这么努力去工作。”

蚂蚁金服区块链和国际事业群团队在跨境支付上线成功后庆功现场
蚂蚁金服区块链和国际事业群团队在跨境支付上线成功后庆功现场

  在输出战斗意志的同时,杭派工程师也在对外输出技术规范。

  2015 年阿里内部启动了一件得罪广大工程师的事:在全集团范围内统一 Java 编码规约。

  作为全球范围内 Java 语言最强的公司,阿里巴巴内部的各部门、以及集团内部的各公司之间,在写 Java 时没有统一的规范。这是全世界的常态,就像每个作者有自己的文风一样,每个工程师也有自己的代码习惯。

  “一些人说,四个空格开成两个空格,我宁肯辞职;有人说你再坚持两个空格,我火把都准备好了。”Java 规约的发起者孤尽(杨冠宝,高级技术专家)告诉“甲子光年”,统一规范的过程极其艰难。

  孤尽记得最夸张的一次是阿里另一个团队的 leader 专门挑周六的时间请孤尽喝茶,两人从早上 8 点理论到晚上 8 点,谁都没有说服谁。“特别较真,你这样做故障能减少吗?证据是什么?工程师之间流行一句话:No data,no bb。”孤尽说。

  经过近两年的探索,2017 年 2 月,千辛万苦实现内部“书同文”的阿里对外推出了《阿里巴巴 Java 开发规约》。杭州于是成了全球 Java 规范的策源地,配合开发规约推出的编码插件在全球范围内被下载了 35 万次。

  过去的中国互联网出海更多是“产品出海”,现在则进入到更深层次的“技术出海”、“技术标准出海”。

  与十年前相比,经过三代杭派工程师的努力,在互联网版图中,杭州已从过去的角落进入核心圈层,并在某些特定领域表现出成为输出源头的潜质。

  一边是对外输出,一边是向内吸引,愿意成为杭派工程师的人数正在壮大。新来者更精英了,也更具有国际化背景。

  以蚂蚁金服为例,公司内部技术人员中,来自北大、清华、浙大等全国排名前 7 的高校的人数占到了 37%。BOSS 直聘研究院的《2018 中国海归人才就业选择报告》显示,2018 年上半年应届海归人才最青睐的城市中,杭州海归占比增幅居全国首位,增幅为 0.66%。相比之下,北京、上海的海归人才占比增幅都呈负增长,分别下降了 1.28% 和 0.26%。

  从阿里西溪总部星巴克的“咖啡指数”可以大致判断阿里内部工程师背景的变化,店员们观察到的规律是:中国工程师更钟情焦糖玛奇朵,而外籍工程师则喜欢冰美式或卡布奇诺。现在,“冰美式”的比例正在逐步上升。

  一座城市的技术升级

  如果说之前“杭派工程师”的主体在阿里巴巴、在蚂蚁金服。近几年来,随着杭州技术创业生态的繁荣,杭派工程师的构成变得更加多元化。

  去年,创立于广州的无人机公司奇志科技,在杭州设立了研发部门。“广州人才池很小,深圳成本太高,房价跟北京差不多。去年看了人才报告,杭州人才流入比例最高。”奇志科技联合创始人 Cash 向“甲子光年”解释“空降”杭州的原因。

  更多技术类创业公司的诞生、落户,吸引着全国乃至全球的技术人才,杭派工程师这一群体的面貌正“持续迭代”。

  阿里依然是绕不开的策源地之一。

  2014 年阿里巴巴上市后,一批财务自由的阿里人开始转行做投资或自己创业,充实了杭州的创投生态。有赞科技、蘑菇街、数澜科技等公司的创始团队都有阿里背景。

  2017 年,有 1026 家阿里系创业公司登上“阿里校友创业黄埔榜”。创业主要集中在新零售和人工智能领域。据鲸准数据显示,从阿里出来的人创立的项目中,有近一半扎根在杭州。

  浙江大学是另一个引擎。

  根据浙大管理学院科技创业中心发布的《2017 浙江大学创新创业生态蓝皮书》,浙大系创业项目落户杭州的比例高达 51.93%。杭州本土的知名创业公司中,Rokid 创始人 Misa、云象区块链的创始人黄步添、趣链科技创始人李启雷、蘑菇街联合创始人陈琪等都来自浙大。

  2017 年,杭州新成立的创业公司数量已比肩北京、上海、深圳,且人均创业密度已排名全国第一。

2017 年北上深杭创业密度(数据来源:元璟资本《2016-2017 杭州创业趋势分析》)
2017 年北上深杭创业密度(数据来源:元璟资本《2016-2017 杭州创业趋势分析》)

  值得注意的是,以云计算产业、数据服务为代表的企业服务创业比例攀升,根据元璟资本《2016-2017 杭州创业趋势分析》,2017 年企业服务领域创业公司成为杭州第一大方向,占比超过 30%,排名第二、第三的分别是金融(14%)和电商(10%)。

  过去的杭州是市民的、商业的杭州,在阿里巴巴更多投入技术前,电商创业是杭州的主流。但现在,技术创业成为新增长点,更加渴求技术力量的杭州,吸引了更多工程师。

  根据猎聘网发布的《2018 年中国互联网工程师大数据与调研报告》,从 2017 年第一季度到 2018 年的第二季度,杭州以 13.6% 的流入率,成为互联网工程师净流入排名第一的城市,主要来源是上海和北京。

  源源不断涌如的工程师们,在现在这个时间点的杭州,有平台拼,也有余裕“玩”。

  奇志科技的 Cash 发现,有人做过一个 GitHub 榜单,显示所有中国开源项目里,作者签名地址最多的就是杭州,这是杭派工程师的分享精神和技术热情的例证。

  在云象区块链的会议室里有 13 把椅子和一块不断更新技术思路的白板,上面的字迹就没干过。偶尔,这块白板上也会出现其他杭州区块链公司的名字,比如,imToken。

  10 公里外,全球最大的以太坊钱包公司 imToken 的会议室里放着 5 张折叠床——方便工程师们通宵加班。Blue 的大部分时间都在办公室度过,作为首席安全官,他要 7×24 小时待命,容不得半点疏忽。

  有赞的 90 后女工程师冬冬已是 4 岁孩子的妈妈。在 2014 年刚从阿里加入有赞时,她每周都有几天会加班到凌晨 3 到 4 点。即使现在开始带团队,一线工作减少了,她仍然习惯在完成整整一天工作后多待一会儿,因为这样可以静下心来看看最新的技术进展资料。

  杭派工程师们会干活,也会玩。不过他们“玩”的不是杭州的美景,还是技术。

  imToken 的首席安全官 Blue 刚入职时,为了让全公司注意到安全的重要性,就小小玩了把“恶作剧”。他采取“社会工程学攻击”方法,模拟 imToken 创始人何斌的邮件地址,在某个周六给全体员工群发邮件,要求所有人填写安全反馈。只有一个员工发现了蹊跷,追溯到这是一封钓鱼邮件,后来何斌给这名员工奖了一枚以太坊。

  生活中可用技术代劳的事,杭派工程师们都不惜一试,边玩边练手。

  比如为了搞定取花名这件事,宋爽(刘永凯,阿里算法专家)专门写了个代码,他找了 1000 多本武侠小说,把里面的所有人名抠出来,看哪些没被用过,最终找到了《大唐双龙传》里的一个人物宋爽。

  之后宋爽又和其他 4 个同事一起做了一套“刷菜支付”产品,通过计算机视觉技术识别食物,实现自动结账,可以提高排队、买饭的效率。“其实这个项目跟我本身的工作没一点关系。”宋爽说,但阿里和蚂蚁内部的“20% 业余时间文化”鼓励大家鼓捣和工作无关的兴趣。

  阿里 IoT 产品专家木酱(代立晨)则把他的“20% 业余时间”用到了“居家”上。他使用了一套开源智能家居系统改造自己的房子,让这套系统学习他的生活习惯:“我回家之前,灯和空调自动开;我睡觉之前,卧室的空调会预热;我起床之前,客厅的空调会预热;我离开时这些东西都会自动关掉。”

  自从有了这套系统,木酱出差不管住多么好的酒店,都感到不满意。不过,最近他家的系统也得升级一次了,因为木酱交了女朋友,也是工程师,他们正着手把单人智能家居系统改造成能学习多人生活习惯的系统。

  这些工程师们自己玩出来、探索出来的项目,也很有可能变成正式的事业。此前淘宝的“聚划算”、蚂蚁金服的“二维码”移动支付都是例子。玩与拼,拼与玩随时切换。

  比如杭州区块链技术公司秘猿科技的创始团队是一帮开源社区和加密数字货币的爱好者,其中谢晗剑曾是以太坊社区的核心开发人员,现在,当初完全出于兴趣的尝试成了他的事业方向。

  在蚂蚁金服里,最新的例子是区块链。

  由于负责清算、账务资金等业务,蚂蚁金服的高级技术专家寇恂(李奕)和裴秀(李玄)在 2015 年时关注到了区块链技术。

  2016 年蚂蚁黑客马拉松中,裴秀、寇恂拉其他几个同学用几个通宵搞了一个区块链公益项目 POC(概念验证),可以溯源每一笔善款的来源、去向,保证善款不被滥用。这也是后来蚂蚁金服上线的第一个实际运转的区块链项目。虽然是“玩出来”的业务,但眼下,蚂蚁金服正在“用当年做阿里云的决心去投入区块链”。

  能在工作之外乐此不彼“玩一把”的原因之一是,杭派工程师不用考虑太多别的东西。

  根据猎聘调查,杭州工程师是全国最有幸福感的工程师,比排名第二的上海还高出 10 个百分点。“有价值感的工作”“杭州美景及江南文化”“惬意的生活”构成了杭派工程师幸福感的三大来源。以通勤来说,每日通勤时间在 1 个小时以内的杭派工程师超过 67%,而在北上广深这一数字均未超过 50%,其中北京有 13.56% 的工程师通勤时间在 2.5 小时以上。

北、上、广、深、杭互联网工程师幸福感对比
北、上、广、深、杭互联网工程师幸福感对比

  但收入上杭州又并没有被甩下。杭州工程师的平均年收入和北上广深一线城市的同行们同处于 20 万之上。

不同城市互联网工程师收入排名
不同城市互联网工程师收入排名

  尚未达到一线城市水平的房价和相对高的收入,使得杭派工程师容易安心。“此心安处是吾乡”。他们普遍恋爱、结婚很早。“甲子光年”接触了 30 位来自不同公司的“杭派工程师”,他们的年龄跨度为 22 岁—33 岁,其中已婚者 25 人,19 人是在杭脱单的。

  周末,当北京 30 岁的程序员边打 Dota2 边摸索外卖餐包里的牙签时,杭州 25 岁的工程师也许正躺在郊外的草地上,嚼着女儿塞进嘴里的荔枝。

  对杭派工程师们来说,正在冲刺期的杭州有两面:整座城市在变得更有科技感,是实现技术抱负的舞台;但它尚保留着江南水乡的风流和温柔,保留着一点让人想生活下去的松弛感。

  “Hello World”

  杭派工程师只是中国崛起的工程师力量中的一个门派,作为一个整体,工程师在整个社会的辐射力正越来越强。

  如果稍微浮想一下,我们现在也正处于一种思维范式的历史转折中,数据、量化、机器日益成为文明的重要拼图。

  作为最了解机器的人群之一,工程师也已从半个世纪前隐匿在网络角落的少数极客、主流世界的边缘人,变成了如今支撑经济发展、技术迭代、深远影响每个普通人衣食住行的重要群体。

  在飞往杭州的班机的邻座位置,在阿里工位的书格上,在有赞 CTO 崔玉松的办公室里,“甲子光年”记者都看到了桥水基金创始人达里奥写的畅销书《原则》。达里奥就是一个思维上的工程师,他把个人管理、公司管理的问题都理解为机器、工程问题,在桥水内部用各种可量化、可复用的方式来处理决策、信息分享和人事评价的难题。

  不过,工程师们传递给世界最宝贵的经验和价值,不仅仅是理性、计算和逻辑,而是几次潮起潮落后,对世界不灭的好奇。

  2016 年的蚂蚁金服年会上,舞台特效还原了《黑客帝国》里的代码漫天坠落的经典场景,蚂蚁金服 CTO 鲁肃在代码里挥舞着手臂上台,最后千万行代码汇成一句话,这也是 1969 年人类的第一封电子邮件写给世界的话:

  Hello, World!

2016 年蚂蚁金服年会上鲁肃穿着“hello world”T恤登场
2016 年蚂蚁金服年会上鲁肃穿着“Hello World”T恤登场

Redis 链表实现

Redis封装了链表,下面看redis如何实现的链表

typedef struct listNode{
    struct listNode *prev;
    struct listNode *next;
    void *value;
}listNode;


typedef struct list{
    listNode *head;//头指针
    listNode *tail;//尾指针
    unsigned long len;//链表包含节点数量
    void *(*dup)(void *ptr);//节点复制
    void (*free)(void *ptr);//节点释放
    void (*match)(void *ptr,void *key);//节点值比较
    
}list;

Redis sdshdr字符串结构优点

Redis里面使用sdshdr结构体来保存字符串,结构体结构如下:

struct sdshdr{
    int len;//已使用保存的字符串长度
    int free;//未使用字符串长度
    char buf[];保存字符串的数组
}

redis_sdshdr.png

这里test字符串保存到buf数组中,“\0”表示字符串结束标志,len表示字符长度,free表示未使用空间为0

redis_sdshdr2.png

这里我们也是存了test数组,不过buf却有多余的空间,这个free=2表示的就是这个多余的空间,下面说下redis 使用sdshdr结构体的好处。

  1. 获取字符串长度快到极致,直接读取len

  2. 防止缓冲区溢出,因为可以读取len判断是否溢出

  3. 减少内存重分配次数通过free来预分配和惰性释放

  4. 二进制安全(防止\0过滤)

  5. 兼容部分c字符串函数stirng.h

排序算法-堆排序-php

<?php
#堆排序
function heapSort(&$arr) {
    #初始化大顶堆
    initHeap($arr);
 
    #开始交换首尾节点,并每次减少一个末尾节点再调整堆,直到剩下一个元素
    for($end = count($arr) - 1; $end >= 0; $end--) {
        $temp = $arr[0];
        $arr[0] = $arr[$end];
        $arr[$end] = $temp;
        ajustNodes($arr, 0, $end - 1);
    }
}
 
#初始化最大堆,从最后一个非叶子节点开始,最后一个非叶子节点编号为 数组长度/2 向下取整
function initHeap(&$arr) {
    $len = count($arr);
    for($start = floor($len / 2) - 1; $start > 0; $start--) {
        ajustNodes($arr, $start, $len - 1);
    }
}
 
#调整节点
#@param $arr    待调整数组
#@param $start    调整的父节点坐标
#@param $end    待调整数组结束节点坐标
function ajustNodes(&$arr, $start, $end) {
    $maxInx = $start;
    $len = $end + 1;    #待调整部分长度
    $leftChildInx = ($start + 1) * 2 - 1;    #左孩子坐标
    $rightChildInx = ($start + 1) * 2;    #右孩子坐标
 
    #如果待调整部分有左孩子
    if($leftChildInx + 1 <= $len) {
        #获取最小节点坐标
        if($arr[$maxInx] < $arr[$leftChildInx]) {
            $maxInx = $leftChildInx;
        }
 
        #如果待调整部分有右子节点
        if($rightChildInx + 1 <= $len) {
            if($arr[$maxInx] < $arr[$rightChildInx]) {
                $maxInx = $rightChildInx;
            }
        }
    }
 
    #交换父节点和最大节点
    if($start != $maxInx) {
        $temp = $arr[$start];
        $arr[$start] = $arr[$maxInx];
        $arr[$maxInx] = $temp;
 
        #如果交换后的子节点还有子节点,继续调整
        if(($maxInx + 1) * 2 <= $len) {
            ajustNodes($arr, $maxInx, $end);
        }
    }
}
 
$arr = array(1, 5, 3, 7, 9 ,10, 2, 8);
heapSort($arr);
print_r($arr);
?>

js模块化编程之彻底弄懂CommonJS和AMD/CMD!

答:因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。
但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!

于是下面三个模块规范出来了,这篇文章也出来了(拼出来的 {捂脸笑})。

 

JS中的模块规范(CommonJS,AMD,CMD),如果你听过js模块化这个东西,那么你就应该听过或CommonJS或AMD甚至是CMD这些规范咯,我也听过,但之前也真的是听听而已。 现在就看看吧,这些规范到底是啥东西,干嘛的。本文包括这三个规范的来源及对应的产物的原理。

 

一、CommonJS

1.一开始大家都认为JS是辣鸡,没什么用,官方定义的API只能构建基于浏览器的应用程序,逗我呢,这太狭隘了吧(用了个高端词,嘎嘎),CommonJS就按耐不住了,CommonJS API定义很多普通应用程序(主要指非浏览器的应用)使用的API,从而填补了这个空白。它的终极目标是提供一个类似Python,Ruby和Java标准库。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用可以运行在不同的JavaScript解释器和不同的主机环境中。

在兼容CommonJS的系统中,你可以使用JavaScript开发以下程序:

 

(1).服务器端JavaScript应用程序
(2).命令行工具
(3).图形界面应用程序
(4).混合应用程序(如,Titanium或Adobe AIR)

2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志"Javascript模块化编程"正式诞生。因为老实说,在浏览器环境下,没有模块也不是特别大的问题,毕竟网页程序的复杂性有限;但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。NodeJS是CommonJS规范的实现,webpack 也是以CommonJS的形式来书写。

node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

var math = require('math');

然后,就可以调用模块提供的方法:

  var math = require('math');

      math.add(2,3); // 5

CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}

require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。

虽说Node遵循CommonJS的规范,但是相比也是做了一些取舍,填了一些新东西的。

不过,说了CommonJS也说了Node,那么我觉得也得先了解下NPM了。NPM作为Node的包管理器,不是为了帮助Node解决依赖包的安装问题嘛,那它肯定也要遵循CommonJS规范啦,它遵循包规范(还是理论)的。CommonJS WIKI讲了它的历史,还介绍了modules和packages等。

下面讲讲commonJS的原理以及简易实现:

1、原理

浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量。

 

  • module

  • exports

  • require

  • global

 

只要能够提供这四个变量,浏览器就能加载 CommonJS 模块。

 

下面是一个简单的示例。

 

var module = {
  exports: {}};(function(module, exports) {
  exports.multiply = function (n) { return n * 1000 };}(module, module.exports))var f = module.exports.multiply;f(5) // 5000

 

上面代码向一个立即执行函数提供 module 和 exports 两个外部变量,模块就放在这个立即执行函数里面。模块的输出值放在 module.exports 之中,这样就实现了模块的加载。

 

2、Browserify 的实现

知道了原理,就能做出工具了。Browserify 是目前最常用的 CommonJS 格式转换的工具。

请看一个例子,main.js 模块加载 foo.js 模块。

 

// foo.js
module.exports = function(x) {
  console.log(x);};// main.jsvar foo = require("./foo");foo("Hi");

 

使用下面的命令,就能将main.js转为浏览器可用的格式。

 

$ browserify main.js > compiled.js

 

Browserify到底做了什么?安装一下browser-unpack,就能看清楚了。

 

$ npm install browser-unpack -g

 

然后,将前面生成的compile.js解包。

 

$ browser-unpack < compiled.js[  {    "id":1,    "source":"module.exports = function(x) {\n  console.log(x);\n};",    "deps":{}  },  {    "id":2,    "source":"var foo = require(\"./foo\");\nfoo(\"Hi\");",    "deps":{"./foo":1},    "entry":true  }]

 

可以看到,browerify 将所有模块放入一个数组,id 属性是模块的编号,source 属性是模块的源码,deps 属性是模块的依赖。

 

因为 main.js 里面加载了 foo.js,所以 deps 属性就指定 ./foo 对应1号模块。执行的时候,浏览器遇到 require('./foo') 语句,就自动执行1号模块的 source 属性,并将执行后的 module.exports 属性值输出。

 

3、Tiny Browser Require

虽然 Browserify 很强大,但不能在浏览器里操作,有时就很不方便。

 

我根据 mocha 的内部实现,做了一个纯浏览器的 CommonJS 模块加载器 tiny-browser-require 。完全不需要命令行,直接放进浏览器即可,所有代码只有30多行。

 

 

它的逻辑非常简单,就是把模块读入数组,加载路径就是模块的id。

 

function require(p){  var path = require.resolve(p);  var mod = require.modules[path];  if (!mod) throw new Error('failed to require "' + p + '"');  if (!mod.exports) {
    mod.exports = {};
    mod.call(mod.exports, mod, mod.exports, require.relative(path));  }  return mod.exports;}

require.modules = {};

require.resolve = function (path){  var orig = path;  var reg = path + '.js';  var index = path + '/index.js';  return require.modules[reg] && reg    || require.modules[index] && index    || orig;};

require.register = function (path, fn){
  require.modules[path] = fn;};

require.relative = function (parent) {  return function(p){    if ('.' != p.charAt(0)) return require(p);    var path = parent.split('/');    var segs = p.split('/');
    path.pop();    for (var i = 0; i < segs.length; i++) {      var seg = segs[i];      if ('..' == seg) path.pop();      else if ('.' != seg) path.push(seg);    }    return require(path.join('/'));  };};

 

使用的时候,先将上面的代码放入页面。然后,将模块放在如下的立即执行函数里面,就可以调用了。

 

<script src="require.js" />

<script>
require.register("moduleId", function(module, exports, require){
  // Module code goes here
});
var result = require("moduleId");
</script>

 

还是以前面的 main.js 加载 foo.js 为例。

 

require.register("./foo.js", function(module, exports, require){
  module.exports = function(x) {
    console.log(x);  };});var foo = require("./foo.js");foo("Hi");

 

注意,这个库只模拟了 require 、module 、exports 三个变量,如果模块还用到了 global 或者其他 Node 专有变量(比如 process),就通过立即执行函数提供即可。

二、AMD

基于commonJS规范的nodeJS出来以后,服务端的模块概念已经形成很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。还是上面的代码,如果在浏览器中运行,会有一个很大的问题,你能看出来吗?

 

  var math = require('math');

  math.add(2, 3);

 

第二行math.add(2, 3),在第一行require('math')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的。

这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。

 

因此,浏览器端的模块,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

 

CommonJS是主要为了JS在后端的表现制定的,他是不适合前端的,AMD(异步模块定义)出现了,它就主要为前端JS的表现制定规范。

AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。

AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:

  require([module], callback);

第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

  require(['math'], function (math) {

    math.add(2, 3);

  });

math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。目前,主要有两个Javascript库实现了AMD规范:require.jscurl.js

RequireJS就是实现了AMD规范的呢。

详细概括:下面以RequireJS为例说明AMD规范

一、为什么要用require.js?

最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。

 

  <script src="1.js"></script>
  <script src="2.js"></script>
  <script src="3.js"></script>
  <script src="4.js"></script>
  <script src="5.js"></script>
  <script src="6.js"></script>

 

这段代码依次加载多个js文件。

 

这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

 

require.js的诞生,就是为了解决这两个问题:

 

  

  (1)实现js文件的异步加载,避免网页失去响应;

  (2)管理模块之间的依赖性,便于代码的编写和维护。

 

二、require.js的加载

 

使用require.js的第一步,是先去官方网站下载最新版本。

 

下载后,假定把它放在js子目录下面,就可以加载了。

 

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

 

有人可能会想到,加载这个文件,也可能造成网页失去响应。解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:

 

  <script src="js/require.js" defer async="true" ></script>

 

async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。

 

加载require.js以后,下一步就要加载我们自己的代码了。假定我们自己的代码文件是main.js,也放在js目录下面。那么,只需要写成下面这样就行了:

 

  <script src="js/require.js" data-main="js/main"></script>

 

data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。

 

三、主模块的写法

 

上一节的main.js,我把它称为"主模块",意思是整个网页的入口代码。它有点像C语言的main()函数,所有代码都从这儿开始运行。

 

下面就来看,怎么写main.js。

 

如果我们的代码不依赖任何其他模块,那么可以直接写入javascript代码。

 

  // main.js

  alert("加载成功!");

 

但这样的话,就没必要使用require.js了。真正常见的情况是,主模块依赖于其他模块,这时就要使用AMD规范定义的的require()函数。

 

  // main.js

  require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){

    // some code here

  });

 

require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块。

 

require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

 

下面,我们看一个实际的例子。

 

假定主模块依赖jquery、underscore和backbone这三个模块,main.js就可以这样写:

 

  require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){

    // some code here

  });

 

require.js会先加载jQuery、underscore和backbone,然后再运行回调函数。主模块的代码就写在回调函数中。

 

四、模块的加载

 

上一节最后的示例中,主模块的依赖模块是['jquery', 'underscore', 'backbone']。默认情况下,require.js假定这三个模块与main.js在同一个目录,文件名分别为jquery.js,underscore.js和backbone.js,然后自动加载。

 

使用require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。

 

  require.config({

    paths: {

      "jquery": "jquery.min",
      "underscore": "underscore.min",
      "backbone": "backbone.min"

    }

  });

 

上面的代码给出了三个模块的文件名,路径默认与main.js在同一个目录(js子目录)。如果这些模块在其他目录,比如js/lib目录,则有两种写法。一种是逐一指定路径。

 

  require.config({

    paths: {

      "jquery": "lib/jquery.min",
      "underscore": "lib/underscore.min",
      "backbone": "lib/backbone.min"

    }

  });

 

另一种则是直接改变基目录(baseUrl)。

 

  require.config({

    baseUrl: "js/lib",

    paths: {

      "jquery": "jquery.min",
      "underscore": "underscore.min",
      "backbone": "backbone.min"

    }

  });

 

如果某个模块在另一台主机上,也可以直接指定它的网址,比如:

 

  require.config({

    paths: {

      "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"

    }

  });

 

require.js要求,每个模块是一个单独的js文件。这样的话,如果加载多个模块,就会发出多次HTTP请求,会影响网页的加载速度。因此,require.js提供了一个优化工具,当模块部署完毕以后,可以用这个工具将多个模块合并在一个文件中,减少HTTP请求数。

 

五、AMD模块的写法

 

require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。

 

具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

 

假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写:

 

  // math.js

  define(function (){

    var add = function (x,y){

      return x+y;

    };

    return {

      add: add
    };

  });

 

加载方法如下:

 

  // main.js

  require(['math'], function (math){

    alert(math.add(1,1));

  });

 

如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

 

  define(['myLib'], function(myLib){

    function foo(){

      myLib.doSomething();

    }

    return {

      foo : foo

    };

  });

 

当require()函数加载上面这个模块的时候,就会先加载myLib.js文件。

 

六、加载非规范的模块

 

理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。那么,require.js是否能够加载非规范的模块呢?

 

回答是可以的。

 

这样的模块在用require()加载之前,要先用require.config()方法,定义它们的一些特征。

 

举例来说,underscore和backbone这两个库,都没有采用AMD规范编写。如果要加载它们的话,必须先定义它们的特征。

 

  require.config({

    shim: {

      'underscore':{
        exports: '_'
      },

      'backbone': {
        deps: ['underscore', 'jquery'],
        exports: 'Backbone'
      }

    }

  });

 

require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。

 

比如,jQuery的插件可以这样定义:

 

  shim: {

    'jquery.scroll': {

      deps: ['jquery'],

      exports: 'jQuery.fn.scroll'

    }

  }

 

七、require.js插件

 

require.js还提供一系列插件,实现一些特定的功能。

 

domready插件,可以让回调函数在页面DOM结构加载完成后再运行。

 

  require(['domready!'], function (doc){

    // called once the DOM is ready

  });

 

text和image插件,则是允许require.js加载文本和图片文件。

 

  define([

    'text!review.txt',

    'image!cat.jpg'

    ],

    function(review,cat){

      console.log(review);

      document.body.appendChild(cat);

    }

  );

 

类似的插件还有json和mdown,用于加载json文件和markdown文件。(完)

 

另一个人的概括(有点简单):

AMD就只有一个接口:define(id?,dependencies?,factory);

 

它要在声明模块的时候制定所有的依赖(dep),并且还要当做形参传到factory中,像这样:

 

1 define(['dep1','dep2'],function(dep1,dep2){...});

 

要是没什么依赖,就定义简单的模块,下面这样就可以啦:

 

复制代码
1 define(function(){
2     var exports = {};
3     exports.method = function(){...};
4     return exports;
5 });
复制代码

 

咦,这里有define,把东西包装起来啦,那Node实现中怎么没看到有define关键字呢,它也要把东西包装起来呀,其实吧,只是Node隐式包装了而已…..

这有AMD的WIKI中文版,讲了很多蛮详细的东西,用到的时候可以查看:AMD的WIKI中文版

三、CMD

大名远扬的玉伯写了seajs,就是遵循他提出的CMD规范,与AMD蛮相近的,不过用起来感觉更加方便些,最重要的是中文版,应有尽有:seajs官方doc

1 define(function(require,exports,module){...});

用过seajs吧,这个不陌生吧,对吧。

前面说AMD,说RequireJS实现了AMD,CMD看起来与AMD好像呀,那RequireJS与SeaJS像不像呢?

虽然CMD与AMD蛮像的,但区别还是挺明显的,官方非官方都有阐述和理解,我觉得吧,说的都挺好:

官方阐述SeaJS与RequireJS异同

SeaJS与RequireJS的最大异同(这个说的也挺好)

 

以上这些只是为了学习做的总结,有部分摘自大牛原话,本人只是为了学习方便做的笔记,如有侵权,联系必删,致敬大牛!

kmp算法

#include<stdio.h>
#include<string.h>
void makeNext(const char P[],int next[])
{
    int q,k;
    int m = strlen(P);
    next[0] = 0;
    for (q = 1,k = 0; q < m; ++q)
    {
        while(k > 0 && P[q] != P[k])
            k = next[k-1];
        if (P[q] == P[k])
        {
            k++;
        }
        next[q] = k;
    }
}

int kmp(const char T[],const char P[],int next[])
{
    int n,m;
    int i,q;
    n = strlen(T);
    m = strlen(P);
    makeNext(P,next);
    for (i = 0,q = 0; i < n; ++i)
    {
        while(q > 0 && P[q] != T[i])
            q = next[q-1];
        if (P[q] == T[i])
        {
            q++;
        }
        if (q == m)
        {
            printf("Pattern occurs with shift:%d\n",(i-m+1));
        }
    }    
}

int main()
{
    int i;
    int next[20]={0};
    char T[] = "ababxbababcadfdsss";
    char P[] = "abcadfd";
    printf("%s\n",T);
    printf("%s\n",P );
    // makeNext(P,next);
    kmp(T,P,next);
    for (i = 0; i < strlen(P); ++i)
    {
        printf("%d ",next[i]);
    }
    printf("\n");

    return 0;
}

centos 修改默认python2.6升级为python3.4

  • 摘要:装了个Centos6.6桌面版,由于系统是python2.6而自己用python3.4.3,因此准备安装个python3.4,并当作默认。参考来源:http://www.centoscn.com/image-text/install/2015/1230/6590.html其实原文讲得很详细了,我在尝试过程中也没有出现问题,所以就简单1、下载python3.4,步骤略。我下载的是Python-3.4.3.tgz,然后解压tar-xf,命令略。2、切换到root用户,顺序执行下面

  • 装了个Centos6.6桌面版,由于系统是python2.6而自己用python3.4.3,因此准备安装个python3.4,并当作默认。参考来源:http://www.centoscn.com/image-text/install/2015/1230/6590.html

    其实原文讲得很详细了,我在尝试过程中也没有出现问题,所以就简单

    1、下载python3.4,步骤略。我下载的是Python-3.4.3.tgz,然后解压tar -xf,命令略。

    2、切换到root用户,顺序执行下面的。

    make /usr/python3.4

    ./configure –prefix=/usr/python3.4

    make

    make install

    3、替换原有的python

    cd /usr/bin

    mv python python.bak

    ln -s /usr/python3.4/bin/python3 /usr/bin/python

    4、更改yum的python

    vi /usr/bin/yum

    把第一行的#!/usr/bin/python改成#!/usr/bin/python2.6即可

  • 以上是centos6.6中安装python3并切换为默认的内容,更多 切换 默认 Python3 安装 CentOS 6.6 的内容,请您使用右上方搜索功能获取相关信息。