-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
525 lines (373 loc) · 325 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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Elasticsearch:用Curator辅助Marvel,实现自动删除旧marvel索引]]></title>
<url>%2F2017%2FEsDeleteMarvel%2F</url>
<content type="text"><![CDATA[Marvel几乎是所有Elasticsearch用户的标配。Marvel保留观测数据的代价是,它默认每天会新建一个index,命名规律像是这样:.marvel-2017-12-10。marvel自建的索引一天可以产生大概500M的数据,而且将会越来越多,占的容量也将越来越大。有没有什么办法能让它自动过期?比如说只保留最近两天的观测数据,其他的都抛弃掉。 当然有办法,curator就可以帮你实现. curator是什么?它是一个命令,可以帮助你管理你在Elasticsearch中的索引,帮你删除,关闭(close),打开(open)它们。当然这是比较片面的说法,更完整的说明见:https://www.elastic.co/guide/en/elasticsearch/client/curator/current/index.html 实践我们集群里面安装的Elasticsearch的版本是2.1.1.按照官网,我装了最新的5.x版本,显示版本不对.按照 http://blog.csdn.net/hereiskxm/article/details/47423715 这个博客,我装了3.3.0版本.显示也不对. 然后我搜了一下,感觉应该装一个中间的版本,因此我安装了4.0.0版本1pip install elasticsearch-curator (4.0.0) 然后我看了一下这个版本提供的参数 123456789101112 curator --helpUsage: curator [OPTIONS] ACTION_FILE Curator for Elasticsearch indices. See http://elastic.co/guide/en/elasticsearch/client/curator/currentOptions: --config PATH Path to configuration file. Default: ~/.curator/curator.yml --dry-run Do not perform any changes. --version Show the version and exit. --help Show this message and exit. 和我安装最新的5.X的版本看起来是一致的.正好在这个站点看到配置的办法https://stackoverflow.com/questions/33430055/removing-old-indices-in-elasticsearch/42268400#42268400 之前在博客里面看到的那个3.3.0版本,还不兼容呢. 用法 目的是删除2天前以.marvel开头的索引 新建目录 /opt/curator 1234567~ pwd/opt/curator~ lltotal 12-rw-r--r-- 1 root root 184 Dec 12 10:48 config_file.yml-rw-r--r-- 1 root root 1311 Dec 12 10:37 delete_marvel_indices.ymldrwxr-xr-x 2 root root 4096 Dec 12 10:49 logs config_file.yml12345678910111213# 记住,这个logfile得提前新建好.不然会启动报错.vim config_file.yml---client: hosts: - 10.10.25.217 port: 9200logging: loglevel: INFO logfile: "/opt/curator/logs/actions.log" logformat: default blacklist: ['elasticsearch', 'urllib3'] delete_marvel_indices.yml删除以.marvel前缀且是2天之前的索引123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051---# Remember, leave a key empty if there is no value. None will be a string,# not a Python "NoneType"## Also remember that all examples have 'disable_action' set to True. If you# want to use this action as a template, be sure to set this to False after# copying it.actions: 1: action: delete_indices description: >- Delete indices older than 30 days (based on index name), for rc- prefixed indices. options: ignore_empty_list: True timeout_override: continue_if_exception: False disable_action: False filters: - filtertype: pattern kind: prefix value: rc- exclude: - filtertype: age source: name direction: older timestring: '%Y.%m.%d' unit: days unit_count: 30 exclude: 2: action: delete_indices description: >- Delete indices older than 2 days (based on index name), for .marvel prefixed indices. options: ignore_empty_list: True timeout_override: continue_if_exception: False disable_action: False filters: - filtertype: pattern kind: prefix value: .marvel exclude: - filtertype: age source: name direction: older timestring: '%Y.%m.%d' unit: days unit_count: 2 exclude: 配置完成. 执行命令1curator --config config_file.yml [--dry-run] delete_marvel_indices.yml 注意: –dry-run 是可选参数,加上后不会真的删除,只会执行逻辑.你可以通过看日志来判断是否正确.确认正确后,去掉–dry-run参数,再执行命令,既是真正的执行删除了. 如果没有在config_file.yml里面配置logfile参数,那么日志会在console打印出来. 配置日常任务很明显,我们需要自动化这个过程,让它每天自动执行,因此写一个脚本,让crontab每天自动调用即可 12345#!/bin/bashcurator --config /opt/curator/config_file.yml /opt/curator/delete_marvel_indices.ymlecho "delete success" 配置crontab 12# 每天2点执行删除脚本0 2 * * * source /etc/profile;bash /opt/curator/delete_marvel_daily.sh > /opt/curator/delete.log 2>&1]]></content>
</entry>
<entry>
<title><![CDATA[PythonVirtualenv总结]]></title>
<url>%2F2017%2FPythonVirtualenv%2F</url>
<content type="text"><![CDATA[我的电脑是macbookpro.我在电脑里面分别装了python2.7和python3.6. 当我用pip安装了virtual后,我如果想要对应版本的python virtualenv安装1pip install virtualenv virtualenv的参数12345678910111213141516171819202122232425262728293031323334353637383940hushiwei@hsw ~/virtual virtualenvYou must provide a DEST_DIRUsage: virtualenv [OPTIONS] DEST_DIROptions: --version show program's version number and exit -h, --help show this help message and exit -v, --verbose Increase verbosity. -q, --quiet Decrease verbosity. -p PYTHON_EXE, --python=PYTHON_EXE The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 interpreter to create the new environment. The default is the interpreter that virtualenv was installed with (/usr/local/opt/python3/bin/python3.6) --clear Clear out the non-root install and start from scratch. --no-site-packages DEPRECATED. Retained only for backward compatibility. Not having access to global site-packages is now the default behavior. --system-site-packages Give the virtual environment access to the global site-packages. --always-copy Always copy files rather than symlinking. --unzip-setuptools Unzip Setuptools when installing it. --relocatable Make an EXISTING virtualenv environment relocatable. This fixes up scripts and makes all .pth files relative. --no-setuptools Do not install setuptools in the new virtualenv. --no-pip Do not install pip in the new virtualenv. --no-wheel Do not install wheel in the new virtualenv. --extra-search-dir=DIR Directory to look for setuptools/pip distributions in. This option can be used multiple times. --download Download preinstalled packages from PyPI. --no-download, --never-download Do not download preinstalled packages from PyPI. --prompt=PROMPT Provides an alternative prompt prefix for this environment. --distribute DEPRECATED. Retained only for backward compatibility. This option has no effect. 可以看到里面的–python参数可以指定虚拟环境的python版本.并且说明了默认是python3.6 创建虚拟环境virtual安装Python2.71virtualenv --python=python python2env virtual安装Python3.6环境1virtualenv --python=python3 python3env 激活进入虚拟环境12source python2env/bin/activatesource python3env/bin/activate 退出虚拟环境1deactivate 删除虚拟环境12# 直接用rm删除目录就是删除虚拟环境了rm -rf 虚拟环境的目录名称 激活虚拟环境,将全部依赖写入文件1pip freeze > requirements.txt 进入项目内,安装全部依赖 1pip install -r requirements.txt virtualenvwrapper virtualenvwrapper是virtualenv的扩展管理包,用于更方便管理虚拟环境. 他可以做; 将所有虚拟环境整合在一个目录下 管理(新增,删除,复制)虚拟环境 切换虚拟环境 安装1pip install virtualenvwrapper 使用方法1.初始化配置 默认virtualenvwrapper安装在/usr/local/bin下面,实际上需要运行virtualenvwrapper.sh文件才行; 所以需要先进行配置一下: 1.1 创建虚拟环境管理目录: 1mkdir $HOME/.local/virtualenvs 1.2 在~/.bash_profile中添加行 12345678910export VIRTUALENV_USE_DISTRIBUTE=1 # 总是使用 pip/distributeexport WORKON_HOME=$HOME/.local/virtualenvs # 所有虚拟环境存储的目录if [ -e $HOME/.local/bin/virtualenvwrapper.sh ];then source $HOME/.local/bin/virtualenvwrapper.shelse if [ -e /usr/local/bin/virtualenvwrapper.sh ];then source /usr/local/bin/virtualenvwrapper.shfi fiexport PIP_VIRTUALENV_BASE=$WORKON_HOMEexport PIP_RESPECT_VIRTUALENV=true 2.使用方法 所有的命令可使用:virtualenvwrapper --help 进行查看,这里列出几个常用的: 创建基本环境:mkvirtualenv [环境名] 删除环境:rmvirtualenv [环境名] 激活环境:workon [环境名] 退出环境:deactivate 列出所有环境:workon 或者 lsvirtualenv -b 在使用mkvirtualenv命令的时候,-p选项可以指定使用哪一个python环境 3.举例 安装python2.7 1mkvirtualenv -p python python2env 安装python3.6 1mkvirtualenv -p python3 python3env 查看现在装了几个虚拟环境 123hushiwei@hsw ~ workonpython2envpython3env 所有命令都可在后面使用--help参数查看具体用法!Enjoy it !]]></content>
</entry>
<entry>
<title><![CDATA[机器学习实战之朴素贝叶斯]]></title>
<url>%2F2017%2FNaiveBayes%2F</url>
<content type="text"><![CDATA[朴素贝叶斯就是利用先验知识来解决后验概率,因为训练集中我们已经知道了每个单词在类别0和1中的概率,即p(w|c),我们就是要利用这个知识去解决在出现这些单词的组合情况下,类别更可能是0还是1,即p(c|w)。如果说之前的训练样本少,那么这个p(w|c)就更可能不准确,所以样本越多我们会觉得这个p(w|c)越可信。 1234import osimport sysfrom numpy import *sys.path.append(os.getcwd()) 1import bayes 12# 返回实验样本和类别标签(侮辱类和非侮辱类)listOPosts,listClasses=bayes.loadDataSet() 1234567# 创建词汇表# 将实验样本里面的词汇进行去重def createVocabList(dataSet): vocabSet=set([]) for document in dataSet: vocabSet=vocabSet|set(document) return list(vocabSet) 1myVocabList=createVocabList(listOPosts) 12345678910# 将词汇转成特征向量# 也就是将每一行样本转成特征向量# 向量的每一元素为1或者0,分别表示词汇表中的单词在输入文档中是否出现def setOfWordsVec(vocabList,inputSet): returnVec=[0]*len(vocabList) for word in inputSet: if word in vocabList: returnVec[vocabList.index(word)]=1 else:print "the word:%s is not in my Vocabulary!" % word return returnVec 训练算法:从词向量计算概率朴素贝叶斯分类器训练函数 12345678910111213141516171819202122232425262728# 输入为文档矩阵以及由每篇文档类别标签所构成的向量def trainNB0(trainMatrix,trainCategory): # 文档总数,有几篇文档,在这里也就是有几个一维数组 numTrainDocs=len(trainMatrix) # 每篇文档里面的单词数,也就是一维数组的长度 numWords=len(trainMatrix[0]) # 因为就只有0和1两个分类,将类别列表求和后,就是其中一个类别的个数 # 然后numTrainDocs也就是文档总数,这样相除后就是这个类别的概率了 pAbusive=sum(trainCategory)/float(numTrainDocs) # 以下两行,初始化概率 p0Num=ones(numWords);p1Num=ones(numWords) p0Denom=2.0;p1Denom=2.0 # 依次遍历所有的文档 for i in range(numTrainDocs): # 判断这个文档所属类别 if trainCategory[i]==1: # 数组与数组相加,这里就是统计每个词在这个分类里面出现的次数 p1Num+=trainMatrix[i] # 统计该类别下,这些词语一共出现了多少次 p1Denom+=sum(trainMatrix[i]) else: p0Num+=trainMatrix[i] p0Denom+=sum(trainMatrix[i]) # 通过求对数避免数据下溢出 p1Vect=log(p1Num/p1Denom) p0Vect=log(p0Num/p0Denom) return p0Vect,p1Vect,pAbusive 12# 所有文档的特征向量trainMat=[] 123# 将文档的每一行,转成词向量,然后追加到trainMat中for postinDoc in listOPosts: trainMat.append(bayes.setOfWords2Vec(myVocabList,postinDoc)) 1p0V,p1V,pAb=trainNB0(trainMat,listClasses) 1pAb 0.5 1p0V array([-2.56494936, -2.56494936, -2.56494936, -3.25809654, -3.25809654, -2.56494936, -2.56494936, -2.56494936, -3.25809654, -2.56494936, -2.56494936, -2.56494936, -2.56494936, -3.25809654, -3.25809654, -2.15948425, -3.25809654, -3.25809654, -2.56494936, -3.25809654, -2.56494936, -2.56494936, -3.25809654, -2.56494936, -2.56494936, -2.56494936, -3.25809654, -2.56494936, -3.25809654, -2.56494936, -2.56494936, -1.87180218]) 1p1V array([-3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526, -3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526, -3.04452244, -3.04452244, -3.04452244, -2.35137526, -2.35137526, -2.35137526, -2.35137526, -2.35137526, -3.04452244, -1.94591015, -3.04452244, -2.35137526, -2.35137526, -3.04452244, -1.94591015, -3.04452244, -1.65822808, -3.04452244, -2.35137526, -3.04452244, -3.04452244, -3.04452244]) 123456789# 朴素贝叶斯分类函数def classifyNB(vec2classify,p0Vec,p1Vec,pClass1): #元素相乘 p1=sum(vec2classify*p1Vec)+log(pClass1) p0=sum(vec2classify*p0Vec)+log(pClass1) if p1>p0: return 1 else: return 0 12345678910111213141516def testingNB(): listOPosts,listClasses=bayes.loadDataSet() myVocabList=createVocabList(listOPosts) trainMat=[] for postinDoc in listOPosts: trainMat.append(setOfWordsVec(myVocabList,postinDoc)) p0V,p1V,pAb=trainNB0(array(trainMat),array(listClasses)) testEntry=['love','my','dalmation'] thisDoc=array(setOfWordsVec(myVocabList,testEntry)) print testEntry,'classified as : ',classifyNB(thisDoc,p0V,p1V,pAb) testEntry=['stupid','garbage'] thisDoc=array(setOfWordsVec(myVocabList,testEntry)) print testEntry,'classified as : ',classifyNB(thisDoc,p0V,p1V,pAb) 1testingNB() ['love', 'my', 'dalmation'] classified as : 0 ['stupid', 'garbage'] classified as : 1 使用朴素贝叶斯过滤垃圾邮件 收集数据:提供文本文件 准备数据:将文本文件解析成词条向量 分析数据:检查词条确保解析的正确性 训练算法:使用我们之前建立的trainNB0()函数 测试算法:使用classifyNB() 1mySent='This book is the best book on Python or M.L. I have ever laid eyes upon.' 12345# 文件解析及完整的垃圾邮件测试函数def textParse(bigString): import re listOfTokens=re.split(r'\W*',bigString) return [tok.lower() for tok in listOfTokens if len(tok)>2] 1234567891011121314151617181920212223242526272829303132333435363738def spamTest(): docList=[];classList=[];fullText=[] for i in range(1,26): # 导入邮件文本,并解析成词条 wordList=textParse(open('email/spam/%d.txt' %i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(1) wordList=textParse(open('email/ham/%d.txt' %i).read()) docList.append(wordList) fullText.extend(wordList) classList.append(0) # 生成词汇表 vocabList=createVocabList(docList) # 随机构建训练集、测试集 trainingSet=range(50);testSet=[] for i in range(10): randIndex=int(random.uniform(0,len(trainingSet))) testSet.append(trainingSet[randIndex]) del(trainingSet[randIndex]) # 生成测试集的特征向量 trainMat=[];trainClasses=[] for docIndex in trainingSet: trainMat.append(setOfWordsVec(vocabList,docList[docIndex])) trainClasses.append(classList[docIndex]) p0V,p1V,pSpam=trainNB0(trainMat,trainClasses) # 测试集,测试错误率 errorCount=0 for docIndex in testSet: wordVector=setOfWordsVec(vocabList,docList[docIndex]) if classifyNB(wordVector,p0V,p1V,pSpam)!=classList[docIndex]: errorCount+=1 print 'the error rate is : ',float(errorCount)/len(testSet) 1spamTest() the error rate is : 0.2 使用朴素贝叶斯分类器从个人广告中获取区域倾向 收集数据:从RSS源收集内容,这里需要对RSS源构建一个接口 准备数据:将文本文件解析成词条向量 分析数据:检查词条确保解析的正确性 训练算法:使用我们之前建立的trainNB0()函数 测试算法:观察错误率,确保分类器可用.可以修改切分程序,以降低错误率,提高分类结果. 使用算法:构建一个完整的程序,封装所有内容.给定两个RSS源,该程序会显示最常用的公共词. 下面将使用来自不同城市的广告训练一个分类器,然后观察分类器的效果。我们的目的并不是使用该分类器进行分类,而是通过观察单词和条件概率值来发现与特定城市相关的内容。]]></content>
</entry>
<entry>
<title><![CDATA[【Scikit-Learn 中文文档 】安装 scikit-learn | ApacheCN]]></title>
<url>%2F2017%2FScikit-Learn-Chinese%2F</url>
<content type="text"><![CDATA[1.安装 sciki-learn 中文文档: http://blog.csdn.net/u012185296/article/details/78582711 2.使用 scikit-learn 介绍机器学习 : http://blog.csdn.net/u012185296/article/details/78583115 3.广义线性模型: http://blog.csdn.net/u012185296/article/details/78583436 4.线性和二次判别分析: http://blog.csdn.net/u012185296/article/details/78584918 5.内核岭回归: http://blog.csdn.net/u012185296/article/details/78584989]]></content>
</entry>
<entry>
<title><![CDATA[Presto的学习笔记]]></title>
<url>%2F2017%2FPrestoLearnning%2F</url>
<content type="text"><![CDATA[presto学习笔记 是什么?可以做什么? Presto是一个开源的分布式SQL查询引擎,适用于交互式分析查询,数据量支持GB到PB字节。 Presto支持在线数据查询,包括Hive, Cassandra, 关系数据库以及专有数据存储。 一条Presto查询可以将多个数据源的数据进行合并,可以跨越整个组织进行分析。 作为Hive和Pig(Hive和Pig都是通过MapReduce的管道流来完成HDFS数据的查询)的替代者,Presto不仅可以访问HDFS,也可以操作不同的数据源,包括:RDBMS和其他的数据源(例如:Cassandra)。 查询后的数据自动分页,这个很不错. 源码编译下载presto源码包地址:https://github.com/prestodb/presto/releases 安装文档地址(注意这个中文文档的版本是0.100): 注意: jdk得是1.8以上 我是用的presto0.161 1234tar -xzvf presto-0.161.tar.gz# 编译./mvnw clean install -DskipTests 在pom.xml文件中加入阿里云的仓库,加速下载依赖 1234567<repositories> <repository> <id>nexus-aliyun</id> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </repository></repositories> 编译报错 12345678910111213141516171819[ERROR] Failed to execute goal pl.project13.maven:git-commit-id-plugin:2.1.13:revision (default) on project presto-spi: .git directory could not be found! Please specify a valid [dotGitDirectory] in your pom.xml -> [Help 1]org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal pl.project13.maven:git-commit-id-plugin:2.1.13:revision (default) on project presto-spi: .git directory could not be found! Please specify a valid [dotGitDirectory] in your pom.xml at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:212) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145) at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116) at org.apache.maven.lifecycle.internal.builder.multithreaded.MultiThreadedBuilder$1.call(MultiThreadedBuilder.java:185) at org.apache.maven.lifecycle.internal.builder.multithreaded.MultiThreadedBuilder$1.call(MultiThreadedBuilder.java:181) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745)Caused by: org.apache.maven.plugin.MojoExecutionException: .git directory could not be found! Please specify a valid [dotGitDirectory] in your pom.xml at pl.project13.maven.git.GitCommitIdMojo.throwWhenRequiredDirectoryNotFound(GitCommitIdMojo.java:432) at pl.project13.maven.git.GitCommitIdMojo.execute(GitCommitIdMojo.java:337) at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:134) at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:207) 解决办法 123456789101112pom文件中加入这个插件<pluginManagement> <plugins> <plugin> <groupId>pl.project13.maven</groupId> <artifactId>git-commit-id-plugin</artifactId> <configuration> <skip>true</skip> </configuration> </plugin> </plugins> </pluginManagement> 部署包安装下载地址下载presto-cli(记住要下载的presto-cli-xxxx-executable.jar):https://repo1.maven.org/maven2/com/facebook/presto/presto-cli 下载部署包的地址:https://repo1.maven.org/maven2/com/facebook/presto/presto-server/ 服务器说明机器共有三台U006,U007,U008 coordinator U007 discovery U007 worker U006 U008 coordinator和worker的其他配置都是一样的,除了config.properties不一样.具体哪里不一样,看下面的配置说明. 配置说明以下配置均是presto0.161版本的.如果你的版本不一样,启动后如果报错了,那么可能是配置文件里面的参数和版本对应不上,请找相应版本的配置. 在presto-server-0.161目录下新建etc目录,下面的配置文件均在此etc目录下 123456789[druid@U007 presto-server-0.161]$ tree etc/etc/├── catalog│ ├── hive.properties│ └── jmx.properties├── config.properties├── jvm.config├── log.properties└── node.properties jvm.config 包含一系列在启动JVM的时候需要使用的命令行选项。这份配置文件的格式是:一系列的选项,每行配置一个单独的选项。由于这些选项不在shell命令中使用。 因此即使将每个选项通过空格或者其他的分隔符分开,java程序也不会将这些选项分开,而是作为一个命令行选项处理,信息如下: VM 系统属性 HADOOP_USER_NAME 来指定用户名 123456789-server-Xmx16G-XX:+UseG1GC-XX:G1HeapRegionSize=32M-XX:+UseGCOverheadLimit-XX:+ExplicitGCInvokesConcurrent-XX:+HeapDumpOnOutOfMemoryError-XX:OnOutOfMemoryError=kill -9 %p-DHADOOP_USER_NAME=hdfs log.properties 这个配置文件中允许你根据不同的日志结构设置不同的日志级别。每个logger都有一个名字(通常是使用logger的类的全标示类名). Loggers通过名字中的“.“来表示层级和集成关系,信息如下: 1com.facebook.presto=INFO 配置日志等级,类似于log4j。四个等级:DEBUG,INFO,WARN,ERROR node.properties 包含针对于每个节点的特定的配置信息。 一个节点就是在一台机器上安装的Presto实例,etc/node.properties配置文件至少包含如下配置信息 123node.environment=productionnode.id=ffffffff-ffff-ffff-ffff-ffffffffffff # 每个节点的node.id一定要不一样node.data-dir=/home/druid/data/presto # 计算临时存储目录,presto得有读写权限 说明: node.environment: 集群名称, 所有在同一个集群中的Presto节点必须拥有相同的集群名称. node.id: 每个Presto节点的唯一标示。每个节点的node.id都必须是唯一的。在Presto进行重启或者升级过程中每个节点的node.id必须保持不变。如果在一个节点上安装多个Presto实例(例如:在同一台机器上安装多个Presto节点),那么每个Presto节点必须拥有唯一的node.id. node.data-dir: 数据存储目录的位置(操作系统上的路径), Presto将会把日期和数据存储在这个目录下 config.propertiescoordinator 主节点配置123456coordinator=truenode-scheduler.include-coordinator=falsehttp-server.http.port=8585query.max-memory=10GBdiscovery-server.enabled=truediscovery.uri=http://U007:8585 说明: coordinator表示此节点是否作为一个coordinator。每个节点可以是一个worker,也可以同时是一个coordinator,但作为性能考虑,一般大型机群最好将两者分开。 若coordinator设置成true,则此节点成为一个coordinator。 若node-scheduler.include-coordinator设置成true,则成为一个worker,两者可以同时设置成true,此节点拥有两种身份。在一个节点上的Presto server即作为coordinator又作为worke将会降低查询性能。因为如果一个服务器作为worker使用,那么大部分的资源都会被worker占用,那么就不会有足够的资源进行关键任务调度、管理和监控查询执行. http-server.http.port:指定HTTP server的端口。Presto 使用 HTTP进行内部和外部的所有通讯. query.max-memory=10GB:一个单独的任务使用的最大内存 (一个查询计划的某个执行部分会在一个特定的节点上执行)。 这个配置参数限制的GROUP BY语句中的Group的数目、JOIN关联中的右关联表的大小、ORDER BY语句中的行数和一个窗口函数中处理的行数。 该参数应该根据并发查询的数量和查询的复杂度进行调整。如果该参数设置的太低,很多查询将不能执行;但是如果设置的太高将会导致JVM把内存耗光. discovery-server.enabled:Presto 通过Discovery 服务来找到集群中所有的节点。为了能够找到集群中所有的节点,每一个Presto实例都会在启动的时候将自己注册到discovery服务。Presto为了简化部署,并且也不想再增加一个新的服务进程,Presto coordinator 可以运行一个内嵌在coordinator 里面的Discovery 服务。这个内嵌的Discovery 服务和Presto共享HTTP server并且使用同样的端口. discovery.uri:Discovery server的URI。由于启用了Presto coordinator内嵌的Discovery 服务,因此这个uri就是Presto coordinator的uri。注意:这个URI一定不能以“/“结尾 worker节点配置123456coordinator=falsenode-scheduler.include-coordinator=truehttp-server.http.port=8585query.max-memory=5GBquery.max-memory-per-node=1GBdiscovery.uri=http://U007:8585 cataloghive.properties(hive连接器的配置) 连接hive 12345678# 在etc/catalog目录下,新建hive.properties文件,配置上hive的一些信息[druid@U006 catalog]$ pwd/home/druid/presto-server-0.161/etc/catalog[druid@U006 catalog]$ more hive.propertiesconnector.name=hive-cdh5hive.metastore.uri=thrift://U006:9083hive.config.resources=/etc/hadoop/conf.cloudera.yarn/core-site.xml,/etc/hadoop/conf.cloudera.yarn/hdfs-site.xml 保证每个节点presto对core-site.xml,hdfs-site.xml两个文件有读权限 启动停止presto单节点启动在每个节点依次执行启动脚本 12345678910# 后台运行bin/launcher start# 前台运行bin/launcher run# 重启prestobin/launcher restart# 停止prestobin/launcher stop 批量启动停止脚本1ssh -t ${i} -C '. /usr/local/bin/env.sh && /usr/local/presto-server-0.161/bin/launcher restart' 监控presto启动完成后,在浏览器输入: 1http://U007:8585 这个地址也就是coordinator的discovery.uri cli连接连接器注意说明 cli下载地址(找到自己的版本下载):https://repo1.maven.org/maven2/com/facebook/presto/presto-cli 连接器的配置文件必须是以.properties后缀结尾的,前面的名字就是连接器的catalog名字 每次新加连接器配置文件后,都需要在presto的所有机器上加上相同的配置文件,然后重启 要下载对应版本的cli连接器,不然可能不好使.名字类似presto-cli-0.161-executable.jar hive连接器配置说明(hive连接器的配置在说catalog的时候已经配置好了,你可以回头看看): connector.name=hive-cdh5(根据你的hive版本来选择) hive.metastore.uri=thrift://U006:9083(hive的metastore地址) hive.config.resources=/etc/hadoop/conf.cloudera.yarn/core-(配置文件的地址) 12345chmod +x presto-cli-0.161-executable.jar./presto-cli-0.161-executable.jar --server U007:8585 --catalog hive --schema default或者 mv presto-cli-0.161-executable.jar presto都可以./presto --server U007:8585 --catalog hive --schema default执行该语句后在 presto shell 中执行: show tables 查看 hive 中的 default 库下的表。如果出现对应的表,表安装验证成功 jmx连接器 JMX提供了有关JVM中运行的Java虚拟机和软件的信息 jmx连接器用于在presto服务器中查询JMX信息 在etc/catalog目录下新建jmx.properties 1connector.name=jmx 现在连接presto cli以启用JMX插件 123456789101112[druid@U007 presto-server-0.161]$ ./presto --server U007:8585 --catalog jmx --schema jmxpresto:jmx> show schemas from jmx; Schema-------------------- current history information_schema(3 rows)Query 20171012_063601_00020_yuhat, FINISHED, 2 nodesSplits: 2 total, 2 done (100.00%)0:00 [3 rows, 47B] [39 rows/s, 614B/s] MySQL连接器vim etc/catalog/mysql.properties 1234connector.name=mysqlconnection-url=jdbc:mysql://10.10.25.13:3306connection-user=rootconnection-password=wankatest*** schema 后面跟的mysql的数据库, 123456789101112131415161718[druid@U007 presto-server-0.161]$ ./presto --server U007:8585 --catalog mysql --schema testpresto:test> show tables;Query 20171012_071844_00002_iz4q8 failed: No worker nodes availablepresto:test> show tables; Table-------------------------- dmp_summary_daily_report tb_dmp_stat_appboot tb_dmp_stat_asdk_detail tb_dmp_stat_device tb_leidian1 tb_leidian2(6 rows)Query 20171012_072024_00003_iz4q8, FINISHED, 2 nodesSplits: 2 total, 2 done (100.00%)0:01 [6 rows, 190B] [6 rows/s, 196B/s] kafka连接器暂时没这个需求,未测试. 系统连接器 系统连接器提供了正在运行的Presto集群的一些信息和指标 那么这个就可以通过标准sql很方便的查询这些信息 系统连接器不需要配置,已经内置了.我们可以很方便的访问名为system的catalog 12345678910111213[druid@U007 presto-server-0.161]$ ./presto --server U007:8585 --catalog systempresto> show schemas from system; Schema-------------------- information_schema jdbc metadata runtime(4 rows)Query 20171012_082437_00019_iz4q8, FINISHED, 2 nodesSplits: 2 total, 2 done (100.00%)0:00 [4 rows, 57B] [70 rows/s, 997B/s] 查询有多少个节点 1234567891011presto> SELECT * FROM system.runtime.nodes; node_id | http_uri | node_version | coord-------------------------------------------+-------------------------+--------------+------ ffffffff-ffff-ffff-ffff-ffffffffffff-u006 | http://10.10.25.13:8585 | 0.161 | false ffffffff-ffff-ffff-ffff-ffffffffffff-u007 | http://10.10.25.14:8585 | 0.161 | true ffffffff-ffff-ffff-ffff-ffffffffffff-u008 | http://10.10.25.15:8585 | 0.161 | false(3 rows)Query 20171012_082952_00023_iz4q8, FINISHED, 2 nodesSplits: 2 total, 2 done (100.00%)4:08 [3 rows, 228B] [0 rows/s, 0B/s] JDBC接口依赖下载安装 下载地址(找到自己的版本下载):https://repo1.maven.org/maven2/com/facebook/presto/presto-jdbc presto-jdbc-0.161.jar在jar文件下载之后,将其添加到Java应用程序的classpath中。 我不太喜欢用jar包的方式,那么可以在pom文件加入presto-jdbc的依赖 http://mvnrepository.com/artifact/com.facebook.presto/presto-jdbc 找到自己相应的版本 123456<!-- https://mvnrepository.com/artifact/com.facebook.presto/presto-jdbc --><dependency> <groupId>com.facebook.presto</groupId> <artifactId>presto-jdbc</artifactId> <version>0.161</version></dependency> Presto支持的URL格式如下: 123jdbc:presto://host:portjdbc:presto://host:port/catalogjdbc:presto://host:port/catalog/schema 例如,可以使用下面的URL来连接运行在U007服务器8585端口上的Presto的mysql catalog中的test schema: 1jdbc:presto://10.10.25.14:8585/mysql/test 这个url就是来连接hive catalog中的default schema 1jdbc:presto://10.10.25.14:8585/hive/default ###Java代码 读取mysql下的test库下的所有表 12345678910111213141516public class PrestoJdbcDemo { public static void main(String[] args) throws SQLException, ClassNotFoundException { Class.forName("com.facebook.presto.jdbc.PrestoDriver"); Connection connection = DriverManager .getConnection("jdbc:presto://10.10.25.14:8585/mysql/test", "root", "wankatest***"); Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("show tables"); while (rs.next()) { System.out.println(rs.getString(1)); } rs.close(); connection.close(); }} 读取hive下的default库下的所有表 12345678910111213141516public class PrestoJdbcDemo { public static void main(String[] args) throws SQLException, ClassNotFoundException { Class.forName("com.facebook.presto.jdbc.PrestoDriver"); Connection connection = DriverManager .getConnection("jdbc:presto://10.10.25.14:8585/hive/default","root",null); Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("show tables"); while (rs.next()) { System.out.println(rs.getString(1)); } rs.close(); connection.close(); }} 不同数据源之间的joinpresto的一个特性就是其支持在不同的数据源之间进行join 当连接presto的客户端的时候,也可以不指定连接器 不同的数据源就用catalog名称指定,然后加上库名表明即可. 12./presto --server U007:8585 show tables from mysql.dsp_test; presto提供的函数和运算符参考文档:http://prestodb-china.com/docs/current/functions.html 看日志日志路径node.properties中配置了node.data-dir=/home/druid/data/presto 12345678910111213[druid@U007 presto]$ tree.├── etc -> /home/druid/presto-server-0.161/etc├── plugin -> /home/druid/presto-server-0.161/plugin└── var ├── log │ ├── http-request.log │ ├── launcher.log │ └── server.log └── run └── launcher.pid5 directories, 4 files 出现错误后,我们主要关注var/log目录下的日志. 当服务有问题的时候,看server.log找到报错原因,从而解决问题. 常见错误连接不上连接器类似这样的错误 12No factory for connector mysqlNo factory for connector hive 如果服务报相关这样的错误,那么就需要关注各个连接器的配置文件是否写对了.各个连接器的配置文件是否在每个presto服务器上都部署了. Presto的实现原理 Presto的架构 Presto执行原理 简单来说: cli客户端把查询需求发送给Coordinator节点.Coordinator节点负责解析sql语句,生成执行计划,分发执行任务给worer节点执行.所以worker节点负责实际执行查询任务. worker节点启动后向discovery server服务注册,因此coordinator就可以从discovery server获得可以正常工作的worker节点. 深入来说: 参考网上相关blog. 买书. 看源码! 参考文档 presto官网 presto-Github presto-京东维护-中文文档 Presto实现原理和美团的使用实践 Presto-Hive-Mysql-InteractiveQuery]]></content>
</entry>
<entry>
<title><![CDATA[二八定律与长尾理论]]></title>
<url>%2F2017%2Ferbachangwei%2F</url>
<content type="text"><![CDATA[刚入广告行业的时候,有时候会听同事们说长尾啥的.一直不太明白长尾是啥意思.最近看一本书,书里面提到了一个二八定律.本着好奇的态度,搜索了一下二八定律,没想到顺带都提到了长尾理论.接着这个机会,好好理解一下二八定律与长尾理论. 什么是二八定律我把wiki上的解释摘过来看看. 帕雷托法则(英语:Pareto principle),也称为二八定律或80/20法则,此法则指在众多现象中,80%的结果取决于20%的原因,而这一法则在很多方面被广泛的应用。如80%的劳动成果取决于20%的前期努力等等。这个法则最初是意大利经济学家维弗雷多·帕雷托在1906年对意大利20%的人口拥有80%的财产的观察而得出的,后来管理学思想家约瑟夫·朱兰和其他人把它概括为帕雷托法则。 所以简单说也就是,最重要的 什么是长尾理论未完待续]]></content>
</entry>
<entry>
<title><![CDATA[ELK搭建使用]]></title>
<url>%2F2017%2FELKBuild%2F</url>
<content type="text"><![CDATA[elk搭建记录,学习资料. ELK学习资料 ELKstack 中文指南 Elasticsearch权威指南(中文版) ….. ELK下载历史版本下载地址 : https://www.elastic.co/downloads/past-releases elasticsearch : 2.4.0 logstash : 2.4.0 kibana : 4.6.2 安装准备12345678910111213141516[root@U006 opt]# cd /opt[root@U006 opt]# mkdir elk[root@U006 opt]# chown -R hadoop:hadoop elk[root@U006 opt]# su hadoop[hadoop@U006 opt]$ cd /opt/elk/[hadoop@U006 elk]$ mkdir jars[hadoop@U006 jars]$ pwd/opt/elk/jars# 上传jar包[hadoop@U006 jars]$ lltotal 116996-rw-r--r-- 1 hadoop hadoop 27364449 Sep 18 09:36 elasticsearch-2.4.0.tar.gz-rw-r--r-- 1 hadoop hadoop 34125464 Sep 18 09:37 kibana-4.6.2-linux-x86_64.tar.gz-rw-r--r-- 1 hadoop hadoop 58310656 Sep 18 09:41 logstash-2.4.0.tar.gz 123456[hadoop@U006 elk]$ lltotal 16drwxrwxr-x 6 hadoop hadoop 4096 Sep 18 09:45 elasticsearch-2.4.0drwxrwxr-x 2 hadoop hadoop 4096 Sep 18 09:40 jarsdrwxrwxr-x 11 hadoop hadoop 4096 Oct 21 2016 kibana-4.6.2-linux-x86_64drwxrwxr-x 5 hadoop hadoop 4096 Sep 18 09:47 logstash-2.4.0 安装elasticsearch 测试环境搭建,单节点为例 12# 解压elasticsearchtar -zxvf elasticsearch-2.4.0.tar.gz -C /opt/elk/ 修改配置文件config/elasticsearch.yml1234567891011121314[hadoop@U006 config]$ pwd/opt/elk/elasticsearch-2.4.0/config[hadoop@U006 config]$ lltotal 8-rw-rw-r-- 1 hadoop hadoop 3192 Aug 24 2016 elasticsearch.yml-rw-rw-r-- 1 hadoop hadoop 2571 Aug 24 2016 logging.yml[hadoop@U006 elasticsearch-2.4.0]$ vim config/elasticsearch.yml# 打开这三个配置的注释 cluster.name: test_elasticsearch # es集群名字 node.name: node1 # 该节点在es中的名字 network.host: 0.0.0.0 # 任意节点可以访问 启动es12./bin/elasticsearch./bin/elasticsearch -d #后台启动 123456789101112131415[hadoop@U006 elasticsearch-2.4.0]$ ./bin/elasticsearch[2017-09-18 10:08:50,300][WARN ][bootstrap ] unable to install syscall filter: seccomp unavailable: requires kernel 3.5+ with CONFIG_SECCOMP and CONFIG_SECCOMP_FILTER compiled in[2017-09-18 10:08:51,697][INFO ][node ] [node1] version[2.4.0], pid[3098], build[ce9f0c7/2016-08-29T09:14:17Z][2017-09-18 10:08:51,697][INFO ][node ] [node1] initializing ...[2017-09-18 10:08:52,332][INFO ][plugins ] [node1] modules [lang-groovy, reindex, lang-expression], plugins [], sites [][2017-09-18 10:08:52,363][INFO ][env ] [node1] using [1] data paths, mounts [[/home (/dev/mapper/VolGroup-lv_home)]], net usable_space [195.3gb], net total_space [857.4gb], spins? [possibly], types [ext4][2017-09-18 10:08:52,363][INFO ][env ] [node1] heap size [989.8mb], compressed ordinary object pointers [true][2017-09-18 10:08:54,472][INFO ][node ] [node1] initialized[2017-09-18 10:08:54,473][INFO ][node ] [node1] starting ...[2017-09-18 10:08:54,675][INFO ][transport ] [node1] publish_address {10.10.25.13:9300}, bound_addresses {[::]:9300}[2017-09-18 10:08:54,685][INFO ][discovery ] [node1] test_elasticsearch/oAbAZR_tTaGAJU-5yd3BqQ[2017-09-18 10:08:57,757][INFO ][cluster.service ] [node1] new_master {node1}{oAbAZR_tTaGAJU-5yd3BqQ}{10.10.25.13}{10.10.25.13:9300}, reason: zen-disco-join(elected_as_master, [0] joins received)[2017-09-18 10:08:57,798][INFO ][http ] [node1] publish_address {10.10.25.13:9200}, bound_addresses {[::]:9200}[2017-09-18 10:08:57,799][INFO ][node ] [node1] started[2017-09-18 10:08:57,811][INFO ][gateway ] [node1] recovered [0] indices into cluster_state 打开http://10.10.25.13:9200/ 将会看到以下内容.返回数据中包含配置的cluster.name和node.name,以及es的版本等信息. 安装插件elasticsearch-head 插件安装下载安装12345678# 进入bin目录下[hadoop@U006 bin]$ ./plugin install mobz/elasticsearch-head-> Installing mobz/elasticsearch-head...Trying https://github.com/mobz/elasticsearch-head/archive/master.zip ...Downloading ...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................DONEVerifying https://github.com/mobz/elasticsearch-head/archive/master.zip checksums if available ...NOTE: Unable to verify checksum for downloaded plugin (unable to find .sha1 or .md5 file to verify)Installed head into /opt/elk/elasticsearch-2.4.0/plugins/head 使用说明 head插件是一个用浏览器跟ES集群交互的插件,可以查看集群状态、集群的doc内容、执行搜索和普通的Rest请求等。 在浏览器中直接访问接口 http://10.10.25.13:9200/_plugin/head/ ,可以看到es集群状态 安装Marvel插件下载安装1234567891011121314151617181920212223242526272829303132333435363738394041424344454647# ./bin/plugin install license# ./bin/plugin install marvel-agent[hadoop@U006 elasticsearch-2.4.0]$ ./bin/plugin install license-> Installing license...Trying https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/license/2.4.0/license-2.4.0.zip ...Downloading .......DONEVerifying https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/license/2.4.0/license-2.4.0.zip checksums if available ...Downloading .DONEInstalled license into /opt/elk/elasticsearch-2.4.0/plugins/license[hadoop@U006 elasticsearch-2.4.0]$ ./bin/plugin install marvel-agent-> Installing marvel-agent...Trying https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/marvel-agent/2.4.0/marvel-agent-2.4.0.zip ...Downloading ..........DONEVerifying https://download.elastic.co/elasticsearch/release/org/elasticsearch/plugin/marvel-agent/2.4.0/marvel-agent-2.4.0.zip checksums if available ...Downloading .DONE@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ WARNING: plugin requires additional permissions @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* java.lang.RuntimePermission setFactory* javax.net.ssl.SSLPermission setHostnameVerifierSee http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.htmlfor descriptions of what these permissions allow and the associated risks.Continue with installation? [y/N]yInstalled marvel-agent into /opt/elk/elasticsearch-2.4.0/plugins/marvel-agent# 安装的插件都会出现在es的plugins目录下[hadoop@U006 elasticsearch-2.4.0]$ lltotal 56drwxrwxr-x 2 hadoop hadoop 4096 Sep 18 09:45 bindrwxrwxr-x 3 hadoop hadoop 4096 Sep 18 10:05 configdrwxrwxr-x 3 hadoop hadoop 4096 Sep 18 09:59 datadrwxrwxr-x 2 hadoop hadoop 4096 Sep 18 09:45 lib-rw-rw-r-- 1 hadoop hadoop 11358 Aug 24 2016 LICENSE.txtdrwxrwxr-x 2 hadoop hadoop 4096 Sep 18 09:59 logsdrwxrwxr-x 5 hadoop hadoop 4096 Aug 29 2016 modules-rw-rw-r-- 1 hadoop hadoop 150 Aug 24 2016 NOTICE.txtdrwxrwxr-x 3 hadoop hadoop 4096 Sep 18 10:15 plugins-rw-rw-r-- 1 hadoop hadoop 8700 Aug 24 2016 README.textile[hadoop@U006 elasticsearch-2.4.0]$ cd plugins/[hadoop@U006 plugins]$ lltotal 12drwxrwxr-x 6 hadoop hadoop 4096 Sep 18 10:15 headdrwxrwxr-x 2 hadoop hadoop 4096 Sep 18 10:34 licensedrwxrwxr-x 2 hadoop hadoop 4096 Sep 18 10:34 marvel-agent 使用说明 Marvel是Elasticsearch的管理和监控工具,在开发环境下免费使用。它包含了一个叫做Sense的交互式控制台,使用户方便的通过浏览器直接与Elasticsearch进行交互。marvel插件主要会和kibana进行配置使用,待会看kibana也需要安装marvel插件 注意 如何之前在config/elasticsearch.yml的文件中,没有修改network.host项.那么你只能用localhost或者127.0.0.1访问es了. 注意配置yml结尾的配置文件都需要冒号后面加空格才行 安装kibana解压安装1tar -zxvf kibana-4.6.2-linux-x86_64.tar.gz -C /opt/elk/ 修改config/kibana.yml的elasticsearch.url属性即可。 安装插件安装Marvel插件 在安装es的时候,已经给es安装了marvel插件,现在给kibana也安装上marvel插件 下载123456789[hadoop@U006 kibana-4.6.2-linux-x86_64]$ bin/kibana plugin --install elasticsearch/marvel/latestInstalling marvelAttempting to transfer from https://download.elastic.co/elasticsearch/marvel/marvel-latest.tar.gz.....Transfer completeExtracting plugin archiveExtraction completeOptimizing and caching browser bundles...Plugin installation complete 启动验证12bin/elasticsearchbin/kibana 查看http://10.10.25.13:5601/app/marvel 页面: 安装sense插件 Sense是flask写的elasticsearch查询工具。支持es查询语言自动提示,es结构自动提示,支持两种主题,支持查询历史记录,支持快捷键。 下载123456789[hadoop@U006 kibana-4.6.2-linux-x86_64]$ ./bin/kibana plugin --install elastic/senseInstalling senseAttempting to transfer from https://download.elastic.co/elastic/sense/sense-latest.tar.gz.....Transfer completeExtracting plugin archiveExtraction completeOptimizing and caching browser bundles...Plugin installation complete 使用说明启动es和kibana查看http://10.10.25.13:5601/app/sense 页面: 安装logstash解压安装即可1tar -zxvf logstash-2.4.0.tar.gz -C /opt/elk/ 配置logstash的配置文件此文件input为从log4j接收日志.output为输出到es集群,进行搜索. 字段具体意思可以看官网 https://www.elastic.co/guide/en/logstash/current/plugins-inputs-log4j.html 12345678910111213141516[hadoop@U006 logstash-2.4.0]$ vim logstash_log4j_to_es.confinput { log4j { mode => "server" host => "10.10.25.13" port => 4567 type => "log4j" }}output{ elasticsearch{ action => "index" hosts => "10.10.25.13:9200" index => "test_log" }} 启动logstash12345678910bin/logstash agent -f logstash_log4j_to_es.conf# 或者bin/logstash -f logstash_log4j_to_es.conf[hadoop@U006 logstash-2.4.0]$ bin/logstash -f logstash_log4j_to_es.confSettings: Default pipeline workers: 24log4j:WARN No appenders could be found for logger (org.apache.http.client.protocol.RequestAuthCache).log4j:WARN Please initialize the log4j system properly.log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.Pipeline main started ELK框架的综合实际使用 在工作中,我们会使用ELK对业务日志进行收集分析.也就是把项目中的log4j日志用logstash进行收集,然后输出到es中进行索引搜索.最后使用Kibana进行可视化的搜索展示. 启动elk123bin/elasticsearchbin/kibanabin/logstash -f logstash_log4j_to_es.conf 打开:http://10.10.25.13:5601 如图所以,这里有个WARN警告:没有默认的索引模式,需要创建一个才能继续.那么我们就创建一个索引. 创建索引Kibana界面日志检索只有当第一条日志通过Logstash进入ElasticSearch后,才能配置Kibana索引。 1、在“Index name or pattern”项下,填入一个elasticsearch的索引名,也即是Logstash配置文件中output项下的index对应的名称;在你这里应该是将“logstash-* ” 改成“test_log”2、在“Time-field name”,选用默认的配置:“@timestamp”3、点击“create”即可 log4j日志接入编写log4j的测试代码12345678910111213141516public class TestFunc { Logger logger = LoggerFactory.getLogger(TestFunc.class); @Test public void testlog4j() throws Exception { while (true) { long s_time = System.currentTimeMillis(); logger.info("当前时间戳: "+s_time+" i am info info hadoop"); logger.warn("当前时间戳: "+s_time+" i am info warn spark hushiwei nice"); logger.error("当前时间戳: "+s_time+" i am info error elk"); Thread.sleep(1000L); } } } log4j的配置log4j.propertiesremotehost填写logstash的服务器地址.也就是那个input项里面的地址.12345log4j.rootLogger=INFO,socketlog4j.appender.socket=org.apache.log4j.net.SocketAppenderlog4j.appender.socket.RemoteHost=10.10.25.13log4j.appender.socket.Port=4567log4j.appender.socket.LocationInfo=true 执行代码,输出log4j日志,观察kibana页面变化索引日志里面的信息]]></content>
</entry>
<entry>
<title><![CDATA[Python读取文件编码及内容]]></title>
<url>%2F2017%2FPythonReadFiles%2F</url>
<content type="text"><![CDATA[当你不知道文件的具体编码方式的时候,如何正确的读取文件内容呢? 报错日志当我们不知道文件编码方式的时候,贸然的读取文件,有时候就会出现这些问题 12UnicodeDecodeError: 'gbk' codec can't decode byte...... 解决办法所以就是编码方式不对,那么需要先能识别文件的编码文件,然后根据此编码方式进行对文件编码,最后返回文件内容。可以借助一个第三方库chardet12# 安装chardetpip install chardet 正确实例1234with open("your_file", 'rb') as fp: file_data = fp.read() result = chardet.detect(file_data) file_content = file_data.decode(encoding=result['encoding'])]]></content>
</entry>
<entry>
<title><![CDATA[spark开发中遇到的问题]]></title>
<url>%2F2017%2FSparkBug%2F</url>
<content type="text"><![CDATA[spark连接mysql问题描述1总是报no suitable driver以及 jdbc.mysql.driver类似这样的错误 解决办法1121.提交任务的时候带上这个,手动指定mysql jar包的位置 SPARK_CLASSPATH=/usr/local/spark-1.4.1-bin-hadoop2.6/lib/mysql-connector-java-5.1.38.jar ./bin/spark-submit --class sparkDemo /root/data/demon-parent-1.0-SNAPSHOT-jar-with-dependencies.jar hdfs://192.168.119.100:9000/examples/custom.txt 解决办法2123修改了这个配置SPARK_HOME/conf/spark-env.sh文件,在里面加上了这个参数,就OK了export SPARK_CLASSPATH=$SPATH_CLASSPATH:/usr/hdp/2.4.0.0-169/spark/lib/mysql-connector-java-5.1.38.jar 在spark中使用hive抛出错误报错日志1234567891011121317/08/09 12:11:51 WARN DataNucleus.Persistence: Error creating validator of type org.datanucleus.properties.CorePropertyValidatorClassLoaderResolver for class "" gave error on creation : {1}org.datanucleus.exceptions.NucleusUserException: ClassLoaderResolver for class "" gave error on creation : {1} at org.datanucleus.NucleusContext.getClassLoaderResolver(NucleusContext.java:1087) at org.datanucleus.PersistenceConfiguration.validatePropertyValue(PersistenceConfiguration.java:797) at org.datanucleus.PersistenceConfiguration.setProperty(PersistenceConfiguration.java:714) at org.datanucleus.PersistenceConfiguration.setPersistenceProperties(PersistenceConfiguration.java:693) at org.datanucleus.NucleusContext.<init>(NucleusContext.java:273) at org.datanucleus.NucleusContext.<init>(NucleusContext.java:247) at org.datanucleus.NucleusContext.<init>(NucleusContext.java:225) at org.datanucleus.api.jdo.JDOPersistenceManagerFactory.<init>(JDOPersistenceManagerFactory.java:416) at org.datanucleus.api.jdo.JDOPersistenceManagerFactory.createPersistenceManagerFactory(JDOPersistenceManagerFactory.java:301) at org.datanucleus.api.jdo.JDOPersistenceManagerFactory.getPersistenceManagerFactory(JDOPersistenceManagerFactory.java:202) 问题分析看日志应该是缺少了hive的一些包,在网上搜了一下,是下面几个包12345678[hadoop@U007 lib]$ pwd/opt/spark-1.6.0/lib[hadoop@U007 lib]$ lltotal 305220-rw-r--r-- 1 hadoop hadoop 339666 Apr 15 2016 datanucleus-api-jdo-3.2.6.jar-rw-r--r-- 1 hadoop hadoop 1890075 Apr 15 2016 datanucleus-core-3.2.10.jar-rw-r--r-- 1 hadoop hadoop 1809447 Apr 15 2016 datanucleus-rdbms-3.2.9.jar... 所以在提交spark任务的时候,把这几个包加入到classpath中即可 解决办法在提交spark的脚本中加上这几个jar包和hive-site.xml文件如下123456789101112nohup spark-submit \ --master yarn \ --deploy-mode cluster \ --class ${className} \ --driver-memory 4g \ --executor-memory 2g \ --executor-cores 4 \ --num-executors 4 \ --jars ./lib/datanucleus-api-jdo-3.2.6.jar,./lib/datanucleus-core-3.2.10.jar,./lib/datanucleus-rdbms-3.2.9.jar \ --files ./lib/hive-site.xml \ ./app-jar-with-dependencies.jar \ 加上–jars 和 –files即可 在spark中将数据插入hive动态分区问题描述当我用standalone以及yarn-client模式进行提交任务的时候,不会报错.但是当我改成yarn-cluster模式进行提交任务,有时候就会报下面的错 报错日志1234567891011121314151617181920212223242526272817/08/09 10:08:01 ERROR scheduler.JobScheduler: Error running job streaming job 1502188440000 ms.0java.lang.NoSuchMethodException: org.apache.hadoop.hive.ql.metadata.Hive.loadDynamicPartitions(org.apache.hadoop.fs.Path, java.lang.String, java.util.Map, boolean, int, boolean, boolean) at java.lang.Class.getMethod(Class.java:1670) at org.apache.spark.sql.hive.client.Shim.findMethod(HiveShim.scala:114) at org.apache.spark.sql.hive.client.Shim_v0_12.loadDynamicPartitionsMethod$lzycompute(HiveShim.scala:168) at org.apache.spark.sql.hive.client.Shim_v0_12.loadDynamicPartitionsMethod(HiveShim.scala:167) at org.apache.spark.sql.hive.client.Shim_v0_12.loadDynamicPartitions(HiveShim.scala:261) at org.apache.spark.sql.hive.client.ClientWrapper$$anonfun$loadDynamicPartitions$1.apply$mcV$sp(ClientWrapper.scala:560) at org.apache.spark.sql.hive.client.ClientWrapper$$anonfun$loadDynamicPartitions$1.apply(ClientWrapper.scala:560) at org.apache.spark.sql.hive.client.ClientWrapper$$anonfun$loadDynamicPartitions$1.apply(ClientWrapper.scala:560) at org.apache.spark.sql.hive.client.ClientWrapper$$anonfun$withHiveState$1.apply(ClientWrapper.scala:279) at org.apache.spark.sql.hive.client.ClientWrapper.liftedTree1$1(ClientWrapper.scala:226) at org.apache.spark.sql.hive.client.ClientWrapper.retryLocked(ClientWrapper.scala:225) at org.apache.spark.sql.hive.client.ClientWrapper.withHiveState(ClientWrapper.scala:268) at org.apache.spark.sql.hive.client.ClientWrapper.loadDynamicPartitions(ClientWrapper.scala:559) at org.apache.spark.sql.hive.execution.InsertIntoHiveTable.sideEffectResult$lzycompute(InsertIntoHiveTable.scala:225) at org.apache.spark.sql.hive.execution.InsertIntoHiveTable.sideEffectResult(InsertIntoHiveTable.scala:127) at org.apache.spark.sql.hive.execution.InsertIntoHiveTable.doExecute(InsertIntoHiveTable.scala:276) at org.apache.spark.sql.execution.SparkPlan$$anonfun$execute$5.apply(SparkPlan.scala:132) at org.apache.spark.sql.execution.SparkPlan$$anonfun$execute$5.apply(SparkPlan.scala:130) at org.apache.spark.rdd.RDDOperationScope$.withScope(RDDOperationScope.scala:150) at org.apache.spark.sql.execution.SparkPlan.execute(SparkPlan.scala:130) at org.apache.spark.sql.execution.QueryExecution.toRdd$lzycompute(QueryExecution.scala:55) at org.apache.spark.sql.execution.QueryExecution.toRdd(QueryExecution.scala:55) at org.apache.spark.sql.DataFrame.<init>(DataFrame.scala:145) at org.apache.spark.sql.DataFrame.<init>(DataFrame.scala:130) at org.apache.spark.sql.DataFrame$.apply(DataFrame.scala:52) at org.apache.spark.sql.SQLContext.sql(SQLContext.scala:817) 分析用client模式的时候,是在13上运行的.没有问题用cluster模式的时候,有时候报错,有时候没有报错那不禁让我猜想,为啥cluster模式时而报错时而不报错呢? 然后我用client模式,在14上提交,不出我所料,基本上每个job都抛出了那个错误.所以定位到问题就是,除了13这个节点外,别的节点缺少了什么包,导致抛出了错误.因为抛出来的错误是java.lang.NoSuchMethodException:,所以肯定是缺少了什么包.之前cluster模式时而报错时而不报错的原因肯定是,当不报错的时候,正好driver端是在13上 现在的问题就是找出别的机器缺少什么包了. 然后我在spark的环境变量里面发现了这个参数1spark.sql.hive.metastore.jars : /usr/lib/hive/lib/*:/opt/spark-1.6.0/lib/spark-assembly-1.6.0-hadoop2.4.0.jar 我去,13上有这个/usr/lib/hive/lib/* 路径14和15上都没有,,,问题找到了 解决办法1把13上这个路径/usr/lib/hive/lib/* 拷贝到14和15上,各自都有一份.这样无论driver端在哪里,都能找到相应的jar包.就这样愉快的解决了.所以遇到问题,慢慢分析,不要像无头苍蝇一样.在网上搜的解决办法,都无法解决这个问题.所以有时候,具体问题具体分析,要慢慢的分析到出错原因.找到了原因,bug就能迎刃而解. 解决办法2在spark的配置文件中把 spark.sql.hive.metastore.jars 给删了.因为你总不能在每个节点上去拷贝hive的一些依赖吧,如果以后hive升级了,还得替换hive的jar包,太麻烦.所以改成下面的解决办法更好. 在pom文件中加上hive的依赖 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950<!-- https://mvnrepository.com/artifact/org.apache.spark/spark-core_2.10 --><dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-core_2.10</artifactId> <version>1.6.0</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.spark/spark-mllib_2.10 --><dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-mllib_2.10</artifactId> <version>1.6.0</version> <scope>provided</scope></dependency><dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming_2.10</artifactId> <version>1.6.0</version></dependency><dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming-kafka_2.10</artifactId> <version>1.6.0</version></dependency><dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-sql_2.10</artifactId> <version>1.6.0</version></dependency><dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-hive_2.10</artifactId> <version>1.6.0</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.32</version></dependency><dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-jdbc</artifactId> <version>0.13.1</version></dependency><!-- https://mvnrepository.com/artifact/org.apache.hive/hive-exec --><dependency> <groupId>org.apache.hive</groupId> <artifactId>hive-exec</artifactId> <version>0.13.1</version></dependency> sparkstreaming读取kafka数据问题描述Couldn’t find leaders for SetSparkStreaming程序从Kafka读数据的程序运行期间报了描述中的异常.通过监控分析发现,是由于有一个Broker挂掉了。可是对应Topic的replica设置的2,就算挂掉一个,应该有replica顶上啊。后来发现,这是由于存在Partition的Replica没有跟Leader保持同步更新,也就是通常所说的“没追上”。 查看某个Topic是否存在没追上的情况: 查看某个Topic是否存在没追上的情况:1kafka-topics.sh --describe --zookeeper XXX --topic XXX 报错日志123456717/10/13 09:41:13 ERROR DirectKafkaInputDStream: ArrayBuffer(java.nio.channels.ClosedChannelException, org.apache.spark.SparkException: Couldn't find leader offsets for Set([dsp_request_event,2]))17/10/13 09:41:13 ERROR StreamingContext: Error starting the context, marking it as stoppedorg.apache.spark.SparkException: ArrayBuffer(java.nio.channels.ClosedChannelException, org.apache.spark.SparkException: Couldn't find leader offsets for Set([dsp_request_event,2])) at org.apache.spark.streaming.kafka.DirectKafkaInputDStream.latestLeaderOffsets(DirectKafkaInputDStream.scala:123) at org.apache.spark.streaming.kafka.DirectKafkaInputDStream.compute(DirectKafkaInputDStream.scala:145) at org.apache.spark.streaming.dstream.DStream$$anonfun$getOrCompute$1$$anonfun$1$$anonfun$apply$7.apply(DStream.scala:352) at org.apache.spark.streaming.dstream.DStream$$anonfun$getOrCompute$1$$anonfun$1$$anonfun$apply$7.apply(DStream.scala:352) 解决办法观察其中的Replicas和Isr是否一致,如果出现Isr少于Replicas,则对应Partition存在没追上的情况解决方法:增大num.replica.fetchers的值,此参数是Replicas从Leader同步数据的线程数,默认为1,增大此参数即增大了同步IO。经过测试,增大此值后,不再有追不上的情况确定问题已解决的方法:启动出现问题的SparkStreaming程序,在程序正常计算的状态下,kill掉任意一个Broker后,再观察运行情况。在增大同步线程数之前,kill后SparkStreaming会报同样的异常,而增大后程序依然正常运行,问题解决。 参考:http://blog.csdn.net/yanshu2012/article/details/53995159]]></content>
</entry>
<entry>
<title><![CDATA[诗经]]></title>
<url>%2F2017%2Ffatan%2F</url>
<content type="text"><![CDATA[抄一首诗,换个心情… 伐檀坎坎伐檀兮,置之河之干兮,河水清且涟猗。不稼不穑,胡取禾三百廛[1]兮?不狩不猎,胡瞻尔庭有县[2]貆兮?彼君子兮,不素餐兮! 坎坎伐辐兮,置之河之侧兮,河水清且直猗。不稼不穑,胡取禾三百亿兮?不狩不猎,胡瞻尔庭有县特[3]兮?彼君子兮,不素食兮! 坎坎伐轮兮,置之河之漘[4]兮,河水清且沦猗。不稼不穑,胡取禾三百囷[5]兮?不狩不猎,胡瞻尔庭有县鹑兮?彼君子兮,不素飧兮! 伐檀白话砍伐檀树声坎坎啊,棵棵放倒堆河边啊,河水清清微波转哟。不播种来不收割,为何三百捆禾往家搬啊?不冬狩来不夜猎,为何见你庭院猪獾悬啊?那些老爷君子啊,不会白吃闲饭啊! 砍下檀树做车辐啊,放在河边堆一处啊。河水清清直流注哟。不播种来不收割,为何三百捆禾要独取啊?不冬狩来不夜猎,为何见你庭院兽悬柱啊?那些老爷君子啊,不会白吃饱腹啊! 砍下檀树做车轮啊,棵棵放倒河边屯啊。河水清清起波纹啊。不播种来不收割,为何三百捆禾要独吞啊?不冬狩来不夜猎,为何见你庭院挂鹌鹑啊?那些老爷君子啊,可不白吃腥荤啊!]]></content>
</entry>
<entry>
<title><![CDATA[ITerm2下使用ssh访问Linux(包括堡垒机)]]></title>
<url>%2F2017%2FMacSsh%2F</url>
<content type="text"><![CDATA[mac下没有xshell,虽然有SecurtCRT,但是真的太丑了.我还是比较喜欢用Iterm2来进行远程连接.这样不可避免的会碰到要记录远程密码,如果每次都输入,那就太麻烦了. Iterm2下使用ssh访问Linux通过情况下,Iterm2访问远程Linux使用ssh命令,如下:1ssh <用户名>@<ip> 然后输入访问密码即可登录进去.有时候远程访问的默认端口如果不是22,那就需要额外加上-p参数跟上远程访问端口进行登录了.很明显如果每次都要输入访问密码,那在开发过程中是相当的不方便的. 这里有两个方式实现免密登录.都是用Iterm2的Profiles功能加上脚本来实现. 方式1:使用spawn脚本文件将远程访问的相关内容写成一个脚本,然后在Profile里面调用即可.12cd /Users/hushiwei/.ssh/$ touch filename 脚本内容1234567891011#!/usr/bin/expect -f set user <用户名> set host <ip地址> set password <密码> set timeout -1 spawn ssh $user@$host expect "*assword:*" send "$password\r" interact expect eof 如何调用呢?在command中使用命令.command在哪看下面的图你就知道了.1expect <保存的脚本完整路径> 方式2:使用sshpass(推荐方式)brew安装sshpass1brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb 然后把密码写入到一个文件中1234hushiwei@localhost ~/sshpass pwd/Users/hushiwei/sshpasshushiwei@localhost ~/sshpass more passpasswd123 参考图中进行配置 command写上命令1/usr/local/bin/sshpass -f /Users/hushiwei/sshpass/pass ssh -p22 用户名@密码 然后在iterm2的菜单栏选择Profiles,然后点击刚刚的配置,即可免密自动登录到服务器上 注意:首先用命令行登录一次 iterm2登录堡垒机通过SSH和密钥文件(.pem格式)登录服务器[可能是堡垒机] 首先修改下密钥文件权限1sudo chmod 600 /Users/hushiwei/sshpass/Jumpserver/hushiwei.pem 其次,终端可直接命令连接1ssh -i /Users/hushiwei/sshpass/Jumpserver/hushiwei.pem hushiwei@xxx.xxx.xxx.xxx 注:首次连接时,会弹出密钥文件密码输入框,可以输入并保存! 除了直接命令连接外,也可参考上面Profiles功能,配置好,直接在Profile里调用!简单脚本如下: 配置Profile脚本自动登录堡垒机脚本文件 vim jumpserver12345678910hushiwei@localhost ~/sshpass/Jumpserver more jumpserver#!/usr/bin/expect -f set user hushiwei set host xxx.xxx.xxx.xxx set empath /Users/hushiwei/sshpass/Jumpserver/hushiwei.pem set timeout -1 spawn ssh -i $empath $user@$host interact expect eof 命令行执行123456789101112131415161718hushiwei@localhost ~/sshpass/Jumpserver expect /Users/hushiwei/sshpass/Jumpserver/jumpserverspawn ssh -i /Users/hushiwei/sshpass/Jumpserver/hushiwei.pem hushiwei@xxx.xxx.xxx.xxxLast login: Fri Aug 18 11:06:16 2017 from xxx.xxx.xxx.xxx### 欢迎使用Jumpserver开源跳板机系统 ### 1) 输入 ID 直接登录 或 输入部分 IP,主机名,备注 进行搜索登录(如果唯一). 2) 输入 / + IP, 主机名 or 备注 搜索. 如: /ip 3) 输入 P/p 显示您有权限的主机. 4) 输入 G/g 显示您有权限的主机组. 5) 输入 G/g + 组ID 显示该组下主机. 如: g1 6) 输入 E/e 批量执行命令. 7) 输入 U/u 批量上传文件. 8) 输入 D/d 批量下载文件. 9) 输入 H/h 帮助. 0) 输入 Q/q 退出.Opt or ID>: 参考上面的Profile功能,配置好,直接在Profile里调用即可12# 在Command里面写入以下即可expect /Users/hushiwei/sshpass/Jumpserver/jumpserver]]></content>
</entry>
<entry>
<title><![CDATA[监控SparkStreaming程序脚本]]></title>
<url>%2F2017%2FMonitorSparkStreamingOnYarn%2F</url>
<content type="text"><![CDATA[虽然Spark on yarn非常的稳定,一般情况下是不会出问题的.但是我们的SparkStreaming程序是一直运行着出实时报表的.我们必须得对SparkStreaming程序进行监控,在程序退出后,能够及时的重启.基于此需求,我想到了通过调用yarn的rest接口来获取提交到yarn上的任务 思路调用yarn提供的rest接口来获取所有正在运行的任务1curl --compressed -H "Accept: application/json" -X GET "http://master:8088/ws/v1/cluster/apps?states=RUNNING" 如果对别的接口有兴趣,可以看看官网. https://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-site/ResourceManagerRest.html http://www.winseliu.com/blog/2014/12/07/hadoop-mr-rest-api/ 脚本脚本也很简单,简单看看就明白了 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271# -*- coding: utf-8 -*-''' Created by hushiwei on 2018/1/5. 监控SparkStreaming程序 一旦挂了,执行重启,同时发送邮件和微信报警'''import osimport subprocessimport jsonimport loggingimport timeimport urllib2import smtplibfrom email.mime.text import MIMETextfrom email.mime.multipart import MIMEMultipartfrom email.header import Headerwechats = "HuShiwei"sendEmails = ['hsw_v5@163.com', 'xxxx@gm825.com']urlRun = 'curl --compressed -H "Accept: application/json" -X GET "http://u007:8089/ws/v1/cluster/apps?states=RUNNING"'urlAcc = 'curl --compressed -H "Accept: application/json" -X GET "http://u007:8089/ws/v1/cluster/apps?states=ACCEPTED"'monitorPrograms = { "com.xxxx.streaming.ADXStreaming": "/home/hadoop/statistics/ad/adxstreaming/start_adx_streaming_yarn.sh", "com.xxxx.online.streaming.DSPStreaming": "/home/hadoop/statistics/ad/dsp_ad_puton/dsp_ad_puton_streaming/start_dsp_streaming_yarn_test.sh", "com.xxxx.streaming.CPDAppStreaming": "/home/hadoop/statistics/ad/dsp_app_promotion/start_dsp_app_promotion_yarn.sh"}class WeChat(object): ''' 发送微信工具类 ''' def __init__(self, corpid, corpsecret, tokenpath): self.corpid = corpid self.corpsecret = corpsecret self.tokenpath = tokenpath self.logger = logging.getLogger('wechat') def saveToken(self): ''' :return: ''' try: with open(self.tokenpath, 'r') as f: token = f.read() if len(token) < 10: token = self.getToken() self.logger.info("Can not get token from %s,prepare to get token on api which token is %s" % ( self.tokenpath, token)) return token else: return token except IOError: token = self.getToken() self.logger.info( "Can not get token from %s,prepare to get token on api which token is %s" % (self.tokenpath, token)) return token def getToken(self): Url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s' % (self.corpid, self.corpsecret) req = urllib2.Request(Url) result = urllib2.urlopen(req) json_access_token = json.loads(result.read()) access_token = json_access_token['access_token'] with open(self.tokenpath, 'w') as f: f.write(access_token) return access_token def setMessage(self, wechatids, text): token = self.saveToken() message = self.makeMessage(text) submiturl = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={0}'.format(token) data = {"touser": wechatids, "msgtype": "text", "agentid": "1000002", "text": {"content": message}, "safe": "0"} data = json.dumps(data, ensure_ascii=False) send_request = urllib2.Request(submiturl, data) self.logger.info("Send wechat %s" % text) response = json.loads(urllib2.urlopen(send_request).read()) if response['errcode'] == 42001 or response['errcode'] == 40014: self.logger.info("Send wechat errorcode : %s" % response['errcode']) os.remove(self.tokenpath) self.setMessage(wechatids, text) def makeMessage(self, text): def date(): date = time.strftime('%m-%d %H:%M:%S', time.localtime()) return date return "%s \nCall Time:%s" % (text, date())class Message(object): ''' 构造邮箱发送的内容 ''' def format_str(self, strs): if not isinstance(strs, unicode): strs = unicode(strs) return strs def __init__(self, from_user, to_user, subject, content, with_attach=False): ''' :param from_user: 谁发过来的邮件 :param to_user: 发给谁 :param subject: 邮件主题 :param content: 邮件内容 :param with_attach: 邮件是否包含附件 ''' if with_attach: self._message = MIMEMultipart() self._message.attach(MIMEText(content, 'plain', 'utf-8')) else: self._message = MIMEText(content, 'plain', 'utf-8') self._message['Subject'] = Header(subject, 'utf-8') self._message['From'] = Header(self.format_str(from_user), 'utf-8') self._message['To'] = Header(self.format_str(to_user), 'utf-8') self._with_attach = with_attach def attach(self, file_path): if self._with_attach == False: print "Please init the Message with attr 'with_attach = True'" exit(1) if os.path.isfile(file_path) == False: print "The file doesn`t exist!" exit(1) atta = MIMEText(open(file_path, 'rb').read(), 'base64', 'utf-8') atta['Content-Type'] = 'application/octet-stream' atta['Content-Disposition'] = 'attachment; filename="%s"' % Header(os.path.basename(file_path), 'utf-8') self._message.attach(atta) def getMessage(self): return self._message.as_string()class SMTPClient(object): ''' 发送邮件工具类 ''' def __init__(self, hostname, port, user, passwd): ''' 初始化相关参数 :param hostname: QQ邮箱:smtp.qq.com :param port: QQ邮箱ssl加密端口:465 :param user: QQ邮箱账号 :param passwd: QQ邮箱授权秘钥,在web qq邮箱上获取 ''' self._HOST = hostname self._PORT = port self._USER = user self._PASS = passwd def send(self, receivers, msg): ''' 发送邮件方法 :param receivers: 邮件接收者,可以是多个.为列表 :param msg: 发送的邮件内容 :return: ''' if isinstance(msg, Message) == False: print "Error Message Instance!" exit(1) try: smtpObj = smtplib.SMTP_SSL(self._HOST, self._PORT) smtpObj.connect(self._HOST) smtpObj.login(self._USER, self._PASS) smtpObj.sendmail(self._USER, receivers, msg.getMessage()) return (1, "邮件发送成功") except smtplib.SMTPException, e: return (0, "Error: 无法发送邮件%s" % e)def run_it(cmd): ''' 通过python执行shell命令 :param cmd: :return: ''' p = subprocess.Popen(cmd, stdout=subprocess.PIPE, shell=True, stderr=subprocess.PIPE) # print ('running:%s' % cmd) out, err = p.communicate() if p.returncode != 0: print ("Non zero exit code:%s executing: %s \nerr course ---> %s" % (p.returncode, cmd, err)) return outdef reStartSparkScript(scriptPath): ''' 执行spark脚本 1.cd到脚本所在路径 2.在改路径执行脚本 :param scripyPath: :return: ''' logger = logging.getLogger("Main") scriptDir, script = os.path.split(scriptPath) os.chdir(scriptDir) run_it("sh %s" % script) logger.info("exec [ %s ] on [ %s ] " % (script, scriptDir))def collectMonitorStatus(yarnRestApi): ''' 从Yarn的Running接口或者Accept接口中获取我们需要监控的程序状态 :param str: yarn的running接口或者accept接口 :return: ''' strUrl = run_it(yarnRestApi) result = [] obj = json.loads(strUrl) if obj['apps'] is None: return result else: apps = obj['apps']['app'] result = [(app['name'], app['state']) for app in apps if app['name'] in monitorPrograms] return resultdef checkMonitorApps(): ''' 调用yarn的running接口和accept接口 判断这里面是否有我们需要监控的spark程序 如果没有就执行报警和重启 :return: ''' logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S') logger = logging.getLogger("Main") smpt_client = SMTPClient('smtp.qq.com', 465, '694244330@qq.com', 'xxxxxx') wechat_client = WeChat('xxxxxxxxxxxxxx', 'xxxxxxxxxxxxxxxxxxxxxxxxx', '/tmp/token.txt') runningStatus = collectMonitorStatus(urlRun) acceptStatus = collectMonitorStatus(urlAcc) runningAcceptApps = dict(runningStatus + acceptStatus) logger.info("SparkStreaming ON Yarn Running And Accept ===>%s " % str(runningAcceptApps)) for monitor in monitorPrograms: if monitor not in runningAcceptApps: logging.info("[ %s ] is not running or accept,prepare to restart!" % monitor) msg = Message("694244330@qq.com", "hushwiei", monitor, '%s is failed, prepare to resart! -- %s' % ( monitor, time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()))) smpt_client.send(sendEmails, msg) wechat_client.setMessage(wechats, "%s is not running or accept,prepare to restart!" % monitor) reStartSparkScript(monitorPrograms[monitor])def main(): checkMonitorApps()if __name__ == '__main__': main()]]></content>
</entry>
<entry>
<title><![CDATA[YARN 资源分配的配置参数]]></title>
<url>%2F2017%2FSparkOnYarn%2F</url>
<content type="text"><![CDATA[无论是mapreduce程序或者是Spark程序,提交到Yarn上来进行资源管理与分配的时候.都是运行在Yarn的Container容器中.而Container容器中是Yarn封装的内存和CPU资源.暂时还不支持对网络IO等资源进行封装分配.那么在开发调优过程中,我们肯定无法避免会对内存进行一些分配.那么Yarn的哪些配置参数是对哪个地方进行分配的,就很重要,也就值得记一记. 内存资源ResourceManager 配置参数 说明 备注 yarn.scheduler.minimum-allocation-mb 单个任务可申请的最少物理内存量,默认是1024(MB),如果一个任务申请的物理内存量少于该值,则该对应的值改为这个数 yarn.scheduler.maximum-allocation-mb 单个任务可申请的最多物理内存量,默认是8192(MB) 说明:也就是ResourceManager启动的Container容器的最大与最小内存. nodemanager 配置参数 说明 备注 yarn.nodemanager.resource.memory-mb 节点最大可用内存,默认8096M yarn.nodemanager.vmem-pmem-ratio 虚拟内存率,任务每使用1MB物理内存,最多可使用虚拟内存量,默认为 2.1 yarn.nodemanager.pmem-check-enabled 是否启动一个线程检查每个任务正使用的物理内存量,如果任务超出分配值,则直接将其杀掉,默认是true yarn.nodemanager.vmem-check-enabled 是否启动一个线程检查每个任务正使用的虚拟内存量,如果任务超出分配值,则直接将其杀掉,默认是true 说明: 在 Centos/RHEL 6 下,由于虚拟内存的分配策略比较激进,可以调高 yarn.nodemanager.vmem-pmem-ratio 或者关闭 yarn.nodemanager.vmem-check-enabled。 不然的话有时候就会碰上抛出容器超出内存限制,然后容器被kill掉. 比如这个问题 https://stackoverflow.com/questions/21005643/container-is-running-beyond-memory-limits ApplicationMaster 配置参数 说明 备注 mapreduce.map.memory.mb 分配给 Map Container的内存大小,运行时按需指定 mapreduce.reduce.memory.mb 分配给 Reduce Container的内存大小,运行时按需指定 mapreduce.map.java.opts 运行 Map 任务的 jvm 参数,如 -Xmx,-Xms 等选项 mapreduce.reduce.java.opts 运行 Reduce 任务的 jvm 参数,如-Xmx,-Xms等选项 CPU资源 配置参数 说明 备注 yarn.nodemanager.resource.cpu-vcores 该节点上 YARN 可使用的虚拟 CPU 个数,默认是8 yarn.scheduler.minimum-allocation-vcores 单个任务可申请的最小虚拟CPU个数, 默认是1 yarn.scheduler.maximum-allocation-vcores 单个任务可申请的最多虚拟CPU个数,默认是32]]></content>
</entry>
<entry>
<title><![CDATA[深入理解Java虚拟机之JDK可视化工具(二)]]></title>
<url>%2F2017%2FJDKOrderConsole%2F</url>
<content type="text"><![CDATA[除了JDK命令行工具,还有几个很强大的JDK可视化工具,希望接下来的学习,可以提高我们解决bug的能力 JConsole:Java监视与管理控制台 JConsole(Java Monitoring and Management Console)JConsole是在JDK1.5时期就已经提供的虚拟机监控工具JConsole是一款基于JMX的可视化监视和管理工具,它管理部分的功能是针对JMX MBean进行管理. 启动JConsole 1.安装的bin目录下执行jconsole 2.如果配置了 JAVA_HOME 直接输入jconsole Jconsole启动后,会自动搜索出本机运行的所有虚拟机进程,不需要用户再使用jps来查询了,如下图所示,双击选择其中一个进程即可开始监控.当然也可以使用 远程进程 功能来连接远程服务器,对远程虚拟机进行监控 栈内存 堆内存-Xms100m -Xmx100m -XX:+UseSerialGC]]></content>
</entry>
<entry>
<title><![CDATA[深入理解Java虚拟机之Java内存区域与内存溢出]]></title>
<url>%2F2017%2FJDKMemory%2F</url>
<content type="text"><![CDATA[对于java程序员来说,虽然有虚拟机的自动内存管理机制,我们即使不清楚内存是如何分配的,也不妨碍我们写代码.但是如果你不明白虚拟机究竟做了啥,你既不能快速定位问题,也不能成为一个优秀的程序员. 概述对于java程序员来说,在虚拟机的自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码(C/C++语言是需要的),而且不容易出现内存泄漏和内存溢出问题,看起来由虚拟机管理内存一切都很美好.不过,也正是因为java程序员把内存控制的权利交给了Java虚拟机,一旦出现内存泄露和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那排查错误将会成为一项异常艰难的工作.在这篇文章里,我们会写到这几个部分,了解了这几个部分,也就可以翻越虚拟机内存管理的第一步. java虚拟机内存的各个区域 各个区域的作用,服务对象,以及其中可能产生的问题 运行时数据区域 Java虚拟机在执行Java程序的过程中,会把它管理的内存划分为若干个不同的数据区域. 这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有些区域则是依赖用户线程的启动和结束而建立和销毁.根据Java虚拟机规范规定,Java虚拟机所管理的内存将会包括以下几个运行时数据区域程序计数器 程序计数器(Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器. 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理,线程恢复等基础功能都需要依赖这个计数器来完成.(想想我们平时写的java代码,是不是感觉一切都是有原因的) 对于多线程来说.由于每个线程都会执行自己的指令.那么为了线程切换后能恢复到正确的执行位置,因此每条线程都需要有一个独立的程序计数器,这样每条线程之间的计数器互不影响,独立存储.我们称这类内存区域称为 线程私有 的内存. Java虚拟机栈 与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stacks)也是线程私有的.它的生命周期与线程相同. 我们平时所说的栈内存,也就是这里的虚拟机栈.那么这个虚拟机栈究竟是什么呢?虚拟机栈描述的是java方法执行的内存模型: 每个方法被执行的时候都会同时创建一个 栈帧(Stack Frame) 用于存储局部变量表,操作栈,动态链接,方法出口等信息. 每一个方法被调用直至执行完成的过程,就对应着一个 栈帧 在虚拟机栈中从入栈到出栈的过程. 提一下局部变量表: 局部变量表存放了编译期可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double),对象引用(reference类型,它不等同于对象本身). 局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小. 局部变量表区域可能会抛出两种异常状况 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常. 如果虚拟机可以动态扩展,当扩展时无法申请到足够的内存时会抛出OutOfMemoryError. 本地方法栈 本地方法栈(Native Method Stacks)与虚拟机所发挥的作用是非常相似的 本地方法栈与虚拟机栈的区别 虚拟机栈为虚拟机执行Java方法(也就是字节码)服务 本地方法栈则是为虚拟机使用到的Native方法服务 有些虚拟机(譬如Sun HotSpot)直接就把本地方法栈和虚拟机栈合二为一 关于什么是Native方法呢?参考:http://blog.csdn.net/wike163/article/details/6635321 Java堆堆内存的特点 对于大多数应用来说,Java堆(Java heap)是Java虚拟机所管理的内存中最大的一块. Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建 . Java堆内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都是在这里分配内存. Java堆是垃圾收集器管理的主要区域.因为那么多实例在堆上分配内存,实例用完后,我们肯定要及时回收内存,这样才能给新的实例分配足够的内存呢. Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样. 如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryErrory异常. 再细分一下堆内存 从垃圾回收的角度看 因为现在的垃圾收集器都是采用分代收集算法,所以Java堆中还可以细分为:新生代,老年代,等等区域. 后面写到垃圾回收的时候,再细说这部分. 从内存分配的角度看 线程共享的Java堆中可能划分出多个线程私有的分配缓存区 在实现上 既可以实现成固定大小的,也可以是扩展的. 不过目前主流的虚拟机都是按照可扩展来实现的 -Xmx来设置程序的堆内存大小 -Xms来设置程序的栈内存大小 方法区 方法区(Method Area)方法区的特点 方法区与Java堆一样,是各个线程共享的内存区域. 它用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据. 然后Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java堆区分开来. 方法区被有些人称为”永久代(Permanent Generation)”是因为GC分代收集时候,方法区的变量会在永久代区域. 当方法区无法满足内存分区需求时,将抛出OutOfMemoryErrory异常. 运行时常量池运行时常量池是方法区的一部分.Class文件中除了有类的版本,字段,方法,接口等描述信息外,还有一项信息是 常量池(Constant Pool Table).用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中. Java语言并不要求常量一定只能在编译器产生,也就是并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中. 直接内存直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryErrory异常.那么究竟什么是直接内存呢? 在JDK1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作. 这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据. 对象访问(问题:在Java语言中,对象访问是如何进行的?) 对象访问在java语言中无处不在,是最普通的程序行为,但即使是最简单的访问,也会涉及Java栈,Java堆,方法区这三个最重要内存区域之间的关联关系.上面简单介绍了Java虚拟机的运行时数据区.说得比较文字化,不够具体.那么我们具体来探讨一个问题.在Java语言中,对象访问是如何进行的? 看这行最简单的代码,我们来解释这行代码.1Object obj=new Object(); 假设这句代码出现在方法体中,那么 Object obj 这部分的语义将会反映到 Java栈的本地变量表 中,作为一个reference类型数据出现. new Object() 这部分的语义将会反映到 Java堆 中,形成一块存储了Object类型所有实例数据值的结构化内存. 这块内存的长度是不固定 另外,在这个堆中还必须包含能查找到此对象类型数据(如对象类型,父类,实现的接口,方法等)的地址信息,这些类型数据则存储在方法区.]]></content>
</entry>
<entry>
<title><![CDATA[深入理解Java虚拟机之JDK命令行工具(一)]]></title>
<url>%2F2017%2FJDKOrder%2F</url>
<content type="text"><![CDATA[JDK命令行工具,是java提供给我们的礼物,我们怎么能拒绝他们的馈赠呢 jps:虚拟机进程状况工具 jps(JVM Process Status)可以列出正在运行的虚拟机进程,,并显示虚拟机执行主类(main函数的名称),以及这些进程的本地虚拟机的唯一ID(LVMID,Local Virtual Machine Identifier)对于本地虚拟机进程来说,LVMID与操作系统的进程ID(PID,Process Identifier)是一致的 jps命令格式1jps [options] [hostid] jps工具主要选项 选项 作用 -q 只输出LVMID,省略主类的名称 -m 输出虚拟机进程启动时传递给主类main()函数的参数 -l 输出主类的全名,如果进程执行的是jar包,输出jar路径 -v 输出虚拟机进程启动时JVM参数 jps命令样例11234567[hadoop@U006 ~]$ jps -l3183 sun.tools.jps.Jps17831 org.apache.spark.executor.CoarseGrainedExecutorBackend10004 org.apache.spark.deploy.worker.Worker17659 org.apache.spark.deploy.SparkSubmit10254 org.apache.spark.deploy.worker.Worker9830 org.apache.spark.deploy.master.Master jstat:虚拟机统计信息监视工具 jstat(JVM Statistics Monitoring Tool)jstat是用于监视虚拟机各种运行状态信息的命令行工具它可以显示本地或远程虚拟机进程中的类装载,内存,垃圾收集,JIT编译等运行数据.在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具 jstat命令格式1jstat [option vmid [interval[s|ms] [count]]] 注意: 对于命令中的VMID与LVMID需要特别说明一下:如果是本地虚拟机进程,VMID与LVMID是一致的. 如何是远程虚拟机进程,那VMID的格式应当是:[protocal:][//]lvmid[@hostname[:port]/servername] 参数interval和count代表查询间隔和次数.如果省略这两个参数,说明只查询一次. 假设需要每250毫秒查询一次进程2764垃圾收集的状况,一共查询20次,那么命令应该是:1jstat -gc 2764 250 20 每2毫秒查询10次spark进程的垃圾收集状况:123456789101112[hadoop@U006 ~]$ jstat -gc 17659 2 10 S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056349184.0 349184.0 0.0 0.0 2098176.0 954275.3 5592576.0 246723.4 116224.0 115802.6 52 5.099 48 18.956 24.056 jstat工具主要选项 选项 作用 -class 监视类装载,卸载数据,总空间及类装载所耗费的时间 -gc 监视Java堆状况,包括Eden区,2个survivor区,老年代,永久代等的容量,已用空间,GC时间合计等信息 -gccapacity 监视内容与-gc基本相同,但输出主要关注Java堆各个区域使用到的最大和最小空间 -gcutil 监视内容与-gc基本相同,但输出主要关注已使用空间占总空间的百分比 … … jstat执行样例123hadoop@U006 ~]$ jstat -gcutil 17659 S0 S1 E O P YGC YGCT FGC FGCT GCT 0.00 0.00 37.59 4.41 99.68 77 7.439 73 27.473 34.912 查询结果表明:这个进程的 新生代Eden区(E,表示Eden)使用了37.59%的空间. 两个Survivor区(S0,S1,表示Survivor0,Survivor1)里面都是空的. 老年代(O,表示Old)和永久代(P,表示Permanent)则分别使用了4.41%和99.68%的空间. 程序运行以来共发生Minor GC(YGC,表示Young GC)77次,总耗时7.439秒. 发生Full GC(FGC,表示Full GC)73次,Full GC总耗时(FGCT,表示Full GC Time)为27.473秒. 所有GC总耗时(GCT,表示GC Time)为34.912秒. 总结:使用jstat工具在纯文本状态下监视虚拟机状态的变化,虽然没有一些可视化监控工具来得直观.但与我而言,更显极客本色. jinfo:Java配置信息工具 jinso(Configuration Info For Java)jinfo的作用是实时地查看和调整虚拟机的各项参数 jinfo命令格式1jinfo [option] pid 使用方式前面解释过jps的-v选项.这个可以查看虚拟机启动时显示指定的参数列表,比如1234567[hadoop@U006 ~]$ jps -v17831 CoarseGrainedExecutorBackend -Xms4096M -Xmx4096M -Dspark.driver.port=42195 -XX:MaxPermSize=256m5146 Jps -Dapplication.home=/usr/java/jdk1.7.0_71 -Xms8m10004 Worker -Xms1g -Xmx1g -XX:MaxPermSize=256m17659 SparkSubmit -Xms8g -Xmx8g -XX:MaxPermSize=256m10254 Worker -Xms1g -Xmx1g -XX:MaxPermSize=256m9830 Master -Xms1g -Xmx1g -XX:MaxPermSize=256m 但如果想知道未被显示指定的参数的系统默认值,除了查找资料,还可以使用这里的jinfo的-flag选项进行查询了. 比如查询CMSInitiatingOccupancyFraction参数值:12[hadoop@U006 ~]$ jinfo -flag CMSInitiatingOccupancyFraction 17659-XX:CMSInitiatingOccupancyFraction=-1 jmap:Java内存映像工具 jmap(Memory Map For Java)jmap是用于生成堆转储快照(一般称为Heapdump或dump文件). jstack:Java堆栈跟踪工具 jstack(Stack Trace for Java)jstack命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或javacore文件) 线程快照: 就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是 定位线程出现长时间停顿的原因. 线程长时间停顿的常见原因:1.线程间死锁,死循环2.请求外部资源导致的长时间等待3.等等 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做些什么事情,或者等待着什么资源. jstack命令格式1jstack [option] vmid jstack工具的主要选项 选项 作用 -F 当正常输出的请求不被响应时,强制输出线程堆栈 -l 除堆栈外,显示关于锁的附加信息 -m 如果调用到本地方法的话,可以显示C/C++的堆栈]]></content>
</entry>
<entry>
<title><![CDATA[机器视觉处理与Tesseract介绍]]></title>
<url>%2F2017%2FTesseractBasic%2F</url>
<content type="text"><![CDATA[在读取和处理图像、图像相关的机器学习以及创建图像等任务中,Python 一直都是非常出色的语言。虽然有很多库可以进行图像处理,但目前我只接触到Tesseract. TesseractTesseract 是一个 OCR 库,目前由 Google 赞助(Google 也是一家以 OCR 和机器学习技术闻名于世的公司)。Tesseract 是目前公认最优秀、最精确的开源 OCR 系统。 除了极高的精确度,Tesseract 也具有很高的灵活性。它可以通过训练识别出任何字体,也可以识别出任何 Unicode 字符。 安装TesseractWindows 系统1下载可执行安装文件https://code.google.com/p/tesseract-ocr/downloads/list安装。 Linux 系统1可以通过 apt-get 安装: $sudo apt-get tesseract-ocr Mac OS X系统用 Homebrew(http://brew.sh/)等第三方库可以很方便地安装1brew install tesseract 要使用 Tesseract 的功能,比如后面的示例中训练程序识别字母,要先在系统中设置一个新的环境变量 $TESSDATA_PREFIX,让 Tesseract 知道训练的数据文件存储在哪里,然后搞一份tessdata数据文件,放到Tesseract目录下。 在大多数 Linux 系统和 Mac OS X 系统上,你可以这么设置:1$export TESSDATA_PREFIX=/usr/local/share/Tesseract 或者1234567hushiwei@localhost ~ more ~/.bash_profilealias l='ls -lF'alias ll='ls -alF'JAVA_HOME=`/usr/libexec/java_home`SCALA_HOME=/Users/hushiwei/devApps/scala-2.10.5MAVEN_HOME=/Users/hushiwei/devApps/maven-3.3.9TESSDATA_PREFIX=/Users/hushiwei/devApps/Tesseract 在 Windows 系统上也类似,你可以通过下面这行命令设置环境变量:1#setx TESSDATA_PREFIX C:\Program Files\Tesseract OCR\Tesseract 安装pytesseractTesseract 是一个 Python 的命令行工具,不是通过 import 语句导入的库。安装之后,要用 tesseract 命令在 Python 的外面运行,但我们可以通过 pip 安装支持Python 版本的 Tesseract库:1pip install pytesseract 简单示例目前只能处理规范的文字,那么什么算格式规范呢?格式规范的文字具有以下特点: 使用一个标准字体(不包含手写体、草书,或者十分“花哨的”字体) • 虽然被复印或拍照,字体还是很清晰,没有多余的痕迹或污点 排列整齐,没有歪歪斜斜的字 没有超出图片范围,也没有残缺不全,或紧紧贴在图片的边缘 格式规范的图片示例 命令行方式那么试一试Tesseract,,看看效果如何.用起来也是非常简单.读取图片,然后把结果写入到一个文本文件中123hushiwei@localhost ~/Desktop tesseract test.png textTesseract Open Source OCR Engine v3.05.01 with LeptonicaWarning. Invalid resolution 0 dpi. Using 70 instead. 接着打开这个文本看看效果123hushiwei@localhost ~/Desktop more text.txtThis is some text, written in Arial, that will be read byTesseract. Here are some symbols: !@#$%"&'() 除了一个小符号没有识别出来,其他的字符基本上都识别对了. python代码方式进行识别用之前安装的pytesseract模块,就可以很方便的完成我们想要的效果 12345678910111213import pytesseractfrom PIL import Image# 打开一个图片image=Image.open('test.png')# 调用pytesseract的image_to_string方法识别出图片中的文字,返回识别出来的文字text=pytesseract.image_to_string(image)# 打印文字看看效果print text 输出结果1234This is some text, written in Arial, that will be read byTesseract. Here are some symbols: !@#$%"&'()Process finished with exit code 0]]></content>
</entry>
<entry>
<title><![CDATA[python 可视化包-Matplotlib]]></title>
<url>%2F2017%2FMatplotlibBasic%2F</url>
<content type="text"><![CDATA[Matplotlib是Python中最常用的可视化工具之一,可以非常方便地创建海量类型地2D图表和一些基本的3D图表。 安装方式1234# ubuntu上安装sudo apt install python-matplotlib# mac上安装pip install matplotlib 快速入门快速入门小例子1之画单个图我们只要有x轴的数和y轴的数,那么就可以在坐标轴上画出图来了.1234567891011121314151617181920212223242526272829303132333435import numpy as npimport matplotlib as mplimport matplotlib.pyplot as plt# 通过rcParams设置全局横纵轴字体大小mpl.rcParams['xtick.labelsize']=24mpl.rcParams['ytick.labelsize']=24# x轴的点x1 = np.arange(11)# y轴的点y1 = [ 1.0847275042134147E-4, 2.0106877828356476E-4, 1.1836360644802181E-4, 0.043453404423487926, 0.03113001646083574, 0.06, 0.012709253496067191, 0.06, 3.284899860591644E-4, 0.015235253124714847, 0.0034946847451197242,]# 创建一个图,名字为ctrplt.figure("ctr")# 在图上绘制plt.plot(x1,y1)# 将当前figure的图像保存到文件result.pngplt.savefig('result.png')# 一定要加上这句才能让画好的图显示在屏幕上plt.show() 如图所示: 看上面就没有几行代码,但是就画出了一个图.所以用Matplotlib可以非常方便的绘制我们想要的图形.这里这是用最简单的例子说明一下. 快速入门小例子2之把两组坐标画在一个图上进行比较这里我们有两组数据,希望能够方便的比较这两组数据的差异,那么我们就可以把趋势都画在一个图上1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465import numpy as npimport matplotlib as mplimport matplotlib.pyplot as plt# 通过rcParams设置全局横纵轴字体大小mpl.rcParams['xtick.labelsize']=24mpl.rcParams['ytick.labelsize']=24# x轴的点x1 = np.arange(11)# y轴的点y1 = [ 1.0847275042134147E-4, 2.0106877828356476E-4, 1.1836360644802181E-4, 0.043453404423487926, 0.03113001646083574, 0.06, 0.012709253496067191, 0.06, 3.284899860591644E-4, 0.015235253124714847, 0.0034946847451197242,]# 创建一个图,名字为ctr#plt.figure("ctr")# 在图上绘制#plt.plot(x1,y1)x2 = np.arange(11)y2 = [ 3.529088519807792E-5, 1.1895968858318187E-4, 0.0013049292594645469, 0.046417845349992326, 0.03282177644291713, 0.06, 0.013313023920004725, 0.06, 3.554547063283854E-4, 0.014309633417956262, 0.0034946847451197242,]#plt.figure("ctrEstimate")#plt.plot(x2,y2,'k')# 两个图画一起plt.figure('ctr & ctrEstimate')plt.plot(x1, y1)# scatter可以方便出散点图# plt.scatter(x1,y11,c='red',marker='v')# plt.scatter(x2,y22,marker='^')# 'r'表示用红色线plt.plot(x2, y2, 'r')plt.show() 如图所示: 入门小例子3之多布局在一张图上构建多个布局画多张图12345678910111213141516171819202122232425import matplotlib as mplimport matplotlib.pyplot as plt# 通过rcParams设置全局横纵轴字体大小mpl.rcParams['xtick.labelsize']=24mpl.rcParams['ytick.labelsize']=24x=range(10)y=[5,4,3,2,1,6,7,8,9,0]fig=plt.figure("one figure many subplot")ax=fig.add_subplot(131)ax.set_title('Histogram')ax.bar(x,y)ax=fig.add_subplot(132)ax.set_title('line chart')ax.plot(x,y)ax=fig.add_subplot(133)ax.set_title(u'Scatter plot')ax.scatter(x,y)plt.show() 如图所示 matplotlib画图api解释单独的讲解api很无聊.直接写代码画图,代码里都有详细的说明 画2维的柱图和饼图12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576import numpy as npimport matplotlib as mplimport matplotlib.pyplot as pltmpl.rcParams['axes.titlesize'] = 20mpl.rcParams['xtick.labelsize'] = 16mpl.rcParams['ytick.labelsize'] = 16mpl.rcParams['axes.labelsize'] = 16mpl.rcParams['xtick.major.size'] = 0mpl.rcParams['ytick.major.size'] = 0# 包含了狗,猫和猎豹的最高奔跑速度,还有对应的可视化颜色speed_map = { 'dog': (48, '#7199cf'), 'cat': (45, '#4fc4aa'), 'cheetah': (120, '#e1a7a2')}# 整体图的标图fig=plt.figure('Bar chart & Pie chart')# 在整张图上加入一个子图,121的意思是在一个1行2列的子图中的第一张ax=fig.add_subplot(121)ax.set_title("Running speed - bar chart")# 生成x轴每个元素的位置 0 1 2xticks=np.arange(3)# 定义柱状图每个柱的宽度bar_width=0.5# 动物名称animals=speed_map.keys()# 奔跑速度speeds=[x[0] for x in speed_map.values()]# 对应颜色colors=[x[1] for x in speed_map.values()]# 画柱状图,横轴是动物标签的位置,纵轴是速度,定义柱的宽度,同时设置柱的边缘为透明bars=ax.bar(xticks,speeds,width=bar_width,edgecolor='none')# 设置y轴的标图ax.set_ylabel('Speed(km/h)')# x轴每个标签的具体位置,设置为每个柱的中央ax.set_xticks(xticks+bar_width/2)# 设置每个标签的名字ax.set_xticklabels(animals)# 设置x轴的范围ax.set_xlim([bar_width/2-0.5,3-bar_width/2])# 设置y轴的范围ax.set_ylim([0,125])# 给每个bar分配指定的颜色for bar,color in zip(bars,colors): bar.set_color(color)# 在122位置加入新的图ax=fig.add_subplot(122)ax.set_title('Running speed - pie chart')labels=['{}\n{} km/h'.format(animal,speed) for animal,speed in zip(animals,speeds)]# 画饼状图,并指定标签和对应颜色ax.pie(speeds,labels=labels,colors=colors)# ax.plot(speeds)plt.show()]]></content>
</entry>
<entry>
<title><![CDATA[Hbase笔记]]></title>
<url>%2F2017%2FHbaseNotes%2F</url>
<content type="text"><![CDATA[在开发hbase过程中,遇到的一些问题.还有些许知识点的总结. hbase的内存分配 HBase的默认堆分配策略,40%给blockcache,40%给memstore在HBase中,有两个在内存中的结构消费了绝大多数的heap空间。BlockCache缓存读操作的HFile block,Memstore缓存近期的写操作。 hfile.block.cache.size(读多的场景下,适当增大这个参数的值) hbase.regionserver.global.memstore.upperLimit(写多的场景下,适当增大这个参数的值)]]></content>
</entry>
<entry>
<title><![CDATA[年中总结]]></title>
<url>%2F2017%2FWhatIWriteIsShit%2F</url>
<content type="text"><![CDATA[写了这么多句,没有写出一句有意思的话;写了这么多篇,没有写出一篇有深意的文章;不是流水账,仍似流水账; 我干了些啥 2017已经过去了一半]]></content>
</entry>
<entry>
<title><![CDATA[机器学习实战之逻辑回归]]></title>
<url>%2F2017%2FLogicalRegression%2F</url>
<content type="text"><![CDATA[不知道什么是逻辑回归,但是也许看完接下来的文章,你会有个大概的印象吧 简单介绍Logistic回归Logistic回归用到的知识点 Sigmoid函数和Logistic回归分类器 最优化理论初步 梯度下降最优化算法 数据中的缺失项处理 Logistic回归的一般过程 1.收集数据:采用任意方法收集数据 2.准备数据:由于需要进行距离计算,因此要求数据类型为数值型.另外,结构化数据格式则最佳. 3.分析数据:采用任意方法对数据进行分析. 4.训练算法:大部分时间将用于训练,训练的目的是为了找到最佳的分类回归系数. 5.使用算法:首先,我们需要一些输入数据,并将其转换成对应的结构化数值;接着,基于训练好的回归系数就可以对这些数值进行简单的回归计算,判定他们属于哪个类别;在这之后,我们就可以在输出的类别上做一些其他分析工作. 基于Logistic回归和Sigmoid函数的分类Logistic回归 优点 计算代价不高 易于理解和实现 缺点 容易欠拟合 分类精度可能不高 适用类型 数值型(数值型目标变量则可以从无限的数值集合中取值,如0.100,42.001等 (数值型目标变量主要用于回归分析)) 标称型数据(标称型目标变量的结果只在有限目标集中取值,如真与假(标称型目标变量主要用于分类)) 基本公式:Sigmoid函数具体的计算公式 σ z = 1 1 + e - z]]></content>
</entry>
<entry>
<title><![CDATA[druid遇到的坑]]></title>
<url>%2F2017%2FDruidQuickStart%2F</url>
<content type="text"><![CDATA[学习druid过程中遇到的一些坑,记录一下.方便自己方便大家. 提交任务失败 错误日志 12341) Not enough direct memory. Please adjust -XX:MaxDirectMemorySize, druid.processing.buffer.sizeBytes, druid.processing.numThreads, or druid.processing.numMergeBuffers: maxDirectMemory[8,589,934,592], memoryNeeded[13,958,643,712] = druid.processing.buffer.sizeBytes[1,073,741,824] * (druid.processing.numMergeBuffers[2] + druid.processing.numThreads[10] + 1) 解决办法修改启动参数中的配置conf/druid/middlemanager/runtime.properties 123-XX:MaxDirectMemorySize=25gdruid.processing.numThreads=35只需要将处理线程数改小,或者调大启动参数 -XX:MaxDirectMemorySize 即可 coordinator协调节点,数据源报红 问题描述 解决办法]]></content>
</entry>
<entry>
<title><![CDATA[Ubuntu16.04使用Docker部署Gitlab]]></title>
<url>%2F2017%2FDockerInstallGitlab%2F</url>
<content type="text"><![CDATA[我个人非常不喜欢用SVN同步代码,正好想重温一下docker的一些命令,那就用docker方便的搭建一下gitlab. ubuntu安装docker 前提条件 Docker 要求 Ubuntu 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的Ubuntu版本是否支持 Docker 。打开控制台使用 uname -r命令来查看你当前的内核版本。12$ uname -r 3.11.0-15-generic 安装步骤1234sudo apt-get updatesudo apt-get update $ sudo apt-get install wget //安装wgetwget -qO- https://get.docker.com/ | sh //获取最新版本的 Docker 安装包,输入完成之后,就会下载脚本并且安装Docker及依赖包。docker info //验证安装结果 问题Cannot connect to the Docker daemon. Is the docker daemon running on this host? 解决:1234sudo su - //切换到rootservice docker start //启动docker servicedocker images //显示所有imagesdocker run hello-world //重新运行 恩,是权限问题,当前用户没权限,root用户可以运行 下载需要的镜像以下脚本会下载gitlab,mysql,redis镜像123docker pull sameersbn/gitlab:latestdocker pull sameersbn/mysql:latestdocker pull sameersbn/redis:latest 启动需要的镜像启动redis1234docker run \ --name=gitlab_redis \ -tid \ sameersbn/redis:latest 启动mysql注意这里写你的用户名和密码123456789mkdir -p /opt/gitlab/mysqldocker run \ --name=gitlab_mysql \ -tid \ -e 'DB_NAME=gitlabhq_production' \ -e 'DB_USER=gitlab' \ -e 'DB_PASS=password' \ -v /opt/gitlab/mysql:/var/lib/mysql \ sameersbn/mysql:latest 启动gitlab12345678910111213141516docker run \ --name='gitlab' \ -itd \ --link gitlab_mysql:mysql \ --link gitlab_redis:redisio \ -e 'GITLAB_PORT=80' \ -e 'GITLAB_SSH_PORT=22' \ --env 'GITLAB_SECRETS_DB_KEY_BASE=long-and-random-alpha-numeric-string' \ --env 'GITLAB_SECRETS_SECRET_KEY_BASE=long-and-random-alpha-numeric-string' \ --env 'GITLAB_SECRETS_OTP_KEY_BASE=long-and-random-alpha-numeric-string' \ -p 10022:22 -p 10080:80 \ -v /var/run/docker.sock:/run/docker.sock \ -v $(which docker):/bin/docker \ -v /opt/gitlab/data:/home/git/data \ -v /opt/gitlab/log:/var/log/gitlab \ sameersbn/gitlab:latest 这一步骤会耗时几分钟,因为这一步会做一些初始化操作,我们可以通过docker logs gitlab来查看安装过程。我们指定了10022作为ssh的访问端口,10080作为http的访问端口, 执行完后,稍微过1-2分钟后,打开以下地址应该就可以访问gitlab了http://localhost:10080如果是在服务器上搭建,请替换localhost为服务器地址 注意:当出了问题,可以用一些命令查看 123456# 列出正在运行的容器root@bigdata:~# docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESaa93b0654f4b sameersbn/gitlab:latest "/sbin/entrypoint...." 43 minutes ago Up 13 minutes 443/tcp, 0.0.0.0:10022->22/tcp, 0.0.0.0:10080->80/tcp gitlab881c31d50c53 sameersbn/mysql:latest "/sbin/entrypoint...." About an hour ago Up 14 minutes 3306/tcp gitlab_mysqle5573736680f sameersbn/redis:latest "/sbin/entrypoint.sh" About an hour ago Up 14 minutes 6379/tcp gitlab_redis 列出所有容器的状态12345root@bigdata:~# docker ps -aCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESaa93b0654f4b sameersbn/gitlab:latest "/sbin/entrypoint...." 44 minutes ago Up 14 minutes 443/tcp, 0.0.0.0:10022->22/tcp, 0.0.0.0:10080->80/tcp gitlab881c31d50c53 sameersbn/mysql:latest "/sbin/entrypoint...." About an hour ago Up 14 minutes 3306/tcp gitlab_mysqle5573736680f sameersbn/redis:latest "/sbin/entrypoint.sh" About an hour ago Up 15 minutes 6379/tcp gitlab_redis 后面可以是容易id,也可以是容器名称 docker start CONTAINER ID #启动容器 docker stop CONTAINER ID #停止容器 docker rm -f CONTAINER ID #删除容器 docker logs CONTAINERID #查看容器的运行时日志 进入docker中的服务器1docker exec -it 容器名字 bash 举例:上面部署gitlab,我们还启动了一个mysql容器,我们可以进入这个mysql容器里面看看123456789101112131415root@bigdata:~# docker exec -it gitlab_mysql bashroot@881c31d50c53:/# mysqlWelcome to the MySQL monitor. Commands end with ; or \g.Your MySQL connection id is 16Server version: 5.5.54-0ubuntu0.14.04.1 (Ubuntu)Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.Oracle is a registered trademark of Oracle Corporation and/or itsaffiliates. Other names may be trademarks of their respectiveowners.Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql> 这样就进入了mysql容器中了. 加入开机启动加入/etc/rc.local 123echo 'docker start gitlab_redis' >> /etc/rc.localecho 'docker start gitlab_mysql' >> /etc/rc.localecho 'docker start gitlab' >> /etc/rc.local 备份和恢复gitlab备份123docker stop gitlab && docker rm gitlabdocker run --name=gitlab -it --rm [OPTIONS] \sameersbn/gitlab:latest app:rake gitlab:backup:create 恢复123docker stop gitlab && docker rm gitlabdocker run --name=gitlab -it --rm [OPTIONS] \sameersbn/gitlab:latest app:rake gitlab:backup:restore 补充当进入了gitlab的web页面后,新建了一个项目.可以用下面的链接去把工程clone下来.ip换成你的ip地址.因为上面做了端口映射容器的80端口映射到外面是10080端口. 1git clone http://10.211.55.7:10080/shiwei/learndocker.git]]></content>
</entry>
<entry>
<title><![CDATA[项目中配置文件的读取]]></title>
<url>%2F2017%2FReadConfigFile%2F</url>
<content type="text"><![CDATA[在工作中,我们一般都不会把变化的地方写死.比如开发环境和测试环境的服务器地址,等等之类的信息.这些我们都会写入到配置文件中,方便修改. com.typesafe.config加载任意位置的配置文件 typesafe下的config包可以用来读取配置文件,支持多种形式。 Overview Typesafe的Config库,纯Java写成、零外部依赖、代码精简、功能灵活、API友好。支持Java properties、JSON、JSON超集格式HOCON以及环境变量。它也是Akka的配置管理库. 如果没引上,使用如下依赖 123456<!-- https://mvnrepository.com/artifact/com.typesafe/config --><dependency> <groupId>com.typesafe</groupId> <artifactId>config</artifactId> <version>1.3.1</version></dependency> 纯java实现,无任何依赖 可以合并各种格式的配置文件 可以通过文件、urls、classpath加载配置 支持多层嵌套的配置方式 可以转换长短,大小等单位 类型转换 加载配置文件的两种方式目录结构1234567891011 hushiwei@localhost ~/IdeaProjects/testProject/src/main tree.├── java│ └── com│ └── husw├── resources│ ├── conf│ │ └── application.conf│ ├── testlog.txt│ └── log4j.properties└── scala 配置文件示例1234567891011121314kafka { topics = "test_imp" #注释 brokers = "192.168.1.1:9092,192.168.2.1:9092" groupid="test_group1" offset_reset="smallest" #smallest and largest}spark { app_name = "dsp_count" mode = "local[2]" #"yarn-client" queue = "normal" duration = 2 numFilesPerBatch = 1 stagingdir = "/tmp/spark/checkpoint"} 加载resources目录下的配置文件默认读取resources目录下的文件1val config = ConfigFactory.load("conf/application.conf") 加载任意指定位置的配置文件路径可以写任意的绝对路径,或者相对路径1val config = ConfigFactory.parseFile(new File("src/main/resources/conf/application.conf")) 获取配置文件中的键值读取配置文件后返回了config对象.直接调用config对象相应的api即可获取配置数据了Api文档示例12val topics = config.getString("kafka.topics").split(",").toSetprintln(topics) 若要知道更详细的api说明,点击上面的链接即可.]]></content>
</entry>
<entry>
<title><![CDATA[python 高级特性之切片]]></title>
<url>%2F2017%2FPythonSplitFunc%2F</url>
<content type="text"><![CDATA[python很方便实用,如果你不会切片.那么你真的错过了什么叫方便和实用. 特点 python中的序列可以使用切片特性 序列类型后面跟上一对方括号. 方括号有一对可选的数字,并用冒号分隔. 冒号是必须的 切片操作符中的第一个数(冒号之前)表示切片开始的位置,第二个数(冒号之后)表示切片到哪里结束,第三个数(冒号之后)表示切片间隔数(也就是步长)。如果不指定第一个数,Python就从序列首开始。如果没有指定第二个数,则python会停止在序列尾。注意:左包右闭12str='dmlcoding.com'str[]]]></content>
</entry>
<entry>
<title><![CDATA[Git 常用命令以及笔记]]></title>
<url>%2F2017%2FGitSkills%2F</url>
<content type="text"><![CDATA[平时一直是用Intellij进行开发,git命令也不会用命令行去输入.导致有些命令都不太熟悉了,每次用到的时候都是google.今天把平时用到的命令做个笔记记录一下. git分布式仓库工作流程这里有个图,非常形象的展示了本地仓库和远程仓库以及常用的一些命令的作用范围.一图胜千言,图片摘自http://krishnaiitd.github.io/gitcommands/git-workflow/ 设置 commit 的用户和邮箱1234git config user.name "xx" #设置 commit 的用户git config user.email "xx@xx.com" #设置 commit 的邮箱git commit --amend --author "xxx <xxx@gmail.com>" #修改上次提交的用户信息git config format.pretty oneline #显示历史记录时,每个提交的信息只显示一行 配置本地到github的免密参考我之前写的 http://dmlcoding.com/2016/sshlogin/ 远程仓库添加远程仓库1git remote add origin git@server-name:path/repo-name.git #添加一个远程库 查看远程仓库 origin是远程仓库的别名,约定俗成的叫做original. 12git remote #要查看远程库的信息git remote -v #显示更详细的信息 推送分支1git push origin master #推送到远程master分支 抓取分支12345git clone git@server-name:path/repo-name.git #克隆远程仓库到本地(能看到master分支)git checkout -b dev origin/dev #创建远程origin的dev分支到本地,并命名为devgit checkout origin/dev --track #与上面效果一样git pull origin master #从远程分支进行更新git fetch origin master #获取远程分支上的数据 抓取GitHub上某个pull request到本地12345git fetch origin pull/ID/head:BRANCHNAMEgit checkout BRANCHNAME$ git branch --set-upstream branch-name origin/branch-name,可以建立起本地分支和远程分支的关联,之后可以直接git pull从远程抓取分支。另外,git pull = git fetch + merge to local 删除远程分支1234567$ git push origin --delete bugfixTo https://github.com/wuchong/jacman - [deleted] bugfix# 或者直接push一个空分支$ git push origin :bugfixTo https://github.com/wuchong/jacman - [deleted] bugfix 更新远程分支信息项目往前推进的过程中,远程仓库上经常会增加一些分支、删除一些分支。 所以有时需要与远程同步下分支信息。1git fetch -p -p就是修剪的意思。它在fetch之后删除掉没有与远程分支对应的本地分支,并且同步一些远程新创建的分支和tag。 历史管理查看历史123git log --pretty=oneline filename #一行显示git log -p -2 #显示最近2次提交内容的差异git show cb926e7 #查看某次修改 版本回退123git reset --hard HEAD^ #回退到上一个版本git reset --hard cb926e7 #回退到具体某个版git reflog #查看命令历史,常用于帮助找回丢失掉的commit 用HEAD表示当前版本,上一个版本就是HEAD^,上上一个版本就是HEAD^^,HEAD~100就是上100个版本。 管理修改123git status #查看工作区、暂存区的状态git checkout -- <file> #丢弃工作区上某个文件的修改git reset HEAD <file> #丢弃暂存区上某个文件的修改,重新放回工作区 查看差异12345git diff #查看未暂存的文件更新git diff --cached #查看已暂存文件的更新git diff HEAD -- readme.txt #查看工作区和版本库里面最新版本的区别git diff <source_branch> <target_branch> #在合并改动之前,预览两个分支的差异使用内建的图形化git:gitk,可以更方便清晰地查看差异。当然 Github 客户端也不错。 删除文件12git rm <file> #直接删除文件git rm --cached <file> #删除文件暂存状态 储藏和恢复1234git stash #储藏当前工作git stash list #查看储藏的工作现场git stash apply #恢复工作现场,stash内容并不删除git stash pop #恢复工作现场,并删除stash内容 分支管理创建分支12git branch develop #只创建分支git checkout -b master develop #创建并切换到 develop 分支 合并分支123git checkout master #切换到主分支git merge --no-ff develop #把 develop 合并到 master 分支,no-ff 选项的作用是保留原分支记录git branch -d develop #删除 develop 分支 标签显示标签12git tag #列出现有标签git show <tagname> #显示标签信息 创建标签123git tag v0.1 #新建标签,默认位 HEADgit tag v0.1 cb926e7 #对指定的 commit id 打标签git tag -a v0.1 -m 'version 0.1 released' #新建带注释标签 操作标签123456git checkout <tagname> #切换到标签git push origin <tagname> #推送分支到源上git push origin --tags #一次性推送全部尚未推送到远程的本地标签git tag -d <tagname> #删除标签git push origin :refs/tags/<tagname> #删除远程标签Git 设置 转载自Jark’s Blog]]></content>
</entry>
<entry>
<title><![CDATA[zabbix 部署文档]]></title>
<url>%2F2017%2FZabbix%2F</url>
<content type="text"><![CDATA[记录下搭建过程,以免以后又重复花时间找搭建文档 搭建过程搭建是参照这篇博客来搭建的: http://blog.csdn.net/su1322339466/article/details/53869738 注意安装zabbix_server后,启动 web 页面,默认账号为: 12默认账号: Admin默认密码: zabbix]]></content>
</entry>
<entry>
<title><![CDATA[saltstack web halite 界面部署文档]]></title>
<url>%2F2017%2FSaltStackWeb%2F</url>
<content type="text"><![CDATA[安装saltstack webui halite管理界面,方便查看和操作安装部署自动化管理工具SaltStack.在管理多台服务器上,这个工具还是非常好使的,另外由于是 Python写的,也可以很方便的对其进行个性化修改. 安装halite12345cd /var/www/ git clone https://github.com/saltstack/halite[root@master halite]# pwd/var/www/halite/halite[root@master halite]# ./genindex.py -C #生成index.html文件: 安装salt-api1yum install salt-api 配置salt master文件配置salt的master文件,添加:12345678910111213[root@master ~]# vim /etc/salt/masterrest_cherrypy: host: 0.0.0.0 port: 8080 debug: true static: /var/www/halite/halite app: /var/www/halite/halite/index.htmlexternal_auth: pam: rui: - .* - '@runner' - '@wheel' 重启master123[root@master ~]# /etc/init.d/salt-master restartStopping salt-master daemon: [ OK ]Starting salt-master daemon: [ OK ] 添加登陆用户12345678[root@master ~]# useradd rui[root@master ~]# passwd ruiChanging password for user rui.New password:BAD PASSWORD: it is too shortBAD PASSWORD: is too simpleRetype new password:passwd: all authentication tokens updated successfully. 启动 salt-api123salt-api -d 或 cd /var/www/halite/halitepython server_bottle.py -d -C -l debug -s cherrypy 123456789101112131415[root@master ~]# cd /var/www/halite/halite/[root@master halite]# python server_bottle.py -d -C -l debug -s cherrypy20170415_103758.177076 Bottle: Running web application server 'cherrypy' on 0.0.0.0:8080.20170415_103758.177237 Bottle: CORS is disabled.20170415_103758.177289 Bottle: TLS/SSL is disabled.20170415_103758.177335 Bottle: Server options:{}20170415_103758.417684 Bottle: Running web application server 'cherrypy' on 0.0.0.0:8080.20170415_103758.417833 Bottle: CORS is disabled.20170415_103758.417886 Bottle: TLS/SSL is disabled.20170415_103758.417931 Bottle: Server options:{}Bottle v0.12-dev server starting up (using CherryPyServer())...Listening on http://0.0.0.0:8080/Hit Ctrl-C to quit.]]></content>
</entry>
<entry>
<title><![CDATA[saltstack部署文档]]></title>
<url>%2F2017%2FSaltStack%2F</url>
<content type="text"><![CDATA[安装部署自动化管理工具SaltStack.在管理多台服务器上,这个工具还是非常好使的,另外由于是 Python写的,也可以很方便的对其进行个性化修改. 机器 IP hosts 角色 10.211.55.4 master salt-master/salt-minion 10.211.55.5 minion salt-minion master 端配置防火墙(没有配置,直接把防火墙关了)1234[root@master ~]# vim /etc/sysconfig/iptables#加入-A INPUT -m state --state new -m tcp -p tcp --dport 4505 -j ACCEPT-A INPUT -m state --state new -m tcp -p tcp --dport 4506 -j ACCEPT 安装 epel 源12[root@master ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo[root@minion ~]# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo 关闭防火墙123456789[root@master ~]# service iptables stopiptables: Setting chains to policy ACCEPT: nat mangle filte[ OK ]iptables: Flushing firewall rules: [ OK ]iptables: Unloading modules: [ OK ][root@minion ~]# service iptables stopiptables: Setting chains to policy ACCEPT: nat mangle filte[ OK ]iptables: Flushing firewall rules: [ OK ]iptables: Unloading modules: [ OK ] 安装配置master 端安装1[root@master ~]# yum -y install salt-master minion 端安装1[root@minion ~]# yum -y install salt-minion master 端配置1234567891011121314151617181920212223# 备份[root@master ~]# cp /etc/salt/master /etc/salt/master.bak[root@master ~]# vim /etc/salt/master# 去掉这几行的注释405 # Example:406 # file_roots:407 # base:408 # - /srv/salt/409 # dev:410 # - /srv/salt/dev/services411 # - /srv/salt/dev/states412 # prod:413 # - /srv/salt/prod/services414 # - /srv/salt/prod/states415 #416 file_roots:417 base:418 - /srv/salt419# 去掉这几行的注释529 pillar_roots:530 base:531 - /srv/pillar 接着启动 saltmaster 服务12[root@master ~]# /etc/init.d/salt-master startStarting salt-master daemon: [ OK ] minion 端配置123456789101112# 备份[root@minion ~]# cp /etc/salt/minion /etc/salt/minion.bak [root@minion ~]# vim /etc/salt/minion# Set the location of the salt master server. If the master server cannot be# resolved, then the minion will fail to start.master: master #改成 master 的主机或者 IP 76 # same machine but with different ids, this can be useful for salt compute 77 # clusters. 78 id: salt-minion #定义个名字(建议起个有意义的名字如:nfs、nginx等) 79 接着启动 saltminion 服务12[root@minion ~]# /etc/init.d/salt-minion startStarting salt-minion daemon: [ OK ] 验证master 端123456[root@master ~]# salt-key -L#显示所有minion认证信息Accepted Keys:Denied Keys:Unaccepted Keys:salt-minionRejected Keys: 12345678910111213[root@master ~]# salt-key -a salt-minion #接受salt-minion的认证信息;可跟参数 -yThe following keys are going to be accepted:Unaccepted Keys:salt-minionProceed? [n/Y] yKey for minion salt-minion accepted.[root@master ~]# salt-keyAccepted Keys:salt-minionDenied Keys:Unaccepted Keys:Rejected Keys: 1[root@master ~]# salt-key -A #接受所有Unaccepted状态的minion认证信息 一些简单命令介绍1234567891011121314151617[root@master ~]# salt '*' test.pingsalt-minion: True[root@master ~]# salt 'salt-minion' test.pingsalt-minion: True[root@master ~]# salt '*' cmd.run 'df -h'salt-minion: Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 31G 3.4G 26G 12% / tmpfs 497M 96K 497M 1% /dev/shm /dev/sda1 485M 35M 426M 8% /boot /dev/mapper/VolGroup-lv_home 31G 178M 29G 1% /home Home 233G 99G 134G 43% /media/psf/Home iCloud 233G 99G 134G 43% /media/psf/iCloud /dev/sr1 4.2G 4.2G 0 100% /media/CentOS_6.5_Final /dev/sr0 98M 98M 0 100% /media/CDROM 在 master 上也安装上 minion这样就有两个 minion]]></content>
</entry>
<entry>
<title><![CDATA[mysql赋给用户权限grant all privileges on]]></title>
<url>%2F2017%2Fmysqlprivileges%2F</url>
<content type="text"><![CDATA[权限问题 问题 1遇到了 SQLException: access denied for @'localhost' (using password: no) 解决办法 12grant all privileges on *.* to joe@localhost identified by '1';flush privileges; 详细介绍1MySQL> grant 权限1,权限2,…权限n on 数据库名称.表名称 to 用户名@用户地址 identified by ‘连接口令’; 权限1,权限2,…权限n代表select,insert,update,delete,create,drop,index,alter,grant,references,reload,shutdown,process,file等14个权限。 当权限1,权限2,…权限n被all privileges或者all代替,表示赋予用户全部权限。 当数据库名称.表名称被.代替,表示赋予用户操作服务器上所有数据库所有表的权限。 用户地址可以是localhost,也可以是ip地址、机器名字、域名。也可以用’%’表示从任何地址连接。 ‘连接口令’不能为空,否则创建失败。 实例1234567891011mysql>grant select,insert,update,delete,create,drop on vtdc.employee to joe@10.163.225.87 identified by ‘123′;给来自10.163.225.87的用户joe分配可对数据库vtdc的employee表进行select,insert,update,delete,create,drop等操作的权限,并设定口令为123。mysql>grant all privileges on vtdc.* to joe@10.163.225.87 identified by ‘123′;给来自10.163.225.87的用户joe分配可对数据库vtdc所有表进行所有操作的权限,并设定口令为123。mysql>grant all privileges on *.* to joe@10.163.225.87 identified by ‘123′;给来自10.163.225.87的用户joe分配可对所有数据库的所有表进行所有操作的权限,并设定口令为123。mysql>grant all privileges on *.* to joe@localhost identified by ‘123′;给本机用户joe分配可对所有数据库的所有表进行所有操作的权限,并设定口令为123。]]></content>
</entry>
<entry>
<title><![CDATA[Linux 文件同步工具——rsync]]></title>
<url>%2F2017%2FRsyncSkills%2F</url>
<content type="text"><![CDATA[介绍rsync是类unix系统下的数据镜像备份工具,从软件的命名上就可以看出来了——remote sync rsync服务器安装与搭建软件包安装123 # sudo apt-get install rsync 注:在debian、ubuntu 等在线安装方法; # yum install rsync 注:Fedora、Redhat 等在线安装方法; # rpm -ivh rsync 注:Fedora、Redhat 等rpm包安装方法; 其它Linux发行版,请用相应的软件包管理方法来安装。 源码包安装123 tar xvf rsync-xxx.tar.gz cd rsync-xxx ./configure --prefix=/usr ;make ;make install 注:在用源码包编译安装之前,您得安装gcc等编译工具才行; rsync 服务端配置 安装好 rsync 软件后,只需要创建配置好两个文件,然后开启 rsync 服务就可以了 服务器配置文件(/etc/rsyncd.conf),该文件默认不存在,请创建它。具体步骤如下 用root用户创建:1234#touch /etc/rsyncd.conf #创建rsyncd.conf,这是rsync服务器的配置文件。#touch /etc/rsyncd.secrets #创建rsyncd.secrets ,这是用户密码文件。这个密码文件很好的哦,你可以设置一个用户名:密码的数值对,然后在客户端设置一个密码文件,只写密码就可以了,一会细讲。#chmod 600 /etc/rsyncd.secrets #将rsyncd.secrets这个密码文件的文件属性设为root拥有, 且权限要设为600, 否则无法备份成功! 修改rsyncd.conf文件 rsyncd.conf是rsync服务器主要配置文件 比如我想要把远程机器上的文件,拉到这个服务器上的/home/root/tmp_hsw位置 123456789101112131415161718192021222324252627282930313233343536vi /etc/rsyncd.confpid file = /var/run/rsyncd.pidport = 873uid = rootgid = rootuse chroot = yesread only = no#limit access to private LANshosts allow= 10.10.25.14hosts deny=*max connections = 5#motd file = /etc/rsyncd.motd#This will give you a separate log filelog file = /var/log/rsync.log#This will log every file transferred - up to 85,000+ per user, per synctransfer logging = yeslog format = %t %a %m %f %bsyslog facility = local3timeout = 300[getcoolpaddata]path = /home/root/tmp_hsw/testRsynclist=yesignore errorsauth users = rootsecrets file = /etc/rsyncd.secretscomment = This is test for get coolpad data#exclude = easylife/ samba/ 注意: 关于auth users是必须在服务器上存在的真实的系统用户,如果你想用多个用户以,号隔开,比如auth users = easylife,root 解释rsyncd.conf文件配置项 在rsync 服务器中,全局定义有几个比较关健的,根据我们前面所给的配置文件 rsyncd.conf 文件 全局配置 pid file = /var/run/rsyncd.pid 注:告诉进程写到 /var/run/rsyncd.pid 文件中; port = 873 注:指定运行端口,默认是873,您可以自己指定; uid = root gid = root 注:服务器端传输文件时,要发哪个用户和用户组来执行,默认是nobody。 如果用nobody 用户和用户组,可能遇到权限问题,有些文件从服务器上拉不下来。所以我就偷懒,为了方便,用了root 。不过您可以在定义要同步的目录时定义的模块中指定用户来解决权限的问题。 use chroot = yes read only = yes 注:read only 是只读选择,也就是说,不让客户端上传文件到服务器上。还有一个 write only选项,自己尝试是做什么用的吧; hosts allow= 10.10.25.14 注: 允许访问的主机 IP max connections = 5 注:客户端最多连接数 log file = /var/log/rsync.log 注:rsync 服务器的日志; transfer logging = yes 注:这是传输文件的日志 log format = %t %a %m %f %b syslog facility = local3 timeout = 300 模块定义什么呢?主要是定义服务器哪个目录要被同步。每个模块都要以[name]形式。这个名字就是在rsync 客户端看到的名字,其实有点象Samba服务器提供的共享名。而服务器真正同步的数据是通过path 指定的。我们可以根据自己的需要,来指定多个模块。每个模块要指定认证用户,密码文件、并排除并不是必须的文件 模块定义 [getcoolpaddata] #模块它为我们提供了一个链接的名字,在本模块中链接到了/home/root/tmp_hsw/testRsync目录;要用[name] 形式 path = /home #指定文件目录所在位置,这是必须指定的 auth users = root #认证用户是root ,是必须在服务器上存在的用户 list=yes #list 意思是把rsync 服务器上提供同步数据的目录在服务器上模块是否显示列出来。默认是yes 。如果你不想列出来,就no ;如果是no是比较安全的,至少别人不知道你的服务器上提供了哪些目录。你自己知道就行了; ignore errors #忽略IO错误 secrets file = /etc/rsyncd.secrets #密码存在哪个文件 comment = linuxsir home data #注释可以自己定义 exclude = beinan/ samba/ # exclude是排除的意思,也就是说,要把/home目录下的easylife和samba排除在外; easylife/和samba/目录之间有空格分开 设定密码文件/etc/rsyncd.secrets密码文件格式很简单,rsyncd.secrets的内容格式为: 用户名:密码 比如:123vi /etc/rsyncd.secretsroot:rsy123456 修改权限12chown root.root rsyncd.secrets #修改属主chmod 600 rsyncd.secrets #修改权限 注意 1、将rsyncd.secrets这个密码文件的文件属性设为root拥有, 且权限要设为600, 否则无法备份成功! 出于安全目的,文件的属性必需是只有属主可读。 2、这里的密码值得注意,为了安全你不能把系统用户的密码写在这里。比如你的系统用户root密码是 helloworld,为了安全你可以让rsync中的rsyncd.secrets文件中的密码为为123456。这样,在客户端连接的时候,也是用这个123456会使用这个rsyncd.secrets作为认证文件,不和os有任何牵连。 配置客户端 上面两个配置文件都是在启动 rsycn的服务器端,然而在我们的客户端,我们安装了 rsync后,只需要创建一个密码文件,里面放上服务器上定义的密码即可了 12root: vi /etc/rsyncd.passwdrsy123456 启动rsync服务器及防火墙的设置 启动rsync服务器相当简单 123--daemon参数方式,是让rsync以服务器模式运行# /usr/bin/rsync --daemon --config=/etc/rsyncd/rsyncd.conf #--config用于指定rsyncd.conf的位置,如果在/etc下可以不写 简单使用把客户端/home/root/tmp_hsw 路径下的文件同步到服务器下.路径为 path 里面定义的 1rsync -avz --progress --password-file=/etc/rsyncd.passwd --delete /home/root/tmp_hsw root@10.10.25.13::getcoolpaddata/ 总结1.在客户端的时候,你想用客户端的哪个用户传输数据,那么客户端上面的/etc/rsyncd.passwd文件就得是这个用户文件,并且权限是6002.都使用 root 用户肯定是没有问题的3.在服务端的时候,如果不想用 root 用户.那么那两个配置文件的权限都得是 root 用户,然后再 rsync.conf 里面把 uid,cid,auth users改成普通用户即可. 参考配置过程参考文章:http://blog.csdn.net/cymm_liu/article/details/31347531]]></content>
</entry>
<entry>
<title><![CDATA[Shell Skills]]></title>
<url>%2F2017%2FShellSkills%2F</url>
<content type="text"><![CDATA[写 shell 脚本两三技巧 Shell脚本编程30分钟入门https://github.com/qinjx/30min_guides/blob/master/shell.md 获取脚本的所处的当前路径 1DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 解释 示例: 12345678910111213#DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"S0="${BASH_SOURCE[0]}"DIRNAME="$( dirname "$S0")"DIR="$( cd "$DIRNAME" && pwd)"echo "$S0"echo "$DIRNAME"echo "$DIR" 输出: 123./ss.sh./Users/hushiwei/Downloads 解释: S0是脚本相对于当前目录的路径,是相对路径 再用dirname命令得到脚本的目录名,也是相对路径 用cd命令切换到脚本所在目录,再执行pwd命令, 就得到了脚本所在目录的绝对路径 后台运行脚本,输出日志12nohup ./daily.sh > data.log 2>&1 & 给一个起始时间一个结束时间,按时间进行遍历1234567891011121314151617#! /bin/shdate=`date -d "+0 day $1" +%Y%m%d`enddate=`date -d "+1 day $2" +%Y%m%d`echo "------------------------------"echo "date=$date"echo "enddate=$enddate"echo "------------------------------"while [[ $date < $enddate ]]do echo $date date=`date -d "+1 day $date" +%Y%m%d`done执行: ./dotime.sh 20170101 20170130 文本统计查询1234567wordcount--以\001分隔,取第一个字段进行统计cat merge | awk -F '\\\001' '{print $1}' | sort | uniq -c >merge_imei_num.logcat merge_imei_num.log | sort -r | more按照文件里面的每一行的长度进行排序,并且打印出第一个字段cat merge | awk -F '\\\001' '{print length($0)" "$1}' | sort -nr >sortmerge.log`]]></content>
</entry>
<entry>
<title><![CDATA[Zero-Copy&SendFile浅析]]></title>
<url>%2F2017%2FSendFileZeroCopy%2F</url>
<content type="text"><![CDATA[为了减少不必要的文件内存拷贝和数据传输.直接在系统内核层进行数据传输会大大的提高速度. 比如消息传输框架kafka,为了提高消息的传输性能,就是用到了 sendFile 机制. 要深入了解 zerocopy和 sendfile,可以参考一些博客 http://blog.csdn.net/oanqoanq/article/details/50865683]]></content>
</entry>
<entry>
<title><![CDATA[二进制加法与逻辑门的关系]]></title>
<url>%2F2017%2FBinaryAddition%2F</url>
<content type="text"><![CDATA[如何实现二进制加法,只需要简单的逻辑门就行 二进制相加 二进制相加将会产生一个加法位和一个进位位 二进制的加法位 +加和 0 1 0 0 1 1 1 0 二进制的进位位 +进位 0 1 0 0 0 1 0 1 那么如何用逻辑门来实现这个结果呢? 正好可以用异或门和与门分别来实现这两个结果 异或门 XOR 0 1 0 0 1 1 1 0 与门 AND 0 1 0 0 0 1 0 1 结果1.所以可以用两个逻辑门分别求出二进制的加法了. 2.这里只是简单的说明二进制计算与逻辑门的关系.如果有兴趣可以看看<<编码:隐匿在计算机软硬件背后的语言>> 3.确实是一本很棒的书. 补充异或门是什么?异或门的组成,如图所示: 表格显示更直观]]></content>
</entry>
<entry>
<title><![CDATA[用hexo搭建博客]]></title>
<url>%2F2017%2FHexoBlog%2F</url>
<content type="text"><![CDATA[总结安装好 hexo 后,用 Markdown 写好文章后,设置好部署模式.会把内容编译好,上传到 GitHub, 进行博客的渲染.主题在 theme 文件夹下.可以随意更改.如果要加页面,并且页面是分类页面还是标签等等,直接到文章中进行设置即可.好好看看这个主题的设置http://theme-next.iissnan.com/theme-settings.html好好看看hexo https://hexo.io/zh-cn/docs/ 发布文章的步骤: 1、hexo new 创建文章 2、Markdown语法编辑文章 3、hexo new page categories 新建页面 4、部署(所有打开bash都是在blog目录下)1234hexo clean #清除缓存 网页正常情况下可以忽略此条命令hexo generate #生成hexo server #启动服务预览,非必要,可本地浏览网页hexo deploy #部署发布 简写Tips:12345hexo n "我的博客" == hexo new "我的博客" #新建文章hexo p == hexo publishhexo g == hexo generate#生成hexo s == hexo server #启动服务预览hexo d == hexo deploy#部署 安装 hexomac电脑直接用下面的命令就可以很简单的安装成功1234567891011121314# 安装mac下的神器,brew包管理器ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"# 更新 brewbrew update# 先用brew安装nodebrew install node# 然后用node安装hexo就行了npm install -g hexo-cli# 验证成功与否# 输入 hexo 有提示就成了 为了后面用npm下载依赖更快速,这里建议把npm的镜像改成淘宝源,可以使用下面的几种方法来修改npm镜像源 1.临时使用1npm --registry https://registry.npm.taobao.org install express 2.持久使用12345npm config set registry https://registry.npm.taobao.org# 配置后可通过下面方式来验证是否成功npm config get registry# 或npm info express 3.通过cnpm使用123npm install -g cnpm --registry=https://registry.npm.taobao.org# 使用cnpm install express 初始化博客12345678// 建立一个博客文件夹,并初始化博客,<folder>为文件夹的名称,可以随便起名字$ hexo init <folder>成功提示:INFO Start blogging with Hexo!// 进入博客文件夹,<folder>为文件夹的名称$ cd <folder>// node.js的命令,根据博客既定的dependencies配置安装所有的依赖包$ npm install 执行完 hexo init test/ 命令后, test 文件下出现了这些1234567891011hushiweideMacBook-Pro:test hushiwei$ lltotal 24drwxr-xr-x 9 hushiwei staff 306 3 2 10:05 ./drwxr-xr-x 3 hushiwei staff 102 3 2 09:53 ../-rw-r--r-- 1 hushiwei staff 65 3 2 10:00 .gitignore-rw-r--r-- 1 hushiwei staff 1483 3 2 10:00 _config.ymldrwxr-xr-x 287 hushiwei staff 9758 3 2 10:06 node_modules/-rw-r--r-- 1 hushiwei staff 443 3 2 10:00 package.jsondrwxr-xr-x 5 hushiwei staff 170 3 2 10:00 scaffolds/drwxr-xr-x 3 hushiwei staff 102 3 2 10:00 source/drwxr-xr-x 3 hushiwei staff 102 3 2 10:00 themes/ 因为你初始化hexo 之后source目录下自带一篇hello world文章, 所以直接执行下方命令1$ hexo generate 启动本地服务器1$ hexo server 在浏览器输入 http://localhost:4000/就可以看见网页和模板了12INFO Start processingINFO Hexo is running at http://localhost:4000/. Press Ctrl+C to stop. 访问http://localhost:4000/,便可以看到网站初步的模样,不要激动,我们还要把网页发布到Github上去。 我们只需要在 配置文件中把 deploy 改成这样就行了.repo 这个 GitHub 的仓库我们需要提前在 GitHub 上建好.仓库名为:Timehsw.github.io(Timehsw为我的 GitHub 用户名)然后设置 ssh 免密码登录即可.不需要再 GitHub 上配置其他的什么东西了.真的非常简单.1234deploy: type: git repo: https://github.com/Timehsw/Timehsw.github.io.git branch: master 部署到github上后,如何找到自己的博客地址?部署到github上后,打开自己这个项目,然后找到 setting 选项卡.拉到Github Pages,这里会显示出你的博客地址.我因为是做了域名映射,所以这里显示的是我的域名.注意: 为了让你的博客地址短小好看,请务必在新建这个项目的时候,取项目的名字为你的github用户名加上.github.io 就像我这样取就行了 搭建一个可以显示数学公式的hexo博客 利用MathJax来渲染LaTeX数学公式 安装方式也很简单,在博客文件夹下执行12npm install hexo-math --savehexo math install 然后写博客的时候就可以用上了如果不会用MathJax写公式,可以借助于这个网站,非常方便在线数学公式编辑器 示例看图片所示 配置博客基于上一步,我们对博客修改相应的配置,我们用到_config.yml文件,下面是该文件的默认参数信息:12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970# Hexo Configuration## Docs: http://hexo.io/docs/configuration.html## Source: https://github.com/hexojs/hexo/# Site #站点信息title: #标题subtitle: #副标题description: #站点描述,给搜索引擎看的author: #作者email: #电子邮箱language: zh-CN #语言# URL #链接格式url: #网址root: / #根目录permalink: :year/:month/:day/:title/ #文章的链接格式tag_dir: tags #标签目录archive_dir: archives #存档目录category_dir: categories #分类目录code_dir: downloads/codepermalink_defaults:# Directory #目录source_dir: source #源文件目录public_dir: public #生成的网页文件目录# Writing #写作new_post_name: :title.md #新文章标题default_layout: post #默认的模板,包括 post、page、photo、draft(文章、页面、照片、草稿)titlecase: false #标题转换成大写external_link: true #在新选项卡中打开连接filename_case: 0render_drafts: falsepost_asset_folder: falserelative_link: falsehighlight: #语法高亮 enable: true #是否启用 line_number: true #显示行号 tab_replace:# Category & Tag #分类和标签default_category: uncategorized #默认分类category_map:tag_map:# Archives2: 开启分页1: 禁用分页0: 全部禁用archive: 2category: 2tag: 2# Server #本地服务器port: 4000 #端口号server_ip: localhost #IP 地址logger: falselogger_format: dev# Date / Time format #日期时间格式date_format: YYYY-MM-DD #参考http://momentjs.com/docs/#/displaying/format/time_format: H:mm:ss# Pagination #分页per_page: 10 #每页文章数,设置成 0 禁用分页pagination_dir: page# Disqus #Disqus评论,替换为多说disqus_shortname:# Extensions #拓展插件theme: landscape-plus #主题exclude_generator:plugins: #插件,例如生成 RSS 和站点地图的- hexo-generator-feed- hexo-generator-sitemap# Deployment #部署,将 lmintlcx 改成用户名deploy: type: git repo: 刚刚github创库地址.git branch: master 注意特别提醒,在每个参数的:后都要加一个空格 修改网站相关信息 123456title: Time渐行渐远subtitle:Coding Changing The Worlddescription: 时间渐行渐远author: Hushiweilanguage: zh-CNtimezone: Asia/Shanghai 配置部署(修改成自己的)1234deploy: type: git repo: https://github.com/Timehsw/Timehsw.github.io.git branch: master 增加站内搜索Local Search安装 hexo-generator-searchdb,在站点的根目录下执行以下命令:1$ npm install hexo-generator-searchdb --save 编辑 站点配置文件,新增以下内容到任意位置:12345search: path: search.xml field: post format: html limit: 10000 编辑 主题配置文件,启用本地搜索功能:123# Local searchlocal_search: enable: true 绑定自己的域名(可选)首先获取 gitpage 的 ip 地址12345678hushiweideMacBook-Pro:myblog hushiwei$ ping timehsw.github.ioPING github.map.fastly.net (151.101.100.133): 56 data bytes64 bytes from 151.101.100.133: icmp_seq=0 ttl=52 time=63.278 ms64 bytes from 151.101.100.133: icmp_seq=1 ttl=52 time=63.262 ms64 bytes from 151.101.100.133: icmp_seq=2 ttl=52 time=64.162 ms64 bytes from 151.101.100.133: icmp_seq=3 ttl=52 time=64.281 ms^C--- github.map.fastly.net ping statistics --- 注意: 这里ip可能会变,多ping几次,发现ip不变后,再进行下面的域名和ip的映射. 设置域名与 ip 的解析如图所示,增加两条即可,除了 ip 地址写成自己的,其他的都一样. 增加 CNAME在 hexo 博客的 source 文件夹下新建一个名为CNAME的文件,无后缀名.内容为你的域名.即可 问题 部署的时候出现 12hushiweideMacBook-Pro:test hushiwei$ hexo deployERROR Deployer not found: git 解决办法 12npm install hexo-deployer-git --save重新 deploy 即可 阅读全文 1<!-- more --> 用这个手动截断更合适]]></content>
</entry>
<entry>
<title><![CDATA[学习摩尔斯码]]></title>
<url>%2F2017%2Fmrs%2F</url>
<content type="text"><![CDATA[摩尔斯码由点和划组成,也就是2个符号.这两个符号编码可表示一切. 摩尔斯表 26字母 快速记忆方法 跟着表发送字母还是很方便,关键是解码的时候,如何有效的进行解码 图形 树形 用树形方法进行解码毫无疑问非常有效的了]]></content>
</entry>
<entry>
<title><![CDATA[17年阅读书单]]></title>
<url>%2F2017%2FReadBooks33%2F</url>
<content type="text"><![CDATA[书单很多,思考却不多.我们总是以为有了书单就有了知识一般.静下心来,好好看书吧! 优秀的程序员应该具备两方面能力 良好的程序设计能力: 掌握常用的数据结构和算法(例如链表,栈,堆,队列,排序和散列); 理解计算机科学的核心概念(例如计算机系统结构、操作系统、编译原理和计算机网络); 熟悉至少两门以上编程语言(例如C++,Java,C#,和Python); 专业的软件开发素养: 具备良好的编程实践,能够编写可测试(Testable),可扩展(Extensible),可维护(Maintainable)的代码; 把握客户需求,按时交付客户所需要的软件产品; 理解现代软件开发过程中的核心概念(例如面向对象程序设计,测试驱动开发,持续集成,和持续交付等等)。 入门书籍程序设计 基础理论:编码:隐匿在计算机软硬件背后的语言 编程语言: Java:Java核心技术(第9版) Python:Python基础教程(第二版) 编程语言理论:编程语言实现模式 程序设计:程序设计方法 算法与数据结构:算法(第4版) 程序调试:调试九法——软硬件错误的排查之道 软件开发 编程实践:程序设计实践 面向对象程序设计:Head First设计模式 重构:重构 软件测试:How to Break Software 项目管理:极客与团队 专业开发:程序员修炼之道:从小工到专家 大师之言:奇思妙想:15位计算机天才及其重大发现 界面设计:写给大家看的设计书 交互设计:通用设计法则 个人成长 职业规划:软件开发者路线图 思维方式:程序员的思维修炼:开发认知潜能的九堂课 求职面试:金领简历:敲开苹果微软谷歌的大门 英语写作:The Only Grammar Book You’ll Ever Need 必读书籍程序设计 基础理论:深入理解计算机系统(第2版) 编程语言: Java:Effective Java(第2版) Python:Python参考手册(第4版) 编程语言理论:程序设计语言——实践之路(第3版) 程序设计:计算机程序的构造与解释(第2版) 算法与数据结构:编程珠玑(第2版) 程序调试:调试九法——软硬件错误的排查之道 软件开发 编程实践:代码大全(第2版) 面向对象程序设计:设计模式 重构:修改代码的艺术 软件测试:xUnit Test Patterns 项目管理:人月神话 专业开发:程序员职业素养 大师之言:编程人生:15位软件先驱访谈录 界面设计:认知与设计:理解UI设计准则(第2版) 交互设计:交互设计精髓(第3版) 个人成长 职业规划:软件开发者路线图 思维方式:如何把事情做到最好 求职面试:程序员面试金典(第5版) 英语写作:风格的要素 最后 正是有前辈们无私的分享自己的经验,我们才能少走弯路,思考自己的职业生涯. 如何成为一个优秀的程序员而不是码农 基础理论包括了程序员应该掌握的计算机基础知识; 编程语言对软件开发至关重要,我选择了C,C++,Java,C#,Python,和JavaScript这六门主流编程语言进行介绍,如果想进一步理解编程语言,可以阅读编程语言理论里的书目; 在理解编程语言的基础上,优秀的程序员还应该了解各种程序设计技巧,熟悉基本的算法数据结构,并且能够高效的进行程序调试。 良好的程序设计能力是成为优秀程序员的前提,但软件开发知识也是必不可少的:优秀的程序员应具备良好的编程实践,知道如何利用面向对象,重构,和软件测试编写可复用,可扩展,可维护的代码,并具备软件项目管理知识和专业开发素养; 就像我们可以从名人传记里学习名人的成功经验,程序员也可以通过追随优秀程序员的足迹使自己少走弯路。大师之言包含一系列对大师程序员/计算机科学家的访谈,任何程序员都可以从中获益良多; 为了打造用户满意的软件产品,程序员应当掌握一定的界面设计知识和交互设计知识(是的,这些工作应该交给UI和UX,但如果你想独自打造一个产品呢?); 专业程序员应当对自己进行职业规划,并熟悉程序员求职面试的流程,以便在职业道路上越走越远; 软件开发是一项需要不断学习的技能,学习思维方式可以有效的提升学习能力和学习效率; 软件开发是一项国际化的工作,为了让更多的人了解你的代码(工作),良好的英语写作能力必不可少。 谢谢Lucida 正是看了Lucida大神的博客,我才更清楚接下来如何更好的提高自己的能力.让自己成为一名优秀的程序员而不是一名普通的码农. 书单和内容是Lucida博客里面的程序员必读书单 1.0里的,这里我摘出来作为我接下来的书单. 虽不认识Lucida大神,但真心谢谢你的无私分享.]]></content>
</entry>
<entry>
<title><![CDATA[IntellijIdea使用笔记]]></title>
<url>%2F2017%2FIntellijSkills%2F</url>
<content type="text"><![CDATA[给IDEA设置用户名其中${USER}将会被替换成当前登录操作系统的用户名,但有时我们的计算机名有可能是Administrator这样一类的,那应该怎样去修改这个 ${USER}呢?答案在于IntelliJ IDEA的启动配置idea.exe.vmoptions,打开此文件,在最后加上一句:-Duser.name=HuShiwei这样就可以不使用默认的登录名了。 修改IntelljIDEA的背景色为护眼色 将方法加入到Favorites1alt+shift+f 全局搜索内容 快捷键 描述 shift+shift 搜索所有 ctrl+shift+f 搜索内容 Ctrl+Shift+A 搜索命令 书签功能 快捷键 描述 Ctrl + F11 选中文件 / 文件夹,使用助记符设定 / 取消书签 F11 添加书签 Shift + F11 弹出书签显示层 Ctrl + 1,2,3…9 定位到对应数值的书签位置 Ctrl + Shift + 1,2,3…9 快速添加指定数值的书签 翻译插件 安装 离线下载地址 在线安装:Preferences(Settings) > Plugins > Browse repositories… > 搜索并找到”Translation” >Install Plugin 重启 使用 Translation: Alt + 0 打开翻译对话框,默认显示在工具栏上。 Translate: Alt + 1 取词并翻译。 Translate(Inclusive): Alt + 2 取词并翻译。以最大范围自动取词,忽略手动选择的文本。 Translate(Exclusive): Alt + 3 取词并翻译。自动取单个词,忽略手动选择的文本 注意 设置有道 API KEY: 由于有道的 API 在查询请求数量上存在限制,如果在 1 小时内查询请求次数达到一定数量后将会暂时禁止查询一段时间(大概 1 小时)。如果很多人同时使用同一个 KEY,可能会很容易就达到了限制条件,这就可以通过使用自己的 KEY 来避免(一人一个 KEY 基本足够用了)。 获取Preferences(Settings) > Other Settings > Translation > 获取有道 API KEY 申请地址 激活1http://idea.iteblog.com/key.php IDEA中编译打包Scala和Java代码命令原因是mvn clean package默认只处理java源代码的编译、打包,而不管scala,所以编译时遇到Hello这个由scala语言编写的class,此时scala还没编译生成class,所以找不到相应的调用入口。解决办法: 1mvn clean scala:compile compile package 如上,在compile前加入scala:compile,这是maven-scala-plugin插件提供的选项,表示编译scala,这样一来,先编译scala,再编译java,最后打包,妥妥滴!]]></content>
</entry>
<entry>
<title><![CDATA[Intellij快捷键 for mac]]></title>
<url>%2F2017%2FIntellijIdeaForMac%2F</url>
<content type="text"><![CDATA[Mac键盘符号和修饰键说明 ⌘ Command ⇧ Shift ⌥ Option ⌃ Control ↩︎ Return/Enter ⌫ Delete ⌦ 向前删除键(Fn+Delete) ↑ 上箭头 ↓ 下箭头 ← 左箭头 → 右箭头 ⇞ Page Up(Fn+↑) ⇟ Page Down(Fn+↓) Home Fn + ← End Fn + → ⇥ 右制表符(Tab键) ⇤ 左制表符(Shift+Tab) ⎋ Escape (Esc)一、Editing(编辑) ⌃Space 基本的代码补全(补全任何类、方法、变量) ⌃⇧Space 智能代码补全(过滤器方法列表和变量的预期类型) ⌘⇧↩ 自动结束代码,行末自动添加分号 ⌘P 显示方法的参数信息 ⌃J, Mid. button click 快速查看文档 ⇧F1 查看外部文档(在某些代码上会触发打开浏览器显示相关文档) ⌘+鼠标放在代码上 显示代码简要信息 ⌘F1 在错误或警告处显示具体描述信息 ⌘N, ⌃↩, ⌃N 生成代码(getter、setter、构造函数、hashCode/equals,toString) ⌃O 覆盖方法(重写父类方法) ⌃I 实现方法(实现接口中的方法) ⌘⌥T 包围代码(使用if..else, try..catch, for, synchronized等包围选中的代码) ⌘/ 注释/取消注释与行注释 ⌘⌥/ 注释/取消注释与块注释 ⌥↑ 连续选中代码块 ⌥↓ 减少当前选中的代码块 ⌃⇧Q 显示上下文信息 ⌥↩ 显示意向动作和快速修复代码 ⌘⌥L 格式化代码 ⌃⌥O 优化import ⌃⌥I 自动缩进线 ⇥ / ⇧⇥ 缩进代码 / 反缩进代码 ⌘X 剪切当前行或选定的块到剪贴板 ⌘C 复制当前行或选定的块到剪贴板 ⌘V 从剪贴板粘贴 ⌘⇧V 从最近的缓冲区粘贴 ⌘D 复制当前行或选定的块 ⌘⌫ 删除当前行或选定的块的行 ⌃⇧J 智能的将代码拼接成一行 ⌘↩ 智能的拆分拼接的行 ⇧↩ 开始新的一行 ⌘⇧U 大小写切换 ⌘⇧] / ⌘⇧[ 选择直到代码块结束/开始 ⌥⌦ 删除到单词的末尾(⌦键为Fn+Delete) ⌥⌫ 删除到单词的开头 ⌘+ / ⌘- 展开 / 折叠代码块 ⌘⇧+ 展开所以代码块 ⌘⇧- 折叠所有代码块 ⌘W 关闭活动的编辑器选项卡二、Search/Replace(查询/替换) Double ⇧ 查询任何东西 ⌘F 文件内查找 ⌘G 查找模式下,向下查找 ⌘⇧G 查找模式下,向上查找 ⌘R 文件内替换 ⌘⇧F 全局查找(根据路径) ⌘⇧R 全局替换(根据路径) ⌘⇧S 查询结构(Ultimate Edition 版专用,需要在Keymap中设置) ⌘⇧M 替换结构(Ultimate Edition 版专用,需要在Keymap中设置)三、Usage Search(使用查询) ⌥F7 / ⌘F7 在文件中查找用法 / 在类中查找用法 ⌘⇧F7 在文件中突出显示的用法 ⌘⌥F7 显示用法四、Compile and Run(编译和运行) ⌘F9 编译Project ⌘⇧F9 编译选择的文件、包或模块 ⌃⌥R 弹出 Run 的可选择菜单 ⌃⌥D 弹出 Debug 的可选择菜单 ⌃R 运行 ⌃D 调试 ⌃⇧R, ⌃⇧D 从编辑器运行上下文环境配置五、Debugging(调试) F8 进入下一步,如果当前行断点是一个方法,则不进入当前方法体内 F7 进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则不会进入该内嵌的方法中 ⇧F7 智能步入,断点所在行上有多个方法调用,会弹出进入哪个方法 ⇧F8 跳出 ⌥F9 运行到光标处,如果光标前有其他断点会进入到该断点 ⌥F8 计算表达式(可以更改变量值使其生效) ⌘⌥R 恢复程序运行,如果该断点下面代码还有断点则停在下一个断点上 ⌘F8 切换断点(若光标当前行有断点则取消断点,没有则加上断点) ⌘⇧F8 查看断点信息六、Navigation(导航) ⌘O 查找类文件 ⌘⇧O 查找所有类型文件、打开文件、打开目录,打开目录需要在输入的内容前面或后面加一个反斜杠/ ⌘⌥O 前往指定的变量 / 方法 ⌃← / ⌃→ 左右切换打开的编辑tab页 F12 返回到前一个工具窗口 ⎋ 从工具窗口进入代码文件窗口 ⇧⎋ 隐藏当前或最后一个活动的窗口,且光标进入代码文件窗口 ⌘⇧F4 关闭活动run/messages/find/… tab ⌘L 在当前文件跳转到某一行的指定处 ⌘E 显示最近打开的文件记录列表 ⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方 ⌘⇧⌫ 跳转到最后一个编辑的地方 ⌥F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择(如在代码编辑窗口可以选择显示该文件的Finder) ⌘B / ⌘ 鼠标点击 进入光标所在的方法/变量的接口或是定义处 ⌘⌥B 跳转到实现处,在某个调用的方法名上使用会跳到具体的实现处,可以跳过接口 ⌥ Space, ⌘Y 快速打开光标所在方法、类的定义 ⌃⇧B 跳转到类型声明处 ⌘U 前往当前光标所在方法的父类的方法 / 接口定义 ⌃↓ / ⌃↑ 当前光标跳转到当前文件的前一个/后一个方法名位置 ⌘] / ⌘[ 移动光标到当前所在代码的花括号开始/结束位置 ⌘F12 弹出当前文件结构层,可以在弹出的层上直接输入进行筛选(可用于搜索类中的方法) ⌃H 显示当前类的层次结构 ⌘⇧H 显示方法层次结构 ⌃⌥H 显示调用层次结构 F2 / ⇧F2 跳转到下一个/上一个突出错误或警告的位置 F4 / ⌘↓ 编辑/查看代码源 ⌥ Home 显示到当前文件的导航条 F3选中文件/文件夹/代码行,添加/取消书签 ⌥F3 选中文件/文件夹/代码行,使用助记符添加/取消书签 ⌃0…⌃9 定位到对应数值的书签位置 ⌘F3 显示所有书签七、Refactoring(重构) F5 复制文件到指定目录 F6 移动文件到指定目录 ⌘⌫ 在文件上为安全删除文件,弹出确认框 ⇧F6 重命名文件 ⌘F6 更改签名 ⌘⌥N 一致性 ⌘⌥M 将选中的代码提取为方法 ⌘⌥V 提取变量 ⌘⌥F 提取字段 ⌘⌥C 提取常量 ⌘⌥P 提取参数八、VCS/Local History(版本控制/本地历史记录) ⌘K 提交代码到版本控制器 ⌘T 从版本控制器更新代码 ⌥⇧C 查看最近的变更记录 ⌃C 快速弹出版本控制器操作面板九、Live Templates(动态代码模板) ⌘⌥J 弹出模板选择窗口,将选定的代码使用动态模板包住 ⌘J 插入自定义动态代码模板十、General(通用) ⌘1…⌘9 打开相应编号的工具窗口 ⌘S 保存所有 ⌘⌥Y 同步、刷新 ⌃⌘F 切换全屏模式 ⌘⇧F12 切换最大化编辑器 ⌥⇧F 添加到收藏夹 ⌥⇧I 检查当前文件与当前的配置文件 §⌃, ⌃` 快速切换当前的scheme(切换主题、代码样式等) ⌘, 打开IDEA系统设置 ⌘; 打开项目结构对话框 ⇧⌘A 查找动作(可设置相关选项) ⌃⇥ 编辑窗口标签和工具窗口之间切换(如果在切换的过程加按上delete,则是关闭对应选中的窗口)十一、Other(一些官方文档上没有体现的快捷键) ⌘⇧8 竖编辑模式 导航 ⌘O 查找类文件 Ctrl + N ⌘⌥O 前往指定的变量 / 方法 Ctrl + Shift + Alt + N ⌃← / ⌃→ 左右切换打开的编辑tab页 Alt← / Alt→ ⎋ 从工具窗口进入代码文件窗口 ESC ⌘L 在当前文件跳转到某一行的指定处 Ctrl + G ⌘E 显示最近打开的文件记录列表 Ctrl + E ⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方 Ctrl + Alt + ← Ctrl + Alt + → ⌘⇧⌫ 跳转到最后一个编辑的地方 ⌃H 显示当前类的层次结构 Ctrl + H ⌘⇧H 显示方法层次结构 ⌃⌥H 显示调用层次结构 F4 / ⌘↓ 编辑/查看代码源 ⌘⌥U 显示类UML图 ⌃J 查看注释编辑 ⌥⌦ 删除到单词的末尾(⌦键为Fn+Delete) ⌥⌫ 删除到单词的开头 ⌘+ / ⌘- 展开 / 折叠代码块 ⌘F1 在错误或警告处显示具体描述信息 ⌘⌥L 格式化代码 ⌃⌥O 优化import ⇧↩ 开始新的一行 ⌘⇧↩ 自动结束代码,行末自动添加分号 ⌃I 实现方法(实现接口中的方法) ⇧F6 重命名文件或者变量 ⌘N, ⌃↩, ⌃N 生成代码(getter、setter、构造函数、hashCode/equals,toString) ⌘P 显示方法的参数信息查找 Double⇧ 查找任何东西 ⌘⇧F 全局查找(根据路径) ⌘F 文件内查找 ⌘G 查找模式下,向下查找 ⌘⇧G 查找模式下,向上查找导航 ⌘⌥B 跳转到接口的实现 ⌘U 查看接口定义 ⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方 ⌘B / ⌘ 鼠标点击 进入光标所在的方法/变量的接口或是定义处 ⌃⇧B 跳转到类型声明处 ⌥ Space, ⌘Y 快速打开光标所在方法、类的定义 ⌘O 查找类文件 ⌘⇧O 查找所有类型文件、打开文件、打开目录,打开目录需要在输入的内容前面或后面加一个反斜杠/ F12 返回到前一个工具窗口 ⎋ 从工具窗口进入代码文件窗口 ⇧⎋ 隐藏当前或最后一个活动的窗口,且光标进入代码文件窗口 F3选中文件/文件夹/代码行,添加/取消书签 ⌥F3 选中文件/文件夹/代码行,使用助记符添加/取消书签 ⌃0…⌃9 定位到对应数值的书签位置 ⌘F3 显示所有书签 ⌥F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择(如在代码编辑窗口可以选择显示该文件的Finder) ⌘F12 弹出当前文件结构层,可以在弹出的层上直接输入进行筛选(可用于搜索类中的方法)通用 ⌃⌘F 切换全屏模式]]></content>
</entry>
<entry>
<title><![CDATA[Macbook下搭建开发环境]]></title>
<url>%2F2017%2FMacbookEnr%2F</url>
<content type="text"><![CDATA[切换root账号1234567macbook pro启用root的方法用管理员帐号进入shell: 1) sudo passwd root 2) 输入新的root密码。 3) su 4) 使用新密码 这样就进入到root帐号了 添加安装任何来源的文件安装破解软件的时候,需要设置成-允许从任何来源,不然会出现xxx.app已损坏,打不开.你应该将它移到废纸篓 1sudo spctl --master-disable 安装brew包管理器123ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"更新 brewbrew update 安装brew cask1ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" < /dev/null 2> /dev/null ; brew install caskroom/cask/brew-cask 2> /dev/null 安装reds可视化客户端1brew cask install rdm 重新安装 pythonPython会被安装到/usr/local/bin目录下1brew install python 然后安装 ipython1sudo pip install ipython 然后安装 jupyter notebook执行ipynb文件,好东西.1234# 安装sudo pip install jupyter# 运行jupyter notebook 安装 wget1brew install wget 安装 node1brew install node 安装hexo 下载 1npm install -g hexo-cli 验证 1输入 hexo 有提示就成了 配置 GitHub123456重新打开bash,输入:ssh-keygen -t rsa -C "Github的注册邮箱地址"一路Enter过来就好,得到信息:Your public key has been saved in /Users/hushiwei/.ssh找到该文件,打开(sublime text),Ctrl + a复制里面的所有内容,然后进入Sign in to GitHub:New SSH key ——Title:blog —— Key:输入刚才复制的—— Add SSH key 安装pip(如果重新用 brew 安装了 python,那么 pip 已经安装了)12wget https://bootstrap.pypa.io/get-pip.pysudo python get-pip.py 安装jdk配置scala,maven,环境变量 maven和Scala 123.tgz解压:tar zxvf FileName.tgz压缩:tar zcvf FileName.tgz FileName 环境变量 12345678hushiweideMacBook-Pro:Downloads hushiwei$ more .bash_profilealias ll='ls -lF'alias ll='ls -alF'JAVA_HOME=`/usr/libexec/java_home`SCALA_HOME=/Users/hushiwei/devApps/scala-2.10.5MAVEN_HOME=/Users/hushiwei/devApps/maven-3.3.9CLASSPAHT=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jarPATH=$SCALA_HOME/bin:$MAVEN_HOME/bin:$JAVA_HOME/bin:$PATH:/usr/local/bin: 安装MySQL 下载下载地址]]></content>
</entry>
<entry>
<title><![CDATA[Docker 笔记]]></title>
<url>%2F2017%2FDockerSkils%2F</url>
<content type="text"><![CDATA[ubuntu安装docker前提条件 Docker 要求 Ubuntu 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的Ubuntu版本是否支持 Docker 。打开控制台使用 uname -r命令来查看你当前的内核版本。12$ uname -r 3.11.0-15-generic 安装步骤1234sudo apt-get updatesudo apt-get update $ sudo apt-get install wget //安装wgetwget -qO- https://get.docker.com/ | sh //获取最新版本的 Docker 安装包,输入完成之后,就会下载脚本并且安装Docker及依赖包。docker info //验证安装结果 问题Cannot connect to the Docker daemon. Is the docker daemon running on this host? 解决:1234sudo su - //切换到rootservice docker start //启动docker servicedocker images //显示所有imagesdocker run hello-world //重新运行 恩,是权限问题,当前用户没权限,root用户可以运行 图形化镜像docker-ubuntu-vnc-desktop From Docker Indexdocker pull dorowu/ubuntu-desktop-lxde-vnc Build yourselfgit clone https://github.com/fcwu/docker-ubuntu-vnc-desktop.gitdocker build –rm -t dorowu/ubuntu-desktop-lxde-vnc docker-ubuntu-vnc-desktop Rundocker run -i -t -p 6080:6080 dorowu/ubuntu-desktop-lxde-vncBrowse http://127.0.0.1:6080/vnc.html docker 常用命令常用 命令 描述 docker images 列出所有的镜像 docker search name 搜索docker 容器,name是名字 docker pull name 下载容器,name是名字 docker run -i -t name /bin/bash 运行name镜像 docker exec -i -t name/bin/bash 进入容器中 docker rmi XXXXXX 删除一个docker镜像 docker rm xxxxxx 删除一个docker容器 docker ps 列出正在运行的(容器)containers docker ps -a 查看容器状态 docker commit 3a09b2588478 ubuntu:mynewimage 把容器提交为一个镜像保存状态 获取 Ubuntu 镜像。 docker pull ubuntu完成后执行 docker images 就能看到一个刚刚更新的镜像了。 进入容器 docker run -it ubuntu 安装软件、配置环境变量 首先更新apt-get apt-get update 接下来就可以使用 apt-get install * 安装你需要的软件了,如果没有就下载安装包自行安装,同时配置好环境变量,这里就不赘述了。 启动服务进入tomcat目录,启动服务,在浏览器打开 0.0.0.0:8080, 如果没有错的话你会看到该服务器无法访问。这是因为我们刚才启动的服务是在docker内,如果不做一些操作的话我们是无法访问到docker内部的服务的。 退出容器所以,我们先退出容器 exit 退出之后执行 docker ps -a就能看到我们刚才的容器依然还在,可能大多刚接触docker的人都会犯这个错误,以为退出容器之后容器就销毁了,其实不然。 再次进入容器如果我们想再进入这个容器可以执行下面的命令,容器ID请复制自己的。 docker exec -it 容器ID bash 持久化容器虽然容器还在运行,但是他并没有持久化,为了防止万一,在我们修改容器里面的内容之后尽快持久化。 docker commit 容器ID java这个命令的意思是将我们容器持久化为java镜像。如果镜像不存在就会新建一个。 启动这个新建的镜像。docker run -it -p 8080:8080 java注意看我们的启动命令发生了变化,多了一个 -P 这个命令的意思是将容器内的 8080 端口暴漏到宿主机上。再次访问 0.0.0.0:8080,我们就能看到那只小花猫了,真可爱。刚才那个容器还在占用我们的内存怎么办,干掉他。 删除容器 docker rm 容器ID 至此我们的第一步已经完成了,接下来我们就要集成我们的代码了。 集成代码我们刚才启动的容器是一个完全的独立的黑盒子,它根本不知道我们的代码再哪里,所以我们就要使用docker的挂载卷让宿主机和容器可以共享目录。不好意思,我们又要干掉刚才启动的那个容器了。 docker run -it -v /Users/name/web:/opt/root -p 8080:8080 java 我们的启动命令又加入了新成员 -v。这个命令的意思就是将用户根目录下的 web 目录挂在到容器中 /opt/root 目录下。进入目录后我们就能发现web目录下的文件静静的躺在里面,像是沉睡多年的玛丽苏在等待你的呼唤。 开始呼唤吧。]]></content>
</entry>
<entry>
<title><![CDATA[kafka 笔记]]></title>
<url>%2F2016%2FKafkaEnr%2F</url>
<content type="text"><![CDATA[有时候,说程序员是复制粘贴代码.我觉得你说得对.但是,得看你复制几手代码了.读源码,复制源码,学习源码.学习优秀程序员写代码的风格. 我有时候看源码,喜欢找找工具类,看看能不能复用.比如kafka的源码 看看源码工具类序列化字符串123456789101112131415161718192021222324252627282930313233import kafka.utils.VerifiableProperties/** * A decoder is a method of turning byte arrays into objects. * An implementation is required to provide a constructor that * takes a VerifiableProperties instance. */trait Decoder[T] { def fromBytes(bytes: Array[Byte]): T}/** * The default implementation does nothing, just returns the same byte array it takes in. */class DefaultDecoder(props: VerifiableProperties = null) extends Decoder[Array[Byte]] { def fromBytes(bytes: Array[Byte]): Array[Byte] = bytes}/** * The string encoder translates strings into bytes. It uses UTF8 by default but takes * an optional property serializer.encoding to control this. */class StringDecoder(props: VerifiableProperties = null) extends Decoder[String] { val encoding = if(props == null) "UTF8" else props.getString("serializer.encoding", "UTF8") def fromBytes(bytes: Array[Byte]): String = { new String(bytes, encoding) }} 反序列化123456789101112131415161718192021222324252627282930313233343536373839import kafka.utils.VerifiableProperties/** * An encoder is a method of turning objects into byte arrays. * An implementation is required to provide a constructor that * takes a VerifiableProperties instance. */trait Encoder[T] { def toBytes(t: T): Array[Byte]}/** * The default implementation is a no-op, it just returns the same array it takes in */class DefaultEncoder(props: VerifiableProperties = null) extends Encoder[Array[Byte]] { override def toBytes(value: Array[Byte]): Array[Byte] = value}class NullEncoder[T](props: VerifiableProperties = null) extends Encoder[T] { override def toBytes(value: T): Array[Byte] = null}/** * The string encoder takes an optional parameter serializer.encoding which controls * the character set used in encoding the string into bytes. */class StringEncoder(props: VerifiableProperties = null) extends Encoder[String] { val encoding = if(props == null) "UTF8" else props.getString("serializer.encoding", "UTF8") override def toBytes(s: String): Array[Byte] = if(s == null) null else s.getBytes(encoding)} 时间123456789101112131415161718192021222324252627282930313233343536373839404142/** * Some common constants */object Time { val NsPerUs = 1000 val UsPerMs = 1000 val MsPerSec = 1000 val NsPerMs = NsPerUs * UsPerMs val NsPerSec = NsPerMs * MsPerSec val UsPerSec = UsPerMs * MsPerSec val SecsPerMin = 60 val MinsPerHour = 60 val HoursPerDay = 24 val SecsPerHour = SecsPerMin * MinsPerHour val SecsPerDay = SecsPerHour * HoursPerDay val MinsPerDay = MinsPerHour * HoursPerDay}/** * A mockable interface for time functions */trait Time { def milliseconds: Long def nanoseconds: Long def sleep(ms: Long)}/** * The normal system implementation of time functions */object SystemTime extends Time { def milliseconds: Long = System.currentTimeMillis def nanoseconds: Long = System.nanoTime def sleep(ms: Long): Unit = Thread.sleep(ms) } 加载配置文件12345678910111213141516/** * Read a properties file from the given path * @param filename The path of the file to read */ def loadProps(filename: String): Properties = { val props = new Properties() var propStream: InputStream = null try { propStream = new FileInputStream(filename) props.load(propStream) } finally { if(propStream != null) propStream.close } props } 等等 比如还有一些 zkUtil,offerset 之类的. 所以,当我们开发的时候,需要某个框架的某个功能的时候,我们不如简单看看源码,往往源码的测试类里都有我们需要的功能. 这样,比找博客是不是更直接一点呢.]]></content>
</entry>
<entry>
<title><![CDATA[Flask框架中用QQ邮箱发邮件]]></title>
<url>%2F2016%2FFlaskMail%2F</url>
<content type="text"><![CDATA[配置信息,看注释 12345678910111213141516171819202122232425basedir = os.path.abspath(os.path.dirname(__file__))app = Flask(__name__)app.config['SECRET_KEY'] = 'hard to guess string'app.config['SQLALCHEMY_DATABASE_URI'] =\ 'sqlite:///' + os.path.join(basedir, 'data.sqlite')app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True# 邮箱提供商服务器,这里是QQ邮箱app.config['MAIL_SERVER'] = 'smtp.qq.com'# 邮箱服务器端口(QQ邮箱的是465)app.config['MAIL_PORT'] = 465# 邮箱通讯协议,注意这里是SSL(选错了邮件发不出的)app.config['MAIL_USE_SSL'] = True# 发件邮箱app.config['MAIL_USERNAME'] = '694244330@qq.com'# 发件密码app.config['MAIL_PASSWORD'] = 'tdjzzgfdiwgobehh'# 拼接邮件的主题用,不是必选配置app.config['FLASKY_MAIL_SUBJECT_PREFIX'] = '[Flasky]'# 发件邮箱和Mail_username的邮箱要一样app.config['FLASKY_MAIL_SENDER'] = 'Flasky Admin <694244330@qq.com>'# 收件邮箱app.config['FLASKY_ADMIN'] = 'hsw_v5@163.com'mail = Mail(app) 注意:上述配置项一个也不要少 发邮件方法12345678910111213def send_async_email(app, msg): with app.app_context(): mail.send(msg)def send_email(to, subject, template, **kwargs): msg = Message(app.config['FLASKY_MAIL_SUBJECT_PREFIX'] + ' ' + subject, sender=app.config['FLASKY_MAIL_SENDER'], recipients=[to]) msg.body = render_template(template + '.txt', **kwargs) msg.html = render_template(template + '.html', **kwargs) thr = Thread(target=send_async_email, args=[app, msg]) thr.start() return thr]]></content>
</entry>
<entry>
<title><![CDATA[RDD究竟是什么]]></title>
<url>%2F2016%2FSparkRdd%2F</url>
<content type="text"><![CDATA[看源码部分关于RDD的注释在我们学习spark的时候,如果有什么拿不准,其实去找相关的源码,往往那部分源码都有比较详细的注释。想想,那可是作者的一手解释呢?比起在网上找博客要更合适一些呢。 抽象类RDD的注释 1234567891011121314151617181920212223242526A Resilient Distributed Dataset (RDD), the basic abstraction in Spark. bke an immutable,partitioned collection of elements that can be operated on in parallel. This class contains thebasic operations available on all RDDs, such as `map`, `filter`, and `persist`. In addition,[[org.apache.spark.rdd.PairRDDFunctions]] contains operations available only on RDDs of key-valuepairs, such as `groupByKey` and `join`;[[org.apache.spark.rdd.DoubleRDDFunctions]] contains operations available only on RDDs ofDoubles; and[[org.apache.spark.rdd.SequenceFileRDDFunctions]] contains operations available on RDDs thatcan be saved as SequenceFiles.All operations are automatically available on any RDD of the right type (e.g. RDD[(Int, Int)]through implicit.Internally, each RDD is characterized by five main properties: - A list of partitions - A function for computing each split - A list of dependencies on other RDDs - Optionally, a Partitioner for key-value RDDs (e.g. to say that the RDD is hash-partitioned) - Optionally, a list of preferred locations to compute each split on (e.g. block locations for an HDFS file)All of the scheduling and execution in Spark is done based on these methods, allowing each RDDto implement its own way of computing itself. Indeed, users can implement custom RDDs (e.g. forreading data from a new storage system) by overriding these functions. Please refer to the[[http://www.cs.berkeley.edu/~matei/papers/2012/nsdi_spark.pdf Spark paper]] for more detailson RDD internals. 我简单做个整理总结 1.RDD是分布式弹性数据集,是Spark中抽象出的最基本概念。它是不可变,并且一组分区中的元素可以并行处理的数据集。Spark中创建了一些算子来对RDD进行处理。比如:map,filter,persist等算子。2.另外在[[org.apache.spark.rdd.PairRDDFunctions]]这个类中,还包含了一些操作key-value类型的RDD。比如:groupByKey,join等算子3.还有其他的类处理对应的一些RDD中包含的数据类型。比如double,sequence等4.所有对RDD的操作都是可用的。主要是借助于scala的隐式转换特性。这样是操作单一元素的RDD还是操作pair类型的RDD,对于RDD而言就隐藏了。 RDD的5个特性 一系列的分区(partition),即数据集的基本组成单位 一个计算每个分片的函数 与父RDD的一系列依赖。这个也就是spark中的血统(lineage)+]]></content>
</entry>
<entry>
<title><![CDATA[Linux进程分析之内存]]></title>
<url>%2F2016%2FLinuxMem%2F</url>
<content type="text"><![CDATA[free命令解析 free命令可以用来了解当前操作系统整体可用内存的使用情况 详解 看看free支持的参数 123456789101112131415161718192021222324252627hushiwei@hsw:~$ free --helpUsage: free [options]Options: -b, --bytes show output in bytes --kilo show output in kilobytes --mega show output in megabytes --giga show output in gigabytes --tera show output in terabytes --peta show output in petabytes -k, --kibi show output in kibibytes -m, --mebi show output in mebibytes -g, --gibi show output in gibibytes --tebi show output in tebibytes --pebi show output in pebibytes -h, --human show human-readable output --si use powers of 1000 not 1024 -l, --lohi show detailed low and high memory statistics -t, --total show total for RAM + swap -s N, --seconds N repeat printing every N seconds -c N, --count N repeat printing N times, then exit -w, --wide wide output --help display this help and exit -V, --version output version information and exit 以人类可看的方式输出 1234hushiwei@hsw:~$ free -h total used free shared buff/cache availableMem: 7.7G 4.4G 240M 970M 3.0G 2.1GSwap: 0B 0B 0B 我用的是Deepin系统,free命令出来的比一般的ubuntu或者linux命令出来的要少一行。 为了详细的说明,我换一台服务器12345[devel@xxx ~]$ free -h total used free shared buffers cachedMem: 24G 20G 3.6G 18M 358M 13G-/+ buffers/cache: 6.9G 17GSwap: 4.0G 0B 4.0G 简单说明: total:24G 即为总的物理内存大小 used:20G 为当前被系统调度的内存大小 free:3.6G 当前未被系统调度的内存大小 注意: 这里的用词是被系统调度,而不是被系统进程物理占用的内存大小 如何理解: used只是被系统调度的内存,系统可以从used里面划分出有效的内存来调度给新进程使用 只有在可调度内存不够的情况下,才会从free中挪用新内存进行调度。 used内存会被系统用来做哪些调度 1.实实在在被进程占用。比如进程申请的栈内存,被分配并被使用的堆内存。在进程存活并未主动释放它的内存情况下,这块内存是无法被系统调度给其他进程使用的。即第三行used=6.9G部分 2.用于写buffers,即上面buffers:358M.对于文件写操作需要经过写buffer的过程再落到磁盘上,而buffer这块内存是可以被系统回收并反复被调度的内存。 3.用于读cached,即上面的cached:13G.系统会将一大部分内存用于cache,比如用于文件预读等,与buffer相同,这部分内存可以被系统重复调度给信息的进程 总结: 因此,一个系统**被调度内存大小**=buffers(358M)+cached(13G)+第三行的used(6.9G)=第二行的used(20G) **可用于新进程的内存大小**=free(3.6G)+buffers(358M)+cached(13G)=第三行的free(17G) 所以,这里第三行表示了当前物理内存被系统占用的内存(used),以及可以被调度的内存大小(free) 而这里free+used即为total的物理内存大小,也就是机器的所有内存大小。 所以,在日常工作中,可以通过第三行的信息快速获取机器物理内存被实际占用情况。(Deepin系统居然没有这一行…什么原因) 理解了之后,这里终于知道这个-/+ buffers/cache:是什么意思了.实际占用内存也就是已被系统调度内存减去buffers和cached内存. 那么实际可用内存也就是系统未被调度内存加上buffers和cached内存了 最后,知道每个部分的意思后,就算没有第三行,自己也可以轻易的看出来系统内存实际使用情况 相关命令 vmstat free,buffer,cached的值,也可以通过vmstat命令来动态获取它的变化值,比如每隔一秒采集一次,总共采集10次memory的变化;12345678910111213hushiwei@hsw:~$ vmstat 1 10procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 0 0 0 420908 95636 3952264 0 0 309 89 420 3104 10 3 83 5 0 0 0 0 438580 95636 3932768 0 0 0 360 1548 12740 8 3 88 0 0 0 0 0 438944 95636 3932684 0 0 0 0 788 7131 2 2 96 0 0 1 0 0 439020 95636 3932684 0 0 0 0 817 11039 3 2 95 0 0 2 0 0 423376 95636 3950340 0 0 0 0 1092 8635 7 5 88 0 0 0 0 0 421692 95644 3951528 0 0 0 236 1231 12386 10 3 84 3 0 1 0 0 439968 95644 3932768 0 0 0 360 881 7472 3 2 95 0 0 1 0 0 439800 95644 3932680 0 0 0 0 3319 22657 15 3 81 0 0 0 0 0 439692 95644 3932680 0 0 0 0 737 7149 3 1 96 0 0 2 0 0 422388 95644 3950288 0 0 0 0 916 12164 7 2 91 0 0 watch 在linux中可以通过watch命令,实时监控每一条命令执行的结果动态变化。 watch命令可以实时全屏监控当前命令执行的动态变化结果 watch命令的常用参数有: -n:时隔多少秒刷新(默认每隔2秒刷一次结果) -d:高亮显示动态变化 -t:关闭命令顶部的时间间隔,命令显示 example:1234# 每隔3秒查看当前系统时间watch -n 3 date# -d 参数可以高亮显示执行结果的变化watch -n 3 -d date 因此,watch与free命令配合,也可以获取到动态的内存变化值。1watch -n 1 -d free top命令解析 top不仅仅用于内存分析,还用于进程分析。先说关于内存部分 12345678910111213141516171819202122232425262728top - 10:28:54 up 19 days, 20:48, 1 user, load average: 0.00, 0.00, 0.00Tasks: 362 total, 1 running, 361 sleeping, 0 stopped, 0 zombieCpu(s): 0.0%us, 0.0%sy, 0.0%ni, 99.9%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%stMem: 25402148k total, 22153960k used, 3248188k free, 367512k buffersSwap: 4194300k total, 0k used, 4194300k free, 14689300k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 27234 kafka 20 0 9023m 596m 18m S 1.3 2.4 265:13.15 java 1344 root 1 -19 510m 284m 1032 S 0.3 1.1 75:39.37 mfsmount 23913 hdfs 20 0 2932m 645m 24m S 0.3 2.6 81:37.27 java 29664 yarn 20 0 2990m 359m 25m S 0.3 1.4 79:56.77 java 36226 hive 20 0 9349m 744m 25m S 0.3 3.0 112:08.36 java 1 root 20 0 19356 1540 1228 S 0.0 0.0 0:02.15 init 2 root 20 0 0 0 0 S 0.0 0.0 0:00.02 kthreadd 3 root RT 0 0 0 0 S 0.0 0.0 0:01.00 migration/0 4 root 20 0 0 0 0 S 0.0 0.0 0:03.30 ksoftirqd/0 5 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/0 6 root RT 0 0 0 0 S 0.0 0.0 0:01.80 watchdog/0 7 root RT 0 0 0 0 S 0.0 0.0 0:01.35 migration/1 8 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/1 9 root 20 0 0 0 0 S 0.0 0.0 0:04.01 ksoftirqd/1 10 root RT 0 0 0 0 S 0.0 0.0 0:01.56 watchdog/1 11 root RT 0 0 0 0 S 0.0 0.0 0:01.00 migration/2 12 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/2 13 root 20 0 0 0 0 S 0.0 0.0 0:03.74 ksoftirqd/2 14 root RT 0 0 0 0 S 0.0 0.0 0:01.62 watchdog/2 15 root RT 0 0 0 0 S 0.0 0.0 0:01.10 migration/3 16 root RT 0 0 0 0 S 0.0 0.0 0:00.00 stopper/3 其中mem部分的total,used,free,buffers,cached与free命令显示的含义是一致的,他们都反应了整个系统的内存消耗情况; 未完待续。。。]]></content>
</entry>
<entry>
<title><![CDATA[配置Ubuntu/Deepin下要安装的常用软件]]></title>
<url>%2F2016%2FDeepinSoft%2F</url>
<content type="text"><![CDATA[给系统更新1sudo apt-get update 安装sublime3 1.在商店里下载sublime3后 2.用第一个这个就可以注册成功 1234567891011121314151617181920212223Ryan ClarkSingle User LicenseEA7E-8124792158A7DE B690A7A3 8EC04710 006A5EEB34E77CA3 9C82C81F 0DB6371B 79704E6F93F36655 B031503A 03257CCC 01B20F60D304FA8D B1B4F0AF 8A76C7BA 0FA94D5556D46BCE 5237A341 CD837F30 4D60772D349B1179 A996F826 90CDB73C 24D41245FD032C30 AD5E7241 4EAA66ED 167D91FB55896B16 EA125C81 F550AF6B A6820916Michael BarnesSingle User LicenseEA7E-8213858A353C41 872A0D5C DF9B2950 AFF6F667C458EA6D 8EA3C286 98D1D650 131A97ABAA919AEC EF20E143 B361B1E7 4C8B7F04B085E65E 2F5F5360 8489D422 FB8FC1AA93F6323C FD7F7544 3F39C318 D95E6480FCCC7561 8A4A1741 68FA4223 ADCEDE07200C25BE DBBC4855 C4CFB774 C5EC138C0FEC1CEF D9DCECEC D3A5DAD1 01316C36 3.安装插件包管理 1import urllib.request,os; pf = 'Package Control.sublime-package'; ipp = sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHandler()) ); open(os.path.join(ipp, pf), 'wb').write(urllib.request.urlopen( 'http://sublime.wbond.net/' + pf.replace(' ','%20')).read()) 安装java,scala,mvn,intellij idea 1.解压tar包 2.设置环境变量 3.加载环境变量以生效 安装Mysqlubuntu上安装MySQL非常简单只需要几条命令就可以完成。12345 1. sudo apt-get install mysql-server 2. apt-get isntall mysql-client 3. sudo apt-get install libmysqlclient-dev 安装过程中会提示设置密码什么的,注意设置了不要忘了,安装完成之后可以使用如下命令来检查是否安装成功:1 sudo netstat -tap | grep mysql 通过上述命令检查之后,如果看到有mysql 的socket处于 listen 状态则表示安装成功。 登陆mysql数据库可以通过如下命令:123 mysql -u root -p -u 表示选择登陆的用户名, -p 表示登陆的用户密码,上面命令输入之后会提示输入密码,此时输入密码就可以登录到mysql。 安装 python 环境12sudo apt-get install python-pip python-devsudo pip install --upgrade pip pip安装常用模块123sudo pip install djangosudo pip install MySQL-pythonsudo pip install beautifulsoup4 安装git1sudo apt-get install git git的执行文件在/usr/bin/git 安装nodejs及npm12345sudo add-apt-repository ppa:chris-lea/node.jssudo apt-get updatesudo apt-get install python-software-properties python g++ make nodejs npm 安装jekyll步骤 安装一个本地的jekyll环境,方便写博客的时候调试预览效果 123456sudo apt-get install rubysudo apt-get install ruby-dev(用这个命令,不然后面装jekyll会出现缺文件的错误)ruby -v (检查一下版本)gem -v (检查一下版本)sudo apt-get install nodejs (把nodejs也装上)sudo gem install jekyll (最后可以安装jekyll了) 注意: 最后安装jekyll的时候,如果报了缺少什么的错误。根据提示的错误,下载缺少的文件即可。]]></content>
</entry>
<entry>
<title><![CDATA[Ubuntu/Deepin下用Shadowsocks实现翻墙上网]]></title>
<url>%2F2016%2FUbuntuSocks%2F</url>
<content type="text"><![CDATA[下载安装小飞机12345sudo add-apt-repository ppa:hzwhuang/ss-qt5sudo apt-get updatesudo apt-get install shadowsocks-qt5 配置代理 配置账号 如图所示 点击连接,配置自己的代理服务器的IP地址,密码,端口等。仿照我填的那样填写。 然后,点击测试延迟,看看有没有出现延迟,出现了,那么就是连接上了。 本地配置代理那里,方法请选择手动,Socks主机位置填写的是你Shadowsocks里的本地地址IP,我的都是127.0.0.1,端口也是一样。然后,点击应用到整个系统。 Ubuntu这里是这样的 Deepin是这样的 浏览器插件此时你并不能科学上网,因为这个方式并不是所谓”全局代理”,所以你需要配置一下你的浏览器。我所使用的是Chrome,然后安装SwitchySharp插件,市场访问不了可以搜一下墙内的资源,跨平台所以win的也能用,拖放进去而已。记住要打开Chrome的扩展界面,才可以把离线插件拖进行进行安装。为了方便大家安装,这里我提供下载链接。链接失效请联系我.下载地址 密码: fjfq Chrome代理设置 首先,我们确保已在Chrome浏览器中安装好了Proxy SwitchySharp扩展. 进入选项页后,我们在情景模式中,新建一个模式。名称随便填写然后按照我的截图那样填写,千万不要填错了。shadowsocks使用的就是SOCKS v5的协议,ip和端口号要跟系统设置的一致。然后点击保存。 2.设置切换规则 然后点击“切换规则”,分别新建两条规则。 本地规则,选择正则表达式目的是让浏览器可以访问局域网等此类网址时候,不适用任何代理,即可直接访问。 1192\.168\.|localhost 国外规则,选择正则表达式让我们访问国外的网址的时候,就可以通过代理进行翻墙访问了。 1(fbcdn|akamaihd|pixnet)\.net|wretch\.cc|t\.co|goo\.gl|(google(usercontent|apis)*|chrome|staticflickr|imdb|ytimg|gstatic|html5rocks|amazonaws|github|tumblr|addthis|wordpress|blogger|(blog|app)spot|friendfeed|twitter|facebook|youtube|dropbox|feedburner|googleapis|android)\.com 自动切换代理最后保存之后,再点击浏览器上面的Proxy SwitchySharp扩展图标,点击勾选其中的自动却换模式,即可在浏览网页时,让扩展自动根据之前的规则来判断使用何种代理了,而不需要我们自己受到切换了了。 最后,根据自己的情况设置是否开机自启动。 完成可以自由翻墙了。。。]]></content>
</entry>
<entry>
<title><![CDATA[linux创建软硬链接]]></title>
<url>%2F2016%2FLinuxSymbolicLink%2F</url>
<content type="text"><![CDATA[创建软链接或是硬链接在 linux 中是非常常用的,所以有必要记住掌握. 命令源文件 目标文件 1234root@localhost ~/demo lnusage: ln [-Ffhinsv] source_file [target_file] ln [-Ffhinsv] source_file ... target_dir link source_file target_file 创建软链接也就是创建一个快捷方式 12#创建test.log的一个符号连接文件test1.logln -s test.log test1.log 创建硬链接12345#创建test.log的一个硬连接文件test2.logln test.log test2.log # -i参数显示文件的inode节点信息ls -li #软硬链接的区别 硬链接文件有两个限制 1)、不允许给目录创建硬链接; 2)、只有在同一文件系统中的文件之间才能创建链接,而且只有超级用户才有建立硬链接权限。 对硬链接文件进行读写和删除操作时候,结果和软链接相同。但如果我们删除硬链接文件的源文件,硬链接文件仍然存在,而且保留了愿有的内容。 这时,系统就“忘记”了它曾经是硬链接文件。而把他当成一个普通文件。 那么我们就可以这样理解:硬连接指通过索引节点来进行的连接,其作用是允许一个文件拥有多个有效路径名,能够达到误删除的作用。 其原因是因为对应的文件的索引节点有一个以上的连接。只删除一个连接并不影响索引节点本身和其它的连接,只有当最后一个连接被删除后,文件的数据块及目录的连接才会被释放。文件才会被真正删除。 软链接没有硬链接以上的两个限制,因而现在更为广泛使用,它具有更大的灵活性,甚至可以跨越不同机器、不同网络对文件进行链接。 但是软链接的缺点在于:因为链接文件包含有原文件的路径信息,所以当原文件从一个目录下移到其他目录中,再访问链接文件,系统就找不到了,而硬链接就没有这个缺陷,你想怎么移就怎么移; 但是硬链接它要系统分配额外的空间用于建立新的索引节点和保存原文件的路径. 注:保存在磁盘分区中的文件不管是什么类型都给它分配一个编号,称为索引节点号(Inode Index即I节点)。]]></content>
</entry>
<entry>
<title><![CDATA[配置 ssh 免密码登录]]></title>
<url>%2F2016%2Fsshlogin%2F</url>
<content type="text"><![CDATA[配置 ssh 免密码登录很简单,记住几个命令就行了 生成公钥1ssh-keygen -t rsa 把公钥拷贝到目标机器上即可1ssh-copy-id -i /root/.ssh/id_rsa.pub master(主机名或IP或用户名@IP) 1.会提示输入 yes 2.然后输入目标机器的密码即可 3.成功了,会有类似的提示 12345Now try logging into the machine, with "ssh '10.30.23.216'", and check in: .ssh/authorized_keysto make sure we haven't added extra keys that you weren't expecting. 验证1ssh '10.30.23.216' 说明在.ssh 目录下有这么几个文件 123456[root@bigdata .ssh]# lltotal 16-rw------- 1 root root 399 Apr 6 18:24 authorized_keys-rw------- 1 root root 1675 Apr 6 18:23 id_rsa-rw-r--r-- 1 root root 399 Apr 6 18:23 id_rsa.pub-rw-r--r-- 1 root root 2399 Apr 10 12:15 known_hosts 文件名 备注 authorized_keys 这个文件里存储了可以免密码登录到此台服务器的机器 id_rsa 私钥 id_rsa.pub 公钥 known_hosts 这个文件里存储了已经远程登录到此台服务器的机器,不代表可以免密码登录 注意 因此,想要配置免密码登录,也就是把公钥拷贝到服务器的authorized_keys这个文件里就行了. 举例 1.A 机器想要免密码登录到 B 机器 2.A机器生成公钥(没有公钥就执行:ssh-keygen -t rsa.有就不必要不执行了) 3.把 A 机器的公钥拷贝到 B机器的authorized_keys文件里即可. 4.ssh-copy-id -i /root/.ssh/id_rsa.pub master(主机名或IP) 5.上面的命令就是第3步的实现 6.输入 yes,输入远程机器的密码 7.完成]]></content>
</entry>
<entry>
<title><![CDATA[Java数据结构之队列]]></title>
<url>%2F2016%2FQueue%2F</url>
<content type="text"><![CDATA[什么是队列 队列是一种受限的线性表,其限制是仅允许在队的一端进行插入,而在队的另一端进行删除. 在队列中把插入数据元素的一端称为队尾(rear). 删除数据元素的一端称为队首(front). 向队尾插入元素称为进队或入队.新元素入队后成为新的队尾元素. 从队列中删除元素称为离队或出队.元素出队后,其后续元素成为新的队首元素 因此在队列中,先入队的元素,肯定是先出队.所以称队列为先进先出表(FIFO). 队列结构与日常生活中排队等候服务的模型是一致的,最早进入队列的人,最早得到服务从队首离开;最后到来的人只能排在队列的最后,最后得到服务并最后离开. 队列的常用方法队列结构一般都满足以下几个方法: 方法 功能描述 getSize() 返回队列的大小,即队列中元素的个数 isEmpty() 判断队列是为空 enqueue(e) 将元素e入队 dequeue 删除队尾元素 peek() 获取队首元素,但不出队 接口代码如下: 123456789101112131415/** * Created by HuShiwei on 2016/10/18 0018. * FIFO先进先出 */public interface Queue { public int getSize();//返回队列的大小 public boolean isEmpty();//判断队列是否为空 public void enqueue(Object e);//数据元素e入队 public Object dequeue();//队首元素出队 public Object peek();//取队首元素} 队列的线性表实现实现分析 队列的线性表实现:底层是用的数组存储队列元素. 如何判断队列是否满队列还是空队列 设计了一个循环数组 设计一个队首指针front,和一个队尾指针rear front指向队列的首元素,rear指向队尾的最后一个元素+1的位置 如何判断队列是空队列还是满队列? 当是空队列的时候,front=rear,两个指针都指向同一个位置 当是满队列的时候,因为是循环数组,所以front=rear.这样我们就没办法区分是空队列还是满队列了. 如何解决这个问题呢? 把数组的最后一个位置空出来,浪费最后的存储空间. 这样,判断是满队列的时候,就可以根据(rear+1)%capacity=front 注意点 队尾指针rear指向队尾元素+1的位置.这样在队尾入队的时候就可以指针赋值了 代码实现注意: 在实现队列的时候,一定要记住 队列的结构特性,FIFO 如何通过指针来操作 理解了这些,自己就可以很轻松的实现队列了 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586/** * 队列的顺序存储实现 */public class QueueArray implements Queue { private static final int CAP = 7;//队列默认大小 private Object[] elements;//数据元素数组 private int capacity;//数组的大小 private int front;//队首指针 private int rear;//队尾指针 public QueueArray() { this(CAP); }// 初始化队列 public QueueArray(int cap){ capacity = cap + 1; elements = new Object[capacity]; front = rear = 0; }// 返回队列的大小 @Override public int getSize() { return (rear-front+capacity)%capacity; }// 判断队列是否为空 @Override public boolean isEmpty() { return front==rear; }// 数据元素e入队 @Override public void enqueue(Object e) { if (getSize()==capacity-1) { expandSpace(); } elements[rear] = e; rear = (rear + 1) % capacity; }// 扩充队列大小 private void expandSpace() { Object[] objects = new Object[elements.length * 2]; int i=front; int j = 0; while (i != rear) { objects[j++] = elements[i]; i = (i + 1) % capacity; } elements = objects; capacity = elements.length;// 设置新的队首队尾指针 front = 0; rear = j; }// 队首元素出队 @Override public Object dequeue() { if (isEmpty()) { try { throw new Exception("错误: 队列空了..."); } catch (Exception e) { e.printStackTrace(); } } Object element = elements[front]; elements[front] = null; front = (front + 1) % capacity; return element; }// 取队首元素 @Override public Object peek() { if (isEmpty()) { try { throw new Exception("错误: 队列空了..."); } catch (Exception e) { e.printStackTrace(); } } return elements[front]; }} 队列的链式存储实现未完待续….]]></content>
</entry>
<entry>
<title><![CDATA[Java多线程总结笔记]]></title>
<url>%2F2016%2FJavaThread%2F</url>
<content type="text"><![CDATA[多线程的概述进程和线程 进程:是一个正在运行的程序,每个进程执行,都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元 线程:进程中的一个独立控制单元,线程在控制着进程的执行,一个进程中至少有一个线程(控制单元) 简单实现多线程 常用的实现多线程的两种方法,继承Thread类,实现Runnable接口 继承javalangThread类 步骤: 1、首先,定义类继承Thread; 2、复写Thread类中的run方法;目的:将自定义代码存储在run方法中,让线程执行 3、调用线程的start方法(该方法有两个作用:启动线程和调用run()方法) 代码示例: 123456789101112131415161718public class BasicThread extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println("Thread ID: "+i); } }}public class MainFunction { public static void main(String[] args) { BasicThread basicThread = new BasicThread(); basicThread.start(); for (int i = 0; i < 10; i++) { System.out.println("Main Function ID: "+i); } }} 要点:继承Thread类,重写run方法. 实现javalangRunnable接口 步骤: 1、定义类实现Runnable接口 2、复写接口中的run方法 3、创建实现Runnable接口对象 4、创建Thread类对象,将实现Runnable接口的对象作为实际参数传递给Thread类的构造函数 5、调用Thread类的start方法,开启接口 代码示例: 12345678910111213141516171819202122232425262728public class BasicRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+" ---> "+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }public class MainRunnable { public static void main(String[] args) { Thread thread = new Thread(new BasicRunnable()); thread.setName("Child Thread"); thread.start(); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread()+" ---> "+i); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }} 为什么要重写run方法因为Thread类定义了一个用于存储线程要运行的代码的方法,该方法就是run方法.也就是说Thread类中run方法,用于存储线程要运行的代码.主线程中的代码存储于main方法中. 继承Thread类和实现Runnable接口有什么区别? 继承Thread类:线程代码存放在Thread子类的run方法中 实现Runnable接口:线程代码存放在Runnable实现类的run方法中 实现Runnable接口避免了单继承的局限性,在定义线程时建议使用实现方式来完成 线程数据共享问题—锁问题: 当多个线程共享同一个数据时,导致共享数据出现错误解决方案: 当一个线程执行时,其他的线程不能参与执行.锁: 对象如图锁,持有锁的线程才能在同步中执行,没有锁的线程,即使获取了CPU的执行权,也进不去. 同步的前提: 必须要有两个或两个以上的线程访问共享数据 必须是多个线程使用同一个锁 好处:解决了多线程安全问题 弊端:多个线程都需要判断锁,较为消耗资源 伪代码: 123synchronized(监视器对象){//需要被同步的代码} synchronized同步代码块虽然Java允许使用任何对象作为监视器对象.但想一下同步的目的:阻止多个线程同时对同一个共享资源并发访问,因此通常使用可能被并发访问的资源作为同步监视器对象. 经典案例:模拟银行取钱操作 账户对象: 12345678910111213141516171819202122232425public class Account { private double balance;//账户余额 public Account(double balance) { this.balance = balance; } /** * 取钱 * @param drawBalance * @return */ public double drawMoney(double drawBalance) { balance = balance - drawBalance; return balance; } /** * 查询余额 * @return */ public double getBalance() { return balance; }} 取钱线程: 1234567891011121314151617181920212223242526272829public class DrawThread extends Thread { private Account account; private double drawBalance; public DrawThread(String name, Account account, double drawBalance) { super(name); this.account=account; this.drawBalance=drawBalance; } @Override public void run() { while (true) {// 同步代码块,将共享访问的Account对象作为锁 synchronized (account) { if (account.getBalance()<drawBalance) { System.out.println(Thread.currentThread()+" : 余额不足,当前余额: "+account.getBalance()); break; } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } account.drawMoney(drawBalance); System.out.println(Thread.currentThread()+"取钱成功,取走金额: "+drawBalance+" ,当前余额: "+account.getBalance()); } } }} 测试取钱: 1234567891011121314151617public class DrawTest { public static void main(String[] args) { Account account = new Account(1000); new DrawThread("A账户: ",account,500).start(); new DrawThread("B账户: ",account,300).start(); new DrawThread("C账户: ",account,100).start(); }}结果输出:Thread[B账户: ,5,main]取钱成功,取走金额: 300.0 ,当前余额: 700.0Thread[B账户: ,5,main]取钱成功,取走金额: 300.0 ,当前余额: 400.0Thread[B账户: ,5,main]取钱成功,取走金额: 300.0 ,当前余额: 100.0Thread[B账户: ,5,main] : 余额不足,当前余额: 100.0Thread[C账户: ,5,main]取钱成功,取走金额: 100.0 ,当前余额: 0.0Thread[C账户: ,5,main] : 余额不足,当前余额: 0.0Thread[A账户: ,5,main] : 余额不足,当前余额: 0.0 synchronized同步方法 同步方法是使用synchronized关键字来修饰某个方法,则该方法就被称为同步方法. 伪代码: 1public synchronized void disPlay(){} 特点: 1.对于同步方法而言,无需指定同步监视器对象.同步方法的同步监视器对象是this. 2.一个对象的一个同步方法被调用,这个对象的其他同步方法都用不了. 代码说明: 业务类: 1234567891011121314151617public class Working { public synchronized void tom() { System.out.println("tom in..."+"\n"+"tom say: Hello Jerry "); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("tom out..."); } public synchronized void jerry() { System.out.println("jerry in..."+"\n"+"jerry say: Hello Tom"); System.out.println("jerry out..."); }} 线程类: 123456789101112131415161718192021222324public class JerryThread extends Thread { private Working working; public JerryThread(Working working) { this.working = working; } @Override public void run() { working.jerry(); }}public class TomThread extends Thread { private Working working; public TomThread(Working working) { this.working = working; } @Override public void run() { working.tom(); }} 测试类: 1234567891011121314151617public class SynFunction { public static void main(String[] args) { Working working = new Working(); TomThread tomThread = new TomThread(working); JerryThread jerryThread = new JerryThread(working); tomThread.start(); jerryThread.start(); }}输出结果:jerry in...jerry say: Hello Tomjerry out...tom in...tom say: Hello Jerrytom out... 从输出可以印证之前说的同步方法的特点:当一个对象的同步方法被调用,它的其他同步方法都用不了. 死锁死锁现象: 当两个线程互相等待对方释放同步监视器时就会发生死锁.Java虚拟机没有检测,也没有采取措施来处理死锁情况,所以多线程应该避免死锁的出现.一旦程序出现死锁,整个程序既不会发生异常,也不会有任何提示.只是所有线程都处于阻塞状态. 一般这种情况发生在:同步中嵌套同步,但锁不同.比如:a线程锁定一个资源,同时想获取b线程的资源.b线程锁定一个资源,同时想获取a线程的资源. 代码示例: 线程1: 1234567891011121314151617public class Thread1 extends Thread { @Override public void run() { synchronized (Object.class) { System.out.println(Thread.currentThread().getName()+"...外层锁"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (String.class) { System.out.println("haha,锁主了"); } } }} 线程2: 1234567891011121314151617public class Thread2 extends Thread { @Override public void run() { synchronized (String.class) { System.out.println(Thread.currentThread().getName()+"...外层锁"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (Object.class) { System.out.println("haha,我也锁主了"); } } }} 测试类: 123456789101112public class LockFunction { public static void main(String[] args) { Thread1 thread1 = new Thread1(); Thread2 thread2 = new Thread2(); thread1.start(); thread2.start(); }}输出:Thread-0...外层锁Thread-1...外层锁 线程通信 当多个线程访问同一个资源,每个线程的实现功能不一样,而每个线程之间需要协调发展(两种线程交替运行) 例如: 123老师:我们开始上课;学生A:老师等一下,我要去厕所;老师:OK,你快点,那我wait()了,等你回来notify我一下。 线程通信的前提:同步环境 代码说明:加入了存钱的业务,现在分别有两个不同的业务了. 银行类: 123456789101112131415161718192021222324252627282930313233343536public class Bank { private double money; boolean flag = false; public Bank(double money) { this.money = money; } /** * 取钱方法 * @param drawMoney * @return */ public double drawMoney(double drawMoney) { money = money - drawMoney; return money; } /** * 存钱 * @param saveMoney * @return */ public double saveMoney(double saveMoney) { money = money + saveMoney; return money; } /** * 查询余额 * @return */ public double getMoney() { return money; }} 存钱类: 1234567891011121314151617181920212223242526272829public class SaveThread extends Thread { private Bank bank; private double saveMoney; public SaveThread(String threadName, Bank bank, double saveMoney) { super(threadName); this.bank=bank; this.saveMoney = saveMoney; } @Override public void run() { while (true) { synchronized (bank) { if (bank.flag) { try { bank.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } bank.saveMoney(saveMoney); System.out.println(Thread.currentThread().getName() + " 存款成功,存款金额: " + saveMoney + ",账户余额:" + bank.getMoney()); bank.flag = true; bank.notify(); } } }} 取钱类: 1234567891011121314151617181920212223242526272829303132public class DrawThread extends Thread { private Bank bank; private double drawMoney; public DrawThread(String threadName, Bank bank, double drawMoney) { super(threadName); this.bank = bank; this.drawMoney = drawMoney; } @Override public void run() { while (true) { synchronized (bank) { if (!bank.flag) { try { bank.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } if (bank.getMoney() < drawMoney) { System.out.println("余额不足,当前余额是: " + bank.getMoney()); break; } bank.drawMoney(drawMoney); System.out.println(Thread.currentThread().getName()+" 取款成功,取款金额是: "+drawMoney+",账户余额: "+bank.getMoney()); bank.flag = false; bank.notify(); } } }} 测试类: 12345678910111213141516171819202122232425262728293031323334353637383940414243public class BankTest { public static void main(String[] args) { Bank bank = new Bank(5000); new DrawThread("取钱线程",bank,500).start(); new SaveThread("存钱线程",bank,200).start(); }}输出:存钱线程存款成功,存款金额: 200.0,账户余额:5200.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 4700.0存钱线程存款成功,存款金额: 200.0,账户余额:4900.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 4400.0存钱线程存款成功,存款金额: 200.0,账户余额:4600.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 4100.0存钱线程存款成功,存款金额: 200.0,账户余额:4300.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 3800.0存钱线程存款成功,存款金额: 200.0,账户余额:4000.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 3500.0存钱线程存款成功,存款金额: 200.0,账户余额:3700.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 3200.0存钱线程存款成功,存款金额: 200.0,账户余额:3400.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 2900.0存钱线程存款成功,存款金额: 200.0,账户余额:3100.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 2600.0存钱线程存款成功,存款金额: 200.0,账户余额:2800.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 2300.0存钱线程存款成功,存款金额: 200.0,账户余额:2500.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 2000.0存钱线程存款成功,存款金额: 200.0,账户余额:2200.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 1700.0存钱线程存款成功,存款金额: 200.0,账户余额:1900.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 1400.0存钱线程存款成功,存款金额: 200.0,账户余额:1600.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 1100.0存钱线程存款成功,存款金额: 200.0,账户余额:1300.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 800.0存钱线程存款成功,存款金额: 200.0,账户余额:1000.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 500.0存钱线程存款成功,存款金额: 200.0,账户余额:700.0取钱线程 取款成功,取款金额是: 500.0,账户余额: 200.0存钱线程存款成功,存款金额: 200.0,账户余额:400.0余额不足,当前余额是: 400.0 线程学习的经典案例-生产者消费者案例 大多数设计模式中,都会有一个第三者来进行解耦.在生产者和消费者模式中,我们需要一个第三者来对此进行解耦 为什么要用生产者和消费者模式?在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。 什么是生产者消费者模式生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。 这个阻塞队列就是用来给生产者和消费者解耦的。纵观大多数设计模式,都会找一个第三者出来进行解耦,如工厂模式的第三者是工厂类,模板模式的第三者是模板类。在学习一些设计模式的过程中,如果先找到这个模式的第三者,能帮助我们快速熟悉一个设计模式。 单生产者单消费者一个生产者线程,一个消费者线程.过程看代码注释,非常详细了.希望我以后看见我的注释就能回想起一切. 资源类: 123456789101112131415161718192021222324252627282930313233343536373839404142434445public class Resource { private int num = 0; boolean flag = false; public Resource(int num) { this.num = num; }// 生产资源 public synchronized void createNum() {// flag为true表示有资源。那么生产者就不能生产了,生产者线程就开始wait等待 if (flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }// 如果到了这里,表示:生产者已经被唤醒了,那么意味着资源已经被消费了,生产者要开始生产了 num++; System.out.println(Thread.currentThread().getName()+" 生产者线程: ---> "+num);// 生产者生产完资源后,把flag标记为true.表示有资源了 flag = true;// 有资源后,生产者去唤醒消费者去消费资源 notify(); } // 消费资源 public synchronized void destory() {// 因为flag为true时候有资源,那么!flag表示没用资源了,因此消费者线程就需要等待了,等待生产者线程去生产资源 if (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }// 到了这里,说明消费者线程被生产者线程唤醒了,那么也意味着有资源了 System.out.println(Thread.currentThread().getName()+" 消费者线程: ---> "+num);// 消费了数据,那么消费者就得把flag标记为false,表示没用数据了,提醒生产者得去生产数据了 flag = false;// 接下来就得去唤醒生产者去生产数据了 notify(); }} 生产者线程: 123456789101112131415161718public class Producer implements Runnable { private Resource resource; public Producer(Resource resource) { this.resource = resource; } @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } resource.createNum(); } }} 消费者线程: 123456789101112131415161718public class Consumer implements Runnable { private Resource resource; public Consumer(Resource resource) { this.resource = resource; } @Override public void run() { while (true) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } resource.destory(); } }} 测试类: 123456789101112131415161718192021public class Test { public static void main(String[] args) { Resource resource = new Resource(50); new Thread(new Producer(resource)).start(); new Thread(new Consumer(resource)).start(); }}输出结果:Thread-0 生产者线程: ---> 51Thread-1 消费者线程: ---> 51Thread-0 生产者线程: ---> 52Thread-1 消费者线程: ---> 52Thread-0 生产者线程: ---> 53Thread-1 消费者线程: ---> 53Thread-0 生产者线程: ---> 54Thread-1 消费者线程: ---> 54Thread-0 生产者线程: ---> 55Thread-1 消费者线程: ---> 55Thread-0 生产者线程: ---> 56Thread-1 消费者线程: ---> 56 单生产者和单消费者没有问题. 多生产者多消费者 两个生产者两个消费者.生产者生产一个,消费者消费一个. 测试代码: 1234567891011121314public class Test { public static void main(String[] args) { Resource resource = new Resource(0); new Thread(new Producer(resource)).start(); new Thread(new Producer(resource)).start(); new Thread(new Consumer(resource)).start(); new Thread(new Consumer(resource)).start(); }}输出:Thread-1 生产者线程: ---> 105Thread-3 消费者线程: -----------> 105Thread-2 消费者线程: -----------> 105 这里出现了问题.105生产了一次,消费了两次.为了在做实验的时候,看出问题.我们不能把线程的等待时间设置得过长,不然可能没法暴露出问题.然后输出打印的时候,让消费者和生产者的输出格式不一样,是为了更快的定位到问题.所以,在这里我修改了消费者的输出.所以会和上面的不太一样. 原因分析 当两个线程同时操作生产者生产或者消费者消费时,如果有生产者或者消费者的两个线程都wait()时,再次notify(),由于其中一个线程已经改变了标记而另外一个线程再次往下直接执行的时候没有判断标记而导致的。 if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。 解决方案 +while判断标记,解决了线程获取执行权后,是否要运行!也就是每次wait()后再notify()时先再次判断标记 注意: 把单消费者单生产者改为多消费者和多生产者时候. 1.把if改为while 2.把notify改为notifyAll(不然会出现锁死) 代码: 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647public class Resource { private int num = 0; boolean flag = false; public Resource(int num) { this.num = num; }// 生产资源 public synchronized void createNum() {// flag为true表示有资源。那么生产者就不能生产了,生产者线程就开始wait等待 while (flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }// 如果到了这里,表示:生产者已经被唤醒了,那么意味着资源已经被消费了,生产者要开始生产了 num++; System.out.println(Thread.currentThread().getName()+" 生产者线程: ---> "+num);// 生产者生产完资源后,把flag标记为true.表示有资源了 flag = true;// 有资源后,生产者去唤醒消费者去消费资源// notify(); notifyAll(); } // 消费资源 public synchronized void destory() {// 因为flag为true时候有资源,那么!flag表示没用资源了,因此消费者线程就需要等待了,等待生产者线程去生产资源 while (!flag) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } }// 到了这里,说明消费者线程被生产者线程唤醒了,那么也意味着有资源了 System.out.println(Thread.currentThread().getName()+" 消费者线程: -----------> "+num);// 消费了数据,那么消费者就得把flag标记为false,表示没用数据了,提醒生产者得去生产数据了 flag = false;// 接下来就得去唤醒生产者去生产数据了// notify(); notifyAll(); }}]]></content>
</entry>
<entry>
<title><![CDATA[使用Spark分析泄露的2000W开房数据和600W条CSDN用户数据]]></title>
<url>%2F2016%2FSparkExampleCSDN%2F</url>
<content type="text"><![CDATA[2000W开房数据数据格式 Name,CardNo,Descriot,CtfTp,CtfId,Gender,Birthday,Address,Zip,Dirty,District1,District2,District3,District4,District5,District6,FirstNm,LastNm,Duty,Mobile,Tel,Fax,EMail,Nation,Taste,Education,Company,CTel,CAddress,CZip,Family,Version,id 数据预览为了保护用户隐私,相关数据打码了. 开房数据分析 因为是单机local模式运行,用其中一个文件作为测试数据.感兴趣的可以全部加载进来,放在集群里跑一跑.sortbykey是对每个分区中的数据进行排序,如果想要全局排序,把分区数设置为1 分析开房次数TOP10用户(第0,3,4三个字段确定唯一的用户) 12345678910111213141516171819202122val conf = new SparkConf().setAppName("KaiFan").setMaster("local[*]")val sc = new SparkContext(conf)// 酒店开房数据的字段// Name,CardNo,Descriot,CtfTp,CtfId,Gender,Birthday,Address,Zip,Dirty,District1,District2,District3,District4,District5,District6,FirstNm,LastNm,Duty,Mobile,Tel,Fax,EMail,Nation,Taste,Education,Company,CTel,CAddress,CZip,Family,Version,idvar hotel1RDD = sc.textFile("G:\\2000W\\1-200W.csv")// var hotel2RDD = sc.textFile("G:\\2000W\\200W-400W.csv")// var hotel3RDD = sc.textFile("G:\\2000W\\400W-600W.csv")// var hotel4RDD = sc.textFile("G:\\2000W\\600W-800W.csv")// var hotel5RDD = sc.textFile("G:\\2000W\\800W-1000W.csv")// var hotel6RDD = sc.textFile("G:\\2000W\\1000W-1200W.csv")// var hotel7RDD = sc.textFile("G:\\2000W\\1200W-1400W.csv")// var hotel8RDD = sc.textFile("G:\\2000W\\1400W-1600W.csv")// var hotel9RDD = sc.textFile("G:\\2000W\\1600w-1800w.csv")// var hotel10RDD = sc.textFile("G:\\2000W\\1800w-2000w.csv")// var hotelRDD = hotel1RDD ++ hotel2RDD ++ hotel3RDD ++ hotel4RDD ++ hotel5RDD ++ hotel6RDD ++ hotel7RDD ++ hotel8RDD ++ hotel9RDD ++ hotel10RDDvar hotelRDD = hotel1RDD// 分析开房次数TOP10用户(第0,3,4三个字段确定唯一的用户)val tmpRDD=hotelRDD.map(line=>line.toString().split(",")).filter(_.length==33)val result=tmpRDD.map(arr=> (arr(0),arr(3),arr(4))).map(arr=>(arr,1)).reduceByKey(_+_).sortBy(arr=>arr._2,false)result.take(10).foreach(println) 统计一天中,各个小时离店的人数 先去除脏数据,然后从时间字段里过滤出小时部分.然后统计小时的词频 1234567891011val hourRDD = hotelRDD.map(line => line.toString().split(",")).filter(_.length == 33).filter(arr => arr(31).trim.length != 0) .map(arr => arr(31)).map(str => str.split("[- :]")).filter(arr => arr.length == 6) .map(arr => arr(3).toInt).map(hour => (hour, 1)).reduceByKey(_ + _).map { case (x, y) => (y, x)} // 用sortbykey进行排序,然后再取前10val sortedHourRDD=hourRDD.sortByKey(false, 1)sortedHourRDD.take(10).foreach(println)println("--------------------------------")// 对未排序的序列,调用top(n)算子,底层帮你排序,然后取topn数据.hourRDD.top(10).foreach(println) 性别分布统计 1234567891011// 性别分布统计 val sexRDD=hotelRDD.map(line=> line.toString().split(",")).filter(_.length==33).filter(arr=>arr(31).trim.length!=0) .map(arr=>arr(5))// 过滤掉脏数据 .filter(_.trim.length==1) .filter(sex=>(sex=="F" || sex=="M")) .map((_,1))// 对性别字段进行统计 .reduceByKey(_+_) sexRDD.foreach(println) 开房的年龄分布 12345678910111213141516// 年龄分布统计(从身份证中截取出身年份,然后用今年减去即可)val ageRDD = hotelRDD.map(line => line.toString().split(",")).filter(_.length == 33) .filter(arr => arr(3) == "ID") //只取ID类型的用户 .filter(arr => arr(4).length == 18) //确保身份证号的位数是对的 .map(_ (4)) .filter("^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$".r.pattern.matcher(_).matches()) .map(num => num.substring(6, 10)) // 取出年份 .map(2016 - _.toInt) .filter(age => (age > 0 && age < 110)) .map((_, 1)) .reduceByKey(_ + _) .map { case (x, y) => (y, x) } .sortByKey(false, 1)println(ageRDD.count())ageRDD.foreach(println) CSDN用户数据分析数据格式预览 以免泄露别人的信息,以下的信息我做了额外的增加 12345zdgs # 12344fd321 # zdfdag@csdn.netLaafoZheng # 6702fsd03313747 # chenfdagming_zheng@163.comfstfaao # 730df413 # fstafao@tom.comhufdwolf # 25352gf63 # hujiadfye@263.netcafaddcjl # KIC43aafdk6! # ccegfdsdcjl@21cn.com 数据分析 分析最多人使用的TOPn个密码 1234567891011val csdnRDD = sc.textFile("G:\\600W-CSDN\\csdnuser.txt")println(s"csdn一共泄露了: ${csdnRDD.count()} 个用户数据")// cafaddcjl # KIC43aafdk6! # ccegfdsdcjl@21cn.com// 分析最多人使用的TOPn个密码val pwRDD = csdnRDD.map(line => line.toString.split(" # ")) .map(_ (1)) .map((_, 1)).reduceByKey(_ + _) .map { case (x, y) => (y, x) }.sortByKey(false) .map { case (x, y) => (y, x) }println("最常用的前50个密码: ")pwRDD.take(50).foreach(println) 统计使用纯数字作为密码的人数 123456val numPwRDD = csdnRDD.map(line => line.toString.split(" # ")) .map(_ (1)) .filter("\\d+".r.pattern.matcher(_).matches()) .map((_, 1)).reduceByKey(_ + _).sortBy(_._2, false)println("最常用的前20个纯数字密码: ")numPwRDD.take(20).foreach(println) 统计使用纯字母作为密码的人数 123456val letterRDD = csdnRDD.map(line => line.toString.split(" # ")) .map(_ (1)) .filter("[a-zA-Z]+".r.pattern.matcher(_).matches()) .map((_, 1)).reduceByKey(_ + _).sortBy(_._2, false)println("最常用的前20个纯字母密码: ")letterRDD.take(20).foreach(println) 声明: 统计指标来源与网络上的博客 数据也来自于网络,想要数据的可以联系我]]></content>
</entry>
<entry>
<title><![CDATA[在VPS上安装Jekyll,搭建自己的静态博客]]></title>
<url>%2F2016%2FVPSInstallJekyll%2F</url>
<content type="text"><![CDATA[Jekyll是什么? jekyll可以将纯文本转化为静态网站和博客。 Jekyll具体可以做什么,不在本文的讨论范围内,有兴趣的可以去官网上看看帮助文档。Jekyll官网 安装Jekyll的过程 环境:本教程是在VPS的CentOS6.5上搭建的 1.安装Ruby1.1 安装rvm,然后通过rvm安装较新版本的Ruby12345gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3curl -sSL https://get.rvm.io | bash -s stablesource /etc/profile.d/rvm.sh 1.2 安装ruby 2.2.1123sudo yum install libyamlrvm install 2.2.1 1.3 设为默认版本1rvm use 2.2.1 --default 2.安装 Nodejs Jekyll 依赖 JavaScript 运行时库,需要安装 Nodejs 12345sudo rpm -ivh http://mirrors.zju.edu.cn/epel/6/i386/epel-release-6-8.noarch.rpmsudo yum updatesudo yum install nodejs 3.安装 Jekyll1gem install jekyll 4.测试是否成功12345jekyll new blogcd blogjekyll serve --host 0.0.0.0 然后在浏览器中打开 http://<外网 IP 地址>:4000,应能看到 Jekyll 默认页面。 5.搭建自己的静态博客 做好上述的准备后,就可以着手搭建自己的博客了. 5.1 如果你会前端那么你可以自己写页面或者修改别人写的模板. 5.2 如果你不会前端像我这样不会前端的,那么可以在网上找些Jekyll的模板,然后套上去就可以用了. 6. 如何套上模板6.1 Jekyll主题介绍如图 我们主要知道这三个地方就行了,有兴趣的也可以把所有的内容都看看.做到心里有数. _post:在这个路径下放你写的博客就行了.要注意命名规则和博客的handle规则.这些Jekyll的官网都有说明.不会博客可能不会显示出来. page:这个页面下放的是你的博客有几个tab页. _config.yml:关于Jekyll静态博客,所有的可配参数,都可以在这个配置文件中进行修改. 6.2 启动博客把你的博客工程放到你的装好了Jekyll的VPS服务器上后,进入这个目录.用下面的几个命令就完事了. 12345678910启动你的博客,让任何主机都可以访问(--detach 表示后台运行):jekyll serve --detach --host 0.0.0.0当修改了博客,或者添加了博文的时候,需要先关闭在后台运行的博客服务.查找博客进程,第一个就是,第二个是Jekyll服务,不用kill:ps aux | grep jekyll关闭上面查到的第一个进程就行了:kill -9 pid 现在你在浏览器上就可以通过Ip+端口访问了. 6.3 购买域名+修改默认端口6.3.1 购买域名根据自己的需要购买自己的域名,价格根据你的选择而来的.我是在阿里云上买的.购买域名的地址 买好域名后,再把域名和IP绑定,做好映射.买的时候注意看,会有引导步骤. 6.3.2 修改默认端口 Jekyll的默认端口是4000.但是浏览器访问的默认端口是80.为了省去别人访问你的博客的时候还得写端口.我们需要把Jekyll的默认端口修改为80 1.查看监听80端口 12[root@bigdata ~]# netstat -nat | grep ':80'tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 2.如果显示listen表示正在运行,那么我们需要先关闭被占用的80端口.切换到/etc/init.d服务启动脚本下面查看.(httpd就是了) 123456[root@bigdata init.d]# cd /etc/init.d/[root@bigdata init.d]# lscrond httpd modules_dep network quota_nld rsyslog single sshdfunctions ip6tables named nmb rdisc sandbox smb udev-posthalt iptables netconsole nscd restorecond saslauthd snmpd winbindhtcacheclean killall netfs portreserve rpcbind sendmail snmptrapd xinetd 3.找到占用80服务的端口之后,通过命令关闭80端口. 1[root@bigdata ~]# /etc/init.d/httpd stop 4.再次查看端口使用情况,已经没有80在使用了 12[root@bigdata ~]# netstat -nat | grep ':80'[root@bigdata ~]# 5.那么就可以把Jekyll的默认端口改成80就完事了. 修改你的模板下的_config.yml文件,在里面加上 1port: 80 6.然后上传你的工程到VPS下,启动你的博客就行了,若是域名配好了,那么就可以用域名访问了 1[root@bigdata dmlBlog]# jekyll serve --detach --host 0.0.0.0 我的博客 完成了….]]></content>
</entry>
<entry>
<title><![CDATA[ssh 隧道转发]]></title>
<url>%2F2016%2Flinuxssh%2F</url>
<content type="text"><![CDATA[只有22端口能连上远程服务器,但是要访问远程服务器的别的端口.那么可以用ssh 隧道转发 命令1ssh -L 7180:localhost:7180 root@120.17.119.10 说明 把120.17.119.10的7180端口转发到 localhost 的7180端口]]></content>
</entry>
<entry>
<title><![CDATA[递归算法]]></title>
<url>%2F2016%2FRecursion%2F</url>
<content type="text"><![CDATA[递归与堆栈递归的概念递归: 是指在定义自身的同时又出现对自身的引用.如果一个算法直接或间接地调用自己,则称这个算法是一个递归算法.递归组成: 任何一个有意义的递归算法总是由两部分组成:递归调用与递归终止条件. 递归组成 递归调用 递归终止条件 递归的实例求N的阶乘1234567891011121314 public static void main(String[] args) {// 求n的阶乘 int sum = factorial(4); System.out.println(sum); } public static int factorial(int n) { if (n==1) { // 递归终止条件 return 1; }else{ return n * factorial(n - 1); //递归调用 } } 从代码中可以看出,在编写递归算法时,一定要注意有递归调用和递归终止条件.如果没有递归终止条件,那么将无休止的进行下去,直到溢出.如果没有递归调用,那么就不是一个递归算法. 递归的实现与堆栈 我们知道在递归算法中会递归调用自身,因此在递归算法的执行过程中会多次进行自我调用.那么这个调用过程是如何实现的呢? 为了说明自身的递归调用,我们先看任意两个函数之间进行调用的情形. 通常在一个函数执行过程中需要调用另一个函数时,在运行被调用函数之前系统通常需要完成如下工作: 1.对调用函数的运行现场进行保护,主要是参数与返回地址等信息的保存; 2.创建被调用函数的运行环境; 3.将程序控制转移到调用函数的入口. 在被调用函数执行结束之后,返回调用函数之前,系统同样需要完成3件工作: 1.保存被调函数的返回结果; 2.释放被调用函数的数据区; 3.依照保存的调用函数的返回地址将程序控制转移到调用函数. 注意: 1.如果上述函数调用的过程中发生了新的调用,也就是被调函数在执行完成之前又调用了其他函数, 此时构成了多个函数的嵌套调用.2.当发生嵌套调用时按照后调用先返回的原则处理,如此则形成了一个保存函数运行时环境变量的后进先出的使用过程.3.因此整个函数调用期间的相关信息的保存需要一个堆栈来实现.4.系统在整个运行时需要的数据空间安排在一个堆栈中. 4.1每当调用一个函数时就为它在栈顶分配一个存储区; 4.2每当从一个函数返回时就释放它的存储区. 从底层过程理解递归调用 从上一节我们明白了函数之间是如何调用的了.那么一个递归算法的实现实际上就是多个相同函数的嵌套调用. 对上面N阶乘法的例子,具体说明,看图 从上图,我们知道了递归算法的实现原理.后调用的先返回,因此我们就是从递归的终止条件开始层层往上返回.再次说明了,递归算法必须得有终止条件. 但是: 我们也看到了递归算法在某些情况下并不一定是最高效的方法,主要原因在于递归方法过于频繁的函数调用和参数传递,这会使系统有较大的开销.在某些情况下,采用循环或递归算法的非递归实现,将会大大提高算法的实际执行效率. 基于归纳的递归 基于归纳的递归是一种较为简单并且也是一种基本的递归算法设计方法.它的主要思想是把数学归纳法应用于算法设计之中. 可以看看wiki上关于数学归纳法的解释,明白什么是数学归纳法,如何与递归结合起来.WIKI-数学归纳法 数学归纳法–>递归 从数学归纳法的思路推导到递归的算法思想 比如这个经典的例子 在这个例子里面,我们主要是要理解数学归纳法的思想和递归的思想是如何联系在一起的]]></content>
</entry>
<entry>
<title><![CDATA[VPS服务器搭建Shadowsocks服务]]></title>
<url>%2F2016%2FVPSshadowsocks%2F</url>
<content type="text"><![CDATA[购买自己的VPS服务器购买渠道 1.http://banwagong.cn/fangan.html 2.https://hostodo.com/vz.html 我就只在这两个网站上买过VPS.第一个网站是搬瓦工,好处是可以支付宝支付。最低的包年配置啥的还能接受。搭个梯子,建个blog没啥问题。第二个网站的包年vps的配置比搬瓦工的要好一点,而且价格差不多便宜一半。可惜是不能支付宝支付。 有了VPS后,在VPS上搭建ShadowSocks shadowsocks 是一个轻量级隧道代理,用来穿过防火墙。 我的VPS机器安装的是CentOS系统、所以下面的操作都是以CentOS为准、当然你要是用RedHat也是一样的操作方式。 1.安装Setuptools 1.先下载Setuptools的egg安装包 2.然后设置运行权限 3.最后./运行 123456wget --no-check-certificate https://pypi.python.org/packages/2.6/s/setuptools/setuptools-0.6c11-py2.6.eggchmod +x setuptools-0.6c11-py2.6.egg./setuptools-0.6c11-py2.6.egg 2.安装Python-pip 由于一些第三方库需要用pip来安装所以要把Python-pip安装上。 12345678wget --no-check-certificate https://pypi.python.org/packages/source/p/pip/pip-1.4.tar.gztar -zxvf ./pip-1.4.tar.gzcd pip-1.4sudo python setup.py install 3.安装Python-Gevent 为了提高性能Python-Gevent还是必须要安装的. 由于gevent需要用到libevent和python-devel所以我们需要现在这个.在CentOS下可以用yum install libevent python-devel来安装如果你的ubuntu的话可以用 apt-get来安装 123yum install libeventyum install python-develpip install gevent 4.安装Python-M2Crypto模块 M2Crypto是用于加密的第三库、由于众所周知的一些原因我们还是需要进行加密的。否则你的梯子可能不几天就被墙了。 首先需要先安装M2Crypto的一些依赖库 123yum install openssl-develyum install swigpip install M2Crypto 5.安装ShadowSocks-Python程序 ShadowSocks其实有很多版本可以选择的、这里我是选择了Python版本、当然你可以选择安装Shadowsocks-go、libev、libuv、nodejs、还有erlang版本。 1pip install shadowsocks 6.创建config.json配置文件 ShadowSocks的配置文件你可以随便找个目录自己创建即可.在不同的目录下创建不同端口的config.json文件,然后分别启动.就会生成多个账号 123mkdir ShadowSockscd ShadowSocksvim config.json config.json配置文件内容如下 12345678{"server":"my_server_ip","server_port":8388,"local_port":1080,"password":"barfoo!","timeout":600,"method":"aes-256-cfb"} 每一个字段的含义: server 服务器 IP (IPv4/IPv6),注意这也将是服务端监听的 IP 地址 server_port 服务器端口 local_port 本地端端口 password 用来加密的密码 timeout 超时时间(秒) method 加密方法,可选择 “bf-cfb”, “aes-256-cfb”, “des-cfb”, “rc4”, 等等。默认是一种不安全的加密,推荐用 “aes-256-cfb” 7.运行ShadowSocks程序 在服务器上cd到config.json所在的目录。运行ssserver即可 1sserver 一般来说我们都让其在后台一直运行的,所以用如下命令即可。 1nohup ssserver > log & 到此ShadowSocks-python服务端的所有安装设置都完成了下面我们需要用到客户端来操作。 8.下载客户端软件 这里不得不说下ShadowSocks做得如此之好、到底有多么的好、您能常见的系统都有客户端支持。 从Windows、Mac OS、Linux、Android、iOS、甚至支持OpenWRT。 Windows设置下载下载地址1下载地址2下载地址3 配置去ShadowSocks官网下载ShadowSocks-gui客户端。解压之后运行exe程序,然后设置好我们前面在config.json里面设置的内容即可。按照截图里进行填写 浏览器设置如果是Chrome请使用Proxy SwitchyShar插件、如果是Firefox请使用AutoProxy插件。下面以Chrome为例来说明。 在 Proxy SwitchyShar中新建立一个SockS5代理。 设置完成之后、就没有之后了。开始翻越吧少年。]]></content>
</entry>
<entry>
<title><![CDATA[设计模式之命令模式]]></title>
<url>%2F2016%2FCommandPattern%2F</url>
<content type="text"><![CDATA[命令模式 命令模式在于有命令 命令也许有很多个命令,因此需要有个命令模式去解决这个问题 既然是命令,那么肯定符合这么几个要求 命令本身 命令的接收者 命令模式怎么开始 构造命令 命令本身 命令的接收者 执行命令 我们只需要关心最上层模式,对底层命令的实现封装. 对命令模式做简单介绍 场景:餐厅点餐命令模式的运用场景命令模式能解决什么样的问题命令模式有什么问题如何实现运用命令模式]]></content>
</entry>
<entry>
<title><![CDATA[Scala写测试类]]></title>
<url>%2F2016%2FScalaTests%2F</url>
<content type="text"><![CDATA[Java中可以用Junit来写测试类,scala中当然也可以很方便的写测试类.在日常开发中,我们应该按照规范多写测试类.也不是为了测试一个方法,就写一个main方法,这样太不专业了. 添加Scala测试类相关依赖scalatest依赖添加以下依赖后,然后就可以在类中继承FunSuite类,就可以写测试类了 123456<dependency> <groupId>org.scalatest</groupId> <artifactId>scalatest_2.10</artifactId> <version>2.2.1</version> <scope>test</scope></dependency> scala测试报告插件如果嫌麻烦也不用加下面的插件.插件写这里,这是为了做记录12345678910111213141516171819<plugin> <groupId>org.scalatest</groupId> <artifactId>scalatest-maven-plugin</artifactId> <version>1.0</version> <configuration> <reportsDirectory>${project.build.directory}/surefire-reports</reportsDirectory> <junitxml>.</junitxml> <filereports>WDF TestSuite.txt</filereports> <argLine>-XX:MaxPermSize=256m -Xmx2g</argLine> </configuration> <executions> <execution> <id>test</id> <goals> <goal>test</goal> </goals> </execution> </executions></plugin> 12345678910111213141516171819202122232425262728293031<plugin> <groupId>net.alchim31.maven</groupId> <artifactId>scala-maven-plugin</artifactId> <version>3.2.2</version> <configuration> <scalaVersion>${scala.complete.version}</scalaVersion> <javacArgs> <javacArg>-source</javacArg> <javacArg>${java.version}</javacArg> <javacArg>-target</javacArg> <javacArg>${java.version}</javacArg> </javacArgs> </configuration> <executions> <execution> <id>scala-compile</id> <phase>process-resources</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>scala-test-compile</id> <phase>process-test-resources</phase> <goals> <goal>testCompile</goal> </goals> </execution> </executions> </plugin> 实践例子123456789101112131415161718192021222324252627282930import org.scalatest.{BeforeAndAfterAll, FunSuite, ShouldMatchers}/** * Created by hushiwei * desc : scala tests */class ScalaFunc extends FunSuite with BeforeAndAfterAll with ShouldMatchers{ override def beforeAll(): Unit = { println("test case start...") } test("hello world"){ val l1=List(1,2,3) val res=l1.flatMap(x=>x match{ case 3=>List('a','b') case _ => List(x*2) }) println(res) println("~"*50) val str="hello world" str should be ("hello world") assert(str.length==11) } override def afterAll(): Unit = { println("test case over...") }} 注意: 测试类必须是class,不能是object beforeAll与afterAll都是可选的. 与Junit都是类似的.]]></content>
</entry>
<entry>
<title><![CDATA[linux 添加用户、权限]]></title>
<url>%2F2016%2Flinuxuser%2F</url>
<content type="text"><![CDATA[1useradd –d /usr/sam -m sam 此命令创建了一个用户sam,其中-d和-m选项用来为登录名sam产生一个主目录/usr/sam(/usr为默认的用户主目录所在的父目录)。 修改密码假设当前用户是sam,则下面的命令修改该用户自己的口令:1234567 passwdOld password:******New password:*******Re-enter new password:******* 如果是超级用户,可以用下列形式指定任何用户的口令:12345 passwd samNew password:*******Re-enter new password:******* 添加用户首先用adduser命令添加一个普通用户,命令如下:123456adduser tommy //添加一个名为tommy的用户passwd tommy //修改密码Changing password for user tommy.New UNIX password: //在这里输入新密码Retype new UNIX password: //再次输入新密码passwd: all authentication tokens updated successfully. 赋予root权限 方法一: 修改 /etc/sudoers 文件,找到下面一行,把前面的注释()去掉 1234 Allows people in group wheel to run all commands%wheel ALL=(ALL) ALL然后修改用户,使其属于root组(wheel),命令如下:usermod -g root tommy 修改完毕,现在可以用tommy帐号登录,然后用命令 su - ,即可获得root权限进行操作。 方法二: 修改 /etc/sudoers 文件,找到下面一行,在root下面添加一行,如下所示: 123 Allow root to run any commands anywhereroot ALL=(ALL) ALLtommy ALL=(ALL) ALL 修改完毕,现在可以用tommy帐号登录,然后用命令 su - ,即可获得root权限进行操作。 linux用户操作增加用户123456789101112131415161718192021222324252627282930313233343536373839404142434445[root@bigdata mytest] useradd –helpUsage: useradd [options] LOGINOptions:-b, –base-dir BASE_DIR 设置基本路径作为用户的登录目录-c, –comment COMMENT 对用户的注释-d, –home-dir HOME_DIR 设置用户的登录目录-D, –defaults 改变设置-e, –expiredate EXPIRE_DATE 设置用户的有效期-f, –inactive INACTIVE 用户过期后,让密码无效-g, –gid GROUP 使用户只属于某个组-G, –groups GROUPS 使用户加入某个组-h, –help 帮助-k, –skel SKEL_DIR 指定其他的skel目录-K, –key KEY=VALUE 覆盖 /etc/login.defs 配置文件-m, –create-home 自动创建登录目录-l, 不把用户加入到lastlog文件中-M, 不自动创建登录目录-r, 建立系统账号-o, –non-unique 允许用户拥有相同的UID-p, –password PASSWORD 为新用户使用加密密码-s, –shell SHELL 登录时候的shell-u, –uid UID 为新用户指定一个UID-Z, –selinux-user SEUSER use a specific SEUSER for the SELinux user mapping 修改密码1passwd test 增加用户test,有一点要注意的,useradd增加一个用户后,不要忘了给他设置密码,不然不能登录的。 修改用户1234567usermod -d /home/test -G test2 test将test用户的登录目录改成/home/test,并加入test2组,注意这里是大G。gpasswd -a test test2 将用户test加入到test2组gpasswd -d test test2 将用户test从test2组中移出 删除用户1userdel test 将test用户删除 查看用户查看当前登录用户123[root@bigdata ~] w[root@bigdata ~] who 查看自己的用户名1[root@bigdata ~] whoami 查看单个用户信息123[root@bigdata ~] finger apacheuser[root@bigdata ~] id apacheuser 查看用户登录记录123[root@bigdata ~] last 查看登录成功的用户记录[root@bigdata ~] lastb 查看登录不成功的用户记录 查看所有用户123[root@bigdata ~] cut -d : -f 1 /etc/passwd[root@bigdata ~] cat /etc/passwd |awk -F \: '{print $1}' linux用户组操作创建组1groupadd test 增加一个test组 修改组1groupmod -n test2 test 将test组的名子改成test2 删除组1groupdel test2 删除 组test2 查看组 a)查看当前登录用户所在的组 groups,查看apacheuser所在组groups apacheuser b)查看所有组 cat /etc/group c)有的linux系统没有/etc/group文件的,这个时候看下面的这个方法 12cat /etc/passwd |awk -F [:] ‘{print $4}’ |sort|uniq | getent group |awk -F [:] ‘{print $1}’ 这里用到一个命令是getent,可以通过组ID来查找组信息,如果这个命令没有的话,那就很难查找,系统中所有的组了. 配置 ssh用useradd新增的用户不能直接用ssh远程访问,需要修改ssh相关配置如下: 12345vi /etc/ssh/sshd_config # 添加AllowUsers root@192.168.1.32 admin# 多个用户用空格隔开 如何让普通用户获得root用户的权限执行操作而不需要知道root用户的密码或向root用户进行切换呢?有一个命令sudo可以实现这个功能. Sudo的工作流程: (1) root用户编辑/etc/sudoers文件,添加要分配的普通用户记录,其中有这么一行记录:root ALL=(ALL) ALL,在这行后面添加:Sam ALL=(ALL) ALL (2) 那么sam用户在执行操作时,如果所执行的操作自己没有权限,则会去/etc/sudoers文件中查找是否有对应的记录,如果有的话则临时获得root权限,执行只有root才能执行的操作. 注意:这里会提示输入密码,但是输入的是sam用户的密码,而不是root用户的密码.例如: 123456Shell> sudo mkdir –p a/b/c/dWe trust you have received the usual lecture from the local SystemAdministrator. It usually boils down to these three things:1) Respect the privacy of others.2) Think before you type.3) With great power comes great responsibility. Password: 这里的密码是sam用户自己的密码,而不是root的密码 几个操作: 123456781) 编辑/etc/sudoers文件,使用visudo命令,Shell> visudo其实visudo命令也是调用vi去编辑sudoer文件的,但是在保存时会去检查你修改后文件的语法,如果错误是不能保存的,但是如果直接用vi来编辑该文件,强制保存虽然成功,但是如果有语法错误这里并不能给出提示,也就导致了后面sudo命令的不可用.2) 记录解析:Sam ALL=(ALL) ALL使用者帐户 登入的主机=(可以变换的身份) 可以下达的命令上述语句的意思是 sam用户可以在任何地方登录,并可切换成任何用户进行任何操作.这里如果(ALL)不写,只写为 sam ALL=ALL,他默认只能切换为root用户. linux chown命令参数及用法详解–改变档案的所有者:举例1要更改文件 program.c 的所有者:1chown jim program.c program.c 的用户访问权限现在应用到 jim。作为所有者,jim 可以使用 chmod 命令允许或拒绝其他用户访问 program.c。 举例2要将目录 /tmp/src 中所有文件的所有者和组更改为用户 john 和组 build:1234chown -R john:build /tmp/src# 如: tomcat的安装目录的所有者修改chown -R user:group /usr/java/tomcat5.5]]></content>
</entry>
<entry>
<title><![CDATA[Spark的RDD基础操作]]></title>
<url>%2F2016%2FSparkRDDOpe%2F</url>
<content type="text"><![CDATA[简单算子 1.构造一些数据 2.用map算子进行一些操作 3.用mapPartitions算子进行操作 4.调用zipwithpartition算子标记分区 5.调用foreachPartiton进行输出 6.调用glom算子收集每个分区的数据 7.调用flodleft函数处理Array 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 val conf = new SparkConf().setAppName("SimpleRDD").setMaster("local[*]") val sc = new SparkContext(conf) /** * 1.构造一些数据 * 2.用map算子进行一些操作 * 3.用mapPartitions算子进行操作 * 4.调用zipwithpartition算子标记分区 * 5.调用foreachPartiton进行输出 * 6.调用glom算子收集每个分区的数据 * 7.调用flodleft函数处理Array */ def printRDD(rdd:RDD[_])={ val str=rdd.collect().mkString(" , ") println(str) } val numberRDD=sc.parallelize(1 until 10,2) println("查看原始的RDD中的每一个元素:")// numberRDD.foreach(println)// printRDD(numberRDD) val numberRDD10=numberRDD.map(_*10) printRDD(numberRDD10) val numberRDDLess=numberRDD10.mapPartitions(iter=> { val arr=new ArrayBuffer[Double] while (iter.hasNext) { val value=iter.next().toDouble/10 arr+=value } arr.iterator }) printRDD(numberRDDLess) // glom 是把每个分区里面的元素放到一个list里面 val partitions=numberRDDLess.glom() println("构造数据的时候创建了 "+numberRDDLess.partitions.size+" 个分区") println("目前经过glom操作后,还有: "+partitions.count()+" 个分区,只是每个分区的数据存到到一个list中去了") partitions.foreach(arr=>{ println("分区数据:"+arr.mkString(" , ")) }) val numbers=partitions.zipWithIndex() numbers.foreach{ case (arr,v)=> println("第 "+ v+"个分区数据: "+arr.mkString(" , ")) } partitions.foreach(arr=>{ println("分区内容: "+ arr.foldLeft("")((x,y)=>x+" "+y)) }) Spark懒加载实践 通过例子说明,spark的确是当执行了一个action算子,才会生成一个job. 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 val conf = new SparkConf().setAppName("Computations").setMaster("local[*]") val sc = new SparkContext(conf) // 构造一个数据集 val numbers = sc.parallelize(1 to 10, 4) val bigger = numbers.map(n => n * 100) val biggerStill = bigger.map(n => n + 1) println("调用 toDebugString 算子去查看经过几次转换后,依赖关系是什么样:") println(biggerStill.toDebugString) // 进行一次reduce操作 val s = biggerStill.reduce(_ + _) println("sum = " + s) println("numbersRDD的id = " + numbers.id) println("biggerRDD的id = " + bigger.id) println("biggerStillRDD的id = " + biggerStill.id) println("查看biggerStill RDD 依赖继承关系: ") showDependency(biggerStill) val moreNumbers = bigger ++ biggerStill println("moreNumbers的依赖继承关系: ") println(moreNumbers.toDebugString) println("moreNumbers: id=" + moreNumbers.id) showDependency(moreNumbers) moreNumbers.cache() // cache操作可能会丢失数据,而且并没有发生依赖的变化 println("cached moreNumbers的依赖继承关系(并没有变化): ") println(moreNumbers.toDebugString) println("执行 cache 操作后,moreNumbers的依赖继承关系: ") showDependency(moreNumbers) println("检查一下moreNumbers有没有设置检查点? : " + moreNumbers.isCheckpointed) sc.setCheckpointDir("/tmp/sparkcps") moreNumbers.checkpoint() println("现在执行了checkpoint 检查一下moreNumbers有没有设置检查点? : " + moreNumbers.isCheckpointed) moreNumbers.count() println("现在执行了一个count操作 检查一下moreNumbers有没有设置检查点? : " + moreNumbers.isCheckpointed) println(moreNumbers.toDebugString) println("做了以上操作后,moreNumbers的依赖继承关系: ") showDependency(moreNumbers) println("这里不应该抛异常...") println("因为spark是懒加载,只有遇到action算子的时候,才会开始生成job开始调度计算....") val thisWillBlowUp = numbers map { case (7) => { throw new Exception } case (n) => n } println("异常应该在这里抛出来...") try { println(thisWillBlowUp.count()) } catch { case (e: Exception) => println("Nice,果真在这里抛异常了...") }}// 利用递归函数来输出rdd的依赖继承关系def showDependency[T](rdd: RDD[T]): Unit = { showDependency(rdd, 0)}private def showDependency[T](rdd: RDD[T], length: Int): Unit = { println("".padTo(length, ' ') + "RDD id= " + rdd.id) rdd.dependencies.foreach(dep => { showDependency(dep.rdd, length + 1) })} #]]></content>
</entry>
<entry>
<title><![CDATA[大数据学习时候,造数据的常用方式]]></title>
<url>%2F2016%2FMakeData%2F</url>
<content type="text"><![CDATA[一些提供数据的网站 Grouplens datahub profiledata_06-May-2005.tar.gz UCI Machine Learning Repository 搜狗实验室 large datasets list from quora kdnuggets AmazonPublicData 用log4j造假日志数据代码我就不贴了,用log4j来造假的日志数据是我们经常用到的方法.借助于随机数,以及一些数据结构,我们可以很方便的造一些文本数据出来.供我们测试程序.]]></content>
</entry>
<entry>
<title><![CDATA[Spark算子详解]]></title>
<url>%2F2016%2FSparkFunctions%2F</url>
<content type="text"><![CDATA[aggregate def aggregateU: ClassTag(seqOp: (U, T) => U, combOp: (U, U) => U): U 1234567891011121314151617181920212223 val conf=new SparkConf().setAppName("aggregate").setMaster("local[*]") val sc=new SparkContext(conf) val z=sc.parallelize(List(1,2,3,4,5,6),2)//打印每个分区里面的内容 z.glom().foreach(arr=>{ println(arr.foldLeft(" ")((x,y)=>x+y)) }) // This example returns 16 since the initial value is 5 设置处理值5,注意这个初始值会出现在每个每个分区中作为初始值。就像下面的,每个分区都有5 // reduce of partition 0 will be max(5, 1, 2, 3) = 5 // reduce of partition 1 will be max(5, 4, 5, 6) = 6 // final reduce across partitions will be 5 + 5 + 6 = 16 进行最后一个方法的时候,还会加上初始值5 // note the final reduce include the initial value 最后得出的才是结果 // 因此得出结论,aggregate聚合算子的三个参数分别是。第一个是设置初始值。第二个Sep函数是对每个分区中的内容进行操作的函数。第三个Combi函数是聚合每个分区的函数 val result=z.aggregate(0)(math.max(_,_),_+_) val result1=z.aggregate(5)(math.max(_,_),_+_) println("result: "+result) println("result1: "+result1)// 456// 123// result: 9// result1: 16 sc.stop() cartesian def cartesianU: ClassTag: RDD[(T, U)] 将其中一个分区里面的元素跟另一个分区的每一个元素进行笛卡尔积计算. 1234567891011121314val conf = new SparkConf().setAppName("Cartesian").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("A1", 1), ("A2", 2), ("B1", 3), ("B2", 4), ("C1", 5), ("C1", 6))val data2 = Array[(String, Int)](("A1", 7), ("A2", 8), ("B1", 9), ("C1", 0))val pairs1 = sc.parallelize(data1, 3)val pairs2 = sc.parallelize(data2, 2)val resultRDD = pairs1.cartesian(pairs2)resultRDD.foreach(println) collectAsMap 把元组作为元素的List转化成Map,主要是调用可变的HashMap 1234567891011121314151617181920 val conf = new SparkConf().setAppName("CollectAsMap").setMaster("local[*]") val sc = new SparkContext(conf) val data = Array[(String, Int)](("A", 1), ("B", 2), ("B", 3), ("C", 4), ("C", 5), ("C", 6)) // as same as "val pairs = sc.parallelize(data, 3)" 底层源码,也还是调用的parallelize val pairs = sc.makeRDD(data, 3) val result = pairs.collectAsMap // output Map(A -> 1, C -> 6, B -> 3) print(result)// list2map(data) } def list2map(list: Array[(String, Int)]): mutable.HashMap[String, Int] = { val map = new mutable.HashMap[String, Int] list.foreach(value=> map.put(value._1,value._2)) map flatMap def flatMapU: ClassTag: RDD[U] 123456val conf = new SparkConf().setAppName("FlatMap").setMaster("local[*]")val sc = new SparkContext(conf)val array=Array[String]("Hello","World","hadoop")val strRDD=sc.parallelize(array)val str2arrayRDD=strRDD.flatMap(x=>x.toCharArray)str2arrayRDD.foreach(println) GroupByKey def groupByKey(numPartitions: Int): RDD[(K, Iterable[V])] 会造成shuffle的算子,根据key分组 12345678910111213141516171819202122232425262728293031 var numMappers = 10 var numPartition=10 var numKVPairs = 100 var valSize = 100 var numReducers = 3 val conf = new SparkConf().setAppName("GroupBy Test").setMaster("local[*]") val sc = new SparkContext(conf)// 造一些测试数据// 0到10,10次循环,10个分区// 每次循环里面造100个键值对 val pairs1 = sc.parallelize(0 until numMappers, numPartition).flatMap { p => val ranGen = new Random var arr1 = new Array[(Int, Array[Byte])](numKVPairs) for (i <- 0 until numKVPairs) { val byteArr = new Array[Byte](valSize) ranGen.nextBytes(byteArr) arr1(i) = (ranGen.nextInt(10), byteArr) } arr1 }.cache // cache一下// 因此一共是10X100=1000个 println("pairs1.count: "+pairs1.count) val result = pairs1.groupByKey(numReducers)// 造数据的时候,key的取值是10以内,所以分组肯定是10个,和结果一样 println("result.count: "+result.count) println(result.toDebugString) sc.stop() groupBy def groupByK(implicit kt: ClassTag[K]): RDD[(K, Iterable[T])] 1234567891011121314151617val conf = new SparkConf().setAppName("GroupByAction").setMaster("local[*]")val sc = new SparkContext(conf)val data = Array[(String, Int)](("A1", 1), ("A2", 2), ("B1", 6), ("A2", 4), ("B1", 3), ("B1", 5))val pairs = sc.parallelize(data, 3)pairs.foreach(println)val result1 = pairs.groupBy(K => K._1)// 设置分区数量val result2 = pairs.groupBy((K: (String, Int)) => K._1,1)// 定义分区的类val result3 = pairs.groupBy((K: (String, Int)) => K._1, new RangePartitioner(3, pairs))result1.foreach(println)result2.foreach(println)result3.foreach(println) groupWith def groupWithW]): RDD[(K, (Iterable[V], Iterable[W]))] Alias for cogroup 12345678910111213141516val conf = new SparkConf().setAppName("GroupWith").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("A1", 1), ("A2", 2), ("B1", 3), ("B2", 4), ("C1", 5), ("C1", 6))val data2 = Array[(String, Int)](("A1", 7), ("A2", 8), ("B1", 9), ("C1", 0))val pairs1 = sc.parallelize(data1, 3)val pairs2 = sc.parallelize(data2, 2)val result = pairs1.groupWith(pairs2)result.foreach(println) join def joinW], partitioner: Partitioner): RDD[(K, (V, W))] 1234567891011121314151617val conf = new SparkConf().setAppName("JoinAction").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("A1", 1), ("A2", 2), ("B1", 3), ("B2", 4), ("C1", 5), ("C1", 6))val data2 = Array[(String, Int)](("A1", 7), ("A2", 8), ("B1", 9), ("C1", 0))val pairs1 = sc.parallelize(data1, 3)val pairs2 = sc.parallelize(data2, 2)val result = pairs1.join(pairs2)result.foreach(println) lookup def lookup(key: K): Seq[V] (针对pair类型的RDD)根据这个K值,找出相应的value值,放入到List里面返回出来 123456789101112val conf = new SparkConf().setAppName("LookUp").setMaster("local[*]")val sc = new SparkContext(conf)val data = Array[(String, Int)](("A", 1), ("B", 2), ("B", 3), ("C", 4), ("C", 5), ("C", 6))val pairs = sc.parallelize(data, 3)val finalRDD = pairs.lookup("B")finalRDD.foreach(println) mapPartitions def mapPartitionsU: ClassTag: RDD[U] 是针对这个RDD的每个partition进行操作的,比如在进行数据库链接的时候,使用这个算子,可以减少 1234567891011121314151617val conf = new SparkConf().setAppName("MapPartitionsRDD").setMaster("local[*]") val sc = new SparkContext(conf) val data = Array[(String, Int)](("A1", 1), ("A2", 2), ("B1", 1), ("B2", 4), ("C1", 3), ("C2", 4) ) val pairs = sc.parallelize(data, 3) val finalRDD = pairs.mapPartitions(iter => iter.filter(_._2 >= 2)) finalRDD.foreachPartition(iter => { while (iter.hasNext) { val next = iter.next() println(next._1 + " --- " + next._2) } }) mapValues def mapValuesU: RDD[(K, U)] 不改变RDD原有的分区,也不改变Key值,修改value的值 12345678910 val conf = new SparkConf().setAppName("mapValues").setMaster("local[*]") val sc = new SparkContext(conf) val data1 = Array[(String, Int)](("K", 1), ("T", 2), ("T", 3), ("W", 4), ("W", 5), ("W", 6) ) val pairs = sc.parallelize(data1, 3) val result = pairs.mapValues(V => 10 * V)// val result=pairs.map { case (k, v) => (k, v * 10) } result.foreach(println) partitionBy def partitionBy(partitioner: Partitioner): RDD[(K, V)] 用指定的分区类来处理RDD 12345678910val conf = new SparkConf().setAppName("partitionBy").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("K", 1), ("T", 2), ("T", 3), ("W", 4), ("W", 5), ("W", 6))val pairs = sc.parallelize(data1, 3)val result = pairs.partitionBy(new RangePartitioner(2, pairs, true))//val result = pairs.partitionBy(new HashPartitioner(2))result.foreach(println) pipe def pipe(command: String): RDD[String] command命令在windows上测不了额 123456789val conf = new SparkConf().setAppName("Pip").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("K1", 1), ("K2", 2), ("U1", 3), ("U2", 4), ("W1", 3), ("W2", 4))val pairs = sc.parallelize(data1, 3)val finalRDD = pairs.pipe("grep 2")finalRDD.foreach(println) reduce def reduce(f: (T, T) => T): T 迭代处理之前的元素 1234567891011val conf = new SparkConf().setAppName("reduce").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("K1", 1), ("K2", 2), ("U1", 3), ("U2", 4), ("W1", 3), ("W2", 4))val pairs = sc.parallelize(data1, 3)val result = pairs.reduce((A, B) => (A._1 + "#" + B._1, A._2 + B._2))//val result = pairs.fold(("K0",10))((A, B) => (A._1 + "#" + B._1, A._2 + B._2))println(result) reduceByKey reduceByKey(func: (V, V) => V): RDD[(K, V)] 有好几个重载方法,可以指定分区数,分区的类 123456789val conf = new SparkConf().setAppName("reduceByKey").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("K", 1), ("U", 2), ("U", 3), ("W", 4), ("W", 5), ("W", 6))val pairs = sc.parallelize(data1, 3)val result = pairs.reduceByKey(_ + _)result.foreach(println) sortByKey def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length): RDD[(K, V)] 12345678910111213val conf = new SparkConf().setAppName("sortByKey").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("K1", 1), ("K2", 2), ("U1", 3), ("U2", 4), ("W1", 5), ("W1", 6))val pairs1 = sc.parallelize(data1, 3)//val result = pairs.fold(("K0",10))((A, B) => (A._1 + "#" + B._1, A._2 + B._2))val result = pairs1.sortByKey()result.foreach(println) take def take(num: Int): Array[T] 123456789val conf = new SparkConf().setAppName("take").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("K1", 1), ("K2", 2), ("U1", 3), ("U2", 4), ("W1", 3), ("W2", 4))val pairs = sc.parallelize(data1, 3)val result = pairs.take(5)result.foreach(println) union def union(other: RDD[T]): RDD[T] union聚合算子,不会去重,如果需要去重,调用distinct算子 123456789101112131415val conf = new SparkConf().setAppName("union").setMaster("local[*]")val sc = new SparkContext(conf)val data1 = Array[(String, Int)](("K1", 1), ("K2", 2), ("U1", 3), ("U2", 4), ("W1", 5), ("W1", 6))val data2 = Array[(String, Int)](("K1", 7), ("K2", 8), ("U1", 9), ("W1", 0))val pairs1 = sc.parallelize(data1, 3)val pairs2 = sc.parallelize(data2, 2)val result = pairs1.union(pairs2)result.foreach(println) checkpoint def checkpoint(): Unit 设置了checkpoint后,写下来的数据,不会因为应用的停止而删除 12345678910111213141516171819202122232425262728293031323334var numMappers = 10var numPartition = 10var numKVPairs = 100var valSize = 100var numReducers = 3val conf = new SparkConf().setAppName("Checkpoint").setMaster("local[*]")val sc = new SparkContext(conf)sc.setCheckpointDir("D:\\data\\checkpoint")// 造一些测试数据// 0到10,10次循环,10个分区// 每次循环里面造100个键值对val pairs1 = sc.parallelize(0 until numMappers, numPartition).flatMap { p => val ranGen = new Random var arr1 = new Array[(Int, Array[Byte])](numKVPairs) for (i <- 0 until numKVPairs) { val byteArr = new Array[Byte](valSize) ranGen.nextBytes(byteArr) arr1(i) = (ranGen.nextInt(10), byteArr) } arr1}.cache// cache一下// 因此一共是10X100=1000个println("pairs1.count: " + pairs1.count)val result = pairs1.groupByKey(numReducers)result.checkpoint// 造数据的时候,key的取值是10以内,所以分组肯定是10个,和结果一样println("result.count: " + result.count)println(result.toDebugString)sc.stop() zip def zipU: ClassTag: RDD[(T, U)] 注意: zip算子默认是认为这两个RDD的分区数和每个分区里面的个数是一样的,如果不能保证这两个条件,那么就会报错。如果不符合,那么可以用zipPartitions 123456789101112131415161718192021 val conf = new SparkConf().setAppName("zip").setMaster("local[*]") val sc = new SparkContext(conf) // 构造数据集 val numberRDD = sc.parallelize(1 to 20, 4) val numberRDDCopy = sc.parallelize(1 to 20, 4) val letterRDD = sc.parallelize('a' to 'z', 4)// val result = numberRDD.zip(letterRDD) val result = numberRDD.zip(numberRDDCopy) try { result foreach { case (c, i) => println(i + ": " + c) } } catch { case iae: IllegalArgumentException => println("Exception caught IllegalArgumentException: " + iae.getMessage) case se: SparkException => { val t = se.getMessage println("Exception caught SparkException: " + se.getMessage) } } zipPartitions def zipPartitionsB: ClassTag, V: ClassTag(f: (Iterator[T], Iterator[B]) => Iterator[V]): RDD[V] 这个的灵活性就比zip算子高得多了 1234567891011121314151617181920212223242526272829303132333435363738 val conf = new SparkConf().setAppName("zipPartitions").setMaster("local[*]") val sc = new SparkContext(conf) // 构造数据集 val numberRDD = sc.parallelize(1 to 20, 4) val letterRDD = sc.parallelize('a' to 'z', 4)// printRDD(numberRDD)// printRDD(letterRDD)// 假设所有的partiton里面的元素个数是一样,当不一样的时候,我们需要处理一下咯 def func(numIter: Iterator[Int], lettIter: Iterator[Char]): Iterator[(Int, Char)] = { val arr = new ListBuffer[(Int, Char)] while (numIter.hasNext || lettIter.hasNext) { if (numIter.hasNext && lettIter.hasNext) { arr += ((numIter.next(), lettIter.next())) } else if (numIter.hasNext) { arr += ((numIter.next(), ' ')) } else if (lettIter.hasNext) { arr += ((0, lettIter.next())) } } arr.iterator } val result=numberRDD.zipPartitions(letterRDD)(func) result.foreach{ case (k,v)=>println(k+" "+v) } } def printRDD(rdd: RDD[_]) = { val nrdd = rdd.glom().zipWithIndex() nrdd.foreach { case (k, v) => println("第" + v + "个分区数据: " + k.foldLeft("")((x, y) => x + " " + y)) } }]]></content>
</entry>
<entry>
<title><![CDATA[Scala集合]]></title>
<url>%2F2016%2FScalaCollection%2F</url>
<content type="text"><![CDATA[Scala集合scala有一组丰富的集合库:List,Tuple,Option,Map 集合可能是严格或懒惰 集合可以是可变的或不可变 不可变的集合可能包含可变项 ListList与Array区别 相同点 所有元素都具有相同的类型 不同点 列表是不可变的,这意味着一个列表的元素可以不被分配来改变. 列表底层是一个链表,而数组是平坦的. 定义Scala的List列表1.有以下几种常用的方式来定义列表 12345678910111213141516// List of Stringsval fruit: List[String] = List("apples", "oranges", "pears")// List of Integersval nums: List[Int] = List(1, 2, 3, 4)// Empty List.val empty: List[Nothing] = List()// Two dimensional listval dim: List[List[Int]] = List( List(1, 0, 0), List(0, 1, 0), List(0, 0, 1) ) 2.所有的列表可以使用两种基本的构建模块来定义,一个无尾Nil和::,但是这有明显的缺点.Nil也代表了空列表.上述的例子,也可以这样写 12345678910111213// List of Stringsval fruit = "apples" :: ("oranges" :: ("pears" :: Nil))// List of Integersval nums = 1 :: (2 :: (3 :: (4 :: Nil)))// Empty List.val empty = Nil// Two dimensional listval dim = (1 :: (0 :: (0 :: Nil))) :: (0 :: (1 :: (0 :: Nil))) :: (0 :: (0 :: (1 :: Nil))) :: Nil ::符号就可以简单的替代了List()方法 列表的基本操作 主要针对列表的头尾合是否为空来说 方法 描述 head 此方法返回的列表中的第一个元素 tail 此方法返回一个由除了第一个元素外的所有元素的列表 isEmpty 如果列表为空,此方法返回true,否则为false 1234567891011object Test { def main(args: Array[String]) { val fruit = "apples" :: ("oranges" :: ("pears" :: Nil)) val nums = Nil println( "Head of fruit : " + fruit.head ) println( "Tail of fruit : " + fruit.tail ) println( "Check if fruit is empty : " + fruit.isEmpty ) println( "Check if nums is empty : " + nums.isEmpty ) }} 串联列表 可以使用::运算符或者列表List.:::()方法来添加两个或多个列表 eg: 1234567891011121314151617181920object Test { def main(args: Array[String]) { val fruit1 = "apples" :: ("oranges" :: ("pears" :: Nil)) val fruit2 = "mangoes" :: ("banana" :: Nil) // use two or more lists with ::: operator var fruit = fruit1 ::: fruit2 println( "fruit1 ::: fruit2 : " + fruit ) // use two lists with Set.:::() method fruit = fruit1.:::(fruit2) println( "fruit1.:::(fruit2) : " + fruit ) // pass two or more lists as arguments fruit = List.concat(fruit1, fruit2) println( "List.concat(fruit1, fruit2) : " + fruit ) }} :: 元素之间进行连接,:::列表之间进行连接 创建统一列表 可以使用List.fill()方法来创建,包括相同的元素如下的零个或更多个拷贝的列表 eg 123456789object Test { def main(args: Array[String]) { val fruit = List.fill(3)("apples") // Repeats apples three times. println( "fruit : " + fruit ) //fruit : List(apples, apples, apples) val num = List.fill(10)(2) // Repeats 2, 10 times. println( "num : " + num ) //num : List(2, 2, 2, 2, 2, 2, 2, 2, 2, 2) }} 反向列表顺序 可以使用List.reverse方法来扭转列表中的所有元素 eg 123456789object Test { def main(args: Array[String]) { val fruit = "apples" :: ("oranges" :: ("pears" :: Nil)) println( "Before reverse fruit : " + fruit ) // Before reverse fruit : List(apples, oranges, pears) println( "After reverse fruit : " + fruit.reverse ) // After reverse fruit : List(pears, oranges, apples) }} Scala列表常用方法 方法 描述 def +(elem: A): List[A] 前置一个元素列表 def ::(x: A): List[A] 在这个列表的开头添加的元素。返回一个新的列表 def :::(prefix: List[A]): List[A] 增加了一个给定列表中该列表前面的元素。返回一个新的列表 def contains(elem: Any): Boolean 测试该列表中是否包含一个给定值作为元素。 def distinct: List[A] 建立从列表中没有任何重复的元素的新列表。 def drop(n: Int): List[A] 返回除了第n个的所有元素。 def endsWithB: Boolean 测试列表是否使用给定序列结束。 …… …… Set Set集合是不包含重复元素的集合 默认情况下,Scala中使用不可变的集.如果想使用可变集,必须明确导入sala.collection.mutable.Set类 声明Set集合类1234567// Empty set of integer typevar s : Set[Int] = Set()// Set of integer typevar s : Set[Int] = Set(1,3,5,7)orvar s = Set(1,3,5,7) 集合基本操作 集合所有操作可以体现在以下三个方法 方法 描述 head 此方法返回集合的第一个元素。 tail|该方法返回集合由除第一个以外的所有元素。||isEmpty|如果设置为空,此方法返回true,否则为false。| eg: 1234567891011object Test { def main(args: Array[String]) { val fruit = Set("apples", "oranges", "pears") val nums: Set[Int] = Set() println( "Head of fruit : " + fruit.head ) println( "Tail of fruit : " + fruit.tail ) println( "Check if fruit is empty : " + fruit.isEmpty ) println( "Check if nums is empty : " + nums.isEmpty ) }} 串联Set集合 可以使用++运算符或集。++()方法来连接两个或多个集,但同时增加了集它会删除重复的元素 eg 1234567891011121314object Test { def main(args: Array[String]) { val fruit1 = Set("apples", "oranges", "pears") val fruit2 = Set("mangoes", "banana") // use two or more sets with ++ as operator var fruit = fruit1 ++ fruit2 println( "fruit1 ++ fruit2 : " + fruit ) // use two sets with ++ as method fruit = fruit1.++(fruit2) println( "fruit1.++(fruit2) : " + fruit ) }} ScalaSet集合常用方法 方法 描述 def +(elem: A): Set[A] 创建一组新的具有附加元件,除非该元件已经存在 def -(elem: A): Set[A] 创建一个新的从这个集合中删除一个给定的元素 def contains(elem: A): Boolean 如果elem包含在这个集合返回true,否则为false。 def take(n: Int): Set[A] 返回前n个元素 …… …… Map 在scala中的映射是键/值对的集合即是Map 任何值可以根据它的键进行检索 键是在映射中唯一的,但值不一定是唯一的 映射也被称为哈希表 有两种映射,不可变的以及可变的.不可变的意味着对象本身是不可变的. 默认情况下,scala中使用不可变的映射.要想使用可变集,必须明确导入scala.collection.mutable.Map类 声明Map类12345// Empty hash table whose keys are strings and values are integers:var A:Map[Char,Int] = Map()// A map with keys and values.val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF") 映射的基本操作 在映射上的所有操作可被表示在下面的三种方法 方法 描述 keys 这个方法返回一个包含映射中的每个键的迭代。 values 这个方法返回一个包含映射中的每个值的迭代。 isEmpty 如果映射为空此方法返回true,否则为false。 举例1234567891011121314object Test { def main(args: Array[String]) { val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF", "peru" -> "#CD853F") val nums: Map[Int, Int] = Map() println( "Keys in colors : " + colors.keys ) println( "Values in colors : " + colors.values ) println( "Check if colors is empty : " + colors.isEmpty ) println( "Check if nums is empty : " + nums.isEmpty ) }} 给可变映射添加元素1234A += ('I' -> 1)A += ('J' -> 5)A += ('K' -> 10)A += ('L' -> 100) 串联映射 可以使用++运算符或映射。++()方法来连接两个或更多的映射,但同时增加了映射,将删除重复的键 eg 12345678910111213141516171819object Test { def main(args: Array[String]) { val colors1 = Map("red" -> "#FF0000", "azure" -> "#F0FFFF", "peru" -> "#CD853F") val colors2 = Map("blue" -> "#0033FF", "yellow" -> "#FFFF00", "red" -> "#FF0000") // use two or more Maps with ++ as operator var colors = colors1 ++ colors2 println( "colors1 ++ colors2 : " + colors ) // use two maps with ++ as method colors = colors1.++(colors2) println( "colors1.++(colors2)) : " + colors ) }} 遍历Map 方法很多,模式匹配最方便了 1234567891011121314151617181920val colors = Map("red" -> "#FF0000", "azure" -> "#F0FFFF", "peru" -> "#CD853F")println("第一种遍历")colors.keys.foreach{key=>print("key= "+key)println(" value= "+colors(key))}println("第二种遍历")for(key <- colors.keySet.toArray) { println(key+" : "+colors.get(key))}println("第三种遍历")colors.map{ case (k,v)=>println(k+" : "+v)} scala中Map的常用方法 方法 描述 def contains(key: A): Boolean 如果有一个绑定在该映射的键返回true,否则为false。 def clone(): Map[A, B] 创建接收器对象的副本 def clear(): Unit 从映射中删除所有绑定。在此之后操作已完成时,映射将是空的。 ….. ….. Tuple Scala的元组结合多个固定数量在一起,使它们可以被传来传去作为一个整体. 特点 不像数组或列表,元组可以容纳不同类型的对象 同时记住,元组也是不可改变的 scala目前的元组上限在22个 声明Tuple元组一个元组的实际类型取决于它包含的元素和这些元素的类型的数目 123456789val t = (1, "hello", Console)这是语法修饰(快捷方式)以下:val t = new Tuple3(1, "hello", Console)//类型:// (99, "Luftballons") 是 Tuple2[Int, String]// ('u', 'r', "the", 1, 4, "me") 的类型是 Tuple6[Char, Char, String, Int, Int, String] 访问元组中的元素123456789object Test { def main(args: Array[String]) { val t = (4,3,2,1) val sum = t._1 + t._2 + t._3 + t._4 println( "Sum of elements: " + sum ) }} 遍历元组 可以使用Tuple.productIterator()方法来遍历一个元组的所有元素 123456object Test { def main(args: Array[String]) { val t = (4,3,2,1) t.productIterator.foreach{ i =>println("Value = " + i )} }} 元组的简单方法1.转换为字符串2.交换元素 12345678910object Test { def main(args: Array[String]) { val t1 = new Tuple3(1, "hello", Console) println("Concatenated String: " + t1.toString() ) val t2 = new Tuple2("Scala", "hello") println("Swapped Tuple: " + t2.swap ) }}]]></content>
</entry>
<entry>
<title><![CDATA[Scala数组]]></title>
<url>%2F2016%2FScalaArray%2F</url>
<content type="text"><![CDATA[Scala数组 说明:存储相同类型的元素的固定大小的连续集合 相同类型 固定大小 声明数组变量在程序中要使用数组,那么就需要先声明一个变量来引用数组.12345var arr:Array[String]=new Array[String](3)orvar arr=new Array[String](3)orvar arr=Array("hello","world","scala") 在这里,arr被声明为字符串数组,最多可以容纳三个变量. 给数组变量赋值有以下的方式给数组变量赋值 1234arr(0)="hello"arr(1)="world"arr(4/2)="scala"创建的时候就赋值 处理数组当要处理数组元素的时候,根据数组元素具有相同的类型和数组的大小已知这些特点,我们经常就会使用循环来处理数组. 创建,初始化,处理数组 12345678910111213141516171819202122232425object Test { def main(args: Array[String]) { var myList = Array(1.9, 2.9, 3.4, 3.5) // Print all the array elements for ( x <- myList ) { println( x ) } // Summing all elements var total = 0.0; for ( i <- 0 to (myList.length - 1)) { total += myList(i); } println("Total is " + total); // Finding the largest element var max = myList(0); for ( i <- 1 to (myList.length - 1) ) { if (myList(i) > max) max = myList(i); } println("Max is " + max); }} 可变数组 在程序中,我们常常需要可变的数组,ArrayBuffer 12345val buffer=new ArrayBuffer[Int]for (i<- 0 until 10 if i%2==0) { buffer.+=(i)}println(buffer.mkString(" - ")) scala多维数组 在很多情况下,仅仅只用一维数组是没法解决问题的.因此我们需要定义多维数组(即数组的元素也是数组) 例如,矩阵 scala不直接支持多维数组,但提供各种方法来处理任何尺寸数组 创建scala多维数组12import Array._var myMatrix=ofDim[Int](3,3) myMatrix是一个具有每个元素都是整数,它是一个3X3的数组 处理scala多维数组1234567891011121314151617181920212223import Array._object Test { def main(args: Array[String]) { var myMatrix = ofDim[Int](3,3) // build a matrix for (i <- 0 to 2) { for ( j <- 0 to 2) { myMatrix(i)(j) = j; } } // Print two dimensional array for (i <- 0 to 2) { for ( j <- 0 to 2) { print(" " + myMatrix(i)(j)); } println(); } }} 联接scala数组 使用concat()方法来连接两个数组 123456789101112131415import Array._object Test { def main(args: Array[String]) { var myList1 = Array(1.9, 2.9, 3.4, 3.5) var myList2 = Array(8.9, 7.9, 0.4, 1.5) var myList3 = concat( myList1, myList2) // Print all the array elements for ( x <- myList3 ) { println( x ) } }} 创建具有范围的数组(常用) 该方法很实用,可以方便的创建一个数组供测试或者开发用 range() 产生包含在给定的范围内增加整数序列的数组.最后一个参数是步长,默认是1 1234567891011121314151617import Array._object Test { def main(args: Array[String]) { var myList1 = range(10, 20, 2) var myList2 = range(10,20) // Print all the array elements for ( x <- myList1 ) { print( " " + x ) } println() for ( x <- myList2 ) { print( " " + x ) } }}]]></content>
</entry>
<entry>
<title><![CDATA[设计模式]]></title>
<url>%2F2016%2FDesignPattern%2F</url>
<content type="text"><![CDATA[命令模式未完待续…..]]></content>
</entry>
<entry>
<title><![CDATA[PySpark做日志分析]]></title>
<url>%2F2016%2FSparkExample%2F</url>
<content type="text"><![CDATA[第一步:给你的样本日志目录修改这个参数在这个例子里面,我们会用到这个样本文件”/databricks-datasets/sample_logs”,样本数据已经存储到DBFS(Databricks的文件系统) DBFS_SAMPLES_LOGS_FOLDER=”/databricks-datasets/samples_logs” #path to the log file to be analyzed 第二步:为Apache访问日志的每一行创建一个解析器以此来创建一个Row对象 每行日志的格式为了分析这些数据,我们需要去把他们解析成可看的格式对与这个实验,我们要用的日志数据在Apache Common Log Format( clf).CLF生产的日志文件条目将如下所示: 127.0.0.1 - - [01/Aug/1995:00:00:01 -0400] “GET /images/launch-logo.gif HTTP/1.0” 200 1839以下是摘要,描述日志记录的每一部分 127.0.0.1 这个是访问服务器的客户端(远程主机)的IP地址(或者是主机名,可选) - 这个”连字符”在输出中表示继续请求的信息(从远程机器来的身份)是不可用的 - 这个”连字符”在输出中表示继续请求的信息(从本地登录的用户身份)是不可用的 [01/Aug/1995:00:00:01 -0400] 服务器完成请求的时间.格式是这样的: day=2 digits month=3 letters year=4 digits hour=2 digits minute=2 digits second=2 digits zone=(+|-)4 digits “GET /images/launch-logo.gif HTTP/1.0” 这个是从客户端来的请求字符串的第一行。它是由三部分组成的:访问的方式(e.g.,GET,POST,etc),终端(统一资源标识符),还有客户端的协议版本 200 这个是服务器返回给客户端的状态码.这个信息是非常重要的,因为它显示了该请求是否是一个成功的响应(状态码以2开始),一个重定向(状态码以3开始),一个由客户端导致的错误(状态码以4开始),或者一个在服务器的错误(状态码以5开始).可获得的状态代码的完整列表可以在HTTP规范中找到RFC 2616 section 10 1839 这最后一个记录显示返回给客户端的对象的大小,不包括响应headers.如果没有内容返回给客户端,那么这个值可能是”-“(或者有时候是0)因此,对于有恶意的客户端可能会插入控制日志文件中的字符,所以必须注意处理源日志. 日志示例1234564.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 1284664.242.88.10 - - [07/Mar/2004:16:06:51 -0800] "GET /twiki/bin/rdiff/TWiki/NewUserTemplate?rev1=1.3&rev2=1.2 HTTP/1.1" 200 452364.242.88.10 - - [07/Mar/2004:16:10:02 -0800] "GET /mailman/listinfo/hsdivision HTTP/1.1" 200 629164.242.88.10 - - [07/Mar/2004:16:11:58 -0800] "GET /twiki/bin/view/TWiki/WikiSyntax HTTP/1.1" 200 735264.242.88.10 - - [07/Mar/2004:16:20:55 -0800] "GET /twiki/bin/view/Main/DCCAndPostFix HTTP/1.1" 200 5253 12345678910111213141516171819import refrom pyspark.sql import RowAPACHE_ACCESS_LOG_PATTERN='^(\S+) (\S+) (\S+) \[([\w:/]+\s[+\-]\d{4})\] "(\S+) (\S+) (\S+)" (\d{3}) (\d+)'def parse_apache_log_line(logline): match=re.search(APACHE_ACCESS_LOG_PATTERN,logline) if match is None: raise Exception("Invalid logline: %s" % logline) return Row( ipAddress=match.group(1), clientIdentd=match.group(2), userId=match.group(3), dateTime=match.group(4), method=match.group(5), endpoint=match.group(6), protocol=match.group(7), responseCode=int(match.group(8)), contentSize=long(match.group(9))) 第三步:把日志文件的所有行加载到Spark RDD中(弹性式分布数据集)12345conf=SparkConf().setAppName("Parse Apache Log").setMaster("local[*]")sc=SparkContext(conf=conf)path="hdfs://ncp162:8020/hsw/access_log"access_logs=sc.textFile(path).map(parse_apache_log_line).cache()print access_logs.count() 第四步:计算统计通过GET请求返回的内容大小123456789content_sizes=access_logs.map(lambda row:row.contentSize).cache()# 计算页面大小的平均值average_content_size=content_sizes.reduce(lambda x,y:x+y)/content_sizes.count()# 计算最小的页面min_content_size=content_sizes.min()# 计算最大的页面max_content_size=content_sizes.max()print "Content Size Statistics:\n Avg: %s\n Min: %s\n Max: %s" % (average_content_size,min_content_size,max_content_size) 第5步:计算统计返回状态码123# 计算各个返回状态码的个数response_code_to_count_pair_rdd = access_logs.map(lambda row: (row.responseCode, 1)).reduceByKey(lambda x, y: x + y)print response_code_to_count_pair_rdd.take(100) 第6步:展示访问这个服务端超过N次的IP列表123456n=10ip_addresses_rdd=access_logs.map(lambda row:(row.ipAddress,1)).reduceByKey(lambda x,y:x+y)\ .filter(lambda s:s[1]>n).map(lambda s:Row(ip_address=s[0]))print ip_addresses_rdd.collect()ip_addresses_dataframe=sqlContext.createDataFrame(ip_addresses_rdd)print ip_addresses_dataframe.rdd.toDebugString() 第7步:研究统计endpoints信息12345endpoint_counts_rdd=access_logs.map(lambda row:(row.endpoint,1))\ .reduceByKey(lambda x,y:x+y).map(lambda s:Row(endpoint=s[0],num_hits=s[1]))print endpoint_counts_rdd.collect()top_endpoints_array=endpoint_counts_rdd.takeOrdered(10,lambda row:-1*row.num_hits) top_endpoints_dataframe=sqlContext.createDataFrame(sc.parallelize(top_endpoints_array)) 附加:在Python中用SQL 一个dataframe能够被注册成一个临时的SQL表 然后你能够争对数据做SQL查询]]></content>
</entry>
<entry>
<title><![CDATA[ScalaNote]]></title>
<url>%2F2016%2FScalaNote%2F</url>
<content type="text"><![CDATA[1.sealed trait 被sealed 声明的 trait仅能被同一文件的的类继承。 除了这个,我们通常将sealed用于枚举中,因为编译器在编译的时候知道这个trait被哪些类继承过,因此我们在match时对sealed trait进行case 的时候,如果你没有判断全部编译器在编译时就会报错。12345678@DeveloperApisealed trait JobResult@DeveloperApicase object JobSucceeded extends JobResult@DeveloperApiprivate[spark] case class JobFailed(exception: Exception) extends JobResult 2.assert Scala的assert方法检查传入的Boolean并且如果是假,抛出AssertionError。如果传入的Boolean是真,assert只是静静地返回。 3.偏函数(Partial Function) 偏函数是只对函数定义域的一个子集进行定义的函数。 scala中用scala.PartialFunction[-T, +S]类来表示因为偏函数都只对其定义域Int的部分值做了处理. 那么定义成偏函数的额外好处是, 你可以在调用前使用一个isDefinedAt方法, 来校验参数是否会得到处理. 或者在调用时使用一个orElse方法, 该方法接受另一个偏函数,用来定义当参数未被偏函数捕获时该怎么做. 也就是能够进行显示的声明. 在实际代码中最好使用PartialFunction来声明你确实是要定义一个偏函数, 而不是漏掉了什么. 比如定义了一个函数: 1def sum(x: Int)(y: Int) = x + y 当调用sum的时候,如果不提供所有的参数或某些参数还未知时,比如 123sum _sum(3)(_: Int)sum(_: Int)(3) 这样就生成了所谓的部分应用函数。部分应用函数只是逻辑上的一个表达,scala编译器会用Function1, Function2这些类来表示它. 下面这个变量signal引用了一个偏函数 1234val signal: PartialFunction[Int, Int] = { case x if x > 1 => 1 case x if x < -1 => -1} 这个signal所引用的函数除了0值外,对所有整数都定义了相应的操作。signal(0) 会抛出异常,因此使用前最好先signal.isDefinedAt(0)判断一下。 偏函数主要用于这样一种场景:对某些值现在还无法给出具体的操作(即需求还不明朗),也有可能存在几种处理方式(视乎具体的需求);我们可以先对需求明确的部分进行定义,比如上述除了0外的所有整数域,然后根据具体情况补充对其他域的定义,比如 : 12345val composed_signal: PartialFunction[Int,Int] = signal.orElse{case 0 => 0}composed_signal(0) // 返回 0 或者对定义域进行一定的偏移(假如需求做了变更, 1 为无效的点) 1234567val new_signal: Function1[Int, Int] = signal.compose{ case x => x - 1}new_signal(1) // throw exceptionnew_signal(0) // 返回 -1new_signal(2) // 返回 1 还可以用andThen将两个相关的偏函数串接起来 1234567val another_signal: PartialFunction[Int, Int] = { case 0 => 0 case x if x > 0 => x - 1 case x if x < 0 => x + 1}val then_signal = another_signal andThen signal 这里的then_signal 剔除了-1, 0, 1三个点的定义 4.部分应用函数(Partial Applied Function) 部分应用函数,是指一个函数有N个参数,而我们为其提供少于N个参数,那就得到了一个部分应用函数. 比如我先定义一个函数 1def sum(a:Int,b:Int,c:Int)=a+b+c; 那么就可以从这个函数衍生出一个偏函数是这样的: 1def p_sum=sum(1,_:Int,_:Int) 于是就可以这样调用psum(2,3), 相当于调用sum(1,2,3) 得到的结果是6. 这里的两个分别对应函数sum对应位置的参数. 所以你也可以定义成 1def p_sum = sum (_:Int, 1, _:Int) 这东西有啥用呢? 一个是当你在代码中需要多次调用一个函数, 而其中的某个参数又总是一样的时候, 使用这个可以使你少敲一些代码. 另一个呢? 5.函数与闭包函数 函数式编程风格的一个重要设计原则:函数是一等公民,一个程序可以被解构成若干个小的函数,每个完成一个定义良好的任务 1.本地函数Scala中提供了可以把函数定义在另一个函数中。就好象本地变量那样,这种本地函数仅在包含它的代码块中可见。 123456789101112131415def processFile(fileName:String,width:Int){ // 本地函数 def processLine(line:String){ // 长度大数指定的长度,进行打印 if(line.length() > width){ println(fileName+":"+line) } } // 读取文件 val source = Source.fromFile("E:\\input.txt"); for(line <- source.getLines()){ processLine(line); } } processLine的定义放在processFile的定义里。作为本地函数,processLine的范围局限于processFile之内,外部无法访问。]]></content>
</entry>
<entry>
<title><![CDATA[浅谈逻辑]]></title>
<url>%2F2016%2FWriterLogical%2F</url>
<content type="text"><![CDATA[问题 写着写着,突然写不下去了 被外界打断了,不知道怎么写下去 写完之后,逻辑不通发现自己都看不懂 那么一旦脑子里有了清晰完整的逻辑,这类问题,即将迎刃而解了.如何做到呢? SCAQ结构 第一次看到这个SCAQ结构的时候,眼前一亮,确实是个很实用的办法.当然,无论多么好的思路,也需要花时间去实践.不然也没有什么用处.举例: 1234567S:我们都知道最近发生了……C:但是这与我们的……目标有所冲突Q:现在的当务之急是需要在……时间内解决A:我们的解决方案建议是…… SCAQ结构既解答了事情重要性,抓住听众的注意力,又提供了解决方案,非常适合做工作汇报。 2W1H2W1H是下面的5W2H的简化版,有时候也只需要这么简单就能做好呢, Why :为什么要这样做 What :这是什么 How :如何达到目标 简单来说,就是为什么是什么怎么办。俗话说大道至简,要想运用好这些简单的办法,需要在生活中留心留意的去思考和实践。 5W2H WHAT——是什么?目的是什么?做什么工作? HOW ——怎么做?如何提高效率?如何实施?方法怎样? WHY——为什么?为什么要这么做?理由何在?原因是什么?造成这样的结果为什么? WHEN——何时?什么时间完成?什么时机最适宜? WHERE——何处?在哪里做?从哪里入手? WHO——谁?由谁来承担?谁来完成?谁负责? HOW MUCH——多少?做到什么程度?数量如何?质量水平如何?费用产出如何? 金字塔原理未完待续…]]></content>
</entry>
<entry>
<title><![CDATA[机器学习实战之决策树]]></title>
<url>%2F2016%2FDecisitionTree%2F</url>
<content type="text"><![CDATA[什么是决策树决策树的概念 如图1: 图1中就是一个决策树,长方形代表判断模块,椭圆形代表终止模块,表示已经得出结论,可以终止运行.从判断模块引出的左右箭头称作分支.它可以到达另一个判断模块或者终止模块. 决策树的特点 优点:计算复杂度不高,输出结果易于理解,对中间值的缺失不敏感,可以处理不相关特征数据 缺点:可能会产生过度匹配问题 使用数据类型:数值型和标称型 决策树的一般流程 收集数据:可以使用任何方法 准备数据:树构造算法只适用于标称型数据,因此数值型数据必须离散化 分析数据:可以使用任何方法 构造树完成之后,我们应该检查图形是否符合预期 训练算法:构造树的数据结构 测试算法:使用经验树计算错误率 使用算法:此步骤可以适用于任何监督学习算法 如何构建决策树 接下来我们将学习如何从一堆原始数据中构造决策树 计算信息增益来度量信息 我们需要解决的第一个问题就是,当前数据集上哪个特征在划分数据分类时起决定性作用.为了找到决定性的特征,划分出最好的结果,我们必须评估每个特征. 如何对数据进行评估划分数据集的最大原则是:将无序的数据变得更加有序.因此,我们可以在划分数据前后使用信息论量化度量信息的内容.在划分数据集之前之后信息发生的变化称为信息增益,知道如何计算信息增益,我们就可以计算每个特征划分数据集获得的信息增益,或得信息增益最高的特征就是最好的选择. 集合信息的度量方式称为香农熵或者简称熵(这个名字来源于信息论之父克劳德-香农) 熵定义为信息的期望值,在明晰这个概念之前,我们必须知道信息的定义.信息的定义:如果待分类的事物可能划分在多个分类之中,则符号Xi的信息定义为 其中P(Xi)是选择该分类的概率. 为了计算熵,我们需要计算所有类别所有可能值包含的信息期望值,通过下面的公式得到: 其中n类是分类的数目 计算给定数据集的香农熵 利用上面的公式,对数据集求香农熵,得出信息增益,度量信息量 1.计算有多少行2.对每一行的最后一个label进行统计.(用一个字典可以方便的解决这个问题,key是label,value是次数)3.对每个label求概率4.运用熵公式求值 先创建一个数据集 12345678def createDataSet(): dataSet=[[1,1,'maybe'], [1,1,'yes'], [1,0,'no'], [0,1,'no'], [0,1,'no']] labels=['no surfacing','flippers'] return dataSet,labels 步入正题 123456789101112131415from math import logdef calcShannonEnt(dataSet): numEntries=len(dataSet) labelCounts={} for featVec in dataSet: currentLable=featVec[-1] if currentLable not in labelCounts.keys(): labelCounts[currentLable]=0 labelCounts[currentLable]+=1 shannonEnt=0.0 for key in labelCounts: prob=float(labelCounts[key])/numEntries shannonEnt-=prob*log(prob,2) return shannonEnt 注意:求出的熵值越高,表示混合的数据也越多.也就是这个数据集中的分类也越多. 合理划分数据集 得到熵之后,我们就可以按照获取最大信息增益的方法划分数据集. 我们将对每个特征划分数据集的结果计算一次信息熵.然后判断按照哪个特征划分数据集是最好的划分方式.也就是对划分后的数据集求信息熵,熵值越小,表示数据划分的越好. 划分数据集12345678def splitDataSet(dataSet,axis,values): retDataSet=[] for featVec in dataSet: if featVec[axis]==values: reducedFeatVec=featVec[:axis] reducedFeatVec.extend(featVec[axis+1:]) retDataSet.append(reducedFeatVec) return retDataSet 说明:该代码需要三个参数 参数1:数据集 参数2:axis是按照第几个特征划分 参数3:values是该特征的值 我们要遍历数据集中的每个元素,一旦发现符合要求的值,则将其添加到新创建的列表中.这里要注意python中,对列表操作的两个方法的不同之处.extend和append 选择最好的数据集划分方式 接下来我们遍历整个数据集,循环计算香农熵和splitDataSet()函数,找到最好的特征划分方式.熵计算将会告诉我们如何划分数据集是最好的数据组织方式. 数据需要满足的要求: 1.数据必须是一种由列表元素组成的列表,而且所有的列表元素都要具有相同的数据长度. 2.数据的最后一列或者每个实例的最后一个元素是当前实例的类别标签. 1234567891011121314151617181920212223242526def chooseBestFeatureToSplit(dataSet): # 返回一共有几个特征 numFeatures=len(dataSet[0])-1 baseEntropy=calcShannonEnt(dataSet) bestInfoGain=0.0 bestFeature=-1 # 遍历特征,一个特征一个特征的来处理 for i in range(numFeatures): featList=[example[i] for example in dataSet] print "featList \n",featList uniqueVals=set(featList) newEntropy=0.0 # 对该特征的所有可能取值的方式进行划分,然后求信息熵,最后把该特征的信息增益求出来 for value in uniqueVals: # 对每个特征的不同值进行划分数据 subDataSet=splitDataSet(dataSet,i,value) prob=len(subDataSet)/float(len(dataSet)) newEntropy+=prob*calcShannonEnt(subDataSet) infoGain=baseEntropy-newEntropy # 信息增益越大,说明用该特征划分的数据集计算出的信息熵越小,也就是划分的越好. # 在此跟上一次的划分进行比较,得出最合适的特质 if (infoGain>bestInfoGain): bestInfoGain=infoGain bestFeature=i return bestFeature 递归构建决策树 有了上面的准备后,我们要开始把上面的方法组合起来,构建决策树了. 目前我们已经知道了从数据集构建决策树算法所需要的子功能模块,其工作原理如下:得到原始数据集,然后基于最好的属性值划分数据集,由于特征值可能多于两个,因此可能存在大于两个分支的数据集划分.第一次划分后,数据将被向下传递到树分支的下一个节点,在这个节点上,我们可以再次划分数据.因此我们可以采用递归的原则处理数据集.递归结束的条件是:程序遍历完所有划分数据集的属性,或者每个分支下的所有实例都具有相同的分类,则得到一个叶子节点或者终止块.任何到达叶子节点的数据必然属于叶子节点的分类 1234567891011121314151617181920212223242526272829def majorityCnt(classList): classCount={} for vote in classList: if vote not in classCount.keys():classCount[vote]=0 classCount[vote]+=1 sortedClassCount=sorted(classCount.iteritems(),key=operator.itemgetter(1),reverse=True) return sortedClassCount[0][0]def createTree(dataSet,labels): # 获取该数据集的所有label,存放到一个列表中 classList=[example[-1] for example in dataSet] # 判断:如果在这个数据集的列表中,第一个元素的个数和这个列表的元素个数相等.说明这个数据集已经划分完全了. if classList.count(classList[0])==len(classList): return classList[0] if len(dataSet[0])==1: return majorityCnt(classList) # 获取最合适的分类特征 bestFeat=chooseBestFeatureToSplit(dataSet) # bestFeatLabel=labels[bestFeat] myTree={bestFeatLabel:{}} # 得到列表包含的所有属性值 del(labels[bestFeat]) featValues=[example[bestFeat] for example in dataSet] uniqueVals=set(featValues) for value in uniqueVals: subLabels=labels[:] myTree[bestFeatLabel][value]=createTree(splitDataSet(dataSet,bestFeat,value),subLabels) return myTree]]></content>
</entry>
<entry>
<title><![CDATA[修改 mysql 用户密码]]></title>
<url>%2F2016%2Fmysqlpasswd%2F</url>
<content type="text"><![CDATA[修改的用户都以root为列。 拥有原来的myql的root的密码; 方法一:在mysql系统外,使用mysqladmin 123# mysqladmin -u root -p password "test123"Enter password: 【输入原来的密码】 方法二:通过登录mysql系统, 123456# mysql -uroot -pEnter password: 【输入原来的密码】mysql>use mysql;mysql> update user set password=passworD("test") where user='root';mysql> flush privileges;mysql> exit; 忘记原来的myql的root的密码;首先,你必须要有操作系统的root权限了。要是连系统的root权限都没有的话,先考虑root系统再走下面的步骤。类似于安全模式登录系统,有人建议说是pkill mysql,但是我不建议哈。因为当你执行了这个命令后,会导致这样的状况: 12/etc/init.d/mysqld statusmysqld dead but subsys locked 这样即使你是在安全模式下启动mysql都未必会有用的,所以一般是这样/etc/init.d/mysqld stop,如果你不幸先用了pkill,那么就start一下再stop咯。 12345678# mysqld_safe --skip-grant-tables &&,表示在后台运行,不再后台运行的话,就再打开一个终端咯。# mysqlmysql> use mysql;mysql> UPDATE user SET password=password("test123") WHERE user='root'; mysql> flush privileges;mysql> exit; ##本来mysql是不分大小写的,但是这个是修改的mysql中的mysql数据库的具体的值,要注意到。]]></content>
</entry>
<entry>
<title><![CDATA[Git 命令]]></title>
<url>%2F2016%2FGitOrder%2F</url>
<content type="text"><![CDATA[git 常用命令git 配置命令git init # 初始化本地git仓库(创建新仓库)git config –global user.name “xxx” # 配置用户名git config –global user.email “xxx@xxx.com” # 配置邮件ssh-keygen -t rsa -C xxx@xxx.com # 生成SSH秘钥git config –global color.ui true # git status等命令自动着色git config –global color.status autogit config –global color.diff autogit config –global color.branch autogit config –global color.interactive autogit config –global –unset http.proxy # remove proxy configuration on gitgit clone git+ssh://git@192.168.53.168/VT.git # clone远程仓库git status # 查看当前版本状态(是否修改)git add xyz # 添加xyz文件至indexgit add . # 增加当前子目录下所有更改过的文件至indexgit commit -m ‘xxx’ # 提交git commit –amend -m ‘xxx’ # 合并上一次提交(用于反复修改)git commit -am ‘xxx’ # 将add和commit合为一步 git 删除相关git rm xxx # 删除index中的文件git rm -r * # 递归删除git rm –cached xxx.rb # 将xxx.rb从暂存区删除,但并不从文件系统里移除,只是移出git的管理 git mv README README2 # 重命名文件README为README2 git 日志相关git log # 显示提交日志git log -1 # 显示1行日志 -n为n行git log -5git log –stat # 显示提交日志及相关变动文件git log -p -mgit log v2.0 # 显示v2.0的日志git show dfb02e6e4f2f7b573337763e5c0013802e392818 # 显示某个提交的详细内容git show dfb02 # 可只用commitid的前几位git show HEAD # 显示HEAD提交日志git show HEAD^ # 显示HEAD的父(上一个版本)的提交日志 ^^为上两个版本 ^5为上5个版本git show v2.0 # 显示v2.0的日志及详细内容git show HEAD@{5}git show master@{yesterday} # 显示master分支昨天的状态git show HEAD~3git show -s –pretty=raw 2be7fcb476git tag # 显示已存在的taggit tag -a v2.0 -m ‘xxx’ # 增加v2.0的taggit diff # 显示所有未添加至index的变更git diff –cached # 显示所有已添加index但还未commit的变更git diff HEAD^ # 比较与上一个版本的差异git diff HEAD – ./lib # 比较与HEAD版本lib目录的差异git diff origin/master..master # 比较远程分支master上有本地分支master上没有的git diff origin/master..master –stat # 只显示差异的文件,不显示具体内容git remote add origin git+ssh://git@192.168.53.168/VT.git # 增加远程定义(用于push/pull/fetch) git 分支相关git branch testing # 创建分支testinggit checkout testing # 切换到testing分支git checkout -b testing #创建testing分支,并切换到testing分支,是上面两个命令的合并git log –oneline –decorate –graph –all # 输出你的提交历史、各个分支的指向以及项目的分支分叉情况git branch # 显示本地分支git branch –contains 50089 # 显示包含提交50089的分支git branch -a # 显示所有分支git branch -r # 显示所有原创分支git branch –merged # 显示所有已合并到当前分支的分支git branch –no-merged # 显示所有未合并到当前分支的分支git branch -m master master_copy # 本地分支改名git branch -d hotfixes/BJVEP933 # 删除分支hotfixes/BJVEP933(本分支修改已合并到其他分支)git branch -D hotfixes/BJVEP933 # 强制删除分支hotfixes/BJVEP933git show-branch # 图示当前分支历史git show-branch –all # 图示所有分支历史git checkout -b master_copy # 从当前分支创建新分支master_copy并检出git checkout -b master master_copy # 上面的完整版git checkout features/performance # 检出已存在的features/performance分支git checkout –track hotfixes/BJVEP933 # 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支git checkout v2.0 # 检出版本v2.0git checkout -b devel origin/develop # 从远程分支develop创建新本地分支devel并检出git checkout – README # 检出head版本的README文件(可用于修改错误回退)git merge origin/master # 合并远程master分支至当前分支git cherry-pick ff44785404a8e # 合并提交ff44785404a8e的修改 git 发布相关git push origin master # 将当前分支push到远程master分支git push origin :hotfixes/BJVEP933 # 删除远程仓库的hotfixes/BJVEP933分支git push –tags # 把所有tag推送到远程仓库git fetch # 获取所有远程分支(不更新本地分支,另需merge)git fetch –prune # 获取所有原创分支并清除服务器上已删掉的分支git pull origin master # 获取远程分支master并merge到当前分支 git reset –hard HEAD # 将当前版本重置为HEAD(通常用于merge失败回退)git rebasegit ls-files # 列出git index包含的文件git whatchanged # 显示提交历史对应的文件修改git revert dfb02e6e4f2f7b573337763e5c0013802e392818 # 撤销提交dfb02e6e4f2f7b573337763e5c0013802e392818git ls-tree HEAD # 内部命令:显示某个git对象git rev-parse v2.0 # 内部命令:显示某个ref对于的SHA1 HASHgit reflog # 显示所有提交,包括孤立节点git log –pretty=format:’%h %s’ –graph # 图示提交日志 git 状态相关git stash # 暂存当前修改,将所有至为HEAD状态.你做了一些修改并且向稍后再它们上面工作。你应该保存它们,但是不提交它们git stash list # 查看所有暂存git stash show -p stash@{0} # 参考第一次暂存git stash apply stash@{0} # 应用第一次暂存git grep “delete from” # 文件中搜索文本“delete from”git grep -e ‘#define’ –and -e SORT_DIRENT git 更改分支名字git branch -m old_branch new_branch # Rename branch locallygit push origin :old_branch # Delete the old branchgit push –set-upstream origin new_branch # Push the new branch, set local branch to track the new remote git 查看日志git log –pretty=oneline # 一行显示一次提交信息 git 远程仓库git clone https://github.com/Timehsw/LearnGit # 下载远程仓库git remote -v # 显示当前的远程仓库(简写与其对应的 URL)git remote # 显示当前的远程仓库(只有简写)git remote add # 添加远程仓库(可以用shortname代替url的引用,也就是简写)git fetch [remote-name] # 从远程仓库中抓取与拉取git push -u [remote-name] [branch-name] # 推送到远程仓库(eg:git push origin master)git remote show [remote-name] # 查看远程仓库(eg:git remote show origin)git remote rename [shortname] [newname] # 远程仓库的重命名(eg:git remote rename pb paul)git remote rm [shortname] # 远程仓库的移除(eg:git remote rm paul) git 标签git tag # 列出标签git tag -a v1.0 -m “create v1.0 tag” # 创建附注标签(附注标签会包含一些标签信息)git show v1.0 # 查看标签信息与对应的提交信息git tag v1.1 # 创建轻量标签(轻量标签本质上是将提交校验和存储到一个文件中 - 没有保存任何其他信息)git tag -a v1.2 9fceb02 # 后期打标签(给之前的提交打个标签,v1.2是标签名字 9fceb02是某一次提交的hash值或者部分hash值 )git push origin [tagname] # 共享标签到远程仓库(eg:git push origin v1.0)git push origin –tags # 把所有不在远程仓库服务器上的标签全部传送到那里git checkout -b version2 v1.1 # 检出标签(在特定的标签上创建一个新分支)Git 别名 git 笔记从 git 管理中取消1234我们得先把idea从git的管理里撤销git rm --cached -r .idea/然后提交此次动作git commit -m "rm cached .idea"]]></content>
</entry>
<entry>
<title><![CDATA[CentOS 常用开发环境]]></title>
<url>%2F2016%2FCentosEnr%2F</url>
<content type="text"><![CDATA[安装gccyum -y install gcc gcc-c++ kernel-devel 安装python2.7wget https://www.python.org/ftp/python/2.7.8/Python-2.7.8.tgztar xf Python-2.7.8.tgzcd Python-2.7.8./configure –prefix=/usr/localmake && make install 安装pip….首先安装epel扩展源:sudo yum -y install epel-releasesudo yum upgrade python-setuptoolssudo yum install gcc libffi-devel python-devel python-pip python-wheel openssl-devel libsasl2-devel openldap-devel]]></content>
</entry>
<entry>
<title><![CDATA[Maven 笔记]]></title>
<url>%2F2016%2FMavenSkills%2F</url>
<content type="text"><![CDATA[maven 阿里云镜像123456<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror> 或者在pom.xml中写入1234567<repositories> <repository> <id>nexus-aliyun</id> <name>Nexus aliyun</name> <url>http://maven.aliyun.com/nexus/content/groups/public</url> </repository> </repositories> 手动添加依赖到仓库当mvn工程中,有依赖下载不下来的时候,手动添加依赖到仓库中的办法12345678各个内容是以:分隔DgroupId:DartifactId:Dversion:Dfile:手动下载的jar包的位置mvn install:install-file -DgroupId=org.eclipse.paho -DartifactId=org.eclipse.paho.client.mqttv3 -Dversion=1.0.1 -Dpackaging=jar -Dfile=E:/org.eclipse.paho.client.mqttv3-1.0.1.jar然后再Dos窗口执行上面的命令即可 maven 中打包的依赖 注意:放在pom的最后面, 改下你的jdk版本和代码路径 12345678910111213141516171819202122232425262728293031323334353637383940414243<build> <sourceDirectory>src/main/java</sourceDirectory> <testSourceDirectory>src/test/java</testSourceDirectory> <resources> <resource> <directory>src/main/resources</directory> </resource> </resources> <plugins> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <!-- execution元素包含了插件执行需要的信息 --> <execution> <id>make-assembly</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <!-- 源代码使用的开发版本 --> <target>1.8</target> <!-- 需要生成的目标class文件的编译版本 --> <!-- 一般而言,target与source是保持一致的,但是,有时候为了让程序能在其他版本的jdk中运行(对于低版本目标jdk,源代码中需要没有使用低版本jdk中不支持的语法),会存在target不同于source的情况 --> </configuration> </plugin> </plugins> <defaultGoal>compile</defaultGoal> </build> 编译打包scala12345678910111213<plugin> <groupId>org.scala-tools</groupId> <artifactId>maven-scala-plugin</artifactId> <!-- <version>2.15.2</version> --> <executions> <execution> <goals> <goal>compile</goal> <goal>testCompile</goal> </goals> </execution> </executions></plugin>]]></content>
</entry>
<entry>
<title><![CDATA[Vim实用命令]]></title>
<url>%2F2016%2FVimSkill%2F</url>
<content type="text"><![CDATA[提高效率的几个vim常用命令 在同一窗口下显示多个文件1命令:vim filename1 filename2 说明:上述命令同时打开了filename1,filename2两个文件,不过文件filename1的内容将占据整个vim窗口,要想在各个文件之间进行切换,我们可通过如下命令123:n #切换到下一个文件:N #切换到上一个文件:n filename2 #切换到文件filename2 我们提到每个打开的文件都对应着一个buffer,故可通过切换buffer来达到切换文件的目的,如下 123456:ls 列出vim打开的所有文件的信息,包括文件名,buffer id等:b2 切换到buffer id为2的buffer:bn 切换到当前buffer的下一个buffer:bp 切换当前buffer的前一个buffer:bd 关闭当前buffer,对应文件也随之关闭:bd2 关闭buffer id为2的buffer,对应文件也随之关闭 我们可通过安装miniBufExplorer插件来简化buffer的切换。不过该插件有时会导致语法高亮显示失效,我们可通过命令:syntax on来恢复高亮。 分割窗口,显示多个文件(这个方便实用) 对于在同一个窗口下打开多个文件,对于各文件之间内容的复制,剪切操作终究不方便,我们可使用命令对窗口进行分割,每个子窗口显示一个文件 命令: 12vim -o filename1 filename2 # 水平分割窗口vim -O filename1 filename2 # 垂直分割窗口 我们可按ctrl ww在各窗口之间进行切换 复制,剪切与粘贴操作命令 通过该种方式可非常方便各文件内容之间的复制与剪切操作,这里顺便简单提一下复制,剪切与粘贴操作命令 yy : 复制光标所在行 dd : 剪切光标所在行 5yy : 复制从光标所在行开始计数的下五行文本 5dd : 剪切从光标所在行开始计数的下五行文本 v(小写): visual模式,通过上下左右键选择光标“扫过”的所有字符 V (大写): visual line 模式,通过上下键选择光标“扫过”的所有行 ctrl + v : visual block 模式,通过上下左右键选择一个矩形文本 y : 复制在visual模式, visual line模式和visual block 模式下选择的文本 d : 剪切在visual模式, visual line模式和visual block 模式下 选择的文本 p(小写): 粘贴, 粘贴位置为光标所在行的下一行 P(大写): 粘贴, 粘贴位置为光标所在行的上一行 u : 撤销操作 r : 重做操作 在不关闭vim的情况下继续打开其他文件(实用)可在vim中使用如下命令:12345:e file #不会分割窗口:sp file #将会水平分割窗口:vsp file #将会垂直分割窗口 vim和shell之间切换(实用+1)1:shell # 切换到shell,此时vim在后台运行,在shell中输入命令exit,切换回vim]]></content>
</entry>
</search>