HDFS namenode

HDFS namenode

  1. 双缓冲机制
  2. paxos协议
  • hdfs-site.xml配置文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <property>
    <name>dfs.nameservices</name>
    <value>sync</value>
    <description>Logical name for this new nameservice</description>
    </property>
    <property>
    <name>dfs.namenode.name.dir</name>
    <value>file://home/wudi/hadoop/nn</value>
    </property>
    <property>
    <name>dfs.namenode.shared.edits.dir</name>
    <value>qjournal://host1:port1;host2:port2;host3:port3/sync</value>
    </property>
    这个配置是说,edit log需要往两个地方写:
  1. file://home/wudi/hadoop/nn
  2. qjournal://host1:port1;host2:port2;host3:port3/sync

qjournal是一个共享的edit log目录;
JouralNode节点之间运行Paxos协议(帕克萨斯),保证多点写时也能对edit log达成一致。

  • 双buffer机制
    edit log 维护双buffer,用途:
  1. 填充数据;
  2. flush。
    往buffer写需要事先加锁,写完后检查如果buffer中数据大小达到阈值,则进行sync,将buffer真正写出;
    或:线程主动调用sync,主动将buffer写出。

Hadoop FileInputFormat相关

#版本:
hadoop1. mapred.xxx : 使用JobConf
hadoop2. mapreduce.lib.xxx : 使用taskAttemptContext

#FileInputFormat

FileInputFormat:

直接通过文件大小\split大小计算偏移量,生成InputSplit(FileSplit).
然后把InputSplit传给RecordReader.

NLineInputFormat:

将多行作为一个Split.
具体多少行由job参数控制:
mapreduce.input.lineinputformat.linespermap
新老api都一样.
把文件传给LineReader,找出N行的偏移量,生成InputSplit,
再把InputSplit传给RecordReader,RecordReader再传给LineReader.(二次解析)

CombineFileInputFormat:

Map的时候临时合并小文件.减少Split的数量(减少Map的数量)
从多个Block(文件)获取数据,生成InputSplit. 相关参数:

1
2
3
4
public static final String SPLIT_MINSIZE_PERNODE = 
"mapreduce.input.fileinputformat.split.minsize.per.node";
public static final String SPLIT_MINSIZE_PERRACK =
"mapreduce.input.fileinputformat.split.minsize.per.rack";

上述的都是Key为偏移量,Value为具体内容的.还有一个不是这样的:
KeyValueTextInputFormat:

生成Split的时候按大小;
分割Recode的时候按换行.
特殊之处在于KeyValueLineRecordReader.
默认的K,V分隔符为制表符\t.若一行中没有的话,就Key为整行,Value为空字符串””.
控制分隔符的参数:
mapreduce.input.keyvaluelinerecordreader.key.value.separator

#RecordReader
##LineRecordReader

1
2
3
4
5
6
7
8
9
10
11
12
13
14
比较健全,支持不同字符集(因为直接接受的是byte数组,而且对utf-8有bom格式有处理);
支持压缩.

由于inputSplit是按大小分割的,可能出现某一行跨split的情况.
源码中的处理逻辑是将跨行line的算作前一个split.
实现上: (建议直接看源码)
注意只能跨split,不能跨文件.
因为同一个文件的不同split,可以用同一个流读,因此是可以实现的.
1. 除了第一个split,每个split丢弃第一行.
(因为默认已经被前一个split消费了)
2. 每个split处理时,
while(没到split末尾){
读一行;// 这个过程中可能用流读到了下一个split.
}

##XMLRecordReader
原理仿照LineRecordReader,但减免了压缩和字符集的处理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(源码的逻辑更为严密,这里尽量简单叙述)
定义startTag和endTag.
返回startTag->endTag之间的数据.
丢弃endTag->startTag之间的数据.
若没有endTag,返回startTag到文件尾的数据.

仿LineRecordReader,将跨split的record划分到前一个split中.
实现:
while(没到文件末尾){
找startTag: 沿途遇到的byte=>不存;
}
1. 找不到或找到的startTag完全在下一个split,返回false. (实际代码有优化)
2. 存一下startTag,继续:
while(没到文件末尾){
找endTag: 沿途遇到的byte=>存;
}
1. 找不到endTag,返回到文件尾的数据.
2. 找到了endTag,返回到此为止的数据.

源码如下:

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
package com.fenbi.ape.hive.serde.fileformat;

import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.DataOutputBuffer;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapred.*;
import org.apache.hadoop.mapred.FileSplit;
import org.apache.hadoop.mapred.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.input.*;

import java.io.IOException;

/**
* Created by xiaoyue26 on 17/9/14.
* <p>
* 不接受压缩. 要修改为接受压缩的话,需要学LineRecordReader再修改.
* 只支持utf-8
*/
public class XmlInputFormat extends TextInputFormat {

public static final String START_TAG_KEY = "xmlinput.start";
public static final String END_TAG_KEY = "xmlinput.end";

@Override
public RecordReader<LongWritable, Text> getRecordReader(InputSplit inputSplit,
JobConf jobConf,
Reporter reporter) throws IOException {
//new org.apache.hadoop.mapreduce.lib.input.LineRecordReader();
return new XmlRecordReader((FileSplit) inputSplit, jobConf);
}

/**
* XMLRecordReader class to read through a given xml document to output xml
* blocks as records as specified by the start tag and end tag
*/
public static class XmlRecordReader implements
RecordReader<LongWritable, Text> {
private final byte[] startTag;
private final byte[] endTag;
private final long start;
private final long end;
private final FSDataInputStream fsin;
private final DataOutputBuffer buffer = new DataOutputBuffer();

public XmlRecordReader(FileSplit split, JobConf jobConf) throws IOException {
//不支持通配符,正则
startTag = jobConf.get(START_TAG_KEY).getBytes("utf-8");
endTag = jobConf.get(END_TAG_KEY).getBytes("utf-8");

// open the file and seek to the start of the split
start = split.getStart();
end = start + split.getLength();
Path file = split.getPath();
FileSystem fs = file.getFileSystem(jobConf);
fsin = fs.open(split.getPath());
//先定位到文件此次的开头
fsin.seek(start);
}

// 捞出 偏移量key,文本value
@Override
public boolean next(LongWritable key, Text value) throws IOException {
if (fsin.getPos() < end) {
if (readUntilMatch(startTag, false)) {
try {
//存一下startTag
buffer.write(startTag);
if (readUntilMatch(endTag, true)) {
key.set(fsin.getPos());
value.set(buffer.getData(), 0, buffer.getLength());
return true;
}
} finally {
buffer.reset();
}
}
}
return false;
}

@Override
public LongWritable createKey() {
return new LongWritable();
}

@Override
public Text createValue() {
return new Text();
}

@Override
public long getPos() throws IOException {
return fsin.getPos();
}

@Override
public synchronized void close() throws IOException {
if (fsin != null) {
fsin.close();
}

}

@Override
public float getProgress() throws IOException {
if (start == end) {
return 0.0f;
}
return Math.min(1.0f, (fsin.getPos() - start) / (float) (end - start));
}

/**
* withinBlock=True : 遇到的byte都存入buffer,包括match数组(找endTag)
* withinBlock=False : 不存入buffer(找startTag)
* <p>
* return True : 匹配上了
* return False : 文件结束都没匹配上
* </p>
* split是按splitSize划分的.
*/
private boolean readUntilMatch(byte[] match, boolean withinBlock) throws IOException {
int i = 0;
while (true) {
int b = fsin.read();
// end of file:
if (b == -1) return false;
// save to buffer:
if (withinBlock) buffer.write(b);

// check if we're matching:
if (b == match[i]) {
i++;
// 匹配完毕:
if (i >= match.length) return true;
} else {
// 回溯,从头再来
i = 0;
}
// see if we've passed the stop point:
/*
* 前面检查了有没超过文件尾,这里检查有没有超过split尾
* 情形1: 如果在找startTag(withinBlock=false)
* , 而且一个都没匹配上, 而且已经到达split尾, [start,end)
* 返回false.
* (万里缉凶):
* 情形2: 如果在找endTag(withinBlock=True)
* ,即使已经到split末尾,也允许跨split的查找.
* 尽量返回true.
* 情形3: 如果i!=0,即使已经到split末尾,也允许跨split的查找,以保证startTag或者endTag不被分割.
* 尽量返回true.
*
**/

if (!withinBlock && i == 0 && fsin.getPos() >= end) {
return false;
}
}
}
}
}

groovy语法相关

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
/**
* Created by xiaoyue26 on 17/7/10.
*/
// 1
def getSomething() {

"getSomething return value" //如果这是最后一行代码,则返回类型为String

1000 //如果这是最后一行代码,则返回类型为Integer

}

println getSomething()
// 2
String getString() {
return "I am a string"
}

println getString()

// 3 单双引号(同php)
def x = 1
def doubleQuoteWithDollar = "I am $x dolloar" //输出I am 1 dolloar
def singleQuote = 'I am $x dolloar'
println doubleQuoteWithDollar
println singleQuote

//4 数据类型
def int y = 1
println y.getClass()
println y.getClass().getCanonicalName()

// 5. 容器 List
def aList = [5, 'string', true] //List由[]定义,其元素可以是任何对象
println aList

assert aList[1] == 'string'
assert aList[5] == null //第6个元素为空
aList[100] = 100 //设置第101个元素的值为10
assert aList[100] == 100
println aList.size // 101

// 6. Map
def aMap = ['key1': 'value1', 'key2': true]
println aMap['key1']
def key1 = "wowo"
def aConfusedMap1 = [key1: "aConfusedMap1"] // 不转义
println aConfusedMap1.key1
def aConfusedMap2 = [(key1): "aConfusedMap2"] // 转义
println aConfusedMap2.wowo
println aConfusedMap2['wowo']

//7. Range
def aRange = 1..5
println aRange
def aRangeWithoutEnd = 1..<5 // <==包含1,2,3,4这4个元素
println aRangeWithoutEnd.from // 潜规则调用了 getFrom 方法 (from其实是private的)
println aRangeWithoutEnd.to

// 8. 闭包
def aClosure = {
//闭包是一段代码,所以需要用花括号括起来..
String param1, int param2 -> //这个箭头很关键。箭头前面是参数定义,箭头后面是代码
println "this is code $param1,$param2" //这是代码,最后一句是返回值,
//也可以使用return,和Groovy中普通函数一样
}


aClosure('hello', 1)
aClosure.call('hello', 1)

// 默认参数 $it
def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'

// 显式声明无参数:
def noParamClosure = { -> true }

// 9. 闭包进阶

def iamList = [1, 2, 3, 4, 5] //定义一个List
iamList.each { //调用它的each,这段代码的格式看不懂了吧?each是个函数,圆括号去哪了?
println it
}
// each函数的声明:
// public static <T> List<T> each(List<T> self, Closure closure)
// 第一个参数是self,省略;最后一个参数是闭包,因此可以省略圆括号.
iamList.each({ //加上圆括号
println it
})

// 例2 :
def testClosure(Closure closure) {
//do something
closure() //调用闭包
}

testClosure {
println "i am in closure"
}

//例3: 闭包传参

def aaMap = [k1: 'value1', k2: true]
def results = aaMap.findAll {
key, value ->
println "scan : key=$key,value=$value"
if (key == 'k1')
return true
return false
}
print results

// 10. 成员变量
// eg 1
thisX = 1 // 不加def和类型,则是实例变量, 会在对应类的 main 函数中 动态添加变量.
def printx() {
println thisX
}

printx()
// eg 2
def smallX = 1 // 普通 main函数中的 局部变量
def printSmallx() { // 类的实例方法
println smallX; // error
}
// printSmallx() // error
// eg 3
import groovy.transform.Field;

//必须要先import
@Field fieldX = 1 // <==在x前面加上@Field标注,这样,x就彻彻底底是test的成员变量了。
def printFieldX() {
println fieldX
}

printFieldX()

// 11. IO操作
def srcFile = new File('/Users/xiaoyue26/input')
srcFile.eachLine {
println it
}
// 写入
def targetFile = new File('/Users/xiaoyue26/output')
targetFile.withOutputStream {
os ->
srcFile.withInputStream {
ins -> os << ins
}
}

gradle学习笔记

有用的命令:

1
2
3
// 查看某个依赖具体是怎么引入的.
gradle dI --dependency org.springframework:spring-messaging
// dI是dependencyInsight的缩写

https://www.jetbrains.com/help/idea/creating-and-running-your-scala-application.html
https://docs.gradle.org/3.3/userguide/scala_plugin.html
https://dzone.com/articles/intellij-scala-and-gradle
http://www.cnblogs.com/davenkin/p/gradle-learning-4.html

http://www.cnblogs.com/davenkin/p/gradle-learning-1.html
git clone https://github.com/davenkin/gradle-learning.git

gradle task

gradle主要有两个元素:task和plugin.
如下task开头的就是task,没有task的就是project的属性(或扩展属性).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
task helloWorld << {
println "Hello World!"
}

task copyFile(type: Copy) {
from 'xml'
into 'destination'
}

task hello2 { //doLast等效于helloWorld的 <<
doLast {
println 'hello2'
}
}
task hello3 {
doLast {
println 'hello last'
}
doFirst {
println 'hello first'
}
}

可以看出gradle也是有语法的.据说是groovy:
http://blog.csdn.net/wangbaochu/article/details/51177672
http://www.jianshu.com/p/37b46cc815b3

  1. 大括号之间的内容则表示传递给task()方法的一个闭包.
  2. << 表示向helloWorld中追加入执行代码——其实就是groovy代码.
  3. 执行命令: gradle helloWorld,表示执行helloWorld这个task.
  4. helloWorld是默认类型,copyFile是Copy类型的task.

task之间的依赖:

1
2
3
task taskA(dependsOn: taskB) {
//do something
}

查看当前项目的task列表:(包括自带的tasks)

1
gradle tasks --all

查看当前的属性配置列表:

1
gradle properties

#执行顺序:
Gradle在执行Task时分为两个阶段,首先是配置阶段,然后才是实际执行阶段.

1
2
3
4
5
6
7
task hello9 << {
println description
}

hello9.configure {
description = "this is hello9"
}

配置在定义后,但依然能打印出this is hello9.
一个Task除了执行操作之外,还可以包含多个Property,其中有Gradle为每个Task默认定义的Property,比如description,logger等。另外,每一个特定的Task类型还可以含有特定的Property,比如Copy的from和to等。当然,我们还可以动态地向Task中加入额外的Property。

增量编译:

1
2
3
//规定输入输出:
inputs.dir sources
outputs.file destination

静态设置变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
version = 'this is the project version'
description = 'this is the project description'
task showProjectProperties << {
println version // task没有version属性,因此默认调用 project.version
println project.version
println description // task有description属性,但是没有赋值
println project.description

}

// 为project添加额外的Property(不能像python那样直接定义,需要用ext关键字添加)
ext.property1 = "this is property1"

ext {
property2 = "this is property2"
}

// project和task都是实现了ExtensionAware接口的gradle对象,因此可以这样添加属性
task showProperties << {
println property1
println property2
}

动态设置变量

1
2
3
task showCommandLieProperties << {
println property3
}

调用命令:

1
2
3
4
5
6
7
8
// 动态定义属性(命令行中定义),调用命令:
gradle -Pproperty3="this is property3" showCommandLieProperties
// 或者:
gradle -Dorg.gradle.project.property3="this is another property3" showCommandLieProperties
// 或者:
export ORG_GRADLE_PROJECT_property3="this is yet another property3"
gradle showCommandLieProperties
unset ORG_GRADLE_PROJECT_property3

gradle复合项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//根项目:
//settings.gradle
include 'sub-project1', 'sub-project2'

//作用于所有项目的task:(包括root-project)
allprojects {
apply plugin: 'idea'
task allTask << {
println project.name
}
}
//作用于所有子项目的task:(不包括root-project)
subprojects {
task subTask << {
println project.name
}
}
//gradle allTask 输出:
:allTask // 调用的root的allTask
root-project //输出
:sub-project1:allTask //调用sub-project1的allTask
sub-project1
:sub-project2:allTask
sub-project2

可以在跟项目的build.gradle中定义所有子项目的task:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 方法1:
project(':sub-project1') {
task forProject1 << {
println 'for project 1'
}
}

// 方法2:
configure(allprojects.findAll { it.name.startsWith('sub') }) {
subTask << {
println 'this is a sub project'
}
}

执行gradle forProject1时会级联查找所有项目的对应task.也可以显式得调用某个子项目的task:

1
gradle :sub-project1:forProject1

groovy语法:
http://blog.csdn.net/u014761700/article/details/51867939

gradle执行阶段:

Initialization phase
->hook1
->Configuration phase
->hook2
->Execution phase
->hook3

其中hook1:

1
2
3
4
gradle.beforeProject{
project->
...
}

hook2:

1
2
3
4
gradle.taskGraph.whenReady{
graph->
...
}

hook3:

1
2
3
4
gradle.buildFinished{
result->
...
}
  1. 初始化阶段执行settings.gradle
  2. 配置阶段解析每个project中的build.gradle
    ,以确定整个build的project以及内部的task关系.(即hook2中的task的有向图)
  3. 执行阶段

gradle包含三大对象: gradle对象,project对象,task对象.
一个project对象有多少task对象,往往由plugin对象决定.

spring的gradle:
https://github.com/spring-projects/spring-scala/blob/master/build.gradle

gradle排除依赖:

1
2
3
compile("org.springframework.boot:spring-boot-starter-web") {
exclude group: 'com.fasterxml.jackson.core'
} // 排除依赖示例

maven也有类似的语法排除.

多版本库的选择:

  • maven: 后面引入的覆盖前面的.可以使用shadow插件进行换名;
  • gradle: 新版本覆盖旧版本.

git笔记

git教程:
http://rogerdudler.github.io/git-guide/index.zh.html

工作流:

本地仓库由 git 维护的三棵“树”组成。第一个是你的 工作目录,它持有实际文件;第二个是 暂存区(Index),它像个缓存区域,临时保存你的改动;最后是 HEAD,它指向你最后一次提交的结果。
此处输入图片的描述

  • 创建新仓库
    创建新文件夹,打开,然后执行
    git init
    以创建新的 git 仓库。

  • 设置用户名和邮箱

    1
    2
    3
    git config --global user.name xiaoyue26
    git config --global user.email 296671657@qq.com
    git config --global color.ui true

在项目目录下ls -a查看隐藏的git配置文件

修改.gitignore文件,例如在里面另起一行,添加:*.zip,则可以忽略zip后缀名的文件。

1
2
3
-- 忽略除了lib.a以外的.a后缀文件
*.a
!lib.a

git status命令看到的绿色的就是已经stage的文件,
红色的就是还没有git add的文件(unstaged)。
## unstage 删除远端没有 本地stage的文件。

1
git rm --cached <file>...

暂存(慎用)

想暂存到目前为止作出的修改,但是不想提交到版本库,git提供了一个命令git stash,所有修改会提交到栈中

git commit 的时候把时间改成明天:

1
2
-- 网上查到明天的unix时间戳是1451750400
git commit --date 1451750400

撤销最后一次commit:

git reset --soft HEAD~1

从远端还原一个文件:

git checkout config.rb

查看远端信息:

1
2
3
git remote -v
git pull origin master
githug

向本地仓库添加远程版本库,名叫origin

git remote add origin https://github.com/githug/githug

合并分支,我们可以使用rebase,这个题目里,本地master提交了Thrid commit,而远程分支也提交了Fourth commit,现在需要把远程更新合并到本地,可以使用下面的命令。

1
2
git rebase origin/master
git push origin master

查看config.rb文件每一行的作者:

git blame config.rb

##检出仓库
执行如下命令以创建一个本地仓库的克隆版本:

clone```
1
2
如果是远端服务器上的仓库,你的命令会是这个样子:
```git clone username@host:/path/to/repository

用git定位代码从哪一次commit后开始出错:

原理:二分查找
背景条件:

  1. ruby prog.rb 5命令在当前代码条件下应该返回正确答案15.
  2. 从过去的某一次commit后的代码之后,这个结果就错了。
    现在要用git bisect找出到底是哪一次之后就出错了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# 查找正确那次的commit hash:
git log
# 例如已知f608824那次commit运行`ruby prog.rb 5`命令的结果确实是15.
# 告诉git bisect查找的起点和终点:
git bisect start -- 开始查找
git bisect good f608824 -- 起点(正确)
git bisect bad master -- 终点(错误)
# 交互地验证(类似监督学习)
ruby prog.rb 5 #看结果是否 15
# 1. 如果上述命令结果正确(15),则输入:
git bisect good
# 3. 如果上述命令结果错误(不是15),则输入:
git bisect bad

# 反复进行上述步骤,直到出现提示:
>18ed2ac1522a014412d4303ce7c8db39becab076 is the first bad commit
commit 18ed2ac1522a014412d4303ce7c8db39becab076
Author: Robert Bittle <guywithnose@gmail.com>
Date: Mon Apr 23 06:52:10 2012 -0400

##添加和提交
你可以提出更改(把它们添加到暂存区),使用如下命令:

add
1
git add *

这是 git 基本工作流程的第一步;使用如下命令以实际提交改动:

commit -m "代码提交信息"```
1
2
3
4
5
现在,你的改动已经提交到了 `HEAD`,但是还没到你的远端仓库。

##推送改动
你的改动现在已经在本地仓库的 HEAD 中了。执行如下命令以将这些改动提交到远端仓库:
```git push origin master

可以把 master 换成你想要推送的任何分支。

如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:

remote add origin ```
1
2
3
4
5
6
7
8
9
10
11
12
如此你就能够将你的改动推送到所添加的服务器上去了。

## 切换到tag v1.2 :
`git checkout v1.2`
或者明确指定是tag:
`git checkout tags/v1.2`

###分支
创建一个叫做`my_branch`的分支:
`git branch my_branch`
创建一个叫做“feature_x”的分支,并切换过去:
```git checkout -b feature_x

上述都是在当前代码基础上创建分支,也可以在任意一次commit的基础上创建分支:

1
2
3
4
# 先查看commit的hash标记
git log
# 创建分支
git branch test_branch -v 27b1836a65de099f6ebddb5998

切换回主分支:

checkout master```
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
查看当前分支情况:(类似git status)
`git branch `
push到指定分支上:
`git push --set-upstream origin test_branch`
其中:
>origin 是默认的远程版本库名称
可以在 .git/config 之中进行修改

把`feature`分支合并到当前分支:
`git merge feature`

不merge但是获取远端的东西:
`git fetch`
>git fetch:相当于是从远程获取最新版本到本地,不会自动merge
git pull:相当于是从远程获取最新版本并merge到本地

git rebase和git merge区别:
>1. 在B分支上执行 git merge A 后 A就被合到B上了
2. 在B分支上执行 git rebase B A 后,效果与merge是一样的,但是 A就没有了,两个分支就合在一起了。

git rebase分支:
`git rebase master feature`

用`git gc`代替`git repack`命令。

# cherry-pick 从别的分支提取commit(提取文件)。

切换到feature分支:

git checkout feature

查看log树:

git log

找到添加和修改想要文件那次commit的hash ca32a6…

切换回master分支:

git checkout master

提取文件:

git cherry-pick ca32a6dac7b6f97

1
2
3
4
5

查看还有多少个`TODO`:
`git grep TODO`

修改很久以前的注释,(修改上一次的直接git commit --amend -m就行了)

往前倒到上两次的:

git rebase -i HEAD~2

把pick改成reword,并修改注释。

1
2

合并前四次commit:

通过git log确定要合并的commit有几次

git log

修改前四次的:

git rebase -i HEAD~4

这时的顺序是从按时间最早到最晚排列的,所以把后面几个pick都改成s然后退出保存即可。

1
2

合并分支时,把分支的多次提交变成一次提交。

跟上面的类似,但是从别的分支取出几个commit,然后在本分支上体现为一个commit:

git merge –squash long-feature-branch
git commit -m “commit from merge branch”

1
2

修改commit的顺序:

git rebase -i HEAD~3
然后把里面的几行顺序换一下就好了。

1
2
3

再把新建的分支删掉:
```git branch -d feature_x

除非你将分支推送到远端仓库,不然该分支就是 不为他人所见的:

push origin ```
1
2
3
4
5
6
>origin 是默认的远程版本库名称你可以在 `.git/config` 之中进行修改.
>事实上 `git push origin master` 的意思是 `git push origin master:master` (将本地的 master 分支推送至远端的 master 分支,如果没有就新建一个)

##更新与合并
要更新你的本地仓库至最新改动,执行:
```git pull

以在你的工作目录中 获取(fetch) 并 合并(merge) 远端的改动。
要合并其他分支到你的当前分支(例如 master),执行:

merge ```
1
2
在这两种情况下,git 都会尝试去自动合并改动。遗憾的是,这可能并非每次都成功,并可能出现冲突(conflicts)。 这时候就需要你修改这些文件来手动合并这些冲突(conflicts)。改完之后,你需要执行如下命令以将它们标记为合并成功:
```git add <filename>

在合并改动之前,你可以使用如下命令预览差异:

diff ```
1
2
3
4

##标签
为软件发布创建标签是推荐的。这个概念早已存在,在 SVN 中也有。你可以执行如下命令创建一个叫做 1.0.0 的标签:
```git tag 1.0.0 1b2e1d63ff

1b2e1d63ff 是你想要标记的提交 ID 的前 10 位字符。可以使用下列命令获取提交 ID:

log```
1
2
3
4
5
你也可以使用少一点的提交 ID 前几位,只要它的指向具有唯一性。

##替换本地改动
假如你操作失误(当然,这最好永远不要发生),你可以使用如下命令替换掉本地改动:
```git checkout -- <filename>

此命令会使用 HEAD 中的最新内容替换掉你的工作目录中的文件。已添加到暂存区的改动以及新文件都不会受到影响。

假如你想丢弃你在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将你本地主分支指向它:

1
2
git fetch origin
git reset --hard origin/master

##实用小贴士
内建的图形化 git:
gitk
彩色的 git 输出:
git config color.ui true
显示历史记录时,每个提交的信息只显示一行:
git config format.pretty oneline
交互式添加文件到暂存区:
git add -i

进阶教程:
http://marklodato.github.io/visual-git-guide/index-zh-cn.html

  1. 开分支:
    branch 新分支名```
    1
    2
     如新建一个开发分支:
    ``` git branch dev
  2. 切换到某分支:
    checkout 分支名```
    1
    2
    3. 合并上述两个命令:
    ``` git checkout -b 新分支名
  3. 合并分支:
    merge 需要合并的分支名```
    1
    2
    5. 查看本地分支列表:
    ``` git branch -a
  4. 查看远端分支列表:
    branch -r```
    1
    2
    7. 向远程分支提交本地新开的分支:
    ```git push origin 新分支名
  5. 删除远程分支:(多一个空格和冒号)
    push origin :远程分支名```
    1
    2
    3
    原理就是把一个空的branch赋值给已有的branch,这样就删除了。
    9. 删除本地分支:
    ```git branch 分支名称 -d
  6. 拉取远端:
    pull origin master```
    1
    2
    11. 更新分支列表信息:
    ```git fetch -p
  7. 查看帮助:
    1
    2
    3
    git -h
    git branch -h
    git pull -h
  • 其他
    1
    2
    3
    4
    5
    git push origin :branch_you_want_to_delete
    #注意空格
    #下述命令没使用过,不知道含义 :
    #查看git branch -h时得知-r表示对远端生效,-d表示删除。
    #git branch -r -d origin/branch-name

竟然没有commit和pull.

1
2
git commit fileA;
git pull ;
1
2
3
4
5
6
#安装brew:
curl -LsSf http://github.com/mxcl/homebrew/tarball/master | sudo tar xvz -C/usr/local --strip 1
#查看brew软件列表:
sudo brew search /apache*/
#安装软件:
sudo brew install wget
  • 给git log 上颜色
    git log --decorate --graph
    git commit –amend
    直接删掉changeid
    会自动生成新的changeid

  • git add时(stage)先进行diff和修改

    1
    git add feature.rb -e

查看切换分支的记录log:

git reflog

结果:

894a16d HEAD@{0}: commit: commit another todo
6876e5b HEAD@{1}: checkout: moving from solve_world_hunger to kill_the_batman
324336a HEAD@{2}: commit: commit todo
6876e5b HEAD@{3}: checkout: moving from blowup_sun_for_ransom to solve_world_hunger
6876e5b HEAD@{4}: checkout: moving from kill_the_batman to blowup_sun_for_ransom
6876e5b HEAD@{5}: checkout: moving from cure_common_cold to kill_the_batman
6876e5b HEAD@{6}: commit (initial): initial commit

然后可以切换到想去的分支:
git checkout solve_world_hunger

取消一个已经提交(push)的commit,原理其实是提交一次revert操作的commit.
通过git log查看想要取消的那次commit的hash值。

1
2
3
git revert d71adf7ad90cc0206c2076b
# 或者如果是倒数第二次的commit:
git revert HEAD~1

删除最近一次提交:
git reset --hard HEAD^
取消删除最近一次提交:
git reflog查看到倒数第二个commit的hash然后:
git checkout b5ed1ea

git submodule add 仓库地址 路径

fluentd学习笔记

配置文件

vi /etc/td-agent/td-agent.conf

插件

cd /etc/td-agent/plugin

日志

tail -fn 100 /var/log/td-agent/td-agent.log

运行

sudo /opt/td-agent/usr/sbin/td-agent --log /var/log/td-agent/td-agent.log --use-v1-config

测试

curl -X POST -d 'json={"json":"message"}' http://localhost:8888/debug.test

配置

source: 输入
match: 输出目标
filter: 过滤 (输入与输出之间)
system: 系统级别设置
label: 操作名字
@include: 引入其他文件

fluentd Event事件的生命周期

  • 设置:

    定义输入\监听,匹配规则,输出.

  • event的结构:
    (tag,time,record)
    input插件负责把数据源的数据转换到event的结构.(三元组)
    例如数据源是:
    192.168.0.1 - - [28/Feb/2013:12:00:00 +0900] "GET / HTTP/1.1" 200 777

  • event:

    1
    2
    3
    tag: apache.access # set by configuration
    time: 1362020400 # 28/Feb/2013:12:00:00 +0900
    record: {"user":"-","method":"GET","code":200,"size":777,"host":"192.168.0.1","path":"/"}

    filter:

  • 过滤器:

    1
    2
    3
    4
    <filter test.cycle>
    @type grep
    exclude1 action logout
    </filter>

    这个filter会过滤掉这种输入:

    1
    curl -i -X POST -d 'json={"action":"logout","user":2}' http://localhost:8888/test.cycle

label

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<source>
@type http
bind 0.0.0.0
port 8888
@label @STAGING
</source>

<filter test.cycle>
@type grep
exclude1 action login
</filter>

<label @STAGING>
<filter test.cycle>
@type grep
exclude1 action logout
</filter>
<match test.cycle>
@type stdout
</match>
</label>

source type

几种输入type:

forward: tcp
http: http

  • e.g.:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Receive events from 24224/tcp
# This is used by log forwarding and the fluent-cat command
<source>
@type forward
port 24224
</source>

# http://this.host:9880/myapp.access?json={"event":"data"}
<source>
@type http
port 9880
</source>
# 把发送到localhost:9880/test.cyle的数据转发到标准输出上.
<match test.cycle>
@type stdout
</match>
# 发送到myapp.access的数据发到/var/log...文件.
<match myapp.access>
@type file
path /var/log/fluent/access
</match>

builder模式

构造一个对象时,若参数有多个,且其中几个为可选,有几种实现方式:

  • 重叠构造函数(重叠构造器)
    1
    2
    3
    4
    5
    6
    public NutritionFacts(int a,int b){this(a,b,0);}#调用底层构造函数
    public NutritionFacts(int a,int b,int c){
    this.a=a;
    this.b=b;
    this.b=b;
    }
    这种方法较为繁琐,颠倒其中两个参数顺序会造成严重后果。不易扩展。

  • javeBeans模式
    使用多个setter方法来设定必要的参数,可读性很强,但同时也是javabean容易处于不一致状态、不可用状态,需要额外努力来保证线程安全要求。

  • builder模式
    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
    import java.lang.reflect.Field;

    public class NutritionFacts {

    private final int servingSize;
    private final int serving;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
    // Required parameters:
    private final int servingSize;
    private final int serving;
    // optional parameters:
    private int calories = 0;
    private int fat = 0;
    private int sodium = 0;
    private int carbohydrate = 0;

    public Builder(int servingSize, int serving) {
    this.servingSize = servingSize;
    this.serving = serving;
    }

    public Builder calories(int val) {
    calories = val;
    return this;
    }

    public Builder fat(int val) {
    fat = val;
    return this;
    }

    public Builder sodium(int val) {
    sodium = val;
    return this;
    }
    public Builder carbohydrate(int val) {
    carbohydrate = val;
    return this;
    }
    public NutritionFacts build() {
    return new NutritionFacts(this);
    }
    }
    private NutritionFacts(Builder builder) {
    this.servingSize = builder.servingSize;
    this.serving = builder.serving;
    this.calories = builder.serving;
    this.fat = builder.serving;
    this.sodium = builder.serving;
    this.carbohydrate = builder.carbohydrate;

    }
    public static void main(String[] args) {
    NutritionFacts cocacola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();

    }
    }

builder模式代码比较冗长,但是当大部分参数都是可选的时候是一个不错的选择,拓展性强,比javaBean安全。

  • 其他技巧
    避免创建不必要的对象,使用静态工厂方法。例如能使用Boolean.valueOf(String)时,就不要使用Boolean(String)。后者会多创建一个对象。

  • 内存泄露
    消除过期的对象引用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    //自己实现的容器如栈:
    private Object[]elements;
    public Object pop()//此函数存在内存泄露
    {
    if(size==0)
    throw new EmptyStackException();
    return elements[--size];
    }
    //正确写法:
    public Object pop()//此函数存在内存泄露
    {
    if(size==0)
    throw new EmptyStackException();
    Object res=elements[--size];
    elements[size]=null;//使废弃对象的引用数减至0.让GC回收。
    return res;
    }

    其他可能出现内存泄露的来源:监听器、回调、缓存。

引用:
1.强引用
强引用就是普通的Java引用,代码:
StringBuffer buffer = new StringBuffer();
2.软引用(SoftReference)//用于缓存
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。

3.弱引用(WeakReference)
弱引用与软引用的区别在于:弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
>当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候你就是用弱引用。

1
2
3
String abc = new String("abcde");
WeakReference<String> wf= new WeakReference<String>(str, rq);
String abc1 = wf.get()//如果abcde这个对象没有被垃圾回收器回收,那么abc1就指向"abcde"对象

4.虚引用(PhantomReference)//它允许你知道对象何时从内存中移除。
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

三种类型的引用定义了三种不同层次的可达性级别,由强到弱排列如下:

  SoftReference > WeakReference > PhantomReference

java内存

#1. PC

每个线程都有自己独立的程序计数器.
JVM规范中规定:
如果线程执行的是非native方法,则PC中保存的是当前需要执行的指令地址;
如果线程执行的是native方法,则PC中的值undefined。

#2. Java虚拟机栈

存放的是栈帧. 每个方法生成一个栈帧.
栈帧中包括:局部变量表、操作数栈、动态连接、方法返回地址、附加信息.
##(1) 局部变量表:
各种基本数据类型,对象引用.
这个区域的两种异常:stackOverFlowError,OutOfMemoryError
1.如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;
2.如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

##(2) 操作数栈
略.

##(3) 动态连接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用(指向运行时常量池:在方法执行的过程中有可能需要用到类中的常量),持有这个引用是为了支持方法调用过程中的动态连接.

##(4) 方法返回地址

当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。(PC)

##(5) 附加信息

取决于具体的虚拟机实现.在实际开发中,一般会把动态连接,方法返回地址与其它附加信息全部归为一类,称为栈帧信息。

3. 本地方法栈

执行非java代码. 其他类似,也抛stackoverflow和outOfMemory.

4. 堆

所有线程共享.
所有对象分配的地方.

5. 方法区

所有线程共享.
存储已被虚拟机加载的类信息、常量、静态变量、以及编译器编译后的代码.

常量池

运行时常量池是方法区的一部分。
Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

redis笔记

1. 安装:

mac下安装很简单:

1
brew install redis

启动服务端:

1
redis-server

启动客户端:

1
redis-cli

2. 配置

mac默认的配置文件在:

1
/usr/local/etc/redis.conf

可以通过命令brew info redis查看到.
但具体的安装路径,找了一圈连接发现是在:

1
/usr/local/Cellar/redis

cli会话下查询某项配置的值:

1
2
CONFIG GET loglevel
CONFIG GET * # 获取所有

3. 数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

#1. string

redis 127.0.0.1:6379> SET name “runoob”
OK
redis 127.0.0.1:6379> GET name
“runoob”

#2. hash(k,v都是string)

127.0.0.1:6379> HMSET user:1 username runoob password runoob points 200
OK
127.0.0.1:6379> HGETALL user:1

1) “username”
2) “runoob”
3) “password”
4) “runoob”
5) “points”
6) “200”

3. list

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。

redis 127.0.0.1:6379> lpush runoob redis
(integer) 1
redis 127.0.0.1:6379> lpush runoob mongodb
(integer) 2
redis 127.0.0.1:6379> lpush runoob rabitmq
(integer) 3
redis 127.0.0.1:6379> lrange runoob 0 10

1) “rabitmq”
2) “mongodb”
3) “redis”

相关命令

1
2
3
lpush key value [value...]
rpush key value [value...]
lrange key start end #左闭右闭

4. Set

命令: sadd key member

redis 127.0.0.1:6379> sadd new_set redis
(integer) 1
redis 127.0.0.1:6379> sadd new_set mongodb
(integer) 1
redis 127.0.0.1:6379> sadd new_set rabitmq
(integer) 1
redis 127.0.0.1:6379> sadd new_set rabitmq
(integer) 0
redis 127.0.0.1:6379> smembers new_set

1) “rabitmq”
2) “mongodb”
3) “redis”

5. zset 排序的set,根据score排序从小到大

命令: zadd key score member

redis 127.0.0.1:6379> zadd new_zset 0 redis
(integer) 1
redis 127.0.0.1:6379> zadd new_zset 0 mongodb
(integer) 1
redis 127.0.0.1:6379> zadd new_zset 0 rabitmq
(integer) 1
redis 127.0.0.1:6379> zadd new_zset 0 rabitmq
(integer) 0
redis 127.0.0.1:6379> ZRANGEBYSCORE new_zset 0 1000

1) “redis”
2) “mongodb”
3) “rabitmq”

连接远端redis服务器

1
redis-cli -h host -p port -a password

spark中的存储

  • #存储架构

Spark是一种基于内存的运算框架。数据存储在worker节点中。对于每一个worker,worker Excutor有一个JVM,数据存储在JVM中。


  • #故障恢复

当计算引擎的进程损坏,Cache 丢失,Spark只能重新启动并计算恢复数据。


  • #Lineage(世代)

Lineage保证数据的Reliability。

  • 当数据E丢失后:
  1. 通过世代找到相应的之前数据,重新部署一个Job将数据重新计算。
  2. 将数据在底层文件系统中备份。

  • #数据流

计算数据在JVM的内存中存储一份,以保证较少的网络通信和读写。同时记录存储数据的世代(lineage),当数据丢失时,基于世代将job重新运行,得到相应数据。


  • #计算过程

Spark内部,单个executor进程内RDD的分片数据是用Iterator流式访问的。
Iterator的hasNext方法和next方法是由RDD lineage上各个transformation携带的闭包函数复合而成的。
该复合Iterator每访问一个元素,就对该元素应用相应的复合函数,得到的结果再流式地落地。空间复杂度为O(1)。


  • #持久化

spark.local.dir:可以设置Spark的暂存目录,包括映射输出文件和需要存储在磁盘上的RDDs。这个磁盘目录在系统上面访问速度越快越好。可以用逗号隔开来设置多个目录。默认值为’/tmp’。
org.apache.spark.storage.StorageLevel中5种持久化的级别.(result.persist(StorageLevel.DISK_ONLY)) 默认为MEMORY_ONLY

级别 空间 时间 内存 磁盘 备注
MEMORY_ONLY 使用 不使用 -
MEMORY_ONLY_SER 使用 不使用 -
MEMORY_AND_DISK 中等 部分 部分 如果数据在内存中放不下,则溢写到磁盘上
MEMORY_AND_DISK_SER 部分 部分 如果数据在内存中放不下,则溢写到磁盘上。在内存中存放序列化后的数据。
DISK_ONLY 部分 部分 -

  • #数据分享

在Spark中,如果job2需要Job1运算的数据,Job1首先需要将数据写入到HDFS的block中,会产生硬盘甚至跨网络的读写,同时在HDFS中默认数据需要写三份,因此造成性能的损失。