diff --git a/HuangJunLong/README.MD b/HuangJunLong/README.MD deleted file mode 100644 index 60330d5..0000000 --- a/HuangJunLong/README.MD +++ /dev/null @@ -1,2 +0,0 @@ -# 加油 - diff --git a/LiYu/README.MD b/LiYu/README.MD deleted file mode 100644 index 60330d5..0000000 --- a/LiYu/README.MD +++ /dev/null @@ -1,2 +0,0 @@ -# 加油 - diff --git a/README.md b/README.md index 35e4c7b..9be7294 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ - 每次学习完,打卡,交笔记。 +- 如果一直需要登录账号或者密码,尝试用SSH clone下来。 + + + ​ @@ -35,44 +39,71 @@ | 4.26 | 最近疯狂加班,顶不住 | | 1.JDK1.6 之后的底层优化(偏向锁,轻量级锁,自旋锁和自适应自旋,锁消除,锁粗化)2.i++线程安全吗? | | | | | 4.30 | 微服务核心理念探究 | | | | | | + + ### 2020年5月 -| | 唐轩 | 李昱 | 杨磊 | 宋声林 | 唐斌 | 黄俊龙 | -| :--: | :-----------------------: | :----------------------------: | :--: | :----: | :--: | :----: | -| 5.3 | | | | | | | -| 5.7 | 微服务核心理念探究2 | 看java编程思想,复习面试基础。 | | | | | -| 5.10 | 数据库知识复习+数据库索引 | | | | | | -| 5.14 | 206.链表反转 (三种解法) | 523. 连续的子数组和 | | | | | -| 5.17 | | | | | | | -| 5.21 | | | | | | | -| 5.24 | | | | | | | -| 5.28 | | | | | | | -| 5.31 | | | | | | | +| | 唐轩 | 李昱 | 杨磊 | 宋声林 | 唐斌 | 黄俊龙 | +| :--: | :----------------------------: | :----------------------------: | :----------------------------------------------------------: | :----: | :--: | :----------------------: | +| 5.3 | | | | | | | +| 5.7 | 微服务核心理念探究2 | 看java编程思想,复习面试基础。 | | | | | +| 5.10 | 数据库知识复习+数据库索引 | | blibli上面的SpringCloud | | | | +| 5.14 | 206.链表反转 (三种解法) | 523. 连续的子数组和 | SpringCloud注册中心Eureka | | | | +| 5.17 | 《Java并发编程之美》第一章笔记 | | | | | java数据结构与算法之链表 | +| 5.21 | | | 看《代码整洁之道》,看了两章。 | | | | +| 5.24 | | | 学习了配置Eureka,和Eureka集群,服务发现discover,给服务提供者部署成集群 | | | | +| 5.28 | | | 《代码整洁之道》看完第三章 | | | | +| 5.31 | | | 复习了Dubbo | | | | + + + +### 2020年6月 + +| | 唐轩 | 李昱 | 杨磊 | 宋声林 | 唐斌 | 黄俊龙 | +| :--: | :--: | :--: | :--: | :----: | :--: | :----: | +| 6.4 | | | | | | | +| 6.7 | | | | | | | +| 6.10 | | | | | | | +| 6.14 | | | | | | | +| 6.18 | | | | | | | +| 6.21 | | | | | | | +| 6.25 | | | | | | | +| 6.28 | | | | | | | + + + + + + + + + + ## LeetCode学习打卡记录 ### 2020年5月 -| | 唐轩 | 李昱 | 杨磊 | 宋声林 | 唐斌 | 黄俊龙 | -| :--: | :-----------------------: | :-----------------------------: | :--: | :----: | :--: | :----: | -| 5.12 | 206.链表反转 (三种解法) | 523. 连续的子数组和 | | | | | -| 5.13 | 3.无重复字符的最长字串 | 1277. 统计全为 1 的正方形子矩阵 | | | | | -| 5.14 | | | | | | | -| 5.15 | | | | | | | -| 5.16 | | | | | | | -| 5.17 | | | | | | | -| 5.18 | | | | | | | -| 5.19 | | | | | | | -| 5.20 | | | | | | | -| 5.21 | | | | | | | -| 5.22 | | | | | | | -| 5.23 | | | | | | | -| 5.24 | | | | | | | -| 5.25 | | | | | | | -| 5.26 | | | | | | | -| 5.27 | | | | | | | -| 5.28 | | | | | | | -| 5.29 | | | | | | | -| 5.30 | | | | | | | -| 5.31 | | | | | | | +| | 唐轩 | 李昱 | 杨磊 | 宋声林 | 唐斌 | 黄俊龙 | +| :--: | :------------------------: | :-----------------------------: | :--: | :----: | :--: | :--------------------: | +| 5.12 | 206.链表反转 (三种解法) | 523. 连续的子数组和 | | | | | +| 5.13 | 3.无重复字符的最长字串 | 1277. 统计全为 1 的正方形子矩阵 | | | | | +| 5.14 | 4.寻找两个正序数组的中位数 | | | | | 5.最长回文字符串 | +| 5.15 | | | | | | 1.两数之和 | +| 5.16 | | | | | | 2.两数相加 | +| 5.17 | | | | | | | +| 5.18 | | | | | | | +| 5.19 | | | | | | 3.无重复字符的最长子串 | +| 5.20 | | | | | | | +| 5.21 | | | | | | | +| 5.22 | | | | | | | +| 5.23 | | | | | | | +| 5.24 | | | | | | | +| 5.25 | | | | | | | +| 5.26 | | | | | | | +| 5.27 | | | | | | | +| 5.28 | | | | | | | +| 5.29 | | | | | | | +| 5.30 | | | | | | | +| 5.31 | | | | | | | diff --git a/SongSenLin/README.MD b/SongSenLin/README.MD deleted file mode 100644 index 60330d5..0000000 --- a/SongSenLin/README.MD +++ /dev/null @@ -1,2 +0,0 @@ -# 加油 - diff --git a/TangBing/README.MD b/TangBing/README.MD deleted file mode 100644 index 60330d5..0000000 --- a/TangBing/README.MD +++ /dev/null @@ -1,2 +0,0 @@ -# 加油 - diff --git a/TangXuan/README.MD b/TangXuan/README.MD deleted file mode 100644 index 60330d5..0000000 --- a/TangXuan/README.MD +++ /dev/null @@ -1,2 +0,0 @@ -# 加油 - diff --git "a/TangXuan/leetcode/\346\227\240\351\207\215\345\244\215\346\234\200\351\225\277\345\255\227\344\270\262.md" "b/TangXuan/leetcode/\346\227\240\351\207\215\345\244\215\346\234\200\351\225\277\345\255\227\344\270\262.md" deleted file mode 100644 index c7a6fe1..0000000 --- "a/TangXuan/leetcode/\346\227\240\351\207\215\345\244\215\346\234\200\351\225\277\345\255\227\344\270\262.md" +++ /dev/null @@ -1,28 +0,0 @@ -# 无重复最长字串 - -```java -class Solution { - public int lengthOfLongestSubstring(String s) { - // 哈希集合,记录每个字符是否出现过 - Set occ = new HashSet(); - int n = s.length(); - // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动 - int rk = -1, ans = 0; - for (int i = 0; i < n; ++i) { - if (i != 0) { - // 左指针向右移动一格,移除一个字符 - occ.remove(s.charAt(i - 1)); - } - while (rk + 1 < n && !occ.contains(s.charAt(rk + 1))) { - // 不断地移动右指针 - occ.add(s.charAt(rk + 1)); - ++rk; - } - // 第 i 到 rk 个字符是一个极长的无重复字符子串 - ans = Math.max(ans, rk - i + 1); - } - return ans; - } -} -``` - diff --git a/YangLei/CleanCode.md b/YangLei/CleanCode.md new file mode 100644 index 0000000..e91e7aa --- /dev/null +++ b/YangLei/CleanCode.md @@ -0,0 +1,619 @@ +# CleanCode + +## 《代码整洁之道》读书笔记 + +书中提到一种观点:代码质量与其整洁度成正比。干净的代码,既在质量上较为可靠,也为后期维护,升级奠定良好的基础。 + + + +“我们就是一群代码猴子,上蹿下跳,自以为领略了编程的真谛。可惜当我们抓着几个酸桃子,得意洋洋坐到树枝上,却对自己造成的混乱熟视无睹。那堆“可以运行”的乱麻程序,就在我们的眼皮底下慢慢腐坏。” + + + +​ ——《代码整洁之道》作者 RobertC.Martin,于SD West 2007技术大会 + + +## 前言 + +里面有一句话,阅读本书呀多用心思,这可不是那种降落前就能读完的“感觉不错”的飞机书,本书要让你用过,而且要非常用功。 那么如何用功呢? + +就是要阅读代码,阅读大量的艾玛,而且你要琢磨某一段代码好在什么地方,坏在什么地方,让我们分解,而后组合模块的时候,得亦步亦趋的更上,这里带下功夫。 + + + +其实意思就是说,这书要慢慢品,同时要结合具体的工作,多阅读别人的代码,多总结思考。 + +## 第一章 整洁代码 + +阅读本书有两个原因:第一,你是个程序员,第二,你想成为更好的程序员。很好,我们需要更好的程序员。 + + + +### 程序员的价值谜题 + +为了赶工期,程序员可能会写一些烂代码,制造混乱,其实这样是无助于赶上工期的。 + +赶上工期的唯一办法就是-------始终保持代码的整洁。 + + + +### 整洁代码的艺术 + +编写整洁代码的程序员就像艺术家,他能用一系列变换把一张白板变为由优雅的代码构成的系统。 + + + +### 什么是代码整洁之道 + +这里是好多大佬的经典语录合集: + +- 优雅,代码逻辑直截了当,减少代码依赖关系,便于维护(尤其微服务项目要注意,不要有耦合),性能调至最优,整洁的代码只做一件事。 +- 要有单元测试和验收测试,命名要有意义,表达要清晰,只提供一种而非多种用途,少依赖,清晰少的API。 +- 好的代码基本没有要修改的地方,该有的,作者都想到啦。 + +- 能通过所有的测试,没有重复代码(比较重要),提高表达力,代码中可以体现全部设计理念,包含尽可能少的实体(类,方法,函数(但是感觉有时候bean多一点好维护!)) + + + +### 童子军军规 + +光把代码写好还不够,还有让代码试试保持整洁。 + +让营地比你来时更干净。 + + + +### 小结(很重要,很升华) + +艺术书并不能保证你读过以后就成为艺术家,他只能告诉你艺术家用过的工具,思维过程。 + +最后, **你还得练,孩子,你还得练。** + + + +## 第二章 有意义的命名 + +### 见名知意 + +不要用单个字母表示,最好用有实际意义的单词表示。 + + + +### 避免误导 + +数字1和字母l容易混淆,数字0和字母O容易混淆,所以不要用字母l和O给命名。 + + + +### 做有意义的区分 + +比如theMesssage和Message其实意思一样 + +money和MoneyAmount意思一样 + + + +### 使用读的出来的名称 + +比如genymdhms(生成日期,年月日时分秒) 这样比较难理解,最好用单词比如generationTimetamp等。 + + + +### 使用可搜索的名称 + +比如数字7 和 MAX_CLASS_PER_STUDENT , 显然后面这个要比数字7好搜索许多。还有比如单独搜索一个字母,可能不好搜,如果搜一个稍微长一点的单词,就相对容易许多。 + + + +### 避免使用编码 + +类不要加前缀,比如m_desc ,没必要,这样很不舒服,麻烦,阅读者还要解码,要了解什么意思。(数据库表可能不算在内) + +还有借口写成IShapeFactory(大写I代表接口),现在一般不这么写,工作中接口都是ShapeFactoryMapper,或者是ShapeFactoryServices等吧。 + + + +### 避免思维映射 + +没看懂,好显说的是要明确,不要太抽象,其他人看了会想到别的意思。 + + + +### 类名 ** + +类名要尽量使用名词或者名词短语,比如Customer,Account,但是要避免使用Manager,Processer,Data,Info这样的类名(避免使用这几个,个人感觉是这几个意思不明确,这么写比如LibiaryManager 图书管理员 还是没有完全get到点 ),类名不应该是动词。 + + + +### 方法名 + +方法名应该是动词或者动词短语。比如postPayment,deletePage或者save。 + + + +### 别使用可爱的名字 + +别用俚语俗语等奇怪的名字。 + +别用eatMyShorts()表示 abort()(前面这个表示去死吧 后面这个意思是中止,流产,夭折等) + +比如别用whack()表示kill()(whack表示砍柴) + + + +### 每一个概念对应一个词 + +其实项目中就是这么用的 比如controller 叫......Controller, + +有的地方叫.......Api + + + +### 别用双关语 + +比如add,好多地方把插入也叫add,其实应该叫insert,这样可以避免双关语。 + +写代码,我们要的是那种大众化的作者尽量写清楚的平装书模式,而不需要那种学者挖地三尺才能搞清楚的学院派模式。 + + + +对于英语一般的我来说,这个有没有无所谓,反正不会发生在我身上。 + + + +### 使用解决方案命名 + +其实这里和设计模式有关联,比如StringBuider用建造者模式,AccountVisitor用访问者模式等。 + + + +### 添加有意义的语境 + +P26 将实现内容提取出来一个新方法,和if-else一起使用。好用简单。好理解。 + + + +### 不要添加没有意义的语境 + +比如现在这个类就是GSD系统 + +写GSDAccountAddress + +和GSDAccountAddressMail + +前面一大推多余啦。 + + + +其实结合实际开发经验,有时候会有这种写法。 + +如果利用包将功能都分开的话,就没必要这么写啦,这样太长啦。 + + + +### 最后的话 + +取好名字最难得地方就是在于需要良好的描述技巧和共有的文化背景。 + + + +## 第三章 函数 + +### 如何写好函数 + +函数应该尽可能的短小。函数要短小精干。 + +**if语句,else语句,while语句等其中代码块应该只有一句**(这个,有待考量啊),**该行大概应该是一个函数调用的语句**。这样的好处就是,代码块的函数有较好的说明性的名称,可以增加文档上的价值。 + +函数的缩进,层次不该少于一层或者两层。 + + + +### 只做一件事 + +一个函数应该只做一件事,做好这件事。判断一个函数是否不止做了一件事,还有一个方法就是看这个函数能否可以再拆成一个函数。 + + + +### 每个函数一个抽象层级 + +其实就是下面这个意思,内部调用的方法,感觉应该写成private,子方法要写在调用他的方法的下面。 + +```java +public void function1(){ + test1(); + test2(); +} + +private void test1(){ +} + +private void test2(){ +} + +public function2(){ + test3(); + test4(); +} + +private void test1(){ +} + +private void test2(){ +} +``` + + + +if else太多怎么办? 看看 + +https://www.cnblogs.com/eric-shao/p/10115577.html + + + +### switch语句 ** + +没有看懂,说是为了让函数变小,结果用工厂模式,好像也没变小。??? + + + +### 使用描述性的名称 + +“如果每个例程都能让你感到深合己意,那就是代码整洁” + + + +别害怕长名字,长而有描述性的名称,要比短而令人费解的名称好。 + +长而有描述性的名称,要比描述性的长注释好。 + + + +### 函数参数 + +最理想的参数数量是0,其次是1,再次是2,尽量避免3个参数。 + + + +#### 一元函数的普遍形式 + +感觉书上的意思是最好有返回值。有些函数没有返回值,比如事件,要小心使用这种形式。 + + + +#### 标识参数 + +标识参数丑陋不堪。 + +就是说让Boolean值作为函数参数,很明显的说这个函数做的不止一件事,当参数为true的时候,做这么一件事,当参数为false的时候,做那么一件事。 + + + +#### 二元函数 + +二元函数不算恶劣,不过编写二元函数的时候要付出一些代价,应该尽可能的通过一些机制,将二元函数,转换为一元函数。 + +``` +比如writeField(outputStream,name) +``` + +可以写成: + +``` +outputStream.writeField(name) (就是将writeField作为outputStream的成员之一) +``` + +**或者可以将outputStream作为当前类的成员变量,而无需再传递它。** + + + +#### 三元函数 + +注意参数顺序和位置吧。容易出错。 + + + +#### 参数对象 + +当函数看来需要两个,三个或者三个以上的参数,就说明其中一个参数应该封装成类啦。 + +比如: + +```java +Circle makeCircle(double x,double y,double radius); + +Circle makeCircle(Point center,double radius); +``` + +下面这一种明显要优雅许多。 + + + + + +## 第四章 注释 + + + + + + + + + +## 第五章 格式 + + + + + + + + + + + + + + + + + + + +## 第六章 对象和数据结构 + + + + + + + + + + + + + + + + + +## 第七章 错误处理 + + + + + + + + + + + + + + + + + + + + + + + +## 第八章 边界 + + + + + + + + + + + + + + + + + + + + + +## 第九章 单元测试 + + + + + + + + + + + + + + + + + + + + + + + +## 第十章 类 + + + + + + + + + + + + + + + + + + + + + + + +## 第十一章 系统 + + + + + + + + + + + + + + + + + + + + + +## 第十二章 迭代 + + + + + + + + + + + + + + + + + + + + + + + +## 第十三章 并发编程 + + + + + + + + + + + + + + + + + + + + + + + +## 第十四章 逐步改进 + + + + + + + + + + + + + + + + + + + + + +## 第十五章 JUnit内幕 + + + + + + + + + + + + + + + + + + + + + + + +## 第十六章 重构SerialDate + + + + + + + + + + + + + + + + + + + + + +## 第十七章 味道与启发 + + + + + diff --git a/YangLei/Dubbo.assets/04c8cb6e6104504c76086b191c682e19.png b/YangLei/Dubbo.assets/04c8cb6e6104504c76086b191c682e19.png new file mode 100644 index 0000000..7d90e59 Binary files /dev/null and b/YangLei/Dubbo.assets/04c8cb6e6104504c76086b191c682e19.png differ diff --git a/YangLei/Dubbo.assets/07619108db87df25f185a18013636026.png b/YangLei/Dubbo.assets/07619108db87df25f185a18013636026.png new file mode 100644 index 0000000..e1548ef Binary files /dev/null and b/YangLei/Dubbo.assets/07619108db87df25f185a18013636026.png differ diff --git a/YangLei/Dubbo.assets/0766564067b558c3cdac95551d3d953a.png b/YangLei/Dubbo.assets/0766564067b558c3cdac95551d3d953a.png new file mode 100644 index 0000000..f14a527 Binary files /dev/null and b/YangLei/Dubbo.assets/0766564067b558c3cdac95551d3d953a.png differ diff --git a/YangLei/Dubbo.assets/0cb2e6a8664c7ac126ba073055ea1d98.png b/YangLei/Dubbo.assets/0cb2e6a8664c7ac126ba073055ea1d98.png new file mode 100644 index 0000000..a8557fe Binary files /dev/null and b/YangLei/Dubbo.assets/0cb2e6a8664c7ac126ba073055ea1d98.png differ diff --git a/YangLei/Dubbo.assets/116873b8f0e64756d54775a4b40c1d82.png b/YangLei/Dubbo.assets/116873b8f0e64756d54775a4b40c1d82.png new file mode 100644 index 0000000..c34e5f3 Binary files /dev/null and b/YangLei/Dubbo.assets/116873b8f0e64756d54775a4b40c1d82.png differ diff --git a/YangLei/Dubbo.assets/118ad755da8147dcf95f19aa62dd18ff.png b/YangLei/Dubbo.assets/118ad755da8147dcf95f19aa62dd18ff.png new file mode 100644 index 0000000..8aa502f Binary files /dev/null and b/YangLei/Dubbo.assets/118ad755da8147dcf95f19aa62dd18ff.png differ diff --git a/YangLei/Dubbo.assets/11c12babf9fd43b68454fbb9403ec29e.png b/YangLei/Dubbo.assets/11c12babf9fd43b68454fbb9403ec29e.png new file mode 100644 index 0000000..6b72bd1 Binary files /dev/null and b/YangLei/Dubbo.assets/11c12babf9fd43b68454fbb9403ec29e.png differ diff --git a/YangLei/Dubbo.assets/125b23e796ba24d0e683d687662d5e94.png b/YangLei/Dubbo.assets/125b23e796ba24d0e683d687662d5e94.png new file mode 100644 index 0000000..ed40c6e Binary files /dev/null and b/YangLei/Dubbo.assets/125b23e796ba24d0e683d687662d5e94.png differ diff --git a/YangLei/Dubbo.assets/127a68ffe191362a99e5221b6d4e0c97.png b/YangLei/Dubbo.assets/127a68ffe191362a99e5221b6d4e0c97.png new file mode 100644 index 0000000..6f98eeb Binary files /dev/null and b/YangLei/Dubbo.assets/127a68ffe191362a99e5221b6d4e0c97.png differ diff --git a/YangLei/Dubbo.assets/1495afee990ef5edce341497764f38ae.png b/YangLei/Dubbo.assets/1495afee990ef5edce341497764f38ae.png new file mode 100644 index 0000000..52a5c8c Binary files /dev/null and b/YangLei/Dubbo.assets/1495afee990ef5edce341497764f38ae.png differ diff --git a/YangLei/Dubbo.assets/15adcb224fcef4191f9106fd36f62105.png b/YangLei/Dubbo.assets/15adcb224fcef4191f9106fd36f62105.png new file mode 100644 index 0000000..36e062b Binary files /dev/null and b/YangLei/Dubbo.assets/15adcb224fcef4191f9106fd36f62105.png differ diff --git a/YangLei/Dubbo.assets/167a3119d6eb87a271d3708cb48dcc61.png b/YangLei/Dubbo.assets/167a3119d6eb87a271d3708cb48dcc61.png new file mode 100644 index 0000000..34a6c9e Binary files /dev/null and b/YangLei/Dubbo.assets/167a3119d6eb87a271d3708cb48dcc61.png differ diff --git a/YangLei/Dubbo.assets/1ddc209e6ef6a524024997bc53ca3cf1.png b/YangLei/Dubbo.assets/1ddc209e6ef6a524024997bc53ca3cf1.png new file mode 100644 index 0000000..64f1727 Binary files /dev/null and b/YangLei/Dubbo.assets/1ddc209e6ef6a524024997bc53ca3cf1.png differ diff --git a/YangLei/Dubbo.assets/1e3adc3901c569b59d50e303575ef8a3.png b/YangLei/Dubbo.assets/1e3adc3901c569b59d50e303575ef8a3.png new file mode 100644 index 0000000..ddbf36d Binary files /dev/null and b/YangLei/Dubbo.assets/1e3adc3901c569b59d50e303575ef8a3.png differ diff --git a/YangLei/Dubbo.assets/22e8dba61d1030921e6aed34169e2cdd.png b/YangLei/Dubbo.assets/22e8dba61d1030921e6aed34169e2cdd.png new file mode 100644 index 0000000..b5b486a Binary files /dev/null and b/YangLei/Dubbo.assets/22e8dba61d1030921e6aed34169e2cdd.png differ diff --git a/YangLei/Dubbo.assets/24c31648ebc1e8930711fd2d3574c171.png b/YangLei/Dubbo.assets/24c31648ebc1e8930711fd2d3574c171.png new file mode 100644 index 0000000..fbe3a3a Binary files /dev/null and b/YangLei/Dubbo.assets/24c31648ebc1e8930711fd2d3574c171.png differ diff --git a/YangLei/Dubbo.assets/26954a843fb9f74e9cde7c16c19902e4.png b/YangLei/Dubbo.assets/26954a843fb9f74e9cde7c16c19902e4.png new file mode 100644 index 0000000..5ca6793 Binary files /dev/null and b/YangLei/Dubbo.assets/26954a843fb9f74e9cde7c16c19902e4.png differ diff --git a/YangLei/Dubbo.assets/27e77f44be552ada54ffea71bdfd543e.png b/YangLei/Dubbo.assets/27e77f44be552ada54ffea71bdfd543e.png new file mode 100644 index 0000000..64672df Binary files /dev/null and b/YangLei/Dubbo.assets/27e77f44be552ada54ffea71bdfd543e.png differ diff --git a/YangLei/Dubbo.assets/2a8076a9b4bb260775a7d40d71d222a6.png b/YangLei/Dubbo.assets/2a8076a9b4bb260775a7d40d71d222a6.png new file mode 100644 index 0000000..164a6e9 Binary files /dev/null and b/YangLei/Dubbo.assets/2a8076a9b4bb260775a7d40d71d222a6.png differ diff --git a/YangLei/Dubbo.assets/2a9bd7920f7813c110ef1a937fdaf30b.png b/YangLei/Dubbo.assets/2a9bd7920f7813c110ef1a937fdaf30b.png new file mode 100644 index 0000000..eeb6d64 Binary files /dev/null and b/YangLei/Dubbo.assets/2a9bd7920f7813c110ef1a937fdaf30b.png differ diff --git a/YangLei/Dubbo.assets/2b761916433447e02e3da361c8dc94e6.png b/YangLei/Dubbo.assets/2b761916433447e02e3da361c8dc94e6.png new file mode 100644 index 0000000..cf77951 Binary files /dev/null and b/YangLei/Dubbo.assets/2b761916433447e02e3da361c8dc94e6.png differ diff --git a/YangLei/Dubbo.assets/2ce43942af3f1ff23b44b75314bfd3af.png b/YangLei/Dubbo.assets/2ce43942af3f1ff23b44b75314bfd3af.png new file mode 100644 index 0000000..e8ecf43 Binary files /dev/null and b/YangLei/Dubbo.assets/2ce43942af3f1ff23b44b75314bfd3af.png differ diff --git a/YangLei/Dubbo.assets/32527396-1590722959236.jpg b/YangLei/Dubbo.assets/32527396-1590722959236.jpg new file mode 100644 index 0000000..5888b22 Binary files /dev/null and b/YangLei/Dubbo.assets/32527396-1590722959236.jpg differ diff --git a/YangLei/Dubbo.assets/32527396.jpg b/YangLei/Dubbo.assets/32527396.jpg new file mode 100644 index 0000000..5888b22 Binary files /dev/null and b/YangLei/Dubbo.assets/32527396.jpg differ diff --git a/YangLei/Dubbo.assets/32a3c0f74c325ec723440dcc369cd82c.png b/YangLei/Dubbo.assets/32a3c0f74c325ec723440dcc369cd82c.png new file mode 100644 index 0000000..9099616 Binary files /dev/null and b/YangLei/Dubbo.assets/32a3c0f74c325ec723440dcc369cd82c.png differ diff --git a/YangLei/Dubbo.assets/37345851-1590722959226.jpg b/YangLei/Dubbo.assets/37345851-1590722959226.jpg new file mode 100644 index 0000000..d3714ef Binary files /dev/null and b/YangLei/Dubbo.assets/37345851-1590722959226.jpg differ diff --git a/YangLei/Dubbo.assets/37345851.jpg b/YangLei/Dubbo.assets/37345851.jpg new file mode 100644 index 0000000..d3714ef Binary files /dev/null and b/YangLei/Dubbo.assets/37345851.jpg differ diff --git a/YangLei/Dubbo.assets/391bfa438a9991af7f55b236c2a73e12.png b/YangLei/Dubbo.assets/391bfa438a9991af7f55b236c2a73e12.png new file mode 100644 index 0000000..843076e Binary files /dev/null and b/YangLei/Dubbo.assets/391bfa438a9991af7f55b236c2a73e12.png differ diff --git a/YangLei/Dubbo.assets/412a0bf298b75f5ccd29d43c53426c1d.png b/YangLei/Dubbo.assets/412a0bf298b75f5ccd29d43c53426c1d.png new file mode 100644 index 0000000..1def45f Binary files /dev/null and b/YangLei/Dubbo.assets/412a0bf298b75f5ccd29d43c53426c1d.png differ diff --git a/YangLei/Dubbo.assets/43050183.jpg b/YangLei/Dubbo.assets/43050183.jpg new file mode 100644 index 0000000..84c7577 Binary files /dev/null and b/YangLei/Dubbo.assets/43050183.jpg differ diff --git a/YangLei/Dubbo.assets/4555e69fc2b8656871ef69a1da3097cc.png b/YangLei/Dubbo.assets/4555e69fc2b8656871ef69a1da3097cc.png new file mode 100644 index 0000000..fd29aaf Binary files /dev/null and b/YangLei/Dubbo.assets/4555e69fc2b8656871ef69a1da3097cc.png differ diff --git a/YangLei/Dubbo.assets/46816446.jpg b/YangLei/Dubbo.assets/46816446.jpg new file mode 100644 index 0000000..333c30d Binary files /dev/null and b/YangLei/Dubbo.assets/46816446.jpg differ diff --git a/YangLei/Dubbo.assets/4a0dbe731faa86194e46031ef5df36f2.png b/YangLei/Dubbo.assets/4a0dbe731faa86194e46031ef5df36f2.png new file mode 100644 index 0000000..6700f6d Binary files /dev/null and b/YangLei/Dubbo.assets/4a0dbe731faa86194e46031ef5df36f2.png differ diff --git a/YangLei/Dubbo.assets/4ae2855b71fd5fa7c8a6ce98e97e5066.png b/YangLei/Dubbo.assets/4ae2855b71fd5fa7c8a6ce98e97e5066.png new file mode 100644 index 0000000..eeb76cf Binary files /dev/null and b/YangLei/Dubbo.assets/4ae2855b71fd5fa7c8a6ce98e97e5066.png differ diff --git a/YangLei/Dubbo.assets/4af8d78e7df99c245100ea374eb7b2e3.png b/YangLei/Dubbo.assets/4af8d78e7df99c245100ea374eb7b2e3.png new file mode 100644 index 0000000..5aedc91 Binary files /dev/null and b/YangLei/Dubbo.assets/4af8d78e7df99c245100ea374eb7b2e3.png differ diff --git a/YangLei/Dubbo.assets/4cb52dce8dcdca21462f7f9019c73659.png b/YangLei/Dubbo.assets/4cb52dce8dcdca21462f7f9019c73659.png new file mode 100644 index 0000000..795aa10 Binary files /dev/null and b/YangLei/Dubbo.assets/4cb52dce8dcdca21462f7f9019c73659.png differ diff --git a/YangLei/Dubbo.assets/4dc25fa09547d7d41812a63e195cfd2b.png b/YangLei/Dubbo.assets/4dc25fa09547d7d41812a63e195cfd2b.png new file mode 100644 index 0000000..acdce98 Binary files /dev/null and b/YangLei/Dubbo.assets/4dc25fa09547d7d41812a63e195cfd2b.png differ diff --git a/YangLei/Dubbo.assets/4f4062b2cf24f0b5389881f84d0ad11e.png b/YangLei/Dubbo.assets/4f4062b2cf24f0b5389881f84d0ad11e.png new file mode 100644 index 0000000..3e2b308 Binary files /dev/null and b/YangLei/Dubbo.assets/4f4062b2cf24f0b5389881f84d0ad11e.png differ diff --git a/YangLei/Dubbo.assets/51fe4c42da71e48176d5ca19da87e283.png b/YangLei/Dubbo.assets/51fe4c42da71e48176d5ca19da87e283.png new file mode 100644 index 0000000..d752021 Binary files /dev/null and b/YangLei/Dubbo.assets/51fe4c42da71e48176d5ca19da87e283.png differ diff --git a/YangLei/Dubbo.assets/571e012ddd402ef93dae007ba9589fb9.png b/YangLei/Dubbo.assets/571e012ddd402ef93dae007ba9589fb9.png new file mode 100644 index 0000000..a60c482 Binary files /dev/null and b/YangLei/Dubbo.assets/571e012ddd402ef93dae007ba9589fb9.png differ diff --git a/YangLei/Dubbo.assets/57a0844a42642946790e94fe6b33e488.png b/YangLei/Dubbo.assets/57a0844a42642946790e94fe6b33e488.png new file mode 100644 index 0000000..14906af Binary files /dev/null and b/YangLei/Dubbo.assets/57a0844a42642946790e94fe6b33e488.png differ diff --git a/YangLei/Dubbo.assets/5af9e605d5751eec4aa074727bdb3d79.png b/YangLei/Dubbo.assets/5af9e605d5751eec4aa074727bdb3d79.png new file mode 100644 index 0000000..7806f29 Binary files /dev/null and b/YangLei/Dubbo.assets/5af9e605d5751eec4aa074727bdb3d79.png differ diff --git a/YangLei/Dubbo.assets/5bb55b0b35a442e2882accb51ad945dd.png b/YangLei/Dubbo.assets/5bb55b0b35a442e2882accb51ad945dd.png new file mode 100644 index 0000000..fd35e78 Binary files /dev/null and b/YangLei/Dubbo.assets/5bb55b0b35a442e2882accb51ad945dd.png differ diff --git a/YangLei/Dubbo.assets/5df28bc9df4c9e48ef7fe21319ca30f3.png b/YangLei/Dubbo.assets/5df28bc9df4c9e48ef7fe21319ca30f3.png new file mode 100644 index 0000000..8dc1881 Binary files /dev/null and b/YangLei/Dubbo.assets/5df28bc9df4c9e48ef7fe21319ca30f3.png differ diff --git a/YangLei/Dubbo.assets/60b2c5c4cf5cb7ac83a51b3a7cfddc62.png b/YangLei/Dubbo.assets/60b2c5c4cf5cb7ac83a51b3a7cfddc62.png new file mode 100644 index 0000000..f0f0659 Binary files /dev/null and b/YangLei/Dubbo.assets/60b2c5c4cf5cb7ac83a51b3a7cfddc62.png differ diff --git a/YangLei/Dubbo.assets/625f185c66747f68f35690b1f7757cc0.png b/YangLei/Dubbo.assets/625f185c66747f68f35690b1f7757cc0.png new file mode 100644 index 0000000..d3347fb Binary files /dev/null and b/YangLei/Dubbo.assets/625f185c66747f68f35690b1f7757cc0.png differ diff --git a/YangLei/Dubbo.assets/640-1588995722550.webp b/YangLei/Dubbo.assets/640-1588995722550.webp new file mode 100644 index 0000000..57c8518 Binary files /dev/null and b/YangLei/Dubbo.assets/640-1588995722550.webp differ diff --git a/YangLei/Dubbo.assets/640-1588995722584.webp b/YangLei/Dubbo.assets/640-1588995722584.webp new file mode 100644 index 0000000..e42cb55 Binary files /dev/null and b/YangLei/Dubbo.assets/640-1588995722584.webp differ diff --git a/YangLei/Dubbo.assets/640-1588995722595.webp b/YangLei/Dubbo.assets/640-1588995722595.webp new file mode 100644 index 0000000..0719a48 Binary files /dev/null and b/YangLei/Dubbo.assets/640-1588995722595.webp differ diff --git a/YangLei/Dubbo.assets/640-1588995722606.webp b/YangLei/Dubbo.assets/640-1588995722606.webp new file mode 100644 index 0000000..822e41d Binary files /dev/null and b/YangLei/Dubbo.assets/640-1588995722606.webp differ diff --git a/YangLei/Dubbo.assets/640.png b/YangLei/Dubbo.assets/640.png new file mode 100644 index 0000000..0c8f17d Binary files /dev/null and b/YangLei/Dubbo.assets/640.png differ diff --git a/YangLei/Dubbo.assets/640.webp b/YangLei/Dubbo.assets/640.webp new file mode 100644 index 0000000..b564ae9 Binary files /dev/null and b/YangLei/Dubbo.assets/640.webp differ diff --git a/YangLei/Dubbo.assets/64702923.jpg b/YangLei/Dubbo.assets/64702923.jpg new file mode 100644 index 0000000..005408a Binary files /dev/null and b/YangLei/Dubbo.assets/64702923.jpg differ diff --git a/YangLei/Dubbo.assets/67ec53833e12374d957954c3881f6510.png b/YangLei/Dubbo.assets/67ec53833e12374d957954c3881f6510.png new file mode 100644 index 0000000..e61a37c Binary files /dev/null and b/YangLei/Dubbo.assets/67ec53833e12374d957954c3881f6510.png differ diff --git a/YangLei/Dubbo.assets/6a7c6b3b2755feb69455529e8497395b.png b/YangLei/Dubbo.assets/6a7c6b3b2755feb69455529e8497395b.png new file mode 100644 index 0000000..c4eedf9 Binary files /dev/null and b/YangLei/Dubbo.assets/6a7c6b3b2755feb69455529e8497395b.png differ diff --git a/YangLei/Dubbo.assets/7131d4fb96a772e2ce12dc39c84e0718.png b/YangLei/Dubbo.assets/7131d4fb96a772e2ce12dc39c84e0718.png new file mode 100644 index 0000000..d5a9a2a Binary files /dev/null and b/YangLei/Dubbo.assets/7131d4fb96a772e2ce12dc39c84e0718.png differ diff --git a/YangLei/Dubbo.assets/730080cbd8e58d9bde067d8b0903a607.png b/YangLei/Dubbo.assets/730080cbd8e58d9bde067d8b0903a607.png new file mode 100644 index 0000000..0e140dc Binary files /dev/null and b/YangLei/Dubbo.assets/730080cbd8e58d9bde067d8b0903a607.png differ diff --git a/YangLei/Dubbo.assets/77722327.jpg b/YangLei/Dubbo.assets/77722327.jpg new file mode 100644 index 0000000..6f58b6e Binary files /dev/null and b/YangLei/Dubbo.assets/77722327.jpg differ diff --git a/YangLei/Dubbo.assets/8113cfa60b0ec87420404f95fc6c0126.png b/YangLei/Dubbo.assets/8113cfa60b0ec87420404f95fc6c0126.png new file mode 100644 index 0000000..3ae68f2 Binary files /dev/null and b/YangLei/Dubbo.assets/8113cfa60b0ec87420404f95fc6c0126.png differ diff --git a/YangLei/Dubbo.assets/8641ddc536193a03a16a54f50d9e2405.png b/YangLei/Dubbo.assets/8641ddc536193a03a16a54f50d9e2405.png new file mode 100644 index 0000000..d661ff2 Binary files /dev/null and b/YangLei/Dubbo.assets/8641ddc536193a03a16a54f50d9e2405.png differ diff --git a/YangLei/Dubbo.assets/865681c44f921c699fdc928a19eb4a9e.png b/YangLei/Dubbo.assets/865681c44f921c699fdc928a19eb4a9e.png new file mode 100644 index 0000000..4ff5d72 Binary files /dev/null and b/YangLei/Dubbo.assets/865681c44f921c699fdc928a19eb4a9e.png differ diff --git a/YangLei/Dubbo.assets/89ede97f1c1d1fcdf78d604efd72cb1d.png b/YangLei/Dubbo.assets/89ede97f1c1d1fcdf78d604efd72cb1d.png new file mode 100644 index 0000000..382c926 Binary files /dev/null and b/YangLei/Dubbo.assets/89ede97f1c1d1fcdf78d604efd72cb1d.png differ diff --git a/YangLei/Dubbo.assets/8a9851260dd0ecae0c618170f242ff13.png b/YangLei/Dubbo.assets/8a9851260dd0ecae0c618170f242ff13.png new file mode 100644 index 0000000..248cebe Binary files /dev/null and b/YangLei/Dubbo.assets/8a9851260dd0ecae0c618170f242ff13.png differ diff --git a/YangLei/Dubbo.assets/8d9f3596a9273968defb166629b682bb.png b/YangLei/Dubbo.assets/8d9f3596a9273968defb166629b682bb.png new file mode 100644 index 0000000..dbac816 Binary files /dev/null and b/YangLei/Dubbo.assets/8d9f3596a9273968defb166629b682bb.png differ diff --git a/YangLei/Dubbo.assets/8f6c780959c4b54d2f2ba4678e912817.png b/YangLei/Dubbo.assets/8f6c780959c4b54d2f2ba4678e912817.png new file mode 100644 index 0000000..2b70eaa Binary files /dev/null and b/YangLei/Dubbo.assets/8f6c780959c4b54d2f2ba4678e912817.png differ diff --git a/YangLei/Dubbo.assets/97933247.jpg b/YangLei/Dubbo.assets/97933247.jpg new file mode 100644 index 0000000..b94f386 Binary files /dev/null and b/YangLei/Dubbo.assets/97933247.jpg differ diff --git a/YangLei/Dubbo.assets/a178f9aaf032b2830966f554dcd3a43c.png b/YangLei/Dubbo.assets/a178f9aaf032b2830966f554dcd3a43c.png new file mode 100644 index 0000000..46b1f86 Binary files /dev/null and b/YangLei/Dubbo.assets/a178f9aaf032b2830966f554dcd3a43c.png differ diff --git a/YangLei/Dubbo.assets/a2e28d69a76f38bb32409e87c3202c2f.png b/YangLei/Dubbo.assets/a2e28d69a76f38bb32409e87c3202c2f.png new file mode 100644 index 0000000..3a3220e Binary files /dev/null and b/YangLei/Dubbo.assets/a2e28d69a76f38bb32409e87c3202c2f.png differ diff --git a/YangLei/Dubbo.assets/a4a494abc4050b987ef6ff9c37134c69.png b/YangLei/Dubbo.assets/a4a494abc4050b987ef6ff9c37134c69.png new file mode 100644 index 0000000..df4c05f Binary files /dev/null and b/YangLei/Dubbo.assets/a4a494abc4050b987ef6ff9c37134c69.png differ diff --git a/YangLei/Dubbo.assets/a5ed18885a7240334ad1b8cf16d6f785.png b/YangLei/Dubbo.assets/a5ed18885a7240334ad1b8cf16d6f785.png new file mode 100644 index 0000000..731e5c8 Binary files /dev/null and b/YangLei/Dubbo.assets/a5ed18885a7240334ad1b8cf16d6f785.png differ diff --git a/YangLei/Dubbo.assets/a7db731855aaf8a42e32d6130bc9c215.png b/YangLei/Dubbo.assets/a7db731855aaf8a42e32d6130bc9c215.png new file mode 100644 index 0000000..8ed0664 Binary files /dev/null and b/YangLei/Dubbo.assets/a7db731855aaf8a42e32d6130bc9c215.png differ diff --git a/YangLei/Dubbo.assets/abe0094bdc581e819951d987450a3cc4.png b/YangLei/Dubbo.assets/abe0094bdc581e819951d987450a3cc4.png new file mode 100644 index 0000000..67464b0 Binary files /dev/null and b/YangLei/Dubbo.assets/abe0094bdc581e819951d987450a3cc4.png differ diff --git a/YangLei/Dubbo.assets/ac8ff59370709f3755eec59630f93b2e.png b/YangLei/Dubbo.assets/ac8ff59370709f3755eec59630f93b2e.png new file mode 100644 index 0000000..8fecb8f Binary files /dev/null and b/YangLei/Dubbo.assets/ac8ff59370709f3755eec59630f93b2e.png differ diff --git a/YangLei/Dubbo.assets/acc8b5168c63f753c642f845b11de339.png b/YangLei/Dubbo.assets/acc8b5168c63f753c642f845b11de339.png new file mode 100644 index 0000000..290af69 Binary files /dev/null and b/YangLei/Dubbo.assets/acc8b5168c63f753c642f845b11de339.png differ diff --git a/YangLei/Dubbo.assets/b1c3e2a952896cdd1b44ae24315b7253.png b/YangLei/Dubbo.assets/b1c3e2a952896cdd1b44ae24315b7253.png new file mode 100644 index 0000000..ed0c2e6 Binary files /dev/null and b/YangLei/Dubbo.assets/b1c3e2a952896cdd1b44ae24315b7253.png differ diff --git a/YangLei/Dubbo.assets/bb743b6f6d742f4917a9d8d8c19cdc70.png b/YangLei/Dubbo.assets/bb743b6f6d742f4917a9d8d8c19cdc70.png new file mode 100644 index 0000000..586580b Binary files /dev/null and b/YangLei/Dubbo.assets/bb743b6f6d742f4917a9d8d8c19cdc70.png differ diff --git a/YangLei/Dubbo.assets/bd408a2b9d05020774f00010c52a9d9c.png b/YangLei/Dubbo.assets/bd408a2b9d05020774f00010c52a9d9c.png new file mode 100644 index 0000000..102c214 Binary files /dev/null and b/YangLei/Dubbo.assets/bd408a2b9d05020774f00010c52a9d9c.png differ diff --git a/YangLei/Dubbo.assets/be01386bf19f71fe8467bf20132bcc0c.png b/YangLei/Dubbo.assets/be01386bf19f71fe8467bf20132bcc0c.png new file mode 100644 index 0000000..3f0bb3f Binary files /dev/null and b/YangLei/Dubbo.assets/be01386bf19f71fe8467bf20132bcc0c.png differ diff --git a/YangLei/Dubbo.assets/c7e6d9f38b4ef5eb42001a8ef20f3560.png b/YangLei/Dubbo.assets/c7e6d9f38b4ef5eb42001a8ef20f3560.png new file mode 100644 index 0000000..6fb5dfb Binary files /dev/null and b/YangLei/Dubbo.assets/c7e6d9f38b4ef5eb42001a8ef20f3560.png differ diff --git a/YangLei/Dubbo.assets/d77c37dd3a3a108509e903211a14ff94.png b/YangLei/Dubbo.assets/d77c37dd3a3a108509e903211a14ff94.png new file mode 100644 index 0000000..39089f1 Binary files /dev/null and b/YangLei/Dubbo.assets/d77c37dd3a3a108509e903211a14ff94.png differ diff --git a/YangLei/Dubbo.assets/dab02c1d9a41b181d8710bc7ce9a6ae7.png b/YangLei/Dubbo.assets/dab02c1d9a41b181d8710bc7ce9a6ae7.png new file mode 100644 index 0000000..5ce689d Binary files /dev/null and b/YangLei/Dubbo.assets/dab02c1d9a41b181d8710bc7ce9a6ae7.png differ diff --git a/YangLei/Dubbo.assets/dcc02d102b9eb1eaa70f932afaa399a8.png b/YangLei/Dubbo.assets/dcc02d102b9eb1eaa70f932afaa399a8.png new file mode 100644 index 0000000..26469e2 Binary files /dev/null and b/YangLei/Dubbo.assets/dcc02d102b9eb1eaa70f932afaa399a8.png differ diff --git a/YangLei/Dubbo.assets/dd06bd3a7e584111a4c8a408e78ddba4.png b/YangLei/Dubbo.assets/dd06bd3a7e584111a4c8a408e78ddba4.png new file mode 100644 index 0000000..d50617f Binary files /dev/null and b/YangLei/Dubbo.assets/dd06bd3a7e584111a4c8a408e78ddba4.png differ diff --git a/YangLei/Dubbo.assets/ea1e741ce818c1e439ab913c4c84e620.png b/YangLei/Dubbo.assets/ea1e741ce818c1e439ab913c4c84e620.png new file mode 100644 index 0000000..e6ade19 Binary files /dev/null and b/YangLei/Dubbo.assets/ea1e741ce818c1e439ab913c4c84e620.png differ diff --git a/YangLei/Dubbo.assets/ec83dfc5f99bf5219216aa4f80d1e1c4.png b/YangLei/Dubbo.assets/ec83dfc5f99bf5219216aa4f80d1e1c4.png new file mode 100644 index 0000000..8ed6dbb Binary files /dev/null and b/YangLei/Dubbo.assets/ec83dfc5f99bf5219216aa4f80d1e1c4.png differ diff --git a/YangLei/Dubbo.assets/edadf78718f7c269dbcb13406ec29b03.png b/YangLei/Dubbo.assets/edadf78718f7c269dbcb13406ec29b03.png new file mode 100644 index 0000000..2b705eb Binary files /dev/null and b/YangLei/Dubbo.assets/edadf78718f7c269dbcb13406ec29b03.png differ diff --git a/YangLei/Dubbo.assets/f0e79697c6c1d5415d3c878f84d3b34c.png b/YangLei/Dubbo.assets/f0e79697c6c1d5415d3c878f84d3b34c.png new file mode 100644 index 0000000..4d888e1 Binary files /dev/null and b/YangLei/Dubbo.assets/f0e79697c6c1d5415d3c878f84d3b34c.png differ diff --git a/YangLei/Dubbo.assets/f20cc42b7e9a13f584f39ab87bdd02ce.png b/YangLei/Dubbo.assets/f20cc42b7e9a13f584f39ab87bdd02ce.png new file mode 100644 index 0000000..5624acf Binary files /dev/null and b/YangLei/Dubbo.assets/f20cc42b7e9a13f584f39ab87bdd02ce.png differ diff --git a/YangLei/Dubbo.assets/f5d603dd5304e5827ab7a95b6a5c1f7a.png b/YangLei/Dubbo.assets/f5d603dd5304e5827ab7a95b6a5c1f7a.png new file mode 100644 index 0000000..2834ce6 Binary files /dev/null and b/YangLei/Dubbo.assets/f5d603dd5304e5827ab7a95b6a5c1f7a.png differ diff --git a/YangLei/Dubbo.assets/fa1e3e4cc8f504c746e87a1e01ce7286.png b/YangLei/Dubbo.assets/fa1e3e4cc8f504c746e87a1e01ce7286.png new file mode 100644 index 0000000..109579f Binary files /dev/null and b/YangLei/Dubbo.assets/fa1e3e4cc8f504c746e87a1e01ce7286.png differ diff --git a/YangLei/Dubbo.assets/fb0154c9a3d57c61127dae4a3413f9db.png b/YangLei/Dubbo.assets/fb0154c9a3d57c61127dae4a3413f9db.png new file mode 100644 index 0000000..bab7d69 Binary files /dev/null and b/YangLei/Dubbo.assets/fb0154c9a3d57c61127dae4a3413f9db.png differ diff --git a/YangLei/Dubbo.assets/fc0f480ef3e9e2d133c8418bc8aa5b2a.png b/YangLei/Dubbo.assets/fc0f480ef3e9e2d133c8418bc8aa5b2a.png new file mode 100644 index 0000000..7aeae28 Binary files /dev/null and b/YangLei/Dubbo.assets/fc0f480ef3e9e2d133c8418bc8aa5b2a.png differ diff --git a/YangLei/Dubbo.assets/ffd1647ad2a27e2a3cbd4bd9fd668ce0.png b/YangLei/Dubbo.assets/ffd1647ad2a27e2a3cbd4bd9fd668ce0.png new file mode 100644 index 0000000..09e2168 Binary files /dev/null and b/YangLei/Dubbo.assets/ffd1647ad2a27e2a3cbd4bd9fd668ce0.png differ diff --git a/YangLei/Dubbo.md b/YangLei/Dubbo.md new file mode 100644 index 0000000..a4ecd71 --- /dev/null +++ b/YangLei/Dubbo.md @@ -0,0 +1,1655 @@ +# Dubbo + +# JavaGuide-RPC + +## 1.什么是 RPC?RPC原理是什么? + +### **什么是 RPC?** + +RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务 A、B 部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求 当然可以,但是可能会比较慢而且一些优化做的并不好。 RPC 的出现就是为了解决这个问题。 + +### **RPC原理是什么?** + +我这里这是简单的提一下,详细内容可以查看下面这篇文章: + +http://www.importnew.com/22003.html + +![RPC原理图](Dubbo.assets/37345851-1590722959226.jpg) + +1. 服务消费方(client)调用以本地调用方式调用服务; +2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体; +3. client stub找到服务地址,并将消息发送到服务端; +4. server stub收到消息后进行解码; +5. server stub根据解码结果调用本地的服务; +6. 本地服务执行并将结果返回给server stub; +7. server stub将返回结果打包成消息并发送至消费方; +8. client stub接收到消息,并进行解码; +9. 服务消费方得到最终结果。 + +下面再贴一个网上的时序图: + +![RPC原理时序图](Dubbo.assets/32527396-1590722959236.jpg) + +### RPC 解决了什么问题? + +从上面对 RPC 介绍的内容中,概括来讲RPC 主要解决了:**让分布式或者微服务系统中不同服务之间的调用像本地调用一样简单。** + +### 常见的 RPC 框架总结? + +- **RMI(JDK自带):** JDK自带的RPC,有很多局限性,不推荐使用。 +- **Dubbo:** Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。目前 Dubbo 已经成为 Spring Cloud Alibaba 中的官方组件。 +- **gRPC** :gRPC是可以在任何环境中运行的现代开源高性能RPC框架。它可以通过可插拔的支持来有效地连接数据中心内和跨数据中心的服务,以实现负载平衡,跟踪,运行状况检查和身份验证。它也适用于分布式计算的最后一英里,以将设备,移动应用程序和浏览器连接到后端服务。 +- **Hessian:** Hessian是一个轻量级的remotingonhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。 +- **Thrift:** Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于thrift研发一套分布式服务框架,增加诸如服务注册、服务发现等功能。 + +## 2.既有 HTTP ,为啥用 RPC 进行服务调用? + +### RPC 只是一种设计而已 + +RPC 只是一种概念、一种设计,就是为了解决 **不同服务之间的调用问题**, 它一般会包含有 **传输协议** 和 **序列化协议** 这两个。 + +但是,HTTP 是一种协议,RPC框架可以使用 HTTP协议作为传输协议或者直接使用TCP作为传输协议,使用不同的协议一般也是为了适应不同的场景。 + +### HTTP 和 TCP + +**可能现在很多对计算机网络不太熟悉的朋友已经被搞蒙了,要想真正搞懂,还需要来简单复习一下计算机网络基础知识:** + +> 我们通常谈计算机网络的五层协议的体系结构是指:应用层、传输层、网络层、数据链路层、物理层。 +> +> **应用层(application-layer)的任务是通过应用进程间的交互来完成特定网络应用。**HTTP 属于应用层协议,它会基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)。HTTP协议工作于客户端-服务端架构为上。浏览器作为HTTP客户端通过 URL 向HTTP服务端即WEB服务器发送所有请求。Web服务器根据接收到的请求后,向客户端发送响应信息。HTTP协议建立在 TCP 协议之上。 +> +> **运输层(transport layer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输服务**。TCP是传输层协议,主要解决数据如何在网络中传输。相比于UDP,**TCP** 提供的是**面向连接**的,**可靠的**数据传输服务。 + +### RPC框架功能更齐全 + +成熟的 RPC框架还提供好了“服务自动注册与发现”、"智能负载均衡"、“可视化的服务治理和运维”、“运行期流量调度”等等功能,这些也算是选择 +RPC 进行服务注册和发现的一方面原因吧! + +**相关阅读:** + +- http://www.ruanyifeng.com/blog/2016/08/http.html (HTTP 协议入门- 阮一峰) + +### 一个常见的错误观点 + +很多文章中还会提到说 HTTP 协议相较于自定义 TCP 报文协议,增加的开销在于连接的建立与断开,但是这个观点已经被否认,下面截取自知乎中一个回答,原回答地址:https://www.zhihu.com/question/41609070/answer/191965937。 + +> 首先要否认一点 HTTP 协议相较于自定义 TCP 报文协议,增加的开销在于连接的建立与断开。HTTP 协议是支持连接池复用的,也就是建立一定数量的连接不断开,并不会频繁的创建和销毁连接。二一要说的是 HTTP 也可以使用 Protobuf 这种二进制编码协议对内容进行编码,因此二者最大的区别还是在传输协议上。 + + + + + +# JavaGuide-Dubbo + +本文是作者根据官方文档以及自己平时的使用情况,对 Dubbo 所做的一个总结。如果不懂 Dubbo 的使用的话,可以参考我的这篇文章[《超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务》](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484706&idx=1&sn=d413fc17023482f67ca17cb6756b9ff8&chksm=fd985343caefda555969568fdf4734536e0a1745f9de337d434a7dbd04e893bd2d75f3641aab&token=1902169190&lang=zh_CN#rd) + +Dubbo 官网:http://dubbo.apache.org/zh-cn/index.html + +Dubbo 中文文档: http://dubbo.apache.org/zh-cn/index.html + + + +- [一 重要的概念](#一-重要的概念) + - [1.1 什么是 Dubbo?](#11-什么是-dubbo) + - [1.2 什么是 RPC?RPC原理是什么?](#12-什么是-rpcrpc原理是什么) + - [1.3 为什么要用 Dubbo?](#13-为什么要用-dubbo) + - [1.4 什么是分布式?](#14-什么是分布式) + - [1.5 为什么要分布式?](#15-为什么要分布式) +- [二 Dubbo 的架构](#二-dubbo-的架构) + - [2.1 Dubbo 的架构图解](#21-dubbo-的架构图解) + - [2.2 Dubbo 工作原理](#22-dubbo-工作原理) +- [三 Dubbo 的负载均衡策略](#三-dubbo-的负载均衡策略) + - [3.1 先来解释一下什么是负载均衡](#31-先来解释一下什么是负载均衡) + - [3.2 再来看看 Dubbo 提供的负载均衡策略](#32-再来看看-dubbo-提供的负载均衡策略) + - [3.2.1 Random LoadBalance\(默认,基于权重的随机负载均衡机制\)](#321-random-loadbalance默认基于权重的随机负载均衡机制) + - [3.2.2 RoundRobin LoadBalance\(不推荐,基于权重的轮询负载均衡机制\)](#322-roundrobin-loadbalance不推荐基于权重的轮询负载均衡机制) + - [3.2.3 LeastActive LoadBalance](#323-leastactive-loadbalance) + - [3.2.4 ConsistentHash LoadBalance](#324-consistenthash-loadbalance) + - [3.3 配置方式](#33-配置方式) +- [四 zookeeper宕机与dubbo直连的情况](#四-zookeeper宕机与dubbo直连的情况) + + + +## 一 重要的概念 + +### 1.1 什么是 Dubbo? + +Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。简单来说 Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。 + +Dubbo 目前已经有接近 23k 的 Star ,Dubbo的Github 地址:[https://github.com/apache/incubator-dubbo](https://github.com/apache/incubator-dubbo) 。 另外,在开源中国举行的2018年度最受欢迎中国开源软件这个活动的评选中,Dubbo 更是凭借其超高人气仅次于 vue.js 和 ECharts 获得第三名的好成绩。 + +Dubbo 是由阿里开源,后来加入了 Apache 。正式由于 Dubbo 的出现,才使得越来越多的公司开始使用以及接受分布式架构。 + +**我们上面说了 Dubbo 实际上是 RPC 框架,那么什么是 RPC呢?** + +### 1.2 什么是 RPC?RPC原理是什么? + +**什么是 RPC?** + +RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务 A、B 部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求当然可以,但是可能会比较麻烦。 RPC 的出现就是为了让你调用远程方法像调用本地方法一样简单。 + +**RPC原理是什么?** + +我这里这是简单的提一下。详细内容可以查看下面这篇文章: + +[http://www.importnew.com/22003.html](http://www.importnew.com/22003.html) + +![RPC原理图](Dubbo.assets/37345851.jpg) + +1. 服务消费方(client)调用以本地调用方式调用服务; +2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体; +3. client stub找到服务地址,并将消息发送到服务端; +4. server stub收到消息后进行解码; +5. server stub根据解码结果调用本地的服务; +6. 本地服务执行并将结果返回给server stub; +7. server stub将返回结果打包成消息并发送至消费方; +8. client stub接收到消息,并进行解码; +9. 服务消费方得到最终结果。 + +下面再贴一个网上的时序图: + +![RPC原理时序图](Dubbo.assets/32527396.jpg) + +**说了这么多,我们为什么要用 Dubbo 呢?** + +### 1.3 为什么要用 Dubbo? + +Dubbo 的诞生和 SOA 分布式架构的流行有着莫大的关系。SOA 面向服务的架构(Service Oriented Architecture),也就是把工程按照业务逻辑拆分成服务层、表现层两个工程。服务层中包含业务逻辑,只需要对外提供服务即可。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。SOA架构中有两个主要角色:服务提供者(Provider)和服务使用者(Consumer)。 + +![为什么要用 Dubbo](Dubbo.assets/43050183.jpg) + +**如果你要开发分布式程序,你也可以直接基于 HTTP 接口进行通信,但是为什么要用 Dubbo呢?** + +我觉得主要可以从 Dubbo 提供的下面四点特性来说为什么要用 Dubbo: + +1. **负载均衡**——同一个服务部署在不同的机器时该调用那一台机器上的服务。 +2. **服务调用链路生成**——随着系统的发展,服务越来越多,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。Dubbo 可以为我们解决服务之间互相是如何调用的。 +3. **服务访问压力以及时长统计、资源调度和治理**——基于访问压力实时管理集群容量,提高集群利用率。 +4. **服务降级**——某个服务挂掉之后调用备用服务。 + +另外,Dubbo 除了能够应用在分布式系统中,也可以应用在现在比较火的微服务系统中。不过,由于 Spring Cloud 在微服务中应用更加广泛,所以,我觉得一般我们提 Dubbo 的话,大部分是分布式系统的情况。 + +**我们刚刚提到了分布式这个概念,下面再给大家介绍一下什么是分布式?为什么要分布式?** + +### 1.4 什么是分布式? + +分布式或者说 SOA 分布式重要的就是面向服务,说简单的分布式就是我们把整个系统拆分成不同的服务然后将这些服务放在不同的服务器上减轻单体服务的压力提高并发量和性能。比如电商系统可以简单地拆分成订单系统、商品系统、登录系统等等,拆分之后的每个服务可以部署在不同的机器上,如果某一个服务的访问量比较大的话也可以将这个服务同时部署在多台机器上。 + +### 1.5 为什么要分布式? + +从开发角度来讲单体应用的代码都集中在一起,而分布式系统的代码根据业务被拆分。所以,每个团队可以负责一个服务的开发,这样提升了开发效率。另外,代码根据业务拆分之后更加便于维护和扩展。 + +另外,我觉得将系统拆分成分布式之后不光便于系统扩展和维护,更能提高整个系统的性能。你想一想嘛?把整个系统拆分成不同的服务/系统,然后每个服务/系统 单独部署在一台服务器上,是不是很大程度上提高了系统性能呢? + +## 二 Dubbo 的架构 + +### 2.1 Dubbo 的架构图解 + +![Dubbo 架构](Dubbo.assets/46816446.jpg) + +**上述节点简单说明:** + +- **Provider:** 暴露服务的服务提供方 +- **Consumer:** 调用远程服务的服务消费方 +- **Registry:** 服务注册与发现的注册中心 +- **Monitor:** 统计服务的调用次数和调用时间的监控中心 +- **Container:** 服务运行容器 + +**调用关系说明:** + +1. 服务容器负责启动,加载,运行服务提供者。 +2. 服务提供者在启动时,向注册中心注册自己提供的服务。 +3. 服务消费者在启动时,向注册中心订阅自己所需的服务。 +4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。 +5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。 +6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。 + +**重要知识点总结:** + +- **注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小** +- **监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示** +- **注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外** +- **注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者** +- **注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表** +- **注册中心和监控中心都是可选的,服务消费者可以直连服务提供者** +- **服务提供者无状态,任意一台宕掉后,不影响使用** +- **服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复** + + + + + +### 2.2 Dubbo 工作原理 + +![Dubbo 工作原理](Dubbo.assets/64702923.jpg) + +图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。 + +**各层说明**: + +- 第一层:**service层**,接口层,给服务提供者和消费者来实现的 +- 第二层:**config层**,配置层,主要是对dubbo进行各种配置的 +- 第三层:**proxy层**,服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton +- 第四层:**registry层**,服务注册层,负责服务的注册与发现 +- 第五层:**cluster层**,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务 +- 第六层:**monitor层**,监控层,对rpc接口的调用次数和调用时间进行监控 +- 第七层:**protocol层**,远程调用层,封装rpc调用 +- 第八层:**exchange层**,信息交换层,封装请求响应模式,同步转异步 +- 第九层:**transport层**,网络传输层,抽象mina和netty为统一接口 +- 第十层:**serialize层**,数据序列化层,网络传输需要 + +## 三 Dubbo 的负载均衡策略 + +### 3.1 先来解释一下什么是负载均衡 + +**先来个官方的解释。** + +> 维基百科对负载均衡的定义:负载均衡改善了跨多个计算资源(例如计算机,计算机集群,网络链接,中央处理单元或磁盘驱动的的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间,并避免任何单个资源的过载。使用具有负载平衡而不是单个组件的多个组件可以通过冗余提高可靠性和可用性。负载平衡通常涉及专用软件或硬件。 + +**上面讲的大家可能不太好理解,再用通俗的话给大家说一下。** + +比如我们的系统中的某个服务的访问量特别大,我们将这个服务部署在了多台服务器上,当客户端发起请求的时候,多台服务器都可以处理这个请求。那么,如何正确选择处理该请求的服务器就很关键。假如,你就要一台服务器来处理该服务的请求,那该服务部署在多台服务器的意义就不复存在了。负载均衡就是为了避免单个服务器响应同一请求,容易造成服务器宕机、崩溃等问题,我们从负载均衡的这四个字就能明显感受到它的意义。 + +### 3.2 再来看看 Dubbo 提供的负载均衡策略 + +在集群负载均衡时,Dubbo 提供了多种均衡策略,默认为 `random` 随机调用。可以自行扩展负载均衡策略,参见:[负载均衡扩展](https://dubbo.gitbooks.io/dubbo-dev-book/content/impls/load-balance.html)。 + +备注:下面的图片来自于:尚硅谷2018Dubbo 视频。 + +#### 3.2.1 Random LoadBalance(默认,基于权重的随机负载均衡机制) + +- **随机,按权重设置随机概率。** +- 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。 + +![基于权重的随机负载均衡机制](Dubbo.assets/77722327.jpg) + + + +#### 3.2.2 RoundRobin LoadBalance(不推荐,基于权重的轮询负载均衡机制) + +- 轮循,按公约后的权重设置轮循比率。 +- 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。 + +![基于权重的轮询负载均衡机制](Dubbo.assets/97933247.jpg) + +#### 3.2.3 LeastActive LoadBalance + +- 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。 +- 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。 + +#### 3.2.4 ConsistentHash LoadBalance + +- **一致性 Hash,相同参数的请求总是发到同一提供者。(如果你需要的不是随机负载均衡,是要一类请求都到一个节点,那就走这个一致性hash策略。)** +- 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。 +- 算法参见:http://en.wikipedia.org/wiki/Consistent_hashing +- 缺省只对第一个参数 Hash,如果要修改,请配置 `` +- 缺省用 160 份虚拟节点,如果要修改,请配置 `` + +### 3.3 配置方式 + +**xml 配置方式** + +服务端服务级别 + +```java + +``` + +客户端服务级别 + +```java + +``` + +服务端方法级别 + +```java + + + +``` + +客户端方法级别 + +```java + + + +``` + +**注解配置方式:** + +消费方基于基于注解的服务级别配置方式: + +```java +@Reference(loadbalance = "roundrobin") +HelloService helloService; +``` + +## 四 zookeeper宕机与dubbo直连的情况 + +zookeeper宕机与dubbo直连的情况在面试中可能会被经常问到,所以要引起重视。 + +在实际生产中,假如zookeeper注册中心宕掉,一段时间内服务消费方还是能够调用提供方的服务的,实际上它使用的本地缓存进行通讯,这只是dubbo健壮性的一种体现。 + +**dubbo的健壮性表现:** + +1. 监控中心宕掉不影响使用,只是丢失部分采样数据 +2. 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务 +3. 注册中心对等集群,任意一台宕掉后,将自动切换到另一台 +4. 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯 +5. 服务提供者无状态,任意一台宕掉后,不影响使用 +6. 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复 + +我们前面提到过:注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。所以,我们可以完全可以绕过注册中心——采用 **dubbo 直连** ,即在服务消费方配置服务提供方的位置信息。 + +**xml配置方式:** + +```xml + +``` + +**注解方式:** + +```java + @Reference(url = "127.0.0.1:20880") + HelloService helloService; +``` + + + + + +# CS-Dubbo + +## 单体架构 + +![](Microservice.assets/be01386bf19f71fe8467bf20132bcc0c.png) + +![](Dubbo.assets/d77c37dd3a3a108509e903211a14ff94-1570887088291.png) + +## 优缺点 + +![](Dubbo.assets/391bfa438a9991af7f55b236c2a73e12.png) + +修改后 测试麻烦 + +迭代困难 + +修改工具类,其他的模块都受到影响 + +某个模块扩展扩容起来麻烦 + +部署和回滚不方便 + +## 微服务架构引入 + +## 概念 + +微服务是指开发一个单个小型的但有业务功能的服务,每个服务都有自己的处理和轻量通讯机制,可以部署在单个或多个服务器上。微服务也指一种种松耦合的、有一定的有界上下文的面向服务架构。也就是说,如果每个服务都要同时修改,那么它们就不是微服务,因为它们紧耦合在一起;如果你需要掌握一个服务太多的上下文场景使用条件,那么它就是一个有上下文边界的服务。 + +## 架构图 + +![](Dubbo.assets/a5ed18885a7240334ad1b8cf16d6f785.png) + +![](Dubbo.assets/4dc25fa09547d7d41812a63e195cfd2b.png) + +## 微服务架构设计原则 + +拆分足够小 + +服务之间轻量级通信 + +## 微服务的优点 + +相对于单体架构,它的主要特点是**组件化、松耦合、自治、去中心化**,体现在以下几个方面: + +一组小的服务 +服务粒度要小,而每个服务是针对一个单一职责的业务能力的封装,专注做好一件事情。 + +独立部署运行和扩展 + +每个服务能够独立被部署并运行在一个进程内。这种运行和部署方式能够赋予系统灵活的代码组织方式和发布节奏,使得快速交付和应对变化成为可能。 + +独立开发和演化 + +技术选型灵活,不受遗留系统技术约束。合适的业务问题选择合适的技术可以独立演化。服务与服务之间采取与语言无关的API进行集成。相对单体架构,微服务架构是更面向业务创新的一种架构模式。 + +独立团队和自治 + +团队对服务的整个生命周期负责,工作在独立的上下文中,自己决策自己治理,而不需要统一的指挥中心。团队和团队之间通过松散的社区部落进行衔接。 + +## 微服务的缺点 + +服务拆分微服务架构可能带来过多的操作。 + +分布式系统可能复杂难以管理。 + +因为分布部署跟踪问题难。 + +分布式事务比较难处理 + +当服务数量增加,管理复杂性增加。 + +等 + +## 微服务拆分思路 + +### 横向拆分 + +根据业务来拆分 + +![](Dubbo.assets/22e8dba61d1030921e6aed34169e2cdd.png) + +### 纵向拆分 + +根据层次来拆分 + +![](Dubbo.assets/fb0154c9a3d57c61127dae4a3413f9db.png) + +## 微服务的选择 + +下面两个就想当于自配电脑(Dubbo),和品牌电脑的关系(Springcloud)! + +### Dubbo (RPC) + +#### Dubbo(这个是传输层协议 速度快) + +Dubbo是阿里集团开源的一个极为出名的RPC框架,在很多互联网公司和企业应用中广泛使用。协议和序列化框架都可以插拔是及其鲜明的特色。同样的远程接口是基于Java Interface,并且依托于spring框架方便开发。可以方便的打包成单一文件,独立进程运行,和现在的微服务概念一致。所以目前Dubbo是一种广泛使用的微服务架构框架。 + +#### RPC + +RPC= Remote Process Call 跨进程调用 + +RPC的本质是提供了一种轻量无感知的跨进程通信的方式,在分布式机器上调用其他方法与本地调用无异(远程调用的过程是透明的,你并不知道这个调用的方法是部署在哪里,通过PRC能够解耦服务)。RPC是根据语言的API来定义的,而不是基于网络的应用来定义的,调用更方便,协议私密更安全、内容更小效率更高。 + +![](Dubbo.assets/ac8ff59370709f3755eec59630f93b2e.png) + +客户端(Client),服务的调用方。 + +服务端(Server),真正的服务提供者。 + +客户端存根,存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。 + +服务端存根,接收客户端发送过来的消息,将消息解包,并调用本地的方法。 + +基于TCP/IP协议。速度快。 + +![](Dubbo.assets/625f185c66747f68f35690b1f7757cc0.png) + +### Springcloud (HTTP) + +#### Springcloud (HTTP/REST)(这个是应用层协议 速度慢 但是全 想当于品牌电脑) + +Spring Cloud来源于Spring,利用Spring Boot进行快捷开发。 Spring +Cloud基本上都是使用了现有的开源框架进行的集成,学习的难度和部署的门槛就比较低,对于中小型企业来说,更易于使用和落地。Spring +Cloud +核心组件Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service +Registry和Service Discovery实现。也是Spring Cloud体系中最重要最核心的组件之一。 + +#### http + +**应用层协议**,简单。 + +http接口是在接口不多、系统与系统交互较少的情况下,解决信息孤岛初期常使用的一种通信手段;优点就是简单、直接、开发方便。利用现成的http协议 +进行传输。 + +使用Http协议的微服务,通常返回json数据,然后把json转换为对象。 + +### 小结 + +RPC服务和HTTP服务还是存在很多的不同点的,一般来说,RPC服务主要是针对大型企业的,而HTTP服务主要是针对小企业的,因为RPC效率更高,而HTTP服务开发迭代会更快。**总之,选用什么样的框架不是按照市场上流行什么而决定的,而是要对整个项目进行完整地评估。** + +## 微服务的基本概念 + +### 服务提供者Provider + +提供服务的具体实现。 + +### 服务调用者Consumer + +通过一些框架来调用服务提供者提供的服务 + +注意:同一个微服务,既可以是provider,也可以是consumer。 + +## 拓展 阿里架构演变之路 + +https://segmentfault.com/a/1190000018626163 这是作者原来文章的链接! + +下面是从源出处复制过来的! + +### 1. 概述 + +本文以淘宝作为例子,介绍从一百个并发到千万级并发情况下服务端的架构的演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知,文章最后汇总了一些架构设计的原则。 + +### 2. 基本概念 + +在介绍架构之前,为了避免部分读者对架构设计中的一些概念不了解,下面对几个最基础的概念进行介绍: + +- **分布式** + 系统中的多个模块在不同服务器上部署,即可称为分布式系统,如Tomcat和数据库分别部署在不同的服务器上,或两个相同功能的Tomcat分别部署在不同服务器上 +- **高可用** + 系统中部分节点失效时,其他节点能够接替它继续提供服务,则可认为系统具有高可用性 +- **集群** + 一个特定领域的软件部署在多台服务器上并作为一个整体提供一类服务,这个整体称为集群。如Zookeeper中的Master和Slave分别部署在多台服务器上,共同组成一个整体提供集中配置服务。在常见的集群中,客户端往往能够连接任意一个节点获得服务,并且当集群中一个节点掉线时,其他节点往往能够自动的接替它继续提供服务,这时候说明集群具有高可用性 +- **负载均衡** + 请求发送到系统时,通过某些方式把请求均匀分发到多个节点上,使系统中每个节点能够均匀的处理请求负载,则可认为系统是负载均衡的 +- **正向代理和反向代理** + 系统内部要访问外部网络时,统一通过一个代理服务器把请求转发出去,在外部网络看来就是代理服务器发起的访问,此时代理服务器实现的是正向代理;当外部请求进入系统时,代理服务器把该请求转发到系统中的某台服务器上,对外部请求来说,与之交互的只有代理服务器,此时代理服务器实现的是反向代理。简单来说,正向代理是代理服务器代替系统内部来访问外部网络的过程,反向代理是外部请求访问系统时通过代理服务器转发到内部服务器的过程。 + +### 3. 架构演进 + +#### 3.1 单机架构 + +![clipboard.png](Dubbo.assets/2664959638-5ca02e1d2e99b_articlex.png) + +以淘宝作为例子。在网站最初时,应用数量与用户数都较少,可以把Tomcat和数据库部署在同一台服务器上。浏览器往www.taobao.com发起请求时,首先经过DNS服务器(域名系统)把域名转换为实际IP地址10.102.4.1,浏览器转而访问该IP对应的Tomcat。 + +> **随着用户数的增长,Tomcat和数据库之间竞争资源,单机性能不足以支撑业务** + +#### 3.2 第一次演进:Tomcat与数据库分开部署 + +![clipboard.png](Dubbo.assets/2571350918-5ca02dfbdc242_articlex.png) + +Tomcat和数据库分别独占服务器资源,显著提高两者各自性能。 + +> **随着用户数的增长,并发读写数据库成为瓶颈** + +#### 3.3 第二次演进:引入本地缓存和分布式缓存 + +![clipboard.png](Dubbo.assets/1088865837-5ca031313f044_articlex.png) + +在Tomcat同服务器上或同JVM中增加本地缓存,并在外部增加分布式缓存,缓存热门商品信息或热门商品的html页面等。通过缓存能把绝大多数请求在读写数据库前拦截掉,大大降低数据库压力。其中涉及的技术包括:使用memcached作为本地缓存,使用Redis作为分布式缓存,还会涉及缓存一致性、缓存穿透/击穿、缓存雪崩、热点数据集中失效等问题。 + +> **缓存抗住了大部分的访问请求,随着用户数的增长,并发压力主要落在单机的Tomcat上,响应逐渐变慢** + +#### 3.4 第三次演进:引入反向代理实现负载均衡 + +![clipboard.png](Dubbo.assets/2872647211-5c95fef4928ad_articlex.png) + +在多台服务器上分别部署Tomcat,使用反向代理软件(Nginx)把请求均匀分发到每个Tomcat中。此处假设Tomcat最多支持100个并发,Nginx最多支持50000个并发,那么理论上Nginx把请求分发到500个Tomcat上,就能抗住50000个并发。其中涉及的技术包括:Nginx、HAProxy,两者都是工作在网络第七层的反向代理软件,主要支持http协议,还会涉及session共享、文件上传下载的问题。 + +> **反向代理使应用服务器可支持的并发量大大增加,但并发量的增长也意味着更多请求穿透到数据库,单机的数据库最终成为瓶颈** + +#### 3.5 第四次演进:数据库读写分离 + +![clipboard.png](Dubbo.assets/1589885053-5c96032e3c356_articlex.png) + +把数据库划分为读库和写库,读库可以有多个,通过同步机制把写库的数据同步到读库,对于需要查询最新写入数据场景,可通过在缓存中多写一份,通过缓存获得最新数据。其中涉及的技术包括:Mycat,它是数据库中间件,可通过它来组织数据库的分离读写和分库分表,客户端通过它来访问下层数据库,还会涉及数据同步,数据一致性的问题。 + +> **业务逐渐变多,不同业务之间的访问量差距较大,不同业务直接竞争数据库,相互影响性能** + +#### 3.6 第五次演进:数据库按业务分库 + +![clipboard.png](Dubbo.assets/250737400-5c9653d44e54e_articlex.png) + +把不同业务的数据保存到不同的数据库中,使业务之间的资源竞争降低,对于访问量大的业务,可以部署更多的服务器来支撑。这样同时导致跨业务的表无法直接做关联分析,需要通过其他途径来解决,但这不是本文讨论的重点,有兴趣的可以自行搜索解决方案。 + +> **随着用户数的增长,单机的写库会逐渐会达到性能瓶颈** + +#### 3.7 第六次演进:把大表拆分为小表 + +![clipboard.png](Dubbo.assets/111902257-5c960f793734f_articlex.png) + +比如针对评论数据,可按照商品ID进行hash,路由到对应的表中存储;针对支付记录,可按照小时创建表,每个小时表继续拆分为小表,使用用户ID或记录编号来路由数据。只要实时操作的表数据量足够小,请求能够足够均匀的分发到多台服务器上的小表,那数据库就能通过水平扩展的方式来提高性能。其中前面提到的Mycat也支持在大表拆分为小表情况下的访问控制。 + +这种做法显著的增加了数据库运维的难度,对DBA的要求较高。数据库设计到这种结构时,已经可以称为分布式数据库,但是这只是一个逻辑的数据库整体,数据库里不同的组成部分是由不同的组件单独来实现的,如分库分表的管理和请求分发,由Mycat实现,SQL的解析由单机的数据库实现,读写分离可能由网关和消息队列来实现,查询结果的汇总可能由数据库接口层来实现等等,这种架构其实是MPP(大规模并行处理)架构的一类实现。 + +目前开源和商用都已经有不少MPP数据库,开源中比较流行的有Greenplum、TiDB、Postgresql XC、HAWQ等,商用的如南大通用的GBase、睿帆科技的雪球DB、华为的LibrA等等,不同的MPP数据库的侧重点也不一样,如TiDB更侧重于分布式OLTP场景,Greenplum更侧重于分布式OLAP场景,这些MPP数据库基本都提供了类似Postgresql、Oracle、MySQL那样的SQL标准支持能力,能把一个查询解析为分布式的执行计划分发到每台机器上并行执行,最终由数据库本身汇总数据进行返回,也提供了诸如权限管理、分库分表、事务、数据副本等能力,并且大多能够支持100个节点以上的集群,大大降低了数据库运维的成本,并且使数据库也能够实现水平扩展。 + +> **数据库和Tomcat都能够水平扩展,可支撑的并发大幅提高,随着用户数的增长,最终单机的Nginx会成为瓶颈** + +#### 3.8 第七次演进:使用LVS或F5来使多个Nginx负载均衡 + +![clipboard.png](Dubbo.assets/1157555056-5c965af7a8de0_articlex.png) + +由于瓶颈在Nginx,因此无法通过两层的Nginx来实现多个Nginx的负载均衡。图中的LVS和F5是工作在网络第四层的负载均衡解决方案,其中LVS是软件,运行在操作系统内核态,可对TCP请求或更高层级的网络协议进行转发,因此支持的协议更丰富,并且性能也远高于Nginx,可假设单机的LVS可支持几十万个并发的请求转发;F5是一种负载均衡硬件,与LVS提供的能力类似,性能比LVS更高,但价格昂贵。由于LVS是单机版的软件,若LVS所在服务器宕机则会导致整个后端系统都无法访问,因此需要有备用节点。可使用keepalived软件模拟出虚拟IP,然后把虚拟IP绑定到多台LVS服务器上,浏览器访问虚拟IP时,会被路由器重定向到真实的LVS服务器,当主LVS服务器宕机时,keepalived软件会自动更新路由器中的路由表,把虚拟IP重定向到另外一台正常的LVS服务器,从而达到LVS服务器高可用的效果。 + +此处需要注意的是,上图中从Nginx层到Tomcat层这样画并不代表全部Nginx都转发请求到全部的Tomcat,在实际使用时,可能会是几个Nginx下面接一部分的Tomcat,这些Nginx之间通过keepalived实现高可用,其他的Nginx接另外的Tomcat,这样可接入的Tomcat数量就能成倍的增加。 + +> **由于LVS也是单机的,随着并发数增长到几十万时,LVS服务器最终会达到瓶颈,此时用户数达到千万甚至上亿级别,用户分布在不同的地区,与服务器机房距离不同,导致了访问的延迟会明显不同** + +#### 3.9 第八次演进:通过DNS轮询实现机房间的负载均衡 + +![clipboard.png](Dubbo.assets/1896228394-5c9662ac87756_articlex.png) + +在DNS服务器中可配置一个域名对应多个IP地址,每个IP地址对应到不同的机房里的虚拟IP。当用户访问www.taobao.com时,DNS服务器会使用轮询策略或其他策略,来选择某个IP供用户访问。此方式能实现机房间的负载均衡,至此,系统可做到机房级别的水平扩展,千万级到亿级的并发量都可通过增加机房来解决,系统入口处的请求并发量不再是问题。 + +> **随着数据的丰富程度和业务的发展,检索、分析等需求越来越丰富,单单依靠数据库无法解决如此丰富的需求** + +#### 3.10 第九次演进:引入NoSQL数据库和搜索引擎等技术 + +![clipboard.png](Dubbo.assets/1190021994-5ca03c930e572_articlex.png) + +当数据库中的数据多到一定规模时,数据库就不适用于复杂的查询了,往往只能满足普通查询的场景。对于统计报表场景,在数据量大时不一定能跑出结果,而且在跑复杂查询时会导致其他查询变慢,对于全文检索、可变数据结构等场景,数据库天生不适用。因此需要针对特定的场景,引入合适的解决方案。如对于海量文件存储,可通过分布式文件系统HDFS解决,对于key value类型的数据,可通过HBase和Redis等方案解决,对于全文检索场景,可通过搜索引擎如ElasticSearch解决,对于多维分析场景,可通过Kylin或Druid等方案解决。 + +当然,引入更多组件同时会提高系统的复杂度,不同的组件保存的数据需要同步,需要考虑一致性的问题,需要有更多的运维手段来管理这些组件等。 + +> **引入更多组件解决了丰富的需求,业务维度能够极大扩充,随之而来的是一个应用中包含了太多的业务代码,业务的升级迭代变得困难** + +#### 3.11 第十次演进:大应用拆分为小应用 + +![clipboard.png](Dubbo.assets/1992263855-5ca04d46dd717_articlex.png) + +按照业务板块来划分应用代码,使单个应用的职责更清晰,相互之间可以做到独立升级迭代。这时候应用之间可能会涉及到一些公共配置,可以通过分布式配置中心Zookeeper来解决。 + +> **不同应用之间存在共用的模块,由应用单独管理会导致相同代码存在多份,导致公共功能升级时全部应用代码都要跟着升级** + +3.12 第十一次演进:复用的功能抽离成微服务 + +![clipboard.png](Dubbo.assets/651851067-5ca04fe08f7ee_articlex.png) + +如用户管理、订单、支付、鉴权等功能在多个应用中都存在,那么可以把这些功能的代码单独抽取出来形成一个单独的服务来管理,这样的服务就是所谓的微服务,应用和服务之间通过HTTP、TCP或RPC请求等多种方式来访问公共服务,每个单独的服务都可以由单独的团队来管理。此外,可以通过Dubbo、SpringCloud等框架实现服务治理、限流、熔断、降级等功能,提高服务的稳定性和可用性。 + +> **不同服务的接口访问方式不同,应用代码需要适配多种访问方式才能使用服务,此外,应用访问服务,服务之间也可能相互访问,调用链将会变得非常复杂,逻辑变得混乱** + +#### 3.13 第十二次演进:引入企业服务总线ESB屏蔽服务接口的访问差异 + +![clipboard.png](Dubbo.assets/1162448692-5ca052a998911_articlex.png) + +通过ESB统一进行访问协议转换,应用统一通过ESB来访问后端服务,服务与服务之间也通过ESB来相互调用,以此降低系统的耦合程度。这种单个应用拆分为多个应用,公共服务单独抽取出来来管理,并使用企业消息总线来解除服务之间耦合问题的架构,就是所谓的SOA(面向服务)架构,这种架构与微服务架构容易混淆,因为表现形式十分相似。个人理解,微服务架构更多是指把系统里的公共服务抽取出来单独运维管理的思想,而SOA架构则是指一种拆分服务并使服务接口访问变得统一的架构思想,SOA架构中包含了微服务的思想。 + +> **业务不断发展,应用和服务都会不断变多,应用和服务的部署变得复杂,同一台服务器上部署多个服务还要解决运行环境冲突的问题,此外,对于如大促这类需要动态扩缩容的场景,需要水平扩展服务的性能,就需要在新增的服务上准备运行环境,部署服务等,运维将变得十分困难** + +#### 3.14 第十三次演进:引入容器化技术实现运行环境隔离与动态服务管理 + +![clipboard.png](Dubbo.assets/2760745238-5ca055e4b20a9_articlex.png) + +目前最流行的容器化技术是Docker,最流行的容器管理服务是Kubernetes(K8S),应用/服务可以打包为Docker镜像,通过K8S来动态分发和部署镜像。Docker镜像可理解为一个能运行你的应用/服务的最小的操作系统,里面放着应用/服务的运行代码,运行环境根据实际的需要设置好。把整个“操作系统”打包为一个镜像后,就可以分发到需要部署相关服务的机器上,直接启动Docker镜像就可以把服务起起来,使服务的部署和运维变得简单。 + +在大促的之前,可以在现有的机器集群上划分出服务器来启动Docker镜像,增强服务的性能,大促过后就可以关闭镜像,对机器上的其他服务不造成影响(在3.14节之前,服务运行在新增机器上需要修改系统配置来适配服务,这会导致机器上其他服务需要的运行环境被破坏)。 + +> **使用容器化技术后服务动态扩缩容问题得以解决,但是机器还是需要公司自身来管理,在非大促的时候,还是需要闲置着大量的机器资源来应对大促,机器自身成本和运维成本都极高,资源利用率低** + +#### 3.15 第十四次演进:以云平台承载系统 + +![clipboard.png](Dubbo.assets/1409345676-5ca05cae06402_articlex.png) + +系统可部署到公有云上,利用公有云的海量机器资源,解决动态硬件资源的问题,在大促的时间段里,在云平台中临时申请更多的资源,结合Docker和K8S来快速部署服务,在大促结束后释放资源,真正做到按需付费,资源利用率大大提高,同时大大降低了运维成本。 + +所谓的云平台,就是把海量机器资源,通过统一的资源管理,抽象为一个资源整体,在之上可按需动态申请硬件资源(如CPU、内存、网络等),并且之上提供通用的操作系统,提供常用的技术组件(如Hadoop技术栈,MPP数据库等)供用户使用,甚至提供开发好的应用,用户不需要关系应用内部使用了什么技术,就能够解决需求(如音视频转码服务、邮件服务、个人博客等)。在云平台中会涉及如下几个概念: + +- **IaaS:**基础设施即服务。对应于上面所说的机器资源统一为资源整体,可动态申请硬件资源的层面; +- **PaaS:**平台即服务。对应于上面所说的提供常用的技术组件方便系统的开发和维护; +- **SaaS:**软件即服务。对应于上面所说的提供开发好的应用或服务,按功能或性能要求付费。 + +> **至此,以上所提到的从高并发访问问题,到服务的架构和系统实施的层面都有了各自的解决方案,但同时也应该意识到,在上面的介绍中,其实是有意忽略了诸如跨机房数据同步、分布式事务实现等等的实际问题,这些问题以后有机会再拿出来单独讨论** + +### 4. 架构设计总结 + +- **架构的调整是否必须按照上述演变路径进行?** + 不是的,以上所说的架构演变顺序只是针对某个侧面进行单独的改进,在实际场景中,可能同一时间会有几个问题需要解决,或者可能先达到瓶颈的是另外的方面,这时候就应该按照实际问题实际解决。如在政府类的并发量可能不大,但业务可能很丰富的场景,高并发就不是重点解决的问题,此时优先需要的可能会是丰富需求的解决方案。 +- **对于将要实施的系统,架构应该设计到什么程度?** + 对于单次实施并且性能指标明确的系统,架构设计到能够支持系统的性能指标要求就足够了,但要留有扩展架构的接口以便不备之需。对于不断发展的系统,如电商平台,应设计到能满足下一阶段用户量和性能指标要求的程度,并根据业务的增长不断的迭代升级架构,以支持更高的并发和更丰富的业务。 +- **服务端架构和大数据架构有什么区别?** + 所谓的“大数据”其实是海量数据采集清洗转换、数据存储、数据分析、数据服务等场景解决方案的一个统称,在每一个场景都包含了多种可选的技术,如数据采集有Flume、Sqoop、Kettle等,数据存储有分布式文件系统HDFS、FastDFS,NoSQL数据库HBase、MongoDB等,数据分析有Spark技术栈、机器学习算法等。总的来说大数据架构就是根据业务的需求,整合各种大数据组件组合而成的架构,一般会提供分布式存储、分布式计算、多维分析、数据仓库、机器学习算法等能力。而服务端架构更多指的是应用组织层面的架构,底层能力往往是由大数据架构来提供。 +- **有没有一些架构设计的原则?** + - N+1设计。系统中的每个组件都应做到没有单点故障; + - 回滚设计。确保系统可以向前兼容,在系统升级时应能有办法回滚版本; + - 禁用设计。应该提供控制具体功能是否可用的配置,在系统出现故障时能够快速下线功能; + - 监控设计。在设计阶段就要考虑监控的手段; + - 多活数据中心设计。若系统需要极高的高可用,应考虑在多地实施数据中心进行多活,至少在一个机房断电的情况下系统依然可用; + - 采用成熟的技术。刚开发的或开源的技术往往存在很多隐藏的bug,出了问题没有商业支持可能会是一个灾难; + - 资源隔离设计。应避免单一业务占用全部资源; + - 架构应能水平扩展。系统只有做到能水平扩展,才能有效避免瓶颈问题; + - 非核心则购买。非核心功能若需要占用大量的研发资源才能解决,则考虑购买成熟的产品; + - 使用商用硬件。商用硬件能有效降低硬件故障的机率; + - 快速迭代。系统应该快速开发小功能模块,尽快上线进行验证,早日发现问题大大降低系统交付的风险; + - 无状态设计。服务接口应该做成无状态的,当前接口的访问不依赖于接口上次访问的状态。 + +## Dubbo引入 + +## Dubbo介绍 + + + +## Spring和Dubbo的结合 + +### 入门案例 + +参考文档: + +#### 导包 + +见资料。 + +#### 服务提供者 + +##### 代码 + +首先我们在服务端定义一个接口 + +![](Dubbo.assets/1ddc209e6ef6a524024997bc53ca3cf1.png) + +然后我在服务端提供这个接口的实现 + +![](Dubbo.assets/6a7c6b3b2755feb69455529e8497395b.png) + +##### 配置 + +![](Dubbo.assets/fa1e3e4cc8f504c746e87a1e01ce7286.png) + +#### 服务器使用者 + +##### 代码 + +![](Dubbo.assets/4f4062b2cf24f0b5389881f84d0ad11e.png) + +远程调用代码 + +![](Dubbo.assets/2ce43942af3f1ff23b44b75314bfd3af.png) + +##### 配置 + +![](Dubbo.assets/32a3c0f74c325ec723440dcc369cd82c.png) + +#### 测试结果 + +![](Dubbo.assets/26954a843fb9f74e9cde7c16c19902e4.png) + + + +**这里需要注意的是,这里服务的提供者和服务的消费者名字要一样,不光是接口和类的名字一样,而且包名也要一样!** + +![1570800056119](Dubbo.assets/1570800056119.png) + +这里的两个接口必须要一样! + +## 架构 + +![1570802882355](Dubbo.assets/1570802882355.png) + +### 第一种方式直连!相当于没有上面注册! + +### 第二种方式用注册中心中的zookepper! + +zookepper使用,首先减压! + +然后进去目录中,将文件名字修改: + +![1570803035936](Dubbo.assets/1570803035936.png) + +因为启动以后会默认找zoo.cfg这个文件! + + + +然后需要启动这的zookeeper,这里需要注意的是,路径不能有中文,目录如果放的太深,或者路径中有其他奇怪字符,这个是运行不了的,会一直报错,提示找不到主类! + +![1570804540341](Dubbo.assets/1570804540341.png) + +![1570804910045](Dubbo.assets/1570804910045.png) + +这样就启动成功啦! + +这其中遇到一个问题,问题详细信息看error18! + +zookeeper会受电脑广播和其他东西的影响! + +而且在wins环境下,会运行不稳定!有时候需要耐心一定! + +![1570870852064](Dubbo.assets/1570870852064.png) + +出现这个,就意味着成功啦! + + + +### 入门案例分析 + +## SpringBoot 和 Dubbo的结合 + +参考文档 + + + +首先需要导一个包!springboot对dubbo的支持包!上面是github的地址! + +### 导包 + +```xml + + com.alibaba.spring.boot + dubbo-spring-boot-starter + 2.0.0 + +``` + +Springboot约定大于配置,官方文档中没有配置端口,其实是默认配置了端口,只要不修改端口,默认就是 + +```xml +spring.application.name=dubbo-spring-boot-starter +spring.dubbo.server=true +spring.dubbo.registry=N/A +``` + + + + + +### 提供者 + +![](Dubbo.assets/f0e79697c6c1d5415d3c878f84d3b34c.png) + +**注意:这里的Service是dubbo中的!要注意!** + +使用之前要导包! + +![](Dubbo.assets/04c8cb6e6104504c76086b191c682e19.png) + +配置: + +```java +spring.application.name=dubbo-spring-boot-starter +spring.dubbo.protocol.name=dubbo +spring.dubbo.protocol.port=20880 +spring.dubbo.server=true +spring.dubbo.registry=N/A +``` + + + +### 使用者 + +也要实现一模一样的接口! + +![](Dubbo.assets/ffd1647ad2a27e2a3cbd4bd9fd668ce0.png) + +![](Dubbo.assets/1e3adc3901c569b59d50e303575ef8a3.png) + +然后在main方法里测试! + +**注意**:这里要开启功能,需要上面的注解 + +```java +@EnableDubboConfiguraion +``` + +![](Dubbo.assets/8d9f3596a9273968defb166629b682bb.png) + +### 测试 + +先启动服务提供者: + +![](Dubbo.assets/7131d4fb96a772e2ce12dc39c84e0718.png) + +在启动使用者: + +这行log出现在下面的空行位置 + +![](Dubbo.assets/167a3119d6eb87a271d3708cb48dcc61.png) + +![](Dubbo.assets/c7e6d9f38b4ef5eb42001a8ef20f3560.png) + + + +在老师上课的dome中,老师使用了一个公共包common,公共包里面的pom.xml配置,要写成 + +```xml +jar +``` + +公共包里面建放了DemoService接口,那么下面两个中就没有必要放这个接口啦! + +但是要导入上面的common包! + +```xml + + + com.cskaoyan + common + 0.0.1-SNAPSHOT + +``` + +这样,下面两个包就可以使用common里面的包啦! + +这种导入一个公共包的做法,不容易犯错! + + + +公共类中导入一个依赖,然后下面两个包中都是可以使用的! + +![1570882252588](Dubbo.assets/1570882252588.png) + +```xml + + + com.alibaba.spring.boot + dubbo-spring-boot-starter + 2.0.0 + +``` + +这里可以看到下面两个包中已经包含了上面包中的,已经包含了公共包中的依赖! + +上面的是直连 + +如果想让项目变成需要注册的,还需要导入zookeeper + +```xml + + + com.101tec + zkclient + 0.10 + +``` + + + + + +## 直连提供者 + +### 介绍 + +我们刚才使用的微服务的用法,实际上是一种最简单的点对点调用方式。这种方式通常用于测试环境。 + +在开发及测试环境下,经常需要绕过注册中心,只测试指定服务提供者,这时候可能需要点对点直连,点对点直联方式,将以服务接口为单位,忽略注册中心的提供者列表,A +接口配置点对点,不影响 B 接口从注册中心获取列表。 + +![](Dubbo.assets/15adcb224fcef4191f9106fd36f62105.png) + +**注意** 为了避免复杂化线上环境,不要在线上使用这个功能,只应在测试阶段使用。 + +### 弊端 + +不利于分布式的扩展 + +## 服务注册和服务发现 + +### 架构图 + +这里是长连接!长连接最主要的特点是建立心跳连接!过一会会发射一些数据! + +![](Dubbo.assets/acc8b5168c63f753c642f845b11de339.png) + +名词解释: + +![](Dubbo.assets/116873b8f0e64756d54775a4b40c1d82.png) + +### ZooKeeper + +是一个中间件,负责为分布式系统提供协调服务。服务注册和服务发现。 + + + +下载路径 + +下载解压之后 + +修改一个文件名 + +zoo_sample.cfg修改为 + +![](Dubbo.assets/ec83dfc5f99bf5219216aa4f80d1e1c4.png) + +启动: + +![](Dubbo.assets/730080cbd8e58d9bde067d8b0903a607.png) + +![](Dubbo.assets/5bb55b0b35a442e2882accb51ad945dd.png) + +## Spring +dubbo项目 如何整合zookeeper + +### 导入依赖 + +\ +\com.101tec\ +\zkclient\ +\0.9\ +\ +\ +\org.apache.zookeeper\ +\zookeeper\ +\3.4.9\ +\pom\ +\ + +### 修改服务端 + +![](Dubbo.assets/5af9e605d5751eec4aa074727bdb3d79.png) + +启动: + +没有报错, + +Zookeeper那边的命令行会出现一些log + +![](Dubbo.assets/8641ddc536193a03a16a54f50d9e2405.png) + +### 修改客户端 + +![](Dubbo.assets/a2e28d69a76f38bb32409e87c3202c2f.png) + +### 测试 + +![](Dubbo.assets/0cb2e6a8664c7ac126ba073055ea1d98.png) + +![](Dubbo.assets/4af8d78e7df99c245100ea374eb7b2e3.png) + +## Springboot 和 dubbo项目整合zookeeper + +### 服务端 + +增加了依赖 + +![](Dubbo.assets/bb743b6f6d742f4917a9d8d8c19cdc70.png) + +修改地址: + +spring.application.name=dubbo-spring-boot-starter +spring.dubbo.server=true +spring.dubbo.registry=zookeeper://localhost:2181 + +启动 + +### 使用端 + +\ +\com.101tec\ +\zkclient\ +\0.10\ +\ + +配置文件: + +增加如下: + +spring.dubbo.registry=zookeeper://localhost:2181 + +代码里: + +![](Dubbo.assets/125b23e796ba24d0e683d687662d5e94.png) + +### 测试 + +![](Dubbo.assets/11c12babf9fd43b68454fbb9403ec29e.png) + +这里有一个面试题: + +加入zookeeper挂掉以后,Dubbo还能不能正常调用?? + +可正常调用!因为本地有缓存! + +![1570885788370](Dubbo.assets/1570885788370.png) + + + + + + + + + +## Dubbo负载均衡 + +## 概念 + +LoadBalance +中文意思为负载均衡,它的职责是将网络请求,或者其他形式的负载“均摊”到不同的机器上。避免集群中部分服务器压力过大,而另一些服务器比较空闲的情况。通过负载均衡,可以让每台服务器获取到适合自己处理能力的负载。在为高负载服务器分流的同时,还可以避免资源浪费,一举两得。 + +## 负载均衡策略 + +![](Dubbo.assets/dd06bd3a7e584111a4c8a408e78ddba4.png) + +### Random + +随机加权 + +RandomLoadBalance +是加权随机算法的具体实现,它的算法思想很简单。假设我们有一组服务器 servers = [A, +B, C],他们对应的权重为 weights = [5, 3, +2],权重总和为10。现在把这些权重值平铺在一维坐标值上,[0, 5) 区间属于服务器 +A,[5, 8) 区间属于服务器 B,[8, 10) 区间属于服务器 +C。接下来通过随机数生成器生成一个范围在 [0, 10) +之间的随机数,然后计算这个随机数会落到哪个区间上。权重越大的机器,在坐标轴上对应的区间范围就越大,因此随机数生成器生成的数字就会有更大的概率落到此区间内。 + +### RoundRobin + +我们有三台服务器 A、B、C。我们将第一个请求分配给服务器 A,第二个请求分配给服务器 +B,第三个请求分配给服务器 C,第四个请求再次分配给服务器 A。这个过程就叫做 + +轮询。轮询是一种无状态负载均衡算法,实现简单,适用于每台服务器性能相近的场景下。 + +通常我们可以给轮询的机器设置不同的权重,经过加权后,每台服务器能够得到的请求数比例,接近或等于他们的权重比。比如服务器 +A、B、C 权重比为 5:2:1。那么在8次请求中,服务器 A 将收到其中的5次请求,服务器 B +会收到其中的2次请求,服务器 C 则收到其中的1次请求。 + +### LeastActive + +LeastActiveLoadBalance +翻译过来是最小活跃数负载均衡。活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求。此时应优先将请求分配给该服务提供者。在具体实现中,每个服务提供者对应一个活跃数 +active。初始情况下,所有服务提供者活跃数均为0。每收到一个请求,活跃数加1,完成请求后则将活跃数减1。在服务运行一段时间后,性能好的服务提供者处理请求的速度更快,因此活跃数下降的也越快,此时这样的服务提供者能够优先获取到新的服务请求、这就是最小活跃数负载均衡算法的基本思想。 + +### ConsistentHash + +![](Dubbo.assets/ea1e741ce818c1e439ab913c4c84e620.png) + +## 配置 + +### Random + +默认配置 + +### RoundRobin + +#### 客户端配置 + +客户端服务级别 + +![](Dubbo.assets/a4a494abc4050b987ef6ff9c37134c69.png) + +该服务的所有方法都使用roundrobin负载均衡。 + +客户端方法级别 + +![](Dubbo.assets/118ad755da8147dcf95f19aa62dd18ff.png) + +如 + +![](Dubbo.assets/127a68ffe191362a99e5221b6d4e0c97.png) + +只有该服务的hello方法使用roundrobin负载均衡。 + +#### 服务端配置 + +服务端服务级别 + +![](Dubbo.assets/57a0844a42642946790e94fe6b33e488.png) + +该服务的所有方法都使用roundrobin负载均衡。 + +服务端方法级别 + +![](Dubbo.assets/27e77f44be552ada54ffea71bdfd543e.png) + +只有该服务的该方法使用roundrobin负载均衡。 + +### LeastActive + +### 补充说明 + +和Dubbo其他的配置类似,多个配置是有覆盖关系的: + +1. 方法级优先,接口级次之,全局配置再次之。 +2. 如果级别一样,则消费方优先,提供方次之。 + +所以,上面4种配置的优先级是: + +1. 客户端方法级别配置。 +2. 客户端接口级别配置。 +3. 服务端方法级别配置。 +4. 服务端接口级别配置。 + +## 补充说明 + +### 启动检查 + +后面的check = false 就是启动检查! + +```java +@Component +public class ThirdService { + + //这句话相当于 url = "dubbo://localhost:20880" + @Reference(interfaceClass = DemoService.class,check = false) + DemoService demoService; + + public String sendMsg(String msg){ + return demoService.sendMsg(msg); + } +} +``` + +启动检查加了以后,我们不关心现在有没有provider,他会去检查! + +### Dubbo的特性 + +# CS-Guns + + + +## 介绍 + + + +- 快速构建应用系统(后台管理系统)。 +- 默认提供了诸多业务系统的基本功能 +- 继承了很多优秀的开源框架(开源) +- 可以直接作为一个后台管理系统的脚手架! + +## 基本概念 + +![](Dubbo.assets/8f6c780959c4b54d2f2ba4678e912817.png) + +## 基本使用 + +这里开始搭建后台开发的架子。 + +### 导入guns到IDEA + +![](Dubbo.assets/dab02c1d9a41b181d8710bc7ce9a6ae7.png) + +### 如何启动guns项目 + +#### 运行admin模块 + +![](Dubbo.assets/a178f9aaf032b2830966f554dcd3a43c.png) + +如何修改数据源? + +在Guns-admin模块里 ,有个 配置文件 application.yml + +里面默认的数据源配置如下: + +![](Dubbo.assets/07619108db87df25f185a18013636026.png) + +修改成我们自己的: + +![](Dubbo.assets/89ede97f1c1d1fcdf78d604efd72cb1d.png) + +**spring:** +**profiles:** local +**datasource:** +**url:** +jdbc:mysql://127.0.0.1:3306/guns?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC +**username:** root +**password:** 123456 +**db-name:** guns *\#用来搜集数据库的所有表* +**filters:** wall,mergeStat + +然后启动项目: + +登录 用户名 admin 密码 111111 可以成功登录! + +项目启动成功! + +### 初始化rest项目 (开发主要使用) + +新建guns-rest数据库和里面的表 + +修改guns-rest的配置文件 + +![](Dubbo.assets/4cb52dce8dcdca21462f7f9019c73659.png) + +报错,因为我们的数据库是mysql8,所以有一个配置字段不一样。 + +Caused by: com.baomidou.mybatisplus.exceptions.MybatisPlusException: Error: +GlobalConfigUtils setMetaData Fail ! Cause:java.sql.SQLException: The connection +property 'zeroDateTimeBehavior' acceptable values are: 'CONVERT_TO_NULL', +'EXCEPTION' or 'ROUND'. The value 'convertToNull' is not acceptable. + +把 + +zeroDateTimeBehavior=convertToNull + +修改为 + +&zeroDateTimeBehavior=CONVERT_TO_NULL + +去掉也可以额的 + +再次启动 + +缺少包: + +![](Dubbo.assets/4ae2855b71fd5fa7c8a6ce98e97e5066.png) + +导入一个包: + +\ +\log4j\ +\log4j\ +\1.2.17\ +\ + +访问下测试链接 + + + +返回 + +**{**"randomKey": "51656p","token": "eyJhbGciOiJIUzUxMiJ9.eyJyYW5kb21LZXkiOiI1MTY1NnAiLCJzdWIiOiJhZG1pbiIsImV4cCI6MTU1NTgyNzI2NCwiaWF0IjoxNTU1MjIyNDY0fQ.xfwl_6937X8T2etvQq6dQLuYb6ezCu4iJsAP0ggWQxn-TRuSJdoUQz0I02z4yNtVKtJbNakMwEolPp_XrbR46w"**}** + +说明rest项目启动 OK! + +## Rest项目的讲解 + +### Rest的使用场景 + +APP开发 后端 + +前后端分离项目 后端 + +### Rest新的模块的生成 + +### 工具的按装 + +Chrome浏览器的两个插件 + +RestLet :类似PostMan,模拟浏览器发请求,跟后端进行交互 + +FeHelper:优化返回的Json的显示效果 + +### 基本使用 + +我们在Rest模块里写一个Rest接口 + +![](Dubbo.assets/2a9bd7920f7813c110ef1a937fdaf30b.png) + +测试: + +![](Dubbo.assets/67ec53833e12374d957954c3881f6510.png) + +如何解决呢? + +临时的解决方案: + +修改配置文件,关闭鉴权 + +![](Dubbo.assets/b1c3e2a952896cdd1b44ae24315b7253.png) + +测试: + +![](Dubbo.assets/abe0094bdc581e819951d987450a3cc4.png) + +### Rest模块的代码生成 + +生成工具的路径 + +![](Dubbo.assets/1495afee990ef5edce341497764f38ae.png) + +步骤: + +#### 第一步,准备数据 + +新建数据库和表。 + +#### 个性化配置 + +修改生成代码的输出路径 + +![](Dubbo.assets/571e012ddd402ef93dae007ba9589fb9.png) + +修改作者 + +修改数据源配置: + +![](Dubbo.assets/2b761916433447e02e3da361c8dc94e6.png) + +修改生成策略 作用的表 + +![](Dubbo.assets/51fe4c42da71e48176d5ca19da87e283.png) + +修改生成代码的包名: + +![](Dubbo.assets/8a9851260dd0ecae0c618170f242ff13.png) + +最后,右键执行测试用例代码: + +![](Dubbo.assets/bd408a2b9d05020774f00010c52a9d9c.png) + +成功: + +![](Dubbo.assets/edadf78718f7c269dbcb13406ec29b03.png) + +生成之后,注意 + +![](Dubbo.assets/24c31648ebc1e8930711fd2d3574c171.png) + +这个如果没有放到指定的目录,而是放到modular目录里,项目启动的时候mapper无法自动生成实例,会报找不到实例的错误。 + +![](Dubbo.assets/a7db731855aaf8a42e32d6130bc9c215.png) + +这里mapper不用自己写实现 + +然后写代码测试一个 从controller 到service,到dao,出入一个Product + +如下:测试插入一个product: + +![](Dubbo.assets/0766564067b558c3cdac95551d3d953a.png) + +使用RestLet发请求 + +![](Dubbo.assets/dcc02d102b9eb1eaa70f932afaa399a8.png) + +代码正确的话,可以看到,product已经插入到数据库, + +同时返回响应报文: + +![](Dubbo.assets/865681c44f921c699fdc928a19eb4a9e.png) + +说明测试成功! + + + + + +# Dubbo面试题 + +**想往高处走,怎么能不懂 Dubbo?** + +Dubbo是国内最出名的分布式服务框架,也是 Java 程序员必备的必会的框架之一。Dubbo 更是中高级面试过程中经常会问的技术,无论你是否用过,你都必须熟悉。 + +**下面我为大家准备了一些 Dubbo 常见的的面试题,一些是我经常问别人的,一些是我过去面试遇到的一些问题,总结给大家,希望对大家能有所帮助。** + +------ + +## **1、Dubbo是什么?** + +Dubbo是阿里巴巴开源的基于 Java 的高性能 RPC 分布式服务框架,现已成为 Apache 基金会孵化项目。 + +面试官问你如果这个都不清楚,那下面的就没必要问了。 + +> 官网:http://dubbo.apache.org + +## **2、为什么要用Dubbo?** + +因为是阿里开源项目,国内很多互联网公司都在用,已经经过很多线上考验。内部使用了 Netty、Zookeeper,保证了高性能高可用性。 + +使用 Dubbo 可以将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,可用于提高业务复用灵活扩展,使前端应用能更快速的响应多变的市场需求。 + +下面这张图可以很清楚的诠释,最重要的一点是,分布式架构可以承受更大规模的并发流量。 + +![img](Dubbo.assets/640.webp) + +下面是 Dubbo 的服务治理图。 + +![img](Dubbo.assets/640.webp) + +## **3、Dubbo 和 Spring Cloud 有什么区别?** + +两个没关联,如果硬要说区别,有以下几点。 + +1)通信方式不同 + +Dubbo 使用的是 RPC 通信,而 Spring Cloud 使用的是 HTTP RESTFul 方式。 + +2)组成部分不同 + +![img](Dubbo.assets/640.webp) + +## **4、dubbo都支持什么协议,推荐用哪种?** + +- dubbo://(推荐) +- rmi:// +- hessian:// +- http:// +- webservice:// +- thrift:// +- memcached:// +- redis:// +- rest:// + +## **5、Dubbo需要 Web 容器吗?** + +不需要,如果硬要用 Web 容器,只会增加复杂性,也浪费资源。 + +## **6、Dubbo内置了哪几种服务容器?** + +- Spring Container +- Jetty Container +- Log4j Container + +Dubbo 的服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。 + +## **7、Dubbo里面有哪几种节点角色?** + +![img](Dubbo.assets/640-1588995722550.webp) + +**8、画一画服务注册与发现的流程图** + +![img](Dubbo.assets/640.png) + +该图来自 Dubbo 官网,供你参考,如果你说你熟悉 Dubbo, 面试官经常会让你画这个图,记好了。 + +## **9、Dubbo默认使用什么注册中心,还有别的选择吗?** + +推荐使用 Zookeeper 作为注册中心,还有 Redis、Multicast、Simple 注册中心,但不推荐。 + +## **10、Dubbo有哪几种配置方式?** + +1)Spring 配置方式 +2)Java API 配置方式 + +## **11、Dubbo 核心的配置有哪些?** + +我曾经面试就遇到过面试官让你写这些配置,我也是蒙逼。。 + +![img](Dubbo.assets/640.png) + +配置之间的关系见下图。 + +![img](data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==) + +## **12、在 Provider 上可以配置的 Consumer 端的属性有哪些?** + +1)timeout:方法调用超时 +2)retries:失败重试次数,默认重试 2 次 +3)loadbalance:负载均衡算法,默认随机 +4)actives 消费者端,最大并发调用限制 + +## **13、Dubbo启动时如果依赖的服务不可用会怎样?** + +Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,默认 check="true",可以通过 check="false" 关闭检查。 + +## **14、Dubbo推荐使用什么序列化框架,你知道的还有哪些?** + +推荐使用Hessian序列化,还有Duddo、FastJson、Java自带序列化。 + +## **15、Dubbo默认使用的是什么通信框架,还有别的选择吗?** + +Dubbo 默认使用 Netty 框架,也是推荐的选择,另外内容还集成有Mina、Grizzly。 + +## **16、Dubbo有哪几种集群容错方案,默认是哪种?** + +![img](Dubbo.assets/640-1588995722584.webp) + +## **17、Dubbo有哪几种负载均衡策略,默认是哪种?** + +![img](data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==) + +## **18、注册了多个同一样的服务,如果测试指定的某一个服务呢?** + +可以配置环境点对点直连,绕过注册中心,将以服务接口为单位,忽略注册中心的提供者列表。 + +## **19、Dubbo支持服务多协议吗?** + +Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。 + +## **20、当一个服务接口有多种实现时怎么做?** + +当一个接口有多种实现时,可以用 group 属性来分组,服务提供方和消费方都指定同一个 group 即可。 + +## **21、服务上线怎么兼容旧版本?** + +可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用。这个和服务分组的概念有一点类似。 + +## **22、Dubbo可以对结果进行缓存吗?** + +可以,Dubbo 提供了声明式缓存,用于加速热门数据的访问速度,以减少用户加缓存的工作量。 + +## **23、Dubbo服务之间的调用是阻塞的吗?** + +默认是同步等待结果阻塞的,支持异步调用。 + +Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。 + +异步调用流程图如下。 + +![img](Dubbo.assets/640-1588995722595.webp) + +## **24、Dubbo支持分布式事务吗?** + +目前暂时不支持,后续可能采用基于 JTA/XA 规范实现,如以图所示。 + +![img](Dubbo.assets/640-1588995722606.webp) + +## **25、Dubbo telnet 命令能做什么?** + +dubbo 通过 telnet 命令来进行服务治理,具体使用看这篇文章《[dubbo服务调试管理实用命令](https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247483709&idx=1&sn=afe0688c184f00902529583a85d90089&scene=21#wechat_redirect)》。 + +> telnet localhost 8090 + +## **26、Dubbo支持服务降级吗?** + +Dubbo 2.2.0 以上版本支持。 + +## **27、Dubbo如何优雅停机?** + +Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。 + +## **28、服务提供者能实现失效踢出是什么原理?** + +服务失效踢出基于 Zookeeper 的临时节点原理。 + +## **29、如何解决服务调用链过长的问题?** + +Dubbo 可以使用 Pinpoint 和 Apache Skywalking(Incubator) 实现分布式服务追踪,当然还有其他很多方案。 + +## **30、服务读写推荐的容错策略是怎样的?** + +读操作建议使用 Failover 失败自动切换,默认重试两次其他服务器。 + +写操作建议使用 Failfast 快速失败,发一次调用失败就立即报错。 + +## **31、Dubbo必须依赖的包有哪些?** + +Dubbo 必须依赖 JDK,其他为可选。 + +## **32、Dubbo的管理控制台能做什么?** + +管理控制台主要包含:路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等管理功能。 + +## **33、说说 Dubbo 服务暴露的过程。** + +Dubbo 会在 Spring 实例化完 bean 之后,在刷新容器最后一步发布 ContextRefreshEvent 事件的时候,通知实现了 ApplicationListener 的 ServiceBean 类进行回调 onApplicationEvent 事件方法,Dubbo 会在这个方法中调用 ServiceBean 父类 ServiceConfig 的 export 方法,而该方法真正实现了服务的(异步或者非异步)发布。 + +## **34、Dubbo 停止维护了吗?** + +2014 年开始停止维护过几年,17 年开始重新维护,并进入了 Apache 项目。 + +## **35、Dubbo 和 Dubbox 有什么区别?** + +Dubbox 是继 Dubbo 停止维护后,当当网基于 Dubbo 做的一个扩展项目,如加了服务可 Restful 调用,更新了开源组件等。 + +## **36、你还了解别的分布式框架吗?** + +别的还有 Spring cloud、Facebook 的 Thrift、Twitter 的 Finagle 等。 + +## **37、Dubbo 能集成 Spring Boot 吗?** + +可以的,项目地址如下。 + +> https://github.com/apache/incubator-dubbo-spring-boot-project + +## **38、在使用过程中都遇到了些什么问题?** + +Dubbo 的设计目的是为了满足高并发小数据量的 rpc 调用,在大数据量下的性能表现并不好,建议使用 rmi 或 http 协议。 + +## **39、你读过 Dubbo 的源码吗?** + +要了解 Dubbo 就必须看其源码,了解其原理,花点时间看下吧,网上也有很多教程,后续有时间我也会在公众号上分享 Dubbo 的源码。 + +## **40、你觉得用 Dubbo 好还是 Spring Cloud 好?** + diff --git a/YangLei/README.MD b/YangLei/README.MD deleted file mode 100644 index 60330d5..0000000 --- a/YangLei/README.MD +++ /dev/null @@ -1,2 +0,0 @@ -# 加油 - diff --git a/YangLei/SpringCloud.assets/1588990404007.png b/YangLei/SpringCloud.assets/1588990404007.png new file mode 100644 index 0000000..a5bcbc3 Binary files /dev/null and b/YangLei/SpringCloud.assets/1588990404007.png differ diff --git a/YangLei/SpringCloud.assets/1589015707302.png b/YangLei/SpringCloud.assets/1589015707302.png new file mode 100644 index 0000000..12da864 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589015707302.png differ diff --git a/YangLei/SpringCloud.assets/1589039327360.png b/YangLei/SpringCloud.assets/1589039327360.png new file mode 100644 index 0000000..2a1ce4c Binary files /dev/null and b/YangLei/SpringCloud.assets/1589039327360.png differ diff --git a/YangLei/SpringCloud.assets/1589093101330.png b/YangLei/SpringCloud.assets/1589093101330.png new file mode 100644 index 0000000..6b8b066 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589093101330.png differ diff --git a/YangLei/SpringCloud.assets/1589160173719.png b/YangLei/SpringCloud.assets/1589160173719.png new file mode 100644 index 0000000..96a4e0f Binary files /dev/null and b/YangLei/SpringCloud.assets/1589160173719.png differ diff --git a/YangLei/SpringCloud.assets/1589161833971.png b/YangLei/SpringCloud.assets/1589161833971.png new file mode 100644 index 0000000..15a4e99 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589161833971.png differ diff --git a/YangLei/SpringCloud.assets/1589163324413.png b/YangLei/SpringCloud.assets/1589163324413.png new file mode 100644 index 0000000..ccd3e81 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589163324413.png differ diff --git a/YangLei/SpringCloud.assets/1589163718372.png b/YangLei/SpringCloud.assets/1589163718372.png new file mode 100644 index 0000000..3e5cd2d Binary files /dev/null and b/YangLei/SpringCloud.assets/1589163718372.png differ diff --git a/YangLei/SpringCloud.assets/1589163743409.png b/YangLei/SpringCloud.assets/1589163743409.png new file mode 100644 index 0000000..41eee45 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589163743409.png differ diff --git a/YangLei/SpringCloud.assets/1589163765354.png b/YangLei/SpringCloud.assets/1589163765354.png new file mode 100644 index 0000000..f518395 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589163765354.png differ diff --git a/YangLei/SpringCloud.assets/1589163783913.png b/YangLei/SpringCloud.assets/1589163783913.png new file mode 100644 index 0000000..7fd1170 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589163783913.png differ diff --git a/YangLei/SpringCloud.assets/1589164534662.png b/YangLei/SpringCloud.assets/1589164534662.png new file mode 100644 index 0000000..30bff7c Binary files /dev/null and b/YangLei/SpringCloud.assets/1589164534662.png differ diff --git a/YangLei/SpringCloud.assets/1589182326509.png b/YangLei/SpringCloud.assets/1589182326509.png new file mode 100644 index 0000000..eabcbef Binary files /dev/null and b/YangLei/SpringCloud.assets/1589182326509.png differ diff --git a/YangLei/SpringCloud.assets/1589182358527.png b/YangLei/SpringCloud.assets/1589182358527.png new file mode 100644 index 0000000..984298e Binary files /dev/null and b/YangLei/SpringCloud.assets/1589182358527.png differ diff --git a/YangLei/SpringCloud.assets/1589183406090.png b/YangLei/SpringCloud.assets/1589183406090.png new file mode 100644 index 0000000..bd50b6c Binary files /dev/null and b/YangLei/SpringCloud.assets/1589183406090.png differ diff --git a/YangLei/SpringCloud.assets/1589183439630.png b/YangLei/SpringCloud.assets/1589183439630.png new file mode 100644 index 0000000..ed6eb69 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589183439630.png differ diff --git a/YangLei/SpringCloud.assets/1589183851900.png b/YangLei/SpringCloud.assets/1589183851900.png new file mode 100644 index 0000000..ffda733 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589183851900.png differ diff --git a/YangLei/SpringCloud.assets/1589247242169.png b/YangLei/SpringCloud.assets/1589247242169.png new file mode 100644 index 0000000..b9b9a3c Binary files /dev/null and b/YangLei/SpringCloud.assets/1589247242169.png differ diff --git a/YangLei/SpringCloud.assets/1589253232498.png b/YangLei/SpringCloud.assets/1589253232498.png new file mode 100644 index 0000000..fa8c69c Binary files /dev/null and b/YangLei/SpringCloud.assets/1589253232498.png differ diff --git a/YangLei/SpringCloud.assets/1589269251237.png b/YangLei/SpringCloud.assets/1589269251237.png new file mode 100644 index 0000000..ed0b540 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589269251237.png differ diff --git a/YangLei/SpringCloud.assets/1589269384192.png b/YangLei/SpringCloud.assets/1589269384192.png new file mode 100644 index 0000000..6a6e6c8 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589269384192.png differ diff --git a/YangLei/SpringCloud.assets/1589269768295.png b/YangLei/SpringCloud.assets/1589269768295.png new file mode 100644 index 0000000..054b25a Binary files /dev/null and b/YangLei/SpringCloud.assets/1589269768295.png differ diff --git a/YangLei/SpringCloud.assets/1589269788201.png b/YangLei/SpringCloud.assets/1589269788201.png new file mode 100644 index 0000000..a5e7562 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589269788201.png differ diff --git a/YangLei/SpringCloud.assets/1589269811604.png b/YangLei/SpringCloud.assets/1589269811604.png new file mode 100644 index 0000000..5e8584c Binary files /dev/null and b/YangLei/SpringCloud.assets/1589269811604.png differ diff --git a/YangLei/SpringCloud.assets/1589270117812.png b/YangLei/SpringCloud.assets/1589270117812.png new file mode 100644 index 0000000..cec6645 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270117812.png differ diff --git a/YangLei/SpringCloud.assets/1589270137902.png b/YangLei/SpringCloud.assets/1589270137902.png new file mode 100644 index 0000000..c0a1fdf Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270137902.png differ diff --git a/YangLei/SpringCloud.assets/1589270153928.png b/YangLei/SpringCloud.assets/1589270153928.png new file mode 100644 index 0000000..c12018d Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270153928.png differ diff --git a/YangLei/SpringCloud.assets/1589270169180.png b/YangLei/SpringCloud.assets/1589270169180.png new file mode 100644 index 0000000..c661227 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270169180.png differ diff --git a/YangLei/SpringCloud.assets/1589270185940.png b/YangLei/SpringCloud.assets/1589270185940.png new file mode 100644 index 0000000..b4226f8 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270185940.png differ diff --git a/YangLei/SpringCloud.assets/1589270204279.png b/YangLei/SpringCloud.assets/1589270204279.png new file mode 100644 index 0000000..9a32b59 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270204279.png differ diff --git a/YangLei/SpringCloud.assets/1589270218187.png b/YangLei/SpringCloud.assets/1589270218187.png new file mode 100644 index 0000000..cfe9cf3 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270218187.png differ diff --git a/YangLei/SpringCloud.assets/1589270231874.png b/YangLei/SpringCloud.assets/1589270231874.png new file mode 100644 index 0000000..a3a720b Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270231874.png differ diff --git a/YangLei/SpringCloud.assets/1589270263169.png b/YangLei/SpringCloud.assets/1589270263169.png new file mode 100644 index 0000000..00e5448 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270263169.png differ diff --git a/YangLei/SpringCloud.assets/1589270363344.png b/YangLei/SpringCloud.assets/1589270363344.png new file mode 100644 index 0000000..aaef816 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270363344.png differ diff --git a/YangLei/SpringCloud.assets/1589270390144.png b/YangLei/SpringCloud.assets/1589270390144.png new file mode 100644 index 0000000..3aeb9eb Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270390144.png differ diff --git a/YangLei/SpringCloud.assets/1589270520137.png b/YangLei/SpringCloud.assets/1589270520137.png new file mode 100644 index 0000000..a132328 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270520137.png differ diff --git a/YangLei/SpringCloud.assets/1589270599052.png b/YangLei/SpringCloud.assets/1589270599052.png new file mode 100644 index 0000000..e581f0e Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270599052.png differ diff --git a/YangLei/SpringCloud.assets/1589270693045.png b/YangLei/SpringCloud.assets/1589270693045.png new file mode 100644 index 0000000..837bd51 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589270693045.png differ diff --git a/YangLei/SpringCloud.assets/1589272436687.png b/YangLei/SpringCloud.assets/1589272436687.png new file mode 100644 index 0000000..79b49a4 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589272436687.png differ diff --git a/YangLei/SpringCloud.assets/1589332927419.png b/YangLei/SpringCloud.assets/1589332927419.png new file mode 100644 index 0000000..9a5af4b Binary files /dev/null and b/YangLei/SpringCloud.assets/1589332927419.png differ diff --git a/YangLei/SpringCloud.assets/1589787749730.png b/YangLei/SpringCloud.assets/1589787749730.png new file mode 100644 index 0000000..bdc3637 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589787749730.png differ diff --git a/YangLei/SpringCloud.assets/1589789891368.png b/YangLei/SpringCloud.assets/1589789891368.png new file mode 100644 index 0000000..0e7af63 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589789891368.png differ diff --git a/YangLei/SpringCloud.assets/1589793272794.png b/YangLei/SpringCloud.assets/1589793272794.png new file mode 100644 index 0000000..75ae18f Binary files /dev/null and b/YangLei/SpringCloud.assets/1589793272794.png differ diff --git a/YangLei/SpringCloud.assets/1589881957900.png b/YangLei/SpringCloud.assets/1589881957900.png new file mode 100644 index 0000000..691660e Binary files /dev/null and b/YangLei/SpringCloud.assets/1589881957900.png differ diff --git a/YangLei/SpringCloud.assets/1589956868025.png b/YangLei/SpringCloud.assets/1589956868025.png new file mode 100644 index 0000000..2ae3def Binary files /dev/null and b/YangLei/SpringCloud.assets/1589956868025.png differ diff --git a/YangLei/SpringCloud.assets/1589956993325.png b/YangLei/SpringCloud.assets/1589956993325.png new file mode 100644 index 0000000..8c54998 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589956993325.png differ diff --git a/YangLei/SpringCloud.assets/1589957071222.png b/YangLei/SpringCloud.assets/1589957071222.png new file mode 100644 index 0000000..0a389d1 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589957071222.png differ diff --git a/YangLei/SpringCloud.assets/1589957131580.png b/YangLei/SpringCloud.assets/1589957131580.png new file mode 100644 index 0000000..4027232 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589957131580.png differ diff --git a/YangLei/SpringCloud.assets/1589962499895.png b/YangLei/SpringCloud.assets/1589962499895.png new file mode 100644 index 0000000..6dc7956 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589962499895.png differ diff --git a/YangLei/SpringCloud.assets/1589963110082.png b/YangLei/SpringCloud.assets/1589963110082.png new file mode 100644 index 0000000..97e97e0 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589963110082.png differ diff --git a/YangLei/SpringCloud.assets/1589963383557.png b/YangLei/SpringCloud.assets/1589963383557.png new file mode 100644 index 0000000..a99c1c1 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589963383557.png differ diff --git a/YangLei/SpringCloud.assets/1589963514015.png b/YangLei/SpringCloud.assets/1589963514015.png new file mode 100644 index 0000000..8e79810 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589963514015.png differ diff --git a/YangLei/SpringCloud.assets/1589963780719.png b/YangLei/SpringCloud.assets/1589963780719.png new file mode 100644 index 0000000..9cb60fd Binary files /dev/null and b/YangLei/SpringCloud.assets/1589963780719.png differ diff --git a/YangLei/SpringCloud.assets/1589963909530.png b/YangLei/SpringCloud.assets/1589963909530.png new file mode 100644 index 0000000..3ee177f Binary files /dev/null and b/YangLei/SpringCloud.assets/1589963909530.png differ diff --git a/YangLei/SpringCloud.assets/1589963982249.png b/YangLei/SpringCloud.assets/1589963982249.png new file mode 100644 index 0000000..f4fa146 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589963982249.png differ diff --git a/YangLei/SpringCloud.assets/1589964023335.png b/YangLei/SpringCloud.assets/1589964023335.png new file mode 100644 index 0000000..d68e91e Binary files /dev/null and b/YangLei/SpringCloud.assets/1589964023335.png differ diff --git a/YangLei/SpringCloud.assets/1589964081265.png b/YangLei/SpringCloud.assets/1589964081265.png new file mode 100644 index 0000000..f6b732a Binary files /dev/null and b/YangLei/SpringCloud.assets/1589964081265.png differ diff --git a/YangLei/SpringCloud.assets/1589964612780.png b/YangLei/SpringCloud.assets/1589964612780.png new file mode 100644 index 0000000..9429e54 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589964612780.png differ diff --git a/YangLei/SpringCloud.assets/1589964669435.png b/YangLei/SpringCloud.assets/1589964669435.png new file mode 100644 index 0000000..79687e8 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589964669435.png differ diff --git a/YangLei/SpringCloud.assets/1589964677776.png b/YangLei/SpringCloud.assets/1589964677776.png new file mode 100644 index 0000000..79687e8 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589964677776.png differ diff --git a/YangLei/SpringCloud.assets/1589964775725.png b/YangLei/SpringCloud.assets/1589964775725.png new file mode 100644 index 0000000..12b33af Binary files /dev/null and b/YangLei/SpringCloud.assets/1589964775725.png differ diff --git a/YangLei/SpringCloud.assets/1589965830847.png b/YangLei/SpringCloud.assets/1589965830847.png new file mode 100644 index 0000000..7f1c072 Binary files /dev/null and b/YangLei/SpringCloud.assets/1589965830847.png differ diff --git a/YangLei/SpringCloud.assets/1590117896412.png b/YangLei/SpringCloud.assets/1590117896412.png new file mode 100644 index 0000000..f9ddc66 Binary files /dev/null and b/YangLei/SpringCloud.assets/1590117896412.png differ diff --git a/YangLei/SpringCloud.assets/1590156366126.png b/YangLei/SpringCloud.assets/1590156366126.png new file mode 100644 index 0000000..780495c Binary files /dev/null and b/YangLei/SpringCloud.assets/1590156366126.png differ diff --git a/YangLei/SpringCloud.assets/1590206266102.png b/YangLei/SpringCloud.assets/1590206266102.png new file mode 100644 index 0000000..4943659 Binary files /dev/null and b/YangLei/SpringCloud.assets/1590206266102.png differ diff --git a/YangLei/SpringCloud.assets/1590206298961.png b/YangLei/SpringCloud.assets/1590206298961.png new file mode 100644 index 0000000..d6de06b Binary files /dev/null and b/YangLei/SpringCloud.assets/1590206298961.png differ diff --git a/YangLei/SpringCloud.assets/1590290371971.png b/YangLei/SpringCloud.assets/1590290371971.png new file mode 100644 index 0000000..bdd84bf Binary files /dev/null and b/YangLei/SpringCloud.assets/1590290371971.png differ diff --git a/YangLei/SpringCloud.assets/1590290849023.png b/YangLei/SpringCloud.assets/1590290849023.png new file mode 100644 index 0000000..052a49d Binary files /dev/null and b/YangLei/SpringCloud.assets/1590290849023.png differ diff --git a/YangLei/SpringCloud.assets/1590290929821.png b/YangLei/SpringCloud.assets/1590290929821.png new file mode 100644 index 0000000..3153cf0 Binary files /dev/null and b/YangLei/SpringCloud.assets/1590290929821.png differ diff --git a/YangLei/SpringCloud.assets/1590291514784.png b/YangLei/SpringCloud.assets/1590291514784.png new file mode 100644 index 0000000..dfa343f Binary files /dev/null and b/YangLei/SpringCloud.assets/1590291514784.png differ diff --git a/YangLei/SpringCloud.assets/1590292364166.png b/YangLei/SpringCloud.assets/1590292364166.png new file mode 100644 index 0000000..06c2ba2 Binary files /dev/null and b/YangLei/SpringCloud.assets/1590292364166.png differ diff --git a/YangLei/SpringCloud.assets/1590292904941.png b/YangLei/SpringCloud.assets/1590292904941.png new file mode 100644 index 0000000..475642d Binary files /dev/null and b/YangLei/SpringCloud.assets/1590292904941.png differ diff --git a/YangLei/SpringCloud.assets/1590293660041.png b/YangLei/SpringCloud.assets/1590293660041.png new file mode 100644 index 0000000..f912754 Binary files /dev/null and b/YangLei/SpringCloud.assets/1590293660041.png differ diff --git a/YangLei/SpringCloud.assets/1590293751939.png b/YangLei/SpringCloud.assets/1590293751939.png new file mode 100644 index 0000000..a8226a3 Binary files /dev/null and b/YangLei/SpringCloud.assets/1590293751939.png differ diff --git a/YangLei/SpringCloud.assets/1590326623829.png b/YangLei/SpringCloud.assets/1590326623829.png new file mode 100644 index 0000000..1a1fbe4 Binary files /dev/null and b/YangLei/SpringCloud.assets/1590326623829.png differ diff --git a/YangLei/SpringCloud.assets/1590395847445.png b/YangLei/SpringCloud.assets/1590395847445.png new file mode 100644 index 0000000..0842823 Binary files /dev/null and b/YangLei/SpringCloud.assets/1590395847445.png differ diff --git a/YangLei/SpringCloud.assets/1590396709900.png b/YangLei/SpringCloud.assets/1590396709900.png new file mode 100644 index 0000000..cc3f3dc Binary files /dev/null and b/YangLei/SpringCloud.assets/1590396709900.png differ diff --git a/YangLei/SpringCloud.assets/1591147867297.png b/YangLei/SpringCloud.assets/1591147867297.png new file mode 100644 index 0000000..ca65917 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591147867297.png differ diff --git a/YangLei/SpringCloud.assets/1591148436300.png b/YangLei/SpringCloud.assets/1591148436300.png new file mode 100644 index 0000000..fa2fe09 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591148436300.png differ diff --git a/YangLei/SpringCloud.assets/1591148537228.png b/YangLei/SpringCloud.assets/1591148537228.png new file mode 100644 index 0000000..b424610 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591148537228.png differ diff --git a/YangLei/SpringCloud.assets/1591148666900.png b/YangLei/SpringCloud.assets/1591148666900.png new file mode 100644 index 0000000..8fc0793 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591148666900.png differ diff --git a/YangLei/SpringCloud.assets/1591148895261.png b/YangLei/SpringCloud.assets/1591148895261.png new file mode 100644 index 0000000..cbe704c Binary files /dev/null and b/YangLei/SpringCloud.assets/1591148895261.png differ diff --git a/YangLei/SpringCloud.assets/1591149077741.png b/YangLei/SpringCloud.assets/1591149077741.png new file mode 100644 index 0000000..f40d506 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591149077741.png differ diff --git a/YangLei/SpringCloud.assets/1591151710444.png b/YangLei/SpringCloud.assets/1591151710444.png new file mode 100644 index 0000000..43b4bb1 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591151710444.png differ diff --git a/YangLei/SpringCloud.assets/1591151860745.png b/YangLei/SpringCloud.assets/1591151860745.png new file mode 100644 index 0000000..cfdc1e2 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591151860745.png differ diff --git a/YangLei/SpringCloud.assets/1591689508621.png b/YangLei/SpringCloud.assets/1591689508621.png new file mode 100644 index 0000000..8b165cb Binary files /dev/null and b/YangLei/SpringCloud.assets/1591689508621.png differ diff --git a/YangLei/SpringCloud.assets/1591689589925.png b/YangLei/SpringCloud.assets/1591689589925.png new file mode 100644 index 0000000..f677c98 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591689589925.png differ diff --git a/YangLei/SpringCloud.assets/1591944008562.png b/YangLei/SpringCloud.assets/1591944008562.png new file mode 100644 index 0000000..971487e Binary files /dev/null and b/YangLei/SpringCloud.assets/1591944008562.png differ diff --git a/YangLei/SpringCloud.assets/1591945814431.png b/YangLei/SpringCloud.assets/1591945814431.png new file mode 100644 index 0000000..79d509c Binary files /dev/null and b/YangLei/SpringCloud.assets/1591945814431.png differ diff --git a/YangLei/SpringCloud.assets/1591948389606.png b/YangLei/SpringCloud.assets/1591948389606.png new file mode 100644 index 0000000..1f49f91 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591948389606.png differ diff --git a/YangLei/SpringCloud.assets/1591955254185.png b/YangLei/SpringCloud.assets/1591955254185.png new file mode 100644 index 0000000..5056471 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591955254185.png differ diff --git a/YangLei/SpringCloud.assets/1591955937766.png b/YangLei/SpringCloud.assets/1591955937766.png new file mode 100644 index 0000000..dcdce76 Binary files /dev/null and b/YangLei/SpringCloud.assets/1591955937766.png differ diff --git a/YangLei/SpringCloud.assets/1592186527918.png b/YangLei/SpringCloud.assets/1592186527918.png new file mode 100644 index 0000000..786a1a8 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592186527918.png differ diff --git a/YangLei/SpringCloud.assets/1592186565880.png b/YangLei/SpringCloud.assets/1592186565880.png new file mode 100644 index 0000000..c27f67e Binary files /dev/null and b/YangLei/SpringCloud.assets/1592186565880.png differ diff --git a/YangLei/SpringCloud.assets/1592187031257.png b/YangLei/SpringCloud.assets/1592187031257.png new file mode 100644 index 0000000..b12bab4 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592187031257.png differ diff --git a/YangLei/SpringCloud.assets/1592874931363.png b/YangLei/SpringCloud.assets/1592874931363.png new file mode 100644 index 0000000..f78346b Binary files /dev/null and b/YangLei/SpringCloud.assets/1592874931363.png differ diff --git a/YangLei/SpringCloud.assets/1592880224156.png b/YangLei/SpringCloud.assets/1592880224156.png new file mode 100644 index 0000000..2c9bbc9 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592880224156.png differ diff --git a/YangLei/SpringCloud.assets/1592882064655.png b/YangLei/SpringCloud.assets/1592882064655.png new file mode 100644 index 0000000..7e61280 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592882064655.png differ diff --git a/YangLei/SpringCloud.assets/1592882457545.png b/YangLei/SpringCloud.assets/1592882457545.png new file mode 100644 index 0000000..d798d86 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592882457545.png differ diff --git a/YangLei/SpringCloud.assets/1592882971685.png b/YangLei/SpringCloud.assets/1592882971685.png new file mode 100644 index 0000000..5befae1 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592882971685.png differ diff --git a/YangLei/SpringCloud.assets/1592891587067.png b/YangLei/SpringCloud.assets/1592891587067.png new file mode 100644 index 0000000..385e85d Binary files /dev/null and b/YangLei/SpringCloud.assets/1592891587067.png differ diff --git a/YangLei/SpringCloud.assets/1592891693564.png b/YangLei/SpringCloud.assets/1592891693564.png new file mode 100644 index 0000000..6cf81c3 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592891693564.png differ diff --git a/YangLei/SpringCloud.assets/1592989477724.png b/YangLei/SpringCloud.assets/1592989477724.png new file mode 100644 index 0000000..f2f72bf Binary files /dev/null and b/YangLei/SpringCloud.assets/1592989477724.png differ diff --git a/YangLei/SpringCloud.assets/1592989508234.png b/YangLei/SpringCloud.assets/1592989508234.png new file mode 100644 index 0000000..c6bb899 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592989508234.png differ diff --git a/YangLei/SpringCloud.assets/1592989813112.png b/YangLei/SpringCloud.assets/1592989813112.png new file mode 100644 index 0000000..d0ffb60 Binary files /dev/null and b/YangLei/SpringCloud.assets/1592989813112.png differ diff --git a/YangLei/SpringCloud.assets/1593306118196.png b/YangLei/SpringCloud.assets/1593306118196.png new file mode 100644 index 0000000..4ef4052 Binary files /dev/null and b/YangLei/SpringCloud.assets/1593306118196.png differ diff --git a/YangLei/SpringCloud.assets/1ada747175704ecba3507074847002d0-new-imagee5249fee-c5ee-4472-9983-f1bd5801387c.png b/YangLei/SpringCloud.assets/1ada747175704ecba3507074847002d0-new-imagee5249fee-c5ee-4472-9983-f1bd5801387c.png new file mode 100644 index 0000000..655f74c Binary files /dev/null and b/YangLei/SpringCloud.assets/1ada747175704ecba3507074847002d0-new-imagee5249fee-c5ee-4472-9983-f1bd5801387c.png differ diff --git a/YangLei/SpringCloud.assets/24382ce6bbd44932ac38b1accade12d1-new-image2ff8affc-6f1d-49de-a8c3-801e7bad2b11.png b/YangLei/SpringCloud.assets/24382ce6bbd44932ac38b1accade12d1-new-image2ff8affc-6f1d-49de-a8c3-801e7bad2b11.png new file mode 100644 index 0000000..f99e187 Binary files /dev/null and b/YangLei/SpringCloud.assets/24382ce6bbd44932ac38b1accade12d1-new-image2ff8affc-6f1d-49de-a8c3-801e7bad2b11.png differ diff --git a/YangLei/SpringCloud.assets/4d161e2950414113834f2f0a8fc2c16c-new-imaged17347a0-e653-4830-9542-3d7ae4305b2b.png b/YangLei/SpringCloud.assets/4d161e2950414113834f2f0a8fc2c16c-new-imaged17347a0-e653-4830-9542-3d7ae4305b2b.png new file mode 100644 index 0000000..56ada3b Binary files /dev/null and b/YangLei/SpringCloud.assets/4d161e2950414113834f2f0a8fc2c16c-new-imaged17347a0-e653-4830-9542-3d7ae4305b2b.png differ diff --git a/YangLei/SpringCloud.assets/513d7e7f6d574fd799195d05556f4aa7-new-image9265b6bd-41ca-4e62-86f3-4341e5bdbe6c.png b/YangLei/SpringCloud.assets/513d7e7f6d574fd799195d05556f4aa7-new-image9265b6bd-41ca-4e62-86f3-4341e5bdbe6c.png new file mode 100644 index 0000000..5c2cae6 Binary files /dev/null and b/YangLei/SpringCloud.assets/513d7e7f6d574fd799195d05556f4aa7-new-image9265b6bd-41ca-4e62-86f3-4341e5bdbe6c.png differ diff --git a/YangLei/SpringCloud.assets/5d723c49eca1468ab7b89af06743023c-new-imageb8aa3d41-fad4-4b38-add9-c304930ab285.png b/YangLei/SpringCloud.assets/5d723c49eca1468ab7b89af06743023c-new-imageb8aa3d41-fad4-4b38-add9-c304930ab285.png new file mode 100644 index 0000000..1852944 Binary files /dev/null and b/YangLei/SpringCloud.assets/5d723c49eca1468ab7b89af06743023c-new-imageb8aa3d41-fad4-4b38-add9-c304930ab285.png differ diff --git a/YangLei/SpringCloud.assets/FCat.png b/YangLei/SpringCloud.assets/FCat.png new file mode 100644 index 0000000..4cef9dc Binary files /dev/null and b/YangLei/SpringCloud.assets/FCat.png differ diff --git a/YangLei/SpringCloud.assets/PInfrastructure.png b/YangLei/SpringCloud.assets/PInfrastructure.png new file mode 100644 index 0000000..7a09e02 Binary files /dev/null and b/YangLei/SpringCloud.assets/PInfrastructure.png differ diff --git a/YangLei/SpringCloud.assets/PiggyMetrics_sercive.png b/YangLei/SpringCloud.assets/PiggyMetrics_sercive.png new file mode 100644 index 0000000..ebaf157 Binary files /dev/null and b/YangLei/SpringCloud.assets/PiggyMetrics_sercive.png differ diff --git a/YangLei/SpringCloud.assets/a2b.jpg b/YangLei/SpringCloud.assets/a2b.jpg new file mode 100644 index 0000000..b273a94 Binary files /dev/null and b/YangLei/SpringCloud.assets/a2b.jpg differ diff --git a/YangLei/SpringCloud.assets/a2b2c.jpg b/YangLei/SpringCloud.assets/a2b2c.jpg new file mode 100644 index 0000000..a6cf2c3 Binary files /dev/null and b/YangLei/SpringCloud.assets/a2b2c.jpg differ diff --git a/YangLei/SpringCloud.assets/ab.jpg b/YangLei/SpringCloud.assets/ab.jpg new file mode 100644 index 0000000..6959de2 Binary files /dev/null and b/YangLei/SpringCloud.assets/ab.jpg differ diff --git a/YangLei/SpringCloud.assets/abc.jpg b/YangLei/SpringCloud.assets/abc.jpg new file mode 100644 index 0000000..adfe29a Binary files /dev/null and b/YangLei/SpringCloud.assets/abc.jpg differ diff --git a/YangLei/SpringCloud.assets/ace-security.png b/YangLei/SpringCloud.assets/ace-security.png new file mode 100644 index 0000000..c1cea23 Binary files /dev/null and b/YangLei/SpringCloud.assets/ace-security.png differ diff --git a/YangLei/SpringCloud.assets/api_gateway-1589793999473.png b/YangLei/SpringCloud.assets/api_gateway-1589793999473.png new file mode 100644 index 0000000..3c1a0bd Binary files /dev/null and b/YangLei/SpringCloud.assets/api_gateway-1589793999473.png differ diff --git a/YangLei/SpringCloud.assets/api_gateway.png b/YangLei/SpringCloud.assets/api_gateway.png new file mode 100644 index 0000000..3c1a0bd Binary files /dev/null and b/YangLei/SpringCloud.assets/api_gateway.png differ diff --git a/YangLei/SpringCloud.assets/awesome-spring-cloud.png b/YangLei/SpringCloud.assets/awesome-spring-cloud.png new file mode 100644 index 0000000..519ae60 Binary files /dev/null and b/YangLei/SpringCloud.assets/awesome-spring-cloud.png differ diff --git a/YangLei/SpringCloud.assets/bff-process.png b/YangLei/SpringCloud.assets/bff-process.png new file mode 100644 index 0000000..585ff05 Binary files /dev/null and b/YangLei/SpringCloud.assets/bff-process.png differ diff --git a/YangLei/SpringCloud.assets/bff.png b/YangLei/SpringCloud.assets/bff.png new file mode 100644 index 0000000..4030b41 Binary files /dev/null and b/YangLei/SpringCloud.assets/bff.png differ diff --git a/YangLei/SpringCloud.assets/calling_relation.png b/YangLei/SpringCloud.assets/calling_relation.png new file mode 100644 index 0000000..0a11749 Binary files /dev/null and b/YangLei/SpringCloud.assets/calling_relation.png differ diff --git a/YangLei/SpringCloud.assets/configbus1.jpg b/YangLei/SpringCloud.assets/configbus1.jpg new file mode 100644 index 0000000..043f020 Binary files /dev/null and b/YangLei/SpringCloud.assets/configbus1.jpg differ diff --git a/YangLei/SpringCloud.assets/configbus2-1589793999251.jpg b/YangLei/SpringCloud.assets/configbus2-1589793999251.jpg new file mode 100644 index 0000000..45d8725 Binary files /dev/null and b/YangLei/SpringCloud.assets/configbus2-1589793999251.jpg differ diff --git a/YangLei/SpringCloud.assets/configbus2.jpg b/YangLei/SpringCloud.assets/configbus2.jpg new file mode 100644 index 0000000..45d8725 Binary files /dev/null and b/YangLei/SpringCloud.assets/configbus2.jpg differ diff --git a/YangLei/SpringCloud.assets/configbus3.jpg b/YangLei/SpringCloud.assets/configbus3.jpg new file mode 100644 index 0000000..28ef004 Binary files /dev/null and b/YangLei/SpringCloud.assets/configbus3.jpg differ diff --git a/YangLei/SpringCloud.assets/consol_cmd.png b/YangLei/SpringCloud.assets/consol_cmd.png new file mode 100644 index 0000000..59a7abb Binary files /dev/null and b/YangLei/SpringCloud.assets/consol_cmd.png differ diff --git a/YangLei/SpringCloud.assets/consol_manage.png b/YangLei/SpringCloud.assets/consol_manage.png new file mode 100644 index 0000000..5dfa2f4 Binary files /dev/null and b/YangLei/SpringCloud.assets/consol_manage.png differ diff --git a/YangLei/SpringCloud.assets/consol_producer-2.png b/YangLei/SpringCloud.assets/consol_producer-2.png new file mode 100644 index 0000000..8dc2dc4 Binary files /dev/null and b/YangLei/SpringCloud.assets/consol_producer-2.png differ diff --git a/YangLei/SpringCloud.assets/consol_producer.png b/YangLei/SpringCloud.assets/consol_producer.png new file mode 100644 index 0000000..de45068 Binary files /dev/null and b/YangLei/SpringCloud.assets/consol_producer.png differ diff --git a/YangLei/SpringCloud.assets/consol_service.png b/YangLei/SpringCloud.assets/consol_service.png new file mode 100644 index 0000000..e3b58dd Binary files /dev/null and b/YangLei/SpringCloud.assets/consol_service.png differ diff --git a/YangLei/SpringCloud.assets/consul-server-client.png b/YangLei/SpringCloud.assets/consul-server-client.png new file mode 100644 index 0000000..e286201 Binary files /dev/null and b/YangLei/SpringCloud.assets/consul-server-client.png differ diff --git a/YangLei/SpringCloud.assets/consul_insall.png b/YangLei/SpringCloud.assets/consul_insall.png new file mode 100644 index 0000000..3978306 Binary files /dev/null and b/YangLei/SpringCloud.assets/consul_insall.png differ diff --git a/YangLei/SpringCloud.assets/consul_win.png b/YangLei/SpringCloud.assets/consul_win.png new file mode 100644 index 0000000..03024d6 Binary files /dev/null and b/YangLei/SpringCloud.assets/consul_win.png differ diff --git a/YangLei/SpringCloud.assets/dubbo-architecture.png b/YangLei/SpringCloud.assets/dubbo-architecture.png new file mode 100644 index 0000000..cf76625 Binary files /dev/null and b/YangLei/SpringCloud.assets/dubbo-architecture.png differ diff --git a/YangLei/SpringCloud.assets/dubbo_admin.png b/YangLei/SpringCloud.assets/dubbo_admin.png new file mode 100644 index 0000000..f405f17 Binary files /dev/null and b/YangLei/SpringCloud.assets/dubbo_admin.png differ diff --git a/YangLei/SpringCloud.assets/dubbox_rest.jpg b/YangLei/SpringCloud.assets/dubbox_rest.jpg new file mode 100644 index 0000000..99a1b5b Binary files /dev/null and b/YangLei/SpringCloud.assets/dubbox_rest.jpg differ diff --git a/YangLei/SpringCloud.assets/dyl.png b/YangLei/SpringCloud.assets/dyl.png new file mode 100644 index 0000000..eb715d3 Binary files /dev/null and b/YangLei/SpringCloud.assets/dyl.png differ diff --git a/YangLei/SpringCloud.assets/eureka-architecture-overview.png b/YangLei/SpringCloud.assets/eureka-architecture-overview.png new file mode 100644 index 0000000..431c36d Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka-architecture-overview.png differ diff --git a/YangLei/SpringCloud.assets/eureka-cluster.jpg b/YangLei/SpringCloud.assets/eureka-cluster.jpg new file mode 100644 index 0000000..e0f78ff Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka-cluster.jpg differ diff --git a/YangLei/SpringCloud.assets/eureka-config01.jpg b/YangLei/SpringCloud.assets/eureka-config01.jpg new file mode 100644 index 0000000..9d1f3af Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka-config01.jpg differ diff --git a/YangLei/SpringCloud.assets/eureka-config02.jpg b/YangLei/SpringCloud.assets/eureka-config02.jpg new file mode 100644 index 0000000..e8b169f Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka-config02.jpg differ diff --git a/YangLei/SpringCloud.assets/eureka-config03.jpg b/YangLei/SpringCloud.assets/eureka-config03.jpg new file mode 100644 index 0000000..867dbd4 Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka-config03.jpg differ diff --git a/YangLei/SpringCloud.assets/eureka-two.jpg b/YangLei/SpringCloud.assets/eureka-two.jpg new file mode 100644 index 0000000..e1002a5 Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka-two.jpg differ diff --git a/YangLei/SpringCloud.assets/eureka.jpg b/YangLei/SpringCloud.assets/eureka.jpg new file mode 100644 index 0000000..6d3d977 Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka.jpg differ diff --git a/YangLei/SpringCloud.assets/eureka_server.png b/YangLei/SpringCloud.assets/eureka_server.png new file mode 100644 index 0000000..be8c92f Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka_server.png differ diff --git a/YangLei/SpringCloud.assets/eureka_server2.png b/YangLei/SpringCloud.assets/eureka_server2.png new file mode 100644 index 0000000..b73777b Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka_server2.png differ diff --git a/YangLei/SpringCloud.assets/eureka_start.jpg b/YangLei/SpringCloud.assets/eureka_start.jpg new file mode 100644 index 0000000..4a2687a Binary files /dev/null and b/YangLei/SpringCloud.assets/eureka_start.jpg differ diff --git a/YangLei/SpringCloud.assets/hystrix-1-1589793999051.png b/YangLei/SpringCloud.assets/hystrix-1-1589793999051.png new file mode 100644 index 0000000..fa6aab3 Binary files /dev/null and b/YangLei/SpringCloud.assets/hystrix-1-1589793999051.png differ diff --git a/YangLei/SpringCloud.assets/hystrix-1.png b/YangLei/SpringCloud.assets/hystrix-1.png new file mode 100644 index 0000000..fa6aab3 Binary files /dev/null and b/YangLei/SpringCloud.assets/hystrix-1.png differ diff --git a/YangLei/SpringCloud.assets/hystrix-2.png b/YangLei/SpringCloud.assets/hystrix-2.png new file mode 100644 index 0000000..e8fcedf Binary files /dev/null and b/YangLei/SpringCloud.assets/hystrix-2.png differ diff --git a/YangLei/SpringCloud.assets/hystrix-dashboard-1.jpg b/YangLei/SpringCloud.assets/hystrix-dashboard-1.jpg new file mode 100644 index 0000000..930632f Binary files /dev/null and b/YangLei/SpringCloud.assets/hystrix-dashboard-1.jpg differ diff --git a/YangLei/SpringCloud.assets/hystrix-dashboard-2.jpg b/YangLei/SpringCloud.assets/hystrix-dashboard-2.jpg new file mode 100644 index 0000000..c1a119b Binary files /dev/null and b/YangLei/SpringCloud.assets/hystrix-dashboard-2.jpg differ diff --git a/YangLei/SpringCloud.assets/hystrix-dashboard-3.png b/YangLei/SpringCloud.assets/hystrix-dashboard-3.png new file mode 100644 index 0000000..9509cfe Binary files /dev/null and b/YangLei/SpringCloud.assets/hystrix-dashboard-3.png differ diff --git a/YangLei/SpringCloud.assets/paascloud.png b/YangLei/SpringCloud.assets/paascloud.png new file mode 100644 index 0000000..425c943 Binary files /dev/null and b/YangLei/SpringCloud.assets/paascloud.png differ diff --git a/YangLei/SpringCloud.assets/piggyMetrics.png b/YangLei/SpringCloud.assets/piggyMetrics.png new file mode 100644 index 0000000..56e8ff2 Binary files /dev/null and b/YangLei/SpringCloud.assets/piggyMetrics.png differ diff --git a/YangLei/SpringCloud.assets/ping.png b/YangLei/SpringCloud.assets/ping.png new file mode 100644 index 0000000..0696632 Binary files /dev/null and b/YangLei/SpringCloud.assets/ping.png differ diff --git a/YangLei/SpringCloud.assets/single_structure.jpg b/YangLei/SpringCloud.assets/single_structure.jpg new file mode 100644 index 0000000..f770553 Binary files /dev/null and b/YangLei/SpringCloud.assets/single_structure.jpg differ diff --git a/YangLei/SpringCloud.assets/soa__structure.jpg b/YangLei/SpringCloud.assets/soa__structure.jpg new file mode 100644 index 0000000..503dd51 Binary files /dev/null and b/YangLei/SpringCloud.assets/soa__structure.jpg differ diff --git a/YangLei/SpringCloud.assets/spring-boot-cloud.jpg b/YangLei/SpringCloud.assets/spring-boot-cloud.jpg new file mode 100644 index 0000000..3d7b7ad Binary files /dev/null and b/YangLei/SpringCloud.assets/spring-boot-cloud.jpg differ diff --git a/YangLei/SpringCloud.assets/spring-cloud-architecture.png b/YangLei/SpringCloud.assets/spring-cloud-architecture.png new file mode 100644 index 0000000..5be6d8d Binary files /dev/null and b/YangLei/SpringCloud.assets/spring-cloud-architecture.png differ diff --git a/YangLei/SpringCloud.assets/spring-cloud-examples.png b/YangLei/SpringCloud.assets/spring-cloud-examples.png new file mode 100644 index 0000000..b8dc2e4 Binary files /dev/null and b/YangLei/SpringCloud.assets/spring-cloud-examples.png differ diff --git a/YangLei/SpringCloud.assets/spring-cloud-gateway.png b/YangLei/SpringCloud.assets/spring-cloud-gateway.png new file mode 100644 index 0000000..25ffbcc Binary files /dev/null and b/YangLei/SpringCloud.assets/spring-cloud-gateway.png differ diff --git a/YangLei/SpringCloud.assets/spring-cloud-gateway1.png b/YangLei/SpringCloud.assets/spring-cloud-gateway1.png new file mode 100644 index 0000000..19dd940 Binary files /dev/null and b/YangLei/SpringCloud.assets/spring-cloud-gateway1.png differ diff --git a/YangLei/SpringCloud.assets/spring-cloud-gateway3.png b/YangLei/SpringCloud.assets/spring-cloud-gateway3.png new file mode 100644 index 0000000..e76ffaf Binary files /dev/null and b/YangLei/SpringCloud.assets/spring-cloud-gateway3.png differ diff --git a/YangLei/SpringCloud.assets/spring-cloud-rest-tcc.png b/YangLei/SpringCloud.assets/spring-cloud-rest-tcc.png new file mode 100644 index 0000000..4150188 Binary files /dev/null and b/YangLei/SpringCloud.assets/spring-cloud-rest-tcc.png differ diff --git a/YangLei/SpringCloud.assets/spring_cloud_structure.png b/YangLei/SpringCloud.assets/spring_cloud_structure.png new file mode 100644 index 0000000..f151e60 Binary files /dev/null and b/YangLei/SpringCloud.assets/spring_cloud_structure.png differ diff --git a/YangLei/SpringCloud.assets/springcloud-question.png b/YangLei/SpringCloud.assets/springcloud-question.png new file mode 100644 index 0000000..e0bf45b Binary files /dev/null and b/YangLei/SpringCloud.assets/springcloud-question.png differ diff --git a/YangLei/SpringCloud.assets/tracing1.png b/YangLei/SpringCloud.assets/tracing1.png new file mode 100644 index 0000000..1295c57 Binary files /dev/null and b/YangLei/SpringCloud.assets/tracing1.png differ diff --git a/YangLei/SpringCloud.assets/tracing2.png b/YangLei/SpringCloud.assets/tracing2.png new file mode 100644 index 0000000..0603bb0 Binary files /dev/null and b/YangLei/SpringCloud.assets/tracing2.png differ diff --git a/YangLei/SpringCloud.assets/tracing3.png b/YangLei/SpringCloud.assets/tracing3.png new file mode 100644 index 0000000..eb88585 Binary files /dev/null and b/YangLei/SpringCloud.assets/tracing3.png differ diff --git a/YangLei/SpringCloud.assets/turbine-01.jpg b/YangLei/SpringCloud.assets/turbine-01.jpg new file mode 100644 index 0000000..ed52087 Binary files /dev/null and b/YangLei/SpringCloud.assets/turbine-01.jpg differ diff --git a/YangLei/SpringCloud.assets/turbine-02-1589793999206.jpg b/YangLei/SpringCloud.assets/turbine-02-1589793999206.jpg new file mode 100644 index 0000000..d883889 Binary files /dev/null and b/YangLei/SpringCloud.assets/turbine-02-1589793999206.jpg differ diff --git a/YangLei/SpringCloud.assets/turbine-02.jpg b/YangLei/SpringCloud.assets/turbine-02.jpg new file mode 100644 index 0000000..d883889 Binary files /dev/null and b/YangLei/SpringCloud.assets/turbine-02.jpg differ diff --git a/YangLei/SpringCloud.assets/vertical__structure.jpg b/YangLei/SpringCloud.assets/vertical__structure.jpg new file mode 100644 index 0000000..c59aead Binary files /dev/null and b/YangLei/SpringCloud.assets/vertical__structure.jpg differ diff --git a/YangLei/SpringCloud.assets/webhook.jpg b/YangLei/SpringCloud.assets/webhook.jpg new file mode 100644 index 0000000..2ed89d9 Binary files /dev/null and b/YangLei/SpringCloud.assets/webhook.jpg differ diff --git a/YangLei/SpringCloud.assets/xxpay.png b/YangLei/SpringCloud.assets/xxpay.png new file mode 100644 index 0000000..d32b9d8 Binary files /dev/null and b/YangLei/SpringCloud.assets/xxpay.png differ diff --git a/YangLei/SpringCloud.assets/zipkin1.png b/YangLei/SpringCloud.assets/zipkin1.png new file mode 100644 index 0000000..e9e0bc9 Binary files /dev/null and b/YangLei/SpringCloud.assets/zipkin1.png differ diff --git a/YangLei/SpringCloud.assets/zipkin2.png b/YangLei/SpringCloud.assets/zipkin2.png new file mode 100644 index 0000000..010ff62 Binary files /dev/null and b/YangLei/SpringCloud.assets/zipkin2.png differ diff --git a/YangLei/SpringCloud.assets/zipkin3.png b/YangLei/SpringCloud.assets/zipkin3.png new file mode 100644 index 0000000..514794c Binary files /dev/null and b/YangLei/SpringCloud.assets/zipkin3.png differ diff --git a/YangLei/SpringCloud.assets/zuul-01.jpg b/YangLei/SpringCloud.assets/zuul-01.jpg new file mode 100644 index 0000000..ae782bd Binary files /dev/null and b/YangLei/SpringCloud.assets/zuul-01.jpg differ diff --git a/YangLei/SpringCloud.assets/zuul-case.png b/YangLei/SpringCloud.assets/zuul-case.png new file mode 100644 index 0000000..b0e65d5 Binary files /dev/null and b/YangLei/SpringCloud.assets/zuul-case.png differ diff --git a/YangLei/SpringCloud.assets/zuul-core.png b/YangLei/SpringCloud.assets/zuul-core.png new file mode 100644 index 0000000..552aab9 Binary files /dev/null and b/YangLei/SpringCloud.assets/zuul-core.png differ diff --git a/YangLei/SpringCloud.md b/YangLei/SpringCloud.md new file mode 100644 index 0000000..ed95159 --- /dev/null +++ b/YangLei/SpringCloud.md @@ -0,0 +1,5625 @@ +# SpringCloud + +# Bilibili 尚硅谷 + +看bilibili老是SpringCloud,语言少了,思维就出来了,多行动。 + +Say Say easy, Do Do hard . + + + +还是要看一下 之前的springboot的知识哔哩哔哩上面的springboot 里面actuator监控检查。 + + + +## 基础项目搭建 + +### SpringBoot和SpringCloud版本对于情况 + +参考:https://www.cnblogs.com/zhuwenjoyce/p/10261079.html + +官网:https://start.spring.io/actuator/info + +```json +{ +"git": { +"commit": { +"time": "2020-05-20T08:34:33Z", +"id": "ff14f94" +}, +"branch": "ff14f94eaa9ae4cb899f91870bcf7489225567ff" +}, +"build": { +"version": "0.0.1-SNAPSHOT", +"artifact": "start-site", +"name": "start.spring.io website", +"versions": { +"initializr": "0.9.0.BUILD-SNAPSHOT", +"spring-boot": "2.3.0.RELEASE" +}, +"group": "io.spring.start", +"time": "2020-05-20T08:48:13.153Z" +}, +"bom-ranges": { +"codecentric-spring-boot-admin": { +"2.0.6": "Spring Boot >=2.0.0.M1 and <2.1.0.M1", +"2.1.6": "Spring Boot >=2.1.0.M1 and <2.2.0.M1", +"2.2.3": "Spring Boot >=2.2.0.M1" +}, +"solace-spring-cloud": { +"1.0.0": "Spring Boot >=2.2.0.RELEASE and <2.3.0.M1" +}, +"spring-cloud": { +"Finchley.M2": "Spring Boot >=2.0.0.M3 and <2.0.0.M5", +"Finchley.M3": "Spring Boot >=2.0.0.M5 and <=2.0.0.M5", +"Finchley.M4": "Spring Boot >=2.0.0.M6 and <=2.0.0.M6", +"Finchley.M5": "Spring Boot >=2.0.0.M7 and <=2.0.0.M7", +"Finchley.M6": "Spring Boot >=2.0.0.RC1 and <=2.0.0.RC1", +"Finchley.M7": "Spring Boot >=2.0.0.RC2 and <=2.0.0.RC2", +"Finchley.M9": "Spring Boot >=2.0.0.RELEASE and <=2.0.0.RELEASE", +"Finchley.RC1": "Spring Boot >=2.0.1.RELEASE and <2.0.2.RELEASE", +"Finchley.RC2": "Spring Boot >=2.0.2.RELEASE and <2.0.3.RELEASE", +"Finchley.SR4": "Spring Boot >=2.0.3.RELEASE and <2.0.999.BUILD-SNAPSHOT", +"Finchley.BUILD-SNAPSHOT": "Spring Boot >=2.0.999.BUILD-SNAPSHOT and <2.1.0.M3", +"Greenwich.M1": "Spring Boot >=2.1.0.M3 and <2.1.0.RELEASE", +"Greenwich.SR5": "Spring Boot >=2.1.0.RELEASE and <2.1.15.BUILD-SNAPSHOT", +"Greenwich.BUILD-SNAPSHOT": "Spring Boot >=2.1.15.BUILD-SNAPSHOT and <2.2.0.M4", +"Hoxton.SR4": "Spring Boot >=2.2.0.M4 and <2.3.1.BUILD-SNAPSHOT", +"Hoxton.BUILD-SNAPSHOT": "Spring Boot >=2.3.1.BUILD-SNAPSHOT" +}, +"spring-cloud-services": { +"2.0.3.RELEASE": "Spring Boot >=2.0.0.RELEASE and <2.1.0.RELEASE", +"2.1.7.RELEASE": "Spring Boot >=2.1.0.RELEASE and <2.2.0.RELEASE", +"2.2.3.RELEASE": "Spring Boot >=2.2.0.RELEASE and <2.3.0.M1" +}, +"vaadin": { +"10.0.17": "Spring Boot >=2.0.0.M1 and <2.1.0.M1", +"14.2.0": "Spring Boot >=2.1.0.M1" +}, +"spring-statemachine": { +"2.0.0.M4": "Spring Boot >=2.0.0.RC1 and <=2.0.0.RC1", +"2.0.0.M5": "Spring Boot >=2.0.0.RC2 and <=2.0.0.RC2", +"2.0.1.RELEASE": "Spring Boot >=2.0.0.RELEASE" +}, +"azure": { +"2.0.10": "Spring Boot >=2.0.0.RELEASE and <2.1.0.RELEASE", +"2.1.10": "Spring Boot >=2.1.0.RELEASE and <2.2.0.M1", +"2.2.4": "Spring Boot >=2.2.0.M1" +}, +"wavefront": { +"2.0.0-RC1": "Spring Boot >=2.1.0.RELEASE and <2.3.1.BUILD-SNAPSHOT", +"2.0.0-SNAPSHOT": "Spring Boot >=2.3.1.BUILD-SNAPSHOT" +}, +"solace-spring-boot": { +"1.0.0": "Spring Boot >=2.2.0.RELEASE and <2.3.0.M1" +}, +"spring-cloud-alibaba": { +"2.2.1.RELEASE": "Spring Boot >=2.2.0.RELEASE and <2.3.0.M1" +} +}, +"dependency-ranges": { +"okta": { +"1.2.1": "Spring Boot >=2.1.2.RELEASE and <2.2.0.M1", +"1.4.0": "Spring Boot >=2.2.0.M1" +}, +"mybatis": { +"2.0.1": "Spring Boot >=2.0.0.RELEASE and <2.1.0.RELEASE", +"2.1.2": "Spring Boot >=2.1.0.RELEASE" +}, +"geode": { +"1.2.7.RELEASE": "Spring Boot >=2.2.0.M5 and <2.3.0.M1", +"1.3.0.RC1": "Spring Boot >=2.3.0.M1 and <2.3.1.BUILD-SNAPSHOT", +"1.3.0.BUILD-SNAPSHOT": "Spring Boot >=2.3.1.BUILD-SNAPSHOT" +}, +"camel": { +"2.22.4": "Spring Boot >=2.0.0.M1 and <2.1.0.M1", +"2.25.1": "Spring Boot >=2.1.0.M1 and <2.2.0.M1", +"3.3.0": "Spring Boot >=2.2.0.M1" +} +} +} +``` + + + +### 说起SpringCloud那些方面,可以说说这些方面。 + +![1589039327360](../media/pictures/SpringCloud.assets/1589039327360.png) + + + +服务注册中心:Eureka 不更新啦 + +如果技术用老的,那么用zookeeper,最好用的是Nacos + +![1589093101330](../media/pictures/SpringCloud.assets/1589093101330.png) + +### 代码构建 + +约定>配置>编码 + + + +### 构建项目流程 + +- 建module +- 改POM +- 写YML +- 主启动 +- 业务类 + + + +注意:改POM的时候 + +父pom文件写了对于的版本号,如果子module里面写了版本号,则用子module的版本,如果子模块没 写,则用父模块的版本号。 + + + +### 代码自动热部署 + +开发阶段,可以开,当代码很少的时候 可以开。 + +​ 其实感觉开了这个以后,一直重启,电脑会很热。还是不要用的好。 + +生产环境,热部署不可以开。 + +代码重新修改以后,系统会重启 + +- 在子工程pom中添加依赖: + +```xml + + + org.springframework.boot + spring-boot-devtools + runtime + true + +``` + +- 在父工程pom中添加一个插件: + +```xml + + + + + org.springframework.boot + spring-boot-maven-plugin + + true + true + + + + +``` + +- 然后修改设置 compiler ABCD + +![1589956868025](../media/pictures/SpringCloud.assets/1589956868025.png) + +- ctrl+ shift + alt + / + + ![1589956993325](../media/pictures/SpringCloud.assets/1589956993325.png) + + 下面这几个打钩: + +![1589957071222](../media/pictures/SpringCloud.assets/1589957071222.png) + +![1589957131580](../media/pictures/SpringCloud.assets/1589957131580.png) + +- 然后重启idea + + + + + +### 消费 + +![1589962499895](../media/pictures/SpringCloud.assets/1589962499895.png) + +restTemplate的官方文档: + +``` +https://docs.spring.io/spring-framework/docs/5.2.2.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html +``` + + + + + +## Eureka服务注册与发现 + +### 单机Eureka + +Eureka模块 要导入依赖,注意这里是server + +```xml + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + +``` + +主启动要加注解@EnableEurekaServer + +```java +@SpringBootApplication +@EnableEurekaServer //代表这里就是服务注册中心 +public class EurekMain7001 { + public static void main(String[] args) { + SpringApplication.run(EurekMain7001.class,args); + } +} +``` + + + +而需要注册进Eureka的模块需要导入的依赖是 client + +```xml + + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + +``` + +消费者这边需要给主启动添加@EnableEurekaClient + +```java +@SpringBootApplication +@EnableEurekaClient //Eureka 客户端 +public class PaymentMain8001 { + public static void main(String[] args) { + SpringApplication.run(PaymentMain8001.class,args); + } +} +``` + + + +将注册中心启动以后,消费注册进注册中心以后,显示的名字就是yml里面配置的名字 + +![1590117896412](../media/pictures/SpringCloud.assets/1590117896412.png) + + + +### 集群Eureka搭建和部署 + +![1590156366126](../media/pictures/SpringCloud.assets/1590156366126.png) + + + +一句话概括 : 互相守望,相互注册。 + +需要在C盘搭建host中 加这个 + +C:\Windows\System32\drivers\etc + +``` +####### --------------------------SpringCloud----------------------------- + +#配置eureka集群 这里是两台 要是三台往下面加就好 +127.0.0.1 eureka7001.com +127.0.0.1 eureka7002.com +``` + + + +Eureka集群三个的话,在defaultzone 下面 设置两个 ,中间逗号隔开 + +```yml +eureka: + instance: + hostname: eureka7001.com #eureka服务端的实例名称 + client: + # false表示不向注册中心注册自己(物业公司不向自己收费 当然想收也可以) + register-with-eureka: false + # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要检索服务 + fetch-registry: false + service-url: + # 设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址 + # 相互注册 如果是三台机器,这个后面逗号分开 写另一个的地址 + defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ +``` + + + +配置好了的话,是这个样子的 + +![1590206266102](../media/pictures/SpringCloud.assets/1590206266102.png) + + + +![1590206298961](../media/pictures/SpringCloud.assets/1590206298961.png) + + + +集群搞好了以后,启动顺序有讲究。需要先启动Eureka集群,两个都要启动,然后启动payment,然后启动消费者。 + + + +### 服务提供者集群 + +原来的8001,现在再加8002 + +application.yml 里面的 配置文件 端口改成8002 + +项目启动成功以后,注册中心服务提供者里面有两个服务。 + +![1590290371971](../media/pictures/SpringCloud.assets/1590290371971.png) + + + +虽然配置好了服务提供者,但是消费者在调用服务提供者的时候,有个bug, + +要想让服务提供者起到负载均衡的作用,需要配置一下这里的这个地址不能写死,需要写上注册中心的地址 + +```java +public static final String PAYMENT_URL = "http://CLOUD-PAYMENT-SERVICE"; +``` + +![1590290929821](../media/pictures/SpringCloud.assets/1590290929821.png) + +![1590290849023](../media/pictures/SpringCloud.assets/1590290849023.png) + + + +使用@LoadBalanced注解赋予RestTemplate负载均衡的能力 + +```java +@Configuration +public class ApplicationContextConfig { + + //将这个注入到容器中,用来调用其他 + @Bean + @LoadBalanced //负载均衡注解 不然找不到服务提供者 + public RestTemplate getRestTemplate() { + return new RestTemplate(); + } +} +``` + +加了这个注解以后,每次启动都会负载均衡啦,8001和8002端口会轮询调用。 + +现在的架构: + +![1590291514784](../media/pictures/SpringCloud.assets/1590291514784.png) + +### 完善 + +在payment8001,和payment8002里面application.yml 加了 + +```yml +instance: + instance-id: payment8002 +``` + +这样的话 在注册中心 就可以看到 名称发生了变化 + +![1590292364166](../media/pictures/SpringCloud.assets/1590292364166.png) + + + +这里可以查看每个服务的健康状况 + +```java +http://localhost:8001/actuator/health //这个地址 +``` + +![1590292904941](../media/pictures/SpringCloud.assets/1590292904941.png) + + + +访问地址可以查看IP ,需要在application下面加这一句 + +```yml +instance: + instance-id: payment8001 + prefer-ip-address: true #访问路径可以显示ip +``` + +![1590293751939](../media/pictures/SpringCloud.assets/1590293751939.png) + +![1590293660041](../media/pictures/SpringCloud.assets/1590293660041.png) + + + +### 服务发现discover + +在controller里面重新写了个方法 ,写好方法,然后调用,会将两个服务名字打印出来。 + +![1590326623829](../media/pictures/SpringCloud.assets/1590326623829.png) + +### Eureka自我保护 + +某时刻某一个微服务不可用了,Eureka不会立即清理,依旧会对该微服务的信息进行保存。 + +通俗举例子就是:因为疫情,如果你没交物业费,物业公司不会立即将你清退,迟几天交也行。 + + + +这是一种高可用的设计思想,如果出现网络延时,心跳暂时没有收到,也不会立即停止。 + +![1590395847445](../media/pictures/SpringCloud.assets/1590395847445.png) + + + +这就是把自我保护机制关掉以后:公司这个就是关掉的。 + +![1590396709900](../media/pictures/SpringCloud.assets/1590396709900.png) + + + +## Zookeeper服务注册与发现 + +### zookeeper注册中心 + +首先在linux上面部署zookeeper,在安全组里面开放2181端口。 + + + +如果在虚拟机中的CentOS里面安装zookeeper的话,需要双方可以ping通,同时要关闭虚拟机的防火墙。 + + + +### 服务提供者 + +启动项目发现 有时候会报错 + +因为框架原来带来一个zookeeper包,版本是 + +![1591147867297](../media/pictures/SpringCloud.assets/1591147867297.png) + + + +需要排除框架里面的版本: + +```xml + + + org.springframework.cloud + spring-cloud-starter-zookeeper-discovery + + + + org.apache.zookeeper + zookeeper + + + +``` + + + +启动完成项目以后 + +可以在阿里云服务器上面看一下,如果是下面这样 说明注册成功啦。 + +``` +ls / +[services,zookeeper] +``` + +![1591148436300](../media/pictures/SpringCloud.assets/1591148436300.png) + +``` +ls /services +[cloud-provider-payment] +``` + +![1591148537228](../media/pictures/SpringCloud.assets/1591148537228.png) + +服务器访问这个 出现下面这个 就算成功啦 + +![1591148666900](../media/pictures/SpringCloud.assets/1591148666900.png) + +还可以访问里面:可以看到流水号 + +![1591148895261](../media/pictures/SpringCloud.assets/1591148895261.png) + + + +还可以继续往下操作: + +![1591149077741](../media/pictures/SpringCloud.assets/1591149077741.png) + + + +中间的那一部分json串,格式化以后是这样的: + +```json +{"name":"cloud-provider-payment","id":"93a6b5fd-783c-4b62-910a-c3064e61ed47","address":"LAPTOP-5GQHGLD2","port":8004,"sslPort":null,"payload":{"@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance","id":"application-1","name":"cloud-provider-payment","metadata":{}},"registrationTimeUTC":1591148022189,"serviceType":"DYNAMIC","uriSpec":{"parts":[{"value":"scheme","variable":true},{"value":"://","variable":false},{"value":"address","variable":true},{"value":":","variable":false},{"value":"port","variable":true}]}} +``` + + + +```json +{ + "name":"cloud-provider-payment", + "id":"93a6b5fd-783c-4b62-910a-c3064e61ed47", + "address":"LAPTOP-5GQHGLD2", + "port":8004, + "sslPort":null, + "payload":{ + "@class":"org.springframework.cloud.zookeeper.discovery.ZookeeperInstance", + "id":"application-1", + "name":"cloud-provider-payment", + "metadata":{ + } + }, + "registrationTimeUTC":1591148022189, + "serviceType":"DYNAMIC", + "uriSpec":{ + "parts":[ + { + "value":"scheme", + "variable":true + }, + { + "value":"://", + "variable":false + }, + { + "value":"address", + "variable":true + }, + { + "value":":", + "variable":false + }, + { + "value":"port", + "variable":true + }] + } +} +``` + + + +zookeeper 上面注册的节点是临时的,当吧8004服务关闭以后,过一会,zookeeper上面的注册没有啦,所以是临时的。 + +zookeeper算是渣男类型,他没有Eureka那样温情脉脉,还会等你。zookeeper相对无情一点,你关掉服务,他就会把你清理掉。 + + + +### 服务消费者 + +启动orderZK80 可以看到zookeeper上面有 新启动的服务消费者 + +![1591151710444](../media/pictures/SpringCloud.assets/1591151710444.png) + + + +经过测试 可以调用服务消费者 + +![1591151860745](../media/pictures/SpringCloud.assets/1591151860745.png) + +这里没有讲zookeeper部署集群,部署集群其实在后面加一个逗号,然后写另一个zookeeper就好。 + +主要的是后面的技术。 + + + +## Consul服务注册与发现 + +学到这里学一下 docker 看一下 部署是否方便许多 + +这里跳过啦,具体项目中如果用到的话,再学。 + +https://www.bilibili.com/video/BV18E411x7eT?p=32 + + + +### 三个注册中心异同点: + +CAP + C:Consistency(强一致性) 数据必须一致,不一致就会报错 + A:Availability(可用性) + P:Partition tolerance(分区容错性) 这个是一定要满足的一个 + CAP理论关注粒度是数据,而不是整体系统设计 +经典CAP图 + AP(Eureka) 好死不如赖活着。只要可以用,出一点错没关系。 + CP(Zookeeper/Consul) 测试发现Zookeeper,心跳如果找不到,马上剔除。 + + + +老师举例子说的,一般系统先要保证可用,也就是先保证A,然后再用其他技术,什么柔性事务什么的,恢复数据不一致的问题。 + + + +![1591689508621](../media/pictures/SpringCloud.assets/1591689508621.png) + + + +![1591689589925](../media/pictures/SpringCloud.assets/1591689589925.png) + + + +## Ribbon负载均衡服务调用 + +### 概述 + +这个负载均衡是进程内负载均衡。Nginx是服务器端负载均衡。 + + + +意思是什么呢? + +就是说,比如你要进医院,医院为了避免人多,弄了10个分院,Nginx就干这个事情,他先把人按照一定比例分到十个医院,负载均衡。 + +而进去医院以后,每个医院人还是很多,还需要根据科室进行负载均衡(比如口腔科十个医生,怎么分开),每个科室怎么分,这就是Ribbon干的事情。 + + + +#### 一句话就是 + +负载均衡+RestTemplate调用 + + + +### Ribbon负载均衡演示 + +使用的时候 不用引入依赖。因为Eureka里面集成了ribbon。 + + + +打开order80,看导入的依赖,发现Eureka原来的里面已经继承了ribbon + +![1591944008562](../media/pictures/SpringCloud.assets/1591944008562.png) + + + +在order80的controller中加了接口,调用就是这个样子的: + +![1591945814431](../media/pictures/SpringCloud.assets/1591945814431.png) + + + +### Ribbon核心组件IRuleige + +#### IRule 有七个 + +系统默认的是轮询的 + + + +根据特定算法中从服务列表中选择一个要访问的服务 + com.netflix.loadbalancer.RoundRobinRule + 轮询 + com.netflix.loadbalancer.RandomRule + 随机 + com.netflix.loadbalancer.RetryRule + 先按照RoundRobinRule的策略获取服务,如果获取服务失败则再指定时间内重试,获取可用服务 + WeightResponseTimeRule + 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大 + BestAvailableRule + 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 + AvailabilityFilterRule + 先过滤掉故障实例,再选择并发较小的实例 + ZoneAvoidanceRule + 默认规则,复合判断server所在区域的性能和server的可用性选择服务器 + + + +#### 替换 + +在替换的时候,有一个需要注意的: + +就是不能放在@ComponentScan注解可以扫描的地方 + + + +也就是需要在外边新建立一个包,就是这个样子的。 + +![1591948389606](../media/pictures/SpringCloud.assets/1591948389606.png) + + + +```java +package com.cskaoyan.myruler; + +import com.netflix.loadbalancer.IRule; +import com.netflix.loadbalancer.RandomRule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Steve + * @date 2020/6/12-15:59 + */ +@Configuration +public class MySelfRule { + + @Bean + public IRule myRule(){ + return new RandomRule(); //定义为随机 + } +} +``` + +将轮询改成随机,同时在主启动类上面加注解: + +```java +@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class) +``` + + + +```java +package com.cskaoyan.springcloud; + +import com.cskaoyan.myruler.MySelfRule; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.eureka.EnableEurekaClient; +import org.springframework.cloud.netflix.ribbon.RibbonClient; + +@EnableEurekaClient +@SpringBootApplication +@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class) +public class OrderMain80 { + public static void main(String[] args) { + SpringApplication.run(OrderMain80.class, args); + } +} +``` + + + +这样的话 测试 结果就是随机的: + +![1591955254185](../media/pictures/SpringCloud.assets/1591955254185.png) + + + +### Ribbon负载均衡算法 + +#### 原理 + +![1591955937766](../media/pictures/SpringCloud.assets/1591955937766.png) + + + +负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启后rest接口技术从1开始。 + +```java +//假如现在有两台进行负载均衡 具体请求那一台? 通过下面算出来的 + +1 % 2 = 1 ----> index = 1 list.get(incdex); +2 % 2 = 1 ----> index = 0 list.get(incdex); +3 % 2 = 1 ----> index = 1 list.get(incdex); +``` + + + +#### 源码 + +![1592186565880](../media/pictures/SpringCloud.assets/1592186565880.png) + +这个类里面有基本的是基本的轮询 + +```java +/* + * + * Copyright 2013 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.netflix.loadbalancer; + +import com.netflix.client.config.IClientConfig; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * The most well known and basic load balancing strategy, i.e. Round Robin Rule. + * + * @author stonse + * @author Nikos Michalakis + * + */ +public class RoundRobinRule extends AbstractLoadBalancerRule { + + private AtomicInteger nextServerCyclicCounter; + private static final boolean AVAILABLE_ONLY_SERVERS = true; + private static final boolean ALL_SERVERS = false; + + private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class); + + public RoundRobinRule() { + nextServerCyclicCounter = new AtomicInteger(0); + } + + public RoundRobinRule(ILoadBalancer lb) { + this(); + setLoadBalancer(lb); + } + + public Server choose(ILoadBalancer lb, Object key) { + if (lb == null) { + log.warn("no load balancer"); + return null; + } + + Server server = null; + int count = 0; + while (server == null && count++ < 10) { + List reachableServers = lb.getReachableServers(); + List allServers = lb.getAllServers(); + int upCount = reachableServers.size(); + int serverCount = allServers.size(); + + if ((upCount == 0) || (serverCount == 0)) { + log.warn("No up servers available from load balancer: " + lb); + return null; + } + + int nextServerIndex = incrementAndGetModulo(serverCount); + server = allServers.get(nextServerIndex); + + if (server == null) { + /* Transient. */ + Thread.yield(); + continue; + } + + if (server.isAlive() && (server.isReadyToServe())) { + return (server); + } + + // Next. + server = null; + } + + if (count >= 10) { + log.warn("No available alive servers after 10 tries from load balancer: " + + lb); + } + return server; + } + + /** + * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}. + * + * @param modulo The modulo to bound the value of the counter. + * @return The next value. + */ + private int incrementAndGetModulo(int modulo) { + for (;;) { + int current = nextServerCyclicCounter.get(); + int next = (current + 1) % modulo; + if (nextServerCyclicCounter.compareAndSet(current, next)) + return next; + } + } + + @Override + public Server choose(Object key) { + return choose(getLoadBalancer(), key); + } + + @Override + public void initWithNiwsConfig(IClientConfig clientConfig) { + } +} + +``` + + + +源码里面: + +```java +if ((upCount == 0) || (serverCount == 0)) { + log.warn("No up servers available from load balancer: " + lb); + return null; +} +``` + +upCound 这里指的就是 Eureka 网页上面的,只有这里将服务注册进容器里面,才会有后续的2操作。 + +![1592187031257](../media/pictures/SpringCloud.assets/1592187031257.png) + + + +```java +/** + * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}. + * + * @param modulo The modulo to bound the value of the counter. + * @return The next value. + */ +private int incrementAndGetModulo(int modulo) { + for (;;) { + int current = nextServerCyclicCounter.get(); + int next = (current + 1) % modulo; + if (nextServerCyclicCounter.compareAndSet(current, next)) //比较并交换,自旋锁 CAS + return next; + } +} +``` + + + +#### 手写一个负载算法 + +原理 + JUC (CAS + 自旋锁的复习) + + + +首先去掉原来的order80 config里面的@LoadBalance的 这个注解,避免这个注解影响自己写的负载, + + + +**在MyLB 类上面加一个注解 @component** + +1、@controller 控制器(注入服务) + +2、@service 服务(注入dao) + +3、@repository dao(实现dao访问) + +4、@component (把普通pojo实例化到spring容器中,相当于配置文件中的) + +参考: https://www.cnblogs.com/lyjing/p/8427832.html + + + +## OpenFeign服务接口调用 + +### 概况 + +#### 是什么 + +Feign是一个声明式的Web服务客户端,让编写Web服务客户端变得非常容易,**只需创建一个接口并在接口上申明注解**。 + + + + + +![1592874931363](../media/pictures/SpringCloud.assets/1592874931363.png) + + + + + +### OpenFeign使用步骤 + +#### 接口+注解 + +微服务调用接口+@FeignClient + + + +#### 步骤 + +pom->yml->主启动->业务类->测试 + + + +#### 小总结 + +![1592880224156](../media/pictures/SpringCloud.assets/1592880224156.png) + + + +### OpenFeign超时控制 + +OpenFeign默认超时控制是1秒钟,如果在这之内请求不到就会报请求超时。 + +![1592882064655](../media/pictures/SpringCloud.assets/1592882064655.png) + + + +为了使OpenFeign默认时间长一点,需要在application.yml 中添加配置 + +```yml +#设置feign客户端超时时间(OpenFeign默认支持ribbon) +ribbon: + #指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的实际 + ReadTimeout: 5000 + #指的是建立连接后从服务器读取到可用资源所用的时间 + ConnectTimeout: 5000 +``` + +这样的话就可以访问的到啦 + +![1592882457545](../media/pictures/SpringCloud.assets/1592882457545.png) + +### OpenFeign日志打印功能 + +#### 日志级别 + +![1592882971685](../media/pictures/SpringCloud.assets/1592882971685.png) + + + +注意这里一定是类对应的包,不用自己写,复制 + +```yml +logging: + level: + #feign日志以什么级别监控那个接口 以dubug的形式打印full全日志 + com.cskaoyan.springcloud.service.PaymentFeignService: debug +``` + + + +## Hystrix断路器(服务降级) + +发音:海思拽克斯 + +阳哥学习三板斧:理论 + 实操 + 小总结 + +### 概述 + +#### 分布式系统所面临的问题 + +服务雪崩 + +![1592891587067](../media/pictures/SpringCloud.assets/1592891587067.png) + + + +#### 是什么 + +基本介绍 + +![1592891693564](../media/pictures/SpringCloud.assets/1592891693564.png) + + + +### Hystrix重要概念 + +![1592989813112](../media/pictures/SpringCloud.assets/1592989813112.png) + + + +#### 服务降级 fallback + +@PathVariable是spring3.0的一个新功能:接收请求路径中占位符的值,代码中用到这个注解 + + + +##### 服务降级指的是 + +服务器忙,请稍后重试,不让客户等待,并返回一个友好界面 + + + +##### 通常会出现降级的几种情况 + +- 程序运行异常 +- 超时 +- 服务熔断触发服务降级 +- 线程池/信号已满 + + + +#### 服务熔断 break + +类似于保险丝,当达到最大访问后,直接拒绝访问,拉闸限电,返回友好提示 + + + +#### 服务限流 flowlimit + +秒杀高并发操作,严禁一窝蜂过来拥挤,等待排队,一秒N个,有序进行。 + + + +### Hystrix案例 + +#### 构建 cloud-provider-hystrix-payment8001 + +pom--yml--主启动--业务类--正常测试 + + + +##### 注意 + +还有一个,Hystrix里面的原理好像是利用tomcat的多线程,如果请求很多事是会消耗完tomcat里面的线程的。 + + + +#### 高并发测试 + +用 Jmeter直接弄20000个请求打payment8001,看两个方法怎么样。 + +测试结果是,请求确实慢了很多。 + + + +这里还有涉及到Jmeter的下载,安装,使用等。在笔记微服务秒杀里面。 + +#### 故障现象和导致原因 + +8001同一个层次的其他服务被困死,因为tomcat线程池里面的工作线程已经被挤占完毕。 + +80此时调用8001,客户端访问缓慢,转圈圈。 + + + +#### 上诉结论 + +正因为有上诉故障或不佳表现,才有我们的降级/容错/限流等技术诞生 + + + +#### 如何解决?解决要求 + +![1593306118196](../media/pictures/SpringCloud.assets/1593306118196.png) + + + +#### 服务降级 + + + +#### 服务熔断 + + + +#### 服务限流 + + + +### Hystrix工作流程 + + + +### 服务监控Hystrix Dashboard + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# 纯洁的微笑 + +## Spring Cloud 学习资料汇总 + + 2016/12/30 + +收集 Spring Cloud 相关的学习资料 + +> 学习 Spring Cloud 首先需要了解 Spring Boot,不了解 Spring Boot 的同学戳这里[Spring Boot学习资料汇总](http://www.ityouknow.com/springboot/2015/12/30/springboot-collect.html) + +**重点推荐:Spring Cloud 中文索引** + +### 推荐博客 + +- [纯洁的微笑 Spring Cloud 系列文章](http://www.ityouknow.com/spring-cloud) +- [windmt一spring cloud](https://windmt.com/tags/Spring-Cloud/) +- [方志朋 Spring Cloud 专栏](http://blog.csdn.net/column/details/15197.html) +- [许进 跟我学 Spring Cloud](http://xujin.org/categories/跟我学Spring-Cloud/) +- [liaokailin的专栏 Spring Cloud](http://blog.csdn.net/liaokailin/article/category/6212338) +- [猿天地尹吉欢 Spring Cloud](http://cxytiandi.com/blog/detail/17470) +- [唐亚峰 Battcn 起来学SpringCloud](https://blog.battcn.com/categories/SpringCloud/) +- [yjclsx spring cloud之路](https://blog.csdn.net/column/details/24531.html) +- [aoho spring cloud](http://blueskykong.com/tags/Spring-Cloud) +- [江南一点雨 Spring Cloud](https://wangsong.blog.csdn.net/column/info/17373) + +### 开源 + +- [纯洁的微笑的 Spring Cloud 示例](https://github.com/ityouknow/spring-cloud-examples) +- [spring cloud + vue 全家桶实战,模拟商城,完整的购物流程](https://github.com/paascloud/paascloud-master) +- [PiggyMetrics-一个供个人处理财务的解决方案](https://github.com/sqshq/PiggyMetrics) +- [基于Spring Cloud Netflix的TCC柔性事务和EDA事件驱动示例](https://github.com/prontera/spring-cloud-rest-tcc) +- [方志朋 SpringCloudLearning](https://github.com/forezp/SpringCloudLearning) +- [一套基于springcloud + mybatis + vue全家桶](https://github.com/OptionalDay/spring-cloud-vue) +- [cloudE 基于spring cloud的分布式系统架构](https://github.com/vangao1989/cloudE) +- [shop spring cloud最佳实践项目实例](https://github.com/lrwinx/shop) +- [Cloud-Admin是国内首个基于Spring Cloud微服务化开发平台](https://gitee.com/minull/ace-security) +- [spring-boot-cloud综合练手项目](https://github.com/zhangxd1989/spring-boot-cloud) +- [基于Spring Cloud的在线考试系统](https://gitee.com/wells2333/spring-cloud-online-exam) +- [基于SpringCloud的微服务架构实战案例项目,以一个简单的购物流程为示例](https://github.com/backkoms/simplemall) +- [XxPay 使用Spring Cloud实现的聚合支付](https://gitee.com/jmdhappy/xxpay-master) +- [FCat项目基于 Angular 4 + Spring Cloud 的企业级基础功能框架](https://gitee.com/xfdm/FCat) +- [基于Spring Cloud、oAuth2.0开发基于Vue前后分离的开发平台](https://gitee.com/log4j/pig) + +### 网站 + +- [Spring Cloud 官网](http://projects.spring.io/spring-cloud/) +- [Spring Cloud 中国社区](http://springcloud.cn/) +- [Spring Cloud 中文网](https://springcloud.cc/) +- [网易云课堂 Spring Cloud 视频](http://study.163.com/courses-search?keyword=Spring Cloud) +- [Spring Cloud 参考指南- 英文版](https://projects.spring.io/spring-cloud/spring-cloud.html) +- [Nepxion](https://github.com/Nepxion/Aquarius) + +### 其它 + +- [Spring Boot 中文索引](https://github.com/ityouknow/awesome-spring-boot) +- [程序员导航网站](http://tooool.org/) +- [IT行业中文资源大全](https://github.com/ityouknow/awesome-list) + +**欢迎大家推荐更多的资料** + + + +## Spring Boot 学习资料汇总 + + 2015/12/30 + +收集 Spring Boot 相关的学习资料,[Spring Cloud点这里](http://www.ityouknow.com/springcloud/2016/12/30/springcloud-collect.html) + +**重点推荐:Spring Boot 中文索引** + +### 推荐博客 + +- [纯洁的微笑-Spring Boot系列文章](http://www.ityouknow.com/spring-boot.html) +- [林祥纤-从零开始学Spring Boot](http://412887952-qq-com.iteye.com/category/356333) +- [Mkyong-Spring Boot教程(国外)](http://www.mkyong.com/tutorials/spring-boot-tutorials/) +- [baeldung-Spring Boot教程(国外)](https://www.baeldung.com/spring-boot) +- [liaokailin的专栏-Spring Boot实战](http://blog.csdn.net/liaokailin/article/category/5765237) +- [catoop的专栏-Spring Boot 学习](http://blog.csdn.net/column/details/spring-boot.html) +- [方志朋-SpringBoot 非官方教程](http://blog.csdn.net/column/details/15397.html) +- [嘟嘟-Spring-Boot干货系列](http://tengj.top/categories/Spring-Boot干货系列/) +- [小柒-SpringBoot开发案例](https://blog.52itstyle.com/category/springBoot/) +- [江南一点雨-关于Spring Boot](http://blog.csdn.net/column/details/13987.html) +- [天码营-Spring Boot](https://www.tianmaying.com/tutorials/tag/Springboot) +- [猿天地-Spring Boot](http://cxytiandi.com/blog/detail/17437) +- [刘冬的博客-Spring Boot](http://www.cnblogs.com/GoodHelper/tag/spring boot/default.html) +- [唐亚峰 Battcn-Spring Boot](https://blog.battcn.com/categories/SpringBoot/) +- [sylvanassun-Spring Boot](https://sylvanassun.github.io/categories/后端/Java/Spring-Boot/) +- [dalaoyang-Spring Boot](https://www.dalaoyang.cn/tag/springboot/) + +### 开源 + +- [纯洁的微笑 Spring Boot 示例](https://github.com/ityouknow/spring-boot-examples) +- [Spring Boot 官方示例](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples) +- [Spring Boot开源软件 云收藏](https://github.com/cloudfavorites/favorites-web) +- [Docker+SpringBoot+Mybatis+thymeleaf等技术实现的Java博客系统](https://github.com/ZHENFENG13/My-Blog) +- [Spring boot & Shiro 权限管理系统](https://github.com/wuyouzhuguli/FEBS) +- [Spring Boot实现支付服务:支付宝,微信…](https://gitee.com/52itstyle/spring-boot-pay) +- [Spring Boot后台商城 h5 小程序](https://gitee.com/JiaGou-XiaoGe/webappchat) +- [基于Spring Boot响应式文件浏览管理器](https://gitee.com/alexyang/spring-boot-filemanager) +- [Spring Boot开源博客](https://github.com/Raysmond/SpringBlog) +- [邮件发送服务多种实现,队列,线程定时任务](https://gitee.com/52itstyle/spring-boot-mail) +- [Spring Boot视频展示项目](https://github.com/ChinaSilence/any-video) +- [Spring Boot项目实践总结](https://github.com/timebusker/spring-boot) +- [Vue+SpringBoot实现的多用户博客管理平台](https://github.com/lenve/VBlog) +- [Vue+SpringBoot实现的人力资源管理系统](https://github.com/lenve/vhr) +- [hsweb企业后台管理系统基础框架](https://github.com/hs-web/hsweb-framework) +- [一个基于spring boot 实现的股票指数💹爬虫](https://github.com/kingschan1204/istock) +- [KKFileView-SpringBoot实现在线预览](https://gitee.com/kekingcn/file-online-preview) +- [boot-websocket-log-SpringBoot实现日志WEB输出](https://gitee.com/kailing/boot-websocket-log) +- [SpringBoot+MyBatis+A pacheShiro+Ehcahe基础平台](https://gitee.com/lcg0124/bootdo) +- [leelance Spring Boot各种示例](https://github.com/leelance/spring-boot-all) +- [一个基于Spring Boot & MyBatis的种子项目,用于快速构建中小型API、RESTful API项目](https://github.com/lihengming/spring-boot-api-project-seed) +- [JWT (Json Web Token) with Spring Security and Spring Boot 2](https://github.com/szerhusenBC/jwt-spring-security-demo) +- [基于Spring-boot和bootstrap搭建的商城系统](https://github.com/vito16/shop) +- [Deployment scripts & config for Sock Shop](https://github.com/microservices-demo/microservices-demo) +- [Spring Boot 开源博客-DBlog](https://gitee.com/yadong.zhang/DBlog) +- [Spring Boot 实现的简易社区](https://github.com/ChinaLHR/JavaQuarkBBS) +- [springboot+shiro+jwt 开源项目](https://gitee.com/tomsun28/bootshiro) +- [Guns-基于SpringBoot的后台管理系统](https://github.com/stylefeng/Guns) +- [halo-基于SpringBoot的博客系统](https://github.com/ruibaby/halo) +- [zhudyos/duic Distributed configuration center(分布式配置中心):new:](https://github.com/zhudyos/duic) +- [Spring Boot后端 + Vue管理员前端 + 微信小程序用户前端](https://github.com/linlinjava/litemall) +- [mall-SpringBoot+MyBatis 电商系统](https://github.com/macrozheng/mall) +- [基于Spring Boot2.0微服务脚手架](https://github.com/Senssic/sc-whorl) + +### 网站 + +- [云收藏](http://favorites.ren/lookAround) +- [Spring boot 官网](http://projects.spring.io/spring-boot/) +- [Spring Boot 参考指南- 英文版](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) +- [网易云课堂Spring Boot视频](http://study.163.com/courses-search?keyword=Spring Boot) +- [慕课网Spring Boot视频](https://www.imooc.com/search/?words=spring boot) +- [开源书籍-微服务:从设计到部署](https://github.com/oopsguy/microservices-from-design-to-deployment-chinese) + + + + + +## SpringCloud(一):大话Spring Cloud + +研究了一段时间Spring Boot了准备向Spring Cloud进发,公司架构和项目也全面拥抱了Spring Cloud。在使用了一段时间后发现Spring Cloud从技术架构上降低了对大型系统构建的要求,使我们以非常低的成本(技术或者硬件)搭建一套高效、分布式、容错的平台,但Spring Cloud也不是没有缺点,小型独立的项目不适合使用。 + +### Spring Cloud是什么鬼? + +Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。 + +微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元,springcloud就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多,springcloud做为大管家需要管理好这些微服务,自然需要很多小弟来帮忙。 + +主要的小弟有:Spring Cloud Config、Spring Cloud Netflix(Eureka、Hystrix、Zuul、Archaius…)、Spring Cloud Bus、Spring Cloud for Cloud Foundry、Spring Cloud Cluster、Spring Cloud Consul、Spring Cloud Security、Spring Cloud Sleuth、Spring Cloud Data Flow、Spring Cloud Stream、Spring Cloud Task、Spring Cloud Zookeeper、Spring Cloud Connectors、Spring Cloud Starters、Spring Cloud CLI,每个小弟身怀独门绝技武功高强下面来做一一介绍。 + +### 核心成员 + +#### Spring Cloud Netflix + +这可是个大boss,地位仅次于老大,老大各项服务依赖与它,与各种Netflix OSS组件集成,组成微服务的核心,它的小弟主要有Eureka, Hystrix, Zuul, Archaius… 太多了 + +**Netflix Eureka** + +服务中心,云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。这个可是springcloud最牛鼻的小弟,服务中心,任何小弟需要其它小弟支持什么都需要从这里来拿,同样的你有什么独门武功的都赶紧过报道,方便以后其它小弟来调用;它的好处是你不需要直接找各种什么小弟支持,只需要到服务中心来领取,也不需要知道提供支持的其它小弟在哪里,还是几个小弟来支持的,反正拿来用就行,服务中心来保证稳定性和质量。 + +**Netflix Hystrix** + +熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。比如突然某个小弟生病了,但是你还需要它的支持,然后调用之后它半天没有响应,你却不知道,一直在等等这个响应;有可能别的小弟也正在调用你的武功绝技,那么当请求多之后,就会发生严重的阻塞影响老大的整体计划。这个时候Hystrix就派上用场了,当Hystrix发现某个小弟不在状态不稳定立马马上让它下线,让其它小弟来顶上来,或者给你说不用等了这个小弟今天肯定不行,该干嘛赶紧干嘛去别在这排队了。 + +**Netflix Zuul** + +Zuul 是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web 网站后端所有请求的前门。当其它门派来找大哥办事的时候一定要先经过zuul,看下有没有带刀子什么的给拦截回去,或者是需要找那个小弟的直接给带过去。 + +**Netflix Archaius** + +配置管理API,包含一系列配置管理API,提供动态类型化属性、线程安全配置操作、轮询框架、回调机制等功能。可以实现动态获取配置, 原理是每隔60s(默认,可配置)从配置源读取一次内容,这样修改了配置文件后不需要重启服务就可以使修改后的内容生效,前提使用archaius的API来读取。 + +#### Spring Cloud Config + +俗称的配置中心,配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储、Git以及Subversion。就是以后大家武器、枪火什么的东西都集中放到一起,别随便自己带,方便以后统一管理、升级装备。 + +#### Spring Cloud Bus + +事件、消息总线,用于在集群(例如,配置变化事件)中传播状态变化,可与Spring Cloud Config联合实现热部署。相当于水浒传中日行八百里的神行太保戴宗,确保各个小弟之间消息保持畅通。 + +#### Spring Cloud for Cloud Foundry + +Cloud Foundry是VMware推出的业界第一个开源PaaS云平台,它支持多种框架、语言、运行时环境、云平台及应用服务,使开发人员能够在几秒钟内进行应用程序的部署和扩展,无需担心任何基础架构的问题 + +其实就是与CloudFoundry进行集成的一套解决方案,抱了Cloud Foundry的大腿。 + +#### Spring Cloud Cluster + +Spring Cloud Cluster将取代Spring Integration。提供在分布式系统中的集群所需要的基础功能支持,如:选举、集群的状态一致性、全局锁、tokens等常见状态模式的抽象和实现。 + +如果把不同的帮派组织成统一的整体,Spring Cloud Cluster已经帮你提供了很多方便组织成统一的工具。 + +#### Spring Cloud Consul + +Consul 是一个支持多数据中心分布式高可用的服务发现和配置共享的服务软件,由 HashiCorp 公司用 Go 语言开发, 基于 Mozilla Public License 2.0 的协议进行开源. Consul 支持健康检查,并允许 HTTP 和 DNS 协议调用 API 存储键值对. + +Spring Cloud Consul 封装了Consul操作,consul是一个服务发现与配置工具,与Docker容器可以无缝集成。 + +### 其它小弟 + +**Spring Cloud Security** + +基于spring security的安全工具包,为你的应用程序添加安全控制。这个小弟很牛鼻专门负责整个帮派的安全问题,设置不同的门派访问特定的资源,不能把秘籍葵花宝典泄漏了。 + +**Spring Cloud Sleuth** + +日志收集工具包,封装了Dapper和log-based追踪以及Zipkin和HTrace操作,为SpringCloud应用实现了一种分布式追踪解决方案。 + +**Spring Cloud Data Flow** + +- Data flow 是一个用于开发和执行大范围数据处理其模式包括ETL,批量运算和持续运算的统一编程模型和托管服务。 +- 对于在现代运行环境中可组合的微服务程序来说,Spring Cloud data flow是一个原生云可编配的服务。使用Spring Cloud data flow,开发者可以为像数据抽取,实时分析,和数据导入/导出这种常见用例创建和编配数据通道 (data pipelines)。 +- Spring Cloud data flow 是基于原生云对 spring XD的重新设计,该项目目标是简化大数据应用的开发。Spring XD 的流处理和批处理模块的重构分别是基于 Spring Boot的stream 和 task/batch 的微服务程序。这些程序现在都是自动部署单元而且他们原生的支持像 Cloud Foundry、Apache YARN、Apache Mesos和Kubernetes 等现代运行环境。 +- Spring Cloud data flow 为基于微服务的分布式流处理和批处理数据通道提供了一系列模型和最佳实践。 + +**Spring Cloud Stream** + +Spring Cloud Stream是创建消息驱动微服务应用的框架。Spring Cloud Stream是基于Spring Boot创建,用来建立单独的/工业级spring应用,使用spring integration提供与消息代理之间的连接。数据流操作开发包,封装了与Redis,Rabbit、Kafka等发送接收消息。 + +一个业务会牵扯到多个任务,任务之间是通过事件触发的,这就是Spring Cloud stream要干的事了 + +**Spring Cloud Task** + +Spring Cloud Task 主要解决短命微服务的任务管理,任务调度的工作,比如说某些定时任务晚上就跑一次,或者某项数据分析临时就跑几次。 + +**Spring Cloud Zookeeper** + +ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。ZooKeeper的目标就是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。 + +操作Zookeeper的工具包,用于使用zookeeper方式的服务发现和配置管理,抱了Zookeeper的大腿。 + +**Spring Cloud Connectors** + +Spring Cloud Connectors 简化了连接到服务的过程和从云平台获取操作的过程,有很强的扩展性,可以利用Spring Cloud Connectors来构建你自己的云平台。 + +便于云端应用程序在各种PaaS平台连接到后端,如:数据库和消息代理服务。 + +**Spring Cloud Starters** + +Spring Boot式的启动项目,为Spring Cloud提供开箱即用的依赖管理。 + +**Spring Cloud CLI** + +基于 Spring Boot CLI,可以让你以命令行方式快速建立云组件。 + +### 和Spring Boot 是什么关系 + +Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个个体,Spring Cloud是关注全局的服务治理框架;Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。 + +Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。 + +> spring -> spring boot > Spring Cloud 这样的关系。 + +### Spring Cloud的优势 + +微服务的框架那么多比如:dubbo、Kubernetes,为什么就要使用Spring Cloud的呢? + +- 产出于spring大家族,spring在企业级开发框架中无人能敌,来头很大,可以保证后续的更新、完善。比如dubbo现在就差不多死了 +- 有Spring Boot 这个独立干将可以省很多事,大大小小的活Spring Boot都搞的挺不错。 +- 作为一个微服务治理的大家伙,考虑的很全面,几乎服务治理的方方面面都考虑到了,方便开发开箱即用。 +- Spring Cloud 活跃度很高,教程很丰富,遇到问题很容易找到解决方案 +- 轻轻松松几行代码就完成了熔断、均衡负载、服务中心的各种平台功能 + +Spring Cloud对于中小型互联网公司来说是一种福音,因为这类公司往往没有实力或者没有足够的资金投入去开发自己的分布式系统基础设施,使用Spring Cloud一站式解决方案能在从容应对业务发展的同时大大减少开发成本。同时,随着近几年微服务架构和Docker容器概念的火爆,也会让Spring Cloud在未来越来越“云”化的软件开发风格中立有一席之地,尤其是在目前五花八门的分布式解决方案中提供了标准化的、全站式的技术方案,意义可能会堪比当前Servlet规范的诞生,有效推进服务端软件系统技术水平的进步。 + + + +## SpringCloud(二):注册中心Eureka + +Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现。也是springcloud体系中最重要最核心的组件之一。 + +### 背景介绍 + +#### 服务中心 + +服务中心又称注册中心,管理各种服务功能包括服务的注册、发现、熔断、负载、降级等,比如dubbo admin后台的各种功能。 + +有了服务中心调用关系会有什么变化,画几个简图来帮忙理解 + +项目A调用项目B + +正常调用项目A请求项目B + +![img](../media/pictures/SpringCloud.assets/ab.jpg) + +有了服务中心之后,任何一个服务都不能直接去掉用,都需要通过服务中心来调用 + +![img](../media/pictures/SpringCloud.assets/a2b.jpg) + +项目A调用项目B,项目B在调用项目C + +![img](../media/pictures/SpringCloud.assets/abc.jpg) + +这时候调用的步骤就会为两步:第一步,项目A首先从服务中心请求项目B服务器,然后项目B在从服务中心请求项目C服务。 + +![img](../media/pictures/SpringCloud.assets/a2b2c.jpg) + +上面的项目只是两三个相互之间的简单调用,但是如果项目超过20个30个呢,在15年底的时候我司分布式的项目就达到了二十几个,画一张图来描述几十个项目之间的相互调用关系全是线条,任何其中的一个项目改动,就会牵连好几个项目跟着重启,巨麻烦而且容易出错。通过服务中心来获取服务你不需要关注你调用的项目IP地址,由几台服务器组成,每次直接去服务中心获取可以使用的服务去调用既可。 + +由于各种服务都注册到了服务中心,就有了去做很多高级功能条件。比如几台服务提供相同服务来做均衡负载;监控服务器调用成功率来做熔断,移除服务列表中的故障点;监控服务调用时间来对不同的服务器设置不同的权重等等。 + +说Eureka之前我先八卦一下Netflix + +#### Netflix + +以下介绍来自于百度百科: + +> Netflix是一家美国公司,在美国、加拿大提供互联网随选流媒体播放,定制DVD、蓝光光碟在线出租业务。该公司成立于1997年,总部位于加利福尼亚州洛斯盖图,1999年开始订阅服务。2009年,该公司可提供多达10万部DVD电影,并有1千万的订户。2007年2月25日,Netflix宣布已经售出第10亿份DVD。HIS一份报告中表示,2011年Netflix网络电影销量占据美国用户在线电影总销量的45%。 + +我第一次看到这个单词的时候,是在各种美剧或者电影的开头,Netflix拍摄的代表性的美剧有《纸牌屋》、《毒枭》、《怪奇物语》。后来研究springcloud的时候发现了Netflix公司,就在想它们是不是同一家公司,经过核对github上面邮件后缀判定确实是同一家公司,其实springcloud的微服务就基于Netflix公司的开源产品来做的。 + +Netflix的开源框架组件已经在Netflix的大规模分布式微服务环境中经过多年的生产实战验证,正逐步被社区接受为构造微服务框架的标准组件。Spring Cloud开源产品,主要是基于对Netflix开源组件的进一步封装,方便Spring开发人员构建微服务基础框架。对于一些打算构建微服务框架体系的公司来说,充分利用或参考借鉴Netflix的开源微服务组件(或Spring Cloud),在此基础上进行必要的企业定制,无疑是通向微服务架构的捷径。 + +#### Eureka + +按照官方介绍: + +> Eureka is a REST (Representational State Transfer) based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers. +> +> Eureka 是一个基于 REST 的服务,主要在 AWS 云中使用, 定位服务来进行中间层服务器的负载均衡和故障转移。 + +Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现。Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server,并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。 + +Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。 + +用一张图来认识以下: + +![img](../media/pictures/SpringCloud.assets/eureka-architecture-overview.png) + +上图简要描述了Eureka的基本架构,由3个角色组成: + +1、Eureka Server + +- 提供服务注册和发现 + +2、Service Provider + +- 服务提供方 +- 将自身服务注册到Eureka,从而使服务消费方能够找到 + +3、Service Consumer + +- 服务消费方 +- 从Eureka获取注册服务列表,从而能够消费服务 + +### 案例实践 + +#### Eureka Server + +spring cloud已经帮我实现了服务注册中心,我们只需要很简单的几个步骤就可以完成。 + +1、pom中添加依赖 + +```xml + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-starter-eureka-server + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +2、添加启动代码中添加`@EnableEurekaServer`注解 + +```java +@SpringBootApplication +@EnableEurekaServer +public class SpringCloudEurekaApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudEurekaApplication.class, args); + } +} +``` + +3、配置文件 + +在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,在`application.properties`添加以下配置: + +```properties +spring.application.name=spring-cloud-eureka + +server.port=8000 +eureka.client.register-with-eureka=false +eureka.client.fetch-registry=false + +eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/ +``` + +- `eureka.client.register-with-eureka` :表示是否将自己注册到Eureka Server,默认为true。 +- `eureka.client.fetch-registry` :表示是否从Eureka Server获取注册信息,默认为true。 +- `eureka.client.serviceUrl.defaultZone` :设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址。默认是http://localhost:8761/eureka ;多个地址可使用 , 分隔。 + +启动工程后,访问:http://localhost:8000/,可以看到下面的页面,其中还没有发现任何服务 + +![img](../media/pictures/SpringCloud.assets/eureka_start.jpg) + +### 集群 + +注册中心这么关键的服务,如果是单点话,遇到故障就是毁灭性的。在一个分布式系统中,服务注册中心是最重要的基础部分,理应随时处于可以提供服务的状态。为了维持其可用性,使用集群是很好的解决方案。Eureka通过互相注册的方式来实现高可用的部署,所以我们只需要将Eureke Server配置其他可用的serviceUrl就能实现高可用部署。 + +#### 双节点注册中心 + +首次我们尝试一下双节点的注册中心的搭建。 + +1、创建application-peer1.properties,作为peer1服务中心的配置,并将serviceUrl指向peer2 + +```properties +spring.application.name=spring-cloud-eureka +server.port=8000 +eureka.instance.hostname=peer1 + +eureka.client.serviceUrl.defaultZone=http://peer2:8001/eureka/ +``` + +2、创建application-peer2.properties,作为peer2服务中心的配置,并将serviceUrl指向peer1 + +```properties +spring.application.name=spring-cloud-eureka +server.port=8001 +eureka.instance.hostname=peer2 + +eureka.client.serviceUrl.defaultZone=http://peer1:8000/eureka/ +``` + +3、host转换 + +在hosts文件中加入如下配置 + +```properties +127.0.0.1 peer1 +127.0.0.1 peer2 +``` + +4、打包启动 + +依次执行下面命令 + +```properties +#打包 +mvn clean package +# 分别以peer1和peeer2 配置信息启动eureka +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 +``` + +依次启动完成后,浏览器输入:`http://localhost:8000/` 效果图如下: + +![img](../media/pictures/SpringCloud.assets/eureka-two.jpg) + +根据图可以看出peer1的注册中心DS Replicas已经有了peer2的相关配置信息,并且出现在available-replicas中。我们手动停止peer2来观察,发现peer2就会移动到unavailable-replicas一栏中,表示peer2不可用。 + +到此双节点的配置已经完成。 + +#### eureka集群使用 + +在生产中我们可能需要三台或者大于三台的注册中心来保证服务的稳定性,配置的原理其实都一样,将注册中心分别指向其它的注册中心。这里只介绍三台集群的配置情况,其实和双节点的注册中心类似,每台注册中心分别又指向其它两个节点即可,使用application.yml来配置。 + +application.yml配置详情如下: + +``` +--- +spring: + application: + name: spring-cloud-eureka + profiles: peer1 +server: + port: 8000 +eureka: + instance: + hostname: peer1 + client: + serviceUrl: + defaultZone: http://peer2:8001/eureka/,http://peer3:8002/eureka/ +--- +spring: + application: + name: spring-cloud-eureka + profiles: peer2 +server: + port: 8001 +eureka: + instance: + hostname: peer2 + client: + serviceUrl: + defaultZone: http://peer1:8000/eureka/,http://peer3:8002/eureka/ +--- +spring: + application: + name: spring-cloud-eureka + profiles: peer3 +server: + port: 8002 +eureka: + instance: + hostname: peer3 + client: + serviceUrl: + defaultZone: http://peer1:8000/eureka/,http://peer2:8001/eureka/ +``` + +分别以peer1、peer2、peer3的配置参数启动eureka注册中心。 + +``` +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer3 +``` + +依次启动完成后,浏览器输入:`http://localhost:8000/` 效果图如下: + +![img](../media/pictures/SpringCloud.assets/eureka-cluster.jpg) + +可以在peer1中看到了peer2、peer3的相关信息。至此eureka集群也已经完成了 + + + +## SpringCloud(三):服务提供与调用 + +上一篇文章我们介绍了eureka服务注册中心的搭建,这篇文章介绍一下如何使用eureka服务注册中心,搭建一个简单的服务端注册服务,客户端去调用服务使用的案例。 + +案例中有三个角色:服务注册中心、服务提供者、服务消费者,其中服务注册中心就是我们上一篇的eureka单机版启动既可,流程是首先启动注册中心,服务提供者生产服务并注册到服务中心中,消费者从服务中心中获取服务并执行。 + +### 服务提供 + +我们假设服务提供者有一个hello方法,可以根据传入的参数,提供输出“hello xxx,this is first messge”的服务 + +#### 1、pom包配置 + +创建一个springboot项目,pom.xml中添加如下配置: + +``` + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +#### 2、配置文件 + +application.properties配置如下: + +``` +spring.application.name=spring-cloud-producer +server.port=9000 +eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ +``` + +参数在上一篇都已经解释过,这里不多说。 + +#### 3、启动类 + +启动类中添加`@EnableDiscoveryClient`注解 + +``` +@SpringBootApplication +@EnableDiscoveryClient +public class ProducerApplication { + + public static void main(String[] args) { + SpringApplication.run(ProducerApplication.class, args); + } +} +``` + +#### 4、controller + +提供hello服务 + +``` +@RestController +public class HelloController { + + @RequestMapping("/hello") + public String index(@RequestParam String name) { + return "hello "+name+",this is first messge"; + } +} +``` + +添加`@EnableDiscoveryClient`注解后,项目就具有了服务注册的功能。启动工程后,就可以在注册中心的页面看到SPRING-CLOUD-PRODUCER服务。 + +![img](../media/pictures/SpringCloud.assets/eureka_server.png) + +到此服务提供者配置就完成了。 + +### 服务调用 + +#### 1、pom包配置 + +和服务提供者一致 + +``` + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +#### 2、配置文件 + +application.properties配置如下: + +``` +spring.application.name=spring-cloud-consumer +server.port=9001 +eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ +``` + +#### 3、启动类 + +启动类添加`@EnableDiscoveryClient`和`@EnableFeignClients`注解。 + +``` +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +public class ConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConsumerApplication.class, args); + } + +} +``` + +- `@EnableDiscoveryClient` :启用服务注册与发现 +- `@EnableFeignClients`:启用feign进行远程调用 + +> Feign是一个声明式Web Service客户端。使用Feign能让编写Web Service客户端更加简单, 它的使用方法是定义一个接口,然后在上面添加注解,同时也支持JAX-RS标准的注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。 + +#### 4、feign调用实现 + +``` +@FeignClient(name= "spring-cloud-producer") +public interface HelloRemote { + @RequestMapping(value = "/hello") + public String hello(@RequestParam(value = "name") String name); +} +``` + +- name:远程服务名,及spring.application.name配置的名称 + +此类中的方法和远程服务中contoller中的方法名和参数需保持一致。 + +#### 5、web层调用远程服务 + +将HelloRemote注入到controller层,像普通方法一样去调用即可。 + +``` +@RestController +public class ConsumerController { + + @Autowired + HelloRemote HelloRemote; + + @RequestMapping("/hello/{name}") + public String index(@PathVariable("name") String name) { + return HelloRemote.hello(name); + } + +} +``` + +到此,最简单的一个服务注册与调用的例子就完成了。 + +### 测试 + +#### 简单调用 + +依次启动spring-cloud-eureka、spring-cloud-producer、spring-cloud-consumer三个项目 + +先输入:`http://localhost:9000/hello?name=neo` 检查spring-cloud-producer服务是否正常 + +返回:`hello neo,this is first messge` + +说明spring-cloud-producer正常启动,提供的服务也正常。 + +浏览器中输入:`http://localhost:9001/hello/neo` + +返回:`hello neo,this is first messge` + +说明客户端已经成功的通过feign调用了远程服务hello,并且将结果返回到了浏览器。 + +#### 负载均衡 + +以上面spring-cloud-producer为例子修改,将其中的controller改动如下: + +``` +@RestController +public class HelloController { + + @RequestMapping("/hello") + public String index(@RequestParam String name) { + return "hello "+name+",this is producer 2 send first messge"; + } +} +``` + +在配置文件中改动端口: + +``` +spring.application.name=spring-cloud-producer +server.port=9003 +eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ +``` + +打包启动后,在eureka就会发现两个服务提供者,如下图: + +![img](../media/pictures/SpringCloud.assets/eureka_server2.png) + +然后在浏览器再次输入:`http://localhost:9001/hello/neo` 进行测试: + +第一次返回结果:`hello neo,this is first messge` + +第二次返回结果:`hello neo,this is producer 2 send first messge` + +不断的进行测试下去会发现两种结果交替出现,说明两个服务中心自动提供了服务均衡负载的功能。如果我们将服务提供者的数量在提高为N个,测试结果一样,请求会自动轮询到每个服务端来处理。 + + + +## SpringCloud(四):熔断器Hystrix + +说起springcloud熔断让我想起了去年股市中的熔断,多次痛的领悟,随意实施的熔断对整个系统的影响是灾难性的,好了接下来我们还是说正事。 + +### 熔断器 + +#### 雪崩效应 + +在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。 + +如果下图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。 + +![img](../media/pictures/SpringCloud.assets/hystrix-1.png) + +#### 熔断器(CircuitBreaker) + +熔断器的原理很简单,如同电力过载保护器。它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。 + +熔断器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。 熔断器开关相互转换的逻辑如下图: + +![img](../media/pictures/SpringCloud.assets/hystrix-2.png) + +熔断器就是保护服务高可用的最后一道防线。 + +#### Hystrix特性 + +**1.断路器机制** + +断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力. + + + +**2.Fallback** + +Fallback相当于是降级操作. 对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存. + +**3.资源隔离** + +在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池. 例如调用产品服务的Command放入A线程池, 调用账户服务的Command放入B线程池. 这样做的主要优点是运行环境被隔离开了. 这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响. 但是带来的代价就是维护多个线程池会对系统带来额外的性能开销. 如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用Hystrix的信号模式(Semaphores)来隔离资源. + + + +### Feign Hystrix +因为熔断只是作用在服务调用这一端,因此我们根据上一篇的示例代码只需要改动spring-cloud-consumer项目相关代码就可以。因为,Feign中已经依赖了Hystrix所以在maven配置上不用做任何改动。 + +#### 1、配置文件 + +application.properties添加这一条: + +``` +feign.hystrix.enabled=true +``` + +#### 2、创建回调类 + +创建HelloRemoteHystrix类继承与HelloRemote实现回调的方法 + +``` +@Component +public class HelloRemoteHystrix implements HelloRemote{ + + @Override + public String hello(@RequestParam(value = "name") String name) { + return "hello" +name+", this messge send failed "; + } +} +``` + +#### 3、添加fallback属性 + +在`HelloRemote`类添加指定fallback类,在服务熔断的时候返回fallback类中的内容。 + +``` +@FeignClient(name= "spring-cloud-producer",fallback = HelloRemoteHystrix.class) +public interface HelloRemote { + + @RequestMapping(value = "/hello") + public String hello(@RequestParam(value = "name") String name); + +} +``` + +改动点就这点,很简单吧。 + +#### 4、测试 + +那我们就来测试一下看看效果吧。 + +依次启动spring-cloud-eureka、spring-cloud-producer、spring-cloud-consumer三个项目。 + +浏览器中输入:`http://localhost:9001/hello/neo` + +返回:`hello neo,this is first messge` + +说明加入熔断相关信息后,不影响正常的访问。接下来我们手动停止spring-cloud-producer项目再次测试: + +浏览器中输入:`http://localhost:9001/hello/neo` + +返回:`hello neo, this messge send failed` + +根据返回结果说明熔断成功。 + +## SpringCloud(五):熔断监控Hystrix Dashboard和Turbine + +Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据。但是只使用Hystrix Dashboard的话, 你只能看到单个应用内的服务信息, 这明显不够. 我们需要一个工具能让我们汇总系统内多个服务的数据并显示到Hystrix Dashboard上, 这个工具就是Turbine. + +### Hystrix Dashboard + +我们在熔断示例项目spring-cloud-consumer-hystrix的基础上更改,重新命名为:spring-cloud-consumer-hystrix-dashboard。 + +#### 1、添加依赖 + +``` + + org.springframework.cloud + spring-cloud-starter-hystrix + + + org.springframework.cloud + spring-cloud-starter-hystrix-dashboard + + + org.springframework.boot + spring-boot-starter-actuator + +``` + +这三个包必须添加 + +#### 2、启动类 + +启动类添加启用Hystrix Dashboard和熔断器 + +``` +@SpringBootApplication +@EnableDiscoveryClient +@EnableFeignClients +@EnableHystrixDashboard +@EnableCircuitBreaker +public class ConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConsumerApplication.class, args); + } +} +``` + +#### 3、测试 + +启动工程后访问 http://localhost:9001/hystrix,将会看到如下界面: + +![img](../media/pictures/SpringCloud.assets/hystrix-dashboard-1.jpg) + +图中会有一些提示: + +> Cluster via Turbine (default cluster): http://turbine-hostname:port/turbine.stream +> Cluster via Turbine (custom cluster): http://turbine-hostname:port/turbine.stream?cluster=[clusterName] +> Single Hystrix App: http://hystrix-app:port/hystrix.stream + +大概意思就是如果查看默认集群使用第一个url,查看指定集群使用第二个url,单个应用的监控使用最后一个,我们暂时只演示单个应用的所以在输入框中输入: http://localhost:9001/hystrix.stream ,输入之后点击 monitor,进入页面。 + +如果没有请求会先显示`Loading ...`,访问http://localhost:9001/hystrix.stream 也会不断的显示ping。 + +请求服务http://localhost:9001/hello/neo,就可以看到监控的效果了,首先访问http://localhost:9001/hystrix.stream,显示如下: + +``` +ping: + +data: {"type":...} + +data: {"type":...} +``` + +说明已经返回了监控的各项结果 + +到监控页面就会显示如下图: + +![img](../media/pictures/SpringCloud.assets/hystrix-dashboard-2.jpg) + +其实就是http://localhost:9001/hystrix.stream返回结果的图形化显示,Hystrix Dashboard Wiki上详细说明了图上每个指标的含义,如下图: + +![img](../media/pictures/SpringCloud.assets/hystrix-dashboard-3.png) + +到此单个应用的熔断监控已经完成。 + +### Turbine + +在复杂的分布式系统中,相同服务的节点经常需要部署上百甚至上千个,很多时候,运维人员希望能够把相同服务的节点状态以一个整体集群的形式展现出来,这样可以更好的把握整个系统的状态。 为此,Netflix提供了一个开源项目(Turbine)来提供把多个hystrix.stream的内容聚合为一个数据源供Dashboard展示。 + +#### 1、添加依赖 + +``` + + + org.springframework.cloud + spring-cloud-starter-turbine + + + org.springframework.cloud + spring-cloud-netflix-turbine + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-hystrix-dashboard + + +``` + +#### 2、配置文件 + +``` +spring.application.name=hystrix-dashboard-turbine +server.port=8001 +turbine.appConfig=node01,node02 +turbine.aggregator.clusterConfig= default +turbine.clusterNameExpression= new String("default") + +eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ +``` + +- `turbine.appConfig` :配置Eureka中的serviceId列表,表明监控哪些服务 +- `turbine.aggregator.clusterConfig` :指定聚合哪些集群,多个使用”,”分割,默认为default。可使用`http://.../turbine.stream?cluster={clusterConfig之一}`访问 +- `turbine.clusterNameExpression` : 1. clusterNameExpression指定集群名称,默认表达式appName;此时:`turbine.aggregator.clusterConfig`需要配置想要监控的应用名称;2. 当clusterNameExpression: default时,`turbine.aggregator.clusterConfig`可以不写,因为默认就是default;3. 当clusterNameExpression: metadata[‘cluster’]时,假设想要监控的应用配置了`eureka.instance.metadata-map.cluster: ABC`,则需要配置,同时`turbine.aggregator.clusterConfig: ABC` + +#### 3、启动类 + +启动类添加`@EnableTurbine`,激活对Turbine的支持 + +``` +@SpringBootApplication +@EnableHystrixDashboard +@EnableTurbine +public class DashboardApplication { + + public static void main(String[] args) { + SpringApplication.run(DashboardApplication.class, args); + } + +} +``` + +到此Turbine(hystrix-dashboard-turbine)配置完成 + +#### 4、测试 + +在示例项目spring-cloud-consumer-hystrix基础上修改为两个服务的调用者spring-cloud-consumer-node1和spring-cloud-consumer-node2 + +spring-cloud-consumer-node1项目改动如下: application.properties文件内容 + +``` +spring.application.name=node01 +server.port=9001 +feign.hystrix.enabled=true + +eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ +``` + +spring-cloud-consumer-node2项目改动如下: application.properties文件内容 + +``` +spring.application.name=node02 +server.port=9002 +feign.hystrix.enabled=true + +eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ +``` + +HelloRemote类修改: + +``` +@FeignClient(name= "spring-cloud-producer2", fallback = HelloRemoteHystrix.class) +public interface HelloRemote { + + @RequestMapping(value = "/hello") + public String hello2(@RequestParam(value = "name") String name); + +} +``` + +对应的`HelloRemoteHystrix`和`ConsumerController`类跟随修改,具体查看代码 + +修改完毕后,依次启动spring-cloud-eureka、spring-cloud-consumer-node1、spring-cloud-consumer-node1、hystrix-dashboard-turbine(Turbine) + +打开eureka后台可以看到注册了三个服务: + +![img](../media/pictures/SpringCloud.assets/turbine-01.jpg) + +访问 http://localhost:8001/turbine.stream + +返回: + +``` +: ping +data: {"reportingHostsLast10Seconds":1,"name":"meta","type":"meta","timestamp":1494921985839} +``` + +并且会不断刷新以获取实时的监控数据,说明和单个的监控类似,返回监控项目的信息。进行图形化监控查看,输入:http://localhost:8001/hystrix,返回酷酷的小熊界面,输入: http://localhost:8001/turbine.stream,然后点击 Monitor Stream ,可以看到出现了俩个监控列表 + +![img](../media/pictures/SpringCloud.assets/turbine-02.jpg) + +## SpringCloud(六):配置中心git示例 + +随着线上项目变的日益庞大,每个项目都散落着各种配置文件,如果采用分布式的开发模式,需要的配置文件随着服务增加而不断增多。某一个基础服务信息变更,都会引起一系列的更新和重启,运维苦不堪言也容易出错。配置中心便是解决此类问题的灵丹妙药。 + +市面上开源的配置中心有很多,BAT每家都出过,360的QConf、淘宝的diamond、百度的disconf都是解决这类问题。国外也有很多开源的配置中心Apache Commons Configuration、owner、cfg4j等等。这些开源的软件以及解决方案都很优秀,但是我最钟爱的却是Spring Cloud Config,因为它功能全面强大,可以无缝的和spring体系相结合,够方便够简单颜值高我喜欢。 + +### Spring Cloud Config + +在我们了解spring cloud config之前,我可以想想一个配置中心提供的核心功能应该有什么 + +- 提供服务端和客户端支持 +- 集中管理各环境的配置文件 +- 配置文件修改之后,可以快速的生效 +- 可以进行版本管理 +- 支持大的并发查询 +- 支持各种语言 + +Spring Cloud Config可以完美的支持以上所有的需求。 + +Spring Cloud Config项目是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,client通过接口获取数据、并依据此数据初始化自己的应用。Spring cloud使用git或svn存放配置文件,默认情况下使用git,我们先以git为例做一套示例。 + +首先在github上面创建了一个文件夹config-repo用来存放配置文件,为了模拟生产环境,我们创建以下三个配置文件: + +``` +// 开发环境 +neo-config-dev.properties +// 测试环境 +neo-config-test.properties +// 生产环境 +neo-config-pro.properties +``` + +每个配置文件中都写一个属性neo.hello,属性值分别是 hello im dev/test/pro 。下面我们开始配置server端 + +### server 端 + +#### 1、添加依赖 + +``` + + + org.springframework.cloud + spring-cloud-config-server + + +``` + +只需要加入spring-cloud-config-server包引用既可。 + +#### 2、配置文件 + +``` +server: + port: 8001 +spring: + application: + name: spring-cloud-config-server + cloud: + config: + server: + git: + uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址 + search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。 + username: # git仓库的账号 + password: # git仓库的密码 +``` + +Spring Cloud Config也提供本地存储配置的方式。我们只需要设置属性`spring.profiles.active=native`,Config Server会默认从应用的`src/main/resource`目录下检索配置文件。也可以通过`spring.cloud.config.server.native.searchLocations=file:E:/properties/`属性来指定配置文件的位置。虽然Spring Cloud Config提供了这样的功能,但是为了支持更好的管理内容和版本控制的功能,还是推荐使用git的方式。 + +#### 3、启动类 + +启动类添加`@EnableConfigServer`,激活对配置中心的支持 + +``` +@EnableConfigServer +@SpringBootApplication +public class ConfigServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigServerApplication.class, args); + } +} +``` + +到此server端相关配置已经完成 + +#### 4、测试 + +首先我们先要测试server端是否可以读取到github上面的配置信息,直接访问:`http://localhost:8001/neo-config/dev` + +返回信息如下: + +``` +{ + "name": "neo-config", + "profiles": [ + "dev" + ], + "label": null, + "version": null, + "state": null, + "propertySources": [ + { + "name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties", + "source": { + "neo.hello": "hello im dev" + } + } + ] +} +``` + +上述的返回的信息包含了配置文件的位置、版本、配置文件的名称以及配置文件中的具体内容,说明server端已经成功获取了git仓库的配置信息。 + +如果直接查看配置文件中的配置信息可访问:`http://localhost:8001/neo-config-dev.properties`,返回:`neo.hello: hello im dev` + +修改配置文件`neo-config-dev.properties`中配置信息为:`neo.hello=hello im dev update`,再次在浏览器访问`http://localhost:8001/neo-config-dev.properties`,返回:`neo.hello: hello im dev update`。说明server端会自动读取最新提交的内容 + +仓库中的配置文件会被转换成web接口,访问可以参照以下的规则: + +- /{application}/{profile}[/{label}] +- /{application}-{profile}.yml +- /{label}/{application}-{profile}.yml +- /{application}-{profile}.properties +- /{label}/{application}-{profile}.properties + +以neo-config-dev.properties为例子,它的application是neo-config,profile是dev。client会根据填写的参数来选择读取对应的配置。 + +### client 端 + +主要展示如何在业务项目中去获取server端的配置信息 + +#### 1、添加依赖 + +``` + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +引入spring-boot-starter-web包方便web测试 + +#### 2、配置文件 + +需要配置两个配置文件,application.properties和bootstrap.properties + +application.properties如下: + +``` +spring.application.name=spring-cloud-config-client +server.port=8002 +``` + +bootstrap.properties如下: + +``` +spring.cloud.config.name=neo-config +spring.cloud.config.profile=dev +spring.cloud.config.uri=http://localhost:8001/ +spring.cloud.config.label=master +``` + +- spring.application.name:对应{application}部分 +- spring.cloud.config.profile:对应{profile}部分 +- spring.cloud.config.label:对应git的分支。如果配置中心使用的是本地存储,则该参数无用 +- spring.cloud.config.uri:配置中心的具体地址 +- spring.cloud.config.discovery.service-id:指定配置中心的service-id,便于扩展为高可用配置集群。 + +> 特别注意:上面这些与spring-cloud相关的属性必须配置在bootstrap.properties中,config部分内容才能被正确加载。因为config的相关配置会先于application.properties,而bootstrap.properties的加载也是先于application.properties。 + +#### 3、启动类 + +启动类添加`@EnableConfigServer`,激活对配置中心的支持 + +``` +@SpringBootApplication +public class ConfigClientApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigClientApplication.class, args); + } +} +``` + +启动类只需要`@SpringBootApplication`注解就可以 + +#### 4、web测试 + +使用`@Value`注解来获取server端参数的值 + +``` +@RestController +class HelloController { + @Value("${neo.hello}") + private String hello; + + @RequestMapping("/hello") + public String from() { + return this.hello; + } +} +``` + +启动项目后访问:`http://localhost:8002/hello`,返回:`hello im dev update`说明已经正确的从server端获取到了参数。到此一个完整的服务端提供配置服务,客户端获取配置参数的例子就完成了。 + +我们在进行一些小实验,手动修改`neo-config-dev.properties`中配置信息为:`neo.hello=hello im dev update1`提交到github,再次在浏览器访问`http://localhost:8002/hello`,返回:`neo.hello: hello im dev update`,说明获取的信息还是旧的参数,这是为什么呢?因为springboot项目只有在启动的时候才会获取配置文件的值,修改github信息后,client端并没有在次去获取,所以导致这个问题。如何去解决这个问题呢?留到下一章我们在介绍。 + +## SpringCloud(七):配置中心svn示例和refresh + +上一篇[springcloud(六):配置中心git示例](http://www.ityouknow.com/springcloud/2017/05/22/springcloud-config-git.html)留了一个小问题,当重新修改配置文件提交后,客户端获取的仍然是修改前的信息,这个问题我们先放下,待会再讲。国内很多公司都使用的svn来做代码的版本控制,我们先介绍以下如何使用svn+Spring Cloud Config来做配置中心。 + +### svn版本 + +同样先示例server端的代码,基本步骤一样。 + +#### 1、添加依赖 + +``` + + + org.springframework.cloud + spring-cloud-config-server + + + org.tmatesoft.svnkit + svnkit + + +``` + +需要多引入svnkitr包 + +#### 2、配置文件 + +``` +server: + port: 8001 + +spring: + cloud: + config: + server: + svn: + uri: http://192.168.0.6/svn/repo/config-repo + username: username + password: password + default-label: trunk + profiles: + active: subversion + application: + name: spring-cloud-config-server +``` + +和git版本稍有区别,需要显示声明subversion. + +#### 3、启动类 + +启动类没有变化,添加`@EnableConfigServer`激活对配置中心的支持 + +``` +@EnableConfigServer +@SpringBootApplication +public class ConfigServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigServerApplication.class, args); + } +} +``` + +#### 4、测试 + +**服务端测试** + +访问:`http://localhost:8001/neo-config-dev.properties`,返回:`neo.hello: hello im dev`,说明服务端可以正常读取到svn代码库中的配置信息。修改配置文件`neo-config-dev.properties`中配置信息为:`neo.hello=hello im dev update`,再次在浏览器访问`http://localhost:8001/neo-config-dev.properties`,返回:`neo.hello: hello im dev update`。说明server端会自动读取最新提交的内容 + +**客户端测试** + +客户端直接使用上一篇示例项目`spring-cloud-config-client`来测试,配置基本不用变动。启动项目后访问:`http://localhost:8002/hello,返回:`hello im dev update`说明已经正确的从server端获取到了参数。同样修改svn配置并提交,再次访问``http://localhost:8002/hello```依然获取的是旧的信息,和git版本的问题一样。 + +### refresh + +现在来解决上一篇的遗留问题,这个问题在svn版本中依然存在。Spring Cloud Config分服务端和客户端,服务端负责将git(svn)中存储的配置文件发布成REST接口,客户端可以从服务端REST接口获取配置。但客户端并不能主动感知到配置的变化,从而主动去获取新的配置。客户端如何去主动获取新的配置信息呢,springcloud已经给我们提供了解决方案,每个客户端通过POST方法触发各自的`/refresh`。 + +修改`spring-cloud-config-client`项目已到达可以refresh的功能。 + +#### 1、添加依赖 + +``` + + org.springframework.boot + spring-boot-starter-actuator + +``` + +增加了`spring-boot-starter-actuator`包,`spring-boot-starter-actuator`是一套监控的功能,可以监控程序在运行时状态,其中就包括`/refresh`的功能。 + +#### 2、 开启更新机制 + +需要给加载变量的类上面加载`@RefreshScope`,在客户端执行`/refresh`的时候就会更新此类下面的变量值。 + +``` +@RestController +@RefreshScope // 使用该注解的类,会在接到SpringCloud配置中心配置刷新的时候,自动将新的配置更新到该类对应的字段中。 +class HelloController { + + @Value("${neo.hello}") + private String hello; + + @RequestMapping("/hello") + public String from() { + return this.hello; + } +} +``` + +#### 3、测试 + +*springboot 1.5.X 以上默认开通了安全认证,所以需要在配置文件application.properties添加以下配置* + +``` +management.security.enabled=false +``` + +OK 这样就改造完了,以post请求的方式来访问`http://localhost:8002/refresh` 就会更新修改后的配置文件。 + +我们再次来测试,首先访问`http://localhost:8002/hello`,返回:`hello im dev`,我将库中的值修改为`hello im dev update`。在win上面打开cmd执行`curl -X POST http://localhost:8002/refresh`,返回`["neo.hello"]`说明已经更新了`neo.hello`的值。我们再次访问`http://localhost:8002/hello`,返回:`hello im dev update`,客户端已经得到了最新的值。 + +每次手动刷新客户端也很麻烦,有没有什么办法只要提交代码就自动调用客户端来更新呢,github的webhook是一个好的办法。 + +#### 4、webhook + +WebHook是当某个事件发生时,通过发送http post请求的方式来通知信息接收方。Webhook来监测你在Github.com上的各种事件,最常见的莫过于push事件。如果你设置了一个监测push事件的Webhook,那么每当你的这个项目有了任何提交,这个Webhook都会被触发,这时Github就会发送一个HTTP POST请求到你配置好的地址。 + +如此一来,你就可以通过这种方式去自动完成一些重复性工作,比如,你可以用Webhook来自动触发一些持续集成(CI)工具的运作,比如Travis CI;又或者是通过 Webhook 去部署你的线上服务器。下图就是github上面的webhook配置。 + +![img](../media/pictures/SpringCloud.assets/webhook.jpg) + +- `Payload URL` :触发后回调的URL +- `Content type` :数据格式,两种一般使用json +- `Secret` :用作给POST的body加密的字符串。采用HMAC算法 +- `events` :触发的事件列表。 + +| events事件类型 | 描述 | +| :------------- | :------------------------- | +| push | 仓库有push时触发。默认事件 | +| create | 当有分支或标签被创建时触发 | +| delete | 当有分支或标签被删除时触发 | + +> svn也有类似的hook机制,每次提交后会触发post-commit脚本,我们可以在这里写一些post请求 + +这样我们就可以利用hook的机制去触发客户端的更新,但是当客户端越来越多的时候hook支持的已经不够优雅,另外每次增加客户端都需要改动hook也是不现实的。其实Spring Cloud给了我们更好解决方案,后面文章来介绍。 + +## SpringCloud(八):配置中心服务化和高可用 + +在前两篇的介绍中,客户端都是直接调用配置中心的server端来获取配置文件信息。这样就存在了一个问题,客户端和服务端的耦合性太高,如果server端要做集群,客户端只能通过原始的方式来路由,server端改变IP地址的时候,客户端也需要修改配置,不符合springcloud服务治理的理念。springcloud提供了这样的解决方案,我们只需要将server端当做一个服务注册到eureka中,client端去eureka中去获取配置中心server端的服务既可。 + +这篇文章我们基于配置中心git版本的内容来改造 + +### server端改造 + +#### 1、添加依赖 + +``` + + + org.springframework.cloud + spring-cloud-config-server + + + org.springframework.cloud + spring-cloud-starter-eureka + + +``` + +需要多引入`spring-cloud-starter-eureka`包,来添加对eureka的支持。 + +#### 2、配置文件 + +``` +server: +server: + port: 8001 +spring: + application: + name: spring-cloud-config-server + cloud: + config: + server: + git: + uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址 + search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。 + username: username # git仓库的账号 + password: password # git仓库的密码 +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8000/eureka/ ## 注册中心eurka地址 +``` + +增加了eureka注册中心的配置 + +#### 3、启动类 + +启动类添加`@EnableDiscoveryClient`激活对注册中心的支持 + +``` +@EnableDiscoveryClient +@EnableConfigServer +@SpringBootApplication +public class ConfigServerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigServerApplication.class, args); + } +} +``` + +这样server端的改造就完成了。先启动eureka注册中心,在启动server端,在浏览器中访问:`http://localhost:8000/` 就会看到server端已经注册了到注册中心了。 + +![img](../media/pictures/SpringCloud.assets/eureka-config01.jpg) + +按照上篇的测试步骤对server端进行测试服务正常。 + +### 客户端改造 + +#### 1、添加依赖 + +``` + + + org.springframework.cloud + spring-cloud-starter-config + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.cloud + spring-cloud-starter-eureka + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +需要多引入`spring-cloud-starter-eureka`包,来添加对eureka的支持。 + +#### 2、配置文件 + +``` +spring.application.name=spring-cloud-config-client +server.port=8002 + +spring.cloud.config.name=neo-config +spring.cloud.config.profile=dev +spring.cloud.config.label=master +spring.cloud.config.discovery.enabled=true +spring.cloud.config.discovery.serviceId=spring-cloud-config-server + +eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ +``` + +主要是去掉了`spring.cloud.config.uri`直接指向server端地址的配置,增加了最后的三个配置: + +- `spring.cloud.config.discovery.enabled` :开启Config服务发现支持 +- `spring.cloud.config.discovery.serviceId` :指定server端的name,也就是server端`spring.application.name`的值 +- `eureka.client.serviceUrl.defaultZone` :指向注册中心的地址 + +这三个配置文件都需要放到`bootstrap.properties`的配置中 + +#### 3、启动类 + +启动类添加`@EnableDiscoveryClient`激活对配置中心的支持 + +``` +@EnableDiscoveryClient +@SpringBootApplication +public class ConfigClientApplication { + + public static void main(String[] args) { + SpringApplication.run(ConfigClientApplication.class, args); + } +} +``` + +启动client端,在浏览器中访问:`http://localhost:8000/` 就会看到server端和client端都已经注册了到注册中心了。 + +![img](../media/pictures/SpringCloud.assets/eureka-config02.jpg) + +### 高可用 + +为了模拟生产集群环境,我们改动server端的端口为8003,再启动一个server端来做服务的负载,提供高可用的server端支持。 + +![img](../media/pictures/SpringCloud.assets/eureka-config03.jpg) + +如上图就可发现会有两个server端同时提供配置中心的服务,防止某一台down掉之后影响整个系统的使用。 + +我们先单独测试服务端,分别访问:`http://localhost:8001/neo-config/dev`、`http://localhost:8003/neo-config/dev`返回信息: + +``` +{ + "name": "neo-config", + "profiles": [ + "dev" + ], + "label": null, + "version": null, + "state": null, + "propertySources": [ + { + "name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties", + "source": { + "neo.hello": "hello im dev" + } + } + ] +} +``` + +说明两个server端都正常读取到了配置信息。 + +再次访问:`http://localhost:8002/hello`,返回:`hello im dev update`。说明客户端已经读取到了server端的内容,我们随机停掉一台server端的服务,再次访问`http://localhost:8002/hello`,返回:`hello im dev update`,说明达到了高可用的目的。 + +## SpringCloud(九):配置中心和消息总线(配置中心终结版) + +我们在[springcloud(七):配置中心svn示例和refresh](http://www.ityouknow.com/springcloud/2017/05/23/springcloud-config-svn-refresh.html)中讲到,如果需要客户端获取到最新的配置信息需要执行`refresh`,我们可以利用webhook的机制每次提交代码发送请求来刷新客户端,当客户端越来越多的时候,需要每个客户端都执行一遍,这种方案就不太适合了。使用Spring Cloud Bus可以完美解决这一问题。 + +### Spring Cloud Bus + +Spring cloud bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其他的消息指令。Spring bus的一个核心思想是通过分布式的启动器对spring boot应用进行扩展,也可以用来建立一个多个应用之间的通信频道。目前唯一实现的方式是用AMQP消息代理作为通道,同样特性的设置(有些取决于通道的设置)在更多通道的文档中。 + +Spring cloud bus被国内很多都翻译为消息总线,也挺形象的。大家可以将它理解为管理和传播所有分布式项目中的消息既可,其实本质是利用了MQ的广播机制在分布式的系统中传播消息,目前常用的有Kafka和RabbitMQ。利用bus的机制可以做很多的事情,其中配置中心客户端刷新就是典型的应用场景之一,我们用一张图来描述bus在配置中心使用的机制。 + +![img](../media/pictures/SpringCloud.assets/configbus1.jpg) + +根据此图我们可以看出利用Spring Cloud Bus做配置更新的步骤: + +- 1、提交代码触发post给客户端A发送bus/refresh +- 2、客户端A接收到请求从Server端更新配置并且发送给Spring Cloud Bus +- 3、Spring Cloud bus接到消息并通知给其它客户端 +- 4、其它客户端接收到通知,请求Server端获取最新配置 +- 5、全部客户端均获取到最新的配置 + +### 项目示例 + +我们选择上一篇文章[springcloud(八):配置中心服务化和高可用](http://www.ityouknow.com/springcloud/2017/05/25/springcloud-config-eureka.html)版本的[示例代码](https://github.com/ityouknow/spring-cloud-starter/tree/master/spring-cloud-config-eureka)来改造,MQ我们使用RabbitMQ来做示例。 + +**客户端spring-cloud-config-client改造** + +#### 1、添加依赖 + +``` + + org.springframework.cloud + spring-cloud-starter-bus-amqp + +``` + +需要多引入`spring-cloud-starter-bus-amqp`包,增加对消息总线的支持 + +#### 2、配置文件 + +``` +## 刷新时,关闭安全验证 +management.security.enabled=false +## 开启消息跟踪 +spring.cloud.bus.trace.enabled=true + +spring.rabbitmq.host=192.168.9.89 +spring.rabbitmq.port=5672 +spring.rabbitmq.username=admin +spring.rabbitmq.password=123456 +``` + +配置文件需要增加RebbitMq的相关配置,这样客户端代码就改造完成了。 + +#### 3、测试 + +依次启动spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client项目,在启动spring-cloud-config-client项目的时候我们会发现启动日志会输出这样的一条记录。 + +``` +2017-05-26 17:05:38.568 INFO 21924 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/bus/refresh],methods=[POST]}" onto public void org.springframework.cloud.bus.endpoint.RefreshBusEndpoint.refresh(java.lang.String) +``` + +说明客户端已经具备了消息总线通知的能力了,为了更好的模拟消息总线的效果,我们更改客户端spring-cloud-config-client项目的端口为8003、8004依次启动,这样测试环境就准备好了。启动后eureka后台效果图如下: + +![img](../media/pictures/SpringCloud.assets/configbus3.jpg) + +我们先分别测试一下服务端和客户端是否正确运行,访问:`http://localhost:8001/neo-config/dev`,返回信息: + +``` +{ + "name": "neo-config", + "profiles": [ + "dev" + ], + "label": null, + "version": null, + "state": null, + "propertySources": [ + { + "name": "https://github.com/ityouknow/spring-cloud-starter/config-repo/neo-config-dev.properties", + "source": { + "neo.hello": "hello im dev" + } + } + ] +} +``` + +说明server端都正常读取到了配置信息。 + +依次访问:`http://localhost:8002/hello`、`http://localhost:8003/hello`、`http://localhost:8004/hello`,返回:`hello im dev`。说明客户端都已经读取到了server端的内容。 + +现在我们更新`neo-config-dev.properties` 中`neo.hello`的值为`hello im dev update`并提交到代码库中,访问:`http://localhost:8002/hello` 依然返回`hello im dev`。我们对端口为8002的客户端发送一个`/bus/refresh`的post请求。在win下使用下面命令来模拟webhook. + +``` +curl -X POST http://localhost:8002/bus/refresh +``` + +执行完成后,依次访问:`http://localhost:8002/hello`、`http://localhost:8003/hello`、`http://localhost:8004/hello`,返回:`hello im dev update`。说明三个客户端均已经拿到了最新配置文件的信息,这样我们就实现了图一中的示例。 + +### 改进版本 + +在上面的流程中,我们已经到达了利用消息总线触发一个客户端`bus/refresh`,而刷新所有客户端的配置的目的。但这种方式并不优雅。原因如下: + +- 打破了微服务的职责单一性。微服务本身是业务模块,它本不应该承担配置刷新的职责。 +- 破坏了微服务各节点的对等性。 +- 有一定的局限性。例如,微服务在迁移时,它的网络地址常常会发生变化,此时如果想要做到自动刷新,那就不得不修改WebHook的配置。 + +因此我们将上面的架构模式稍微改变一下 + +![img](../media/pictures/SpringCloud.assets/configbus2.jpg) + +这时Spring Cloud Bus做配置更新步骤如下: + +- 1、提交代码触发post请求给bus/refresh +- 2、server端接收到请求并发送给Spring Cloud Bus +- 3、Spring Cloud bus接到消息并通知给其它客户端 +- 4、其它客户端接收到通知,请求Server端获取最新配置 +- 5、全部客户端均获取到最新的配置 + +这样的话我们在server端的代码做一些改动,来支持`bus/refresh` + +#### 1、添加依赖 + +``` + + + org.springframework.cloud + spring-cloud-config-server + + + org.springframework.cloud + spring-cloud-starter-bus-amqp + + + org.springframework.cloud + spring-cloud-starter-eureka + + +``` + +需要多引入`spring-cloud-starter-bus-amqp`包,增加对消息总线的支持 + +#### 2、配置文件 + +``` +server: + port: 8001 +spring: + application: + name: spring-cloud-config-server + cloud: + config: + server: + git: + uri: https://github.com/ityouknow/spring-cloud-starter/ # 配置git仓库的地址 + search-paths: config-repo # git仓库地址下的相对地址,可以配置多个,用,分割。 + username: username # git仓库的账号 + password: password # git仓库的密码 + rabbitmq: + host: 192.168.0.6 + port: 5672 + username: admin + password: 123456 + +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8000/eureka/ ## 注册中心eurka地址 + + +management: + security: + enabled: false +``` + +配置文件增加RebbitMq的相关配置,关闭安全验证。这样server端代码就改造完成了。 + +#### 3、测试 + +依次启动spring-cloud-eureka、spring-cloud-config-server、spring-cloud-config-client项目,改动spring-cloud-config-client项目端口为8003、8004依次启动。测试环境准备完成。 + +按照上面的测试方式,访问server端和三个客户端测试均可以正确返回信息。同样修改`neo-config-dev.properties` 中`neo.hello`的值为`hello im dev update`并提交到代码库中。在win下使用下面命令来模拟webhook触发server端`bus/refresh`. + +``` +curl -X POST http://localhost:8001/bus/refresh +``` + +执行完成后,依次访问:`http://localhost:8002/hello`、`http://localhost:8003/hello`、`http://localhost:8004/hello`,返回:`hello im dev update`。说明三个客户端均已经拿到了最新配置文件的信息,这样我们就实现了上图中的示例。 + +### 其它 + +#### 局部刷新 + +某些场景下(例如灰度发布),我们可能只想刷新部分微服务的配置,此时可通过`/bus/refresh`端点的destination参数来定位要刷新的应用程序。 + +例如:`/bus/refresh?destination=customers:8000`,这样消息总线上的微服务实例就会根据destination参数的值来判断是否需要要刷新。其中,`customers:8000`指的是各个微服务的ApplicationContext ID。 + +destination参数也可以用来定位特定的微服务。例如:`/bus/refresh?destination=customers:**`,这样就可以触发customers微服务所有实例的配置刷新。 + +#### 跟踪总线事件 + +一些场景下,我们可能希望知道Spring Cloud Bus事件传播的细节。此时,我们可以跟踪总线事件(RemoteApplicationEvent的子类都是总线事件)。 + +跟踪总线事件非常简单,只需设置`spring.cloud.bus.trace.enabled=true`,这样在`/bus/refresh`端点被请求后,访问`/trace`端点就可获得类似如下的结果: + +``` +{ + "timestamp": 1495851419032, + "info": { + "signal": "spring.cloud.bus.ack", + "type": "RefreshRemoteApplicationEvent", + "id": "c4d374b7-58ea-4928-a312-31984def293b", + "origin": "stores:8002", + "destination": "*:**" + } + }, + { + "timestamp": 1495851419033, + "info": { + "signal": "spring.cloud.bus.sent", + "type": "RefreshRemoteApplicationEvent", + "id": "c4d374b7-58ea-4928-a312-31984def293b", + "origin": "spring-cloud-config-client:8001", + "destination": "*:**" + } + }, + { + "timestamp": 1495851422175, + "info": { + "signal": "spring.cloud.bus.ack", + "type": "RefreshRemoteApplicationEvent", + "id": "c4d374b7-58ea-4928-a312-31984def293b", + "origin": "customers:8001", + "destination": "*:**" + } +} +``` + +这个日志显示了`customers:8001`发出了RefreshRemoteApplicationEvent事件,广播给所有的服务,被`customers:9000`和`stores:8081`接受到了。想要对接受到的消息自定义自己的处理方式的话,可以添加`@EventListener`注解的AckRemoteApplicationEvent和SentApplicationEvent类型到你自己的应用中。或者到TraceRepository类中,直接处理数据。 + +这样,我们就可清晰地知道事件的传播细节。 + +### `/bus/refresh` BUG + +`/bus/refresh` 有一个很严重的BUG,一直没有解决:对客户端执行`/bus/refresh`之后,挂到总线的上的客户端都会从Eureka注册中心撤销登记;如果对server端执行`/bus/refresh`,server端也会从Eureka注册中心撤销登记。再用白话解释一下,就是本来人家在Eureka注册中心注册的好好的,只要你对着它执行一次`/bus/refresh`,立刻就会从Euraka中挂掉。 + +其实这个问题挺严重的,本来你利用`/bus/refresh`给所有的节点来更新配置信息呢,结果把服务从Euraka中给搞掉了,那么如果别人需要调用客户端的服务的时候就直接歇菜了。不知道国内有童鞋公司在生产中用到这个功能没有,用了不就很惨烈。在网上搜索了一下,国内网友和国外网友都遇到过很多次,但是一直没有解决,很幸运就是我在写这篇文章的**前三天**,Netflix修复了这个问题,使用Spring Cloud最新版本的包就可以解决这个问题。由此也可以发现Spring Cloud还在快速的发展中,最新的版本可能也会有一些不稳定性,可见路漫漫而修远兮。 + +在pom中使用Spring Cloud的版本,解决这个bug. + +``` + + UTF-8 + UTF-8 + 1.8 + Dalston.SR1 + +``` + +主要是这句:`Dalston.SR1` ,详情可以参考本文示例中的代码 + +BUG的讨论和解决过程可以看github上面这两个issue: + +- [/bus/refresh causes instances registered in Eureka Server disappeared #692](https://github.com/spring-cloud/spring-cloud-config/issues/692) +- [Making POST on ‘refresh’ permamently deregisters the service from Eureka #1857](https://github.com/spring-cloud/spring-cloud-netflix/issues/1857) + + + +## SpringCloud(十):服务网关zuul初级篇 + +前面的文章我们介绍了,Eureka用于服务的注册于发现,Feign支持服务的调用以及均衡负载,Hystrix处理服务的熔断防止故障扩散,Spring Cloud Config服务集群配置中心,似乎一个微服务框架已经完成了。 + +我们还是少考虑了一个问题,外部的应用如何来访问内部各种各样的微服务呢?在微服务架构中,后端服务往往不直接开放给调用端,而是通过一个API网关根据请求的url,路由到相应的服务。当添加API网关后,在第三方调用端和服务提供方之间就创建了一面墙,这面墙直接与调用方通信进行权限控制,后将请求均衡分发给后台服务端。 + +### 为什么需要API Gateway + +1、简化客户端调用复杂度 + +在微服务架构模式下后端服务的实例数一般是动态的,对于客户端而言很难发现动态改变的服务实例的访问地址信息。因此在基于微服务的项目中为了简化前端的调用逻辑,通常会引入API Gateway作为轻量级网关,同时API Gateway中也会实现相关的认证逻辑从而简化内部服务之间相互调用的复杂度。 + +![img](../media/pictures/SpringCloud.assets/api_gateway.png) + +2、数据裁剪以及聚合 + +通常而言不同的客户端对于显示时对于数据的需求是不一致的,比如手机端或者Web端又或者在低延迟的网络环境或者高延迟的网络环境。 + +因此为了优化客户端的使用体验,API Gateway可以对通用性的响应数据进行裁剪以适应不同客户端的使用需求。同时还可以将多个API调用逻辑进行聚合,从而减少客户端的请求数,优化客户端用户体验 + +3、多渠道支持 + +当然我们还可以针对不同的渠道和客户端提供不同的API Gateway,对于该模式的使用由另外一个大家熟知的方式叫Backend for front-end, 在Backend for front-end模式当中,我们可以针对不同的客户端分别创建其BFF,进一步了解BFF可以参考这篇文章:[Pattern: Backends For Frontends](http://samnewman.io/patterns/architectural/bff/) + +![img](../media/pictures/SpringCloud.assets/bff.png) + +4、遗留系统的微服务化改造 + +对于系统而言进行微服务改造通常是由于原有的系统存在或多或少的问题,比如技术债务,代码质量,可维护性,可扩展性等等。API Gateway的模式同样适用于这一类遗留系统的改造,通过微服务化的改造逐步实现对原有系统中的问题的修复,从而提升对于原有业务响应力的提升。通过引入抽象层,逐步使用新的实现替换旧的实现。 + +![img](../media/pictures/SpringCloud.assets/bff-process.png) + +> 在Spring Cloud体系中, Spring Cloud Zuul就是提供负载均衡、反向代理、权限认证的一个API gateway。 + +### Spring Cloud Zuul + +Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。 + +下面我们通过代码来了解Zuul是如何工作的 + +#### 简单使用 + +1、添加依赖 + +``` + + org.springframework.cloud + spring-cloud-starter-zuul + +``` + +引入`spring-cloud-starter-zuul`包 + +2、配置文件 + +``` +spring.application.name=gateway-service-zuul +server.port=8888 + +#这里的配置表示,访问/it/** 直接重定向到http://www.ityouknow.com/** +zuul.routes.baidu.path=/it/** +zuul.routes.baidu.url=http://www.ityouknow.com/ +``` + +3、启动类 + +``` +@SpringBootApplication +@EnableZuulProxy +public class GatewayServiceZuulApplication { + + public static void main(String[] args) { + SpringApplication.run(GatewayServiceZuulApplication.class, args); + } +} +``` + +启动类添加`@EnableZuulProxy`,支持网关路由。 + +史上最简单的zuul案例就配置完了 + +4、测试 + +启动`gateway-service-zuul-simple`项目,在浏览器中访问:`http://localhost:8888/it/spring-cloud`,看到页面返回了:`http://www.ityouknow.com/spring-cloud` 页面的信息,如下: + +![img](../media/pictures/SpringCloud.assets/zuul-01.jpg) + +我们以前面文章的示例代码`spring-cloud-producer`为例来测试请求的重定向,在配置文件中添加: + +``` +zuul.routes.hello.path=/hello/** +zuul.routes.hello.url=http://localhost:9000/ +``` + +启动`spring-cloud-producer`,重新启动`gateway-service-zuul-simple`,访问:`http://localhost:8888/hello/hello?name=%E5%B0%8F%E6%98%8E`,返回:`hello 小明,this is first messge` + +说明访问`gateway-service-zuul-simple`的请求自动转发到了`spring-cloud-producer`,并且将结果返回。 + +#### 服务化 + +通过url映射的方式来实现zull的转发有局限性,比如每增加一个服务就需要配置一条内容,另外后端的服务如果是动态来提供,就不能采用这种方案来配置了。实际上在实现微服务架构时,服务名与服务实例地址的关系在eureka server中已经存在了,所以只需要将Zuul注册到eureka server上去发现其他服务,就可以实现对serviceId的映射。 + +我们结合示例来说明,在上面示例项目`gateway-service-zuul-simple`的基础上来改造。 + +1、添加依赖 + +``` + + org.springframework.cloud + spring-cloud-starter-eureka + +``` + +增加`spring-cloud-starter-eureka`包,添加对eureka的支持。 + +2、配置文件 + +配置修改为: + +``` +spring.application.name=gateway-service-zuul +server.port=8888 + +zuul.routes.api-a.path=/producer/** +zuul.routes.api-a.serviceId=spring-cloud-producer + +eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/ +``` + +3、测试 + +依次启动 `spring-cloud-eureka`、 `spring-cloud-producer`、`gateway-service-zuul-eureka`,访问:`http://localhost:8888/producer/hello?name=%E5%B0%8F%E6%98%8E`,返回:`hello 小明,this is first messge` + +说明访问`gateway-service-zuul-eureka`的请求自动转发到了`spring-cloud-producer`,并且将结果返回。 + +为了更好的模拟服务集群,我们复制`spring-cloud-producer`项目改为`spring-cloud-producer-2`,修改`spring-cloud-producer-2`项目端口为9001,controller代码修改如下: + +``` +@RestController +public class HelloController { + + @RequestMapping("/hello") + public String index(@RequestParam String name) { + return "hello "+name+",this is two messge"; + } +} +``` + +修改完成后启动`spring-cloud-producer-2`,重启`gateway-service-zuul-eureka`。测试多次访问`http://localhost:8888/producer/hello?name=%E5%B0%8F%E6%98%8E`,依次返回: + +``` +hello 小明,this is first messge +hello 小明,this is two messge +hello 小明,this is first messge +hello 小明,this is two messge +... +``` + +说明通过zuul成功调用了producer服务并且做了均衡负载。 + +**网关的默认路由规则** + +但是如果后端服务多达十几个的时候,每一个都这样配置也挺麻烦的,spring cloud zuul已经帮我们做了默认配置。默认情况下,Zuul会代理所有注册到Eureka Server的微服务,并且Zuul的路由规则如下:`http://ZUUL_HOST:ZUUL_PORT/微服务在Eureka上的serviceId/**`会被转发到serviceId对应的微服务。 + +我们注销掉`gateway-service-zuul-eureka`项目中关于路由的配置: + +``` +#zuul.routes.api-a.path=/producer/** +#zuul.routes.api-a.serviceId=spring-cloud-producer +``` + +重新启动后,访问`http://localhost:8888/spring-cloud-producer/hello?name=%E5%B0%8F%E6%98%8E`,测试返回结果和上述示例相同,说明Spring cloud zuul默认已经提供了转发功能。 + +到此zuul的基本使用我们就介绍完了。关于zuul更高级使用,我们下篇再来介绍。 + + + +## Spring Cloud在国内中小型公司能用起来吗? + +今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题[Spring Cloud在国内中小型公司能用起来吗?](https://www.zhihu.com/question/61403505),吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将自己的疑问表达了出来,作为一个研究并使用Spring Boot和Spring Cloud近两年的程序员,看的我手痒痒不答不快呀。 + +### 好问题 + +好问题必须配认真的回答,仔细的看了题主的问题,发现这个问题非常具有代表性,可能是广大网友想使用Spring Cloud却又对Spring Cloud不太了解的共同想法,题主对Spring Cloud使用的方方面面都进行过了思考,包括市场,学习、前后端、测试、配置、部署、开发以及运维,下面就是题主原本的问题: + +**想在公司推广Spring Cloud,但我对这项技术还缺乏了解,画了一张脑图,总结了种种问题。** + +![img](../media/pictures/SpringCloud.assets/springcloud-question.png) + +**微服务是这样一个结构吗?** + +``` +前端或二方 - > ng集群 -> zuul集群 -> eureka-server集群 -> service provider集群 +``` + +> (二方指其他业务部门) + +想要明白这个问题,首先需要知道什么是Spring Boot,什么是Spring Cloud,以及两者之间有什么关系? + +### 什么是Spring Boot + +Spring Boot简化了基于Spring的应用开发,通过少量的代码就能创建一个独立的、产品级别的Spring应用。 Spring Boot为Spring平台及第三方库提供开箱即用的设置,这样你就可以有条不紊地开始。多数Spring Boot应用只需要很少的Spring配置。 + +Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。用我的话来理解,就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring Boot整合了所有的框架(不知道这样比喻是否合适)。 + +Spring Boot的核心思想就是约定大于配置,一切自动完成。采用Spring Boot可以大大的简化你的开发模式,所有你想集成的常用框架,它都有对应的组件支持。如果你对Spring Boot完全不了解,可以参考我的这篇文章:[Springboot(一):入门篇](http://www.ityouknow.com/springboot/2016/01/06/springboot(一)-入门篇.html) + +### 什么是Spring Cloud + +Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。 + +微服务是可以独立部署、水平扩展、独立访问(或者有独立的数据库)的服务单元,Spring Cloud就是这些微服务的大管家,采用了微服务这种架构之后,项目的数量会非常多,Spring Cloud做为大管家就需要提供各种方案来维护整个生态。 + +Spring Cloud就是一套分布式服务治理的框架,既然它是一套服务治理的框架,那么它本身不会提供具体功能性的操作,更专注于服务之间的通讯、熔断、监控等。因此就需要很多的组件来支持一套功能,如果你对Spring Cloud组件不是特别了解的话,可以参考我的这篇文章:[springcloud(一):大话Spring Cloud](http://www.ityouknow.com/springcloud/2017/05/01/simple-springcloud.html) + +### Spring Boot和Spring Cloud的关系 + +Spring Boot 是 Spring 的一套快速配置脚手架,可以基于Spring Boot 快速开发单个微服务,Spring Cloud是一个基于Spring Boot实现的云应用开发工具;Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架;Spring Boot使用了默认大于配置的理念,很多集成方案已经帮你选择好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot来实现,可以不基于Spring Boot吗?不可以。 + +Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud离不开Spring Boot,属于依赖的关系。 + +> Spring -> Spring Boot > Spring Cloud 这样的关系。 + +### 回答 + +> 以下为我在知乎的回答。 + +首先楼主问的这些问题都挺好的,算是经过了自己的一番思考,我恰好经历了你所说的中小公司,且都使用Spring Cloud并且已经投产上线。第一家公司技术开发人员15人左右,项目实例 30多,第二家公司开发人员100人左右,项目实例达160多。 + +实话说Spring Boot、Spring Cloud仍在高速发展,技术生态不断的完善和扩张,不免也会有一些小的bug,但对于中小公司的使用来将,完全可以忽略,基本都可以找到解决方案,接下来回到你的问题。 + +1、市场 + +据我所知有很多知名互联网公司都已经使用了Spring Cloud,比如阿里、美团但都是小规模,没有像我经历的这俩家公司,业务线全部拥抱Spring Cloud;另外Spring Cloud并不是一套高深的技术,普通的Java程序员经过一到俩个月完全就可以上手,但前期需要一个比较精通人的来带队。 + +> 后记,找阿里的小马哥确认了下,阿里也在大规模使用。 + +2、学习 + +有很多种方式,现在Spring Cloud越来越火的情况下,各种资源也越来越丰富,查看官方文档和示例,现在很多优秀的博客在写Spring cloud的相关教程,我这里收集了一些Spring Boot和Spring Cloud的相关资源可以参考,找到博客也就找到人和组织了。 + +- [Spring Boot学习资料汇总](http://www.ityouknow.com/springboot/2015/12/30/springboot-collect.html): +- [Spring Cloud学习资料汇总](http://www.ityouknow.com/springcloud/2016/12/30/springcloud-collect.html) : + +3、前后职责划分 + +其实这个问题是每个系统架构都应该考虑的问题,Spring Cloud只是后端服务治理的一套框架,唯一和前端有关系的是thymeleaf,Spring推荐使用它做模板引擎。一般情况下,前端app或者网页通过zuul来调用后端的服务,如果包含静态资源也可以使用nginx做一下代理转发。 + +4、测试 + +Spring-boot-starter-test支持项目中各层方法的测试,也支持controller层的各种属性。所以一般测试的步奏是这样,首先开发人员覆盖自己的所有方法,然后测试微服务内所有对外接口保证微服务内的正确性,再进行微服务之间集成测试,最后交付测试。 + +5、配置 + +**session共享有很多种方式**,比如使用tomcat sesion共享机制,但我比较推荐使用redis缓存来做session共享。完全可以分批引入,我在上一家公司就是分批过渡上线,新旧项目通过zuul进行交互,分批引入的时候,最好是新业务线先使用Spring Cloud,老业务做过渡,当完全掌握之后在全部替换。如果只是请求转发,zuul的性能不一定比nginx低,但是如果涉及到静态资源,还是建议在前端使用nginx做一下代理。另外Spring Cloud有配置中心,可以非常灵活的做所有配置的事情。 + +6、部署 + +多环境不同配置,Spring Boot最擅长做这个事情了,使用不同的配置文件来配置不同环境的参数,在服务启动的时候指明某个配置文件即可,例如:`java -jar app.jar --spring.profiles.active=dev`就是启动测试环境的配置文件;Spring Cloud 没有提供发布平台,因为jenkins已经足够完善了,推荐使用jenkins来部署Spring Boot项目,会省非常多的事情;灰度暂时不支持,可能需要自己来做,如果有多个实例,可以一个一个来更新;支持混合部署,一台机子部署多个是常见的事情。 + +7、开发 + +你说的包含html接口就是前端页面吧,Spring Boot可以支持,但其实也是Spring Mvc在做这个事情,Spring Cloud只做服务治理,其它具体的功能都是集成了各种框架来解决而已;excel报表可以,其实除过swing项目外,其它Java项目都可以想象;Spring Cloud和老项目可以混合使用,通过zuul来支持。是否支持callback,可以通过MQ来实现,还是强调Spring Cloud只是服务治理。 + +8、运维 + +Turbine、zipkin可以用来做熔断和性能监控;动态上下线某个节点可以通过jenkins来实现;provider下线后,会有其它相同的实例来提供服务,Eureka会间隔一段时间来检测服务的可用性;不同节点配置不同的流量权值目前还不支持。注册中心必须做高可用集群,注册中心挂掉之后,服务实例会全部停止。 + +总结,中小企业是否能用的起来Spring Cloud,完全取决于自己公司的环境,如果是一个技术活跃型的团队就大胆的去尝试吧,目前Spring Cloud是所有微服务治理中最优秀的方案,也是一个趋势,未来一两年可能就会像Spring一样流行,早接触早学习岂不更好。 + +希望能解答了你的疑问。 + +### Spring Cloud 架构 + +我们从整体来看一下Spring Cloud主要的组件,以及它的访问流程 + +![img](../media/pictures/SpringCloud.assets/spring-cloud-architecture.png) + +- 1、外部或者内部的非Spring Cloud项目都统一通过API网关(Zuul)来访问内部服务. +- 2、网关接收到请求后,从注册中心(Eureka)获取可用服务 +- 3、由Ribbon进行均衡负载后,分发到后端的具体实例 +- 4、微服务之间通过Feign进行通信处理业务 +- 5、Hystrix负责处理服务超时熔断 +- 6、Turbine监控服务间的调用和熔断相关指标 + +图中没有画出配置中心,**配置中心管理各微服务不同环境下的配置文件**。 + +以上就是一个完整的Spring Cloud生态图。 + +最后送一个完整示例的Spring Cloud开源项目等你去[spring-cloud-examples](https://github.com/ityouknow/spring-cloud-examples) + + + +## 从架构演进的角度聊聊Spring Cloud都做了些什么? + +Spring Cloud作为一套微服务治理的框架,几乎考虑到了微服务治理的方方面面,之前也写过一些关于Spring Cloud文章,主要偏重各组件的使用,本次分享主要解答这两个问题:Spring Cloud在微服务的架构中都做了哪些事情?Spring Cloud提供的这些功能对微服务的架构提供了怎样的便利? + +这也是我写Spring Cloud三部曲的最后一篇文章,前两面篇内容如下: + +- [中小型互联网公司微服务实践-经验和教训](http://mp.weixin.qq.com/s/bciSlKearaVFQg1QWOSn_g) +- [Spring Cloud在国内中小型公司能用起来吗?](https://mp.weixin.qq.com/s/vnWXpH5pv-FAzLZfbgTGvg) + +我们先来简单回顾一下,我们以往互联网架构的发展情况: + +### 传统架构发展史 + +#### 单体架构 + +单体架构在小微企业比较常见,典型代表就是一个应用、一个数据库、一个web容器就可以跑起来,比如我们开发的开源软件[云收藏](https://github.com/cloudfavorites/favorites-web),就是标准的单体架构。 + +在两种情况下可能会选择单体架构:一是在企业发展的初期,为了保证快速上线,采用此种方案较为简单灵活;二是传统企业中垂直度较高,访问压力较小的业务。在这种模式下对技术要求较低,方便各层次开发人员接手,也能满足客户需求。 + +下面是单体架构的架构图: + +![img](../media/pictures/SpringCloud.assets/single_structure.jpg) + +在单体架构中,技术选型非常灵活,优先满足快速上线的要求,也便于快速跟进市场。 + +#### 垂直架构 + +在单体架构发展一段时间后,公司的业务模式得到了认可,交易量也慢慢的大起来,这时候有些企业为了应对更大的流量,就会对原有的业务进行拆分,比如说:后台系统、前端系统、交易系统等。 + +在这一阶段往往会将系统分为不同的层级,每个层级有对应的职责,UI层负责和用户进行交互、业务逻辑层负责具体的业务功能、数据库层负责和上层进行数据交换和存储。 + +下面是垂直架构的架构图: + +![img](../media/pictures/SpringCloud.assets/vertical__structure.jpg) + +在这个阶段SSH(struts+spring+hibernate)是项目的关键技术,Struts负责web层逻辑控制、Spring负责业务层管理Bean、Hibernate负责数据库操作进行封装,持久化数据。 + +#### 服务化架构 + +如果公司进一步的做大,垂直子系统会变的越来越多,系统和系统之间的调用关系呈指数上升的趋势。在这样的背景下,很多公司都会考虑服务的SOA化。SOA代表面向服务的架构,将应用程序根据不同的职责划分为不同的模块,不同的模块直接通过特定的协议和接口进行交互。这样使整个系统切分成很多单个组件服务来完成请求,当流量过大时通过水平扩展相应的组件来支撑,所有的组件通过交互来满足整体的业务需求。 + +SOA服务化的优点是,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。服务层是SOA的基础,可以直接被应用调用,从而有效控制系统中与软件代理交互的人为依赖性。 + +服务化架构是一套松耦合的架构,服务的拆分原则是服务内部高内聚,服务之间低耦合。 + +下面是服务化架构图: + +![img](../media/pictures/SpringCloud.assets/soa__structure.jpg) + +在这个阶段可以使用WebService或者dubbo来服务治理。 + +我们发现从单体架构到服务化架构,应用数量都在不断的增加,慢慢的下沉的就成了基础组建,上浮的就成为业务系统。从上述也可以看出架构的本质就是不断的拆分重构:分的过程是把系统拆分为各个子系统/模块/组件,拆的时候,首先要解决每个组件的定位问题,然后才能划分彼此的边界,实现合理的拆分。合就是根据最终要求,把各个分离的组件有机整合在一起。拆分的结果使开发人员能够做到业务聚焦、技能聚焦,实现开发敏捷,合的结果是系统变得柔性,可以因需而变,实现业务敏捷。 + +### SOA和微服务架构 + +#### SOA和微服务的区别 + +其实服务化架构已经可以解决大部分企业的需求了,那么我们为什么要研究微服务呢?先说说它们的区别; + +- 微服务架构强调业务系统需要彻底的组件化和服务化,一个组件就是一个产品,可以独立对外提供服务 +- 微服务不再强调传统SOA架构里面比较重的ESB企业服务总线 +- 微服务强调每个微服务都有自己独立的运行空间,包括数据库资源。 +- 微服务架构本身来源于互联网的思路,因此组件对外发布的服务强调了采用HTTP Rest API的方式来进行 +- 微服务的切分粒度会更小 + +> 总结:微服务架构是 SOA 架构思想的一种扩展,更加强调服务个体的独立性、拆分粒度更小。 + +#### 为什么考虑Spring Cloud + +- Spring Cloud来源于Spring,质量、稳定性、持续性都可以得到保证 +- Spring Cloud天然支持Spring Boot,更加便于业务落地。 +- Spring Cloud发展非常的快,从16年开始接触的时候相关组件版本为1.x,到现在将要发布2.x系列 +- Spring Cloud是Java领域最适合做微服务的框架。 +- 相比于其它框架,Spring Cloud对微服务周边环境的支持力度最大。 +- 对于中小企业来讲,使用门槛较低。 + +> Spring Cloud 是微服务架构的最佳落地方案 + +#### 它的特性 + +以下为Spring Cloud的核心特性: + +- 分布式/版本化配置 +- 服务注册和发现 +- 路由 +- 服务和服务之间的调用 +- 负载均衡 +- 断路器 +- 分布式消息传递 + +这些特性都是由不同的组件来完成,在架构的演进过程中扮演着重要的角色,接下来我们一起看看。 + +### 微服务架构 + +Spring Cloud解决的第一个问题就是:服务与服务之间的解耦。很多公司在业务高速发展的时候,服务组件也会相应的不断增加。服务和服务之间有着复杂的相互调用关系,经常有服务A调用服务B,服务B调用服务C和服务D …,随着服务化组件的不断增多,服务之间的调用关系成指数级别的增长,极端情况下就如下图所示: + +![img](../media/pictures/SpringCloud.assets/calling_relation.png) + +这样最容易导致的情况就是牵一发而动全身。经常出现由于某个服务更新而没有通知到其它服务,导致上线后惨案频发。这时候就应该进行服务治理,将服务之间的直接依赖转化为服务对服务中心的依赖。Spring Cloud 核心组件Eureka就是解决这类问题。 + +#### Eureka + +Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现。也是Spring Cloud体系中最重要最核心的组件之一。 + +用大白话讲,Eureka就是一个服务中心,将所有的可以提供的服务都注册到它这里来管理,其它各调用者需要的时候去注册中心获取,然后再进行调用,避免了服务之间的直接调用,方便后续的水平扩展、故障转移等。如下图: + +![img](../media/pictures/SpringCloud.assets/eureka.jpg) + +当然服务中心这么重要的组件一但挂掉将会影响全部服务,因此需要搭建Eureka集群来保持高可用性,生产中建议最少两台。随着系统的流量不断增加,需要根据情况来扩展某个服务,Eureka内部已经提供均衡负载的功能,只需要增加相应的服务端实例既可。那么在系统的运行期间某个实例挂了怎么办?Eureka内容有一个心跳检测机制,如果某个实例在规定的时间内没有进行通讯则会自动被剔除掉,避免了某个实例挂掉而影响服务。 + +因此使用了Eureka就自动具有了注册中心、负载均衡、故障转移的功能。如果想对Eureka进一步了解可以参考这篇文章:[注册中心Eureka](http://www.ityouknow.com/springcloud/2017/05/10/springcloud-eureka.html) + +#### Hystrix + +在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。 + +如下图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。 + +![img](../media/pictures/SpringCloud.assets/hystrix-1-1589793999051.png) + +在这种情况下就需要整个服务机构具有故障隔离的功能,避免某一个服务挂掉影响全局。在Spring Cloud 中Hystrix组件就扮演这个角色。 + +Hystrix会在某个服务连续调用N次不响应的情况下,立即通知调用端调用失败,避免调用端持续等待而影响了整体服务。Hystrix间隔时间会再次检查此服务,如果服务恢复将继续提供服务。 + +继续了解Hystrix可以参考:[熔断器Hystrix](http://www.ityouknow.com/springcloud/2017/05/10/springcloud-eureka.html) + +#### Hystrix Dashboard和Turbine + +当熔断发生的时候需要迅速的响应来解决问题,避免故障进一步扩散,那么对熔断的监控就变得非常重要。熔断的监控现在有两款工具:Hystrix-dashboard和Turbine + +Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据。但是只使用Hystrix Dashboard的话, 你只能看到单个应用内的服务信息, 这明显不够. 我们需要一个工具能让我们汇总系统内多个服务的数据并显示到Hystrix Dashboard上, 这个工具就是Turbine. 监控的效果图如下: + +![img](../media/pictures/SpringCloud.assets/turbine-02-1589793999206.jpg) + +想了解具体都监控了哪些指标,以及如何监控可以参考这篇文章:[熔断监控Hystrix Dashboard和Turbine](http://www.ityouknow.com/springcloud/2017/05/18/hystrix-dashboard-turbine.html) + +#### 配置中心 + +随着微服务不断的增多,每个微服务都有自己对应的配置文件。在研发过程中有测试环境、UAT环境、生产环境,因此每个微服务又对应至少三个不同环境的配置文件。这么多的配置文件,如果需要修改某个公共服务的配置信息,如:缓存、数据库等,难免会产生混乱,这个时候就需要引入Spring Cloud另外一个组件:Spring Cloud Config。 + +##### Spring Cloud Config + +Spring Cloud Config是一个解决分布式系统的配置管理方案。它包含了Client和Server两个部分,Server提供配置文件的存储、以接口的形式将配置文件的内容提供出去,Client通过接口获取数据、并依据此数据初始化自己的应用。 + +其实就是Server端将所有的配置文件服务化,需要配置文件的服务实例去Config Server获取对应的数据。将所有的配置文件统一整理,避免了配置文件碎片化。配置中心git实例参考:[配置中心git示例](http://www.ityouknow.com/springcloud/2017/05/22/springcloud-config-git.html); + +如果服务运行期间改变配置文件,服务是不会得到最新的配置信息,需要解决这个问题就需要引入Refresh。可以在服务的运行期间重新加载配置文件,具体可以参考这篇文章:[配置中心svn示例和refresh](http://www.ityouknow.com/springcloud/2017/05/23/springcloud-config-svn-refresh.html) + +当所有的配置文件都存储在配置中心的时候,配置中心就成为了一个非常重要的组件。如果配置中心出现问题将会导致灾难性的后果,因此在生产中建议对配置中心做集群,来支持配置中心高可用性。具体参考:[配置中心服务化和高可用](http://www.ityouknow.com/springcloud/2017/05/25/springcloud-config-eureka.html) + +##### Spring Cloud Bus + +上面的Refresh方案虽然可以解决单个微服务运行期间重载配置信息的问题,但是在真正的实践生产中,可能会有N多的服务需要更新配置,如果每次依靠手动Refresh将是一个巨大的工作量,这时候Spring Cloud提出了另外一个解决方案:Spring Cloud Bus + +Spring Cloud Bus通过轻量消息代理连接各个分布的节点。这会用在广播状态的变化(例如配置变化)或者其它的消息指令中。Spring Cloud Bus的一个核心思想是通过分布式的启动器对Spring Boot应用进行扩展,也可以用来建立一个或多个应用之间的通信频道。目前唯一实现的方式是用AMQP消息代理作为通道。 + +Spring Cloud Bus是轻量级的通讯组件,也可以用在其它类似的场景中。有了Spring Cloud Bus之后,当我们改变配置文件提交到版本库中时,会自动的触发对应实例的Refresh,具体的工作流程如下: + +![img](../media/pictures/SpringCloud.assets/configbus2-1589793999251.jpg) + +也可以参考这篇文章来了解:[配置中心和消息总线](http://www.ityouknow.com/springcloud/2017/05/26/springcloud-config-eureka-bus.html) + +#### 服务网关 + +在微服务架构模式下,后端服务的实例数一般是动态的,对于客户端而言很难发现动态改变的服务实例的访问地址信息。因此在基于微服务的项目中为了简化前端的调用逻辑,通常会引入API Gateway作为轻量级网关,同时API Gateway中也会实现相关的认证逻辑从而简化内部服务之间相互调用的复杂度。 + +![img](../media/pictures/SpringCloud.assets/api_gateway-1589793999473.png) + +Spring Cloud体系中支持API Gateway落地的技术就是Zuul。Spring Cloud Zuul路由是微服务架构中不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。 + +它的具体作用就是服务转发,接收并转发所有内外部的客户端调用。使用Zuul可以作为资源的统一访问入口,同时也可以在网关做一些权限校验等类似的功能。 + +具体使用参考这篇文章:[服务网关zuul](http://www.ityouknow.com/springcloud/2017/06/01/gateway-service-zuul.html) + +#### 链路跟踪 + +随着服务的越来越多,对调用链的分析会越来越复杂,如服务之间的调用关系、某个请求对应的调用链、调用之间消费的时间等,对这些信息进行监控就成为一个问题。在实际的使用中我们需要监控服务和服务之间通讯的各项指标,这些数据将是我们改进系统架构的主要依据。因此分布式的链路跟踪就变的非常重要,Spring Cloud也给出了具体的解决方案:Spring Cloud Sleuth和Zipkin + +![img](../media/pictures/SpringCloud.assets/dyl.png) + +Spring Cloud Sleuth为服务之间调用提供链路追踪。通过Sleuth可以很清楚的了解到一个服务请求经过了哪些服务,每个服务处理花费了多长时间。从而让我们可以很方便的理清各微服务间的调用关系。 + +Zipkin是Twitter的一个开源项目,允许开发者收集 Twitter 各个服务上的监控数据,并提供查询接口 + +分布式链路跟踪需要Sleuth+Zipkin结合来实现,具体操作参考这篇文章:[分布式链路跟踪(Sleuth)](http://www.jianshu.com/p/c3d191663279) + +#### 总结 + +我们从整体上来看一下Spring Cloud各个组件如何来配套使用: + +![img](../media/pictures/SpringCloud.assets/spring_cloud_structure.png) + +从上图可以看出Spring Cloud各个组件相互配合,合作支持了一套完整的微服务架构。 + +- 其中Eureka负责服务的注册与发现,很好将各服务连接起来 +- Hystrix 负责监控服务之间的调用情况,连续多次失败进行熔断保护。 +- Hystrix dashboard,Turbine 负责监控 Hystrix的熔断情况,并给予图形化的展示 +- Spring Cloud Config 提供了统一的配置中心服务 +- 当配置文件发生变化的时候,Spring Cloud Bus 负责通知各服务去获取最新的配置信息 +- 所有对外的请求和服务,我们都通过Zuul来进行转发,起到API网关的作用 +- 最后我们使用Sleuth+Zipkin将所有的请求数据记录下来,方便我们进行后续分析 + +Spring Cloud从设计之初就考虑了绝大多数互联网公司架构演化所需的功能,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等。这些功能都是以插拔的形式提供出来,方便我们系统架构演进的过程中,可以合理的选择需要的组件进行集成,从而在架构演进的过程中会更加平滑、顺利。 + +微服务架构是一种趋势,Spring Cloud提供了标准化的、全站式的技术方案,意义可能会堪比当前Servlet规范的诞生,有效推进服务端软件系统技术水平的进步。 + + + +## 阿里Dubbo疯狂更新,关Spring Cloud什么事? + +最近,开源社区发生了一件大事,那个全国 Java 开发者使用最广的开源服务框架 Dubbo 低调重启维护,并且 3 个月连续发布了 4 个维护版本。 + +我上次在写[放弃Dubbo,选择最流行的Spring Cloud微服务架构实践与经验总结](http://mp.weixin.qq.com/s/bciSlKearaVFQg1QWOSn_g)这篇文章的时候,就有很多的网友给我留言说,Dubbo 又开始更新了。我当然是清楚的,我也一直在关注着 Dubbo 的走向,在几个月前技术圈里面就有一个消息说是 Dubbo 又开始更新了,大家议论纷纷不知真伪。我还专门跑到 GitHub 上面进行了留言询问,最后在 Dubbo 的 gitter 聊天室里面找到了确信的答案,说是正在组建团队。虽然稍稍有所期待,但也不知道阿里这次拿出了多少的诚意来做这件事,于是我昨天又到 GitHub 逛了一下,发现从 9 月开始,阿里三个月连着发布了四个版本,还是非常有诚意的,值得关注。 + +### Dubbo简介 + +Dubbo 是阿里巴巴公司一个开源的高性能服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,以及 SOA 服务治理方案,使得应用可通过高性能 RPC 实现服务的输出、输入功能和 Spring 框架无缝集成。Dubbo 包含远程通讯、集群容错和自动发现三个核心部分。 + +它提供透明化的远程方法调用,实现像调用本地方法一样调用远程方法,只需简单配置,没有任何 API 侵入。同时它具备软负载均衡及容错机制,可在内网替代 F5 等硬件负载均衡器,降低成本,减少单点。它可以实现服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的 IP 地址,并且能够平滑添加或删除服务提供者。 + +2011 年末,阿里巴巴在 GitHub 上开源了基于 Java 的分布式服务治理框架 Dubbo,之后它成为了国内该类开源项目的佼佼者,许多开发者对其表示青睐。同时,先后有不少公司在实践中基于 Dubbo 进行分布式系统架构。目前在 GitHub 上,它的 fork、star 数均已破万。 + +**Dubbo核心功能**: + +- 远程通讯,提供对多种基于长连接的 NIO 框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。 +- 集群容错,提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。 +- 自动发现,基于注册中心目录服务,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。 + +![img](../media/pictures/SpringCloud.assets/dubbo-architecture.png) + +### Dubbo发展史 + +**发展到开源** + +2008 年底在阿里内部开始规划调用,2009 年初开发 1.0 版本;2010 年 04 月在 1.0 的版本之上进行了重构,发布了 2.0 版本;2011 年 10 月阿里宣布将 Dubbo 开源,开源的第一个版本为版本 **dubbo-2.0.7**。 + +**开源成长** + +Dubbo 开源之后,框架发展比较迅速,几乎两三个月会发布一个版本,于 2012 年 3 月 14 号发布版本 dubbo-2.1.0。随后又进入另一个快速发展期,版本发布频繁,几乎每一个月会发布好几次。直到 2013 年 3 月 17 号发布了 dubbo-2.4.10,版本陷入停滞;2014 年 10 月 30 号发布版本 dubbo-2.4.11,修复了一个小 Bug,版本又陷入漫长的停滞到现在。 + +**阿里之外的发展** + +2014 年的 10 月 20 号,当当网 Fork 了阿里的一个 Dubbo 版本开始维护,并命名为 dubbox-2.8.0。值得注意的是,当当网扩展 Dubbo 服务框架支持 REST 风格远程调用,并且跟随着 ZooKeepe 和 Spring 升级了对应的版本。之后 Dubbox 一直在小版本维护,2015 年 3 月 31 号发布了最后一个版本 **dubbox-2.8.4**。 + +![img](../media/pictures/SpringCloud.assets/dubbox_rest.jpg) + +### Dubbo团队这三个月都做了什么 + +目前 Dubbo 的主力开发以阿里巴巴中间件团队为主,同时在阿里内部也招募了一些对 Dubbo 感兴趣的同事。大家要知道,Dubbo 距离今年开始维护的上一个版本是什么时间发布的吗?是 2014 年 10 月 30 号,差了整整将近 3 年,Dubbo 所依赖的 Jdk、Spring、Zookeeper、Zkclient 等等不知道都更新了多少个版本。因此阿里恢复更新的第一步就是适配所依赖的各组件版本,让 Dubbo 所依赖的基础环境不要太落伍,另外也 Fixed 掉了一些严重的 Bug。 + +**dubbo-2.5.4/5版本** + +2017 年 9 月,阿里发布了 dubbo-2.5.4/5 版本,更新内容如下: + +**依赖升级** + +| 依赖 | 当前版本 | 目标版本 | 影响点 | +| :-------------- | :------------- | :------------- | :--------------------------- | +| spring | 3.2.16.RELEASE | 4.3.10.RELEASE | schema配置解析;Http RPC协议 | +| zookeeper | 3.3.3 | 3.4.9 | 常用注册中心 | +| zkclient | 0.1 0.10 | zookeeper | 客户端工具 | +| curator | 1.1.16 | 2.12.0 | zookeeper客户端工具 | +| commons-logging | 1.1.1 | 1.2 | 日志实现集成 | +| hessian | 4.0.6 | 4.0.38 | hessian RPC协议 | +| jedis | 2.1.0 | 2.9.0 | redis注册中心;缓存RPC | +| httpclient | 4.1.2 | 4.5.3 | hessian等用http连接池 | +| validator | 1.0.0 | 1.1.0.Final | java validation规范 | +| cxf | 2.6.1 | 3.0.14 | webservice | +| jcache | 0.4 | 1.0.0 | jcache规范 | + +这版在升级相关依赖版本的同时,以问题反馈频率和影响面排定优先级,优先解决了几个反馈最多、影响较大的一些缺陷,包括优雅停机、异步调用、动态配置、MonitorFilter 监控统计等问题。 + +**dubbo-2.5.6版本** + +2017 年 10 月,阿里发布了 dubbo-2.5.6 版本,又 Fixed 掉了一大批严重的 Bug。 + +**发布内容** + +- 泛化调用PojoUtils工具类不能正确处理枚举类型、私有字段等问题 +- provider业务线程池满后,拒绝请求的异常无法通知到consumer端 +- 业务返回值payload超阈值时,payload被重复发送回consumer +- slf4jLogger正确输出log调用实际所在行号 +- 延迟(delay)暴露存在潜在并发问题,导致不同服务占用多个端口 +- 无provider时,consumer端mock逻辑不能生效 +- 一些小优化:OverrideListener监听逻辑、provider端关闭心跳请求、Main启动类停机逻辑等 +- 一些小bug修复:动态配置不能删除、telnet支持泛型json调用、monitor统计错误等 + +**dubbo-2.5.7版本** + +2017 年 11 月,也就是 12 天前,阿里发布了 dubbo-2.5.7。这次不但修复了一批主要的 Bug,还做了一处小功能的完善。 + +**发布内容** + +- 完善注解配置方式,修复社区反馈的旧版注解bug +- 支持启动时从环境变量读取注册ip port、绑定ip port,支持社区反馈的容器化部署场景等 +- 调整、开放一些不完善的xml配置项,如dump.directory等 +- 解决启动阶段zk无法连接导致应用无限阻塞的问题 +- 解决zk无法连接时,MonitorService初次访问会导致rpc请求阻塞问题 #672 +- 内部json实现标记deprecate,转为依赖开源fastjson实现 +- RMI协议支持传递attachments +- Hessian支持EnumSet类型序列化 +- 社区反馈的一些小bug修复及优化 + +这次版本发布内容较多,因此还给出了升级建议。 + +**升级请注意** + +- 此次升级存在以下不兼容或需要注意点,但对核心功能均无影响,且只需添加依赖或遵守配置规则即可避免。这里只是把潜在的注意点列出来,如果你没用到这些功能无需额外关注。 +- AccesslogFilter、telnet、mock等部分功能依赖了老版JSON实现,如开启以上功能,升级后请添加fastjson作为第三方依赖。 +- 此次升级完善了注解配置方式,同时保留了老的注解配置代码,如工程从之前的老版本注解配置转到2.5.7版本,请确保删除老的注解扫描配置,使用新的配置形式。 +- 在工程启动阶段,如遇到zk不可达,当前版本的行为是使用注册中心缓存继续启动(具体由check参数决定。 MonitorService初次调用,如遇zk不可达,当前版本会忽略monitor数据上传,以避免阻塞rpc主流程。 + +**重点** + +在 2.5.7 版本更新的同时还给出了下一步的预告,近期即将提供 dubbo-spring-boot-starter 启动配置模块。 + +这个提示说明了两个事情: + +- Dubbo 还会继续完善,后续会开发很多的新的功能,所以希望大家关注。 +- 说明 Spring Boot 的影响力也越来越大,各种牛逼的开源软件纷纷给出了支持,现在也将包括 Dubbo。 + +### Dubbo 下一步会做什么? + +**根据阿里技术的信息,最近三个版本会做的事情如下:** + +- 优先解决社区使用过程中的问题和框架的缺陷,吸收社区贡献的新特性,解决文档访问和不全面的问题。 +- 提供服务延迟暴乱、优雅停机 API 接口支持 RESTFULE 风格服务调用,提供 netty http 的支持,集成高性能序列化协议。 +- 路由功能优化、消费端异步功能优化、提供端异步调用支持注册中心推送通知异步、合并处理改造等。 + +**未来计划**: + +重构动态配置模块,动态配置和注册中心分离,集成流行的开源分布式配置管理框架,服务元数据注册与注册中心分离,丰富元数据内容,适配流行的 consul etcd 等注册中心方案。考虑提供 opentrace、oauth2、metrics、health、gateway 等部分服务化基础组件的支持,服务治理平台 OPS 重做,除代码、UI 重构外,期望能提供更强的服务测试、健康检查、服务动态治理等特性。Dubbo 模块化,各个模块可单独打包、单独依赖,集群熔断和自动故障检测能力。 + +继续在 Dubbo 框架现代化、国际化这两个大的方向上进行探索。现代化方面主要是考虑到目前微服务架构以及容器化日渐流行的大趋势,Dubbo 作为 RPC 框架如何很好地融入其中,成为其生态体系中不可或缺的一个组件。**强调的是 Dubbo 未来的定位并不是要成为一个微服务的全面解决方案,而是专注在 RPC 领域,成为微服务生态体系中的一个重要组件。**至于大家关注的微服务化衍生出的服务治理需求, Dubbo 将积极适配开源解决方案,甚至启动独立的开源项目予以支持。 + +### Dubbo和Spring Cloud有何不同? + +首先做一个简单的功能对比: + +| | Dubbo | Spring Cloud | +| :----------- | :------------ | :--------------------------- | +| 服务注册中心 | Zookeeper | Spring Cloud Netflix Eureka | +| 服务调用方式 | RPC | REST API | +| 服务监控 | Dubbo-monitor | Spring Boot Admin | +| 断路器 | 不完善 | Spring Cloud Netflix Hystrix | +| 服务网关 | 无 | Spring Cloud Netflix Zuul | +| 分布式配置 | 无 | Spring Cloud Config | +| 服务跟踪 | 无 | Spring Cloud Sleuth | +| 消息总线 | 无 | Spring Cloud Bus | +| 数据流 | 无 | Spring Cloud Stream | +| 批量任务 | 无 | Spring Cloud Task | +| …… | …… | …… | + +**从上图可以看出其实Dubbo的功能只是Spring Cloud体系的一部分。** + +这样对比是不够公平的,首先 Dubbo 是 SOA 时代的产物,它的关注点主要在于服务的调用,流量分发、流量监控和熔断。而 Spring Cloud 诞生于微服务架构时代,考虑的是微服务治理的方方面面,另外由于依托了 Spring、Spring Boot 的优势之上,两个框架在开始目标就不一致,Dubbo 定位服务治理、Spring Cloud 是一个生态。 + +**如果仅仅关注于服务治理的这个层面,Dubbo其实还优于Spring Cloud很多:** + +- Dubbo 支持更多的协议,如:rmi、hessian、http、webservice、thrift、memcached、redis 等。 +- Dubbo 使用 RPC 协议效率更高,在极端压力测试下,Dubbo 的效率会高于 Spring Cloud 效率一倍多。 +- Dubbo 有更强大的后台管理,Dubbo 提供的后台管理 Dubbo Admin 功能强大,提供了路由规则、动态配置、访问控制、权重调节、均衡负载等诸多强大的功能。 +- 可以限制某个 IP 流量的访问权限,设置不同服务器分发不同的流量权重,并且支持多种算法,利用这些功能我们可以在线上做灰度发布、故障转移等,Spring Cloud 到现在还不支持灰度发布、流量权重等功能。 + +![img](../media/pictures/SpringCloud.assets/dubbo_admin.png) + +**所以Dubbo专注于服务治理;Spring Cloud关注于微服务架构生态。** + +### Dubbo发布对Spring Cloud有影响吗? + +国内技术人喜欢拿 Dubbo 和 Spring Cloud 进行对比,是因为两者都是服务治理非常优秀的开源框架。但它们两者的出发点是不一样的,Dubbo 关注于服务治理这块并且以后也会继续往这个方向去发展。Spring Cloud 关注的是微服务治理的生态。因为微服务治理的方方面面都是它所关注的内容,服务治理也只是微服务生态的一部分而已。因此可以大胆的断定,Dubbo 未来会在服务治理方面更为出色,而 Spring Cloud 在微服务治理上面无人能敌。 + +同时根据 Dubbo 最新的更新技术来看,Dubbo 也会积极的拥抱开源,拥抱新技术。Dubbo 接下来的版本将会很快的支持 Spring Boot,方便我们享受高效开发的同时,也可以支持高效的服务调用。Dubbo 被广泛应用于中国各互联网公司,如今阿里又重新重视起来并且发布了新版本和一系列的计划,对于正在使用 Dubbo 的公司来说是一个喜讯,对于中国广大的开发者来说更是一件非常喜悦的事情。我们非常乐于看到中国有一款非常优秀的开源框架,让我们有更多的选择,有更好的支持。 + +**两者其实不一定有竞争关系,如果使用得当甚至可以互补;另外两个关注的领域也不一致,因此对 Spring Cloud 的影响甚微。** + +### 如何选择? + +可能很多人正在犹豫,在服务治理的时候应该选择那个框架呢?如果公司对效率有极高的要求建议使用 Dubbo,相对比 RPC 的效率会比 HTTP 高很多;如果团队不想对技术架构做大的改造建议使用 Dubbo,Dubbo 仅仅需要少量的修改就可以融入到内部系统的架构中。但如果技术团队喜欢挑战新技术,建议选择 Spring Cloud,Spring Cloud 架构体系有有趣很酷的技术。如果公司选择微服务架构去重构整个技术体系,那么 Spring Cloud 是当仁不让之选,它可以说是目前最好的微服务框架没有之一。 + +最后,技术选型是一个综合的问题,需要考虑团队的情况、业务的发展以及公司的产品特征。最炫最酷的技术并不一定是最好的,选择适合自己团队、符合公司业务的框架才是最佳方案。技术的发展永远没有尽头,因此我们对技术也要保持空杯、保持饥饿、保持敬畏! + + + +## SpringCloud(十一):服务网关Zuul高级篇 + +时间过的很快,写[springcloud(十):服务网关zuul初级篇](http://www.ityouknow.com/springcloud/2017/06/01/gateway-service-zuul.html)还在半年前,现在已经是2018年了,我们继续探讨Zuul更高级的使用方式。 + +上篇文章主要介绍了Zuul网关使用模式,以及自动转发机制,但其实Zuul还有更多的应用场景,比如:鉴权、流量转发、请求统计等等,这些功能都可以使用Zuul来实现。 + +### Zuul的核心 + +Filter是Zuul的核心,用来实现对外服务的控制。Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”,整个生命周期可以用下图来表示。 + +![img](../media/pictures/SpringCloud.assets/zuul-core.png) + +Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。 + +- **PRE:** 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。 +- **ROUTING:**这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。 +- **POST:**这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。 +- **ERROR:**在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。 + +#### Zuul中默认实现的Filter + +| 类型 | 顺序 | 过滤器 | 功能 | +| :---- | :--- | :---------------------- | :------------------------- | +| pre | -3 | ServletDetectionFilter | 标记处理Servlet的类型 | +| pre | -2 | Servlet30WrapperFilter | 包装HttpServletRequest请求 | +| pre | -1 | FormBodyWrapperFilter | 包装请求体 | +| route | 1 | DebugFilter | 标记调试标志 | +| route | 5 | PreDecorationFilter | 处理请求上下文供后续使用 | +| route | 10 | RibbonRoutingFilter | serviceId请求转发 | +| route | 100 | SimpleHostRoutingFilter | url请求转发 | +| route | 500 | SendForwardFilter | forward请求转发 | +| post | 0 | SendErrorFilter | 处理有错误的请求响应 | +| post | 1000 | SendResponseFilter | 处理正常的请求响应 | + +**禁用指定的Filter** + +可以在application.yml中配置需要禁用的filter,格式: + +``` +zuul: + FormBodyWrapperFilter: + pre: + disable: true +``` + +### 自定义Filter + +实现自定义Filter,需要继承ZuulFilter的类,并覆盖其中的4个方法。 + +``` +public class MyFilter extends ZuulFilter { + @Override + String filterType() { + return "pre"; //定义filter的类型,有pre、route、post、error四种 + } + + @Override + int filterOrder() { + return 10; //定义filter的顺序,数字越小表示顺序越高,越先执行 + } + + @Override + boolean shouldFilter() { + return true; //表示是否需要执行该filter,true表示执行,false表示不执行 + } + + @Override + Object run() { + return null; //filter需要执行的具体操作 + } +} +``` + +### 自定义Filter示例 + +我们假设有这样一个场景,因为服务网关应对的是外部的所有请求,为了避免产生安全隐患,我们需要对请求做一定的限制,比如请求中含有Token便让请求继续往下走,如果请求不带Token就直接返回并给出提示。 + +首先自定义一个Filter,在run()方法中验证参数是否含有Token。 + +``` +public class TokenFilter extends ZuulFilter { + + private final Logger logger = LoggerFactory.getLogger(TokenFilter.class); + + @Override + public String filterType() { + return "pre"; // 可以在请求被路由之前调用 + } + + @Override + public int filterOrder() { + return 0; // filter执行顺序,通过数字指定 ,优先级为0,数字越大,优先级越低 + } + + @Override + public boolean shouldFilter() { + return true;// 是否执行该过滤器,此处为true,说明需要过滤 + } + + @Override + public Object run() { + RequestContext ctx = RequestContext.getCurrentContext(); + HttpServletRequest request = ctx.getRequest(); + + logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString()); + + String token = request.getParameter("token");// 获取请求的参数 + + if (StringUtils.isNotBlank(token)) { + ctx.setSendZuulResponse(true); //对请求进行路由 + ctx.setResponseStatusCode(200); + ctx.set("isSuccess", true); + return null; + } else { + ctx.setSendZuulResponse(false); //不对其进行路由 + ctx.setResponseStatusCode(400); + ctx.setResponseBody("token is empty"); + ctx.set("isSuccess", false); + return null; + } + } + +} +``` + +将TokenFilter加入到请求拦截队列,在启动类中添加以下代码: + +``` +@Bean +public TokenFilter tokenFilter() { + return new TokenFilter(); +} +``` + +这样就将我们自定义好的Filter加入到了请求拦截中。 + +**测试** + +我们依次启动示例项目:`spring-cloud-eureka`、`spring-cloud-producer`、`spring-cloud-zuul`,这个三个项目均为上一篇示例项目,`spring-cloud-zuul`稍微进行改造。 + +访问地址:`http://localhost:8888/spring-cloud-producer/hello?name=neo`,返回:token is empty ,请求被拦截返回。 +访问地址:`http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx`,返回:hello neo,this is first messge,说明请求正常响应。 + +通过上面这例子我们可以看出,我们可以使用“PRE”类型的Filter做很多的验证工作,在实际使用中我们可以结合shiro、oauth2.0等技术去做鉴权、验证。 + +### 路由熔断 + +当我们的后端服务出现异常的时候,我们不希望将异常抛出给最外层,期望服务可以自动进行一降级。Zuul给我们提供了这样的支持。当某个服务出现异常时,直接返回我们预设的信息。 + +我们通过自定义的fallback方法,并且将其指定给某个route来实现该route访问出问题的熔断处理。主要继承ZuulFallbackProvider接口来实现,ZuulFallbackProvider默认有两个方法,一个用来指明熔断拦截哪个服务,一个定制返回内容。 + +``` +public interface ZuulFallbackProvider { + /** + * The route this fallback will be used for. + * @return The route the fallback will be used for. + */ + public String getRoute(); + + /** + * Provides a fallback response. + * @return The fallback response. + */ + public ClientHttpResponse fallbackResponse(); +} +``` + +实现类通过实现getRoute方法,告诉Zuul它是负责哪个route定义的熔断。而fallbackResponse方法则是告诉 Zuul 断路出现时,它会提供一个什么返回值来处理请求。 + +后来Spring又扩展了此类,丰富了返回方式,在返回的内容中添加了异常信息,因此最新版本建议直接继承类`FallbackProvider` 。 + +我们以上面的spring-cloud-producer服务为例,定制它的熔断返回内容。 + +``` +@Component +public class ProducerFallback implements FallbackProvider { + private final Logger logger = LoggerFactory.getLogger(FallbackProvider.class); + + //指定要处理的 service。 + @Override + public String getRoute() { + return "spring-cloud-producer"; + } + + public ClientHttpResponse fallbackResponse() { + return new ClientHttpResponse() { + @Override + public HttpStatus getStatusCode() throws IOException { + return HttpStatus.OK; + } + + @Override + public int getRawStatusCode() throws IOException { + return 200; + } + + @Override + public String getStatusText() throws IOException { + return "OK"; + } + + @Override + public void close() { + + } + + @Override + public InputStream getBody() throws IOException { + return new ByteArrayInputStream("The service is unavailable.".getBytes()); + } + + @Override + public HttpHeaders getHeaders() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + return headers; + } + }; + } + + @Override + public ClientHttpResponse fallbackResponse(Throwable cause) { + if (cause != null && cause.getCause() != null) { + String reason = cause.getCause().getMessage(); + logger.info("Excption {}",reason); + } + return fallbackResponse(); + } +} +``` + +当服务出现异常时,打印相关异常信息,并返回”The service is unavailable.”。 + +启动项目spring-cloud-producer-2,这时候服务中心会有两个spring-cloud-producer项目,我们重启Zuul项目。再手动关闭spring-cloud-producer-2项目,多次访问地址:`http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx`,会交替返回: + +``` +hello neo,this is first messge +The service is unavailable. +... +``` + +根据返回结果可以看出:spring-cloud-producer-2项目已经启用了熔断,返回:`The service is unavailable.` + +> Zuul 目前只支持服务级别的熔断,不支持具体到某个URL进行熔断。 + +### 路由重试 + +有时候因为网络或者其它原因,服务可能会暂时的不可用,这个时候我们希望可以再次对服务进行重试,Zuul也帮我们实现了此功能,需要结合Spring Retry 一起来实现。下面我们以上面的项目为例做演示。 + +**添加Spring Retry依赖** + +首先在spring-cloud-zuul项目中添加Spring Retry依赖。 + +``` + + org.springframework.retry + spring-retry + +``` + +**开启Zuul Retry** + +再配置文件中配置启用Zuul Retry + +``` +#是否开启重试功能 +zuul.retryable=true +#对当前服务的重试次数 +ribbon.MaxAutoRetries=2 +#切换相同Server的次数 +ribbon.MaxAutoRetriesNextServer=0 +``` + +这样我们就开启了Zuul的重试功能。 + +**测试** + +我们对spring-cloud-producer-2进行改造,在hello方法中添加定时,并且在请求的一开始打印参数。 + +``` +@RequestMapping("/hello") +public String index(@RequestParam String name) { + logger.info("request two name is "+name); + try{ + Thread.sleep(1000000); + }catch ( Exception e){ + logger.error(" hello two error",e); + } + return "hello "+name+",this is two messge"; +} +``` + +重启 spring-cloud-producer-2和spring-cloud-zuul项目。 + +访问地址:`http://localhost:8888/spring-cloud-producer/hello?name=neo&token=xx`,当页面返回:`The service is unavailable.`时查看项目spring-cloud-producer-2后台日志如下: + +``` +2018-01-22 19:50:32.401 INFO 19488 --- [io-9001-exec-14] o.s.c.n.z.f.route.FallbackProvider : request two name is neo +2018-01-22 19:50:33.402 INFO 19488 --- [io-9001-exec-15] o.s.c.n.z.f.route.FallbackProvider : request two name is neo +2018-01-22 19:50:34.404 INFO 19488 --- [io-9001-exec-16] o.s.c.n.z.f.route.FallbackProvider : request two name is neo +``` + +说明进行了三次的请求,也就是进行了两次的重试。这样也就验证了我们的配置信息,完成了Zuul的重试功能。 + +**注意** + +开启重试在某些情况下是有问题的,比如当压力过大,一个实例停止响应时,路由将流量转到另一个实例,很有可能导致最终所有的实例全被压垮。说到底,断路器的其中一个作用就是防止故障或者压力扩散。用了retry,断路器就只有在该服务的所有实例都无法运作的情况下才能起作用。这种时候,断路器的形式更像是提供一种友好的错误信息,或者假装服务正常运行的假象给使用者。 + +不用retry,仅使用负载均衡和熔断,就必须考虑到是否能够接受单个服务实例关闭和eureka刷新服务列表之间带来的短时间的熔断。如果可以接受,就无需使用retry。 + +### Zuul高可用 + +![img](../media/pictures/SpringCloud.assets/zuul-case.png) + +我们实际使用Zuul的方式如上图,不同的客户端使用不同的负载将请求分发到后端的Zuul,Zuul在通过Eureka调用后端服务,最后对外输出。因此为了保证Zuul的高可用性,前端可以同时启动多个Zuul实例进行负载,在Zuul的前端使用Nginx或者F5进行负载转发以达到高可用性。 + + + +## SpringCloud(十二):使用Spring Cloud Sleuth和Zipkin进行分布式链路跟踪 + +随着业务发展,系统拆分导致系统调用链路愈发复杂一个前端请求可能最终需要调用很多次后端服务才能完成,当整个请求变慢或不可用时,我们是无法得知该请求是由某个或某些后端服务引起的,这时就需要解决如何快读定位服务故障点,以对症下药。于是就有了分布式系统调用跟踪的诞生。 + +现今业界分布式服务跟踪的理论基础主要来自于 Google 的一篇论文[《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》](https://research.google.com/pubs/pub36356.html),使用最为广泛的开源实现是 Twitter 的 Zipkin,为了实现平台无关、厂商无关的分布式服务跟踪,CNCF 发布了布式服务跟踪标准 Open Tracing。国内,淘宝的“鹰眼”、京东的“Hydra”、大众点评的“CAT”、新浪的“Watchman”、唯品会的“Microscope”、窝窝网的“Tracing”都是这样的系统。 + +### Spring Cloud Sleuth + +一般的,一个分布式服务跟踪系统,主要有三部分:数据收集、数据存储和数据展示。根据系统大小不同,每一部分的结构又有一定变化。譬如,对于大规模分布式系统,数据存储可分为实时数据和全量数据两部分,实时数据用于故障排查(troubleshooting),全量数据用于系统优化;数据收集除了支持平台无关和开发语言无关系统的数据收集,还包括异步数据收集(需要跟踪队列中的消息,保证调用的连贯性),以及确保更小的侵入性;数据展示又涉及到数据挖掘和分析。虽然每一部分都可能变得很复杂,但基本原理都类似。 + +![img](../media/pictures/SpringCloud.assets/tracing1.png) + +服务追踪的追踪单元是从客户发起请求(request)抵达被追踪系统的边界开始,到被追踪系统向客户返回响应(response)为止的过程,称为一个“trace”。每个 trace 中会调用若干个服务,为了记录调用了哪些服务,以及每次调用的消耗时间等信息,在每次调用服务时,埋入一个调用记录,称为一个“span”。这样,若干个有序的 span 就组成了一个 trace。在系统向外界提供服务的过程中,会不断地有请求和响应发生,也就会不断生成 trace,把这些带有span 的 trace 记录下来,就可以描绘出一幅系统的服务拓扑图。附带上 span 中的响应时间,以及请求成功与否等信息,就可以在发生问题的时候,找到异常的服务;根据历史数据,还可以从系统整体层面分析出哪里性能差,定位性能优化的目标。 + +Spring Cloud Sleuth为服务之间调用提供链路追踪。通过Sleuth可以很清楚的了解到一个服务请求经过了哪些服务,每个服务处理花费了多长。从而让我们可以很方便的理清各微服务间的调用关系。此外Sleuth可以帮助我们: + +- 耗时分析: 通过Sleuth可以很方便的了解到每个采样请求的耗时,从而分析出哪些服务调用比较耗时; +- 可视化错误: 对于程序未捕捉的异常,可以通过集成Zipkin服务界面上看到; +- 链路优化: 对于调用比较频繁的服务,可以针对这些服务实施一些优化措施。 + +spring cloud sleuth可以结合zipkin,将信息发送到zipkin,利用zipkin的存储来存储信息,利用zipkin ui来展示数据。 + +这是Spring Cloud Sleuth的概念图: + +![img](../media/pictures/SpringCloud.assets/tracing2.png) + +### ZipKin + +Zipkin 是一个开放源代码分布式的跟踪系统,由Twitter公司开源,它致力于收集服务的定时数据,以解决微服务架构中的延迟问题,包括数据的收集、存储、查找和展现。 + +每个服务向zipkin报告计时数据,zipkin会根据调用关系通过Zipkin UI生成依赖关系图,显示了多少跟踪请求通过每个服务,该系统让开发者可通过一个 Web 前端轻松的收集和分析数据,例如用户每次请求服务的处理时间等,可方便的监测系统中存在的瓶颈。 + +Zipkin提供了可插拔数据存储方式:In-Memory、MySql、Cassandra以及Elasticsearch。接下来的测试为方便直接采用In-Memory方式进行存储,生产推荐Elasticsearch。 + +### 快速上手 + +#### 创建zipkin-server项目 + +**项目依赖** + +``` + + + org.springframework.cloud + spring-cloud-starter-eureka + + + io.zipkin.java + zipkin-server + + + io.zipkin.java + zipkin-autoconfigure-ui + + +``` + +**启动类** + +``` +@SpringBootApplication +@EnableEurekaClient +@EnableZipkinServer +public class ZipkinApplication { + + public static void main(String[] args) { + SpringApplication.run(ZipkinApplication.class, args); + } + +} +``` + +使用了`@EnableZipkinServer`注解,启用Zipkin服务。 + +**配置文件** + +``` +eureka: + client: + serviceUrl: + defaultZone: http://localhost:8761/eureka/ +server: + port: 9000 +spring: + application: + name: zipkin-server +``` + +配置完成后依次启动示例项目:`spring-cloud-eureka`、`zipkin-server`项目。刚问地址:`http://localhost:9000/zipkin/`可以看到Zipkin后台页面 + +![img](../media/pictures/SpringCloud.assets/tracing3.png) + +#### 项目添加zipkin支持 + +在项目`spring-cloud-producer`和`spring-cloud-zuul`中添加zipkin的支持。 + +``` + + org.springframework.cloud + spring-cloud-starter-zipkin + +``` + +Spring应用在监测到Java依赖包中有sleuth和zipkin后,会自动在RestTemplate的调用过程中向HTTP请求注入追踪信息,并向Zipkin Server发送这些信息。 + +同时配置文件中添加如下代码: + +``` +spring: + zipkin: + base-url: http://localhost:9000 + sleuth: + sampler: + percentage: 1.0 +``` + +spring.zipkin.base-url指定了Zipkin服务器的地址,spring.sleuth.sampler.percentage将采样比例设置为1.0,也就是全部都需要。 + +Spring Cloud Sleuth有一个Sampler策略,可以通过这个实现类来控制采样算法。采样器不会阻碍span相关id的产生,但是会对导出以及附加事件标签的相关操作造成影响。 Sleuth默认采样算法的实现是Reservoir sampling,具体的实现类是PercentageBasedSampler,默认的采样比例为: 0.1(即10%)。不过我们可以通过spring.sleuth.sampler.percentage来设置,所设置的值介于0.0到1.0之间,1.0则表示全部采集。 + +这两个项目添加zipkin之后,依次进行启动。 + +#### 进行验证 + +这样我们就模拟了这样一个场景,通过外部请求访问Zuul网关,Zuul网关去调用`spring-cloud-producer`对外提供的服务。 + +四个项目均启动后,在浏览器中访问地址:`http://localhost:8888/producer/hello?name=neo` 两次,然后再打开地址: `http://localhost:9000/zipkin/`点击对应按钮进行查看。 + +点击查找看到有两条记录 + +![img](../media/pictures/SpringCloud.assets/zipkin1.png) + +点击记录进去页面,可以看到每一个服务所耗费的时间和顺序 + +![img](../media/pictures/SpringCloud.assets/zipkin2.png) + +点击依赖分析,可以看到项目之间的调用关系 + +![img](../media/pictures/SpringCloud.assets/zipkin3.png) + +## SpringCloud(十三):注册中心 Consul 使用详解 + +在上个月我们知道 Eureka 2.X 遇到困难停止开发了,但其实对国内的用户影响甚小,一方面国内大都使用的是 Eureka 1.X 系列,另一方面 Spring Cloud 支持很多服务发现的软件,Eureka 只是其中之一,下面是 Spring Cloud 支持的服务发现软件以及特性对比: + +| Feature | euerka | Consul | zookeeper | etcd | +| :------------------- | :--------------------------- | :--------------------- | :-------------------- | :---------------- | +| 服务健康检查 | 可配支持 | 服务状态,内存,硬盘等 | (弱)长连接,keepalive | 连接心跳 | +| 多数据中心 | — | 支持 | — | — | +| kv 存储服务 | — | 支持 | 支持 | 支持 | +| 一致性 | — | raft | paxos | raft | +| cap | ap | cp | cp | cp | +| 使用接口(多语言能力) | http(sidecar) | 支持 http 和 dns | 客户端 | http/grpc | +| watch 支持 | 支持 long polling/大部分增量 | 全量/支持long polling | 支持 | 支持 long polling | +| 自身监控 | metrics | metrics | — | metrics | +| 安全 | — | acl /https | acl | https 支持(弱) | +| spring cloud 集成 | 已支持 | 已支持 | 已支持 | 已支持 | + +在以上服务发现的软件中,Euerka 和 Consul 使用最为广泛。如果大家对注册中心的概念和 Euerka 不太了解的话, 可以参考我前期的文章:[springcloud(二):注册中心Eureka ](http://www.ityouknow.com/springcloud/2017/05/10/springcloud-eureka.html),本篇文章主要给大家介绍 Spring Cloud Consul 的使用。 + +### Consul 介绍 + +Consul 是 HashiCorp 公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 等)。使用起来也较 为简单。Consul 使用 Go 语言编写,因此具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与 Docker 等轻量级容器可无缝配合。 + +**Consul 的优势:** + +- 使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接. 相比较而言, zookeeper 采用的是 Paxos, 而 etcd 使用的则是 Raft。 +- 支持多数据中心,内外网的服务采用不同的端口进行监听。 多数据中心集群可以避免单数据中心的单点故障,而其部署则需要考虑网络延迟, 分片等情况等。 zookeeper 和 etcd 均不提供多数据中心功能的支持。 +- 支持健康检查。 etcd 不提供此功能。 +- 支持 http 和 dns 协议接口。 zookeeper 的集成较为复杂, etcd 只支持 http 协议。 +- 官方提供 web 管理界面, etcd 无此功能。 +- 综合比较, Consul 作为服务注册和配置管理的新星, 比较值得关注和研究。 + +**特性:** + +- 服务发现 +- 健康检查 +- Key/Value 存储 +- 多数据中心 + +**Consul 角色** + +- client: 客户端, 无状态, 将 HTTP 和 DNS 接口请求转发给局域网内的服务端集群。 +- server: 服务端, 保存配置信息, 高可用集群, 在局域网内与本地客户端通讯, 通过广域网与其它数据中心通讯。 每个数据中心的 server 数量推荐为 3 个或是 5 个。 + +Consul 客户端、服务端还支持夸中心的使用,更加提高了它的高可用性。 + +![img](../media/pictures/SpringCloud.assets/consul-server-client.png) + +**Consul 工作原理:** + +![img](../media/pictures/SpringCloud.assets/consol_service.png) + +- 1、当 Producer 启动的时候,会向 Consul 发送一个 post 请求,告诉 Consul 自己的 IP 和 Port +- 2、Consul 接收到 Producer 的注册后,每隔10s(默认)会向 Producer 发送一个健康检查的请求,检验Producer是否健康 +- 3、当 Consumer 发送 GET 方式请求 /api/address 到 Producer 时,会先从 Consul 中拿到一个存储服务 IP 和 Port 的临时表,从表中拿到 Producer 的 IP 和 Port 后再发送 GET 方式请求 /api/address +- 4、该临时表每隔10s会更新,只包含有通过了健康检查的 Producer + +Spring Cloud Consul 项目是针对 Consul 的服务治理实现。Consul 是一个分布式高可用的系统,它包含多个组件,但是作为一个整体,在微服务架构中为我们的基础设施提供服务发现和服务配置的工具。 + +### Consul VS Eureka + +Eureka 是一个服务发现工具。该体系结构主要是客户端/服务器,每个数据中心有一组 Eureka 服务器,通常每个可用区域一个。通常 Eureka 的客户使用嵌入式 SDK 来注册和发现服务。对于非本地集成的客户,官方提供的 Eureka 一些 REST 操作 API,其它语言可以使用这些 API 来实现对 Eureka Server 的操作从而实现一个非 jvm 语言的 Eureka Client。 + +Eureka 提供了一个弱一致的服务视图,尽可能的提供服务可用性。当客户端向服务器注册时,该服务器将尝试复制到其它服务器,但不提供保证复制完成。服务注册的生存时间(TTL)较短,要求客户端对服务器心跳检测。不健康的服务或节点停止心跳,导致它们超时并从注册表中删除。服务发现可以路由到注册的任何服务,由于心跳检测机制有时间间隔,可能会导致部分服务不可用。这个简化的模型允许简单的群集管理和高可扩展性。 + +Consul 提供了一些列特性,包括更丰富的健康检查,键值对存储以及多数据中心。Consul 需要每个数据中心都有一套服务,以及每个客户端的 agent,类似于使用像 Ribbon 这样的服务。Consul agent 允许大多数应用程序成为 Consul 不知情者,通过配置文件执行服务注册并通过 DNS 或负载平衡器 sidecars 发现。 + +Consul 提供强大的一致性保证,因为服务器使用 Raft 协议复制状态 。Consul 支持丰富的健康检查,包括 TCP,HTTP,Nagios / Sensu 兼容脚本或基于 Eureka 的 TTL。客户端节点参与基于 Gossip 协议的健康检查,该检查分发健康检查工作,而不像集中式心跳检测那样成为可扩展性挑战。发现请求被路由到选举出来的 leader,这使他们默认情况下强一致性。允许客户端过时读取取使任何服务器处理他们的请求,从而实现像 Eureka 这样的线性可伸缩性。 + +Consul 强烈的一致性意味着它可以作为领导选举和集群协调的锁定服务。Eureka 不提供类似的保证,并且通常需要为需要执行协调或具有更强一致性需求的服务运行 ZooKeeper。 + +Consul 提供了支持面向服务的体系结构所需的一系列功能。这包括服务发现,还包括丰富的运行状况检查,锁定,密钥/值,多数据中心联合,事件系统和 ACL。Consul 和 consul-template 和 envconsul 等工具生态系统都试图尽量减少集成所需的应用程序更改,以避免需要通过 SDK 进行本地集成。Eureka 是一个更大的 Netflix OSS 套件的一部分,该套件预计应用程序相对均匀且紧密集成。因此 Eureka 只解决了一小部分问题,可以和 ZooKeeper 等其它工具可以一起使用。 + +Consul 强一致性(C)带来的是: + +服务注册相比 Eureka 会稍慢一些。因为 Consul 的 raft 协议要求必须过半数的节点都写入成功才认为注册成功 Leader 挂掉时,重新选举期间整个 Consul 不可用。保证了强一致性但牺牲了可用性。 + +Eureka 保证高可用(A)和最终一致性: + +服务注册相对要快,因为不需要等注册信息 replicate 到其它节点,也不保证注册信息是否 replicate 成功 当数据出现不一致时,虽然 A, B 上的注册信息不完全相同,但每个 Eureka 节点依然能够正常对外提供服务,这会出现查询服务信息时如果请求 A 查不到,但请求 B 就能查到。如此保证了可用性但牺牲了一致性。 + +其它方面,eureka 就是个 servlet 程序,跑在 servlet 容器中; Consul 则是 go 编写而成。 + +### Consul 安装 + +Consul 不同于 Eureka 需要单独安装,访问[Consul 官网](https://www.consul.io/downloads.html)下载 Consul 的最新版本,我这里是 consul_1.2.1。 + +根据不同的系统类型选择不同的安装包,从下图也可以看出 Consul 支持所有主流系统。 + +![img](../media/pictures/SpringCloud.assets/consul_insall.png) + +我这里以 Windows 为例,下载下来是一个 consul_1.2.1_windows_amd64.zip 的压缩包,解压是是一个 consul.exe 的执行文件。 + +![img](../media/pictures/SpringCloud.assets/consul_win.png) + +cd 到对应的目录下,使用 cmd 启动 Consul + +``` +cd D:\Common Files\consul +#cmd启动: +consul agent -dev # -dev表示开发模式运行,另外还有-server表示服务模式运行 +``` + +为了方便期间,可以在同级目录下创建一个 run.bat 脚本来启动,脚本内容如下: + +``` +consul agent -dev +pause +``` + +启动结果如下: + +![img](../media/pictures/SpringCloud.assets/consol_cmd.png) + +启动成功之后访问:`http://localhost:8500`,可以看到 Consul 的管理界面 + +![img](../media/pictures/SpringCloud.assets/consol_manage.png) + +这样就意味着我们的 Consul 服务启动成功了。 + +### Consul 服务端 + +接下来我们开发 Consul 的服务端,我们创建一个 spring-cloud-consul-producer 项目 + +#### 添加依赖包 + +依赖包如下: + +``` + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.cloud + spring-cloud-starter-consul-discovery + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +- `spring-boot-starter-actuator` 健康检查依赖于此包。 +- `spring-cloud-starter-consul-discovery` Spring Cloud Consul 的支持。 + +Spring Boot 版本使用的是 2.0.3.RELEASE,Spring Cloud 最新版本是 Finchley.RELEASE 依赖于 Spring Boot 2.x. + +``` + + org.springframework.boot + spring-boot-starter-parent + 2.0.3.RELEASE + + + + + + + org.springframework.cloud + spring-cloud-dependencies + ${spring-cloud.version} + pom + import + + + +``` + +完整的 pom.xml 文件大家可以参考示例源码。 + +#### 配置文件 + +配置文件内容如下 + +``` +spring.application.name=spring-cloud-consul-producer +server.port=8501 +spring.cloud.consul.host=localhost +spring.cloud.consul.port=8500 +#注册到consul的服务名称 +spring.cloud.consul.discovery.serviceName=service-producer +``` + +Consul 的地址和端口号默认是 localhost:8500 ,如果不是这个地址可以自行配置。 `spring.cloud.consul.discovery.serviceName` 是指注册到 Consul 的服务名称,后期客户端会根据这个名称来进行服务调用。 + +#### 启动类 + +``` +@SpringBootApplication +@EnableDiscoveryClient +public class ConsulProducerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConsulProducerApplication.class, args); + } +} +``` + +添加了 `@EnableDiscoveryClient` 注解表示支持服务发现。 + +#### 提供服务 + +我们在创建一个 Controller,推文提供 hello 的服务。 + +``` +@RestController +public class HelloController { + + @RequestMapping("/hello") + public String hello() { + return "hello consul"; + } +} +``` + +为了模拟注册均衡负载复制一份上面的项目重命名为 spring-cloud-consul-producer-2 ,修改对应的端口为 8502,修改 hello 方法的返回值为:”hello consul two”,修改完成后依次启动两个项目。 + +这时候我们再次在浏览器访问地址:http://localhost:8500,显示如下: + +![img](../media/pictures/SpringCloud.assets/consol_producer.png) + +我们发现页面多了 service-producer 服务,点击进去后页面显示有两个服务提供者: + +![img](../media/pictures/SpringCloud.assets/consol_producer-2.png) + +这样服务提供者就准备好了。 + +### Consul 消费端 + +我们创建一个 spring-cloud-consul-consumer 项目,pom 文件和上面示例保持一致。 + +#### 配置文件 + +配置文件内容如下 + +``` +spring.application.name=spring-cloud-consul-consumer +server.port=8503 +spring.cloud.consul.host=127.0.0.1 +spring.cloud.consul.port=8500 +#设置不需要注册到 consul 中 +spring.cloud.consul.discovery.register=false +``` + +客户端可以设置注册到 Consul 中,也可以不注册到 Consul 注册中心中,根据我们的业务来选择,只需要在使用服务时通过 Consul 对外提供的接口获取服务信息即可。 + +#### 启动类 + +``` +@SpringBootApplication +public class ConsulConsumerApplication { + + public static void main(String[] args) { + SpringApplication.run(ConsulConsumerApplication.class, args); + } +} +``` + +#### 进行测试 + +我们先来创建一个 ServiceController ,试试如果去获取 Consul 中的服务。 + +``` +@RestController +public class ServiceController { + + @Autowired + private LoadBalancerClient loadBalancer; + @Autowired + private DiscoveryClient discoveryClient; + + /** + * 获取所有服务 + */ + @RequestMapping("/services") + public Object services() { + return discoveryClient.getInstances("service-producer"); + } + + /** + * 从所有服务中选择一个服务(轮询) + */ + @RequestMapping("/discover") + public Object discover() { + return loadBalancer.choose("service-producer").getUri().toString(); + } +} +``` + +Controller 中有俩个方法,一个是获取所有服务名为`service-producer`的服务信息并返回到页面,一个是随机从服务名为`service-producer`的服务中获取一个并返回到页面。 + +添加完 ServiceController 之后我们启动项目,访问地址:`http://localhost:8503/services`,返回: + +``` +[{"serviceId":"service-producer","host":"windows10.microdone.cn","port":8501,"secure":false,"metadata":{"secure":"false"},"uri":"http://windows10.microdone.cn:8501","scheme":null},{"serviceId":"service-producer","host":"windows10.microdone.cn","port":8502,"secure":false,"metadata":{"secure":"false"},"uri":"http://windows10.microdone.cn:8502","scheme":null}] +``` + +发现我们刚才创建的端口为 8501 和 8502 的两个服务端都存在。 + +多次访问地址:`http://localhost:8503/discover`,页面会交替返回下面信息: + +``` +http://windows10.microdone.cn:8501 +http://windows10.microdone.cn:8502 +... +``` + +说明 8501 和 8502 的两个服务会交替出现,从而实现了获取服务端地址的均衡负载。 + +大多数情况下我们希望使用均衡负载的形式去获取服务端提供的服务,因此使用第二种方法来模拟调用服务端提供的 hello 方法。 + +创建 CallHelloController : + +``` +@RestController +public class CallHelloController { + + @Autowired + private LoadBalancerClient loadBalancer; + + @RequestMapping("/call") + public String call() { + ServiceInstance serviceInstance = loadBalancer.choose("service-producer"); + System.out.println("服务地址:" + serviceInstance.getUri()); + System.out.println("服务名称:" + serviceInstance.getServiceId()); + + String callServiceResult = new RestTemplate().getForObject(serviceInstance.getUri().toString() + "/hello", String.class); + System.out.println(callServiceResult); + return callServiceResult; + } + +} +``` + +使用 RestTemplate 进行远程调用。添加完之后重启 spring-cloud-consul-consumer 项目。在浏览器中访问地址:`http://localhost:8503/call`,依次返回结果如下: + +``` +hello consul +hello consul two +... +``` + +说明我们已经成功的调用了 Consul 服务端提供的服务,并且实现了服务端的均衡负载功能。通过今天的实践我们发现 Consul 提供的服务发现易用、强大。 + + + +## SpringCloud(十四):Spring Cloud 开源软件都有哪些? + +学习一门新的技术如果有优秀的开源项目,对初学者的学习将会是事半功倍,通过研究和学习优秀的开源项目,可以快速的了解此技术的相关应用场景和应用示例,参考优秀开源项目会降低将此技术引入到项目中的成本。为此抽了一些时间为大家寻找了一些非常优秀的 Spring Cloud 开源软件供大家学习参考。 + +上次写了一篇文章[Spring Boot 2 (三):Spring Boot 开源软件都有哪些](http://www.ityouknow.com/springboot/2018/03/05/spring-boot-open-source.html) 给大家介绍优秀的 Spring Boot 开源项目,本篇文章给介绍 Spring Cloud 的优秀开源项目。Spring Cloud 开源项目主要集中在 Github/码云 ,本文所有项目地址也均来自于这两个网站。 + +### 1、 [awesome-spring-cloud](https://github.com/ityouknow/awesome-spring-cloud) + +首先给大家介绍的就是 Spring Cloud 中文索引,这是一个专门收集 Spring Cloud 相关资料的开源项目,也有对应的导航页面。 + +**产品主页** + +http://springcloud.fun/ + +**项目主页** + +https://github.com/ityouknow/awesome-spring-cloud + +**产品截图** + +![img](../media/pictures/SpringCloud.assets/awesome-spring-cloud.png) + +### 2、 [PiggyMetrics](https://github.com/sqshq/PiggyMetrics) + +一个简单的个人财务系统,基于 Spring Boot,Spring Cloud 和 Docker 简单演示了微服务的架构模式,整个项目几乎包含了 Spring Cloud 的所有特性包括 配置中心、Gateway zuul API 网关、Eureka 服务发现、Hystrix、Turbine仪 表盘应用健康监控等等。 + +PiggyMetrics 被分解为三个核心微服务。这些服务都是围绕某些业务能力组织的可独立部署的应用程序。 + +![img](../media/pictures/SpringCloud.assets/PiggyMetrics_sercive.png) + +PiggyMetrics 的项目架构图 + +![img](../media/pictures/SpringCloud.assets/PInfrastructure.png) + +**项目主页** + +https://github.com/sqshq/PiggyMetrics + +**产品截图** + +![img](../media/pictures/SpringCloud.assets/piggyMetrics.png) + +### 3、 [spaascloud-master](https://github.com/paascloud/paascloud-master) + +spring cloud + vue 全家桶实战,模拟商城,完整的购物流程、后端运营平台,可以实现快速搭建企业级微服务项目。 + +功能点: 模拟商城,完整的购物流程、后端运营平台对前端业务的支撑,和对项目的运维,有各项的监控指标和运维指标。 + +技术点: 核心技术为springcloud+vue两个全家桶实现,采取了取自开源用于开源的目标,所以能用开源绝不用收费框架,整体技术栈只有 阿里云短信服务是收费的,都是目前java前瞻性的框架,可以为中小企业解决微服务架构难题,可以帮助企业快速建站。由于服务 器成本较高,尽量降低开发成本的原则,本项目由10个后端项目和3个前端项目共同组成。真正实现了基于RBAC、jwt和oauth2的 无状态统一权限认证的解决方案,实现了异常和日志的统一管理,实现了MQ落地保证100%到达的解决方案。 + +**产品主页** + +http://mall.paascloud.net/index + +**项目主页** + +https://github.com/paascloud/paascloud-master + +**产品截图** + +![img](../media/pictures/SpringCloud.assets/paascloud.png) + +### 4、 [Cloud-Admin](https://gitee.com/minull/ace-security) + +Cloud-Admin是国内首个基于Spring Cloud微服务化开发平台,核心技术采用Spring Boot2以及Spring Cloud Gateway相关核心组件,前端采用vue-element-admin组件。具有统一授权、认证后台管理系统,其中包含具备用户管理、资源权限管理、网关API管理等多个模块,支持多业务系统并行开发,可以作为后端服务的开发脚手架。代码简洁,架构清晰,适合学习和直接项目中使用。 + +**项目主页** + +https://gitee.com/minull/ace-security + +**项目架构** + +![img](../media/pictures/SpringCloud.assets/ace-security.png) + +### 5、 [spring-cloud-rest-tcc](https://github.com/prontera/spring-cloud-rest-tcc) + +基于Spring Cloud Netflix的TCC柔性事务和EDA事件驱动示例,结合Spring Cloud Sleuth进行会话追踪和Spring Boot Admin的健康监控,并辅以Hystrix Dashboard提供近实时的熔断监控. + +**项目主页** + +https://github.com/prontera/spring-cloud-rest-tcc + +**项目架构** + +![img](../media/pictures/SpringCloud.assets/spring-cloud-rest-tcc.png) + +### 6、 [pig](https://gitee.com/log4j/pig) + +基于Spring Cloud、oAuth2.0开发,基于Vue前后分离的开发平台,支持账号、短信、SSO等多种登录 + +**产品主页** + +https://www.pig4cloud.com/ + +**项目主页** + +https://gitee.com/log4j/pig + +**产品截图** + +![img](../media/pictures/SpringCloud.assets/ping.png) + +### 7、 [xxpay-master](https://gitee.com/jmdhappy/xxpay-master) + +XxPay聚合支付使用Java开发,包括spring-cloud、dubbo、spring-boot三个架构版本,已接入微信、支付宝等主流支付渠道,可直接用于生产环境。 + +**产品主页** + +http://www.xxpay.org/ + +**项目主页** + +https://gitee.com/jmdhappy/xxpay-master + +**产品截图** + +![img](../media/pictures/SpringCloud.assets/xxpay.png) + +### 8、 [spring-boot-cloud](https://github.com/zhangxd1989/spring-boot-cloud) + +基于 Spring Boot、Spring Cloud、Spring Oauth2 和 Spring Cloud Netflix 等框架构建的微服务项目 + +**项目主页** + +https://github.com/zhangxd1989/spring-boot-cloud + +**项目架构** + +![img](../media/pictures/SpringCloud.assets/spring-boot-cloud.jpg) + +### 9、 [FCat](https://gitee.com/xfdm/FCat) + +FCat项目基于 Angular 4 + Spring Cloud 的企业级基础功能框架。 + +**项目主页** + +https://gitee.com/xfdm/FCat + +**项目架构** + +![img](../media/pictures/SpringCloud.assets/FCat.png) + +### 10、 [spring-cloud-examples](https://github.com/ityouknow/spring-cloud-examples) + +Spring Cloud 技术栈示例代码,快速简单上手教程,一个帮助大家学习 Spring Cloud 的开源示例项目,每个 Spring Cloud 组件都有独立的示例供大家参考学习。 + +**项目主页** + +https://github.com/ityouknow/spring-cloud-examples + +**项目截图** + +![img](../media/pictures/SpringCloud.assets/spring-cloud-examples.png) + +> 应该还有更多优秀的 Spring Cloud 开源项目,目前仅发现这些,也希望大家多反馈一些优秀的 Spring Cloud 开源项目,统一将这些项目收集到 awesome-spring-cloud 中,方便后续大家学习查找。 + +## SpringCloud(十五):服务网关 Spring Cloud GateWay 入门 + +Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSockets,Spring Cloud Gateway 使用非阻塞 API,支持 WebSockets,支持限流等新特性。 + +### Spring Cloud Gateway + +Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。 + +Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。 + +**相关概念:** + +- Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。 +- Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。 +- Filter(过滤器):这是`org.springframework.cloud.gateway.filter.GatewayFilter`的实例,我们可以使用它修改请求和响应。 + +**工作流程:** + +![img](../media/pictures/SpringCloud.assets/spring-cloud-gateway.png) + +客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。 + +Spring Cloud Gateway 的特征: + +- 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0 +- 动态路由 +- Predicates 和 Filters 作用于特定路由 +- 集成 Hystrix 断路器 +- 集成 Spring Cloud DiscoveryClient +- 易于编写的 Predicates 和 Filters +- 限流 +- 路径重写 + +### 快速上手 + +Spring Cloud Gateway 网关路由有两种配置方式: + +- 在配置文件 yml 中配置 +- 通过`@Bean`自定义 RouteLocator,在启动主类 Application 中配置 + +这两种方式是等价的,建议使用 yml 方式进配置。 + +使用 Spring Cloud Finchley 版本,Finchley 版本依赖于 Spring Boot 2.0.6.RELEASE。 + +``` + + org.springframework.boot + spring-boot-starter-parent + 2.0.6.RELEASE + + + + + + + org.springframework.cloud + spring-cloud-dependencies + Finchley.SR2 + pom + import + + + +``` + +> 经测试 Finchley.RELEASE 有 bug 多次请求会报空指针异常,SR2 是 Spring Cloud 的最新版本。 + +添加项目需要使用的依赖包 + +``` + + org.springframework.cloud + spring-cloud-starter-gateway + +``` + +Spring Cloud Gateway 是使用 netty+webflux 实现因此不需要再引入 web 模块。 + +我们先来测试一个最简单的请求转发。 + +``` +server: + port: 8080 +spring: + cloud: + gateway: + routes: + - id: neo_route + uri: http://www.ityouknow.com + predicates: + - Path=/spring-cloud +``` + +各字段含义如下: + +- id:我们自定义的路由 ID,保持唯一 +- uri:目标服务地址 +- predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。 +- filters:过滤规则,本示例暂时没用。 + +上面这段配置的意思是,配置了一个 id 为 neo_route 的路由规则,当访问地址 `http://localhost:8080/spring-cloud`时会自动转发到地址:`http://www.ityouknow.com/spring-cloud`。配置完成启动项目即可在浏览器访问进行测试,当我们访问地址`http://localhost:8080/spring-cloud` 时会展示页面展示如下: + +![img](../media/pictures/SpringCloud.assets/spring-cloud-gateway1.png) + +证明页面转发成功。 + +转发功能同样可以通过代码来实现,我们可以在启动类 GateWayApplication 中添加方法 `customRouteLocator()` 来定制转发规则。 + +``` +@SpringBootApplication +public class GateWayApplication { + + public static void main(String[] args) { + SpringApplication.run(GateWayApplication.class, args); + } + + @Bean + public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { + return builder.routes() + .route("path_route", r -> r.path("/about") + .uri("http://ityouknow.com")) + .build(); + } + +} +``` + +上面配置了一个 id 为 path_route 的路由,当访问地址`http://localhost:8080/about`时会自动转发到地址:`http://www.ityouknow.com/about`和上面的转发效果一样,只是这里转发的是以`项目地址/about`格式的请求地址。 + +上面两个示例中 uri 都是指向了我的个人网站,在实际项目使用中可以将 uri 指向对外提供服务的项目地址,统一对外输出接口。 + +以上便是 Spring Cloud Gateway 最简单的两个请求示例,Spring Cloud Gateway 还有更多实用的功能接下来我们一一介绍。 + +### 路由规则 + +Spring Cloud Gateway 的功能很强大,我们仅仅通过 Predicates 的设计就可以看出来,前面我们只是使用了 predicates 进行了简单的条件匹配,其实 Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。 + +Spring Cloud Gateway 是通过 Spring WebFlux 的 `HandlerMapping` 做为底层支持来匹配到转发路由,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。 + +#### Predicate 介绍 + +Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。 + +在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现。 + +![img](../media/pictures/SpringCloud.assets/spring-cloud-gateway3.png) + +说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。 + +#### 通过时间匹配 + +Predicate 支持设置一个时间,在请求进行转发的时候,可以通过判断在这个时间之前或者之后进行转发。比如我们现在设置只有在2019年1月1日才会转发到我的网站,在这之前不进行转发,我就可以这样配置: + +``` +spring: + cloud: + gateway: + routes: + - id: time_route + uri: http://ityouknow.com + predicates: + - After=2018-01-20T06:06:06+08:00[Asia/Shanghai] +``` + +Spring 是通过 ZonedDateTime 来对时间进行的对比,ZonedDateTime 是 Java 8 中日期时间功能里,用于表示带时区的日期与时间信息的类,ZonedDateTime 支持通过时区来设置时间,中国的时区是:`Asia/Shanghai`。 + +After Route Predicate 是指在这个时间之后的请求都转发到目标地址。上面的示例是指,请求时间在 2018年1月20日6点6分6秒之后的所有请求都转发到地址`http://ityouknow.com`。`+08:00`是指时间和UTC时间相差八个小时,时间地区为`Asia/Shanghai`。 + +添加完路由规则之后,访问地址`http://localhost:8080`会自动转发到`http://ityouknow.com`。 + +Before Route Predicate 刚好相反,在某个时间之前的请求的请求都进行转发。我们把上面路由规则中的 After 改为 Before,如下: + +``` +spring: + cloud: + gateway: + routes: + - id: after_route + uri: http://ityouknow.com + predicates: + - Before=2018-01-20T06:06:06+08:00[Asia/Shanghai] +``` + +就表示在这个时间之前可以进行路由,在这时间之后停止路由,修改完之后重启项目再次访问地址`http://localhost:8080`,页面会报 404 没有找到地址。 + +除过在时间之前或者之后外,Gateway 还支持限制路由请求在某一个时间段范围内,可以使用 Between Route Predicate 来实现。 + +``` +spring: + cloud: + gateway: + routes: + - id: after_route + uri: http://ityouknow.com + predicates: + - Between=2018-01-20T06:06:06+08:00[Asia/Shanghai], 2019-01-20T06:06:06+08:00[Asia/Shanghai] +``` + +这样设置就意味着在这个时间段内可以匹配到此路由,超过这个时间段范围则不会进行匹配。通过时间匹配路由的功能很酷,可以用在限时抢购的一些场景中。 + +#### 通过 Cookie 匹配 + +Cookie Route Predicate 可以接收两个参数,一个是 Cookie name ,一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。 + +``` +spring: + cloud: + gateway: + routes: + - id: cookie_route + uri: http://ityouknow.com + predicates: + - Cookie=ityouknow, kee.e +``` + +使用 curl 测试,命令行输入: + +``` +curl http://localhost:8080 --cookie "ityouknow=kee.e" +``` + +则会返回页面代码,如果去掉`--cookie "ityouknow=kee.e"`,后台汇报 404 错误。 + +#### 通过 Header 属性匹配 + +Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。 + +``` +spring: + cloud: + gateway: + routes: + - id: header_route + uri: http://ityouknow.com + predicates: + - Header=X-Request-Id, \d+ +``` + +使用 curl 测试,命令行输入: + +``` +curl http://localhost:8080 -H "X-Request-Id:666666" +``` + +则返回页面代码证明匹配成功。将参数`-H "X-Request-Id:666666"`改为`-H "X-Request-Id:neo"`再次执行时返回404证明没有匹配。 + +#### 通过 Host 匹配 + +Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用`.`号作为分隔符。它通过参数中的主机地址作为匹配规则。 + +``` +spring: + cloud: + gateway: + routes: + - id: host_route + uri: http://ityouknow.com + predicates: + - Host=**.ityouknow.com +``` + +使用 curl 测试,命令行输入: + +``` +curl http://localhost:8080 -H "Host: www.ityouknow.com" +curl http://localhost:8080 -H "Host: md.ityouknow.com" +``` + +经测试以上两种 host 均可匹配到 host_route 路由,去掉 host 参数则会报 404 错误。 + +#### 通过请求方式匹配 + +可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。 + +``` +spring: + cloud: + gateway: + routes: + - id: method_route + uri: http://ityouknow.com + predicates: + - Method=GET +``` + +使用 curl 测试,命令行输入: + +``` +# curl 默认是以 GET 的方式去请求 +curl http://localhost:8080 +``` + +测试返回页面代码,证明匹配到路由,我们再以 POST 的方式请求测试。 + +``` +# curl 默认是以 GET 的方式去请求 +curl -X POST http://localhost:8080 +``` + +返回 404 没有找到,证明没有匹配上路由 + +#### 通过请求路径匹配 + +Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。 + +``` +spring: + cloud: + gateway: + routes: + - id: host_route + uri: http://ityouknow.com + predicates: + - Path=/foo/{segment} +``` + +如果请求路径符合要求,则此路由将匹配,例如:/foo/1 或者 /foo/bar。 + +使用 curl 测试,命令行输入: + +``` +curl http://localhost:8080/foo/1 +curl http://localhost:8080/foo/xx +curl http://localhost:8080/boo/xx +``` + +经过测试第一和第二条命令可以正常获取到页面返回值,最后一个命令报404,证明路由是通过指定路由来匹配。 + +#### 通过请求参数匹配 + +Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。 + +``` +spring: + cloud: + gateway: + routes: + - id: query_route + uri: http://ityouknow.com + predicates: + - Query=smile +``` + +这样配置,只要请求中包含 smile 属性的参数即可匹配路由。 + +使用 curl 测试,命令行输入: + +``` +curl localhost:8080?smile=x&id=2 +``` + +经过测试发现只要请求汇总带有 smile 参数即会匹配路由,不带 smile 参数则不会匹配。 + +还可以将 Query 的值以键值对的方式进行配置,这样在请求过来时会对属性值和正则进行匹配,匹配上才会走路由。 + +``` +spring: + cloud: + gateway: + routes: + - id: query_route + uri: http://ityouknow.com + predicates: + - Query=keep, pu. +``` + +这样只要当请求中包含 keep 属性并且参数值是以 pu 开头的长度为三位的字符串才会进行匹配和路由。 + +使用 curl 测试,命令行输入: + +``` +curl localhost:8080?keep=pub +``` + +测试可以返回页面代码,将 keep 的属性值改为 pubx 再次访问就会报 404,证明路由需要匹配正则表达式才会进行路由。 + +#### 通过请求 ip 地址进行匹配 + +Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号(IPv4 或 IPv6 )字符串的列表(最小大小为1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子网掩码)。 + +``` +spring: + cloud: + gateway: + routes: + - id: remoteaddr_route + uri: http://ityouknow.com + predicates: + - RemoteAddr=192.168.1.1/24 +``` + +可以将此地址设置为本机的 ip 地址进行测试。 + +``` +curl localhost:8080 +``` + +果请求的远程地址是 192.168.1.10,则此路由将匹配。 + +#### 组合使用 + +上面为了演示各个 Predicate 的使用,我们是单个单个进行配置测试,其实可以将各种 Predicate 组合起来一起使用。 + +例如: + +``` +spring: + cloud: + gateway: + routes: + - id: host_foo_path_headers_to_httpbin + uri: http://ityouknow.com + predicates: + - Host=**.foo.org + - Path=/headers + - Method=GET + - Header=X-Request-Id, \d+ + - Query=foo, ba. + - Query=baz + - Cookie=chocolate, ch.p + - After=2018-01-20T06:06:06+08:00[Asia/Shanghai] +``` + +各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。 + +> 一个请求满足多个路由的谓词条件时,请求只会被首个成功匹配的路由转发 + +### 总结 + +通过今天的学习发现 Spring Cloud Gateway 使用非常的灵活,可以根据不同的情况来进行路由分发,在实际项目中可以自由组合使用。同时 Spring Cloud Gateway 还有更多很酷的功能,比如 Filter 、熔断和限流等,下次我们继续学习 Spring Cloud Gateway 的高级功能。 + +## SpringCloud(十六):服务网关 Spring Cloud GateWay 服务化和过滤器 + +上一篇文章[服务网关 Spring Cloud GateWay 初级篇](http://www.ityouknow.com/springcloud/2018/12/12/spring-cloud-gateway-start.html),介绍了 Spring Cloud Gateway 的相关术语、技术原理,以及如何快速使用 Spring Cloud Gateway。这篇文章我们继续学习 Spring Cloud Gateway 的高级使用方式,比如如何配置服务中心来使用,如何使用熔断、限流等高级功能。 + +### 注册中心 + +上篇主要讲解了网关代理单个服务的使用语法,在实际的工作中,服务的相互调用都是依赖于服务中心提供的入口来使用,服务中心往往注册了很多服务,如果每个服务都需要单独配置的话,这将是一份很枯燥的工作。Spring Cloud Gateway 提供了一种默认转发的能力,只要将 Spring Cloud Gateway 注册到服务中心,Spring Cloud Gateway 默认就会代理服务中心的所有服务,下面用代码演示。 + +#### 准备服务和注册中心 + +在介绍[服务网关 zuul 的使用](http://www.ityouknow.com/springcloud/2017/06/01/gateway-service-zuul.html)时,提供了 spring-cloud-eureka 、spring-cloud-producer 项目示例,本次演示我们将两个项目版本升级到 `Finchley.SR2` 后继续演示使用。 + +spring-cloud-eureka(Eureka Server) 的 pom 文件更改,其它依赖包不变。 + +升级前: + +``` + + org.springframework.cloud + spring-cloud-starter-eureka-server + +``` + +升级后: + +``` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-server + +``` + +spring-cloud-producer(Eureka Client)的 pom 文件更改。因为配置中心需要作为服务注册到注册中心,所以需要升级 Eureka Client,其他依赖没有变动。 + +升级前: + +``` + + org.springframework.cloud + spring-cloud-starter-eureka + +``` + +升级后: + +``` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + +``` + +两个项目升级完依赖包后依次重启,访问注册中心地址 `http://localhost:8000/` 即可看到名为 `SPRING-CLOUD-PRODUCER`的服务。 + +#### 服务网关注册到注册中心 + +复制上一节的示例项目 [cloud-gateway](http://www.ityouknow.com/springcloud/2018/12/12/spring-cloud-gateway-start.html) 重新命名为 cloud-gateway-eureka,添加 eureka 的客户端依赖包。 + +``` + + org.springframework.cloud + spring-cloud-starter-netflix-eureka-client + +``` + +修改 application.yml 配置文件内容如下 + +``` +server: + port: 8888 +spring: + application: + name: cloud-gateway-eureka + cloud: + gateway: + discovery: + locator: + enabled: true +eureka: + client: + service-url: + defaultZone: http://localhost:8000/eureka/ +logging: + level: + org.springframework.cloud.gateway: debug +``` + +配置说明: + +- `spring.cloud.gateway.discovery.locator.enabled`:是否与服务注册于发现组件进行结合,通过 serviceId 转发到具体的服务实例。默认为 false,设为 true 便开启通过服务中心的自动根据 serviceId 创建路由的功能。 +- `eureka.client.service-url.defaultZone`指定注册中心的地址,以便使用服务发现功能 +- `logging.level.org.springframework.cloud.gateway` 调整相 gateway 包的 log 级别,以便排查问题 + +修改完成后启动 cloud-gateway-eureka 项目,访问注册中心地址 `http://localhost:8000/` 即可看到名为 `CLOUD-GATEWAY-EUREKA`的服务。 + +#### 测试 + +将 Spring Cloud Gateway 注册到服务中心之后,网关会自动代理所有的在注册中心的服务,访问这些服务的语法为: + +``` +http://网关地址:端口/服务中心注册 serviceId/具体的url +``` + +比如我们的 spring-cloud-producer 项目有一个 `/hello` 的服务,访问此服务的时候会返回:hello world。 + +比如访问地址:`http://localhost:9000/hello`,页面返回:hello world! + +按照上面的语法我们通过网关来访问,浏览器输入:`http://localhost:8888/SPRING-CLOUD-PRODUCER/hello` 同样返回:hello world!证明服务网关转发成功。 + +我们将项目 spring-cloud-producer 复制一份为 spring-cloud-producer-1,将`/hello`服务的返回值修改为 hello world smile !,修改端口号为 9001 ,修完完成后重启,这时候访问注册中心后台会发现有两个名为 `SPRING-CLOUD-PRODUCER`的服务。 + +在浏览器多次访问地址:`http://localhost:8888/SPRING-CLOUD-PRODUCER/hello`,页面交替返回以下信息: + +``` +hello world! +hello world smile! +``` + +说明后端服务自动进行了均衡负载。 + +### 基于 Filter(过滤器) 实现的高级功能 + +在[服务网关Zuul高级篇](http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html)中大概介绍过 Filter 的概念。 + +Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。 + +- **PRE**: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。 +- **POST**:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。 + +Spring Cloud Gateway 的 Filter 分为两种:GatewayFilter 与 GlobalFilter。GlobalFilter 会应用到所有的路由上,而 GatewayFilter 将应用到单个路由或者一个分组的路由上。 + +Spring Cloud Gateway 内置了9种 GlobalFilter,比如 Netty Routing Filter、LoadBalancerClient Filter、Websocket Routing Filter 等,根据名字即可猜测出这些 Filter 的作者,具体大家可以参考官网内容:[Global Filters](http://cloud.spring.io/spring-cloud-gateway/single/spring-cloud-gateway.html#_global_filters) + +利用 GatewayFilter 可以修改请求的 Http 的请求或者响应,或者根据请求或者响应做一些特殊的限制。 更多时候我们会利用 GatewayFilter 做一些具体的路由配置,下面我们做一些简单的介绍。 + +#### 快速上手 Filter 使用 + +我们以 AddRequestParameter GatewayFilter 来演示一下,如何在项目中使用 GatewayFilter,AddRequestParameter GatewayFilter 可以在请求中添加指定参数。 + +**application.yml配置示例** + +``` +spring: + cloud: + gateway: + routes: + - id: add_request_parameter_route + uri: http://example.org + filters: + - AddRequestParameter=foo, bar +``` + +这样就会给匹配的每个请求添加上`foo=bar`的参数和值。 + +我们将以上配置融入到 cloud-gateway-eureka 项目中,完整的 `application.yml` 文件配置信息如下: + +``` +server: + port: 8888 +spring: + application: + name: cloud-gateway-eureka + cloud: + gateway: + discovery: + locator: + enabled: true + routes: + - id: add_request_parameter_route + uri: http://localhost:9000 + filters: + - AddRequestParameter=foo, bar + predicates: + - Method=GET +eureka: + client: + service-url: + defaultZone: http://localhost:8000/eureka/ +logging: + level: + org.springframework.cloud.gateway: debug +``` + +这里的 routes 手动指定了服务的转发地址,设置所有的 GET 方法都会自动添加`foo=bar`,`http://localhost:9000` 是 spring-cloud-producer 项目,我们在此项目中添加一个 foo() 方法,用来接收转发中添加的参数 foo。 + +``` +@RequestMapping("/foo") +public String foo(String foo) { + return "hello "+foo+"!"; +} +``` + +修改完成后重启 cloud-gateway-eureka、spring-cloud-producer 项目。访问地址`http://localhost:9000/foo`页面返回:hello null!,说明并没有接受到参数 foo;通过网关来调用此服务,浏览器访问地址`http://localhost:8888/foo`页面返回:hello bar!,说明成功接收到参数 foo 参数的值 bar ,证明网关在转发的过程中已经通过 filter 添加了设置的参数和值。 + +#### 服务化路由转发 + +上面我们使用 uri 指定了一个服务转发地址,单个服务这样使用问题不大,但是我们在注册中心往往会使用多个服务来共同支撑整个服务的使用,这个时候我们就期望可以将 Filter 作用到每个应用的实例上,spring cloud gateway 工了这样的功能,只需要简单配置即可。 + +为了测试两个服务提供者是否都被调用,我们在 spring-cloud-producer-1 项目中也同样添加 foo() 方法。 + +``` +@RequestMapping("/foo") +public String foo(String foo) { + return "hello "+foo+"!!"; +} +``` + +为了和 spring-cloud-producer 中 foo() 方法有所区别,这里使用了两个感叹号。同时将 cloud-gateway-eureka 项目配置文件中的 uri 内容修改如下: + +``` +#格式为:lb://应用注册服务名 +uri: lb://spring-cloud-producer +``` + +修改完之后,重新启动项目 cloud-gateway-eureka、spring-cloud-producer-1,浏览器访问地址:`http://localhost:8888/foo`页面交替出现: + +``` +hello bar! +hello bar!! +``` + +证明请求依据均匀转发到后端服务,并且后端服务均接收到了 filter 增加的参数 foo 值。 + +这里其实默认使用了全局过滤器 LoadBalancerClient ,当路由配置中 uri 所用的协议为 lb 时(以uri: lb://spring-cloud-producer为例),gateway 将使用 LoadBalancerClient 把 spring-cloud-producer 通过 eureka 解析为实际的主机和端口,并进行负载均衡。 + +下篇再给大家介绍集中比较常用的 Filter 功能。 + +## SpringCloud(十七):服务网关 Spring Cloud GateWay 熔断、限流、重试 + +上篇文章介绍了 Gataway 和注册中心的使用,以及 Gataway 中 Filter 的基本使用,这篇文章我们将继续介绍 Filter 的一些常用功能。 + +### 修改请求路径的过滤器 + +**StripPrefix Filter** + +StripPrefix Filter 是一个请求路径截取的功能,我们可以利用这个功能来做特殊业务的转发。 + +application.yml 配置如下: + +``` +spring: + cloud: + gateway: + routes: + - id: nameRoot + uri: http://nameservice + predicates: + - Path=/name/** + filters: + - StripPrefix=2 +``` + +上面这个配置的例子表示,当请求路径匹配到`/name/**`会将包含name和后边的字符串接去掉转发, `StripPrefix=2`就代表截取路径的个数,这样配置后当请求`/name/bar/foo`后端匹配到的请求路径就会变成`http://nameservice/foo`。 + +我们还是在 cloud-gateway-eureka 项目中进行测试,修改 application.yml 如下: + +``` +spring: + cloud: + routes: + - id: nameRoot + uri: lb://spring-cloud-producer + predicates: + - Path=/name/** + filters: + - StripPrefix=2 +``` + +配置完后重启 cloud-gateway-eureka 项目,访问地址:`http://localhost:8888/name/foo/hello`页面会交替显示: + +``` +hello world! +hello world smile! +``` + +和直接访问地址 `http://localhost:8888/hello`展示的效果一致,说明请求路径中的 `name/foo/` 已经被截取。 + +**PrefixPath Filter** + +PrefixPath Filter 的作用和 StripPrefix 正相反,是在 URL 路径前面添加一部分的前缀 + +``` +spring: + cloud: + gateway: + routes: + - id: prefixpath_route + uri: http://example.org + filters: + - PrefixPath=/mypath +``` + +大家可以下来去测试,这里不在演示。 + +### 限速路由器 + +限速在高并发场景中比较常用的手段之一,可以有效的保障服务的整体稳定性,Spring Cloud Gateway 提供了基于 Redis 的限流方案。所以我们首先需要添加对应的依赖包`spring-boot-starter-data-redis-reactive` + +``` + + org.springframework.cloud + spring-boot-starter-data-redis-reactive + +``` + +配置文件中需要添加 Redis 地址和限流的相关配置 + +``` +spring: + application: + name: cloud-gateway-eureka + redis: + host: localhost + password: + port: 6379 + cloud: + gateway: + discovery: + locator: + enabled: true + routes: + - id: requestratelimiter_route + uri: http://example.org + filters: + - name: RequestRateLimiter + args: + redis-rate-limiter.replenishRate: 10 + redis-rate-limiter.burstCapacity: 20 + key-resolver: "#{@userKeyResolver}" + predicates: + - Method=GET +``` + +- filter 名称必须是 RequestRateLimiter +- redis-rate-limiter.replenishRate:允许用户每秒处理多少个请求 +- redis-rate-limiter.burstCapacity:令牌桶的容量,允许在一秒钟内完成的最大请求数 +- key-resolver:使用 SpEL 按名称引用 bean + +项目中设置限流的策略,创建 Config 类。 + +``` +public class Config { + + @Bean + KeyResolver userKeyResolver() { + return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); + } +} +``` + +根据请求参数中的 user 字段来限流,也可以设置根据请求 IP 地址来限流,设置如下: + +``` +@Bean +public KeyResolver ipKeyResolver() { + return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); +} +``` + +这样网关就可以根据不同策略来对请求进行限流了。 + +### 熔断路由器 + +在之前的 Spring Cloud 系列文章中,大家对熔断应该有了一定的了解,如过不了解可以先读这篇文章:[熔断器 Hystrix](http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html) + +Spring Cloud Gateway 也可以利用 Hystrix 的熔断特性,在流量过大时进行服务降级,同样我们还是首先给项目添加上依赖。 + +``` + + org.springframework.cloud + spring-cloud-starter-netflix-hystrix + +``` + +配置示例 + +``` +spring: + cloud: + gateway: + routes: + - id: hystrix_route + uri: http://example.org + filters: + - Hystrix=myCommandName +``` + +配置后,gateway 将使用 myCommandName 作为名称生成 HystrixCommand 对象来进行熔断管理。如果想添加熔断后的回调内容,需要在添加一些配置。 + +``` +spring: + cloud: + gateway: + routes: + - id: hystrix_route + uri: lb://spring-cloud-producer + predicates: + - Path=/consumingserviceendpoint + filters: + - name: Hystrix + args: + name: fallbackcmd + fallbackUri: forward:/incaseoffailureusethis +``` + +`fallbackUri: forward:/incaseoffailureusethis`配置了 fallback 时要会调的路径,当调用 Hystrix 的 fallback 被调用时,请求将转发到`/incaseoffailureuset`这个 URI。 + +### 重试路由器 + +RetryGatewayFilter 是 Spring Cloud Gateway 对请求重试提供的一个 GatewayFilter Factory + +配置示例: + +``` +spring: + cloud: + gateway: + routes: + - id: retry_test + uri: lb://spring-cloud-producer + predicates: + - Path=/retry + filters: + - name: Retry + args: + retries: 3 + statuses: BAD_GATEWAY +``` + +Retry GatewayFilter 通过这四个参数来控制重试机制: retries, statuses, methods, 和 series。 + +- retries:重试次数,默认值是 3 次 +- statuses:HTTP 的状态返回码,取值请参考:`org.springframework.http.HttpStatus` +- methods:指定哪些方法的请求需要进行重试逻辑,默认值是 GET 方法,取值参考:`org.springframework.http.HttpMethod` +- series:一些列的状态码配置,取值参考:`org.springframework.http.HttpStatus.Series`。符合的某段状态码才会进行重试逻辑,默认值是 SERVER_ERROR,值是 5,也就是 5XX(5 开头的状态码),共有5 个值。 + +以上便是项目中常用的一些网关操作,更多关于 Spring Cloud GateWay 的使用请参考官网。 + + + +# SpringCloud常见问题 + +https://blog.csdn.net/hjq_ku/article/details/89504229 + +https://blog.csdn.net/qq_40117549/article/details/84944840 + +https://www.cnblogs.com/aishangJava/p/11927311.html 这个也很好 + +https://baijiahao.baidu.com/s?id=1654336657640346337&wfr=spider&for=pc + +https://www.cnblogs.com/lingboweifu/p/11797840.html diff --git a/YangLei/StudyGoup.assets/1216484-20190825002227995-188371311.png b/YangLei/StudyGoup.assets/1216484-20190825002227995-188371311.png new file mode 100644 index 0000000..a756385 Binary files /dev/null and b/YangLei/StudyGoup.assets/1216484-20190825002227995-188371311.png differ diff --git a/YangLei/StudyGoup.assets/1216484-20190825002311121-524783044.png b/YangLei/StudyGoup.assets/1216484-20190825002311121-524783044.png new file mode 100644 index 0000000..25ec952 Binary files /dev/null and b/YangLei/StudyGoup.assets/1216484-20190825002311121-524783044.png differ diff --git a/YangLei/StudyGoup.assets/1586450469031.png b/YangLei/StudyGoup.assets/1586450469031.png new file mode 100644 index 0000000..48d982a Binary files /dev/null and b/YangLei/StudyGoup.assets/1586450469031.png differ diff --git a/YangLei/StudyGoup.assets/1586450560610.png b/YangLei/StudyGoup.assets/1586450560610.png new file mode 100644 index 0000000..b0ac472 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586450560610.png differ diff --git a/YangLei/StudyGoup.assets/1586450682543.png b/YangLei/StudyGoup.assets/1586450682543.png new file mode 100644 index 0000000..4527580 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586450682543.png differ diff --git a/YangLei/StudyGoup.assets/1586450723195.png b/YangLei/StudyGoup.assets/1586450723195.png new file mode 100644 index 0000000..c89f405 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586450723195.png differ diff --git a/YangLei/StudyGoup.assets/1586450770230.png b/YangLei/StudyGoup.assets/1586450770230.png new file mode 100644 index 0000000..705333d Binary files /dev/null and b/YangLei/StudyGoup.assets/1586450770230.png differ diff --git a/YangLei/StudyGoup.assets/1586450832673.png b/YangLei/StudyGoup.assets/1586450832673.png new file mode 100644 index 0000000..e7b5b34 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586450832673.png differ diff --git a/YangLei/StudyGoup.assets/1586450931714.png b/YangLei/StudyGoup.assets/1586450931714.png new file mode 100644 index 0000000..9cb9473 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586450931714.png differ diff --git a/YangLei/StudyGoup.assets/1586451075322.png b/YangLei/StudyGoup.assets/1586451075322.png new file mode 100644 index 0000000..a2642d4 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586451075322.png differ diff --git a/YangLei/StudyGoup.assets/1586451262545.png b/YangLei/StudyGoup.assets/1586451262545.png new file mode 100644 index 0000000..86b244e Binary files /dev/null and b/YangLei/StudyGoup.assets/1586451262545.png differ diff --git a/YangLei/StudyGoup.assets/1586484828157.png b/YangLei/StudyGoup.assets/1586484828157.png new file mode 100644 index 0000000..03a994a Binary files /dev/null and b/YangLei/StudyGoup.assets/1586484828157.png differ diff --git a/YangLei/StudyGoup.assets/1586485017694.png b/YangLei/StudyGoup.assets/1586485017694.png new file mode 100644 index 0000000..06b02f8 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586485017694.png differ diff --git a/YangLei/StudyGoup.assets/1586485582921.png b/YangLei/StudyGoup.assets/1586485582921.png new file mode 100644 index 0000000..3347c21 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586485582921.png differ diff --git a/YangLei/StudyGoup.assets/1586485738777.png b/YangLei/StudyGoup.assets/1586485738777.png new file mode 100644 index 0000000..fb1657e Binary files /dev/null and b/YangLei/StudyGoup.assets/1586485738777.png differ diff --git a/YangLei/StudyGoup.assets/1586485789493.png b/YangLei/StudyGoup.assets/1586485789493.png new file mode 100644 index 0000000..69c8d2a Binary files /dev/null and b/YangLei/StudyGoup.assets/1586485789493.png differ diff --git a/YangLei/StudyGoup.assets/1586485808631.png b/YangLei/StudyGoup.assets/1586485808631.png new file mode 100644 index 0000000..79d3f61 Binary files /dev/null and b/YangLei/StudyGoup.assets/1586485808631.png differ diff --git a/YangLei/StudyGoup.assets/1586485876121.png b/YangLei/StudyGoup.assets/1586485876121.png new file mode 100644 index 0000000..e7d0b9e Binary files /dev/null and b/YangLei/StudyGoup.assets/1586485876121.png differ diff --git a/YangLei/StudyGoup.assets/1586486120621.png b/YangLei/StudyGoup.assets/1586486120621.png new file mode 100644 index 0000000..3add75a Binary files /dev/null and b/YangLei/StudyGoup.assets/1586486120621.png differ diff --git a/YangLei/StudyGoup.assets/1586600321902.png b/YangLei/StudyGoup.assets/1586600321902.png new file mode 100644 index 0000000..f02b1ad Binary files /dev/null and b/YangLei/StudyGoup.assets/1586600321902.png differ diff --git a/YangLei/StudyGoup.assets/1587439674710.png b/YangLei/StudyGoup.assets/1587439674710.png new file mode 100644 index 0000000..13c5dde Binary files /dev/null and b/YangLei/StudyGoup.assets/1587439674710.png differ diff --git a/YangLei/StudyGoup.assets/1587439715060.png b/YangLei/StudyGoup.assets/1587439715060.png new file mode 100644 index 0000000..2bed276 Binary files /dev/null and b/YangLei/StudyGoup.assets/1587439715060.png differ diff --git a/YangLei/StudyGoup.assets/169fab4ad1e262c7 b/YangLei/StudyGoup.assets/169fab4ad1e262c7 new file mode 100644 index 0000000..cc924fc Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4ad1e262c7 differ diff --git a/YangLei/StudyGoup.assets/169fab4ae00ad805 b/YangLei/StudyGoup.assets/169fab4ae00ad805 new file mode 100644 index 0000000..75b8d45 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4ae00ad805 differ diff --git a/YangLei/StudyGoup.assets/169fab4ae1b1e740 b/YangLei/StudyGoup.assets/169fab4ae1b1e740 new file mode 100644 index 0000000..0996c02 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4ae1b1e740 differ diff --git a/YangLei/StudyGoup.assets/169fab4ae260d343 b/YangLei/StudyGoup.assets/169fab4ae260d343 new file mode 100644 index 0000000..fa3f74f Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4ae260d343 differ diff --git a/YangLei/StudyGoup.assets/169fab4ae468dd5f b/YangLei/StudyGoup.assets/169fab4ae468dd5f new file mode 100644 index 0000000..b87630e Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4ae468dd5f differ diff --git a/YangLei/StudyGoup.assets/169fab4aeeea3142 b/YangLei/StudyGoup.assets/169fab4aeeea3142 new file mode 100644 index 0000000..26b62c0 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4aeeea3142 differ diff --git a/YangLei/StudyGoup.assets/169fab4af64502e8 b/YangLei/StudyGoup.assets/169fab4af64502e8 new file mode 100644 index 0000000..3590386 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4af64502e8 differ diff --git a/YangLei/StudyGoup.assets/169fab4affd9796f b/YangLei/StudyGoup.assets/169fab4affd9796f new file mode 100644 index 0000000..1a11ee8 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4affd9796f differ diff --git a/YangLei/StudyGoup.assets/169fab4b0047390e b/YangLei/StudyGoup.assets/169fab4b0047390e new file mode 100644 index 0000000..d77a563 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b0047390e differ diff --git a/YangLei/StudyGoup.assets/169fab4b06548ad2 b/YangLei/StudyGoup.assets/169fab4b06548ad2 new file mode 100644 index 0000000..75615ee Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b06548ad2 differ diff --git a/YangLei/StudyGoup.assets/169fab4b06d96e59 b/YangLei/StudyGoup.assets/169fab4b06d96e59 new file mode 100644 index 0000000..ccad0c3 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b06d96e59 differ diff --git a/YangLei/StudyGoup.assets/169fab4b14bfcb89 b/YangLei/StudyGoup.assets/169fab4b14bfcb89 new file mode 100644 index 0000000..44556ef Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b14bfcb89 differ diff --git a/YangLei/StudyGoup.assets/169fab4b1c978ec4 b/YangLei/StudyGoup.assets/169fab4b1c978ec4 new file mode 100644 index 0000000..c513f10 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b1c978ec4 differ diff --git a/YangLei/StudyGoup.assets/169fab4b23724f6c b/YangLei/StudyGoup.assets/169fab4b23724f6c new file mode 100644 index 0000000..7e97e40 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b23724f6c differ diff --git a/YangLei/StudyGoup.assets/169fab4b25c75f18 b/YangLei/StudyGoup.assets/169fab4b25c75f18 new file mode 100644 index 0000000..cc52796 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b25c75f18 differ diff --git a/YangLei/StudyGoup.assets/169fab4b25c75f18-1587051845458 b/YangLei/StudyGoup.assets/169fab4b25c75f18-1587051845458 new file mode 100644 index 0000000..cc52796 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b25c75f18-1587051845458 differ diff --git a/YangLei/StudyGoup.assets/169fab4b2c6ecb0a b/YangLei/StudyGoup.assets/169fab4b2c6ecb0a new file mode 100644 index 0000000..308cf99 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b2c6ecb0a differ diff --git a/YangLei/StudyGoup.assets/169fab4b3331840f b/YangLei/StudyGoup.assets/169fab4b3331840f new file mode 100644 index 0000000..b64a57a Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b3331840f differ diff --git a/YangLei/StudyGoup.assets/169fab4b38f8b6a9 b/YangLei/StudyGoup.assets/169fab4b38f8b6a9 new file mode 100644 index 0000000..a22c463 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b38f8b6a9 differ diff --git a/YangLei/StudyGoup.assets/169fab4b4082bcd3 b/YangLei/StudyGoup.assets/169fab4b4082bcd3 new file mode 100644 index 0000000..43c6d99 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b4082bcd3 differ diff --git a/YangLei/StudyGoup.assets/169fab4b4450f7a8 b/YangLei/StudyGoup.assets/169fab4b4450f7a8 new file mode 100644 index 0000000..6ecd3e6 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b4450f7a8 differ diff --git a/YangLei/StudyGoup.assets/169fab4b555acbb1 b/YangLei/StudyGoup.assets/169fab4b555acbb1 new file mode 100644 index 0000000..4cd47ac Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b555acbb1 differ diff --git a/YangLei/StudyGoup.assets/169fab4b563095df b/YangLei/StudyGoup.assets/169fab4b563095df new file mode 100644 index 0000000..8a7a72e Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b563095df differ diff --git a/YangLei/StudyGoup.assets/169fab4b5cd19ef9 b/YangLei/StudyGoup.assets/169fab4b5cd19ef9 new file mode 100644 index 0000000..26bf1d8 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b5cd19ef9 differ diff --git a/YangLei/StudyGoup.assets/169fab4b5f67f5db b/YangLei/StudyGoup.assets/169fab4b5f67f5db new file mode 100644 index 0000000..8013859 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b5f67f5db differ diff --git a/YangLei/StudyGoup.assets/169fab4b627a3aa3 b/YangLei/StudyGoup.assets/169fab4b627a3aa3 new file mode 100644 index 0000000..7dba21c Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b627a3aa3 differ diff --git a/YangLei/StudyGoup.assets/169fab4b7a2dd2ac b/YangLei/StudyGoup.assets/169fab4b7a2dd2ac new file mode 100644 index 0000000..08a7869 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b7a2dd2ac differ diff --git a/YangLei/StudyGoup.assets/169fab4b7c297188 b/YangLei/StudyGoup.assets/169fab4b7c297188 new file mode 100644 index 0000000..f61b48c Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b7c297188 differ diff --git a/YangLei/StudyGoup.assets/169fab4b80770fc3 b/YangLei/StudyGoup.assets/169fab4b80770fc3 new file mode 100644 index 0000000..3f34d3d Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b80770fc3 differ diff --git a/YangLei/StudyGoup.assets/169fab4b81039ab4 b/YangLei/StudyGoup.assets/169fab4b81039ab4 new file mode 100644 index 0000000..af15435 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b81039ab4 differ diff --git a/YangLei/StudyGoup.assets/169fab4b97c18e40 b/YangLei/StudyGoup.assets/169fab4b97c18e40 new file mode 100644 index 0000000..3f978f7 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b97c18e40 differ diff --git a/YangLei/StudyGoup.assets/169fab4b992d6c22 b/YangLei/StudyGoup.assets/169fab4b992d6c22 new file mode 100644 index 0000000..6ecc2d4 Binary files /dev/null and b/YangLei/StudyGoup.assets/169fab4b992d6c22 differ diff --git a/YangLei/StudyGoup.assets/2456362658-5b32f1212cb9a_articlex.png b/YangLei/StudyGoup.assets/2456362658-5b32f1212cb9a_articlex.png new file mode 100644 index 0000000..994f064 Binary files /dev/null and b/YangLei/StudyGoup.assets/2456362658-5b32f1212cb9a_articlex.png differ diff --git a/YangLei/StudyGoup.assets/6078367.jpg b/YangLei/StudyGoup.assets/6078367.jpg new file mode 100644 index 0000000..f8157b1 Binary files /dev/null and b/YangLei/StudyGoup.assets/6078367.jpg differ diff --git a/YangLei/StudyGoup.assets/a2b.jpg b/YangLei/StudyGoup.assets/a2b.jpg new file mode 100644 index 0000000..b273a94 Binary files /dev/null and b/YangLei/StudyGoup.assets/a2b.jpg differ diff --git a/YangLei/StudyGoup.assets/a2b2c.jpg b/YangLei/StudyGoup.assets/a2b2c.jpg new file mode 100644 index 0000000..a6cf2c3 Binary files /dev/null and b/YangLei/StudyGoup.assets/a2b2c.jpg differ diff --git a/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzE4NjExNzY3LmpwZw.jpg b/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzE4NjExNzY3LmpwZw.jpg new file mode 100644 index 0000000..8b2ede8 Binary files /dev/null and b/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzE4NjExNzY3LmpwZw.jpg differ diff --git a/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzcxMzc1OTYzLmpwZw.jpg b/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzcxMzc1OTYzLmpwZw.jpg new file mode 100644 index 0000000..fc1c703 Binary files /dev/null and b/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzcxMzc1OTYzLmpwZw.jpg differ diff --git a/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzgyMjc2NDU4LmpwZw.jpg b/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzgyMjc2NDU4LmpwZw.jpg new file mode 100644 index 0000000..c933674 Binary files /dev/null and b/YangLei/StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzgyMjc2NDU4LmpwZw.jpg differ diff --git a/YangLei/StudyGoup.assets/ab.jpg b/YangLei/StudyGoup.assets/ab.jpg new file mode 100644 index 0000000..6959de2 Binary files /dev/null and b/YangLei/StudyGoup.assets/ab.jpg differ diff --git a/YangLei/StudyGoup.assets/abc.jpg b/YangLei/StudyGoup.assets/abc.jpg new file mode 100644 index 0000000..adfe29a Binary files /dev/null and b/YangLei/StudyGoup.assets/abc.jpg differ diff --git a/YangLei/StudyGoup.assets/eureka-architecture-overview.png b/YangLei/StudyGoup.assets/eureka-architecture-overview.png new file mode 100644 index 0000000..431c36d Binary files /dev/null and b/YangLei/StudyGoup.assets/eureka-architecture-overview.png differ diff --git a/YangLei/StudyGoup.assets/eureka-cluster.jpg b/YangLei/StudyGoup.assets/eureka-cluster.jpg new file mode 100644 index 0000000..e0f78ff Binary files /dev/null and b/YangLei/StudyGoup.assets/eureka-cluster.jpg differ diff --git a/YangLei/StudyGoup.assets/eureka-two.jpg b/YangLei/StudyGoup.assets/eureka-two.jpg new file mode 100644 index 0000000..e1002a5 Binary files /dev/null and b/YangLei/StudyGoup.assets/eureka-two.jpg differ diff --git a/YangLei/StudyGoup.assets/eureka_start.jpg b/YangLei/StudyGoup.assets/eureka_start.jpg new file mode 100644 index 0000000..4a2687a Binary files /dev/null and b/YangLei/StudyGoup.assets/eureka_start.jpg differ diff --git a/YangLei/StudyGoup.md b/YangLei/StudyGoup.md new file mode 100644 index 0000000..3ed39f4 --- /dev/null +++ b/YangLei/StudyGoup.md @@ -0,0 +1,1997 @@ +# StudyGoup + +## 2020.4.5 + +### 1、Springboot项目部署阿里云,Vue打包App + +在王道的时候学习了在linux安装常用开发软件。这里不再写安装过程。 + +1)、首先再阿里云服务器上安装mysql,tomcat,jdk,redis等,然后让软件跑起来。 + + + +2)、然后将数据库表用navicat连接阿里云服务器,然后将数据库表导入到阿里云mysql中: + +![1586450560610](StudyGoup.assets/1586450560610.png) + +3.修改Springboot项目中 application.yml 中的 数据库配置,redis配置,influxdb配置 + +![1586450469031](StudyGoup.assets/1586450469031.png) + +![1586450682543](StudyGoup.assets/1586450682543.png) + +![1586450723195](StudyGoup.assets/1586450723195.png) + +3)、用IDEA打包 先点击Maven中的clean,然后点击install + +![1586450770230](StudyGoup.assets/1586450770230.png) + +打包完了以后 会在target文件夹下生产.jar文件 + +![1586450832673](StudyGoup.assets/1586450832673.png) + +然后利用FTP工具,将jar包发送到阿里云服务器上(最好发之前,在电脑上cmd里面运行一下这个jar包是否没问题) + +![1586450931714](StudyGoup.assets/1586450931714.png) + +4)、让.jar文件永久运行在阿里云服务器上: + +![1586451075322](StudyGoup.assets/1586451075322.png) + +```shell +nohup java -jar blog.jar > system.log 2>&1 & +``` + +5)、开放阿里云安全组(软件的安全组也要开启,比如mysql,redis,influxdb等 这里打码啦) + +![1586451262545](StudyGoup.assets/1586451262545.png) + +6)、然后用电脑的前端连接服务器测试 + +### 2、解决Ubuntu18设置mysql的sql_mode导致的数据库数据导入不到服务器的问题 + +由于要往阿里云服务器导入数据,结果有一个表始终导入不进去,网上找各种解决方案,都是修改sql_mode,但是网上好多解决方案好多不是Ubuntu,可能是CentOS的,文件夹目录不一样,这个问题搞了两天才解决。 + + + +**原因:** + +MySQL 5.7.5及以上功能依赖检测功能。如果启用了ONLY_FULL_GROUP_BY SQL模式(默认情况下),MySQL将拒绝选择列表,HAVING条件或ORDER BY列表的查询引用在GROUP BY子句中既未命名的非集合列,也不在功能上依赖于它们。 + +(5.7.5之前,MySQL没有检测到功能依赖关系,默认情况下不启用ONLY_FULL_GROUP_BY。有关5.7.5之前的行为的说明,请参见“MySQL 5.6参考手册”。) + +**一.临时性解决** + +终端执行 set @@global.sql_mode = ‘STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION’; + +如果 NO_AUTO_CREATE_USER 报错,去掉它重新执行。 + +set @@global.sql_mode = ‘STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION’; + +注:该方法在MySQL重启后就会失效。 + +**二.从根本解决** + +终端执行 **sudo vim /etc/mysql/conf.d/mysql.cnf** **文件目录很重要 网上的好多那个目录改了不起作用,这个值Ubuntu的没错。** + +粘贴以下文本: + +[mysqld] +**sql_mode=STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION** + +保存退出 + +**重启mysql服务 sudo service mysql restart** + +如果重启失败,去掉NO_AUTO_CREATE_USER,保存退出重新启动 + + + +## 2020.4.9 + +### 1.influxdb 安装使用 部署 修改用户名 结合SpringBoot项目 + +前面这部分是网上的资源 :https://blog.csdn.net/llwy1428/article/details/85331753 + +测试有效 所以保存。 + + + +用在哪里? + +我们公司做交易所项目,这个数据库用在存储交易信息,然后页面显示k线图 + +![1586485582921](StudyGoup.assets/1586485582921.png) + +听16期渝淮老哥说,他们万维是用这个做系统功能监控的。 + + + +influxdb是目前比较流行的**时间序列数据库** + +- 何谓时间序列数据库? + 什么是时间序列数据库,最简单的定义就是数据格式里包含Timestamp字段的数据,比如某一时间环境的温度,CPU的使用率等。但是,有什么数据不包含Timestamp呢?几乎所有的数据其实都可以打上一个Timestamp字段。时间序列数据的更重要的一个属性是如何去查询它,包括数据的过滤,计算等等。 + +Influxdb + +Influxdb是一个开源的分布式时序、时间和指标数据库,使用go语言编写,无需外部依赖。 +它有三大特性: + +1. 时序性(Time Series):与时间相关的函数的灵活使用(诸如最大、最小、求和等); +2. 度量(Metrics):对实时大量数据进行计算; +3. 事件(Event):支持任意的事件数据,换句话说,任意事件的数据我们都可以做操作。 + +同时,它有以下几大特点: + +- schemaless(无结构),可以是任意数量的列; +- min, max, sum, count, mean, median 一系列函数,方便统计; +- Native HTTP API, 内置http支持,使用http读写; +- Powerful Query Language 类似sql; +- Built-in Explorer 自带管理工具。 + +### Influxdb安装 + +------ + +> 注:本文使用的influxdb version是1.0.2 + +在讲解具体的安装步骤之前,先说说influxdb的两个http端口:8083和8086 + +- port 8083:管理页面端口,访问localhost:8083可以进入你本机的influxdb管理页面; +- port 8086:http连接influxdb client端口,一般使用该端口往本机的influxdb读写数据。 +- + +Docker Image + +```shell +docker pull influxdb +``` + +Ubuntu & Debian + +```shell +wget https://dl.influxdata.com/influxdb/releases/influxdb_1.0.2_amd64.debsudo dpkg -i influxdb_1.0.2_amd64.deb +``` + +**How to start?** +**安装完之后,如何启动呢?** + +```shell +sudo service influxdb start +``` + +到这里influxdb安装启动完成,可以访问influxdb管理页面:[本地管理页面](http://localhost:8083/),该版本没有登录用户及密码,可以自行设置读写的用户名和密码。 + +如何在命令行使用 +安装完毕之后,如何在命令行使用呢? + +![1586484828157](StudyGoup.assets/1586484828157.png) + +influxdb在命令行中使用 + +### influxdb基本操作 + +------ + +名词解释 + +在具体的讲解influxdb的相关操作之前先说说influxdb的一些专有名词,这些名词代表什么。 + +influxdb相关名词 + +- database:数据库; +- measurement:数据库中的表;(这里要注意,插入数据的时候 数据库中表也一起插入) +- points:表里面的一行数据。 + +influxDB中独有的一些概念 + +Point由时间戳(time)、数据(field)和标签(tags)组成。 + +- time:每条数据记录的时间,也是数据库自动生成的主索引; +- fields:各种记录的值; +- tags:各种有索引的属性。 + +还有一个重要的名词:series +所有在数据库中的数据,都需要通过图表来表示,series表示这个表里面的所有的数据可以在图标上画成几条线(注:线条的个数由tags排列组合计算出来) +举个简单的小栗子: +有如下数据: + +![1586485017694](StudyGoup.assets/1586485017694.png) + + + +influxdb基本操作 + +- 数据库与表的操作 + 可以直接在web管理页面做操作,当然也可以命令行。 + + ```sql + #创建数据库 + create database "db_name" + + #显示所有的数据库 + show databases + + #删除数据库 + drop database "db_name" + + #使用数据库 + use db_name + + #显示该数据库中所有的表show measurements + + #创建表,直接在插入数据的时候指定表名 + insert test,host=127.0.0.1,monitor_name=test count=1 + + #删除表 + drop measurement "measurement_name" + ``` + +- 增 + 向数据库中插入数据。 + + - 通过命令行 + + ```sql + use testDb + insert test,host=127.0.0.1,monitor_name=test count=1 + ``` + + - 通过http接口 + + ```sql + curl -i -XPOST 'http://127.0.0.1:8086/write?db=testDb' --data-binary 'test,host=127.0.0.1,monitor_name=test count=1' + ``` + + + + 读者看到这里可能会观察到插入的数据的格式貌似比较奇怪,这是因为influxDB存储数据采用的是Line Protocol格式。那么何谓Line Protoco格式? + + Line Protocol格式:写入数据库的Point的固定格式。 + 在上面的两种插入数据的方法中都有这样的一部分: + + ```sql + test,host=127.0.0.1,monitor_name=test count=1 + ``` + + 其中: + + 1. test:表名; + 2. host=127.0.0.1,monitor_name=test:tag; + 3. count=1:field + + 想对此格式有详细的了解参见[官方文档](https://docs.influxdata.com/influxdb/v0.10/write_protocols/line/) + +- 查 + 查询数据库中的数据。 + + - 通过命令行 + + ```sql + select * from test order by time desc + ``` + + - 通过http接口 + + ```sql + curl -G 'http://localhost:8086/query?pretty=true' --data-urlencode "db=testDb" --data-urlencode "q=select * from test order by time desc" + ``` + + influxDB是支持类sql语句的,具体的查询语法都差不多,这里就不再做详细的赘述了。 + + + +- 连续查询(Continous Queries) + + 当数据超过保存策略里指定的时间之后就会被删除,但是这时候可能并不想数据被完全删掉,怎么办? + + influxdb提供了联系查询,可以做数据统计采样。 + + - 查看数据库的Continous Queries + + ``` + show continuous queries + ``` + + + + continuous_queries. + + - 创建新的Continous Queries + + ```sql + create continous query cq_name on db_name begin select sum(count) into new_table_name from table_name group by time(30m) end + ``` + + - cq_name:连续查询名字; + - db_name:数据库名字; + - sum(count):计算总和; + - table_name:当前表名; + - new_table_name:存新的数据的表名; + - 30m:时间间隔为30分钟 + + - 删除Continous Queries + + ```sql + drop continous query cp_name on db_name + ``` + +- 用户管理 + + 可以直接在web管理页面做操作,也可以命令行。 + + ```sql + #显示用户 + show users# + + 创建用户 + create user "username" with password 'password' + + #创建管理员权限用户 + create user "username" with password 'password' with all privileges + + #删除用户 + drop user "username" + ``` + + + +**结合SpringBoot项目** + +首先导入依赖: + +```xml + + org.influxdb + influxdb-java + 2.12 + +``` + +然后再application.yml 中添加配置信息 + +```yml +influx: + database: klines + #host: http://172.31.10.214:8086 + host: http://47.92.208.93:8086 + username: kline + password: adr1968 +``` + +然后就可以在项目中使用啦。 + + + +**注意:** + +如果在wins启动这个influxdb的时候,需要在先启动下面的influxd.exe,然后启动上面的influx.exe + +![1586485738777](StudyGoup.assets/1586485738777.png) + +![1586485789493](StudyGoup.assets/1586485789493.png) + +![1586485808631](StudyGoup.assets/1586485808631.png) + + + +当然啦,还有和redis一样的可视化工具,influxDB Studio + +![1586485876121](StudyGoup.assets/1586485876121.png) + +### 2.定时器@Scheduled(cron = "0 0/1 * * * ?") + +有的地方也叫 cron表达式 + +项目中遇到,所以研究了一下 + +![1586486120621](StudyGoup.assets/1586486120621.png) + + + +**一些栗子:** + +CRON表达式 含义 +"0 0 12 * * ?" 每天中午十二点触发 +"0 15 10 ? * *" 每天早上10:15触发 +"0 15 10 * * ?" 每天早上10:15触发 +"0 15 10 * * ? *" 每天早上10:15触发 +"0 15 10 * * ? 2005" 2005年的每天早上10:15触发 +"0 * 14 * * ?" 每天从下午2点开始到2点59分每分钟一次触发 +"0 0/5 14 * * ?" 每天从下午2点开始到2:55分结束每5分钟一次触发 +"0 0/5 14,18 * * ?" 每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发 +"0 0-5 14 * * ?" 每天14:00至14:05每分钟一次触发 +"0 10,44 14 ? 3 WED" 三月的每周三的14:10和14:44触发 +"0 15 10 ? * MON-FRI" 每个周一、周二、周三、周四、周五的10:15触发 + + + +0 0 10,14,16 * * ? 每天上午10点,下午2点,4点 +0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时 +0 0 12 ? * WED 表示每个星期三中午12点 +"0 0 12 * * ?" 每天中午12点触发 +"0 15 10 ? * *" 每天上午10:15触发 +"0 15 10 * * ?" 每天上午10:15触发 +"0 15 10 * * ? *" 每天上午10:15触发 +"0 15 10 * * ? 2005" 2005年的每天上午10:15触发 +"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发 +"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发 +"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发 +"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发 +"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发 +"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发 +"0 15 10 15 * ?" 每月15日上午10:15触发 +"0 15 10 L * ?" 每月最后一日的上午10:15触发 +"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发 +"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发 +"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发 + + + +**从前到后 依次的顺序为:** + +1.秒(0~59) + +2.分钟(0~59) + +3.小时(0~23) + +4.天(月)(0~31,但是你需要考虑你月的天数) + +5.月(0~11) + +6.天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT) + +7.年份(1970-2099) + + + +**前面的数字代表各个时间,那么后面的符号代表什么的?** + +每一个域都使用数字,但还可以出现如下特殊字符,它们的含义是: +1)* :表示匹配该域的任意值,假如在Minutes域使用*, **即表示每分钟都会触发事件**。 + +(所以项目中“0 0/1 * * * ?”的就表示任何时候,都是每一分钟执行依次) + +2) ?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和 DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。 + +3) -:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次 + +4) /:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次. + +5) ,:表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。 + +6) L:表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。 + +7) W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一 到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份。 + +8) LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。 + +9) #:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。 + + + +## 2020.4.12 + +### 1.做了两道LeetCode,斐波那契数列和爬楼梯,都可以用递归做,但是动态规划简单,而且这两个题的初始值f(0),f(1)是不一样的,小细节 + +青蛙跳台阶问题和斐波那契数列(兔子出生问题)比较: + +#### 递归思想: + +斐波那契数列: + +```java +public static int fibSeq(int n){ + if(n<0){ + throw new IllegalArgumentException("the param is less than 0"); + } + if(n==0) + return 0; + + if(n==1) + return 1; + return fibSeq(n-1);+fibSeq(n-2); +} + +//王道的经典50道java题写法: +private static int fun(int n){ + if(n==1 || n==2) + return 1; + else + return fun(n-1)+fun(n-2); +} + +``` + +爬楼梯: + +``` +public static int fibSeq(int n){ + if(n<0){ + throw new IllegalArgumentException("the param is less than 0"); + } + if(n==1) + return 1; + + if(n==2) + return 2; + return fibSeq(n-1);+fibSeq(n-2); +} +``` + + + +#### 动态规划思想: + +斐波那契数列: + +```java +public int fib(int n) { //动态规划做 + int a = 0; + int b = 1; + int sum; + for (int i = 0;i + +**缓存穿透情况处理流程:** + + + +一般MySQL 默认的最大连接数在 150 左右,这个可以通过 `show variables like '%max_connections%'; `命令来查看。最大连接数一个还只是一个指标,cpu,内存,磁盘,网络等无力条件都是其运行指标,这些指标都会限制其并发能力!所以,一般 3000 个并发请求就能打死大部分数据库了。 + +**有哪些解决办法?** + +最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮箱格式不对的时候直接返回错误消息给客户端等等。 + +**1)缓存无效 key** : 如果缓存和数据库都查不到某个 key 的数据就写一个到 redis 中去并设置过期时间,具体命令如下:`SET key value EX 10086`。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求key,会导致 redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的 key 的过期时间设置短一点比如 1 分钟。 + +另外,这里多说一嘴,一般情况下我们是这样设计 key 的: `表名:列名:主键名:主键值`。 + + 如果用 Java 代码展示的话,差不多是下面这样的: + +```java +public Object getObjectInclNullById(Integer id) { + // 从缓存中获取数据 + Object cacheValue = cache.get(id); + // 缓存为空 + if (cacheValue == null) { + // 从数据库中获取 + Object storageValue = storage.get(key); + // 缓存空对象 + cache.set(key, storageValue); + // 如果存储数据为空,需要设置一个过期时间(300秒) + if (storageValue == null) { + // 必须设置过期时间,否则有被攻击的风险 + cache.expire(key, 60 * 5); + } + return storageValue; + } + return cacheValue; +} +``` + +**2)布隆过滤器:**布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在与海量数据中。我们需要的就是判断 key 是否合法,有没有感觉布隆过滤器就是我们想要找的那个“人”。具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,我会先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。总结一下就是下面这张图(这张图片不是我画的,为了省事直接在网上找的): + + + +更多关于布隆过滤器的内容可以看我的这篇原创:[《不了解布隆过滤器?一文给你整的明明白白!》](https://github.com/Snailclimb/JavaGuide/blob/master/docs/dataStructures-algorithms/data-structure/bloom-filter.md) ,强烈推荐,个人感觉网上应该找不到总结的这么明明白白的文章了。 + + + +## 2020.4.16 + +### 1.SQL索引,索引数据结构B树,B+树性质特点,为什么不用平衡二叉树二叉树要用B+树,索引优化规则,怎么查看索引使用上了没? + +#### 首先说什么是索引? + +**索引是一种用于快速查询和检索数据的数据结构。常见的索引结构有: B树, B+树和Hash。** + +索引的作用就相当于目录的作用。打个比方: 我们在查字典的时候,如果没有目录,那我们就只能一页一页的去找我们需要查的那个字,速度很慢。如果有目录了,我们只需要先去目录里查找字的位置,然后直接翻到那一页就行了。 + + + +Mysql底层用的数据结构是B+树和哈希表,不过多数是B+树。说B+树之前先说一下B树。 + +哈希表只查一个数据的时候还行,查范围就不行啦。 + +**一个m阶的B树具有如下几个特征:**(B+树的阶数m表示一个节点最多能有m个子节点,也就是每个节点上最多的键值个数)(这里下面的图,m应该表示的是3) + +1.根结点至少有两个子女。 + +2.每个中间节点都至少包含`ceil(m / 2)`个孩子,最多有m个孩子。 (ceil是向上取整,这里是ceil(1.5)= 2) + +3.每一个叶子节点都包含k-1个元素,其中 m/2 <= k <= m。 (1.5<=k<=3) + +4.所有的叶子结点都位于同一层。 + +5.每个节点中的元素从小到大排列,节点当中k-1个元素正好是k个孩子包含的元素的值域分划。 + + + +![img](https:////upload-images.jianshu.io/upload_images/7862980-42f0acde88d3c0cb.png?imageMogr2/auto-orient/strip|imageView2/2/w/825/format/webp) + +#### B+树与B树的区别 + +- 有k个子结点的结点必然有k个关键码; +- 非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中。 +- 树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录。 + +#### 为什么不用平衡二叉树? + +因为随着数据量的增多,二叉树的高度会很高,查询就变慢啦。 + + + +#### 那为什么要用B+树呢? + +以InnoDB为例: + +数据是放在主键索引上面,也就是说实际上在每个节点上还会存放所有的数据 + +使用B树存放数据之后实际是这样子的,会在每个对应的索引列的值上存放上对应的数据 + +![img](StudyGoup.assets/1216484-20190825002227995-188371311.png) + +而B+树则不同,它只会在叶子节点上面挂载数据,非叶子节点不会存放数据,数据只会存在叶子节点上面,非叶子节点只存放索引列的数据 + + ![img](StudyGoup.assets/1216484-20190825002311121-524783044.png) + +这样一个节点就可以存放很多个索引列数据,一次IO就可以拿到很多数据,mysql默认的一个节点16K的大小,可以通过show global status like "Innodb_page_size" 看到该值是16384,每次IO读取16K大小的数据,以索引列是bigInt类型为例,大小8字节,每一条数据还有一个指向下一层的指针6字节,16384/(8+6)=1170,一个节点就大约可以存1170条数据。 + +以一个**层高为3的树**为例,叶子节点存放数据之后大小1KB,那么这个树可以存放 1170 *1170 *16 =21,902,400,**大约2200万条数据**。所以在这种千万级的表中通过主键索引查找一条数据,最多3次IO就可以找到一条数据。而很多时候树的根节点基本都是在内存中,所以多数时候只需要2次IO。 + +叶子节点之间也有双向指针连接,提高区间范围性能,范围查找。 + +创建索引的时候,可以选择索引数据类型,一个是btree一个是hash,hash查找当然也快,但是当遇到范围查找的时候hash就尴尬了,所以根据实际业务需求来看是用btree还是hash。 + +参考:https://www.cnblogs.com/nijunyang/p/11406688.html + + + +#### 索引优化规则 + +王道当时有一个文档:Mysql索引及其优化(王道的文档,有些粗略,没讲模糊查询左右之分) + +**1)如果MySQL估计使用索引比全表扫描还慢,则不会使用索引。** + +返回数据的比例是重要的指标,比例越低越容易命中索引。记住这个范围值——30%,后面所讲的内容都是建立在返回数据的比例在30%以内的基础上。 + +**2)前导模糊查询不能命中索引。** + +name列创建普通索引: + +![img](StudyGoup.assets/169fab4ad1e262c7) + +前导模糊查询不能命中索引: + +EXPLAIN SELECT * FROM user WHERE name LIKE '%s%'; + +![img](StudyGoup.assets/169fab4ae00ad805) + +非前导模糊查询则可以使用索引,可优化为使用非前导模糊查询: + +EXPLAIN SELECT * FROM user WHERE name LIKE 's%'; + +![img](StudyGoup.assets/169fab4ae1b1e740) + +**3)数据类型出现隐式转换的时候不会命中索引,特别是当列类型是字符串,一定要将字符常量值用引号引起来。** + +EXPLAIN SELECT * FROM user WHERE name=1; + +![img](StudyGoup.assets/169fab4aeeea3142) + +EXPLAIN SELECT * FROM user WHERE name='1'; + +![img](StudyGoup.assets/169fab4af64502e8) + +**4)复合索引的情况下,查询条件不包含索引列最左边部分(不满足最左原则),不会命中符合索引。** + +(是不是相当于没有B+树的上面一层,自然找不到下面一层? 有待考究 ) + +name,age,status列创建复合索引: + +ALTER TABLE user ADD INDEX index_name (name,age,status); + +![img](StudyGoup.assets/169fab4ae468dd5f) + +user表索引详情: + +SHOW INDEX FROM user; + +![img](StudyGoup.assets/169fab4ae260d343) + +根据最左原则,可以命中复合索引index_name: + +EXPLAIN SELECT * FROM user WHERE name='swj' AND status=1; + +![img](StudyGoup.assets/169fab4affd9796f) + +注意,最左原则并不是说是查询条件的顺序: + +EXPLAIN SELECT * FROM user WHERE status=1 AND name='swj'; + +![img](StudyGoup.assets/169fab4b0047390e) + +而是查询条件中是否包含索引最左列字段: + +EXPLAIN SELECT * FROM user WHERE status=2 ; + +![img](StudyGoup.assets/169fab4b06548ad2) + +**5)union、in、or都能够命中索引,建议使用in。** + +union: + +EXPLAIN SELECT*FROM user WHERE status=1 + +UNION ALL + +SELECT*FROM user WHERE status = 2; + +![img](StudyGoup.assets/169fab4b06d96e59) + +in: + +EXPLAIN SELECT * FROM user WHERE status IN (1,2); + +![img](StudyGoup.assets/169fab4b14bfcb89) + +or: + +EXPLAIN SELECT*FROM user WHERE status=1OR status=2; + +![img](StudyGoup.assets/169fab4b1c978ec4) + +查询的CPU消耗:or>in>union。 + +**6)用or分割开的条件,如果or前的条件中列有索引,而后面的列中没有索引,那么涉及到的索引都不会被用到。** + +EXPLAIN SELECT * FROM payment WHERE customer_id = 203 OR amount = 3.96; + +![img](StudyGoup.assets/169fab4b23724f6c) + +因为or后面的条件列中没有索引,那么后面的查询肯定要走全表扫描,在存在全表扫描的情况下,就没有必要多一次索引扫描增加IO访问。 + +**7)负向条件查询不能使用索引,可以优化为in查询。** + +负向条件有:**!=、<>、not in、not exists、not like**等。 + +status列创建索引: + +ALTER TABLE user ADD INDEX index_status (status); + +![img](StudyGoup.assets/169fab4b25c75f18) + +user表索引详情: + +SHOW INDEX FROM user; + +![img](StudyGoup.assets/169fab4b2c6ecb0a) + +负向条件不能命中缓存: + +EXPLAIN SELECT * FROM user WHERE status !=1 AND status != 2; + +![img](StudyGoup.assets/169fab4b3331840f) + +可以优化为in查询,但是前提是区分度要高,返回数据的比例在30%以内: + +EXPLAIN SELECT * FROM user WHERE status IN (0,3,4); + +![img](StudyGoup.assets/169fab4b38f8b6a9) + +**8)范围条件查询可以命中索引。范围条件有:<、<=、>、>=、between等。** + +status,age列分别创建索引: + +ALTER TABLE user ADD INDEX index_status (status); + +![img](StudyGoup.assets/169fab4b25c75f18-1587051845458) + +ALTER TABLE user ADD INDEX index_age (age); + +![img](StudyGoup.assets/169fab4b4082bcd3) + +user表索引详情: + +SHOW INDEX FROM user; + +![img](StudyGoup.assets/169fab4b555acbb1) + +范围条件查询可以命中索引: + +EXPLAIN SELECT * FROM user WHERE status>5; + +![img](StudyGoup.assets/169fab4b4450f7a8) + +范围列可以用到索引(联合索引必须是最左前缀),但是范围列后面的列无法用到索引,索引最多用于一个范围列,如果查询条件中有两个范围列则无法全用到索引: + +EXPLAIN SELECT * FROM user WHERE status>5 AND age<24; + +![img](StudyGoup.assets/169fab4b563095df) + +如果是范围查询和等值查询同时存在,优先匹配等值查询列的索引: + +EXPLAIN SELECT * FROM user WHERE status>5 AND age=24; + +![img](StudyGoup.assets/169fab4b5cd19ef9) + +**8)数据库执行计算不会命中索引。** + +EXPLAIN SELECT * FROM user WHERE age>24; + +![img](StudyGoup.assets/169fab4b5f67f5db) + +EXPLAIN SELECT * FROM user WHERE age+1>24; + +![img](StudyGoup.assets/169fab4b627a3aa3) + +计算逻辑应该尽量放到业务层处理,节省数据库的CPU的同时最大限度的命中索引。 + +**9)利用覆盖索引进行查询,避免回表。** + +被查询的列,数据能从索引中取得,而不用通过行定位符row-locator再到row上获取,即“被查询列要被所建的索引覆盖”,这能够加速查询速度。 + +user表的索引详情: + +![img](StudyGoup.assets/169fab4b7a2dd2ac) + +因为status字段是索引列,所以直接从索引中就可以获取值,不必回表查询: + +Using Index代表从索引中查询: + +EXPLAIN SELECT status FROM user where status=1; + +![img](StudyGoup.assets/169fab4b7c297188) + +当查询其他列时,就需要回表查询,这也是为什么要避免SELECT*的原因之一: + +EXPLAIN SELECT * FROM user where status=1; + +![img](StudyGoup.assets/169fab4b80770fc3) + +**10)建立索引的列,不允许为null。** + +单列索引不存null值,复合索引不存全为null的值,如果列允许为null,可能会得到“不符合预期”的结果集,所以,请使用not null约束以及默认值。 + +remark列建立索引: + +ALTER TABLE user ADD INDEX index_remark (remark); + +![img](StudyGoup.assets/169fab4b81039ab4) + +IS NULL可以命中索引: + +EXPLAIN SELECT * FROM user WHERE remark IS NULL; + +![img](StudyGoup.assets/169fab4b97c18e40) + +IS NOT NULL不能命中索引: + +EXPLAIN SELECT * FROM user WHERE remark IS NOT NULL; + +![img](StudyGoup.assets/169fab4b992d6c22) + +虽然IS NULL可以命中索引,但是NULL本身就不是一种好的数据库设计,应该使用NOT NULL约束以及默认值。 + + + +a. 更新十分频繁的字段上不宜建立索引:因为更新操作会变更B+树,重建索引。这个过程是十分消耗数据库性能的。 + +b. 区分度不大的字段上不宜建立索引:类似于性别这种区分度不大的字段,建立索引的意义不大。因为不能有效过滤数据,性能和全表扫描相当。另外返回数据的比例在30%以外的情况下,优化器不会选择使用索引。 + +c. 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。虽然唯一索引会影响insert速度,但是对于查询的速度提升是非常明显的。另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,在并发的情况下,依然有脏数据产生。 + +d. 多表关联时,要保证关联字段上一定有索引。 + +e. 创建索引时避免以下错误观念:索引越多越好,认为一个查询就需要建一个索引;宁缺勿滥,认为索引会消耗空间、严重拖慢更新和新增速度;抵制唯一索引,认为业务的唯一性一律需要在应用层通过“先查后插”方式解决;过早优化,在不了解系统的情况下就开始优化。 + + + +小结: + +**对于自己编写的SQL查询语句,要尽量使用EXPLAIN命令分析一下,做一个对SQL性能有追求的程序员。衡量一个程序员是否靠谱,SQL能力是一个重要的指标。作为后端程序员,深以为然。** + + + +参考:https://www.cnblogs.com/gdjk/p/10668868.html (这篇文章写得真好,美团的一位工程师) + + + + + +补充: + +#### explain + +**用法:在select语句前加上explain** +显示mysql如何使用索引来处理select语句以及连接表,可以帮助选择更好的索引和写出更优化的查询语句 + +参考王道的文档,还有:https://www.jianshu.com/p/f29a619b3ee8 + + + +### 2.MySQL的分库分表几种思路 + +#### **数据库分表:** + +维度: + +##### **垂直拆分** + + 垂直分库(根据业务不同与微服务类似单独服务对应单独库) + 垂直分表 + +垂直分表是基于数据库中的”列”进行,某个表字段较多,可以新建一张扩展表,将不经常用或字段长度较大的字段拆分出去到扩展表中。在字段很多的情况下(例如一个大表有100多个字段),通过”大表拆小表”,更便于开发与维护,也**能避免跨页问题**,MySQL底层是通过数据页存储的,**一条记录占用空间过大会导致跨页,造成额外的性能开销**。另外数据库以行为单位将数据加载到内存中,这样表中字段长度较短且访问频率较高,内存能加载更多的数据,命中率更高,减少了磁盘IO,从而提升了数据库性能。 + +拆分字段的操作建议在数据库设计阶段就做好。如果是在发展过程中拆分,则需要改写以前的查询语句,会额外带来一定的成本和风险,建议谨慎。 + + **垂直拆分优缺点:** + + **优点:** +解决业务系统层面的耦合,业务清晰 +与微服务的治理类似,也能对不同业务的数据进行分级管理、维护、监控、扩展等 +高并发场景下,垂直切分一定程度的提升IO、数据库连接数、单机硬件资源的瓶颈 + 缺点: +部分表无法join,只能通过接口聚合方式解决,提升了开发的复杂度 +依然存在单表数据量过大的问题(需要水平切分) +分布式事务处理复杂 + + + +##### 水平拆分 + +(根据表内数据内在的逻辑关系,将同一个表按不同的条件分散到多个数据库或多个表中,每个表中只包含一部分数据,从而使得单个表的数据量变小,达到分布式的效果。) + + **水平拆分优缺点:** + 优点: +不存在单库数据量过大、高并发的性能瓶颈,提升系统稳定性和负载能力 +应用端改造较小,不需要拆分业务模块 +“冷热数据分离”实现方案 + 缺点: +跨分片事务难以保证 +跨分片的复杂查询如join关联查询 +数据多次扩展难度和维护量极大 + + + +##### 数据分片规则 + + 冷热数据隔离(近6个月或者1年的数据作为热数据,历史数据作为冷数据再进行时间维度拆分) + 地域区域或者其他拆分方式 + userNo范围分表,比如0~500w用户在user1表,501w-1000w在user2表等 + 优点: +单表大小可控 +天然便于水平扩展,后期如果想对整个分片集群扩容时,只需要添加节点即可,无需对其他分片的数 据进行迁移 +使用分片字段进行范围查找时,连续分片可快速定位分片进行快速查询,有效避免跨分片查询的问题 + 缺点: +热点数据成为性能瓶颈。连续分片可能存在数据热点 + + + +##### hash取模mod的切分方式 + + 优点:根据主键id进行数据切分,达到数据均匀分布,使用一致性hash算法可以避免后期扩展问题 + 缺点:跨分片聚合操作 + + + +## 2020.4.19 + +### 1.线程池常用的七个参数设置? + +#### 线程池常用的七个参数,重要的三个: + +**`ThreadPoolExecutor` 3 个最重要的参数)**(先判断核心线程数-->然后判断队列,队列满了-->判断最大容量) + +- **`corePoolSize` :** 核心线程数线程数定义了**最小可以同时运行的线程数量**。(即使这些线程处于空闲状态,这些线程也不会被销毁) +- **`maximumPoolSize` :** 当队列中存放的任务达到队列容量的时候,**当前可以同时运行的线程数量变为最大线程数**。 +- **`workQueue`:** 当新任务来的时候会**先判断当前运行的线程数量是否达到核心线程数**,**如果达到的话,新任务就会被存放在队列中**。 + +`ThreadPoolExecutor`其他常见参数: + +1. **`keepAliveTime`**:当线程池中的线程数量大于 `corePoolSize` 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 `keepAliveTime`才会被回收销毁; +2. **`unit`** : `keepAliveTime` 参数的时间单位。 +3. **`threadFactory`** :executor 创建新线程的时候会用到。 +4. **`handler`** :饱和策略。关于饱和策略下面单独介绍一下。 + +下面这张图可以加深你对线程池中各个参数的相互关系的理解(图片来源:《Java 性能调优实战》): + +![线程池各个参数的关系](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-7/线程池各个参数的关系.jpg) + + + +按照阿里巴巴推荐的设置: + +```java +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +public class ThreadPoolExecutorDemo { + + private static final int CORE_POOL_SIZE = 5; + private static final int MAX_POOL_SIZE = 10; + private static final int QUEUE_CAPACITY = 100; + private static final Long KEEP_ALIVE_TIME = 1L; + public static void main(String[] args) { + + //使用阿里巴巴推荐的创建线程池的方式 + //通过ThreadPoolExecutor构造函数自定义参数创建 + ThreadPoolExecutor executor = new ThreadPoolExecutor( + CORE_POOL_SIZE, + MAX_POOL_SIZE, + KEEP_ALIVE_TIME, + TimeUnit.SECONDS, + new ArrayBlockingQueue<>(QUEUE_CAPACITY), + new ThreadPoolExecutor.CallerRunsPolicy()); + + for (int i = 0; i < 10; i++) { + //创建WorkerThread对象(WorkerThread类实现了Runnable 接口) + Runnable worker = new MyRunnable("" + i); + //执行Runnable + executor.execute(worker); + } + //终止线程池 + executor.shutdown(); + while (!executor.isTerminated()) { + } + System.out.println("Finished all threads"); + } +} +``` + +可以看到我们上面的代码指定了: + +1. `corePoolSize`: 核心线程数为 5。 +2. `maximumPoolSize` :最大线程数 10 +3. `keepAliveTime` : 等待时间为 1L。 +4. `unit`: 等待时间的单位为 TimeUnit.SECONDS。 +5. `workQueue`:任务队列为 `ArrayBlockingQueue`,并且容量为 100; +6. `handler`:饱和策略为 `CallerRunsPolicy`。 + +### 2.常用的几种线程池原码里面参数是怎么样的,缺点是什么样的?平时创建线程的时候,推荐用那种线程池? + +#### 1 FixedThreadPool + +##### 1.1 介绍 + +`FixedThreadPool` 被称为可重用固定线程数的线程池。通过 Executors 类中的相关源代码来看一下相关实现: + +```java + /** + * 创建一个可重用固定数量线程的线程池 + */ + public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(), + threadFactory); + } +``` + +另外还有一个 `FixedThreadPool` 的实现方法,和上面的类似,所以这里不多做阐述: + +```java + public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + } +``` + +**从上面源代码可以看出新创建的 `FixedThreadPool` 的 `corePoolSize` 和 `maximumPoolSize` 都被设置为 nThreads,这个 nThreads 参数是我们使用的时候自己传递的。** + +##### 1.2 执行任务过程介绍 + +`FixedThreadPool` 的 `execute()` 方法运行示意图(该图片来源:《Java 并发编程的艺术》): + +![FixedThreadPool的execute(StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzcxMzc1OTYzLmpwZw.jpg)方法运行示意图](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzcxMzc1OTYzLmpwZw?x-oss-process=image/format,png) + +**上图说明:** + +1. 如果当前运行的线程数小于 corePoolSize, 如果再来新任务的话,就创建新的线程来执行任务; +2. 当前运行的线程数等于 corePoolSize 后, 如果再来新任务的话,会将任务加入 `LinkedBlockingQueue`; +3. 线程池中的线程执行完 手头的任务后,会在循环中反复从 `LinkedBlockingQueue` 中获取任务来执行; + +##### 1.3 为什么不推荐使用`FixedThreadPool`? + +**`FixedThreadPool` 使用无界队列 `LinkedBlockingQueue`(队列的容量为 Intger.MAX_VALUE)作为线程池的工作队列会对线程池带来如下影响 :** + +1. 当线程池中的线程数达到 `corePoolSize` 后,新任务将在无界队列中等待,因此线程池中的线程数不会超过 corePoolSize; +2. 由于使用无界队列时 `maximumPoolSize` 将是一个无效参数,因为不可能存在任务队列满的情况。所以,通过创建 `FixedThreadPool`的源码可以看出创建的 `FixedThreadPool` 的 `corePoolSize` 和 `maximumPoolSize` 被设置为同一个值。 +3. 由于 1 和 2,使用无界队列时 `keepAliveTime` 将是一个无效参数; +4. 运行中的 `FixedThreadPool`(未执行 `shutdown()`或 `shutdownNow()`)不会拒绝任务,在任务比较多的时候会导致 OOM(内存溢出)。 + +#### 2 SingleThreadExecutor 详解 + +##### 2.1 介绍 + +`SingleThreadExecutor` 是只有一个线程的线程池。下面看看**SingleThreadExecutor 的实现:** + +```java + /** + *返回只有一个线程的线程池 + */ + public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue(), + threadFactory)); + } +``` + +```java + public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); + } +``` + +从上面源代码可以看出新创建的 `SingleThreadExecutor` 的 `corePoolSize` 和 `maximumPoolSize` 都被设置为 1.其他参数和 `FixedThreadPool` 相同。 + +##### 2.2 执行任务过程介绍 + +**`SingleThreadExecutor` 的运行示意图(该图片来源:《Java 并发编程的艺术》):** +![SingleThreadExecutor的运行示意图](StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzgyMjc2NDU4LmpwZw.jpg) + +**上图说明;** + +1. 如果当前运行的线程数少于 corePoolSize,则创建一个新的线程执行任务; +2. 当前线程池中有一个运行的线程后,将任务加入 `LinkedBlockingQueue` +3. 线程执行完当前的任务后,会在循环中反复从`LinkedBlockingQueue` 中获取任务来执行; + +##### 2.3 为什么不推荐使用`SingleThreadExecutor`? + +`SingleThreadExecutor` 使用无界队列 `LinkedBlockingQueue` 作为线程池的工作队列(队列的容量为 Intger.MAX_VALUE)。`SingleThreadExecutor` 使用无界队列作为线程池的工作队列会对线程池带来的影响与 `FixedThreadPool` 相同。说简单点就是可能会导致 OOM, + +#### 3 CachedThreadPool 详解 + +##### 3.1 介绍 + +`CachedThreadPool` 是一个会根据需要创建新线程的线程池。下面通过源码来看看 `CachedThreadPool` 的实现: + +```java + /** + * 创建一个线程池,根据需要创建新线程,但会在先前构建的线程可用时重用它。 + */ + public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue(), + threadFactory); + } + +``` + +```java + public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); + } +``` + +`CachedThreadPool` 的`corePoolSize` 被设置为空(0),`maximumPoolSize`被设置为 Integer.MAX.VALUE,即它是无界的,这也就意味着如果主线程提交任务的速度高于 `maximumPool` 中线程处理任务的速度时,`CachedThreadPool` 会不断创建新的线程。极端情况下,这样会导致耗尽 cpu 和内存资源。 + +##### 3.2 执行任务过程介绍 + +**CachedThreadPool 的 execute()方法的执行示意图(该图片来源:《Java 并发编程的艺术》):** +![CachedThreadPool的execute(StudyGoup.assets/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzE4NjExNzY3LmpwZw.jpg)方法的执行示意图](https://imgconvert.csdnimg.cn/aHR0cDovL215LWJsb2ctdG8tdXNlLm9zcy1jbi1iZWlqaW5nLmFsaXl1bmNzLmNvbS8xOC00LTE2LzE4NjExNzY3LmpwZw?x-oss-process=image/format,png) + +**上图说明:** + +1. 首先执行 `SynchronousQueue.offer(Runnable task)` 提交任务到任务队列。如果当前 `maximumPool` 中有闲线程正在执行 `SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)`,那么主线程执行 offer 操作与空闲线程执行的 `poll` 操作配对成功,主线程把任务交给空闲线程执行,`execute()`方法执行完成,否则执行下面的步骤 2; +2. 当初始 `maximumPool` 为空,或者 `maximumPool` 中没有空闲线程时,将没有线程执行 `SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)`。这种情况下,步骤 1 将失败,此时 `CachedThreadPool` 会创建新线程执行任务,execute 方法执行完成; + +##### 3.3 为什么不推荐使用`CachedThreadPool`? + +`CachedThreadPool`允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致 OOM。 + +## 2020.4.23 + +### 1.Callable和Runable有什么区别,run()和start()有什么区别,join()和yield()有什么区别,execute()和submit()的区别,shutdown()和shutdownNow()的区别,isTerminated()和isShutdown()区别 + +#### 1 `Runnable` vs `Callable` + +`Runnable`自 Java 1.0 以来一直存在,但`Callable`仅在 Java 1.5 中引入,目的就是为了来处理`Runnable`不支持的用例。**`Runnable` 接口**不会返回结果或抛出检查异常,但是**`Callable` 接口**可以。所以,如果任务不需要返回结果或抛出异常推荐使用 **`Runnable` 接口**,这样代码看起来会更加简洁。 + +工具类 `Executors` 可以实现 `Runnable` 对象和 `Callable` 对象之间的相互转换。(`Executors.callable(Runnable task`)或 `Executors.callable(Runnable task,Object resule)`)。 + +`Runnable.java` + +```java +@FunctionalInterface +public interface Runnable { + /** + * 被线程执行,没有返回值也无法抛出异常 + */ + public abstract void run(); +} +``` + +`Callable.java` + +```java +@FunctionalInterface +public interface Callable { + /** + * 计算结果,或在无法这样做时抛出异常。 + * @return 计算得出的结果 + * @throws 如果无法计算结果,则抛出异常 + */ + V call() throws Exception; +} + +``` + +#### 2 `execute()` vs `submit()` + +1. **`execute()`方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;** +2. **`submit()`方法用于提交需要返回值的任务。线程池会返回一个 `Future` 类型的对象,通过这个 `Future` 对象可以判断任务是否执行成功**,并且可以通过 `Future` 的 `get()`方法来获取返回值,`get()`方法会阻塞当前线程直到任务完成,而使用 `get(long timeout,TimeUnit unit)`方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。 + +我们以**`AbstractExecutorService`**接口中的一个 `submit` 方法为例子来看看源代码: + +```java + public Future submit(Runnable task) { + if (task == null) throw new NullPointerException(); + RunnableFuture ftask = newTaskFor(task, null); + execute(ftask); + return ftask; + } +``` + +上面方法调用的 `newTaskFor` 方法返回了一个 `FutureTask` 对象。 + +```java + protected RunnableFuture newTaskFor(Runnable runnable, T value) { + return new FutureTask(runnable, value); + } +``` + +我们再来看看`execute()`方法: + +```java + public void execute(Runnable command) { + ... + } +``` + +#### 3 `shutdown()`VS`shutdownNow()` + +- **`shutdown()`** :关闭线程池,线程池的状态变为 `SHUTDOWN`。线程池不再接受新任务了,但是队列里的任务得执行完毕。 +- **`shutdownNow()`** :关闭线程池,线程的状态变为 `STOP`。线程池会终止当前正在运行的任务,并停止处理排队的任务并返回正在等待执行的 List。 + +#### 4 `isTerminated()` VS `isShutdown()` + +- **`isShutDown`** 当调用 `shutdown()` 方法后返回为 true。 +- **`isTerminated`** 当调用 `shutdown()` 方法后,并且所有提交的任务完成后返回为 true + +#### 5.run( ) VS start() + +```java +public class CallableDemo1 { + public static void main(String[] args) { + //创建方式有点和前两种都不一样 + MyCallable callable1 = new MyCallable(); + MyCallable callable2 = new MyCallable(); + MyCallable callable3 = new MyCallable(); + FutureTask task1 = new FutureTask(callable1); + FutureTask task2 = new FutureTask(callable1); + FutureTask task3 = new FutureTask(callable1); + new Thread(task1,"窗口1:").run(); + new Thread(task2,"窗口2:").run(); + new Thread(task3,"窗口3:").run(); + + try { + System.out.println("窗口1返回的结果为:" + task1.get()); + System.out.println("窗口2返回的结果为:" + task2.get()); + System.out.println("窗口3返回的结果为:" + task3.get()); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } +} +``` + +看上面线程创建的第三种方式,如果把.start(),修改为.run(),结果会怎么呢? + +```java +main:正在卖票,剩下票数:29 +main:正在卖票,剩下票数:28 +main:正在卖票,剩下票数:27 +main:正在卖票,剩下票数:26 +main:正在卖票,剩下票数:25 +main:正在卖票,剩下票数:24 +main:正在卖票,剩下票数:23 +main:正在卖票,剩下票数:22 +main:正在卖票,剩下票数:21 +main:正在卖票,剩下票数:20 +main:正在卖票,剩下票数:19 +main:正在卖票,剩下票数:18 +main:正在卖票,剩下票数:17 +main:正在卖票,剩下票数:16 +main:正在卖票,剩下票数:15 +main:正在卖票,剩下票数:14 +main:正在卖票,剩下票数:13 +main:正在卖票,剩下票数:12 +main:正在卖票,剩下票数:11 +main:正在卖票,剩下票数:10 +main:正在卖票,剩下票数:9 +main:正在卖票,剩下票数:8 +main:正在卖票,剩下票数:7 +main:正在卖票,剩下票数:6 +main:正在卖票,剩下票数:5 +main:正在卖票,剩下票数:4 +main:正在卖票,剩下票数:3 +main:正在卖票,剩下票数:2 +main:正在卖票,剩下票数:1 +main:正在卖票,剩下票数:0 +窗口1返回的结果为:票卖完了 +窗口2返回的结果为:票卖完了 +窗口3返回的结果为:票卖完了 +``` + +就是说,调用start()方法,其实执行的是异步操作,三个线程进行售票。 + +如果在main()方法里面调用的是run()方法,则进行的是同步操作,就是说,其实只有main方法在执行。 + +#### 6.join() VS yield() + +线程的转换状态: + +![1587439715060](StudyGoup.assets/1587439715060.png) + +线程的生命周期: + +![1587439674710](StudyGoup.assets/1587439674710.png) + +由上面图可以看出: + +**yield()**(这个也叫礼让状态)方法和 sleep() 方法类似,也不会释放“锁标志”,区别在于,它没有参数,即yield()方法只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行,另外yield()方法**只能使同优先级或者高优先级的线程得到执行机会**,这也和sleep()方法不同。 +**join()** (这个也叫自闭状态)方法会使当前线程等待调用join()方法的线程结束后才能继续执行,例如: + +```java +public class TestJoin { + + public static void main(String[] args) { + Thread thread = new Thread(new JoinDemo()); + thread.start(); + + for (int i = 0; i < 20; i++) { + System.out.println("主线程第" + i + "次执行!"); + if (i >= 2) + try { + // t1线程合并到主线程中,主线程停止执行过程,转而执行t1线程,直到t1执行完毕后继续。 + thread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } +} + +class JoinDemo implements Runnable { + + @Override + public void run() { + for (int i = 0; i < 10; i++) { + System.out.println("线程1第" + i + "次执行!"); + } + } +} +``` + +运行结果: + +```java +主线程第0次执行! +主线程第1次执行! +主线程第2次执行! +线程1第0次执行! +线程1第1次执行! +线程1第2次执行! +线程1第3次执行! +线程1第4次执行! +线程1第5次执行! +线程1第6次执行! +线程1第7次执行! +线程1第8次执行! +线程1第9次执行! +主线程第3次执行! +主线程第4次执行! +主线程第5次执行! +主线程第6次执行! +主线程第7次执行! +主线程第8次执行! +主线程第9次执行! +主线程第10次执行! +主线程第11次执行! +主线程第12次执行! +主线程第13次执行! +主线程第14次执行! +主线程第15次执行! +主线程第16次执行! +主线程第17次执行! +主线程第18次执行! +主线程第19次执行! +``` + + + +参考:https://www.cnblogs.com/aqiu-jiang/p/11850692.html + +#### 7.sleep() VS wait() + +- 两者最主要的区别在于:**sleep 方法没有释放锁,而 wait 方法释放了锁** 。 +- 两者都可以暂停线程的执行。 +- Wait 通常被用于线程间交互/通信,sleep 通常被用于暂停执行。 +- wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或者可以使用 wait(long timeout)超时后线程会自动苏醒。 + +### 2.乐观锁和悲观锁区别和使用 + +#### 乐观锁: + +总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。 + +#### 悲观锁: + +总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。 + + + +#### 乐观锁应用: + +##### 1.版本号机制(项目中使用的是每次更新数据以后更新时间,判断时间是不是一样) + +​ 一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。 + +举一个简单的例子: + +假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。当需要对账户信息表进行更新的时候,需要首先读取version字段。 + +操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。 +在操作员 A 操作的过程中,操作员B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。 +操作员 A 完成了修改工作,提交更新之前会先看数据库的版本和自己读取到的版本是否一致,一致的话,就会将数据版本号加1( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。 +操作员 B 完成了操作,提交更新之前会先看数据库的版本和自己读取到的版本是否一致,但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,而自己读取到的版本号为1 ,不满足 “ 当前最后更新的version与操作员第一次读取的版本号相等 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。 +这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。 + +##### 2.CAS算法 + +即compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三个操作数 + +需要读写的内存值 V +进行比较的值 A +拟写入的新值 B +当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。 + +#### 乐观锁的缺点 + +ABA 问题是乐观锁一个常见的问题 + +##### 1 ABA 问题 + +如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 "ABA"问题。 + +JDK 1.5 以后的 AtomicStampedReference 类就提供了此种能力,其中的 compareAndSet 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。 + +## 2020.4.26 + +### 1.JDK1.6 之后的底层优化(偏向锁,轻量级锁,自旋锁和自适应自旋,锁消除,锁粗化) + +JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、自旋锁、适应性自旋锁、锁消除、锁粗化等技术来减少锁操作的开销。 + +锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。 + +**①偏向锁** + +**引入偏向锁的目的和引入轻量级锁的目的很像,他们都是为了没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。但是不同是:轻量级锁在无竞争的情况下使用 CAS 操作去代替使用互斥量。而偏向锁在无竞争的情况下会把整个同步都消除掉**。 + +偏向锁的“偏”就是偏心的偏,它的意思是会偏向于第一个获得它的线程,如果在接下来的执行中,该锁没有被其他线程获取,那么持有偏向锁的线程就不需要进行同步!关于偏向锁的原理可以查看《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版的13章第三节锁优化。 + +但是对于锁竞争比较激烈的场合,偏向锁就失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这种场合下不应该使用偏向锁,否则会得不偿失,需要注意的是,偏向锁失败后,并不会立即膨胀为重量级锁,而是先升级为轻量级锁。 + +**② 轻量级锁** + +倘若偏向锁失败,虚拟机并不会立即升级为重量级锁,它还会尝试使用一种称为轻量级锁的优化手段(1.6之后加入的)。**轻量级锁不是为了代替重量级锁,它的本意是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗,因为使用轻量级锁时,不需要申请互斥量。另外,轻量级锁的加锁和解锁都用到了CAS操作。** 关于轻量级锁的加锁和解锁的原理可以查看《深入理解Java虚拟机:JVM高级特性与最佳实践》第二版的13章第三节锁优化。 + +**轻量级锁能够提升程序同步性能的依据是“对于绝大部分锁,在整个同步周期内都是不存在竞争的”,这是一个经验数据。如果没有竞争,轻量级锁使用 CAS 操作避免了使用互斥操作的开销。但如果存在锁竞争,除了互斥量开销外,还会额外发生CAS操作,因此在有锁竞争的情况下,轻量级锁比传统的重量级锁更慢!如果锁竞争激烈,那么轻量级将很快膨胀为重量级锁!** + +**③ 自旋锁和自适应自旋** + +轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。 + +互斥同步对性能最大的影响就是阻塞的实现,因为挂起线程/恢复线程的操作都需要转入内核态中完成(用户态转换到内核态会耗费时间)。 + +**一般线程持有锁的时间都不是太长,所以仅仅为了这一点时间去挂起线程/恢复线程是得不偿失的。** 所以,虚拟机的开发团队就这样去考虑:“我们能不能让后面来的请求获取锁的线程等待一会而不被挂起呢?看看持有锁的线程是否很快就会释放锁”。**为了让一个线程等待,我们只需要让线程执行一个忙循环(自旋),这项技术就叫做自旋**。 + +百度百科对自旋锁的解释: + +> 何谓自旋锁?它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。 + +自旋锁在 JDK1.6 之前其实就已经引入了,不过是默认关闭的,需要通过`--XX:+UseSpinning`参数来开启。JDK1.6及1.6之后,就改为默认开启的了。需要注意的是:自旋等待不能完全替代阻塞,因为它还是要占用处理器时间。如果锁被占用的时间短,那么效果当然就很好了!反之,相反!自旋等待的时间必须要有限度。如果自旋超过了限定次数任然没有获得锁,就应该挂起线程。**自旋次数的默认值是10次,用户可以修改`--XX:PreBlockSpin`来更改**。 + +另外,**在 JDK1.6 中引入了自适应的自旋锁。自适应的自旋锁带来的改进就是:自旋的时间不在固定了,而是和前一次同一个锁上的自旋时间以及锁的拥有者的状态来决定,虚拟机变得越来越“聪明”了**。 + +**④ 锁消除** + +锁消除理解起来很简单,它指的就是虚拟机即使编译器在运行时,如果检测到那些共享数据不可能存在竞争,那么就执行锁消除。锁消除可以节省毫无意义的请求锁的时间。 + +**⑤ 锁粗化** + +原则上,我们在编写代码的时候,总是推荐将同步块的作用范围限制得尽量小,——直在共享数据的实际作用域才进行同步,这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那等待线程也能尽快拿到锁。 + +大部分情况下,上面的原则都是没有问题的,但是如果一系列的连续操作都对同一个对象反复加锁和解锁,那么会带来很多不必要的性能消耗。 + + + +其实JavaGuide里面没有说清楚,在《深入理解JVM虚拟机》P400,后面还有一句话: + +比如代码: + +```java +public String concatString(String s1,String s2,String s3){ + StringBuffer sb = new StringBuffer(); + sb.append(s1); + sb.append(s2); + sb.append(s3); + return sb.toString(); +} +``` + +如果虚拟机探测到这样一串操作都是对一个对象加锁,那么将会把锁同步的范围扩展(粗化)到整个操作系统序列化的外部,会将锁扩展到第一个append()之前,或者最后一个append()之后,这样值只需要加一次锁即可。 + +### 2.i++线程安全吗? + +如果这里的i是局部变量,肯定是安全的,其他线程调用不到它,这里主要讨论的是i是全局变量的时候,是不是线程安全的。 + +答案当然不是线程安全的。因为这个操作既不满足内存可见性,也不满足原子性。 + + + +先来看下面的示例来验证下 i++ 到底是不是线程安全的。 + +1000个线程,每个线程对共享变量 count 进行 1000 次 ++ 操作。 + +```java +public class ThreadSafeTest { + static int count = 0; + static CountDownLatch cd1 = new CountDownLatch(1000); + + public static void main(String[] args) throws InterruptedException { + CountRunnable countRunnable = new CountRunnable(); + for (int i = 0; i < 1000; i++) { + new Thread(countRunnable).start(); + } + cd1.await(); + System.out.println(count); + } + + static class CountRunnable implements Runnable { + private void count() { + for (int i = 0; i < 1000; i++) { + count++; + } + } + + @Override + public void run() { + count(); + cd1.countDown(); + } + } +} +``` + +上面的例子我们期望的结果应该是 1000000,但运行 N 遍,你会发现总是不为 1000000,至少你现在知道了 i++ +操作它不是线程安全的了。 + +(上面代码亲自测试过,只有很少的时候才会出现1000000,多数时候都是比它小很多) + +#### JMM 模型中对共享变量的读写原理。 + +![clipboard.png](StudyGoup.assets/2456362658-5b32f1212cb9a_articlex.png) + +**每个线程都有自己的工作内存,每个线程需要对共享变量操作时必须先把共享变量从主内存 load 到自己的工作内存,等完成对共享变量的操作时再 save 到主内存。** + +问题就出在这了,如果一个线程运算完后还没刷到主内存,此时这个共享变量的值被另外一个线程从主内存读取到了,这个时候读取的数据就是脏数据了,它会覆盖其他线程计算完的值。。。 + +这也是经典的内存不可见问题,那么把 count 加上 volatile 让内存可见是否能解决这个问题呢? 答案是:不能。因为 +**volatile 只能保证可见性,不能保证原子性**。多个线程同时读取这个共享变量的值,就算保证其他线程修改的可见性,也不能保证线程之间读取到同样的值然后相互覆盖对方的值的情况。 + +关于多线程的几种关键概念请翻阅《多线程之原子性、可见性、有序性详解》这篇文章。 + +#### 解决方案 + +- 说了这么多,对于 i++ 这种线程不安全问题有没有其他解决方案呢?当然有,请参考以下几种解决方案。 + + 1、对 i++ 操作的方法加同步锁,同时只能有一个线程执行 i++ 操作; + + ```java + private synchronized void count() { //synchronized 关键字既可以保证可见性,也可以保证原子性 + for (int i = 0; i < 1000; i++) { + count++; + } + } + ``` + + 2、使用支持原子性操作的类,如 **java.util.concurrent.atomic.AtomicInteger**,它使用的是 + CAS 算法,效率优于第 1 种; + + ```java + public class ThreadSafeTest { + //static int count = 0; + static AtomicInteger count = new AtomicInteger(); + static CountDownLatch cd1 = new CountDownLatch(1000); + + public static void main(String[] args) throws InterruptedException { + CountRunnable countRunnable = new CountRunnable(); + for (int i = 0; i < 1000; i++) { + new Thread(countRunnable).start(); + } + cd1.await(); + System.out.println(count); + } + + static class CountRunnable implements Runnable { + private void count() { + for (int i = 0; i < 1000; i++) { + //count++; + count.getAndIncrement(); + } + } + + @Override + public void run() { + count(); + cd1.countDown(); + } + } + } + ``` + + + +参考:https://segmentfault.com/a/1190000015401766 (segmentfault上面一篇好文章) + +上面解决方法用到, CountDownLatch 参考:https://www.jianshu.com/p/e233bb37d2e6 见下面文章 + +## 2020.5.14 + +## 注册中心Eureka + +Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现。也是springcloud体系中最重要最核心的组件之一。 + +### 背景介绍 + +#### 服务中心 + +服务中心又称注册中心,管理各种服务功能包括服务的注册、发现、熔断、负载、降级等,比如dubbo admin后台的各种功能。 + +有了服务中心调用关系会有什么变化,画几个简图来帮忙理解 + +项目A调用项目B + +正常调用项目A请求项目B + +![img](StudyGoup.assets/ab.jpg) + +有了服务中心之后,任何一个服务都不能直接去掉用,都需要通过服务中心来调用 + +![img](StudyGoup.assets/a2b.jpg) + +项目A调用项目B,项目B在调用项目C + +![img](StudyGoup.assets/abc.jpg) + +这时候调用的步骤就会为两步:第一步,项目A首先从服务中心请求项目B服务器,然后项目B在从服务中心请求项目C服务。 + +![img](StudyGoup.assets/a2b2c.jpg) + +上面的项目只是两三个相互之间的简单调用,但是如果项目超过20个30个呢,在15年底的时候我司分布式的项目就达到了二十几个,画一张图来描述几十个项目之间的相互调用关系全是线条,任何其中的一个项目改动,就会牵连好几个项目跟着重启,巨麻烦而且容易出错。通过服务中心来获取服务你不需要关注你调用的项目IP地址,由几台服务器组成,每次直接去服务中心获取可以使用的服务去调用既可。 + +由于各种服务都注册到了服务中心,就有了去做很多高级功能条件。比如几台服务提供相同服务来做均衡负载;监控服务器调用成功率来做熔断,移除服务列表中的故障点;监控服务调用时间来对不同的服务器设置不同的权重等等。 + +说Eureka之前我先八卦一下Netflix + +#### Netflix + +以下介绍来自于百度百科: + +> Netflix是一家美国公司,在美国、加拿大提供互联网随选流媒体播放,定制DVD、蓝光光碟在线出租业务。该公司成立于1997年,总部位于加利福尼亚州洛斯盖图,1999年开始订阅服务。2009年,该公司可提供多达10万部DVD电影,并有1千万的订户。2007年2月25日,Netflix宣布已经售出第10亿份DVD。HIS一份报告中表示,2011年Netflix网络电影销量占据美国用户在线电影总销量的45%。 + +我第一次看到这个单词的时候,是在各种美剧或者电影的开头,Netflix拍摄的代表性的美剧有《纸牌屋》、《毒枭》、《怪奇物语》。后来研究springcloud的时候发现了Netflix公司,就在想它们是不是同一家公司,经过核对github上面邮件后缀判定确实是同一家公司,其实springcloud的微服务就基于Netflix公司的开源产品来做的。 + +Netflix的开源框架组件已经在Netflix的大规模分布式微服务环境中经过多年的生产实战验证,正逐步被社区接受为构造微服务框架的标准组件。Spring Cloud开源产品,主要是基于对Netflix开源组件的进一步封装,方便Spring开发人员构建微服务基础框架。对于一些打算构建微服务框架体系的公司来说,充分利用或参考借鉴Netflix的开源微服务组件(或Spring Cloud),在此基础上进行必要的企业定制,无疑是通向微服务架构的捷径。 + +#### Eureka + +按照官方介绍: + +> Eureka is a REST (Representational State Transfer) based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers. +> +> Eureka 是一个基于 REST 的服务,主要在 AWS 云中使用, 定位服务来进行中间层服务器的负载均衡和故障转移。 + +Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现。Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server,并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。 + +Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。 + +用一张图来认识以下: + +![img](StudyGoup.assets/eureka-architecture-overview.png) + +上图简要描述了Eureka的基本架构,由3个角色组成: + +1、Eureka Server + +- 提供服务注册和发现 + +2、Service Provider + +- 服务提供方 +- 将自身服务注册到Eureka,从而使服务消费方能够找到 + +3、Service Consumer + +- 服务消费方 +- 从Eureka获取注册服务列表,从而能够消费服务 + +### 案例实践 + +#### Eureka Server + +spring cloud已经帮我实现了服务注册中心,我们只需要很简单的几个步骤就可以完成。 + +1、pom中添加依赖 + +```xml + + + org.springframework.cloud + spring-cloud-starter + + + org.springframework.cloud + spring-cloud-starter-eureka-server + + + org.springframework.boot + spring-boot-starter-test + test + + +``` + +2、添加启动代码中添加`@EnableEurekaServer`注解 + +```java +@SpringBootApplication +@EnableEurekaServer +public class SpringCloudEurekaApplication { + + public static void main(String[] args) { + SpringApplication.run(SpringCloudEurekaApplication.class, args); + } +} +``` + +3、配置文件 + +在默认设置下,该服务注册中心也会将自己作为客户端来尝试注册它自己,所以我们需要禁用它的客户端注册行为,在`application.properties`添加以下配置: + +```properties +spring.application.name=spring-cloud-eureka + +server.port=8000 +eureka.client.register-with-eureka=false +eureka.client.fetch-registry=false + +eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/ +``` + +- `eureka.client.register-with-eureka` :表示是否将自己注册到Eureka Server,默认为true。 +- `eureka.client.fetch-registry` :表示是否从Eureka Server获取注册信息,默认为true。 +- `eureka.client.serviceUrl.defaultZone` :设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址。默认是http://localhost:8761/eureka ;多个地址可使用 , 分隔。 + +启动工程后,访问:http://localhost:8000/,可以看到下面的页面,其中还没有发现任何服务 + +![img](StudyGoup.assets/eureka_start.jpg) + +### 集群 + +注册中心这么关键的服务,如果是单点话,遇到故障就是毁灭性的。在一个分布式系统中,服务注册中心是最重要的基础部分,理应随时处于可以提供服务的状态。为了维持其可用性,使用集群是很好的解决方案。Eureka通过互相注册的方式来实现高可用的部署,所以我们只需要将Eureke Server配置其他可用的serviceUrl就能实现高可用部署。 + +#### 双节点注册中心 + +首次我们尝试一下双节点的注册中心的搭建。 + +1、创建application-peer1.properties,作为peer1服务中心的配置,并将serviceUrl指向peer2 + +```properties +spring.application.name=spring-cloud-eureka +server.port=8000 +eureka.instance.hostname=peer1 + +eureka.client.serviceUrl.defaultZone=http://peer2:8001/eureka/ +``` + +2、创建application-peer2.properties,作为peer2服务中心的配置,并将serviceUrl指向peer1 + +```properties +spring.application.name=spring-cloud-eureka +server.port=8001 +eureka.instance.hostname=peer2 + +eureka.client.serviceUrl.defaultZone=http://peer1:8000/eureka/ +``` + +3、host转换 + +在hosts文件中加入如下配置 + +```properties +127.0.0.1 peer1 +127.0.0.1 peer2 +``` + +4、打包启动 + +依次执行下面命令 + +```properties +#打包 +mvn clean package +# 分别以peer1和peeer2 配置信息启动eureka +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 +``` + +依次启动完成后,浏览器输入:`http://localhost:8000/` 效果图如下: + +![img](StudyGoup.assets/eureka-two.jpg) + +根据图可以看出peer1的注册中心DS Replicas已经有了peer2的相关配置信息,并且出现在available-replicas中。我们手动停止peer2来观察,发现peer2就会移动到unavailable-replicas一栏中,表示peer2不可用。 + +到此双节点的配置已经完成。 + +#### eureka集群使用 + +在生产中我们可能需要三台或者大于三台的注册中心来保证服务的稳定性,配置的原理其实都一样,将注册中心分别指向其它的注册中心。这里只介绍三台集群的配置情况,其实和双节点的注册中心类似,每台注册中心分别又指向其它两个节点即可,使用application.yml来配置。 + +application.yml配置详情如下: + +``` +--- +spring: + application: + name: spring-cloud-eureka + profiles: peer1 +server: + port: 8000 +eureka: + instance: + hostname: peer1 + client: + serviceUrl: + defaultZone: http://peer2:8001/eureka/,http://peer3:8002/eureka/ +--- +spring: + application: + name: spring-cloud-eureka + profiles: peer2 +server: + port: 8001 +eureka: + instance: + hostname: peer2 + client: + serviceUrl: + defaultZone: http://peer1:8000/eureka/,http://peer3:8002/eureka/ +--- +spring: + application: + name: spring-cloud-eureka + profiles: peer3 +server: + port: 8002 +eureka: + instance: + hostname: peer3 + client: + serviceUrl: + defaultZone: http://peer1:8000/eureka/,http://peer2:8001/eureka/ +``` + +分别以peer1、peer2、peer3的配置参数启动eureka注册中心。 + +``` +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2 +java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer3 +``` + +依次启动完成后,浏览器输入:`http://localhost:8000/` 效果图如下: + +![img](StudyGoup.assets/eureka-cluster.jpg) + +可以在peer1中看到了peer2、peer3的相关信息。至此eureka集群也已经完成了 + + + + + + +