-
Notifications
You must be signed in to change notification settings - Fork 8
/
search.xml
197 lines (138 loc) · 130 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[看懂的人都还在加班中...]]></title>
<url>http://ehlxr.me/2016/10/27/%E7%9C%8B%E6%87%82%E7%9A%84%E4%BA%BA%E9%83%BD%E8%BF%98%E5%9C%A8%E5%8A%A0%E7%8F%AD%E4%B8%AD/</url>
<content type="text"><![CDATA[以下是关于程序员的一些笑话,据说看懂的人都还在加班中。 0、老婆给当程序员的老公打电话:下班顺路买十个包子,如果看到卖西瓜的,买一个。当晚老公手捧一个包子进了家门。老婆怒道:你怎么只买一个包子?!老公甚恐,喃喃道:因为我真看到卖西瓜的了。 1、一程序员去面试,面试官问:“你毕业才两年,这三年工作经验是怎么来的?!”程序员答:“加班。” 2、某程序员对书法十分感兴趣,退休后决定在这方面有所建树。于是花重金购买了上等的文房四宝。一日,饭后突生雅兴,一番磨墨拟纸,并点上了上好的檀香,颇有王羲之风范,又具颜真卿气势,定神片刻,泼墨挥毫,郑重地写下一行字:hello world。 3、问:程序员最讨厌康熙的哪个儿子。答:胤禩。因为他是八阿哥(bug) 4、程序猿要了3个孩子,分别取名叫Ctrl、Alt 和 Delete,如果他们不听话,程序猿就只要同时敲他们一下就会好的。 5、今天在公司听到一句惨绝人寰骂人的话:“你 TM 就是一个没有对象的野指针!” 6、程xx遭遇车祸成植物人,医生说她活下来的希望只有万分之一,唤醒更为渺茫。她的同事和亲人没放弃,并根据程xx对 testing 痴迷的作风,每天都在她身边念:“你测的模块上线后回滚了。”奇迹发生了,程xx醒来第一句话:确认那模块是我测的? 7、一个程序员在海滨游泳时溺水身亡。他死前拼命的呼救,当时海滩上有许多救生员,但是没有人救他。因为他一直大喊“F1!”“F1!”,谁都不知道“F1”究竟是什么意思。 8、世界上最远的距离,是我在 if 里你在 else 里,虽然经常一起出现,但却永不结伴执行。 9、正在码代码 ing,医院回来的同事一脸的苦逼样子,问他怎么了?他回答:得了类风湿性关节炎了,我怕会遗传给下一代啊。我一脸的问号:谁说类风湿性关节炎能遗传的?丫一脸诧异:类不是继承的吗? 10、我很奇怪客栈这个词,难道后入住的必须先退房吗? 11、话说,决定一个程序员跳槽与否的关键因素是他前同事的现工资。 12、程序员最憋屈的事情就是:你辛辛苦苦熬夜写了一个风格优雅的源文件,被一个代码风格极差的同事改了且没署名,以至于别人都以为你是写的。 13、前端工程师说,我去交友网站找女朋友去了。朋友问,找到了么?工程师说,找到了他们页面的一个 bug`。 14、C 程序看不起 C++ 程序员, C++ 程序员看不起 Java 程序员, Java 程序员看不起 C# 程序员,C# 程序员看不起美工,周末了,美工带着妹子出去约会了,一群程序员还在加班! 15、据说一老外年轻的时候,立志要当一名伟大的作家。怎么才算伟大呢?他说:我写的东西全世界都要看到!看完他们必定会歇斯底里!会火冒三丈!会痛苦万分!结果,他成功了,他在微软公司负责写系统蓝屏时的报错提示信息。 16、程序员应聘必备词汇:了解=听过名字;熟悉=知道是啥;熟练=用过;精通=做过东西。 17、两程序员聊天,程序员甲抱怨:“做程序员太辛苦了,我想换行……我该怎么办?”程序员乙:“敲一下回车。” 18、程序员最讨厌的四件事:写注释、写文档、别人不写注释、别人不写文档…… 19、假如生活欺骗了你,找 50 个程序员问问为什么编程;假如生活让你想死,找 50 个程序员问问 Bug 改完了没有;假如你觉得生活拮据,找 50 个程序员问问工资涨了没有;假如你觉得活着无聊,找 50 个程序员问问他们一天都干了什么! 20、男人要记住,与女人吵架的要领是,要像在安装软件或注册网站时阅读 服务条款 那样,直接忽略所有的内容,到最后面勾选 我同意,然后点击 确定。 21、朋友今天遇到的真事:客户说我们设备卡,死活找不到原因,工程师赶到现场,给客户换了个鼠标垫,故障排除…… 22、产品经理:“你明白吧,这里向右划可以出菜单,然后需要一个闪烁的动画,还有,我想这个tab可以拉下来,你懂吧? 设计师:“别废话,把你要抄的产品给我看下。” 23、百度研发的无人驾驶汽车,你会发现有些地方你是去不了的。腾讯研发的无人驾驶汽车,你会发现很多地方你要去是要黄钻会员的。当然,谷歌研发的无人驾驶汽车,你会发现查无此车。 24、话说昨天是周日,程序猿跟产品经理一起看电视。每个节目看到一半程序猿就换台,看到一半就换台,几次之后产品经理终于忍无可忍的咆哮:老子刚看出点意思你就换、刚看出点意思你就换,到底还让不让人看啦?!程序猿淡定的盯着电视道:你半路改需求的时候我可没吱过声! 25、“为什么删除手机上的图标的时候它们会抖?”“它们怕被删除呗。”“那为什么电话短信之类系统自带的删不掉的也在抖呢?”“那是它们在得瑟…” 26、有时候觉得,电脑就像一个高贵冷艳的妹纸。400,是她冷冰冰地说:“我听不懂你在说什么”;401,是她无情地转身:“我不认识你,别说那些奇怪的话”;403,是她残酷的拒绝:“我听懂你的话,也认出你的脸,可我不爱你”;404,是她紧闭心门:“我这儿没有你想要的东西”;503,是“呵呵我去洗澡”。 27、问:为何软件正在占领全世界,而程序员得不到尊重?答曰:遍身罗绮者,不是养蚕人。 28、她来例假了肚子疼,他坐着她旁边,看了她一眼,拿出手机玩游戏,她看在眼里,心里凉了半截。两分钟后,她实在坐不下去了,正准备离开,只见他默默地递过来他的小米手机说:拿去捂着。 29、惊闻微软要裁员 1.8 万人。他们就不能让这些员工们“在后台运行”吗? 30、今天看到我同事在笔记本电脑上实现 滑动解锁 !真的,看到我当场就震精了,异常碉堡……你知道吗?!他的电脑开机解锁密码是 ASDFGHJKL;’ ,然后唰得一下过去最后一个键落在回车上,就,就解锁了! 31、骗子网站太特么多了,你一打开,必定跳出一个很下流的游戏广告!——但这还不是最关键的,关键是如果你忍不住点击进入游戏之后,就会发现这些下流的元素全都不见了!这也太没有职业道德了吧! 32、最近发现自己陷入了一种状态,叫开机迷失。开电脑前,该做什么清清楚楚。但只要一登录,随手开个网页,刷下新闻微博甚至仅仅是音乐网站,然后再回过神来,已经到了深夜,要做的事一项没做。而最痛苦的是,在睡前闭眼的时候,整个被浪费的白天和那些被耽误的正事,总会掺杂着负罪感,无比清晰地浮现。 33、我要设计一个新的智能机应用程序叫做 惊慌,只要你一说 老婆 这两个字, 它就会关闭所有网页, 隐藏所有跟女性的聊天以及怪怪的文件夹, 并且把我老婆的照片设为墙纸。 34、我从苹果手机的背面知道了一个好地方叫做加利福尼亚,据说那里有阳光和沙滩,还有 Google 和 Tesla。据说那里的人经常讨论怎么创业,而不是怎么移民。我虽然在中国,但也去过那里,不是在梦中,而是通过 VPN。 35、边上工位的妹纸叫柳依依,她的爸爸也是位程序员,依依的大姐叫玲玲,二姐叫玲依,三姐叫依玲 整理自网络]]></content>
</entry>
<entry>
<title><![CDATA[Windows 10 删除、恢复资源管理器中“视频、图片”等文件夹]]></title>
<url>http://ehlxr.me/2016/10/25/Windows-10-%E5%88%A0%E9%99%A4%E3%80%81%E6%81%A2%E5%A4%8D%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%99%A8%E4%B8%AD%E2%80%9C%E8%A7%86%E9%A2%91%E3%80%81%E5%9B%BE%E7%89%87%E2%80%9D%E7%AD%89%E6%96%87%E4%BB%B6%E5%A4%B9/</url>
<content type="text"><![CDATA[Windows 10 资源管理器中的”视频、图片、文档、下载、音乐、桌面”等文件夹目前通过一般途径是删除不了的,只能通过注册表的删除删掉,本文介绍一种比较简单的删除、恢复方法。 准备打开注册表(Win+R 输入 regedit)找到以下键:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\ 单击 NameSpace 右键 导出,保存文件后缀名为 .reg。 删除用文本编辑器打开该文件,只保留类似以下内容,并且在行首添加 - 号,然后保存。 1234567891011121314151617181920212223Windows Registry Editor Version 5.00[-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{088e3905-0323-4b02-9826-5d99428e115f}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{1CF1260C-4DD0-4ebb-811F-33C572699FDE}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{24ad3ad4-a569-4530-98e1-ab02f9417aa8}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{374DE290-123F-4565-9164-39C4925E467B}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{3ADD1653-EB32-4cb0-BBD7-DFA0ABB5ACCA}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{3dfdf296-dbec-4fb4-81d1-6a3438bcf4de}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{A0953C92-50DC-43bf-BE83-3742FED03C9C}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{A8CDFF1C-4878-43be-B5FD-F8091C1C60D0}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{B4BFCC3A-DB2C-424C-B029-7FE99A87C641}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{d3162b92-9365-467a-956b-92703aca08af}][-HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MyComputer\NameSpace\{f86fa3ab-70d2-4fc7-9c99-fcbf05467f3a}] 双击该文件即可删除。 恢复想要恢复,编辑文件,删除行首 - 号,保存,双击运行即可恢复。]]></content>
</entry>
<entry>
<title><![CDATA[设置 Sublime Text3 主题透明]]></title>
<url>http://ehlxr.me/2016/10/24/%E8%AE%BE%E7%BD%AE-Sublime-Text3-%E4%B8%BB%E9%A2%98%E9%80%8F%E6%98%8E/</url>
<content type="text"><![CDATA[1. 下载安装包下载地址:SublimeTextTrans。 2. 安装插件解压到 Sublime Text3 的 Packages 存放目录下,命名为:SublimeTextTrans。点击 Sublime Text3 的 Preferences -> Browse Packages 打开 Packages 存放目录,一般情况是在 C:\Users\YOURNAME\AppData\Roaming\Sublime Text 3\Packages 目录下。 3. 设置 Sublime Text3 的透明度级别。点击 Sublime Text3 的 Preferences -> Package Setting -> SublimeTextTrans -> Setting - User 就可以设置透明度的级别了。 也可以通过 Ctrl+Shift+1、2、3、4、5、6 加载预设的 6 个透明级别。预设的透明级别在 Preferences -> Package Setting -> SublimeTextTrans -> Setting - Default 中可以查看。]]></content>
</entry>
<entry>
<title><![CDATA[[转]用 Markdown 写印象笔记(Evernote)]]></title>
<url>http://ehlxr.me/2016/10/19/%E8%BD%AC-%E7%94%A8-Markdown-%E5%86%99%E5%8D%B0%E8%B1%A1%E7%AC%94%E8%AE%B0%EF%BC%88Evernote%EF%BC%89/</url>
<content type="text"><![CDATA[使用印象笔记很久了,什么都觉得很好,就是不支持 Markdown 书写语法,实在是太遗憾了。今天发现网上有人介绍了一款 Sublime 的一个插件 Evernote,尝试了一下觉得还是一个不错的方案,正好我也很喜欢使用 Sublime 编辑器,如此甚好,哈哈… 一、安装1.1 用 Sublime 的 PackageControl 安装 Evernote 插件1.2 设置 Sublime 与印象笔记做关联国内印象笔记用户打开链接:https://app.yinxiang.com/api/DeveloperToken.action,国际 Evernote 用户打开链接:https://www.evernote.com/api/DeveloperToken.action。然后点击页面按钮 Create a developer token 生成开发者秘钥。 打开 Sublime Preferences -> Package Settings -> Evernote -> Settings - User 在文件中贴入如下内容: 1234{ "noteStoreUrl": "", "token": ""} noteStoreUrl 和 token 值为之前打开的页面的上的值,然后保存。 测试是否成功:通过快捷键 ctrl+shift+p 打开 Sublime 命令窗口,输入 evernote,就会看见 Evernote 的许多命令,点击 evernote:list recent notes,如果看到罗列出最新的笔记,则说明授权成功。 二、快捷键设置插件默认没有添加快捷键,但可以自己配置。通过快捷键 ctrl+shift+p 打开 Sublime 命令窗口,输入 key binding,选择 User 那,写入你的内容。 下面是我的设置: 12345[ { "keys": ["Ctrl+e", "Ctrl+o"], "command": "open_evernote_note" }, { "keys": ["Ctrl+s"], "command": "save_evernote_note", "context": [{"key": "evernote_note"}, {"key": "evernote_has_guid"}] }, { "keys": ["Ctrl+s"], "command": "send_to_evernote", "context": [{"key": "evernote_note"}, {"key": "evernote_has_guid", "operator": "equal", "operand": false}] }] 解释:意思是,按 ctrl+e, o 后,会打开印象笔记,按 ctrl+s 会将笔记保存并且同步到印象笔记。 三、常用命令通过快捷键 ctrl+shift+p,打开 Sublime 命令窗口就可以使用一些常用命令了。 Evernote: New empty note:创建笔记 Evernote: Open Evernote Note:打开印象笔记 Evernote: List recent notes:打开最近笔记 Evernote: Search note:搜索笔记 四、个人配置主要更改了代码的字体和颜色,需要注意的是替换掉 noteStoreUrl 和 token 值为步骤 2 中获取内容。点击 Sublime Text 的 Preferences -> Package Settings -> Evernote -> Settings - User 粘贴以下内容: 123456789101112131415161718192021222324252627282930313233343536{ "noteStoreUrl": "更换为步骤 2 中获取的 noteStoreUrl", "token": "更换为步骤 2 中获取的 token", "inline_css": { "body": "", "pre": "color: #000000; font-family: Consolas,monospace; font-size: 0.9em; white-space: pre-wrap; word-wrap: break-word; border: 1px solid #cccccc; border-radius: 3px; overflow: auto; padding: 6px 10px; margin-bottom: 10px;", "code": "color: black; font-family: Consolas,monospace; font-size: 1.1em;", "inline-code": "color: #c7254e; font-family: Consolas,monospace; padding: 0.1em 0.2em; margin: 0.1em; font-size: 85%; background-color: #f9f2f4; border-radius: 3px; border: 1px solid #d6d6d6;", "h1": "margin-bottom: 1em; margin-top: 1.2em;", "footnotes": "border-top: 1px solid #9AB39B; font-size: 80%;", "hr": "color:#9AB39B;background-color:#9AB39B;height:1px;border:none;", "sup": "color:#6D6D6D;font-size:1ex", "blockquote": "border-left: .5ex solid #BFBFBF; margin-left: 0px; padding-left: 1em; margin-top: 1.4285em; margin-bottom: 1.4285em;", "table": "border-collapse: collapse; border-spacing: 0; margin: 1em;", "td": "border: 1px solid #DDD; padding: 6px 13px;", "th": "border: 1px solid #DDD; padding: 6px 13px;", "tr:odd": "border: 1px solid #DDD; padding: 6px 13px;", "tr:even": "border: 1px solid #DDD; padding: 6px 13px; background-color: #F8F8F8;" }, "code_highlighting_style": "github", "code_friendly": true, "gfm_tables": true, "wiki_tables": false, "emphasis_mark": "_", "strong_mark": "**", "item_mark": "*", "notes_order": "updated", "max_notes": 100, "update_on_save": false, "evernote_autocomplete": true, "sort_notebooks": false, "show_stacks": true, "open_single_result": true, "tab_prefix": "Evernote: ", "warn_on_close": true} 原文地址]]></content>
</entry>
<entry>
<title><![CDATA[[转]Kafka入门教程]]></title>
<url>http://ehlxr.me/2016/10/10/%E8%BD%AC-Kafka%E5%85%A5%E9%97%A8%E6%95%99%E7%A8%8B/</url>
<content type="text"><![CDATA[一、基本概念1. 介绍Kafka 是一个分布式的、可分区的、可复制的消息系统。它提供了普通消息系统的功能,但具有自己独特的设计。这个独特的设计是什么样的呢? 首先让我们看几个基本的消息系统术语: Kafka 将消息以 topic 为单位进行归纳。 将向 Kafka topic 发布消息的程序成为 producers。 将预订 topics 并消费消息的程序成为 consumer。 Kafka 以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个 broker。 producers 通过网络将消息发送到 Kafka 集群,Kafka 集群向消费者提供消息,如下图所示: 客户端和服务端通过 TCP 协议通信。Kafka 提供了 Java 客户端,并且对多种语言都提供了支持。 2. Topics 和 Logs先来看一下 Kafka 提供的一个抽象概念:topic。一个 topic 是对一组消息的归纳。对每个 topic,Kafka 对它的日志进行了分区,如下图所示: 每个分区都由一系列有序的、不可变的消息组成,这些消息被连续的追加到分区中。分区中的每个消息都有一个连续的序列号叫做 offset,用来在分区中唯一的标识这个消息。 在一个可配置的时间段内,Kafka 集群保留所有发布的消息,不管这些消息有没有被消费。比如,如果消息的保存策略被设置为 2 天,那么在一个消息被发布的两天时间内,它都是可以被消费的。之后它将被丢弃以释放空间。Kafka 的性能是和数据量无关的常量级的,所以保留太多的数据并不是问题。 实际上每个 consumer 唯一需要维护的数据是消息在日志中的位置,也就是 offset。这个 offset 有 consumer 来维护:一般情况下随着 consumer 不断的读取消息,这 offset 的值不断增加,但其实 consumer 可以以任意的顺序读取消息,比如它可以将 offset 设置成为一个旧的值来重读之前的消息。 以上特点的结合,使 Kafka consumers 非常的轻量级:它们可以在不对集群和其他 consumer 造成影响的情况下读取消息。你可以使用命令行来 tail 消息而不会对其他正在消费消息的 consumer 造成影响。 将日志分区可以达到以下目的:首先这使得每个日志的数量不会太大,可以在单个服务上保存。另外每个分区可以单独发布和消费,为并发操作 topic 提供了一种可能。 3. 分布式每个分区在 Kafka 集群的若干服务中都有副本,这样这些持有副本的服务可以共同处理数据和请求,副本数量是可以配置的。副本使 Kafka 具备了容错能力。 每个分区都由一个服务器作为 leader,零或若干服务器作为 followers,leader 负责处理消息的读和写,followers 则去复制 leader。如果 leader down 了,followers 中的一台则会自动成为 leader。集群中的每个服务都会同时扮演两个角色:作为它所持有的一部分分区的 leader,同时作为其他分区的 followers,这样集群就会据有较好的负载均衡。 4. ProducersProducer 将消息发布到它指定的 topic 中,并负责决定发布到哪个分区。通常简单的由负载均衡机制随机选择分区,但也可以通过特定的分区函数选择分区。使用的更多的是第二种。 5. Consumers发布消息通常有两种模式:队列模式(queuing)和发布-订阅模式(publish-subscribe)。 队列模式中 consumers 可以同时从服务端读取消息,每个消息只被其中一个 consumer 读到。 发布-订阅模式中消息被广播到所有的 consumer 中。 Consumers 可以加入一个 consumer 组,共同竞争一个 topic,topic 中的消息将被分发到组中的一个成员中。同一组中的 consumer 可以在不同的程序中,也可以在不同的机器上。如果所有的 consumer 都在一个组中,这就成为了传统的队列模式,在各 consumer 中实现负载均衡。如果所有的 consumer 都不在不同的组中,这就成为了发布-订阅模式,所有的消息都被分发到所有的 consumer 中。更常见的是,每个 topic 都有若干数量的 consumer 组,每个组都是一个逻辑上的 “订阅者”,为了容错和更好的稳定性,每个组由若干 consumer 组成。这其实就是一个发布-订阅模式,只不过订阅者是个组而不是单个consumer。 相比传统的消息系统,Kafka 可以很好的保证有序性。传统的队列在服务器上保存有序的消息,如果多个 consumers 同时从这个服务器消费消息,服务器就会以消息存储的顺序向 consumer 分发消息。虽然服务器按顺序发布消息,但是消息是被异步的分发到各 consumer 上,所以当消息到达时可能已经失去了原来的顺序,这意味着并发消费将导致顺序错乱。为了避免故障,这样的消息系统通常使用 专用consumer 的概念,其实就是只允许一个消费者消费消息,当然这就意味着失去了并发性。 在这方面 Kafka 做的更好,通过分区的概念,Kafka 可以在多个 consumer 组并发的情况下提供较好的有序性和负载均衡。将每个分区分只分发给一个 consumer 组,这样一个分区就只被这个组的一个 consumer 消费,就可以顺序的消费这个分区的消息。因为有多个分区,依然可以在多个 consumer 组之间进行负载均衡。 注意: consumer 组的数量不能多于分区的数量,也就是有多少分区就允许多少并发消费。 Kafka 只能保证一个分区之内消息的有序性,在不同的分区之间是不可以的,这已经可以满足大部分应用的需求。如果需要 topic 中所有消息的有序性,那就只能让这个 topic 只有一个分区,当然也就只有一个 consumer 组消费它。 二、环境搭建Step 1:下载 Kafka点击下载最新的版本并解压。 12$ tar -zxvf kafka_2.11-0.10.0.1.tgz -C ~/apps$ cd kafka_2.11-0.10.0.1 Step 2:启动服务Kafka 用到了 Zookeeper,所有首先启动 Zookeeper,下面简单的启用一个单实例的 Zookkeeper 服务。可以在命令的结尾加个 & 符号,这样就可以启动后离开控制台。 1234$ ./bin/zkServer.sh start ZooKeeper JMX enabled by defaultUsing config: /home/ehlxr/apps/zookeeper-3.4.9/bin/../conf/zoo.cfgStarting zookeeper ... STARTED 现在启动Kafka: 123$ bin/kafka-server-start.sh config/server.properties[2013-04-22 15:01:47,028] INFO Verifying properties (kafka.utils.VerifiableProperties)[2013-04-22 15:01:47,051] INFO Property socket.send.buffer.bytes is overridden to 1048576 (kafka.utils.VerifiableProperties) Step 3:创建 topic创建一个叫做 “test” 的 topic,它只有一个分区,一个副本。 1$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test 可以通过 list 命令查看创建的 topic: 12$ bin/kafka-topics.sh --list --zookeeper localhost:2181test 除了手动创建 topic,还可以配置 broker 让它自动创建 topic。 Step 4:发送消息Kafka 使用一个简单的命令行 producer,从文件中或者从标准输入中读取消息并发送到服务端。默认的每条命令将发送一条消息。 运行 producer 并在控制台中输一些消息,这些消息将被发送到服务端: 12$ bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test This is a messageThis is another message ctrl+c 可以退出发送。 Step 5:启动 consumerKafka 也有一个命令行 consumer 可以读取消息并输出到标准输出: 123$ bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginningThis is a messageThis is another message 你在一个终端中运行 consumer 命令行,另一个终端中运行 producer 命令行,就可以在一个终端输入消息,另一个终端读取消息。 这两个命令都有自己的可选参数,可以在运行的时候不加任何参数可以看到帮助信息。 Step 6:搭建一个多个 broker 的集群刚才只是启动了单个 broker,现在启动有 3 个 broker 组成的集群,这些 broker 节点也都是在本机上的。 首先为每个节点编写配置文件: 12$ cp config/server.properties config/server-1.properties$ cp config/server.properties config/server-2.properties 在拷贝出的新文件中添加以下参数: 1234config/server-1.properties: broker.id=1 port=9093 log.dir=/tmp/kafka-logs-1 1234config/server-2.properties: broker.id=2 port=9094 log.dir=/tmp/kafka-logs-2 broker.id 在集群中唯一的标注一个节点,因为在同一个机器上,所以必须制定不同的端口和日志文件,避免数据被覆盖。 刚才已经启动可 Zookeeper 和一个节点,现在启动另外两个节点: 123$ bin/kafka-server-start.sh config/server-1.properties &$ bin/kafka-server-start.sh config/server-2.properties & 创建一个拥有 3 个副本的 topic: 1$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic my-replicated-topic 现在我们搭建了一个集群,怎么知道每个节点的信息呢?运行 describe topics 命令就可以了: 1$ bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic my-replicated-topic 下面解释一下这些输出。第一行是对所有分区的一个描述,然后每个分区都会对应一行,因为我们只有一个分区所以下面就只加了一行。 leader:负责处理消息的读和写,leader是从所有节点中随机选择的。 replicas:列出了所有的副本节点,不管节点是否在服务中。 isr:是正在服务中的节点。 在我们的例子中,节点 1 是作为 leader 运行。 向 topic 发送消息: 1$ bin/kafka-console-producer.sh --broker-list localhost:9092 --topic my-replicated-topic 1my test message 1my test message 2 消费这些消息: 1$ bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic 12my test message 1my test message 2 测试一下容错能力,Broker 1 作为 leader 运行,现在我们 kill 掉它: 12$ ps | grep server-1.properties7564 ttys002 0:15.91 /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home/bin/java...$ kill -9 7564 另外一个节点被选做了 leader,node 1 不再出现在 in-sync 副本列表中: 1234$ bin/kafka-topics.sh --describe --zookeeper localhost:218192 --topic my-replicated-topicTopic:my-replicated-topic PartitionCount:1 ReplicationFactor:3 Configs:Topic: my-replicated-topic Partition: 0 Leader: 2 Replicas: 1,2,0 Isr: 2,0 虽然最初负责续写消息的 leader down 掉了,但之前的消息还是可以消费的: 1234$ bin/kafka-console-consumer.sh --zookeeper localhost:2181 --from-beginning --topic my-replicated-topic...my test message 1my test message 2 看来 Kafka 的容错机制还是不错的。 三、搭建Kafka开发环境我们搭建了 kafka 的服务器,并可以使用 Kafka 的命令行工具创建 topic,发送和接收消息。下面我们来搭建 kafka 的开发环境。 1. 添加依赖搭建开发环境需要引入 kafka 的 jar 包,一种方式是将 Kafka 安装包中 lib 下的 jar 包加入到项目的 classpath 中,这种比较简单了。不过我们使用另一种更加流行的方式:使用 maven 管理 jar 包依赖。 创建好 maven 项目后,在 pom.xml 中添加以下依赖: 12345<dependency> <groupId> org.apache.kafka</groupId > <artifactId> kafka_2.10</artifactId > <version> 0.8.0</ version></dependency> 添加依赖后你会发现有两个 jar 包的依赖找不到。没关系我都帮你想好了,点击这里下载这两个 jar 包,解压后你有两种选择,第一种是使用 mvn 的 install 命令将 jar 包安装到本地仓库,另一种是直接将解压后的文件夹拷贝到 mvn 本地仓库的 com 文件夹下,比如我的本地仓库是 d:\mvn,完成后我的目录结构是这样的: 2. 配置程序首先是一个充当配置文件作用的接口,配置了 Kafka 的各种连接参数: 123456789101112131415package com.sohu.kafkademon;public interface KafkaProperties { final static String zkConnect = "10.22.10.139:2181"; final static String groupId = "group1"; final static String topic = "topic1"; final static String kafkaServerURL = "10.22.10.139"; final static int kafkaServerPort = 9092; final static int kafkaProducerBufferSize = 64 * 1024; final static int connectionTimeOut = 20000; final static int reconnectInterval = 10000; final static String topic2 = "topic2"; final static String topic3 = "topic3"; final static String clientId = "SimpleConsumerDemoClient";} 3. Producer123456789101112131415161718192021222324252627282930313233package com.sohu.kafkademon;import java.util.Properties;import kafka.producer.KeyedMessage;import kafka.producer.ProducerConfig;public class KafkaProducer extends Thread { private final kafka.javaapi.producer.Producer<Integer, String> producer; private final String topic; private final Properties props = new Properties(); public KafkaProducer(String topic) { props.put("serializer.class", "kafka.serializer.StringEncoder"); props.put("metadata.broker.list", "10.22.10.139:9092"); producer = new kafka.javaapi.producer.Producer<Integer, String>(new ProducerConfig(props)); this.topic = topic; } @Override public void run() { int messageNo = 1; while (true) { String messageStr = new String("Message_" + messageNo); System.out.println("Send:" + messageStr); producer.send(new KeyedMessage<Integer, String>(topic, messageStr)); messageNo++; try { sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }} 4. Consumer123456789101112131415161718192021222324252627282930313233343536373839404142434445package com.sohu.kafkademon;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Properties;import kafka.consumer.ConsumerConfig;import kafka.consumer.ConsumerIterator;import kafka.consumer.KafkaStream;import kafka.javaapi.consumer.ConsumerConnector;public class KafkaConsumer extends Thread { private final ConsumerConnector consumer; private final String topic; public KafkaConsumer(String topic) { consumer = kafka.consumer.Consumer.createJavaConsumerConnector( createConsumerConfig()); this.topic = topic; } private static ConsumerConfig createConsumerConfig() { Properties props = new Properties(); props.put("zookeeper.connect", KafkaProperties.zkConnect); props.put("group.id", KafkaProperties.groupId); props.put("zookeeper.session.timeout.ms", "40000"); props.put("zookeeper.sync.time.ms", "200"); props.put("auto.commit.interval.ms", "1000"); return new ConsumerConfig(props); } @Override public void run() { Map<String, Integer> topicCountMap = new HashMap<String, Integer>(); topicCountMap.put(topic, new Integer(1)); Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = consumer.createMessageStreams(topicCountMap); KafkaStream<byte[], byte[]> stream = consumerMap.get(topic).get(0); ConsumerIterator<byte[], byte[]> it = stream.iterator(); while (it.hasNext()) { System.out.println("receive:" + new String(it.next().message())); try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }} 5. 简单的发送接收运行下面这个程序,就可以进行简单的发送接收消息了: 1234567891011package com.sohu.kafkademon;public class KafkaConsumerProducerDemo { public static void main(String[] args) { KafkaProducer producerThread = new KafkaProducer(KafkaProperties.topic); producerThread.start(); KafkaConsumer consumerThread = new KafkaConsumer(KafkaProperties.topic); consumerThread.start(); }} 6. 高级别的consumer下面是比较负载的发送接收的程序: 123456789101112131415161718192021222324252627282930313233343536373839404142434445package com.sohu.kafkademon;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Properties;import kafka.consumer.ConsumerConfig;import kafka.consumer.ConsumerIterator;import kafka.consumer.KafkaStream;import kafka.javaapi.consumer.ConsumerConnector;public class KafkaConsumer extends Thread { private final ConsumerConnector consumer; private final String topic; public KafkaConsumer(String topic) { consumer = kafka.consumer.Consumer.createJavaConsumerConnector( createConsumerConfig()); this.topic = topic; } private static ConsumerConfig createConsumerConfig() { Properties props = new Properties(); props.put("zookeeper.connect", KafkaProperties.zkConnect); props.put("group.id", KafkaProperties.groupId); props.put("zookeeper.session.timeout.ms", "40000"); props.put("zookeeper.sync.time.ms", "200"); props.put("auto.commit.interval.ms", "1000"); return new ConsumerConfig(props); } @Override public void run() { Map<String, Integer> topicCountMap = new HashMap<String, Integer>(); topicCountMap.put(topic, new Integer(1)); Map<String, List<KafkaStream<byte[], byte[]>>> consumerMap = consumer.createMessageStreams(topicCountMap); KafkaStream<byte[], byte[]> stream = consumerMap.get(topic).get(0); ConsumerIterator<byte[], byte[]> it = stream.iterator(); while (it.hasNext()) { System.out.println("receive:" + new String(it.next().message())); try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } }} 四、数据持久化1. 不要畏惧文件系统!Kafka 大量依赖文件系统去存储和缓存消息。对于硬盘有个传统的观念是硬盘总是很慢,这使很多人怀疑基于文件系统的架构能否提供优异的性能。实际上硬盘的快慢完全取决于使用它的方式。设计良好的硬盘架构可以和内存一样快。 在 6 块 7200 转的 SATA RAID-5 磁盘阵列的线性写速度差不多是 600MB/s,但是随即写的速度却是 100k/s,差了差不多6000倍。现代的操作系统都对次做了大量的优化,使用了 read-ahead 和 write-behind 的技巧,读取的时候成块的预读取数据,写的时候将各种微小琐碎的逻辑写入组织合并成一次较大的物理写入。对此的深入讨论可以查看这里,它们发现线性的访问磁盘,很多时候比随机的内存访问快得多。 为了提高性能,现代操作系统往往使用内存作为磁盘的缓存,现代操作系统乐于把所有空闲内存用作磁盘缓存,虽然这可能在缓存回收和重新分配时牺牲一些性能。所有的磁盘读写操作都会经过这个缓存,这不太可能被绕开除非直接使用 I/O。所以虽然每个程序都在自己的线程里只缓存了一份数据,但在操作系统的缓存里还有一份,这等于存了两份数据。 另外再来讨论一下 JVM,以下两个事实是众所周知的: Java对象占用空间是非常大的,差不多是要存储的数据的两倍甚至更高。 随着堆中数据量的增加,垃圾回收回变的越来越困难。 基于以上分析,如果把数据缓存在内存里,因为需要存储两份,不得不使用两倍的内存空间,Kafka 基于 JVM,又不得不将空间再次加倍,再加上要避免 GC 带来的性能影响,在一个 32G 内存的机器上,不得不使用到 28-30G 的内存空间。并且当系统重启的时候,又必须要将数据刷到内存中(10GB 内存差不多要用10分钟),就算使用冷刷新(不是一次性刷进内存,而是在使用数据的时候没有就刷到内存)也会导致最初的时候新能非常慢。但是使用文件系统,即使系统重启了,也不需要刷新数据。使用文件系统也简化了维护数据一致性的逻辑。 所以与传统的将数据缓存在内存中然后刷到硬盘的设计不同,Kafka 直接将数据写到了文件系统的日志中。 2. 常量时间的操作效率在大多数的消息系统中,数据持久化的机制往往是为每个 cosumer 提供一个 B 树或者其他的随机读写的数据结构。B 树当然是很棒的,但是也带了一些代价:比如 B 树的复杂度是 O(log N),O(log N) 通常被认为就是常量复杂度了,但对于硬盘操作来说并非如此。磁盘进行一次搜索需要 10ms,每个硬盘在同一时间只能进行一次搜索,这样并发处理就成了问题。虽然存储系统使用缓存进行了大量优化,但是对于树结构的性能的观察结果却表明,它的性能往往随着数据的增长而线性下降,数据增长一倍,速度就会降低一倍。 直观的讲,对于主要用于日志处理的消息系统,数据的持久化可以简单的通过将数据追加到文件中实现,读的时候从文件中读就好了。这样做的好处是读和写都是 O(1) 的,并且读操作不会阻塞写操作和其他操作。这样带来的性能优势是很明显的,因为性能和数据的大小没有关系了。 既然可以使用几乎没有容量限制(相对于内存来说)的硬盘空间建立消息系统,就可以在没有性能损失的情况下提供一些一般消息系统不具备的特性。比如,一般的消息系统都是在消息被消费后立即删除,Kafka 却可以将消息保存一段时间(比如一星期),这给 consumer 提供了很好的机动性和灵活性,这点在今后的文章中会有详述。 五、消息传输的事务定义之前讨论了 consumer 和 producer 是怎么工作的,现在来讨论一下数据传输方面。数据传输的事务定义通常有以下三种级别: 最多一次: 消息不会被重复发送,最多被传输一次,但也有可能一次不传输。 最少一次: 消息不会被漏发送,最少被传输一次,但也有可能被重复传输。 精确的一次(Exactly once): 不会漏传输也不会重复传输,每个消息都传输被一次而且仅仅被传输一次,这是大家所期望的。 大多数消息系统声称可以做到 “精确的一次”,但是仔细阅读它们的的文档可以看到里面存在误导,比如没有说明当 consumer 或 producer 失败时怎么样,或者当有多个 consumer 并行时怎么样,或写入硬盘的数据丢失时又会怎么样。kafka 的做法要更先进一些。当发布消息时,Kafka 有一个 “committed” 的概念,一旦消息被提交了,只要消息被写入的分区的所在的副本 broker 是活动的,数据就不会丢失。关于副本的活动的概念,下节文档会讨论。现在假设 broker 是不会 down 的。 如果 producer 发布消息时发生了网络错误,但又不确定实在提交之前发生的还是提交之后发生的,这种情况虽然不常见,但是必须考虑进去,现在 Kafka 版本还没有解决这个问题,将来的版本正在努力尝试解决。 并不是所有的情况都需要 “精确的一次” 这样高的级别,Kafka 允许 producer 灵活的指定级别。比如 producer 可以指定必须等待消息被提交的通知,或者完全的异步发送消息而不等待任何通知,或者仅仅等待 leader 声明它拿到了消息(followers 没有必要)。 现在从 consumer 的方面考虑这个问题,所有的副本都有相同的日志文件和相同的 offset,consumer 维护自己消费的消息的 offset,如果 consumer 不会崩溃当然可以在内存中保存这个值,当然谁也不能保证这点。如果 consumer 崩溃了,会有另外一个 consumer 接着消费消息,它需要从一个合适的 offset 继续处理。这种情况下可以有以下选择: consumer 可以先读取消息,然后将 offset 写入日志文件中,然后再处理消息。这存在一种可能就是在存储 offset 后还没处理消息就 crash 了,新的 consumer 继续从这个 offset 处理,那么就会有些消息永远不会被处理,这就是上面说的 “最多一次”。 consumer 可以先读取消息,处理消息,最后记录 offset,当然如果在记录 offset 之前就 crash 了,新的 consumer 会重复的消费一些消息,这就是上面说的 “最少一次”。 “精确一次” 可以通过将提交分为两个阶段来解决:保存了 offset 后提交一次,消息处理成功之后再提交一次。但是还有个更简单的做法:将消息的 offset 和消息被处理后的结果保存在一起。比如用 Hadoop ETL 处理消息时,将处理后的结果和 offset 同时保存在 HDFS 中,这样就能保证消息和 offser 同时被处理了。 六、性能优化Kafka 在提高效率方面做了很大努力。Kafka 的一个主要使用场景是处理网站活动日志,吞吐量是非常大的,每个页面都会产生好多次写操作。读方面,假设每个消息只被消费一次,读的量的也是很大的,Kafka 也尽量使读的操作更轻量化。 我们之前讨论了磁盘的性能问题,线性读写的情况下影响磁盘性能问题大约有两个方面:太多的琐碎的 I/O 操作和太多的字节拷贝。I/O 问题发生在客户端和服务端之间,也发生在服务端内部的持久化的操作中。 1. 消息集(message set)为了避免这些问题,Kafka 建立了 “消息集(message set)” 的概念,将消息组织到一起,作为处理的单位。以消息集为单位处理消息,比以单个的消息为单位处理,会提升不少性能。Producer 把消息集一块发送给服务端,而不是一条条的发送;服务端把消息集一次性的追加到日志文件中,这样减少了琐碎的 I/O 操作。consumer 也可以一次性的请求一个消息集。 另外一个性能优化是在字节拷贝方面。在低负载的情况下这不是问题,但是在高负载的情况下它的影响还是很大的。为了避免这个问题,Kafka 使用了标准的二进制消息格式,这个格式可以在 producer,broker 和 producer 之间共享而无需做任何改动。 2. Zero CopyBroker 维护的消息日志仅仅是一些目录文件,消息集以固定队的格式写入到日志文件中,这个格式 producer 和 consumer 是共享的,这使得 Kafka 可以一个很重要的点进行优化:消息在网络上的传递。现代的 unix 操作系统提供了高性能的将数据从页面缓存发送到 socket 的系统函数,在 linux 中,这个函数是 sendfile。 为了更好的理解 sendfile 的好处,我们先来看下一般将数据从文件发送到 socket 的数据流向: 操作系统把数据从文件拷贝内核中的页缓存中 应用程序从页缓存从把数据拷贝自己的内存缓存中 应用程序将数据写入到内核中 socket 缓存中操作系统把数据从 socket 缓存中拷贝到网卡接口缓存,从这里发送到网络上。 这显然是低效率的,有 4 次拷贝和 2 次系统调用。Sendfile 通过直接将数据从页面缓存发送网卡接口缓存,避免了重复拷贝,大大的优化了性能。 在一个多 consumers 的场景里,数据仅仅被拷贝到页面缓存一次而不是每次消费消息的时候都重复的进行拷贝。这使得消息以近乎网络带宽的速率发送出去。这样在磁盘层面你几乎看不到任何的读操作,因为数据都是从页面缓存中直接发送到网络上去了。 这篇文章详细介绍了 sendfile 和 zero-copy 技术在 Java 方面的应用。 3. 数据压缩很多时候,性能的瓶颈并非 CPU 或者硬盘而是网络带宽,对于需要在数据中心之间传送大量数据的应用更是如此。当然用户可以在没有 Kafka 支持的情况下各自压缩自己的消息,但是这将导致较低的压缩率,因为相比于将消息单独压缩,将大量文件压缩在一起才能起到最好的压缩效果。 Kafka 采用了端到端的压缩:因为有 “消息集” 的概念,客户端的消息可以一起被压缩后送到服务端,并以压缩后的格式写入日志文件,以压缩的格式发送到 consumer,消息从 producer 发出到 consumer 拿到都被是压缩的,只有在 consumer 使用的时候才被解压缩,所以叫做 “端到端的压缩”。 Kafka 支持 GZIP 和 Snappy 压缩协议。更详细的内容可以查看这里。 七、Producer 和 Consumer1. Kafka Producer 消息发送producer 直接将数据发送到 broker 的 leader(主节点),不需要在多个节点进行分发。为了帮助 producer 做到这点,所有的 Kafka 节点都可以及时的告知:哪些节点是活动的,目标 topic 目标分区的 leader 在哪。这样 producer 就可以直接将消息发送到目的地了。 客户端控制消息将被分发到哪个分区。可以通过负载均衡随机的选择,或者使用分区函数。Kafka 允许用户实现分区函数,指定分区的 key,将消息 hash 到不同的分区上(当然有需要的话,也可以覆盖这个分区函数自己实现逻辑)。比如如果你指定的 key 是 user id,那么同一个用户发送的消息都被发送到同一个分区上。经过分区之后,consumer 就可以有目的的消费某个分区的消息。 2. 异步发送批量发送可以很有效的提高发送效率。Kafka producer 的异步发送模式允许进行批量发送,先将消息缓存在内存中,然后一次请求批量发送出去。这个策略可以配置的,比如可以指定缓存的消息达到某个量的时候就发出去,或者缓存了固定的时间后就发送出去(比如 100 条消息就发送,或者每 5 秒发送一次)。这种策略将大大减少服务端的 I/O 次数。 既然缓存是在 producer 端进行的,那么当 producer 崩溃时,这些消息就会丢失。Kafka 0.8.1 的异步发送模式还不支持回调,就不能在发送出错时进行处理。Kafka 0.9 可能会增加这样的回调函数。见 Proposed Producer API。 3. Kafka ConsumerKafa consumer 消费消息时,向broker发出 fetch 请求去消费特定分区的消息。consumer 指定消息在日志中的偏移量(offset),就可以消费从这个位置开始的消息。customer 拥有了 offset 的控制权,可以向后回滚去重新消费之前的消息,这是很有意义的。 4. 推还是拉?Kafka 最初考虑的问题是,customer 应该从 brokes 拉取消息还是 brokers 将消息推送到 consumer,也就是 pull 还 push。在这方面,Kafka 遵循了一种大部分消息系统共同的传统的设计:producer 将消息推送到 broker,consumer 从 broker 拉取消息。 一些消息系统比如 Scribe 和 Apache Flume 采用了push 模式,将消息推送到下游的 consumer。这样做有好处也有坏处:由 broker 决定消息推送的速率,对于不同消费速率的 consumer 就不太好处理了。消息系统都致力于让 consumer 以最大的速率最快速的消费消息,但不幸的是,push 模式下,当 broker 推送的速率远大于 consumer 消费的速率时,consumer 恐怕就要崩溃了。最终 Kafka 还是选取了传统的 pull 模式。 Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push 模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率,将可能导致一次只推送较少的消息而造成浪费。Pull 模式下,consumer 就可以根据自己的消费能力去决定这些策略。 Pull 有个缺点是,如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,直到新消息到 t 达。为了避免这点,Kafka 有个参数可以让 consumer 阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送)。 5. 消费状态跟踪对消费消息状态的记录也是很重要的。 大部分消息系统在 broker 端的维护消息被消费的记录:一个消息被分发到 consumer 后 broker 就马上进行标记或者等待 customer 的通知后进行标记。这样也可以在消息在消费后立马就删除以减少空间占用。 但是这样会不会有什么问题呢?如果一条消息发送出去之后就立即被标记为消费过的,一旦 consumer 处理消息时失败了(比如程序崩溃)消息就丢失了。为了解决这个问题,很多消息系统提供了另外一个个功能:当消息被发送出去之后仅仅被标记为已发送状态,当接到 consumer 已经消费成功的通知后才标记为已被消费的状态。这虽然解决了消息丢失的问题,但产生了新问题,首先如果 consumer 处理消息成功了但是向 broker 发送响应时失败了,这条消息将被消费两次。第二个问题时,broker 必须维护每条消息的状态,并且每次都要先锁住消息然后更改状态然后释放锁。这样麻烦又来了,且不说要维护大量的状态数据,比如如果消息发送出去但没有收到消费成功的通知,这条消息将一直处于被锁定的状态,Kafka 采用了不同的策略。Topic 被分成了若干分区,每个分区在同一时间只被一个 consumer 消费。这意味着每个分区被消费的消息在日志中的位置仅仅是一个简单的整数:offset。这样就很容易标记每个分区消费状态就很容易了,仅仅需要一个整数而已。这样消费状态的跟踪就很简单了。 这带来了另外一个好处:consumer 可以把 offset 调成一个较老的值,去重新消费老的消息。这对传统的消息系统来说看起来有些不可思议,但确实是非常有用的,谁规定了一条消息只能被消费一次呢?consumer发现解析数据的程序有 bug,在修改 bug 后再来解析一次消息,看起来是很合理的额呀! 6. 离线处理消息高级的数据持久化允许 consumer 每个隔一段时间批量的将数据加载到线下系统中比如 Hadoop 或者数据仓库。这种情况下,Hadoop 可以将加载任务分拆,拆成每个 broker 或每个 topic 或每个分区一个加载任务。Hadoop 具有任务管理功能,当一个任务失败了就可以重启而不用担心数据被重新加载,只要从上次加载的位置继续加载消息就可以了。 八、主从同步Kafka 允许 topic 的分区拥有若干副本,这个数量是可以配置的,你可以为每个 topic 配置副本的数量。Kafka 会自动在每个个副本上备份数据,所以当一个节点 down 掉时数据依然是可用的。 Kafka 的副本功能不是必须的,你可以配置只有一个副本,这样其实就相当于只有一份数据。 创建副本的单位是 topic 的分区,每个分区都有一个 leader 和零或多个 followers。所有的读写操作都由 leader 处理,一般分区的数量都比 broker 的数量多的多,各分区的 leader 均匀的分布在 brokers 中。所有的 followers 都复制 leader 的日志,日志中的消息和顺序都和 leader 中的一致。flowers 向普通的 consumer 那样从 leader 那里拉取消息并保存在自己的日志文件中。 许多分布式的消息系统自动的处理失败的请求,它们对一个节点是否存活着(alive)有着清晰的定义。Kafka 判断一个节点是否活着有两个条件: 节点必须可以维护和 ZooKeeper 的连接,Zookeeper 通过心跳机制检查每个节点的连接。 如果节点是个 follower,他必须能及时的同步 leader 的写操作,延时不能太久。 符合以上条件的节点准确的说应该是“同步中的(in sync)”,而不是模糊的说是 “活着的” 或是 “失败的”。Leader 会追踪所有 “同步中” 的节点,一旦一个 down 掉了,或是卡住了,或是延时太久,leader 就会把它移除。至于延时多久算是 “太久”,是由参数 replica.lag.max.messages 决定的,怎样算是卡住了,怎是由参数 replica.lag.time.max.ms 决定的。 只有当消息被所有的副本加入到日志中时,才算是 “committed”,只有 committed 的消息才会发送给 consumer,这样就不用担心一旦 leader down 掉了消息会丢失。Producer 也可以选择是否等待消息被提交的通知,这个是由参数 request.required.acks 决定的。 Kafka 保证只要有一个 “同步中” 的节点,“committed” 的消息就不会丢失。 1. Leader的选择Kafka 的核心是日志文件,日志文件在集群中的同步是分布式数据系统最基础的要素。 如果leaders永远不会down的话我们就不需要followers了!一旦leader down掉了,需要在followers中选择一个新的leader.但是followers本身有可能延时太久或者crash,所以必须选择高质量的follower作为leader.必须保证,一旦一个消息被提交了,但是leader down掉了,新选出的leader必须可以提供这条消息。大部分的分布式系统采用了多数投票法则选择新的leader,对于多数投票法则,就是根据所有副本节点的状况动态的选择最适合的作为leader.Kafka并不是使用这种方法。 Kafaka动态维护了一个同步状态的副本的集合(a set of in-sync replicas),简称ISR,在这个集合中的节点都是和leader保持高度一致的,任何一条消息必须被这个集合中的每个节点读取并追加到日志中了,才回通知外部这个消息已经被提交了。因此这个集合中的任何一个节点随时都可以被选为leader.ISR在ZooKeeper中维护。ISR中有f+1个节点,就可以允许在f个节点down掉的情况下不会丢失消息并正常提供服。ISR的成员是动态的,如果一个节点被淘汰了,当它重新达到“同步中”的状态时,他可以重新加入ISR.这种leader的选择方式是非常快速的,适合kafka的应用场景。 一个邪恶的想法:如果所有节点都down掉了怎么办?Kafka对于数据不会丢失的保证,是基于至少一个节点是存活的,一旦所有节点都down了,这个就不能保证了。实际应用中,当所有的副本都down掉时,必须及时作出反应。可以有以下两种选择: 等待ISR中的任何一个节点恢复并担任leader。 选择所有节点中(不只是ISR)第一个恢复的节点作为leader. 这是一个在可用性和连续性之间的权衡。如果等待ISR中的节点恢复,一旦ISR中的节点起不起来或者数据都是了,那集群就永远恢复不了了。如果等待ISR意外的节点恢复,这个节点的数据就会被作为线上数据,有可能和真实的数据有所出入,因为有些数据它可能还没同步到。Kafka目前选择了第二种策略,在未来的版本中将使这个策略的选择可配置,可以根据场景灵活的选择。这种窘境不只Kafka会遇到,几乎所有的分布式数据系统都会遇到。 2. 副本管理以上仅仅以一个topic一个分区为例子进行了讨论,但实际上一个Kafka将会管理成千上万的topic分区.Kafka尽量的使所有分区均匀的分布到集群所有的节点上而不是集中在某些节点上,另外主从关系也尽量均衡这样每个几点都会担任一定比例的分区的leader。 优化leader的选择过程也是很重要的,它决定了系统发生故障时的空窗期有多久。Kafka选择一个节点作为“controller”,当发现有节点down掉的时候它负责在游泳分区的所有节点中选择新的leader,这使得Kafka可以批量的高效的管理所有分区节点的主从关系。如果controller down掉了,活着的节点中的一个会备切换为新的controller. 九、客户端API1. Kafka Producer APIsKafka Procuder API 有两种,它们分别是:kafka.producer.SyncProducer 和 kafka.producer.async.AsyncProducer。它们都实现了同一个接口: 12345678910class Producer { /* 将消息发送到指定分区 */ publicvoid send(kafka.javaapi.producer.ProducerData<K,V> producerData); /* 批量发送一批消息 */ publicvoid send(java.util.List<kafka.javaapi.producer.ProducerData<K,V>> producerData); /* 关闭producer */ publicvoid close();} Producer API提供了以下功能: 可以将多个消息缓存到本地队列里,然后异步的批量发送到broker,可以通过参数producer.type=async做到。缓存的大小可以通过一些参数指定:queue.time和batch.size。一个后台线程((kafka.producer.async.ProducerSendThread)从队列中取出数据并让kafka.producer.EventHandler将消息发送到broker,也可以通过参数event.handler定制handler,在producer端处理数据的不同的阶段注册处理器,比如可以对这一过程进行日志追踪,或进行一些监控。只需实现kafka.producer.async.CallbackHandler接口,并在callback.handler中配置。 自己编写Encoder来序列化消息,只需实现下面这个接口。默认的Encoder是kafka.serializer.DefaultEncoder。 123interface Encoder<T> { public Message toMessage(T data);} 提供了基于Zookeeper的broker自动感知能力,可以通过参数zk.connect实现。如果不使用Zookeeper,也可以使用broker.list参数指定一个静态的brokers列表,这样消息将被随机的发送到一个broker上,一旦选中的broker失败了,消息发送也就失败了。 通过分区函数kafka.producer.Partitioner类对消息分区。 123interface Partitioner<T> { int partition(T key, int numPartitions);} 分区函数有两个参数:key和可用的分区数量,从分区列表中选择一个分区并返回id。默认的分区策略是hash(key)%numPartitions.如果key是null,就随机的选择一个。可以通过参数partitioner.class定制分区函数。 2. KafKa Consumer APIsConsumer API有两个级别。低级别的和一个指定的broker保持连接,并在接收完消息后关闭连接,这个级别是无状态的,每次读取消息都带着offset。 高级别的API隐藏了和brokers连接的细节,在不必关心服务端架构的情况下和服务端通信。还可以自己维护消费状态,并可以通过一些条件指定订阅特定的topic,比如白名单黑名单或者正则表达式。 2.1 低级别的API123456789101112131415class SimpleConsumer { /*向一个broker发送读取请求并得到消息集 */ public ByteBufferMessageSet fetch(FetchRequest request); /*向一个broker发送读取请求并得到一个相应集 */ public MultiFetchResponse multifetch(List<FetchRequest> fetches); /** * 得到指定时间之前的offsets * 返回值是offsets列表,以倒序排序 * @param time: 时间,毫秒, * 如果指定为OffsetRequest$.MODULE$.LATIEST_TIME(), 得到最新的offset. * 如果指定为OffsetRequest$.MODULE$.EARLIEST_TIME(),得到最老的offset. */ publiclong[] getOffsetsBefore(String topic, int partition, long time, int maxNumOffsets);} 低级别的API是高级别API实现的基础,也是为了一些对维持消费状态有特殊需求的场景,比如Hadoop consumer这样的离线consumer。 2.2 高级别的API123456789101112131415161718192021222324/* 创建连接 */ConsumerConnector connector = Consumer.create(consumerConfig);interface ConsumerConnector {/*** 这个方法可以得到一个流的列表,每个流都是MessageAndMetadata的迭代,* 通过MessageAndMetadata可以拿到消息和其他的元数据(目前之后topic)* Input: a map of <topic, #streams>* Output: a map of <topic, list of message streams>*/public Map<String,List<KafkaStream>> createMessageStreams(Map<String,Int> topicCountMap);/*** 你也可以得到一个流的列表,它包含了符合TopicFiler的消息的迭代,* 一个TopicFilter是一个封装了白名单或黑名单的正则表达式。*/public List<KafkaStream> createMessageStreamsByFilter( TopicFilter topicFilter, int numStreams); /* 提交目前消费到的offset */ public commitOffsets() /* 关闭连接 */ public shutdown()} 这个API围绕着由KafkaStream实现的迭代器展开,每个流代表一系列从一个或多个分区多和broker上汇聚来的消息,每个流由一个线程处理,所以客户端可以在创建的时候通过参数指定想要几个流。一个流是多个分区多个broker的合并,但是每个分区的消息只会流向一个流。 每调用一次createMessageStreams都会将consumer注册到topic上,这样consumer和brokers之间的负载均衡就会进行调整。API鼓励每次调用创建更多的topic流以减少这种调整。createMessageStreamsByFilter方法注册监听可以感知新的符合filter的tipic。 十、消息和日志消息由一个固定长度的头部和可变长度的字节数组组成。头部包含了一个版本号和CRC32校验码。 1234567891011121314/*** 具有N个字节的消息的格式如下* 如果版本号是0* 1. 1个字节的 "magic" 标记* 2. 4个字节的CRC32校验码* 3. N - 5个字节的具体信息** 如果版本号是1* 1. 1个字节的 "magic" 标记* 2.1个字节的参数允许标注一些附加的信息比如是否压缩了,解码类型等* 3.4个字节的CRC32校验码* 4. N - 6 个字节的具体信息*/ 1. 日志一个叫做“my_topic”且有两个分区的的topic,它的日志有两个文件夹组成,my_topic_0和my_topic_1,每个文件夹里放着具体的数据文件,每个数据文件都是一系列的日志实体,每个日志实体有一个4个字节的整数N标注消息的长度,后边跟着N个字节的消息。每个消息都可以由一个64位的整数offset标注,offset标注了这条消息在发送到这个分区的消息流中的起始位置。每个日志文件的名称都是这个文件第一条日志的offset.所以第一个日志文件的名字就是00000000000.kafka.所以每相邻的两个文件名字的差就是一个数字S,S差不多就是配置文件中指定的日志文件的最大容量。 消息的格式都由一个统一的接口维护,所以消息可以在producer,broker和consumer之间无缝的传递。存储在硬盘上的消息格式如下所示: 消息长度: 4 bytes (value: 1+4+n) 版本号: 1 byte CRC校验码: 4 bytes 具体的消息: n bytes 2. 写操作消息被不断的追加到最后一个日志的末尾,当日志的大小达到一个指定的值时就会产生一个新的文件。对于写操作有两个参数,一个规定了消息的数量达到这个值时必须将数据刷新到硬盘上,另外一个规定了刷新到硬盘的时间间隔,这对数据的持久性是个保证,在系统崩溃的时候只会丢失一定数量的消息或者一个时间段的消息。 3. 读操作需要两个参数:一个64位的offset和一个S字节的最大读取量。S通常比单个消息的大小要大,但在一些个别消息比较大的情况下,S会小于单个消息的大小。这种情况下读操作会不断重试,每次重试都会将读取量加倍,直到读取到一个完整的消息。可以配置单个消息的最大值,这样服务器就会拒绝大小超过这个值的消息。也可以给客户端指定一个尝试读取的最大上限,避免为了读到一个完整的消息而无限次的重试。 在实际执行读取操纵时,首先需要定位数据所在的日志文件,然后根据offset计算出在这个日志中的offset(前面的的offset是整个分区的offset),然后在这个offset的位置进行读取。定位操作是由二分查找法完成的,Kafka在内存中为每个文件维护了offset的范围。 下面是发送给 consumer 的结果的格式: 1234567891011121314MessageSetSend (fetch result)total length : 4 byteserror code : 2 bytesmessage 1 : x bytes...message n : x bytesMultiMessageSetSend (multiFetch result)total length : 4 byteserror code : 2 bytesmessageSetSend 1...messageSetSend n 4. 删除日志管理器允许定制删除策略。目前的策略是删除修改时间在N天之前的日志(按时间删除),也可以使用另外一个策略:保留最后的N GB数据的策略(按大小删除)。为了避免在删除时阻塞读操作,采用了copy-on-write形式的实现,删除操作进行时,读取操作的二分查找功能实际是在一个静态的快照副本上进行的,这类似于Java的CopyOnWriteArrayList。 5. 可靠性保证日志文件有一个可配置的参数M,缓存超过这个数量的消息将被强行刷新到硬盘。一个日志矫正线程将循环检查最新的日志文件中的消息确认每个消息都是合法的。合法的标准为:所有文件的大小的和最大的offset小于日志文件的大小,并且消息的CRC32校验码与存储在消息实体中的校验码一致。如果在某个offset发现不合法的消息,从这个offset到下一个合法的offset之间的内容将被移除。 有两种情况必须考虑: 当发生崩溃时有些数据块未能写入。 写入了一些空白数据块。第二种情况的原因是,对于每个文件,操作系统都有一个inode(inode是指在许多“类Unix文件系统”中的一种数据结构。每个inode保存了文件系统中的一个文件系统对象,包括文件、目录、大小、设备文件、socket、管道, 等等),但无法保证更新inode和写入数据的顺序,当inode保存的大小信息被更新了,但写入数据时发生了崩溃,就产生了空白数据块。CRC校验码可以检查这些块并移除,当然因为崩溃而未写入的数据块也就丢失了。 原文地址]]></content>
</entry>
<entry>
<title><![CDATA[闲言碎语,不知所云...]]></title>
<url>http://ehlxr.me/2016/09/28/%E9%97%B2%E8%A8%80%E7%A2%8E%E8%AF%AD%EF%BC%8C%E4%B8%8D%E7%9F%A5%E6%89%80%E4%BA%91/</url>
<content type="text"><![CDATA[闲言碎语,不知所云,乱七八糟,聊以自慰… 搭建博客有一段时间了,记录总结的基本都是技术相关的,对于一个有强迫症加语文学的不好的人来说,总结技术文章太痛苦了,总结一篇文章得纠结好久,总觉得组织的语言不够恰当准确,每次都是改了又改,反复琢磨,怎奈肚子里的墨水实在是少的可怜,真是羡慕那些可以把自己心里所想的东西用文字能够表达很清楚的人。 说到底还是看的东西少,缺少写东西的锻炼,青春迷茫的时候有大把机会可以利用,可惜都不知道把时间用在了哪儿,等慢慢想要沉淀的时候才发现工作和生活中琐碎已经占据了所有的时间。但不管怎样,从此刻启程,做一些自己想要做的事儿,我想应该不会太晚吧! 搭建 Hexo 博客的初衷之一是觉得对于身为码农的我来说,Hexo 好玩,有新鲜感,对于非码农来说有一定的门槛(显的逼格高)。之二就是书写简单,不必局限于单一的编辑工具,少了刻意去追求排版样式烦恼。之三是觉得安全可靠,文章资源一切都可以掌控与自己,不必担心丢失,之前也买过 VPS 摆弄过 WordPress ,最后 VPS 租期到了,那段时间忙于找工作,博客的内容也没有及时保存下来,虽说博客没多少东西,但始终觉得有些遗憾… 夜深人不静,半夜公司楼下打车真是不好打。这几天每天到家都凌晨以后了,身体真是大不如前了…]]></content>
</entry>
<entry>
<title><![CDATA[Oh My Zsh 替换你的 Bash Shell]]></title>
<url>http://ehlxr.me/2016/09/24/Oh-My-Zsh-%E6%9B%BF%E6%8D%A2%E4%BD%A0%E7%9A%84-Bash-Shell/</url>
<content type="text"><![CDATA[Oh-My-Zsh is an open source, community-driven framework for managing your ZSH configuration. It comes bundled with a ton of helpful functions, helpers, plugins, themes, and a few things that make you shout… Oh My ZSH! 1. Oh My Zsh 简介无意中看见了 Linux 的一款 Shell,相比于 Bash 极其美艳,决定入坑试水一下,不试不知道,一试绝对有惊喜,不仅外观美艳而且功能强大,那还有不替换 Bash 的理由吗?搞起搞起…… Linux 提供了很多种 Shell ,想要查看系统有安装哪些 Shell 可以通过命令:chsh -l 或者 cat /etc/shells 进行查看。 1234567$ cat /etc/shells /bin/sh/bin/bash/sbin/nologin/usr/bin/sh/usr/bin/bash/usr/sbin/nologin 其中 Bash 是绝大多数 Linux 系统默认的 Shell,虽然 Zsh Shell 没有被所有 Linux 预安装,但几乎每一款 Linux 都包含 Zsh Shell, 根据不同版本的 Linux 可以用 apt-get、yum 等包管理器进行安装。 但是我们今天介绍的并非是 Zsh,而是 Oh My Zsh,这是个什么鬼了?虽说 Zsh Shell 很牛逼,但配置相当麻烦,所以阻挡了好多人尝试的勇气(我想这也是为啥 Zsh 不被 Linux 设为默认 Shell 的原因吧),但是永远不要低估一个爱折腾程序猿的创造力,国外一程序猿真就创造出了一款易于使用的 Zsh 版本:Oh My Zsh Oh My Zsh 几乎兼容日常使用的所有 Bash Shell 指令,让你可以无缝接入,不需要再去花额外的时间去适应。当然还扩展了大量炫酷的指令,要不然干嘛用它对吧,而且支持更换主题和插件机制。下面就开始搞起吧! 2. 安装安装 Oh My Zsh 首先需要安装 git 和 zsh,如果已经安装,请自行忽略。本文是基于 CentOS 介绍,其它 Linux 大致相同。 2.1 git 和 zsh 安装12345# gitsodu yum install git# zshsodu yum install zsh 2.2 Oh My Zsh 安装Oh My Zsh 提供了 curl 和 wget 两种安装方式,选择任意一种自己方便的方式即可。 12345# via curlsudo sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"# via wgetsudo sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)" 3. 配置使用3.1 切换 Shell安装完成之后,使用以下命令即可切换当前 Shell: 1chsh -s /bin/zsh 退出当前终端连接,再次登入即可看到当前的 Shell 已经成功更改为 Oh My Zsh。 3.2 更改主题Oh My Zsh 的配置文件路径为:~/.zshrc,配置文件中的 ZSH_THEME 就是主题配置字段,如下默认为 robbyrussell 主题配色。更多主题配色可参考 Oh My Zsh 主题,这里我推荐是用 ys 主题。 1ZSH_THEME="robbyrussell" 3.3 其它 Linux 用户配置使用Linux 其它用户想要使用 Oh My Zsh Shell,只需要拷贝当前用户家目录下的 ~/.zshrc 配置文件到当想要使用 Oh My Zsh Shell 用户的家目录下即可。需要注意的是配置文件中 Oh My Zsh 安装路径其它用户可以访问的到,所以最好更改为绝对路径,如下: 1export ZSH=/home/ehlxr/.oh-my-zsh 4. 插件配置Oh My Zsh 支持插件机制,配置插件可以大大简化相关繁琐重复的命令。例如默认开启的 git 插件,就可以使用以下图表中的简写命令了: 那如何配置其它的插件了?只需要在 Oh My Zsh 的配置文件 ~/.zshrc 中找到 plugins=(git) 字段,配置插件的名称即可,多个插件使用空格隔开。 那支持哪些插件了?Oh My Zsh 安装目录下的 plugins 目录下就是支持的所有插件。例如:ls ~/.oh-my-zsh/plugins 就可以看到所有支持的插件名称了。 Oh My Zsh 支持插件的简写说明可以参看 oh-my-zsh Plugins wiki。 5. 常用命令简介 l 等价与 ls -la,查看当前目录下所有文件。 打开目录不需要再敲 cd,直接输入目录名即可。 TAB 提示更加聪明。 聪明的历史记录,例如:敲下 ls 命令,按下键盘 up 按键,就会带出以 ls 开头的历史记录命令。]]></content>
</entry>
<entry>
<title><![CDATA[5 分钟搭建 Git 服务器-Gogs]]></title>
<url>http://ehlxr.me/2016/09/06/5-%E5%88%86%E9%92%9F%E6%90%AD%E5%BB%BA-Git-%E6%9C%8D%E5%8A%A1%E5%99%A8-Gogs/</url>
<content type="text"><![CDATA[Gogs 基于 Go 语言的自助 Git 服务。它具有易安装、跨平台、轻量级、开源化等特性… 最近新到一家公司,发现在使用 Gogs 搭建 Git 服务,遂研究了一下,和前段时间研究的 GitLab 做了一个简单的对比,虽然 Gogs 相对与 GitLab 还比较年轻,也许没有 GitLab 强大和稳健,但 Gogs 更加简单易用,而且能够满足正常的工作使用。 Gogs 是轻量级的 Git 服务,正如官方介绍的:一个廉价的树莓派的配置足以满足 Gogs 的最低系统硬件要求。最大程度上节省您的服务器资源!关键的一点是免费开源的,所有的代码都开源在 GitHub 上。下面结合官方的介绍,总结一下在 Linux 系统下的安装方法,真的是相当的简单,5 分钟足矣! 一、安装1.1 下载下载对应系统版本的二进制安装包,并上传至 Linux 系统,或通过以下命令下载: 1$ wget https://github.com/gogits/gogs/releases/download/v0.9.97/linux_amd64.tar.gz Gogs发布版本 1.2 解压安装包1$ tar -xzvf gogs_v0.9.97_linux_amd64.tar.gz 1.3 安装进入到刚刚解压后的目录执行命令 ./gogs web,出现以下信息: 1.4 配置打开浏览器输地址入:http://ip:3000,第一次会出现以下的配置界面,根据实际情况选择即可。 1.5 完成安装之后进入以下界面,表明安装已经完成。 1.6 后台运行后台运行可参考以下命令: 1$ nohup ./gogs web > nohup.out 2>&1 & 升级引用自官方二级制升级文档 2.1 首先,确认当前安装的位置: 1234567# 默认位置在 git 用户下的家目录$ sudo su - git$ cd ~$ pwd/home/git$ lsgogs gogs-repositories 2.2 然后将当前目录移动到另一个临时的位置,但不是删除!1$ mv gogs gogs_old 2.3 下载并解压新的二进制:12345# 请根据系统和类型获取相应的二进制版本$ wget https://dl.gogs.io/gogs_v$VERSION_$OS_$ARCH.tar.gz$ tar -zxvf gogs_v$VERSION_$OS_$ARCH.tar.gz$ lsgogs gogs_old gogs-repositories gogs_v$VERSION_$OS_$ARCH.tar.gz 2.4 复制 custom、data 和 log 目录到新解压的目录中:123$ cp -R gogs_old/custom gogs$ cp -R gogs_old/data gogs$ cp -R gogs_old/log gogs 2.5 最后,运行并打开浏览器进行测试:12$ cd gogs$ ./gogs web]]></content>
</entry>
<entry>
<title><![CDATA[CentOS 中配置 Git 命令自动补全]]></title>
<url>http://ehlxr.me/2016/09/04/CentOS-%E4%B8%AD%E9%85%8D%E7%BD%AE-Git-%E5%91%BD%E4%BB%A4%E8%87%AA%E5%8A%A8%E8%A1%A5%E5%85%A8/</url>
<content type="text"><![CDATA[1. Step 1保存以下文件的内容为:git-completion.bash git-completion.bash 2. Step 2将上述文件 git-completion.bash copy 至个人 home 目录,可设为隐藏文件以免后续被误删。 1$ cp git-completion.bash ~/.git-completion.bash Step 3编辑环境变量文件 1$ vi ~/.bashrc 在最后加入下面内容 1source ~/.git-completion.bash 完成以上步骤后,重启 shell,就可以通过 tab 键自动补全 Git 命令了。]]></content>
</entry>
<entry>
<title><![CDATA[Naruto-Pictures]]></title>
<url>http://ehlxr.me/2016/09/02/Naruto-Pictures/</url>
<content type="text"><![CDATA[]]></content>
</entry>
<entry>
<title><![CDATA[使用Hexo基于GitHub Pages搭建个人博客(三)]]></title>
<url>http://ehlxr.me/2016/08/30/%E4%BD%BF%E7%94%A8Hexo%E5%9F%BA%E4%BA%8EGitHub-Pages%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%EF%BC%88%E4%B8%89%EF%BC%89/</url>
<content type="text"><![CDATA[生命不息,折腾不休。从搭建 blog 以来,博文虽没有写几篇,但折腾的时间花了不少,走过了不少弯路,也踩过了不少的坑,虽然很懒,但本着好记性不如烂笔头的宗旨,打算在折腾记忆尚未磨灭之际记录一下走过的路和踩过的坑… 接着前两篇使用 Hexo 基于 GitHub Pages 搭建个人博客之上,本文记录了在使用非常漂亮简洁的 Next 主题过程中的各种折腾,其它主题的相关设置大同小异。再次衷心的感谢 Hexo 的作者和 Next 主题作者的无私奉献。 一、主题基本配置记录一下 Next 主题的基本配置、设置「阅读全文」、关闭新建页面的评论功能、页面文章的篇数、宽度调节、设置「JavaScript 第三方库」等内容、在线字体替换(选择关闭,加快访问速度) 1.1 主题安装Next 主题安装与所有 Hexo 主题安装一样。当克隆或者下载(Next主题GitHub地址)完成主题文件后,拷贝至站点目录的 themes 目录下,一般命名为 next,打开站点配置文件, 找到 theme 字段,并将其值更改为 next 即可。 1theme: next 1.2 Next 主题宽度调节编辑 themes/next/source/css/_variables/custom.styl 文件,新增变量: 12345// 修改成你期望的宽度$content-desktop = 700px// 当视窗超过 1600px 后的宽度$content-desktop-large = 900px 以上方法不适用 Pisces Scheme Pisces Scheme 编辑 themes/next/source/css/_schemes/Picses/_layout.styl 文件,更改以下 css 选项定义值: 123.header{ width: 1150px; }.container .main-inner { width: 1150px; }.content-wrap { width: calc(100% - 260px); } 二、多说评论2.1 安装多说注册多说,登录后在首页选择 “我要安装”。 2.2 创建站点按要求填写如下图所示表单: 注意: 多说域名 这一栏填写的即是你的 duoshuo_shortname,在下一步主题配置中需要使用到。 2.3 主题配置多说创建站点完成后,在 主题配置文件 中新增 duoshuo_shortname 字段,值设置成上一步创建站点中填写的 多说域名 即可。 2.4 开启多说热评文章在 主题配置文件 中,设置 duoshuo_hotartical 配置项的值为 true,即可开启多说热评文章。 2.5 多说评论样式调整登录多说后,在首页右上角点击“后台管理”,选择站点名称打开多说后台管理页面,选择“设置”下拉找到“自定义CSS”输入框,填写以下CSS样式,效果参考本站文章结尾评论样式。 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970/*-------------访客底部----------------*/.ds-recent-visitors { margin-bottom: 200px;}@media (max-width: 768px) { .ds-recent-visitors { margin-bottom: 440px; }}/*-------------非圆角----------------*/#ds-reset .ds-rounded { border-radius: 0px;}.theme-next #ds-thread #ds-reset .ds-textarea-wrapper { border-top-right-radius: 0px; border-top-left-radius: 0px;}.theme-next #ds-thread #ds-reset .ds-post-button { border-radius: 0px;}.ds-post-self xmp { word-wrap: break-word;}/*-------------访客----------------*/#ds-reset .ds-avatar img,#ds-recent-visitors .ds-avatar img { width: 54px; height: 54px; /*设置图像的长和宽,这里要根据自己的评论框情况更改*/ border-radius: 27px; /*设置图像圆角效果,在这里我直接设置了超过width/2的像素,即为圆形了*/ -webkit-border-radius: 27px; /*圆角效果:兼容webkit浏览器*/ -moz-border-radius: 27px; box-shadow: inset 0 -1px 0 #3333sf; /*设置图像阴影效果*/ -webkit-box-shadow: inset 0 -1px 0 #3333sf; -webkit-transition: 0.4s; -webkit-transition: -webkit-transform 0.4s ease-out; transition: transform 0.4s ease-out; /*变化时间设置为0.4秒(变化动作即为下面的图像旋转360读)*/ -moz-transition: -moz-transform 0.4s ease-out;}/*-------------访客悬浮在头像----------------*/#ds-reset .ds-avatar img:hover,#ds-recent-visitors .ds-avatar img:hover { box-shadow: 0 0 10px #fff;rgba(255, 255, 255, .6), inset 0 0 20 px rgba(255, 255, 255, 1); -webkit-box-shadow: 0 0 10px #fff;rgba(255, 255, 255, .6), inset 0 0 20 px rgba(255, 255, 255, 1); transform: rotateZ(360deg); /*图像旋转360度*/ -webkit-transform: rotateZ(360deg); -moz-transform: rotateZ(360deg);}#ds-thread #ds-reset .ds-textarea-wrapper textarea { background: url(http://ww4.sinaimg.cn/small/649a4735gw1et7gnhy5fej20zk0m8q3q.jpg) right no-repeat;}#ds-recent-visitors .ds-avatar { float: left}/*-------------隐藏版权----------------*/#ds-thread #ds-reset .ds-powered-by { display: none;} 三、统计我使用 LeanCloud 统计文章阅读数,使用不蒜子统计站点的 PV 和 UV 数。 3.1 文章阅读次数统计(LeanCloud)参考为NexT主题添加文章阅读量统计功能 3.2 不蒜子统计站点访问统计编辑 主题配置文件 中的 busuanzi_count 的配置项,配置以下内容: 1234567891011121314151617# count values only if the other configs are falseenable: true# custom uv span for the whole sitesite_uv: truesite_uv_header: <i class="fa fa-user"></i>site_uv_footer:# custom pv span for the whole sitesite_pv: truesite_pv_header: <i class="fa fa-eye"></i>site_pv_footer:# custom pv span for one page onlypage_pv: falsepage_pv_header: <i class="fa fa-file-o"></i>page_pv_footer: site/page_pv/uv_header 和 site/page_pv/uv_footer 为自定义样式配置,相关的值留空时将不显示,可以使用(带特效的)font-awesome。 四、设置 RSS4.1 安装 hexo-generator-feed在站点的根目录下执行以下命令: 1$ npm install hexo-generator-feed --save 4.2 启用 RSS编辑 站点配置文件,新增以下内容到任意位置: 123456789# RSS订阅支持plugin:- hexo-generator-feed# Feed Atomfeed:type: atompath: atom.xmllimit: 20 五、内容分享使用 JiaThis 作为内容分享服务,具体步骤如下: 编辑 站点配置文件, 添加字段 jiathis,值为 true 即可。 六、搜索服务6.1 安装 hexo-generator-search在站点的根目录下执行以下命令: 1$ npm install hexo-generator-search --save 6.2 启用搜索编辑 站点配置文件,新增以下内容到任意位置: 123search: path: search.xml field: post 6.3 安装 hexo-generator-searchdb在站点的根目录下执行以下命令: 1$ npm install hexo-generator-searchdb --save 6.4 启用搜索编辑 站点配置文件,新增以下内容到任意位置: 12345search: path: search.xml field: post format: html limit: 10000 七、背景效果介绍博客背景动态效果图和点击小红心效果的相关设置。 7.1 添加 JS 文件把 js 文件 love.js 和 particle.js 放在\themes\next\source\js\src文件目录下。 7.2 引用添加的 JS 文件更新\themes\next\layout\_layout.swig文件,在末尾(在前面引用会出现找不到的bug)添加以下 js 引入代码: 1234<!-- 背景动画 --><script type="text/javascript" src="/js/src/particle.js"></script><!-- 页面点击小红心 --><script type="text/javascript" src="/js/src/love.js"></script> 八、图片模式8.1 新建博文新建博文,设置type: "picture",使用{\% gp x-x \%} ... {\% endgp \%}标签引用要展示的图片地址,如下所示: 12345678910111213141516---title: Naruto-Picturescategories: [图片]tags: [picture,naruto]date: 2016-09-02 14:36:04keywords: picture,narutotype: "picture"top: 999---{% gp 5-3 %}![](http://oapjp6spr.bkt.clouddn.com/18210.jpg)![](http://oapjp6spr.bkt.clouddn.com/196232.jpg)![](http://oapjp6spr.bkt.clouddn.com/224147.jpg)![](http://oapjp6spr.bkt.clouddn.com/199301.jpg)![](http://oapjp6spr.bkt.clouddn.com/213318.jpg){% endgp %} 8.2 图片展示效果{\% gp 5-3 \%}:设置图片展示效果,参考 theme/next/scripts/tags/group-pictures.js 注释示意图。 九、博文压缩目前知道的有两个插件可以压缩博文,hexo-all-minifier 插件和 gulp插件。hexo-all-minifier插件虽然使用比较简单,而且可以压缩图片,但是发现对文章缩进(输入法全拼模式下按Tab`)不支持,所以暂时使用第二种压缩手段。 9.1 hexo-all-minifier 配置使用安装 hexo-all-minifier,在站点的根目录下执行以下命令: 1$ npm install hexo-all-minifier --save hexo g 生产博文的时候就会自动压缩 HTML、JS、图片,详情参考插件介绍 9.2 gulp 插件配置使用hexo 依赖 gulp 插件安装,在站点的根目录下执行以下命令: 12$ npm install gulp -g$ npm install gulp-minify-css gulp-uglify gulp-htmlmin gulp-htmlclean gulp --save 在 package.json 同级目录下,新建 gulpfile.js 并填入以下内容: 12345678910111213141516171819202122232425262728293031323334var gulp = require('gulp');var minifycss = require('gulp-minify-css');var uglify = require('gulp-uglify');var htmlmin = require('gulp-htmlmin');var htmlclean = require('gulp-htmlclean');// 压缩 public 目录 cssgulp.task('minify-css', function() { return gulp.src('./public/**/*.css') .pipe(minifycss()) .pipe(gulp.dest('./public'));});// 压缩 public 目录 htmlgulp.task('minify-html', function() { return gulp.src('./public/**/*.html') .pipe(htmlclean()) .pipe(htmlmin({ removeComments: true, minifyJS: true, minifyCSS: true, minifyURLs: true, })) .pipe(gulp.dest('./public'))});// 压缩 public/js 目录 jsgulp.task('minify-js', function() { return gulp.src('./public/**/*.js') .pipe(uglify()) .pipe(gulp.dest('./public'));});// 执行 gulp 命令时执行的任务gulp.task('default', [ 'minify-html','minify-css','minify-js']); 生成博文是执行 hexo g && gulp 就会根据 gulpfile.js 中的配置,对 public 目录中的静态资源文件进行压缩。 十、博文置顶10.1 修改 hexo-generator-index 插件替换文件:node_modules/hexo-generator-index/lib/generator.js 为:generator.js 10.2 设置文章置顶在文章 Front-matter 中添加 top 值,数值越大文章越靠前,如: 123456789---title: Naruto 图集categories: [图片]tags: [picture,naruto]date: 2016-09-02 14:36:04keywords: picture,narutotype: "picture"top: 10--- 十一、头像圆形旋转介绍一下实现头像圆形,鼠标经过旋转或者一直让旋转效果,主要是修改 Hexo 目录下 \themes\next\source\css\_common\components\sidebar\sidebar-author.styl 文件。 11.1 头像圆形修改修改 sidebar-author.styl 文件中 .site-author-image CSS 样式如下: 12345678910111213.site-author-image { display: block; margin: 0 auto; padding: $site-author-image-padding; max-width: $site-author-image-width; height: $site-author-image-height; border: $site-author-image-border-width solid $site-author-image-border-color; /*头像圆形*/ border-radius: 80px; -webkit-border-radius: 80px; -moz-border-radius: 80px;} 11.2 鼠标经过旋转修改修改 sidebar-author.styl 文件,添加 CSS 样式 img:hover 如下代码: 123456img:hover { /* 鼠标经过头像旋转360度 */ -webkit-transform: rotateZ(360deg); -moz-transform: rotateZ(360deg); transform: rotateZ(360deg);} 修改 sidebar-author.styl 文件中 .site-author-image CSS 样式如下: 12345678910111213141516171819.site-author-image { display: block; margin: 0 auto; padding: $site-author-image-padding; max-width: $site-author-image-width; height: $site-author-image-height; border: $site-author-image-border-width solid $site-author-image-border-color; /* 头像圆形 */ border-radius: 80px; -webkit-border-radius: 80px; -moz-border-radius: 80px; box-shadow: inset 0 -1px 0 #333sf; /* 鼠标经过头像旋转360度 */ -webkit-transition: -webkit-transform 1.5s ease-out; -moz-transition: -moz-transform 1.5s ease-out; transition: transform 1.5s ease-out;} 11.3 头像循环旋转效果修改修改 sidebar-author.styl 文件,添加如下代码: 12345678910111213141516171819202122232425/* Z 轴旋转动画 */@-webkit-keyframes play { 0% { -webkit-transform: rotateZ(0deg); } 100% { -webkit-transform: rotateZ(360deg); }}@-moz-keyframes play { 0% { -moz-transform: rotateZ(0deg); } 100% { -moz-transform: rotateZ(360deg); }}@keyframes play { 0% { transform: rotateZ(0deg); } 100% { transform: rotateZ(360deg); }} 修改 sidebar-author.styl 文件中 .site-author-image CSS 样式如下: 12345678910111213141516171819202122232425.site-author-image { display: block; margin: 0 auto; padding: $site-author-image-padding; max-width: $site-author-image-width; height: $site-author-image-height; border: $site-author-image-border-width solid $site-author-image-border-color; /* 头像圆形 */ border-radius: 80px; -webkit-border-radius: 80px; -moz-border-radius: 80px; box-shadow: inset 0 -1px 0 #333sf; /* 设置循环动画:animation:动画名称 动画播放时长单位秒或微秒 动画播放的速度曲线linear为匀速 动画播放次数infinite为循环播放; */ -webkit-animation: play 3s linear infinite; -moz-animation: play 3s linear infinite; animation: play 3s linear infinite; /* 鼠标经过头像旋转360度 -webkit-transition: -webkit-transform 1.5s ease-out; -moz-transition: -moz-transform 1.5s ease-out; transition: transform 1.5s ease-out;*/} 鼠标经过停止头像旋转:修改 sidebar-author.styl 文件,添加 CSS 样式 img:hover 如下代码: 12345678910img:hover { /* 鼠标经过停止头像旋转 */ -webkit-animation-play-state:paused; animation-play-state:paused; /* 鼠标经过头像旋转360度 -webkit-transform: rotateZ(360deg); -moz-transform: rotateZ(360deg); transform: rotateZ(360deg);*/} 完整 sidebar-author.styl 文件参考地址。]]></content>
</entry>
<entry>
<title><![CDATA[[转]Java程序员情书]]></title>
<url>http://ehlxr.me/2016/08/22/%E8%BD%AC-Java%E7%A8%8B%E5%BA%8F%E5%91%98%E6%83%85%E4%B9%A6/</url>
<content type="text"><![CDATA[我能抽象出整个世界... 但是我却不能抽象出你... 你肯定是一个单例,因为你是那样的独一无二… 所以我的世界并不完整... 我可以重载甚至覆盖这个世界里的任何一种方法... 但是却不能覆盖对你的思念... 也许命中注定了 你与我存在于不同的包里… 在你的世界里,你被烙上了私有的属性… 我用尽全身力气,也找不到访问你的接口… 我不愿就此甘心,找到了藏身在神殿的巫师,教会了我穿越时空的方法… 终于,我用反射这把利剑,打开了你空间的缺口… 并发现了接近你的秘密… 当我迫不及待地调用了爱你这个方法... 并义无返顾的把自己作为参数传进这个方法时... 我才发现爱上你是一个没有终止条件的递归... 它不停的返回我对你的思念并压入我心里的堆栈... 在这无尽的黑夜中 ,终于体验到你对我爱的回调… 我的内存里已经再也装不下别人… 当我以为将与你在这个死循环中天荒地老时… 万恶的系统抛出了爱的异常… 此刻我才发现,我不过是操纵于虚拟机下的一个线程,你也是… 但我毫不后悔,因为在爱的洗礼之后… 我看见了一个新的生命,那是我们的, 继承]]></content>
</entry>
<entry>
<title><![CDATA[CentOS 系统下 GitLab 搭建与基本配置]]></title>
<url>http://ehlxr.me/2016/07/31/CentOS-%E7%B3%BB%E7%BB%9F%E4%B8%8B-GitLab-%E6%90%AD%E5%BB%BA%E4%B8%8E%E5%9F%BA%E6%9C%AC%E9%85%8D%E7%BD%AE/</url>
<content type="text"><![CDATA[GitLab 是一个开源的版本管理系统,提供了类似于 GitHub 的源代码浏览,管理缺陷和注释等功能,你可以将代码免费托管到 GitLab.com,而且不限项目数量和成员数。最吸引人的一点是,可以在自己的服务器上搭建 GitLab CE (社区免费版)版本,方便内部团队协作开发和代码管理。 下面介绍如何在 CentOS 服务器上搭建 GitLab CE 版本,以及一些基本的配置。 1. 安装GitLab 提供了两种安装方式:源码手动编译安装和软件包管理安装。 源码手动编译安装虽然配置灵活,但过程比较麻烦,不容易安装成功,所以我这里选择软件包管理安装的形式。 1.1 使用 GitLab 提供仓库在线安装12curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bashyum install gitlab-ce 国外的 GitLab 仓库访问速度较慢,可以使用国内的站点: 12curl -sS http://packages.gitlab.cc/install/gitlab-ce/script.rpm.sh | sudo bashyum install gitlab-ce 1.2 下载离线软件包安装如果网络速度不理想,可以使用离线软件包 rpm 的方式进行安装,下面提供了几个站点的下载地址。 GitLab 官方:https://packages.gitlab.com/gitlab/gitlab-ce?filter=rpms 清华大学TUNA开源镜像站:https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/ 浙大开源镜像站:http://mirrors.lifetoy.org/gitlab-ce/yum/el7/ 下载好 rpm 软件安装包后上传到服务器指定的目录下,通过以下命令进行安装: 1rpm -ivh gitlab-ce-8.9.6-ce.0.el7.x86_64.rpm 记录一下 rpm 卸载软件安装包命令: 1rpm -e --nodeps gitlab-ce-8.9.6-ce.0.el7.x86_64 2. 启动 GitLab安装完成之后,打开配置文件 /etc/gitlab/gitlab.rb 将 external_url = 'http://git.example.com' 修改为自己的 IP 地址:external_url 'http://ip_address' ,然后执行下面的命令,对 GitLab 进行编译: 1gitlab-ctl reconfigure 完成后,使用浏览器访问:http://ip_address 可进入 GitLab 登录页面,首次访问系统会让你重新设置管理员的密码,默认的管理员账号是 root,如果你想更改默认管理员账号,登录系统后可以修改帐号名。 3. GitLab 基本配置GitLab 的相关参数配置都存在 /etc/gitlab/gitlab.rb 文件里。自 GitLab 7.6 开始的新安装包, 已经默认将所有的参数写入到 /etc/gitlab/gitlab.rb 配置文件中。 3.1 配置端口GitLab 默认使用 80 端口对外提供服务,因为 80 端口被其他服务占用,所以需要更改。打开 /etc/gitlab/gitlab.rb 配置文件,修改 external_url 'http://ip_address' 为 external_url 'http://ip_address:new-port', 重新编译配置: 1gitlab-ctl reconfigure 这时候就可以通更改后的 IP + 端口号码进行访问了。 3.2 邮箱配置以下是 163 邮箱的配置参考,打开 /etc/gitlab/gitlab.rb 配置文件,添加以下内容: 12345678gitlab_rails['smtp_enable'] = truegitlab_rails['smtp_address'] = "smtp.163.com"gitlab_rails['smtp_port'] = 25gitlab_rails['smtp_user_name'] = "[email protected]"gitlab_rails['smtp_password'] = "password"gitlab_rails['smtp_authentication'] = "login"gitlab_rails['smtp_enable_starttls_auto'] = truegitlab_rails['gitlab_email_from'] = "[email protected]" 注意: [email protected] 和 password 更新为自己邮箱地址和密码;邮箱需要开启 SMTP 协议。 重新编译配置即可生效: 1gitlab-ctl reconfigure 其它邮箱的配置可参考:https://doc.gitlab.cc/omnibus/settings/smtp.html 3.3 头像配置GitLab 默认使用的是 Gravatar 头像服务,不过现在貌似 Gravatar 国内好像访问不了,导致 GitLab 默认头像破裂,无法显示,可以替换为多说 Gravatar 服务器。打开 /etc/gitlab/gitlab.rb 配置文件,增加下面这一行: 1gitlab_rails['gravatar_plain_url'] = 'http://gravatar.duoshuo.com/avatar/%{hash}?s=%{size}&d=identicon' 再分别执行以下命令即可 12gitlab-ctl reconfigure gitlab-rake cache:clear RAILS_ENV=production 也可以关闭 Gravatar 头像显示配置,登录 GitLab 管理员账户,进入设置界面(路径地址:http://ip:port/admin/application_settings ),取消以下选项即可。 3.4 用户注册配置管理员设置界面(路径地址:http://ip:port/admin/application_settings )以下选项可以控制用户注册配置,包括是否允许登录、注册和注册邮箱验证等选项。 3.5 常用命令GitLab 服务启动、停止、状态查询、修改配置生效等命令: 1gitlab-ctl start/stop/status/reconfigure # 服务启动、停止、状态查询、修改配置生效 也可以查看帮助文档获取更多命令信息: 1gitlab-ctl --help]]></content>
</entry>
<entry>
<title><![CDATA[CentOS 7 安装最新的 Git]]></title>
<url>http://ehlxr.me/2016/07/30/CentOS-7-%E5%AE%89%E8%A3%85%E6%9C%80%E6%96%B0%E7%9A%84-Git/</url>
<content type="text"><![CDATA[yum 源仓库里的 Git 版本更新不及时,最新版本的 Git 是 1.8.3.1,但是官方最新版本已经到了 2.9.2。想要安装最新版本的的 Git,只能下载源码进行安装。 1. 查看 yum 源仓库的 Git 信息:1# yum info git 可以看出,截至目前,yum 源仓库中最新的 Git 版本才 1.8.3.1,而查看最新的 Git 发布版本,已经 2.9.2 了。 2. 依赖库安装12# yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel# yum install gcc perl-ExtUtils-MakeMaker 3. 卸载低版本的 Git通过命令:git –-version 查看系统带的版本,Git 版本是: 1.8.3.1,所以先要卸载低版本的 Git,命令: 1# yum remove git 4. 下载新版的 Git 源码包1# wget https://github.com/git/git/archive/v2.9.2.tar.gz 也可以离线下载,然后传到 CentOS 系统中指定的目录下。 5. 解压到指定目录1# tar -xzvf v2.9.2.tar.gz -C ~/app/ 6. 安装 Git分别执行以下命令进行编译安装,编译过程可能比较漫长,请耐心等待完成。 123# cd git-2.9.2# make prefix=/usr/local/git all# make prefix=/usr/local/git install 7. 添加到环境变量12# echo "export PATH=$PATH:/usr/local/git/bin" >> /etc/bashrc# source /etc/bashrc # 实时生效 8. 查看版本号12# git --versiongit version 2.9.2 至此,CentOS 就安装上了最新版本的 Git。]]></content>
</entry>
<entry>
<title><![CDATA[CentOS 7 安装 Node.js]]></title>
<url>http://ehlxr.me/2016/07/30/CentOS-7-%E5%AE%89%E8%A3%85-Node-js/</url>
<content type="text"><![CDATA[Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. Node.js’ package ecosystem, npm, is the largest ecosystem of open source libraries in the world. 1. 下载源码安装文件1.1 在线安装通过以下命令下载源文件: 1wget https://nodejs.org/dist/v4.4.7/node-v4.4.7.tar.gz 1.2 离线安装如果网络不好,可以通过官方网站下载,然后上传到 CentOS 系统中,下载如图所示: 我这儿官方网站是无法正常访问的,需要一些手段(大家懂得),所以在网盘备份一份,地址:http://pan.baidu.com/s/1bpIAUAz 2. 编译安装2.1 解压文件到指定的目录1tar -xzvf node-v4.4.7.tar.gz -C app/ 2.2 安装依赖包1yum install gcc gcc-c++ 2.3 配置安装1./configure ./configure 是源代码安装的第一步,主要的作用是对即将安装的软件进行配置,检查当前的环境是否满足要安装软件的依赖关系,生成 makefile文件,以便你可以用 make 和 make install 来编译和安装程序。 2.4 编译安装编译命令: 1make 编译的过程会花很长一段时间,等编译完成再执行安装命令: 1make install 3. 检查安装运行以下命令: 1node --version 若输出对应的版本号,则安装成功。]]></content>
</entry>
<entry>
<title><![CDATA[[转]将 Centos 的 yum 源更改为国内的阿里云源]]></title>
<url>http://ehlxr.me/2016/07/30/%E8%BD%AC-%E5%B0%86-Centos-%E7%9A%84-yum-%E6%BA%90%E6%9B%B4%E6%94%B9%E4%B8%BA%E5%9B%BD%E5%86%85%E7%9A%84%E9%98%BF%E9%87%8C%E4%BA%91%E6%BA%90/</url>
<content type="text"><![CDATA[阿里云Linux安装镜像源地址:http://mirrors.aliyun.com/ CentOS系统更换软件安装源 一、备份你的原镜像文件,以免出错后可以恢复。1mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup 二、下载新的 CentOS-Base.repo 到 /etc/yum.repos.d/2.1 CentOS 51wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-5.repo 2.2 CentOS 61wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-6.repo 2.3 CentOS 71wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo 三、运行 yum makecache 生成缓存12yum clean allyum makecache]]></content>
</entry>
<entry>
<title><![CDATA[Oracle 与 MySQL 知识总结]]></title>
<url>http://ehlxr.me/2016/07/28/Oracle-%E4%B8%8E-MySQL-%E7%9F%A5%E8%AF%86%E6%80%BB%E7%BB%93/</url>
<content type="text"><![CDATA[对日常工作中使用 Oracle 和 MySQL 数据库知识对比总结。 SQLPlus 连接数据库的方式cmd 中输入:sqlplus 用户名/密码@数据库实例12sqlplus system/lxr316@oraclesqlplus sys/admin as sysdba 打开 SQLPlus 直接输入用户名和密码使用命令12connect sys/admin as sysdbaconnect system/lxr316 超级管理员登录1sys as sysdba 断开数据库1disconnect; # 可简写 disconn MySQL 连接命令: mysql [–h 服务器地址] –u 用户名 –p [密码](需要配置 mysql 数据库的 bin 到环境变量中) 12mysql -h localhost –u root –p rootmysql –u root -p 用户操作Oracle创建用户1create user [username] identified by [password]; 修改用户密码1alter user [username] identified by [password]; 账户上锁、解锁1alter user [username] account lock|unlock; 用户首次登录时直接修改密码–密码失效1alter user [username] password expire; MySQL创建用户:1CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 备注: username:你将创建的用户名。 host:指定该用户在哪个主机上可以登陆,如果是本地用户可用localhost, 如果想让该用户可以从任意远程主机登陆,可以使用通配符%。 password:该用户的登陆密码,密码可以为空,如果为空则该用户可以不需要密码登陆服务器。 例如: 12345CREATE USER 'dog'@'localhost'IDENTIFIED BY '123456';CREATE USER 'pig'@'192.168.1.101_'IDENDIFIED BY '123456';CREATE USER 'pig'@'%' IDENTIFIED BY '123456';CREATE USER 'pig'@'%' IDENTIFIED BY '';CREATE USER 'pig'@'%'; 设置与更改用户密码1SET PASSWORD FOR 'username'@'host' = PASSWORD('newpassword'); 如果是当前登陆用户 1SET PASSWORD = PASSWORD("newpassword"); 删除用户1DROP USER 'username'@'host'; 权限管理Oracle授权12grant 权限 to 用户名;grant all on 表名 to用户/角色 收回权限1revoke 权限from 用户名; 对象权限可以级联收回,但是系统权限不可以级联收回。 系统权限: A 赋予一个系统权限给 B,B 再授予 C。然后A将该权限从 B 回收,此后 C 仍然有该权限。 对象权限: A 赋予一个对象权限给 B,B 再授予 C。然后A将该权限从 B 回收,此后 C 也失去了该权限。 常用系统权限123create session --用户登录create table --创建表unlimited tablespace --无限表空间 常用对象权限1234select on 表名; -- 查询权限update on 表名; -- 更新权限delete on 表名; -- 删除权限insert on 表名; -- 插入权限 权限的级联授予1grant 权限 to 用户名 with admin option|with grant option; with admin option:系统权限 with grant option:对象权限 MySQL授权:1GRANT privileges ON databasename.tablename TO 'username'@'host' 备注: privileges:用户的操作权限,如 SELECT , INSERT , UPDATE 等;如果要授予所的权限则使用 ALL。 databasename:数据库名。 tablename:表名 如果要授予该用户对所有数据库和表的相应操作权限则可用 * 表示, 如 *.* 。例如: 123GRANT SELECT, INSERT ON test.user TO 'pig'@'%'; GRANT ALL ON *.* TO 'pig'@'%'; 注意: 用以上命令授权的用户不能给其它用户授权,如果想让该用户可以授权,用以下命令: 1GRANT privileges ON databasename.tablename TO 'username'@'host' WITH GRANT OPTION; 撤销用户权限1REVOKE privilege ON databasename.tablename FROM 'username'@'host'; 说明: privilege, databasename, tablename - 同授权部分。例如: 1REVOKE SELECT ON *.* FROM 'pig'@'%'; 注意: 假如你在给用户 'pig'@'%' 授权的时候是这样的(或类似的):GRANT SELECT ON test.user TO 'pig'@'%'; 则在使用:REVOKE SELECT ON *.* FROM'pig'@'%'; 命令并不能撤销该用户对 test 数据库中 user 表的 SELECT 操作;相反,如果授权使用的是:GRANT SELECT ON *.* TO'pig'@'%'; 则:REVOKE SELECT ONtest.user FROM 'pig'@'%'; 命令也不能撤销该用户对 test 数据库中 user 表的 SELECT 权限。 具体信息可以用命令:SHOW GRANTS FOR 'pig'@'%'; 查看。 Oracle角色 role角色:权限的集合 角色数据字典表 查看当前用户中的角色:user_role_privs 查看角色中的系统权限:role_sys_privs 查看角色中的对象权限:role_tab_privs 建一个角色1create role role1; 授权给角色1grant create any table, create procedure to role1; 授予/回收用户角色12grant role1 to user1;revoke role1 from user1; 创建带有口令以角色在生效带有口令的角色时必须提供口令 1create role role1 identified by password1; 修改角色:是否需要口令12alter role role1 not identified;alter role role1 identified by password1; 设置当前用户要生效的角色1234567set role role1; -- 使 role1 生效set role role1, role2; -- 使 role1, role2 生效set role role1 identified by password1; -- 使用带有口令的 role1 生效set role all; -- 使用该用户的所有角色生效set role none; -- 设置所有角色失效set role all except role1; -- 除 role1 外的该用户的所有其它角色生效select * from SESSION_ROLES; -- 查看当前用户的生效的角色。 修改指定用户,设置其默认角色12alter user user1 default role role1;alter user user1 default role all except role1; 删除角色1drop role role1; 注意: 角色删除后,原来拥用该角色的用户将失去该角色,相应的权限也将会失去。 系统角色 DBA: 拥有全部特权,是系统最高权限角色 RESOURCE:拥有 RESOURCE 角色权限的用户只可以创建实体 CONNECT:拥有 CONNECT 角色权限的用户只可以登录 Oracle 对于普通用户:授予 CONNECT,RESOURCE 角色权限;对于DBA管理用户:授予 CONNECT,RESOURCE, DBA 角色权限。 查看表结构信息命令Oracle1desc(ribe) 表名; -- 如:desc lxr MySQL1desc(ribe) 表名; 显示当前登录用户Oracle1show user; MySQL1select user(); MySQL 查看用户下的数据库1show databases; 查看表 1show tables; Oracle 用户锁定与解锁12ALTER USER username ACCOUNT LOCK; -- 锁定用户ALTER USER username ACCOUNT UNLOCK; -- 解锁用户 清屏命令SQLPlus123clear screen -- 简写:cl scrcls -- dos 清屏命令 MySQL12system clear; -- Linux 环境下system cls; -- dos 环境下 Oracle 更改当前用户日期格式命令1alter session set nls_date_format = 'yyyy-mm-dd'; Oracle 中的基本数据类型 数值:number(5,2),int 字符:char,varchar2(4000) 日期:date 图片:BLOB (binaryLargeObject) 4G 文本:CLOB (Character LargeObject) 4G Oracle 表结构操作表重命名1alter table 表名 rename to 新表名; 给表增加注释1comment on table 表名 is '注释内容'; 添加约束方式方式一1alter table 表名 add constraint 约束名 约束类型(约束的字段名); Constraint 约束类型: 值 名称 primary key 主键 unique 唯一 check 限制 not null 不能为null 方式二1alter table 表名 add constraint 约束名 foreign key(字段名) references 表名(字段名); foreign key 外键: 参照主键中存在的值,可以插入重复的记录、可以插入重复的空值 删除约束方式1alter table 表名 drop constraint 约束名; 删除表结构1drop table 表名; -- 此操作属DDL,会自动提交且不可回滚 表中增加字段1alter table 表名 add 字段名 类型; 删除字段1alter table 表名 drop 字段名; 通常在系统不忙的时候删除不使用的字段,可以先设置字段为 unused 1alter table test3 set unused column address; 再执行删除 1alter table test3 unused column; 字段重命名1alter table 表名 rename column 字段名 to 新字段名; 修改字段1alter table 表名 modify 字段名 新类型; 添加 not null 1alter table 表名 modify 字段名 not null 删除 not null 1alter table 表名 modify 字段名 null; Oracle 备份表在当前的数据库之内进行备份1create table 表名(字段) as select 查询语句; 数据的移动1insert into 表名(字段列表)select 字段列表 from 表名; 数据库服务器之间拷贝表客户端连接服务器1copy from system/hhl@hhl create hhl_table using select * from scott.emp; 从A服务器拷贝到自己的数据库中1copy from 用户名/密码@主机字符串 create 表名 using 查询语句; 从自己的数据库中拷贝到A服务器1copy to 用户用/密码@主机字符串 create 表名 using 查询语句; 从A服务器拷贝表到B服务器1copy from 用户用/密码@主机字符串 to 用户用/密码@主机字符串 create 表名 using 查询语句; OracleDBLINK 数据库连接在当前的数据库内直接操作其他服务器中的表做增删改查,格式如下: 1create database link 名 connect to 用户名 identified by 密码 using '主机字符串'; MySQL 查看数据库字符集12show variables like 'character%';show variables like '%collation%'; truncate 与 delete使用格式12truncate table 表名; -- 删除表中全部记录delete from 表名; truncate 与 delete 的区别 truncate 删除速度比 delete 删除速度快; truncate 不可以回滚,delete 可以回滚。]]></content>
</entry>
<entry>
<title><![CDATA[GitHub 更新已经 fork 的项目]]></title>
<url>http://ehlxr.me/2016/07/28/GitHub-%E6%9B%B4%E6%96%B0%E5%B7%B2%E7%BB%8F-fork-%E7%9A%84%E9%A1%B9%E7%9B%AE/</url>
<content type="text"><![CDATA[GitHub 上有个很方便的功能叫 fork,将别人的工程一键复制到自己账号下。这个功能很方便,但有点不足的是,当源项目更新后,你 fork 的分支并不会一起更新,需要自己手动去更新,下面记录下网上找到的更新的开发方法。 1. 在本地装好 GitHub 客户端,或者 Git 客户端2. clone 自己的 fork 分支到本地可以直接使用 GitHub 客户端,clone 到本地,如果使用命令行,命令为: 1$ git clone [email protected]:ehlxr/strman-java.git 3. 增加源分支地址到你项目远程分支列表中此处是关键,先得将原来的仓库指定为 upstream,命令为: 1$ git remote add upstream [email protected]:shekhargulati/strman-java.git 此处可使用 git remote -v 查看远程分支列表 12345$ git remote -vorigin [email protected]:ehlxr/strman-java.git (fetch)origin [email protected]:ehlxr/strman-java.git (push)upstream [email protected]:shekhargulati/strman-java.git (fetch)upstream [email protected]:shekhargulati/strman-java.git (push) 4. fetch 源分支的新版本到本地1$ git fetch upstream 5. 合并两个版本的代码1$ git merge upstream/master 6. 将合并后的代码 push 到 GitHub 上去1$ git push origin master 参考网址: https://help.github.com/articles/fork-a-repo 原文出处]]></content>
</entry>
<entry>
<title><![CDATA[Sublime Text 插件安装]]></title>
<url>http://ehlxr.me/2016/07/25/Sublime-Text-%E6%8F%92%E4%BB%B6%E5%AE%89%E8%A3%85/</url>
<content type="text"><![CDATA[Sublime Text 官方介绍: “Sublime Text is a sophisticated text editor for code, markup and prose.You’ll love the slick user interface, extraordinary features and amazing performance.” Sublime Text 是一款优秀的轻量级编辑器,而且支持跨平台,支持Windows、Linux、Mac OS X 等主流操作系统。虽然是一款收费软件,但也允许人们无限期免费使用。(官方下载地址) Sublime Text 具有漂亮的用户界面和强大可扩展插件功能,本文介绍如何安装 Sublime Text 插件。 打开 Sublime Text 编辑器的 Console(控制台)使用快捷键 ctrl+` 或者点击菜单 View > Show Console menu 打开 Console,如图: Sublime Text 2 粘贴以下代码: 1import urllib2,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); os.makedirs( ipp ) if not os.path.exists(ipp) else None; urllib2.install_opener( urllib2.build_opener( urllib2.ProxyHandler()) ); by = urllib2.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); open( os.path.join( ipp, pf), 'wb' ).write(by) if dh == h else None; print('Error validating download (got %s instead of %s), please try manual install' % (dh, h) if dh != h else 'Please restart Sublime Text to finish installation') Sublime Text 3 粘贴以下代码: 1import urllib.request,os,hashlib; h = '2915d1851351e5ee549c20394736b442' + '8bc59f460fa1548d1514676163dafc88'; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); by = urllib.request.urlopen( 'http://packagecontrol.io/' + pf.replace(' ', '%20')).read(); dh = hashlib.sha256(by).hexdigest(); print('Error validating download (got %s instead of %s), please try manual install' % (dh, h)) if dh != h else open(os.path.join( ipp, pf), 'wb' ).write(by) 敲回车等待安装完成后重启 Sublime Text 即可,按下快捷键 ctrl + shift + p ,输入 pci(Package Control Install 简写)如图: 敲回车即可打开插件安装界面,输入想要安装插件的名称即可开始安装,如图: Sublime Text 插件安装介绍完了,需要注意的是安装过程中要保持网络畅通。 参考网址]]></content>
</entry>
<entry>
<title><![CDATA[Git 同时 push 到多个远程仓库]]></title>
<url>http://ehlxr.me/2016/07/24/Git-%E5%90%8C%E6%97%B6-push-%E5%88%B0%E5%A4%9A%E4%B8%AA%E8%BF%9C%E7%A8%8B%E4%BB%93%E5%BA%93/</url>
<content type="text"><![CDATA[方法一如果一个本地仓库添加多个远程仓库,不想 git push 多次,可以修改 .git/config 文件 1vim .git/config 比如以下信息表示在 git@OSC 和 GitHub 两个远程托管 修改为以下信息 则可同时 push 到两个远程仓库 123$ git push origin masterEverything up-to-dateEverything up-to-date 方法二添加第二个远程地址时使用以下命令: 1git remote set-url --add origin [email protected]:ehlxr/ehlxr-Hexo.git 查看远程分支 origin: 1234$ git remote -vorigin [email protected]:ehlxr/ehlxr-Hexo.git (fetch)origin [email protected]:ehlxr/ehlxr-Hexo.git (push)origin [email protected]:ehlxr/ehlxr-Hexo.git (push) 也可以同时 push 到多个远程地址 123$ git push origin masterEverything up-to-dateEverything up-to-date]]></content>
</entry>
<entry>
<title><![CDATA[使用Hexo基于GitHub Pages搭建个人博客(二)]]></title>
<url>http://ehlxr.me/2016/07/23/%E4%BD%BF%E7%94%A8Hexo%E5%9F%BA%E4%BA%8EGitHub-Pages%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%EF%BC%88%E4%BA%8C%EF%BC%89/</url>
<content type="text"><![CDATA[上一篇介绍了 GitHub Pages 服务开启,这篇文章将介绍 Hexo 的安装和使用以及将博客部署到 GitHub Pages 的操作。 Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。 一、Node.js 安装Hexo 是基于 Node.js 安装,所以安装 Hexo 之前首先要安装 Node.js。下载地址:https://nodejs.org/en/download/ 根据自己的操作系统,下载对应版本即可。安装过程比较简单,一直下一步即可安装完成。 二、Hexo 安装打开 Git Bash 输入以下命令开始安装 Hexo 1npm install hexo-cli -g 出现以下界面表示安装完成 三、Hexo 建站安装 Hexo 完成后,在本地磁盘创建一个目录用于存放 Hexo 配置文件和博客源代码,我的 Hexo 存放在 D:/ehlxr 目录下。 分别执行下列命令,Hexo 将会在 D:/ehlxr/Hexo 新建所需要的文件 1234567891011# 进入 D:/ehlxr 目录cd /d/ehlxr/# 初始化所需要的文件到 Hexo 文件夹hexo init Hexo# 进入 Hexo 文件夹cd Hexo# 安装npm install 分别执行以下命令在本地进行预览 12345# 编译生成博文hexo generate# 启动Hexo服务器hexo server 出现以下画面表示服务启动成功 打开浏览器输入 http://localhost:4000/ 便可以看到最原始的博客了 四、Hexo 部署到 GitHub Pages更新 Hexo 安装文件夹中 _config.yml 文件,如图 更新 _config.yml 文件中的 deploy 节点,如图所示,可添加多个地址(如一个 GitHub、Coding、oschina) 分别执行以下命令即可完成部署 12345npm install hexo-deployer-git --save # 安装 hexo-deployer-githexo clean # 可简写为 hexo clhexo generate # 可简写为 hexo ghexo deploy # 可简写为 hexo d 访问 https://yourname.github.io 即可看到博客模板的内容,对于 Hexo 的一些详细设置可以参考Hexo官方文档 。本站使用的 Hexo 主题是 NexT,详细设置可参考Next主题官方网站。 使用以下命令就可以新建一篇博客文章 1hexo new "开始blog,哈哈" 打开 Hexo 目录下的 source\_posts 目录就可以看见创建的文章了,如图所示 文章是 MarkDown 格式文件(关于 Markdown 语法可参考Markdown 语法说明(简体中文版)),编辑文章后保存,可以先启动本地 Hexo Server 查看效果,然后执行部署命令就完成了博客文章的发布 Markdown 编辑器我推荐使用 小书匠 至此,使用 Hexo 基于 GitHub Pages 搭建个人博客就介绍完了。]]></content>
</entry>
<entry>
<title><![CDATA[使用Hexo基于GitHub Pages搭建个人博客(一)]]></title>
<url>http://ehlxr.me/2016/07/23/%E4%BD%BF%E7%94%A8Hexo%E5%9F%BA%E4%BA%8EGitHub-Pages%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2%EF%BC%88%E4%B8%80%EF%BC%89/</url>
<content type="text"><![CDATA[GitHub Pages 是 GitHub 提供的免费开源的静态站点托管服务,利用这个服务可以搭建轻量级的博客系统,本文介绍了如何使用 Hexo 结合 GitHub Pages 服务搭建个人博客。 一、安装配置 Git1.1 安装 Git使用 GitHub 首先要安装 Git,可根据操作系统下载不同版本的 Git,Windows 下载地址:https://git-scm.com/download/win 安装过程比较简单,选择默认选项配置即可下一步,不再叙述。 1.2 配置 Git设置本地机器默认 commit 的昵称与 Email,姓名与 Email 只用于日志标识,实际推送到远程仓库时,要用有操作权限的账号登录。 12git config --global user.name "ehlxr" git config --global user.email "[email protected]" 二、生成 SSH keysSSH Keys 是 GitHub 推荐的公钥、秘钥形式验证用户合法性的机制,添加 SSH Keys 可以省去每次都要输入密码的步骤,下面将演示如何在本地计算机生成 SSH Keys 的公钥、秘钥。 2.1 首先打开安装的 Git Bash 2.2 运行 ssh-keygen 命令输入以下命令:(替换 "[email protected]" 为注册时候的邮箱) 1ssh-keygen -t rsa -b 4096 -C "[email protected]" 2.3 保存秘钥文件当出现以下提示时,直接敲回车键选择默认地址即可(保存私钥文件位置,默认保存在当前用户文件夹下的 .ssh 文件夹中,文件名称为 id_rsa ) 1Enter file in which to save the key (/c/Users/lt/.ssh/id_rsa): 如果已经存在会提示,输入:y 敲回车替换即可 12/c/Users/lt/.ssh/id_rsa already exists.Overwrite (y/n)? 出现以下提示设置密码提示,不用设置密码,敲回车键即可 1Enter passphrase (empty for no passphrase): 再次确认密码提示,不用设密码敲回车即可 1Enter same passphrase again: 出现以下提示,表明已经成功生成了 SSH Keys 12345678910111213141516Your identification has been saved in /c/Users/lt/.ssh/id_rsa.Your public key has been saved in /c/Users/lt/.ssh/id_rsa.pub.The key fingerprint is:SHA256:dBtq6OOJs1JQat630kGqh420Y0JSb7smaR6c9jjo7h0 [email protected] key's randomart image is:+---[RSA 4096]----+| || . || o . o || = .o o o || + + o. S . ||.oo.*.o. ||oo=E +oo ||o.#+Oooo ||+OoO==o |+----[SHA256]-----+ Github 官网 SSH Keys 生成教程 三、开启 GitHub Pages 服务GitHub 账户注册比较简单,注册免费使用的账户就可以了,这里就不细说了,需要注意的是尽量不要使用 126/163 邮箱注册,我在测试的时候收不到验证码,等收到验证码的时候已经第二天了,早就过期了,我使用 Gmail 和 QQ 邮箱都能很快收到验证码。 3.1 添加生成的 SSH Keys 到 GitHub打开生成的 SSH Keys 公钥 登录注册的 GitHub 账户,按照以下图示 1、2、3、4、5、6、7 步骤操作,复制公钥内容粘贴到下图 步骤 5 的 Key 输入框中,在 步骤 6 输入一个标题,点击 步骤 7 的 Add SSH key 按钮,这时候会提示你输入 GitHub 账号密码,输入密码确认即可添加成功。 3.2 创建 GitHub 仓库按照下图所示操作,仓库名称按照:yourname.github.io 格式创建,这样就可以通过 yourname.github.io 方式访问你的博客。如果创建名称为:myblog ,则博客的访问路径为 yourname.github.io/myblog 填写完仓库名称,其他选项默认,点击 Create repository 即可完成创建。 3.3 测试 GitHub Pages 服务接下来在仓库根目录下创建一个 index.html 文件测试 GitHub Pages 服务是否开启成功。打开刚才创建的仓库,因为我们之前已经添加了 SSH Keys,所以选择复制如下图所示的 SSH 仓库连接 在电脑磁盘创建一个文件夹(我创建在:D:/lxr)在 Git Bash 分别输入以下两条命令: 123cd /d/lxr/ # 进入创建的文件夹git init # 初始化本地仓库 在本地文件夹中创建 index.html 文件,随便输入一些内容即可,命令如下: 1echo "<h1>Hello World</h1>" >> index.html # 创建 index.html 文件 然后分别执行以下命令: 1234git add index.html # 添加文件git commit -m "create index.html" # 提交文件git remote add origin [email protected]:ehlxr/ehlxr.github.io.git # 添加 GitHub 仓库地址,注意地址填写自己注册的 GitHub 仓库地址git push -u origin master # push 到 GitHub 浏览器访问:yourname.github.io 如果出现 index.html 输入的内容,即 GitHub Pages 服务开启成功]]></content>
</entry>
<entry>
<title><![CDATA[[转]JMS基本概念]]></title>
<url>http://ehlxr.me/2016/07/21/%E8%BD%AC-JMS%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/</url>
<content type="text"><![CDATA[JMS(JAVA Message Service,java消息服务)API是一个消息服务的标准或者说是规范,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。 基本概念JMS是java的消息服务,JMS的客户端之间可以通过JMS服务进行异步的消息传输。 消息模型 Point-to-Point(P2P) Publish/Subscribe(Pub/Sub) 即点对点和发布订阅模型 P2P模型P2P模式图 涉及到的概念 消息队列(Queue) 发送者(Sender) 接收者(Receiver) 每个消息都被发送到一个特定的队列,接收者从队列中获取消息。队列保留着消息,直到他们被消费或超时。 P2P模型的特点 每个消息只有一个消费者(Consumer)(即一旦被消费,消息就不再在消息队列中) 发送者和接收者之间在时间上没有依赖性,也就是说当发送者发送了消息之后,不管接收者有没有正在运行,它不会影响到消息被发送到队列 接收者在成功接收消息之后需向队列应答成功 如果你希望发送的每个消息都应该被成功处理的话,那么你需要P2P模式。 Pub/Sub模型Pub/Sub模式图 涉及到的概念 主题(Topic) 发布者(Publisher) 订阅者(Subscriber) 客户端将消息发送到主题。多个发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。 Pub/Sub模型的特点 每个消息可以有多个消费者 发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。 为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。 如果你希望发送的消息可以不被做任何处理、或者被一个消息者处理、或者可以被多个消费者处理的话,那么可以采用Pub/Sub模型。 消息的消费在JMS中,消息的产生和消息是异步的。对于消费来说,JMS的消息者可以通过两种方式来消费消息。 同步订阅者或接收者调用receive方法来接收消息,receive方法在能够接收到消息之前(或超时之前)将一直阻塞 异步订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法 JMS编程模型 ConnectionFactory 创建Connection对象的工厂,针对两种不同的jms消息模型,分别有QueueConnectionFactory和TopicConnectionFactory两种。可以通过JNDI来查找ConnectionFactory对象。 Destination Destination的意思是消息生产者的消息发送目标或者说消息消费者的消息来源。对于消息生产者来说,它的Destination是某个队列(Queue)或某个主题(Topic);对于消息消费者来说,它的Destination也是某个队列或主题(即消息来源)。 所以,Destination实际上就是两种类型的对象:Queue、Topic可以通过JNDI来查找Destination。 Connection Connection表示在客户端和JMS系统之间建立的链接(对TCP/IP socket的包装)。Connection可以产生一个或多个Session。跟ConnectionFactory一样,Connection也有两种类型:QueueConnection和TopicConnection。 Session Session是我们操作消息的接口。可以通过session创建生产者、消费者、消息等。Session提供了事务的功能。当我们需要使用session发送/接收多个消息时,可以将这些发送/接收动作放到一个事务中。同样,也分QueueSession和TopicSession。 消息的生产者 消息生产者由Session创建,并用于将消息发送到Destination。同样,消息生产者分两种类型:QueueSender和TopicPublisher。可以调用消息生产者的方法(send或publish方法)发送消息。 消息消费者 消息消费者由Session创建,用于接收被发送到Destination的消息。两种类型:QueueReceiver和TopicSubscriber。可分别通过session的createReceiver(Queue)或createSubscriber(Topic)来创建。当然,也可以session的creatDurableSubscriber方法来创建持久化的订阅者。 MessageListener 消息监听器。如果注册了消息监听器,一旦消息到达,将自动调用监听器的onMessage方法。EJB中的MDB(Message-Driven Bean)就是一种MessageListener。 企业消息系统的好处我们先来看看下图,应用程序A将Message发送到服务器上,然后应用程序B从服务器中接收A发来的消息,通过这个图我们一起来分析一下JMS的好处: 提供消息灵活性 松散耦合 异步性 原文地址]]></content>
</entry>
<entry>
<title><![CDATA[Java List与数组之间的转换]]></title>
<url>http://ehlxr.me/2016/07/20/Java-List%E4%B8%8E%E6%95%B0%E7%BB%84%E4%B9%8B%E9%97%B4%E7%9A%84%E8%BD%AC%E6%8D%A2/</url>
<content type="text"><![CDATA[1. 数组转换为List12String[] arr = new String[] {"str1", "str2"};List<String> list = Arrays.asList(arr); 2 .List转换为数组12345List<String> list = new ArrayList<String>();list.add("str1");list.add("str2");int size = list.size();String[] arr = list.toArray(new String[size]);]]></content>
</entry>
<entry>
<title><![CDATA[MySQL日期函数]]></title>
<url>http://ehlxr.me/2016/07/20/MySQL%E6%97%A5%E6%9C%9F%E5%87%BD%E6%95%B0/</url>
<content type="text"><![CDATA[1. 取得当天123456mysql> SELECT curdate();+------------+| curdate() |+------------+| 2013-07-29 |+------------+ 2. 取得当前日期123456789101112131415mysql> select sysdate();+---------------------+| sysdate() |+---------------------+| 2016-05-30 13:58:17 |+---------------------+1 row in setmysql> select now();+---------------------+| now() |+---------------------+| 2016-05-30 13:58:29 |+---------------------+1 row in set 3. 取得当前时间1234567mysql> select curtime();+-----------+| curtime() |+-----------+| 13:54:31 |+-----------+1 row in set 4. 取得前一天123456mysql> select date_sub(curdate(),interval 1 day);+------------------------------------+| date_sub(curdate(),interval 1 day) |+------------------------------------+| 2013-07-28 |+------------------------------------+ 5. 取得下一天1234567mysql> SELECT DATE_ADD(NOW(),INTERVAL 1 DAY);+--------------------------------+| DATE_ADD(NOW(),INTERVAL 1 DAY) |+--------------------------------+| 2016-05-31 14:09:16 |+--------------------------------+1 row in set 括号中为当天时间的前一天,如果统计前几天就将括号中的’1’改成相应的天数。如果要算月或年,直接将day改为month或year即可 6. 取得前一天的年份123456mysql> SELECT YEAR(DATE_SUB(CURDATE(),INTERVAL 1 DAY));+------------------------------------------+| YEAR(DATE_SUB(CURDATE(),INTERVAL 1 DAY)) |+------------------------------------------+| 2013 |+------------------------------------------+ 7. SYSDATE()为:2016-05-30 周一123456SELECT WEEK(SYSDATE()); -- 22SELECT WEEKOFYEAR(SYSDATE()); -- 22SELECT WEEKDAY(SYSDATE()); -- 0SELECT DAY(SYSDATE()); -- 30SELECT MONTH(SYSDATE()); -- 5SELECT YEAR(SYSDATE()); -- 2016 8. date_sub()函数的例子今天是2013年5月20日 12345678910date_sub('2012-05-25', INTERVAL 1 DAY) -- 表示 2012-05-24date_sub('2012-05-25', INTERVAL 0 DAY) -- 表示 2012-05-25date_sub('2012-05-25', INTERVAL - 1 DAY) -- 表示 2012-05-26date_sub('2012-05-31', INTERVAL - 1 DAY) -- 表示 2012-06-01date_sub(curdate(), INTERVAL 1 DAY) -- 表示 2013-05-19date_sub(curdate(), INTERVAL - 1 DAY) -- 表示 2013-05-21date_sub(curdate(), INTERVAL 1 MONTH) -- 表示 2013-04-20date_sub(curdate(), INTERVAL - 1 MONTH) -- 表示 2013-06-20date_sub(curdate(), INTERVAL 1 YEAR) -- 表示 2012-05-20date_sub(curdate(), INTERVAL - 1 YEAR) -- 表示 2014-05-20]]></content>
</entry>
<entry>
<title><![CDATA[开始blog,哈哈]]></title>
<url>http://ehlxr.me/2016/07/19/%E5%BC%80%E5%A7%8Bblog%EF%BC%8C%E5%93%88%E5%93%88/</url>
<content type="text"><![CDATA[123public static void main(String args[]){ System.out.println("hello world");}]]></content>
</entry>
<entry>
<title><![CDATA[Hello World]]></title>
<url>http://ehlxr.me/2016/07/19/hello-world/</url>
<content type="text"><![CDATA[Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new "My New Post" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment]]></content>
</entry>
</search>