<< 六月 2014 | 首页 | 八月 2014 >>

一生必看的经典电影

一生必看的经典电影 
1、十佳科幻片: 
1)星球大战系列:开创了一个电影神话。 
2)异次元骇客(第十三层):应该说它比黑客帝国的构思更精妙。 
3)超人:所有漫画类科幻电影的代表。 
4)终结者(1、2):科幻电影经典中的经典。 
5)12猴子:如此引人深思的科幻电影真不多见。 
6)黑客帝国系列:引发了对现实和未来网络发展的思考,形成了一种黑客文化。 
7)移魂都市(黑暗城市):风格另类的科幻片,结尾出人意表。 
8)超时空接触:比较严肃地探讨外星文明问题的力作。 
9)千钧一发:描写未来社会人的基因问题的惊险影片,内容和主题俱佳。 
10)2001漫游太空:经典作品,以严肃的科学性和预见性著称。 
2、 十佳剧情片: 
1)肖申克的救赎(刺激1995):男人必看的励志影片。 
2)教父(1、2):经典黑帮片,有此作品,其他同类一概低头。 
3)美国往事:整个人生都在里面。 
4)天堂电影院:每个男人的童年回忆,太经典了。 
5)无主之城:人家怎么能拍出这么牛的电影! 
6)活着:也许是中国目前最伟大的电影。 
7)阿甘正传:教导所有的人要去宽容别人,傻就是福气。 
8)勇敢的心:民族自尊的好教材,希望大家要爱中国。 
9)楚门的世界:探讨人的价值和人性根本的奇思怪作。 
10)音乐之声:音乐的力量、音乐的快乐! 
11)辛德勒的名单:震撼人心的历史、充满感染力的摄影和杰出的演员 

3、 十佳战争片: 
1)拯救大兵瑞恩:最真实反映战争和人性的超级巨作。 
2)猎杀红色十月:节奏和人物拿捏准确的潜艇影片代表作。 
3)兵临城下:从独特的视角描写二战的巨片,演员表演到位。 
4)大逃杀:归入战争片只因其太震撼、太残酷。 
5)巴顿将军:全景式展示战争的代表作,演员表演出色。 
6)u-571:效果出众的新型海战片,拍得很有特点。 
7)全金属外壳:库布里克对战争的深刻反思,看过后使人对战争产生恐惧。 
8)星际舰队:科幻性质的战争片,士兵的训练和战斗的描写很有煽动性和争 
9)复仇:也是罗马尼亚的老电影,这部影片的枪战让人百看不厌。 
10)三步杀人曲系列:干净利落的墨西哥风格枪战电影。 
11)第一滴血(1):有内涵有力度有故事,是史泰龙为数不多的好片。 

5 、十佳KB片: 
1)夺命狂呼系列:校园KB片的代表作,对年轻人的胃口。 
2)杀出个黎明:另类夸张的KB片,不吓人,反而很搞笑和另类。 
3)活死人的黎明:活死人系列代表作,以恶心的僵尸著称。 
4)驱魔人:画面阴郁,声效凄厉,晚上看真的噤若寒蝉! 
5)见鬼:港式KB片代表,有KB,也有情感。 
6)解剖(1、2):欧洲KB片的代表,内容奇怪前卫。 
7)坏品味:指环王导演的早期作品,KB而搞笑。 
8)异形系列:科幻类KB片经典,1、2、4都很精彩 
9)咒怨:日式KB的代表,极其邪恶! 
10)活跳尸:罕见的血腥的黑色幽默! 

6 、十佳喜剧片 : 
1)两杆大烟枪:在英式幽默和一团乱麻中寻找答案的乐趣 
2)我为玛丽狂:美国厕所文化的代表,低俗但好玩。 
3)反斗神鹰系列:美式无厘头动作喜剧。 
4)大话西游(1、2):经典! 
5)花田喜事:港式老喜剧片的代表,明星云集。 
6)惊声尖笑系列:以模仿糟改其他影片取乐的新型喜剧片。 
7)虎口脱险:欧式喜剧片经典作,百看不厌。 
8)金鸡:近年少见的优秀香港电影,有很深的内涵。 
9)面具:金凯瑞的成名作。 
10)喜剧之王:周星弛最有内涵的电影 
7 、十佳武侠片: 
1)卧虎藏龙:“美”式武侠片的开山之作! 
2)新龙门客栈:现在的武侠片制作模式都是照它来的。 
3)黄飞鸿系列:捧红了李连杰啊!!! 
4)醉拳:成龙代表作,功夫片黄金时代的作品。 
5)少林寺三十六房:刘家辉的成名作,现在来看也趣味无穷。 
6)少林寺:不用说了,真功夫的代表。 
7)佐罗:法国剑侠片的代表,迷到很多mm。 
8)笑傲江湖:对林青霞扮演的东方不败印象最深。 
9)座头市:创新的日本剑侠片,很有特点。 
10)杀死比尔:呵呵,新派东西方结合的功夫片来啦!!!! 

8 、十佳动画片: 
1)怪物公司:罕见的数码特技!动人有趣的创意! 
2)冰冻星球:虽然卖座不是很好,但它的场景可是真的漂亮! 
3)辛巴达航海记:巧妙结合手绘和3d技术的优秀作品。 
4)怪物史莱克:健康的爱情观和幽默的故事! 
5)寻找尼莫(海底总动员):融合温馨情感和尖端技术的动画经典。 
6)千与千寻:宫岐峻颠峰之作! 
7)最终幻想:3d人物数码化的先驱,技术出众。 
8)吸血鬼猎人:日式风格的华丽吸血鬼大作。 
9)盖娜:欧洲的动画大作,风格很怪异! 
10)恐龙:不用说了,好看! 
9 、十大QS片: 
1)YANWU女郎:少见的描写夜总会无上装演员的作品,场面很精致! 
2)卡里古拉:有史以来最“严肃”和宏伟的QS巨片! 
3)罗曼史:探讨爱情冲突和仇恨的情色名作。 
4)亲密:获得柏林金熊,在欧洲大型电影节获奖影片中首次出现KJ的场面! 
5)巴黎野玫瑰:描写爱到极至的感情,可怕! 
6)深喉:现代QS片的鼻祖! 
7)悲情城市:法国QS片演员出演的反映丑恶社会的独立电影。 
8)感官世界:大岛渚的惊世骇俗之作,以演员的真实ZUOAI场面闻名! 
9)巴黎最后的探戈:怀念马龙白兰度,就看看这部片子吧! 
10)丑闻:韩国QS作品。 
10 、十佳惊悚片: 
1)死神来临(1、2):构思巧妙,场景惊人。 
2)黑暗降临:描写鬼怪传说的惊悚片,有些新意思。 
3)沉默的羔羊:获得奥斯卡奖的惊悚片! 
4)七宗罪:风格阴暗,让人时不时想逃!表演精致! 
5)闪灵:可以尽情欣赏杰克尼科尔森的超凡演技。 
6)第六感:此类影片代表作!!结尾精彩之极! 
7)断头谷:蒂姆伯顿的惊悚KB大作,人头乱滚! 
8)心慌方(1、2):加拿大导演的匪夷所思之作!!! 
9)本能:不用说了,很色情! 
10)医院风云:拉斯冯提尔导演的丹麦影片,吓的很多人不敢独自回家。 

11、 十佳爱情片: 
1)泰坦尼克:商业大作! 
2)漂亮女人:现代版麻雀变凤凰! 
3)罗马假日:奥黛丽赫本的经典。 
4)金玉盟:淡淡的幽怨、一生的承诺! 
5)卡萨布兰卡(北非谍影):经典老片。 
6)毕业生:我们还能找到青春时代的纯洁爱情吗? 
7)生命中不能承受之轻:以时代为背景的爱情名片,节奏缓慢。 
8)保镖:轻松健康的爱情电影。 
9)克莱默夫妇:对婚姻、儿女进行深入思考的伦理片。 
10)阳光灿烂的日子:属于我们这些人的青春! 

12、 十佳魔幻片: 

1)狼族盟约:法式魔幻片,明星众多的大制作。 
2)印第安纳琼斯(夺宝奇兵)三部曲:斯皮尔伯格和卢卡斯的强大组合! 
3)倩女幽魂:中国鬼电影的里程碑!决不输于外国片! 
4)魔戒三部曲:伟大的经典的真正的电影!!! 
5)哈利波特系列:新魔幻电影的奇特分支。 
6)魔幻屠龙(龙的心):感情真挚。 
7)木乃伊:幽默和特技结合的娱乐片。 
8)剪刀手爱德华:蒂姆伯顿最有想象力的作品。 
9)小飞侠:崭新的适合儿童的幻想片。 
10)大鱼:多看两遍吧,活着要善待自己啊! 


第一滴血1234 
速度与激情123 
惊声尖笑1234 X战警 终结者 
哈利•波特123 刀锋战士 死神来了 尖峰时刻 黑客帝国 
小鬼当家123 虎胆龙威123 谍影重重123 的士速递 007 
黑衣人 透明人 非常小特务 大逃杀 黑夜传说 国家宝藏 夺宝奇兵 非常人贩 超人 纳尼亚传奇 天外来菌 拳霸 蝙蝠侠怪物史莱克 碟中谍 星球大战 暗流 木乃伊 X档案 合伙人 教父 怪医 鬼娃 回魂 牛仔裤的夏天 狂蟒之灾 蝴蝶效应 恐怖休息站头 师傅一体 王牌大贱谍 异形 蜘蛛侠加勒比海盗 异种 生化危机 电锯惊魂 魔戒 机械警察 


先说欧美的本人强力推荐: 
第一,那当然是《肖申克的救赎》(台湾翻译为刺激1995) 
《越狱》的紧张情节就是建立在这部电影的基础上。如果不是遇到当年的《阿甘正传》它必然是奥斯卡最佳电影。 

第二,《美国丽人》(1999年奥斯卡最佳电影) 
整个片子色调晦涩,虽然不是黑白。 
“世界如此之美,应该平静地去欣赏”,主人公莱斯特•伯纳姆(凯文•斯帕西饰)死前的最后一席话启发我们,用崭新视角去看待我们周围的世界,从我们深爱之人的缺点,到风中飘舞的塑料袋。如此颇具哲理的结局既让观众对莱斯特-伯纳姆的离开充满悲伤,也让我们对人生意义价值产生新的思考。 
物质带给我们便利,同时也带来太多负担,又有多少人,可以真的放下。 
一生忙忙碌碌,追求的到底是什么,想要的到底是什么结果。 
人们都在撒谎,对生活撒谎,对除了自己的周围撒谎,最后,迷惑了自己。 
人生的意义在于体验一切,挑战自己,当你身处逆境你仍然满载希望,积极向上,充满关爱,那对你来说,就没有逆境了。 
请宽容,让自己幸福,不能带给别人幸福至少不要带去伤害。…… 
当然影片所要表达的内容实在太多太多了,一千个读者一千个汉姆雷特,请你自己感悟吧。

第三,《美丽心灵》(2001年奥斯卡最佳电影) 
这个电影的名字太好了,看到影片的最后,我们自己总结,美丽心灵到底说的是谁呢?是约翰纳什的美丽妻子?还是长期帮助他的那位老同学?还是那个让一个“疯子”随心所欲的在校园生活的学校管理人员?是诺贝尔奖项委员会?是美国政府福利体制?是那些可爱的学生?是一切!!美丽心灵是你是我是生活,是世界。 

第四,《老无所依》(去年的奥斯卡最佳电影) 
这个电影一定要看两次,第一次看情节,看内容,看影片大概要表达的东西,然后看下专家的影评,再看一次细节的处理,感受影片表达的思想,感受影片的内涵。我最欣赏的这个电影的原因是,影片里面每一个长时间人与人的对话都包含了丰富的内涵和哲学道理,太强了。。。。。。 

第五,《阿甘正传》(1995奥斯卡最佳影片,没错就是它在1995的奥斯卡最佳电影评选中干掉了《肖申克的救赎》) 
这个影片我就不介绍了,随便一百N多的影评。 

第六,《当幸福来敲门》(威尔斯密斯父子齐上阵) 
当我们抱怨生活的不如意时,当我们觉得才华不被认可时,当我们觉得应该获得更好的工作时,当我们希望获得更多时,请你想想,我们付出了什么。 
《当幸福来敲门》是一部奋斗与立志的影片,这部影片对与我们来说是什么呢?可以是风向标,可以是灯塔,也可以是指南针,更可以是希望!是梦想,是我们未来的行动与收获,是你我后半生的一切!!! 


男人十大必看电影: 

1、《阿甘正传》课程:执着 

每次想起阿甘在美国东西海岸之间的奔跑,心里都会止不住的伤感,还有振奋。 

你相信一个智障儿的成功吗?你相信这世上得到最多的人正是那些不计得失的 
人吗? 

阿甘不懂得他不能总跟着一个女人帮她打架,也不懂得一个成年人不该总把 
妈妈的话挂在嘴边。阿甘什么都不知道,他只知道凭着直觉在路上不停地 
跑,并且最终跑到了终点。另外,《阿甘正传》还会教给你一个男人必须 
具备的一种素质——困境中的幽默感。 

2、《东方不败》课程:才华 

男人不应该不看武侠片,如果你只能看一部武侠电影,你会选择什么?我 
想应该是《东方不败》。“满堂花醉三千客,一剑霜寒十四州”,剑客的 
身姿随着剑在空气中的游走而起舞,翩若惊鸿,宛若游龙。金庸的《笑傲 
江湖》讲的是对自由的追求,徐克、程小东、张叔平、李连杰等一干天才 
用电影再现了金庸笔下这个瑰丽无比的武侠世界。沧海一声笑的曲子传唱 
至今,成为我们一个幻想的凭籍,一个逃避现实的出口。 

3、《美国往事》课程:人生 

《美国往事》包含了一个男人在这个世界上所能遇到的一切。友情、爱情 
、幻想、责任、冲突。它更像是一场让人不愿醒来的梦,当面条躺在床上 
,在温暖的灯光和迷离的电话铃声中回到那些逝去了的岁月,这场梦便开 
始了,直到最后,面条终于露出笑容,我们才回到自己的人生,去继续那 
些不尽的故事。什么帮派,什么仇杀,原来都不重要,印象中只有一个毛 
躁的少年,偷看一个美丽女孩儿跳舞;只有一个负罪的兄弟,每天早早地 
上床睡觉;只有一个白发苍苍的老人,面对背叛了的友谊,语调平和,不 
动声色。《美国往事》带着你作了一个3小时45分钟的梦。人生如梦,这也 
许是惟一的感受。 

4、《罗马假日》课程:爱情 

也许《罗马假日》有点瞎浪漫的嫌疑,但奥黛丽?赫本的出现使它真的成了 
一部童话。她就像是游历人间的天使,美丽得不染纤尘。记者吻过湿淋淋 
的公主,然后看着她慢慢地走向自己的官邸。那一刻,你是否会在心里默 
默地说“别走”? 

在罗马的宫殿里,两个人站得那样近,也离得那样远。乔只能说:“你的 
朋友绝不会让你失望。”而公主也只能这样回答:“罗马,当然是罗马。 
”在人的一生里,即便只有这样一刻心灵的相通,也会少却多少遗憾! 

5、《勇敢的心》课程:勇气 

也许英雄并不是无所不能的神明,但英雄一定是无所畏惧的勇士。在你站在 
霓虹闪烁的街头,当你面对卑鄙委琐的笑脸,你又想起了那个让你汗颜的华 
莱士,这时你收起脸上惯带的笑容,默默地向梅尔?吉布森致敬,从来没有 
这么庄重。因为他让我们明白,什么才是真正的英雄。“freedom!”华莱 
士临死前的一声呐喊,把你的血也点燃了。 

6、《辛德勒的名单》课程:责任 

所谓责任,就是未必做得成却必须去做的事。对于辛德勒来说,救助落难的 
犹太人是作为一个真正的人的责任。而把那段历史搬上银幕,则是斯皮尔 
伯格作为一个犹太艺术家的责任。《辛德勒的名单》包含着对受难者的祭 
奠,对拯救者的敬意,和对光明一定到来的信念。在影片放映后的一片赞 
誉声中,斯皮尔伯格平静地把影片的全部收益捐给了美国的纳粹大屠杀纪 
念馆。 

7、《肖申克的救赎》课程:信念 

“有一种鸟儿是永远也关不住的,因为它的每片羽翼上都沾满了自由的光辉 
。” 

一个人能够在15年痛苦的牢狱生活里,不放弃对自由的向往,这是一种怎 
样的精神力量?所以他成功了,成功夺回了自由。《肖申克的救赎》中有 
这样一句话:体制化是这样一种东西,一开始你排斥它,后来你习惯它, 
直到最后你离不开它。想想看,我们的身体已经有多大一部分被体制化了? 

8、《e.t》课程:童心 

如果看《e.t》(外星人)时你流泪了,你不要害怕也不要惭愧,不管你 
有多大的年纪。实际上,能够和孩子们一起为e.t的遭遇而悲喜,是一件 
多么让人庆幸的事,这代表着我们还有一点童心未曾泯灭,代表着我们有 
一些梦想还藏在心底。当长着一双孩子的眼睛的e.t用超能力让孩子们的 
自行车飞上夜空,穿过那轮明月的时候,你是否感到了震憾?是否对自己 
一些已经习惯了的东西产生了怀疑? 

9、《现代启示录》课程:痛苦 

战争是一种秩序的破坏,世界上最可悲最痛苦的事莫过于战争,最痛苦的战 
争电影莫过于《现代启示录》。《现代启示录》讲的是人性的倒退,秩序的 
破坏。但文明的进程却没有人能够阻挡。 

10、《第七封印》课程:哲思 

瑞典大师伯格曼代表作,在这部1957年完成的黑白影片中,伯格曼明确地提 
出了“上帝是否存在”的疑问。我不敢说我看懂了《第七封印》,但如果要 
我在临死之前看一部电影,我一定选择《第七封印》。 

阅读全文……

标签 : ,

oryx-editor - Web-based Graphical Business Process Editor. - Google Project Hosting

Oryx is a web-based editor for modeling business processes in various modeling languages like BPMN or EPC. You can create models and share them with your business partners, clients and friends. You may not only share a model with your colleagues, but discuss and improve it within one working environment. Thus, Oryx brings all the advantages of Web 2.0 into the world of modeling. To start modeling you need zero installation–your model is just one click away.

Oryx is an open platform for developments regarding business process modeling. Everyone is invited to contribute new process modeling languages, features and knowledge to Oryx. The number of contributors is growing rapidly and the developers come from all over the world.

A prominent tool that built on top of Oryx is the Signavio Process Editor, a professional BPMN designer and Business Process Analysis (BPA) tool.

This page is the drop-in center for all contributors of the project as well as those who want to deploy Oryx on their own server or integrate it into their own projects. In the wiki and downloads sections you find various documentation for developers.

Oryx is a project of the Business Process Technology research group at the Hasso Plattner Institute of IT Systems Engineering at the University of Potsdam. The research group is led by Professor Mathias Weske, as is the Oryx project.

Oryx in a Nutshell

  • A powerful tool for graphical process modeling
  • Oryx runs in your web browser, zero software installation required
  • Oryx is a process platform - stakeholders can access process models via the web
  • Based on web technology, integration in wikis and your corporate IT is simplified
  • Oryx creates transparency and acceptance
  • Oryx is extensible - new functions can be added via a plugin mechanism
  • Oryx directly supports BPMN, EPC, and other process modeling languages
  • Oryx is based on open internet standards
  • User authentication through OpenID

Facts for Developers

  • Easy, declarative definition of new process modeling languages (we call them stencil sets)
  • Easy extension of the editor's functionality via a plugin mechanism (small core, almost all features are implemented as plugins)
  • The rendering technology used is SVG and XHTML
  • Client-side code is written in Javascript, server-side code is written in Java
  • Support of I18N

阅读全文……

Atmosphere/atmosphere · GitHub

Realtime Client Server Framework for the JVM, supporting WebSockets and Cross-Browser Fallbacks Support

 

http://async-io.org/

 

The Atmosphere Framework contains client and server side components for building Asynchronous Web Applications. The majority of popular frameworks are either supporting Atmosphere or supported natively by the framework. The Atmosphere Framework supports all major Browsers and Servers.

 

Follow us on Twitter.

Atmosphere transparently supports WebSockets, Server Side Events (SSE), Long-Polling, HTTP Streaming (Forever frame) and JSONP.

The Atmosphere Framework Stack consists of:

Atmosphere Stack

The Atmosphere Framework Stack works on all Servlet based servers including Tomcat, JBoss Jetty, Resin, GlassFish, Undertow, WebSphere, WebLogic etc. Not running a Servlet Container? NettyPlay! Framework orVert.x. We support a variety of extensions like STOMPRedisHazelcastJMSJGroups and many more. Support for Socket.IOSockJS and Cometd are also available.

Using another framework? Look at the list of supported extensions. Easiest way to learn Atmosphere is by trying a sample.

Atmosphere's Java/Scala/Android Client is called wAsync.

阅读全文……

标签 : ,

HTML table Export

HTML 表格导出 jQuery 插件可以帮助用户导出 HTML 表格到 JSON、XML、PNG、CSV、TXT、SQL、MS-Word、MS-Excel、MS-PowerPoint 和 PDF 格式。您可以轻松地设置字体大小,分隔符,导出类型等。

Installation

 jquery Plugin

<script type="text/javascript" src="tableExport.js">
<script type="text/javascript" src="jquery.base64.js">

 PNG Export

<script type="text/javascript" src="html2canvas.js">

 PDF Export

<script type="text/javascript" src="jspdf/libs/sprintf.js">
<script type="text/javascript" src="jspdf/jspdf.js">
<script type="text/javascript" src="jspdf/libs/base64.js">

 Usage

onClick ="$('#tableID').tableExport({type:'pdf',escape:'false'});"

 Type

{type:'json',escape:'false'}
{type:'json',escape:'false',ignoreColumn:'[2,3]'}
{type:'json',escape:'true'}

{type:'xml',escape:'false'}
{type:'sql'}

{type:'csv',escape:'false'}
{type:'txt',escape:'false'}

{type:'excel',escape:'false'}
{type:'doc',escape:'false'}
{type:'powerpoint',escape:'false'}

{type:'png',escape:'false'}
{type:'pdf',pdfFontSize:'7',escape:'false'}

 Options

separator: ','
ignoreColumn: [2,3],
tableName:'yourTableName'
type:'csv'
pdfFontSize:14
pdfLeftMargin:20
escape:'true'
htmlContent:'false'
consoleLog:'false' 

 Sample TABLE Format

<table id="customers" class="table table-striped" >
	<thead>			
		<tr class='warning'>
			<th>Country</th>
			<th>Population</th>
			<th>Date</th>
			<th>%ge</th>
		</tr>
	</thead>
	<tbody>
		<tr>
			<td>Chinna</td>
			<td>1,363,480,000</td>
			<td>March 24, 2014</td>
			<td>19.1</td>
		</tr>
		<tr>
			<td>India</td>
			<td>1,241,900,000</td>
			<td>March 24, 2014</td>
			<td>17.4</td>
		</tr>
		<tr>
			<td>United States</td>
			<td>317,746,000</td>
			<td>March 24, 2014</td>
			<td>4.44</td>
		</tr>
		<tr>
			<td>Indonesia</td>
			<td>249,866,000</td>
			<td>July 1, 2013</td>
			<td>3.49</td>
		</tr>
		<tr>
			<td>Brazil</td>
			<td>201,032,714</td>
			<td>July 1, 2013</td>
			<td>2.81</td>
		</tr>
	</tbody>
</table> 

阅读全文……

标签 : ,

交通标志识别项目 putsi/tsaraisa · GitHub

问:识别交通标志,怎么确定图片里有交通标志,具体在哪里?答:这个应该算目标识别和物体识别,CV的经典教程都有object recognition的内容可以参考。具体到交通标志识别,Github上有些开源代码 Matlab C++ Java的都有

Tsaraisa was run with "./tsaraisa.py -s -g -c lbpCascade.xml -M" on demo screenshot.

What does it do?

  • Detect traffic signs.
  • Recognize speed limits in signs.
  • (optional) Compare GPS-speed to speed limit.
  • (optional) Run user command when overspeeding.

How?

GPS-class

  • Uses threading to update GPS-info automatically.
  • Gets speed from GPS-daemon using python bindings.

Frame handling

  • Reads frame from webcam.
  • Converts frame to grayscale with cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY).
  • (optional) Morphological Open/close.
  • (optional) Equalizes histograms with cv2.equalizeHist().
  • Downscale frame with multiplier n.

Traffic sign detection

  • Uses LBP- or HAAR-cascade to detect speed signs.
  • LBP-cascade was trained with 2000 positive and 1000 negative images.
    • Negative image is image of road without speed limit sign.
    • Positive image is image of road with speed limit sign.
  • LBP- and HAAR-detection allows great differences in lightning.
  • LBP- and HAAR-detection works great on low performance machines.

Recognizing speed limits

  • Uses Fast Approximate Nearest Neighbor Search Library feature matching.
  • Creates "keypoints" of detected sign and compares them to all known speed limits (files in data-folder).
  • Keypoint match distances need to be inside a threshold.
  • Match with biggest proper keypoint amount will be returned.
  • It is fast and pretty accurate with different lightning conditions.

Speed assist

  • When new speed limit is detected it is added as current speed limit.
  • After every frame script compares current speed to current speed limit.
  • Script runs specified command when overspeeding (e.g. "beep").

Requirements

Required software

  • OpenCV >=3.0.0
  • Python >=2.7.3
  • LibAV >=0.8.10
  • (optional) gpsd && python-gps
  • (optional) V4L2 1.0.1

Required hardware

  • Webcam or some other video-source.
  • (optional) GPS Module BU-353

阅读全文……

标签 : ,

淘宝搜索优化的本质(上) - IT观察 - 我的 Blog博客

文/鬼脚七

【导读】今天文章介绍搜索优化的本质,一切方法都会过时,但了解了本质,无论环境如何变,方法都会出现。本文带有一定技术型,不在淘宝做电商的朋友就不要看了。

淘宝搜索流量对卖家的重要性不用多说,如果一个商家失去了搜索流量,在淘宝上基本上被判处死刑,是立即执行还是缓期半年都无所谓,总之活不了了。

淘宝搜索的算法很复杂,复杂的连工程师自己也说不清楚,因为后台已经不是简单的线性公式,而是用机器学习的方法,来拟合各种参数,从而达到预先设定的目标。

搜索的目标是什么?这个很重要,如果你的优化策略符合搜索的目标,那么这个方法就是对的,是长远的,如果不符合,那么这个方式迟早要出问题。搜索的目标有三个:

帮助买家快速找到想要的商品和服务;

建立相对公平的卖家竞争机制;

让淘宝平台有健康长远的发展。

当然最重要的是第一个目标。不满足买家,一切都是空谈。

既然搜索那么重要,但搜索背后的算法那么复杂,搜索目标又有多个,那如何做搜索优化?这是所有卖家都关心的问题。

以前有人问我搜索优化的方法,我还很详细的介绍方法,后来我觉得那些方法都没有用。因为任何方法都会过时,现在告诉你,过不了两三个星期,这些技巧就不能用了。什么叫技巧?当所有人都知道了以后,技巧就不是技巧了。

有没有某个方法是长期有效的,淘宝搜索优化的本质又是什么?这是个非常好的问题。知道了搜索优化的本质,就能以不变应万变,自然能找到长期有效的优化办法。

讲搜索优化的本质之前,要先讲竞争的相对性。

所谓的搜索优化,其实就是跟其他卖家竞争。既然是竞争,就不是绝对的,而是相对的。

鬼脚七身高177cm,我去南方很多城市,发现自己算长得比较高,如果我去北方很多城市,会发现自己顶多算一个中等身材。到底鬼脚七算长得高,还是算长得不高?

淘宝的商品也是一样。销量50件的男士牛仔裤,价格在199元,这个算是贵还是便宜,销量算多还是少,搜索排名会靠前还是靠后?答案是:这个宝贝跟一些没有销量的排在一起,就是靠前,跟有几千件销量的商品排在一起,就会靠后。

或许这个时候,你稍微懂了一点。什么是淘宝搜索优化的本质?

淘宝搜索优化的本质是:选择合适的竞争对手。

淘宝平台上,已经有超过1000万的卖家。分布在任何一个类目,哪怕这个类目再小,也有近十万的卖家,稍微大一点的类目,有几十万的卖家。正是因为卖家数越来越多,很多卖家觉得在淘宝上做生意越来越难,赚钱越来越难。但淘宝有一个很科学的机制:商品上下架时间。

名词解释:商品上下架时间。商品上架时间是指商品发布的时间,商品的下架时间,只是商品上架后七天的同一时间。虽然买家和卖家都看不见,但系统后台会计算。

淘宝的默认搜索,在排序的时候,会自然按照一定时间内下架的商品来做为一个集合,然后在这个集合中进行排序。先举个例子:

“连衣裙”这个词在淘宝上有2000万个商品,如果你是一个淘宝卖家卖一款连衣裙,那么你是在跟这2000万商品竞争么?不是的。因为在用户搜索“连衣裙”的某个具体时刻,排在前100页的商品,一定是最近10分钟下架的那些“连衣裙”商品。如果平均分配,那么每个时间段平均分配的商品是:

2000万/(7*24*6)=2万商品。

也就是说,你的连衣裙不用和2000万商品竞争,只需要和2万的商品竞争。连衣裙是一个大类目商品,如果再多添加一些关键词,例如:“白色连衣裙”只有456万商品,那么你的连衣裙同时竞争的商品只有:

456万/(7*24*6)=4560个。

这就是商品上下架时间自然划分的竞争区间。

在淘宝上,每个时刻搜索排名都是变化的,你的商品都是在和不同的竞争对手在竞争。当然,每个搜索结果选择的下架时间段,其实不是固定的,有的是10分钟,有的是半个小时。根据商品量的多少来做定义,原则是必须保证有一定数量的商品做比较。

所以,无论你的商品自己的条件如何,淘宝上一定有一些商品比你的商品条件好,也一定有一些商品比你的商品条件差。如果你希望搜索排名靠前,你就必须要选择合适的竞争对手。就像鬼脚七身高177cm,如果我希望身高排名靠前,我就应该选择去一些南方城市,这是一个道理。

知道了这个原理,如何选择竞争对手?原则是:避免和那些竞争力强的商品在同一个时间段下架。

竞争力强的商品指的是:销量大,评分高,好评率高,转化率高,宝贝图片和详情页设计都很漂亮的商品。这种商品很好发现,你搜索一下,去看看详情页就知道了。

如果你的牛仔裤卖了40件,就要尽量避免和那些卖了500件、2000件的商品在同一时间段下架。如果你们在一起下架,一方面你的商品很难排序在首页,就算你的其他条件很好,也排序到了首页,也很难获得流量。

再举个例子:

搜索“男士牛仔裤”,有115万个搜索结果,首页有44个宝贝,别人家的宝贝大多卖了500件,你的只有30人成交,别人牛仔裤价格卖159元,你的牛仔裤卖99元,图片一样,材质一样。这时候,买家大多还是选择那个卖了500多件的,因为他们会觉得你的可能是假货。为什么?买家心理学有一条:损失规避心理。为了避免损失,买家还是会选择大家选择的那个。

明白了吗?消费者,绝大多数时候,都不会那么理性。

还有个问题:怎么知道那些竞争力强的商品是什么时候下架呢?

这个问题也不难,就是有些繁琐。你需要经常去搜索你产品相关的词,这样看看那些排序在首页的是哪些宝贝,然后尽量避免和他们在一起。当然最好是使用工具。我知道有个很不错的工具:老A工具箱。里面有很强大的上下架时间分析功能。放心,那个工具是免费的 http://13980.com/soft.html)。应该也有别的工具,只是我不太了解。

7天有将近1000个下架时间段,总有一些下架时间段是竞争力不强的,这时候,你可以选择这些时间段。就算你个子不高,你去了矮人国,你会发现你也是高富帅。

搜索优化就这么简单么?

是的,本质就这么简单。我想起一个故事:

两个人在森林里散步,这时发现远处过来一头熊。其中一人赶紧蹲下来系鞋带。另一人不解,问:你以为你能跑过狗熊?答曰:我只要跑过你就好。

其实所有竞争都是如此,选择合适的竞争对手。

搜索优化真的就这么简单么?

也不是。本质简单,知道了本质,如何应用,还有诸多方法。本文主要介绍了通过上下架时间来选择合适的竞争对手。等着看下篇吧,我会继续告诉大家如何通过标题、类目、关键词等来教大家选择不同的竞争对手。

—-

【七哥闲谈】

今天的文章相信对很多卖家朋友会有帮助,同样也肯定有人不太知道该怎么做,这都没关系,下一篇文章继续讲。是明天还是后天,不确定啊,反正不是明天就是后天,嘻嘻。

经常有人问我,向做店铺转让,或者要买一个店铺,我不专业。今天我给你推荐一个专业的:淘宝天猫店铺的交易平台——舞泡网,专业做店铺的转让交易。上次和他们老板盛总见面,聊了一下,发现这个市场真的蛮大的,他们也很专业。

如果你想开天猫商城又不想走那些麻烦的申请流程,或者你想转让店铺又不认识靠谱的渠道,可以加舞泡网店铺转让的微信号:shwupao,备注七哥推荐。也可以打他们的客服电话400-059-8889。

 

阅读全文……

标签 : ,

淘宝搜索优化的本质(下) - IT观察 - 我的 Blog博客

文/鬼脚七

【导读】本文是淘宝搜索优化的本质下篇,着重解释了如何做标题优化、多维度优化、广告流量的优化等。在看这篇之前,你需要在微信taobaoguijiaoqi后台回复535查看。这篇文章适合做电商的朋友看。

接着讲搜索优化的本质:选择合适的竞争对手。

公式的问题

淘宝搜索的上下架时间,把整个卖家按照时间段分成了上千个区间。每个卖家不是在和同类目的所有卖家竞争,而是跟自己商品同一时间下架的商品竞争。

上篇文章(微信taobaoguijiaoqi中回复532查看)中有个公式:

7*24*6,有人不明白是代表什么意思。这里解释一下:

7:代表7天,是每个商品从上架的那一天开始算,第7天后的同一时间下架,然后再自动上架。(当然之前也有14天的,这里不做讨论)

24:代表一天有24个小时。

虽然第二个词的流量没有第一个词流量大,但是相对某个卖家来说,那个流量还是可观的,如果你能占居豆腐块的位置,就更不得了了。

标题中选择不同的词,就是在选择不同的竞争对手。

多维度优化

在淘宝搜索结果页上,有很多选项,用的比较多的有那些单维度排序、类目属性、价格区间等。选项的不同,也决定了有不同的竞争对手。

以前有个朋友,她在夏天每周都能卖出去两三件皮衣,我问她原因,她告诉我说,她的皮衣商品价格很高,都是8000以上,一般是别人找不到的,但发现搜索皮衣,按照价格从高往低排序的时候,她的商品就排在前三位。

由于淘宝搜索结果页流量很大,每个选项都会有人点击。价格从高往低排列,也有人习惯如此。点击再少,一般也是几万的数量级。几万的UV,对于某个商家来说,已经是很可观的流量了。

明白了么?不要把眼睛只盯着默认搜索结果页,在那里竞争最激烈,但在其它维度和选项的时候,你会发现竞争根本没有那么激烈。

每增加一个选项,就换了一拨竞争对手。

还有一个相对容易优化的是宝贝属性的优化。

在搜索结果页的上部分,有类目和属性的选项,每次用户的选择,都会过滤掉很多商品。你看看那些实力强的商品,看能否可以错开和他们卖的属性,直接竞争。

最贵的是最便宜的

有一次演讲,我问大家,有谁想要免费的搜索流量?所有人都举手了。

有谁愿意每天至少花1000元买广告流量?有一少半人举手了。

有谁愿意每天至少花1万元买广告流量?只有几个人举手。

有谁愿意每天花5万元买广告流量?没有人举手了。

为什么?你会说,这很正常啊,因为大家没有那么多钱。

虽然淘宝的流量也是有限的,但相对于每个卖家来说,淘宝还是相当于一个大湖。你挑几桶水出来,影响不大。理解了这个观点,你再来看上面的问题。

如果你希望不花钱获得流量,你是在跟所有人竞争,也就是上千万的淘宝卖家竞争。

如果你每天花1000元,你已经过滤掉大部分竞争对手了。

如果你每天花1万元,你得竞争对手会少很多很多!

当然,花钱的前提还是需要打好基础,否则花钱就是烧钱,没有意义!

从这个角度看,也能明白当今社会的一个现象:富人越富,穷人越穷。

如果你有足够好的基础,我还是建议你采用花钱的方式去做,这样可以自然加快速度。

如果你不愿意花钱,你只是觉得要获得免费流量,那么你就需要花更多的时间,你需要每天琢磨优化方法,你需要考虑刷单,你有太多的风险需要控制。你回头会发现,这些免费的流量,一点都不便宜…….用两句话概括:

免费的,就是最贵的!

最贵的,就是最便宜的!

无论从销量、上下架时间、标题、多维度排序,还是花钱买广告流量,不同的策略,会选择不同的竞争对手,选择不同的竞争对手,就会决定你的搜索排名位置。

搜索算法在不停的更新,竞争环境也在不断的更新,所有的技巧都会过时。一旦你理解了搜索优化的本质,就自然知道如何面对这些变化,也就自然能掌握了所有优化技巧。

因此,记住淘宝搜索优化的本质:

选择合适的竞争对手。

6:代表每个小时有6个10分钟。(如果以10分钟作为下架时间的区间的话)

7*24*6=1008.

这个数字代表,总共会有1008个下架时间段。当然每个下架时间段不是独立分割的,是连续的。每个商品前后20分钟都会参与竞争。

标题优化

讲完下架时间和销量,接下来讲标题优化。

标题优化是所有搜索优化中,见效最快的。应该在10分钟以内就会看见效果,因为淘宝后台的数据更新是在3分钟左右,加上缓存、机房同步和数据切换等,10分钟肯定是可以看见效果的。

标题只可以写32个汉字,这32个汉字非常关键,以为淘宝搜索主要的搜索数据,都来自于标题。所以选那些词就成了关键。标题选不同的词,决定了你会选择不同的竞争对手。

淘宝的标题有两个作用:一方面是写给买家看的,一方面是写给搜索引擎看的。淘宝最近还推出了商品买点描述功能,这个功能以后再谈。

如何做标题优化?

淘宝上有很多热门词,例如:蚕丝被、连衣裙、牛仔裤等。只要是跟类目相关的短词,基本上都是热门词。热门词流量大,很多买家会搜索,淘宝的小二也会在不同的活动时间推荐一些关键词。导致很多卖家会把这些词都堆积到标题中。

这时问题就来了,虽然放了很多热门词,但仍然没有流量。其原因不是标题写得不好,而是因为你选择的竞争对手太强。

竞争实力强的商品,标题都用了热门词,如果你的商品综合评分不高,你的热门词放在标题里面,就是浪费资源。因为就算搜索到了,排名也不会靠前。

举个例子:你的牛仔裤才买了30件,评分也一般。你用了一个热门词“韩版牛仔裤”,这个时候,你很难排序靠前。还有人想抢豆腐块,更不可能了。但是如果你用其他词,说不定就可以。例如:“小脚超薄牛仔裤”。豆腐块的上商品的销量才8件,你30件,一定有可能超过它。

虽然第二个词的流量没有第一个词流量大,但是相对某个卖家来说,那个流量还是可观的,如果你能占居豆腐块的位置,就更不得了了。

标题中选择不同的词,就是在选择不同的竞争对手。

多维度优化

在淘宝搜索结果页上,有很多选项,用的比较多的有那些单维度排序、类目属性、价格区间等。选项的不同,也决定了有不同的竞争对手。

以前有个朋友,她在夏天每周都能卖出去两三件皮衣,我问她原因,她告诉我说,她的皮衣商品价格很高,都是8000以上,一般是别人找不到的,但发现搜索皮衣,按照价格从高往低排序的时候,她的商品就排在前三位。

由于淘宝搜索结果页流量很大,每个选项都会有人点击。价格从高往低排列,也有人习惯如此。点击再少,一般也是几万的数量级。几万的UV,对于某个商家来说,已经是很可观的流量了。

明白了么?不要把眼睛只盯着默认搜索结果页,在那里竞争最激烈,但在其它维度和选项的时候,你会发现竞争根本没有那么激烈。

每增加一个选项,就换了一拨竞争对手。

还有一个相对容易优化的是宝贝属性的优化。

在搜索结果页的上部分,有类目和属性的选项,每次用户的选择,都会过滤掉很多商品。你看看那些实力强的商品,看能否可以错开和他们卖的属性,直接竞争。

最贵的是最便宜的

有一次演讲,我问大家,有谁想要免费的搜索流量?所有人都举手了。

有谁愿意每天至少花1000元买广告流量?有一少半人举手了。

有谁愿意每天至少花1万元买广告流量?只有几个人举手。

有谁愿意每天花5万元买广告流量?没有人举手了。

为什么?你会说,这很正常啊,因为大家没有那么多钱。

虽然淘宝的流量也是有限的,但相对于每个卖家来说,淘宝还是相当于一个大湖。你挑几桶水出来,影响不大。理解了这个观点,你再来看上面的问题。

如果你希望不花钱获得流量,你是在跟所有人竞争,也就是上千万的淘宝卖家竞争。

如果你每天花1000元,你已经过滤掉大部分竞争对手了。

如果你每天花1万元,你得竞争对手会少很多很多!

当然,花钱的前提还是需要打好基础,否则花钱就是烧钱,没有意义!

从这个角度看,也能明白当今社会的一个现象:富人越富,穷人越穷。

如果你有足够好的基础,我还是建议你采用花钱的方式去做,这样可以自然加快速度。

如果你不愿意花钱,你只是觉得要获得免费流量,那么你就需要花更多的时间,你需要每天琢磨优化方法,你需要考虑刷单,你有太多的风险需要控制。你回头会发现,这些免费的流量,一点都不便宜…….用两句话概括:

免费的,就是最贵的!

最贵的,就是最便宜的!

无论从销量、上下架时间、标题、多维度排序,还是花钱买广告流量,不同的策略,会选择不同的竞争对手,选择不同的竞争对手,就会决定你的搜索排名位置。

搜索算法在不停的更新,竞争环境也在不断的更新,所有的技巧都会过时。一旦你理解了搜索优化的本质,就自然知道如何面对这些变化,也就自然能掌握了所有优化技巧。

因此,记住淘宝搜索优化的本质:

选择合适的竞争对手。

阅读全文……

标签 : ,

切片和切块 钻取 旋转 - honkcal - 博客园

1 切片和切块(Slice and Dice)
在多维数据结构中,按二维进行切片,按三维进行切块,可得到所需要的数据。如在“贷
款银行、贷款质量、时间”三维立方体中进行切块和切片,可得到各贷款银行、各种贷款的
统计情况。每次都是沿其中一维进行分割称为分片,每次沿多维进行的分片称为分块。

2、钻取(Drill)
钻取包含向下钻取(Drill-down)和向上钻取(Drill-up)/上卷(Roll-up)操作, 钻取的深
度与维所划分的层次相对应。

3 旋转(Rotate)/转轴(Pivot)
通过旋转可以得到不同视角的数据

阅读全文……

标签 : ,

美国中学是否存在反智主义倾向?如是,它的根源是什么? - 知乎

我女儿在美国读小学三年级,她很聪明,也爱读书。但她很反感大人表扬她聪明,只要有人这么表扬她,她就会喊,“我不是'know it all'". 他们班有个书呆气学习很好的男生,外号就是“know it all".

我观察,不是所有的学习好的学生都被嘲笑,但学习好体育不太好的孩子必定被嘲笑。如果学习好,体育不好,社交能力又欠缺的孩子更是学校最底层。美国的学校的社交压力实在太大,没有朋友的孩子很难生存。感觉女儿在想方设法避免一切可能被嘲笑或者与众不同的衣着,说话和行为,可惜她这张中国人的脸改变不了,有时候觉得孩子真的很累。

所以我觉得美国学校未必是反智主义,而是过于崇拜社交主义。社交崇拜又源于成人对青少年的无为而治。因为美国的学校和家长对学生管理松散,干涉少,不允许排名次和用学习成绩差别对待学生,使得学生过早形成了自我管理的小等级社会。在青少年的等级社会里,朋友是最重要的资源,人缘好朋友多的人自然被人羡慕,站在金字塔顶端。体育好的孩子往往能带上很多人一起玩,朋友资源多,而学习好在这个等级社会里不能带来任何资源,甚至可能会妨碍社交,权重最低。

在中国则不同,我中学的时候学习好不合群,但好处占尽,老师学校给的荣誉,班级学校学生干部,各种出风头的福利等等。所以中国学校也有等级社会,这个等级是学校老师和家长的干涉下形成的,学习好的学生获得最多成人资源而在金字塔顶端。在一些不重视学习的“差学校”,技校职校等家长老师放弃管理的地方,类似美国的学生等级社会也存在。

即使在美国,到孩子长大了,就不一样了,智力资源开始变得重要。成人社会是逐利的社会,能带来利润的人能获得重视,这样学习好的人就有了一定优势。当然学习和人缘都好的优势更大。而体育除了少数明星外,带来的资源就很有限了。

另外,美国的私立精英学校也有所不同。这种学校一个班只有七八个学生,老师多,管理严格,家长花了大价钱投资智力也期待回报,因为成人干涉多学生的自由相对公立学校要少,前面说的等级社会就失去了存在土壤。

阅读全文……

标签 : ,

关于Redis的常识 Redis · springside/springside4 Wiki · GitHub

1. Overview

1.1 资料

1.2 优缺点

非常非常的快,有测评说比Memcached还快(当大家都是单CPU的时候),而且是无短板的快,读写都一般的快,所有API都差不多快,也没有MySQL Cluster、MongoDB那样更新同一条记录如Counter时慢下去的毛病。

丰富的数据结构,超越了一般的Key-Value数据库而被认为是一个数据结构服务器。组合各种结构,限制Redis用途的是你自己的想象力,作者自己捉刀写的用途入门

因为是个人作品,Redis2.6版只有2.3万行代码,Keep it simple的死硬做法,使得普通公司而不需淘宝那个级别的文艺公司也可以吃透它。 Redis宣言就是作者的自白,我最喜欢其中的“代码像首诗”,”设计是一场与复杂性的战斗“,“Coding是一件艰苦的事情,唯一的办法是享受它。如果它已不能带来快乐就停止它。为了防止这一天的出现,我们要尽量避免把Redis往乏味的路上带。”

让人又爱又恨的单线程架构,使得代码不用处理平时最让人头痛的并发而大幅简化,也不用老是担心作者的并发有没有写对,但也带来单CPU的瓶颈,而且单线程被慢操作所阻塞时,其他请求的延时变得不确定。

那Redis不是什么?

  • Redis 不是Big Data,数据都在内存中,无法以T为单位。
  • 在Redis 3.0的Redis-Cluster发布并被稳定使用之前,Redis没有真正的平滑水平扩展能力。
  • Redis 不支持Ad-Hoc Query,提供的只是数据结构的API,没有SQL一样的查询能力。

1.3 Feature速览

  • 所有数据都在内存中。
  • 五种数据结构:String / Hash / List / Set / Ordered Set。
  • 数据过期时间支持。
  • 不完全的事务支持。
  • 服务端脚本:使用Lua Script编写,作用类似存储过程。
  • PubSub:捞过界的消息一对多发布订阅功能,起码Redis-Sentinel在使用它。
  • 持久化:支持定期导出内存的Snapshot 与 记录写操作日志的Append Only File两种模式。
  • Replication:Master-Slave模式,Master可连接多个只读Slave,Geographic Replication也只支持Active-Standby。
  • Fail-Over:Redis-Sentinel节点负责监控Master节点,在master失效时提升slave。
  • Sharding:开发中的Redis-Cluser。
  • 动态配置:所有参数可用命令行动态配置不需重启,2.8版可以重新写回配置文件中,对云上的大规模部署非常合适。

1.4 八卦

  • 作者是意大利的Salvatore Sanfilippo(antirez),又是VMWare大善人聘请了他专心写Redis。
  • EMC与VMWare将旗下的开源产品如Redis和Spring都整合到了孙公司Pivotal公司。
  • Pivotal做的antirez访谈录,内含一切八卦,比如他的爱好是举重、跑步和品红酒。
  • 默认端口6379,是手机按键上MERZ对应的号码,意大利歌女Alessia Merz是antirez和朋友们认为愚蠢的代名词。

2. 数据结构

2.1 Key

  • Key 不能太长,比如1024字节,但antirez也不喜欢太短如"u:1000:pwd",要表达清楚意思才好。他私人建议用":"分隔域,用"."作为单词间的连接,如"comment:12345:reply.to"。
  • Keys,返回匹配的key,支持通配符如 "keys a*" 、 "keys a?c",但不建议在生产环境大数据量下使用。
  • SCAN命令,针对Keys的改进,支持分页查询Key。在迭代过程中,Keys有增删怎么办?要锁定写操作么?--不会,不做任何保证,撞大运,甚至同一条key可能会被返回多次。
  • Sort,对集合按数字或字母顺序排序后返回或另存为list,还可以关联到外部key等。因为复杂度是最高的O(N+M*log(M))(N是集合大小,M 为返回元素的数量),有时会安排到slave上执行。
  • Expire/ExpireAt/Persist/TTL,关于Key超时的操作。默认以秒为单位,也有p字头的以毫秒为单位的版本, Redis的内部实现见2.9 过期数据清除。

2.2 String

最普通的key-value类型,说是String,其实是任意的byte[],比如图片,最大512M。 所有常用命令的复杂度都是O(1),普通的Get/Set方法,可以用来做Cache,存Session,为了简化架构甚至可以替换掉Memcached。

Incr/IncrBy/IncrByFloat/Decr/DecrBy,可以用来做计数器,做自增序列。key不存在时会创建并贴心的设原值为0。IncrByFloat专门针对float,没有对应的decrByFloat版本?用负数啊。

SetNx, 仅当key不存在时才Set。可以用来选举Master或做分布式锁:所有Client不断尝试使用SetNx master myName抢注Master,成功的那位不断使用Expire刷新它的过期时间。如果Master倒掉了key就会失效,剩下的节点又会发生新一轮抢夺。

其他Set指令:

  • SetEx, Set + Expire 的简便写法,p字头版本以毫秒为单位。
  • GetSet, 设置新值,返回旧值。比如一个按小时计算的计数器,可以用GetSet获取计数并重置为0。这种指令在服务端做起来是举手之劳,客户端便方便很多。
  • MGet/MSet/MSetNx, 一次get/set多个key。
  • 2.6.12版开始,Set命令已融合了Set/SetNx/SetEx三者,SetNx与SetEx可能会被废弃,这对Master抢注非常有用,不用担心setNx成功后,来不及执行Expire就倒掉了。可惜有些懒惰的Client并没有快速支持这个新指令。

GetBit/SetBit/BitOp,与或非/BitCount, BitMap的玩法,比如统计今天的独立访问用户数时,每个注册用户都有一个offset,他今天进来的话就把他那个位设为1,用BitCount就可以得出今天的总人数。

Append/SetRange/GetRange/StrLen,对文本进行扩展、替换、截取和求长度,只对特定数据格式如字段定长的有用,json就没什么用。

2.3 Hash

Key-HashMap结构,相比String类型将这整个对象持久化成JSON格式,Hash将对象的各个属性存入Map里,可以只读取/更新对象的某些属性。这样有些属性超长就让它一边呆着不动,另外不同的模块可以只更新自己关心的属性而不会互相并发覆盖冲突。

另一个用法是土法建索引。比如User对象,除了id有时还要按name来查询。可以有如下的数据记录:

  • (String) user:101 -> {"id":101,"name":"calvin"...}
  • (String) user:102 -> {"id":102,"name":"kevin"...}
  • (Hash) user:name:index-> "calvin"->101, "kevin" -> 102

底层实现是hash table,一般操作复杂度是O(1),要同时操作多个field时就是O(N),N是field的数量。

2.4 List

List是一个双向链表,支持双向的Pop/Push,江湖规矩一般从左端Push,右端Pop——LPush/RPop,而且还有Blocking的版本BLPop/BRPop,客户端可以阻塞在那直到有消息到来,所有操作都是O(1)的好孩子,可以当Message Queue来用。当多个Client并发阻塞等待,有消息入列时谁先被阻塞谁先被服务。任务队列系统Resque是其典型应用。

还有RPopLPushBRPopLPush,弹出来返回给client的同时,把自己又推入另一个list,LLen获取列表的长度。

还有按值进行的操作:LRem(按值删除元素)、LInsert(插在某个值的元素的前后),复杂度是O(N),N是List长度,因为List的值不唯一,所以要遍历全部元素,而Set只要O(log(N))。

按下标进行的操作:下标从0开始,队列从左到右算,下标为负数时则从右到左。

  • LSet ,按下标设置元素值。
  • LIndex,按下标返回元素。
  • LRange,不同于POP直接弹走元素,只是返回列表内一段下标的元素,是分页的最爱。
  • LTrim,限制List的大小,比如只保留最新的20条消息。

复杂度也是O(N),其中LSet的N是List长度,LIndex的N是下标的值,LRange的N是start的值+列出元素的个数,因为是链表而不是数组,所以按下标访问其实要遍历链表,除非下标正好是队头和队尾。LTrim的N是移除元素的个数。

在消息队列中,并没有JMS的ack机制,如果消费者把job给Pop走了又没处理完就死机了怎么办?

  • 解决方法之一是加多一个sorted set,分发的时候同时发到list与sorted set,以分发时间为score,用户把job做完了之后要用ZREM消掉sorted set里的job,并且定时从sorted set中取出超时没有完成的任务,重新放回list。
  • 另一个做法是为每个worker多加一个的list,弹出任务时改用RPopLPush,将job同时放到worker自己的list中,完成时用LREM消掉。如果集群管理(如zookeeper)发现worker已经挂掉,就将worker的list内容重新放回主list。

2.5 Set

Set就是Set,可以将重复的元素随便放入而Set会自动去重,底层实现也是hash table

2.6 Sorted Set

有序集,元素放入集合时还要提供该元素的分数,默认从小到大排列。

Sorted Set的实现是hash table(element->score, 用于实现ZScore及判断element是否在集合内),和skip list(score->element,按score排序)的混合体。 skip list有点像平衡二叉树那样,不同范围的score被分成一层一层,每层是一个按score排序的链表。

ZAdd/ZRem是O(log(N)),ZRangeByScore/ZRemRangeByScore是O(log(N)+M),N是Set大小,M是结果/操作元素的个数。可见,原本可能很大的N被很关键的Log了一下,1000万大小的Set,复杂度也只是几十不到。当然,如果一次命中很多元素M很大那谁也没办法了。

2.7 事务

Multi(Start Transaction)、Exec(Commit)、Discard(Rollback)实现。 在事务提交前,不会执行任何指令,只会把它们存到一个队列里,不影响其他客户端的操作。在事务提交时,批量执行所有指令。《Redis设计与实现》中的详述

注意,Redis里的事务,与我们平时的事务概念很不一样:

  • 它仅仅是保证事务里的操作会被连续独占的执行。因为是单线程架构,在执行完事务内所有指令前是不可能再去同时执行其他客户端的请求的。
  • 它没有隔离级别的概念,因为事务提交前任何指令都不会被实际执行,也就不存在"事务内的查询要看到事务里的更新,在事务外查询不能看到"这个让人万分头痛的问题。
  • 它不保证原子性——所有指令同时成功或同时失败,只有决定是否开始执行全部指令的能力,没有执行到一半进行回滚的能力。在redis里失败分两种,一种是明显的指令错误,比如指令名拼错,指令参数个数不对,在2.6版中全部指令都不会执行。另一种是隐含的,比如在事务里,第一句是SET foo bar, 第二句是LLEN foo,对第一句产生的String类型的key执行LLEN会失败,但这种错误只有在指令运行后才能发现,这时候第一句成功,第二句失败。还有,如果事务执行到一半redis被KILL,已经执行的指令同样也不会被回滚。

Watch指令,类似乐观锁,事务提交时,如果Key的值已被别的客户端改变,比如某个list已被别的客户端push/pop过了,整个事务队列都不会被执行。

2.8 Lua Script

Redis2.6内置的Lua Script支持,可以在Redis的Server端一次过运行大量逻辑,就像存储过程一样,避免了海量中间数据在网路上的传输。

  • Lua自称是在Script语言里关于快的标准,Redis选择了它而不是流行的JavaScript。
  • 因为Redis的单线程架构,整个Script默认是在一个事务里的。
  • Script里涉及的所有Key尽量用变量,从外面传入,使Redis一开始就知道你要改变哪些key,为了日后做水平分区做准备。如果涉及的key在不同服务器......
  • Eval每次传输一整段Script比较费带宽,可以先用Script Load载入script,返回哈希值。然后用EvalHash执行。因为就是SHA-1,所以任何时候执行返回的哈希值都是一样的。Replicate时,2.6版还是会传整段Script到Slave,2.8版改进了。
  • 内置的Lua库里还很贴心的带了CJSON,可以处理json字符串。
  • Script一旦执行则不容易中断,中断了也会有不可知后果,因此最好在开发环境充分测试了再上线。
  • 一段用Redis做Timer的示例代码,下面的script被定期调用,从以触发时间为score的sorted set中取出已到期的Job,放到list中给Client们blocking popup。
-- KEYS: [1]job:sleeping, [2]job:ready -- ARGS: [1]currentTime -- Comments: result is the  job id local jobs=redis.call('zrangebyscore', KEYS[1], '-inf', ARGV[1]) local count = table.maxn(jobs)  if count>0  then   -- Comments: remove from Sleeping Job sorted set   redis.call('zremrangebyscore', KEYS[1], '-inf', ARGV[1])    -- Comments: add to the Ready Job list   -- Comments: can optimize to use lpush id1,id2,... for better performance   for i=1,count do      redis.call('lpush', KEYS[2], jobs[i])   end end 

2.9 过期数据清除

官方文档 与 《Redis设计与实现》中的详述,过期数据的清除从来不容易,为每一条key设置一个timer,到点立刻删除的消耗太大,每秒遍历所有数据消耗也大,Redis使用了一种相对务实的做法:

当client主动访问key会先对key进行超时判断,过时的key会立刻删除。

如果clien永远都不再get那条key呢? 它会在Master的后台,每秒10次的执行如下操作: 随机选取100个key校验是否过期,如果有25个以上的key过期了,立刻额外随机选取下100个key(不计算在10次之内)。可见,如果过期的key不多,它最多每秒回收200条左右,如果有超过25%的key过期了,它就会做得更多,但只要key不被主动get,它占用的内存什么时候最终被清理掉只有天知道。

3. 性能

3.1 测试结果

  • 测试环境: RHEL 6.3 / HP Gen8 Server/ 2 * Intel Xeon 2.00GHz(6 core) / 64G DDR3 memory / 300G RAID-1 SATA / 1 master(writ AOF), 1 slave(write AOF & RDB)
  • 数据准备: 预加载两千万条数据,占用10G内存。
  • 测试工具:自带的redis-benchmark,默认只是基于一个很小的数据集进行测试,调整命令行参数如下,就可以开100条线程(默认50),SET 1千万次(key在0-1千万间随机),key长21字节,value长256字节的数据。
redis-benchmark -t SET -c 100 -n 10000000 -r 10000000 -d 256  
  • 测试结果(TPS): 1.SET:4.5万, 2.GET:6万 ,3.INCR:6万,4.真实混合场景: 2.5万SET & 3万GET
  • 单条客户端线程时6千TPS,50与100条客户端线程差别不大,200条时会略多。
  • Get/Set操作,经过了LAN,延时也只有1毫秒左右,可以反复放心调用,不用像调用REST接口和访问数据库那样,每多一次外部访问都心痛。
  • 资源监控:
    1.CPU: 占了一个处理器的100%,总CPU是4%(因为总共有2CPU*6核*超线程 = 24个处理器),可见单线程下单处理器的能力是瓶颈。 AOF rewrite时另一个处理器占用50-70%。
    2.网卡:15-20 MB/s receive, 3Mb/s send(no slave) or 15-20 MB/s send (with slave) 。当把value长度加到4K时,receive 99MB/s,已经到达千兆网卡的瓶颈,TPS降到2万。
    3.硬盘:15MB/s(AOF append), 100MB/s(AOF rewrite/AOF load,普通硬盘的瓶颈),

3.2 为什么快

  • 纯ANSI C编写。
  • 不依赖第三方类库,没有像memcached那样使用libevent,因为libevent迎合通用性而造成代码庞大,所以作者用libevent中两个文件修改实现了自己的epoll event loop。微软的兼容Windows补丁也因为同样原因被拒了。
  • 快,原因之一是Redis多样的数据结构,每种结构只做自己爱做的事,当然比数据库只有Table,MongogoDB只有JSON一种结构快了。
  • 可惜单线程架构,虽然作者认为CPU不是瓶颈,内存与网络带宽才是。但实际测试时并非如此,见上。

3.3 性能调优

  • 官方文档关于各种产生Latency的原因的详细分析中文版
  • 正视网络往返时间:
    1.MSet/LPush/ZAdd等都支持一次输入多个Key。
    2.PipeLining模式 可以一次输入多个指令。在Jedis的实现里,所有指令先在本地的buffer中存着,直到调用sync。
    3.更快的是Lua Script模式,还可以包含逻辑,直接在服务端又get又set的,见2.8 Lua Script。
  • 发现执行缓慢的命令,可配置执行超过多少时间的指令算是缓慢指令(默认10毫秒,不含IO时间),可以用slowlog get 指令查看(默认只保留最后的128条)。单线程的模型下,一个请求占掉10毫秒是件大事情,注意设置和显示的单位为微秒。
  • CPU永远是瓶颈,但top看到单个CPU 100%时,就是垂直扩展的时候了。
  • 持久化对性能的影响很大,见5.1持久化。
  • 要熟悉各指令的复杂度,不过只要不是O(N)一个超大集合,都不用太担心。

4. 容量

4.1 最大内存

  • 所有的数据都必须在内存中,原来2.0版的VM策略(将Value放到磁盘,Key仍然放在内存),2.4版后嫌麻烦又不支持了。
  • 一定要设置最大内存,否则物理内存用爆了就会大量使用Swap,写RDB文件时的速度慢得你想死。
  • 多留一倍内存是最安全的。重写AOF文件和RDB文件的进程(即使不做持久化,复制到Slave的时候也要写RDB)会fork出一条新进程来,采用了操作系统的Copy-On-Write策略(子进程与父进程共享Page。如果父进程的Page-每页4K有修改,父进程自己创建那个Page的副本,不会影响到子进程,父爱如山)。留意Console打出来的报告,如"RDB: 1215 MB of memory used by copy-on-write"。在系统极度繁忙时,如果父进程的所有Page在子进程写RDB过程中都被修改过了,就需要两倍内存。
  • 按照Redis启动时的提醒,设置 vm.overcommit_memory = 1 ,使得fork()一条10G的进程时,因为COW策略而不一定需要有10G的free memory。
  • 其他需要考虑的内存包括:
    1.AOF rewrite过程中对新写入命令的缓存(rewrite结束后会merge到新的aof文件),留意"Background AOF buffer size: 80 MB"的字样。
    2.负责与Slave同步的Client的缓存,默认设置master需要为每个slave预留不高于256M的缓存(见5.1持久化)。
  • 当最大内存到达时,按照配置的Policy进行处理, 默认策略为volatile-lru,对设置了expire time的key进行LRU清除(不是按实际expire time)。如果沒有数据设置了expire time或者policy为noeviction,则直接报错,但此时系统仍支持get之类的读操作。 另外还有几种policy,比如volatile-ttl按最接近expire time的,allkeys-lru对所有key都做LRU。见Redis的文档:Redis as an LRU cache

4.2 内存占用

  • 测试表明,string类型需要90字节的额外代价,就是说key 1个字节,value 1个字节时,还是需要占用92字节的长度,而上面的benchmark的记录就占用了367个字节。其他类型可根据文档自行计算或实际测试一下。
  • 使用jemalloc分配内存,删除数据后,内存并不会乖乖还给操作系统而是被Redis截留下来重用到新的数据上,直到Redis重启。因此进程实际占用内存是看INFO里返回的used_memory_peak_human。
  • Redis内部用了ziplist/intset这样的压缩结构来减少hash/list/set/zset的存储,默认当集合的元素少于512个且最长那个值不超过64字节时使用,可配置。
  • 用make 32bit可以编译出32位的版本,每个指针占用的内存更小,但只支持最大4GB内存。

4.4 水平分区,Sharding,Partition

  • Redis文档:Partitioning
  • 其实,大内存加上垂直分区也够了,不一定非要沙丁一把。
  • Jedis支持在客户端做分区,局限是不能动态re-sharding, 有分区的master倒了,不能减少分区必须用slave顶上。要增加分区的话,呃.....
  • antire在博客里提到了Twemproxy,一个Twitter写的Proxy,但它在发现节点倒掉后,只会重新计算一致性哈希环,把数据存到别的master去,而不是集成Sentinel指向新由slave升级的master,像Memcached一样的做法也只适合做Cache的场景。

Redis-Cluster在3.0版发布,支持自动re-sharding,Redis文档:集群教程

  • 采用和Hazelcast类似的算法,总共有N个分区(eg.N=1024),每台Server负责若干个分区,而且在Server间共享此信息,而不是像Memcached那样每台Server直接在一致性哈希环上占一块地方。
  • 在客户端先用一致性哈希出key 属于哪个分区,随便发给一台server,server会告诉它真正哪个Server负责这个分区,客户端缓存下来,下次还有该分区的请求就直接发到地儿了。
  • Re-sharding时,会将某些分区的数据移到新的Server上,完成后各Server周知分区<->Server映射的变化,因为分区数量有限,所以通讯量不大。 在迁移过程中,客户端缓存的依然是旧的分区映射信息,原server对于已经迁移走的数据的get请求,会返回一个临时转向的应答,客户端先不会更新Cache。等迁移完成了,就会像前面那样返回一条永久转向信息,客户端更新Cache,以后就都去新server了。
  • 目前问题:1. 事务和Lua脚本如果涉及到不同分区的Key如何解决,2. 创建Cluster Node的命令是Ruby写的,要有Ruby才能跑。

5. 高可用性

高可用性关乎系统出错时到底会丢失多少数据,多久不能服务。要综合考虑持久化,Master-Slave复制及Fail-Over配置,以及具体Crash情形,比如Master死了,但Slave没死。或者只是Redis死了,操作系统没死等等。

5.1 持久化

  • 综述: 解密Redis持久化(中文概括版)英文原版,《Redis设计与实现》: RDB 与 AOF
  • 很多人开始会想象两者是互相结合的,即dump出一个snapshot到RDB文件,然后在此基础上记录变化日志到AOF文件。实际上两者毫无关系,完全独立运行,因为作者认为简单才不会出错。如果使用了AOF,重启时只会从AOF文件载入数据,不会再管RDB文件。
  • 正确关闭服务器:redis-cli shutdown 或者 kill,都会graceful shutdown,保证写RDB文件以及将AOF文件fsync到磁盘,不会丢失数据。 如果是粗暴的Ctrl+C,或者kill -9 就可能丢失。

5.1.1 RDB文件

  • RDB是整个内存的压缩过的Snapshot,RDB的数据结构,可以配置复合的快照触发条件,默认是1分钟内改了1万次,或5分钟内改了10次,或15分钟内改了1次。
  • RDB写入时,会连内存一起Fork出一个新进程,遍历新进程内存中的数据写文件,这样就解决了些Snapshot过程中又有新的写入请求进来的问题。 Fork的细节见4.1最大内存。
  • RDB会先写到临时文件,完了再Rename成,这样外部程序对RDB文件的备份和传输过程是安全的。而且即使写新快照的过程中Server被强制关掉了,旧的RDB文件还在。
  • 可配置是否进行压缩,压缩方法是字符串的LZF算法,以及将string形式的数字变回int形式存储。
  • 动态所有停止RDB保存规则的方法:redis-cli config set save ""

5.1.2 AOF文件

  • 操作日志,记录所有有效的写操作,等于mysql的binlog,格式就是明文的Redis协议的纯文本文件。
  • 一般配置成每秒调用一次fdatasync将kernel的文件缓存刷到磁盘。当操作系统非正常关机时,文件可能会丢失不超过2秒的数据(更严谨的定义见后)。 如果设为fsync always,性能只剩几百TPS,不用考虑。如果设为no,靠操作系统自己的sync,Linux系统一般30秒一次。
  • AOF文件持续增长而过大时,会fork出一条新进程来将文件重写(也是先写临时文件,最后再rename,), 遍历新进程的内存中数据,每条记录有一条的Set语句。默认配置是当AOF文件大小是上次rewrite后大小的一倍,且文件大于64M时触发。
  • Redis协议,如set mykey hello, 将持久化成*3 $3 set $5 mykey $5 hello, 第一个数字代表这条语句有多少元,其他的数字代表后面字符串的长度。这样的设计,使得即使在写文件过程中突然关机导致文件不完整,也能自我修复,执行redis-check-aof即可。

综上所述,RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件。那要不要只使用AOF呢?作者建议不要,因为RDB更适合用于备份数据库(AOF在不断变化不好备份),快速重启,而且不会有AOF可能潜在的bug,留着作为一个万一的手段。

5.1.3 读写性能

  • AOF重写和RDB写入都是在fork出新进程后,遍历新进程的内存顺序写的,既不阻塞主进程继续处理客户端请求,顺序写的速度也比随机写快。
  • 测试把刚才benchmark的11G数据写成一个1.3的RDB文件,或者等大的AOF文件rewrite,需要80秒,在redis-cli info中可查看。启动时载入一个AOF或RDB文件的速度与上面写入时相同,在log中可查看。
  • Fork一个使用了大量内存的进程也要时间,大约10ms per GB的样子,但Xen在EC2上是让人郁闷的239ms (KVM和VMWare貌似没有这个毛病),各种系统的对比,Info指令里的latest_fork_usec显示上次花费的时间。
  • 在bgrewriteaof过程中,所有新来的写入请求依然会被写入旧的AOF文件,同时放到buffer中,当rewrite完成后,会在主线程把这部分内容合并到临时文件中之后才rename成新的AOF文件,所以rewrite过程中会不断打印"Background AOF buffer size: 80 MB, Background AOF buffer size: 180 MB",计算系统容量时要留意这部分的内存消耗。注意,这个合并的过程是阻塞的,如果你产生了280MB的buffer,在100MB/s的传统硬盘上,Redis就要阻塞2.8秒!!!
  • NFS或者Amazon上的EBS都不推荐,因为它们也要消耗带宽。
  • bgsave和bgaofrewrite不会被同时执行,如果bgsave正在执行,bgaofrewrite会自动延后。
  • 2.4版以后,写入AOF时的fdatasync由另一条线程来执行,不会再阻塞主线程。
  • 2.4版以后,lpush/zadd可以输入一次多个值了,使得AOF重写时可以将旧版本中的多个lpush/zadd指令合成一个,每64个key串一串。

5.1.4 性能调整

因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条规则。

如果Enalbe AOF,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了。代价一是带来了持续的IO,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上。默认超过原大小100%大小时重写可以改到适当的数值,比如之前的benchmark每个小时会产生40G大小的AOF文件,如果硬盘能撑到半夜系统闲时才用cron调度bgaofrewrite就好了。

如果不Enable AOF ,仅靠Master-Slave Replication 实现高可用性也可以。能省掉一大笔IO也减少了rewrite时带来的系统波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave中的RDB文件,载入较新的那个。新浪微博就选用了这种架构,见Tim的博客

5.1.5 Trouble Shooting —— Enable AOF可能导致整个Redis被Block住,在2.6.12版之前

现象描述:当AOF rewrite 15G大小的内存时,Redis整个死掉的样子,所有指令甚至包括slave发到master的ping,redis-cli info都不能被执行。

原因分析:

  • 官方文档,由IO产生的Latency详细分析, 已经预言了悲剧的发生,但一开始没留意。
  • Redis为求简单,采用了单请求处理线程结构。
  • 打开AOF持久化功能后, Redis处理完每个事件后会调用write(2)将变化写入kernel的buffer,如果此时write(2)被阻塞,Redis就不能处理下一个事件。
  • Linux规定执行write(2)时,如果对同一个文件正在执行fdatasync(2)将kernel buffer写入物理磁盘,或者有system wide sync在执行,write(2)会被block住,整个Redis被block住。
  • 如果系统IO繁忙,比如有别的应用在写盘,或者Redis自己在AOF rewrite或RDB snapshot(虽然此时写入的是另一个临时文件,虽然各自都在连续写,但两个文件间的切换使得磁盘磁头的寻道时间加长),就可能导致fdatasync(2)迟迟未能完成从而block住write(2),block住整个Redis。
  • 为了更清晰的看到fdatasync(2)的执行时长,可以使用"strace -p (pid of redis server) -T -e -f trace=fdatasync",但会影响系统性能。
  • Redis提供了一个自救的方式,当发现文件有在执行fdatasync(2)时,就先不调用write(2),只存在cache里,免得被block。但如果已经超过两秒都还是这个样子,则会硬着头皮执行write(2),即使redis会被block住。此时那句要命的log会打印:“Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis.” 之后用redis-cli INFO可以看到aof_delayed_fsync的值被加1。
  • 因此,对于fsync设为everysec时丢失数据的可能性的最严谨说法是:如果有fdatasync在长时间的执行,此时redis意外关闭会造成文件里不多于两秒的数据丢失。如果fdatasync运行正常,redis意外关闭没有影响,只有当操作系统crash时才会造成少于1秒的数据丢失。

解决方法:
最后发现,原来是AOF rewrite时一直埋头的调用write(2),由系统自己去触发sync。在RedHat Enterprise 6里,默认配置vm.dirty_background_ratio=10,也就是占用了10%的可用内存才会开始后台flush,而我的服务器有64G内存。很明显一次flush太多数据会造成阻塞,所以最后果断设置了sysctl vm.dirty_bytes=33554432(32M),问题解决。

然后提了个issue,AOF rewrite时定时也执行一下fdatasync嘛, antirez三分钟后就回复了,新版中,AOF rewrite时32M就会重写主动调用fdatasync。

5.2 Master-Slave复制

5.2.1 概述

  • slave可以在配置文件、启动命令行参数、以及redis-cli执行SlaveOf指令来设置自己是奴隶。
  • 测试表明同步延时非常小,指令一旦执行完毕就会立刻写AOF文件和向Slave转发,除非Slave自己被阻塞住了。
  • 比较蠢的是,即使在配置文件里设了slavof,slave启动时依然会先从数据文件载入一堆没用的数据,再去执行slaveof。
  • "Slaveof no one",立马变身master。
  • 2.8版本将支持PSYNC部分同步,master会拨出一小段内存来存放要发给slave的指令,如果slave短暂的断开了,重连时会从内存中读取需要补读的指令,这样就不需要断开两秒也搞一次全同步了。但如果断开时间较长,已经超过了内存中保存的数据,就还是要全同步。
  • Slave也可以接收Read-Only的请求。

5.2.2 slaveof执行过程,完全重用已有功能,非常经济

  • 先执行一次全同步 -- 请求master BgSave出自己的一个RDB Snapshot文件发给slave,slave接收完毕后,清除掉自己的旧数据,然后将RDB载入内存。
  • 再进行增量同步 -- master作为一个普通的client连入slave,将所有写操作转发给slave,没有特殊的同步协议。

5.2.3 Trouble Shooting again

有时候明明master/slave都活得好好的,突然间就说要重新进行全同步了:

1.Slave显示:# MASTER time out: no data nor PING received...

slave会每隔repl-ping-slave-period(默认10秒)ping一次master,如果超过repl-timeout(默认60秒)都没有收到响应,就会认为Master挂了。如果Master明明没挂但被阻塞住了也会报这个错。可以适当调大repl-timeout。

2.Master显示:# Client addr=10.175.162.123:44670 flags=S oll=104654 omem=2147487792 events=rw cmd=sync scheduled to be closed ASAP for overcoming of output buffer limits.

当slave没挂但被阻塞住了,比如正在loading Master发过来的RDB, Master的指令不能立刻发送给slave,就会放在output buffer中(见oll是命令数量,omem是大小),在配置文件中有如下配置:client-output-buffer-limit slave 256mb 64mb 60, 这是说负责发数据给slave的client,如果buffer超过256m或者连续60秒超过64m,就会被立刻强行关闭!!! Traffic大的话一定要设大一点。否则就会出现一个很悲剧的循环,Master传输一个大的RDB给Slave,Slave努力的装载,但还没装载完,Master对client的缓存满了,再来一次。

平时可以在master执行 redis-cli client list 找那个cmd=sync,flag=S的client,注意OMem的变化。

5.3 Fail-Over

Redis-sentinel是2.6版开始加入的另一组独立运行的节点,提供自动Fail Over的支持。

5.3.1 主要执行过程

  • Sentinel每秒钟对所有master,slave和其他sentinel执行Ping,redis-server节点要应答+PONG或-LOADING或-MASTERDOWN.
  • 如果某一台Sentinel没有在30秒内(可配置得短一些哦)收到上述正确应答,它就会认为master处于sdown状态(主观Down)
  • 它向其他sentinel询问是否也认为该master倒了(SENTINEL is-master-down-by-addr ), 如果quonum台(默认是2)sentinel在5秒钟内都这样认为,就会认为master真是odown了(客观Down)。
  • 此时会选出一台sentinel作为Leader执行fail-over, Leader会从slave中选出一个提升为master(执行slaveof no one),然后让其他slave指向它(执行slaveof new master)。

5.3.2 master/slave 及 其他sentinel的发现

master地址在sentinel.conf里, sentinel会每10秒一次向master发送INFO,知道master的slave有哪些。 如果master已经变为slave,sentinel会分析INFO的应答指向新的master。以前,sentinel重启时,如果master已经切换过了,但sentinel.conf里master的地址并没有变,很可能有悲剧发生。另外master重启后如果没有切换成slave,也可能有悲剧发生。新版好像修复了一点这个问题,待研究。

另外,sentinel会在master上建一个pub/sub channel,名为"sentinel:hello",通告各种信息,sentinel们也是通过接收pub/sub channel上的+sentinel的信息发现彼此,因为每台sentinel每5秒会发送一次自己的host信息,宣告自己的存在。

5.3.3 自定义reconfig脚本

  • sentinel在failover时还会执行配置文件里指定的用户自定义reconfig脚本,做用户自己想做的事情,比如让master变为slave并指向新的master。
  • 脚本的将会在命令行按顺序传入如下参数: <master-name> <role(leader/observer)> <state(上述三种情况)> <from-ip> <from-port> <to-ip> <to-port>
  • 脚本返回0是正常,如果返回1会被重新执行,如果返回2或以上不会。 如果超过60秒没返回会被强制终止。

觉得Sentinel至少有两个可提升的地方:

  • 一是如果master 主动shutdown,比如系统升级,有办法主动通知sentinel提升新的master,减少服务中断时间。
  • 二是比起redis-server太原始了,要自己丑陋的以nohup sentinel > logfile 2>&1 & 启动,也不支持shutdown命令,要自己kill pid。

5.4 Client的高可用性

基于Sentinel的方案,client需要执行语句SENTINEL get-master-addr-by-name mymaster 可获得当前master的地址。 Jedis正在集成sentinel,已经支持了sentinel的一些指令,但还没发布,但sentinel版的连接池则暂时完全没有,在公司的项目里我参考网友的项目自己写了一个。

淘宝的Tedis driver,使用了完全不同的思路,不基于Sentinel,而是多写随机读, 一开始就同步写入到所有节点,读的话随便读一个还活着的节点就行了。但有些节点成功有些节点失败如何处理? 节点死掉重新起来后怎么重新同步?什么时候可以重新Ready? 所以不是很敢用。

另外如Ruby写的redis_failover,也是抛开了Redis Sentinel,基于ZooKeeper的临时方案。

Redis作者也在博客里抱怨怎么没有人做Dynamo-style 的client

5.5 Geographic Replication

依然用Master Slave复制,支持Active-Standby模式的Geographic Replication,主要用于容灾数据恢复,或者在site1倒掉时,启动备用系统指向备库。3Scale想出了诸如用压缩的SSH隧道降低传输量等方法,可以设置远端的Slave的优先级为0,则site2上的slave永远不会被选举成master,master只会在site1的slave中产生。

6. 运维

6.1 安装

  • 安装包制作:没有现成,需要自己编译,自己写rpm包的脚本,可参考utils中的install_server.sh与redis_init_script。
    但RHEL下设定script runlevel的方式不一样,redis_init_script中要增加一句 "# chkconfig: 345 90 10" ,而install_server.sh可以删掉后面的那句“chkconfig --level 345 reis"
  • 云服务:Redis Cloud,在Amazon、Heroku、Windows Azure、App Frog上提供云服务,供同样部署在这些云上的应用使用。其他的云服务有GarantiaData,已被redis-cloud收购。另外还有Redis To GoOpenRedisRedisGreen
  • CopperEgg统计自己的用户在AWS上的数据库部署:mysqld占了50%半壁江山, redis占了18%排第二, mongodb也有11%, cassandra是3%,Oracle只有可怜的2%。
  • Chef Recipes:brianbianco/redisio,活跃,同步更新版本。

6.2 部署模型

  • Redis只能使用单线程,为了提高CPU利用率,有提议在同一台服务器上启动多个Redis实例,但这会带来严重的IO争用,除非Redis不需要持久化,或者有某种方式保证多个实例不会在同一个时间重写AOF。
  • 一组sentinel能同时监控多个Master。
  • 有提议说环形的slave结构,即master只连一个slave,然后slave再连slave,此部署有两个前提,一是有大量的只读需求需要在slave完成,二是对slave传递时的数据不一致性不敏感。

6.3 配置

约30个配置项,全都有默认配置,对redif.conf默认配置的修改见附录1。

6.3.1 三条路

  • 可以配置文件中编写。
  • 可以在启动时的命令行配置,redis-server --port 7777 --slaveof 127.0.0.1 8888。
  • 云时代大规模部署,把配置文件满街传显然不是好的做法, 可以用redis-cli执行Config Set指令, 修改所有的参数,达到维护人员最爱的不重启服务而修改参数的效果,而且新里还可以执行 Config Rewrite 将改动写回到文件中,原配置文件里有的项会就地更改,新的项而且不是默认值的,写在文件最后。如果写入过程中crash,所有修改都不会发生。

6.3.2 安全保护

  • 在配置文件里设置密码:requirepass foobar。
  • 禁止某些危险命令,比如残暴的FlushDB,将它rename成"":rename-command FLUSHDB ""。

6.4 监控与维护

综述: Redis监控技巧

6.4.1 监控指令

Info指令将返回非常丰富的信息。 着重监控检查内存使用,是否已接近上限,used_memory是Redis申请的内存,used_memory_rss是操作系统分配给Redis的物理内存,两者之间隔着碎片,隔着Swap。 还有重点监控 AOF与RDB文件的保存情况,以及master-slave的关系。Statistic 信息还包括key命中率,所有命令的执行次数,所有client连接数量等, CONFIG RESETSTAT 可重置为0。

Monitor指令可以显示Server收到的所有指令,主要用于debug,影响性能,生产环境慎用。

SlowLog 检查慢操作(见2.性能)。

6.4.2 Trouble Shooting支持

  • 日志可以动态的设置成verbose/debug模式,但不见得有更多有用的log可看,verbose还会很烦的每5秒打印当前的key情况和client情况。指令为config set loglevel verbose。
  • 最爱Redis的地方是代码只有2.3万行,而且编码优美,而且huangz同学还在原来的注释上再加上了中文注释——Redis 2.6源码中文注释版 ,所以虽然是C写的代码,虽然有十年没看过C代码,但这几天trouble shooting毫无难度,一看就懂。
  • Trobule shotting的经历证明antirez处理issue的速度非常快(如果你的issue言之有物的话),比Weblogic之类的商业支持还好。

6.4.3 持久化文件维护

  • 如果AOF文件在写入过程中crash,可以用redis-check-aof修复,见5.1.2
  • 如果AOF rewrite和 RDB snapshot的过程中crash,会留下无用的临时文件,需要定期扫描删除。

6.4.4 三方工具

官网列出了如下工具,但暂时没发现会直接拿来用的:

  • Redis Live,基于Python的web应用,使用Info和Monitor获得系统情况和指令统计分析。 因为Monitor指令影响性能,所以建议用cron定期运行,每次偷偷采样两分钟的样子。
  • phpRedisAdmin,基于php的Web应用,目标是MysqlAdmin那样的管理工具,可以管理每一条Key的情况,但它的界面应该只适用于Key的数量不太多的情况,Demo
  • Redis Faina,基于Python的命令行,Instagram出品,用户自行获得Monitor的输出后发给它进行统计分析。由于Monitor输出的格式在Redis版本间不一样,要去github下最新版。
  • Redis-rdb-tools 基于Python的命令行,可以分析RDB文件每条Key对应value所占的大小,还可以将RDB dump成普通文本文件然后比较两个库是否一致,还可以将RDB输出成JSON格式,可能是最有用的一个了。
  • Redis Sampler,基于Ruby的命令行,antirez自己写的,统计数据分布情况。

7. Java Driver

7.1 Driver选择

各个Driver好像只有Jedis比较活跃,但也5个月没提交了,也是Java里唯一的Redis官方推荐。

Spring Data Redis的封装并不太必要,因为Jedis已足够简单,没有像Spring Data MongoDB对MongoDB java driver的封装那样大幅简化代码,顶多就是加强了一点点点pipeline和transaction状态下的coding,禁止了一些此状态下不能用的命令。而所谓屏蔽各种底层driver的差异并不太吸引人,因为我就没打算选其他几种driver。有兴趣的可以翻翻它的JedisConnection代码

所以,SpringSide直接在Jedis的基础上,按Spring的风格封装了一个JedisTemplate,负责从池中获取与归还Jedis实例,处理异常。

7.2 Jedis的细节

Jedis基于Apache Commons Pool做的连接池,默认MaxActive最大连接数只有8,必须重新设置。而且MaxIdle也要相应增大,否则所有新建的连接用完即弃,然后会不停的重新连接。

另外Jedis设定了每30秒对所有连接执行一次ping,以发现失效的连接,这样每30秒会有一个拿不到连接的高峰。但效果如何需要独立分析。比如系统高峰之后可能有一长段时间很闲,而且Redis Server那边做了Timeout控制会把连接断掉,这时候做idle checking是有意义的,但30秒一次也太过频繁了。否则关掉它更好。

Jedis的blocking pop函数,应用执行ExecutorService.shutdownNow()中断线程时并不能把它中断,见讨论组。两个解决方法:

  • 不要用不限时的blocking popup,传多一个超时时间参数,如5秒。
  • 找地方将调用blocking popup的jedis保存起来,shutdown时主动调用它的close。

7.3 Redis对Client端连接的处理

  • Redis默认最大连接数是一万。
  • Redis默认不对Client做Timeout处理,可以用timeout 项配置,但即使配了也不会非常精确。

8. Windows的版本

Windows版本方便对应用的本地开发调试,但Redis并没有提供,好在微软提供了一个依赖LibUV实现兼容的补丁,https://github.com/MSOpenTech/redis,但redis作者拒绝合并到master中,微软只好苦憋的时时人工同步。 目前的稳定版是2.6版本,支持Lua脚本。

因为github现在已经没有Download服务了,所以编译好的可执行文件藏在这里:

9. 单元测试、集成测试

NoSQL Unit 是使用了Redis的项目的福音,它提供三个功能: 1. 嵌入式的Jedis实例,用于单元测试。在springside-extension的JedisTemplateTest里使用了它。 1. ManagedRedis,可控制已安装在机器上的redis,可用集成测试。将在下个迭代试用。 1. 将数据定义在json文件里,可以在测试时装载数据或校验redis中的数据。

但嵌入式的Redis,不能模仿Lua脚本。而ManagedRedis也不支持Windows上的Redis。

10. 成功案例

注:下文中的链接都是网站的架构描述文档。

Twitter新浪微博, 都属于将Redis各种数据结构用得出神入化的那种,如何发布大V如奥巴马的消息是它们最头痛的问题。

Tumblr: 11亿美刀卖给Yahoo的图片日志网站,22 台Redis server,每台运行8 - 32个实例,总共100多个Redis实例在跑。有着Redis has been completely problem free and the community is great的崇高评价。Redis在里面扮演了八爪鱼多面手的角色:

  • Dashboard的海量通知的存储。
  • Dashboard的二级索引。
  • 存储海量短链接的HBase前面的缓存。
  • Gearman Job Queue的存储。
  • 正在替换另外30台memcached。

Instagram ,曾经,Redis powers their main feed, activity feed, sessions system, and other services。但可惜目前已迁往Cassandra,说新架构只需1/4的硬件费用,是的,就是那个导致Digg CTO辞职的Canssandra。

Flickr , 依然是asynchronous task system and rudimentary queueing system。之前Task system放在mysql innodb,根本,撑不住。

The Others:

  • Pinterest,混合使用MySQL、Membase与Redis作为存储。
  • Youporn.com,100%的Redis,MySQL只用于创建新需求用到的sorted set,300K QPS的大压力。
  • 日本微信 ,Redis在前负责异步Job Queue和O(n)的数据,且作为O(n*t)数据的cache,HBase在后,负责O(n*t)数据, n是用户,t是时间。
  • StackOverflow ,2 Redis servers for distribute caching,好穷好轻量。
  • Github,任务系统Resque的存储。
  • Digg,用来做页面计数器之类的。
  • Discourge,号称是为下一个十年打造的论坛系统, We use Redis for our job queue, rate limiting, as a cache and for transient data,刚好和我司的用法一样。
  • 情色网站 YouPorn,使用 Redis 进行数据存储,Redis 服务器每秒处理30万个页面请求,每小时会记录8-15GB数据。

11. In SpringSide

extension modules项目封装了常用的函数与场景,showcase example的src/demo/redis目录里有各场景的benchmark测试。

11.1 Jedis Template

典型的Spring Template风格,和JdbcTemplate,HibernateTemplate一样,封装从JedisPool获取与归还Connecton的代码,有带返回值与无返回值两种返回接口。同时,对最常用的Jedis调用,直接封装了一系列方法。

11.2 Scheduler与Master Elector

Scheduler实现了基于Redis的高并发单次定时任务分发。具体选型见Scheduler章节。

Master Elector基于redis setNx()与expire()两个api实现,与基于Zookeeper,Hazelcast实现的效果类似。

11.3 Showcase中的Demo

计有Session,Counter,Scheduler 与 Master Elector四款。

12. What is new in Redis 2.8

  • 全新的Sentinel实现,Setinel会在自己配置文件持久化谁是最新master,也会让每台redis-server持久化新的mater,重新起来的节点如果错误的把自己当成maser,sentinel也会发出slaveof的指令纠正它,原来要自行实现的脚本都可以去掉了。
  • CONFIG REWRITE将命令行CONFIG SET动态改变的配置写回到配置文件。
  • SCAN命令,分页匹配遍历所有的Key。
  • EVALSHA ,用sha1执行的Lua脚本可被直接Replicate,不需要翻译成完整的Lua脚本再replicate,节约了Replicate的带宽。
  • 提升了Expired keys的收集算法,在CPU繁忙时也不会累积大量超时的Key??
  • 说了很久的半同步,Slave短时间断开后不需要做全同步。
  • 支持IPv6,并可绑定多个IP地址。
  • 进程名会带上端口号和是否子进程的标识,ps时能分得清一台机器上的多个redis,或者是fork出来的bgrewrite/bgrewriteaof字进程了。

其他用不上的new feature:

  • PUBSUB 收取keyspace 更新信息.
  • Masters can stop accepting writes if not enough slaves with a given maximum latency are connected. 默认为0.

附录

附录1: 对redis.conf默认配置的修改

Master上

  • maxmemory,设置为可用内存的一半.
  • logfile stdout -> /var/log/redis/redis.log ,指定日志文件
  • dir ./ -> /var/lib/redis,指定持久化文件及临时文件目录.
  • (可选)daemonize no -> yes ,启动daemonize模式,注意如果用daemon工具启动redis-server时设回false。
  • appendonly no->yes,打开AOF文件.
  • auto-aof-rewrite-percentage 100, 综合考虑硬盘大小,可接受重启加载延时等尽量的大,减少AOF rewrite频率.
  • auto-aof-rewrite-min-size 64mb,同上,起码设为5G. *(可选)注释掉RDB的所有触发规则,在Master不保存RDB文件。
  • client-output-buffer-limit slave 256mb 64mb 60. 考虑Traffic及Slave同步是RDB加载所需时间,正确设置避免buffer撑爆client被关掉后又要重新进行全同步。
  • 安全配置,可选。

Slave上

  • 设置RDB保存频率,因为RDB只作为Backup工具,设置为30分钟保存一次就够了save 1800 1。
  • repl-timeout 60, 适当加大比如120,避免master实际还没倒掉就认为master倒了。
  • (可选)slaveof 设置master地址,也可动态设定。

附录2:版本变更历史

  • 3.1版 20 2014-3-24 增加Redis 2.8内容
  • 3.0.1版-3.0.3版 2013-8-1-2014-1-24,在微博发布后反应良好,持续修改。
  • 3.0版 2013-6-29,在公司Workshop后修订,提高wiki的可读性而不只是简单的记录知识点。

附录3:其他参考资料

(@江南白衣版权所有,转载请保留出处)

阅读全文……

标签 : ,

Linux下搭建高可用redis集群方案-chaosuper85-ChinaUnix博客

在单台机器上搭建redis的高可用集群步骤

一,    安装以下

a)       redis

b)       redis-sentinel

c)       nutcracker

d)       redis-twemproxy-agent,

e)       nodejs

 

 

二,      配置和启动redis服务

1,         启动redis-server主一,监听端口6379,

修改./conf/redis.conf,

port 6379

然后启动,执行:

./bin/redis-server ./conf/redis.conf &

 

2, 启动redis-server从一,监听端口6380,

修改./conf/redis.conf,

port 6380,

slaveof 127.0.0.1 6379

然后启动,执行:./bin/redis-server ./conf/redis.conf &

 

3, 同样的完成另一备用主从的启动

备份redis

主二,127.0.0.1 6389

从二, 127.0.0.1 6390

 

                        4, 使用redis-cli命令,确认四个redis-server的主从关系和运行状态。

 

三,      配置和启动redis-sentinel服务

1,         修改sentinel.conf,增加一行内容:

sentinel monitor CPymtCache13 127.0.0.1 6379 1

sentinel monitor CPymtCache12 127.0.0.1 6389 1

 

2,         启动redis-sentinel服务

./bin/redis-sentinel ./conf/sentinel.conf &

 

 

            四, 配置nutcracker(twemproxy)

1,         修改nutcracker.yml文件

write:

  listen: "0.0.0.0:2121"

  hash: fnv1a_64

  distribution: ketama

  auto_eject_hosts: true

  redis: true

  server_retry_timeout: 2000

  server_failure_limit: 1

  servers:

    - "127.0.0.1:6379:1 CPymtCache13"

    - "127.0.0.1:6389:1 CPymtCache12"

 

 

            五,  配置和启动redis-twemproxy-agent

1,         修改redis-twemproxy-agent/lib/cli.js文件

修改cli.parse里的文件路径为部署的路径。

 

2,         修改文件

redis-twemproxy-agent/init.d/ twemproxy_sentinel_start.sh

修改该脚本redis-twemproxy-agentnodejsforever,的路径为部署的路径。

 

3,         执行命令启动redis-twemproxy-agent

./init.d/twemproxy_sentinel_start.sh start

4,         检查以下服务是否正常启动

nutcracker

redis-twemproxy-agent

redis-twemproxy-agent  forever

 

 

            六, 验证高可以用方案的正确性

                                    1./bin/redis-cli  -p 6379

2,执行shutdown ,关闭redis-server主一

                                    3./bin/redis-cli  -p 6380

4,执行info命令,观察redis-serverrole:slave切换位role:master

5,验证配置文件正确

查看配置文件的servers /nutcracker/conf/nutcracker.yml

阅读全文……

标签 : , ,

Redis HA 方案选型

本文整理了redis当前的高可用方案,以及比较各方案的优劣和我们最后的选型。

Redis-Cluster

  • 引入Hash slots概念,便于分片以及数据迁移.解决了按照节点分片带来的扩容以及数据迁移的困难

    slot = crc16("foo") mod NUMERSLOTS

  • 节点都是对等的,复制是基于slots的,不是基于节点的.每个slots有两份冗余,可以容忍随机的两台节点宕机而不影响服务
  • 支持自动重新分片以及故障迁移
  • 节点通过PING-PONG以及Gossip互相感知,不需要中心监控服务监控节点的状态
  • 通过SmartClient在Client端缓存了slots的分布图,无需中心代理.

详情参看 Redis_Cluster.pdf 

  1. 优点
    • 优点很多 能确保高可用 高性能,基本上是分布式缓存的完美方案
  2. 缺点:
    • 还是beta版本,不能在生产环境使用

HAProxy + Twemproxy + redis-twemproxy-agent(NodeJS) + redis sentinel

方案来源(REDIS-SENTINEL TWEMPROXY AGENT

  • Twemproxy 是twitter开源的代理服务,支持Memcached和Redis协议,在这里主要的作用是 1.解决分片的问题,这样就不需要客户端自己做分片,分片对客户端是透明的.2.客户端应用连接Twemproxy,主从切换对客户端透明
  • Redis sentinel 是redis官方提供的redis检测工具,会检测redis的状态然后触发事件.
  • Redis-Twemproxy-Agent主要是用于监听redis sentinel的变更事件,修改Twemproxy的配置.
  • HAProxy 主要是为了解决Twemproxy的高可用问题。

  • 优点

    • 解决了分片问题
    • 能保证高可用
  • 缺点
    • Twemproxy的hash规则和我们当前使用的方式不兼容,改造后会有数据迁移的问题,比较麻烦
    • 这个方案引入的组件过多,担心不好运维
    • 不支持读写分离Slave节点只起备份的作用

Keepalived + HAProxy + Redis sentinel + 自定义脚本

这个方案基本上是上个版本的简化版本 redis-ha

  • Keepalived负责虚拟ip和高可用
  • HAProxy 负责代理Redis的端口,同一个实例可以代理多个redis节点
  • Redis sentinel负责检测Redis的存活状况,并进行主从切换
  • 自定义脚本由Keepalived的定时调用,通过命令向Redis sentinel查询Redis Master的ip,判断是否发生变化,如果变化则修改HAProxy配置文件并重启HAProxy.

  • 优点:

    • 组件较少,并且都比较成熟,运维成本较低
  • 缺点:
    • redis的slave一直处于备用状态 比较浪费(同上一个方案)
    • 没有解决分片问题,分片由应用解决
    • 代理对性能有影响
    • 脚本是定时轮询的机制通过Redis sentinel查询redis状态,主从变更后感知会比较慢,如果发生切换,整体上服务会有分钟级别的时间处于不可用状态

Zookeeper + Redis sentinel + 自定义同步服务 + SmartClient

这个方案的思路和RedisCluster有一定的共通之处,

  • Redis sentinel 检测redis实例并进行主从切换
  • 自定义同步服务负责监听Redis sentinel的状态变更,将redis实例的状态同步到Zookeeper
  • Zookeeper扮演配置中心角色
  • SmartClient连接到Zookeeper并且watch Redis 实例的状态,根据状态将请求发送到正确的redis节点

  • 优点:

    • 不需要代理 没有性能浪费
    • SmartClient机制是当前分布式缓存的一种通用解决方案
  • 缺点:
    • 自定义同步服务以及SmartClient当前都需要额外开发 考虑到RedisCluster本身已经包含了这些改造,不如等待Cluster正式发布.

结论

基于当前流量不是太大,数据分片也不是当前最大的的问题,主要的需求点在高可用,最后使用了方案二.上线后整体稳定,服务器重启,网络不通等故障,基本不用人工处理redis的问题

  •  

阅读全文……

标签 : ,

攻略:澳门一日游最佳线路 - 澳门一日游攻略 - 澳门一日游路线 - 美景旅游博客网

路线名称:澳门一日游最佳线路
游览景点:威尼斯人酒店 - 龙环葡韵 - 渔人码头 - 大三巴牌坊 - 澳门博物馆
路线说明:澳门具有400多年历史,东西文化一直在此地相互交融,使澳门成为一个独特的城市,既有古色古香的传统庙宇,又有庄严肃穆的天主圣堂,还有众多的历史文化遗产,以及沿岸优美的海滨胜景。
行程天数:1天
适合人群:初次到澳门游玩的人
 

 

澳门威尼斯人酒店 澳门威尼斯人酒店 澳门威尼斯人酒店
图片:澳门威尼斯人酒店(点击查看大图)

上午:澳门威尼斯人酒店
攻略:从珠海横琴过关,关口门口有直达威尼斯人的免费中巴,威尼斯人门口有巴士站直接到官也街。那里有很多美食,例如西餐厅,澳门的小吃木糠布甸等……著名的猪扒包店利记,不过要下午三点才供应的,一日只有一次出炉。

澳门威尼斯人酒店(The Venetian Macao-Resort-Hotel)是由美国拉斯韦加斯金沙集团投资的威尼斯人度假村,投资约200亿元,这所奉行多元经营理念的度假村设有三千间豪华客房及大规模的博彩、会展、购物、体育、综艺及休闲设施等,其中占地十一万平方米的会展场地,势必成为香港的竞争对手。酒店位于澳门路氹城填地区金光大道地段,酒店楼高39层。

威尼斯人度假村拥有世界一流的设施,其规模更超越美国拉斯韦加斯,其中包括超过六十平方米的豪华客房、近十万平方米并汇集世界名牌的大运河购物区、八千平方米的水疗中心,以及驻场表演的太阳马戏团等。预计威尼斯人度假村的开幕,除了能吸引赌客外,亦会吸引不少商务会议及展览活动转到澳门举行,度假村期望可以吸引到一批高消费的商务旅客到澳门消费。 

大运河购物中心,零售及餐饮设施: 93,548平方米,大运河购物中心在蓝天白云下漫步,一边陶醉于贡多拉船船夫的美妙歌声中,一边被别具特色的街头表演给吸引着。游走在1,000,000尺的购物空间,提供超过352家国际名店,让您尽情购物。这儿就是大运河购物中心。

澳门威尼斯人酒店交通指南:

如果您从珠海出发:
横琴口岸:
乘坐过境巴士,经过边检站抵达澳门。抵步后,可免费搭乘穿梭巴士直达澳门威尼斯人。行驶时间是早上9时至晚上8时。
拱北口岸:经过边检站抵达澳门。抵步后,可免费乘坐穿梭巴士直达澳门威尼斯人。行驶时间是早上7时30分至午夜12时。

如果您从广州/中山/东莞/深圳出发:
乘坐过境巴士直达珠海拱北口岸地下商场或莲花大桥,步行经过边检站抵达澳门。抵步后,再免费搭乘穿梭巴士直达澳门威尼斯人。

如果您从澳门国际机场出发:
乘坐免费威尼斯人穿梭巴士,5分钟即可到达澳门威尼斯人,每15 -20分钟即有一班巴士从机场出发。

如果您从香港国际机场出发: 
直接在机场乘坐快速渡轮1个小时内即可抵达澳门,无需花1个小时通过香港海关。

如果您从广州白云机场出发:
搭乘巴士到达珠海信禾汽车站(拱北海关的东面),通过拱北海关抵达澳门。进入澳门后可乘坐免费服务的威尼斯人穿梭巴士到达澳门威尼斯人。从广州至澳门总行程需约两个半小时。

如果您从珠海机场出发:
乘坐穿梭巴士到珠海横琴口岸,经过莲花大桥,搭乘免费服务的威尼斯人穿梭巴士到达澳门威尼斯人。(每日上午9时30分到晚上7时30分,每隔10-12分钟即有一班)。整个行程约30分钟。

如果您从深圳宝安国际机场出发:
乘坐穿梭巴士到达深圳福永码头,搭乘渡轮45分钟可到达澳门。

 

澳门龙环葡韵 澳门龙环葡韵 澳门龙环葡韵
图片:澳门龙环葡韵(点击查看大图)

澳门渔人码头 澳门渔人码头 澳门渔人码头
图片:澳门渔人码头(点击查看大图)

中午:龙环葡韵 - 渔人码头
之后可以从官也街出发步行到龙环葡韵(五分钟左右)。参观土生葡人之家,行番出来官也街街口有巴士28A到渔人码头(金莎赌场),车费:3.3元,游览之后可以直接打车到新马路(议事亭前地,也可以叫喷水池),大约20元(坐车也可以的,但由于公交车是环线行驶。容易坐错方向,加上公交车车钱好似是2.5元/人

龙环葡韵住宅式博物馆(简称龙环葡韵),是位于澳门的旅游景点,整个景点位于凼仔岛 ,以海边马路的五幢葡萄牙式住宅为主的博物馆。龙环葡韵住宅式博物馆于1999年12月5日正式对外开放。五幢葡萄牙式住宅分别为:土生葡人之家、海岛之家、葡萄牙地区之家、展览馆、迎宾馆。现在,此住宅式博物馆是澳门重要的文物建筑与文化遗产,也是澳门极富代表性的景点之一。整个龙环葡韵景区包括龙环葡韵住宅式博物馆、嘉模教堂、凼仔图书馆、凼仔市政花园和十字花园。博物馆前面更是一片红树林湿地,可以观赏到黑脸琵鹭、白鹭、灰鹭、翠鸟和鸳鸯等鸟类。龙环葡韵景区被评定为澳门八景之一。

“龙环”是凼仔岛的旧称,“葡韵”是指这里葡萄牙的建筑风格。此五幢葡萄牙式建筑,落成于1921年。原为澳门离岛高级官员的官邸及一些土生葡人家庭住宅。在1992年,澳葡政府对该建筑群进行修复和改建为住宅式博物馆。

开放时间:星期二至日:上午10:00至下午6:00 ,星期一:休息 
门票价格:澳门币5元,10岁以下或60岁以上:免费入场,逢星期日:免费入场
澳门渔人码头Macau Fisherman's Wharf)是澳门首个主题公园和仿欧美渔人码头的购物中心。澳门渔人码头建于外港新填海区海岸,邻近港澳码头。由何鸿燊及周锦辉投资兴建,总投资约为澳门币18.5亿,经过5年时间筹备与兴建。2005年12月31日由澳门行政长官何厚铧及渔人码头主席何鸿燊揭幕仪式及试业。试业一年后于2006年12月23日正式开幕。澳门渔人码头坐落于外港新填海区海岸,占地超过111,500平方米,集娱乐、购物、饮食、酒店、游艇码头及会展设施于一体,结合不同建筑特色及中西文化,务求使游客突破地域界限,体验不同地区的感受。而区内多元化的娱乐设施必定能使澳门渔人码头成为举家同游的好去处。澳门渔人码头的落成相信将为本澳旅游业展现全新的旅游面貌。

澳门渔人码头主要分为三部份:唐城 、东西汇聚、励骏码头。 唐城是一幢仿唐朝建筑风格的中式城楼,城楼内以购物商场为主,集各地潮流商品以至高级食府。 东西汇聚糅合了东方传统概念与西方建筑风格的设计特色。区内设施包罗万有,无论是小朋友喜欢的机动游戏、多用途表演及会展场地、以致电子游戏及购物中心,均应有尽有。在火山里面,游人可乘搭亚洲首创、夺宝奇兵式名为「飞龙快车」的单轨室内过山车及名为「火焰激流」的观光船机动游戏,令游客可亲身体验热力澎湃的熔岩区域。夜晚更可欣赏这个四十米高的人造火山熔岩沸腾蓄势而发的壮丽景观。阿拉伯堡垒将成为小孩们的首选地点。此区设有四个适合小朋友乘坐的机动游戏,名为「飞天骆驼」、「魔法飞毯」、「阿拉丁快车」及「阿拉伯跳跳塔」。

除此以外,同区亦设有游戏摊位、小吃店与及纪念品店等。会议展览中心占地5,000平方米,其中主会展厅占地3,000平方米,楼高6.3米的大堂配合无柱设计,适合会议展览、公司宴会、私人派对或婚宴喜庆。罗马表演场是一个可容纳2,000名观众的户外表演场,附设顶级音响视听设备及占地200 平方米的多用途舞台,务求为观众缔造一个前所未有的难忘回忆 励骏码头以欧陆及拉丁式建筑群组成,游客可在区内找到各国特色的美酒佳肴,又可在林林总总的名牌专门店内选购商品,更有表演者为您带来不同的街头表演。

澳门渔人码头 地址:澳门新口岸友谊大马路及孙逸仙大马路
澳 门渔人码头开放时间:
24小时免费入场 (机动游戏:上午十时至晚上九时) 
澳门渔人码头公布票价:
夺宝历奇 单项-澳门币 40元 
烈焰激流 单项-澳门币 40元 
飞天骆驼 单项-澳门币 20元 两项-澳门币 36元
魔法飞毯 单项-澳门币 20元 两项-澳门币 36元 
阿拉丁快车 单项-澳门币 20元 两项-澳门币 36元 
青云升降 单项-澳门币 20元 两项-澳门币 36元 
水世界表演场入场费–澳门币 50元 

 

澳门议事亭前地 澳门博物馆 澳门博物馆
图片:澳门议事亭前地 、 澳门博物馆 、澳门炮台(点击查看大图)

大三巴牌坊 大三巴牌坊 大三巴牌坊
图片:澳门大三巴牌坊(点击查看大图)

 

下午:议事亭前地 - 大三巴牌坊 - 澳门博物馆
新马路(议事亭前地,也可以叫喷水池)是购物旺地,各牌子专卖店都集中在这里。另外景点有:大三巴、炮台、澳门博物馆、主教堂、玫瑰堂,之后可以在新马路坐3号车或者打车去关闸(约20元)

大三巴牌坊
澳门地标大三巴牌坊是旅客必到之地。自从1835年一场大火,原为中西合璧的圣保禄教堂,变成了只有前壁的遗址。圣保禄教堂是当时东方最大的天主教堂,号称「东方梵蒂冈」,附属于圣保禄学院。学院是远东地区第一所西式大学,从1594年成立至1762年结束期间,不少访华的外国传教士来修读中文,令学院成为这些传教士进入中国的重要传教基地,对推动欧洲和中国的宗教及文化交流起到无可比拟的卓越作用。

教堂成为遗址之后,因前壁与中国传统牌坊相似,加上「圣保禄」从葡文 (São Paulo) 音译成中文,说成「三巴」,才有大三巴牌坊的称谓。牌坊的建筑是巴洛克式,并有明显东方色彩的雕刻,包括代表中国和日本的牡丹及菊花图案,令她在全世界的天主教教堂中具有独一无二的特色。

大三巴牌坊虽然已失去教堂的实际功能,但她与澳门人的生活息息相关。这里不定期举行各种文化活动,牌坊前长长的梯级正好成为天然的座位,让牌坊刹那间变成巨大的布景,舞台浑然天成。相信几百年前生活在澳门的人,想不到这座教堂竟成为举行文化活动的理想户外场地。

从牌坊后面的铁梯步上,可直达牌坊的第2层。站在这里望出去,仿佛当年站在圣保禄教堂内俯览小城人民的生活情形,必须亲身经历才能领略这种特别感觉。

参观完牌坊,可以到内侧广场的天主教艺术博物馆,馆内收藏了澳门教堂和修院具代表性的画作、雕塑等,当中最珍贵的是一批以宗教生活为题材的油画,这是远东的第一批画作,也是东方最古老的油画。隔壁的墓室更存放着日本和越南殉教者的遗骨,展示澳门的宗教历史。

天主教艺术博物馆与墓室:开放时间:每日上午9时至下午6时 免费入场
议事亭前地
世界各地尤以欧洲为主,随处可见大小不一的广场空间,游人相遇倾谈,或是在咖啡座闲适地品味人生。澳门亦有不少广场,地上铺砌着波浪型葡萄牙黑白碎石,仿如大浪滔滔的海洋,配以各款海洋生物及澳门景点为图案,更具立体感,充份表达葡萄牙的航海事业,亦巧妙地切合澳门昔日的渔港形象。当您走过着名的议事亭前地、妈阁庙前地、阿婆井前地、岗顶前地、板樟堂前地、大堂前地、白鸽巢前地以至耶稣会纪念广场,当你举目四周,欣赏中西共融的情境时,别忘了低头细看,你已踏进澳门的文化汪洋。

论及众多广场空间,着名的议事亭前地会否是您必到之选?议事亭前地旧称喷水池,因为广场中央有一座喷水池,虽然多年来换过不同面貌,但不少澳门老居民仍沿用此名称。现今,喷水池上摆放着象征葡萄牙航海远征的天球仪,晚上配上灯光效果让议事亭前地生色不少。

在不同时期来议事亭前地,又有另一番感受。农历新年、中秋节以至圣诞节,这里顿然换上富节日气息的装饰,配合四周的欧陆建筑,将澳门中西文化融合的特色发挥得淋漓尽致。议事亭前地更是举行活动的热门地点,如每年「澳门艺穗」将全城各处变作舞台,当游人途经此地,不知不觉由观众变成表演者。

这里商店林立,从以往到现在都是繁盛的商业区,更因设有不少手信店而吸引旅客到此购物。最难得之处是这里保存着昔日二、三层高的中西房子,而附近又有西式建筑如玫瑰堂、仁慈堂、民政总署大楼以至三街会这类中式庙宇,结合现代人的生活节奏,达至老建筑和现代气息和谐共存,并体现中西文化交融,正是澳门的独特之处。当您继续走访不远处的岗顶前地、板樟堂前地以及其他广场空间,这些属于澳门的特色如影随形,陆续展现她的动人魅力。 
澳门博物馆
澳门博物馆在1998年4月18日落成揭幕。博物馆位于大炮台上,大炮台于十七世纪初由耶稣会会士兴建。当时着名的圣保禄学院 (也称天主圣母学院) 和圣保禄教堂就建在大炮台附近,圣保禄学院被视为远东地区的第一所西方大学。

兴建澳门博物馆旨在保存数个世纪东、西方文化在澳门交汇,并和谐共存的多种面貌:传统、风俗和文化等。

博物馆的藏品并非价值连城,但却蕴藏丰富的历史意义和人们对美好生活的回忆。通过展品向参观者展现,在过去数百年当中居住在澳门的不同民族和平共处的生活状况和历史面貌。

澳门博物馆共有三层:一楼澳门地区文明的原始,介绍澳门地区的起源、欧维士于1513年到达珠江三角洲前中国和葡萄牙各自的发展历程、两者在澳门相遇后引起的贸易、宗教和文化等方面的接触和在往后的数百年里逐渐形成的独特的澳门文化。

二楼澳门民间艺术与传统,让参观者可以了解澳门的传统、民间艺术和现已式微甚至消失的行业或活动。展品还揭示澳门历史的各个阶段和方方面面:娱乐、日常生活方式、宗教礼仪和庆典等,以突显不同文化和种族的人民在澳门相互包容、共存以及既丰富又独特的生活方式。

三楼当代澳门的特色,展示当代澳门城市生活的特色和对未来的展望。此外,还展出一些与澳门有着密切联系的作家的作品,其中包括着名的葡萄牙文学家贾梅士和庇山耶。展览的最后部分向参观者介绍作为中华人民共和国特别行政区的澳门的前景与机遇。

参观博物馆后,游人可顺道游览大炮台花园,俯瞰澳门城市的旖旎风光。

地址:澳门博物馆前地112号 (大三巴牌坊侧)
开放时间:每日上午10时至下午6时 (售票至下午5时30分止),逢星期一休馆 (公众假期除外)
门票价格:成人澳门币15元;11岁以下儿童、60岁以上长者及学生澳门币8元;团体、学校和公共机构优惠价。每月的15日免费开放。
到达的公共汽车有:2,3,3A,4,5,6,7,8A,10,10A,11,17,18,19,21,21A,26,26A,33 

出游攻略:
1、如果上炮台,可以从往博物馆的电梯上去,不必走楼梯那么辛苦,到了博物馆后,再继续往上乘,就是炮台了
2、议事厅前地走向大三巴的途中,会有很多所谓的特色小食。例如猪扒包盐酥鸡等,味道不怎么样的。
3、澳门出租车起标是10元
4、过关时可以拿份《澳门地图》和《澳门新福利巴士路线指南》,很有用的
5、拱北的免费巴士比横琴要多,也可以从拱北直接坐车去威尼斯人,从最远的景点玩起,最后去新马路SHOPPING,那么就可以直接拿着手信过关了。
 

阅读全文……

标签 : ,

使用 GDB 调试 Linux 软件

除了调试 core 文件或程序之外,gdb 还可以连接到已经运行的进程,例如:

#./nginx -V

#ps -ef|grep nginx

查找 nginx pid 为1283

#gdb /usr/local/nginx/sbin/nginx 1283

#(gdb) bt

 

编译

开始调试之前,必须用程序中的调试信息编译要调试的程序。这样,gdb 才能够调试所使用的变量、代码行和函数。如果要进行编译,请在 gcc(或 g++)下使用额外的 '-g' 选项来编译程序:

gcc -g eg.c -o eg
 

运行 gdb

在 shell 中,可以使用 'gdb' 命令并指定程序名作为参数来运行 gdb,例如 'gdb eg';或者在 gdb 中,可以使用 file 命令来装入要调试的程序,例如 'file eg'。这两种方式都假设您是在包含程序的目录中执行命令。装入程序之后,可以用 gdb 命令 'run' 来启动程序。

 

调试会话示例

如果一切正常,程序将执行到结束,此时 gdb 将重新获得控制。但如果有错误将会怎么样?这种情况下,gdb 会获得控制并中断程序,从而可以让您检查所有事物的状态,如果运气好的话,可以找出原因。为了引发这种情况,我们将使用一个 示例程序:

代码示例 eg1.c
#include 
int wib(int no1, int no2)
{
  int result, diff;
  diff = no1 - no2;
  result = no1 / diff;
  return result;
}
int main(int argc, char *argv[])
{
  int value, div, result, i, total;
  value = 10;
  div = 6;
  total = 0;
  for(i = 0; i < 10; i++)
  {
    result = wib(value, div);
    total += result;
    div++;
    value--;
  }
  printf("%d wibed by %d equals %d\n", value, div, total);
  return 0;
}

这个程序将运行 10 次 for 循环,使用 'wib()" 函数计算出累积值,最后打印出结果。

在您喜欢的文本编辑器中输入这个程序(要保持相同的行距),保存为 'eg1.c',使用 'gcc -g eg1.c -o eg1' 进行编译,并用 'gdb eg1' 启动 gdb。使用 'run' 运行程序可能会产生以下消息:

Program received signal SIGFPE, Arithmetic exception.
0x80483ea in wib (no1=8, no2=8) at eg1.c:7
7         result = no1 / diff;
(gdb)

gdb 指出在程序第 7 行发生一个算术异常,通常它会打印这一行以及 wib() 函数的自变量值。要查看第 7 行前后的源代码,请使用 'list' 命令,它通常会打印 10 行。再次输入 'list'(或者按回车重复上一条命令)将列出程序的下 10 行。从 gdb 消息中可以看出,第 7 行中的除法运算出了错,程序在这一行中将变量 "no1" 除以 "diff"。

要查看变量的值,使用 gdb 'print' 命令并指定变量名。输入 'print no1' 和 'print diff',可以相应看到 "no1" 和 "diff" 的值,结果如下:

(gdb) print no1
$5 = 8
(gdb) print diff
$2 = 0

gdb 指出 "no1" 等于 8,"diff" 等于 0。根据这些值和第 7 行中的语句,我们可以推断出算术异常是由除数为 0 的除法运算造成的。清单显示了第 6 行计算的变量 "diff",我们可以打印 "diff" 表达式(使用 'print no1 - no2' 命令),来重新估计这个变量。gdb 告诉我们 wib 函数的这两个自变量都等于 8,于是我们要检查调用 wib() 函数的 main() 函数,以查看这是在什么时候发生的。在允许程序自然终止的同时,我们使用 'continue' 命令告诉 gdb 继续执行。

(gdb) continue
Continuing.
Program terminated with signal SIGFPE, Arithmetic exception.
The program no longer exists.
 

使用断点

为了查看在 main() 中发生了什么情况,可以在程序代码中的某一特定行或函数中设置断点,这样 gdb 会在遇到断点时中断执行。可以使用命令 'break main' 在进入 main() 函数时设置断点,或者可以指定其它任何感兴趣的函数名来设置断点。然而,我们只希望在调用 wib() 函数之前中断执行。输入 'list main' 将打印从 main() 函数开始的源码清单,再次按回车将显示第 21 行上的 wib() 函数调用。要在那一行上设置断点,只需输入 'break 21'。gdb 将发出以下响应:

(gdb) break 21
Breakpoint 1 at 0x8048428: file eg1.c, line 21.

以显示它已在我们请求的行上设置了 1 号断点。'run' 命令将从头重新运行程序,直到 gdb 中断为止。发生这种情况时,gdb 会生成一条消息,指出它在哪个断点上中断,以及程序运行到何处:

Breakpoint 1, main (argc=1, argv=0xbffff954) at eg1.c:21
21          result = wib(value, div);

发出 'print value' 和 'print div' 将会显示在第一次调用 wib() 时,变量分别等于 10 和 6,而 'print i' 将会显示 0。幸好,gdb 将显示所有局部变量的值,并使用 'info locals' 命令保存大量输入信息。

从以上的调查中可以看出,当 "value" 和 "div" 相等时就会出现问题,因此输入 'continue' 继续执行,直到下一次遇到 1 号断点。对于这次迭代,'info locals' 显示了 value=9 和 div=7。

与其再次继续,还不如使用 'next' 命令单步调试程序,以查看 "value" 和 "div" 是如何改变的。gdb 将响应:

(gdb) next
22          total += result;

再按两次回车将显示加法和减法表达式:

(gdb)
23          div++;
(gdb)
24          value--;

再按两次回车将显示第 21 行,wib() 调用。'info locals' 将显示目前 "div" 等于 "value",这就意味着将发生问题。如果有兴趣,可以使用 'step' 命令(与 'next' 形成对比,'next' 将跳过函数调用)来继续执行 wib() 函数,以再次查看除法错误,然后使用 'next' 来计算 "result"。

现在已完成了调试,可以使用 'quit' 命令退出 gdb。由于程序仍在运行,这个操作会终止它,gdb 将提示您确认。

 

更多断点和观察点

由于我们想要知道在调用 wib() 函数之前 "value" 什么时候等于 "div",因此在上一示例中我们在第 21 行中设置断点。我们必须继续执行两次程序才会发生这种情况,但是只要在断点上设置一个条件就可以使 gdb 只在 "value" 与 "div" 真正相等时暂停。要设置条件,可以在定义断点时指定 "break <line number> if <conditional expression>"。将 eg1 再次装入 gdb,并输入:

(gdb) break 21 if value==div
Breakpoint 1 at 0x8048428: file eg1.c, line 21.

如果已经在第 21 行中设置了断点,如 1 号断点,则可以使用 'condition' 命令来代替在断点上设置条件:

(gdb) condition 1 value==div

使用 'run' 运行 eg1.c 时,如果 "value" 等于 "div",gdb 将中断,从而避免了在它们相等之前必须手工执行 'continue'。调试 C 程序时,断点条件可以是任何有效的 C 表达式,一定要是程序所使用语言的任意有效表达式。条件中指定的变量必须在设置了断点的行中,否则表达式就没有什么意义!

使用 'condition' 命令时,如果指定断点编号但又不指定表达式,可以将断点设置成无条件断点,例如,'condition 1' 就将 1 号断点设置成无条件断点。

要查看当前定义了什么断点及其条件,请发出命令 'info break':

(gdb) info break
Num Type           Disp Enb Address    What
1   breakpoint     keep y   0x08048428 in main at eg1.c:21
        stop only if value == div
        breakpoint already hit 1 time

除了所有条件和已经遇到断点多少次之外,断点信息还在 'Enb' 列中指定了是否启用该断点。可以使用命令 'disable <breakpoint number>'、'enable <breakpoint number>' 或 'delete <breakpoint number>' 来禁用、启用和彻底删除断点,例如 'disable 1' 将阻止在 1 号断点处中断。

如果我们对 "value" 什么时候变得与 "div" 相等更感兴趣,那么可以使用另一种断点,称作监视。当指定表达式的值改变时,监视点将中断程序执行,但必须在表达式中所使用的变量在作用域中时设置监视点。要获取作用域中的 "value" 和 "div",可以在 main 函数上设置断点,然后运行程序,当遇到 main() 断点时设置监视点。重新启动 gdb,并装入 eg1,然后输入:

(gdb) break main
Breakpoint 1 at 0x8048402: file eg1.c, line 15.
(gdb) run
...
Breakpoint 1, main (argc=1, argv=0xbffff954) at eg1.c:15
15        value = 10;

要了解 "div" 何时更改,可以使用 'watch div',但由于要在 "div" 等于 "value" 时中断,那么应输入:

(gdb) watch div==value
Hardware watchpoint 2: div == value

如果继续执行,那么当表达式 "div==value" 的值从 0(假)变成 1(真)时,gdb 将中断:

(gdb) continue
Continuing.
Hardware watchpoint 2: div == value
Old value = 0
New value = 1
main (argc=1, argv=0xbffff954) at eg1.c:19
19        for(i = 0; i < 10; i++)

'info locals' 命令将验证 "value" 是否确实等于 "div"(再次声明,是 8)。

'info watch' 命令将列出已定义的监视点和断点(此命令等价于 'info break'),而且可以使用与断点相同的语法来启用、禁用和删除监视点。

 

core 文件

在 gdb 下运行程序可以使俘获错误变得更容易,但在调试器外运行的程序通常会中止而只留下一个 core 文件。gdb 可以装入 core 文件,并让您检查程序中止之前的状态。

在 gdb 外运行示例程序 eg1 将会导致核心信息转储:

$ ./eg1
Floating point exception (core dumped)

要使用 core 文件启动 gdb,在 shell 中发出命令 'gdb eg1 core' 或 'gdb eg1 -c core'。gdb 将装入 core 文件,eg1 的程序清单,显示程序是如何终止的,并显示非常类似于我们刚才在 gdb 下运行程序时看到的消息:

...
Core was generated by `./eg1'.
Program terminated with signal 8, Floating point exception.
...
#0  0x80483ea in wib (no1=8, no2=8) at eg1.c:7
7         result = no1 / diff;

此时,可以发出 'info locals'、'print'、'info args' 和 'list' 命令来查看引起除数为零的值。'info variables' 命令将打印出所有程序变量的值,但这要进行很长时间,因为 gdb 将打印 C 库和程序代码中的变量。为了更容易地查明在调用 wib() 的函数中发生了什么情况,可以使用 gdb 的堆栈命令。

 

堆栈跟踪

程序“调用堆栈”是当前函数之前的所有已调用函数的列表(包括当前函数)。每个函数及其变量都被分配了一个“帧”,最近调用的函数在 0 号帧中(“底部”帧)。要打印堆栈,发出命令 'bt'('backtrace' [回溯] 的缩写):

(gdb) bt
#0  0x80483ea in wib (no1=8, no2=8) at eg1.c:7
#1  0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21

此结果显示了在 main() 的第 21 行中调用了函数 wib()(只要使用 'list 21' 就能证实这一点),而且 wib() 在 0 号帧中,main() 在 1 号帧中。由于 wib() 在 0 号帧中,那么它就是执行程序时发生算术错误的函数。

实际上,发出 'info locals' 命令时,gdb 会打印出当前帧中的局部变量,缺省情况下,这个帧中的函数就是被中断的函数(0 号帧)。可以使用命令 'frame' 打印当前帧。要查看 main 函数(在 1 号帧中)中的变量,可以发出 'frame 1' 切换到 1 号帧,然后发出 'info locals' 命令:

(gdb) frame 1
#1  0x8048435 in main (argc=1, argv=0xbffff9c4) at eg1.c:21
21          result = wib(value, div);
(gdb) info locals
value = 8
div = 8
result = 4
i = 2
total = 6

此信息显示了在第三次执行 "for" 循环时(i 等于 2)发生了错误,此时 "value" 等于 "div"。

可以通过如上所示在 'frame' 命令中明确指定号码,或者使用 'up' 命令在堆栈中上移以及 'down' 命令在堆栈中下移来切换帧。要获取有关帧的进一步信息,如它的地址和程序语言,可以使用命令 'info frame'。

gdb 堆栈命令可以在程序执行期间使用,也可以在 core 文件中使用,因此对于复杂的程序,可以在程序运行时跟踪它是如何转到函数的。

 

连接到其它进程

除了调试 core 文件或程序之外,gdb 还可以连接到已经运行的进程(它的程序已经过编译,并加入了调试信息),并中断该进程。只需用希望 gdb 连接的进程标识替换 core 文件名就可以执行此操作。以下是一个执行循环并睡眠的 示例程序

eg2 示例代码
#include 
int main(int argc, char *argv[])
{
  int i;
  for(i = 0; i < 60; i++)
  {
    sleep(1);
  }
  return 0;
}

使用 'gcc -g eg2.c -o eg2' 编译该程序并使用 './eg2 &' 运行该程序。请留意在启动该程序时在背景上打印的进程标识,在本例中是 1283:

./eg2 &
[3] 1283

启动 gdb 并指定进程标识,在我举的这个例子中是 'gdb eg2 1283'。gdb 会查找一个叫作 "1283" 的 core 文件。如果没有找到,那么只要进程 1283 正在运行(在本例中可能在 sleep() 中),gdb 就会连接并中断该进程:

...
/home/seager/gdb/1283: No such file or directory.
Attaching to program: /home/seager/gdb/eg2, Pid 1283
...
0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
(gdb)

此时,可以发出所有常用 gdb 命令。可以使用 'backtrace' 来查看当前位置与 main() 的相对关系,以及 mian() 的帧号是什么,然后切换到 main() 所在的帧,查看已经在 "for" 循环中运行了多少次:

(gdb) backtrace
#0  0x400a87f1 in __libc_nanosleep () from /lib/libc.so.6
#1  0x400a877d in __sleep (seconds=1) at ../sysdeps/unix/sysv/linux/sleep.c:78
#2  0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
(gdb) frame 2
#2  0x80483ef in main (argc=1, argv=0xbffff9c4) at eg2.c:7
7           sleep(1);
(gdb) print i
$1 = 50

如果已经完成了对程序的修改,可以 'detach' 命令继续执行程序,或者 'kill' 命令杀死进程。还可以首先使用 'file eg2' 装入文件,然后发出 'attach 1283' 命令连接到进程标识 1283 下的 eg2。

 

其它小技巧

gdb 可以让您通过使用 shell 命令在不退出调试环境的情况下运行 shell 命令,调用形式是 'shell [commandline]',这有助于在调试时更改源代码。

最后,在程序运行时,可以使用 'set ' 命令修改变量的值。在 gdb 下再次运行 eg1,使用命令 'break 7 if diff==0' 在第 7 行(将在此处计算结果)设置条件断点,然后运行程序。当 gdb 中断执行时,可以将 "diff" 设置成非零值,使程序继续运行直至结束:

Breakpoint 1, wib (no1=8, no2=8) at eg1.c:7
7         result = no1 / diff;
(gdb) print diff
$1 = 0
(gdb) set diff=1
(gdb) continue
Continuing.
0 wibed by 16 equals 10
Program exited normally.
 

阅读全文……

标签 : , ,

使用 gdb 抓取进程Stack Trace

[root@centos ~]# ps -ef|grep nginx

root      1348     1  0 12:01 ?        00:00:00 nginx: master process /usr/local/nginx/sbin/nginx

nobody    1350  1348  0 12:01 ?        00:00:00 nginx: worker process

root      2457  2223  0 12:05 pts/0    00:00:00 grep nginx

[root@centos ~]# gdb /usr/local/nginx/sbin/nginx 1350

GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.el6)

Copyright (C) 2010 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law.  Type "show copying"

and "show warranty" for details.

This GDB was configured as "i686-redhat-linux-gnu".

For bug reporting instructions, please see:

<http://www.gnu.org/software/gdb/bugs/>...

Reading symbols from /usr/local/nginx/sbin/nginx...done.

Attaching to program: /usr/local/nginx/sbin/nginx, process 1350

Reading symbols from /lib/libpthread.so.0...(no debugging symbols found)...done.

[Thread debugging using libthread_db enabled]

Loaded symbols for /lib/libpthread.so.0

Reading symbols from /lib/libcrypt.so.1...(no debugging symbols found)...done.

Loaded symbols for /lib/libcrypt.so.1

Reading symbols from /lib/libc.so.6...(no debugging symbols found)...done.

Loaded symbols for /lib/libc.so.6

Reading symbols from /lib/ld-linux.so.2...(no debugging symbols found)...done.

Loaded symbols for /lib/ld-linux.so.2

Reading symbols from /lib/libfreebl3.so...(no debugging symbols found)...done.

Loaded symbols for /lib/libfreebl3.so

Reading symbols from /lib/libdl.so.2...(no debugging symbols found)...done.

Loaded symbols for /lib/libdl.so.2

Reading symbols from /lib/libnss_files.so.2...(no debugging symbols found)...done.

Loaded symbols for /lib/libnss_files.so.2

0x00399416 in __kernel_vsyscall ()

Missing separate debuginfos, use: debuginfo-install glibc-2.12-1.132.el6_5.2.i686 nss-softokn-freebl-3.12.9-11.el6.i686

(gdb) bt

#0  0x00399416 in __kernel_vsyscall ()

#1  0x00a055c8 in __epoll_wait_nocancel () from /lib/libc.so.6

#2  0x08066d18 in ngx_epoll_process_events (cycle=0x946eeb8, timer=53791, flags=1) at src/event/modules/ngx_epoll_module.c:579

#3  0x0805fd41 in ngx_process_events_and_timers (cycle=0x946eeb8) at src/event/ngx_event.c:248

#4  0x08065add in ngx_worker_process_cycle (cycle=0x946eeb8, data=0x0) at src/os/unix/ngx_process_cycle.c:816

#5  0x080643a4 in ngx_spawn_process (cycle=0x946eeb8, proc=0x8065a26 <ngx_worker_process_cycle>, data=0x0, name=0x80c153a "worker process", respawn=-3)

    at src/os/unix/ngx_process.c:198

#6  0x08064fbf in ngx_start_worker_processes (cycle=0x946eeb8, n=1, type=-3) at src/os/unix/ngx_process_cycle.c:364

#7  0x08066035 in ngx_master_process_cycle (cycle=0x946eeb8) at src/os/unix/ngx_process_cycle.c:136

#8  0x0804b38d in main (argc=1, argv=0xbfba0114) at src/core/nginx.c:407

(gdb) n

Single stepping until exit from function __kernel_vsyscall,

which has no line number information.

0x00a055c8 in __epoll_wait_nocancel () from /lib/libc.so.6

(gdb)

 

 

标签 :

使用 GNU profiler 来提高代码运行速度

各种软件对于性能的需求可能会有很大的区别,但是很多应用程序都有非常严格的性能需求,这一点并不奇怪。电影播放器就是一个很好的例子:如果一个电影播放器只能以所需要速度的 75% 来播放电影,那么它几乎就没什么用处了。

其他应用程序(例如视频编码)如果是耗时非常长的操作,最好以 “批处理” 任务的方式运行,此时启动一个作业,让其一直运行,然后我们就可以去干别的事情了。尽管这些类型的应用程序没有这种硬性性能指标的限制,但是提高速度仍然会带来很多好处,例如可以在给定的时间内可以对更多电影进行编码,在同样的时间内可以以更高的品质进行编码。

通常,除了最简单的应用程序之外,对于其他应用程序来说,性能越好,这个应用程序的用处就越大,也就会越流行。由于这个原因,性能考虑是(也应该是)很多应用程序开发人员脑袋中的第一根弦。

不幸的是,很多尝试让应用程序速度更快的努力都白费了,因为开发人员通常都是对自己的软件进行一些小型的优化,而没有去研究程序在更大的范围内是如何操作的。例如,我们可能会花费大量的时间来让某个特定函数的运行速度达到原来的两倍,这一点非常不错,但是如果这个函数很少被调用(例如打开文件),那么将这个函数的执行时间从 200ms 减少到 100ms,对于整个软件的总体执行时间来说并不会有太大的影响。

有效地利用您的时间的方法是,尽量优化软件中被频繁调用的部分。例如,假设应用程序花了 50% 的时间在字符串处理函数上,如果可以对这些函数进行优化,提高 10% 的效率,那么应用程序的总体执行时间就会改进 5%。

因此,如果希望能够有效地对程序进行优化,那么精确地了解时间在应用程序中是如何花费的,以及真实的输入数据,这一点非常重要。这种行为就称为代码剖析(code profiling)。本文将简要介绍 GNU 编译器工具包所提供的一种剖析工具,它的名字让人可以产生无限遐想,叫 GNU profiler(gprof)。本文主要面向那些开放源码软件开发工具的新手。

gprof 来救援了

在开始介绍如何使用 gprof 之前,需要首先了解一下在整个开发周期中,剖析应该在何处进行。通常来说,编写代码应该有 3 个目标,按照重要性的次序分别如下所示:

  1. 保证软件可以正确地工作。这通常是开发过程的重点。通常,如果一个软件根本连我们期望它做的事情都实现不了,那么即使它运行速度非常快,也根本没有任何意义!显然,正确性在某些情况下可能并不是至关重要的;例如,如果一个电影播放器可以正确地播放 99% 的电影文件,但是偶然会有些显示问题,那它依然可以使用。但是通常来说,正确性要远远比速度更加重要。
  2. 保证软件是可维护的。这实际上是第一个目标的一个子项。通常,如果软件编写得可维护性不好,那么即使它最开始时可以很好地工作,很快您(或其他人)在修正 bug 或添加新特性时可能也会破坏程序的正确性。
  3. 让软件可以快速运行。这就是剖析的用武之地。当软件可以正确运行之后,我们就可以开始剖析的过程来帮助它更快地运行了。

假设我们现在已经有了一个可以工作的应用程序,接下来让我们来看一下如何使用 gprof 来精确测量应用程序执行过程中时间都花费到什么地方去了,这样做的目的是了解一下在什么地方进行优化效果最佳。

gprof 可以对 C、C++、Pascal 和 Fortran 77 应用程序进行剖析。本文中的例子使用的是 C。

清单 1. 耗时的应用程序示例
#include <stdio.h>
int a(void) {
  int i=0,g=0;
  while(i++<100000)
  {
     g+=i;
  }
  return g;
}
int b(void) {
  int i=0,g=0;
  while(i++<400000)
  {
    g+=i;
  }
  return g;
}
int main(int argc, char** argv)
{
   int iterations;
   if(argc != 2)
   {
      printf("Usage %s <No of Iterations>\n", argv[0]);
      exit(-1);
   }
   else
      iterations = atoi(argv[1]);
   printf("No of iterations = %d\n", iterations);
   while(iterations--)
   {
      a();
      b();
   }
}

正如我们从代码中可以看到的,这个非常简单的应用程序包括两个函数:a 和 b,它们都处于一个繁忙的循环中消耗 CPU 周期。main 函数中采用了一个循环来反复调用这两个函数。第二个函数 b 循环的次数是 a 函数的 4 倍,因此我们期望在对代码分析完之后,可以看出大概有 20% 的时间花在了 a 函数中,而 80% 的时间花在了 b 函数中。下面就开始剖析代码,并看一下我们的这些期望是否正确。

启用剖析非常简单,只需要在 gcc 编译标志中加上 -pg 即可。编译方法如下:

gcc example1.c -pg -o example1 -O2 -lc

在编译好这个应用程序之后,可以按照普通方式运行这个程序:

./example1 50000

当这个程序运行完之后,应该会看到在当前目录中新创建了一个文件 gmon.out。

使用输出结果

首先看一下 “flat profile”,我们可以使用 gprof 命令获得它,这需要为其传递可执行文件和 gmon.out 文件作为参数,如下所示:

gprof example1 gmon.out -p

这会输出以下内容:

清单 2. flat profile 的结果
Flat profile:
Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls  ms/call  ms/call  name
 80.24     63.85    63.85    50000     1.28     1.28  b
 20.26     79.97    16.12    50000     0.32     0.32  a

从这个输出结果中可以看到,正如我们期望的一样,b 函数所花费的时间大概是 a 函数所花费的时间的 4 倍。真正的数字并不是十分有用;由于取整舍入错误,这些数字可能并不是非常精确。

聪明的读者可能会注意到,很多函数调用(例如 printf)在这个输出中都没有出现。这是因为这些函数都是在 C 运行时库(libc.so)中的,(在本例中)它们都没有使用 -pg 进行编译,因此就没有对这个库中的函数收集剖析信息。稍后我们会回到这个问题上来。

接下来我们希望了解的是 “call graph”,这可以通过下面的方式获得:

gprof example1 gmon.out -q

这会输出下面的结果。

清单 3. Call graph
                     Call graph (explanation follows)
granularity: each sample hit covers 2 byte(s) for 0.01% of 79.97 seconds
index % time    self  children    called     name
                                                 <spontaneous>
[1]    100.0    0.00   79.97                 main [1]
               63.85    0.00   50000/50000       b [2]
               16.12    0.00   50000/50000       a [3]
-----------------------------------------------
               63.85    0.00   50000/50000       main [1]
[2]     79.8   63.85    0.00   50000         b [2]
-----------------------------------------------
               16.12    0.00   50000/50000       main [1]
[3]     20.2   16.12    0.00   50000         a [3]
-----------------------------------------------

最后,我们可能会希望获得一个 “带注解的源代码” 清单,它会将源代码输出到应用程序中,并加上每个函数被调用了多少次的注释。

要使用这种功能,请使用启用调试功能的标志来编译源代码,这样源代码就会被加入可执行程序中:

gcc example1.c -g -pg -o example1 -O2 -lc

像以前一样重新运行这个应用程序:

./example1 50000

gprof 命令现在应该是:

gprof example1 gmon.out -A

这会输出下面的结果:

清单 4. 带注释的源代码
*** File /home/martynh/profarticle/example1.c:
                #include <stdio.h>
       50000 -> int a(void) {
                  int i=0,g=0;
                  while(i++<100000)
                  {
                     g+=i;
                  }
                  return g;
                }
       50000 -> int b(void) {
                  int i=0,g=0;
                  while(i++<400000)
                  {
                    g+=i;
                  }
                  return g;
                }
                int main(int argc, char** argv)
       ##### -> {
                   int iterations;
                   if(argc != 2)
                   {
                      printf("Usage %s <No of Iterations>\n", argv[0]);
                      exit(-1);
                   }
                   else
                      iterations = atoi(argv[1]);
                   printf("No of iterations = %d\n", iterations);
                   while(iterations--)
                   {
                      a();
                      b();
                   }
                }
Top 10 Lines:
     Line      Count
        3      50000
       11      50000
Execution Summary:
        3   Executable lines in this file
        3   Lines executed
   100.00   Percent of the file executed
   100000   Total number of line executions
 33333.33   Average executions per line

共享库的支持

正如在前面曾经介绍的,对于代码剖析的支持是由编译器增加的,因此如果希望从共享库(包括 C 库 libc.a)中获得剖析信息,就需要使用 -pg来编译这些库。幸运的是,很多发行版都提供了已经启用代码剖析支持而编译的 C 库版本(libc_p.a)。

在我使用的发行版 gentoo 中,需要将 “profile” 添加到 USE 标志中,并重新执行 emerge glibc。当这个过程完成之后,就会看到 /usr/lib/libc_p.a 文件已经创建好了。对于没有按照标准提供 libc_p 的发行版本来说,需要检查它是否可以单独安装,或者可能需要自己下载 glibc 的源代码并进行编译。

在获得 libc_p.a 文件之后,就可以简单地重新编译前面的例子了,方法如下:

gcc example1.c -g -pg -o example1 -O2 -lc_p

然后,可以像以前一样运行这个应用程序,并获得 flat profile 或 call graph,应该会看到很多 C 运行函数,包括 printf(这些函数在我们的测试函数中并不是太重要)。

用户时间与内核时间

现在我们已经知道如何使用 gprof 了,接下来可以简单且有效地对应用程序进行分析了,希望可以消除性能瓶颈。

不过现在您可能已经注意到了 gprof 的最大缺陷:它只能分析应用程序在运行 过程中所消耗掉的用户 时间。通常来说,应用程序在运行时既要花费一些时间来运行用户代码,也要花费一些时间来运行 “系统代码”,例如内核系统调用。

如果对清单 1 稍加修改,就可以清楚地看出这个问题:

清单 5. 为清单 1 添加系统调用分析功能
#include <stdio.h>
int a(void) {
  sleep(1);
  return 0;
}
int b(void) {
  sleep(4);
  return 0;
}
int main(int argc, char** argv)
{
   int iterations;
   if(argc != 2)
   {
      printf("Usage %s <No of Iterations>\n", argv[0]);
      exit(-1);
   }
   else
      iterations = atoi(argv[1]);
   printf("No of iterations = %d\n", iterations);
   while(iterations--)
   {
      a();
      b();
   }
}

正如您可以看到的,我们对清单 1 中的代码进行了修改,现在 a 函数和 b 函数不再只处理繁忙的循环了,而是分别调用 C 运行时函数 sleep 来挂起执行 1 秒和 4 秒。

像以前一样编译这个应用程序:

gcc example2.c -g -pg -o example2 -O2 -lc_p

并让这个程序循环 30 次:

./example2 30

所生成的 flat profile 如下所示:

清单 6. flat profile 显示了系统调用的结果
Flat profile:
Each sample counts as 0.01 seconds.
 no time accumulated
  %   cumulative   self              self     total
 time   seconds   seconds    calls  Ts/call  Ts/call  name
  0.00      0.00     0.00      120     0.00     0.00  sigprocmask
  0.00      0.00     0.00       61     0.00     0.00  __libc_sigaction
  0.00      0.00     0.00       61     0.00     0.00  sigaction
  0.00      0.00     0.00       60     0.00     0.00  nanosleep
  0.00      0.00     0.00       60     0.00     0.00  sleep
  0.00      0.00     0.00       30     0.00     0.00  a
  0.00      0.00     0.00       30     0.00     0.00  b
  0.00      0.00     0.00       21     0.00     0.00  _IO_file_overflow
  0.00      0.00     0.00        3     0.00     0.00  _IO_new_file_xsputn
  0.00      0.00     0.00        2     0.00     0.00  _IO_new_do_write
  0.00      0.00     0.00        2     0.00     0.00  __find_specmb
  0.00      0.00     0.00        2     0.00     0.00  __guard_setup
  0.00      0.00     0.00        1     0.00     0.00  _IO_default_xsputn
  0.00      0.00     0.00        1     0.00     0.00  _IO_doallocbuf
  0.00      0.00     0.00        1     0.00     0.00  _IO_file_doallocate
  0.00      0.00     0.00        1     0.00     0.00  _IO_file_stat
  0.00      0.00     0.00        1     0.00     0.00  _IO_file_write
  0.00      0.00     0.00        1     0.00     0.00  _IO_setb
  0.00      0.00     0.00        1     0.00     0.00  ____strtol_l_internal
  0.00      0.00     0.00        1     0.00     0.00  ___fxstat64
  0.00      0.00     0.00        1     0.00     0.00  __cxa_atexit
  0.00      0.00     0.00        1     0.00     0.00  __errno_location
  0.00      0.00     0.00        1     0.00     0.00  __new_exitfn
  0.00      0.00     0.00        1     0.00     0.00  __strtol_internal
  0.00      0.00     0.00        1     0.00     0.00  _itoa_word
  0.00      0.00     0.00        1     0.00     0.00  _mcleanup
  0.00      0.00     0.00        1     0.00     0.00  atexit
  0.00      0.00     0.00        1     0.00     0.00  atoi
  0.00      0.00     0.00        1     0.00     0.00  exit
  0.00      0.00     0.00        1     0.00     0.00  flockfile
  0.00      0.00     0.00        1     0.00     0.00  funlockfile
  0.00      0.00     0.00        1     0.00     0.00  main
  0.00      0.00     0.00        1     0.00     0.00  mmap
  0.00      0.00     0.00        1     0.00     0.00  moncontrol
  0.00      0.00     0.00        1     0.00     0.00  new_do_write
  0.00      0.00     0.00        1     0.00     0.00  printf
  0.00      0.00     0.00        1     0.00     0.00  setitimer
  0.00      0.00     0.00        1     0.00     0.00  vfprintf
  0.00      0.00     0.00        1     0.00     0.00  write

如果对这个输出结果进行分析,我们就会看到,尽管 profiler 已经记录了每个函数被调用的确切次数,但是为这些函数记录的时间(实际上是所有函数)都是 0.00。这是因为 sleep 函数实际上是执行了一次对内核空间的调用,从而将应用程序的执行挂起了,然后有效地暂停执行,并等待内核再次将其唤醒。由于花在用户空间执行的时间与花在内核中睡眠的时间相比非常小,因此就被取整成零了。其原因是 gprof 仅仅是通过以固定的周期对程序运行时间 进行采样测量来工作的。因此,当程序不运行时,就不会对程序进行采样测量。

这实际上是一把双刃剑。从一个方面来说,这使得有些程序非常难以进行优化,例如花费大部分时间在内核空间的程序,或者由于外部因素(例如操作系统的 I/O 子系统过载)而运行得非常慢的程序。从另一个方面来说,这意味着剖析不会受到系统中其他事件的影响(例如另外一个用户使用了大量的 CPU 时间)。

通常,有一个很好的基准测试可以用来查看 gprof 对于帮助对应用程序进行优化是多么有用,方法是在 time 命令下面执行它。这个命令会显示一个应用程序运行完成需要多少时间,并可以测量它在用户空间和内核空间各花费了多少时间。

如果查看一下清单 2 中的例子:

time ./example2 30

输出结果应该如下所示:

清单 7. time 命令的输出结果
No of iterations = 30
real    2m30.295s
user    0m0.000s
sys     0m0.004s

我们可以看出几乎没有多少时间被花费在执行用户空间的代码上,因此 gprof 在此处不会非常有用。

结束语

尽管 gprof 存在上面的限制,但是它对于优化代码来说依然是个非常有用的工具,如果您的代码大部分是用户空间 CPU 密集型的,它的用处就更加明显。首先使用 time 来运行程序从而判断 gprof 是否能产生有用信息是个好主意。

如果 gprof 不适合您的剖析需要,那么还有其他一些工具可以克服 gprof 部分缺陷,包括 OProfile 和 Sysprof (请参看 参考资料 中有关这些工具信息的链接)。

从另一个方面来说,假设我们已经安装了 gcc,gprof 相对于其他工具来说,一个主要的优点是很可能早已在 Linux 机器上安装了需要使用的工具。

阅读全文……

标签 :

keepalived手册中未更新的功能挖掘及使用介绍_李小红_新浪博客

vrrp_script chk_sshd {
 script "killall -0 sshd"    # cheaper than pidof
 interval 2         # check every 2 seconds
 weight -4        # default prio: -4 if KO
}

vrrp_script chk_haproxy {
 script "killall -0 haproxy"    # cheaper than pidof
 interval 2        # check every 2 seconds
}

vrrp_script chk_http_port {
 script "/tcp/127.0.0.1/80"    # connects and exits
 interval 1        # check every second
 weight -2        # default prio: -2 if connect fails
}

vrrp_script chk_https_port {
 script "/tcp/127.0.0.1/443"
 interval 1
 weight -2
}

vrrp_script chk_smtp_port {
 script "/tcp/127.0.0.1/25"
 interval 1
 weight -2
}

vrrp_instance VI_1 {
 interface eth0  state MASTER  virtual_router_id 51  priority 100   virtual_ipaddress {
  192.168.200.18/25
 }

 track_interface {
  eth1 weight 2      # prio = +2 if UP
  eth2 weight -2      # prio = -2 if DOWN
  eth3        # no weight, fault if down
 }

 track_script {
  chk_sshd       # use default weight from the script
  chk_haproxy weight 2    # +2 if process is present
  chk_http_port
  chk_https_port
  chk_smtp_port
 }
}

vrrp_instance VI_2 {
 interface eth1  state MASTER  virtual_router_id 52  priority 100   virtual_ipaddress {
  192.168.201.18/26
 }

 track_interface {
  eth0 weight 2      # prio = +2 if UP
  eth2 weight -2      # prio = -2 if DOWN
  eth3        # no weight, fault if down
 }

 track_script {
  chk_haproxy weight 2
  chk_http_port
  chk_https_port
  chk_smtp_port
 }
}
1、vrrp_script和track_script
track_script指定检查脚本,定期运行它们来改变优先级,并最终引发主备切换。如果配过交换机的vrrp/hsrp部分的话,应该对这个功能比较熟悉
2、notify_stop
keepalived停止运行前运行notify_stop指定的脚本

下面是我这边应用上面两个功能介绍:
1、两台双master台数据库,用keepalivd的vrrp模式给客户端提供一个HA的虚拟IP
这个要求检查mysql数据库的状态,如果mysql出现问题,就降低优先级,这样虽然机器正常,也会引发新一轮的master选举,以保证mysql服务的高可用性

先定义两个检查脚本
vrrp_script chk_mysqld {
   script "/usr/bin/mysqladmin -u ganglia extended-status -pganglia_status | grep -q Slave_running"
   interval 10  # check every 10 seconds
   weight -40   # if failed, decrease 40 of the priority
   fall   3     # require 2 failures for failures
   rise   1     # require 1 sucesses for ok
}

vrrp_script chk_schedown {
   script "if [ -f /var/run/down ]; then exit 1; else exit 0; fi"
   interval 10  # check every 10 seconds
   weight -40   # if failed, decrease 40 of the priority
   fall   1     # require 2 failures for failures
   rise   1     # require 1 sucesses for ok
}
上面的chk_schedown脚本,给管理员提供一个切换的地方,如果管理员在master上手工touch /var/run/down,这样,流量会比这台机器上切走了。

阅读全文……

标签 : ,

安装配置keepalived 使nginx高可用

wget  ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-8.35.tar.gz

tar -zxvf pcre-8.35.tar.gz

cd pcre-8.35

./configure

make

make install

 

wget http://zlib.net/zlib-1.2.8.tar.gz

tar -zxvf zlib-1.2.8.tar.gz

cd zlib-1.2.8

./configure

make

make install

 

wget  http://nginx.org/download/nginx-1.7.0.tar.gz

tar -zvxf nginx-1.7.0.tar.gz

 

cd nginx1.7.0

./configure --with-pcre=/root/pcre-8.35 --with-zlib=/root/zlib-1.2.8

make && make install

 

 

 

 

yum install openssl-devel

 

wget  http://www.keepalived.org/software/keepalived-1.2.12.tar.gz

 

vi /etc/keepalived/keepalived.conf

10.124.55.134:

global_defs {

 

  notification_email {

[email protected]

  }

 

  notification_email_from [email protected]

  smtp_server 127.0.0.1

  smtp_connect_timeout 30

  router_id LVS_DEVEL

 

}

 

vrrp_script chk_http_port {

script "/opt/nginx_pid.sh" ############该脚本我们在后面书写

interval 2

weight 2

}

 

vrrp_instance VI_1 {

state MASTER        ############ 辅机为 BACKUP

interface eth0

virtual_router_id 51

mcast_src_ip 10.124.55.134

priority 102                  ########### 权值要比 back 高

advert_int 1

authentication {

auth_type PASS

auth_pass 1111

}

 

track_script { 

chk_http_port ### 执行监控的服务 

}

 

virtual_ipaddress {

 10.124.55.150 ############ 此处的虚拟IP 地址即 我们web所                                                      ####要访问的IP地址  必须在同一网段

}

 

}

10.124.55.135:

global_defs {

 

  notification_email {

[email protected]

  }

 

  notification_email_from [email protected]

  smtp_server 127.0.0.1

  smtp_connect_timeout 30

  router_id LVS_DEVEL

 

}

 

vrrp_script chk_http_port {

script "/opt/nginx_pid.sh" ############该脚本我们在后面书写

interval 2

weight 2

}

 

vrrp_instance VI_1 {

state BACKUP        ############ 辅机为 BACKUP

interface eth0

virtual_router_id 51

mcast_src_ip 10.124.55.135

priority 105                  ########### 权值要比 master 低

advert_int 1

authentication {

auth_type PASS

auth_pass 1111

}

 

track_script { 

chk_http_port ### 执行监控的服务 

}

 

virtual_ipaddress {

 10.124.55.150 ############ 此处的虚拟IP 地址即 我们web所                                                      ####要访问的IP地址  必须在同一网段

}

 

}

 

vi /opt/nginx_pid.sh

 

 

 

输入以下配置

 

nginxpid=`ps -C nginx --no-header | wc -l`

if [ $nginxpid -eq 0 ];then

/usr/local/nginx/sbin/nginx

sleep 5

  if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then

  /etc/init.d/keepalived stop

  fi

fi

 

 

标签 : ,

IBM IPV6 Can Cause Poor Java Performance - United States

Problem(Abstract)

If the entire network is not IPv6-enabled or capable, users may find that Java programs perform poorly.

Resolving the problem

This document explains that if the entire network is not IPv6-enabled or capable, users may find that Java programs perform poorly.

On IBM i 6.1 and 7.1, the default JVM is the IBM Technology for Java (J9). This JVM runs in the PASE environment. When IPv6 is enabled (which is the default), each DNS entry is checked to see if it is IPv6-capable. When PASE hits a slow or unresponsive DNS entry, it will wait for a reply or timeout. A java thread dump (javacore) will confirm that IPv6 is the source of the poor performance. The stack will show "Inet6AddressImpl" as shown below:

...
at java/net/Inet6AddressImpl.lookupAllHostAddr(Native Method) 
at java/net/InetAddress$2.lookupAllHostAddr(InetAddress.java:949) 
at java/net/InetAddress.getAddressFromNameService(InetAddress.java:1318) 
at java/net/InetAddress.getLocalHost(InetAddress.java:1505) 
...

Resolution: 

Disable IPv6 at JVM invocation by using the following java properties: 

o -Dcom.ibm.cacheLocalHost=true 
o -Djava.net.preferIPv4Stack=true 
o -Djava.net.preferIPv6Addresses=false

Note: These properties can be passed as command-line arguments or added to a SystemDefault.properties file.

阅读全文……

标签 : ,

使用nginx sticky模块实现基于cookie的负载均衡

1、nginx sticky 模块工作流程图

ip_hash

nginx sticky

2、下载安装nginx sticky
下载地址:http://code.google.com/p/nginx-sticky-module/downloads/list
目前共有2个版本,一个是1.0,一个是1.1,1.0已经寿终正寝了.1.1增加了权重的参数.

安装nginx + sticky模块

# wget http://nginx-sticky-module.googlecode.com/files/nginx-sticky-module-1.1.tar.gz
# tar -xzvf nginx-sticky-module-1.1.tar.gz

# wget http://nginx.org/download/nginx-1.0.6.tar.gz
# tar -czvf nginx-1.0.6
# cd nginx-1.0.6
# ./configure --prefix=/usr/local/nginx-1.0.6 --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --add-module=../nginx-sticky-module-1.1
# make
# make install

3、配置nginx sticky

nginx 的upstream使用sticky,如下

upstream cluster_test {
     sticky;
     server 192.168.100.209:80;
     server 192.168.100.225:80;
}

配置虚拟主机(以下有配置的可以忽略掉)

server {
        listen        80;
        server_name     test.ttlsa.com;
        index index.jsp;

        access_log /data/logs/nginx/test.ttlsa.com_access.log main;

        set $proxy_pass cluster_test;

        location /
        {
                proxy_pass http://$proxy_pass;
                include proxy.conf;
                add_header Cache-Control no-store;
        }

}

备注:
nginx和apache不同,nginx每次安装一个新的模块都需要重新编译一次,编译完成之后将nginx这一个文件拷贝到sbin下面即可.我这边全新安装一次,因为公司在两年前就选择了这个nginx版本,也没打算去换,所以大家可以把nginx换成自己最合适的一个版本,不用完全跟着文章来安装.

阅读全文……

标签 : , ,

Centos6.3禁用IPv6 - poijm - 博客频道 - CSDN.NET

在操作系统中禁用 IPv6 是没用的,这个需要在 JVM 中禁用 IPv6 特性,加入以下参数即可:

 

-Djava.net.preferIPv4Stack=true

 

IPv6 还没有完全普及,但是安装完系统之后IPv6是有效的,在一定程度上影响网络性能,所以在我们在完全不使用IPv6的情况下,最好关闭IPv6。。

1.修改/etc/sysconfig/network,追加:

 

[plain] view plaincopy
 
  1. NETWORKING_IPV6=no  

2.修改/etc/hosts,把ipv6的那句本地主机名解析的也注释掉:

 

#::1   localhost localhost6 localhost6.localdomain6

3.让系统不加载ipv6相关模块,这需要修改modprobe相关设定文件/etc/modprobe.d/dist.conf。

 

[plain] view plaincopy
 
  1. alias net-pf-10 off  
  2. alias ipv6 off  


4.重启系统,然后确认:

 

[root@test ~]# lsmod | grep -i ipv6
[root@test ~]# ifconfig | grep -i inet6
如果上述2个命令执行的结果没有任何显示,那么说明ipv6已经被完全禁止了。

阅读全文……

标签 :

java.sql.SQLException: OALL8 is in an inconsist... | Oracle Community

org.eclipse.birt.report.engine.api.EngineException: Cannot get the result set metadata.

SQL statement does not return a ResultSet object.

SQL error #1: 关闭的连接

SQL error #1:OALL8 处于不一致状态。

以下是可能的问题以及解决办法:

1, This error occurred for me because I made a change to a db table (added a column) and did not restart the application server to refresh the connection pools. So the prepared statements were still cached by the connections and needed to be refreshed.

2, I use weblogic 10.3 , ojdbc6.jar and my RAC is ojdbc14.jar.I use Spring. I often get that error of OALL8 being inconsistent. Real Cause is Network. If you see above in somebody's error codes, it says Invalid Packet length, which cannot be changed by us except network layer.
In my case too, I think network layer writes headers rampantly making Oracle 10G Dysfunction. I also get Closed Connection afterwards. So, Please check your router firewalls that is eating our jdbc sockets, and oracle would never know if connecting entity has passed away, disconnected, or network Slow speed made it assume erroneous situation.

3, "OALL8 is in an inconsistent state" is a generic exception that occurs in JDBC 10.1, 10.2, and 11.1. It indicates that an internal inconsistency has been detected in the JDBC connection but it does not provide information on what caused the inconsistency. The exception no longer occurs in JDBC 11.2. The error is usually caused by a bug in the JDBC code. These are very hard to analyze. The simplest solution is to upgrade the JDBC driver.

阅读全文……

Linux下实现文件双向同步 - 无云安全技术站 - 51CTO技术博客

使用 rsync 也可以进行双向文件同步,如:

rsync -azvr [email protected]::psmis_file /root/psmis_file && rsync -azvr /root/psmis_file/ [email protected]::psmis_file

一、Unison简介
Unison是Windows、Linux以及其他Unix平台下都可以使用的文件同步工具,它能使两个文件夹(本地或网络上的)保持内容的一致。Unison拥有与其它一些同步工具或文件系统的相同的特性,但也有自身的特点:
1.跨平台使用;
2.对内核和用户权限没有特别要求;
3.Unison是双向的,它能自动处理两分拷贝中更新没有冲突的部分,有冲突的部分将会显示出来让用户选择更新策略;
4.只要是能连通的两台主机,就可以运行unison,可以直接使用socket连接或安全的ssh连接方式,对带宽的要求不高,使用类似rsync的压缩传输协议。

环境如下:
vm1:10.13.114.19
vm2:10.13.114.32

二、编译安装Unison
Linux下通过源码包编译安装Unison时,需要用到Objective Caml compiler。
通过以下方式安装
[root@vm1 ~]# wget http://caml.inria.fr/pub/distrib/ocaml-3.12/ocaml-3.12.1.tar.gz
[root@vm1 ~]# tar -xzvf ocaml-3.12.1.tar.gz
[root@vm1 ~]# cd ocaml-3.12.1
[root@vm1 ocaml-3.12.1]# ./configure
[root@vm1 ocaml-3.12.1]# make world opt
[root@vm1 ocaml-3.12.1]# make install

编译安装Unison
[root@vm1 ~]# wget http://www.seas.upenn.edu/~bcpierce/unison//download/releases/stable/unison-2.40.63.tar.gz
[root@vm1 ~]# tar -xzvf unison-2.40.63.tar.gz
[root@vm1 ~]# cd unison-2.40.63
[root@vm1 unison-2.40.63]# make UISTYLE=text
[root@vm1 unison-2.40.63]# make install

在执行make install的过程中,可能会出现以下错误提示:
mv: cannot stat '/root/bin//unison': No such file or directory
make: [doinstall] Error 1 (ignored)
cp unison /root/bin/
cp: cannot create regular file '/root/bin/': Is a directory
make: *** [doinstall] Error 1

出现错误的原因在与Unison默认是将文件Copy到/root/bin目录,但Linux默认是没有该目录的,因此我们需要将生成的可执行文件unison复制到系统的PATH目录。
[root@vm1 unison-2.40.63]# cp unison /usr/local/bin



将可执行文件unison上传到远程主机10.13.114.32
[root@vm1 unison-2.40.63]# scp unison [email protected]:/root/
通过SSH登陆到远程主机,再将unison复制到vm2的PATH目录
[root@vm2 ~]#cp unison /usr/local/bin

三、配置ssh key信任
建议通过普通用户进行操作,理由是通过root操作本身就危险,免密码登陆的root就更危险了。

在两台服务器上创建admin用户
[root@vm1 ~]# useradd -m admin
[root@vm1 ~]# passwd 12345
[root@vm2 ~]# useradd -m admin
[root@vm2 ~]# passwd 123456

在vm1上创建key并配置vm2的信任
[root@vm1 ~]# su – unison
[admin@vm1 ~]$ ssh-keygen -t rsa
在提示保存私钥(key)和公钥(public key)的位置时,使用默认值;
在提示是否需要私钥密码(passphrase)时,直接敲回车,即不使用私钥密码。
之后,将生成一对密钥,id_rsa(私钥文件)和id_rsa.pub(公钥文件),保存在/home/unison/.ssh/目录下。

将公钥添加到vm2的 authorized_keys 文件中
将文件上传到vm2
[admin@vm1 ~]$ scp ~/.ssh/id_rsa.pub [email protected]:/home/unison/

使用rsync用户SSH到登陆到远程主机,并将公钥添加到 authorized_keys 文件中
[admin@vm2 ~]$ mkdir .ssh
[admin@vm2 ~]$ chmod 700 .ssh
[admin@vm2 ~]$ mv ~/id_rsa.pub ~/.ssh/authorized_keys
[admin@vm2 ~]$ chmod 600 ~/.ssh/authorized_keys

同理,执行以下步骤在vm2上创建key并配置vm1的信任
[root@vm2 ~]# su – admin
[admin@vm2 ~]$ ssh-keygen -t rsa

将文件上传到vm1
[admin@vm2 ~]$ scp ~/.ssh/id_rsa.pub [email protected]:/home/unison/

使用rsync用户SSH到登陆到vm1,并将公钥添加到 authorized_keys 文件中
[admin@vm1 ~]$ mv ~/id_rsa.pub ~/.ssh/authorized_keys

重启SSH服务
[root@vm1 ~]# /etc/init.d/sshd restart
[root@vm2 ~]# /etc/init.d/sshd restart

四、Unison的配置与使用
在两台服务器上创建test目录,用于测试
[root@vm1 ~]# su - admin
[unison@vm1 ~]$ mkdir test
[root@vm2 ~]# su - unison
[unison@vm2 ~]$ mkdir test

在两台服务器上分别执行一次unison,如果出现提示确认,则直接敲回车选择默认值
[unison@vm1 ~]$ unison /home/admin/test/ ssh://[email protected]//home/admin/test/
[unison@vm2 ~]$ unison /home/admin/test/ ssh://[email protected]//home/admin/test/

修改两台服务器的unison配置文件,输入以下内容
[unison@vm1 ~]$ vim /home/unison/.unison/default.prf
 

  1. #Unison preferences file 
  2. root = /home/admin/test 
  3. root = ssh://[email protected]//home/admin/test/ 
  4. #force = 
  5. #ignore = 
  6. batch = true 
  7. #repeat = 1 
  8. #retry = 3 
  9. owner = true 
  10. group = true 
  11. perms = -1 
  12. fastcheck = false 
  13. rsync = false 
  14. sshargs = -C 
  15. xferbycopying = true 
  16. log = true 
  17. logfile = /home/unison/.unison/unison.log 



[unison@vm2 ~]$ vim /home/unison/.unison/default.prf

  1. #Unison preferences file 
  2. root = /home/admin/test 
  3. root = ssh://[email protected]//home/admin/test/ 
  4. #force = 
  5. #ignore = 
  6. batch = true 
  7. #repeat = 1 
  8. #retry = 3 
  9. owner = true 
  10. group = true 
  11. perms = -1 
  12. fastcheck = false 
  13. rsync = false 
  14. sshargs = -C 
  15. xferbycopying = true 
  16. log = true 
  17. logfile = /home/unison/.unison/unison.log 



相关注解如下:
force表示会以本地所指定文件夹为标准,将该目录同步到远端。这里需要注意,如果指定了force参数,那么Unison就变成了单项同步了,也就是说会以force指定的文件夹为准进行同步,类似与rsync。
Unison双向同步基本原理是:假如有A B两个文件夹,A文件夹把自己的改动同步到B,B文件夹也把自己的改动同步到A,最后A B两文件夹的内容相同,是AB文件夹的合集。
Unison双向同步的一个缺点是,对于一个文件在两个同步文件夹中都被修改时,unison是不会去同步的,因为unison无法判断以那个为准。
ignore = Path表示忽略指定目录,即同步时不同步它。
batch = true,表示全自动模式,接受缺省动作,并执行。
-fastcheck true 表示同步时仅通过文件的创建时间来比较,如果选项为false,Unison则将比较两地文件的内容。
log = true 表示在终端输出运行信息。
logfile 指定输出的log文件。

另外,Unison有很多参数,这里仅介绍常用的几个,详细的请参看Unison手册。
-auto //接受缺省的动作,然后等待用户确认是否执行。
-batch //batch mode, 全自动模式,接受缺省动作,并执行。
-ignore xxx //增加 xxx 到忽略列表中
-ignorecase [true|false|default] //是否忽略文件名大小写
-follow xxx //是否支持对符号连接指向内容的同步
owner = true //保持同步过来的文件属主
group = true //保持同步过来的文件组信息
perms = -1 //保持同步过来的文件读写权限
repeat = 1 //间隔1秒后,开始新的一次同步检查
retry = 3 //失败重试
sshargs = -C //使用ssh的压缩传输方式
xferbycopying = true"
-immutable xxx //不变目录,扫描时可以忽略
-silent //安静模式
-times //同步修改时间
-path xxx 参数 //只同步 -path 参数指定的子目录以及文件,而非整个目录,-path 可以多次出现。

PS:Windows下的unison配置文件默认位于C:\Documents and Settings\currentuser\.unison目录,默认的配置文件名是default.prf。

五、测试
首先分别在server1与server2的/home/unison/test目录下创建文件或目录,然后在server1上执行unison,接着如果在server1与server2上都能看到各自创建的文件,就说明同步成功。

分别在server1与server2上创建文件
[unison@server1 ~]$ cd test
[unison@server1 test]$ touch 1.txt touch 3.txt
[unison@server2 ~]$ cd test
[unison@server2 test]$ touch 2.txt touch 4.txt

在server1上执行unison
[unison@server1 ~]$ unison

在server1与server2上查看文件是否同步
[unison@server1 ~]$ cd test
[unison@server1 test]$ ls
1.txt 2.txt 3.txt 4.txt
[unison@server2 ~]$ cd test
[unison@server2 test]$ ls
1.txt 2.txt 3.txt 4.txt

均看到了“1.txt 2.txt 3.txt 4.txt”所有文件,说明文件同步已经成功!

注意:第一次SSH连接的时候可能需要输入一次密码,之后就不需要输入了。

六、定期或实时执行同步
如果想要定期执行,则通过crontab计划任务来实现,例如通过以下方式设置每5分钟执行一次
[root@server1 ~]# su - unison
[unison@server1 ~]$ crontab -e
1    */5 * * * * /usr/local/bin/unison

阅读全文……

标签 : ,