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

分布式系统 读书笔记(二)数据平滑迁移

$
0
0

可以参考方案

在开始进行数据迁移时,记录增量的日志,在迁移结束后,再对增量变化进行处理。在最后,可以把要迁移的数据的写暂停,保证增量日志都处理完毕后,再切换规则,放开所有的写,完成迁移工作。

 

 用户信息表示示例

id     name   age   gender

 

我们希望根据id去模把上面这个表 划分到两个数据库中,  也就是id  mod 2 为0的还在原数据库  为1的在新库中

假设我们有4条数据

根据前面描述

1首先我们确定要开始扩容,并且开始记录数据库的数据变更的增量日志

    源库表          增量日志       新库表

     id=1 v=1

     id=2 v=1

     id=3 v=1

     id=4 v=2

id标识记录   v 标识版本号

 

2 接下来,数据开始复制到新库表,并且更新进来。可能形成以下格式

     源库表          增量日志         新库表

     id=1 v=2       id=1 update    id=1 v=1

     id=2 v=2       id=2 update    id=3 v=2

     id=3 v=2       id=3 update

     id=4 v=2   

可以看到1 和3 的已经在新库中。但是id=1的记录版本是旧的 id=3是新的

 

3.当全量迁移结束后,我们把增量日志中的数据也进行迁移 ,如下

  源库表          增量日志         新库表

     id=1 v=2       id=1 update    id=1 v=2

     id=2 v=2       id=2 update    id=3 v=2

     id=3 v=2       id=3 update  

     id=4 v=2       id=4 update

                          id=3 update

虽然不能保证新库和源库一直,但这是一个逐渐收敛的过程

 

4 然后我们进行数据比对,这时可能会有新库数据和源库数据不同的情况,把它们记录下来。

 

5、接着我们停止源数据库中对于要迁移的数据的写操作,然后进行递增日志处理,以使得新库表的数据时最新的。

 

6最后更新路由规则,所有新数据的读和写就到了新库表,这样就完成了迁移过程

 

 

书名《大型网站系统和Java中间件实践》

 



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


ITeye推荐




用户界面设计原则

$
0
0

“设计绝不是简单的拼合,排列甚至编辑;设计是通过阐明,简化、明确、修饰,使之庄严,有说服性,甚至带一点趣味性,来赋予其价值及意义。”——保罗·兰德(Paul Rand)

58331

1.“明确”应该放在设计的首要位置

对任何界面而言,“明确”是首要的也是最重要的一点。人们必须能够辨别出它是什么,才能有效地使用你设计出来的界面。设计师们在设计的时候,要去关心人们为何会使用这个应用,去了解什么样的界面是能帮助他们与之互动的,去预测人们在使用时的行为并能够成功地反馈给他们。这样做了之后,界面中再出现的需要推理的地方以及延时反应都是可以被容忍的,但是绝对不能出现让用户困惑的地方。明确的界面能够给使用者进一步操作的信心。一个应用就算它有一百张页面,但是每一页都是清晰明确的,它也远胜于只有一页却不知所云的应用。

 

2.界面是为了交互而存在

界面的存在是为了让人和我们的世界产生互动。它可以帮助人们厘清,明白,使用,展示相互之间的关系,他能够把我们聚集在一起可以将我们隔开,实现我们的价值并为我们服务。界面设计不是艺术设计。界面设计也不是用来标榜设计师的个人。界面的功用和效果是可以被测量的。但是它们不是功利性的。优秀的界面不但能够让我们做事有效率,还能够激发、唤起和加强我们与这个世界的联系。

 

3.不惜一切代价吸引用户注意

我们生活在一个容易被打扰的世界。我们很难在一个不被干扰的环境中静下心来阅读。用户注意力是非常宝贵的。所以,不要在应用的周围丢一些容易令人分心的东西……

要把设计这个画面最初的目的时刻放在首位。如果用户正在阅读,那先让他们专心的读完之后再弹出广告(如果一定要放广告的话)。尊重用户的注意力,不仅仅会让用户感到高兴,你本身的设计也会收获好结果。如果在界面设计中,用户使用是首要目标的话,那么尊重用户的注意力是先决条件。要不惜一切代价保护它。

 

4.让用户掌控一切

人们会在自己能掌控的环境中感觉最舒心,最放松。设计草率的软件应用不但剥夺了这种舒适性,还会迫使人们面对毫无预期的互动,困惑的流程和意外的结果。通过定期的梳理系统状态,描述因果关系(如果你这个做了,就会被体现出来),并且在每一步操作都给出提示,让用户感觉每一步操作都在他的掌控中。不要担心说,这不是“显而易见”的吗?因为世界上从来没有显而易见的事情。

 

5.直观操作是最好的

好的界面是无意识的,就像我们在实际生活中直接操作的感觉一样。这并不是那么容易实现的,并且随着元素和资讯的不断增加,这就变得更难,所以我们设计界面来帮助我们去和它们互动。要想在画面上添加一个不必要的东西是非常简单,例如,加个华丽的按钮,镶边,图形,选项,偏好,窗口,附件和其它一些冗余的东西。以至于我们一头扎进处理界面元素细节的怪圈中而忽视了最重要的事情。取而代之的,你应该抓住直观操作这个最初的目标……界面设计要尽可能的简洁,更多的可识别的惯用自然手势。理想情况下,界面会变得非常细微,用户在会有直观操作的感觉。

 

6.一个页面一个主要操作

我们设计的每一个画面,都应给用户提供有实际意义的单一操作。这一点,令界面更快上手、易于操作,如果有需要的话,新增或扩充也更简易。如果一个画面上有两个或两个以上的主要操作,瞬间就会让用户感到困惑。就像写文章要有单一的以及强有力的论点一样,界面设计中的单个画面,也都应该有单一且明确的操作,这是它存在的理由。

 

7.让次要操作在次要位置

画面在包含一个主要动作的同时,可以有多个次要动作,但尽量不要让它们喧宾夺主!就像你写一篇文章的目的,是为了让人可以阅读可以了解,而不是为了人们能够把它转载在社交网络上……让次要得动作放在次要的位置,削减它们的视觉冲击力,或是在主要动作完成后再显示它们。

 

8.提供自然而然的下一步操作

很少有交互是故意被放在最后的,所以要为用户精心设计交互的下一步操作。你要预期用户下一步的交互是怎样的,并且通过设计将其实现。就像我们的日常谈话,要为深入交谈开一个好头。当用户已经完成要做的操作后,别让它们不知所措的停留在那……提供自然而然的下一步,帮助他们完成操作。

 

9.界面外观遵循用户行为

人总是对符合期望的行为最感舒适。当某人或某件事的行为始终按照我们所期望的那样去进行时,我们会感觉到和他们之间的关系不错。因此,设计出来的元素,看起来,应该像它们本身特征一样。在具体操作中,这意味着,用户只要看到这个界面元素,就应该能猜测出这个元素是做什么的。如果看起来是个按钮,它就应该有按钮的功能,不要在基本的交互问题上耍小聪明……把你的创造力留到更高层次的需求上吧。

 

10.前后一致的重要性

遵循上一规则,画面中,视觉元素的外观不应该是一样的,除非他们的功能相近。如果是功能相同或相近的元素,那么它们外观就应该是类似的,反之,如果元素各自的功能不同,那么它们的外观也应该不同。为了保持一致性,新手设计师通常在会把相同的视觉处理 (重用代码) 方式用在,应该用不同的视觉处理方式的元素上。

 

11.强烈的视觉层次会让效果更好

强烈的视觉层次会让画面有清晰的浏览次序。也就是说,当用户每次都用相同的顺序浏览同样的东西,微小的视觉层次令使用者不知道哪里才是需要注意的重点,最后只会让用户感到困惑和混乱。在不断变化的设计环境中,保持强烈的视觉层次是很困难的,因为所有元素视觉上的重量是相对的:当所有文字都是粗体,那就没有所谓的"粗体"了。如果要在画面中添加一个视觉强烈的元素时,设计者应该要重新调整页面上所有元素的重量分配,来达到强烈视觉层次的效果。大多数人都不会注意到视觉重量这一点,但它其实是强化(弱化)设计的最简单的方法。

 

12.巧妙的布局减轻用户认知负担

正如约翰 前田(John Maeda)在他《Simplicity》书中所说的,恰当地编排画面上的元素能够以少见多,帮助他人更加快速简单地理解你设计的界面,因为你已经用你的设计清楚的说明了她们彼此之间的关系。用方位和方向上的编排可以自然地将相同的元素联系在一起。通过对内容的巧妙编排,可以减轻用户的认知负担,他们不再需要花时间去思考元素之间的关联,因为你已经做好了。不要迫使用户去思考……用你的设计直接呈现给他们看。

 

13.重点不要总是用颜色来强调

物体的色彩会随光线改变而改变。艳阳高照下与夕阳西下时,同一棵树,也会呈现不同的景象。在自然世界中,色彩很容易受环境影响而改变,所以在设计时,色彩不应该占很大的比重。作为辅助,我们可以用高亮的颜色,吸引注意力,但这不是区别这些的唯一方法。在长篇阅读或是长时间对着电脑屏幕的情况下,可以使用柔和的背景,降低亮度,当然也可用活泼亮丽的色彩当背景,但是要确保适合你的读者。

 

14.逐步说明

只在画面中显示必要的信息。如果用户需要作出决定,只要展现足够的信息供其选择,然后他们会到下一页去寻找更多的细节。避免一次呈现或解释全部的信息,如果可以的话,将选择放在后面的画面展示。这会使你的界面交互更加清晰。

 

15.内置帮助

在理想的界面设计中,「帮助」选项是没有必要,因为界面操作是有引导性的。「帮助」的下一步,实际上是,内嵌在上下文中的“帮助”,只有在用户确实需要的时候才显示,平常应该是隐藏的状态。让用户自己去寻求帮助,。重要的是你要保证用户可以顺畅的使用你的界面。

 

16.重要时刻:初始状态

第一次使用界面的体验是非常重要的,而这却常常被设计师忽略。为了让用户更快的上手,最好在设计的时候保持初始状态,也就是还没开始使用过的状态。这个状态不是一张空白的画布……它应该要提供一个方向和指引,令用户迅速进入状况。在初始状态下的互动过程中可能会存在一些摩擦,一旦用户了解了规则,那将会有很高的机会获得成功。

 

17.好的设计是隐形的

好的设计有一个奇怪的特性,它通常是会被用户忽略的。其中的一个原因是,一这个设计非常的成功,以至于用户完全专注在他想要达到的目标,而不是这个界面……当用户顺利的完成目的,他们会感到很满意,并不需要反映任何问题。作为一名设计师,这样会很困难……因为我们很少收到正面的回应,我们很少知道哪些设计是好的。但是优秀的设计师是满足于设计出好用的界面……并且他们知道满意的使用者通常是沉默的。

 

18.从其他设计领域下手

视觉,平面设计,排版,文案,信息架构和视觉设计……所有这些学科都是界面设计的一部分。他们都是可以被涉猎和研究的。不要企图跟他们划分界线,或看不起其他的领域:从学到学到很多知识可以帮助你的工作推动你成长,还可以从看似无关的学科学起,接触你不熟悉的领域……我们能从出版、编程、书本装订 、滑板、消防,空手道中学到哪些知识呢?

 

19.界面的存在是为了被使用

在大多数设计领域,有用户使用就是界面设计的成功。就像是一个漂亮椅子,虽然漂亮,但坐起来不舒服,用户就不会选择它,它就是一个失败的设计。因此界面的存在就为了尽可能多的创造好用的环境让用户使用,就像你设计了一个好用的工艺品。设计师设计作品如果仅仅是拿来满足自己的虚荣心,那是远远不够的:它必须要被使用!

source:UI中国


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

phonegap使用的java与js互相调用的原理

$
0
0

phonegap使用的java与js互相调用的原理。phonegap实现的模型刚也说了,有同步和异步两种。js实现的api,所以是js先会调用java代码,然后再返回给js。对于同步的而言,就是js调用java,然后java返回一个结果作为返回值。对于异步的而言,可能js掉了很多java代码,但是立即返回,然后java代码执行结束后再回调js代码,这里就涉及到js调java,然后java再调用js。

对于js调用java:

js调用java的入口是通过在js中调用prompt方法,这很奇怪吧,这个方法本来是让浏览器弹出个输入框的。我当初找了好久也没发现phonegap到底怎么搞得的让js调用java的代码,后来看到一会觉得该是这个方法,但是这是一个浏览器的客户端自己的东西,而且怪异的是浏览器并没有弹出输入框,后来终于发现。

在DroidGap.java中有个hack,重载了WebviewClient的onJsPrompt方法,然后执行了自己的逻辑。 也就是js调用prompt的时候,java端浏览器代码接受到这个,然后在响应的处理函数中根据传过来的参数,实现了一些特别的逻辑。可以从这个方法的注释上看出一二。

private LinkedList javascript;

服务器保存要回调的js的代码,供js客户端取回,这里java端是服务器端,js端是客户端,服务器端不可能请求客户端做啥,是b/s模型,所以phonegap实现了两种服务模型,一种是轮询,一种是XHR异步回调,也就是Ajax的模型。src/com/phonegap/CallbackServer.java是回调服务器的代码所在处。从类的注释中可以看到。

This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap. The CallbackServer class implements an XHR server and a polling server with a list of JavaScript statements that are to be executed on the web page.

CallbackServer提供的这两种模型,一种是XHR,一种是轮询,轮询很简单了,callbackserver服务器端,有一个保存回调js的列表,前面所说,然后每隔一段时间客户端的js会询问一次服务器,是否有需要回调的js,如果有则调用,然后每隔一段时再查询一次服务器。而基于XHR的,其实这个就是ajax用的机制了,js发起一个异步请求,然后服务器会在返回数据之前保持住这个连接,当返回数据就位后,服务器给请求客户端返回数据,然后关闭连接。然后客户端接受并且处理。

刚说了服务器端的代码实现,现在来看一下客户端js的相关代码。

PhoneGap.JSCallback = function() { ... xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true); xmlhttp.send(); }

这个是XHR模型的代码,客户端js使用xhr请求服务器来获取js代码,进行回调。

PhoneGap.JSCallbackPolling = function() { ... var msg = prompt("", "gap_poll:"); if (msg) { setTimeout(function() { try { var t = eval_r(""+msg); } catch (e) { console.log("JSCallbackPolling: Message from Server: " + msg); console.log("JSCallbackPolling Error: "+e); } }, 1); setTimeout(PhoneGap.JSCallbackPolling, 1); } else { setTimeout(PhoneGap.JSCallbackPolling, PhoneGap.JSCallbackPollingPeriod); } }

这个是轮询方式的,可以看到客户端每隔PhoneGap.JSCallbackPollingPeriod段时间,就请求一次服务器(通过prompt("", "gap_poll:");)。

 

转自: http://blog.sina.com.cn/s/blog_6e4d9a9b0101ny26.html



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


ITeye推荐



一个冷静致敬者的分析——营销销售必看

$
0
0

昨天没头没脑的发了一句“我要是不买个锤子,我就对不起老罗。”今天晚上跑到安福路话剧中心对面的马里昂吧咖啡,要一杯茶,打开电脑,好好跟大家解释一下我到底为啥要买锤子。

外行看热闹,内行看门道。作为一个从事销售工作的人,自然要看的是他如何进行一场产品演示,幸运的是,这场锤子手机的发布会堪称经典。网友戏称听了一场“相声”,这可不是一场简单的逗逗乐子的相声,整个相声是经过精心策划、编排的,哪里有包袱、哪里有掌声、哪里应该停顿一下、哪里应该卖卖关子,都经过了非常精密的设计。老罗绝对是一个销售天才,他对于产品卖点的包装、对于用户需求的把握、对于观众情绪的控制、对公司价值观的阐释都堪称世界级水平,就凭这一点,他赢得了我发自心底的尊敬,我觉得我必须要买一台锤子。

我是边看发布会边做笔记的,一共记了9条,分享给大家:

1、重新定义游戏规则

洗脑(请允许我用这个不是特别好的词,因为找不到更合适的)不是靠喊口号,这件事情需要坚实的逻辑基础和论证过程。老罗开场做的第一件事情是什么?重新定义游戏规则。

他先是罗列了许多手机厂商的卖点,什么四核、八核,2100万像素,HI-FI音质、跑分天王……然后他用嗤之以鼻的态度告诉你,这些东西都是低层次的竞争手段,他们是因为无法打造好的用户体验,所以用简单粗暴的参数来进行市场营销,这就好比一个女孩子无法用气质征服男人,于是亮出三围尺寸一样。

普通的竞争对手,会跟你在规则以内竞争,跟你比拼参数高低。高段位的竞争对手,就像老罗这样,直接修改规则。你不是说你那啥牛逼吗?我告诉你,那啥根本就没意义!恐怖的是,消费者还真的相信。这招够高。

2、说人话

做销售的谁都知道要跟客户说人话,但真能做到的,其实没有几个。老罗非常懂得这一点,他在展示硬件参数时,先用术语把参数写出来,然后自嘲自己也搞不懂,PPT画面切换,人话出来了:“量产的世界最好的CPU”(大意如此)。

做产品出身的营销人,最爱犯不说人话的错误。因为自己对产品太懂行了,就总觉得别人也跟自己一样,于是满口喷术语,人家听不懂还觉得人土鳖。亲,你是做生意呢,还是做学问呢?

3、信息透明

在99%的企业那里,“商业机密”就好像是姑娘的胸罩一样,你稍微要掀开来看看她就大嘴巴抽过来了。其实关于这一点,我一直没太想明白,企业害怕的到底是什么呢?如果你怕对手知道了秘密就超越你,那只能说明你的竞争门槛太低了。如果你怕客户知道了不买你,那是不是说明你赚的是黑心钱?

当99%的企业都拼命遮掩的时候,那1%公开透明的企业哪怕啥也不做,他都具有巨大的信用优势。老罗把他如何搞定JDI显示屏,如何搞定富士通的拍摄解决方案,如何搞定Ammunition设计公司背后的故事原原本本、详详细细的讲了一遍,他做的其他厂商其实也做了,但是别人都不讲,那他讲出来大家就会觉得他特别靠谱。

不管你信不信,反正我是信了。

4、身段低

在演讲过程当中,“考虑到我们是一个小厂……”这句话就像电影里面的重复蒙太奇一样,出现了N次,每次出现以后都伴随着一个小故事。我太喜欢这个桥段了,这句话一下就把公司和消费者之间的距离拉近了。以往,品牌往往喜欢把自己包装成非常高大上的模样,一副我很屌、你们都来膜拜我吧的样子,还说什么“粉丝经济”。这年头,大众媒体都走下神坛了,品牌还端着就是自欺欺人了。

就像老罗在最后说的,买卖是一件公平的事情,你情我愿,没有谁求着谁的事情,不是说你是消费者你就是上帝,大家其实都是平等的。

5、逼格高

身段低,那是一种态度。逼格高,那也是一种态度。虽然锤子科技是一个小厂,但是它的产品图片逼格是世界级的,Ammunition的Robert给他们做背书拍的那段video是世界级的。嘴上可以谦虚,做事绝不能含糊,这才能赢得尊敬。

6、从具体的使用场景出发

老罗最喜欢的环节是演示软件,因为这里他发挥的空间最大,他能够和其他品牌的差异化越大。在这个环节,我“哇”了好几次,我在笔记本上记下来的就有:抢拍功能、定时发短信、录音打小旗、屏幕整体下拉、定时静音、短信添加到日程、日程设置的图标化、截屏裁切。

一个产品对消费者有没有价值,不在于它价格多低,不在于它用了什么材料,不在于它的技术多么先进,只在于它对消费者有没有用处。用处越具体、解决的痛点越明确,产品就越好卖。我们怎么把excel卖给一个人?如果我们说excel可以做公式运算、可以生成图表、可以写程序,消费者估计听了一头雾水。正常的卖法应该这样:“老兄你是做财务的是吧?你看我这就有一个模板,你把数字往里边一填,不用你算,结果全都自动出来了,每天能给你节省5个小时时间。”

7、定价策略

定价不是越低越好,当然也不是越高越好。老罗自己都用PPT引用了网友对他的嘲笑,一个国产手机要卖3000块,脑子进水了?这个定价确实不低,但定价是否成功的关键不在于它的绝对值,而在于消费者是不是认为它值那个价,这个时候,就看品牌如何做出合理的解释了。

老罗的解释是:我们不做高性价比的手机,因为一分价钱一分货,低价格背后是对产品的妥协,我不想妥协(你看顺道又输出了价值观);我们也不做奢侈品,我们只做品质上不妥协的产品。解释合理吧?很合理。而且这个解释的言外之意是:买锤子的人,既不是那些只关心价格的屌丝,也不是那些买奢侈品手机的冤大头,而是那些真正在意用户体验的理性用户。

8、自问自答,打消疑虑

经过前面2个小时的演示之后,价格也揭秘了之后,就该进入到成交环节了。这时候消费者已经对产品产生了欲望,但仍然有一些“小小的障碍”会妨碍他们立刻下单,这个时候就要进入到“打消疑虑”环境了。

你们在等待4G版本吗?我告诉你,4G其实不要等了,没意义的。

3000块仍然觉得贵?没关系,我们和招商银行合作,可以24个月分期付款。

新厂商的售后和客服怎么样?别担心,作为一个砸过西门子冰箱的公众人物,你觉得我敢售后不好吗?

一个英语培训学校的前校长,如何领着一群前英语老师做出一个手机?我怎么可能领着一群英语老师做手机呢?神经病啊。我们的CTO是前moto的三大产品经理之一钱程博士,我们有200位工程师。

几个自问自答之后,你还不敢买锤子手机吗?

9、价值观输出

如果说前面2个多小时,老罗仍然是在产品层面在进行营销,那到最后这一段,就升华到价值观的层面了,昨晚一过10点,朋友圈里就反复出现“我不是为了输赢,我就是认真”这句话。为啥大家要转发这句话?一定是因为这句话触动了我们心中那块柔软的地方,那就叫做共鸣吧。

还有一句话没有获得很多的转发,但也让人感受到了老罗作为一个资本家的坦诚和追求:“处心积虑的靠改善人类生活来获取利润”。

至此,一个完美的产品演示胜利完成了。

跳出这个产品演示之外,我还想再补充几个我的观点:

1、老罗卖的是手机吗?

如果老罗仅仅是在卖一部手机,那就无法解释我昨天预定锤子手机的行为,因为我根本不缺手机(为此还和老婆小小的争论了一番)。我买的,其实是对认真做事的人的一种肯定。为什么我要肯定他?因为我在他身上寻找到了共鸣,我自己获得了一种精神上的体验。而且,我觉得使用锤子手机本身,也是在通过它向外界传递一种信号:我是用锤子的人,我既不是屌丝,也不是土豪,我就是一个认真的人。

其实所有商家都应该好好想想这个问题。如果你卖的仅仅是某种产品的使用价值,那么你就只能赚一个原材料的钱(据说国内手机厂商的利润率只有1%)。如果你卖的更多,那么你就能赚更多。比如说——

Roseonly卖的不是玫瑰花,是对爱人的在乎;

无印良品卖的不是东西,是一种朴素、自然、健康的生活方式;

明道卖的不是一个协同软件,是一种互联网时代的办公解决方案;

百达翡丽卖的不是手表,是家族的传承;

EMBA卖的不是课程,是同学圈子。

2、老罗是不可复制的。

如果你看了我前面总结的9条,然后很高兴的说:“哈,我知道了老罗成功的秘密,我也要模仿一下。”那就too simple,sometimes naive了,老罗是不可复制的。

商业好玩就好玩在这里,几乎所有成功的商业案例都是不可复制的。我们可以事后进行总结,但那只是时候诸葛亮,同样的方法换一个人用,就不奏效,即便是同一个人,换一个时间、换一个地点,可能也不奏效。

品牌的不可复制性,是对品牌最好的保护。你可以复制我的产品,你可以复制我的模式,但是你永远复制不了我经历的苦难,所以你永远无法变成我。

3、销售员的价值应该得到尊重。

虽然老罗自己一直在强调工匠精神,但真的只有锤子手机讲究工匠精神吗?魅族也讲究,moto也讲究,htc也讲究,但是为什么我们感受不到他们的工匠精神?因为他们没有一个像老罗这么能吹的好销售员。

其实,乔布斯也是苹果最大的销售员。设计不是乔布斯做的,技术不是乔布斯做的,凭什么乔布斯做老大?因为他能把东西卖出去。所以,千万别被老罗真的忽悠了,光有工匠精神是远远不够的,你还得有一个非常能吹的销售员帮你把好的东西卖出去,好的销售员,应该获得应有的尊重。

【产品运营】如何获得你的前一百万个用户?

$
0
0

到达第一个“百万用户”,社交平台Twitter用了24个月,图片视觉分享网站Pinterest用了20个月,地理签到应用Foursquare用了13个月,社交媒体Facebook用了10个月,网络文件共享工具Dropbox用了7个月,移动图片分享应用Instagram用了2个半月,而Path 2.0仅用了2周就达到了100万次的下载量。

那么,你又如何获取第一百万个用户?连续创业者Michael Geer和大家分享了他的经验。

你是否也认为好的产品就一定会吸引用户?是否也相信媒体报道最能吸引用户?Geer首先质疑了这两点,他提到,好的产品能留住(retain)用户,而非吸引(attract)用户。也就是说,再好的产品,都没有机会让人知道,怎么来的吸引?再者,公关和媒体报道,能吸引的是受众(audience),而不是用户(users),而这并不是做产品的真正目的。而找到正确的目标用户,然后站在信息流的顶端展开病毒式推广才是可行的方式。

Geer曾先后在十数家创业公司担任顾问或者联合创始人,目前他也在一家业界知识分享公司担任“创业学”的高级讲师,他乐于分享自己在这些创业企业中得来的经验。

原先深信不疑,却没有实际效益的方式:

1.购买用户数据:他也曾花钱去买了一堆用户资料,邮箱地址,通常这些购买来的用户质量都非常低,也不是你的目标用户,那就不能指望他们收到你的邮件后会点击注册。

2.在各个博客、论坛、社交网络留言板上留下创业公司的信息:除非你有免费的实习生,否则这就是在浪费时间。

3.接受所有早期用户的建议:用户的回馈和建议当然重要,但是众口难调,每一项产品和服务都有自己的褒赏和贬低。但是创业者要保持自己的战略和方向。

4.最酷的网站,最炫的视频:没错,这是一个企业形象很重要的一部分,然后有时候这未必能带来最好的效果,这不像加多宝广告一支持中国好声音就能增加销量,有时候你的视频被转载了几十万次,也换不来多少真正的用户。

其实不难看出,上述方法,要么没有找到自己的目标用户群,要么站在了信息流的末端。那么,什么才是实用的方法?

如何找到种子用户

万事开头难,或许比第一百万个用户更难的是第100个用户。Geer在这个阶段提到了合作伙伴的重要性,找那些与你拥有相同或者相似目标用户,但是提供完全不同服务的公司,去和他们合作,争取到他们的一些数据,甚至通过他们的订阅邮件来发布你的产品信息。

当然你会问,凭什么人家要跟你合作?这就是我们反复强调的创业者要有充足的资源(resourceful),以及互利共赢的概念。要么这些合作伙伴可能就来自你的朋友圈,要么,就给别人提供一个盈利的模式。

这些用户就像你的种子投资一样,是你依赖成长出百万用户的种子。

种子用户的蔓延

有了种子用户以后,我们如何最好地发挥种子效应?要依据自身产品的特点,获取客户的交际圈子,常见的方法有:

1.邮箱联系人:在美国市场,你最好开放四大邮箱的API,包括Gmail, Yahoo, Outlook(hotmail),和 AOL。当然,在中国市场,也别忘了网易邮箱,QQ邮箱等。

2.社交网络的API链接。就像国内允许接入微博账号,在美国,因为社交网络的差异可能会要求你做出不同的选择,主要的社交网络包括Facebook,Twitter,LinkedIn, Foursquare, Google+等。

3.手机通讯录

4.联系人文档:甚至包括一些传统的名片文件等。

打通用户链的3个方法

当你有了种子用户,当种子用户准备好了他们的联络人名单,我们接下去要做的就是让信息到达这些用户名单。根据不同产品的本性,Geer列举了下列三种方法:

1.产品天然的共享性:比如说Dropbox,和之前盛行的你画我猜。这些产品本身具有交互的共性,用户会自然地去鼓励下一个用户。

2.提供一些实惠,让用户参与到传播途径:这里的典型例子就是赠与,比如说团购网站,你邀请了朋友加入并完成第一次购物后,你可以获得相应的积分或者现金卡。

3.分享咨询:这是一般企业做的最多的方法,比如在社交网络上分享咨询,发起活动,传递信息等,并由种子用户传播给他们的朋友,再由一个圈子传递到下一个圈子。

开启病毒营销

所谓病毒式网络营销,就是信息像病毒一样传播和扩散,利用快速复制的方式传向数以万计的受众。那我们要做的,就是洒出这些传染源,哪些是开启病毒营销的好办法呢?

1.进入信息源的钥匙:哪怕信息爆炸,人们还是需要渴望杀手级的信息。你可以是TechCrunch这样的媒体,也可以是Pinerest那样的图片社交网站。

2.链接其他用户的通道:最好的例子是LinkedIn,这个以分享用户专业信息的社交网络,你必须连接更多人,才能体会其价值。

3.让用户自主生产信息:正如Foursquare或者街旁这样的地理签到软件,或者Instagram这样的图片分享工具,用户的参与性会激发他们向下传递。

4.分享折扣等信息,无论在哪个地方,折扣总是个好东西。

HBuilder :免费html5移动应用开发平台

$
0
0

HBuilder

HBuilder是一个工作环境(IDE),可以加速html5新应用的开发,这是在中国的首个同类产品,在该产品背后的开发团队Dcloud,相信这是全球同类产品当中表现最好的一个。

公司已经找出某种方式来促进html5移动应用在中国的表现。它们跟原生应用已经差不多好了,同样在低端的安卓设备上也可以表现很好,而低端安卓机占领中国智能手机市场的一大部分,也将会是不远的将来市场的主要驱动力量。

不像是世界上大多数其它产品,HBuilder 是免费的,也不准备对应用开发相关的任何服务针对开发者收费。团队有一个更大的计划。

这个几个人的团队Dcloud从2013年初开始建造这个工具。这是从商业软件解决方案开发者数字天堂分拆出来的团队。公司创始人王安曾是其总经理,但公司并不支持他们为html5移动应用增进体验创造开发工具的想法。王安最终辞去总经理职务,成为了Dcloud的CEO,但仍然保留在董事会当中的席位。

大家都对html5感到兴奋,这允许网页版的程序也可以做得很复杂,而且在技术方面的问题也已经出现很长一段时间。今天,随着各种各样的互联网服务提供商看到,来自智能移动设备越来越多的流量是被本地原生应用所占据,这类科技企业总想让网页版来一个流量的逆袭。

浏览器提供商发现,用户并不需要打开移动浏览器才能消费内容;搜索引擎没有办法索引原生应用内的内容,但原生内容却成为了移动应用的主流。现在,像谷歌这样的搜索引擎都鼓励开发者,把它们应用内的内容变得可以索引。

在中国领先的搜索提供商百度,发布了名为轻应用的方案,鼓励开发者将内容做得对搜索引擎更友好,并且可以在百度移动客户端当中访问。中国的手机浏览器制造商uc也开发了一个类似的计划,用户可以首先打开浏览器,然后再进入提交到那里的网页应用当中。有一些中国公司实际上已经从uc的方案当中获益,因为uc开发了针对pc的浏览器。

已经看到盈利的奇虎360,是通过免费的安全互联网产品和浏览器来赚钱的,他们决定开发网页游戏平台,而不是客户端游戏。大多数中国游戏分发者长期使用端游来赚钱,端游需要用户花时间和精力下载客户端。奇虎的用户只需要打开浏览器当中这些网页游戏的图标,就可以开始了。奇虎可能做了正确的选择。基于html5的网页游戏很快就会在中国起飞了。

现在很多科技公司,就像上面提到的搜索引擎和浏览器制造商,都希望基于网页的移动内容,有着htm15驱动的复杂功能,可以转换目前以原生应用为主的移动互联网,进入更新的竞争局面。

Dcloud也希望这样的事情能够发生。HBuilder是他们的核心产品,可以吸引开发者,CEO说,现在已经有差不多两万人进入。接下来公司会围绕它建立一个生态系统,提供针对开发者的网页论坛等服务。最终公司会接管应用分发,希望创造新的应用程序分发的方式,就像奇虎曾经做过的那样,从这里再获得营收。

有一些投资者已经觉得这种想法很靠谱。公司获得了来自CSDN老总,已经成为天使投资者的蒋涛的融资,同时还有一位原来facebook员工转向的天使投资者Harry Wang,以及另外一家中国科技公司。(译:dio)

HBuilder: to Make HTML5 Mobile Apps Booming in China

本文 HBuilder :免费html5移动应用开发平台来自 动点科技.

POJO中使用ThreadLocal实现Java嵌套事务

$
0
0

大多嵌套事务都是通过EJB实现的,现在我们尝试实现对POJO的嵌套事务。这里我们使用了ThreadLocal的功能。

理解嵌套事务

事务是可以嵌套的。所以内层事务或外层事务可以在不影响其他事务的条件下进行回滚或提交。

新建的事务嵌套在外层事务中。如果内层事务完成(不论是回滚或是提交),外层的事务就可以进行回滚或提交,这样的操作并不会影响内层事务。首先关闭最内层的事务,并逐步移动到外层事务。

使用简单的POJO实现

新建如下接口:

importjava.sql.Connection;

public interface TransactionManager {

    Connection getConnection();
    void beginTransaction();
    void commit();
    void rollback();
}

新建如下事务管理类:

importjava.sql.Connection;
importjava.sql.DriverManager;
importjava.sql.SQLException;
importjava.util.Stack;

public class TransactionManagerStackImpl implements TransactionManager {
    
    private Stack<Connection>connections = new Stack<Connection>();

    @Override
    public Connection getConnection() {

        if (connections.isEmpty()) {
            this.addConn();
        }

        return connections.peek();
    }

    @Override
    public void beginTransaction() {
        this.addConn();
    }

    @Override
    public void commit() {
        try {
            if (connections.peek() != null&& !connections.peek().isClosed()) {
                System.out.println(connections.peek().toString() +"--Commit---");
                connections.peek().commit();
                connections.pop().close();
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void rollback() {
        try {

            if (connections.peek() != null&& !connections.peek().isClosed()) {
                System.out.println(connections.peek().toString() +"--Rollback---");
                connections.peek().rollback();
                connections.pop().close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    private void addConn() {
        try {
            Connection con = this.getMysqlConnection();
            con.setAutoCommit(false);
            connections.push(con);
            System.out.println(con.toString() +"--Conection---");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private Connection getMysqlConnection() {
        return getConnection("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/testdb", "test", "test12345");
    }

    private Connection getConnection(String driver, String connection,
            String user, String password) {

        try {
            Class.forName(driver);
            return DriverManager.getConnection(connection, user, password);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        returnnull;

    }
}

到这里,我们创建了一个栈(Stack)

private Stack<Connection> connections = new Stack<Connection>();

事务遵循栈“先进后出”的原则,通过栈存储事务的连接:

public void beginTransaction()

beginTransaction()用于开启一个新的事务,并将连接加入到栈中。自动提交设置为否:

public Connection getConnection()

getConnection()获得当前事务的连接。如果连接为空,则创建新的连接并将其加入到栈:

public void commit()

提交当前的事务,之后关闭连接,并将其从栈中移除:

public void rollback()

回滚当前的事务,之后关闭连接,并将其从栈中移除。

上面的TransactionManagerStackImpl类为单线程创建了嵌套事务。

多线程的嵌套事务

在多线程的应用中,每个线程都有其独立的事务和嵌套事务。

我们使用ThreadLocal管理栈的连接。

importjava.sql.Connection;

public class TransactionManagerThreadLocalimplementsTransactionManager {
    
    private static final ThreadLocal<TransactionManager>tranManager = newThreadLocal<TransactionManager>() {
    protected TransactionManager initialValue() {
        System.out.println(this.toString() + "--Thread Local Initialize--");
    return new TransactionManagerStackImpl();
        }
      };

    @Override
    public void beginTransaction() {
        tranManager.get().beginTransaction();
    }

    @Override
    public void commit() {
        tranManager.get().commit();
    }

    @Override
    public void rollback() {
        tranManager.get().rollback();
    }

    @Override
    public Connection getConnection() {
        returntranManager.get().getConnection();
    }
}

这里初始化TransactionManagerStackImpl,在线程中创建嵌套的事务。

测试

测试上面的方法,提交内层事务,回滚外层事务。

importjava.sql.Connection;

public class NestedMain implements Runnable {
    
    private int v = 0;
    private String name;
    
    NestedMain(int v, String name) {
        this.v = v;
        this.name = name;
    }

    public static void main(String[] args) throws Exception{
        
        for (inti = 0; i< 3; i++) {
            NestedMain main = newNestedMain(i * 10, "Ravi" + i);
            new Thread(main).start();
        }
    }

    @Override
    public void run() {
        
        try {
            TransactionManagerThreadLocal local = new TransactionManagerThreadLocal();
            
            // Transaction 1 ( outer )
            local.beginTransaction();
            Connection con = local.getConnection();
            String sql = "INSERT INTO test_tran (emp_id, name) VALUES ('1"+v+"', '"+ name+v+"')";
            this.insert(con, sql);
                // Transaction 2 ( Inner )
                local.beginTransaction();
                con = local.getConnection();
                sql = "INSERT INTO test_tran (emp_id, name) VALUES ('2"+v+"', '"+ name+v+"')";
                this.insert(con, sql);
                local.commit(); // Committing 2

            local.rollback(); // Rollback 1 Outer

        } catch (Exception e) {
            e.printStackTrace();
        }

结果

com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.mysql.jdbc.JDBC4Connection@10dd1f7--Conection---
com.mysql.jdbc.JDBC4Connection@1813fac--Conection---
com.mysql.jdbc.JDBC4Connection@136228--Conection---
com.mysql.jdbc.JDBC4Connection@1855af5--Conection---
com.mysql.jdbc.JDBC4Connection@e39a3e--Conection---
com.mysql.jdbc.JDBC4Connection@1855af5--Commit---
com.mysql.jdbc.JDBC4Connection@e39a3e--Commit---
com.mysql.jdbc.JDBC4Connection@9fbe93--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Commit---
com.mysql.jdbc.JDBC4Connection@10dd1f7--Rollback---
com.mysql.jdbc.JDBC4Connection@1813fac--Rollback---
com.mysql.jdbc.JDBC4Connection@136228--Rollback---

|  name       | emp_id           
| ------------- |:-------------:
| Ravi220        | 220
| Ravi00      | 20      
|Ravi110 | 210      

内层事务回滚,外层事务提交的情况:

com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.ttit.TransactionManagerThreadLocal$1@1270b73--Thread Local Initialize--
com.mysql.jdbc.JDBC4Connection@9f2a0b--Conection---
com.mysql.jdbc.JDBC4Connection@136228--Conection---
com.mysql.jdbc.JDBC4Connection@1c672d0--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Conection---
com.mysql.jdbc.JDBC4Connection@1858610--Conection---
com.mysql.jdbc.JDBC4Connection@9fbe93--Rollback---
com.mysql.jdbc.JDBC4Connection@1858610--Rollback---
com.mysql.jdbc.JDBC4Connection@1a5ab41--Conection---
com.mysql.jdbc.JDBC4Connection@1a5ab41--Rollback---
com.mysql.jdbc.JDBC4Connection@9f2a0b--Commit---
com.mysql.jdbc.JDBC4Connection@136228--Commit---
com.mysql.jdbc.JDBC4Connection@1c672d0--Commit---
...
|  name       | emp_id           
| ------------- |:-------------:
| Ravi00         | 10
| Ravi220      | 120      
|Ravi110 | 110 

相关文章

Teiid 8.8 Beta1 发布,数据虚拟化系统

$
0
0

Teiid 8.8 Beta1 发布,Teiid是一个数据虚拟化系统,让应用程序使用来自多个异构数据存储的数据。

Teiid由一堆工具,组件和创建和执行双向数据的服务所组成。通过抽象和联合、数据存取和集成的实时分布式数据源,无需进行复制或以其他方式移动数据系统的纪录。

简而言之:Teiid 可以让你用 JDBC + SQL 来访问企业的任何数据,并可对这些不同源的数据进行联合查询。

与 Alpha1 版本比较,值得关注的改进有:

  • TEIID-2263 More control over MAKE (NOT) DEP hints via syntax that allows for traversing views.  See the reference for more.
  • TEIID-2263 Common Table (WITH) planning improvements such that the usage of a WITH clause in a view or inline view no longer inhibits planning as part of the larger query.

Hadoop学习总结

$
0
0

 
Hadoop是什么东西?什么用? 
(一) Hadoop是什么? 
  一个开发和运行处理大规模数据的软件平台,是Appach的一个用java语言实现开源软件框架,实现在大量计算机组成的集群中对海量数据进行分布式计算。Hadoop框架中最核心设计就是:HDFS和MapReduce,HDFS实现存储,而MapReduce实现原理分析处理,这两部分是hadoop的核心。数据在Hadoop中处理的流程可以简单的按照下图来理解:数据通过Haddop的集群处理后得到结果,它是一个高性能处理海量数据集的工具。 

(二) Hadoop为什么如此受欢迎,有什么用? 

Hadoop开源系统实现了MapReduce编程模型,采用了分布式存储方式提高了读写速度,并扩大了存储容量。采用MapReduce来整合分布式文件系统上的数据,可保证分析和处理数据的高效。与此同时,Hadoop还采用存储冗余数据的方式保证了数据的安全性,Hadoop中的HDFS的高容错性,以及它是基于Java语言开发的,这使得Hadoop可以部署在低廉的计算机集群中,同时不限于某个操作系统。Hadoop中的HDFS的数据管理能力,MapReduce处理任务时的高效率,以及它的开源特性,使其在同类的分布式系统中大放异彩。MapReduce编程模型之所以受到欢迎和迅速得到应用,在技术方面有三方面的原因: 
1.MapReduc所采用的是无共享大规模集群系统,集群系统具有良好的性价比和可伸缩性。 
2.MapReduce编程模型简单,易于理解,易于使用。 
3.虽然基本的MapReduce模型只提供一个过程性的编程接口,但是在海量的数据环境,需要保证可伸缩性的前提下,通过使用适合的查询优化和索引技术,MapReduce仍然能够提供相当好的数据处理性能。 
Hadoop的优缺点介绍: 
(一) 优点: 
(一)高可靠性。Hadoop按位存储和处理数据的能力值得人们信赖; 
(二)高扩展性。Hadoop是在可用的计算机集簇间分配数据并完成计算任务的,这些集簇可以方便地扩展到数以千计的节点中。 
(三)高效性。Hadoop能够在节点之间动态地移动数据,并保证各个节点的动态平衡,因此处理速度非常快。 
(四)高容错性。Hadoop能够自动保存数据的多个副本,并且能够自动将失败的任务重新分配。 
(二) 缺点: 
(一)不适合低延迟数据访问。 
(二)无法高效存储大量小文件。 
(三)不支持多用户写入及任意修改文件。
Hadoop集群的组成介绍: 
(一) Hadoop家族由以下几个子项目组成 
整个Hadoop家族由以下几个子项目组成,现在Hadoop已经发展成为包含多个子项目的集合。虽然其核心内容是MapReduce和Hadoop分布式文件系统(HDFS),但是在Hadoop下的Common、Avro、Chukwa、Hive、HBase等子项目也是不可或缺的。他们在提供了互补性服务或在核心层上提供了更高层的服务。 

1). Core/Common 
从Hadoop0.20版本开始,Hadoop Core项目便更名为Common。它是Hadoop体系最底层的一个模块,为Hadoop各子项目提供各种工具,如:FileSystem、RPC和串行化库,他们为在廉价的硬件上搭建云计算环境提供基本的服务,并且为运行在该平台上的软件开发提供了所需的API,配置文件和日志操作等。 
2) . Avro 
Avro是用于数据序列化的系统。它提供了丰富的数据结构类型、快速可压缩 
的二进制数据格式、存储持久性数据的文件集、远程调用RPC的功能和简单的动态语言集成功能。其中,代码生成器既不需要读写文件数据,也不需要使用或实现RPC协议,它只是一个可选的对静态类型语言的实现。 
Avro系统依赖于模式(Schema),Avro数据的读和写是在模式之下完成的。这样就可以 
减少写入数据的开销,提高序列化的速度并缩减其大小。同时,也可以方便动态脚本语言的使用,因为数据连同其模式都是自描述的。 
在RPC中,Avro系统的客户端和服务端通过握手协议进行模式的交换。因此当客户端 
和服务端拥有彼此全部的模式时,不同模式下的相同命名字段、丢失字段和附加字段等信息的一致性问题就得到了很好的解决。 
3). Chukwa 
Chukwa:Chukwa是开源的数据收集系统,用于监控和分析大型分布式系统的数据。 
Chukwa是在Hadoop的HDFS和MapReduce框架之上搭建的,它同时继承了Hadoop的可扩展性和健壮性。Chukwa通过HDFS来存储数据,并依赖于MapReduce任务处理数据。 
Chukwa中也附带了灵活且强大的工具,用于显示、监视和分析数据结果,以便更好地利用所收集的数据。 
4). HBase 
基于Hadoop Distributed File System,是一个开源的,基于列存储模型的分布式数据库。 
5). HDFS 
是一个分布式文件系统。由于HDFS具有高容错性(fault-tolerant)的特点,所以可以设计部署在低廉(low-cost)的硬件上。它可以通过提供高吞吐率(highthroughput)来访问应用程序的数据,适合那些有着超大数据集的应用程序。HDFS放宽了可移植操作系统接口(POSIX,Portable Operating System Interface)的要求,这样就可以实现以流的形式访问文件系统中的数据。HDFS原本是开源的Apache项目Nutch的基础结构,最后它成为了Hadoop的基础架构之一。 
以下是HDFS的设计目标: 
  检测和快速恢复硬件故障。硬件故障是常见的问题,整个HDFS系统由数百台或数千 
台存储着数据文件的服务器组成,而如此多的服务器意味着高故障率,因此,故障的 
检测和自动快速恢复是HDFS的一个核心目标。 
  流式的数据访问。HDFS使应用程序能流式地访问它们的数据集。HDFS被设计成适 
合进行批量处理,而不是用户交互式的处理。所以它重视数据吞吐量,而不是数据访 
问的反应速度。 
  简化一致性模型。大部分的HDFS程序操作文件时需要一次写入,多次读取。一个文 
件一旦经过创建、写入、关闭之后就不需要修改了,从而简化了数据一致性问题和高 
吞吐量的数据访问问题。 
  通信协议。所有的通信协议都在TCP/IP协议之上。一个客户端和明确配置了端口的 
名字节点(NameNode)建立连接之后,它和名称节点(NameNode)的协议便是客户 
端协议(Client Protocal)。数据节点(DataNode)和名字节点(NameNode)之间则 
用数据节点协议(DataNode Protocal)。 
6). Hive 
Hive最早是由Facebook设计的,是一个建立在Hadoop基础之上的数据仓库, 
它提供了一些用于数据整理、特殊查询和分析存储在Hadoop文件中的数据集的工具。Hive 
提供的是一种结构化数据的机制,它支持类似于传统RDBMS中的SQL语言来帮助那些熟悉SQL的用户查询Hadoop中的数据,该查询语言称为Hive QL。与此同时,那些传统的MapReduce编程人员也可以在Mapper或Reducer中通过Hive QL查询数据。Hive编译器会把Hive QL编译成一组MapReduce任务,从而方便MapReduce编程人员进行Hadoop应用的开发。 
7). MapReduce 
实现了MapReduce编程框架 
8). Pig 
Pig是一个对大型数据集进行分析和评估的平台。Pig最突出的优势是它的 
结构能够经受住高度并行化的检验,这个特性让它能够处理大型的数据集。目前,Pig 
的底层由一个编译器组成,它在运行的时候会产生一些MapReduce程序序列,Pig的语 
言层由一种叫做Pig Latin的正文型语言组成。 
9). ZooKeeper 
Zookeeper 是Google的Chubby一个开源的实现。它是一个针对大型分布式系统的可靠协调系统,提供的功能包括:配置维护、名字服务、分布式同步、组服务等。 ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 
(二) HDFS的体系结构 
我们首先介绍HDFS的体系结构,HDFS采用了主从(Master/Slave)结构模型,一个HDFS集群是由一个NameNode和若干个DataNode组成的。其中NameNode作为主服务器,管理文件系统的命名空间和客户端对文件的访问操作;集群中的DataNode管理存储的数据。HDFS允许用户以文件的形式存储数据。从内部来看,文件被分成若干个数据块,而且这若干个数据块存放在一组DataNode上。NameNode执行文件系统的命名空间操作,比如打开、关闭、重命名文件或目录等,它也负责数据块到具体DataNode的映射。DataNode负责处理文件系统客户端的文件读写请求,并在NameNode的统一调度下进行数据块的创建、删除和复制工作。 
NameNode和DataNode都被设计成可以在普通商用计算机上运行。这些计算机通常运行的是GNU/Linux操作系统。HDFS采用Java语言开发,因此任何支持Java的机器都可以部署NameNode和DataNode。一个典型的部署场景是集群中的一台机器运行一个NameNode实例,其他机器分别运行一个DataNode实例。当然,并不排除一台机器运行多个DataNode实例的情况。集群中单一的NameNode的设计则大大简化了系统的架构。NameNode是所有HDFS元数据的管理者,用户数据永远不会经过NameNode。 
Hadoop组成主要由 NameNode,DataNode,Secondary NameNode,JobTracker,TaskTracker组成。 

(一)NameNode中记录了文件是如何被拆分成block以及这些block都存储到了那些DateNode节点,同时保存了文件系统运行的状态信息。 (Block(块):一个文件分块,默认64M) 
(二)DataNode中存储的是被拆分的blocks。 
(三)Secondary NameNode帮助NameNode收集文件系统运行的状态信息。 
(四)JobTracker当有任务提交到Hadoop集群的时候负责Job的运行,负责调度多个TaskTracker。 
(五)TaskTracker负责某一个map或者reduce任务。 
(三) MapReduce的体系结构 
     接下来介绍MapReduce的体系结构,MapReduce是一种并行编程模式,这种模式使得 
软件开发者可以轻松地编写出分布式并行程序。在Hadoop的体系结构中,MapReduce是一 
个简单易用的软件框架,基于它可以将任务分发到由上千台商用机器组成的集群上,并以一 
种高容错的方式并行处理大量的数据集,实现Hadoop的并行任务处理功能。MapReduce框 
架是由一个单独运行在主节点上的JobTracker和运行在每个集群从节点上的TaskTracker共 
同组成的。主节点负责调度构成一个作业的所有任务,这些任务分布在不同的从节点上。主 
节点监控它们的执行情况,并且重新执行之前失败的任务;从节点仅负责由主节点指派的任 
务。当一个Job被提交时,JobTracker接收到提交作业和配置信息之后,就会将配置信息等 
分发给从节点,同时调度任务并监控TaskTracker的执行。 
从上面的介绍可以看出,HDFS和MapReduce共同组成了Hadoop分布式系统体系结构 
的核心。HDFS在集群上实现了分布式文件系统,MapReduce在集群上实现了分布式计算和 
任务处理。HDFS在MapReduce任务处理过程中提供了文件操作和存储等支持,MapReduce 
在HDFS的基础上实现了任务的分发、跟踪、执行等工作,并收集结果,二者相互作用,完 
成了Hadoop分布式集群的主要任务。 
(四) MapReduce并行运行原理 
MapReduce计算模型非常适合在大量计算机组成的大规模集群上并行运行。每一个 map 任务和每一个reduce 任务均可以同时运行于一个单独的计算节点上,可想而知,其运算效率是很高的,那么这样的并行计算是如何做到的呢?下面将简单介绍一下其原理。 

1. 数据分布存储 
Hadoop分布式文件系统(HDFS)由一个名称节点(NameNode )和N个数据节点 (DataNode)组成,每个节点均是一台普通的计算机。在使用方式上HDFS与我们熟悉的单机文件系统非常类似,它可以创建目录,创建、复制和删除文件,以及查看文件的内容等。但HDFS底层把文件切割成了Block,然后这些 Block 分散地存储于不同的 DataNode 上,每个 Block 还可以复制数份数据存储于不同的 DataNode 上,达到容错容灾的目的。NameNode 则是整个 HDFS 的核心,它通过维护一些数据结构来记录每一个文件被切割成了多少个Block、这些 Block 可以从哪些 DataNode 中获得,以及各个 DataNode 的状态等重要信息。 

2. 分布式并行计算 
Hadoop 中有一个作为主控的 JobTracker,用于调度和管理其他的 TaskTracker,JobTracker 可以运行于集群中的任意一台计算机上。TaskTracker则负责执行任务,它必须运行于 DataNode 上,也就是说DataNode 既是数据存储节点,也是计算节点。 JobTracker 将 map 任务和 reduce 任务分发给空闲的 TaskTracker,让这些任务并行运行,并负责监控任务的运行情况。如果某一个 TaskTracker 出了故障,JobTracker 会将其负责的任务转交给另一个空闲的 TaskTracker 重新运行。 

3. 本地计算 
数据存储在哪一台计算机上,就由哪台计算机进行这部分数据的计算,这样可以减少数 
据在网络上的传输,降低对网络带宽的需求。在 Hadoop 这类基于集群的分布式并行系统中,计算节点可以很方便地扩充,它所能够提供的计算能力近乎无限,但是由于数据需要在不同的计算机之间流动,故网络带宽变成了瓶颈,“本地计算”是一种最有效的节约网络带宽的手段,业界把这形容为“移动计算比移动数据更经济”。 

4. 任务粒度 
把原始大数据集切割成小数据集时,通常让小数据集小于或等于 HDFS 中一个 Block 的 
大小(默认是64MB),这样能够保证一个小数据集是位于一台计算机上的,便于本地计算。有 M 个小数据集待处理,就启动 M 个 map 任务,注意这 M 个map 任务分布于 N 台计算机上,它们会并行运行,reduce 任务的数量 R 则可由用户指定。 

5. 数据分割(Partition) 
把 map 任务输出的中间结果按 key 的范围划分成R份(R是预先定义的reduce 任务的 
个数),划分时通常使用 hash 函数(如:hash(key) mod R),这样可以保证某一范围内的 key一定是由一个 reduce 任务来处理的,可以简化 Reduce 的过程。 

6. 数据合并(Combine) 
在数据分割之前,还可以先对中间结果进行数据合并(Combine),即将中间结果中有相 
同 key的 <key, value> 对合并成一对。Combine 的过程与reduce 的过程类似,很多情况下可以直接使用reduce 函数,但 Combine 是作为map 任务的一部分,在执行完map函数后紧接着执行的。Combine 能够减少中间结果中 <key, value> 对的数目,从而降低网络流量。 

7. Reduce 
Map 任务的中间结果在做完 Combine 和 Partition 之后,以文件形式存于本地磁盘 
上。中间结果文件的位置会通知主控 JobTracker,JobTracker 再通知 reduce 任务到哪一个 DataNode 上去取中间结果。注意,所有的map 任务产生的中间结果均按其key 值用同一个 hash 函数划分成了R份,R个reduce 任务各自负责一段key 区间。每个reduce 需要向许多个map 任务节点取得落在其负责的key 区间内的中间结果,然后执行reduce函数,形成一个最终的结果文件。 

8. 任务管道 
有 R 个 reduce 任务,就会有 R 个最终结果,很多情况下这 R 个最终结果并不需要合并成一个最终结果,因为这 R 个最终结果又可以作为另一个计算任务的输入,开始另一个并行计算任务,这也就形成了任务管道。 
(五) HDFS的数据管理 
HDFS是分布式计算的存储基石,Hadoop分布式文件系统和其他分布式文件系统有很多类似的特质: 
  对于整个集群有单一的命名空间; 
  具有数据一致性。适合一次写入多次读取的模型,客户端在文件没有被成功创建之前是无法看到文件存在的; 
  文件会被分割成多个文件块,每个文件块被分配存储到数据节点上,而且会根据配置由复制文件块来保证数据的安全性。 

HDFS通过三个重要的角色来进行文件系统的管理: 
NameNode、DataNode和Client。NameNode可以看做是分布式文件系统中的管理者,主要负责管理文件系统的命名空间、集群配置信息和存储块的复制等。NameNode会将文件系统的 Metadata存储在内存中,这些信息主要包括文件信息、每一个文件对应的文件块的信息和每一个文件块在DataNode中的信息等。 DataNode是文件存储的基本单元,它将文件块(Block)存储在本地文件系统中,保存了所有Block的Metadata,同时周期性地将所有存在的 Block信息发送给NameNode。Client就是需要获取分布式文件系统文件的应用程序。以下通过三个具体的操作来说明HDFS对数据的管理。 
(1)文件写入 
1) Client向NameNode发起文件写入的请求。 
2)NameNode根据文件大小和文件块的配置情况,返回给Client它所管理的DataNode 
的信息。 
3)Client将文件划分为多个Block,根据DataNode的地址信息,按顺序将其写入每一 
个DataNode块中。 
(2)文件读取 
1) Client向NameNode发起读取文件的请求。 
2) NameNode返回文件存储的DataNode信息。 
3)Client读取文件信息。 
(3)文件块(Block)复制 
1) NameNode发现部分文件的Block不符合最小复制数这一要求或部分DataNode失效。 
2)通知DataNode相互复制Block。 
3)DataNode开始直接相互复制。 

HDFS作为分布式文件系统在数据管理方面还有几个值得借鉴的功能: 
  文件块(Block)的放置:一个Block会有三份备份,一份放在NameNode指定的 
DataNode上,另一份放在与指定的DataNode不在同一台机器上的DataNode上,最 
后一份放在与指定的DataNode在同一Rack上的DataNode上。备份的目的是为了数 
据安全,采用这种配置方式主要是考虑同一Rack失败的情况,以及不同Rack之间的 
数据拷贝会带来的性能问题。 
  心跳检测:用心跳检测DataNode的健康状况,如果发现问题就采取数据备份的方式 
来保证数据的安全性。 
  数据复制(场景为DataNode失败、需要平衡DataNode的存储利用率和平衡 
DataNode数据交互压力等情况):使用Hadoop时可以用HDFS的balancer命令配置 
Threshold来平衡每一个DataNode的磁盘利用率。假设设置了Threshold为10%,那 
么执行balancer命令的时候,首先会统计所有DataNode的磁盘利用率的平均值,然 
后判断如果某一个DataNode的磁盘利用率超过这个均值,那么将会把这个DataNode 
的block转移到磁盘利用率低的DataNode上,这对于新节点的加入来说十分有用。 
  数据校验:采用CRC32做数据校验。在写入文件Block的时候,除了写入数据外还 
会写入校验信息,在读取的时候则需要校验后再读入。 
  单个NameNode:如果失败,任务处理信息将会记录在本地文件系统和远端的文件系 
统中。 
  数据管道性的写入:当客户端要写入文件到DataNode上时,客户端首先会读取一个 
Block,然后写到第一个DataNode上,接着由第一个 DataNode将其传递到备份的 
DataNode上,直到所有需要写入这个Block的DataNode都成功写入后,客户端才会 
开始写下一个 Block。 
  安全模式:分布式文件系统启动的时候会有安全模式(系统运行期间也可以通过命令 
进入安全模式),当分布式文件系统处于安全模式时,文件系统中的内容不允许修改 
也不允许删除,直到安全模式结束。安全模式主要是为了在系统启动的时候检查各 
个DataNode上的数据块的有效性,同时根据策略进行必要的复制或删除部分数据块。 
在实际操作过程中,若在系统启动时修改和删除文件会出现安全模式不允许修改的错 
误提示,需要等待片刻即可恢复。 



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


ITeye推荐



MapReduce原理

$
0
0
转载自:http://www.cnblogs.com/wildman/archive/2008/08/22/1274175.html

我们为什么要关注MapReduce?

1.什么是MapReduce?

       MapReduce 是由Google公司的Jeffrey Dean 和 Sanjay Ghemawat 开发的一个针对大规模群组中的海量数据处理的分布式编程模型。MapReduce实现了两个功能。Map把一个函数应用于集合中的所有成员,然后返回一个基于这个处理的结果集。而Reduce是把从两个或更多个Map中,通过多个线程,进程或者独立系统并行执行处理的结果集进行分类和归纳。Map() 和 Reduce() 两个函数可能会并行运行,即使不是在同一的系统的同一时刻。

        Google 用MapReduce来索引每个抓取过来的Web页面。它取代了2004开始试探的最初索引算法,它已经证明在处理大量和非结构化数据集时更有效。用不同程序设计语言实现了多个MapReduce,包括 Java, C++, Python, Perl, Ruby和C, 其它语言。在某些范例里,如Lisp或者Python, Map() 和Reduce()已经集成到语言自身的结构里面。通常,这些函数可能会如下定义:

List2 map(Functor1, List1);
Object reduce(Functor2, List2);

 

Map()函数把大数据集进行分解操作到两个或更多的小“桶”。而一个“桶”则是包含松散定义的逻辑记录或者文本行的集合。每个线程,处理器或者系统在独立的“桶”上执行Map()函数,去计算基于每个逻辑记录处理的一系列中间值。合并的结果值就会如同是单个Map()函数在单个“桶”上完全一致。Map()函数的通常形式是:

 map(function, list) {
  foreach element in list {
    v = function(element)
    intermediateResult.add(v)
  }
} // map

 

Reduce()函数把从内存,磁盘或者网络介质提取过来的一个或多个中间结果列表,对列表中的每个元素逐一执行一个函数。完成操作的最终结果是通过对所有运行reduce()操作的处理结果进行分类和解释。Reduce()函数的通常形式是:

 reduce(function, list, init) {
  result = init
  foreach value in list {
    result = function(result, value)
  }
  outputResult.add(result)
}

MapReduce的实现把业务逻辑从多个处理逻辑中分离出来了,map()和reduce()函数跨越多个系统,通过共享池和部分RPC的形式来达到彼此之间的同步和通信。这里的业务逻辑是由用户自定义的函数子实现,并且这些函数子只能用在逻辑记录处理的工作上,而不用关心多个处理操作的问题。这样一旦MapReduce框架就位,就能通过大量的处理器快速的转变为应用系统的并行处理。因为开发人员可以把精力花在写函数子上面了。MapReduce簇可以通过替换函数子和提供新的数据源来重新使用,而无需每次都对整个应用进行编译,测试和部署。

 

2.实现MapReduce

       MapReduce()的目的是为了大型集群的系统能在大数据集上进行并行工作。

图1显示了一个运行在一个主系统上的主程序,协调其它实例进行map()或者reduce()操作,然后从每个reduce操作中收集结果。

【图 1】

 

主应用程序负责把基础的数据集分解到“桶”中。桶的最佳大小依赖于应用,结点的数量和可用的I/O带宽。这些“桶”通常存储在磁盘,但有必要也可能分散到主存中,这依赖于具体的应用。“桶”将作为map()函数的输入。

       主应用程序也负责调度和分散几个MapReduce的核心备份,除了控制者给空闲的处理器或线程分配了调整map()和reduce()任务之外,它们是完全相致的。控制者会持续跟踪每个map()和reduce()任务的状态,并且可以作为map()和reduce()任务之间路由中间结果的管道。每个map() 任务处理器完全指派给“桶”,然后产生一个存到共享存储区域的中间结果集。共享存储可以设计成分布缓存,磁盘或其它设备等形式。当一个新的中间结果写入共享存储区域后,任务就向控制者发出通知,并提供指向其共享存储位置的句柄。

       当新的中间结果可用时,控制者分配reduce()任务。这个任务通过应用独立的中间键值来实现排序,使相同的数据能聚集在一起,以提供更快的检索。大块的结果集可以进行外部排序,reduce()任务遍历整个排序的数据,把每个唯一的键和分类的结果传递到用户的reduce() 函数子进行处理。

      通过map()和reduce()实例终端的处理,当所有的“桶”都用完,然后全部的reduce()任务就通知控制者,以说明它们的结果已经产生了。控制者就向主应用程序发出检索这个结果的信号。主应用程序可能就直接操作这些结果,或者重新分配到不同的MapReduce控制者和任务进行进一步的处理,

       显示情况下MapReduce的实现可能通常分配给控制者,map()和reduce()任务分配给单独的系统。Google操作模型是基于跨越大量的廉价硬件设备上组成的集群或者白盒子上面部署MapReduce应用。为了处理它自己的桶的需要,每个白盒子都有本地存储装置,一个合理数量的私有内存(2到4GB RAM)和至少两个处理内核。白盒子是可互相交换的,主应用程序可能把集群中的任何机器指派为控制者,而这个机器就把map()和reduce()任务分派给其它连接的白盒子。

 

3.基于Java的MapReduce 实现

       Google的环境是为它自己的需求定制和适应它们的操作环境。比如,为了它的MapReduce实现更好的执行这种类型的操作,使其更优化,Google使用了专有的文件系统用来存储文件。相反,企业应用系统都是建立在Java或类似的技术上面的,它们依赖于已有的文件系统,通信协议和应用栈。

       一个基于Java的MapReduce实现应该考虑到已存在的数据存储设备,将来部署到的结构里面支持那种协议,有哪些内部API和支持部署那种第三方产品(开源的或商业的)。图2显示了通常的架构是如何通过映射到已有的、健壮的Java开源架构来实现的。

【图 2】

这个架构采用了已有的工具,比如Terracotta和Mule,它们经常出现在很多企业系统的组织里面。已物理或虚拟系统形成存在的白盒子通过有效简单的配置和部署,设计成MapReduce群组中的一部分。为了效率,一个很大的系统可以分解到几个虚拟机器上,如果需要可以分配更多的结点。可以根据容量的问题和处理器的有效利用赖决定是否在群集中使用物理“白盒子”,虚拟机或者它们两者的结合。

       Terracotta 集群技术是map()和reduce()任务之间共享数据的很好选择,因为它把map()和reduce()之间的通信过程,包括共享文件或者使用RPC调用已初始处理结构都做了抽象。

从前面章节的描述知道,Map()和reduce()任务是在同一个核心应用中实现的。用来共享中间结构集的数据结构可以保持在内存的数据结构中,通过Terracotta透明的共享交换。

       由跨域集群的MapReduce产生的进程内通信问题,自从Terracotta运行时掌管着这些共享数据结构后就不存在了。不同于实现一个复杂的信号系统,所有的map()任务需要标记内存中的中间结构集,然后reduce()任务就直接提取它们。

       控制者和主应用程序都会在同时处在等待状态一段时间,即使是MapReduce集群有大量可用的并行处理能力。这两个组件之间以及当归并完成后的reduce()任务和控制者之间,都是通过Mule的ESB传递信号的。通过这种方式,为了其它应用的处理,输出结构可以排到队列,或者像前面章节讲的一样,为了其他MapReduced的处理,一个Mule服务对象(or UMO)可以把这些输出结果分解到“桶”中。

       通过主流的企业应用协议或者完全的原始TCP/IP Sockets,Mule支持在内存中进行同步和异步的数据传输. Mule可以用于在同一台机器执行的应用系统,跨域不同的数据中心或者在完全不同地方且被程序员分开标识的本地终端结点互相传递输出结构集。其它基于Java的实现可以通过Hadoop, 是一个用于运行应用程序在大型集群的廉价硬件设备上的框架, 它是基于lucene框架。Hadoop是一个开源,点对点,通用的MapReduce实现。

 

4.结论

       不管使用什么技术,索引大量非结构化数据是一件很艰难的任务。应用传统的算法和启发式方法很难维护,因为随着时间的推移,系统的性能下降使系统变得难以控制。RDBMS 能有效的用于索引和检索大量的结构化数据集合,但不适合用于非结构化的数据。MapReduce为并行系统的数据处理,提供了一个简单,优雅的解决方案,优势有:

l         归并成本

l         程序员的高产出,因为用并行的代码独立实现了业务逻辑

l         比传统RDBMS技术更好的性能和更优的结果。

l         使用Java企业框架和开发人员都熟悉的已有的技术和工具更易部署

   

    用MapReduce, Google有令人印象深刻的跟踪记录,而且每天出现的工具都能轻易的融入到这个体系中。在企业级的应用系统中,如果你准备开始一个快速,简单的任务,例如根据IP地址分析请求的拥堵模型到你的web集群中,或者类似的东西。一个这样的练习将会很大程度上提高你对关键系统面临的问题和挑战的认识,MapReduce则就是为这些而准备的。

 

英文原址: http://www.theserverside.com/tt/knowledgecenter-tc/knowledgecenter-tc.tss?l=MapReduce






杨粼波 2014-06-10 00:12 发表评论

谈大数据应用场景

$
0
0
对于大数据的应用场景,谈的文章已经相当多,包括各行各业对大数据处理和分析的应用,在此仅仅思考在各种不同的行业如何来发现潜在存在的大数据应用场景。

首先可以从大数据的4V特性入手来进行思考和分析,在数据类型上更加强调了多种异构类型数据形成的混合存储,对于传统单纯的结构化数据或单纯的文档类非结构化数据都有解决方案,而真正难的是混合存储并提供统一的大数据服务开放能力接口。拿企业内部信息化应用场景来说,如果从单一入口原则入手,某个关键字能够搜索到邮件,业务系统,文本文档,互联网等多种渠道来源的异构混合数据,即可形成一个典型的大数据场景。

对于海量的问题一定要区分结构化数据和非结构化数据分别对待,对于完全的结构化数据往往上10T已经是一个海量的数据库,如果仅仅从单节点考虑这种数据库已经很难真正满足大数据分析所需要的速度要求,转而才是需要的类似MPP+ShareNothing机制或Hadoop分布式存储加分析机制来解决OLAP层面的问题。因此对于传统的BI应用面对海量数据无法满足准实时性数据分析需求的时候,需要考虑的是大数据分析和应用。

在速度和时效上是我们考虑的另外一个重要问题,传统的ODS库或OLAP分析往往很难满足实时性的要求。而基于增量的实时数据采集,流处理机制等很好的解决了这个问题。在这里并不是强调的数据量和数据的异构情况,而是更加强调了对数据的增量实时采集和分析机制。那么对于传统ODS构建无法满足实时或即席查询的场景往往也存在大数据技术的应用。

其次从大数据带来的一些思维转变上来分析大数据的场景,首先是对于企业的大数据分析和应用,首先就是要将视线从传统的企业内部拓展到企业外部,特别是在用户行为分析,市场营销等方面基于企业内部传统业务系统收集的数据是远远不够的,只有基于大量外部数据的相关性分析往往才能得出更加有价值的推论。这也是往往互联网行业对大数据应用最先发展和成熟一样,来自企业外围的用户行为,社交,交易,行动路线等数据,来自各种传感设备采集的视频,流量,温度数据才真正构成了一个大数据环境。

大数据关注的是全量数据而非抽样数据,那么这带来的思维转变就是原来采用抽样数据分析和统计的场景是否可以转化为大数据场景,而需要采集全量数据一定不可能靠人工来完成,转化的替代思维就是需要通过传感网和各种传感设备自动采集完成。因此抽样-》全量-》传感设备实时采集全量数据-》全量数据存储和分析即构成一个完整的大数据思维的转变。

分析评估或预测模型,类似交通行业的交通流预测或诱导模型,金融行业的信用评估和风控模型,医疗行业的疾病预测模型,保险行业的精算模型等,当我们对这些模型进行重新思考的时候会发现,原有建模和模型分析思路往往并没有错,但是在原来本身就会遇到数据收集困难性,如涉及到大量外部协同单位数据的开放和收集,涉及到用户行为和习惯数据的收集等,而这些也正是大数据的重要应用场景。大数据下我们强调相关性,但是不能否定因果关系。其实很多时候实际情况还是我们首先在思考一个价值目标,然后再考虑围绕这个价值目标所涉及到的所有相关因素和因子,再考虑这些因子间的相互关系权重,因素的采集和分析方法等。

不论是哪个行业的大数据分析和应用场景,可以看到一个典型的特点还是无法离开以人为中心所产生的各种用户行为数据,用户业务活动和交易记录,用户社交数据,这些核心数据的相关性再加上可感知设备的智能数据采集就构成一个完整的大数据生态环境。

单纯的数据采集,数据存储,数据处理往往都只是大数据中应用到的技术能力,而大数据场景的本质还是业务价值驱动下的大数据分析和挖掘,为了达到这个目标往往则涉及到数据采集,集成,存储,处理,分析,挖掘等大数据的全生命周期管理过程。

  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

LTPA Cookie原理

$
0
0

1. 什么是LTPA?

Lightweight Third-Party Authentication (LTPA)是IBM Websphere和Domino产品中使用单点登录技术。当服务器配置好LTPA认证方式,用户通过浏览器成功登录后,服务器会自动发送一个session cookie给浏览器;此cookie中包含一个LTPA Token。


2. WebSphere部分

本部分描述适用于已实施WebSphere系列产品应用和Domino平台应用,或WebSphere与Domino之间已完成单点登录。在这样的环境中与构异系统实现单点登录。

一个通过有效的LTPA Cookie能够在同一个认证域中所有服务器被自动认证。此Cookie中包含认证信息和时间戳。这些信息通过共享的3DES Key进行了bis 加密。使用公共密钥/私有密钥进行签名。
LTPA Cookie通过3DES密钥使用DESede/ECB/PKCS5P进行加密。此密钥也是采用DESede/ECB/PKCS5P进行加密,加密后再使用提供的密码再进行SHA Hash,生成24个字节的密钥,再进行Base64编码。
 
如果你要解析LTPA Token,先得使用Key的密码,生成3DES密钥;再使用3DES密钥解密Token Cookie。也可以使用公共/私有密钥来签名或验证LTPA Cookie。

2.1 WebSphere LTPA 生成原理

首先,这个 cookie 由以下部分组成,以%进行分隔:
  • 用户信息,格式为u:user\:<RealmName>/<UserDN>,如:u:user\:VGOLiveRealm/CN=squallzhong,O=VGOLive Technology
  • 过期时间
  • 签名信息,如:
    u:user\:VGOLiveRealm/CN=squallzhong,O=VGOLive Technology%1301558320666%Cy2CAeru5kEElGj0hrvYsKW2ZVsvvcu6Un573aeX55OO4G3EMYWc0e/ZbqDp1z7MS+dLzniuUH4sYWCMpnKdm7ZGabwmV+WcraBl+y+yzwcl722gHVMOnDZAW7U3jEay9Tk2yG4yXkMWU+617xndpVxke2jtS5wIyVVM3q7UDPw=

2.2 异构系统所需信息

从WebSphere系统中导出ltpa的key文件,使用文本文件打开,如:

com.ibm.websphere.CreationDate=Thu Mar 31 11\:08\:09 GMT+08\:00 2011 com.ibm.websphere.ltpa.version=1.0 com.ibm.websphere.ltpa.3DESKey=7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g\= com.ibm.websphere.CreationHost=wasserver com.ibm.websphere.ltpa.PrivateKey=N3bnOE1IbiXNsHXxxemC98iiCnmtw3JUuQvdFjEyh9r2gu+FlQRmG8xp5RBltqc6raI4EgYFhTr+t5/tmRQrFqfNKgvujeJZODeCspohi1V4C0qit7DOoqD9xOOn9Rzdb4PIuJM3ekwuBiZZYTYu7q0TANDygc7VbmwoD3xMPCk5svyvFJ/VshPyg5f7Q+VNM8dlIitU4gK9Qp8VZEqjGoXsYYzYYTQgnwAVtR2GfZtXKlf24EPXSkgUz9j8FwTvcylcKwjS22d6eVjciyAzInnxPqxE2iMRPEFDatHZFox3flsqBswmeDQrAGv8zIiffgP1DLKdjozUyAG+50v97xx7u1RtIrB4B01ik8DuLhw\= com.ibm.websphere.ltpa.Realm=VGOLiveRealm com.ibm.websphere.ltpa.PublicKey=AM04If2+ElGSyVRF0ZEesgvC59vGw8gSIfptjfoXj8iz4C7Ip/KVAu2PDkpQi3LUN/FgVF696tmsegBThks9rmMMHzOix/vGP2721dQZKbD7plOLdWtiY2AYZChsBVkOF26DfiWJ6euxD+a+KNcrfDnu2AXRC/tKncIUJV4LbeJdAQAB
  • 所使用的DNS域,如:vgolive.com
  • 过期时间(分钟),如:30
  • LTPA 3DESKey 密钥及密钥的保护密码
  • Base DN,如:O=VGOLive Technology或DC=vgolive,DC=com
注:
  • 在3DESKey中的反斜杠只是为了在JAVA中可解释等于号,所以正确的3DESKey为 7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g=
  • 用户名可以通过Base Dn验证字进行拼接,如异构系统中用户名为SquallZhong,相应在Domino中的DN就是CN=SquallZhong,O=DigiWin。此类情况仅限于LDAP中的CN与异构系统中的用户名一致。如果不一致,需要异构系统做用户对应

2.3 实现

Base64解码/编码所需Jar包: apache-commons-codec-1.3.jar以上
SHA-1的校验使用 java.security.MessageDigest

2.3.1 解析

以下代码为解析从WebSphere或Domino发送过来的LTPAToken Cookie以Java为例:

 
01
02         // LTPA 3DES 密钥
03         String ltpa3DESKey = "7dH4i81YepbVe+gF9XVUzE4C1Ca5g6A4Q69OFobJV9g=";
04         // LTPA 密钥密码
05         String ltpaPassword = "Passw0rd";
06         try  {
07             // 获得加密key
08             byte[] secretKey = getSecretKey(ltpa3DESKey, ltpaPassword);
09             // 使用加密key解密ltpa Cookie
10             String ltpaPlaintext = new  String(decryptLtpaToken(tokenCipher,
11                     secretKey));
12             displayTokenData(ltpaPlaintext);
13         } catch  (Exception e) {
14             System.out.println("Caught inner: "  + e);
15         }
16
17     //获得安全Key
18     private  static  byte[] getSecretKey(String ltpa3DESKey, String password)
19             throws  Exception {
20         // 使用SHA获得key密码的hash值
21         MessageDigest md = MessageDigest.getInstance("SHA");
22         md.update(password.getBytes());
23         byte[] hash3DES = new  byte[24];
24         System.arraycopy(md.digest(), 0, hash3DES, 0, 20);
25         // 使用0替换后4个字节
26         Arrays.fill(hash3DES, 20, 24, (byte) 0);
27         // BASE64解码 ltpa3DESKey
28         byte[] decode3DES = Base64.decodeBase64(ltpa3DESKey.getBytes());
29         // 使用key密码hash值解密已Base64解码的ltpa3DESKey
30         return  decrypt(decode3DES, hash3DES);
31     }
32     //解密LtpaToken
33     public  static  byte[] decryptLtpaToken(String encryptedLtpaToken, byte[] key)
34             throws  Exception {
35         // Base64解码LTPAToken
36         final  byte[] ltpaByteArray = Base64.decodeBase64(encryptedLtpaToken
37                 .getBytes());
38         // 使用key解密已Base64解码的LTPAToken
39         return  decrypt(ltpaByteArray, key);
40     }
41     // DESede/ECB/PKC5Padding解方法
42     public  static  byte[] decrypt(byte[] ciphertext, byte[] key)
43             throws  Exception {
44         final  Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
45         final  KeySpec keySpec = new  DESedeKeySpec(key);
46         final  Key secretKey = SecretKeyFactory.getInstance("TripleDES")
47                 .generateSecret(keySpec);
48         cipher.init(Cipher.DECRYPT_MODE, secretKey);
49         return  cipher.doFinal(ciphertext);
50     }
51

解析出来的LTPAToken信息以%分隔


2.3.2 生成

Websphere LTPA生成时的签名信息是由用户DN和一些用户其他信息组成字符串,使用私有密钥进行签名,由于不清楚这些信息的组成,故无法产生正确的LTPA。


3. Domino部分

本部分的描述仅适用于单一的Domino平台应用与构异系统实现单点登录。


3.1 Domino LTPA Cookie 生成原理

在与 Domino 做 SSO 的时候,会使用 LTPA Token的认证方式,本文描述它的生成原理,通过它我们可以自己编码生成身份认证的 cookie,实现 SSO。
首先,这个 cookie 由以下部分组成:
  • LTPA token 版本(4字节)
  • 创建时间(8字节)
  • 过期时间(8字节)
  • 用户名(可变长度)
  • Domino LTPA 密钥(20字节)
接下来分别说明各部分的具体内容:
  • LTPA token 版本目前 Domino 只有一种值:0x0001
  • 创建时间为以十六进制方式表示的Unix time,例如:2009-04-09 13:52:42 (GMT +8) = 1239256362 = 49DD8D2A。
  • 过期时间=创建时间 + SSO 配置文档的过期时间(LTPA_TokenExpiration域)
  • 用户名为 Names 中用户文档的FullName域值;如:Squall Zhong/Digiwin
  • Domino LTPA 密钥通过 Base64编码后,保存在 SSO 配置文档的LTPA_DominoSecret域中

当然不能将密钥直接发送给浏览器,所以将上述部分合并起来(如上图),计算 SHA-1 校验和。
然后用 SHA-1 校验和替换掉 Domino LTPA 密钥,最后再将内容通过 Base64 编码,形成最终的 cookie 发送给浏览器(如上图)。这样如果 cookie 中的任何内容被修改,校验和就不对了,达到了防篡改的效果。所以最终LTPA Cookie所得到的值为以下公式组成:
SHA-1=LTPA版本号+创建时间+过期时间+用户名+Domino LTPA 密钥
LTPA Cookie= Base64(LTPA版本号+创建时间+过期时间+用户名+SHA-1)

3.2 异构系统所需信息

  • Domino 所使用的DNS域,如:vgolive.com
  • 过期时间(分钟),如:30
  • Domino LTPA 密钥
  • Domino验证字名称,如:/O=VGOLive Technology
注:用户名可以通过Domino验证字进行拼接,如异构系统中用户名为SquallZhong,相应在Domino中的DN就是CN=SquallZhong/O=VGOLive Technology。此类情况仅限于Domino中的CN与异构系统中的用户名一致。如果不一致,需要异构系统做用户对应

3.3 实现

Base64解码/编码所需Jar包: apache-commons-codec-1.3.jar以上

SHA-1的校验使用 java.security.MessageDigest
转换字符集使用: Cp850

3.3.1 解析

 
01     import  org.apache.commons.codec.binary.Base64;
02…...
03final  String CHARSET = "Cp850";
04byte[] dominoSecret = Base64.decodeBase64(ltpaDominoSecret.getBytes());
05byte[] ltpa = Base64.decodeBase64(ltpaToken.getBytes());
06ByteArrayInputStream stream = new  ByteArrayInputStream(ltpa);
07int  usernameLength = ltpa.length – 40;
08byte  header[] = new  byte[4];
09byte  creation[] = new  byte[8];
10byte  expires[] = new  byte[8];
11byte  username[] = new  byte[usernameLength];
12byte[] sha = new  byte[20];
13// 读取LTPAToken版本号
14stream.read(header, 0, 4);
15if  (header[0] != 0  || header[1] != 1  || header[2] != 2|| header[3] != 3)
16         throw  new  IllegalArgumentException("Invalid ltpaToken format");
17     // 读取开始时间
18    stream.read(creation, 0, 8);
19    // 读取到期时间
20    stream.read(expires, 0, 8);
21    // 读取Domino用户DN
22    stream.read(username, 0, usernameLength);
23    // 读取SHA校验和
24    stream.read(sha, 0, 20);
25     // 转换用户名
26    char  characters[] = new  char[usernameLength];
27    try  {
28         InputStreamReader isr = new  InputStreamReader(
29             new  ByteArrayInputStream(username),
30             CHARSET);
31         isr.read(characters);
32     } catch  (Exception e) {
33     }
34     // 获得Domino用户DN
35     String dn = new  String(characters);
36     // 获得创建时间
37    Date creationDate = new  Date(
38         Long.parseLong(new  String(creation), 16) * 1000);
39     // 获得到期时间
40    Date expiresDate = new  Date(
41         Long.parseLong(new  String(expires), 16) * 1000);
42…...
43// 创建LTPA Token
44     ByteArrayOutputStream ostream = new  ByteArrayOutputStream();
45     try  {
46         // LTPA Token版本号
47         ostream.write(header);
48         // 创建时间
49         ostream.write(creation);
50         // 过期时间
51         ostream.write(expires);
52         // Domino用户DN,如CN=SquallZhong/O=DigiWin
53         ostream.write(username);
54         // Domino LTPA 密钥
55         ostream.write(dominoSecret);
56         ostream.close();
57     } catch  (IOException e) {
58         throw  new  RuntimeException(e);
59     }
60     // 进行 SHA-1 校验和
61     MessageDigest md;
62     try  {
63         md = MessageDigest.getInstance("SHA-1");
64         md.reset();
65     } catch  (NoSuchAlgorithmException e) {
66         throw  new  RuntimeException(e);
67     }
68     byte[] digest = md.digest(ostream.toByteArray());
69     // 完成 SHA-1 校验和,digest长度为20
70     boolean  valid = MessageDigest.isEqual(digest, sha);

3.3.2 生成

 
01     /**
02      * 为指定用户创建有效的LTPA Token.创建时间为<tt>now</tt>.
03      *
04      * @param username
05      *            - 用户名,注:使用用户全称,如:CN=SquallZhong/O=VGOLive Technology
06      * @param creationTime
07      *            - 创建时间
08      * @param durationMinutes
09      *            - 到期时间,单位:分钟
10@param ltpaSecretStr
11      *            - Domino Ltpa 加密字符串
12      * @return - 返回已Base64编码的Ltpa Cookie.
13      * @throws NoSuchAlgorithmException
14      * @throws Base64DecodeException
15      */
16     public  static  String createLtpaToken(String username,
17             GregorianCalendar creationTime, int  durationMinutes,
18             String ltpaSecretStr) throws  NoSuchAlgorithmException {
19         // Base64解码ltpaSecretStr
20         byte[] ltpaSecret = Base64.decodeBase64(ltpaSecretStr.getBytes());
21         // 用户名字节数组
22         byte[] usernameArray = username.getBytes();
23         byte[] workingBuffer = new  byte[preUserDataLength
24                 + usernameArray.length + ltpaSecret.length];
25 
26         // 设置ltpaToken版本至workingBuffer
27         System.arraycopy(ltpaTokenVersion, 0, workingBuffer, 0,
28                 ltpaTokenVersion.length);
29         // 获得过期时间,过期时间=当前时间+到期时间(分钟)
30         GregorianCalendar expirationDate = (GregorianCalendar) creationTime
31                 .clone();
32         expirationDate.add(Calendar.MINUTE, durationMinutes);
33 
34         // 转换创建时间至16进制字符串
35         String hex = dateStringFiller
36                 + Integer.toHexString(
37                         (int) (creationTime.getTimeInMillis() / 1000))
38                         .toUpperCase();
39         // 设置创建时间至workingBuffer
40         System.arraycopy(hex.getBytes(), hex.getBytes().length
41                 - dateStringLength, workingBuffer, creationDatePosition,
42                 dateStringLength);
43 
44         // 转换过期时间至16进制字符串
45         hex = dateStringFiller
46                 + Integer.toHexString(
47                         (int) (expirationDate.getTimeInMillis() / 1000))
48                         .toUpperCase();
49         // 设置过期时间至workingBuffer
50         System.arraycopy(hex.getBytes(), hex.getBytes().length
51                 - dateStringLength, workingBuffer, expirationDatePosition,
52                 dateStringLength);
53 
54         // 设置用户全称至workingBuffer
55         System.arraycopy(usernameArray, 0, workingBuffer, preUserDataLength,
56                 usernameArray.length);
57 
58         // 设置已Base64解码ltpaSecret至workingBuffer
59         System.arraycopy(ltpaSecret, 0, workingBuffer, preUserDataLength
60                 + usernameArray.length, ltpaSecret.length);
61         // 创建Hash字符串
62         byte[] hash = createHash(workingBuffer);
63 
64         // ltpaToken版本+开始时间(16进制)+到期时间(16进制)+用户全名+SHA-1(ltpaToken版本+开始时间(16进制)+到期时间(16进制)+用户全名)
65         byte[] outputBuffer = new  byte[preUserDataLength + usernameArray.length
66                 + hashLength];
67         System.arraycopy(workingBuffer, 0, outputBuffer, 0, preUserDataLength
68                 + usernameArray.length);
69         System.arraycopy(hash, 0, outputBuffer, preUserDataLength
70                 + usernameArray.length, hashLength);
71         // 返回已Base64编码的outputBuffer
72         return  new  String(Base64.encodeBase64(outputBuffer));
73     }
74…...

4. 通过F5 BIG-IP创建Domino LTPAToken

F5 iRule代码如下:

when RULE_INIT {

 
01set  cookie_name "LtpaToken"            # 不更改
02  set  ltpa_version "\x00\x01\x02\x03"    # 不更改
03  set  ltpa_secret "b64encodedsecretkey"  # 从Domino SSO文档获得ltpa密钥
04  set  ltpa_timeout "1800"                # 从Domino SSO文档中获得过期时间,单位:秒
05}
06 
07when HTTP_REQUEST {
08  #
09  # Do your usual F5 HTTP authentication here
10  #
11  # Initial values
12  set  creation_time_temp [clock seconds]
13  set  creation_time [format  %X $creation_time_temp]
14  set  expr_time_temp [expr { $creation_time_temp +  $::ltpa_timeout}]
15  set  expr_time [format  %X $expr_time_temp]
16  set  username [HTTP::username]
17  set  ltpa_secret_decode [b64decode $::ltpa_secret]
18  # First part of token
19  set  cookie_data_raw {}
20  append cookie_data_raw $::ltpa_version
21  append cookie_data_raw $creation_time
22  append cookie_data_raw $expr_time
23  append cookie_data_raw $username
24  append cookie_data_raw $ltpa_secret_decode
25  # SHA1 of first part of token
26  set  sha_cookie_raw [sha1 $cookie_data_raw]
27  # Final not yet encoded token
28  set  ltpa_token_raw {}
29  append ltpa_token_raw $::ltpa_version
30  append ltpa_token_raw $creation_time
31  append ltpa_token_raw $expr_time
32  append ltpa_token_raw $username
33  append ltpa_token_raw $sha_cookie_raw
34  # Final Base64 encoded token
35  set  ltpa_token_final [b64encode $ltpa_token_raw]
36  # Insert the cookie
37  HTTP::cookie insert name $::cookie_name value $ltpa_token_final
38  }
39  # Remove Authorization HTTP header to avoid using basic authentication
40  if  { [HTTP::header exists "Authorization"] } {
41  HTTP::header remove "Authorization"
42  }
43}

 

 

 

来源连接 http://www.cnblogs.com/hannover/archive/2011/05/29/2061798.html



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


ITeye推荐



Netty系列之Netty高性能之道

$
0
0

1. 背景

1.1. 惊人的性能数据

最近一个圈内朋友通过私信告诉我,通过使用Netty4 + Thrift压缩二进制编解码技术,他们实现了10W TPS(1K的复杂POJO对象)的跨节点远程服务调用。相比于传统基于Java序列化+BIO(同步阻塞IO)的通信框架,性能提升了8倍多。

事实上,我对这个数据并不感到惊讶,根据我5年多的NIO编程经验,通过选择合适的NIO框架,加上高性能的压缩二进制编解码技术,精心的设计Reactor线程模型,达到上述性能指标是完全有可能的。

下面我们就一起来看下Netty是如何支持10W TPS的跨节点远程服务调用的,在正式开始讲解之前,我们先简单介绍下Netty。

Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。1.2. Netty基础入门

作为当前最流行的NIO框架,Netty在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,一些业界著名的开源组件也基于Netty的NIO框架构建。

2. Netty高性能之道

2.1. RPC调用的性能模型分析

2.1.1. 传统RPC调用性能差的三宗罪

网络传输方式问题:传统的RPC框架或者基于RMI等方式的远程服务(过程)调用采用了同步阻塞IO,当客户端的并发压力或者网络时延增大之后,同步阻塞IO会由于频繁的wait导致IO线程经常性的阻塞,由于线程无法高效的工作,IO处理能力自然下降。

下面,我们通过BIO通信模型图看下BIO通信的弊端:


图2-1 BIO通信模型图

采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,接收到客户端连接之后为客户端连接创建一个新的线程处理请求消息,处理完成之后,返回应答消息给客户端,线程销毁,这就是典型的一请求一应答模型。该架构最大的问题就是不具备弹性伸缩能力,当并发访问量增加后,服务端的线程个数和并发访问数成线性正比,由于线程是JAVA虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能急剧下降,随着并发量的继续增加,可能会发生句柄溢出、线程堆栈溢出等问题,并导致服务器最终宕机。

序列化方式问题:Java序列化存在如下几个典型问题:

1) Java序列化机制是Java内部的一种对象编解码技术,无法跨语言使用;例如对于异构系统之间的对接,Java序列化后的码流需要能够通过其它语言反序列化成原始对象(副本),目前很难支持;

2) 相比于其它开源的序列化框架,Java序列化后的码流太大,无论是网络传输还是持久化到磁盘,都会导致额外的资源占用;

3) 序列化性能差(CPU资源占用高)。

线程模型问题:由于采用同步阻塞IO,这会导致每个TCP连接都占用1个线程,由于线程资源是JVM虚拟机非常宝贵的资源,当IO读写阻塞导致线程无法及时释放时,会导致系统性能急剧下降,严重的甚至会导致虚拟机无法创建新的线程。

2.1.2. 高性能的三个主题

1) 传输:用什么样的通道将数据发送给对方,BIO、NIO或者AIO,IO模型在很大程度上决定了框架的性能。

2) 协议:采用什么样的通信协议,HTTP或者内部私有协议。协议的选择不同,性能模型也不同。相比于公有协议,内部私有协议的性能通常可以被设计的更优。

3) 线程:数据报如何读取?读取之后的编解码在哪个线程进行,编解码后的消息如何派发,Reactor线程模型的不同,对性能的影响也非常大。


图2-2 RPC调用性能三要素

2.2. Netty高性能之道

2.2.1. 异步非阻塞通信

在IO编程过程中,当需要同时处理多个客户端接入请求时,可以利用多线程或者IO多路复用技术进行处理。IO多路复用技术通过把多个IO的阻塞复用到同一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。与传统的多线程/多进程模型比,I/O多路复用的最大优势是系统开销小,系统不需要创建新的额外进程或者线程,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省了系统资源。

JDK1.4提供了对非阻塞IO(NIO)的支持,JDK1.5_update10版本使用epoll替代了传统的select/poll,极大的提升了NIO通信的性能。

JDK NIO通信模型如下所示:

图2-3 NIO的多路复用模型图

与Socket类和ServerSocket类相对应,NIO也提供了SocketChannel和ServerSocketChannel两种不同的套接字通道实现。这两种新增的通道都支持阻塞和非阻塞两种模式。阻塞模式使用非常简单,但是性能和可靠性都不好,非阻塞模式正好相反。开发人员一般可以根据自己的需要来选择合适的模式,一般来说,低负载、低并发的应用程序可以选择同步阻塞IO以降低编程复杂度。但是对于高负载、高并发的网络应用,需要使用NIO的非阻塞模式进行开发。

Netty架构按照Reactor模式设计和实现,它的服务端通信序列图如下:


图2-3 NIO服务端通信序列图

客户端通信序列图如下:


图2-4 NIO客户端通信序列图

Netty的IO线程NioEventLoop由于聚合了多路复用器Selector,可以同时并发处理成百上千个客户端Channel,由于读写操作都是非阻塞的,这就可以充分提升IO线程的运行效率,避免由于频繁IO阻塞导致的线程挂起。另外,由于Netty采用了异步通信模式,一个IO线程可以并发处理N个客户端连接和读写操作,这从根本上解决了传统同步阻塞IO一连接一线程模型,架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。

2.2.2. 零拷贝

很多用户都听说过Netty具有“零拷贝”功能,但是具体体现在哪里又说不清楚,本小节就详细对Netty的“零拷贝”功能进行讲解。

Netty的“零拷贝”主要体现在如下三个方面:

1) Netty的接收和发送ByteBuffer采用DIRECT BUFFERS,使用堆外直接内存进行Socket读写,不需要进行字节缓冲区的二次拷贝。如果使用传统的堆内存(HEAP BUFFERS)进行Socket读写,JVM会将堆内存Buffer拷贝一份到直接内存中,然后才写入Socket中。相比于堆外直接内存,消息在发送过程中多了一次缓冲区的内存拷贝。

2) Netty提供了组合Buffer对象,可以聚合多个ByteBuffer对象,用户可以像操作一个Buffer那样方便的对组合Buffer进行操作,避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。

3) Netty的文件传输采用了transferTo方法,它可以直接将文件缓冲区的数据发送到目标Channel,避免了传统通过循环write方式导致的内存拷贝问题。

下面,我们对上述三种“零拷贝”进行说明,先看Netty 接收Buffer的创建:


图2-5 异步消息读取“零拷贝”

每循环读取一次消息,就通过ByteBufAllocator的ioBuffer方法获取ByteBuf对象,下面继续看它的接口定义:


图2-6 ByteBufAllocator 通过ioBuffer分配堆外内存

当进行Socket IO读写的时候,为了避免从堆内存拷贝一份副本到直接内存,Netty的ByteBuf分配器直接创建非堆内存避免缓冲区的二次拷贝,通过“零拷贝”来提升读写性能。

下面我们继续看第二种“零拷贝”的实现CompositeByteBuf,它对外将多个ByteBuf封装成一个ByteBuf,对外提供统一封装后的ByteBuf接口,它的类定义如下:


图2-7 CompositeByteBuf类继承关系

通过继承关系我们可以看出CompositeByteBuf实际就是个ByteBuf的包装器,它将多个ByteBuf组合成一个集合,然后对外提供统一的ByteBuf接口,相关定义如下:


图2-8 CompositeByteBuf类定义

添加ByteBuf,不需要做内存拷贝,相关代码如下:


图2-9 新增ByteBuf的“零拷贝”

最后,我们看下文件传输的“零拷贝”:


图2-10 文件传输“零拷贝”

Netty文件传输DefaultFileRegion通过transferTo方法将文件发送到目标Channel中,下面重点看FileChannel的transferTo方法,它的API DOC说明如下:


图2-11 文件传输 “零拷贝”

对于很多操作系统它直接将文件缓冲区的内容发送到目标Channel中,而不需要通过拷贝的方式,这是一种更加高效的传输方式,它实现了文件传输的“零拷贝”。

2.2.3. 内存池

随着JVM虚拟机和JIT即时编译技术的发展,对象的分配和回收是个非常轻量级的工作。但是对于缓冲区Buffer,情况却稍有不同,特别是对于堆外直接内存的分配和回收,是一件耗时的操作。为了尽量重用缓冲区,Netty提供了基于内存池的缓冲区重用机制。下面我们一起看下Netty ByteBuf的实现:


图2-12 内存池ByteBuf

Netty提供了多种内存管理策略,通过在启动辅助类中配置相关参数,可以实现差异化的定制。

下面通过性能测试,我们看下基于内存池循环利用的ByteBuf和普通ByteBuf的性能差异。

用例一,使用内存池分配器创建直接内存缓冲区:


图2-13 基于内存池的非堆内存缓冲区测试用例

用例二,使用非堆内存分配器创建的直接内存缓冲区:


图2-14 基于非内存池创建的非堆内存缓冲区测试用例

各执行300万次,性能对比结果如下所示:


图2-15 内存池和非内存池缓冲区写入性能对比

性能测试表明,采用内存池的ByteBuf相比于朝生夕灭的ByteBuf,性能高23倍左右(性能数据与使用场景强相关)。

下面我们一起简单分析下Netty内存池的内存分配:


图2-16 AbstractByteBufAllocator的缓冲区分配

继续看newDirectBuffer方法,我们发现它是一个抽象方法,由AbstractByteBufAllocator的子类负责具体实现,代码如下:

图2-17 newDirectBuffer的不同实现

代码跳转到PooledByteBufAllocator的newDirectBuffer方法,从Cache中获取内存区域PoolArena,调用它的allocate方法进行内存分配:


图2-18 PooledByteBufAllocator的内存分配

PoolArena的allocate方法如下:


图2-18 PoolArena的缓冲区分配

我们重点分析newByteBuf的实现,它同样是个抽象方法,由子类DirectArena和HeapArena来实现不同类型的缓冲区分配,由于测试用例使用的是堆外内存,


图2-19 PoolArena的newByteBuf抽象方法

因此重点分析DirectArena的实现:如果没有开启使用sun的unsafe,则


图2-20 DirectArena的newByteBuf方法实现

执行PooledDirectByteBuf的newInstance方法,代码如下:


图2-21 PooledDirectByteBuf的newInstance方法实现

通过RECYCLER的get方法循环使用ByteBuf对象,如果是非内存池实现,则直接创建一个新的ByteBuf对象。从缓冲池中获取ByteBuf之后,调用AbstractReferenceCountedByteBuf的setRefCnt方法设置引用计数器,用于对象的引用计数和内存回收(类似JVM垃圾回收机制)。

2.2.4. 高效的Reactor线程模型

常用的Reactor线程模型有三种,分别如下:

1) Reactor单线程模型;

2) Reactor多线程模型;

3) 主从Reactor多线程模型

Reactor单线程模型,指的是所有的IO操作都在同一个NIO线程上面完成,NIO线程的职责如下:

1) 作为NIO服务端,接收客户端的TCP连接;

2) 作为NIO客户端,向服务端发起TCP连接;

3) 读取通信对端的请求或者应答消息;

4) 向通信对端发送消息请求或者应答消息。

Reactor单线程模型示意图如下所示:

图2-22 Reactor单线程模型

由于Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关的操作。从架构层面看,一个NIO线程确实可以完成其承担的职责。例如,通过Acceptor接收客户端的TCP连接请求消息,链路建立成功之后,通过Dispatch将对应的ByteBuffer派发到指定的Handler上进行消息解码。用户Handler可以通过NIO线程将消息发送给客户端。

对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用却不合适,主要原因如下:

1) 一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;

2) 当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,NIO线程会成为系统的性能瓶颈;

3) 可靠性问题:一旦NIO线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。

为了解决这些问题,演进出了Reactor多线程模型,下面我们一起学习下Reactor多线程模型。

Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理IO操作,它的原理图如下:


图2-23 Reactor多线程模型

Reactor多线程模型的特点:

1) 有专门一个NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;

2) 网络IO操作-读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;

3) 1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。

在绝大多数场景下,Reactor多线程模型都可以满足性能需求;但是,在极特殊应用场景中,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。例如百万客户端并发连接,或者服务端需要对客户端的握手消息进行安全认证,认证本身非常损耗性能。在这类场景下,单独一个Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型-主从Reactor多线程模型。

主从Reactor线程模型的特点是:服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工作。Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作。

它的线程模型如下图所示:


图2-24 Reactor主从多线程模型

利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题。因此,在Netty的官方demo中,推荐使用该线程模型。

事实上,Netty的线程模型并非固定不变,通过在启动辅助类中创建不同的EventLoopGroup实例并通过适当的参数配置,就可以支持上述三种Reactor线程模型。正是因为Netty 对Reactor线程模型的支持提供了灵活的定制能力,所以可以满足不同业务场景的性能诉求。

2.2.5. 无锁化的串行设计理念

在大多数场景下,并行多线程处理可以提升系统的并发性能。但是,如果对于共享资源的并发访问处理不当,会带来严重的锁竞争,这最终会导致性能的下降。为了尽可能的避免锁竞争带来的性能损耗,可以通过串行化设计,即消息的处理尽可能在同一个线程内完成,期间不进行线程切换,这样就避免了多线程竞争和同步锁。

为了尽可能提升性能,Netty采用了串行无锁化设计,在IO线程内部进行串行操作,避免多线程竞争导致的性能下降。表面上看,串行化设计似乎CPU利用率不高,并发程度不够。但是,通过调整NIO线程池的线程参数,可以同时启动多个串行化的线程并行运行,这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。

Netty的串行化设计工作原理图如下:


图2-25 Netty串行化工作原理图

Netty的NioEventLoop读取到消息之后,直接调用ChannelPipeline的fireChannelRead(Object msg),只要用户不主动切换线程,一直会由NioEventLoop调用到用户的Handler,期间不进行线程切换,这种串行化处理方式避免了多线程操作导致的锁的竞争,从性能角度看是最优的。

2.2.6. 高效的并发编程

Netty的高效并发编程主要体现在如下几点:

1) volatile的大量、正确使用;

2) CAS和原子类的广泛使用;

3) 线程安全容器的使用;

4) 通过读写锁提升并发性能。

如果大家想了解Netty高效并发编程的细节,可以阅读之前我在微博分享的《多线程并发编程在 Netty 中的应用分析》,在这篇文章中对Netty的多线程技巧和应用进行了详细的介绍和分析。

2.2.7. 高性能的序列化框架

影响序列化性能的关键因素总结如下:

1) 序列化后的码流大小(网络带宽的占用);

2) 序列化&反序列化的性能(CPU资源占用);

3) 是否支持跨语言(异构系统的对接和开发语言切换)。

Netty默认提供了对Google Protobuf的支持,通过扩展Netty的编解码接口,用户可以实现其它的高性能序列化框架,例如Thrift的压缩二进制编解码框架。

下面我们一起看下不同序列化&反序列化框架序列化后的字节数组对比:


图2-26 各序列化框架序列化码流大小对比

从上图可以看出,Protobuf序列化后的码流只有Java序列化的1/4左右。正是由于Java原生序列化性能表现太差,才催生出了各种高性能的开源序列化技术和框架(性能差只是其中的一个原因,还有跨语言、IDL定义等其它因素)。

2.2.8. 灵活的TCP参数配置能力

合理设置TCP参数在某些场景下对于性能的提升可以起到显著的效果,例如SO_RCVBUF和SO_SNDBUF。如果设置不当,对性能的影响是非常大的。下面我们总结下对性能影响比较大的几个配置项:

1) SO_RCVBUF和SO_SNDBUF:通常建议值为128K或者256K;

2) SO_TCPNODELAY:NAGLE算法通过将缓冲区内的小封包自动相连,组成较大的封包,阻止大量小封包的发送阻塞网络,从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法;

3) 软中断:如果Linux内核版本支持RPS(2.6.35以上版本),开启RPS后可以实现软中断,提升网络吞吐量。RPS根据数据包的源地址,目的地址以及目的和源端口,计算出一个hash值,然后根据这个hash值来选择软中断运行的cpu,从上层来看,也就是说将每个连接和cpu绑定,并通过这个hash值,来均衡软中断在多个cpu上,提升网络并行处理性能。

Netty在启动辅助类中可以灵活的配置TCP参数,满足不同的用户场景。相关配置接口定义如下:


图2-27 Netty的TCP参数配置定义

2.3. 总结

通过对Netty的架构和性能模型进行分析,我们发现Netty架构的高性能是被精心设计和实现的,得益于高质量的架构和代码,Netty支持10W TPS的跨节点服务调用并不是件十分困难的事情。

峰哥地址:http://www.infoq.com/cn/articles/netty-high-performance#theCommentsSection;

作者:working_brain 发表于2014-6-10 20:17:42 原文链接
阅读:1 评论:0 查看评论

Morphia的使用

$
0
0

Morphia配合mongoDB就如同Hibernate配合关系型数据库.所以可以认为Morphia在MongoDB的基础上面调用mongoDB的语法.

在java中直接调用Morphia的api会显得比较简单快捷.


1.加入mongoDB和Morphia的Jar包.

//创建连接源
Mongo mongo = null;
		try {
			mongo = new Mongo();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		Morphia morphia = new Morphia();
		Datastore ds = morphia.createDatastore(mongo, "test");

//查找
		for(User user:ds.find(User.class, "password", "admin")){
			System.out.println(user);
		}
		System.out.println(ds.find(User.class, "password", "admin").get());

		//插入

		User user1 = new User("00018", "hello1", "hello1");
		User user2 = new User("00019", "hello2", "hello2");
		List<User> list = new ArrayList<>();
		list.add(user1);
		list.add(user2);
		ds.save(list);

	// 修改
		Query<User> query =
				ds.createQuery(User.class).field("uid").equal("00013");
		UpdateOperations<User> uo =
				ds.createUpdateOperations(User.class).set("password","IDONTKONW").set("userName", "IDONTKONW");
		ds.update(query, uo);


		/**
		 * 删除
		 * 
		 */
		// ds.delete(User.class, "00018");
		Query<User> query = ds.createQuery(User.class).field("userName").contains("t");
		ds.delete(query);


作者:liu00614 发表于2014-6-10 19:56:28 原文链接
阅读:41 评论:0 查看评论

SQL常见的可优化点

$
0
0

   # ###################################################

   # 索引相关

   # ###################################################

    1. 查询(或更新,删除,可以转换为查询)没有用到索引

   这是最基础的步骤,需要对sql执行explain查看执行计划中是否用到了索引,需要重点关注type=ALL, key=NULL的字段。

    2.  在索引字段上施加函数

   to_char(gmt_created, ‘mmdd’) = ’0101′

   正确的写法

   gmt_created between to_date(“20090101″, “yyyymmdd”) and to_date(“20090102″, “yyyymmdd”)

   

    3. 在索引字段上使用全模糊

   member_id like ‘%alibab%’

   B树无法解决此类问题,可以考虑搜索引擎。

   但是member_id like ‘alibab%’可以用到索引。

   其实,对任何一个字段使用 like ‘%xxxx%’都是一种不规范的做法,需要能检查到这种错误用法。

   

    4.  多列字段的索引,没有用到前导索引

   索引:(memeber_id, group_id)

   where group_id=9234,实际上,这个条件是没有办法用到上面的索引的。这是一个很常见的误用。要理解为什么不能用到这个索引,需要理解mysql如何构造多列索引的。

   索引是一棵B树,问题是,对于多列索引,mysql将索引字段按照索引建立的顺序进行拼装,组成一个新的字符串,这个字符串被用来做为构建B树的键。所以,在查询条件里,如果没有用到前导列,就没办法访问多列索引的B树。

   应该建立索引:(group_id, member_id)

   

    5. 访问到了索引之外的字段

   索引(member_id, subject)

   select subject from offer where member_id=234

   在member_id=234记录数很多的情况下,会优于

   select subject, gmt_created from offer where member_id=234

   原因是第二条sql会根据索引查找到的rowid访问表里的记录。第一条sql使用索引范围扫描就可以得到结果。

   如果某个sql执行次数很多,但是读取的字段没有被索引覆盖,那么,可能需要建立覆盖性索引。

   

    6.  计数count(id)有时比count(*)慢

   count(id) === count(1) where id is not null

   如果没有(id)索引,那么会用全表扫描,而count(*)会使用最优的索引进行用索引快速全扫描

   计数统一使用count(*)

   

    7.  正确使用stop机制

   判断member_id在offer表中是否存在记录:

   select count(*) from offer where member_id=234 limit 1

   优于

   select count(*) from offer where member_id=234

   原因是第一条sql会在得到第一条符合条件的记录后停止。

   # ###################################################

   # 高效分页

   # ###################################################

    1.  高效的分页

   使用join技术,利用索引查找到符合条件的id,构造成临时表,用这个小的临时表与原表做join

   select *

   from

   (

   select t.*, rownum AS rn

   from

   (select * from blog.blog_article

   where domain_id=1

   and draft=0

   order by domain_id, draft, gmt_created desc) t

   where rownum >= 2

   ) a

   where a.rn <= 3

   应该改写成

   select blog_article.*

   from

   (

   select rid, rownum as rn

   from

   (

   select rowid as id from blog.blog_article

   where domain_id=1

   and draft=0

   order by domain_id, draft, gmt_created desc

   ) t

   where rownum >= 2

   ) a, blog_article

   where a.rn >= 3

   and a.rid = blog_article.rowid

    2. order by没有用到索引

   有索引(a, b,c )

   混合排序规则

   ORDER BY a ASC, b DESC, c DESC /* mixed sort direction */

   缺失了前导列

   WHERE g = const ORDER BY b, c /* a prefix is missing */

   缺失了中间列

   WHERE a = const ORDER BY c /* b is missing */

   使用了不在索引中的列进行排序

   WHERE a = const ORDER BY a, d /* d is not part of index */

   # ###################################################

   # 高效地利用primary key

   # ###################################################

    随机查询

   一个错误的做法:

   select * from title where kind_id=1 order by rand() limit 1;

   create index k on title(kind_id);

   这个sql执行过程中需要全表扫描,并且将数据保存到临时表,这是一个非常耗时的操作。

   改进的做法,利用偏移量。

   select round(rand() * count(*)) from title where kind_id=1;

   select * from title where kind_id=1 limit 1 offset $random;

   create index k on title(kind_id);

   相比上面的做法,这种写法能够利用到kind_id上的索引,减少了需要扫描的数据块。但是,如果offset非常大,那么需要扫描的数据块也非常大,极端情况是扫描索引k的所有数据块。

   最优的做法,利用主键进行范围查找

   select round(rand() * count(*)) from title where kind_id=1;

   select * from title where kind_id = and id > $random limit 1;

   这个sql利用primary key进行范围查询,完全走索引,并且只读取一条记录,速度非常快。但是,这种用法的限制是primary key必须是int型,并且是连续自增长的。

   # ###################################################

   # 高效join

   # ###################################################

1. 小表驱动大表进行join
2. 避免子查询

   子查询是一个影响性能的隐患。应该使用join改写sql。

   # ###################################################

   # 数据类型

   # ###################################################

    1.  避免隐式转换

   CREATE TABLE `user` (

   `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,

   `account` char(11) NOT NULL COMMENT ”,

   `email` varchar(128),

   PRIMARY KEY (`id`),

   UNIQUE KEY `username` (`account`)

   ) ENGINE=InnoDB CHARSET=utf8;

   mysql> explain select * from user where account=123 \G

   *************************** 1. row ***************************

   id: 1

   select_type: SIMPLE

   table: user

   type: ALL

   possible_keys: username

   key: NULL

   key_len: NULL

   ref: NULL

   rows: 2

   Extra: Using where

   1 row in set (0.00 sec)

   可以看到,account=123的条件并没有用到唯一索引`username`。mysql的server从storage engine中读取所有的记录,使用to_number()函数,将记录中的account转换成数字,被转换后的数字用来和参数比较。我们的测试表里有2条记录,而执行计划中rows的值也是2,并且type的值为ALL,这也说明索引`username`并没有被用到。

   mysql> explain select * from user where account=’123′ \G

   *************************** 1. row ***************************

   id: 1

   select_type: SIMPLE

   table: user

   type: const

   possible_keys: username

   key: username

   key_len: 33

   ref: const

   rows: 1

   Extra:

   1 row in set (0.00 sec)

   参数为字符串类型,我们可以看到索引`username`,被使用到了。

   这是一个经常被误用的做法。

   

    2. 主键不是自增列

   自增列的主键有多个好处:

   插入性能高。

   减小page的碎片。

   提供二级索引的性能,降低二级索引的空间,因为二级索引存储的是主键的值,并不是page中的行id。

猜您喜欢


redis+Keepalived主从热备秒级切换

$
0
0

一 简介

安装使用centos 5.10 

Master 192.168.235.135

Slave 192.168.235.152

Vip 192.168.235.200

编译环境 yum -y install gcc gcc+ gcc-c++ openssl openssl-devel pcre pcre-devel

 

当 Master 与 Slave 均运作正常时, Master负责服务,Slave负责Standby;
当 Master 挂掉,Slave 正常时, Slave接管服务,同时关闭主从复制功能;
当 Master 恢复正常,则从Slave同步数据,同步数据之后关闭主从复制功能,恢复Master身份,于此同时Slave等待Master同步数据完成之后,恢复Slave身份。
然后依次循环。

需要注意的是,这样做需要在Master与Slave上都开启本地化策略,否则在互相自动切换的过程中,未开启本地化的一方会将另一方的数据清空,造成数据完全丢失

二 安装

Redis安装配置->:  http://liuyieyer.iteye.com/blog/2078093

下载安装wget  http://www.keepalived.org/software/keepalived-1.2.12.tar.gz

tar xf keepalived-1.2.12.tar.gz

cd keepalived-1.2.12

./configure

make && make install

cp /usr/local/etc/rc.d/init.d/keepalived /etc/init.d/

cp /usr/local/etc/sysconfig/keepalived /etc/sysconfig/

chmod +x /etc/init.d/keepalived

chkconfig –add keepalived

mkdir /etc/keepalived

ln –s /usr/local/sbin/keepalived /usr/sbin

 

三 配置

首先在master上创建如下配置

mkdir –p /etc/keepalived

 vim /etc/keepalived/keepalived.conf

//加入如下内容脚本

vrrp_script chk_redis { 

                script "/etc/keepalived/scripts/redis_check.sh"   ###监控脚本 

                interval 2                                        ###监控时间 

vrrp_instance VI_1 { 

        state MASTER                            ###设置为MASTER

        interface eth0                          ###监控网卡    

        virtual_router_id 51

        priority 101                            ###权重值

        authentication { 

                     auth_type PASS             ###加密 

                     auth_pass redis            ###密码 

        } 

        track_script { 

                chk_redis                       ###执行上面定义的chk_redis

        } 

        virtual_ipaddress { 

             192.168.235.200                         ###VIP 

        }

        notify_master /etc/keepalived/scripts/redis_master.sh

        notify_backup /etc/keepalived/scripts/redis_backup.sh

        notify_fault  /etc/keepalived/scripts/redis_fault.sh

        notify_stop   /etc/keepalived/scripts/redis_stop.sh 

}

 

vim /etc/keepalived/scripts/redis_master.sh

 

#!/bin/bash

REDISCLI="/opt/redis/bin/redis-cli"

LOGFILE="/var/log/keepalived-redis-state.log"

echo "[master]" >> $LOGFILE

date >> $LOGFILE

echo "Being master...." >> $LOGFILE 2>&1

echo "Run SLAVEOF cmd ..." >> $LOGFILE

$REDISCLI SLAVEOF 192.168.135.152(slave) 6379 >> $LOGFILE  2>&1

sleep 10 #延迟10秒以后待数据同步完成后再取消同步状态

echo "Run SLAVEOF NO ONE cmd ..." >> $LOGFILE

$REDISCLI SLAVEOF NO ONE >> $LOGFILE 2>&1

 

vim /etc/keepalived/scripts/redis_backup.sh

#!/bin/bash

REDISCLI="/opt/redis/bin/redis-cli"

LOGFILE="/var/log/keepalived-redis-state.log"

echo "[backup]" >> $LOGFILE

date >> $LOGFILE

echo "Being slave...." >> $LOGFILE 2>&1

sleep 15 #延迟15秒待数据被对方同步完成之后再切换主从角色

echo "Run SLAVEOF cmd ..." >> $LOGFILE

$REDISCLI SLAVEOF 192.168.135.152(slave)  6379 >> $LOGFILE  2>&1

 

在slave上创建如下文件:

vim /etc/keepalived/keepalived.conf

vrrp_script chk_redis { 

                script "/etc/keepalived/scripts/redis_check.sh"   ###监控脚本 

                interval 2                                        ###监控时间 

vrrp_instance VI_1 { 

        state BACKUP                                ###设置为BACKUP 

        interface eth0                              ###监控网卡

        virtual_router_id 51 

        priority 100                                ###比MASTRE权重值低 

        authentication { 

                     auth_type PASS 

                     auth_pass redis                ###密码与MASTRE相同

        } 

        track_script { 

                chk_redis                       ###执行上面定义的chk_redis

        } 

        virtual_ipaddress { 

             192.168.235.200                         ###VIP 

        } 

        notify_master /etc/keepalived/scripts/redis_master.sh

        notify_backup /etc/keepalived/scripts/redis_backup.sh

        notify_fault  /etc/keepalived/scripts/redis_fault.sh

        notify_stop   /etc/keepalived/scripts/redis_stop.sh 

}

 

vim /etc/keepalived/scripts/redis_master.sh

#!/bin/bash

REDISCLI="/opt/redis/bin/redis-cli"

LOGFILE="/var/log/keepalived-redis-state.log"

echo "[master]" >> $LOGFILE

date >> $LOGFILE

echo "Being master...." >> $LOGFILE 2>&1

echo "Run SLAVEOF cmd ..." >> $LOGFILE

$REDISCLI SLAVEOF 192.168.235.135(master) 6379 >> $LOGFILE  2>&1

sleep 10 #延迟10秒以后待数据同步完成后再取消同步状态

echo "Run SLAVEOF NO ONE cmd ..." >> $LOGFILE

$REDISCLI SLAVEOF NO ONE >> $LOGFILE 2>&1

vim /etc/keepalived/scripts/redis_backup.sh

#!/bin/bash

REDISCLI="/opt/redis/bin/redis-cli"

LOGFILE="/var/log/keepalived-redis-state.log"

echo "[backup]" >> $LOGFILE

date >> $LOGFILE

echo "Being slave...." >> $LOGFILE 2>&1

sleep 15 #延迟15秒待数据被对方同步完成之后再切换主从角色

echo "Run SLAVEOF cmd ..." >> $LOGFILE

$REDISCLI SLAVEOF 10.6.1.143 6379 >> $LOGFILE  2>&1

 

以下脚本分别在master和slave上创建

mkdir /etc/keepalived/scripts

vim /etc/keepalived/scripts/redis_check.sh

#!/bin/bash

 

ALIVE=`/opt/redis/bin/redis-cli PING`

if [ "$ALIVE" == "PONG" ]; then

  echo $ALIVE

  exit 0

else

  echo $ALIVE

  exit 1

fi

 vim /etc/keepalived/scripts/redis_fault.sh

#!/bin/bash

LOGFILE=/var/log/keepalived-redis-state.log

echo "[fault]" >> $LOGFILE

date >> $LOGFILE

 vim /etc/keepalived/scripts/redis_stop.sh

#!/bin/bash

LOGFILE=/var/log/keepalived-redis-state.log

echo "[stop]" >> $LOGFILE

date >> $LOGFILE

 

授执行权  chmod +x /etc/keepalived/scripts/*.sh

 

测试

在msater和slave上分别启动redis;

Service redis start

分别启动keepalived

Service keepalived start

在master上执行 ip a

虚拟IP已经绑定了

在master上执行service redis stop 查看slave

看到了吧秒级切换。

接下来再吧master上的redis启动起来

 

又切换回来了。

测试redis同步

 

 

到此安装完成了。



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


ITeye推荐



9个最新的手机/移动设备jQuery插件

$
0
0

随着互联网的流行,移动网站开始急速增加,在2014年手机网站将会出现很多,所以手机网站是必须要学会制作的。手机网站不像桌面平台一样制作,否则会影响显示效果,目前大部分手机网站使用 响应式设计技术,而且也很流行。

但是新手想实现响应式技术是不容易的,所以我们可以用一些响应式框架或适用于移动设备的jQuery插件来制作,这样能方便实现我们的需求。

今天设计达人网小编整理了9个好用的手机/移动设备jQuery插件,也许对你的手机项目有帮助哦!

IOSSCRIPTS

iosslider这个jQuery插件可以让桌面、手机、平板设备实现自适应的网页幻灯片(灯箱),支持水平和垂直方式。

IOSSCRIPTS 手机插件 jQuery插件

查看演示及下载

Slip.js

如果想实现触摸设备排序的话,我想Slip.js插件是最好的选择。

Slip.js 手机插件 jQuery插件

查看演示  下载插件

jQuery Finger

一个触摸事件插件。和其它相关插件不同的是,它会移除触摸设备的300毫秒延迟。

jQuery Finger 手机插件 jQuery插件

查看演示  下载插件

Tocca.js

超轻量级的触摸事件插件,大小只有1KB啊。

Tocca.js 手机插件 jQuery插件

查看演示  下载插件

Leader.js

用户登陆插件,目前为概念设计,是否真适用在实际项目中使用呢?

Leader.js 手机插件 jQuery插件

查看演示  下载插件

JQuery Mobile Date Navigation

移动手机设备的日历导航插件,如果有日期弹出来选择就更强大了。

JQuery Mobile Date Navigation 手机插件 jQuery插件

查看演示及下载

jQuery Navobile

这类侧边栏导航菜单插件我们介绍过,请看《 好用的侧边栏菜单/面板jQuery插件》一文。

查看演示时,请把你的浏览器缩小到手机的分辨率就可以看一以了。

jQuery Navobile 手机插件 jQuery插件

查看演示及下载

Responsive Mobile Menu

一个响应式移动设备菜单插件。

Responsive Mobile Menu 手机插件 jQuery插件

查看演示及下载

PhotoSwipe

图像展示类的移动设备插件,功能实用,而且效果不错,请看DEMO体验。

PhotoSwipe 手机插件 jQuery插件

查看演示  下载插件


Copyright ©2010-2014 ¦ RSS订阅¦ 新浪微博¦ 本文链接¦ 添加评论
交流:UI设计交流群:59300679,与500名设计师交流设计,分享素材。

paypal相关问题

$
0
0

paypal ,号称是全球最大的网络支付公司,在国外确实很强,不过在国内被支付宝干掉了。

paypal在国内中文名叫贝宝,国内有了支付宝一般人基本上用不上这玩意,今天文章里和大家说说paypal国际版的问题。为什么要说paypal呢?因为今年3月份,全球最大的电子商务平台ebay(曾经,现在是淘宝了)搞了一个海淘节(专门针对中国买家),很多数码产品、手表、包包等都有非常实惠的价格。仅仅只过了三个月,6月9号开始,ebay又搞了一次专门针对中国的海淘节优惠,我觉得ebay是在这里面尝到了甜头,我觉得以后这类活动估计会越来越多。 反正价格实惠对消费者来说是好事,而且很多产品是免运费 美国upsp直邮。有好多产品算下来价格比国内便宜700+呢!!!!

【这里必须要插一下,我发现很多自傻X说 走upsp直邮必税, 走顺丰转运必税,实际情况真是这样吗? 从各大网站、论坛晒单来看,并非如此,缴税要满足2个条件:1是你的运气很背;2是税金超过人民币50元, 如果你买一双NB的运动鞋70美元,鞋类税金是10%,42元的税金海关是不会收的】 既然选择海淘,那你首先就要做好被税的打算,除非你选择包税的转运公司。再说了,纳税光荣!!!!!

如果你喜欢海淘,首先需要有一张双币信用卡,接着注册一个paypal帐户(国外很多购物网站均采用paypal),请登陆paypal官方网站 www. paypal.com进行注册。流程简单,就是输入一些基本的资料,最后输入信用卡用于验证身份。

身份验证

这里着重提醒一下paypal帐户验证的问题,以信用卡为例,输入个人信用卡之后,paypal会从信用卡上扣除1.95美元,同时在交易明细中有一个四位数的code(验证码),你需要登陆网上银行查询这笔交易,找到四位数验证码,然后登陆paypal 帐户里面 输入四个数字进行验证。

付款问题

我昨晚在ebay上购物使用paypal付款,试了起码不少于10次,老是说失败。今天早上打电话给paypal客服021-28913888, 他说是浏览器问题,建议我不要使用360chrome和傲游!!!! 推荐使用谷歌、IE、火狐浏览器。我换了IE8来支付一下子就搞定了。

手续费问题

涉及到金钱的问题才是最重要的,我今天在paypal支付的时候发现信用卡扣款短信里面的金额和实际金额不符,短信里面的扣款金额比美元换算后人民币多70多元。我打电话给银行,他们说走银联通道会有手续费,具体他们不清楚。我马上联系paypal客服,客服解释说信用卡默认付款是走银联通道,会有费用,现在我们将会补偿你,多扣的部分会退到paypal帐户里面,过了几分钟就到账了。(如果我不打电话去问,是不是这笔钱就被吃了?)退回到paypal的美元因为金额不大,不到100美元不能提现,只能用于今后海淘购物了。

那么如何避免上述问题呢?

如果你是新注册的paypal国际用户,在你身份验证之后建议拨打paypal客服,让她把paypal 信用卡默认付款币种改为美元;

如果你也遇到像我这样钱被多扣了,那你赶紧打电话问paypal客服,我那可是双币信用卡,为啥不直接扣美元?????赶紧把多扣的钱退回来!!!!

最后提醒大家一点,海淘,买越贵的东西越划算,经常对比国内京东、亚马逊和国外亚马逊同款产品价格,你就知道优势了。

还有一点需要纠正,made in china 并不一定要比made in usa 差,这主要是看工厂面对的客户。从我个人见闻,国内制造商为日本客户提供的产品质量一向很优秀,因为日本人对于质量要求非常严格,一批产品中抽检如果发现有不合格的,这批产品会被全部退货。

over

 

 

 

 

猜你喜欢:

京东移动客户端首次下单返五元京券

东京恋爱模樣 迅雷下载

输入192.168.1.1进入我的e家

路由器上再接一个无线路由器

fitbit flex手环一周体验
无觅

解放QA的唯一途径是"干掉"QA

$
0
0

        在整理资料的时候翻出了一位大神曾经转发给我的分享《 从QA到EP》。联想到最近发生的事,又颇有感慨。

        已经有很多前辈对QA的工作职责,现状及演变方向做了分析。对很多评论,我也是深有同感。

        以下观点只针对部分QA,但国内几乎绝大部分QA都类似。
        个人感觉QA都是苦逼的手工测试者,没什么技术含量,入行门槛极低。
一般用人单位所招QA多是毕业于计算机相关专业或被测系统所处领域的相关专业。但他们的工作内容所需计算机和特定领域的知识少之又少。可以说他们的工作和所学专业不对口。不写代码,不搞领域相关专业研究。自然专业都白读了。 工作内容决定了QA工作看不到未来。

        其实不应该设立专职的QA。这是解放QA的唯一途径。

        不做测试的开发是不合格的开发;测试没有架构话语权的项目是不合格的组织。
        测试的工作应该让开发自己来做。开发没时间?难道QA就有时间?开发做测试和QA做测试哪个成本小?绝对是开发来做更划算!如果测试工作多到需要专职的手动测试人员参与,这个项目问题大大的,绝对需要改组!
        测试必须自动化! 自动化包括三个层次:单元测试,Service层和UI层。开发在提交代码前肯定要保证功能可用。再加上必须的单元测试,手动测试部分就很少了,开发绝对能顺手做掉。
        Service层的自动化依赖于架构的良好设计。而设计架构(或实施架构)的人必出自开发,当然有架构话语权。让非开发的“外人”,QA,来做这一层的自动化测试绝对是愚蠢至极。
        UI层的自动化代价很高,必须要少而精。BTW, 现实中QA团队没有技术含量,也少有眼界合格的领导。也就只能在这一层做点努力,导致这一层所占比例极高,甚至会出现“UI自动化100%”这样的目标。
        以上三层所占比例一般是 单元测试70%,Service层占剩余的大部(20%-25%)

        再到前面提到的那篇《从QA到EP》。作者在文中提到用高技术含量的特殊团队来替代QA,以及一些组织架构上的见解。我觉得如果有专门的团队来替代QA,那么 这支团队在整个项目中的地位必须非常高,不仅仅是尊敬,而是对开发团队的绝对领导权。EP成员的 各项福利(薪水,奖金,升职空间)必须出自开发团队,优于其它开发人员。EP的工作非常重要,但又不是在所发布产品中能直接体现的,容易受委屈。当然要让他们拿头一份。其实EP应该由架构师,项目导师级别的人组成。具体如果有编码工作可分配给底下开发。


        还有就是,我非常赞同作者提到的项目Owner制度!这个 扁平化的组织管理结构是必须的。不然就会出现明显的子团队,子团队之间各种不合作。这Owner的综合能力也必须非常强大, 必须非常熟悉项目各项任务,又能为项目争取到该有的资源。除了程序员,没人能管得了程序员。这个 Owner必须是大神级的程序员。统帅无能,累死三军这种事很常见。慎选Owner啊!

 

        正常的项目就不该有专职QA。有就说明这个项目病了, 得治啊!



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


ITeye推荐



Sina股票数据接口

$
0
0
以大秦铁路(股票代码:601006)为例,如果要获取它的最新行情,只需访问新浪的股票数据
接口:http://hq.sinajs.cn/list=sh601006这个url会返回一串文本,例如:

var hq_str_sh601006="大秦铁路, 27.55, 27.25, 26.91, 27.55, 26.20, 26.91, 26.92,
22114263, 589824680, 4695, 26.91, 57590, 26.90, 14700, 26.89, 14300,
26.88, 15100, 26.87, 3100, 26.92, 8900, 26.93, 14230, 26.94, 25150, 26.95, 15220, 26.96, 2008-01-11, 15:05:32";

这个字符串由许多数据拼接在一起,不同含义的数据用逗号隔开了,按照程序员的思路,顺序号从0开始。
0:”大秦铁路”,股票名字;
1:”27.55″,今日开盘价;
2:”27.25″,昨日收盘价;
3:”26.91″,当前价格;
4:”27.55″,今日最高价;
5:”26.20″,今日最低价;
6:”26.91″,竞买价,即“买一”报价;
7:”26.92″,竞卖价,即“卖一”报价;
8:”22114263″,成交的股票数,由于股票交易以一百股为基本单位,所以在使用时,通常把该值除以一百;
9:”589824680″,成交金额,单位为“元”,为了一目了然,通常以“万元”为成交金额的单位,所以通常把该值除以一万;
10:”4695″,“买一”申请4695股,即47手;
11:”26.91″,“买一”报价;
12:”57590″,“买二”
13:”26.90″,“买二”
14:”14700″,“买三”
15:”26.89″,“买三”
16:”14300″,“买四”
17:”26.88″,“买四”
18:”15100″,“买五”
19:”26.87″,“买五”
20:”3100″,“卖一”申报3100股,即31手;
21:”26.92″,“卖一”报价
(22, 23), (24, 25), (26,27), (28, 29)分别为“卖二”至“卖四的情况”
30:”2008-01-11″,日期;
31:”15:05:32″,时间;

这个接口对于JavaScript程序非常方便,通常的使用方式为,静态或动态地在页面中插入:

<script type="text/javascript" src="http://hq.sinajs.cn/list=sh601006" charset="gb2312">

</script>

<script type="text/javascript">

var elements=hq_str_sh601006.split(",");

document.write("current price:"+elements[3]);

</script>

这段代码输出大秦铁路(股票代码:601006)的当前股价

current price:14.20

如果你要同时查询多个股票,那么在URL最后加上一个逗号,再加上股票代码就可以了;比如你要一次查询大秦铁路(601006)和大同煤业(601001)的行情,就这样使用URL:
http://hq.sinajs.cn/list=sh601003,sh601001

但如果你要查询大盘指数,情况会有不同,比如查询上证综合指数(000001),使用如下URL:
http://hq.sinajs.cn/list=s_sh000001 服务器返回的数据为:

var hq_str_s_sh000001="上证指数,3094.668,-128.073,-3.97,436653,5458126";

数据含义分别为:指数名称,当前点数,当前价格,涨跌率,成交量(手),成交额(万元);

查询深圳成指的URL为:
http://hq.sinajs.cn/list=s_sz399001



对于股票的K线图,日线图等的获取可以通过请求http://image.sinajs.cn/…./…/*.gif此URL获取,其中*代表股票代码,详见如下:

查看日K线图:

http://image.sinajs.cn/newchart/daily/n/sh601006.gif


分时线的查询:

http://image.sinajs.cn/newchart/min/n/sh000001.gif

日K线查询:

http://image.sinajs.cn/newchart/daily/n/sh000001.gif

周K线查询:

http://image.sinajs.cn/newchart/weekly/n/sh000001.gif

月K线查询:

http://image.sinajs.cn/newchart/monthly/n/sh000001.gif

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


ITeye推荐



Viewing all 15843 articles
Browse latest View live


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