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

如何更有效地与各级别员工沟通?

$
0
0

Feedback

LinkedIn CEO韦纳推荐了一套系统,可以更有效地与各级别员工沟通。

几年以前,一位赫赫有名的消费者网络公司创始人暨首席执行官曾分享过这样的趣闻:有一家公司的产品团队花费了几个星期时间,加班加点地想要完成在一个至关重要的发布日如期推出产品的目标。

在对这种产品进行最终评测的过程中,这家公司的首席执行官不经意地说道,他原本希望该产品的一个关键设计元素以另一种颜色出现。说完以后,他自己并没把这句话当回事儿,而是继续进行评测了。

但在几个星期以后,这位首席执行官怒气冲冲地质问产品团队为何会错过发布日,后者作出的解释是,他们暂时搁置了所有工作,全力对这种产品进行重新设计,以便使其符合他的口味。对于这个回答,这位首席执行官作出的反应是:“你们都做了什么啊?!”

其结果就是,这位首席执行官的“非正式观察”被产品团队错误地解读为“全员集合”式的命令,从而导致这种产品被迫推迟发布。

尽管这只是极端的例子,但确实凸显出了一个重要的问题:你所处的职位越高,那么发生这种事情的可能性就越大。对于那些经验不足的管理者来说,由于他们尚未适应自己新获得的权威,因此这个问题可能会显得尤其尖锐;然而,即使是在那些从业时间很长的公司高管中,类似的情况也屡见不鲜,这是因为他们已经忘记了自己的职位会给整个公司投下多么长的影子。

在几年以前,一名直接向我汇报工作的下属让我认识到了这一点。虽然他和他的团队欢迎我提出的意见,但他注意到,很多时候我所认为的“非此即彼”的言论会带来一场极具破坏性的“消防训练”,让所有人都在突然之间变得忙个不停。直到那时,我才发现原来我的观点会被如此看重。

为了解决这个问题,确保我和这个团队在遇到与此类似的情况时能保持一致的步调,我们将我可能提供的任何反馈信息(无论是在对话中提供的信息,还是通过电子邮件提供的信息)划分成了三个类别:个人观点、强烈建议以及强制命令。

后来的实施结果证明,这套系统起到了很大的帮助作用,因此我一直沿用至今。在这套系统的帮助下,我得以更加有效地与公司各个级别的员工进行沟通,尤其是那些可能还不太熟悉我的反馈信息风格或频率的新员工。

1.个人观点

在我提供的反馈信息中,绝大多数都可归入个人观点这一类别中。基本上来说,这是一种主观的、带有轶事性质的观点;也就是说,公司员工应该将其视为来自于一名用户、客户或是团队成员的观点。在完成沟通以后,到底是否按照我所提出的这种建议来采取行动,完全由项目的负责人来作出决定。

当提供个人观点这一类别的反馈信息时,你的头衔和权力都不应被考虑在内。需要指出的是,想要做到一点可谓知易行难,原因是你可能需要花费一些时间才能让团队中的初级成员认识到,对于他们挑战一名高级上司所作指导的作法,你确实是并不在意。

鉴于团队本身会拥有自己的强烈信仰,而且团队成员会忙于很多具有更高优先性的工作,再加上对于你所提出的产品改变,他们才是更加了解具体规格和成本的人,因此你要做好你的建议会被他们所摒弃的准备。如果你所给出的确实只是个人观点,而同时你手下又拥有真正合适的人才,那么你的建议遭到抛弃其实是件好事。

2.强烈建议

比“个人观点”类别高一级的则是“强烈建议”。与前者相比,“强烈建议”的程度当然要更高一些,但仍旧并非由你告诉产品团队该做些什么。

通常情况下,当一名高管想要通过自身经验来向产品团队给出建议,但同时又希望给后者以冒险、犯错和磨练其才能的机会,并最终扩大公司内部决策程序的范畴时,可使用“强烈建议”这种类别的反馈信息。

想要以这种方式来成功地提供反馈信息,那么你就必须信任你所任命的负责人,赋予他们以最终发言权。与此同时,想要做到这一点还需要团队本身能保持开放的思路,并在必要的时候尊重你所作出的更好的判断。不管是哪种情况,都需要你自己或是产品团队控制住自己的自负情绪,作出最符合公司利益的决定。

如果说这样的要求听起来像是一种很难达成的平衡的话,那是因为事实确实如此。

3.强制命令

在有些时候,无可避免地会出现产品团队判断错误的情况,这种错误的判断可能会带来非常严重的后果,而此时你需要做的就是先发制人地阻止这种后果成为事实,这跟允许产品团队冒险、犯错并从错误中汲取经验教训是一样重要的。

打个比方,如果你想要教会别人知道醉驾到底有多么危险,那么你要做的不是等着他们去喝点酒然后坐上驾驶座,从而让他们从亲身体验中认识到醉驾的危险性。

作为一名管理者,你需要判定如果一个团队履行你不认同的一项决定,那么随之而来的风险将会有多么巨大。然后,你必须确保如果这种风险有可能会对个人、团队和/或公司造成持续性的损害,那么你就应该采取合适的措施来干预其中,并在阻止这种风险以后对相关团队进行教育。

在确有必要时发布强制性的命令,能让公司避免犯下代价高昂的错误,这对公司来说是非常有利的。但是,如果过于频繁地发布强制命令,或是在没有正确理由可提供支持的情况下发布这种命令,那么这种表现就意味着你对团队缺乏信任,从而导致团队失去动力。在有可能的情况下,还是尽量避免采用这种类型的信息反馈方式为好。

作者:杰夫·韦纳(Jeff Weiner),LinkedIn首席执行官。( 本文最初发表在LinkedIn

来源: 腾讯科技


(关注更多人人都是产品经理观点,参与微信互动(微信搜索“人人都是产品经理”或“woshipm”)


谈谈系统架构这个东西

$
0
0

架构这个词在很多人看来都是很高大上的一个东西。事实上,搞架构的这些人却也都是些大神,至少都是在这个领域浸淫N久的专家级人物。现在很火的全栈工程师这个概念,就是架构师的另一种表现形式。  

之于架构,其含义无非是一个从技术细节跳出来自上而下宏观地看待系统的一个思维,就好比房屋建筑一样。架构师的角色和建筑设计师在某种意义上是相同的。在微博上看到蔡学镛分享过这么一个架构设计流程的图,从中或多或少能看出架构设计一个大概的流程。

首当其冲的,肯定是需要对整个系统的业务进行拆分,进行业务设计,目的就是要捋清楚系统是干什么的,能提供什么功能,对系统的需求要做到详尽的分析和考虑。不过这部分,在我参与过的一些项目看来,尤其是对现在普遍使用的敏捷开发流程来说,无需考虑的太面面俱到,但至少不能太窄或者偏离正轨,后续的开发过程会不断的反馈回来进行调整。  

接下来,系统的业务明确之后,交互设计和领域建模便可以同时执行。当然,这里我是觉得交互设计和架构师是没啥关系的,顶多就是两者要相辅相成。而领域建模这个就显得很重要了。领域建模是业务设计的主要逻辑,把现实中的业务转化成抽象的对象,这个确实是能力的体现了。我觉得这一部分很多出色的架构师相比其他人突出的一个很关键的地方。  

技术模块设计则是在理解了系统的业务需求之后,对整体的一个技术框架上的设计。这里对于技术架构,我一直有一个分不太清楚的东西,就是软件架构和系统架构。说到底,这两者都是软件层面的含义,所不同的是前者到了代码层面,而系统架构则是到了软件层面。软件架构是位于系统架构之上的。一个系统,使用了Spring、Hibernater然后用了MVC设计模式,这就是软件架构;一个系统分成负载均衡模块、Link模块、队列模块、数据模块、推送模块等等则就是系统架构。再往下就应该是部署架构了,比如系统部署了几个结点、结点之间的关系、网络的规划结构、系统的高可用、可扩展等等。当然对于一个系统来说,数据的设计是可以拿出来重点进行的,毕竟对于互联网应用来说,数据 is all,系统的很多性能、效率问题是和数据的存储设计有密切关系的。  

到最后,业务之上的这些设计会反作用于业务,将系统的关键点反馈回来,从而对业务进行调整,进而再推进整个架构的流程。现在很火的敏捷开发,某种角度看来就是一个不断迭代、反馈的过程,是传统架构设计的一种演化形式。    

以上对架构的一些理解,很多地方自认还是有点迷糊。在进行系统设计的时候,也经常摸不着头脑,把不同层次的东西混为一谈。记得蔡学镛大神之前还分享过一张图片,对架构讲的挺透彻的。不明白的时常看看这个,会有种茅塞顿开的感觉。



  



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


ITeye推荐



DBCP、C3P0、Proxool 、 BoneCP开源连接池的比较(转)

$
0
0

转载地址: http://blog.csdn.net/miclung/article/details/7231553

  简介  使用评价 项目主页
 DBCP

DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池.DBCP可以直接的在应用程序用使用

 

可以设置最大和最小连接,连接等待时间等,基本功能都有,此连接池的持续运行的稳定性还是可以,不过速度稍慢,在大并发量的压力下稳定性有所下降,此外不提供连接池监控

 

http://homepages.nildram.

co.uk/~slink/java/DBPool/

 C3P0  C3P0是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。

 

连接池可以设置最大和最小连接,连接等待时间等,基本功能都有,连接池的持续运行的稳定性相当不错,在大并发量的压力下稳定性也有一定保证,此外不提供连接池监控。

 

http://sourceforge.net/projects/c3p0
 Proxool  这是一个Java SQL Driver驱动程序,提供了对你选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中。完全可配置。快速,成熟,健壮。可以透明地为你现存的JDBC驱动程序增加连接池功能。 连接池可以设置最大和最小连接,连接等待时间等,基本功能都有,连接池的持续运行的稳定性有一定问题,有一个优势--连接池监控  http://proxool.sourceforge.net/
 BoneCP  BoneCP是一个快速,开源的数据库连接池。帮你管理数据连接让你的应用程序能更快速地访问数据库。比C3P0/DBCP连接池快25倍  一种新的数据连接技术,以其效率高,速度 快著称,连接池可以设置最大和最小连接,连接等待时间等,基本功能都有          

http://jolbox.com/about.html          

 

 
  参数说明
 DBCP  dataSource: 要连接的 datasource (通常我们不会定义在 server.xml)
defaultAutoCommit: 对于事务是否 autoCommit, 默认值为 true
defaultReadOnly: 对于数据库是否只能读取, 默认值为 false
driverClassName:连接数据库所用的 JDBC Driver Class,
maxActive: 可以从对象池中取出的对象最大个数,为0则表示没有限制,默认为8
maxIdle: 最大等待连接中的数量,设 0 为没有限制 (对象池中对象最大个数)
minIdle:对象池中对象最小个数
maxWait: 最大等待秒数, 单位为 ms, 超过时间会丟出错误信息
password: 登陆数据库所用的密码
url: 连接数据库的 URL
username: 登陆数据库所用的帐号
validationQuery: 验证连接是否成功, SQL SELECT 指令至少要返回一行
removeAbandoned: 是否自我中断, 默认是 false
removeAbandonedTimeout: 几秒后会自我中断, removeAbandoned 必须为 true
logAbandoned: 是否记录中断事件, 默认为 false
minEvictableIdleTimeMillis:大于0 ,进行连接空闲时间判断,或为0,对空闲的连接不进行验证;默认30分钟
timeBetweenEvictionRunsMillis:失效检查线程运行时间间隔,如果小于等于0,不会启动检查线程,默认-1
testOnBorrow:取得对象时是否进行验证,检查对象是否有效,默认为false
testOnReturn:返回对象时是否进行验证,检查对象是否有效,默认为false
testWhileIdle:空闲时是否进行验证,检查对象是否有效,默认为false
initialSize:初始化线程数
 C3P0  acquireIncrement: 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3
acquireRetryAttempts: 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30
acquireRetryDelay: 两次连接中间隔时间,单位毫秒。Default: 1000
autoCommitOnClose: 连接关闭时默认将所有未提交的操作回滚。Defaul t: false 
automaticTestTable: c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么属性preferredTestQuery将被忽略。你不 能在这张Test表上进行任何操作,它将只供c3p0测试使用。Default: null
breakAfterAcquireFailure: 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用getConnection()的时候继续尝试获取连 接。如果设为true,那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。Default: false
checkoutTimeout:当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。Default: 0
connectionTesterClassName: 通过实现ConnectionTester或QueryConnectionT ester的类来测试连接。类名需制定全路径。Default: com.mchange.v2.c3p0.impl.Def aultConnectionTester
factoryClassLocation: 指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可Default: null
idleConnectionTestPeriod: 每60秒检查所有连接池中的空闲连接。Defaul t: 0
initialPoolSize: 初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3
maxIdleTime: 最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0
maxPoolSize: 连接池中保留的最大连接数。Default: 15
maxStatements: JDBC的标准参数,用以控制数据源内加载的PreparedSt atements数量。但由于预缓存的statements属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。如 果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0
maxStatementsPerConnection: maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0
numHelperThreads:c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能通过多线程实现多个操作同时被执行。Default: 3
overrideDefaultUser:当用户调用getConnection()时使root用户成为去获取连接的用户。主要用于连接池连接非c3p0的数据源时。Default: null
overrideDefaultPassword:与overrideDefaultUser参数对应使用的一个参数。Default: null
password:密码。Default: null
user:用户名。Default: null
preferredTestQuery:定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意:测试的表必须在初始数据源的时候就存在。Default: null
propertyCycle:用户修改系统配置参数执行前最多等待300秒。Defaul t: 300 
testConnectionOnCheckout:因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交 的时候都将校验其有效性。建议使用idleConnectio nTestPeriod或automaticTestTable等方法来提升连接测试的性能。Default: false 
testConnectionOnCheckin:如果设为true那么在取得连接的同时将校验连接的有效性。Default: false
 Proxool  acquireIncrement: 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3
fatal-sql-exception : 它是一个逗号分割的信息片段.当一个SQL异常发生时,他的异常信息将与这个信息片段进行比较.如果在片段中存在,那么这个异常将被认为是个致命错误 (Fatal SQL Exception ).这种情况下,数据库连接将要被放弃.无论发生什么,这个异常将会被重掷以提供给消费者.用户最好自己配置一个不同的异常来抛出.
fatal-sql-exception-wrapper-class : 正如上面所说,你最好配置一个不同的异常来重掷.利用这个属性,用户可以包装SQLException,使他变成另外一个异常.这个异常或者继承 SQLException或者继承字RuntimeException.proxool 自带了2个实现:'org.logicalcobwebs.proxool .FatalSQLException' 和'org.logicalcobweb s.proxool .FatalRuntimeException' .后者更合适.
house-keeping-sleep-time : house keeper 保留线程处于睡眠状态的最长时间,house keeper 的职责就是检查各个连接的状态,并判断是否需要销毁或者创建.
house-keeping-test-sql : 如果发现了空闲的数据库连接.house keeper 将会用这个语句来测试.这个语句最好非常快的被执行.如果没有定义,测试过程将会被忽略。
injectable-connection-interface : 允许proxool 实现被代理的connection对象法.
injectable-statement-interface : 允许proxool 实现被代理的Statement 对象方法.
injectable-prepared-statement-interface : 允许proxool 实现被代理的PreparedS tatement 对象方法.
injectable-callable-statement-interface : 允许proxool 实现被代理的CallableStat ement 对象方法.
jmx : 如果属性为true,就会注册一个消息Bean到jms服务,消息Bean对象名: "Proxool:type=Pool, name=<alias>". 默认值为false.
jmx-agent-id : 一个逗号分隔的JMX代理列表(如使用MbeanServerFactory .fi ndMBeanServer(String agentId)注册的连接池。)这个属性是仅当"jmx"属性设置为"true"才有效。所有注册jmx服务器使用这个属性是不确定的
jndi-name : 数据源的名称
maximum-active-time : 如果housekeeper 检测到某个线程的活动时间大于这个数值.它将会杀掉这个线程.所以确认一下你的服务器的带宽.然后定一个合适的值.默认是5分钟.
maximum-connection-count : 最大的数据库连接数.
maximum-connection-lifetime : 一个线程的最大寿命.
minimum-connection-count : 最小的数据库连接数
overload-without-refusal-lifetime : 这可以帮助我们确定连接池的状态。如果我们已经拒绝了一个连接在这个设定值(毫秒),然后被认为是超载。默认为60秒。
prototype-count : 连接池中可用的连接数量.如果当前的连接池中的连接少于这个数值.新的连接将被建立(假设没有超过最大可用数).例如.我们有3个活动连接2个可用连接, 而我们的prototype-count是4,那么数据库连接池将试图建立另外2个连接.这和 minimum-connection-count不同. minimum-connect ion-count把活动的连接也计算在内.prototype-count 是spare connections 的数量.
recently-started-threshold :这可以帮助我们确定连接池的状态,连接数少还是多或超载。只要至少有一个连接已开始在此值(毫秒)内,或者有一些多余的可用连接,那么我们假设连接池是开启的。默认为60秒
simultaneous-build-throttle :这是我们可一次建立的最大连接数。那就是新增的连接请求,但还没有可供使用的连接。由于连接可以使用多线程,在有限的时间之间建立联系从而带来可用连 接,但是我们需要通过一些方式确认一些线程并不是立即响应连接请求的,默认是10。
statistics :  连接池使用状况统计。 参数“10s,1m,1d”
statistics-log-level :日志统计跟踪类型。 参数“ERROR”或 “INFO”
test-before-use : 如果为true,在每个连接被测试前都会服务这个连接,如果一个连接失败,那么将被丢弃,另一个连接将会被处理,如果所有连接都失败,一个新的连接将会被建立。否则将会抛出一个SQLException异常。
test-after-use : 如果为true,在每个连接被测试后都会服务这个连接,使其回到连接池中,如果连接失败,那么将被废弃。
trace : 如果为true,那么每个被执行的SQL语句将会在执行期被log记录(DEBUG LEVEL).你也可以注册一个ConnectionListener (参看ProxoolFacade)得到这些信息
 BoneCP  acquireIncrement: 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3
driveClass:数据库驱动
jdbcUrl:响应驱动的jdbcUrl
username:数据库的用户名
password:数据库的密码
idleConnectionTestPeriod:检查数据库连接池中控线连接的间隔时间,单位是分,默认值:240,如果要取消则设置为0
idleMaxAge:连接池中未使用的链接最大存活时间,单位是分,默认值:60,如果要永远存活设置为0
maxConnectionsPerPartition:每个分区最大的连接数
minConnectionsPerPartition:每个分区最小的连接数
partitionCount:分区数,默认值2,最小1,推荐3-4,视应用而定
acquireIncrement:每次去拿数据库连接的时候一次性要拿几个,默认值:2
statementsCacheSize:缓存prepared statements的大小,默认值:0
releaseHelperThreads:每个分区释放链接助理进程的数量,默认值:3,除非你的一个数据库连接的时间内做了很多工作,不然过多的助理进程会影响你的性能
 
性能比较:
     
 DBCP  C3P0  Proxool  BoneCP
模拟5个线程循环10次并发访问数据库 用时1181ms 用时860ms 用时1563ms 用时31ms
模拟10个线程循环10次并发访问数据库 用时1188ms 用时953ms 用时1625ms 用时63ms
模拟30个线程循环10次并发访问数据库 用时1250ms 用时1047ms 用时1657ms 用时156ms
模拟50个线程循环10次并发访问数据库 用时1406ms 用时1343ms 用时1843ms 用时172ms
模拟100个线程循环10次并发访问数据库 用时1641ms 用时2703ms 用时2031ms 用时532ms
模拟200个线程循环10次并发访问数据库 用时2093ms 用时4891ms 用时2406ms 用时936ms
模拟500个线程循环10次并发访问数据库用时3219ms用时11703ms用时3343ms用时1922ms
模拟800个线程循环10次并发访问数据库用时4688ms用时12063ms用时4141ms

用时2859ms

模拟1000个线程循环10次并发访问数据库用时5187ms用时12563ms用时4703m用时3610ms
模拟3000个线程循环10次并发访问数据库用时14094ms用时16297ms用时11344ms用时11391ms
模拟5000个线程循环10次并发访问数据库用时23610ms用时22032ms用时20125ms用时17125ms


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


ITeye推荐



Javascript的DOM操作

$
0
0
1.访问节点
document.getElementById(id);
返回对拥有指定id的第一个对象进行访问

document.getElementsByName(name);
返回带有指定名称的节点集合
注意:Elements

document.getElementsByTagName(tagname);
返回带有指定标签名的对象集合
注意:Elements

document.getElementsByClassName(classname);
返回带有指定class名称的对象集合
注意:Elements

2.生成节点
document.createElement(eName);
创建一个节点

document.createAttribute(attrName);
对某个节点创建属性

document.createTextNode(text);
创建文本节点

3.添加节点
document.insertBefore(newNode,referenceChild);
在某个节点前插入节点

parentNode.appendChild(newNode);
给某个节点添加子节点

4.复制节点
cloneNode(true | false);
复制某个节点
参数:是否复制原节点的所有属性

5.删除节点
parentNode.removeChild(node)
删除某个节点的子节点
node是要删除的节点
注意:IE会忽略节点间生成的空白文本节点(例如,换行符号),而Mozilla不会这样做。在删除指定节点的时候不会出错,但是如果要删除最后一个子结点或者是第一个子结点的时候,就会出现问题。这时候,就需要用一个函数来判断首个子结点的节点类型。
元素节点的节点类型是 1,因此如果首个子节点不是一个元素节点,它就会移至下一个节点,然后继续检查此节点是否为元素节点。整个过程会一直持续到首个元素子节点被找到为止。通过这个方法,我们就可以在 Internet Explorer 和 Mozilla 得到正确的方法。

6.修改文本节点
appendData(data);
将data加到文本节点后面

deleteData(start,length);
将从start处删除length个字符

insertData(start,data)
在start处插入字符,start的开始值是0;

replaceData(start,length,data)
在start处用data替换length个字符

splitData(offset)
在offset处分割文本节点

substringData(start,length)
从start处提取length个字符

7.属性操作
getAttribute(name)
通过属性名称获取某个节点属性的值

setAttribute(name,value);
修改某个节点属性的值

removeAttribute(name)
删除某个属性

8.查找节点
parentObj.firstChild
如果节点为已知节点的第一个子节点就可以使用这个方法。此方法可以递归进行使用
parentObj.firstChild.firstChild.....

parentObj.lastChild
获得一个节点的最后一个节点,与firstChild一样也可以进行递归使用
parentObj.lastChild.lastChild.....

parentObj.childNodes
获得节点的所有子节点,然后通过循环和索引找到目标节点

9.获取相邻的节点
neborNode.previousSibling :获取已知节点的相邻的上一个节点
nerbourNode.nextSlbling: 获取已知节点的下一个节点

10.获取父节点
childNode.parentNode:得到已知节点的父节点

作者:u011043843 发表于2014-6-5 0:58:09 原文链接
阅读:141 评论:0 查看评论

Apache Qpid 0.28 发布,企业通讯解决方案

$
0
0

Apache Qpid 0.28 发布了,该版本包含众多的新特性和 bug 修复,详细列表请看 发行说明

Apache Qpid (Open Source AMQP Messaging) 是一个跨平台的企业通讯解决方案,实现了高级消息队列协议。提供了 Java、C++ 两种服务端版本以及 Java、C++、.NET、Python和Ruby语言的客户端。

其中C++版本的服务器端具备高性能/低消耗以及RDMA支持,可运行于Windows/Linux和Solaris平台;而Java版本的服务器则支持JMS,以及各种平台。

关于RDMA的相关知识:


写在阿里去IOE一周年

$
0
0

文/ 任英杰

去年5月17日,阿里巴巴支付宝最后一台IBM小型机在下线,标志着阿里完成去IOE。随后一场去IOE运动不断发酵,甚至传闻IBM中国去年损失了20%的合同额。


去了IOE,奔向何方?
阿里的去IOE一声吆喝引来众多附和必有原因。从当前大背景看,经过二十年的信息化建设浪潮,国内市场逐渐成熟了,客户经过多轮教育也觉悟了,就不当冤大头了,乱花钱的现象少了。IOE的产品价格却一直不菲,两头一夹,国内应用集成和解决方案厂商日子就不太好过了。其实即便阿里不喊去IOE,国内的软件厂商们也早就开始尝试采用开源技术了,脚踩两只船,大项目仍然和IOE合作,采用其商用产品,利薄的小项目就基于划算的开源软件开发了。

去IOE的本质是抛弃专有商用软件,奔向开源技术和服务。采用开源策略还是闭源策略是每一个平台级厂商都需要仔细权衡的。采用开源策略更容易构建大生态系统,凝聚社区资源,降低研发成本;采用闭源策略的公司往往占据技术优势,凭借技术和产品壁垒收取高昂的费用。有的公司对不同产品分别采用开源和闭源策略,IOE三巨头不仅拥有专有商用软件,在开源技术和产品上也有布局,Java和MySQL受Oracle控制,OpenStack以前隶属于EMC。是用商用软件还是用基于开源产品的服务选择权完全在客户手中,阿里一边在大喊去IOE,一边仍在使用Java、MySQL让IOE三巨头一肚子苦水而又无可奈何。和在IBM的同事聊天,他们已被告知不要对外评论此事。

去IOE谁高兴?谁难过?
开放和自由是时代的主旋律,开源模式和服务模式在势头上必然压过闭源模式。IT产业链中对去IOE的态度如何?问过几个大客户的CTO,都是不差钱的主,人家关心的是服务质量,只要能把活儿干漂亮了,多点少点不是事儿,关键是不能耽误业务赚钱。IOE的软硬件虽然贵,但性能可靠、服务体系完善、更新补丁及时,兼有利弊,去IOE去到什么程度需要慎重,通常客户都是不具备驾驭开源软件的能力的,不找帮手的话搞不定。

二线中间件厂商挺高兴,机会来了,开源中间件厂商更高兴,以前花了血本推广但效果不佳,去IOE这一整,搭车占便宜何乐不为?红帽全年收入达到13.3亿美金,同比增长17%,其中应该也有去IOE运动的贡献。按需使用的订阅模式比传统的许可证模式更灵活,在XaaS模式下更有生命力。

IBM、Oracle和EMC实际上是早有准备的,早有布局,收购或支持了多个开源项目或技术,形成了专有商用软件和开源产品并举的局面。只是以前的许可证+服务费的模式带来了丰厚收益,要自己抛弃自己的亲儿子,心里怎么也过不去这个坎儿。

阿里到底想干啥?

阿里在下一盘什么样的棋?按说阿里的营收和利润不菲,货真价实的土豪,只要业务系统正常运转,还会在乎区区几千万的服务费?阿里云已经耕耘了若干年,14年将是它大局拓展的年份。但云计算服务是要靠扩大规模平摊成本来维持运营的,要赚大钱不太容易,与阿里的其他业务比起来利太薄。但为什么阿里还有投?云平台不是目的是手段,是获取数据的基础,是未来阿里进军其他行业的构建新型业务形态的通道。阿里去IOE的实质是想引导市场转向阿里的公有云服务,这一点在阿里的“去IOE工程师”招聘广告中可以揣摩得到,当然,Amazon和Azure也可以搭车收益。

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

       欢迎关注老任微信公号: IT-Veteran

         

      

作者:jeffren 发表于2014-6-4 23:05:17 原文链接
阅读:92 评论:0 查看评论

从LinkedIn的数据处理机制学习数据架构

$
0
0

LinkedIn.com是当今最流行的专业社交网站之一,本文描述了LinkedIn.com是如何管理数据的。如你对文中的观点有异议亦或文中有遗漏的部分请随时告诉我。

LinkedIn.com数据用例

下面是一些数据用例,可能我们在浏览LinkedIn网页时都已经看到过了。

  • 更新后的个人资料后几乎可以实时的出现在招聘搜索页面
  • 更新后的个人资料后几乎可以实时的出现在人脉网页
  • 分享一个更新,可以近实时的出现在新闻feed页面
  • 然后会更新到其他只读页面,像”你可能认识的人“、”看过我资料的人“、”相关搜索“等。

令人震惊的是,如果我们使用较好的宽带,这些页面可以在数毫秒内完成加载!让我们向 LinkedIn工程师团队致敬!

早期的LinkedIn数据架构

像其它初创公司一样,LinkedIn 早期也是通过单个的RDBMS (关系型数据库管理系统)的几张表来保存用户资料和人脉关系。是不是很原始?后来这个RDMBS扩展出两个额外的数据库系统,其中一个用来支撑用户个人资料的全文搜索,另一个用来实现社交图。这两个数据库通过Databus来取得最新数据。Databus是一个变化捕捉系统,它的主要目标就是捕捉那些来至可信源(像Oracle)中数据集的变更,并且把这些变化更新到附加数据库系统中。

但是,没过多久这种架构就已经很难满足网站的数据需求了。因为按照Brewerd的CAP理论想要同时满足下面的条件看似不太可能:

一致性:所有应用在同一时刻看到相同的数据

可用性:保证每个请求都能收到应答,无论成功或失败

分区容错性:部分系统的消息丢失或失败不影响系统系统整体的正常运行

根据上面的法则,LinkedIn工程师团队实现了他们称作为时间线一致性(或者说近线系统的最终一致性,下面会解释)以及另外两个特性:可用性和分区容错性。下面介绍目前LinkedIn的数据架构。

LinkedIn如今的数据架构

如果要支撑在不到一秒钟内处理数百万用户的相关事务,上面的数据架构已经明显不足了。因此,LinkedIn 工程师团队提出了三段式(three-phase)数据架构,由在线、离线以及近线数据系统组成。总体上讲,LinkedIn数据被存储在如下几种不同形式的数据系统中(看下面的图):

  • RDBMS
    • Oracle
    • MySQL(作为Espresso的底层数据存储)
  • RDBMS
    • Espresso(LinkedIn自己开发的文档型NoSQL数据存储系统)
    • Voldemart (分布式Key-value存储系统)
    • HDFS (存放Hadoop map-reduce任务的数据)
  • Caching
    • Memcached
  • 基于Lucene的索引
    • 存放查询、关系图等功能数据的Lucene 索引
    • Espresso使用的索引

db-systems-New-Page-1-1024x838

图:LinkedIn数据库系统包括了DataBus、NoSQL、RDBMS以及Indexes

上面提到的数据存储库被归为三种不同类型的系统,下面会逐一解释:

在线数据库系统

在线系统处理用户的实时互动;主数据库像Oracle就属于这一类别。主数据存储用来支撑用户的写操作和少量的读操作。以Orcale为例,Oracle master会执行所有的写操作。最近,LinkedIn正在开发另一个叫做“Espresso”的数据系统来满足日益复杂的数据需求,而这些数据看似不应从像Oracle这类的RDBMS中获取。他们能否淘汰所有或大部分的Oracle并将数据完全转移到像Espresso这类的NoSQL数据存储系统中去?让我们拭目以待。

Espresso是一个支持水平扩展、索引、时间线一致性、基于文档且高可用的NoSQL数据仓库, 旨在代替支撑公司网页操作所使用的传统Oracle数据库。设计它的初衷是为了提高LinkedIn的InMail消息服务的可用性。目前有如下一些应用在使用Espresso作为可信源系统。能够看到NoSQL数据存储是如果被用来处理如此众多应用的数据需求很是神奇!
  • 成员间消息,
  • 社交动作,如:更新
  • 文章分享
  • 用户个人资料
  • 公司资料
  • 新闻文章

离线数据库系统

离线系统主要包括Hadoop和一个Teradata数据仓库,用来执行批处理和分析类的工作。之所以被称为离线是因为它对数据执行的的批处理操作。  Apache Azkaban被用来管理Hadoop和ETL任务,这些任务从主可信源系统获取数据后交由map-reduce处理,处理结果被保存在 HDFS,然后通知’消费者‘(例如: Voldemart)通过合适的方式来获取这些数据并切换索引来保证能获取到最新的数据。

近线数据库系统(时间线一致性)

近线系统的目标是为了实现时间线一致性(或最终一致性),它处理类似’你可能认识的人(只读数据集)‘、搜索以及社交图这些功能,这些功能的数据会持续更新,但它们对延迟性的要求并不像在线系统那样高。下面是几种不同类型的近线系统:

  • Voldemart,一个Key-Value存储系统,为系统中的只读页面提供服务。Voldemart的数据来源于Hadoop框架(Hadoop Azkaban:编排Hadoop map-reduce任务的执行计划)。这就是近线系统,它们从类似Hadoop的离线系统获取数据。下面这些页面的数据都是来自于Voldemart:
      • 你可能认识的人
      • 看过本页面的人还在看
      • 相关搜索
      • 你可能感兴趣的工作
      • 你可能感兴趣的事件
  • 下面是几种不同的索引,这些索引由 Databus-一个变化数据捕捉系统-来更新的:
      • 供SeaS(Search-as-a-Service)使用的’成员搜索索引‘。当你在LinkedIn上搜索不同的成员时,这些数据就是来自于搜索索引。通常这个功能对招聘人员的帮助很大。
      • 社交图索引帮助在人们的人脉关系中显示成员以及关系。通过这个索引用户几乎可以实时的得到网络关系的变化。
      • 通过读复制集获取到的成员资料数据。这些数据会被’标准化服务‘访问。读复制集是对源数据库的复制,这样能使源数据库的更新同步到这些复制集上面。增加读复制集的最主要原因是能够通过将读操查询分散到读复制集上来减轻源数据库(执行用户发起的写操作)的压力。

下图展示了数据变化捕获事件是如何利用Databus更新到近线系统的:

databus-usecases

用数据用例来展示它们是如何工作的

假如你更新了你个人资料中的最新技能和职位。你还接受了一个连接请求。那么在系统内部到底发生了什么:

  • 将更新写入Oracle Master数据库
  • 然后Databus做了如下一系列奇妙的工作来实现时间线一致性:
      • 将资料变更,如最新技能和职位信息,更新到标准化服务。
      • 将上面提到的变更更新到搜索索引服务。
      • 将关系变更更新到图索引服务。

数据架构经验

如果要设计一个像LinkedIn.com一样的支持数据一致性、高扩展性且高可用性的数据架构,可以借鉴下面的经验:

  • 数据库读写分离:你应当计划两种数据库,一种用来执行写操作的可以称为“ 可信源”系统,另一种执行读操作的可以称为派生数据库系统。这里的经验法则就是将由用户发起的写操作和用户读操作使用的数据库区分开来。
  • 派生数据库系统:用户的读操作应该被分配到派生数据库或者读复制集上去。而派生数据库系统则可以建立在下面的系统之上:
      • Lucene 索引
      • NoSQL数据存储,例如:Voldemart、Redis、Cassandra、MongoDB等。
  • 对于用户的读操作,应该尽量从主可信源数据库系统创建索引或者基于key-value的数据(来源于Hadoop map-reduce之类的系统),并且将每次由用户发起的被写入主可信源系统的变更一并更新到这些索引或派生数据(key-value)。
  • 为确保派生数据库系统的数据是最新的,你可以选择应用复写(application-dual writes),即在应用层同时写入主数据库和派生数据库系统,或日志挖掘(读取通过批处理任务得到的主数据存储系统的事务提交日志)。
  • 创建派生数据时,你可以针对主数据集或者变更数据集执行 基于Hadoop的map-reduce任务,然后更新 HDFS并且通知派生数据存储系统(类似Voldemart的NoSQL存储)来取走数据。
  • 对于数据一致性来说,你可以以将这 些数据存储库创建为分布式系统,集群中的每个节点又都包含主从节点。所有节点都可以创建水平扩展的数据Shards。
  • 为了保证这些分布式数据存储系统正常运行时间最大化,你可以使用像Apache Helix这一类的集群管理工具。

参考文献

从LinkedIn的数据处理机制学习数据架构,首发于 博客 - 伯乐在线

图像检索中为什么仍用BOW和LSH

$
0
0

  去年年底的时候在一篇 博客中,用ANN的框架解释了BOW模型[1],并与LSH[2]等哈希方法做了比较,当时得出了结论,BOW就是一种经过学习的Hash函数。去年再早些时候,又简单介绍过LLC[3]等稀疏的表示模型,当时的相关论文几乎一致地得出结论,这些稀疏表示的方法在图像识别方面的性能一致地好于BOW的效果。后来我就逐渐产生两个疑问:

1)BOW在检索时好于LSH,那么为什么不在任何时候都用BOW代替LSH呢?
2)既然ScSPM,LLC等新提出的方法一致地好于BOW,那能否直接用这些稀疏模型代替BOW来表示图像的特征?


      粗略想了一下,心中逐渐对这两个问题有了答案。这篇博文我就试图在检索问题上,谈一谈Bag-of-words模型与LSH存在的必要性。

  • 一、回顾LSH      

 LSH方法本身已经在很多文章中有过介绍,大家可以参考 这里这里。其主要思想就是在特征空间中对所有点进行多次随机投影(相当于对特征空间的随机划分),越相近的点,随机投影后的值就越有可能相同。通常投影后的值是个binary code(0或者1),那么点xi经过N次随机投影后就可以得到一个N维的二值向量qi,qi就是xi经过LSH编码后的值。
问题是LSH是一种随机投影(见图1),上篇博客中也提到这样随机其实没有充分利用到样本的实际分布信息,因此N需要取一个十分大的数才能取得好的效果。因此,[2]中作者理所当然地就想到对LSH的投影函数进行学习(用BoostSSC和RBM来做学习),效果可以见图3。经过学习的LSH就可以通过更少的投影函数取得更好的区分性。这就和BOW的作用有点像了(都是通过学习对原始的特征空间进行划分),只不过BOW对特征空间的划分是非线性的(见图2),而LSH则是线性的。


图1


图2


图3


  • 二、LSH VS BOW:检索的时候对什么特征做编码?
( 以下对LSH的介绍将不区分是否利用BoostSSC和RBM来做学习)。LSH一般是对图像的全局特征做LSH。比如图像的GIST,HOG,HSV等全局的特征。可以说,LSH是将一个特征编码成另外一个特征。这有一点降维的味道。经过N次随机投影后,特征被降维为一个长度为N的二值特征了。
BOW一般是对图像的局部特征做编码,比如SIFT,MSER等。BOW是将一组特征(局部特征)编码成一个特征(全局特征),带有一种aggregation的性质。这是它与LSH最大的不同之处。

  • 三、LSH VS BOW:检索和排序的过程有何不同?
先来说说LSH。假设两个样本x1和y1经过LSH编码后得到q1和q2,那么两个样本之间的相似度可以这么计算:

    (1)

这就是LSH编码后两个样本之间的 汉明距离。假设我们有一个dataset,把dataset里面的图片记做di。有一个查询图片query,记做q。假设已经对dataset和query的所有图片经过LSH编码了,会有两种方式进行图片检索:
a) 建立一张哈希表,di编码后的code做为哈希值(键值)。每个di都有唯一的一个键值。query编码后,在这张哈希表上进行查找,凡是与query不超过D个bits不同的codes,就认为是与query近邻的,也就把这些键值下的图片检索出来。这种做法十分快速(几乎不用做任何计算),缺点在于Hash table将会非常大,大小是。
b) 如果N大于30(这时(a)中的hash table太大了),通常采用exhaustive search,即按照(1)式计算q到di的hamming距离,并做排序。因为是binary code,所以速度会非常快(12M图片不用1秒钟就能得到结果)。

再来说说BOW如何做检索(这个大家都很熟悉了)。假设已经通过BOW得到了图像的全局特征向量,通常通过计算两个向量的直方图距离确定两个向量的相似度,然后进行排序。因为BOW特征是比较稀疏的,所以可以利用 倒排索引提高检索速度。

  • 四、BOW能否代替LSH

BOW是从一组特征到一个特征之间的映射。你可能会说,当“一组特征”就是一个特征(也就是全局特征)的时候,BOW不也能用来对全局特征做编码么?这样做是不好的,因为这时BOW并不和LSH等效。为什么呢?一幅图像只能提取出一个GIST向量,经过BOW编码后,整个向量将会只有在1个bin上的取值为1,而在其他bin上的取值为0。于是乎,两幅图像之间的相似度要么为0,要么为1。想像在一个真实的图像检索系统中,dataset中的相似度要么是0要么是1,相似的图片相似度都是1,被两级化了,几乎无法衡量相似的程度了。所以说BOW还是比较适合和局部特征搭配起来用。其实LSH的索引a)方法也很类似,Hash值(codes)一样的图像之间是比较不出相似程度的。确实也如此,但是LSH和BOW相比仍然有处不同,便是经过LSH编码后不会像BOW那样极端(整个向量只有1个值为1,其它值为0)。所以通过1)式计算出的相似度依然能够反映两特征原始的相似度。所以在比较全局特征的时候,还是LSH比较好用些。

  • 五、LSH能否代替BOW

BOW在处理局部特征的时候,相当于两幅图像之间做点点匹配。如果把LSH编码的所有可能级联成一维向量的话,我觉得在一定程度上是起到了BOW相似的作用的。

  • 六、LLC能否代替BOW

不完全可以吧。尽管在识别问题上,LLC性能是比BOW好,但是由于HKM[4]和AKM[5]的提出,BOW的码书可以训练到非常大(可以达到1000000维)。而LLC之类的学习方法就没那么幸运了,说到天上去也就几万维吧。尽管相同维数下BOW性能不那么好,但是放到100万维上,优势就体现出来了。所以在检索问题上,BOW依然如此流行。


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

参考文献:

【1】Video Google: A Text Retrieval Approach to Object Matching in Videos

【2】Small Codes and Large Image Databases for Recognition

【3】Locality-constrained Linear Coding for Image Classification

【4】Scalable Recognition with a Vocabulary Tree

【5】Object retrieval with large vocabularies and fast spatial matching

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

jiang1st2010

转载请注明出处:http://blog.csdn.net/jwh_bupt/article/details/27713453



作者:jiang1st2010 发表于2014-6-5 17:25:24 原文链接
阅读:14 评论:0 查看评论

深入研究memcache 特性和限制

$
0
0
转自 http://hi.baidu.com/jqxw4444/item/59c33ea3656ede3e020a4d1c

特性和限制
在 Memcached中可以保存的item数据量是没有限制的,只要内存足够 。

Memcached单进程最大使用内存为2G,要使用更多内存,可以分多个端口开启多个Memcached进程,最大30天的数据过期时间,设置为永久的也会在这个时间过期

常量REALTIME_MAXDELTA 60*60*24*30控制最大键长为250字节,大于该长度无法存储

常量KEY_MAX_LENGTH 250控制单个item最大数据是1MB,超过1MB数据不予存储

常量POWER_BLOCK 1048576进行控制,它是默认的slab大小 最大同时连接数是200,通过 conn_init()中的freetotal进行控制,最大软连接数是1024,通过settings.maxconns=1024 进行控制跟空间占用相关的参数:settings.factor=1.25, settings.chunk_size=48, 影响slab的数据占用和步进方式memcached是一种无阻塞的socket通信方式服务,基于libevent库,由于无阻塞通信,对内存读写速度非常之快。

memcached分服务器端和客户端,可以配置多个服务器端和客户端,应用于分布式的服务非常广泛。
memcached作为小规模的数据分布式平台是十分有效果的。

memcached是键值一一对应,key默认最大不能超过128个字 节,value默认大小是1M,也就是一个slabs,如果要存2M的值(连续的),不能用两个slabs,因为两个slabs不是连续的,无法在内存中 存储,故需要修改slabs的大小,多个key和value进行存储时,即使这个slabs没有利用完,那么也不会存放别的数据。


# /usr/local/bin/memcached -d -m 10 -u root -l 192.168.0.200 -p 12000 -c 256 -P /tmp/memcached.pid

-d选项是启动一个守护进程,
-m是分配给Memcache使用的内存数量,单位是MB,我这里是10MB,
-u是运行Memcache的用户,我这里是root,
-l是监听的服务器IP地址,如果有多个地址的话,我这里指定了服务器的IP地址192.168.0.200,
-p是设置Memcache监听的端口,我这里设置了12000,最好是1024以上的端口,
-c选项是最大运行的并发连接数,默认是1024,我这里设置了256,按照你服务器的负载量来设定,
-P是设置保存Memcache的pid文件,我这里是保存在 /tmp/memcached.pid,

2)如果要结束Memcache进程,执行:

# kill `cat /tmp/memcached.pid`



Memcache使用了Slab Allocator的内存分配机制:按照预先规定的大小,将分配的内存分割成特定长度的块,以完全解决内存碎片问题。Memcache的存储涉及到slab,page,chunk三个概念
1.Chunk为固定大小的内存空间,默认为96Byte。
2.page对应实际的物理空间,1个page为1M。
3.同样大小的chunk又称为slab。
Memcached再启动的时候根据-n和-f参数,产生若干slab。具体应用中Memcache每次申请1page,并将这1M空间分割成若干个chunk,这些chunk有着同样的大小,属于同一个slab。

【添加】

通过memcache添加item的时候:
1. Memcache计算item的大小(key+value+flags),选取合适的slab(刚好能放下该item的slab)
2. 如果这个item对应的slab未出现过,则申请1个page(注意,这1M空间不论是否达到memcached使用内存都可以申请成功)并加该item存入slab中的chunk
3. 如果item对应的slab出现过,则在该slab中优先选择expired(free_chunks)和delete(在1.2.2中delete的chunk存在着不能被重复利用的问题)的chunk进行存储,其次将选择未使用过的chunk(free_chunks_end)进行存储。
4. 如果item对应的slab出现过,但是对应的slab已经存储满了,那么会申请一个新的page,这个page被分为对应大小的chunk,继续存储。
5. 如果item对应的slab出现过,但是对应的slab已经存储满了并且memcache也达到了最大内存使用。将使用lru算法,清除item(可能将未过期的item清除)此时会有eviction++

【删除】:
1. Delete操作只是将该chunk置为删除状态,这样在下次使用将优先利用这样的chunk。
【flush】
1. Flush操作相当于将所有的item失效的一个动作。并不会改变memcache内存分配情况。
【一些注意】
1. memcache已经分配的内存不会再主动清理。
2. memcache分配给某个slab的内存页不能再分配给其他slab。
3. flush_all不能重置memcache分配内存页的格局,只是给所有的item置为过期。
4. memcache最大存储的item(key+value)大小限制为1M,这由page大小1M限制
5.由于memcache的分布式是客户端程序通过hash算法得到的key取模来实现,不同的语言可能会采用不同的hash算法,同样的客户端程序也有可能使用相异的方法,因此在多语言、多模块共用同一组memcached服务时,一定要注意在客户端选择相同的hash算法
6.启动memcached时可以通过-M参数禁止LRU替换,在内存用尽时add和set会返回失败
7.memcached启动时指定的是数据存储量,没有包括本身占用的内存、以及为了保存数据而设置的管理空间。因此它占用的内存量会多于启动时指定的内存分配量,这点需要注意。
8.memcache存储的时候对key的长度有限制,php和C的最大长度都是250

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


ITeye推荐



swift学习资料汇总

$
0
0
今年的苹果开发者大会(WWDC)上,公布了ios8的几个新特性,其中包括引入了群聊功能,支持第三方输入法等功能。但更让开发者感兴趣的莫过于Swift语言的发布了。

Swift是apple自创的一门转为cocoa和cocoa touch设计的语言,意用来替换objc。Swift能使代码大量简化,并且开发者们可以在同一款软件中同事使用obj-c,Swift,c语言。方便开发者来学习这么语言。这里我总结了一些Swift的一些网络上的资源,希望能和大家一起学习,如有错误欢迎大家更正,后续有好的资料也会陆续更新。


1)apple Swift编程入门文档-  http://gashero.iteye.com/blog/2075324

一位大神写的关于Swift的一些介绍和简单的使用,里面介绍了Swift和其他语言的区分和一些新的特性及使用方法。


2)apple Swift编程视频-http://www.jikexueyuan.com/course/89.html/?hmsr=baiduwenku&hmmd=wwdc

极客学院出的Swift的视频,出的很快发布会刚结束课程都出来了,是免费的,而且看介绍后期还会有语法和实战课程,个人感觉不错,对Swift讲的很细,不错的教程,有兴趣的可以看看。


3) Apple Swift官方店主-https://itunes.apple.com/us/book/the-swift-programming-language/id881256329?mt=11

apple官方的文档。就不多说了,我英语是不太好,慢慢翻译吧,哈哈。


4)Apple Developer Swift 文档介绍:https://developer.apple.com/swift/

打开速度稍稍有点慢,大家可以耐心一点。


5)中文版Apple官方Swift教程(Github协作翻译中):https://github.com/numbbbbb/the-swift-programming-language-in-chinese


6)Apple Swift编程语言入门教程:http://swiftlang.com.cn/start/

比较基础,介绍了基本概念、变量与常量、类型推导、字符串格式化、数组与变量。


7)Cocoachina翻译的Swift官方文档:http://www.cocoachina.com/newbie/basic/2014/0604/8667.html


8)letsswift 编译的Swift中文教程:http://letsswift.com/


9)Github上的Swift开源项目列表:https://github.com/search?l=Swift&amp;p=1&amp;q=swift&amp;ref=cmdform&amp;type=Repositories


10)国内第一个Swift社区:http://swift.sh/
          
这速度!


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


ITeye推荐



[译]Android ActionBar完全解析,使用官方推荐的最佳导航栏(上)

$
0
0

转载请注明出处: http://blog.csdn.net/guolin_blog/article/details/18234477

本篇文章主要内容来自于Android Doc,我翻译之后又做了些加工,英文好的朋友也可以直接去读原文。

http://developer.android.com/guide/topics/ui/actionbar.html

Action Bar是一种新増的导航栏功能,在Android 3.0之后加入到系统的API当中,它标识了用户当前操作界面的位置,并提供了额外的用户动作、界面导航等功能。使用ActionBar的好处是,它可以给提供一种全局统一的UI界面,使得用户在使用任何一款软件时都懂得该如何操作,并且ActionBar还可以自动适应各种不同大小的屏幕。下面是一张使用ActionBar的界面截图:


其中,[1]是ActionBar的图标,[2]是两个action按钮,[3]是overflow按钮。

由于Action Bar是在3.0以后的版本中加入的,如果想在2.x的版本里使用ActionBar的话则需要引入 Support Library,不过3.0之前版本的市场占有率已经非常小了,这里简单起见我们就不再考虑去做向下兼容,而是只考虑4.0以上版本的用法。


添加和移除Action Bar

ActionBar的添加非常简单,只需要在AndroidManifest.xml中指定Application或Activity的theme是Theme.Holo或其子类就可以了,而使用Eclipse创建的项目自动就会将Application的theme指定成Theme.Holo,所以ActionBar默认都是显示出来的。新建一个空项目并运行,效果如下图所示:


而如果想要移除ActionBar的话通常有两种方式,一是将theme指定成Theme.Holo.NoActionBar,表示使用一个不包含ActionBar的主题,二是在Activity中调用以下方法:

ActionBar actionBar = getActionBar();
actionBar.hide();
现在重新运行一下程序,就可以看到ActionBar不再显示了,如下图所示:



修改Action Bar的图标和标题

默认情况下,系统会使用<application>或者<activity>中icon属性指定的图片来作为ActionBar的图标,但是我们也可以改变这一默认行为。如果我们想要使用另外一张图片来作为ActionBar的图标,可以在<application>或者<activity>中通过logo属性来进行指定。比如项目的res/drawable目录下有一张weather.png图片,就可以在AndroidManifest.xml中这样指定:
<activity
    android:name="com.example.actionbartest.MainActivity"
    android:logo="@drawable/weather" ></activity>
现在重新运行一下程序,效果如下图所示:

OK,ActionBar的图标已经修改成功了,那么标题中的内容该怎样修改呢?其实也很简单,使用label属性来指定一个字符串就可以了,如下所示:
<activity
    android:name="com.example.actionbartest.MainActivity"
    android:label="天气"
    android:logo="@drawable/weather" ></activity>
现在重新运行一下程序,结果如下图所示:


添加Action按钮

ActionBar还可以根据应用程序当前的功能来提供与其相关的Action按钮,这些按钮都会以图标或文字的形式直接显示在ActionBar上。当然,如果按钮过多,ActionBar上显示不完,多出的一些按钮可以隐藏在overflow里面(最右边的三个点就是overflow按钮),点击一下overflow按钮就可以看到全部的Action按钮了。
当Activity启动的时候,系统会调用Activity的onCreateOptionsMenu()方法来取出所有的Action按钮,我们只需要在这个方法中去加载一个menu资源,并把所有的Action按钮都定义在资源文件里面就可以了。
那么我们先来看下menu资源文件该如何定义,代码如下所示:
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="com.example.actionbartest.MainActivity" ><item
        android:id="@+id/action_compose"
        android:icon="@drawable/ic_action_compose"
        android:showAsAction="always"
        android:title="@string/action_compose"/><item
        android:id="@+id/action_delete"
        android:icon="@drawable/ic_action_delete"
        android:showAsAction="always"
        android:title="@string/action_delete"/><item
        android:id="@+id/action_settings"
        android:icon="@drawable/ic_launcher"
        android:showAsAction="never"
        android:title="@string/action_settings"/></menu>
可以看到,这里我们通过三个<item>标签定义了三个Action按钮。<item>标签中又有一些属性,其中id是该Action按钮的唯一标识符,icon用于指定该按钮的图标,title用于指定该按钮可能显示的文字(在图标能显示的情况下,通常不会显示文字),showAsAction则指定了该按钮显示的位置,主要有以下几种值可选:always表示永远显示在ActionBar中,如果屏幕空间不够则无法显示,ifRoom表示屏幕空间够的情况下显示在ActionBar中,不够的话就显示在overflow中,never则表示永远显示在overflow中。
接着,重写Activity的onCreateOptionsMenu()方法,代码如下所示:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
	MenuInflater inflater = getMenuInflater();
	inflater.inflate(R.menu.main, menu);
	return super.onCreateOptionsMenu(menu);
}
这部分代码很简单,仅仅是调用了MenuInflater的inflate()方法来加载menu资源就可以了。现在重新运行一下程序,结果如下图所示:

可以看到,action_compose和action_delete这两个按钮已经在ActionBar中显示出来了,而action_settings这个按钮由于showAsAction属性设置成了never,所以被隐藏到了overflow当中,只要点击一下overflow按钮就可以看到它了。
这里我们注意到,显示在ActionBar上的按钮都只有一个图标而已,我们在title中指定的文字并没有显示出来。没错,title中的内容通常情况下只会在overflow中显示出来,ActionBar中由于屏幕空间有限,默认是不会显示title内容的。但是出于以下几种因素考虑,即使title中的内容无法显示出来,我们也应该给每个item中都指定一个title属性:
  • 当ActionBar中的剩余空间不足的时候,如果Action按钮指定的showAsAction属性是ifRoom的话,该Action按钮就会出现在overflow当中,此时就只有title能够显示了。
  • 如果Action按钮在ActionBar中显示,用户可能通过长按该Action按钮的方式来查看到title的内容。

响应Action按钮的点击事件

当用户点击Action按钮的时候,系统会调用Activity的onOptionsItemSelected()方法,通过方法传入的MenuItem参数,我们可以调用它的getItemId()方法和menu资源中的id进行比较,从而辨别出用户点击的是哪一个Action按钮,比如:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
	switch (item.getItemId()) {
	case R.id.action_compose:
		Toast.makeText(this, "Compose", Toast.LENGTH_SHORT).show();
		return true;
	case R.id.action_delete:
		Toast.makeText(this, "Delete", Toast.LENGTH_SHORT).show();
		return true;
	case R.id.action_settings:
		Toast.makeText(this, "Settings", Toast.LENGTH_SHORT).show();
		return true;
	default:
		return super.onOptionsItemSelected(item);
	}
}
可以看到,我们让每个Action按钮被点击的时候都弹出一个Toast,现在重新运行一下代码,结果如下图所示:



通过Action Bar图标进行导航

启用ActionBar图标导航的功能,可以允许用户根据当前应用的位置来在不同界面之间切换。比如,A界面展示了一个列表,点击某一项之后进入了B界面,这时B界面就应该启用ActionBar图标导航功能,这样就可以回到A界面。
我们可以通过调用setDisplayHomeAsUpEnabled()方法来启用ActionBar图标导航功能,比如:
@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setTitle("天气");
	setContentView(R.layout.activity_main);
	ActionBar actionBar = getActionBar();
	actionBar.setDisplayHomeAsUpEnabled(true);
}
现在重新运行一下程序,结果如下图所示:

可以看到,在ActionBar图标的左侧出现了一个向左的箭头,通常情况下这都表示返回的意思,因此最简单的实现就是在它的点击事件里面加入finish()方法就可以了,如下所示:
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case android.R.id.home:
			finish();
			return true;……
		}
	}
当点击ActionBar图标的时候,系统同样会调用onOptionsItemSelected()方法,并且此时的itemId是android.R.id.home,所以finish()方法也就是加在这里的了。
现在看上去,ActionBar导航和Back键的功能貌似是一样的。没错,如果我们只是简单地finish了一下,ActionBar导航和Back键的功能是完全一样的,但ActionBar导航的设计初衷并不是这样的,它和Back键的功能还是有一些区别的,举个例子吧。

上图中的Conversation List是收件箱的主界面,现在我们点击第一封邮件会进入到Conversation1 details界面,然后点击下一封邮件会进入到Conversation 2 details界面,再点击下一封邮箱会进入到Conversation3 details界面。好的,这个时候如果我们按下Back键,应该会回到Conversation 2 details界面,再按一次Back键应该回到Conversation1 details界面,再按一次Back键才会回到Conversation List。而ActionBar导航则不应该表现出这种行为,无论我们当前在哪一个Conversation details界面,点击一下导航按钮都应该回到Conversation List界面才对。
这就是ActionBar导航和Back键在设计上的区别,那么该怎样才能实现这样的功能呢?其实并不复杂,实现标准的ActionBar导航功能只需三步走。
第一步我们已经实现了,就是调用setDisplayHomeAsUpEnabled()方法,并传入true。
第二步需要在AndroidManifest.xml中配置父Activity,如下所示:
<activity
    android:name="com.example.actionbartest.MainActivity"
    android:logo="@drawable/weather" ><meta-data
        android:name="android.support.PARENT_ACTIVITY"
        android:value="com.example.actionbartest.LaunchActivity" /></activity>
可以看到,这里通过meta-data标签指定了MainActivity的父Activity是LaunchActivity,在Android 4.1版本之后,也可以直接使用android:parentActivityName这个属性来进行指定,如下所示:
<activity
    android:name="com.example.actionbartest.MainActivity"
    android:logo="@drawable/weather"
    android:parentActivityName="com.example.actionbartest.LaunchActivity" ></activity>
第三步则需要对android.R.id.home这个事件进行一些特殊处理,如下所示:
@Override
public boolean onOptionsItemSelected(MenuItem item) {
	switch (item.getItemId()) {
	case android.R.id.home:
		Intent upIntent = NavUtils.getParentActivityIntent(this);
		if (NavUtils.shouldUpRecreateTask(this, upIntent)) {
			TaskStackBuilder.create(this)
					.addNextIntentWithParentStack(upIntent)
					.startActivities();
		} else {
			upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
			NavUtils.navigateUpTo(this, upIntent);
		}
		return true;
        ......
	}
}
其中,调用NavUtils.getParentActivityIntent()方法可以获取到跳转至父Activity的Intent,然后如果父Activity和当前Activity是在同一个Task中的,则直接调用navigateUpTo()方法进行跳转,如果不是在同一个Task中的,则需要借助TaskStackBuilder来创建一个新的Task。
这样,就按照标准的规范成功实现ActionBar导航的功能了。

添加Action View

ActionView是一种可以在ActionBar中替换Action按钮的控件,它可以允许用户在不切换界面的情况下通过ActionBar完成一些较为丰富的操作。比如说,你需要完成一个搜索功能,就可以将SeachView这个控件添加到ActionBar中。
为了声明一个ActionView,我们可以在menu资源中通过actionViewClass属性来指定一个控件,例如可以使用如下方式添加SearchView:
<menu xmlns:android="http://schemas.android.com/apk/res/android" ><item
        android:id="@+id/action_search"
        android:icon="@drawable/ic_action_search"
        android:actionViewClass="android.widget.SearchView"
        android:showAsAction="ifRoom|collapseActionView"
        android:title="@string/action_search" />
    ......</menu>
注意在showAsAction属性中我们还声明了一个collapseActionView,这个值表示该控件可以被合并成一个Action按钮。
现在重新运行一下程序,效果如下图所示:

OK,果然有一个搜索样式的Action按钮出现了,现在点击一下这个搜索按钮,效果如下图所示:

可以看到,这时SearchView就会展开占满整个ActionBar,而其它的Action按钮由于将showAsAction属性设置成了ifRoom,此时都会隐藏到overflow当中。
如果你还希望在代码中对SearchView的属性进行配置(比如添加监听事件等),完全没有问题,只需要在onCreateOptionsMenu()方法中获取该ActionView的实例就可以了,代码如下所示:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
	MenuInflater inflater = getMenuInflater();
	inflater.inflate(R.menu.main, menu);
	MenuItem searchItem = menu.findItem(R.id.action_search);
	SearchView searchView = (SearchView) searchItem.getActionView();
	// 配置SearchView的属性
	......
	return super.onCreateOptionsMenu(menu);
}
在得到了SearchView的实例之后,就可以任意地配置它的各种属性了。关于SearchView的更多详细用法,可以参考官方文档  http://developer.android.com/guide/topics/search/search-dialog.html 。
除此之外,有些程序可能还希望在ActionView展开和合并的时候显示不同的界面,其实我们只需要去注册一个ActionView的监听器就能实现这样的功能了,代码如下所示:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
	MenuInflater inflater = getMenuInflater();
	inflater.inflate(R.menu.main, menu);
	MenuItem searchItem = menu.findItem(R.id.action_search);
	searchItem.setOnActionExpandListener(new OnActionExpandListener() {
		@Override
		public boolean onMenuItemActionExpand(MenuItem item) {
			Log.d("TAG", "on expand");
			return true;
		}
		@Override
		public boolean onMenuItemActionCollapse(MenuItem item) {
			Log.d("TAG", "on collapse");
			return true;
		}
	});
	return super.onCreateOptionsMenu(menu);
}
可以看到,调用MenuItem的setOnActionExpandListener()方法就可以注册一个监听器了,当SearchView展开的时候就会回调onMenuItemActionExpand()方法,当SearchView合并的时候就会调用onMenuItemActionCollapse()方法,我们在这两个方法中进行相应的UI操作就可以了。

Overflow按钮不显示的情况

虽然现在我们已经掌握了不少ActionBar的用法,但是当你真正去使用它的时候还是可能会遇到各种各样的问题,比如很多人都会碰到overflow按钮不显示的情况。明明是同样的一份代码,overflow按钮在有些手机上会显示,而在有些手机上偏偏就不显示,这是为什么呢?后来我总结了一下,overflow按钮的显示情况和手机的硬件情况是有关系的,如果手机没有物理Menu键的话,overflow按钮就可以显示,如果有物理Menu键的话,overflow按钮就不会显示出来。比如我们启动一个有Menu键的模拟器,然后将代码运行到该模拟器上,结果如下图所示:

可以看到,ActionBar最右边的overflow按钮不见了!那么此时我们如何查看隐藏在overflow中的Action按钮呢?其实非常简单,按一下Menu键,隐藏的内容就会从底部出来了,如下图所示:

看到这里相信不少朋友都想吐槽一下了,这显然是一种非常蛋疼的设计,在不同手机上竟然显示了不同的界面,而且操作方法也完全不一样,这样会给用户一种非常不习惯的感觉。话说Google为什么要把ActionBar的overflow设计成这样我也不太理解,但是我们还是有办法改变这一默认行为的。
实际上,在ViewConfiguration这个类中有一个叫做sHasPermanentMenuKey的静态变量,系统就是根据这个变量的值来判断手机有没有物理Menu键的。当然这是一个内部变量,我们无法直接访问它,但是可以通过反射的方式修改它的值,让它永远为false就可以了,代码如下所示:
@Override
protected void onCreate(Bundle savedInstanceState) {
	......
	setOverflowShowingAlways();
}

private void setOverflowShowingAlways() {
	try {
		ViewConfiguration config = ViewConfiguration.get(this);
		Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
		menuKeyField.setAccessible(true);
		menuKeyField.setBoolean(config, false);
	} catch (Exception e) {
		e.printStackTrace();
	}
}
这里我们在onCreate()方法的最后调用了setOverflowShowingAlways()方法,而这个方法的内部就是使用反射的方式将sHasPermanentMenuKey的值设置成false,现在重新运行一下代码,结果如下图所示:

可以看到,即使是在有Menu键的手机上,也能让overflow按钮显示出来了,这样就可以大大增加我们软件界面和操作的统一性。

让Overflow中的选项显示图标

如果你点击一下overflow按钮去查看隐藏的Action按钮,你会发现这部分Action按钮都是只显示文字不显示图标的,如下图所示:

这是官方的默认效果,Google认为隐藏在overflow中的Action按钮都应该只显示文字。当然,如果你认为这样不够美观,希望在overflow中的Action按钮也可以显示图标,我们仍然可以想办法来改变这一默认行为。
其实,overflow中的Action按钮应不应该显示图标,是由MenuBuilder这个类的setOptionalIconsVisible变量来决定的,如果我们在overflow被展开的时候将这个变量赋值为true,那么里面的每一个Action按钮对应的图标就都会显示出来了。赋值的方法当然仍然是用反射了,代码如下所示:
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
	if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {
		if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
			try {
				Method m = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
				m.setAccessible(true);
				m.invoke(menu, true);
			} catch (Exception e) {
			}
		}
	}
	return super.onMenuOpened(featureId, menu);
}
可以看到,这里我们重写了一个onMenuOpened()方法,当overflow被展开的时候就会回调这个方法,接着在这个方法的内部通过返回反射的方法将MenuBuilder的setOptionalIconsVisible变量设置为true就可以了。
现在重新运行一下代码,结果如下图所示:

好了,目前为止我们已经把ActionBar的基础知识介绍完了,那么今天的讲解就到这里,下篇文章中我会带领大家一起更深入地了解ActionBar。
作者:sinyu890807 发表于2014-6-5 8:56:56 原文链接
阅读:734 评论:20 查看评论

今日头条的五个亿

$
0
0

有媒体传出消息,“今日头条”日前完成了C轮融资,融资额度为1亿美元,对应的,公司估值5个亿美元。这个数字很夸张,一来因为一年前B轮融资的时候,估值还不过6000万美元。仅仅一年,就迅速扩大近十倍。二来,一个新闻客户端,居然值那么多钱?

i黑马昨日发表了一篇很长的对今日头条创始人张一鸣的专访,从这篇专访中,大致可以看出点端倪。其中有两点,引起了我的注意。

第一点,今日头条讲的是个什么故事?或者说,它是一个什么定位?

从一般用户认知来说,今日头条就是一个新闻客户端,但在产品方自己,有这样一段话:“张一鸣多年来的积累——好的搜索技术,基础运算工具,在整个社交网络里获得和分发信息的能力——均指向这个现在被定名为今日头条的产品。”

在这段话里,有搜索技术,有运算工具,有分发能力,就是没有“新闻客户端”。这一长串话,按照我多年的写作经验和访谈经历,很像是今日头条自身的描述。更有意味的是,在i黑马这篇极长的文字中,“新闻客户端”这五个字只出现了四次,而且每次都是这样写的:“腾讯和搜狐等新闻客户端”,这篇长文从来没把这五个字用在今日头条上。

这已经很有些稿成后送访谈者过目修订的痕迹了,或者也有可能是事先就点明这一点:不要说今日头条是新闻客户端。这五个字用与不用,和它的融资大有关联。

近一两年来,整个互联网投资市场热情高涨,无论是巨头出手并购,还是专业投资商出手投资。这些热情高涨必然的投资方法是“估值推动型”,而不是“业绩推动型”。估值推动型的投资,一般会找一个对标,其实在过去新浪中国的雅虎,百度中国的谷歌,人人中国的脸谱等等等等大戏中轮番上演。对标非常重要,直接决定了这个项目到底值多少钱。对标找得好,估值很极其惊人,对标找得不好,估值缩水屡见不鲜。

找对标,就是找“讲故事的角度”。一个产品,你从这个角度来看,嗯,是一个新闻客户端,从那个角度看,嗯,是推荐渠道、分发渠道、搜索渠道。I黑马那篇长文,前后出现了三十次“推荐”、九次“分发”、二十四次“搜索”,牢牢地对标在这三大渠道之上。

分发渠道,有对标。文章一开头就提及百度收购91手机助手的事,19亿美元。91手机助手是APP的分发,今日头条就是内容以及内容源的分发。搜索渠道,有对标。移动设备里的搜索入口——这事,想想就兴奋,想想就大得不得了,投资人自己也这样表示:“今日头条更大的价值在于,可以把它看作一个移动端的信息入口”。搜索找个对标更猛的地方在于,Google这个搜索公司在移动端里的广告ARPU值可以达到40美元左右,是Facebook的6倍,twitter的20倍。

推荐渠道,对标不重要了,这是今日头条的核心竞争力。张一鸣表现出对其它门户客户端的自信,完全建立在这上头。你要是三个东西都有对标,核心竞争力不好找了,被对标灭死可能性大了,故事也就不好听了。

整个故事,大致就是这么讲述而成的,值得所有创业者好好学学:对标+核心竞争力,两者兼具,再加上“1.2亿装机,日活1300万”,非常完美的故事。

第二点:今日头条估值推动有了,但总要有业绩的那天。它的业绩在哪里?

请允许我再次引用那篇长文:“今日头条的收入仍来自广告,而且张一鸣认为它是可见的唯一收入来源。”问题在于:移动广告真得如“对于广告商,在今日头条上投放不影响用户体验的信息流广告,效率比在门户网站上高得多。”这样表述吗?

支持者会搬出近日Mary Meeker的2014版互联网趋势报告来说事。在女皇的报告里,她毫不掩饰地表达了对移动广告的信心。但很遗憾,中美移动广告市场,完全不同。

中国移动互联网领域中,互导流量互推用户非常司空见惯,存在大量的“僵尸用户”,这造成了中国移动广告市场整体ARPU值并不高,巨流无线董事长黄维在他的《实战第三屏》一书给出了ARPU五毛的数字——当然,这个数字相对老一点。近期,普华永道发布相当乐观的中国移动广告市场预期,对2014年给出的数字是130亿人民币,相对于中国八亿移动用户,也就是个年15元ARPU的水平。它还乐观地表示,2017年中国市场能达到250亿人民币,考虑到那个时候的用户规模,估计也就是个年20元的水准。对比一下游戏市场,有研究表明,ARPU值今天已经可以做到100元左右。

坐拥1.2亿用户的今日头条,ARPU值显然还没有做到15元的水平,不然一年进项18亿人民币,不可能是“目前已基本可以达到收支平衡”,反而会赚得钵满盆满。即便按照黄维那个相当夸张的五毛党标准,6000万收入的可能性也不好讲(有一种说法是今日头条广告年入过亿)。91手机助手估值那么高,因为今天中国应用的获新成本已经到了20元CPA的规模(中国移动广告ARPU值15元,估计都是被这种APP导流给抬上去的),但给内容/内容源做分发导流,还能有这个ARPU值吗?

当文中提及,像腾讯这样的巨头也举起“推荐”这个大旗,今日头条如何应对时,张一鸣是这样回答的:

“腾讯这样的公司,有巨多的用户、巨多的钱,什么时候都可以杀进来,只是性价比实在不划算。”

有趣。什么叫性价比?

对巨头来说,自己做一个性价比高?还是收一个性价比高?——呵呵,这只是这段话的解释之一罢了。姑妄言之,姑妄听之罢。

—— 钛媒体 供稿 ——

说明:
1、本博客文字,除特别注明外,均为本人原创,可以自由转载,谢绝长微博形式转载;
2、转载时请注明本人大名,魏武挥,不是魏武辉,不要搞错。
3、转载时请保留此段:本文由扯氮集博主魏武挥原创撰写,欢迎于微信/网易云阅读/腾讯新闻客户端中搜索ItTalks以订阅公众账号,或于钛媒体/搜狐新闻客户端科技频道订阅“魏武挥”
4、本人不接受商业文章(俗称软文)撰写的合作,不要再询问我如何合作法。


Copyleft © 2013 知识共享署名-非商业性使用-禁止演绎 注意:转载勿改标题!
ItTalks -- 魏武挥的Blog (digitalfingerprint:fc4f8fc31f70097eea4b780b13146415)

欢迎 关注 我的微博
欢迎 订阅我的微信公众账号:ittalks
欢迎 于搜狐新闻客户端中订阅“魏武挥”

latch:cache buffers chains的优化思路

$
0
0
        数据块在buffer cache存放是以linked list方式存放的。当一个session想要访问/修改buffer cache的block,首先需要通过hash算法检查该block是否存在于buffer cache中,检查相同的SQL语句是否存在于library cache中也是通过hash算法实现的。要判断block是否存在于buffer cache中,就需要扫描linked list(此处都是串行的,不能并发),获取block的信息。而扫描linked list必须获得一个latch,防止并发对linked list照成破坏,如果未能获得该latch,就会在数据库中标记一个latch: cache buffers chains这个等待事件。如果该block存在于buffer cache中就不需要物理读,如果不存在,就需要从磁盘读取该block到buffer cache中。为了能够读取,并修改该block,我们就需要pin住该block,防止并发对于该block造成破坏,所以如果别的session不能获得pin,同时会标记一个buffer busy waits等待事件。
一般产生CACHE BUFFERS CHAINS的原因有几个方面:1、buffer cache太少(也说明SQL语句效率低);2、热块挣用。(从oracle9i开始,对latch:cache buffer chains支持只读共享访问,这可以减少部分争用,但并不能完全消除争用。)
一、buffer cache太少(也说明SQL语句效率低)
应用程序执行多个相同的低效率SQL语句并发会话,这些SQL语句都设法得到相同的数据集。较多的逻辑读意味着较多的latch get操作,从而增加了锁存器争用。多个进程同时扫描大范围的索引或表时,可能广泛地发生cache buffers chains 锁存器争用。每次执行都带有高 BUFFER_GETS(逻辑读取)的SQL语句是主要的原因。
1、查看当前的等待事件 ( latch: cache buffers chains)
SQL> select event, count(*) from v$session
 where wait_class <> 'Idle' group by event order by 2;
2、查看 latch: cache buffers chains事件相关的会话信息
SQL> select sid,username,machine,program,p1raw,sql_id,logon_time,last_call_et from v$session where event='latch: cache buffers chains';
二、热块挣用
当多个会话重复访问一个或多个由同一个子cache buffers chains锁存器保护的块时,就会产生热块挣用。当多个会话争用cache buffers chains锁存器时,找出是否有热块的最好的方法是检查latch free等待事件的P1RAW参数值。
判断热块挣用的另一种方法是从 v$session_wait 视图获得锁存器地址后进行比较。v$session_wait的P1RAW就相当于子锁存器地址,若从 v$session_wait 视图获得的锁存器地址过多重复出现,就意味着对相应锁存器发生次数偏多,此时可解释为热快引起的争用。如果会话正在相同的锁存器地址上等待,就是热块。
SQL> select sid,p1raw,p2,p3,seconds_in_wait,wait_time,state from v$session_wait
where event='latch: cache buffers chains' order by 3,2;
查看热块的对象:
根据TCH值确认热块。注意块从LRU列表的冷端移动到热端时,TCH值将重置为0,所以判断的时候,要注意TCH为0的块不一定是冷块。
使用P1RAW=00000300DA316800为例子进行关联热快对象。
SQL> select a.hladdr,a.file#,a.dbablk,a.tch,a.obj,b.object_name from x$bh a, dba_objects b
where (a.obj = b.object_id or a.obj = b.data_object_id) and a.hladdr = '00000300DA316800'
union select hladdr,file#,dbablk,tch,obj,null from x$bh
where obj in (select obj from x$bh where hladdr = '00000300DA316800' minus select object_id from dba_objects minus select data_object_id from dba_objects) and hladdr = '00000300DA316800' order by 4;
若没有关于SQL语句的信息,也有方法间接判断是热块引起的问题,还是低效SQL语句引起的问题。v$latch_children视图中,比较子cache buffers chains锁存器相应的 child#、gets、sleeps值, 以此判断特定子锁存器上使用的次数和争用是否集中,利用以下语句,获取sleeps次数高的子锁存器。
SQL> select * from (select addr, child#, gets, sleeps from v$latch_children where name = 'cache buffers chains' order by sleeps desc)
where rownum < =20;
当结果中sleeps的值倾斜较大的时候就说明是热块挣用。
根据sleeps较高的addr确定哪些块是热块。
SQL> select hladdr,obj,(select object_name from dba_objects where (data_object_id is null and object_id = x.obj) or data_object_id = x.obj and rownum = 1) as object_name,dbarfil,dbablk,tch from x$bh x where hladdr ='&p1raw' order by hladdr, obj;

==============End========================================================
作者:TangYun_ 发表于2014-6-5 22:35:28 原文链接
阅读:86 评论:0 查看评论

10个优秀的Objective-C和iOS开发在线视频教程

$
0
0

如果你自己开发iOS应用,你肯定会发现网上有很多资源。学习编程的一个最好的方法就是自己写代码,而开始写代码的最快的方式就是看其他人怎么写。我们从海量视频和学习网站中整理出了我

如果你自己开发iOS应用,你肯定会发现网上有很多资源。学习编程的一个最好的方法就是自己写代码,而开始写代码的最快的方式就是看其他人怎么写。我们从海量视频和学习网站中整理出了我们认为对你学习Objective-C和iOS开发最有用的视频教程列表。

 
在线视频
免费。这是斯坦福大学2013/2014学期关于开发iPhone/iPad  iOS 7应用程序的教程。讲师是Paul Hegarty,有18个讲座和几个课外作业。虽然它是优秀的iOS应用开发的入门教程,但对于学习C语言和面向对象的编程也很有帮助。
 
14天免费教程。在“iOS开发过程”中,有入门教程,比如“Objective-C基础” 和“iOS foundations”。也有教程基于的项目,比如“构建一个简单的iPhone应用程序(iOS 7)”和“构建一个blog reader iPhone应用”,这种教程可以帮你快速提升。
 
免费/付费。Udemy有超过两百万的注册会员。Udemy上有免费的教程,比如“如何制作iPhone应用——iOS开发教程”,也有比较受欢迎的付费教程,比如“学习如何使用Objective-C为iOS 7制作iPhone应用”。
 
7天免费教程。 Lynda.com有1-6小时长度不等的各种教程。比如初级教程:“iOS应用程序开发基础练习”,速成中级课程“iOS 7 SDK的新功能”。大多数教程只有会员可以观看,但可以免费预览。
 
免费。一共有12个讲座,涉及HTML5(2个),Android(5个),iOS(4个),Windows Mobile(1个)。每个讲座都有相应的幻灯片、源代码和视频。相应的课外作业也会发布到网上。
 
14天免费试听。SkillShare与前面提到的Udemy和Lynda.com类似,如果你是会员,可以访问所有会员教程以及20%的其它教程。
 
7天免费试听。该教程分为初级、中级和高级。其中的视频都是教师上传整理的。比如初级教程有“Objective-C初学者教程”。
 
付费。Pluralsight有丰富的iOS视频库。一些最新的教程会被在网站上推荐,比如 “IOS 7基础”。
 
免费/付费:Code School里面的视频倾向于边做边学。其中有一个比较受欢迎的免费教程是“Try Objective-C”。尽管这个特殊的教程并不包含视频,但是内容很有意思。
 
 
免费。65个YouTube视频,每个大约8分钟。这些演示讲座是学习Objective-C的入门教程。


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


ITeye推荐



为何要在Java中使用内存映射文件(Memory Mapped File)或者MappedByteBuffer

$
0
0

尽管从JDK 1.4版本开始,Java内存映射文件(Memory Mapped Files)就已经在java.nio包中,但它对很多程序开发者来说仍然是一个相当新的概念。引入NIO后,Java IO已经相当快,而且内存映射文件提供了Java有可能达到的最快IO操作,这也是为什么那些高性能Java应用应该使用内存映射文件来持久化数据。这在一些交易非常频繁的场合已经应用得很多,这些场合要求电子交易系统必须非常快速,单向时延要小于毫秒级。IO一直是那些高性能系统的一个主要关注点,内存映射文件允许你使用direct或者non-direct 字节缓存(Byte buffer)来直接读写内存。内存映射文件的一个关键优势是操作系统负责真正的读写,即使你的程序在刚刚写入内存后就挂了,操作系统仍然会将内存中的数据写入文件系统。另外一个更突出的优势是共享内存,内存映射文件可以被多个进程同时访问,起到一种低时延共享内存的作用。

 

什么是Java内存映射文件/IO

内存映射文件是一种允许Java程序直接从内存访问的特殊文件。通过将整个文件或者文件的一部分映射到内存中、操作系统负责获取页面请求和写入文件,应用程序就只需要处理内存数据,这样可以实现非常快速的IO操作。用于内存映射文件的内存在Java的堆空间以外。Java中的java.nio包支持内存映射文件,可以使用MappedByteBuffer来读写内存。

 

内存映射文件的优缺点

可能内存映射IO的主要优势是性能,内存映射文件比通过普通的IO来访问文件要快,这对于繁忙的电子交易系统来说非常重要。内存映射IO另外一个优势是能够加载普通方式无法访问的大文件,实验表明内存映射IO在大文件处理中表现得更好;但缺点是有增加页面错误(page fault)的可能,因为操作系统仅仅加载一部分文件到内存中,如果被请求的页面不在内存中那就会导致一个页面错误。大多数主流操作系统如Windows, Unix, Solaris和其他类Unix的操作系统都支持内存映射IO,在64位架构下,你几乎可以将任何文件映射到内存中并直接使用Java访问。另外一个优势是这些文件能够共享,在进程间提供共享内存,而且比普通的基于loopback接口的Socket要快10倍。

 

Java中MappedByteBuffer读写样例

下面的例子演示了如何使用内存映射文件来读写。我们使用RandomAccessFile打开文件并使用FileChannel的map()方法将它映射到内存,map()方法有三个输入参数:mode, position, size。返回值MappedByteBuffer是用来处理内存映射文件的字节缓存。

 

import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;


public class MemoryMappedFileInJava {


    private static int count = 10485760; // 10 MB


    public static void main(String[] args) throws Exception {


        RandomAccessFile memoryMappedFile = new RandomAccessFile("largeFile.txt", "rw");


        // Mapping a file into memory
        MappedByteBuffer out = memoryMappedFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, count);


        // Writing into Memory Mapped File
        for (int i = 0; i < count; i++) {
            out.put((byte) 'A');
        }
        System.out.println("Writing to Memory Mapped File is completed");


        // reading from memory file in Java
        for (int i = 0; i < 10; i++) {
            System.out.print((char) out.get(i));
        }
        System.out.println("Reading from Memory Mapped File is completed");


        memoryMappedFile.close();
    }


}

 

总结

 

下面快速总结一下Java内存映射文件和IO

1). Java语言通过java.nio包支持内存映射文件和IO。

2). 内存映射文件用于对性能要求高的系统中,如繁忙的电子交易系统

3). 使用内存映射IO你可以将文件的一部分加载到内存中

4). 如果被请求的页面不在内存中,内存映射文件会导致页面错误

5). 将一个文件区间映射到内存中的能力取决于内存的可寻址范围。在32位机器中,不能超过4GB,即2^32比特。

6). Java中的内存映射文件比流IO要快(译注:对于大文件而言是对的,小文件则未必)

7). 用于加载文件的内存在Java的堆内存之外,存在于共享内存中,允许两个不同进程访问文件。顺便说一下,这依赖于你用的是direct还是non-direct字节缓存。

8). 读写内存映射文件是操作系统来负责的,因此,即使你的Java程序在写入内存后就挂掉了,只要操作系统工作正常,数据就会写入磁盘。

9). Direct字节缓存比non-direct字节缓存性能要好

10). 不要经常调用MappedByteBuffer.force()方法,这个方法强制操作系统将内存中的内容写入硬盘,所以如果你在每次写内存映射文件后都调用force()方法,你就不能真正从内存映射文件中获益,而是跟disk IO差不多。

11). 如果电源故障或者主机瘫痪,有可能内存映射文件还没有写入磁盘,意味着可能会丢失一些关键数据。

12). MappedByteBuffer和文件映射在缓存被GC之前都是有效的。sun.misc.Cleaner可能是清除内存映射文件的唯一选择。

 

关于Java内存映射文件和内存映射IO就说这些了。 这相当有用,我希望你能够再深入了解一些。如果你的工作与繁忙的电子交易系统有关,那你很可能用到内存映射文件。

原文链接:http://javarevisited.blogspot.hk/2012/01/memorymapped-file-and-io-in-java.html



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


ITeye推荐




你们公司做代码审查吗?

$
0
0

每当从各种公司听到他们正在尝试自动化部署/测试的事情,我都非常关注,但通常会很吃惊,他们很少会考虑去实行代码审查制度。

看到这种情况,我通常想问: 如果代码没有经过其它人的审查,你如何知道你要测试的是什么?这答案(如果有的话)通常是捏着手指头说 有几个人在做代码审查或“正在考虑中”。

没有代码审查?真的吗? 不可思议?!?

代码审查不是可有可无的。

不论你采用什么形式的测试过程,什么形式的部署过程,没有代码审查——game over。为什么?因为代码的质量是一种人能看懂的质量。不管你如何测试,有如何严谨的部署流程,只有当另外一个人看了这些代码,并且表明能看懂时,这些代码才有意义。如果看不懂,你认为这样的代码——虽然测试通过、部署符合流程——可以上线吗?

没有经过代码审查,测试说明不了任何问题。测试通过但没有经过代码审查的代码仍然是有bug的。

什么是代码审查?

请参考 谷歌是如何做代码审查的

设计中使用的“走廊UX测试”是说:在开始实现你的设计前,至少需要有一个人看过你的设计。代码审查是相同的道理。

代码审查是说:在把你的代码合并到代码库里之前,请至少找一个人看看你的代码。

如何进行代码审查?

下面是代码审查基本的步骤:

  1. 把身体向左转。
  2. 看到另外一个程序员?拍拍他的肩膀。
  3. 让他看你的显示器。
  4. 说:这代码你能看懂吗?我打算把它提交到代码库里。
  5. 听他的建议。
  6. 自己做决定:是应该修改一下,还是继续提交到代码库里。

就这样。

现在,我想告诉你, 有大量的代码审查工具可以使用。它们都能高度的自定义配置。它们的作用都是让你代码审查过程更方面、灵活。

简单的几款代码审查工具

下面是我推荐的几款简单的代码审查工具(如果你的公司的程序员少于5千人)。

  1. less
  2. diff,wdiff
  3. GitHub pull requests

就是这些。虽然还有很多很多的 代码审查工具,我很少听说哪个程序员说喜欢它们的。而我的观点,less, diff, github pull requests能解决我们正常开发中的大部分代码审查问题。

如果你的代码审查过程过于复杂,需要使用大量的工具,这说明你过分的依赖于一些不 必要的形式,你应该简化它们。你可以说成你的反对的观点,或在 微博上和我们讨论。

coding

请阅读全文: 你们公司做代码审查吗?

本文由 外刊IT评论网( www.vaikan.com)原创发表
文章地址: 你们公司做代码审查吗?
[英文原文: Do you do code review? ]

你也许会喜欢这些文章:

  1. 代码审查中的暴力冲突
  2. 事后诸葛亮:如何写出没有bug的软件
  3. 代码审查:ThoughtBot官方给出的代码审查指导原则
  4. 谷歌是如何做代码审查的
  5. 代码审查和不良编程习惯




两种增量更新方案

$
0
0

在邮件/日历/SNS等客户端里,客户端数据要不断与服务端进行数据同步,在同步过程中,只拉取有修改的数据,称为增量更新,增量更新方案一般有两种,一是对比,二是日志。

对比

对比就是客户端请求服务端所有关键数据,跟本地已有的数据进行对比,筛选出增删改的数据进行更新。

用对比方法的好处是服务端什么都不用做,坏处是客户端逻辑复杂,耗网络流量。在这种方案里,数据的新增和删除很容易判断,根据客户端数据的id列表和服务端数据的id列表进行对比就行,若要判断哪个数据有修改则比较麻烦,需要取回数据进行对比,如果从服务端拉回所有对所有数据进行对比会很耗网络流量,有一个优化方式,就是对每个数据的修改进行标记。
以日历为例,一个日历可修改的字段很多,例如时间段,内容,邀请人等,全部拉回来对比不现实,对此可以在服务端给每个日历事件新增一个字段tag,表示这个日历事件的版本,服务端更新一个日历事件时会同时更新这个tag,客户端只需要取回每个id对应的tag,跟本地保存的tag对比,不一致表示这个日历事件已经更新,再去获取日历实体就完成更新了。

若服务端因为某些原因无法给每个数据保存一个版本标记,可以实时计算,在客户端和服务端约定一个算法,把所有可变参数拿出来,通过特定算法hash出一个值,对比这个hash值判断是否需要更新。

邮件协议IMAP,日历协议CalDAV就是用这种方式做增量更新,IMAP并没有做上述的优化,在判断邮件有没有更新时只能乖乖把所有数据请求回来对比,数据是XML,算是相当低效的协议。CalDAV给每个日历事件加了上述的tag,直接对比即可知道是否需要更新。

日志

日志指服务端记录数据的每一次增删改,用一个类似版本号的sync-key标记这次修改,客户端通过一个旧的sync-key向服务端请求,服务端返回这个sync-key与最新sync-key之间所有的修改给客户端,完成增量更新。

这个sync-key在服务端的实现上可以是时间,也可以是一个自增的id,sync-key之间有顺序关系就行。在一个数据集里,每次数据有更新,就新增一个sycn-key,并记录这次更新。图示这个过程:

Untitled Diagram

这个方案客户端逻辑很简单,但服务端负担较大,每次数据更新都要记录,客户端请求时需要查询给出相应的数据。这个方案在实际操作中还有两个问题:

一是时间长了服务端保存数据量过大。可以通过限制记录的条数解决,超过限制就删除最旧的记录。这样做会出现一个问题,若客户端带着在服务端已被删除的sync-key上来请求,该如何处理?一般做法是返回一个错误给客户端,让客户端重新拉取所有数据。

二是若客户端sync-key过旧,增量数据可能过大。客户端数据太老,有太多数据需要更新,若一次性返回所有增量数据,这个请求可能会很大,请求时间太长,成功率也会很低。解决方式是分多次请求,客户端和服务端可以约定一个字段作为阀值,服务端每次返回的增量数据量不超过这个阀值,若总数据超过这个阀值,则分多次请求,通过每次请求返回的sync-key定位下次请求该返回哪些数据。例如客户端sync-key是100,服务端最新sync-key是1000,阀值是50,客户端第一次带sync-key=100请求,服务端第一次返回sycn-key 100-150这一段增量数据,并返回sync-key=150,并有一个值告诉客户端这个sync-key还不是最新,客户端再带上sync-key=150请求,以此类推,直到sync-key=1000。

微软的Exchange/ActiveSync就是用这种方式实现增量更新,ActiveSync还用WBXML压缩了数据,更适用于移动端。此外日历协议CalDAV的也有一个扩展协议 RFC6578使用这种方式。ActiveSync和CalDAV扩展协议都有分多次请求增量数据的策略。

————

对于Timeline式的数据,增量更新方式多是以上两种,或者这两种的变体,可以根据业务特性修改或简化其中的逻辑,例如对于微博Timeline,它可以不考虑微博的修改,不考虑同步评论转发数的变化,不考虑同步删除的微博,并且每一条微博都有一个递增的id,那它的增量更新逻辑就很简单,只需要把客户端最新一条微博的id作为since_id传到服务端,返回比这个id更新的微博就行了,这里微博id相当于日志方式的sync-key,算是对日志方式的一种简化。

Java实现二维码技术探讨。

$
0
0

Java生成二维码方法有三种:

1: 使用SwetakeQRCode在Java项目中生成二维码 
http://swetake.com/qr/ 下载地址 
或着http://sourceforge.jp/projects/qrcode/downloads/28391/qrcode.zip 
这个是日本人写的,生成的是我们常见的方形的二维码 
可以用中文 如:5677777ghjjjjj 

2: 使用BarCode4j生成条形码和二维码 
BarCode4j网址:http://sourceforge.net/projects/barcode4j/ 

barcode4j是使用datamatrix的二维码生成算法,为支持qr的算法 
datamatrix是欧美的标准,qr为日本的标准, 
barcode4j一般生成出来是长方形的

如:88777alec000yan 
这个博客这方面说的挺清楚的: 
http://baijinshan.iteye.com/blog/1004554


3、现在详细介绍第三种也是常用的方式:利用Zxing生成二维码

Zxing是Google提供的关于条码(一维码、二维码)的解析工具,提供了二维码的生成与解析的方法,现在我简单介绍一下使用Java利用Zxing生成与解析二维码。

第一步:将Zxing-core.jar 包加入项目中。

第二步 添加以下三个Java文件,三个文件功能如下:

MatrixToImageWriter.java  生成二维码图片。

BufferedImageLuminanceSource.java   解析二维码图片。

BarcodeFactory.java  生成带头像的二维码。


MatrixToImageWriter.java

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.common.BitMatrix;

import javax.imageio.ImageIO;
import java.io.File;
import java.io.OutputStream;
import java.io.IOException;
import java.util.Hashtable;
import java.awt.image.BufferedImage;

public final class MatrixToImageWriter {

	private static final int BLACK = 0xFF000000;
	private static final int WHITE = 0xFFFFFFFF;

	private MatrixToImageWriter() {
	}

	public static BufferedImage toBufferedImage(BitMatrix matrix) {
		int width = matrix.getWidth();
		int height = matrix.getHeight();
		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		for (int x = 0; x < width; x++) {
			for (int y = 0; y < height; y++) {
				image.setRGB(x, y, matrix.get(x, y) ? BLACK : WHITE);
			}
		}
		return image;
	}

	public static void writeToFile(BitMatrix matrix, String format, File file) throws IOException {
		BufferedImage image = toBufferedImage(matrix);
		if (!ImageIO.write(image, format, file)) {
			throw new IOException("Could not write an image of format " + format + " to " + file);
		}
	}

	public static void writeToStream(BitMatrix matrix, String format, OutputStream stream) throws IOException {
		BufferedImage image = toBufferedImage(matrix);
		if (!ImageIO.write(image, format, stream)) {
			throw new IOException("Could not write an image of format " + format);
		}
	}
	public static void main(String[] args) throws Exception {
		String text = "二维码内容,可以是链接也可以是文本";
		int width = 300;
		int height = 300;
		Hashtable hints = new Hashtable();
		hints.put(EncodeHintType.CHARACTER_SET, "utf-8");//内容所使用编码  
		BitMatrix bitMatrix = new MultiFormatWriter().encode(text, BarcodeFormat.QR_CODE, width, height, hints);

		File outputFile = new File("E:/qrtest/生成普通二维码.jpg");
		//File.separator
		String format = "jpg";//二维码的图片格式  
		MatrixToImageWriter.writeToFile(bitMatrix, format, outputFile);

	}
}

BufferedImageLuminanceSource.java

import com.google.zxing.Binarizer;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.EncodeHintType;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;

import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

public final class BufferedImageLuminanceSource extends LuminanceSource {

	private final BufferedImage image;
	private final int left;
	private final int top;

	public BufferedImageLuminanceSource(BufferedImage image) {
		this(image, 0, 0, image.getWidth(), image.getHeight());
	}

	public BufferedImageLuminanceSource(BufferedImage image, int left, int top, int width, int height) {
		super(width, height);

		int sourceWidth = image.getWidth();
		int sourceHeight = image.getHeight();
		if (left + width > sourceWidth || top + height > sourceHeight) {
			throw new IllegalArgumentException("Crop rectangle does not fit within image data.");
		}

		for (int y = top; y < top + height; y++) {
			for (int x = left; x < left + width; x++) {
				if ((image.getRGB(x, y) & 0xFF000000) == 0) {
					image.setRGB(x, y, 0xFFFFFFFF); // = white
				}
			}
		}

		this.image = new BufferedImage(sourceWidth, sourceHeight, BufferedImage.TYPE_BYTE_GRAY);
		this.image.getGraphics().drawImage(image, 0, 0, null);
		this.left = left;
		this.top = top;
	}

	@Override
	public byte[] getRow(int y, byte[] row) {
		if (y < 0 || y >= getHeight()) {
			throw new IllegalArgumentException("Requested row is outside the image: " + y);
		}
		int width = getWidth();
		if (row == null || row.length < width) {
			row = new byte[width];
		}
		image.getRaster().getDataElements(left, top + y, width, 1, row);
		return row;
	}

	@Override
	public byte[] getMatrix() {
		int width = getWidth();
		int height = getHeight();
		int area = width * height;
		byte[] matrix = new byte[area];
		image.getRaster().getDataElements(left, top, width, height, matrix);
		return matrix;
	}

	@Override
	public boolean isCropSupported() {
		return true;
	}

	@Override
	public LuminanceSource crop(int left, int top, int width, int height) {
		return new BufferedImageLuminanceSource(image, this.left + left, this.top + top, width, height);
	}

	@Override
	public boolean isRotateSupported() {
		return true;
	}

	@Override
	public LuminanceSource rotateCounterClockwise() {

		int sourceWidth = image.getWidth();
		int sourceHeight = image.getHeight();

		AffineTransform transform = new AffineTransform(0.0, -1.0, 1.0, 0.0, 0.0, sourceWidth);

		BufferedImage rotatedImage = new BufferedImage(sourceHeight, sourceWidth, BufferedImage.TYPE_BYTE_GRAY);

		Graphics2D g = rotatedImage.createGraphics();
		g.drawImage(image, transform, null);
		g.dispose();

		int width = getWidth();
		return new BufferedImageLuminanceSource(rotatedImage, top, sourceWidth - (left + width), getHeight(), width);
	}

	public static void main(String[] args) {
		//解析二维码
		try {
			MultiFormatReader formatReader = new MultiFormatReader();
			String filePath = "E:/qrtest/生成普通二维码.jpg";
			File file = new File(filePath);
			BufferedImage image = ImageIO.read(file);
			LuminanceSource source = new BufferedImageLuminanceSource(image);
			Binarizer binarizer = new HybridBinarizer(source);
			BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
			Map hints = new HashMap();
			hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
			Result result = formatReader.decode(binaryBitmap, hints);

			System.out.println("result = " + result.toString());
			System.out.println("resultFormat = " + result.getBarcodeFormat());
			System.out.println("resultText = " + result.getText());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

BarcodeFactory.java

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;


public class BarcodeFactory {
	// 图片宽度的一般
	private static final int IMAGE_WIDTH = 80;
	private static final int IMAGE_HEIGHT = 80;
	private static final int IMAGE_HALF_WIDTH = IMAGE_WIDTH / 2;
	private static final int FRAME_WIDTH = 2;

	// 二维码写码器
	private static MultiFormatWriter mutiWriter = new MultiFormatWriter();

	/**
	 * 将srcImagePath嵌入到destImagePath
	 */
	public static void encode(String content, int width, int height, String srcImagePath, String destImagePath) {
		try {
			ImageIO.write(genBarcode(content, width, height, srcImagePath), "jpg", new File(destImagePath));
		} catch (IOException e) {
			e.printStackTrace();
		} catch (WriterException e) {
			e.printStackTrace();
		}
	}

	private static BufferedImage genBarcode(String content, int width, int height, String srcImagePath)
			throws WriterException, IOException {
		// 读取源图像
		BufferedImage scaleImage = scale(srcImagePath, IMAGE_WIDTH, IMAGE_HEIGHT, true);
		int[][] srcPixels = new int[IMAGE_WIDTH][IMAGE_HEIGHT];
		for (int i = 0; i < scaleImage.getWidth(); i++) {
			for (int j = 0; j < scaleImage.getHeight(); j++) {
				srcPixels[i][j] = scaleImage.getRGB(i, j);
			}
		}

		Map<EncodeHintType, Object> hint = new HashMap<EncodeHintType, Object>();
		hint.put(EncodeHintType.CHARACTER_SET, "utf-8");
		hint.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
		// 生成二维码
		BitMatrix matrix = mutiWriter.encode(content, BarcodeFormat.QR_CODE, width, height, hint);

		// 二维矩阵转为一维像素数组
		int halfW = matrix.getWidth() / 2;
		int halfH = matrix.getHeight() / 2;
		int[] pixels = new int[width * height];

		for (int y = 0; y < matrix.getHeight(); y++) {
			for (int x = 0; x < matrix.getWidth(); x++) {
				// 读取图片
				if (x > halfW - IMAGE_HALF_WIDTH && x < halfW + IMAGE_HALF_WIDTH && y > halfH - IMAGE_HALF_WIDTH&& y < halfH + IMAGE_HALF_WIDTH) {
					pixels[y * width + x] = srcPixels[x - halfW + IMAGE_HALF_WIDTH][y - halfH + IMAGE_HALF_WIDTH];
				}
				// 在图片四周形成边框
				else if ((x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH && x < halfW - IMAGE_HALF_WIDTH + FRAME_WIDTH&& y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH + IMAGE_HALF_WIDTH + FRAME_WIDTH)
						|| (x > halfW + IMAGE_HALF_WIDTH - FRAME_WIDTH && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH&& y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH + IMAGE_HALF_WIDTH + FRAME_WIDTH)
						|| (x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH&& y > halfH - IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH - IMAGE_HALF_WIDTH + FRAME_WIDTH)
						|| (x > halfW - IMAGE_HALF_WIDTH - FRAME_WIDTH && x < halfW + IMAGE_HALF_WIDTH + FRAME_WIDTH&& y > halfH + IMAGE_HALF_WIDTH - FRAME_WIDTH && y < halfH + IMAGE_HALF_WIDTH + FRAME_WIDTH)) {
					pixels[y * width + x] = 0xfffffff;
				} else {
					// 此处可以修改二维码的颜色,可以分别制定二维码和背景的颜色;
					pixels[y * width + x] = matrix.get(x, y) ? 0xff000000 : 0xfffffff;
				}
			}
		}

		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
		image.getRaster().setDataElements(0, 0, width, height, pixels);

		return image;
	}

	/**
	 * 把传入的原始图像按高度和宽度进行缩放,生成符合要求的图标
	 * 
	 * @param srcImageFile
	 *            源文件地址
	 * @param height
	 *            目标高度
	 * @param width
	 *            目标宽度
	 * @param hasFiller
	 *            比例不对时是否需要补白:true为补白; false为不补白;
	 * @throws IOException
	 */
	private static BufferedImage scale(String srcImageFile, int height, int width, boolean hasFiller) throws IOException {
		double ratio = 0.0; // 缩放比例
		File file = new File(srcImageFile);
		BufferedImage srcImage = ImageIO.read(file);
		Image destImage = srcImage.getScaledInstance(width, height, BufferedImage.SCALE_SMOOTH);
		// 计算比例
		if ((srcImage.getHeight() > height) || (srcImage.getWidth() > width)) {
			if (srcImage.getHeight() > srcImage.getWidth()) {
				ratio = (new Integer(height)).doubleValue() / srcImage.getHeight();
			} else {
				ratio = (new Integer(width)).doubleValue() / srcImage.getWidth();
			}
			AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(ratio, ratio), null);
			destImage = op.filter(srcImage, null);
		}
		if (hasFiller) {// 补白
			BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
			Graphics2D graphic = image.createGraphics();
			graphic.setColor(Color.white);
			graphic.fillRect(0, 0, width, height);
			if (width == destImage.getWidth(null))
				graphic.drawImage(destImage, 0, (height - destImage.getHeight(null)) / 2, destImage.getWidth(null), destImage
						.getHeight(null), Color.white, null);
			else
				graphic.drawImage(destImage, (width - destImage.getWidth(null)) / 2, 0, destImage.getWidth(null), destImage
						.getHeight(null), Color.white, null);
			graphic.dispose();
			destImage = image;
		}
		return (BufferedImage) destImage;
	}

	public static void main(String[] args) {
		//src.jpg为头像文件
		BarcodeFactory.encode("http://www.baidu.com", 300, 300, "E:/qrtest/src.jpg","E:/qrtest/生成带头像的二维码.jpg");
	}
}



作者:ziyeH2 发表于2014-6-6 11:19:52 原文链接
阅读:132 评论:0 查看评论

通过nginx配置文件抵御攻击

$
0
0

前言

大家好,我们是OpenCDN团队的Twwy。这次我们来讲讲如何通过简单的配置文件来实现nginx防御攻击的效果。

其实很多时候,各种防攻击的思路我们都明白,比如限制IP啊,过滤攻击字符串啊,识别攻击指纹啦。可是要如何去实现它呢?用守护脚本吗?用 PHP在外面包一层过滤?还是直接加防火墙吗?这些都是防御手段。不过本文将要介绍的是直接通过nginx的普通模块和配置文件的组合来达到一定的防御效 果。

验证浏览器行为

简易版

我们先来做个比喻。

社区在搞福利,在广场上给大家派发红包。而坏人派了一批人形的机器人(没有语言模块)来冒领红包,聪明工作人员需要想出办法来防止红包被冒领。
于是工作人员在发红包之前,会给领取者一张纸,上面写着“红包拿来”,如果那人能念出纸上的字,那么就是人,给红包,如果你不能念出来,那么请自觉。于是机器人便被识破,灰溜溜地回来了。

是的,在这个比喻中,人就是浏览器,机器人就是攻击器,我们可以通过鉴别cookie功能(念纸上的字)的方式来鉴别他们。下面就是nginx的配置文件写法。

if ($cookie_say != "hbnl"){      add_header Set-Cookie "say=hbnl";      rewrite .* "$scheme://$host$uri" redirect;  }

让我们看下这几行的意思,当cookie中say为空时,给一个设置cookie say为hbnl的302重定向包,如果访问者能够在第二个包中携带上cookie值,那么就能正常访问网站了,如果不能的话,那他永远活在了302中。 你也可以测试一下,用CC攻击器或者webbench或者直接curl发包做测试,他们都活在了302世界中。

当然,这么简单就能防住了?当然没有那么简单。

增强版

仔细的你一定会发现配置文件这样写还是有缺陷。如果攻击者设置cookie为say=hbnl(CC攻击器上就可以这么设置),那么这个防御就形同虚设了。我们继续拿刚刚那个比喻来说明问题。

坏人发现这个规律后,给每个机器人安上了扬声器,一直重复着“红包拿来,红包拿来”,浩浩荡荡地又来领红包了。
这时,工作人员的对策是这样的,要求领取者出示有自己名字的户口本,并且念出自己的名字,“我是xxx,红包拿来”。于是一群只会嗡嗡叫着“红包拿来”的机器人又被撵回去了。
当然,为了配合说明问题,每个机器人是有户口本的,被赶回去的原因是不会念自己的名字,虽然这个有点荒诞,唉。

然后,我们来看下这种方式的配置文件写法

if ($cookie_say != "hbnl$remote_addr"){      add_header Set-Cookie "say=hbnl$remote_addr";      rewrite .* "$scheme://$host$uri" redirect;  }

这样的写法和前面的区别是,不同IP的请求cookie值是不一样的,比如IP是1.2.3.4,那么需要设置的cookie是 say=hbnl1.2.3.4。于是攻击者便无法通过设置一样的cookie(比如CC攻击器)来绕过这种限制。你可以继续用CC攻击器来测试下,你会 发现CC攻击器打出的流量已经全部进入302世界中。

不过大家也能感觉到,这似乎也不是一个万全之计,因为攻击者如果研究了网站的机制之后,总有办法测出并预先伪造cookie值的设置方法。因为 我们做差异化的数据源正是他们本身的一些信息(IP、user agent等)。攻击者花点时间也是可以做出专门针对网站的攻击脚本的。

完美版

那么要如何根据他们自身的信息得出他们又得出他们算不出的数值?

我想,聪明的你一定已经猜到了,用salt加散列。比如md5(“opencdn$remote_addr”),虽然攻击者知道可以自己IP, 但是他无法得知如何用他的IP来计算出这个散列,因为他是逆不出这个散列的。当然,如果你不放心的话,怕cmd5.com万一能查出来的话,可以加一些特 殊字符,然后多散几次。

很可惜,nginx默认是无法进行字符串散列的,于是我们借助nginx_lua模块来进行实现。

rewrite_by_lua '      local say = ngx.md5("opencdn" .. ngx.var.remote_addr)      if (ngx.var.cookie_say ~= say) then          ngx.header["Set-Cookie"] = "say=" .. say          return ngx.redirect(ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri)      end  ';

通过这样的配置,攻击者便无法事先计算这个cookie中的say值,于是攻击流量(代理型CC和低级发包型CC)便在302地狱无法自拔了。

大家可以看到,除了借用了md5这个函数外,其他的逻辑和上面的写法是一模一样的。因此如果可以的话,你完全可以安装一个nginx的计算散列的第三方模块来完成,可能效率会更高一些。

这段配置是可以被放在任意的location里面,如果你的网站有对外提供API功能的话,建议API一定不能加入这段,因为API的调用也是没有浏览器行为的,会被当做攻击流量处理。并且,有些弱一点爬虫也会陷在302之中,这个需要注意。

同时,如果你觉得set-cookie这个动作似乎攻击者也有可能通过解析字符串模拟出来的话,你可以把上述的通过header来设置cookie的操作,变成通过高端大气的js完成,发回一个含有doument.cookie=…的文本即可。

那么,攻击是不是完全被挡住了呢?只能说那些低级的攻击已经被挡住而来,如果攻击者必须花很大代价给每个攻击器加上webkit模块来解析js 和执行set-cookie才行,那么他也是可以逃脱302地狱的,在nginx看来,确实攻击流量和普通浏览流量是一样的。那么如何防御呢?下节会告诉 你答案。

请求频率限制

不得不说,很多防CC的措施是直接在请求频率上做限制来实现的,但是,很多都存在着一定的问题。

那么是哪些问题呢?

首先,如果通过IP来限制请求频率,容易导致一些误杀,比如我一个地方出口IP就那么几个,而访问者一多的话,请求频率很容易到上限,那么那个地方的用户就都访问不了你的网站了。

于是你会说,我用SESSION来限制就有这个问题了。嗯,你的SESSION为攻击者敞开了一道大门。为什么呢?看了上文的你可能已经大致知 道了,因为就像那个“红包拿来”的扬声器一样,很多语言或者框架中的SESSION是能够伪造的。以PHP为例,你可以在浏览器中的cookie看到 PHPSESSIONID,这个ID不同的话,session也就不同了,然后如果你杜撰一个PHPSESSIONID过去的话,你会发现,服务器也认可 了这个ID,为这个ID初始化了一个会话。那么,攻击者只需要每次发完包就构造一个新的SESSIONID就可以很轻松地躲过这种在session上的请 求次数限制。

那么我们要如何来做这个请求频率的限制呢?

首先,我们先要一个攻击者无法杜撰的sessionID,一种方式是用个池子记录下每次给出的ID,然后在请求来的时候进行查询,如果没有的 话,就拒绝请求。这种方式我们不推荐,首先一个网站已经有了session池,这样再做个无疑有些浪费,而且还需要进行池中的遍历比较查询,太消耗性能。 我们希望的是一种可以无状态性的sessionID,可以吗?可以的。

rewrite_by_lua '  local random = ngx.var.cookie_random  if(random == nil) then  random = math.random(999999)  end  local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)  if (ngx.var.cookie_token ~= token) then  ngx.header["Set-Cookie"] = {"token=" .. token, "random=" .. random}  return ngx.redirect(ngx.var.scheme .. "://" .. ngx.var.host .. ngx.var.uri)  end  ';

大家是不是觉得好像有些眼熟?是的,这个就是上节的完美版的配置再加个随机数,为的是让同一个IP的用户也能有不同的token。同样的,只要有nginx的第三方模块提供散列和随机数功能,这个配置也可以不用lua直接用纯配置文件完成。

有了这个token之后,相当于每个访客有一个无法伪造的并且独一无二的token,这种情况下,进行请求限制才有意义。

由于有了token做铺垫,我们可以不做什么白名单、黑名单,直接通过limit模块来完成。

http{      ...      limit_req_zone $cookie_token zone=session_limit:3m rate=1r/s;  }

然后我们只需要在上面的token配置后面中加入

limit_req zone=session_limit burst=5;

于是,又是两行配置便让nginx在session层解决了请求频率的限制。不过似乎还是有缺陷,因为攻击者可以通过一直获取token来突破 请求频率限制,如果能限制一个IP获取token的频率就更完美了。可以做到吗?可以。同时,我们也要感谢一下Tengine,Tengine的 limit模块可以支持多个变量。本文的所有的实验均在Tengine下完成。

http{  ...  limit_req_zone $cookie_token zone=session_limit:3m rate=1r/s;  limit_req_zone $binary_remote_addr $uri zone=auth_limit:3m rate=1r/m;  }  location /{  limit_req zone=session_limit burst=5;  rewrite_by_lua '  local random = ngx.var.cookie_random  if (random == nil) then  return ngx.redirect("/auth?url=" .. ngx.var.request_uri)  end  local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)  if (ngx.var.cookie_token ~= token) then  return ngx.redirect("/auth?url=".. ngx.var.request_uri)  end  ';  }  location /auth {  limit_req zone=auth_limit burst=1;  if ($arg_url = "") {  return 403;  }  access_by_lua '  local random = math.random(9999)  local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random)  if (ngx.var.cookie_token ~= token) then  ngx.header["Set-Cookie"] = {"token=" .. token, "random=" .. random}  return ngx.redirect(ngx.var.arg_url)  end  ';  }

我想大家也应该已经猜到,这段配置文件的原理就是:把本来的发token的功能分离到一个auth页面,然后用limit对这个auth页面进行频率限制即可。这边的频率是1个IP每分钟授权1个token。当然,这个数量可以根据业务需要进行调整。

需要注意的是,这个auth部分我lua采用的是access_by_lua,原因在于limit模块是在rewrite阶段后执行的,如果在 rewrite阶段302的话,limit将会失效。因此,这段lua配置我不能保证可以用原生的配置文件实现,因为不知道如何用配置文件在 rewrite阶段后进行302跳转,也求大牛能够指点一下啊。

当然,你如果还不满足于这种限制的话,想要做到某个IP如果一天到达上限超过几次之后就直接封IP的话,也是可以的,你可以用类似的思路再做个 错误页面,然后到达上限之后不返回503而是跳转到那个错误页面,然后错误页面也做个请求次数限制,比如每天只能访问100次,那么当超过报错超过100 次(请求错误页面100次)之后,那天这个IP就不能再访问这个网站了。

于是,通过这些配置我们便实现了一个网站访问频率限制。不过,这样的配置也不是说可以完全防止了攻击,只能说让攻击者的成本变高,让网站的扛攻 击能力变强,当然,前提是nginx能够扛得住这些流量,然后带宽不被堵死。如果你家门被堵了,你还想开门营业,那真心没有办法了。

然后,做完流量上的防护,让我们来看看对于扫描器之类的攻击的防御。

防扫描

ngx_lua_waf模块 https://github.com/loveshell/ngx_lua_waf

这个是一个不错的waf模块,这块我们也无需重复造轮子。可以直接用这个模块来做防护,当然也完全可以再配合limit模块,用上文的思路来做到一个封IP或者封session的效果。

总结

本文旨在达到抛砖引玉的作用,我们并不希望你直接单纯的复制我们的这些例子中的配置,而是希望根据你的自身业务需要,写出适合自身站点的配置文件。

[via@ Twwy]

您可能也喜欢:

Nginx配置文件nginx.conf中文详解

nginx fastcgi配置失误+解析漏洞引发的漏洞

豌豆荚nginx解析漏洞,搜索型xss漏洞

nginx & flup & django & python3.x @ window7配置备忘录

记我配置Nginx代理的遭遇
无觅

The post 通过nginx配置文件抵御攻击 appeared first on 神刀安全网.

JMETER 参数化

$
0
0
1. 用Jmeter中的函数获取参数值,__Random,__threadNum,__CSVRead,__StringFromFile
   具体看函数帮助
2.用户定义的变量 “配置元件”->“用户定义的变量”
3. 从csv文件中读取“配置元件”->“CSV Data Set Config”
4.数据库中读取








调用方法${id_g1} ${id_g2} ${id_g3}
5.正则从其他请求获得数据,就是关联数据

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


ITeye推荐



Viewing all 15843 articles
Browse latest View live