Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/xiaoheiAh/hugo-theme-pure.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorotis <xiaohei.zyx@gmail.com>2019-11-18 12:39:36 +0300
committerotis <xiaohei.zyx@gmail.com>2019-11-18 12:39:36 +0300
commit5a08f9d501e0cfbd60d873ec1375867c5bbd451f (patch)
tree7ca29b920a21dac8b58d269e1d9d17f7756bb4c0
parentcc1c8b66878ea4ceeff0838db078423124327b5a (diff)
update gh-pages
-rw-r--r--2019/08/001-two-sum/index.html26
-rw-r--r--2019/08/026-remove-duplicates-from-sorted-array/index.html24
-rw-r--r--2019/08/189-rotate-array/index.html24
-rw-r--r--2019/08/rust/index.html24
-rw-r--r--2019/08/rxjava-guide/index.html24
-rw-r--r--2019/08/rxjava-in-hystrix/index.html24
-rw-r--r--2019/09/amqp-0-9-1-model-explained/index.html24
-rw-r--r--2019/09/hugo-theme-dev-note/index.html24
-rw-r--r--2019/09/rabbitmq-guide-and-ha-cluster/index.html24
-rw-r--r--2019/09/rabbitmq-msg-distribution/index.html24
-rw-r--r--2019/10/data-structure/index.html36
-rw-r--r--2019/10/rabbitmq-ack-confirm/index.html26
-rw-r--r--2019/11/aof/index.html242
-rw-r--r--2019/11/db/index.html24
-rw-r--r--2019/11/distributed-lock/index.html26
-rw-r--r--2019/11/event/index.html763
-rw-r--r--2019/11/obj/index.html24
-rw-r--r--2019/11/rdb/index.html31
-rw-r--r--2019/11/replication/index.html712
-rw-r--r--about/index.html45
-rw-r--r--categories/corejava/index.html38
-rw-r--r--categories/corejava/index.xml28
-rw-r--r--categories/hystrix/index.html24
-rw-r--r--categories/index.html59
-rw-r--r--categories/index.xml4
-rw-r--r--categories/leetcode/index.html24
-rw-r--r--categories/redis/index.html64
-rw-r--r--categories/redis/index.xml33
-rw-r--r--categories/消息队列/index.html24
-rw-r--r--collections/arraylist/index.html631
-rw-r--r--collections/hashmap/index.html1210
-rw-r--r--collections/hashset/index.html568
-rw-r--r--collections/index.html406
-rw-r--r--collections/index.xml77
-rw-r--r--collections/linkedhashmap/index.html648
-rw-r--r--collections/linkedlist/index.html880
-rw-r--r--collections/treemap/index.html705
-rw-r--r--collections/treeset/index.html614
-rw-r--r--index.html363
-rw-r--r--index.xml15
-rw-r--r--posts/index.html99
-rw-r--r--posts/index.xml96
-rw-r--r--searchindex.json2
-rw-r--r--sitemap.xml64
-rw-r--r--tags/collections/index.html38
-rw-r--r--tags/collections/index.xml28
-rw-r--r--tags/hugo/index.html24
-rw-r--r--tags/hystrix/index.html24
-rw-r--r--tags/index.html97
-rw-r--r--tags/index.xml4
-rw-r--r--tags/leetcode/index.html24
-rw-r--r--tags/rabbitmq/index.html24
-rw-r--r--tags/redis/index.html67
-rw-r--r--tags/redis/index.xml33
-rw-r--r--tags/rust/index.html24
-rw-r--r--tags/rxjava/index.html24
-rw-r--r--tags/分布式锁/index.html24
-rw-r--r--tags/响应式编程/index.html24
-rw-r--r--tags/数据结构/index.html26
-rw-r--r--tags/数据结构/index.xml2
60 files changed, 8396 insertions, 938 deletions
diff --git a/2019/08/001-two-sum/index.html b/2019/08/001-two-sum/index.html
index f010267..5c2e1ef 100644
--- a/2019/08/001-two-sum/index.html
+++ b/2019/08/001-two-sum/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -373,7 +373,7 @@
<div class="bar-inner">
<ul class="pager pull-left">
<li class="prev">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/" title="Collections-Arraylist"><i
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/arraylist/" title="Collections-Arraylist"><i
class="icon icon-angle-left"
aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
</li>
diff --git a/2019/08/026-remove-duplicates-from-sorted-array/index.html b/2019/08/026-remove-duplicates-from-sorted-array/index.html
index a7bbd88..9a7683e 100644
--- a/2019/08/026-remove-duplicates-from-sorted-array/index.html
+++ b/2019/08/026-remove-duplicates-from-sorted-array/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/08/189-rotate-array/index.html b/2019/08/189-rotate-array/index.html
index 1a89b7b..73afa38 100644
--- a/2019/08/189-rotate-array/index.html
+++ b/2019/08/189-rotate-array/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/08/rust/index.html b/2019/08/rust/index.html
index 645a940..b79ba61 100644
--- a/2019/08/rust/index.html
+++ b/2019/08/rust/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/08/rxjava-guide/index.html b/2019/08/rxjava-guide/index.html
index bf6c92b..8d151d8 100644
--- a/2019/08/rxjava-guide/index.html
+++ b/2019/08/rxjava-guide/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/08/rxjava-in-hystrix/index.html b/2019/08/rxjava-in-hystrix/index.html
index 1bf704a..83c4ad1 100644
--- a/2019/08/rxjava-in-hystrix/index.html
+++ b/2019/08/rxjava-in-hystrix/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/09/amqp-0-9-1-model-explained/index.html b/2019/09/amqp-0-9-1-model-explained/index.html
index 4ef9cbb..396d68d 100644
--- a/2019/09/amqp-0-9-1-model-explained/index.html
+++ b/2019/09/amqp-0-9-1-model-explained/index.html
@@ -161,7 +161,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -193,7 +193,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -227,50 +227,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/09/hugo-theme-dev-note/index.html b/2019/09/hugo-theme-dev-note/index.html
index 7f0bae3..c9c73eb 100644
--- a/2019/09/hugo-theme-dev-note/index.html
+++ b/2019/09/hugo-theme-dev-note/index.html
@@ -161,7 +161,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -193,7 +193,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -227,50 +227,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/09/rabbitmq-guide-and-ha-cluster/index.html b/2019/09/rabbitmq-guide-and-ha-cluster/index.html
index 2b335e0..7cb4a3a 100644
--- a/2019/09/rabbitmq-guide-and-ha-cluster/index.html
+++ b/2019/09/rabbitmq-guide-and-ha-cluster/index.html
@@ -161,7 +161,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -193,7 +193,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -227,50 +227,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/09/rabbitmq-msg-distribution/index.html b/2019/09/rabbitmq-msg-distribution/index.html
index 1eceb03..fa2bd02 100644
--- a/2019/09/rabbitmq-msg-distribution/index.html
+++ b/2019/09/rabbitmq-msg-distribution/index.html
@@ -161,7 +161,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -193,7 +193,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -227,50 +227,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/10/data-structure/index.html b/2019/10/data-structure/index.html
index 066106a..35cbcbe 100644
--- a/2019/10/data-structure/index.html
+++ b/2019/10/data-structure/index.html
@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>
- Redis - 数据结构 - 赵小黑的博客
+ Redis-数据结构 - 赵小黑的博客
</title>
<head>
<meta charset="utf-8">
@@ -26,19 +26,19 @@
<meta name="description" content="系统学习 redis 相关的知识,从数据结构开始~
" />
<meta name="generator" content="Hugo 0.58.0 with theme pure" />
- <title>Redis - 数据结构 - 赵小黑的博客</title>
+ <title>Redis-数据结构 - 赵小黑的博客</title>
<link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
<link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
- <meta property="og:title" content="Redis - 数据结构" />
+ <meta property="og:title" content="Redis-数据结构" />
<meta property="og:description" content="系统学习 redis 相关的知识,从数据结构开始~" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" />
<meta property="article:published_time" content="2019-10-24T09:59:11+08:00" />
<meta property="article:modified_time" content="2019-10-24T09:59:11+08:00" />
-<meta itemprop="name" content="Redis - 数据结构">
+<meta itemprop="name" content="Redis-数据结构">
<meta itemprop="description" content="系统学习 redis 相关的知识,从数据结构开始~">
@@ -50,7 +50,7 @@
<meta itemprop="keywords" content="redis,数据结构," />
<meta name="twitter:card" content="summary"/>
-<meta name="twitter:title" content="Redis - 数据结构"/>
+<meta name="twitter:title" content="Redis-数据结构"/>
<meta name="twitter:description" content="系统学习 redis 相关的知识,从数据结构开始~"/>
<!--[if lte IE 9]>
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -369,7 +369,7 @@
<a
class="article-title"
href="/hugo-theme-pure/2019/10/data-structure/"
- >Redis - 数据结构</a
+ >Redis-数据结构</a
>
</h1>
diff --git a/2019/10/rabbitmq-ack-confirm/index.html b/2019/10/rabbitmq-ack-confirm/index.html
index 35458a8..abe3a51 100644
--- a/2019/10/rabbitmq-ack-confirm/index.html
+++ b/2019/10/rabbitmq-ack-confirm/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -456,7 +456,7 @@
</li>
<li class="next">
<a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/"
- title="Redis - 数据结构"><span>下一篇&nbsp;&nbsp;</span><i
+ title="Redis-数据结构"><span>下一篇&nbsp;&nbsp;</span><i
class="icon icon-angle-right" aria-hidden="true"></i></a>
</li>
diff --git a/2019/11/aof/index.html b/2019/11/aof/index.html
index 8ef32ba..0c55268 100644
--- a/2019/11/aof/index.html
+++ b/2019/11/aof/index.html
@@ -4,7 +4,7 @@
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>
- - 赵小黑的博客
+ Redis-AOF持久化 - 赵小黑的博客
</title>
<head>
<meta charset="utf-8">
@@ -23,34 +23,35 @@
<meta http-equiv="window-target" content="_top" />
- <meta name="description" content="" />
+ <meta name="description" content="RDB 和 AOF 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 写命令 保证数据库的状态.
+" />
<meta name="generator" content="Hugo 0.58.0 with theme pure" />
- <title> - 赵小黑的博客</title>
+ <title>Redis-AOF持久化 - 赵小黑的博客</title>
<link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
<link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
- <meta property="og:title" content="" />
-<meta property="og:description" content="" />
+ <meta property="og:title" content="Redis-AOF持久化" />
+<meta property="og:description" content="RDB 和 AOF 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 写命令 保证数据库的状态." />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" />
<meta property="article:published_time" content="2019-11-08T15:18:05+08:00" />
<meta property="article:modified_time" content="2019-11-08T15:18:05+08:00" />
-<meta itemprop="name" content="">
-<meta itemprop="description" content="">
+<meta itemprop="name" content="Redis-AOF持久化">
+<meta itemprop="description" content="RDB 和 AOF 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 写命令 保证数据库的状态.">
<meta itemprop="datePublished" content="2019-11-08T15:18:05&#43;08:00" />
<meta itemprop="dateModified" content="2019-11-08T15:18:05&#43;08:00" />
-<meta itemprop="wordCount" content="0">
+<meta itemprop="wordCount" content="1461">
-<meta itemprop="keywords" content="" />
+<meta itemprop="keywords" content="redis," />
<meta name="twitter:card" content="summary"/>
-<meta name="twitter:title" content=""/>
-<meta name="twitter:description" content=""/>
+<meta name="twitter:title" content="Redis-AOF持久化"/>
+<meta name="twitter:description" content="RDB 和 AOF 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 写命令 保证数据库的状态."/>
<!--[if lte IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
@@ -161,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -193,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -220,6 +221,63 @@
</div>
</div>
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
</div>
</aside>
@@ -229,7 +287,33 @@
<div class="slimContent">
<nav id="toc" class="article-toc">
<h3 class="toc-title">文章目录</h3>
- <div class="toc-content always-active">
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#什么是-aof">什么是 AOF ?</a>
+<ul>
+<li><a href="#开启-aof">开启 AOF</a></li>
+<li><a href="#aof-文件格式">AOF 文件格式</a></li>
+<li><a href="#aof-持久化过程">AOF 持久化过程</a>
+<ul>
+<li><a href="#命令追加-append">命令追加 append</a></li>
+<li><a href="#aof-写入同步">AOF 写入同步</a>
+<ul>
+<li><a href="#appendfsync">appendfsync</a></li>
+</ul></li>
+<li><a href="#aof-还原数据">AOF 还原数据</a></li>
+</ul></li>
+<li><a href="#aof-重写">AOF 重写</a>
+<ul>
+<li><a href="#触发条件">触发条件</a></li>
+<li><a href="#重写过程">重写过程</a></li>
+</ul></li>
+</ul></li>
+<li><a href="#参考">参考</a></li>
+</ul></li>
+</ul>
+</nav>
</div>
</nav>
</div>
@@ -243,7 +327,7 @@
<a
class="article-title"
href="/hugo-theme-pure/2019/11/aof/"
- ></a
+ >Redis-AOF持久化</a
>
</h1>
@@ -253,7 +337,14 @@
<a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="article-date">
<time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</a>
-</span>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/redis/"> redis </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/redis/"> redis </a>
+ </span>
<span class="article-read hidden-xs">
<i class="icon icon-eye-fill" aria-hidden="true"></i>
@@ -263,12 +354,118 @@
</span>
<span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/11/aof/#comments"
class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:0字</span>
- <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:0分 </span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:1461字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:3分 </span>
</div>
</div>
<div class="article-entry marked-body" itemprop="articleBody">
-
+ <p><code>RDB</code> 和 <code>AOF</code> 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 <strong>写命令</strong> 保证数据库的状态.</p>
+
+<h2 id="什么是-aof">什么是 AOF ?</h2>
+
+<p><code>AOF</code> 持久化通过保存服务器执行的写命令实现,进行恢复时通过重放 <code>AOF</code> 文件中的写命令,来保证数据安全.就像 <code>mysql</code> 的 <code>binlog</code> 一样.</p>
+
+<h3 id="开启-aof">开启 AOF</h3>
+
+<p>通过在 <code>redis.conf</code> 中将 <code>appendonly</code> 设为 <code>yes</code> 即可</p>
+
+<pre><code class="language-bash"># redis.conf
+appendonly yes
+# 设置 aof 文件名字
+appendfilename &quot;appendonly.aof&quot;
+# Redis支持三种不同的刷写模式:
+# appendfsync always #每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用。
+appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。
+# appendfsync no #完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐。
+</code></pre>
+
+<h3 id="aof-文件格式">AOF 文件格式</h3>
+
+<p><code>AOF</code> 文件格式以 <code>redis</code> 命令请求协议为标准的,<code>*.aof</code> 文件可以直接打开.</p>
+
+<p><img src="https://raw.githubusercontent.com/xiaoheiAh/imgs/master/20191112184639.png" alt="redis设计与实现-aof格式" /></p>
+
+<h3 id="aof-持久化过程">AOF 持久化过程</h3>
+
+<h4 id="命令追加-append">命令追加 append</h4>
+
+<p><code>redis</code> 执行完客户端的写命令后,会将该命令以协议的格式写入到 <code>aof_buf</code> 中.该属性为 <code>redisServer</code> 中的一个.</p>
+
+<pre><code class="language-c">#src/server.h
+struct redisServer {
+ ....
+ sds aof_buf; /* AOF buffer, written before entering the event loop */
+}
+</code></pre>
+
+<h4 id="aof-写入同步">AOF 写入同步</h4>
+
+<p><code>redis</code> 的服务进程是一个 <strong>事件循环</strong> - <code>event loop</code> , 每次循环大概会做三件事.</p>
+
+<ol>
+<li>文件事件: 接收客户端的命令,返回结果</li>
+<li>时间事件: 执行系统的定时任务(<code>serverCron</code>), 完成渐进 <code>rehash</code> 扩容之类的操作</li>
+
+<li><p>aof flush: 是否将 <code>aof_buf</code> 中的内容写入文件中</p>
+
+<pre><code class="language-bash"># 伪代码
+def eventloop():
+while true:
+ processFileEvents() # 处理命令
+ processTimeEvents() # 处理定时任务
+ flushAppendOnlyFile() # 处理 aof 写入
+
+</code></pre></li>
+</ol>
+
+<p><code>flushAppendOnlyFile</code> 中的动作是否执行是根据一个配置决定的.</p>
+
+<h5 id="appendfsync">appendfsync</h5>
+
+<p>该配置有几个值可选,默认是 <code>everysec</code>.</p>
+
+<ol>
+<li>always: 总是写入.只要程序执行到这一步了,就将 <code>aof_buf</code> 中命令协议写入到文件</li>
+<li>everysec: 每秒写入. 每次执行前会先判断是否与上次写入间隔一秒,再次同步时通过 <strong>一个线程</strong> 专门执行</li>
+<li>no: 不写入. 命令写入 <code>aof_buf</code> 后由操作系统决定何时同步到文件</li>
+</ol>
+
+<blockquote>
+<p>fsync: 现代操作系统为了提高文件读写的效率,通常会将 <code>write</code> 函数写入的数据缓存在内存中,等到缓存空间填满或者超过一定时限,再将其写入磁盘.这样的问题在于宕机时缓存中的数据就无法恢复.所以操作系统提供了 <strong>fsync/fdatasync</strong> 两个函数,强制操作系统将数据立即写入磁盘,保证数据安全.两函数区别在于: 前者会更新文件的属性,后者只更新数据.</p>
+</blockquote>
+
+<p>三种模式在性能和数据上都有相对的优缺点. <code>always</code> 模式数据安全性更强,毕竟每次都是直接写入,但是就会影响性能.磁盘读写是比较慢的. <code>everysec</code> 模式性能较好,但会丢失一秒内的缓存数据. <code>no</code> 模式就完全取决于操作系统了.</p>
+
+<h4 id="aof-还原数据">AOF 还原数据</h4>
+
+<p><img src="https://raw.githubusercontent.com/xiaoheiAh/imgs/master/20191113182005.png" alt="redis设计与实现-aof还原数据" /></p>
+
+<h3 id="aof-重写">AOF 重写</h3>
+
+<p><code>AOF</code> 重写的意思其实就是对单个命令的多个操作进行整理,留下最终态的执行命令来减少 <code>aof</code> 文件的大小.你可以想象一下执行 1w 次 <code>incr</code> 操作,写入 <code>aof</code> 1w 次的场景.</p>
+
+<h4 id="触发条件">触发条件</h4>
+
+<p><code>AOF</code> 重写可以自动触发.通过配置 <code>auto-aof-rewrite-min-size</code> 和<code>auto-aof-rewrite-percentage</code>,满足条件就会自动重写.具体可以查看官方的 <code>redis.conf</code></p>
+
+<h4 id="重写过程">重写过程</h4>
+
+<ol>
+<li>创建子进程,根据内存里的数据重写<code>aof</code>,保存到<code>temp</code>文件</li>
+<li>此时主进程还会接收命令,会将写操作追加到旧的<code>aof</code>文件中,并保存在<code>server.aof_rewrite_buf_blocks</code>中,通过管道发送给子进程存在<code>server.aof_child_diff</code>中,最后追加到<code>temp</code>文件结尾</li>
+<li>子进程重写完成后退出,主进程根据子进程退出状态,判断成功与否。成功就将剩余的<code>server.aof_rewrite_buf_blocks</code>追加到<code>temp file</code>中,然后<code>rename()</code>覆盖原<code>aof</code>文件</li>
+</ol>
+
+<p>重写的过程中主进程还是会一直接受客户端的命令,所以重写子进程与主进程肯定会存在数据不一致的情况.<code>redis</code>针对这种情况作出了解决方案: 新增一个 <code>aof_rewrite_buf_blocks</code>, <code>aof</code> 写入命令时,不仅写入到 <code>aof_buf</code>, 如果正在重写,那么也写入到 <code>aof_rewrite_buf_blocks</code> 中,这样在子进程重写完毕后,可以将 <code>aof_rewrite_buf_blocks</code> 的命令追加到新文件中,保证数据不丢失.</p>
+
+<p><code>rename</code> 操作是原子的,也是唯一会造成主进程阻塞的操作.</p>
+
+<h2 id="参考">参考</h2>
+
+<ol>
+<li><a href="https://redis.io/topics/persistence">https://redis.io/topics/persistence</a></li>
+<li><a href="https://youjiali1995.github.io/redis/persistence/">https://youjiali1995.github.io/redis/persistence/</a></li>
+</ol>
</div>
</article>
<section id="comments">
@@ -282,6 +479,11 @@
class="icon icon-angle-left"
aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
</li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/"
+ title="Redis-事件"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
<li class="toggle-toc">
<a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
@@ -394,7 +596,7 @@ hljs.initHighlightingOnLoad();
UNTITLED: '(未命名)',
},
ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
- CONTENT_URL: 'https:\/\/xiaohei.im\/searchindex.json ',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
};
window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
diff --git a/2019/11/db/index.html b/2019/11/db/index.html
index 01435ae..bed79db 100644
--- a/2019/11/db/index.html
+++ b/2019/11/db/index.html
@@ -161,7 +161,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -193,7 +193,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -227,50 +227,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/11/distributed-lock/index.html b/2019/11/distributed-lock/index.html
index d3f9967..f5a0f66 100644
--- a/2019/11/distributed-lock/index.html
+++ b/2019/11/distributed-lock/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -529,7 +529,7 @@ end
<div class="bar-inner">
<ul class="pager pull-left">
<li class="prev">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" title="Redis - 数据结构"><i
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" title="Redis-数据结构"><i
class="icon icon-angle-left"
aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
</li>
diff --git a/2019/11/event/index.html b/2019/11/event/index.html
new file mode 100644
index 0000000..f1e2f06
--- /dev/null
+++ b/2019/11/event/index.html
@@ -0,0 +1,763 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Redis-事件 - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content=" 事件驱动程序设计(英语:Event-driven programming)是一种电脑程序设计模型。这种模型的程序运行流程是由用户的动作(如鼠标的按键,键盘的按键动作)或者是由其他程序的消息来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由程序员来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的。 &amp;ndash;wikipedia
+" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Redis-事件 - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Redis-事件" />
+<meta property="og:description" content="
+事件驱动程序设计(英语:Event-driven programming)是一种电脑程序设计模型。这种模型的程序运行流程是由用户的动作(如鼠标的按键,键盘的按键动作)或者是由其他程序的消息来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由程序员来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的。 &ndash;wikipedia
+" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/2019/11/event/" />
+<meta property="article:published_time" content="2019-11-14T15:01:45+08:00" />
+<meta property="article:modified_time" content="2019-11-14T15:01:45+08:00" />
+<meta itemprop="name" content="Redis-事件">
+<meta itemprop="description" content="
+事件驱动程序设计(英语:Event-driven programming)是一种电脑程序设计模型。这种模型的程序运行流程是由用户的动作(如鼠标的按键,键盘的按键动作)或者是由其他程序的消息来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由程序员来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的。 &ndash;wikipedia
+">
+
+
+<meta itemprop="datePublished" content="2019-11-14T15:01:45&#43;08:00" />
+<meta itemprop="dateModified" content="2019-11-14T15:01:45&#43;08:00" />
+<meta itemprop="wordCount" content="2358">
+
+
+
+<meta itemprop="keywords" content="redis," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Redis-事件"/>
+<meta name="twitter:description" content="
+事件驱动程序设计(英语:Event-driven programming)是一种电脑程序设计模型。这种模型的程序运行流程是由用户的动作(如鼠标的按键,键盘的按键动作)或者是由其他程序的消息来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由程序员来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的。 &ndash;wikipedia
+"/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#文件事件">文件事件</a>
+<ul>
+<li><a href="#文件事件处理器">文件事件处理器</a>
+<ul>
+<li><a href="#什么是-i-o-multiplexing">什么是 I/O Multiplexing?</a></li>
+<li><a href="#文件事件处理器结构">文件事件处理器结构</a></li>
+<li><a href="#文件事件类型">文件事件类型</a>
+<ul>
+<li><a href="#事件处理的先后顺序">事件处理的先后顺序</a></li>
+</ul></li>
+</ul></li>
+<li><a href="#事件处理器">事件处理器</a>
+<ul>
+<li><a href="#常用时间处理器">常用时间处理器</a></li>
+</ul></li>
+<li><a href="#文件事件处理流程">文件事件处理流程</a></li>
+</ul></li>
+<li><a href="#时间事件">时间事件</a>
+<ul>
+<li><a href="#属性">属性</a></li>
+<li><a href="#时钟问题">时钟问题</a>
+<ul>
+<li><a href="#时间事件执行流程">时间事件执行流程</a></li>
+</ul></li>
+</ul></li>
+<li><a href="#事件循环-event-loop">事件循环 Event Loop</a>
+<ul>
+<li><a href="#aeeventloop">aeEventLoop</a></li>
+<li><a href="#aeprocessevent">aeProcessEvent</a></li>
+</ul></li>
+<li><a href="#参考">参考</a></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/2019/11/event/"
+ >Redis-事件</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="article-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/redis/"> redis </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/redis/"> redis </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/11/event/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:2358字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:5分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <blockquote>
+<p><strong>事件驱动程序设计</strong>(英语:<strong>Event-driven programming</strong>)是一种电脑<a href="https://zh.wikipedia.org/wiki/程式設計">程序设计</a><a href="https://zh.wikipedia.org/wiki/模型">模型</a>。这种模型的程序运行流程是由用户的动作(如<a href="https://zh.wikipedia.org/wiki/滑鼠">鼠标</a>的按键,键盘的按键动作)或者是由其他程序的<a href="https://zh.wikipedia.org/wiki/訊息">消息</a>来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由<a href="https://zh.wikipedia.org/wiki/程式設計師">程序员</a>来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在<a href="https://zh.wikipedia.org/w/index.php?title=互動程序&amp;action=edit&amp;redlink=1">交互程序</a>(Interactive program)的情况下孕育而生的。 <a href="https://zh.wikipedia.org/wiki/事件驅動程式設計">&ndash;wikipedia</a></p>
+</blockquote>
+
+<h2 id="文件事件">文件事件</h2>
+
+<p>服务端通过套接字与客户端进行连接,文件事件就是服务端对套接字操作的抽象.服务端与客户端的通信会产生多种文件事件(连接 <code>accept</code> ,读取 <code>read</code>, 写入 <code>write</code> ,关闭 <code>close</code>),服务器监听并处理相应的事件.</p>
+
+<h3 id="文件事件处理器">文件事件处理器</h3>
+
+<p><code>redis</code> 基于 <code>Reactor</code> 模式实现了网络事件处理 &ndash;&gt; <strong>文件时间处理器</strong>.通过 <code>I/O 多路复用</code> 保证了单进程下的高性能网络模型.</p>
+
+<h4 id="什么是-i-o-multiplexing">什么是 I/O Multiplexing?</h4>
+
+<p>参考: <a href="https://draveness.me/redis-io-multiplexing">https://draveness.me/redis-io-multiplexing</a></p>
+
+<p>首先需要知道什么是文件描述符(<code>File Descriptor</code> ,简称 <code>FD</code>)? 文件描述符就是操作系统中操作文件时内核返回的一个 <strong>非负整数</strong>,可以通过文件描述符来指定待读写的文件.而套接字 <code>socket</code> 本质上也是一种文件描述符.</p>
+
+<p>简单来说就是通常我们使用的 <code>I/O</code> 模型是阻塞型的,服务器在处理一个客户端请求(即处理一个<code>FD</code>)时无法再处理其它的了. <code>I/O多路复用</code> 是通过利用操作系统的多路复用函数(<code>select()</code>)来监听多个 <code>FD</code> 的可读可写情况,一旦有可读或可写的 <code>FD</code>,<code>select()</code> 就返回对应的个数.</p>
+
+<p><img src="https://img.draveness.me/2016-11-26-redis-choose-io-function.jpg-1000width" alt="盗用draveness大佬的图-侵删" style="zoom:50%;" /></p>
+
+<p>由于不同操作系统的有不同的多路复用函数,<code>select</code>是性能最差的.而 <code>redis</code> 也会根据操作系统的不同选择性能最好的函数来使用.并且由于不同平台的差异, <code>redis</code> 提供了一套相同的结构并针对不同平台进行了实现,以此屏蔽了对上层应用的影响.</p>
+
+<pre><code class="language-c">#ifdef HAVE_EVPORT
+#include &quot;ae_evport.c&quot;
+#else
+ #ifdef HAVE_EPOLL
+ #include &quot;ae_epoll.c&quot;
+ #else
+ #ifdef HAVE_KQUEUE
+ #include &quot;ae_kqueue.c&quot;
+ #else
+ #include &quot;ae_select.c&quot;
+ #endif
+ #endif
+#endif
+</code></pre>
+
+<h4 id="文件事件处理器结构">文件事件处理器结构</h4>
+
+<p><img src="https://raw.githubusercontent.com/xiaoheiAh/imgs/master/20191115142151.png" alt="文件事件处理器结构" style="zoom:50%;" /></p>
+
+<p>每一个套接字 <code>socket</code> 可以执行连接,读写,关闭操作时,会产生一个 <strong>文件事件</strong>.,<code>I/O 多路复用</code> 监听这些 <code>FD</code> 的操作请求,并向 <strong>文件事件派发器</strong> 传递产生文件事件的 <code>FD</code>. 虽然会并发的产生 N 个文件事件,但 <code>I/O多路复用</code> 会将其都放入一个队列中,<strong>顺序且同步地</strong>向 <strong>文件事件分派器</strong> 传送.处理完一个再传下一个.</p>
+
+<p><strong>文件事件派发器</strong> 接收到 <code>FD</code> 后,就会根据<code>FD</code> 所绑定的文件事件类型选择相应的事件处理器进行处理.</p>
+
+<h4 id="文件事件类型">文件事件类型</h4>
+
+<ul>
+<li><strong>AE_READABLE</strong> 可读事件</li>
+</ul>
+
+<p>客户端对套接字 <code>write</code> 操作, <code>close</code> 操作或者客户端与服务端进行连接(出现 <code>acceptable</code> 套接字)时产生可读事件</p>
+
+<ul>
+<li><strong>AE_WRITABLE</strong> 可写事件</li>
+</ul>
+
+<p>客户端对套接字执行 <code>read</code> 操作,套接字产生可写事件</p>
+
+<ul>
+<li><strong>AE_NONE</strong> 无任何事件</li>
+</ul>
+
+<h5 id="事件处理的先后顺序">事件处理的先后顺序</h5>
+
+<p><strong>AE_READABLE</strong> &gt; <strong>AE_WRITABLE</strong></p>
+
+<h3 id="事件处理器">事件处理器</h3>
+
+<p>事件处理器是针对不同的文件事件实现的逻辑.客户端连接时,服务器需要进行应答,此时服务器就会将套接字关联到应答处理器.接收客户端的命令请求,服务器会将套接字关联到命令请求处理器.</p>
+
+<h4 id="常用时间处理器">常用时间处理器</h4>
+
+<ol>
+<li>连接应答处理器 <code>networking.c/acceptTcpHandler</code></li>
+</ol>
+
+<p>客户端连接时会对其进应答.<code>redis</code> 在初始化时会将服务器的监听套接字的可读事件与该处理器关联起来,客户端只要连接监听套接字就会产生可读事件,执行对应的逻辑.</p>
+
+<ol>
+<li>命令请求处理器 <code>networking.c/readQueryFromClient</code></li>
+</ol>
+
+<p>客户端连接服务器后,服务器会将客户端套接字的可读事件与命令请求处理器关联起来,当客户端向服务器发送命令请求时,产生可读事件,执行对应逻辑.</p>
+
+<ol>
+<li>命令回复处理器 <code>networking.c/sendReplyToClient</code></li>
+</ol>
+
+<p>服务器有命令回复需要传送给客户端时,服务器会将客户端套接字的可写事件与命令回复处理器关联起来,客户端准备好接收服务器回复时,会产生可写事件,触发命令回复器执行.服务器发送完毕时,会解除关联.</p>
+
+<h3 id="文件事件处理流程">文件事件处理流程</h3>
+
+<p><img src="https://img.draveness.me/2016-12-09-eventloop-file-event-in-redis.png-1000width" alt="draveness.me-侵删" style="zoom:50%;" /></p>
+
+<p><code>aeCreateFileEvent</code> 可以将一个给定<code>FD</code> 的给定事件加入到多路复用的监听范围中,并将事件与时间处理器关联</p>
+
+<p><code>aeDeleteFileEvent</code> 取消给定<code>FD</code> 的给定事件的监听</p>
+
+<p><code>aeApiPoll</code> 该方法会在每个平台的多路复用中进行实现,阻塞等待所有监听的<code>FD</code> 所产生的事件并返回可用时间的数量.会有超时处理.</p>
+
+<h2 id="时间事件">时间事件</h2>
+
+<p><code>Redis</code> 中有两种时间事件 &mdash;- 定时事件(隔一段时间执行一次),非定时事件(某个时间点执行一次)</p>
+
+<h3 id="属性">属性</h3>
+
+<ol>
+<li><strong>id</strong> 全局唯一ID,顺序递增</li>
+<li><strong>when</strong> 毫秒精度 UNIX 时间戳,记录时间事件到达时间</li>
+<li><strong>timeProc</strong> 时间事件处理器,需要执行时间事件时,根据该处理器执行</li>
+</ol>
+
+<p>时间事件是定时还是非定时,取决去 <code>timeProc</code> 返回值是否等于 <code>AE_NOMORE</code>. 等于则给事件ID标记为待删除,不等于则更新执行时间到下一次.</p>
+
+<pre><code class="language-c">retval = te-&gt;timeProc(eventLoop, id, te-&gt;clientData);
+if (retval != AE_NOMORE) {
+ aeAddMillisecondsToNow(retval,&amp;te-&gt;when_sec,&amp;te-&gt;when_ms);
+} else {
+ te-&gt;id = AE_DELETED_EVENT_ID;
+}
+</code></pre>
+
+<p><code>Redis</code> 处理时间事件时,不会在当前循环中直接移除不再需要执行的事件,而是会在当前循环中将时间事件的 <code>id</code> 设置为 <code>AE_DELETED_EVENT_ID</code>,然后再下一个循环中删除,并执行绑定的 <code>finalizerProc</code>。</p>
+
+<pre><code class="language-c">/* Remove events scheduled for deletion. */
+if (te-&gt;id == AE_DELETED_EVENT_ID) {
+ aeTimeEvent *next = te-&gt;next;
+ if (te-&gt;prev)
+ te-&gt;prev-&gt;next = te-&gt;next;
+ else
+ eventLoop-&gt;timeEventHead = te-&gt;next;
+ if (te-&gt;next)
+ te-&gt;next-&gt;prev = te-&gt;prev;
+ if (te-&gt;finalizerProc)
+ te-&gt;finalizerProc(eventLoop, te-&gt;clientData);
+ zfree(te);
+ te = next;
+ continue;
+}
+</code></pre>
+
+<h3 id="时钟问题">时钟问题</h3>
+
+<p>时间事件的执行影响最大的因素就是 <strong>系统时间</strong>. 系统时间的调整会影响时间事件的执行,所以在<code>eventLoop</code> 中有个 <code>lastTime</code> 属性来检测系统时间.如果发现系统时间改变了,比上次执行时间事件的时间小,就会强制尽早执行.</p>
+
+<h4 id="时间事件执行流程">时间事件执行流程</h4>
+
+<p><img src="https://img.draveness.me/2016-12-09-process-time-event.png-1000width" alt="draveness.me-侵删" style="zoom:50%;" /></p>
+
+<h2 id="事件循环-event-loop">事件循环 Event Loop</h2>
+
+<p>上述的 <code>文件事件</code>, <code>时间事件</code> 是从何时开始? 在 <code>事件循环</code> 中开始. <code>事件循环</code> 是 <code>redis</code> 在启动后初始化完服务配置,就会陷入一个巨大的循环 <code>aeEventLoop</code> 中. 这个巨大的循环从 <code>aeMain()</code> 开始.</p>
+
+<pre><code class="language-c">void aeMain(aeEventLoop *eventLoop) {
+ eventLoop-&gt;stop = 0;
+ while (!eventLoop-&gt;stop) {
+ if (eventLoop-&gt;beforesleep != NULL)
+ eventLoop-&gt;beforesleep(eventLoop);
+ aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
+ }
+}
+</code></pre>
+
+<p>源码中可以看出来,除非给 <code>eventLoop-&gt;stop</code> 设置为 <code>true</code> ,程序会一直跑,一直执行 <code>aeProcessEvents</code>.</p>
+
+<h3 id="aeeventloop">aeEventLoop</h3>
+
+<p><img src="https://img.draveness.me/2016-12-09-reids-eventloop.png-1000width" alt="draveness.me-aeEventLoop 结构" /></p>
+
+<p><code>aeEventLoop</code> 保存着事件循环的上下文信息,并有三个重要的数组:保存监听的文件事件 <code>aeFileEvent</code> , 时间事件 <code>aeTimeEvent</code>, 待处理文件事件 <code>aeFiredEvent</code>.</p>
+
+<h3 id="aeprocessevent">aeProcessEvent</h3>
+
+<p>在一般情况下,<code>aeProcessEvents</code> 都会先<strong>计算最近的时间事件发生所需要等待的时间</strong>,然后调用 <code>aeApiPoll</code> 方法在这段时间中等待事件的发生,在这段时间中如果发生了文件事件,就会优先处理文件事件,否则就会一直等待,直到最近的时间事件需要触发.</p>
+
+<pre><code class="language-c">int aeProcessEvents(aeEventLoop *eventLoop, int flags) {
+ int processed = 0, numevents;
+
+ if (!(flags &amp; AE_TIME_EVENTS) &amp;&amp; !(flags &amp; AE_FILE_EVENTS)) return 0;
+
+ if (eventLoop-&gt;maxfd != -1 ||
+ ((flags &amp; AE_TIME_EVENTS) &amp;&amp; !(flags &amp; AE_DONT_WAIT))) {
+ struct timeval *tvp;
+
+ #1:计算 I/O 多路复用的等待时间 tvp
+
+ numevents = aeApiPoll(eventLoop, tvp);
+ for (int j = 0; j &lt; numevents; j++) {
+ aeFileEvent *fe = &amp;eventLoop-&gt;events[eventLoop-&gt;fired[j].fd];
+ int mask = eventLoop-&gt;fired[j].mask;
+ int fd = eventLoop-&gt;fired[j].fd;
+ int rfired = 0;
+
+ if (fe-&gt;mask &amp; mask &amp; AE_READABLE) {
+ rfired = 1;
+ fe-&gt;rfileProc(eventLoop,fd,fe-&gt;clientData,mask);
+ }
+ if (fe-&gt;mask &amp; mask &amp; AE_WRITABLE) {
+ if (!rfired || fe-&gt;wfileProc != fe-&gt;rfileProc)
+ fe-&gt;wfileProc(eventLoop,fd,fe-&gt;clientData,mask);
+ }
+ processed++;
+ }
+ }
+ if (flags &amp; AE_TIME_EVENTS) processed += processTimeEvents(eventLoop);
+ return processed;
+}
+</code></pre>
+
+<h2 id="参考">参考</h2>
+
+<ol>
+<li><a href="https://draveness.me/redis-eventloop">https://draveness.me/redis-eventloop</a></li>
+<li><a href="https://draveness.me/redis-io-multiplexing">https://draveness.me/redis-io-multiplexing</a></li>
+<li><a href="https://book.douban.com/subject/25900156/">Redis设计与实现</a></li>
+</ol>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" title="Redis-AOF持久化"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/"
+ title="Redis-复制功能探索"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+
+<script src="https://cdn.jsdelivr.net/npm/gitalk@1.4.0/dist/gitalk.min.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/blueimp-md5@2.10.0/js/md5.min.js"></script>
+<script type="text/javascript">
+ var gitalk = new Gitalk({
+ clientID: 'e38fc798c72a7e4e1386',
+ clientSecret: 'e151aa3b7b98d3cfaa1f096b88fdd7897e2c8007',
+ repo: 'xiaoheiAh.github.io',
+ owner: 'xiaoheiAh',
+ admin: ['xiaoheiAh'],
+ id: md5(location.pathname),
+ distractionFreeMode: true
+ });
+ gitalk.render('comments');
+</script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/2019/11/obj/index.html b/2019/11/obj/index.html
index d0fe0a8..292b250 100644
--- a/2019/11/obj/index.html
+++ b/2019/11/obj/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/2019/11/rdb/index.html b/2019/11/rdb/index.html
index ace4cd6..646c8e8 100644
--- a/2019/11/rdb/index.html
+++ b/2019/11/rdb/index.html
@@ -162,7 +162,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -194,7 +194,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -228,50 +228,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -346,7 +346,7 @@
<div class="article-header">
<h1 itemprop="name">
<a
- class=""
+ class="article-title"
href="/hugo-theme-pure/2019/11/rdb/"
>Redis-RDB持久化</a
>
@@ -794,6 +794,11 @@ int rdbSaveInfoAuxFields(rio *rdb, int flags, rdbSaveInfo *rsi) {
class="icon icon-angle-left"
aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
</li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/"
+ title="Redis-AOF持久化"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
<li class="toggle-toc">
<a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
diff --git a/2019/11/replication/index.html b/2019/11/replication/index.html
new file mode 100644
index 0000000..93eec09
--- /dev/null
+++ b/2019/11/replication/index.html
@@ -0,0 +1,712 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Redis-复制功能探索 - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content="之前多redis 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 https://redis.io/topics/replication
+" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Redis-复制功能探索 - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Redis-复制功能探索" />
+<meta property="og:description" content="之前多redis 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 https://redis.io/topics/replication" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" />
+<meta property="article:published_time" content="2019-11-16T14:24:40+08:00" />
+<meta property="article:modified_time" content="2019-11-16T14:24:40+08:00" />
+<meta itemprop="name" content="Redis-复制功能探索">
+<meta itemprop="description" content="之前多redis 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 https://redis.io/topics/replication">
+
+
+<meta itemprop="datePublished" content="2019-11-16T14:24:40&#43;08:00" />
+<meta itemprop="dateModified" content="2019-11-16T14:24:40&#43;08:00" />
+<meta itemprop="wordCount" content="3579">
+
+
+
+<meta itemprop="keywords" content="redis," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Redis-复制功能探索"/>
+<meta name="twitter:description" content="之前多redis 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 https://redis.io/topics/replication"/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#复制是什么">复制是什么?</a></li>
+<li><a href="#redis-主从复制">Redis 主从复制</a>
+<ul>
+<li><a href="#主从复制作用">主从复制作用</a></li>
+<li><a href="#redis-复制设计要点">Redis 复制设计要点</a></li>
+<li><a href="#主从复制过程">主从复制过程</a>
+<ul>
+<li><a href="#backlog-buffer-是啥">backlog buffer 是啥?</a></li>
+<li><a href="#全量复制">全量复制</a></li>
+<li><a href="#sync-psync">SYNC/PSYNC</a></li>
+<li><a href="#replication-id">Replication ID</a>
+<ul>
+<li><a href="#为什么有两个-replid">为什么有两个<code>replId</code>?</a></li>
+<li><a href="#为什么晋升后需要生成新-replid">为什么晋升后需要生成新 replId?</a></li>
+</ul></li>
+<li><a href="#无盘复制">无盘复制</a></li>
+<li><a href="#如何处理可以过期的键">如何处理可以过期的键?</a></li>
+<li><a href="#心跳机制">心跳机制</a>
+<ul>
+<li><a href="#master-slave">master -&gt; slave</a></li>
+<li><a href="#replica-master">replica -&gt; master</a></li>
+</ul></li>
+</ul></li>
+</ul></li>
+<li><a href="#复制惨痛案例">复制惨痛案例</a>
+<ul>
+<li>
+<ul>
+<li><a href="#数据过期问题">数据过期问题</a></li>
+<li><a href="#数据延迟不一致">数据延迟不一致</a></li>
+<li><a href="#复制超时导致复制中断">复制超时导致复制中断</a>
+<ul>
+<li><a href="#为什么要判断超时">为什么要判断超时?</a></li>
+<li><a href="#判断机制">判断机制?</a></li>
+<li><a href="#问题">问题</a></li>
+</ul></li>
+<li><a href="#backlog-过小导致无限全量复制">backlog 过小导致无限全量复制</a></li>
+</ul></li>
+</ul></li>
+<li><a href="#参考">参考</a></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/2019/11/replication/"
+ >Redis-复制功能探索</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="article-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/redis/"> redis </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/redis/"> redis </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/11/replication/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:3579字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:8分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <p>之前多<code>redis</code> 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 <a href="https://redis.io/topics/replication">https://redis.io/topics/replication</a></p>
+
+<h2 id="复制是什么">复制是什么?</h2>
+
+<p>分布式系统有一个重要的点时保证数据不丢失,数据不丢失就意味着不能单点,不能单点就意味着最好能把数据多存几份形成数据的冗余.这就是复制的来由.复制类型主要是两种: <strong>同步</strong>, <strong>异步</strong>. 前者需要等待所有的节点返回写入确认,后者只需要返回个确认收到就行.</p>
+
+<h2 id="redis-主从复制">Redis 主从复制</h2>
+
+<h3 id="主从复制作用">主从复制作用</h3>
+
+<ol>
+<li><strong>数据冗余</strong>:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。</li>
+<li><strong>故障恢复</strong>:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。</li>
+<li><strong>负载均衡</strong>:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。</li>
+<li><strong>高可用基石</strong>:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。</li>
+</ol>
+
+<h3 id="redis-复制设计要点">Redis 复制设计要点</h3>
+
+<ul>
+<li><p>默认使用异步复制 &ndash; replica -&gt; master 异步返回处理了多少数据的结果(<strong>偏移量</strong>)</p></li>
+
+<li><p><code>master</code> 可以多 <code>replicas</code></p></li>
+
+<li><p><code>replicas</code> 可以从其他 <code>replica</code> 同步(从从复制).类似于级联更新的架构.</p></li>
+</ul>
+
+<p><img src="https://raw.githubusercontent.com/xiaoheiAh/imgs/master/20191118110202.png" alt="从从复制" /></p>
+
+<ul>
+<li><code>master</code> 主节点在同步时不会阻塞.</li>
+<li>在 <code>replica</code> 侧复制也是非阻塞的.在进行初始化同步(全量)时,可以使用 <code>replica</code>上的旧数据供客户端查询.也可以在 <code>redis.conf</code> 进行配置,在初始化同步完成前客户端的请求都报错.初始化同步完成后,需要删除老数据,加载新数据.在这段时间中会阻塞外部连接请求,数据量大的话可能要很久.从 <code>4.0</code> 版本后,删除老数据可以通过多线程来优化效率,但是加载新数据还是会 <strong>阻塞</strong>.</li>
+<li>复制可以用来弹性扩容,提供多可读副本,提升数据安全性,保证高可用</li>
+<li>副本可以避免 <code>master</code> 保存全量数据到磁盘的资源消耗:可以由 <code>replica</code> 完成持久化,或者开启 <code>aof</code> 写入.不过需要慎重: 这样会导致 <code>master</code> 节点再重启时会是空的,其他 <code>replica</code>复制时也就成空的了.</li>
+</ul>
+
+<h3 id="主从复制过程">主从复制过程</h3>
+
+<p>每一个 <code>master</code> 节点都会有一个特别大的随机数(40字节十六进制随机字符)作为 <code>replication ID</code> 来标识自己.每个 <code>master</code> 节点也有一个 持续递增的 <code>offset</code> 来记录发送给 <code>replicas</code> 的每一个 <code>byte</code>,利用该 <code>offset</code> 来保证副本更新的状态.</p>
+
+<p>当一个 <code>replica</code> 连接到 <code>master</code> 时,会使用 <code>PSYNC</code> 命令发送之前复制的 <code>master</code> 的 <code>replicationID</code>,以及自己的更新进度(<code>offset</code>).<code>master</code> 可以根据这个值给副本按需返回为更新的数据.如果在<code>master</code> 的 <code>backlog buffer</code> 中没有对应的数据可以给到,副本发送的 <code>replicationID</code> 与 <code>master</code> 的 ID 不一致,就会触发全量复制(<code>Full Synchronization</code>).</p>
+
+<h4 id="backlog-buffer-是啥">backlog buffer 是啥?</h4>
+
+<p>复制积压缓冲区,在 <code>master</code> 有 <code>replica</code> 进行复制时,存储 <code>master</code> 最近一段时间的写命令,以便在 <code>replica</code> 断开重连后,可以利用缓冲区更新断开这段时间中,从节点丢掉的更新.</p>
+
+<p><code>backlog buffer</code> 是有固定的长度,先进先出的队列,默认大小 <code>1MB</code>. 其实就是一个环.<code>buffer</code> 会存储每一个 <code>offset</code> 已经对应的写命令,这样 <code>replica</code> 在断连恢复后,发送 <code>PSYNC</code> 命令提供其最后一次更新的 <code>offset</code>, <code>master</code> 就可以根据 <code>replica</code> 提供的 <code>offset</code> 去 <code>buffer</code> 中找对应的数据发送给 <code>replica</code> 保持最新.</p>
+
+<p>如果断开时间过长,<code>buffer</code> 存储的数据已经换了一批又一批, <code>replica</code> 在重连后发送给 <code>master</code> 的 <code>offset</code> 在 <code>buffer</code> 已经找不到了.此时会触发 <strong>全量复制</strong>.</p>
+
+<h4 id="全量复制">全量复制</h4>
+
+<p><code>master</code>调用 <code>bgsave</code> 在后台生成 <code>rdb</code> 文件.同时记录客户端新的写命令到 <code>backlog buffer</code> 中. <code>rdb</code> 文件生成后,发送给 <code>replica</code> 保存到其硬盘中,然后再加载到内存中并通知<code>master</code> 加载完成.然后 <code>master</code> 会发送 <code>buffer pool</code> 中的命令给 <code>replica</code> 完成最后的同步.</p>
+
+<h4 id="sync-psync">SYNC/PSYNC</h4>
+
+<p>两者都是同步的命令.<code>SYNC</code> 只支持全量同步, <code>PSYNC</code> 支持上述的部分同步.<code>2.8</code> 版本之前只有 <code>SYNC</code>,为了避免每次都只能全量同步造成资源的浪费,就新增了 <code>PSYNC</code> 命令实现部分同步的语义.</p>
+
+<h4 id="replication-id">Replication ID</h4>
+
+<p><code>Replication ID</code> 标记了数据的历史信息,从0开始成为<code>master</code> 的节点,或者晋升成为 <code>master</code> 的 <code>replica</code> 节点,都会生成一个 <code>Replication ID</code>.<code>replicas</code> 的 <code>replId</code> 是和其复制的 <code>master</code> 一致的,<code>master</code> 通过该 ID 和 <code>offset</code> 来判断主从之间数据是否一致.</p>
+
+<h5 id="为什么有两个-replid">为什么有两个<code>replId</code>?</h5>
+
+<pre><code class="language-c">/* src/server.h */
+struct redisServer {
+ ...
+ /* Replication (master) */
+ char replid[CONFIG_RUN_ID_SIZE+1]; /* My current replication ID. */
+ char replid2[CONFIG_RUN_ID_SIZE+1]; /* replid inherited from master*/
+ ...
+ long long master_repl_offset; /* My current replication offset */
+ long long second_replid_offset; /* Accept offsets up to this for replid2. */
+ ...
+}
+</code></pre>
+
+<p>一般情况下,故障转移(<code>failover</code>)后,晋升的 <code>replica</code> 需要记录自己之前复制的 <code>master</code> 对应的 <code>replId</code>.其他 <code>replicas</code> 会向新 <code>master</code> 进行部分同步,但发送过来的 <code>replId</code> 还是之前 <code>master</code> 的.所以 <code>replica</code> 在晋升时,会生成新的<code>replId</code>,并将原来的 <code>replId</code> 记录到 <code>replId2</code>,同时记录下当时所更新到的 <code>offset</code> 到 <code>second_replid_offset</code>.当其他的 <code>replica</code> 向新 <code>master</code> 进行连接时,新 <code>master</code> 会比较当前的和之前 <code>master</code> 的 <code>replId</code>,<code>offset</code>,这样就可以防止在故障转移后导致不必要的 <strong>全量复制</strong>.</p>
+
+<h5 id="为什么晋升后需要生成新-replid">为什么晋升后需要生成新 replId?</h5>
+
+<p><code>old master</code> 可能还存活,但由于网络分区原因无法和其他 <code>replicas</code> 通信,如果保留原来的 <code>id</code> 不再生成,就会导致有相同数据相同id的<code>master</code> 存在.</p>
+
+<h4 id="无盘复制">无盘复制</h4>
+
+<p>全量复制时,<code>master</code> 会创建 <code>rdb</code> 文件存到磁盘,然后再读取 <code>rdb</code> 文件发送给 <code>replicas</code>.磁盘性能差的情况下,效率会很低,所以支持了 <strong>无盘复制</strong> &ndash; 子进程直接发送 <code>rdb</code> 给 <code>replicas</code>,不经过硬盘存储.</p>
+
+<h4 id="如何处理可以过期的键">如何处理可以过期的键?</h4>
+
+<ol>
+<li>副本不会主动去过期键,而是由 <code>master</code> 过期键后向副本发送 <code>DEL</code> 命令.</li>
+<li>由于是通过 <code>master</code> 驱动,副本收到 <code>DEL</code> 命令可能有延迟,这就会导致从副本中还可能查到已过期的键.针对这种情况,副本会利用自身的物理时钟作为依据报告该键不存在(仅在不违反数据一致性的 <strong>只读操作</strong>),因为 <code>DEL</code> 命令总是会发过来的.</li>
+<li><code>LUA</code> 脚本执行期间,是不会去执行 <code>key</code> 过期的.脚本执行期间相当于 <code>master</code> 时间冻结了,不作过期时间的记录,所以在这期间过期键只有存在或不存在的概念.这样可以防止键在执行期间过期.同时,<code>master</code> 也需要发送同样的脚本给副本,保持一致.</li>
+</ol>
+
+<p>如果<code>replica</code> 晋升 <code>master</code> 了,它就会自己去处理键的过期了.</p>
+
+<h4 id="心跳机制">心跳机制</h4>
+
+<p>在正常的进行 <strong>部分同步</strong> 期间,主从之间会维持心跳,来协助超时判断,数据安全等问题.</p>
+
+<h5 id="master-slave">master -&gt; slave</h5>
+
+<p>主节点发送 <code>PING</code> ,从节点回复 <code>PONG</code>.目的是让从节点进行超时判断.发送频率有 <code>repl-ping-slave-period</code> 参数控制.单位秒,默认 <code>10s</code>.</p>
+
+<h5 id="replica-master">replica -&gt; master</h5>
+
+<p>从节点向主节点发送 <code>REPLCONF ACK {offset}</code> ,频率每秒1次.作用:</p>
+
+<ol>
+<li>试试检测主从网络状态,该命令被主节点用于复制超时的判断.</li>
+<li>检测命令丢失,主节点会比较从节点发送的 <code>offset</code> 与自身的是否一致,不一致则从 <code>buffer</code> 中查找对应数据进行补发,如果 <code>buffer</code> 中没有对应数据,则会进行全量复制.</li>
+<li>辅助保证从节点的数量和延迟,<code>master</code> 通过 <code>min-salves-to-write</code> 和 <code>min-slaves-max-lag</code> 参数,来保证主节点在不安全情况下不会执行写命令.是指从节点数量太少,或延迟过高。例如 <code>min-slaves-to-write</code> 和<code>min-slaves-max-lag</code> 分别是3和10,含义是如果从节点数量小于3个,或所有从节点的延迟值都大于10s,则主节点拒绝执行写命令。</li>
+</ol>
+
+<h2 id="复制惨痛案例">复制惨痛案例</h2>
+
+<h4 id="数据过期问题">数据过期问题</h4>
+
+<p>数据删除没有及时同步到从节点,其实在 <code>3.2</code> 版本后避免了这个问题.从节点会对键进行判断,已过期不展示.</p>
+
+<p><a href="#如何处理可以过期的键?">如何处理可以过期的键?</a></p>
+
+<h4 id="数据延迟不一致">数据延迟不一致</h4>
+
+<p>这种情况不可避免.可能的优化措施包括:优化主从节点之间的网络环境(如在同机房部署);监控主从节点延迟通过<code>offset</code>判断,如果从节点延迟过大,通知应用不再通过该从节点读取数据;使用集群同时扩展写负载和读负载等。</p>
+
+<h4 id="复制超时导致复制中断">复制超时导致复制中断</h4>
+
+<h5 id="为什么要判断超时">为什么要判断超时?</h5>
+
+<ol>
+<li><code>master</code> 在判断超时后,会释放从节点的连接,释放资源.</li>
+<li>断开后即时重连</li>
+</ol>
+
+<h5 id="判断机制">判断机制?</h5>
+
+<p>核心参数: <code>repl-timeout</code> ,默认 60s.</p>
+
+<p>(1)主节点:每秒1次调用复制定时函数replicationCron(),在其中判断当前时间距离上次收到各个从节点 <code>REPLCONF ACK</code> 的时间,是否超过了 <code>repl-timeout</code> 值,如果超过了则释放相应从节点的连接。</p>
+
+<p>(2)从节点:从节点对超时的判断同样是在复制定时函数中判断,基本逻辑是:</p>
+
+<ul>
+<li>如果当前处于连接建立阶段,且距离上次收到主节点的信息的时间已超过 <code>repl-timeout</code>,则释放与主节点的连接;</li>
+<li>如果当前处于数据同步阶段,且收到主节点的 <code>RDB</code> 文件的时间超时,则停止数据同步,释放连接;</li>
+<li>如果当前处于命令传播阶段,且距离上次收到主节点的 <code>PING</code> 命令或数据的时间已超过repl-timeout值,则释放与主节点的连接。</li>
+</ul>
+
+<h5 id="问题">问题</h5>
+
+<ol>
+<li>全量复制时,如果 <code>RDB</code> 文件过大,耗时很长就会触发超时,此时从节点会重连,再生成<code>RDB</code>,再超时,在生成<code>RDB</code>&hellip;解决方案就是单机数据量尽量不要太大,增大 <code>repl-timeout</code>.</li>
+<li>慢查询导致服务器阻塞: <code>keys *</code>,<code>hgetall</code></li>
+</ol>
+
+<h4 id="backlog-过小导致无限全量复制">backlog 过小导致无限全量复制</h4>
+
+<p><code>backlog buffer</code> 是固定大小的,写入命令超出长度就会覆盖.如果再全量复制的时候用时超长,存入<code>buffer</code> 的命令超过了其大小限制,那么就会导致连接中断,再重连,全量复制,连接中断,全量复制&hellip;.死循环.解决方案就是需要正确设置 <code>backlog buffer</code> 的大小. 通过 <code>client-output-buffer-limit slave {hard limit} {soft limit} {soft seconds}</code> 配置,默认值为 <code>client-output-buffer-limit slave 256MB 64MB 60</code>,其含义是:如果 <code>buffer</code> 大于<code>256MB</code>,或者连续 <code>60s</code> 大于 <code>64MB</code> ,则主节点会断开与该从节点的连接。该参数是可以通过 <code>config set</code> 命令动态配置的(即不重启Redis也可以生效).</p>
+
+<h2 id="参考">参考</h2>
+
+<ol>
+<li><p><a href="https://www.cnblogs.com/kismetv/p/9236731.html">深入学习Redis(3):主从复制</a></p></li>
+
+<li><p>「Redis 设计与实现」</p></li>
+
+<li><p><a href="https://redis.io/topics/replication">https://redis.io/topics/replication</a></p></li>
+</ol>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" title="Redis-事件"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+
+<script src="https://cdn.jsdelivr.net/npm/gitalk@1.4.0/dist/gitalk.min.js"></script>
+<script src="https://cdn.jsdelivr.net/npm/blueimp-md5@2.10.0/js/md5.min.js"></script>
+<script type="text/javascript">
+ var gitalk = new Gitalk({
+ clientID: 'e38fc798c72a7e4e1386',
+ clientSecret: 'e151aa3b7b98d3cfaa1f096b88fdd7897e2c8007',
+ repo: 'xiaoheiAh.github.io',
+ owner: 'xiaoheiAh',
+ admin: ['xiaoheiAh'],
+ id: md5(location.pathname),
+ distractionFreeMode: true
+ });
+ gitalk.render('comments');
+</script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/about/index.html b/about/index.html
index 5e65777..7940c99 100644
--- a/about/index.html
+++ b/about/index.html
@@ -23,7 +23,7 @@
<meta http-equiv="window-target" content="_top" />
- <meta name="description" content="xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&amp;gt; http://1t.click/aCyT @githubchart" />
+ <meta name="description" content="xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&amp;gt; http://1t.click/aCyT 关注的大佬博客 面向信仰编程 真·面向信仰,硬核,主 go ,其他技术也很在行, kubernetes 贡献者 @githubchart" />
<meta name="generator" content="Hugo 0.58.0 with theme pure" />
<title>关于自己 - 赵小黑的博客</title>
@@ -32,25 +32,25 @@
<link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
<meta property="og:title" content="关于自己" />
-<meta property="og:description" content="xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&gt; http://1t.click/aCyT @githubchart" />
+<meta property="og:description" content="xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&gt; http://1t.click/aCyT 关注的大佬博客 面向信仰编程 真·面向信仰,硬核,主 go ,其他技术也很在行, kubernetes 贡献者 @githubchart" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/about/" />
<meta property="article:published_time" content="2019-08-15T13:19:18+08:00" />
<meta property="article:modified_time" content="2019-08-15T13:19:18+08:00" />
<meta itemprop="name" content="关于自己">
-<meta itemprop="description" content="xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&gt; http://1t.click/aCyT @githubchart">
+<meta itemprop="description" content="xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&gt; http://1t.click/aCyT 关注的大佬博客 面向信仰编程 真·面向信仰,硬核,主 go ,其他技术也很在行, kubernetes 贡献者 @githubchart">
<meta itemprop="datePublished" content="2019-08-15T13:19:18&#43;08:00" />
<meta itemprop="dateModified" content="2019-08-15T13:19:18&#43;08:00" />
-<meta itemprop="wordCount" content="16">
+<meta itemprop="wordCount" content="55">
<meta itemprop="keywords" content="" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="关于自己"/>
-<meta name="twitter:description" content="xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&gt; http://1t.click/aCyT @githubchart"/>
+<meta name="twitter:description" content="xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&gt; http://1t.click/aCyT 关注的大佬博客 面向信仰编程 真·面向信仰,硬核,主 go ,其他技术也很在行, kubernetes 贡献者 @githubchart"/>
<!--[if lte IE 9]>
<script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
@@ -161,7 +161,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -193,7 +193,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -227,50 +227,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -296,6 +296,7 @@
<ul>
<li><a href="#xiaoheiah">xiaoheiAh~</a></li>
<li><a href="#联系方式">联系方式</a></li>
+<li><a href="#关注的大佬博客">关注的大佬博客</a></li>
<li><a href="#githubchart-https-github-com-2016rshah-githubchart-api"><a href="https://github.com/2016rshah/githubchart-api">@githubchart</a></a></li>
</ul></li>
</ul></li>
@@ -335,7 +336,7 @@
</span>
<span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/about/#comments"
class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:16字</span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:55字</span>
<span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:1分 </span>
</div>
</div>
@@ -352,6 +353,12 @@
<li>节点推荐 -&gt; <a href="http://1t.click/aCyT">http://1t.click/aCyT</a></li>
</ul>
+<h4 id="关注的大佬博客">关注的大佬博客</h4>
+
+<ol>
+<li><a href="https://draveness.me/">面向信仰编程</a> 真·面向信仰,硬核,主 go ,其他技术也很在行, <code>kubernetes</code> 贡献者</li>
+</ol>
+
<h4 id="githubchart-https-github-com-2016rshah-githubchart-api"><a href="https://github.com/2016rshah/githubchart-api">@githubchart</a></h4>
<p><img src="http://ghchart.rshah.org/xiaoheiAh" alt="xiaoheiAh's Github chart" /></p>
@@ -365,7 +372,7 @@
<div class="bar-inner">
<ul class="pager pull-left">
<li class="next">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/08/treeset/"
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/treeset/"
title="Collections-Treeset"><span>下一篇&nbsp;&nbsp;</span><i
class="icon icon-angle-right" aria-hidden="true"></i></a>
</li>
diff --git a/categories/corejava/index.html b/categories/corejava/index.html
index bfb717b..06d4cc1 100644
--- a/categories/corejava/index.html
+++ b/categories/corejava/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -305,37 +305,37 @@
aria-labelledby="heading2ad15b214588dd8294cd71c020a8d2df">
<div class="panel-body">
<div class="collection">
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/arraylist/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Arraylist
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/hashmap/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Hashmap
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/hashset/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/hashset/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Hashset
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Linkedhashmap
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Linkedlist
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/treemap/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/treemap/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Treemap
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/treeset/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/treeset/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Treeset
diff --git a/categories/corejava/index.xml b/categories/corejava/index.xml
index 078b60b..cdfea2d 100644
--- a/categories/corejava/index.xml
+++ b/categories/corejava/index.xml
@@ -12,64 +12,64 @@
<item>
<title>Collections-Arraylist</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/arraylist/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/arraylist/</guid>
<description></description>
</item>
<item>
<title>Collections-Hashmap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/hashmap/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/hashmap/</guid>
<description></description>
</item>
<item>
<title>Collections-Hashset</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/hashset/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/hashset/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/hashset/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/hashset/</guid>
<description></description>
</item>
<item>
<title>Collections-Linkedhashmap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/</guid>
<description></description>
</item>
<item>
<title>Collections-Linkedlist</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/linkedlist/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/linkedlist/</guid>
<description></description>
</item>
<item>
<title>Collections-Treemap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/treemap/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/treemap/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/treemap/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/treemap/</guid>
<description></description>
</item>
<item>
<title>Collections-Treeset</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/treeset/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/treeset/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/treeset/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/treeset/</guid>
<description></description>
</item>
diff --git a/categories/hystrix/index.html b/categories/hystrix/index.html
index 587836c..553d1d4 100644
--- a/categories/hystrix/index.html
+++ b/categories/hystrix/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/categories/index.html b/categories/index.html
index d81f942..0cc9756 100644
--- a/categories/index.html
+++ b/categories/index.html
@@ -36,7 +36,7 @@
<meta property="og:type" content="website" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/categories/" />
-<meta property="og:updated_time" content="2019-11-06T19:08:56+08:00" />
+<meta property="og:updated_time" content="2019-11-16T14:24:40+08:00" />
<meta itemprop="name" content="Categories">
<meta itemprop="description" content="">
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -305,37 +305,37 @@
aria-labelledby="headingcorejava ">
<div class="panel-body">
<div class="collection">
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/arraylist/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Arraylist
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/hashmap/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Hashmap
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/hashset/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/hashset/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Hashset
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Linkedhashmap
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Linkedlist
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/treemap/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/treemap/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Treemap
</a>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/treeset/" class="collection-item" itemprop="url" target="_blank" >
+<a href="https://xiaohei.im/hugo-theme-pure/collections/treeset/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-08-15 13:19:18 &#43;0800 CST"
itemprop="datePublished">2019-08-15</time>
<span>&nbsp;&nbsp;&nbsp;</span>Collections-Treeset
@@ -409,13 +409,28 @@
<a data-toggle="collapse" href="#collapse86a1b907d54bf7010394bf316e183e67" aria-expanded="true">
<i class="icon icon-folder text-active"></i><i class="icon icon-folder-open text"></i>redis
</a>
- <small class="text-muted">(Total5 articles)</small>
+ <small class="text-muted">(Total8 articles)</small>
</h3>
</div>
<div id="collapse86a1b907d54bf7010394bf316e183e67" class="panel-collapse collapse in" role="tabpanel"
aria-labelledby="headingredis ">
<div class="panel-body">
<div class="collection">
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST"
+ itemprop="datePublished">2019-11-16</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-复制功能探索
+</a>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST"
+ itemprop="datePublished">2019-11-14</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-事件
+</a>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST"
+ itemprop="datePublished">2019-11-08</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-AOF持久化
+</a>
<a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-11-06 19:08:56 &#43;0800 CST"
itemprop="datePublished">2019-11-06</time>
@@ -439,7 +454,7 @@
<a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-10-24 09:59:11 &#43;0800 CST"
itemprop="datePublished">2019-10-24</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Redis - 数据结构
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-数据结构
</a>
</div>
</div>
diff --git a/categories/index.xml b/categories/index.xml
index 49b4337..f52c33b 100644
--- a/categories/index.xml
+++ b/categories/index.xml
@@ -5,7 +5,7 @@
<link>https://xiaohei.im/hugo-theme-pure/categories/</link>
<description>Recent content in Categories on 赵小黑的博客</description>
<generator>Hugo -- gohugo.io</generator>
- <lastBuildDate>Wed, 06 Nov 2019 19:08:56 +0800</lastBuildDate>
+ <lastBuildDate>Sat, 16 Nov 2019 14:24:40 +0800</lastBuildDate>
<atom:link href="https://xiaohei.im/hugo-theme-pure/categories/index.xml" rel="self" type="application/rss+xml" />
@@ -13,7 +13,7 @@
<item>
<title>redis</title>
<link>https://xiaohei.im/hugo-theme-pure/categories/redis/</link>
- <pubDate>Wed, 06 Nov 2019 19:08:56 +0800</pubDate>
+ <pubDate>Sat, 16 Nov 2019 14:24:40 +0800</pubDate>
<guid>https://xiaohei.im/hugo-theme-pure/categories/redis/</guid>
<description></description>
diff --git a/categories/leetcode/index.html b/categories/leetcode/index.html
index d942ce8..bd4d87e 100644
--- a/categories/leetcode/index.html
+++ b/categories/leetcode/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/categories/redis/index.html b/categories/redis/index.html
index b42046a..3c01a44 100644
--- a/categories/redis/index.html
+++ b/categories/redis/index.html
@@ -36,7 +36,7 @@
<meta property="og:type" content="website" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/categories/redis/" />
-<meta property="og:updated_time" content="2019-11-06T19:08:56+08:00" />
+<meta property="og:updated_time" content="2019-11-16T14:24:40+08:00" />
<meta itemprop="name" content="redis">
<meta itemprop="description" content="">
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -298,27 +298,33 @@
<a data-toggle="collapse" href="#collapse86a1b907d54bf7010394bf316e183e67" aria-expanded="true">
<i class="icon icon-folder text-active"></i><i class="icon icon-folder-open text"></i>redis
</a>
- <small class="text-muted">(Total5 articles)</small>
+ <small class="text-muted">(Total8 articles)</small>
</h4>
</div>
<div id="collapse86a1b907d54bf7010394bf316e183e67" class="panel-collapse collapse in" role="tabpanel"
aria-labelledby="heading86a1b907d54bf7010394bf316e183e67">
<div class="panel-body">
<div class="collection">
-<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
- <div class="panel-body">
- <div class="article-meta">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST"
- itemprop="datePublished">2019-11-06</time>
- </div>
- <h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/">Redis-RDB持久化</a>
- </h3>
- </div>
- <div class="panel-footer">
- <a href="https://xiaohei.im/hugo-theme-pure/tags/redis" class="label label-default mb">redis</a>
- </div>
-</article>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST"
+ itemprop="datePublished">2019-11-16</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-复制功能探索
+</a>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST"
+ itemprop="datePublished">2019-11-14</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-事件
+</a>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST"
+ itemprop="datePublished">2019-11-08</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-AOF持久化
+</a>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST"
+ itemprop="datePublished">2019-11-06</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-RDB持久化
+</a>
<a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-11-06 11:00:32 &#43;0800 CST"
itemprop="datePublished">2019-11-06</time>
@@ -337,7 +343,7 @@
<a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-10-24 09:59:11 &#43;0800 CST"
itemprop="datePublished">2019-10-24</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Redis - 数据结构
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-数据结构
</a>
</div>
</div>
diff --git a/categories/redis/index.xml b/categories/redis/index.xml
index c580042..78a60ef 100644
--- a/categories/redis/index.xml
+++ b/categories/redis/index.xml
@@ -5,12 +5,41 @@
<link>https://xiaohei.im/hugo-theme-pure/categories/redis/</link>
<description>Recent content in redis on 赵小黑的博客</description>
<generator>Hugo -- gohugo.io</generator>
- <lastBuildDate>Wed, 06 Nov 2019 19:08:56 +0800</lastBuildDate>
+ <lastBuildDate>Sat, 16 Nov 2019 14:24:40 +0800</lastBuildDate>
<atom:link href="https://xiaohei.im/hugo-theme-pure/categories/redis/index.xml" rel="self" type="application/rss+xml" />
<item>
+ <title>Redis-复制功能探索</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/replication/</link>
+ <pubDate>Sat, 16 Nov 2019 14:24:40 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/replication/</guid>
+ <description>&lt;p&gt;之前多&lt;code&gt;redis&lt;/code&gt; 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 &lt;a href=&#34;https://redis.io/topics/replication&#34;&gt;https://redis.io/topics/replication&lt;/a&gt;&lt;/p&gt;</description>
+ </item>
+
+ <item>
+ <title>Redis-事件</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/event/</link>
+ <pubDate>Thu, 14 Nov 2019 15:01:45 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/event/</guid>
+ <description>&lt;blockquote&gt;
+&lt;p&gt;&lt;strong&gt;事件驱动程序设计&lt;/strong&gt;(英语:&lt;strong&gt;Event-driven programming&lt;/strong&gt;)是一种电脑&lt;a href=&#34;https://zh.wikipedia.org/wiki/程式設計&#34;&gt;程序设计&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/模型&#34;&gt;模型&lt;/a&gt;。这种模型的程序运行流程是由用户的动作(如&lt;a href=&#34;https://zh.wikipedia.org/wiki/滑鼠&#34;&gt;鼠标&lt;/a&gt;的按键,键盘的按键动作)或者是由其他程序的&lt;a href=&#34;https://zh.wikipedia.org/wiki/訊息&#34;&gt;消息&lt;/a&gt;来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由&lt;a href=&#34;https://zh.wikipedia.org/wiki/程式設計師&#34;&gt;程序员&lt;/a&gt;来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在&lt;a href=&#34;https://zh.wikipedia.org/w/index.php?title=互動程序&amp;amp;action=edit&amp;amp;redlink=1&#34;&gt;交互程序&lt;/a&gt;(Interactive program)的情况下孕育而生的。 &lt;a href=&#34;https://zh.wikipedia.org/wiki/事件驅動程式設計&#34;&gt;&amp;ndash;wikipedia&lt;/a&gt;&lt;/p&gt;
+&lt;/blockquote&gt;</description>
+ </item>
+
+ <item>
+ <title>Redis-AOF持久化</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/aof/</link>
+ <pubDate>Fri, 08 Nov 2019 15:18:05 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/aof/</guid>
+ <description>&lt;p&gt;&lt;code&gt;RDB&lt;/code&gt; 和 &lt;code&gt;AOF&lt;/code&gt; 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 &lt;strong&gt;写命令&lt;/strong&gt; 保证数据库的状态.&lt;/p&gt;</description>
+ </item>
+
+ <item>
<title>Redis-RDB持久化</title>
<link>https://xiaohei.im/hugo-theme-pure/2019/11/rdb/</link>
<pubDate>Wed, 06 Nov 2019 19:08:56 +0800</pubDate>
@@ -47,7 +76,7 @@
</item>
<item>
- <title>Redis - 数据结构</title>
+ <title>Redis-数据结构</title>
<link>https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/</link>
<pubDate>Thu, 24 Oct 2019 09:59:11 +0800</pubDate>
diff --git a/categories/消息队列/index.html b/categories/消息队列/index.html
index 17c3b42..712c1b2 100644
--- a/categories/消息队列/index.html
+++ b/categories/消息队列/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/collections/arraylist/index.html b/collections/arraylist/index.html
new file mode 100644
index 0000000..b0f8818
--- /dev/null
+++ b/collections/arraylist/index.html
@@ -0,0 +1,631 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Collections-Arraylist - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content="" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Collections-Arraylist - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Collections-Arraylist" />
+<meta property="og:description" content="" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/collections/arraylist/" />
+<meta property="article:published_time" content="2019-08-15T13:19:18+08:00" />
+<meta property="article:modified_time" content="2019-08-15T13:19:18+08:00" />
+<meta itemprop="name" content="Collections-Arraylist">
+<meta itemprop="description" content="">
+
+
+<meta itemprop="datePublished" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="dateModified" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="wordCount" content="1001">
+
+
+
+<meta itemprop="keywords" content="collections," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Collections-Arraylist"/>
+<meta name="twitter:description" content=""/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#arraylist和linkedlist和vector的区别">ArrayList和LinkedList和Vector的区别</a></li>
+<li><a href="#synchronizedlist和vector的区别">SynchronizedList和Vector的区别</a></li>
+<li><a href="#参数">参数</a></li>
+<li><a href="#构造函数">构造函数</a></li>
+<li><a href="#主要方法">主要方法</a></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/collections/arraylist/"
+ >Collections-Arraylist</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/collections/arraylist/" class="article-date">
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/collections/arraylist/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:1001字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:2分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <h2 id="arraylist和linkedlist和vector的区别">ArrayList和LinkedList和Vector的区别</h2>
+
+<p>简单讲:
+1. ArrayList和LinkedList是线程不安全的,而Vector在增删改的操作上都有synchronized关键字修饰,是线性的,但是效率不高
+2. ArrayList和Vector都是基于数组的实现,扩容时,ArrayList扩容为1.5倍,Vector扩容为2倍,查找快,增删慢.LinkedList是一个双向链表,增删快,查找慢.
+&gt; <a href="https://blog.csdn.net/csdn15698845876/article/details/79480066">参考blog</a></p>
+
+<h2 id="synchronizedlist和vector的区别">SynchronizedList和Vector的区别</h2>
+
+<ol>
+<li>SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类。</li>
+<li>使用SynchronizedList的时候,进行遍历时要手动进行同步处理。</li>
+<li>SynchronizedList可以指定锁定的对象。
+&gt; <a href="http://www.hollischuang.com/archives/498">Hollis的blog</a></li>
+</ol>
+
+<h2 id="参数">参数</h2>
+
+<ol>
+<li>static final int DEFAULT_CAPACITY=10 默认大小</li>
+<li>transient object[] elementData; arraylist存储对象的数组.当空ArrayList第一次添加对象时,容量会扩展成<code>DEFAULT_CAPACITY</code></li>
+<li>size elementData中实际的对象数</li>
+</ol>
+
+<h2 id="构造函数">构造函数</h2>
+
+<blockquote>
+<p>看几个主要的
+1. 带初始化参数,参数违法时会抛RuntimeException</p>
+
+<pre><code class="language-java"> public ArrayList(int initialCapacity) {
+ if (initialCapacity &gt; 0) {
+ this.elementData = new Object[initialCapacity];
+ } else if (initialCapacity == 0) {
+ this.elementData = EMPTY_ELEMENTDATA;
+ } else {
+ throw new IllegalArgumentException(&quot;Illegal Capacity: &quot;+
+ initialCapacity);
+ }
+ }
+</code></pre>
+
+<ol>
+<li><p>从其他集合中导入,collection需要notNull,否则会抛<code>空指针</code></p>
+
+<pre><code class="language-java">public ArrayList(Collection&lt;? extends E&gt; c) {
+ elementData = c.toArray();
+ if ((size = elementData.length) != 0) {
+ // c.toArray might (incorrectly) not return Object[] (see 6260652)
+ if (elementData.getClass() != Object[].class)
+ elementData = Arrays.copyOf(elementData, size, Object[].class);
+ } else {
+ // replace with empty array.
+ this.elementData = EMPTY_ELEMENTDATA;
+ }
+}
+</code></pre>
+
+<h2 id="主要方法">主要方法</h2></li>
+
+<li><p>get</p>
+
+<pre><code class="language-java">public E get(int index) {
+ //判断index是否在范围内 不在会抛 IndexOutOfBoundsException
+ rangeCheck(index);
+ //获取对象值
+ return elementData(index);
+}
+</code></pre></li>
+
+<li><p>set 替换指定位置的值</p>
+
+<pre><code class="language-java">public E set(int index, E element) {
+ rangeCheck(index);//范围检查
+
+ E oldValue = elementData(index);//获取对象旧值
+ elementData[index] = element; //赋新值
+ return oldValue; //返回旧值
+}
+</code></pre></li>
+
+<li><p>add 在<code>elementData</code>尾部添加一个对象</p>
+
+<pre><code class="language-java">public boolean add(E e) {
+ //确保在容量范围内,不在则扩容
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ elementData[size++] = e;
+ return true;
+}
+
+private void ensureCapacityInternal(int minCapacity) {
+ if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
+ }
+
+ ensureExplicitCapacity(minCapacity);
+}
+
+private void ensureExplicitCapacity(int minCapacity) {
+ modCount++;//操作list的次数
+
+ //若最小容量大于 elementData的长度 则扩容
+ // overflow-conscious code
+ if (minCapacity - elementData.length &gt; 0)
+ grow(minCapacity);
+}
+private void grow(int minCapacity) {
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity &gt;&gt; 1); //扩容大概是1.5倍
+ if (newCapacity - minCapacity &lt; 0)
+ newCapacity = minCapacity;
+ if (newCapacity - MAX_ARRAY_SIZE &gt; 0)
+ newCapacity = hugeCapacity(minCapacity);
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity);
+}
+
+</code></pre></li>
+
+<li><p>add 指定位置添加对象</p>
+
+<pre><code class="language-java">public void add(int index, E element) {
+ //专门的add操作范围检查 主要是保证 0 &lt; index &lt; size
+ rangeCheckForAdd(index);
+
+ ensureCapacityInternal(size + 1); // Increments modCount!!
+ System.arraycopy(elementData, index, elementData, index + 1,
+ size - index);
+ elementData[index] = element;
+ size++;
+}
+</code></pre></li>
+</ol>
+</blockquote>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/hashmap/" title="Collections-Hashmap"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/08/001-two-sum/"
+ title="[LeetCode In Rust]001-Two Sum"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/collections/hashmap/index.html b/collections/hashmap/index.html
new file mode 100644
index 0000000..1c67a68
--- /dev/null
+++ b/collections/hashmap/index.html
@@ -0,0 +1,1210 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Collections-Hashmap - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content="" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Collections-Hashmap - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Collections-Hashmap" />
+<meta property="og:description" content="" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/collections/hashmap/" />
+<meta property="article:published_time" content="2019-08-15T13:19:18+08:00" />
+<meta property="article:modified_time" content="2019-08-15T13:19:18+08:00" />
+<meta itemprop="name" content="Collections-Hashmap">
+<meta itemprop="description" content="">
+
+
+<meta itemprop="datePublished" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="dateModified" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="wordCount" content="6419">
+
+
+
+<meta itemprop="keywords" content="collections," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Collections-Hashmap"/>
+<meta name="twitter:description" content=""/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#hashmap-hashtable-concurrenthashmap的区别">HashMap,HashTable,ConcurrentHashMap的区别?</a></li>
+<li><a href="#hashmap在不同版本之间实现的区别">HashMap在不同版本之间实现的区别?</a></li>
+<li><a href="#官方文档介绍">官方文档介绍:</a></li>
+<li><a href="#源码">源码</a></li>
+<li><a href="#1-8红黑树化源码解析">1.8红黑树化源码解析</a></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/collections/hashmap/"
+ >Collections-Hashmap</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/collections/hashmap/" class="article-date">
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/collections/hashmap/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:6419字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:13分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <blockquote>
+<p>美团的blog:<a href="https://tech.meituan.com/java_hashmap.html">https://tech.meituan.com/java_hashmap.html</a>
+参考blog: <a href="https://www.tianxiaobo.com/2018/01/18/HashMap-%E6%BA%90%E7%A0%81%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90-JDK1-8/">田小波的博客</a>
+<a href="https://www.tianxiaobo.com/2018/01/11/%E7%BA%A2%E9%BB%91%E6%A0%91%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90/">红黑树介绍</a></p>
+</blockquote>
+
+<h2 id="hashmap-hashtable-concurrenthashmap的区别">HashMap,HashTable,ConcurrentHashMap的区别?</h2>
+
+<p><strong>HashMap</strong>是非线程安全的,<strong>HashTable</strong>和<strong>ConcurrentHashMap</strong>是线程安全的.HashTable不允许null key和null Value,HashMap允许.
+ConcurrentHashMap推出之后官方推荐不要在使用HashTable作为线程安全的使用类,而是使用这个.关于<strong>ConcurrentHashMap</strong>后面再学习.
+&gt; <a href="https://juejin.im/post/5add97a46fb9a07aa212f4c0">参考blog</a></p>
+
+<h2 id="hashmap在不同版本之间实现的区别">HashMap在不同版本之间实现的区别?</h2>
+
+<blockquote>
+<p><a href="http://linfenliang.com/hashmap/2017/08/04/HashMapInJDK-6-7-8-Differ/">区别</a></p>
+</blockquote>
+
+<h2 id="官方文档介绍">官方文档介绍:</h2>
+
+<ol>
+<li>基于<code>Map</code>接口实现的哈希表.提供了所有map可选的操作,允许key为null,value为null.<code>HashMap</code>与<code>HashTable</code>基本一致,除了<code>HashMap</code> <em>线程不安全并且允许为空</em>.
+不保证有序,尤其不保证顺序一直不变(因为扩容时会rehash,基本上就顺序就重排了)</li>
+<li>假设hash分布均匀的情况下,基本的操作(<code>get/put</code>)性能很不错.迭代所需要的时间与<code>buckets</code>数量与每个<code>bukets</code>下的键值对的数量之和成正比.所以官方建议如果要求hashmap的迭代性能的话,初始的<code>capacity</code>不能太高,<code>loadFactor</code>不要太高.</li>
+
+<li><p>HashMap有两个重要的参数:<code>initial capacity</code>,<code>load factor</code>.capacity定义bucket的数量,initial capacity定义的是初始化bucket数量.load factor(中文名: <em>加载因子</em> )是判断哈希表是否需要扩容的阈值,当entries数量超过(load factor * current capacity),哈希表会触发<code>rehash</code>操作,内部数据结构会重整,buckets数量会变为之前大约两倍左右</p></li>
+
+<li><p>通常情况下,load factor 默认<code>0.75f</code>,在时间空间上是很平衡的.值偏高时,空间减少,查找时间上升了(影响大部分的操作,get/put之类的),在设置初始容量时,需要考虑到预期的entries数量和加载因子,以便最小化rehash的数量.如果初始化的容量大于最大数量的entries除以加载因子,不会发生rehash操作.</p></li>
+
+<li><p>如果有大量的键值对存到hashmap中,那么创建一个足够大的hashmap来存储要比让他自动rehash扩容来存储的性能要好很多.注意:具有相同hashcode的多个key肯定会影响哈希表的性能.为了改善这种影响,当key是Comparable类型时,可以通过key之间的比较顺序来打破这种关系.</p></li>
+
+<li><p>注意hashmap是<code>Non synchronized</code>,即 <em>非线程安全</em>.如果多线程并发访问hashmap,并且至少有一个线程操作map的结构,在外部必须<code>synchronized</code>.(结构修改是指任何关于add或delte的操作,仅仅只是修改key关联的value时则不属于结构修改).通常在将object封装进map做synchronized操作</p></li>
+
+<li><p>如果不存在上面的objects,那这个map需要被Collections.synchronizedMap包装下.最好在创建的时候就做好,防止偶然的并发访问.</p>
+
+<pre><code class="language-java">Map m = Collections.synchronizedMap(new HashMap(...));
+</code></pre></li>
+
+<li><p>迭代器的所有方法都是<code>fail-fast</code>,如果迭代器创建后,在迭代器里的结构操作必须通过迭代器的方法来操作,否则会抛<code>ConcurrentModificationException</code>.因此,面对并发修改,迭代器会快速而干净的失败,而不是在未来的不确定时间冒任意非确定行为的风险.</p></li>
+
+<li><p>请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证. 快速失败迭代器会尽最大努力抛出ConcurrentModificationException. 因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的快速失败行为应该仅用于检测错误.</p></li>
+</ol>
+
+<h2 id="源码">源码</h2>
+
+<pre><code class="language-java"> /**
+ * The default initial capacity - MUST be a power of two.
+ * 默认的容量16 必须是2的n次方
+ */
+ static final int DEFAULT_INITIAL_CAPACITY = 1 &lt;&lt; 4; // aka 16
+
+ /**
+ * 最大的容量限制
+ * The maximum capacity, used if a higher value is implicitly specified
+ * by either of the constructors with arguments.
+ * MUST be a power of two &lt;= 1&lt;&lt;30.
+ */
+ static final int MAXIMUM_CAPACITY = 1 &lt;&lt; 30;
+
+ /**
+ * 默认的加载因子
+ * The load factor used when none specified in constructor.
+ */
+ static final float DEFAULT_LOAD_FACTOR = 0.75f;
+ /**
+ * 大于这个值转红黑树
+ * The bin count threshold for using a tree rather than list for a
+ * bin. Bins are converted to trees when adding an element to a
+ * bin with at least this many nodes. The value must be greater
+ * than 2 and should be at least 8 to mesh with assumptions in
+ * tree removal about conversion back to plain bins upon
+ * shrinkage.
+ */
+ static final int TREEIFY_THRESHOLD = 8;
+
+ /**
+ * 大于这个值小于 TREEIFY_THRESHOLD 不转树
+ * The bin count threshold for untreeifying a (split) bin during a
+ * resize operation. Should be less than TREEIFY_THRESHOLD, and at
+ * most 6 to mesh with shrinkage detection under removal.
+ */
+ static final int UNTREEIFY_THRESHOLD = 6;
+
+ /**
+ * hashmap整体容量大于这个值时才能树化
+ * The smallest table capacity for which bins may be treeified.
+ * (Otherwise the table is resized if too many nodes in a bin.)
+ * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
+ * between resizing and treeification thresholds.
+ */
+ static final int MIN_TREEIFY_CAPACITY = 64;
+
+ /**
+ * node节点
+ * Basic hash bin node, used for most entries. (See below for
+ * TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
+ */
+ static class Node&lt;K,V&gt; implements Map.Entry&lt;K,V&gt; {
+ final int hash;
+ final K key;
+ V value;
+ Node&lt;K,V&gt; next;
+
+ Node(int hash, K key, V value, Node&lt;K,V&gt; next) {
+ this.hash = hash;
+ this.key = key;
+ this.value = value;
+ this.next = next;
+ }
+
+ public final K getKey() { return key; }
+ public final V getValue() { return value; }
+ public final String toString() { return key + &quot;=&quot; + value; }
+
+ public final int hashCode() {
+ return Objects.hashCode(key) ^ Objects.hashCode(value);
+ }
+
+ public final V setValue(V newValue) {
+ V oldValue = value;
+ value = newValue;
+ return oldValue;
+ }
+
+ public final boolean equals(Object o) {
+ if (o == this)
+ return true;
+ if (o instanceof Map.Entry) {
+ Map.Entry&lt;?,?&gt; e = (Map.Entry&lt;?,?&gt;)o;
+ if (Objects.equals(key, e.getKey()) &amp;&amp;
+ Objects.equals(value, e.getValue()))
+ return true;
+ }
+ return false;
+ }
+ }
+
+ //hashMap中的静态方法
+ /**
+ * hash方法详解 blog:http://www.hollischuang.com/archives/2091
+ * 扰动算法--使hash分布更均匀
+ */
+ static final int hash(Object key) {
+ int h;
+ return (key == null) ? 0 : (h = key.hashCode()) ^ (h &gt;&gt;&gt; 16);
+ }
+
+ //取模运算,获得对象存储到bukets的下标
+ //实际上就是取模,一般取模使用% 但是考虑到效率问题,采用位运算
+ //X % 2^n = X &amp; (2^n-1) 这也是为什么hashmap容量为2的n次方的原因
+ static int indexFor(int h, int length) {
+ return h &amp; (length-1);
+ }
+ //返回hashmap的容量 2的n次方 很巧妙的位运算
+ static final int tableSizeFor(int cap) {
+ int n = cap - 1;
+ n |= n &gt;&gt;&gt; 1;
+ n |= n &gt;&gt;&gt; 2;
+ n |= n &gt;&gt;&gt; 4;
+ n |= n &gt;&gt;&gt; 8;
+ n |= n &gt;&gt;&gt; 16;
+ return (n &lt; 0) ? 1 : (n &gt;= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
+ }
+
+ /**
+ * 参数都用transient不让序列化的原因:https://segmentfault.com/q/1010000000630486
+ */
+
+ //bukets hashmap是链表加数组的结构.此为数组
+ transient Node&lt;K,V&gt;[] table;
+
+ //保存键值对的Entry
+ transient Set&lt;Map.Entry&lt;K,V&gt;&gt; entrySet;
+
+ //hashmap的size
+ transient int size;
+
+ //结构操作次数 可用于快速失败的比较条件 例如并发操作时
+ transient int modCount;
+
+ //resize的临界点: capacity * load factor
+ int threshold;
+
+ //加载因子
+ final float loadFactor;
+
+ //公有操作方法
+
+ //构造方法
+ /**
+ * 根据 initial capactity 和 loadFactor创建空的hashmap
+ * Constructs an empty &lt;tt&gt;HashMap&lt;/tt&gt; with the specified initial
+ * capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity
+ * @param loadFactor the load factor
+ * @throws IllegalArgumentException if the initial capacity is negative
+ * or the load factor is nonpositive
+ */
+ public HashMap(int initialCapacity, float loadFactor) {
+ //校验initialCapacity
+ if (initialCapacity &lt; 0)
+ throw new IllegalArgumentException(&quot;Illegal initial capacity: &quot; +
+ initialCapacity);
+ //容量校验
+ if (initialCapacity &gt; MAXIMUM_CAPACITY)
+ initialCapacity = MAXIMUM_CAPACITY;
+ //校验loadFactor isNaN--&gt; 是否是一个number Not-a-Number
+ if (loadFactor &lt;= 0 || Float.isNaN(loadFactor))
+ throw new IllegalArgumentException(&quot;Illegal load factor: &quot; +
+ loadFactor);
+ //加载因子赋值
+ this.loadFactor = loadFactor;
+ //扩容阈值赋值 2的n次方
+ this.threshold = tableSizeFor(initialCapacity);
+ }
+
+ /**
+ * 通过initialCapacity赋值
+ * Constructs an empty &lt;tt&gt;HashMap&lt;/tt&gt; with the specified initial
+ * capacity and the default load factor (0.75).
+ *
+ * @param initialCapacity the initial capacity.
+ * @throws IllegalArgumentException if the initial capacity is negative.
+ */
+ public HashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * 根据默认容量和默认加载因子创建空的hashmap
+ * Constructs an empty &lt;tt&gt;HashMap&lt;/tt&gt; with the default initial capacity
+ * (16) and the default load factor (0.75).
+ */
+ public HashMap() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
+ }
+
+ /**
+ * 根据传进来的map创建一个新的hashMap
+ * initialCapacity 足以装下参数map的数量
+ * loadFactor使用默认值
+ * Constructs a new &lt;tt&gt;HashMap&lt;/tt&gt; with the same mappings as the
+ * specified &lt;tt&gt;Map&lt;/tt&gt;. The &lt;tt&gt;HashMap&lt;/tt&gt; is created with
+ * default load factor (0.75) and an initial capacity sufficient to
+ * hold the mappings in the specified &lt;tt&gt;Map&lt;/tt&gt;.
+ *
+ * @param m the map whose mappings are to be placed in this map
+ * @throws NullPointerException if the specified map is null
+ */
+ public HashMap(Map&lt;? extends K, ? extends V&gt; m) {
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ putMapEntries(m, false);
+ }
+
+ /**
+ * Implements Map.putAll and Map constructor
+ *
+ * @param m the map
+ * @param evict false when initially constructing this map, else
+ * true (relayed to method afterNodeInsertion).
+ * evict 初始化构建map时 为false 其他情况下为true
+ */
+ final void putMapEntries(Map&lt;? extends K, ? extends V&gt; m, boolean evict) {
+ int s = m.size();
+ if (s &gt; 0) {
+ //初次创建hashmap
+ if (table == null) { // pre-size
+ //计算m所需要的容量
+ float ft = ((float)s / loadFactor) + 1.0F;
+ //获得真实的容量
+ int t = ((ft &lt; (float)MAXIMUM_CAPACITY) ?
+ (int)ft : MAXIMUM_CAPACITY);
+ //如果比默认的阈值大则计算该 t 对应的capacity
+ if (t &gt; threshold)
+ threshold = tableSizeFor(t);
+ }
+ else if (s &gt; threshold) // 如果是table不为null 即是后续往map中添加 如果s &gt; 阈值就要重置map了
+ resize();//resize操作 后面介绍
+ //确定容量后put操作
+ for (Map.Entry&lt;? extends K, ? extends V&gt; e : m.entrySet()) {
+ K key = e.getKey();
+ V value = e.getValue();
+ putVal(hash(key), key, value, false, evict);//
+ }
+ }
+ }
+
+ /*主要调用 putVal */
+ public V put(K key, V value) {
+ return putVal(hash(key), key, value, false, true);
+ }
+
+ /**
+ * put 操作
+ * Implements Map.put and related methods
+ *
+ * @param hash hash for key
+ * @param key the key
+ * @param value the value to put
+ * @param onlyIfAbsent if true, don't change existing value 不存在才put&lt;D-[&gt;
+ * @param evict if false, the table is in creation mode.
+ * @return previous value, or null if none
+ */
+ final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
+ boolean evict) {
+ Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; p; int n, i;
+ //若是新建map的情况下 resize创建指定长度的table
+ if ((tab = table) == null || (n = tab.length) == 0)
+ n = (tab = resize()).length;
+ //取模计算该key对应的数组下标 并判断该坐标下的对象是否为null
+ //为null时创建一个新node存入tab[i]
+ if ((p = tab[i = (n - 1) &amp; hash]) == null)
+ tab[i] = newNode(hash, key, value, null);
+ else {//tab[i] != null
+ Node&lt;K,V&gt; e; K k;
+ //如果p与存入的key完全相同
+ if (p.hash == hash &amp;&amp;
+ ((k = p.key) == key || (key != null &amp;&amp; key.equals(k))))
+ e = p;
+ else if (p instanceof TreeNode)
+ //如果是红黑树节点 调用putTreeVal
+ e = ((TreeNode&lt;K,V&gt;)p).putTreeVal(this, tab, hash, key, value);
+ else {
+ //普通的put
+ //binCount记录了链表的长度
+ for (int binCount = 0; ; ++binCount) {
+ //如果当前node的next==null说明就可以往该链上添加一个节点
+ if ((e = p.next) == null) {
+ //新建node接到p.next下面
+ p.next = newNode(hash, key, value, null);
+ //如果binCount大于设定的红黑树化阈值
+ if (binCount &gt;= TREEIFY_THRESHOLD - 1) // -1 for 1st
+ treeifyBin(tab, hash);//红黑树化
+ break;
+ }
+ //如果key与链表中的任意node完全相同break
+ if (e.hash == hash &amp;&amp;
+ ((k = e.key) == key || (key != null &amp;&amp; key.equals(k))))
+ break;
+ p = e;
+ }
+ }
+ //如果存在该key
+ if (e != null) { // existing mapping for key
+ V oldValue = e.value;//获得旧值
+ if (!onlyIfAbsent || oldValue == null)//若没有设置不存在才put或者oldValue=null
+ e.value = value;//赋新值
+ afterNodeAccess(e);//LinkedHashMap操作
+ return oldValue;//返回旧值
+ }
+ }
+ ++modCount;
+ if (++size &gt; threshold)//是否需要扩容
+ resize();
+ afterNodeInsertion(evict);//LinkedHashMap操作
+ return null;
+ }
+
+ /**
+ * 扩容操作
+ * 若是初始化则根据initialCapacity创建一个table
+ * 否则,扩容为2的n次方倍
+ * Initializes or doubles table size. If null, allocates in
+ * accord with initial capacity target held in field threshold.
+ * Otherwise, because we are using power-of-two expansion, the
+ * elements from each bin must either stay at same index, or move
+ * with a power of two offset in the new table.
+ *
+ * @return the table
+ */
+ final Node&lt;K,V&gt;[] resize() {
+ Node&lt;K,V&gt;[] oldTab = table;
+ int oldCap = (oldTab == null) ? 0 : oldTab.length;
+ int oldThr = threshold;
+ int newCap, newThr = 0;
+ if (oldCap &gt; 0) {
+ //超过最大值不会再扩容了
+ if (oldCap &gt;= MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return oldTab;
+ }
+ else if ((newCap = oldCap &lt;&lt; 1) &lt; MAXIMUM_CAPACITY &amp;&amp;
+ oldCap &gt;= DEFAULT_INITIAL_CAPACITY)
+ newThr = oldThr &lt;&lt; 1; // double threshold 扩成两倍
+ }
+ else if (oldThr &gt; 0) // initial capacity was placed in threshold
+ newCap = oldThr;
+ else { // 默认配置 zero initial threshold signifies using defaults
+ newCap = DEFAULT_INITIAL_CAPACITY;
+ newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
+ }
+ if (newThr == 0) {
+ //计算新的阈值
+ float ft = (float)newCap * loadFactor;
+ newThr = (newCap &lt; MAXIMUM_CAPACITY &amp;&amp; ft &lt; (float)MAXIMUM_CAPACITY ?
+ (int)ft : Integer.MAX_VALUE);
+ }
+ threshold = newThr;
+ @SuppressWarnings({&quot;rawtypes&quot;,&quot;unchecked&quot;})
+ Node&lt;K,V&gt;[] newTab = (Node&lt;K,V&gt;[])new Node[newCap];
+ table = newTab;
+ if (oldTab != null) {
+ //把old buket 移到新的bukets里
+ for (int j = 0; j &lt; oldCap; ++j) {
+ Node&lt;K,V&gt; e;
+ if ((e = oldTab[j]) != null) {
+ oldTab[j] = null;
+ if (e.next == null)//直接添加
+ newTab[e.hash &amp; (newCap - 1)] = e;
+ else if (e instanceof TreeNode)
+ ((TreeNode&lt;K,V&gt;)e).split(this, newTab, j, oldCap);
+ else { // preserve order
+ Node&lt;K,V&gt; loHead = null, loTail = null;
+ Node&lt;K,V&gt; hiHead = null, hiTail = null;
+ Node&lt;K,V&gt; next;
+ do {
+ next = e.next;
+ //这个取模很精辟 请结合美团的blog resize 1.8优化学习
+ //因为扩容是2倍扩容,二进制中相当于左移一位
+ /**
+ * 假设一次扩容
+ * 扩容前 oldCap = 00010000 oldCap - 1 = 00001111
+ * 扩容后 newCap = 00100000 newCap - 1 = 00011111
+ * 可以看出扩容后 newCap-1 在高位多了1
+ * 计算index时 hash &amp; n-1 = 原位置 + oldCap
+ * 所以只需要判断hash &amp; oldCap是否为1
+ * 为1则把该node的位置移到 oldCap+原位置
+ * 为 0 还在原位置
+ */
+ if ((e.hash &amp; oldCap) == 0) {//为0说明位置没有变
+ if (loTail == null)//第一次添加时loHead=e
+ loHead = e;
+ else
+ loTail.next = e;//直接往后插入
+ loTail = e;
+ }
+ else {//为1 说明位置会+oldCap长度
+ if (hiTail == null)
+ hiHead = e;//头节点初始化
+ else
+ hiTail.next = e;//直接插入
+ hiTail = e;
+ }
+ } while ((e = next) != null);
+ if (loTail != null) {//放在原位置上
+ loTail.next = null;
+ newTab[j] = loHead;
+ }
+ if (hiTail != null) {//放在原位置+oldCap上
+ hiTail.next = null;
+ newTab[j + oldCap] = hiHead;
+ }
+ }
+ }
+ }
+ }
+ return newTab;
+ }
+
+ /**
+ * get操作
+ * 为null时返回null 这个要注意下
+ */
+ public V get(Object key) {
+ Node&lt;K,V&gt; e;
+ return (e = getNode(hash(key), key)) == null ? null : e.value;
+ }
+
+
+ /**
+ * Implements Map.get and related methods
+ * get方法
+ * 主要是 key相等 或者 key equals的比较
+ * @param hash hash for key
+ * @param key the key
+ * @return the node, or null if none
+ */
+ final Node&lt;K,V&gt; getNode(int hash, Object key) {
+ Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; first, e; int n; K k;
+ if ((tab = table) != null &amp;&amp; (n = tab.length) &gt; 0 &amp;&amp;
+ (first = tab[(n - 1) &amp; hash]) != null) {//获得节点
+ if (first.hash == hash &amp;&amp; // always check first node
+ ((k = first.key) == key || (key != null &amp;&amp; key.equals(k))))
+ return first;
+ if ((e = first.next) != null) {
+ if (first instanceof TreeNode)//树节点
+ return ((TreeNode&lt;K,V&gt;)first).getTreeNode(hash, key);
+ do {
+ if (e.hash == hash &amp;&amp;
+ ((k = e.key) == key || (key != null &amp;&amp; key.equals(k))))
+ return e;
+ } while ((e = e.next) != null);
+ }
+ }
+ return null;
+ }
+
+
+
+
+</code></pre>
+
+<h2 id="1-8红黑树化源码解析">1.8红黑树化源码解析</h2>
+
+<pre><code class="language-java"> /**
+ * TreeNode extends LinkedHashMap.Entry
+ * LinkedHashMap.Entry extends HashMap.Node
+ */
+
+ static final class TreeNode&lt;K,V&gt; extends LinkedHashMap.Entry&lt;K,V&gt; {
+ TreeNode&lt;K,V&gt; parent; // red-black tree links 红黑树父节点
+ TreeNode&lt;K,V&gt; left;
+ TreeNode&lt;K,V&gt; right;
+ TreeNode&lt;K,V&gt; prev; // needed to unlink next upon deletion 删除的时候用来连接前后
+ boolean red;//红还是黑
+ TreeNode(int hash, K key, V val, Node&lt;K,V&gt; next) {
+ super(hash, key, val, next);
+ }
+ }
+
+
+ /**树化
+ * putVal里有用到
+ * 将链表重置为红黑树并放到该hash映射的tab下,如果tab过下则resize
+ * Replaces all linked nodes in bin at index for given hash unless
+ * table is too small, in which case resizes instead.
+ */
+ final void treeifyBin(Node&lt;K,V&gt;[] tab, int hash) {
+ int n, index; Node&lt;K,V&gt; e;
+ if (tab == null || (n = tab.length) &lt; MIN_TREEIFY_CAPACITY)//小于最小树化的容量时不树化而resize capacity为64,
+ resize();
+ else if ((e = tab[index = (n - 1) &amp; hash]) != null) {
+ TreeNode&lt;K,V&gt; hd = null, tl = null;//头尾节点
+ do {
+ TreeNode&lt;K,V&gt; p = replacementTreeNode(e, null);//这个就是返回一个新建的TreeNode对象,内容为e
+ if (tl == null)//确定是头结点
+ hd = p;//标记头结点
+ else {//非头结点就首尾连接
+ p.prev = tl;
+ tl.next = p;
+ }
+ tl = p;//尾节点一直为p
+ } while ((e = e.next) != null);//遍历链表 其实此时形成也还算是个链表
+ if ((tab[index] = hd) != null)//将该treeNode挂到table下
+ hd.treeify(tab);//完成红黑树化
+ }
+ }
+
+ /**
+ * Forms tree of the nodes linked from this node.
+ * @return root of tree
+ */
+ final void treeify(Node&lt;K,V&gt;[] tab) {
+ TreeNode&lt;K,V&gt; root = null;
+ for (TreeNode&lt;K,V&gt; x = this, next; x != null; x = next) {//x 从当前节点开始(从treeifyBin里调用看是头结点)
+ next = (TreeNode&lt;K,V&gt;)x.next;//获取下个节点
+ x.left = x.right = null;
+ if (root == null) {//设置root节点并给他黑色
+ x.parent = null;
+ x.red = false;
+ root = x;
+ }
+ else {
+ K k = x.key;
+ int h = x.hash;
+ Class&lt;?&gt; kc = null;
+ //遍历所有节点与当前节点x比较 调整位置 有点像冒泡排序
+ for (TreeNode&lt;K,V&gt; p = root;;) {
+ int dir, ph;
+ K pk = p.key;
+ //比较hash值
+ if ((ph = p.hash) &gt; h)
+ dir = -1;
+ else if (ph &lt; h)
+ dir = 1;
+ else if ((kc == null &amp;&amp;
+ (kc = comparableClassFor(k)) == null) ||
+ (dir = compareComparables(kc, k, pk)) == 0)
+ dir = tieBreakOrder(k, pk);
+
+ //根据dir判断x是p的左孩子 还是 右孩子
+ TreeNode&lt;K,V&gt; xp = p;
+ if ((p = (dir &lt;= 0) ? p.left : p.right) == null) {
+ x.parent = xp;
+ if (dir &lt;= 0)
+ xp.left = x;
+ else
+ xp.right = x;
+ //平衡节点
+ root = balanceInsertion(root, x);
+ break;
+ }
+ }
+ }
+ }
+ moveRootToFront(tab, root);
+ }
+
+ /**
+ * Returns a list of non-TreeNodes replacing those linked from
+ * this node.
+ */
+ final Node&lt;K,V&gt; untreeify(HashMap&lt;K,V&gt; map) {
+ Node&lt;K,V&gt; hd = null, tl = null;
+ for (Node&lt;K,V&gt; q = this; q != null; q = q.next) {
+ Node&lt;K,V&gt; p = map.replacementNode(q, null);
+ if (tl == null)
+ hd = p;
+ else
+ tl.next = p;
+ tl = p;
+ }
+ return hd;
+ }
+
+ /**
+ * 红黑树版put操作
+ * Tree version of putVal.
+ */
+ final TreeNode&lt;K,V&gt; putTreeVal(HashMap&lt;K,V&gt; map, Node&lt;K,V&gt;[] tab,
+ int h, K k, V v) {
+ Class&lt;?&gt; kc = null;
+ boolean searched = false;
+ TreeNode&lt;K,V&gt; root = (parent != null) ? root() : this;//每次从根节点遍历
+ for (TreeNode&lt;K,V&gt; p = root;;) {
+ int dir, ph; K pk;
+ if ((ph = p.hash) &gt; h)
+ dir = -1;
+ else if (ph &lt; h)
+ dir = 1;
+ else if ((pk = p.key) == k || (k != null &amp;&amp; k.equals(pk)))
+ //如果当前节点key相同或equals 返回
+ return p;
+ else if ((kc == null &amp;&amp;
+ (kc = comparableClassFor(k)) == null) ||
+ (dir = compareComparables(kc, k, pk)) == 0) {
+ //hash值如果相等 但类不相同,只能挨个对比左右孩子
+ if (!searched) {
+ TreeNode&lt;K,V&gt; q, ch;
+ searched = true;
+ if (((ch = p.left) != null &amp;&amp;
+ (q = ch.find(h, k, kc)) != null) ||
+ ((ch = p.right) != null &amp;&amp;
+ (q = ch.find(h, k, kc)) != null))
+ return q;
+ }
+ //哈希值相等 但键无法比较 只能通过其他方法比较
+ dir = tieBreakOrder(k, pk);
+ }
+
+ //得到两个节点的大小关系 即dir的值时
+ //并判断只有在左孩子或右孩子不能
+ TreeNode&lt;K,V&gt; xp = p;
+ if ((p = (dir &lt;= 0) ? p.left : p.right) == null) {
+ Node&lt;K,V&gt; xpn = xp.next;
+ TreeNode&lt;K,V&gt; x = map.newTreeNode(h, k, v, xpn);
+ if (dir &lt;= 0)
+ xp.left = x;
+ else
+ xp.right = x;
+ xp.next = x;
+ x.parent = x.prev = xp;
+ if (xpn != null)
+ ((TreeNode&lt;K,V&gt;)xpn).prev = x;
+ //平衡二叉树
+ moveRootToFront(tab, balanceInsertion(root, x));
+ return null;
+ }
+ }
+ }
+
+ /** 查找操作 传入 hash值 和 key值
+ * Calls find for root node.
+ */
+ final TreeNode&lt;K,V&gt; getTreeNode(int h, Object k) {
+ return ((parent != null) ? root() : this).find(h, k, null);//判断从当前节点还是root节点开始查找
+ }
+
+
+ /**
+ * Finds the node starting at root p with the given hash and key.
+ * The kc argument caches comparableClassFor(key) upon first use
+ * comparing keys.
+ */
+ final TreeNode&lt;K,V&gt; find(int h, Object k, Class&lt;?&gt; kc) {
+ TreeNode&lt;K,V&gt; p = this;
+ do {
+ int ph, dir; K pk;
+ TreeNode&lt;K,V&gt; pl = p.left, pr = p.right, q;
+ //根据hash值查找 当前节点hash值大于h则 查左孩子 否则右孩子 当key相等或者equal时返回
+ if ((ph = p.hash) &gt; h)
+ p = pl;
+ else if (ph &lt; h)
+ p = pr;
+ else if ((pk = p.key) == k || (k != null &amp;&amp; k.equals(pk)))
+ return p;
+ else if (pl == null)
+ p = pr;
+ else if (pr == null)
+ p = pl;
+ else if ((kc != null ||
+ (kc = comparableClassFor(k)) != null) &amp;&amp;
+ (dir = compareComparables(kc, k, pk)) != 0)
+ p = (dir &lt; 0) ? pl : pr;
+ else if ((q = pr.find(h, k, kc)) != null)//不相等则从子树继续查找
+ return q;
+ else
+ p = pl;
+ } while (p != null);
+ return null;
+ }
+
+</code></pre>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/hashset/" title="Collections-Hashset"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/arraylist/"
+ title="Collections-Arraylist"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/collections/hashset/index.html b/collections/hashset/index.html
new file mode 100644
index 0000000..9d47833
--- /dev/null
+++ b/collections/hashset/index.html
@@ -0,0 +1,568 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Collections-Hashset - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content="" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Collections-Hashset - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Collections-Hashset" />
+<meta property="og:description" content="" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/collections/hashset/" />
+<meta property="article:published_time" content="2019-08-15T13:19:18+08:00" />
+<meta property="article:modified_time" content="2019-08-15T13:19:18+08:00" />
+<meta itemprop="name" content="Collections-Hashset">
+<meta itemprop="description" content="">
+
+
+<meta itemprop="datePublished" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="dateModified" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="wordCount" content="272">
+
+
+
+<meta itemprop="keywords" content="collections," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Collections-Hashset"/>
+<meta name="twitter:description" content=""/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#介绍">介绍</a></li>
+<li><a href="#参数">参数</a></li>
+<li><a href="#常用方法">常用方法</a></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/collections/hashset/"
+ >Collections-Hashset</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/collections/hashset/" class="article-date">
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/collections/hashset/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:272字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:1分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <blockquote>
+<p><a href="http://cmsblogs.com/?p=599">HashSet详解</a></p>
+</blockquote>
+
+<h2 id="介绍">介绍</h2>
+
+<ol>
+<li><p>基于<code>HashMap</code>保存元素不保证有序,不包含<code>重复</code>元素,允许<code>null</code>值.主要继承关系如图:</p>
+
+<pre><code class="language-mermaid">graph BT
+HashSet--&gt;AbstractSet
+HashSet-.-&gt;Set
+AbstractSet-.-&gt;Set
+AbstractSet--&gt;AbstractCollection
+AbstractCollection-.-&gt;Collection
+</code></pre></li>
+</ol>
+
+<h2 id="参数">参数</h2>
+
+<pre><code class="language-java">static final long serialVersionUID = -5024744406713321676L;
+
+//底层存储用的hashMap
+private transient HashMap&lt;E,Object&gt; map;
+
+//定义一个Object对象作为HashMap的value
+private static final Object PRESENT = new Object();
+
+</code></pre>
+
+<h2 id="常用方法">常用方法</h2>
+
+<p>由于底层是<code>hashmap</code>存储的,所以基本是一样的.没什么区别.实现不重复插入是通过比较put操作的返回值是不是null</p>
+
+<pre><code class="language-java">
+public Iterator&lt;E&gt; iterator() {
+ return map.keySet().iterator();
+}
+
+public int size() {
+ return map.size();
+}
+
+public boolean isEmpty() {
+ return map.isEmpty();
+}
+
+public boolean contains(Object o) {
+ return map.containsKey(o);
+}
+
+public boolean add(E e) {
+ return map.put(e, PRESENT)==null;
+}
+
+public boolean remove(Object o) {
+ return map.remove(o)==PRESENT;
+}
+
+public void clear() {
+ map.clear();
+}
+
+@SuppressWarnings(&quot;unchecked&quot;)
+public Object clone() {
+ try {
+ HashSet&lt;E&gt; newSet = (HashSet&lt;E&gt;) super.clone();
+ newSet.map = (HashMap&lt;E, Object&gt;) map.clone();
+ return newSet;
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError(e);
+ }
+}
+</code></pre>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/" title="Collections-Linkedhashmap"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/hashmap/"
+ title="Collections-Hashmap"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/collections/index.html b/collections/index.html
new file mode 100644
index 0000000..ab8fb3c
--- /dev/null
+++ b/collections/index.html
@@ -0,0 +1,406 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ 赵小黑的博客</title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>赵小黑的博客</title>
+
+
+ <link rel="alternate" type="application/rss+xml" href="https://xiaohei.im/hugo-theme-pure/collections/index.xml" title="赵小黑的博客" />
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Collections" />
+<meta property="og:description" content="" />
+<meta property="og:type" content="website" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/collections/" />
+
+<meta property="og:updated_time" content="2019-08-15T13:19:18+08:00" />
+<meta itemprop="name" content="Collections">
+<meta itemprop="description" content="">
+
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Collections"/>
+<meta name="twitter:description" content=""/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+<main class="main" role="main">
+ <article class="content article article-archives article-type-list" itemscope="">
+ <header class="article-header">
+ <h1 itemprop="title">Collections</h1>
+ <p class="text-muted">共 7 篇文章</p>
+ </header>
+ <div class="article-body">
+
+
+ <section class="panel panel-default b-no">
+ <div class="panel-heading" role="tab">
+ <h3 class="panel-title">
+ <a data-toggle="collapse" href="#collapse2019" aria-expanded="true">
+ <i class="icon icon-calendar-plus text-active"></i><i class="icon icon-calendar-minus text"></i>
+ &nbsp;2019
+ </a>
+ </h3>
+ </div>
+ <div id="collapse2019" class="panel-collapse collapse in" role="tabpanel"
+ aria-labelledby="heading2019">
+ <div class="panel-body">
+ <div class="collection">
+<a href="https://xiaohei.im/hugo-theme-pure/collections/arraylist/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
+ itemprop="datePublished">2019-08-15</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Collections-Arraylist
+</a>
+
+<a href="https://xiaohei.im/hugo-theme-pure/collections/hashmap/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
+ itemprop="datePublished">2019-08-15</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Collections-Hashmap
+</a>
+
+<a href="https://xiaohei.im/hugo-theme-pure/collections/hashset/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
+ itemprop="datePublished">2019-08-15</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Collections-Hashset
+</a>
+
+<a href="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
+ itemprop="datePublished">2019-08-15</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Collections-Linkedhashmap
+</a>
+
+<a href="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
+ itemprop="datePublished">2019-08-15</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Collections-Linkedlist
+</a>
+
+<a href="https://xiaohei.im/hugo-theme-pure/collections/treemap/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
+ itemprop="datePublished">2019-08-15</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Collections-Treemap
+</a>
+
+<a href="https://xiaohei.im/hugo-theme-pure/collections/treeset/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
+ itemprop="datePublished">2019-08-15</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Collections-Treeset
+</a>
+ </div>
+ </div></div></div></section>
+ </article>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/collections/index.xml b/collections/index.xml
new file mode 100644
index 0000000..c2e73e9
--- /dev/null
+++ b/collections/index.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
+ <channel>
+ <title>Collections on 赵小黑的博客</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/</link>
+ <description>Recent content in Collections on 赵小黑的博客</description>
+ <generator>Hugo -- gohugo.io</generator>
+ <lastBuildDate>Thu, 15 Aug 2019 13:19:18 +0800</lastBuildDate>
+
+ <atom:link href="https://xiaohei.im/hugo-theme-pure/collections/index.xml" rel="self" type="application/rss+xml" />
+
+
+ <item>
+ <title>Collections-Arraylist</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/arraylist/</link>
+ <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/arraylist/</guid>
+ <description></description>
+ </item>
+
+ <item>
+ <title>Collections-Hashmap</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/hashmap/</link>
+ <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/hashmap/</guid>
+ <description></description>
+ </item>
+
+ <item>
+ <title>Collections-Hashset</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/hashset/</link>
+ <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/hashset/</guid>
+ <description></description>
+ </item>
+
+ <item>
+ <title>Collections-Linkedhashmap</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/</link>
+ <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/</guid>
+ <description></description>
+ </item>
+
+ <item>
+ <title>Collections-Linkedlist</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/linkedlist/</link>
+ <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/linkedlist/</guid>
+ <description></description>
+ </item>
+
+ <item>
+ <title>Collections-Treemap</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/treemap/</link>
+ <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/treemap/</guid>
+ <description></description>
+ </item>
+
+ <item>
+ <title>Collections-Treeset</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/treeset/</link>
+ <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/treeset/</guid>
+ <description></description>
+ </item>
+
+ </channel>
+</rss>
diff --git a/collections/linkedhashmap/index.html b/collections/linkedhashmap/index.html
new file mode 100644
index 0000000..ebb8bab
--- /dev/null
+++ b/collections/linkedhashmap/index.html
@@ -0,0 +1,648 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Collections-Linkedhashmap - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content="" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Collections-Linkedhashmap - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Collections-Linkedhashmap" />
+<meta property="og:description" content="" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/" />
+<meta property="article:published_time" content="2019-08-15T13:19:18+08:00" />
+<meta property="article:modified_time" content="2019-08-15T13:19:18+08:00" />
+<meta itemprop="name" content="Collections-Linkedhashmap">
+<meta itemprop="description" content="">
+
+
+<meta itemprop="datePublished" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="dateModified" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="wordCount" content="1027">
+
+
+
+<meta itemprop="keywords" content="collections," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Collections-Linkedhashmap"/>
+<meta name="twitter:description" content=""/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#介绍">介绍</a></li>
+<li><a href="#源码介绍">源码介绍</a>
+<ul>
+<li><a href="#entry">Entry</a></li>
+<li><a href="#变量">变量</a></li>
+<li><a href="#构造参数">构造参数</a></li>
+<li><a href="#基本操作">基本操作</a></li>
+</ul></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/collections/linkedhashmap/"
+ >Collections-Linkedhashmap</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/" class="article-date">
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/collections/linkedhashmap/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:1027字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:3分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <blockquote>
+<p>参考blog:<a href="https://www.tianxiaobo.com/2018/01/24/LinkedHashMap-%E6%BA%90%E7%A0%81%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90%EF%BC%88JDK1-8%EF%BC%89/">田小波的blog</a></p>
+</blockquote>
+
+<h2 id="介绍">介绍</h2>
+
+<p>LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条<code>双向链表</code>,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题。除此之外,LinkedHashMap 对访问顺序也提供了相关支持。在一些场景下,该特性很有用,比如缓存。在实现上,LinkedHashMap 很多方法直接继承自 HashMap,仅为维护双向链表覆写了部分方法。还提供了一些增删改查操作时的回调方法.</p>
+
+<h2 id="源码介绍">源码介绍</h2>
+
+<h3 id="entry">Entry</h3>
+
+<p>LinkedHashMap的节点 LinkedHashMap.Entry继承了hashMap的Node 增加了before和after两个节点,用于维护链表有序</p>
+
+<pre><code class="language-java"> static class Entry&lt;K,V&gt; extends HashMap.Node&lt;K,V&gt; {
+ Entry&lt;K,V&gt; before, after;
+ Entry(int hash, K key, V value, Node&lt;K,V&gt; next) {
+ super(hash, key, value, next);
+ }
+ }
+</code></pre>
+
+<h3 id="变量">变量</h3>
+
+<pre><code class="language-java"> /**
+ * 头结点
+ * The head (eldest) of the doubly linked list.
+ */
+ transient LinkedHashMap.Entry&lt;K,V&gt; head;
+
+ /**
+ * 尾节点
+ * The tail (youngest) of the doubly linked list.
+ */
+ transient LinkedHashMap.Entry&lt;K,V&gt; tail;
+
+ /* 设置链表排序规则: true 按照访问顺序排序 false按照插入顺序排序*/
+ final boolean accessOrder;
+</code></pre>
+
+<h3 id="构造参数">构造参数</h3>
+
+<p>LinkedHashMap基本都是复用的的hashmap的构造方法,只是对<code>accessOrder</code>初始化</p>
+
+<pre><code class="language-java"> public LinkedHashMap(int initialCapacity, float loadFactor) {
+ super(initialCapacity, loadFactor);
+ accessOrder = false;
+ }
+
+ public LinkedHashMap(int initialCapacity) {
+ super(initialCapacity);
+ accessOrder = false;
+ }
+
+ public LinkedHashMap() {
+ super();
+ accessOrder = false;
+ }
+
+ public LinkedHashMap(Map&lt;? extends K, ? extends V&gt; m) {
+ super();
+ accessOrder = false;
+ putMapEntries(m, false);
+ }
+
+ public LinkedHashMap(int initialCapacity,
+ float loadFactor,
+ boolean accessOrder) {
+ super(initialCapacity, loadFactor);
+ this.accessOrder = accessOrder;
+ }
+
+</code></pre>
+
+<h3 id="基本操作">基本操作</h3>
+
+<pre><code class="language-java"> /* get操作:和hashmap逻辑一致,只是会判断是否根据访问顺序排序 */
+ public V get(Object key) {
+ Node&lt;K,V&gt; e;
+ if ((e = getNode(hash(key), key)) == null)
+ return null;
+ if (accessOrder)
+ afterNodeAccess(e);//访问后的操作,将节点放到最后
+ return e.value;
+ }
+
+ /**
+ * 将节点移到最后的逻辑很简单
+ * 将该节点的before,after节点相连,并将该节点挂到last上
+ * 如果该节点的before为头结点,则head=after 否则before.after = after
+ * 如果该节点的after为尾节点,则last=before 否则 after.before = before
+ * 最后再把该节点挂在尾节点上
+ */
+ void afterNodeAccess(Node&lt;K,V&gt; e) { // move node to last
+ LinkedHashMap.Entry&lt;K,V&gt; last;
+ if (accessOrder &amp;&amp; (last = tail) != e) {//e不为尾节点时
+ LinkedHashMap.Entry&lt;K,V&gt; p =
+ (LinkedHashMap.Entry&lt;K,V&gt;)e, b = p.before, a = p.after;//获取前后节点
+ p.after = null;
+ if (b == null)//如果b为头结点时
+ head = a;
+ else
+ b.after = a;//否则与a连接
+ if (a != null)//a不为尾节点时
+ a.before = b;//与b连接
+ else
+ last = b;
+ if (last == null)//说明链表是空的
+ head = p;
+ else {
+ p.before = last;
+ last.after = p;
+ }
+ tail = p;//挂靠到tail上
+ ++modCount;
+ }
+ }
+
+ /**
+ * return true 删除链表中最久的entry
+ * 一般重写来实现简单版的LRU缓存
+ */
+ protected boolean removeEldestEntry(Map.Entry&lt;K,V&gt; eldest) {
+ return false;
+ }
+
+ /* 新建一个node 并插入到尾部 */
+ Node&lt;K,V&gt; newNode(int hash, K key, V value, Node&lt;K,V&gt; e) {
+ LinkedHashMap.Entry&lt;K,V&gt; p =
+ new LinkedHashMap.Entry&lt;K,V&gt;(hash, key, value, e);
+ linkNodeLast(p);
+ return p;
+ }
+
+ // link at the end of list
+ private void linkNodeLast(LinkedHashMap.Entry&lt;K,V&gt; p) {
+ LinkedHashMap.Entry&lt;K,V&gt; last = tail;
+ tail = p;
+ if (last == null)//链表为null时
+ head = p;
+ else {
+ p.before = last;
+ last.after = p;
+ }
+ }
+
+
+
+</code></pre>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/" title="Collections-Linkedlist"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/hashset/"
+ title="Collections-Hashset"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/collections/linkedlist/index.html b/collections/linkedlist/index.html
new file mode 100644
index 0000000..d88f7f7
--- /dev/null
+++ b/collections/linkedlist/index.html
@@ -0,0 +1,880 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Collections-Linkedlist - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content="" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Collections-Linkedlist - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Collections-Linkedlist" />
+<meta property="og:description" content="" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/" />
+<meta property="article:published_time" content="2019-08-15T13:19:18+08:00" />
+<meta property="article:modified_time" content="2019-08-15T13:19:18+08:00" />
+<meta itemprop="name" content="Collections-Linkedlist">
+<meta itemprop="description" content="">
+
+
+<meta itemprop="datePublished" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="dateModified" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="wordCount" content="2904">
+
+
+
+<meta itemprop="keywords" content="collections," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Collections-Linkedlist"/>
+<meta name="twitter:description" content=""/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li>
+<ul>
+<li>
+<ul>
+<li><a href="#主要属性">主要属性</a></li>
+<li><a href="#构造函数">构造函数</a></li>
+</ul></li>
+<li><a href="#主要方法">主要方法</a></li>
+<li><a href="#查询操作">查询操作</a></li>
+<li><a href="#队列类的操作">队列类的操作</a></li>
+<li><a href="#linkedlist中的迭代器">LinkedList中的迭代器</a></li>
+</ul></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/collections/linkedlist/"
+ >Collections-Linkedlist</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/" class="article-date">
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/collections/linkedlist/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:2904字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:6分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <p>实现了<code>List</code>和<code>Deque</code>接口的双向队列,允许插入<code>null</code>值</p>
+
+<h4 id="主要属性">主要属性</h4>
+
+<pre><code class="language-java"> transient int size = 0;//大小
+
+ /**
+ * Pointer to first node.
+ * Invariant: (first == null &amp;&amp; last == null) ||
+ * (first.prev == null &amp;&amp; first.item != null)
+ */
+ transient Node&lt;E&gt; first; //头结点
+
+ /**
+ * Pointer to last node.
+ * Invariant: (first == null &amp;&amp; last == null) ||
+ * (last.next == null &amp;&amp; last.item != null)
+ */
+ transient Node&lt;E&gt; last; //尾节点
+ //Node节点长这样:item实体对象,prev/next指向前一个后一个节点
+ private static class Node&lt;E&gt; {
+ E item;
+ Node&lt;E&gt; next;
+ Node&lt;E&gt; prev;
+
+ Node(Node&lt;E&gt; prev, E element, Node&lt;E&gt; next) {
+ this.item = element;
+ this.next = next;
+ this.prev = prev;
+ }
+ }
+</code></pre>
+
+<h4 id="构造函数">构造函数</h4>
+
+<p>主要介绍带参数的构造函数</p>
+
+<pre><code class="language-java"> //c == null 时会抛空指针异常
+ public LinkedList(Collection&lt;? extends E&gt; c) {
+ this();
+ addAll(c);
+ }
+ //最终会走到 addAll方法
+ public boolean addAll(int index, Collection&lt;? extends E&gt; c) {
+ checkPositionIndex(index);//判断index是否非法 index&lt;0 || index&gt;size
+ //集合转数组
+ Object[] a = c.toArray();
+ int numNew = a.length;
+ if (numNew == 0)
+ return false;
+
+ Node&lt;E&gt; pred, succ;
+ if (index == size) { //默认会从最后一个节点追加
+ succ = null;
+ pred = last;
+ } else { //自定义index后 会先取到该index对应的对象
+ succ = node(index);//调用node方法取得index位置下的node
+ pred = succ.prev;
+ }
+
+ //遍历需要赋值的数组
+ for (Object o : a) {
+ @SuppressWarnings(&quot;unchecked&quot;) E e = (E) o;
+ Node&lt;E&gt; newNode = new Node&lt;&gt;(pred, e, null);//不要next节点是因为迭代时可以自行设置
+ if (pred == null)//说明是从头开始
+ first = newNode;
+ else
+ pred.next = newNode;
+ pred = newNode;
+ }
+
+ //尾部node双向绑定
+ if (succ == null) {//不是指定index地方插入时,即从尾部插入,没有succ节点
+ last = pred;
+ } else {//从指定index插入时,需要与尾部节点连接
+ pred.next = succ;
+ succ.prev = pred;
+ }
+
+ size += numNew;
+ modCount++;
+ return true;
+ }
+
+ Node&lt;E&gt; node(int index) {
+ // assert isElementIndex(index);
+ //掰成两半儿查找
+ if (index &lt; (size &gt;&gt; 1)) {//小于size的一半时从头开始查
+ Node&lt;E&gt; x = first;
+ for (int i = 0; i &lt; index; i++)
+ x = x.next;
+ return x;
+ } else {//index大于size的一半时,从后往前找
+ Node&lt;E&gt; x = last;
+ for (int i = size - 1; i &gt; index; i--)
+ x = x.prev;
+ return x;
+ }
+ }
+</code></pre>
+
+<h3 id="主要方法">主要方法</h3>
+
+<p>1.get</p>
+
+<pre><code class="language-java"> public E get(int index) {
+ checkElementIndex(index);//检查index界限,会抛下标越界异常
+ return node(index).item;//遍历取得指定index下的node
+ }
+</code></pre>
+
+<p>2.set</p>
+
+<pre><code class="language-java"> public E set(int index, E element) {
+ //检查下标
+ checkElementIndex(index);
+ Node&lt;E&gt; x = node(index);//获取对应的node
+ E oldVal = x.item;//取出旧item
+ x.item = element;//赋值新item
+ return oldVal;
+ }
+</code></pre>
+
+<ol>
+<li><p>add</p>
+
+<pre><code class="language-java">public void add(int index, E element) {
+ checkPositionIndex(index);
+
+ if (index == size)//index等于size大小时在最后追加
+ linkLast(element);
+ else
+ linkBefore(element, node(index));//在该index直接添加
+}
+
+//在尾部连接一个对象
+void linkLast(E e) {
+ final Node&lt;E&gt; l = last;//获得当前的最后一个节点
+ final Node&lt;E&gt; newNode = new Node&lt;&gt;(l, e, null);//新建一个节点,当前最后一个节点作为prev节点
+ last = newNode;
+ if (l == null)//若前一个节点为null list还没有值 则头尾都用该节点
+ first = newNode;
+ else
+ l.next = newNode;//将上一个节点与新节点连接起来
+ size++;//链表长度+1
+ modCount++;//操作链表次数+1
+}
+
+//官方文档上这么说:在一个非空节点前插入新节点
+//但是其实没有做非空校验了
+void linkBefore(E e, Node&lt;E&gt; succ) {
+ // assert succ != null; 非空校验注释掉了
+ final Node&lt;E&gt; pred = succ.prev;//中间插入 那就是succ的prev节点要重新与新节点的prev连接,新节点的next节点为succ节点
+ final Node&lt;E&gt; newNode = new Node&lt;&gt;(pred, e, succ);
+ succ.prev = newNode;//succ节点与新节点连接
+ if (pred == null)//上一个节点为空时则首尾节点都是该节点
+ first = newNode;
+ else
+ pred.next = newNode;
+ size++;//链表长度+1
+ modCount++;//操作次数+1
+}
+</code></pre></li>
+</ol>
+
+<p>4.remove
+除了各种逻辑最后都会用到unlink方法:大致是将需要删除的对象的prev和next节点重新连接起来,在将该对象置空让gc回收</p>
+
+<pre><code class="language-java"> E unlink(Node&lt;E&gt; x) {
+ // assert x != null;
+ final E element = x.item;
+ final Node&lt;E&gt; next = x.next;//获取该节点下一个节点
+ final Node&lt;E&gt; prev = x.prev;//获取该节点上一个节点
+
+ if (prev == null) {//上一个节点为null时则删除的是头结点 将下一个节点变为头结点
+ first = next;
+ } else {
+ prev.next = next;//prev不为null时 将prev的next改为x的next
+ x.prev = null;//将x的prev置空 让gc回收
+ }
+
+ if (next == null) {//x的next为null时说明该节点是尾节点
+ last = prev;
+ } else {
+ next.prev = prev;//next的prev挂靠到x的prev
+ x.next = null;//x的next置空回收
+ }
+
+ x.item = null;//item置空回收
+ size--;//长度减1
+ modCount++;//操作次数+1
+ return element;
+ }
+</code></pre>
+
+<ol>
+<li><p>clear操作</p>
+
+<pre><code class="language-java">//清空所有的节点
+public void clear() {
+ // Clearing all of the links between nodes is &quot;unnecessary&quot;, but:
+ // - helps a generational GC if the discarded nodes inhabit
+ // more than one generation
+ // - is sure to free memory even if there is a reachable Iterator
+ for (Node&lt;E&gt; x = first; x != null; ) {
+ Node&lt;E&gt; next = x.next;
+ x.item = null;
+ x.next = null;
+ x.prev = null;
+ x = next;
+ }
+ first = last = null;
+ size = 0;
+ modCount++;
+}
+
+</code></pre></li>
+</ol>
+
+<h3 id="查询操作">查询操作</h3>
+
+<p>1.indexOf
+容易看懂就不解释了,查不到时返回-1,可以查null</p>
+
+<pre><code class="language-java"> public int indexOf(Object o) {
+ int index = 0;
+ if (o == null) {
+ for (Node&lt;E&gt; x = first; x != null; x = x.next) {
+ if (x.item == null)
+ return index;
+ index++;
+ }
+ } else {
+ for (Node&lt;E&gt; x = first; x != null; x = x.next) {
+ if (o.equals(x.item))
+ return index;
+ index++;
+ }
+ }
+ return -1;
+ }
+</code></pre>
+
+<h3 id="队列类的操作">队列类的操作</h3>
+
+<ol>
+<li><p>peek</p>
+
+<pre><code class="language-java">//获取头节点 但不删除 可以为null
+public E peek() {
+ final Node&lt;E&gt; f = first;
+ return (f == null) ? null : f.item;
+}
+</code></pre></li>
+
+<li><p>element 和peek一样也是获取头结点 但是若为null会抛空指针异常</p></li>
+
+<li><p>poll 就是队列里的pop操作,即出队</p>
+
+<pre><code class="language-java">public E poll() {
+ final Node&lt;E&gt; f = first;
+ return (f == null) ? null : unlinkFirst(f);
+}
+</code></pre>
+
+<p>4.offer入队
+队尾插入元素</p>
+
+<pre><code class="language-java">public boolean offer(E e) {
+ return add(e);
+}
+</code></pre></li>
+</ol>
+
+<h3 id="linkedlist中的迭代器">LinkedList中的迭代器</h3>
+
+<pre><code class="language-java"> //该迭代器是快速失败的,如果在创建迭代器后操作了链表(add/remove),不是迭代器中的操作(add/remove),就会抛ConcurrentModificationException异常.
+ //原因是迭代器中维护了expectedModCount每次操作前都会比较该值与modCount是否一致,不一致就抛,所以在迭代中增删节点时还是要通过迭代器的操作比较好
+ private class ListItr implements ListIterator&lt;E&gt; {
+ private Node&lt;E&gt; lastReturned;//用作返回节点
+ private Node&lt;E&gt; next;//记录下一个节点
+ private int nextIndex;//下一个index
+ private int expectedModCount = modCount;//初始化期望操作值
+
+ ListItr(int index) {
+ // assert isPositionIndex(index);
+ next = (index == size) ? null : node(index);//若index==size则是尾节点
+ nextIndex = index;
+ }
+
+ public boolean hasNext() {
+ return nextIndex &lt; size;//通过坐标值判断是否有下一个
+ }
+
+ public E next() {
+ checkForComodification();//校验操作避免使用list的add/remove操作
+ if (!hasNext())
+ throw new NoSuchElementException();
+
+ lastReturned = next;
+ next = next.next;
+ nextIndex++;
+ return lastReturned.item;
+ }
+
+ public boolean hasPrevious() {
+ return nextIndex &gt; 0;
+ }
+
+ public E previous() {
+ checkForComodification();
+ if (!hasPrevious())
+ throw new NoSuchElementException();
+
+ lastReturned = next = (next == null) ? last : next.prev;
+ nextIndex--;
+ return lastReturned.item;
+ }
+
+ public int nextIndex() {
+ return nextIndex;
+ }
+
+ public int previousIndex() {
+ return nextIndex - 1;
+ }
+ //迭代器的删除节点操作
+ public void remove() {
+ checkForComodification();//校验操作次数
+ if (lastReturned == null)//状态校验
+ throw new IllegalStateException();
+
+ Node&lt;E&gt; lastNext = lastReturned.next;//获得当前节点的下一个节点
+ unlink(lastReturned);//断开当前节点,将当前节点的前后节点相连接 size会减1 modCount会+1
+ if (next == lastReturned)
+ next = lastNext;
+ else
+ nextIndex--;//坐标值减1
+ lastReturned = null;
+ expectedModCount++;//保持与modCount一致
+ }
+ //设置当前的节点的item
+ public void set(E e) {
+ if (lastReturned == null)
+ throw new IllegalStateException();
+ checkForComodification();
+ lastReturned.item = e;
+ }
+ //和普通add操作一样 主要是需要将nextIndex和expectedModCount都+1
+ public void add(E e) {
+ checkForComodification();
+ lastReturned = null;
+ if (next == null)
+ linkLast(e);
+ else
+ linkBefore(e, next);
+ nextIndex++;
+ expectedModCount++;
+ }
+ //挺方便的迭代 重写下accept方法 可以用lambda表达式
+ public void forEachRemaining(Consumer&lt;? super E&gt; action) {
+ Objects.requireNonNull(action);
+ while (modCount == expectedModCount &amp;&amp; nextIndex &lt; size) {
+ action.accept(next.item);
+ lastReturned = next;
+ next = next.next;
+ nextIndex++;
+ }
+ checkForComodification();
+ }
+ //操作校验
+ final void checkForComodification() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ }
+ }
+</code></pre>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/treemap/" title="Collections-Treemap"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/"
+ title="Collections-Linkedhashmap"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/collections/treemap/index.html b/collections/treemap/index.html
new file mode 100644
index 0000000..c0258eb
--- /dev/null
+++ b/collections/treemap/index.html
@@ -0,0 +1,705 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Collections-Treemap - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content="" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Collections-Treemap - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Collections-Treemap" />
+<meta property="og:description" content="" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/collections/treemap/" />
+<meta property="article:published_time" content="2019-08-15T13:19:18+08:00" />
+<meta property="article:modified_time" content="2019-08-15T13:19:18+08:00" />
+<meta itemprop="name" content="Collections-Treemap">
+<meta itemprop="description" content="">
+
+
+<meta itemprop="datePublished" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="dateModified" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="wordCount" content="2098">
+
+
+
+<meta itemprop="keywords" content="collections," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Collections-Treemap"/>
+<meta name="twitter:description" content=""/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#简介">简介</a></li>
+<li><a href="#基本操作">基本操作</a></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/collections/treemap/"
+ >Collections-Treemap</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/collections/treemap/" class="article-date">
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/collections/treemap/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:2098字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:5分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <blockquote>
+<p><a href="https://www.tianxiaobo.com/2018/01/11/%E7%BA%A2%E9%BB%91%E6%A0%91%E8%AF%A6%E7%BB%86%E5%88%86%E6%9E%90/">红黑树介绍</a>
+<a href="http://cmsblogs.com/?p=1013">TreeMap解析</a></p>
+
+<h2 id="简介">简介</h2>
+
+<ol>
+<li>TreeMap底层基于<code>红黑树</code>实现,可以保证在<code>log(n)</code>时间复杂度下完成增删改查的操作,效率很高,由于基于<code>红黑树</code>,所以保持<code>有序</code>.</li>
+<li>TreeMap继承自<code>AbstractMap</code>,并实现了<code>NavigableMap</code>(主要是提供一些导航类的操作,比如获得比当前节点小的最大值,比当前节点大的最小值等)</li>
+<li><code>key</code>不允许为空</li>
+</ol>
+</blockquote>
+
+<pre><code class="language-mermaid">graph BT
+A(C:TreeMap) -.-&gt; B(I:NavigableMap)
+A --&gt; C(C:AbstractMap)
+B --&gt; E(I:SortedMap)
+E --&gt; D(I:Map)
+C -.-&gt;D(I:Map)
+</code></pre>
+
+<h2 id="基本操作">基本操作</h2>
+
+<pre><code class="language-java"> /* get 操作 可以返回null值 如果key为null 则会抛 NPE */
+ public V get(Object key) {
+ Entry&lt;K,V&gt; p = getEntry(key);
+ return (p==null ? null : p.value);
+ }
+
+ /* get操作实际调用的方法 key为null时 抛NPE */
+ final Entry&lt;K,V&gt; getEntry(Object key) {
+ // Offload comparator-based version for sake of performance
+ // 除非用comparator构建TreeMap,否则不使用它,为了性能考虑
+ if (comparator != null)
+ return getEntryUsingComparator(key);
+ if (key == null)
+ throw new NullPointerException();
+ @SuppressWarnings(&quot;unchecked&quot;)
+ Comparable&lt;? super K&gt; k = (Comparable&lt;? super K&gt;) key;
+ Entry&lt;K,V&gt; p = root;
+ while (p != null) {
+ int cmp = k.compareTo(p.key);//compare key遍历二叉树
+ if (cmp &lt; 0)
+ p = p.left;
+ else if (cmp &gt; 0)
+ p = p.right;
+ else
+ return p;
+ }
+ return null;
+ }
+
+
+
+public V put(K key, V value) {
+ //用t表示二叉树的当前节点
+ Entry&lt;K,V&gt; t = root;
+ //t为null表示一个空树,即TreeMap中没有任何元素,直接插入
+ if (t == null) {
+ //比较key值,个人觉得这句代码没有任何意义,空树还需要比较、排序?
+ compare(key, key); // type (and possibly null) check
+ //将新的key-value键值对创建为一个Entry节点,并将该节点赋予给root
+ root = new Entry&lt;&gt;(key, value, null);
+ //容器的size = 1,表示TreeMap集合中存在一个元素
+ size = 1;
+ //修改次数 + 1
+ modCount++;
+ return null;
+ }
+ int cmp; //cmp表示key排序的返回结果
+ Entry&lt;K,V&gt; parent; //父节点
+ // split comparator and comparable paths
+ Comparator&lt;? super K&gt; cpr = comparator; //指定的排序算法
+ //如果cpr不为空,则采用既定的排序算法进行创建TreeMap集合
+ if (cpr != null) {
+ do {
+ parent = t; //parent指向上次循环后的t
+ //比较新增节点的key和当前节点key的大小
+ cmp = cpr.compare(key, t.key);
+ //cmp返回值小于0,表示新增节点的key小于当前节点的key,则以当前节点的左子节点作为新的当前节点
+ if (cmp &lt; 0)
+ t = t.left;
+ //cmp返回值大于0,表示新增节点的key大于当前节点的key,则以当前节点的右子节点作为新的当前节点
+ else if (cmp &gt; 0)
+ t = t.right;
+ //cmp返回值等于0,表示两个key值相等,则新值覆盖旧值,并返回新值
+ else
+ return t.setValue(value);
+ } while (t != null);
+ }
+ //如果cpr为空,则采用默认的排序算法进行创建TreeMap集合
+ else {
+ if (key == null) //key值为空抛出异常
+ throw new NullPointerException();
+ /* 下面处理过程和上面一样 */
+ Comparable&lt;? super K&gt; k = (Comparable&lt;? super K&gt;) key;
+ do {
+ parent = t;
+ cmp = k.compareTo(t.key);
+ if (cmp &lt; 0)
+ t = t.left;
+ else if (cmp &gt; 0)
+ t = t.right;
+ else
+ return t.setValue(value);
+ } while (t != null);
+ }
+ //将新增节点当做parent的子节点
+ Entry&lt;K,V&gt; e = new Entry&lt;&gt;(key, value, parent);
+ //如果新增节点的key小于parent的key,则当做左子节点
+ if (cmp &lt; 0)
+ parent.left = e;
+ //如果新增节点的key大于parent的key,则当做右子节点
+ else
+ parent.right = e;
+ /*
+ * 上面已经完成了排序二叉树的的构建,将新增节点插入该树中的合适位置
+ * 下面fixAfterInsertion()方法就是对这棵树进行调整、平衡,具体过程参考上面的五种情况
+ */
+ fixAfterInsertion(e);
+ //TreeMap元素数量 + 1
+ size++;
+ //TreeMap容器修改次数 + 1
+ modCount++;
+ return null;
+ }
+
+
+ /**
+ * 上面代码中do{}代码块是实现排序二叉树的核心算法,通过该算法我们可以确认新增节点在该树的正确位置。
+ * 找到正确位置后将插入即可,这样做了其实还没有完成,因为我知道TreeMap的底层实现是红黑树,红黑树是一棵平衡排序二叉树,
+ * 普通的排序二叉树可能会出现失衡的情况,所以下一步就是要进行调整。fixAfterInsertion(e); 调整的过程务必会涉及到红黑树的左
+ * 旋、右旋、着色三个基本操作
+ * 新增节点后的修复操作
+ * x 表示新增节点
+ */
+ private void fixAfterInsertion(Entry&lt;K,V&gt; x) {
+ x.color = RED; //新增节点的颜色为红色
+
+ //循环 直到 x不是根节点,且x的父节点不为红色
+ while (x != null &amp;&amp; x != root &amp;&amp; x.parent.color == RED) {
+ //如果X的父节点(P)是其父节点的父节点(G)的左节点
+ if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
+ //获取X的叔节点(U)
+ Entry&lt;K,V&gt; y = rightOf(parentOf(parentOf(x)));
+ //如果X的叔节点(U) 为红色(情况三)
+ if (colorOf(y) == RED) {
+ //将X的父节点(P)设置为黑色
+ setColor(parentOf(x), BLACK);
+ //将X的叔节点(U)设置为黑色
+ setColor(y, BLACK);
+ //将X的父节点的父节点(G)设置红色
+ setColor(parentOf(parentOf(x)), RED);
+ x = parentOf(parentOf(x));
+ }
+ //如果X的叔节点(U为黑色);这里会存在两种情况(情况四、情况五)
+ else {
+ //如果X节点为其父节点(P)的右子树,则进行左旋转(情况四)
+ if (x == rightOf(parentOf(x))) {
+ //将X的父节点作为X
+ x = parentOf(x);
+ //右旋转
+ rotateLeft(x);
+ }
+ //(情况五)
+ //将X的父节点(P)设置为黑色
+ setColor(parentOf(x), BLACK);
+ //将X的父节点的父节点(G)设置红色
+ setColor(parentOf(parentOf(x)), RED);
+ //以X的父节点的父节点(G)为中心右旋转
+ rotateRight(parentOf(parentOf(x)));
+ }
+ }
+ //如果X的父节点(P)是其父节点的父节点(G)的右节点
+ else {
+ //获取X的叔节点(U)
+ Entry&lt;K,V&gt; y = leftOf(parentOf(parentOf(x)));
+ //如果X的叔节点(U) 为红色(情况三)
+ if (colorOf(y) == RED) {
+ //将X的父节点(P)设置为黑色
+ setColor(parentOf(x), BLACK);
+ //将X的叔节点(U)设置为黑色
+ setColor(y, BLACK);
+ //将X的父节点的父节点(G)设置红色
+ setColor(parentOf(parentOf(x)), RED);
+ x = parentOf(parentOf(x));
+ }
+ //如果X的叔节点(U为黑色);这里会存在两种情况(情况四、情况五)
+ else {
+ //如果X节点为其父节点(P)的右子树,则进行左旋转(情况四)
+ if (x == leftOf(parentOf(x))) {
+ //将X的父节点作为X
+ x = parentOf(x);
+ //右旋转
+ rotateRight(x);
+ }
+ //(情况五)
+ //将X的父节点(P)设置为黑色
+ setColor(parentOf(x), BLACK);
+ //将X的父节点的父节点(G)设置红色
+ setColor(parentOf(parentOf(x)), RED);
+ //以X的父节点的父节点(G)为中心右旋转
+ rotateLeft(parentOf(parentOf(x)));
+ }
+ }
+ }
+ //将根节点G强制设置为黑色
+ root.color = BLACK;
+ }
+
+
+</code></pre>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/treeset/" title="Collections-Treeset"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/"
+ title="Collections-Linkedlist"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/collections/treeset/index.html b/collections/treeset/index.html
new file mode 100644
index 0000000..1ddc439
--- /dev/null
+++ b/collections/treeset/index.html
@@ -0,0 +1,614 @@
+<!DOCTYPE html>
+<html lang="zh">
+ <head>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
+ <title>
+ Collections-Treeset - 赵小黑的博客
+ </title>
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+ <meta name="viewport"
+ content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
+ <meta name="renderer" content="webkit">
+ <meta http-equiv="Cache-Control" content="no-transform" />
+ <meta http-equiv="Cache-Control" content="no-siteapp" />
+ <meta name="apple-mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="format-detection" content="telephone=no,email=no,adress=no">
+
+ <meta name="theme-color" content="#000000" />
+
+ <meta http-equiv="window-target" content="_top" />
+
+
+ <meta name="description" content="" />
+ <meta name="generator" content="Hugo 0.58.0 with theme pure" />
+ <title>Collections-Treeset - 赵小黑的博客</title>
+
+
+ <link rel="stylesheet" href="https://xiaohei.im/hugo-theme-pure/css/style.css">
+ <link rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/9.15.10/styles/github.min.css">
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gitalk@1/dist/gitalk.css">
+ <meta property="og:title" content="Collections-Treeset" />
+<meta property="og:description" content="" />
+<meta property="og:type" content="article" />
+<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/collections/treeset/" />
+<meta property="article:published_time" content="2019-08-15T13:19:18+08:00" />
+<meta property="article:modified_time" content="2019-08-15T13:19:18+08:00" />
+<meta itemprop="name" content="Collections-Treeset">
+<meta itemprop="description" content="">
+
+
+<meta itemprop="datePublished" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="dateModified" content="2019-08-15T13:19:18&#43;08:00" />
+<meta itemprop="wordCount" content="767">
+
+
+
+<meta itemprop="keywords" content="collections," />
+<meta name="twitter:card" content="summary"/>
+<meta name="twitter:title" content="Collections-Treeset"/>
+<meta name="twitter:description" content=""/>
+
+ <!--[if lte IE 9]>
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/classlist/1.1.20170427/classList.min.js"></script>
+ <![endif]-->
+
+ <!--[if lt IE 9]>
+ <script src="https://cdn.jsdelivr.net/npm/html5shiv@3.7.3/dist/html5shiv.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/respond.js@1.4.2/dest/respond.min.js"></script>
+ <![endif]-->
+
+</head>
+ </head>
+ <body class="main-center theme-black" itemscope itemtype="http://schema.org/WebPage"><header class="header" itemscope itemtype="http://schema.org/WPHeader">
+ <div class="slimContent">
+ <div class="navbar-header">
+ <div class="profile-block text-center">
+ <a id="avatar" href="https://github.com/xiaoheiAh" target="_blank">
+ <img class="img-circle img-rotate" src="https://xiaohei.im/hugo-theme-pure/avatar.png" width="200" height="200">
+ </a>
+ <h2 id="name" class="hidden-xs hidden-sm">赵小黑</h2>
+ <h3 id="title" class="hidden-xs hidden-sm hidden-md">Java Developer</h3>
+ <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i>Shanghai, China</small>
+ </div><div class="search" id="search-form-wrap">
+ <form class="search-form sidebar-form">
+ <div class="input-group">
+ <input type="text" class="search-form-input form-control" placeholder="搜索" />
+ <span class="input-group-btn">
+ <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i
+ class="icon icon-search"></i></button>
+ </span>
+ </div>
+ <div class="ins-search">
+ <div class="ins-search-mask"></div>
+ <div class="ins-search-container">
+ <div class="ins-input-wrapper">
+ <input type="text" class="ins-search-input" placeholder="想要查找什么..."
+ x-webkit-speech />
+ <button type="button" class="close ins-close ins-selectable" data-dismiss="modal"
+ aria-label="Close"><span aria-hidden="true">×</span></button>
+ </div>
+ <div class="ins-section-wrapper">
+ <div class="ins-section-container"></div>
+ </div>
+ </div>
+ </div>
+ </form>
+</div>
+ <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ </div>
+ <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
+ <ul class="nav navbar-nav main-nav menu-highlight">
+ <li class="menu-item menu-item-home">
+ <a href="/hugo-theme-pure/">
+ <i class="icon icon-home-fill"></i>
+ <span class="menu-title">主页</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-archives">
+ <a href="/hugo-theme-pure/posts">
+ <i class="icon icon-archives-fill"></i>
+ <span class="menu-title">归档</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-categories">
+ <a href="/hugo-theme-pure/categories">
+ <i class="icon icon-folder"></i>
+ <span class="menu-title">分类</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-tags">
+ <a href="/hugo-theme-pure/tags">
+ <i class="icon icon-tags"></i>
+ <span class="menu-title">标签</span>
+ </a>
+ </li>
+ <li class="menu-item menu-item-about">
+ <a href="/hugo-theme-pure/about">
+ <i class="icon icon-cup-fill"></i>
+ <span class="menu-title">关于</span>
+ </a>
+ </li>
+ </ul>
+ </nav>
+ </div>
+ </header>
+ <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+
+ <div class="widget">
+ <h3 class="widget-title">公告</h3>
+ <div class="widget-body">
+ <div id="board">
+ <div class="content"><p>自用科学上网节点推荐(便宜又好用)<a href="https://tianlinzhao.com/aff.php?aff=4969" target="_blank" style="background-color:#FFFF00">点这里跳转</a>
+ </div>
+ </div>
+ </div>
+</div>
+
+ <div class="widget">
+ <h3 class="widget-title"> 分类</h3>
+ <div class="widget-body">
+ <ul class="category-list">
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
+ </ul>
+ </div>
+</div>
+ <div class="widget">
+ <h3 class="widget-title"> 标签</h3>
+ <div class="widget-body">
+ <ul class="tag-list">
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/collections/" class="tag-list-link">collections</a><span
+ class="tag-list-count">7</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hugo/" class="tag-list-link">hugo</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/hystrix/" class="tag-list-link">hystrix</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/leetcode/" class="tag-list-link">leetcode</a><span
+ class="tag-list-count">3</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/" class="tag-list-link">rabbitmq</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
+ class="tag-list-count">8</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
+ class="tag-list-count">4</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rxjava/" class="tag-list-link">rxjava</a><span
+ class="tag-list-count">2</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="tag-list-link">分布式锁</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/" class="tag-list-link">响应式编程</a><span
+ class="tag-list-count">1</span></li>
+
+
+ <li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" class="tag-list-link">数据结构</a><span
+ class="tag-list-count">1</span></li>
+
+ </ul>
+
+ </div>
+</div>
+
+<div class="widget">
+ <h3 class="widget-title">最新文章</h3>
+ <div class="widget-body">
+ <ul class="recent-post-list list-unstyled no-thumbnail">
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ <li>
+ <div class="item-inner">
+ <p class="item-title">
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ </p>
+ <p class="item-date">
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ </p>
+ </div>
+ </li>
+ </ul>
+ </div>
+</div>
+ </div>
+</aside>
+
+
+
+ <aside class="sidebar sidebar-toc collapse" id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
+ <div class="slimContent">
+ <nav id="toc" class="article-toc">
+ <h3 class="toc-title">文章目录</h3>
+ <div class="toc-content always-active"><nav id="TableOfContents">
+<ul>
+<li>
+<ul>
+<li><a href="#参数">参数</a></li>
+<li><a href="#构造方法">构造方法</a></li>
+<li><a href="#主要方法">主要方法</a></li>
+</ul></li>
+</ul>
+</nav>
+ </div>
+ </nav>
+ </div>
+ </aside>
+<main class="main" role="main"><div class="content">
+ <article id="-" class="article article-type-" itemscope
+ itemtype="http://schema.org/BlogPosting">
+
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/collections/treeset/"
+ >Collections-Treeset</a
+ >
+</h1>
+
+ <div class="article-meta">
+ <span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/collections/treeset/" class="article-date">
+ <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
+ </span>
+
+ <span class="article-read hidden-xs">
+ <i class="icon icon-eye-fill" aria-hidden="true"></i>
+ <span id="busuanzi_container_page_pv">
+ <span id="busuanzi_value_page_pv">0</span>
+ </span>
+ </span>
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/collections/treeset/#comments"
+ class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:767字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:2分 </span>
+ </div>
+ </div>
+ <div class="article-entry marked-body" itemprop="articleBody">
+ <p>如同<code>HashSet</code>基于<code>HashMap</code>一样,<code>TreeSet</code>基于<code>TreeMap</code>,<code>TreeMap</code>是一棵有序的红黑树,那么<code>TreeSet</code>也如此.提供有序的set集合,不允许重复插入
+继承关系如下:</p>
+
+<pre><code class="language-mermaid">graph BT
+A(TreeSet) -.-&gt; B(NavigableSet)
+A --&gt; C(AbstractSet)
+C--&gt;D(AbstractCollection)
+D-.-&gt;E(Collection)
+</code></pre>
+
+<h2 id="参数">参数</h2>
+
+<pre><code class="language-java">private transient NavigableMap&lt;E,Object&gt; m;
+
+//PRESENT会被当做Map的value与key构建成键值对
+ private static final Object PRESENT = new Object();
+</code></pre>
+
+<h2 id="构造方法">构造方法</h2>
+
+<pre><code class="language-java"> //创建TreeSet的基础构成 map
+ TreeSet(NavigableMap&lt;E,Object&gt; m) {
+ this.m = m;
+ }
+
+ //按照自然排序构建
+ public TreeSet() {
+ this(new TreeMap&lt;E,Object&gt;());
+ }
+
+ //按照自定义排序构建
+ public TreeSet(Comparator&lt;? super E&gt; comparator) {
+ this(new TreeMap&lt;&gt;(comparator));
+ }
+
+ //按照自然排序构建 并添加入参集合的元素
+ public TreeSet(Collection&lt;? extends E&gt; c) {
+ this();
+ addAll(c);
+ }
+
+ //根据已有的TreeSet构建一个新的TreeSet
+ public TreeSet(SortedSet&lt;E&gt; s) {
+ this(s.comparator());
+ addAll(s);
+ }
+
+</code></pre>
+
+<h2 id="主要方法">主要方法</h2>
+
+<pre><code class="language-java">/**
+ * 将集合中所有的元素添加到TreeMap中
+ * 如果集合为空,或者任一元素为null并且使用的是自然排序,或者
+ * comparator不允许为空元素则会抛NPE
+ */
+public boolean addAll(Collection&lt;? extends E&gt; c) {
+ // Use linear-time version if applicable
+ if (m.size()==0 &amp;&amp; c.size() &gt; 0 &amp;&amp;
+ c instanceof SortedSet &amp;&amp;
+ m instanceof TreeMap) {
+ SortedSet&lt;? extends E&gt; set = (SortedSet&lt;? extends E&gt;) c;
+ TreeMap&lt;E,Object&gt; map = (TreeMap&lt;E, Object&gt;) m;
+ Comparator&lt;?&gt; cc = set.comparator();
+ Comparator&lt;? super E&gt; mc = map.comparator();
+ if (cc==mc || (cc != null &amp;&amp; cc.equals(mc))) {
+ map.addAllForTreeSet(set, PRESENT);
+ return true;
+ }
+ }
+ return super.addAll(c);
+}
+
+/* add操作 会去重 put返回值为null时说明成功 */
+public boolean add(E e) {
+ return m.put(e, PRESENT)==null;
+}
+
+/* 获取并移除第一个元素 如果set为空 则返回null */
+public E pollFirst() {
+ Map.Entry&lt;E,?&gt; e = m.pollFirstEntry();
+ return (e == null) ? null : e.getKey();
+}
+
+/* 获取并移除最后一个元素 如果set为空 则返回null */
+public E pollLast() {
+ Map.Entry&lt;E,?&gt; e = m.pollFirstEntry();
+ return (e == null) ? null : e.getKey();
+}
+
+/**
+ * 返回此 set 的部分视图,其元素大于(或等于,如果 inclusive 为 true)fromElement。
+ */
+public NavigableSet&lt;E&gt; tailSet(E fromElement, boolean inclusive) {
+ return new TreeSet&lt;&gt;(m.tailMap(fromElement, inclusive));
+}
+
+/**
+ * 返回此 set 的部分视图,其元素大于等于 fromElement。
+ */
+public SortedSet&lt;E&gt; tailSet(E fromElement) {
+ return tailSet(fromElement, true);
+}
+
+/**
+ * 返回此 set 的部分视图,其元素范围从 fromElement 到 toElement。
+ */
+public NavigableSet&lt;E&gt; subSet(E fromElement, boolean fromInclusive,
+ E toElement, boolean toInclusive) {
+ return new TreeSet&lt;&gt;(m.subMap(fromElement, fromInclusive,
+ toElement, toInclusive));
+}
+
+/**
+ * 返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。
+ */
+public SortedSet&lt;E&gt; subSet(E fromElement, E toElement) {
+ return subSet(fromElement, true, toElement, false);
+}
+</code></pre>
+ </div>
+ </article>
+<section id="comments">
+</section>
+
+</div><nav class="bar bar-footer clearfix" data-stick-bottom>
+ <div class="bar-inner">
+ <ul class="pager pull-left">
+ <li class="prev">
+ <a href="https://xiaohei.im/hugo-theme-pure/about/" title="关于自己"><i
+ class="icon icon-angle-left"
+ aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
+ </li>
+ <li class="next">
+ <a href="https://xiaohei.im/hugo-theme-pure/collections/treemap/"
+ title="Collections-Treemap"><span>下一篇&nbsp;&nbsp;</span><i
+ class="icon icon-angle-right" aria-hidden="true"></i></a>
+ </li>
+
+ <li class="toggle-toc">
+ <a class="toggle-btn collapsed" data-toggle="collapse" href="#collapseToc" aria-expanded="false"
+ title="文章目录" role="button">
+ <span>[&nbsp;</span><span>文章目录</span>
+ <i class="text-collapsed icon icon-anchor"></i>
+ <i class="text-in icon icon-close"></i>
+ <span>]</span>
+ </a>
+ </li>
+ </ul>
+
+ <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal"
+ data-target="#donateModal"><span>赏</span></button>
+
+ <div class="bar-right">
+ <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter"
+ data-mobile-sites="weibo,qq,qzone"></div>
+ </div>
+ </div>
+</nav>
+
+<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
+ <div class="modal-dialog" role="document">
+ <div class="modal-content donate">
+ <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
+ aria-hidden="true">&times;</span></button>
+ <div class="modal-body">
+ <div class="donate-box">
+ <div class="donate-head">
+ <p>感谢您的支持,我会继续努力的!</p>
+ </div>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane fade active in" id="alipay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/alipayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开支付宝扫一扫,即可进行扫码打赏哦~</p>
+ </div>
+ <div role="tabpanel" class="tab-pane fade" id="wechatpay">
+ <div class="donate-payimg">
+ <img src="https://xiaohei.im/hugo-theme-pure/donate/wechatpayimg.png"
+ alt="扫码支持" title="扫一扫" />
+ </div>
+ <p class="text-muted mv">扫码打赏, 多少你说了算~</p>
+ <p class="text-grey">打开微信扫一扫,即可进行扫码打赏哦</p>
+ </div>
+ </div>
+ <div class="donate-footer">
+ <ul class="nav nav-tabs nav-justified" role="tablist">
+ <li role="presentation" class="active">
+ <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay"
+ aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
+ </li>
+ <li role="presentation" class="">
+ <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab"
+ aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i>
+ 微信支付</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
+<ul class="social-links">
+ <li><a href="https://github.com/xiaoheiAh" target="_blank" title="github" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-github"></i></a></li>
+ <li><a href="https://xiaohei.im/index.xml" target="_blank" title="rss" data-toggle=tooltip data-placement=top >
+ <i class="icon icon-rss"></i></a></li>
+</ul>
+ <div class="copyright">
+ &copy;2017 -
+ 2019
+ <div class="publishby">
+ Theme by <a href="https://github.com/xiaoheiAh" target="_blank"> xiaoheiAh </a>base on<a href="https://github.com/xiaoheiAh/hugo-theme-pure" target="_blank"> pure</a>.
+ </div>
+ </div>
+</footer>
+<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
+<script>
+ window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
+</script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/highlight.min.js"></script>
+<script type="text/javascript" src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/rust.min.js"></script>
+<script type="text/javascript"
+ src="https://cdn.staticfile.org/highlight.js/9.15.10/languages/dockerfile.min.js"></script>
+<script>
+hljs.configure({
+ tabReplace: ' ',
+ classPrefix: ''
+
+})
+hljs.initHighlightingOnLoad();
+</script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/application.js"></script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/plugin.js"></script>
+<script>
+ (function (window) {
+ var INSIGHT_CONFIG = {
+ TRANSLATION: {
+ POSTS: '文章',
+ PAGES: '页面',
+ CATEGORIES: '分类',
+ TAGS: '标签',
+ UNTITLED: '(未命名)',
+ },
+ ROOT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure',
+ CONTENT_URL: 'https:\/\/xiaohei.im\/hugo-theme-pure\/searchindex.json ',
+ };
+ window.INSIGHT_CONFIG = INSIGHT_CONFIG;
+ })(window);
+ </script>
+<script type="text/javascript" src="https://xiaohei.im/hugo-theme-pure/js/insight.js"></script>
+
+<script async src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
+<script type="application/javascript">
+var doNotTrack = false;
+if (!doNotTrack) {
+ window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
+ ga('create', 'UA-98254666-1', 'auto');
+
+ ga('send', 'pageview');
+}
+</script>
+<script async src='https://www.google-analytics.com/analytics.js'></script>
+
+ </body>
+</html>
diff --git a/index.html b/index.html
index de94a88..aac31cd 100644
--- a/index.html
+++ b/index.html
@@ -36,7 +36,7 @@
<meta property="og:type" content="website" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/" />
-<meta property="og:updated_time" content="2019-11-06T19:08:56+08:00" />
+<meta property="og:updated_time" content="2019-11-16T14:24:40+08:00" />
<meta itemprop="name" content="赵小黑的博客">
<meta itemprop="description" content="">
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -286,6 +286,110 @@
<h1 itemprop="name">
<a
class="article-title"
+ href="/hugo-theme-pure/2019/11/replication/"
+ >Redis-复制功能探索</a
+ >
+</h1>
+
+ </div>
+ <div class="article-entry text-muted" itemprop="description"><p>之前多<code>redis</code> 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 <a href="https://redis.io/topics/replication">https://redis.io/topics/replication</a></p></div>
+ <p class="article-meta"><span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/redis/"> redis </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/redis/"> redis </a>
+ </span>
+
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/11/replication/#comments" class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:3579字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:8分 </span>
+ </p>
+ </article><article
+ class="article article-type-page"
+ itemscope
+ itemtype="http://schema.org/BlogPosting"
+ >
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/2019/11/event/"
+ >Redis-事件</a
+ >
+</h1>
+
+ </div>
+ <div class="article-entry text-muted" itemprop="description"><blockquote>
+<p><strong>事件驱动程序设计</strong>(英语:<strong>Event-driven programming</strong>)是一种电脑<a href="https://zh.wikipedia.org/wiki/程式設計">程序设计</a><a href="https://zh.wikipedia.org/wiki/模型">模型</a>。这种模型的程序运行流程是由用户的动作(如<a href="https://zh.wikipedia.org/wiki/滑鼠">鼠标</a>的按键,键盘的按键动作)或者是由其他程序的<a href="https://zh.wikipedia.org/wiki/訊息">消息</a>来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由<a href="https://zh.wikipedia.org/wiki/程式設計師">程序员</a>来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在<a href="https://zh.wikipedia.org/w/index.php?title=互動程序&amp;action=edit&amp;redlink=1">交互程序</a>(Interactive program)的情况下孕育而生的。 <a href="https://zh.wikipedia.org/wiki/事件驅動程式設計">&ndash;wikipedia</a></p>
+</blockquote></div>
+ <p class="article-meta"><span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/redis/"> redis </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/redis/"> redis </a>
+ </span>
+
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/11/event/#comments" class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:2358字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:5分 </span>
+ </p>
+ </article><article
+ class="article article-type-page"
+ itemscope
+ itemtype="http://schema.org/BlogPosting"
+ >
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
+ href="/hugo-theme-pure/2019/11/aof/"
+ >Redis-AOF持久化</a
+ >
+</h1>
+
+ </div>
+ <div class="article-entry text-muted" itemprop="description"><p><code>RDB</code> 和 <code>AOF</code> 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 <strong>写命令</strong> 保证数据库的状态.</p></div>
+ <p class="article-meta"><span class="article-date">
+ <i class="icon icon-calendar-check"></i>
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
+</a>
+</span><span class="article-category">
+ <i class="icon icon-folder"></i>
+ <a class="article-category-link" href="/hugo-theme-pure/categories/redis/"> redis </a>
+</span>
+ <span class="article-tag">
+ <i class="icon icon-tags"></i>
+ <a class="article-tag-link" href="/hugo-theme-pure/tags/redis/"> redis </a>
+ </span>
+
+ <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/11/aof/#comments" class="article-comment-link">评论</a></span>
+ <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:1461字</span>
+ <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:3分 </span>
+ </p>
+ </article><article
+ class="article article-type-page"
+ itemscope
+ itemtype="http://schema.org/BlogPosting"
+ >
+ <div class="article-header">
+ <h1 itemprop="name">
+ <a
+ class="article-title"
href="/hugo-theme-pure/2019/11/rdb/"
>Redis-RDB持久化</a
>
@@ -423,7 +527,7 @@
<a
class="article-title"
href="/hugo-theme-pure/2019/10/data-structure/"
- >Redis - 数据结构</a
+ >Redis-数据结构</a
>
</h1>
@@ -818,237 +922,6 @@
<span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:99字</span>
<span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:1分 </span>
</p>
- </article><article
- class="article article-type-page"
- itemscope
- itemtype="http://schema.org/BlogPosting"
- >
- <div class="article-header">
- <h1 itemprop="name">
- <a
- class="article-title"
- href="/hugo-theme-pure/2019/08/arraylist/"
- >Collections-Arraylist</a
- >
-</h1>
-
- </div>
- <p class="article-meta"><span class="article-date">
- <i class="icon icon-calendar-check"></i>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/" class="">
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
-</a>
-</span><span class="article-category">
- <i class="icon icon-folder"></i>
- <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
-</span>
- <span class="article-tag">
- <i class="icon icon-tags"></i>
- <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
- </span>
-
- <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/08/arraylist/#comments" class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:1001字</span>
- <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:2分 </span>
- </p>
- </article><article
- class="article article-type-page"
- itemscope
- itemtype="http://schema.org/BlogPosting"
- >
- <div class="article-header">
- <h1 itemprop="name">
- <a
- class="article-title"
- href="/hugo-theme-pure/2019/08/hashmap/"
- >Collections-Hashmap</a
- >
-</h1>
-
- </div>
- <p class="article-meta"><span class="article-date">
- <i class="icon icon-calendar-check"></i>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/" class="">
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
-</a>
-</span><span class="article-category">
- <i class="icon icon-folder"></i>
- <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
-</span>
- <span class="article-tag">
- <i class="icon icon-tags"></i>
- <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
- </span>
-
- <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/08/hashmap/#comments" class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:6419字</span>
- <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:13分 </span>
- </p>
- </article><article
- class="article article-type-page"
- itemscope
- itemtype="http://schema.org/BlogPosting"
- >
- <div class="article-header">
- <h1 itemprop="name">
- <a
- class="article-title"
- href="/hugo-theme-pure/2019/08/hashset/"
- >Collections-Hashset</a
- >
-</h1>
-
- </div>
- <p class="article-meta"><span class="article-date">
- <i class="icon icon-calendar-check"></i>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/hashset/" class="">
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
-</a>
-</span><span class="article-category">
- <i class="icon icon-folder"></i>
- <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
-</span>
- <span class="article-tag">
- <i class="icon icon-tags"></i>
- <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
- </span>
-
- <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/08/hashset/#comments" class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:272字</span>
- <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:1分 </span>
- </p>
- </article><article
- class="article article-type-page"
- itemscope
- itemtype="http://schema.org/BlogPosting"
- >
- <div class="article-header">
- <h1 itemprop="name">
- <a
- class="article-title"
- href="/hugo-theme-pure/2019/08/linkedhashmap/"
- >Collections-Linkedhashmap</a
- >
-</h1>
-
- </div>
- <p class="article-meta"><span class="article-date">
- <i class="icon icon-calendar-check"></i>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/" class="">
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
-</a>
-</span><span class="article-category">
- <i class="icon icon-folder"></i>
- <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
-</span>
- <span class="article-tag">
- <i class="icon icon-tags"></i>
- <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
- </span>
-
- <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/08/linkedhashmap/#comments" class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:1027字</span>
- <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:3分 </span>
- </p>
- </article><article
- class="article article-type-page"
- itemscope
- itemtype="http://schema.org/BlogPosting"
- >
- <div class="article-header">
- <h1 itemprop="name">
- <a
- class="article-title"
- href="/hugo-theme-pure/2019/08/linkedlist/"
- >Collections-Linkedlist</a
- >
-</h1>
-
- </div>
- <p class="article-meta"><span class="article-date">
- <i class="icon icon-calendar-check"></i>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/" class="">
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
-</a>
-</span><span class="article-category">
- <i class="icon icon-folder"></i>
- <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
-</span>
- <span class="article-tag">
- <i class="icon icon-tags"></i>
- <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
- </span>
-
- <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/08/linkedlist/#comments" class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:2904字</span>
- <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:6分 </span>
- </p>
- </article><article
- class="article article-type-page"
- itemscope
- itemtype="http://schema.org/BlogPosting"
- >
- <div class="article-header">
- <h1 itemprop="name">
- <a
- class="article-title"
- href="/hugo-theme-pure/2019/08/treemap/"
- >Collections-Treemap</a
- >
-</h1>
-
- </div>
- <p class="article-meta"><span class="article-date">
- <i class="icon icon-calendar-check"></i>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/treemap/" class="">
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
-</a>
-</span><span class="article-category">
- <i class="icon icon-folder"></i>
- <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
-</span>
- <span class="article-tag">
- <i class="icon icon-tags"></i>
- <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
- </span>
-
- <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/08/treemap/#comments" class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:2098字</span>
- <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:5分 </span>
- </p>
- </article><article
- class="article article-type-page"
- itemscope
- itemtype="http://schema.org/BlogPosting"
- >
- <div class="article-header">
- <h1 itemprop="name">
- <a
- class="article-title"
- href="/hugo-theme-pure/2019/08/treeset/"
- >Collections-Treeset</a
- >
-</h1>
-
- </div>
- <p class="article-meta"><span class="article-date">
- <i class="icon icon-calendar-check"></i>
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/treeset/" class="">
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST" itemprop="datePublished">2019-08-15</time>
-</a>
-</span><span class="article-category">
- <i class="icon icon-folder"></i>
- <a class="article-category-link" href="/hugo-theme-pure/categories/corejava/"> CoreJava </a>
-</span>
- <span class="article-tag">
- <i class="icon icon-tags"></i>
- <a class="article-tag-link" href="/hugo-theme-pure/tags/collections/"> collections </a>
- </span>
-
- <span class="post-comment"><i class="icon icon-comment"></i> <a href="/hugo-theme-pure/2019/08/treeset/#comments" class="article-comment-link">评论</a></span>
- <span class="post-wordcount hidden-xs" itemprop="wordCount">字数统计:767字</span>
- <span class="post-readcount hidden-xs" itemprop="timeRequired">阅读时长:2分 </span>
- </p>
</article></div>
</main><footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
diff --git a/index.xml b/index.xml
index b668f7a..698f20a 100644
--- a/index.xml
+++ b/index.xml
@@ -5,7 +5,7 @@
<link>https://xiaohei.im/hugo-theme-pure/</link>
<description>Recent content on 赵小黑的博客</description>
<generator>Hugo -- gohugo.io</generator>
- <lastBuildDate>Wed, 06 Nov 2019 19:08:56 +0800</lastBuildDate>
+ <lastBuildDate>Sat, 16 Nov 2019 14:24:40 +0800</lastBuildDate>
<atom:link href="https://xiaohei.im/hugo-theme-pure/index.xml" rel="self" type="application/rss+xml" />
@@ -13,19 +13,28 @@
<item>
<title>Posts</title>
<link>https://xiaohei.im/hugo-theme-pure/posts/</link>
- <pubDate>Wed, 06 Nov 2019 19:08:56 +0800</pubDate>
+ <pubDate>Sat, 16 Nov 2019 14:24:40 +0800</pubDate>
<guid>https://xiaohei.im/hugo-theme-pure/posts/</guid>
<description></description>
</item>
<item>
+ <title>Collections</title>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/</link>
+ <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/</guid>
+ <description></description>
+ </item>
+
+ <item>
<title>关于自己</title>
<link>https://xiaohei.im/hugo-theme-pure/about/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
<guid>https://xiaohei.im/hugo-theme-pure/about/</guid>
- <description>xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&amp;gt; http://1t.click/aCyT @githubchart</description>
+ <description>xiaoheiAh~ 联系方式 github: @xiaoheiAh gmail: xiaohei.zyx@gmail.com 节点推荐 -&amp;gt; http://1t.click/aCyT 关注的大佬博客 面向信仰编程 真·面向信仰,硬核,主 go ,其他技术也很在行, kubernetes 贡献者 @githubchart</description>
</item>
<item>
diff --git a/posts/index.html b/posts/index.html
index dbad80c..0b6eb28 100644
--- a/posts/index.html
+++ b/posts/index.html
@@ -36,7 +36,7 @@
<meta property="og:type" content="website" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/posts/" />
-<meta property="og:updated_time" content="2019-11-06T19:08:56+08:00" />
+<meta property="og:updated_time" content="2019-11-16T14:24:40+08:00" />
<meta itemprop="name" content="Posts">
<meta itemprop="description" content="">
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -278,7 +278,7 @@
<article class="content article article-archives article-type-list" itemscope="">
<header class="article-header">
<h1 itemprop="title">Posts</h1>
- <p class="text-muted">共 23 篇文章</p>
+ <p class="text-muted">共 19 篇文章</p>
</header>
<div class="article-body">
@@ -296,6 +296,33 @@
aria-labelledby="heading2019">
<div class="panel-body">
<div class="collection">
+<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
+ <div class="panel-body">
+ <div class="article-meta">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST"
+ itemprop="datePublished">2019-11-16</time>
+ </div>
+ <h3 class="article-title" itemprop="name">
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/">Redis-复制功能探索</a>
+ </h3>
+ </div>
+ <div class="panel-footer">
+ <a href="https://xiaohei.im/hugo-theme-pure/tags/redis" class="label label-default mb">redis</a>
+ </div>
+</article>
+
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST"
+ itemprop="datePublished">2019-11-14</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-事件
+</a>
+
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST"
+ itemprop="datePublished">2019-11-08</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-AOF持久化
+</a>
+
<a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-11-06 19:08:56 &#43;0800 CST"
itemprop="datePublished">2019-11-06</time>
@@ -323,7 +350,7 @@
<a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="collection-item" itemprop="url" target="_blank" >
<time datetime="2019-10-24 09:59:11 &#43;0800 CST"
itemprop="datePublished">2019-10-24</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Redis - 数据结构
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-数据结构
</a>
<a href="https://xiaohei.im/hugo-theme-pure/2019/10/rabbitmq-ack-confirm/" class="collection-item" itemprop="url" target="_blank" >
@@ -391,48 +418,6 @@
itemprop="datePublished">2019-08-16</time>
<span>&nbsp;&nbsp;&nbsp;</span>[LeetCode In Rust]001-Two Sum
</a>
-
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/" class="collection-item" itemprop="url" target="_blank" >
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
- itemprop="datePublished">2019-08-15</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Collections-Arraylist
-</a>
-
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/" class="collection-item" itemprop="url" target="_blank" >
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
- itemprop="datePublished">2019-08-15</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Collections-Hashmap
-</a>
-
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/hashset/" class="collection-item" itemprop="url" target="_blank" >
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
- itemprop="datePublished">2019-08-15</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Collections-Hashset
-</a>
-
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/" class="collection-item" itemprop="url" target="_blank" >
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
- itemprop="datePublished">2019-08-15</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Collections-Linkedhashmap
-</a>
-
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/" class="collection-item" itemprop="url" target="_blank" >
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
- itemprop="datePublished">2019-08-15</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Collections-Linkedlist
-</a>
-
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/treemap/" class="collection-item" itemprop="url" target="_blank" >
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
- itemprop="datePublished">2019-08-15</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Collections-Treemap
-</a>
-
-<a href="https://xiaohei.im/hugo-theme-pure/2019/08/treeset/" class="collection-item" itemprop="url" target="_blank" >
- <time datetime="2019-08-15 13:19:18 &#43;0800 CST"
- itemprop="datePublished">2019-08-15</time>
- <span>&nbsp;&nbsp;&nbsp;</span>Collections-Treeset
-</a>
</div>
</div></div></div></section>
</article>
diff --git a/posts/index.xml b/posts/index.xml
index 2e905af..3929df8 100644
--- a/posts/index.xml
+++ b/posts/index.xml
@@ -5,12 +5,41 @@
<link>https://xiaohei.im/hugo-theme-pure/posts/</link>
<description>Recent content in Posts on 赵小黑的博客</description>
<generator>Hugo -- gohugo.io</generator>
- <lastBuildDate>Wed, 06 Nov 2019 19:08:56 +0800</lastBuildDate>
+ <lastBuildDate>Sat, 16 Nov 2019 14:24:40 +0800</lastBuildDate>
<atom:link href="https://xiaohei.im/hugo-theme-pure/posts/index.xml" rel="self" type="application/rss+xml" />
<item>
+ <title>Redis-复制功能探索</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/replication/</link>
+ <pubDate>Sat, 16 Nov 2019 14:24:40 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/replication/</guid>
+ <description>&lt;p&gt;之前多&lt;code&gt;redis&lt;/code&gt; 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 &lt;a href=&#34;https://redis.io/topics/replication&#34;&gt;https://redis.io/topics/replication&lt;/a&gt;&lt;/p&gt;</description>
+ </item>
+
+ <item>
+ <title>Redis-事件</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/event/</link>
+ <pubDate>Thu, 14 Nov 2019 15:01:45 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/event/</guid>
+ <description>&lt;blockquote&gt;
+&lt;p&gt;&lt;strong&gt;事件驱动程序设计&lt;/strong&gt;(英语:&lt;strong&gt;Event-driven programming&lt;/strong&gt;)是一种电脑&lt;a href=&#34;https://zh.wikipedia.org/wiki/程式設計&#34;&gt;程序设计&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/模型&#34;&gt;模型&lt;/a&gt;。这种模型的程序运行流程是由用户的动作(如&lt;a href=&#34;https://zh.wikipedia.org/wiki/滑鼠&#34;&gt;鼠标&lt;/a&gt;的按键,键盘的按键动作)或者是由其他程序的&lt;a href=&#34;https://zh.wikipedia.org/wiki/訊息&#34;&gt;消息&lt;/a&gt;来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由&lt;a href=&#34;https://zh.wikipedia.org/wiki/程式設計師&#34;&gt;程序员&lt;/a&gt;来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在&lt;a href=&#34;https://zh.wikipedia.org/w/index.php?title=互動程序&amp;amp;action=edit&amp;amp;redlink=1&#34;&gt;交互程序&lt;/a&gt;(Interactive program)的情况下孕育而生的。 &lt;a href=&#34;https://zh.wikipedia.org/wiki/事件驅動程式設計&#34;&gt;&amp;ndash;wikipedia&lt;/a&gt;&lt;/p&gt;
+&lt;/blockquote&gt;</description>
+ </item>
+
+ <item>
+ <title>Redis-AOF持久化</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/aof/</link>
+ <pubDate>Fri, 08 Nov 2019 15:18:05 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/aof/</guid>
+ <description>&lt;p&gt;&lt;code&gt;RDB&lt;/code&gt; 和 &lt;code&gt;AOF&lt;/code&gt; 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 &lt;strong&gt;写命令&lt;/strong&gt; 保证数据库的状态.&lt;/p&gt;</description>
+ </item>
+
+ <item>
<title>Redis-RDB持久化</title>
<link>https://xiaohei.im/hugo-theme-pure/2019/11/rdb/</link>
<pubDate>Wed, 06 Nov 2019 19:08:56 +0800</pubDate>
@@ -47,7 +76,7 @@
</item>
<item>
- <title>Redis - 数据结构</title>
+ <title>Redis-数据结构</title>
<link>https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/</link>
<pubDate>Thu, 24 Oct 2019 09:59:11 +0800</pubDate>
@@ -158,68 +187,5 @@
<description></description>
</item>
- <item>
- <title>Collections-Arraylist</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/</link>
- <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
-
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/</guid>
- <description></description>
- </item>
-
- <item>
- <title>Collections-Hashmap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/</link>
- <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
-
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/</guid>
- <description></description>
- </item>
-
- <item>
- <title>Collections-Hashset</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/hashset/</link>
- <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
-
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/hashset/</guid>
- <description></description>
- </item>
-
- <item>
- <title>Collections-Linkedhashmap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/</link>
- <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
-
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/</guid>
- <description></description>
- </item>
-
- <item>
- <title>Collections-Linkedlist</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/</link>
- <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
-
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/</guid>
- <description></description>
- </item>
-
- <item>
- <title>Collections-Treemap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/treemap/</link>
- <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
-
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/treemap/</guid>
- <description></description>
- </item>
-
- <item>
- <title>Collections-Treeset</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/treeset/</link>
- <pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
-
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/treeset/</guid>
- <description></description>
- </item>
-
</channel>
</rss>
diff --git a/searchindex.json b/searchindex.json
index 343b6d2..7d0fc14 100644
--- a/searchindex.json
+++ b/searchindex.json
@@ -1 +1 @@
-{"categories":[{"title":"CoreJava","uri":"https://xiaohei.im/hugo-theme-pure/categories/corejava/"},{"title":"Hystrix","uri":"https://xiaohei.im/hugo-theme-pure/categories/hystrix/"},{"title":"leetcode","uri":"https://xiaohei.im/hugo-theme-pure/categories/leetcode/"},{"title":"redis","uri":"https://xiaohei.im/hugo-theme-pure/categories/redis/"},{"title":"消息队列","uri":"https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"}],"posts":[{"content":"redis 为内存数据库,一旦服务器进程退出,服务器中的数据就不见了.所以内存中的数据需要持久化的硬盘中来保证可以在必要的时候进行故障恢复. RDB 就是 redis 提供的一种持久化方式.\n 官方关于持久化的文章: https://redis.io/topics/persistence\n 什么是 RDB? RDB 是 redis 提供的一种持久化方式,可以手动执行,也可以通过定时任务定期执行,可以将某个时间节点的数据库状态保存到一个 RDB 文件中,叫做 dump.rdb.如果开启了压缩算法( LZF )的支持,则可以利用算法减少文件大小.服务器意外宕机或者断电后重启都可以通过该文件来恢复数据库状态.\n如何执行? 有两个命令可以生成 RDB 文件.\n SAVE: 执行时进程阻塞,无法处理其他命令 BGSAVE: 新建一个子进程来后台生成 RDB 文件 具体实现逻辑在: src/rdb.c/rdbSave(),从官方文档可知,该实现是基于 cow 的.\n https://redis.io/topics/persistence\nThis method allows Redis to benefit from copy-on-write semantics.\n 如何载入? RDB 文件会在 redis 启动时自动载入.\n由于 AOF 持久化的实时性更好,所以如果同时开启了 AOF , RDB 两种持久化,会优先使用 AOF 来恢复.\nBGSAVE 执行时的状态 BGSAVE 执行期间会拒绝 SAVE/BGSAVE 的命令,避免产生 竞争条件.\nBGSAVE 执行期间 BGREWRITEAOF 命令会延迟到 BGSAVE 执行完之后执行.\nBGREWRITEAOF 在执行时, BGSAVE 命令会被拒绝.\nBGSAVE 和 BGREWRITEAOF 命令的权衡完全是性能方面的考虑.毕竟都会有大量的磁盘写入,影响性能.\n定时执行BGSAVE BGSAVE 不会阻塞服务器进程,所以 redis 允许用户通过配置, 定时执行 BGSAVE 命令.\n快照策略 Snapshotting 可以通过设置 N 秒内至少 M 次修改来触发一次 BGSAVE.\nsave 60 1000 # 60s内有至少1000次修改时 bgsave 一次 默认的保存条件 save 900 1 save 300 10 save 60 10000 dirty 计数器 \u0026amp; lastsave 属性 redis 中维护了一个计数器,来记录距离上一次 SAVE/BGSAVE 后服务器对所有数据库进行了多少次增删改,叫做 dirty计数器.属于 redisServer 结构体的属性之一.\nlastsave 是记录了上一次成功执行 SAVE/BGSAVE 的 UNIX时间戳 , 同样是 redisServer 结构体的属性之一.\n# src/server.h struct redisServer { ... long long dirty; /* Changes to DB from the last save */ time_t lastsave; /* Unix time of last successful save */ ... } 定时执行过程 redis 有一个定时任务 serverCron , 每隔 100ms 就会执行一次,用于维护服务器.该任务就会检查 save 设置的保存条件是否满足,满足则执行 BGSAVE\n满足条件逻辑 遍历设置的 save 参数, 计算当前时间到 lastsave 的间隔 interval , 如果 dirty \u0026gt; save.change \u0026amp; interval \u0026gt; save.seconds 那么就执行保存\nRDB 文件结构 https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format\n写下这篇文章时参考版本为 2019.09.05 更新的版本\n RDB 文件格式对读写进行了很多优化,这类优化导致其格式与内存中存在的形式极其相似,同时利用 LZF 压缩算法来优化文件的大小.一般来讲, redis 对象都会提前标记自身的大小,所以备份RDB 在读取这些 object 时,可以提前知道要分配多少内存.\n解析RDB结构 下面的代码展示的是 16 进制下 RDB 文件的结构,便于理解\n----------------------------# RDB is a binary format. There are no new lines or spaces in the file. 52 45 44 49 53 # 魔数 REDIS的16进制表示,代表这是个RDB文件 30 30 30 37 # 4位ascii码表示当前RDB版本号,这里表示\u0026quot;0007\u0026quot; = 7 ---------------------------- FE 00 # FE 表明这是数据库选择标记. 00 表示选中0号数据库 ----------------------------# Key-Value pair starts FD $unsigned int # FD 是秒级过期时间的标记. 紧接着是 4 byte unsigned int 过期时间 $value-type # 1 byte 标记 value 类型 - set, map, sorted set etc. $string-encoded-key # 经过编码后的键 $encoded-value # 值,编码格式取决去 $value-type ---------------------------- FC $unsigned long # FC 表明是毫秒级过期时间. 过期时间值是 8 bytes的 unsigned long,是一个unit时间戳 $value-type # 同上秒级时间 $string-encoded-key # 同上秒级时间 $encoded-value # 同上秒级时间 ---------------------------- $value-type # 这一栏是没有过期时间的key-value $string-encoded-key $encoded-value ---------------------------- FE $length-encoding # 前一个数据库的编码完成,选择新的数据库进行处理.数据库编号会根据 length-encoding 格式获得 ---------------------------- ... # Key value pairs for this database, additonal database FF ## 表明 RDB 文件结束了 8 byte checksum ## 8byte CRC 64 校验码 value type 1 byte 表示了 value 的类型.\n type(以下为十进制表示) 编码类型 0 String 1 List 2 Set 3 Sorted Set 4 Hash 9 Zipmap 10 Ziplist 11 Intset 12 Sorted Set in Ziplist 13 HashMap in Ziplist 键值编码格式 键(key)都是字符串,所以使用string 编码格式.\n值(value)就会有不同的区分:\n 如果 value type 为 0 ,会是简单的字符串. 如果 value type 为 9,10,11,12, 值会被包装为 string, 在读到该字符串后,会进一步解析. 如果 value type 为 1,2,3,4, 值会是一个字符串数组. Length Encoding 长度编码是用来存储对象的长度的.是一种可变字节码,旨在使用尽可能少的字节.\n如何工作? 从流中读取 1byte,得到高两位. 如果是 00 开头, 那么剩下 6 位表示长度 如果是 01 开头, 会再从流中读取 1byte,合起来总共 14 位作为长度. 如果是 10 开头, 会直接丢弃剩下的 6 位.再从流中读取 4bytes作为长度. 如果是 11 开头, 说明这个对象是一种特殊编码格式. 剩下的 6 位表示了它的格式类型.这个编码通常用来将数字存储为字符串,或者存储被编码过得字符串(String Encoding). 编码结果是? 从上述可得,可能的编码格式是这样的:\n 1 byte 最多存储到 63 2 bytes 最多存储到 16383 5 bytes 最多存储到 2^32 - 1 String Encoding redis 的字符串是二进制安全的,所以可以存储 anything. 没有任何字符串结尾的标记.最好将 redis 字符串视为一个字节数组.\n有三种类型的字符串:\n 长度编码字符串 这是最简单的一种,字符串的长度会利用 Length Encoding 编码作为前缀,后面跟着字符串的编码\n 数字作为字符串 这里就将上面 Length Encoding 的特殊编码格式联系起来了,数字作为字符串时以 11 开头,剩下的 6 位表示不同的数字类型\n 0 表示接下来是一个 8 位数字 1 表示接下来是一个 16 位数字 2 表示接下来是一个 32 位数字 压缩字符串 压缩字符串的 Length Encoding 还是以 11 开头的, 但是剩下的6 位二进制的值为 4, 表明后面读取到的是一个压缩字符串.压缩字符串会存储压缩前和压缩后的长度.解析规则如下:\n 根据 Length Encoding 读取压缩的长度 clen 根据 Length Encoding 读取未压缩的长度 从流中读取 clen bytes 的数据 利用 LZF 算法进行解析 分析RDB文件 利用 od 命令来分析来看看 rdb 文件长什么样子.我将 redis 数据库清空后,执行了 set msg hello,所以现在只有一个键 msg, 值为 hello.下面的命令第一行输出的是 16 进制,下面一行输出的是对应的 ascii. 下面进行解析~\n➜ od -A x -t x1c -v dump.rdb 0000000 52 45 44 49 53 30 30 30 39 fa 09 72 65 64 69 73 R E D I S 0 0 0 9 372 \\t r e d i s 0000010 2d 76 65 72 05 35 2e 30 2e 34 fa 0a 72 65 64 69 - v e r 005 5 . 0 . 4 372 \\n r e d i 0000020 73 2d 62 69 74 73 c0 40 fa 05 63 74 69 6d 65 c2 s - b i t s 300 @ 372 005 c t i m e 051 0000030 29 e8 c3 5d fa 08 75 73 65 64 2d 6d 65 6d c2 d0 ) 350 303 ] 372 \\b u s e d - m e m 302 007 0000040 07 10 00 fa 0c 61 6f 66 2d 70 72 65 61 6d 62 6c \\a 020 \\0 372 \\f a o f - p r e a m b l 0000050 65 c0 00 fe 00 fb 01 00 00 03 6d 73 67 05 68 65 e 300 \\0 376 \\0 373 001 \\0 \\0 003 m s g 005 h e 0000060 6c 6c 6f ff fc 0e 6b 79 fe 47 1a 36 l l o 377 374 016 k y 376 G 032 6 000006c 魔数和版本号 前 5 个字节就是我们看到的 REDIS,以及后四个字节对应的版本号9\n辅助字段 Aux Fields 这是 Version 7 之后加入的字段, Redis设计与实现 所使用的版本是没有这个,所以一开始有点懵~ 只能看代码了.\n# src/rdb.c /* 该函数负责执行 RDB 文件的写入 */ int rdbSave(char *filename, rdbSaveInfo *rsi) { //伪代码 1. 创建一个临时文件 temp-$pid.rdb,并处理创建失败的逻辑 2. 新建一个redis封装的I/O流 3. 写入rdb文件 rdbSaveRio() 4. 将文件重命名, 默认重命名为 dump.rdb 5. 更新服务器的一些状态: dirty计数器置0,更新lastsave等 } 然后我们来看下写入的 Aux Fields, 在函数 rdbSaveRio 中\nint rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) { // 忽略所有只看重点 if (server.rdb_checksum) rdb-\u0026gt;update_cksum = rioGenericUpdateChecksum; // 生成校验码 snprintf(magic,sizeof(magic),\u0026quot;REDIS%04d\u0026quot;,RDB_VERSION); // 生成魔数及版本号 if (rdbWriteRaw(rdb,magic,9) == -1) goto werr; // 写入魔数及版本号 if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr; // 写入 AuxFileds } /* Save a few default AUX fields with information about the RDB generated. */ int rdbSaveInfoAuxFields(rio *rdb, int flags, rdbSaveInfo *rsi) { int redis_bits = (sizeof(void*) == 8) ? 64 : 32; int aof_preamble = (flags \u0026amp; RDB_SAVE_AOF_PREAMBLE) != 0; /* Add a few fields about the state when the RDB was created. */ if (rdbSaveAuxFieldStrStr(rdb,\u0026quot;redis-ver\u0026quot;,REDIS_VERSION) == -1) return -1; if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;redis-bits\u0026quot;,redis_bits) == -1) return -1; if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;ctime\u0026quot;,time(NULL)) == -1) return -1; if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;used-mem\u0026quot;,zmalloc_used_memory()) == -1) return -1; /* Handle saving options that generate aux fields. */ if (rsi) { if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;repl-stream-db\u0026quot;,rsi-\u0026gt;repl_stream_db) == -1) return -1; if (rdbSaveAuxFieldStrStr(rdb,\u0026quot;repl-id\u0026quot;,server.replid) == -1) return -1; if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;repl-offset\u0026quot;,server.master_repl_offset) == -1) return -1; } if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;aof-preamble\u0026quot;,aof_preamble) == -1) return -1; return 1; } 以上可以看出会写入这些字段.\n redis-ver:版本号\n redis-bits:OS 操作系统位数 32\u0026frasl;64\n ctime:RDB文件创建时间\n used-mem:使用内存大小\n repl-stream-db:在server.master客户端中选择的数据库\n repl-id:当前实例 replication ID\n repl-offset:当前实例复制的偏移量\n 每一个属性写入前都会写入 0XFA, 标记这是一个辅助字段.在上面命令行输出中,ascii 展示为 372\n数据库相关标记 0000050 65 c0 00 fe 00 fb 01 00 00 03 6d 73 67 05 68 65 e 300 \\0 376 \\0 373 001 \\0 \\0 003 m s g 005 h e 这一行中的 0XFE 表示选择数据库,后面紧接着 00 即为,选择 0 号数据库. 0XFB 是标记了当前数据库中键存储的数量,这里用到了 Length Encoding, 01 是我们存储的字典中key-value的数量,00 是过期字典(expires)中的数量.\n redisDB中有两个属性, dict 记录了我们写入的所有键, expires 存储了我们设置有过期时间的键以及其过期时间.\n Key Value 结构 我们设置了 msg -\u0026gt; hello,在输出中是这样的.\n0000050 65 c0 00 fe 00 fb 01 00 00 03 6d 73 67 05 68 65 e 300 \\0 376 \\0 373 001 \\0 \\0 003 m s g 005 h e 在 msg 前面的字段 \\0 003, 表示他是 string 类型, 且长度为 3, 005 hello, 表示是长度为 5 的 hello.\n还有其他数据结构这里就不做展示了.\n结束符 \u0026amp; 校验码 0000060 6c 6c 6f ff fc 0e 6b 79 fe 47 1a 36 l l o 377 374 016 k y 376 G 032 6 最后一行输出中 0xff , 文件结束符, 剩下的八个字节就是 CRC64\n参考 https://cloud.tencent.com/developer/article/1179710\n Redis5.0 RDB文件解析\n ","id":0,"section":"posts","summary":"\u003cp\u003e\u003ccode\u003eredis\u003c/code\u003e 为内存数据库,一旦服务器进程退出,服务器中的数据就不见了.所以内存中的数据需要持久化的硬盘中来保证可以在必要的时候进行故障恢复. \u003ccode\u003eRDB\u003c/code\u003e 就是 \u003ccode\u003eredis\u003c/code\u003e 提供的一种持久化方式.\u003c/p\u003e","tags":["redis"],"title":"Redis-RDB持久化","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/rdb/","year":"2019"},{"content":"服务器中的数据库 redis的数据库是保存在一个db数组中的,默认会新建16个数组.\n# src/server.h struct redisServer { ... redisDb *db; // db 存放的数组 int dbnum; /* 根据该属性决定创建数据库数量 默认: 16 */ ... } 切换数据库 redis 数据库从 0 开始计算,通过 select 命令切换数据库. client 会有一个属性指向当前选中的 DB.\n# src/server.h typedef struct client { ... redisDb *db; /* 指向当前选中的redisDb */ ... } 键空间 redisDb 的结构是怎样的呢?\n# src/server.h /* Redis database representation. There are multiple databases identified * by integers from 0 (the default database) up to the max configured * database. The database number is the 'id' field in the structure. */ typedef struct redisDb { dict *dict; /* 键空间 */ dict *expires; /* Timeout of keys with a timeout set */ dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/ dict *ready_keys; /* Blocked keys that received a PUSH */ dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */ int id; /* Database ID */ long long avg_ttl; /* Average TTL, just for stats */ list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */ } redisDb; 键空间 指的是每一个数据库中存放用户设置键和值的地方. 可以看到上述结构中, dict 属性就是每一个数据库的键空间, 字典结构, 也就是我们命令的执行结构.例如 set msg \u0026quot;hello world~\u0026quot; .\n所以针对数据库的操作就是操作字典.\n读写键空间后的操作 维护 hit, miss 次数, 可以利用 info stats 查看 keyspace_hits 以及 keyspace_misses 读取一个键后会更新键的 LRU ,用于计算键的闲置时间 object idletime {key} 查看 服务器读取一个键后发现已经过期,则会删除这个键在执行其他操作 如果客户端 watch 了某个键, 该键修改之后,会被标记为 dirty, 从而事务程序可以注意到该键已经被修改了 服务器每修改一个键后, 都会对 dirty 计数器 +1 ,这个计数器会触发服务器的持久化和复制操作 服务器开启数据库通知之后,键修改后会发送相应的数据库通知 过期时间保存 上述的 redisDb 结构中有 expires 的字典, redis 就是将我们设置的过期时间存到了这个字典中.键就是数据库键,值是一个 long long 类型的整数, 保存了键的过期时间: 一个毫秒精度的 UNI\u0010X 时间戳.\nRedis的过期键删除策略 有这么三种删除方式.\n定时删除 设置键过期时间的同时,创建一个定时器,到期自动删除\n优点 内存友好,键过期就删除\n缺点 对 CPU 不友好,过期键较多时,会占用较长时间,CPU 资源紧张的情况下会影响服务器的响应时间和吞吐量 创建定时器需要用到 redis 的时间事件,实现方式为无序链表,查找效率低 惰性删除 无视键是否过期,每次从键空间取键时,先判断是否过期,过期就删除,没过期就返回.\n优点 对 CPU 友好,遇到过期键才删除\n缺点 如果过期键很多,且一直不会被访问,就会导致大量内存被浪费\n定期删除 定期的在数据库中检查,删除过期的键.定期删除策略是上面两种策略的折中方案.\n优点 每隔一段时间删除过期键,可以减少删除操作对 CPU 的影响 定期删除也可以减少过期键带来的内存浪费 难点 确定删除操作执行的时长和频率\nredis采用方案 惰性删除 + 定期删除\n惰性删除是在所有读写数据库命令执行之前检查键是否过期来实现的.\n定期删除是通过 redis 的定时任务执行.在规定的时间内,多次遍历服务器的各个数据库,从 expires 字典中 随机抽查 一部分键的过期时间.current_db 会记录当前函数检查的进度,并在下一次函数执行时,接着上次的执行.循环往复地执行.\nAOF,RDB \u0026amp; 复制功能对过期键的处理 生成 RDB 文件时,过期键不会被保存到新文件中 载入 RDB 文件 以主服务器运行:未过期的键被载入,过期键忽略 以从服务器运行:保存所有键,无论是否过期.由于主从服务器在进行数据同步时,从服务器数据库就会被清空,所以一般来讲,也不会造成什么影响. AOF 写入时,键过期还没有被删除,AOF 文件不会受到影响,当键被惰性删除或被定期删除后,AOF 文件会追加一条 DEL 命令来显示记录该键已被删除 AOF 重写时,会对键过期进行确认,过期补充些. 复制模式下,从服务器的过期键删除由主服务器控制. 主服务器删除一个键后,会显示发送 DEL 命令给从服务器. 从服务器接收读命令时,如果键已过期,也不会将其删除,正常处理 从服务器只在主服务器发送 DEL 命令才删除键 主从复制不及时怎么办?会有脏读现象~\n数据库通知 通过订阅的模式,可以实时获取键的变化,命令的执行情况.通过 redis 的 pub/sub 模式来实现.命令对数据库进行了操作后,就会触发该通知,置于能不能发送出去完全看你的配置了.\nnotify_keyspace_events 系统配置决定了服务器发送的配置类型.如果给定的 type 不是服务器允许发送的类型,程序就直接返回了.然后就判断能发送键通知就发送,能发送命令通知就发送.\n/* The API provided to the rest of the Redis core is a simple function: * * notifyKeyspaceEvent(char *event, robj *key, int dbid); * * 'event' is a C string representing the event name. * 'key' is a Redis object representing the key name. * 'dbid' is the database ID where the key lives. */ void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) { sds chan; robj *chanobj, *eventobj; int len = -1; char buf[24]; /* If any modules are interested in events, notify the module system now. * This bypasses the notifications configuration, but the module engine * will only call event subscribers if the event type matches the types * they are interested in. */ moduleNotifyKeyspaceEvent(type, event, key, dbid); /* If notifications for this class of events are off, return ASAP. */ if (!(server.notify_keyspace_events \u0026amp; type)) return; eventobj = createStringObject(event,strlen(event)); /* __keyspace@\u0026lt;db\u0026gt;__:\u0026lt;key\u0026gt; \u0026lt;event\u0026gt; notifications. */ if (server.notify_keyspace_events \u0026amp; NOTIFY_KEYSPACE) { chan = sdsnewlen(\u0026quot;__keyspace@\u0026quot;,11); len = ll2string(buf,sizeof(buf),dbid); chan = sdscatlen(chan, buf, len); chan = sdscatlen(chan, \u0026quot;__:\u0026quot;, 3); chan = sdscatsds(chan, key-\u0026gt;ptr); chanobj = createObject(OBJ_STRING, chan); pubsubPublishMessage(chanobj, eventobj); decrRefCount(chanobj); } /* __keyevent@\u0026lt;db\u0026gt;__:\u0026lt;event\u0026gt; \u0026lt;key\u0026gt; notifications. */ if (server.notify_keyspace_events \u0026amp; NOTIFY_KEYEVENT) { chan = sdsnewlen(\u0026quot;__keyevent@\u0026quot;,11); if (len == -1) len = ll2string(buf,sizeof(buf),dbid); chan = sdscatlen(chan, buf, len); chan = sdscatlen(chan, \u0026quot;__:\u0026quot;, 3); chan = sdscatsds(chan, eventobj-\u0026gt;ptr); chanobj = createObject(OBJ_STRING, chan); pubsubPublishMessage(chanobj, key); decrRefCount(chanobj); } decrRefCount(eventobj); } ","id":1,"section":"posts","summary":"","tags":["redis"],"title":"Redis-数据库长什么样?","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/db/","year":"2019"},{"content":"Redis有很多种数据结构,但其并没有直接使用这些数据结构来构建这个 NOSQL, 而是通过 对象系统 完成了对所有数据结构的统一管理, 实现内存回收, 对象共享等特性~\n类型及编码 在 Redis 中使用任何命令操作,都是操作的一个对象.有键对象,值对象.\nset msg \u0026quot;hello~\u0026quot; # msg 为键对象, \u0026quot;hello~\u0026quot; 为值对象 每个对象都会有如下的结构:\ntypedef struct redisObject { unsigned type:4; // 类型 unsigned encoding:4; // 编码 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; // 引用计数 void *ptr; // 指向底层实现数据结构的指针 } robj; type 类型 type 指明了该对象的类型. redis 中类型有如下几种\n/* The actual Redis Object */ #define OBJ_STRING 0 /* String object. */ #define OBJ_LIST 1 /* List object. */ #define OBJ_SET 2 /* Set object. */ #define OBJ_ZSET 3 /* Sorted set object. */ #define OBJ_HASH 4 /* Hash object. */ #define OBJ_MODULE 5 /* Module object. */ #define OBJ_STREAM 6 /* Stream object. */ redis 中键都为字符串对象,利用 type 命令可以查看值对象的类型\nreids\u0026gt; type language list encoding 编码 encoding 属性记录了该对象使用的什么数据结构存储底层的实现,即 *ptr 所指向的那个数据结构.以下是目前的编码类型.\n/* Objects encoding. Some kind of objects like Strings and Hashes can be * internally represented in multiple ways. The 'encoding' field of the object * is set to one of this fields for this object. */ #define OBJ_ENCODING_RAW 0 /* Raw representation */ #define OBJ_ENCODING_INT 1 /* Encoded as integer */ #define OBJ_ENCODING_HT 2 /* Encoded as hash table */ #define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ #define OBJ_ENCODING_INTSET 6 /* Encoded as intset */ #define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */ #define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */ #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */ #define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */ 基本上每种类型的对象都会对应两种编码类型,可以动态的根据用户输入的值提供最有的数据结构,减少资源消耗.\n字符串对象 字符串对象有三种编码格式. int,embstr,raw,不同长度不同格式有不一样的编码类型.\n47.100.254.74:6379\u0026gt; set msg \u0026quot;abcdefg\u0026quot; OK (0.53s) 47.100.254.74:6379\u0026gt; object encoding msg \u0026quot;embstr\u0026quot; 47.100.254.74:6379\u0026gt; set msg \u0026quot;abcdefghijklmnopqrstuvwxyz01234567890123456789\u0026quot; OK 47.100.254.74:6379\u0026gt; object encoding msg \u0026quot;raw\u0026quot; 47.100.254.74:6379\u0026gt; set msg 123 OK 47.100.254.74:6379\u0026gt; object encoding msg \u0026quot;int\u0026quot; embstr vs raw 一个字符串对象包括 redisObject 和 sds 两部分组成.正常情况下是需要分配两次内存来创建这两个结构.这也是raw 的格式,但是如果当 value 长度较短时, (由于 redis 使用的是 jemalloc 分配内存)我们可以将内存分配控制在一次,将 RedisObject 和 sds 分配在连续的内存空间,这也就是 embstr 编码格式了.那多短算短呢?\n在此之前先了解下创建一个 redisObject 时所占用的空间.\nembstr编码是由 代表着 字符串的数据结构是 SDS.假设为 sdshdr8\nstruct sdshdr8 { uint8_t len; /* 1byte used */ uint8_t alloc; /* 1byte excluding the header and null terminator */ unsigned char flags; /* 1byte 3 lsb of type, 5 unused bits */ char buf[]; }; jemalloc 可以分配 8/16/32/64 字节大小的内存,从上可以发现最少的内存需要占用 19 字节, Redis 在总体大于 64 字节时,会改为 raw 存储. 所以 embstr 形式时最大长度是 64 - 19 - 结束符\\0长度 = 44\n编码转换 由于 redis 没有为 embstr 编写修改相关的程序,所以是只读的, 如果对其执行任何修改命令,就会变为 raw 格式.\n类型检查 redis 中的操作命令一般有两种: 所有类型都能用的(DEL, EXPIRE\u0026hellip;), 特定类型适用的(各种数据类型对应的命令).若操作键的命令不对, redis 会提示报错.\n47.100.254.74:6379\u0026gt; set numbers 1 OK 47.100.254.74:6379\u0026gt; object encoding numbers \u0026quot;int\u0026quot; 47.100.254.74:6379\u0026gt; rpush numbers a (error) WRONGTYPE Operation against a key holding the wrong kind of value 如何实现? 利用 RedisObject 的type 来控制.在输入一个命令时, 服务器会先检查输入键所对应的的值对象是否为命令对应的类型,是的话就执行,不是就报错.\n多态命令 同一种数据结构可能有多种编码格式.比如字符串对象的编码格式可能有 int, embstr, raw.所以当命令执行前,还需要根据值对象的编码来选择正确的命令来实现.\n比如想要执行 llen 获取 list 长度, 如果编码为 ziplist, 那么程序就会使用 ziplist 对应的函数来计算, 编码为 quicklist 时则是使用 quicklist 对应的函数来计算. 此为命令的 多态 .\n内存回收 redis 利用引用计数来实现内存回收机制.由 RedisObject 中的 refcount 属性记录.\n引用计数是有导致循环引用的弊端的,那么redis为啥还是会用的?找了很久也没有找到答案.\n有一个说法是: 引用的复杂度很低,不太容易导致循环引用.就一切从简呗.\n对象共享 对象共享指的是创建一次对象后,后面如果还有客户端需要创建同样的值对象则直接把现在这个的引用只给他,引用计数加1,可以节省内存的开销.类似 Java 常量池. 所以refcount 也被用来做对象共享的.\nredis 在初始化服务器时, 会创建 0 - 9999 一万个整数字符串, 为了节省资源.\n为什么不共享其他的复杂对象? 整数复用几率很大 整数比较算法时间复杂度是 O(1), 字符串是 O(N), hash/list 复杂度是 O(n2) 键的空转时长 redisObject 的 lru 属性记录着该对象最后一次被命令程序访问的时间.该属性在内存回收中有很大的作用.\n空转时长指的是now() - lru\n47.100.254.74:6379\u0026gt; object idletime numbers (integer) 4023 ","id":2,"section":"posts","summary":"\u003cp\u003eRedis有很多种数据结构,但其并没有直接使用这些数据结构来构建这个 \u003ccode\u003eNOSQL\u003c/code\u003e, 而是通过 \u003ccode\u003e对象系统\u003c/code\u003e 完成了对所有数据结构的统一管理, 实现内存回收, 对象共享等特性~\u003c/p\u003e","tags":["redis"],"title":"Redis-万物皆「对象」","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/obj/","year":"2019"},{"content":"分布式锁有很多中实现(纯数据库,zookeeper,redis),纯数据库的受限于数据库性能,zk 可以保证加锁的顺序,是公平锁.Redis中的实现就是接下来要学习的.\n为什么使用分布式锁? 在分布式环境下想要保证只能有一个请求更新一条数据,普通的加锁(比如 Java 中的 synchronized,JUC 中的各种 Lock)都不能胜任. 分布式锁的意义在于可以将操作锁的权利中心化,从而串行控制业务的执行.但是使用分布式锁也有很多弊端,后面再说.\n分布式锁的特点? 互斥:具有强排他性,需要保证不同节点不同线程的互斥 可重入:同一个节点的同一个线程如果获得了锁,那也可以再次获得 高效,高可用:加锁解锁要高效,高可用保证分布式锁服务不会宕机失效 阻塞/非阻塞:像 ReentrantLock 支持 lock, tryLock, tryLock(long timeout) 支持公平锁/非公平锁(Option) 如何使用分布式锁? Redis中有多种实现分布式锁的方式,一个一个看看.\n简单粗暴版 设置一个坑,让所有节点去抢就好.即语义为: set if not exist, 抢到后执行逻辑,逻辑完成后在del即可.\nredis 2.8 版本之前我们会通过以下方式:\nsetnx {resource-name} {anystring} 我们还需要加一个过期时间,以免各种异常宕机情况导致锁无法释放的问题.\nexpire key {max-lock-time} 这两条命令并不是原子操作的,所以我们需要通过 Lua 脚本来保证其原子性\nredis 2.8 版本之后官方提供了 nx ex 的原子操作,使用起来更加简单了.\nset {resource-name} {anystring} nx ex {max-lock-time} Redission版 https://github.com/redisson/redisson\n Redission 和 Jedis 都是 Java 中的 redis 客户端, Jedis 使用的是阻塞式 I/O, 而 Redission 使用的 Netty 来进行通信,而且 API 封装更友好, 继承了 java.util.concurrent.locks.Lock 的接口,可以像操作本地 Lock 一样操作分布式锁. 而且 Redission 还提供了不同编程模式的 API: sync/async, Reactive, RxJava, 非常人性化. Redission 有丰富的接口实现以及对不同异常情况的处理设计很值得学习.\n// 1. 设置 config Config config = new Config(); // 2. 创建 redission 实例 RedissonClient redisson = Redisson.create(config); // 4. 获取锁 RLock lock = redisson.getLock(\u0026quot;myLock\u0026quot;); // 5. 加锁 // 方式一 // 加锁以后10秒钟自动解锁 // 无需调用unlock方法手动解锁 lock.lock(10, TimeUnit.SECONDS); // 方式二 // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁 boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); if (res) { try { ... } finally { lock.unlock(); } } // 方式三 // 异步加锁 RLock lock = redisson.getLock(\u0026quot;anyLock\u0026quot;); lock.lockAsync(); lock.lockAsync(10, TimeUnit.SECONDS); Future\u0026lt;Boolean\u0026gt; res = lock.tryLockAsync(100, 10, TimeUnit.SECONDS); RedLock https://redis.io/topics/distlock\n 上述的分布式锁实现都是基于单实例实现,所以会出现单点问题.胆大RedLock 基本原理是利用多个 Redis 集群,用多数的集群加锁成功,减少Redis某个集群出故障,造成分布式锁出现问题的概率。\n加锁过程 客户端获取当前的时间戳。 对 N 个 Redis 实例进行获取锁的操作,具体的操作同单机分布式锁。对 Redis 实例的操作时间需要远小于分布式锁的超时时间,这样可以保证在少数 Redis 节点 Down 掉的时候仍可快速对下一个节点进行操作。 客户端会记录所有实例返回加锁成功的时间,只有从多半的实例(在这里例子中 \u0026gt;= 3)获取到了锁,且操作的时间远小于分布式锁的超时时间,锁才被人为是正确获取。 如果锁被成功获取了,当前分布式锁的合法时间为初始设定的合法时间减去上锁所花的时间。 若分布式锁获取失败,会强制对所有实例进行锁释放的操作,即使这个实例上不存在相应的键值。 分布式锁的一些问题 锁被其他客户端释放 如果线程 A 在获取锁后处理业务时间过长,导致锁被自动释放了,此时 线程 B 重新获取到了锁. 线程 A 在执行完业务逻辑后释放锁(DEL操作),这是就会把线程 B 获取到的锁给释放掉.\n如何解决? 在设置 value 时,生成一个随机 token, 删除 key 时先做判断,只有在 token 与自己持有的相等时,才能删除. 由于需要保证原子性, 我们需要通过 Lua 脚本来实现.像下面这样,不过 Redission 已经有对应的实现了.\nif redis.call(\u0026quot;get\u0026quot;,KEYS[1]) == ARGV[1] then return redis.call(\u0026quot;del\u0026quot;,KEYS[1]) else return 0 end 超时问题 如果在加锁和释放锁之间的业务逻辑过长,超出了锁的过期时间,那么就可能会导致另一个线程获取到锁,导致逻辑不能严格的串行执行.所以分布式锁的初衷是: 逻辑越短越好,持有锁的时间越短越好.\n如何解决? 这个目前没有太好解决的方案,后面如果看到了,就更新到这里.自己觉得: 尽量保证持锁时间短,优化代码逻辑.虽然可以延长锁的时间,但是会影响吞吐量的吧.如果真的有多个客户端持有了锁,还需要尽量保证业务逻辑中数据的幂等性,日志监控,及时报警,这样也可以做到尽快的人工介入.\n 技术莫得银弹~适合的才是最好的.\n 时钟不一致 RedLock 强依赖时间,所以机器时间不一致会有很大的问题\n如何解决? 人为调整 NTP自动调整: 可以将时间精度控制在一定范围内. 性能、故障恢复和 fsync 假设 Redis 没有持久性,当一个客户端获得了 5 个实例中的 3 个锁,若 3 个锁所在的实例 Down 掉了,实例再次启动时,其他的客户端也可以再次获得锁。\n这个问题会因为开启了 Redis 的持久化而改观,对于 AOF 持久化(区别与 RDB 的二进制持久化,是文本持久化)。默认采用的是每秒钟通过 fsync 落盘,这意味着会丢失一秒内的数据,如果需要更有安全保证的持久化,可以设置 fsync=always,但对应的会损失一部分性能。\n更好的解决办法是在实例 Down 掉后延迟一个略长于锁合法时间的时间,这样就可以保证在实例启动起来时锁一定是过期的,从而无须以损失性能为代价而使用 fsync=always 的持久化。\n参考 再有人问你分布式锁,这篇文章扔给他 RedLock中译 ","id":3,"section":"posts","summary":"\u003cp\u003e分布式锁有很多中实现(纯数据库,zookeeper,redis),纯数据库的受限于数据库性能,zk 可以保证加锁的顺序,是公平锁.Redis中的实现就是接下来要学习的.\u003c/p\u003e","tags":["分布式锁","redis"],"title":"Redis-分布式锁","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/","year":"2019"},{"content":"系统学习 redis 相关的知识,从数据结构开始~\nString 字符串 Redis 的字符串是 动态字符串, 长度可变,自动扩容。利用预分配空间方式减少内存的分配。默认分配 1M 大小的内存。扩容时加倍现有空间,最大占用为 512M.\n常用命令 SET,SETNX\u0026hellip;\n结构 struct SDS\u0026lt;T\u0026gt; { T capacity; // 数组容量 T len; // 数组长度 byte flags; // 特殊标识位,不理睬它 byte [] content; // 数组内容 } Redis 中的字符串叫做 Simple Dynamic String, 上述 struct 是一个简化版,实际的代码中,redis 会根据 str 的不同长度,使用不同的 SDS, 有 sdshdr8, sdshdr16, sdshdr32 等等\u0026hellip; 但结构体都是如上的类型.\ncapacity 存储数组的长度,len 表示数组的实际长度。需要注意的是: string 的字符串是以 \\0 结尾的,这样可以便于调试打印,还可以直接使用 glibc 的字符串函数进行操作.\n字符串存储 字符串有两种存储方式,长度很短时,使用 emb 形式存储,长度超过 44 时,使用 raw 形式存储.\n可以使用 debug object {your_string} 来查看存储形式\n\u0026gt; set codehole abcdefghijklmnopqrstuvwxyz012345678912345678 OK \u0026gt; debug object codehole Value at:0x7fec2de00370 refcount:1 encoding:embstr serializedlength:45 lru:5958906 lru_seconds_idle:1 \u0026gt; set codehole abcdefghijklmnopqrstuvwxyz0123456789123456789 OK \u0026gt; debug object codehole Value at:0x7fec2dd0b750 refcount:1 encoding:raw serializedlength:46 lru:5958911 lru_seconds_idle:1 WHY? 首先需要解释 RedisObject, 所有 Redis 对象都有的结构体\nstruct RedisObject { int4 type; // 4bits int4 encoding; // 4bits int24 lru; // 24bits int32 refcount; // 4bytes void *ptr; // 8bytes,64-bit system } robj; 不同的对象具有不同的类型 type (4bit),同一个类型的 type 会有不同的存储形式 encoding (4bit),为了记录对象的 LRU 信息,使用了 24 个 bit 来记录 LRU 信息。每个对象都有个引用计数,当引用计数为零时,对象就会被销毁,内存被回收。ptr 指针将指向对象内容 (body) 的具体存储位置。这样一个 RedisObject 对象头需要占据 16 字节的存储空间。\n接着我们再看 SDS 结构体的大小,在字符串比较小时,SDS 对象头的大小是 capacity+3,至少是 3。意味着分配一个字符串的最小空间占用为 19 字节 (16+3)。\n一张图解释:\nList 列表 Redis 的列表是用链表来实现的,插入删除 O (1), 查找 O (n), 列表弹出最后一个元素时,数据结构删除,内存回收.\n常用命令 LPUSH,LPOP,RPUSH,RPOP,LRANGE\u0026hellip;\n列表的数据结构 列表底层的存储结构并不是简简单单的一个链表~通过 ziplist 连接起来组成 quicklist.\nziplist 压缩列表 在列表元素较少时,redis 会使用一块连续内存来进行存储,这个结构就是 ziplist. 所有的元素紧挨着存储.\n\u0026gt; zadd z_lang 1 java 2 rust 3 go (integer) 3 \u0026gt; debug object z_lang Value at:0x7fde1c466660 refcount:1 encoding:ziplist serializedlength:34 lru:11974320 lru_seconds_idle:11 可以看到上述输出 encoding 为 ziplist.\nstruct ziplist\u0026lt;T\u0026gt; { int32 zlbytes; // 整个压缩列表占用字节数 int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个节点 int16 zllength; // 元素个数 T [] entries; // 元素内容列表,挨个挨个紧凑存储 int8 zlend; // 标志压缩列表的结束,值恒为 0xFF } zltail_offset 是为了支持双向遍历才设计的,可以快速定位到最后一个元素,然后倒着遍历.\nentry 会随着容纳的元素不同而结构不同.\nstruct entry { int\u0026lt;var\u0026gt; prevlen; // 前一个 entry 的字节长度 int\u0026lt;var\u0026gt; encoding; // 元素类型编码 optional byte [] content; // 元素内容 } prevlen 表示前一个 entry 的字节长度,倒序遍历时,可以根据这个字段来推算前一个 entry 的位置。它是变长的整数,字符串长度小于 254 ( 0XFE ) 时,使用一个字节表示,大于等于 254, 使用 5 个字节来表示。第一个字节是 254, 剩余四个字节表示字符串长度.\nencoding 编码类型 encoding 存储编码类型信息,ziplist 通过其来决定 content 内容的形式。所以其设计是很复杂的.\n 00xxxxxx 最大长度位 63 的短字符串,后面的 6 个位存储字符串的位数,剩余的字节就是字符串的内容。 01xxxxxx xxxxxxxx 中等长度的字符串,后面 14 个位来表示字符串的长度,剩余的字节就是字符串的内容。 10000000 aaaaaaaa bbbbbbbb cccccccc dddddddd 特大字符串,需要使用额外 4 个字节来表示长度。第一个字节前缀是 10,剩余 6 位没有使用,统一置为零。后面跟着字符串内容。不过这样的大字符串是没有机会使用的,压缩列表通常只是用来存储小数据的。 11000000 表示 int16,后跟两个字节表示整数。 11010000 表示 int32,后跟四个字节表示整数。 11100000 表示 int64,后跟八个字节表示整数。 11110000 表示 int24,后跟三个字节表示整数。 11111110 表示 int8,后跟一个字节表示整数。 11111111 表示 ziplist 的结束,也就是 zlend 的值 0xFF。 1111xxxx 表示极小整数,xxxx 的范围只能是 (0001~1101), 也就是 1~13,因为 0000、1110、1111 都被占用了。读取到的 value 需要将 xxxx 减 1,也就是整数 0~12 就是最终的 value。 增加元素 ziplist 是连续存储的,没有多余空间,这意味着每次插入一个元素,就需要扩展内存。如果占用内存过大,重新分配内存和拷贝内存就会有很大的消耗。所以其缺点是不适合存储 大型字符串, 存储元素不宜 过多.\n级联更新 每一个 entry 都是有 prevlen, 而且时而为 1 字节存储,时而为 5 字节存储,取决于字符串的字节长度是否大于 254, 如果某次操作导致字节长度从 254 变为 256, 那么其下一个节点所存储的 prevlen 就要从 1 个字节变为 5 个字节来存储,如果下一个节点刚好因此超过了 254 的长度,那么下下个节点也要更新\u0026hellip; 这就是级联更新了~\nquicklist Redis 中 list 的存储结构就是 quicklist. 下面的 language 是一个记录编程语言的集合。可以看到 encoding 即为 quicklist.\n\u0026gt; debug object language Value at:0x7fde1c4665f0 refcount:1 encoding:quicklist serializedlength:29 lru:11974264 lru_seconds_idle:62740 ql_nodes:1 ql_avg_node:3.00 ql_ziplist_max:-2 ql_compressed:0 ql_uncompressed_size:27 Redis 的 quicklist 是一种基于 ziplist 实现的可压缩(quicklistLZF)的双向链表,结合了链表和 ziplist 的 优点 组成的。下面可以看下他的结构体.\n/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist. * 'count' is the number of total entries. * 'len' is the number of quicklist nodes. * 'compress' is: -1 if compression disabled, otherwise it's the number * of quicklistNodes to leave uncompressed at ends of quicklist. * 'fill' is the user-requested (or default) fill factor. */ /** * quicklist 是一个 40byte (64 位系统) 的结构 */ typedef struct quicklist { quicklistNode *head; quicklistNode *tail; unsigned long count; /* 元素总数 */ unsigned long len; /* quicklistNode 的长度 */ int fill : 16; /* ziplist 的最大长度 */ unsigned int compress : 16; /* 节点压缩深度 */ } quicklist; typedef struct quicklistNode { struct quicklistNode *prev; struct quicklistNode *next; unsigned char *zl; /* 没有压缩,指向 ziplist, 否则指向 quicklistLZF unsigned int sz; /* ziplist 字节总数 */ unsigned int count : 16; /* ziplist 元素数量 */ unsigned int encoding : 2; /* RAW==1 or LZF==2 */ ... } quicklistNode; //LZF 无损压缩算法,压缩过的 ziplist typedef struct quicklistLZF { // 未压缩之前的大小 unsigned int sz; /* LZF size in bytes*/ // 存放压缩过的 ziplist 数组 char compressed []; } quicklistLZF; 一张图展示结构 压缩深度 quicklist 默认的压缩深度是 0,也就是不压缩。压缩的实际深度由配置参数 list-compress-depth 决定。为了支持快速的 push/pop 操作,quicklist 的首尾两个 ziplist 不压缩,此时深度就是 1。如果深度为 2,就表示 quicklist 的首尾第一个 ziplist 以及首尾第二个 ziplist 都不压缩。\nSet 集合 Redis 的集合相当于 Java 语言里面的 HashSet,它内部的键值对是无序的唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值NULL。\n常用命令 SADD,SMEMBERS,SPOP,SISMEMBER,SCARD\u0026hellip;\nHash 哈希 Redis 的 Hash相当于Java 中的 HashMap, 数组 + 链表的二维结构.与 HashMap 不同的地方在于 rehash 方式不同, HashMap 中的 rehash 是阻塞式的, 需要一次性全部 rehash, 而 redis 为了性能考虑, 采用的是 渐进式 rehash.\n常用命令 HSET,HGET,HMSET,HLEN\u0026hellip;\n\u0026gt; hset books java \u0026quot;think in java\u0026quot; # 命令行的字符串如果包含空格,要用引号括起来 (integer) 1 \u0026gt; hset books golang \u0026quot;concurrency in go\u0026quot; (integer) 1 \u0026gt; hset books python \u0026quot;python cookbook\u0026quot; (integer) 1 \u0026gt; hgetall books # entries(),key 和 value 间隔出现 1) \u0026quot;java\u0026quot; 2) \u0026quot;think in java\u0026quot; 3) \u0026quot;golang\u0026quot; 4) \u0026quot;concurrency in go\u0026quot; 5) \u0026quot;python\u0026quot; 6) \u0026quot;python cookbook\u0026quot; \u0026gt; hlen books (integer) 3 \u0026gt; hget books java \u0026quot;think in java\u0026quot; \u0026gt; hset books golang \u0026quot;learning go programming\u0026quot; # 因为是更新操作,所以返回 0 (integer) 0 \u0026gt; hget books golang \u0026quot;learning go programming\u0026quot; \u0026gt; hmset books java \u0026quot;effective java\u0026quot; python \u0026quot;learning python\u0026quot; golang \u0026quot;modern golang programming\u0026quot; # 批量 set OK 字典 Redis 的 Hash 是通过 dict 结构来实现的, 该结构的底层是由哈希表来实现.类似于 HashMap, 数组+链表, 超过负载因子所对应的阈值时,进行 rehash, 扩容. 在具体实现中,使用了渐进式hash的方式来避免 HashMap 这种阻塞式的 rehash, 将 rehash 的工作分摊到对字典的增删改查中.\nstruct typedef struct dictEntry { void *key; //键 union { void *val; //值 uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next; //指向下一节点,形成链表 } dictEntry; /* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table. */ typedef struct dictht { dictEntry **table; // 哈希表数组,数组的每一项都是 distEntry 的头结点 unsigned long size; // 哈希表的大小,也是触发扩容的阈值 unsigned long sizemask; // 哈希表大小掩码,用于计算索引值,总是等于 size-1 unsigned long used; // 哈希表中实际保存的节点数量 } dictht; typedef struct dict { dictType *type; //属性是一个指向 dictType 结构的指针,每个 dictType 结构保存了一簇用于操作特定类型键值对的函数,Redis 会为用途不同的字典设置不同的类型特定函数 void *privdata; // 保存了需要传给那些类型特定函数的可选参数 dictht ht[2]; // 在字典内部,维护了两张哈希表. 一般情况下,字典只使用 ht[0] 哈希表,ht[1] 哈希表只会在对 ht[0] 哈希表进行 rehash 时使用 long rehashidx; // 记录 rehash 的状态, 没有进行 rehash 则为 -1 unsigned long iterators; /* number of iterators currently running */ } dict; 一张图来表示 何时扩容? 找到dictAddRow 函数观察源码可以发现,会在 _dictExpandIfNeeded 函数中进行扩容的判断.\n/* Expand the hash table if needed */ static int _dictExpandIfNeeded(dict *d) { /* Incremental rehashing already in progress. Return. */ // 正在渐进式扩容, 就返回 OK if (dictIsRehashing(d)) return DICT_OK; /* If the hash table is empty expand it to the initial size. */ // 如果哈希表 ht[0] size 为 0 ,初始化, 说明 redis 是懒加载的,延长初始化策略 if (d-\u0026gt;ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); /* If we reached the 1:1 ratio, and we are allowed to resize the hash * table (global setting) or we should avoid it but the ratio between * elements/buckets is over the \u0026quot;safe\u0026quot; threshold, we resize doubling * the number of buckets. */ /* * 如果哈希表ht[0]中保存的key个数与哈希表大小的比例已经达到1:1,即保存的节点数已经大于哈希表大小 * 且redis服务当前允许执行rehash,或者保存的节点数与哈希表大小的比例超过了安全阈值(默认值为5) * 则将哈希表大小扩容为原来的两倍 */ if (d-\u0026gt;ht[0].used \u0026gt;= d-\u0026gt;ht[0].size \u0026amp;\u0026amp; (dict_can_resize || d-\u0026gt;ht[0].used/d-\u0026gt;ht[0].size \u0026gt; dict_force_resize_ratio)) { return dictExpand(d, d-\u0026gt;ht[0].used*2); } return DICT_OK; } 正常情况下,当 hash 表中元素的个数等于第一维数组的长度时,就会开始扩容,扩容的新数组是原数组大小的 2 倍。不过如果 Redis 正在做 bgsave,为了减少内存页的过多分离 (Copy On Write),Redis 尽量不去扩容 (dict_can_resize),但是如果 hash 表已经非常满了,元素的个数已经达到了第一维数组长度的 5 倍 (dict_force_resize_ratio),说明 hash 表已经过于拥挤了,这个时候就会强制扩容。\n何时缩容? 当哈希表的负载因子小于 0.1 时,自动缩容.这个操作会在 redis 的定时任务中来完成.函数为 databasesCron,该函数的作用是在后台慢慢的处理过期,rehashing, 缩容.\n执行条件: 没有子进程执行aof重写或者生成RDB文件\n/* 遍历所有的redis数据库,尝试缩容 */ for (j = 0; j \u0026lt; dbs_per_call; j++) { tryResizeHashTables(resize_db % server.dbnum); resize_db++; } /* If the percentage of used slots in the HT reaches HASHTABLE_MIN_FILL * we resize the hash table to save memory */ void tryResizeHashTables(int dbid) { if (htNeedsResize(server.db[dbid].dict)) dictResize(server.db[dbid].dict); if (htNeedsResize(server.db[dbid].expires)) dictResize(server.db[dbid].expires); } /* Hash table parameters */ #define HASHTABLE_MIN_FILL 10 /* Minimal hash table fill 10% */ int htNeedsResize(dict *dict) { long long size, used; size = dictSlots(dict); used = dictSize(dict); return (size \u0026gt; DICT_HT_INITIAL_SIZE \u0026amp;\u0026amp; (used*100/size \u0026lt; HASHTABLE_MIN_FILL)); } /* Resize the table to the minimal size that contains all the elements, * but with the invariant of a USED/BUCKETS ratio near to \u0026lt;= 1 */ int dictResize(dict *d) { int minimal; if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR; minimal = d-\u0026gt;ht[0].used; if (minimal \u0026lt; DICT_HT_INITIAL_SIZE) minimal = DICT_HT_INITIAL_SIZE; return dictExpand(d, minimal); } 从 htNeedsResize函数中可以看到,当哈希表保存的key数量与哈希表的大小的比例小于10%时需要缩容.最小容量为DICT_HT_INITIAL_SIZE = 4. dictResize 函数中,当正在执行 aof 重写或生成 rdb 时, dict_can_resize 会变为 0, 也就说明上面的 执行条件.\n渐进式 rehash 从上述源码中可以看出,所有的扩容或者创建都经过 dictExpand 函数.\n/* Expand or create the hash table */ int dictExpand(dict *d, unsigned long size) { /* the size is invalid if it is smaller than the number of * elements already inside the hash table */ if (dictIsRehashing(d) || d-\u0026gt;ht[0].used \u0026gt; size) return DICT_ERR; // 计算新的哈希表大小,获得大于等于size的第一个2次方 dictht n; /* the new hash table */ unsigned long realsize = _dictNextPower(size); /* Rehashing to the same table size is not useful. */ if (realsize == d-\u0026gt;ht[0].size) return DICT_ERR; /* Allocate the new hash table and initialize all pointers to NULL */ n.size = realsize; n.sizemask = realsize-1; n.table = zcalloc(realsize*sizeof(dictEntry*)); n.used = 0; /* Is this the first initialization? If so it's not really a rehashing * we just set the first hash table so that it can accept keys. */ // 第一次初始化也会通过这里来完成创建 if (d-\u0026gt;ht[0].table == NULL) { d-\u0026gt;ht[0] = n; return DICT_OK; } /* Prepare a second hash table for incremental rehashing */ // ht[1] 开始派上用场,扩容时是在 ht[1] 上操作, rehash 完毕后,在交换到 ht[0] d-\u0026gt;ht[1] = n; d-\u0026gt;rehashidx = 0; return DICT_OK; } 从 dictExpand 这个函数可以发现做了这么几件事:\n 校验是否可以执行 rehash 创建一个新的哈希表 n, 分配更大的内存 将哈希表 n 复制给 ht[1], 将 rehashidx 标志置为 0 ,意味着开启了渐进式rehash. 该值也标志渐进式rehash当前已经进行到了哪个hash槽. 该函数没有将key重新 rehash 到新的 slot 上,而是交由增删改查的操作, 以及后台定时任务来处理.\n增删改查辅助rehash 看源码其实可以发现在所有增删改查的源码中,开头都会有一个判断,是否处于渐进式rehash中.\ndictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing) { long index; dictEntry *entry; dictht *ht; if (dictIsRehashing(d)) _dictRehashStep(d); ... } // 进入 rehash 后是 \u0026gt;=0的值 #define dictIsRehashing(d) ((d)-\u0026gt;rehashidx != -1) /* * 此函数仅执行一步hash表的重散列,并且仅当没有安全迭代器绑定到哈希表时。 * 当我们在重新散列中有迭代器时,我们不能混淆打乱两个散列表的数据,否则某些元素可能被遗漏或重复遍历。 * * 该函数被在字典中查找或更新等普通操作调用,以致字典中的数据能自动的从哈系表1迁移到哈系表2 */ static void _dictRehashStep(dict *d) { if (d-\u0026gt;iterators == 0) dictRehash(d,1); } 后台任务rehash 虽然redis实现了在读写操作时,辅助服务器进行渐进式rehash操作,但是如果服务器比较空闲,redis数据库将很长时间内都一直使用两个哈希表.所以在redis周期函数中,如果发现有字典正在进行渐进式rehash操作,则会花费1毫秒的时间,帮助一起进行渐进式rehash操作.\n还是上面缩容时使用的任务函数databasesCron.源码如下:\n/* Rehash */ if (server.activerehashing) { for (j = 0; j \u0026lt; dbs_per_call; j++) { int work_done = incrementallyRehash(rehash_db); if (work_done) { /* If the function did some work, stop here, we'll do * more at the next cron loop. */ break; } else { /* If this db didn't need rehash, we'll try the next one. */ rehash_db++; rehash_db %= server.dbnum; } } } 渐进式rehash弊端 渐进式rehash避免了redis阻塞,可以说非常完美,但是由于在rehash时,需要分配一个新的hash表,在rehash期间,同时有两个hash表在使用,会使得redis内存使用量瞬间突增,在Redis 满容状态下由于Rehash会导致大量Key驱逐.\nZset 有序集合 首先 zset 是一个 set 结构,拥有 set 的所有特性,其次他可以给每一个 value 赋予一个 score 作为权重.内部实现用的跳表(skiplist)\n常用命令 ZADD,ZRANGE,ZREVRANGE,ZSCORE,ZCARD,ZRANK\u0026hellip;\n\u0026gt; zadd books 9.0 \u0026quot;think in java\u0026quot; (integer) 1 \u0026gt; zadd books 8.9 \u0026quot;java concurrency\u0026quot; (integer) 1 \u0026gt; zadd books 8.6 \u0026quot;java cookbook\u0026quot; (integer) 1 \u0026gt; zrange books 0 -1 # 按 score 排序列出,参数区间为排名范围 1) \u0026quot;java cookbook\u0026quot; 2) \u0026quot;java concurrency\u0026quot; 3) \u0026quot;think in java\u0026quot; \u0026gt; zrevrange books 0 -1 # 按 score 逆序列出,参数区间为排名范围 1) \u0026quot;think in java\u0026quot; 2) \u0026quot;java concurrency\u0026quot; 3) \u0026quot;java cookbook\u0026quot; \u0026gt; zcard books # 相当于 count() (integer) 3 \u0026gt; zscore books \u0026quot;java concurrency\u0026quot; # 获取指定 value 的 score \u0026quot;8.9000000000000004\u0026quot; # 内部 score 使用 double 类型进行存储,所以存在小数点精度问题 \u0026gt; zrank books \u0026quot;java concurrency\u0026quot; # 排名 (integer) 1 \u0026gt; zrangebyscore books 0 8.91 # 根据分值区间遍历 zset 1) \u0026quot;java cookbook\u0026quot; 2) \u0026quot;java concurrency\u0026quot; \u0026gt; zrangebyscore books -inf 8.91 withscores # 根据分值区间 (-∞, 8.91] 遍历 zset,同时返回分值。inf 代表 infinite,无穷大的意思。 1) \u0026quot;java cookbook\u0026quot; 2) \u0026quot;8.5999999999999996\u0026quot; 3) \u0026quot;java concurrency\u0026quot; 4) \u0026quot;8.9000000000000004\u0026quot; \u0026gt; zrem books \u0026quot;java concurrency\u0026quot; # 删除 value (integer) 1 \u0026gt; zrange books 0 -1 1) \u0026quot;java cookbook\u0026quot; 2) \u0026quot;think in java\u0026quot; 数据结构 众所周知, Zset 是一个有序的set集合, redis 通过 hash table 来存储 value 和 score 的映射关系,可以达到 O(1), 通过 score 排序或者说按照 score 范围来获取这个区间的 value, 则是通过 跳表 来实现的. Zset 可以达到 O(log(N)) 的插入和读写.\n什么是跳跃列表? 如图,跳跃列表是指具有纵向高度的有序链表.跳表会随机的某提升些链表的高度,并将每一层的节点进行连接,相当于构建多级索引,这样在查找的时候,从最高层开始查,可以过滤掉一大部分的范围,有点类似于二分查找.跳表也是典型的空间换时间的方式.\n每一个 kv 块对应的结构如下面的代码中的zslnode结构,kv header 也是这个结构,只不过 value 字段是 null 值——无效的,score 是 Double.MIN_VALUE,用来垫底的。\nstruct struct zslnode { string value; double score; zslnode*[] forwards; // 多层连接指针 zslnode* backward; // 回溯指针 } struct zsl { zslnode* header; // 跳跃列表头指针 int maxLevel; // 跳跃列表当前的最高层 map\u0026lt;string, zslnode*\u0026gt; ht; // hash 结构的所有键值对 } redis中跳表的优化 允许 score 是重复的 比较不仅是通过 key(即 score), 也还会比较 data 最底层(Level 1)是有反向指针的,所以是一个双向链表,这样适用于从大到小的排序需求(ZREVRANGE) 一次查找的过程 redis中level是如何生成的? /* Returns a random level for the new skiplist node we are going to create. * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL * (both inclusive), with a powerlaw-alike distribution where higher * levels are less likely to be returned. */ int zslRandomLevel(void) { int level = 1; while ((random()\u0026amp;0xFFFF) \u0026lt; (ZSKIPLIST_P * 0xFFFF)) level += 1; return (level\u0026lt;ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL; } ZSKIPLIST_MAXLEVEL 最大值是 64, 也就是最多 64 层.ZSKIPLIST_P 为 1/4, 也就是说有 25% 的概率有机会获得level,要获得更高的level,概率更小. 这也就导致了, redis中的跳表层级不会特别高,较扁平,较低层节点较多.有个小优化的地方: 跳表会记录下当前的最高层数 MaxLevel 这样就不需要从最顶层开始遍历了.\n为什么使用跳表而不是红黑树或者哈希表? skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。 在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。 从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。 查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当;而哈希表在保持较低的哈希值冲突概率的前提下,查找时间复杂度接近O(1),性能更高一些。所以我们平常使用的各种Map或dictionary结构,大都是基于哈希表实现的。 从算法实现难度上来比较,skiplist比平衡树要简单得多。 参考 渐进式 rehash 机制 美团针对Redis Rehash机制的探索和实践 zset内部实现 ","id":4,"section":"posts","summary":"\u003cp\u003e系统学习 redis 相关的知识,从数据结构开始~\u003c/p\u003e","tags":["redis","数据结构"],"title":"Redis - 数据结构","uri":"https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/","year":"2019"},{"content":"RabbitMQ在保证生产端与消费端的数据安全上,提供了消息确认的机制来保证. 消费端到 broker 端的确认常叫做ack机制, broker 到生产端常叫做confirm.\n消费端确认机制 Delivery Tag Delivery Tag 是 RabbitMQ 来确认消息如何发送的标志. Consumer 在注册到 RabbitMQ 上后, RabbitMQ 通过 basic.deliver 方法向消费者推送消息, 这个方法中就带着可以在 Channel中唯一识别消息的 delivery tag . Delivery Tag 是channel 隔离的.\ntag是一个大于零的增长的整型, 客户端在确认消息时将其当做参数传回来就可以保证是同一条消息的确认了.\ntag是channel隔离的, 所以必须在接受消息的channel上确认消息收到,否则会抛 unknown delivery tag的异常.\n最大值: delivery tag 是 64 位的long,最大值是 9223372036854775807. tag是channel隔离的,理论上来说是不会超过这个值的.\n确认机制 消息确认有两种模式: 自动/手动.\n自动模式会在消息一经发出就自动确认.这是在吞吐量和 可靠投递之间的权衡.如果在发送的过程中, TCP断掉了或是其他的问题,那消息就会丢掉了,这个问题需要考虑.还需要考虑的一个问题是: Consumer 消费速率如果不能跟上broker的发送速率, 会导致Consumer过载(消息堆积,内存耗尽),而在手动模式中可以通过prefetch来控制消费端的速率.有些客户端会提供TCP的背压,不能处理时,就丢弃了.\n手动模式需要Consumer端在收到消息后调用:\n basic.ack : 消息处理好了,可以丢掉了 basic.nack : 可以批量reject, 如果Consumer设置了requeue,消息还会重新回到broker的队列中 basic.reject : 消息没有处理但是也需要删除 Channel Prefetch 由于消息的发送和接收是独立的且完全异步,消息的手动确认也是完全异步的.所以这里有一个未确认消息的滑动窗口.在消费端我们经常需要控制接收消息的数量,防止出现消息缓存buffer越界的问题.此时我们就可以通过basic.qos来设置prefetch count, 该值定义了一个Channel中能存放的消息条数上限,超过这个值,RabbitMQ在收到至少一条ack之前都不能再往Channel上发送消息了.\n这里需要注意前面说的滑动窗口: 意味着当Channel满的时候,不会再往Channel上发消息,但是当你ack了一条,就会往Channel上发一条,ack了N条,就会发N条到Channel上.\nbasic.get设置prefetch是无效的,请使用basic.consume\n吞吐量影响因素: Ack机制 \u0026amp; Prefetch 确认机制的选择和Prefetch的值决定了消费端的吞吐量.一般来讲,增大Prefetch值以及 自动确认 会提升推送消息的速率,但也会增加待处理消息的堆积,消费端内存压力也会上升.\n如果Prefetch无界,Consumer在消费大量消息时没有ack会导致消费端连接的那个节点内存压力上升.所以找到一个完美的Prefetch值还是很重要的. 一般 100-300 左右吞吐量还不错,且消费端压力不大. 设置为 1 时,就很保守了,这种情况下吞吐量就很低,延迟较高.\n发布端确认机制 网络有很多种失败的方式,并且需要花时间检测.所以客户端并不能保证消息可以正常的发送到broker,正常的被处理.有可能丢了也有可能有延迟.\n根据AMQP-0-9-1, 只有通过 事务 的方式来保证.将Channel设置为事务型的,每条消息都以事务形式推送提交.但是,事务是很重,会降低吞吐量,所以RabbitMQ就换了种方式来实现: 通过模仿已有的Consumer端的确认机制.\n启用Confirm,客户端调用confirm.select即可.Broker会返回confirm.select-ok,取决于是否有no-wait设置. Channel如果设置了confirm.select,说明处于confirm模式,此时是不能设置为事务型Channel,两者不可互通.\nBroker的应答机制同Consumer一致,通过basic.ack即可,也可批量ack.\n发布端的NACK 在某些情况下,broker无法再接收消息,就会向发布端回执basic.nack,意味着消息会被丢弃,发布端需要重新发布这些消息.当Channel置为Confirm模式后,后面收到的消息都将会confirm或者nack 一次. 需要注意的几点:\n 不能保证消息何时confirm. 消息也不会同时confirm和nack 只有在Erlang进程内部报错时才会有nack Broker何时确认发布的消息? 无法路由的消息: 当确认消息不会被路由时, broker会立即发出confirm. 如果消息设置了强制(mandatory)发送,basic.return会在basic.ack之前回执. nack逻辑一致.\n可路由的消息: 所有queue接受了消息时返回basic.ack ,如果队列是持久化的,意味着持久化完成后才发出.对镜像队列(Mirrored Queues),意味着所有镜像都收到后发出.\n持久化消息的ack延迟 RabbitMQ的持久化通常是批量的,需要间隔几百毫秒来减少 fsync(2)的调用次数或者等待 queue 是空闲状态的时候,这意味着,每一次basic.ack的延迟可能达到几百毫秒.为了提高吞吐量最好是将持久化做成异步的,或者使用批量publish,这个需要参考客户端的api实现.\n确认消息的顺序 大多数情况下, RabbitMQ 会根据消息发送的顺序依次回执(要求消息发送在同一个channel上).但确认回执都是异步的,并且可以确认一条,或一组消息.确切的confirm发送时间取决于: 消息是否需要持久化,消息的路由方式.意味着不同的消息的确认时间是不同的.也就意味着返回确认的顺序并不一定相同.应用方不能将其作为一个依据.\n参考 https://www.rabbitmq.com/confirms.html#acknowledgement-modes ","id":5,"section":"posts","summary":"\u003cp\u003eRabbitMQ在保证生产端与消费端的数据安全上,提供了消息确认的机制来保证. 消费端到 \u003ccode\u003ebroker\u003c/code\u003e 端的确认常叫做\u003ccode\u003eack机制\u003c/code\u003e, \u003ccode\u003ebroker\u003c/code\u003e 到生产端常叫做\u003ccode\u003econfirm\u003c/code\u003e.\u003c/p\u003e","tags":["rabbitmq"],"title":"RabbitMQ-消息确认机制","uri":"https://xiaohei.im/hugo-theme-pure/2019/10/rabbitmq-ack-confirm/","year":"2019"},{"content":" 最近使用Hugo作为博客引擎后,闲不下来总想去找一些简单好看的主题.在官方的主题列表搜罗了一圈后,选择了yinyang,非常简单,但是用了一段时间还是想找个功能全点的,无意中瞄到了一个博主的博客,主题特别吸引我,但是是 hexo 平台的,搜了半天也没有人移植,就自己来吧~ 移植的过程中,遇到了挺多问题,也是这些问题慢慢的熟悉了hugo的模板结构.下面就来写一写自己遇到的问题~\n 页面变量参数 https://gohugo.io/variables/\n hugo的页面有基本的变量(我更愿意称为属性,根据这些属性来实现我们的主题模板.最主要的有三类:Site, Page, Taxonomy.\n.Site 站点相关的属性,即config.toml(yml)文件中的配置.\n 在页面模板中,我们可以使用{{- .Site.Autor }}这样的方式来获取你想要的站点属性.具体的站点属性可以查看https://gohugo.io/variables/site/. .Site 属于全局配置,在 作用域 得当的情况下是可以正常调用的.非正常情况我们下面再讲.\n常用属性 .Site.Pages : 获取所有文章(包含生成的一些分类页,比如说 标签页),按时间倒序排序.返回是一个数组.我们经常用来渲染一个列表.比如 归档 页面.\n .Site.Taxonomies : 获取所有的分类(这里的分类是广义上的),可以获取到按tag分类的集合,也可以获取到按category分类的集合,可以用这个属性来完成分类的页面.下面这段代码就代表着我可以拿到所有的 分类页 ,循环得到分类页的链接和标题.\n {{- range .Site.Taxonomies.categories }} \u0026lt;li\u0026gt;\u0026lt;a href=\u0026quot;{{ .Page.Permalink }}\u0026quot;\u0026gt;{{ .Page.Title }}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; {{- end }} .Site.Params 可以获取到我们在config.toml的Params标签下设置的内容.也是很重要的属性.比如说下面的例子.我可以设置日期的格式化样式,展示成你想要的类型. \u0026lt;p\u0026gt;{{ .Date.Format (.Site.Params.dateFormatToUse | default \u0026quot;2006-01-02\u0026quot;)}}\u0026lt;/p\u0026gt; .Page 页面中定义的属性.\n 页面属性可以大致分为两部分,一个是Hugo原生的属性,一个是每一篇文章的文件头,即front matter中的属性.具体的属性可以在https://gohugo.io/variables/page/查看. 在一个页面的作用域中使用时可以直接调用.比如我们想要知道页面的创建日期就可以直接 {{ .Date }} 即可.\n常用属性 .Date/.Title/.ReadingTime/.WordCount 见名知意 .Permalink/.RelPermalink 永久链接及相对连接 .Summary 摘要,默认70字 .Pages 为什么页面中还有一个这样的属性呢? Page是包含生成的分类页, 标签页的,所有当处于这些页面时会返回一个集合,若是我们自己真正写的文件,即markdown文件,会返回nil的. .Taxonomies 用作内容分类的管理. 我们经常在写文章时会写上 categories 或者 tags, 这些标签类目就是 .Taxonomies 的集中展示, Hugo 默认会有 categories 和 tags 两种分类. 你也可以自己再自定义设置. 具体参考: https://gohugo.io/content-management/taxonomies\n 使用案例 官方提供了多种 Template 实现常用的遍历.\n 我通常会用来写标签页(tags)和分类页(categories). 直接调用 .Taxonomies 会获得所有的分类项(即: tags, categories, 自定义分类项), .Taxonomies.tags 就可以获得所有的标签,以及标签下的所有文章.以下就是我的主题中 标签 页的实现逻辑.\n{{- $tags := .Site.Taxonomies.tags }} \u0026lt;main class=\u0026quot;main\u0026quot; role=\u0026quot;main\u0026quot;\u0026gt; \u0026lt;article class=\u0026quot;article article-tags post-type-list\u0026quot; itemscope=\u0026quot;\u0026quot;\u0026gt; \u0026lt;header class=\u0026quot;article-header\u0026quot;\u0026gt; \u0026lt;h1 itemprop=\u0026quot;name\u0026quot; class=\u0026quot;hidden-xs\u0026quot;\u0026gt;{{- .Title }}\u0026lt;/h1\u0026gt; \u0026lt;p class=\u0026quot;text-muted hidden-xs\u0026quot;\u0026gt;{{- T \u0026quot;total_tag\u0026quot; (len $tags) }}\u0026lt;/p\u0026gt; \u0026lt;nav role=\u0026quot;navigation\u0026quot; id=\u0026quot;nav-main\u0026quot; class=\u0026quot;okayNav\u0026quot;\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;\u0026lt;a href=\u0026quot;{{- \u0026quot;tags\u0026quot; | relURL }}\u0026quot;\u0026gt;{{- T \u0026quot;nav_all\u0026quot; }}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; {{- range $tags }} \u0026lt;li\u0026gt;\u0026lt;a href=\u0026quot;{{ .Page.Permalink }}\u0026quot;\u0026gt;{{ .Page.Title }}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; {{- end }} \u0026lt;/ul\u0026gt; \u0026lt;/nav\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;!-- /header --\u0026gt; \u0026lt;div class=\u0026quot;article-body\u0026quot;\u0026gt; {{- range $name, $taxonomy := $tags }} \u0026lt;h3 class=\u0026quot;panel-title mb-1x\u0026quot;\u0026gt; \u0026lt;a href=\u0026quot;{{ \u0026quot;/tags/\u0026quot; | relURL}}{{ $name | urlize }}\u0026quot; title=\u0026quot;#{{- $name }}\u0026quot;\u0026gt;# {{ $name }}\u0026lt;/a\u0026gt; \u0026lt;small class=\u0026quot;text-muted\u0026quot;\u0026gt;(Total {{- .Count }} articles)\u0026lt;/small\u0026gt; \u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;row\u0026quot;\u0026gt; {{- range $taxonomy }} \u0026lt;div class=\u0026quot;col-md-6\u0026quot;\u0026gt; {{ .Page.Scratch.Set \u0026quot;type\u0026quot; \u0026quot;card\u0026quot;}} {{- partial \u0026quot;item-post.html\u0026quot; . }} \u0026lt;/div\u0026gt; {{- end }} \u0026lt;/div\u0026gt; {{- end }} \u0026lt;/div\u0026gt; \u0026lt;/article\u0026gt; \u0026lt;/main\u0026gt; 上下文传递 刚开始写 Hugo 的页面时,最让我头疼的地方就在在于此.现在想想他的逻辑是很标准的.不同的代码块上下文隔离.\n 在Hugo中,上下文的传递一般是靠.符号来完成的. 用的最多的就是再组装页面时,需要将当前页面的作用域传递到 partial 的页面中去以便组装进来的页面可以获得当前页面的属性.\n以下是我的 baseof.html 页面, 可以看到 partial 相关的代码中都有 . 符号, 这里就是将当前页面的属性传递下去了, 其他页面也就可以正常使用该页面的属性了.\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026quot;{{ .Site.Language }}\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot; /\u0026gt; \u0026lt;meta http-equiv=\u0026quot;X-UA-Compatible\u0026quot; content=\u0026quot;IE=edge,chrome=1\u0026quot; /\u0026gt; \u0026lt;title\u0026gt; {{- block \u0026quot;title\u0026quot; . -}} {{ if .IsPage }} {{ .Title }} - {{ .Site.Title }} {{ else}} {{ .Site.Title}}{{ end }} {{- end -}} \u0026lt;/title\u0026gt; {{ partial \u0026quot;head.html\u0026quot; . }} \u0026lt;/head\u0026gt; \u0026lt;body class=\u0026quot;main-center\u0026quot; itemscope itemtype=\u0026quot;http://schema.org/WebPage\u0026quot;\u0026gt; {{- partial \u0026quot;header.html\u0026quot; .}} {{- if and (.Site.Params.sidebar) (or (ne .Params.sidebar \u0026quot;none\u0026quot;) (ne .Params.sidebar \u0026quot;custom\u0026quot;))}} {{- partial \u0026quot;sidebar.html\u0026quot; . }} {{end}} {{ block \u0026quot;content\u0026quot; . }}{{ end }} {{- partial \u0026quot;footer.html\u0026quot; . }} {{- partial \u0026quot;script.html\u0026quot; . }} \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 页面组织 baseof.html baseof 可以理解为一种模板,符合规范定义的页面都会按照 baseof.html 的框架完成最后的渲染,具体可以查看官网页, 以本次移植主题的 baseof.html 来说一下.\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026quot;{{ .Site.Language }}\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot; /\u0026gt; \u0026lt;meta http-equiv=\u0026quot;X-UA-Compatible\u0026quot; content=\u0026quot;IE=edge,chrome=1\u0026quot; /\u0026gt; \u0026lt;title\u0026gt; {{- block \u0026quot;title\u0026quot; . -}} {{ if .IsPage }} {{ .Title }} - {{ .Site.Title }} {{ else}} {{ .Site.Title}}{{ end }} {{- end -}} \u0026lt;/title\u0026gt; {{ partial \u0026quot;head.html\u0026quot; . }} \u0026lt;/head\u0026gt; \u0026lt;body class=\u0026quot;main-center\u0026quot; itemscope itemtype=\u0026quot;http://schema.org/WebPage\u0026quot;\u0026gt; {{- partial \u0026quot;header.html\u0026quot; .}} {{- if and (.Site.Params.sidebar) (or (ne .Params.sidebar \u0026quot;none\u0026quot;) (ne .Params.sidebar \u0026quot;custom\u0026quot;))}} {{- partial \u0026quot;sidebar.html\u0026quot; . }} {{end}} {{ block \u0026quot;content\u0026quot; . }}{{ end }} {{- partial \u0026quot;footer.html\u0026quot; . }} {{- partial \u0026quot;script.html\u0026quot; . }} \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 可以看到上面的页面中就是一个完整的 HTML 结构,我在其中组装了很多页面,比如head,header,footer等等,这些在最后渲染的时候都会加入进来组成一个完整的页面.\n在上面还有一个关键字 block, 比如 {{ block \u0026quot;title\u0026quot; }}, {{ block \u0026quot;content\u0026quot;}}.该关键字允许你自定义一个模板嵌进来, 只要按照规定的方式来.比如说我的文章页 single.html.\n{{- define \u0026quot;content\u0026quot;}} \u0026lt;main class=\u0026quot;main\u0026quot; role=\u0026quot;main\u0026quot;\u0026gt; {{- partial \u0026quot;article.html\u0026quot; . }} \u0026lt;/main\u0026gt; {{- end}} 这里我们定义了 content 的模板, 和 baseof.html 的模板呼应,在渲染一篇文章时,就会将single.html 嵌入 baseof.html 生成最后的页面了.\n模板页面查询规则 Hugo要怎么知道文章页还是标签页对应的模板是什么呢?答案: 有一套以多个属性作为依据的查询各类模板的标准.具体可以查看官网页.\n以文章页来举例, Hugo 官网上的内容页寻址规则如下:\n\n由上可见,会按照该顺序依次往下找,我一般会写在layouts/_default/single.html 下,这样可以在所有页面下通用.\n这里有个小坑也是之前文档没看好遇到的: 标签页和分类页这种对应的查找规则要按照该指引.\n参考 https://harmstyler.me/posts/2019/how-to-pass-variables-to-a-partial-template-in-hugo/ https://www.qikqiak.com/post/hugo-integrated-algolia-search/ ","id":6,"section":"posts","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近使用\u003ca href=\"https://gohugo.io/\"\u003eHugo\u003c/a\u003e作为博客引擎后,闲不下来总想去找一些简单好看的主题.在\u003ca href=\"https://themes.gohugo.io/\"\u003e官方的主题列表\u003c/a\u003e搜罗了一圈后,选择了\u003ca href=\"https://github.com/joway/hugo-theme-yinyang\"\u003eyinyang\u003c/a\u003e,非常简单,但是用了一段时间还是想找个功能全点的,无意中瞄到了一个博主的博客,主题特别吸引我,但是是 \u003ccode\u003ehexo\u003c/code\u003e 平台的,搜了半天也没有人移植,就自己来吧~ 移植的过程中,遇到了挺多问题,也是这些问题慢慢的熟悉了hugo的模板结构.下面就来写一写自己遇到的问题~\u003c/p\u003e\n\u003c/blockquote\u003e","tags":["hugo"],"title":"Hexo =\u003e Hugo主题移植记录","uri":"https://xiaohei.im/hugo-theme-pure/2019/09/hugo-theme-dev-note/","year":"2019"},{"content":"rabbitmq有多种使用模式,在这里记录下不同模式的消息路由规则\n预备知识 总结的不错的文章: https://blog.csdn.net/qq_27529917/article/details/79289564\n Binding Exchange 与 队列 之间的绑定为 Binding.绑定时可以设置 binding key, 发消息时会有一个 routing key, 当 routing key 与 binding key 相同时, 这条消息才能发送到队列中去.\nExchange Type Exchange 有不同的类型, 每种类型的功能也是不一致的\n Fanout 把所有发送到该 Exchange 的消息转发到所有绑定到他的队列中\n Direct/默认(empty string) 根据 routing_key 来决定发送到具体的队列去\n Topic binding key 可以带有匹配规则.\n Headers 不依赖 binding key 和 routing key, 只根据消息中的 headers 属性来匹配\n模式列表 参考: https://www.rabbitmq.com/getstarted.html\n 直连 上图展示了 Producer 与 Consumer 通过 Queue 直连, 实际上在 rabbitmq 中是不能直连的,必须通过 Exchange 指定 routingKey 才可以. 这里我们可以使用一个默认的 Exchange (空字符串) 来绕过限制.\n工作队列 直连 属于一对一的模式,工作队列 则属于一对多, 通常用于分发耗时任务给多个Consumer.可以提升响应效率.消息的分发策略是 轮询分发 .\n发布/订阅 发布订阅模型是 RabbitMQ 的核心模式. 我们大多数也是使用它来写业务.之前的 直连/工作队列 模式, 我们并没有用到 Exchange ,都是使用默认的空exchange.但是在 发布订阅 模式中, Producer 只会把消息发到 Exchange 中,不会关注是否会发送到队列, 由 Exchange 来决定.\n发布/订阅 中的 Exchange 类型为 Fanout, 所有发到 Exchange 上的消息都会再发到绑定在这个Exchange 上的所有队列中.\n路由模式 路由模式 采用 direct 类型的 Exchange 利用 binding key 来约束发送的队列.\nTopic Topic模式 利用模式匹配,以及 .的格式来按规则过滤. * 代表只有一个词, #代表 0 或 多个.\n","id":7,"section":"posts","summary":"\u003cp\u003erabbitmq有多种使用模式,在这里记录下不同模式的消息路由规则\u003c/p\u003e","tags":["rabbitmq"],"title":"RabbitMQ-消息分发机制","uri":"https://xiaohei.im/hugo-theme-pure/2019/09/rabbitmq-msg-distribution/","year":"2019"},{"content":" rabbitmq version: 3.7.15\n 常用操作 sbin/rabbitmq-server 启动 sbin/rabbitmq-server -detached 后台启动 sbin/rabbitmqctl shutdown/stop 关闭/停止server sbin/rabbitmqctl status 检查server状态 sbin/rabbitmq-plugins enable rabbitmq_management 开启控制台 端口 server启动后默认监听5672 控制台默认监听15672 构建集群 构建集群的方式 在config文件中声明节点信息 使用DNS发现 使用AWS实例发现(通过插件) 使用kubernetes发现(通过插件) 使用consul发现(通过插件) 使用etcd发现(通过插件) 手动执行rabbitmqctl 节点名称 节点名称是节点的身份识别证明.两部分组成: prefix \u0026amp; hostname.例如 rabbit@node1.messaging.svc.local的prefix是 rabbit ,hostname是 node1.messaging.svc.local.\n 集群中名称必须 唯一. 如果使用同一个hostname 那么prefix要保持不一致\n 集群中,节点通过节点名称互相进行识别和通信.所以hostname必须能解析.CLI Tools也要使用节点名称.\n 单机集群构建 单机运行多节点需要保证:\n 不同节点名称 \u0010RABBITMQ_NODENAME 不同存储路径 RABBITMQ_DIST_PORT 不同日志路径 RABBITMQ_LOG_BASE 不同端口,包括插件使用的 RABBITMQ_NODE_PORT rabbitmqctl 构建集群 RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit rabbitmq-server -detached RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=hare rabbitmq-server -detached # 重置正在运行的节点 rabbitmqctl -n hare stop_app # 加入集群 rabbitmqctl -n hare join_cluster rabbit@`hostname -s` rabbitmqctl -n hare start_app 每个节点若配置有其他的插件.那么每个节点插件监听的端口不能冲突,例如添加控制台\n# 首先开启控制台插件 ./rabbitmq-plugins enable rabbitmq_management RABBITMQ_NODE_PORT=5672 RABBITMQ_SERVER_START_ARGS=\u0026quot;-rabbitmq_management listener [{port,15672}]\u0026quot; RABBITMQ_NODENAME=rabbit ./rabbitmq-server -detached RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS=\u0026quot;-rabbitmq_management listener [{port,15673}]\u0026quot; RABBITMQ_NODENAME=hare ./rabbitmq-server -detached # 加入rabbit节点生成集群 rabbitmqctl -n hare stop_app # 加入集群 rabbitmqctl -n hare join_cluster rabbit@`hostname -s` rabbitmqctl -n hare start_app 以上就建了带控制台的两个节点.\n遇到的问题 添加节点进集群时,报错 ./rabbitmqctl -n rabbit2 join_cluster rabbit@`hostname -s` Clustering node rabbit2@localhost with rabbit@localhost Error: {:inconsistent_cluster, 'Node rabbit@localhost thinks it\\'s clustered with node rabbit2@localhost, but rabbit2@localhost disagrees'} 集群残留的cluster信息导致认证失败.删除${RABBIT_MQ_HOME}/var/lib/rabbitmq/mnesia文件夹.再reset节点\n建集群报错 Clustering node rabbit2@localhost with rabbit@localhost Error: {:corrupt_or_missing_cluster_files, {:error, :enoent}, {:error, :enoent}} 同上\n启动第三个节点时爆端口占用,该端口是第一个节点的控制台端口15672.没有解决 2019-09-05 15:35:42.749 [error] \u0026lt;0.555.0\u0026gt; Failed to start Ranch listener rabbit_web_dispatch_sup_15672 in ranch_tcp:listen([{cacerts,'...'},{key,'...'},{cert,'...'},{port,15672}]) for reason eaddrinuse (address already in use) 使用案例 Topic Exchange topic类型的exchange ,routing key 是按一定规则来的,通过.连接,类似于正则.有两种符号:\n * 代表一个单词 # 代表0或多个单词 如果 单单只有#号, 那么topic exchange就像fanout exchange,如果没有使用*和#,那就是direct exchange了.\n参考 docker hub rabbit mq 镜像 ","id":8,"section":"posts","summary":"","tags":["rabbitmq"],"title":"RabbitMQ-入门及高可用集群部署","uri":"https://xiaohei.im/hugo-theme-pure/2019/09/rabbitmq-guide-and-ha-cluster/","year":"2019"},{"content":" 转载自 https://rabbitmq.mr-ping.com/AMQP/AMQP_0-9-1_Model_Explained.html\n AMQP 0-9-1 和 AMQP 模型高阶概述 AMQP是什么 AMQP(高级消息队列协议)是一个网络协议。它支持符合要求的客户端应用(application)和消息中间件代理(messaging middleware broker)之间进行通信。\n消息代理和他们所扮演的角色 消息代理(message brokers)从发布者(publishers)亦称生产者(producers)那儿接收消息,并根据既定的路由规则把接收到的消息发送给处理消息的消费者(consumers)。\n由于AMQP是一个网络协议,所以这个过程中的发布者,消费者,消息代理 可以存在于不同的设备上。\nAMQP 0-9-1 模型简介 AMQP 0-9-1的工作过程如下图:消息(message)被发布者(publisher)发送给交换机(exchange),交换机常常被比喻成邮局或者邮箱。然后交换机将收到的消息根据路由规则分发给绑定的队列(queue)。最后AMQP代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取。\n\n发布者(publisher)发布消息时可以给消息指定各种消息属性(message meta-data)。有些属性有可能会被消息代理(brokers)使用,然而其他的属性则是完全不透明的,它们只能被接收消息的应用所使用。\n从安全角度考虑,网络是不可靠的,接收消息的应用也有可能在处理消息的时候失败。基于此原因,AMQP模块包含了一个消息确认(message acknowledgements)的概念:当一个消息从队列中投递给消费者后(consumer),消费者会通知一下消息代理(broker),这个可以是自动的也可以由处理消息的应用的开发者执行。当“消息确认”被启用的时候,消息代理不会完全将消息从队列中删除,直到它收到来自消费者的确认回执(acknowledgement)。\n在某些情况下,例如当一个消息无法被成功路由时,消息或许会被返回给发布者并被丢弃。或者,如果消息代理执行了延期操作,消息会被放入一个所谓的死信队列中。此时,消息发布者可以选择某些参数来处理这些特殊情况。\n队列,交换机和绑定统称为AMQP实体(AMQP entities)。\nAMQP是一个可编程的协议 AMQP 0-9-1是一个可编程协议,某种意义上说AMQP的实体和路由规则是由应用本身定义的,而不是由消息代理定义。包括像声明队列和交换机,定义他们之间的绑定,订阅队列等等关于协议本身的操作。\n这虽然能让开发人员自由发挥,但也需要他们注意潜在的定义冲突。当然这在实践中很少会发生,如果发生,会以配置错误(misconfiguration)的形式表现出来。\n应用程序(Applications)声明AMQP实体,定义需要的路由方案,或者删除不再需要的AMQP实体。\n交换机和交换机类型 交换机是用来发送消息的AMQP实体。交换机拿到一个消息之后将它路由给一个或零个队列。它使用哪种路由算法是由交换机类型和被称作绑定(bindings)的规则所决定的。AMQP 0-9-1的代理提供了四种交换机\n Name(交换机类型) Default pre-declared names(预声明的默认名称) Direct exchange(直连交换机) (Empty string) and amq.direct Fanout exchange(扇型交换机) amq.fanout Topic exchange(主题交换机) amq.topic Headers exchange(头交换机) amq.match (and amq.headers in RabbitMQ) 除交换机类型外,在声明交换机时还可以附带许多其他的属性,其中最重要的几个分别是:\n Name Durability (消息代理重启后,交换机是否还存在) Auto-delete (当所有与之绑定的消息队列都完成了对此交换机的使用后,删掉它) Arguments(依赖代理本身) 交换机可以有两个状态:持久(durable)、暂存(transient)。持久化的交换机会在消息代理(broker)重启后依旧存在,而暂存的交换机则不会(它们需要在代理再次上线后重新被声明)。然而并不是所有的应用场景都需要持久化的交换机。\n默认交换机 默认交换机(default exchange)实际上是一个由消息代理预先声明好的没有名字(名字为空字符串)的直连交换机(direct exchange)。它有一个特殊的属性使得它对于简单应用特别有用处:那就是每个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(routing key)名称与队列名称相同。\n举个栗子:当你声明了一个名为\u0026quot;search-indexing-online\u0026quot;的队列,AMQP代理会自动将其绑定到默认交换机上,绑定(binding)的路由键名称也是为\u0026quot;search-indexing-online\u0026quot;。因此,当携带着名为\u0026quot;search-indexing-online\u0026quot;的路由键的消息被发送到默认交换机的时候,此消息会被默认交换机路由至名为\u0026quot;search-indexing-online\u0026quot;的队列中。换句话说,默认交换机看起来貌似能够直接将消息投递给队列,尽管技术上并没有做相关的操作。\n直连交换机 直连型交换机(direct exchange)是根据消息携带的路由键(routing key)将消息投递给对应队列的。直连交换机用来处理消息的单播路由(unicast routing)(尽管它也可以处理多播路由)。下边介绍它是如何工作的:\n 将一个队列绑定到某个交换机上,同时赋予该绑定一个路由键(routing key) 当一个携带着路由键为R的消息被发送给直连交换机时,交换机会把它路由给绑定值同样为R的队列。 直连交换机经常用来循环分发任务给多个工作者(workers)。当这样做的时候,我们需要明白一点,在AMQP 0-9-1中,消息的负载均衡是发生在消费者(consumer)之间的,而不是队列(queue)之间。\n直连型交换机图例: \n扇型交换机 扇型交换机(funout exchange)将消息路由给绑定到它身上的所有队列,而不理会绑定的路由键。如果N个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的N个队列。扇型用来交换机处理消息的广播路由(broadcast routing)。\n因为扇型交换机投递消息的拷贝到所有绑定到它的队列,所以他的应用案例都极其相似:\n 大规模多用户在线(MMO)游戏可以使用它来处理排行榜更新等全局事件 体育新闻网站可以用它来近乎实时地将比分更新分发给移动客户端 分发系统使用它来广播各种状态和配置更新 在群聊的时候,它被用来分发消息给参与群聊的用户。(AMQP没有内置presence的概念,因此XMPP可能会是个更好的选择) 扇型交换机图例: \n主题交换机 主题交换机(topic exchanges)通过对消息的路由键和队列到交换机的绑定模式之间的匹配,将消息路由给一个或多个队列。主题交换机经常用来实现各种分发/订阅模式及其变种。主题交换机通常用来实现消息的多播路由(multicast routing)。\n主题交换机拥有非常广泛的用户案例。无论何时,当一个问题涉及到那些想要有针对性的选择需要接收消息的 多消费者/多应用(multiple consumers/applications) 的时候,主题交换机都可以被列入考虑范围。\n使用案例:\n 分发有关于特定地理位置的数据,例如销售点 由多个工作者(workers)完成的后台任务,每个工作者负责处理某些特定的任务 股票价格更新(以及其他类型的金融数据更新) 涉及到分类或者标签的新闻更新(例如,针对特定的运动项目或者队伍) 云端的不同种类服务的协调 分布式架构/基于系统的软件封装,其中每个构建者仅能处理一个特定的架构或者系统。 头交换机 有时消息的路由操作会涉及到多个属性,此时使用消息头就比用路由键更容易表达,头交换机(headers exchange)就是为此而生的。头交换机使用多个消息属性来代替路由键建立路由规则。通过判断消息头的值能否与指定的绑定相匹配来确立路由规则。\n我们可以绑定一个队列到头交换机上,并给他们之间的绑定使用多个用于匹配的头(header)。这个案例中,消息代理得从应用开发者那儿取到更多一段信息,换句话说,它需要考虑某条消息(message)是需要部分匹配还是全部匹配。上边说的“更多一段消息”就是\u0026quot;x-match\u0026quot;参数。当\u0026quot;x-match\u0026quot;设置为“any”时,消息头的任意一个值被匹配就可以满足条件,而当\u0026quot;x-match\u0026quot;设置为“all”的时候,就需要消息头的所有值都匹配成功。\n头交换机可以视为直连交换机的另一种表现形式。头交换机能够像直连交换机一样工作,不同之处在于头交换机的路由规则是建立在头属性值之上,而不是路由键。路由键必须是一个字符串,而头属性值则没有这个约束,它们甚至可以是整数或者哈希值(字典)等。\n队列 AMQP中的队列(queue)跟其他消息队列或任务队列中的队列是很相似的:它们存储着即将被应用消费掉的消息。队列跟交换机共享某些属性,但是队列也有一些另外的属性。\n Name Durable(消息代理重启后,队列依旧存在) Exclusive(只被一个连接(connection)使用,而且当连接关闭后队列即被删除) Auto-delete(当最后一个消费者退订后即被删除) Arguments(一些消息代理用他来完成类似与TTL的某些额外功能) 队列在声明(declare)后才能被使用。如果一个队列尚不存在,声明一个队列会创建它。如果声明的队列已经存在,并且属性完全相同,那么此次声明不会对原有队列产生任何影响。如果声明中的属性与已存在队列的属性有差异,那么一个错误代码为406的通道级异常就会被抛出。\n队列名称 队列的名字可以由应用(application)来取,也可以让消息代理(broker)直接生成一个。队列的名字可以是最多255字节的一个utf-8字符串。若希望AMQP消息代理生成队列名,需要给队列的name参数赋值一个空字符串:在同一个通道(channel)的后续的方法(method)中,我们可以使用空字符串来表示之前生成的队列名称。之所以之后的方法可以获取正确的队列名是因为通道可以默默地记住消息代理最后一次生成的队列名称。\n以\u0026quot;amq.\u0026quot;开始的队列名称被预留做消息代理内部使用。如果试图在队列声明时打破这一规则的话,一个通道级的403 (ACCESS_REFUSED)错误会被抛出。\n队列持久化 持久化队列(Durable queues)会被存储在磁盘上,当消息代理(broker)重启的时候,它依旧存在。没有被持久化的队列称作暂存队列(Transient queues)。并不是所有的场景和案例都需要将队列持久化。\n持久化的队列并不会使得路由到它的消息也具有持久性。倘若消息代理挂掉了,重新启动,那么在重启的过程中持久化队列会被重新声明,无论怎样,只有经过持久化的消息才能被重新恢复。\n绑定 绑定(Binding)是交换机(exchange)将消息(message)路由给队列(queue)所需遵循的规则。如果要指示交换机“E”将消息路由给队列“Q”,那么“Q”就需要与“E”进行绑定。绑定操作需要定义一个可选的路由键(routing key)属性给某些类型的交换机。路由键的意义在于从发送给交换机的众多消息中选择出某些消息,将其路由给绑定的队列。\n打个比方:\n 队列(queue)是我们想要去的位于纽约的目的地 交换机(exchange)是JFK机场 绑定(binding)就是JFK机场到目的地的路线。能够到达目的地的路线可以是一条或者多条 拥有了交换机这个中间层,很多由发布者直接到队列难以实现的路由方案能够得以实现,并且避免了应用开发者的许多重复劳动。\n如果AMQP的消息无法路由到队列(例如,发送到的交换机没有绑定队列),消息会被就地销毁或者返还给发布者。如何处理取决于发布者设置的消息属性。\n消费者 消息如果只是存储在队列里是没有任何用处的。被应用消费掉,消息的价值才能够体现。在AMQP 0-9-1 模型中,有两种途径可以达到此目的:\n 将消息投递给应用 (\u0026quot;push API\u0026quot;) 应用根据需要主动获取消息 (\u0026quot;pull API\u0026quot;) 使用push API,应用(application)需要明确表示出它在某个特定队列里所感兴趣的,想要消费的消息。如是,我们可以说应用注册了一个消费者,或者说订阅了一个队列。一个队列可以注册多个消费者,也可以注册一个独享的消费者(当独享消费者存在时,其他消费者即被排除在外)。\n每个消费者(订阅者)都有一个叫做消费者标签的标识符。它可以被用来退订消息。消费者标签实际上是一个字符串。\n消息确认 消费者应用(Consumer applications) - 用来接受和处理消息的应用 - 在处理消息的时候偶尔会失败或者有时会直接崩溃掉。而且网络原因也有可能引起各种问题。这就给我们出了个难题,AMQP代理在什么时候删除消息才是正确的?AMQP 0-9-1 规范给我们两种建议:\n 当消息代理(broker)将消息发送给应用后立即删除。(使用AMQP方法:basic.deliver或basic.get-ok) 待应用(application)发送一个确认回执(acknowledgement)后再删除消息。(使用AMQP方法:basic.ack) 前者被称作自动确认模式(automatic acknowledgement model),后者被称作显式确认模式(explicit acknowledgement model)。在显式模式下,由消费者应用来选择什么时候发送确认回执(acknowledgement)。应用可以在收到消息后立即发送,或将未处理的消息存储后发送,或等到消息被处理完毕后再发送确认回执(例如,成功获取一个网页内容并将其存储之后)。\n如果一个消费者在尚未发送确认回执的情况下挂掉了,那AMQP代理会将消息重新投递给另一个消费者。如果当时没有可用的消费者了,消息代理会死等下一个注册到此队列的消费者,然后再次尝试投递。\n拒绝消息 当一个消费者接收到某条消息后,处理过程有可能成功,有可能失败。应用可以向消息代理表明,本条消息由于“拒绝消息(Rejecting Messages)”的原因处理失败了(或者未能在此时完成)。当拒绝某条消息时,应用可以告诉消息代理如何处理这条消息——销毁它或者重新放入队列。当此队列只有一个消费者时,请确认不要由于拒绝消息并且选择了重新放入队列的行为而引起消息在同一个消费者身上无限循环的情况发生。\nNegative Acknowledgements 在AMQP中,basic.reject方法用来执行拒绝消息的操作。但basic.reject有个限制:你不能使用它决绝多个带有确认回执(acknowledgements)的消息。但是如果你使用的是RabbitMQ,那么你可以使用被称作negative acknowledgements(也叫nacks)的AMQP 0-9-1扩展来解决这个问题。更多的信息请参考帮助页面\n预取消息 在多个消费者共享一个队列的案例中,明确指定在收到下一个确认回执前每个消费者一次可以接受多少条消息是非常有用的。这可以在试图批量发布消息的时候起到简单的负载均衡和提高消息吞吐量的作用。For example, if a producing application sends messages every minute because of the nature of the work it is doing.(???例如,如果生产应用每分钟才发送一条消息,这说明处理工作尚在运行。)\n注意,RabbitMQ只支持通道级的预取计数,而不是连接级的或者基于大小的预取。\n消息属性和有效载荷(消息主体) AMQP模型中的消息(Message)对象是带有属性(Attributes)的。有些属性及其常见,以至于AMQP 0-9-1 明确的定义了它们,并且应用开发者们无需费心思思考这些属性名字所代表的具体含义。例如:\n Content type(内容类型) Content encoding(内容编码) Routing key(路由键) Delivery mode (persistent or not) 投递模式(持久化 或 非持久化) Message priority(消息优先权) Message publishing timestamp(消息发布的时间戳) Expiration period(消息有效期) Publisher application id(发布应用的ID) 有些属性是被AMQP代理所使用的,但是大多数是开放给接收它们的应用解释器用的。有些属性是可选的也被称作消息头(headers)。他们跟HTTP协议的X-Headers很相似。消息属性需要在消息被发布的时候定义。\nAMQP的消息除属性外,也含有一个有效载荷 - Payload(消息实际携带的数据),它被AMQP代理当作不透明的字节数组来对待。消息代理不会检查或者修改有效载荷。消息可以只包含属性而不携带有效载荷。它通常会使用类似JSON这种序列化的格式数据,为了节省,协议缓冲器和MessagePack将结构化数据序列化,以便以消息的有效载荷的形式发布。AMQP及其同行者们通常使用\u0026quot;content-type\u0026quot; 和 \u0026quot;content-encoding\u0026quot; 这两个字段来与消息沟通进行有效载荷的辨识工作,但这仅仅是基于约定而已。\n消息能够以持久化的方式发布,AMQP代理会将此消息存储在磁盘上。如果服务器重启,系统会确认收到的持久化消息未丢失。简单地将消息发送给一个持久化的交换机或者路由给一个持久化的队列,并不会使得此消息具有持久化性质:它完全取决与消息本身的持久模式(persistence mode)。将消息以持久化方式发布时,会对性能造成一定的影响(就像数据库操作一样,健壮性的存在必定造成一些性能牺牲)。\n消息确认 由于网络的不确定性和应用失败的可能性,处理确认回执(acknowledgement)就变的十分重要。有时我们确认消费者收到消息就可以了,有时确认回执意味着消息已被验证并且处理完毕,例如对某些数据已经验证完毕并且进行了数据存储或者索引操作。\n这种情形很常见,所以 AMQP 0-9-1 内置了一个功能叫做 消息确认(message acknowledgements),消费者用它来确认消息已经被接收或者处理。如果一个应用崩溃掉(此时连接会断掉,所以AMQP代理亦会得知),而且消息的确认回执功能已经被开启,但是消息代理尚未获得确认回执,那么消息会被从新放入队列(并且在还有还有其他消费者存在于此队列的前提下,立即投递给另外一个消费者)。\n协议内置的消息确认功能将帮助开发者建立强大的软件。\nAMQP 0-9-1 方法 AMQP 0-9-1由许多方法(methods)构成。方法即是操作,这跟面向对象编程中的方法没半毛钱关系。AMQP的方法被分组在类(class)中。这里的类仅仅是对AMQP方法的逻辑分组而已。在 AMQP 0-9-1参考中有对AMQP方法的详细介绍。\n让我们来看看交换机类,有一组方法被关联到了交换机的操作上。这些方法如下所示:\n exchange.declare exchange.declare-ok exchange.delete exchange.delete-ok (请注意,RabbitMQ网站参考中包含了特用于RabbitMQ的交换机类的扩展,这里我们不对其进行讨论)\n以上的操作来自逻辑上的配对:exchange.declare 和 exchange.declare-ok,exchange.delete 和 exchange.delete-ok. 这些操作分为“请求 - requests”(由客户端发送)和“响应 - responses”(由代理发送,用来回应之前提到的“请求”操作)。\n如下的例子:客户端要求消息代理使用exchange.declare方法声明一个新的交换机: \n如上图所示,exchange.declare方法携带了好几个参数。这些参数可以允许客户端指定交换机名称、类型、是否持久化等等。\n操作成功后,消息代理使用exchange.declare-ok方法进行回应: \nexchange.declare-ok方法除了通道号之外没有携带任何其他参数(通道-channel 会在本指南稍后章节进行介绍)。\nAMQP队列类的配对方法 - queue.declare方法 和 queue.declare-ok有着与其他配对方法非常相似的一系列事件: \n\n不是所有的AMQP方法都有与其配对的“另一半”。许多(basic.publish是最被广泛使用的)都没有相对应的“响应”方法,另外一些(如basic.get)有着一种以上与之对应的“响应”方法。\n连接 AMQP连接通常是长连接。AMQP是一个使用TCP提供可靠投递的应用层协议。AMQP使用认证机制并且提供TLS(SSL)保护。当一个应用不再需要连接到AMQP代理的时候,需要优雅的释放掉AMQP连接,而不是直接将TCP连接关闭。\n通道 有些应用需要与AMQP代理建立多个连接。无论怎样,同时开启多个TCP连接都是不合适的,因为这样做会消耗掉过多的系统资源并且使得防火墙的配置更加困难。AMQP 0-9-1提供了通道(channels)来处理多连接,可以把通道理解成共享一个TCP连接的多个轻量化连接。\n在涉及多线程/进程的应用中,为每个线程/进程开启一个通道(channel)是很常见的,并且这些通道不能被线程/进程共享。\n一个特定通道上的通讯与其他通道上的通讯是完全隔离的,因此每个AMQP方法都需要携带一个通道号,这样客户端就可以指定此方法是为哪个通道准备的。\n虚拟主机 为了在一个单独的代理上实现多个隔离的环境(用户、用户组、交换机、队列 等),AMQP提供了一个虚拟主机(virtual hosts - vhosts)的概念。这跟Web servers虚拟主机概念非常相似,这为AMQP实体提供了完全隔离的环境。当连接被建立的时候,AMQP客户端来指定使用哪个虚拟主机。\nAMQP是可扩展的 AMQP 0-9-1 拥有多个扩展点:\n 定制化交换机类型 可以让开发者们实现一些开箱即用的交换机类型尚未很好覆盖的路由方案。例如 geodata-based routing。 交换机和队列的声明中可以包含一些消息代理能够用到的额外属性。例如RabbitMQ中的per-queue message TTL即是使用该方式实现。 特定消息代理的协议扩展。例如RabbitMQ所实现的扩展。 新的 AMQP 0-9-1 方法类可被引入。 消息代理可以被其他的插件扩展,例如RabbitMQ的管理前端 和 已经被插件化的HTTP API。 这些特性使得AMQP 0-9-1模型更加灵活,并且能够适用于解决更加宽泛的问题。\nAMQP 0-9-1 客户端生态系统 AMQP 0-9-1 拥有众多的适用于各种流行语言和框架的客户端。其中一部分严格遵循AMQP规范,提供AMQP方法的实现。另一部分提供了额外的技术,方便使用的方法和抽象。有些客户端是异步的(非阻塞的),有些是同步的(阻塞的),有些将这两者同时实现。有些客户端支持“供应商的特定扩展”(例如RabbitMQ的特定扩展)。\n因为AMQP的主要目标之一就是实现交互性,所以对于开发者来讲,了解协议的操作方法而不是只停留在弄懂特定客户端的库就显得十分重要。这样一来,开发者使用不同类型的库与协议进行沟通时就会容易的多。\n","id":9,"section":"posts","summary":"","tags":["rabbitmq"],"title":"AMQP消息模型","uri":"https://xiaohei.im/hugo-theme-pure/2019/09/amqp-0-9-1-model-explained/","year":"2019"},{"content":"前言 Hystrix已经不在维护了,但是成功的开源项目总是值得学习的.刚开始看 Hystrix 源码时,会发现一堆 Action,Function 的逻辑,这其实就是 RxJava 的特点了\u0026ndash;响应式编程.上篇文章已经对RxJava作过入门介绍,不熟悉的同学可以先去看看.本文会简单介绍 Hystrix,再根据demo结合源码来了解Hystrix的执行流程.\nHystrix简单介绍 什么是 Hystrix?\nHystrix 是一个延迟和容错库,旨在隔离对远程系统、服务和第三方库的访问点,停止级联故障,并在错误不可避免的复杂分布式系统中能够弹性恢复。\n 核心概念\n Command 命令\nCommand 是Hystrix的入口,对用户来说,我们只需要创建对应的 command,将需要保护的接口包装起来就可以.可以无需关注再之后的逻辑.与 Spring 深度集成后还可以通过注解的方式,就更加对开发友好了.\n Circuit Breaker 断路器\n断路器,是从电气领域引申过来的概念,具有过载、短路和欠电压保护功能,有保护线路和电源的能力.在Hystrix中即为当请求超过一定比例响应失败时,hystrix 会对请求进行拦截处理,保证服务的稳定性,以及防止出现服务之间级联雪崩的可能性.\n Isolation 隔离策略\n隔离策略是 Hystrix 的设计亮点所在,利用舱壁模式的思想来对访问的资源进行隔离,每个资源是独立的依赖,单个资源的异常不应该影响到其他. Hystrix 的隔离策略目前有两种:线程池隔离,信号量隔离.\n Hystrix的运行流程\n 官方的 How it Works 对流程有很详细的介绍,图示清晰,相信看完流程图就能对运行流程有一定的了解.\n 一次Command执行 HystrixCommand是标准的命令模式实现,每一次请求即为一次命令的创建执行经历的过程.从上述Hystrix流程图可以看出创建流程最终会指向toObservable,在之前RxJava入门时有介绍到Observable即为被观察者,作用是发送数据给观察者进行相应的,因此可以知道这个方法应该是较为关键的.\nUML HystrixInvokable 标记这个一个可执行的接口,没有任何抽象方法或常量 HystrixExecutable 是为HystrixCommand设计的接口,主要提供执行命令的抽象方法,例如:execute(),queue(),observe() HystrixObservable 是为Observable设计的接口,主要提供自动订阅(observe())和生成Observable(toObservable())的抽象方法 HystrixInvokableInfo 提供大量的状态查询(获取属性配置,是否开启断路器等) AbstractCommand 核心逻辑的实现 HystrixCommand 定制逻辑实现以及留给用户实现的接口(比如:run()) 样例代码 通过新建一个 command 来看 Hystrix 是如何创建并执行的.HystrixCommand 是一个抽象类,其中有一个run方法需要我们实现自己的业务逻辑,以下是偷懒采用匿名内部类的形式呈现.构造方法的内部实现我们就不关注了,直接看下执行的逻辑吧.\nHystrixCommand demo = new HystrixCommand\u0026lt;String\u0026gt;(HystrixCommandGroupKey.Factory.asKey(\u0026quot;demo-group\u0026quot;)) { @Override protected String run() { return \u0026quot;Hello World~\u0026quot;; } }; demo.execute(); 执行过程 流程图 这是官方给出的一次完整调用的链路.上述的 demo 中我们直接调用了execute方法,所以调用的路径为execute() -\u0026gt; queue() -\u0026gt; toObservable() -\u0026gt; toBlocking() -\u0026gt; toFuture() -\u0026gt; get().核心的逻辑其实就在toObservable()中.\nHystrixCommand.java execute execute方法为同步调用返回结果,并对异常作处理.内部会调用queue\n// 同步调用执行 public R execute() { try { // queue()返回的是Future类型的对象,所以这里是阻塞get return queue().get(); } catch (Exception e) { throw decomposeException(e); } } queue queue的第一行代码完成了核心的订阅逻辑.\n toObservable() 生成了 Hystrix 的 Observable 对象 将 Observable 转换为 BlockingObservable 可以阻塞控制数据发送 toFuture 实现对 BlockingObservable 的订阅\npublic Future\u0026lt;R\u0026gt; queue() { // 着重关注的是这行代码 // 完成了Observable的创建及订阅 // toBlocking()是将Observable转为BlockingObservable,转换后的Observable可以阻塞数据的发送 final Future\u0026lt;R\u0026gt; delegate = toObservable().toBlocking().toFuture(); final Future\u0026lt;R\u0026gt; f = new Future\u0026lt;R\u0026gt;() { // 由于toObservable().toBlocking().toFuture()返回的Future如果中断了, // 不会对当前线程进行中断,所以这里将返回的Future进行了再次包装,处理异常逻辑 ... } // 判断是否已经结束了,有异常则直接抛出 if (f.isDone()) { try { f.get(); return f; } catch (Exception e) { // 省略这段判断 } } return f; } BlockingObservable.java // 被包装的Observable private final Observable\u0026lt;? extends T\u0026gt; o; // toBlocking()会调用该静态方法将 源Observable简单包装成BlockingObservable public static \u0026lt;T\u0026gt; BlockingObservable\u0026lt;T\u0026gt; from(final Observable\u0026lt;? extends T\u0026gt; o) { return new BlockingObservable\u0026lt;T\u0026gt;(o); } public Future\u0026lt;T\u0026gt; toFuture() { return BlockingOperatorToFuture.toFuture((Observable\u0026lt;T\u0026gt;)o); } BlockingOperatorToFuture.java ReactiveX 关于toFuture的解读\nThe toFuture operator applies to the BlockingObservable subclass, so in order to use it, you must first convert your source Observable into a BlockingObservable by means of either the BlockingObservable.from method or the Observable.toBlocking operator.\n toFuture只能作用于BlockingObservable所以也才会有上文想要转换为BlockingObservable的操作\n// 该操作将 源Observable转换为返回单个数据项的Future public static \u0026lt;T\u0026gt; Future\u0026lt;T\u0026gt; toFuture(Observable\u0026lt;? extends T\u0026gt; that) { // CountDownLatch 判断是否完成 final CountDownLatch finished = new CountDownLatch(1); // 存储执行结果 final AtomicReference\u0026lt;T\u0026gt; value = new AtomicReference\u0026lt;T\u0026gt;(); // 存储错误结果 final AtomicReference\u0026lt;Throwable\u0026gt; error = new AtomicReference\u0026lt;Throwable\u0026gt;(); // single()方法可以限制Observable只发送单条数据 // 如果有多条数据 会抛 IllegalArgumentException // 如果没有数据可以发送 会抛 NoSuchElementException @SuppressWarnings(\u0026quot;unchecked\u0026quot;) final Subscription s = ((Observable\u0026lt;T\u0026gt;)that).single().subscribe(new Subscriber\u0026lt;T\u0026gt;() { // single()返回的Observable就可以对其进行标准的处理了 @Override public void onCompleted() { finished.countDown(); } @Override public void onError(Throwable e) { error.compareAndSet(null, e); finished.countDown(); } @Override public void onNext(T v) { // \u0026quot;single\u0026quot; guarantees there is only one \u0026quot;onNext\u0026quot; value.set(v); } }); // 最后将Subscription返回的数据封装成Future,实现对应的逻辑 return new Future\u0026lt;T\u0026gt;() { // 可以查看源码 }; } AbstractCommand.java AbstractCommand是toObservable实现的地方,属于Hystrix的核心逻辑,代码较长,可以和方法调用的流程图一起食用.toObservable主要是完成缓存和创建Observable,requestLog的逻辑,当第一次创建Observable时,applyHystrixSemantics方法是Hystrix的语义实现,可以跳着看.\n tips: 下文中有很多 Action和 Function,他们很相似,都有call方法,但是区别在于Function有返回值,而Action没有,方法后跟着的数字代表有几个入参.Func0/Func3即没有入参和有三个入参\n toObservable toObservable代码较长且分层还是清晰的,所以下面一块一块写.其逻辑和文章开始提到的Hystrix流程图是完全一致的.\npublic Observable\u0026lt;R\u0026gt; toObservable() { final AbstractCommand\u0026lt;R\u0026gt; _cmd = this; // 此处省略掉了很多个Action和Function,大部分是来做扫尾清理的函数,所以用到的时候再说 // defer在上篇rxjava入门中提到过,是一种创建型的操作符,每次订阅时会产生新的Observable,回调方法中所实现的才是真正我们需要的Observable return Observable.defer(new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call() { // 校验命令的状态,保证其只执行一次 if (!commandState.compareAndSet(CommandState.NOT_STARTED, CommandState.OBSERVABLE_CHAIN_CREATED)) { IllegalStateException ex = new IllegalStateException(\u0026quot;This instance can only be executed once. Please instantiate a new instance.\u0026quot;); //TODO make a new error type for this throw new HystrixRuntimeException(FailureType.BAD_REQUEST_EXCEPTION, _cmd.getClass(), getLogMessagePrefix() + \u0026quot; command executed multiple times - this is not permitted.\u0026quot;, ex, null); } commandStartTimestamp = System.currentTimeMillis(); // properties为当前command的所有属性 // 允许记录请求log时会保存当前执行的command if (properties.requestLogEnabled().get()) { // log this command execution regardless of what happened if (currentRequestLog != null) { currentRequestLog.addExecutedCommand(_cmd); } } // 是否开启了请求缓存 final boolean requestCacheEnabled = isRequestCachingEnabled(); // 获取缓存key final String cacheKey = getCacheKey(); // 开启缓存后,尝试从缓存中取 if (requestCacheEnabled) { HystrixCommandResponseFromCache\u0026lt;R\u0026gt; fromCache = (HystrixCommandResponseFromCache\u0026lt;R\u0026gt;) requestCache.get(cacheKey); if (fromCache != null) { isResponseFromCache = true; return handleRequestCacheHitAndEmitValues(fromCache, _cmd); } } // 没有开启请求缓存时,就执行正常的逻辑 Observable\u0026lt;R\u0026gt; hystrixObservable = // 这里又通过defer创建了我们需要的Observable Observable.defer(applyHystrixSemantics) // 发送前会先走一遍hook,默认executionHook是空实现的,所以这里就跳过了 .map(wrapWithAllOnNextHooks); // 得到最后的封装好的Observable后,将其放入缓存 if (requestCacheEnabled \u0026amp;\u0026amp; cacheKey != null) { // wrap it for caching HystrixCachedObservable\u0026lt;R\u0026gt; toCache = HystrixCachedObservable.from(hystrixObservable, _cmd); HystrixCommandResponseFromCache\u0026lt;R\u0026gt; fromCache = (HystrixCommandResponseFromCache\u0026lt;R\u0026gt;) requestCache.putIfAbsent(cacheKey, toCache); if (fromCache != null) { // another thread beat us so we'll use the cached value instead toCache.unsubscribe(); isResponseFromCache = true; return handleRequestCacheHitAndEmitValues(fromCache, _cmd); } else { // we just created an ObservableCommand so we cast and return it afterCache = toCache.toObservable(); } } else { afterCache = hystrixObservable; } return afterCache // 终止时的操作 .doOnTerminate(terminateCommandCleanup) // perform cleanup once (either on normal terminal state (this line), or unsubscribe (next line)) // 取消订阅时的操作 .doOnUnsubscribe(unsubscribeCommandCleanup) // perform cleanup once // 完成时的操作 .doOnCompleted(fireOnCompletedHook); } } handleRequestCacheHitAndEmitValues 缓存击中时的处理\nprivate Observable\u0026lt;R\u0026gt; handleRequestCacheHitAndEmitValues(final HystrixCommandResponseFromCache\u0026lt;R\u0026gt; fromCache, final AbstractCommand\u0026lt;R\u0026gt; _cmd) { try { // Hystrix中有大量的hook 如果有心做二次开发的,可以利用这些hook做到很完善的监控 executionHook.onCacheHit(this); } catch (Throwable hookEx) { logger.warn(\u0026quot;Error calling HystrixCommandExecutionHook.onCacheHit\u0026quot;, hookEx); } // 将缓存的结果赋给当前command return fromCache.toObservableWithStateCopiedInto(this) // doOnTerminate 或者是后面看到的doOnUnsubscribe,doOnError,都指的是在响应onTerminate/onUnsubscribe/onError后的操作,即在Observable的生命周期上注册一个动作优雅的处理逻辑 .doOnTerminate(new Action0() { @Override public void call() { // 命令最终状态的不同进行不同处理 if (commandState.compareAndSet(CommandState.OBSERVABLE_CHAIN_CREATED, CommandState.TERMINAL)) { cleanUpAfterResponseFromCache(false); //user code never ran } else if (commandState.compareAndSet(CommandState.USER_CODE_EXECUTED, CommandState.TERMINAL)) { cleanUpAfterResponseFromCache(true); //user code did run } } }) .doOnUnsubscribe(new Action0() { @Override public void call() { // 命令最终状态的不同进行不同处理 if (commandState.compareAndSet(CommandState.OBSERVABLE_CHAIN_CREATED, CommandState.UNSUBSCRIBED)) { cleanUpAfterResponseFromCache(false); //user code never ran } else if (commandState.compareAndSet(CommandState.USER_CODE_EXECUTED, CommandState.UNSUBSCRIBED)) { cleanUpAfterResponseFromCache(true); //user code did run } } }); } applyHystrixSemantics 因为本片文章的主要目的是在讲执行流程,所以失败回退和断路器相关的就留到以后的文章中再写.\nfinal Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt; applyHystrixSemantics = new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call() { // 不再订阅了就返回不发送数据的Observable if (commandState.get().equals(CommandState.UNSUBSCRIBED)) { // 不发送任何数据或通知 return Observable.never(); } return applyHystrixSemantics(_cmd); } }; private Observable\u0026lt;R\u0026gt; applyHystrixSemantics(final AbstractCommand\u0026lt;R\u0026gt; _cmd) { // 标记开始执行的hook // 如果hook内抛异常了,会快速失败且没有fallback处理 executionHook.onStart(_cmd); /* determine if we're allowed to execute */ // 断路器核心逻辑: 判断是否允许执行(TODO) if (circuitBreaker.allowRequest()) { // Hystrix自己造的信号量轮子,之所以不用juc下,官方解释为juc的Semphore实现太复杂,而且没有动态调节的信号量大小的能力,简而言之,不满足需求! // 根据不同隔离策略(线程池隔离/信号量隔离)获取不同的TryableSemphore final TryableSemaphore executionSemaphore = getExecutionSemaphore(); // Semaphore释放标志 final AtomicBoolean semaphoreHasBeenReleased = new AtomicBoolean(false); // 释放信号量的Action final Action0 singleSemaphoreRelease = new Action0() { @Override public void call() { if (semaphoreHasBeenReleased.compareAndSet(false, true)) { executionSemaphore.release(); } } }; // 异常处理 final Action1\u0026lt;Throwable\u0026gt; markExceptionThrown = new Action1\u0026lt;Throwable\u0026gt;() { @Override public void call(Throwable t) { // HystrixEventNotifier是hystrix的插件,不同的事件发送不同的通知,默认是空实现. eventNotifier.markEvent(HystrixEventType.EXCEPTION_THROWN, commandKey); } }; // 线程池隔离的TryableSemphore始终为true if (executionSemaphore.tryAcquire()) { try { /* used to track userThreadExecutionTime */ // executionResult是一次命令执行的结果信息封装 // 这里设置起始时间是为了记录命令的生命周期,执行过程中会set其他属性进去 executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis()); return executeCommandAndObserve(_cmd) // 报错时的处理 .doOnError(markExceptionThrown) // 终止时释放 .doOnTerminate(singleSemaphoreRelease) // 取消订阅时释放 .doOnUnsubscribe(singleSemaphoreRelease); } catch (RuntimeException e) { return Observable.error(e); } } else { // tryAcquire失败后会做fallback处理,TODO return handleSemaphoreRejectionViaFallback(); } } else { // 断路器短路(拒绝请求)fallback处理 TODO return handleShortCircuitViaFallback(); } } executeCommandAndObserve /** * 执行run方法的地方 */ private Observable\u0026lt;R\u0026gt; executeCommandAndObserve(final AbstractCommand\u0026lt;R\u0026gt; _cmd) { // 获取当前上下文 final HystrixRequestContext currentRequestContext = HystrixRequestContext.getContextForCurrentThread(); // 发送数据时的Action响应 final Action1\u0026lt;R\u0026gt; markEmits = new Action1\u0026lt;R\u0026gt;() { @Override public void call(R r) { // 如果onNext时需要上报时,做以下处理 if (shouldOutputOnNextEvents()) { // result标记 executionResult = executionResult.addEvent(HystrixEventType.EMIT); // 通知 eventNotifier.markEvent(HystrixEventType.EMIT, commandKey); } // commandIsScalar是一个我不解的地方,在网上也没有查到好的解释 // 该方法为抽象方法,有HystrixCommand实现返回true.HystrixObservableCommand返回false if (commandIsScalar()) { // 耗时 long latency = System.currentTimeMillis() - executionResult.getStartTimestamp(); // 通知 eventNotifier.markCommandExecution(getCommandKey(), properties.executionIsolationStrategy().get(), (int) latency, executionResult.getOrderedList()); eventNotifier.markEvent(HystrixEventType.SUCCESS, commandKey); executionResult = executionResult.addEvent((int) latency, HystrixEventType.SUCCESS); // 断路器标记成功(断路器半开时的反馈,决定是否关闭断路器) circuitBreaker.markSuccess(); } } }; final Action0 markOnCompleted = new Action0() { @Override public void call() { if (!commandIsScalar()) { // 同markEmits 类似处理 } } }; // 失败回退的逻辑 final Func1\u0026lt;Throwable, Observable\u0026lt;R\u0026gt;\u0026gt; handleFallback = new Func1\u0026lt;Throwable, Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call(Throwable t) { // 不是重点略过了 } }; // 请求上下文的处理 final Action1\u0026lt;Notification\u0026lt;? super R\u0026gt;\u0026gt; setRequestContext = new Action1\u0026lt;Notification\u0026lt;? super R\u0026gt;\u0026gt;() { @Override public void call(Notification\u0026lt;? super R\u0026gt; rNotification) { setRequestContextIfNeeded(currentRequestContext); } }; Observable\u0026lt;R\u0026gt; execution; // 如果有执行超时限制,会将包装后的Observable再转变为支持TimeOut的 if (properties.executionTimeoutEnabled().get()) { // 根据不同的隔离策略包装为不同的Observable execution = executeCommandWithSpecifiedIsolation(_cmd) // lift 是rxjava中一种基本操作符 可以将Observable转换成另一种Observable // 包装为带有超时限制的Observable .lift(new HystrixObservableTimeoutOperator\u0026lt;R\u0026gt;(_cmd)); } else { execution = executeCommandWithSpecifiedIsolation(_cmd); } return execution.doOnNext(markEmits) .doOnCompleted(markOnCompleted) .onErrorResumeNext(handleFallback) .doOnEach(setRequestContext); } executeCommandWithSpecifiedIsolation 根据不同的隔离策略创建不同的执行Observable\nprivate Observable\u0026lt;R\u0026gt; executeCommandWithSpecifiedIsolation(final AbstractCommand\u0026lt;R\u0026gt; _cmd) { if (properties.executionIsolationStrategy().get() == ExecutionIsolationStrategy.THREAD) { // mark that we are executing in a thread (even if we end up being rejected we still were a THREAD execution and not SEMAPHORE) return Observable.defer(new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call() { // 由于源码太长,这里只关注正常的流程,需要详细了解可以去看看源码 if (threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.STARTED)) { try { return getUserExecutionObservable(_cmd); } catch (Throwable ex) { return Observable.error(ex); } } else { //command has already been unsubscribed, so return immediately return Observable.error(new RuntimeException(\u0026quot;unsubscribed before executing run()\u0026quot;)); } }}) .doOnTerminate(new Action0() {}) .doOnUnsubscribe(new Action0() {}) // 指定在某一个线程上执行,是rxjava中很重要的线程调度的概念 .subscribeOn(threadPool.getScheduler(new Func0\u0026lt;Boolean\u0026gt;() { })); } else { // 信号量隔离策略 return Observable.defer(new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { // 逻辑与线程池大致相同 }); } } getUserExecutionObservable 获取用户执行的逻辑\nprivate Observable\u0026lt;R\u0026gt; getUserExecutionObservable(final AbstractCommand\u0026lt;R\u0026gt; _cmd) { Observable\u0026lt;R\u0026gt; userObservable; try { // getExecutionObservable是抽象方法,有HystrixCommand自行实现 userObservable = getExecutionObservable(); } catch (Throwable ex) { // the run() method is a user provided implementation so can throw instead of using Observable.onError // so we catch it here and turn it into Observable.error userObservable = Observable.error(ex); } // 将Observable作其他中转 return userObservable .lift(new ExecutionHookApplication(_cmd)) .lift(new DeprecatedOnRunHookApplication(_cmd)); } lift操作符\nlift可以转换成一个新的Observable,它很像一个代理,将原来的Observable代理到自己这里,订阅时通知原来的Observable发送数据,经自己这里流转加工处理再返回给订阅者.Map/FlatMap操作符底层其实就是用的lift进行实现的.\ngetExecutionObservable @Override final protected Observable\u0026lt;R\u0026gt; getExecutionObservable() { return Observable.defer(new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call() { try { // just操作符就是直接执行的Observable // run方法就是我们实现的业务逻辑: Hello World~ return Observable.just(run()); } catch (Throwable ex) { return Observable.error(ex); } } }).doOnSubscribe(new Action0() { @Override public void call() { // 执行订阅时将执行线程记为当前线程,必要时我们可以interrupt executionThread.set(Thread.currentThread()); } }); } 总结 希望自己能把埋下的坑一一填完: 容错机制,metrics,断路器等等\u0026hellip;\n参考 Hystrix How it Works ReactiveX官网 阮一峰: 中文技术文档写作规范 RxJava lift 原理解析 ","id":10,"section":"posts","summary":"","tags":["rxjava","hystrix"],"title":"Hystrix命令执行流程","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/rxjava-in-hystrix/","year":"2019"},{"content":" 本文基于 rxjava 1.x 版本\n 前言 写这篇文章是因为之前在看Hystrix时,觉得响应式编程很有意思,之前也了解到Spring5主打特性就是响应式,就想来试试水,入个门.本文主要介绍RxJava的特点,入门操作\nRxJava是什么 Reactive X ReactiveX是使用Observable序列来组合异步操作且基于事件驱动的一个库.其继承自观察者模式来支持数据流或者事件流通过添加操作符(operators)的方式来声明式的操作,并抽象出对低级别线程(low-level thread),同步,线程安全,并发数据结构,非阻塞IO问题的关注.\nReactiveX 在不同语言中都有实现,RxJava 只是在JVM上实现的一套罢了.\n概念 观察者模式是该框架的灵魂~\n 上图可以表述为: 观察者(Observer) 订阅(subscribe)被观察者(Observable),当Observable产生事件或数据时,会调用Observer的方法进行回调.\n听起来有点别扭,这里举一个形象点的例子.\n显示器开关\n显示器开关即为 Observable, 显示器为 Observer,这两个组件就会形成联系.当开关按下时,显示器就会通电点亮,这里即可抽象成Observable发出一个事件,Observer对事件做了处理.做什么样的处理其实在Subscribe时就已经决定了.\n回调方法\n在subscribe时会要求实现对应的回调方法,标准方法有以下三个:\n onNext Observable调用这个方法发射数据,方法的参数就是Observable发射的数据,这个方法可能会被调用多次,取决于你的实现。\n onError 当Observable遇到错误或者无法返回期望的数据时会调用这个方法,这个调用会终止Observable,后续不会再调用onNext和onCompleted,onError方法的参数是抛出的异常。\n onCompleted 正常终止,如果没有遇到错误,Observable在最后一次调用onNext之后调用此方法。\n\u0026ldquo;Hot\u0026rdquo; or \u0026ldquo;Cold\u0026rdquo; Observables Observable何时开始发送数据呢?基于此问题,可以将Observable分为两类: Hot \u0026amp; Cold . 可以理解为主动型和被动型.\nHot Observable: Observable一经创建,就会开始发送数据. 所以后面订阅的Observer可能消费不到Observable完整的数据.\nCold Observable: Observable会等到有Observer订阅时才开始发送数据,此时Observer会消费到完整的数据\nRxJava入门 Hello World Observable.create(new Observable.OnSubscribe\u0026lt;String\u0026gt;() { @Override public void call(Subscriber\u0026lt;? super String\u0026gt; subscriber) { subscriber.onNext(\u0026quot;Hello World\u0026quot;); subscriber.onCompleted(); //subscriber.onError(new RuntimeException(\u0026quot;error\u0026quot;)); } }).subscribe(new Subscriber\u0026lt;String\u0026gt;() { @Override public void onCompleted() { System.out.println(\u0026quot;观察结束啦~~~\u0026quot;); } @Override public void onError(Throwable e) { System.out.println(\u0026quot;观察出错啦~~~\u0026quot;); } @Override public void onNext(String s) { System.out.println(\u0026quot;onNext:\u0026quot; + s); } }); } // onNext:Hello World // 观察结束啦~~~ // 注释掉上一行 打开下一行注释 就会输出 // onNext:Hello World // 观察出错啦~~~ 上述即为一个标准的创建观察者被观察者并订阅,实现订阅逻辑.\n疑问\n 为什么subscribe方法的参数是Subscriber呢? 在rxjava中Observer是接口,Subscriber实现了Observer并提供了拓展.所以普遍用这个.\n 为什么是Observable.subscribe(Observer)?用上面的显示器开关的例子来说就相当于显示器开关订阅显示器. 为了保证流式风格~rxjava提供了一系列的操作符来对Observable发出的数据做处理,流式风格可以使操作符使用起来更友好.所以就当做Observable订阅了Observer吧🤦‍♂\n操作符 Operators 单纯的使用上面的Hello World撸码只能说是观察者模式的运用罢了,操作符才是ReactiveX最强大的地方.我们可以通过功能不同的操作符对Observable发出的数据做过滤(filter),转换(map)来满足业务的需求.其实就可以当作是Java8的lambda特性.\n Observable在经过操作符处理后还是一个Observable,对应上述的流式风格\n 案例: 假设我们需要监听鼠标在一个直角坐标系中的点击,取得所有在第一象限点击的坐标.\n从该流程图可以看出,鼠标点击后会发出很多数据,一次点击一个点,我们对数据进行filter,得到了下方时间轴上的数据源.这就是我们想要的.下面来看下常用的操作符有哪些?\n创建型操作符 用于创建Observable对象的操作符\n Create 创建一个Observable,需要传递一个Function来完成调用Observer的逻辑.\n一个标准的Observable必须只能调用一次(Exactly Once)onCompleted或者onError,并且在调用后不能再调用Observer的其他方法(eg: onNext).\nsample code\nObservable.create(new Observable.OnSubscribe\u0026lt;Integer\u0026gt;() { @Override public void call(Subscriber\u0026lt;? super Integer\u0026gt; observer) { try { if (!observer.isUnsubscribed()) { for (int i = 1; i \u0026lt; 5; i++) { observer.onNext(i); } observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } } ).subscribe(new Subscriber\u0026lt;Integer\u0026gt;() { @Override public void onNext(Integer item) { System.out.println(\u0026quot;Next: \u0026quot; + item); } @Override public void onError(Throwable error) { System.err.println(\u0026quot;Error: \u0026quot; + error.getMessage()); } @Override public void onCompleted() { System.out.println(\u0026quot;Sequence complete.\u0026quot;); } }); Next: 1 Next: 2 Next: 3 Next: 4 Sequence complete. Defer 直到有Observer订阅时才会创建,并且会为每一个Observer创建新的Observable,这样可以保证所有Observer可以看到相同的数据,并且从头开始消费.\nsample code\nObservable\u0026lt;String\u0026gt; defer = Observable.defer(new Func0\u0026lt;Observable\u0026lt;String\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;String\u0026gt; call() { return Observable.just(\u0026quot;Hello\u0026quot;, \u0026quot;World\u0026quot;); } }); defer.subscribe(new Subscriber\u0026lt;String\u0026gt;() { @Override public void onCompleted() { System.out.println(\u0026quot;第一个订阅完成啦~\u0026quot;); } @Override public void onError(Throwable e) { System.out.println(\u0026quot;第一个订阅报错啦~\u0026quot;); } @Override public void onNext(String s) { System.out.println(\u0026quot;第一个订阅收到:\u0026quot; + s); } }); defer.subscribe(new Subscriber\u0026lt;String\u0026gt;() { //与上一个订阅逻辑相同 }); 第一个订阅收到:Hello 第一个订阅收到:World 第一个订阅完成啦~ 第二个订阅收到:Hello 第二个订阅收到:World 第二个订阅完成啦~ Note:\nDefer在RxJava中的实现其实有点像指派,可以看到构建时,传参为Func0\u0026lt;Observable\u0026lt;T\u0026gt;\u0026gt;,Observer真正订阅的是传参中的Observable.\nJust 在上文Defer中代码中就用了Just,指的是可以发送特定的数据.代码一致就不作展示了.\nInterval 可以按照指定时间间隔从0开始发送无限递增序列.\n参数 initalDelay 延迟多长时间开始第一次发送 period 指定时间间隔 unit 时间单位 如下例子:延迟0秒后开始发送,每1秒发送一次. 因为sleep 100秒,会发送0-99终止\nsample code\nObservable.interval(0,1,TimeUnit.SECONDS).subscribe(new Action1\u0026lt;Long\u0026gt;() { // 这里只实现了OnNext方法,onError和onCompleted可以有默认实现.一种偷懒写法 @Override public void call(Long aLong) { System.out.println(aLong); } }); try { //阻塞当前线程使程序一直跑 TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } 转换操作符 将Observable发出的数据进行各类转换的操作符\n Buffer 如上图所示,buffer定期将数据收集到集合中,并将集合打包发送.\nsample code\nObservable.just(2,3,5,6) .buffer(3) .subscribe(new Action1\u0026lt;List\u0026lt;Integer\u0026gt;\u0026gt;() { @Override public void call(List\u0026lt;Integer\u0026gt; integers) { System.out.println(integers); } }); [2, 3, 5] [6] Window\nwindow和buffer是非常像的两个操作符,区别在于buffer会将存起来的item打包再发出去,而window则只是单纯的将item堆起来,达到阈值再发出去,不对原数据结构做修改.\nsample code\nObservable.just(2,3,5,6) .window(3) .subscribe(new Action1\u0026lt;Observable\u0026lt;Integer\u0026gt;\u0026gt;() { @Override public void call(Observable\u0026lt;Integer\u0026gt; integerObservable) { integerObservable.subscribe(new Action1\u0026lt;Integer\u0026gt;() { @Override public void call(Integer integer) { // do anything } }); } }); 合并操作符 将多个Observable合并为一个的操作符\n Zip 使用一个函数组合多个Observable发射的数据集合,然后再发射这个结果。如果多个Observable发射的数据量不一样,则以最少的Observable为标准进行组合.\nsample code\nObservable\u0026lt;Integer\u0026gt; observable1=Observable.just(1,2,3,4); Observable\u0026lt;Integer\u0026gt; observable2=Observable.just(4,5,6); Observable.zip(observable1, observable2, new Func2\u0026lt;Integer, Integer, String\u0026gt;() { @Override public String call(Integer item1, Integer item2) { return item1+\u0026quot;and\u0026quot;+item2; } }).subscribe(new Action1\u0026lt;String\u0026gt;() { @Override public void call(String s) { System.out.println(s); } }); 1and4 2and5 3and6 背压操作符 用于平衡Observer消费速度,Observable生产速度的操作符\n 背压是指在异步场景中,被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略.下图可以很好阐释背压机制是如何运行的.\n宗旨就是下游告诉上游我能处理多少你就给我发多少.\n//被观察者将产生100000个事件 Observable observable=Observable.range(1,100000); observable.observeOn(Schedulers.newThread()) .subscribe(new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Object o) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(\u0026quot;on Next Request...\u0026quot;); request(1); } }); 背压支持 上述样例代码中创建Observable使用的是range操作符,这是因为他是支持背压的,如果用interval,request的方法将不起作用.因为interval不支持背压.那什么样的Observable支持背压呢?\n在前面介绍概念时,有提到过Hot\u0026amp;Cold的区别,Hot类型的Observable,即一经创建就开始发送,不支持背压,Cold类型的Observable也只是部分支持.\nonBackpressurebuffer/onBackpressureDrop 不支持背压的操作符我们可以如何实现背压呢?就通过onBackpressurebuffer/onBackpressureDrop来实现.顾名思义一个是缓存,一个是丢弃.\n这里以drop方式来展示.\nObservable.interval(1, TimeUnit.MILLISECONDS) .onBackpressureDrop() //指定observer调度io线程上,并将缓存size置为1,这个缓存会提前将数据存好在消费, //默认在PC上是128,设置小一点可以快速的看到drop的效果 .observeOn(Schedulers.io(), 1) .subscribe(new Subscriber\u0026lt;Long\u0026gt;() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { System.out.println(\u0026quot;Error:\u0026quot; + e.getMessage()); } @Override public void onNext(Long aLong) { System.out.println(\u0026quot;订阅 \u0026quot; + aLong); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }) 订阅 0 订阅 103 订阅 207 订阅 300 订阅 417 订阅 519 订阅 624 订阅 726 订阅 827 订阅 931 订阅 1035 订阅 1138 订阅 1244 订阅 1349 可以很明显的看出很多数据被丢掉了,这就是背压的效果.\n总结 写了这么多后,想来说说自己的感受.\n 转变思想: 响应式编程的思想跟我们现在后端开发思路是有区别的.可能刚开始会不适应. 不易调试: 流式风格写着爽,调着难 参考 ReactiveX官网\n关于RxJava最友好的文章——背压(Backpressure)\n如何形象地描述RxJava中的背压和流控机制?\n","id":11,"section":"posts","summary":"\u003cblockquote\u003e\n\u003cp\u003e本文基于 rxjava 1.x 版本\u003c/p\u003e\n\u003c/blockquote\u003e","tags":["rxjava","响应式编程"],"title":"RxJava入门","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/rxjava-guide/","year":"2019"},{"content":"Given an array, rotate the array to the right by k steps, where k is non-negative.\nExample 1:\nInput: [1,2,3,4,5,6,7] and k = 3 Output: [5,6,7,1,2,3,4] Explanation: rotate 1 steps to the right: [7,1,2,3,4,5,6] rotate 2 steps to the right: [6,7,1,2,3,4,5] rotate 3 steps to the right: [5,6,7,1,2,3,4] Example 2:\nInput: [-1,-100,3,99] and k = 2 Output: [3,99,-1,-100] Explanation: rotate 1 steps to the right: [99,-1,-100,3] rotate 2 steps to the right: [3,99,-1,-100] Note:\n Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem. Could you do it in-place with O(1) extra space? 思路\n依次反转前半部分及后半部分,最后反转整个数组\neg: 1,2,3,4,5,6,7 k=3\n 反转前半部分 4,3,2,1,5,6,7\n 反转后半部分 4,3,2,1,7,6,5\n 反转整个数组 5,6,7,1,2,3,4\nSolution 1\npub fn rotate(nums: \u0026amp;mut Vec\u0026lt;i32\u0026gt;, k: i32) { if nums.is_empty() || k \u0026lt;= 0 { return; } let o_len = nums.len(); let mod_k = k as usize % o_len; reverse(nums, 0, o_len - mod_k - 1); reverse(nums, o_len - mod_k, o_len - 1); reverse(nums, 0, o_len - 1); } pub fn reverse(nums: \u0026amp;mut Vec\u0026lt;i32\u0026gt;, start: usize, end: usize) { let mut o_start = start; let mut o_end = end; while o_start \u0026lt; o_end { nums.swap(o_start, o_end); o_start += 1; o_end -= 1; } } Solution 2\n api 解法,效率不高,但好看\n pub fn rotate(nums: \u0026amp;mut Vec\u0026lt;i32\u0026gt;, k: i32) { if nums.is_empty() { return; } let mod_k = k % nums.len() as i32; for _ in 0..mod_k as usize { let item = nums.pop().unwrap(); nums.insert(0, item); } } ","id":12,"section":"posts","summary":"","tags":["leetcode","rust"],"title":"[LeetCode In Rust]189-Rotate Array","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/189-rotate-array/","year":"2019"},{"content":"pub fn remove_duplicates(nums: \u0026amp;mut Vec\u0026lt;i32\u0026gt;) -\u0026gt; i32 { if nums.is_empty() { return 0 } let mut idx = 0; for i in idx .. nums.len() { if nums[i].gt(\u0026amp;nums[idx]) { idx += 1; nums.swap(i,idx); } } (idx + 1) as i32 } ","id":13,"section":"posts","summary":"","tags":["leetcode","rust"],"title":"[LeetCode In Rust]026-Remove Duplicates From Sorted Array","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/026-remove-duplicates-from-sorted-array/","year":"2019"},{"content":"基本类型-Primitives 标准类型 Scalar Types 有符号整型 signed integers: i8, i16, i32, i64, i128 and isize (pointer size)\n 无符号整型 unsigned integers: u8, u16, u32, u64, u128 and usize (pointer size)\n 浮点型 floating point: f32, f64\n 字符 char Unicode scalar values like 'a', 'α' and '∞' (4 bytes each)\n 布尔 bool either true or false\n the unit type (), whose only possible value is an empty tuple: ()\n 我的理解就是个empty吧\n 数值字面,没有后缀时.默认整数为 i32,浮点数 f64\n三级目录 有符号整型 signed integers: i8, i16, i32, i64, i128 and isize (pointer size)\n 无符号整型 unsigned integers: u8, u16, u32, u64, u128 and usize (pointer size)\n 浮点型 floating point: f32, f64\n 字符 char Unicode scalar values like 'a', 'α' and '∞' (4 bytes each)\n 布尔 bool either true or false\n the unit type (), whose only possible value is an empty tuple: ()\n 我的理解就是个empty吧\n 数值字面,没有后缀时.默认整数为 i32,浮点数 f64\n四级目录 有符号整型 signed integers: i8, i16, i32, i64, i128 and isize (pointer size)\n 无符号整型 unsigned integers: u8, u16, u32, u64, u128 and usize (pointer size)\n 浮点型 floating point: f32, f64\n 字符 char Unicode scalar values like 'a', 'α' and '∞' (4 bytes each)\n 布尔 bool either true or false\n the unit type (), whose only possible value is an empty tuple: ()\n 我的理解就是个empty吧\n 数值字面,没有后缀时.默认整数为 i32,浮点数 f64\n复合类型 Compound Types 数组 array [1,2,3] 元组 tuple (1,true,\u0026ldquo;str\u0026rdquo;,\u0026lsquo;a\u0026rsquo;\u0026hellip;) Note: 默认变量赋值后是不可变的,需要重复赋值需要用mut 修饰\nlet immutable = 1; immutable = 2; // ERROR let mut immutable = 1; immutable = 2; // SUCCESS 所有权踩坑 可变引用在作用域下有且只能有一个可变引用\n 不可在拥有不可变引用的同时拥有可变引用,除非作用域没有重叠.即在引用可变引用时,不可变引用已经失效了.\nlet mut s = String::from(\u0026quot;hello\u0026quot;); let r1 = \u0026amp;s; // 没问题 let r2 = \u0026amp;s; // 没问题 let r3 = \u0026amp;mut s; // 大问题 println!(\u0026quot;{}, {}, and {}\u0026quot;, r1, r2, r3); ////////////////////////////////////// let mut s = String::from(\u0026quot;hello\u0026quot;); let r1 = \u0026amp;s; // 没问题 let r2 = \u0026amp;s; // 没问题 println!(\u0026quot;{} and {}\u0026quot;, r1, r2); // 此位置之后 r1 和 r2 不再使用 let r3 = \u0026amp;mut s; // 没问题 println!(\u0026quot;{}\u0026quot;, r3); ","id":14,"section":"posts","summary":"","tags":["rust"],"title":"rust踩坑笔记","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/rust/","year":"2019"},{"content":"pub fn two_sum(nums: Vec\u0026lt;i32\u0026gt;, target: i32) -\u0026gt; Vec\u0026lt;i32\u0026gt; { let map: HashMap\u0026lt;i32, usize\u0026gt; = nums.iter().enumerate().map(|(idx, \u0026amp;data)| (data, idx)).collect(); nums.iter().enumerate().find(|(idx, \u0026amp;num)| { match map.get(\u0026amp;(target - num)) { Some(\u0026amp;idx_in_map) =\u0026gt; idx_in_map != *idx, None =\u0026gt; false, } }).map(|(idx, \u0026amp;num)| vec![*map.get(\u0026amp;(target - num)).unwrap() as i32, idx as i32]).unwrap() } pub fn two_sum_v2(nums: Vec\u0026lt;i32\u0026gt;, target: i32) -\u0026gt; Vec\u0026lt;i32\u0026gt; { let map: HashMap\u0026lt;i32, usize\u0026gt; = nums.iter().enumerate().map(|(idx, \u0026amp;data)| (data, idx)).collect(); for (i,\u0026amp;num) in nums.iter().enumerate() { match map.get(\u0026amp;(target - num) ) { Some(\u0026amp;x) =\u0026gt; { if i != x { return vec![i as i32, x as i32] } }, None =\u0026gt; continue, } } vec![] } ","id":15,"section":"posts","summary":"","tags":["leetcode","rust"],"title":"[LeetCode In Rust]001-Two Sum","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/001-two-sum/","year":"2019"},{"content":"ArrayList和LinkedList和Vector的区别 简单讲: 1. ArrayList和LinkedList是线程不安全的,而Vector在增删改的操作上都有synchronized关键字修饰,是线性的,但是效率不高 2. ArrayList和Vector都是基于数组的实现,扩容时,ArrayList扩容为1.5倍,Vector扩容为2倍,查找快,增删慢.LinkedList是一个双向链表,增删快,查找慢. \u0026gt; 参考blog\nSynchronizedList和Vector的区别 SynchronizedList有很好的扩展和兼容功能。他可以将所有的List的子类转成线程安全的类。 使用SynchronizedList的时候,进行遍历时要手动进行同步处理。 SynchronizedList可以指定锁定的对象。 \u0026gt; Hollis的blog 参数 static final int DEFAULT_CAPACITY=10 默认大小 transient object[] elementData; arraylist存储对象的数组.当空ArrayList第一次添加对象时,容量会扩展成DEFAULT_CAPACITY size elementData中实际的对象数 构造函数 看几个主要的 1. 带初始化参数,参数违法时会抛RuntimeException\npublic ArrayList(int initialCapacity) { if (initialCapacity \u0026gt; 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException(\u0026quot;Illegal Capacity: \u0026quot;+ initialCapacity); } } 从其他集合中导入,collection需要notNull,否则会抛空指针\npublic ArrayList(Collection\u0026lt;? extends E\u0026gt; c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if (elementData.getClass() != Object[].class) elementData = Arrays.copyOf(elementData, size, Object[].class); } else { // replace with empty array. this.elementData = EMPTY_ELEMENTDATA; } } 主要方法 get\npublic E get(int index) { //判断index是否在范围内 不在会抛 IndexOutOfBoundsException rangeCheck(index); //获取对象值 return elementData(index); } set 替换指定位置的值\npublic E set(int index, E element) { rangeCheck(index);//范围检查 E oldValue = elementData(index);//获取对象旧值 elementData[index] = element; //赋新值 return oldValue; //返回旧值 } add 在elementData尾部添加一个对象\npublic boolean add(E e) { //确保在容量范围内,不在则扩容 ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++;//操作list的次数 //若最小容量大于 elementData的长度 则扩容 // overflow-conscious code if (minCapacity - elementData.length \u0026gt; 0) grow(minCapacity); } private void grow(int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity \u0026gt;\u0026gt; 1); //扩容大概是1.5倍 if (newCapacity - minCapacity \u0026lt; 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE \u0026gt; 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData, newCapacity); } add 指定位置添加对象\npublic void add(int index, E element) { //专门的add操作范围检查 主要是保证 0 \u0026lt; index \u0026lt; size rangeCheckForAdd(index); ensureCapacityInternal(size + 1); // Increments modCount!! System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; } ","id":16,"section":"posts","summary":"","tags":["collections"],"title":"Collections-Arraylist","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/","year":"2019"},{"content":" 美团的blog:https://tech.meituan.com/java_hashmap.html 参考blog: 田小波的博客 红黑树介绍\n HashMap,HashTable,ConcurrentHashMap的区别? HashMap是非线程安全的,HashTable和ConcurrentHashMap是线程安全的.HashTable不允许null key和null Value,HashMap允许. ConcurrentHashMap推出之后官方推荐不要在使用HashTable作为线程安全的使用类,而是使用这个.关于ConcurrentHashMap后面再学习. \u0026gt; 参考blog\nHashMap在不同版本之间实现的区别? 区别\n 官方文档介绍: 基于Map接口实现的哈希表.提供了所有map可选的操作,允许key为null,value为null.HashMap与HashTable基本一致,除了HashMap 线程不安全并且允许为空. 不保证有序,尤其不保证顺序一直不变(因为扩容时会rehash,基本上就顺序就重排了) 假设hash分布均匀的情况下,基本的操作(get/put)性能很不错.迭代所需要的时间与buckets数量与每个bukets下的键值对的数量之和成正比.所以官方建议如果要求hashmap的迭代性能的话,初始的capacity不能太高,loadFactor不要太高. HashMap有两个重要的参数:initial capacity,load factor.capacity定义bucket的数量,initial capacity定义的是初始化bucket数量.load factor(中文名: 加载因子 )是判断哈希表是否需要扩容的阈值,当entries数量超过(load factor * current capacity),哈希表会触发rehash操作,内部数据结构会重整,buckets数量会变为之前大约两倍左右\n 通常情况下,load factor 默认0.75f,在时间空间上是很平衡的.值偏高时,空间减少,查找时间上升了(影响大部分的操作,get/put之类的),在设置初始容量时,需要考虑到预期的entries数量和加载因子,以便最小化rehash的数量.如果初始化的容量大于最大数量的entries除以加载因子,不会发生rehash操作.\n 如果有大量的键值对存到hashmap中,那么创建一个足够大的hashmap来存储要比让他自动rehash扩容来存储的性能要好很多.注意:具有相同hashcode的多个key肯定会影响哈希表的性能.为了改善这种影响,当key是Comparable类型时,可以通过key之间的比较顺序来打破这种关系.\n 注意hashmap是Non synchronized,即 非线程安全.如果多线程并发访问hashmap,并且至少有一个线程操作map的结构,在外部必须synchronized.(结构修改是指任何关于add或delte的操作,仅仅只是修改key关联的value时则不属于结构修改).通常在将object封装进map做synchronized操作\n 如果不存在上面的objects,那这个map需要被Collections.synchronizedMap包装下.最好在创建的时候就做好,防止偶然的并发访问.\nMap m = Collections.synchronizedMap(new HashMap(...)); 迭代器的所有方法都是fail-fast,如果迭代器创建后,在迭代器里的结构操作必须通过迭代器的方法来操作,否则会抛ConcurrentModificationException.因此,面对并发修改,迭代器会快速而干净的失败,而不是在未来的不确定时间冒任意非确定行为的风险.\n 请注意,迭代器的快速失败行为无法得到保证,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证. 快速失败迭代器会尽最大努力抛出ConcurrentModificationException. 因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的快速失败行为应该仅用于检测错误.\n 源码 /** * The default initial capacity - MUST be a power of two. * 默认的容量16 必须是2的n次方 */ static final int DEFAULT_INITIAL_CAPACITY = 1 \u0026lt;\u0026lt; 4; // aka 16 /** * 最大的容量限制 * The maximum capacity, used if a higher value is implicitly specified * by either of the constructors with arguments. * MUST be a power of two \u0026lt;= 1\u0026lt;\u0026lt;30. */ static final int MAXIMUM_CAPACITY = 1 \u0026lt;\u0026lt; 30; /** * 默认的加载因子 * The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * 大于这个值转红黑树 * The bin count threshold for using a tree rather than list for a * bin. Bins are converted to trees when adding an element to a * bin with at least this many nodes. The value must be greater * than 2 and should be at least 8 to mesh with assumptions in * tree removal about conversion back to plain bins upon * shrinkage. */ static final int TREEIFY_THRESHOLD = 8; /** * 大于这个值小于 TREEIFY_THRESHOLD 不转树 * The bin count threshold for untreeifying a (split) bin during a * resize operation. Should be less than TREEIFY_THRESHOLD, and at * most 6 to mesh with shrinkage detection under removal. */ static final int UNTREEIFY_THRESHOLD = 6; /** * hashmap整体容量大于这个值时才能树化 * The smallest table capacity for which bins may be treeified. * (Otherwise the table is resized if too many nodes in a bin.) * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts * between resizing and treeification thresholds. */ static final int MIN_TREEIFY_CAPACITY = 64; /** * node节点 * Basic hash bin node, used for most entries. (See below for * TreeNode subclass, and in LinkedHashMap for its Entry subclass.) */ static class Node\u0026lt;K,V\u0026gt; implements Map.Entry\u0026lt;K,V\u0026gt; { final int hash; final K key; V value; Node\u0026lt;K,V\u0026gt; next; Node(int hash, K key, V value, Node\u0026lt;K,V\u0026gt; next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } public final K getKey() { return key; } public final V getValue() { return value; } public final String toString() { return key + \u0026quot;=\u0026quot; + value; } public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } public final boolean equals(Object o) { if (o == this) return true; if (o instanceof Map.Entry) { Map.Entry\u0026lt;?,?\u0026gt; e = (Map.Entry\u0026lt;?,?\u0026gt;)o; if (Objects.equals(key, e.getKey()) \u0026amp;\u0026amp; Objects.equals(value, e.getValue())) return true; } return false; } } //hashMap中的静态方法 /** * hash方法详解 blog:http://www.hollischuang.com/archives/2091 * 扰动算法--使hash分布更均匀 */ static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h \u0026gt;\u0026gt;\u0026gt; 16); } //取模运算,获得对象存储到bukets的下标 //实际上就是取模,一般取模使用% 但是考虑到效率问题,采用位运算 //X % 2^n = X \u0026amp; (2^n-1) 这也是为什么hashmap容量为2的n次方的原因 static int indexFor(int h, int length) { return h \u0026amp; (length-1); } //返回hashmap的容量 2的n次方 很巧妙的位运算 static final int tableSizeFor(int cap) { int n = cap - 1; n |= n \u0026gt;\u0026gt;\u0026gt; 1; n |= n \u0026gt;\u0026gt;\u0026gt; 2; n |= n \u0026gt;\u0026gt;\u0026gt; 4; n |= n \u0026gt;\u0026gt;\u0026gt; 8; n |= n \u0026gt;\u0026gt;\u0026gt; 16; return (n \u0026lt; 0) ? 1 : (n \u0026gt;= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; } /** * 参数都用transient不让序列化的原因:https://segmentfault.com/q/1010000000630486 */ //bukets hashmap是链表加数组的结构.此为数组 transient Node\u0026lt;K,V\u0026gt;[] table; //保存键值对的Entry transient Set\u0026lt;Map.Entry\u0026lt;K,V\u0026gt;\u0026gt; entrySet; //hashmap的size transient int size; //结构操作次数 可用于快速失败的比较条件 例如并发操作时 transient int modCount; //resize的临界点: capacity * load factor int threshold; //加载因子 final float loadFactor; //公有操作方法 //构造方法 /** * 根据 initial capactity 和 loadFactor创建空的hashmap * Constructs an empty \u0026lt;tt\u0026gt;HashMap\u0026lt;/tt\u0026gt; with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap(int initialCapacity, float loadFactor) { //校验initialCapacity if (initialCapacity \u0026lt; 0) throw new IllegalArgumentException(\u0026quot;Illegal initial capacity: \u0026quot; + initialCapacity); //容量校验 if (initialCapacity \u0026gt; MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; //校验loadFactor isNaN--\u0026gt; 是否是一个number Not-a-Number if (loadFactor \u0026lt;= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException(\u0026quot;Illegal load factor: \u0026quot; + loadFactor); //加载因子赋值 this.loadFactor = loadFactor; //扩容阈值赋值 2的n次方 this.threshold = tableSizeFor(initialCapacity); } /** * 通过initialCapacity赋值 * Constructs an empty \u0026lt;tt\u0026gt;HashMap\u0026lt;/tt\u0026gt; with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * 根据默认容量和默认加载因子创建空的hashmap * Constructs an empty \u0026lt;tt\u0026gt;HashMap\u0026lt;/tt\u0026gt; with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } /** * 根据传进来的map创建一个新的hashMap * initialCapacity 足以装下参数map的数量 * loadFactor使用默认值 * Constructs a new \u0026lt;tt\u0026gt;HashMap\u0026lt;/tt\u0026gt; with the same mappings as the * specified \u0026lt;tt\u0026gt;Map\u0026lt;/tt\u0026gt;. The \u0026lt;tt\u0026gt;HashMap\u0026lt;/tt\u0026gt; is created with * default load factor (0.75) and an initial capacity sufficient to * hold the mappings in the specified \u0026lt;tt\u0026gt;Map\u0026lt;/tt\u0026gt;. * * @param m the map whose mappings are to be placed in this map * @throws NullPointerException if the specified map is null */ public HashMap(Map\u0026lt;? extends K, ? extends V\u0026gt; m) { this.loadFactor = DEFAULT_LOAD_FACTOR; putMapEntries(m, false); } /** * Implements Map.putAll and Map constructor * * @param m the map * @param evict false when initially constructing this map, else * true (relayed to method afterNodeInsertion). * evict 初始化构建map时 为false 其他情况下为true */ final void putMapEntries(Map\u0026lt;? extends K, ? extends V\u0026gt; m, boolean evict) { int s = m.size(); if (s \u0026gt; 0) { //初次创建hashmap if (table == null) { // pre-size //计算m所需要的容量 float ft = ((float)s / loadFactor) + 1.0F; //获得真实的容量 int t = ((ft \u0026lt; (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY); //如果比默认的阈值大则计算该 t 对应的capacity if (t \u0026gt; threshold) threshold = tableSizeFor(t); } else if (s \u0026gt; threshold) // 如果是table不为null 即是后续往map中添加 如果s \u0026gt; 阈值就要重置map了 resize();//resize操作 后面介绍 //确定容量后put操作 for (Map.Entry\u0026lt;? extends K, ? extends V\u0026gt; e : m.entrySet()) { K key = e.getKey(); V value = e.getValue(); putVal(hash(key), key, value, false, evict);// } } } /*主要调用 putVal */ public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * put 操作 * Implements Map.put and related methods * * @param hash hash for key * @param key the key * @param value the value to put * @param onlyIfAbsent if true, don't change existing value 不存在才put\u0026lt;D-[\u0026gt; * @param evict if false, the table is in creation mode. * @return previous value, or null if none */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node\u0026lt;K,V\u0026gt;[] tab; Node\u0026lt;K,V\u0026gt; p; int n, i; //若是新建map的情况下 resize创建指定长度的table if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //取模计算该key对应的数组下标 并判断该坐标下的对象是否为null //为null时创建一个新node存入tab[i] if ((p = tab[i = (n - 1) \u0026amp; hash]) == null) tab[i] = newNode(hash, key, value, null); else {//tab[i] != null Node\u0026lt;K,V\u0026gt; e; K k; //如果p与存入的key完全相同 if (p.hash == hash \u0026amp;\u0026amp; ((k = p.key) == key || (key != null \u0026amp;\u0026amp; key.equals(k)))) e = p; else if (p instanceof TreeNode) //如果是红黑树节点 调用putTreeVal e = ((TreeNode\u0026lt;K,V\u0026gt;)p).putTreeVal(this, tab, hash, key, value); else { //普通的put //binCount记录了链表的长度 for (int binCount = 0; ; ++binCount) { //如果当前node的next==null说明就可以往该链上添加一个节点 if ((e = p.next) == null) { //新建node接到p.next下面 p.next = newNode(hash, key, value, null); //如果binCount大于设定的红黑树化阈值 if (binCount \u0026gt;= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash);//红黑树化 break; } //如果key与链表中的任意node完全相同break if (e.hash == hash \u0026amp;\u0026amp; ((k = e.key) == key || (key != null \u0026amp;\u0026amp; key.equals(k)))) break; p = e; } } //如果存在该key if (e != null) { // existing mapping for key V oldValue = e.value;//获得旧值 if (!onlyIfAbsent || oldValue == null)//若没有设置不存在才put或者oldValue=null e.value = value;//赋新值 afterNodeAccess(e);//LinkedHashMap操作 return oldValue;//返回旧值 } } ++modCount; if (++size \u0026gt; threshold)//是否需要扩容 resize(); afterNodeInsertion(evict);//LinkedHashMap操作 return null; } /** * 扩容操作 * 若是初始化则根据initialCapacity创建一个table * 否则,扩容为2的n次方倍 * Initializes or doubles table size. If null, allocates in * accord with initial capacity target held in field threshold. * Otherwise, because we are using power-of-two expansion, the * elements from each bin must either stay at same index, or move * with a power of two offset in the new table. * * @return the table */ final Node\u0026lt;K,V\u0026gt;[] resize() { Node\u0026lt;K,V\u0026gt;[] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; if (oldCap \u0026gt; 0) { //超过最大值不会再扩容了 if (oldCap \u0026gt;= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } else if ((newCap = oldCap \u0026lt;\u0026lt; 1) \u0026lt; MAXIMUM_CAPACITY \u0026amp;\u0026amp; oldCap \u0026gt;= DEFAULT_INITIAL_CAPACITY) newThr = oldThr \u0026lt;\u0026lt; 1; // double threshold 扩成两倍 } else if (oldThr \u0026gt; 0) // initial capacity was placed in threshold newCap = oldThr; else { // 默认配置 zero initial threshold signifies using defaults newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } if (newThr == 0) { //计算新的阈值 float ft = (float)newCap * loadFactor; newThr = (newCap \u0026lt; MAXIMUM_CAPACITY \u0026amp;\u0026amp; ft \u0026lt; (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({\u0026quot;rawtypes\u0026quot;,\u0026quot;unchecked\u0026quot;}) Node\u0026lt;K,V\u0026gt;[] newTab = (Node\u0026lt;K,V\u0026gt;[])new Node[newCap]; table = newTab; if (oldTab != null) { //把old buket 移到新的bukets里 for (int j = 0; j \u0026lt; oldCap; ++j) { Node\u0026lt;K,V\u0026gt; e; if ((e = oldTab[j]) != null) { oldTab[j] = null; if (e.next == null)//直接添加 newTab[e.hash \u0026amp; (newCap - 1)] = e; else if (e instanceof TreeNode) ((TreeNode\u0026lt;K,V\u0026gt;)e).split(this, newTab, j, oldCap); else { // preserve order Node\u0026lt;K,V\u0026gt; loHead = null, loTail = null; Node\u0026lt;K,V\u0026gt; hiHead = null, hiTail = null; Node\u0026lt;K,V\u0026gt; next; do { next = e.next; //这个取模很精辟 请结合美团的blog resize 1.8优化学习 //因为扩容是2倍扩容,二进制中相当于左移一位 /** * 假设一次扩容\t* 扩容前\toldCap = 00010000 oldCap - 1 = 00001111 * 扩容后\tnewCap = 00100000 newCap - 1 = 00011111 * 可以看出扩容后 newCap-1 在高位多了1 * 计算index时 hash \u0026amp; n-1 = 原位置 + oldCap * 所以只需要判断hash \u0026amp; oldCap是否为1 * 为1则把该node的位置移到 oldCap+原位置 * 为 0 还在原位置 */ if ((e.hash \u0026amp; oldCap) == 0) {//为0说明位置没有变 if (loTail == null)//第一次添加时loHead=e loHead = e; else loTail.next = e;//直接往后插入 loTail = e; } else {//为1 说明位置会+oldCap长度 if (hiTail == null) hiHead = e;//头节点初始化 else hiTail.next = e;//直接插入 hiTail = e; } } while ((e = next) != null); if (loTail != null) {//放在原位置上 loTail.next = null; newTab[j] = loHead; } if (hiTail != null) {//放在原位置+oldCap上 hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; } /** * get操作 * 为null时返回null 这个要注意下 */ public V get(Object key) { Node\u0026lt;K,V\u0026gt; e; return (e = getNode(hash(key), key)) == null ? null : e.value; } /** * Implements Map.get and related methods * get方法 * 主要是 key相等 或者 key equals的比较 * @param hash hash for key * @param key the key * @return the node, or null if none */ final Node\u0026lt;K,V\u0026gt; getNode(int hash, Object key) { Node\u0026lt;K,V\u0026gt;[] tab; Node\u0026lt;K,V\u0026gt; first, e; int n; K k; if ((tab = table) != null \u0026amp;\u0026amp; (n = tab.length) \u0026gt; 0 \u0026amp;\u0026amp; (first = tab[(n - 1) \u0026amp; hash]) != null) {//获得节点 if (first.hash == hash \u0026amp;\u0026amp; // always check first node ((k = first.key) == key || (key != null \u0026amp;\u0026amp; key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode)//树节点 return ((TreeNode\u0026lt;K,V\u0026gt;)first).getTreeNode(hash, key); do { if (e.hash == hash \u0026amp;\u0026amp; ((k = e.key) == key || (key != null \u0026amp;\u0026amp; key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; } 1.8红黑树化源码解析 /** * TreeNode extends LinkedHashMap.Entry * LinkedHashMap.Entry extends HashMap.Node */ static final class TreeNode\u0026lt;K,V\u0026gt; extends LinkedHashMap.Entry\u0026lt;K,V\u0026gt; { TreeNode\u0026lt;K,V\u0026gt; parent; // red-black tree links 红黑树父节点 TreeNode\u0026lt;K,V\u0026gt; left; TreeNode\u0026lt;K,V\u0026gt; right; TreeNode\u0026lt;K,V\u0026gt; prev; // needed to unlink next upon deletion 删除的时候用来连接前后 boolean red;//红还是黑 TreeNode(int hash, K key, V val, Node\u0026lt;K,V\u0026gt; next) { super(hash, key, val, next); } } /**树化 * putVal里有用到 * 将链表重置为红黑树并放到该hash映射的tab下,如果tab过下则resize * Replaces all linked nodes in bin at index for given hash unless * table is too small, in which case resizes instead. */ final void treeifyBin(Node\u0026lt;K,V\u0026gt;[] tab, int hash) { int n, index; Node\u0026lt;K,V\u0026gt; e; if (tab == null || (n = tab.length) \u0026lt; MIN_TREEIFY_CAPACITY)//小于最小树化的容量时不树化而resize capacity为64, resize(); else if ((e = tab[index = (n - 1) \u0026amp; hash]) != null) { TreeNode\u0026lt;K,V\u0026gt; hd = null, tl = null;//头尾节点 do { TreeNode\u0026lt;K,V\u0026gt; p = replacementTreeNode(e, null);//这个就是返回一个新建的TreeNode对象,内容为e if (tl == null)//确定是头结点 hd = p;//标记头结点 else {//非头结点就首尾连接 p.prev = tl; tl.next = p; } tl = p;//尾节点一直为p } while ((e = e.next) != null);//遍历链表 其实此时形成也还算是个链表 if ((tab[index] = hd) != null)//将该treeNode挂到table下 hd.treeify(tab);//完成红黑树化 } } /** * Forms tree of the nodes linked from this node. * @return root of tree */ final void treeify(Node\u0026lt;K,V\u0026gt;[] tab) { TreeNode\u0026lt;K,V\u0026gt; root = null; for (TreeNode\u0026lt;K,V\u0026gt; x = this, next; x != null; x = next) {//x 从当前节点开始(从treeifyBin里调用看是头结点) next = (TreeNode\u0026lt;K,V\u0026gt;)x.next;//获取下个节点 x.left = x.right = null; if (root == null) {//设置root节点并给他黑色 x.parent = null; x.red = false; root = x; } else { K k = x.key; int h = x.hash; Class\u0026lt;?\u0026gt; kc = null; //遍历所有节点与当前节点x比较 调整位置 有点像冒泡排序 for (TreeNode\u0026lt;K,V\u0026gt; p = root;;) { int dir, ph; K pk = p.key; //比较hash值 if ((ph = p.hash) \u0026gt; h) dir = -1; else if (ph \u0026lt; h) dir = 1; else if ((kc == null \u0026amp;\u0026amp; (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) dir = tieBreakOrder(k, pk); //根据dir判断x是p的左孩子 还是 右孩子 TreeNode\u0026lt;K,V\u0026gt; xp = p; if ((p = (dir \u0026lt;= 0) ? p.left : p.right) == null) { x.parent = xp; if (dir \u0026lt;= 0) xp.left = x; else xp.right = x; //平衡节点 root = balanceInsertion(root, x); break; } } } } moveRootToFront(tab, root); } /** * Returns a list of non-TreeNodes replacing those linked from * this node. */ final Node\u0026lt;K,V\u0026gt; untreeify(HashMap\u0026lt;K,V\u0026gt; map) { Node\u0026lt;K,V\u0026gt; hd = null, tl = null; for (Node\u0026lt;K,V\u0026gt; q = this; q != null; q = q.next) { Node\u0026lt;K,V\u0026gt; p = map.replacementNode(q, null); if (tl == null) hd = p; else tl.next = p; tl = p; } return hd; } /** * 红黑树版put操作 * Tree version of putVal. */ final TreeNode\u0026lt;K,V\u0026gt; putTreeVal(HashMap\u0026lt;K,V\u0026gt; map, Node\u0026lt;K,V\u0026gt;[] tab, int h, K k, V v) { Class\u0026lt;?\u0026gt; kc = null; boolean searched = false; TreeNode\u0026lt;K,V\u0026gt; root = (parent != null) ? root() : this;//每次从根节点遍历 for (TreeNode\u0026lt;K,V\u0026gt; p = root;;) { int dir, ph; K pk; if ((ph = p.hash) \u0026gt; h) dir = -1; else if (ph \u0026lt; h) dir = 1; else if ((pk = p.key) == k || (k != null \u0026amp;\u0026amp; k.equals(pk))) //如果当前节点key相同或equals 返回 return p; else if ((kc == null \u0026amp;\u0026amp; (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) { //hash值如果相等 但类不相同,只能挨个对比左右孩子 if (!searched) { TreeNode\u0026lt;K,V\u0026gt; q, ch; searched = true; if (((ch = p.left) != null \u0026amp;\u0026amp; (q = ch.find(h, k, kc)) != null) || ((ch = p.right) != null \u0026amp;\u0026amp; (q = ch.find(h, k, kc)) != null)) return q; } //哈希值相等 但键无法比较 只能通过其他方法比较 dir = tieBreakOrder(k, pk); } //得到两个节点的大小关系 即dir的值时 //并判断只有在左孩子或右孩子不能 TreeNode\u0026lt;K,V\u0026gt; xp = p; if ((p = (dir \u0026lt;= 0) ? p.left : p.right) == null) { Node\u0026lt;K,V\u0026gt; xpn = xp.next; TreeNode\u0026lt;K,V\u0026gt; x = map.newTreeNode(h, k, v, xpn); if (dir \u0026lt;= 0) xp.left = x; else xp.right = x; xp.next = x; x.parent = x.prev = xp; if (xpn != null) ((TreeNode\u0026lt;K,V\u0026gt;)xpn).prev = x; //平衡二叉树 moveRootToFront(tab, balanceInsertion(root, x)); return null; } } } /** 查找操作 传入 hash值 和 key值 * Calls find for root node. */ final TreeNode\u0026lt;K,V\u0026gt; getTreeNode(int h, Object k) { return ((parent != null) ? root() : this).find(h, k, null);//判断从当前节点还是root节点开始查找 } /** * Finds the node starting at root p with the given hash and key. * The kc argument caches comparableClassFor(key) upon first use * comparing keys. */ final TreeNode\u0026lt;K,V\u0026gt; find(int h, Object k, Class\u0026lt;?\u0026gt; kc) { TreeNode\u0026lt;K,V\u0026gt; p = this; do { int ph, dir; K pk; TreeNode\u0026lt;K,V\u0026gt; pl = p.left, pr = p.right, q; //根据hash值查找 当前节点hash值大于h则 查左孩子 否则右孩子 当key相等或者equal时返回 if ((ph = p.hash) \u0026gt; h) p = pl; else if (ph \u0026lt; h) p = pr; else if ((pk = p.key) == k || (k != null \u0026amp;\u0026amp; k.equals(pk))) return p; else if (pl == null) p = pr; else if (pr == null) p = pl; else if ((kc != null || (kc = comparableClassFor(k)) != null) \u0026amp;\u0026amp; (dir = compareComparables(kc, k, pk)) != 0) p = (dir \u0026lt; 0) ? pl : pr; else if ((q = pr.find(h, k, kc)) != null)//不相等则从子树继续查找 return q; else p = pl; } while (p != null); return null; } ","id":17,"section":"posts","summary":"","tags":["collections"],"title":"Collections-Hashmap","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/","year":"2019"},{"content":" HashSet详解\n 介绍 基于HashMap保存元素不保证有序,不包含重复元素,允许null值.主要继承关系如图:\ngraph BT HashSet--\u0026gt;AbstractSet HashSet-.-\u0026gt;Set AbstractSet-.-\u0026gt;Set AbstractSet--\u0026gt;AbstractCollection AbstractCollection-.-\u0026gt;Collection\t 参数 static final long serialVersionUID = -5024744406713321676L; //底层存储用的hashMap private transient HashMap\u0026lt;E,Object\u0026gt; map; //定义一个Object对象作为HashMap的value private static final Object PRESENT = new Object(); 常用方法 由于底层是hashmap存储的,所以基本是一样的.\u0008没什么区别.实现\u0008不重复插入是通过比较put操作的返回值是不是null\npublic Iterator\u0026lt;E\u0026gt; iterator() { return map.keySet().iterator(); } public int size() { return map.size(); } public boolean isEmpty() { return map.isEmpty(); } public boolean contains(Object o) { return map.containsKey(o); } public boolean add(E e) { return map.put(e, PRESENT)==null; } public boolean remove(Object o) { return map.remove(o)==PRESENT; } public void clear() { map.clear(); } @SuppressWarnings(\u0026quot;unchecked\u0026quot;) public Object clone() { try { HashSet\u0026lt;E\u0026gt; newSet = (HashSet\u0026lt;E\u0026gt;) super.clone(); newSet.map = (HashMap\u0026lt;E, Object\u0026gt;) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(e); } } ","id":18,"section":"posts","summary":"","tags":["collections"],"title":"Collections-Hashset","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/hashset/","year":"2019"},{"content":" 参考blog:田小波的blog\n 介绍 LinkedHashMap 继承自 HashMap,在 HashMap 基础上,通过维护一条双向链表,解决了 HashMap 不能随时保持遍历顺序和插入顺序一致的问题。除此之外,LinkedHashMap 对访问顺序也提供了相关支持。在一些场景下,该特性很有用,比如缓存。在实现上,LinkedHashMap 很多方法直接继承自 HashMap,仅为维护双向链表覆写了部分方法。还提供了一些增删改查操作时的回调方法.\n源码介绍 Entry LinkedHashMap的节点 LinkedHashMap.Entry继承了hashMap的Node 增加了before和after两个节点,用于维护链表有序\nstatic class Entry\u0026lt;K,V\u0026gt; extends HashMap.Node\u0026lt;K,V\u0026gt; { Entry\u0026lt;K,V\u0026gt; before, after; Entry(int hash, K key, V value, Node\u0026lt;K,V\u0026gt; next) { super(hash, key, value, next); } } 变量 /** * 头结点 * The head (eldest) of the doubly linked list. */ transient LinkedHashMap.Entry\u0026lt;K,V\u0026gt; head; /** * 尾节点 * The tail (youngest) of the doubly linked list. */ transient LinkedHashMap.Entry\u0026lt;K,V\u0026gt; tail; /* 设置链表排序规则: true 按照访问顺序排序 false按照插入顺序排序*/ final boolean accessOrder; 构造参数 LinkedHashMap基本都是复用的的hashmap的构造方法,只是对accessOrder初始化\npublic LinkedHashMap(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); accessOrder = false; } public LinkedHashMap(int initialCapacity) { super(initialCapacity); accessOrder = false; } public LinkedHashMap() { super(); accessOrder = false; } public LinkedHashMap(Map\u0026lt;? extends K, ? extends V\u0026gt; m) { super(); accessOrder = false; putMapEntries(m, false); } public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; } 基本操作 /* get操作:和hashmap逻辑一致,只是会判断是否根据访问顺序排序 */ public V get(Object key) { Node\u0026lt;K,V\u0026gt; e; if ((e = getNode(hash(key), key)) == null) return null; if (accessOrder) afterNodeAccess(e);//访问后的操作,将节点放到最后 return e.value; } /** * 将节点移到最后的逻辑很简单 * 将该节点的before,after节点相连,并将该节点挂到last上 * 如果该节点的before为头结点,则head=after 否则before.after = after * 如果该节点的after为尾节点,则last=before 否则 after.before = before * 最后再把该节点挂在尾节点上 */ void afterNodeAccess(Node\u0026lt;K,V\u0026gt; e) { // move node to last LinkedHashMap.Entry\u0026lt;K,V\u0026gt; last; if (accessOrder \u0026amp;\u0026amp; (last = tail) != e) {//e不为尾节点时 LinkedHashMap.Entry\u0026lt;K,V\u0026gt; p = (LinkedHashMap.Entry\u0026lt;K,V\u0026gt;)e, b = p.before, a = p.after;//获取前后节点 p.after = null; if (b == null)//如果b为头结点时 head = a; else b.after = a;//否则与a连接 if (a != null)//a不为尾节点时 a.before = b;//与b连接 else last = b; if (last == null)//说明链表是空的 head = p; else { p.before = last; last.after = p; } tail = p;//挂靠到tail上 ++modCount; } } /** * return true 删除链表中最久的entry * 一般重写来实现简单版的LRU缓存 */ protected boolean removeEldestEntry(Map.Entry\u0026lt;K,V\u0026gt; eldest) { return false; } /* 新建一个node 并插入到尾部 */ Node\u0026lt;K,V\u0026gt; newNode(int hash, K key, V value, Node\u0026lt;K,V\u0026gt; e) { LinkedHashMap.Entry\u0026lt;K,V\u0026gt; p = new LinkedHashMap.Entry\u0026lt;K,V\u0026gt;(hash, key, value, e); linkNodeLast(p); return p; } // link at the end of list private void linkNodeLast(LinkedHashMap.Entry\u0026lt;K,V\u0026gt; p) { LinkedHashMap.Entry\u0026lt;K,V\u0026gt; last = tail; tail = p; if (last == null)//链表为null时 head = p; else { p.before = last; last.after = p; } } ","id":19,"section":"posts","summary":"","tags":["collections"],"title":"Collections-Linkedhashmap","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/","year":"2019"},{"content":"实现了List和Deque接口的双向队列,允许插入null值\n主要属性 transient int size = 0;//大小 /** * Pointer to first node. * Invariant: (first == null \u0026amp;\u0026amp; last == null) || * (first.prev == null \u0026amp;\u0026amp; first.item != null) */ transient Node\u0026lt;E\u0026gt; first; //头结点 /** * Pointer to last node. * Invariant: (first == null \u0026amp;\u0026amp; last == null) || * (last.next == null \u0026amp;\u0026amp; last.item != null) */ transient Node\u0026lt;E\u0026gt; last; //尾节点 //Node节点长这样:item实体对象,prev/next指向前一个后一个节点 private static class Node\u0026lt;E\u0026gt; { E item; Node\u0026lt;E\u0026gt; next; Node\u0026lt;E\u0026gt; prev; Node(Node\u0026lt;E\u0026gt; prev, E element, Node\u0026lt;E\u0026gt; next) { this.item = element; this.next = next; this.prev = prev; } } 构造函数 主要介绍带参数的构造函数\n//c == null 时会抛空指针异常 public LinkedList(Collection\u0026lt;? extends E\u0026gt; c) { this(); addAll(c); } //最终会走到 addAll方法 public boolean addAll(int index, Collection\u0026lt;? extends E\u0026gt; c) { checkPositionIndex(index);//判断index是否非法 index\u0026lt;0 || index\u0026gt;size //集合转数组 Object[] a = c.toArray(); int numNew = a.length; if (numNew == 0) return false; Node\u0026lt;E\u0026gt; pred, succ; if (index == size) { //默认会从最后一个节点追加 succ = null; pred = last; } else { //自定义index后 会先取到该index对应的对象 succ = node(index);//调用node方法取得index位置下的node pred = succ.prev; } //遍历需要赋值的数组 for (Object o : a) { @SuppressWarnings(\u0026quot;unchecked\u0026quot;) E e = (E) o; Node\u0026lt;E\u0026gt; newNode = new Node\u0026lt;\u0026gt;(pred, e, null);//不要next节点是因为迭代时可以自行设置 if (pred == null)//说明是从头开始 first = newNode; else pred.next = newNode; pred = newNode; } //尾部node双向绑定 if (succ == null) {//不是指定index地方插入时,即从尾部插入,没有succ节点 last = pred; } else {//从指定index插入时,需要与尾部节点连接 pred.next = succ; succ.prev = pred; } size += numNew; modCount++; return true; } Node\u0026lt;E\u0026gt; node(int index) { // assert isElementIndex(index); //掰成两半儿查找 if (index \u0026lt; (size \u0026gt;\u0026gt; 1)) {//小于size的一半时从头开始查 Node\u0026lt;E\u0026gt; x = first; for (int i = 0; i \u0026lt; index; i++) x = x.next; return x; } else {//index大于size的一半时,从后往前找 Node\u0026lt;E\u0026gt; x = last; for (int i = size - 1; i \u0026gt; index; i--) x = x.prev; return x; } } 主要方法 1.get\npublic E get(int index) { checkElementIndex(index);//检查index界限,会抛下标越界异常 return node(index).item;//遍历取得指定index下的node } 2.set\npublic E set(int index, E element) { //检查下标 checkElementIndex(index); Node\u0026lt;E\u0026gt; x = node(index);//获取对应的node E oldVal = x.item;//取出旧item x.item = element;//赋值新item return oldVal; } add\npublic void add(int index, E element) { checkPositionIndex(index); if (index == size)//index等于size大小时在最后追加 linkLast(element); else linkBefore(element, node(index));//在该index直接添加 } //在尾部连接一个对象 void linkLast(E e) { final Node\u0026lt;E\u0026gt; l = last;//获得当前的最后一个节点 final Node\u0026lt;E\u0026gt; newNode = new Node\u0026lt;\u0026gt;(l, e, null);//新建一个节点,当前最后一个节点作为prev节点 last = newNode; if (l == null)//若前一个节点为null list还没有值 则头尾都用该节点 first = newNode; else l.next = newNode;//将上一个节点与新节点连接起来 size++;//链表长度+1 modCount++;//操作链表次数+1 } //官方文档上这么说:在一个非空节点前插入新节点 //但是其实没有做非空校验了 void linkBefore(E e, Node\u0026lt;E\u0026gt; succ) { // assert succ != null; 非空校验注释掉了 final Node\u0026lt;E\u0026gt; pred = succ.prev;//中间插入 那就是succ的prev节点要重新与新节点的prev连接,新节点的next节点为succ节点 final Node\u0026lt;E\u0026gt; newNode = new Node\u0026lt;\u0026gt;(pred, e, succ); succ.prev = newNode;//succ节点与新节点连接 if (pred == null)//上一个节点为空时则首尾节点都是该节点 first = newNode; else pred.next = newNode; size++;//链表长度+1 modCount++;//操作次数+1 } 4.remove 除了各种逻辑最后都会用到unlink方法:大致是将需要删除的对象的prev和next节点重新连接起来,在将该对象置空让gc回收\nE unlink(Node\u0026lt;E\u0026gt; x) { // assert x != null; final E element = x.item; final Node\u0026lt;E\u0026gt; next = x.next;//获取该节点下一个节点 final Node\u0026lt;E\u0026gt; prev = x.prev;//获取该节点上一个节点 if (prev == null) {//上一个节点为null时则删除的是头结点 将下一个节点变为头结点 first = next; } else { prev.next = next;//prev不为null时 将prev的next改为x的next x.prev = null;//将x的prev置空 让gc回收 } if (next == null) {//x的next为null时说明该节点是尾节点 last = prev; } else { next.prev = prev;//next的prev挂靠到x的prev x.next = null;//x的next置空回收 } x.item = null;//item置空回收 size--;//长度减1 modCount++;//操作次数+1 return element; } clear操作\n//清空所有的节点 public void clear() { // Clearing all of the links between nodes is \u0026quot;unnecessary\u0026quot;, but: // - helps a generational GC if the discarded nodes inhabit // more than one generation // - is sure to free memory even if there is a reachable Iterator for (Node\u0026lt;E\u0026gt; x = first; x != null; ) { Node\u0026lt;E\u0026gt; next = x.next; x.item = null; x.next = null; x.prev = null; x = next; } first = last = null; size = 0; modCount++; } 查询操作 1.indexOf 容易看懂就不解释了,查不到时返回-1,可以查null\npublic int indexOf(Object o) { int index = 0; if (o == null) { for (Node\u0026lt;E\u0026gt; x = first; x != null; x = x.next) { if (x.item == null) return index; index++; } } else { for (Node\u0026lt;E\u0026gt; x = first; x != null; x = x.next) { if (o.equals(x.item)) return index; index++; } } return -1; } 队列类的操作 peek\n//获取头节点 但不删除 可以为null public E peek() { final Node\u0026lt;E\u0026gt; f = first; return (f == null) ? null : f.item; } element 和peek一样也是获取头结点 但是若为null会抛空指针异常\n poll 就是队列里的pop操作,即出队\npublic E poll() { final Node\u0026lt;E\u0026gt; f = first; return (f == null) ? null : unlinkFirst(f); } 4.offer入队 队尾插入元素\npublic boolean offer(E e) { return add(e); } LinkedList中的迭代器 //该迭代器是快速失败的,如果在创建迭代器后操作了链表(add/remove),不是迭代器中的操作(add/remove),就会抛ConcurrentModificationException异常. //原因是迭代器中维护了expectedModCount每次操作前都会比较该值与modCount是否一致,不一致就抛,所以在迭代中增删节点时还是要通过迭代器的操作比较好 private class ListItr implements ListIterator\u0026lt;E\u0026gt; { private Node\u0026lt;E\u0026gt; lastReturned;//用作返回节点 private Node\u0026lt;E\u0026gt; next;//记录下一个节点 private int nextIndex;//下一个index private int expectedModCount = modCount;//初始化期望操作值 ListItr(int index) { // assert isPositionIndex(index); next = (index == size) ? null : node(index);//若index==size则是尾节点 nextIndex = index; } public boolean hasNext() { return nextIndex \u0026lt; size;//通过坐标值判断是否有下一个 } public E next() { checkForComodification();//校验操作避免使用list的add/remove操作 if (!hasNext()) throw new NoSuchElementException(); lastReturned = next; next = next.next; nextIndex++; return lastReturned.item; } public boolean hasPrevious() { return nextIndex \u0026gt; 0; } public E previous() { checkForComodification(); if (!hasPrevious()) throw new NoSuchElementException(); lastReturned = next = (next == null) ? last : next.prev; nextIndex--; return lastReturned.item; } public int nextIndex() { return nextIndex; } public int previousIndex() { return nextIndex - 1; } //迭代器的删除节点操作 public void remove() { checkForComodification();//校验操作次数 if (lastReturned == null)//状态校验 throw new IllegalStateException(); Node\u0026lt;E\u0026gt; lastNext = lastReturned.next;//获得当前节点的下一个节点 unlink(lastReturned);//断开当前节点,将当前节点的前后节点相连接 size会减1 modCount会+1 if (next == lastReturned) next = lastNext; else nextIndex--;//坐标值减1 lastReturned = null; expectedModCount++;//保持与modCount一致 } //设置当前的节点的item public void set(E e) { if (lastReturned == null) throw new IllegalStateException(); checkForComodification(); lastReturned.item = e; } //和普通add操作一样 主要是需要将nextIndex和expectedModCount都+1 public void add(E e) { checkForComodification(); lastReturned = null; if (next == null) linkLast(e); else linkBefore(e, next); nextIndex++; expectedModCount++; } //挺方便的迭代 重写下accept方法 可以用lambda表达式 public void forEachRemaining(Consumer\u0026lt;? super E\u0026gt; action) { Objects.requireNonNull(action); while (modCount == expectedModCount \u0026amp;\u0026amp; nextIndex \u0026lt; size) { action.accept(next.item); lastReturned = next; next = next.next; nextIndex++; } checkForComodification(); } //操作校验 final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } } ","id":20,"section":"posts","summary":"","tags":["collections"],"title":"Collections-Linkedlist","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/","year":"2019"},{"content":" 红黑树介绍 TreeMap解析\n简介 TreeMap底层基于红黑树实现,可以保证在log(n)时间复杂度下完成增删改查的操作,效率很高,由于基于红黑树,所以保持有序. TreeMap继承自AbstractMap,并实现了Nav\u0008igableMap(主要是提供一些\u0008导航类的操作,比如获得\u0008比当前节点小\u0008的最大值,比当前节点大的最小值等) key不允许为空 graph BT A(C:TreeMap) -.-\u0026gt; B(I:NavigableMap) A --\u0026gt; C(C:AbstractMap) B --\u0026gt; E(I:SortedMap) E --\u0026gt; D(I:Map) C -.-\u0026gt;D(I:Map) 基本操作 /* get 操作 可以返回null值 如果key为null 则会抛 NPE */ public V get(Object key) { Entry\u0026lt;K,V\u0026gt; p = getEntry(key); return (p==null ? null : p.value); } /* get操作实际调用的方法 key为null时 抛NPE */ final Entry\u0026lt;K,V\u0026gt; getEntry(Object key) { // Offload comparator-based version for sake of performance // 除非用comparator构建TreeMap,否则不使用它,为了性能考虑 if (comparator != null) return getEntryUsingComparator(key); if (key == null) throw new NullPointerException(); @SuppressWarnings(\u0026quot;unchecked\u0026quot;) Comparable\u0026lt;? super K\u0026gt; k = (Comparable\u0026lt;? super K\u0026gt;) key; Entry\u0026lt;K,V\u0026gt; p = root; while (p != null) { int cmp = k.compareTo(p.key);//compare key遍历二叉树 if (cmp \u0026lt; 0) p = p.left; else if (cmp \u0026gt; 0) p = p.right; else return p; } return null; } public V put(K key, V value) { //用t表示二叉树的当前节点 Entry\u0026lt;K,V\u0026gt; t = root; //t为null表示一个空树,即TreeMap中没有任何元素,直接插入 if (t == null) { //比较key值,个人觉得这句代码没有任何意义,空树还需要比较、排序? compare(key, key); // type (and possibly null) check //将新的key-value键值对创建为一个Entry节点,并将该节点赋予给root root = new Entry\u0026lt;\u0026gt;(key, value, null); //容器的size = 1,表示TreeMap集合中存在一个元素 size = 1; //修改次数 + 1 modCount++; return null; } int cmp; //cmp表示key排序的返回结果 Entry\u0026lt;K,V\u0026gt; parent; //父节点 // split comparator and comparable paths Comparator\u0026lt;? super K\u0026gt; cpr = comparator; //指定的排序算法 //如果cpr不为空,则采用既定的排序算法进行创建TreeMap集合 if (cpr != null) { do { parent = t; //parent指向上次循环后的t //比较新增节点的key和当前节点key的大小 cmp = cpr.compare(key, t.key); //cmp返回值小于0,表示新增节点的key小于当前节点的key,则以当前节点的左子节点作为新的当前节点 if (cmp \u0026lt; 0) t = t.left; //cmp返回值大于0,表示新增节点的key大于当前节点的key,则以当前节点的右子节点作为新的当前节点 else if (cmp \u0026gt; 0) t = t.right; //cmp返回值等于0,表示两个key值相等,则新值覆盖旧值,并返回新值 else return t.setValue(value); } while (t != null); } //如果cpr为空,则采用默认的排序算法进行创建TreeMap集合 else { if (key == null) //key值为空抛出异常 throw new NullPointerException(); /* 下面处理过程和上面一样 */ Comparable\u0026lt;? super K\u0026gt; k = (Comparable\u0026lt;? super K\u0026gt;) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp \u0026lt; 0) t = t.left; else if (cmp \u0026gt; 0) t = t.right; else return t.setValue(value); } while (t != null); } //将新增节点当做parent的子节点 Entry\u0026lt;K,V\u0026gt; e = new Entry\u0026lt;\u0026gt;(key, value, parent); //如果新增节点的key小于parent的key,则当做左子节点 if (cmp \u0026lt; 0) parent.left = e; //如果新增节点的key大于parent的key,则当做右子节点 else parent.right = e; /* * 上面已经完成了排序二叉树的的构建,将新增节点插入该树中的合适位置 * 下面fixAfterInsertion()方法就是对这棵树进行调整、平衡,具体过程参考上面的五种情况 */ fixAfterInsertion(e); //TreeMap元素数量 + 1 size++; //TreeMap容器修改次数 + 1 modCount++; return null; } /** * 上面代码中do{}代码块是实现排序二叉树的核心算法,通过该算法我们可以确认新增节点在该树的正确位置。 * 找到正确位置后将插入即可,这样做了其实还没有完成,因为我知道TreeMap的底层实现是红黑树,红黑树是一棵平衡排序二叉树, * 普通的排序二叉树可能会出现失衡的情况,所以下一步就是要进行调整。fixAfterInsertion(e); 调整的过程务必会涉及到红黑树的左 * 旋、右旋、着色三个基本操作 * 新增节点后的修复操作 * x 表示新增节点 */ private void fixAfterInsertion(Entry\u0026lt;K,V\u0026gt; x) { x.color = RED; //新增节点的颜色为红色 //循环 直到 x不是根节点,且x的父节点不为红色 while (x != null \u0026amp;\u0026amp; x != root \u0026amp;\u0026amp; x.parent.color == RED) { //如果X的父节点(P)是其父节点的父节点(G)的左节点 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //获取X的叔节点(U) Entry\u0026lt;K,V\u0026gt; y = rightOf(parentOf(parentOf(x))); //如果X的叔节点(U) 为红色(情况三) if (colorOf(y) == RED) { //将X的父节点(P)设置为黑色 setColor(parentOf(x), BLACK); //将X的叔节点(U)设置为黑色 setColor(y, BLACK); //将X的父节点的父节点(G)设置红色 setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } //如果X的叔节点(U为黑色);这里会存在两种情况(情况四、情况五) else { //如果X节点为其父节点(P)的右子树,则进行左旋转(情况四) if (x == rightOf(parentOf(x))) { //将X的父节点作为X x = parentOf(x); //右旋转 rotateLeft(x); } //(情况五) //将X的父节点(P)设置为黑色 setColor(parentOf(x), BLACK); //将X的父节点的父节点(G)设置红色 setColor(parentOf(parentOf(x)), RED); //以X的父节点的父节点(G)为中心右旋转 rotateRight(parentOf(parentOf(x))); } } //如果X的父节点(P)是其父节点的父节点(G)的右节点 else { //获取X的叔节点(U) Entry\u0026lt;K,V\u0026gt; y = leftOf(parentOf(parentOf(x))); //如果X的叔节点(U) 为红色(情况三) if (colorOf(y) == RED) { //将X的父节点(P)设置为黑色 setColor(parentOf(x), BLACK); //将X的叔节点(U)设置为黑色 setColor(y, BLACK); //将X的父节点的父节点(G)设置红色 setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } //如果X的叔节点(U为黑色);这里会存在两种情况(情况四、情况五) else { //如果X节点为其父节点(P)的右子树,则进行左旋转(情况四) if (x == leftOf(parentOf(x))) { //将X的父节点作为X x = parentOf(x); //右旋转 rotateRight(x); } //(情况五) //将X的父节点(P)设置为黑色 setColor(parentOf(x), BLACK); //将X的父节点的父节点(G)设置红色 setColor(parentOf(parentOf(x)), RED); //以X的父节点的父节点(G)为中心右旋转 rotateLeft(parentOf(parentOf(x))); } } } //将根节点G强制设置为黑色 root.color = BLACK; } ","id":21,"section":"posts","summary":"","tags":["collections"],"title":"Collections-Treemap","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/treemap/","year":"2019"},{"content":"如同HashSet基于HashMap一样,\u001bTreeSet基于TreeMap,TreeMap是一棵有序的红黑树,那么\u0008TreeSet也如此.提供有序的set集合,不允许重复插入 继承关系如下:\ngraph BT A(TreeSet) -.-\u0026gt; B(NavigableSet) A --\u0026gt; C(AbstractSet) C--\u0026gt;D(AbstractCollection) D-.-\u0026gt;E(Collection) 参数 private transient NavigableMap\u0026lt;E,Object\u0026gt; m; //PRESENT会被当做Map的value与key构建成键值对 private static final Object PRESENT = new Object(); 构造方法 //创建TreeSet的基础构成 map TreeSet(NavigableMap\u0026lt;E,Object\u0026gt; m) { this.m = m; } //按照自然排序构建 public TreeSet() { this(new TreeMap\u0026lt;E,Object\u0026gt;()); } //按照自定义排序构建 public TreeSet(Comparator\u0026lt;? super E\u0026gt; comparator) { this(new TreeMap\u0026lt;\u0026gt;(comparator)); } //按照自然排序构建 并\u0008添加入参集合的元素 public TreeSet(Collection\u0026lt;? extends E\u0026gt; c) { this(); addAll(c); } //根据已有的TreeSet构建\u0008一个新的TreeSet public TreeSet(SortedSet\u0026lt;E\u0026gt; s) { this(s.comparator()); addAll(s); } 主要方法 /** * 将集合中所有的\u0008元素添加到TreeMap中 * 如果集合为空,\u0008或者任一元素为null并且使用的是自然排序,或者 * comparator不允许为空元素则会抛NPE */ public boolean addAll(Collection\u0026lt;? extends E\u0026gt; c) { // Use linear-time version if applicable if (m.size()==0 \u0026amp;\u0026amp; c.size() \u0026gt; 0 \u0026amp;\u0026amp; c instanceof SortedSet \u0026amp;\u0026amp; m instanceof TreeMap) { SortedSet\u0026lt;? extends E\u0026gt; set = (SortedSet\u0026lt;? extends E\u0026gt;) c; TreeMap\u0026lt;E,Object\u0026gt; map = (TreeMap\u0026lt;E, Object\u0026gt;) m; Comparator\u0026lt;?\u0026gt; cc = set.comparator(); Comparator\u0026lt;? super E\u0026gt; mc = map.comparator(); if (cc==mc || (cc != null \u0026amp;\u0026amp; cc.equals(mc))) { map.addAllForTreeSet(set, PRESENT); return true; } } return super.addAll(c); } /* add操作 会去重 \u0008put\u0008返回值为null时说明成功 */ public boolean add(E e) { return m.put(e, PRESENT)==null; } /* 获取并移除第一个元素 如果set为空 则返回null */ public E pollFirst() { Map.Entry\u0026lt;E,?\u0026gt; e = m.pollFirstEntry(); return (e == null) ? null : e.getKey(); } /* 获取并移除最后一个元素 如果set为空 则返回null */ public E pollLast() { Map.Entry\u0026lt;E,?\u0026gt; e = m.pollFirstEntry(); return (e == null) ? null : e.getKey(); } /** * 返回此 set 的部分视图,其元素大于(或等于,如果 inclusive 为 true)fromElement。 */ public NavigableSet\u0026lt;E\u0026gt; tailSet(E fromElement, boolean inclusive) { return new TreeSet\u0026lt;\u0026gt;(m.tailMap(fromElement, inclusive)); } /** * 返回此 set 的部分视图,其元素大于等于 fromElement。 */ public SortedSet\u0026lt;E\u0026gt; tailSet(E fromElement) { return tailSet(fromElement, true); } /** * 返回此 set 的部分视图,其元素范围从 fromElement 到 toElement。 */ public NavigableSet\u0026lt;E\u0026gt; subSet(E fromElement, boolean fromInclusive, E toElement, boolean toInclusive) { return new TreeSet\u0026lt;\u0026gt;(m.subMap(fromElement, fromInclusive, toElement, toInclusive)); } /** * 返回此 set 的部分视图,其元素从 fromElement(包括)到 toElement(不包括)。 */ public SortedSet\u0026lt;E\u0026gt; subSet(E fromElement, E toElement) { return subSet(fromElement, true, toElement, false); } ","id":22,"section":"posts","summary":"","tags":["collections"],"title":"Collections-Treeset","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/treeset/","year":"2019"}],"tags":[{"title":"collections","uri":"https://xiaohei.im/hugo-theme-pure/tags/collections/"},{"title":"hugo","uri":"https://xiaohei.im/hugo-theme-pure/tags/hugo/"},{"title":"hystrix","uri":"https://xiaohei.im/hugo-theme-pure/tags/hystrix/"},{"title":"leetcode","uri":"https://xiaohei.im/hugo-theme-pure/tags/leetcode/"},{"title":"rabbitmq","uri":"https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/"},{"title":"redis","uri":"https://xiaohei.im/hugo-theme-pure/tags/redis/"},{"title":"rust","uri":"https://xiaohei.im/hugo-theme-pure/tags/rust/"},{"title":"rxjava","uri":"https://xiaohei.im/hugo-theme-pure/tags/rxjava/"},{"title":"分布式锁","uri":"https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/"},{"title":"响应式编程","uri":"https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/"},{"title":"数据结构","uri":"https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"}]} \ No newline at end of file
+{"categories":[{"title":"CoreJava","uri":"https://xiaohei.im/hugo-theme-pure/categories/corejava/"},{"title":"Hystrix","uri":"https://xiaohei.im/hugo-theme-pure/categories/hystrix/"},{"title":"leetcode","uri":"https://xiaohei.im/hugo-theme-pure/categories/leetcode/"},{"title":"redis","uri":"https://xiaohei.im/hugo-theme-pure/categories/redis/"},{"title":"消息队列","uri":"https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/"}],"posts":[{"content":"之前多redis 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 https://redis.io/topics/replication\n复制是什么? 分布式系统有一个重要的点时保证数据不丢失,数据不丢失就意味着不能单点,不能单点就意味着最好能把数据多存几份形成数据的冗余.这就是复制的来由.复制类型主要是两种: 同步, 异步. 前者需要等待所有的节点返回写入确认,后者只需要返回个确认收到就行.\nRedis 主从复制 主从复制作用 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。 高可用基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础。 Redis 复制设计要点 默认使用异步复制 \u0026ndash; replica -\u0026gt; master 异步返回处理了多少数据的结果(偏移量)\n master 可以多 replicas\n replicas 可以从其他 replica 同步(从从复制).类似于级联更新的架构.\n master 主节点在同步时不会阻塞. 在 replica 侧复制也是非阻塞的.在进行初始化同步(全量)时,可以使用 replica上的旧数据供客户端查询.也可以在 redis.conf 进行配置,在初始化同步完成前客户端的请求都报错.初始化同步完成后,需要删除老数据,加载新数据.在这段时间中会阻塞外部连接请求,数据量大的话可能要很久.从 4.0 版本后,删除老数据可以通过多线程来优化效率,但是加载新数据还是会 阻塞. 复制可以用来弹性扩容,提供多可读副本,提升数据安全性,保证高可用 副本可以避免 master 保存全量数据到磁盘的资源消耗:可以由 replica 完成持久化,或者开启 aof 写入.不过需要慎重: 这样会导致 master 节点再重启时会是空的,其他 replica复制时也就成空的了. 主从复制过程 每一个 master 节点都会有一个特别大的随机数(40字节十六进制随机字符)作为 replication ID 来标识自己.每个 master 节点也有一个 持续递增的 offset 来记录发送给 replicas 的每一个 byte,利用该 offset 来保证副本更新的状态.\n当一个 replica 连接到 master 时,会使用 PSYNC 命令发送之前复制的 master 的 replicationID,以及自己的更新进度(offset).master 可以根据这个值给副本按需返回为更新的数据.如果在master 的 backlog buffer 中没有对应的数据可以给到,副本发送的 replicationID 与 master 的 ID 不一致,就会触发全量复制(Full Synchronization).\nbacklog buffer 是啥? 复制积压缓冲区,在 master 有 replica 进行复制时,存储 master 最近一段时间的写命令,以便在 replica 断开重连后,可以利用缓冲区更新断开这段时间中,从节点丢掉的更新.\nbacklog buffer 是有固定的长度,先进先出的队列,默认大小 1MB. 其实就是一个环.buffer 会存储每一个 offset 已经对应的写命令,这样 replica 在断连恢复后,发送 PSYNC 命令提供其最后一次更新的 offset, master 就可以根据 replica 提供的 offset 去 buffer 中找对应的数据发送给 replica 保持最新.\n如果断开时间过长,buffer 存储的数据已经换了一批又一批, replica 在重连后发送给 master 的 offset 在 buffer 已经找不到了.此时会触发 全量复制.\n全量复制 master调用 bgsave 在后台生成 rdb 文件.同时记录客户端新的写命令到 backlog buffer 中. rdb 文件生成后,发送给 replica 保存到其硬盘中,然后再加载到内存中并通知master 加载完成.然后 master 会发送 buffer pool 中的命令给 replica 完成最后的同步.\nSYNC/PSYNC 两者都是同步的命令.SYNC 只支持全量同步, PSYNC 支持上述的部分同步.2.8 版本之前只有 SYNC,为了避免每次都只能全量同步造成资源的浪费,就新增了 PSYNC 命令实现部分同步的语义.\nReplication ID Replication ID 标记了数据的历史信息,从0开始成为master 的节点,或者晋升成为 master 的 replica 节点,都会生成一个 Replication ID.replicas 的 replId 是和其复制的 master 一致的,master 通过该 ID 和 offset 来判断主从之间数据是否一致.\n为什么有两个replId? /* src/server.h */ struct redisServer { ... /* Replication (master) */ char replid[CONFIG_RUN_ID_SIZE+1]; /* My current replication ID. */ char replid2[CONFIG_RUN_ID_SIZE+1]; /* replid inherited from master*/ ... long long master_repl_offset; /* My current replication offset */ long long second_replid_offset; /* Accept offsets up to this for replid2. */ ... } 一般情况下,故障转移(failover)后,晋升的 replica 需要记录自己之前复制的 master 对应的 replId.其他 replicas 会向新 master 进行部分同步,但发送过来的 replId 还是之前 master 的.所以 replica 在晋升时,会生成新的replId,并将原来的 replId 记录到 replId2,同时记录下当时所更新到的 offset 到 second_replid_offset.当其他的 replica 向新 master 进行连接时,新 master 会比较当前的和之前 master 的 replId,offset,这样就可以防止在故障转移后导致不必要的 全量复制.\n为什么晋升后需要生成新 replId? old master 可能还存活,但由于网络分区原因无法和其他 replicas 通信,如果保留原来的 id 不再生成,就会导致有相同数据相同id的master 存在.\n无盘复制 全量复制时,master 会创建 rdb 文件存到磁盘,然后再读取 rdb 文件发送给 replicas.磁盘性能差的情况下,效率会很低,所以支持了 无盘复制 \u0026ndash; 子进程直接发送 rdb 给 replicas,不经过硬盘存储.\n如何处理可以过期的键? 副本不会主动去过期键,而是由 master 过期键后向副本发送 DEL 命令. 由于是通过 master 驱动,副本收到 DEL 命令可能有延迟,这就会导致从副本中还可能查到已过期的键.针对这种情况,副本会利用自身的物理时钟作为依据报告该键不存在(仅在不违反数据一致性的 只读操作),因为 DEL 命令总是会发过来的. LUA 脚本执行期间,是不会去执行 key 过期的.脚本执行期间相当于 master 时间冻结了,不作过期时间的记录,所以在这期间过期键只有存在或不存在的概念.这样可以防止键在执行期间过期.同时,master 也需要发送同样的脚本给副本,保持一致. 如果replica 晋升 master 了,它就会自己去处理键的过期了.\n心跳机制 在正常的进行 部分同步 期间,主从之间会维持心跳,来协助超时判断,数据安全等问题.\nmaster -\u0026gt; slave 主节点发送 PING ,从节点回复 PONG.目的是让从节点进行超时判断.发送频率有 repl-ping-slave-period 参数控制.单位秒,默认 10s.\nreplica -\u0026gt; master 从节点向主节点发送 REPLCONF ACK {offset} ,频率每秒1次.作用:\n 试试检测主从网络状态,该命令被主节点用于复制超时的判断. 检测命令丢失,主节点会比较从节点发送的 offset 与自身的是否一致,不一致则从 buffer 中查找对应数据进行补发,如果 buffer 中没有对应数据,则会进行全量复制. 辅助保证从节点的数量和延迟,master 通过 min-salves-to-write 和 min-slaves-max-lag 参数,来保证主节点在不安全情况下不会执行写命令.是指从节点数量太少,或延迟过高。例如 min-slaves-to-write 和min-slaves-max-lag 分别是3和10,含义是如果从节点数量小于3个,或所有从节点的延迟值都大于10s,则主节点拒绝执行写命令。 复制惨痛案例 数据过期问题 数据删除没有及时同步到从节点,其实在 3.2 版本后避免了这个问题.从节点会对键进行判断,已过期不展示.\n如何处理可以过期的键?\n数据延迟不一致 这种情况不可避免.可能的优化措施包括:优化主从节点之间的网络环境(如在同机房部署);监控主从节点延迟通过offset判断,如果从节点延迟过大,通知应用不再通过该从节点读取数据;使用集群同时扩展写负载和读负载等。\n复制超时导致复制中断 为什么要判断超时? master 在判断超时后,会释放从节点的连接,释放资源. 断开后即时重连 判断机制? 核心参数: repl-timeout ,默认 60s.\n(1)主节点:每秒1次调用复制定时函数replicationCron(),在其中判断当前时间距离上次收到各个从节点 REPLCONF ACK 的时间,是否超过了 repl-timeout 值,如果超过了则释放相应从节点的连接。\n(2)从节点:从节点对超时的判断同样是在复制定时函数中判断,基本逻辑是:\n 如果当前处于连接建立阶段,且距离上次收到主节点的信息的时间已超过 repl-timeout,则释放与主节点的连接; 如果当前处于数据同步阶段,且收到主节点的 RDB 文件的时间超时,则停止数据同步,释放连接; 如果当前处于命令传播阶段,且距离上次收到主节点的 PING 命令或数据的时间已超过repl-timeout值,则释放与主节点的连接。 问题 全量复制时,如果 RDB 文件过大,耗时很长就会触发超时,此时从节点会重连,再生成RDB,再超时,在生成RDB\u0026hellip;解决方案就是单机数据量尽量不要太大,增大 repl-timeout. 慢查询导致服务器阻塞: keys *,hgetall backlog 过小导致无限全量复制 backlog buffer 是固定大小的,写入命令超出长度就会覆盖.如果再全量复制的时候用时超长,存入buffer 的命令超过了其大小限制,那么就会导致连接中断,再重连,全量复制,连接中断,全量复制\u0026hellip;.死循环.解决方案就是需要正确设置 backlog buffer 的大小. 通过 client-output-buffer-limit slave {hard limit} {soft limit} {soft seconds} 配置,默认值为 client-output-buffer-limit slave 256MB 64MB 60,其含义是:如果 buffer 大于256MB,或者连续 60s 大于 64MB ,则主节点会断开与该从节点的连接。该参数是可以通过 config set 命令动态配置的(即不重启Redis也可以生效).\n参考 深入学习Redis(3):主从复制\n 「Redis 设计与实现」\n https://redis.io/topics/replication\n ","id":0,"section":"posts","summary":"\u003cp\u003e之前多\u003ccode\u003eredis\u003c/code\u003e 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 \u003ca href=\"https://redis.io/topics/replication\"\u003ehttps://redis.io/topics/replication\u003c/a\u003e\u003c/p\u003e","tags":["redis"],"title":"Redis-复制功能探索","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/replication/","year":"2019"},{"content":" 事件驱动程序设计(英语:Event-driven programming)是一种电脑程序设计模型。这种模型的程序运行流程是由用户的动作(如鼠标的按键,键盘的按键动作)或者是由其他程序的消息来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由程序员来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在交互程序(Interactive program)的情况下孕育而生的。 \u0026ndash;wikipedia\n 文件事件 服务端通过套接字与客户端进行连接,文件事件就是服务端对套接字操作的抽象.服务端与客户端的通信会产生多种文件事件(连接 accept ,读取 read, 写入 write ,关闭 close),服务器监听并处理相应的事件.\n文件事件处理器 redis 基于 Reactor 模式实现了网络事件处理 \u0026ndash;\u0026gt; 文件时间处理器.通过 I/O 多路复用 保证了单进程下的高性能网络模型.\n什么是 I/O Multiplexing? 参考: https://draveness.me/redis-io-multiplexing\n首先需要知道什么是文件描述符(File Descriptor ,简称 FD)? 文件描述符就是操作系统中操作文件时内核返回的一个 非负整数,可以通过文件描述符来指定待读写的文件.而套接字 socket 本质上也是一种文件描述符.\n简单来说就是通常我们使用的 I/O 模型是阻塞型的,服务器在处理一个客户端请求(即处理一个FD)时无法再处理其它的了. I/O多路复用 是通过利用操作系统的多路复用函数(select())来监听多个 FD 的可读可写情况,一旦有可读或可写的 FD,select() 就返回对应的个数.\n由于不同操作系统的有不同的多路复用函数,select是性能最差的.而 redis 也会根据操作系统的不同选择性能最好的函数来使用.并且由于不同平台的差异, redis 提供了一套相同的结构并针对不同平台进行了实现,以此屏蔽了对上层应用的影响.\n#ifdef HAVE_EVPORT #include \u0026quot;ae_evport.c\u0026quot; #else #ifdef HAVE_EPOLL #include \u0026quot;ae_epoll.c\u0026quot; #else #ifdef HAVE_KQUEUE #include \u0026quot;ae_kqueue.c\u0026quot; #else #include \u0026quot;ae_select.c\u0026quot; #endif #endif #endif 文件事件处理器结构 每一个套接字 socket 可以执行连接,读写,关闭操作时,会产生一个 文件事件.,I/O 多路复用 监听这些 FD 的操作请求,并向 文件事件派发器 传递产生文件事件的 FD. 虽然会并发的产生 N 个文件事件,但 I/O多路复用 会将其都放入一个队列中,顺序且同步地向 文件事件分派器 传送.处理完一个再传下一个.\n文件事件派发器 接收到 FD 后,就会根据FD 所绑定的文件事件类型选择相应的事件处理器进行处理.\n文件事件类型 AE_READABLE 可读事件 客户端对套接字 write 操作, close 操作或者客户端与服务端进行连接(出现 acceptable 套接字)时产生可读事件\n AE_WRITABLE 可写事件 客户端对套接字执行 read 操作,套接字产生可写事件\n AE_NONE 无任何事件 事件处理的先后顺序 AE_READABLE \u0026gt; AE_WRITABLE\n事件处理器 事件处理器是针对不同的文件事件实现的逻辑.客户端连接时,服务器需要进行应答,此时服务器就会将套接字关联到应答处理器.接收客户端的命令请求,服务器会将套接字关联到命令请求处理器.\n常用时间处理器 连接应答处理器 networking.c/acceptTcpHandler 客户端连接时会对其进应答.redis 在初始化时会将服务器的监听套接字的可读事件与该处理器关联起来,客户端只要连接监听套接字就会产生可读事件,执行对应的逻辑.\n 命令请求处理器 networking.c/readQueryFromClient 客户端连接服务器后,服务器会将客户端套接字的可读事件与命令请求处理器关联起来,当客户端向服务器发送命令请求时,产生可读事件,执行对应逻辑.\n 命令回复处理器 networking.c/sendReplyToClient 服务器有命令回复需要传送给客户端时,服务器会将客户端套接字的可写事件与命令回复处理器关联起来,客户端准备好接收服务器回复时,会产生可写事件,触发命令回复器执行.服务器发送完毕时,会解除关联.\n文件事件处理流程 aeCreateFileEvent 可以将一个给定FD 的给定事件加入到多路复用的监听范围中,并将事件与时间处理器关联\naeDeleteFileEvent 取消给定FD 的给定事件的监听\naeApiPoll 该方法会在每个平台的多路复用中进行实现,阻塞等待所有监听的FD 所产生的事件并返回可用时间的数量.会有超时处理.\n时间事件 Redis 中有两种时间事件 \u0026mdash;- 定时事件(隔一段时间执行一次),非定时事件(某个时间点执行一次)\n属性 id 全局唯一ID,顺序递增 when 毫秒精度 UNIX 时间戳,记录时间事件到达时间 timeProc 时间事件处理器,需要执行时间事件时,根据该处理器执行 时间事件是定时还是非定时,取决去 timeProc 返回值是否等于 AE_NOMORE. 等于则给事件ID标记为待删除,不等于则更新执行时间到下一次.\nretval = te-\u0026gt;timeProc(eventLoop, id, te-\u0026gt;clientData); if (retval != AE_NOMORE) { aeAddMillisecondsToNow(retval,\u0026amp;te-\u0026gt;when_sec,\u0026amp;te-\u0026gt;when_ms); } else { te-\u0026gt;id = AE_DELETED_EVENT_ID; } Redis 处理时间事件时,不会在当前循环中直接移除不再需要执行的事件,而是会在当前循环中将时间事件的 id 设置为 AE_DELETED_EVENT_ID,然后再下一个循环中删除,并执行绑定的 finalizerProc。\n/* Remove events scheduled for deletion. */ if (te-\u0026gt;id == AE_DELETED_EVENT_ID) { aeTimeEvent *next = te-\u0026gt;next; if (te-\u0026gt;prev) te-\u0026gt;prev-\u0026gt;next = te-\u0026gt;next; else eventLoop-\u0026gt;timeEventHead = te-\u0026gt;next; if (te-\u0026gt;next) te-\u0026gt;next-\u0026gt;prev = te-\u0026gt;prev; if (te-\u0026gt;finalizerProc) te-\u0026gt;finalizerProc(eventLoop, te-\u0026gt;clientData); zfree(te); te = next; continue; }\t 时钟问题 时间事件的执行影响最大的因素就是 系统时间. 系统时间的调整会影响时间事件的执行,所以在eventLoop 中有个 lastTime 属性来检测系统时间.如果发现系统时间改变了,比上次执行时间事件的时间小,就会强制尽早执行.\n时间事件执行流程 事件循环 Event Loop 上述的 文件事件, 时间事件 是从何时开始? 在 事件循环 中开始. 事件循环 是 redis 在启动后初始化完服务配置,就会陷入一个巨大的循环 aeEventLoop 中. 这个巨大的循环从 aeMain() 开始.\nvoid aeMain(aeEventLoop *eventLoop) { eventLoop-\u0026gt;stop = 0; while (!eventLoop-\u0026gt;stop) { if (eventLoop-\u0026gt;beforesleep != NULL) eventLoop-\u0026gt;beforesleep(eventLoop); aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP); } } 源码中可以看出来,除非给 eventLoop-\u0026gt;stop 设置为 true ,程序会一直跑,一直执行 aeProcessEvents.\naeEventLoop aeEventLoop 保存着事件循环的上下文信息,并有三个重要的数组:保存监听的文件事件 aeFileEvent , 时间事件 aeTimeEvent, 待处理文件事件 aeFiredEvent.\naeProcessEvent 在一般情况下,aeProcessEvents 都会先计算最近的时间事件发生所需要等待的时间,然后调用 aeApiPoll 方法在这段时间中等待事件的发生,在这段时间中如果发生了文件事件,就会优先处理文件事件,否则就会一直等待,直到最近的时间事件需要触发.\nint aeProcessEvents(aeEventLoop *eventLoop, int flags) { int processed = 0, numevents; if (!(flags \u0026amp; AE_TIME_EVENTS) \u0026amp;\u0026amp; !(flags \u0026amp; AE_FILE_EVENTS)) return 0; if (eventLoop-\u0026gt;maxfd != -1 || ((flags \u0026amp; AE_TIME_EVENTS) \u0026amp;\u0026amp; !(flags \u0026amp; AE_DONT_WAIT))) { struct timeval *tvp; #1:计算 I/O 多路复用的等待时间 tvp numevents = aeApiPoll(eventLoop, tvp); for (int j = 0; j \u0026lt; numevents; j++) { aeFileEvent *fe = \u0026amp;eventLoop-\u0026gt;events[eventLoop-\u0026gt;fired[j].fd]; int mask = eventLoop-\u0026gt;fired[j].mask; int fd = eventLoop-\u0026gt;fired[j].fd; int rfired = 0; if (fe-\u0026gt;mask \u0026amp; mask \u0026amp; AE_READABLE) { rfired = 1; fe-\u0026gt;rfileProc(eventLoop,fd,fe-\u0026gt;clientData,mask); } if (fe-\u0026gt;mask \u0026amp; mask \u0026amp; AE_WRITABLE) { if (!rfired || fe-\u0026gt;wfileProc != fe-\u0026gt;rfileProc) fe-\u0026gt;wfileProc(eventLoop,fd,fe-\u0026gt;clientData,mask); } processed++; } } if (flags \u0026amp; AE_TIME_EVENTS) processed += processTimeEvents(eventLoop); return processed; } 参考 https://draveness.me/redis-eventloop https://draveness.me/redis-io-multiplexing Redis设计与实现 ","id":1,"section":"posts","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003cstrong\u003e事件驱动程序设计\u003c/strong\u003e(英语:\u003cstrong\u003eEvent-driven programming\u003c/strong\u003e)是一种电脑\u003ca href=\"https://zh.wikipedia.org/wiki/程式設計\"\u003e程序设计\u003c/a\u003e\u003ca href=\"https://zh.wikipedia.org/wiki/模型\"\u003e模型\u003c/a\u003e。这种模型的程序运行流程是由用户的动作(如\u003ca href=\"https://zh.wikipedia.org/wiki/滑鼠\"\u003e鼠标\u003c/a\u003e的按键,键盘的按键动作)或者是由其他程序的\u003ca href=\"https://zh.wikipedia.org/wiki/訊息\"\u003e消息\u003c/a\u003e来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由\u003ca href=\"https://zh.wikipedia.org/wiki/程式設計師\"\u003e程序员\u003c/a\u003e来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在\u003ca href=\"https://zh.wikipedia.org/w/index.php?title=互動程序\u0026amp;action=edit\u0026amp;redlink=1\"\u003e交互程序\u003c/a\u003e(Interactive program)的情况下孕育而生的。 \u003ca href=\"https://zh.wikipedia.org/wiki/事件驅動程式設計\"\u003e\u0026ndash;wikipedia\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e","tags":["redis"],"title":"Redis-事件","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/event/","year":"2019"},{"content":"RDB 和 AOF 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 写命令 保证数据库的状态.\n什么是 AOF ? AOF 持久化通过保存服务器执行的写命令实现,进行恢复时通过重放 AOF 文件中的写命令,来保证数据安全.就像 mysql 的 binlog 一样.\n开启 AOF 通过在 redis.conf 中将 appendonly 设为 yes 即可\n# redis.conf appendonly yes # 设置 aof 文件名字 appendfilename \u0026quot;appendonly.aof\u0026quot; # Redis支持三种不同的刷写模式: # appendfsync always #每次收到写命令就立即强制写入磁盘,是最有保证的完全的持久化,但速度也是最慢的,一般不推荐使用。 appendfsync everysec #每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,是受推荐的方式。 # appendfsync no #完全依赖OS的写入,一般为30秒左右一次,性能最好但是持久化最没有保证,不被推荐。 AOF 文件格式 AOF 文件格式以 redis 命令请求协议为标准的,*.aof 文件可以直接打开.\nAOF 持久化过程 命令追加 append redis 执行完客户端的写命令后,会将该命令以协议的格式写入到 aof_buf 中.该属性为 redisServer 中的一个.\n#src/server.h struct redisServer { .... sds aof_buf; /* AOF buffer, written before entering the event loop */ } AOF 写入同步 redis 的服务进程是一个 事件循环 - event loop , 每次循环大概会做三件事.\n 文件事件: 接收客户端的命令,返回结果 时间事件: 执行系统的定时任务(serverCron), 完成渐进 rehash 扩容之类的操作 aof flush: 是否将 aof_buf 中的内容写入文件中\n# 伪代码 def eventloop(): while true: processFileEvents() # 处理命令 processTimeEvents() # 处理定时任务 flushAppendOnlyFile() # 处理 aof 写入 flushAppendOnlyFile 中的动作是否执行是根据一个配置决定的.\nappendfsync 该配置有几个值可选,默认是 everysec.\n always: 总是写入.只要程序执行到这一步了,就将 aof_buf 中命令协议写入到文件 everysec: 每秒写入. 每次执行前会先判断是否与上次写入间隔一秒,再次同步时通过 一个线程 专门执行 no: 不写入. 命令写入 aof_buf 后由操作系统决定何时同步到文件 fsync: 现代操作系统为了提高文件读写的效率,通常会将 write 函数写入的数据缓存在内存中,等到缓存空间填满或者超过一定时限,再将其写入磁盘.这样的问题在于宕机时缓存中的数据就无法恢复.所以操作系统提供了 fsync/fdatasync 两个函数,强制操作系统将数据立即写入磁盘,保证数据安全.两函数区别在于: 前者会更新文件的属性,后者只更新数据.\n 三种模式在性能和数据上都有相对的优缺点. always 模式数据安全性更强,毕竟每次都是直接写入,但是就会影响性能.磁盘读写是比较慢的. everysec 模式性能较好,但会丢失一秒内的缓存数据. no 模式就完全取决于操作系统了.\nAOF 还原数据 AOF 重写 AOF 重写的意思其实就是对单个命令的多个操作进行整理,留下最终态的执行命令来减少 aof 文件的大小.你可以想象一下执行 1w 次 incr 操作,写入 aof 1w 次的场景.\n触发条件 AOF 重写可以自动触发.通过配置 auto-aof-rewrite-min-size 和auto-aof-rewrite-percentage,满足条件就会自动重写.具体可以查看官方的 redis.conf\n重写过程 创建子进程,根据内存里的数据重写aof,保存到temp文件 此时主进程还会接收命令,会将写操作追加到旧的aof文件中,并保存在server.aof_rewrite_buf_blocks中,通过管道发送给子进程存在server.aof_child_diff中,最后追加到temp文件结尾 子进程重写完成后退出,主进程根据子进程退出状态,判断成功与否。成功就将剩余的server.aof_rewrite_buf_blocks追加到temp file中,然后rename()覆盖原aof文件 重写的过程中主进程还是会一直接受客户端的命令,所以重写子进程与主进程肯定会存在数据不一致的情况.redis针对这种情况作出了解决方案: 新增一个 aof_rewrite_buf_blocks, aof 写入命令时,不仅写入到 aof_buf, 如果正在重写,那么也写入到 aof_rewrite_buf_blocks 中,这样在子进程重写完毕后,可以将 aof_rewrite_buf_blocks 的命令追加到新文件中,保证数据不丢失.\nrename 操作是原子的,也是唯一会造成主进程阻塞的操作.\n参考 https://redis.io/topics/persistence https://youjiali1995.github.io/redis/persistence/ ","id":2,"section":"posts","summary":"\u003cp\u003e\u003ccode\u003eRDB\u003c/code\u003e 和 \u003ccode\u003eAOF\u003c/code\u003e 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 \u003cstrong\u003e写命令\u003c/strong\u003e 保证数据库的状态.\u003c/p\u003e","tags":["redis"],"title":"Redis-AOF持久化","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/aof/","year":"2019"},{"content":"redis 为内存数据库,一旦服务器进程退出,服务器中的数据就不见了.所以内存中的数据需要持久化的硬盘中来保证可以在必要的时候进行故障恢复. RDB 就是 redis 提供的一种持久化方式.\n 官方关于持久化的文章: https://redis.io/topics/persistence\n 什么是 RDB? RDB 是 redis 提供的一种持久化方式,可以手动执行,也可以通过定时任务定期执行,可以将某个时间节点的数据库状态保存到一个 RDB 文件中,叫做 dump.rdb.如果开启了压缩算法( LZF )的支持,则可以利用算法减少文件大小.服务器意外宕机或者断电后重启都可以通过该文件来恢复数据库状态.\n如何执行? 有两个命令可以生成 RDB 文件.\n SAVE: 执行时进程阻塞,无法处理其他命令 BGSAVE: 新建一个子进程来后台生成 RDB 文件 具体实现逻辑在: src/rdb.c/rdbSave(),从官方文档可知,该实现是基于 cow 的.\n https://redis.io/topics/persistence\nThis method allows Redis to benefit from copy-on-write semantics.\n 如何载入? RDB 文件会在 redis 启动时自动载入.\n由于 AOF 持久化的实时性更好,所以如果同时开启了 AOF , RDB 两种持久化,会优先使用 AOF 来恢复.\nBGSAVE 执行时的状态 BGSAVE 执行期间会拒绝 SAVE/BGSAVE 的命令,避免产生 竞争条件.\nBGSAVE 执行期间 BGREWRITEAOF 命令会延迟到 BGSAVE 执行完之后执行.\nBGREWRITEAOF 在执行时, BGSAVE 命令会被拒绝.\nBGSAVE 和 BGREWRITEAOF 命令的权衡完全是性能方面的考虑.毕竟都会有大量的磁盘写入,影响性能.\n定时执行BGSAVE BGSAVE 不会阻塞服务器进程,所以 redis 允许用户通过配置, 定时执行 BGSAVE 命令.\n快照策略 Snapshotting 可以通过设置 N 秒内至少 M 次修改来触发一次 BGSAVE.\nsave 60 1000 # 60s内有至少1000次修改时 bgsave 一次 默认的保存条件 save 900 1 save 300 10 save 60 10000 dirty 计数器 \u0026amp; lastsave 属性 redis 中维护了一个计数器,来记录距离上一次 SAVE/BGSAVE 后服务器对所有数据库进行了多少次增删改,叫做 dirty计数器.属于 redisServer 结构体的属性之一.\nlastsave 是记录了上一次成功执行 SAVE/BGSAVE 的 UNIX时间戳 , 同样是 redisServer 结构体的属性之一.\n# src/server.h struct redisServer { ... long long dirty; /* Changes to DB from the last save */ time_t lastsave; /* Unix time of last successful save */ ... } 定时执行过程 redis 有一个定时任务 serverCron , 每隔 100ms 就会执行一次,用于维护服务器.该任务就会检查 save 设置的保存条件是否满足,满足则执行 BGSAVE\n满足条件逻辑 遍历设置的 save 参数, 计算当前时间到 lastsave 的间隔 interval , 如果 dirty \u0026gt; save.change \u0026amp; interval \u0026gt; save.seconds 那么就执行保存\nRDB 文件结构 https://github.com/sripathikrishnan/redis-rdb-tools/wiki/Redis-RDB-Dump-File-Format\n写下这篇文章时参考版本为 2019.09.05 更新的版本\n RDB 文件格式对读写进行了很多优化,这类优化导致其格式与内存中存在的形式极其相似,同时利用 LZF 压缩算法来优化文件的大小.一般来讲, redis 对象都会提前标记自身的大小,所以备份RDB 在读取这些 object 时,可以提前知道要分配多少内存.\n解析RDB结构 下面的代码展示的是 16 进制下 RDB 文件的结构,便于理解\n----------------------------# RDB is a binary format. There are no new lines or spaces in the file. 52 45 44 49 53 # 魔数 REDIS的16进制表示,代表这是个RDB文件 30 30 30 37 # 4位ascii码表示当前RDB版本号,这里表示\u0026quot;0007\u0026quot; = 7 ---------------------------- FE 00 # FE 表明这是数据库选择标记. 00 表示选中0号数据库 ----------------------------# Key-Value pair starts FD $unsigned int # FD 是秒级过期时间的标记. 紧接着是 4 byte unsigned int 过期时间 $value-type # 1 byte 标记 value 类型 - set, map, sorted set etc. $string-encoded-key # 经过编码后的键 $encoded-value # 值,编码格式取决去 $value-type ---------------------------- FC $unsigned long # FC 表明是毫秒级过期时间. 过期时间值是 8 bytes的 unsigned long,是一个unit时间戳 $value-type # 同上秒级时间 $string-encoded-key # 同上秒级时间 $encoded-value # 同上秒级时间 ---------------------------- $value-type # 这一栏是没有过期时间的key-value $string-encoded-key $encoded-value ---------------------------- FE $length-encoding # 前一个数据库的编码完成,选择新的数据库进行处理.数据库编号会根据 length-encoding 格式获得 ---------------------------- ... # Key value pairs for this database, additonal database FF ## 表明 RDB 文件结束了 8 byte checksum ## 8byte CRC 64 校验码 value type 1 byte 表示了 value 的类型.\n type(以下为十进制表示) 编码类型 0 String 1 List 2 Set 3 Sorted Set 4 Hash 9 Zipmap 10 Ziplist 11 Intset 12 Sorted Set in Ziplist 13 HashMap in Ziplist 键值编码格式 键(key)都是字符串,所以使用string 编码格式.\n值(value)就会有不同的区分:\n 如果 value type 为 0 ,会是简单的字符串. 如果 value type 为 9,10,11,12, 值会被包装为 string, 在读到该字符串后,会进一步解析. 如果 value type 为 1,2,3,4, 值会是一个字符串数组. Length Encoding 长度编码是用来存储对象的长度的.是一种可变字节码,旨在使用尽可能少的字节.\n如何工作? 从流中读取 1byte,得到高两位. 如果是 00 开头, 那么剩下 6 位表示长度 如果是 01 开头, 会再从流中读取 1byte,合起来总共 14 位作为长度. 如果是 10 开头, 会直接丢弃剩下的 6 位.再从流中读取 4bytes作为长度. 如果是 11 开头, 说明这个对象是一种特殊编码格式. 剩下的 6 位表示了它的格式类型.这个编码通常用来将数字存储为字符串,或者存储被编码过得字符串(String Encoding). 编码结果是? 从上述可得,可能的编码格式是这样的:\n 1 byte 最多存储到 63 2 bytes 最多存储到 16383 5 bytes 最多存储到 2^32 - 1 String Encoding redis 的字符串是二进制安全的,所以可以存储 anything. 没有任何字符串结尾的标记.最好将 redis 字符串视为一个字节数组.\n有三种类型的字符串:\n 长度编码字符串 这是最简单的一种,字符串的长度会利用 Length Encoding 编码作为前缀,后面跟着字符串的编码\n 数字作为字符串 这里就将上面 Length Encoding 的特殊编码格式联系起来了,数字作为字符串时以 11 开头,剩下的 6 位表示不同的数字类型\n 0 表示接下来是一个 8 位数字 1 表示接下来是一个 16 位数字 2 表示接下来是一个 32 位数字 压缩字符串 压缩字符串的 Length Encoding 还是以 11 开头的, 但是剩下的6 位二进制的值为 4, 表明后面读取到的是一个压缩字符串.压缩字符串会存储压缩前和压缩后的长度.解析规则如下:\n 根据 Length Encoding 读取压缩的长度 clen 根据 Length Encoding 读取未压缩的长度 从流中读取 clen bytes 的数据 利用 LZF 算法进行解析 分析RDB文件 利用 od 命令来分析来看看 rdb 文件长什么样子.我将 redis 数据库清空后,执行了 set msg hello,所以现在只有一个键 msg, 值为 hello.下面的命令第一行输出的是 16 进制,下面一行输出的是对应的 ascii. 下面进行解析~\n➜ od -A x -t x1c -v dump.rdb 0000000 52 45 44 49 53 30 30 30 39 fa 09 72 65 64 69 73 R E D I S 0 0 0 9 372 \\t r e d i s 0000010 2d 76 65 72 05 35 2e 30 2e 34 fa 0a 72 65 64 69 - v e r 005 5 . 0 . 4 372 \\n r e d i 0000020 73 2d 62 69 74 73 c0 40 fa 05 63 74 69 6d 65 c2 s - b i t s 300 @ 372 005 c t i m e 051 0000030 29 e8 c3 5d fa 08 75 73 65 64 2d 6d 65 6d c2 d0 ) 350 303 ] 372 \\b u s e d - m e m 302 007 0000040 07 10 00 fa 0c 61 6f 66 2d 70 72 65 61 6d 62 6c \\a 020 \\0 372 \\f a o f - p r e a m b l 0000050 65 c0 00 fe 00 fb 01 00 00 03 6d 73 67 05 68 65 e 300 \\0 376 \\0 373 001 \\0 \\0 003 m s g 005 h e 0000060 6c 6c 6f ff fc 0e 6b 79 fe 47 1a 36 l l o 377 374 016 k y 376 G 032 6 000006c 魔数和版本号 前 5 个字节就是我们看到的 REDIS,以及后四个字节对应的版本号9\n辅助字段 Aux Fields 这是 Version 7 之后加入的字段, Redis设计与实现 所使用的版本是没有这个,所以一开始有点懵~ 只能看代码了.\n# src/rdb.c /* 该函数负责执行 RDB 文件的写入 */ int rdbSave(char *filename, rdbSaveInfo *rsi) { //伪代码 1. 创建一个临时文件 temp-$pid.rdb,并处理创建失败的逻辑 2. 新建一个redis封装的I/O流 3. 写入rdb文件 rdbSaveRio() 4. 将文件重命名, 默认重命名为 dump.rdb 5. 更新服务器的一些状态: dirty计数器置0,更新lastsave等 } 然后我们来看下写入的 Aux Fields, 在函数 rdbSaveRio 中\nint rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) { // 忽略所有只看重点 if (server.rdb_checksum) rdb-\u0026gt;update_cksum = rioGenericUpdateChecksum; // 生成校验码 snprintf(magic,sizeof(magic),\u0026quot;REDIS%04d\u0026quot;,RDB_VERSION); // 生成魔数及版本号 if (rdbWriteRaw(rdb,magic,9) == -1) goto werr; // 写入魔数及版本号 if (rdbSaveInfoAuxFields(rdb,flags,rsi) == -1) goto werr; // 写入 AuxFileds } /* Save a few default AUX fields with information about the RDB generated. */ int rdbSaveInfoAuxFields(rio *rdb, int flags, rdbSaveInfo *rsi) { int redis_bits = (sizeof(void*) == 8) ? 64 : 32; int aof_preamble = (flags \u0026amp; RDB_SAVE_AOF_PREAMBLE) != 0; /* Add a few fields about the state when the RDB was created. */ if (rdbSaveAuxFieldStrStr(rdb,\u0026quot;redis-ver\u0026quot;,REDIS_VERSION) == -1) return -1; if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;redis-bits\u0026quot;,redis_bits) == -1) return -1; if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;ctime\u0026quot;,time(NULL)) == -1) return -1; if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;used-mem\u0026quot;,zmalloc_used_memory()) == -1) return -1; /* Handle saving options that generate aux fields. */ if (rsi) { if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;repl-stream-db\u0026quot;,rsi-\u0026gt;repl_stream_db) == -1) return -1; if (rdbSaveAuxFieldStrStr(rdb,\u0026quot;repl-id\u0026quot;,server.replid) == -1) return -1; if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;repl-offset\u0026quot;,server.master_repl_offset) == -1) return -1; } if (rdbSaveAuxFieldStrInt(rdb,\u0026quot;aof-preamble\u0026quot;,aof_preamble) == -1) return -1; return 1; } 以上可以看出会写入这些字段.\n redis-ver:版本号\n redis-bits:OS 操作系统位数 32\u0026frasl;64\n ctime:RDB文件创建时间\n used-mem:使用内存大小\n repl-stream-db:在server.master客户端中选择的数据库\n repl-id:当前实例 replication ID\n repl-offset:当前实例复制的偏移量\n 每一个属性写入前都会写入 0XFA, 标记这是一个辅助字段.在上面命令行输出中,ascii 展示为 372\n数据库相关标记 0000050 65 c0 00 fe 00 fb 01 00 00 03 6d 73 67 05 68 65 e 300 \\0 376 \\0 373 001 \\0 \\0 003 m s g 005 h e 这一行中的 0XFE 表示选择数据库,后面紧接着 00 即为,选择 0 号数据库. 0XFB 是标记了当前数据库中键存储的数量,这里用到了 Length Encoding, 01 是我们存储的字典中key-value的数量,00 是过期字典(expires)中的数量.\n redisDB中有两个属性, dict 记录了我们写入的所有键, expires 存储了我们设置有过期时间的键以及其过期时间.\n Key Value 结构 我们设置了 msg -\u0026gt; hello,在输出中是这样的.\n0000050 65 c0 00 fe 00 fb 01 00 00 03 6d 73 67 05 68 65 e 300 \\0 376 \\0 373 001 \\0 \\0 003 m s g 005 h e 在 msg 前面的字段 \\0 003, 表示他是 string 类型, 且长度为 3, 005 hello, 表示是长度为 5 的 hello.\n还有其他数据结构这里就不做展示了.\n结束符 \u0026amp; 校验码 0000060 6c 6c 6f ff fc 0e 6b 79 fe 47 1a 36 l l o 377 374 016 k y 376 G 032 6 最后一行输出中 0xff , 文件结束符, 剩下的八个字节就是 CRC64\n参考 https://cloud.tencent.com/developer/article/1179710\n Redis5.0 RDB文件解析\n ","id":3,"section":"posts","summary":"\u003cp\u003e\u003ccode\u003eredis\u003c/code\u003e 为内存数据库,一旦服务器进程退出,服务器中的数据就不见了.所以内存中的数据需要持久化的硬盘中来保证可以在必要的时候进行故障恢复. \u003ccode\u003eRDB\u003c/code\u003e 就是 \u003ccode\u003eredis\u003c/code\u003e 提供的一种持久化方式.\u003c/p\u003e","tags":["redis"],"title":"Redis-RDB持久化","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/rdb/","year":"2019"},{"content":"服务器中的数据库 redis的数据库是保存在一个db数组中的,默认会新建16个数组.\n# src/server.h struct redisServer { ... redisDb *db; // db 存放的数组 int dbnum; /* 根据该属性决定创建数据库数量 默认: 16 */ ... } 切换数据库 redis 数据库从 0 开始计算,通过 select 命令切换数据库. client 会有一个属性指向当前选中的 DB.\n# src/server.h typedef struct client { ... redisDb *db; /* 指向当前选中的redisDb */ ... } 键空间 redisDb 的结构是怎样的呢?\n# src/server.h /* Redis database representation. There are multiple databases identified * by integers from 0 (the default database) up to the max configured * database. The database number is the 'id' field in the structure. */ typedef struct redisDb { dict *dict; /* 键空间 */ dict *expires; /* Timeout of keys with a timeout set */ dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/ dict *ready_keys; /* Blocked keys that received a PUSH */ dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */ int id; /* Database ID */ long long avg_ttl; /* Average TTL, just for stats */ list *defrag_later; /* List of key names to attempt to defrag one by one, gradually. */ } redisDb; 键空间 指的是每一个数据库中存放用户设置键和值的地方. 可以看到上述结构中, dict 属性就是每一个数据库的键空间, 字典结构, 也就是我们命令的执行结构.例如 set msg \u0026quot;hello world~\u0026quot; .\n所以针对数据库的操作就是操作字典.\n读写键空间后的操作 维护 hit, miss 次数, 可以利用 info stats 查看 keyspace_hits 以及 keyspace_misses 读取一个键后会更新键的 LRU ,用于计算键的闲置时间 object idletime {key} 查看 服务器读取一个键后发现已经过期,则会删除这个键在执行其他操作 如果客户端 watch 了某个键, 该键修改之后,会被标记为 dirty, 从而事务程序可以注意到该键已经被修改了 服务器每修改一个键后, 都会对 dirty 计数器 +1 ,这个计数器会触发服务器的持久化和复制操作 服务器开启数据库通知之后,键修改后会发送相应的数据库通知 过期时间保存 上述的 redisDb 结构中有 expires 的字典, redis 就是将我们设置的过期时间存到了这个字典中.键就是数据库键,值是一个 long long 类型的整数, 保存了键的过期时间: 一个毫秒精度的 UNI\u0010X 时间戳.\nRedis的过期键删除策略 有这么三种删除方式.\n定时删除 设置键过期时间的同时,创建一个定时器,到期自动删除\n优点 内存友好,键过期就删除\n缺点 对 CPU 不友好,过期键较多时,会占用较长时间,CPU 资源紧张的情况下会影响服务器的响应时间和吞吐量 创建定时器需要用到 redis 的时间事件,实现方式为无序链表,查找效率低 惰性删除 无视键是否过期,每次从键空间取键时,先判断是否过期,过期就删除,没过期就返回.\n优点 对 CPU 友好,遇到过期键才删除\n缺点 如果过期键很多,且一直不会被访问,就会导致大量内存被浪费\n定期删除 定期的在数据库中检查,删除过期的键.定期删除策略是上面两种策略的折中方案.\n优点 每隔一段时间删除过期键,可以减少删除操作对 CPU 的影响 定期删除也可以减少过期键带来的内存浪费 难点 确定删除操作执行的时长和频率\nredis采用方案 惰性删除 + 定期删除\n惰性删除是在所有读写数据库命令执行之前检查键是否过期来实现的.\n定期删除是通过 redis 的定时任务执行.在规定的时间内,多次遍历服务器的各个数据库,从 expires 字典中 随机抽查 一部分键的过期时间.current_db 会记录当前函数检查的进度,并在下一次函数执行时,接着上次的执行.循环往复地执行.\nAOF,RDB \u0026amp; 复制功能对过期键的处理 生成 RDB 文件时,过期键不会被保存到新文件中 载入 RDB 文件 以主服务器运行:未过期的键被载入,过期键忽略 以从服务器运行:保存所有键,无论是否过期.由于主从服务器在进行数据同步时,从服务器数据库就会被清空,所以一般来讲,也不会造成什么影响. AOF 写入时,键过期还没有被删除,AOF 文件不会受到影响,当键被惰性删除或被定期删除后,AOF 文件会追加一条 DEL 命令来显示记录该键已被删除 AOF 重写时,会对键过期进行确认,过期补充些. 复制模式下,从服务器的过期键删除由主服务器控制. 主服务器删除一个键后,会显示发送 DEL 命令给从服务器. 从服务器接收读命令时,如果键已过期,也不会将其删除,正常处理 从服务器只在主服务器发送 DEL 命令才删除键 主从复制不及时怎么办?会有脏读现象~\n数据库通知 通过订阅的模式,可以实时获取键的变化,命令的执行情况.通过 redis 的 pub/sub 模式来实现.命令对数据库进行了操作后,就会触发该通知,置于能不能发送出去完全看你的配置了.\nnotify_keyspace_events 系统配置决定了服务器发送的配置类型.如果给定的 type 不是服务器允许发送的类型,程序就直接返回了.然后就判断能发送键通知就发送,能发送命令通知就发送.\n/* The API provided to the rest of the Redis core is a simple function: * * notifyKeyspaceEvent(char *event, robj *key, int dbid); * * 'event' is a C string representing the event name. * 'key' is a Redis object representing the key name. * 'dbid' is the database ID where the key lives. */ void notifyKeyspaceEvent(int type, char *event, robj *key, int dbid) { sds chan; robj *chanobj, *eventobj; int len = -1; char buf[24]; /* If any modules are interested in events, notify the module system now. * This bypasses the notifications configuration, but the module engine * will only call event subscribers if the event type matches the types * they are interested in. */ moduleNotifyKeyspaceEvent(type, event, key, dbid); /* If notifications for this class of events are off, return ASAP. */ if (!(server.notify_keyspace_events \u0026amp; type)) return; eventobj = createStringObject(event,strlen(event)); /* __keyspace@\u0026lt;db\u0026gt;__:\u0026lt;key\u0026gt; \u0026lt;event\u0026gt; notifications. */ if (server.notify_keyspace_events \u0026amp; NOTIFY_KEYSPACE) { chan = sdsnewlen(\u0026quot;__keyspace@\u0026quot;,11); len = ll2string(buf,sizeof(buf),dbid); chan = sdscatlen(chan, buf, len); chan = sdscatlen(chan, \u0026quot;__:\u0026quot;, 3); chan = sdscatsds(chan, key-\u0026gt;ptr); chanobj = createObject(OBJ_STRING, chan); pubsubPublishMessage(chanobj, eventobj); decrRefCount(chanobj); } /* __keyevent@\u0026lt;db\u0026gt;__:\u0026lt;event\u0026gt; \u0026lt;key\u0026gt; notifications. */ if (server.notify_keyspace_events \u0026amp; NOTIFY_KEYEVENT) { chan = sdsnewlen(\u0026quot;__keyevent@\u0026quot;,11); if (len == -1) len = ll2string(buf,sizeof(buf),dbid); chan = sdscatlen(chan, buf, len); chan = sdscatlen(chan, \u0026quot;__:\u0026quot;, 3); chan = sdscatsds(chan, eventobj-\u0026gt;ptr); chanobj = createObject(OBJ_STRING, chan); pubsubPublishMessage(chanobj, key); decrRefCount(chanobj); } decrRefCount(eventobj); } ","id":4,"section":"posts","summary":"","tags":["redis"],"title":"Redis-数据库长什么样?","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/db/","year":"2019"},{"content":"Redis有很多种数据结构,但其并没有直接使用这些数据结构来构建这个 NOSQL, 而是通过 对象系统 完成了对所有数据结构的统一管理, 实现内存回收, 对象共享等特性~\n类型及编码 在 Redis 中使用任何命令操作,都是操作的一个对象.有键对象,值对象.\nset msg \u0026quot;hello~\u0026quot; # msg 为键对象, \u0026quot;hello~\u0026quot; 为值对象 每个对象都会有如下的结构:\ntypedef struct redisObject { unsigned type:4; // 类型 unsigned encoding:4; // 编码 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; // 引用计数 void *ptr; // 指向底层实现数据结构的指针 } robj; type 类型 type 指明了该对象的类型. redis 中类型有如下几种\n/* The actual Redis Object */ #define OBJ_STRING 0 /* String object. */ #define OBJ_LIST 1 /* List object. */ #define OBJ_SET 2 /* Set object. */ #define OBJ_ZSET 3 /* Sorted set object. */ #define OBJ_HASH 4 /* Hash object. */ #define OBJ_MODULE 5 /* Module object. */ #define OBJ_STREAM 6 /* Stream object. */ redis 中键都为字符串对象,利用 type 命令可以查看值对象的类型\nreids\u0026gt; type language list encoding 编码 encoding 属性记录了该对象使用的什么数据结构存储底层的实现,即 *ptr 所指向的那个数据结构.以下是目前的编码类型.\n/* Objects encoding. Some kind of objects like Strings and Hashes can be * internally represented in multiple ways. The 'encoding' field of the object * is set to one of this fields for this object. */ #define OBJ_ENCODING_RAW 0 /* Raw representation */ #define OBJ_ENCODING_INT 1 /* Encoded as integer */ #define OBJ_ENCODING_HT 2 /* Encoded as hash table */ #define OBJ_ENCODING_ZIPMAP 3 /* Encoded as zipmap */ #define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */ #define OBJ_ENCODING_INTSET 6 /* Encoded as intset */ #define OBJ_ENCODING_SKIPLIST 7 /* Encoded as skiplist */ #define OBJ_ENCODING_EMBSTR 8 /* Embedded sds string encoding */ #define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */ #define OBJ_ENCODING_STREAM 10 /* Encoded as a radix tree of listpacks */ 基本上每种类型的对象都会对应两种编码类型,可以动态的根据用户输入的值提供最有的数据结构,减少资源消耗.\n字符串对象 字符串对象有三种编码格式. int,embstr,raw,不同长度不同格式有不一样的编码类型.\n47.100.254.74:6379\u0026gt; set msg \u0026quot;abcdefg\u0026quot; OK (0.53s) 47.100.254.74:6379\u0026gt; object encoding msg \u0026quot;embstr\u0026quot; 47.100.254.74:6379\u0026gt; set msg \u0026quot;abcdefghijklmnopqrstuvwxyz01234567890123456789\u0026quot; OK 47.100.254.74:6379\u0026gt; object encoding msg \u0026quot;raw\u0026quot; 47.100.254.74:6379\u0026gt; set msg 123 OK 47.100.254.74:6379\u0026gt; object encoding msg \u0026quot;int\u0026quot; embstr vs raw 一个字符串对象包括 redisObject 和 sds 两部分组成.正常情况下是需要分配两次内存来创建这两个结构.这也是raw 的格式,但是如果当 value 长度较短时, (由于 redis 使用的是 jemalloc 分配内存)我们可以将内存分配控制在一次,将 RedisObject 和 sds 分配在连续的内存空间,这也就是 embstr 编码格式了.那多短算短呢?\n在此之前先了解下创建一个 redisObject 时所占用的空间.\nembstr编码是由 代表着 字符串的数据结构是 SDS.假设为 sdshdr8\nstruct sdshdr8 { uint8_t len; /* 1byte used */ uint8_t alloc; /* 1byte excluding the header and null terminator */ unsigned char flags; /* 1byte 3 lsb of type, 5 unused bits */ char buf[]; }; jemalloc 可以分配 8/16/32/64 字节大小的内存,从上可以发现最少的内存需要占用 19 字节, Redis 在总体大于 64 字节时,会改为 raw 存储. 所以 embstr 形式时最大长度是 64 - 19 - 结束符\\0长度 = 44\n编码转换 由于 redis 没有为 embstr 编写修改相关的程序,所以是只读的, 如果对其执行任何修改命令,就会变为 raw 格式.\n类型检查 redis 中的操作命令一般有两种: 所有类型都能用的(DEL, EXPIRE\u0026hellip;), 特定类型适用的(各种数据类型对应的命令).若操作键的命令不对, redis 会提示报错.\n47.100.254.74:6379\u0026gt; set numbers 1 OK 47.100.254.74:6379\u0026gt; object encoding numbers \u0026quot;int\u0026quot; 47.100.254.74:6379\u0026gt; rpush numbers a (error) WRONGTYPE Operation against a key holding the wrong kind of value 如何实现? 利用 RedisObject 的type 来控制.在输入一个命令时, 服务器会先检查输入键所对应的的值对象是否为命令对应的类型,是的话就执行,不是就报错.\n多态命令 同一种数据结构可能有多种编码格式.比如字符串对象的编码格式可能有 int, embstr, raw.所以当命令执行前,还需要根据值对象的编码来选择正确的命令来实现.\n比如想要执行 llen 获取 list 长度, 如果编码为 ziplist, 那么程序就会使用 ziplist 对应的函数来计算, 编码为 quicklist 时则是使用 quicklist 对应的函数来计算. 此为命令的 多态 .\n内存回收 redis 利用引用计数来实现内存回收机制.由 RedisObject 中的 refcount 属性记录.\n引用计数是有导致循环引用的弊端的,那么redis为啥还是会用的?找了很久也没有找到答案.\n有一个说法是: 引用的复杂度很低,不太容易导致循环引用.就一切从简呗.\n对象共享 对象共享指的是创建一次对象后,后面如果还有客户端需要创建同样的值对象则直接把现在这个的引用只给他,引用计数加1,可以节省内存的开销.类似 Java 常量池. 所以refcount 也被用来做对象共享的.\nredis 在初始化服务器时, 会创建 0 - 9999 一万个整数字符串, 为了节省资源.\n为什么不共享其他的复杂对象? 整数复用几率很大 整数比较算法时间复杂度是 O(1), 字符串是 O(N), hash/list 复杂度是 O(n2) 键的空转时长 redisObject 的 lru 属性记录着该对象最后一次被命令程序访问的时间.该属性在内存回收中有很大的作用.\n空转时长指的是now() - lru\n47.100.254.74:6379\u0026gt; object idletime numbers (integer) 4023 ","id":5,"section":"posts","summary":"\u003cp\u003eRedis有很多种数据结构,但其并没有直接使用这些数据结构来构建这个 \u003ccode\u003eNOSQL\u003c/code\u003e, 而是通过 \u003ccode\u003e对象系统\u003c/code\u003e 完成了对所有数据结构的统一管理, 实现内存回收, 对象共享等特性~\u003c/p\u003e","tags":["redis"],"title":"Redis-万物皆「对象」","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/obj/","year":"2019"},{"content":"分布式锁有很多中实现(纯数据库,zookeeper,redis),纯数据库的受限于数据库性能,zk 可以保证加锁的顺序,是公平锁.Redis中的实现就是接下来要学习的.\n为什么使用分布式锁? 在分布式环境下想要保证只能有一个请求更新一条数据,普通的加锁(比如 Java 中的 synchronized,JUC 中的各种 Lock)都不能胜任. 分布式锁的意义在于可以将操作锁的权利中心化,从而串行控制业务的执行.但是使用分布式锁也有很多弊端,后面再说.\n分布式锁的特点? 互斥:具有强排他性,需要保证不同节点不同线程的互斥 可重入:同一个节点的同一个线程如果获得了锁,那也可以再次获得 高效,高可用:加锁解锁要高效,高可用保证分布式锁服务不会宕机失效 阻塞/非阻塞:像 ReentrantLock 支持 lock, tryLock, tryLock(long timeout) 支持公平锁/非公平锁(Option) 如何使用分布式锁? Redis中有多种实现分布式锁的方式,一个一个看看.\n简单粗暴版 设置一个坑,让所有节点去抢就好.即语义为: set if not exist, 抢到后执行逻辑,逻辑完成后在del即可.\nredis 2.8 版本之前我们会通过以下方式:\nsetnx {resource-name} {anystring} 我们还需要加一个过期时间,以免各种异常宕机情况导致锁无法释放的问题.\nexpire key {max-lock-time} 这两条命令并不是原子操作的,所以我们需要通过 Lua 脚本来保证其原子性\nredis 2.8 版本之后官方提供了 nx ex 的原子操作,使用起来更加简单了.\nset {resource-name} {anystring} nx ex {max-lock-time} Redission版 https://github.com/redisson/redisson\n Redission 和 Jedis 都是 Java 中的 redis 客户端, Jedis 使用的是阻塞式 I/O, 而 Redission 使用的 Netty 来进行通信,而且 API 封装更友好, 继承了 java.util.concurrent.locks.Lock 的接口,可以像操作本地 Lock 一样操作分布式锁. 而且 Redission 还提供了不同编程模式的 API: sync/async, Reactive, RxJava, 非常人性化. Redission 有丰富的接口实现以及对不同异常情况的处理设计很值得学习.\n// 1. 设置 config Config config = new Config(); // 2. 创建 redission 实例 RedissonClient redisson = Redisson.create(config); // 4. 获取锁 RLock lock = redisson.getLock(\u0026quot;myLock\u0026quot;); // 5. 加锁 // 方式一 // 加锁以后10秒钟自动解锁 // 无需调用unlock方法手动解锁 lock.lock(10, TimeUnit.SECONDS); // 方式二 // 尝试加锁,最多等待100秒,上锁以后10秒自动解锁 boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS); if (res) { try { ... } finally { lock.unlock(); } } // 方式三 // 异步加锁 RLock lock = redisson.getLock(\u0026quot;anyLock\u0026quot;); lock.lockAsync(); lock.lockAsync(10, TimeUnit.SECONDS); Future\u0026lt;Boolean\u0026gt; res = lock.tryLockAsync(100, 10, TimeUnit.SECONDS); RedLock https://redis.io/topics/distlock\n 上述的分布式锁实现都是基于单实例实现,所以会出现单点问题.胆大RedLock 基本原理是利用多个 Redis 集群,用多数的集群加锁成功,减少Redis某个集群出故障,造成分布式锁出现问题的概率。\n加锁过程 客户端获取当前的时间戳。 对 N 个 Redis 实例进行获取锁的操作,具体的操作同单机分布式锁。对 Redis 实例的操作时间需要远小于分布式锁的超时时间,这样可以保证在少数 Redis 节点 Down 掉的时候仍可快速对下一个节点进行操作。 客户端会记录所有实例返回加锁成功的时间,只有从多半的实例(在这里例子中 \u0026gt;= 3)获取到了锁,且操作的时间远小于分布式锁的超时时间,锁才被人为是正确获取。 如果锁被成功获取了,当前分布式锁的合法时间为初始设定的合法时间减去上锁所花的时间。 若分布式锁获取失败,会强制对所有实例进行锁释放的操作,即使这个实例上不存在相应的键值。 分布式锁的一些问题 锁被其他客户端释放 如果线程 A 在获取锁后处理业务时间过长,导致锁被自动释放了,此时 线程 B 重新获取到了锁. 线程 A 在执行完业务逻辑后释放锁(DEL操作),这是就会把线程 B 获取到的锁给释放掉.\n如何解决? 在设置 value 时,生成一个随机 token, 删除 key 时先做判断,只有在 token 与自己持有的相等时,才能删除. 由于需要保证原子性, 我们需要通过 Lua 脚本来实现.像下面这样,不过 Redission 已经有对应的实现了.\nif redis.call(\u0026quot;get\u0026quot;,KEYS[1]) == ARGV[1] then return redis.call(\u0026quot;del\u0026quot;,KEYS[1]) else return 0 end 超时问题 如果在加锁和释放锁之间的业务逻辑过长,超出了锁的过期时间,那么就可能会导致另一个线程获取到锁,导致逻辑不能严格的串行执行.所以分布式锁的初衷是: 逻辑越短越好,持有锁的时间越短越好.\n如何解决? 这个目前没有太好解决的方案,后面如果看到了,就更新到这里.自己觉得: 尽量保证持锁时间短,优化代码逻辑.虽然可以延长锁的时间,但是会影响吞吐量的吧.如果真的有多个客户端持有了锁,还需要尽量保证业务逻辑中数据的幂等性,日志监控,及时报警,这样也可以做到尽快的人工介入.\n 技术莫得银弹~适合的才是最好的.\n 时钟不一致 RedLock 强依赖时间,所以机器时间不一致会有很大的问题\n如何解决? 人为调整 NTP自动调整: 可以将时间精度控制在一定范围内. 性能、故障恢复和 fsync 假设 Redis 没有持久性,当一个客户端获得了 5 个实例中的 3 个锁,若 3 个锁所在的实例 Down 掉了,实例再次启动时,其他的客户端也可以再次获得锁。\n这个问题会因为开启了 Redis 的持久化而改观,对于 AOF 持久化(区别与 RDB 的二进制持久化,是文本持久化)。默认采用的是每秒钟通过 fsync 落盘,这意味着会丢失一秒内的数据,如果需要更有安全保证的持久化,可以设置 fsync=always,但对应的会损失一部分性能。\n更好的解决办法是在实例 Down 掉后延迟一个略长于锁合法时间的时间,这样就可以保证在实例启动起来时锁一定是过期的,从而无须以损失性能为代价而使用 fsync=always 的持久化。\n参考 再有人问你分布式锁,这篇文章扔给他 RedLock中译 ","id":6,"section":"posts","summary":"\u003cp\u003e分布式锁有很多中实现(纯数据库,zookeeper,redis),纯数据库的受限于数据库性能,zk 可以保证加锁的顺序,是公平锁.Redis中的实现就是接下来要学习的.\u003c/p\u003e","tags":["分布式锁","redis"],"title":"Redis-分布式锁","uri":"https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/","year":"2019"},{"content":"系统学习 redis 相关的知识,从数据结构开始~\nString 字符串 Redis 的字符串是 动态字符串, 长度可变,自动扩容。利用预分配空间方式减少内存的分配。默认分配 1M 大小的内存。扩容时加倍现有空间,最大占用为 512M.\n常用命令 SET,SETNX\u0026hellip;\n结构 struct SDS\u0026lt;T\u0026gt; { T capacity; // 数组容量 T len; // 数组长度 byte flags; // 特殊标识位,不理睬它 byte [] content; // 数组内容 } Redis 中的字符串叫做 Simple Dynamic String, 上述 struct 是一个简化版,实际的代码中,redis 会根据 str 的不同长度,使用不同的 SDS, 有 sdshdr8, sdshdr16, sdshdr32 等等\u0026hellip; 但结构体都是如上的类型.\ncapacity 存储数组的长度,len 表示数组的实际长度。需要注意的是: string 的字符串是以 \\0 结尾的,这样可以便于调试打印,还可以直接使用 glibc 的字符串函数进行操作.\n字符串存储 字符串有两种存储方式,长度很短时,使用 emb 形式存储,长度超过 44 时,使用 raw 形式存储.\n可以使用 debug object {your_string} 来查看存储形式\n\u0026gt; set codehole abcdefghijklmnopqrstuvwxyz012345678912345678 OK \u0026gt; debug object codehole Value at:0x7fec2de00370 refcount:1 encoding:embstr serializedlength:45 lru:5958906 lru_seconds_idle:1 \u0026gt; set codehole abcdefghijklmnopqrstuvwxyz0123456789123456789 OK \u0026gt; debug object codehole Value at:0x7fec2dd0b750 refcount:1 encoding:raw serializedlength:46 lru:5958911 lru_seconds_idle:1 WHY? 首先需要解释 RedisObject, 所有 Redis 对象都有的结构体\nstruct RedisObject { int4 type; // 4bits int4 encoding; // 4bits int24 lru; // 24bits int32 refcount; // 4bytes void *ptr; // 8bytes,64-bit system } robj; 不同的对象具有不同的类型 type (4bit),同一个类型的 type 会有不同的存储形式 encoding (4bit),为了记录对象的 LRU 信息,使用了 24 个 bit 来记录 LRU 信息。每个对象都有个引用计数,当引用计数为零时,对象就会被销毁,内存被回收。ptr 指针将指向对象内容 (body) 的具体存储位置。这样一个 RedisObject 对象头需要占据 16 字节的存储空间。\n接着我们再看 SDS 结构体的大小,在字符串比较小时,SDS 对象头的大小是 capacity+3,至少是 3。意味着分配一个字符串的最小空间占用为 19 字节 (16+3)。\n一张图解释:\nList 列表 Redis 的列表是用链表来实现的,插入删除 O (1), 查找 O (n), 列表弹出最后一个元素时,数据结构删除,内存回收.\n常用命令 LPUSH,LPOP,RPUSH,RPOP,LRANGE\u0026hellip;\n列表的数据结构 列表底层的存储结构并不是简简单单的一个链表~通过 ziplist 连接起来组成 quicklist.\nziplist 压缩列表 在列表元素较少时,redis 会使用一块连续内存来进行存储,这个结构就是 ziplist. 所有的元素紧挨着存储.\n\u0026gt; zadd z_lang 1 java 2 rust 3 go (integer) 3 \u0026gt; debug object z_lang Value at:0x7fde1c466660 refcount:1 encoding:ziplist serializedlength:34 lru:11974320 lru_seconds_idle:11 可以看到上述输出 encoding 为 ziplist.\nstruct ziplist\u0026lt;T\u0026gt; { int32 zlbytes; // 整个压缩列表占用字节数 int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个节点 int16 zllength; // 元素个数 T [] entries; // 元素内容列表,挨个挨个紧凑存储 int8 zlend; // 标志压缩列表的结束,值恒为 0xFF } zltail_offset 是为了支持双向遍历才设计的,可以快速定位到最后一个元素,然后倒着遍历.\nentry 会随着容纳的元素不同而结构不同.\nstruct entry { int\u0026lt;var\u0026gt; prevlen; // 前一个 entry 的字节长度 int\u0026lt;var\u0026gt; encoding; // 元素类型编码 optional byte [] content; // 元素内容 } prevlen 表示前一个 entry 的字节长度,倒序遍历时,可以根据这个字段来推算前一个 entry 的位置。它是变长的整数,字符串长度小于 254 ( 0XFE ) 时,使用一个字节表示,大于等于 254, 使用 5 个字节来表示。第一个字节是 254, 剩余四个字节表示字符串长度.\nencoding 编码类型 encoding 存储编码类型信息,ziplist 通过其来决定 content 内容的形式。所以其设计是很复杂的.\n 00xxxxxx 最大长度位 63 的短字符串,后面的 6 个位存储字符串的位数,剩余的字节就是字符串的内容。 01xxxxxx xxxxxxxx 中等长度的字符串,后面 14 个位来表示字符串的长度,剩余的字节就是字符串的内容。 10000000 aaaaaaaa bbbbbbbb cccccccc dddddddd 特大字符串,需要使用额外 4 个字节来表示长度。第一个字节前缀是 10,剩余 6 位没有使用,统一置为零。后面跟着字符串内容。不过这样的大字符串是没有机会使用的,压缩列表通常只是用来存储小数据的。 11000000 表示 int16,后跟两个字节表示整数。 11010000 表示 int32,后跟四个字节表示整数。 11100000 表示 int64,后跟八个字节表示整数。 11110000 表示 int24,后跟三个字节表示整数。 11111110 表示 int8,后跟一个字节表示整数。 11111111 表示 ziplist 的结束,也就是 zlend 的值 0xFF。 1111xxxx 表示极小整数,xxxx 的范围只能是 (0001~1101), 也就是 1~13,因为 0000、1110、1111 都被占用了。读取到的 value 需要将 xxxx 减 1,也就是整数 0~12 就是最终的 value。 增加元素 ziplist 是连续存储的,没有多余空间,这意味着每次插入一个元素,就需要扩展内存。如果占用内存过大,重新分配内存和拷贝内存就会有很大的消耗。所以其缺点是不适合存储 大型字符串, 存储元素不宜 过多.\n级联更新 每一个 entry 都是有 prevlen, 而且时而为 1 字节存储,时而为 5 字节存储,取决于字符串的字节长度是否大于 254, 如果某次操作导致字节长度从 254 变为 256, 那么其下一个节点所存储的 prevlen 就要从 1 个字节变为 5 个字节来存储,如果下一个节点刚好因此超过了 254 的长度,那么下下个节点也要更新\u0026hellip; 这就是级联更新了~\nquicklist Redis 中 list 的存储结构就是 quicklist. 下面的 language 是一个记录编程语言的集合。可以看到 encoding 即为 quicklist.\n\u0026gt; debug object language Value at:0x7fde1c4665f0 refcount:1 encoding:quicklist serializedlength:29 lru:11974264 lru_seconds_idle:62740 ql_nodes:1 ql_avg_node:3.00 ql_ziplist_max:-2 ql_compressed:0 ql_uncompressed_size:27 Redis 的 quicklist 是一种基于 ziplist 实现的可压缩(quicklistLZF)的双向链表,结合了链表和 ziplist 的 优点 组成的。下面可以看下他的结构体.\n/* quicklist is a 40 byte struct (on 64-bit systems) describing a quicklist. * 'count' is the number of total entries. * 'len' is the number of quicklist nodes. * 'compress' is: -1 if compression disabled, otherwise it's the number * of quicklistNodes to leave uncompressed at ends of quicklist. * 'fill' is the user-requested (or default) fill factor. */ /** * quicklist 是一个 40byte (64 位系统) 的结构 */ typedef struct quicklist { quicklistNode *head; quicklistNode *tail; unsigned long count; /* 元素总数 */ unsigned long len; /* quicklistNode 的长度 */ int fill : 16; /* ziplist 的最大长度 */ unsigned int compress : 16; /* 节点压缩深度 */ } quicklist; typedef struct quicklistNode { struct quicklistNode *prev; struct quicklistNode *next; unsigned char *zl; /* 没有压缩,指向 ziplist, 否则指向 quicklistLZF unsigned int sz; /* ziplist 字节总数 */ unsigned int count : 16; /* ziplist 元素数量 */ unsigned int encoding : 2; /* RAW==1 or LZF==2 */ ... } quicklistNode; //LZF 无损压缩算法,压缩过的 ziplist typedef struct quicklistLZF { // 未压缩之前的大小 unsigned int sz; /* LZF size in bytes*/ // 存放压缩过的 ziplist 数组 char compressed []; } quicklistLZF; 一张图展示结构 压缩深度 quicklist 默认的压缩深度是 0,也就是不压缩。压缩的实际深度由配置参数 list-compress-depth 决定。为了支持快速的 push/pop 操作,quicklist 的首尾两个 ziplist 不压缩,此时深度就是 1。如果深度为 2,就表示 quicklist 的首尾第一个 ziplist 以及首尾第二个 ziplist 都不压缩。\nSet 集合 Redis 的集合相当于 Java 语言里面的 HashSet,它内部的键值对是无序的唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值NULL。\n常用命令 SADD,SMEMBERS,SPOP,SISMEMBER,SCARD\u0026hellip;\nHash 哈希 Redis 的 Hash相当于Java 中的 HashMap, 数组 + 链表的二维结构.与 HashMap 不同的地方在于 rehash 方式不同, HashMap 中的 rehash 是阻塞式的, 需要一次性全部 rehash, 而 redis 为了性能考虑, 采用的是 渐进式 rehash.\n常用命令 HSET,HGET,HMSET,HLEN\u0026hellip;\n\u0026gt; hset books java \u0026quot;think in java\u0026quot; # 命令行的字符串如果包含空格,要用引号括起来 (integer) 1 \u0026gt; hset books golang \u0026quot;concurrency in go\u0026quot; (integer) 1 \u0026gt; hset books python \u0026quot;python cookbook\u0026quot; (integer) 1 \u0026gt; hgetall books # entries(),key 和 value 间隔出现 1) \u0026quot;java\u0026quot; 2) \u0026quot;think in java\u0026quot; 3) \u0026quot;golang\u0026quot; 4) \u0026quot;concurrency in go\u0026quot; 5) \u0026quot;python\u0026quot; 6) \u0026quot;python cookbook\u0026quot; \u0026gt; hlen books (integer) 3 \u0026gt; hget books java \u0026quot;think in java\u0026quot; \u0026gt; hset books golang \u0026quot;learning go programming\u0026quot; # 因为是更新操作,所以返回 0 (integer) 0 \u0026gt; hget books golang \u0026quot;learning go programming\u0026quot; \u0026gt; hmset books java \u0026quot;effective java\u0026quot; python \u0026quot;learning python\u0026quot; golang \u0026quot;modern golang programming\u0026quot; # 批量 set OK 字典 Redis 的 Hash 是通过 dict 结构来实现的, 该结构的底层是由哈希表来实现.类似于 HashMap, 数组+链表, 超过负载因子所对应的阈值时,进行 rehash, 扩容. 在具体实现中,使用了渐进式hash的方式来避免 HashMap 这种阻塞式的 rehash, 将 rehash 的工作分摊到对字典的增删改查中.\nstruct typedef struct dictEntry { void *key; //键 union { void *val; //值 uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next; //指向下一节点,形成链表 } dictEntry; /* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table. */ typedef struct dictht { dictEntry **table; // 哈希表数组,数组的每一项都是 distEntry 的头结点 unsigned long size; // 哈希表的大小,也是触发扩容的阈值 unsigned long sizemask; // 哈希表大小掩码,用于计算索引值,总是等于 size-1 unsigned long used; // 哈希表中实际保存的节点数量 } dictht; typedef struct dict { dictType *type; //属性是一个指向 dictType 结构的指针,每个 dictType 结构保存了一簇用于操作特定类型键值对的函数,Redis 会为用途不同的字典设置不同的类型特定函数 void *privdata; // 保存了需要传给那些类型特定函数的可选参数 dictht ht[2]; // 在字典内部,维护了两张哈希表. 一般情况下,字典只使用 ht[0] 哈希表,ht[1] 哈希表只会在对 ht[0] 哈希表进行 rehash 时使用 long rehashidx; // 记录 rehash 的状态, 没有进行 rehash 则为 -1 unsigned long iterators; /* number of iterators currently running */ } dict; 一张图来表示 何时扩容? 找到dictAddRow 函数观察源码可以发现,会在 _dictExpandIfNeeded 函数中进行扩容的判断.\n/* Expand the hash table if needed */ static int _dictExpandIfNeeded(dict *d) { /* Incremental rehashing already in progress. Return. */ // 正在渐进式扩容, 就返回 OK if (dictIsRehashing(d)) return DICT_OK; /* If the hash table is empty expand it to the initial size. */ // 如果哈希表 ht[0] size 为 0 ,初始化, 说明 redis 是懒加载的,延长初始化策略 if (d-\u0026gt;ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); /* If we reached the 1:1 ratio, and we are allowed to resize the hash * table (global setting) or we should avoid it but the ratio between * elements/buckets is over the \u0026quot;safe\u0026quot; threshold, we resize doubling * the number of buckets. */ /* * 如果哈希表ht[0]中保存的key个数与哈希表大小的比例已经达到1:1,即保存的节点数已经大于哈希表大小 * 且redis服务当前允许执行rehash,或者保存的节点数与哈希表大小的比例超过了安全阈值(默认值为5) * 则将哈希表大小扩容为原来的两倍 */ if (d-\u0026gt;ht[0].used \u0026gt;= d-\u0026gt;ht[0].size \u0026amp;\u0026amp; (dict_can_resize || d-\u0026gt;ht[0].used/d-\u0026gt;ht[0].size \u0026gt; dict_force_resize_ratio)) { return dictExpand(d, d-\u0026gt;ht[0].used*2); } return DICT_OK; } 正常情况下,当 hash 表中元素的个数等于第一维数组的长度时,就会开始扩容,扩容的新数组是原数组大小的 2 倍。不过如果 Redis 正在做 bgsave,为了减少内存页的过多分离 (Copy On Write),Redis 尽量不去扩容 (dict_can_resize),但是如果 hash 表已经非常满了,元素的个数已经达到了第一维数组长度的 5 倍 (dict_force_resize_ratio),说明 hash 表已经过于拥挤了,这个时候就会强制扩容。\n何时缩容? 当哈希表的负载因子小于 0.1 时,自动缩容.这个操作会在 redis 的定时任务中来完成.函数为 databasesCron,该函数的作用是在后台慢慢的处理过期,rehashing, 缩容.\n执行条件: 没有子进程执行aof重写或者生成RDB文件\n/* 遍历所有的redis数据库,尝试缩容 */ for (j = 0; j \u0026lt; dbs_per_call; j++) { tryResizeHashTables(resize_db % server.dbnum); resize_db++; } /* If the percentage of used slots in the HT reaches HASHTABLE_MIN_FILL * we resize the hash table to save memory */ void tryResizeHashTables(int dbid) { if (htNeedsResize(server.db[dbid].dict)) dictResize(server.db[dbid].dict); if (htNeedsResize(server.db[dbid].expires)) dictResize(server.db[dbid].expires); } /* Hash table parameters */ #define HASHTABLE_MIN_FILL 10 /* Minimal hash table fill 10% */ int htNeedsResize(dict *dict) { long long size, used; size = dictSlots(dict); used = dictSize(dict); return (size \u0026gt; DICT_HT_INITIAL_SIZE \u0026amp;\u0026amp; (used*100/size \u0026lt; HASHTABLE_MIN_FILL)); } /* Resize the table to the minimal size that contains all the elements, * but with the invariant of a USED/BUCKETS ratio near to \u0026lt;= 1 */ int dictResize(dict *d) { int minimal; if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR; minimal = d-\u0026gt;ht[0].used; if (minimal \u0026lt; DICT_HT_INITIAL_SIZE) minimal = DICT_HT_INITIAL_SIZE; return dictExpand(d, minimal); } 从 htNeedsResize函数中可以看到,当哈希表保存的key数量与哈希表的大小的比例小于10%时需要缩容.最小容量为DICT_HT_INITIAL_SIZE = 4. dictResize 函数中,当正在执行 aof 重写或生成 rdb 时, dict_can_resize 会变为 0, 也就说明上面的 执行条件.\n渐进式 rehash 从上述源码中可以看出,所有的扩容或者创建都经过 dictExpand 函数.\n/* Expand or create the hash table */ int dictExpand(dict *d, unsigned long size) { /* the size is invalid if it is smaller than the number of * elements already inside the hash table */ if (dictIsRehashing(d) || d-\u0026gt;ht[0].used \u0026gt; size) return DICT_ERR; // 计算新的哈希表大小,获得大于等于size的第一个2次方 dictht n; /* the new hash table */ unsigned long realsize = _dictNextPower(size); /* Rehashing to the same table size is not useful. */ if (realsize == d-\u0026gt;ht[0].size) return DICT_ERR; /* Allocate the new hash table and initialize all pointers to NULL */ n.size = realsize; n.sizemask = realsize-1; n.table = zcalloc(realsize*sizeof(dictEntry*)); n.used = 0; /* Is this the first initialization? If so it's not really a rehashing * we just set the first hash table so that it can accept keys. */ // 第一次初始化也会通过这里来完成创建 if (d-\u0026gt;ht[0].table == NULL) { d-\u0026gt;ht[0] = n; return DICT_OK; } /* Prepare a second hash table for incremental rehashing */ // ht[1] 开始派上用场,扩容时是在 ht[1] 上操作, rehash 完毕后,在交换到 ht[0] d-\u0026gt;ht[1] = n; d-\u0026gt;rehashidx = 0; return DICT_OK; } 从 dictExpand 这个函数可以发现做了这么几件事:\n 校验是否可以执行 rehash 创建一个新的哈希表 n, 分配更大的内存 将哈希表 n 复制给 ht[1], 将 rehashidx 标志置为 0 ,意味着开启了渐进式rehash. 该值也标志渐进式rehash当前已经进行到了哪个hash槽. 该函数没有将key重新 rehash 到新的 slot 上,而是交由增删改查的操作, 以及后台定时任务来处理.\n增删改查辅助rehash 看源码其实可以发现在所有增删改查的源码中,开头都会有一个判断,是否处于渐进式rehash中.\ndictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing) { long index; dictEntry *entry; dictht *ht; if (dictIsRehashing(d)) _dictRehashStep(d); ... } // 进入 rehash 后是 \u0026gt;=0的值 #define dictIsRehashing(d) ((d)-\u0026gt;rehashidx != -1) /* * 此函数仅执行一步hash表的重散列,并且仅当没有安全迭代器绑定到哈希表时。 * 当我们在重新散列中有迭代器时,我们不能混淆打乱两个散列表的数据,否则某些元素可能被遗漏或重复遍历。 * * 该函数被在字典中查找或更新等普通操作调用,以致字典中的数据能自动的从哈系表1迁移到哈系表2 */ static void _dictRehashStep(dict *d) { if (d-\u0026gt;iterators == 0) dictRehash(d,1); } 后台任务rehash 虽然redis实现了在读写操作时,辅助服务器进行渐进式rehash操作,但是如果服务器比较空闲,redis数据库将很长时间内都一直使用两个哈希表.所以在redis周期函数中,如果发现有字典正在进行渐进式rehash操作,则会花费1毫秒的时间,帮助一起进行渐进式rehash操作.\n还是上面缩容时使用的任务函数databasesCron.源码如下:\n/* Rehash */ if (server.activerehashing) { for (j = 0; j \u0026lt; dbs_per_call; j++) { int work_done = incrementallyRehash(rehash_db); if (work_done) { /* If the function did some work, stop here, we'll do * more at the next cron loop. */ break; } else { /* If this db didn't need rehash, we'll try the next one. */ rehash_db++; rehash_db %= server.dbnum; } } } 渐进式rehash弊端 渐进式rehash避免了redis阻塞,可以说非常完美,但是由于在rehash时,需要分配一个新的hash表,在rehash期间,同时有两个hash表在使用,会使得redis内存使用量瞬间突增,在Redis 满容状态下由于Rehash会导致大量Key驱逐.\nZset 有序集合 首先 zset 是一个 set 结构,拥有 set 的所有特性,其次他可以给每一个 value 赋予一个 score 作为权重.内部实现用的跳表(skiplist)\n常用命令 ZADD,ZRANGE,ZREVRANGE,ZSCORE,ZCARD,ZRANK\u0026hellip;\n\u0026gt; zadd books 9.0 \u0026quot;think in java\u0026quot; (integer) 1 \u0026gt; zadd books 8.9 \u0026quot;java concurrency\u0026quot; (integer) 1 \u0026gt; zadd books 8.6 \u0026quot;java cookbook\u0026quot; (integer) 1 \u0026gt; zrange books 0 -1 # 按 score 排序列出,参数区间为排名范围 1) \u0026quot;java cookbook\u0026quot; 2) \u0026quot;java concurrency\u0026quot; 3) \u0026quot;think in java\u0026quot; \u0026gt; zrevrange books 0 -1 # 按 score 逆序列出,参数区间为排名范围 1) \u0026quot;think in java\u0026quot; 2) \u0026quot;java concurrency\u0026quot; 3) \u0026quot;java cookbook\u0026quot; \u0026gt; zcard books # 相当于 count() (integer) 3 \u0026gt; zscore books \u0026quot;java concurrency\u0026quot; # 获取指定 value 的 score \u0026quot;8.9000000000000004\u0026quot; # 内部 score 使用 double 类型进行存储,所以存在小数点精度问题 \u0026gt; zrank books \u0026quot;java concurrency\u0026quot; # 排名 (integer) 1 \u0026gt; zrangebyscore books 0 8.91 # 根据分值区间遍历 zset 1) \u0026quot;java cookbook\u0026quot; 2) \u0026quot;java concurrency\u0026quot; \u0026gt; zrangebyscore books -inf 8.91 withscores # 根据分值区间 (-∞, 8.91] 遍历 zset,同时返回分值。inf 代表 infinite,无穷大的意思。 1) \u0026quot;java cookbook\u0026quot; 2) \u0026quot;8.5999999999999996\u0026quot; 3) \u0026quot;java concurrency\u0026quot; 4) \u0026quot;8.9000000000000004\u0026quot; \u0026gt; zrem books \u0026quot;java concurrency\u0026quot; # 删除 value (integer) 1 \u0026gt; zrange books 0 -1 1) \u0026quot;java cookbook\u0026quot; 2) \u0026quot;think in java\u0026quot; 数据结构 众所周知, Zset 是一个有序的set集合, redis 通过 hash table 来存储 value 和 score 的映射关系,可以达到 O(1), 通过 score 排序或者说按照 score 范围来获取这个区间的 value, 则是通过 跳表 来实现的. Zset 可以达到 O(log(N)) 的插入和读写.\n什么是跳跃列表? 如图,跳跃列表是指具有纵向高度的有序链表.跳表会随机的某提升些链表的高度,并将每一层的节点进行连接,相当于构建多级索引,这样在查找的时候,从最高层开始查,可以过滤掉一大部分的范围,有点类似于二分查找.跳表也是典型的空间换时间的方式.\n每一个 kv 块对应的结构如下面的代码中的zslnode结构,kv header 也是这个结构,只不过 value 字段是 null 值——无效的,score 是 Double.MIN_VALUE,用来垫底的。\nstruct struct zslnode { string value; double score; zslnode*[] forwards; // 多层连接指针 zslnode* backward; // 回溯指针 } struct zsl { zslnode* header; // 跳跃列表头指针 int maxLevel; // 跳跃列表当前的最高层 map\u0026lt;string, zslnode*\u0026gt; ht; // hash 结构的所有键值对 } redis中跳表的优化 允许 score 是重复的 比较不仅是通过 key(即 score), 也还会比较 data 最底层(Level 1)是有反向指针的,所以是一个双向链表,这样适用于从大到小的排序需求(ZREVRANGE) 一次查找的过程 redis中level是如何生成的? /* Returns a random level for the new skiplist node we are going to create. * The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL * (both inclusive), with a powerlaw-alike distribution where higher * levels are less likely to be returned. */ int zslRandomLevel(void) { int level = 1; while ((random()\u0026amp;0xFFFF) \u0026lt; (ZSKIPLIST_P * 0xFFFF)) level += 1; return (level\u0026lt;ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL; } ZSKIPLIST_MAXLEVEL 最大值是 64, 也就是最多 64 层.ZSKIPLIST_P 为 1/4, 也就是说有 25% 的概率有机会获得level,要获得更高的level,概率更小. 这也就导致了, redis中的跳表层级不会特别高,较扁平,较低层节点较多.有个小优化的地方: 跳表会记录下当前的最高层数 MaxLevel 这样就不需要从最顶层开始遍历了.\n为什么使用跳表而不是红黑树或者哈希表? skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的,而哈希表不是有序的。因此,在哈希表上只能做单个key的查找,不适宜做范围查找。所谓范围查找,指的是查找那些大小在指定的两个值之间的所有节点。 在做范围查找的时候,平衡树比skiplist操作要复杂。在平衡树上,我们找到指定范围的小值之后,还需要以中序遍历的顺序继续寻找其它不超过大值的节点。如果不对平衡树进行一定的改造,这里的中序遍历并不容易实现。而在skiplist上进行范围查找就非常简单,只需要在找到小值之后,对第1层链表进行若干步的遍历就可以实现。 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。 从内存占用上来说,skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。 查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当;而哈希表在保持较低的哈希值冲突概率的前提下,查找时间复杂度接近O(1),性能更高一些。所以我们平常使用的各种Map或dictionary结构,大都是基于哈希表实现的。 从算法实现难度上来比较,skiplist比平衡树要简单得多。 参考 渐进式 rehash 机制 美团针对Redis Rehash机制的探索和实践 zset内部实现 ","id":7,"section":"posts","summary":"\u003cp\u003e系统学习 redis 相关的知识,从数据结构开始~\u003c/p\u003e","tags":["redis","数据结构"],"title":"Redis-数据结构","uri":"https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/","year":"2019"},{"content":"RabbitMQ在保证生产端与消费端的数据安全上,提供了消息确认的机制来保证. 消费端到 broker 端的确认常叫做ack机制, broker 到生产端常叫做confirm.\n消费端确认机制 Delivery Tag Delivery Tag 是 RabbitMQ 来确认消息如何发送的标志. Consumer 在注册到 RabbitMQ 上后, RabbitMQ 通过 basic.deliver 方法向消费者推送消息, 这个方法中就带着可以在 Channel中唯一识别消息的 delivery tag . Delivery Tag 是channel 隔离的.\ntag是一个大于零的增长的整型, 客户端在确认消息时将其当做参数传回来就可以保证是同一条消息的确认了.\ntag是channel隔离的, 所以必须在接受消息的channel上确认消息收到,否则会抛 unknown delivery tag的异常.\n最大值: delivery tag 是 64 位的long,最大值是 9223372036854775807. tag是channel隔离的,理论上来说是不会超过这个值的.\n确认机制 消息确认有两种模式: 自动/手动.\n自动模式会在消息一经发出就自动确认.这是在吞吐量和 可靠投递之间的权衡.如果在发送的过程中, TCP断掉了或是其他的问题,那消息就会丢掉了,这个问题需要考虑.还需要考虑的一个问题是: Consumer 消费速率如果不能跟上broker的发送速率, 会导致Consumer过载(消息堆积,内存耗尽),而在手动模式中可以通过prefetch来控制消费端的速率.有些客户端会提供TCP的背压,不能处理时,就丢弃了.\n手动模式需要Consumer端在收到消息后调用:\n basic.ack : 消息处理好了,可以丢掉了 basic.nack : 可以批量reject, 如果Consumer设置了requeue,消息还会重新回到broker的队列中 basic.reject : 消息没有处理但是也需要删除 Channel Prefetch 由于消息的发送和接收是独立的且完全异步,消息的手动确认也是完全异步的.所以这里有一个未确认消息的滑动窗口.在消费端我们经常需要控制接收消息的数量,防止出现消息缓存buffer越界的问题.此时我们就可以通过basic.qos来设置prefetch count, 该值定义了一个Channel中能存放的消息条数上限,超过这个值,RabbitMQ在收到至少一条ack之前都不能再往Channel上发送消息了.\n这里需要注意前面说的滑动窗口: 意味着当Channel满的时候,不会再往Channel上发消息,但是当你ack了一条,就会往Channel上发一条,ack了N条,就会发N条到Channel上.\nbasic.get设置prefetch是无效的,请使用basic.consume\n吞吐量影响因素: Ack机制 \u0026amp; Prefetch 确认机制的选择和Prefetch的值决定了消费端的吞吐量.一般来讲,增大Prefetch值以及 自动确认 会提升推送消息的速率,但也会增加待处理消息的堆积,消费端内存压力也会上升.\n如果Prefetch无界,Consumer在消费大量消息时没有ack会导致消费端连接的那个节点内存压力上升.所以找到一个完美的Prefetch值还是很重要的. 一般 100-300 左右吞吐量还不错,且消费端压力不大. 设置为 1 时,就很保守了,这种情况下吞吐量就很低,延迟较高.\n发布端确认机制 网络有很多种失败的方式,并且需要花时间检测.所以客户端并不能保证消息可以正常的发送到broker,正常的被处理.有可能丢了也有可能有延迟.\n根据AMQP-0-9-1, 只有通过 事务 的方式来保证.将Channel设置为事务型的,每条消息都以事务形式推送提交.但是,事务是很重,会降低吞吐量,所以RabbitMQ就换了种方式来实现: 通过模仿已有的Consumer端的确认机制.\n启用Confirm,客户端调用confirm.select即可.Broker会返回confirm.select-ok,取决于是否有no-wait设置. Channel如果设置了confirm.select,说明处于confirm模式,此时是不能设置为事务型Channel,两者不可互通.\nBroker的应答机制同Consumer一致,通过basic.ack即可,也可批量ack.\n发布端的NACK 在某些情况下,broker无法再接收消息,就会向发布端回执basic.nack,意味着消息会被丢弃,发布端需要重新发布这些消息.当Channel置为Confirm模式后,后面收到的消息都将会confirm或者nack 一次. 需要注意的几点:\n 不能保证消息何时confirm. 消息也不会同时confirm和nack 只有在Erlang进程内部报错时才会有nack Broker何时确认发布的消息? 无法路由的消息: 当确认消息不会被路由时, broker会立即发出confirm. 如果消息设置了强制(mandatory)发送,basic.return会在basic.ack之前回执. nack逻辑一致.\n可路由的消息: 所有queue接受了消息时返回basic.ack ,如果队列是持久化的,意味着持久化完成后才发出.对镜像队列(Mirrored Queues),意味着所有镜像都收到后发出.\n持久化消息的ack延迟 RabbitMQ的持久化通常是批量的,需要间隔几百毫秒来减少 fsync(2)的调用次数或者等待 queue 是空闲状态的时候,这意味着,每一次basic.ack的延迟可能达到几百毫秒.为了提高吞吐量最好是将持久化做成异步的,或者使用批量publish,这个需要参考客户端的api实现.\n确认消息的顺序 大多数情况下, RabbitMQ 会根据消息发送的顺序依次回执(要求消息发送在同一个channel上).但确认回执都是异步的,并且可以确认一条,或一组消息.确切的confirm发送时间取决于: 消息是否需要持久化,消息的路由方式.意味着不同的消息的确认时间是不同的.也就意味着返回确认的顺序并不一定相同.应用方不能将其作为一个依据.\n参考 https://www.rabbitmq.com/confirms.html#acknowledgement-modes ","id":8,"section":"posts","summary":"\u003cp\u003eRabbitMQ在保证生产端与消费端的数据安全上,提供了消息确认的机制来保证. 消费端到 \u003ccode\u003ebroker\u003c/code\u003e 端的确认常叫做\u003ccode\u003eack机制\u003c/code\u003e, \u003ccode\u003ebroker\u003c/code\u003e 到生产端常叫做\u003ccode\u003econfirm\u003c/code\u003e.\u003c/p\u003e","tags":["rabbitmq"],"title":"RabbitMQ-消息确认机制","uri":"https://xiaohei.im/hugo-theme-pure/2019/10/rabbitmq-ack-confirm/","year":"2019"},{"content":" 最近使用Hugo作为博客引擎后,闲不下来总想去找一些简单好看的主题.在官方的主题列表搜罗了一圈后,选择了yinyang,非常简单,但是用了一段时间还是想找个功能全点的,无意中瞄到了一个博主的博客,主题特别吸引我,但是是 hexo 平台的,搜了半天也没有人移植,就自己来吧~ 移植的过程中,遇到了挺多问题,也是这些问题慢慢的熟悉了hugo的模板结构.下面就来写一写自己遇到的问题~\n 页面变量参数 https://gohugo.io/variables/\n hugo的页面有基本的变量(我更愿意称为属性,根据这些属性来实现我们的主题模板.最主要的有三类:Site, Page, Taxonomy.\n.Site 站点相关的属性,即config.toml(yml)文件中的配置.\n 在页面模板中,我们可以使用{{- .Site.Autor }}这样的方式来获取你想要的站点属性.具体的站点属性可以查看https://gohugo.io/variables/site/. .Site 属于全局配置,在 作用域 得当的情况下是可以正常调用的.非正常情况我们下面再讲.\n常用属性 .Site.Pages : 获取所有文章(包含生成的一些分类页,比如说 标签页),按时间倒序排序.返回是一个数组.我们经常用来渲染一个列表.比如 归档 页面.\n .Site.Taxonomies : 获取所有的分类(这里的分类是广义上的),可以获取到按tag分类的集合,也可以获取到按category分类的集合,可以用这个属性来完成分类的页面.下面这段代码就代表着我可以拿到所有的 分类页 ,循环得到分类页的链接和标题.\n {{- range .Site.Taxonomies.categories }} \u0026lt;li\u0026gt;\u0026lt;a href=\u0026quot;{{ .Page.Permalink }}\u0026quot;\u0026gt;{{ .Page.Title }}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; {{- end }} .Site.Params 可以获取到我们在config.toml的Params标签下设置的内容.也是很重要的属性.比如说下面的例子.我可以设置日期的格式化样式,展示成你想要的类型. \u0026lt;p\u0026gt;{{ .Date.Format (.Site.Params.dateFormatToUse | default \u0026quot;2006-01-02\u0026quot;)}}\u0026lt;/p\u0026gt; .Page 页面中定义的属性.\n 页面属性可以大致分为两部分,一个是Hugo原生的属性,一个是每一篇文章的文件头,即front matter中的属性.具体的属性可以在https://gohugo.io/variables/page/查看. 在一个页面的作用域中使用时可以直接调用.比如我们想要知道页面的创建日期就可以直接 {{ .Date }} 即可.\n常用属性 .Date/.Title/.ReadingTime/.WordCount 见名知意 .Permalink/.RelPermalink 永久链接及相对连接 .Summary 摘要,默认70字 .Pages 为什么页面中还有一个这样的属性呢? Page是包含生成的分类页, 标签页的,所有当处于这些页面时会返回一个集合,若是我们自己真正写的文件,即markdown文件,会返回nil的. .Taxonomies 用作内容分类的管理. 我们经常在写文章时会写上 categories 或者 tags, 这些标签类目就是 .Taxonomies 的集中展示, Hugo 默认会有 categories 和 tags 两种分类. 你也可以自己再自定义设置. 具体参考: https://gohugo.io/content-management/taxonomies\n 使用案例 官方提供了多种 Template 实现常用的遍历.\n 我通常会用来写标签页(tags)和分类页(categories). 直接调用 .Taxonomies 会获得所有的分类项(即: tags, categories, 自定义分类项), .Taxonomies.tags 就可以获得所有的标签,以及标签下的所有文章.以下就是我的主题中 标签 页的实现逻辑.\n{{- $tags := .Site.Taxonomies.tags }} \u0026lt;main class=\u0026quot;main\u0026quot; role=\u0026quot;main\u0026quot;\u0026gt; \u0026lt;article class=\u0026quot;article article-tags post-type-list\u0026quot; itemscope=\u0026quot;\u0026quot;\u0026gt; \u0026lt;header class=\u0026quot;article-header\u0026quot;\u0026gt; \u0026lt;h1 itemprop=\u0026quot;name\u0026quot; class=\u0026quot;hidden-xs\u0026quot;\u0026gt;{{- .Title }}\u0026lt;/h1\u0026gt; \u0026lt;p class=\u0026quot;text-muted hidden-xs\u0026quot;\u0026gt;{{- T \u0026quot;total_tag\u0026quot; (len $tags) }}\u0026lt;/p\u0026gt; \u0026lt;nav role=\u0026quot;navigation\u0026quot; id=\u0026quot;nav-main\u0026quot; class=\u0026quot;okayNav\u0026quot;\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li\u0026gt;\u0026lt;a href=\u0026quot;{{- \u0026quot;tags\u0026quot; | relURL }}\u0026quot;\u0026gt;{{- T \u0026quot;nav_all\u0026quot; }}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; {{- range $tags }} \u0026lt;li\u0026gt;\u0026lt;a href=\u0026quot;{{ .Page.Permalink }}\u0026quot;\u0026gt;{{ .Page.Title }}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; {{- end }} \u0026lt;/ul\u0026gt; \u0026lt;/nav\u0026gt; \u0026lt;/header\u0026gt; \u0026lt;!-- /header --\u0026gt; \u0026lt;div class=\u0026quot;article-body\u0026quot;\u0026gt; {{- range $name, $taxonomy := $tags }} \u0026lt;h3 class=\u0026quot;panel-title mb-1x\u0026quot;\u0026gt; \u0026lt;a href=\u0026quot;{{ \u0026quot;/tags/\u0026quot; | relURL}}{{ $name | urlize }}\u0026quot; title=\u0026quot;#{{- $name }}\u0026quot;\u0026gt;# {{ $name }}\u0026lt;/a\u0026gt; \u0026lt;small class=\u0026quot;text-muted\u0026quot;\u0026gt;(Total {{- .Count }} articles)\u0026lt;/small\u0026gt; \u0026lt;/h3\u0026gt; \u0026lt;div class=\u0026quot;row\u0026quot;\u0026gt; {{- range $taxonomy }} \u0026lt;div class=\u0026quot;col-md-6\u0026quot;\u0026gt; {{ .Page.Scratch.Set \u0026quot;type\u0026quot; \u0026quot;card\u0026quot;}} {{- partial \u0026quot;item-post.html\u0026quot; . }} \u0026lt;/div\u0026gt; {{- end }} \u0026lt;/div\u0026gt; {{- end }} \u0026lt;/div\u0026gt; \u0026lt;/article\u0026gt; \u0026lt;/main\u0026gt; 上下文传递 刚开始写 Hugo 的页面时,最让我头疼的地方就在在于此.现在想想他的逻辑是很标准的.不同的代码块上下文隔离.\n 在Hugo中,上下文的传递一般是靠.符号来完成的. 用的最多的就是再组装页面时,需要将当前页面的作用域传递到 partial 的页面中去以便组装进来的页面可以获得当前页面的属性.\n以下是我的 baseof.html 页面, 可以看到 partial 相关的代码中都有 . 符号, 这里就是将当前页面的属性传递下去了, 其他页面也就可以正常使用该页面的属性了.\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026quot;{{ .Site.Language }}\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot; /\u0026gt; \u0026lt;meta http-equiv=\u0026quot;X-UA-Compatible\u0026quot; content=\u0026quot;IE=edge,chrome=1\u0026quot; /\u0026gt; \u0026lt;title\u0026gt; {{- block \u0026quot;title\u0026quot; . -}} {{ if .IsPage }} {{ .Title }} - {{ .Site.Title }} {{ else}} {{ .Site.Title}}{{ end }} {{- end -}} \u0026lt;/title\u0026gt; {{ partial \u0026quot;head.html\u0026quot; . }} \u0026lt;/head\u0026gt; \u0026lt;body class=\u0026quot;main-center\u0026quot; itemscope itemtype=\u0026quot;http://schema.org/WebPage\u0026quot;\u0026gt; {{- partial \u0026quot;header.html\u0026quot; .}} {{- if and (.Site.Params.sidebar) (or (ne .Params.sidebar \u0026quot;none\u0026quot;) (ne .Params.sidebar \u0026quot;custom\u0026quot;))}} {{- partial \u0026quot;sidebar.html\u0026quot; . }} {{end}} {{ block \u0026quot;content\u0026quot; . }}{{ end }} {{- partial \u0026quot;footer.html\u0026quot; . }} {{- partial \u0026quot;script.html\u0026quot; . }} \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 页面组织 baseof.html baseof 可以理解为一种模板,符合规范定义的页面都会按照 baseof.html 的框架完成最后的渲染,具体可以查看官网页, 以本次移植主题的 baseof.html 来说一下.\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html lang=\u0026quot;{{ .Site.Language }}\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot; /\u0026gt; \u0026lt;meta http-equiv=\u0026quot;X-UA-Compatible\u0026quot; content=\u0026quot;IE=edge,chrome=1\u0026quot; /\u0026gt; \u0026lt;title\u0026gt; {{- block \u0026quot;title\u0026quot; . -}} {{ if .IsPage }} {{ .Title }} - {{ .Site.Title }} {{ else}} {{ .Site.Title}}{{ end }} {{- end -}} \u0026lt;/title\u0026gt; {{ partial \u0026quot;head.html\u0026quot; . }} \u0026lt;/head\u0026gt; \u0026lt;body class=\u0026quot;main-center\u0026quot; itemscope itemtype=\u0026quot;http://schema.org/WebPage\u0026quot;\u0026gt; {{- partial \u0026quot;header.html\u0026quot; .}} {{- if and (.Site.Params.sidebar) (or (ne .Params.sidebar \u0026quot;none\u0026quot;) (ne .Params.sidebar \u0026quot;custom\u0026quot;))}} {{- partial \u0026quot;sidebar.html\u0026quot; . }} {{end}} {{ block \u0026quot;content\u0026quot; . }}{{ end }} {{- partial \u0026quot;footer.html\u0026quot; . }} {{- partial \u0026quot;script.html\u0026quot; . }} \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 可以看到上面的页面中就是一个完整的 HTML 结构,我在其中组装了很多页面,比如head,header,footer等等,这些在最后渲染的时候都会加入进来组成一个完整的页面.\n在上面还有一个关键字 block, 比如 {{ block \u0026quot;title\u0026quot; }}, {{ block \u0026quot;content\u0026quot;}}.该关键字允许你自定义一个模板嵌进来, 只要按照规定的方式来.比如说我的文章页 single.html.\n{{- define \u0026quot;content\u0026quot;}} \u0026lt;main class=\u0026quot;main\u0026quot; role=\u0026quot;main\u0026quot;\u0026gt; {{- partial \u0026quot;article.html\u0026quot; . }} \u0026lt;/main\u0026gt; {{- end}} 这里我们定义了 content 的模板, 和 baseof.html 的模板呼应,在渲染一篇文章时,就会将single.html 嵌入 baseof.html 生成最后的页面了.\n模板页面查询规则 Hugo要怎么知道文章页还是标签页对应的模板是什么呢?答案: 有一套以多个属性作为依据的查询各类模板的标准.具体可以查看官网页.\n以文章页来举例, Hugo 官网上的内容页寻址规则如下:\n\n由上可见,会按照该顺序依次往下找,我一般会写在layouts/_default/single.html 下,这样可以在所有页面下通用.\n这里有个小坑也是之前文档没看好遇到的: 标签页和分类页这种对应的查找规则要按照该指引.\n参考 https://harmstyler.me/posts/2019/how-to-pass-variables-to-a-partial-template-in-hugo/ https://www.qikqiak.com/post/hugo-integrated-algolia-search/ ","id":9,"section":"posts","summary":"\u003cblockquote\u003e\n\u003cp\u003e最近使用\u003ca href=\"https://gohugo.io/\"\u003eHugo\u003c/a\u003e作为博客引擎后,闲不下来总想去找一些简单好看的主题.在\u003ca href=\"https://themes.gohugo.io/\"\u003e官方的主题列表\u003c/a\u003e搜罗了一圈后,选择了\u003ca href=\"https://github.com/joway/hugo-theme-yinyang\"\u003eyinyang\u003c/a\u003e,非常简单,但是用了一段时间还是想找个功能全点的,无意中瞄到了一个博主的博客,主题特别吸引我,但是是 \u003ccode\u003ehexo\u003c/code\u003e 平台的,搜了半天也没有人移植,就自己来吧~ 移植的过程中,遇到了挺多问题,也是这些问题慢慢的熟悉了hugo的模板结构.下面就来写一写自己遇到的问题~\u003c/p\u003e\n\u003c/blockquote\u003e","tags":["hugo"],"title":"Hexo =\u003e Hugo主题移植记录","uri":"https://xiaohei.im/hugo-theme-pure/2019/09/hugo-theme-dev-note/","year":"2019"},{"content":"rabbitmq有多种使用模式,在这里记录下不同模式的消息路由规则\n预备知识 总结的不错的文章: https://blog.csdn.net/qq_27529917/article/details/79289564\n Binding Exchange 与 队列 之间的绑定为 Binding.绑定时可以设置 binding key, 发消息时会有一个 routing key, 当 routing key 与 binding key 相同时, 这条消息才能发送到队列中去.\nExchange Type Exchange 有不同的类型, 每种类型的功能也是不一致的\n Fanout 把所有发送到该 Exchange 的消息转发到所有绑定到他的队列中\n Direct/默认(empty string) 根据 routing_key 来决定发送到具体的队列去\n Topic binding key 可以带有匹配规则.\n Headers 不依赖 binding key 和 routing key, 只根据消息中的 headers 属性来匹配\n模式列表 参考: https://www.rabbitmq.com/getstarted.html\n 直连 上图展示了 Producer 与 Consumer 通过 Queue 直连, 实际上在 rabbitmq 中是不能直连的,必须通过 Exchange 指定 routingKey 才可以. 这里我们可以使用一个默认的 Exchange (空字符串) 来绕过限制.\n工作队列 直连 属于一对一的模式,工作队列 则属于一对多, 通常用于分发耗时任务给多个Consumer.可以提升响应效率.消息的分发策略是 轮询分发 .\n发布/订阅 发布订阅模型是 RabbitMQ 的核心模式. 我们大多数也是使用它来写业务.之前的 直连/工作队列 模式, 我们并没有用到 Exchange ,都是使用默认的空exchange.但是在 发布订阅 模式中, Producer 只会把消息发到 Exchange 中,不会关注是否会发送到队列, 由 Exchange 来决定.\n发布/订阅 中的 Exchange 类型为 Fanout, 所有发到 Exchange 上的消息都会再发到绑定在这个Exchange 上的所有队列中.\n路由模式 路由模式 采用 direct 类型的 Exchange 利用 binding key 来约束发送的队列.\nTopic Topic模式 利用模式匹配,以及 .的格式来按规则过滤. * 代表只有一个词, #代表 0 或 多个.\n","id":10,"section":"posts","summary":"\u003cp\u003erabbitmq有多种使用模式,在这里记录下不同模式的消息路由规则\u003c/p\u003e","tags":["rabbitmq"],"title":"RabbitMQ-消息分发机制","uri":"https://xiaohei.im/hugo-theme-pure/2019/09/rabbitmq-msg-distribution/","year":"2019"},{"content":" rabbitmq version: 3.7.15\n 常用操作 sbin/rabbitmq-server 启动 sbin/rabbitmq-server -detached 后台启动 sbin/rabbitmqctl shutdown/stop 关闭/停止server sbin/rabbitmqctl status 检查server状态 sbin/rabbitmq-plugins enable rabbitmq_management 开启控制台 端口 server启动后默认监听5672 控制台默认监听15672 构建集群 构建集群的方式 在config文件中声明节点信息 使用DNS发现 使用AWS实例发现(通过插件) 使用kubernetes发现(通过插件) 使用consul发现(通过插件) 使用etcd发现(通过插件) 手动执行rabbitmqctl 节点名称 节点名称是节点的身份识别证明.两部分组成: prefix \u0026amp; hostname.例如 rabbit@node1.messaging.svc.local的prefix是 rabbit ,hostname是 node1.messaging.svc.local.\n 集群中名称必须 唯一. 如果使用同一个hostname 那么prefix要保持不一致\n 集群中,节点通过节点名称互相进行识别和通信.所以hostname必须能解析.CLI Tools也要使用节点名称.\n 单机集群构建 单机运行多节点需要保证:\n 不同节点名称 \u0010RABBITMQ_NODENAME 不同存储路径 RABBITMQ_DIST_PORT 不同日志路径 RABBITMQ_LOG_BASE 不同端口,包括插件使用的 RABBITMQ_NODE_PORT rabbitmqctl 构建集群 RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit rabbitmq-server -detached RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=hare rabbitmq-server -detached # 重置正在运行的节点 rabbitmqctl -n hare stop_app # 加入集群 rabbitmqctl -n hare join_cluster rabbit@`hostname -s` rabbitmqctl -n hare start_app 每个节点若配置有其他的插件.那么每个节点插件监听的端口不能冲突,例如添加控制台\n# 首先开启控制台插件 ./rabbitmq-plugins enable rabbitmq_management RABBITMQ_NODE_PORT=5672 RABBITMQ_SERVER_START_ARGS=\u0026quot;-rabbitmq_management listener [{port,15672}]\u0026quot; RABBITMQ_NODENAME=rabbit ./rabbitmq-server -detached RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS=\u0026quot;-rabbitmq_management listener [{port,15673}]\u0026quot; RABBITMQ_NODENAME=hare ./rabbitmq-server -detached # 加入rabbit节点生成集群 rabbitmqctl -n hare stop_app # 加入集群 rabbitmqctl -n hare join_cluster rabbit@`hostname -s` rabbitmqctl -n hare start_app 以上就建了带控制台的两个节点.\n遇到的问题 添加节点进集群时,报错 ./rabbitmqctl -n rabbit2 join_cluster rabbit@`hostname -s` Clustering node rabbit2@localhost with rabbit@localhost Error: {:inconsistent_cluster, 'Node rabbit@localhost thinks it\\'s clustered with node rabbit2@localhost, but rabbit2@localhost disagrees'} 集群残留的cluster信息导致认证失败.删除${RABBIT_MQ_HOME}/var/lib/rabbitmq/mnesia文件夹.再reset节点\n建集群报错 Clustering node rabbit2@localhost with rabbit@localhost Error: {:corrupt_or_missing_cluster_files, {:error, :enoent}, {:error, :enoent}} 同上\n启动第三个节点时爆端口占用,该端口是第一个节点的控制台端口15672.没有解决 2019-09-05 15:35:42.749 [error] \u0026lt;0.555.0\u0026gt; Failed to start Ranch listener rabbit_web_dispatch_sup_15672 in ranch_tcp:listen([{cacerts,'...'},{key,'...'},{cert,'...'},{port,15672}]) for reason eaddrinuse (address already in use) 使用案例 Topic Exchange topic类型的exchange ,routing key 是按一定规则来的,通过.连接,类似于正则.有两种符号:\n * 代表一个单词 # 代表0或多个单词 如果 单单只有#号, 那么topic exchange就像fanout exchange,如果没有使用*和#,那就是direct exchange了.\n参考 docker hub rabbit mq 镜像 ","id":11,"section":"posts","summary":"","tags":["rabbitmq"],"title":"RabbitMQ-入门及高可用集群部署","uri":"https://xiaohei.im/hugo-theme-pure/2019/09/rabbitmq-guide-and-ha-cluster/","year":"2019"},{"content":" 转载自 https://rabbitmq.mr-ping.com/AMQP/AMQP_0-9-1_Model_Explained.html\n AMQP 0-9-1 和 AMQP 模型高阶概述 AMQP是什么 AMQP(高级消息队列协议)是一个网络协议。它支持符合要求的客户端应用(application)和消息中间件代理(messaging middleware broker)之间进行通信。\n消息代理和他们所扮演的角色 消息代理(message brokers)从发布者(publishers)亦称生产者(producers)那儿接收消息,并根据既定的路由规则把接收到的消息发送给处理消息的消费者(consumers)。\n由于AMQP是一个网络协议,所以这个过程中的发布者,消费者,消息代理 可以存在于不同的设备上。\nAMQP 0-9-1 模型简介 AMQP 0-9-1的工作过程如下图:消息(message)被发布者(publisher)发送给交换机(exchange),交换机常常被比喻成邮局或者邮箱。然后交换机将收到的消息根据路由规则分发给绑定的队列(queue)。最后AMQP代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取。\n\n发布者(publisher)发布消息时可以给消息指定各种消息属性(message meta-data)。有些属性有可能会被消息代理(brokers)使用,然而其他的属性则是完全不透明的,它们只能被接收消息的应用所使用。\n从安全角度考虑,网络是不可靠的,接收消息的应用也有可能在处理消息的时候失败。基于此原因,AMQP模块包含了一个消息确认(message acknowledgements)的概念:当一个消息从队列中投递给消费者后(consumer),消费者会通知一下消息代理(broker),这个可以是自动的也可以由处理消息的应用的开发者执行。当“消息确认”被启用的时候,消息代理不会完全将消息从队列中删除,直到它收到来自消费者的确认回执(acknowledgement)。\n在某些情况下,例如当一个消息无法被成功路由时,消息或许会被返回给发布者并被丢弃。或者,如果消息代理执行了延期操作,消息会被放入一个所谓的死信队列中。此时,消息发布者可以选择某些参数来处理这些特殊情况。\n队列,交换机和绑定统称为AMQP实体(AMQP entities)。\nAMQP是一个可编程的协议 AMQP 0-9-1是一个可编程协议,某种意义上说AMQP的实体和路由规则是由应用本身定义的,而不是由消息代理定义。包括像声明队列和交换机,定义他们之间的绑定,订阅队列等等关于协议本身的操作。\n这虽然能让开发人员自由发挥,但也需要他们注意潜在的定义冲突。当然这在实践中很少会发生,如果发生,会以配置错误(misconfiguration)的形式表现出来。\n应用程序(Applications)声明AMQP实体,定义需要的路由方案,或者删除不再需要的AMQP实体。\n交换机和交换机类型 交换机是用来发送消息的AMQP实体。交换机拿到一个消息之后将它路由给一个或零个队列。它使用哪种路由算法是由交换机类型和被称作绑定(bindings)的规则所决定的。AMQP 0-9-1的代理提供了四种交换机\n Name(交换机类型) Default pre-declared names(预声明的默认名称) Direct exchange(直连交换机) (Empty string) and amq.direct Fanout exchange(扇型交换机) amq.fanout Topic exchange(主题交换机) amq.topic Headers exchange(头交换机) amq.match (and amq.headers in RabbitMQ) 除交换机类型外,在声明交换机时还可以附带许多其他的属性,其中最重要的几个分别是:\n Name Durability (消息代理重启后,交换机是否还存在) Auto-delete (当所有与之绑定的消息队列都完成了对此交换机的使用后,删掉它) Arguments(依赖代理本身) 交换机可以有两个状态:持久(durable)、暂存(transient)。持久化的交换机会在消息代理(broker)重启后依旧存在,而暂存的交换机则不会(它们需要在代理再次上线后重新被声明)。然而并不是所有的应用场景都需要持久化的交换机。\n默认交换机 默认交换机(default exchange)实际上是一个由消息代理预先声明好的没有名字(名字为空字符串)的直连交换机(direct exchange)。它有一个特殊的属性使得它对于简单应用特别有用处:那就是每个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(routing key)名称与队列名称相同。\n举个栗子:当你声明了一个名为\u0026quot;search-indexing-online\u0026quot;的队列,AMQP代理会自动将其绑定到默认交换机上,绑定(binding)的路由键名称也是为\u0026quot;search-indexing-online\u0026quot;。因此,当携带着名为\u0026quot;search-indexing-online\u0026quot;的路由键的消息被发送到默认交换机的时候,此消息会被默认交换机路由至名为\u0026quot;search-indexing-online\u0026quot;的队列中。换句话说,默认交换机看起来貌似能够直接将消息投递给队列,尽管技术上并没有做相关的操作。\n直连交换机 直连型交换机(direct exchange)是根据消息携带的路由键(routing key)将消息投递给对应队列的。直连交换机用来处理消息的单播路由(unicast routing)(尽管它也可以处理多播路由)。下边介绍它是如何工作的:\n 将一个队列绑定到某个交换机上,同时赋予该绑定一个路由键(routing key) 当一个携带着路由键为R的消息被发送给直连交换机时,交换机会把它路由给绑定值同样为R的队列。 直连交换机经常用来循环分发任务给多个工作者(workers)。当这样做的时候,我们需要明白一点,在AMQP 0-9-1中,消息的负载均衡是发生在消费者(consumer)之间的,而不是队列(queue)之间。\n直连型交换机图例: \n扇型交换机 扇型交换机(funout exchange)将消息路由给绑定到它身上的所有队列,而不理会绑定的路由键。如果N个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的N个队列。扇型用来交换机处理消息的广播路由(broadcast routing)。\n因为扇型交换机投递消息的拷贝到所有绑定到它的队列,所以他的应用案例都极其相似:\n 大规模多用户在线(MMO)游戏可以使用它来处理排行榜更新等全局事件 体育新闻网站可以用它来近乎实时地将比分更新分发给移动客户端 分发系统使用它来广播各种状态和配置更新 在群聊的时候,它被用来分发消息给参与群聊的用户。(AMQP没有内置presence的概念,因此XMPP可能会是个更好的选择) 扇型交换机图例: \n主题交换机 主题交换机(topic exchanges)通过对消息的路由键和队列到交换机的绑定模式之间的匹配,将消息路由给一个或多个队列。主题交换机经常用来实现各种分发/订阅模式及其变种。主题交换机通常用来实现消息的多播路由(multicast routing)。\n主题交换机拥有非常广泛的用户案例。无论何时,当一个问题涉及到那些想要有针对性的选择需要接收消息的 多消费者/多应用(multiple consumers/applications) 的时候,主题交换机都可以被列入考虑范围。\n使用案例:\n 分发有关于特定地理位置的数据,例如销售点 由多个工作者(workers)完成的后台任务,每个工作者负责处理某些特定的任务 股票价格更新(以及其他类型的金融数据更新) 涉及到分类或者标签的新闻更新(例如,针对特定的运动项目或者队伍) 云端的不同种类服务的协调 分布式架构/基于系统的软件封装,其中每个构建者仅能处理一个特定的架构或者系统。 头交换机 有时消息的路由操作会涉及到多个属性,此时使用消息头就比用路由键更容易表达,头交换机(headers exchange)就是为此而生的。头交换机使用多个消息属性来代替路由键建立路由规则。通过判断消息头的值能否与指定的绑定相匹配来确立路由规则。\n我们可以绑定一个队列到头交换机上,并给他们之间的绑定使用多个用于匹配的头(header)。这个案例中,消息代理得从应用开发者那儿取到更多一段信息,换句话说,它需要考虑某条消息(message)是需要部分匹配还是全部匹配。上边说的“更多一段消息”就是\u0026quot;x-match\u0026quot;参数。当\u0026quot;x-match\u0026quot;设置为“any”时,消息头的任意一个值被匹配就可以满足条件,而当\u0026quot;x-match\u0026quot;设置为“all”的时候,就需要消息头的所有值都匹配成功。\n头交换机可以视为直连交换机的另一种表现形式。头交换机能够像直连交换机一样工作,不同之处在于头交换机的路由规则是建立在头属性值之上,而不是路由键。路由键必须是一个字符串,而头属性值则没有这个约束,它们甚至可以是整数或者哈希值(字典)等。\n队列 AMQP中的队列(queue)跟其他消息队列或任务队列中的队列是很相似的:它们存储着即将被应用消费掉的消息。队列跟交换机共享某些属性,但是队列也有一些另外的属性。\n Name Durable(消息代理重启后,队列依旧存在) Exclusive(只被一个连接(connection)使用,而且当连接关闭后队列即被删除) Auto-delete(当最后一个消费者退订后即被删除) Arguments(一些消息代理用他来完成类似与TTL的某些额外功能) 队列在声明(declare)后才能被使用。如果一个队列尚不存在,声明一个队列会创建它。如果声明的队列已经存在,并且属性完全相同,那么此次声明不会对原有队列产生任何影响。如果声明中的属性与已存在队列的属性有差异,那么一个错误代码为406的通道级异常就会被抛出。\n队列名称 队列的名字可以由应用(application)来取,也可以让消息代理(broker)直接生成一个。队列的名字可以是最多255字节的一个utf-8字符串。若希望AMQP消息代理生成队列名,需要给队列的name参数赋值一个空字符串:在同一个通道(channel)的后续的方法(method)中,我们可以使用空字符串来表示之前生成的队列名称。之所以之后的方法可以获取正确的队列名是因为通道可以默默地记住消息代理最后一次生成的队列名称。\n以\u0026quot;amq.\u0026quot;开始的队列名称被预留做消息代理内部使用。如果试图在队列声明时打破这一规则的话,一个通道级的403 (ACCESS_REFUSED)错误会被抛出。\n队列持久化 持久化队列(Durable queues)会被存储在磁盘上,当消息代理(broker)重启的时候,它依旧存在。没有被持久化的队列称作暂存队列(Transient queues)。并不是所有的场景和案例都需要将队列持久化。\n持久化的队列并不会使得路由到它的消息也具有持久性。倘若消息代理挂掉了,重新启动,那么在重启的过程中持久化队列会被重新声明,无论怎样,只有经过持久化的消息才能被重新恢复。\n绑定 绑定(Binding)是交换机(exchange)将消息(message)路由给队列(queue)所需遵循的规则。如果要指示交换机“E”将消息路由给队列“Q”,那么“Q”就需要与“E”进行绑定。绑定操作需要定义一个可选的路由键(routing key)属性给某些类型的交换机。路由键的意义在于从发送给交换机的众多消息中选择出某些消息,将其路由给绑定的队列。\n打个比方:\n 队列(queue)是我们想要去的位于纽约的目的地 交换机(exchange)是JFK机场 绑定(binding)就是JFK机场到目的地的路线。能够到达目的地的路线可以是一条或者多条 拥有了交换机这个中间层,很多由发布者直接到队列难以实现的路由方案能够得以实现,并且避免了应用开发者的许多重复劳动。\n如果AMQP的消息无法路由到队列(例如,发送到的交换机没有绑定队列),消息会被就地销毁或者返还给发布者。如何处理取决于发布者设置的消息属性。\n消费者 消息如果只是存储在队列里是没有任何用处的。被应用消费掉,消息的价值才能够体现。在AMQP 0-9-1 模型中,有两种途径可以达到此目的:\n 将消息投递给应用 (\u0026quot;push API\u0026quot;) 应用根据需要主动获取消息 (\u0026quot;pull API\u0026quot;) 使用push API,应用(application)需要明确表示出它在某个特定队列里所感兴趣的,想要消费的消息。如是,我们可以说应用注册了一个消费者,或者说订阅了一个队列。一个队列可以注册多个消费者,也可以注册一个独享的消费者(当独享消费者存在时,其他消费者即被排除在外)。\n每个消费者(订阅者)都有一个叫做消费者标签的标识符。它可以被用来退订消息。消费者标签实际上是一个字符串。\n消息确认 消费者应用(Consumer applications) - 用来接受和处理消息的应用 - 在处理消息的时候偶尔会失败或者有时会直接崩溃掉。而且网络原因也有可能引起各种问题。这就给我们出了个难题,AMQP代理在什么时候删除消息才是正确的?AMQP 0-9-1 规范给我们两种建议:\n 当消息代理(broker)将消息发送给应用后立即删除。(使用AMQP方法:basic.deliver或basic.get-ok) 待应用(application)发送一个确认回执(acknowledgement)后再删除消息。(使用AMQP方法:basic.ack) 前者被称作自动确认模式(automatic acknowledgement model),后者被称作显式确认模式(explicit acknowledgement model)。在显式模式下,由消费者应用来选择什么时候发送确认回执(acknowledgement)。应用可以在收到消息后立即发送,或将未处理的消息存储后发送,或等到消息被处理完毕后再发送确认回执(例如,成功获取一个网页内容并将其存储之后)。\n如果一个消费者在尚未发送确认回执的情况下挂掉了,那AMQP代理会将消息重新投递给另一个消费者。如果当时没有可用的消费者了,消息代理会死等下一个注册到此队列的消费者,然后再次尝试投递。\n拒绝消息 当一个消费者接收到某条消息后,处理过程有可能成功,有可能失败。应用可以向消息代理表明,本条消息由于“拒绝消息(Rejecting Messages)”的原因处理失败了(或者未能在此时完成)。当拒绝某条消息时,应用可以告诉消息代理如何处理这条消息——销毁它或者重新放入队列。当此队列只有一个消费者时,请确认不要由于拒绝消息并且选择了重新放入队列的行为而引起消息在同一个消费者身上无限循环的情况发生。\nNegative Acknowledgements 在AMQP中,basic.reject方法用来执行拒绝消息的操作。但basic.reject有个限制:你不能使用它决绝多个带有确认回执(acknowledgements)的消息。但是如果你使用的是RabbitMQ,那么你可以使用被称作negative acknowledgements(也叫nacks)的AMQP 0-9-1扩展来解决这个问题。更多的信息请参考帮助页面\n预取消息 在多个消费者共享一个队列的案例中,明确指定在收到下一个确认回执前每个消费者一次可以接受多少条消息是非常有用的。这可以在试图批量发布消息的时候起到简单的负载均衡和提高消息吞吐量的作用。For example, if a producing application sends messages every minute because of the nature of the work it is doing.(???例如,如果生产应用每分钟才发送一条消息,这说明处理工作尚在运行。)\n注意,RabbitMQ只支持通道级的预取计数,而不是连接级的或者基于大小的预取。\n消息属性和有效载荷(消息主体) AMQP模型中的消息(Message)对象是带有属性(Attributes)的。有些属性及其常见,以至于AMQP 0-9-1 明确的定义了它们,并且应用开发者们无需费心思思考这些属性名字所代表的具体含义。例如:\n Content type(内容类型) Content encoding(内容编码) Routing key(路由键) Delivery mode (persistent or not) 投递模式(持久化 或 非持久化) Message priority(消息优先权) Message publishing timestamp(消息发布的时间戳) Expiration period(消息有效期) Publisher application id(发布应用的ID) 有些属性是被AMQP代理所使用的,但是大多数是开放给接收它们的应用解释器用的。有些属性是可选的也被称作消息头(headers)。他们跟HTTP协议的X-Headers很相似。消息属性需要在消息被发布的时候定义。\nAMQP的消息除属性外,也含有一个有效载荷 - Payload(消息实际携带的数据),它被AMQP代理当作不透明的字节数组来对待。消息代理不会检查或者修改有效载荷。消息可以只包含属性而不携带有效载荷。它通常会使用类似JSON这种序列化的格式数据,为了节省,协议缓冲器和MessagePack将结构化数据序列化,以便以消息的有效载荷的形式发布。AMQP及其同行者们通常使用\u0026quot;content-type\u0026quot; 和 \u0026quot;content-encoding\u0026quot; 这两个字段来与消息沟通进行有效载荷的辨识工作,但这仅仅是基于约定而已。\n消息能够以持久化的方式发布,AMQP代理会将此消息存储在磁盘上。如果服务器重启,系统会确认收到的持久化消息未丢失。简单地将消息发送给一个持久化的交换机或者路由给一个持久化的队列,并不会使得此消息具有持久化性质:它完全取决与消息本身的持久模式(persistence mode)。将消息以持久化方式发布时,会对性能造成一定的影响(就像数据库操作一样,健壮性的存在必定造成一些性能牺牲)。\n消息确认 由于网络的不确定性和应用失败的可能性,处理确认回执(acknowledgement)就变的十分重要。有时我们确认消费者收到消息就可以了,有时确认回执意味着消息已被验证并且处理完毕,例如对某些数据已经验证完毕并且进行了数据存储或者索引操作。\n这种情形很常见,所以 AMQP 0-9-1 内置了一个功能叫做 消息确认(message acknowledgements),消费者用它来确认消息已经被接收或者处理。如果一个应用崩溃掉(此时连接会断掉,所以AMQP代理亦会得知),而且消息的确认回执功能已经被开启,但是消息代理尚未获得确认回执,那么消息会被从新放入队列(并且在还有还有其他消费者存在于此队列的前提下,立即投递给另外一个消费者)。\n协议内置的消息确认功能将帮助开发者建立强大的软件。\nAMQP 0-9-1 方法 AMQP 0-9-1由许多方法(methods)构成。方法即是操作,这跟面向对象编程中的方法没半毛钱关系。AMQP的方法被分组在类(class)中。这里的类仅仅是对AMQP方法的逻辑分组而已。在 AMQP 0-9-1参考中有对AMQP方法的详细介绍。\n让我们来看看交换机类,有一组方法被关联到了交换机的操作上。这些方法如下所示:\n exchange.declare exchange.declare-ok exchange.delete exchange.delete-ok (请注意,RabbitMQ网站参考中包含了特用于RabbitMQ的交换机类的扩展,这里我们不对其进行讨论)\n以上的操作来自逻辑上的配对:exchange.declare 和 exchange.declare-ok,exchange.delete 和 exchange.delete-ok. 这些操作分为“请求 - requests”(由客户端发送)和“响应 - responses”(由代理发送,用来回应之前提到的“请求”操作)。\n如下的例子:客户端要求消息代理使用exchange.declare方法声明一个新的交换机: \n如上图所示,exchange.declare方法携带了好几个参数。这些参数可以允许客户端指定交换机名称、类型、是否持久化等等。\n操作成功后,消息代理使用exchange.declare-ok方法进行回应: \nexchange.declare-ok方法除了通道号之外没有携带任何其他参数(通道-channel 会在本指南稍后章节进行介绍)。\nAMQP队列类的配对方法 - queue.declare方法 和 queue.declare-ok有着与其他配对方法非常相似的一系列事件: \n\n不是所有的AMQP方法都有与其配对的“另一半”。许多(basic.publish是最被广泛使用的)都没有相对应的“响应”方法,另外一些(如basic.get)有着一种以上与之对应的“响应”方法。\n连接 AMQP连接通常是长连接。AMQP是一个使用TCP提供可靠投递的应用层协议。AMQP使用认证机制并且提供TLS(SSL)保护。当一个应用不再需要连接到AMQP代理的时候,需要优雅的释放掉AMQP连接,而不是直接将TCP连接关闭。\n通道 有些应用需要与AMQP代理建立多个连接。无论怎样,同时开启多个TCP连接都是不合适的,因为这样做会消耗掉过多的系统资源并且使得防火墙的配置更加困难。AMQP 0-9-1提供了通道(channels)来处理多连接,可以把通道理解成共享一个TCP连接的多个轻量化连接。\n在涉及多线程/进程的应用中,为每个线程/进程开启一个通道(channel)是很常见的,并且这些通道不能被线程/进程共享。\n一个特定通道上的通讯与其他通道上的通讯是完全隔离的,因此每个AMQP方法都需要携带一个通道号,这样客户端就可以指定此方法是为哪个通道准备的。\n虚拟主机 为了在一个单独的代理上实现多个隔离的环境(用户、用户组、交换机、队列 等),AMQP提供了一个虚拟主机(virtual hosts - vhosts)的概念。这跟Web servers虚拟主机概念非常相似,这为AMQP实体提供了完全隔离的环境。当连接被建立的时候,AMQP客户端来指定使用哪个虚拟主机。\nAMQP是可扩展的 AMQP 0-9-1 拥有多个扩展点:\n 定制化交换机类型 可以让开发者们实现一些开箱即用的交换机类型尚未很好覆盖的路由方案。例如 geodata-based routing。 交换机和队列的声明中可以包含一些消息代理能够用到的额外属性。例如RabbitMQ中的per-queue message TTL即是使用该方式实现。 特定消息代理的协议扩展。例如RabbitMQ所实现的扩展。 新的 AMQP 0-9-1 方法类可被引入。 消息代理可以被其他的插件扩展,例如RabbitMQ的管理前端 和 已经被插件化的HTTP API。 这些特性使得AMQP 0-9-1模型更加灵活,并且能够适用于解决更加宽泛的问题。\nAMQP 0-9-1 客户端生态系统 AMQP 0-9-1 拥有众多的适用于各种流行语言和框架的客户端。其中一部分严格遵循AMQP规范,提供AMQP方法的实现。另一部分提供了额外的技术,方便使用的方法和抽象。有些客户端是异步的(非阻塞的),有些是同步的(阻塞的),有些将这两者同时实现。有些客户端支持“供应商的特定扩展”(例如RabbitMQ的特定扩展)。\n因为AMQP的主要目标之一就是实现交互性,所以对于开发者来讲,了解协议的操作方法而不是只停留在弄懂特定客户端的库就显得十分重要。这样一来,开发者使用不同类型的库与协议进行沟通时就会容易的多。\n","id":12,"section":"posts","summary":"","tags":["rabbitmq"],"title":"AMQP消息模型","uri":"https://xiaohei.im/hugo-theme-pure/2019/09/amqp-0-9-1-model-explained/","year":"2019"},{"content":"前言 Hystrix已经不在维护了,但是成功的开源项目总是值得学习的.刚开始看 Hystrix 源码时,会发现一堆 Action,Function 的逻辑,这其实就是 RxJava 的特点了\u0026ndash;响应式编程.上篇文章已经对RxJava作过入门介绍,不熟悉的同学可以先去看看.本文会简单介绍 Hystrix,再根据demo结合源码来了解Hystrix的执行流程.\nHystrix简单介绍 什么是 Hystrix?\nHystrix 是一个延迟和容错库,旨在隔离对远程系统、服务和第三方库的访问点,停止级联故障,并在错误不可避免的复杂分布式系统中能够弹性恢复。\n 核心概念\n Command 命令\nCommand 是Hystrix的入口,对用户来说,我们只需要创建对应的 command,将需要保护的接口包装起来就可以.可以无需关注再之后的逻辑.与 Spring 深度集成后还可以通过注解的方式,就更加对开发友好了.\n Circuit Breaker 断路器\n断路器,是从电气领域引申过来的概念,具有过载、短路和欠电压保护功能,有保护线路和电源的能力.在Hystrix中即为当请求超过一定比例响应失败时,hystrix 会对请求进行拦截处理,保证服务的稳定性,以及防止出现服务之间级联雪崩的可能性.\n Isolation 隔离策略\n隔离策略是 Hystrix 的设计亮点所在,利用舱壁模式的思想来对访问的资源进行隔离,每个资源是独立的依赖,单个资源的异常不应该影响到其他. Hystrix 的隔离策略目前有两种:线程池隔离,信号量隔离.\n Hystrix的运行流程\n 官方的 How it Works 对流程有很详细的介绍,图示清晰,相信看完流程图就能对运行流程有一定的了解.\n 一次Command执行 HystrixCommand是标准的命令模式实现,每一次请求即为一次命令的创建执行经历的过程.从上述Hystrix流程图可以看出创建流程最终会指向toObservable,在之前RxJava入门时有介绍到Observable即为被观察者,作用是发送数据给观察者进行相应的,因此可以知道这个方法应该是较为关键的.\nUML HystrixInvokable 标记这个一个可执行的接口,没有任何抽象方法或常量 HystrixExecutable 是为HystrixCommand设计的接口,主要提供执行命令的抽象方法,例如:execute(),queue(),observe() HystrixObservable 是为Observable设计的接口,主要提供自动订阅(observe())和生成Observable(toObservable())的抽象方法 HystrixInvokableInfo 提供大量的状态查询(获取属性配置,是否开启断路器等) AbstractCommand 核心逻辑的实现 HystrixCommand 定制逻辑实现以及留给用户实现的接口(比如:run()) 样例代码 通过新建一个 command 来看 Hystrix 是如何创建并执行的.HystrixCommand 是一个抽象类,其中有一个run方法需要我们实现自己的业务逻辑,以下是偷懒采用匿名内部类的形式呈现.构造方法的内部实现我们就不关注了,直接看下执行的逻辑吧.\nHystrixCommand demo = new HystrixCommand\u0026lt;String\u0026gt;(HystrixCommandGroupKey.Factory.asKey(\u0026quot;demo-group\u0026quot;)) { @Override protected String run() { return \u0026quot;Hello World~\u0026quot;; } }; demo.execute(); 执行过程 流程图 这是官方给出的一次完整调用的链路.上述的 demo 中我们直接调用了execute方法,所以调用的路径为execute() -\u0026gt; queue() -\u0026gt; toObservable() -\u0026gt; toBlocking() -\u0026gt; toFuture() -\u0026gt; get().核心的逻辑其实就在toObservable()中.\nHystrixCommand.java execute execute方法为同步调用返回结果,并对异常作处理.内部会调用queue\n// 同步调用执行 public R execute() { try { // queue()返回的是Future类型的对象,所以这里是阻塞get return queue().get(); } catch (Exception e) { throw decomposeException(e); } } queue queue的第一行代码完成了核心的订阅逻辑.\n toObservable() 生成了 Hystrix 的 Observable 对象 将 Observable 转换为 BlockingObservable 可以阻塞控制数据发送 toFuture 实现对 BlockingObservable 的订阅\npublic Future\u0026lt;R\u0026gt; queue() { // 着重关注的是这行代码 // 完成了Observable的创建及订阅 // toBlocking()是将Observable转为BlockingObservable,转换后的Observable可以阻塞数据的发送 final Future\u0026lt;R\u0026gt; delegate = toObservable().toBlocking().toFuture(); final Future\u0026lt;R\u0026gt; f = new Future\u0026lt;R\u0026gt;() { // 由于toObservable().toBlocking().toFuture()返回的Future如果中断了, // 不会对当前线程进行中断,所以这里将返回的Future进行了再次包装,处理异常逻辑 ... } // 判断是否已经结束了,有异常则直接抛出 if (f.isDone()) { try { f.get(); return f; } catch (Exception e) { // 省略这段判断 } } return f; } BlockingObservable.java // 被包装的Observable private final Observable\u0026lt;? extends T\u0026gt; o; // toBlocking()会调用该静态方法将 源Observable简单包装成BlockingObservable public static \u0026lt;T\u0026gt; BlockingObservable\u0026lt;T\u0026gt; from(final Observable\u0026lt;? extends T\u0026gt; o) { return new BlockingObservable\u0026lt;T\u0026gt;(o); } public Future\u0026lt;T\u0026gt; toFuture() { return BlockingOperatorToFuture.toFuture((Observable\u0026lt;T\u0026gt;)o); } BlockingOperatorToFuture.java ReactiveX 关于toFuture的解读\nThe toFuture operator applies to the BlockingObservable subclass, so in order to use it, you must first convert your source Observable into a BlockingObservable by means of either the BlockingObservable.from method or the Observable.toBlocking operator.\n toFuture只能作用于BlockingObservable所以也才会有上文想要转换为BlockingObservable的操作\n// 该操作将 源Observable转换为返回单个数据项的Future public static \u0026lt;T\u0026gt; Future\u0026lt;T\u0026gt; toFuture(Observable\u0026lt;? extends T\u0026gt; that) { // CountDownLatch 判断是否完成 final CountDownLatch finished = new CountDownLatch(1); // 存储执行结果 final AtomicReference\u0026lt;T\u0026gt; value = new AtomicReference\u0026lt;T\u0026gt;(); // 存储错误结果 final AtomicReference\u0026lt;Throwable\u0026gt; error = new AtomicReference\u0026lt;Throwable\u0026gt;(); // single()方法可以限制Observable只发送单条数据 // 如果有多条数据 会抛 IllegalArgumentException // 如果没有数据可以发送 会抛 NoSuchElementException @SuppressWarnings(\u0026quot;unchecked\u0026quot;) final Subscription s = ((Observable\u0026lt;T\u0026gt;)that).single().subscribe(new Subscriber\u0026lt;T\u0026gt;() { // single()返回的Observable就可以对其进行标准的处理了 @Override public void onCompleted() { finished.countDown(); } @Override public void onError(Throwable e) { error.compareAndSet(null, e); finished.countDown(); } @Override public void onNext(T v) { // \u0026quot;single\u0026quot; guarantees there is only one \u0026quot;onNext\u0026quot; value.set(v); } }); // 最后将Subscription返回的数据封装成Future,实现对应的逻辑 return new Future\u0026lt;T\u0026gt;() { // 可以查看源码 }; } AbstractCommand.java AbstractCommand是toObservable实现的地方,属于Hystrix的核心逻辑,代码较长,可以和方法调用的流程图一起食用.toObservable主要是完成缓存和创建Observable,requestLog的逻辑,当第一次创建Observable时,applyHystrixSemantics方法是Hystrix的语义实现,可以跳着看.\n tips: 下文中有很多 Action和 Function,他们很相似,都有call方法,但是区别在于Function有返回值,而Action没有,方法后跟着的数字代表有几个入参.Func0/Func3即没有入参和有三个入参\n toObservable toObservable代码较长且分层还是清晰的,所以下面一块一块写.其逻辑和文章开始提到的Hystrix流程图是完全一致的.\npublic Observable\u0026lt;R\u0026gt; toObservable() { final AbstractCommand\u0026lt;R\u0026gt; _cmd = this; // 此处省略掉了很多个Action和Function,大部分是来做扫尾清理的函数,所以用到的时候再说 // defer在上篇rxjava入门中提到过,是一种创建型的操作符,每次订阅时会产生新的Observable,回调方法中所实现的才是真正我们需要的Observable return Observable.defer(new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call() { // 校验命令的状态,保证其只执行一次 if (!commandState.compareAndSet(CommandState.NOT_STARTED, CommandState.OBSERVABLE_CHAIN_CREATED)) { IllegalStateException ex = new IllegalStateException(\u0026quot;This instance can only be executed once. Please instantiate a new instance.\u0026quot;); //TODO make a new error type for this throw new HystrixRuntimeException(FailureType.BAD_REQUEST_EXCEPTION, _cmd.getClass(), getLogMessagePrefix() + \u0026quot; command executed multiple times - this is not permitted.\u0026quot;, ex, null); } commandStartTimestamp = System.currentTimeMillis(); // properties为当前command的所有属性 // 允许记录请求log时会保存当前执行的command if (properties.requestLogEnabled().get()) { // log this command execution regardless of what happened if (currentRequestLog != null) { currentRequestLog.addExecutedCommand(_cmd); } } // 是否开启了请求缓存 final boolean requestCacheEnabled = isRequestCachingEnabled(); // 获取缓存key final String cacheKey = getCacheKey(); // 开启缓存后,尝试从缓存中取 if (requestCacheEnabled) { HystrixCommandResponseFromCache\u0026lt;R\u0026gt; fromCache = (HystrixCommandResponseFromCache\u0026lt;R\u0026gt;) requestCache.get(cacheKey); if (fromCache != null) { isResponseFromCache = true; return handleRequestCacheHitAndEmitValues(fromCache, _cmd); } } // 没有开启请求缓存时,就执行正常的逻辑 Observable\u0026lt;R\u0026gt; hystrixObservable = // 这里又通过defer创建了我们需要的Observable Observable.defer(applyHystrixSemantics) // 发送前会先走一遍hook,默认executionHook是空实现的,所以这里就跳过了 .map(wrapWithAllOnNextHooks); // 得到最后的封装好的Observable后,将其放入缓存 if (requestCacheEnabled \u0026amp;\u0026amp; cacheKey != null) { // wrap it for caching HystrixCachedObservable\u0026lt;R\u0026gt; toCache = HystrixCachedObservable.from(hystrixObservable, _cmd); HystrixCommandResponseFromCache\u0026lt;R\u0026gt; fromCache = (HystrixCommandResponseFromCache\u0026lt;R\u0026gt;) requestCache.putIfAbsent(cacheKey, toCache); if (fromCache != null) { // another thread beat us so we'll use the cached value instead toCache.unsubscribe(); isResponseFromCache = true; return handleRequestCacheHitAndEmitValues(fromCache, _cmd); } else { // we just created an ObservableCommand so we cast and return it afterCache = toCache.toObservable(); } } else { afterCache = hystrixObservable; } return afterCache // 终止时的操作 .doOnTerminate(terminateCommandCleanup) // perform cleanup once (either on normal terminal state (this line), or unsubscribe (next line)) // 取消订阅时的操作 .doOnUnsubscribe(unsubscribeCommandCleanup) // perform cleanup once // 完成时的操作 .doOnCompleted(fireOnCompletedHook); } } handleRequestCacheHitAndEmitValues 缓存击中时的处理\nprivate Observable\u0026lt;R\u0026gt; handleRequestCacheHitAndEmitValues(final HystrixCommandResponseFromCache\u0026lt;R\u0026gt; fromCache, final AbstractCommand\u0026lt;R\u0026gt; _cmd) { try { // Hystrix中有大量的hook 如果有心做二次开发的,可以利用这些hook做到很完善的监控 executionHook.onCacheHit(this); } catch (Throwable hookEx) { logger.warn(\u0026quot;Error calling HystrixCommandExecutionHook.onCacheHit\u0026quot;, hookEx); } // 将缓存的结果赋给当前command return fromCache.toObservableWithStateCopiedInto(this) // doOnTerminate 或者是后面看到的doOnUnsubscribe,doOnError,都指的是在响应onTerminate/onUnsubscribe/onError后的操作,即在Observable的生命周期上注册一个动作优雅的处理逻辑 .doOnTerminate(new Action0() { @Override public void call() { // 命令最终状态的不同进行不同处理 if (commandState.compareAndSet(CommandState.OBSERVABLE_CHAIN_CREATED, CommandState.TERMINAL)) { cleanUpAfterResponseFromCache(false); //user code never ran } else if (commandState.compareAndSet(CommandState.USER_CODE_EXECUTED, CommandState.TERMINAL)) { cleanUpAfterResponseFromCache(true); //user code did run } } }) .doOnUnsubscribe(new Action0() { @Override public void call() { // 命令最终状态的不同进行不同处理 if (commandState.compareAndSet(CommandState.OBSERVABLE_CHAIN_CREATED, CommandState.UNSUBSCRIBED)) { cleanUpAfterResponseFromCache(false); //user code never ran } else if (commandState.compareAndSet(CommandState.USER_CODE_EXECUTED, CommandState.UNSUBSCRIBED)) { cleanUpAfterResponseFromCache(true); //user code did run } } }); } applyHystrixSemantics 因为本片文章的主要目的是在讲执行流程,所以失败回退和断路器相关的就留到以后的文章中再写.\nfinal Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt; applyHystrixSemantics = new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call() { // 不再订阅了就返回不发送数据的Observable if (commandState.get().equals(CommandState.UNSUBSCRIBED)) { // 不发送任何数据或通知 return Observable.never(); } return applyHystrixSemantics(_cmd); } }; private Observable\u0026lt;R\u0026gt; applyHystrixSemantics(final AbstractCommand\u0026lt;R\u0026gt; _cmd) { // 标记开始执行的hook // 如果hook内抛异常了,会快速失败且没有fallback处理 executionHook.onStart(_cmd); /* determine if we're allowed to execute */ // 断路器核心逻辑: 判断是否允许执行(TODO) if (circuitBreaker.allowRequest()) { // Hystrix自己造的信号量轮子,之所以不用juc下,官方解释为juc的Semphore实现太复杂,而且没有动态调节的信号量大小的能力,简而言之,不满足需求! // 根据不同隔离策略(线程池隔离/信号量隔离)获取不同的TryableSemphore final TryableSemaphore executionSemaphore = getExecutionSemaphore(); // Semaphore释放标志 final AtomicBoolean semaphoreHasBeenReleased = new AtomicBoolean(false); // 释放信号量的Action final Action0 singleSemaphoreRelease = new Action0() { @Override public void call() { if (semaphoreHasBeenReleased.compareAndSet(false, true)) { executionSemaphore.release(); } } }; // 异常处理 final Action1\u0026lt;Throwable\u0026gt; markExceptionThrown = new Action1\u0026lt;Throwable\u0026gt;() { @Override public void call(Throwable t) { // HystrixEventNotifier是hystrix的插件,不同的事件发送不同的通知,默认是空实现. eventNotifier.markEvent(HystrixEventType.EXCEPTION_THROWN, commandKey); } }; // 线程池隔离的TryableSemphore始终为true if (executionSemaphore.tryAcquire()) { try { /* used to track userThreadExecutionTime */ // executionResult是一次命令执行的结果信息封装 // 这里设置起始时间是为了记录命令的生命周期,执行过程中会set其他属性进去 executionResult = executionResult.setInvocationStartTime(System.currentTimeMillis()); return executeCommandAndObserve(_cmd) // 报错时的处理 .doOnError(markExceptionThrown) // 终止时释放 .doOnTerminate(singleSemaphoreRelease) // 取消订阅时释放 .doOnUnsubscribe(singleSemaphoreRelease); } catch (RuntimeException e) { return Observable.error(e); } } else { // tryAcquire失败后会做fallback处理,TODO return handleSemaphoreRejectionViaFallback(); } } else { // 断路器短路(拒绝请求)fallback处理 TODO return handleShortCircuitViaFallback(); } } executeCommandAndObserve /** * 执行run方法的地方 */ private Observable\u0026lt;R\u0026gt; executeCommandAndObserve(final AbstractCommand\u0026lt;R\u0026gt; _cmd) { // 获取当前上下文 final HystrixRequestContext currentRequestContext = HystrixRequestContext.getContextForCurrentThread(); // 发送数据时的Action响应 final Action1\u0026lt;R\u0026gt; markEmits = new Action1\u0026lt;R\u0026gt;() { @Override public void call(R r) { // 如果onNext时需要上报时,做以下处理 if (shouldOutputOnNextEvents()) { // result标记 executionResult = executionResult.addEvent(HystrixEventType.EMIT); // 通知 eventNotifier.markEvent(HystrixEventType.EMIT, commandKey); } // commandIsScalar是一个我不解的地方,在网上也没有查到好的解释 // 该方法为抽象方法,有HystrixCommand实现返回true.HystrixObservableCommand返回false if (commandIsScalar()) { // 耗时 long latency = System.currentTimeMillis() - executionResult.getStartTimestamp(); // 通知 eventNotifier.markCommandExecution(getCommandKey(), properties.executionIsolationStrategy().get(), (int) latency, executionResult.getOrderedList()); eventNotifier.markEvent(HystrixEventType.SUCCESS, commandKey); executionResult = executionResult.addEvent((int) latency, HystrixEventType.SUCCESS); // 断路器标记成功(断路器半开时的反馈,决定是否关闭断路器) circuitBreaker.markSuccess(); } } }; final Action0 markOnCompleted = new Action0() { @Override public void call() { if (!commandIsScalar()) { // 同markEmits 类似处理 } } }; // 失败回退的逻辑 final Func1\u0026lt;Throwable, Observable\u0026lt;R\u0026gt;\u0026gt; handleFallback = new Func1\u0026lt;Throwable, Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call(Throwable t) { // 不是重点略过了 } }; // 请求上下文的处理 final Action1\u0026lt;Notification\u0026lt;? super R\u0026gt;\u0026gt; setRequestContext = new Action1\u0026lt;Notification\u0026lt;? super R\u0026gt;\u0026gt;() { @Override public void call(Notification\u0026lt;? super R\u0026gt; rNotification) { setRequestContextIfNeeded(currentRequestContext); } }; Observable\u0026lt;R\u0026gt; execution; // 如果有执行超时限制,会将包装后的Observable再转变为支持TimeOut的 if (properties.executionTimeoutEnabled().get()) { // 根据不同的隔离策略包装为不同的Observable execution = executeCommandWithSpecifiedIsolation(_cmd) // lift 是rxjava中一种基本操作符 可以将Observable转换成另一种Observable // 包装为带有超时限制的Observable .lift(new HystrixObservableTimeoutOperator\u0026lt;R\u0026gt;(_cmd)); } else { execution = executeCommandWithSpecifiedIsolation(_cmd); } return execution.doOnNext(markEmits) .doOnCompleted(markOnCompleted) .onErrorResumeNext(handleFallback) .doOnEach(setRequestContext); } executeCommandWithSpecifiedIsolation 根据不同的隔离策略创建不同的执行Observable\nprivate Observable\u0026lt;R\u0026gt; executeCommandWithSpecifiedIsolation(final AbstractCommand\u0026lt;R\u0026gt; _cmd) { if (properties.executionIsolationStrategy().get() == ExecutionIsolationStrategy.THREAD) { // mark that we are executing in a thread (even if we end up being rejected we still were a THREAD execution and not SEMAPHORE) return Observable.defer(new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call() { // 由于源码太长,这里只关注正常的流程,需要详细了解可以去看看源码 if (threadState.compareAndSet(ThreadState.NOT_USING_THREAD, ThreadState.STARTED)) { try { return getUserExecutionObservable(_cmd); } catch (Throwable ex) { return Observable.error(ex); } } else { //command has already been unsubscribed, so return immediately return Observable.error(new RuntimeException(\u0026quot;unsubscribed before executing run()\u0026quot;)); } }}) .doOnTerminate(new Action0() {}) .doOnUnsubscribe(new Action0() {}) // 指定在某一个线程上执行,是rxjava中很重要的线程调度的概念 .subscribeOn(threadPool.getScheduler(new Func0\u0026lt;Boolean\u0026gt;() { })); } else { // 信号量隔离策略 return Observable.defer(new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { // 逻辑与线程池大致相同 }); } } getUserExecutionObservable 获取用户执行的逻辑\nprivate Observable\u0026lt;R\u0026gt; getUserExecutionObservable(final AbstractCommand\u0026lt;R\u0026gt; _cmd) { Observable\u0026lt;R\u0026gt; userObservable; try { // getExecutionObservable是抽象方法,有HystrixCommand自行实现 userObservable = getExecutionObservable(); } catch (Throwable ex) { // the run() method is a user provided implementation so can throw instead of using Observable.onError // so we catch it here and turn it into Observable.error userObservable = Observable.error(ex); } // 将Observable作其他中转 return userObservable .lift(new ExecutionHookApplication(_cmd)) .lift(new DeprecatedOnRunHookApplication(_cmd)); } lift操作符\nlift可以转换成一个新的Observable,它很像一个代理,将原来的Observable代理到自己这里,订阅时通知原来的Observable发送数据,经自己这里流转加工处理再返回给订阅者.Map/FlatMap操作符底层其实就是用的lift进行实现的.\ngetExecutionObservable @Override final protected Observable\u0026lt;R\u0026gt; getExecutionObservable() { return Observable.defer(new Func0\u0026lt;Observable\u0026lt;R\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;R\u0026gt; call() { try { // just操作符就是直接执行的Observable // run方法就是我们实现的业务逻辑: Hello World~ return Observable.just(run()); } catch (Throwable ex) { return Observable.error(ex); } } }).doOnSubscribe(new Action0() { @Override public void call() { // 执行订阅时将执行线程记为当前线程,必要时我们可以interrupt executionThread.set(Thread.currentThread()); } }); } 总结 希望自己能把埋下的坑一一填完: 容错机制,metrics,断路器等等\u0026hellip;\n参考 Hystrix How it Works ReactiveX官网 阮一峰: 中文技术文档写作规范 RxJava lift 原理解析 ","id":13,"section":"posts","summary":"","tags":["rxjava","hystrix"],"title":"Hystrix命令执行流程","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/rxjava-in-hystrix/","year":"2019"},{"content":" 本文基于 rxjava 1.x 版本\n 前言 写这篇文章是因为之前在看Hystrix时,觉得响应式编程很有意思,之前也了解到Spring5主打特性就是响应式,就想来试试水,入个门.本文主要介绍RxJava的特点,入门操作\nRxJava是什么 Reactive X ReactiveX是使用Observable序列来组合异步操作且基于事件驱动的一个库.其继承自观察者模式来支持数据流或者事件流通过添加操作符(operators)的方式来声明式的操作,并抽象出对低级别线程(low-level thread),同步,线程安全,并发数据结构,非阻塞IO问题的关注.\nReactiveX 在不同语言中都有实现,RxJava 只是在JVM上实现的一套罢了.\n概念 观察者模式是该框架的灵魂~\n 上图可以表述为: 观察者(Observer) 订阅(subscribe)被观察者(Observable),当Observable产生事件或数据时,会调用Observer的方法进行回调.\n听起来有点别扭,这里举一个形象点的例子.\n显示器开关\n显示器开关即为 Observable, 显示器为 Observer,这两个组件就会形成联系.当开关按下时,显示器就会通电点亮,这里即可抽象成Observable发出一个事件,Observer对事件做了处理.做什么样的处理其实在Subscribe时就已经决定了.\n回调方法\n在subscribe时会要求实现对应的回调方法,标准方法有以下三个:\n onNext Observable调用这个方法发射数据,方法的参数就是Observable发射的数据,这个方法可能会被调用多次,取决于你的实现。\n onError 当Observable遇到错误或者无法返回期望的数据时会调用这个方法,这个调用会终止Observable,后续不会再调用onNext和onCompleted,onError方法的参数是抛出的异常。\n onCompleted 正常终止,如果没有遇到错误,Observable在最后一次调用onNext之后调用此方法。\n\u0026ldquo;Hot\u0026rdquo; or \u0026ldquo;Cold\u0026rdquo; Observables Observable何时开始发送数据呢?基于此问题,可以将Observable分为两类: Hot \u0026amp; Cold . 可以理解为主动型和被动型.\nHot Observable: Observable一经创建,就会开始发送数据. 所以后面订阅的Observer可能消费不到Observable完整的数据.\nCold Observable: Observable会等到有Observer订阅时才开始发送数据,此时Observer会消费到完整的数据\nRxJava入门 Hello World Observable.create(new Observable.OnSubscribe\u0026lt;String\u0026gt;() { @Override public void call(Subscriber\u0026lt;? super String\u0026gt; subscriber) { subscriber.onNext(\u0026quot;Hello World\u0026quot;); subscriber.onCompleted(); //subscriber.onError(new RuntimeException(\u0026quot;error\u0026quot;)); } }).subscribe(new Subscriber\u0026lt;String\u0026gt;() { @Override public void onCompleted() { System.out.println(\u0026quot;观察结束啦~~~\u0026quot;); } @Override public void onError(Throwable e) { System.out.println(\u0026quot;观察出错啦~~~\u0026quot;); } @Override public void onNext(String s) { System.out.println(\u0026quot;onNext:\u0026quot; + s); } }); } // onNext:Hello World // 观察结束啦~~~ // 注释掉上一行 打开下一行注释 就会输出 // onNext:Hello World // 观察出错啦~~~ 上述即为一个标准的创建观察者被观察者并订阅,实现订阅逻辑.\n疑问\n 为什么subscribe方法的参数是Subscriber呢? 在rxjava中Observer是接口,Subscriber实现了Observer并提供了拓展.所以普遍用这个.\n 为什么是Observable.subscribe(Observer)?用上面的显示器开关的例子来说就相当于显示器开关订阅显示器. 为了保证流式风格~rxjava提供了一系列的操作符来对Observable发出的数据做处理,流式风格可以使操作符使用起来更友好.所以就当做Observable订阅了Observer吧🤦‍♂\n操作符 Operators 单纯的使用上面的Hello World撸码只能说是观察者模式的运用罢了,操作符才是ReactiveX最强大的地方.我们可以通过功能不同的操作符对Observable发出的数据做过滤(filter),转换(map)来满足业务的需求.其实就可以当作是Java8的lambda特性.\n Observable在经过操作符处理后还是一个Observable,对应上述的流式风格\n 案例: 假设我们需要监听鼠标在一个直角坐标系中的点击,取得所有在第一象限点击的坐标.\n从该流程图可以看出,鼠标点击后会发出很多数据,一次点击一个点,我们对数据进行filter,得到了下方时间轴上的数据源.这就是我们想要的.下面来看下常用的操作符有哪些?\n创建型操作符 用于创建Observable对象的操作符\n Create 创建一个Observable,需要传递一个Function来完成调用Observer的逻辑.\n一个标准的Observable必须只能调用一次(Exactly Once)onCompleted或者onError,并且在调用后不能再调用Observer的其他方法(eg: onNext).\nsample code\nObservable.create(new Observable.OnSubscribe\u0026lt;Integer\u0026gt;() { @Override public void call(Subscriber\u0026lt;? super Integer\u0026gt; observer) { try { if (!observer.isUnsubscribed()) { for (int i = 1; i \u0026lt; 5; i++) { observer.onNext(i); } observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } } ).subscribe(new Subscriber\u0026lt;Integer\u0026gt;() { @Override public void onNext(Integer item) { System.out.println(\u0026quot;Next: \u0026quot; + item); } @Override public void onError(Throwable error) { System.err.println(\u0026quot;Error: \u0026quot; + error.getMessage()); } @Override public void onCompleted() { System.out.println(\u0026quot;Sequence complete.\u0026quot;); } }); Next: 1 Next: 2 Next: 3 Next: 4 Sequence complete. Defer 直到有Observer订阅时才会创建,并且会为每一个Observer创建新的Observable,这样可以保证所有Observer可以看到相同的数据,并且从头开始消费.\nsample code\nObservable\u0026lt;String\u0026gt; defer = Observable.defer(new Func0\u0026lt;Observable\u0026lt;String\u0026gt;\u0026gt;() { @Override public Observable\u0026lt;String\u0026gt; call() { return Observable.just(\u0026quot;Hello\u0026quot;, \u0026quot;World\u0026quot;); } }); defer.subscribe(new Subscriber\u0026lt;String\u0026gt;() { @Override public void onCompleted() { System.out.println(\u0026quot;第一个订阅完成啦~\u0026quot;); } @Override public void onError(Throwable e) { System.out.println(\u0026quot;第一个订阅报错啦~\u0026quot;); } @Override public void onNext(String s) { System.out.println(\u0026quot;第一个订阅收到:\u0026quot; + s); } }); defer.subscribe(new Subscriber\u0026lt;String\u0026gt;() { //与上一个订阅逻辑相同 }); 第一个订阅收到:Hello 第一个订阅收到:World 第一个订阅完成啦~ 第二个订阅收到:Hello 第二个订阅收到:World 第二个订阅完成啦~ Note:\nDefer在RxJava中的实现其实有点像指派,可以看到构建时,传参为Func0\u0026lt;Observable\u0026lt;T\u0026gt;\u0026gt;,Observer真正订阅的是传参中的Observable.\nJust 在上文Defer中代码中就用了Just,指的是可以发送特定的数据.代码一致就不作展示了.\nInterval 可以按照指定时间间隔从0开始发送无限递增序列.\n参数 initalDelay 延迟多长时间开始第一次发送 period 指定时间间隔 unit 时间单位 如下例子:延迟0秒后开始发送,每1秒发送一次. 因为sleep 100秒,会发送0-99终止\nsample code\nObservable.interval(0,1,TimeUnit.SECONDS).subscribe(new Action1\u0026lt;Long\u0026gt;() { // 这里只实现了OnNext方法,onError和onCompleted可以有默认实现.一种偷懒写法 @Override public void call(Long aLong) { System.out.println(aLong); } }); try { //阻塞当前线程使程序一直跑 TimeUnit.SECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } 转换操作符 将Observable发出的数据进行各类转换的操作符\n Buffer 如上图所示,buffer定期将数据收集到集合中,并将集合打包发送.\nsample code\nObservable.just(2,3,5,6) .buffer(3) .subscribe(new Action1\u0026lt;List\u0026lt;Integer\u0026gt;\u0026gt;() { @Override public void call(List\u0026lt;Integer\u0026gt; integers) { System.out.println(integers); } }); [2, 3, 5] [6] Window\nwindow和buffer是非常像的两个操作符,区别在于buffer会将存起来的item打包再发出去,而window则只是单纯的将item堆起来,达到阈值再发出去,不对原数据结构做修改.\nsample code\nObservable.just(2,3,5,6) .window(3) .subscribe(new Action1\u0026lt;Observable\u0026lt;Integer\u0026gt;\u0026gt;() { @Override public void call(Observable\u0026lt;Integer\u0026gt; integerObservable) { integerObservable.subscribe(new Action1\u0026lt;Integer\u0026gt;() { @Override public void call(Integer integer) { // do anything } }); } }); 合并操作符 将多个Observable合并为一个的操作符\n Zip 使用一个函数组合多个Observable发射的数据集合,然后再发射这个结果。如果多个Observable发射的数据量不一样,则以最少的Observable为标准进行组合.\nsample code\nObservable\u0026lt;Integer\u0026gt; observable1=Observable.just(1,2,3,4); Observable\u0026lt;Integer\u0026gt; observable2=Observable.just(4,5,6); Observable.zip(observable1, observable2, new Func2\u0026lt;Integer, Integer, String\u0026gt;() { @Override public String call(Integer item1, Integer item2) { return item1+\u0026quot;and\u0026quot;+item2; } }).subscribe(new Action1\u0026lt;String\u0026gt;() { @Override public void call(String s) { System.out.println(s); } }); 1and4 2and5 3and6 背压操作符 用于平衡Observer消费速度,Observable生产速度的操作符\n 背压是指在异步场景中,被观察者发送事件速度远快于观察者的处理速度的情况下,一种告诉上游的被观察者降低发送速度的策略.下图可以很好阐释背压机制是如何运行的.\n宗旨就是下游告诉上游我能处理多少你就给我发多少.\n//被观察者将产生100000个事件 Observable observable=Observable.range(1,100000); observable.observeOn(Schedulers.newThread()) .subscribe(new Subscriber() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Object o) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(\u0026quot;on Next Request...\u0026quot;); request(1); } }); 背压支持 上述样例代码中创建Observable使用的是range操作符,这是因为他是支持背压的,如果用interval,request的方法将不起作用.因为interval不支持背压.那什么样的Observable支持背压呢?\n在前面介绍概念时,有提到过Hot\u0026amp;Cold的区别,Hot类型的Observable,即一经创建就开始发送,不支持背压,Cold类型的Observable也只是部分支持.\nonBackpressurebuffer/onBackpressureDrop 不支持背压的操作符我们可以如何实现背压呢?就通过onBackpressurebuffer/onBackpressureDrop来实现.顾名思义一个是缓存,一个是丢弃.\n这里以drop方式来展示.\nObservable.interval(1, TimeUnit.MILLISECONDS) .onBackpressureDrop() //指定observer调度io线程上,并将缓存size置为1,这个缓存会提前将数据存好在消费, //默认在PC上是128,设置小一点可以快速的看到drop的效果 .observeOn(Schedulers.io(), 1) .subscribe(new Subscriber\u0026lt;Long\u0026gt;() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { System.out.println(\u0026quot;Error:\u0026quot; + e.getMessage()); } @Override public void onNext(Long aLong) { System.out.println(\u0026quot;订阅 \u0026quot; + aLong); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }) 订阅 0 订阅 103 订阅 207 订阅 300 订阅 417 订阅 519 订阅 624 订阅 726 订阅 827 订阅 931 订阅 1035 订阅 1138 订阅 1244 订阅 1349 可以很明显的看出很多数据被丢掉了,这就是背压的效果.\n总结 写了这么多后,想来说说自己的感受.\n 转变思想: 响应式编程的思想跟我们现在后端开发思路是有区别的.可能刚开始会不适应. 不易调试: 流式风格写着爽,调着难 参考 ReactiveX官网\n关于RxJava最友好的文章——背压(Backpressure)\n如何形象地描述RxJava中的背压和流控机制?\n","id":14,"section":"posts","summary":"\u003cblockquote\u003e\n\u003cp\u003e本文基于 rxjava 1.x 版本\u003c/p\u003e\n\u003c/blockquote\u003e","tags":["rxjava","响应式编程"],"title":"RxJava入门","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/rxjava-guide/","year":"2019"},{"content":"Given an array, rotate the array to the right by k steps, where k is non-negative.\nExample 1:\nInput: [1,2,3,4,5,6,7] and k = 3 Output: [5,6,7,1,2,3,4] Explanation: rotate 1 steps to the right: [7,1,2,3,4,5,6] rotate 2 steps to the right: [6,7,1,2,3,4,5] rotate 3 steps to the right: [5,6,7,1,2,3,4] Example 2:\nInput: [-1,-100,3,99] and k = 2 Output: [3,99,-1,-100] Explanation: rotate 1 steps to the right: [99,-1,-100,3] rotate 2 steps to the right: [3,99,-1,-100] Note:\n Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem. Could you do it in-place with O(1) extra space? 思路\n依次反转前半部分及后半部分,最后反转整个数组\neg: 1,2,3,4,5,6,7 k=3\n 反转前半部分 4,3,2,1,5,6,7\n 反转后半部分 4,3,2,1,7,6,5\n 反转整个数组 5,6,7,1,2,3,4\nSolution 1\npub fn rotate(nums: \u0026amp;mut Vec\u0026lt;i32\u0026gt;, k: i32) { if nums.is_empty() || k \u0026lt;= 0 { return; } let o_len = nums.len(); let mod_k = k as usize % o_len; reverse(nums, 0, o_len - mod_k - 1); reverse(nums, o_len - mod_k, o_len - 1); reverse(nums, 0, o_len - 1); } pub fn reverse(nums: \u0026amp;mut Vec\u0026lt;i32\u0026gt;, start: usize, end: usize) { let mut o_start = start; let mut o_end = end; while o_start \u0026lt; o_end { nums.swap(o_start, o_end); o_start += 1; o_end -= 1; } } Solution 2\n api 解法,效率不高,但好看\n pub fn rotate(nums: \u0026amp;mut Vec\u0026lt;i32\u0026gt;, k: i32) { if nums.is_empty() { return; } let mod_k = k % nums.len() as i32; for _ in 0..mod_k as usize { let item = nums.pop().unwrap(); nums.insert(0, item); } } ","id":15,"section":"posts","summary":"","tags":["leetcode","rust"],"title":"[LeetCode In Rust]189-Rotate Array","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/189-rotate-array/","year":"2019"},{"content":"pub fn remove_duplicates(nums: \u0026amp;mut Vec\u0026lt;i32\u0026gt;) -\u0026gt; i32 { if nums.is_empty() { return 0 } let mut idx = 0; for i in idx .. nums.len() { if nums[i].gt(\u0026amp;nums[idx]) { idx += 1; nums.swap(i,idx); } } (idx + 1) as i32 } ","id":16,"section":"posts","summary":"","tags":["leetcode","rust"],"title":"[LeetCode In Rust]026-Remove Duplicates From Sorted Array","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/026-remove-duplicates-from-sorted-array/","year":"2019"},{"content":"基本类型-Primitives 标准类型 Scalar Types 有符号整型 signed integers: i8, i16, i32, i64, i128 and isize (pointer size)\n 无符号整型 unsigned integers: u8, u16, u32, u64, u128 and usize (pointer size)\n 浮点型 floating point: f32, f64\n 字符 char Unicode scalar values like 'a', 'α' and '∞' (4 bytes each)\n 布尔 bool either true or false\n the unit type (), whose only possible value is an empty tuple: ()\n 我的理解就是个empty吧\n 数值字面,没有后缀时.默认整数为 i32,浮点数 f64\n三级目录 有符号整型 signed integers: i8, i16, i32, i64, i128 and isize (pointer size)\n 无符号整型 unsigned integers: u8, u16, u32, u64, u128 and usize (pointer size)\n 浮点型 floating point: f32, f64\n 字符 char Unicode scalar values like 'a', 'α' and '∞' (4 bytes each)\n 布尔 bool either true or false\n the unit type (), whose only possible value is an empty tuple: ()\n 我的理解就是个empty吧\n 数值字面,没有后缀时.默认整数为 i32,浮点数 f64\n四级目录 有符号整型 signed integers: i8, i16, i32, i64, i128 and isize (pointer size)\n 无符号整型 unsigned integers: u8, u16, u32, u64, u128 and usize (pointer size)\n 浮点型 floating point: f32, f64\n 字符 char Unicode scalar values like 'a', 'α' and '∞' (4 bytes each)\n 布尔 bool either true or false\n the unit type (), whose only possible value is an empty tuple: ()\n 我的理解就是个empty吧\n 数值字面,没有后缀时.默认整数为 i32,浮点数 f64\n复合类型 Compound Types 数组 array [1,2,3] 元组 tuple (1,true,\u0026ldquo;str\u0026rdquo;,\u0026lsquo;a\u0026rsquo;\u0026hellip;) Note: 默认变量赋值后是不可变的,需要重复赋值需要用mut 修饰\nlet immutable = 1; immutable = 2; // ERROR let mut immutable = 1; immutable = 2; // SUCCESS 所有权踩坑 可变引用在作用域下有且只能有一个可变引用\n 不可在拥有不可变引用的同时拥有可变引用,除非作用域没有重叠.即在引用可变引用时,不可变引用已经失效了.\nlet mut s = String::from(\u0026quot;hello\u0026quot;); let r1 = \u0026amp;s; // 没问题 let r2 = \u0026amp;s; // 没问题 let r3 = \u0026amp;mut s; // 大问题 println!(\u0026quot;{}, {}, and {}\u0026quot;, r1, r2, r3); ////////////////////////////////////// let mut s = String::from(\u0026quot;hello\u0026quot;); let r1 = \u0026amp;s; // 没问题 let r2 = \u0026amp;s; // 没问题 println!(\u0026quot;{} and {}\u0026quot;, r1, r2); // 此位置之后 r1 和 r2 不再使用 let r3 = \u0026amp;mut s; // 没问题 println!(\u0026quot;{}\u0026quot;, r3); ","id":17,"section":"posts","summary":"","tags":["rust"],"title":"rust踩坑笔记","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/rust/","year":"2019"},{"content":"pub fn two_sum(nums: Vec\u0026lt;i32\u0026gt;, target: i32) -\u0026gt; Vec\u0026lt;i32\u0026gt; { let map: HashMap\u0026lt;i32, usize\u0026gt; = nums.iter().enumerate().map(|(idx, \u0026amp;data)| (data, idx)).collect(); nums.iter().enumerate().find(|(idx, \u0026amp;num)| { match map.get(\u0026amp;(target - num)) { Some(\u0026amp;idx_in_map) =\u0026gt; idx_in_map != *idx, None =\u0026gt; false, } }).map(|(idx, \u0026amp;num)| vec![*map.get(\u0026amp;(target - num)).unwrap() as i32, idx as i32]).unwrap() } pub fn two_sum_v2(nums: Vec\u0026lt;i32\u0026gt;, target: i32) -\u0026gt; Vec\u0026lt;i32\u0026gt; { let map: HashMap\u0026lt;i32, usize\u0026gt; = nums.iter().enumerate().map(|(idx, \u0026amp;data)| (data, idx)).collect(); for (i,\u0026amp;num) in nums.iter().enumerate() { match map.get(\u0026amp;(target - num) ) { Some(\u0026amp;x) =\u0026gt; { if i != x { return vec![i as i32, x as i32] } }, None =\u0026gt; continue, } } vec![] } ","id":18,"section":"posts","summary":"","tags":["leetcode","rust"],"title":"[LeetCode In Rust]001-Two Sum","uri":"https://xiaohei.im/hugo-theme-pure/2019/08/001-two-sum/","year":"2019"}],"tags":[{"title":"collections","uri":"https://xiaohei.im/hugo-theme-pure/tags/collections/"},{"title":"hugo","uri":"https://xiaohei.im/hugo-theme-pure/tags/hugo/"},{"title":"hystrix","uri":"https://xiaohei.im/hugo-theme-pure/tags/hystrix/"},{"title":"leetcode","uri":"https://xiaohei.im/hugo-theme-pure/tags/leetcode/"},{"title":"rabbitmq","uri":"https://xiaohei.im/hugo-theme-pure/tags/rabbitmq/"},{"title":"redis","uri":"https://xiaohei.im/hugo-theme-pure/tags/redis/"},{"title":"rust","uri":"https://xiaohei.im/hugo-theme-pure/tags/rust/"},{"title":"rxjava","uri":"https://xiaohei.im/hugo-theme-pure/tags/rxjava/"},{"title":"分布式锁","uri":"https://xiaohei.im/hugo-theme-pure/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/"},{"title":"响应式编程","uri":"https://xiaohei.im/hugo-theme-pure/tags/%E5%93%8D%E5%BA%94%E5%BC%8F%E7%BC%96%E7%A8%8B/"},{"title":"数据结构","uri":"https://xiaohei.im/hugo-theme-pure/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"}]} \ No newline at end of file
diff --git a/sitemap.xml b/sitemap.xml
index ad8458f..74bb4f3 100644
--- a/sitemap.xml
+++ b/sitemap.xml
@@ -4,48 +4,69 @@
<url>
<loc>https://xiaohei.im/hugo-theme-pure/categories/</loc>
- <lastmod>2019-11-06T19:08:56+08:00</lastmod>
+ <lastmod>2019-11-16T14:24:40+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://xiaohei.im/hugo-theme-pure/posts/</loc>
- <lastmod>2019-11-06T19:08:56+08:00</lastmod>
+ <lastmod>2019-11-16T14:24:40+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/categories/redis/</loc>
- <lastmod>2019-11-06T19:08:56+08:00</lastmod>
+ <loc>https://xiaohei.im/hugo-theme-pure/tags/redis/</loc>
+ <lastmod>2019-11-16T14:24:40+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/tags/redis/</loc>
- <lastmod>2019-11-06T19:08:56+08:00</lastmod>
+ <loc>https://xiaohei.im/hugo-theme-pure/categories/redis/</loc>
+ <lastmod>2019-11-16T14:24:40+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/2019/11/rdb/</loc>
- <lastmod>2019-11-06T19:08:56+08:00</lastmod>
+ <loc>https://xiaohei.im/hugo-theme-pure/2019/11/replication/</loc>
+ <lastmod>2019-11-16T14:24:40+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://xiaohei.im/hugo-theme-pure/tags/</loc>
- <lastmod>2019-11-06T19:08:56+08:00</lastmod>
+ <lastmod>2019-11-16T14:24:40+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
<loc>https://xiaohei.im/hugo-theme-pure/</loc>
+ <lastmod>2019-11-16T14:24:40+08:00</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+
+ <url>
+ <loc>https://xiaohei.im/hugo-theme-pure/2019/11/event/</loc>
+ <lastmod>2019-11-14T15:01:45+08:00</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+
+ <url>
+ <loc>https://xiaohei.im/hugo-theme-pure/2019/11/aof/</loc>
+ <lastmod>2019-11-08T15:18:05+08:00</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+
+ <url>
+ <loc>https://xiaohei.im/hugo-theme-pure/2019/11/rdb/</loc>
<lastmod>2019-11-06T19:08:56+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
@@ -199,14 +220,14 @@
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/tags/leetcode/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/categories/leetcode/</loc>
<lastmod>2019-08-21T15:29:34+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/categories/leetcode/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/tags/leetcode/</loc>
<lastmod>2019-08-21T15:29:34+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
@@ -241,6 +262,13 @@
</url>
<url>
+ <loc>https://xiaohei.im/hugo-theme-pure/collections/</loc>
+ <lastmod>2019-08-15T13:19:18+08:00</lastmod>
+ <changefreq>monthly</changefreq>
+ <priority>0.5</priority>
+ </url>
+
+ <url>
<loc>https://xiaohei.im/hugo-theme-pure/tags/collections/</loc>
<lastmod>2019-08-15T13:19:18+08:00</lastmod>
<changefreq>monthly</changefreq>
@@ -248,49 +276,49 @@
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/collections/arraylist/</loc>
<lastmod>2019-08-15T13:19:18+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/collections/hashmap/</loc>
<lastmod>2019-08-15T13:19:18+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/2019/08/hashset/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/collections/hashset/</loc>
<lastmod>2019-08-15T13:19:18+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/</loc>
<lastmod>2019-08-15T13:19:18+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/collections/linkedlist/</loc>
<lastmod>2019-08-15T13:19:18+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/2019/08/treemap/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/collections/treemap/</loc>
<lastmod>2019-08-15T13:19:18+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
</url>
<url>
- <loc>https://xiaohei.im/hugo-theme-pure/2019/08/treeset/</loc>
+ <loc>https://xiaohei.im/hugo-theme-pure/collections/treeset/</loc>
<lastmod>2019-08-15T13:19:18+08:00</lastmod>
<changefreq>monthly</changefreq>
<priority>0.5</priority>
diff --git a/tags/collections/index.html b/tags/collections/index.html
index a6873c1..f86a500 100644
--- a/tags/collections/index.html
+++ b/tags/collections/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -306,7 +306,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/">Collections-Arraylist</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/arraylist/">Collections-Arraylist</a>
</h3>
</div>
<div class="panel-footer">
@@ -322,7 +322,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/">Collections-Hashmap</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/hashmap/">Collections-Hashmap</a>
</h3>
</div>
<div class="panel-footer">
@@ -338,7 +338,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/hashset/">Collections-Hashset</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/hashset/">Collections-Hashset</a>
</h3>
</div>
<div class="panel-footer">
@@ -354,7 +354,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/">Collections-Linkedhashmap</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/">Collections-Linkedhashmap</a>
</h3>
</div>
<div class="panel-footer">
@@ -370,7 +370,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/">Collections-Linkedlist</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/">Collections-Linkedlist</a>
</h3>
</div>
<div class="panel-footer">
@@ -386,7 +386,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/treemap/">Collections-Treemap</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/treemap/">Collections-Treemap</a>
</h3>
</div>
<div class="panel-footer">
@@ -402,7 +402,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/treeset/">Collections-Treeset</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/treeset/">Collections-Treeset</a>
</h3>
</div>
<div class="panel-footer">
diff --git a/tags/collections/index.xml b/tags/collections/index.xml
index d503554..22eaa20 100644
--- a/tags/collections/index.xml
+++ b/tags/collections/index.xml
@@ -12,64 +12,64 @@
<item>
<title>Collections-Arraylist</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/arraylist/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/arraylist/</guid>
<description></description>
</item>
<item>
<title>Collections-Hashmap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/hashmap/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/hashmap/</guid>
<description></description>
</item>
<item>
<title>Collections-Hashset</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/hashset/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/hashset/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/hashset/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/hashset/</guid>
<description></description>
</item>
<item>
<title>Collections-Linkedhashmap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/</guid>
<description></description>
</item>
<item>
<title>Collections-Linkedlist</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/linkedlist/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/linkedlist/</guid>
<description></description>
</item>
<item>
<title>Collections-Treemap</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/treemap/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/treemap/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/treemap/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/treemap/</guid>
<description></description>
</item>
<item>
<title>Collections-Treeset</title>
- <link>https://xiaohei.im/hugo-theme-pure/2019/08/treeset/</link>
+ <link>https://xiaohei.im/hugo-theme-pure/collections/treeset/</link>
<pubDate>Thu, 15 Aug 2019 13:19:18 +0800</pubDate>
- <guid>https://xiaohei.im/hugo-theme-pure/2019/08/treeset/</guid>
+ <guid>https://xiaohei.im/hugo-theme-pure/collections/treeset/</guid>
<description></description>
</item>
diff --git a/tags/hugo/index.html b/tags/hugo/index.html
index 95346b5..dfbcef9 100644
--- a/tags/hugo/index.html
+++ b/tags/hugo/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/tags/hystrix/index.html b/tags/hystrix/index.html
index 288e8bc..3c6d824 100644
--- a/tags/hystrix/index.html
+++ b/tags/hystrix/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/tags/index.html b/tags/index.html
index 277dbc8..8cdafbe 100644
--- a/tags/index.html
+++ b/tags/index.html
@@ -36,7 +36,7 @@
<meta property="og:type" content="website" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/tags/" />
-<meta property="og:updated_time" content="2019-11-06T19:08:56+08:00" />
+<meta property="og:updated_time" content="2019-11-16T14:24:40+08:00" />
<meta itemprop="name" content="Tags">
<meta itemprop="description" content="">
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -312,7 +312,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/arraylist/">Collections-Arraylist</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/arraylist/">Collections-Arraylist</a>
</h3>
</div>
<div class="panel-footer">
@@ -329,7 +329,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/hashmap/">Collections-Hashmap</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/hashmap/">Collections-Hashmap</a>
</h3>
</div>
<div class="panel-footer">
@@ -346,7 +346,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/hashset/">Collections-Hashset</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/hashset/">Collections-Hashset</a>
</h3>
</div>
<div class="panel-footer">
@@ -363,7 +363,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedhashmap/">Collections-Linkedhashmap</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/linkedhashmap/">Collections-Linkedhashmap</a>
</h3>
</div>
<div class="panel-footer">
@@ -380,7 +380,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/linkedlist/">Collections-Linkedlist</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/linkedlist/">Collections-Linkedlist</a>
</h3>
</div>
<div class="panel-footer">
@@ -397,7 +397,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/treemap/">Collections-Treemap</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/treemap/">Collections-Treemap</a>
</h3>
</div>
<div class="panel-footer">
@@ -414,7 +414,7 @@
itemprop="datePublished">2019-08-15</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/08/treeset/">Collections-Treeset</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/collections/treeset/">Collections-Treeset</a>
</h3>
</div>
<div class="panel-footer">
@@ -606,7 +606,7 @@
</div>
<h3 class="panel-title mb-1x">
<a href="https://xiaohei.im/hugo-theme-pure/tags/redis" title="#redis"># redis</a>
- <small class="text-muted">(Total5 articles)</small>
+ <small class="text-muted">(Total8 articles)</small>
</h3>
<div class="row">
<div class="col-md-6">
@@ -614,6 +614,57 @@
<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
<div class="panel-body">
<div class="article-meta">
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST"
+ itemprop="datePublished">2019-11-16</time>
+ </div>
+ <h3 class="article-title" itemprop="name">
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/">Redis-复制功能探索</a>
+ </h3>
+ </div>
+ <div class="panel-footer">
+ <a href="https://xiaohei.im/hugo-theme-pure/tags/redis" class="label label-default mb">redis</a>
+ </div>
+</article>
+ </div>
+ <div class="col-md-6">
+
+<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
+ <div class="panel-body">
+ <div class="article-meta">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST"
+ itemprop="datePublished">2019-11-14</time>
+ </div>
+ <h3 class="article-title" itemprop="name">
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/11/event/">Redis-事件</a>
+ </h3>
+ </div>
+ <div class="panel-footer">
+ <a href="https://xiaohei.im/hugo-theme-pure/tags/redis" class="label label-default mb">redis</a>
+ </div>
+</article>
+ </div>
+ <div class="col-md-6">
+
+<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
+ <div class="panel-body">
+ <div class="article-meta">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST"
+ itemprop="datePublished">2019-11-08</time>
+ </div>
+ <h3 class="article-title" itemprop="name">
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/">Redis-AOF持久化</a>
+ </h3>
+ </div>
+ <div class="panel-footer">
+ <a href="https://xiaohei.im/hugo-theme-pure/tags/redis" class="label label-default mb">redis</a>
+ </div>
+</article>
+ </div>
+ <div class="col-md-6">
+
+<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
+ <div class="panel-body">
+ <div class="article-meta">
<time datetime="2019-11-06 19:08:56 &#43;0800 CST"
itemprop="datePublished">2019-11-06</time>
</div>
@@ -687,7 +738,7 @@
itemprop="datePublished">2019-10-24</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/">Redis - 数据结构</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/">Redis-数据结构</a>
</h3>
</div>
<div class="panel-footer">
@@ -878,7 +929,7 @@
itemprop="datePublished">2019-10-24</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/">Redis - 数据结构</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/">Redis-数据结构</a>
</h3>
</div>
<div class="panel-footer">
diff --git a/tags/index.xml b/tags/index.xml
index eb85a12..95b7c4c 100644
--- a/tags/index.xml
+++ b/tags/index.xml
@@ -5,7 +5,7 @@
<link>https://xiaohei.im/hugo-theme-pure/tags/</link>
<description>Recent content in Tags on 赵小黑的博客</description>
<generator>Hugo -- gohugo.io</generator>
- <lastBuildDate>Wed, 06 Nov 2019 19:08:56 +0800</lastBuildDate>
+ <lastBuildDate>Sat, 16 Nov 2019 14:24:40 +0800</lastBuildDate>
<atom:link href="https://xiaohei.im/hugo-theme-pure/tags/index.xml" rel="self" type="application/rss+xml" />
@@ -13,7 +13,7 @@
<item>
<title>redis</title>
<link>https://xiaohei.im/hugo-theme-pure/tags/redis/</link>
- <pubDate>Wed, 06 Nov 2019 19:08:56 +0800</pubDate>
+ <pubDate>Sat, 16 Nov 2019 14:24:40 +0800</pubDate>
<guid>https://xiaohei.im/hugo-theme-pure/tags/redis/</guid>
<description></description>
diff --git a/tags/leetcode/index.html b/tags/leetcode/index.html
index cd83ba2..b405e26 100644
--- a/tags/leetcode/index.html
+++ b/tags/leetcode/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/tags/rabbitmq/index.html b/tags/rabbitmq/index.html
index 21df55a..469b643 100644
--- a/tags/rabbitmq/index.html
+++ b/tags/rabbitmq/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/tags/redis/index.html b/tags/redis/index.html
index cf18481..59f2578 100644
--- a/tags/redis/index.html
+++ b/tags/redis/index.html
@@ -36,7 +36,7 @@
<meta property="og:type" content="website" />
<meta property="og:url" content="https://xiaohei.im/hugo-theme-pure/tags/redis/" />
-<meta property="og:updated_time" content="2019-11-06T19:08:56+08:00" />
+<meta property="og:updated_time" content="2019-11-16T14:24:40+08:00" />
<meta itemprop="name" content="redis">
<meta itemprop="description" content="">
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -299,6 +299,45 @@
<div class="article-body">
<div class="col-md-6">
+<a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="collection-item" itemprop="url" target="_blank" >
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST"
+ itemprop="datePublished">2019-11-16</time>
+ <span>&nbsp;&nbsp;&nbsp;</span>Redis-复制功能探索
+</a>
+ </div>
+ <div class="col-md-6">
+<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
+ <div class="panel-body">
+ <div class="article-meta">
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST"
+ itemprop="datePublished">2019-11-14</time>
+ </div>
+ <h3 class="article-title" itemprop="name">
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/11/event/">Redis-事件</a>
+ </h3>
+ </div>
+ <div class="panel-footer">
+ <a href="https://xiaohei.im/hugo-theme-pure/tags/redis" class="label label-default mb">redis</a>
+ </div>
+</article>
+ </div>
+ <div class="col-md-6">
+<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
+ <div class="panel-body">
+ <div class="article-meta">
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST"
+ itemprop="datePublished">2019-11-08</time>
+ </div>
+ <h3 class="article-title" itemprop="name">
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/">Redis-AOF持久化</a>
+ </h3>
+ </div>
+ <div class="panel-footer">
+ <a href="https://xiaohei.im/hugo-theme-pure/tags/redis" class="label label-default mb">redis</a>
+ </div>
+</article>
+ </div>
+ <div class="col-md-6">
<article class="panel panel-default hover-shadow hover-grow" itemscope itemtype="http://schema.org/BlogPosting">
<div class="panel-body">
<div class="article-meta">
@@ -371,7 +410,7 @@
itemprop="datePublished">2019-10-24</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/">Redis - 数据结构</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/">Redis-数据结构</a>
</h3>
</div>
<div class="panel-footer">
diff --git a/tags/redis/index.xml b/tags/redis/index.xml
index 2093453..661c7ce 100644
--- a/tags/redis/index.xml
+++ b/tags/redis/index.xml
@@ -5,12 +5,41 @@
<link>https://xiaohei.im/hugo-theme-pure/tags/redis/</link>
<description>Recent content in redis on 赵小黑的博客</description>
<generator>Hugo -- gohugo.io</generator>
- <lastBuildDate>Wed, 06 Nov 2019 19:08:56 +0800</lastBuildDate>
+ <lastBuildDate>Sat, 16 Nov 2019 14:24:40 +0800</lastBuildDate>
<atom:link href="https://xiaohei.im/hugo-theme-pure/tags/redis/index.xml" rel="self" type="application/rss+xml" />
<item>
+ <title>Redis-复制功能探索</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/replication/</link>
+ <pubDate>Sat, 16 Nov 2019 14:24:40 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/replication/</guid>
+ <description>&lt;p&gt;之前多&lt;code&gt;redis&lt;/code&gt; 的复制只有一点点了解,这次想要搞明白的是: 如何实现的复制? 复制会遇到哪些问题(时延/一致性保证/网络故障时的处理)? 如何解决?高可用实现方案? 文章有部分是直接翻译的 &lt;a href=&#34;https://redis.io/topics/replication&#34;&gt;https://redis.io/topics/replication&lt;/a&gt;&lt;/p&gt;</description>
+ </item>
+
+ <item>
+ <title>Redis-事件</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/event/</link>
+ <pubDate>Thu, 14 Nov 2019 15:01:45 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/event/</guid>
+ <description>&lt;blockquote&gt;
+&lt;p&gt;&lt;strong&gt;事件驱动程序设计&lt;/strong&gt;(英语:&lt;strong&gt;Event-driven programming&lt;/strong&gt;)是一种电脑&lt;a href=&#34;https://zh.wikipedia.org/wiki/程式設計&#34;&gt;程序设计&lt;/a&gt;&lt;a href=&#34;https://zh.wikipedia.org/wiki/模型&#34;&gt;模型&lt;/a&gt;。这种模型的程序运行流程是由用户的动作(如&lt;a href=&#34;https://zh.wikipedia.org/wiki/滑鼠&#34;&gt;鼠标&lt;/a&gt;的按键,键盘的按键动作)或者是由其他程序的&lt;a href=&#34;https://zh.wikipedia.org/wiki/訊息&#34;&gt;消息&lt;/a&gt;来决定的。相对于批处理程序设计(batch programming)而言,程序运行的流程是由&lt;a href=&#34;https://zh.wikipedia.org/wiki/程式設計師&#34;&gt;程序员&lt;/a&gt;来决定。批量的程序设计在初级程序设计教学课程上是一种方式。然而,事件驱动程序设计这种设计模型是在&lt;a href=&#34;https://zh.wikipedia.org/w/index.php?title=互動程序&amp;amp;action=edit&amp;amp;redlink=1&#34;&gt;交互程序&lt;/a&gt;(Interactive program)的情况下孕育而生的。 &lt;a href=&#34;https://zh.wikipedia.org/wiki/事件驅動程式設計&#34;&gt;&amp;ndash;wikipedia&lt;/a&gt;&lt;/p&gt;
+&lt;/blockquote&gt;</description>
+ </item>
+
+ <item>
+ <title>Redis-AOF持久化</title>
+ <link>https://xiaohei.im/hugo-theme-pure/2019/11/aof/</link>
+ <pubDate>Fri, 08 Nov 2019 15:18:05 +0800</pubDate>
+
+ <guid>https://xiaohei.im/hugo-theme-pure/2019/11/aof/</guid>
+ <description>&lt;p&gt;&lt;code&gt;RDB&lt;/code&gt; 和 &lt;code&gt;AOF&lt;/code&gt; 区别在于: 前者保存数据库快照,持久化所有键值对,后者通过保存 &lt;strong&gt;写命令&lt;/strong&gt; 保证数据库的状态.&lt;/p&gt;</description>
+ </item>
+
+ <item>
<title>Redis-RDB持久化</title>
<link>https://xiaohei.im/hugo-theme-pure/2019/11/rdb/</link>
<pubDate>Wed, 06 Nov 2019 19:08:56 +0800</pubDate>
@@ -47,7 +76,7 @@
</item>
<item>
- <title>Redis - 数据结构</title>
+ <title>Redis-数据结构</title>
<link>https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/</link>
<pubDate>Thu, 24 Oct 2019 09:59:11 +0800</pubDate>
diff --git a/tags/rust/index.html b/tags/rust/index.html
index e47b57e..b3408d1 100644
--- a/tags/rust/index.html
+++ b/tags/rust/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/tags/rxjava/index.html b/tags/rxjava/index.html
index 58bc194..0571759 100644
--- a/tags/rxjava/index.html
+++ b/tags/rxjava/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/tags/分布式锁/index.html b/tags/分布式锁/index.html
index 5286300..5ef3929 100644
--- a/tags/分布式锁/index.html
+++ b/tags/分布式锁/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/tags/响应式编程/index.html b/tags/响应式编程/index.html
index a004904..1bf6894 100644
--- a/tags/响应式编程/index.html
+++ b/tags/响应式编程/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
diff --git a/tags/数据结构/index.html b/tags/数据结构/index.html
index 5bcd1e6..7ef8a5a 100644
--- a/tags/数据结构/index.html
+++ b/tags/数据结构/index.html
@@ -153,7 +153,7 @@
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/corejava/" class="category-list-link">corejava</a><span class="category-list-count">7</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/hystrix/" class="category-list-link">hystrix</a><span class="category-list-count">2</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/leetcode/" class="category-list-link">leetcode</a><span class="category-list-count">3</span></li>
- <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">5</span></li>
+ <li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/redis/" class="category-list-link">redis</a><span class="category-list-count">8</span></li>
<li class="category-list-item"><a href="https://xiaohei.im/hugo-theme-pure/categories/%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97/" class="category-list-link">消息队列</a><span class="category-list-count">4</span></li>
</ul>
</div>
@@ -185,7 +185,7 @@
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/redis/" class="tag-list-link">redis</a><span
- class="tag-list-count">5</span></li>
+ class="tag-list-count">8</span></li>
<li class="tag-list-item"><a href="https://xiaohei.im/hugo-theme-pure/tags/rust/" class="tag-list-link">rust</a><span
@@ -219,50 +219,50 @@
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/replication/" class="title">Redis-复制功能探索</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-16 14:24:40 &#43;0800 CST" itemprop="datePublished">2019-11-16</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/event/" class="title">Redis-事件</a>
</p>
<p class="item-date">
- <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
+ <time datetime="2019-11-14 15:01:45 &#43;0800 CST" itemprop="datePublished">2019-11-14</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/obj/" class="title">Redis-万物皆「对象」</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/aof/" class="title">Redis-AOF持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-04 18:56:15 &#43;0800 CST" itemprop="datePublished">2019-11-04</time>
+ <time datetime="2019-11-08 15:18:05 &#43;0800 CST" itemprop="datePublished">2019-11-08</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/11/distributed-lock/" class="title">Redis-分布式锁</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/rdb/" class="title">Redis-RDB持久化</a>
</p>
<p class="item-date">
- <time datetime="2019-11-03 14:49:56 &#43;0800 CST" itemprop="datePublished">2019-11-03</time>
+ <time datetime="2019-11-06 19:08:56 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
<li>
<div class="item-inner">
<p class="item-title">
- <a href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/" class="title">Redis - 数据结构</a>
+ <a href="https://xiaohei.im/hugo-theme-pure/2019/11/db/" class="title">Redis-数据库长什么样?</a>
</p>
<p class="item-date">
- <time datetime="2019-10-24 09:59:11 &#43;0800 CST" itemprop="datePublished">2019-10-24</time>
+ <time datetime="2019-11-06 11:00:32 &#43;0800 CST" itemprop="datePublished">2019-11-06</time>
</p>
</div>
</li>
@@ -306,7 +306,7 @@
itemprop="datePublished">2019-10-24</time>
</div>
<h3 class="article-title" itemprop="name">
- <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/">Redis - 数据结构</a>
+ <a class="article-link" href="https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/">Redis-数据结构</a>
</h3>
</div>
<div class="panel-footer">
diff --git a/tags/数据结构/index.xml b/tags/数据结构/index.xml
index 3b79403..9909828 100644
--- a/tags/数据结构/index.xml
+++ b/tags/数据结构/index.xml
@@ -11,7 +11,7 @@
<item>
- <title>Redis - 数据结构</title>
+ <title>Redis-数据结构</title>
<link>https://xiaohei.im/hugo-theme-pure/2019/10/data-structure/</link>
<pubDate>Thu, 24 Oct 2019 09:59:11 +0800</pubDate>