Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

服务器TIME_WAIT和CLOSE_WAIT区别及解决方案

$
0
0

  系统上线之后,通过如下语句查看服务器时,发现有不少TIME_WAIT和CLOSE_WAIT。

 

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

 

打印显示如下:

TIME_WAIT 297
ESTABLISHED 53
CLOSE_WAIT 5

 

     TIME_WAIT:表示主动关闭,通过优化系统内核参数可容易解决。

     CLOSE_WAIT:表示被动关闭,需要从程序本身出发。

     ESTABLISHED:表示正在通信

 

    通过上网了解,总结如下:

 

一、TIME_WAIT(通过优化系统内核参数可容易解决)

       TIME_WAIT是主动关闭连接的一方保持的状态,对于服务器来说它本身就是“客户端”,在完成一个爬取任务之后,它就会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。为什么要这么做?明明就已经主动关闭连接了为啥还要保持资源一段时间呢?这个是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:

        1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)

        2.可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。

        解决方案很简单,通过修改/etc/sysctl.conf文件,服务器能够快速回收和重用那些TIME_WAIT的资源  

        

#表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭  
net.ipv4.tcp_syncookies = 1  
#表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭  
net.ipv4.tcp_tw_reuse = 1  
#表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭  
net.ipv4.tcp_tw_recycle = 1
#表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间  
net.ipv4.tcp_fin_timeout=30

 

        生效,如下命令        

/sbin/sysctl -p

           

 

 

二、CLOSE_WAIT(需要从程序本身出发)

       TCP状态转移要点

       TCP协议规定,对于已经建立的连接,网络双方要进行四次握手才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本身占用的资源不会被释放。网络服务器程序要同时管理大量连接,所以很有必要保证无用连接完全断开,否则大量僵死的连接会浪费许多服务器资源.

        客户端TCP状态迁移:        

CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
     
         服务器TCP状态迁移:      
CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED

       当客户端开始连接时,服务器还处于LISTENING,客户端发一个SYN包后,他就处于SYN_SENT状态,服务器就处于SYS收到状态,然后互相确认进入连接状态ESTABLISHED。

 

       TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,总之不是由于自己程序错误导致的。

        但是CLOSE_WAIT就不一样了,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。

       什么情况下,连接处于CLOSE_WAIT状态呢?

       答案一:在被动关闭连接情况下,在已经接收到FIN,但是还没有发送自己的FIN的时刻,连接处于CLOSE_WAIT状态。通常来讲,CLOSE_WAIT状态的持续时间应该很短,正如SYN_RCVD状态。但是在一些特殊情况下,就会出现连接长时间处于CLOSE_WAIT状态的情况。

        答案二:出现大量close_wait的现象,主要原因是某种情况下对方关闭了socket链接,但是我方忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下errno,如果不是AGAIN,就断开连接。

        

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




用户研究经验分享

$
0
0

    前段时间参与了游戏用户的研究和分析,对于用户研究(以下简称用研)积累了一些经验,在此进行总结跟大家分享一下。

 

1. 为什么要用研(动机?解决什么问题?)

 

    做任何事情之前都可以多问为什么,这个习惯可以帮助自己思考的更加全面。


    在回答这个问题之前我们可以进一步思考:

    l)在什么情况下用研?

    2)不同场合的用研是不是有局限性?

    3)用研应该关注哪些内容?

    4)用研应该用什么方法,不同的方法在哪些场合使用?

 

    从用研的目的出发,主要是解决三个层面的问题:战略、产品、体验


1.1   战略层面

    为了支持企业的战略分析和战略规划而进行的用户研究,解决产品的方向问题。

    这个层面用研关注的内容包括:

    1)市场现状和发展趋势

    2)定位目标用户

    3)细分用户模型(年龄、爱好、背景等)

    4)寻找灵感,开拓蓝海

    5)竞品分析

 

    在这个阶段常用的用研方法有:

    1)问卷调查(定量,了解市场概况)   

    2)抽样市场调查(定量,用户范围缩小稍微有针对性)

    3)代表性用户访谈(定性,更准备定位目标用户)

    4)数据统计分析(定量+定性,寻找新机会和切入点)

 

    以下是诺基亚的一个用研例子:

    2006年,诺基亚在全球范围内启动了用户研究项目,这是目前移动行业最为全面的研究项目,研究人员共采访了全球16个国家/地区的42000名用户。根据这个用户研究的结果:

  (1)诺基亚将手机用户市场分为11个细分市场,这11个细分市场分类的推出,将使诺基亚更加贴近不同地区不同类型的用户。——用户细分战略

  (2)诺基亚将其手机产品分为四大类,即生活(Live),联系(Connect)、成就(Achieve)和探索(Explore)。四类手机将针对不同的用户,提供不同的功能和应用。——产品定位战略

    我们可以看到,诺基亚所做的全球性样本的用户研究,其中的一个最重要的目标是为了规划它未来的手机细分市场,并对自己的产品进行定位,它采用的方法主要是抽样的市场调查,典型用户访谈等。

 

1.2   产品层面

    为了支持产品设计而进行的用户研究,解决产品形态的问题

    这个层面用研关注的内容包括:

    1)使用场景:用户与产品接触的场景

    2)行为:用户在使用产品过程中的行为表现

    3)动机:用户为什么会用这个产品,这个产品帮助用户解决了什么问题

    4)喜好:用户的偏好直接影响产品形态

    5)用户需求:用户对产品功能的期望和评价(用户需求与产品需求是不同的)

    6)竞品分析:对竞品的产品形态进行用研,寻找不足,便于自身改进

 

    在这个阶段常用的用研方法有:

    1)抽样问卷调查(定量,了解相关产品的市场)    

    2)产品概念测试(定量,了解用户对于产品形态的喜好)

    3)深度访谈(定性,深入了解用户想法)

    4)焦点小组(定性,了解用户想法,通过用户思维交叉碰撞寻找新思路)

      

1.3   体验层面

    为了提升用户体验而进行的用户研究,解决可用性和易用性的问题

    这个层面用研关注的内容包括:

    1)可用性:是否达到了用户的预期,让用户可以正常使用

    2)易用性:使用过程中是否方便,更加人性化

    3)痛点:发现用户使用产品过程中遇到的问题和麻烦,是一个创新的机会

 

    在这个阶段常用的用研方法有:

    1)可用性测试(定性,寻找使用过程中的痛点)    

    2)实验室/现场用户测试(定量+定性,寻找使用过程中的痛点)

    3)眼动议(定性,主要针对交互和UI方面的深入研究)

 

2. 用研的常用方法


根据以上的分析,这里我们总结一下常用的用研方法

1)定量:网上问卷、邮寄问卷、现场问卷、抽样调查、数据统计分析

2)定性:QQ、QT、电话、面谈、公司访谈、眼动仪、焦点小组、可用性测试

 

备注:以上的用研方法可以公司策划,也可以委托第三方的机构进行用研

 

附:

用户研究工作中的14个经典方法:

http://www.yixieshi.com/ucd/12961.html

 

 

3. (实战)定性:焦点小组


    用研的目的:游戏用户需求洞察以及游戏社区平台竞品分析。属于产品层面的用户研究。

    本次用研采用焦点小组方式,委托第三方用研机构执行。这次焦点小组用研方式有几点比较深刻的体会:

3.1 优点:

    1)访谈用户有针对性挑选,具体有一定的代表性。本次参与者从年龄、游戏水平、游戏年龄、学历等方面进行的挑选,总体来说还是符合用研的目标用户。

    2)可以在单向镜后面观察用户,及时接收到用户的反馈。通过参与者表面的反馈,可以发现他们潜在需求。

    3)信息收集速度快、效率高。可以快速了解游戏用户的喜好,对各大社区平台的使用情况。

3.2 缺点:

    1)太过依赖主持人的水平,主持人的发挥直接影响到用研的效果。有一个问题主持人问了同一位参与者三次,我们听到快睡着了。

    2)参与者的观点不一定具有代表性。在用户选择上就已经存在缺失,不可能做到百分百,所以参与者的观点都是个人主观的,不能代表全部。

    3)善于发言的参与者会直接影响到其他人的观点。某一场用研过程中,两位参与者很热情,积极发言,结果导致有些人从头到尾只说了两句话。很多参与者的观点无法表达出来,直接影响到最终的结果。

    4)比较难有统一的结论,对于资料的整理和收集有一定的难度。信息量很大,需要人工观看录像和收听录音进行过滤,工作量比较大。

 

4. 产品形态分析


    本次用研属于产品层面,经过收集大量的用户调研之后,我们需要确定社区产品形态,这种形态上需要进行创新。

    目前市面上存在的社区产品形态主要有:门户资讯类、游戏视频、辅助工具、UGC类门户

    1)(资讯类)多玩网: http://www.duowan.com/

    2)(游戏辅助类)多玩盒子: http://lol.duowan.com/hezi/

    3)(资讯类)17173: http://www.17173.com/

    4)(UGC视频类)爱拍: http://www.aipai.com/

    5)(单机类)游民星空: http://www.gamersky.com/

    6)(腾讯社区)游戏人生: http://igame.qq.com

 

    面对这么多社区类产品,我们应该怎么突围,请留意后续《 合理构建产品形态




Author: Andy
Introduction: Web工程师、项目经理、产品经理
Sign: 做人如果没有梦想,跟咸鱼有什么区别 

作者:minidrupal 发表于2014-6-29 22:26:04 原文链接
阅读:69 评论:0 查看评论

JMeter使用记录1 -- JDBC测试

$
0
0

场景:使用jmeter对web应用和mysql数据库进行压力测试


JMeter是一款非常强大的测试工具,可以用来测试web,数据库,从07年用过之后一直对它情有独钟,下面记录下在一个项目中对它的简单使用。


项目分数据库测试和web应用测试

数据库测试比较简单,将要测试的数据库的jdbc driver放到jmeter的lib文件夹,新建线程组,配置起100线程,循环10次,10秒启动所有线程,加入一个jdbc connection configuration,加入要进行的CRUD操作,点击启动按钮,然后通过summary report看performance。



建立线程组,启动100个线程(10s起完),每个线程循环执行所有步骤10次,某个步骤出错也继续执行:



jdbc connection configuration:



CRUD中C,由于数据某些字段要求唯一性,所以加入了一个计数器(counter),来保证字段在数据库的唯一性:



Counter:



执行ctrl+R,在summary report里面可以看测试数据汇总


在view result tree中可以看每次请求的交互,方便debug



更多debug,可以通过jmeter界面,选项-》log viewer来帮助debug jmeter运行时遇到的问题,要希望log viewer里面显示更多的log,可以修改bin文件夹下面的jmeter.properties

log_level.jmeter=INFO 
log_level.jmeter.junit=DEBUG
#log_level.jmeter.control=DEBUG
#log_level.jmeter.testbeans=DEBUG
#log_level.jmeter.engine=DEBUG
#log_level.jmeter.threads=DEBUG
#log_level.jmeter.gui=WARN
#log_level.jmeter.testelement=DEBUG
#log_level.jmeter.util=WARN
#log_level.jmeter.util.classfinder=WARN
#log_level.jmeter.test=DEBUG
#log_level.jmeter.protocol.http=DEBUG
# For CookieManager, AuthManager etc:
#log_level.jmeter.protocol.http.control=DEBUG
#log_level.jmeter.protocol.ftp=WARN
#log_level.jmeter.protocol.jdbc=DEBUG
#log_level.jmeter.protocol.java=WARN
#log_level.jmeter.testelements.property=DEBUG
log_level.jorphan=INFO

作者:cloud_ll 发表于2014-6-29 17:23:44 原文链接
阅读:82 评论:0 查看评论

lucene索引创建的理解思路

$
0
0

虽然lucene4很早就出来,但是这里仍然以lucene3.0为基础,理解lucene索引创建的思路:

 

1. 要记录正向信息

 

field的数据,fdx,fdt,依次写每个field的即可

 

词向量,tvx,tvd,tvf

 

tvf是真正存储的地方,tvx是每个文档一项,具体包含第一个field的位置,其他field只要记录与覅一个field的偏移量即可

 

2. 记录反向信息

字典信息,tii,tis,通过字典,能够找到frq位置和prx位置

 

即,term1记录有此文档的倒排链表的位置,以及位置信息的链表

 

frq是倒排表,首先是倒排表,之后是跳跃表(肯定先放正常的倒排表,有了确定的位置后,方便建跳跃表),一层一层的建。

prx文档类似的

 

3. 如果自己来实现,如何实现?

(1)针对正向的数据fdx,fdt,就是一个文档的每个field的进来的原始数据,因此,可以直接保存,因为这不需要额外的分析就可以确定,可以直接写到磁盘的,顺序的写。

文档1

文档2

文档3

...

文档n

(2)针对正向信息的tvx,tvd,tvf等,肯定需要等每个field的term分析结束才行,否则不可能得到完整的信息,具体一个词在本文档中的词频,位置信息,也需要分析完term才能确定

field-->term

term->docid,freq,position

 

(3)tii,tis,frq,prx的形成,只有在一批文档完成后,才能形成,因为只有知道了所有的文档后,按文档id排序,才能构建

tii,tis

frq->docId1,freq1;docId2,freq2

prox->docId1,prox1;docId2,prox2

 

4. lucene3.0中的实现

剔除掉接口的描述,只看具体的实现,其实很清楚:

        --> code: DocFieldProcessor / DocFieldProcessorPerThread

              --> code: DocInverter / DocInverterPerThread / DocInverterPerField

                  --> code: TermsHash / TermsHashPerThread / TermsHashPerField

                      --> code: FreqProxTermsWriter / FreqProxTermsWriterPerThread / FreqProxTermsWriterPerField

                      --> code: TermVectorsTermsWriter / TermVectorsTermsWriterPerThread / TermVectorsTermsWriterPerField

                  --> code: NormsWriter / NormsWriterPerThread / NormsWriterPerField

              --> code: StoredFieldsWriter / StoredFieldsWriterPerThread

 

处理的层次的分类规则:

(1)是所有文档共享还是线程独立

(2)是正向还是反向的处理

 

所有文档共享:DocFieldProcessor

所有文档的共享(正向):StoredFieldsWriter

所有文档的共享(反向):DocInverter

 

线程的入口:DocFieldProcessorPerThread

线程的处理(正向):StoredFieldsWriterPerThread

线程的处理(反向):DocInverterPerThread

 

 

4.1 线程的处理(正向):StoredFieldsWriterPerThread

 

(1)DocumentsWriter有统一的内存管理

(2)每个线程写一个文档时,首先从池中获取一个内存块,写信息

(3)写文档结束后,直接将正向的fdt和fdx写到磁盘上,重用内存块

 

多个段之间的正向fdt和fdx是可以共享一个文件的。例如没有提交一批文档之前,如果内存满了,那么先flush,会生成tii,tis和frq,prx等。

之后后续commit了,会生成另一个段的tii,tis等,但是fdt和fdx是相同的。

 

4.2 线程的处理(反向):DocInverterPerThread

 

一个文档的反向信息要缓存的信息有哪些?

(1)term的文本

(2)term对应的docId,freq,prox信息

(3)上述信息的指针

 

每个field的有一个缓存的term的hash数组:此hash数组的对象,包含了指针信息:term的文本的指针;term对应的prox信息(遇见一次,记录一次)

 

后续的文档:当遇到同样的一个term,文本重用,同样的缓存frq,prox即可。

 

4.3 方向信息的生成:通过flush到磁盘时,从缓存中生成。

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



转注意Hibernate4在开发当中的一些改变

$
0
0

Hibernate4的改动较大只有spring3.1以上版本能够支持,Spring3.1取消了HibernateTemplate,因为Hibernate4的事务管理已经很好了,不用Spring再扩展了。这里简单介绍了hibernate4相对于hibernate3配置时出现的错误,只列举了问题和解决方法,详细原理如果大家感兴趣还是去自己搜吧,网上很多。

  1. Spring3.1去掉了HibernateDaoSupport类。hibernate4需要通过getCurrentSession()获取session。并且设置
    <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop>
    (在hibernate3的时候是thread和jta)。

  2. 缓存设置改为<prop key="hibernate.cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</prop>
                    <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>

  3. Spring对hibernate的事务管理,不论是注解方式还是配置文件方式统一改为:
    <bean id="txManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager" >  
    <property name="sessionFactory"><ref bean="sessionFactory"/>
    </property> 
    </bean>

  4. getCurrentSession()事务会自动关闭,所以在有所jsp页面查询数据都会关闭session。要想在jsp查询数据库需要加入:
    org.springframework.orm.hibernate4.support.OpenSessionInViewFilter过滤器。

  5. Hibernate分页出现 ResultSet may only be accessed in a forward direction 需要设置hibernate结果集滚动
     <prop key="jdbc.use_scrollable_resultset">false</prop>

 

 

--------------------------------------------------------------------

找到篇好文章,我之前遇到的问题都在这都能找到。其实出现这些问题的关键就是hibernate4和hibernate3出现了session管理的变动。

spring也作出相应的变动....

错误1:java.lang.NoClassDefFoundError: org/hibernate/cache/CacheProvider

原因:spring的sessionfactory和transactionmanager与支持hibernate3时不同。

解决:

 

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

<property name="dataSource" ref="dataSource"/>

...

</bean>

 

 

<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">

<property name="sessionFactory" ref="sessionFactory"/>

</bean>

 

 

错误2:java.lang.NoSuchMethodError:org.hibernate.SessionFactory.openSession()Lorg/hibernate/classic/Session

原因:hibernate4之后,spring31把HibernateDaoSupport去除,包括数据访问都不需要hibernatetemplate,这意味着dao需要改写,直接使用hibernate的session和query接口。

解决:为了改写dao,足足花了一天时间,然后一个个接口进行单元测试,这是蛋疼的一个主要原因。

 

错误3:nested exception is org.hibernate.HibernateException: No Session found for current thread

原因:发现一些bean无法获得当前session,需要把之前一些方法的事务从NOT_SUPPORT提升到required,readonly=true

见https://jira.springsource.org/browse/SPR-9020, http://www.iteye.com/topic/1120924

解决:

 

<tx:advice id="baseServiceAdvice" transaction-manager="transactionManager">

   <tx:attributes>

      <tx:method name="get*" read-only="true" propagation="REQUIRED"/><!--之前是NOT_SUPPORT-->

      <tx:method name="find*" read-only="true" propagation="REQUIRED"/><!--之前是NOT_SUPPORT-->

      <tx:method name="save*" propagation="REQUIRED"/>

      <tx:method name="update*" propagation="REQUIRED"/>

      <tx:method name="remove*" propagation="REQUIRED"/>

      <tx:method name="add*" propagation="REQUIRED"/>

      <!--默认其他方法都是REQUIRED-->

      <tx:method name="*"/>

   </tx:attributes>

</tx:advice>

 

 

错误4:与错误3报错类似,java.lang.NoSuchMethodError:org.hibernate.SessionFactory.openSession()Lorg/hibernate/classic/Session;

        at org.springframework.orm.hibernate3.SessionFactoryUtils.doGetSession(SessionFactoryUtils.java:324) [spring-orm-3.1.1.RELEASE.jar:3.1.1.RELEASE]

        at org.springframework.orm.hibernate3.SessionFactoryUtils.getSession(SessionFactoryUtils.java:202) [spring-orm-3.1.1.RELEASE.jar:3.1.1.RELEASE]

        at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter

 

原因:因为opensessioninview filter的问题,如果你的配置还是hibernate3,需要改为hibernate4 

<filter>

    <filter-name>openSessionInViewFilter</filter-name>

   <filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>

</filter>

 

--------------------------------------------------------------------

由于Hibernate4已经完全可以实现事务了, 与Spring3.1中的hibernatedao,hibernateTemplete等有冲突,所以Spring3.1里已经不提供Hibernatedaosupport,HibernateTemplete了,只能用Hibernate原始的方式用session:

        Session session = sessionFactory.openSession();
        Session session = sessionFactory.getCurrentSession();
在basedao里可以用注入的sessionFactory获取session.

注意, 配置事务的时候必须将父类baseServiceImpl也配上,要不然会出现错误:No Session found for currentthread, 以前是不需要的

SessionFactory.getCurrentSession()的后台实现是可拔插的。因此,引入了新的扩展接口 ( org.hibernate.context.spi.CurrentSessionContext)和

新的配置参数(hibernate.current_session_context_class),以便对什么是“当前session”的范围和上下文(scope and context)的定义进行拔插。

 

Spring @Transactional声明式事务管理,”currentSession”的定义为: 当前被 Spring事务管理器 管理的Session,此时应配置:

hibernate.current_session_context_class= org.springframework.orm.hibernate4.SpringSessionContext。

 

此处一定注意 使用 hibernate4,在不使用OpenSessionInView模式时,在使用getCurrentSession()时会有如下问题: 当有一个方法list 传播行为为Supports,当在另一个方法getPage()(无事务)调用list方法时会抛出org.hibernate.HibernateException: No Session found for current thread 异常。 这是因为getCurrentSession()在没有session的情况下不会自动创建一个,不知道这是不是Spring3.1实现的bug。 因此最好的解决方案是使用REQUIRED的传播行为



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



利用Inotify和Rsync将web工程文件自动同步到多台应用服务器

$
0
0


背景:需要搭建一套跟线上一模一样的环境,用来预发布,这是其中的web分发的一个小模块的实现过程。

 

1 工具以及环境简介

1.1,Inotify工具

Inotify,它是一个内核用于通知用户空间程序文件系统变化的机制。众所周知,Linux 桌面系统与 MAC 或 Windows 相比有许多不如人意的地方,为了改善这种状况,开源社区提出用户态需要内核提供一些机制,以便用户态能够及时地得知内核或底层硬件设备发生了什么,从而能够更好地管理设备,给用户提供更好的服务,如hotplug、udev 和 inotify 就是这种需求催生的。Hotplug 是一种内核向用户态应用通报关于热插拔设备一些事件发生的机制,桌面系统能够利用它对设备进行有效的管理,udev 动态地维护 /dev 下的设备文件,inotify 是一种文件系统的变化通知机制,如文件增加、删除等事件可以立刻让用户态得知,该机制是著名的桌面搜索引擎项目 beagle 引入的,并在 Gamin 等项目中被应用。

 

1.2,rsync工具

它是类unix系统下的数据镜像备份工具,实现远程同步remote sync,它的特性如下:

(1),可以镜像保存整个目录树和文件系统。

(2),可以很容易做到保持原来文件的权限、时间、软硬链接等等。

(3),无须特殊权限即可安装。

(4),快速:第一次同步时 rsync 会复制全部内容,但在下一次只传输修改过的文件。rsync 在传输数据的过程中可以实行压缩及解压缩操作,因此可以使用更少的带宽。

(5),安全:可以使用scp、ssh等方式来传输文件,当然也可以通过直接的socket连接。

(6),支持匿名传输,以方便进行网站镜象。

 

1.3,简单环境介绍:

(1),服务器端(代码发布服务器):192.168.0.51

(2),客户端(Web服务器):192.168.0.50,192.168.0.53

(3),Web目录:/usr/local/nginx/web/

(4),基本原理:由192.168.0.51上inotify服务监测文件目录/usr/local/nginx/web是否有更新,如果有更新(修改,删除,新建)inotify就会通过rsync命令将更新的文件推向二台web服务器(192.168.0.50和192.168.0.53)。

(5),架构图如下:

2.1,查看线上inotify版本

通过rsync -h找到查看帮助,找到 --version参数。

[root@localhost bin]# inotifywait --help

inotifywait 3.14

Wait for a particular event on a file or set of files.

Usage: inotifywait [ options ] file1 [ file2 ] [ file3 ] [ ... ]

Options:

……

 看到版本号码是3.14

 

2.2,下载inotify版本

下载地址:http://download.csdn.net/detail/mchdba/7564775

 

2.3,开始编译安装

[root@localhost root] tar -xvf inotify-tools-3.14.tar.gz

通过./configure --help查看编译参数,这里选取--prefix参数,开始编译:

[root@localhost inotify-tools-3.14]# ./configure --prefix=/usr/local/inotify-tools-3.14

...

config.status: creating Makefile

config.status: creating src/Makefile

config.status: creating man/Makefile

config.status: creating libinotifytools/Makefile

config.status: creating libinotifytools/src/Makefile

config.status: creating libinotifytools/src/inotifytools/Makefile

config.status: creating config.h

config.status: creating libinotifytools/src/inotifytools/inotify.h

config.status: executing depfiles commands

config.status: executing libtool commands

 

[root@localhost inotify-tools-3.14]# time make

...

fytools.so -Wl,-rpath -Wl,/usr/local/inotify-tools-3.14/lib

make[2]: Leaving directory `/root/inotify-tools-3.14/src'

Making all in man

make[2]: Entering directory `/root/inotify-tools-3.14/man'

make[3]: Entering directory `/root/inotify-tools-3.14'

make[3]: Leaving directory `/root/inotify-tools-3.14'

make[2]: Nothing to be done for `all'.

make[2]: Leaving directory `/root/inotify-tools-3.14/man'

make[2]: Entering directory `/root/inotify-tools-3.14'

cd . && /bin/sh ./config.status config.h

config.status: creating config.h

config.status: config.h is unchanged

make[2]: Leaving directory `/root/inotify-tools-3.14'

make[1]: Leaving directory `/root/inotify-tools-3.14'

 

real  0m2.889s

user 0m1.768s

sys   0m0.589s

 

[root@localhost inotify-tools-3.14]# time make install

...

make[2]: Nothing to be done for `install-exec-am'.

make[2]: Nothing to be done for `install-data-am'.

make[2]: Leaving directory `/root/inotify-tools-3.14'

make[1]: Leaving directory `/root/inotify-tools-3.14'

 

real  0m0.854s

user 0m0.454s

sys   0m0.254s

 

2.4,做成软连接到/usr/lib下

ln -sv /usr/local/inotify-tools-3.14/lib/libinotify* /usr/lib/ 

ln -s /usr/local/inotify-tools-3.14/lib/libinotifytools.so.0 /usr/lib64/libinotifytools.so.0

设置环境变量:

[root@localhost ~]# echo "export PATH=$PATH:/usr/local/inotify-tools-3.14/bin">>/etc/profile

[root@localhost ~]# source /etc/profile

[root@localhost ~]# inotifywait --help

inotifywait 3.14

Wait for a particular event on a file or set of files.

Usage: inotifywait [ options ] file1 [ file2 ] [ file3 ] [ ... ]

...

现在可以直接用inotify命令而不用附带加上全路径

 

3,开始安装rsync软件

在192.168.0.51,192.168.0.50,192.168.0.53按照如下顺序安装rsync软件


3.1,查看线上rsync版本

通过rsync -h找到查看帮助,找到 --version参数。

[root@localhost ~]# rsync --version

rsync  version 3.0.6  protocol version 30

Copyright (C) 1996-2009 by Andrew Tridgell, Wayne Davison, and others.

Web site: http://rsync.samba.org/

[root@localhost ~]#

 

3.2,下载

wget  http://rsync.samba.org/ftp/rsync/src/rsync-3.0.6.tar.gz

 

3.3,编译安装

# 解压缩

[root@localhost root]# tar -xvf rsync-3.0.6.tar.gz

[root@localhost rsync-3.0.6]# cd rsync-3.0.6

# 通过./configure --help查看编译参数,这里选取--prefix参数

[root@localhost rsync-3.0.6]# ./configure --prefix=/usr/local/rsync-3.0.6/

......

config.status: creating lib/dummy

config.status: creating zlib/dummy

config.status: creating popt/dummy

config.status: creating shconfig

config.status: creating config.h

 

    rsync 3.0.6 configuration successful

 

[root@localhost rsync-3.0.6]# make

......

gcc -std=gnu99 -I. -I. -g -O2 -DHAVE_CONFIG_H -Wall -W -I./popt  -c popt/poptparse.c -o popt/poptparse.o

gcc -std=gnu99 -g -O2 -DHAVE_CONFIG_H -Wall -W -I./popt  -o rsync flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o main.o checksum.o match.o syscall.o log.o backup.o options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o fileio.o batch.o clientname.o chmod.o acls.o xattrs.o progress.o pipe.o params.o loadparm.o clientserver.o access.o connection.o authenticate.o lib/wildmatch.o lib/compat.o lib/snprintf.o lib/mdfour.o lib/md5.o lib/permstring.o lib/pool_alloc.o lib/sysacls.o lib/sysxattrs.o  zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o popt/findme.o  popt/popt.o  popt/poptconfig.o popt/popthelp.o popt/poptparse.o

[root@localhost rsync-3.0.6]#

[root@localhost rsync-3.0.6]# make install

mkdir -p /usr/local/rsync-3.0.6/bin

/usr/bin/install -c  -m 755 rsync /usr/local/rsync-3.0.6/bin

mkdir -p /usr/local/rsync-3.0.6/share/man/man1

mkdir -p /usr/local/rsync-3.0.6/share/man/man5

if test -f rsync.1; then /usr/bin/install -c -m 644 rsync.1 /usr/local/rsync-3.0.6/share/man/man1; fi

if test -f rsyncd.conf.5; then /usr/bin/install -c -m 644 rsyncd.conf.5 /usr/local/rsync-3.0.6/share/man/man5; fi

[root@localhost rsync-3.0.6]#

 

3.3,check命令

[root@localhost ~]# rsync -h

rsync  version 3.0.6  protocol version 30

Copyright (C) 1996-2009 by Andrew Tridgell, Wayne Davison, and others.

Web site: http://rsync.samba.org/

看来已经安装好,命令可以直接使用。

 

3.4,配置rsyncd.conf启动参数文件

[root@localhost ~]# vim /etc/rsyncd.conf

uid=nginx  #用户id名称

gid=nginx  #用户所属组ID

use chroot=no

max connections=10

strict modes=yes

port=873

address=192.168.0.50 #本机地址,3台IP地址都填写自己的IP地址

#ignore erros

read only=no

list=no

auth users=nginx

secrets file=/etc/rsync.pas  #密码认证文件地址

hosts allow=192.168.0.51,192.168.0.53 #允许rsync同步的ip地址,除了本机地址的其它2个ip地址。

pid file=/home/nginx/rsync/rsyncd.pid

lock file=/home/nginx/rsync/rsync.lock

log file=/home/nginx/rsync/rsyncd.log

 

[web]

path=/usr/local/nginx/ # 这里的web就是rsync同步的参数名字,path就是同步的目录

comment=mirror for web

 

3.5,添加认证文件

# 创建认证文件

[root@localhost ~]# vim /etc/rsync.pas

nginxpasspd #密码

 nginx:nginxpasswd #用户名:密码

【PS】:不这样设置两行就会用户验证失败。

 # 赋予权限

[root@localhost ~]# chmod 600 /etc/rsync.pas

 

3.6,创建目录并赋予权限

# 创建目录

mkdir -p /usr/local/nginx

mkdir -p /home/nginx/rsync

mkdir -p /usr/local/nginx/web

# 赋予权限

chown -R nginx.nginx  /usr/local/nginx /home/nginx/rsync/ /usr/local/nginx/web

 

3.7,启动rsync服务

[root@localhost ~]# rsync --daemon --config=/etc/rsyncd.conf

[root@localhost ~]# ps -eaf|grep rsync

root      1387     1  0 Jun28 ?        00:00:00 rsync --daemon --config=/etc/rsyncd.conf

root      3201  3136  0 00:50 pts/0    00:00:00 grep rsync

[root@localhost ~]#

启动成功

 

3.8,测试rsync功能

一些调试报错经历:

[root@localhost ]#

rsync -vzrt --delete --progress --itemize-changes --exclude-from=/home/nginx/exclude_fastdfs.txt /data/fastdfs/data nginx@192.168.0.53::web --password-file=/etc/rsync.pas

password file must not be other-accessible

continuing without password file

Password:

@ERROR: auth failed on module web

rsync error: error starting client-server protocol (code 5) at main.c(1503) [sender=3.0.6]

[root@localhost ]#

说明:这是因为/etc/rsync.pas的权限不对,应该设置为600。如:chmod 600 /etc/rsync.pas

 

[root@localhost inotify-tools-3.14]#

rsync -vzrt --delete --progress --itemize-changes --exclude-from=/home/nginx/exclude_fastdfs.txt /data/fastdfs/data nginx@192.168.0.53::fastdfs --password-file=/home/nginx/rsync.pas

@ERROR: auth failed on module fastdfs

rsync error: error starting client-server protocol (code 5) at main.c(1503) [sender=3.0.6]

[root@localhost inotify-tools-3.14]# ll

去查看192.168.0.53的log信息

[root@localhost inotify-tools-3.14]# tail -f /home/nginx/rsync/rsyncd.log

2014/06/28 17:24:14 [19031] auth failed on module web from unknown (192.168.0.50): missing secret for user "nginx"

2014/06/28 17:28:21 [19198] name lookup failed for 192.168.0.50: Name or service not known

2014/06/28 17:28:21 [19198] connect from UNKNOWN (192.168.0.50)

2014/06/28 17:28:21 [19198] auth failed on module web from unknown (192.168.0.50): missing secret for user "nginx"

2014/06/28 17:28:48 [19488] name lookup failed for 192.168.0.50: Name or service not known

去192.168.0.53上面修改认证文件

[root@localhost data]# vim /etc/rsync.pas

nginxpasswd

nginx: nginxpasswd

将原来只有一行密码的改成如此2行,就好使了,能使用rsync功能了。

测试验证,在192.168.0.50的空目录下,建立测试文件 1.txt,2.txt

[root@localhost data]# cd /usr/local/nginx/web

[root@localhost web]# ll

总用量 0

[root@localhost web]# vim 1.txt

[root@localhost web]# vim 2.txt

[root@localhost web]# mkdir test

[root@localhost web]# vim ./test/3.txt

[root@localhost web]# ll /usr/local/nginx/web

总用量 12

-rw-r--r--. 1 nginx nginx    6 6月  28 19:18 1.txt

-rw-r--r--. 1 nginx nginx    6 6月  28 19:18 2.txt

drwxr-xr-x. 2 nginx nginx 4096 6月  28 19:22 test

[root@localhost web]#

 

在rsync同步之前,先去53上面check下目标目录,为空目录,如下所示:

[root@localhost web]# ll /usr/local/nginx/web

总用量 0

[root@localhost web]#

 

在192.168.0.50上执行rsync命令,同步目录

[root@localhost web]# /usr/bin/rsync -auzv --progress --delete /usr/local/nginx/web nginx@192.168.0.53::webroot   --password-file=/etc/rsync.pas

sending incremental file list

web/

web/1.txt

           6 100%    0.00kB/s    0:00:00 (xfer#1, to-check=3/5)

web/2.txt

           6 100%    5.86kB/s    0:00:00 (xfer#2, to-check=2/5)

web/test/

web/test/3.txt

           3 100%    2.93kB/s    0:00:00 (xfer#3, to-check=0/5)

sent 264 bytes  received 73 bytes  224.67 bytes/sec

total size is 15  speedup is 0.04

[root@localhost web]#

 

再去192.168.0.53上check下,看到文件已经同步过来,测试成功,如下所示:

[root@localhost web]# ll

总用量 12

-rw-r--r--. 1 nginx nginx    6 6月  28 19:18 1.txt

-rw-r--r--. 1 nginx nginx    6 6月  28 19:18 2.txt

drwxr-xr-x. 2 nginx nginx 4096 6月  28 19:22 test

[root@localhost web]#

 

4,使用Inotify结合rsync来进行随时随地自动发布web工程

编写一个inotify使用案例脚本inotify_web.sh:

4.1 inotify_web脚本

[root@localhost inotify-tools-3.14]# vim /usr/local/inotify-tools-3.14/inotify_web.sh

#!/bin/bash

src=/usr/local/nginx/web

des=web

#ip1=192.168.0.50,ip2是另外一台服务器ip地址

host="192.168.0.50 192.168.0.53"

# 使用inotifywait随时监控$src目录的一切变更,如果有,就自动调用后面的do…done里面的rsync代码块,将一切变更同步到两台web服务器上相同的目录里面。

/usr/local/inotify-tools-3.14/bin/inotifywait -mrq --timefmt '%d/%m/%y/%H:%M' --format '%T%w%f' -e close_write,move,delete,create $src | while read files

do

  for hostip in $host

         do

                       echo "`date '+%F %T'` start to resync $src to $hostip"

                       /usr/bin/rsync -auzv --progress --delete $src nginx@$hostip::$des --password-file=/etc/rsync.pas

                   echo "$src has been resynced to $hostip `date '+%F %T'`"

         done

    echo "${files} was rsynced" >>/tmp/rsync.log 2>&1

done

 

4.2,设置后台启动任务

[root@localhost inotify-tools-3.14]#

# 启动

nohup sh /usr/local/inotify-tools-3.14/inotify_web.sh >/tmp/inotify_rsync.log 2>&1 &

# 可以/tmp/inotify_rsync.log随时查看执行的日志信息

 

4.3,启动后,查看后台运行的进程:

[root@localhost inotify-tools-3.14]# ps -eaf|grep ino

root     17842 17594  0 20:15 pts/1    00:00:00 sh /usr/local/inotify-tools-3.14/inotify_web.sh

root     17843 17842  0 20:15 pts/1    00:00:00 /usr/local/inotify-tools-3.14/bin/inotifywait -mrq --timefmt %d/%m/%y/%H:%M --format %T%w%f -e close_write,move,delete,create /usr/local/nginx/web

root     17844 17842  0 20:15 pts/1    00:00:00 sh /usr/local/inotify-tools-3.14/inotify_web.sh

root     17872 17594  0 20:16 pts/1    00:00:00 tail -f /tmp/inotify_rsync.log

nginx    17882 17848  0 20:18 pts/0    00:00:00 grep ino

 

4.4,测试,check结果:

  清空192.168.0.50和192.168.0.53上面的/usr/local/nginx/web下面所有文件,然后在inotify监听服务器上的/usr/local/nginx/web创建wb.txt。

按照原理,一旦在inotify创建了文件,那么就会把/usr/local/nginx/web下面的所有文件同步到192.168.0.50和192.168.0.53的相应/usr/local/nginx/web目录下面。

(1),去查看inotify任务日志信息

[root@localhost web]# tail -f /tmp/inotify_rsync.log

nohup: 忽略输入

2014-06-28 20:16:20 start to resync /usr/local/nginx/web to 192.168.0.50

sending incremental file list

web/

web/dd.txt

           0 100%    0.00kB/s    0:00:00 (xfer#1, to-check=4/6)

web/i3.txt/

web/t.txt/

 

sent 193 bytes  received 40 bytes  466.00 bytes/sec

total size is 6  speedup is 0.03

/usr/local/nginx/web has been resynced to 192.168.0.50 2014-06-28 20:16:20

2014-06-28 20:16:20 start to resync /usr/local/nginx/web to 192.168.0.53

sending incremental file list

web/

web/dd.txt

           0 100%    0.00kB/s    0:00:00 (xfer#1, to-check=4/6)

web/wb.txt

           6 100%    0.00kB/s    0:00:00 (xfer#2, to-check=3/6)

web/i3.txt/

web/t.txt/

web/test/

 

sent 242 bytes  received 62 bytes  608.00 bytes/sec

total size is 6  speedup is 0.02

/usr/local/nginx/web has been resynced to 192.168.0.53 2014-06-28 20:16:20

 

(2),去另外一台192.168.0.53,查看下,文件已经同步过去。

[root@localhost web]# pwd

/usr/local/nginx/web

[root@localhost web]# ll

总用量 16

-rw-r--r--. 1 nginx nginx    0 6月  28 20:04 dd.txt

drwxrwxr-x. 2 nginx nginx 4096 6月  28 20:16 i3.txt

drwxr-xr-x. 2 nginx nginx 4096 6月  28 19:53 test

drwxr-xr-x. 2 nginx nginx 4096 6月  28 20:10 t.txt

-rw-r--r--. 1 nginx nginx    6 6月  28 19:51 wb.txt

 

至此,简单的通过inotify和rsync实现web工程自动同步功能已经完成,还有一点小细节留待后续解决。

 

参考文献:

http://zhumeng8337797.blog.163.com/blog/static/100768914201172952619883/

 


作者:mchdba 发表于2014-6-30 11:33:08 原文链接
阅读:0 评论:0 查看评论

Amazon前技术副总裁解剖完美技术面试

$
0
0

   英文原文:The Anatomy of the Perfect Technical Interview from a Former Amazon VP

  编者按:本文来自First Round Review,他们准备的文章既讲故事,还同时向创业者提供可操作的建议,以助力打造优秀的公司。

  Neil Roseman 厌倦了硅谷公司“我们只要最优秀和最闪亮人才”的口号。因为无论怎样强调,在真正招新时,多数人还是凭借自己的直觉,和应聘者的基本资格而定,例如 GPA,是否来自藤校,有无大公司的工作经历—甚至是 SAT 成绩。

  Roseman 对这样的考核标准不以为意。对曾在 Amazon 和 Zynga 任技术副总裁,面试过数百人的 Roseman 来说,面试的每个环节都需要被一丝不苟地设计。只有这样,才能从面试中挖掘出有真才实学的,契合企业文化的,且有领导者潜质的应试者。下面,Roseman 分享了他的面试哲学,解析他是如何挑选人才的。

  在10,000英尺的高度上

  关于面试,有一些需要遵循的基本组织原则,能帮你拟定一个清晰的计划。

  • 在每个面试结束时,你都应该对这个应试者是否会对公司带来帮助有基本的评估。
  • 成功的面试要花功夫。从面试前对简历的了解,到面试本身,再到后期总结讨论,都要当其是工作一样认真对待。
  • 在面试开始的1分钟内,你会对应聘者形成初步印象。在剩下的时间里则要使这个印象无效。
  • 大量做笔记,以得到强有力的证据选择或放弃应聘者。
  • 多数情况下“最优秀和最闪亮”的人都已经有工作了。因而你真正要做的是在可选人才中挑出最好的。另外,因为你无法对招聘结果进行A/B测试,你无从得知是否挑到了对的人。
  • 依然力求将巨星式人物纳入麾下,但也明确并非所有人都有“水上飘”的功夫。只要他们在某些方面超越现有员工且有潜力就足够了。
  • 要找那些聪明的,能完成任务的,对特定职位有相应技能的人。

  筛选简历和设计问题的正确方法

  简历通常都是第一步。然而,简历并不能完全反应出应聘者的真实水平。Roseman 建议一个深入地筛选简历阶段。

“多数团队在如何阅读简历,如何找到目标条目并对其深挖方面知之甚少。”

  在筛选简历时,Roseman 会关注那些应聘者未来能够提高的领域。他会去寻找简历上能被量化的条目,例如“将营业额提高 50%”。

  Roseman 认为,要去发现应聘者真正完成的东西,而不仅仅作为观察者或是参与者。即使是在一个伟大的公司,主心骨和起辅助作用的员工间有很大差异。

  很多情况下,这也能立刻显现应聘者对他们自己和其职责的认识。假如现在要雇用一个系统工程师,那么你需要知道他简历上”将系统可用性提高 50%”这一条他究竟参与了多少。一个优秀的候选人,无论你把问题挖掘地多深,都能够言之有物地解释清楚他声称自己完成的事。

  一个具体的例子是,在 Roseman 看到简历上有这么一句”带领一组 3 人工程师团队完成了可大规模扩展的存储基础设施,并被广泛用于 Google 的多种产品上”时,Roseman 选择让应试者在白板上绘制出基础设施的结构图,并划出哪些是由他完成的,接着 Roseman 再就那些方面提问测试应聘者究竟知道多少。

  总结起来,在技术面试中,要就能证明其贡献,行为以及决策的方面进行提问。具体操作如下:

  • 抛出问题:给我一个关于…的例子
  • 深入挖掘:对于每一个处细小成果完成的时间,地点,合作者,细节,为什么采取这种方式及其如何执行的进行提问。
  • 区分出:优秀vs.卓越;有所涉猎vs.专家;参与者vs.领头羊。

  Roseman 补充,他会去寻找那些足够有分量和深度的项目按照 STAR 的方法进行提问—STAR 代表”情况”(Situation),”任务”(Task),”行动” (Actions) 和”结果“(Results)。具体问题就是:

  • 你从事的这个项目的背景是什么
  • 你的具体任务是什么
  • 你采取了怎样的行动* 你是如何评估结果的

  此外,在面试前阅读简历的时候也应当弄清楚,简历上列出的这些项目或产品,无论成功与否,是否会对你自己的公司有益。

  技术问题与面试

  Roseman 常常听到招聘主管的第一个问题是让应聘者介绍一下自己的简历。Roseman 并不认为这是个有价值的问题。Roseman 的建议是面试官先介绍自己,并陈述这次面试的目的,通过这种方式,双方同样可以消除紧张,令应聘者进入状态。

  简介之后,Roseman 会直接进入技术问题的面试。他认为,对于一个工程师职位来说,落选的主要原因多为技术不过关,因此,摸清应试者的技术水平是首要的。

  Roseman 首先会把注意力放在候选人所擅长的领域上。例如,如果他擅长写代码,那就问一个编程问题。重点要注意的是,不要提出一个全新的你没在非面试情况下问过别人的问题。因为你会无从得知如何引导对方来完成这个问题,也会很难判断答案的好坏。

  尤其在当下,网络上的面试题库丰富。候选人完全可以从 Glassdoor 或者 Quora 等网站上得到大量信息。Roseman 认为从这些资源中找问题是可以的,但要能将其雕琢成自己的问题。对于问题的选择和修改,整个团队都应该坐下来讨论。提出的问题要能从高处开始不断向下挖掘,即使应聘者曾见过这个问题,你的深入提问也能得到不一样的信息。

  而对于原创问题,Roseman 的建议则是让应聘者试着解决一些你的公司实际面临的难题。在 Amazon 时,Roseman 经常会提一个关于”推荐”系统(根据用户的购买历史而向他们推荐他们更有可能会买的其他产品产品)的设计问题。Roseman 认为,一个人们都有所熟悉的产品的问题,能看出应聘者是否既关注产品又关注解决方案。

  Roseman 尤其关注产品设计方面的问题。他认为,卓越的工程师不仅只会接任务,更应该能够参与到产品开发的过程中来。而设计上的问题则能让你得知应聘者的思考方式。Roseman 会让应聘者给他们曾参与过的项目写一个小的项目管理计划, 或是提出一个通用的设计问题例如,怎样设计一台给盲人用的 ATM/ 电梯。

  技术面试还需要掌握好时间分配与节奏。一个 60 分钟的面试里,在一个特别复杂的查询语句上花 45 分钟是很稀松平常的。

  至于企业文化的契合和软技能,Roseman 最喜欢的一个问题是:你认为自己是个幸运的人吗?无论职位如何,这都是他的必问问题。

  Roseman 的解释是,他遇到过许多不认为自己幸运的人。那些人会抱怨,自己差一点就升职,可惜最后一刻被上级取消了自己在做的项目;或是会找到一些失败的其他原因。而 Roseman 要寻找的则是坚信”机会只给有准备的人”的求职者。他们懂得未雨绸缪,继而抓住一切能够展示自己的机会。对于创业公司而言这点尤其可贵。

  在“幸运”的问题之后,Roseman 通常有个后续问题。不同于常规的”你认为自己的优势和劣势在哪儿“,Roseman 换了一种思路去提问。他会让应聘者试想,如果让他的师长,同事,或是上级用三个形容词去形容他,他们会选择什么,并具体例证。对与经典的“自我评估优势,劣势问题”一些人会回答,自己的缺点是工作太努力了。这种提问方式就规避了很多“优势,劣势”问题的弊端。

  另外一点 Roseman 坚持的是,不要轻易缩短面试的时间。即使你在 15 分钟的时候已经决定这不是个合适的候选人,也应该完成整个面试过程。对应聘者来说,他们依然会有所受益。
  相反,如果这个应征者明显是这个职位的绝佳人选,结束时则应进一步增加他对职位的兴趣。在面试时,能够很好地回答应试者的问题并传达出你对公司的热忱是很重要的两点。

  招聘团队

  招聘团队的质量会直接影响到所招员工的整体质素。Roseman 认为,多数公司都没有花时间训练当前雇员的面试技巧。

  Roseman 要求招聘团队中的每个人都和应征者见面,并在面试过程中做详细的笔记。当最后所有人聚在一起时, 每人投票并反馈信息。这个最后商议时间不能被无限拉长,每个面试官在言之有物的前提下要精简。Roseman 对于他自己的招聘团队的要求是:1)如果他们无法综合地反馈信息,例如面试后对应聘者的评价仅仅是 “这人还不错”, 那他们则是在浪费所有人的时间。2)如果你无法学会成为优秀的面试官,要么你去学会如何成为优秀的面试官,就不要面试别人。Roseman 在面试中不会给应征者施压,却对他自己团队成员在面试新人上的表现要求严格。

  成为一个好的面试者也是现有雇员展示自己能承担更多责任,对公司有深刻思考的好机会。
在做出招聘决策时,提出的观点要能够令人信服。Roseman 认为,这是个团体的决定,并不能是来自招聘经理的压倒性意见。

  无论硅谷标准有多高,你都不应该要求每位新员工有“水上飘”的超能力。最简单的衡量标准是只要新招比在同等职位上现有员工的水平高足矣。长远看来你想要实现的是在每一次招聘中逐渐提高标准。

  至于员工推荐,Roseman 十分欢迎,他十分乐意给员工推荐奖金。他认为这是找到优秀人才最有效和最经济的方式。

  Roseman的招聘哲学总结

  • 自我介绍让大家放松进入状态。
  • “简单介绍你自自己”在技术面试中不是个有用的问题。
  • 通过深度提问以辨别应聘者的对项目的参与程度。
  • 不要尝试无从辨别答案好坏的新问题。
  • 现场写代码。
  • 探究算法,数据结构,代码组织和简洁性。
  • 故意提出指示不明的问题,观察应聘者是否发问。
  • 通过设计问题考察其全局思考的能力。
  • 确认应聘者具备公司所需要的核心才能。
  • 让面试变得有难度却不失趣味。优秀的研发人员只想和聪明人对话。

什么样的自媒体有未来?优秀内容+被信任的流量

$
0
0

钛媒体注:本文来自于作者微信公众号的一篇文章更新。虽然没有详细的案例,但其中观点值得做媒体、在任何渠道上做内容的人思考:

答应大家每天更新,也不能食言啊;今天不写太长的文章了,简单写写一些观点吧

1、社交媒体微信,微博的成熟,让更多的用户需要高质量的内容,但是原来的门户网站也好,垂直行业网站也好,很多内容都是为了做给搜索引擎看的,很多内容都都是摘抄,很粗糙。未来能够真正产生符合网友阅读习惯的精致内容的网站,特别是垂直行业网站,非常有机会做大做强。

2、内容不要只局限文章,图片,视频,长视频,音频等等结合起来的文章更吸引人,要为现在的用户使用习惯去生产内容。

3、大的互联网公司都想做平台,但是现在是小而美的时代,专注某方面内容的小而美的公司非常有机会。

4、个人如果专注某个非常小的领域,哪怕针尖那么大的领域,不断深入做下去,也许未来的威力不会亚于一个大的互联网公司。

5、个人要做品牌,但是要结合你擅长的领域和你未来要盈利的领域来做。

6、个人品牌,自明星解决的是信任问题,解决了信任问题后,一切都非常容易实现超高价值。

7、流量很重要,但是没有信任的流量价值要远远低于有信任的流量价值。比如秦刚说每天更新,你看不管我多忙 ,不管我写多少,我都每天更新,秦刚对这些读者来说就是个可信任的人,而且我每天只贡献干货。这样我的文章产生的流量是非常有价值的流量,真正被信任的流量。

现在很多网站,公众号,APP等内容都是杂乱拼凑,很多相互矛盾,用户对这些内容是带着怀疑态度的,这样的流量不是可信任的流量,价值要小很多。

可信任的IP有个公式,我发明的,但是也是经过实践证明的,哪个网站想要借用这个公式的时候,记得写上出处:

秦刚可信任流量公式:1可信任IP=100个普通IP

 

【本文作者秦刚,垂直互联网实战教练,自明星概念创始人。1999年从事垂直网站工作,历任太平洋电脑网总编,太平洋汽车网市场总监,IT世界网CEO,39健康网联席总裁。QQ&个人微信号1111884 ,微信公众号 welovecanada】

(关注更多 钛媒体作者观点,参与钛媒体微信互动(微信搜索“钛媒体”或“taimeiti”))

Android启动过程深入解析

$
0
0
当按下 Android设备电源键时究竟发生了什么?
  Android的启动过程是怎么样的?
  什么是 Linux内核?
  桌面系统linux内核与Android系统linux内核有什么区别?
  什么是引导装载程序?
  什么是Zygote?
  什么是X86以及ARM linux?
  什么是init.rc?
  什么是系统服务?
  当我们想到Android启动过程时,脑海中总是冒出很多疑问。本文将介绍Android的启动过程,希望能帮助你找到上面这些问题的答案。
  Android是一个基于Linux的开源 操作系统。x86(x86是一系列的基于intel 8086 CPU的计算机微处理器指令集架构)是linux内核部署最常见的系统。然而,所有的Android设备都是运行在ARM处理器(ARM 源自进阶精简指令集机器,源自ARM架构)上,除了英特尔的Xolo设备(http://xolo.in/xolo-x900-features)。Xolo来源自凌动1.6GHz x86处理器。Android设备或者嵌入设备或者基于linux的ARM设备的启动过程与桌面版本相比稍微有些差别。这篇 文章中,我将解释Android设备的启动过程。深入linux启动过程是一篇讲桌面linux启动过程的好文。
  当你按下电源开关后Android设备执行了以下步骤。
   Android启动流程/过程
  第一步:启动电源以及系统启动
  当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到RAM,然后执行。
   第二步:引导程序
  引导程序是在Android操作系统开始运行前的一个小程序。引导程序是运行的第一个程序,因此它是针对特定的主板与芯片的。设备制造商要么使用很受欢迎的引导程序比如redboot、uboot、qi bootloader或者开发自己的引导程序,它不是Android操作系统的一部分。引导程序是OEM厂商或者运营商加锁和限制的地方。
  引导程序分两个阶段执行。第一个阶段,检测外部的RAM以及加载对第二阶段有用的程序;第二阶段,引导程序设置网络、内存等等。这些对于运行内核是必要的,为了达到特殊的目标,引导程序可以根据配置参数或者输入数据设置内核。
  Android引导程序可以在\bootable\bootloader\legacy\usbloader找到。
  传统的加载器包含的个文件,需要在这里说明:
  init.s初始化堆栈,清零BBS段,调用main.c的_main()函数;
  main.c初始化硬件(闹钟、主板、键盘、控制台),创建linux标签。
  更多关于Android引导程序的可以在这里了解。
   第三步:内核
  Android内核与桌面linux内核启动的方式差不多。内核启动时,设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找”init”文件,然后启动root进程或者系统的第一个进程。
   第四步:init进程
  init是第一个进程,我们可以说它是root进程或者说有进程的父进程。init进程有两个责任,一是挂载目录,比如/sys、/dev、/proc,二是运行init.rc脚本。
  init进程可以在/system/core/init找到。
  init.rc文件可以在/system/core/rootdir/init.rc找到。
  readme.txt可以在/system/core/init/readme.txt找到。
  对于init.rc文件,Android中有特定的格式以及规则。在Android中,我们叫做Android初始化语言。
  Android初始化语言由四大类型的声明组成,即Actions(动作)、Commands(命令)、Services(服务)、以及Options(选项)。
  Action(动作):动作是以命令流程命名的,有一个触发器决定动作是否发生。
 语法
on <trigger>
<command>
<command>
<command>
  Service(服务):服务是init进程启动的程序、当服务退出时init进程会视情况重启服务。
  语法
service <name> <pathname> [<argument>]*
<option>
<option>
...
Options(选项)
  选项是对服务的描述。它们影响init进程如何以及何时启动服务。
  咱们来看看默认的init.rc文件。这里我只列出了主要的事件以及服务。
Table
Action/Service 描述
on early-init 设置init进程以及它创建的子进程的优先级,设置init进程的安全环境
on init 设置全局环境,为cpu accounting创建cgroup(资源控制)挂载点
on fs 挂载mtd分区
on post-fs 改变系统目录的访问权限
on post-fs-data 改变/data目录以及它的子目录的访问权限
on boot 基本网络的初始化,内存管理等等
service servicemanager 启动系统管理器管理所有的本地服务,比如位置、音频、Shared preference等等…
service zygote 启动zygote作为应用进程
  在这个阶段你可以在设备的屏幕上看到“Android”logo了。
   第五步
  在Java中,我们知道不同的虚拟机实例会为不同的应用分配不同的内存。假如Android应用应该尽可能快地启动,但如果Android系统为每一个应用启动不同的Dalvik虚拟机实例,就会消耗大量的内存以及时间。因此,为了克服这个问题,Android系统创造了”Zygote”。Zygote让Dalvik虚拟机共享代码、低内存占用以及最小的启动时间成为可能。Zygote是一个虚拟器进程,正如我们在前一个步骤所说的在系统引导的时候启动。Zygote预加载以及初始化核心库类。通常,这些核心类一般是只读的,也是Android SDK或者核心框架的一部分。在Java虚拟机中,每一个实例都有它自己的核心库类文件和堆对象的拷贝。
  Zygote加载进程
  加载ZygoteInit类,源代码:/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  registerZygoteSocket()为zygote命令连接注册一个服务器套接字。
  preloadClassed “preloaded-classes”是一个简单的包含一系列需要预加载类的文本文件,你可以在<Android Source>/frameworks/base找到“preloaded-classes”文件。
  preloadResources() preloadResources也意味着本地主题、布局以及android.R文件中包含的所有东西都会用这个方法加载。
  在这个阶段,你可以看到启动动画。  第六步:系统服务或服务
  完成了上面几步之后,运行环境请求Zygote运行系统服务。系统服务同时使用native以及java编写,系统服务可以认为是一个进程。同一个系统服务在Android SDK可以以System Services形式获得。系统服务包含了所有的System Services。
  Zygote创建新的进程去启动系统服务。你可以在ZygoteInit类的”startSystemServer”方法中找到源代码。
  核心服务:
  1.启动电源管理器;
  2.创建Activity管理器;
  3.启动电话注册;
  4.启动包管理器;
  5.设置Activity管理服务为系统进程;
  6.启动上下文管理器;
  7.启动系统Context Providers;
  8.启动电池服务;
  9.启动定时管理器;
  10.启动传感服务;
  11.启动窗口管理器;
  12.启动蓝牙服务;
  13.启动挂载服务。
  其他服务:
  1.启动状态栏服务;
  2.启动硬件服务;
  3.启动网络状态服务;
  4.启动网络连接服务;
  5.启动通知管理器;
  6.启动设备存储监视服务;
  7.启动定位管理器;
  8.启动搜索服务;
  9.启动剪切板服务;
  10.启动登记服务;
  11.启动壁纸服务;
  12.启动音频服务;
  13启动耳机监听;
  14.启动AdbSettingsObserver(处理adb命令)。
   第七步:引导完成
  一旦系统服务在内存中跑起来了,Android就完成了引导过程。在这个时候“ACTION_BOOT_COMPLETED”开机启动广播就会发出去。


顺其自然EVO 2014-06-30 18:49 发表评论

面试总结

$
0
0

最近我在找工作,面试了多家公司:百度、阿里、小米、美团、Yahoo、Symantec、Amazon。其中Amazon面的是供应链(被HR忽悠的),fail了。其它拿到了offer,但是都有些不如意。很多公司给我的薪水和职级只相当于毕业1-2年的人的水平,而我已经毕业7年了,所以这些公司的尽管给我发了offer,在我看来他们不过是婉拒了我。下面开始吐槽面试经历。

我认为无论是哪个公司的社会面试,看重的主要是以下几点:编码能力、算法、概念知识、项目经验、教育背景。

编码能力:我自己对编码能力比较看重,为什么别人花一个小时就做完的东西,你要花一天才能写完还全是BUG?但是我此番面的公司基本都不太考察这个。Amazon用它的online test狠狠打击了一下我的对自己编码能力的自信。我想以后找点类似的题多练练。顺便透露一下,amazon的online test是在 https://www.hackerrank.com/这个网站上进行的。阿里是唯一一家没有考我手写代码的公司。我和阿里的面试官谈过这个问题,他说阿里更看重人的综合素质,而百度有点拿着铁箍买鸡蛋的感觉。阿里问我:“你说你编码能力好,怎么证明?” 我回答不上来。 接着又问我:”你工作中出过的最大的BUG是什么?” 我又回答不上来。

算法:这次几家公司的面试中,算法的重要性远远低于我的想象。即便我面的是凤巢很核心的算法部门,也没有考我很难的算法问题。我认为算法能力主要分为三点:分析、设计、实现。分析最基础的,我将会持续加大在此方向的锻炼,多读paper多做数学推导。可惜,算法分析对面试或升职涨薪都完全无用。面试官太看重候选人是如何寻求解法、设计算法,思维方向以及速度。我觉得这是不对的,因为除非面试官的算法能力远远超过并且涵盖候选人所知,否则对方怎么想的他怎么看的出来。甚至,我给出了一个正确的解答之后,因为不是标准答案,面试官甚至不能理解我是不是对的(要在10分钟内完整理解一个算法的设计或代码实现确实不易)。像百度这样的公司,挑选面试官挺随意的,你想应聘T6的职位它却找一群T6来面你。相比而下,微软的面试官挑选就很严格,有资格做面试官的人很少。

概念知识:《crack the coding interview》一书中说,面试官是为了考察你的capabilities而非knowledge,像设计模式这样的东西都是不该出现在面试中的,因为太经验化。而实际上,面试往往是以“你知不知道XXX”这样的模式往下走的。就面试中所谈论的技术话题的广度来说,Yahoo是难的,有个面试官很喜欢跟我聊新技术,比如actor、协程、lock-free等。阿里的技术面试虽然很短(最短),但是问了我很多很深的东西,比如java7的新数据结构、Mutex内部的队列是如何实现的、JVM的intrinsic、openjdk里hotspot的代码、有没有看过JSR、intel CPU近几年的架构变迁等等。

项目经验:这个环节我很不喜欢。这完全就是在考察一个程序员的销售能力。面试官希望看到你过去的项目很有技术难度,用户量、并发量很大,用到的新技术很多,他不在乎你是怎样用非技术手段解决技术问题的,他也不在乎你怎样以很小的成本、很初级的技术实现了很核心的业务需求。做销售的核心是名校毕业,见客户前准备充分,把相关的技术名词背的滚瓜烂熟,介绍自己的产品技术时滔滔不绝,会察言观色。每个技术名词都是一块金箔,贴的越多,你就赢了。

Behavioral question: 据说Amazon至少有30%的面试时间都是在考察这个。实际上据我的经历,确实是这样。

最后说自己的选择。

就工作内容来说,我最喜欢的是百度和Yahoo,是做搜索广告。两家的薪水也给的差不多,所以我一直在这两家之间犹豫。

就公司的未来发展来说,小米应该是最好的,Yahoo可能是最差的。

就面试体验来说,小米和yahoo最好。小米每个面试官都会先介绍自己叫什么名字,并且会花很多时间介绍他们的项目。小米号称采用Google风格的招聘,只要基础能力好,就愿意要,至于去哪个项目就再谈。小米和yahoo的hr在谈薪水的时候都很直接,不跟我绕弯子。smth上有人(ppstay)说”雅虎北研是国内外企不多的良心企业”,就我接触来看,此言不差。

最终去哪还没定下来,等我签完劳动合同了再来更新此文。 ^_^

This article is from: https://www.sunchangming.com/blog/post/4629.html

再聊聊浏览器资源加载优化

$
0
0

几乎每一个前端程序员都知道应该把script标签放在页面底部。关于这个经典的论述可以追溯到Nicholas的 High Performance Javasript 这本书的第一章Loading and Execution中,他之所以建议这么做是因为:

Put all <script> tags at the bottom of the page, just inside of the closing </body> tag. This ensures that the page can be almost completely rendered before script execution begins.

简而言之,如果浏览器加载并执行脚本,会引起页面的渲染被暂停,甚至还会阻塞其他资源(比如图片)的加载。为了更快的给用户呈现网页内容,更好的用户体验,应该把脚本放在页面底部,使之最后加载。

为什么要在标题中使用“再”这个字?因为在工作中逐渐发现,我们经常谈论的一些页面优化技巧,比如上面所说的总是把脚本放在页面的底部,压缩合并样式或者脚本文件等,时至今日已不再是最佳的解决方案,甚至事与愿违,转化为性能的毒药。这篇文章所要聊的,便是展示某些不被人关注的浏览器特性或者技巧,来继续完成资源加载性能优化的任务。

一. Preloader

什么是Preloader

首先让我们看一看这样一类资源分布的 页面

<head><link rel="stylesheet" type="text/css" href=""><script type="text/javascript"></script></head><body><img src=""><img src=""><img src=""><img src=""><img src=""><img src=""><img src=""><img src=""><script type="text/javascript"></script><script type="text/javascript"></script><script type="text/javascript"></script></body>

 这类页面的特点是,一个外链脚本置于页面头部,三个外链脚本置于页面的底部,并且是故意跟随在一系列img之后,在Chrome中页面加载的网络请求瀑布图如下:

 

值得注意的是,虽然脚本放置在图片之后,但加载仍先于图片。为什么会出现这样的情况?为什么故意置后资源能够提前得到加载?

虽然浏览器引擎的实现不同,但原理都十分的近似。不同浏览器的制造厂商们(vendor)非常清楚浏览器的瓶颈在哪(比如network, javascript evaluate, reflow, repaint)。针对这些问题,浏览器也在不断的进化,所以我们才能看到更快的脚本引擎,调用GPU的渲染等一推陈出新的优化技术和方案。

同样在资源加载上,早在IE8开始,一种叫做lookahead pre-parser(在Chrome中称为preloader)的机制就已经开始在不同浏览器中兴起。IE8相对于之前IE版本的提升除了将每台host最高并行下载的资源数从2提升至6,并且能够允许并行下载脚本文件之外,最后就是这个lookahead pre-parser机制

但我还是没有详述这是一个什么样的机制,不着急,首先看看与IE7的对比:

以上面的页面为例,我们看看IE7下的瀑布图:

底部的脚本并没有提前被加载,并且因为由于单个域名最高并行下载数2的限制,资源总是两个两个很整齐的错开并行下载。

但在IE8下,很明显底部脚本又被提前:

并没有统一的标准规定这套机制应具备何种功能已经如何实现。但你可以大致这么理解:浏览器通常会准备两个页面解析器parser,一个(main parser)用于正常的页面解析,而另一个(preloader)则试图去文档中搜寻更多需要加载的资源,但这里的资源通常仅限于外链的js、stylesheet、image;不包括audio、video等。并且动态插入页面的资源无效。

但细节方面却值得注意:

  1. 比如关于preloader的触发时机,并非与解析页面同时开始,而通常是在加载某个head中的外链脚本阻塞了main parser的情况下才启动;

  2. 也不是所有浏览器的preloader会把图片列为预加载的资源,可能它认为图片加载过于耗费带宽而不把它列为预加载资源之列;

  3. preloader也并非最优,在某些浏览器中它会阻塞body的解析。因为有的浏览器将页面文档拆分为head和body两部分进行解析,在head没有解析完之前,body不会被解析。一旦在解析head的过程中触发了preloader,这无疑会导致head的解析时间过长。

Preloader在响应式设计中的问题

preloader的诞生本是出于一番好意,但好心也有可能办坏事。

filamentgroup有一种著名的响应式设计的图片解决方案 Responsive Design Images

<html><head><title></title><script type="text/javascript" src="./responsive-images.js"></script></head><body><img src="./running.jpg?medium=_imgs/running.medium.jpg&large=_imgs/running.large.jpg"></body></html>

 它的工作原理是,当responsive-images.js加载完成时,它会检测当前显示器的尺寸,并且设置一个cookie来标记当前尺寸。同时你需要在服务器端准备一个.htaccess文件,接下来当你请求图片时,.htaccess中的配置会检测随图片请求异同发送的Cookie是被设置成medium还是large,这样也就保证根据显示器的尺寸来加载对于的图片大小。

很明显这个方案成功的前提是,js执行先于发出图片请求。但在Chrome下打开,你会发现执行顺序是这样:

responsive-images.js和图片几乎是同一时间发出的请求。结果是第一次打开页面给出的是默认小图,如果你再次刷新页面,因为Cookie才设置成功,服务器返回的是大图。

严格意义上来说在某些浏览器中这不一定是preloader引起的问题,但preloader引起的问题类似:插入脚本的顺序和位置或许是开发者有意而为之的,但preloader的这种“聪明”却可能违背开发者的意图,造成偏差。

如果你觉得上一个例子还不够说明问题的话,最后请考虑使用 picture(或者@srcset)元素的情况:

<picture><source src="med.jpg" media="(min-width: 40em)" /><source src="sm.jpg"/><img src="fallback.jpg" alt="" /></picture>

 在preloader搜寻到该元素并且试图去下载该资源时,它应该怎么办?一个正常的paser应该是在解析该元素时根据当时页面的渲染布局去下载,而当时这类工作不一定已经完成,preloader只是提前找到了该元素。退一步来说,即使不考虑页面渲染的情况,假设preloader在这种情形下会触发一种默认加载策略,那应该是"mobile first"还是"desktop first"?默认应该加载高清还是低清照片?

二. JS Loader

理想是丰满的,现实是骨感的。出于种种的原因,我们几乎从不直接在页面上插入js脚本,而是使用第三方的加载器,比如seajs或者requirejs。关于使用加载器和模块化开发的优势在这里不再赘述。但我想回到原点,讨论应该如何利用加载器,就从seajs与requirejs的不同聊起。

在开始之前我已经假设你对requirejs与seajs语法已经基本熟悉了,如果还没有,请移步这里:

BTW: 如果你还是习惯在部署上线前把所有js文件合并打包成一个文件,那么seajs和requirejs其实对你来说并无区别。

seajs与requirejs在模块的加载方面是没有差异的,无论是requirejs在定义模块时定义的依赖模块,还是seajs在factory函数中require的依赖模块,在会在加载当前模块时被载入,异步,并且顺序不可控。差异在于factory函数执行的时机。

执行差异

为了增强对比,我们在定义依赖模块的时候,故意让它们的factory函数要执行相当长的时间,比如1秒:

// dep_A.js定义如下,dep_B、dep_C定义同理

define(function(require, exports, module) {

    (function(second) {
        var start = +new Date();
        while (start + second * 1000 > +new Date()) {}
    })(window.EXE_TIME);

    // window.EXE_TIME = 1;此处会连续执行1s

    exports.foo = function() {
        console.log("A");
    }
})

 为了增强对比,设置了三组进行对照试验,分别是:

 

//require.js:
require(["dep_A", "dep_B", "dep_C"], function(A, B, C) {

});

//sea.js:
define(function(require, exports, module) {

    var mod_A = require("dep_A");
    var mod_B = require("dep_B");
    var mod_C = require("dep_C");
});

//sea.js(定义依赖但并不require):
define(["dep_A", "dep_B", "dep_C"], function(require, exports, module){

}

 接下来我们看看代码执行的瀑布图:

1.require.js:在加载完依赖模块之后立即执行了该模块的factory函数

2.sea.js: 下面两张图应该放在一起比较。两处代码都同时加载了依赖模块,但因为没有require的关系,第三张图中没有像第二张图那样执行耗时的factory函数。可见seajs执行的原则正如CMD标准中所述Execution must be lazy。

我想进一步表达的是,无论requirejs和seajs,通常来说大部分的逻辑代码都会放在模块的factory函数中,所以factory函数执行的代价是非常大的。但上图也同样告诉我们模块的define,甚至模块文件的Evaluate代价非常小,与factory函数无关。所以我们是不是应该尽可能的避免执行factory函数,或者等到我们需要的指定功能的时候才执行对应的factory函数?比如:

document.body.onclick = function () {
    require(some_kind_of_module);
}

 这是非常实际的问题,比如爱奇艺一个视频播放的页面,我们有没有必要在第一屏加载页面的时候就加载登陆注册,或者评论,或者分享功能呢?因为有非常大的可能用户只是来这里看这个视频,直至看完视频它都不会用到登陆注册功能,也不会去分享这个视频等。加载这些功能不仅仅对浏览器是一个负担,还有可能调用后台的接口,这样的性能消耗是非常可观的。

我们可以把这样称之为"懒执行"。虽然seajs并非有意实现如上所说的“懒执行”(它只是在尽可能遵循CommonJS标准靠近)。但“懒执行”确实能够有助于提升一部分性能。

但也有人会对此产生顾虑。

记得玉伯转过的一个帖子: SeaJS与RequireJS最大的区别。我们看看其中反对这么做的人的 观点

我个人感觉requirejs更科学,所有依赖的模块要先执行好。如果A模块依赖B。当执行A中的某个操doSomething()后,再去依赖执行B模块require('B');如果B模块出错了,doSomething的操作如何回滚? 很多语言中的import, include, useing都是先将导入的类或者模块执行好。如果被导入的模块都有问题,有错误,执行当前模块有何意义?

而依赖dependencies是工厂的原材料,在工厂进行生产的时候,是先把原材料一次性都在它自己的工厂里加工好,还是把原材料的工厂搬到当前的factory来什么时候需要,什么时候加工,哪个整体时间效率更高?

首先回答第一个问题。

第一个问题的题设并不完全正确,“依赖”和“执行”的概念比较模糊。编程语言执行通常分为两个阶段,编译(compilation)和运行(runtime)。对于静态语言(比如C/C++)来说,在编译时如果出现错误,那可能之前的编译都视为无效,的确会出现描述中需要回滚或者重新编译的问题。但对于 动态语言或者脚本语言,大部分执行都处在运行时阶段或者解释器中:假设我使用Nodejs或者Python写了一段服务器运行脚本,在持续运行了一段时间之后因为某项需求要加载某个(依赖)模块,同时也因为这个模块导致服务端挂了——我认为这时并不存在回滚的问题。在加载依赖模块之前当前的模块的大部分功能已经成功运行了。

再回答第二个问题。

对于“工厂”和“原材料”的比喻不够恰当。难道依赖模块没有加载完毕当前模块就无法工作吗?requirejs的确是这样的,从上面的截图可以看出,依赖模块总是先于当前模块加载和执行完毕。但我们考虑一下基于CommonJS标准的Nodejs的语法,使用require函数加载依赖模块可以在页面的任何位置,可以只是在需要的时候。也就是说当前模块不必在依赖模块加载完毕后才执行。

你可能会问,为什么要拿AMD标准与CommonJS标准比较,而不是CMD标准?

玉伯在 CommonJS 是什么这篇文章中已经告诉了我们CMD某种程度上遵循的就是CommonJS标准:

从上面可以看出,Sea.js 的初衷是为了让 CommonJS Modules/1.1 的模块能运行在浏览器端,但由于浏览器和服务器的实质差异,实际上这个梦无法完全达成,也没有必要去达成。

更好的一种方式是,Sea.js 专注于 Web 浏览器端,CommonJS 则专注于服务器端,但两者有共通的部分。对于需要在两端都可以跑的模块,可以 有便捷的方案来快速迁移。

其实AMD标准的推出同时也是遵循CommonJS,在requirejs官方文档的 COMMONJS NOTES中说道:

CommonJS defines a module format. Unfortunately, it was defined without giving browsers equal footing to other JavaScript environments. Because of that, there are CommonJS spec proposals for Transport formats and an asynchronous require.

RequireJS tries to keep with the spirit of CommonJS, with using string names to refer to dependencies, and to avoid modules defining global objects, but still allow coding a module format that works well natively in the browser.

CommonJS当然是一个理想的标准,但至少现阶段对浏览器来说还不够友好,所以才会出现AMD与CMD,其实他们都是在做同一件事,就是致力于前端代码更友好的模块化。所以个人认为依赖模块的加载和执行在不同标准下实现不同,可以理解为在用不同的方式在完成同一个目标, 并不是一件太值得过于纠结的事。

懒加载

其实我们可以走的更远,对于非必须模块不仅仅可以延迟它的执行,甚至可以延迟它的加载。

但问题是我们如何决定一个模块是必须还是非必须呢,最恰当莫过取决于用户使用这个模块的概率有多少。Faceboook早在09年的时候就已经注意到这个问题: Frontend Performance Engineering in Facebook : Velocity 2009,只不过他们是以样式碎片来引出这个问题。

假设我们需要在页面上加入A、B、C三个功能,意味着我们需要引入A、B、C对应的html片段和样式碎片(暂不考虑js),并且最终把三个功能样式碎片在上线前压缩到同一个文件中。但可能过了相当长时间,我们移除了A功能,但这个时候大概不会有人记得也把关于A功能的样式从上线样式中移除。久而久之冗余的代码会变得越来越多。Facebook引入了一套静态资源管理方案(Static Resource Management)来解决这个问题:

具体来说是将样式的“声明”(Declaration)和请求(Delivery)请求,并且是否请求一个样式由是否拥有该功能的 html片段决定。

当然同时也考虑也会适当的合并样式片段,但这完全是基于使用算法对用户使用模块情况进行分析,挑选出使用频率比较高的模块进行拼合。

这一套系统不仅仅是对样式碎片,对js,对图片sprites的拼合同样有效。

你会不会觉得我上面说的懒加载还是离自己太远了? 但然不是,你去看看现在的人人网个人主页看看

如果你在点击图中标注的“与我相关”、“相册”、“分享”按钮并观察Chrome的Timeline工具,那么都是在点击之后才加载对应的模块

三. Delay Execution

利用浏览器缓存

脚本最致命的不是加载,而是执行。因为何时加载毕竟是可控的,甚至可以是异步的,比如通过调整外链的位置,动态的创建脚本。但一旦脚本加载完成,它就会被立即执行(Evaluate Script),页面的渲染也就随之停止,甚至导致在低端浏览器上假死。

更加充分的理由是,大部分的页面不是Single Page Application,不需要依靠脚本来初始化页面。服务器返回的页面是立即可用的,可以想象我们初始化脚本的时间都花在用户事件的绑定,页面信息的丰满(用户信息,个性推荐)。 Steve Souders发现在Alexa上排名前十的美国网站上的js代码,只有29%在window.onload事件之前被调用,其他的71%的代码与页面的渲染无关。

Steve Souders的 ControlJS是我认为一直被忽视的一个加载器,它与Labjs一样能够控制的脚本的异步加载,甚至(包括行内脚本,但不完美)延迟执行。它延迟执行脚本的思路非常简单:既然只要在页面上插入脚本就会导致脚本的执行,那么在需要执行的时候才把脚本插入进页面。但这样一来脚本的加载也被延迟了?不,我们会通过其他元素来提前加载脚本,比如img或者是object标签,或者是非法的mine type的script标签。这样当真正的脚本被插入页面时,只会从缓存中读取。而不会发出新的请求。

Stoyan Stefanov在它的文章 Preload CSS/JavaScript without execution中详细描述了这个技巧, 如果判断浏览器是IE就是用image标签,如果是其他浏览器,则使用object元素:

window.onload = function () {

    var i = 0,
        max = 0,
        o = null,

        preload = [
            // list of stuff to preload    
        ],

        isIE = navigator.appName.indexOf('Microsoft') === 0;

    for (i = 0, max = preload.length; i < max; i += 1) {

        if (isIE) {
            new Image().src = preload[i];
            continue;
        }
        o = document.createElement('object');
        o.data = preload[i];

        // IE stuff, otherwise 0x0 is OK
        //o.width = 1;
        //o.height = 1;
        //o.style.visibility = "hidden";
        //o.type = "text/plain"; // IE 
        o.width  = 0;
        o.height = 0;


        // only FF appends to the head
        // all others require body
        document.body.appendChild(o);
    }

};

 同时它还列举了其他的一些尝试,但并非对所有的浏览器都有效,比如:

  • 使用<link>元素加载script,这么做在Chrome中的风险是,在当前页有效,但是在以后打开需要使用该脚本的页面会无视该文件为缓存

  • 改变script标签外链的type值,比如改为text/cache来阻止脚本的执行。这么做会导致在某些浏览器(比如FF3.6)中压根连请求都不会发出

type=prefetch

延迟执行并非仅仅作为当前页面的优化方案,还可以为用户可能打开的页面提前缓存资源,如果你对这两种类型的link元素熟悉的话:

  • <link rel="subresource" href="jquery.js">: subresource类型用于加载当前页面将使用(但还未使用)的资源(预先载入缓存中),拥有较高优先级

  • <link rel="prefetch" href="http://NextPage.html">: prefetch类型用于加载用户将会打开页面中使用到的资源,但优先级较低,也就意味着浏览器不做保证它能够加载到你指定的资源。

那么上一节延迟执行的方案就可以作为subresource与prefeth的回滚方案。同时还有其他的类型:

  • <link rel="dns-prefetch" href="//host_name_to_prefetch.com">: dns-prefetch类型用于提前dns解析和缓存域名主机信息,以确保将来再请求同域名的资源时能够节省dns查找时间,比如我们可以看到淘宝首页就使用了这个类型的标签:

  • <link rel="prerender" href="http://example.org/index.html">: prerender类型就比较霸道了,它告诉浏览器打开一个新的标签页(但不可见)来渲染指定页面,比如这个 页面:

这也就意味着如果用户真的访问到该页面时,就会有“秒开”的用户体验。

但现实并非那么美好,首先你如何能预测用户打开的页面呢,这个功能更适合阅读或者论坛类型的网站,因为用户有很大的概率会往下翻页;要注意提前的渲染页面的网络请求和优先级和GPU使用权限优先级都比其他页面的要低,浏览器对提前渲染页面类型也有一定的要求,具体可以参考 这里

利用LocalStorage

在聊如何用它来解决我们遇到的问题之前,个人觉得首先应该聊聊它的优势和劣势。

Chris Heilmann在文章 There is no simple solution for local storage中指出了一些常见的LS劣势,比如同步时可能会阻塞页面的渲染、I/O操作会引起不确定的延时、持久化机制会导致冗余的数据等。虽然Chirs在文章中用到了比如"terrible performance", "slow"等字眼,但却没有真正的指出究竟是具体的哪一项操作导致了性能的低下。

Nicholas C. Zakas于是写了一篇针对该文的文章 In defense of localStorage,从文章的名字就可以看出,Nicholas想要捍卫LS,毕竟它不是在上一文章中被描述的那样一无是处,不应该被抵制。

比较性能这种事情,应该看怎么比,和谁比。

就“读”数据而言,如果你把“从LS中读一个值”和“从Object对象中读一个属性”相比,是不公平的,前者是从硬盘里读,后者是从内存里读,就好比让汽车与飞机赛跑一样,有一个benchmark各位可以参考一下: localStorage vs. Objects:

跑分的标准是OPS(operation per second),值当然是越高越好。你可能会注意到,在某个浏览器的对比列中,没有显示关于LS的红色列——这不是因为统计出错,而是因为LS的操作性能太差,跑分太低(相对从Object中读取属性而言),所以无法显示在同一张表格内,如果你真的想看的话,可以给你看一张放大的版本:

这样以来你大概就知道两者在什么级别上了。

在浏览器中与LS最相近的机制莫过于Cookie了:Cookie同样以key-value的形式进行存储,同样需要进行I/O操作,同样需要对不同的tab标签进行同步。同样有benchmark可以供我们进行参考: localStorage vs. Cookies

从Brwoserscope中提供的结果可以看出,就Reading from cookie, Reading from localStorage getItem, Writing to cookie,Writing to localStorage property四项操作而言,在不同浏览器不同平台,读和写的效率都不太相同,有的趋于一致,有的大相径庭。

甚至就LS自己而言,不同的存储方式和不同的读取方式也会产生效率方面的问题。有两个benchmark非常值得说明问题:

  1. localStorage-string-size

  2. localStorage String Size Retrieval

在第一个测试中,Nicholas在LS中用四个key分别存储了100个字符,500个字符,1000个字符和2000个字符。测试分别读取不同长度字符的速度。结果是: 读取速度与读取字符的长度无关

第二个测试用于测试读取1000个字符的速度,不同的是对照组是一次性读取1000个字符;而实验组是从10个key中(每个key存储100个字符)分10次读取。结论:  是分10此读取的速度会比一次性读取慢90%左右

LS也并非没有痛点。大部分的LS都是基于同一个域名共享存储数据,所以当你在多个标签打开同一个域名下的站点时,必须面临一个同步的问题,当A标签想写入LS与B标签想从LS中读同时发生时,哪一个操作应该首先发生?为了保证数据的一致性,在读或者在写时 务必会把LS锁住(甚至在操作系统安装的杀毒软件在扫描到该文件时,会暂时锁住该文件)。因为单线程的关系,在等待LS I/O操作的同时,UI线程和Javascript也无法被执行。

但实际情况远比我们想象的复杂的多。为了提高读写的速度,某些浏览器(比如火狐)会在加载页面时就把该域名下LS数据加载入内存中,这么做的副作用是延迟了页面的加载速度。但如果不这么做而是在临时读写LS时再加载,同样有死锁浏览器的风险。并且把数据载入内存中也面临着将内存同步至硬盘的问题。

上面说到的这些问题大部分归咎于内部的实现,需要依赖浏览器开发者来改进。并且并非仅仅存在于LS中,相信在IndexedDB、webSQL甚至Cookie中也有类似的问题在发生。

实战开始

考虑到移动端网络环境的不稳定,为了避免网络延迟(network latency),大部分网站的移动端站点会将体积庞大的类库存储于本地浏览器的LS中。但 百度音乐将这个技术也应用到了PC端,他们将所依赖的jQuery类库存入LS中。用一段很简单的 代码来保证对jQuery的正确载入。我们一起来看看这段代码。代码详解就书写在注释中了:

!function (globals, document) {
    var storagePrefix = "mbox_";
    globals.LocalJs = {
        require: function (file, callback) {
            /*如果无法使用localstorage,则使用document.write把需要请求的脚本写在页面上
                作为fallback,使用document.write确保已经加载了所需要的类库
            */

            if (!localStorage.getItem(storagePrefix + "jq")) {
                document.write('<script src="' + file + '" type="text/javascript"></script>');
                var self = this;

            /*
                并且3s后再请求一次,但这次请求的目的是为了获取jquery源码,写入localstorage中(见下方的_loadjs函数)
                这次“一定”走缓存,不会发出多余的请求
                为什么会延迟3s执行?为了确保通过document.write请求jQuery已经加载完成。但很明显3s也并非一个保险的数值
                同时使用document.write也是出于需要故意阻塞的原因,而无法为其添加回调,所以延时3s
            */
                setTimeout(function () {
                    self._loadJs(file, callback)
                }, 3e3)
            } else {
                // 如果可以使用localstorage,则执行注入
                this._reject(localStorage.getItem(storagePrefix + "jq"), callback)
            }
        },
        _loadJs: function (file, callback) {
            if (!file) {
                return false
            }
            var self = this;
            var xhr = new XMLHttpRequest;
            xhr.open("GET", file);
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status === 200) {
                        localStorage.setItem(storagePrefix + "jq", xhr.responseText)
                    } else {}
                }
            };
            xhr.send()
        },
        _reject: function (data, callback) {
            var el = document.createElement("script");
            el.type = "text/javascript";
            /*关于如何执行LS中的源码,我们有三种方式
                1. eval
                2. new Function
                3. 在一段script标签中插入源码,再将该script标签插入页码中

                关于这三种方式的执行效率,我们内部初步测试的结果是不同的浏览器下效率各不相同
                参考一些jsperf上的测试,执行效率甚至和具体代码有关。
            */
            el.appendChild(document.createTextNode(data));
            document.getElementsByTagName("head")[0].appendChild(el);
            callback && callback()
        },
        isSupport: function () {
            return window.localStorage
        }
    }
}(window, document);
!
function () {
    var url = _GET_HASHMAP ? _GET_HASHMAP("/player/static/js/naga/common/jquery-1.7.2.js") : "/player/static/js/naga/common/jquery-1.7.2.js";
    url = url.replace(/^\/\/mu[0-9]*\.bdstatic\.com/g, "");
    LocalJs.require(url, function () {})
}(); 

 因为桌面端的浏览器兼容性问题比移动端会严峻的多,所以大多数对LS利用属于“做加法”,或者“轻量级”的应用。最后一瞥不同站点在PC平台的对LS的使用情况:

    • 比如百度和github用LS记录用户的搜素行为,为了提供更好的搜索建议

    • Twitter利用LS最主要的记录了与用户关联的信息(截图自我的Twitter账号,因为关注者和被关注者的不同数据会有差异):
    • userAdjacencyList表占40,158 bytes,用于记录每个字关联的用户信息
    • userHash表占36,883 bytes,用于记录用户被关注的人信息

    • Google利用LS记录了样式:

    • 天猫用LS记录了导航栏的HTML碎片代码:

总结

No silver bullet.没有任何一项技术或者方案是万能的,虽然开源社区和浏览器厂商在提供给我们越来越丰富的资源,但并不意味着今后遇见的问题就会越来越少。相反,或许正因为多样性,和发展中技术的不完善,事情会变得更复杂,我们在选择时要权衡更多。我无意去推崇某一项解决方案,我想尽可能多的把这些方案与这些方案的厉害呈现给大家,毕竟不同人考虑问题的方面不同,业务需求不同。

还有一个问题是,本文描述的大部分技术都是针对现代浏览器而言,那么如何应对低端浏览器呢?

从百度统计这张17个月的浏览器市场份额图中可以看出(当然可能因为不同站点的用户特征不同会导致使用的浏览器分布与上图有出入),我们最关心的IE6的市场份额一直是呈现的是下滑的趋势,目前已经降至几乎与IE9持平;而IE9在今年的市场份额也一直稳步上升;IE7已经被遥遥甩在身后。领头的IE8与Chrome明显让我们感受到有足够的信心去尝试新的技术。还等什么,行动起来吧!

转自: http://www.infoq.com/cn/articles/browser-resource-loading-optimization



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Wi-Fi对新生儿有害吗?

$
0
0

couldwifibeharmfultoanewbornbaby00

Wi-Fi已经成为我们生活不可缺少的一部分。除非它停止工作,否则我们很少会想到它。但是,如果你家有刚出生的孩子,你会怎样想?作为父母,是否应该知道WIFI可能带来的危害?

问题

SuperUser网站的读者Avy想了解WIFI是否真的会给他的孩子带来伤害:

我可能是一个过度保护孩子的父亲,自从我们的孩子出生后,妻子和我一直想了解关于WIFI及其对健康影响的权威研究。我爱我的WIFI,它是我家里的各种电子产品工作的基础,让我的生活变得简单和扁平。但是,孩子的出生改变了我对所有东西的想法。

在你们写下“WIFI是安全的,因为大家在医院和学校都用它”之前,让我说明一点,这些情况我是知道的。但是,WIFI日复一日年复一年地辐射到一个我们有责任保护的小孩子身上,让我很想对这个问题有一个清晰的答案。

我暂时先戴上锡箔帽子(锡材料可以防辐射–译者注)吧,等待一些有见解有深度的答案。

WIFI对新生儿有害吗?或者这只是无必要的偏执想法?

答案

SuperUser投稿人NothingsImpossible 和 Bob回答了我们的问题。NothingsImpossible的回答如下:

免责声明:这是一个粗略的解释,很可能有误。

辐射有两类,电离辐射和非电离辐射。

用行外的话来说,电离辐射一种能将组成物体的分子“粉碎”的辐射。

与之相反,非电离辐射只是穿透物体或者当它击中物体时转化为热量。

Wi-Fi网络运行在与微波炉一样的频率上。它使用非电离辐射,并且当它射到物体时,它只是转化为热量,并没有改变物体本身的构造。它是无害的,它只会使你变热,但这种热量小到无法测量,你不会感觉到。

电离辐射很危险。紫外线和核辐射就是其中的例子。它们不仅让你变热,并且改变组成你身体的分子的构造。它们可以改变你细胞的DNA,引发癌症。

例子:晒斑。长时间、无保护地暴露在太阳底下,你的皮肤会有火烧的感觉,并不是因为它变热了。太阳的紫外线破坏了皮肤细胞的DNA,身体感觉就像被火烧。

总结:Wi-Fi是无害的

Bob的答案如下:

绝对安全。

“辐射”这个术语通常用来吓唬人。让我们简单这样说,它有两个方面,一是频率,另一个是强度。频率的高低对辐射的破坏性的大小有更大的影响。 Wi-Fi和其它无线电通信设备使用的都是非常低的、远远低于可见光的频率。

会造成问题、引发癌症的辐射通常是电离辐射。它具有很高的频率,能造成DNA的突变,这样的情况能导致癌症。多高的频率才能电离化?至少1000000GHz。严格地说,这是WIFI传输的频率(2.4GHz或者5GHz)的500000倍。WIFI属于非电离辐射,它只是传输热量。

你知道光线也是一种电磁辐射吗?是的。实际上,光线(近红外光频率500000GHz,近紫外光频率750000)比WIFI更接近电离辐射。阳光其实包含了一些电离辐射,如UVB和UVC。UVA也可以造成DNA的破坏,但方式不同。话虽如此,但你不能一辈子都躲在房子里,对吗?

除了频率,还有强度。非电离辐射也可以造成伤害,但这种情况只适用于高强度的辐射。并且,电离辐射并不总是危险的,我们的身体可以应付低强度的电离辐射,这就是我们并没有被太阳晒焦的原因(吸血鬼另当别论)。Wi-Fi传输的能量通常远低于1瓦特(我曾经看到一个数字是200毫瓦),并且大部分能量根本没有接触到你。根据平方反比定律,你仅仅接收到(1/距离)的平方分之一的强度。用门外汉的话说,能量是均匀地向各个方向传送。距离10米的强度是多少?

1/100 * 200 mW = 2 mW。这种强度微乎其微。

微波炉工作的频率和Wi-Fi差不多,传输的能量约1000瓦特,但绝大部分集中在微波炉这个金属盒里。也许只有1瓦特从屏蔽门泄露出来,即使这样,它也认为是绝对安全的。当阳光(频率高,能量大)照射到地面时,它辐射的能量约为1000瓦特每平方米,其中一半是可见光或频率更高的光线。

Wi-Fi对新生儿有害吗?,首发于 极客范 - GeekFan.net

数据库优化:mysql数据库单机数十亿数据查询设计

$
0
0

    很久没写文章,是不是想着写点什么东西,分享下我的数据库设计思路,主要是针对单机数十亿及以上数据查询优化技巧。


如果只是简单的查询,没有频繁的写入操作,对查询速度不要求在毫秒级别,就不需要什么大型的数据库软件设计复杂的集群关系,也不需要分布式水平分割等太重的优化。

只需要用mysql在本机笔记本搭建一个普通的环境就行。

   那么首先是针对mysql做一些普通常见的优化,比如分表分区、建索引、表字段设计以及mysql的配置优化,比如缓冲区大小等等,这类配置我找了一个文章,详细的可以看 http://www.cnblogs.com/Bozh/archive/2013/01/22/2871545.html
。其实mysql水平分表也是数据水平分割的做法。只需要在入库时针对不同的数据库入到不同的表即可,对于比较大的单个库,比如上两亿的单库,这时候就可以把它进行分表放到两个或者三个表,我的做法是单表不超过一亿。


除了这些优化,我优化的核心设计思想是在建立索引和查询代码上面。


很多人在设计社工库的时候,都是把所有要查询的字段都建立索引,而对于数据库来说,查询的数据库数据量越小,那么查询速度越快,另外索引也比较占空间,所以我们在索引上面做做动作,可以节省大量硬盘空间和优化查询速度。


我的做法是只查可能有我想要的数据的表,肯定没有我想要的数据表直接不查询,我不需要查询的字段就不需要建索引,那这样就可以实现一来我查询的量小了,而来少给很多字段建立索引。


举个简单的例子,比如你现有的数据库info中两个表:


1A表和B表。AB表字段一样。AB表都有明文password字段不为空。
2.A表username字段不为空,B表username字段为空。

3.B表email字段不为空,A表email字段为空。


那在这种情况下,就可以针对这两个表做如下的设计:
1.A表和B表password字段都建立索引。
2.A表username字段建立索引,B表username字段不建立索引且默认为空。

3.B表email字段建立索引,A表email字段不建立索引且默认为空。

OK,那么表设计就完成了。

那么我们就可以利用这种表设计减少我们查询的量来优化查询速度。

对于有索引的字段,在mysql数据库information_schema的statistics表里面保存了所有表的索引信息,那么我们就可以利用这个表来过滤掉我们要查询的字段没有建立索引的表。

比如我要查询info库里面邮箱为root@cnseay.com的信息。那么这个查询中我会丢掉A表,因为A表email字段为空,肯定没有我要查询的root@cnseay.com信息。

那么如何过滤掉A表。一条SQL语句即可:


SELECT TABLE_NAME FROM information_schema.statistics WHERE INDEX_NAME!=’PRIMARY’ and table_schema = ‘info’ and COLUMN_NAME=’email’ GROUP BY TABLE_NAME;


利用这条语句,就可以输出info库email字段存在索引的表。然后利用脚本动态拼接union查询语句查询即可。

我的环境是USB3.0 2TB移动硬盘,笔记本win7 i7-4700,8G内存。20亿数据查询速度在1~3秒。

博主猜你喜欢:

数据优化:解决mysql in 子查询速度慢 优化查询效率

php注入load_file读数据库配置文件

MySql得到webshell或提权

BT5 Metasploit(MSF)连接postgersql数据库

Python学习:python操作MySQL数据库
无觅

Android中播放声音的两种方法

$
0
0
       在Android中,音频、视频等多媒体元素的加入,使得应用程序的用户体验更好。可以说,现在的手机,已经远远不只作为通信工具,更成为娱乐、办公的必备产品。

Android提供了简单的音频API。一般大家使用的是MediaPlayer播放音频,这也是最常见的一种播放声音的工具。这种工具在互联网上有大量的实例,因此在此只做简单的介绍。

对播放行为的控制是三个大家非常熟悉的方法:start()、stop()和pause()。
通过static MediaPlayer create(Context, Uri)这个方法,可以获得一个新创建的MediaPlayer对象。
在播放过程中,有几个可以监听播放过程的监听器,如:
n setOnCompletionListener(MediaPlayer.OnCompletionListener listener),监听音频播放结束;
n setOnErrorListener(MediaPlayer.OnErrorListener listener),监听播放过程中的错误事件;
n setOnPreparedListener(MediaPlayer.OnPreparedListener listener),当prepare()被调用时触发。
然而,使用MediaPlayer播放时,也有一些问题。我们知道MediaPlayer在创建和销毁时都会耗费大量的系统资源,且创建和销毁的时间相对较长。此外,如果我们需要在同一时刻播放很多声音,MediaPlayer是不支持的。
因此,我们需要一个更加轻量级的声音播放工具。

Android提供了另外一种,叫做SoundPool,它适合播放那些需要反复播放,但时间较短的音效。它支持同时播放多种声音,这些声音在系统开始时会加载到列表中,按照这些声音的id,我们可以调用这些音效。

下面我们进入一个实例看看SoundPool到底是怎么工作的。

例如,现在在一个五子棋游戏中,我们需要在棋子落盘的时候播放一段声音。我们可以利用SoundPool,因为它时间很短,而且需要反复播放,并且我们不希望声音占用太大资源。

先看看代码:
private SoundPool soundPool;
soundPool= newSoundPool(10,AudioManager.STREAM_SYSTEM,5);
soundPool.load(this,R.raw.collide,1);
soundPool.play(1,1, 1, 0, 0, 1);

代码非常简单,第一行是声明了一个SoundPool对象,这个一般是作为类的成员属性出现的。第二行将soundPool实例化,第一个参数为soundPool可以支持的声音数量,这决定了Android为其开设多大的缓冲区,第二个参数为声音类型,在这里标识为系统声音,除此之外还有AudioManager.STREAM_RING以及AudioManager.STREAM_MUSIC等,系统会根据不同的声音为其标志不同的优先级和缓冲区,最后参数为声音品质,品质越高,声音效果越好,但耗费更多的系统资源。

第三行,系统为soundPool加载声音,第一个参数为上下文参数,第二个参数为声音的id,一般我们将声音信息保存在res的raw文件夹下,如下图所示。

第三个参数为声音的优先级,当多个声音冲突而无法同时播放时,系统会优先播放优先级高的。

第四行就是播放了,第一个参数为id,id即为放入到soundPool中的顺序,比如现在collide.wav是第一个,因此它的id就是1。第二个和第三个参数为左右声道的音量控制。第四个参数为优先级,由于只有这一个声音,因此优先级在这里并不重要。第五个参数为是否循环播放,0为不循环,-1为循环。最后一个参数为播放比率,从0.5到2,一般为1,表示正常播放。

SoundPool只能用于播放音效,因为超过大约5.6秒的声音便播放不出来,而且加载超过大约5.6秒的音效还会导致其它声音播放的问题

游戏开发中,通过资料和书籍了解到在有两种播放音频形式可以用在我们的游戏开发中,第一个:MediaPlayer 类 ;第二个:SoundPool 类!
当然还有一个JetPlayer 但是 播放的文件格式比较麻烦,所以这里抛开不解释,有兴趣的可以去自己研究下

SoundPool存在的一些问题:
1. SoundPool最大只能申请1M的内存空间,这就意味着我们只能使用一些很短的声音片段,而不是用它来播放歌曲或者游戏背景音乐(背景音乐可以考虑使用JetPlayer来播放)。
2. SoundPool提供了pause和stop方法,但这些方法建议最好不要轻易使用,因为有些时候它们可能会使你的程序莫名其妙的终止。还有些朋友反映它们不会立即中止播放声音,而是把缓冲区里的数据播放完才会停下来,也许会多播放一秒钟。
3. 音频格式建议使用OGG格式。使用WAV格式的音频文件存放游戏音效,经过反复测试,在音效播放间隔较短的情况下会出现异常关闭的情况(有说法是SoundPool目前只对16bit的WAV文件有较好的支持)。后来将文件转成OGG格式,问题得到了解决。
4.在使用SoundPool播放音频的时候,如果在初始化中就调用播放函数进行播放音乐那么根本没有声音,不是因为没有执行,而是SoundPool需要一准备时间!囧。当然这个准备时间也很短,不会影响使用,只是程序一运行就播放会没有声音罢了,所以我把SoundPool播放写在了按键中处理了



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



项目开发中碰到的一些常见的不规范的代码

$
0
0

从事java EE开发4年多了,从2011年尾开始参与一个大型电子商务系统,一直做到现在。项目在2012年10月完成了验收,2012年11月开始转运维,巧的是自己跟另外一个同事被客户指定为长期固定运维人员。就这样开始了1年多了运维工作。虽说是运维,但我们不需要管理服务器,不需要管理数据库,做的工作就是修改上生产后项目出现的一些问题(处理用户报障),同时开发一些新的需求(开发新功能),以及不断的优化重构项目源码,处理一些原功能存在的严重性能问题,提炼或从新定义共用的业务API等。

 

下面列举一些我这1年多运维工作中碰到的一些常见的不规范的代码。(之前有整理过文档,所以现在写这个博文一些内容直接copy过来的)

一、存在警告的代码。

1、冗余转换。 

2、集合类未使用泛型。 

3、定义未使用的变量或方法。 

4、import引入了过多的未用到的类。 

 

二、其它不规范的代码

5、在spring容器中存在的bean却使用new方式来构建实例。 

6、业务层捕获了异常却不抛出,导致事物无法回滚,而且异常没有抛出,但功能出错时,使用火狐的firebug插件也看不到错误信息,加大的代码的调式难度。

7、大量定义辅助性的vo类。(个人建议,开发业务功能时,尽可能使用pojo,封装数据或接收参数pojo搞不定的时候使用Map<String, Object>,而不用再定义一个java类,因为里面大量的字段跟pojo是重复的,大量的setter,getter操作也影响性能)

8、NullPointException,在对一个对象进行使用前,没有判断是否非空导致的空指针异常。

9、业务代码写到了controller层。(这样写不但降低了代码的可读性,而且如果controller中写了增、删、改的业务代码时也破坏了事物的完整性,一些对spring理解不深,对事物控制没什么概念的新手经常这样搞)

10、常量类的大量重复定义。(开发时可能各个开发人员很少去看别人的代码,如果项目经理没有统筹规范好的话,很容易导致开发人员自己定义常量自己用,而有些常量是整个系统可以共用的,大量的重复定义加大了维护的难度。)

11、在js中拼写HTML代码。(对于很多jQuery用的不熟的新手经常这样搞)

12、与金额有关的字段,很多被定义成了double类型,其实double是存在精度问题的,应该采用BigDecimal替换之。(运维期修改了很多这样的代码)

 

以上列举了12种常见的不规范代码,下面我贴一些代码举例说明。

1、冗余转换。



上图中因为List使用了泛型,下面的for循环中就不需要强制转型了。(当然转了也不会有错) 

 

2、集合类未使用泛型。 

 

 上诉报警告的代码虽然也没错,但jdk1.5就推出了泛型,所以用集合类时应该养成使用泛型的习惯。

 

3、定义未使用的变量或方法。



 上诉报警告的代码编译也不会错,但是既然一个变量定义了没有用到,就应该删掉。

多定义一个变量,应该就会多占用一点内存。

 

4、import引入了过多的未用到的类。 

 

 这个问题同样也不会引起编译错误,但没用到的类没必要引入进来。可通过按ctrl+shift+o来去除多余的import,也可通过设置eclipse,在保存java文件时自动删除这些多余的import,eclipse的设置方式如下图:



 

5、在spring容器中存在的bean却使用new方式来构建实例。 

我见的最多的是使用在业务bean中注入dataSource,然后new 一个JdbcTemplate对象,其实JdbcTemplate对象也是在spring定义了的,所以直接注入JdbcTemplate对象即可。而且spring容器默认的bean的scope是single=true,即单实例,直接注入JdbcTemplate对象可以避免大量JdbcTemplate实例占用内存(虽然会被gc自动回收掉)。

如下图:



 上图注入的是一个dataSource,其实可以直接注入JdbcTemplate对象的,因为JdbcTemplate对象也定义在了spring容器中,见下图:



 

所以前面的代码可修改为直接注入JdbcTemplate对象,修改后如下图:



 

 

6、业务层捕获了异常却不抛出。



 直接打印了一下,写这样代码的开发人员明显是刚从学校出来的。

 

7、大量定义辅助性的vo类。



 如上图代码,会员各种数据按业务需要分别存放在dealer表跟dealer_personal_info表,当视图层需要会员数据时,需要将这两个表的一些数据进行返回。一些开发人员首先想到的就是新建一个vo类,里面定义好视图层需要的所有字段,然后从数据库查询数据封装好这个vo类并进行返回。这样做其实对性能影响挺大的,特别当查询很多数据,在一个循环里大量的setter getter。让我来重构这个代码,首先我就会删掉DealerInfoVo.java这个类,然后将会员数据封装到一个Map<String, Object>中,然后返回给视图层使用。

修改后的代码如下图:



 现在都流行“约定优于配置”,更应该采用Map的方式。

 

8、NullPointException,在对一个对象进行使用前,没有判断是否非空导致的空指针异常。

这个不举例了,有个1年开发经验的朋友都知道。

 

9、业务代码写到了controller层。

有开发经验的朋友都应该知道业务代码不能写在controller层,而要写到业务层或说应用层。

 

10、常量类的大量重复定义。

为避免大量重复的枚举常量定义,我的建议及现在的做法就是使用枚举类。首先把所有数据表字段有枚举值的字段定义一个枚举类,在开发时告知大家对于数据表枚举字段不允许定义常量,而要统一使用对应的枚举类。

 

以上是个人这些年的一些开发经验总结和见解,特同大家分享,欢迎指正,讨论。



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




面试时,如何向公司提问?

$
0
0
问题一:你们为什么要招聘这个职位?
Q1: Why are you currently recruiting for this position?
这个问题会使得面试官开始谈论当前的项目,或者谈论前一位离职人员。无论哪种情况,都会让你了解,一些与你最密切相关的公司情况。

问题二:你们的新员工多吗?
Q2: Do you have many new staffs?
这个问题起一个过渡作用,使得谈话导向公司内部的情况。但是,它本身也能说明一些问题。如果公司成立已经超过四年,又没有新项目,但是新员工却很多,这往往说明公司文化不是很健康。

问题三:你们公司(团队)目前面临的最大挑战是什么?
Q3: What are the biggest challenges your team are facing right now?
如果面试官开始谈论一些具体的技术问题,这很好;如果他的回答是项目时间紧迫,或者需要更多的资金,那你就要小心一点了,公司管理上面可能有问题。

问题四:什么新技术(编程语言)是你们未来希望采用的?
Q4: What technologies/languages would you like to see your team adapt to that aren't currently being utilised?
如果你申请的是技术职位,面试官恰巧又是技术负责人,那么这个问题将会非常合适。你会对公司的技术路线有所了解和准备,一旦入职,就能更好地适应公司的需要。

问题五:在业务方面,有没有什么地方是你们不满意的,未来想要改进的?
Q5: Few companies, if any, are 100% satisfied with the way their business is operating. If you could simply flick a switch to fix it, what one thing would you change?
很少有公司,会百分之百满意自身的现状,即使那些状况很良好的公司也是如此。这个问题可以让你对公司管理层的关注重点和担忧之处,有所了解。

问题六:我申请的这个职位,对公司的业务有何影响?
Q6: If you struggle to fill the position I have applied for, what impact would that have on the business?
这个问题会让你了解自己在公司的角色,以及你的岗位对公司是否重要。

已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



10个有关编程的至理名言

$
0
0

程序员世界里有哪些名言呢?Jun Auza 列出了一些启迪人心的至理名言,它们大多来自产业界富于经验的人们。
下文列出前10个供读者欣赏:
10. “People think that computer science is the art of geniuses but the actual reality is the opposite, just many people doing things that build on each other, like a wall of mini stones”——Donald Knuth
10. “人们认为计算机科学是天才的艺术,但事实完全相反:只是很多人在共同建立起来的事物之上工作,就像一条由小石头铺成的小径。”——Donald Knuth


9. “First learn computer science and all the theory. Next develop a programming style. Then forget all that and just hack”——George Carrette
9. “首先学会计算机科学和所有的理论,然后发展出一个编程风格,之后便要忘掉所有这些,以自由的方式探索。”——George Carrette


8. “Most of you are familiar with the virtues of a programmer. There are three, of course: laziness, impatience, and hubris”——Larry Wall
8. “大多数的你们都熟悉程序员的美德,它们有三点:懒、不耐烦、以及狂妄自大。”——Larry Wall


7. “Most software today is very much like an Egyptian pyramid with millions of bricks piled on top of each other,with no structural integrity, but just done by brute force and thousands of slaves”——Alan Kay
7. “今日的大多数软件很像埃及金字塔,由千百万砖头堆砌起来,层层相切,没有着整体的结构,是由畜力和成千上万奴隶的力量建立起来的。”——Alan Kay


6. “The trouble with programmers is that you can never tell what a programmer is doing until it’s too late”——Seymour Cray
6. “程序员的问题是,不到太晚,你永远无法知道一个他在做着些什么。”——Seymour Cray


5. “To iterate is human, to recurse divine”——L. Peter Deutsch
5. “人理解迭代,神理解递归。”——Peter Deutsch


4. “On two occasions I have been asked [by members of Parliament]: ‘Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?' I am not able rightly to apprehend the kind of confusion of ideas that could provoke such a question”——Charles Babbage
4. “有两次我被(国会议员)问道:‘ Mr. Babbage,如果你输入计算机错误的数据,正确的答案会出来吗?’

我完全无法理解能产生此种问题的大脑的混乱。”——Charles Babbage


3. “Most good programmers do programming not because they expect to get paid or get adulation by the public, but because it is fun to program”——Linus Torvalds
3. “大部分好的程序员编程并不是为了钱或名望,而只是因为纯粹的乐趣。”——Linus Torvalds


2. “Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live”——Martin Golding
2. “编程的时候,总是想着那个维护你代码的人会是一个知道你住在哪儿的有暴力倾向的精神病患者。”——Martin Golding


1. “There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies”—— C.A.R. Hoare
1. “有两种生成一个软件设计方案的途径。一个是把它做得如此简单,以致于明显不会有漏洞存在。另一个是把它做的如此复杂,以致于不会有明显的漏洞存在。”——C.A.R. Hoare



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



9 個提升 WordPress 網站安全性的方法

$
0
0

本文參考資料為 How to Improve the Security of your WordPress Blog, 已取得原作者 Amit Agarwal 授權。

大約一個月前,這個部落格被黑客入侵(編按:Amit Agarwal 的網站)。而其他托管於相同主機商的網站像是 ctrlq.org 和 hundredzeros.com 也深受其害,黑客成功從網路上拿下了這些網站。

托管網站的主機商表示這可能發生於某些使用舊版的 WordPress 網站,導致密碼不幸洩漏,這段時間雖然歷經一些艱難,但幸運的是被刪除的網站已經回復,且流量也回到正常。

9 個提升 WordPress 網站安全性的方法

以下是我所做的變更,用來提高我的 WordPress 網站安全性,縱使這樣的意外可能再次發生。

#1 使用你的 Email 作為登入帳號

當你安裝完一個 WordPress 網站時,預設的第一位用戶為 “admin”。你應該建立不同的使用者名稱來管理你的 WordPress 網站,並將預設使用者 “admin” 刪除,或是將它的權限從「系統管理員」降級為「讀者」。

你也可以建立一個完全亂數(難以被猜中)的使用者名稱,然後使用你的 Email 來登入 WordPress。外掛 WP-Email Login可以加入此支援,使用你的 Email 取代帳號登入。

#2 不要向全世界展示你的 WordPress 版本

WordPress 網站會在原始碼顯示版本號,讓其他人能夠知道你正在執行舊的 WordPress。

要從網頁裡移除 WordPress 版本是很簡單的一件事,但你需要做一些額外的補強,從你的 WordPress 目錄將 readme.html 檔案刪除,因為它也會把你所使用的 WordPress 版本展示給全世界。

#3 別讓其他人擁有”寫入”你 WordPress 目錄的權限

登入你的 WordPress 網站 Linux 系統列,執行以下指令來取得所有「公開」、其他用戶皆能寫入的目錄清單。

find . -type d -perm -o=w

你也許可以執行以下兩行指令,來將你 WordPress 內的檔案和目錄設定為正確的權限( 參考資料)。

find /your/wordpress/folder/ -type d -exec chmod 755 {} \;
find /your/wordpress/folder/ -type f -exec chmod 644 {} \;

對目錄來說,755(rwxr-xr-x) 意味著只有擁有者具備寫入權限,其他人只有讀取和執行的權限。對檔案來說,644 (rw-r–r–) 意味著只有檔案擁有者具備讀取和寫入權限,其他人為唯讀。

#4 重新命名你的 WordPress 資料表前綴

如果你使用預設選項來安裝 WordPress 的話,你的 WordPress 資料表應該會像是 wp_posts 或 wp_users。將資料表的前綴(wp_)更改為其他隨機值是比較好的作法,外掛  Change DB Prefix 可以讓你在彈指之間重新命名你的資料表前綴。

#5 防止使用者瀏覽你的 WordPress 目錄結構

這很重要。開啟你 WordPress 根目錄底下的 .htaccess 檔案,然後在最上方加入這行。

Options -Indexes

這能夠防止其他人在能建立檔案清單時看到你資料夾內的所有檔案。例如目錄下缺少預設的 index.php 或 index.html 時。

#6 更新 WordPress 安全密鑰

開啟此網頁來為你的 WordPress 網站產生八組安全密鑰。開啟 WordPress 目錄下的 wp-config.php 檔案,將預設的密鑰以產生的密鑰取而代之。

這些隨機的字串能使你儲存於 WordPress 的密碼更加安全,另一個好處是,當有人在你不知情的情抗下登入 WordPress,他們將會被立即登出,使他們的 cookies 失效。

#7 保留 WordPress PHP 和資料庫錯誤記錄

從錯誤記錄有時候可以發現針對你 WordPress 所發出的無效資料庫查詢或檔案查詢。我更喜歡外掛 Error Log Monitor,因為它能定期透過 Email 發送錯誤日誌到你的信箱,也能顯示於你的 WordPress 控制台。

要在 WordPress 啟用錯誤日誌功能,將以下程式碼加入你的 wp-config.php 檔案,記得要把 /path/to/error.log 替換為你的日誌文件實際路徑。error.log 應該放在無法直接從瀏覽器存取得到的目錄。( 參考資料

define('WP_DEBUG', true);
if (WP_DEBUG) {
 define('WP_DEBUG_DISPLAY', false);
 @ini_set('log_errors', 'On');
 @ini_set('display_errors', 'Off');
 @ini_set('error_log', '/path/to/error.log');
}

#8 以密碼保護 Admin 控制台

使用密碼來保護 wp-admin 目錄是一個不錯的方法,因為瀏覽你的公開 WordPress 網站並不需要用到這目錄下的任何檔案。一旦設定完成,即使是授權的用戶也需要輸入兩道密碼才能登入他們的 WordPress 控制台。

#9 追蹤你的 WordPress 伺服器登入動態

你可以在 Linux 下使用 “last -i” 指令來列出所有登入你 WordPress 伺服器的使用者,包括他們的 IP 位址。如果你發現清單內有未知的 IP 來源,那肯定要修改密碼了。

此外,下面的指令將顯示較長時間區間的登入動態,並使用 IP 位址分組(將 USERNAME 改為你的使用者名稱)。

last -if /var/log/wtmp.1 | grep USERNAME | awk '{print $3}' | sort | uniq -c

使用外掛來監控你的 WordPress 網站

WordPress.org 外掛庫包含不少好用的安全相關外掛,可以持續監控你的 WordPress 網站是否有被入侵,或是其他可疑活動。這些是我會建議使用,也較為基本的安全外掛。

  1. Exploit Scanner– 它會迅速掃描你的所有 WordPress 檔案和文章,並列出潛藏惡意程式碼的。例如垃圾鏈結可能會使用 CSS 或 IFRAME 方式隱藏在你的 WordPress 網誌文章裡,而這個外掛可以將它們找出來。
  2. WordFence Security– 這是一個非常強大、且應該使用的安全外掛。它會比對你 WordPress 的核心檔案和原始檔案間是否已被修改。而且,該外掛會鎖定嘗試登入你的網站卻失敗的使用者。
  3. WordPress Sentinel– 另一個實用的外掛,可以監控你的 WordPress 檔案,當有任何檔案被加入、刪除或修改時會發出警告。
  4. WP Notifier– 如果你不常登入你的 WordPress 控制台,那這外掛適合你。它會在你安裝的佈景主題、外掛和 WordPress 核心有新的更新時以 Email 通知你。
  5. VIP Scanner– “官方”安全外掛將掃描你的 WordPress 佈景主題有無任何問題,它也能檢測出有無任何的廣告程式碼被注入你的 WordPress 佈景主題裡。

小技巧:你也可以使用以下 Linux 指令來列出近三天被修改的檔案清單。將 mtime 改為 mmin 可以看到 “n” 分鐘前被修改的檔案清單。

find . -type f -mtime -3 | grep -v "/Maildir/" | grep -v "/logs/"

提高 WordPress 登入頁面安全性

你的 WordPress 登入頁面是每個人都可以存取的,但如果你想防止未授權的使用者登入 WordPress,你有以下三種選擇。

  1. 使用 .htaccess 加入密碼保護 – 在 WordPress 認證以外加入另一道帳號密碼來保護你的 wp-admin 目錄。
  2. Google Authenticator– 這出色的外掛能為你的 WordPress 加入兩步驟驗證功能。除了輸入正確的密碼外,還必須搭配手機應用程式來輸入隨機產生的驗證碼。
  3. Login Dongle– 這個外掛使用一個非常獨特的方法來保護你的 WordPress。它能產生一個書籤列(加上秘密問題),你可以將它加入瀏覽器。當你要登入 WordPress 時,輸入你的密碼並按下書籤列才能登入 WordPress – 登入頁面的按鈕將無法使用。

深入閱讀:

喜歡 免費資源網路社群的文章嗎?歡迎追蹤我們的 FacebookTwitterGoogle+,或是 透過 Email 訂閱更新

修改resolv.conf应对DNS污染

$
0
0

测试于 OpenSuse 13.1,Xfce。

ray@OpenSUSE-Ray:~> cd /etc
ray@OpenSUSE-Ray:/etc> su
Password:
OpenSUSE-Ray:/etc # leafpad resolv.conf

在里面添加:

nameserver 127.0.0.1
nameserver 168.95.192.1
nameserver 168.95.192.2
nameserver 156.154.70.22
nameserver 156.154.71.22
nameserver 203.80.96.9
nameserver  203.80.96.10

最后:

OpenSUSE-Ray:/etc # dnsmasq restart

Over

如何阅读公司的项目代码

$
0
0

看到ITFriend中有些小伙伴刚进入公司,面对大量的项目代码,往往手足无措,有点慌,我说下我的经历。

声明:我从事的是C++开发工作,阅读的代码都是基于客户端/服务器的,以下谈的是C++项目代码的阅读方法多些

希望懂java的小伙伴分享下java公司项目代码的阅读方法,小弟感激不尽。

    编译代码把项目跑起来  代码到手以后,先尝试着编译一下,无非就是库的配置,库路径的配置,缺少某个文件,或者头文件包含路径不正确,一般公司给你的代码都可以编译成功,windows平台下一般使用vs工具来编译,集成调试工具,而Linux下编译.使用gcc(g++)编译器 make工具或者automake工具,具体用法在搜索引擎上能找到。编译成功以后把项目跑起来,我喜欢看看客户端长啥样,哈哈,恭喜你,迈出了第一步

     看项目文档,和项目开发人员(维护人员)进行沟通    公司的项目代码会有相关的设计文档,比如XX项目概要设计文档、XX项目详细设计文档、XX项目模块设计以及之间依赖关系的文档等等,通过这些文档,软件开发人员起码对项目有个整体的认识,比如项目本身是解决什么领域问题的?有哪些功能模块以及他们的大体功能是什么?使用了什么开源库?通过文档还可以了解到代码中的一些数据结构的详细注释,网络传输是如何设计的,使用了哪些协议?数据库中的字段是如何设计的,以及数据库中各个表之间的关系。在看代码的过程中,遇到模糊不清或者自己拿不住的代码,要及时和同事沟通,他们对代码的熟悉程度比我们强,虚心有礼貌的问人家。否则自己在那憋着瞎猜代码的含义,费时费力还得不偿失,开口求人帮忙就那么难?嘻嘻,你帮我下,我帮你下,咱俩慢慢就熟悉了嘛

     搞清楚程序的流程和大体框架,具体功能模块具体分析    从main函数开始,把客户端和服务器怎么工作的流程大致的看一下,客户端如何连接服务器段的?服务器客户端是如何进行数据交互的?在关键的地方下断点,看看客户端发送来的内容是什么,服务器在接收到客户端发送来的消息后是如何处理的。

个人建议,先把程序的流程和整体框架搞懂一点,明白一些函数大概是实现什么功能就可以,现在不要去过多的关注和沉迷在功能模块的实现细节中,当需要定位到具体的某个功能实现模块的时候,再切换到函数实现中一点点分析代码就ok。如果对某个地方不太清楚,可以自己修改下源代码,运行一下看看运行的结果怎样。我刚开始的时候不敢改源代码,怕改错了项目就不好使了,领导会骂我,先拷贝一份编译好的源代码,然后随意改,反正我还有一份备份的源代码,我不怕。

     个人兴趣驱动或者debug驱动方式来看代码    说实话,单纯的看代码真心没劲枯燥,而且效率不高。我们可以自我驱动看代码,比如思考某个功能是如何实现的呢?然后猜测可能是如何实现的,然后再项目代码中一点点去跟踪源代码,慢慢的剥茧抽丝,相信你会有恍然大悟的感觉。还有一种就是公司会用改bug的方式来驱动你熟悉代码,我刚进公司的时候就是这样,解决一个bug后,不仅仅对于项目整体流程熟悉了,也对其中的几个模块熟悉了,一举两得。

    UML帮助我们整理思路    使用UML帮助我们整理思维,使用类图来分析类与类之间的关系,使用UML里的活动图,配合IDE工具分析核心业务流程,理解软件是如何工作的。使用UML来整理程序流程这招,还是在我进入公司以后,带我的老师教我的,个人感觉非常受益,程序开发人员在初期的时候可能对于画UML图有点不适应,慢慢就习惯了。对于整理思路挺有帮助的,希望大家养成个好习惯,嘻嘻。
     具体的模块分析    对需要的具体的代码进行详细分析。搞清变量的意义和关联关系,搞清实现的逻辑和算法。好的代码,此处是会有注释的。可以自己F11走到项目代码中,然后单步调试,看看每个变量是如何变化的,会加深对这个函数中的逻辑和算法的理解。

     心理状态的调整    项目源代码的代码量多没啥,但是我们自己心里不要着急,慢慢来,自己努力的功夫到了,多花点时间在看代码思考代码上,公司的代码会被你一点点啃透的。如果遇到看不懂代码的情况,就分析下自己为什么看不懂?是项目使用了自己不熟悉的设计模式?还是一些业务领域基础知识自己没有掌握(比如我自己做视频服务器开发,就需要看很多资料去了解视频格式,视频压缩,视频编码以及视频传输的一些协议)?或者是自己对项目框架的理解不对导致的,要自己主动的分析

ps:有一本《代码阅读方法和实践》(code reading),会教你怎样去阅读源代码。

作者:haolipengzhanshen 发表于2014-7-1 21:07:26 原文链接
阅读:0 评论:0 查看评论
Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>