From 77af89c8f82b75275f252afb6ad609d8c5b6d28c Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 00:10:44 +0800
Subject: [PATCH 01/85] Set theme jekyll-theme-hacker

---
 _config.yml |  1 +
 index.md    | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+)
 create mode 100644 _config.yml
 create mode 100644 index.md

diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000..fc24e7a
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-hacker
\ No newline at end of file
diff --git a/index.md b/index.md
new file mode 100644
index 0000000..ab57b39
--- /dev/null
+++ b/index.md
@@ -0,0 +1,37 @@
+## Welcome to GitHub Pages
+
+You can use the [editor on GitHub](https://github.com/OceanOverflow/oceanoverflow.github.io/edit/master/index.md) to maintain and preview the content for your website in Markdown files.
+
+Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.
+
+### Markdown
+
+Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for
+
+```markdown
+Syntax highlighted code block
+
+# Header 1
+## Header 2
+### Header 3
+
+- Bulleted
+- List
+
+1. Numbered
+2. List
+
+**Bold** and _Italic_ and `Code` text
+
+[Link](url) and ![Image](src)
+```
+
+For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/).
+
+### Jekyll Themes
+
+Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/OceanOverflow/oceanoverflow.github.io/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file.
+
+### Support or Contact
+
+Having trouble with Pages? Check out our [documentation](https://help.github.com/categories/github-pages-basics/) or [contact support](https://github.com/contact) and we’ll help you sort it out.

From 7f9015da51318b5ffa1f78095bebed7b96e0e4f3 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 00:11:29 +0800
Subject: [PATCH 02/85] Update index.md


From 741f43f22bd9c140e4233c0750680c0d40faf742 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 07:56:22 +0800
Subject: [PATCH 03/85] Update index.md

Add task list in index.md, this indicates progress
---
 index.md | 38 +++-----------------------------------
 1 file changed, 3 insertions(+), 35 deletions(-)

diff --git a/index.md b/index.md
index ab57b39..c823d0e 100644
--- a/index.md
+++ b/index.md
@@ -1,37 +1,5 @@
-## Welcome to GitHub Pages
-
-You can use the [editor on GitHub](https://github.com/OceanOverflow/oceanoverflow.github.io/edit/master/index.md) to maintain and preview the content for your website in Markdown files.
-
-Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.
-
-### Markdown
-
-Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for
-
 ```markdown
-Syntax highlighted code block
-
-# Header 1
-## Header 2
-### Header 3
-
-- Bulleted
-- List
-
-1. Numbered
-2. List
-
-**Bold** and _Italic_ and `Code` text
-
-[Link](url) and ![Image](src)
+- [] Golang 设计模式
+- [] PKCS7 Padding
+- [] PBFT 共识算法
 ```
-
-For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/).
-
-### Jekyll Themes
-
-Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/OceanOverflow/oceanoverflow.github.io/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file.
-
-### Support or Contact
-
-Having trouble with Pages? Check out our [documentation](https://help.github.com/categories/github-pages-basics/) or [contact support](https://github.com/contact) and we’ll help you sort it out.

From c471bbafad0945d3a82bccd677501cc3445f7154 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 15:26:18 +0800
Subject: [PATCH 04/85] Create FSM.md

Learn Finite State Machine & its application
---
 FSM.md | 277 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 277 insertions(+)
 create mode 100644 FSM.md

diff --git a/FSM.md b/FSM.md
new file mode 100644
index 0000000..e95085c
--- /dev/null
+++ b/FSM.md
@@ -0,0 +1,277 @@
+# 实现一个有限状态机
+
+  在实际工程代码当中，常常会用到有限状态机(Finite-state Machine. FSM)，例如在p2p网络中一个节点可能拥有多种状态，有限状态机可以用于表示有限个状态
+及所有可能的状态之间转移的情况。利用有限状态机就可以轻松地解决节点在不同状态之间转移的情况。这种编程模型可以避免丑陋的switch语句。
+
+  以门的开启和关闭举例，一扇门拥有两种状态（state），open和closed，我们将门关上，那它的状态就由open转移到closed，这里我们将open称为初始状态，
+将closed称为目标状态，分别用src和dst表示。而导致状态转移的动作我们称它为事件（event），状态的转移都是由特定事件触发的。
+ 
+```golang
+type EventDesc struct {
+    Name string 
+    Src []string 
+    Dst string  
+}
+
+type Events []EventDesc
+```
+
+  这里Name表示触发状态转移的事件名称，Src是状态转移过程中的初始状态，注意到它是一个数组，因为可以有多个不同的初始状态转移到同一个目标状态，而Dst是
+状态转移过程中的目标状态。上面的结构体就代表了有限状态机中触发状态转移的事件，可以把它理解成一个导火索，有了它，有限状态机才在不同的状态之间跳跃。
+
+  下面我们再来定义有限状态机的结构体，一个有限状态机需要哪些信息呢，首先，肯定需要一个值来标定它现在处于什么状态，也就是有限个状态中它处于哪个阶段，
+还需要一个东西来表示各个状态之间如何转移，如在事件E发生的情况下状态A转移到状态B。还需要一个回调方法集合（callbacks），它可以用于在状态转移发生时需
+要分别调用什么方法。
+
+```golang
+type eKey struct {
+    event string
+    src string
+}
+
+type cKey struct {
+    target string
+    callbackType int
+}
+
+type FSM struct {
+    current string
+    transitions map[eKey]string
+    callbacks map[cKey]string
+    transition func()
+}
+
+```
+
+  还是拿开门关门举例子，当门刚开始的状态是open，那么current=open，我们用手关门，就会查看transitions这个map中open和关门这个动作所对应的目标状态
+是什么，发现是closed，然后在状态转移的过程中还会调用注册过的callback方法。以上就是基本的实现一个FSM的思路。
+
+  下面我们来讨论一下回调方法（callback）的种类。
+  
+```golang
+const (
+    callbackNone int = iota
+    callbackBeforeEvent
+    callbackLeaveState
+    callbackEnterState
+    callbackAfterState
+)
+```
+
+  触发一次状态的导火索是一个特定的事件Event，我们记为E，因为E的发生，状态A变成状态B，所以我们一共可以定义四种回调方法，分别是在事件发生之前和之后，
+也就是callbackBeforeEvent，callbackAfterEvent，还有就是离开初始状态和进入目标状态的回调函数，callbackLeaveState和callbackEnterState。
+
+  而一个callback其实就是一个回调函数 `type Callback func(*Event)` 。
+
+```golang
+func NewFSM(initial string, events []EventDesc, callbacks map[string]Callback) *FSM {
+	var f FSM
+	f.current = initial
+	f.transitions = make(map[eKey]string)
+	f.callbacks = make(map[cKey]Callback)
+
+	// Build transition map and store sets of all events and states.
+	allEvents := make(map[string]bool)
+	allStates := make(map[string]bool)
+	for _, e := range events {
+		for _, src := range e.Src {
+			f.transitions[eKey{e.Name, src}] = e.Dst
+			allStates[src] = true
+			allStates[e.Dst] = true
+		}
+		allEvents[e.Name] = true
+	}
+
+	// Map all callbacks to events/states.
+	for name, c := range callbacks {
+		var target string
+		var callbackType int
+
+		switch {
+		case strings.HasPrefix(name, "before_"):
+			target = strings.TrimPrefix(name, "before_")
+			if target == "event" {
+				target = ""
+				callbackType = callbackBeforeEvent
+			} else if _, ok := allEvents[target]; ok {
+				callbackType = callbackBeforeEvent
+			}
+		case strings.HasPrefix(name, "leave_"):
+			target = strings.TrimPrefix(name, "leave_")
+			if target == "state" {
+				target = ""
+				callbackType = callbackLeaveState
+			} else if _, ok := allStates[target]; ok {
+				callbackType = callbackLeaveState
+			}
+		case strings.HasPrefix(name, "enter_"):
+			target = strings.TrimPrefix(name, "enter_")
+			if target == "state" {
+				target = ""
+				callbackType = callbackEnterState
+			} else if _, ok := allStates[target]; ok {
+				callbackType = callbackEnterState
+			}
+		case strings.HasPrefix(name, "after_"):
+			target = strings.TrimPrefix(name, "after_")
+			if target == "event" {
+				target = ""
+				callbackType = callbackAfterEvent
+			} else if _, ok := allEvents[target]; ok {
+				callbackType = callbackAfterEvent
+			}
+		default:
+			target = name
+			if _, ok := allStates[target]; ok {
+				callbackType = callbackEnterState
+			} else if _, ok := allEvents[target]; ok {
+				callbackType = callbackAfterEvent
+			}
+		}
+
+		if callbackType != callbackNone {
+			f.callbacks[cKey{target, callbackType}] = c
+		}
+	}
+
+	return &f
+}
+```
+
+1. 在new一个FSM的时候，我们先定义它的初始状态current，还有状态机中所有可能的状态迁移情况以及它的回调函数。
+2. 这里我们建立所有可能的状态转移的情况。
+3. 注册回调函数，也就是在事件发生前后，和改变状态的时候的回调函数。
+
+  在初始化完一个FSM之后，也就是说规定了FSM该拥有的所有状态以及状态转移时候应该调用的函数之后，我们来看看如何让这个状态机动起来，我们之前已经知道，
+触发状态机变化的就是外部的特定的事件，所以我们需要灵活地去处理这些事件，来使得我们的状态机可以动起来。
+  
+  在上面我们定义了EventDesc这个结构体，注意到那个结构体是多对一的，也就是多个初始状态对应一个目标状态，我们现在定义一个真正的一对一的事件，这里的
+一对一指的是一个初始状态对应一个目标状态。
+
+```golang
+type Event struct {
+    FSM *FSM
+    Event string
+    Src string
+    Dst string
+    Err error
+    Args []interface{}
+    canceled bool 
+    async bool
+}
+
+```
+
+  所以我们如何调用FSM并且进而使它的状态进行转化并且调用相应预先注册好的回调函数呢，我们通过向FSM实例传送一个event事件，FSM会先判断当前是否正在转移之
+中，也就是 f.transition 是否为nil，若不为nil则说明状态机正在状态转移之中，否则则说明不是，继而综合FSM当前的状态和此次遇到的事件决定目标状态，然后按
+顺序调用回调函数beforeEventCallbacks => leaveStateCallbacks => enterStateCallbacks => afterEventCallbacks。这样一次状态转移就算完成了。
+
+```golang
+func (f *FSM) Event(event string, args ...interface{}) error {
+	if f.transition != nil {
+		return &InTransitionError{event}
+	}
+
+	dst, ok := f.transitions[eKey{event, f.current}]
+	if !ok {
+		for ekey := range f.transitions {
+			if ekey.event == event {
+				return &InvalidEventError{event, f.current}
+			}
+		}
+		return &UnknownEventError{event}
+	}
+
+	e := &Event{f, event, f.current, dst, nil, args, false, false}
+
+	err := f.beforeEventCallbacks(e)
+	if err != nil {
+		return err
+	}
+
+	if f.current == dst {
+		f.afterEventCallbacks(e)
+		return &NoTransitionError{e.Err}
+	}
+
+	f.transition = func() {
+		f.current = dst
+		f.enterStateCallbacks(e)
+		f.afterEventCallbacks(e)
+	}
+
+	err = f.leaveStateCallbacks(e)
+	if err != nil {
+		return err
+	}
+
+	err = f.Transition()
+	if err != nil {
+		return &InternalError{}
+	}
+
+	return e.Err
+}
+
+func (f *FSM) Transition() error {
+	  if f.transition == nil {
+		  return &NotInTransitionError{}
+	  }
+	  f.transition()
+	  f.transition = nil
+	  return nil
+}
+```
+
+## 有限状态机的简单实践
+
+```golang
+package main
+
+import (
+    "fmt"
+    "github.com/looplab/fsm"
+)
+
+func main() {
+    fsm := fsm.NewFSM(
+        "closed",
+        fsm.Events{
+            {Name: "open", Src: []string{"closed"}, Dst: "open"},
+            {Name: "close", Src: []string{"open"}, Dst: "closed"},
+        },
+        fsm.Callbacks{},
+    )
+
+    fmt.Println(fsm.Current())
+
+    err := fsm.Event("open")
+    if err != nil {
+        fmt.Println(err)
+    }
+
+    fmt.Println(fsm.Current())
+
+    err = fsm.Event("close")
+    if err != nil {
+        fmt.Println(err)
+    }
+
+    fmt.Println(fsm.Current())
+}
+
+```
+
+  上述例子就定义了一个简单的开门和关门的状态机，这里我们门这个状态机的初始状态是closed，它可以利用open这个事件将状态从closed状态转移到open状态，
+也可以利用close这个事件将状态从open状态转移到closed状态。注意这里没有定义回调函数（fsm.Callbacks{}为空），所以这两件事件触发的时候什么事情也不
+会发生。
+
+  而调用事件就是通过fsm.Event("open”)或fsm.Event("close”)来完成的。
+  
+  上述就是实现一个简单的有限状态机的代码啦，具体的实现可以参考[Finite State Machine](http://github.com/looplab/fsm)。
+  
+  简单总结一下，实现有限状态机需要下面几件事情。
+1. 有限状态机的初始状态
+2. 有限状态机各个状态之间迁移的各种情况，也就是状态拓扑图
+3. 状态迁移时的回调方法
+

From 2077ccfe511139a4a892e6c667dd58a6bed8f900 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 15:27:53 +0800
Subject: [PATCH 05/85] Update index.md

FSM done
---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index c823d0e..8bf924b 100644
--- a/index.md
+++ b/index.md
@@ -2,4 +2,5 @@
 - [] Golang 设计模式
 - [] PKCS7 Padding
 - [] PBFT 共识算法
+- [x] 有限状态机FSM
 ```

From dab9dbdb506f31863b02496dcb0ff57f96456b9d Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 15:34:36 +0800
Subject: [PATCH 06/85] Update index.md

Update link for FSM.md
---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 8bf924b..60ff070 100644
--- a/index.md
+++ b/index.md
@@ -2,5 +2,5 @@
 - [] Golang 设计模式
 - [] PKCS7 Padding
 - [] PBFT 共识算法
-- [x] 有限状态机FSM
+- [x] 有限状态机FSM [FSM](FSM.md)
 ```

From a2d7e45bbadefa676ee597a8623a9f3827db3245 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 15:53:08 +0800
Subject: [PATCH 07/85] Create PKCS7_Padding.md

---
 PKCS7_Padding.md | 83 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)
 create mode 100644 PKCS7_Padding.md

diff --git a/PKCS7_Padding.md b/PKCS7_Padding.md
new file mode 100644
index 0000000..c04c1c6
--- /dev/null
+++ b/PKCS7_Padding.md
@@ -0,0 +1,83 @@
+  由于某些特定加密算法的要求，例如AES中的CBC，要求被加密的数据是特定块大小的整数倍，例如在AES中一个块大小即BlockSize的大小为16字节，故被加密数据的
+长度`len(src) % BlockSize == 0`，但现实生活中要求加密的数据长度往往是随机的，所以需要有一种可靠高效的数据填充方法（padding）。
+
+  现在我们的问题是这样的，一个不定长度的数据作为加密的输入，我们需要把它填充到固定的长度，即满足`len(src) % BlockSize == 0`的条件，并且在解密时
+可以获得原来的数据。
+
+  现在的前提是BlockSize为16字节，输入长度随机。流程大致经过下面几步，填充=>加密=>网络传输=>解密=>去填充。问题的难点就是如何保证去填充之后可以获取
+原来填充之前的数据。
+
+  我们的初始数据即待加密数据的格式如下
+  
+  |XX16bytesXX|XX16bytesXX|…….|XX16bytesXX|XX?bytesXX|
+  
+  填充后得到的数据格式为如下。
+  
+  |XX16bytesXX|XX16bytesXX|…….|XX16bytesXX|XX16bytesXX|
+  
+  这里我们使用 |XX16bytesXX| 表示一段16字节的随机数据。也就是说我们需要把最后的?bytes填充到固定长度的16bytes，这里？的取值范围是0到15。
+现在我们就来引入PKCS7这种填充标准。
+
+  PKCS7填充的做法是这样的，首先它会确定需要被填充的字节的长度，也就是上述所说的?bytes的那一段字节。
+```golang
+padding := aes.BlockSize - len(src)%aes.BlockSize
+```
+
+  上面的代码段非常好理解，首先对待加密的数据长度对BlockSize取模，获得我们的?bytes，再利用它与BlockSize的差值获得我们需要填充的长度值，
+这里的padding就是一个int值。
+
+  下面就是真正填充的一个逻辑了。
+  
+```golang
+padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+```
+
+  这一行代码乍看起来有点复杂，我们一步一步来看，它要做的一件事情就是获得一个用于填充的文字，而这个文字就是利用我们上述获得的padding（注意它是一个int）
+转化成一个byte后重复padding次。
+
+  也就是说假设我们的padding是7，也就是需要填充7字节的信息，那我们的padtext也就是我们填充的文字就是 7 7 7 7 7 7 7，当然我们这里的每个7都是一个byte。
+  
+  以此类推，同理我们padtext里面的每一个数字就是一个byte，6 6 6 6 6 6 应该理解成byte(6) byte(6) byte(6) byte(6) byte(6) byte(6)。
+  
+  获得了我们的填充信息后，我们将原数据与我们的填充信息相连接就行了，下面就是我们完整填充过程的函数，非常简单。
+  
+```golang
+func PKCS7Padding(src []byte) []byte {
+    padding := aes.BlockSize - len(src)%aes.BlockSize
+    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+    return append(src, padtext...)
+}
+```
+
+  理解了PKCS7中填充的标准之后，我们再来看一下如何去填充，去填充的过程也非常简单，但是在工程代码中实现还需要考虑一些特殊的边界情况。
+  
+	下面直接给出去填充的代码。
+  
+```golang
+func PKCS7Unpadding(src []byte) ([]byte, error) {
+    length := len(src)
+    unpadding := int(src[length - 1])
+	
+    if unpadding > aes.BlockSize || unpadding == 0 {
+    return nil, fmt.Errorf("invalid unpadding")
+    }
+	  
+    pad := src[len(src)-unpadding:]
+    for i := 0; i < unpadding; i++ {
+        if pad[i] != byte(unpadding) {
+            return nil, fmt.Errorf("invalid padding")
+        }
+    }
+
+    return src[:(len(src)-unpadding)], nil
+}	
+```
+  这里我们需要去填充的为src，讲src的最后一个byte取出再转换为int，这个数值就是我们之前填充字节的个数，记为unpadding，如果unpadding的值大于
+BlockSize或者出现unpadding为0的情况，这种情况是一场的因为我们填充的时候就将padding值约束在[1, BlockSize-1)，所以出现不符合要求的padding值都
+将返回异常。
+
+  然后将数组末尾取出unpadding个字节，将unpadding个字节分别与byte(unpadding)这个字节进行比较，如果不想等，则返回异常。
+
+  最后如果一切都没有问题，则返回去除padding个字节后的byte数组。
+
+

From d3fbd336bf8f0b17a3f84337feb17030a4fa6ff6 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 15:54:15 +0800
Subject: [PATCH 08/85] Update index.md

Update link for PKCS7 padding
---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 60ff070..e4f7090 100644
--- a/index.md
+++ b/index.md
@@ -1,6 +1,6 @@
 ```markdown
 - [] Golang 设计模式
-- [] PKCS7 Padding
+- [x] PKCS7 Padding [PKCS7_Padding](PKCS7_Padding.md)
 - [] PBFT 共识算法
 - [x] 有限状态机FSM [FSM](FSM.md)
 ```

From 1602b1172312a250d0a11d5f8e9decb2d0670333 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 15:59:34 +0800
Subject: [PATCH 09/85] Create README.md

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 README.md

diff --git a/README.md b/README.md
new file mode 100644
index 0000000..82b141e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+# oceanoverflow.github.io
+Pieces of valuable programming knowledges

From 16c1d9eb5a8a67845905fca0d56b9bad1da482f2 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 20:36:34 +0800
Subject: [PATCH 10/85] Create Fanin.md

Fan-in programming model, useful for aggregating messages
---
 Fanin.md | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 102 insertions(+)
 create mode 100644 Fanin.md

diff --git a/Fanin.md b/Fanin.md
new file mode 100644
index 0000000..6e15dbf
--- /dev/null
+++ b/Fanin.md
@@ -0,0 +1,102 @@
+  在对消息的处理过程中，我们常常会遇到不同来源的消息，如在Golang中这些消息来自于不同的通道，但是这些消息在本质上都是相同的，所以，如何把这些消息
+进行聚合后再处理，就是我们这篇文章需要探讨的内容。下面我们引入Fanin这种编程模型。
+
+  假设Master节点需要处理3个Slave节点的消息，这3个Slave节点的消息我们使用Golang中的通道进行模拟。
+  
+```golang
+type Message interface{}
+
+func Fanin(cs ...<-chan *Message) <-chan *Message {
+    var wg sync.WaitGroup
+    
+    out := make(chan *Message)
+    
+    send := func(c <-chan *Message) {
+        for n := range c {
+            out <- n
+        }
+        wg.Done()
+    }
+    
+    wg.Add(len(cs))
+    
+    for c := range cs {
+        go send(c)
+    }
+    
+    go func() {
+        wg.Wait()
+        close(out)
+    }()
+    return out
+} 
+```
+
+  在函数中我们新建一个用于消息输出的通道out，并且遍历所有cs中的输入通道c，将中的消息取出后再重新放入out当中，这样就达到了将多个通道合并为一个通道的
+目的。
+
+  但是上述方法也有其弊端，就是需要提前知晓所有的输入通道，但是在很多情况下，输入通道可能是动态增加的，那这种情况下我们应该怎么办呢？
+  
+```golang
+type Message interface{}
+
+type MessageFan struct {
+    ins []<-chan *Message
+    out <-chan *Message
+    lock sync.Mutex
+}
+
+func NewMessageFan() *MessageFan {
+    return &MessageFan{
+        ins: []<-chan *Message,
+        out: make(<-chan *Message),
+    }
+}
+
+func (fan *MessageFan) AddFaninChannel(channel <-chan *Message) {
+    fan.lock.Lock()
+    defer fan.lock.Unlock()
+    
+    for _, c := range fan.in {
+        if c == channel {
+            fmt.Println("Received duplicate connection")
+            return
+        }
+    }
+    
+    fan.ins = append(fan.ins, channel)
+    
+    go func() {
+        for msg := range channel {
+            out <- msg
+        }
+        
+        fan.lock.Lock()
+        defer fan.lock.Unlock()
+        
+        for i, c := range fan.ins {
+            if c == channel {
+                fan.ins = append(fan.ins[:i], fan.ins[i+1:]...)
+            }
+        }
+    }()
+}
+
+func (fan *MessageFan) GetOutChannel() {
+    return fan.out
+}
+```
+  这里我们定义了一个`MessageFan`结构体，里面包含了一个输入通道的数组和一个输出通道，在实例化`MessageFan`的时候，注意，我们仅仅对`out`进行了`make`
+操作，因为`out`通道是确定的，只有一个，而我们的输入通道却是`runtime`时决定的，所以无法使用`make`操作。
+  当我们调用`AddFaninChannel`方法的时候，我们先遍历`ins`这个通道数组，看有没有相同的实现已经加入过的通道，有则返回，没有则将它加入`ins`，同样的，
+在`goroutine`中轮询这个`channel`，将它的消息塞入`out`中，注意，在一般情况下该for循环是阻塞的，如果for循环返回，则说明`channel`已经被关闭了，即
+在代码的某处执行了`close(channel)`这个操作，所以说明它已经没有作用了，我们就可以把它在我们的ins数组中删除。    
+    
+# 总结
+  
+  上述两种方法都实现了Fanin这种编程模型，当你遇到那种需要对不同来源但是类型却相同的消息进行集中处理时，就可以考虑使用这种编程模型啦。
+    
+    
+    
+    
+    

From feb7cd32af6ab83957f071d189d4f978347838b1 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 20:37:53 +0800
Subject: [PATCH 11/85] Update index.md

Update table of contents
---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index e4f7090..2562889 100644
--- a/index.md
+++ b/index.md
@@ -3,4 +3,5 @@
 - [x] PKCS7 Padding [PKCS7_Padding](PKCS7_Padding.md)
 - [] PBFT 共识算法
 - [x] 有限状态机FSM [FSM](FSM.md)
+- [x] Fanin [Fan-in](Fanin.md)
 ```

From d3004a7d8bec08b8414ff202b53b43de2da77bf8 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Wed, 25 Apr 2018 20:41:16 +0800
Subject: [PATCH 12/85] Fix stupid bug

---
 index.md | 2 --
 1 file changed, 2 deletions(-)

diff --git a/index.md b/index.md
index 2562889..33032a5 100644
--- a/index.md
+++ b/index.md
@@ -1,7 +1,5 @@
-```markdown
 - [] Golang 设计模式
 - [x] PKCS7 Padding [PKCS7_Padding](PKCS7_Padding.md)
 - [] PBFT 共识算法
 - [x] 有限状态机FSM [FSM](FSM.md)
 - [x] Fanin [Fan-in](Fanin.md)
-```

From 3372586c38a9a635897f508ddf4e28cd6cc85751 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Thu, 26 Apr 2018 13:36:58 +0800
Subject: [PATCH 13/85] Create EventProcessEngine.md

---
 EventProcessEngine.md | 167 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 167 insertions(+)
 create mode 100644 EventProcessEngine.md

diff --git a/EventProcessEngine.md b/EventProcessEngine.md
new file mode 100644
index 0000000..b01693f
--- /dev/null
+++ b/EventProcessEngine.md
@@ -0,0 +1,167 @@
+# 实现一个事件处理引擎
+
+  在许多情况下，尤其是在搞一致性算法的时候，如何对事件（Event）集中进行处理就变成了一件很重要的事情，下面，我们来介绍如何写一个简单的事件处理引擎，
+并且向上层提供一个简单可用（无脑）的API供其他程序员使用。
+
+  首先我们需要明白我们要对各种各样的事件进行处理，例如，在PBFT中，我们的消息类型有`Preprepare`，`Prepare`，`Commit`，`ViewChange`等等，具体的
+消息类型的定义我们可以使用protobuf来，而无论是什么事件，我们在Golang中我们都可以把它们抽象成一个 `interface{}` 。
+
+```golang
+type Event interface{}
+```
+
+  这里我们定义的`Event`是我们以后围绕着处理的中心，我们继续看。定义完了`Event`，我们还要定义一个`Event`的接受者（Receiver），也就是事物处理的核心。
+  
+```golang
+type Receiver interface {
+    ProcessEvent(e Event) Event
+}
+```
+  
+  在这里我们注意到这是一个`interface`，只要你定义的对象实现了`ProcessEvent`这个方法，那么它就是一个`Receiver`，当然，你自己定义的`ProcessEvent`
+里面才是真正的业务逻辑，这里仅仅提供一个框架而已。
+
+  定义完了事件的接受者，`Receiver`，我们来看下更高层次的管理者`Manager`，如果把`Manager`想象成公司里的领导，那么他负责的事情就是调度，并且指派任务
+给专门的个体也就是我们的`Receiver`去处理。`Receiver`是具体干活的，而`Manager`则是分配任务的。
+
+
+```golang
+type Manager interface {
+    Inject(Event)
+    Queue() <-chan Event
+    SetReceiver(Receiver)
+    Start()
+    Halt()
+} 
+
+type managerImpl {
+    threaded 
+    receiver Receiver
+    events chan Event
+}
+
+type threaded struct {
+    exit chan struct{}
+}
+```
+
+  一个Manager的职能具体有两大点，一就是管理事件的队列（events这个通道），二就是取出队列中的事件，指派接受者（SetReceiver(Receiver)）进行处理
+（Start）。
+```golang
+func (em *managerImpl) Queue() {
+    return em.events
+}
+
+func (em *managerImpl) SetReceiver(receiver Receiver) {
+    em.receiver = receiver
+}
+
+func NewManagerImpl() Manager {
+    return &managerImpl{
+        events: make(chan Event),
+        threaded: threaded{make(chan struct{})}
+    }
+}
+```
+
+```golang
+func (em *managerImpl) Start() {
+    go em.eventLoop()
+}
+```
+  `Manager`的启动就是就是开了一个goroutine对消息进行循环处理，下面来看一下`eventLoop`的实现。
+
+```golang
+func (em *managerImpl) eventLoop() {
+    for {
+        select {
+        case e := <-em.events:
+            em.Inject(e)
+        case <-em.exit:
+            logger.Debug("event told to exit")
+            return
+        }
+    }
+}
+```
+
+  `eventLoop`里面是咋一看是一个死循环，但它还是有出口的，它通过`for select`机制一直去轮询`em.events`和`em.exit`这两个通道，如果`em.exit`这
+个通道有值，则说明该事件处理循环要退出了，这就是程序的一个出口，但如果`em.events`有消息返回，我们则将它插入到我们`manager`的事件队列里等待被处理，
+也就是调用`em.Inject(e)`。
+
+```golang
+func (em *managerImpl) Inject(event Event) {
+    if em.Receiver != nil {
+        SendEvent(em.Receiver, event)
+    }
+}
+```
+
+  `Inject`行为的主体是`manager`，`manager`把任务（event）分配给我们的receiver。
+  
+```golang
+func SendEvent(receiver Receiver, event Event) {
+    next := Event
+    for {
+        next  := receiver.ProcessEvent(next)
+        if next == nil {
+            break
+        }
+    }
+}
+```
+  `SendEvent`函数才是真正干活的主力，它主要就是调用`Receiver`实现定义好的`ProcessEvent`方法，因为处理一个事件后可能会返回另一件事件，所以循环
+处理它直到为空时候返回。看到这里，你可能会不理解处理意见事件会什么会返回另一件事件呢，其实现实中处理完A事件返回B事件的例子有很多，例如在PBFT中，
+处理完`Preprepare`事件，我们赶忙就要开始处理`Prepare`事件了。这种例子还有很多，以后有机会我们再详细介绍。
+
+  上面就是我们整个事件处理的引擎了，不难，但是设计的非常精妙，下面我们来看如何使用它。
+```golang
+type aEvent Event
+type bEvent Event
+type cEvent Event
+
+type Instance struct{
+    manager Manager
+}
+
+func (i *Instance) ProcessEvent(e Event) Event {
+    switch et := e.(type) {
+    case aEvent:
+        fmt.Println("Received A Event, return B Event")
+        return bEvent
+    case bEvent:
+        fmt.Println("Received B Event, return C Event")
+        return cEvent
+    case cEvent:
+        fmt.Println("Received C Event, return nil")
+        return nil
+    default:
+        fmt.Println("Received undefined message type")
+    }
+}
+
+func main() {
+    var i Instance
+    i.manager = NewManagerImpl()
+    i.manager.SetReceiver(i)
+    i.manager.Start()
+
+    i.manager.Queue() <- aEvent
+}
+```
+
+  这里我们定义了三种事件类型，并且定义了一个包含manager的结构体，这个结构体实现了`ProcessEvent`方法，也就是说它实现了`Receiver`这个接口，
+`ProcessEvent`方法里通过判断接收到的event的类型，来进行相应的处理，注意到处理完`aEvent`会返回`bEvent`，这就说我们说的处理完一件事件再返回
+一件事件的例子了。
+
+  在`main`函数中，我们新起一个`Instance`实例，并初始化里面的`manager`，把`manager`的`receiver`设置为自己，因为是`Instance`实现了
+`ProcessEvent`方法，所以说谁实现了`ProcessEvent`方法，`SetReceiver`就设置为谁，最后再用`Start`起一个goroutine不断地处理接收进行的事件。
+
+  在我们向manager管理的事件队列中插入一个`aEvent`事件后，`eventLoop`就会开始处理事件，命令行中应该会依次输出：
+```
+Received A Event, return B Event
+Received B Event, return C Event
+Received C Event, return nil
+```
+
+  这个事件处理引擎的实现非常精妙，还是希望读者可以反复阅读几次。

From 62ab19e94e8e89747ff94877c46cd02c63e6493f Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Thu, 26 Apr 2018 13:38:52 +0800
Subject: [PATCH 14/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 33032a5..caae0ea 100644
--- a/index.md
+++ b/index.md
@@ -3,3 +3,4 @@
 - [] PBFT 共识算法
 - [x] 有限状态机FSM [FSM](FSM.md)
 - [x] Fanin [Fan-in](Fanin.md)
+- [x] 事件处理引擎 [EventProcessEngine](EventProcessEngine.md)

From 96567a7b5bb031a620741e2836a65e72ae1076cd Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Thu, 26 Apr 2018 13:57:52 +0800
Subject: [PATCH 15/85] Update index.md

---
 index.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/index.md b/index.md
index caae0ea..4cd94ee 100644
--- a/index.md
+++ b/index.md
@@ -1,6 +1,10 @@
 - [] Golang 设计模式
 - [x] PKCS7 Padding [PKCS7_Padding](PKCS7_Padding.md)
 - [] PBFT 共识算法
+- [] Paxos & Raft
 - [x] 有限状态机FSM [FSM](FSM.md)
 - [x] Fanin [Fan-in](Fanin.md)
 - [x] 事件处理引擎 [EventProcessEngine](EventProcessEngine.md)
+- [] p2p Discoverer
+- [] gRPC & Protobuf
+

From 21cfc4ee18d057c9253ef9c1e7aad4b0d681194d Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Thu, 26 Apr 2018 20:34:31 +0800
Subject: [PATCH 16/85] Create Protobuf.md

---
 Protobuf.md | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 117 insertions(+)
 create mode 100644 Protobuf.md

diff --git a/Protobuf.md b/Protobuf.md
new file mode 100644
index 0000000..83fffbd
--- /dev/null
+++ b/Protobuf.md
@@ -0,0 +1,117 @@
+# Protocol Buffer
+
+  Protobuf是一种用于信息交换的数据格式，你们可能会问我可以用JSON，再不济XML也可以用啊，为什么要接触这个完全没有用过的玩意儿呢？答案只有两个字：高效。
+使用XML不仅会占用更多的存储空间，而且会降低整个系统的性能。
+
+  搞过分布式系统的朋友一定知道，消息在分布式系统中起着至关重要的作用，例如两个节点之间相互交换自己的状态信息，一个网络中节点之间的交流就需要负载在消息
+之上，与人类一样，只有节点之间有一套共用的语言，分布式系统才能正常工作下去。而且protobuf可以让我们非常方便的定义消息的类型，它还提供了供我们序列化和
+反序列化的API，下面我们就来看看该如何使用它。
+
+## 前期准备
+  首先需要安装protobuf，使用macOS的用户可以考虑使用brew进行安装，安装完了之后还需要安装Go语言的protobuf插件—proto-gen-go，这个插件会被安装在
+`$GOPATH/bin`目录下。
+
+```
+$ brew install protobuf
+$ go get -u github.com/golang/protobuf/protoc-gen-go
+```
+
+## 定义我们自己的消息格式
+
+  使用protobuf，我们只需要定义一个以 `.proto` 结尾的文件，这里我们以PBFT算法中的几个消息类型为例子进行讲解，首先注意到”=1”， “=2”这样的标记，
+这些标记是为了二进制编码用的。其次，如果一个消息中有多个类似的成员，也就是一个数组，我们可以使用 `repeated` 这个关键字来表示。
+
+```protobuf
+syntax = "proto3";
+
+import "google/protobuf/timestamp.proto";
+
+package pbft;
+
+message request {
+    google.protobuf.Timestamp timestamp = 1;
+    bytes payload = 2;
+    uint64 replica_id = 3;
+    bytes signature = 4;
+}
+
+message request_batch {
+    repeated request batch = 1;
+}
+
+message pre_prepare {
+    uint64 view = 1;
+    uint64 sequence_number = 2;
+    string batch_digest = 3;
+    request_batch request_batch = 4;
+    uint64 replica_id = 5;
+}
+
+message prepare {
+    uint64 view = 1;
+    uint64 sequence_number = 2;
+    string batch_digest = 3;
+    uint64 replica_id = 4;
+}
+
+message commit {
+    uint64 view = 1;
+    uint64 sequence_number = 2;
+    string batch_digest = 3;
+    uint64 replica_id = 4;
+}
+```
+
+## 使用protoc编译器编译我们的消息
+```
+protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/messages.proto
+```
+  在命令行中运行上述命令，`-I` 表示你的源文件所在的目录，`--go_out`  表示你指定的生成目标文件（messages.pb.go）的目录，一般来说与源文件所在的目录
+相同，最后就是我们需要编译的源文件的路径。
+
+## 使用protobuf提供的API对消息进行写操作
+  
+  我们可以使用 `proto.Marshal()` 对数据进行序列化。序列化之后得到的是一个字节数组（ []byte ）。
+
+```golang
+prep := &pb.Prepare{
+    view: 1,
+    SequenceNumber: 2,
+    BatchDigest: "aaaaa",
+    ReplicaId: 11,    
+}
+
+out, err := proto.Marshal(prep)
+
+if err != nil {
+    fmt.Printf("Error marshalling prepare: %s", err) 
+    return 
+}
+
+if err := ioutil.WriteFile(fname, out, 0644); err != nil {
+    fmt.Printf("Error writing file: %s", err) 
+    return    
+}
+```
+
+## 使用protobuf提供的API对消息进行读操作
+  
+  对编码过的数据调用 `proto.Unmarshal` 就可以进行反序列化操作得到原来的信息。
+
+```golang
+in, err := ioutil.ReadFile(fname)
+
+if err != nil {
+    fmt.Printf("Error reading file: %s", err) 
+    return    
+}
+
+prep := &pb.Prepare{}
+
+if err := proto.Unmarshal(in, prep); err != nil {
+    fmt.Printf("Error unmarshalling prepare: %s", err)
+    return
+}
+```
+
+  当然了，上面两个例子是将序列化的数据存储到文件中，同样的，我们可以将序列化后的数据直接在网上传播，因为序列化后的数据已经是 `wire format` 了。

From 999fb82ab0244e42d067bfb70759f50c11663ed1 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Thu, 26 Apr 2018 20:35:46 +0800
Subject: [PATCH 17/85] Update index.md

---
 index.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 4cd94ee..9b82b94 100644
--- a/index.md
+++ b/index.md
@@ -6,5 +6,6 @@
 - [x] Fanin [Fan-in](Fanin.md)
 - [x] 事件处理引擎 [EventProcessEngine](EventProcessEngine.md)
 - [] p2p Discoverer
-- [] gRPC & Protobuf
+- [] gRPC
+- [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
 

From 78532ce75c053e9b6dcdd5a6b99a405d10a6ff7b Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Thu, 26 Apr 2018 20:40:17 +0800
Subject: [PATCH 18/85] Update index.md

---
 index.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/index.md b/index.md
index 9b82b94..51d4b14 100644
--- a/index.md
+++ b/index.md
@@ -8,4 +8,8 @@
 - [] p2p Discoverer
 - [] gRPC
 - [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
+- [] Leaky Buffer
+- [] 密码学基础
+- [] TCP/IP
+- [] 实现一个简单的Shadowsocks
 

From 3f67ab8c0826bd9c83d7b7c58c14ec3b710f19a2 Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Fri, 27 Apr 2018 13:50:55 +0800
Subject: [PATCH 19/85] Create LeakyBuffer.md

---
 LeakyBuffer.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 76 insertions(+)
 create mode 100644 LeakyBuffer.md

diff --git a/LeakyBuffer.md b/LeakyBuffer.md
new file mode 100644
index 0000000..271db0a
--- /dev/null
+++ b/LeakyBuffer.md
@@ -0,0 +1,76 @@
+# Leaky Buffer
+
+  在科学上网服务中，客户端不断地向shadowsocks服务器发送数据，服务器从网络中读取经过加密的数据然后进行解密操作，为了避免重复的分配内存和释放内存操作，
+我们引入 `LeakyBuffer` 这种编程模型，`LeakyBuffer` 通常也被叫做 `LeakyBucket` ，或者说是漏桶模型。`LeakyBuffer` 维护了固定个数个字节数组
+（free list），如果我们的程序需要使用内存， 那么它会从 `LeakyBuffer` 维护的 `freelist` 去拿，如果 `freelist` 中没有了，则再重新make一个，
+当程序使用完了这段内存，不像通常那么被golang的runtime进行垃圾回收操作，我们把它重新放回 `LeakyBuffer` 维护的 `freelist` 中。
+
+  下面我们来看一下 `Leaky Buffer` 的实现吧。
+
+```golang
+const leakyBufferSize = 4096
+const maxNum = 2048
+
+type LeakyBuffer struct {
+    bufferSize  int
+    freeList chan []byte
+}
+```
+
+  `LeakyBuffer` 的实现非常简单，它主要维护了两个变量，一就是每个 `buffer`  的大小，而就是一个 `freelist` ，真正用于维护我们的内存。
+
+  在上面的代码中我们定义每个 `buffer` 的大小是4096，这里的单位是字节，`freelist` 的长度是2048，所以我们这个 `LeakyBuffer` 总共可以使用的内存
+大小就是 4096 * 2048 B 也就是 8MB ，这对于一般的网络应用已经足够了。
+
+```golang
+var leakyBuf = NewLeakyBuffer(maxNum, leakyBufferSize)
+
+func NewLeakyBuffer(n, bufSize int) *LeakyBuffer {
+    return &LeakyBuffer{
+        bufferSize: bufSize,
+        freeList: make(chan []byte, n)
+    }
+}
+```
+
+  `NewLeakyBuffer` 返回一个 `LeakyBuffer` 指针，它维护的 `freelist`  就是一个容量为n的通道。
+
+  下面我们来看看如何使用 `LeakyBuffer` ，`LeakyBuffer` 只定义了两种操作，存和取。
+
+## 取操作
+  `Get` 操作从 `LeakyBuffer` 所维护的 `freelist` 的通道中取出一个字节数组，如果 `freelist` 为空，则说明没有多余的空闲字节数组，则自己make一个字节数组然后返回。
+
+```golang
+func (lb *LeakyBuffer) Get() (b []byte) {
+    select {
+    case b := <- lb.freeList:
+    default:
+        b := make([]byte, lb.bufferSize)
+    }
+    return
+}
+```
+
+
+## 存操作
+  当程序使用完了这段内存空间后，我们将它重新放回 `LeakyBuffer` 中进行回收而不是用 `golang runtime` 进行垃圾回收，注意一种特殊情况，就是当
+`freelist` 已经满了，这时我们会执行default case， 也就是什么事情也不干，然后这个无家可归的字节数组就会自动被系统进行垃圾回收，这就是
+`LeakyBuffer` 这个名字中 Leaky 的由来，也就是有可能会泄漏出去。
+
+```golang
+func (lb *LeakyBuffer) Put(b []byte) {
+     if lb.bufferSize != len(b) {
+         panic("invalid buffer size that's put into leaky buffer")
+     }
+     select {
+     case lb.freeList <- b:
+     default:
+     }
+     return
+}
+```
+
+  上述就是 `LeakyBuffer` 的实现了，如果下次你遇到需要重复分配很多相同大小的内存空间时，不妨可以使用一下这个编程模型。
+
+## Golang 标准库的实现
+  在 golang 标准库中也实现了类似于 `LeakyBuffer` 的接口，`sync.Pool` ，有兴趣的读者不妨阅读一下它的源码，这里就不再重复介绍了。

From a7ca632702c4f7a809a60c5b9187468cd0658dfe Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Fri, 27 Apr 2018 13:51:45 +0800
Subject: [PATCH 20/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 51d4b14..42e9789 100644
--- a/index.md
+++ b/index.md
@@ -8,7 +8,7 @@
 - [] p2p Discoverer
 - [] gRPC
 - [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
-- [] Leaky Buffer
+- [] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
 - [] 密码学基础
 - [] TCP/IP
 - [] 实现一个简单的Shadowsocks

From 2c2ce7fd5793bd53b57df91d1fe6ca7993b7836c Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Fri, 27 Apr 2018 13:58:24 +0800
Subject: [PATCH 21/85] Update index.md

---
 index.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 42e9789..fbd9359 100644
--- a/index.md
+++ b/index.md
@@ -6,10 +6,12 @@
 - [x] Fanin [Fan-in](Fanin.md)
 - [x] 事件处理引擎 [EventProcessEngine](EventProcessEngine.md)
 - [] p2p Discoverer
-- [] gRPC
 - [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
+- [] 使用gRPC来进行节点之间的通信
 - [] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
 - [] 密码学基础
 - [] TCP/IP
 - [] 实现一个简单的Shadowsocks
+- [] 使用 Viper & Cobra 快速构建一个命令行工具
+
 

From dc6b683a61b402b7067aa7004882202de3d6a6bc Mon Sep 17 00:00:00 2001
From: oceanoverflow <OceanOverflow@users.noreply.github.com>
Date: Fri, 27 Apr 2018 15:10:11 +0800
Subject: [PATCH 22/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index fbd9359..5e20fef 100644
--- a/index.md
+++ b/index.md
@@ -5,6 +5,7 @@
 - [x] 有限状态机FSM [FSM](FSM.md)
 - [x] Fanin [Fan-in](Fanin.md)
 - [x] 事件处理引擎 [EventProcessEngine](EventProcessEngine.md)
+- [] EventTimer 事件定时器
 - [] p2p Discoverer
 - [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
 - [] 使用gRPC来进行节点之间的通信

From a3a070e87343ed4fdcc9fb08c4d10678bb79a38a Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Fri, 27 Apr 2018 22:24:34 +0800
Subject: [PATCH 23/85] Create EventTimer.md

---
 EventTimer.md | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 245 insertions(+)
 create mode 100644 EventTimer.md

diff --git a/EventTimer.md b/EventTimer.md
new file mode 100644
index 0000000..fda0326
--- /dev/null
+++ b/EventTimer.md
@@ -0,0 +1,245 @@
+# Event Timer
+
+  还记得我们在 [EventProcessEngine](EventProcessEngine.md) 里讲过的 `Event Manager` 吗，我们这里要实现的 `Event Timer` 其实和那个是
+有点耦合的，本篇博客与[EventProcessEngine](EventProcessEngine.md) 是姊妹篇，如果没有看过的小伙伴建议先看上一篇再来看这一篇。
+
+  关于定时器，golang的标准库已经提供了一个免费的解决方案给我们了 。标准库里的 `Timer` 结构体里包含一个 `<- chan Time` 通道，在经历过一定的时间
+后，该通道就会返回，这样就达到了倒计时的目的，当然还有一个 `Reset` 方法用于重置我们的定时器。
+
+```golang
+type Timer struct {
+    C <- chan Time
+}
+
+func AfterFunc(d Duration, f func()) *Timer 
+
+func NewTimer(d Duration) *Timer
+
+func (t *Timer) Reset(d Duration) bool
+
+func (t *Timer) Stop() bool
+```
+
+  那么为什么要实现一个自己的定时器呢，当然是觉得官方提供的不够用啦。
+
+  先说一说我们的 `EventTimer` 会在哪里用到，例如在分布式系统中，主节点需要向从节点周期性发送心跳消息，以期望从节点可以判定主节点有没有出现宕机
+之类的故障，所以每个从节点都需要设置一个心跳超时的定时器，如果在规定的时间内，从节点接收到了来自主节点的心跳消息，那么从节点将定时器重置，如果没有
+收到，那么从节点会发起申请成为主节点的请求。
+
+  下面例举几个在PBFT算法中定义的几个定时器超时的事件。
+
+```golang
+// viewChangeTimerEvent is sent when the view change timer expires
+type viewChangeTimerEvent struct{}
+
+// viewChangeResendTimerEvent is sent when the view change resend timer expires
+type viewChangeResendTimerEvent struct{}
+
+// batchTimerEvent is sent when the batch timer expires
+type batchTimerEvent struct{}
+```
+
+  所以我们接下来需要实现的定时器需要有两个功能，第一点就是老功能，定时操作，第二点是增加的新功能，当定时器超时将我们预设好的事件发送给我们的事件管理
+器（Event Manager）作进一步处理。
+
+  首先，我们来定义一下我们自己实现的 `Timer` 需要实现的接口，作为一个定时器，第一要务肯定是定时了，所以我们需要它有倒计时的功能，当倒计时结束我们
+发送事先定义好的事件给我们的事件管理器（Event Manager），第二点我们需要在不需要它的时候随时停止它。
+
+```golang
+type Event interface{}
+
+type Timer interface {
+    SoftReset(timeout time.Duration, event Event)
+    Reset(timeout time.Duration, event Event)
+    Stop()
+    Halt()
+}
+
+type threaded struct {
+    exit chan struct{}
+}
+
+type timerImpl struct {
+    threaded 
+    timerChan <- chan time.Time
+    startChan chan *timerStart
+    stopChan  chan struct{}
+    manager   Manager
+}
+
+type timerStart struct {
+    hard     bool
+    event    Event
+    duration time.Duration
+}
+
+func newTimer(manager Manager) Timer {
+    et := &timerImpl{
+        threaded:  threaded{make(chan struct{})},
+        startChan: make(chan *timerStart),
+        stopChan:  make(chan struct{}),
+        manager: manager,
+    }
+    go et.loop()
+    return et
+}
+```
+
+  interface `Timer` 中的 `Reset` 和 `SoftReset` 方法的功能都是让定时器重新开始倒计时，倒计时结束向我们的事件管理器发送事件，然后由事件管理器
+对我们的事件进行集中处理。它们之间的区别就是 `Reset` 会清除掉之前所有定时的事件，而 `SoftReset` 则不会，`Stop` 和 `Halt` 则非常简单，就是停止
+定时器，但`Stop` 会清除掉所有定时的事件。
+
+  `timerImpl`  实现了 `Timer` 接口，这里就可以清楚地看到 `Reset` 和 `SoftReset` 就是向 `startChan` 中发送我们的倒计时事件，也就是和我们
+的定时器申明，在 `timeout` 这么长的时间后，发射一个 `event` 事件。
+
+```golang
+func (t *timerImpl) Reset(timeout time.Duration, event Event) {
+    t.startChan <- &timerStart{
+        duration: timeout,
+        event: event,
+        hard: true,
+    }
+}
+
+func (t *timerImpl) SoftReset(timeout time.Duration, event Event) {
+     t.startChan <- &timerStart{
+         duration: timeout,
+         event: event,
+         hard: false,
+     }
+}
+
+func (t *timerImpl) Stop() {
+    et.stopChan <- struct{}
+}
+
+func (t *timerImpl) Halt() {
+    select {
+    case <-t.threaded.exit:
+        logger.Warning("Attempted to halt a threaded object twice")
+    default:
+        close(t.threaded.exit)
+    }
+}
+```
+
+  接下来我们用工厂模式来新建我们的 `Timer`， 使用工厂模式有很多好处，我们会在另一篇博客中详细讲述它的用法和好处。
+
+```golang
+type TimerFactory interface {
+    CreateTimer() Timer
+}
+
+type timerFactoryImpl struct {
+    manager Manager
+}
+
+func NewTimerFactory(manager Manager) TimerFactory {
+    return &timerFactoryImpl(manager)
+}
+
+func (etf *timerFactoryImpl) CreateTimer() Timer {
+    return newTimer(etf.manager)
+}
+```
+
+  最后我们来看一下定时器最核心的函数 `loop()` 的实现。
+
+```golang
+func (et *timerImpl) loop() {
+	var eventDestChan chan<- Event
+	var event Event
+
+	for {
+		// A little state machine, relying on the fact that nil channels will block on read/write indefinitely
+
+		select {
+    // case 1.
+		case start := <-et.startChan:
+			if et.timerChan != nil {
+				if start.hard {
+					logger.Debug("Resetting a running timer")
+				} else {
+					continue
+				}
+			}
+			logger.Debug("Starting timer")
+			et.timerChan = time.After(start.duration)
+			if eventDestChan != nil {
+				logger.Debug("Timer cleared pending event")
+			}
+			event = start.event
+			eventDestChan = nil
+    // case 2.
+		case <-et.stopChan:
+			if et.timerChan == nil && eventDestChan == nil {
+				logger.Debug("Attempting to stop an unfired idle timer")
+			}
+			et.timerChan = nil
+			logger.Debug("Stopping timer")
+			if eventDestChan != nil {
+				logger.Debug("Timer cleared pending event")
+			}
+			eventDestChan = nil
+			event = nil
+    // case 3.
+		case <-et.timerChan:
+			logger.Debug("Event timer fired")
+			et.timerChan = nil
+			eventDestChan = et.manager.Queue()
+    // case 4.
+		case eventDestChan <- event:
+			logger.Debug("Timer event delivered")
+			eventDestChan = nil
+    // case 5.
+		case <-et.exit:
+			logger.Debug("Halting timer")
+			return
+		}
+	}
+}
+```
+
+  虽然又一点恐怖，还是一步一步来吧。
+
+  首先，说一下大致的思路，`loop()` 方法在开头定义了 `eventDestChan` 通道变量和 `event` 变量分别用于记录事件发送的目的地和事件本身。`loop()` 
+方法使用 `for select` 机制去轮询 `timerImpl` 中定义的几个通道。并且5个case条件中都是等待从通道中读取数据，2和5都是非正常流程，而1，3，4则是
+正常的流程。我们先从非正常流程说起。
+
+  当程序某处调用 `Stop()` 方法时，`stopChan` 里就会有数据 `struct{}` ，`case 2` 就会被执行，我们分别将`timerChan`，`eventDstChan`，
+`event` 设置为nil就可以达到暂停的目的了，非常简单。
+
+  当程序某处调用 `Halt()` 方法时，同样道理， `case 5` 会被执行，整个定时器将会被暂停，然后返回，这里也没有什么难度。
+
+  接下来我们来捋一捋正常的流程也就是倒计时结束时发射事件的流程。一般来说正常的流程无非是程序某处调用了 `Reset()` 或者 `SoftReset()` 方法，那么
+我们的 `startChan` 里面就会有一个 `timerStart` 结构体，此时 `case 1` 就会被执行，然后判断当前时候是否有正在倒计时的时间，如果有并且新事件为
+hard，那么我们可以重置之前的倒计时，如果不是，则进入下一个for循环中。之后我们通过设置 `et.timerChan = time.After(start.duration)` 来真正
+开始倒计时，并且设置倒计时结束后要发生的event，但此时 `eventDestChan` 仍应设置为nil，毕竟倒计时还没有结束呢，超时事件不能马上发射。
+
+  5，4， 3， 2， 1，眼瞧着我们之前设置好的倒计时结束了，那么我们的 `case 3` 就会被紧接着执行了，倒计时结束，说明超时事件已经触发了，但是现在我们
+超时事件究竟应该发送给谁还不知道，通过设置 `eventDestChan = et.manager.Queue()` 就将我们的事件目的地设置为我们的事件管理器，然后通过设置
+`et.timerChan = nil` 就可以暂停我们的定时器了。
+
+  执行完 `case 3`， 注意到我们现在 `event` 和 `eventDstChan` 都已经不为nil了，那么终于可以执行我们的 `case 4` 了，此处才真正地将我们的超时
+事件发送给我们的事件处理的目的地，之后的流程就交给我们的事件管理器（Event Manager）来处理啦。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

From 4aa4631ddeb23f42cd717be062c6e222dea596a9 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Fri, 27 Apr 2018 22:25:45 +0800
Subject: [PATCH 24/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 5e20fef..1c1b86d 100644
--- a/index.md
+++ b/index.md
@@ -5,7 +5,7 @@
 - [x] 有限状态机FSM [FSM](FSM.md)
 - [x] Fanin [Fan-in](Fanin.md)
 - [x] 事件处理引擎 [EventProcessEngine](EventProcessEngine.md)
-- [] EventTimer 事件定时器
+- [x] 事件定时器 [EventTimer](EventTimer.md)
 - [] p2p Discoverer
 - [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
 - [] 使用gRPC来进行节点之间的通信

From 2dc3eedd0c388d2eda8449b0540dc4b3fe1e1d89 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sat, 28 Apr 2018 13:48:21 +0800
Subject: [PATCH 25/85] Create FactoryPattern.md

---
 FactoryPattern.md | 149 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 149 insertions(+)
 create mode 100644 FactoryPattern.md

diff --git a/FactoryPattern.md b/FactoryPattern.md
new file mode 100644
index 0000000..e4528e0
--- /dev/null
+++ b/FactoryPattern.md
@@ -0,0 +1,149 @@
+# 工厂模式
+  工厂模式是设计模式里面的一种，具体点属于创建模式（creational pattern），它提供了一种创建对象的最佳方式。与传统的new来进行初始化对象不同，
+工厂模式在初始化对象时不会向客户端暴露创建的逻辑，也就减少了出错时到处修改的麻烦，工厂方法虽然稍为增加了程序猿的工作量，但会给系统带来更大的可
+拓展性和尽量少的修改。
+
+  下面我们来看一下如何使用工厂模式，工厂模式有几个要点，掌握住了几个要点，就可以轻松写代码了。
+
+* Creator: 申明我们的工厂方法，会返回抽象的Product
+* Product: 申明我们的工厂方法会返回的东西
+* Concrete Creator: 具体的工厂，返回一个特定的Product或者说是Concrete Product
+* Concrete Product: 具体的Product的实现
+
+我们以实现一个制造电脑组件的工厂方法作为例子来深入了解一下工厂模式的使用吧，我们的工厂会生产鼠标，键盘，显示器这三种组件，我们把工厂定义为接口
+`Factory` ，而生产的产品定义为 `Product` 。`Factory`  通过 `Create()` 来生产 `Product` ，这就是工厂方法的精髓所在。
+
+```golang
+type Factory interface {
+    Create(spec Spec) Component
+}
+
+type Component interface {
+    Make(io.Writer) error
+}
+
+type Spec struct {
+    MadeIn string
+}
+```
+
+  下面我们都实现了三种产品，分别是 `Mouse` ，`Keyboard` 和 `Screen` 。注意到他们都实现了 `Component` 接口，说明它们都是电脑的组件。
+  
+```golang
+type Mouse struct {
+    MadeIn    string
+    Bluetooth bool
+}
+
+func (m *Mouse) Make(w io.Writer) error {
+    _, err := fmt.Fprintf(w, "The mouse is made in %s, and bluetooth enabled is %v\n", m.MadeIn, m.Bluetooth)
+    return err
+}
+```
+
+```golang
+type Keyboard struct {
+    MadeIn    string
+    Bluetooth bool
+}
+
+func (k *Keyboard) Make(w io.Writer) error {
+    _, err := fmt.Fprintf(w, "The Keyboard is made in %s, and bluetooth enabled is %v\n", k.MadeIn, k.Bluetooth)
+    return err
+}
+```
+
+```golang
+type Screen struct {
+    MadeIn     string
+    Resolution uint64
+}
+
+func (s *Screen) Make(w io.Writer) error {
+    _, err := fmt.Fprintf(w, "The Screen is made in %s, And the resolution is %d\n", s.MadeIn, s.Resolution)
+    return err
+}
+```
+
+  然后我们定义具体的工厂，来生产具体的电脑组件，`MouseFactory` ，`KeyboardFactory` 和 `ScreenFactory` 都实现了 `Factory` 接口，并且返回
+具体的 `Component` 例如 `Mouse` ，`Keyboard` 和 `Screen` 。也就是说让子类决定实例化哪一个工厂类，工厂模式使得其创建过程延迟到子类，当然这里
+说子类并不是特别恰当，毕竟golang里没有类的概念。
+
+```golang
+type MouseFactory struct{}
+
+func (mf *MouseFactory) Create(spec Spec) Component {
+    return &Mouse{
+        MadeIn:    "China",
+        Bluetooth: true,
+    }
+}
+```
+
+```golang
+type KeyboardFactory struct{}
+
+func (kf *KeyboardFactory) Create(spec Spec) Component {
+    return &Keyboard{
+        MadeIn:    "China",
+        Bluetooth: false,
+    }
+}
+```
+
+```golang
+type ScreenFactory struct{}
+
+func (sf *ScreenFactory) Create(spec Spec) Component {
+    return &Screen{
+        MadeIn: "China",
+        Resolution: 4000000,
+    }
+}
+```
+
+  `Computer` 这个结构体包含了不同的工厂，这就可以让我们自定义`Computer` 所包含的内容。
+
+```golang
+type Computer struct {
+    ComponentFactories []Factory
+}
+
+
+func (c *Computer) Make(w io.Writer) error {
+    spec := Spec{
+        MadeIn: "China",
+    }
+
+    if _, err := fmt.Fprintln(w, "Computer Manufacture Begin"); err != nil {
+        return err
+    }
+
+    for _, factory := range c.ComponentFactories {
+        component := factory.Create(spec)
+        if err := component.Make(w); err != nil {
+            return err
+        }
+    }
+
+    _, err := fmt.Fprintln("Computer Manufacture Done")
+    return err
+}
+```
+
+  注意到通过工厂模式来产生实例时非常简单，因为我们之前已经把代码封装好了，这样就减少了出现错误或者业务需求改变时修改代码的机会了。
+
+```golang
+c := &Computer{
+    ComponentFactories: []Factory{
+        &MouseFactory{},
+        &ScreenFactory{},
+        &KeyboardFactory{},
+    },
+}
+
+c.Make(os.Stdout)
+```
+
+  工厂方法是实现代码去耦合的非常精妙的一种方法，希望读者可以仔细品味并加以掌握。如果下次你的项目中需要在很多地方初始化相同的对象，你可以抛弃传统的
+new方法而转向使用工厂模式来新建对象。

From 0c88791ebe500aed3d2d4e2fd6157a2fe271efb2 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sat, 28 Apr 2018 13:49:25 +0800
Subject: [PATCH 26/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 1c1b86d..1cd3881 100644
--- a/index.md
+++ b/index.md
@@ -1,4 +1,5 @@
 - [] Golang 设计模式
+- [x] 工厂模式 [FactoryPattern](FactoryPattern.md)
 - [x] PKCS7 Padding [PKCS7_Padding](PKCS7_Padding.md)
 - [] PBFT 共识算法
 - [] Paxos & Raft

From 705c919e976d3078659779b32a39b0c3497c63b5 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sat, 28 Apr 2018 14:06:10 +0800
Subject: [PATCH 27/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 1cd3881..f5b32d5 100644
--- a/index.md
+++ b/index.md
@@ -15,5 +15,6 @@
 - [] TCP/IP
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
+- [] Docker & Golang 一对好基友
 
 

From 2db75dde638196ab7d4d81af461d447e0b182bcb Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sat, 28 Apr 2018 15:08:35 +0800
Subject: [PATCH 28/85] Update index.md

---
 index.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/index.md b/index.md
index f5b32d5..48813ed 100644
--- a/index.md
+++ b/index.md
@@ -16,5 +16,9 @@
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
 - [] Docker & Golang 一对好基友
+- [] Bloom Filter
+- [] Merkle Tree
+- [] goLevelDB
+- [] 零知识证明
 
 

From 1caa4356d581eb101e2df667b92219d3658e03ab Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sat, 28 Apr 2018 21:27:14 +0800
Subject: [PATCH 29/85] Create MerkleTree.md

---
 MerkleTree.md | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 162 insertions(+)
 create mode 100644 MerkleTree.md

diff --git a/MerkleTree.md b/MerkleTree.md
new file mode 100644
index 0000000..44a2612
--- /dev/null
+++ b/MerkleTree.md
@@ -0,0 +1,162 @@
+# Merkle Tree
+  `Merkle Tree` 在各个领域都有着广泛的应用，就拿我们熟悉的 `bittorrent` 来举例，在我们使用 `bittorrent` 下载的时候，客户端会向许多并不信任的
+节点去下载目标文件的某个片段，利用 `Merkle proofs` 我们就可以验证我们下载的部分片段是否属于整个文件的一部分。`Merkle proofs` 可以用于验证某个
+数据子集是否从属于更大的集合。
+
+  那么下面我们来看一下如何实现一个 `Merkle Tree` 吧。与传统的二叉树类似，每个 `Merkle Tree` 中由许多节点组成，每个节点都指向它的左孩子节点，右
+孩子节点和父节点，同时，还有一个flag指明它是否为叶子结点等其他元信息。`Content` 是每个节点所存储的内容，只要实现了该接口 `Content` 的结构体都能
+被节点所存储。
+
+## 数据结构
+
+```golang
+type Node struct {
+    Parent *Node
+    Left   *Node
+    Right  *Node
+    leaf   bool
+    dup    bool
+    Hash   []byte
+    C      Content
+}
+
+type Content interface {
+    CalculateHash() []byte
+    Equals(other Content) bool
+}
+
+type MerkleTree struct {
+    Root       *Node
+    merkleRoot []byte
+    Leafs      []*Node
+}
+```
+
+  在 `Merkle Tree` 当中，有一个非常重要的特性就是获取节点的哈希值，我们来定义一个helper function 用于计算节点的哈希值。计算哈希分为两种情况，
+第一种就是如果当前被计算的节点为叶子结点，那么我们调用interface `Content` 中的 `CalculateHash` 方法来计算所存储内容的哈希值，如果不是叶子结点，
+则说明是中间层的节点，那么就计算左子节点和右子节点的哈希值的拼接值的哈希值，即 `H(P) = H( H(P.L) + H(P.R) )`。
+
+```golang
+func (n *Node) calculateNodeHash() []byte {
+    if n.leaf {
+        return n.C.CalculateHash()
+    }
+    h := sha256.New()
+    h.Write(append(n.Left.Hash, n.Right.Hash...))
+    return h.Sum(nil)
+}
+```
+
+## 新建一棵树
+
+```golang
+func NewTree(cs []Content) (*MerkleTree, error) {
+    root, leafs, err := buildWithContent(cs)
+    if err != nil {
+        return nil, err
+    }
+    mk := &MerkleTree{
+        Root: root,
+        merkleRoot: root.Hash,
+        Leafs: leafs
+    } 
+    return mk, nil
+}
+```
+
+
+```golang
+func buildWithContent(cs []Content) (*Node, []*Node, error) {
+    if len(cs) == 0 {
+        return nil, nil, errors.New("Error: cannot construct tree with no content")
+    }    
+    var leafs []*Node
+    for _, c := range cs {
+        leafs = append(leafs, &Node{
+            Hash: c.CalculateHash(),
+            C: c,
+            leaf: true,
+        })
+    }
+    if len(cs) % 2 == 1 {
+        duplicate := &Node{
+            Hash: leafs[len(leafs)-1].Hash,
+            C: leafs[len(leafs)-1].C,
+            leaf: true,
+            dup: true,
+        }
+        leafs = append(leafs, duplicate)
+    }
+    root := buildIntermediate(leafs)
+    return root, leafs, nil
+}
+```
+
+  `buildIntermediate` 函数利用递归的方法建立树，递归在树的建立中是一种非常常用的套路，只需要特别关注一下它的退出条件就可以了，这里的退出条件就是
+最后仅剩两个节点了，说明已经可以返回根节点，那么函数执行到这里就可以返回了。
+
+```golang
+func buildIntermediate(nl []*Node) *Node {
+    var nodes []*Node
+    for i := 0; i < len(nl); i += 2 {
+        h := sha256.New()
+        var l, r int = i, i+1
+        if r == len(nl) {
+            r = len(nl)-1
+        }
+        h.Write(nl[l].Hash, nl[r].Hash...)
+        n := &Node{
+            Left:  nl[l],
+            Right: nl[r],
+            Hash:  h.Sum(nil),
+        }
+        nodes = append(nodes, n)
+        nl[l].Parent = n
+        nl[r].Parent = n
+        if len(nl) == 2 {
+            return n
+        }
+    }
+    return buildIntermediate(nodes)
+}
+```
+
+  只有当叶子结点的个数为 `2^n` 的时候，才能保证每一层的中节点都能两两配对，但一般来说叶子结点的个数不一定恰好为 `2^n` ，在这种情况下，就可以将每一
+层的最右边的结点进行自我复制，自己与自己配对然后再计算哈希值，上面的算法就采用了这样的解决方法。
+
+```
+     ┌───┴──┐       ┌────┴───┐         ┌─────┴─────┐          ┌─────┴─────┐
+  ┌──┴──┐   │    ┌──┴──┐     │      ┌──┴──┐     ┌──┴──┐    ┌──┴──┐     ┌──┴──┐
+┌─┴─┐ ┌─┴─┐ │  ┌─┴─┐ ┌─┴─┐ ┌─┴─┐  ┌─┴─┐ ┌─┴─┐ ┌─┴─┐   │  ┌─┴─┐ ┌─┴─┐ ┌─┴─┐ ┌─┴─┐ 
+   (5-leaf)         (6-leaf)             (7-leaf)                (8-leaf)
+```
+
+## 验证内容是否存在在 Merkle Tree 中
+
+  那么，我们又如何验证某个部分的内容是否真正存在于我们的 `MerkleTree` 中呢。具体的思路非常简单，就是遍历该树的叶子结点，看有没有与我们要验证的内容
+相一致的，如果没有则直接返回false，如果有则开始真正的验证阶段，验证的基本思路也不难，就是一个从叶子节点往根节点走并且验证这条路上的哈希值是否都正确的
+过程，先获取当前遍历的节点的父节点，然后判断父节点的左子节点和右子节点的哈希值是否相匹配，如果不匹配，则直接返回false，如果匹配，则继续验证知道根节点
+为止。
+
+```golang
+func (m *MerkleTree) VerifyContent(expectedMerkleRoot []byte, content Content) bool {
+    for _, l := range m.Leafs {
+        if l.C.Content.Equals(content) {
+            currentParent := l.Parent
+            for currentParent != nil {
+                h := sha256.New()
+               h.Write(append(currentParent.Left.calculateNodeHash(), currentParent.Right.calculateNodeHash()...))  
+                if bytes.Compare(h.Sum(nil), currentParent.Hash) != 0 {
+                    return false
+                } 
+                currentParent = currentParent.Parent
+            }
+            return true
+        }
+    }
+    return false
+}
+```
+
+
+

From d62da409e4630739d4e868e3ed62635dbb53a550 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sat, 28 Apr 2018 21:27:54 +0800
Subject: [PATCH 30/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 48813ed..e46bf1e 100644
--- a/index.md
+++ b/index.md
@@ -17,7 +17,7 @@
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
 - [] Docker & Golang 一对好基友
 - [] Bloom Filter
-- [] Merkle Tree
+- [] Merkle Tree [MerkleTree](MerkleTree.md)
 - [] goLevelDB
 - [] 零知识证明
 

From e0d2510474a670fa0c8fe3af56cfe2e66c3b9be9 Mon Sep 17 00:00:00 2001
From: yiyangyi <oceanoverflow@gmail.com>
Date: Sat, 28 Apr 2018 22:16:15 +0800
Subject: [PATCH 31/85] Add ZeroKnowledgeProof.md

---
 ZeroKnowledgeProof.md | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 ZeroKnowledgeProof.md

diff --git a/ZeroKnowledgeProof.md b/ZeroKnowledgeProof.md
new file mode 100644
index 0000000..e69de29

From 628f4189b62925177d736f7d1fc1f0d15178aed8 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sun, 29 Apr 2018 15:29:56 +0800
Subject: [PATCH 32/85] Create BloomFilter.md

---
 BloomFilter.md | 106 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 106 insertions(+)
 create mode 100644 BloomFilter.md

diff --git a/BloomFilter.md b/BloomFilter.md
new file mode 100644
index 0000000..03734e6
--- /dev/null
+++ b/BloomFilter.md
@@ -0,0 +1,106 @@
+# Bloom Filter
+  当我们写爬虫应用的时候，有一件事情是值得我们仔细考虑的，在抓取网上数据的时候，我们的爬虫会沿着当前爬取的网页中的链接继续进行深度优先搜索或者广度优先
+搜索，但是很显然，这些链接形成的拓扑图肯定不是一个有向无环图，所以肯定会重复抓取某些网页，这肯定不是我们所想要的，为了避免重复抓取导致的返工，我们不加
+思索的增加了一个 `map` 来存储当前已经被抓取过的url，这在要求抓取的url数量较少的时候或许是一个不错的解决方案，但是一旦我们要抓取亿级别的url数量时
+（在现在这种自媒体爆发的时代还是很正常的一件事情），假设我们要抓取的url平均大小是20字节，那么不考虑其他开销，1亿个url需要消耗的内存就是20GB，这显然
+是不现实的。我们需要一种更加优化的解决方案。
+
+  上述的问题可以抽象为，给定一个元素E，问它是否存在于给定的集合S中，上述使用 `map` 的存储导致空间开销过大的主要原因就是我们需要存储原来的url，那有
+没有一种方法不存储每条消息而可以知晓它是否存在于特定的集合中呢，答案就是 `Bloom Filter`。
+
+  `Bloom Filter` 是一个空间和时间都非常高效的数据结构，它解决的问题也非常简单，就是用于查询特定元素是否存在于某个集合当中，但是这种高效是引入错误率
+的基础上的高效，下面我们引入 `BloomFilter` 的两个特性。
+
+* False Positive 
+
+  `Bloom Filter` 可能会出现 `False Positive` 的概率，就是指查询特定元素E是否存在于集合E中，真实情况是E不存在，但是 `Bloom Filter` 却出现了
+误判，当 `Bloom Filter` 存储的元素越多，出现误判的概率也就会越大（一般来说会把误判率控制在可以接受的范围内），这是由 `Bloom Filter` 本身的特性
+造成的。
+* False Negative
+
+  `False Negative` 指的是元素E本来存在于集合S中，但是 `Bloom Filter`却出现误判，认为元素E并不存在于集合S中，这种情况是绝对不会出现的，这也是由
+`Bloom Filter` 本身的特性决定的。
+
+  `Bloom Filter`实现的思路非常简单，它主要由两个元素组成。
+1. 一张位图，决定了 `BloomFilter` 在空间上是高效的。
+2. 多个哈希函数，决定了 `BloomFilter` 是查询时间上是高效的，理论上的时间复杂度是 `O(1)`。
+
+  下面这张图就大概描述了 `Bloom Filter` 实现的大致思路，我们来简单过一遍。
+
+  假设当前由集合 `S: {x, y, z}` ，我们需要查询元素w是否存在于集合S中，第一步应该是位图的构建，位图开始是是一张全部为0的数组，当第一个元素x进入时，
+程序会通过 `Bloom Filter` 的所有哈希函数计算出哈希函数对应的哈希值，并将哈希值对应位图的相应位置都设置为1，y和z同理，也就是说此时位图的某些地方已经
+被设置为1了，但是其余地方都是0。此时我们需要查询元素w是否存在于集合S中，先利用 `Bloom Filter` 的所有哈希函数计算出相应的哈希值，并利用哈希值查询对
+应的位图中元素是否都为1，如果都为1，那么则说明元素w可能存在于该集合中，但是只要有一个位置为0，就可以直接判定该元素不存在于该集合中。
+
+![alt text](https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Bloom_filter.svg/649px-Bloom_filter.svg.png)
+
+  下面是 `Bloom Filter` 的简单实现，`bitmap` 就是位图，`k` 是哈希函数的个数，`n` 是当前存储的元素的个数，当然这里存储的意思并不是把整个元素存储下
+来，而是在位图中进行标记的意思，`m` 是整个 `Bloom Filter` 或者说位图的大小。
+
+```golang
+type BloomFilter struct {
+    bitmap []bool
+    k      int
+    n      int
+    m      int
+    hashfn hash.Hash64
+}
+
+func NewBloomFilter(numHashFuncs, bfSize int) *BloomFilter {
+    bf := new(BloomFilter)
+    bf.bitmap = make([]bool, bfSize)
+    bf.k, bf.m = numHashFuncs, bfSize
+    bf.n = 0
+    bf.hashfn = fnv.New64()
+}
+```
+
+  `getHash` 就是利用 `Bloom Filter` 中事先定义的哈希函数计算对应的多个哈希值，这里可以挑选抗碰撞性较好的哈希函数来提高我们程序的鲁棒性。
+
+```golang
+func (bf *BloomFilter) getHash(b []byte) (uint32, uint32) {
+    bf.hashfn.Reset()
+    bf.hashfn.Write(b)
+    hash64 := bf.hashfn.Sum64()
+    h1 := uint32(hash64 & ((1 << 32) - 1))
+    h2 := uint32(hash64 >> 32)
+    return h1, h2
+}
+```
+
+  `Add` 将元素 e 加入 `Bloom Filter` 中。
+
+```golang
+func (bf *BloomFilter) Add(e []byte) {
+    h1, h2 := bf.getHash(e)
+    for i := 0; i < bf.k; i++ {
+        ind := (h1 + uint32(i)*h2) % uint32(bf.m)
+        bf.bitmap[ind] = true
+    }
+    bf.n++
+}
+```
+
+  `Check` 检查特定元素 e 是否存在于 `Bloom Filter` 中。
+
+```golang
+func (bf *BloomFilter) Check(e []byte) bool {
+    h1, h2 := bf.getHash(e)
+    result := true
+    for i := 0; i < bf.k; i++ {
+        ind := (h1 + uint32(i)*h2) % uint32(bf.m)
+        result = result & bf.bitmap[ind]
+    }
+    return result
+}
+```
+
+  函数 `FalsePositiveRate()` 可以计算 `Bloom Filter` 出现 `False Positive` 的概率，这个公式有严格的数学证明，有兴趣的同学可以参考一下它的
+证明过程，我们现在只需要掌握一点就可以了，就是它存储的元素个数（n）越多，它出现 `False Positive` 的概率也就越大。但只要调整合适的`m` 和 `k` 的
+值，就可以把错误率控制在可以接受的范围内。
+
+```golang
+func (bf *BloomFilter) FalsePositiveRate() float64 {
+    return math.Pow((1 - math.Exp(-float64(bf.k*bf.n) / float64(bf.m))), float64(bf.k))
+}
+```

From 0caf1bd5f01c78271923caa692ed7afd1978ac55 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sun, 29 Apr 2018 15:30:31 +0800
Subject: [PATCH 33/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index e46bf1e..ba6d9d2 100644
--- a/index.md
+++ b/index.md
@@ -16,7 +16,7 @@
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
 - [] Docker & Golang 一对好基友
-- [] Bloom Filter
+- [] Bloom Filter [BloomFilter](BloomFilter.md)
 - [] Merkle Tree [MerkleTree](MerkleTree.md)
 - [] goLevelDB
 - [] 零知识证明

From 530f596745a55c315dd88b43d73b54fe2b3f54de Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sun, 29 Apr 2018 15:34:49 +0800
Subject: [PATCH 34/85] Update index.md

---
 index.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/index.md b/index.md
index ba6d9d2..7d37497 100644
--- a/index.md
+++ b/index.md
@@ -19,6 +19,8 @@
 - [] Bloom Filter [BloomFilter](BloomFilter.md)
 - [] Merkle Tree [MerkleTree](MerkleTree.md)
 - [] goLevelDB
-- [] 零知识证明
+- [] 零知识证明ZKP
+- [] 工作量证明POW
+- [] Skip List
 
 

From d7eaf564f13963eabfb6bd338821fb1bc0622f28 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sun, 29 Apr 2018 15:36:15 +0800
Subject: [PATCH 35/85] Fix Bug

---
 index.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/index.md b/index.md
index 7d37497..8485ff5 100644
--- a/index.md
+++ b/index.md
@@ -10,14 +10,14 @@
 - [] p2p Discoverer
 - [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
 - [] 使用gRPC来进行节点之间的通信
-- [] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
+- [x] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
 - [] 密码学基础
 - [] TCP/IP
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
 - [] Docker & Golang 一对好基友
-- [] Bloom Filter [BloomFilter](BloomFilter.md)
-- [] Merkle Tree [MerkleTree](MerkleTree.md)
+- [x] Bloom Filter [BloomFilter](BloomFilter.md)
+- [x] Merkle Tree [MerkleTree](MerkleTree.md)
 - [] goLevelDB
 - [] 零知识证明ZKP
 - [] 工作量证明POW

From b56df10dde538b20a43adbe81fe55f4ca231f961 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sun, 29 Apr 2018 23:20:44 +0800
Subject: [PATCH 36/85] Create TCP_IP.md

---
 TCP_IP.md | 138 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)
 create mode 100644 TCP_IP.md

diff --git a/TCP_IP.md b/TCP_IP.md
new file mode 100644
index 0000000..c03c5d6
--- /dev/null
+++ b/TCP_IP.md
@@ -0,0 +1,138 @@
+# TCP/IP
+  网络是分布式系统之间通信的媒介，掌握网络编程自然变成了程序猿们居家旅行必备技能之一了，与直接使用应用层的 `http` 协议不同，我们一般使用传输层的协议
+例如 `tcp` 进行编程，掌握好了 `tcp` 编程，我们甚至能写一套类似`http` 这样的应用层协议，是不是很兴奋呢，那今天就让我们从最简单的例子出发，掌握 
+`tcp/ip` 编程的精髓吧。
+
+  一般来说大部分网络通信模型是 `c/s` 模型，也有部分是 `p2p` 模型，今天我们先了解一下 `c/s` 模型的编程使用吧。
+
+  客户端要想与服务器通信，前提必须是服务器已经启动并且监听了某个端口了。如果服务器没有监听而客户端直接连接的话，就会报类似下面这样的错误：
+
+  `dial tcp 127.0.0.1:8080: connect: connection refused`
+ 
+  也就是说，服务器端要调用 `net.Listen` 接口对特定端口进行监听，监听会返回一个 `Listener` ，服务器一旦监听到来自客户端的请求，就会调用 `Accept`
+来接受该请求，`Accept` 会返回一个 `net.Conn` ，以后服务器就可以使用 `net.Conn` 与客户端进行通信。
+
+```golang
+type Listener interface {
+    Accept() (Conn, error)
+    Close() error
+    Addr() Addr
+}
+
+type Conn interface {
+    Read(b []byte) (n int, err error)
+    Write(b []byte) (n int, err error)
+    Close() error
+    LocalAddr() Addr
+    RemoteAddr() Addr
+    SetDeadline(t time.Time) error
+    SetReadDeadline(t time.Time) error
+    SetWriteDeadline(t time.Time) error
+}
+```
+
+## server.go
+
+  一般来说，服务器需要处理来自多个客户端的请求，所以我们使用goroutine来处理不同客户端的请求，这样就达到一个并发的目的。
+
+  服务器处理客户端请求的主要的流程如下：
+  `Listen => Accept => Read & Write => Close` 。
+
+```golang
+package main 
+
+import (
+    "log"
+    "net"
+    "fmt"
+    "bufio"
+    "strings"
+)
+
+func main() {
+    fmt.Println("Launching server...")
+
+    l, err := net.Listen("tcp", ":8080")
+    if err != nil {
+        log.Fatal(err)
+    }
+    defer l.Close()
+
+    for {
+        conn, err := ln.Accept()
+        if err != nil {
+            log.Fatal(err)
+        }
+        go handleConn(conn)
+    }
+}
+
+func handleConn(c net.Conn) {
+    for {
+        m, _ := bufio.NewReader(c).ReadString('\n')
+        fmt.Print("Message Received:", string(m))
+        newMsg := strings.ToUpper(m)
+        c.Write([]byte(newMsg + "\n"))
+    } 
+}
+```
+
+## client.go
+
+  一般来说连接都是由客户端主动发起的，客户端调用 `net.Dial` 向服务器发起连接行为，在正常情况下，则也返回一个 `net.Conn` ，注意到客户端和服务器都
+是通过 `net.Conn` 进行通信的，linux基础好的同学，就知道它的本质就是一个套接字（socket），而套接字的本质则是一个文件，所以我们可以对待正常文件一样
+去使用它，例如使用 `fmt.Fprintf` 来对套接字写入数据。
+
+  主要的流程如下： 
+  `Dial => Read & Write => Close` 。
+
+```golang
+package main
+
+import (
+    "os"
+    "log"
+    "net"
+    "fmt"
+    "bufio"
+)
+
+func main() {
+    c, e := net.Dial("tcp", "localhost:8080")
+    if e != nil {
+        log.Fatal(e)
+    }
+    reader := bufio.NewReader(os.Stdin)
+    for {
+        fmt.Print("Text to send: ")
+        text, err := reader.ReadString('\n')
+        if err != nil {
+            log.Fatal(err)
+        }
+        fmt.Fprintf(c, text + "\n")
+        m, e := bufio.NewReader(c).ReadString('\n')
+        if e != nil {
+            log.Fatal(e)
+        }
+        fmt.Print("Message from server: " + m)
+    }
+}
+```
+
+## 运行
+  先对两个源文件进行编译。
+```
+$ go build *.go
+```
+
+  在一个命令行中输入：
+
+```
+$ ./server
+```
+
+  在另一个命令行中输入：
+
+```
+$ ./client
+```

From 2c6b3e37db2e7a430fbf5b77cd79c9e38caeedbe Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Sun, 29 Apr 2018 23:21:36 +0800
Subject: [PATCH 37/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 8485ff5..5fc7e69 100644
--- a/index.md
+++ b/index.md
@@ -12,7 +12,7 @@
 - [] 使用gRPC来进行节点之间的通信
 - [x] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
 - [] 密码学基础
-- [] TCP/IP
+- [x] TCP/IP [TCP/IP](TCP_IP.md)
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
 - [] Docker & Golang 一对好基友

From 543ab8ec93a9cf3040fd7c20e1cbefd9b5cea950 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Mon, 30 Apr 2018 00:01:10 +0800
Subject: [PATCH 38/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 5fc7e69..1f27768 100644
--- a/index.md
+++ b/index.md
@@ -22,5 +22,6 @@
 - [] 零知识证明ZKP
 - [] 工作量证明POW
 - [] Skip List
+- [] 分布式哈希表DHT
 
 

From 4485a829f38fc50388d225c1008f0d58c757a309 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Mon, 30 Apr 2018 15:03:16 +0800
Subject: [PATCH 39/85] Create Crontab.md

---
 Crontab.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Crontab.md

diff --git a/Crontab.md b/Crontab.md
new file mode 100644
index 0000000..9b6877d
--- /dev/null
+++ b/Crontab.md
@@ -0,0 +1,60 @@
+# crontab
+  事情的经过是这样子的，室友最近要做一个阿里的项目，大概就是利用软件工程结合机器学习对代码缺陷进行预测分析，现在正处于数据分析阶段，每天都会到github上
+拉一大堆项目，然后用写好的代码啪啪啪分析一通，一般来说代码都要跑很久，一般都是12小时左右才能出结果，而且等它跑完还要继续跑剩下一个项目，但是作为程序员，
+这样的体力活自然应该自动化处理啦，毕竟越厉害的程序员是越懒的。
+
+  所以，这种周期性，性质类似，但是需要大量重复劳动的工作可以写成自动化脚本再结合 `crontab` 来解决。`crontab` 就是为了周期性定时执行任务而生的，再也
+不用盯着电脑屏幕等结果跑完啦，感觉是现在搞AI的人的必备良药呢。
+ 
+  `crontab` 的语法还是挺简单的，但是咋一看还是一脸懵逼的，其实关于`crontab` 只要掌握一点就可以使用它了，就是关于时间语法的定义，因为 `crontab` 就是为了定时执行任务而存在的，所以它有自己的一套语法。
+
+```
+$ * * * * * command_to_execute
+  | | | | |
+  | | | | +- day of week (0 - 7) where sunday is 0 and 7
+  | | | +--- month (1 - 12)
+  | | +----- day (1 - 31)
+  | +------- hour (0 - 23)
+  +--------- minute (0 - 59)
+```
+
+  我们要让某个任务在某个特定的时间点执行，时间无非就是哪个月的哪一天的几点几分或者是星期几该执行这项任务，在 `crontab` 的语法中我们先指定分钟，再次小
+时，接着一个月中的某一天，然后月份，最后星期几。具体的顺序如下：`(min)  (hour)  (day_of_month)  (month)  (day_of_week)`。
+
+## 例子
+  不过上面讲的还是太抽象了，我们来具体举几个例子来掌握crontab的使用吧。
+
+  例如我们已经写好了一个脚本，`make_coffee.sh` ，因为程序员都有午后喝咖啡的习惯，但是咖啡机每次启动都要预热好久，为了让自己少浪费一点时间，就写了一个
+脚本让咖啡机在每天下午两点自己开启进行预热，预热完成，我们就可以直接过去接咖啡了。所以，我们要让 `crontab` 每天下午执行这个脚本，我们应该怎么写呢，按照
+上面的语法，不是很难。
+
+`$ 0 14 * * * /path/to/make_coffee.sh`
+
+  也就是说 `crontab` 每天下午两点会启动咖啡机预热，不过现在有个问题，我们周末并不在公司，只有工作日周一到周五在公司，所以，我们上面的语法还是有点问题
+的，应该让 `crontab` 指定在工作日才运行。
+
+`$ 0 14 * * 1-5 /path/to/make_coffee.sh`
+
+  只要把第五个 `*` 换成 `1-5` 就可以啦，非常简单。
+
+  但是每天喝咖啡可能对身体不太好，那我们改成星期一星期三星期五喝咖啡，其他两天改喝牛奶，那我们应该怎么办呢，将1-5改成 1,3,5 就可以了。
+
+`$ 0 14 * * 1,3,5 /path/to/make_coffee.sh`
+
+  `crontab` 的语法还是有很多的，这里就不一一例举了，我们有一个大致的思路，有了大致的思路，具体问题再去看文档就ok啦。
+
+## crontab管理
+  其实直接在命令行中直接数据 `crontab` 命令并不是一个非常好的习惯，我们最好将这些命令存放在文件中以便持久化的目的，也方便我们进行集中管理，很幸运的是
+，`crontab` 已经为我们提供了这些服务啦。
+
+`$ crontab -e`
+
+  `-e` 可以让我们编辑crontab文件，可以在该文件中定义多个 `crontab` 任务。
+
+`$ crontab -l`
+
+  `-l` 帮助我们查看当前用户定义的所有的 `cron jobs`。
+
+`$ crontab -r`
+
+  `-r` 则会清除当前用户所有的 `cron jobs`。

From 04795eccbc3912715816aad6b727b8ad983fa5e9 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Mon, 30 Apr 2018 15:04:23 +0800
Subject: [PATCH 40/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 1f27768..a13d1cb 100644
--- a/index.md
+++ b/index.md
@@ -23,5 +23,6 @@
 - [] 工作量证明POW
 - [] Skip List
 - [] 分布式哈希表DHT
+- [x] 让任务定时执行 [crontab](Crontab.md)
 
 

From 82b2e3b00c6de7ea9c159ff807cdc66f96cb062e Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Mon, 30 Apr 2018 23:40:25 +0800
Subject: [PATCH 41/85] Create ConsistentHashing.md

---
 ConsistentHashing.md | 134 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 134 insertions(+)
 create mode 100644 ConsistentHashing.md

diff --git a/ConsistentHashing.md b/ConsistentHashing.md
new file mode 100644
index 0000000..190e740
--- /dev/null
+++ b/ConsistentHashing.md
@@ -0,0 +1,134 @@
+# Consistent Hashing
+  
+  比如说我们现在有3台服务器用于存储kv值，分别是 `{s1, s2, s3}` ，而我们用的哈希策略非常简单，例如 `h(x) = ( x^2 + 3 * x + 2 ) % 3` ，也就是
+说当k=1时，对应的v值会被存放在s1上，但如果业务量增加，我们又需要增加一个服务器来均衡负载，此时哈希策略就变成 `h(x) = ( x^2 + 3 * x + 2 ) % 4`
+，那按照这种策略，当k=1的时候，应该是把对应的v值存放在s3上面，然而s3上面并没有存放，为了能够获取到k=1对应的v值，我们就需要将原来三台服务器上的数据
+做一个 `remapping` ，也就是说基本上所有的数据都要进行迁移，在小数据量的情况下这倒没什么问题，但是一旦数据量达到T级别，这种开销将是无法忍受的，所以，
+为了当增删节点时尽量少的数据迁移，我们可以利用一致性哈希这种非常神奇的算法。
+
+  所谓一致性哈希，就是当相应的哈希表调整大小的时候（对应到现实生活中就是服务器增删节点），只有 `K/n` 个数据需要迁移，这里的 `K` 是指键的数量，而 `n`
+则是指所有的卡槽（对应服务器的数量），如果不用一致性哈希而是用传统的哈希表，当哈希表调整的时候，基本所有的键都要进行 `remapping`。
+
+  一致性哈希有几个要点，第一个就是它所有的地址空间会形成一个环，例如它的地址空间是从 `[0, 2^32-1)` ，它的头部和尾部会进行拼接而形成一个环，还有就
+是虚节点的概念，例如下图中我们有`{s1, s2, s3, s4}`这四个节点，但每个节点都会有自己的拷贝（称为虚节点），然后这些复制过的节点群会根据特定的哈希算法
+分布在这个环上面，一般来说都是均匀分布的，而我们要存储的键值对，同样根据与节点相同的哈希算法，算出一个值后，顺时针（或者逆时针）去寻找与自己最近的虚节
+点的位置，找到最近的虚节点后，就将自己存放在对应的节点上，这就是实现一致性哈希的基本思路，下面就让我们来看一下一致性哈希算法的实现吧。
+
+![alt text](https://dajh2p2mfq4ra.cloudfront.net/assets/site-images/system_design/consistent_hashing_virtual.jpg)
+
+## 一致性哈希的实现
+
+  上面我们知道了一致性哈希的核心是一个环，环的地址空间假设是 `[0, 2^32-1)` ，我们可以用一个map记录虚节点对应的环上的位置，位置我们用一个uint32表
+示就可以了，因为是遍历map的时候是随机的，所以我们还需要一个数组来记录环上面虚节点的顺序。
+
+```golang
+type uints []uint32
+
+func (x uints) Len() int { return len(x) }
+func (x uints) Less(i, j int) bool { return x[i] < x[j] }
+func (x uints) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+
+type Consistent struct {
+    circle map[uint32]string
+    sortedList uints
+    virtualNode int
+    sync.Mutex
+}
+
+func New() *Consistent {
+    return &Consistent{
+        circle: make(map[uint32]string),
+        sortedList: make(uints, 0)
+        virtualNode: 20,
+    }
+}
+```
+
+  虚节点相当于实节点的影分身，例如s2，s2复制为s2-1，s2-2，s2-3。这些原来的节点和复制出来的节点我们可以把它们形象地称为哨兵。
+
+```golang
+func (c *Consistent) KeyNum(key string, num int) string {
+    return key + ":" + fmt.Sprintf("%d", num)
+}
+```
+
+  要想把虚节点对应到一致性哈希的圆环上，我们还需要一个哈希函数用于把我们的哨兵和我们的键值对映射到环上面去。
+
+```golang
+func (c *Consistent) hashKey(key string uint32) {
+    if len(key) < 64 {
+        scratch := make([]byte, 64)
+        copy(scratch[:], key)
+        return crc32.ChecksumIEEE(scratch[:len(key)])
+    }
+    return crc32.ChecksumIEEE([]byte(key))
+}
+```
+
+  之前说了，我们加入的键值对经过哈希后会在环上顺时针寻找离自己最近的虚节点的位置，因为我们之前已经用 `sortedList` 记录了节点在环上的相对顺序了，
+所以就可以利用二分法去查找对应虚节点的位置啦。
+
+```golang
+func (c *Consistent) search(key uint32) int {
+    i := sort.Search(len(c.sortedList), func(x int) bool {
+        return c.sortedList[x] > key
+    })
+    if i >= len(c.sortedList) {
+        i = 0
+    } 
+    return i
+}
+```
+
+  当然了，我们一致性哈希就牛逼的地方就在于当动态增删节点的时候，需要`remapping` 的键值对的数量比较少，动态增删节点无非也就是将该节点影分身后再经过
+哈希操作对应到环上或者进行相应的删除操作，当然还需要对我们的 `sortedList` 重新进行排序来对应增删的虚节点。
+
+```golang
+func (c *Consistent) Add(key string) {
+    c.Lock()
+    defer c.Unlock()
+
+    for n := 0; n < virtualNode; n++ {
+        c.circle[c.hashKey(c.KeyNum(key, n))] = key
+    }
+
+    c.updateList()
+}
+
+func (c *Consistent) Remove(key string) {
+    c.Lock()
+    defer c.Unlock()
+ 
+    for n := 0; n < c.virtualNode; n++ {
+        delete(c.circle, c.hashKey(c.KeyNum(key, n)))
+    }
+
+    c.updateList()
+}
+
+func (c *Consistent) updateList() {
+    list := c.sortedList[:0]
+    for k, _ := range c.circle {
+        list = append(list, k)
+    } 
+    sort.Sort(list)
+    c.sortedList = list
+}
+```
+
+  最后就是当我们新增一个键值对时，我们可以查找对应的节点。
+
+```golang
+func (c *Consistent) Get(name string) (string, error) {
+    c.Lock()
+    defer c.Unlock()
+
+    if len(c.circle) == 0 {
+        return 0, errors.New("empty consistent circle")
+    }
+    key := c.hashKey(name)
+    i := c.search(key)
+    return c.circle[c.sortedList[i]], nil
+}
+```
+

From cbabee5ec2b08e246a3e57a701c5f7b497b85e3c Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Mon, 30 Apr 2018 23:41:38 +0800
Subject: [PATCH 42/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index a13d1cb..84f78d8 100644
--- a/index.md
+++ b/index.md
@@ -24,5 +24,6 @@
 - [] Skip List
 - [] 分布式哈希表DHT
 - [x] 让任务定时执行 [crontab](Crontab.md)
+- [x] 一致性哈希 [ConsistentHashing](ConsistentHashing.md)
 
 

From 5f66b61b3eb08c6c4a14322affad2f1e9feee8a1 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Tue, 1 May 2018 15:49:03 +0800
Subject: [PATCH 43/85] Create etcd.md

---
 etcd.md | 221 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 221 insertions(+)
 create mode 100644 etcd.md

diff --git a/etcd.md b/etcd.md
new file mode 100644
index 0000000..fbb1b35
--- /dev/null
+++ b/etcd.md
@@ -0,0 +1,221 @@
+# 使用 etcd 来提供服务注册与发现功能
+ 
+  `etcd` 是一个用于存储分布式系统中重要数据结构的存储引擎，而其本身就是一个分布式的键值数据库，例如在微服务架构中的服务注册和发现机制，就可以利用
+`etcd` 作为我们的注册中心进行服务，用于存储服务注册信息。
+
+  但是，什么又是服务注册和发现呢，其实这和现在盛行的微服务架构有着密切的联系，在以前那种非微服务时代，应用运行在物理机上面，服务实例对应的网络地址也是
+相对固定不变的，而到了微服务时代，很多服务实例的网络地址则是动态分配的，而且服务实例也经常面临着失败或者更新的情况，所以这些服务实例对应的网络地址也经
+常会变化，这对应用的开发者和使用者都是非常不利的，所以引入服务注册和发现这种机制，来适应微服务的这种问题。
+
+![alt text](https://code.aliyun.com/middlewarerace2018/docs/raw/master/assets/dubbo-architecture.png)
+
+  我们以 `Dubbo` 中的服务注册与发现为例，深入理解一下到底什么是服务注册与发现，这里有几个角色需要注意一下，`Registry` 用于服务信息的登记和提供，
+`Provider` 是服务的提供商，它会将自己提供的服务信息注册到`Registry` 上，而 `Consumer` 则是相应服务的消费者，它会从 `Registry` 那里获取服务的
+信息（一般来说就是接口的地址），再调用 `Provider` 上提供的真实服务，`Monitor` 这里仅仅作为记录一些调用次数之类的元信息。注意到这里 `Registry` 
+通知 `Consumer` 服务信息是一个异步的过程，而 `Consumer` 调用 `Provider` 的服务则是同步的。
+
+  `etcd` 底层使用 `raft` 协议来维持集群中服务器间数据的一致性，这在之后的博客中会详细探讨。而 `etcd` 也提供了非常简单的客户端接口供我们使用，今天
+我们的目标就是写一个 `etcd` 的客户端供我们进行服务注册和发现等相关方面的使用。
+
+  `etcd` 无论作为什么用途，其本质都是一个kv存储引擎，而我们要实现的客户端必须提供以下接口的服务。例如向 `etcd` 写入键值对，获取相应键对应的值，还有
+获取相应键最新值的机制（通过watch机制）等等。
+
+```golang
+type Store interface {
+    Put(key string, value []byte, options *WriteOptions) error
+    Get(key string) (*KVPair, error)
+    Delete(key string) error
+    Exists(key string) (bool, error)
+    Watch(key string, stopCh <-chan struct{}) (<-chan *KVPair, error)
+    WatchTree(directory string, stopCh <-chan struct{}) (<-chan []*KVPair, error)
+    NewLock(key string, options *LockOptions) (Locker, error)
+    List(directory string) ([]*KVPair, error)
+    DeleteTree(directory string) error
+    AtomicPut(key string, value []byte, previous *KVPair, options *WriteOptions) (bool, *KVPair, error)
+    AtomicDelete(key string, previous *KVPair) (bool, error)
+    Close() 
+}
+
+type KVPair struct {
+    Key       string
+    Value     []byte
+    LastIndex uint64
+}
+
+type WriteOptions struct {
+    IsDir bool
+    TTL   time.Duration
+}
+
+type LockOptions struct {
+    Value     []byte
+    TTL       time.Duration
+    RenewLock chan struct{}
+}
+```
+
+  `etcd` 官方已经给我们提供了一个client接口了（ github.com/coreos/etcd/client），我们只要在文件头导入它就行了。
+
+`import etcd "github.com/coreos/etcd/client`
+
+  我们对该服务进行一层封装以便实现我们的 `Store` 接口。
+
+```golang
+type Etcd struct {
+    client etcd.KeysAPI
+}
+
+func New(addrs []string, options *Config) (Store, error) {
+    s := &Etcd{}
+
+    cfg := &etcd.Config{
+        Endpoints:               entries,
+        Transport:               etcd.DefaultTransport,
+        HeaderTimeoutPerRequest: 3 * time.Second,
+    }
+
+    // Set options
+    if options != nil {
+        if options.TLS != nil {
+            setTLS(cfg, options.TLS, addrs)
+        }
+        if options.ConnectionTimeout != 0 {
+            setTimeout(cfg, options.ConnectionTimeout)
+        }
+        if options.Username != "" {
+            setCredentials(cfg, options.Username, options.Password)
+        }
+    }
+    
+    c, err := etcd.New(*cfg)
+    if err != nil {
+        log.Fatal(err)
+    }
+
+    s.client = etcd.NewKeysAPI(c)
+
+    // Periodic Cluster Sync
+    go func() {
+        for {
+            if err := c.AutoSync(context.Background(), periodicSync); err != nil {
+                return
+            }
+        }
+    }()
+
+    return s, nil
+}
+```
+
+  当然，客户端与 `etcd` 服务器的通信一般都不是明文传输的，所以还需要定义用于传输加密相关的结构体。一般在通信过程中会使用TLS加密，所以安全性问题我们
+就可以不用担心了。
+
+```golang
+type Config struct {
+    ClientTLS         *ClientTLSConfig
+    TLS               *tls.Config
+    ConnectionTimeout time.Duration
+    Bucket            string
+    PersistConnection bool
+    UserName          string
+    Password          string
+}
+
+type ClientTLSConfig struct {
+    CertFile   string
+    KeyFile    string
+    CACertFile string
+}
+```
+
+  下面我们来重点看一下三个接口的实现，分别是 `Put` ，`Get` 和 `Watch` ，分别应对现实生活中，`Provider` 向 `etcd` 写入可以提供的服务信息，
+`Consumer` 在 `etcd` 中读取服务信息，还有就是在 `etcd` 上注册的服务信息改变时，`Consumer` 可以及时获得改变后的值。
+
+  `Get` 操作获取当前 `etcd` 已经注册的服务信息，也就是服务发现的过程。
+
+```golang
+func (s *Etcd) Get(key string) (pair *KVPair, err error) {
+    getOpts := &etcd.GetOptions{
+        Quorum: true,
+    }
+
+    result, err := s.client.Get(context.Background(), s.normalize(key), getOpts)
+
+    if err != nil {
+        return nil, err
+    }
+
+    pair = &KVPair{
+        Key:       key,
+        Value:     []byte(result.Node.Value)
+        LastIndex: result.Node.ModifieIndex,
+    }
+
+    return pair, nil
+}
+```
+
+  `Put` 操作可以向 `etcd` 注册相应的服务信息，简单的说就是服务注册。
+
+```golang
+func (s *Etcd) Put(key string, value []byte, opts *WriteOptions) error {
+    setOpts := &etcd.SetOptions{}
+
+    if opts != nil {
+        setOpts.Dir = opts.IsDir
+        setOpts.TTL = opts.TTL
+    }
+
+    _, err := s.client.Set(context.Background(), s.normalize(key), string(value), setOpts)
+    return err
+}
+```
+
+  当我们关心的某一项键值（对应服务）发生变化时候，我们需要及时获取最新的值以免我们的服务出现过期不可用的情况，例如我们我们提供服务的服务端的一个实例
+挂掉了，某些编排系统如 `k8s` 会自动重启这些实例，但是重启后的实例的服务地址可能会出现改变，我们使用这些服务的客户端肯定需要知道更新过后的值，通过
+`watch` 机制就可以及时获取更新后的键值对。
+
+```golang
+func (s *Etcd) Watch(key string, stopCh <-chan struct{}) (<-chan *KVPair, error) {
+    opts := &etcd.WatcherOptions{Recursive: false}
+    watcher := s.client.Watcher(s.normalize(key), opts)
+    watchCh := make(chan *KVPair)
+
+    go func() {
+        defer close(watchCh)
+
+        pair, err := s.Get(key)
+        if err != nil {
+            return
+        }
+
+        watchCh <- pair
+        for {
+            select {
+            case <-stopCh:
+                return
+            default:
+            }
+           
+            result, err := watcher.Next(context.Background())
+            if err != nil {
+                return
+            }
+
+            watchCh <- KVPair{
+                Key:       key,
+                Value:     []byte(result.Node.Value),
+                LastIndex: result.Node.ModifiedIndex,
+            }
+        }
+    }()
+
+    retrun watchCh, nil
+}
+```
+
+### 后记
+
+  主要是笔者最近要参加阿里的第四届中间件大赛，所以对服务注册和发现做了一些调研工作。在像阿里这种大公司就会提供各种各样的服务端口，而且由于分布式的特性
+，服务间通讯就变得尤为重要，通常服务提供者暴露服务，服务消费者调用服务，而它们之间的中介服务通常就由`etcd`，`consul` 或者 `zookeeper` 这样的高性
+能键值数据库来提供。

From 4f769417c3a8db5d6fc7c8fb68dd44d29ebd6de4 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Tue, 1 May 2018 15:50:28 +0800
Subject: [PATCH 44/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 84f78d8..68a54fc 100644
--- a/index.md
+++ b/index.md
@@ -25,5 +25,6 @@
 - [] 分布式哈希表DHT
 - [x] 让任务定时执行 [crontab](Crontab.md)
 - [x] 一致性哈希 [ConsistentHashing](ConsistentHashing.md)
+- [x] 使用 etcd 来进行服务注册与发现 [etcd](etcd.md)
 
 

From 47ca0f7fd9c3e426f84fa985a805b96da7876da2 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Wed, 2 May 2018 00:07:19 +0800
Subject: [PATCH 45/85] Create iobridge.md

---
 iobridge.md | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)
 create mode 100644 iobridge.md

diff --git a/iobridge.md b/iobridge.md
new file mode 100644
index 0000000..08ac346
--- /dev/null
+++ b/iobridge.md
@@ -0,0 +1,115 @@
+# iobridge
+
+  每个人小时候都或多或少经历过父母冷战吧，当他们冷战的时候，我们这些可怜的小屁孩就成了传话筒，一般事情就会发展成下面这种情况了：
+
+  「老爸 -> 我 ：“叫你老妈安静一点”   
+    然后
+   我 -> 老妈 ：“老妈，老爸叫你安静一点”」
+ 
+  「老妈 -> 我 ：“你老爸才需要安静呢”   
+    然后 
+   我 -> 老爸 ：“老爸，老妈说你才需要安静呢”」
+
+  上述对话如此往复，诶，可怜天下小屁孩，要是冷战的话，老爸老妈必须通过一个中介人——我来进行语言上的交流。
+
+  在现代网络编程中，也会出现与上述例子极其相似的情况，例如，现在有三台服务器，分别是A，B，C，其中A能与B直接通信，B能与C直接通信，但是A不能与C直接通
+信，在这种情况下，A要想能与C交流，就必须要经过B，下面我们就来看如何实现上面这种情况吧。
+
+  下面的 `iobridge` 函数就做了一件传话筒的工作，其原理非常简单，就是从我们的数据源 `src` 读取（调用 `Read` 方法）数据，然后将它写到我们的目的地
+（调用 `Write` 方法）。也就是相当于我们上述过程中我从老妈那里获取消息，然后再将这个消息发给老爸的过程，注意到，这里函数中数据流的方法是单向的。
+
+  因为在读数据的过程中经常需要开辟新内存，为了节约运行时的成本，我们通过之前写过的 `LeakyBuffer` 模型来获取内存。
+
+```golang
+func iobridge(src io.Reader, dst io.Writer, shutdown chan struct{}) {
+    defer func() {
+        shutdown <- struct{}
+    }()
+    
+    for {
+        buf := leakyBuffer.Get()
+        n, err := src.Read(buf)
+        if err != nil {
+            if !(err == io.EOF || UseClosedConn(err)) {
+                log.Print("error reading %s:", src, err)
+            }
+            break
+        }
+        _, err := dst.Write(buf[:n])
+        if err != nil {
+            log.Prinf("error writing %s: %s\n", dst, err)
+            break
+        }
+        leakyBuffer.Put(buf)
+    }
+}
+
+func UseClosedConn(err error) bool {
+    operr, ok := err.(*net.OpErr)
+    return ok && operr.Err.Error() == "use of closed network connection"
+}
+```
+
+  因为我们的 `net.Conn` 同时定义了 `Read(b []byte) (n int, err error)` 方法和 `Write(b []byte) (n int, err error)` 方法，而这两个方法
+分别由接口 `io.Reader` 和 `io.Writer` 定义，所以 `net.Conn` 既是 `io.Reader` 也是 `io.Writer` 。故上述方法中的第一个和第二个参数我们都可以
+使用 `net.Conn` 做我们的参数。
+
+```golang
+// defined in package io
+type Reader interface {
+    Read(p []byte) (n int, err error)
+}
+
+// defined in package io
+type Writer interface {
+    Write(p []byte) (n int, err error)
+}
+
+// defined in package net
+type Conn interface {
+    Read(b []byte) (n int, err error)
+    Write(b []byte) (n int, err error)
+    ...
+    ...
+}
+```
+
+  这里的main函数应该运行在中间那台服务器也就是B上面，B作为客户端向远程服务器C发起TCP连接，并返回一个套接字conn1，同时B也作为服务器监听端口，服务来
+自A的连接请求，`Accept` 成功返回另一个套接字conn2，那么如何使得来个无法直接接触的服务器A和C有来有往的进行交流对话呢，这里的技巧就是起两个
+goroutine分别调用 `iobridge` 方法，使得两个 `net.Conn` conn1和conn2相互传递数据以互通有无，`shutdown` 这个通道这里算一个hack，用于阻塞
+main routine 不让其马上返回。
+
+```golang
+func main() {
+    conn1, err := net.Dial("tcp", "remotehost")
+    if err != nil {
+        log.Println("Error Dialing")
+        return
+    }
+
+    l. err := net.Listen("tcp", ":2333")
+    conn2, err := listener.Accept()
+    if err != nil {
+        log.Println("Error Accepting Connection")
+        return
+    }
+
+    shutdown := make(chan struct{}, 2)
+
+    go iobridge(conn1, conn2, shutdown)
+    go iobridge(conn2, conn1, shutdown)
+
+    <-shutdown
+}
+```
+
+## 总结
+  设有服务器A，B，C，除了A与C不能直接接触，其余两两都能沟通，要想A能与C进行沟通交流，解决方案就是A作为客户端调用 `net.Dial` 向B发起TCP连接，如果
+连接成功，则A上会有一个 `net.Conn` sA，B上也有一个 `net.Conn` sB1，此时B也作为客户端向C发起TCP连接，B上又多了一个 `net.Conn` sB2，C上也有一
+个`net.Conn` sC。注意这些 `net.Conn` 的本质都是一个套接字，可以直接当成文件描述符来进行操作。
+
+  此时A和C要想互相交流，唯一的方法就是将sB1和sB2进行某种程度的联系，通过起两个 `iobridge` 函数，我们就将sB1读到的数据发送给sB2，也可sB2读到的数
+据发送给sB1，注意，一旦sB1将读到的数据发送给sB2，sB2会马上把这个数据发送给sC，这样就达到了A与C相互交流的目的了。
+
+  聪明的小伙伴一定注意到了，上面讲的就是代理（shadowsocks）的基本工作原理了，是不是很简单也很有趣呢。
+  

From 788631e230cffa24e0302512a6113a425455fee5 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Wed, 2 May 2018 00:08:19 +0800
Subject: [PATCH 46/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 68a54fc..616ef26 100644
--- a/index.md
+++ b/index.md
@@ -26,5 +26,5 @@
 - [x] 让任务定时执行 [crontab](Crontab.md)
 - [x] 一致性哈希 [ConsistentHashing](ConsistentHashing.md)
 - [x] 使用 etcd 来进行服务注册与发现 [etcd](etcd.md)
-
+- [x] iobridge [iobridge](iobridge.md)
 

From fbd42901079cd34a6d089f2e54b3083fed624e2c Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Wed, 2 May 2018 15:34:30 +0800
Subject: [PATCH 47/85] Create TLS.md

---
 TLS.md | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 169 insertions(+)
 create mode 100644 TLS.md

diff --git a/TLS.md b/TLS.md
new file mode 100644
index 0000000..fcb98cb
--- /dev/null
+++ b/TLS.md
@@ -0,0 +1,169 @@
+# TLS
+
+  在之前的博客中涉及到的 `TCP` 网络编程都是非常不安全的，因为它将我们的数据直接以明文的形式在公网上面传输，如果有恶意第三方的存在，我们传输的数据就
+可以被捕捉监听甚至修改，从而导致各种各样无法预测的结果，所以，考虑到安全性，我们还是要学一学如果用 `TLS` 进行网络编程，以提高我们的客户端和服务器之
+前传输数据的安全性。
+
+  `TLS` 即 `Transport Layer Security` ，就是为了保证传输层上的数据安全，服务器和客户端会在 `handshake` 阶段协商出只有两者知晓的一个对称密钥
+，然后之后的通信就利用这个对称密钥将数据进行加密后进行传输，很幸运的是，在我们使用 `TLS` 进行网络编程的时候，并不用去关注这些繁杂的细节，因为golang
+的标准库都已经帮我们封装好了（hooray！）。
+
+## 准备工作
+
+  使用 `TLS` 进行网络编程的时候，我们需要在服务器上放一个证书，证书的话可以使用 `openssl` 工具来生成，这里作为例子就先生成一个自签名的证书吧。
+
+  要生成一个证书，第一步当然就是生成一个私钥了，这里我们指定使用 `RSA` 算法生成一个 `2048bit` 的私钥。
+
+`$ openssl genrsa -out server.key 2048`
+ 
+  计算私钥是比较耗时的一个过程，无论多好的电脑，都需要在毫秒级别才能生成一个RSA私钥，这里输出的 `server.key` 就是我们要的私钥，生成的私钥是经过
+`pem` 格式编码的，也就是类似如下的格式：
+
+```
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA1DYBcVnQFyFEW9AhHY+6+Shp7LIT5ZtjbFfu2ZQgg3aTsXVF..........
+-----END RSA PRIVATE KEY-----
+```
+
+  当拥有了私钥之后，为了得到对应的证书，我们还需要有一个 `Certificate Signing Request` ，简称为 `CSR` ，也就是证书签名请求。
+
+`$ openssl req -new -sha256 -key server.key -out server.csr`
+
+  这个命令会要求你输入相关的信息，填入相关信息后就会得到一个 `server.csr` 文件，这个文件也是用`pem` 格式编码的，类似如下：
+
+```
+-----BEGIN CERTIFICATE REQUEST-----
+MIIC0jCCAboCAQAwbjELMAkGA1UEBhMCQ04xETAPBgNVBAcMCEhhbmd6aG91MQww..........
+-----END CERTIFICATE REQUEST-----
+```
+
+  最后一步就是利用上面步骤中得到的私钥和证书签名请求来最终获得一个自签名的证书。
+
+`$ openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt`
+
+  最终获得的证书 `server.crt` 也是 `pem` 格式编码的，类似如下：
+
+```
+-----BEGIN CERTIFICATE-----
+MIIDWDCCAkACCQCFvy51fQRGMTANBgkqhkiG9w0BAQUFADBuMQswCQYDVQQGEwJD..........
+-----END CERTIFICATE-----
+```
+
+  利用下面的命令我们可以查看我们刚刚生成的符合 `x509` 标准的证书的具体信息，里面包含了两个比较重要的信息就是公钥（public key）和签名
+（signature）。
+
+`$ openssl x509 -in server.crt -noout -text`
+
+  上面这么多命令必须按顺序执行，也就是说第一步生成私钥，第二步生成证书签名请求，最后才能生成我们的证书。
+
+## TLS server
+
+  在 `TLS` 编程中，服务器端需要得到私钥和证书的信息。我们使用 `tls.LoadX509KeyPair` 来获得我们之前生成证书和私钥信息，然后在监听时作为
+参数导入。
+
+```golang
+package main
+
+import (
+    "crypto/tls"
+    "net"
+    "log"
+    "bufio"
+    "fmt"
+)
+
+func main() {
+    cert, err := tls.LoadX509KeyPair("server.crt", "server.key")
+    if err != nil {
+        log.Println(err)
+        return
+    }
+
+    config := &tls.Config{Certificates: []tls.Certificate{cert}}
+    listener, err := tls.Listen("tcp", ":443", config)
+    if err != nil {
+        log.Println(err)
+        return
+    }
+    defer listener.Close()
+    
+    for {
+        conn, err := listener.Accept()
+        if err != nil {
+            log.Println(err)
+            continue
+        }
+        go handleConnection(conn)
+    }
+}
+
+func handleConnection(conn net.Conn) {
+    defer conn.Close()
+    r := bufio.NewReader(conn)
+    for {
+        msg, err := r.ReadString('\n')
+        if err != nil {
+            log.Println(err)
+            return
+        }
+        
+        fmt.Println(msg)
+
+        n, err := conn.Write([]byte("world\n"))
+        if err != nil {
+            log.Println(n, err)
+            return
+        }
+    }
+}
+```
+
+## TLS client
+
+  与一般的TCP网络编程类似，作为客户端我们调用 `tls.Dial` 而非 `net.Dial` 来与服务器建立连接，但与 `net.Dial` 不同的是，我们还需要多传递一个
+`tls.Config` 参数，来指定 `TLS` 连接时的各种参数。
+
+```golang
+package main
+
+import (
+    "fmt"
+    "log"
+    "crypto/tls"
+)
+
+func main() {
+    conf := &tls.Config{
+        InsecureSkipVerify: true,
+    }
+
+    conn, err := tls.Dial("tcp", "127.0.0.1:443", conf)
+    if err != nil {
+        log.Println(err)
+        return
+    }
+    defer conn.Close()
+
+    n, err := conn.Write([]byte("hello\n"))
+    if err != nil {
+        log.Println(err)
+        return
+    }
+
+    buf := make([]byte, 100)
+    n , err := conn.Read(buf)
+    if err != nil {
+        log.Println(n, err)
+        return
+    }
+
+    fmt.Println(string(buf[:n]))
+}
+```
+
+## 抓取tcp包进行验证
+
+`$ tcpdump host 127.0.0.1 port 433`
+
+  当我们这时候使用类似 `tcpdump` 这样的抓包工具进行网络抓包时，我们不再能看到明文了，看到的只能是一堆乱七八糟的东西，因为客户端和服务器已经将数据
+经过加密处理了。

From 3d4eac4712f04523a86fa5ef75c0d04b1d343ab3 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Wed, 2 May 2018 15:35:10 +0800
Subject: [PATCH 48/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 616ef26..5dbf601 100644
--- a/index.md
+++ b/index.md
@@ -13,6 +13,7 @@
 - [x] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
 - [] 密码学基础
 - [x] TCP/IP [TCP/IP](TCP_IP.md)
+- [x] TLS [TLS](TLS.md)
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
 - [] Docker & Golang 一对好基友

From 53e24f80a439b2c8af476aadf5095796db7df1b2 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Wed, 2 May 2018 16:06:25 +0800
Subject: [PATCH 49/85] Create AboutMe.md

---
 AboutMe.md | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 AboutMe.md

diff --git a/AboutMe.md b/AboutMe.md
new file mode 100644
index 0000000..70b786d
--- /dev/null
+++ b/AboutMe.md
@@ -0,0 +1 @@
+// TODO

From 3888d1397c94522a8c3ac6f2b0b9bfe23344b36b Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Wed, 2 May 2018 22:30:45 +0800
Subject: [PATCH 50/85] Create Screenshot.md

---
 Screenshot.md | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100644 Screenshot.md

diff --git a/Screenshot.md b/Screenshot.md
new file mode 100644
index 0000000..360b60d
--- /dev/null
+++ b/Screenshot.md
@@ -0,0 +1,120 @@
+# 使用Golang获取手机屏幕截图
+
+  在今年年前的时候，像冲顶大会，芝士超人这样的答题赚钱类应用异常火爆，当然了，聪明的程序猿们也不是吃素的，为了提高答对题目的概率，就写了一些程序（脚本
+）来自动化答题过程，整个作弊的思路并不难，关键点就在于获取手机的当前截屏，然后通过文字识别来获取题目的信息，最后再调用浏览器搜索答案，在浏览器中出现最
+多的那个基本就可以锁定是答案了（当然还有其他的作弊方法，这里不一一列举），这种作弊方法在初期真是屡试不爽，所以今天就让我们来看一下整个作弊流程的关键环
+节——自动获取手机截屏的代码实现吧。
+
+  因为手机分为 `Android` 阵营和 `iOS` 阵营，而且两者实现截屏的方法也稍有不同，所以我们先定义一个 `Screenshot` interface对两种平台进行抽象，之
+后会分别定义 `iOS` 和 `Android` 这两个结构体来实现这个接口（有没有慢慢开始体会到 `interface` 的好处呢），下面的代码中我们也利用了工厂模式来返回 
+`Screenshot` 实例。
+
+```golang
+type Screenshot interface {
+    GetImage() (image.Image. error)
+}
+
+func NewScreenshot(cfg *Config) Screenshot {
+    if cfg.Device == "iOS" {
+        return NewIOS(cfg)
+    }
+    return NewAndroid(cfg)
+}
+
+type Config {
+    Device     string
+    WdaAddress string
+    .....
+}
+```
+
+## Android 实现
+ 
+  我们先定义个 `Android` 结构体，这个结构体实现了 `GetImage` 方法，所以可以在上面的工厂方法中作为 `Screenshot` 接口返回。而在真正运行我们的代码
+之前，我们需要确保安卓机已经正确地连接至电脑，并且要确保电脑上已经安装过 `adb` 这个命令行工具。这里实现截屏主要靠调用 `adb` 的 `screencap` 和
+`pull` 这两个子命令，`screencap` 对当前手机进行截屏并保存到手机本地，`pull` 命令将手机上的截图传到电脑本地（然后就可以接着一通骚操作了），我们通
+过 `exec.Command` 接口对 `adb` 进行调用，整体思路比较清晰，难点应该就在我们不熟悉 `adb` 命令的使用。
+
+```golang
+type Android struct {}
+
+func NewAndroid(cfg *Config) *Android {
+    return new(Android)
+}
+
+func (android *Android) GetImage() (img image.Image, err error) {
+    err := exec.Command("adb", "shell", "screencap", "-p". "/sdcard/screenshot.png").Run()
+    if err != nil {
+        return
+    }
+    dstImagePath := "/Desktop" + "origin.png"
+    err = exec.Command("adb", "pull", "/sdcard/screenshot.png", dstImgPath).Run()
+    if err != nil {
+        return
+    }
+    f, err := os.Open(dstImagePath)
+    if err != nil {
+        return nil, err
+    }
+    defer f.Close()
+    return png.Decode(f)
+}
+```
+
+## iOS 实现
+
+  依葫芦画瓢，同样定义一个 `IOS` 结构体，来实现 `GetImage` 方法，在具体截屏的实现中，由于 `iOS` 系统的封闭性，所以自然没有像 `Android` 的
+`adb` 那样的命令行调试工具，不过也不是说啥事也做不了，在 `iOS` 中，我们可以向特定接口地址 `wdaAddress` 发送 `http` 的`GET` 请求，然后通过解析
+返回的 `json` 数据，这个 `json` 数据中就编码了我们的图片信息，然后再利用 `base64` 进行解码获得一个字节数组，再对该字节数组进行 `png` 解码，最终
+就可以获得我们想要的 `image` 了。
+
+```golang
+type IOS struct {
+    wdaAddress string
+}
+
+type screenshotRes {
+    Value     string `json:"value"`
+    SessionID string `json:"sessionId"`
+    Status    int    `json:"status"`
+}
+
+func NewIOS(cfg *Config) *IOS {
+    ios := new(IOS)
+    ios.wdaAddress = cfg.WdaAddress
+    if ios.wdaAddress == "" {
+        panic("Please specify the wda address")
+    }
+    return ios
+}
+
+func (ios *IOS) GetImage() (img image.Image, err error) {
+    body, err := util.HTTPGet(ios.wdaAddress)
+    if err != nil {
+         return
+    }
+    res := new(screenshotRes)
+    err = json.Unmarshal(body, res)
+    if err != nil {
+        return
+    }
+    pngValue, err := base64.stdEncoding.DecodeString(res.Value)
+    if err != nil {
+        return
+    }
+    src, err := png.Decode(bytes.NewReader(pngValue))
+    if err != nil {
+        return
+    }
+    img = src
+    return
+}
+```
+
+## 总结
+  
+  在本篇博客中，我们看到了工厂模式在具体程序设计中的应用，灵活使用工厂模式可以提供我们工程代码的水平，也见识到了如何利用 `adb` 工具对 `Android` 机
+器进行调试，当然了 `adb` 的功能不仅仅局限于上述两点，它还可以干非常多的事情，有兴趣的小伙伴可以自己研究一下，最后还有就是因为 `iOS` 系统本身的封闭性
+，我们只能通过调用web接口来获取其截图信息。
+
+  最后，作为程序员圈子里的一员，在日常生活中还是要多思考，啥东西要是可以用代码实现自动化，就马上动手去实现它吧。

From 1af6a8bb8b2838c496008116d358801535ce9fd2 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Wed, 2 May 2018 22:31:25 +0800
Subject: [PATCH 51/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 5dbf601..f922fc4 100644
--- a/index.md
+++ b/index.md
@@ -28,4 +28,5 @@
 - [x] 一致性哈希 [ConsistentHashing](ConsistentHashing.md)
 - [x] 使用 etcd 来进行服务注册与发现 [etcd](etcd.md)
 - [x] iobridge [iobridge](iobridge.md)
+- [x] 使用Golang获取手机屏幕截图 [Screenshot](Screenshot.md)
 

From fc2acc021f10496234bd573678b8510be4a3c196 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Thu, 3 May 2018 15:00:04 +0800
Subject: [PATCH 52/85] Create gRPC.md

---
 gRPC.md | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 194 insertions(+)
 create mode 100644 gRPC.md

diff --git a/gRPC.md b/gRPC.md
new file mode 100644
index 0000000..a86b6c2
--- /dev/null
+++ b/gRPC.md
@@ -0,0 +1,194 @@
+# gRPC
+  
+  首先，在了解 `gRPC` 之前，要搞清楚什么是 `RPC` ，`RPC` ， `Remote Procedure Call` ，即远程过程调度，它采用 `C/S`模型，本质就是客户端通过网
+络向远程计算机程序也就是服务端发送请求服务，远程服务器执行后向客户端返回答复，`RPC` 是分布式系统中非常重要的一个组成部分。
+
+  `RPC` 和本地方法调用最大的区别就在于程序是否在一个地址空间内执行，那么我们为什么要用到 `RPC` 呢，例如我们要写一个分布式爬虫的框架，因为自己的笔记
+本电脑处理能力有限，为了提高爬虫效率，我们租借了几台 `8C16G` 的高性能服务器帮我们完成任务，这样就可以由客户端发送爬虫指令，具体的粗活累活可以由服务器
+代替完成，如下面 `client` 向服务器发送一个 `crawl request` ，里面包含了我们要爬取的网站的具体信息，在服务器完成任务之后，向 `client` 返回一个
+`crawl response`，也就是爬虫的结果。
+
+![grpc](https://camo.githubusercontent.com/e4deb67aa41f71d46774f192b05b75be5c3da112/68747470733a2f2f7261772e6769746875622e636f6d2f6875696368656e2f7a6572672f6d61737465722f646f632f7a6572672e706e67)
+
+  关于 `RPC` ，不同的语言有不同的解决方案，例如在golang的标准库中就提供了 `net/rpc` 这个解决方案，虽然这个方法效率很高，但是它的缺点也是比较致命
+的，就是它仅能在golang程序中使用，而我们今天的主角，`gRPC` 就是一个跨语言平台的通用高性能 `RPC` 库，听着是不是很酷炫，下面就让我们来仔细学习一下吧
+。
+
+## 利用gRPC定义我们的服务
+
+  在使用 `gRPC` 之前我们需要安装它，在命令行中输入以下命令。
+
+`$ go get -u google.golang.org/grpc`
+
+  `gRPC` 在定义服务时采用了 `protobuf` 的语法格式，我们需要定义 `gRPC` 中请求消息和答复消息的数据格式，这个我们应该已经非常熟悉了，就是定义两个
+`message` （详情可以参考 `protobuf` 的官方文档），`CrawlRequest` 和 `CrawlResponse` ，除此之外我们还要定义一个 `service` ，说白了也就是我
+们要定义的 `RPC` 方法。也就是说我们向服务器发送一个 `CrawlRequest` 消息，服务器处理完成后会向我们返回一个 `CrawlResponse` 。
+
+```protobuf
+service Crawl {
+    rpc Crawl (CrawlRequest) returns (CrawlResponse) {}
+}
+
+message CrawlRequest {
+    string url = 1;
+    int64 timeout = 2;
+}
+
+message CrawlResponse {
+    string content = 1;
+}
+```
+
+  使用 `gRPC` 的好处就在于它可以根据我们定义的服务自动生成相应的服务端和客户端的代码，也就是说我们只要关心具体调用服务的逻辑，剩下具体的实现由框架帮
+我们干就可以了。
+
+  由于是 `protobuf` 文件，我们使用 `protoc` 来编译它就可以了。
+
+`$ protoc --go_out=plugins=grpc:. *.proto`
+
+## gRPC 原理
+
+  `gRPC` 相当于给我们一个框架，具体的代码就在我们的 `crawl.proto` 这个文件经过 `protoc` 编译后的得到的 `crawl.pb.go` 这个文件中，下面的代码
+都是由 `protoc` 经过编译后自动生成的，编译后的文件都具有类似的格式，基本看一个例子就可以一通百通了。
+
+  编译后得到的代码在客户端部分定义了一个大写的 `CrawlClient` 这个interface，而小写的 `crawlClient` 结构体则具体实现了它，注意到一个
+`crawlClient` 中包含了一个`grpc.ClientConn` 结构体，与 `net.Conn` 类似，它是真正发送网络请求的结构体，`grpc.Invoke` 则才是具体发送网络请求
+的行为。
+
+```golang
+type CrawlClient interface {
+    Crawl(ctx context.Context, in *CrawlRequest, opts ...grpc.CallOption) (*CrawlResponse, error)
+}
+
+type crawlClient struct {
+    cc *grpc.ClientConn
+}
+
+type NewCrawlClient(cc *grpc.ClientConn) CrawlClient {
+    return &crawlClient{cc}
+}
+
+func (c *crawlClient) Crawl(ctx context.Context, 
+                            in *CrawlRequest, 
+                            opts ...grpc.CallOption) (*CrawlResponse, error) {
+    out := new(CrawlResponse)
+    err := grpc.Invoke(ctx, "/protos.Crawl/Crawl", in, out, c.cc, opts...)
+    if err != nil {
+        return nil, err
+    }
+    return out, nil
+}
+```
+
+  与 `CrawClient` 不同的是，并没有结构体直接实现 `CrawlServer` 这个接口，毕竟 `gRPC` 仅仅是一个框架，具体的逻辑代码还是需要我们自己去填充的。
+`RegisterCrawlServer` 方法则向 `gRPC` 注册该服务，注册的过程也需要我们自己在代码中调用。
+
+```golang
+type CrawlServer interface {
+    Crawl(context.Context, *CrawlRequest) (*CrawlResponse, error)
+}
+
+func RegisterCrawlServer(s *grpc.Server, srv CrawlServer) {
+    s.RegisterService(&_Crawl_serviceDesc, srv)
+}
+
+func _Crawl_Crawl_Handler(srv interface{}, 
+                          ctx context.Context, 
+                          dec func(interface{}) error, 
+                          interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+    in := new(CrawlRequest)
+    if err := dec(in); err != nil {
+        return nil, err
+    }
+    if interceptor == nil {
+        return srv.(CrawlServer).Crawl(ctx, in)
+    }
+    info := &grpc.UnaryServerInfo{
+        Server:     srv,
+        FullMethod: "/protos.Crawl/Crawl",
+    }
+    handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+        return srv.(CrawlServer).Crawl(ctx, req.(*CrawlRequest))
+    }
+    return interceptor(ctx, in, info, handler)
+}
+
+var _Crawl_serviceDesc = grpc.ServiceDesc{
+    ServiceName: "protos.Crawl",
+    HandlerType: (*CrawlServer)(nil),
+    Methods: []grpc.MethodDesc{
+        {
+            MethodName: "Crawl",
+            Handler:    _Crawl_Crawl_Handler,
+        },
+    },
+    Streams:  []grpc.StreamDesc{},
+    Metadata: "crawl.proto",
+}
+```
+
+## Server
+  
+  下面需要我们自己实现具体的业务逻辑（这里就是爬虫的业务逻辑），毕竟 `gRPC` 可不能猜到我们究竟要干什么事情，我们先定义一个包含 `http.Client` 的结
+构体 `crawlServer` ，当然了，`crawlServer` 究竟要包含什么要看具体的业务场景了，这里因为是爬虫所以要包含 `http.Client` 用于发送http请求。
+
+所以我们在 `Crawl` 方法中实现具体的爬虫逻辑。
+
+```golang
+type crawlServer struct{
+    client *http.Client
+}
+
+func (s *crawlServer) Crawl(ctx context.Context, in *pb.CrawlRequest) (*pb.CrawlResponse, error) {
+    return s.internalCrawl(in)
+}
+
+func (s *crawlServer) internalCrawl(in *pb.CrawlRequest) (*pb.CrawlResponse, error) {
+    // .... 具体的爬虫逻辑
+}
+```
+
+  与普通的 `tcp` 网络编程类似的，我们需要监听特定的端口，当这个端口监听到来自客户端的连接请求时，`gRPC` 会接受该连接请求，并且新起一个 `goroutine`
+去处理该请求，然后读取该请求的消息类型，`gRPC` 内部会去检查这个消息的类型，最后决定调用哪个 handler（也就是之前Register过的）去处理该请求，具体处
+理后向客户端返回答复。
+
+```golang
+listener, err := net.Listen("tcp", *address)
+if err != nil {
+    log.Fatal(err)
+}
+s:= grpc.NewServer()
+pb.RegisterCrawlServer(s, &crawlServer{})
+s.Serve(listener)
+```
+
+## Client
+
+  在客户端这里，我们需要一个类似 `net.Conn` 这样的结构体用于发送和接受消息，在 `gRPC` 中，我们可以通过类似 `net.Dial` 的方法 `grpc.Dial` 来返
+回一个 `grpc.ClientConn` 结构体，然后通过该结构体新建一个客户端 `pb.NewCrawlClient` ，最后用这个客户端发送我们的请求就可以了，在很多地方，客户
+端也被叫做 `stub`， 因为它仅仅有方法调用的接口，而真正实现业务逻辑的方法还是在服务端上面。
+
+```golang
+conn, err := grpc.Dial(*address, grpc.WithInsecure())
+if err != nil {
+    log.Fatal(err)
+}
+client := pb.NewCrawlClient(conn)
+
+request := pb.CrawlRequest{
+    Url:     *url,
+    Timeout: 10000,
+}
+response, err := client.Crawl(context.Background(), &request)
+if err != nil {
+    log.Fatal(err)
+}
+```
+
+## 总结
+  
+  在使用 `gRPC` 时，我们需要明确自己的责任与分工，什么事情应该我们自己干，而什么事情又是框架帮我们干好而我们不需要重复去做的。其实说到底，我们使用
+`RPC` 的目的就是让任务在远程执行后返回处理结果，具体的任务该怎么执行 `gRPC` 肯定是不知道的，所以相关的业务逻辑应该在服务端进行定义，毕竟活是服务端干
+的（如上面结构体 `crawlServer` 实现的 `Crawl` 方法），我们只需要定义相关的 `Request` 和 `Response` 消息，再定义一个 `service` 服务，用
+`protoc` 编译生成相应的代码，最后再定义服务端上面的处理逻辑就可以了。
+

From bdc29c0e838c4f9becc87ae6d14ba45567110f37 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Thu, 3 May 2018 15:00:46 +0800
Subject: [PATCH 53/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index f922fc4..57b1d81 100644
--- a/index.md
+++ b/index.md
@@ -9,7 +9,7 @@
 - [x] 事件定时器 [EventTimer](EventTimer.md)
 - [] p2p Discoverer
 - [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
-- [] 使用gRPC来进行节点之间的通信
+- [] gRPC [gRPC](gRPC.md)
 - [x] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
 - [] 密码学基础
 - [x] TCP/IP [TCP/IP](TCP_IP.md)

From f28d0fd0b13ae538d760d04d828e45e18ccd8ac0 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Thu, 3 May 2018 15:02:39 +0800
Subject: [PATCH 54/85] Set theme jekyll-theme-cayman

---
 _config.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/_config.yml b/_config.yml
index fc24e7a..c419263 100644
--- a/_config.yml
+++ b/_config.yml
@@ -1 +1 @@
-theme: jekyll-theme-hacker
\ No newline at end of file
+theme: jekyll-theme-cayman
\ No newline at end of file

From af2913d017ad4b2c37f92398287532f29ecdb893 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Thu, 3 May 2018 15:10:39 +0800
Subject: [PATCH 55/85] Update index.md

---
 index.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 57b1d81..f507b9a 100644
--- a/index.md
+++ b/index.md
@@ -27,6 +27,10 @@
 - [x] 让任务定时执行 [crontab](Crontab.md)
 - [x] 一致性哈希 [ConsistentHashing](ConsistentHashing.md)
 - [x] 使用 etcd 来进行服务注册与发现 [etcd](etcd.md)
+- [x] 使用 etcd 来实现负载均衡
 - [x] iobridge [iobridge](iobridge.md)
 - [x] 使用Golang获取手机屏幕截图 [Screenshot](Screenshot.md)
-
+- [] port 0, WTF?
+- [] Makefile
+- [] EDC, my Every Day Carry
+- [] Architecture of Open Source Project

From 1cecb2f183fbaefbd87d748f7958b800030dda55 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Thu, 3 May 2018 15:19:42 +0800
Subject: [PATCH 56/85] Create git.md

---
 git.md | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 git.md

diff --git a/git.md b/git.md
new file mode 100644
index 0000000..8f0d624
--- /dev/null
+++ b/git.md
@@ -0,0 +1,22 @@
+# git 系列
+
+Git 中一些不常用，但可能很有用的一些命令整理
+TODO // 待填坑
+
+## bisect
+
+## blame
+
+## cherrypick
+
+## clean
+
+## gui
+
+## merge
+
+## mergetool
+
+## reset
+
+## stash

From b9aca444448ccf354da07beecee5c211789bfe2f Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Thu, 3 May 2018 15:20:44 +0800
Subject: [PATCH 57/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index f507b9a..5f1e3a5 100644
--- a/index.md
+++ b/index.md
@@ -34,3 +34,4 @@
 - [] Makefile
 - [] EDC, my Every Day Carry
 - [] Architecture of Open Source Project
+- [] git 不常用但实用命令整理 [git](git.md)

From ff07e6b45ce7023aa09bc40be07cd5bfb4e1279b Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Thu, 3 May 2018 23:26:12 +0800
Subject: [PATCH 58/85] Create Port_0.md

---
 Port_0.md | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)
 create mode 100644 Port_0.md

diff --git a/Port_0.md b/Port_0.md
new file mode 100644
index 0000000..8542402
--- /dev/null
+++ b/Port_0.md
@@ -0,0 +1,45 @@
+# port 0, WTF?
+  
+  我们都知道 `TCP` 或者 `UDP` 的端口号可以从0一直到65535，而0到1023端口则被称为熟知端口，是系统保留使用的端口号，0号端口在 `TCP/IP` 中也属于保留
+端口号，但它并不可以在一般的 `TCP` 或者 `UDP` 通信中使用。
+
+  但是，如果0号端口不能被使用的话，那它又有什么用呢，简单来讲，就是如果程序监听了0号端口，那么它就会触发操作系统自动去寻找在0到65535这个范围内可以使
+用的端口号（即未被其他程序占用），这样就相当于动态分配一个端口号了，和 `DHCP` 协议有着异曲同工之妙，不过 `DHCP` 是用于动态分配IP地址，而0号端口则用
+于动态分配端口号，而且两者的工作原理也不同。
+
+  所以，如果想要得到一个动态的端口号，我们只要让程序监听0号端口就可以了，即 `net.Listen("tcp", "127.0.0.1:0")` ，下面给出一个获取动态端口号的示
+例代码。
+
+```golang
+package main
+
+import (
+    "net"
+    "strconv"
+)
+
+func GetFreePort() (port int, err error) {
+    listener, err := net.Listen("tcp", "127.0.0.1:0")
+    if err != nil {
+        return 0, err
+    }
+    defer listener.Close()
+     
+    addr := listener.Addr().String()
+    _, portString, err := net.SplitHostPort(addr)
+    if err != nil {
+        return 0, err
+    }
+    return strconv.Atoi(portString)
+}
+
+func main() {
+    port, err := GetFreePort()
+    if err != nil {
+        fmt.Println(err)
+        return
+    }
+
+    fmt.Println(port)
+}
+```

From ff44bc00de421d881e37cb3dc08cc1ae27251112 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Thu, 3 May 2018 23:26:55 +0800
Subject: [PATCH 59/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 5f1e3a5..6ef1330 100644
--- a/index.md
+++ b/index.md
@@ -30,7 +30,7 @@
 - [x] 使用 etcd 来实现负载均衡
 - [x] iobridge [iobridge](iobridge.md)
 - [x] 使用Golang获取手机屏幕截图 [Screenshot](Screenshot.md)
-- [] port 0, WTF?
+- [x] port 0, WTF? [Port_0](Port_0.md)
 - [] Makefile
 - [] EDC, my Every Day Carry
 - [] Architecture of Open Source Project

From 89953bac91c3cc0da5e6493315447d7a9307245c Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Fri, 4 May 2018 00:29:59 +0800
Subject: [PATCH 60/85] Create LoadBalancing.md

---
 LoadBalancing.md | 156 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 156 insertions(+)
 create mode 100644 LoadBalancing.md

diff --git a/LoadBalancing.md b/LoadBalancing.md
new file mode 100644
index 0000000..8106b4a
--- /dev/null
+++ b/LoadBalancing.md
@@ -0,0 +1,156 @@
+# 使用 etcd 进行负载均衡
+  
+  当服务提供者（Service Provider）将服务注册到 `etcd` 之后，为了更加理想地分配任务·，我们就要考虑负载均衡问题了，负载均衡的核心就是把让任务合理地
+分摊到多个执行单元上执行，以期获得更高的吞吐量或者说是数据处理能力，通俗点讲就是大家都有肉吃，没有人会撑死也没人会饿死，大家都得到适合自己的那一份。
+
+  下图中，`Service Consumer` 即服务的消费者，它通过向 `Provider` 源源不断地发送服务请求，负载均衡问题要解决的问题就是如何将请求合理地分配给不同
+的 `Service Provider` 处理，例如，我们下图中有三台服务提供者，假如服务提供者的服务都是比较消耗时间的（例如计算哈希值），为了增加整个系统的吞吐量，
+我们要考虑合适的任务分配策略，这就是负载均衡的核心所在，要是只将请求发送给其中一台 `Service Provider`，那么其它两台都会空闲着不执行任务，这样的话
+整个系统的吞吐量肯定就上不来了，毕竟没有充分的利用所有的资源，所以，我们要把任务平均地分配给每个执行单元去处理。
+
+![loadbalancing](https://code.aliyun.com/middlewarerace2018/docs/raw/master/assets/system-architecture.png)
+
+  下面我们来实现一个负载均衡的例子，实现的思路非常简单，定义一个 `LoadBalancingService` 结构体，其中的 `etcdClient` 用于与 `etcd` 进行沟通交
+流以获取相应的注册信息，`nodes`，`nodeMap`，`nodeCount` 用于记录已经注册在 `etcd` 的服务提供者的信息。
+
+```golang
+type LoadBalancingService struct {
+    etcdClient client.Client
+    connected  bool
+    nodes      []string
+    nodeMap    map[string]int
+    nodeCount  int
+    sync.RWMutex
+}
+```
+
+```golang
+func (s *LoadBalancingService) watch(watcher client.Watcher) {
+    for {
+        resp, err := watcher.Next(context.Background())
+        if err == nil {
+            if resp.Action == "set" {
+                n := resp.Node.Value
+                s.addNode(n)
+            } else if resp.Action == "delete" {
+                n := resp.PrevNode.Value
+                s.removeNode(n)
+            }
+        }
+    }
+}
+```
+
+  上面的核心就是 `Watcher` 机制，在新事件到来之前，`Watcher` 的`Next` 方法会一直阻塞，直到事件返回为止，一般来说都是在 `for` 循环中调用 `Next`
+以期达到轮询（polling）的效果。
+
+```golang
+type Watcher interface {
+    Next(context.Context) (*Response, error)
+}
+```
+
+  `Connect` 函数就是用于客户端与 `etcd` 连接并根据 `serviceName` 读取当前已经注册过的服务，并通过 `watch` 机制不断地更新当前可用的服务。
+
+```golang
+func  (s *LoadBalancingService) Connect(serviceName string, endPoints []string) error {
+    if s.connected {
+        log.Println("Can't connect twice")
+        return errors.New("Already connected")
+    }
+
+    s.nodeMap = make(map[string]int)
+    s.nodeCount = 0
+
+    cfg := client.Config{
+        Endpoints:   endpoints,
+        Transport:   client.DefaultTransport,
+        HeaderTimeoutPerRequest: time.Second,
+    }
+
+    var err error
+    s.etcdClient, err = client.New(cfg)
+    if err != nil {
+        return err
+    }
+    kapi := client.NewKeysAPI(s.etcdClient)
+
+    resp, err := kapi.Get(context.Background(), serviceName, nil)
+    if err != nil {
+        return err
+    } else {
+        if resp.Node.Dir {
+            for _, peer := resp.Node.Nodes {
+                n := peer.Value
+                s.addNode(n)
+            }
+        }
+    }
+
+    watcher := kapi.Watcher(serviceName, &client.WatcherOptions{Recursive: true})
+    go s.watch(watcher)
+    service.connected = true
+    return nil    
+}
+```
+
+  `GetNode` 采用了随机算法获取当前节点群中的随机节点进行处理我们的任务，这里是负载均衡算法的核心，说白了中随机挑选一个进行发送服务请求让服务提供者进
+行处理。
+
+```golang
+func (s *LoadBalancingService) GetNode() (string, error) {
+    if !s.connected {
+        return "", errors.New("Must call connect first")
+    }
+
+    s.RLock()
+    defer s.RUnlock()
+
+    if s.nodeCount == 0 {
+        return "", ErrEmptyService 
+    }
+
+    return s.nodes[rand.Intn(s.nodeCount)], nil
+}
+```
+
+  `addNode` 和 `removeNode` 分别对应远程服务提供者数量增加或者减少时的情况，并更新 `LoadBalancingService` 结构体中相应的数据结构。
+
+```golang
+func (s *LoadBalancingService) addNode(key string) {
+    s.Lock()
+    defer s.Unlock()
+
+    if _, ok := s.nodeMap[key]; ok {
+        if s.nodeCount >= len(s.nodes) {
+            s.nodes = append(s.nodes, key)
+        } else {
+            s.nodes[s.nodeCount] = key
+        }
+        s.nodeMap[key] = s.nodeCount
+        s.nodeCount++
+    }
+}
+
+func (s *LoadBalancingService) removeNode(key string) {
+    s.Lock()
+    defer s.Unlock()
+
+    if index, ok := s.nodeMap[key]; ok {
+        for i := index; i < len(s.nodes)-1; i++ {
+            s.nodes[i] = s.nodes[i+1]
+        }
+        s.nodeCount--
+        delete(s.nodeMap, key)
+    }
+}
+```
+
+## 负载均衡算法的改良
+  
+  在上面的例子中，我们采用的是随机算法，这种是基于每台处理任务的 `Service Provider` 性能差不多的情况下作出的抉择，但是现实生活中情况并不总是如此，
+每台服务器的性能，网络带宽都存在着或多或少的差异，所以，我们需要根据实际情况，对负载均衡的分配方法作相应的改进。
+
+  假设我们的服务提供者的平均综合性能比值为1:2:3，那么每台服务提供者任务分配的比例也应该是1:2:3，所谓能者多劳就是这个意思了。在其他情况下我们也可以根
+据流量比例分配任务，反正还是要具体问题具体分析。
+

From 324c5a69e84f6170561dca4aa965288c031e0f38 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Fri, 4 May 2018 00:31:58 +0800
Subject: [PATCH 61/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 6ef1330..f0785e0 100644
--- a/index.md
+++ b/index.md
@@ -27,7 +27,7 @@
 - [x] 让任务定时执行 [crontab](Crontab.md)
 - [x] 一致性哈希 [ConsistentHashing](ConsistentHashing.md)
 - [x] 使用 etcd 来进行服务注册与发现 [etcd](etcd.md)
-- [x] 使用 etcd 来实现负载均衡
+- [x] 使用 etcd 来实现负载均衡 [LoadBalancing](LoadBalancing.md)
 - [x] iobridge [iobridge](iobridge.md)
 - [x] 使用Golang获取手机屏幕截图 [Screenshot](Screenshot.md)
 - [x] port 0, WTF? [Port_0](Port_0.md)

From 4bd7e222b0d7b08435f56e66bde43accae920775 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Fri, 4 May 2018 15:04:45 +0800
Subject: [PATCH 62/85] Create DecodingDubboProtocol.md

---
 DecodingDubboProtocol.md | 142 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 142 insertions(+)
 create mode 100644 DecodingDubboProtocol.md

diff --git a/DecodingDubboProtocol.md b/DecodingDubboProtocol.md
new file mode 100644
index 0000000..75c38a2
--- /dev/null
+++ b/DecodingDubboProtocol.md
@@ -0,0 +1,142 @@
+# Decoding Dubbo Protocol
+  
+  之前看 `shadowsocks` 源码的时候看过好多遍 `SOCKS5` 的 `RFC` ，看的一脸懵逼，不知道这些奇奇怪怪的网络协议究竟该如何理解，后来才发现网络协议也并
+没有那么神秘，无非就是为了让两台计算机使用网络进行通信时能互相理解对方而设定的一系列规则而已。就好像一个中国人和一个法国人聊天，法国人讲法语，而中国人
+用中文，他们自然很难理解对方究竟要表达什么意思，但是如果二者约定同时用两者都会的语言——英语进行交流，那么两者就可以正常交流下去了，因为两者协议了一套规
+则，可以相互理解的规则（这里的规则就是英语）。
+
+  一般来说要是想自己定义一套网络协议，首先需要做的事情就是定义这个协议中特定位置的字段分别代表什么含义，例如下图中的 `Dubbo` 协议，它就规定了一个报文
+中各个字段代表的含义，如下面第一个字节和第二个字节合在一起就叫做一个 `Magic Number` ，以此类推。
+
+![Dubbo](https://code.aliyun.com/middlewarerace2018/docs/raw/master/assets/dubbo-protocol.png)
+
+  所以，解析网络协议的本质就是去解析一个字节数组，并将它包含的数据转换成特定的含义的过程，下面我们就以解析 `Dubbo` 协议为例，来看一下如何正确地读取字
+节数组中的信息，在这之前，我们先了解一下golang标准库中的两个函数。
+
+  第一个就是 `bytes.NewBuffer(buf []byte) *bytes.Buffer` ，因为我们处理协议本质上就是处理字节数组，使用该函数对字节数组进行封装而不是直接操作字
+节数组的好处自然不用多说。当使用上述函数的时候，返回的 `Buffer` 会以 `buf` 这个字节数组作为它的初始内容，而且在这之后该 `Buffer` 就会全权掌控该字
+节数组（也就是说之后我们不可以再碰 `buf`了）。
+
+  第二个就是 `binary.Read(r io.Reader, order ByteOrder, data interface{}) error`，该函数可以非常方便的将结构化的二进制数据从 `r` 中以固
+定的字节序（ `ByteOrder` ）读到 `data`相应的数据字段中，由于`bytes.Buffer` 实现了`io.Reader` 中的 `Read` 方法，所以它也能作为第一个参数传入
+`binary.Read` 进行使用。
+
+  很多协议的报文都是变长的，为了方便起见，一般都是先解析定长部分的数据，然后再解析其变长部分的数据。
+
+## Dubbo 协议分析
+
+  在正式写协议解析类的代码之前，最好先明确该协议中各个字段的含义，所谓磨刀不误砍柴工嘛。
+  
+![Dubbo](https://code.aliyun.com/middlewarerace2018/docs/raw/master/assets/dubbo-protocol.png)
+
+* Magic 字段，共2字节，包含 `Magic High` 和 `Magic Low` 字段，用于特定标识该报文是否采用 `Dubbo` 协议而存在的，如果是则要求其值等于0xdabb。
+* Req/Res，1比特，用于表示该报文是请求报文还是答复报文，当值为1时说明该报文为请求报文，0时为答复报文。
+* 2way，1比特，当且仅当 `Req/Res` 为1时有效，也就是该报文为请求报文时，指明是否期望服务器返回数据，当值为1时则表示希望服务器返回数据。
+* Event，1比特，标记该报文是否为事件消息，例如心跳消息，则设置为1。
+* Serialization ID，5比特，用于表示变长数据部分序列化的方法，例如当使用 `fastjson` 时它的值为6。
+* Status，1字节，用于表示请求的状态，其值可以是
+
+    * 20 - OK
+    * 30 - CLIENT_TIMEOUT
+    * 31 - SERVER_TIMEOUT
+    * 40 - BAD_REQUEST
+    * 50 - BAD_RESPONSE
+    * 60 - SERVICE_NOT_FOUND
+    * 70 - SERVICE_ERROR
+    * 80 - SERVER_ERROR
+    * 90 - CLIENT_ERROR
+    * 100 -SERVER_THREADPOOL_EXHAUSTED_ERROR
+      
+* Request ID，64比特即8字节，用于唯一标识该请求。
+* Data Length，变长部分的数据长度，4字节长度。
+* Variable Part 报文中数据的变长部分，其使用的序列化方法由上述的`serializationID` 字段决定。
+
+## 解析过程
+
+  由于从 `Magic Number` 到 `Data Length` 这个数据字段，其总长度都是固定的，所以我们可以定义一个 `dubboHeader` 结构体包含这些数据字段，其中各
+个数据字段的类型由协议中各个字段的长度决定，例如 `MagicHigh` 的长度为一个字节，所以我们在结构体中使用一个 `byte` 来表示它就可以了，再比如
+`RequestID` 的长度是8字节，我们可以用 `uint64` 来表示，依次类推。
+
+  由于我们要解析的是一个字节数组，为了后面方便起见，我们使用 `bytes.NewBuffer` 来封装它，这样就可以得到一个 `io.Reader` ，然后再使用
+`binary.Read` 就可以解码得到 `dubboHeader` 这个结构体了。
+
+  当使用 `bytes.Buffer` 的时候需要注意一点，它里面有类似于指针概念，也就是当我们调用 `Read` 方法读取多少字节时，内部的指针 `off` 也会向前移动多
+少字节的长度，所以当我们使用 `binary.Read(buf, binary.BigEndian, &dubboHeader)` ，它就会将内部的指针移动 `len(dubboHeader)` 个长度，这样我们就可以利用
+`buf.Next` 方法获取剩下变长部分的数据啦。
+
+  定长部分解决好之后，我们来看变长部分应该怎么解决，协议中 ` Serialization ID` 字段规定了变长部分数据的序列化方法，例如这里使用 `json` 格式进行序
+列化，所以我们只要获取剩余部分的字节数组，然后调用`json.Unmarshal` 就可以获得剩余部分的数据内容了。
+
+## 完整代码段
+
+  自定义解析过程中可能出现的错误类型
+
+```golang
+var ErrInvalidMagicNumber = errors.New("Invalid Magic Number")
+
+var ErrInvalidDataLength = errors.New("Invalid Data Length")
+
+var ErrUnmarshallingJSON = errors.New("Encounter Error when Unmarshalling JSON")
+```
+
+  自定义变长部分数据对应的结构体，注意，结构体中的数据字段必须为大写，否则使用 `json.Unmarshal` 会出错。
+
+```golang
+type VariablePart struct {
+    DubboVersion     string `json:"DubboVersion"`
+    ServiceName      string `json:"ServiceName"`
+    ServiceMethod    string `json:"ServiceMethod"`
+    MethodName       string `json:"MethodName"`
+    MethodParamTypes string `json:"MethodParamTypes"`
+    MethodArguments  string `json:"MethodArguments"`
+    Attachments      string `json:"Attachments"`
+}
+```
+
+  按协议规则一步步解析字节数组并得到相应的信息。
+
+```golang
+func Parse(b []byte) (*VariablePart, error) {
+    buf := bytes.NewBuffer(b)
+ 
+    var dubboHeader struct {
+        MagicHigh  byte
+        MagicLow   byte
+        Misc       byte
+        Status     byte
+        RequestID  uint64
+        DataLength uint32
+    }
+
+    var err error
+    err = binary.Read(buf, binary.BigEndian, &dubboHeader)
+    if err != nil {
+        return nil, err
+    }
+    if !(dubboHeader.MagicHigh == 0xda && dubboHeader.MagicLow == 0xbb) {
+        return nil, ErrInvalidMagicNumber
+    }
+
+    partBytes := buf.Next(int(dubboHeader.DataLength))
+    if len(partBytes) < int(dubbo.DataLength) {
+        return nil, ErrInvalidDataLength
+    }
+    
+    partBuffer := bytes.NewBuffer(partBytes)
+
+    var variablePart VariablePart
+
+    switch dubboHeader.Misc & 0x1F {
+    case 1:
+        //
+    case 2:
+        //
+    default:
+        err = json.Unmarshal(partBuffer, &variablePart)
+        if err != nil {
+            return nil, ErrUnmarshallingJSON
+        }
+    }
+    return &variablePart, nil
+}
+```

From 725ca27b2b0884eca1de3eacca2e9cfbfaa3d5d5 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Fri, 4 May 2018 15:06:25 +0800
Subject: [PATCH 63/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index f0785e0..82e0729 100644
--- a/index.md
+++ b/index.md
@@ -9,6 +9,7 @@
 - [x] 事件定时器 [EventTimer](EventTimer.md)
 - [] p2p Discoverer
 - [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
+- [x] 解析Dubbo协议 [DecodingDubboProtocol](DecodingDubboProtocol.md)
 - [] gRPC [gRPC](gRPC.md)
 - [x] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
 - [] 密码学基础

From d43a00d1139964324a02396b7d00843a2424b5c5 Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Fri, 4 May 2018 19:56:51 +0800
Subject: [PATCH 64/85] Create Docker.md

---
 Docker.md | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 108 insertions(+)
 create mode 100644 Docker.md

diff --git a/Docker.md b/Docker.md
new file mode 100644
index 0000000..93e64c4
--- /dev/null
+++ b/Docker.md
@@ -0,0 +1,108 @@
+# Docker & Golang
+
+  还记得 `java` 当初响遍大江南北的口号吗，`Write one，run anywhere` ，到了互联网时代，`Docker` 这家公司打出了更为牛逼的口号，`Build，Ship，
+and Run Any App，Anywhere` ，它们两者有类似的地方，都是为了追求各种平台的兼容性和通用性。`java` 的跨平台是因为它在不同平台下有不同的 `jvm` 实现
+，不过它仅仅局限于 `java` 这个生态系统里面，相比之下，`Docker` 的野心就更大了，它并没有给我们把编程语言给限定死了，它提供了一个与操作系统隔离的环境
+，可以让我们写任何应用，而且可以在几乎所有平台下跑。
+
+  具体 `Docker` 的细节这里就不具体探讨了，网上也有一堆文章，今天我们主要看一下如何使用它，也就是如何将我们的应用打包成一个 `Docker` 镜像。
+
+  下面我们要实现的是一个用 `golang` 写的 `http` 服务器，但是这个服务器并不是跑在裸机上，而是跑在 `Docker` 容器中的。顺便一提，`Docker` 也是用
+`golang` 写的哦，是不是感觉很厉害呢。
+
+## 书写自己的应用逻辑
+
+下面定义了我们要实现的 `http` 服务器的前端，无奈直男不会写 `CSS` ，就用 `HTML` 写了一下最基本的提交 `FORM` 表单的功能。
+
+```html
+<html>
+    <head>
+        <title></title>
+    </head>
+    <body>
+        <form action="/hash" method="post">
+            interface: <input type="text" name="interface">
+            method: <input type="text" name="method">
+            parameterTypesString: <input type="text" name="parameterTypesString">
+            parameter: <input type="text" name="parameter">
+            <input type="submit" value="hash">
+        </form>
+    </body>
+</html>
+```
+
+  下面使用了golang中的标准库 `net/http` 和 `html/template` 起一个服务器，我们自己定义的 `hashHandler` 会根据请求的类型分别进行处理，如果是
+`GET` 请求则展示我们的前端界面，如果是 `POST` 请求则处理我们浏览器提交的表单并将表单输出到命令行中。到这里，一个非常简单的 `http` 服务器就写好了。
+
+```golang
+package main
+
+import (
+    "fmt"
+    "html/template"
+    "log"
+    "net/http"
+    "strings"
+)
+
+func hashHandler(w http.ResponseWriter, r *http.Request) {
+    fmt.Println("method: ", r.Method)
+    if r.Method == "GET" {
+        t, _ := template.ParseFiles("hash.tpl")
+        t.Execute(w, nil)
+    } else {
+        r.ParseForm()
+        for k, v := range r.Form {
+            fmt.Printf("key: %s, value: %s\n", k, v)
+        }
+    }
+}
+
+func main() {
+    http.HandleFunc("/hash", hashHandler)
+    err := http.ListenAndServe(":8080", nil)
+    if err != nil {
+        log.Fatal("ListenAndServe: ", err)
+    }
+}
+```
+
+## Dockerfile的撰写
+
+  到现在，就没我们 `golang` 什么事情了，有请我们今天的主角 `Docker` 登场了。为了能让我们的 `http` 服务器在容器中运行，我们需要写一个
+`Dockerfile` ，`Dockerfile` 的作用就是让 `Docker` 知道该如何构建镜像，当然平时大部分时候我们都可以直接用别人打包好的镜像，但是自己会写
+`Dockerfile` 也是一个非常重要的能力哦。
+
+  `FROM` 指令指定了我们的基础镜像是什么，也就是说我们构建的镜像是基于哪个镜像的基础之上的，这里 `golang:alpine` 作为我们的基础镜像。而`COPY` 指
+令则将我们的文件从操作系统中拷贝到容器环境中。`WORKDIR` 命令则制定了在 `Docker` 内部的工作目录，类似于 `cd` 命令，`RUN` 命令在容器中执行指令，这
+里我们把go源文件编译成二进制。`CMD` 指令指定了启动该容器时内部应该执行的命令。当然 `Dockerfile` 的命令还有很多，具体还是参考官方的文档吧。
+
+```dockerfile
+FROM golang:alpine
+COPY . /home
+WORKDIR /home
+RUN go build -o server ./server.go
+CMD /home/server
+```
+
+  写完了 `Dockerfile` ，就可以构建镜像了，不过构建镜像的事情我可干不来，还是交给 `Docker` 去做吧。下面的命令就帮助我们构建一个名为
+`oceanoverflow/server` 的 `golang` 服务器镜像。构建镜像的过程可能会比较花时间，需要耐心等待。
+
+`$ docker build -t oceanoverflow/server .`
+
+## 运行Docker容器
+
+  构建完了我们使用 `docker image ls` 就可以看到我们新构建的镜像啦，最后一步就是起容器服务了。
+
+  `-p` 命令这里将容器内的端口8080映射到操作系统的端口8080上去，如果不指定8080，那我们在自己的操作系统上就无法获取到它提供的服务，所以 `-p` 还是非
+常重要的。
+
+`$ docker run -p 8080:8080 --rm -it go-server`
+
+  如果一切无误，到浏览器中输入 `localhost:8080/hash` 就可以看到我们跑在 `Docker` 里面的 `http` 服务器已经成功地跑起来啦。
+
+## 总结
+  
+  让自己的应用跑在 `Docker` 容器中非常简单，第一步就是构建自己的应用程序的源码，第二步正确编写适应自己业务场景的 `Dockerfile` 然后利用`Docker` 
+自带的功能打包成一个镜像，最后一步就是运行容器了。就这么三步走，可谓是 `Docker`在手，天下我有了。
+

From 8e85273cd6ca2433720625918e90c8017bb674bb Mon Sep 17 00:00:00 2001
From: oceanoverflow <oceanoverflow@gmail.com>
Date: Fri, 4 May 2018 19:57:34 +0800
Subject: [PATCH 65/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 82e0729..6fabb9e 100644
--- a/index.md
+++ b/index.md
@@ -17,7 +17,7 @@
 - [x] TLS [TLS](TLS.md)
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
-- [] Docker & Golang 一对好基友
+- [x] Docker & Golang 一对好基友 [Docker](Docker.md)
 - [x] Bloom Filter [BloomFilter](BloomFilter.md)
 - [x] Merkle Tree [MerkleTree](MerkleTree.md)
 - [] goLevelDB

From 96e1f1d5d94555b95c218bb4e49296167a1abe43 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Fri, 4 May 2018 20:43:40 +0800
Subject: [PATCH 66/85] Update index.md

---
 index.md | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/index.md b/index.md
index 6fabb9e..144097d 100644
--- a/index.md
+++ b/index.md
@@ -1,8 +1,9 @@
 - [] Golang 设计模式
 - [x] 工厂模式 [FactoryPattern](FactoryPattern.md)
 - [x] PKCS7 Padding [PKCS7_Padding](PKCS7_Padding.md)
-- [] PBFT 共识算法
-- [] Paxos & Raft
+- [] PBFT共识算法
+- [] Paxos共识算法
+- [] Raft共识算法
 - [x] 有限状态机FSM [FSM](FSM.md)
 - [x] Fanin [Fan-in](Fanin.md)
 - [x] 事件处理引擎 [EventProcessEngine](EventProcessEngine.md)
@@ -36,3 +37,6 @@
 - [] EDC, my Every Day Carry
 - [] Architecture of Open Source Project
 - [] git 不常用但实用命令整理 [git](git.md)
+- [] dep，golang的依赖管理工具
+- [] 高并发爬虫
+- [] gossip协议

From 7c757ef441793efc6143e4ac82e0dfac5d0f1a01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 10:52:12 +0800
Subject: [PATCH 67/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 144097d..18e79dc 100644
--- a/index.md
+++ b/index.md
@@ -16,6 +16,7 @@
 - [] 密码学基础
 - [x] TCP/IP [TCP/IP](TCP_IP.md)
 - [x] TLS [TLS](TLS.md)
+- [] WebSocket
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
 - [x] Docker & Golang 一对好基友 [Docker](Docker.md)

From a7248f9bf4fa8677861cd90a084bc563093b093a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 15:59:38 +0800
Subject: [PATCH 68/85] Create Crawler.md

---
 Crawler.md | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 146 insertions(+)
 create mode 100644 Crawler.md

diff --git a/Crawler.md b/Crawler.md
new file mode 100644
index 0000000..bbf673d
--- /dev/null
+++ b/Crawler.md
@@ -0,0 +1,146 @@
+# Crawler
+  
+  爬虫算是练习并发编程的一个非常好的例子了，因为它既不枯燥，难度也不大，而且能在短时间内产出一个有趣的结果。爬虫一般通过某种搜索算法（深度优先搜索或者
+广度优先搜索）在互联网这个海量数据库中去寻找我们感兴趣的东西，如果使用得当的话，爬虫可以极大地提高我们的工作效率。
+
+  那么接下去我们从一个单线程的爬虫程序出发，然后慢慢将它改造成一个并发的爬虫程序。在这里，我们并不关心究竟如何去匹配我们感兴趣的元素(例如使用CSS进行元
+素匹配)，而是给出一个爬虫的基本思路。
+
+  首先，爬虫中非常重要的一个步骤就是匹配当前搜索的网页中的元素，并将符合我们要求的东西下载或保存起来，并且还会记录当前网页中的超链接地址，以便于我们继
+续进行深度优先搜索或者广度优先搜索。一般来说，我们感兴趣的东西都是符合特定规则的，而具体的规则我们可以在单个网页上进行正则匹配来获取，但是，由于不同网
+页的匹配规则并不相同，所以我们定义一个interface来抽象该行为。在这之后无论我们想要爬豆瓣还是新浪微博亦或是其他自己感兴趣的网站，只要重新定义一个结构体
+实现该方法就可以。
+
+  `Fetcher` 这个 `interface` 定义了如下行为，从一个特定的url去取我们感兴趣的内容以及这个网页上的所有的url链接。
+
+```golang
+type Fetcher interface {
+    Fetch(url string) (body string, urls []string, err error)
+}
+```
+
+  例如我们要爬豆瓣网上的东西，我们只要定义好一个结构体，并且实现上述`Fetch` 方法就可以了。所以无论以后你决定改爬别的网站了，再定义如下的结构体并实现
+相应的方法就可以了，这就是为什么上面我们定义了一个 `Fetcher` interface 来抽象这个行为的原因了。
+
+```golang
+type Result struct {
+    body string
+    urls []string
+}
+
+type doubanFetcher struct {
+    map[string]*Result
+    sync.RWMutex
+}
+
+func (f *doubanFetcher) Fetch(url string) (body string, urls []string, err error) {
+     // 这里写具体的匹配逻辑
+}
+```
+
+  爬虫还有一个重点就是搜索的策略，应该是广度优先还是深度优先，但是所有的链接形成为拓扑图并不一定是一个有向无环图，所以我们的爬虫在爬取的过程中可能会遇
+到相同的链接，但是注意这个逻辑同样应在在 `Fetch` 函数中实现。
+
+## 单线程版本实现
+
+  下面我们来看一下单线程深度优先的爬虫应该怎么写。
+
+```golang
+func Crawl(url string, depth int, fetcher Fetcher) {
+    if depth <= 0 {
+        return
+    }
+
+    body, urls, err := fetcher.Fetch(url)
+    if err != nil {
+        fmt.Println(err)
+        return
+    }
+    fmt.Printf("Founf: %s %q\n", url, body)
+    for _, u := range urls {
+        Crawl(u, depth-1, fetcher)
+    }
+    return
+}
+```
+
+  由于是深度优先搜索，所以我们用递归的策略来进行搜索，递归最重要的一点就是递归条件的出口条件，如果递归函数没有出口，那么就是死循环了。这里我们在函数的
+参数中引入一个深度的变量来控制是否应该继续进行递归，如果当前深度小于等于0，则退出。然后用 `fether`  来 `Fetch` 对应的 `url`上的内容，由于使用了接
+口，所以让我们这个函数的实现变得非常的灵活，而不是局限于哪个具体的实现，这就是接口的好处。然后根据当前网页中的链接进行深度优先搜索，
+`Crawl(u, depth-1, fetcher)` ，每搜索一层，深度减一，直到条件不再满足就可以退出了。
+
+  不过上述的爬虫还是单线程的，单线程的效率肯定不高，因为程序大部分时间都是在等待 `I/O`，`CPU` 大部分时间都是空闲的，所以，为了提高 `CPU`的占用率，
+我们应该考虑使用并发。
+
+## 并发版本实现
+
+  在 `golang` 里面写并发程序是相对比较简单的，但是也没有那么简单，下面的实现就是比较tricky的。这里为什么叫它并发而不是多线程呢，是因为`golang` 中
+的 `go` 机制并不是以线程作为基本单位的。
+
+```golang
+func Crawl(url string, depth int, fetcher Fetcher, ret chan string) {
+    defer close(ret)
+    if depth <= 0 {
+        return
+    }
+
+    body, urls, err := fetcher.Fetch(url) 
+    if err != nil {
+        ret <- err.Error()
+        return
+    }
+     
+    ret <- fmt.Sprintf("Found: %s %q", url, body)
+
+    result := make([]chan string, len(urls))
+    for i, u := range urls {
+        result := make(chan string)
+        go Crawl(u, depth-1, fetcher, result)
+    }
+
+    for i := range result {
+        for s := range result[i] {
+            ret <- s
+        }
+    }
+    
+    return
+}
+```
+
+  并发版本和单线程版本的基本框架是一样的，有下面几个区别。
+
+  首先我们定义个一个 `chan string` 也就是一个通道，一般来说，`go` 和`chan` 都是手拉手用在一起的。这个通道用于接受我们爬虫的结果。
+  
+  这个通道在整个函数返回时会关闭，也就是函数最初用的 `defer close(ret)` 。
+
+  当 `fetch.Fetch(url)` 方法返回时，也就是这个网页中的内容已经检索完毕了，我们将结果放到刚才的通道中等待其他地方的程序读取该通道中的内容。
+
+  下面就是函数的精髓了，由于上面通过 `fetch.Fetch(url)` 获得了一个`urls` 数组，因为要进行深度优先搜索，我们为这个 `urls` 中的 `url` 也准备一个
+`chan string` 用于存储它们抓取的内容，然后调用 `go Crawl(u, depth-1, fetcher, result)` 来并发爬取。
+
+  也就是说下面这段函数很快就返回了，但是它们并不一定都已经拿到相应的内容了。
+
+```golang
+for i, u := range urls {
+    result := make(chan string)
+    go Crawl(u, depth-1, fetcher, result)
+}
+```
+
+  所以，我们要等待它们拿回内容，下面代码的意思就是子任务完成后，将它拿到的内容传给上一层的通道中。这个就好比在一个公司中，`CEO` 将任务分别分配给下面
+各个部门的 `Manager` ，然后各个部门的 `Manager` 又细分任务后将任务分给部门下面的各个 `Worker` ，`Worker` 完成自己的任务后，将任务结果传给负责
+自己的 `Manager` ，然后由 `Manager` 再汇报给 `CEO` 。`CEO` 是总任务的分配者同时也是所有结果的收集者。
+
+```golang
+for i := range result {
+    for s := range result[i] {
+        ret <- s
+    }
+}
+```
+
+## 总结
+  
+  并发程序能充分利用现在 `CPU` 的所有核心，它利用单线程程序运行中部分代码段会因为等待 `I/O`（例如发送http请求和等待http答复）而占用 `CPU` 时间的
+特点，同时启动多个 `goroutine` 来最大程度上占用 `CPU` 时间，从而提高程序的运行效率。

From 5067c34f14dfc87f97bbe4d2ac3e10970bb1c4f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 16:00:23 +0800
Subject: [PATCH 69/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 18e79dc..66a9e2b 100644
--- a/index.md
+++ b/index.md
@@ -39,5 +39,5 @@
 - [] Architecture of Open Source Project
 - [] git 不常用但实用命令整理 [git](git.md)
 - [] dep，golang的依赖管理工具
-- [] 高并发爬虫
+- [x] 高并发爬虫 [Crawler](Crawler.md)
 - [] gossip协议

From b1c83ea2d87e80fd4097e633e00d0644be376ecc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 17:28:01 +0800
Subject: [PATCH 70/85] Update index.md

---
 index.md | 48 ++++++++++++++++++++++++------------------------
 1 file changed, 24 insertions(+), 24 deletions(-)

diff --git a/index.md b/index.md
index 66a9e2b..68275e8 100644
--- a/index.md
+++ b/index.md
@@ -1,43 +1,43 @@
 - [] Golang 设计模式
-- [x] 工厂模式 [FactoryPattern](FactoryPattern.md)
-- [x] PKCS7 Padding [PKCS7_Padding](PKCS7_Padding.md)
+- [x] [FactoryPattern](FactoryPattern.md)
+- [x] [PKCS7_Padding](PKCS7_Padding.md)
 - [] PBFT共识算法
 - [] Paxos共识算法
 - [] Raft共识算法
-- [x] 有限状态机FSM [FSM](FSM.md)
-- [x] Fanin [Fan-in](Fanin.md)
-- [x] 事件处理引擎 [EventProcessEngine](EventProcessEngine.md)
-- [x] 事件定时器 [EventTimer](EventTimer.md)
+- [x] [FSM](FSM.md)
+- [x] [Fan-in](Fanin.md)
+- [x] [EventProcessEngine](EventProcessEngine.md)
+- [x] [EventTimer](EventTimer.md)
 - [] p2p Discoverer
-- [x] 使用Protocol Buffer自定义消息类型 [Protobuf](Protobuf.md)
-- [x] 解析Dubbo协议 [DecodingDubboProtocol](DecodingDubboProtocol.md)
-- [] gRPC [gRPC](gRPC.md)
-- [x] Leaky Buffer [LeakyBuffer](LeakyBuffer.md)
+- [x] [Protobuf](Protobuf.md)
+- [x] [DecodingDubboProtocol](DecodingDubboProtocol.md)
+- [x] [gRPC](gRPC.md)
+- [x] [LeakyBuffer](LeakyBuffer.md)
 - [] 密码学基础
-- [x] TCP/IP [TCP/IP](TCP_IP.md)
-- [x] TLS [TLS](TLS.md)
+- [x] [TCP/IP](TCP_IP.md)
+- [x] [TLS](TLS.md)
 - [] WebSocket
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
-- [x] Docker & Golang 一对好基友 [Docker](Docker.md)
-- [x] Bloom Filter [BloomFilter](BloomFilter.md)
-- [x] Merkle Tree [MerkleTree](MerkleTree.md)
+- [x] [Docker](Docker.md)
+- [x] [BloomFilter](BloomFilter.md)
+- [x] [MerkleTree](MerkleTree.md)
 - [] goLevelDB
 - [] 零知识证明ZKP
 - [] 工作量证明POW
 - [] Skip List
 - [] 分布式哈希表DHT
-- [x] 让任务定时执行 [crontab](Crontab.md)
-- [x] 一致性哈希 [ConsistentHashing](ConsistentHashing.md)
-- [x] 使用 etcd 来进行服务注册与发现 [etcd](etcd.md)
-- [x] 使用 etcd 来实现负载均衡 [LoadBalancing](LoadBalancing.md)
-- [x] iobridge [iobridge](iobridge.md)
-- [x] 使用Golang获取手机屏幕截图 [Screenshot](Screenshot.md)
-- [x] port 0, WTF? [Port_0](Port_0.md)
+- [x] [crontab](Crontab.md)
+- [x] [ConsistentHashing](ConsistentHashing.md)
+- [x] [etcd](etcd.md)
+- [x] [LoadBalancing](LoadBalancing.md)
+- [x] [iobridge](iobridge.md)
+- [x] [Screenshot](Screenshot.md)
+- [x] [Port_0](Port_0.md)
 - [] Makefile
 - [] EDC, my Every Day Carry
 - [] Architecture of Open Source Project
-- [] git 不常用但实用命令整理 [git](git.md)
+- [] [git](git.md)
 - [] dep，golang的依赖管理工具
-- [x] 高并发爬虫 [Crawler](Crawler.md)
+- [x] [Crawler](Crawler.md)
 - [] gossip协议

From 326ad779d240aed16804fee4bb9cce764881adcd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 17:45:34 +0800
Subject: [PATCH 71/85] Create CNAME

---
 CNAME | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 CNAME

diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..e796deb
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+oceanoverflow.me
\ No newline at end of file

From f37473a7a2bd540d7ee6b7db76aff6db8ff69bb6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 17:50:03 +0800
Subject: [PATCH 72/85] Delete CNAME

---
 CNAME | 1 -
 1 file changed, 1 deletion(-)
 delete mode 100644 CNAME

diff --git a/CNAME b/CNAME
deleted file mode 100644
index e796deb..0000000
--- a/CNAME
+++ /dev/null
@@ -1 +0,0 @@
-oceanoverflow.me
\ No newline at end of file

From 9048286ba2cdce40b9ccdb54969ea36d53bbccc0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 20:46:24 +0800
Subject: [PATCH 73/85] Create dep.md

---
 dep.md | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 dep.md

diff --git a/dep.md b/dep.md
new file mode 100644
index 0000000..1e7b491
--- /dev/null
+++ b/dep.md
@@ -0,0 +1,100 @@
+# dep
+
+  在写大型工程项目的时候，难免会依赖第三方的库（自己重复造轮子肯定不现实），如果依赖的库数量非常多时，自己去手动管理肯定是不现实的，所以我们需要一个依
+赖管理工具帮助我们管理依赖的库。就像 `node` 有 `npm` ，`java` 有 `maven` 。而 `golang` 之前一直没有一个官方推荐的依赖管理工具，所以第三方的依赖
+管理工具 `glide` ，`govendor` 之类的就应运而生，但由于不是官方出品的，自己用起来心里总有些毛毛的，后来，`golang` 官方终于推出了 `dep` 这个依赖管
+理工具，终于给我们这些强迫症患者安了一个心。下面我们就以一个demo为例子，看看`dep` 如何使用吧。
+
+![godep](https://github.com/golang/dep/raw/master/docs/assets/DigbyShadows.png)
+
+## 安装
+
+  由于 `dep` 是一个命令行工具，安装起来也非常的方便，下面给出两种安装方式。
+
+#### 二进制安装
+
+  这种安装方法通用型比较强，下面的命令会根据你的操作系统直接安装相应的二进制。
+
+`$ curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh`
+
+#### macOS 安装
+
+  但是 `macOS` 用户一般还是比较习惯于使用`brew` 的，使用 `brew` 也是`no-brainer` 。
+
+```
+$ brew install dep
+$ brew upgrade dep
+```
+
+## vendor 机制
+  要了解 `dep` 存在的必要性就不得不提 `vendor` 机制了，我们都知道如果我们在 `go` 文件中导入一个非标准库，在编译时 `go` 会在 `$GOPATH` 下面去寻
+找它，但是这就导致一个问题，如果两个项目A和B同时用到了这个库，但是A和B对这个库的版本需求是不同的，这样就很难办了，毕竟在 `$GOPATH` 下面只能存在一个
+版本的库，所以为了解决这种情况，在`Go1.5`的时候，就出现 `vendor` 文件夹这个机制，也就是说，`go` 在编译项目的时候，会去这个项目目录下面寻找是否存在
+`vendor` 这个文件夹，并且 `vendor` 下面是否存在我们需要的库，如果有则使用它，如果没有则去 `$GOPATH` 下去寻找。
+
+  例如项目C依赖 `github.com/aaa/bbb` 这个库，那么 `go` 在编译时回去查看当前项目目录下有没有 `vendor/github.com/aaa/bbb` 这个文件夹目录，如
+果有则使用这个版本，如果没有再去 `$GOPATH` 下面去找。而 `dep` 就是为了帮我们管理 `vendor` 这个目录下面的依赖而存在的。
+
+## 新建项目
+  例如我们要写一个与 `WebSocket` 相关的项目，无奈 `golang` 的标准库里实现的 `WebSocket` 我们并不是非常满意，所以我们去 `github` 上面搜了一下
+，最后发现 `girilla` 家的 `websocket` 库还是比较符合我们项目的需求的，所以决定使用它。
+
+  第一步就是新建一个 `dep` 项目，使用 `dep init` 命令，会帮我们生成两个文件和一个空的 `vendor` 文件夹。
+
+```
+$ dep init
+$ ls
+Gopkg.toml Gopkg.lock vendor/
+```
+
+  然后我们写了一个 `main.go` 文件用于存放我们项目的所有逻辑（bad practice），这个文件除了标准库之外当然还导入了我们需要的 `websocket` 依赖。
+
+```golang
+import (
+    "flag"
+    "html/template"
+    "io/ioutil"
+    "log"
+    "net/http"
+    "os"
+    "strconv"
+    "time"
+    
+    "github.com/gorilla/websocket"
+)
+```
+
+  编辑完之后，在命令行中输入 `dep ensure` ，`dep` 会扫描我们当前项目中所有的依赖，并找出我们依赖的三方库，然后更新相应的文件（Gopkg.toml 和
+Gopkg.lock），并更新我们的 `vendor` 文件夹，如下所示。
+
+```
+.
+├── Gopkg.lock
+├── Gopkg.toml
+├── main.go
+└── vendor
+    └── github.com
+        └── gorilla
+            └── websocket
+                ├── AUTHORS
+                ├── LICENSE
+                ├── README.md
+                ├── client.go
+                ├── client_clone.go
+                ├── client_clone_legacy.go
+                ├── compression.go
+                ├── conn.go
+                ├── conn_read.go
+                ├── conn_read_legacy.go
+                ├── doc.go
+                ├── json.go
+                ├── mask.go
+                ├── mask_safe.go
+                ├── prepared.go
+                ├── server.go
+                └── util.go
+```
+
+## 总结
+
+  反正以后用 `dep` 作为go项目的依赖管理工具准没错的，如果你用 `glide` 或者 `govendor` 之类的还是趁早投靠 `dep` 吧。

From 775b3d32642ed7df5e0687b86aee430831dc23b0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 20:47:07 +0800
Subject: [PATCH 74/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index 68275e8..bd024f4 100644
--- a/index.md
+++ b/index.md
@@ -38,6 +38,6 @@
 - [] EDC, my Every Day Carry
 - [] Architecture of Open Source Project
 - [] [git](git.md)
-- [] dep，golang的依赖管理工具
+- [x] [dep](dep.md)
 - [x] [Crawler](Crawler.md)
 - [] gossip协议

From 542b89280f35c23ab253e6c67cd107d3f5824363 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sat, 5 May 2018 20:58:11 +0800
Subject: [PATCH 75/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index bd024f4..d3ff4a4 100644
--- a/index.md
+++ b/index.md
@@ -1,3 +1,4 @@
+- [] [AboutMe](AboutMe.md)
 - [] Golang 设计模式
 - [x] [FactoryPattern](FactoryPattern.md)
 - [x] [PKCS7_Padding](PKCS7_Padding.md)

From cceae46391b83bd55adc12980606ef8c70a59b76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sun, 6 May 2018 17:58:32 +0800
Subject: [PATCH 76/85] Create WebSocket.md

---
 WebSocket.md | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 148 insertions(+)
 create mode 100644 WebSocket.md

diff --git a/WebSocket.md b/WebSocket.md
new file mode 100644
index 0000000..0a03ce6
--- /dev/null
+++ b/WebSocket.md
@@ -0,0 +1,148 @@
+# WebSocket
+
+  如果服务器会定期地更新消息，但是具体的时间间隔并不确定，客户端为了能够及时拿取最新的消息，就得不断的发送 `HTTP` 请求来轮询服务器，也就是说为了能够
+及时获取最新资讯，由于 `HTTP` 本身的特点，服务请求的发起方只能是客户端，服务器本身并没有消息推送的功能，这样就导致了资源浪费的问题，很多客户端发送的
+请求都是无效的，当客户端和服务器一对一交流时倒还好，如果很多客户端向服务器以这样的形式咨询，服务器将会承受非常大的压力了。
+
+  所以，为了避免资源浪费，我们来引入 `WebSocket` 解决上述问题，`WebSocket` 可以说是 `HTTP` 协议的一个升级，它同样基于 `TCP` 之上，但与传输层的
+`socket` 不同，它的 `socket` 是应用层的概念。要想使用 `WebSocket` ，客户端和服务器必须同时支持才可以使用。
+
+  `WebSocket` 中比较重要的一个阶段就是握手阶段了，如下图所示，客户端向服务器发送一个协议升级的 `HTTP` 请求，并且附带上一些其他的元信息用于握手。
+
+![WebSocket](http://www.cuelogic.com/blog/wp-content/uploads/2013/11/websocketlifecycle_30624_l.png)
+
+```
+Request headers from client
+
+GET /mychat HTTP/1.1
+Host: server.example.com
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
+Sec-WebSocket-Protocol: chat
+Sec-WebSocket-Version: 13
+Origin: http://example.com
+```
+
+  服务器收到该协议升级的 `HTTP` 请求后，会返回一个 `HTTP` 答复，向客户端确认同意建立 `WebSocket` 连接，从此之后，它们之间就不再用 `HTTP` 的形式
+交流了，而是直接改用 `WebSocket` 格式的报文进行交流，服务器也具备了相应推送消息的能力了。
+
+```
+Response headers from server
+
+HTTP/1.1 101 Switching Protocols
+Upgrade: websocket
+Connection: Upgrade
+Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
+Sec-WebSocket-Protocol: chat
+```
+
+  下面我们来看一下使用 `"github.com/gorilla/websocket"` 时各个步骤对应的代码吧。
+
+  客户端代码的风格和使用 `TCP` 进行编程非常类似，第一步，客户端向服务器发送协议升级请求，对应
+`websocket.DefaultDialer.Dial(u.string(), nil)` 。
+
+```golang
+var addr = flag.String("addr", "localhost:8080", "http service addr")
+u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo"}
+log.Printf("connecting to %s", u.String())
+
+c, _, err := websocket.DefaultDialer.Dial(u.string(), nil)
+if err != nil {
+    log.Fatal("dial:", err)
+} 
+defer c.Close()
+```
+
+  在服务端处理 `WebSocket` 请求与处理 `HTTP` 请求类似，我们同样需要定义一个 `handler` ，但与普通处理 `HTTP` 请求不同的是，此 `handler` 会对
+协议进行升级，也就是 `upgrader.Upgrade(w, r, nil)` ，这段代码相当于服务端向客户端发送一个协议升级的确认消息。在这之后，服务器就可以一直读取客户
+端发来的请求和向客户端返回答复了（代码的逻辑在 `for` 循环之中），也就是说服务器只起了一个 `handler` 来处理这段 `session` 。
+
+```golang
+func echo(w http.ResponseWriter, r *http.Request) {
+    c, err := upgrader.Upgrade(w, r, nil)
+    if err != nil {
+        log.Print("upgrade:", err)
+        return
+    }
+    defer c.Close()
+    for {
+        mt, message, err := c.ReadMessage()
+        if err != nil {
+            log.Println("read:", err)
+            break
+        }
+        log.Printf("recv: %s", message)
+        err = c.WriteMessage(mt, message)
+        if err != nil {
+            log.Println("write:", err)
+            break
+        }
+    }
+}
+
+func main() {
+    http.HandleFunc("echo", echo)
+    log.Fatal(http.ListenAndServe(*addr, nil))
+}
+```
+
+  客户端在握手之后就可以读取和发送数据，在这里，由于在 `WebSocket` 中服务器可以主动向客户端推送数据，所以我们起一个 `goroutine` 专门用于接受服务
+器主动推送的数据（ `c.ReadMessage()` ）。
+
+```golang
+done := make(chan struct{})
+
+go func() {
+    defer close(done)
+    for {
+        _, message, err := c.ReadMessage()
+        if err != nil {
+            log.Println("read:", err)
+            return
+        }
+        log.Printf("recv:", message)
+    }
+}
+```
+
+  下面是客户端发送数据的逻辑，这里我们每隔一秒钟向服务器发送当前的时间，`c.WriteMessage(websocket.TextMessage, []byte(t.string()))` ，这里
+我们注意到，`WebSocket` 中的消息是有类型的，这个函数的第一个参数就是指明该消息的类型，这里我们使用 `websocket.TextMessage` 。
+
+  那么如何让这段 `session` 结束呢，结束会话一般来说都是客户端主动发起的，客户端主要发送一个结束对话的消息类型就可以了，
+`c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))` ，这里注意该结束对话的消
+息类型为 `websocket.CloseMessage` 。
+ 
+```golang
+ticker := time.NewTicker(time.Second)
+defer ticker.Stop()
+
+interrupt := make(chan os.Signal, 1)
+signal.Notify(interrupt, os.Interrupt)
+
+for {
+    select {
+    case <-done:
+        return
+    case t := <-ticker.C:
+        err := c.WriteMessage(websocket.TextMessage, []byte(t.String()))
+        if err != nil {
+            log.Println("write:", err)
+            return
+        }
+    case <-interrupt:
+        log.Println("interrupt")
+        
+        err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
+        if err != nil {
+            log.Println("write close:", err)
+            return
+        }
+        select {
+        case <-done:
+        case <-time.After(time.Second)
+        }
+        return
+    }
+}
+```

From ec8c2eba6e34fcf90cfcd40450223191aa4bf5b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sun, 6 May 2018 17:59:04 +0800
Subject: [PATCH 77/85] Update index.md

---
 index.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/index.md b/index.md
index d3ff4a4..d9eea00 100644
--- a/index.md
+++ b/index.md
@@ -17,7 +17,7 @@
 - [] 密码学基础
 - [x] [TCP/IP](TCP_IP.md)
 - [x] [TLS](TLS.md)
-- [] WebSocket
+- [x] [WebSocket](WebSocket.md)
 - [] 实现一个简单的Shadowsocks
 - [] 使用 Viper & Cobra 快速构建一个命令行工具
 - [x] [Docker](Docker.md)

From 7a815a9d6775b484a5f9daa03758b6c00199193b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sun, 6 May 2018 22:43:58 +0800
Subject: [PATCH 78/85] Create PubSub.md

---
 PubSub.md | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)
 create mode 100644 PubSub.md

diff --git a/PubSub.md b/PubSub.md
new file mode 100644
index 0000000..0e01e5e
--- /dev/null
+++ b/PubSub.md
@@ -0,0 +1,114 @@
+# PubSub
+  消息的发布和订阅这种设计模式在大型开源项目中还是会经常被使用到的，它的要点是一堆消息订阅者（ `Subscriber` ）会订阅某个自己感兴趣的 `topic` ，一
+旦发布者（ `Publisher` ）向这个 `topic` 发布消息，消息的订阅者就能收到该消息。
+
+  这种设计模式的灵感其实就来自于现实生活之中，我们（ `Subscriber` ）向我们感兴趣的杂志社订阅想要阅读的月刊，那么我们订阅的消息（算一个
+`subscription` ）就会被杂志社（ `Publisher` ）记录下来，一旦杂志可以上市发行了，杂志社就会主动向我们推送（ `Publish` ）杂志消息。
+
+![PubSub](https://docs.oracle.com/cd/E19575-01/819-3669/images/jms-publishSubscribe.gif)
+
+  下面我们来实现一个简单的发布者订阅者模型吧。
+
+  由于这个模型的核心就在于很多 `subscriber` 会向某一个 `topic` 订阅消息，所以需要一个结构体来记录特定的 `topic` 对应的所有 `subscriber` ，这
+里 `subscriber` 用一个集合（ `Set` ）来表示。
+
+```golang
+type PubSub struct {
+    sync.RWMutex
+    subscriptions map[string]*Set
+}
+
+type Set struct {
+    items map[interface{}]struct{}
+    lock *sync.RWMutex
+}
+
+func NewPubSub() *PubSub {
+    return &PubSub{
+        subscriptions: make(map[string]*Set),
+    }
+}
+```
+
+  `Subscription` 应该具有监听行为，通过 `Listen` 来获取 `Publisher` 发布的消息。而结构体 `subscription` 则相当于一个订阅的注册信息。
+
+```golang
+type Subscription interface {
+    Listen() (interface{}, error)
+}
+
+type subscription struct {
+    topic string
+    ttl   time.Duration
+    c     chan interface{}    
+}
+
+type (s *subscription) Listen() (interface{}, error) {
+    select {
+    case <-time.After(s.ttl):
+        return nil, errors.New("timed out")
+    case iterm := <-s.c:
+        return item, nil
+    }
+}
+```
+
+  其实订阅和取消订阅的本质就是将自己从订阅名单（ `subscriptions map[string]*Set` ）里面进行增加或删除的操作，因为我们向杂志社订阅了某本杂志，那
+么关于我们的订阅信息一定会在杂志出版商那里存在。
+
+```golang
+func (ps *PubSub) Subscribe(topic string, ttl time.Duration) Subscription {
+    sub := &subscription{
+        topic: topic.
+        ttl:   ttl,
+        c:     make(chan interface{}, subscriptionBuffSize),
+    }
+    ps.Lock()
+    s, exists := ps.subscriptions[topic]
+    if !exists {
+        s = NewSet()
+        ps.subscriptions[topic] = s
+    }
+    ps.Unlock()
+
+    s.Add(sub)
+    time.AfterFunc(ttl, func() {
+        ps.unSubscribe(sub)
+    })
+    return
+}
+
+func (ps *PubSub) unSubscribe(sub *subscription) {
+    ps.Lock()
+    defer ps.Unlock()
+    ps.subscriptions[sub.topic].Remove(sub)
+    if ps.subscriptions[sub.topic].Size() != 0 {
+        return
+    }
+    delete(ps.subscriptions, sub.topic)
+}
+```
+
+  最后我们订阅的杂志终于要发布了，杂志的出版社会根据自己手中的订阅名单，逐一向订阅该 `topic` 的订阅者发送杂志，当然只有订阅了的人才能收到。但是由于
+订阅者可能出去旅游了，也就是说他最近并没有调用 `Listen` 将收到的订阅消息拿出，而我们接受订阅消息的邮箱大小是有上限的（ `c: make(chan interface
+{}, subscriptionBuffSize)` ，我们设置了 `subscriptionBuffSize` 来表示一个通道中最多可以存放的消息数量），如果该邮箱内已有的消息超出这个限制，
+则选择不将消息放入邮箱中，否则则将消息放入这个邮箱中。
+
+```golang
+func (ps *PubSub) Publish(topic string, item interface{}) error {
+    ps.RLock()
+    defer ps.RUnlock()
+    s, subscribed := ps.subscriptions[topic]
+    if !subscribed {
+        return errors.New("no subscribers")
+    }
+    for _, sub := range s.ToArray() {
+        c := sub.(*subscription).c
+        if len(c) == subscriptionBuffSize {
+            continue
+        }
+        c <- item
+    }
+    return nil
+}
+```

From 1bc30bd0d6dd2ac58671fb818a705f716b04eb33 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Sun, 6 May 2018 22:44:44 +0800
Subject: [PATCH 79/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index d9eea00..8b84eda 100644
--- a/index.md
+++ b/index.md
@@ -1,6 +1,7 @@
 - [] [AboutMe](AboutMe.md)
 - [] Golang 设计模式
 - [x] [FactoryPattern](FactoryPattern.md)
+- [x] [Pub/Sub](PubSub.md)
 - [x] [PKCS7_Padding](PKCS7_Padding.md)
 - [] PBFT共识算法
 - [] Paxos共识算法

From b47e38a91e929e9a49912e80bb5dd0cdd841f1fe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Mon, 7 May 2018 16:28:21 +0800
Subject: [PATCH 80/85] Create MagnetLink.md

---
 MagnetLink.md | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)
 create mode 100644 MagnetLink.md

diff --git a/MagnetLink.md b/MagnetLink.md
new file mode 100644
index 0000000..b06bc07
--- /dev/null
+++ b/MagnetLink.md
@@ -0,0 +1,95 @@
+# Magnet Link
+  
+  相信大部分人都使用过磁力链接来下载过网上的文件吧，但是估计很少人会去关心那稀奇古怪的磁力链接究竟是什么鬼，今天就让我们来解析一下磁力链接，看一下它里
+面隐藏的信息吧。
+
+  其实类似下面这种看似复杂的链接地址也就是由几个部分组成，开头的 `magnet` 我们把它称为 `scheme` 或者说是协议名，就像一般的 `http` 地址一样，
+`http` 也算是一种协议名，紧接着 `magnet:?` 后面就是几对键值对了，一般键值对之间以 `&` 符号划分，常见的键有：
+
+```
+magnet:?xt=urn:btih:0FF55426B9713084FF5229A16D21712B1E6C1625&dn=%e5%8d%97%e5%be%81%e5%8c%97%e6%88%98%20-%206415&tr=http%3a%2f%2ftracker.nexushd.org%2fannounce.php%3fpasskey%3d346ae8170f8427b078512150f3683285
+```
+
+* `xt` ：eXact Topic 的缩写，其实本质上就是一个文件的哈希值，用于唯一标示这个文件而存在的，这是整个磁力链接中不可或缺的一部分，少了它，基本上链接地址就无法解析了。
+* `dn` ：Display Name 的缩写，这个是用于向用户展示的文件的名称，这一项可以没有。
+* `tr` ： Tracker 的缩写，表示Tracker服务器的地址，取决于使用的技术，这一项也是选填的。
+* `ws` ： Web Seed 的缩写，表示网络种子，选填。
+* `urn` ：Uniform Resource Name 的缩写，也就是统一资源名称。
+* `btih` ： BitTorrent Info Hash 的缩写，表示种子的散列函数。
+
+知道了磁力链接地址中的关键信息，也就基本可以看懂链接地址中的各个部分分别代表什么含义了，但是由于其中一些信息还是经过特殊编码的，人类读起来还是比较费劲
+的，所以我们还是实现代码来解析它吧。
+
+  首先，来定义一个结构体来定义一下磁力链接中的组成部分，这里只选了几个重要的，`InfoHash` 代表的就是 `"urn:btih:"` 后面那一部分玩意儿，
+`Trackers` 数组代表的就是 `Tracker` 服务器的地址，`DisplayName` 就对应磁力链接中的 `dn` 。
+
+```golang
+type Magnet struct {
+    InfoHash Hash
+    Trackers []string
+    DisplayName string
+}
+
+const xtPrefix = "urn:btih:"
+
+func (m Magnet) String() string {
+    ret : = "magnet:?xt="
+    ret += xtPrefix + hex.EncodeToString(m.InfoHash[:])
+    if m.DisplayName != "" {
+        ret += "&dn=" + url.QueryEscape(m.DisplayName)
+    }
+    for _, tr := range m.Trackers {
+        ret += "&tr=" + url.QueryEscape(tr)
+    }
+    return ret
+}
+```
+
+  解析磁力链接的过程和解析一般的 `url` 的过程非常类似，关键的部分是哈希值部分的解析，由于 `BTIH` 这个部分的哈希值可能用 `Hex` 编码也可能用
+`Base32` 编码，所以我们要根据其哈希长度选择解码的方式，这里定义了一个函数变量 `var decode func(dst, src []byte) (int, error)` 用于接受我们
+具体的解码函数。
+
+```golang
+func ParseMagnetURI(uri string) (m Magnet, err eror) {
+    u, err := url.Parse(uri)
+    if err != nil {
+        err = fmt.Errorf("error parsing uri : %s", err) 
+        return
+    }
+    if u.Scheme != "magnet" {
+        err = fmt.Errorf("unexpected scheme: %q", u.Scheme)
+        return
+    }
+    xt := u.Query().Get("xt")
+    if !strings.HasPrefix(xtPrefix) {
+        err = fmt.Errorf("bad xt parameter")
+        return
+    }
+    
+    infoHash := xt[len(xtPrefix):]
+    
+    var decode func(dst, src []byte) (int, error)
+    switch len(infoHash) {
+    case 40:
+        decode = hex.Decode
+    case 32:
+        decode = base32.StdEncoding.Decode
+    }
+
+    if decode == nil {
+        err = fmt.Errorf("unhandled xt parameter encoding: encoded lenght", len(infoHash))
+        return
+    }
+    n, err := decode(m.InfoHash[:], []byte(infoHash))
+    if err != nil {
+        err = fmt.Errorf("error decoding xt: %s", err)
+        return
+    }
+    if n != 20 {
+        panic(n)
+    }
+    m.DisplayName = u.Query().Get("dn")
+    m.Trackers = u.Query()["tr"]
+    return
+}
+```

From c06b4ed2719c4fddab5e655f0a218d305d1eb2a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Mon, 7 May 2018 16:29:05 +0800
Subject: [PATCH 81/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 8b84eda..5d64818 100644
--- a/index.md
+++ b/index.md
@@ -43,3 +43,4 @@
 - [x] [dep](dep.md)
 - [x] [Crawler](Crawler.md)
 - [] gossip协议
+- [x] [MagnetLink](MagnetLink.md)

From aaf5e869c9394fa28fa903c9fcf6ec3d34f543cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Tue, 8 May 2018 17:11:25 +0800
Subject: [PATCH 82/85] Create SmoothWeightedRoundRobin.md

---
 SmoothWeightedRoundRobin.md | 135 ++++++++++++++++++++++++++++++++++++
 1 file changed, 135 insertions(+)
 create mode 100644 SmoothWeightedRoundRobin.md

diff --git a/SmoothWeightedRoundRobin.md b/SmoothWeightedRoundRobin.md
new file mode 100644
index 0000000..a8d7ba8
--- /dev/null
+++ b/SmoothWeightedRoundRobin.md
@@ -0,0 +1,135 @@
+# Smooth Weighted Round Robin
+
+  我们在之前的文章中就讲过负载均衡，但是那篇文章中的例子比较简单，仅仅是利用随机算法来进行负载均衡，并没有考虑服务器的实际情况，在很多情况下，服务器的性
+能可能存在显著差异，这时候如果采用随机算法，那么性能较差的服务器会承受过多的压力，而性能较好的服务器则大部分时间处于空闲状态，系统整体的吞吐量就无法上来。
+
+  例如服务器的性能比例如下：`{a:3, b: 2, c:1}` ，一般来说可以使用加权的轮询算法（ `Weighted Round Robin` ）进行负载均衡，但是普通的加权轮询会得
+出类似这样的序列， `a a a b b c` ，这样的序列有一个问题，就是它并不够平滑，我们更希望得到像这样的序列 `a b a c b a` 。也就是说一般的轮询调度在调度
+的过程中并没有考虑瞬时的情况，所以会导致对某一台服务器的瞬时访问。
+
+  下面我们就来介绍一下平滑版本的加权轮询算法，来解决上述瞬时访问可能出现的问题。我们对每个节点赋予两个权重， `Weight` ，这是一个较为固定的值，就是前面
+给出的 `{a:3, b: 2, c:1}` ，还需要一个 `CurrentWeight` 来记录当前的权重，初始时均为0，这个权重是在每次选择节点时动态变化的。
+
+  例如现在有三台服务器，其权重比值为 `{a:3, b: 2, c:1}` ，刚开始时它们的 `CurrentWeight` 均为0，按照下面的算法，每一回合中各个节点的
+`CurrentWeight` 都会加上它相应的的 `Weight` ，`total` 变量则用于记录每一个节点增加的 `Weight` 的总和，并记录当前权重最高的那一个节点，也就是我们的 `best` ，在一轮计算之后，再让 `best` 节点的 `CurrentWeight` 减去 `total` 值。
+
+  也就是说开始时每台服务器的 `CurrentWeight` 为 `{a:0, b: 0, c:0}` ，在选择节点的过程中，每个节点均会加上自己对应的 `Weight` ，这样就变成
+`{a:3, b: 2, c:1}` ， 此时 `best=a` ， 最后 a 对应的 `CurrentWeight` 要减去 `total` ，也就是 `{a:-3, b: 2, c:1}`  。
+
+  然后 `{a:-3, b: 2, c:1}` 作为下一轮挑选节点时的初始状态，以此类推，最后可以得到下面这样的序列  `a b a c b a` ，这样就不会导致对服务器 `a` 集中
+访问的情况，也就是说访问序列更加平滑了。
+
+  整个周期计算的流程如下所示。
+
+```
+a   b   c
+0   0   0
+
+3   2   1   Next() : a
+-3  2   1
+
+0   4   2   Next() : b
+0  -2   2
+
+3   0   3   Next() : a
+-3  0   3
+
+0   2   4   Next() : c
+0   2   -2 
+
+3   4   -1  Next() : b
+3  -2   -1
+
+6   0   0   Next() : a
+0   0   0
+
+```
+
+## 普通版本平滑轮询调度算法
+
+```golang
+type Weighted struct {
+    Server string
+    Weight int
+    CurrentWeight int
+}
+```
+
+  普通版本的算法，每次选择结点的时候，我们为每个候选结点的 `CurrentWeight` 增加 `Weight` ，选取 `CurrentWeight` 最大的节点作为我们最后返回的结果
+，然后将该节点减去所有节点增加的 `Weight` 之和。 
+
+```golang
+func Next(servers []*Weighted) (best *Weight) {
+    total := 0 
+    for i := 0; i < len(servers); i++ {
+        w := servers[i]
+        
+        if w == nil {
+            continue
+        }
+
+        w.CurrentWeight += w.Weight
+        total += w.CurrentWeight 
+        if best == nil || w.CurrentWeight > best.CurrentWeight {
+            best = w
+        }
+    }
+    if best == nil {
+        return nil
+    }
+    best.CurrentWeight -= total
+    return best
+} 
+```
+
+## 考虑节点失效情况时的算法
+
+  上面那种算法不能很好的应对节点失效的情况，为了解决节点失效时的这种突发情况，我们多引入一个权重也就是 `EffectiveWeight` ，该权重初始时与 `Weight`
+的值相同，例如上述的例子中都是  `{a:3, b: 2, c:1}` ，但是 `EffectiveWeight` 会在节点失败的时候清0，当节点恢复正常时再慢慢递增直到达到原本的
+`Weight` ，这样就能很好地考虑了节点失效再重启时的真实情况。因为引入了 `EffectiveWeight` ， 我们在相应计算的时候就不直接采用 `Weight` 而是直接使用
+`EffectiveWeight` 来代替计算。 
+
+```golang
+type Weighted struct {
+    Server string
+    Weight int
+    CurrentWeight int
+    EffectiveWeight int
+}
+```
+
+```golang
+func (w *Weighted) fail() {
+    w.EffetiveWeight -= w.Weight
+    if w.EffectiveWeight < 0 {
+        w.EffectiveWeight = 0
+    }
+}
+```
+
+```golang
+func Next(servers []*Weighted) (best *Weight) {
+    total := 0
+    for i := 0; i < len(servers); i++ {
+        w := servers[i]
+
+        if w == nil {
+            continue
+        }
+
+        w.CurrentWeight += w.EffectiveWeight
+        total += w.EffectiveWeight
+        if w.EffectiveWeight < w.Weight {
+            w.EffectiveWeight++
+        }
+        if best == nil || w.CurrentWeight > best.CurrentWeight {
+            best = w
+        }
+    }
+    if best == nil {
+        return nil
+    }
+    best.CurrentWeight -= total
+    return best
+}
+```

From 05dda5910b9c7c104123b7707654c7d1a189455f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Tue, 8 May 2018 17:13:15 +0800
Subject: [PATCH 83/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 5d64818..6183802 100644
--- a/index.md
+++ b/index.md
@@ -33,6 +33,7 @@
 - [x] [ConsistentHashing](ConsistentHashing.md)
 - [x] [etcd](etcd.md)
 - [x] [LoadBalancing](LoadBalancing.md)
+- [x] [SmoothWeightedRoundRobin](SmoothWeightedRoundRobin.md)
 - [x] [iobridge](iobridge.md)
 - [x] [Screenshot](Screenshot.md)
 - [x] [Port_0](Port_0.md)

From 240cdcb97848bc3859cbdb65032a90da28f40ff9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Wed, 9 May 2018 17:02:45 +0800
Subject: [PATCH 84/85] Create CircuitBreaker.md

---
 CircuitBreaker.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 109 insertions(+)
 create mode 100644 CircuitBreaker.md

diff --git a/CircuitBreaker.md b/CircuitBreaker.md
new file mode 100644
index 0000000..5609171
--- /dev/null
+++ b/CircuitBreaker.md
@@ -0,0 +1,109 @@
+# CircuitBreaker
+
+  我们都知道，如果家中因为启动过多大功率电器而导致电流过大，断路开关就会断开以保护这些电路和电器。在分布式系统中，集群中的某些服务器出现异常还是一件非常
+正常的事情，如果客户端向这些异常的服务器进行 `RPC` 请求的话，这些服务器会因为异常而无法向客户端返回远程方法调用的结果，因为一般来说 `RPC` 是阻塞的，客
+户端会像执行本地方法一样调用 `RPC` ，也就是说客户端会阻塞在这里无法执行 直到该方法本地超时，但由于为了得到正确的结果，客户端可能会反复多次调用该 `RPC`
+，由于在这段时间内远程服务器都可能无法恢复正常，所以就会导致客户端一直调用该方法，但却无法得到结果，这样就会导致资源的浪费。
+
+  为了避免上述情况的发生，我们可以在程序设计中借鉴断路开关的模式，也就是允许被保护的函数在规定上限内执行，如果一旦出现错误的次数超过上限，则下次函数再次
+调用时则直接不让其执行（也就是断路开关直接断开），从而避免系统资源的浪费。但是上述方法存在一个问题，就是一旦总的错误次数超过上限，是不是就说明即使现在远
+程服务器恢复正常，客户端也无法调用了呢。
+
+  解决上述问题也很简单，通过模仿网络中很常见的 `Sliding Window` ，也就是移动窗口，允许在一定时间窗口内出现特定上限个错误，如果在该时间窗口内出现错误
+的次数超过上限，则不再允许该函数执行，直到下一个时间窗口的到来才允许它重新执行。
+
+  下面来设计一个滑动窗口版本的断路开关，我们通过判断一个时间窗口 `window` 内出现的错误次数 `failures` 是否超过 `failureThreshold` 来决定断路开关
+是否应该断开（也就是不让函数继续执行）。
+
+```golang
+type CircuitBreaker struct {
+    lastFailureTime  time.Time
+    failures         uint64
+    failureThreshold uint64
+    window           time.Duration
+}
+
+func NewCircuitBreaker(failureThreshold uint64, window time.Duration) {
+    return &CircuitBreaker{
+        failureThreshold: failureThreshold,
+        window:           window,
+    }
+}
+```
+
+  由于是滑动窗口，我们需要观察在现在的窗口出现错误的次数是否超过我们规定的次数，如果超过，则表示该函数还没有 `ready` ，也就是还不能执行，如果该窗口已经
+过去，那么我们重置（ `reset` ）断路开关中记录的出错次数，以让函数可以重新执行。
+
+```golang
+func (cb *CircuitBreaker) ready() bool {
+    if time.Since(cb.lastFailureTIme) > cb.window {
+        cb.reset()
+        return
+    }
+    failures := atomic.LoadUint64(&cb.failures)
+    return failures < cb.failureThreshold
+}
+
+func (cb *CircuitBreaker) success() {
+    cb.reset()
+}
+
+func (cb *CircuitBreaker) fail() {
+    atomic.AddUint64(&cb.failures, 1)
+    cb.lastFailureTime = time.Now()
+}
+
+func (cb *CircuitBreaker) reset() {
+    atomic.StoreUint64(&cb.failures, 0)
+    cb.lastFailureTime = time.Now()
+}
+```
+
+  也就是说，在 `CircuitBreaker` 保护下，客户端准备调用函数时，会预先检察其是否具备资格，也就是说过去一个时间窗口没有出现超过上限个错误，如果不具备，
+就报 `ErrBreakerOpen` 错误，也就是对应下图中的  `circuit open` ，因为断路开关断开了，自然没有办法继续执行。
+
+  如果具备，则可以让受保护的函数继续执行，但是钥匙它在规定的时间内还没有返回结果，说明出现超时情况，则报 `ErrBreakerTimeout` 错误，并增加错误次数直
+到其达到上限为止。
+
+![Circuit Breaker](https://martinfowler.com/bliki/images/circuitBreaker/sketch.png)
+
+```golang
+var (
+    ErrBreakerOpen = errors.New("breaker open")
+    ErrBreakerTimeout = errors.New("breaker time out")
+)
+
+
+func (cb *CircuitBreaker) Call(fn func() error, d time.Duration) error {
+    var err error
+    if !cb.ready() {
+        return 
+    }
+
+    if d == 0 {
+        err = fn()
+    } else {
+        c := make(chan error, 1)
+        go func() {
+            c <- fn()
+            close(c)
+        }()
+
+        t := time.NewTicker(d)
+        select {
+        case e := <-c:
+            err = e
+        case <-t.C:
+            err = ErrBreakerTimeout
+        }
+        t.Stop()
+    }
+
+    if err == nil {
+        cb.success()
+    } else {
+        cb.fail()
+    } 
+    return err
+}
+```

From d6eec17045015b60cdd5b263d50dfe6f6225831b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E6=98=93=E6=B4=8B=E6=BA=A2?= <oceanoverflow@gmail.com>
Date: Wed, 9 May 2018 17:03:57 +0800
Subject: [PATCH 85/85] Update index.md

---
 index.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/index.md b/index.md
index 6183802..f76081a 100644
--- a/index.md
+++ b/index.md
@@ -1,5 +1,6 @@
 - [] [AboutMe](AboutMe.md)
 - [] Golang 设计模式
+- [x] [CircuitBreaker](CircuitBreaker.md)
 - [x] [FactoryPattern](FactoryPattern.md)
 - [x] [Pub/Sub](PubSub.md)
 - [x] [PKCS7_Padding](PKCS7_Padding.md)
