diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2372392 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Auto detect text files and perform LF normalization +* text=auto +*.sh linguist-language=java +*.py linguist-language=java diff --git a/.gitignore b/.gitignore index 0f95020..8cdc1f5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,10 @@ *.bak .vscode/ *.log +*.code-workspace + +ignore.ini +refresh_catalog.py +refresh_md.sh + +bin/ diff --git a/Ai/KnowledgeGraph.md b/Ai/KnowledgeGraph.md new file mode 100644 index 0000000..3a28b7b --- /dev/null +++ b/Ai/KnowledgeGraph.md @@ -0,0 +1,26 @@ +--- +title: KnowledgeGraph +date: 2024-10-13 17:59:35 +tags: +categories: +--- + +💠 + +- 1. [知识图谱](#知识图谱) +- 2. [案例实践项目](#案例实践项目) + +💠 2024-10-13 18:30:08 +**************************************** +# 知识图谱 + +> [知识图谱 - 维基百科](https://zh.wikipedia.org/zh-cn/%E7%9F%A5%E8%AD%98%E5%9C%96%E8%AD%9C) + +> [Note: 图数据库](/Database/Graph.md) + +************************ + +# 案例实践项目 + +> [liuhuanyong/QASystemOnMedicalKG: 以疾病为中心的一定规模医药领域知识图谱](https://github.com/liuhuanyong/QASystemOnMedicalKG)`项目过于久远,依赖和环境有问题` + diff --git a/Algorithm/Algorithm.md b/Algorithm/Algorithm.md new file mode 100644 index 0000000..d1b0b1d --- /dev/null +++ b/Algorithm/Algorithm.md @@ -0,0 +1,505 @@ +--- +title: 数据结构和算法 +date: 2018-11-21 10:56:52 +tags: +categories: + - 算法 +--- + +💠 + +- 1. [数据结构和算法](#数据结构和算法) + - 1.1. [相关书籍和资源](#相关书籍和资源) + - 1.2. [基础概念](#基础概念) + - 1.2.1. [算法的重要特征](#算法的重要特征) + - 1.2.2. [时间复杂度](#时间复杂度) + - 1.2.2.1. [时间复杂度分析](#时间复杂度分析) + - 1.2.2.2. [最好最坏时间复杂度](#最好最坏时间复杂度) + - 1.2.2.3. [平均时间复杂度](#平均时间复杂度) + - 1.2.2.4. [均摊时间复杂度](#均摊时间复杂度) + - 1.2.3. [空间复杂度](#空间复杂度) + - 1.3. [基础数据结构](#基础数据结构) + - 1.3.1. [线性表](#线性表) + - 1.3.1.1. [数组](#数组) + - 1.3.1.2. [链表](#链表) + - 1.3.1.3. [数组和链表对比](#数组和链表对比) + - 1.3.2. [树](#树) + - 1.4. [匹配算法](#匹配算法) + - 1.5. [排序算法](#排序算法) + - 1.6. [搜索算法](#搜索算法) + - 1.7. [哈希算法 Hash](#哈希算法-hash) + - 1.7.1. [Hash函数 构造](#hash函数-构造) + - 1.7.2. [HASH 冲突](#hash-冲突) + - 1.7.2.1. [开放定址法](#开放定址法) + - 1.7.2.2. [再 HASH 法](#再-hash-法) + - 1.7.2.3. [链地址/拉链 法](#链地址拉链-法) + - 1.7.2.4. [公共溢出区](#公共溢出区) + - 1.7.3. [安全相关](#安全相关) +- 2. [密码学](#密码学) + - 2.1. [Diffie-Hellman Key Exchange算法](#diffie-hellman-key-exchange算法) +- 3. [实际问题](#实际问题) + - 3.1. [斐波那契数列](#斐波那契数列) + +💠 2024-09-05 11:52:54 +**************************************** +# 数据结构和算法 +> 数据结构是指一组数据的存储结构 算法就是操作数据的方法 数据结构和算法是相辅相成的,数据结构是为算法服务的,而算法要作用在特定的数据结构之上 + +![](/Algorithm/img/001-algorithm-base.km.svg) + +## 相关书籍和资源 +- 大话数据结构 +- 算法图解 [Github](https://github.com/egonSchiele/grokking_algorithms) +- 算法的乐趣 + +- 算法 [official site](https://algs4.cs.princeton.edu/home/) +- 算法导论(英文原版更好) 麻省理工有公开课 +- 数据结构与算法分析 C C++ Java 三种语言版本 +- 数据结构 严蔚敏 +- 数据结构与算法解析 高一凡 + +- 剑指Offer +- 编程珠玑 对大量数据问题的算法研究 +- 编程之美 微软面试官所著,难度较高 + +- 计算机程序设计艺术 +- 算法帝国 +- 数学之美 +- 算法之美 + +> [王争的课程对应源码](https://github.com/wangzheng0822/algo)`这里主要也是他的课程内容` +> [Github:TheAlgorithms](https://github.com/TheAlgorithms) `有各种编程语言的算法实现` +> [《编程之法》](https://github.com/julycoding/The-Art-Of-Programming-By-July) + +> [清华大学 邓俊辉 数据结构 C++实现](https://dsa.cs.tsinghua.edu.cn/~deng/ds/dsacpp/index.htm) + +> [DataStructures.pdf](http://www-bcf.usc.edu/~dkempe/teaching/DataStructures.pdf) +> [notes.pdf](http://www.cs.yale.edu/homes/aspnes/classes/223/notes.pdf) +> [notes](http://web.stanford.edu/class/archive/cs/cs103/cs103.1164/notes/) +> [data structures.pdf](https://inst.eecs.berkeley.edu/~cs61b/fa18/materials/book2/data-structures.pdf) +> [Data structure.pdf](https://www.cs.auckland.ac.nz/textbookCS220/ebook/DGW2.pdf) +> [algorithms](http://jeffe.cs.illinois.edu/teaching/algorithms/) + +> [数据结构和算法必知必会的50个代码实现](https://github.com/wangzheng0822/algo) + +> [代码随想录](https://programmercarl.com/) +> [labuladong 的算法笔记](https://labuladong.online/algo/) + +********************** + +离散数学基础: 集合、偏序集、良序、数学归纳法、级数、递归、递推 +概率基础: 随机分布、概率、伯努利实验、数学期望、期望值的线性率 + +- 常用数据结构: + - 数组、链表、栈、队列、散列表、二叉树、堆、跳表、图、Trie树 +- 常用算法 + - 递归、排序、二分查找、搜索、哈希算法、贪心算法、分治算法、回溯算法、动态规划、字符串匹配算法。 + +> [Algorithms](https://github.com/williamfiset/Algorithms) 算法库 + +******************* + +## 基础概念 +### 算法的重要特征 +1. 有穷性:保证执行有限步骤之后结束 +2. 确切性:每一步骤都有确切的定义 +3. 输入:每个算法有一个或多个输入,以刻画运算对象的初始情况。所谓零个输入是指算法本身舍弃了初始条件 +4. 输出:每个算法有一个或多个输出,显示对应输入数据加工后的结果。没有输出的算法是毫无意义的 +5. 可行性:在原则上算法能够精确地运行,进行有限次运算即可完成一次运算 + +### 时间复杂度 +> [Java中的实践](http://www.baeldung.com/java-algorithm-complexity) + +所有代码的执行时间 T(n) 与每行代码的执行次数 n 成正比。 + +- T(n) = O(f(n)) + - T(n) 表示代码执行的时间; + - n 表示数据规模的大小; + - f(n) 表示每行代码执行的次数总和。 + +因为这是一个公式,所以用 f(n) 来表示。公式中的 O,表示代码的执行时间 T(n) 与 f(n) 表达式成正比。 + +大 O 时间复杂度实际上并不具体表示代码真正的执行时间,而是表示代码执行时间随数据规模增长的变化趋势,所以,也叫作渐进时间复杂度(asymptotic time complexity) 简称时间复杂度 + +#### 时间复杂度分析 +1. 只关注循环执行次数最多的代码 +1. 加法原则: 相加时的结果是: 总复杂度等于量级最大的那段代码的复杂度 +1. 乘法原则: 嵌套代码的复杂度等于嵌套内外代码复杂度的乘积 + +- 常见的时间复杂度 + - 常量阶 O(1) + - 对数阶 O(log n) + - 线性阶 O(n) + - 线性对数阶 O(n log n) + - 平方阶 O(n^2) 立方阶O(n^3 ) k次方阶 O(n^k) `用 ^ 表示次方` + - 指数阶 O(2^n) + - 阶乘阶 O(n!) + +************************ +> O(1) + +一般情况下,只要算法中不存在循环语句、递归语句,即使有成千上万行的代码,其时间复杂度也是Ο(1) + +************************ +> O(logn)、O(nlogn) + +```c + int i=1; + while (i <= n) { + i = i * 3; + } +``` +对数之间是可以互相转换的,log3 n 就等于 `log3 2 * log2 n`,所以 `O(log3 n) = O(C * log2 n)`,其中 C=log3 2 是一个常量。 +基于我们前面的一个理论:在采用大 O 标记复杂度的时候,可以忽略系数,即 O(Cf(n)) = O(f(n)) +所以,O(log2 n) 就等于 O(log3 n)。因此,在对数阶时间复杂度的表示方法里,我们忽略对数的“底”,统一表示为 O(logn)。 + +O(nlogn) 也是一种非常常见的算法时间复杂度。比如,归并排序、快速排序的时间复杂度都是 O(nlogn)。 + +- 个人看法: 当循环中的循环变量的增长是以指数形式增长, 从而达到循环退出条件, 那么时间复杂度就是对数形式的 + +************************ +> O(m+n)、O(m*n) + +时间复杂度由两个数据的规模来决定, 原来的加法法则需要改为 T1(m) + T2(n) = O(f(m) + g(n)), 不清楚 m n 大小关系, 所以只能同时评估 + +四个复杂度分析方面的知识点,最好情况时间复杂度(best case time complexity)、最坏情况时间复杂度(worst case time complexity)、平均情况时间复杂度(average case time complexity)、均摊时间复杂度(amortized time complexity)。 + +为了表示代码在不同情况下的不同时间复杂度,我们需要引入三个概念:最好情况时间复杂度,最坏情况时间复杂度和平均情况时间复杂度 + +#### 最好最坏时间复杂度 +```C + // n 表示数组 array 的长度 + int find(int[] array, int n, int x) { + int i = 0; + int pos = -1; + for (; i < n; ++i) { + if (array[i] == x) pos = i; + } + return pos; + } +``` + +顾名思义,最好情况时间复杂度就是,在最理想的情况下,执行这段代码的时间复杂度。就像我们刚刚讲到的,在最理想的情况下,要查找的变量x正好是数组的第一个元素,这个时候对应的时间复杂度就是最好情况时间复杂度 O(1)。 +同理,最坏情况时间复杂度就是,在最糟糕的情况下,执行这段代码的时间复杂度。就像刚举的那个例子,如果数组中没有要查找的变量x,我们需要把整个数组都遍历一遍才行,所以这种最糟糕情况下对应的时间复杂度就是最坏情况时间复杂度 O(n)。 + +#### 平均时间复杂度 +要查找的变量 x 在数组中的位置,有 n+1 种情况:`在数组的 0~n-1 位置中和不在数组中`。我们把每种情况下,查找需要遍历的元素个数累加起来,然后再除以 n+1,就可以得到需要遍历的元素个数的平均值 + +> (1+2+3+...+n+n) / (n+1) = n(n+3) / 2(n+1) + +时间复杂度的大 O 标记法中,可以省略掉系数、低阶、常量,所以,咱们把刚刚这个公式简化之后,得到的平均时间复杂度就是 O(n)。 + +我们知道,要查找的变量 x,要么在数组里,要么就不在数组里。这两种情况对应的概率统计起来很麻烦,为了方便, 假设在数组中与不在数组中的概率都为 1/2。 +另外,要查找的数据出现在 0~n-1 这 n 个位置的概率也是一样的,为 1/n。 +所以,根据概率乘法法则,要查找的数据出现在 0~n-1 中任意位置的概率就是 1/(2n)。 +因此,前面的推导过程中存在的最大问题就是,没有将各种情况发生的概率考虑进去。如果我们把每种情况发生的概率也考虑进去,那平均时间复杂度的计算过程就变成了这样: + +> 1 * 1/2n + 2 * 1/2n + n * 1/2n + 1/2 * n = (3n+1)/4 + +这个值就是概率论中的`加权平均值`,也叫作`期望值`,所以平均时间复杂度的全称应该叫`加权平均时间复杂度`或者`期望时间复杂度`。 +引入概率之后,前面那段代码的加权平均值为 (3n+1)/4。用大 O 表示法来表示,去掉系数和常量,这段代码的加权平均时间复杂度仍然是 O(n)。 +你可能会说,平均时间复杂度分析好复杂啊,还要涉及概率论的知识。实际上,在大多数情况下,我们并不需要区分最好、最坏、平均情况时间复杂度三种情况。 +很多时候,我们使用一个复杂度就可以满足需求了。只有同一块代码在不同的情况下,时间复杂度有量级的差距,我们才会使用这三种复杂度表示法来区分。 + +#### 均摊时间复杂度 + +```C + // array 表示一个长度为 n 的数组 + // 代码中的 array.length 就等于 n + int[] array = new int[n]; + int count = 0; + + void insert(int val) { + if (count == array.length) { + int sum = 0; + for (int i = 0; i < array.length; ++i) { + sum = sum + array[i]; + } + array[0] = sum; + count = 1; + } + + array[count] = val; + ++count; + } +``` +这段代码实现了一个往数组中插入数据的功能。当数组满了之后,也就是代码中的 count == array.length 时,我们用 for 循环遍历数组求和,并清空数组, +将求和之后的 sum 值放到数组的第一个位置,然后再将新的数据插入。但如果数组一开始就有空闲空间,则直接将数据插入数组。 + +最理想的情况下,数组中有空闲空间,我们只需要将数据插入到数组下标为 count 的位置就可以了,所以最好情况时间复杂度为 O(1)。 +最坏的情况下,数组中没有空闲空间了,我们需要先做一次数组的遍历求和,然后再将数据插入,所以最坏情况时间复杂度为 O(n)。 + +假设数组的长度是 n,根据数据插入的位置的不同,我们可以分为 n 种情况,每种情况的时间复杂度是 O(1)。 +除此之外,还有一种“额外”的情况,就是在数组没有空闲空间时插入一个数据,这个时候的时间复杂度是 O(n)。 +而且,这 n+1 种情况发生的概率一样,都是 1/(n+1)。所以,根据加权平均的计算方法,我们求得的平均时间复杂度就是 + +> 1 * 1/(n+1) + 1 * 1/(n+1)+...+ n * 1/(n+1) = O(1) + +首先,find() 函数在极端情况下,复杂度才为 O(1)。但 insert() 在大部分情况下,时间复杂度都为 O(1)。只有个别情况下,复杂度才比较高,为 O(n)。这是 insert()第一个区别于 find() 的地方。 +我们再来看第二个不同的地方。对于 insert() 函数来说,O(1) 时间复杂度的插入和 O(n) 时间复杂度的插入,出现的频率是非常有规律的,而且有一定的前后时序关系,一般都是一个 O(n) 插入之后,紧跟着 n-1 个 O(1) 的插入操作,循环往复。 + +所以,针对这样一种特殊场景的复杂度分析,我们并不需要像之前讲平均复杂度分析方法那样,找出所有的输入情况及相应的发生概率,然后再计算加权平均值。 +针对这种特殊的场景,我们引入了一种更加简单的分析方法:摊还分析法,通过摊还分析得到的时间复杂度我们起了一个名字,叫均摊时间复杂度。 + +对一个数据结构进行一组连续操作中,大部分情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间存在前后连贯的时序关系,这个时候,我们就可以将这一组操作放在一块儿分析,看是否能将较高时间复杂度那次操作的耗时,平摊到其他那些时间复杂度比较低的操作上。而且,在能够应用均摊时间复杂度分析的场合,一般均摊时间复杂度就等于最好情况时间复杂度。 + +均摊时间复杂度就是一种特殊的平均时间复杂度,我们没必要花太多精力去区分它们。你最应该掌握的是它的分析方法,摊还分析. + +```C + // 全局变量,大小为 10 的数组 array,长度 len,下标 i。 + int array[] = new int[10]; + int len = 10; + int i = 0; + // 往数组中添加一个元素 + void add(int element) { + if (i >= len) { // 数组空间不够了 + // 重新申请一个 2 倍大小的数组空间 + int new_array[] = new int[len*2]; + // 把原来 array 数组中的数据依次 copy 到 new_array + for (int j = 0; j < len; ++j) { + new_array[j] = array[j]; + } + // new_array 复制给 array,array 现在大小就是 2 倍 len 了 + array = new_array; + len = 2 * len; + } + // 将 element 放到下标为 i 的位置,下标 i 加一 + array[i] = element; + ++i; + } +``` +> 最好是O(1),最差是O(n), 均摊是O(1) + +************************ + +### 空间复杂度 +类比一下,空间复杂度全称就是渐进空间复杂度(asymptotic space complexity),表示算法的存储空间与数据规模之间的增长关系。 + +## 基础数据结构 +### 线性表 +> 数组, 链表, 队列, 栈 + +#### 数组 +> 一组连续的内存空间,存放一组相同数据类型的数据, 由于CPU访问存储器的[局部性原理](https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array/11227902#11227902), 所以数组比链表高效 + +访问: 根据下标任意访问的时间复杂度为O(1), +插入: 从最好O(1) 最坏O(n) 平均O(n) +删除: 从最好O(1) 最坏O(n) 平均O(n) + +多次删除集中在一起,提高删除效率 +记录下已经被删除的数据,每次的删除操作并不是搬移数据,只是记录数据已经被删除,当数组没有更多的存储空间时,再触发一次真正的删除操作。即JVM标记清除垃圾回收算法。 +```c + int main(int argc, char* argv[]){ + int i = 0; + int arr[3] = {0}; + for(; i<=3; i++){ + arr[i] = 0; + printf("hello world\n"); + } + return 0; + } +``` +> 以上代码会死循环执行, 栈设计采用小端模式的系统才会出现 + +例子中死循环的问题跟编译器分配内存和字节对齐有关 数组3个元素 加上一个变量a 。4个整数刚好能满足8字节对齐 所以i的地址恰好跟着a2后面 导致死循环 +如果数组长度为4 且循环5次 则这里不会出现死循环。因为编译器64位操作系统下 默认会进行8字节对齐 变量i的地址就不紧跟着数组后面了。 + +> 存疑1: 当长度为4 无法解释. 压栈顺序: i,a[2],a[1],a[0], 所以 a[3]访问到的是i +> 存疑2: gcc有一个编译选项(-fno-stack-protector)用于关闭堆栈保护功能。默认情况下启动了堆栈保护,不管i声明在前还是在后,i都会在数组之后压栈,只会循环4次;如果关闭堆栈保护功能,则会出现死循环. + +[参考 GCC编译器堆栈保护技术](https://www.ibm.com/developerworks/cn/linux/l-cn-gccstack/index.html) +[GCC 堆栈保护技术](https://firmianay.gitbooks.io/ctf-all-in-one/content/doc/4.4_gcc_sec.html) + +**`容器和数组`** +> 众多语言都有对数组封装好的容器类 + +Java 中 ArrayList 和 数组的对比 + +优势 +1. 对数组操作的细节进行封装, +1. 支持动态扩容, 空间不够会扩容为原大小的1.5倍 +劣势 +1. ArrayList 无法存放基础数据类型, 只能用包装类型, 这样就涉及到了 自动拆装箱,影响性能 +1. 表示二维结构数据时, 没有数组直观 + +> 最好在创建 ArrayList的时候设置好初始大小,避免不必要的数据搬运 +对于业务开发,直接使用容器就足够了,省时省力。毕竟损耗一丢丢性能,完全不会影响到系统整体的性能。但如果你是做一些非常底层的开发,比如开发网络框架,性能的优化需要做到极致,这个时候数组就会优于容器,成为首选。 + +#### 链表 +> 单链表 双向链表 循环链表 + +> [Github: C语言代码实现](https://github.com/Kuangcp/LearnC/tree/master/algorithm/linked_lists)`个人` + +#### 数组和链表对比 + +| 操作\时间复杂度 | 数组 | 链表 | +|:----: |:----:|:----:| +| 插入/删除 | O(n) | O(1) | +| 随机访问 | O(1) | O(n) | + +***************** + +分治算法 +动态规划 最值 极值 +不直接找问题, 而是根据你的输入, 和答案之前关系的规律 + +柯里化, continuation 高阶函数, 尾递归 + +### 树 + +> Radix 树 这是一种基于二进制表示的键值的查找树,尤其适合处理非常长的、可变长度的键值 + +********************* +## 匹配算法 +- [字符串相似度匹配](http://zjwyhll.blog.163.com/blog/static/75149781201281142630851/) + +字符串相似度计算: +- Levenshtein算法 编辑距离 +- 三角比较算法 + +> [Diff Match Patch](https://github.com/google/diff-match-patch) + +## 排序算法 +> [参考: 九种排序算法的可视化及比较](https://zhuanlan.zhihu.com/p/34421623?group_id=955945213303250944) + +## 搜索算法 +> [Trie](https://en.wikipedia.org/wiki/Trie) `字典树` + +## 哈希算法 Hash +> 也称 散列法 关键字地址计算法 + +基本思想: 在元素的关键字 k 和元素的存储位置 p 之间建立一个对应关系 H, 使得 p = H(k),则 H 被称为哈希函数 +- 在创建哈希表时, 把关键字为 k 的元素放到地址为 H(k) 的单元 +- 在查找时则根据关键字 k 计算出地址, 直接获取到元素, 时间复杂度达到 O(1) + +- MD5 +- SHA + - SHA家族算法有SHA-1、SHA-224、SHA-256、SHA-384和SHA-512(后四者通常并称SHA2) +- CRC + - 循环冗余校验, CRC32(12、16、32等值均是指多项式的最高阶N次幂) +- MurmurHash + - 是一种非加密型哈希函数,和其它流行哈希函数相比,对于规律性较强的 key 随机分布特性表现更良好,很多开源的软件项目使用(Redis,Memcached,Cassandra,HBase,Lucene) +- Bcrypt `慢Hash函数` +- [xxHash](https://github.com/Cyan4973/xxHash) + +> [smhasher](https://github.com/rurban/smhasher) Hash函数性能测试对比 + +### Hash函数 构造 +设计哈希函数常考虑 +1. 哈希函数的时间复杂度 +1. 关键字长度 +1. 哈希表大小 +1. 关键字分布的情况 +1. 记录查找的频率 + +常见设计思路: + +`数字分析法` +事先知道关键字集合,并且每个关键字的位数比哈希表的地址码位数多时, 可以从关键字中选出分布较均匀的若干位, 构成哈希地址 + +`平方取中法` +当无法确定关键字中哪几位分布较均匀时, 可以先求出关键字的平方值, 然后依据需要取平方值的中间几位作为哈希地址 +因为平方运算后中间几位和关键字中每一位都相关,所以散列程度比较高 + +`分段叠加法` +按哈希表地址位数, 将关键字分成位数相等的几部分, 后面多余的部分可以截断, 然后将这几部分移位相加或者折叠相加, 求得哈希值 + +`除留余数法` +哈希表长为m, p为小于等于m的最大素数, 哈希函数则是 `H(k)=k%p` 该方法容易产生哈希冲突 + +`伪随机数法` +直接使用随机数函数 `H(k)=random(k)` + +`JDK中的HashMap实现方式` +取键值的hashCode, 然后高低16位做异或运算, 然后再与`哈希表大小`做与运算, 才得到哈希地址 + +### HASH 冲突 +> [参考: hash是如何处理冲突的?](http://www.cnblogs.com/jillzhang/archive/2006/11/03/548671.html) + +> [参考: 一种高级的DoS攻击-Hash碰撞攻击 ](https://yq.aliyun.com/articles/92194?utm_campaign=wenzhang&utm_medium=article&utm_source=QQ-qun&201762&utm_content=m_22308) +> [Application vulnerability due to Non Random Hash Functions](https://stackoverflow.com/questions/8669946/application-vulnerability-due-to-non-random-hash-functions) + +#### 开放定址法 +- 开放定址法有一个公式: `Hi=(H(key)+di) % m; i=1,2,...,k(k<=m-1)` + - 其中 m 为哈希表的表长 di 是产生冲突的时候的`增量序列` + +- 线性探查法 + - 如果di值可能为1,2,3,...m-1, 顺序查看表单元, 直到找到空单元。 +- 平方探测法 + - 如果di取1,则每次冲突之后,向后移动1个位置. 如果di取值可能为 `1,-1^2,2^2,-2^2,...k*k,-k*k (k<=m/2)` +- 伪随机探测 + - di取值是伪随机数列 + +> 优点 + +> 缺点 + +1. 当冲突多的时候数据容易堆集在一起,这时候对查找不友好; +1. 删除结点的时候不能简单将结点的空间置空,否则将截断在它填入散列表之后的同义词结点查找路径。因此如果要删除结点,只能在被删结点上添加删除标记,而不能真正删除结点; +1. 如果哈希表的空间已经满了,还需要建立一个溢出表,来存入多出来的元素。 + +#### 再 HASH 法 +基本思想是构造多个不同的哈希函数, 当发生冲突时,使用第二个、第三个哈希函数计算地址,直到无冲突。 + +> 缺点 + +增加了时间复杂度 + +#### 链地址/拉链 法 +将所有哈希地址为 i 的元素构成一个`同义词链`的单链表, 因此适用于频繁插入删除的情况 + +JDK中的HashMap(JDK8链表部分引入红黑树),Golang的map 均采用该方式 + +> 优点 +1. 处理冲突的方式简单,且无堆集现象,非同义词绝不会发生冲突,因此平均查找长度较短; +1. 由于拉链法中各链表上的结点空间是动态申请的,所以它更适合造表前无法确定表长的情况; +1. 删除结点操作易于实现,只要简单地删除链表上的相应的结点即可。 + +> 缺点 +1. 需要额外的存储空间 + +#### 公共溢出区 +- 假设哈希函数的值域为`[0,m-1]`, 则设向量`HashTable[0..m-1]`为基本表,另外设立存储空间向量 `OverTable[0..v]` 用以存储发生冲突的记录。 + +凡是和基本表发生冲突的元素一律填入溢出表 + +> 缺点 +1. 查找冲突数据的时候,需要遍历溢出表才能得到数据 + +************************ + +### 安全相关 +1. HASH攻击,例如针对Java中HashMap的算法,通过构造特定的参数,使得HashMap退化为链表,浪费服务器资源 +1. 不同的参数进入Hash函数运行时间会不一致,理论上利用这一点可以提高爆破密码的准确性,所以JDK中实现尽量保证了hash时间均匀 + +************************ + +# 密码学 + +## Diffie-Hellman Key Exchange算法 +> Whitfield Diffie 和 Martin Hellman ,他们于2015年获得了计算机科学领域的最高奖:图灵奖 + +![码农翻身](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/arithmetic/Diffie-HellmanKeyExchange.png) + +`最后神奇的魔法发生了, 我们两个得到了同样的值 s = 10!` +- 这个s 的值只有我们两个才知道, 其实就是密钥了, 可以用来做加密解密了( 当然,这只是一个例子,实际的密钥不会这么短), 我们俩的通讯从此就安全了。 + - “数学家小帅哥说了, 原因很简单,(gx mod p)y mod p 和 (gy mod p)x mod p 是相等的! ” + - “那黑客不能从公开传输的 p = 17, g = 3, a = 6 , b = 12 推算出s = 10 吗?” 我问道。 + - “当然不能, 不过前提是需要使用非常大的p , x, y, 这样以来,即使黑客动用地球上所有的计算资源, 也推算不出来。 ” + +# 实际问题 +例如存储一个部门关系, 上下级, 以及同级要有序, 并且, 这个关系树是能随意调整结构的, 每个节点和节点之间任意断开和连接 + +name/id, parent, index + +## 斐波那契数列 + +> [神奇的斐波那契数列](https://open.163.com/movie/2014/2/N/O/M9HKRT25D_M9HNA0UNO.html) | [斐波那契数列为什么那么重要](https://www.zhihu.com/question/28062458) + +斐波那契数列: 递归, 离散数学, 斐波那契博弈 +数据结构: 斐波那契堆, +算法: 分治, 动态规划, 并行算法, 矩阵积, fft +操作系统 线程, 线程级并行, openmp + +二分搜索: 斐波那契数列实际上体现的是自然界某些情形下更本质的平权意识,尤其是当表面上的「对半法」实际上并不能做到平权时。自然情况下,对半分不一定就是平权,而黄金分割才是更本质意义下的平权 +`mid = (hi + low) /2` => `mid = low + fib.get() - 1;` diff --git a/Algorithm/Cryptography.md b/Algorithm/Cryptography.md new file mode 100644 index 0000000..d75c274 --- /dev/null +++ b/Algorithm/Cryptography.md @@ -0,0 +1,383 @@ +--- +title: 密码学 +date: 2023-09-08 11:54:36 +tags: +categories: +--- + +💠 + +- 1. [密码学](#密码学) +- 2. [古典密码学](#古典密码学) + - 2.1. [凯撒密码](#凯撒密码) + - 2.2. [简单替换密码](#简单替换密码) + - 2.3. [多表加密](#多表加密) + - 2.4. [一次性密码本](#一次性密码本) +- 3. [现代密码学](#现代密码学) + - 3.1. [编码](#编码) + - 3.2. [伪随机数生成器](#伪随机数生成器) + - 3.3. [对称密码](#对称密码) + - 3.3.1. [DES](#des) + - 3.3.2. [AES](#aes) + - 3.3.2.1. [混合模式](#混合模式) + - 3.3.2.1.1. [AES-CCM](#aes-ccm) + - 3.3.2.1.2. [AES-GCM](#aes-gcm) + - 3.3.3. [分组密码和流密码](#分组密码和流密码) + - 3.3.4. [分组密码的模式](#分组密码的模式) + - 3.4. [公钥密码](#公钥密码) + - 3.4.1. [RSA](#rsa) + - 3.4.1.1. [生成密钥对](#生成密钥对) + - 3.4.1.2. [模拟完整的RSA过程](#模拟完整的rsa过程) + - 3.4.1.3. [对RSA的攻击](#对rsa的攻击) + - 3.5. [混合密码系统](#混合密码系统) + - 3.6. [散列函数](#散列函数) + - 3.6.1. [MD4 MD5](#md4-md5) + - 3.6.2. [SHA-1](#sha-1) + - 3.6.3. [RIPEMD-160](#ripemd-160) + - 3.7. [消息认证码](#消息认证码) + - 3.8. [数字签名](#数字签名) + - 3.8.1. [签名与验签](#签名与验签) + - 3.9. [证书](#证书) + - 3.10. [密钥](#密钥) + - 3.11. [随机数](#随机数) +- 4. [集大成者](#集大成者) + - 4.1. [PGP](#pgp) + - 4.2. [SSL TLS](#ssl-tls) +- 5. [扩展](#扩展) + +💠 2023-10-10 00:42 +**************************************** +# 密码学 +> [wikipedia](https://zh.wikipedia.org/wiki/%E5%AF%86%E7%A0%81%E5%AD%A6) + + +> 基础概念: +- 此处的密码不等同于常见的信息系统中的用户 **"登录密码"**,严格意义上讲`password`只能称为口令,作为信息系统认证的一部分,密码是指信息的加解密等完整生态。 +- 明文(plaintext) 加密(encrypt) 得到 密文(ciphertext),解密则是逆向过程 + - 加密 解密算法合称 密码算法,密码算法中通常都需要密钥(key) +- 对称密码 和 公钥密码:保证数据的机密性 + - 对称密码即加密和解密使用同一个密钥,公钥密码则是加密和解密使用不同的密钥(分为公钥和私钥)。还有混合密码系统则是结合两种方式一起使用 +- hash函数: 为了保证数据的完整性,即数据没有被篡改过。 +- 消息认证码: 保证完整性并提供认证机制,例如身份证中的校验位 +- 数字签名:保证完整性,提供认证机制,防止否认 +- 伪随机数生成器: 模拟产生随机数的算法。如果生成随机数的算法设计不好,则攻击者容易推测出密钥 +- 隐写术:将消息藏入明文,密码隐藏的是明文,隐写术隐藏的明文消息 + - 计算机中的数字水印就使用了该技术设计,例如将需要嵌入的消息加密得到密文再通过隐写术藏到载体上(图片,视频,音频),达到签名,溯源的目的 +- 社会工程学(social engineering)攻击: 安全系统内的短板在于人类自身。 +- 密钥加密密钥(Key Encrypting Key, KEK) + +最佳实践: +- 不要使用保密的密码算法,而应该使用市面上公开的高强度的密码算法 +- 使用低强度的密码比不进行任何加密更危险 +- 任何密码总有一天都会被破解,重点在于攻击的投入产出比 +- 密码只是信息安全的一部分 +- 低强度加密算法对明文的多轮加密的安全性等同于单次加密 + +参考: +- [可汗学院: 计算机科学-密码学](https://zh.khanacademy.org/computing/computer-science/cryptography) +- 《图解密码技术》 + + +************************ + +# 古典密码学 +> [wikipedia](https://zh.wikipedia.org/wiki/%E5%8F%A4%E5%85%B8%E5%AF%86%E7%A2%BC) + +> [异或与一次性密码本](https://zh.khanacademy.org/computing/computer-science/cryptography/ciphers/a/xor-and-the-one-time-pad)`异或运算后,原始信息无法辨别了` + +## 凯撒密码 +凯撒密码是一种移位密码,将明文按固定偏移量将明文转换为密文。 + +例如:明文 you 经过3个移位得到密文brx。 +缺陷: +- 英文语句中每个字母的出现频率是有固定的差异,可以从密文的字母出现频率对照原始字母的频率从而推算出偏移量 +- 密钥只有26种,密钥空间极小,可以暴力计算(又称 穷举搜索)所有的密钥解密得到明文 + +## 简单替换密码 +将原始的26个字母,打乱后得到替换表,将明文中的字母一一对应并替换,从而将明文加密为密文 + +改进点: 密钥空间从26种扩大到 26!种 +缺陷: +- 同样可以通过字母频率分析,结合单词组(类似数独的解法),推算出替换表 +- 密文越长越容易破解(给出了更多的信息) + +## 多表加密 +将一个单词设置为密钥,单词内的每个字符的自然序循环使用作为偏移量对明文进行加密 + +例如:明文 how are you 密钥 yes(25,5,19) 计算得到密文:__ +缺陷:密钥一般短于明文,还是能得到偏移的规律 + +## 一次性密码本 +> 一次性密码本(one time pad)是一种绝对无法被破译的密码 +> 一次性密码本由 G.S.Vernam 在1917年提出 并由 香农(C.E.Shannon) 在1949年数学方法证明,它是无条件安全的(unconditionally secure),在理论上是无法破译的(theoretically unbreakable)。 + +设置一个与明文长度一致的字符串作为密码本,对明文每个字母按密码本对应的字母的自然序进行移位。 + +如果在计算机的范畴,一次性密码本则是与明文编码后二进制串等长的二进制串。而且明文二进制串通过和密码本异或运算得到密文(且和或会保留大量的明文信息特征)。 + +例如: 明文 how are you 密钥 uwoiufpqx 计算得到密文:__ + +一次性密码本是无法破译的: 因为即使拿到了密文,穷举了密钥,得到明文,但是会发现得到多个情况的明文,无法判断哪个明文才是这个密文真正要携带信息的明文。 + +即使理论上是无法破译的,但是在实用性上几乎是无法使用: 密钥每次都是需要和密文一起发送给接受方,密文中的信息才能有效被传达,然后矛盾点来了,如何安全的传输与明文等长的密钥。 + +一次性密码本和明文长度一样,是否可以压缩。答案:否,因为压缩过程是对重复序列的再编码,得到短序列,从而实现压缩,但是一次性密码本是随机的,不包含任何冗余重复序列。 + +************************ + +# 现代密码学 + +## 编码 +现代密码学都是建立在计算机的基础上,因此需要编码(将 文字,图像,音视频 等 转换成比特序列)。 + + +************************ + +## 伪随机数生成器 +计算机领域没有真随机事件,所以只能称为伪随机数,但是可用利用外部的信息熵来提高随机性。 + +需要具备的性质: +- 事实上不可能根据过去的随机数序列来预测未来的随机数序列。 + +常见的编程语言中random的最简单实现都是固定序列的伪随机数生成器(如果使用此生成器计算的密钥做加密,容易被攻击者攻破) +改进版则是使用操作系统中随机数设备`/dev/urandom`(采集硬件上的信息熵,电压,温度,键盘鼠标等输入事件)来生成不固定序列 + +伪随机数生成算法 + +线性同余 (linear congruential generator, LCG): `X[n+1] = (a·X[n] + c) mod m` + +************************ + +## 对称密码 + +### DES +Data Encryption Standard 是 1977年美国联邦信息处理标准中所采用的对称密码。 + +DES是一种将64比特的明文加密成64位比特的密文的对称加密算法(分组密码的一种),密钥长度为64位: 56位+错误检验的8比特(每隔7位设置一位) +使用Feistel网络作为基本结构,经过多轮分组加密得到密文(轮函数和网络结构解耦)。详细过程参考书籍《图解密码技术》 + +`三重DES` +加密过程:明文 密钥1加密 密钥2解密 密钥3加密 得到密文。 解密过程相反: 密钥3解密 密钥2加密 密钥1解密 +优点:增加了DES算法的强度(加大了密钥空间), 缺陷: 计算量大 + +> DES 密钥空间是 2^56种, 三重DES则是 2^168种。 + +************************ + +### AES +Advanced Encryption Standard。在全世界范围进行公开竞选, 有15个算法进入候选范围,最终 Rijndael 获胜成为AES + +同为分组加密算法,分组长度为128比特,密钥长度可选(128,192,256位),使用SPN结构进行多轮加密。 详细过程参考书籍《图解密码技术》 +而且加密过程的步骤可以并行计算,性能较DES也更好。 +Rijndael算法背后是严谨的数学论证:明文到密文的计算过程全部可以用数学公式来表达。 + +例如128位密钥长度的生成方式:Random生成16位字节数组,或者任意Ascii码16位字符串(强度更弱) + +#### 混合模式 +##### AES-CCM +- AES算法采用CTR/CBC模式 + +##### AES-GCM +- AES算法采用CTR模式,并带有GMAC消息认证码。能防篡改(认证码实现) [wiki: GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) + +************************ + +### 分组密码和流密码 +分组密码(block cipher)是指每次只能处理固定长度数据的一类密码算法。例如 DES,AES等 +流密码(stream cipher)是对数据流连续处理的一类密码算法。 + +分组密码处理完一个分组就结束了,因此不需要内部状态来记录维护加密的进度,反之流密码是对一串数据流进行连续处理,是有状态的。 + +> 由于明文不可能总是密钥的整数倍长,因此需要对最后一个明文分组进行数据填充(padding)达到对齐 + +### 分组密码的模式 +> 分组密码算法需要对分段的明文进行迭代加密,迭代的方法称为模式。 + +《实用密码学》中指出,推荐使用CBC模式和CTR模式。 + +- ECB:Electronic CodeBook mode 电子密码本模式 +- CBC:Cipher Block Chaining mode 密码分组链接模式 +- CFB:Cipher FeekBack mode 密文反馈模式 +- OFB:Output FeedBack mode 输出反馈模式 +- CTR:CounTeR mode 计数器模式 + +ECB模式是按序迭代,也是最容易有安全漏洞的算法,通常不被实际使用。 +- 因为ECB模式下,明文分组和密文分组是按序一一对应的,那理论上可以通过对密文分组的 复制,调序,剪切 等操作,从而达到篡改明文的目的。 +- 但是如果使用消息认证码,则可以发现密文被篡改 + +IV: 初始化向量(initialization vector)。 *iv长度为固定的16字节* + +CBC模式在每个明文分组加密为密文分组前,都需要拿前一个明文分组得到的密文分组对当前明文分组做异或(XOR)运算,第一个分组则依赖IV做XOR。每个分组都依赖前一个分组像一个链条。 +CFB,OFB和CBC类似都需要IV,只是当前明文分组的处理方式不同。 + +************************ + +## 公钥密码 +> 非对称加密 + +类比场景: +- 对称加密,传统房门的锁需要同钥匙来开关门,则密钥是相同的:钥匙; +- 公钥加密:超市中的付费购物车,需要投币开锁,推入前一个购物车后完成加锁,则密钥是不同的,加锁密钥:前一个购物车,解锁密钥:硬币。 + +相较于对称密码,安全性大为加强,但是目前的公钥密码算法性能只有对称密码算法的几百分之一。 + +EIGamal算法利用了mod N 下求离散对数的难度(但是密文长度是明文的两倍),RSA利用了质因数分解的难度,Rabin算法利用了mod N下求平方根的难度 + +椭圆曲线密码 (Elliptic Curve Cryptosystems, ECC), 通过将椭圆曲线上特定点进行特殊的乘法运算来实现,利用了乘法运算的逆运算的困难度。 +特点是所需密钥长度比RSA短。 + +### RSA +> [模运算](/Skills/CS/BasicOperator.md#模运算)`RSA公私钥理论基础` + +RSA 可以被用于公钥密码和数字签名。 +在RSA中,明文,密钥和密文都是数字。 + +加密: `密文 = 明文^E mod N` 元组(E,N) 是公钥 +解密: `明文 = 密文^D mod N` 元组(D,N) 是密钥 + +#### 生成密钥对 + +1. 求 N + - 首先准备两个大质数 p 和 q : `N = p * q` **p和q如果太小密码容易被破解,太大的话加解密时间会很长** +2. 求 L (中间值) + - `L = lcm(p-1, q-1)` **lcm 最小公倍数** +3. 求 E + - `1 < E < L` 且 `gcd(E,L)=1` **E和L互质** +4. 求 D + - `1 < D < L` 且 `E * D mod L = 1` + +> 判断质数的方法: 费马测试和米勒·拉宾测试等。 +> 计算gcd最快速的方法则是欧几里得算法(辗转相除法)。 + +#### 模拟完整的RSA过程 + +模拟密钥生成过程以及加解密过程: +1. 求 N, 准备质数 p = 17 q = 19, N = 323 **注意:实际使用中pq的长度在512位以上,N的长度在1024位以上** +2. 求 L, lcm(16,18) = 144 +3. 求 E, gcd(E,L)=1 有很多数满足条件,有和L互质的合数,也有质数。假设选择5作为E +4. 求 D, 5 * D mod 144 = 1 计算出D=29时可满足 + +此时得到公钥 (E,N) = (5,323) 私钥 (D,N)=(28,323) + +> 注意明文必须是小于N的数,如果大于N就会有多个解,造成解密数据不等于明文的情况 + +- 加密:明文^E mod N = 123^5 mod 323 = 225 +- 解密:明文^D mod N = 225^29 mod 323 = 123 + +#### 对RSA的攻击 + +攻击者拥有的信息:密文, E, N,不知道的信息:明文, D, p, q, L + +1. 密文求解明文: 密文=明文^E mod N , 对明文的求解是求离散对数,成本很大 +2. 暴力求解D,由于pq及N位数很长,E和D长度和N差不多,暴力迭代求解D的成本很大 +3. 对N进行质因数分解求解pq,从而求解D。目前未发明对大整数的质因数分解高效算法,成本很大 +4. 中间人攻击 mitm attack: + - 不破解RSA加密算法本身,而是在通信过程中通过对两端的公钥替换,从而窃听两端的通信 + - A <=> B 变成了 A <=> M <=> B + - 为了防御该攻击需要使用公钥的证书来让对端确保接受到的公钥是自己端的。 + +************************ + +## 混合密码系统 +> PGP, SSL/TLS都使用了混合密码系统。 + +1. 用对称密码加密消息 +2. 通过伪随机数生成器生成对称密码中使用的会话密钥 +3. 用公钥密码加密会话密钥 +4. 从混合密码系统外部赋予公钥(CA) + +密钥强度:通常公钥密码的强度应该要高于对称密码,如果对称密码被破译只会泄漏本次通信内容,如果公钥密码破译,所有使用该供公钥加密的通信内容都会被破译 + +************************ + +## 散列函数 +散列函数也称消息摘要函数,哈希函数, 信息指纹,常用于保障信息的完整性,但是无法保障信息的真实性。 + +> 需要注意 散列函数不是加密算法,因为散列值无法解密为原文。 + +由于此类函数的特点是 输入消息 输出散列值,具有单向性特点,也称单向散列函数。 +输入的消息为二进制序列,长度可以任意长,计算输出的散列值是定长,例如SHA-1输出散列值为20byte。 + +单向散列函数的性质: +- 依据任意长度的消息计算出定长的散列值 +- 能快速计算出散列值 +- 消息不同散列值也完全不同 + - 不同的消息得到了相同的散列值被称为碰撞 collision,理论上无法避免(因为散列函数的输入端信息量大于输出端),但是散列函数要保障很难被人为构造出碰撞,即强抗碰撞性 +- 函数必须具备单向性。即无法通过散列值反推出原始消息的性质。 + +> 实际应用 +- 监测下载的软件是否被篡改 +- 基于口令的加密(Password Based Encryption)。PBE的原理是将口令(Password)和盐(salt 通过伪随机数生成器生成的随机值)混合后计算其散列值,将这个散列值作为加密的密钥。 + - 加盐是为了防止针对口令的散列字典(又称彩虹表)攻击 +- 消息认证码: 将发送方和接受方之间的共享密钥和消息(明文的分片)混合计算后的散列值,可以检测并防止通信过程中的错误,篡改,伪装。在SSL/TLS中也有应用 +- 数字签名: 是现实社会中签名和盖章这样的行为在数字世界的实现。通常不会对完整消息加数字签名,而是先计算出内容的散列值,再对散列值加数字签名 +- 一次性口令: one-time password. 通常用于服务端对客户端的合法性认证。 + +### MD4 MD5 +MD4 是由Rivest于1990年设计,能产生128bit的散列值,之后Dobbertin提出寻找MD4散列碰撞的方法,已经不安全了。 +MD5 是由Rivest于1991年设计,能产生128bit的散列值, 但是MD5的强抗碰撞性已经被攻破,也已经不安全了。 + +### SHA-1 +SHA-1 SHA-256 SHA-384 SHA-512 + +SHA-1 是由 NIST(National Institute of Standards and Technology)设计,能产生160bit的散列值, +1993年发布的是SHA,1995年发布的SHA-1,SHA-1处理的消息长度存在上限,但该理论值接近于264bit,实际使用时不容易遇到问题。 + + +### RIPEMD-160 + + +### BCrypt +- [Wikipedia](https://en.wikipedia.org/wiki/Bcrypt) + +************************ + +## 消息认证码 + +用于判断消息的完整性和真实性 + +************************ + +## 数字签名 +散利函数和公钥密码组合而成 + +### 签名与验签 +> 常见于外部接口的签名验证,安全防护 + +[企点: 消息加解密指引](https://api.qidian.qq.com/wiki/doc/open/epko939s7aq8br19gz0i)`对接企点API对消息的加解密` + +## 证书 +公钥和数字签名组合而成 + +1. 基本概念: + 1. `CA (Certificate Authority)` 证书授权中心,是数字证书发放和管理的机构 + 1. `根证书` 根证书是CA认证中心给自己颁发的证书,是信任链的起始点。安装根证书意味着对这个CA认证中心的信任。 + 1. `数字证书` 数字证书颁发过程一般为: + 1. 用户首先产生自己的密钥对,并将公共密钥及部分个人身份信息传送给认证中心。 + 1. 认证中心在核实身份后,将执行一些必要的步骤,以确信请求确实由用户发送而来。 + 1. 认证中心将发给用户一个数字证书,该证书内包含用户的个人信息和他的公钥信息,同时还附有认证中心的签名信息。 + +- [Githhub:mkcert](https://github.com/FiloSottile/mkcert)`签发证书工具` + +## 密钥 + +## 随机数 + +************************ + +# 集大成者 +## PGP + +## SSL TLS +> [SSL/TLS协议运行机制的概述](http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html) +> [SSL,TLS,HTTPS](https://www.cnblogs.com/songhan/archive/2012/08/01/2617970.html) + + +************************ + +# 扩展 + +电子投票 +电子货币 +盲签名 blind signature +零知识证明 zero knowledge proof \ No newline at end of file diff --git a/Algorithm/DS/Graph.md b/Algorithm/DS/Graph.md new file mode 100644 index 0000000..cecd706 --- /dev/null +++ b/Algorithm/DS/Graph.md @@ -0,0 +1,7 @@ +# Graph + + +**「树」和「图」的根本区别:树不会包含环,图可以包含环** 。 + + +## 最小生成树 diff --git a/Algorithm/DS/LinearList.md b/Algorithm/DS/LinearList.md new file mode 100644 index 0000000..c315bdd --- /dev/null +++ b/Algorithm/DS/LinearList.md @@ -0,0 +1,39 @@ +--- +title: LinearList +date: 2023-10-03 22:50:59 +tags: +categories: +--- + +💠 + +- 1. [线性表](#线性表) + - 1.1. [顺序表](#顺序表) + - 1.2. [链表](#链表) + - 1.2.1. [跳表](#跳表) + +💠 2024-03-30 11:43:28 +**************************************** +# 线性表 + +## 顺序表 +数组 内存连续 + +## 链表 + +### 跳表 +> [参考: 跳表SkipList ](http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html) +> [Skip lists: a probabilistic alternative to balanced trees](https://www.epaperpress.com/sortsearch/download/skiplist.pdf) + +有序链表变化而来, 在部分节点上存储跨越了多个节点距离的节点指针(索引) + +> 例如: 同节点不同的跨度,可以理解为不同的索引层 +![](img/skiplist.drawio.svg) + +优势: +- 结构简单,操作成本低, Redis中Zset有使用到 + +劣势: +- 更新时,节点会随机成为多层索引节点, 指针修改成本高, 索引层是一个不稳定的数据结构 +- 不适合做磁盘层索引,对IO不友好,存储太零散没法批量读,所以MySQL索引会选用B+ + diff --git a/Algorithm/DS/Readme.md b/Algorithm/DS/Readme.md new file mode 100644 index 0000000..6d0a2c2 --- /dev/null +++ b/Algorithm/DS/Readme.md @@ -0,0 +1,4 @@ +数据结构 + +# TODO +跳表 diff --git a/Algorithm/DS/Tree.md b/Algorithm/DS/Tree.md new file mode 100644 index 0000000..9244b74 --- /dev/null +++ b/Algorithm/DS/Tree.md @@ -0,0 +1,85 @@ +--- +title: Tree +date: 2023-10-03 20:23:41 +tags: +categories: +--- + +💠 + +- 1. [树](#树) + - 1.1. [二叉树](#二叉树) + - 1.2. [二叉搜索树](#二叉搜索树) + - 1.3. [AVL树](#avl树) + - 1.4. [红黑树](#红黑树) + - 1.5. [多叉树](#多叉树) + - 1.6. [BTree](#btree) + - 1.7. [B+Tree](#b+tree) + +💠 2024-01-24 18:24:52 +**************************************** +--- + +# 树 + +- [一文搞懂二叉搜索树、B树、B+树、AVL树、红黑树](https://zhuanlan.zhihu.com/p/258078863) + +树是一种特殊的无环连通图 + + +## 二叉树 + +## 二叉搜索树 + +AVL,红黑树都是基于二叉搜索树做了不同的限制而来 + +特点 + +- 任意节点左子树不为空,则左子树的值均小于根节点的值. +- 任意节点右子树不为空,则右子树的值均大于于根节点的值. +- 任意节点的左右子树也分别是二叉查找树. +- 没有键值相等的节点. + +在查找数据时, 最优情况是 O(logn) 最坏是O(n) 即树退化成了链表 + +## AVL树 +> 平衡二叉搜索树 + +最早提出的平衡树, 应用不甚广泛, Windows对进程地址空间的管理有使用到 AVL 树 + +特点: +- 二叉搜索树的前提增加平衡性条件约束:任意节点的左子树和右子树的高度差小于2 + +## 红黑树 + +> 对称二叉 B 树 + +平衡二叉树, 广泛使用在各种语言的基本容器中, Java 的 TreeSet TreeMap HashMap, C++ 的 map set + +红黑树具有以下5种性质: + +1. 节点是红色或黑色。 +2. 根节点是黑色。 +3. 每个叶节点(NIL节点,空节点)是黑色的。 +4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点) +5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。 + +红黑树的时间复杂度为O(log n),与树的高度成正比。 +红黑树每次的插入、删除操作都需要做平衡,平衡时有可能会改变根节点的位置,颜色转换,左旋,右旋等。 + +## 多叉树 + +每个节点下可以有多个子节点, 实际上二叉树是多叉树的特例(子节点只有两个) + +- 多叉树转二叉树: 左节点是孩子节点 右节点是兄弟节点 + +## BTree +- [B Tree](https://en.wikipedia.org/wiki/B-tree) + + + +用在磁盘文件索引和数据库索引等 + +## B+Tree +> [B+ tree](https://en.wikipedia.org/wiki/B%2B_tree) + diff --git a/Algorithm/DS/img/b_tree_index.excalidraw.svg b/Algorithm/DS/img/b_tree_index.excalidraw.svg new file mode 100644 index 0000000..e9dec8c --- /dev/null +++ b/Algorithm/DS/img/b_tree_index.excalidraw.svg @@ -0,0 +1,21 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nOy9247bTNcmdj5X8eGbQ/0tkVXF2vxBXHUwMDEwcCOKXHUwMDEyqVxytZcyXHUwMDAzg3ttSZFcIkVJgzlcdTAwMTkgSFx1MDAxMCRAXHUwMDEyJJijXHUwMDAwuYFcdTAwMTlcdTAwMDTIUS5o/iR3kWLbbrfb3a/b/rpfy/83NmB3a8tirWc9z1q1atW/+Vx1MDAxN3/5y1/zyyH46z/+5a/B2XN2az9zyr/+Q/X4KciO6yTmT4H7349JkXn3r1xc5fnh+I+Nxt7JtkF+2DleUD+tj4WzO+aFv07qXrJvrPNgf/yvqn97zj74L1x1MDAwZsnez7P6ly+5XHUwMDBi/HWeZFx1MDAxZr8r2Fx1MDAwNfsgzo/80/9r/vtf/vJv7v99dHVZ4OVOXHUwMDFj7YL7N9w/9eVcdTAwMDJFyp4+2kvi+4tcdTAwMTVcdTAwMDFcdTAwMTCZXHUwMDAwJUxcdTAwMWVesT5q/PvywOdPh/yagy/PVFx1MDAwZv2VXHUwMDE08olcdTAwMGXC4NxcdTAwMDNcdTAwMTfdX/Tc1nZlffnacL3bjfLL7v6yjlx0XHUwMDFmzZfnjnmWbIPZ2s9Xn+/bo8dfeleWXHUwMDE00SpcdTAwMGWO1fDFh0eTg+Ot80v1mCA8PPrxXHUwMDFl/ONfvjxy5r8hROtcIkWYXHUwMDAxSFx1MDAwMVx1MDAxNYH08Gz1fibUJcifJUggXHUwMDAwYFx0P7kuNdnxieDX9S/FoPr75cpcXMfbRvzyYv/hNXnmxMeDk/Hp+vK68tOIiVhcdTAwMTdcYmZYglx1MDAxMFxiUFx1MDAwNOjhXHUwMDE1q2BcdTAwMWSt8vtcdFx1MDAxMep8SjBcdTAwMTAliVx1MDAxMkmCj64muJ9cdTAwMTPKr1JcdTAwMTRE9GVcdTAwMTDVJVx1MDAxY9r+vXn86y9cdTAwMTORccNqV++Ii93u8d2M/U9387NcdTAwMTl9MST46ZF/+2WM1eubj1xm8Ms3XHUwMDE0XHUwMDA33/loJ1wiXHUwMDExJFFkQCCSSFx1MDAxZp7frePt06/fJd72i2n9i0ff9aM2Lb5o01x1MDAxNFPMjVx1MDAxYb7ept3MwOX0aOfHXHUwMDExMTp3QXg6XHUwMDFl5jdu01x1MDAxMqgjKiGKMVx1MDAxNVx1MDAxOKVcdTAwMTR/ZdRcIkF1iXDDXHUwMDE3JVx1MDAwNCTIX/FuVo3FOlx1MDAxNiVRkLjR8qv51qZcdTAwMDF7asNcdTAwMTK3XHUwMDE0fu1I/EU2/PCeL+9+ZHh5cP4yzEdWklxuXHUwMDFm0qlxuFx1MDAxYbm3PPY7J62pRKu/Przu33766U9cdTAwMDPIV9f5XHUwMDE4XHUwMDFi8EV/j7lBXGKYQOnV0Hh+0LdcclxyXCLVIVx1MDAxM7jlXHUwMDBiXHUwMDEyYtxXPkFcdTAwMDarU0KxyCBcdTAwMTQ5Loj0bshcdTAwMTClOlx1MDAxNSRCXHUwMDA0yrlcdTAwMDVAXHUwMDA06bfgXHUwMDEwKb9WkVx1MDAwMIRcdTAwMDCgXHUwMDE48f++XHUwMDAxXHUwMDBid/iM01x1MDAwMfzbwfLVXHUwMDEz7+rZv1xcVVx1MDAxMuej9fXeMaM6XHUwMDExuVtmnG6fjKh6le7s17vLV7N+b+H8Lo/Fv371mLxbR5Wl/9Xj11x1MDAxZWRfgSBfc1xy9fCC/dr3XHUwMDFmM4jHv8hZx0HWfo3jT7J1tI6d3fiZ6+D3IDBcdTAwMWVmsf5IVLjOMaievafzn6c4Ls1egjF3tFxiiOKjXHUwMDE3fFx1MDAwZsbYXHUwMDE4auC6jGmyPPrCMFx1MDAxMEg4j25cdTAwMWPGnOGoRJmEYWUtXHUwMDAy/WIu91x1MDAxZlx1MDAwMDnDXHUwMDAxiVAoUklcdTAwMTAoenJhv5TgRCxwXHUwMDExR1x1MDAwMPnyzO/AcNlcIlx1MDAxM8NkoYFccl2jq3g3zKUhvEmGwy9BQ0Tc21xukEjs1dh4ftS3jVxyLu9EgZujRERE2LfY4Fx1MDAxY1x1MDAwN1x1MDAxOJ9ccsa5nmDx/dSfyOpiRVxcmINQolx1MDAxMvs5jqNcdTAwMTggLFxi+FxygprfleNM4Z047juu/ynHPb6O9+e4R0HaUyBjkVx1MDAwN+xcdTAwMDJcdTAwMTJfXHUwMDFmxrWM9tbMr1x1MDAwYlx1MDAxMl1Svy2nYLVcdTAwMDXHXHUwMDFiXHUwMDA3Mic5JnAy50E9RJwwnuQmWJ2jXHUwMDFjXHUwMDEwXHUwMDA0XHUwMDEw15E8ZLopkpOIxCU0XHUwMDAxv1dcdTAwMTi3KfBUXGYtXHUwMDE0NSeFhrqiMe9f6e9FclWeinvT1yPj+THfNjJcYqxjzCTGf1x1MDAxMNEjP/Hx7ahcdTAwMGWBhEQg8LBcbmFcYt9cclx1MDAxOECoc3FcdD+x2yN8/lx1MDAwML2JfLb4xWLhXHKQ8rvy2/C9+O07Xv8pv1xy/1R+o8LTR1x1MDAxZlIxmFRcdTAwMWWfvj5cdTAwMTWDjOvcXHUwMDE1Nyza9cXexVjbJuvqt1xyYlx0SvXnc+vV+ymr8uFcdTAwMTRWXHUwMDExXHUwMDFlXHUwMDA0XHUwMDFjPe9cdTAwMDZiXHUwMDAy6lx1MDAxMuY4XHUwMDEwXHSjSFx1MDAxMJ5FMVx1MDAwMHVcdTAwMDJcdTAwMDUmSDx8kJj4TEzHrV6kXHUwMDE0v0FcIuY3zrxcdTAwMDP8cuxFkMTFXGYjr7fpXT9cdTAwMWHQXHUwMDAzSdw23XZObamvjJBx6zbN6lVGjkf4XHUwMDAyxqLwJPSqXCIzxFx1MDAwM1A+J1x1MDAxMuCRXHUwMDE3eTejxpV0XHUwMDA01eRcdTAwMGJcXFx1MDAwYqBnbFx1MDAxYXLuYnw6IIRcXE1L6NG1fjJqRki1JCX+Xlx1MDAxMm7mSeuLXHUwMDE37PZb3+5cdTAwMGY2VzOj1/Nccko48MhXPJVwkN93LIrw1VB5ftC3XHJcdTAwMTWJQ4VRiFx1MDAxOVx1MDAwZu7JI130kIhcdTAwMTdcdTAwMDBcdTAwMTRcdTAwMDRcdTAwMDFiiJDwfsFN5dox5PTDUVx1MDAwMEThpSRcdTAwMDUh3O+LXHUwMDE1WVx0PFx1MDAxMJKeYkWCXFyJUiyQv1x1MDAxZCu/WMRJPKqsVlx1MDAxNSCsnFx1MDAwN33kOr6XiFx1MDAwN+8k4r7DXHUwMDAz3yTiwY+KOPg3MFx1MDAxZXtxPY1rflxmqfQjhLdG3SVcdTAwMTlcZsruh+WyY3ja3d2dd+MoRkJd5DzHXVx1MDAxNlesXCJ9XHUwMDAyY1xi64whiYdcdTAwMDNU5MYpvJ+Ke1x1MDAwYsJcdTAwMTOr6+R+if4qXHUwMDE193OM11/dnVx1MDAwMus8oYOeXHUwMDEwKvFdt4WbzVtkPOllrFxiXFzpS+RHVq2eXHUwMDFm9W2DXHUwMDA1ozqrsnU8tGNcdTAwMTJcdTAwMTWfgoXVOVxySVx1MDAwMuTPUFxi31x1MDAwZitcIqxcdTAwMDNcdTAwMDJcdOXOXHUwMDFkiIi+tPb8XHUwMDFkylx1MDAxM7nMpdzH0b9jzjPfa/H5O1TwTWL+h1x1MDAxN5//XHUwMDA2zoOi9PTRhyhcdTAwMGZyXHUwMDE5ReBcdTAwMGYssC2jqTLE7XQmdqbLOKep4pFcdTAwMWJffL7nPFx1MDAwZVTCoMTpXHUwMDAyPknLkzrhmFx1MDAxMqvyXHUwMDEykYvbd0zLv1x1MDAwNeVJXHUwMDAy199AYL+qZPDnKG9cdTAwMWLGcUdcdTAwMWFCXHUwMDAw4Fx1MDAwNzNLw8V1c73+XpQnUi6JePRcdTAwMDNej5XnR33bWOGUhygg3DxcdTAwMTFgXCJ7UokowDri//BcdTAwMThcdTAwMGZcdTAwMDAgPS7cfHPOQ3WAXHUwMDE4XHUwMDE1RFxmOf9cdTAwMDJ+93+K86CAXHUwMDA0LD12cH93nDd8L877XHUwMDBlXHUwMDE1fJOs/zM5XHUwMDBmPMpAfFMnL1x1MDAxMMRdq/D6dFxyXHUwMDE4urq8OFxyp9p0d85yQyrN8Maz9Vx1MDAxMNG6XHUwMDA0oCiIVMCYg+ArIFNS5/iGXCLAgN9cclx1MDAwMb5cdTAwMWaOXHSoXHUwMDBiWJS48CTVmsHzdfJinVZ1X1VqSVx1MDAxMjF6VOHyXHUwMDExxoBH5tJX+P57zNaL9OVsPebCXmKQvd6mNeonQFx1MDAxYpK52ZSt7sHNXHKt4964TVdVhFx1MDAxNHLtXHUwMDA0XHUwMDE5k7g7fFonXHUwMDBm6lV1XHUwMDA1V3pcdTAwMThTTKV3zV7wQIrHfozTXHUwMDBmXHUwMDAyj77pXHUwMDBmaixcdTAwMTjAXHUwMDFjcFx1MDAxNP5e+flcdTAwMGZHcyNccvS76faUTmxcdTAwMDPDw1JxblC6iY9S0k/RIVx1MDAxMu5cdTAwMDRcdTAwMDVcdTAwMDBfL92eXHUwMDFm9W2jg5s/XHUwMDBmXHUwMDFlXGIjXCLk0kl8upZF6lxcz1x1MDAwMbEqPlwilLL3XHUwMDAzXHUwMDA3XHUwMDAwXFy6XHRCVbBcdTAwMGbFinqeQVx1MDAwN5dumFx1MDAwM5XPXHShosTAN2VcdTAwMTb8bVxmYop+/2RcdTAwMDWqV1x1MDAwNSXVXG4j91x1MDAxM1x1MDAwNFx1MDAxMvTVq/4oQf9eVVx1MDAxNt9x/d8k6P/UKlx1MDAwYuHFKkJutty4yVx1MDAwZlx1MDAwNGAwmm/3YUBoRsaXsd3Ky2CV3TiKJVjnqoy7LIxcdTAwMTmFfLxfw1x1MDAxOIK6XGIpIzxcdTAwMDBgXGJcdTAwMTD8jnvBfobjeCxcdTAwMDZcdTAwMTigXGb9Xin5dXCeLZN1XHUwMDFheabfy2epl1xcNstbJLlHqd9vJSBkXCJXXHUwMDE3r4fH86O+bXhQoU4wq/Y5V/tcdTAwMDLQo3TdR3iQOlx1MDAxNCVcdTAwMDL4X4GHQML7wYPUkVx1MDAwNFx1MDAwNcKpq8qGfLVcdTAwMTP59SzH4Vxc7dlG8PfPT/w0zf2rf/VONPdcdTAwMWTv/5TmXHUwMDFlX8efsCHsxWpcdTAwMTKRidyB8ll5/dra4ewpQ+aRXHUwMDFj+c1cdTAwMGXtfDBP09aNXHUwMDE3y1c8XHUwMDA3XHUwMDA1ylWihDCh+EnpXHUwMDE1w3XOOVx1MDAxOFxuXHUwMDEy92jc691cdTAwMTjNYVx1MDAwMSHE/c/vtSWsXHUwMDFmblpcdTAwMGJ90oovXHUwMDEyXHUwMDE1oFx1MDAxZp3HXHUwMDFisPm9WI5bXHUwMDBij/9/oMXF82O+bWhwjuN0Qbg58mhcclx07CtkiILIKY5BwiBDnDrA+1x1MDAxNSW+XHTFVVpcdTAwMTBcdTAwMGKP60j/M8O9XHUwMDE1w33H7/9ahlx1MDAxM/8gWylgflx1MDAxZn+g0spfK1x1MDAxZlxc4WDt95smXHUwMDBiRqfzLE9vvKlcdTAwMDdEQlx1MDAxZHOpyvErXHUwMDEyhumT3WCE40aCXHUwMDE0M8zJnt+O9+tcXFC5XHUwMDEzsVpcdCBVnbOEn0UxkupV8fD9vpavXvJcdTAwMTnFkCtuILzFppc3JLyPxjFuL5JcdTAwMTWyJp2z4Vx1MDAxY/zZZO1LcvE1/322WCfLkvJcdTAwMTcy3ndqXHUwMDEzX9xgQqmAXHUwMDE4fdwo6HuIiVx1MDAxN4Etbb1esWxfVlx1MDAxM6loXpvgw20jhlSbrzhgXGKGPI7lwd1XgLlcdTAwMDNcIrdRLEpcXKlcdPdNl96P996it1x1MDAxMyfDaiPo4+1sf4+LVoC+nJbH1YZcdTAwMDVGwOvV3J3JRqM09PBuLuiWv+vPR45641Zcclx1MDAwMOdcdTAwMDFcdTAwMDFcdTAwMTChKumBT7ZN3YlQrFx1MDAxM1BcdTAwMTlcdTAwMTDBlPte9m5W/Vx1MDAxM7uCmSQy8su6kz3v879cdTAwMTfkRFx1MDAwMPRM5eh2wu1cdTAwMDfg2Orxg9vVbzDIXHUwMDAxjzpmPd1OWG0/rUrAXo2L51x1MDAwN33buOCSQ1wiqFxuZJiAJMye4Fx1MDAwMuA6XHUwMDAyXCJklFx1MDAxMVb1Pns/d/82rZ1cdTAwMTBlooiA8M8hk3drrZ2+4/Z/aWsnXGL+gOBcdTAwMDQ+L/xcdTAwMTa+XHUwMDFlyHFcYtzDbDZAW2E066VcdTAwMDbGXHUwMDFm9Fx1MDAxYk9XXHUwMDEwXHUwMDAw61x1MDAwMoJcdTAwMTV2MMKMPCU4rqZcYuH+TFx1MDAxMiuao+i2+l5Q7mZcdTAwMTFcdTAwMTLZ77VepVx05nmtfZgv7KblXG6n1Fx1MDAxYtubwS2S3MuZPCpcYlxii+BcdTAwMDdawjw/6NvGXHUwMDA2QnVQXHUwMDA1+azaJ1x1MDAwMr/BXHUwMDA25iRHhKqzXHUwMDEzXHUwMDBmfLhCfL9cXN7b9HZcdTAwMTIxXHUwMDA1lFx1MDAwMfhcdTAwMTZtYn5Xlnu35k7f8f2/tLlcdTAwMTP8g4Vnidu1KDD2epZbubF33YhsL7nCTj9L7Vx0XHRaN47kiuUoXHUwMDEzXHUwMDA0LvKY8LQw/o7bZFx1MDAxZFx1MDAxMFpcdTAwMTVcdTAwMTZjVPXBeMfaw59huUrWssdcdTAwMTHF78BytthcdTAwMWFcdTAwMWWdy1Tb+2Uz/LDzuqfgdJMs92LNUtW8moujXHUwMDFm6Fxm8/ygb1x1MDAxYlx1MDAxYlxi1qnEJSA3x6poiT7FhlhnXHUwMDA0VUl/goHw1TaNW2zwRLg/kyhFvzySYz9guG/Lce/W4Ok7nv+XNniCf9CHXHUwMDFlXHUwMDAyzJVcdTAwMTP7gYXnYb49XHUwMDFkoqVsK8vhNT2Lm7usP75tXHUwMDFjU0GqS7ja/y9cYuSbs1x1MDAxNe64Vq9cdTAwMDNcdTAwMDA5mWBCXHUwMDAxeNw86CZ7PFVlzpRcdTAwMGbjV1x1MDAxNWm8Jbz/XHUwMDA2q8Yvklx1MDAxM0TVfnBcdTAwMDG9vmKQXGY2/phccpflXG5f1GnaXHUwMDFhalp445v4qcDqrKogkqo+29LX5bR39/W0XHUwMDFj15xcdTAwMTNcdTAwMDRcdND3Y6Y3afBUVdZcboD8XrmKmOym0nh50vxp0tqYi5WG51x1MDAxZn5cXMW9XHUwMDE5Wl5ScVx1MDAxML+4TZ4g7mlcdTAwMDRcdTAwMTG9fnfV84O+baBcdTAwMDBcdTAwMGVcdTAwMTRcbnk4J0FcZih9ouLuXHUwMDEz8oxcdTAwMTDu/lx0XHUwMDAyXHUwMDAyeMcm7W/T4lx08seAJP76VMXfLONur8XTd3jgl7Z4QsKLZVx1MDAxNFwiRoDzIWCvl3GGYJhNY7yxLNddXHUwMDA2vaC/Q/P0xoEsXG51wCSxknKU4UerXHUwMDBmn1x1MDAxMvKgzrHBoyRcYiiPXHJunPQgXHUwMDEyiSiCXHUwMDFiqzz6XHUwMDFl6Vx1MDAxMfVcdTAwMDBPXHUwMDBi4sXRQFwisb/qnzvgJ3ZN/lx0pPdizCORqtNcYnl9Vu/5Md82VKBUr1xiRqKkolxu+lx1MDAxNCpcdTAwMTjXMVx1MDAwN4qI7plcdTAwMGa+426rt+nxxIMzXHUwMDFl76C3UIi/K+e9W4un7zDBL23xhP6g2JZcdTAwMTAsQZH8QO2gWZ42XHUwMDFh0GT4QVx1MDAxNTRzcVx1MDAwNyeD9MZ7PFWcx5miqlNniL/8690kd0CgdUlcdTAwMTK5P6NV6Tx5x3Lbt+nyRESuXX+zyivJXHUwMDFhXt2LmVx1MDAxZVx1MDAwYqeTybGa0M7wJ/pcdTAwMWH+Ss5cdTAwMTOrXHUwMDA1UlhtXHUwMDA1ejVYnlx1MDAxZvVtg1x1MDAwNaI6xkSSKFwiXHUwMDE0Yfw0zydUbTZcdTAwMTjmUKE8KJTecVx1MDAxM+VcdTAwMWJ1eZKq3Z7gXHUwMDA2jlx1MDAxY/p1tPduXZ6+Q1x1MDAwNr+0y1x1MDAxM1x1MDAwZmNeQjI3XHUwMDE5XHUwMDAxQEpen7JZ3s3yg0lLXHUwMDFkS3HfXHUwMDBig0s0kG+8hlx1MDAxMlfYXHUwMDEwuGj8XGJcdTAwMWXyTcKeP11cdTAwMTU2SpjrSvKuxyG/RZsnTt9cdTAwMTLlpn+z5yH/OVx1MDAxOXtAX8zY8zC4aryHwOvNelx1MDAwN5f51vXTPZwuXGbJ93vg2rvx9WRcXNVaYFxuJFxuofR4eeJTylx1MDAxZXItR6rkXHUwMDFlgphQ+H5W/XMtMLi5cED+Oiv+OfUmJlx1MDAxZraRtUvX2HRIs1xcSHfJmt2gelx1MDAwM+jlnfPc+1Tty3+gXHUwMDBi2vOjvm10IFhcdTAwMTcrcIhcdTAwMWMjXHUwMDAyfNLC/U5cdTAwMDS0XHUwMDBlqUCrXHUwMDFl74B7f/aO1Vx1MDAxNm/R6IlfYEVcdTAwMDb/XHUwMDFjii1urc/Td1xc/y/t81x1MDAwNIWXXHUwMDBiXG5cdTAwMTnDXFzVgFx1MDAxZjhcdTAwMTNvPS1meKvisjtrmWdj9UFcdTAwMWGbw1x1MDAxYocxJzmEMOOxj1BtXHUwMDA0/qZsXHUwMDFl1cXqRFwiRrmJiu/Yru3nSE7gZlx1MDAwZVx1MDAwMXpcdTAwMGLc/okkd5X6IDEtY0FbXHUwMDEyZlOWhmBk3yTJvbyGRZHI5+lHqjaeXHUwMDFm9W2jQ1x1MDAxMupMqlx1MDAwZSu4PzJSfFx1MDAxMtmImNZcdTAwMTmtOlRcdTAwMGJIqNJcdTAwMTTvXHUwMDE32LxJmyep2kpMXHUwMDAx+P1cdTAwMTNcdTAwMTS311x1MDAwNOM7vv+XNsGA4MU21EyUqlJx+nqtutoyK3LXdkdcdTAwMTLDu3FXlPZcdTAwMDe1e+MwrkiOXHUwMDAxQlx1MDAxOaKAQ+BJKFx1MDAwN8TqZFx1MDAwNlx1MDAwMikhgKB3PPj1ZzlcdTAwMGVXVYi/rIDw5zjuIKvyeT0kWN6cwKHUVGpA5Vx1MDAxNjnuUdO7b9PwlM+X+Fx1MDAwM60+n1x1MDAxZvVtg0NcdTAwMTLrVfREJChcdFx1MDAwMn3S6fNcdTAwMGVcYtzXUlx1MDAwMUPKRP5S8I7JuzfhOFx1MDAwNvmMSo+LzP8zx71Z2fxcdTAwMWa7/l/b6Onlc5Ehklx1MDAxOFx1MDAxNn+kaj7vTked+Wa+OJnCOo0/SO3z0LpxXHUwMDE4c1x1MDAxMqNcdTAwMTVNIMqBLD5t8Fx1MDAwMVx1MDAwMKmQI1wiylx1MDAxZNrXSZKb7PQk8shcdTAwMWNQxP7ey+bBy1x1MDAxMZjAyP3BXHUwMDE5r2en1qm7u8ZcdTAwMTBpdvtoLERj3tyNtrdt1oyzXHUwMDEzglx1MDAxNS1hWvXo/Fq6MVxcbb7guo6Ho9L7XHUwMDFlIPJcdTAwMTbNmLgjqo74XHUwMDEy/r5cdTAwMTeW4KNcdTAwMTNcZp/YNFx1MDAxMlxiZFx1MDAxOP6Ap87Y0Z6qq7w5Pp5a7IN6csvNjXeq4OqEW62IicgoJFx1MDAwMH5d7Sfet5auUuqQP09cdTAwMWVcdTAwMTfR3cBcdTAwMTbeqlx1MDAxZlAlwH6rUCSZe0i1JV9cdTAwMTXYSSd22pfgXHUwMDA13mIoQl5cdTAwMDRcdTAwMDa73+hGfuCg0+dcdTAwMDd928CoulFy7Vx1MDAwZlx1MDAxOZRItSn2XHQwMHfBXHUwMDAyQ+L9XHUwMDAxou9YO/c2nZhEidwvXHUwMDFj//pWTL9uXHUwMDAz77u1YvqO2/+1rZikXHUwMDE3YSxcIlx1MDAxZWHj6lx1MDAxNLHXazab5smdXHUwMDFlo5ZlmsfNOVxid5l54ziWcJ2zisjFPVx1MDAxNCl9svOD31x1MDAwM25OVFx1MDAxNFx1MDAxMJdK/F7dVotcbiAxJlFBfINcdTAwMDN//kSGY0C19mm6ppPN2Vx1MDAwMX3zfLnOurfIcPjlZFtVaFxmXHUwMDA0+lx1MDAwM5uinlx1MDAxZvVtQ4OQOkRcXPaJVeNp8FxyNDjFcaZHgEhcdTAwMDKrXHUwMDE2oN+P496kXHUwMDExXHUwMDEzoYRi8iZccl1+V4p7tz5M33H8v7ZcdTAwMGZcdTAwMTN+uaFcdTAwMWGubiB8/TaP5m7sxYZcdTAwMTZcXDvnmXi+LLpcdTAwMTDtb/x44nuCk1x1MDAwNC5cdTAwMGUlXHUwMDExc/t7gmJBqlx1MDAwM35cdTAwMDeqfWs81lx1MDAwNTdcdTAwMTXAiVxmMSpx3v29du93lFBzxmN3RFx1MDAwNH9pXHUwMDFlm5vIlfFNXHUwMDEy3Fx1MDAxZjQoq1xupHlI8HqCe37Ut1xyXHKC66xcbn1cdTAwMDThfsvsk+NcdTAwMWZFoS6JhEBIq1PuhXfc0/EmPZiAVDWUwn/HXHUwMDEx3Lu1YPqO2/+lLZiQ+OLWLCBgzK1F/IE2g2R0XSw7RnHpf1x1MDAxMJLw6i+EPbzxJlx1MDAxY6JcdTAwMDCrVIzwXHUwMDA0Ovcpd05uuEI4XHUwMDEyXHUwMDE4xoS947k/b9B9XHRWVY1cXHT/qnDuRjLu7MWtXHUwMDFjXCJcdTAwMDV8Olx1MDAxMfqBY95krPda49Tc67FcdTAwMTP4O82wyEW4dXtGoM54wEVcdFx1MDAxMFx1MDAxOEToa5uucu5cdTAwMTCCqlx1MDAwNS5cdTAwMDW4klPvJ9neYl8ul3BcdTAwMDS/SffbP1HCqeJcIm0vV2RFTl1cdTAwMWYlg0F4OO5vUMJB9uIm9uquXHUwMDEzIIqvx8rzg75xrOCqkzqSJFSdXHRKMHiCXHUwMDE1qc5cdTAwMTiqXCKOqlx1MDAxM1x1MDAwNXlHXHUwMDA19yb9l8RqR1wipOSfQUHQ7TVg+lx1MDAwZVx1MDAxNfzaXHUwMDA2TPDF0on7o+d/pKyvPzyMx7tEW2zdjZVcdTAwMWWUMVx1MDAxM/KbhzGCdShhqTq6XHUwMDE0i1x1MDAxMvi6IKjKwouUh2DV+aFcdTAwMDJD5P103FtQXHUwMDFlZVx1MDAwNImMvsVa2p9IecLQX2z3JMmF1rajXHUwMDBmNfWK57e48Fxm2YvxjvixL1x1MDAxMZBen9B7ftQ3XHUwMDBlXHUwMDE2TOtiVZDGJSCAovREXHUwMDFmXCKpTlx1MDAxMa3uXHUwMDAy4+FcYmbv13TwbVx1MDAxYTCJ3Gq42n2T80R+V9J7t1x1MDAwZUzfIYNf24FcdL3cgYny21eVNbye9Vxct8yC2Vx1MDAxObW8/lx1MDAwMlx1MDAxNlD07sxcdTAwMWJPP96zXHUwMDFlXHUwMDEyXHUwMDAwwYjLRvBYnn78XHUwMDAwWGdVkVx1MDAxOVx1MDAwMZhK4uNcXOxNsp54v47wy+pcdTAwMDV/jvVcdTAwMDDCXHUwMDFm1sdLXHUwMDA3XHUwMDBl+4VsfMj711P0XHUwMDEz53v/UtajVSOf6ii1V4Pl+VHfOFgwqVx1MDAxM0CZxLhf5Vx1MDAxNvh0XHUwMDFki9WxXHUwMDAwsMRlXHUwMDE3f1x0j1x1MDAwN9+P9d6mXHUwMDAxXHUwMDEz5MaDvuq283fHeu/WgOk7ZPBrXHUwMDFiMD1abn6asmFVr5pcdTAwMWY5svh4wOFlPG6b+yDIhlkr/TDRprdccmROZXWuSjndVWr9acJcdTAwMWXWXHREuDp5hyFcdTAwMWX23Xj3JSBWlUDscVx1MDAxM9S/y5S9+HL3JVxiq6PPfoCb7lx1MDAxY7WLk1Ore+e7nfn20Fxu7u5uvMSCViVcdTAwMTaAYYlcdTAwMTGh2lx1MDAxMPgkICO0ziBcIpRIiNNcdTAwMDVcdTAwMDDvquN+fM8u5zLGMP7N0lx1MDAxNZFYwmSj4p6wlzeXiSsuS4JuULhcdTAwMDHy4nZ2XHUwMDExQMLu3c+rwfH8qG9cdTAwMWJcdTAwMWNcdTAwMDRzvVx1MDAwNFx1MDAxOeX/SFx1MDAwMDw5TUSkqM69g1x1MDAwNLmYkqojg95cdTAwMGZcdTAwMWNv0nqJO3xcbqlI/lx1MDAxOSQrbq/50ndcXP+vbb70XHUwMDA3pzlWjZcgk35gJ5jCcjj10o27QNtcZvbuXHUwMDE27uWO3DiOOclJXHUwMDAyhVx1MDAwMmCEo1x1MDAxOD0ploK0XHUwMDBloVxiXHUwMDA1oTotgjw+9+UmSK5qXHUwMDAxVKUtfplS+zmWc1xyoEqj+Vx1MDAwN1x1MDAwN+57zLs7dFTZXFzfJMu9mJ5APGSvqsNfv4D1/KBvXHUwMDFiXHUwMDFjnMaqrmSU/yBAwJ4kJ1x1MDAxMKygI6BqXHUwMDA1XHUwMDBiM5G+XHUwMDFmON6mL4UoXHUwMDEy8W16qf+uXHUwMDFj925tKb7j+X9t6yXpRa1Kq87n3GheL1WHd4lI24Y1XHUwMDEw7ENyl1x1MDAwYsphKCY3juKK4iTErYVcIiZcdTAwMDHhyZ5OXHUwMDAxcIqrnoBYQFx1MDAxMsE3RnFcdTAwMTLgl4XQb7ZcdTAwMWJM81xcKVxiMoA0b3bSXHUwMDBilIZ3yS2egFx1MDAwMMjLzS24VVTNXHUwMDA1f6BG4/lR3zY4KorDXCIjmHCXSqQnlVZcdTAwMDKp88kgQkVcdTAwMWScV4T3q0p8XHUwMDFiiqu2/mBcdTAwMWWb/nMol781jvuO6/+1XHUwMDFj93K5PJdpXHUwMDEyoVxm/0BcdTAwMTPdRNiF80677drn7S5cdTAwMTl/kM5old04jrlcdTAwMTitykbu2/F81Ybmcv9snVwifN8tXHUwMDE1v2PXgjdpu1x1MDAwNFx1MDAwNYlcbuRNuqe9IeF9NFxmQzlS3+ru5HFnQjqOXHUwMDA1rl7Cvua/z+bqZFlS/kLG+/j9z0Dl8TmgT+UgpZghXGZfz3jj9lwiWSFr0jlcdTAwMWLOwZ9N1r4kXHUwMDE3t41cdTAwMTRcZjnjVa29ONtBJjypLb5cdTAwMTO5562OfmVMxIx9tVx1MDAwZfzmS85cdTAwMDKug6qUi1xiL1x1MDAxN1oxservi1x1MDAxMcNcdTAwMTK9b/v0XHUwMDE0L5hrVlL1K3lcdTAwMGa8gO/j5V2Y8Jg7Wa6sY39cdTAwMWRHT99cdTAwMTLE/pdnXHUwMDFlXXLw8aI+kpW/Vj64wsHa7zdNXHUwMDE2jE7nWZ7OXHUwMDFmIZWzqFdU13/HXVx1MDAxNlx1MDAxMyDmliBJXFxcdTAwMDMh/IVqq3vpXHUwMDFjqvF8c1x1MDAxYnbOMVeT/X6d81x1MDAxMVx1MDAwZpJ1nD+9zPshyFx1MDAxNVxuV4HzzW3ng3j83FO4XHUwMDFlqk/82lx1MDAxZH356S9fLPr+l4ef//U/PPvqu5dccq36862JffnAf/H4/1x1MDAxZnY1j05keJo+kqRcdTAwMWbrJfTHnvcmXHUwMDFkXHLFqOpcdTAwMWJHKFx1MDAxNp9cYtpPjkaqQ0FcdTAwMTBEylx0s2rw/Y6OhkeePL5cdTAwMTWIWFx1MDAxZJv+PC0zUqdcXEBcYlx1MDAxMFVb6r66moeCTlxmKXyTZkL/jFx1MDAxY81cdTAwMWZrxUeOpurMR4mAXHUwMDAw9zFVVZ3wZUfcg59cdTAwMDFcXFx1MDAxYd9zkvRbO5xcdTAwMTft7f7JbyztXHUwMDA33c1670Q/WoNTxbbVpjv4+ibj+/k1PIDDwrCZNVx1MDAxNDumuVx1MDAwZbZ3t+1xXHUwMDAwonXI9UBVd809j/gk1XVXnSVAMKCoqi2syrtf8jjPepNcdTAwMWb2Otyz1aXK5DGhhH/hc1x1MDAwNaiA1avOuNVRUdwkXHUwMDFlxWhcdTAwMGbpryopQNhcdTAwMWIs8PxNXHUwMDAx/SOq+jHvkt+D/6+H4KMjeWw/XHUwMDFmr/evXHUwMDBlXHUwMDBm71x1MDAxOaqifSAyhyFRYoj/8ZlcdTAwMWaSilx1MDAxY1xidFx1MDAwMlx1MDAwN0mP9i9cdTAwMWN51Fx1MDAxZXyFVfFcdTAwMGLCPmuVP1x1MDAwNtFLXHQx8mLxNuNzRCrbeTWCPoj+XHUwMDFhnTJRZaG2XHUwMDE4TnRrQq1cdTAwMWJHkFx1MDAwNKS6hDiZcNpcdTAwMDaUPO1Ezlx1MDAwNWKdikRcdTAwMTSoKCHwePXrzSlbrFpccjJGOZzBfSnbt+B5pmUxrdq+fnWS/Z9cdTAwMGZcdTAwMTdcdTAwMDQoeHRMyc/lv1x1MDAxZTVcdTAwMWT+Tr7rP/2H/+n//e//3f/z7/7vf/pcdTAwMWb+2//vf/nvxi9kv3ZB+LXBf537ypPDS4mvr678aZbr5W9/Xc6L/Fx1MDAxNEqB9GLZXHUwMDAyqnp3XHUwMDAx+sjSv1t9dFx1MDAxOW2mobpUZSWYXHUwMDA1K1wikXCyvG2Y0vskklxiqrbMXHUwMDAwU/CU5zh4RFxmK1x1MDAwMULvK07fXHKmpFJsrDpqT8BcdTAwMTRK7Jl6029RijDXROjXnoHzZ4P0v/m//r//9T/+p//w780/XHUwMDE5ns9871tcdTAwMDDz5eya8GLMe3/oukR+YD3J3WEot1x1MDAwNb9cdTAwMDPYhozYXHUwMDAwjK6pd9vIxJRHtVwixtViXHUwMDBlgk+Ta1x1MDAxY5N1wDi1cmmKXHUwMDE4XHUwMDEw35E/XHUwMDA1sVx1MDAwZTmRXHUwMDEzXGbvQy78XFxuTST1qtS7anVcYki1xf9cdTAwMWI+ZVx1MDAxMFLKkf0u9UV/Y8zL3Z70U1xi/ptj3ql0KFtJp7XVu1Z3JbPDXeFfXHUwMDFlx7yfsmZcXCtJ1WFcdTAwMTBcYjJcInE3XHRcdTAwMWW95EtYXFzVpSDIQ1x1MDAwMIm/lI+LfXNbfqfY9+5ly/v49DdG9+VcdTAwMTNfpdyD3W59OD5cdTAwMWJcdTAwMDC/XFyUzKMwXFxl919f6PHHc3yTvodzcJ1BIHCAXGJcdTAwMTRh+OT8XHUwMDExyieGVSc1Slx1MDAxMsDiO/ZE5J9d52ErXHUwMDE12ad86zOqQKq60DFcdTAwMDB5qHGf//tWyrOqTOS9uiS+wvU8vOfHXG4/2kaWTFx1MDAwZXDC1PN015XvlOPk8bmS//D8x76G7776/p9YXvtRf/lTyvxxzuWpMlx1MDAxN3lUj7H0+nXo52/mbWNcdTAwMTDS+v2mXHUwMDE4QPhccpeeXHUwMDFlWU4oq1x1MDAwYlx1MDAxMFx0XHUwMDA0IVx1MDAxZUdL8P2UOVx1MDAxNr5cdTAwMDXdc1x1MDAwMTNcdTAwMTftUFx1MDAwNOxccsqrfj6/9NNM/lNa/J/+t//zn/7H//if/vd//051XCLfYY6nXHUwMDEy/ZnLedfQ+eVcdTAwMDNlkVxiXHSqzlF9NUBcdTAwMDfbvD1cdTAwMWGke9Jll3Jqz1xcZ1x1MDAxOUa3XHUwMDBlUFxc9bTiXHUwMDFjWG1eXHUwMDEzn9Q031x1MDAxMVx1MDAwMd0rdC7Z4Fx1MDAxM156Y4AyWK80IVx1MDAwNYD/Izw+VumP8CphLCBcbuivLPC6j53xY2v9XHUwMDEz8PoxvzR8XHUwMDAxs+9cdTAwMTQ+v/DV74lP+uJcdTAwMWFcdTAwMGWTqqzrXHUwMDBmoHOpxaODtYadhdxGa6GXrDdcdTAwMDP5ttFJRHxcdTAwMWY+Q0HglEC+3o3DQ1x1MDAwYiDWXHSXhFWfUZEy9H7o5NdQXHUwMDE1K4s8eONcdTAwMTHc44WZXHUwMDA3dLLq2HZSVU5cdTAwMTBcXLXz/uasMVxmXHUwMDA0xkNcdTAwMWTy22OVoDqpalxi7+OG6jiR10JX+af/43/+y59cdTAwMGLZJ1/5Kqhi9Fx1MDAxMar/4pOA/qtzOIxyfiNcdTAwMWZiXHUwMDA3PnNr/9Pd+PL1fz2tg1L51rL+ZSiFTlx1MDAxOFby/Fx1MDAxZf73i11fXHUwMDAykdeveH1cdF246NhcdTAwMDfjxyvAjcPj5bT1Ty6lcXNxJkOrenP14z8+fPR/Ud1cdTAwMWaM/mE9VfrDUjBbUVwi8z+90WTVnESyrDjVr62hKi/4/+r4Mlx1MDAxY4+rR+R5bzRcdTAwMTTacnZEXHUwMDFltmW5LSudYVOfXHUwMDA0OjlC26d6a46Vhlx1MDAxNl5b5552VnB+sVx1MDAwMJ/CdKF4enCpxXEx7Vx1MDAwZsLTXHUwMDEySFwixPE+aJAudFU53Vx1MDAxZHvBRNHJcGpPXHUwMDBlXHUwMDAzb9Q25uQ6KFx1MDAxYldyJO2RasPZaFx1MDAxY0/EXHUwMDFhvW77XHUwMDFkq3sl8Fx1MDAxYc7m7lx1MDAxNVxcwkGKmq4yWsmyfbAj2ZI6plx1MDAxYSv7Ylx1MDAxN65ou6WfXHUwMDBlReZAd+SDY0s8Kqms2qpcdTAwMWFayWFzsGXdbMqKmtrNdqTMnNVh70iHeL+fL+PS2Geltj9YuNYojlx1MDAxMXr4/PbEs/aydLLO3jya8Lv28TPWti4pedFvTKxIa466bdrsylx1MDAxZDksw47ZTzpjXCI3i6K1aZWTxJab5kTVTopaXlfbXHUwMDExUvauL2STU+ApVO5T5bwv5P5YXHUwMDE3J1x1MDAxZYHHk+VcXNqCqpt8TqprXHUwMDE4ela3rVx1MDAwMFx1MDAxYVxmS0dcdTAwMTnxmeKf127xKTPk8YAsvbNqXHUwMDE3sitcdTAwMGZO7XC4beqXzPNcdTAwMTZLXHUwMDE3wOG+hlxu7DRHm6HfYrZcdTAwMDCHkqp55qQt889W5DCKXHUwMDE3u2ibXHUwMDE5mdtcdTAwMWLxXHUwMDE4NVx1MDAxNcViemkgfZ/vT/vaXCJZTi/tsOPU5DgqhmL7NDhxk9vQYuf0d1H7433oKUNFWcm+NFpK8XRU3aukqdbU7WZ3JGNcdTAwMTfLXHUwMDA3/3SezfxcdTAwMTGO8GLGfGE+j1x1MDAxN9jalVxyXHUwMDBmMJXJU1x1MDAwMTZ6stBqmL1cdTAwMDLAXHUwMDBlWZftXHUwMDBi8zpcdTAwMTKJXHUwMDBl1T3zW6WiLdPCvkrtfCb0dqu9ri/YwqktqZviRS01YVhcdTAwMThzujrEtu+FXHUwMDExmPtyS9FxXHUwMDFhNOPecmIsi8FUoIq6U2V5NSz5RGiO4vfVxG9+ntuRvEUo7Fx1MDAxN/t0wZz8XHUwMDA0NknLzHydsWs/X++Fvlx1MDAxOFx1MDAwZn1cdTAwMTGfkW45zavr7NpFuFWveVxi460xWXy8l4OoSKbE3abSfm+jblx1MDAwYlx1MDAwNoWxnO6AXHUwMDE0jiExp+GGNTcmxMphv21cdTAwMDBryaxI6Vx1MDAxY5ZwfjUuNa+NfXn1cV5cdTAwMDVcdTAwMTXpNUVMXHUwMDBi6Tybr+yPdpbZmjyqyf22qvVsOFx1MDAwMFx1MDAxYlx1MDAwN/u9rJhZg5MgXHUwMDE3Pbu5hVx1MDAxZLUtXmaNfm/m5Yd4WZ7dsvlxzKOobC6abi9cdTAwMWZFrlx1MDAxZE/anfbVTo7xwIyKWeaoaNHKLFNcdTAwMTFKlSqeXCJcdTAwMTHH3nzBVbto7mWZOFZcdTAwMDfp8kdcdTAwMWLmtmParXi9z0PHiPQ8x9KZoHknzI7+0jXylVk2d3pSJP7is/1GNorbZk0rjt5gXHUwMDFj+msh8DfN7bF28qnWJ00n31xiTtlcco6rvlTmpX3ay1x1MDAxZm2Lbvn1z+1grJjG6NN3XHUwMDFik4M8P1x1MDAxNLvC2oa9vrBcdTAwMDFG1jC23VPvdOz3/WOZwHknXHUwMDAzK1pcdTAwMGIk2+ef9dlOxdWs3NaOXqurr63BlTrzQWdcdTAwMDW9pdC/6Mc+WMl9Z8f9fdm1x0Hv/Fx0Y3Jrrlx1MDAwZWQy6/hdpe0rXHUwMDBm9mJAzTJcdTAwMWK9Xbo8NFx1MDAwMeSD3u1cdTAwMGVi/3i1sTTdeJtcdTAwMWVcdTAwMTdC+1HDP+ndh3uGV70lQIuh2TU8v1a7TOMrOZjSXHUwMDAxXHUwMDFjXG7gXHUwMDBlXHUwMDFiQ7Ot7NWu6K7sT7jqy6o8R3OMOlx1MDAxZKQ8vvdNLeJ41Eo5sGop84CVoTNcdTAwMDWy3lx1MDAxZruks2uPlsWD37DTrdIqs5qfJjh0TleJxeJcdTAwMWHi8ekwjkf9UzJtR/H80OtcZr2PdlbZy2Y74ybtozKO8Gj32Vx1MDAwZVx1MDAwZbJcdTAwMTOevIsnx6VcdTAwMWHYi14/9NtHsolcdTAwMTfdfn940vOj0N+OhJxcdTAwMDSOtt755j5s7NG8h2tOwobN3Fx0JWe9XCI71er4XHUwMDFifXySI3PSUjQ5a6MkaPZcdTAwMTLD7Vxca4vTgtLLous3XHUwMDE0V4aRNj8qVCSr6OF+9KPQXHUwMDE1wsWCXGa1rfnZtu1BtznJQLhcdTAwMTl3ZUrjfoRq597mdFRy4dJcdTAwMDancVx1MDAxOSz8nFxuwWF2XHUwMDFhdSYjbenn7b22OKq1bNDM3X1Qysn20l9cdTAwMTWplG37J6E1MIluuu5xYJUoXHUwMDFmiVx1MDAwN1xy7bbW9mK5+75kzbdcdTAwMDHZXHUwMDE0Sj+5jKHZW6WGkoBTamR23q81TufxktinXlPvRpk8LHDT0455g+2z8HCOXCK0WNJdZs5zRWKTrZfJn/yn2leGKzmvXHI6kjNcdTAwMWR+9J9aTZ2tW3TYijRcdTAwMDbxuVx1MDAxYlx1MDAxZVGOXGLeL47SIEuXdHuWbFHvtZqbVjaIWC1JzIZcdTAwMTlcdK1QJqw3nbVO2yD0bFx1MDAwMbVNcdQ9q7iVrJ1cZvfVzrlVtkIto7E+I3t7qOZcdTAwMTH3bVx1MDAxMYhcdTAwMWS163f1sTPX51x1MDAwN1vXbJspcVx1MDAxOcxtcdrY02s6a+KBtVx1MDAxYV6HRTorjupoajevnWlTgVx1MDAwMlx1MDAxZaWTXab6XHUwMDE1x1x0XHUwMDA2ak6VmZCVcmVm5TIpXHUwMDFiQ9TbjTM990hcdTAwMDN4aEab0UdcZiVNpaZeVflaNCRU++jrJ6bCVLuUu1ZcdTAwMWbxiCG4Lmq4iVxcw7rM3GwraFx1MDAxZGc0MdTabKS7XHUwMDAzrSidsynXVsONbS3k2ty2kklx1keObqx8udYmjdA7jrtLa1x1MDAxNUyXoZzmXHTSd8tW1/CllkWPalx1MDAxYYeHseVITJ94/VPL7XU7O3Xa3NBlvlN8fGyBRnt/nbWRUtMnumA2+qu5hlx1MDAwNLVcdTAwMTm1Y+daXFxzv5dm8S5OS2svrnydXHUwMDA2XHUwMDE3cWp0Z+dYcYWTVmtNV8Ys05ZcdTAwMWKzvVt1XHUwMDBl6eKw11x1MDAwZuA0oeJ5XHUwMDAy1Nxupb7S63dcdTAwMTe+28i2Vn7q0pFsLdRum8/DXHUwMDAzfrPtfK00TuTUPlxyReOzXHUwMDBm0LvrmW1ZjWOtyVrApcdAXCJxXCIoJWZqZqHRfk0y091lXHUwMDFl6XTSsSa2Nlx1MDAwN4D1Wlx1MDAxM41zoSxcdTAwMTYsKlaOtOsqh2TkNSN7P7vm7UsnXGbNJW1cdTAwMWVjy19cdTAwMWSkI1x1MDAwZTSI21x1MDAxYt1cdTAwMWFdTpuiXHUwMDA1zzO9u89cdTAwMTamUHPnbdDcm1Tz9dbZXHUwMDE0lMNgZmZpMVx0pDGF0+vhOE9AXHUwMDEwy93E1JprXHUwMDA0c7VVXG5Zfzomg102UrbjXHUwMDAwXHUwMDE1XHUwMDExmVx1MDAwNsd+lmaJmJm4t2Ct1LSwN1mAQbi7aGI46Vx1MDAwNDJYTiRhKlx1MDAwNFx1MDAwNHeQs0km6rXyZfKUrZb04jKVXHUwMDFlXHUwMDFiQftcXFx1MDAwZbRac+bSSego7mpcdTAwMTFcdTAwMWKC/Ynrx+00kjVsni90/1x0a01VWUbbhdw8+2dcdTAwMDJmhyn0mKQ2wFWERjrXbXFu+4P9iFx1MDAxYl5n4cyd/tCBXXc1kFx1MDAxNyRIzzlo6XIzXHUwMDE2/fli3j0unL0p7IfuZj9MgITBUmtkS51lxXh5mI2aSobGw1x1MDAwMVx1MDAwNdlcdTAwMTlcdTAwMTWh5YVcdTAwMDO1dlqxa8J47E/Y1Nq20GGJ9eu2XGY17jv65XxcXKwmRJ/nk9m0WLalUcOB463E72pyOjRcdTAwMDJEay0jb5vDmts+ti5FiuBmg4WUMtpLRWepzdJdXHUwMDFilFJRaZFGlHFdtzvZw/1sXHUwMDFjgnBcdTAwMTDUcnxcdTAwMWT6x1x1MDAxYb5aetm0LdiSitrUXHUwMDBi7If70lD8ZMnoTDrVmlROkk941NTtenlsXFxcdTAwMTDtXHUwMDE3+NRcdTAwMTiX10lcdTAwMTg6kitcdTAwMDLWLFx1MDAxM6Ank6N72G6hOjb3M22+XHUwMDEyXGZraihcdTAwMGLiJeXRlJNlOlx1MDAwZZqTvsXtQev67OjOreV8wClhnMHzcO1cdTAwMDe1WbJH5nC+XHUwMDE29lx1MDAxNMWj3i4815ZrqE3KI9LW0HCOfbJId8Fcbu+beeJ2u5eAXHUwMDA30KHA3Ua/PV7LuchvQlJcbj1IweE87oZWd3q6UDO1JkGg11LSlPAm2cqu6q9nrN3sJ3G2mlx1MDAwZfpwekHnrLlcdTAwMTFnpdyXayo9Z+5wZETOZ17hwmOmXHUwMDAzWb7OXHUwMDBi9bM221x1MDAxYUdFkk5cdTAwMWJzidfZKT/6Qakt94Zud3vdoVx1MDAwZa6y2WpeidVO9PEojn3bql2k8TBWJ9O+nuLdrMh28+ug2T43O1x1MDAwZbc0qmRcdTAwMDTldjo4WcfFbjzpTG3oi+uWsDLPo91cdTAwMWO2xzuxPJ/kII1cdTAwMTjstJx0ulx1MDAxYfO4tDNSR1x1MDAwMssueYbi1UXYdWit33WUgdBN5rvGRFVCxVx1MDAxZqorXHUwMDA1NfD6XHUwMDE07ze98NSrxCFs6F25ppna1Z8sJPeTXHUwMDFmdppUbah4O/CEfYYmXHUwMDFmY46wMy1cdTAwMDfxXHUwMDE5qTV6XHUwMDFlQ1x1MDAwMPpcdTAwMTla0XN0YLhcdTAwMTUveNBUoCxR592dQlToZbRoXHK3RnktpmiEvWY7n/bCoXnAbnFsbaPd0tkptjFcYlfn+VxuXHUwMDE2ezey+VjPgSY6e3E+73B4nDc68enkUINTXHLKw65cdTAwMDBcdTAwMGVcdTAwMGLfrFx1MDAxZJzaqj2Xglx1MDAxYVCs/pqFXHUwMDFicoFcbmC13M6Vk2TAXHUwMDAxTZCwXCLnmbqx20BbXGKcsEtx2LlK0SluuS2nM6Vcbmh3WWLsXHUwMDA3XHUwMDFmY1x1MDAxZT62eftUyjJmcCA3p5/neuq1XHUwMDA0xdqrW0tUJsbA2uC9RVx1MDAxNtOjXHUwMDE1r+XJhpE9SmvUXHUwMDExStOMNofaXu9cdTAwMWKFyPyocyjl3NRcbmvqXHUwMDBlzHVwXHUwMDE1m9awxpaR22ux5kiYTFx1MDAxOL8ng51naKNcdTAwMWGLN4Jvrzdh0DhcdTAwMGXVcYilXHUwMDExXHUwMDE5b5xin7BVadOePXdWSFx1MDAxZdu6d+rOvFpWWklWXHUwMDFhx2uk4nBSnjLU/jw3biTrQnik4fBzXFxTiFrZmSi5de54vWQ+h+emUbtA141TL7is83xUxLHQ6SWFqmzwXCKP3EY4XHUwMDEzlJ5cdTAwMGVmy+k521x1MDAxN1C5XHUwMDAws50s1p32YmB6vd0w9GfLTnO4282zsXCylGgwamZcdTAwMTFwXHUwMDBiXHUwMDAzqoPu2TPa1lx1MDAwMDSSsKXZrFtISqU1LU+W+3shJdvoc7yVOGWzJbSE/HrqfdQ23N+E60Z0iVx1MDAxNGe2vzRcdTAwMTdrp1x1MDAwZlJB5UHEqIlb7dwwXCLWmqzbaktgyrRW60M96MNslE+vMyNcdTAwMTksYX5cdTAwMDV7ifPxYHVedVvJrpB0wnFcdTAwMTdcdTAwMTKvNZGJ15hfXHUwMDFiRkZcdTAwMWLeTFqrs5E7yZ1cdTAwMDIkLLrol2l5sFx1MDAxN3pX1UxFXG7FL3NvUllcdTAwMDbZdFxcXCL9k/6SjWas+LIpzIzF2cW9icQjnYGRbEZ2REeHaNaTXFx7XHSXXHUwMDBlcL3eXHUwMDA26yu/Rf1cdTAwMWWqLSNnOumGPMTTL7NJXHUwMDBmjkduMaBcdTAwMTE8rdY9XHUwMDFl7St53+tcdTAwMWHKprVIXHUwMDBit3/Q166AzF1n1DDUXHUwMDAzzV1i97VldOlpmIvxZTJayJq/U9TGOHrg/aUsXHUwMDBmOIVcdTAwMTjX80NcdTAwMWWg2ZJ9T1x1MDAxN1x1MDAwNu1We0acfqu3XHUwMDE4ny9Xv5yn5f7CXHUwMDA0/0rEWaxTXHUwMDFl9kvz1Z5uLHRQfMErayDyPEn0aq5vN9a7Q9fIi726XHUwMDEy91TTPW+UZOOQizdcdTAwMTNcdTAwMWbWRWtO5+olW3M8XGLTrU5cdTAwMTbZXHUwMDEyjeREaNv8xm2C9upzvGTM1Y0q+1x1MDAwNZx4rVx1MDAwN32rKHo5t/VcdTAwMDJswo5q1Vx1MDAxMHbKfKqM0umpd3Gsi5aPW/aqXFxcdTAwMTfBvj1PPeXc0tenjuSvxK092o5cdTAwMWK+0oe+2ms5V1x1MDAwMlx1MDAwN9eDcJjaQ89cdTAwMTjMN2U2mVwiKuVmzSq7iX6SL1x1MDAwYn+U9IVmq6nCeDxcdTAwMWZcZlx1MDAxZvTSyq4ws9prXvw5vurJppxcIjPR5ydcdERq0nCT9abfYoKmLk1tiTZ7KHtcdTAwMDNcdTAwMGXMiKxcdTAwMWJGy8f5XHUwMDEwdfN+q5Sk9iVQnPl+qF+DsKfIrN/Ip/g8PCSemdfsdsDfdFx1MDAxYVx0JvL8iyc1zf51TCeGsOVx/GhazV9+sPbBl1x1MDAxOHupNjTmd5fb8HNeaSdHuLRcbjVVxESVjihwxjw6hNbkaG82lu32991cdTAwMTGKlqxcdTAwMWYjb7tcdTAwMTDdWKxZSapyLXpcdTAwMWPuN8nFy1Lk52J5oFx1MDAwM0LBNjcn4908pbq5dlx1MDAwZlx0aFxmNoKristcdTAwMTboLbYjjKgmT53pQlZ8k1Nn8UUr7aKojFq4Y6yY9Tn2vbSx7JSdRHfPS1vfK9GS35Mk4vrbTJONY1x1MDAxOaP+pS0t46HU7HdifNqctlmbrCTSuk6Ga2HR57J0JnKnZmZY6VwiR7D7MWHUd2qpVGupwI3AoVx1MDAxZDeOJ2eKMMtNxThcdTAwMWZLqZlPz9iQU2Ff8lBanK2khfxcdTAwMTBHWPfxkziyN9j7XHUwMDFj91xu864syu6o6Vx1MDAxZVqLjaczXHUwMDE1XHUwMDFhOrZQoq2SdNtayNDtdHaR5ixKZco4SoN4W1x1MDAwYnc7b4OV+bQm+LNseIhaojJcdTAwMWTXXHUwMDA2wGqOnW27srHtnsNq3D5ccoSB0Vx1MDAxMvGpXHUwMDE0rMKa9G3YNuCwmLZkeY14/Kzl6Fx1MDAxOK9cdTAwMDT141x1MDAxYydm1+q2tVx1MDAwMlx1MDAxYp6cSfSjXHUwMDFm98tcYnVcdTAwMTJ5tj6kXHUwMDBl7Xa0XHUwMDFl2Fx1MDAxYkowIHLPT+Oxco1QfprEzUnXYkUr3lxmXHUwMDFhzF13WTw1T7rhXGKzTo+HQ/HEVcZI26FGuj5cdTAwMGLQXHUwMDE0XHUwMDE2yNl2cGD0sv1C4TaiXHUwMDBmzjhoTPu9k0V5bLTQjdm8v6l8ntXl0D3OxiX+7PNcdTAwMTRDrynQVCDTXG5NnKCKL1W5VVx1MDAwZT19oljjWkvzi3VzcIbBTJwn3UQpaiO4bnVcdTAwMTM3a9RqeVx1MDAxNIVccst2zl5rtlqmoy5cdTAwMGaxts5EN3alWKxqgOyK9sg8M1ar8lx1MDAxZEUvvMxP43043iZH3VmlwjCrOV2YX+A5xP68vKalO1x1MDAxZoxzozXuy26CtobQV3orfqk90Fx1MDAwMlo5/ZyjamtdRcnVZTDXT1x1MDAwZjmB1rFdqIWsraflpYt96oRakFx1MDAxNib0XHUwMDE3RdqZcWUqcMFquMGhe1xyNnaPkdpZ7TfoUU52MzuNLmXKikGWhPn6Mj2rw8JcdTAwMTTSbHlcbjrd9dTIh51cdTAwMTW0VSBeXHUwMDE2XHUwMDE3fzNcdEbThVx1MDAxME9cdTAwMTarqHW6lsFpiMLVyHVa3lxmhcBcdTAwMDNcdTAwMTdZ61x1MDAwZudNuTSShtZTur1oPr2cyLCcWkXPzmpKOlx1MDAxZmTeYDY+iopz4baN54VcdTAwMTmIcnQuub1cdTAwMTldKo9L9nmsi35Xxa5gXHUwMDFkLvKXfE0qXHUwMDFjhFZTc8zAycfTc1x1MDAwZlx1MDAwZY/zeWrlg9bsUqo4mWWL486OXHUwMDFjXHUwMDE4dVx1MDAwM+1cdTAwMWNcdTAwMDSWXHUwMDE4YCtdRfmhWW7tdXNG21x1MDAwNz+MvYmV5ntz7E3TvTqnkdpuXHUwMDE4SWnt9FHBR1KcXHUwMDA2NOy2XFxcdTAwMTY18usyNXfOZlx1MDAxYVHlXHUwMDEwXHUwMDBmbFVtX1dzXzDzwaAxzsG5pin72lx1MDAwMMZcdTAwMTCXo76S+4FcdTAwMTWo5XJkhtfI9jRsOc5yybG821apU384KYP151x1MDAxY0+Hc67t2iCcb4Uvuf1eM1iVsjs87NfRwfKceHttqIVcdTAwMTP0ppG+aVx1MDAxZvRks4OtTaNpyIeNXHUwMDAwPNrWV05cdTAwMWFZ/TzflYag4YZcdTAwMDFxQ7+icKJcdTAwMDdu2c+mu+n6XHUwMDFhrGYzS1x1MDAwNOpcdTAwMWOHjbXCXHUwMDFhNcVccsNGX69tjlx1MDAxZGdcdMwl8Fx1MDAwYiOGe92+Ll3YmEx7QToqxjPYOi9nw6az3lx1MDAxNubR2Kza8fWMZzZt0HK8XHUwMDFmZGyK1s2SqMcx59WN3thcdTAwMTfuXHUwMDFh6dbplI9qLaJRTm9cZuvtgzXiMfRVXWqO7NrZeE9cdTAwMDbaQp9t91W+MytDs3zInS5cdTAwMTdV7r8oRq3BQ/zfWnPD97pJ82SNTtqwVmusxFa376S7cJhQOFAkynF20VY9eXReZ84121+XhVa4vXhR4OPeOeCg73ZxuFx1MDAxMmub8UT0R1spXGaPXFykrlx1MDAxYlxcOiZZmlx1MDAxZXZcdTAwMTPN6Vx1MDAxNlx1MDAxNzTwjEVvO+tsrlDvSLWGbFCad1x1MDAwN1x1MDAxOJ25xie7KTGd2Zws01UhuD7Xmy7XNLvVKWhken7VKGr27NxxXHUwMDBmlON4XFxwW2qiidfhQlx1MDAxMazg1fZccrv8PKZoy327k5fdXHUwMDA3XHUwMDBlZGXb7yVardbudfr9XHUwMDAxKknHlVx1MDAxZLFjXHUwMDAweXxcdTAwMTllTr7MJ6x5iHDYNWhvOlDPzaPZulxctlx1MDAxYsfNhUVhpurOhTxePk6kw/miXHUwMDBm7eN8MYhHZyfrXHKLbj7zhKPa8DtcdTAwMDMwY8msdjlcIjaWR4LCXGaiXHLJMJVVML16etRcdTAwMWUu8rjdcLOMeoRcdTAwMTApXaWtxXw1ObauSZFm3V2U4fGMx6HaoqtPxlx1MDAxNoHtxmBwXHUwMDFh9c9cdTAwMTF2jZWDgCfHnkBiXHUwMDFhrG3l2Knms1xy1sFDbrOjSpUuuHYhUlx1MDAxZnhNXHUwMDA1yviwlXVxNk8sXGLjxFpKUjmzXHUwMDE2rFx1MDAwNyOqjia7k3ZFtNdCrKbHh1ZcdTAwMDSm49DtpP5cXHdcdTAwMGb5yV6vVmZtVWyyY9Gc6qUwxadcdTAwMWVuXHUwMDFkwvXIOWvOXHUwMDFlnFx1MDAxYihcdTAwMWHbXHUwMDBikdUykc+1uaNaxG1tXHUwMDAwYsfdJLDTLZds0Z3O4pyZtZlWsq3B9uZ6m9vTndNfNJuK1mbzfGpcdTAwMGWNvDX05M10Ojvaaj6Bc7MmoGSlLy6pPN748rLU/N6RduJkos6G00juiNPxXG53XHUwMDFm4uqmwH24tlx1MDAxNPuj84NfO1xuY+4lXHUwMDE0X+tcdTAwMWO1llx1MDAwNlxcwlak01x1MDAxZKSLebkvsmxf4lxcafu1+FxcXHUwMDBix1x1MDAxZNzEY9abXHUwMDA3XHUwMDAxam41K1x1MDAxOLKaXHUwMDEztfvGfNvFxSGdXHUwMDBlXHUwMDBiI89cdTAwMGZcdTAwMTd9olx1MDAxYbhWnFx1MDAxOULaVIsysdZcdTAwMTD8vUThtN2DcSOSVmvhXHUwMDFhumg56JCZpdNcdTAwMTmX8thatJPenE10zoV+r1Fu9vHm6KyVeW86da+piC8nbUn4/HhcdTAwMGKWZ1DuX6pcdTAwMWHBRdHbz7BuXHK1RSsyXHUwMDFhLbSecrOaOvm8jSVcdTAwMGYsV1LaXHUwMDE407A/gLVcdTAwMDFxXHRpRvH+KjrTfTOVr1xcc2XLcj6wUfNY6enrur3XhMlDXiur8lr9M6b76EGrSdFKKGTF2ap2m4U7aqonmFx1MDAwNlm0y9Bhu0CusWb23Fx1MDAwNp7fXHUwMDFilqHm7mRxqvBpcuJeXHKCVuJcdTAwMGU0uLLm3H+vwHlmXjvnYO10pa1cdTAwMWJcYmUh11wiv1fULtdMwYyrXHUwMDE4Mlx1MDAwZlx1MDAxZN3Pj0XZXHUwMDA1XHUwMDEwtu3gND5uPMk3My3XXHUwMDBlp5VgXHUwMDFhXHUwMDA0eafBYI9Kpybw2Go72K25ftlxXHUwMDE0bzdiOl/UrI5suumuIFx1MDAxYj9SxbNYMnpmbVlcdTAwMTNcdTAwMWORtCY8oqAg3E+ibe0qWjHI/SluiWLN19qmvDKsdrlsLE/7XHUwMDAx2TC4oWXYiNJVXpxcdDI0vW+ZmiuWdlfDrWtQXHUwMDFilzPFP7Qr/d9cdTAwMTKMa+8hbjK6yjTDpynZL2v25sHuVsPFXHUwMDFjK5qSK33VXHUwMDEya+OatYcraTSc5pN5+1xuIUSaJPErafZl81x1MDAxOIxXtrfLtOvpKobRIW54q/EsS/N5mFx1MDAwN7PZtlRcdTAwMWPx3OyvoDYsXHUwMDFjP8LUcFx1MDAxNzwq8Ol8XHUwMDA2wCUu98vDeNhsK33jOkJsm1qLaceZKHxcdTAwMTBHXHUwMDAzXiFcdTAwMWL6ub7VtHyt57aHXHUwMDFhXHUwMDE0rmmjsWyKQWNZaYl2OKrVzrVm0SCkb2RcIknEst8+Jz0507bypFx1MDAxZE8m7S3pJ2aW57PF2Fx1MDAxZZ+mNXHVbOtHLWYzTcSbedpcdTAwMWTigY+6U9Q/hUp5iUDD5KbXXHUwMDFitrnpaaZ6Vbqx9pA7cT/mTmp4QdvR5zVtZVx1MDAxY9nItVueKfrj2iQyiChcdTAwMWNPZXtcdTAwMWOf00uebIFcYlx1MDAwZmqtnNS6e1x1MDAxNLFcdTAwMTWnpVp+wPl45c9cdTAwMDCWVFPb1dIh9Pb6UFx1MDAxZZXjdHXwZlx1MDAwN7E96zXQjpSrnclEPE1bbbTola5cdTAwMWaGPtpzjlxcbyfKMlx1MDAxOMWtgVFyx3zF7Xjar42LWI5TeFq46/W8dUostWz1dXvUXHUwMDA1y3mzhvesdzn3855ypK6zqV2SWfdcdTAwMWM1pfl+kFx1MDAwZsvmpMpcdTAwMGL4PWXrqZ/H1lL8KFx1MDAxY4fltN//7Ku43cyqNVE46SzNOZ5SozOVJolXdFx1MDAxOUjN4uBu1utjY1x1MDAxM7nxqVifubBCY1xcO2ip5sPZXGYj1bF2fXXp7VxuXHUwMDFlKlx1MDAwMzBqXHUwMDE1NeLa8q5WiKNcdDrJXHUwMDFh2+OxrFx1MDAxZpxlI1I5efu5XCLHhsFpYM2cpO/q+VpcdTAwMTCvKfGDQpPWw9qaIG63vfk0XHUwMDA3sd6PeGxcdTAwMTlLgFx1MDAxYSWP09oyx+2xPWjbXHUwMDBmWsJfXGarnEI/RdR8mD95XFxprv1slS/02lx1MDAwMa+FwvKaXHUwMDFixoBcdTAwMDYwJm67Z1x1MDAxNFxyWKygkVx1MDAxYVxy3C7ydFx1MDAxZpQq9LTIXHKNiPuGlZClYU9cdTAwMDZcdTAwMWJ3uFFcdTAwMTaRcSpO8pz7wzjrr8Xx0rpAOG+G1831NMbrXCLdOSz288uCc8IuT6hezi5cdTAwMWRcdTAwMWWL9LeFPZDLXHUwMDA37q9V+tfqnS7r3uhz3qi7qrhcdTAwMDNcdTAwMWOAkZFcdTAwMTEu9EVPNVx1MDAxNiVqyD3WaCxmadKZNTyy7MejU3k9jFx1MDAwZaZcbrODfrClVmcjW6AodjaZYrZcXHl9gyycqSktxJBHU0v56oVq3lxyXHUwMDA3zFx1MDAxMt3u0kliM+dcdTAwMDLCtoK8R1krcvjFtLW+uu5cdTAwMGXs6EGv9Kr7ljmGuVx1MDAxMM1PuaTJpVx0ZDlexvN+nFtcdTAwMDGK12jd6e7DctWIN47TXHUwMDFiz0WHx3xJp4uHu+N+5HKH6K3hYFxyw0tztlx1MDAwNvOJTVwivWxkvn3eydllNtTKlrlOsvO131x1MDAxZCA3ySxwRVHWlZEjctdcdTAwMWKOr1x1MDAxOZLRVGU4XG59cXXk+noxk1x1MDAxYduE459zqrK0+/bqUaywqeJnf3qWplx1MDAwZjpD3svRJeoy3XFcdTAwMTlrtlx1MDAxM6lcdTAwMWZPXHUwMDBlZ4FN56jX5FNcdTAwMGWd3iRcIqq/94ZBXHUwMDEx7ENcdTAwMTmwfHeamVx1MDAxOVx1MDAxYuYnXHUwMDE4Ss6Z65mgh+TCWseqt/BcdTAwMDZnKVxix1x1MDAwYmePnVxmXHUwMDFl4Tlpd1xygWLWO1x1MDAxN1ruXHUwMDBio+SUIC70i6BcdTAwMDXLRFx1MDAxOaeTSvtom7l/0qNP+ZroVN3b2nhcdTAwMWFcdTAwMWGq8KlcdTAwMDZiOKhcdTAwMWW7Ls9zdXRmg02N6/dCXHUwMDE1XHUwMDEwJbrV2zfStjpcdTAwMDL7XHUwMDA2mo7HJJtcdTAwMGb4/U4uS8+25I06VkeR5KjrXaRcdTAwMGKooS2jXHK1y/552YWG07X9Tc23dOeIt85cdTAwMGWkIPRCb7Sp1WpcdTAwMTGqXHUwMDE5l9pqs0i7x1pPXHUwMDE3W03L3CS9fmDro83uSpw8TMF1flx1MDAxZJxMcSn3zv2+hnuyXHUwMDE1TVx1MDAxNnbfXHUwMDFmxbVg/zEn1jTdXG6A4+Gp+XDPtaLKWfRgt99pRYWlZurs3Fx1MDAwYstwXHUwMDE3XHUwMDFi0eBgStcjdbjrNeypMyDiOW6sLdFcdTAwMGY3XjqdOlx1MDAxY35Hgc7sY3iOsqRjKpNcdTAwMDNcZot5Oz2Pr6CTXpCkQCedXHUwMDEyf5JcdTAwMWT5W5ZXKF3yXHUwMDEyS1wiXFxcdTAwMDQz09xuuq7B9aM8Us1dXHSuXHUwMDBi2+TxXHUwMDFmYMyJsJThXHT/Us2d2YtxXHUwMDE52Fd7XHUwMDE0zaR22lx1MDAwM5SthG6i5+jYXHUwMDE4rPTuWq/mbG5urvGXeiShqlx1MDAwMzrQeTN+iN3M3DzKclx1MDAxMc3opNhRwd3HqCPA+c5UXHUwMDEyNFx1MDAxYY9XU2Hoh0VrXHUwMDEwprXzXHUwMDFhgaHTXHUwMDFlpl3JXHTCaK4kmqNcdTAwMDbyJu2vk6iEc0PQjSw+ufGawcSZma5lXHUwMDE4IZNcdTAwMTQz8ENb4nKt1rtcdTAwMWVcdTAwMDSSuKi24uZw9WQ4h9jk1MCJeXs97Fx1MDAwN9PLapJuOktd3eRzv9vnsVW+Kc5cIjivMSFo3svPlyanUZxYqFx1MDAxNsXR8bC1XHUwMDBlY81rXHSdvenNqVk0OYZcXNvqJLr2UFx1MDAwM7Y/L1wiuSmQsnyoq1wilVxyaP5V21xcWlxuOs4sT1VssjRcdTAwMDbK8tBvJ2NbYVx1MDAwMmitu62QXtJ8OUmlhVf45cJWQ3epXHUwMDBl3WV/jVjXXHUwMDFh08BtdNJ1XHUwMDAwp+bB2tHMXHUwMDFjQa1cdTAwMTRqbGVcdTAwMDFD23XDjlHWpFGXXHUwMDBlXHUwMDFhy3O+ZJNeXHUwMDFhXFxd96RZq0iqxZHnsfZcdTAwMTL3XHUwMDFjsVx1MDAwNVx1MDAwZZmzs0+0WYIqV+JcdTAwMTh6qGarZiS3hNHFUUv8sN5M54ms9Pr0S31FXHUwMDFm6a2mXGa5n93mapmb1PBcdTAwMWIk4lx1MDAxZfd6XHUwMDFkrqbHUbJZXHUwMDA2Rr6GTdvz+upq3bY3wmDsnVxucSWrgpLLs5OI/OlulJ9Jut90ZzDt4UO+Xl+HZlu/XkWJ0fDa1FiLLS9ccq/B42ZcdTAwMWGONuzSnKSH/ux6SYVp0Vx1MDAxMlk7n205LuS1eIVcXNZcdTAwMGVJW1eJJqK+eJbm08ZcdTAwMTmtNG1cdTAwMWIkp1xykVRtZ7cqX6iN7EI+6lB1Zbl3yLvUOj9omWHSXHUwMDE04uN8vJHNh8emh3GlXHUwMDEzu9DOL1xcMi+AJpSTQUlRI+w1ho1x5Fx1MDAwZe1BrUpviKNpQ5FcdTAwMWFcdTAwMWVU12vXc9rHwcJWej2bKu2Ocu1cYmJcdTAwMTN7gXdVZ9YunlxiylI/bZaoOEOucU8nZHE/ftTcjNSOkjnh4dPMcWK8I57T6q2DxbjAcSo2Ro6YpjCtXHUwMDExacI/zCf6ujnZaMP5YjxcdTAwMTFcdTAwMGVybb1fXHJWXCKfR7uaR/VcdTAwMTLoNm5/msdkW/lcdTAwMTcq4OizXHUwMDBmknQ+yfNRy7so/Gp2S9WXcGM89fw+6azbQ1xyR6uRNjqyXHUwMDEwT3AtTVxc9+jpuljTR+rI84tptjpcdTAwMGXajsGx0N3Ks5XTi1x1MDAwNy7rXTfgaIjTeU3ajkqpXHUwMDE3zniYNl+Odt7xtIHMzD2xbe3kxLhs2kTQuCRcdTAwMDDl1V2QxLrQLU63WVpk5dLS1Ybhi1x1MDAxMo0hW+/NQ+nCMrywa+iHUaPfL+ho1c8yoUi0ayTjRCzcvjiQXHKb64OecNbOpf6QXHUwMDFmkz+u7cy+rO1cXCzKtUXSj6lcZlx1MDAxYrIsc6jumvbUlkbW0uvqujdJxpmV0fb1ej0n/WRjKbOTmWiTnmLPdzPNM2us8lx1MDAxMXBcdTAwMThcdTAwMDOH68WWXHI2s4zzZTJKl2sht+hgP7mMVzVpgHprYYJcdTAwMTPdlIhx4e5wftzvcVIs3ahcdTAwMTa0s4yGRVDD2jyDJHFGQlNBg6ZMltxALq3ApWx9alxyrsdWRq7luH+wL+HCu5502ojkwFx1MDAxOPRV5+ityXjtnVhjMdRM2c1cdTAwMTf5NfKHbe5cXPiYr5jWrlx1MDAxZnO23I7Hi9XHXFyK9ziX0k20/kLGQa+kXHUwMDE2XHUwMDBmerX0yLlPXGIvkjacXHRCSpSxOiwvimGgXHUwMDAxXHJcdTAwMDI73XOm6cQzcoLlpbVccltcdTAwMDWPXHUwMDA3Vmbe37mNYKJdwtVBb26m7aI7dufTrUzd1Uoq1T5G7GxcYiQ/XHUwMDFhy2On2VfOM9t1fPVcdTAwMTCx+YqqXHUwMDExv1x1MDAwMpaM4/i4XHUwMDE0d2JNnqnn5WxcdTAwMDRcdTAwMDb+2dmSXt7oNq4n6DF7om+8iW70ZC3bXHUwMDAwiVx1MDAwYtu2QiqbLqBf9HlcdTAwMTR4j9k+lzN8LvqNMrflTzg2VOVeT6iu4lx1MDAwNINSOK1iz9/rh2sgwI6idVx1MDAwNqdGK9vk2Sk/i6vGYFhxYrymMLr0YZySbqJcdTAwMDScyVx1MDAwNvGJy49NjMu2a7JwPl3aeEfj/Vx1MDAxY4ahslx1MDAxZvDYgqsyzdTtY2wtXHUwMDFkf7vawOYsXlx1MDAwZYmdSbPpzml6XHUwMDEz2th4p0bYWl7WnjVJXHUwMDBlw746gPFcdTAwMDVcdTAwMTMtXHUwMDFmh1xybXaaieaGrLgmwVxmnlx1MDAwNlpbXHUwMDA3u0uFUXfb6DdcdTAwMWY0+9zm/leNTTpnj3RcXFxcxWFcdTAwMTMx75WkykcsYoOAeLaX2lx1MDAxOeDys182XHUwMDE2ckFZbzHRlWBeWsPuVLRcdTAwMWIpkSdRM1x1MDAxY3tcdTAwMDPqrlx1MDAxYlx1MDAxYdcrud08jeW8056CI1xcXHUwMDFlenm7zIbDabHRxUlcYkvJYVGyabrBplx1MDAxN1x1MDAxYV5sTS542lx1MDAxOa+pPFx1MDAwMOfsPlx1MDAxNrOJdsrHnlNrKFx1MDAwNLSuSFx1MDAxNaczV9tT6I/Evlx1MDAxMo728n5cdTAwMWTPMvG6K1x1MDAxMzpcYpq+a51cdTAwMThL14dsN135076r9FxmvePNXHUwMDExxpP5on9ZrM5pYfuCmyfg/2frPbZcdTAwMWRVlrXRXHUwMDA3ooF3TTxCWOHp4a3w/ukvs9za/1x1MDAxZKehjmqUJsqM/ExkRFxiKKyixpO8XHUwMDFm1Fx1MDAxYXJE75NcdTAwMTDH1ddcdTAwMTD2XHUwMDFk1S989eWjxVx1MDAxZuAjUpEhlH+5pFx1MDAwN9jxOVx1MDAxZPC3xvy7I3j9eFneOVx1MDAxNfvtdVx1MDAxZJXs8VwiOVx1MDAxNVx1MDAwNcLI41xyrUSeunFcdTAwMWJtXG5cdTAwMWLt+17SjL5cdGZqXHUwMDAyb1p83r9DXHSSlnngQSBkXHUwMDE1Q24gzlk29+zNdtUwXGbAVDU1lixDTJ3YJuj4pnhVOlx1MDAxNtgyXGZcdTAwMDUk2tl+m+prikvtWTTmv0HcWH2DxG5Ew2iL7kPP60TnyvlH6mpcdTAwMTiLXFykIWFcdTAwMDTLsOXPeTVcdTAwMTlswv7leNZcdTAwMWb/oyNcYtpcdTAwMWP/at9DQfv13XZcdTAwMGVoXHUwMDAzlMVHWE0zXHLxctNcdTAwMDaKO4BcdTAwMDBcdTAwMWalKFx1MDAxM6Dpw0fQXHUwMDAw60/pdcJPLFxcYPnojTR42cf4+L2fnLpQu6nuXHUwMDEzXHUwMDE5bW9cdTAwMWHeqaSRoPehO0lcdTAwMTLWXHUwMDFivEnW94tfzcDVr1x1MDAxZCQ7azhBvJG9snY/XHJlzrT9ylx1MDAxYsBsNVx1MDAxMHhcdTAwMTkgeTPSNFx1MDAxMXdcdTAwMTFXg8ZmXFxSo0rKqZVYMlx1MDAwNmLXOXP8i+VHcjyeVN/+1jfYtZX/qlx1MDAxOei10rNA+/BmkyVFKEy106fu4otcdTAwMGZcdTAwMWIgQe5adY9GXGJcdTAwMDHwRU7Vm1x1MDAwMFJohapK09Coejzi4nLYpC7hj1x1MDAwNpTCg0qQmVx0XHUwMDFm8T77mVx1MDAwZcMu3p2PXHUwMDFlPsFakZH7eMzUl3i7097ropTGj8JcdTAwMWaDYD/kzFx1MDAwNl/KXHUwMDE3wkYmeD1+fq+PQnZcdTAwMDRWscMuXmSFT5xnXHL5vLvftHO8fudcdTAwMGZcZq5Nhf+89fiDw9nx+n9xWP/B4YJcdTAwMDDi4MRQOar85+F1wKLEl98pXHUwMDBmPMdcdTAwMGaFUPo54oToyvB1OFx1MDAwM5pYV9lcdTAwMWVcImT5QjXuIMZAXHUwMDE4llbVXHUwMDE0XHUwMDEzs89F38xcXPE+XHUwMDAwmVx1MDAwMDRcdTAwMDOpgNSvqev6XVx0L9aUnVx1MDAwNjbP6jRMy1xi3ng86Fx1MDAxNeho3fQzXGZYUEOYJj5cdTAwMGaFV5RZ8Vx1MDAxOdY/1HJ7eVx1MDAwMfzGUX1cdTAwMTChg6ugv7kxJ3zkPFx1MDAxZutY9F9O4fi5nzDCg7Cx2C088MZN4cTI5St15WjE+lx1MDAxN84hQFx1MDAwM2lKlnj2WDBIqpFvOq5cdTAwMWXyXHUwMDA0YMa/mE5WMDC5I8NcdTAwMDTwR1xcXHUwMDFm4EpI7yB//u1UNlx1MDAxMMx4w/xq5vVKp3aHU1x1MDAwMVx1MDAwN/3N4jlcdTAwMGZChfe0XHUwMDFmoNDQVLZrj9lM78qD11dcdTAwMDBqLmou4bZcdTAwMDGIss1jzK78+IC/+Wil5EcrbUAuXHUwMDBmhPa3Nn398cRcdTAwMTZEgSqO/dGBR/3zXlx1MDAxZHbWY1x1MDAwYljGT1xmgHV72XG16Vx1MDAxYVx1MDAwM110XGZ9d1x1MDAxMNPVXHSAJszvOFuffifx/lxySC6r6seZqHZcdTAwMTV6iSvhOH2qKklcdTAwMTOzXHUwMDBlXHUwMDAzLUPWp5qDlHNLfP8wseMlTVmiu0UhU39cdTAwMDM8P2EqelCks3s2u/pxXHUwMDE0d+J7mkaQRK9cdTAwMDd1QOrwxMJLTDZcdTAwMGYtXHUwMDBmUkdqNPlUfCCWYdQpSuUj/ItcdTAwMGKD6f7wXHUwMDA2ujfy/9xcdTAwMWJV71x1MDAxZn3rP5zzinBcdTAwMTJvxDRcdTAwMDU6nI+lXHUwMDBlXCLBXHUwMDAwXHUwMDA3i+ancvggRLlpXHUwMDE1+Hn+t7RcIohcdTAwMTayxiPkr1xiVGeo3I1DWVx1MDAxY4WoXHUwMDE4pOtcdTAwMDbI7G/3XHUwMDAyqDpcdTAwMTdkOXzCsZqHqjlobd1a2V1H3/pcdTAwMWGSXHUwMDFh9mNPkqMzSapAXHUwMDBlslxmLtdjY3ZAaCSB7c9lot85u/34e3LEimb4s1x1MDAwZs5cdTAwMDT9zlxyoPQul3/5Xfl5b6wjTtqbWG+xWHdfYj2/tcqHXHUwMDEzPoeSR6CDXHUwMDAyjFx1MDAxM+mtLMbZbVsvNlx1MDAwZiakovVNfuXZhnBWqbzhwePBfsqsg0orUkipXHUwMDE1XHUwMDFhS+qXYcpWXHUwMDFij0v0kGOovpasqa6TRUmM3PaCzfgm6DHzJz+zgeJ4NOJnMNufXFxpklCZ/ID1Xy9cdTAwMTFcdTAwMWRcblpcZklwfVxu8999XHUwMDFlXHUwMDEzpYr24vPz9XnB8W3iVFx1MDAwZU1cdTAwMTld05pIzonRV1x1MDAxYXrR6bZtllx1MDAxZlx1MDAwNKjWPP5w2d4+J1x1MDAwMXHCXHUwMDA0n0vc1NPgJqLb5OdMk1xcXHUwMDE2ZUVrzGcvXVx0ljivzSyq6JanTdStXHUwMDFkXHUwMDE38jvaYlx1MDAwN/Y63uVcdTAwMDFcdTAwMTCo+Fxu4IJcdTAwMWXFX4XHVHsswnA4N1WWXHUwMDAyR0eS5FDE32r2+q9JNzaEoSanYZ9yaFx1MDAxZvBigFI7kpL8XHUwMDE3X/9XnY1ehz9742RHXHUwMDAyzVx1MDAwNjqrh+vB8HNyQ1x1MDAxYYj4XHUwMDE4gjvL2bGUY5KTrFx1MDAwYlx1MDAwN3rrfVx1MDAwMnvZdcxcdTAwMDPePFx1MDAxMlub2+ORLlx1MDAxMk1cdTAwMDN00PFfXFyHXHUwMDEwOG5zOFTKl8la/Vxykl6K4bjVLMHoRa5cdTAwMDYmSPo5tlx1MDAxZMyawjB3yv5o7yqqYe6tXCKtXHUwMDA3+9updTKfXHUwMDE2qZFQsCq8XHUwMDA2XHUwMDFhgtnwI+a5XHQ1iVrQ91TvM6DVldFcdTAwMTmymTelvvycXHUwMDFislxiQ8z8UyP5QMWzv1xmSVx1MDAxOc5cctW/e1wiXHUwMDBl/lx1MDAwN+9cdTAwMTn0XHS66Vx1MDAwZZPKXG5gnVx1MDAwZjEgY0Hte29z+Fx1MDAxY/2I9+cqklx1MDAxYYfiXHUwMDFmTVx1MDAwMYzvbs92VeCMpiXXlmfKJ7Swd8nTVFx1MDAwNHmxlFx1MDAwNpxjfTOjcVx1MDAxZWFcdTAwMGaSTf/Kar8rJPfqhXeNzVx1MDAwN5GmXHUwMDA1Nlx1MDAwMP1jTFx0pFx1MDAxYbywXXtx7op1rSbp8S/oI0uRZHlcdTAwMGWH8Hr8oVZcdTAwMTUt82Ce+9vn59Lxz1x1MDAxZo4v5nHV/1tn7j++n1x1MDAxNqLvS5FeXHUwMDAzalx1MDAwNFrzfKBkYPT8Ur1cdTAwMTc7+JFS2vNcIjGkXHUwMDFhom/T3F9vvrnLd5b4sIRcXH1r7EqWko8xm1dHqLxcdTAwMWPt8HdMkNj3reW5XHK+a87nz81OIHiljTvjXHUwMDAxWvvGYzxfXHUwMDBlaO7Q+rIhYFx1MDAxYoNq70FQXCKzrFx1MDAwMPv9PbemXHUwMDFjtFx1MDAxMNn3Q+x0+kG8X8RcdGSq09g0XHUwMDEw51x1MDAwZlx1MDAxNug/XHUwMDFjXHUwMDA1Q49cdTAwMDeEXv/d32C/clx1MDAxOMr/m8Ng94d0U73abWZTbY5cdTAwMDJcdTAwMTmCXHUwMDAwKEhlX73naHbD4LHJXHUwMDE4XHUwMDEyb1x1MDAwMllgb5PAUOKCYIGrJ05cdTAwMDPxhFx1MDAxYTyPXHUwMDAw7oRDXHUwMDAzXHKhXHUwMDE5SL/wcL7EfVWiQqpcdTAwMTlGeVepjMZcdTAwMTQxXHUwMDEzJrDF03hcdTAwMDHY+3AoXSZcdTAwMTNIjtOoVj6CXHUwMDBlXHUwMDEy7nO2yz5Ag9fd1cBcXK7FjObML1x1MDAwZVIjKjLZ9NGKzsOyo9ryh/ZPw5q/72NcdTAwMTJqaP5hjPPyQoajXHUwMDE5R/bsSTo4XHUwMDFlXHUwMDA0XHJcdTAwMDbqm5Ngv9tcdTAwMDBP3zdM+VxuXFyQMyR1J0pcdTAwMDBwuD9rXmX6hlZcdTAwMTUz31x1MDAwYnl5ZCYtXHUwMDE4QVmPXHUwMDFlXr7yVFx1MDAwMpCnlmTi1JKMXHUwMDFlglxc7eRPWezrU0v9NFx1MDAxOVx1MDAwZldpNuRRriec5thSXFxAYYDbXGJDJXtUq1x1MDAxYkqR6bHfRv1DZ05+QVx1MDAwNIC9KveVJCpVRVxiw5B51Fx1MDAwYksmlSH0k1P/zq7wrzdv/unN0zODtzLpT71ZZj8hSzPh17+mlKFcdTAwMDTONFx1MDAxM8YsXHUwMDFhsvFSZFx1MDAxNZWDWWZ76V+UlmMwqc69PNVzXHUwMDE3sbakI+TDW3mGXHUwMDEw9lx1MDAxZU0qlVx1MDAxMclcZkipi1x1MDAwMVxcUpW6XHUwMDE5XHUwMDAxnVx1MDAxMubCPVx1MDAxNyBwSmmxx49cdTAwMTfHXHUwMDE3PTlz9Fxug6/iXHUwMDFjSF7cr49oYq/P57jldSOo+NguyTW3sv00N/jehjlG5MHdhj2XdCb5kD2UppLlXCI/ec9vk8p/9+9Q/+jD+3dp5K/eRo5hXHUwMDFmXk2+0jXWXHUwMDAy1FxuTfNdZOhcZsHJjEtgXGZ715/1tblkXHUwMDFjo1xu13S91SHA+fBDKPu/fJxnXCLLl8xcdTAwMTTPXHUwMDA3uVVcdTAwMTR9Qlx1MDAwYrxcdTAwMGZY8N2McLX2WVx0dsWzK2dnpNt2XHUwMDA1uHw5XHUwMDFm35r4YVx1MDAwMUJ+0Fx1MDAxZsD6nG5xmsBsSJOdUPvxcvSKhz62wzP8hi/6QY1cYqVGUjfPfd1cdTAwMWTYqn5yV8RcdTAwMDZ2XHUwMDA04GdcdTAwMWLAVZpN1yAgk1x1MDAwM/m+PnX/XHUwMDA2uXxcdTAwMWVxXHUwMDEzLcHL7ldF/Nj+lO79tIPHR6BiPX7ijORSdpVPhsquuSV5leyXWlx1MDAxOEVE/aygRFx1MDAxZMzW9491p8UyOt4/9zBvhzb/1cTqtfuDXHUwMDAxaEX980kos1x1MDAxY6WI73z9cWlz4Vx1MDAwMfN9v53qsUYsvD8nu0WLx1x1MDAwZrVpQU3R9Fx1MDAwNmZcdTAwMDQp5yo7TTJcdTAwMWI+OEcwzrOpb26YrFaqKjVRusqZXHUwMDEzUFx1MDAxMCDVZSRVNYu9eVxcKFxyilx1MDAxMFx1MDAwN4KLgFx1MDAxYSbHXHUwMDA0XHUwMDFl47FN5CdcdTAwMWMpXHUwMDA3MzSNglplfKh8j+3N7bzD32B5vlxyaUiIbZ1cdTAwMDSvuk0zSGbkiVx1MDAwYlx1MDAxNMXkz4lT3Fx1MDAwZdNy4cEuXHUwMDFkXoiLSFx1MDAxMFxm6KxcdTAwMTVcdTAwMGLyI/cvXHUwMDBlLbKsgfPikWmZpUxcbm1cdTAwMDE0XHUwMDA0XHUwMDA0XHUwMDE4OCYk9Fx1MDAxYVx1MDAxNFx1MDAxZel0XHUwMDEw8r+PvjNmUy7sptR+++NEYFxi+a8/XsSYYVx1MDAxY2dny//3LmK+00euyfHkaaPaNLOnXHUwMDFlROifvO5vV3eB1lx1MDAxNr9cdTAwMTJ0hC4x9bsoXHUwMDFla71cdTAwMDKQgFx0rLDr/GAog7Mh5Fx1MDAxNabBzVx1MDAxN859XHUwMDBmdFh7M0VNXHUwMDAxfXFU8HTmcFpSfohQXHUwMDAw/MiMSZl1ymmoXHUwMDEwkjb99cVOXHUwMDEwNZmrQGnQciZj3Fwi2Zfsx1xufVx1MDAxYfBmqzTPhFx1MDAwMi6/5vjx5VV6f1x1MDAxZaH1qVx1MDAwMJ6xMJJL+sWwknwzM1x1MDAwYp+m3Vx1MDAwZs3H21x1MDAxN025O8V5V9tcdTAwMWXuMpdb8I+9OoORMimv1HKAvlx1MDAxOVx1MDAxNstcdTAwMDBijDORoXC1TpBcdTAwMDdcdTAwMGb125tBflx1MDAxMFx1MDAwM655QmpcdTAwMTJb6oGu8Fx1MDAwZtZGr1+5ReX/l1vcViG1XHJ+oIL6LPTWfrRty3j1lVxuUlx1MDAxM8yENPVcdTAwMThccmDKY8qhNbtcdTAwMTi/XHUwMDA2XHUwMDEyXHUwMDFkwbv00ZHnY4PpXHUwMDFjL1x1MDAxYaJdNlx1MDAxN0QgXHUwMDFm3Vx1MDAxMmBd31wia3lR4bDlIy3F0/KxSX4wqlx1MDAwMPJ094hcdTAwMTKZPFx1MDAwZVdcclx1MDAxZMHQMNb2pG4903/OeFx1MDAxONeulbZcdTAwMDFcdTAwMTO2blx1MDAxNVx1MDAwNlFU5eKLXYhwevTjemlcdTAwMDRJhq18QieDX3gmvHZcdTAwMTGaJv+nNlx1MDAwN+lTpYFcdTAwMDDWq9iS0aH3xFx1MDAxZu1/9ZW/eVx1MDAwN6Pq5r88+fDLXHUwMDFmtVx1MDAxMSHUletFQdOWVnZAtPvTQFx1MDAwNWefvKfOXG6jXHUwMDE3XHUwMDBmXYxcdTAwMTSwYDHwiJVgtlx1MDAwN4uWhdna3EzXXHUwMDEzOX7Gh/E0Opmsb22tnlRjqYzqquZcdTAwMWZ9XHUwMDE47lxiyFx1MDAxMFx1MDAwYl12O5RcdTAwMDdcdTAwMDd1XHUwMDAyqrRcdTAwMThcdTAwMDBcZjCjXHUwMDFjfFGyXHUwMDAy64rCbO1GiFx1MDAxYXtZpizwOVxi0lBcZoReZDZVY8/5U1x1MDAwZed7XHUwMDFliIZxXHUwMDFkq7RcdTAwMTdWZ9P25flqXHUwMDEzXHUwMDExXHUwMDEwhFx1MDAxOW0gM+hcdMJcdTAwMTaaXHUwMDBiJXR+sKVCUveEapa2rNAyXHL/3z3PXFz5PzoqJnKJ+c87tj95OD3rptBcdTAwMTmzXG7/vrDsdppF7GVcdTAwMTQ8Nnot70uQMV3sXHUwMDAzTC1IOFeJJvBuOl1pWNhcdTAwMWWNlu/peKJcdTAwMWYvwGJcdTAwMTnciN0zXHUwMDAyXCL0ou5kOIRP91FcdTAwMTLP0MPC1TSXmFx1MDAwNslB7yFcdTAwMWMocPS0yybLWZHjYli/wDFDdISOXHUwMDEwkFx1MDAwMlx1MDAxMmVO09dcdTAwMThcdTAwMTFfXHSm35J1XHUwMDEyXHS5hIzRQSPZpqx4tEcpJ59IZob/cow/XpG/m+l/+lx1MDAwZVx1MDAxZj2YSYMxvl6w/7f2aVx0+j5cdTAwMWNDRVescltcdTAwMTalL4wqKiqS2Y1cdTAwMTOY31JV5lx1MDAwZYD3Klx1MDAwNT04OjFeXHUwMDE1dIG7XHUwMDFmKLnvR6KbJmObMLZcbqZCfifeTO68QMP0lbgnM7y1emDm+fRjZ/104snl41x1MDAxNO+8OJ5gdaVpfrzuXHUwMDExgjk46v1cdE/n/bzsVYdm9Vql2bmpx8eqo2E4h5s3n+l4dj9iJ1xmXHUwMDE3XHUwMDE1gDvl6OAyJDnv5Jgw6/leK7d9y9D4dzeSXGL1L0zkjv9cdTAwMGJcdTAwMTPdx698PDqfYzVrrVclmZNHXHUwMDAwgKfi5OtcdTAwMDC9Q4J6kVwiXHUwMDAys6KCvqb2NtZ1b27OOf3wg1x1MDAxN1x1MDAxN1x1MDAxMqM0fsBaejAjXHUwMDBl68ogv+8gZP9cXHNVm6eK05bf9ENcdTAwMTNAqk4v91x1MDAwM1x1MDAxY2jI6+u2XHUwMDFmXHUwMDFigsjKXGbbkeD33qtAOfyDj3d/P+5pXHUwMDE3XFyqZNxYXn7u+NeEwu5/mjB7/aqrVapcdTAwMGY6XHUwMDFjf2PXrn/56Jx5nHFcdTAwMGZcdTAwMTTfdZv0+k3hhT9cdTAwMDRa9JOGidWtul0vZlx1MDAxMPLrjJ0vIeCiXHUwMDBiuMrvXTbNIV9aLFx1MDAxNSz8wVxiXHUwMDE4ylu9f1tWyWbIJ2bO048+duGSn93gI8laeEKKXGazoqXS+83Xo0OajvWPr9tffF0z/8PXXHUwMDFiplx1MDAwZVxmkL9cdTAwMGJcdTAwMDPQzWqPpKNcdTAwMWT2T+pvUalcdTAwMWFhT+DjZoDw57FcdTAwMDdIPbksXHUwMDBmXHUwMDFks4ssz0FcdTAwMTfesYuZklx1MDAxYz8kXHUwMDBiYe+pXHUwMDAxXHUwMDEwaVx1MDAwZVx1MDAwNv91cujLXHUwMDE45DWJNMtvXGLLZ7faQDBbXHUwMDE39lxcVZFcbk/BkUb7vFNcdTAwMDTHkil0PJPA5KFktNz1S+zIw2+UXFz+Sr7+xEtfhUb9N15qgSFBNlx1MDAxNlx1MDAwNWj5V5NnXHUwMDFjYsBmXHUwMDFjjFlcdTAwMGWCkmEqYuAyXFxcXEbxzu6JXHUwMDEwXHUwMDE0aplFbVx1MDAxMalcXCTTXHUwMDE5nffW8OdQIbFeXHUwMDBlvNxcdTAwMWT4Vlx1MDAwM1xiXHUwMDAxXHUwMDAzj5W4hEq/U79cdTAwMGVLXG52Slx1MDAxOJTFRppTUD9cdTAwMWaRxKxcdTAwMThgN++MQX7uPVx1MDAxMOqnlFr4e+dcdTAwMDdZXHUwMDE4X1xmjsU3XHUwMDE2UP191kjsXHUwMDE53ZKgSsmr7+ubsWfSLJ1Iuzlze4q7aVx1MDAxNOLfSVxy1Vxyo/GF+1x1MDAxMUCaiFx1MDAxY+J7xytcdTAwMGZJ+XDmRLxR38SQOvs5ulDAvy9cdTAwMWRcdTAwMGI1tc0yg5FcdTAwMWEz+ql9yMdvXGI4cSGSWXKToD9TKLCoLutcdTAwMWOrVG/fptXM4fjpw1x1MDAxMX7yT0o3p93xt1x1MDAwZof7VX+C4HaiXHUwMDEw/3L2i/urf8NcdTAwMTLuQlx1MDAxMpqIyEC1XHUwMDAxXHUwMDAwk1x1MDAxY56QcEBcbmK2r4fO3SBA77W68mFIqlx1MDAwNeSvdVx1MDAwZffHLHRDunB+sirIwOPLQ8BcXG5I+MuDXHUwMDFkT8OeQPCL5oQmy1x1MDAwNOMmippcdTAwMDOxRu9hQS6p+0iq3uZPTXJcIky/MIH/i1x0L3b+ff9cdTAwMTRNXHUwMDA2/+euyXiedfvp4vAy6GTNr9qsnYZcdTAwMWOJQ49cdTAwMDTNXHUwMDAwwDGheIF0fjNjmXBlPpYn54JIN7K2kSsynPudrfKroW9IVZtcdTAwMGLSXHTlXHUwMDE0SOX1XCKLhdNcdTAwMGakKHHA70qzh7KdekJcbvk8ruPH535P96fmIDmwP3fXXHUwMDFjXHUwMDEzXHUwMDFmXHUwMDAxJofjPGIh//vccyxfO+W3ZONbdrOJ2VwiQDNY01x1MDAxMaf7mGCIRdCuXFxfZ2FcYlx1MDAwZVxclERcYvhDXZWHolx1MDAwMpKNpoq0XHUwMDE5in9oXHUwMDEz05Tf7Vl41PiIqOSI+bJGYmCWUeBx3oFHdVinyZqp21x1MDAxZLq1XHUwMDA29OV7+6mBXCKT4XmU/+ZWNOWvvLH6v3nj7eGIbFx1MDAxZFx1MDAxZmpcYulaX1xyKa5cYpHMIc91tjrQSKaRvNezSVx1MDAxMVx1MDAxMjXgMLx26Vx1MDAxZI+2qEhcdTAwMTh+nJpcdTAwMDMroCOT3ea+qrN/xJ1cdTAwMWFcdTAwMWLB3W1vpqlS0Fx1MDAwZsVeqVx1MDAwMb66XHUwMDEx2KdU5YTfJfnD6Wqe6Sn9b/bLJXK/7sVD/fpvXHUwMDA2XHUwMDBiM3BzOZVwXHUwMDExRERcdTAwMGZg1E9cdTAwMDOLZKrC8GmUr81aX1wi4qiKKyFcdCxdXHUwMDFhnJZcdTAwMThULtyCkyBCpU/86lx1MDAwNTVHaK+uV4m2XFxcdTAwMTVcdTAwMWW3/Vxy5pS/3p+ll5uN/4aLbVx1MDAwYoNS0D2+7Feb2Vx1MDAwM1x1MDAwM/3K176cyPxY//rWtFKnlWhLxP3fLI73+spcdTAwMWaTXCKO3O036VGSdI3yPGXp5dR+YlxmeVx1MDAwYlJKXHUwMDAw3lx1MDAwYp/9RvV308e3olx1MDAxYcNcdTAwMGZKoWsyK+ijPqyfXHUwMDBiQpb7XHUwMDE4+nLgcuW9+0zAY1x1MDAxZCpfJrrXhFBcdTAwMDLA+ijsmUpEXHUwMDE2X/grzaxcdTAwMTf7+555SPH/5Vx1MDAxYfuHa8RKR4fyXHUwMDFm10ylsfzUOcawR2HJZnDW86GLXHUwMDBlPcC9vi02pjE8XHUwMDA2SqmP3lxmTmimrIpBXHUwMDAzoXZwrJ6KkfJZamq1XHUwMDBiPoaFIVx1MDAxNytEX0eZSFx1MDAwML3WyWeEmezow1R0XCJgjftcIlx1MDAxM31++tbMiUFcdTAwMTWN2z5RySjoLOU8/nb/3lksXHUwMDFlJuefnW//xeL2yY7IYlbVyWrOWjN7RjBybVx1MDAwMEJf4FJqtPvnxjG775XCXpFcIrOlXHUwMDAw8qjhfkBcdTAwMTNcdTAwMTVzr3JEOKd7XHUwMDAyXvSA3nTphquWdIRcdTAwMTdcdTAwMTI9XHUwMDE4Oej4XHUwMDA1XHUwMDA1aC5cZt8raTdSm+rvycS07OKBmaB0ULtvkKVfQPVZxLfC9D/YRyF2bVxif+qHjPr1605cdTAwMWJ4L1x1MDAxND78nouhPcg/YdqDM1xuN+K8a+1cdF3vwppcdTAwMTaIRz7qlohXcCSTs3TbSImPzX5Ab6RcdTAwMWP8K1x1MDAxM0M/XGLFzygsuIHVOXZSMFx1MDAwZV7gWl581E0v+lx1MDAwMXI1TcmJvpNVuJXXLVx1MDAxZVx1MDAxZeSYXHUwMDE1pEQkXHUwMDE1vrtpT+VlS1Bdf1x1MDAwMz1qPb5aXCJRlzrU8d/cgKBcXDFcdTAwMTA30DQ1rN+cs7KPlyi3x2H6/Ph9tHHitV2cZ6nlP0debMQ9SZfKXHUwMDBlZL2T2bi3Q7xh8SHaLelLvlx1MDAwMiQ/bkGlyeuJcVCf2phFMdb9SPTU3p2d4lx1MDAxZZHhofbZXHUwMDEwrexYPOGUarjZXHUwMDEyU96fXCKR5W55YFxmXHUwMDFlJ3D9XHUwMDFhXHUwMDE4WFx1MDAxMEKjJosopJTBrck1n5e8qrCA6Tee6Y7SXHUwMDEwj4BcdTAwMTDvn19nhqhwxixO/fFAXHUwMDA2wtk5Y/1XXHUwMDEzvvz09OlcdTAwMDam/XdHiUlcdTAwMGJLM7beTpiyXHUwMDA2by7TN4JJlz1cdTAwMTRpuUGjT2uIXHTQrcJcdTAwMWMpgzrP+ORjlMLmhKh+P17NOVx1MDAwZVx1MDAxNMG9qPrDhLOm7cGBnIJAWFx1MDAxN7xBsqWnezxGXHUwMDFlXHQ7XCJD2SVKPULdOpb2a33Ftu3PXHUwMDEx9EZcdTAwMWZcdTAwMTbdLOzjXG58cV+DiIWG2mmFpFWnn3xcdTAwMDG6Jby4l8x5kWe44ONcdTAwMGX3MHoyupahzExGXHUwMDFiOFx1MDAwNVx1MDAxNa8ulO3DN5X0XHUwMDFkTWpJ8+FuU2jXoyMxVVx1MDAwN0d/9ldlXHUwMDFlv6BhtsLQ/D/MVDFmR1xuba+/pv1fj637epZkZWxtXHUwMDFjXa2fIWmRe5TZ7ZF7M1j4OJVF5S5lSEZcdTAwMGUyXHUwMDA2Luon4nzwPNtcdTAwMWP/hWcy6Vx1MDAxZnmRlndDXHUwMDFjXG5cdTAwMTlcdTAwMTaubllH204wLPNOnJmLXlieV1JcdTAwMGaJrn1/gm8/XFw5wmR24ItIp+KflW53PtHERGv6YDp9xO/DV1xuXHUwMDBmXHUwMDBilv1VXagx2/l1zW4naaR8XHUwMDFkUFx1MDAxNlx1MDAxYSO4XCLfVVx1MDAwN61Hc+Xip6Y9uPC8VuFcXNLFhohY6o1cdTAwMWW+xIh1NZvDJ39cdTAwMTBxPdlcYk+d1H7Sez6HbM3wX6SRqem1O1x1MDAxNyqUrWqOgGEl0Fx1MDAxYnrrj65EXHUwMDE54dUlvlx1MDAxYVx1MDAxMnVH+dXJXHUwMDE532183H93T4SpM1x1MDAwNFx1MDAxMDBcdTAwMTP0y0t7XoW7/9XkXCIsP9f0SUapof3XXHUwMDEzs7buJVxmXGbp2YC9c/BNx50hX+gj3X1yyMyj4cyxXHUwMDFimev95lZfcHpcdTAwMTC3qyxcdTAwMDemxY6BuZpEXFxEslx1MDAxONlen9dcclvKWlx1MDAwMWAshcVcdTAwMDJV3YOB15Q2lp3CXHUwMDAzPUtQ07pQq5+oyjnvjmj6UrtY8epUXHUwMDE4NGK0XHUwMDBimnmD/aHB54m8glx1MDAwNc2sb1x1MDAxOfBgXbJcdTAwMTJJd1wiW1xmbH+kgHO/0q6kPopep8t1XHRcdTAwMWRJh3VcdTAwMDT2MLEyfjxcdTAwMDdcdTAwMTDe7px7dzT7XHUwMDBlkzXfOS41yJBY88pcdTAwMWbeK/zAwOlcdTAwMDN3NFx1MDAwMbnwRZHL/HyONleS/rOw82RshTGnhlxunVEzdqpVNkSLIZdcdTAwMGUkUyEy5Y27XHUwMDFmXHUwMDAzdbjSWVx1MDAxMWvLalx1MDAxZT3zIEVcdTAwMTRWN87t5U9AaX1cdTAwMWRcdTAwMGbRaVwiXz8thJUhh2bFXG5E9KachM5cdTAwMDTlg3VcdTAwMWE5XHUwMDEzuVn+4ZKy+I1/XHUwMDA1RW3zX41cdTAwMDWy7JA+XFyyh2bDWVvHWolhxCVKr9+vx/iaMJ9cbsdccuekazKiczZcdTAwMWO1qlx1MDAwN0UniC+ql/nf4FAn3VxcT0NcZnwoqUWwg3hD5npnrbqQ6Yms3IaWo3yHTOhYXHUwMDAzktCvp6GruSvo1Vx1MDAxN/g4yFx1MDAxYc3BXHUwMDAwg/EukzzJX1ld0fNw/ixcdTAwMDZEYOldoZ6YYsJcdTAwMTexv707wPvXplx1MDAxNJ1I3JXJM7UnUMbGgbWS2v6N71x1MDAwZVx1MDAxZoudPFliRVx1MDAwM01I0utF8o9pfGlEZzDHxMjP843vmXdcZi9cdTAwMDbuKVx1MDAxNejunam7XHUwMDAxb03MXHUwMDEy1j5RuWjGIP/4lVx1MDAxMbYz9Vx1MDAxYonaquGHyZBTXHUwMDAwXHUwMDA0PDAkyIFRjFxyXHUwMDA3I2+u3Fx1MDAwN1xi3rlcdTAwMGa+qVZcdTAwMWInI/s1I+eg9E3V/CzadDfoqTvIc1x0e0cs/HM37NtFx/zTQWr004fR27BcIlx1MDAwZulcdTAwMWZcXG6s4iX4bMZcdTAwMWHxwY3rfFKeXGIuz7FRiFr5LovPqcJcdTAwMWJD5zo9c4RcdTAwMGJK01x1MDAxYqzPVqBCXHUwMDFl1dfo4f75teyPs3iwNpRG2PE7872D9ahcdTAwMTL6+bvrrVx1MDAxNFx1MDAxYlx1MDAwMlx1MDAxOaqbqPBiXHUwMDFkISy8zFx1MDAwYlx1MDAxOU6bUi1i1KvOke3du1xcSrpvJVx1MDAwZuWk4iP4PPw3IFx1MDAwNSs8XHUwMDFkaanfitv2poqyXHUwMDBmbqtPUDNcdTAwMGKtXHUwMDA1U8glU1x1MDAxYaTOhPaZQbxOXHUwMDBi9pzNn4vFXHJcdTAwMDO+sMp7c7FcdTAwMDZAw9dcdTAwMGJokjyv39kjWGBcdTAwMTmRplx1MDAwNq7D3bFcdTAwMDX5LNwpeeLFXHUwMDEzXm34cal0d5LV9HBTxSrJb3adU+PnP1VcdTAwMTdMilx1MDAwNFx1MDAwM/TGSff9SMyBOVLrZYfqcFxmkv6JPlx1MDAwZfjlWVx1MDAxM8yJwNxYXHUwMDFk/VTbJ7exR6vsv7WK/zVcdTAwMDTsr1ZhfmtcdTAwMTWupfDXX61cdTAwMDIzNfRcdTAwMWVcZm9cdTAwMWE59Z5Zgdb8yeTWjzhcbm+E8Fx1MDAxZr2++aVH5ZT4kVNNjpG4XUr1016I/0Hz8ILqkXknTFx1MDAwZnOlJlx1MDAxOZWHQ4mZVlx1MDAxNNGZu4e6PVx1MDAxMlx1MDAxM4lwS242k/Wm0lDnZSgqfCRP5197cIqsJ8JcdTAwMDWoxlx1MDAxMKKodUCJnXfc0Vx1MDAwN+DgPJ2ONT+i88BaXijY0VaqejPaOX1hM5xcdTAwMDVg0busslx1MDAwM9Bn8GMngncw1r5cYlx1MDAxNIQtx8bv3le543BZ64RU0K9ysjXHMHhA9FxyZFx1MDAxMlHC7Wc/XHUwMDEyqbvDY/mKXdZR3DVVte9Bmcdx0KrZXHUwMDE2381kSlVcdTAwMWFPWFx1MDAwZUe0mv9IXHUwMDBl71x1MDAwM1x1MDAwNo/PRtWf2SCTMd/j40hd8eWXxUlENVxu0LZqucrBMFx1MDAwZvzb7of1XHUwMDFl6jxcdTAwMDVYvVx1MDAwM5lcdTAwMDTnLzN2XHUwMDBllllcdTAwMTO0/PRcdTAwMDSKoYRvwvhX596fbFx1MDAwMCpcdTAwMTiiMFD//NejMrRcbs7UhzLEXHUwMDE59Oz5Yq04RLiq61xcP02lwuNo9eBAf0YpycnFVZY4eq97d1x1MDAwNVx1MDAxNG3Xh7qGXHUwMDBiVT5F1lx1MDAxOEZbdFFcbqavON8sUktcdTAwMTe0heT7XHRcdTAwMDSlqLXThVM9XHUwMDE5QHolvqIhXHJjMFx1MDAxMCNpKrs2U2p3REfhNThRW3pcYi20nMteZ3jSnFxuYzwmdr9cdTAwMWGfxo/eXHUwMDE00/jKIFx1MDAxObRvj3V3fz1KmYtcIlx1MDAxOcsraLaf6WvF9aZRM35/VntcdTAwMTKLXHUwMDEzRvZ7yd2eWK4nsHNYb561+T/rgv/vmS/fXG75qVmPxYdcdTAwMGZcbs0gXHUwMDA2qMk2oFx1MDAxNqtO0y/GXHSO76NpR1x1MDAwMp5f1tzTyLpccjCpfub4TYBcdTAwMTA4NVtcYpo1lSmS0leWXHUwMDAyvylcdTAwMWIm2F5el1x1MDAwMv6020w+XHUwMDE2hrrstpNcdTAwMTR3XCJcdTAwMDRcdTAwMWFMgu+A2PLXXpmr50D584lcdTAwMTXQ1KtcIuBcdTAwMDd1hC3x2SpcdTAwMGL4XHUwMDE5btVtdlx1MDAxZYArXHR62IJcdTAwMDf4N3WDKcO5XHKXfvI+4oODd7Gxf/dcdTAwMTdcdTAwMTMnltZpcVx1MDAwM0P7X67izTJouUBcdTAwMDbG+T91Zm2xJ1O0VlxiSoic0ImJXHUwMDE172Snft5cdTAwMGaOw2FoXHUwMDAzejpToOyUP/4hjCaFXHUwMDEy1u142bJYXHUwMDA0ys1ezTGmqVDuXHUwMDFm41x1MDAxZK339YkjyZdcdTAwMTFcdTAwMWFcXKFhfYmtS8nctjSrTnxcdTAwMWKDXCKm+nzloLyuppqEkZOVkZlcdTAwMTStYSppZ1x1MDAxNO1cdTAwMTmChsdS7mVlXHUwMDEyylVM6YW/Zt/YM0Opf/JY7pvvmP5cYlBZ4eVB+If37iB+XHUwMDFmvJ8oV3DeXFx7pLrxSFVu90/uw+vLZb+ZUbW5lkxcdTAwMTNwT0KrJbJgikiGKH5cdTAwMDZAdGJGPphcclx1MDAwNlx1MDAwYjFcdTAwMGbYXd25XHUwMDA1wrGLZ7lcdMiSM03y4oJy97PuRnSmMJtihpiu6VtXNGC1tVsqJVx1MDAxY0Dl27ZYSzJcdTAwMTP2XHRcdTAwMWGjUHuOe6BASWnUWW5xj9yP55pG03txqLGEzdQqwygzyvzxS67GwT84XHUwMDFhOFx1MDAwYvy4zd8+urfK0lx1MDAwZmVIfTNB/uhcdTAwMTdcdTAwMDTDipIlvMOyaqTwy9bkXHUwMDE2ySGjXHUwMDEyRfdcdTAwMDR7kVx1MDAxMJ1U0bv8gN6Jj1HGPFx1MDAwNsbcd6ZvXHUwMDFhumTxXHUwMDA3aFxuc3r7fCPE308xvIs7J8U1pbTz0SzRNZNcdTAwMWFuU9/StoSiXHUwMDExy1xi4oC3395cdTAwMTlcdTAwMGZxuSpOSVx1MDAwMdJt1J6nnM9OQa1n+lx1MDAxOEX7mis5R9ZUwmilXGbC+UfHoi62qtjxd1ZV8ZPvXHUwMDAzXHUwMDE3qpr/1qY/71x1MDAxZIxWMjnrJLD0PHtKkaV0L9rCXHUwMDFj0ClcZn6bXHUwMDA0hGhcdTAwMDSL1Slj4vM9RWY2g5CwZFGpXHUwMDAyOaVcdTAwMGZVXHUwMDEyXG7+zFx1MDAxMVx1MDAwYihksSxhXHUwMDFlxL1rRlx1MDAwNfc7LTixnsxcdTAwMTZcdTAwMDNnQc/m6FtBXHUwMDEx/7qCQyz44Vx1MDAwYsK1aMBEVCHv8TZ8mY1aa5z9ndVcblx1MDAxYiS7fnvAWKapQvRd4Hq99W+RzFk3Ps59XHUwMDBlXHUwMDFk0fHPxshJ0pIuXHUwMDFj+tm3LtRcdTAwMTeff1EmXHUwMDFkL/6X14td+5mzRpLMwuZU81JcdTAwMGXOcv6bpfvGXHUwMDA1qlk2nEybSO/Hf/mST/+zV8VKvbJqXHUwMDAyzkxcdTAwMDYjPIGk71x1MDAxNVJp+WWQkm2L69K/arV9TTKkSmRMXHUwMDFmfpXhnE9X/0R5lUKNc6lcdTAwMDepgbKHWKLzXCJcYqbSYFj46CR/zaJim+QhrcM0vfBnT1x1MDAxYq2GbdtBkVx1MDAwM/FDSnPfospcdTAwMTZcdTAwMTlcdTAwMWH278CF4IyUlk1GXHS1qn+00/L90U6a31xix/dcclx1MDAwNlx1MDAxNfRW39xcdTAwMWLH8yapb6s+fCCoc5F5r6LGii77XHUwMDFjrCqihlx1MDAxZEegJZD8XGKDw/Sxzy34fXxZnu1jV1x1MDAxONc/P7NhwWWyXHUwMDBmKix7zVx1MDAxOe2/XpW3xExcdTAwMGJpl47kk816XHUwMDFmWknOPsFX237aXHUwMDFjXHUwMDA0XHUwMDA1XHRcdTAwMDFRKTp6wc6sW4BRgEC3XHS7XHUwMDEwuupcdTAwMWEpqlx1MDAxM3ZPUSnoXHUwMDAxRqam5HdcYqJxu7NOR3tbWWlgxpLY0SmDIVtRwMZ2lepuXFztg8hT2FlIXHUwMDE28zxrfWnfNbNcdTAwMWN3XHUwMDE4pYpcdTAwMTXIeD13Q9ZcdTAwMDU+8+U7O1x1MDAwM239ovxS33CvaIg+QCiXcJ3Voyn+dkNcdTAwMTNxRVx1MDAwNFx1MDAwMaC7cc7pp9FIZVaBxlx1MDAxOc1LKG35xlx1MDAwMD1621x1MDAxN1x1MDAxNfsyOth6K/NIt/72uKY1yJDweJlHmv7DXHUwMDA3jscxSlwiitpdXGJcZs9cdTAwMDFcdTAwMTBcdTAwMTOLXCIn3dfuzaH9tVx1MDAwMdlcdTAwMWabfPSaXHUwMDEw4lBcblxm4XWB081E9nFAukC7QldLsoMoKlx1MDAxY/C9w5SJrfhcdTAwMDLKXHUwMDA2ZXlcdTAwMTc2kLtcdTAwMDZ8S3aP32olWiun0rGWgdsqXGbMVy83nq9rXYV/W47jgCGRgemW1upcdTAwMTfC0lohslwiLL+J6TksqSiAcZ85yyzcm0PGXHUwMDE2XHUwMDEzoTD+gFx1MDAxYftcdTAwMDJaj9w/00RUb1xmSFrcXHUwMDE3ragqrpZcdIBrkDqUbCVcdTAwMTYv59Ar38BQXHUwMDBlXHUwMDBiYFbsw1x1MDAxNs9RLN9cdTAwMWJdvOJr4Fx0I35TvfDvzoj8lZcgciX8L5f78DDTcadcdTAwMWGpriyYQFxy3ZFcdTAwMWNcdTAwMWFstVx1MDAxZYqvqp6AcaHxxpaN+IxLclx1MDAwYpPQPFx1MDAwZTf98lCc7Vx1MDAwMe14Nlx1MDAxYSxnvVW9zS3Hm25Qw+kkXHUwMDEzVLS9pIzSuPBcdTAwMDUvq1x1MDAwMXD0xzo0+aRcdTAwMTZW7FxybuQ9XHUwMDEynL8teHW7dpbPl8uWqFx1MDAxNnxIKcnNPnRhWmZcdL70Y4lk+33+LrHVXHRcdTAwMGWA0EEzrDI9XGK6hIdj4ulcdTAwMGLxXG7lh3y3k7RHXHUwMDE0UcMjLHhcdTAwMTI0mnWOpDVFPlx1MDAwZlx1MDAxOVx1MDAxY/mBpiBcdTAwMTXWu2nSXHUwMDBm2tPbNFx1MDAwMjhcdTAwMTT+5F3zkoHU70bKvZptxzZcdTAwMTXBYZaou+Yu4SVe8YK7avlvbtDc/srdvtFv/u8uxnyWNVx1MDAwMr7YI++CXHUwMDFiLyGta1xckf84t95IXHUwMDAwxqtcdLThrpfSR1jt8TsoXHUwMDFlsVXfOCnN+5bdXFwumVx1MDAxYVb3kD+U3/FcdTAwMTSu7mHVd/VRRlx1MDAwMp+n13Ws7EpcdTAwMTJj9TZcdTAwMWR4mpoxXHUwMDE4me+WXHUwMDFlRqhM2lx1MDAxOMcpWOoor7RcdTAwMDFyXHUwMDEyQNi1glvyISVv31x0f/WGX7xcdTAwMTa/odLKhJONXHUwMDA2xtBne1x1MDAxMS2ApLl79SgrTPJLxLnB+0N1VOU/eLVeKzOlXHUwMDA1JMKeNqRNXHUwMDFhfVB18Vx1MDAxZm9cdTAwMDAwi6/lj/CV5cG71flw84OMeCSs+Xr5nEltdpvZqdfusUeQ/sRcdTAwMWFcXHlcdTAwMDfQXHUwMDAw8nsrklx1MDAxZLtcdTAwMWaMu1x1MDAwN2L+XCKNKzC4oqfJv/l3P312NUNi1W0yXHUwMDAyZf47v0zXvqZcdTAwMTdcdTAwMWQtXGZcdTAwMDFEOVx1MDAwMNNcdTAwMTW9zo2uXHUwMDE57+rHXHUwMDE5XHUwMDBiSzl9XHUwMDE2IH6FgW5t4Im9TfBcdTAwMDCqM/upXHUwMDE5nVx1MDAxZOrLY/JcdDJRnlx1MDAxN81L8MRypoKWc8w5UFx1MDAwZahcdTAwMTnkV1x1MDAxN/lim52UtXuAmk3x7qKPPPk8PHE7MSlFKMJXr0UmM1Q/r6P4fCevJqVpmbx4etmUU9atwyGI9Jozg/Q5XHUwMDE4XHUwMDA0tjJqlFx1MDAwYuTrbcD8+1x1MDAwYiH2tZqROPWf98PVqa84XHUwMDE10F9kuHWUOLYoLT1rnZ31NNHxQD9xxrpumFx1MDAxNadiXHUwMDE4XHUwMDA0VN/smJQt8IUx5Vx1MDAwZacv7IY9i1x1MDAxN4JcdTAwMGK/y8LqQ708XHUwMDBi4bWBhVx1MDAxZaW0Lj40SM6PXfYzz5yg+1x1MDAwMraHo5E88f7qWLlcdTAwMWN+cjO8nVx1MDAxYtbf3ExgXHKYeb0pMWbp4ZMhaoZcdTAwMDK3LCFcblxc11x1MDAxM/hcdJMkgeKQl1x1MDAwM+x8ofaDMeBcdTAwMTip5n5xmVx1MDAwYlx1MDAxON1cdTAwMTeYXHUwMDA16VZjdkmBXHUwMDEzXHUwMDAyKjNcZmFcdTAwMDYldVx1MDAxYmRE+F1Yj4/QXHUwMDAzKXShofx8VVx1MDAwYqyWvPJ82J3Cd7j1U6pfMaKxI0gjx5pY23FjROipi/EgnN+fa7Z8XCKaeNVcdTAwMWRqXHUwMDFjanp3xOa/v1x1MDAxN1x0pbFcdTAwMDNcdTAwMWLVc8xoe/qQR8zWp33RXHUwMDE0tVxcXHUwMDFlYDYrXHUwMDE1j1dcdTAwMTDhzPhtvzRcdTAwMWS+IblcdTAwMGZcXJqdcUS3XHUwMDEwtS2wUZ48USBRNOmTPcLwPtxcdTAwMDdjfVXStso4RXB+ktXdzKCVyczIXHUwMDA1Qjne91X2+o2XXG7+Wlj06NvL/Vx1MDAxY98/Olx1MDAwNeJcdTAwMTh53Fx1MDAxMX7ceqKPvC7I9L5cdTAwMDI051vRaFuH+TxKgDT1s8/b1Fx1MDAxNKFcdTAwMTejRFx1MDAxNFju/EXEj4w5h5dcdTAwMDJt6VhcdTAwMDCLJ1Qoklx1MDAwYi3N4iefqjBsl1x1MDAwNtAxlsaypv8uXHUwMDE1xbN2ZOJcdTAwMGLbdDOy4rZ5hl9OMGLSPdStXX2d4piPclx1MDAwMUtcIkziXHUwMDEy7940SDtAjZJke8wsXHUwMDAzXHUwMDEx71x1MDAwMWfJXCLl3GaPKDgpNVx1MDAxNywwR5xhIJVfNeQk0rLa8JJcdTAwMTity1x1MDAxNyXJu45IL3mJUKR87IxcdTAwMTByeb28L+Hb0b9mJtOl06LQui+sfaPzsbpFz+cvYCPQLOl6r3v3p1E4b3cvzNNm/zvvXHUwMDExR/7U6t7aTfybXHUwMDEzXGaJXHUwMDAz55buinhcdTAwMWVcdTAwMWHrs7yogc5iXHUwMDE3XHUwMDA1XHUwMDAxXHUwMDEzXHUwMDAw5lx1MDAxMiGFXHUwMDBmRM1Lyt00LUorkFc+XCJ6VD/pXHUwMDE2xJBxXHUwMDA28mSCXCJuQvN7XHUwMDE5jpaJiNBcdTAwMWFJVfwtp6z/hCm1vfY9XHUwMDE2omhOfF2KrkBpQzmJo+N9KVx1MDAxZaFcdTAwMTP3+IHxOSDRXHUwMDFl5EpcdTAwMTPaLO60p1x1MDAxYk8xJZpcdTAwMTlvXHUwMDEx7y5cdTAwMWH0PFx1MDAwMnNcdTAwMWX0b3l4nPjtUqz2XCJcdTAwMDatXHUwMDA03FagbZF77Vx1MDAxZEWqXHUwMDA3nJdcdTAwMTAu4Vx1MDAxMFwiXHUwMDFmMy9BVu1NINDvRkSQfVx1MDAwYmRcdTAwMWJf8repyGdcdTAwMGXuZlx1MDAwMq/P31x1MDAwN3ZrXHUwMDE2v1x1MDAwN1x1MDAxOVx1MDAwYkw3XHUwMDA1cYLy51GEXHUwMDA3lFx1MDAxY9LfWolf/V1yXHUwMDBiVsnjXHUwMDE4/syJjVx1MDAwZeXhZuJcdTAwMDOpdmC+gsxkNFx1MDAxMySXr5dxqkliwVx1MDAxMlx1MDAwZuKVXHUwMDA3o6J+3NArrvtUqLjA7UxHq9x0Ylx1MDAwMjoxpn1wuk17XHUwMDFhXHUwMDAyt6t7jvy8Jm25XHUwMDE4dlx0Vn5MZzU+RzKRcDbeJVx1MDAxMoyiXHUwMDA2Q/HPXHUwMDEy38IqXHUwMDEw7Fx1MDAxOG7fZtRhQp6Lztq/wyz2N1wiIX1b6OiwXePxNeHqZW6SLYOuS2dUYFx1MDAwNinI+y5Pl89Rf59YJsHP91ZmXGZmfnK2nLu3y4y6cI1cdTAwMDJXRMLE9kF2XHUwMDFmSdh1eFZbkVZcdTAwMWRUOrHpjD/rMryZn56//e2YXHUwMDFh8Vx1MDAxYteYsj9sijFeXGZYOuWOTkegmlxcjCtKZI+Ls+OfXHUwMDEyRydcdTAwMWVG3excdTAwMDAhdE/rzkwyyY8hfmSnWEw0fJHSR6Z5YFx1MDAwZlwiMZ3XxJwpl8XGZndcdTAwMWRJ4ESkfy76I6uV93sp5SSL9MHPJJpZ/aqg6EX2n8+riar4XHUwMDFhc9Xns0LOjlx1MDAxNFx1MDAxN1xy9Fx1MDAwMfOq0JZcdTAwMGahkiRhN6PxqfxXOSfdOyfoeVx1MDAxNz9cdTAwMWSM7p/D+umleplcdTAwMTRwNfn5mZjwkTdfP9DzXHKeVM9G3J1cdTAwMWPiQ4G0XHUwMDFlhn5wSPrXX6JcYr9my/L/uFVEuaBkLlxy3ICDS7lvaTJkJEPEJ/n5QVx1MDAwMuJOXGbIRDqCl1umd1xuzK6wgvFeXHUwMDFmY49COsooulx1MDAxN2LlYPzFXFx0XHUwMDAyl+HwROFcdTAwMDMps7vlv+dDTJY+dFx1MDAxND5aJ/9V4VjlY1wiXHUwMDA2S8wvXHUwMDFlUVxc55NcdTAwMTgvdkx+u+FMZOS4TIZSnpNBxlxyJFx1MDAxYkFcdTAwMGJcYjtJiFxczHybKPjBXHUwMDEznjnLz1x1MDAwMn/owtybT/ahfadcdTAwMDflb1OMMslyXHUwMDEz8JGFXHUwMDFkky5yYEtTi+02/J9cdTAwMTlcdTAwMDRHezB8/IlMdvgzn9p6pX4qjjunvVx1MDAxMUbC2FS2rjN7XFxcdTAwMWT1PN6b78z7Y7rc9pm2vFx1MDAxMlx1MDAxZmBE6PDATsHLZIld0Ius4Ffxlsr8lsmGfYCAjuE25lx1MDAxZVGuxrrTbHldQ1x1MDAwNmgvNsnIKzZ8PVe7uKSvy7y4Xlx1MDAxNTGdtLGek3mPou1MXV3Ic/TYSjdZ7vZdUWtGXGasTpCy8H3PIfmsRM2NzVx1MDAwN9k+d/BcdTAwMTiplNCZz5m9wGJcdTAwMWKK2fRcYnyvVChcdTAwMWFoSyFcdC8zkWtcdTAwMDOla9tcdTAwMWY7xOxcdTAwMDdxkO04Klx1MDAxZdX+mqX6N5/FXHLs/ihcdTAwMDI4+Pw0RPyp0Vx1MDAxMMReXHS60fjuTM7sj71+YE7gqL4wYORxXHUwMDE4qTFcdTAwMGX7XHUwMDA3RbMwzbQ378/cY/aLJ3SmcJ1cdTAwMDI6Slx1MDAxNJghQfBcdTAwMWVT8lx1MDAxZPlcdTAwMGXBk1qeL98nwoFzXHUwMDFlWNpM5Fx1MDAxMNv5pGVcdTAwMWXXpeVcdTAwMTBHXHUwMDE00PcjKbVcdTAwMTTN7fvaJlx1MDAxZlxmXHJcdTAwMWbySLlcdTAwMDaA3FZcdTAwMDPZR7zXqsNcbo2+zIydTJV/VFx1MDAwMoJG8sTxif41JmjUXHUwMDEy1pm+lqniwyeZYNlcdTAwMWKIhco/xlx1MDAxYfjXXG7mmr+BZlx1MDAwYr/IkWhWXmNkXHUwMDA2L/lcIswmvUHrL1x1MDAxNf+p2b5Y6mdeXHUwMDFlrsFp+v57585TLFx1MDAxN11Z9/gpRk55hMvgb9ozRtS2XHUwMDE3XHUwMDBldic2dX5+QIyyjnJcdTAwMGZEXG7MvY9cdTAwMWTnhVx1MDAwZUFccsthnVx1MDAxZIbRT+HyXHUwMDAyzUDHsVx1MDAxZtRIbV3e9kTbl3q3XHUwMDA3T1xixTawS1x1MDAxMLtnx1x1MDAxYpF7hPyZXHUwMDEymVx1MDAwNVx1MDAxYuBI0+WEfKVcdTAwMWWfhXc8tViSWsVpXHUwMDA2TU4zdONcdO5cdTAwMTbFkyecOOAqJXm52UpcdTAwMWGXL5fWW7aa0zK4iPCBfsYrg7aEP6ONaN/XXHUwMDFjZlx1MDAxY71VcGGsV1x1MDAwN+afXHUwMDFjod5cdTAwMTGOzcU0ltWY0fdZ+WFcdTAwMDcyI1x1MDAwN8bmXHUwMDE5f1f/pP7OnP41l+Uj/+SesurExf+ddbSrxFx1MDAxYlx1MDAwZnShvoKRmPL5eFm26NJcdTAwMTcpt1x1MDAwNNVKR9qv7MFcdTAwMDdcZlpmh1x1MDAxYzJqaVx1MDAxZLLG+MRzuF6l/ehcYoXNfVD1YUZCP7tcdTAwMTbxd6pTKVx1MDAwN/VRc6c3umaPXHUwMDE2isZcdLJZJ6GXV8Z2jzDvqFqCutdcdTAwMDBv1ecwuykzXHUwMDFkiJu+t6tvS77TXHUwMDEznVx1MDAwMzw84ShhT0O0XHUwMDEx7Kssnm1wPvHPWNsz0KtexVx1MDAxZFx1MDAwNIFcdTAwMDLaXHUwMDEzjKZxwLhVMIqmxpuxl/zTnG7l8d6redSNmJZuvqaQXHUwMDBmXHUwMDEwc1x1MDAwYlxcqrGwXGZcdTAwMGZ8wVGtUpw0deyh1j+9NVx1MDAxNsawmZaHXHUwMDExjHV/5iZjXHUwMDE2wZ5cdTAwMTR3NFLZMWKLxqrjUlxuylwibp0qg68xo1p0xcG68541547YYmZcdTAwMTL9wHRiZvNcZmuTXHUwMDE4/MK5J05fXHUwMDAywq4piK5cdTAwMDWaQ+gxZFx1MDAwZlx1MDAwZWxnbOFmXHUwMDE3WCb1Xlx1MDAxZUtDPdtcdTAwMDHE7Zw1RFx1MDAxYiVgn/WQSlx1MDAxOa8/+V6O5niOSWrqvvLt7+8xodztXHUwMDAwz1x1MDAwMbxLdmF7XHUwMDA2ZNiL+1x1MDAwMJVY51x1MDAxZeI/Uofks1x1MDAxNmAuzoNcdTAwMWVcdTAwMWZcdTAwMGKu20grSnPZYFx1MDAwNNP9u92kez6PfLr1xUdxoCfAO5mpud20e5B6WNNcdTAwMWXTne3XXHUwMDEwOlqMvlxy3Yds3tBcdTAwMTFEjahF476H2Z79ueTjXHUwMDAzX3RcdTAwMDBeu/JBjH7hf5pjb9ri4YZE9agjgnQux8934z47uKBcdTAwMDGpRE1cdTAwMWNWXHUwMDE14uZzwemYXHRcdTAwMDRcdTAwMDeYXHUwMDFhR1wikFxcXHUwMDE1249GXHUwMDE2PSZhIIaLpVnaoGXGwGtIIcI8ccb526fB/Fx1MDAxMCDKJND9XHUwMDA0XHUwMDBm9VDAn3PPUD+FXFxZgscmi3IkLYtG4M1NsICGNVgzo1x1MDAxZjwmXHUwMDAy+tE2UVHA+6ZApT+Q7HvmqVx1MDAxZC42Vma793i5/Lm2xUhJqZBcdTAwMDDBOe0hp3HUV3Jvs1x1MDAxYptQXHUwMDFkzlx0RnFJvlBQTftjLvmS2d1R42T3ZPZv7lx1MDAxNadcdTAwMTaZcn7UedH9rlx1MDAwZmNcdTAwMTngXHUwMDE0S0aHXHUwMDAxfFSCv3PTyEpcdTAwMWZQZb2YXHUwMDE3XHUwMDA3Mnc56u6Nf9ZcdTAwMTGXg5iLwN6OiX56eDSJpCVccr7gxI1wYyTU3cpJXHUwMDAxJlx1MDAwZsK7ndij1+tlyLWYL1wixFx1MDAwMS1ZffHuXHUwMDFjg90oTDiQWuk0LJyY7yFTSoZNeYMr4F4pZIOljblcdTAwMDCx5uApkeLVpiD01CSyVFNmc6dxXHUwMDE08O/DfL47eVx1MDAxNVx1MDAxNNpcIkbwXHUwMDFmL1x1MDAxN7dcdTAwMWTQx7+5y6Vn2WlcXENcbq9cIqMrdIRZZGGCy+w5I/lcdTAwMTNcdTAwMDGE5VD8S5lcdCFYpGaCLjPGyEk/XHUwMDFh6DmH8vFcdTAwMWPIbX7oV4Kq12BrXHUwMDE1yVx1MDAwM5RcYqd4vDJOXHUwMDAwrNDbeCdomlx1MDAxY8IxdFLCx1x1MDAxObqbJ/lypbRgy4qm9uTRXHUwMDAx0vNfJk3DZMm656VxSZPB/svv/OhcdTAwMDYxXHUwMDE0fEbKRDfMK+bf3PtcdTAwMTJSqzp3U2YlQl/CrWx8K599w11zmYros8iYPkr1tcDzo3mTYlx0UkZcdTAwMWSQg013XHUwMDE4Y9RPcH7uN5PsKXo8hJ5cdTAwMTe9hSWK2yM1XHUwMDBlaCGoPeq4aSpcdTAwMWH7+lx1MDAxZqhM2cd5ljj+QCySzTSZqirOcP/bd9o9Yocwr1jFXGL/z++upG+AXHUwMDEzrs/LtWNmmD9O61x1MDAxMZmERVx1MDAxMkU2XHSwXHUwMDE0UzSl/IfI+zem1TRFb/FcdTAwMGI3xDfnvTpp9r+wXHUwMDAydi1rXHUwMDE3MyRiPCP9XHUwMDE0SpH42OhcYtG8QMBcdTAwMDBEk95Bilx1MDAwYkV26kx1r4Cj31x0x0aKXHUwMDBl/aBcdTAwMDSHVVx1MDAwMlx1MDAwM1x1MDAxZULJkYWC745cdTAwMTa5PvRIwZD5V2f9XHUwMDFiO93f2JlmMFx1MDAwNv95v/VcdTAwMWVMXHUwMDE3XCJcdTAwMTiRXFyNQVx1MDAxZjaboZhtrVx1MDAwZiuUL7xcdTAwMGJcdTAwMTdbb4icq87ucFx1MDAwNemmv1mDXHUwMDFl5PdnRjO++FNUXHUwMDA3dDleXHUwMDBl9WzvTTDM2TE4vblrX1ONvdXb8WwuxXeM5HystW/Rc+u5eT57IG/zoVx1MDAwNbmLd9+v2i/QXHL717P0t4eH+127eSyE8/dsXHUwMDFhp3i8K6nzO5FcdTAwMWLahVxylO9USmSKwj1AitZUjPtE25c3J+/TnqVcZmWOdWOOvlQx6TDQmc24XHUwMDBi01x1MDAwZvb53GRQNpBHPcK892qxz/bxs4xwSO5cXOSOXHUwMDE4ouBcdTAwMWVcdDPYqJ6LnvVAklx1MDAxZqdcdTAwMDRHkFxy8OdAXHUwMDFiXGY020N381x1MDAwZZhcdTAwMGK31SFX9G44JrQ+j1x1MDAwNuU+lriFkzIhL0a4XCLXXHUwMDE12Vx1MDAwZuNoU9Be4Vx1MDAxOdbvsHxASqgkQeFSxUbVY4uzb46T6EhcdTAwMTKLVPVZnUHknoJcdTAwMTFcdTAwMDVcdTAwMWGOXHQ8UrafgGnPgUFRk/L1XHKZ/16QXHUwMDEyXG6qwKFRYuBcdTAwMTBcdTAwMTeVTP28WujrcpBGWmnKO0fV2YuAY00y7Ybni5xcdTAwMGZFlCTPjpfierKobi7fUqnYRN6+z+R191x1MDAxOYpxvT+w22xFcyr1V2NjdN5cdTAwMTelnLKs77T13t/UO0+wXHUwMDFkXCKbu1x1MDAwM1x1MDAxZFx1MDAxNv1cdTAwMGVcdTAwMDNcdTAwMTNcdTAwMDVcdTAwMDdG3cxHO/57mYNXvvxB3WDcg3fhZljh5tj6o75cdTAwMGXEXzro9d3Z2Fx1MDAxMvN1SHbBLlx1MDAwMFx1MDAwYihQkZhcdTAwMTRNc8ZcdTAwMGbw8HvAzLZ998jXXHUwMDFlmEdqsGF335qNZHb6Rv1PzSCK41x1MDAxODF7XHUwMDEyfjk3XHUwMDE0+IGGgthcdTAwMTbbt3tjNo3vI1x1MDAwNpmVsW3My1x1MDAxZYNcZoy3kaH3sHoyil9/1urva9Se9Vx1MDAxNFx06IEp5vz9/KWN1S9e6Fx1MDAwM1x1MDAxZC5ay85Uz6Rf5LdkXHUwMDE1+MVPXHR2L3l/Six+zFx1MDAxY/Y58yqFg8DiXHUwMDE4gVBTXoFDXHUwMDA1kmmTSm06zoVNZJxrn5iTXGJcdTAwMDB2PSTTvvJnTzygSZpcdTAwMWLoW4Xi+1x1MDAxMsGepcm3V/SKv/Hx/+5zyDx/XHUwMDE0Ydjti1x1MDAwZoWvOVx1MDAxYSM42kswXiOjmuvynYA8aGxcdTAwMWWxzeazOSPQft9H8I2rXHUwMDEx01x0q0cmoVQhXHUwMDFmY3YlXHUwMDEzLVx1MDAwN1aKreHEQCtRXHUwMDEzXHUwMDE4XHUwMDFkblx1MDAwNeDeqtxcbvBD9jy46zVcdTAwMDIlbH+gVLls+fTNe4l8RTYy+yay70h3tCv8foDfL2S4XHUwMDE4Ni+R+GZcdTAwMDL4+L35w1tcdTAwMTDSMUOrrP5cdTAwMTGi2qAzSVx1MDAxZdw9aKRkaVFcdTAwMWNKrVxcujRcdTAwMWVcdTAwMWGmSiOEJWuUqOJcdEe7eIooOmFXp0Q0l+R9XHUwMDBlPzBcdTAwMTnpZdTaXHUwMDAxXHUwMDE1arzmXGarXHUwMDFm4C//40Ofoo2U/N1cXHK4WqQ52+TX+oYl89/LXHUwMDEyoZf/4jZcdTAwMGVOXG74T2BcdTAwMGWM9sLf5NhAoIJcdTAwMTWJwbKIon/EWc9cdTAwMTWuOClMmEu6z/OD+6CW8Fx1MDAxOa3qw5VrKp9rYuOvRLTAPLNDpXBh2HJP4FxcrZdcdTAwMDFhtl6bisrMktHrbDha+fSQ7N2H8P/H1Xdsu630zD5cdTAwMTBcdTAwMDckxTxkzjlrRjFcdTAwMDcxRz39pfb2+T/7XHUwMDBlZC9ryVKzXHUwMDAxXHUwMDE0qtBcdTAwMDD5jH1zT3XKQqklg76j1vstXHUwMDE2Nr38/+yrrs7yPdjY4Fpfm1+/fNmvWyBcbjnto1x1MDAxMqJcZj2XqdPe19Z7IXdUXHUwMDAzeEal1KjQclx1MDAxYdrO6IWDbWWzW0h8cu9iPlXEYlx1MDAxNt078SPY6NdcdTAwMDQ8XpEzxlx1MDAwZrRcdTAwMDBz93lI2+3tLuvIXHUwMDFmXHUwMDFj1I6YJFxcZqzdmPdesrCKxuvenM/xdzDbeCff/4XG1lAoNOYnmM/SLp0rXHUwMDAwn72S7HSUmVx1MDAxOVm7XHUwMDFjmiaAuVx1MDAwNG6QaFxmgGlCMjjWW6CZQlx1MDAxMFwifqhcdTAwMDDqmstccvbZVZdblXbvuFx1MDAxY7yzXHUwMDBmOlxuq+wxMm+h0yklhya5eOGPgy2LKsslkXd2u0FjezaGiGHNXTlTX1x1MDAxNUjf///2Ucu89N5HKUf2x7r/7mN8eDy320f74MdcdTAwMThhaMxcblx1MDAwNuMrytBvw512qCa9YCrPwNtBwONQOizKLTLAi1x1MDAwMW3BVqzlvqajLMXbSlx1MDAwNlDwXHUwMDE21Ut/+lx1MDAwNTAgdFa+XHUwMDE1Y/mw8WVaXHUwMDFjXHUwMDAyRaTZ+lx1MDAxN0jkXHUwMDA2n4ih8aBcZlx1MDAwMf784t5/L/bmveLNXHUwMDFloOqlXHUwMDEx8u+b163Walx1MDAxZX5SvMg81Fx1MDAwZVx1MDAxOZd6XHUwMDA0Jlx1MDAwNDLT66nrc1uHUKRcdTAwMDRcdTAwMTEwxzuDuDw2vlx1MDAxYYdWuMNcdTAwMDE6a/RD4yxunFdCUdZwM3741Vx1MDAwMu7RlPq4xlx1MDAxYZd93sRu6sDx9VLR0eNcdTAwMGJyZugjgvDzsk3GWPdcdTAwMDXPJFFKXHUwMDE1wTj+XG5rWVx1MDAxMHmCppGwqYtcdTAwMTFV3jKtvHl25vWmMfjNXHUwMDA2nOZgJ7pcdTAwMTD5c8OucVx1MDAwNne9KFnDjW9cdTAwMGX1vH00ePdcdTAwMDZVY1x1MDAxMmtwXHUwMDA0/Vx1MDAwMW+iKlx1MDAwNfvpXHUwMDE0XHUwMDA1TZWLXHUwMDFkXHLUm2iFuqNelUhxXHUwMDA1s6FcIkPH3tBafLGkxSNcciFcdTAwMGaUikom90Y+TknAuodcdTAwMTbo30fB/Vx1MDAwMz1ewvusMDGvJVxiUqX6g9vR4NVcdTAwMTKd5CVcdTAwMDCYQ2810GB90jBYaNc3wUE55H6UhIWG503Ytlx1MDAxZJDtXHRgrer52C+56MtsYfNDp3tP2C98eoD+QDyxQmwyXHUwMDEw6OlcdTAwMWNcdTAwMTWopeFcdTAwMGbrylx1MDAwMPjG//ElXHUwMDFk9lx1MDAwMFxiVFgmXGKl/7M4czRvXFy8ZYBQRFf5XHUwMDFiR7F4R2hcdTAwMWFdXHUwMDAzZlx1MDAxZKCiQMngPSGVqpikLlx1MDAwNEerUJJccvDXQWqzjb0hemVlm9ZswaZHYCF5k95LXHUwMDAxVXtcdTAwMTZh8ipcdTAwMTmsmNJLualcXNZ1Xlx1MDAxZf4+sPUwu+JkcIuEU1x1MDAxNjl4gIWvV1x1MDAxM6/nXouvv3Hx+zpY/cZG5lH955tAKdL927XjjqhcdTAwMGVBosvNfTBcdTAwMWbWXVx1MDAxY1x1MDAwNs5cdTAwMDGEXHUwMDA2qFx1MDAxYk9cXHSol7Q6SG/iQoZX2YJcdTAwMDWPuvJ1mS6GNyr2N+QkZYJcItnN9/lcdTAwMTh8XGbNK3tcdTAwMWW0RFcovUJAyl3uWKBcdTAwMGZccnKRR4vJXHUwMDFlyCBcdTAwMDdcdTAwMTMqnvnxXHJS1OhfX/vrXHUwMDE10FVcXEpP6b+txaXfPeRRqSnFvHe3nE2PcubfXCLQ2W5cXODFRzI3XGZCViVcdTAwMDWWXGJcZpdcdTAwMWRVbs/jP8P2XHRcdTAwMDXX4MB1xvZXs8LkasJKR4Ncclx1MDAwZj1u0NhcdTAwMTlcdTAwMWFl5OZ0XHUwMDE0MVxcm5KiUlB+XHUwMDAzbn/45tTfLGXDXGai3sY3zT7rP6+GNunuTmBoryvU9lx1MDAxZm5cdTAwMWKldymy7lJcdTAwMDYqSTShS4s0hamqNe3qXHJcdTAwMTPiQVx1MDAwMIhZ2fRGa2x4nPx3756HXG7Q72dLa/5KgsEjhJikfODY+MBcdFx1MDAxYVx1MDAwZXlsLYucZW7NYVx1MDAxOcGQ1WhNzkK+KSnKeiWsfkJkPuftXCJuLrP9tb6KJux7fSpK+Vx1MDAxNOj8hzeA7VxcXHUwMDA2wjxNhExy4ZiCNyFcYoSYXHUwMDA3V1Q5mphj/rihr1x1MDAxZXtllaHYtePNXHUwMDAyUmDQw03lOsJ5pC/9QUx4XHUwMDAxXHUwMDE3iUlIXHUwMDFma0JcdTAwMDRcdTAwMDIlmkxcZqu1Pfhn/XZjPXiJoTqDxUGmLHZcdTAwMTXTxJ2hRCH0grzX6V38y2VcdTAwMTXjl9/0XHUwMDAzXHUwMDE40v/xXHUwMDFinNfrlfrEPDJuS19cdTAwMWKPJHVZrlx1MDAxMlx1MDAxMudVlqFfgIpjaF9bc1x1MDAwM9KEKKbKtHeHxa3QpfeTKZxMXHI93bj9XHUwMDExic3E5lx1MDAxOcpcdTAwMDd4yG7O8K3UTduFQN1cdTAwMTiuqOpcdTAwMDNW9zSGMuDYUCj8pDebpmmZZlx1MDAxNIdcdTAwMTd8XHUwMDFh91x1MDAxNS6vpPfwl1/y9MxrNz7a04py6p+Y6vSuZfOnwCH5+33B/apvPS9/5j5AOq3kSTqiXHUwMDBieJmAQllcdTAwMDd176/6jlx1MDAxZlx1MDAwNqFcdTAwMTGUm5VFgayUzlx1MDAwZiNcdTAwMWGrmuL3k7zxtmFzr1x1MDAxOVx1MDAxMFxmXHUwMDEwXG7aROn+KapLYThPbTFizKxiXHUwMDE2gS6F3Si5XHUwMDAwjfhcdTAwMDE0IVx0XHUwMDFhf1x1MDAxM3B90NtbXHUwMDFm0MhmS2TO/MFHv/Vaqezj4F1XmNKp8Ue0fZZHM1x1MDAwZTTfXiBxlIZZXHUwMDBinfL+uzZKqjBcdTAwMTRcdTAwMDN11vubaFx1MDAxOWK0j6BhZVE/aeRISYFodHq3ebyYrrDS5PnZRHSASvu9hSf6NumE3kHnrJ6WN79SVjluMvPM+48lOK6W7kOO/Vx1MDAxZEv362XHtL3bWlx1MDAxNpfa8Cfv6Cx642A87FTRXldiXHUwMDFmpUL6XHUwMDE2m1x0TevaXHUwMDFl9mJcdTAwMTSWUm+dz1x1MDAxMFGJPC42c22bbux6NlxiZWZypeVcbu5zWJtRstktXHUwMDE5XHUwMDAwOPHkgM4keKaoXHUwMDAyXHUwMDFlOeVcdTAwMDXvy/MlecNulGlcdTAwMDPla5A8rUN5dFx1MDAxZWS87PbRKtFjYZV7qf+9Xr72beJ/XHUwMDE5iqsl9q9cdTAwMGaTdMnKWTM2n62vhHR335lMylxyXHUwMDEyXHUwMDAyqFrU88HnXFySKUTTvkZvtSdcdTAwMDaXXHUwMDE3gn6nnPiwWYq2aFx1MDAxMjZcdTAwMTdy/lx1MDAxNCs0P6OY2JQnay24eqggI6JcbrNwMlx1MDAxYUPFQEooXHUwMDA13uA+Vs/vXHUwMDFk7Vx1MDAwNlx1MDAxZVx1MDAwN9BcdTAwMGVcdTAwMWanf/DdllN3oblssJE5+cGriq5OTdZyXHUwMDEwb4FcdTAwMWTZXGLTZlx1MDAxY+2SN6FcdTAwMDSl8+M2XHUwMDFhujxcXONJXHUwMDE0O7dB6Su41lx1MDAxYttcdTAwMTFcdTAwMWFE5ZdiK1x1MDAxME2ywMH1j1LFm2BDXHUwMDE3dM1fjkRcdTAwMTkmy5WMXHUwMDFjXHUwMDA2dU5/PNhcckq1XHUwMDFmUYSZTr1uUtKdU1PdZkvZovhf3cr6t1/eZMY3ifdcdTAwMWbMpG9MqoEoK2OyK33jq7v7MM7FhypcdTAwMTZCvPrXTSvpXHUwMDFjM3ugzb1NXHUwMDBi6eejOvDBLqJ1RnmQVsb2dkUvXHUwMDE2wPq2emkmYGROXHUwMDEwXHUwMDA2u6pcdTAwMTnR1FB2YlxiNc2xtD1uvq2bhLJAX3zyNlx1MDAwNVx1MDAxZVx1MDAxNdT31PHxr7+1a/yrXVEygOFcdTAwMWbYfJTenVx1MDAxZnVIaMc856pDc6dQNbCj3oVcdTAwMGKLjll0ObM6+5WHklxy2jQx1F7Ywlx1MDAxNCVcdTAwMGZ9XHUwMDE2XHUwMDFmVVx1MDAxZaeTO+PL4Vx1MDAxNslcdTAwMTmdR9sk07rGXHUwMDExUCXRhzNcdTAwMTmQjpHn4805ejDFilx1MDAxZd2kpVWgl0bCXHUwMDAwj4Ih4lx1MDAwMyw+/Vx1MDAxMyvxeVx1MDAxM0lcdTAwMTVcdTAwMDLQ+H+YRDbc81x1MDAwNpGCXHUwMDFl7aGJv1NqXHUwMDE5Q0OK8FJbXHUwMDAwPvBcdTAwMTcmXHQ1/NzCMV3lk77jxnRpXHJcdTAwMTbuUIhoa2GeV6j5QU9cdTAwMDA3T/1Omb6wcjCTs7bJpc5bdNqt8Vxueam3YPLOa+n3IXpixiB5Ib4loU+fN/f+XHUwMDFmXHUwMDA3XHUwMDEyaZ+ubuz1gv82NWl9Nkf4uWI5lJaUWFx1MDAxNdv3Kr/F4OSWXGbDvY/ArlH10Kn1oWlbezS8XTJhrWbALoRv+1x1MDAxNvM52pWK+th8gFx1MDAwMFx0+1XmM1xuXHRcdTAwMGLrXHUwMDFjskRn+idcdTAwMDFKklx1MDAwZUVcdTAwMWR3Zs3CXHUwMDA0bFnLZFvhXHUwMDA0XHUwMDAxfPjv2kTpezR968Wy/Fx1MDAxMy/s4rBWtoaeN+d0dMghR1DaXHUwMDBlSG8l662VXsaY4INmTZn725Rju85ScVx1MDAxNCrnwPK18ND7q7uAgqDnXHUwMDBiplx1MDAxYkx8e49VoPf3e1hmxDO/90lcdTAwMTPHhZ6ffG5cdTAwMTLFQJyRQ85lfkR+dUJgQFD9u1x1MDAxMYB51P/GmO9LtHlccjJR879/z75cXGaEXHUwMDEwarfHxVx1MDAxYbCos1he40QkI8ns9FxyNF3E1smajF6QNTtv10qN4Xfc0tna0bO9gFx1MDAxZKmCusxSUm1cdTAwMDPrXHUwMDExKlx0XHUwMDBiSEtXXHUwMDFheVxmMmApk7rjR1x1MDAwM7JcdTAwMDPNUVxcYPhpe1x0YKxcdTAwMTJNXHUwMDA1XHUwMDFkXy+A4i4nXHUwMDA2/tEz37ioqq+dQ6I6I87+5WiszMpcdTAwMWU6nzo3l6LOhLE1XaJO4UhObZi74IPrz/xcYi6P9SEgYchN58RcdTAwMWGs2kRcdTAwMGKw8Eawf8gjjHmc4VPJ6PEoqPv9OidQcEB8TpmGXHUwMDFmwFTs5r2Uy2B3XHUwMDFhlLNcdTAwMThcdTAwMDPVMWhu0nwjbG/Lhr814WqTtya0Nqjz1+m33lx1MDAwM8uyluFFq16VsSD8xVxcK/CQWXDKnnTYO8KtnjhcZopcdTAwMTdwmJtJuPVcdTAwMWG30VxcrDtm6eRad8jUQmdw1ieylnRL5r6NXGbVoVx1MDAxMdkpcSpQh/xQx8ntVbLdXHUwMDFlot4/3pnNcW5OlDFjxPt/6SzpoYg372mQ1yfxXHUwMDFmv/ol7m9cdTAwMWHxft//UUTK6JbdbPcmK0/24Fx1MDAwNVx1MDAxOFx0XHUwMDE5ZG5D5yM5x+KryHLU0vphnFnWrImUXHUwMDExXHUwMDBm/1TL9fWuXHRSnUuTepVSXCLhIc1cdTAwMDLhq30uN24pXHUwMDFinTxbe46sm3K061BTRm2P4DtVzpN/dlx1MDAxOCNccrHobu/yXHUwMDFmvVx1MDAwMHHH11x1MDAxN+9orvHxXHUwMDA3f1wiWy2zs1x1MDAwNtKeJlPG84HoXHUwMDAydLSyJpL3yptcZm5cdTAwMThCT6OJ28nmQHc+V1l4iuSTNMpbOY6lrbxApHfOXHUwMDE16Fp6lqphXHUwMDE2ioo4a6tkyftadqAlhl4u+zkywFx1MDAxNpRFVX73XFy3clx1MDAxOaJYaVx1MDAwNv/Fc7ykXHUwMDE5aFx1MDAwMdees15cdTAwMTXxj1NcdTAwMWFTWOq2XHUwMDEwJuCIwJ9yYrxP+pjx9zW9KVjjwdXb+U1vbeXKklx1MDAxMSmGXHUwMDA3zGwm/fZ5v+xRKqObXHUwMDAzuFV3tWhcdTAwMTDwSuVcdTAwMDFcdTAwMGYmXvwwPFx1MDAwZlQ+vUZcciiZTHZoeFx1MDAwNOWk89BcdTAwMWFlz2Pg21x1MDAwYpezxcNbOYtcdTAwMWHqrbvFXHUwMDAycMfd/9VcdTAwMTlftlDay01uXHUwMDA0nXjbPzG9clx1MDAwZkbStE/0cOHIgJe6VZg7nz6198NS4Fx1MDAwZj9zMvw+V1x1MDAwNMS3Q1x1MDAxOVx1MDAxNOxmu2v+vlxczKfngOajw7+/b+kzx2Ru73XWl8Gmr8M26SeayXLZnG9Kw1x1MDAwN0TbSMFroVx1MDAxMCq++Vx1MDAwZaIgXHUwMDFlok7kb0w0XHUwMDA2077z3a2I3eAo/+jB4atcdTAwMDen9MHIKMP5XHUwMDA31GyBXHUwMDBi51x1MDAxNtdcdTAwMGLm+ryyXG5cbrlmmFiiuoaTli0gj+iS1O3eNjuffTz4XHUwMDExfVx1MDAwZUJcIuXFq3bt/TD9ybLWUjvYtVx1MDAxZI7jvb3NhoB2TJfB0KCH3lx1MDAwNaDMNCRcdTAwMDHu/8WXMvlyQs+3dlx1MDAwNtt+uIxzWjLjXHUwMDA1XHUwMDAxpUS0g/KrXHUwMDBlXHUwMDE2Y037mZtYt2jVmFx1MDAwZVZjOi5euFneZGVcZvi4bF9cdTAwMTIpZVx1MDAxM+lcdTAwMGVsiFx1MDAxNlx1MDAxOVx1MDAxZt64+on8W1x1MDAwYo2PK8mmOHeX0lx1MDAwZkbpJc7TXHUwMDEz7Vx1MDAxZDLEMckq9cGi0zR3XHUwMDE0jljiXHRft3/qxlx1MDAwM1TFtIjrz1x1MDAwZiUwv7xBKTXs6NDwxmQ+XHUwMDFh1eVzqWeshd1jXHUwMDAyaOrQXCKlY7BhUbZ4y/GsXHUwMDA2XHUwMDE4wMBTXHUwMDA2uJyTcyjv4VFTUSM9kdLI65FcdTAwMWSfXHUwMDAwZGQplftBQrk7nChcdTAwMGKRUWconsCEfKyeZshbtHCbsKWoNdvE+1x1MDAxZo3f+z9FbW7gqFxyNn71wFwiV3JcdTAwMTP28NWuWVX4Ylx1MDAwNEv0u1U4rWjrq9mxXHUwMDE2dYPadlX4UyhcdTAwMTVt3NrUz+FcdTAwMTTVu+DOXHUwMDFmjyfmxFx1MDAxZpdHt6mTNlx1MDAxNZvlilx1MDAwYu9/59ah31Yo51f0epOJ0KBcdTAwMWZru1x1MDAxZKx0TM717t8zPexcdTAwMWZdcsfs5OjfXHUwMDFiPiE0MP9yQ0j0+XRa+zAkc847dv3J0K1e5JwplYLJNcetXHUwMDAySbS800U0RFx1MDAwMuRNXGb91c37YdxcdTAwMWNGRfam9W5cdTAwMTKn97Qxx2FcYuff+OJOc+DWXHUwMDAwsvVcdTAwMDFehP1cdTAwMDGuopXP1Fx1MDAxMH9cZjZPTrn2oM4xgPTTR7GCpMs/fofffO/2u8zW5lx1MDAwNzb9+F13cvKcsW/apMzB897PnUephFC0jN1cXFRLPqfenIC2tFx1MDAwYlEnXHUwMDEz7nx1XGJbgsfMXHUwMDBlzC02S/78alFDj4WbMUQlyX1KXHUwMDFisjJxSVx1MDAxNJhTXHUwMDFko99mXHUwMDA0elwiNFxmm7egiMaIzYHMT/85VymdwVtoJovxm8D+1Ls7iI950puttH+ElVx1MDAxODz7tFx1MDAwN0O5w6RaXHUwMDA3tTJcdTAwMDE6UTmshzlhNTKp02dcYulawEFcdTAwMDdDvX3OcW1pJiBr/KlMQoi1qsSz8CBmw/WWXHUwMDBmsLVL51xyoFbK3Fxu5uH1gFx1MDAxN0CiXGZcdTAwMTcjcEb7JG5cZoqKXHUwMDA1vWxHWqHWXHUwMDA0r0Sb/11/yFxcv6Sj4b1p0Mf/rT2oR9NcdTAwMWWzVZtcYknn4qJcblx1MDAwZVdcdTAwMTm1mjpdJfdcdTAwMDdFqvxcdTAwMGU8haKft3L63iviW1x1MDAwM1x1MDAwM8O17K97XHUwMDBmJ0OppFjHJZ6mki5zL9pcdTAwMDJcXFt2O1x1MDAwZm1GN5JccjJuisyuzMjeXCJS3EeXx2luXHUwMDE4tSp7YEGa/r2PweB891HF1Jvu/O4jg0qpUXTClY9cdTAwMThcdTAwMDDf+Zb9eCAhJ+1cdTAwMTUms4Jb7rHim1fML1DAspP5aD5cXL5or95AX4s1XHUwMDEwsp58N+JXLkRcdTAwMTiH2jfWy4AxpuStK8ThTrRhheKAXHUwMDAzLm9cdTAwMTYoyChcdJCgq3hcdTAwMWNcdTAwMDRcdTAwMGIlXHUwMDAxOPY0VIWs4P4j/1Mvdlx1MDAxZPZbL1ZyjfB/3tQm2Vx1MDAxNjFnbfrjXGJcdTAwMWRDKZ1k1ld2NuYpfFx0XHUwMDBicqxpVyPl8Vx08ZpisamOXHUwMDBmpZQr21wi+VxcfFx1MDAwN3esXHUwMDFjRjvyyVxyXHJcdTAwMWVbUW3ZkmXLwedccvS55VB5J+MqXHUwMDAySFguNpSlI2VK0jD+/PQvi+7vXHUwMDEw+bvGeWtr+qv/XHUwMDA3q3X/5Iya3sRE45dTSihj8NyHq4F9sFAnMSP0idL7XHUwMDEzUKFZX1KXMDVzwGfzfZvYZ1x1MDAwYnY+yFugu7fawN2DITx0jHDokaaJXHUwMDA1M+7TfrhcXLDffFxiL9ZHSs/GXHUwMDEyWFx1MDAwZmtZkPAy+c7AxuhcYkyGSd7pXHSL14io/5DUXHUwMDFiTFx1MDAwZfqmL1wih1x1MDAwNX9wWi9VbMyyh5pdoqZnkCNqXGZcIsLPXHUwMDAyy9CscY5cdTAwMTLT9azr9jTy+9vPeFxcXHUwMDA0+lHsXHUwMDEzhG6ZcNFcItRJSlx1MDAwNd1jXHUwMDE4W+mbXHUwMDE15Fx1MDAxMPKmKFx0XCLojjHpXHUwMDEzSTBDXCI8YGtzo1x1MDAxOIlPyvZcdTAwMTCoZELepaB5XHUwMDA0+nP+J4/cNmLLXHUwMDFmrp/8x/XlXHUwMDFie8moTUg59lnEMJ9cclx1MDAxNMJcdTAwMWSAXHUwMDA3XHUwMDFi4L4x7d7SK8i8JlxcwC1g48uy6eqJXHUwMDFjTFx1MDAxZTx8Zk6c4Vx1MDAwNe2FenS11d7Xzz6lgVx1MDAxYlx1MDAwMEp/3SqBiejCnJA+wG75QFGF6lx1MDAwYpaLXHUwMDFjMldkmDGzXHUwMDAytf7NWaSvXHUwMDFmS5lkXHUwMDEyW/iztoZcdOm561x1MDAxZYrJcIdBjOhcdTAwMDazXGaoWMGbffg5/XggsCW8dGsheXBY6Zd1i5OoajqbofSeXHUwMDE5ujx8889cdTAwMWJP/Su3pTCevFx1MDAwN21cXPmMa1x1MDAxNbFbe0s6Vo9cdTAwMTP656aB9Fx1MDAxM4+9XHS4JXCmYdw/+/Wy5d91yTYxJH/WpdCjwPtGXHUwMDEyLP1cdTAwMTiW020qxJzb8vMmlipcdTAwMWEvl9uh3sp69e16XHSrXHUwMDAx1ETvXHUwMDEyZdotOvNcdTAwMTNccvbz5UhcXPpIiE2eMFKyOz+EbFB+ILTVQJRcdTAwMGaQQP80dmibeyxlxutOKnxcdTAwMTWpb0jqTYBiJGfX10/8N2/Zv7rogW9UJFxmPyEz0TlT8Fx1MDAxMvUyXHUwMDE5NDZcbkjWI1x1MDAxNlx1MDAxNpXEQVx1MDAxYivs6VmRaZdz8Vx1MDAxM4506r14XGLB3zrqpnrndlx1MDAxMG1fd1x1MDAwNFx1MDAwNrg8O0FNqlxcocxtx0BUapP7tlx1MDAxNKGqXHUwMDFjtb70XHUwMDE5XFzBjV+k9ibINLicXHUwMDE5XHUwMDA3Ueblo/0ne6ycXG6I0rfY+r86Jjvzr+85z7Ci+29cdTAwMWTTNnU5YLParGfhnYWEbFx1MDAwM8jwXHUwMDA0YNUoTuyySfBcdTAwMTJesb6rXHUwMDE39sxv70MsvP2w8Vx1MDAxN6P7XHUwMDEyXHT8W1tmnivTJMNjvd+zq6dyXHUwMDE4o45cXJJcdTAwMWUs1/b7ecEu6ceam1x1MDAwNCQrkILAvXr7RfZvXG6ItYH/a23Mb43VmVdU+LM2VldcdTAwMDJtSfbTQ1x1MDAxOY0x1pndssZcZseXZbZ59PHrjEtcdTAwMWXOXHUwMDA3XGK6zK7vmDVccpdlzlx1MDAxNV+axK9Q2brjI3RCbve3Y1OGN3Tn4TysR+dcdTAwMTB2z1x1MDAxNa7n9HSb3O5GXHUwMDE5XHUwMDA02Oj7JPjINlx1MDAxZFx1MDAxMTigjn9TU1rM5d9792GFmn5VT8yi0lx1MDAxZjx8t3q54lxcqlx07edcIoO4Ol9K+L4k5uXwXHTzXHUwMDAwPULMZ+hcdTAwMDLdk/O2X040OVx1MDAxN8vPXHUwMDAzyUrQcKdfgVx1MDAwNo9cbs2jyd9H8GHeXG4ro0CFMZ7i8G3aXHUwMDE4N9g5LIKm9E+DXHUwMDBiaFx1MDAwNsyE+ZwjX1xu5YbwqVx1MDAwN1x1MDAwNvxbXHUwMDA35FP7W1x1MDAwN+RTZHDH3/OKsLz9XHUwMDFhXHUwMDA2KLooaVkqalblnWBcdTAwMDGrYv/sinBcdTAwMDey+zikiHrMz4oqy05//9R91YfVyd2dQ3ST46iPrUFcdTAwMTUjXHUwMDExd6KNXHUwMDA1WZoygMlcdTAwMGZVUGojMVBcdTAwMThKXHUwMDEwSkA7YPjkniSXQEO+WH1kzFVcdTAwMTHSIXqj/9TRid86Or5inJr8nEvdQlx1MDAwNGNcdN68XHUwMDEyXHUwMDA0ZfI2hJ7smH2y6KQoXHUwMDBi7lD1iXWZ99GvZyRcdTAwMDZfPVx1MDAxMN9AqeG9c8jFfGvf4miHgMdRcl3YR4/IjDJKslx1MDAwMdpwkT0sgOVcdTAwMGXT8NrCpr6PctqTN2HSvVx1MDAwM/TYXFzCudRcdTAwMGWfqZCA21x1MDAxN/O8mOW/18n92LpkWouU/9i6LI+3P0RTuPRccql/ePTmwmNI2Vx1MDAxYVb1t+uIZbZ1aL06heSFJ29cdTAwMTHD/MNlMlx1MDAxY3Kfzs7uXHUwMDEwXHUwMDFlXHUwMDA2rTnSUFWcXHUwMDAwMbRcdTAwMWLa1jfxoylJcSXMcztcdTAwMDd93erUasVcdTAwMTly3n1iNHLBYKi501be7XmGXHUwMDBiwlx1MDAwNDzqR/pPzVxivbKbcs3kR7qEPzV1vFxmJ1xy82BPleF+lNVoXHUwMDFh1aeTfPpcdTAwMTZcdTAwMTVcdTAwMWGdXFxcdTAwMTZccq1ee1x1MDAxMmR7i9b8N0+ikjVcdTAwMGWjUM5WQbskXHUwMDEzdFx1MDAxM01M9juDIbzkOVx1MDAxOEBelKC8XHUwMDFhXeSYcMUx8GBcdTAwMDFOuWVpXHUwMDEwXHUwMDFjk7qYOONu5V/1g0NcIm/Oyq3RXHUwMDFkNN819r5fnqKrfDTTXHJcdHZZtrh1XHUwMDFmT2qeebBikSBcdTAwMWH2ksKDtlx1MDAwNFx1MDAwN4qjeieMnFQ8jSGy1Vx1MDAxMy2MXHUwMDEwldeA3p3nIW9cZuZ8a08pXHUwMDEzvdRNwVjQxMKBXHUwMDE0XHUwMDEwXHUwMDFh/1hcdTAwMWGGNHsw5ORWVja58p92zpxdzTo7R7h/+Fx1MDAwMTexX57avG/6/Me+avlTj+5pNqVcdTAwMWJoXdIuK+vMSFT2q6XH8EPuLuWNWVx1MDAwZi4/+lmTJVx1MDAxNHzPnGugstmqXHUwMDA3UFuH3HKFf6XVS7xcYkBs3zqDsnCunfPCSiWTv1x0nMjPosV4n0o+nzIzuTuxm42BWqJNXHUwMDEx7d818/Wnr4FfkOZY+59cdTAwMWP41utW25fXyLrMiSfz8uFesXfMN3Z17LWB48dcXKh9f1x1MDAwYlx1MDAxY6Ew5iCivD3csLyPSc5Xq1RALUvxejiYu4h80sfNh91JwnuiXHUwMDA0vvk6zId6XHUwMDE5LH83RudcIq5IqHbUTb/js0Sw8Cuo/W//rvL9zcVcXO7t41x1MDAxZu4w6s3AXHSp0Km30HROn1GDXHUwMDE1QrXwgrdcdTAwMTRfXHUwMDA38CZ4+GXRXHUwMDAxplx1MDAwYiv7WJQ1XHUwMDE193Xgk9Yv92mRaFxumPiJs2gwZoRuo+cq2Fx1MDAxMDLlxfZb01x1MDAxMNenTqtjpN/aPIe4+Vx1MDAwMVJcdTAwMGVcdTAwMTHm2dBytZBcXHZcdTAwMGb3XXJcdTAwMWRtNFwi3GeMfV0l0PzfviD1ty9I8Pvi/OlzsVx1MDAxM/fixFx1MDAxN9rrIVx1MDAxZVx1MDAxZuJcdTAwMDTKjrqnZ5Br4flQNbBcdTAwMDNQhco/d3ZZilx1MDAwM+LPkitcdTAwMWS2jI6O1MBlw8GVoFx1MDAwYoZE6ZvDiXqJXHUwMDA3xr02X8ZM9SRcdTAwMWKTlKKSW4R2SobGglx1MDAwYrF6XHUwMDFkKcDtx/Dyua5b1VuhbtH1jz52RDmkmUf3x0+NSbs5vzE+VVD0SvUh2vyt2eLJwF1hZi6ciqqU2Vc4j7dXlNosLeFcdTAwMDWmxaMwSqyMaaWZMiy+keI8cY24jvRcYjODNVO7XHUwMDFkXHUwMDFkTe3wXHUwMDFlXHTwuHRSQlx1MDAwM2ospFSmT1x1MDAxOL5p5979uUvz/+npXHUwMDBiaTlcdTAwMWO47ZFcdTAwMDTQ+dUnXFzt2iHMrZx0JFx1MDAwZm5cdTAwMGZS3L7jdlx1MDAxZVx1MDAwMJJ1XHUwMDBlXHUwMDExUdJOXHUwMDAxWzRcZrjz1lxcWVxc86LmuijnTTNcdTAwMTF1h2UxOVx1MDAwZdNcdTAwMDacNPnLfd9xx+ZeNSMx25RcXHs6XlNcdTAwMWGEZ05FXCLvg5X4xjN3TujkOlNqPky1M93f2Ne4Qkm/hrO3IP/HNbFyKYN6ufpcYllEXHUwMDBmnlx1MDAxNaq5oVSDZ/bYmlj5XFxC676u0Fx1MDAxNZEzf8krmq/GwKE82uykvXGQWlxuSVx1MDAxY1lcdTAwMGb6+6ScLrGoXFzaN2ykh6PzK8qyM/jIvZSSmbRvbGCnpESEKYNcdTAwMDCNncU0QazIT1x1MDAwYm17RFrMX7oz4m/dSVNad1x1MDAwMFx1MDAwZvuP77VMszJJXHUwMDFh9s1cdTAwMDSZXHUwMDFhVJKcXHUwMDA3XHUwMDAxVFKZyTmI+plft4CXXHUwMDE4XHUwMDA0aC5X/IRcdTAwMTAp4fE3l290zS/qXHUwMDE5f9A5mjdqzbln2lx1MDAxMlx1MDAwNFxiXHUwMDEyMW25+NtaW/4xXGK3Vn65XHTKXCK0tnXHe29Ci4SU1rni6Fx1MDAwMVx0XFz5hLhcdTAwMWRsenezXHUwMDFmXHUwMDE3bKNcdTAwMWLRsP/j2dUvz65cdTAwMDZiV3/PPvkygjxcdTAwMTXqenY5k1Y/mKeTvuvQL1x1MDAwMFx1MDAxMvLHK6dcdTAwMGLokT9qcdLGPq68hGHlXHUwMDFiel6em71T9+3y50MquEXhtCe9XHUwMDBl2VnaeFx1MDAwYnFcdTAwMDDSkX2VO/fPs8ZcdTAwMDLmXHUwMDE0su1cdTAwMTg22sBWtahcdTAwMTJ0d2DtPVaLhDmaYfLw/5fzViZk2JNe4eCzvegvvf1yv6h5+8ZcdTAwMTQ0N3XHYOnqQ/KwXHUwMDE0dlx1MDAxYuOY992ld/FcdTAwMThSXHUwMDFlbpydhrexNt1o6UxFvVGgUUFpsaL1t3VcdTAwMWWlictrXHUwMDFmd55IQ3Q3vZI7lX5CP8PxXHUwMDAyhVRKt8icsFJn+thdbcPTjauUIUdXOILrIG2cXHUwMDFkXHUwMDAy2P63f95bcPvnPHo5zf6ebXNcdTAwMWMjafNcYj1o7stBXHUwMDE1wFx1MDAxNN5Ec+uDXHUwMDAxtU6or5It6lncxzpcdTAwMWG4aVx1MDAwZXXnw/JFhWzGqHc8QKhcdTAwMTB/a/LRwuLnradom8FyXHUwMDE3S2c/l6pD3D6QsJxcdTAwMWHaXHUwMDFjkiRcdTAwMTlh2ibAXFyM25N/XHUwMDFk4+7e+kSOXHUwMDFl8v/2saNfX72SkJaTffif2sPlsMPaNy7dWTV5Jk8skPNcdTAwMTPu2DW5cus4SenJdi1cdTAwMDWvXG6ls/tcdTAwMWHfnIgsXHUwMDE5sVx1MDAxMVxuXHUwMDBm2lx1MDAxN/gmpCBiKjxXPGlcdTAwMTFcdTAwMDdspOPHiqbv2PlcdTAwMWNA3rNXTUFrdzG646JNSFCTXHUwMDFjlYFcXCpPgVx1MDAxMqMxsiDq8+5KoTiGf+pyYGWR9fT5v1x1MDAxZTBcdTAwMWFnLIWYjYWRStlw/dxlTVglT+qrT5noeVx1MDAwYlx1MDAxYlfw4l6Y0cn9yU3dl+Pw6P3HzfdPmVx1MDAwNVx1MDAxOKRUU1lcdTAwMTdwvaup3f/pq57mx4trS0xCnsZcdTAwMWH3R2RccvDneJz8Xl3sasB7nuqvrH9os/1/PDG7LVx1MDAxMEJcZuJwJs7/5GhYlqVcXOnshlxuNnqINbhcdTAwMWRcdTAwMWP3SSyWXHUwMDEwbIVcdTAwMDZ0wtnPQIBv60I9kJhk8evms+y3j6WPXHUwMDA1bSq9oYpcdTAwMTk2vcrn8NTtrdyetpS6XHUwMDEzjXe6XHUwMDFiXG6YwFxmzVx1MDAwNrntKrFZXHUwMDA2SVx1MDAwNUOgbM+C2Mllj1hIk/N/9X3lW1/QXHUwMDFmLfHIX99al4ryZCQnS1x1MDAwN6F0P1x1MDAwNmWjOK3PUJSORqnPfmJ8rW0knVx1MDAxZOpcdTAwMGVgr76J626bXHUwMDA3XHUwMDA30PvmmFx1MDAxYSlg+ouWSJbUIFx1MDAwNfExXHUwMDE1127/i21ccnVWTzh0i85jXHUwMDAxx9FHmVx1MDAwN0b+XHUwMDA2XGa2p2nrm49P8v5cdTAwMTRGr8bf9eCYh7597ESB/fYsoUc1fIyw9l3tXHUwMDEzmfSB8lXp7OPrpr9cdTAwMWJ1Tc9lnTV9n/vpWbozXHUwMDEyUlx1MDAwYjUg84qQt1BcdTAwMWGYL9gwoYrhw3RTOoVUbT9b0lx1MDAxMJLhgfNAjlx1MDAxMabCllx1MDAwMjIxQ+MrXHUwMDA32jv/YaBYyZ/3eYpxJuCJXHUwMDFkXHUwMDExlJVcdTAwMWWzpVx1MDAwNVx1MDAxM7hcdTAwMDcw+9dZ5/yj6YlcdTAwMTmLpPLbo/SQXHUwMDAzbjX2JUXNpvUncqpMOlx1MDAxZGbN7MDIXHUwMDE1UNFSkOz5JIS9JZ9cdTAwMGbdh8vyS1x1MDAxY1xmNG2/d7Klxlx1MDAwMVqpiOZHXHUwMDEz4KmXzVx1MDAwM/ZcXJOfMkW7gX+Mb35cdTAwMWZcbqTTLmbEQdBBPi5cdTAwMDJpZFx1MDAxMr95cEApk25RXHUwMDFhXHUwMDFje52CgSYtNjX5P23PM1x1MDAxZVx1MDAxYtx6pVx1MDAxZVx1MDAwMYs6eE+lNdZ0zvc7gkFRKpuUhZ6S78NthUE181RmKFx1MDAwMsS9jtFd0rZymN9axFx1MDAxZtXpmlx1MDAwNFTvec5S7FxcuilcdTAwMWLANU3sI/AlUTozMYi+quTxXFyzZanDXWqytFwi1Fx1MDAwNVX8YO4121x1MDAwMN5NQsqwWGmJln2gv4JYXHUwMDFj1YNWIC+S8OdvnjlYWsFcIj2i/OnTu4xccn3/ZlB6p/WUu7prx4lA5rgsQT8+w1x1MDAwM1VK11x1MDAwMj1cdTAwMWZgytZcclx1MDAxM1xu5ZqUn5h5lWRjL4mFXFy/uoMveXJ+S5Xpylx1MDAxY1x1MDAwMVNK61qr9V7NtOp9XHUwMDEyV0zKxJCSQaSP/nfdX/nW/Y1UXHUwMDFh3tivZpaPpmU/L+FpnfbVuXtcdTAwMGK9NN+iXrd8Klx1MDAwYlx1MDAxNsRcdTAwMTFl1Vx1MDAwN3ZRKpqltJ6WdC7UqISwXHUwMDE0nimSUsRVcFx1MDAxM9VcdTAwMDd0a7PAlvyxyZaBSN36SF7zM7F0J/RcdTAwMDWQLarpUX5cIvzVXCIw0mjobvxcdTAwMTMjg6mKWPqnx1Slv+eGSEAhZ+beue3xXGbsTl6A3lx1MDAwM+SNjlFxp9Vh1dawlVx1MDAxMdK/PammK1Uh9JvPXHUwMDA0K+1cdTAwMTany6JcdTAwMGbV4kMkXWpcdTAwMWO/XHUwMDEzk2Rb4ff2SsyKXHUwMDA3dsyOzlxypsuuWVD2gNHCnlE/T1xmPn77b1j3rL9nXHUwMDAwVGb98bV1e6ek8F1z0vAscFxiZtzEJKNBcCeJ8DlcdTAwMDI26Fx1MDAxYzQy7sa1Um04YbhcdTAwMTZcdTAwMWbfXiCarUtcbixOVVwim3rdXHUwMDBlU+VcdTAwMDDqXHUwMDFjWv6Okjy26dSBlNGK0eItf4acPu9EybpcdTAwMDKASccsvfXPqr+3NMlZw6xcdTAwMTg5UiNEMLf1dVx1MDAwZd42xEQ5/Y8n0MM3dkUourVcdTAwMTbP/Jzlzu/XdD6711Hk3HVnXHUwMDAyoj6i5Vq/eOiJPFaMbPnQQthHNlx0vzlcdTAwMDd65SXE7Fxi7ZlcdTAwMWbRot1yj2nwjTL5ftJF7P/mc3XuxYukkXIgrTviSSyMd9KRXHUwMDEwXGLG1M1VhtT4XGL/nDWxd1x1MDAwNjRcdTAwMGaag99cdTAwMDFH/PZoWrhW0l6/sK9Ec/1nwce4XG6R16RQ8Dyg+1xiXHUwMDFhXHUwMDFiXHRohpVcdTAwMGXfPlx1MDAxYv42bklcdTAwMWPa+8Z4/Fx1MDAxY0iqLPTF1nbri1x1MDAwNbDM0FKj6KxcdTAwMGZcdTAwMDSq9eZcdTAwMGWeXHUwMDE4kXF+uVwibd76M35cdKTsylxyoNz80lxcv63XwcBcdTAwMWIg+5c2/lx1MDAxNlx1MDAxNFx1MDAwNu9NVTBJfn0vZyxVeJ+qeEXzLSnfRvB8cuZbc8adgiOlT6NcYlxitKxid1x1MDAxNZPEXGK2vtdI1Xap583jIy81Y9Y93Vx1MDAwM1x1MDAxZtp45E0+urdcdTAwMTNo/FJcIlBcdTAwMTVcdTAwMDFcdTAwMTiE0CbKXHUwMDE5PtJJXHUwMDBm6Io6MlZcdTAwMGUpd08oXHUwMDA2iEfvTPNcdTAwMTj/zak+rnNzqulJWfDC//QtXHUwMDE5dlx1MDAxZlx1MDAwNIBcdTAwMDbQaGykjvCe8ZbEXG7uechcdTAwMDWtxL2Fhrrzes1cdTAwMDW6zcetXHUwMDA1LUihaFx1MDAxZKHPNVxifVx1MDAxYeTB+sPkXHJuoGzx5ZLOxOCvWVdSpjmcln1cdTAwMTHcfC3Mee47XHUwMDA2hPb3kazQ4idSb1xuSFhcdTAwMDZn9n92PUvtiyMxLrUp9PvGQ5Ux4S2qXHUwMDA0eDioRHnDTJNcdHLlqcCF9TBIRYjjRkv5ifp7JqozMsNmXFz0XHUwMDFku7g5XHUwMDFmQT+ybz9Hr9u8N7EmY+n9yC1cZlx1MDAwMXVHj7Xs27JcdTAwMDWFpumS5jveXHUwMDBl7POEXHUwMDA1MpF7t02aQ3u8XHUwMDFiMD2XfTLg78PKfs5cdTAwMDX+0vDxiPTHn5mPWId4soyedMqdh/FcdTAwMWHb9aBcZkd7IDRFKvvDY17u1uCf63lcdTAwMWLua4Pkkku6XHUwMDE4p1hdJjqYlXs1L/lcdTAwMTDxx6Y5WvjLMW2XqS0oj1x1MDAxNzZ/e0XRjztTv/di6Fx1MDAxOTYmXHSm3Vx1MDAwZlx1MDAxYujqq8leXHUwMDE0XHS+vX9qWpH/oenejlxmXHUwMDAyn799w4tcdTAwMTCzefxCXoGuY1TeTqScXHUwMDA1L/D9zjWL5lBcdTAwMWRcdTAwMTP6Wr5cYiiswIGHfvtBbs1PStbofqxmnUpnUOyWsOJlt2DBXt25d1x1MDAxNHDHfz/L2VZcdTAwMTCnIa1+XHUwMDFjSemaXGKBPGBVQFx1MDAwM4P4uZxITjm4cJxu8IGlXHUwMDE2+Ps8bFx1MDAxYtnvLNT2kpzoq+1iXHUwMDE14tP3kvfRIaf0VW5yWVl8rlx1MDAxZlx1MDAwNN1cdTAwMDa8mlx1MDAwZqiw07T5XHUwMDAwunaOMerbl8TKTDaSg5Pz+KMgJlx1MDAxMZ1cdTAwMDZg5CeuK1wiKVFuXoiIynmjP4GqVLBcdTAwMDRQXHUwMDFmvp/F84ujWsunXHUwMDAzoLxTevY3rXNcdTAwMDV7cFx1MDAxZt3TXFxyUtVcdTAwMTVcdTAwMTihO1IqyoVcdTAwMTQ4dlx1MDAwMFx1MDAwNUNY7fAmj8hOVO//2+uGrr8+S1S/80kkX7Lbs2xpv6mM/amMgHf2hYl+PpRaf/juZj5VXHUwMDA3W4haWcNcdTAwMWRbXHUwMDE19UrzhSVLXHUwMDAyzYi2pdObxHiDyzJYrZXp92xAX5jG870l5MyqXHUwMDEwe2XbeVx1MDAxMNqV4zFU71x1MDAxNmj7mVx1MDAxML++uNtNXHUwMDFik36KXG5cYi2itOHfmtd2gDzGhv16rtx5Y1x1MDAwNJvSVqlGo2QhXHUwMDE58pZW+Fx1MDAxYs//N//VfZ/MJeB8X5Hkz4xQ5rBcZsJhkLqzUMdcdTAwMGJHX/tcXFx1MDAxNVWDplx0XHUwMDExtVXOhlJcdTAwMWUv2+BcdTAwMTPyhSFi/C+3c1jTzfldIXNBUVx1MDAxZqqPfryZTXqWjGf7seioJEhZXHUwMDAwP6aPS2rWhHJ7XHUwMDAzi7S09c9cdTAwMTB3TsJB6DtcdTAwMTcrh1x0N/dXXHUwMDEwf3oyWlx1MDAxNJ7kXHUwMDEzVNuc3Y/wzeBGXHUwMDA3qJw3Zvz5copDXHUwMDA1Ou7AnHmK2Odf5+iRfXztXCJlXHUwMDE3Oo/Dtyf6XHUwMDBlXHUwMDAwJ7Sihe5cdTAwMGb3XHUwMDBlXHUwMDAwwtSikWVcckVSrlDrK+Y46+VqXt1hrfw3R3ds6sVystdcdTAwMGKsj8Xhp1xmfLldMIUlM4DnWztvkMUlnml8XHUwMDE3o12Aw+DbeDGJzPLWjVx1MDAxNr+F9ZtkuTJq9YlcIsDqtF2aXCJcdTAwMTLCWWBEYzyV67696tXnpTffwmY7ran/bFqdUFxu6iXtXHUwMDEwpXDfXHUwMDAxtFpcdTAwMDdHwJ9cdPu31v+frZrvbJncnST8tZWa2S6LxMJcdTAwMTHlYn7FyuWeqiq/n+PLXG5IZmdSVL/EXHUwMDAzQMaeoqPoKO23XbOFc73z9H2ms621Ylwi8Fx1MDAxMaPWwaNonLi8U2JAw6+ycUmxXHUwMDBm7Fx1MDAxNkn7ILKMaOKZs1wiklx1MDAwYrxcdTAwMDCI4rqWdFx1MDAxZm5okPlcdTAwMWKPScdpyzOSgPhcZq/MNWCqsX/tt+Q+LDS304afXFw/14/cYJA0j/rmqcD787ow//E+zrUmWvJ//c46/9PvnJomntQv+2dujjxDVUhyny+63MNcdTAwMGVNYzS1OzfKuSpcdTAwMDNOWoebXG66XCJ0xNyleJVitlx1MDAwZlx1MDAxZeK3h8JcdTAwMWM5mXe941ujT6FcdTAwMTl0ITP0PZDfy4aXpJrEND5nXHUwMDFjXHUwMDEwXHUwMDAywFx1MDAwNJXGcKk5X1Q9Yrd91LR7e7ii812+lt53SzCWYFxiQjhcdTAwMWIy6tZR369yWeaSNJEvv/tqhsDZXHUwMDFiXHJcdTAwMDKa4G19gGew5PfWPypqlFx1MDAxOJqsXHUwMDFigp3YtvKG9DNcdTAwMWSaibHz67/rtdqf6y2qjah+5lx1MDAxOOXKmnP1XHUwMDBlwVxyd5S24z7LcHWP+OZcdKli95LiXHUwMDFjXHUwMDE561x1MDAxY/YrRpedS1x0hegwbKWTU7JzVtGsISRZnUdcdTAwMDQs293sZbNrWOl5hGdcdTAwMDYmr0VlPKrGM1x1MDAxOZ2lxJtcZpVZKIFannPzUVZelOdpXHUwMDFimlx1MDAwYslmIVYlmmjBnXNhc1JcdTAwMWR/eqRaepPeq95puYC1t55cdTAwMWR0aD0mXHI3LoKQ49aRXHUwMDAw7HrrLVx1MDAwNUxDXHUwMDE2dJR1/lXLs+OfPnvbqIDu1tRyzNt9kSnHsmdXpKCGUXzy2lJcdTAwMTFvLbruXHUwMDAwXHJosbissFx1MDAxMUE7oo37lOqVx1x1MDAxNO+XXHUwMDFkS13ATHzPxlaGZFx1MDAxNNcgXHUwMDExu36Fjvgyw6FtjkGBqlt/XHUwMDFkvEpEurXQXHUwMDFm+0mRPDVNV2ndQp42Slx1MDAxYeIqw7Uh9day7cBcdTAwMTmeaMyIXHUwMDAwboQ9WJ1cdTAwMGV+4uHyfFx1MDAwYlwiKopcdTAwMWF2kuj4caXfJ2dvqnU6gGk3XHUwMDAz7OaaXHUwMDEx0aA5883epzlcdTAwMDbPy5NcdTAwMTXtJKWTj1x1MDAxZa9cdTAwMDOSUChcdTAwMWFsXHUwMDBlJm/bXHUwMDE5bdbnf1x1MDAxYYCdv+djxDS/s3dgfOd/7e2k4mDUVS6ZTiV4h7zWz5P0otOUZi4/4UfvU/lGm2CFXHUwMDA2yjmtUlx1MDAwNlx1MDAwNud4x8XRWH3nQl42VL443KdqfcE0mn9cdTAwMWKKwF3Is8JXvd3oKmaX9zuL+sQ7U2RcbiblKvg9tWqgmD6ghySYXHSvurM1j3ZFkiA1XHUwMDE5Y8hcdTAwMGb5ZIbXrVx1MDAxYZRzZ1b0mYxPom5dNMVraJdZILyVqmFcdTAwMTVTa/arO6XP5TxDLFx1MDAxNK6/6o3NxZR0NmRHeWsy5nuocmKCxSFcdTAwMDJnYOfDM2CyUfIn1n5cdTAwMWZdb1x1MDAwNX31UtTAicA7Sznd6MJcYpRTbTB8cEROf7T6d/75bEJcdTAwMDGBOVx1MDAxMlthsaaDrFxiXHUwMDFmsvaQnlx1MDAwMmBcdTAwMDWPupFyxZUouHlcdTAwMTU8XHUwMDFl1lx1MDAxMqxAXHUwMDA2j+KiUyS4tXzuXFx462lBVlx1MDAwMFxyf6KqXHUwMDE4OVDdmztcdTAwMDKZZTN3zSlXob5mXHUwMDA1bWlAK4ZcdTAwMTA3wFmb1nCwha+PXHUwMDA014REprJcbmBcdTAwMDWyfl5cdTAwMWZcdTAwMTktXHUwMDEy6L8zz56/f7OcQlx1MDAwZlx1MDAwN3D8d1xiPaM1zoVsqzxvK1x1MDAxZWEkXHUwMDA3XHUwMDExXHUwMDA10yzQpFtcdTAwMGJseDfTKnOrieZcdTAwMDJS8FrIemhcdTAwMDBYoqXknW5DIWDgXGbxXHUwMDE0XHUwMDE43JridlgnZ19Q4ZqGYS24viZoc8a4mSBmlU9cdTAwMWZcdTAwMDRC3ri3mdc1vc6Qb1x1MDAxZMwh9oc6N0Nefb5zXHUwMDA2SruUXHUwMDFmUqx0v2na9KWdOFa9psTCrEflyKHKIfBszllgrFx1MDAwMltcbvGFIylcdTAwMDNcdTAwMWOSpvmKtU47mv83L+qj7beHk4f3ILtcdTAwMTMlVskyXHUwMDFhZOHJUFx1MDAxNjpcdTAwMTTpPpK6Zdys2lx1MDAxYqzxw1x1MDAwNyZ3KdcwOe8lQz6jVah3MmLMoVB6zzxcZlwitTa3f0M0wZdcdTAwMTXjXHUwMDE4aF5AXWVYyjl2XHUwMDFkXFyvjVx1MDAxNdxcdTAwMWai6fnDp1xutJBzi317XHUwMDBiI7svw3HG2Z5cdTAwMGVRXHUwMDFhobPlraim4MzLjOVcdTAwMTGqzjTKkouTXHUwMDEy621cdTAwMTaIXHQ3KVx1MDAwM8uIZKXK0Dvs+IzMe5Xr4r9ZJEiTf+5cdTAwMGLxMkw0vlx1MDAwM6krXHUwMDFktlDirlx1MDAxNuVcdTAwMDFHckVvUs6DtvAsQt3Exlx1MDAxN5V3mHJ5WkLyuuxWqUGaXHUwMDA2STpQ3yp7KFXb9SB6+8aL66Rlvlx1MDAxOKHYoJy45ZFtpiusP8bKXHUwMDE1JiHdXHUwMDE5p7dcdTAwMGXyy6GFSbM5tFxi9PphY5pj6y7YXHUwMDAwnsW7o1x1MDAxMWOpKdrwQExJOyM8lMCHRkdjg9JPuipBaVxmmI16fva1e2rbN+n+ySk6XHUwMDFks8LESbT+7n/iRu5oT3Ph2io/i+iHeKvVxErQXHUwMDE2sLfdWL1gtsI6+MCR41x1MDAwNVx1MDAwNWSAfCbIuVE3Z8HjhFN0w0Njeo5US9fJwZTyyVV4bFcszNbnurOtQ1i9kjRMPc9PXHUwMDE1XGJcdTAwMGXzqy2J2i2Bb9PnUSorXHUwMDBmq7RcbttCpdYk2Nhw3KUglriRXHUwMDAyZMa+QFxyZklcdTAwMGVqz0npObx62/SN6SSEU8zDXHUwMDBi5vtSP44mirCAWOT/5obPNqLp/YYjq2u/v+fbNX+rXHUwMDEwXHUwMDA1fPQ0Sym1fbPw4lx1MDAxNV9nXHUwMDFheOmEJd6SzFx1MDAwYq6CKlx1MDAxYj5u6lx1MDAxMujwx1wiqXp0b25APyl13pogzObsYn97zEJb80pxoz8vOf4oJoVBt45cdTAwMDNcdTAwMWKLnYEqtGoz8Uin/sPBkdKdrOx7XHUwMDA2XHUwMDE3n7f4uFx1MDAxM1x1MDAxN1x1MDAwNijsW9frfjnzzIPeOzU/pc/WXfRedre8XHUwMDEzXHUwMDAz77hhXHUwMDBmmvNSXHUwMDBiU4pKXGIpOsG/6mqvn35a35rFO7dcdTAwMDenKF9FKJWhOVx1MDAxZHfqrPTkdlx1MDAwNbOnXHUwMDE1cUhcYpUmy56q6j6ndGFEXHUwMDFkiV2wmzlcdTAwMWMyoVx1MDAxMp1cdTAwMTCNR/7aXHUwMDBi+Xol7PxJv9/95mWNdzC9iz5objzH0LI2LokvfWlCXFyS8Px3ZupcdTAwMTJsXHUwMDExdeYnVby1T4DpYmU/jKTfzuSYvXe6XGYnOevQZ324n/eDd1wiV5+eayusXFz1SlJcdTAwMTROSUGbrpNMg9i0hvSvXi/sZ1x1MDAwNlx1MDAwNseUiKW9hFtYOVemtuA+L5G3XHUwMDA1p5OH2eJFufdWhy1l+Fx1MDAxMvtcdTAwMWRRV5OxcZmKe94hMbiOXHUwMDExeiX1xddcdTAwMWKlZr2S8LJcdTAwMGUxXHUwMDE4L2HWW5W9XHUwMDFmpuWdT0zcXHLIXHUwMDEwLXOMXHUwMDA0s1xcXHUwMDFl+cH7oZ1NNUjUPzNcdTAwMDVcdTAwMDeFXbpPeeYtvDlcdTAwMWOypaJLvVx1MDAwZthX5Vx1MDAwYqExUtjLdkhcdTAwMTLRzLuhrFx1MDAwZZbicc5+jVOBl6VcdTAwMTN2LyfHg1Tlnniqk4//znyTUjNC+oSNiImQ0sZcdTAwMWKe0fE5pT+l6FdsaC3YsXOi+yR3TWeio9aL+bqKtaGFXHUwMDFl3t3rReh5LCzqx9qHUdb3ZaJcdTAwMTQsKlx1MDAxOShkMyjzr/K0XHUwMDFi3mSdiGDIniWwVFx1MDAxNjs/TS1cdTAwMWPwgZHT90mQTdAvovOnjqLd2KJcdTAwMTCF/Vx0i+5UR6mcluhcdTAwMTMh9ae7gVx1MDAxNkCt4fugp+Qwx1x1MDAxZHTc2lxuR16ni+lZiopzglx1MDAwNlxmnjWn+J+dpi4vz/tcdTAwMDDf33/6f//MuXxnrkTEXHUwMDBiWHy4oTMtgzJWTVx1MDAwYtEwVTihesNqQU4+0sGZXFzBn1xmQ7ahrOzFTlx1MDAxYiinmm+NqIBzsKxcdTAwMWXBUFx1MDAxZd/INt1DqFx1MDAxNN/Y2YvMb9/QUO6cl7PS1SXjOVx1MDAwNbo4XHUwMDFkXHUwMDBmXHUwMDEyhcKpue6d/NpwdVx1MDAxM1s8qIUjXHUwMDBlJlx1MDAxN1dcdTAwMTWYsv50k4Ixb2Gy24JcdTAwMWLXIVx1MDAxZtsm4mefs5uFXHUwMDAzpl7o2sbVJ7CK9s59XHR8mYywzNEmXHUwMDEz//VcdTAwMDet3PVzTopcdTAwMWPeXHUwMDE20z85XCJeNYrNaS5wcTtPhI9cdTAwMGKkvYJcdTAwMTVcdTAwMTBcdTAwMTk3XGaQKJy2QeroXG7I2X+Ytlx1MDAwMO5cdTAwMDTUID5+9nWGXHUwMDA0tT2+hKnUnuWdbzielycx3HlccuhG2Vx0+k2CNfNqo9NOXHUwMDAzXHRhanxcXM5cdTAwMTW38r3OsT+61vTOoYhcYlx1MDAxZVaGXHUwMDE1SGl9SOxcdTAwMDezsOlRk3qc+fz7xFx1MDAwZehtw504XHUwMDAzXHUwMDA2ffZcYlSk1jG4YcZJXHUwMDE2VS9cdTAwMWKdf85PrN3S4D9uJvlcdTAwMTZN955kqP5cdTAwMWOXsqE47K2sTLdG2oXj45dPyizGN+WmuXIoLHLTXHUwMDFkaD+K11x1MDAxMG7sjIFcXOXdb2/SlbPR0T5d2XlHanyUXHUwMDBmQaGLn+u0fD5cdTAwMWVPn1xco6ip2HxScPrmuWGiuv34kVwioS9gxcR+eLwhj/TL4lx1MDAwNMuSXHUwMDA1NVx1MDAxMKvdPKGTOVx1MDAxZJJsUrGbisJ+2Lz6weLlzFBsUjqOvPWnmjbkXHUwMDAzxPtdeDOfTyzn3Puo1uih/2fD37NuJPpsxW1Dxmb1sFx1MDAwYo8x/PjVXHUwMDA1rEH/OGMn7eqMXHUwMDE49Yh5l1OxoVx1MDAwM2he/lx1MDAxM+HgjNdcdTAwMDFcdTAwMTXj7ErvyVx1MDAwNJBO1exMylx1MDAxNFtV5UD/i/nT0fOm5bZPOszOI1lccqyMU5SSiS1/bOqCpFx1MDAxOVh8P1x1MDAwN9nuxDnRw8WlKGtLoc3D81xc4Sk+/E7B3L43XHUwMDEz4zbQXHUwMDEy43JcYnniXHUwMDAzrFx1MDAwMjSpXsj2XHUwMDFjhYp+OVxi4VdAXHUwMDA2JUpcdTAwMWVshqT/da9cdTAwMTn7fcdcYkvJnlx1MDAwM/iyh/NcdTAwMDOrZ13k7PJccjX5XHUwMDFhv2SKr+mVqVApUiBg1lx1MDAxMvZTpVx1MDAxZVx06zGXRiiOr9ecnjfJXCL5i1Nuqf2s+UTqXHUwMDAx7rfOXHUwMDFl1HRZ+urrhchcdTAwMTFMicOWZepcdTAwMWTuulx1MDAxNlqQ+W6FoMa5P3lcIrJFXHUwMDFjkllcdTAwMTL7ODv8wlxmaMDKT7LgY4+EQLMudPVcdTAwMTS8patcdTAwMDX1XHUwMDFj90uL7bXJ9Dd6vuYl8lx1MDAxNC9cdTAwMTaDg3avR8TH/j6Qr+Tx+eap3zPEXHUwMDE3PcBcdKP5q8xcdTAwMTTfN0veduqhvuY2yi1cdTAwMWa5zLrIlcNcdTAwMTRcdTAwMDLSr2dcdTAwMDWEdm/ioazCKtK59UdcdTAwMTVrPbSHhaMk57x+n1x1MDAxZPRTS7E1SDmVXG5TLa5LY/zJX/Arl3uS61x1MDAxYsBcdTAwMTHB+qe2eduusSv/c/CdNZOf0Nmt6Zg1Po0t9OnX9vHEIFx1MDAwYirhKF9I8n1cdTAwMTRrjObwpNpcdTAwMWY4YWWMXHUwMDE2n6DGdntcdTAwMDSmep0xO6xXRfBfsdv/zrbxpNpf5KNM0JpnjPVZSEA0znpD5t1N/lx1MDAxY/z+3Tv3+V5hN1wi+G7aJlJV4pZ30bitWajO/Ct/X69tXHUwMDFioNXVpkklpktZ6Vx1MDAwNyNqKUJcdTAwMGaoXHUwMDAwl7tcYlx1MDAxNlNcdTAwMTB3cm6F4KFlK1x1MDAxOFx1MDAxY/tHw/fvuXF9ajKG2tdT4ZhTXFzd0NV9ZzlW8mycJ57xMDY253q6IEqGXHUwMDE2XGbmuW3RXHUwMDAwbFx1MDAwMO1ZXHUwMDExMFx1MDAxYXfYcuvfeCxu0yxcdTAwMTXUJeInXHUwMDAw/7tPXHUwMDA1ZP3ea61wTPzbXHUwMDE3w9Py+Y5IiJRTn4LfXHUwMDFlYPEyxEkj/Fx0dkbD85lcdTAwMGba3T1cdTAwMTFX7k3pSFx1MDAxZt6gfDR6virZVOEkbUtiXGZbXHUwMDEwV87tnFnOXHUwMDA2z58zvkW582l2Nnw3PeXBXHUwMDEyXHUwMDEyJ66145jnLXoz1s2Rd4iH+Fx1MDAwMSfP75l+eqhJ8phJztYlxS1JZqj4ypkkpZhFM1x1MDAxMlx1MDAwMWyMQeaDqi7LhW1cXKAse3011WrfuM9cdTAwMTlcdTAwMTXSi1x1MDAwNrHTaZst+/xcIlqEpTbyJHr0PzxNrqCko2mtpYu9NVxudcuI53hcdTAwMTj9WLssf1x1MDAwNrZcXFx1MDAxMoVcdTAwMTK231x1MDAxYuhcbk5cdTAwMTCEJK3fmau3dO/VybdcdTAwMTZvwYacXHUwMDExN5+fOLG/l6+fQkwsXHUwMDFl7bpcdTAwMTQmXHUwMDE3KqIr6DBxx7j3nL+DRst6vD7RtMNcdTAwMGL4XHUwMDAw/8xcdTAwMGLBt7+GZ1x1MDAwMrJNuXzCIICbI3JD1JiLflxmUNGjo37D09KL9IxaoeVcdTAwMDTfeZ+SyWdcdTAwMWSIM+xwgcGL+Vx1MDAwMOpcdTAwMWH2atxi/zdvkdhak/v/j6nv2HqTa5q9IFx1MDAwNuQ0JFxikUFkmImcc776g/za3/lcdTAwMDf2sr3kXHUwMDA3senuqtq7u2hccqB7/16rXHUwMDE5foTFrl79Q6N7W5xcYv5FwFqgUFx1MDAwYvZcdTAwMWF8XHTbXFzWXyVtKl5+k7ZL3izmXHUwMDBicHfl53mnXHUwMDBm7PD6dETC5UeKTK22XHUwMDEw+lx1MDAxOeNmIGP90FxcJNxpJvNHszpcblx1MDAxN7Gmgt29xpaR7jDbqruEcFVASfX9Or8mxdtcdTAwMTErMtq8nlx1MDAxNtIyMVx1MDAwNKaWWW1cdTAwMWXuN+L4iX6asZVSPlx0L7FfKPhfb4dySP6gb9PmneHDRbHCYknciYHbfb6MsqjqJLz8hG29L1SvRGX0cML+/IFYrobOtnKO+pM0JT3811e6lC9F4Y7iljFEX6vv0DG19WrojVx1MDAxZFx0XHUwMDFiXHUwMDFiVuJ4MEn+g0krN7KmrG95M7rZK26q94soL27RouF28/Z4OIS3MjnbXHUwMDEzUfmudMKZf6OpUFx1MDAxMVx1MDAxZsPhZKb9hV6opVx1MDAxMLR70Fx1MDAwNplcdTAwMWLk6iCJ+m+21yaq374wh0+x8NRcdTAwMTkt+o1HOOfBgsxcdTAwMWHpMJ1cdTAwMTLo88Vk9Vx1MDAxMXP2XHUwMDE5WTs1mZ7eQlmW1Vx1MDAxMnC/XHUwMDAyhPjVXHUwMDA0++SkJNBn7DVcdTAwMTZ2XHQrgm9cdTAwMGVcdTAwMGX24vv0wbDBou3zz1x1MDAxZUDxUsRWqi9cdTAwMGKf293BXHUwMDFhRXe+olx1MDAwZfWvzPs++DyA8ihi6Zv5XHJaLLF+vlx1MDAxMNdcdTAwMTlcZqvNXHUwMDFlnIm1b0VnTml1M2n8O0+LPr3rN+WEhNOvWa9gNq166fGbwviciZeOXHRcbsr+5FwinbDQMipgzo06OXqvi1Jvi6uJd/mbUVx1MDAwYrufZ2xcdTAwMTm4iXu5gHJ1/tmLYo78XvzFXX+Mc2SDOZta/166hVnYqGopMHlcdTAwMWOeXHUwMDA2KrDcJKDPXHUwMDBm6zc/X8/mqLDEXHUwMDE4ulx1MDAxMFxy+oqqOqlcdP1/8zQw/+vHuG5b4Fx1MDAxMIlTXbdQOVx1MDAwMGDL0z5oJpZgz25cdTAwMTBMvvGEJ6F+q/nZXFzcNZ6aXHLTuD89Vc3rXHScoLiSXHUwMDA30U89+ahxestXP8vyw9+v31x1MDAxZdWzJv5RXHUwMDBlsf79XHUwMDFhmryTMmJcdTAwMDdcdTAwMWLgMz9cdTAwMWVoJ1/i4WiqZqi4XHUwMDFiXHUwMDE3WcLo6/DJuPubh8c/z1x1MDAxMOxP/bWhXHLvXHUwMDFmYm789pSrpetrhXHueMTkUVx1MDAxMSdFfvWmTDW2XHUwMDE43lx1MDAxMDpPKC2zzzomoVWpXHUwMDAyXGbGjrU2kH7SycdysTffwUmbXHUwMDFmzd/ZtZDpXHUwMDFi7sO27MXwtVx1MDAxYbJcXGpUkvtcdTAwMDSvkX331cPV62J57ct4yGbgXHUwMDA1d1ViSNQhOYXK3z461EVcbrhcdTAwMDJ1copHZFxcXHUwMDE54DRcdTAwMWWNm29PyWTqV9N4ZTPzLlqJ0OHkcacjoIzPhkX1xVx1MDAwZr9cdTAwMTerMsW1oVhyOpXDmcJt8V7DkcnahuAmO/m7/vn5X0nclnGklLNcdTAwMDQmIMyxSjA21I+0yVx1MDAxOSbhp0i+S5Or9MGg+399aX/MI7OKXHUwMDE4cv9cdGt+5TRcdTAwMGJbXHUwMDE48lx1MDAwMHArXHUwMDE5I9J/+MDQV2JAWVx009L1d1x1MDAwMFCiLlx1MDAxZZLz1Em9UM4rT91f2N7aZcfdiVx1MDAwMlx1MDAwNnCHxUVzfeFcdTAwMGaq6FxuveaMWT//5/vBh2+IistZluD5JVMvgZGij1x1MDAxZr1cdTAwMWGZoDwldEBcYlQopkTcXHUwMDEztGFcdTAwMGXGNoz856/TsuhPR1x1MDAxM1x1MDAxOFx1MDAxOfJcdTAwMGZfW6vXp1h89KHy81duUKHgS7PHW3vB4enw3cXnb8P+22dnKDpYdJ9cdTAwMTePRnWMXHUwMDFmSI5R1Fx1MDAwYo9NMi1rlLN+9ddcdTAwMWY95jQwXHUwMDEzy3OsVcpj2UxcdTAwMDTZpli+XHUwMDExlzLhMzuqjY9cdTAwMWHwn1x1MDAxN95X/M9fblx1MDAwZr3ELPWBl4SDQ6mZ0twvx0mOXHUwMDEyXHUwMDA0L4hcXDiEzTDUVVx1MDAxNYvn/8tb65H0QvxZuoH8Rlx1MDAwZlx1MDAwN+beysxYWTddk5h8h9PZmD9zXHUwMDFiv95V3upslM+UXHUwMDEwOd9cdTAwMTB9oSw+467xasf3m5pl6lx1MDAwNJ8o/6p3Zv/N1Vx0ln65qpBf5UI+XFxtu59wKZ2CN2ybLFx1MDAxNUnZTKtcdTAwMDTLZKqakqP4eNebjVx1MDAxNnTMyKL/ZsL5LiglXHUwMDFmLvyNOVx1MDAxZOSryKJviHJcdTAwMDb8tldGSFwi19HB7WH+01x1MDAxM8Wx4FM/T70mgvtcdTAwMWT1R/aCYaw2JO8s6zCif3hbwtVkiVxy+Dn+elq8vz/vwCAwmn16/bnFhIPdsPTxXHUwMDAyXHUwMDAwXHUwMDEzTOphW2W50sR66jUywip66siubNpcdL/41iTB4uRD29+NjSGE9LpcdTAwMTKuRHf+3lHtX++YpDlcdTAwMTmG8aiDKEvfXFxlOJmn03ByVFx1MDAwNruJnaLc6/n/9Wzibe/XI1JuJnT82aup31x1MDAwZk94X4aCo466ncxQXijjJk17ZOlcdTAwMTfq0bfWXHUwMDEy5lx1MDAxZi+tb+dyWuRSPFjLcopcdTAwMTB6iy5cdTAwMGYoRS8vjoB48/2Ubv94UJQu99JoiaLfq841VDC/5UxVgFxiP3ijTaVtT+pW/3tcXJLJXHUwMDE1w/olypbmXHUwMDEwXHUwMDE0v7MxSlx1MDAwNFx1MDAwNNuZ+dm3eDpcdTAwMTFlr/jNR3BeW1lCK1x1MDAwM3/7hNLK/VBcdTAwMDRcdTAwMWRcdTAwMWRcdTAwMDZgXHUwMDE37J2JiLG6J4bmeFx1MDAwMplcdTAwMDNcdPsj3f/jN1rx1m96aGC5pj5cdTAwMWLrhuJArFE743hcdTAwMTGA99v7PnxPTFx1MDAxMeGfZ1x1MDAwZjK0jFx1MDAwNlx1MDAwM1x1MDAxNZk4cuT4rFNcdTAwMTlcdTAwMWafpsZxhDq4VFx1MDAxYVx1MDAxZoG1yzBcdTAwMTfQsVompig9jPmkt74zatFcdTAwMGavITnJglFLXHUwMDE4XHUwMDBiXHUwMDA0oVx1MDAxMjeoQYZiTniTXGKFNDB6+v5ir3aZ8MtcdTAwMWZklVCqv5SSRMU0eVx1MDAxODuL2M2QwFJGku7s7Nj5XHUwMDBml2a3gt5j5Vx1MDAxMuiE59uftkOn4lx1MDAwMPrc2k1cdTAwMDPjVedf2WzZJbCXptu9wE5GhuaTOElBXHUwMDAzwXf/5fGXS7jFemOfY+alSKjN9/vzSKaV2Vx1MDAxZj7fIHGlzln5d//nMFx1MDAwYvkrYlx1MDAwYuW19zVDTylKnWWtXHUwMDBmvexB0V//JFx1MDAxNdX1XHUwMDFicPSFYqTy5MBcckq//vXkXHUwMDAxP21cdTAwMGVj2UxcIlx1MDAwMze7Q7H03ZzzXHUwMDE0S5dAjz1CN9uCjXhq7HnFQeqir/RbbsL0jlx1MDAxMVdcdTAwMDbW4jcnVcglU9NcdMKBulx1MDAxNMmM0MevzCxFWoaTJfAxRzjizKG2/86iKVx1MDAxZFx1MDAxM5KcxySAeYjpq69cdTAwMTHLWlx1MDAwNCHTqWtcbtCS3lx1MDAxOZJw2XcnjsjqcmCB/8uH1yU/vH7kXHUwMDEw8bJcdTAwMWUoXYuAmT0u2qPc2s6wh7uNe6dcdTAwMWUl6TzF7cWWsN9cdTAwMGLhdFx1MDAwNFx1MDAwMVPpKSTCJltsvSxOXHUwMDFmYFx1MDAwN/+Zy/J7nUFuXHUwMDFlXHUwMDEyfr9cclx1MDAxZizdf31tylx1MDAwNlj0XHUwMDE1q9jObVxyVcfVozT0ZoTA8cGAnqvINlqUKJf/9iFcdTAwMTXhz4NEh1wiR2F/8cQsTn5Ml1x1MDAxNZaaJ06wXCJtYlia7/UyU1x1MDAwNUs/8WFuzFTR3IqMn99gQ3iwg/RcYlx1MDAwMoDzr+hue1x1MDAwN+RcdTAwMGWEL5NWXHUwMDA3RbWpvCSTtHD9N5OmXHUwMDBmcsm/+fPXKfNcdTAwMTDBxvTuSt3WvSGMlFx1MDAxMvhDJVx1MDAxOSMltEDcauD4V9vE7uXXsIDs92U+XHUwMDA1YWUjVlOV+MlsdV3jleThhCx8olx1MDAwN7yNXHUwMDFlUtBcdTAwMTWLfGO/2Fx1MDAxYmCOXkxYrlx1MDAwMFK1KlM4njcn/NPr4HA8x56Qnr8/mpb3PLA3+8GbzjiutYmmXHUwMDE3/2Tp7+LfpmVCXHUwMDAy096V3Jy5wd6HXHUwMDFhj+BcdTAwMWJcdTAwMTVcIv2q4Vx1MDAxOHwpws7KmIhcdTAwMWEsxajBosxE8M9cdTAwMDNS/c9cdTAwMDOydVJQZYUp+GhcdTAwMTZgVqjAXHUwMDFkXHUwMDEwO+BNy1x1MDAxN5tjqr1qv027T3j4Z0OIXj5cdTAwMDNW/b1cdTAwMWXUXyxMOMz50lx1MDAxYcpkXHUwMDFhh8hXt8VcdTAwMDNcdDmAeq09vdGAXHUwMDA3XHUwMDAzv2cul8+NJF+ycLJcdTAwMWUnc1x1MDAwZVx1MDAxNYtKUdREW+pQPtvR4EDFev3Hb/+cv8WD7et7NU6YSVM/n5LP91Pxr9o3XHUwMDE4/s4tqu7QmCHnXHUwMDA2fzGXONdPLat+Js3pxiDhK/Au/1x1MDAxNfWfPeI/4lT+7Vx1MDAxZuc//StQXHUwMDBlXHUwMDEzXHUwMDEwxa+sXc68fc2qgMg6zOMxXHUwMDE3rU+j6W/KQLXi4VNCwWlptHHs8Vx1MDAxNplcdTAwMDdPsN5cXKrQKDCUXHUwMDAy0ZB5jfFnP6ys3TxhXHRSzSDt7EKKv31pf3xcdTAwMDBcdTAwMDNdtWckdl+f/lx1MDAwNF457zxMQJBIkoTzdyyEXHUwMDFhYcIhx1x1MDAxZlx1MDAwZreRXHUwMDEzXHUwMDBlP2TQIV5QW31Lk6dzrDm2cMhcdTAwMGZU9XVcdTAwMDKb/vTm88xbj0F1yjj0oD17fr34y1x1MDAxZVwiZphrVFx1MDAwNa5cdTAwMTCZl+ovX8qZ8q3ENVpM7OfO3O9XoChcdTAwMTZoUnzX4jYt321cdTAwMWKeMZ1cdTAwMTdcdTAwMTfFxmLxe5FcdTAwMTH4xyP573lcdTAwMTNQvGzVXHUwMDA1XnurQH+wlZW4OO5cdTAwMGVcdTAwMDFk1UhcdTAwMWE6YtXQMfAvXtxcdTAwMDSJ3pvSXHUwMDA1y6wwxCCQdVx1MDAxMac6h/29O0hcdTAwMDLxW63noFx1MDAxOPY9/Pz1s274uk9Cws+0kZfyN4GLyZHp+3zIXHUwMDAwZtbmnH41daB+58zxK6ri1/zoN2pAvumdcnpcdTAwMTE2XHUwMDAyR1+fXHUwMDEyt1x1MDAxNZnYLPFlv1x1MDAxYbUjxTe/aObuUlx1MDAwMCfej8zn44nPfsZ4f2aV2I/9pVx1MDAwMnVhpmtcdTAwMWKKXHUwMDBl+s1cdTAwMDIkXCKlm2r2XHUwMDFlnNyP1iZpPeplvzlAkVx1MDAwMmfj3ZaDP9yR31JzXHUwMDFjRP+K1JSFgj9n+0zzOjqzjVxmVpewO9JcdTAwMWNswrLJKaIlXHUwMDA1yUaeM+uPJ+jKb+zyLjLVuYVtYYiDicf+XHUwMDBlX0IuS2z5hO5AfZmDXYU6rMdXrkGeJ5ZWgZ9R/Fx0kWLt/83grMznT38ypkb8/WbLUv3Q8FTN1Vx1MDAxN3yCXHUwMDEyS0pL3faXq0zXR49aX5HQafG1c+HZSFx1MDAxZlx1MDAwMefT/c51uKVvU890i8qGy55AMWN03lm7c/h/c7dDca5B2pRcdTAwMWW0111vNGlCXHUwMDE4XHUwMDE0y16JJP2siEszuI6//jw2Y86WWM9JXHUwMDFjQdm3dWHF9qVgQZg51LxWj3FcdTAwMWHoj2BAqdPippskdlx1MDAxMua3xsK9bNhAio2kwn7jN1x1MDAxY249+T/vVI39/nrFg2qpeuQ3S5ZcdTAwMTTy2F25hrMmnzBcZscwtmuxnsAkm8q9xGVHzThlxFxuccuBT1jhXG5YI5ubKbjlr/GpXHUwMDA2+8F0K9TeXHUwMDFkqW4u92ffXeZcdTAwMWU9fFx1MDAwYlx1MDAwN558RLdcdTAwMTHpvFx1MDAwZVx1MDAxN91cZsFcbiqwJkSX969X5z+/XHUwMDE3XXpRdI9vioP4kYgrcVWoX69VYpzkMqpS/OpF03KzTFBCTEWw+4NcdTAwMDNVXCKr25tFf0z5zVx1MDAxYoZZdzjjUdi/XniYXHUwMDBiT6F5zyk0r81vzeGPVSVzy3P9cuY2UuqQ+oFMuuy8k3ZQXHUwMDE4UVx1MDAwM5ZcdTAwMDWqvPSzXHUwMDE3hJt4atovk8NcdTAwMTUrfDTm6v49YNQj7WNqV1x1MDAxZFx1MDAwMT9Mt3xcdTAwMDeeZz3Apl5AXHUwMDFkgNtcdTAwMWU22vOniirsf/tB/G8mPzBcdTAwMDFcdTAwMDCp34I1oUK65to36H/eK2FDzv11jUK4XHUwMDEysZuG0bZcdTAwMDJUek4q3ln4Rlx1MDAxMVx1MDAxNtlcdTAwMDb92F5zW72Cclx1MDAxZHNcdTAwMDT7N1x1MDAxZvZXm8W+fn/ZseQ/KTxEi0YxfVxyK7z+okgp4L6ZI2pccl/qb1xcYua40qFcdTAwMTXz6ruXbVx1MDAwM5OTgVNcdTAwMGbQhFwiS1x1MDAwMYjXX13/elx1MDAxZIP+kFxc7lJcdTAwMTnBj1lcdTAwMTVg11rUXHUwMDFjiFx1MDAxY1Uv5f/r0ahenHhcdTAwMWO9RHw6QvNeiXw8K2VcdTAwMWW9o5WX7OuG+TPB5rbklHDohmr4XdV6qrq/vVA7L1x1MDAxZc6l4Fe4yFx1MDAwNtFcdTAwMWJr9YdbbrBcdTAwMTCqXHUwMDEyR8tjQuubWr7W+fk386PTouN8QD4+yUezi7JcdTAwMTOrQ9uDgqpHxa6Bhbm+XHUwMDBmRSRLhEtLWlx1MDAxZnJU2YpPT5VcdTAwMTBDypRcblx1MDAxOWRPZ6gkXHSn1Fx0xv4o7EbxXHUwMDE3XHUwMDA1Mke9pdVklVRcdTAwMGL8kMv7t6bhpX7IXHJs3rp+h15cdTAwMTFX3a5cdTAwMTZfXHUwMDE1X6uraXP2gzmHXHUwMDFiwWNpmlx1MDAxNulcdTAwMGKOKZYm+yw/i1YvkFx1MDAxNVxih38lXHTfXHUwMDAxXHUwMDFhJV7ff37Ux/HpQJfIKoZJKd+p8axH/uA3XHUwMDBicdWuI982SHC8bIh6JVNsWKe+1pdcdH/oKyokMJlcdTAwMWGtd9juVcif6oUpmeBfm2jkXGa52T5hw5BcIkXpKIeA/VZQvFuimbNe//VAK3zMqY+aukgzI1T8Q0AmXHUwMDFh0GxVZbRcdTAwMWJcdTAwMGX7XHUwMDFj185rWrxcZlx1MDAxZcBcdTAwMGKVXGLWoFx1MDAxZjqTXHUwMDA0t58xNG1cdTAwMTOuSuT/+MqvNyClXHUwMDExTM/Ek9P5XHUwMDEza1x1MDAxM22q60ZcdTAwMThcdTAwMWZmXHUwMDBmTv2nb6fqXHUwMDAy3Y1cdTAwMTiDTZXTXHUwMDBmwUilXHUwMDE3QPxcdTAwMWLOXHUwMDE004SFd7Jhg7+V2sPQkilomZlqXHUwMDFitVx1MDAxNItQ5SGh5L6X/4YxkOa6eSVcdNLYlVx1MDAxMFx1MDAxMiBcdTAwMWVigzUr5Pat8+gnp9xyi9iQ42qp2Vx1MDAxYa80Kozy81x1MDAxZNd7ZlakrvDohXgvb5NcdTAwMDGQXHUwMDAxZkjKPM18UcdcXFx1MDAwZa7JL++vXHUwMDBiXG7HLGjU3Fx1MDAxZUVcdTAwMTctOrvpgY7/wd43p7HMrIZcdTAwMTlXXG4mroNcdTAwMTe9UkF2VVOGf3bcgDdAIfGb+5qOXHUwMDEwbXL4lpw2fePs7PTEtyq0o5t1N7d3zkSdklx1MDAwNlHc7qHW9Fx1MDAxN/oztfZUdYblKFx1MDAwNCSz8KqZOPZcdTAwMTld8PktJfJcdTAwMGbct293vc747cLAv5n4XHUwMDE2klx1MDAwN4Gz/OVcdTAwMGJcdTAwMGZcdTAwMDTktFHo7VBe0jhcdTAwMWOe3+q8ZOa825KJY/nJolx1MDAwNJvwXHUwMDBmUlx1MDAwN+vGTeT844aS6Etcbr9cdTAwMTNtXHUwMDA1IFx1MDAwNGz5Y849XHUwMDFjUzy0sYmCwUveL+VhSIb8/G0/gNjN5bDWvlXyMVx1MDAwYsXjXHUwMDEz0Vx1MDAxZE3nTVx1MDAxMMlcdTAwMDLCkPN6SVx1MDAxNV2esCNcdM5/M6iLpWDRXWqB+eJcdTAwMTSu6a9cdTAwMTSwdVx1MDAxY9haVHBcdTAwMTdIlz2NUleWXHUwMDA1lZmJXHUwMDA18ctTXHUwMDAzKWt1o5lJ9H0wqv+fZ9BcdTAwMDS/MVx1MDAxZGRoj6Per/Gr4ED+Wq+pf+tfmaD1UVx1MDAxYVx1MDAxYkOdy3vxKipm7Sn/aKG6KJUr5eBnQ8J0SupkoyHkXHUwMDFiVFDQSqBe/jiH9YSe07qB9HlKgDaXrDxJ91x1MDAwMG7BXHUwMDEzR2VcdTAwMTFcdTAwMDVLMI/Hpiilk9yr6Vx1MDAxOFx1MDAxZvU9SY1cdTAwMTRURGopPcQ3rO/X+51cIq9yXHUwMDAxXHUwMDAyXHUwMDAztTOw2T+vNVHEz41/XHUwMDA05Fx1MDAxNmi0itu97O9r8DLDybGvkHClq1hDKHhnvmyghdHaXHUwMDE5XHUwMDFlMbn3rbiT41x1MDAxOKuRtS2rv8Xbevy2iWtcdGayQFx1MDAwYpilSJTrXHUwMDA0sIHkp33billPTzyuJp+7ssCePKdcdTAwMWMzVs01XHUwMDAzbfwzU2clXG5cdTAwMTDx/OZTfDFcdTAwMTlcdTAwMTJtp2rBK2GB+H7aXHUwMDBlXHUwMDE1lMlcdTAwMWJWSVI835m/gEY0ZyObS6PJkdJ/M56Dukjta+J5XHUwMDA1x1x1MDAxNpWtqFxiXHUwMDEx4M/246F8vaiBMOzCTkTosiuS5+H0XHUwMDE16Jj1KuesbkdcdTAwMGZu9klC1DNcImr7ySmPmuBcdTAwMTVcdTAwMDE7YLnDyMxK1lmA8bZocHxo9lxuQuEsoPYkfoZcdTAwMDPNt1x1MDAxOX/qw+ZcdTAwMDXe8k+7vV92XHUwMDE1KFxmsqpj8vbwRPTu85PvXHUwMDFjckGTXG7uXHUwMDAxuZdcdTAwMDFcboa7rfTNV/FcIuImX1x1MDAwNy1prkailtTbXHUwMDE4/dRJ1fxcdTAwMDJcdTAwMWL8+Zqva9tcdTAwMDPoXHUwMDE2apZuaOSNbyC266pcdTAwMDZ4T1x1MDAwMMF6N4rp1UNcdTAwMTKiS0Tew5+cXHUwMDE53MpDb1x1MDAxMk7OZOG8mFx1MDAwNJOxbJ9CqryHdH/u7CaZ3Et++19PPnH6uqtcdTAwMWZGXHRY7/dUuqCN8jzVRCfXY2pXZ7A+RoJcdTAwMWQjl0LCTT1TwrGJgHWOt2pcdTAwMTUl6Gb0dLJXwaNjxrha/lC2yVx1MDAxOXthZYJ4SUjkPruQXCLKtJpRXHUwMDA3P7+QU5FcYs3mx4NdWZyp5D+PXHUwMDFlWPKYLpDHRleZqZfHUGxHIFFiu9h2c9ow+vd2lOLrK4tcdTAwMWLa2Cz0XHUwMDFiXHUwMDEw2M1zPcSRXHUwMDA1b75y3OtG1tPIhYS0jXNcdTAwMGUlcvBcdTAwMGVUQVx1MDAxNkVFbz46XHUwMDExXHS1bqPDuYF8rlxuLoKRi+k8aVUz/8/MrcNcZj73aW94ePlVPjlcdTAwMWZcdTAwMTclh7tcdTAwMDd8ckaXk/JcdTAwMDRcdTAwMTiHvfDNRFx1MDAwMjOBo3BcdTAwMDJcIsqvXUtsulx1MDAxYn5UUOjdq6TaXsZxXHUwMDEyw2tEXHUwMDA16n1zhfpGXG5TgZBsXHQuwC/pXHUwMDE3llxy7W1cdTAwMDNw/YD2XHUwMDE0XHUwMDFjczpr0uuplcSIiiCNns+NatjbXHUwMDAwQPUwVUHQLTH/i1fyaVxms8CpgjTzJOLT/bFcdP2uKiWAjWhumNAxqXNN1Js4n3Ew9Fx1MDAwMdThIEdcdTAwMTYk96VcdTAwMWEya12+ypK9aWlcci/6XGZFlFx0RDdGV5c7jya3wlx1MDAwM53/ZmKlmy+rvbP7/3vX6JP3MVxcetat2q/S2SF9dH/P8rHKNFxiXHUwMDFlMEeAhJdcdTAwMDBTmWZjNCesdTCgvYZXNi7gsW2dP/CAQ/BsuFx1MDAwYnX7XHUwMDFhRcLGYtIk3kPuoZnXnsh66Vx1MDAxY2TMbctsipU5cmGxN1x1MDAxZWJkOpnBXHUwMDFi+7ffm1x1MDAxNHzkY2D6YFx1MDAwNPsh3+ZJRplcdTAwMGKCaDDjto+KeOZcdTAwMWPCplBaeVxiLbzOs1x1MDAxMnaNh1x1MDAxZVLrXHUwMDA3o+6DfMhguY9cdTAwMDIwWN1aXHUwMDEwXHUwMDBiXHUwMDFlLlPLOm0smlx1MDAxNClcdTAwMTKiweshqVxmtDLMXHUwMDE54Vx1MDAwMI722yFESOv+4cUvSUU+XG5cdTAwMDF22pN26lxyO9H8qIZyV1NcdTAwMTjEyPy3XHUwMDBl1ckujeeKR96qXHUwMDE4XHUwMDA2l5XbIEyGNrX5KEX0mvlofiX016/q8cS/vbqP6uBd35C3XHUwMDBiXZZHPUVxXHUwMDA32JsrZ1x1MDAxMTz7/q9PTFx1MDAxMsFdNmFvjCxHXHUwMDAzXf97Ylx1MDAwZr602UWSYdZvO1xu0COpfNTya1x1MDAwZfCH0MqAfCXtMnW6QORJdWay/Kz5TsrdXHUwMDE5XHUwMDA2nYqfdFx1MDAxYXT5bIaLxtdcZrTJMudGIZqPb8Rh6ePzT1x1MDAwYlx1MDAxOONb4k8qm3ItetfwI3pI+tx4XHUwMDFhhim7XHUwMDAzcF0tXHUwMDEygymeXHUwMDE1Rc65elBAdT6qhVx0Y0h5I3V2T8CDO+lkzr74ylx1MDAwN7r0pyYj7O+YklxcXHUwMDA3UpNcclYoIzLbv7/ziZpBmF7ixUlccnVcdTAwMDK55Fx1MDAxOUWvWldWXHUwMDEwLcu3PMKh/j1KTFx1MDAxZn1z3lx1MDAxZTJcdTAwMWE6OcJcdTAwMDZ4qJix5qv8/D1cdTAwMTL0if9cdTAwMGY6klx1MDAwMPG5Nr9cdTAwMWRx8d4pOC2/UPc7j/hvLVxyTCx/vs4k7FxmZJaxb1x1MDAxMPe3XHUwMDA34c9bgWhcdTAwMWS0W5U2psBcdTAwMWGEXHUwMDAyIIaWXHUwMDE0p95cdTAwMGIzhVxirTNcdTAwMDE5XCLgXHUwMDAxOo++3Vx1MDAwM5LoRWiiQcfhxCsneXFkkFx1MDAwMlisxn/PXHUwMDEwtfeLe02KelqYkqdcdTAwMDZcdTAwMDbQSs7rKDBCINwyqW9cdTAwMTZcdTAwMWZaxWLWpFx1MDAwM5Z2ci6eXHUwMDBmXHUwMDFkeKn5bVx1MDAwNuGDL7Vcclx1MDAxYVxiokLYpOVcdTAwMTOb4VhT6ptcdTAwMDP/PTd/uMBefKq3v++w3iz7XHUwMDE2cFCDgOYsXHUwMDFki/HZZj/x6k2/0+DoTVxm5jAvXHUwMDEzsNGJuvu+TVd7oHu6nprBcM3USjHgIGRCbtm//mfFkUzuONIvhNhcdTAwMTdNY7HNx1x1MDAxNDXM54RcdTAwMDUu7ZpXtVx1MDAxYX5jXFyLa91NMCietFx1MDAxZi9sXHUwMDBiwKydvJXe6eWmbtw/3bd3wbK3XHUwMDAwjlwiv/6cJ/5931x1MDAxM1wiXHUwMDEzLFaPjlXs2VahdLhtkHmerorn6vr8+lx1MDAwNFx1MDAwYlx1MDAwZiPJXG4vtatTeYNXdZ9qKFxutFxunGWMeusrsEqmgCOsKZf/66d8NPaTrEHrvVxiYdnVlcaAfolcdTAwMDJcdTAwMTU/XHUwMDFlZYWbXHUwMDAwh+qWXUo3aHvDQ8y+5zXENS4vv3tzzlx1MDAxN0xbs3I5yvU+SKl3UVx1MDAwN7VW529cdTAwMWZcdTAwMTVRUVxy8eSA3tMrbu9cdTAwMDaAPDpgXHRQu9yDtmV7/PYvy2WN/SDS62ay0M++sYFcdTAwMWE5XHUwMDBmXvvvObrH8lIjuPd1/vy/1kviR+3AvVx1MDAxNfpcdTAwMDSUcpNfcVx1MDAxMrI7+sGW3rWxvGOeXHUwMDAy7l7I4NXDXVEu11NrS+1xXGZcdTAwMTJcdTAwMWNcdTAwMDZccrEpfkFs4Vx1MDAxNFx1MDAxNjyNtEyg/81cboUnszmWhJI2joqoQHy9+N5LZvGYXHUwMDAxVNZErSndXHUwMDE3Kr7OhFC5tlx1MDAwZf9QY/shkp1aZ3bOv1x1MDAwMFZmuM9K73BPxf/rhdlcdTAwMGVae4Tzm9JMtFx1MDAxZiMgnnKxicbQaC5M65nWckV0/pBcdTAwMDFXuFmPW+rM73JD0fm7ckxcdTAwMGbMNZJPOFx1MDAxOEWl/3lcdTAwMTSZWHzIytZyjcdN3nz3cP1cctpcdTAwMTSmylx1MDAwNrBcdTAwMDOmXHUwMDA2Vf3n7DhhdkjKocrigTJcdTAwMDbZXHUwMDBl4Fx1MDAwNnDQy57RXCKAhS63bP596lx1MDAwZlxur/97h1xcXHUwMDE3spX7XCLi43jyop9cdTAwMGawWp/EW1x1MDAxNqFcdTAwMDE/XHUwMDA2+XlcdTAwMWLWzLnvXHUwMDE13DOjj0negVx1MDAxOEzMv3Coalx1MDAwM2uv5Gvt71xyf5Pt/97BXHUwMDE0Xvnn7dWZOVBkXaOt8lwi771NXHUwMDFjQtTeWXA16uVIhSWSoF5QbO7APa2hXHUwMDE4sVx1MDAwM+2jhjte6IgqXHKmyYbaYKAkbupkwJNcdTAwMTHjpffkZ43Ralx1MDAwN7ZcbpktuLWUXHUwMDAyi8Mvulx1MDAwM9LX23DU3pFKXHUwMDFktFxiXHUwMDE4N/iadJQnPFx1MDAxNbbWXHItapKOWNIz9DmVsyyHiCQ75lx1MDAxNSZ3iJJcdTAwMWQ7THxcdTAwMTRcdTAwMWRPXCJcdTAwMWVyXHUwMDExXHUwMDBlh1ZIc/chbumh+1x1MDAwZSHPXHUwMDE2UOnxlz0+gfjdev0hsZB5oT+O5qjyI75cdTAwMTEgXHUwMDFmdjG/clx1MDAxMriN7ZuKRih5md5cdTAwMTAoOp+rXGJOVPS6XHUwMDFk8UtHXHUwMDEwVbcz6bG/3m1FXGLHN4t/XHUwMDAwf97r7lx1MDAxM1x1MDAwNnY1+OpcdTAwMTCfJDdQXHUwMDFlSX9lamt2gVx1MDAxOUGVNsntXHKZ4JObZPklXHUwMDA1LPVVZJs7jTpXOqW0XHUwMDBmse92/Fx1MDAxNbNcdTAwMTHss1x1MDAxY6H9fJHxPz7PzkeYKFx1MDAwMd1cdTAwMTVcdTAwMGZF9zRzPVx1MDAxYT5wQz7Qb1ahWbHLz4+JXHUwMDBmbnvg4N2zZUJcdTAwMDFzR2dTb7NLpytbXHUwMDFjn67cus7r8N5ysWjnx06lXFxgdlx1MDAwMn70OTSNvFx1MDAxZLjjhJFcdTAwMWZcdTAwMDQ2iplIQ8CjnPKplN9BmF5cdTAwMTBMsc3askWk8lxm4TXfb1x1MDAxNbVcdTAwMTK5dvT8cdu1KY4yXHUwMDFiXHUwMDE2XHUwMDAx6CdcdTAwMTGBVnhcIq3ajMGHS8/oXHUwMDAxQLen4FR/T2eefjLzkanuvsFcdTAwMGVNam1Ga4PHj+k5o+zwfZ83XHUwMDBl61wiXHUwMDEywHK8ZptVXHUwMDA3XHUwMDEwZdLsV30+5zjoXHUwMDFi2yeollxuXHUwMDE4nI5dyDtcdTAwMTKDo1x1MDAxN5VcdTAwMTjoXHUwMDBlqz1cdTAwMTIh9SDg+lx1MDAwMbiI7y2u+dn6sM5sa9XSOYNcdTAwMDZeoYdu72xccs9cdTAwMDVcdTAwMDPui1263/RJz3j/vVx1MDAxMLGZ69zw04Dt++qp4rI5Llx1MDAxYor2YXlbiFwinNZcdTAwMWL23m5cdTAwMDJp/n2npPrmYi+AX949gnhWh8iqP7reJ+gzc5Gi/HTudTrkQ2p7IKW+227sPWxeXHUwMDAzYcLqiLxcIlo+bOXK+jJcdTAwMWafyENcdTAwMTEgO+jPZlx1MDAxNqDMXHUwMDE0K080XHUwMDA0YuF7xGdVikL6rCbg4oNcctFccnNgoipcdPvx79CfQVhufvu1gZH4dLNcYkWAXHUwMDFl8tC0g8d4j0hcdTAwMTBMXHUwMDA0XHUwMDE1XHUwMDE4JLt4JnvnmCy7uWdcdTAwMDd7JXH0gfTWKy6X4Fx1MDAxYVx1MDAxN1x1MDAxMdnMPPaO5nXSqqjnftxjjZ/AXHUwMDAw1iDyXHUwMDFj+9WOPlx1MDAxZkXh8p1fXHUwMDBmvXj76lx0NeqS+5pfjaHmaX6koCSHTZG3K9hcdTAwMTZDXvRlXHTsq1xi/mvpeyej26O3e61Pe5U0XHUwMDFh2jH2eUrtvnf9XHUwMDFids32SXN/ZutqyHxcdTAwMWU18+K74UQoei3jXFzrXGZcdTAwMTeW6W3nnFx1MDAxM3P+iZ+y+vDkXHUwMDAz7ZxOzG5cdTAwMDHvplx1MDAwMCUq6Fx1MDAxZb0niUGdOag9XHUwMDFiXHUwMDE5ZVx1MDAxMS/iSFx1MDAxNFWATosxx/2LZ6A4VWtVXHUwMDAxTbtA6YvAtnDw3FZcdTAwMWY0hFx1MDAxNb6r6lx1MDAxONDqqbhKUORcdTAwMTRRXHUwMDBmvZlcIpPwhVx0JZlcdTAwMDGeXGJxr42ZhYZcdTAwMDWiKzSXSsE+MuxLvyaxXHUwMDFkcI/tXHUwMDAzPSfgay1cdTAwMDKyXHTQwHzHg7dcdTAwMDJcdTAwMTliW9DCj/hcdTAwMDPEZ6RruW5cdTAwMDVcbmtvSkNEa+5cdTAwMWEzWHRcdTAwMTDwqFLr0Ty6aP9cdTAwMGWR6q5wnJW643hadVwiy+ksm1x1MDAwMvDRXHUwMDEy8GC64KS2eTTjiNmGUudN2Fx1MDAwM0FJoLJz21P+vOVcdTAwMDFcdTAwMGJHqS6i91wiQ1x1MDAwNITdbXLXXHUwMDAwYKLtNt3EOZr+gcXQ2JIyRc9RPrrY5lx1MDAwMV25wV9C/PW2XHLRd1x1MDAxM9+qTFUvXy37XHUwMDAyxVbfy8c+XG7vJVx1MDAwZalzh1F8o49cdTAwMGViXHUwMDBlTkiAZu9cdTAwMWX+XHUwMDA1XHUwMDAwXHUwMDFkVfv9Psb2VKS7KudRYilPxlx1MDAwNSpcdTAwMTGql8k6ZDDfOHXTvt+Dff4jRYYvJeBcdTAwMTbl/nHMN1x1MDAxMCNg1myPxven4X2f51x1MDAxOM80pkc+vqdRpsXpTlx1MDAwNf5la/asbzHiooNcdTAwMWHz1s49XHRFTtfzmVi+4CzI3nVE6WdGXCL22oGeXHUwMDAxXHUwMDE30Vx1MDAxOXHeXHUwMDFjeUjN1miw3lx1MDAxYztX+kcsh2c9+7WAK1x1MDAxYdBjsvgmPmG0uO7qPPrE6U1cdTAwMTlvve8vyUHbI3XXL9J4XGbqXGJIXHUwMDA3XGY3XHUwMDEz3Vx1MDAwMN3CXHUwMDA03S+b0WlVUdw5XHUwMDFh5lx1MDAwYpGjXvG9wjRcdTAwMGbTkuTKXHRcbmX3s1x1MDAwN15bUanCPlx1MDAwMInC/Vx1MDAxYSRp91xcRFx1MDAxNGZ3jXSvfFx1MDAxM2hcdTAwMWLRs11Z8ed1UW9bfbfsQ8FTfdzlXHUwMDE3+f3efqiOM5eVUlx1MDAxN+XJ9Z1cdTAwMWGPXHUwMDFjbDlcXE3d4kLhM6hK2Fx1MDAxZdzPzJ8l+7F4YC5Zh0OdUWCQWnvoS690hVx1MDAxMeJgXHUwMDFihWriSjlMXym61nFQxsjse3/j2fHfNJKjv9xcdTAwMTRcdTAwMWTVM09fWaRcdTAwMDVAYktcdTAwMTdcdTAwMDCognK/jsKXr7nmmqTZcqlzXHUwMDBmSFx1MDAxOVx1MDAwZSSiJzx30NJLW/JcdTAwMTglUPsnXFxcdTAwMGJcdTAwMTaAJWkk1Vx1MDAxOH7UQJyJg13XXHUwMDBlQqfcvriAQ3rxXHUwMDEwsuaiWoJhyVx1MDAwYt+N5dT08479lkajXHUwMDE1dSZcdTAwMDEpx+GTlJSK5FxmuKwjUtEgQ+BcYmBfRFx1MDAwMc6zXHUwMDFj0NZDNpjsXG5cdTAwMWZcdTAwMTWQXHUwMDA0WIynYcZVWbqAPYFcIoZcdTAwMTUrxVx1MDAwNcV8tSbZ6u5rwH01XHUwMDEyXHSC+FnPXHUwMDA0j+8356Tfh9BAWJ48z03rkElU0O9cdTAwMDZThnoqopXsrULrb7gx5s03S2VcIrDM7lx1MDAwMnS2m2XuWppcdTAwMTRcdTAwMDE+zVDEQe8mXHUwMDAxXHUwMDAzXyH3Nlx1MDAxZJWQmpA3XHUwMDA01zCCdovuP38swfp5WPswhGi6kKTQ+1xcZki7gkBVTFx1MDAwZpfLXG6dWm/35iWpXHQ/YXHv1zJ+PUiG/JePet2G67/z1lKrJSE7zFx1MDAwZSMwSFu2LXyEVLzzhVx1MDAxNjj7ld7mzFx1MDAwMJ5cdTAwMTkgvqKKXHUwMDFlXHT3nmxcdTAwMDYgeFjKKoBOmGtxvqugXHUwMDFh3n58PDLAQbrdWEzRrKW8f0pcdTAwMTnSlGNfgGDwcKt1/1x1MDAxMkTste3v7cf4XHUwMDFlX5nyLHKcV1x1MDAwNthZXHS46jhElyuCiPhTj1hh7L1cIjfIZ72u6aGa6MNp4PSgPrDq9/JmUuRG6H+8td5Sy2wuUK849tDGWoBgsunplHdcdTAwMTKUK33jxrOpgWh1XsEtb6j1WHtYeqqa38g2skZCXHUwMDAzkZykdFx1MDAxN1x1MDAxMF7FivhRmq4jqaNsR7dcdTAwMWVsqCHfUtWGSkNcdTAwMTfF/oVRKO+cufU+2lJcdTAwMTBls1x1MDAxNWF7mVx1MDAwMTxZpelWYfD1VFSiYrKAvzk4p6trZl1/56ZcdTAwMWHg6Nz7jlx1MDAwYmdcXDjo30i+XkTMy6C9eJz75IvUuirmzlIrqirZd9r34uSE6/lP9z2XoczXeHSXyCZcdHNcdTAwMTCN3Lis9LxIalZkpVx1MDAwMr2j17raPCtcdTAwMDB4I12e1Vx1MDAwMomg79d2o+Ri8lGWQrOQXHUwMDExjFxyVV88hHKsMb9wXHUwMDAxrubrINCaxFVPXHUwMDFmxXDWXt84MM80oa83tfuUtXJw6pdsh8N19iiXgezzyZmZXGKKVVBOZVx1MDAxMi3NftnNVsObe5+iz31M3uBCeDqt5nvvuVx1MDAxNKYrINK8XCKyzG09k1x1MDAxZNeVuCXZ5Vx1MDAwMbRcdTAwMTZ8P1pcdTAwMTFpsZuDdMhJ6Xjvp/48s8yoXHRcck7Z+3hcdTAwMWQpXHUwMDEzXHUwMDE23mWMJ/Ik79KIilx1MDAxNefOt4emXHUwMDEwyamB5MaFxjhiXHUwMDBi0CYrJ2GD4UncL4+/Yr0tnJomXHUwMDFmuZ/S/VxyXHUwMDA1bKxcdTAwMTHfTaNw/+C77t5YXHUwMDE34fxRdo6d4lxmv9b8QJgq8cE3jklcYpwoeDpcdTAwMDVcXLY9ICd3QlxivrMrzlwi+CFi/lxcXvPet1xi0i3eLZLU5iNcdTAwMTRUR62TXHUwMDAx/fpcdTAwMGWw3sbrbVx1MDAxM9OLm1x1MDAxN1fydP+6RuK02Vx1MDAwMI2JXHUwMDA0hS+qXHUwMDExwXSLXHUwMDBm+9WdU1x1MDAwZYFqbXYwqWxAe7TQ+1x1MDAxMHB5XHUwMDBmzEBj1/zuxtxIcFx1MDAxOZkr+ibo32tcdTAwMTZqnqNcdTAwMGZ0y3hIXHUwMDAzO1x1MDAxMoWDVctAh85cdTAwMTWGWlx1MDAwMvhcdTAwMTjWN8thck1cdTAwMTJcdTAwMDFcdTAwMDRFUJg7KFx1MDAwMj15Ni/mi1x1MDAwMJMrRFrtlLK+2XLk+KRcdTAwMTGI76E5qj1NwiNcdTAwMTZXgCkqyJ6uZWMsNHj0R1x1MDAwNlHeXHLA++Yn3q6SnXkheIaU8lxyIFisw3D+XCKKgFx0mo52iejMlFx1MDAxOGy/XHUwMDA0vaR7u8eUXHUwMDFhwqRwOlx1MDAwNFx1MDAwZVx1MDAxMMhxbaBcdTAwMWJcdTAwMTFwqOxcXMhsXHUwMDA00E2lgmd66udcYpWfKbx5eFxyXHUwMDAzgeHopFxiY8s3ebleb6SSirspxlx1MDAxY+ZcdTAwMDSHXHUwMDEy0pztx1x1MDAxYcFcdTAwMDB0XHUwMDAye8eIvEeTXHUwMDE1g3k49Dd4cHXYeM3Lh2q3fXblPl3xnrdxgCpcZijXM71cXJy+goVwuWwpXG5jmMhlc/PJV2LTXHUwMDEx8OOT2JOffqZqXHUwMDE3XHUwMDAw9i1K55emtFxmdO5Fz3QkK01yXHUwMDE2r4+cWOiVrpl0XaEtWKFbW+yKToNNXX7/XHUwMDBmP5MlMt9X8Vxy1Vx1MDAwZZiA9740S4dR+TXsaFx1MDAwZrE3fY7BXHUwMDEy8Fx1MDAxMlwiXHUwMDAxXHUwMDBmpsnHuTqi0yOjg3zf5uLy8OTfxLxcdTAwMGJxsvy5wWVv1yeANEGku1tEPI9YQMdUVlx1MDAxY0btrduSXHUwMDFi2195++BVxoD9++Hm4YvgZGqAnSWjIVx1MDAxZsDxT7CDVnYmb69cIkrY+pLEmF+HLHhQrCP9kqk0XFztr1winFntVvBcdTAwMDb2wo3p+CSFM+A+XGZcdTAwMTPIN2OxXHIzXHUwMDAw9lxmZFx1MDAxOZdcdTAwMDAxXHUwMDFlXHUwMDA3crqn21xmXHUwMDE40ddcdTAwMTQrrtLbXHUwMDAy4iVcdTAwMDJhov2HeH5gnVx1MDAwN3FcdTAwMWHVoVuuXHUwMDE0/611OklcdTAwMTRig02xXHSK/vuQ6Fx1MDAwZVx1MDAxZa5vy0udaOx+cpv3jFx1MDAwNFx1MDAxZEDHvtXf3f1+w1xu4lx1MDAwMEvYQZL7Usadc5H0vG9cdTAwMGZUdPSaLkMpU3WK8aBYLe0zXHUwMDE4Wlxull9cdTAwMWG685BcdTAwMDJgvrvRtjHUguj8h8DsrZ3MPd2A37RcdTAwMGV3Kr1cdTAwMDCqJv13d49PXCJcdTAwMWFbf1j7Q9+efKjxXHUwMDA2bC3b1MjYXHUwMDFiUfpNnJXZ0YOnOFx1MDAwM1puXHUwMDAxwipcYlx1MDAxZV1ccmjsUTLHbX8lamnteEYmXHUwMDFji7bO9v68/Vx1MDAxNOUzXc7zR2yTXHUwMDFkiyXuNS3ZJpRzQ26dupZbgUAmwMhONOFq5ldHXHUwMDE02Vr1frdcdTAwMGJcdTAwMTOSsTM5MIDQUrpupqJXp62rhMiDZFT47yZcdTAwMTXKMnU8JuorR++Dflx1MDAwMFFcdTAwMTCczcjDStgkXHUwMDA0Xa5EPpuhb2PLluUkTZTzXHUwMDE3XHJcIvu80HlcdCtjXHUwMDFj+XuJTFxyg1xivX1cdTAwMTZfXHUwMDFlUJaV2T4s+GAummSdbuNcdTAwMDWC52C0XHUwMDFk5DFJw5bNauTE0thBoT7keTZKhdvYXHUwMDEzLVx1MDAwMFx1MDAxZE1cdTAwMWaEor+8z+c7XGb4J3PR8d3pL0F4KZ96XHUwMDBmky/U71x1MDAxMW6C2y7D+qdQSFx1MDAwMP3e29HcPdh8tFxyXFxcdTAwMTHU7O/0wFx1MDAxM1x1MDAwMMDhdT+gISTVmohsx4PmSFx1MDAxM2t7J1x1MDAxN6ks0kLJnVx1MDAwZsXXqmvu7vr6tJqLalxio5hcdTAwMGaXXHUwMDEx9CS3Pm1kxeGHp1xyXHUwMDEzTFpZXHUwMDExkK9NQ8DNmNxmXHUwMDFkn8BOvEc8d1x1MDAxNlJBXHUwMDAzcjrAXHUwMDAwXHUwMDAwi4ZcdTAwMTWyIFxi2lx1MDAxYiBASyXNgWzePeB6Nlx0Z1x0oPRTkyzv+Y3lNVx0ICnBfJr8XHUwMDEwXHUwMDBil5rJMWTqXHUwMDE3idU1Xlx1MDAwMKRSI/FDv9GHXGLJLVx1MDAxZFlcYszNXHUwMDAy3Mrpi/rMdU+jXHUwMDFlO4Mj7n6Npiz9fTjKr1x1MDAxNtKfdPBeiYnf2nmDXHUwMDA2OGhcdTAwMGUkV75cdTAwMDUxPs5/Vu7rzJDtWMs6XHUwMDE5VjN+XHUwMDFjKdYl4KBcdTAwMDfSXHUwMDAwiMZcdTAwMWLt4qHx3iDyMVx1MDAwNVx1MDAxNrI4Y84g8+2S4OVcdTAwMDNcdTAwMWbHXHUwMDEzYMOLZeT98+LtXCKIWEA3wVVN0Vrk3bP45FxmzTJWXHUwMDFi2lx1MDAwMaX3TF4tiWnilNOrelx1MDAxZin2xGrWvp+HWu5cdTAwMGXcnkG2d6HV9fRcdTAwMWLkVjzvkuxRjOdcdTAwMTWdjInTXHUwMDBm9X2YXGKtuqVudscwkVx1MDAwMvJ+1n3GXHUwMDEzXHUwMDExXFyUR9h8sjRYVnzdxW8s3inHXCI9+S3ji+586c18NObidI7tJs1OXCKR3VSvlt9cdTAwMTJcdTAwMWZcdTAwMWaOzZXzSrRpXHUwMDE0fNWVmWqXbGr/Ic9VmHmWcVsyfIFcdTAwMGafJHSKUnkvaVx1MDAxMKexv0hcdTAwMDLa8dnHl4qo6sppwlx1MDAwN6rEZG7ZwzxkrjLUL+nLb1x1MDAxObOE0+BP/63naa/EmLu6RL9cdFx1MDAxZJLJcDKQd2tM/dDbRoXNXHUwMDBlRmTFLarH53zV5q3HX1xc+VhC7IbA/Fx1MDAwMpUyh0Nccvg4sn8saJRq0lx1MDAxYVVeOoyN8VxcrXxZJZbu1q25a4znb/vk0V2ts+om1Ic2pMrlXHUwMDE3JDTJgGhcdTAwMDXiJ1x1MDAxYaTAJp13QtdkXHUwMDA0c+781WohKaFD6ubBe+j+qb9OWX1cdTAwMTWyWztlw5Lh9Vx1MDAwMZDEjjsqn1x1MDAxYfmC+oQ5lzokRYlcdTAwMDMqwMtcdTAwMDD8KzvDXHUwMDA2k3OrXHUwMDE41+CE1/0+lG9km4L8XHUwMDE1lo+TaDFcdTAwMWXmbKDgXHUwMDEyN76VUzTDVujm41x1MDAwZZw+ut1MtaazpPKnUE10W5DhXHUwMDExdFx1MDAwZvlcZiW+m29Mp1xi+UFcdTAwMGbaR2WjWlx1MDAxNpdqaoWEnFNlwVx1MDAwNsJhXHUwMDE5echcdTAwMTnYL1x1MDAxYXs9dX1lPDbMwHvuXHUwMDBioFxiOVqiXHUwMDE46au499BiNKyTX335nFKu4ujBTldYMXi3XyvKXHUwMDFlb2Fb0Fh69YS6T7hkY9vZXHUwMDFkvlx1MDAwMeTeYU+YXHUwMDA2zXJcdTAwMDNOpXN+XHUwMDE05yBTTXOt79FGXHUwMDFmTUBSUShcdTAwMTBcdTAwMWHffs/OKGNcYiSxOYSTWWbUTIxHnNYvNVx1MDAwYkYzcVxy8Vx1MDAwNbPcXG5m1EhloFxux8H7XHUwMDEzXHUwMDAz4N1cdTAwMDe/78qnf77rK2nOV1xcwtNcdTAwMDNcdTAwMDdcIn6ZI4HUwoCCyKRgXHUwMDFkXHUwMDAyXHUwMDE0yVx1MDAxY7VZXHUwMDFiIYv+JDmhuf1cdTAwMTV7nHohIb2yVc1VXHJ2We6mXHUwMDE2qlOytlGSN6FcdTAwMWZxSorVq1Dkbzt6a71sX+smXHUwMDFiSFx1MDAxOc7ZxVg5XHUwMDFlWUO3RLNcdTAwMTgtyYTH8YBWK1x1MDAxOVx1MDAxY7NbTHMx0yRUvlx1MDAxZDjKdFhcdTAwMWE2loFzzb9XjuCwXHUwMDFln+XiU9uKYUSlrtrvsIta6fT2XHUwMDFiXHUwMDE15jPjfcU7vteIQLdcdTAwMGXNj+KYXHUwMDE0J7Lg/opeuaWNNfmRXHUwMDFigVx1MDAxYi/whb2E7MRqrf5aREZ39PQ2wKaTXG4hJKTsPs/Qjn1wcJhcdTAwMThKXHUwMDE4JVjscTbOOs9A0lEkXHUwMDA00JtcdTAwMTH2v7pcdTAwMGKA88xcdTAwMWRqlFx1MDAwM++acrPPLTjaXHUwMDE3TqXc3HAlXYrh0lx1MDAwZfS2dVxiUUrGfn+qfd3JN5vvXHUwMDE2XFzqcJMqJ1x1MDAxZV6jXHUwMDE0UT1D5e2edogn7klcdTAwMTXbXGKM8/NcYpslpYn3THI1luVaXHUwMDBiOGg9zve+v53Tr0aIZSSTfbN62XmYXHUwMDFmLkr+9eHlbSRf0bwhdVAnTVx1MDAxYSzK8TCNpXzpVcJcdTAwMDHUlirCMIzEsLL1XHUwMDEyXFxZLLxZeeri/DGZdr7ISGe0XHUwMDAyr/3SK7qd5FZcdTAwMTS8M1xcrJBDm3N053Jx/8rwflx1MDAwZrKIXGJcdTAwMWU84ZAoXHUwMDFhc1x1MDAxYsZW+s0wa+9pvFx1MDAwYrTJXHUwMDA28i8qkWwxXHUwMDFmMJXdosBcdTAwMTWfXHUwMDE3M0ZsoUdNaWs2hujHl6JeXHUwMDE2XHUwMDBmXHUwMDExXHUwMDA3XHUwMDE41mmTMlx1MDAxNFx1MDAwNm9kqmRvXHUwMDFkIST2SDLdQ46Eo1LL8GT2dbZcdTAwMDJcdTAwMDblVMp7M/dE2utuMKtjrlx1MDAwMn/7pFGPdTJYvFwiXHUwMDA0LO+9L7tSXHUwMDA2mNLmq1x1MDAxOPxcdTAwMTdKqjTjlJdcdTAwMTFa5ddoqyD/bO5TWlpisZt7cC2lpeLq4lxc90PvNMxETDJFjWpgm5yLm2mCkNLOkqxFlVx1MDAxOZaVXHUwMDA0cP5z342TWz2hUZz2tti72fU2c7dcdTAwMGaBKbiPXHUwMDE5jqdxd0TjpVDTWrhSXHUwMDBmzJZQMVxifYlIn0bQ0LqqmpSjRPGDdVx1MDAxOKuN21x1MDAxMp5706L7Z9dcdTAwMDTbl4TSTlx1MDAwYnT/PpfXqHdDRLshtv5WQOmB4ohMXHUwMDBiZmni6vuG6VZ848dA6Hp94dC6XHUwMDE45jtBXHUwMDEybEqgXHUwMDEyyT/iiz4l6WFcdTAwMTJsinFcZj5jtG2P+rdNPnJu4L+yUKmv7nlWtqdvXHUwMDFjXHUwMDFh0lx1MDAwN9B+Q89R3l9cdTAwMGbpht1aUVxuXHUwMDE0tFFcdTAwMWG+I/KlXGZEp1xmOFVYiVx1MDAxMF47wpI882p3L7uFKpOajSne7OdcdTAwMTVKXHUwMDEx43JdJVx1MDAxYk/utnWG9/1nOHdvqCCxYsW3XHUwMDE4rZfOnNImXCKl8ECPV1x1MDAxMznWe22fTEqdVmyM+7HXXCJbpShiUFtVZ73Oj+xcdTAwMDSSXHUwMDE5IDhYXGKrOeCt0X096cth8cfah5eNUn78Piq2umqE02v19cHcXHUwMDE44ULaOJVbp1FcdTAwMTGMvpfLdLF2OirRXGK6gIsmjJWjXHUwMDE2XHUwMDBi9Kelhl1cdTAwMTSsevg6aPUhi3U8KSlcdTAwMTJEeFWxXHUwMDBiXGJfukZcdTAwMWaU95mfzHYky0FcdTAwMTPEj2X0LUXpLY1cdTAwMTK0fX9vUd6vXHUwMDAwObGqqNRHtVxuRSdcdTAwMWXtNJrW8CiNh2Z/WqhZOatZMORcdTAwMTYy+lx1MDAxZFx1MDAwNqCsMlx1MDAxMp5z5JtIpXF0XHUwMDFl/cxcdTAwMWRPjmzN51x01NcqYHbd32S7dy0vZ6FliPFcdTAwMWJsJk2nM1wi5D/Xy9HQXsr5Q64xw35oQ2fg+upoTOOV+Fx1MDAxZM5cdTAwMTWjRrro8Xd+VSm4xuuFouhdsU5cZnw+OVx1MDAwZlx1MDAwZvA3J3NLoOnsoSosa1xcaSGHvlx1MDAxN06T27FXiSlDoFl8LFotXCJDgUxY0sFcdTAwMTlBcWvrXHUwMDE5lo37levVL1x1MDAxM8RK+Vx1MDAwZfB4ZlpcdTAwMTH5/UFcdTAwMWT+XHUwMDE1iVaFTSd8Yi72wjdcdTAwMWOrXHUwMDE4a32VR2pLolxyapGJsqtHLeIhXHUwMDE07ud1XHUwMDAxXHUwMDE19jqF9vmyIE3Ih3ql/GX3z8fsZellqv2QYVx1MDAxM1x1MDAwN9JSXHUwMDA1XHUwMDA33sr80N1Mq61y3SNcdJmnJ1x1MDAxNpBN+rVMre5Y3lbzXHUwMDA216xcdTAwMTl4513paFiCnbxM25Js5zeciKpYw6+dXCLp2nH0lVmcXHUwMDA21KyuXHUwMDA1vfv8ZjmX6NJTZ1wiy1vCuHazh7FX8kk98Vx1MDAwM76oJ2JcdTAwMDbnXHUwMDFjzFx1MDAxZSjGl8rQzNqlhVx1MDAxZVxuym2vuGSUai7dSWti1Vx1MDAxNOrbbVx1MDAwM2CIibH28JSYoFspIIy8R8yFkODjKaO1NdsxKq8gfSHHw+9ZtlxcKNVIRjxcdTAwMGU47ZNcdTAwMTmJtYjJk0PSraTT4cV+8taP31jF8aLf37HAk276jKddXHUwMDFjKos7OTmrI19cZswy2CNcdTAwMTeBwjp/5CrsXHUwMDBlek2rXHUwMDA0IFx1MDAwNSlcdTAwMDKdMvODxm7Wd9VJXHRrpni+clx1MDAxNFN6o/NcdTAwMDF1/aGxlaFcdTAwMTXV8+GXaziWMTxg4caoo+vrJ8ZGScbXzy9ZXCKYZjFibF5cdTAwMDXgx6ZcdTAwMDW4e8SAUFx1MDAwNHyFZFx1MDAxZXFLUTysba5Xt1x1MDAxMKXHtedz7TbyXHUwMDAxrFaixjhl6dYxXGJ4n+an1lnNfilcdTAwMTFoXHUwMDEwIfR1j+5cdTAwMDYxa4FcdTAwMWKMvHlcdTAwMDGqQsPl4layo1x1MDAwZSDLczRcdTAwMDeHM0rCQkJUxYppzD/2y4/ZfF2opapcdTAwMDFcdTAwMGapvU41O5enXHUwMDAwfXHasX1cdTAwMTQ74Pe5p+fVeVx1MDAxNu6Dk29Ge55ZUnDfvGVKndpguvTp/ZZqaWFAnbBteVxyTpBhSOB92OZt8qvwIVx1MDAxNspcdTAwMTCZ8ZFpjFx1MDAwMGL0rmpn8NvGKrUnPOxFWDpcdTAwMDCML7H1XHUwMDEy2WI+XHUwMDBmJTJcblx1MDAxZXIg6f7AXFx4J1vu1IP30ZVneVxczJGli0rcI5YjSUU0x4Y8kEFcdTAwMTP/XHUwMDAyZt+Wu+Ly29ysgofzXHUwMDAzU6NcdTAwMWZWLS/pN+K29ihcdTAwMDSzXHUwMDAwtDPKodTaq/A6XHUwMDFm5TGbYon+3qg8pjJcdTAwMTeqZNs9UVOwvFx1MDAxNclcdTAwMTmoyzq/0jg9T+0qvUenM0hBK8pjXHUwMDAxSO9cdTAwMTOa5dBcdTAwMWH0cabHLYHqXHUwMDE4gbVcZjCvlEjCZGffgVK4r2+jSieTQGaofSFV4r9RXGa/RtmN4iMhv6JlY2YyS15cdTAwMDWBXHUwMDBmXHUwMDAzTVWhaVx1MDAwZUxt2jfdMUm3/oZcdTAwMTg4lOSiiPL3XHJcdTAwMTBcYlSOXHUwMDA1wnCouP89T3LWh4n+MMubdk9dk1lVkp5nXHUwMDEzXHUwMDE2xveBXCKQyFLuXFx3fr2tzup1m6JCp1ks4sjtQ4jh3l6vJFx1MDAxYmBmu+JcdTAwMWOlS1x1MDAwZdidXHUwMDFlXHT79dFBqY9cdTAwMDNPmfBZxH7fY8ixd9/QiXL+ns3BMsHSXHUwMDBmr1x1MDAxZVxcX/hcdTAwMThD9+S+XHUwMDE4d6n1W9BNzl49miSfRd5cdTAwMTJcdTAwMTCFUi1cdTAwMWNlQSHHu/tQRJGAPUFcdTAwMWRcdTAwMDZ/XVR/h05hn4jheZxXV0dcdTAwMDNWtVx1MDAwN6lcdTAwMDVWOTnnq9ZcclVvf/MlJEWU4c1sxnJYrzjoaqZUYFS/XYfRYVhDP1x1MDAxNTSRLlx1MDAxN4mQNeWwhPLo7I6UllxmmdfXXHUwMDEzgvE7loNYUG4kYvqZVY1ztDgvxmn39t7e7HZcdTAwMTMyXHUwMDE30JJdYIWBPFx1MDAxY1nLu35DXHUwMDBiW3ivliP/P5rOY8tRKFx0olx1MDAxZsRcdTAwMDKEZ4n33rPDO+FBmK9cdTAwMWaqZ2bX51RLVfAyI27wXHUwMDEy6N9cdTAwMTB9hv6z+efv+Fp60NTJXGJPbcNcdTAwMTVX/jXmXHUwMDBm2vVcdTAwMWbQklx1MDAwYtdcclx1MDAwMuPJ7vlcdTAwMTDOWmY4uaLdW7X5PfFe/SA/Qlx1MDAxYlxmlcCnzKiriIHkp59cdOVcdTAwMWVv3W9/cFotfs139Hc8/1xm5XpcdTAwMTQqJf1GqMSOUlx1MDAxM7vn1tJcZiFSwMlAStb06p5cdTAwMWMnYmt3a1Ga4ilOXyFSdNZK7U5KLVx0RZZ6YVx1MDAxYTpcdTAwMTJgjUr4PY9IfFxublxueFwiPFxcyJdnwOu1IH49XHUwMDFialx1MDAwMk1cdTAwMDcpXHUwMDA1XHUwMDAxIIJku+z1XHUwMDAxVlx1MDAwZi2tymrsvinxVp0tbjRn0k5LPdGZnypD+vz4uFCb06Sic2UgXHUwMDEwNrXHRyBt5qF2PTT5TiFcdTAwMTXt7qa4hHbGXHUwMDEz7JBdPvbW5PpcdTAwMWRolT6pvFx1MDAxZuouU1FcdTAwMTd/J3Z01XY6+FxmWVx1MDAwMFxcoN7/pDOfZbvL2PCatFhcdTAwMTGP/nRUXHUwMDE5XHUwMDFmlpIgzFx1MDAxM3yXXHUwMDFlf3lQbdGLtYPHXHUwMDE0jVx1MDAxNCM1XHUwMDE5TXmX6jQrMK9Fbpiy2ZJcdTAwMGW0wEPmMOdWvjEtKFx1MDAxN4tcbjTWmrZnfyftayVcdTAwMWKrXHUwMDA1RT5an1x1MDAxYXewUCtzU30lSNKmoFx1MDAwMWSdaa2asPBfYqufsTkzWUB0t9mkYYC2XHUwMDAzVDyuXHUwMDAyW+Kwpp9cdTAwMTJcdTAwMTlg+Zh58D03/lVH/PX/pFZT4WNEXHRv/4hvzI7DpUBqO6Sl3dlpKkbDuWQg0jKfuKxqJmFd3F0hMUbkLnZsbMUnpP9EdybIca2Q8CV4XHJcdTAwMWR00lx1MDAxNEpykVxmXHUwMDE3WuusUlx1MDAxNu1cdTAwMTRnyTLLv5R+04K9+NjR8PG3XHUwMDE1XGaEJO9OuVxyVr70M9k++yU0cohF7k9cdTAwMWRoj+7BgHyOoLHVKGFcdTAwMGJYcOPkYVJcdTAwMGJuXHUwMDAxXHUwMDAwwKPreZOS23mUlCU+nGR0M205rJhcdTAwMTSuTjKNiVx1MDAwZnPYx3JgyFx1MDAwZnxzn4S+cZpStm/DXHUwMDA2pdTKw4nz9pstyMh+XGLYQWVU01j5XHJiP7n66YhkV5/TbFHV9Ufs2/d27lx1MDAxYVx1MDAxNy+ztK3LXHUwMDE4Y7Bhd78pv92ZzoZlUMlQR2zjJj1Ri8lpKrVQveW7t2dd5CWdXVx1MDAwNOZX4oFcdTAwMGVVXHUwMDE0T8fQ6Xc0fkB5uj6Q4iS9nFx1MDAxNlx1MDAxMdzIx1jJTunDhNMgVlx1MDAxMTPNxitidP1SXHUwMDEzoaQvpIifVt/WsSBL6KmD5MN/h1x1MDAwNEJeXHUwMDAxktaI//Y6XHUwMDE0hOPaXHUwMDA3r/+8OdwsXHUwMDEzroGc1NNcdTAwMDHnXV7BXFw08lx1MDAwNXe23uQuM87kXHUwMDE1bMAsbr68oVSlOlBCnohJ/ILhI0Hd7ffnYrHhXHUwMDE2ha5rL0CrXHUwMDEw+1xy4dK9Uyd94uphslUoR2q9I+q1jGnn9EZZTrxsSFxiXHUwMDFiIexx5fvTVGKngJ8gq1x1MDAwZknZfa7/TF5jNlx1MDAxN6LtqE9cdTAwMWHspzznXHUwMDFmg92mtSv8689o/iXh41xyNpI3cZ1cdTAwMGWPp7XesjtFrr+sR1x1MDAwNiHtol28XHUwMDE3hetcdTAwMWIv6lrZbkYn39RsgiRo7Vx1MDAwZsE9XHUwMDE0NMhcdTAwMTLTJIQp8V1cdTAwMWS/Vc5BLJz8N1x1MDAwN1x0QV/CeEwvTfbp5VVve0VJVpJBtFxurEz9jVxmZ0rflVt72bN2N9pYI0zYWyt3s1x1MDAwM+uz7pnfm9h3XHUwMDBmypvAwXBZ3rbwXHUwMDE0vNxuWVx1MDAxYre7WD0lQ1x1MDAwMeYhiFx1MDAxMGf98ECLu33SLXFY0lxcKpir3411XHUwMDFl0rlKcKfePiVB16XSfGko9lx1MDAwZnd7prCphZzo5cOxtc6O3OXrXHUwMDEzZqhcdKdbxVROaaU2LJ6MxVx1MDAxOOk4WUuOnvmJWetLjrw0u/Oo/D7jhMOlXHUwMDAyX6xzVFpEW1x1MDAxNy5cdTAwMTdNu1x1MDAwNEA/QnjvJ1x1MDAwNV7GXHUwMDFl2UyBSLtcdTAwMTb+pXGUamt1I3IgpnNpqGIz4XV2ODYluIm2jsSXXHUwMDE3uSC0hZ3vZMs+f1x0UmA/+YpkxjBcdTAwMDVQfFx1MDAxOf1zYVwi3X9uXHUwMDE3fossMV7b/VxyXbk4tuSrZancTMN4QtNqXHUwMDE2k1xuXHUwMDA0UHJk6oIxWK9NlpRvy1x1MDAwNsXFyVxuO7OiI9HfXHUwMDE5Ud9wYDkhdFx1MDAwM1vHTf5uxpm8g2BcdTAwMTPTtFx1MDAxOVx1MDAxMWqeQeSdXHUwMDBiyIpccmmyXHUwMDE4wjXVuKW8YbSqjpXD5TNB/vgsXHJLkbk4VKRtmVx1MDAxMWCvniSFjyXUyP5mXHUwMDFm8J1PlFx1MDAwNvhcdTAwMTaV+ZvbN602xzRcbrAzqu7nXTRvTldeyUTlXHUwMDE4zVx1MDAwMmGURqmvwahlyPc2nFx1MDAxZUQn3J68olx1MDAwMSvawnSMV/NvkzuCXHUwMDBiMTezm+9xXHUwMDFijjNcdTAwMTeJWblcbilEKeK8mub3LIHcXHUwMDA3soS2O1Joq4Hc/iqq0nid8y3YzeK+kVx1MDAwYq5cdTAwMTWmXHUwMDBloCaH6ZvC3eLvVfN5XGJi5O3XmI7UXHUwMDFkWJVDeV5cdTAwMTMml2+NfZgmqJ/O7Vxmv28zimC3cM1Y2lx1MDAwNzj0XHUwMDExLqJEdC1RU+71kKhpn+j7/XXinYzoR1mXzz5cdTAwMGVRSaRb4ttnrb9l2lwiXHUwMDE41mnA0ZCScfknXHUwMDFijvnkST53rflcXNOLYDm0p4GuqnmSe4ieujh7M2hcYmZ17vGd+Wsj3aeNNpl5XHUwMDE1K4Poerg0XHUwMDFiM7boXlwi8WExXHUwMDAxVeewlueORUDnXHUwMDE1PVx1MDAwNvDOzHtcdTAwMWVpX8lccqc2Kz1U6ZpDIYPJRIW+gPr2nodqrS6OxZg/XHUwMDFmwkU+kClcdTAwMDP0JLzSLL16yM6wwoeV8+ud5Eipb7W1rOIhVHxcdTAwMWJcdTAwMTmSdbykJVx1MDAwZSx/VM2fxu1cdTAwMTfPNVQ/Qy86UOjLkjyNYzBcdTAwMTg3IEtPXHUwMDFleKmy8Z9fXHUwMDBmuLpKuU+P3tB1jXDaku2AfiHwXHUwMDE4XHUwMDEx7NNQXHUwMDAwO5BcYv9UhKVs9aJgXHUwMDFmpVx1MDAwYla3zVx1MDAwM1x1MDAxYlx1MDAwYvOPbPzcYuDEXHUwMDAwXlJkd1aTvneeOUDSvCzAfFPvXHUwMDA1xH7rw1x1MDAwNkNcdTAwMDDjif3wKaDQLI9cdTAwMTLRVsvPUkm/gUPi9ol9OeFcdTAwMTVRXHUwMDFib2E20Ht19E94qbagN1x1MDAxMJlcdTAwMDZcdTAwMTUnkoNcdTAwMDdAdyDP/mcrOWbLW1x1MDAxYzSoLC21R02Jp1x1MDAxNVx1MDAxZF7+IVx1MDAwZUDLWMk9KWGc8laU2bTo6bewXHUwMDEzXHUwMDBmKmArRVwiTTpwXHUwMDAxQ4RcdTAwMDWFNz31XGLcXHUwMDFkXGKdxVx1MDAwMEqM9dkqID5cdTAwMGVx1WzW7zplTflnfCW9/U9LMWrLLl/DbTng16eSdmIoS0xcYiFcZkGst1x1MDAxNYtKI6SDXHUwMDAyiu3pinHEnnuMaUerLmfHT4Q9z7tcdTAwMTTamiRcZtdZU7MkW7le6a5cIqs0Rr9zMeVjbHlLXGLJqdeePD2JWy2zLz/SnPhbmiSGcmNBSq/RZ4DiXHUwMDBi7e1Ls9dwP4xjxs9We7zyt5/TXHUwMDExKjK+6u+q/NP2PsyyZKO5L7PZo6Kaw5isc1x1MDAxOJiu3VxuJsfim8CzKqGwwSw64lx1MDAxY6hLXHUwMDFkKIohyGeQl5zviovoaFx1MDAxNOySduK77Vx1MDAxYr/epCo3I/V5tV+TXHUwMDBiw+3ZcFx1MDAwYkw0yIvqJfdccvuFrlx1MDAwYi14vKe117RpIJjerFx1MDAwMFx1MDAwN+qDXHUwMDFkRf5e0mTcyinhpbs+Kkklp3pcdTAwMDFTLbBhTctvKTpcdTAwMTh0R1uMLclcYoCJ2Xzc9KJIS7mDL+6hXHUwMDE3dlx1MDAwMS3W90On2WN68XavzNowlkipO4s2qz2FTmXWMjq/80PXNFx1MDAxN3ku2CxcdTAwMDSknlx1MDAwMsmvh1xmXHUwMDEx3JhU+9tWVJk7n15cdTAwMWP/iXuMSVx1MDAwZWY5Vd5oLVxuXHUwMDE4zIfS8MxcdTAwMDdcZqeiUVPkXHUwMDEysObIsu1lsTM1XHUwMDFheX1iT1p5YTRMMFx1MDAxZqOYYvq7/ojb0Uh8fsWvx7t+luZcdTAwMDWOy1x1MDAxNkeu7tLJ50eZ7OT76uznqv2J5pnshF/XY1ZcdTAwMDJsxCiNlpK3i+nZpSFcdTAwMDCUXHUwMDE071pq4lx1MDAxN723bE2U353fyJbXa7pcdTAwMGUv3WYqJCf9nD/3XHUwMDEwpjZypObWMECPpCT8+SXQvD2IK6aAsiBcdTAwMWbseOZDpKzTXHJWjWlye1x1MDAwN7zK+qVcdTAwMTko7XNcdTAwMDTvu0W4XHUwMDE0nlfh9Jau+ffImmfmyLGCwunLmfWPMGY9s+Lz++L1nbbB/mjRTlK8pIc7Y2s/gVbZmsd+2dDfXy+mXGbTXFxcdTAwMDN2XHUwMDE2j659dqGu1U5O+4R3WvmUXHUwMDEzXHUwMDA0RpFWXHUwMDA1+fuDXHUwMDFmlYXC3onruMmMwlvsWjx/wqXJx1Q8iC1cclvIXHUwMDE2moXtTaZGtFfhP/lcdTAwMTHlbFx1MDAxMVLVXHUwMDA1XHUwMDE4XHUwMDAyxdC5KHf8a7moyuBNgST0WnxQX1xy4jzJ98iOIzVIpEh7KLKS3u+6SutQ6njfxM9cdTAwMTdLg7fWWuBUqrfayIq5XHUwMDFlWVxyUDffoOp1XHUwMDFhSVx1MDAxYmvCfPRRXGKyXSqZWsmGM3TQlPM2KVx1MDAxYV83+1x1MDAxMjuJXHUwMDE3gH0g2YeSTt1cIlr6tJLTtvrAPbKDkFEgt0DwXHUwMDE2fNWJ2cRdfeaLkUfKzVhcdTAwMWVcdTAwMTlcbla2MqxsyfBcdTAwMWVcdTAwMDe/g/dcdTAwMWJcdTAwMWVONb2awXBcXNWNXHUwMDFlXGZsvlx1MDAxYlx1MDAwMm5W8lx1MDAwM0H6cX5cdTAwMTFvcVx1MDAxM1x1MDAxZFx1MDAxY1GanbiZbfV7rKDA7qX4XHKXXHUwMDBmpFx090E22fu5Q1tcdTAwMTbeQcOnbrac/MpA2Fx1MDAwZb9CXHJcdTAwMTnJW2gkwys8UNWqf6VytcNcdTAwMTCDXHUwMDAy5SlcdTAwMTGc536UXHUwMDA3eENmnoRcdTAwMGLw1LhO34x7XG5f2TIhbeRYlIEmkEWs/KFcdTAwMTm4flx1MDAxZlx1MDAxZSk2hDQ9JUZdgdF0mrZcdTAwMGKJeYt33NpMe1x1MDAwMFVoXHUwMDAxof1GpVxyXGa3ULTpXG5KUKyh2YBXuinTNqO5X2eF0Vx1MDAwZeLotm5J9HShX048K0rSw1xmXHUwMDA2aedrUd2b3JY+M0pdQK6CmYaBuUWaY0dcdTAwMTHN0U4563icXHJcYvTnKVxmvfWMm1RcdTAwMDVcdTAwMTdcdTAwMTf1w18nX/7Cu4WdZFx1MDAxNVx0oHSxcau0Ycyvy2hcYrVccsFxK8fPelx1MDAxNtGLOaAkvtnUYWnIuFx1MDAxMpNuiOYmnetd3NZsTvDQOZLUulRkolx0M71cdTAwMWZnQMUhsE3+d+0o6NnvsofOXHUwMDBmYlx1MDAxYUxvk1x1MDAwMZNC/633Tnbhr0Z1XHUwMDFkbCi8wVx1MDAwZd3IOdHN1PHfNe9cdTAwMDQp6Vx1MDAxN1x1MDAxMaY8smA/VD/S79KUxlx1MDAwMFdcdTAwMTerKu+0XHUwMDA2rPih8/rHoT98i/TbcSXmzYHa8+GHXHQxoPT9ckvT3uPIXHUwMDEyrrBcdTAwMTRcdTAwMDdcdTAwMDW62lx1MDAxNm4gv2J4/Vwi3EA84ElhoMQ7eZZeWv9cdNfQrqxQKlg0XGY3X1x1MDAxNWybL0WHkGZNK+LByq91zPSF9j9mXCLj+Tg07nFNR+Q1I1c7v1+VjVJcdTAwMDa2J5pcdTAwMWG8XHUwMDEzp1mTzrKVM9uzMV6lR7xPq85cdTAwMWRcdTAwMDHmyPPvOdtPjNjFi3dBfepvwz2I17qWXHUwMDEzXz74okU/WNKDX4Lub/VcdTAwMWL0MLhcdTAwMWYj+WxcdTAwMWXEOWXKXHUwMDEzMClxXFxTWeNcdTAwMTaCUXA85a+WrPGMYypccvMoeJxjhKJdXFwjpvKBdPJi+F3QKVxctVZ6XHUwMDAwVIv7if568Ebj5rOzi62yXHUwMDEx5HeoxXOWNJK8TFbIVu1cdTAwMDNVXFwoXHUwMDAxaoekXVx1MDAxMeRIXHUwMDBlXHUwMDFjykWY6SeOXHUwMDFm2cjczEBUh1xcaVx1MDAwMlx0Nn9rkJtrT1x1MDAxMN1JwENH6Fx1MDAxMNGnoE2aRphcdTAwMWFcdTAwMGKpOVx1MDAxM5TaXHUwMDE4UtxqK+Os7fqyRu9TXHUwMDBm8MH45jBKtliTpzjY3MD1XHUwMDAzMcW8iuN85iVcIlx1MDAwMXRcdTAwMDRWvzNcdTAwMTaq4uBy3KN0qaOCZVx1MDAwNGlqdIxhXHJE1FWFt5/LM9ZcdTAwMTTPXHUwMDE4PThWaWWl4lx0XHUwMDExmFRs9d1MiiPunlx1MDAwNFx1MDAwMpvnsiS+8pbG71GB/Yb97/WhZPnYW6BcdTAwMDZcdTAwMGVdSKF5cfy75PzB1lxcMpdyZ75cdDuTb1rGXHUwMDA1XHUwMDFidumY8CMoXHUwMDAwXHUwMDE4LVo/XGJDhe9xVz+xQjpcXJGZ/ea0XHUwMDE0XHUwMDE3U2h6y+swOf2bl2ytpZDJtlo+XHUwMDA3blx1MDAwNlP5gSpUSWTNuY9cdTAwMWVgVdvi2T9+zZVcdTAwMGVhPbBFqU5xS0X1kdxWdfAz3Fx1MDAwNfemsy73x2gzbUWG6dfO99nXJrMy8NqBWpRcdTAwMDROXHJcdTAwMDRBXHUwMDFkklx1MDAxZHSOd8HtWjpu/+6dJJ3fYoT1xIqNRqdcdTAwMDfxtunrsJ03UFx1MDAxNXhcdTAwMDLKLzmsWntcdTAwMGXnXHUwMDE0NHzW9YRaTE2eo1Jre7jfwlx1MDAxNn9iz/VPyMPkXHUwMDEzPZCW8Fn0WH9cdTAwMTkmMSZvcDeDTjtcdTAwMWK+2nX3xfeNXHUwMDBl1efBtVwi3cMprYNcdTAwMWL8vMxcdTAwMDA0UISqlse68Jn7s+13XHUwMDAwNy7p4EOFO5dcdTAwMGagXHUwMDBmj6o5ZeTVabi7YZPxXHUwMDA1XHUwMDA3cX0hOIcmUumBVlx1MDAwN/S3MUvUS2FJh3VNZ4GWXHUwMDE2ZVx1MDAxZZt0XHUwMDFmK+CJlX6GXHTd61x1MDAxMF6Z07eiXHUwMDAwULz+XHUwMDA2tUv4tVx1MDAxZfVHum6CuYyw+ftcdTAwMWTd48NRQ7HtXHI6Myl4iOleNjWedNpiwIt6053n2lx1MDAwM97dbI3lZIP2XHUwMDBiU+uQXcq5kkpHjn1Mi8aWcUjvrz99XHL8UzRgOn9cdTAwMGYp4IafhVxyrWWQ6OynyCOSXHUwMDA00lf9+lx1MDAxMpXoJkFH21RR0lj3s6XNjqHv9GqOTWlPXz9cdTAwMTfBTzRXXCJVTVx0XHUwMDAwiFInOF90odhcdTAwMDE351x1MDAwMXOGtpFcdTAwMTaCRpTQXHTb6CEkk1pjN0biXG41XHUwMDAzaKyP020wk1x1MDAxMS9qlzhcdTAwMDBAXHUwMDFl5WlgXHUwMDAwM/abXHJxaVx1MDAxNua+xJ5eL7u0nNI9XHUwMDEyzjVpmFx1MDAwNIpRTiVcdTAwMWH3KMNqOzVskP7UyFhrXHUwMDE1zD1cdTAwMDP60Fx1MDAxZX9pLu2Tb1tSXHUwMDExfdpMXHUwMDE28i/T/abnTaGQVqwu5PJcctBHST8ncvuZUldcdTAwMWNcdTAwMDNWP+ufsnfJWcxtpO5cbllcZv4p7UVcXFx1MDAxY4M0ICZYXHUwMDEzh8RcdTAwMTias7k+1lx1MDAxN7aKl9UnfeHZ9ozS2GXmq1x1MDAwZcWJXHUwMDBlVvGpzVx0+PCc7XPaXHUwMDAwos+PX1SDXHUwMDBlvYVcdTAwMWGwxDaBgUPL9VS/OP1jR7ulPiaohVx1MDAxNHk597h35JYsXHUwMDAxXHUwMDEymFJ01TkyOL1A31x1MDAxNzntrVqn5Yma3tZghr96TP2hNbIy07pqkYtznd1zv4ixXHUwMDE4Uvqm3I+/kJ7g7tFJar9cbtKaqY9cZmT2XHUwMDE051x1MDAxM+JQXHUwMDEz88Yr6lx1MDAxN5RIXHUwMDEzMnDURfui0lxmXHUwMDFieYBcdTAwMWMmfdRcdTAwMWNm1kScOcxCLe1qMFjUJNpxXHUwMDFkaT0sXHUwMDA1i0kjIDXrWphDa2fxXHUwMDFj5LZCLV1T2G7Npy5cdTAwMDK9XHUwMDE4qmzJMjJcdTAwMWNf8VSBr4K+/IchXHUwMDFm0aH22b1m2qG+L/s+XHUwMDBlsqPjW6f0W6f8/+vUyMZcdTAwMGI6lo+jI1x1MDAwM4LPjFx1MDAxNFx1MDAxZmFIZeMn2qqhwCtw1Y+sXGZcdTAwMGXyS0WPRVx1MDAxZMv86uluXCKZRKhApK89XHUwMDEwXHUwMDEzZrnyX1x1MDAxYfrgMsZcdTAwMDDZR4X5XHUwMDA0XCJLXHUwMDFjXHUwMDEwm6HOe82NMIR3XHUwMDAxXHUwMDE1o1x1MDAxZFxmo1Pvs3hlRltEJGDgOVx1MDAwN1x1MDAwYmyVdUP9PdhSKI56c9BMXHUwMDFkt9KVVWbX1VLtLltcdTAwMWVkk5ZV+kuJXHUwMDFkS/08XHUwMDE3tbpcdTAwMDah2M6+jJCUY7gpNFbB4ClpXHUwMDFjp78kuJ44c2RVfqVcdTAwMGV5pPt2XGLMnK+LQaawN6o3XHUwMDE1WGsn9UuF8IDU7O+5xYXksNJzdXxcYufQp4XH80bn2c3HyIGyaDE2XHUwMDBlSYmUhiba2+2vXHUwMDFi+3q2m395elPvNci4hXOui+xeQ5dzoWb9XHUwMDFie2xxXFytXHUwMDFiq2qVXHUwMDFhPEP2bHPoXHUwMDA252P/RHueO2skXHUwMDE2ocE4Zya2SHRavX9ccuQ3dkjg4KPrXHUwMDA1NWPDxTjAqcTSSlxutu3F2Pye+XjBM3FYbzxcdTAwMDVcdTAwMTPmJFx1MDAxYrM/XHUwMDE3uE9WQ2Xo1SPcj64w6eQnkNjPb95hfVx1MDAwMPx2XHUwMDA2U+dSUYPpoPhzcPZrRS1cblwijc+Lo1x1MDAxMD58fNkwQoazi13Ilz6wXFxcYnaFSlx0XFyMa7WZX6+yQKG0vSxbrzhcciNcdTAwMTFrhk62IH/DXHUwMDA2hkTWosdb6WORLcHr0+Tf61x1MDAwMsxRK5HlOWK/neHv4yXKnEj7XHUwMDAy9eL4lUPHbMzxIVlnsIgv3WfsXHUwMDAwK8hf/46QxX9tmVx1MDAxOW7p5Fx1MDAxYcumusxcIlx1MDAxZomm0VROXHUwMDE5XCLcz8OUXG78x4JtM8mhn4pcdTAwMDGAXHUwMDBiw5lzUk/lm2QgueV8Z8/5Yn3Cel5cdTAwMWJcdTAwMTFcdTAwMTCqoFx1MDAxMEKY1NB5lUtcbrJfSzswwWlcZlx1MDAwZemTXpNeQlx1MDAwMudAeDib9LxCI5w2911cXFuVWmP1+5zf0KBLSmab+GCptTiygdhK8pU9jq1cdTAwMDBcdTAwMDDfcWRJNF1lqM18paxcdTAwMDdBVEPJtCxUoWz69nSHXHUwMDBlXHUwMDE4rarXK/ZU6H1cdTAwMWV0/IfGskhb9MuvLtJcXNto9GRpTkMlzcdTIY3DhPjVp4k9h+cgtFfGkX30UC/qIH9cdTAwMTlRx3L6t/KQa03Iil+7MvpCLPVcbu1+qVx1MDAxMcx/XGb8suHE9btd25smXHUwMDE5d5FH7pft6ml5o1uy3SWaXHUwMDAzOihxOCRQXHUwMDFivFx1MDAwZX9TM3N8XHUwMDA0KILgNpztsXhTyWaHKlx1MDAxYvBIXCK0MPF6bc8p4s7UaNZgyvduaTz8XHUwMDEyuFx0hTt/XHQ2gevDbf7GXHUwMDBibayQqidcdTAwMTP+0uTZMNxJXHUwMDFhvy9IXHUwMDFhZWZcdTAwMGVqa1n8UNw9zudoYZWLhVx1MDAwM2+5/6RGwXTcXHUwMDEzXHUwMDAwNFxyPmwhfLTpL9M5XHUwMDFmxpblX19K3I/bTdOKiJ43ieLIJVe5wZJtbFx1MDAwN9JcdTAwMTNL+brAqMukh28kucdcdTAwMTNcdTAwMWNcdTAwMGVCcPzdttPmXCJcdTAwMGbqw09ANp+xwouzO66BfZGhOyRG8/KyxVp1ntv/ulgh/W6x89Xv28n2XHUwMDE00Hb6N2O9XHUwMDFmUlx1MDAwM4WXV/6kSv90XHUwMDE06Gr5T8KFLM/QcJNGoCqWzybiWnXUM3uniTzU+5g+ii28P5BcdTAwMWKHL4RfIHpWXGJcdTAwMDB02yRz85TLJ9bCpzzipLuf8DPwXHUwMDA2g4HZeeXAMobaWrt3XHUwMDFiqE2WjN16dzZcdTAwMWVcZkQyNeRcdTAwMWOkuILehSbj2MvdX6LMpVhEXHUwMDFik27lelxuXHUwMDE2sFx1MDAwM9ZIg7XXXHUwMDFhUVx1MDAwMtgnIOmpo5xcdTAwMTHNY0aQ3bk+NlwiPj17je1YXHUwMDE00OhmSlx0skB/Ok83XHSNMtA4mZ957CnDNvRcdTAwMDbFnpknhFx1MDAwNciK7U2bvKaUPWpcdTAwMTlAS6RcdTAwMDWUXVx1MDAwMYPZU+RkXHUwMDE4SP9oJNeNr3J7PFx1MDAwZj6V65ZMd7XbPFx1MDAxMsVcdTAwMDBcblNcdTAwMDRcdTAwMTFcdTAwMTBcdFwiu4mte1mDXHUwMDA1c1x1MDAwMqNcdTAwMTdWglx1MDAxYkO7TmQvcVx1MDAxZb6HgiqlXHUwMDE4XHUwMDBmlK1BuCZnTWZGXHUwMDFmXHUwMDE1QP95PeFcdTAwMTJcdTAwMDT2ylx1MDAwNe17Y6hIXGaX4Y3Tonw69e09Ylx1MDAxZp7mQ7atp0cjhn77tELL3yezR+S3alx1MDAwNNkm5lx0dlr9NL21XHUwMDFhf7nYitGv7TNZLKP+ftFyyOiM7bPju1x1MDAwNiXSTLlcdTAwMTnZ8s+bqZX9qegpI8ZJiYmQbn6NfNVvwqCuJkyWXHUwMDA01XOJcNSmf+/f9jip/uEhXHUwMDFiUr6WLVx1MDAxNevnrF+nxreesT9mjVx1MDAxY1BIvpEq21x1MDAxOVx1MDAwYiSZVLpmvZ1cdTAwMTXdk65yaGd55PKY7SZUect+mMaEt0fyXHIqPVpcdTAwMTinbVx1MDAxMftn8Fx1MDAwM5o8pqWyeVx1MDAxMMxcdTAwMWJe5Fx1MDAxOFhpLeYs2nknkqxgxe5gTIhcImsyNdJW7PnWLTy6XGJcItls3Vxc5dB9ZM52MFx1MDAwNXF4dT9bycZ8tVx1MDAxZcNNNOWy5Fx1MDAwN9Zj14BBXHUwMDAzSOpeKOhcdTAwMTfSvlJ1npn1wlx1MDAxMNuEort8X8g78zco912nZFx1MDAxZWtjXCIh/72xXHUwMDAyN+/NlpKQRlWGno9cdTAwMTDPSlx1MDAxZH+Eg+y/XHUwMDExui/2cGg8LDiSfbVq8JuBXHUwMDBmXHUwMDFiNoBqPcWPXHUwMDFiUWz7WdCsWUdtcNColvpcYlx1MDAxZPlRKW3Rolx1MDAwZZvOJ026aWbwKSFq2yX/xrpoieWcYWt98Pq0xLO0RrmxXHUwMDEyknp91VHnKlx1MDAxYctgcyBDdPq08S02f4w+XGZ452tcdTAwMWKS8qNpuqb5L29cdTAwMDdvg1x1MDAwYoxcdTAwMTNI+I/ZXv6QX67KV/pcIvMo/Vx1MDAxNpHAwiGPQsBcIm/PXGZcdTAwMTI2fKtcdTAwMGZr1omPx1x1MDAxNz0vZUPWbNqH6pfUat4xbIw0xVvxR+1zXHUwMDE1Qdwq3uA91ulr/lx1MDAwYj67JCCNZj4sdlx1MDAxN9+41Ts9MzWParDVvVojdLju0WoqIYSriFZcdTAwMDMmucdcdTAwMDdcdTAwMTZdh6b4o7hXzO/WiTnC+Tc3ltBcdTAwMDd50uHnRz63QSeLpMa/z+trI5RcdTAwMDVcdTAwMWZcdTAwMWFIVnJpKmKgXFzS4LZcZntNLC/3KUo+9nDqnzJV4uvvNS/3L5ZMmH+97z3qv0tcdTAwMWHtaVx1MDAxMDsgPbcru0Kzcp+az02ImOnZxFx1MDAwN8svnD2Yje93fPZcdTAwMTHmNLub8CNcIkV31NuoUrFhTbfug1PMoE+vbJ1bJbHU1L/tIyeRXHUwMDEyN39P2v++PrP8gnTOmMKqQi6/U6MmpO66apDEObxqm1x1MDAxMjtMpJJ2aFx1MDAwZvGTQTpcdTAwMDTq7qW8Ls3gkdckITKKfett1GJcdTAwMTL/caB+cVx1MDAxN24uw7X6a0AwXHUwMDA35qlkROSAO9tbe2tC2+n3MCRcdTAwMWb5W06pY348XHJt9PbUXHJ3XHUwMDBiP+Ym55Ou0t36sGV3XHUwMDEwyXYypKfBV15cdTAwMWM/h1x1MDAwN2+h69mysjBcdTAwMTVq/ftcclx1MDAwNUneztp3yaJcdTAwMWMqWWdkn9dSeWd9o3tpebX2m6E07olcdTAwMWQnVslJUkhcdTAwMWR+XHUwMDExQZCwJE1cdTAwMWHYoZjBXHUwMDA0vJZcdTAwMWOFcMFKykXZc8jHz89k9tEt842jo/tcdTAwMTGqfjNcdTAwMGKR7imZKUmUj3+h8zf/UVxmrlx1MDAwM5cxfGyWLPy+zoyeXHUwMDEx2mgy5GVIN1x1MDAwMOC4xfjzRo1s/9BcdTAwMTiWfXhncr9mUsjKXHUwMDFiYJVWon1D2sBSe/OhR9X8z567hXZDKYLWRrlcdTAwMWZcdTAwMTl3/XvY+kRxw1x1MDAwYoeR4O9cdTAwMWGqhHxXfmchOjDXMZJcYqz+QK/q2y/Blm5ERVxuLjAxflHfXHUwMDBmgZa7oCqsmOBuXHRYTeI9YGSmnb/CnNFgNLchmmpcblx1MDAxNd+4Tl+mpTdMuZR61e+MNmRp6duqN8W5liGuxGafTnH3WVx1MDAxYTbqpHrA3r/8I3CK71x1MDAxYqC5JHJcdTAwMTZDXHUwMDAxjV8o992rfLDPO+kjXHUwMDEw5Tp0l/9cIruFbDh0fqd2Ysp9pI9ccimduI2cXHSg7MJcXENcdTAwMTHGXHUwMDBlaFx1MDAxM4KKu1x1MDAxZF6YqXKUXHUwMDE2w2U/iP9dmlT7dr9cdTAwMGbMsrpU9Fx1MDAwNlx1MDAxYartYYq+61xugPfCT2qIgKakXHUwMDFirGuAOSzSx3nFM6kvKE+/9CnE8k/d+J1BTFx1MDAxMj4rL1OjsVkhgsJLx4XHXlx1MDAwZtzG9XHeQoCK+tlcdTAwMTPfe41cXFxi4vz80lx1MDAxNKq3b9Thccr9wJvlbVx1MDAxZHCDiu+ol79mLKEq/bWP7Vx1MDAxZrdcdTAwMTCffLs11bmVYobXc1exJvtcckrgbI9cdTAwMTduoVprVntcdTAwMTB/U1pcdTAwMGI4YNY2au1QfzZ8YKBn53KTMEZYbPhfXHUwMDEyXHUwMDBiOzzquvEydE3UaLvWsDZcdTAwMWYmtUUw1lkn7ts2pKVp+Fx1MDAxYSpbO73c/lxy+Hi1SI9cdTAwMWRG21bn6NOmxEOJrof7+UBcclhccvHvguL+61x1MDAwNPiQvb35XGYoXHJ0XCKxi6tpoFx1MDAxNOf0XHUwMDFmcXfGV4Zz/1x1MDAwZfHwXHUwMDE4xEdXITHoPYi0KslCXHUwMDEw4MX0XHUwMDFjbulag8ov9bc3plx1MDAxNY6XbI+0WFLHO3V8f1V2LYZbPYV9SZXgszqCM1x1MDAwM8FqN9PXsfBqV9O3U3rvW45AXGKqj9XHZoiy9iR3MLdcdTAwMGat6mBS+vNzvJ0/n1d8p1dEJmDY38+79iyyKFx1MDAxYzGdPnZkuFx1MDAxMFx1MDAwNOjxXHUwMDE4StZL4cP2XHUwMDFhXHUwMDFmr15YXHUwMDFjRIWvz095XHUwMDEzwMflXv1cdTAwMTWnbFxypEUuXHUwMDFiIEA8rPJccmNcdTAwMWVX98V7XHUwMDAzOjxG74mF/XWIXX3k+EP6jJnASm8m2Zz742imh5ntiych0CrcbE6ASLf5kd3nsVx1MDAwYomnd/Q8QZNaXHUwMDFhI1pcdTAwMDctXHUwMDAxiqcodVD4q19aOfy6a2K3v+U8M2XPXHJvPVx1MDAwNn4zXHUwMDE2b6HtYOOJe8v6qkhcdTAwMDTHzE5B6zFSaZdcdTAwMDJEXHUwMDEwXHUwMDBimtt4bMHYyvZAr1xm0jWfyVx1MDAxNWvyO1x1MDAwN5s68JybYj5j7ZJ0U4QjyGPGgj7k8HhkoY/TePl4mYVcdTAwMDYlQOMxZGaCbVx1MDAwZiihXHUwMDBiNeJV6Fx1MDAwM1x1MDAxNTQvYvcgh3XeQFx1MDAwNX7+nlDYuvVcdTAwMWRcdTAwMDGNp5CqXHUwMDEzXUw/aN28Ye+pTvfQiyM+w1x1MDAwN2LYvpX6hZ22xfdhkEOq/M11P01cdTAwMDWJpHucpk/1c9pO8bDvifKEXHUwMDE19yRQ32ydeW7VTmTAKq1Ws2xcct2abLSnb6XHalrH0NlEZDcyb4hfiF555Xa7eSYznqqjXHUwMDA2S2LsbeF9ONCS+HXISIDXXHUwMDBij53IzYvc3IHaO6IriNA1YGqEUKMrmi77ZUzJy1n8k+8xul2gs43Ku0Yp+lx1MDAxM1Sqeqz05UBMetZcdTAwMTdow1x1MDAxYVs+6zaYb986LIZcdTAwMDFwe1JlWelRMmlYNp/2gULSz96Gz6hcdPCHXHJcdTAwMDSnXHUwMDBm4lx1MDAxMWuv3fVcdTAwMDX6hio/rG3oqVxmXqhzspruUkrWXHUwMDE2jXVcInVcdTAwMTU0VVxisNz6tDBHhyVcdTAwMDVmSs+490SDa1x1MDAxYqrS4DOtPqnHPIlGIYy+9XBmfFx1MDAxMXr2l1x1MDAxZlmJ5q+nP8NcdTAwMDL3Jlcj6Mct3Z5t+5JLQLdIY0LuXHUwMDBi/fnT0WGgyIqL0DSH0r3GvS40OvTiPSk3W1x1MDAxOLAhN1I5aLZcdTAwMGKoJDp4bXA8u8uvSUvibI5cdTAwMTVcdTAwMWEwMlmtlFdVL5mxtZJ0TCRcdTAwMWG2zoms9nDvuW+yT/VUotz+tPOVXHUwMDFlK0umM0FcdTAwMDRcdTAwMGLJXHUwMDA0pabhXHUwMDEwfotrbyCY6HDm7EtVr7ngqyVcdTAwMTnJPNGBZKNwXHUwMDFiRVTn4Fx1MDAwN6zNnUk8jNxhdaRY2IZLpZJcdTAwMTDuXHUwMDE4lJ3u3lx1MDAxOFlrlVx1MDAwNr7NjqZmjVx1MDAwMaBEapy811pMzohcdTAwMDfCXCKUi6uzWFxm0XwyXVVAXHUwMDAxhFx1MDAxYaut2iXsnu9cdTAwMTcw5U91vCtcXMiXRUrRlrqoSu8/kVQ77G+WIaL/rZdh8saLv8Kg1WZauTyqjiV2L/aHg0JA1Pg7i77L35vLMC428H3Xo0WkZ4RcdTAwMWOV1eS4Y4EkX2Xq2Mn2bJvZrGxcdTAwMWau+pS8wYq8pu1cdTAwMDSh43RcdTAwMGXl+eK7/lsruHF8lFx1MDAxY6/AYItcdTAwMDCfXG4uxYhpn1xcQl5x/1RcdTAwMTGmLuTJslBcdTAwMWWqki4lp3W664SQ96fpc1x1MDAxMoaeXHUwMDA2Llx1MDAxM5VcdTAwMDO/6XnEv76Ip1x1MDAxNJvvyk3rhrz0WFx1MDAxNFx1MDAxOFxc3CHI2DaM+5FcdTAwMDHJmVxmXHUwMDBlXHUwMDAwi9q2clx1MDAxOPdcdTAwMTLzipfonFBOXHUwMDAxpFx1MDAxNl/SXGLjaNFcdTAwMTOIWGVjnD3g7JNHXHUwMDA1/FSQXHUwMDFmlHmrevJcdTAwMDRcdTAwMGLd3NGtf1x1MDAxMOKDNWOZwZZmf6dcdTAwMTgjor86t+yfsaRcdTAwMWXnXHUwMDE1X9HhsPO4xmdcdTAwMTTBxSbBXHUwMDFmXobKXFyA5pU9iU9cdTAwMTlv+FxiJoRWtGCAbveY31x1MDAwNueG97hz9WJP3tZ+Ji3s3Ov3w1Eqp35cdTAwMDfwuWnYRnBcdTAwMDGhXHUwMDBmOFx1MDAxMqplxVg9kOybxVx1MDAxMW/DgVdcdTAwMTdcdTAwMTFd9Fx1MDAwNdJcbrZnLZRbXHUwMDA2pVx1MDAwZW5cdTAwMDdMgkaUtJGFz1x0lXlMdlxuXHUwMDA08S5V/bhh/SRcdTAwMTNvWNShyIZcdTAwMDTWlIJofG1yb1x1MDAwMENqn3pjykOxj/9Y+S9cblx1MDAwYim73YhcdTAwMDSh/SWNUFNu/sFcdTAwMGXYWZukn5pksLNcdTAwMTSFvuD+ZevNilxyyIP4s4tZW/3rmZHlXHUwMDA1elx1MDAwNpghs9XUvEZofnrNsEjRMqZH7k59XGZ+gXfzQyFcdTAwMGW4IIbU221KsNatvqv4ZddcdTAwMDLtXCI/MPPCIFx1MDAxOIqajcdPjrx/XHUwMDBmeMAozFjFb90l8Vx1MDAxYn9cdCj6eZN5eo/QfqRcXGBjgm74lFx1MDAxYdlBJ1x1MDAxZEazL1WM8uszOZDu0Mt851vIlVx1MDAxY3tlpOjjZtK83D63pFxyrGS5XHUwMDA0eGi6WTzfpaj1k0TEr9lcdTAwMWLV/DxkgKMpPW4mkoZdXHUwMDE16rNW9Vx1MDAxM1jB6ukzo4NWPjpI+y3pWOFcdTAwMWZjXHUwMDA3aN2GrLO7w9+MsnXnRKs5XHUwMDBlLn9aXzJcdTAwMDDWt52f5UZs44pkhVVYumbhlkU/rWy45TSvXHUwMDE4iZSakKj1ZYFQlniKO3+kXHUwMDFmnUvPRTgqz6wkZv+++6tu98B9KItcdTAwMTN2gm63kFxi/Fx1MDAxNawsXHUwMDBirlBYXHUwMDFjYj3KzbGDjNiPZlx1MDAxZF5gWWWxXHUwMDA3XHUwMDE2lcp9XHRcZpN16oeIXHUwMDFk0HJIscFbb1x1MDAxNPJcdTAwMGbAXHUwMDFl/TtG98OTnNjx/lx1MDAwYlxcipgkdCz1Kc7teer+e9513E3KW6/6+e/6nyW/0LS3XHUwMDEwo+VvOCdwdVx1MDAwZeH5XHUwMDEzdsXGpkdkTUhcdTAwMTAvh6KXV17lzlx1MDAxMjblXCJ9XHUwMDA1XHUwMDEyuqisXHUwMDE36Fx1MDAwMV3iotrH4q1cdTAwMTFI1aaHXHUwMDEyuXLQQGT44NtcdTAwMDRcdTAwMDJRXHUwMDFh+HFcdTAwMDFcXPnduFtru+jHgj4prG2iXHUwMDBmelx1MDAwZWeTP1x1MDAxMVx1MDAxMmrJuE22V+jngM5cdTAwMDGUnlHWe2JcIojHiFxiOVx1MDAxZLNcdTAwMTV2Rlx1MDAwN/rqX+fuPOsgXHUwMDE2PM5/XCJcYkvubVx1MDAxMfOpstNEwNKDM9BcdTAwMTEvXHUwMDFipHy+xqSKupPzuyjqj/llRGRrXHJcZlx1MDAwNUs0tdLZafdcdTAwMWUpXHUwMDE2prtcdTAwMTAsr2xcdTAwMTV+sXqZRVPMhr99ikzkXHUwMDBmIa5jVueZt595qVn7OOH1o/qQpKa/xndhyi8pXHUwMDBmNeEplnMggl+lb4WW0aNcdTAwMGIjrzLRdmPRm0P10ctV9aosXHUwMDBmicC+4cmryrHNkn54Uve/P65m6k9cdTAwMWbXeYuub1x1MDAxZFTS3Fx1MDAxOEK8wYpTNo9YoetcdTAwMWNcdTAwMTFnwVxcOujvYFx1MDAxMVx1MDAwZaZS4az89uaqRmJh64z4V3thQ5z1xJhnXHUwMDBlhXzB3auVXHJcdTAwMGLa9EpcdTAwMGbfwi+qLjRcdTAwMDA91NpcXPxr+zP/1bxsXHUwMDAzaeQjdrbPaK5zXG6/XGbwuHtzn1JLN1x1MDAwZlK8XHUwMDFisLSv/rjTy5yXXojb2aVv7eN5Z5VLhbJV8btcdTAwMTdcdTAwMGY1dK4gsqk3d6dYXHUwMDE1PY3ryVxma+X76SxbSevY5U3N2ULGQlxi6tInkNLLXHTJKFx1MDAxMGDYOVxc5Y4wXHUwMDBiWY3nXHUwMDA0xU3AYoHMmj6qXFwuYGlcdTAwMWa7XHUwMDE0j57SSXVqSTIos+ZvtumNXHJ/OGpYiWpcdTAwMGW1xp07t8aLwDP5+ENY6K+2neD5p2H8n4ZxycSJP4zwXHUwMDFkXHUwMDFhI4m9iaPnR2a56LzwyGj79tRcZlx1MDAxMlY0qz9YsVxuXHRfMKVA81RWe+Ha38rrpc7vY1xmVYw4gfCKQvKW3N/Dz7tuJHNt7O+qXHUwMDAzoOjxXHUwMDFiyfa3l1x1MDAwMTTB00PrXHUwMDEwYMBguFx1MDAxY01ccsI+g7lz3zrPW6hfXHUwMDFiXYaZmvnbZ9BRup403aJd2EltTK80rbgusP67hqo6YVL4XHUwMDFlNHeI5Fs/XHUwMDBlmGqZXHUwMDBiq37XgrSYcHr97Vx1MDAwZY3aYlx1MDAwM2nNXCKyc7xeyjZcdTAwMTSu7WhAfMOkV8H5yl80XHR3tlx1MDAxNOzAuXbNz/123i2nkvNhXHUwMDE4/1x1MDAxYzxcZtBq11x1MDAxMl1iXHUwMDFkzVx1MDAxZaLKbzVRoMM93Y1cdTAwMDSe+lx1MDAwNW7/u1XO55jTllWH91x1MDAwM8GnkOFrdNm68/lcdTAwMTl9iFBemDlcdTAwMTJcdTAwMDRcdTAwMDEwlqBFmb5cdTAwMGZk5JvoXHUwMDFl96MwpkJcdTAwMDV2Vfwo0C1cdTAwMTOpe8I0JY5iUa/2+0uv1oxcdTAwMDQ08nHmT52t+4GxXHUwMDEwu2N4Rt08XHUwMDEzv1n28Ttcclx1MDAxMOFcdTAwMDJR3ko7XHUwMDEwyH2Ab8DwkbJ9XHUwMDBiQeGiL1qaksVcImVdjH7Av71y/t2DWrs3V/933VX4cJrD7srxO29iJVx1MDAxOdNZQr1cdHK7lXqkQEtEsTVnpH2eV6K/9qzsL8805zOzb1YtfoKhcopcdTAwMDL4XHUwMDEyZIBcdTAwMTTJXHUwMDE56X7ANGQwp2pcdTAwMDDnSP5cdTAwMTaGc6iAUohiLJBcdTAwMGKzL5tgXHUwMDFhJKbwNYRcdTAwMDN3XHUwMDFh+IZ/c1dXcyNJXHUwMDAwoFxuO+irwY+z4aG4bWRMRm+t3ZpcdTAwMWJcdTAwMWX//JZcdTAwMTZcdTAwMThb37io4Ww1a77Um1x0lVx1MDAxNWxcYlnNXGZcdTAwMWNvXHUwMDEzslx1MDAxOFjpwUZGXHUwMDE4oVotOab8iES2XHUwMDAzXHUwMDE3XHUwMDE0qN9AXGJcdTAwMTTvM1x1MDAxZLKMlpdqq2BV1LVhwThcdTAwMGKbzdt/24Smrf3UrMyowVx1MDAwNkD23jNsVVtcdTAwMWRcdTAwMWGKtbdHjM/vX2Us7zVcdH126GJcdTAwMDbZ0bRj3n88XHUwMDFivEKfoOdhd6y+M/xi4muHfpxcbk2n7OHyQd7wXHUwMDFiN3ori39zUitPx1RqVH1/soKdfs82O4hcdTAwMDazl08od1x1MDAxM9ZVeYdcdTAwMTmYPz3subKnklqrXHUwMDEyyjhdSFx1MDAwZe9cdHG0YNWToH61YMez1St5ILE5zk9O4Fxu5+FcdTAwMTbdXHUwMDFinM3v7lx1MDAxNriOrty42d2z++s4WHpcdTAwMTnksuPF3LcsXHUwMDE2bFx1MDAxN5L4La1OeCd9+yAo+Fx1MDAxMlNdLIqiuulx4pdIi4uysXDBN1fBXHUwMDAwqCNtZVx1MDAxYZbGyvFvpjbOWqb3pVbTS1x1MDAxMH8yW+ZAySzMs6DgaZj0dVx1MDAxM27nrKCGXHUwMDAypeVlhzWJbCNPrdF5/tzL4lEgyXXmXHUwMDA2UVDix/NNsrQwv4LB9Fx1MDAwMUdMINnQykv7uslKJogqXHUwMDEwbOAvo2Hmz1x1MDAxZcdgemtcdTAwMDVcdTAwMDVIsDtqrYN+s1x1MDAxOOpcdTAwMGaQdCPOXVx1MDAwNMvt28vVpfu8etJ6wPNn2an3uTRUpFx1MDAwNedvXHUwMDA2zyZ7+mWZWMmCtqxVZ1qcXHUwMDFi/YFas4lpYkksiinq7W6TWlxmc775XHUwMDE3tlx1MDAxYcclSFx1MDAxZFx0VtH7idRcdTAwMTIp9bjVU6Rwqlx1MDAwNP1s3szgJvK6b2RffNfv9uVcdTAwMDby2T6B3p7CZPjNJXpTTVxmTVx1MDAxYezCN2S0XHUwMDFhwbDbdp2k+PB3v9/j99lez7GRXHUwMDFl3ILZ27bNQDvovWmtKLaFo8U79u8tJqdQsJJYm1x1MDAwN79cdFx1MDAxM/xBI9iNXHUwMDE0g2M5yORcdTAwMTaxU0wh2H1ccq3NwrtcdTAwMDdcIn2dha+v4st4U6VcdTAwMTCLpZSeY89BwzRTIejPkVx1MDAxON2KX6FwtOPfbExqTC+/4s76nl2a7lxuV2eBidRcbkDaiVBcdTAwMGZpXHUwMDA3ciXZxl/GuYHkRKRaLH4w04Da01x1MDAxOYD3giPoXHUwMDA26udcIllfTq2XxuZ6TT1rQ4FDmVwiXHUwMDE4j6tcbu7ye3A451x1MDAxOYhi+VxcQaV9vnsvXVx1MDAwM8N/b32/cymSrqq1hzWDOO/Jv+Zdh55L17zGdMy7dFlBv+xoXHUwMDFipc3xp4BcdTAwMTWbXGJG39FmeMFcdTAwMThcdTAwMWW9OjdHt8VBSVx1MDAxMVx1MDAwN1xuo1x1MDAxMt7PVMPyR1x1MDAwMJ+vQzkzXHUwMDA1rFx1MDAwM1xiskG6IS+5o1pVXHUwMDE5+Ovh7MNBOWxcdTAwMDDlXFy8wGtcdTAwMDPAkldS8lAkq+7eblO4KbbyLcdcXJNs3++jXHUwMDE5wtuWWtrvsff33IWQ5v/5XHUwMDE0/eltyFx1MDAxOLiaTW/Pl26679jewIuw4Feo6b9ZwJR61lx1MDAxNPNFoMX+XS6DUX5K2OWanr2ZXHUwMDExXHUwMDAxXHUwMDFi0E/Jtlx1MDAxNWGwXHUwMDAyaaqZcmmOeuVcdTAwMWWt51x1MDAxNny2ObfAflx1MDAxYudriGBAp5FcdTAwMTb4XGZZaVdcdFB+2Zrt5vZ/b6d2VctcdTAwMWPDX8eXXHUwMDE28VhGzfKqN57fdFxyT6tcdTAwMDLhc1x1MDAwNek6OtVcdTAwMWZcdTAwMWR8lk/6Kiic5VJcdTAwMTFSpTF2jPZNYVnZPulEXHUwMDAyNf/31K1egF7NNJTA5+lcdTAwMTCGgEL57MzErFx1MDAwMV0r80sz8tTIrerjifJ0XHUwMDBlvuBcdTAwMTIxYbzva4Lit1/cNURWzzqv8nFcdTAwMGZcdTAwMDCzXHUwMDFkISWRcvtHrJ+85MrfXHUwMDE3i/U3XG7hXHUwMDAy3kWrzK4q2Vx1MDAxY0/HYCFvMOJAcX033kdcdMp9bX6BXHUwMDFm0sFcboMkZFx1MDAxOVwiKsXuXHUwMDE0xHb6fP79XTeb28L/Z/pSWYc3NplVvW/N0lx1MDAxYapPZa9HL2lcdTAwMDb7xlx0XHSaXHUwMDA04v3NXHUwMDFkRt6Ev3whXHQtecfll3ZSOWhcdTAwMWJP0seiXHUwMDFkc2y1lFx1MDAxNlx1MDAxYyU9Z7o6fzkt9oSspFx1MDAwMDKkXGLENyy94Vx1MDAxNOJcdTAwMTPt7MW2vsbX7dJtRMPKgySEhLM+XuRcdTAwMDJy3LxRVYGFmSguzNOP9nPRTK9cbr9cdTAwMWNcclxyKfq3v0ln25ShP5+juYb/p03TWbPmuNmQ5p3HuKOokXPlI/ZcdTAwMDGyhaDf85Jw6IVcdTAwMWQlv++Dysj6edt/Tv1FdN61fD3iXHUwMDAxVqI3XGaNUJhcdTAwMTCcLpuEXHUwMDFhxlx1MDAxY85s7299xPc30Kpip+BcdTAwMDJcdTAwMDTbi9xOuD8zlK6otI6GyEJcdTAwMTD/ZmG7Uc+o6C7NOGR/xVTrXHUwMDFjXFzEfYNMpFx1MDAxYTQ9lsR7yL/UXHUwMDBikVxy0HtcdTAwMGXAuYCn62K/elx1MDAxM0BuP1x1MDAwN50xhnRS8ewmY+t7TsDqLG8vf28+6yMsgvmTU7WbQDujv+a191x1MDAwZfjXP1x1MDAwZSWmf3Mthlx1MDAxNiwhbSFRS7G42cpP9HrH66DUZ4S2Zfz9cJvBY/VJQIz9PLFcdTAwMTGKXHUwMDFlgJ825ObxXHUwMDE2rf2rXHUwMDExM2JcdTAwMGbNJCqmfZfnwJqdbmPdILuzbIS/xbFulGCOIfJB1y60OI858T6PrGg+wlx1MDAxMMlcdTAwMDFsXb2rXHUwMDE1f9d0kuTer1x1MDAwNH2zfcvXrNhykLBkhmpvglx1MDAxNq7mJ96GXHUwMDE4+CX9NdhcdTAwMWWlf3pk9IDsulFcbmNcdTAwMTGKR2Iyfa39doRrs96g/2bOgDlcdTAwMDHngVZcdTAwMWNsXHTTm1rLe+vrPPOzyM9aWVwiXHUwMDE5tE2VWKKfMSChcdVoxt6v8qV+n5RcdTAwMDFcdTAwMWJ2rLN8XbRcZp9cdTAwMDTu4NhcdPkmXHUwMDBle1x1MDAxMjHx4lxyXHUwMDA1TL5H8usmrGRyNdRFZ/Hy9MvzXCK8JZBPvlx1MDAxOS2X/+aTxpbn6Vx1MDAwNKJYsbl9U2tRSqSi0Mh/I1x1MDAxM1ZcdTAwMWbNNFJW+5tjJ+zCtNVcdTAwMGZ8Y45cdTAwMTf3Zqc9m1x1MDAwM6lfJUlRxJl3LO7cl1x1MDAwNplZRCl4Mn/o/lx1MDAwNkZcdTAwMGa9S5J7fZiZzCyU2V1YtZE6s61cdTAwMWZcdTAwMTdcdTAwMDQuXG66m5xox0tYXHUwMDEzMFx1MDAxZYotYkErXHUwMDE4cmHFjYfJXHUwMDAybCjsyuUljlwiXHUwMDA0jL99atDmXHUwMDE5ON7f1ESE88tg/Vx1MDAxMdg++1X2RbBcdTAwMGa8vnBg/+JWo2hXVqTtfKXybCB7XHUwMDEy/c2x9plcdTAwMWTSOdx97VxmISav6q7Hvu/oVDCSyu4vTjXUXHUwMDEyaN6CU0CINTxiIOVcdTAwMTZcdTAwMTK+XHUwMDBm3aXhWcZqtcmszVx1MDAwYkHyXHUwMDE1JHVcdTAwMWOdTqX5d8nYuomqaP1cIlnmXHUwMDAyqbjq6/+mT9on9fuVXHUwMDFmRc8sw5yjpCxcdTAwMDExu9zNXHUwMDEz3TpPmyNcdTAwMWXeXHUwMDA0OXZcdTAwMGLCR+9cdTAwMTFcZvHytzdqnMO+QzNHgO7yZX/W4OmW+orM62WiP6RroO/AQ95cdTAwMDVcdTAwMDWl16e87CNsrn/3UyzY9lx1MDAwNOxC83Pbyod1gkJPXHUwMDEz+1xytFx1MDAxMUzsgyZhY7SVlMLIUDiTdDfbL2LHulxumdXCNmg2XHUwMDAw6d/9NFx1MDAwNkPgklx1MDAxM1x1MDAxYupUXHUwMDE3LVQtPoCiuUVb2lOAXHUwMDFmnFx1MDAxZF6PgZ1kjtNT55pTP5qayPieXHUwMDE1ayxotIilne3lwb9cdTAwMWPLaG+rXHUwMDBixn0vdVxmff7uXHUwMDFmL7/9KtSkRkLtU0Vssn2qdsW5puVJTVfqRHHJvvLghZA5OkJcdTAwMWGEXHUwMDE5QnD2caT4xKtZ41pcdTAwMWFKkFx1MDAxZPCZucpcdTAwMGZE4VxczkGmvcdcdTAwMDJtOpyR2WmOTMPf3H7++Tx/byGfzsSHsvl60N8lpD870HlLWujjXHIoOl+W8jdCk0k81y/8k/PFX8+6XHUwMDFhr1P/XHUwMDFlXFxcdTAwMWSgT1x1MDAwZlRcdTAwMTbPZ7CptrmbplifVpz5/Fx1MDAxYVx1MDAxOHdcdTAwMTYo3bfEXHUwMDBmRFE+YUNyzYH6u8eK/U5z+Vm1N7u72+dzTvLPXCKr21x0gC+EMFx1MDAxYpTFr4pcdTAwMTFVo9I1dP5GSM3kXHUwMDBmlXs0MsNXhlx1MDAxNcNjxYMgQCuTeFx0+UiL3HukpVxiquBCXHUwMDEwifRK3U1cdTAwMGYtxOyohJNcYkTBr1sxRqR+btiGNo9wJ7Ah2Wl9wtW3eYffXHUwMDA1XHUwMDFlXHUwMDFm/jb0mitrs01lODn4u94tol5cdTAwMDQvdmMldD/6wqtEYo802VtcZmIssl+P+Fx1MDAwNeHSX1x1MDAxOKbvVc+EeDEqdzJcdTAwMDavp2vs6/+TXHUwMDFjXHUwMDEyxaEv+Ixl16RcdTAwMDHeaIDxnVn2XHUwMDAyUn83cqTqUzqB/Z22llx1MDAxN77Ak+2O3jFcYsHKK4CBJFx1MDAwMfufaTdHb2/SXHUwMDE3eqHZo0mrRjn2qolI6lu29jzXxlnU3jif/eut5HzznVrV38ZXUnlVKcvzUZNcdTAwMDV+TudcdTAwMWWn8bi/RjGLfK5cdTAwMTZKzKqbXHUwMDA0XHUwMDFk5otcdTAwMTfLlkQ+e2yBJFx1MDAwMVxcPlx1MDAwNLpuXXlkZFx1MDAxN1VcdOKcjqEtQ6PLXHUwMDE5r+/3p1ovdmb3JktamsBiJ1x1MDAwMVxclTlyi3Kd4OGiYdvqnD/ni5dDo1x1MDAwM0HOUFHKKouZXHUwMDE1kXIrTlx1MDAxZH3Hl1fxpvjgXHUwMDEzb5hipOBkhG143tnBMz+GfodcdTAwMDL5i4Rx/IHJNnxcIo6BPnw13lx1MDAwMsqJby2YWj3DbnBt/KZx9o/vUVZ8uaGs+uiYjVMjQVxuKVxm/2LrvFtcdTAwMGajjpeLzFlaSyG5XHUwMDAxSFx1MDAwMNhcdTAwMWSf8+pElZozlqjjP2ydt7qkPFx1MDAxN6UviFx1MDAwMO9CvCm8qVx1MDAwMjK8956rXHUwMDFmTn//JPNM0MlcdKqrkLTWu6S9XHUwMDA1yo3sS/7qQoHi+3FcdTAwMTBbJdbRZYCndPZcdTAwMDa1kjJ2UVx1MDAwNCpoqNh9P2kwUPrUaiG2oFx1MDAxZHdx2Ei/ZDrotFx1MDAwZilPpNFjRue9TOJcdTAwMTlbknv6WExuXHUwMDE3QG2cwEeDp0TUUF7+6M87fI0xXHUwMDFhf89cdTAwMDQrT7d9LbjLhb+9m27hzF3JmFNcdTAwMWaVv++smFx1MDAxY9LvWcRcdTAwMDSKzELh76/Hi5+3QNNf+Fx1MDAwNlx1MDAxMfNWqpNudWdcdTAwMDdeoFHt6U5cdTAwMDC/pD4snFdcdTAwMTWUULm0VGRsscBQLKOJlv7d5Ws9NePnM9h7JN3Ft3dKLiwuKngnjmTJXHUwMDA0m+xEhIFcdTAwMTab37O02Kt0e1x1MDAwMXzj+ud3pHnIvoOU/7nZ73tC2l3933mIf6dP8ELkylxu/+Yh+7dNqOn7y+W/OPHjv0NcdTAwMTUnoq3f/neh6KF/XCLRvCq/bNfl+zjwslx1MDAwMZdcZv9GWHCgbPj12vaRX1x1MDAxY/NV4lx1MDAxM8HxX3goT9/P9tL1N39cdTAwMTFcdTAwMTXjxXFt+oAvpkWqJ9Yys7egeqdXklx1MDAxN9gu1v1Non1cdTAwMDXQJ1FcXFx1MDAxMVxcXHUwMDBi8Oi35seBak9FcSOMdVlcdTAwMDFcdTAwMTTO2IRZyjqmmY/hynrvgHSXJLKTUKhcdTAwMDCjbZVeW/teVntcdTAwMWZmXHUwMDFmyagmwHmnVi4lktVM5fg7/jozXHUwMDAzOIjqfNxcdTAwMDBcdTAwMGWWby3s72+M+KQuglx1MDAwNrqKXHUwMDFkJlx1MDAxMIumXHUwMDBmWKmN/M4oJP63XHUwMDBmvUmIXFy+gesuYXkjwOq1XHUwMDFjXHRcdTAwMTCnk9NcdTAwMTH2XHUwMDE4+jZNPFx1MDAxY9WDMOfO1i5Vrv/GXHUwMDE1XHUwMDE1QtKiXHUwMDAz1kv8a26TjzpcIs5cdTAwMDBasLrFYEXokpVO1kHff32fWcrG+WNMXHKPXHUwMDE28Fj0cohKwlkg5OqNp1x1MDAwN4dcdTAwMTN7gpGZLK4huFPCbujZ/rw4P1x1MDAwMIRcdTAwMTfYc+WQsrZcdTAwMWQ2Z0tccqpcdTAwMTSmXHUwMDFhyyu+cO+yTjeJXHUwMDFk7p1cdTAwMGJFVEPV/Ld2RKj8XHUwMDE22PpcdTAwMDEj0G3feDX2KFx1MDAxOTYtM6y+RMXjpMtcdTAwMDQ6vItpKVx1MDAxZLGZXGJRpfBIn0f97tPyMKKImpZtMOxPLFwiUpuFZHtDr4FGZY9hXHUwMDA1ieI0vTpcdTAwMWNfjThcdTAwMTeLTVx1MDAwZi/5SXely2pcdTAwMWVcdTAwMGLDXHUwMDFiJDBNKJ7/nVx1MDAxZr1g+VwiXHUwMDE5xPVVNChVV4ez5syw8Vx1MDAwNHyFmuvrofbqey9oypAheT4rmCpcdTAwMDJIV1x1MDAxYak/qj1cbmRehIlpXHUwMDFmq8U/XHUwMDFlN3u0+ffq3LCuor0s31x1MDAxMTeiTf7EzGi2n/jmXHUwMDE1/Td14qVlJ406giOFqVx1MDAxNECkeFx1MDAwM/aHpvCSqEdcdNqT9M2geufigWpwyLPB7UhcXG84eTVcdMegf/V1KlxmdGmviUGmQJ92XHUwMDFlTbyOXGZcdTAwMGJaXHUwMDFhXHUwMDE1O7lYqISvXCKe5Vk/x1dcYlx1MDAwNuX+Oz+mXGaUWlx1MDAxZfagK29cdTAwMDLjj1jtw1xyveuW6Wh6uz5cdTAwMDVtnp1cdTAwMGIl7GBl+lx1MDAxZsNBif5784j1YSUlmUoqb0pksXZWdNOamYY+nEbQZbL7psnmXHJaXG4+fU/62FEjmX/jrOSoXHUwMDA2yNqMzlx1MDAxONg/KnKmwiGF+yGjodWEc1Re9LlS+bVw6IjqSfvhTlRRfFpklel3XHUwMDA0XHUwMDBiolhcdTAwMTOSrMRcdTAwMTnnO41cdTAwMTPNT9tkXHUwMDAzvqi5yFx1MDAwMJlGjq9u9cxcdTAwMGI/Xpdwwa6y3Cn921NccpUt+ao2iDPqXHUwMDA3lllNetbHNEHtncx0fLxcdTAwMTFcdTAwMWNcdTAwMWNcdTAwMDRVobjjV/nFdHREall+kC+ItUGU+f3AOVx1MDAwN8MkLVi8+tdcdTAwMGZ8gTLX60n/SN/OrsZhgTWCJiDKty5cYl9cdTAwMGVcYvGo1NJwXGZcdTAwMGWzVfbeVPiXK1x1MDAxYbt6UsxcdTAwMTaeMTSRS33XgYCdzd+dyuT/9itWTClfaDvve3Rl4439tu439azLRmmwYLbDwK5BS1HppWP8XHUwMDE2j6eaX9I6THFBJOt5Ldw9eYxcdTAwMGXX91v/SsJZXHUwMDFktLXrV4cl4vuhZVx1MDAwMfmoXFwqV4tTot/nXHUwMDBiyMnucXdq3IYh8o41IOBvLlxyINAoI/zJL1M/ylPaUFx1MDAwNo9gvJ2pRTzFXHUwMDE5XWhcYith7lx1MDAwMETR81x1MDAxNt1UXHUwMDAwoPCAlZtcdTAwMGWBelZcdTAwMTSvKGEvulx1MDAxNFG/OCdJ9X02gF3Fr6H2mbiixpOnXHUwMDE2mkqHt6JcdTAwMGXiXGIsf1xio1x1MDAwMs1ccsJt4E9oNHGnpYzMrfZcdTAwMDEqL/75o+tnXHUwMDAyy2E4K1x1MDAxZW7YvU88bFW25E0wYFx1MDAwM5AqulxubOhcdTAwMDJWOr1cdTAwMWU/RUXd4PCXp1xm+F6PqPdccjAoQFx1MDAxNGNcdTAwMTiOYd7cxX6FXHRmffC7XGZcckAzPZEh9yeHXHUwMDFlfthHwVW+XHUwMDFl4MtcdTAwMTKuXHUwMDAz4Hkwl3AxP7FcdTAwMWZuJtFfnv5GXHUwMDBlfHaCxkzM+IaEslx1MDAwNrU8wtVcdTAwMTHtcMVTbqairDzzXHUwMDEyXHUwMDAzoIa1f9L8NZnXLXzhXCJcci2Iu1x1MDAxNG2+pvvGId7DPEVcdTAwMTRVXHUwMDExdEeU8lx1MDAxNlx1MDAxMoFoIFx1MDAwYjJnRMl2UFx1MDAwN1xmqS5Y9zeBXHUwMDBlXHUwMDExe1grhX3yMcp//Umo+6HLIFwianEwXHUwMDA1tCY732xcdTAwMTZeuDS6h9dcYjytlcqqZY8mg5/c/vVVJlg+rn99laTdMjZkaatiN+NcInxcdTAwMTmXsEPs866+xLlcdTAwMTRSrMhcIne3ulx1MDAxMk+z/4QsP/mx6Gbxk1x1MDAwMeWudbWhMr2eikHb3Fq9rI3Celx1MDAxOWBPT6JdkSgvkdjeW1lcdTAwMWSJuCBcdTAwMTQwcXvorD7vbG9cdTAwMWM9XHUwMDAwyH75YG7elIadkyqFOYB98ajDWPM01r8ymlx1MDAwYlx1MDAwN561rMBcdTAwMWZcdLn81f712iesLG6N1PTFMGG5aWmJYj874uBXmlxiXHUwMDEzX1x1MDAwM5ote1wivqGyr8mfXHUwMDFjeVx1MDAxYeXrr1x1MDAwZnj2nNTgkrh7pVx1MDAxND1cdTAwMGWh/IZccotcdTAwMWa1vMV5/2nUZZ94/lx0XHUwMDAzjFx1MDAwMOB/ns9Shay+YU2tvEit+1x0+fJccvfLIU792/bkJ1BcbnRAot7B1IhcdTAwMWTAz1jwcabBR0yOQGOo29aUvfInr8/nl/vQMVx1MDAxOVx1MDAxNPzFcr55szJiUEBm6jls13MyXFyMuzmvfvCrXHUwMDFkoDfptc7tii9b/Xn3NDNcdTAwMWTwT1x1MDAwM2z2b+/JUFxcXHUwMDE4R1tCXHUwMDE243lgmVhcdTAwMWOvqkiOcoD14VfMTdu3wERV87++2upcdTAwMDcsR8dcdTAwMDaRvVx1MDAwNlx1MDAxMfltO4nca4X4lGN1+lx1MDAxMpZcdTAwMTbQ5zkumFx1MDAxOYDMUlwiMkOIm6T6h3RUOCefmXxpfH01XHUwMDA0M4OuplxcIVdMXHUwMDA3XHUwMDEye7GUknYlU8lcIuC8SbuGPVx1MDAxOOF6XHUwMDA1Rod+PIiAj9W5s1/Cjlx1MDAxZofF/lf3z3bQakWhULqQzO1qfNqc8Vxu059fnGwqZMy001x1MDAxNcY+sc++XCKQ6T/wZ3BXXHUwMDAyfH4t+DJ9XHUwMDFht/VcdTAwMDDtwVx1MDAxMjZfXHRNm4zGqb9cdTAwMWVcdTAwMTbda++/Wz7zY/v+XCKE2Uq7d7h7XGYy+KTpyMgkNFx1MDAxMVx1MDAwNInV4Ko191x1MDAxZaDcJku/Rlx1MDAxZubqMNzyXHUwMDE5RY2jftpuqJFFoNnimWqRXHJ00CNS9vF7ou6+c6hcdTAwMTdcdTAwMDBcdTAwMDbVJSz8elx1MDAxOIHSX2pcdTAwMTVm/HVLmzSI3XKrXHUwMDAwhC6WXCJcdTAwMDGh0yNeSYPgQGzU8Sfj91x1MDAxMezx70x33J6Q9Xz6g3O8Slx1MDAxZlx1MDAxZT3xXHUwMDAxXHUwMDAwXHUwMDE0Wyu1XHUwMDFmp3mTXHUwMDA2XG5fI0Jcblx0MZC+9Fx0tC7M0ewkXHUwMDEx1Tdel5JcdNhdsTw/0WxgvjLrhk67x7cwuMBcdTAwMWLX+Szmzz333MxcdTAwMWHC7fbvoMFRuTbiYlD0rSM/LJSu0lx1MDAwNTIyQYuMa/zKPlx1MDAwNlx1MDAwMFx1MDAxZslbyNvS4Uw7o/KXnfuootVktVx1MDAxMOn1N9BcbiTaNJ+kkHaLLn+RXHUwMDEzZpWJXHUwMDAys9LmU8zBnFx1MDAwZYPYQ0SYy3fmV583VfMubKlcdTAwMTE5XHUwMDA3yoc1tpBcdTAwMWO1izuZv1x1MDAxYYxcdTAwMWb6XHUwMDBldpzhyFx1MDAwMHQ/98yHbFx1MDAxZr9HIVx1MDAwN1/2XHUwMDAze89cdTAwMWJyTK7+SU2EcEtcdTAwMDPJo8w74Fx1MDAxYma9X+Sm0Z3itf1E/ki84b5cdTAwMDTfNY1oze7cV2Kq6VfLbVx1MDAxMS96XCJY4F/w6tEhUIHqw9E7f5CI1DnGR+RcdTAwMTkm82Grn/A6JH5HtuZ6hjZcdTAwMDWEMdlllSRBXHUwMDE0TnpcdTAwMTnuPH/7XHUwMDEwIda+8Pd+6alnfHc7XHUwMDE0XHUwMDE3d46ZbijOd2T73y1v2lx1MDAxYpKUWH5cdTAwMWSrVIj44u2AfSmJ0vmbJJKKoHtvXHUwMDBinTBxcY9cdTAwMGW9XnXzTV54ufo7wzmvr+zZ1bJYs5+3f3X2XHUwMDA2q3hE5Fx1MDAxNXxcdTAwMDSYMmbCXHUwMDFmvFx1MDAxOH2incRcdTAwMTBIVz0xkJ53+Ie61DHzp9y3OVTty/mvL9KKw1x1MDAxNU/fVYmkrNh7zp5p9ew70N9rtpTfXG5EdI2/mn7uP0hPOFx1MDAwNN75546NK19cdTAwMGVcdTAwMTGYOv6pLkRcdTAwMGJcdTAwMDJGf4h4XHUwMDEwXGL5i+FZw79E/Lf/sPnAQCDvOE5cZlx1MDAxMLS4olxmjE1dk0BcdTAwMGaBtlFvWrghsP5cdTAwMWFd33IuLFqGXnVRjvL4nVxyxlC4hX5cdTAwMTmcXHUwMDFk6pnokCZAtZxcdTAwMWNcdTAwMDA/sJicLFxyMvR8+adcdTAwMDMm50u0XHUwMDA1OqWmXHUwMDEzXHUwMDFjKPGDkCZg35yypp18plx1MDAxMFx1MDAwNVEh7NDkhoBujFx1MDAwNVx1MDAxZUnWe0ZcdTAwMTh9krxq910yZFx1MDAxMkaT3NQ//Ujc3y6+XFzO/50zs7uATnOrvFx1MDAxMURqKeOYRrxzZGngnN62SfX2T1wipKW/Wsaa/1x1MDAwNbbqMoQ5LnCYXHUwMDA2XHUwMDFhXHUwMDFkXG72/Vx1MDAwMmsneb1Dq1x1MDAwNLnf61lcdTAwMDPvd7D+elx1MDAxY10l3kmZXHUwMDA0wVWTPV1cdTAwMWVcYlmxk9a8n+z8xMmrj3jYN28yrl4j2f/uWHHvdHP6o4C6X/JcItayjbXOaVk8zVx1MDAxMlx1MDAxNEGb0Ex3XHUwMDFli1x1MDAwNChcdTAwMWUkjSNcdTAwMDBdJIOPh3FWwdJh+6ZQfSfJ7Fx1MDAwNFx1MDAxYmKiXCL541uJLf7Vw0xcdTAwMTjNMlJmvF6tSpBr8Se9yuSXa+/CXHUwMDEwVOvbsCrfQX6mXHUwMDEyXHUwMDE02NV0aoOfblx1MDAwNoWxkbcqOPuK8dnHXHUwMDFlRsdqsFxc0lx1MDAwNriGQiupSK5cdTAwMWYmW5XXX2Uvvp28Mm5roPW8kEfkOIv5VIevXHUwMDAyXHUwMDAxfvbSgZThoq1ErNCCOouhKVxmbcZlRr2ptcOTLJ6tn1qHU1x1MDAwNex1b4ydO7qmXHUwMDA3XHUwMDFj2XpcdTAwMWUyXHUwMDFkzPGJ1cj8XHUwMDFku30+vpRMkMlcbiNcdTAwMDfSXHUwMDA36rFcdTAwMDdvqHKhwHRcdTAwMDdO5U9C8JFcdTAwMDWQ1FBHqTbmvtFIXbvD8951aFx1MDAxNVx1MDAxOOyh3mOKN0FUP0Z2VG3up65HXGJjKTDoxlx1MDAxM7KcpXXstEbLXHUwMDA23pBT0br4XHUwMDExaWxgV+R2nXZwbdX5WlmquMZOJkpz/3W5PK/cXHSbdjmOZSr2LLbpWW/k5dFdN9WVQ9u+tSB/Y2qLnT0x4uz1YvWPzfFcdTAwMWbm1mzp/9M8yC763anaLtYyZ4+7XHUwMDFizEqkeGO4Qlx1MDAxOYxN95eetnRwITJyfIJRR1+lbDWuXHUwMDBm6LlRzmJcdTAwMDZcdTAwMDem7tmF0qjiQIrFhFx1MDAwZrxF6qy1QkGq6SMxJpr9fD9cdTAwMWRcdTAwMWTgmzNoXHUwMDA33T6MXHUwMDBmXHUwMDAx7atpbJ9ZIPVrXVx1MDAxY1x1MDAwMzn6Zf/D5LM+XHJE6kp2Vt1MNH/i3X5kxPtJ7UFRf3dcblx1MDAxNDwsVm+8PWLxr95cdTAwMWLPOv5fza3RLyyTXHUwMDE1k5ztaMhN4Vx1MDAxOV5cdTAwMTGmK1x1MDAwMqq6gNRjf304jeQuv1x1MDAwM6an9rs8a+SjbfXy0+/NyMmpxCo7XHUwMDA1moRBqVx1MDAwNX1cZjo8baqYayvmXFxcdTAwMTmCgq3aUbKAXG687n7RNbXXM49cdTAwMWSpfv/OTVxcmFxixpamTHJccuxjglx1MDAxY/HeJkO2Jl7/SLW20lx1MDAxNVx1MDAwNZRftbtcdTAwMWLNvox4SpXDTKlcZoBvpPH2T/yiiltgeH4wMklTt+YlXHUwMDAxnGSBoSHKUNjXvz1cdTAwMTFItGXDIMJd6yfB8b8lXFyo4k+DayU8Rjd7w1x1MDAxOetswfCdNs9cdTAwMTkmI3YmjYBiZ0XyciH46+DHWZfofFZcdTAwMGVcYkvdr4ndfDYk81x1MDAxM+1s+lx1MDAwZU7xjdNpXHUwMDAwe+n354kynlNLaYi3TMHxOVx1MDAwMYE1XHUwMDE5/VpccsdcdTAwMWGDX1JH3jWaM1x1MDAxYvUqXCJcdTAwMTOISIEg9eszhrGpMfb6Z5n+21x1MDAxYn5cXHbzK97cXHUwMDFm+J1cdTAwMDWCiNNaO1bpjNPyRSf0i4o/cLk1o990ai19XHUwMDE014P+zvDGljNcdTAwMDTQWipcdTAwMDMhNk9w/DxcdTAwMTP9XG68aCAveD2B/P6Vn+yT1HWX74Gdl+hcZl+XlVx1MDAwNWPt0ZT79+pcdTAwMDSz57/HJ1lgKS0/5ic3pCbFeO5x/29ccpjYSMGudK/yjf/OrOQ95kotOphp7jKkhUyNRVUoNIkplkLhcPhcdTAwMTVR//p+xpV4XHUwMDFkX3CSZVx1MDAxMUrtdkJ6+pYj379cdTAwMTm9rVTFd8Ct34L/Xlx1MDAxZXnZZSb7giN+N8BcZjGLXHUwMDE4XHUwMDFlxd/3VUx/+d+edtpFXHUwMDE5/CGwbG8pYHC2+puzXHUwMDFhaEdMcFx1MDAwYsLQUbhJL5R+XGbPOd39oqgtg1x1MDAxOVx1MDAxZlx1MDAwYs6qOGWZX1x1MDAxNc7n2axfXHUwMDFkhTaQXHUwMDA2QIVcdTAwMTfrwIhcdTAwMWNHz59NJ2/oXHUwMDFkXHUwMDBm/XJcdTAwMDG0RG7llUtcbmi+QzyyilNcdTAwMTRcdTAwMTbxzZ9ss3/k1pPMjFx1MDAxN5bg79PW02OnRFx1MDAxZifc/mqs9GdDyXGoJ72tTLdNXHUwMDE47CxGmPS/yVx1MDAwNlx1MDAxMEuddoRn/t1HpZLFXHUwMDAxlp81adK1bamcfr1GfDVljjtt3r7LVrevTiFyXHUwMDA1qt369Fx1MDAxOSxHn1x1MDAxNzdcdTAwMWN+ikd4lVx1MDAxZuONOCqBXHUwMDE1IVwi1s/9y1xi96+WXHUwMDE1XHUwMDE5XHUwMDE36EuVIZP+2+dcdTAwMWRZabWdxVx1MDAwNc1nnFx1MDAwNVbPuFuiTUI5QIpcdTAwMTja6FNf+I3O6pcwJFTDXHUwMDE0XHUwMDBlz76lorQ7el35T6N7w42tcVUpf2DEXHUwMDA3q/BcdTAwMDON6MJcdTAwMDLA5MuRe8stT1BcdTAwMWJcdTAwMWKUcv3a67s2VM+MJI18XHUwMDAzXHUwMDE5uEtgs1/kXeLoVFx1MDAwMtEpv2tcdOHdz3yM896FXHUwMDE5j4k7XjaJ5Fx1MDAwM5Mospor8m61q1x1MDAxN68k/3r6ISv0/1e7fcn2JlRkLlUnaNaHZEjg50Vie1N26O9cdTAwMWRcdTAwMDVcdTAwMDY7Ulx1MDAwNopahHFReChtKd82XHUwMDBlr1x1MDAwNZyvXHUwMDEw7GtccrFcdTAwMDOlqMHPWHJg9EdcXNE5mfr6I/tcdTAwMTLNltA61V3fML/nU1xum1x1MDAxOLC+4p5cdTAwMTCrwnExNm9WXHUwMDAyOlxik/rbdLzuoplcdTAwMTGU/CUzdD8s21x1MDAxMOaWsJaFK/31ZeS4rFx1MDAwNeBSycBCXHUwMDAw+/NcdTAwMGJcZv/2ZFtcdIlsXHUwMDFiKpBJhVx1MDAxZUqNqyz4NoBSr998R0/ntVxmulx1MDAwNlx1MDAwZpS8+Vx1MDAxM/D09tJzXHUwMDA3461cdTAwMTT1YOfN9c73U/nd74fl8oTrgIKbzewuNlx1MDAxMGjTxVdXXHUwMDA0v/FqkMPLpnnXOK+xgHXLLF+5NnT7RkZPUjhMiSXDhkaKc+pcdTAwMTjbg65cdTAwMDRiXHJcdTAwMDekpVdZlD3lpMjHvlx1MDAxMObgXHUwMDEzrj9Cdit341x1MDAxYi2ZVfisVEMwuJ8tyql9s+X7O/55XirtdjW4I1x1MDAwMSxzXVx1MDAxZGDgW8FWQva73D9cdTAwMWRcdTAwMDWNkbf0d7RaXHUwMDA0mVx1MDAxNo4h1u9Kz1600Fx1MDAwN1x1MDAxNyR4S3lJ6CaaXHUwMDBlyywq+bsnKtpX5lx0ZfjBdU1cbsLMKXtcdTAwMWKbzdjyJqwxfFx1MDAxZXFaVrNdY7ZcdTAwMThcdTAwMThrhHf6LiwhcXb20lx1MDAxZdmgXFyfXHUwMDA1cvrXm6//wk+Lm62GXFwzpjSs/VNbXHUwMDE22fUpcy2lrEblhJq9bN+M7f9vn83+t8+23u/8nf0hXHUwMDBmmvL5tKD+cSz1tMVj47XXM5XoyVBcdTAwMWW96Yj/xlu+jd9EdZ+29FjZ/sTLU6fYQZCAxVx1MDAxZERcdTAwMTZd7Oo4U/eFdSXMke/31UVAQqSBXG5cdTAwMWOZoGL4XHUwMDE3STbMXHUwMDA0XHUwMDE5YaU+XHUwMDEzoDVcblx1MDAxZkrvmejvTVx1MDAxZaVLXHUwMDE3mG6h7sJcdTAwMWKOOUlCVlxyS3PVV25cYqxlJ861qi8hklx1MDAwNbx0bZJJt48tmzRcdTAwMTZcdTAwMDVVtJdcXNWeXHUwMDAyRWOVLOFJXHSbSnC4vNRcXPlLv1x1MDAwMv7mP5WbX1x1MDAxMoYxotPGVrXli1x1MDAxMpqDwnjlXHUwMDEwuWBZXHUwMDBlmUGrNyTxIe5RRyeRv2okspLas0LhXHUwMDE06qxcdTAwMTVnpcLQfvn9mpk2+v5sOW+ZPEc/U1FRVF1Jvsxv3k2QflxcjVxccZ9Js3s8ofiRsH3is+WQpkrqL8Yxycb9fVx1MDAwZbvCXHUwMDE1n31sUCDiXHUwMDAzXHUwMDAyXG6g1LOezlxmXHUwMDExh9O9P0WSbyD1ulx1MDAxOHCEJ6raJpAxP5xAuuOAeubSXHUwMDAwKPCFbL9JePBmpVx1MDAxZFxmomNRpN9XRyr9Zd9cbo6u3NZoXHUwMDA0zJEtPVx1MDAwMlx1MDAxYniKtEKxgODZlVx1MDAxNFx1MDAxZHN4/L9CX09cdTAwMTM05Vx1MDAwYvhcdTAwMTey6v/tXZzfLM5YRkE46r+zz2tjbWMtmFx1MDAxNFXx8/WVmbXgK1Q4e/p8r284f9Vcbl2VXHUwMDAySjo5IddhOLvqrypcdTAwMWTDrkWgTdVcdTAwMDJaNSDf0J5cdTAwMDNMmqlcdTAwMTVFk1xy8OH3XG4rbaG6JO+3QEfHXV3ZNuxcItfBXHUwMDFi2kfRn2W+XHUwMDAy5L9cdTAwMWV5iONcdTAwMTJcdTAwMDZcdTAwMWVVPNxcdTAwMTHrbua20l2Jty5cdTAwMGaUXHI422WepmVcdTAwMTfb7dJcIjQ79F4tZSH/XHUwMDE29EniXHUwMDEyhSgtNKA/2aTW5eVOnOnGKVx1MDAxMVx1MDAxYT8oLbr5XHUwMDAxtUApzmxFZej92/Fcclx0XHUwMDE33rzoLEdcdTAwMDaJX2WsXFxlUlx1MDAxMKt5Q31o9oRcdTAwMTJcZirv7lx1MDAxYlx1MDAxMqltXGZcdTAwMTRKcCkzjMDSRVxyq/Iq2PMz7aeA1DejbcOHXHUwMDE2tTfPXHUwMDA0/3pLZlx1MDAwNl9cdTAwMDQhhqpnI+KAvX+G/FxuXHUwMDBiJ7c8x0Dj/I17KcCI3Fx1MDAwZWost3PZr+OVhUdkVjpNXHUwMDA0cVUkufuvd1x1MDAwM/6gID5ean5LZZ2wwd/dk8rfvbTojpxk+0ygs8hO77NcdTAwMThcdMne+Vx1MDAxOShcdTAwMTRcdTAwMTdcdTAwMGZ5XUUoT3/0WaqNfTr/sYxaVUL2yj3Crv9cdTAwMWJn8W+cXHUwMDAzxkdNtvVcdTAwMTGUrFx1MDAxMPavbmP1PFx1MDAxNVx1MDAwM/9cdTAwMGV7XHUwMDA24VfzZm/lwuHtXHUwMDE23LhvXFxpXHUwMDAzXHUwMDBiXHUwMDBl0bFRhs8qMFx1MDAxMfzObVx1MDAwYlx1MDAwNy1N1IFcdTAwMTiyllx1MDAwYjQgyLHH0US0XHUwMDA0VLnpl9u008aK8spz4/uzmOlmPkTrmXecZTC+oqbZr1xmvlx0T7DIYUe6RX99fcS+XHUwMDE16jhcdTAwMThcdTAwMDZcdTAwMTY8YVx1MDAwNDhcdTAwMTDrmjBcdTAwMTm2mlx1MDAxZl/2m9DVzY+wJvo1sEHhO63Q2yf/7sE9XGK0/djv42criJy/lOeVbWCXdpPp3lx1MDAxOIFlqmC5qVA1SKDwVLUy9Gb96//ZS1pbU1x1MDAwNnxE2aj//16SyCHtdPVk+trRXG4qNbNZfFx1MDAxMK1IyDslOtZSpFwiXHUwMDEz4W3zj3FCXHLDU0CSaW9z3kdqXHRsXHUwMDE4393H17I3l/zgydGDN/eqv9dp/jIxu60h9ZzDKEC5NYo76eytQiiTYLNcdTAwMTTehLXiavr9JZy6yPFcdTAwMTGOXHUwMDE4hdD0PoKemDllg+iI8fu5JSHvOlx1MDAxMFx1MDAxZGrusy92841cXMXQf0dcdTAwMGVcdTAwMTFGNOhSYv0wXHJcdTAwMDP4XHUwMDFhkadlnq81lXiF+En6Olx1MDAwNdiqpEL12kYtjFx1MDAxMqM5/9VcdTAwMDHS/2qUX0p2fUyyhlx1MDAxNVx1MDAxZsmXXHUwMDEzqGfcXHUwMDBmpySP6ZayuPtkIOVA0ruEv6r7KcxkfrNSbLfK9Ml3Xlx1MDAxOVx1MDAwYihEta1xXHUwMDA3ty6xv+Zdf/+V6rJcdTAwMDG3XFxgXHUwMDEypzFcdTAwMWZmrIhccko+N8tcdTAwMTSV/XJq6Vx1MDAwMlx1MDAwNUZZ8+RU+yGLdVx1MDAwNKhDgPBcdTAwMDLyaVx1MDAxNf7JMlx1MDAxZU9cdTAwMDOmQykyKrB8JaBOOlx1MDAwNTZrV+lcdTAwMDeRWWpSJIpcdTAwMGLCJFvU0lwiLXWunFR7mLB4prfakPnYV1xuXHUwMDE5LFxuRrdcdTAwMTdBhyWPyFduLMpcdTAwMWPriKq0mjTn6XVMn1x1MDAwM4vv7T7zL+iu1CQ3xFx1MDAxMF7cJ/J0Xut0XHUwMDE4SJhOXHUwMDExXHLvX+RcdTAwMDdcdTAwMDBRflx1MDAxNzdcXCTYTa/Q31x1MDAxZKuXq3RcdTAwMDBTXG7/nUUwsVx1MDAxMdqo2zKVN6/yScPUXHUwMDAwxZ1oXHUwMDA1UMlFufNcdTAwMTF/vaWFhcn0XHUwMDFkNLZcdTAwMTmzrpX/tGqPM3Zzklx1MDAwNrG8+Jo4JiVcdTAwMDND9rOSozJHwFx1MDAxN7M3ZrSycrXx+fdCXHKo789nga9A5lx1MDAwMdpaXHSgs2y8QmL88aireD1cdTAwMDWq/W5cdTAwMWRw11x1MDAxOZYve1wif29cdTAwMTHddfVlz1xi06qLg0Rmi/75XHUwMDE1Nf2dKdr0m5lNjftcdTAwMDZLkIjQnVtQnD1cdTAwMWN9XHUwMDAzdERcdTAwMDM2NWo6zXLWIT3aI96gaq9cdTAwMDLYrpxZQ+Tt4/pDXHUwMDAw0lx1MDAwYlx1MDAxY0bVgppKXHUwMDFjXHI+nV9ujrpB4tVcdTAwMWG5cD0vfoHbXHUwMDE4rs6szch6bulFf8XKKX8pZ1x1MDAxZa3vwoc/XHUwMDAz6n+NV7N9RnFF/9J3qFFkweFcdTAwMTDq8NDTzbbelOW1tVxiXHUwMDFhl7yi0Xpccvrr3dPfJ0OKwyr8eYRDdZquMT5cdTAwMTJjMFx1MDAxY8NpXHUwMDFl+XdcdTAwMDH38Oejzuo4h399wyo1ubPSjPNNtJEpXHUwMDE47CFPKUSjXHUwMDFikeevvX2pa1x1MDAxNbLk0lx1MDAxM4kwU9drXGJyX7ZcdTAwMDaIxi026lbyVV7jXHUwMDAwsi2W6e9OjOLEnO85XG6JXHUwMDEy/6uxs3vKgCZ2QP+uYPFcdTAwMTRiI2ar1lhbTjtcdTAwMWRxL/VWheels/7zNXdm3ak3XY5CI2psse/BQir0+GVcdTAwMTeYnMtOaWeXMKYrR0hcdTAwMWUyoWk3m1x1MDAxNceWJO1eLY/K1b4gOS2s2Fx1MDAwZehcdTAwMWTM55n+2n7SZu5cdTAwMWVvQK9UL1x1MDAxZKzXOa9cdTAwMTOULsxpclObLbz0+/vO0NPU+rv2Lfc1ManCv9X7R1x1MDAxM1JjfVBTQbRDlieKTfl8b1wiZy7JRVZHcIvdgyZcdTAwMTP6jXdM8YPk4Vx1MDAwN1u3sNJFbIfXsibdU1x1MDAxZkTfr0Lw6p3bMiOFrK1cdTAwMDd3xv624YFUWyuQppBHsIFp47fv8pPN5+Dovdd6XHUwMDBijPfetsA9ifN/PVx1MDAxMtx3RTO0WNElZMczgV2hWFx1MDAwMWvpLyX622O8PbZcdTAwMTNcdTAwMWPKXHUwMDBmXHUwMDA3R1Vbp96Tfkl3KyxcXFLX+XLSU1x1MDAwNFx1MDAxMMfrk2RcdTAwMTXNNt9cdFx1MDAxNqxeUlx1MDAxYb5vpl1fqlTY5Mtfn3dcdTAwMWV+JVxi3fBcdTAwMWPROlinQoOgkPlCLZyt5kNOXHUwMDBl5FpcdTAwMWXRulx1MDAxOcRcdTAwMTknYNtD4u+MsdHTLvl96IO/LmKlQMlqU6n6+MTIRp9OXHUwMDE1L/9cZsCCefol20ucL1x1MDAxZu5X/Fx1MDAwYrEuXGbylVx1MDAxOHCls1Yj86JcYswl56tcdTAwMTN69f3rzfqr51xmXHUwMDA3nWipzDbNsV1u2823mFKd8kz4KmfD8L7tzpOg7l1wzVQ5wE/TY/1cdTAwMTNcdTAwMWKu49Gan+otXHUwMDA1yi/ToOdcdTAwMTOJ/NxcdTAwMDOubXPC3z1cdTAwMTgvjzS46VxutavL4XH0WzEzXHUwMDEy9WFlckRcdTAwMTJj/LgomZ5WTOH6XHUwMDExXG5gpz9Ib6yurcSiVfCGWLy8I/9v//b667v8vVx1MDAxYcQx+lx1MDAxY1RhLo8ji2ZHg6otSDtTMfB1WVx1MDAwNc95K31oXHUwMDBlev6CZ7/J+k+ZwClN9J7JIWb7Llx1MDAwNq03NVx1MDAwNphReLvBY0rilZ2wPJVE1tXx2MupUFgnrbvE8kai6TfCf/elRXDj/DY8SY56Xlx1MDAwNJxcdTAwMTFIos3Ao1x1MDAwNr5cdTAwMDZcdTAwMTTTaUhcdTAwMDNg7nZB6tvSZpQtX2GgTHNcdTAwMDBcdTAwMTBNkihq8HqrXHUwMDEx5MFJXHTpq6dcdTAwMTP5XHUwMDFirKfXWTTHT8agQYn9m6fNXCLWZOP/JlXU4lFccnL3lUrpXCK9XHUwMDA0mCr3/d3GiPYy+/K666jJc5FA5WQ/XHUwMDE0eVxylep6N4xn8FWMvFx1MDAxN//1IFxyXHUwMDFlno71x7CI0vGIOI4pn5VcdTAwMWRClVp+XHUwMDA2ZVx1MDAxZjM/IE7zR0uHSmG1K+nMxcDcXfvoP61cdTAwMDPkbYJcYuWxsUZCJZ1cXFWmmVx1MDAxZsFJUPGsWuZ/tVx1MDAxZvPyVWx4sdBcZs70gGxD6aHz5JvDeCXTXbxcdTAwMDXlsWz7Ld9bq1wiredFnN9cdTAwMTL2+Fxy+1e8Y6u5yFxuXHUwMDAyK5q+eD7wXHUwMDAwMj1cdTAwMWNcdTAwMDD4JW/CdmQnlS/+xe7k9Vx1MDAwNlx1MDAxN7BvbNeqXHUwMDFhmFx1MDAxZOen0URcdTAwMWON9e3chVlRXGIw0CR8ivk3+m0gaix3XHUwMDBiXHUwMDFkk6rYLTdz3U9cdTAwMTFF/ceNs8Jvm3Q1XHUwMDFhXHUwMDA05pAsW/syNlIg6fWIiPJaXFwhVn8+n5pmWc6uuXbDj+RcdTAwMTmG4rdcdTAwMTe7wPFcdTAwMWSqzshDPnjPXHUwMDE0w3B8jqKIclx1MDAwMKZcdTAwMTfQ3izroFxiXHUwMDAwwIXtSFx1MDAxMaprXciAgfhQhDpk/8HNXHUwMDE5Qqzr86jMiFx1MDAwNl+GuDB7Xp72eFxu8l10gdsmlJpcblgrdYMyWaRAY0a9Mlx1MDAwMU1PXHUwMDBiP/TTxOA3o5uQ6z0l6ac2pij9aUzTTI9bbSCOwX74tX7O3elOUdFcIubxXHUwMDE0vLV75Vx1MDAwNYdl9dObpOxRqoVhyqaWcS6cZiZcdTAwMWNcdTAwMDBsXGKCm4KPXHUwMDE0wuxcdTAwMTTz/IrUMMrfN7GY0JskqcKi6d1cdTAwMDJNiZ7TLEucb8mRT4hcdTAwMGZcdTAwMDH4klx1MDAwNUQk5TTPv1x1MDAxNc+JfC5cdTAwMWJV82nWofa8ekApMNeClD7SiYMtoVx1MDAwZVx1MDAxNpkwXHUwMDE4WGlXllfmPN9Sum5cdTAwMDP9c31Gtlx1MDAxZVx1MDAwYlx1MDAxNt9nwtjU56vZkJFKXHUwMDAz0b5TbVRKk8QtbowwzK9cdTAwMTRHMDLLxShcdTAwMWO2V3BfPDRagiBcdTAwMTgyMmmIa9teXHUwMDAzKpQzUkdcdTAwMTn/vHm7u/pjU1XUM4hcdTAwMTc2b/r0VrnzqFx1MDAxZlx1MDAxMT2kbVpW88Lva1x1MDAwMrzhXHUwMDA0UPH3dlx1MDAxMe9cdTAwMDFdv1x1MDAxZDmT3tKKvFPn1Vx1MDAxNq5cdTAwMWT5XVxcnVHIXHUwMDE4X1x1MDAxMHDVpdRFI1x1MDAwMWG+MWImYDYrdmntilwi5fJ9P/BcdTAwMWU2UnRdaYtAcfSVfcZrq1x1MDAxM7MwMqGkhlx1MDAwN/FNfclcdTAwMTFo0M12cFx1MDAwMpT71DzSN1x1MDAxNTiovLi6UUpcdTAwMWKJrNDFr26qg1x1MDAxYlxmmKpcdTAwMDPgi0vXJX4l36HSrX7U3vhcdTAwMDPYXGYwQ8vz4VxcNMecXFwrxuf3uzRLU19BWD9cdTAwMWZqau2yLCuFWaYmVYZWXHUwMDE0nMBBUoVcdTAwMTlcdTAwMTZ3Z7TdN1x1MDAwZWDVXHUwMDBiXHUwMDAxs7bCqFx1MDAwNcSasE7hRzJm9YxMy05cdTAwMGIk6FxiXCLSyFx1MDAxZVwibeTfl7RLuI85qVW8VSpcdTAwMTHF8bpcdTAwMWZkz4E8VEjRnGiWob/ZXHUwMDExKdLxnqdcZrJcdTAwMDc9JT5fRlxc2YXnt3CAQHL1TzMwL6GXVo+tcf9YRrlcdTAwMTBcbkTyYm1wXHUwMDA0XHUwMDBl1GFq7l8lMvN+TCRWn9VcZtVcdTAwMTD8c6pqIVx1MDAxN35m8FOi++/iOFx1MDAxM1x1MDAxMF1GbUpqwUq+JsQso/SunyBcdTAwMDfPvab/wFx0T6PK+3RzqSM1pkLFtpvRc4928qSP7ciz/c0x0qgomT9cdMnFrCPIxy/wUDT1wVx1MDAxM2SaL+OKXHUwMDFjhIdJXHUwMDEy3HHN844r1aUwXHUwMDFjhmE1XHUwMDAwgEBccmKeXX3qs2v/vClcdTAwMDeiXHUwMDA1j9otZ1xi5Fx1MDAwNkkkTPlrida3RCZcdTAwMDOdXHSGZSlILtS+8pe4Q0FlhFxm5JuZsz/xVWKia8Q7WFx1MDAxYWSTUVZaiD+Dj5Fz367s2a1aYLt3XHKv31x1MDAwM1x1MDAwMaQmK9KzXHUwMDA3XHUwMDAwJHJ+V7zdelxc5CZb5Vx1MDAxMslcdTAwMDKUwFx1MDAwYlhU0I3Nelx1MDAxObVjWVx1MDAxMCle7F1cdTAwMTBEcvpcdTAwMWVovy5JqV+fWyTnobxcdTAwMDJLQ2ZcdTAwMWMlvHZd/4vZv8KRr2iaXHUwMDE0hGTVXHUwMDEzL1x1MDAwNTKPN9KlKJijR1x1MDAwM1h6euNC8IONXHUwMDFm/ymamLzakTFPjFx1MDAwYjqcUVx1MDAxY3Z9dY17XHUwMDBl2k/woa10SCZ30Vx1MDAxOLw1OFx1MDAwZfBNrlx1MDAwMyC4v4848SX0aFx1MDAwN+rnmGdcdTAwMGYrNvdaXHUwMDA3XHUwMDA2ivwsbH7HXHUwMDFlh/lNS6PH+4iyXHUwMDBivtFg29aDMJRvZlx1MDAxMOFRJGxcZlBbuVx1MDAwZf5WUJFnq3HE456MTTI4YyjpJFx1MDAxNoJcdTAwMTO+eoHjXHUwMDFjXHUwMDE1ue1PXHUwMDFmbqc32p13jFx1MDAwN1qfr1x1MDAxNbT5/P6e/WGhnXb7nFx1MDAxNc6M+mHRXG6n+u82Mtn3XHUwMDEznVx1MDAwZlCG0qWtZ95wl0KqXHUwMDFmXHUwMDFlRFxmj+lcdTAwMDF/XHUwMDA3pHy09ibaI7Ht0Oh1XHUwMDA23LCl1T/GZKZ/Wu59s+pcZlx1MDAwZmuTepmwV8x0ylx1MDAxOVx1MDAxY548Spqj9YuS/nKI0DJCbleLwVljNlxmb4boIItOgVezN2O1XCJUudX8RKSV+IlnNoFYK2CkUjd6gEqecqZGXHUwMDE4qr+SPGd5wXdcdTAwMDTLlMaBa8ddrlRzX6hcdTAwMDLoW1x1MDAxY/xcdTAwMDEk8Tij5Fxc8rf+leM3n1x1MDAwMqhcXEVZXs+J8PKikOZzXHUwMDAy2Fx1MDAwMu/lMN+7gsoxXHJJ4oxF4YnXXHUwMDAwaYD1gHy4XHUwMDFipeNmXHUwMDE4YNOYkJS7XCKBhZPkXHSQXHUwMDE49sqwVPhicr1h9MAokUxaXHUwMDE5x1uIRMJTXHI1XaSuXGZoXHUwMDE43bJecD7snys4gKlBlFx1MDAwYulSg7C6YEjISVx1MDAxOHtu9i2ktifAKpnRY1x1MDAxMs8/UMr6+sf06uG5dV8zXHUwMDEzXHUwMDE2aaNcdTAwMGZcdTAwMDTWXHUwMDEwkp6jRlx1MDAwM9xcdTAwMDbToJRSNO3T68fdNo64lc/UVzn2cOnrwDV+U2d0XHUwMDE5ookwq8ZK3t/99Fx1MDAxMuwpOt1cdTAwMTPPzSPm8W1VaVx1MDAwZfWlLUlcdTAwMTlcdTAwMDJcdTAwMTBcdTAwMWF980JcdTAwMDFcdTAwMWMl6fHwLezTnGXAuFx1MDAxMua0JoPGpFx1MDAxNcvgq4H8XcSYXHUwMDA2w5NcbodcdTAwMDVcIqeP+lx1MDAxZZJ951x1MDAxNTBzXHUwMDE2XHUwMDA3heDGKO6EXGaHO1x1MDAwNYtIXHUwMDEykNamTJbB2sRM1/+bZzyVYVx1MDAxZKNBTlmyln28y3GiRTZ9IGepXHUwMDAwXHUwMDAwuFx1MDAxM1x1MDAwMNhzL7s/XHUwMDFhcqZxRFG4XHUwMDA0ZGn+5pymWuVcdTAwMDUqvjB4Z3RcdH2+XHUwMDBm7uKZctwoKEBcdTAwMThcdTAwMDU2Wlx1MDAxYVx1MDAxY9uNLFeoaunHKopcdTAwMGZeReVFUXZWyeFgP0HM8u6W31x1MDAxNFxiPt6+W8JcdTAwMDee21x1MDAxZtVzPizowDO7/LRzUUfTbLyM/NLFXGa3fDk0K4iDnVNvQP5cbtlcdTAwMWOEXG5dKzn0kiGF8lDT2meOfydK/JqUyVfzr1x1MDAxYlx1MDAwMzh6XGKN4CNHPfP++4ir9obgYuvqzuBo5MRinUDT++PprJNcdTAwMDOEeOFE1JBLPM31XHUwMDE3KpyOmabVxme7JkFCXHUwMDE4KKIoXHUwMDE3LVx1MDAxNdlcdTAwMWUuMdm+U4FcdTAwMGJeWcDmfcUuhlPyXHUwMDEzzochUFxcPMJJTZVtiDkjulx1MDAwYlx1MDAxOSDUKeppy19bbm05XuwvbVxmXHQ7ebVCP2hcXDzCbIotwzBcbsOqjiD67JWWhlx1MDAwN/G6J12xXHUwMDA0w+MsIWK3I51kRW1cdTAwMGZcXJneoj9tm1XvzKGJeZ3akCqqckdSOHHqedKsKGTQ0CyzXHUwMDEy4sS1wFZlKCxhJurjtDpcdTAwMTPhJ5hmjlx1MDAxOYCsbJKefn5E7iRPyUtN+1pcdTAwMWZcdTAwMTY4TtNcdTAwMWVcdTAwMWFCUWJgRsbwa92kqF5cdTAwMDBVRcCv2LL+g49cckJcdTAwMWbrtlx1MDAxY2CZ0Gku91x1MDAxNmufL8yhlEgrf3vTo2FMoMeQqbZOXHUwMDA0u6hcdTAwMGZcdTAwMDWLocE8JMlTkVx1MDAwMuWm/cVZxVG8lmwjOYXwb7aj49DQv+F5YFejQNN6+bR0UqxQ6jrJLuOhmI3tM/PBiqB84tomXHK0gV7W6bxLtjtcdTAwMTHJh0lS1IvEVZjIQNAxn5CMrUdcZtxvo3+4Ln8wWaRXkVx1MDAwMZaCKmvxNFlcdTAwMTU79ndC76FBT3uxTtcngjvMMOghit/R3o5cdTAwMWVzQFx1MDAxNLWJXHUwMDA1TqPFtnZ+4iOpcVrtXHJKmzr0W8tQYi9cdTAwMTfo37fFXHUwMDE1q0ybnqTZ9X1cdTAwMTbQjVx1MDAxY1xuJ4zyqDxcdTAwMWPD5U1urJSGwbrLzZCc0TQgXHUwMDE36SfO6Vx1MDAwN2q6evA71UuLXHUwMDFjgFDBXHUwMDBlM+PEy1x1MDAwYsMld1x1MDAxOMlwf/NujczzfPDkPYHX4/w1qqRFXHUwMDAxsXRgQ4NRsdOHwNn4OTYxXHUwMDA1QIBd0tTa6Vh+Y/iX7f0qk1xyplx1MDAxYpfq2uJUatPQX8OkbqBBl9mtxXO/0lx1MDAxZcCD61wiXHUwMDEyRud6XHUwMDAx9FtRs4SBS0RcIlbqiHGSXHUwMDA1SY429Ve15Wwwi+FcdTAwMTBGMX9zezvKKcJxsCtFndrz0SVcdTAwMTMvu+S58utcdTAwMGbr9UpprFx1MDAwM21NoDtv5rGIkJdDJ/NS86JcYsfnTYauXGIh9/dxTZcgXHUwMDAw9ptl2b1cdTAwMDeW//NcYvNeWN7pbC0l3GiWMlx1MDAxOYSjXHUwMDBl4Jx3vVx1MDAwN5vj19Nsc1x1MDAxNlxc2t+HXHUwMDAyikdLjddbnkFR9Fx1MDAwNWeWbt91cvNOXHUwMDE3alxy7kR+1nmKXHUwMDEwilGdTFwilDaWbb3Hx/1cdTAwMTRxWK+cXHUwMDAxqFx1MDAxNkBGc0KudvelXHSlm5Ker33q7H/eMmPNO3aln+MqMy29gIHJiSpp6WD1XGa/4SrTMVx1MDAxNe1LXHUwMDA3p19+M+RcdTAwMDb7PVMyyPpcdTAwMDQmxv5pqbhcdTAwMWNz8+b110Xcx3fDXHUwMDFiSTFcdTAwMDdd7UTAXHUwMDE2iObT5fzCOFx1MDAxY5amhK+/XHUwMDA2jsXTgC3snUfpYLzKXHUwMDFlXHUwMDFjMFxiWCkon1xyko1cdTAwMTFIU1x1MDAwNzHXRHfnXHUwMDE1vlx1MDAwZYabXHUwMDFhXHUwMDA3bFx1MDAwMNxcdTAwMWafXHUwMDBmQZ5l3K/hjfM4L1x1MDAwYqAoPjZ7WVh/3a6GJkWlXHUwMDFjpFx1MDAxMlxmx3ZxprHyyDOl+f6/+EahT7Lv+Vx1MDAxMXBcdTAwMDFcYq54sqyRXHUwMDA2gjDaI9TIXHUwMDE5MuK6l1RNQulDQV3pZWvt6HpcdTAwMDCfn3mB7Vx1MDAxMbqO5ZtcdTAwMTD3q2fZgEGPI8ZcbpuUudHuY5bSlGbfQC1cdTAwMWY4TFx1MDAwZthGvXBcdTAwMDO/Zq20o6BHXFzxQYXxxZB/ur3z2mKbzVx1MDAwM9ZcdTAwMDJ3XHUwMDE19GqYwKpkffxy8lx1MDAxYy9+R1RWMlhgZZ3PnFx1MDAxNSMyv0zEXHUwMDE5vIx+llrhgP2wREDWXHUwMDExv8Df8cqMJJzWXG55gb/+/Vx1MDAxYfH5bllcdTAwMDeiaaItJFx1MDAwNd42i4ZIZUrmNb9KR69NSPhcdTAwMGbxksZ4pKEry0tcdTAwMDSamFDjfIBcYlx1MDAwMuOz5iV7iXmvXHUwMDAzXHUwMDBme0VaZt3z7J81dk1zUqVLzN9sXHUwMDE59oxa3lx1MDAwMlx1MDAwNGEm8X1jXXi/wlx1MDAwMWtcdTAwMTLQ/Vx1MDAxNetRXz+NgZ/9KEM5ufjcprqX9urtVvu2XHUwMDFmdvY8TVx1MDAxOXhzlpdcdTAwMTVoKFx1MDAwMk5cItBcdTAwMWJcdTAwMDC32lmmXHUwMDE0M1x1MDAxNlx1MDAwN0szXHUwMDFiiJNcdTAwMDYj+Fx1MDAwYivkRabbMldKg0BhOaLkkVx1MDAwMstYmMxcdTAwMWNcZk9ySljS0Vx1MDAwMVx1MDAxYf7FXHSSUs26XHUwMDA3oLR5RVaFj+7xddZcdJD/m766aVx1MDAxMOD0fMq+4I03TGVcdTAwMWJ9fy6Qvlx1MDAwMiyNLlx1MDAwMC1cdTAwMWHyXHKAsuFcdTAwMTZWloU/yVp/1vVcXKshZCyKyrYmXHUwMDBmXHUwMDA3XHUwMDE2zHg79lx1MDAwZlx1MDAxY9hUQ5+HoDV2YVx1MDAxNUPa49V9qZJbMphgyv3ufLhD4fn/dFB/KfDRWlx1MDAxNf486XfxXHUwMDA2cFx0SrBcdTAwMTlstODhfFqpy4LpqE35XHUwMDA0bL91QVZHsuTyXHUwMDAxdtw7q1x1MDAxNnRmp4ukslx1MDAxZqVcdTAwMTF2uU1cdTAwMDJ83KlC6EPDgYhqklx1MDAxYvg1X4z5Ls6riZ/yXHUwMDBif9CpvUGnpECrelx1MDAxOOflnUBYXftDLNAjUE3bMVx1MDAwM/ZmIYmmYMaWMk4mm/Q3g3QoZeKlXHUwMDBmXHUwMDFhNFx1MDAxY8180XTLLlx1MDAwZdvJX7xcdTAwMTPsXHUwMDE1OKpmkIdcdTAwMGavmCC30pNcYsO4Qlx1MDAxMHhcdTAwMThh7ajtfFx1MDAwYrrI10ZBXHUwMDAwqLLCakxcdTAwMDJsqp1cdTAwMDBcdTAwMWRE5EDhlKDzbO9cdTAwMTUyX5R4fDl9kEhcdTAwMTUw42k7Mc6zwlx1MDAxNc2h2Fx1MDAwNpGBkmpNpKlcdOUt2eR6d0H6Sbl+YVxc/ivXWHWFwFx1MDAwMaFcdTAwMDOD0IUxoOYlsrLIlnY3vULYh8jix2XsfG9dorHpjazyslx1MDAwNn2zKlx1MDAxMbrTR1x1MDAxY7Cyd1x1MDAwNpBh/Vx1MDAxZJD2wzIyqeH8YIRGKMtDXHUwMDA1kbVcdTAwMDfIhNK8dkfv0Sb/MpY1w8BcdTAwMTBg2CxLXHUwMDFkyOy9T/acm/z5XHUwMDFkZ2yS1I2/v51reeBcdTAwMTiXV0O85qH8ZSsmmp/BxdjNn5I3XHUwMDEwkOjKss6/XHUwMDE19MBWXHUwMDA2weJKepmEPM1F/PEnPVxuZ1x1MDAwMlx1MDAxMmdcdTAwMWL/8pzJMj7O0ESEXHUwMDE3r33XkOtcIkaJLWJccvBcblx1MDAwNaY1XHUwMDAwgMdcdTAwMDVcdTAwMDYq6oNbfaE7XHUwMDEwXHUwMDBl0I95ZZDJpCu1XHUwMDE44k1cdTAwMWH2q1xmJ9FcdTAwMTg0WrzSxnFOpeZ/2yi5auIyYHFhZrFf61x1MDAwMKvf+Zp2cl3av71cdTAwMTepdmA8VSq2/dsrO/hPvevXbSFcdTAwMDRcdTAwMDBK+1x1MDAxMSxcdTAwMWIxMT6BjWzWXHUwMDExqSk1lLfRXHUwMDBmoVTH0s9mZ9G9r75Rb8BcdTAwMDCroYz1+ID2fsZOXHUwMDAyf3/B9TChZzRzZfRcdTAwMDNGVtGEMnjKu49cdTAwMDHuMavkXGYqXHUwMDExVpj2XHUwMDA2sY8zaHl8WD900Yg/XHUwMDEwXHUwMDE0tK3Ldlx1MDAxYoE3IzLUl1x1MDAxOW5ftlx1MDAwMcOFXp20o+FcdTAwMGIoc2ZaZy7KcphDm2z3Mzc8u1vAv221Zlx1MDAxMFdsZnX76YKhrW2I0Fx1MDAxMoNcdTAwMWLkzi7P8ae7XFy0ndjLJ8xN83RcXPJcdTAwMDKKI/Y6L/IqkchEXHUwMDA3XHUwMDA3KDa8YeZcdTAwMWbXZuXibv/lJ1x1MDAxZdqepPh7vWW4XHUwMDFm1VJoIPZBsP6n9Vx1MDAxYrN9v99fNZNymbZl2Fx1MDAxMm7Q42bUXHUwMDAwenmZ5uDrcE+iUvxQxVmp0o9zbpM6gHNipvnvro2JX1x1MDAxZVxitVxmXGYk0GBcdTAwMTTW3PGT393PXHUwMDBmtstygDGbOkfvrDdcdTAwMDLxY+FcdTAwMTa/Plx1MDAxMsV3MH1LaXGsMIJcdTAwMTTsPbBCRlxcXHUwMDFhQfVCWH7++Z1C9oc0tcNcdTAwMGUqaKvM5eNWc3LkRPdcdTAwMDOxXHUwMDAyQkWAXHUwMDFiooxuYsjHyVx1MDAwYo77VyxajDB3uZT1RpFcdTAwMTV3JUXw2K3zXf1DOTsjXHUwMDE236g4wcP1m1xyXHSnos9cdTAwMTazmo1/XHUwMDAw7Fx1MDAwN29cdTAwMTfuf4d1oTLCN0ZbXHUwMDAxXHUwMDEz+uQpOlNcdTAwMTeGdlx1MDAxObKDgJJiP8LGOXJcdTAwMTPcQPVhdU4v//yLiX+i/n5cdTAwMTUm3r5cdDJcdTAwMDanOnU3v9R950S/XHUwMDBlXGZEg6tjs5SC6PG7s1x1MDAxYl9AvHnPf9x2vp3URnZIXHUwMDE5XHUwMDEyI3BcdTAwMTftO8FYNJdcdTAwMTBcdTAwMGKgcKrGqjjZjFxyRFx1MDAwZShdalNmpdj1WEsh42f7UZPzhVx1MDAwZuP6eMni5ZEyflxcqeu7SfLLz7PK5bm/om16TD7STkm0t3Kal9LqrLmVz45cdTAwMWOs5jHvXHUwMDEzcW++Ry7KLtU11tdN+SziTnLdjnZcdTAwMWZcdJmWyVx1MDAxOFY9XHUwMDE3sFx1MDAwM2qEp8VLUqJEsWnV+Ver4Y9/cnGkYWF/wbkkRVx1MDAwN6BphWkmjJX3T/lcdFx1MDAwN7Vtw74t1mzmJig0XGY1/VGfXHUwMDE42efduVxc7kfaq2mvc+PqZ3atlEJXI1irw2E8Xyyv6Tv86JxcdTAwMTFbnFwiXHUwMDE19L/csPHHcH5Ivkxq7LZKMlx0TJXdvFx1MDAxZiF24+CATo7j2UBIXHUwMDE1uulcdTAwMWLg32Br/71jQdhcdTAwMWXcVmh2XHUwMDE02IZh0eLogVx1MDAxNJfZn1x1MDAwZkGg4TL0hFUnvHdUZFTp8XE78bu/TzZLvltR/zJcdTAwMDfF2XRQPlBjPq2aopJcdTAwMTDw1Wr031x1MDAxN1x1MDAwN3ErQfmzMUHs7Fx1MDAxOVx1MDAxZlx1MDAxZSFccutVz99J+eJcdTAwMWSo1lxuXHUwMDE33lx1MDAwZWdMslx1MDAxNbtJXHUwMDEwJ5tcZou9XHUwMDE0jI5cdTAwMTQoLVL481x1MDAxZXTgL17ZuOXfXHUwMDA2n1702N/+vWezeq+2UD33iU6P8oBkJVx1MDAxN68xY2m/70xoYuXz64xcdTAwMTizkog7IVx1MDAxM/Eq0FxmlrpixHd0eH7sUeap+tqMSdfVlW2HbJFcdTAwMDGuquS4XHUwMDEwQVx1MDAxNvtqVapccmFZe7a+5duzm6g7dO71c1xciqJcZsSyjIjNtS2qw9Fu0JGb0qzHXHUwMDAw3rlcdTAwMGJcdTAwMWX/SlxyNjlcYp1erVx1MDAwNY5HXHUwMDE0ce6mJF+nlHg5iZsvkVx1MDAwMOmrifcqcPfSREpcYtmfaqlcdTAwMDBcdTAwMTJqR8WS1/qd1zxE1mtcXLKLXHUwMDBlzkLw1LrPQaxcdTAwMTZcdTAwMDdrXHUwMDFjmVJcdTAwMTTLzbVcdTAwMWbfmbtubbKLlFvywoRMXG5nulx0VFx1MDAwZVx1MDAwMoiLj4Gr8+xzPFx1MDAxYvcl4+zfd+Bb9z4wRNhu/13SlUDNnsp8aFx1MDAwZVx1MDAxM1jP59Fn6IH5lLkm3WirgdizJ1x1MDAwM/okXHUwMDAxOiHpYVx1MDAxNCmPxlx1MDAxZUpdXHUwMDBlfIB4Nvp7p1x1MDAxZiGQmjhcbjT3fkBcdTAwMTbxXHUwMDAzSoZWTSrv0l38W7+rMSt1qvxj+GhKXHUwMDA3t3RcYvb80EPIXHUwMDAwOTxcIlGL6qWxJkBcdTAwMTjJR6rXbVx1MDAwMH1+nTPJXHUwMDAwXHUwMDBlzUmzdT/FT1C/hvuI+4gnSTvwk3u2XHUwMDFmpUFccky61przeVa7yHY/x4knNDCQJW6r58fLYH2+XGb5bUV/VzZFVIhWsVZcZnVcdTAwMTdJXHUwMDFks+o5elFcdTAwMTf38axcdTAwMTCAt8jGu72YJ19vVPHfsYBUKotpdo32XHJ8XHUwMDExuC2QtfmOKGCmolaAXHUwMDFmzH1Q5DKCU9iYKC1k+42ze4LUq1x1MDAxMivCmpyTwEA9XHUwMDEyXHUwMDA2R/Jxrlb5pIiop2Dbha1K/lxiKNSXXHUwMDAxxrRcdTAwMGVOeVx1MDAxYbr16yNOutits1x1MDAwMVDnYvRn0v1ic2lcdTAwMTiO6Fx1MDAxZFx1MDAwNmJ2NDPJv+5lq9NcdTAwMTZmiFx1MDAwNChOqdk+jCawXHUwMDE2echsNiW2SMT+aj+8XHUwMDBiKKpxt+7QtFx1MDAwNiVT0E+dxNjkXHUwMDA2hTSg21x1MDAxN7iwV8Nt8O8lNFx1MDAwZTnbXHUwMDE3eupWXGZwP9qsavI4XHUwMDAyXHUwMDAw+Ya09PJwr/NcdTAwMDNQJCg2niPvY5+M4EB8+5aqiFNvpvNZ3j5cdTAwMTlL9cm/mibetlx1MDAxMHi8XHUwMDE09DFuZtJcdTAwMWEncZOyY17t/3C83ZzDXHUwMDE45l7yXFzoh5XQJK9cdTAwMTTR/Vx1MDAxODBcdTAwMDeIT3q8zE7Sz0XiLMm248/1KS/0Vef9Raeza1x1MDAxZlDmOkSGSFtM8/ZcdTAwMDT4avBMXFzVxj7gXVx1MDAwM5BBw5zZXHUwMDEy2Fx1MDAwYincKChwYayFsEMj3lxmZlx1MDAwYq86srxnk6Jy4Vx1MDAwMMuaVlDatuQltcxBPVxyevuOXrFbLmuv5faKhIPx/SDXb0otk24jdtSR/Vwi1sqgf6aEpEEocrPsXHUwMDEwXHUwMDE2z5bhXHUwMDE1XHUwMDAzXHUwMDBihdWMN1wiKDZ11vZ/SDqP7UaVKIp+XHUwMDEwXHUwMDAzolxiQ3LOIMKMLHKOX/9wv7V64PayZYmqe+7eRSiAXHUwMDE1tHuPXHUwMDFhNVx1MDAwMVnArlx1MDAwMJilR5bLm+1cdTAwMDC+sstcdTAwMTf0NvBjYktcZidcdTAwMTPlQe1B8lRdXHUwMDAxIGNcdTAwMWFcdTAwMWVcdTAwMWJcIprSNp7OyVxizZB8rruocVx1MDAwYnVcdTAwMDGrqkK3t3+UOIjf2e+YgshcdTAwMTRYw2/pLWzmXHRf9lx1MDAxMSykkMyFw1xu3V3jTn8lT5/Fge76X4OlRXZ+XHUwMDFkrl2V26Kw5XK1L1x1MDAxNXJ8cPSmt0RcdTAwMTVcdTAwMTAjsF3y1/S7qFLk9JSQ6aTUrifxXHUwMDFjpJJbXHKuXHQ2Jikoo4jh2uRLayjr60s/mflbm3RmuuQ3jt1uUF9NXHUwMDEzMf3vXHRcdGfboiBrVI1cdTAwMTJ+XHUwMDFlMLbDXHUwMDAyXHUwMDEwP1x1MDAwNCBdxDczPlx1MDAwZZDWt4S06UXu9iNcdTAwMWXRVzyLdNVcZlx1MDAwMj+lXHUwMDFm8eFcIltcdTAwMGKGNq99ruXNevvbq2FR4V3ILWlcdTAwMDI0ckRcdTAwMTVObqvmXHUwMDA3XHUwMDEz5f5oW1x1MDAwYlxcXHUwMDAx4+zotquFXHUwMDAxTTZcdTAwMDTUzOWcNe1D3UEztUYvuylCY1xmKVx1MDAxYVSfZFx1MDAwNFx1MDAxOS3CXHUwMDExl+9cclx1MDAwNUjw8yyeXHUwMDBi9+MwQ7WHQJK4O/JHkbG5XHLKxVo3XHUwMDFh2YNcdDJq/5ZcdTAwMGLH2IbrRU3szz3fXHUwMDAyXHUwMDE0nt5cdTAwMDf3a89cdTAwMWPiWYxrVo81u4lD69xcdTAwMDInWFWRYFx1MDAwZnvTXG5cdTAwMWRXRVx1MDAwNlxypZdnnEjyiMyK6Kp4xYE/+V1eX5boPoI44Tfhid1TXHUwMDEyus+lXGLKSkho1FogKp93SkBl/O3e+dCKXFyLO7LcpPWJMTxK1HzPZ1x1MDAwMKjxycZiRLTKzU7ZKNphwOS7VXNrtvf+XfcgXHUwMDE0V9Pq6y8vY9KRXHRcdTAwMWQ8n5OUwL2SXHUwMDA0kFxuK4Mw3/HBpf1C7LeFLVx1MDAxZbdudF2W+1x1MDAxOfD7emcraphcImBAXHUwMDBiXHUwMDFlO186kFlX+PAyxUZ80eiM53q61PXXblnQ6J4pb8dcdTAwMTOcY3xcdTAwMTmRJIuE86VcdTAwMTfJWjZoseKI3Mq4pKlWrJD9hM3g8E3en4u6Ufe79YlfaWeNnDBKbVx1MDAwZn45tDFKSl1cdTAwMTPHZ5FcdTAwMTZ9Y9L66Fx1MDAxNHBcZkpvuYnpQubtIyH7Q8ZcdTAwMWN4qHo++Td/cFx1MDAwNusvI3CRhpEkwWjX6pNcdLabNlx1MDAxZphlpvTglEd/J1x1MDAxZbZeQn9cdTAwMGVvUqDsiq0zYOwqYvBcdTAwMTZcdTAwMWJUP1x1MDAxY6Jd9+NsXfxcdTAwMWVcdTAwMGJyVEVnVVLHc3bWiSdD27TtQbKGT0DfuWZcdTAwMGI3W/78XHUwMDAwIJ2h+1x1MDAxM1x1MDAxOGHb0lx1MDAwZZLM+Vx1MDAwNHjx0D4ojv9cIot9oUPt5XBqXHUwMDBip1No2a1cdTAwMTLMxtWMN6xcdTAwMDExXHUwMDE2k1WRzHy7jVN/705ht9w+SP/+e3plZqbhVtPtOlx1MDAwNc9CXHUwMDFiuY/0yUc5yvfgx8ZJ7lx1MDAxN/1cdTAwMGVLZqFfLlx1MDAxM5lqVcplb+vxrdmA/1x1MDAxYj9LXHUwMDEzyZp3aLmlvbmX71x1MDAxM9iQaIdcYlx1MDAxOFc/XHUwMDE0XfYyil5FuadU0/xcYoJLMJxxtCCzXGJOg1NcdTAwMDHyXsYgRD7HZt+Gs7pbaEQ/IVlR+zF8XHUwMDBlqlbfylx1MDAxZVl2brbabFVNVPW3J5n1mSpcdTAwMTdcdTAwMWXdRbhBXfJr3DZcdTAwMDE/zlulXHUwMDFkoV66xyVcdTAwMTBcdTAwMTDIzcmZ6v1RaGRcdTAwMWJesuzcvGE5yUFSjrGFXHUwMDE2+FB2pdN6qlx1MDAxZTBmvO1y/Fx1MDAxYVx1MDAxM/x5XHUwMDFid2bUq59Pl38lzjxj11H3YKJccpSVdddvuVx1MDAxNvjoTG3p0kDmYiNzx7KieP1cdTAwMWRWwFTZp8usPZP3S6r8aOIu695uXHUwMDFl8aAjNEzWZLK/d4VYk6xXo1puXfS2Mlx1MDAxMuHuUSMmxY/JWrdwej8r/Jp3UVx1MDAwMpj+rN+aW2lcdTAwMDe1aUhcdTAwMDVKg6JHhnQh2iZcdTAwMDEwNl9tJDVnXHUwMDFmpFhcdTAwMTWKfUh/XHUwMDBmVszfwlxyMlx1MDAxMVx1MDAxMX/FXoZcdTAwMTjlQrdcdTAwMThR8EpcdTAwMDLIg1xyLz2x4lx1MDAxObhmzDg8zklox25uWLmzQ1xmXHUwMDFmir/Sd8rlMUrgR8Wqpf/WN3JJ42PbUORn3o1cdTAwMGXeS94+zK9q4dZKI0zz29IrXHUwMDA0/0bbjDHk11x1MDAwNsSeXHUwMDExUz+z5D9cdTAwMTJcdTAwMDNCXHUwMDAxo1wiUEhcdTAwMTVP4ftcdTAwMDfi7z4mk7fMQtXc3uyu2kx2aG8xwJlcdTAwMTIltmadLFx1MDAwZk8gXHUwMDA2Jlx1MDAxNcxTm2BNWb5cdTAwMTfhnT5cdTAwMWW5MN3gsLnt8+Lrs59ojytPcWIt4WN2WGBOgjjPhYt0+STB/q3VRJWfr0R1MtfbqpFefuvlYVx1MDAxYiXD3+Il7ciahlx1MDAxZFx1MDAxZuqLXHUwMDE5cIeQZuC761xyN7qbamdcdTAwMWbjZVx1MDAxY4TjJDHHXHUwMDA0XHUwMDE5XHUwMDAwlsJcdTAwMWbmVlx1MDAwMz+VrHr6XHUwMDE1Wlx1MDAxZtVBYcVGQM6D1NMyJersvlx1MDAwZdNVuM6dNjcgPXtGit/Fk3fhsGlcdTAwMDW8x3VcdTAwMGap+PvFPou+Rf5cdTAwMTFEOVBQg1x1MDAxZjDf9yNjXHUwMDFj9btLubGCv1x1MDAwN1x1MDAxMIO+mcFcdTAwMDJnQzhBUJ99lFx1MDAwMTD6WTNI7v5bZ/vfKnTmQZpcdTAwMTdP9r2U0epmxjvnklPR/F2rflx1MDAwMF41TLqlfawj8ycgZNfq27dcdTAwMWLZXGYxVY+gXHUwMDFiT/SOklNxjIVcdTAwMTOkXHUwMDEyZChtZVx1MDAxYio7XHUwMDFkfm0s9FwiRz7H7Vx1MDAxMnbPXHUwMDBmUbBwelx1MDAwMMtvOcZDTfWFXHUwMDBiOlI+y8epilDgXHUwMDEwuWqKmrlnvvKo1VFEr1x1MDAwMW/hylVcYktU5JdcdTAwMDLhmbQvXHUwMDFiXHUwMDAz0t9WxFx1MDAwNLHSZ6Cj/V6sJVYzzzs2bI9HajKKUY9cZlxcOWD5NbVcdTAwMDPvWrcsqilcdTAwMWHGXv+Z4kDISyC/4Fx1MDAxOeTkXHUwMDE4c+Zweu1cdTAwMTF+keSbZVb8g8dcdTAwMGUqvlx1MDAxN2/LNslcdTAwMGX02F3x8VxmXHUwMDE4qpI78Y58vWjcLuDknLl01NFcdTAwMWTDjdPpQsS2LqtSQWhaWrVvLoOZc2LntVx1MDAxMMy6z3KFXHUwMDA2oFxy2JrIgJW0NIiEuNeNbSxPct6pX+lcdTAwMGJu2pk0KGxfQ6LNXCJZQpulStD66qOysZbTv47Eb8jb3nA8NpyG4Daz+X1lXFzWXt3iXG5IXHUwMDA3pFL6lL/q8+rQJ6U05SNcdTAwMTJkvUbrXHUwMDEyfVx1MDAxNS7QxVC9i8WHxFx1MDAxYt6XMyZhbPr0T+dcdTAwMWJcdTAwMWTZ2j5FYrQ+cCvFIPihsObbYz5F1UJdSFx1MDAxZYGxc0f6KcSjWO9cdTAwMGXtXHUwMDE2R5mSrNzLX01cdTAwMWaaXHUwMDFkvS93RoyoXHUwMDFm3Hhfw1x1MDAwZsDI3Xo74leuueB5oVx1MDAxN6GEN892RugzcJAmnMPuuFx1MDAxZo12MrKZk/zMv1RN7ZZcdTAwMDJcXEj3qYmU5vv4l02TNJut0zpy2yPazcA4W+Y4XHUwMDAzcptcdTAwMDFRL65qzYfqMiRqN0M9XHUwMDBiQX56g1x1MDAxM13yk25cdTAwMWGjq6rNkd1It1xyllx1MDAxZlY/scyKXHUwMDAx9jFAI4alpH3IVlx0N16lXHUwMDAx4DdcdTAwMWJcdTAwMTlcbmX6V+vskDJtd3m9kZVkN1xyuHlSurJcdTAwMWSq789Q0neO2kucvIfzRy5cdTAwMWZcbpPJqbx55+/Kt1x1MDAxZs9cdTAwMWNcbs5cdOewnKKGkDVcdTAwMWJlz3Z/P9Up01/YXG5IXHTP6lwinHO2N5RbU1x1MDAxYkRcdTAwMGZcdTAwMGZ16W5cIje11Vx1MDAxZc9SWmF8fIxTv1x1MDAwNYdcXH/Y6kvccbYoXHUwMDE2XHUwMDA1XHUwMDEwhZ+sXHUwMDE04fpS/Vx1MDAxOPRhYdZ3XHUwMDEyp9+0600v/MdcbqB5Li/H7lx1MDAxZCDFX8u5LyxcdTAwMTbvpCTXXHUwMDE3uV1q11x1MDAxNOYk2OBrPmZaNUPu0EtcdTAwMTKElp7GXtkgXHUwMDE0OpKkXHUwMDEx9Vx1MDAxNtL6dqnF4CmmXHUwMDFhl/opTPyC1KpCdoWokKdcdTAwMDXYuE3SXHUwMDBm6KHSslx1MDAxMnNcdTAwMTNIQ36MOFhmr3lcdTAwMTn0JDCGjparlzNdJIZj96IkXHUwMDFkWifUMnC5bM/S9Dl7v+/NXHKhXHUwMDExblx1MDAwZuA2kjp39+n4VXDtm5dl7Vx1MDAwNcR/+/v5KSNcdTAwMTfx5yTq1ztcblx1MDAwZnVz195cbkA72Vx1MDAwNcWfxrYw/uVaykPkXHUwMDBmY/ydQzvoXHUwMDAziSomJphqllGvR1k54Ka3hlx1MDAxZZ7561M/LFx1MDAxMVGjty58WHiHYlFcdTAwMGJcZirbe9t8XHUwMDEzv985SmtgNTZcdTAwMDeBV7FcdTAwMTZSZ9IpP586j6fCr/7qj6JlqfBaWosmXHUwMDEzYm9cdTAwMTI0KtCj2o+Q4Xu20HpcdTAwMWOM6qlcdTAwMDAq/WEuQOF6ieV8ZFx1MDAwNC4oaVx1MDAxM1xmpWg+lChXXHUwMDBl9TZrIPlcdTAwMTKmylx1MDAwYrDaYIpt1n3Qol4gffl+KIToO7VkNlx1MDAxZlFcblf1MlH5La+wXHQjjFWotNVtXpX+XCL9IYjdma2jI1x1MDAwMz1XZFx1MDAwNu45JPDqZutcdTAwMWH2Qm78u9MyXHUwMDA0vJZcdTAwMDSbXHUwMDEykFDBeFx1MDAxYl1cdTAwMDLjSHhi1pzsw1x1MDAxY5QxxD6WXHUwMDEya1fWezoqIdibRfnQMVlcdTAwMDXeuozRT1xuX3ljXHUwMDE2QE5cdTAwMDaH9m1NXHUwMDEwPFxc8S52XHUwMDBl4DzSrFx1MDAxNGZcdTAwMWRJpS5i04dtOlx1MDAxMWmC//Ki5dHgXHUwMDBmPbqUxT4wOUSNJZNR6cfYxMe0nGt6r1wi0TJY37RcdTAwMTTXMFx1MDAxNFx1MDAxOcRArpjZadqtOWSjoLOeSJltSPVcbr2JNDqrmGSNWoo57Fxui61cdTAwMDGN3WyluL1cdTAwMWYy6WrBkL+WXGL0vrPB5p6MOuSAXFxqYfDomFiCiXUkhFiXt+kkmGG0XHUwMDFhqNBcdTAwMTDXIWda0pWXR35XtH1cdTAwMTbk4pOvv9xcYihcdTAwMWLvXHUwMDAw82Yw8aNY/J0n41xy1lx1MDAwZuK9iHTBmM6LKdtEf+hcYjLF5nVOyfXzJTuOz6ZcdTAwMTQxuDv+9V1gxUlsXHUwMDEzKrpXc143tUpbY6NZ4fJcdTAwMWYnQpRPofwnhd/jo7KpXHLtXGJcdTAwMGL/lDpcdTAwMWF9XG5ZsMXhVojHY9PDVHkys3VvktOTrZ/TaXhcdTAwMDdhM1x1MDAxMbpcdTAwMWSXOrzDXHUwMDE3ssxJOVx1MDAxMt+uaf1mKV7TZup4ub26LjLbwC1zxPqaolx1MDAxY9RQb1mdY3rEZ4/GhU20erTih+5GXGJcIlx1MDAwNHskn4Z8gevLaFx1MDAxNvmo2lx1MDAwNG7X7Wx5XHSnXHUwMDAwhVx1MDAxZjOZ00SPXHUwMDBibHRFrb+uN+Uo597ZmV5HQnch4Vx1MDAwNFx1MDAxN+7tx9/feL7mfjndNlMsdJxcYlx1MDAxZb86M9Y4pMdFw3za9mWMns6qXHUwMDEwL2hcdTAwMWamW42SwV2IfVTFyrOBfVVcdTAwMDVb4kNRpMFcdTAwMDNWKlx1MDAwNFx1MDAxZlx1MDAwNFx1MDAwM0Df9cWva0hcdTAwMGZcdTAwMTF5r6dJXHUwMDEywpvYJkvoO/lhPlx1MDAwNp6TcHySXGb4XHUwMDBmuFx1MDAxNPWvsni/lLbGQ2qLX2v5XHUwMDBmaUZcdTAwMTGweVb7peF3XHRcdTAwMDS9/F1cdTAwMDPcLPUjvFx1MDAxMc1vyk5EJ1x1MDAxZePut0a+s/U0x1czV3nqPyvWXYDzu7fKN6xNScxWzlx1MDAwM+xYK3jKXHUwMDE1az1cdTAwMDfk6Fx1MDAxMK6Lt2X8tTk+gnB+u2JcdTAwMDf1fK0nQGY6ldT4RF+Q0zGX21FcdTAwMDfOOlx1MDAxYsd9XHUwMDA01HrO893NXHUwMDE1Zlx1MDAwNfbA/uTMrKO4u/D0uVZ1gKjDZ3ur+Un2XHUwMDE0j9BlXHUwMDFjRiFcdTAwMWGXXHUwMDEyXHUwMDEzRMluXHUwMDFhz1x1MDAwNypmXHUwMDFjm1qOXHUwMDFm2kWDWKZCXHUwMDFinlnBK1xyrXV+3/rStLUtuFx1MDAxYzXka6qJIF5cdTAwMTFT4y17Rcc5VcRPqEvxiM6WtKK8eWfmwlRo/ajQMC491JGqQLOPj1x1MDAwMYdcdTAwMWImq1x1MDAxN6ssvcYwrlS8W3k3f9r0byR3aPtcdTAwMThraV2159QhXHUwMDAx3Cms+ptcdTAwMDBcdTAwMDbCNEBcdTAwMGYgeyGgbFx1MDAxNOB9W41cdTAwMTD4yjB6NFx1MDAwN9szzkg/lK51OLyN+/6KW+NEXHUwMDA0gqOXLt2HMI5cdTAwMTRRwPDC/vGUXHUwMDA0yZVY2VCLYdnPXHShXHUwMDExXHUwMDA1ZtJcXD9cIlx1MDAxNmhUwqSle5Y2j+6qqMjiPcXuMlx1MDAxMlVzKSrEkk92XHUwMDE52/Whlssxm3PWqlRzJEd6hlx1MDAwMvq6kuMozytcdTAwMTPyqlxiXHUwMDE3kteQhSiK4n1cdTAwMWO+WnFcdTAwMDHdPqVcXIqfmSrkMkVAsbnWhFx1MDAxN1dcdTAwMTTPIGl/KnyFv2hcdTAwMTjRXHUwMDAy68CNUVg3dsZccjZWvOVXXHUwMDE1kes/pPNMV5NhLoNcdTAwMDDLWoq4vnJcdTAwMTRhx5fsT2pcdTAwMWJg2a2fTplwqWlcdTAwMDGuyI1cdTAwMDeR2LtG4f3Zoc5l31x1MDAwZVxm94dcdTAwMDZA7EpcdTAwMDHfilx1MDAxMCr5MZohVFx1MDAwZoS74DKA1Yn9eCBcdTAwMTD/Nvz1N2TPcf9VKpQpkvbarZSnq0epW15zXHUwMDAzT8H0ZtftNlx1MDAxMWqv5Fx1MDAxMYMlnWiQhppcdTAwMTDevF5CXCI/d65cdTAwMDdcIk5SvC9N9i32XCLX2rXUVcBcdTAwMWVcdTAwMDbyXHUwMDEw3pTTj1x1MDAwNnwymPQtnlx1MDAxN+tcdTAwMGJy61x1MDAwNHwxXHUwMDEwz+eIiGdcdTAwMTLN0d7PWErKJHYlbsv1Wd9cdTAwMTcr7uLsKN9cYsBzbVx1MDAxMLU+jy6+8fngVIdcdTAwMTjarf1tXHUwMDEx904oXHUwMDEwIPNPr1x1MDAxOaBda3VEXGY7/jNcdTAwMTBcdTAwMTR9VuNcdTAwMDOBVpX3a0VrxDrNa1x1MDAwNONcXCZT04da0zap9I4g0y7s1L/nbOpcdTAwMDJt16zNeI4upDEznpDpWdxcdTAwMDApdM58Slxm2r9qL4hBUFumXHUwMDFlsTikgNns6aygo/L5qnMgYz+eXyslIPNcdTAwMTSGklx1MDAxYfwsJ1x1MDAxMo2JpW0jXHUwMDBi7Vx1MDAwMW8kefqtXHUwMDA01X/yWFx1MDAxZFx1MDAxNu1cdTAwMTJLrHtwg5C/WnRnSrCdMlx1MDAxNDphNt9j/Vx1MDAxNCtcdTAwMWW3WNZQ0ND9pvA44fbubVBHbY+1jVx1MDAxN3/FX1ZcdTAwMDBcYv104GtB15eOPpBcXC7yt/zuOJpvVIAkQnXC+fv/OnFcdTAwMWRlPZ6uXHUwMDE2Ll38lCy1i/tDverAJ0LpN+yvt1FXX34wXHUwMDA0XHUwMDFio/kjvnB3/liYsnEpiqny06r41FH8gFFnk01sXHUwMDEyXHUwMDE5X01Tg40xS+1lSYdcdTAwMDRtUFx1MDAxOD5hnVx1MDAxN3+X47RcdTAwMTlcXN5Mc1qQN3fbXHUwMDA0XHUwMDEylEgpj4RCmiNOydScOO7zubEnnmWKKZDBZv5Dp3Ao8JL/KVRv1lx1MDAxOKhcXET141x1MDAxNlx1MDAwNy+5XHUwMDEzvEulXGa/XHR3VYgxwPXC/r9cdTAwMWWnNOR8blx1MDAxOSawXHUwMDE1g0zHulx1MDAxYS3Cy1SnPpZRXHUwMDEzibewXHUwMDBlqEqgNcKj75KXlpOYrYjx77lZNnroebywJHNQXHUwMDBmu7jcXGa+vs9LXHUwMDA0XHUwMDAynVx1MDAxM1x0dquRXG6R17FcdTAwMWZSui1AOzp7k6J0aFx1MDAwYnxLKtJ25Yr3WN5myb91O8A1by9JP0P4IVx1MDAwMFx1MDAxZfBcblx1MDAxN2yQ9Vx1MDAxYlx0XHUwMDFm6LGu1KVu7VXoxIeDctZ2cEBEXCL7kIUnxemDsluATiiELNnOkMyUXHK+XHUwMDBigC/jmFYhp6qN3VqJblx1MDAxMcTYgElIXHUwMDFmsl0nfCrP46P1XHUwMDE0uV/IUaaF0Z6k8f7Sj1x1MDAxY0pCz55cdTAwMWJ2bVx1MDAxNvL9yn9zz8x7lmJcdTAwMWa90Vx1MDAwM0y2XHUwMDA32SX85uqJnqmJ7utcdTAwMTJkaLxU2I8hTzk+2KvQYlxiy/KZO5qmK5rvePvr6FxczGXqorhcdTAwMDTXUCt7ZiY7K1x1MDAxZtt5XHUwMDFi/05cdTAwMWORXHUwMDE48T7kXHUwMDE5PKpvVu00tDV9YeFzj1x1MDAxOfgtRVx1MDAxYVx1MDAxZidcdTAwMTJeO51zsKNV4ur84lx1MDAwMrffXHUwMDBlj2XtXHUwMDBm85+tf2vk+4uR8fr2n8NG1Onmb6Cp75WwW2dM3nTCKOO8WzF2+cPvb85BP1bO1LLjOT38oYdkyT3L0VxyXHUwMDBiv5bzXHUwMDExs6+qUkhWxM/32vxz3Fx1MDAwMS1cdTAwMWYrjkTUq2xYRrR7XHUwMDA0/CWej1x1MDAxZEZcdTAwMTfA0Vx1MDAxYYhM5/lFXjtTKdlq1jHUIU3PTvwwWDsnSFx1MDAxN9fS4Crbwlx1MDAwMUzYJDzYZlB14fLNr3HfZvVcdTAwMDfLb/5g88M81ecn3Wr460NcdTAwMDc181xuIFiy3izJsVx1MDAxMrXxYf2dfflOaNZylFx1MDAxNa1cXOnlfOKsbJ6NuXUtuoa/h9fOdrO+ISDgZU/U6WAlTXRcdTAwMDHfK/4gyVx1MDAwNuBUXHUwMDAwXHUwMDFmXCJcdTAwMGVt38xcdH9u9USqUF3q1kuHbGP6XHUwMDFkLY0y01x1MDAxMLiU4Vx1MDAwZTKa61lBXHUwMDFh9FxmXHUwMDA0oE1KfmxaxUJ+ozPe9HItZr1otbAxakdXXHUwMDA2XHUwMDE4mYrYjiylXYXGxPeurUsgYFx1MDAwM7+qIIimIUeeXFzDmPq3hvLDUdSLUjYpJqTeRby+Jc3doeXwXGbo8+D1O+6BxWA9tFx1MDAxNomDpiaIXHUwMDAxl9B0XHUwMDAz7r1ML87SKVx1MDAxOfdcXI+MXHUwMDBlUzN8kL2yXHUwMDE19lx1MDAxNnRN8ts3SaODXHUwMDAyby6+rGdgq9aSLVv9dbnXXHUwMDFm6UdpZEGCvaopVs5cdTAwMDRKrNJ+kiFITFx1MDAxMlC4XHUwMDBlop/gTN+Jgaw3QGxcdTAwMDVcdTAwMTfEglx1MDAxZOK4XHUwMDE0rqHi0ihcdTAwMWF9sFx1MDAxZPt2SZr6+8LWOyOm25LdvO89NSOkVPms6vSq2cZYvFlcdTAwMGKM5p1cdTAwMTbyi6dcdTAwMDVcclx1MDAwMZZXRD4mlqfSXGKyn8XnioJcXJWN75ZARjNjqN+OXHUwMDFhXHUwMDE0msmsVr+h8VFJPSNcdTAwMDPzoPw5LG/L3zxAcWC/zr/dXHUwMDAwV4dnk/I3vOwp1LqKuieK1FdvMPpxIIgwn+/xV4QlPtD8SZ7VXHUwMDBi4qPhfshcdFrNasQ+2aNccmBRVeltxpcuXHUwMDA18p2Lm/RcdJGH21DQJ3/TXGZrpeUu8a9UeM4+0400oVx1MDAwNUdTjFrtXFws01xyIDtIXHUwMDE1KE7euIblXbZcdTAwMTKxxk21ZmckXHUwMDE2XFx1t7e5zq9cdTAwMDZcdTAwMTOcmVWakCjUkp62d2DxnalLrXTRQSQy3sSgW1x1MDAxY3ez/PiSSb1o79uafcWtcVx1MDAxMIyHbLLQZcA3n1x1MDAxYc9XW1s8t4BN/buz81xu61xi+T49llx1MDAwYj9+lFXUT8K9w/aKgyYxTlxm/7mb7uvcXHUwMDA1krjiOktSQ7jBXHUwMDE3MDU0XHUwMDE48VqkXHUwMDAyV/j1ordcdTAwMTTqOJ/YwNlcdTAwMWZcdTAwMWFcXE1cdTAwMTehh/qJXHUwMDA3T+ZwXHUwMDFmXGLxXGZWodrUXFxcdTAwMTNcdTAwMTjToPLTjUgr0Fx1MDAwMVx1MDAxNrJcZo8tujneU12XbmtcdTAwMTnCk0Hur5/dbVvfgGksepv0+0rFXHUwMDFirXP3yDimaUF/1YZx/CpcXL38Vop7rVx1MDAxY+1OrjXjzG3+c9Z3XHSRQNpcdTAwMTevWiBcdTAwMWMnLIolpFx1MDAwNDBimEJ/9k/2qOXnzsFMplx1MDAxMb7OrXt1R1BqzJJxW26AX6iM3YhcdTAwMTSOclx1MDAwNlx1MDAwNZyitpOIcY9kXHUwMDFlXHRcdTAwMThK/5OSjYR6psTQnctcdTAwMTRcdTAwMDM2XHUwMDA1XHJcdTAwMDHgo0Jccnvqllx1MDAwMPbDlEY99k3kXGI4/WJcdTAwMDXUfZPmJT7ZQ3BzmSswaVRcdTAwMWbTuLduz0t/uUhofpW23DxEPeJFJGCGpPUmJOSBjlFcdG/p0vRcdTAwMTPq3GQz8t8mMMb18c6Khi3U3aXrXHUwMDA0gFx1MDAxOTcpXHUwMDE1fdp8flwilPwsJIJHPddEiVVUePhcdTAwMDJVaiBUfSlcdTAwMTe22nrDjNmDNo7FqjNrp2I/X4eqdne1NiT6ZCiGo8f26Irr8+6KXHUwMDBmy8gyIetcdTAwMGX0mZiuXHUwMDFlbLVyaC5RPmBcdTAwMDZcdTAwMWHOMlx1MDAxM/ZcdTAwMTTwSb88XHUwMDE4pmdzqblk3/CI29/q43kvXHLbXHUwMDAzwjGLX0VcdTAwMTJZd7YzgFx1MDAwM/lcdTAwMTW6X/A6T1x1MDAwZlx1MDAxZOXJ0u+cVmVWdlWuqyGXhCXtOtLuXHUwMDFis4S9qO5jhoO5qHotf1xuTrtcdTAwMTTpVclcdTAwMTlcdTAwMGLGh1x1MDAxNH98RaIzXHUwMDEzQlx1MDAxYlSiuVHYR73Pfa2XS36Xekv/ZIY+XHUwMDFiSK/U5DOoIUxmd+2Xe4uNMYD4lZFcdTAwMTMs7SeXMm684E96UaCpe8RcdTAwMDeikJCs+uOnx5NcdTAwMTJpLF28bI3HSJ2xXGKB9Ws5w9ay3FVhucGFnkpcdTAwMTDGJL9cdTAwMTkhfDtcdTAwMGXLtVx1MDAxOVx1MDAwNVxya1x1MDAwNDaq7mxcdTAwMWFccsWX67tpaid8XHREmo6WZoHiSVx1MDAxMVxiwoqJhCRcdTAwMGVcdTAwMDOYcpigpTjkXHJcdTAwMDSaZya3xFZcdTAwMTFgNIAjLoSkNVUkXdu/hf2FXHUwMDBmqL5JSulcdTAwMDDD8XZdrDqCNFx1MDAwMc749/pcdTAwMTU7t0kuM6zNMj9aNk5+c/9cdTAwMWWnPZk6iYH6J4MoPlx1MDAwMlidYa1LtcOTR+ptXHUwMDBicNL6/S0pT9WDMaKCrPnFptb0XHUwMDE5lZFSr9l2XFynzqVcdTAwMTNYvnk/XVIlRVx1MDAwMU93wuftoZVcdTAwMGXtiW0sXHUwMDE11OeK+LvTUaIyXHUwMDA0W45v/orq+lx1MDAxNa2jm/Fni1x1MDAxYoasm6xLvyFcdTAwMDaqf+c73OBcXGyvRs+CPvw8Q59q/DtfrD4oVZzFN0PZMFx1MDAxYa1cYnxjWrFcZlx1MDAwZU5cdTAwMWPx7faqUl16fH9jrPRcdTAwMTbnyeVcdTAwMTLsI9aZwTQg0e+LqrNcdTAwMTcmXCKlwaR4YGNqx1x1MDAwMjpcdTAwMTXr7OfdXHUwMDEx70o94/VeKMnPXHUwMDAwITJpsVx1MDAwZlZlXGKLtqdJ2D5cdTAwMDKCaS6BSoc80DpcdTAwMDWv21x1MDAwMfH8XHUwMDE00PhE6TpTTlx1MDAxMe1NXGZTv9qp5a1cdTAwMWKa021cdTAwMTVw2WTEgYB5ozyT01xyVKZB6qdT88BhXVxihzxM642St3FcdTAwMDdcdTAwMTBTz0WNbrliYXzjRbuZoVx1MDAxYilcYpVcdTAwMTfiXHUwMDA3MNVcdTAwMDNPXHUwMDAxilmidU+1zareb2KqRXOxb1x1MDAxMXSIMoJyOGztXHUwMDA18uIvkmN7SjqUhCnCdbNXabWZuqJtiVx1MDAxOFx1MDAxNMf3XGJU/U/dLcanXHUwMDFkovny8uzzupB6xbjIXHUwMDAx5yqziYVkXHUwMDE3RVx1MDAxNY9cdTAwMWLy9YBcIk3xYsGPUoK8yblho1xcmVxy2l/b6LVyIaH0mY3Tp6/DXHUwMDFhumhcdTAwMWJIpcRYZV3TSVx1MDAwNJsjho+XeKklslx1MDAwNK3/1fGn+uZeYVx1MDAxNlWHSYT6fpZ3sptd9YRx4UKyZYNBnFx1MDAwMVxyXHUwMDA1XHUwMDFjqVx1MDAxM4dcdTAwMTilJLM4OVloiddcdTAwMWVcdTAwMWJCjSyjNNBvllx1MDAxM9yZXHUwMDEzmkNCadWWYjNZg6FcdTAwMWEjXHUwMDExW1x1MDAxNFxcXHUwMDBlxTvC29RJs0TwXHUwMDE5US5cdCFcdTAwMWRcdTAwMGa5O2jgMWC0q91cdTAwMDdcdN1cdTAwMDNcdTAwMDaQ7ty9QL9llKtcdTAwMTLT00XRUkxnuCxcdTAwMDZHXHUwMDA1XGb3vFx1MDAxYmtDyVx1MDAxNqi4XHJcdTAwMDZWXHUwMDEwmNO4KjhcdTAwMWScq1x1MDAwMo43Mvt7zlx1MDAwZVx1MDAwNUdidG2mn0TVsHryX+mDXGJ2/UNccq2ec+Jo0nFmrDTWQ1x1MDAwZkXun23aXHUwMDBl99KFXHUwMDA3vFx1MDAwN3JKrCnnIzbji1x1MDAxZs94pS5cdTAwMDJBXHKlXHUwMDA0XG713LOa931BXHUwMDA0XHUwMDE0vsPQdpSakFuKdmHT4dA8Quz2bfS3hVxyKke9scDTPJV8bVc5hY57XyncxKWie856XHUwMDEylDTeoC9GfFx1MDAxOF+jK55gXHUwMDAzs6GbzlN2jIe7UH9cdChLXGKdxdh3XHUwMDAwzMftLFx1MDAwZlx1MDAwNFxiibJcXFx1MDAxNZoyoe/P3dlcdTAwMGX/bKepfLf8gSyHK8v210tWUYqdbqdkjYYgXHUwMDA3XHUwMDFiy3OBYWzue6NZI55Rrs+WdsJkjGY7POFeQ1x1MDAwMo6Vk2WIzCRA7Hy0YCG7naIjWuHcl3S17eRBbEyA5E5cXF2bTyZ+iMc1lT1cdTAwMTBcdTAwMDZPXHUwMDE1tvk5ZckpbMyPjepD1/3tl1x1MDAxM25U4JIoXHUwMDA2MOHphbW9hlx1MDAwMG8/X5iXzPSv4WL7fPt39uR3XHUwMDE0Z47qJVx1MDAwNSo7+6dndGnqZeBcdTAwMTdcdTAwMWTdh0F+k/jaMK+kN1x1MDAxMTd7XHUwMDA2XCKk1josi5B1fpJuYui//PF3OHv7/y1R3eXy/UlyppxaLUDeXHUwMDAyTKosS6v2+rpNafQwumKPkuJfY1x1MDAxML+CXHUwMDE475CchLJJ9c+M6ejeY8ZcdTAwMGZA4kbfhs9qaGJ0XHUwMDE0y+/97FvHPZ6LwMt0fjDZTdXuOpFlMnu6SnmQVyZhRZJcdTAwMGXhdMC48obhasyYZNptLslFXHUwMDFitZCMdnncZrUx17pcdTAwMDGUm3Zofzq3XYfk9iDkpf9VrelnXro70aErV0dcIjFOKJSUQfvQkPBNp/lq42e4LfFtzd8lbYbGUfyvtv1YiFVrnv52LlxyPUjUXCJcdTAwMWWNWXbcsNdcdTAwMTl1TikpXHUwMDA14+9t4jq+Zu1Clpw7w7U2Olx1MDAwNreWqftKeVx1MDAwNlx1MDAwMfovRGI9bPpcXJsrXHUwMDExKlwi2UncOmwu24u1aEm59lc/OFx1MDAxZi/Th8GBuyouK3BMjNqQSjPxXHUwMDExetCoT3Mj7X+sOlxuXHUwMDE0Uv9cdTAwMTB0XHUwMDEwXHUwMDE2XHUwMDE5fUstQctjXl8oX8T1fMBSJc06RFaOqH5WwFxivaFcdTAwMDPej3fMoLrEXHUwMDBm+f09XCIt/fpU0JnBXHUwMDAwXHUwMDA2lLJlRYm1xkVU53JcdTAwMTi539jkUPutXHUwMDBmQ3JcdTAwMWWSmC7gjHFcdTAwMWFpv4yGJCk7e9q5XHUwMDFm6SpcdTAwMGZcdTAwMTNUMejIw+5cdTAwMDcrXHUwMDA3n1x1MDAwM1x1MDAwZmFX/d1cdTAwMTFcdTAwMTPYTFx1MDAwZfPniTT7k+Liq2J4LNDrXHLuf3kzLOIvp2FcdTAwMTTlyuXeXGZcdTAwMTL5fKx+h3OhiKA0IaeEXHUwMDE0N0WG5iXkbstn/lx1MDAxNlx1MDAxMf6eo8xcZlx1MDAxOaaYVr9GvbNL4ov3OPPNiZ0gQ3vq/VifhJBcdTAwMDJn2Dni/u9cdTAwMTlF61HBZmvx0P/r4LJQODznWbzTXCKDP1xioVxuv+TnzSRwXGbe/UZcdTAwMTVqXHUwMDA3zKzXmlx1MDAxY1x1MDAxZdRcIlx1MDAxZSf1xs83lHOgaLvi9ncoalOL2yBcblx0pIqCt7I7XHUwMDFhREde+fnKXHUwMDAyfzRQ1yXnmlx1MDAxNi7/XHUwMDBiXHUwMDE4qoBcdTAwMTZ/XHUwMDFjgl9AgS/C8E5cZqXK1qXZTrxS5nVcdTAwMTBcdTAwMTi83mJcdTAwMTI9gpR8pJIsXHUwMDAzJW76IYNcdTAwMTHOXco/SnRFyKtcdTAwMDZcdTAwMDXPradURDx1dvl5Sfmf+yHf1z7dIY+yL1RcdTAwMWRcdTAwMGKsPFx1MDAxODtM8o6+rXkov8Dwfu/iyfFR7P+vV2LfXHUwMDE0epx1WLC4/V1cdTAwMDE6dLNcXK1XXHUwMDA0ryc0XHUwMDFlsMwwtFx1MDAwMsImj2OfsFx1MDAwNoTey1x1MDAxN5pbRlCLcX/y43ic5Vx1MDAwZocgmSrrz1x1MDAxM1xiQirfsYJAe1x1MDAwNLzUesMk/FaWuFx1MDAxYmtcbrLVmIXaxX5cdTAwMDVoXHUwMDFmbCNcdTAwMTbHUPOIr/9pifUuLPo3eNnWv6wjL5FcdTAwMGKxfuBJKE5cYlx1MDAwMkj5edFcdTAwMWS8Mejm1unMdGB6ffEjlPK9vN78fcu1XHUwMDE0PDSvq3249WdcdTAwMTnXXHUwMDFiYV1wNyHASlx1MDAxNZP+lI8+WbG6dFxin2aupjxdp+TbXHUwMDA1X1x1MDAxZCc6U7bhXHUwMDBlXHUwMDE2i41cdJ0l/Ettay9R01x1MDAxYnPQ/oY+5YdcdTAwMTNgXHUwMDA1O0sk+uhJjlxyV45cInw5pJv4XHUwMDBiXHUwMDAyYc85SL1oikT53Ns+ILM4qmhDsaDmjP51O2W0PELG5MvaXHUwMDFi7kF/S1BQOYd66Z+/PTq1dkapI1x1MDAwMHN248l0w20zVP/Jq6TYv1MxXHUwMDFjPkpFz3I2KEVzxPnwf+c5jF/ygZJcdTAwMDchy3hTry5cdTAwMTFcdTAwMTTMhkK/Z8ThXHLmXHUwMDFlh6FTvkqJmFx1MDAwMHOvU1x1MDAxNzKhXCKPp0B1mn+X31Kkaiw0ms7OXHUwMDA308iZno6hQ7pW6Lmqbu23XHUwMDAyhrGFRFLmKXk9UFx0YDaMZ6x5q1FF6o+C31x1MDAxYc6SkajSckI9bFa+1Fx1MDAxY77J6sk9Slx1MDAxZFx1MDAwN1J8PsLeRV09t+qcltpI9lpGyjFcdTAwMWVcdTAwMDC/Z1I4XHUwMDFkN0flXHUwMDE32vFcdTAwMWR8sjxTqqPoq2VfPMKXhvYgXHUwMDEw9kOFmFx1MDAxNlxuyKnOXG6jhzBcYtVNss24NFxifXtcdTAwMDOBn7iS3137XGaP0t5rXGZcdTAwMDHlS5DzruHCgNbcNa1xlPpptXOi8e+3KVx1MDAwNsskKlxm/0UqtIH4VNNcdTAwMDbwuFg9XHUwMDEwXHUwMDA30cWf69m+XG5cdTAwMDJv14WpxiyiXHUwMDEwwzwyXHUwMDAxYjtZ3UrFO5VyOjhrXHUwMDBiXHUwMDA2+VxcXG7w/njBxeagPFxugNhNXHUwMDAxy7/I/1x1MDAxMj+C3Fx1MDAxMLnp3qrJqZ80PtaYXHUwMDAzR9T/bGN89Y26tFx1MDAxMsXplyyWYlx1MDAwMKukbl1gXHUwMDBi0VlcYq1cdTAwMTfVIGwlkyrq5vD1XHUwMDEzXHUwMDBmXHUwMDFku9hzqlx1MDAxZo7l61x1MDAxMXRcdTAwMTL3ei9cdTAwMDNwXHUwMDE1d6fFYFtcYsJLMCFcdTAwMWUmXHUwMDBmkH88JYll3zJcdTAwMDBcdTAwMTj5tvb6jfNSmjnvMFx1MDAwNmThy1x1MDAxOP+ul7argfaR1DNcdTAwMTY7RFx1MDAxOV/7zfk1XVx1MDAxN1nWkCjQPaXoZVdY+fT96rlcdTAwMTSEVa45M5E5mvVKvrffLepMzG20IVbbzuFccsObZ3FKQNND1dpGxKS2OUhcdTAwMDJZnr6UXHUwMDE5XHUwMDEw24r+9Zjxcy1+gXNI8F3uabuWK6y7XHUwMDE2VKm6XHUwMDAxU1x1MDAxM30gzFx1MDAwNZ8jwsFERIEhjibHWXmdXHUwMDBi+N9cdTAwMDRZerw52aeRoS3srjvlPlx1MDAwYrjJdZLZTDKSjOmB6NHhrPRkkCzH6ka/xKdcdTAwMThcdTAwMTSutFx1MDAxZoD6lHZPVS1CnEZcdTAwMDPfQdOsI7V5XHUwMDFhXHUwMDAwXHUwMDExJTqacUvokbBcbvZcdTAwMTYp61x1MDAwZZrfJ6+BLFx1MDAxNoeqMJpcdTAwMTFFTlx1MDAxM8I5lFx1MDAxM1dcdTAwMDFcZn59XHUwMDAxWj15yN7xt/PX3l3dTFx1MDAxOdBbtVmvXcTzqVxiXHUwMDA396CGU8gyMVx1MDAxNt6XWaZfW4rq+TiGNqRcdTAwMTahQ1a5aXOr0Vx1MDAwZVx1MDAxN90gz/0jM6VMXHUwMDE3hnTN2sSC5Fx1MDAwMmZodsfPl4RQkziQZTBcbuSzZFx1MDAwM1x1MDAxMDnH5+Zu851i9lx1MDAxYvXN9suLvfxccmxv6tDkVIb4Y3tnwKDN6lx1MDAxZfSskn2pxdr6/Fx1MDAxMmhDe1CKXHUwMDEzXFyCKWmTyjTKyf77k+j3IyVcdTAwMTI+MDTrXHUwMDA24LI0wlx1MDAwM/b9Rcq1XHUwMDAw1mStg+jlNkdcdTAwMTGke15cdTAwMTVcdTAwMThbTlx1MDAxNP9TMsBj+YpcdTAwMDdpXHUwMDFjjZaUXHJSUNjQlGGCXHUwMDE4PT9xR9N4ZTdcdTAwMWG98pv+xdW48ydPXHUwMDFhv/U+eFx1MDAxOMFm1G2HRFx1MDAwMFx1MDAwN8dMbYbAZSz1XHUwMDEyZ+J5Vlx1MDAwYt4/XFz8tlx1MDAxN19DSmfAV092Xto0W4SSPGA+z/pBcek5YIdcIq+cQ8NVRVx1MDAxNZhoQNW4dylcdTAwMTOf8t+c73+OtcxcXPtTXCKf10lKQ1x0glgw/Vx1MDAxYztMXHUwMDFmQT3vuVx1MDAwMnVccqpiQOZiXHJcdTAwMTDAPsBMhr/wXHUwMDE4ULS5NevfXHUwMDAyh3xWPIuxwUbSavqjnlx1MDAxZq0/XHUwMDE42nA82/R3jnu1mvKIZVx1MDAwMGaKms1DXHUwMDEye4I8XG5Ib+uZv/lIcCE9skTkWKzx8YZ1rbqBvzRT+XX2Q7VcdTAwMDNXfFx1MDAxZFdg2cW2sv5tmD1wlqW2htabXHUwMDE1+Fx1MDAxZM3OxvJcdTAwMWOt+aVxiqt0JrtkwFx1MDAxZfLSKYdFfIaWbLVYaVTfXHUwMDE4Rtj1RyyjIFx1MDAxYZtWXHUwMDA0NPqMXHUwMDAzniDIxYmsQ0NcdTAwMTRcdTAwMWT4suv3XHUwMDBirjplktAsUzNvMEqwoY9L8Fo35dvHZkS5hFx1MDAxYcRqXHUwMDFl6Tc4XHUwMDAwglx1MDAxMYCmVsqdaWvqo9qOXFzQ755ojEpcdTAwMTjGXHUwMDE0UVU20onwxz7O85J8poHpx/RQbmrXenwrjFx1MDAxNYI/tC1Ydu1Of1x1MDAwYmxcdTAwMDAtWOJcdTAwMTSyfVpzjT+RqKBE/W5cIl304iM+7YdvREVcdTAwMTXkoKTkuP1cdTAwMTdqi/z9XHUwMDA2pq6A/Vx1MDAxZETGZ4SWx6455KVZfjLZySPHT8Kw8jRcXKhHQeVnePrRmKlE45pAJz1cdTAwMTgskC9J6Fxm6Fx1MDAxM4lLK8PJ1ucs8yVcdTAwMDLHLPJcdTAwMDHzV0VxLTvM58g5l/tcdTAwMTCXylxmbVJcdIhDQ9flwMDDXHUwMDEwtC5h+TOZr75cYm/VazrkzS3INaWp7blAjoKchXH/s8jnaUtuSz/4dn9cbufg9YokvJPwe1x1MDAwMlx1MDAwMFx1MDAwN2tDo4m80vFD8WWZ/CpcdTAwMThxXHUwMDE5qVx1MDAxY16Ux4pcdTAwMWbXS/EgweHOvm1BTZhcbiSoPvwuI9dFtFx0j95VylxyW71ZXHUwMDFk0j5k5pJKMlx1MDAxMVBYmKaNttXJTatBqvLiXHUwMDFhlObE21x1MDAwM5wvvPex++qzW3Hx/lx1MDAwZYnFtYBcdTAwMDdh+9L8XHUwMDAwfVx1MDAwZfeOmZxJcT9KXHUwMDFmXHUwMDA3LVx1MDAwNvalR6RcdTAwMTVcdTAwMTWQS+CsWC9fcFx1MDAxMlx1MDAwMztf0GLUY2X8XGLF3zXAyTN5J0tXXG5Ww5mPZvjLoMKT8V6syFx1MDAwMYCYtFj6e+fqonNbXHUwMDA1iWVXXHUwMDFk5nRJ2yUvOnRcblx1MDAwM/Vlolx1MDAwN6zpfo0wiFAml1ex21ufXHUwMDAwdZ1+LSHiLsumwKg5rlWNa5nVdPQpyd1cXFx1MDAwNK1cdTAwMTJcdTAwMTGmxZZprNhcdTAwMTSz+lx1MDAxMkqQ6XGMLl1FL1xu5Nh9YFx1MDAxM9KAo4J+hMBpXHUwMDFhWfdW5U7rQzvwjog8XHUwMDAyoVx1MDAwZjGgkTJqRCa1yUGYXHUwMDEyN7bRXHUwMDE4UIDwrr6WXHUwMDFhXzCY2/RcYolcdTAwMTKK0oOfloLtxytcYjNcdTAwMGWlvPgwK7CDrf2Yy8Tu3lx1MDAxM5XUx9p6OsPDmMyG2n06LFx1MDAxZDBhy7xpaEr1uJv3ql9cZv7GZtOfjbBcXEDjoJZlTIxHkLO7jIsx2SNzylx1MDAwN1x1MDAxMkyCUqpg4u1WoI8uyvNcdTAwMTi1e6heJlk1QkKV+4l32jpNXGbmRzm8T/gzPif5s7aQUUbezYDa/bwjXHUwMDE5pvtcdTAwMDdQ+2ZAiYzDoUOJ+YilXHUwMDFinsb+9oT9XHUwMDAyW4Pe8lwiXHUwMDEwSfZdw7hcdTAwMWGlzbft2372YlveeC/U9VmYlqSpQdxcdIxIw2TCXG6u9Grw3zmSilpWqHVzbGFq8Jd9XHUwMDBiK3nYXG5cdTAwMDNcXFrGQdXUP16Rhe1cdTAwMTPrZ1x1MDAxMEpTcfCfiOk29OXTxiCyeVx1MDAwZVx1MDAwNmmY72Ctr1Q3rlx1MDAwNdiAg/hersv9TElcdTAwMDFExPR1VtuEXHUwMDA1gWmbzsPxdL7WgnxduWOk13RcdTAwMWIxhlBe9EDrXHUwMDFkeyf8vK5NIFxm+luUmWm0/OJcdHn/XHUwMDFh39PdqVxyh0mdN4TcXHUwMDA0LPtcdTAwMDNssfe76eM8cVQhXHUwMDFhmtmlZ1x1MDAwMd+YNWtUnPGitVxmsqIt8TT71oKu5CPm1k0msU+YXHUwMDFm19Hg5Wxa7G9b1+j6u1x1MDAxZfsksUNYzHZKXHUwMDFk/3Wh1Fx1MDAxNJxv60RGrVx1MDAwN5TJXHUwMDA3SlOtW/DKhV+bR1x1MDAxYdaEuFfz4GnVySwk6d6yLdEy5b0+mfI4k2yqMUFpzUNZz8Q+oyXjgHa/rHZcblx1MDAwMNWn55ikQiBFxlx1MDAxOFx1MDAxONAp9ed2YFOaJD6reyg+8fZu/cohwDjcQfGbXHUwMDAxO1x1MDAwYv32uKBribrTXHUwMDFhXHUwMDAwa3QxRTku2LOWMKpcdTAwMDLI3e7KZb6/Z4xWXHUwMDFlK4CKw5+/lolYsaZd2JLlXHUwMDE0fKi/zXI0YLWh/cUziW3yPbO90NXLNKDBdbV9rjTDbKR+QHEyfUZcdTAwMTdcdTAwMWa4XGLlt5lcckNcdTAwMWZxRbU5mFxuXHUwMDAyuaWrmq1VcqJoXHUwMDE1XCI835dTXG40q+N8tkOxr21Vu1x1MDAwZbhcdTAwMDF2ZZFbZbzk81x1MDAwNz/VYLh4sv7WsU3kL/F5jLmSj5KiSVQ+ML9cdTAwMWFNXHUwMDEyiK5K1Y/XmW2p6Kpm/HGVRlxmQMPeXHUwMDA1bFxcnK1OdFx1MDAxNlx1MDAwM6lcdTAwMDFcdTAwMDGsjsj0XHUwMDEy5q9cblx1MDAxY8RbNb1HxCrNXHLsKXJ3flxmZD28UNqthlF5nSyabO3p2zFcdTAwMTgvRlNcdTAwMTmM7UlotFx1MDAxNYu8PaeKP+JFe1N2gkqjh1uGTZFV97b0W+pyVOnjpfGof1wifPfEwFA5drNcdTAwMTNy+HNcdTAwMDSez+ui6k414mVZpFjD9pQshiM6gFx1MDAxYVxiWmBcblx0OewtXHUwMDAw9O1cdTAwMThcItNOI+TAP4PtPmUo9+GxLWpGXHUwMDEwWfFrM/pcdTAwMTQzZVx1MDAxZVNcdTAwMDWD5UuEn3qU33/fwzvhY0E/yFB6XHUwMDFiTdIgJFhp21x1MDAxZaY4plxmnkhcdTAwMWbq+LuXwD3KKeNzlUZaM9debWdHRmf3ut1j+CFJobXOa05ZXHUwMDFkXHUwMDFkiG+it6yPYfWFfazTqTVe1EjxmvJFXGL9XHUwMDFhdaverMwzuvWOXHUwMDE1QXfu7bc7N58z/bK48OHPzONcdTAwMDPQLDy9fsjlJ0/kN4lTJ5B5+y5cdTAwMTBcdTAwMThLO9bxqm1Y9Ub46YhcdTAwMDBcdTAwMTf2tvdcdTAwMGJVaKBv2Fx06GOqbtWGztNcdTAwMWXUXHUwMDBmt4391lx1MDAxN1BcdTAwMDLGjJRek1x1MDAwN1x1MDAxNZzmUyCveOWEX1x1MDAxZPs0LSVJKFhcdTAwMGL89/yhWkM0iZaWPDdcdTAwMWFIWIba4DtTXCJcbkT2+Vx1MDAxZlx1MDAxNZjIiJZcdTAwMDVcdTAwMTVth26rXlxiyafh+1x0zoioLZJN04BcdTAwMDG2NiRJO8pcdTAwMDPuNUmO+rGTWl2plE/MXHUwMDAww+FcdTAwMTXnWChcdTAwMWGKunzq/qhyhuXIPdgwb/9omOB1Ocyx8IekOWuh5nTkf2c2rKcjSd2UXHUwMDFjtqDpXHUwMDBizf/It0W720zmLDFcdTAwMDZcdTAwMDdFoYddoVx1MDAxNyNnXHUwMDAys1x1MDAxZiT61Fx1MDAxZt6bxlx1MDAxY61cdTAwMThcdTAwMThcdTAwMDSGQNOl39+elEZSaevPXHUwMDA2b6GwrY8uc1xcxVx1MDAxYjxcIiCBXCJlcKCkXHUwMDAwulxin5Hy8lx1MDAxZKBRptrxhHVcdTAwMWHeSUCZ81x1MDAxNWf5rTBcdTAwMGUrOk68vIk3kvtjmJFcbmos46O9qTamXHUwMDFh9Vx1MDAwNPJA6kBcdTAwMTVSXHUwMDEyk63EdZqTvVx1MDAwN6ltXGbZPbiaLEFcdTAwMWXR/Vx1MDAxYvjbLlg49eNzY5ZcdTAwMWRcdTAwMTL0XHUwMDA3XHUwMDAxUbIofPnl+1x1MDAxMeJVNEgqby4/mk5HcKpX8t3TxlTxXHUwMDE2X2CMXHUwMDEzXG7A+Vx1MDAxYe2sZ5teXCKPu2aEUtJbS6S92LhXP4POhcGFMNHS9lx1MDAwNnXjP1Z/KiorsVwi5d+uv5vPr1xyM9QlXHKep0XqJ3hcdTAwMWNl8dMuMmK40njrK0vtOVx1MDAwMlxifno1xKaksEdL9JnbtUjW4Fi3QrpZe3OFM4P+IcX290hDamzKcCPjXHUwMDE4paWiiV1ui1xcXHUwMDA05IFEyNMtN1CEp179ocM20lx1MDAxM/BcdTAwMTTsVvWgy1x1MDAxODaAM1xiXCL+gVOeTt6lXHUwMDE2/ONh34BcdTAwMGZcdTAwMTnbKV1cdTAwMDe2hVx1MDAwNas8m1x1MDAxOeveXFxHYJwszYSfhKXn2Fx1MDAwZlNmYSa1TzwmjWD2Q9ZcdTAwMDZ6R1x1MDAxOYrQ3JupNFx1MDAwMjTtl6PDs+7VXHUwMDA3wPhJ2Yf33elcdTAwMDXphkPzlFx1MDAwNMpjI1x1MDAxYTpcdTAwMTFkm4bVXVfK4SbCrXAkwffQXHUwMDE0qzI5gTlIj0t+s+Y3Va+h0fD1RNOjOVx1MDAwNKxeX4hnl1x0gkVJ9L/OXHUwMDE28WgmtuJcdTAwMDNcdTAwMTDY35ZV5HeLapiJMVnX2I03mlx1MDAxNFwiXoTNLlLiXCJEXHS4SXFcdTAwMDQo1IJP76zFXHUwMDFkzO2psWFcZq083Vx1MDAwNjRcdTAwMWL8XHUwMDE2zVxctPJcYrZcdTAwMDLU75WaXHUwMDEy6uzpXHUwMDExk2V2XFxhUIho8pxNYNpcYkggJGS4i++PvlAj+Fx1MDAxObdcdTAwMDRhf/cupU11XHUwMDBiN/SHcUbRmptcdTAwMDBcdTAwMDDE0Vx1MDAxYaq67ZxXc2bHZcy+fPZcdTAwMTlcdTAwMTjlxlKWJos8PFx1MDAxMzFMXCKo1lx1MDAxOY+D2Iz1XHUwMDEzTM8nzGbceuTKPVx1MDAwZWj6u5dcdTAwMDP/miNcdTAwMWKI9LFA4tKq81wiWU1cbngpjHorXHUwMDAylT1efL+KLXM8LYDvV1x1MDAwYobGXHUwMDFikSma8C3hvUagY0C6V7ZNMVxmk5J4VUtcdTAwMTFVmELdOI1eXHUwMDAySVV+XHUwMDA0XHUwMDBi44JeXHUwMDBlQiVcdCWryf/WK3hgXGZcdTAwMTJikdR++TlcZmLekd4gt91QXGJaXHUwMDAxc42LbauAO5tRs0NhcGupP9LRq1x1MDAxZryCXHUwMDAxyfhcdTAwMDQjLfPKXHUwMDEw/91nxfhlXHUwMDFmmVx0z7HvXFyqxWqPneLtw4UvK6XRvG84a2myXHUwMDAw95xVqFmQ0lwiKofISJg+qHSb3cxOuL0maVx1MDAxOSybXGbvq1x1MDAxMUdz0Db4Q8ecxC02M/PPiWw8ol51fd2j5Fx1MDAwNvRcblx1MDAwMFx1MDAxYa966CdD+1Z6iXFccvbTTFx1MDAwYj1cdTAwMDQpeS7BXG6d3KmkTVx1MDAxYbRcdTAwMWTOjrdcdTAwMTfe21x1MDAwN5vwOdbqPiO9bHvrRqnnouPGWVx1MDAwMefhsVx1MDAxMWdu6u3E9Nhj0aGJUNec8M2lSSPaz+FcdTAwMDBcdDr30PtmbEItp4SYrre+n1xmnDLZXHUwMDFmyMRVg7dt0rafXHUwMDE4LndW09y/RFx1MDAwZlx1MDAxZVx1MDAwMzyLgONcdTAwMTVQXHUwMDFk3/WZUoeNrYFe5sZ1e8JH01+Y/JRcdTAwMGXxupAqylthapzeY63T8itcdTAwMTB0XHUwMDAy1Gs3Uuk57aPw6cxho1wi4KHu31Kqm2jT/Fx1MDAwYlx1MDAwZSuspu+R9pnSxenpkU2TJ7d+mD/Y4MNqXHUwMDA3f5Woo/S1+35qL929nFx1MDAwYlx1MDAxNlx1MDAxOVx1MDAxZc1RWX1cdTAwMTAofJ0jOlx1MDAxY3PthJefe3RcdTAwMWNcdTAwMGIjMUK+kkdcdTAwMTFtXlx1MDAxMLeNLO5cdTAwMWEj4m9cclxu2Fxclpvct1x1MDAwN0Z13mYg4O7/Pc5cboWLXG7bb1dWyFx1MDAxMlx1MDAxZnZcdTAwMDS23shq//aIrclt5KSXi1x1MDAxNOqoIaG+guNoPqHuXHUwMDE3ylx1MDAwZfr+2d+3OeNcdTAwMGJcZr0jK22a7fhcdTAwMTBcXHw/+o+exmTeuFx1MDAxZVTT2CZcdTAwMTgprW9Pb2M5XHUwMDExJl/NTO9mXG5cdTAwMTfFaaGvI1x1MDAwMyhcdTAwMDUpXHUwMDFmfi5GVp7Se9XAmjdUOO5cdTAwMWVcdTAwMTmrrJclelx1MDAxM1x1MDAwModcdTAwMWPz9GVAWaWFzP1cZpZYJdBPXXfzgEb/Z6zMXHUwMDEy8LO55nyhs1/gWHSLIz6+2Clteqm6eSuA3Vx1MDAwMstccnA0Jp9cdTAwMTFcbuFRfWajgf7dlStcdTAwMDYrpVx1MDAxMpowPHh2Qlx1MDAwNYDXeolcdTAwMWJyXHUwMDExu3JTW05cdTAwMWRjiJDllqHh0yiFalJFyeAue7DQXHUwMDBlTiSIrKdB5lx1MDAwNUxJmK9cdTAwMDZcdTAwMTA340/N7eVGJfo9ZkF1LZ6LPFPfN3ZcdTAwMDdcdTAwMWJcZsnXWuvCrbO8QrPn+335zc7QXHUwMDEyZiZL/F4nKsrhq4V4941zldoxXHUwMDBipthcdTAwMGKPXtlcdTAwMGbrS4xcdTAwMTM9XHUwMDE12u9cZkuJXHUwMDE03Fxy6O2mO3dr/+ZcdTAwMDWfQGT2TuBCXHUwMDE3v5pcdJy9ZW1cdTAwMTOTXHUwMDEzQOStkrPbjmm7fFx0LVx1MDAwMJEhgyAq6+v6XHUwMDA2M0Bjf1hcciEmq4qW9bKuzkxhNM24uus7zNfI0HGVpu7rXHUwMDFmrWd+XHUwMDBmTeErhq9Uv0/lXHUwMDBi6oZcdTAwMDeQ61x1MDAxZFx1MDAwNOlPvVxukFx1MDAwNltMy9NcdTAwMDVyuIucMUNTem6F6aKko2pcdTAwMDY1hGHiLjZ6f5vE2Lv/XHUwMDFlSzHK+6hAqqCvUFXzxDiuXHUwMDE0XVx1MDAxYzgpuY4rgXqPXHUwMDEzLqFcdTAwMTBvL6XAmUz4U8eSZf9YXHKVXCKhn5OAnid686ItJvfW3Yt9SUvTm7d+VMvaf2Wdx87DzJKe93NcdTAwMTWD2dJcdTAwMDBz8o45k2JcdTAwMGU7ZjGJOVx1MDAwMr538/vP+GBcdTAwMDBcdTAwMGLQSlx1MDAwNKXurqr3fYTqJrA8k5V8cZtJj8WPcU1b1VT6XHUwMDA2eVEsXHUwMDEyKVx1MDAwMWPtX8HUn0Igtne8fkrGo5pgXHUwMDFlw8rC953Y7iv07uqOman5sOh1TFx1MDAwMP3sXHUwMDAzqMDcYVx1MDAwZSvdasojlIu/i6GhYz+Zv+1QaVx1MDAxZZnHi0zFXHUwMDE1ovF1t4Z+dJvJy/C0kO6SLMI9ftWr0s9V3pSuS6e74ic3wqtG5fO39vnSQTKC7Fx1MDAxMYuwXHUwMDE5XHUwMDEw+XpaWVx1MDAxMMZuhd7xqbZpITRTh1afL7Mhxo1hxcNgtuWrRVx1MDAwNa8pNryEpPXrXHUwMDE1IEdcdTAwMWayZFxyb2+9Njt9S/WceLOp/WefUMbjr+tXmLzyy+L5ofaWdWg2XHUwMDFjXCKa0ocjhbz0pLrkepWigIris/3aXHUwMDEzRzM7XHUwMDE4R2Dg7DAtXHUwMDA1mMxcdTAwMTNcdTAwMDIkqKaY73pmf8dcdTAwMTTgiL1cdTAwMWUkKFRdMlx1MDAwNlxcXYBaW1x1MDAwNvpcdTAwMGKOt0VcdTAwMTFcdTAwMDZcdTAwMGLDnsxoymAwXG5x+LF3qZ7qwte/dpJ8L5qSV/o3e1x1MDAwMDhKTeVcdTAwMTDcSUPxnCVX2eRcIrNcdTAwMGX9RkfcksPJZOC8aVx1MDAwMlxuQG3Xr1x1MDAwMkXhh4M/hc/LYmmeoXSVn9fCmMBUl1bxXHUwMDBl5F1cXFxyfVxy0XVcdTAwMTf6TbotarEjR0+g+fLBmZ1cblx1MDAwMt1oXHUwMDEwp9+9ep1P6nGT5MaCtDBcdTAwMTFcdTAwMThl/edcdTAwMGZX+b+9OlhWmE9w9VxuXHUwMDE3XspcdTAwMDBtZVx1MDAwNbFtzIG8yf8ohKd3umzz0kBhpfiKXG5cdTAwMGbjmj7BXHUwMDFh/7WR+yO/zlx1MDAwNZnU28FIXG5cIlxyoIS0l2/0j7OPtcOZLLGvr1GxXWm6geVcdTAwMDeLbfxd+ExAXGaYpPLvplt9g6eNZl9PRlWHLlx1MDAxNHWjJKMrt9DBqqWXpVRCXHUwMDAzgLqJkr3aUcizT6y7WaCWUHDnqli5f3azXHUwMDEyQlRu71cgXHUwMDE0XHUwMDA1XHUwMDA1o0ZcdTAwMDJcdTAwMGXKJGJtUlx1MDAwMHBcdTAwMTm04/kkXGJvuf2kR3/8K12tmFx0kYqfILk1NFxi2Xec0HJjfVumXHUwMDA30lx1MDAxODbUXHUwMDBiJ1+/XHUwMDEy3td3X/k0U3c6bePprtm/fTwtdXZcdTAwMWF5z+xSyEmgSVx1MDAwMTq9ljRi0la6ZIFNNnv7VL1n8Vx08oK6UFW/d/hccvNrRFx1MDAxZSBwXHUwMDExt2RcdTAwMTZS+l0pNFJJdlhN1Tlfwswti+w8M2V+PsB99vSttdY2XHUwMDBlxlx1MDAwNlx1MDAxNttVen6+XHK59rRcdTAwMWTFUZ0loXQ0XHTCyrewMr7Z61x1MDAxNFxiXHUwMDAyIzSMbTLqktK0Tn6hWD/QMfD0veA1odJcdTAwMDR8LCjjYkxzTEJcdTAwMDWP01x1MDAxOTS5SttcdTAwMGLTKVx1MDAwMFVu9y3163ZcdTAwMWab8PzITafuXHUwMDBlKna+6Kaiv7BcdTAwMTe9JPFcdTAwMTkwIP1kMyjkME7kUzZ8ji/UMC91vUE2Ov+cb34p89yXiKW6fkW2zFx1MDAxMkHQs5lqXHUwMDE3kVx1MDAwYjHP+Ok6aebrVtiXvGri0FjjLzCpe7QnXHUwMDE1k3ar753Nhi+5Or5fS0Q8rlSYXHUwMDE3JTMoSfNcdTAwMDcyvJ9BJTngtsbz/OCqhmB6jrWydlx1MDAwNZ6kgDZeebD6ulPptSvW3GGksV9muEct1cNcbkd6YEJFXHUwMDE1NVx1MDAwNlx1MDAxN45hXGbbP2Yqv6Jn4J3I5NLjXHUwMDE4XeOFu/J62F9cIunvXHUwMDA0dVx1MDAwMjFfh/c9R6O4bDG9Q1x1MDAxZqHUXHUwMDAxOGiAV1tqP1x1MDAwYlx1MDAxOeLj9rBbXGZsMGrLXFzbllhGW0Ho7o89XHUwMDA051x1MDAxN1x1MDAwYi2/ZImjLyHS5ZnGLVx1MDAxMeTT91R8f1JzvttcdTAwMWb+pYFcdTAwMWH/4uew/rxIskaj+42War1gXHUwMDEyyHgoNzPnljrSXHUwMDFlV4XL+7OqIGa43vZ617FcdTAwMTNROLvL7k5+hVq1XHUwMDA2ecJcdTAwMTUsinH58aiwmpC4XZ1P9Vx1MDAxYi5cdTAwMGX8fkxcbniyimRcdTAwMTI1XGK3fFwiPu30w/T1YrtcdTAwMTCo0rX426d0Ld1RRXtZh/05R8d8M6/2hVx1MDAxY+LpqTpq6LAwMcEwc7tcdTAwMDdHXHUwMDE4TVx1MDAxMO1XXHUwMDA2OXs2Y9EpTU9dztKFRmlcdTAwMTX4e1x1MDAxMvic1ihDNm7DZ5fxJ95cdTAwMDbK7lx1MDAxYXhcYu+o9Y9cdTAwMDU3UJM9rJs9U/pcckGRkJRxSM7ffyCTXCKi0CDZuP6BXHUwMDFjxP4xv1xywlx1MDAxYugjLZUpnEWPLS/5r+hcdTAwMGI+X1x1MDAwZYJcdTAwMTLiS/jZXHUwMDAwilx1MDAwMLXP619ftp1/mFx1MDAwZcpcdTAwMWGLyOhcdTAwMWMguUR9XHUwMDAyXHUwMDExo7zpzmtHwHP/+1lcdTAwMGZcdTAwMDF1gt6nuVx1MDAxY/5cdTAwMTL3miOyOnTpi+ZGT0xcdTAwMGJM6Fx1MDAxYlx1MDAxNDRcdTAwMDao1vbXcLOqTozPY7zKXHUwMDFmXHTrXHUwMDFkXG7IRlqfXHUwMDFiK7ZcInzdcrEzXHUwMDFinvjjLCfcc5hVpqlcdGCbsIVZgVx1MDAxMDBcdTAwMDTMUWh8vt4va05WVCXVaTBiZMM5XHUwMDFmfYFcdTAwMDXh1iFWSVxcOJrEsnaPjkDlXHUwMDFmilx1MDAwMFVXO15A4lx1MDAxYjI4XCLlJ0FN6FsodGJcIrq2JCXFSFx1MDAwNFx1MDAwNtJcbrAgQ3dPtlx1MDAwN3I0dl5cdTAwMTlcdTAwMDdcdTAwMTKwjOG90I1cdTAwMDPl0y2iaNQ0jNOp24pj5lx1MDAwMUTpMaNB80x2cFRcdTAwMTlVVMi194eq+Vx1MDAwZeJcdTAwMDArKuzd1F2C1HY/eVx1MDAwNfduJfFPo1i983FcdTAwMWOmXFxcdTAwMDHfXGaKXHUwMDEyo7QmQpbz9sFAXHUwMDE27rr0M4uiXHUwMDA1eNi48lx1MDAxMVx1MDAxMFx1MDAxOCPe4To1qiqCp0NtXHUwMDE0Vlxy6VxiU2Rm7Vx1MDAxNUKdXHUwMDE3XHUwMDFkwC5M+0IrXFxLy2Wb3lx1MDAxYr5hklYz6WtcdTAwMTcz8a3MnyVYrqbuzt9ZYb+XXCJcdTAwMDCy31x1MDAwNmL3YDxVNz0mqSOJvlx1MDAxM0hxYj6kyXzpUpOaz1uX572JlVx1MDAwNCTxMIi3Mvvqufg9QlqE/es4Nl5M0n1GirsjbFxm589cbs2EtfqsNEuV/NDXXHUwMDFmgprRXGL4oGSwv+b6jWigR1x1MDAwNVx1MDAxMqPZ6Gtcciqn3q7lXHUwMDE5XFyDx/RcdTAwMWLRi1eCXHUwMDFh6dZcdTAwMTJYXHRcYtWQd+dcdTAwMTAqpaTaqLnQ2dzTa7c5lFx1MDAwNFlMjz9jlOfvOPTxqDlgbaKXiMOSmsW+kd2/3pJ5v1x1MDAxMZpcdTAwMTT6qVx1MDAwYllB/WvgkvdBXHUwMDE1t8wo9InSmfLc10dccts1N1x1MDAxMjubLixL/DdHrFx1MDAxZqJ4KPt9PZhsvKxwXHUwMDBiXHUwMDEzvM6WIFx1MDAwN5u+/fbLmN2kdcnKeczJN0JcdTAwMDFcZo07/17TWzOAP7yjijaV45WA9Ex343dRwFx1MDAxNDPiavLR+DBRMVxmylvmd6L4aj1cdTAwMTK/tGPPUvJcdTAwMTXUrJTcO4x/XU7t6pGPo2ShQ1x1MDAxYdDjsJe/XHUwMDEyl7ZGb35AXG6Aok06X1x1MDAxZrO4PZXF8vCgwOyuSlxmcTS3jXRcdTAwMTm3Y+XMifrj40AjqVuvLrA8jGZWqXH4pS3L/VxcXHUwMDE4kqWulle2XHUwMDFlXHUwMDEwkHUmXHUwMDEyrF2qTuMgppdsPN+Swd9cdTAwMWPuwL5cdTAwMDN8v0jLPrvdpzWJu8Qxfz+b6GPlgLqPpK1ccucp/C5kjWhJgu9cdTAwMDXJz0CMj320flI7N/FCLXfvXHUwMDA1OtnR/pHuSVx1MDAxOVx1MDAwMj4k9tDxXHUwMDE3y3JKXHUwMDE2x59cdTAwMTRzXHUwMDA0M+h6LVAuw1NcdTAwMGXx26nmaKdP2cIlzFx1MDAwN7FcdTAwMTGl1JytKdFMKcy5if7Je0dlNY2phfj1Mrp9yOFGglx1MDAxNZ7gzXcvctZieab40ZfdsiyWxXJweb1cci/t97qe5aafh/iYUVvB3oujdsZjRrVWw0zSP1xcXHUwMDFj/+nR3N1cdTAwMGWB5PR0XFxqXHUwMDFlUCQuXGZEhdPQnC2R7jEknshcdTAwMWVtVm0kMj48XHUwMDFiVdc/6teU8bx8sulcdTAwMTnXw1x1MDAwMFx1MDAxYWK2tsIl1m7BaVx1MDAwZlx1MDAxZn45/SpGXHRcdTAwMWXkrmc+oqsy4W9cdTAwMTErjLjvkN/i19nnybyoRLitlGGRj8wk+PlAn8uUrp1pmeBUXHUwMDAwiFx1MDAwNlx1MDAwNHOh+q7qr1x1MDAwMLbXh8aHlp96oKN+WThQXHUwMDE4h4+nKEWGoyZOd7+K7JXZX+fiXHUwMDBl/lJPZzNaOVx1MDAwYjfMXHUwMDEwUGva1oXNS3e+Y0u3oDxDbym3r9bAX5FcdTAwMDDuXHUwMDEx9lx1MDAxZXrTKvHodlFcdFU8svyLkzPBr4RXJN9cIlVnhf9s25J8vvOatKjijLZcdTAwMGI6R3bdJ4xFi2QtR9VcdTAwMTfxoE37L/9Naft8Scivbs2ZXlOdmtFb21GbqELY+obSdZp0MVfkdU57MqGzyp2EwFx1MDAxY/XBO1OhV7WYM1x1MDAxNqs6IMaik/TyvCThm1x1MDAxMPmK0Zk8hD6l4MtaiteEXHUwMDFhi3rdlPZLzLOMTouckNmBiOtcdTAwMDemgNwmbsR0r7lKuE1jhlx1MDAxNlx1MDAwNtraa2udoLXK7/dcdTAwMDevXVx1MDAxNtq7bmhIbFu/cCAvPa31+Vx1MDAwZknhXHUwMDAx3T0tI45f5oZVrDD1aFxu6Vx1MDAwNPO8XHUwMDFkomZdb3Sb7JFff9jj1LGCaNzQQkNa6O1eXHUwMDAzNipcdTAwMThcdTAwMDborU2jWedzvFxmhV1S6maVXHI6jl1OW9Tp6PnEXHUwMDFlOliURzif7lx1MDAwNlx1MDAwMMG7tlxiln+j0aRcdTAwMTBcZqdKcLEmXHUwMDFkzPTYu1mlZ0JWqqN6XGJcdTAwMDfOWcF38tI9ulx1MDAwM+hcdTAwMWM64FfN+9xcdTAwMTbJL8fbWJ17RzxcdTAwMDPVtGH560flXHUwMDE0Vodh/PqUqJVmzEMpQFx1MDAxY1x1MDAwYoJSVa29gnNz8144oCevuGGac1x1MDAwMVK0vWteobfrvM1AXHUwMDFiJYmTwLiCXGJEaGeP5lx1MDAxMXjDoVx1MDAxNbJXXHUwMDEyZcTKN8dfbHxcdJIpySdCoeylV1x1MDAxY8CUz6bFk5HyzaCzw1JcdTAwMWXUk64gbXNcdTAwMWTQf2drXG7AXHUwMDExmj1C+Fx1MDAxZepcdTAwMTW/oS1cdTAwMWFcdTAwMTEwplx1MDAxOTlcctVeLE5iXHUwMDFldNZcdTAwMTVFRo4kKtHVly+L3EY7+SXLWkA+rF/sKDrEnvWBXHUwMDEx7ZrDXHUwMDFjx1xmxoxfILXpXHUwMDE0WFaHXHUwMDE1v2XYqDXHSKn0XHUwMDA0cYyrNmj2fLqH91x1MDAwNCFafOBcdTAwMDaEXn6XOmjE0LSXJqNcdTAwMWVcdTAwMDZcdTAwMTFqXHUwMDE3XHUwMDAxmFx1MDAwMcJcdTAwMThYtIJUNOprgW/aul13tcE9aDSmXHUwMDA2MmzWX44vjyhuJ9WAXHUwMDA1vCuJ8nNcdTAwMTm3qWvOeehcdTAwMTRLpKugUjyldsE0XHUwMDE1JHztXHUwMDFmclx1MDAwNoBcdTAwMDNcdTAwMGIx0JB6pVx1MDAxMMyY3TNy6U1cdTAwMTNcdTAwMDM0e1x1MDAxZCzvOdjv1SPvc1x1MDAxZFx1MDAxNSVk/VM65t85fkw8MI7ANIJcdTAwMDBcdK5h8bdU6vxcdTAwMWKTn7LNjt9cckjxhHOeV602O4Mx5frTcIDXxMm/elx1MDAxNCXfXHUwMDExZZMlXHUwMDA3+ZVkZfhccjJiOVx1MDAwN+zTXHUwMDEz6mHnwFx1MDAxZWop1NJfbyGjvCaHYCAlXHUwMDE3XHUwMDFkK8ZQdZ39XHUwMDAzyEegolx1MDAxOFx1MDAwMzDm71x1MDAxY+dsyFx1MDAwMlx1MDAxY31Ye3ZX0Fx1MDAwMmv1YFx1MDAwMlx1MDAxMt6uY1ZdeURcdTAwMGJcdTAwMGWMz99y5vFKXHUwMDFm68U7/2lp+uf5iZzyd+BUXeuMllwipztvXHQ9IzhGPtO4RP7GXGKYQGnDqLHaalx1MDAxY7CaZHTJsDlXmlfnQt7fo6+916qaXHUwMDA22KdcdTAwMTIkNNDkwapccvz7d3dMbeiMXHL5vFx1MDAxN2VcblwiemDwha7yU2p7eCBcclx1MDAxYvej7KX+Op/wtzPJd1x1MDAxNU09+cQ+I4F8XCL8jE9pz1x1MDAxMU1cYlx1MDAxMIXM8Fx1MDAxOVx1MDAwNTiWNGMztf86T+i/10BiayXnkuuKM0+KyT9vgnNcdTAwMDdIXGbCXHUwMDEz8IWYSkNcdTAwMGLA3Wvi7PdSjLsq+zLlXHUwMDAwXHUwMDAzNvw54sBbXHUwMDE2Ln7EXGb7cChQZaqBsFOswNLfns//XHUwMDFlQ2t/psHtV1x1MDAxNGFF91x1MDAwMDtlXHUwMDAzXHUwMDBm5s3veVZcdTAwMTFmZyvmYHRnmtKxy4pcIoBmik6zkfNZgImXqJN/WjIsaPWDrqG54FJ8gvxNfEN7ftL/XHUwMDE4XHUwMDAzo1x1MDAxOJwrYSPXPFx1MDAxMKtcdTAwMDd+tsVI9SFOJOu/gvR9Xu/b/Ziqhnl29n6hOLHvSlx1MDAxOH9cdTAwMWT4W8ngXHUwMDFm78rRXHUwMDA3Z0EwyGSrq2Odj1SoW1x0Qb7Extdf//avjXoxxVx1MDAxY8SaoKbBbI9pvtT/JFx1MDAxNjBPdGU2XHUwMDFic9hp/t63YCR94/lcIlx1MDAxZruK//Wcb1x1MDAxMux0XpY2XHUwMDFkjeTpRyqqRTnJ34J3T1lcdTAwMTjrziHrylx1MDAxN/HPv+br+8/+ON+3XHUwMDE41X6pzDotQL/24PNgc79cdTAwMDC0a1xmKYvNfDZaPlx1MDAwYuZFXHUwMDAy5n7yXHJcdTAwMWS5XHUwMDA1X8PgMGyv2idcdTAwMWSsd5rXuMuBjlvBRDDOYyH8osGZfDPSVVf+yz2Fley/3sNcdTAwMTeRuUkmP1x1MDAxYT43JiY8Yfipi2yH7ik6UnpcdTAwMGY/knmbuOegKDClxYSDX7VaXcmElVVv0kdtZoWawl9K6O265k1KsTlcXFW1/7V1VWtcdTAwMDOOaYOgl7bXNp31cq//XHUwMDFlI+RcdTAwMTdMoTnfI4pEVv28Yi9GS7iYPb2EMo5dW+BR7nDcWod6T12dmW3fP7R440NcdTAwMWV4MDzuwFq57KdcZodcdTAwMWSZ0edcdTAwMTNfoa77ifNxybJcdTAwMTOsXHUwMDE2XFyd+3c9tO9+XHUwMDBl7b/HqjKaY0v8z1xmXHUwMDA2Nl50LtPFpUgjfYvH3iNM4IRLKCMgKWdcdTAwMDemf3b33GfDXG5cdJ3iZo1cdTAwMWGb18qa5lbbnfhF5mSHyFx1MDAxMFxyurZllmTamrVvvibCn5SrZP629y7HdMv/i1x1MDAxZO6fr285+a1q10dcdTAwMGY3Tur8b1x1MDAwMVx1MDAxN1ZcdTAwMTPMIH33JVx1MDAxZY9y8KpvXHUwMDFm5VGbQSr0OVntc2Xdqlxu5FfWrFC1ntjTXHUwMDBiPnhcdTAwMTFcdTAwMTWbxNy+y1qQSdBcdTAwMTiySOeOO3SvXHUwMDAxOsOBJL9v1Vx1MDAwN56QXHUwMDFhXHUwMDBmh+XL4MFD7F/19p81f9PGNvc0k7JmKIpugfZNg5E9Oza8TsNcdJc3hkLGcyhcdTAwMTM+SCnZZ2U2nZ1cIoLcXHUwMDE1tInY+1BykEFWzUFIXG7RXctcdTAwMWbiy5rUi1x1MDAxZInYVcu6YVx1MDAxNGqVQYoqilxyyUDAKfXf2VvuW4olRqL9Z/Sv/Lp9YolcdTAwMThcYoVTXHUwMDAxzeBjRvRcdTAwMWZowlwiT01+XHUwMDBmm4HKXHUwMDAwsmlCXHUwMDA0uc6dhs2006+lXHUwMDAwn6FLMNqkXHUwMDAxr59cdTAwMWbV2umeOJ9Tw6Lvd1x1MDAxZFx1MDAwMt3Iy0FT3cQxglx1MDAxN5hcdTAwMTI/hTyIMi1v+9uL8U+t4Nia5d3x09BcdTAwMTQ5pGbCofk7QTOZJ/0sgujrYaOwXHUwMDBiovz+W+9wSLOcXHUwMDBmrVx1MDAxNKx076VHXHUwMDAxIT1tezyDlVx1MDAxMVx1MDAwNFNhce89/8tTb1xigMyAY9BcdTAwMTM6d9I0PsKFydJyXG481kbJfVx1MDAxNPnferQxusBxZoJcdTAwMDO/oDJcYlx0YDdkzoJcdTAwMWVcdTAwMDRcdTAwMWF4Jlx02FIn3fpI9clcdTAwMDEs9aDMgqOv/mVEOHdP91x1MDAxZIHjoEUhxKPCgIits1x1MDAxM2x6/aRSfsE33I5S1t3fx5GVb9LvVNxqXHUwMDFlq6WrtGPiXHStQN39W1eEmmF6OTDhlixhu7LgQFx1MDAxYcxcdTAwMTAuXHUwMDE2M12jIdiXoLf8111BXFyTeVx1MDAwN8HNXHUwMDFlqibLhlfdh/7ChEVcdTAwMDZcdTAwMTlPs+3qMEZ/+4bVn6eAXHUwMDAxklx1MDAxZbJm7a1cYmc1cdGsdXRKh+r/t1b+XHUwMDEzXHUwMDAzNKNcYqZcdTAwMWE+M31uW8XA/eeIvIjY6WiJonXParVMpU34witSulx1MDAxY71cdTAwMWZ3vNSfvjvlfK2tjlmPdvJ46VSHjJemVXlT/mBLXnPwv/iy/6XNPvv37PZXK1x1MDAxZYv91alWc3BQXdhMYsNAU3zFrKJwoWI6e1x1MDAxNr2Jf17g5iveYn5MV0sycbtJLPzMuvVXMbh7R79cdTAwMDP83/d+xzIy1HfU+lx1MDAxZFx1MDAxMOdcZlx1MDAwYpecXersUlxyXHUwMDE0wFx1MDAwZqCq2zqw+UYmJ1x1MDAwM9pnq2bjKEMpJ93Pk2u5j4H3Z6ctxHXUXHUwMDEx4tqMXHUwMDExQYpcdTAwMDNcdTAwMGXj//QrXG5jj/62cWp2aquIXWGEu0F08qWZM4jlyo2ZsMjgIexHomY8QKokdmp5MO7Uzlx1MDAxN20pV5tHXHUwMDE4lbFcdTAwMTQxXHUwMDFkQqpj9e//q30qzTNimuBcdTAwMTAzM8505H1cdTAwMDdcdTAwMDRBsIVcdTAwMTSDXHUwMDAyXHUwMDE0k4rIUJ87h0ClUYJMUaBIyKzMfVx1MDAwZVx1MDAxZXcmy4BcdTAwMDaVV1xuyKDIUv67n+P0y2DD4/8x7zzDiUy3syTDTlx1MDAxZlx1MDAxNdR3wFx1MDAwMzOaXHUwMDE5XHUwMDE4mJEmyXnD1J09kCVnjKcw9+4xhfl7XHS96HXublx1MDAwZlx1MDAxY/df/+s//vOf13/lS5luZfFf//s/YVx1MDAxMsJhmIZcdFxuQrF/f96n6+aU29KUx/9/1T9cdTAwMTf9n//4e/+f/1x1MDAwYu3Qbk4ifQ== + + + + + T1K0R0T2K1R1T0\\T1K0R0T2K1R1T0\\T1K0R0T2K1R1T0\\子节点指针T关键字K数据块数据指针RB树 \ No newline at end of file diff --git a/Algorithm/DS/img/skiplist.drawio.svg b/Algorithm/DS/img/skiplist.drawio.svg new file mode 100644 index 0000000..b4a81cf --- /dev/null +++ b/Algorithm/DS/img/skiplist.drawio.svg @@ -0,0 +1,254 @@ + + + + + + + + + +
+
+
+ 1 +
+
+
+
+ + 1 + +
+
+ + + + + + +
+
+
+ 2 +
+
+
+
+ + 2 + +
+
+ + + + + + +
+
+
+ 3 +
+
+
+
+ + 3 + +
+
+ + + + + + +
+
+
+ 4 +
+
+
+
+ + 4 + +
+
+ + + + + + +
+
+
+ 5 +
+
+
+
+ + 5 + +
+
+ + + + +
+
+
+ 6 +
+
+
+
+ + 6 + +
+
+ + + + +
+
+
+ 1 +
+
+
+
+ + 1 + +
+
+ + + + + + + + +
+
+
+ 3 +
+
+
+
+ + 3 + +
+
+ + + + +
+
+
+ 6 +
+
+
+
+ + 6 + +
+
+ + + + +
+
+
+ 1 +
+
+
+
+ + 1 + +
+
+ + + + + + + + +
+
+
+ 3 +
+
+
+
+ + 3 + +
+
+ + + + +
+
+
+ 6 +
+
+
+
+ + 6 + +
+
+ + + + + + +
+
+
+ 5 +
+
+
+
+ + 5 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/Algorithm/Sort.md b/Algorithm/Sort.md new file mode 100644 index 0000000..b6982f2 --- /dev/null +++ b/Algorithm/Sort.md @@ -0,0 +1,213 @@ +--- +title: 常见排序算法 +date: 2019-05-02 00:47:43 +tags: +categories: +--- +**目录 start** + +1. [排序算法](#排序算法) + 1. [冒泡](#冒泡) + 2. [插入](#插入) + 3. [选择](#选择) + 4. [归并](#归并) + 5. [希尔](#希尔) + 6. [快速](#快速) + 7. [基数](#基数) + 8. [堆](#堆) + 9. [Tim](#tim) + +**目录 end**|_2020-04-27 23:42_| + +--- + +# 排序算法 + +> Github: [Java 实现](https://github.com/Kuangcp/JavaBase/tree/algorithms/src/main/java/com/github/kuangcp/sort) | [Python 实现](https://github.com/Kuangcp/PythonLearn/tree/master/algorithm/sort) + +> [参考: 十大经典排序算法](https://www.runoob.com/w3cnote/ten-sorting-algorithm.html) +> [Github: 十大经典排序算法](https://github.com/hustcc/JS-Sorting-Algorithm) + +[wikipedia 排序算法](https://zh.wikipedia.org/zh-cn/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95) + +--- + +| 排序算法 | 平均时间复杂度 | 最好情况 | 最坏情况 | 空间复杂度 | 排序方式 | 稳定性 | +| :------- | :------------- | :------- | :------- | :--------- | :------- | :----: | +| 冒泡 | n^2 | n | n^2 | 1 | In | 稳定 | +| 插入 | n^2 | n | n^2 | 1 | In | 稳定 | +| 选择 | n^2 | n^2 | n^2 | 1 | In | \ | +| 希尔 | n log n | n log n | n log n | 1 | In | \ | +| 归并 | n log n | n log n | n log n | n | Out | 稳定 | +| 快速 | n log n | n log n | n^2 | log n | In | \ | +| 堆 | n log n | n log n | n log n | 1 | In | \ | +| 计数 | n + k | n + k | n + k | k | Out | 稳定 | +| 桶 | n + k | n + k | n^2 | n + k | Out | 稳定 | +| 基数 | n * k | n * k | n * k | n + k | Out | 稳定 | + +- n:数据规模 +- k:"桶"的个数 +- In-place:占用常数内存,不占用额外内存 +- Out-place:占用额外内存 +- 稳定性:排序后 2 个相等键值的顺序和排序之前它们的顺序相同 + +--- + +## 冒泡 + +> 步骤 + +1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 +2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。 + - 这步做完后,最后的元素会是最大的数。 +3. 针对所有的元素重复以上的步骤,除了最后一个。 +4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 + +> 优缺点 + +1. 优点:实现简单 +2. 缺点:性能略差 + +> 极端情况 + +1. 最好:数据已经有序 +2. 最坏:数据全反序 + +> 优化策略 + +1. 在每次遍历的时候加上一个检查, 判断是否已经有序,有序就直接退出排序 + +## 插入 + +> 步骤 + +1. 首先将第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。 + - 随着算法执行有序序列逐渐替换掉未排序序列 +2. 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置,实现无序向有序的转换 + - 如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。 + +> 优缺点 + +1. 优点:实现简单 +2. 缺点:性能略差 + +> 极端情况 + +1. 最好:数据已经有序 +2. 最坏:数据全反序 + +> 优化算法 + +1. 拆半插入 + +## 选择 + +> 步骤 + +> 优缺点 + +1. 优点: +2. 缺点: + +> 极端情况 + +1. 最好: +2. 最坏: + +> 优化算法 + +## 归并 + +> 步骤 + +> 优缺点 + +1. 优点: +2. 缺点: + +> 极端情况 + +1. 最好: +2. 最坏: + +> 优化算法 + +## 希尔 + +> 步骤 + +> 优缺点 + +1. 优点: +2. 缺点: + +> 极端情况 + +1. 最好: +2. 最坏: + +> 优化算法 + +## 快速 + +> 步骤 + +> 优缺点 + +1. 优点: +2. 缺点: + +> 极端情况 + +1. 最好: +2. 最坏: + +> 优化算法 + +## 基数 + +> 步骤 + +> 优缺点 + +1. 优点: +2. 缺点: + +> 极端情况 + +1. 最好: +2. 最坏: + +> 优化算法 + +## 堆 + +> 步骤 + +> 优缺点 + +1. 优点: +2. 缺点: + +> 极端情况 + +1. 最好: +2. 最坏: + +> 优化算法 + +## Tim + +> 步骤 + +> 优缺点 + +1. 优点: +2. 缺点: + +> 极端情况 + +1. 最好: +2. 最坏: + +> 优化算法 diff --git a/Algorithm/img/001-algorithm-base.km.svg b/Algorithm/img/001-algorithm-base.km.svg new file mode 100644 index 0000000..0b951a0 --- /dev/null +++ b/Algorithm/img/001-algorithm-base.km.svg @@ -0,0 +1 @@ +数据结构 & 算法复杂度分析时间复杂度空间复杂度最好最坏平均均摊基本算法思想贪心算法分治算法动态规划回溯算法枚举算法排序O(n^2)冒泡排序插入排序选择排序希尔排序O(n log n)归并排序快速排序堆排序O(n)计数排序基数排序桶排序搜索深度优先搜索广度优先搜索A*启发式搜索查找线性表查找树结构查找散列表查找字符串匹配朴素KMPRobin-KarpBoyer-MooreAC自动机Trie后缀数组数学基础数论计算几何概率论分析并查集拓扑网络矩阵运算线性规划线性表数组链表单链表双向链表循环链表双向循环链表静态链表顺序栈链式栈双向栈队列普通队列双端队列阻塞队列并发队列阻塞并发队列散列表散列函数冲突解决链表法开放寻址其他动态扩容位图二叉树平衡二叉树二叉查找树平衡二叉查找树AVL树红黑树完全平衡二叉树满二叉树多路查找树B 树B+ 树2-3 树2-3-4 树小顶堆大顶堆优先级队列斐波那契堆二顶堆其他树状数组线段树图的存储邻接矩阵邻接表拓扑排序最短路径关键路径最小生成树二分图最大流 \ No newline at end of file diff --git a/Article/Font.md b/Article/Font.md deleted file mode 100644 index d93cff6..0000000 --- a/Article/Font.md +++ /dev/null @@ -1,49 +0,0 @@ -`目录 start` - -- [字体](#字体) - - [资源](#资源) - - [终端中常用字体](#终端中常用字体) - - [操作系统默认字体](#操作系统默认字体) - - [编辑器](#编辑器) - - [终端](#终端) - -`目录 end` |_2018-09-06_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 字体 - -> [有哪些适合用于写代码的西文字体?](https://www.zhihu.com/question/20299865) - -- Linxu 字体缓存目录: `$HOME/.local/share/fonts/` -## 资源 -> Github -- [IBM字体](https://github.com/IBM/type)`2017年发布的新字体` -- [codefont](https://github.com/zhenruyan/codefont)`常用的代码字体` -- [nerd-fonts](https://github.com/ryanoasis/nerd-fonts)`系列字体图标` -- [Font-Awesome](https://github.com/FortAwesome/Font-Awesome)`一大堆字体图标` - -> website -- [https://www.fontsquirrel.com/](https://www.fontsquirrel.com/) -- [https://www.urbanfonts.com/](https://www.urbanfonts.com/) -- [https://www.1001fonts.com/](https://www.1001fonts.com/) -- [https://www.ffonts.net/](https://www.ffonts.net/) - -### 终端中常用字体 -- [powerline](https://github.com/powerline/powerline) -- [fonts](https://github.com/powerline/fonts)`终端中常用字体` - -## 操作系统默认字体 -- 微软雅黑 -- Adobe 楷体 Std - - -## 编辑器 -- Fira Code Retina -- IBM Plex Mono SemiBold [Download](https://fontmeme.com/fonts/ibm-plex-mono-font/) - -## 终端 -- Source Code Pro for Powerline - - 并且 + Powerline + Awesonme 的 Bold 最适合ZSH的 Bullet Train 主题 -- Droid Sans Mono for Powerline -- Roboto Mono for Powerline Bold -- Hack - diff --git a/Article/HistoricalBias.md b/Article/HistoricalBias.md deleted file mode 100644 index cddba86..0000000 --- a/Article/HistoricalBias.md +++ /dev/null @@ -1,45 +0,0 @@ -`目录 start` - -- [历史的偏见性](#历史的偏见性) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 历史的偏见性 - -六十年过去了,有几个中国人知道同盟国二次大战中国战区最高司令是蒋中正。 - -六十年过去了,有几个中国人知道在内战中“恶贯满盈”的“中美合作所”在抗战中是盟军重要的情报机关。 - -六十年过去了,有几个中国人知道在影视作品中恶贯满盈的张灵甫是抗战英雄。在武汉会战中,他率领敢死队包抄小道,夜夺张古峰,为国军成功阻击日军立下汗马功劳,张灵甫还为抗战伤了一条腿。 - -六十年过去了,有几个中国人知道抗战中唯一全歼日本师团的战役-万家岭大捷。有几个中国人知道歼敌13万人的三次长沙会战。 - -六十年过去了,有几个中国人知道抗战中被日本人认为仅有的两次中国军队的勇猛程度要超过自己的战役之一的桂林保卫战(另一次为昆仑关战役日军第5师团被中央军第5军击败)。 - -六十年过去了,有几个中国人知道随枣会战中士兵以血肉之躯与敌人坦克相搏斗,官兵竟攀登敌人的坦克之上,以手榴弹向车里投掷,作战之勇敢与牺牲之壮烈,笔难尽述。 - -六十年过去了,有几个中国人知道抗战期间战线最长的会战-武汉会战。有几个中国人知道在武汉曾经上演了一场规模仅次于英德伦敦空战的武汉空战,有几个中国人知道宋美龄女士为鼓舞官兵士气五次赶赴武汉前线差点被日本人炸死。 - -六十年过去了,有几个中国人发出过这样的疑问:55年授衔的共和国将帅们都少有抗战的经历,而国民党高级将领几乎个个与小日本干过。 - -六十年过去了,在大陆的主流媒体和教科书上没有把抗日战争的所有战役完整的介绍过一次,甚至连起码的大事记也没有。 - -六十年过去了,有几个中国人看过日本的投降书?难道就是因为上面写着大日本皇军向国民政府投降向蒋委员长投降之类的字眼? - -六十年过去了,蒋介石动用70万国军发动了淞沪会战。在会战中,国军空军炸毁日本海军陆战队司令部,炸沉日本海军第3舰队旗舰,国军陆军为补充战损而五次发布动员令,超过半数的团职以上高级将领以身殉国。淞沪会战未能阻止日军占领上海,却改变了日军在中国战场的战略部署,还为上海资本向西转移赢得三个月时间。 - -六十年过去了,有几个中国人知道最让外国人刮目相看的不是新四军,而是孙立人的新一军。新一军远征缅甸,以伤亡1.7万人的代价击毙击伤日军10.9万人。在新一军攻占缅甸重镇于邦的时候,下属向孙立人询问如何处理日军战俘,孙将军的回答是:你去问问那些狗杂种,都谁到过中国,到过中国的就地枪毙,以后都这样办。 - -六十年过去了,有几个中国人知道八路军在平型关大捷中消灭的只是一支日军运输队,而平型关大捷只是平型关战役的一部分,平型关战役又是太原会战的一部分。 - -六十年过去了,有几个中国人知道李向阳和他的游击队是虚构的,真正让日军闻风丧胆的军队是国军的委员长卫队。这支军队使用德军的装备,甚至有德军教官亲手指导。在南京雨花台,委员长卫队的两个营独自阻击日军一个甲种师团(在第二次世界大战中,日军一共只有六个甲种师团),平均每个士兵要坚守25米长的阵地,面对50名日军精锐部队的士兵,但胜利者依然是中国人。 - -六十年过去了,有几个中国人知道在常德保卫战中,74军57师的8000名官兵阻击10万日军15天之久,最后只有200人能够战斗。师长发出了74军57师最后一封电报:弹尽,援绝,人无,城已破。职率副师长、师附、政治部主任、参谋部主任死守中央银行,各团长划分区域,扼守一屋,作最后抵抗,誓死为止,并祝胜利。 - -六十年过去了,有几个中国人知道在武汉上空爆发过持续时间仅次于不列颠空战的武汉空战。那场空战中,国军空军击落日军飞机78架,炸沉日军舰艇23艘。那个时候,每当防空警报响起,很多武汉市民不是钻进防空洞,而是爬上房顶,为的是能看到日军飞机被击落的场景。 - -六十年过去了,有几个中国人知道在重庆有17家军工厂在敌机轰炸下坚持24小时不间断生产。以金陵军工厂为例,抗战期间共生产迫击炮7000门、重机枪1.8万挺、步枪28万支、手榴弹30万枚、炸药包20万个。 - -六十多年过去了,有几个中国人知道日军投降书是什么样子。为什么某个政府只宣传9.18日军侵华而不宣传8.15日军投降,不让国民看看日军投降书?难道仅仅是因为文中多次出现:日本陆海空军及其辅助部队向蒋委员长投降。或者是因为受降落款是:中国战区最高统帅特级上将蒋中正特派代表陆军一级上将何应钦。 - -要知道,伟大的中国卫国战争是世界四大反法西斯战争之一!它不是用游击战、麻雀战、地道战、地雷战就能打赢的。它是用重兵集团与敌人浴血奋战才打赢的!战争期间,国军陆军有3211418名官兵壮烈牺牲,其中包括8名上将,41名中将,71名少将。国军空军有6164名飞行员血洒长空,2468架战机被击落。国军海军全军复没,所有舰艇全部打光。 diff --git a/Article/Memorandum.md b/Article/Memorandum.md deleted file mode 100644 index 085cd12..0000000 --- a/Article/Memorandum.md +++ /dev/null @@ -1,85 +0,0 @@ -`目录 start` - -- [备忘录](#备忘录) - - [数码](#数码) - - [硬盘](#硬盘) - - [笔记本](#笔记本) - - [键盘](#键盘) - - [Poker II](#poker-ii) - - [资源](#资源) - - [阿里云的服务器地域的划分](#阿里云的服务器地域的划分) - - [特殊字符](#特殊字符) - - [规范](#规范) - - [提交规范](#提交规范) - - [游戏](#游戏) - - [科技](#科技) - -`目录 end` |_2018-09-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 备忘录 -> 用来记录一些容易忘的乱七八糟的事情 - -## 数码 -### 硬盘 - -> [参考博客: 为SSD编程(6):总结—每个程序员都应该了解的固态硬盘知识](http://blog.jobbole.com/69969/) -> [参考博客: 隐患重重遭诟病 细数固态硬盘“七宗罪”](http://ssd.zol.com.cn/471/4715723_all.html#p5061992) -> [参考博客: 我偏要逆势而行 细数固态硬盘“七宗罪”](http://diy.pconline.com.cn/cpu/study_cpu/1205/2795735_all.html) -> [参考博客: 越用越慢?谈谈使用SSD的几个注意事项](http://diy.pconline.com.cn/cpu/study_cpu/1203/2722291_all.html#content_page_2) - - -> [参考博客: 显著提升程序员身心健康和工作效率的装备有哪些?](https://www.zhihu.com/question/23165812) -### 笔记本 -> 关于方向键的移动速度一定要快, 便携性上, 2kg以下 16g内存 - -### 键盘 -> 现在使用的是 Poker II, 61键, 小巧方便, 习惯上要稍微适应一下 - -#### Poker II -> [官网](http://www.ikbc.com.cn/) - -[参考博客: Poker II 机械键盘键位改造记](https://segmentfault.com/a/1190000000585559) `Caps Win Alt 换位置` -[参考博客: IKBC Poker2的终极使用方法](http://www.dgtle.com/thread-366040-1-1.html) `编程实现方向键更简单` -*********************** -## 资源 -### 阿里云的服务器地域的划分 -`在ECS帮助文档里的地域和可用区 国内地域:` -``` -地域名称 华北 1 华北 2 华北 3 华东 1 华东 2 华南 1 -所在城市 青岛 北京 张家口 杭州 上海 深圳 -RegionId cn-qingdao cn-beijing cn-zhangjiakou cn-hangzhou cn-shanghai cn-shenzhen -``` -### 特殊字符 -> [getemoji.com](http://getemoji.com/) - -********************************** -## 规范 -### 提交规范 - -> github 采用的字符渲染emoji (只能用于github) - -- `bug` :x: `:x:` -- `解决bug` :white_check_mark: `:white_check_mark:` - -- `待办需求` :pushpin: `:pushpin:` -- `doing` :writing_hand: `:writing_hand:` -- `done` :ok_hand: `:ok_hand:` - -- `里程碑` :trophy: `:trophy:` -- `改良的余地` :warning: `:warning:` - -> 直接使用emoji字符, 但是兼容性有问题 -- [🏆] 里程碑 -- [➕] 增加 -- [➖] 删除 -- [☯️] 修复bug - -*************************************** - -## 游戏 -> [DMC 鬼泣](http://www.gamersky.com/z/devilmaycry/down/) -*************************** - -## 科技 - -> [关于LTE频段划分及4G手机适配情况分析](https://bbs.meizu.cn/thread-4912100-1-1.html) diff --git a/Article/NotCode.md b/Article/NotCode.md deleted file mode 100644 index 75d3c95..0000000 --- a/Article/NotCode.md +++ /dev/null @@ -1,22 +0,0 @@ -`目录 start` - -- [非技术类的思考](#非技术类的思考) - - [开源文化](#开源文化) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 非技术类的思考 - - -## 开源文化 -- [两岸开源文化面面观(上)](https://linuxtoy.org/archives/opensource-culture-difference-between-mainland-taiwan-part1.html) - - [两岸开源文化面面观(下)](https://linuxtoy.org/archives/opensource-culture-difference-between-mainland-taiwan-part2.html) - - -- [朝鲜买房指南 ](https://mp.weixin.qq.com/s?__biz=MzI1ODUzNjQ1Mw==&mid=2247493125&idx=1&sn=c68049756f0bb2c88c418884aeab4bd7&chksm=ea04036fdd738a792bcf3fa977ec4fe3348a42c76e0238ece80a6028dc40270d92b14c58a551&mpshare=1&scene=1&srcid=0423qQ6JgZmLvFunigwVDmaw&pass_ticket=OlLchaEQYD%2FdFTOsFP5xA%2Bup1uT85elTpf0V3E56%2FoXyBmfSQkbqRgUrGARlldtK#rd) - - -- [知乎为什么是个垃圾网站](https://www.douban.com/note/361685574/) - -- [农业](http://blog.sina.com.cn/s/blog_173a9afb20102ybj0.html?tj=1) - diff --git a/Article/Science.md b/Article/Science.md deleted file mode 100644 index e789bfe..0000000 --- a/Article/Science.md +++ /dev/null @@ -1,16 +0,0 @@ -`目录 start` - -- [科学](#科学) - - [统计学佯谬](#统计学佯谬) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 科学 - -## 统计学佯谬 -辛普森佯谬 - - -> [参考博客: 王垠 : AlphaGo与人工智能](http://www.techug.com/post/alpha-go.html) - - diff --git a/Article/backup/Process_2016.md b/Article/backup/Process_2016.md deleted file mode 100644 index e29a508..0000000 --- a/Article/backup/Process_2016.md +++ /dev/null @@ -1,38 +0,0 @@ -`目录 start` - -- [2016年度总结](#2016年度总结) - - [2016.11.29](#20161129) - - [2016.10.07](#20161007) - - [2016.08.11](#20160811) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** - -# 2016年度总结 -- 因为是很晚才进行整理笔记,并决定保持这个习惯的,所以有些事就记不清了 -- 这一年在感情的路上一直是理不清的状态,也为了维持自己表面上的面子而犯下大错,后患至今。只怪自己当时就是懒,就是懒的思考。 - - 一心想着她想到很独到,和这样的人在一起能够互补,肯定很有意思。但实际上改变自己的是自己。 -- 这一年帮老师做了一个系统,第一次体会到和甲方聊需求什么的是很重要的事情,沟通表达能力的强弱决定了以后代码重构和推倒重来的几率成反比。 - - 这个系统采用的旧的技术栈,部署上简直是要命,不知道后来有没有部署成功,应该是从这开始就体会到了规范化的重要性,然后就开始了Maven的学习之路 -- 这一年,应该是这一年2333 拿到了驾照,这个过程也是很有意思值得思考的。 - - 考试之中充斥着关系和金钱的不平等地位。还有交际中活泛的思路以及客套的套路的重要性。因为自己心态的不够稳重导致了科二挂掉了,而在第二次考试中,因为看淡了,考试甚至在唱歌。反而满分过。 - - 已经很能说明心态好的重要性了。 -- 这一年里,我学会了朋友之间的感情还是家里人更亲近,同学还是要多来往。朋友很重要 - -## 2016.11.29 -* push时出现如下异常 cancelException 然后需要重新输入用户名密码。几次之后就不用输入,但是提示一直存在 -* 更改了输出窗口的编码后该提示是说取消了一个任务,还是不懂,但是正常使用 -* `back:使用ssh登录就不会有用户名密码这样的问题` -- [X] [使用SSH登录github](/Linux/Git_Action.md) - -## 2016.10.07 -- 图床的使用,百度云不方便,github更方便 -* 1:[百度云盘上存放的图片,可使用控制台找到图片真实地址,加以使用](http://pan.baidu.com/s/1c2FVvaC#list/path=%2FMD_Images) -* 2:将图片上传至github上,然后找到图片点下载,浏览器上方的URL就可以直接用 -* 3:使用markdownpad2的快捷键Ctrl+G,上传图片至一个服务器,会返回一个地址,最方便,但是自己没有图片的副本保存 -* 4:如果更改文件夹名,之后添加的文件是会在新的文件夹名下,之前的文件在旧文件夹下 -* *结论*:不要随便更改已经有文件的文件夹,不然github上会看起来不舒服 -* `back:使用github即可,使用git mv 更改文件名就不会有问题` - -## 2016.08.11 -- 这是用来记录我的笔记的仓库,用上git来管理,促使我熟悉git命令,习惯电子笔记 diff --git a/Article/backup/Process_2017.md b/Article/backup/Process_2017.md deleted file mode 100644 index 9c08654..0000000 --- a/Article/backup/Process_2017.md +++ /dev/null @@ -1,343 +0,0 @@ -`目录 start` - -- [2017年度总结](#2017年度总结) -- [2018展望](#2018展望) - - [2017-12-25 20:55:26](#2017-12-25-205526) - - [2017-12-18 23:31:29](#2017-12-18-233129) - - [2017-12-17 19:24:50](#2017-12-17-192450) - - [2017-12-14 17:39:53](#2017-12-14-173953) - - [2017-12-13 20:00:40](#2017-12-13-200040) - - [2017-12-12 10:34:03](#2017-12-12-103403) - - [2017-12-08 21:49:42](#2017-12-08-214942) - - [2017-12-07 09:50:52](#2017-12-07-095052) - - [2017-12-06 18:07:02](#2017-12-06-180702) - - [2017-12-03 18:40:32](#2017-12-03-184032) - - [2017-12-01 10:49:39](#2017-12-01-104939) - - [2017-10-30 15:37:05](#2017-10-30-153705) - - [2017-10-29 17:51:20](#2017-10-29-175120) - - [2017-10-25 22:58:30](#2017-10-25-225830) - - [2017-10-24 21:32:19](#2017-10-24-213219) - - [2017-10-23 09:50:52](#2017-10-23-095052) - - [2017-10-17 20:35:45](#2017-10-17-203545) - - [2017-10-12 14:11:03](#2017-10-12-141103) - - [2017-10-06 22:04:50](#2017-10-06-220450) - - [2017-10-03 20:20:24](#2017-10-03-202024) - - [2017-10-01 10:41:12](#2017-10-01-104112) - - [2017-09-29 21:48:35](#2017-09-29-214835) - - [2017-09-27 18:00:17](#2017-09-27-180017) - - [2017-09-25 19:05:41](#2017-09-25-190541) - - [2017-09-21 18:50:16](#2017-09-21-185016) - - [2017-09-13 23:02:14](#2017-09-13-230214) - - [2017-09-10 21:59:55](#2017-09-10-215955) - - [2017-08-31 18:24:44](#2017-08-31-182444) - - [2017-08-30 19:04:44](#2017-08-30-190444) - - [2017-08-29 15:29:38](#2017-08-29-152938) - - [2017-08-23 19:56:32](#2017-08-23-195632) - - [2017-08-15 21:00:20](#2017-08-15-210020) - - [2017-08-13 16:48:04](#2017-08-13-164804) - - [2017-08-01 16:42:49](#2017-08-01-164249) - - [2017-07-24 16:31:38](#2017-07-24-163138) - - [2017-07-15 09:50:27](#2017-07-15-095027) - - [2017-07-14 20:31:34](#2017-07-14-203134) - - [2017-07-10 11:30:36](#2017-07-10-113036) - - [2017-07-02 15:49:06](#2017-07-02-154906) - - [2017-06-27 21:14:08](#2017-06-27-211408) - - [2017-06-15 00:01:37](#2017-06-15-000137) - - [2017-06-08 21:17:56](#2017-06-08-211756) - - [2017-06-05 20:23:29](#2017-06-05-202329) - - [2017-05-24 20:12:45](#2017-05-24-201245) - - [2017-05-21 19:37:18 新的开始Linux](#2017-05-21-193718-新的开始linux) - - [2017-05-07 10:21:17](#2017-05-07-102117) - - [2017-05-04 08:51:58](#2017-05-04-085158) - - [2017-04-27 15:16:17](#2017-04-27-151617) - - [2017-3-10 21:08:06](#2017-3-10-210806) - - [2017-2-21 21:40:30](#2017-2-21-214030) - - [2017-1-1 10:00:53](#2017-1-1-100053) - - [没有提交的原因](#没有提交的原因) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** - -# 2017年度总结 -- 算一算 学习编程已有三个半年头了,时间过得是真的快,自打长大以来,时间就已经是感觉不到的速度在流逝了,所以更加要珍惜眼前事物才行。 -- 这一年里最大的事情就是脱离了FF团,这一路走来又哭又笑。深知自己的责任和能力的重要。 -- 这一年里编程技术的增长是横向的,扩展了很多技术栈方向,不管以后是否真的要用的上,至少遇到一个问题我能有各种各样的解决方案,而不只是一个。 -- 这一年里是有点后悔,当时一下冲动在最后一天报名了考研,本意是不想错过这次机会,但实际上其实就是因为没有强烈的明确自己需要的,想要的是什么。 - - 所以导致了不停的纠结中,又加上本来就是比较优柔寡断的人,所以问题就比较严重了。幸好熬到了考研结束,可以专心的做一件事了 - - 这一年的考研也发生了一件不大不小的事情,那就是泄题,视频都很清楚的在考前将原题泄露出来了,然后教育部一纸文书说没有此事,然后不了了之。刚好押中到超纲的那些题目,这个能力就有点令人玩味了。 - - 随之而来的是洗地,是严正声明!! 然后,半个月后就传出了他在原大学辞职然后在考研培训机构任职。呵呵 - - 这个世界本没有公平,做梦的人多了也就有了一群人的梦,如果你梦醒了就赶紧继续睡吧,梦里什么都有!!!! -- 这一年里也十分感激老师的知遇之恩,在这个团体里担任了要职,虽然在时间上是多花费了很多时间,但是确实是学习和锻炼到了自己的能力 - - 看到那些学弟羞涩内向的样子就想到了自己,对于技术上的问题没有信心去解决,没有耐心去死磕,这一点也看到了大二的自己 - - 但是这些如今我都有一定的克服和提高,这次经历的过程是十分有帮助的。 -- 这一年里也体会到了,有病就要早点治,不要侥幸,万一的事情发生后就是一万了,甚至一万也挽救不回来了!! -- 什么是穷人思维,什么是阶级固化,什么是zhengzhi敏感。什么是键盘侠,自己真的有那么睿智么,睿智到可以随意贬低某个群体? - -# 2018展望 - - - -## 2017-12-25 20:55:26 -- 又是花了好久的时间来写Shell,将逻辑整合了,但是却因为Shell的语法而花费大量时间 -- 不要因为有些东西很难处理就逃避!! - -## 2017-12-18 23:31:29 -- 要注意观察和阅读官方文档以及别人的经验,关于Shell,真的无力学习 - -## 2017-12-17 19:24:50 -- 还是要靠自己,关于代码规范到底是怎么的一个样子呢? -- 不要贪得无厌,关于技术的使用上,就不要太执着了。凑合就行,不然用的技术栈太多,实现上和调试上是很累的。 - -## 2017-12-14 17:39:53 -- 不过是个copyer而已,还美其名曰整理了笔记,就是一个复制粘贴的码畜而已 -- 复制过来能运行了能用了, 就不管了,这样的学习有什么用!!!离开笔记和博客,什么都做不了了。 - -## 2017-12-13 20:00:40 -- 需求没有确定就在开发业务模块!!! - - 最多写写基础构件啊。现在又要重新设计重写很多代码了。 - -## 2017-12-12 10:34:03 -- 任何事情都要主动的去争取,不管结果和过程怎么样,坐等别人来提醒你,是没有用的 - -## 2017-12-08 21:49:42 -- 别把人想的太坏,这是自私的表现,心中有阳光,世界自然是美好的 -- Idea更新了,内存降了一大截,嘿嘿 - -## 2017-12-07 09:50:52 -- 毕设全面抛弃模板引擎,使用MVC负责跳转,数据的交互使用js -- 一直以来有个误区,templates其实并不是模板引擎所设计出来的而是SpringMVC设计使用的,早该意识到的,所以模板引擎就没有那么重要了 -- 如果前后端分离的话,SpringMVC也不重要了,URL上的跳转逻辑都交给了前端路由 - -## 2017-12-06 18:07:02 -- 今天拍毕业照了,大学就要结束了,后悔: - - 没有主动的去了解奖助学金的评选 - - 没有主动的了解ACM或者别的大学生计算机的相关竞赛 - - 没有主动的去了解考研和准备考研 - - 没有主动的去改正自己的弱点,而是选择逃避 -- 然而后悔有什么用,生活还是要继续,发展是永恒的,做好手上的事情,20之前要交东西27答辩,呵呵,尽人事听天命 - -*** -- 今天把系统升级了, 是否还是显卡的问题,如果真不行的话,这个系统真的就比较不实用了,还是要去折腾显卡 - -*** -- 模板引擎Thymeleaf路转黑,要么试下前后端分离,要么使用别的模板引擎 - -## 2017-12-03 18:40:32 -- 开始着手设计毕业设计,发现都不容易 - -## 2017-12-01 10:49:39 -- 使用ngrok反向代理,完美绕过考试。技术的力量 -- 呵呵,既然是身为负责者就不要顾虑太多的人情什么的,事情要做还是要负责任,截止日期前一天做完是必须的 -- 不仅仅是做技术,处理事情也要负责任。 -- 记录下今天遇到的跨域问题,前后端分离使用HTML5的路由,就很容易出现跨域,具体再研究 IP 端口任一不同都属于跨域 -- 凡事不要急躁,即使时间已经很赶了,今天遇到的rm问题就是以后要警觉的,幸好现在误操作只是删除可以补救的文件 -- 官网才是可靠的网址,博客什么的都是别人玩剩下的,而且还不一定有用,但是能快速上手就是,官网多逛,或者开源社区,了解最新动态 -- 就比如这次就了解到了Tomcat官网有好多牛逼的东西 - -## 2017-10-30 15:37:05 -- 系统化的思想,合乎场合的逞能,有些是要制止的 - -## 2017-10-29 17:51:20 -- 坚持做一件事,只要是用心了的,总会有一点点的效果的,即使那个小孩很懒,这么久的练习和不停的教授题型的解法 -- 事情总应该往好的方面想,这样子你的心境才会积极,好的心境也会进一步影响你的外在表现,从而得到事情更好的结果。 -- 主要就是要有自信,以及规则的重视性(什么时候需要) - -## 2017-10-25 22:58:30 -- docker环境一致??,一样的基础镜像,一样的docker版本,一样的war包,得到的运行的容器资源的消耗截然不同 -- 内存消耗达到了另一个的两倍!!! - -## 2017-10-24 21:32:19 -- idea 调试是个什么鬼 Jsessionid???? - - 并不是任何的环境问题,而是chrome浏览器的cookie缓存问题 WTF ,在设置里面清除才有用,页面上清除没有用 -- 总结,有浏览器的诡异问题就要尝试多个浏览器,检查浏览器问题还是后台代码问题 - -## 2017-10-23 09:50:52 -- 阿里云的服务器地域的划分 在ECS帮助文档里的地域和可用区 - -`国内地域:` -``` -地域名称 华北 1 华北 2 华北 3 华东 1 华东 2 华南 1 -所在城市 青岛 北京 张家口 杭州 上海 深圳 -RegionId cn-qingdao cn-beijing cn-zhangjiakou cn-hangzhou cn-shanghai cn-shenzhen -``` -- 还是需要HTTPS 不好弄哦 -- 有些话在人前都不好说的,即使大家都知道,但就是不能说出来,谁说出来谁就是傻逼。 -- 规则要遵守,但是要看时机,衡量好这个度,太死守规则了是不适合这个社会的。 - -## 2017-10-17 20:35:45 -- 一个人的外放气质确实很重要,别人在第一眼看到你的时候,只会从自己的感受到的你的一言一行和气质来判断你的人, -- 即使你后面表现出来的样子是很好的,但是第一印象已经存在了,难以修改,所以要谨言慎行但是也要放开手脚的去做事 -- 关于作为一个项目的领导,以及管理的学问确实不简单,怎么将手里的活合理的划分和分配,怎么进行快速有效的沟通 - -## 2017-10-12 14:11:03 -- 做好一份工作不容易,尤其是这样的小组织团队,很多事情需要亲力亲为,又没有规范性 -- 沟通上需要寻求一种高效的方式,图表式的数据展示和交流是最好的。 -- 感觉身体出了些问题,现在很久没有运动,饮食也很乱,烦ing,感觉入了一个坑 - -## 2017-10-06 22:04:50 -- 回头再看小时候的举动,天真,自私,不会想太多。所以上不得台面,有好多事站在成人的角度来看,都是小问题,不足以挂齿的,是老了么。。。 -- 感觉现在的状态是一点点的让自己更成熟,当接触的事情越来越多后,不要以为自己过得舒服,只是有人为你负重前行而已!!! - -## 2017-10-03 20:20:24 -- 火车票一定要提前一星期买,节假日得半个月,不然买不到合适的票,今天的赶火车整个人都不好了,还有太早的火车要注意进站口会少,所有的工作人员都少 -- 关于交谈的技巧,要提高了,有个小收获就是和老板讨价还价了,最终也成功赢得了3块钱的让步,虽然少,但是在以前的话,付钱走人了。说明以前被坑的多傻。 -- 发现Termux真是神器,能跑一堆的linux命令,要不是手机性能不行,真的能当个树莓派玩了 - -## 2017-10-01 10:41:12 -- 不要复制自己,这一条我没有好好遵守,是不是应该全都单独分一个个的小项目,然后做成一个个的依赖,就不会有冗余 -- 做成父子项目,然后分步依赖,这样就不会很冗余 - -## 2017-09-29 21:48:35 -- 问题是什么呢,使用 frolvlad/alpine-oraclejdk8:slim镜像构建war应用,就不会占用特别高的资源300-400M,虽然包大一些 200+M, - - 使用alpine 安装openjdk8-jre 构建出来的镜像 才84M 但是运行却800多M - -## 2017-09-27 18:00:17 -- 晚上的惊喜确实是很感谢,酒桌上的礼仪也确实缺失了,需要多学习。情商也就是场面的事情 -- 关于如何管理人员也的确需要好好学习 - -## 2017-09-25 19:05:41 -- 又是一波重装系统,幸好分区home单独放了,不然就完了,手动安装deb 风险很大..以后装软件事先去商店里,或者命令安装 -- 还有,我以为别人傻,其实是自己傻,呵呵,不来就直说,浪费大家时间,不过这个习惯是好的,问清楚工作的所有细节 -- 很多人都说,我不知道自己想要什么,其实这句话的真正含义就是:`我没有勇气面对和足够的努力去争取我想要的` - -## 2017-09-21 18:50:16 -- 和外行人进行沟通不是一件简单的事,这时候的交流技能树就很重要了.在这种类型的公司里,要想工作都成问题,因为都是兼职,主要负责人话语人到处跑不见人 -- 公司越小就越累,因为公司规模小,职责划分不明确,杂七杂八的事都是要做的.这时候心态很重要 -- 并不是你讨厌的人就是垃圾,往往是你讨厌的人比你更努力,因为他们要做那些事情也是要花心思的,当然确实有你认为很不齿的人, -- 但是人家还是有很多特长能够有亮点,而你自己技能树匮乏之极,还是有人会和他谈笑风生,而你还是很恶心,并且被冷落 - -## 2017-09-13 23:02:14 -- 原本担心的是技术不够熟练,结果呢呵呵,无知至极的落后入门人员在指指点点,有何意义?极度恶心。。 -- 要是高考多考几分就不会这样烦了,唉,环境圈子决定了你要遇到什么人,什么事,很大程度上影响了你的一生 - -## 2017-09-10 21:59:55 -- 整理论文开题答辩报告,发现做一个功能完善的易用系统不简单。。。 -- 需要对业务模块非常熟悉,多思考细节 - -## 2017-08-31 18:24:44 -- 基本功能都完成了,做一个免费的东西给别人用,还是没这么简单 - -## 2017-08-30 19:04:44 -- 使用python开发一个和sdkman类似的sdk管理的脚本,对git的这种删除还留在索引里有点不舒服,操作一个文件库不是好主意,处理代码是好的 -- 使用七牛云或百度云的对象存储是比较好的选择,在处理数据上更方便 -- 使用全英文可以避免编码问题。。。 - -## 2017-08-29 15:29:38 -- 也是醉了,以前那次因为终端开启太慢了,从而换用户估计就是因为 sdkman的加载速度问题,网络不好或是他服务器慢就会这么恶心,也是厉害了 -- 还不入自己下载,然后配置环境变量,sdkman的好处就是切换版本,但是这个也太坑了,按照他的思路其实可以自己写一个类似sdk的脚本来用,只要写好每个sdk的下载地址就好了,或者说自己用github来搭建一个就好了 -- 试试用百度云 https://pan.baidu.com/s/1i48uER7 还是不能用命令下载 -- 认识到了坚果云 以及webdav - -## 2017-08-23 19:56:32 -- 多逛社区的确是有好处的,了解更多信息,学习到好用的工具,技术,了解更多的资讯 - -## 2017-08-15 21:00:20 -- 并不总是有好书的,别人的好评又如何,还是要自己去看,去思考去验证,例如 : -- [书籍读后感](/Article/ReadBook.md) 里面发表的对于 《Python从入门到实践》 和《Java程序员修炼之道》的看法 - - -## 2017-08-13 16:48:04 -- `https://raw.githubusercontent.com/Kuangcp/ImageRepos/maste/Tech/8.jpg` - - `https://raw.githubusercontent.com/用户/仓库/分支/文件的相对目录` - -## 2017-08-01 16:42:49 -- 使用逆序来写记录,更利于查看 -- 分区当时就应该分 / 和 /home 这样的话,换系统几乎没有太大影响(用户名一致) -- 在家待一星期了,没有学习的欲望和动力,只想 - -## 2017-07-24 16:31:38 -- 重装了系统,又回到了尴尬的使用,电脑要么不能休眠,要么窗口特效开不了,二选一,选了前者,然后就要改正自己习惯了, 开机一分多种,环境的开启也要几分钟,那么就要珍惜每次开机的使用,不要晾在那,提高效率了要 -- 暑假过了两个月了,要么搞毕设,要么搞Python,要么学Java Spring家族 - -## 2017-07-15 09:50:27 -- 在md中引用文件时,相对路径最好,/开头写相对于仓库根目录的路径更好 - -## 2017-07-14 20:31:34 -- 使用表格,引用,等markdown的特殊语法的时候,特别注意要空行隔开 - -## 2017-07-10 11:30:36 -- gitbook 识别 包含了网页标签 css样式 的 markdown 会解析失败,不是因为名字,而是因为内容 - -## 2017-07-02 15:49:06 -- 一开始将代码编写的尽可能简单,并在项目越来越复杂时,进行重构 - -## 2017-06-27 21:14:08 -- 使用了Gitbook来展示笔记,文件夹的命名不能随意,放在互联网上,指不定就有啥bug,至少不能使用 HTML CSS JS等网络资源的直接命名 -- 使用了脚本来生成目录,为了方便以后的使用 [Python文件](https://github.com/Kuangcp/Notes/blob/master/create_tree.py) - -## 2017-06-15 00:01:37 -- 遇到bug不要轻言放弃,到处找解决方案,就会有希望的 - -## 2017-06-08 21:17:56 -- 当你无法确定依赖之间用哪个版本号时,去[maven仓库](http://mvnrepository.com)瞧瞧是个好主意 - -## 2017-06-05 20:23:29 -- 使用微信的个人公众号来进行系统的监测,可以部署在百度BAE上 -- ![公众号](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/8.jpg) -- [X] 公众号还没做完,待系统完成后使用上公众号 - -## 2017-05-24 20:12:45 -- 使用IRC -- `back: 客户端不方便,活跃的人不是很多,使用上还是没有国内常用的舒服` - -## 2017-05-21 19:37:18 新的开始Linux -- Linux Deepin上使用Gedit来编辑 Moeditor来预览MarkDown ,Typora也还行 - -## 2017-05-07 10:21:17 -- `特殊字符表` 方便用来做协议控制符,正则切分 -``` - ╭ ─ ╳ ╬ ┇ ╧ ♀ ♂ ♡ ▽ △ □ ◇ ♢ ○ ☆ ☒ ★ - ◆ ■ ▲ ▼ ◀ ▶ ♠ ♣ ♥ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▉ ▊ ▋ ▌ ▍ ▎ ▏ - -``` - -## 2017-05-04 08:51:58 -- 使用Python的脚本生成目录(URL编码),但是要注意不能有空格,不能以.开头,没有大写字母 -- [X] 关于数据类型和基本运算都要系统学习才行 - -## 2017-04-27 15:16:17 -- 转移到了Linux平台进行开发,工具使用方便了些 -- `back:使用Linux更多折腾,但是这样促进了学习的兴趣` - -## 2017-3-10 21:08:06 -- `![ER图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/ER.png)`图片链接地址 -- `[](https://github.com/Kuangcp/Notes/blob/master/Java/EE.md)`文本链接地址 - -## 2017-2-21 21:40:30 -- 使用chrome的插件更省资源,内存真不够用 -- `back:chrome更费内存,Firefox省内存,使用Linux后更直观的感受到` - -## 2017-1-1 10:00:53 -- 使用gitBash操作maven更方便,所以说还是Linux适合码代码 -- [ × ] [安装deepin系统后配置开发环境记录](https://github.com/Kuangcp/Notes/blob/master/Linux/java_init.md) - - - -### 没有提交的原因 - -- `2017-11 12` - - 这两个月陷入了自我否定和借助游戏逃避的境地中,因为看到考研太难就心生退意,但是又不好明说,工作和毕设也在拖延。 - - 幸好身边有个警醒和关心我的人,正在变好,一切都会好的,只要你的心态是好的,不要总想坏的方向 up up up!!! - -- `2017-10-31` - - 这几天都是忙于项目的开始准备,新入职人员的融入 - - 还要忙于准备考研 - -- `2017-10-15 >16` - - 这两天都是忙手上的项目,没有时间做自己的事情 - -- `2017-10-11` - - ??做别的事去了,笔记也没有提交 -- `2017-10-05` - - 回家一趟,在斌斌家没带电脑 - -- `2017-09-24` - - 生日 - -- `2017-09-04 -> 2017-09-05` - - 在摆摊 - -- `2017-08-28` - -- `2017-07-21 -> 2017-07-29` - - 因为回家了,好多事情,太累了就不想码代码,而且那个时期很迷茫 \ No newline at end of file diff --git a/Article/backup/Project_Process_2017.md b/Article/backup/Project_Process_2017.md deleted file mode 100644 index 2f98478..0000000 --- a/Article/backup/Project_Process_2017.md +++ /dev/null @@ -1,39 +0,0 @@ -`目录 start` - -- [2017项目经历](#2017项目经历) - - [微信公众号](#微信公众号) - - [时间](#时间) - - [需求](#需求) - - [技术选型](#技术选型) - - [问题](#问题) - - [毕业设计](#毕业设计) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 2017项目经历 - -## 微信公众号 -### 时间 -> 2017年9月到11月两个月的开发周期 - -### 需求 -编写一个微信公众号, 内嵌了一个网站, 是优惠券的分发的网站, 只要有人在平台上领取优惠券然后进行购物,即可得到会扣从而盈利 -数据来源于阿里妈妈, 直接在平台上注册就能下载到对应的Excel, 然后从Excel中读取数据录入数据库进行展示和搜索使用 - -### 技术选型 -- 后端 - - Springboot - - themleaf - -### 问题 -- 微信对于淘宝域名的封杀, 导致了不能直接打开淘宝的链接, 然后就需要转换方式进行处理,因为有两种方式,一种是复制口令,这个是不影响的, 一种就是直接的URL跳转, 这个就受影响了 - - 这个是没有办法进行解决的,除非更改入口 - -- 一个是获取对应淘宝的页面,然后拿到优惠券是否过期的状态值,进行商品的下线, 由于是动态的页面, 又做了很多安全处理, 凭代码去获取页面再判断是比较困难的, 目前来说, 折腾好几天 - - 没有找到有效的解决方案, 这个是比较丧气的, 但是后来发现,如果借用淘宝的开放API不知道能不能做到, 由于人手上的关系, 最后是没有弄的 - - -****************** -## 毕业设计 -> 从2017年6月份一直拖到现在 2018-03-27 08:28:08 - diff --git a/BSD/FreeBSD.md b/BSD/FreeBSD.md new file mode 100644 index 0000000..9748892 --- /dev/null +++ b/BSD/FreeBSD.md @@ -0,0 +1,17 @@ +--- +title: FreeBSD +date: 2020-02-18 21:06:01 +tags: +categories: +--- + +**目录 start** + +1. [FreeBSD](#freebsd) + +**目录 end**|_2020-05-28 16:05_| +**************************************** +# FreeBSD +> [Official Site](https://www.freebsd.org/) + +> [软件安装预览](https://www.freebsd.org/doc/zh_CN/books/handbook/ports.html#ports-synopsis) diff --git a/BSD/OpenBSD.md b/BSD/OpenBSD.md new file mode 100644 index 0000000..8f7484c --- /dev/null +++ b/BSD/OpenBSD.md @@ -0,0 +1,21 @@ +--- +title: OpenBSD +date: 2020-02-18 21:06:01 +tags: +categories: +--- + +**目录 start** + +1. [OpenBSD](#openbsd) + 1. [安装](#安装) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# OpenBSD +> [Official Site](http://www.openbsd.org/) + +## 安装 +> [How to install openBSD](http://blog.up-link.ro/openbsd-how-to-install-openbsd-openbsd-4-7-installation-guide/) +> [How to install XFCE Desktop in OpenBSD](https://www.linuxhelp.com/how-to-install-xfce-desktop) + diff --git a/BSD/Readme.md b/BSD/Readme.md new file mode 100644 index 0000000..03d0af6 --- /dev/null +++ b/BSD/Readme.md @@ -0,0 +1,4 @@ +# BSD + +> [Difference between FreeBSD and OpenBSD ](http://www.differencebetween.info/difference-between-freebsd-and-openbsd)`简单说就是OpenBSD重安全 FreeBSD重性能` + diff --git a/BigData/DataAnalysis.md b/BigData/DataAnalysis.md new file mode 100644 index 0000000..fad57dc --- /dev/null +++ b/BigData/DataAnalysis.md @@ -0,0 +1,20 @@ +--- +title: DataAnalysis +date: 2024-06-04 00:06:44 +tags: +categories: +--- + +💠 + +- 1. [数据分析](#数据分析) + +💠 2024-09-06 11:36:43 +**************************************** +# 数据分析 +[Wikipedia: Data analysis](https://en.wikipedia.org/wiki/Data_analysis) + +> [OLAP 数据库](/Database/OLAP/Readme.md) + +> [RoaringBitmap](https://github.com/RoaringBitmap/RoaringBitmap)更好用的 Java 压缩位图数据结构, Spark,Hive,Clickhouse 都有使用 + diff --git a/BigData/DataIntegration.md b/BigData/DataIntegration.md new file mode 100644 index 0000000..e9bdd1e --- /dev/null +++ b/BigData/DataIntegration.md @@ -0,0 +1,157 @@ +--- +title: DataIntegration +date: 2024-04-10 14:17:16 +tags: +categories: +--- + +💠 + +- 1. [Data Integration](#data-integration) +- 2. [Datax](#datax) + - 2.1. [使用](#使用) + - 2.1.1. [Tips](#tips) + - 2.2. [设计](#设计) + - 2.3. [组件](#组件) + - 2.3.1. [Reader](#reader) + - 2.3.2. [Writer](#writer) +- 3. [SeaTunnel](#seatunnel) +- 4. [FlinkX ChunJun](#flinkx-chunjun) +- 5. [Flink CDC](#flink-cdc) +- 6. [Kettle](#kettle) + +💠 2024-10-08 11:23:38 +**************************************** +# Data Integration +数据集成 + +# Datax +> [Github](https://github.com/alibaba/DataX) 阿里云DataWorks的开源版 | [HashData](https://github.com/HashDataInc/DataX/) 增加了插件支持 + +> **注意** 这是一次性的开源项目,bug基本需要自己处理,从代码行数提交情况和issue,PR的活跃情况可以看出 +- [Clickhouse reader writer](https://github.com/alibaba/DataX/pull/264) +- [Kafka writer](https://github.com/alibaba/DataX/pull/1856) + +离线数据同步框架, 扩展读/写 Plugin 以支持多种数据源。 +核心框架负责处理流控,缓存,并发,自定义的[数据转换Transformer](https://github.com/alibaba/DataX/blob/master/transformer/doc/transformer.md)等。 + +- 特性 + - 轻量: 一份JSON配置启动一个Java进程 + - 支持线程级并发同步,按指定分批字段拆分数据 **限制分批字段整型或字符串** + - 运行时定期展示流量,行数等进度信息 + - 支持脏数据探测,failfast + - 支持流控策略配置 字节数,行数 +- 限制 + - 不支持实时增量,离线增量需要手动调整JSON配置实现 + - 单进程模式,无法集群式同步,资源利用不够高(单任务在做好读端和写端的优化话是可以打满网卡的) + +## 使用 +> [使用手册](https://github.com/alibaba/DataX/blob/master/userGuid.md) + + +### Tips +- 配置的json文件要`严格按照案例JSON来配置`,因为他不是按对象解析是按无结构json来**顺序遍历字段**去解析的 + - 踩过一个坑就是writer部分在reader部分前面,然后驱动加载出问题了,查看对应源码和jvm的加载类发现是有的,很隐蔽的报错,完全想不到是json配置顺序问题。 + +> [为什么不建议使用DataX读写GreenPlum](https://www.modb.pro/db/52542) 不建议用 postgresqlwriter,可以用 [HashData DataX](https://github.com/HashDataInc/DataX) 的 gpdbwriter 插件替代 +> 打包指定模块 mvn clean package -DskipTests assembly:assembly -pl plugin-rdbms-util -am + +************************ + +## 设计 +> [DataX 3.0 源码解析一](https://www.cnblogs.com/yaozhenfa/p/13840134.html) | [DataX核心源码流程](https://blog.csdn.net/ooeeerrtt/article/details/123779721) + +![](./img/datax-main-process.webp) + +- Job 管理子任务: JobContainer 和 TaskGroupContainer + - Reader将全部数据拆分成若干任务,在TaskGroupContainer中消费完(包含错误重试,并发限制,超时提示) +- Task 执行读写: TaskGroupContainer.TaskExecutor + - readerThread writerThread 实现多线程的生产者消费者模型 通过 Communication 通信 + - 读写线程的逻辑实现为 ReaderRunner 和 WriterRunner + +## 组件 +### Reader +- table模式: 只配置源表的 column,不灵活(需要源表对目标表字段名和类型一致)但支持并发。 +- querySQL模式:配置源表查询SQL,可以join,别名,函数计算。更灵活但是**不支持并发**,同步性能差 + +> 并行同步: 通过splitPk:拆分字段`只支持Long,字符串` 和 speed.channel: 并发数 去拆分上游数据 +- `SingleTableSplitUtil#genPKSql` + - 查询出 分片字段在上游表的最小和最大值,确认拆分的边界 +- `SingleTableSplitUtil#splitSingleTable` + - 参数 expectSliceNumber 的来源于Datax.json的直接指定和 限速channel,限速速率等取较小值。 + - 由于拆分是按ascii实现(先将字符串按ascii转为超大整数BigInteger,做完分段拆分后将若干段的边界值(超大整数)转回ascii字符),这个方式是有风险的 问题如下。 +- `TaskGroupContainer#start` 拆分出的若干SQL,包装为 TaskExecutor 绑定读写线程后启动 + - 依据前文拆分的若干分片SQL,while true 顺序遍历依据限制的并发值启动线程执行, 并对失败任务重试 + - 注意绑定的读写线程都通过设置线程对象的 setContextClassLoader 来实现对各种插件的加载,且和Datax主JVM进程的类加载器隔离 + - 每条SQL均是游标查询方式 CommonRdbmsReader.Task#startRead `ResultSet query(Connection conn, String sql, int fetchSize)` + +- 问题 + 1. 超大整数转ascii字符时,转出了单引号但是未转义,然后直接拼到SQL里,导致SQL语法错误。 + 1. 分段后数据范围有交叉导致同步的数据量大于上游数据总量,是概率性出现问题,因为这个字符转int的做法导致了字符的边界互相影响了,范围SQL产生了交集: + - 从ascii码来计算的完整切分分段,在GP执行时会有问题,因为GP的字符串比较方式并不是严格按照字符ascii的值 + - [Comparison Functions and Operators](https://www.postgresql.org/docs/current/functions-comparison.html) + ```java + int channel = 5; + List result = List result = RdbmsRangeSplitWrap.splitAndWrap("P010", "P024", channel * 5, "prodcode", "'", DataBaseType.PostgreSQL); + // 按Datax逻辑来说分段有26段,其中有4段的条件都命中了 P010 的数据, 导致了P010这个部分的数据重复了。 + // 例如这两段SQL: 在GP里面 select 'P010' > 'P01<'; 返回为True + // 'P01<' <= prodcode AND prodcode < 'P01B' + // 'P010' <= prodcode AND prodcode < 'P016' + + List result = RdbmsRangeSplitWrap.splitAndWrap("2016-02-06", "2024-05-06", 4, "period", "'", DataBaseType.PostgreSQL); + // 结果的数组中有元素的字面值包含了控制字符 \r. 将生成的SQL去查数据库没有问题,拆分的四段只有13段能查出数据 24段数据为空 + ``` +- 扩展 + - 特定优化思路:将拆分列查出全部的去重值 构造出分批in的SQL。 优点:将以该列的数据分布情况并发同步,贴合数据的业务特点。缺点:如果该列的去重值非常多,SQL会超长。 + +### Writer +- CommonRdbmsWriter.Task#startWriteWithConnection **模板类** 消费Reader的数据 批量写入目标库 + - 错误重试机制 相关配置项为 `core.container.task.failOver` + - 当一个批次遇到任意SQLException时,都会将这个批次的数据做事务回滚,逐条事务写入(估计是为了规避事务死锁) + - 但是这个机制*没有考虑下游数据源不支持事务*的情况,此时这个机制就引发下游出现重复数据了。 + +> 两个参数,任一条件满足就执行一次insert +- batchSize 默认2048 +- batchByteSize 默认32mib + - 该参数值需要谨慎设置,此大小是每个Task都需要的缓存区大小,如果设置过大,会发生OOM + - 例如设置堆内存1G 5并发 该值200Mib时,刚开始同步就会触发OOM,因为堆内存不够,没有留空间给datax自身 + +************************ + +# SeaTunnel +> [Github](https://github.com/apache/seatunnel) | [关于 SeaTunnel](https://seatunnel.apache.org/zh-CN/docs/about) + +> [首个国人主导的开源数据集成工具:揭秘 Apache 顶级项目 SeaTunnel 背后的故事](https://36kr.com/p/2311155472330244) + +使用 Spark、Flink 作为底层数据同步引擎使其具备分布式执行能力,开放并完善的插件体系和API集成。 +核心流程为 Source -> Transform -> Sink 。 Source 和 Sink 统称为Connector 负责读写数据库, Transform负责数据转换:别名映射,函数处理过滤。 + +这个架构设计将读和转换分离了,就没有Datax的两个模式所面临的问题,既支持读数据时做别名,也支持并发。 + +************************ + +> [并行读取](https://seatunnel.apache.org/zh-CN/docs/connector-v2/source/Jdbc#parallel-reader) 支持 数值,字符串,日期 类型字段 +- 生成拆分列逻辑 ChunkSplitter#generateSplits 字符串类型字段采用的是hash后取模方式 ` JdbcDialect#hashModForField` pg,oracle,mssql。 +- 执行数据拆分 FixedChunkSplitter#createSplitStatement + +************************ + +# FlinkX ChunJun +> [Github](https://github.com/DTStack/chunjun) + +# Flink CDC +> [Github](https://github.com/apache/flink-cdc) + + +************************ + +# Kettle +> [Github](https://github.com/pentaho/pentaho-kettle) +> [web kettle](https://github.com/JoeyBling/webkettle) + +[kettle java源码 kettle源码分析](https://blog.51cto.com/u_16213668/8667940) + +************************ + +> [Kettle大量数据快速导出的解决方案](https://www.cnblogs.com/47Gamer/p/13993373.html)`奇怪的是SpringBoot项目同样Fetch方式加流式Excel导出,整体导出效率低很多` +- 关联源码在 org.pentaho.di.trans.steps 下的 tableinput 和 excelwriter 包 diff --git a/BigData/DataOrchestration.md b/BigData/DataOrchestration.md new file mode 100644 index 0000000..4f804a2 --- /dev/null +++ b/BigData/DataOrchestration.md @@ -0,0 +1,52 @@ +--- +title: DataOrchestration +date: 2024-04-17 15:18:45 +tags: +categories: +--- + +💠 + +- 1. [数据协作](#数据协作) + - 1.1. [DolphinScheduler](#dolphinscheduler) + - 1.2. [Argo](#argo) + - 1.3. [Airflow](#airflow) +- 2. [其他](#其他) + - 2.1. [Azkaban](#azkaban) + - 2.2. [nifi](#nifi) + +💠 2024-09-03 14:05:18 +**************************************** +# 数据协作 +> [What is Data Orchestration & Why It’s Essential for Analysis](https://segment.com/resources/data-strategy/what-is-data-orchestration/) + +## DolphinScheduler +> [Github](https://github.com/apache/dolphinscheduler) +> [Youtube](https://www.youtube.com/@apachedolphinscheduler) + +DolphinScheduler 是国内易观数据公司在2018年开源,2019年进入Apache项目的分布式调度工具, + +思考:Job实例的执行交由K8S,避免Worker出现资源瓶颈,甚至去掉Worker只保留master,实例执行全部用K8S + +## Argo +> [Github](https://github.com/argoproj/argo-workflows) + +云原生工作流引擎 + +## Airflow +> [Github](https://github.com/apache/airflow) + +强代码实现的工作流引擎 + +************************ + +# 其他 +## Azkaban +> [Github](https://github.com/azkaban/azkaban) + +主要用于管理Hadoop工作流程 + +## nifi +> [Github](https://github.com/apache/nifi) + +处理和分发数据, 组件也只针对数据处理,功能比较单一 diff --git a/BigData/DataProcessingEngine/Flink.md b/BigData/DataProcessingEngine/Flink.md new file mode 100644 index 0000000..8d55ae8 --- /dev/null +++ b/BigData/DataProcessingEngine/Flink.md @@ -0,0 +1,18 @@ +--- +title: Flink +date: 2019-05-31 09:30:43 +tags: +categories: +--- + +**目录 start** + +1. [Flink](#flink) + +**目录 end**|_2020-05-17 16:13_| +**************************************** +# Flink +> [Official Site](https://flink.apache.org/) | [Github](https://github.com/apache/flink) +> [https://training.ververica.com/](https://training.ververica.com/) + +> [flink-learning](https://github.com/zhisheng17/flink-learning) diff --git a/BigData/DataProcessingEngine/Readme.md b/BigData/DataProcessingEngine/Readme.md new file mode 100644 index 0000000..f20a02a --- /dev/null +++ b/BigData/DataProcessingEngine/Readme.md @@ -0,0 +1,6 @@ +# 数据处理引擎 + +Flink +Spark +Storm + diff --git a/BigData/DataQuality.md b/BigData/DataQuality.md new file mode 100644 index 0000000..5585870 --- /dev/null +++ b/BigData/DataQuality.md @@ -0,0 +1,38 @@ +--- +title: DataQuality +date: 2024-05-07 19:32:22 +tags: +categories: +--- + +💠 + +- 1. [Data Quality](#data-quality) +- 2. [Topic](#topic) + - 2.1. [大宽表周期滚动更新时 不同版本间的数据质量分析](#大宽表周期滚动更新时-不同版本间的数据质量分析) + +💠 2024-05-08 20:26:55 +**************************************** +# Data Quality + + +************************ + +# Topic + +> [数据一致性比对(番外)](https://developer.aliyun.com/article/1204687) + +## 大宽表周期滚动更新时 不同版本间的数据质量分析 +背景: A1表 A2表 表结构一致,内容数据有区别,例如A1包含1月的销售数据 A2包含1-2月销售数据。 +诉求: 由于A1 A2表都是经过ETL过程产生的数据表,需要检查确认A2表中1月的数据是否存在较大程度的偏离,以及SKU变化情况(新增,删除,变更,一致)需要生成A1A2所有列加差异状态列拼接的结果大宽表。 + +注意前提:业务主键所标识的数据不能重复 +实现方案: + +A: +1. 依据业务主键做两个表之间的集合差运算先找出 新增和删除 +1. 选择A1表作为驱动表, 分批找出 not in 新增和删除的主键, 即变更或一致的主键数据, 依据A1表数据拎出A2表数据,Java应用层计算得到差异细节,写入到结果表 + +B: +1. `A1 LEFT JOIN A2` 一步得到结果大宽表, 强依赖底层数据引擎的大宽表JOIN能力,像CK就不适合。 +1. 扫描结果大宽表,应用层计算差异,更新到差异状态列 diff --git a/BigData/DataSecurity.md b/BigData/DataSecurity.md new file mode 100644 index 0000000..80a91f3 --- /dev/null +++ b/BigData/DataSecurity.md @@ -0,0 +1,40 @@ +--- +title: DataSecurity +date: 2024-01-24 18:24:52 +tags: +categories: +--- + + +💠 + +- 1. [数据安全](#数据安全) + - 1.1. [数据溯源](#数据溯源) + +💠 2024-01-24 18:24:52 +**************************************** +# 数据安全 + +## 数据溯源 +> 云服务商 +https://www.alibabacloud.com/help/zh/dataworks/user-guide/trace-leak-sources +https://support.huaweicloud.com/usermanual-dataartsstudio/dataartsstudio_01_1022.html + + +> 政府招标 +https://topics.gmw.cn/2023-02/02/content_36340223.htm + + + +https://www.esensoft.com/industry-news/dx-5149.html + +https://www.h3c.com/cn/pub/2023_Event/Training/WebHelp_H3C_LZPT/help/datasecurity/watermarkextraction.html +https://www.secrss.com/articles/29189 + +> 商业化公司 +https://www.ankki.com/productServer/93 + + +> 数据水印 + +文件水印:[朝鲜“红星”操作系统解析,给插入电脑的U盘内文件打水印](https://www.thepaper.cn/newsDetail_forward_1414238)`在文件上加上24位的机器识别码,理论上可以通过查看这部分的二进制数据倒推出打开过文件的操作系统链路` diff --git a/BigData/Readme.md b/BigData/Readme.md new file mode 100644 index 0000000..393a19a --- /dev/null +++ b/BigData/Readme.md @@ -0,0 +1,14 @@ +# 数据科学和大数据 +- [Jetbrains: data tools](https://www.jetbrains.com/data-tools/) + - [datalore](https://www.jetbrains.com/datalore/) `数据,代码,BI报表 工作流整合工具` + + +- [zeppelin](https://zeppelin.apache.org/) +- [jupyter](https://jupyter.org/) + +[数据集成](/BigData/DataIntegration.md) +[数据分析](/BigData/DataAnalysis.md) +[数据质量](/BigData/DataQuality.md) +[数据协作](/BigData/DataOrchestration.md) +[数据安全](/BigData/DataSecurity.md) + diff --git a/BigData/img/datax-main-process.webp b/BigData/img/datax-main-process.webp new file mode 100644 index 0000000..6252b8b Binary files /dev/null and b/BigData/img/datax-main-process.webp differ diff --git a/Blog/Blog.md b/Blog/Blog.md index 884995c..536d6e6 100644 --- a/Blog/Blog.md +++ b/Blog/Blog.md @@ -1,26 +1,28 @@ -`目录 start` - -- [博客](#博客) - - [个人博客网](#个人博客网) - - [博客能人](#博客能人) - - [专栏](#专栏) -- [【Blog】](#blog) - - [搭建博客](#搭建博客) - - [NetWork](#network) - - [经验之谈](#经验之谈) - - [操作系统](#操作系统) - - [Windows](#windows) - - [Linux](#linux) - - [安卓](#安卓) - - [Web性能](#web性能) - - [Tool](#tool) - - [团队开发](#团队开发) - - [毕业](#毕业) - - [生活](#生活) - - [娱乐](#娱乐) - - [商业](#商业) - -`目录 end` |_2018-09-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Blog +date: 2018-12-16 17:30:58 +tags: + - Blog +categories: + - Blog +--- + +💠 + +- 1. [博客](#博客) + - 1.1. [技术团队](#技术团队) + - 1.2. [个人博客网](#个人博客网) + - 1.3. [专栏](#专栏) + - 1.4. [经验之谈](#经验之谈) + - 1.5. [Tool](#tool) + - 1.6. [团队开发](#团队开发) + - 1.7. [毕业](#毕业) + - 1.8. [生活](#生活) + - 1.8.1. [娱乐](#娱乐) + - 1.8.2. [玩机](#玩机) + - 1.9. [商业](#商业) + +💠 2024-05-06 19:59:21 **************************************** > 只用来记录非技术类的文章,技术类的直接放到对应的笔记中去 @@ -28,12 +30,37 @@ # 博客 +## 技术团队 +- [阿里中间件](http://jm.taobao.org/) + ## 个人博客网 + +- [Paul Graham](http://www.paulgraham.com/hackpaint.html) +- [baeldung](https://www.baeldung.com/)`搜Spring相关总会搜到他的博客` + +> 搭建博客 +- [知乎话题:如何搭建博客](https://www.zhihu.com/question/20463581) +- [知乎专栏:使用Springboot搭建博客](https://zhuanlan.zhihu.com/p/28428463) +- [solo](https://github.com/b3log/solo)`博客系统 markdown` +- [咖啡兔](https://github.com/henryyan/henryyan.github.com)`现成的模板直接用md就能生成了` +- [Gitment:使用 GitHub Issues 搭建评论系统 ](https://imsun.net/posts/gitment-introduction/) + +************************ + +- [java-success](https://www.java-success.com/) + +- [解道](https://www.jdon.com/) - [王垠](http://www.yinwang.org/) - [ciaoshen](http://www.ciaoshen.com/)`肉身翻墙? Java大佬` - [阮一峰](http://www.ruanyifeng.com/blog/)`一个勤奋的布道者,Github每天都有提交` - [酷壳](https://coolshell.cn/)`左耳朵耗子个人博客网 阿里架构师` - [程序员DD](http://blog.didispace.com/)`翟永超 Spring4all社区创立者` +- [纯洁的微笑](http://www.ityouknow.com/)`Java丰富技术栈 云收藏的开发者` +- [Hollis](http://www.hollischuang.com/) + +- [java3y](https://zhongfucheng.bitcron.com/)`技术比较全面` +- [mythsman](https://blog.mythsman.com/#blog) + - [冰封千里](http://ice1000.org/)`Javaer,JavaFX和JVM类语言擅长` - [hushuang.me](https://hushuang.me/) - [猛禽](http://my.csdn.net/Raptor) `一个坚持了17年博客的程序员` @@ -41,29 +68,20 @@ - [David's blog](blog.weisiliang.com)`Java程序员` - [http://blog.leapoahead.com](http://blog.leapoahead.com)`经验之谈` - [Java和Vue前后端](http://blog.exrick.cn/) - - [Web程序猿](https://imququ.com/) - [letus.club](http://letus.club/) - [海底苍鹰(tank)](http://blog.51yip.com/) `前端和Linux` - [彭鑫](http://www.worldhello.net)`Git权威指南作者,Git比较熟练` - [Chenxu](https://www.dogxu.cn)`使用Github page 以及Https 全免费搭建` - - [Romeng's blog](https://www.romeng.men/)`edu邮箱,VPS等` - [如有乐享](https://51.ruyo.net/)`突破限制的 教程资源比较多` - - [javabolg](https://javablog.net/)`nutz作者的博客` - - [chenssy ](http://cmsblogs.com/)`技术较新 Java博客网` -- [java3y](https://zhongfucheng.bitcron.com/)`技术比较全面` - - [tengj](http://tengj.top/)`SpringBoot了解深入` - [dreamlikes](http://dreamlikes.cn/)`Java后台开发` - - [张子阳的博客](http://www.tracefact.net/) `有技术,有推荐的书` - [绿色记忆](https://blog.gmem.cc/) `技术涉猎广泛` - - [嘟嘟独立博客](http://tengj.top/)`SpringBoot系` -- [ityouknow](http://www.ityouknow.com/)`Java丰富技术栈 云收藏的开发者` - [会飞的污熊](https://www.xncoding.com/)`github 搭建静态博客, 内容多` - [yscoder](https://yscoder.github.io/)`indigo主题的作者 hexo` - [晓风轻技术小站](https://xwjie.github.io/)`程序员你为什么这么累? 作者` @@ -76,8 +94,15 @@ - [importNew](http://www.importnew.com/) - [漠然](https://mritd.me/)`容器 kubernetes方面` +- [senra](http://www.senra.me/) `服务器, 容器等方面` + +- [xxl](https://www.cnblogs.com/xuxueli/)`开发了一套分布式组件` +- [infotech](http://www.infotech.vip) +- [yourbatman](https://www.yourbatman.cn/)`励志成长` + + +************************ -## 博客能人 - [赵劼](http://www.cnblogs.com/JeffreyZhao/) `对函数式编程,并行程序开发,代码之美以及程序员能力与修养等相关问题` - [进击的Java新人](https://zhuanlan.zhihu.com/hinus) - [Java知音](http://blog.csdn.net/sky_blue12321)`较多面试题` @@ -92,6 +117,10 @@ - [杜琪 ](https://www.jianshu.com/u/28d7875c78df)`Java 服务端` - [恒宇少年 ](https://www.jianshu.com/u/092df3f77bca) +- [wsdjeg](http://www.cnblogs.com/wsdjeg/)`SpaceVim作者` + +- [Dale](https://www.cnblogs.com/Anker/)`Linux方面` + ************************ ## 专栏 - [CSDN 知识林](http://blog.csdn.net/zsl129) @@ -99,17 +128,6 @@ - [设计模式](http://blog.csdn.net/column/details/zsxdesignpattern.html) - [网络安全](https://zhuanlan.zhihu.com/avcom) -********************************* -# 【Blog】 -## 搭建博客 -- [知乎话题:如何搭建博客](https://www.zhihu.com/question/20463581) -- [知乎专栏:使用Springboot搭建博客](https://zhuanlan.zhihu.com/p/28428463) -- [solo](https://github.com/b3log/solo)`博客系统 markdown` -- [咖啡兔](https://github.com/henryyan/henryyan.github.com)`现成的模板直接用md就能生成了` -- [Gitment:使用 GitHub Issues 搭建评论系统 ](https://imsun.net/posts/gitment-introduction/) - -## NetWork -- [使用清华的IPV6](http://ju.outofmemory.cn/entry/154398) ## 经验之谈 - [不好的编程习惯](http://blog.csdn.net/xishining/article/details/78824148) @@ -136,25 +154,11 @@ _如何将脑海中的思维火花转化为代码_ >先把流程大致想清楚,在 main() 中写下大致的调用结构,不一定要实现每个细节,可以先用函数分装起来。实现最少的功能,之后再想怎么改进,加功能;这个过程也可以检验一开始写下来的代码是不是具备可拓展性。 比如用 js 做日历,可以先实现一个只能打印字符串日历的版本(这涉及到日期的计算,简单格式化输出),然后思考怎么把字符串适配到 html 上?或者服务端的话输出为 json ? -或者其实可以跳过序列化这一步?等等。可以学习一下 TDD ,一边写代码一边写测试,切忌自以为思考完所有细节一气呵成实现完毕,这样的话基本上写代码:调试找 bug = 1:10 - +或者其实可以跳过序列化这一步?等等。 +可以学习一下 TDD ,一边写代码一边写测试,切忌自以为思考完所有细节一气呵成实现完毕,这样的话基本上 写代码:调试找bug = 1:10 *************** -## 操作系统 -### Windows - -### Linux -- [Ubuntu放弃战斗, Linux桌面的悲哀](http://www.jianshu.com/p/86dd6e34ce91)`deepin王总` -- [一些工具软件](https://bbs.deepin.org/forum.php?mod=viewthread&tid=134241&extra=) -- [关于uptime输出的详解](http://blog.csdn.net/adparking/article/details/6684690) -- [Ubuntu安装我的世界](https://www.linuxidc.com/Linux/2016-04/129764.htm) - -### 安卓 -> [Android,开源还是封闭?](http://www.ruanyifeng.com/blog/2010/02/open_android_or_not.html) -*************** -## Web性能 -- [ab: Apache Benchmark 的使用的个人浅薄经验](https://ruby-china.org/topics/13870) -- [1M带宽的服务器并发问题](http://www.cnblogs.com/zikai/p/4971426.html) +> [参考: 2018开发者技能报告,让你更了解自己的行业。](https://mp.weixin.qq.com/s?__biz=MzU3OTYxOTU4NA==&mid=2247483877&idx=1&sn=bf8d6078770992004e7ac10e606112c7&chksm=fd621f8aca15969c2094683eb3d233f6e98ebda1028ce57081faef013f02e742c57c21f6a919&scene=21#wechat_redirect) *************** ## Tool @@ -166,7 +170,7 @@ _如何将脑海中的思维火花转化为代码_ *************** ## 团队开发 - [团队开发工作流项目](http://www.cnblogs.com/foundation/archive/2009/10/17/1584875.html) -> [参考博客: 有人向我反馈了一个bug](http://www.techug.com/post/when_someone_gives_you_a_bug.html) +- [参考: 有人向我反馈了一个bug](http://www.techug.com/post/when_someone_gives_you_a_bug.html) *************** ## 毕业 @@ -185,6 +189,10 @@ _如何将脑海中的思维火花转化为代码_ - [业务分析三维度(场景+角色+时间)之程序员坐禅论道](http://www.cnblogs.com/bluedoctor/archive/2013/04/30/3051418.html#C5) ### 娱乐 + +### 玩机 +> [华为全面屏手势](https://cn.club.vmall.com/forum.php?mod=viewthread&tid=16067108) + ## 商业 - [都说在做某界的 Airbnb,Airbnb 到底做了什么](http://daily.zhihu.com/story/4826947) diff --git a/Blog/Java.md b/Blog/Java.md index 8ac45c9..9f68432 100644 --- a/Blog/Java.md +++ b/Blog/Java.md @@ -1,25 +1,35 @@ -`目录 start` - -- [Java](#java) - - [社区](#社区) - - [SE](#se) - - [JavaFx](#javafx) - - [爬虫](#爬虫) - - [Springboot](#springboot) - - [BuildTool](#buildtool) - - [Test](#test) - - [精彩系列](#精彩系列) - - [码农翻身](#码农翻身) - - [架构师](#架构师) - - [Java语言讨论](#java语言讨论) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Java相关精彩博客 +date: 2018-12-16 17:32:04 +tags: + - Blog +categories: + - Java +--- + +💠 + +- 1. [Java](#java) + - 1.1. [社区](#社区) + - 1.2. [SE](#se) + - 1.3. [JavaFx](#javafx) + - 1.4. [爬虫](#爬虫) + - 1.5. [Springboot](#springboot) + - 1.6. [BuildTool](#buildtool) + - 1.7. [Test](#test) + - 1.8. [精彩系列](#精彩系列) + - 1.8.1. [码农翻身](#码农翻身) + - 1.8.2. [架构师](#架构师) + - 1.9. [Java语言讨论](#java语言讨论) + +💠 2024-03-30 11:43:28 **************************************** # Java > 和Java有关的博客和网页 ## 社区 - [并发编程网 Java](http://ifeve.com/category/java/) - [InfoQ Java](http://www.infoq.com/cn/java) +- [IBM Java](https://www.ibm.com/developerworks/cn/java/) ## SE - [ 菜鸟入门:Java程序员学习之路 ](http://blog.csdn.net/zzp16/article/details/5614588) @@ -30,6 +40,10 @@ - [推荐!国外程序员整理的Java资源大全](http://www.codeceo.com/article/java-resource-collection.html) - [oracle magazine](https://blogs.oracle.com/java/java-magazine-design-pattern) +- [参考: 一个牛人给Java初学者的建议(必看篇)](https://www.jb51.net/article/113819.htm) + +- [Fasterj](http://www.fasterj.com/index.shtml) + *************** - [Java动态代理机制详解](http://blog.csdn.net/luanlouis/article/details/24589193) `博客很细致,深入原理` - [使用JMockit编写java单元测试](http://blog.csdn.net/chjttony/article/details/17838693) @@ -38,6 +52,7 @@ ## JavaFx - [xJavaFxTool](https://gitee.com/xwintop/xJavaFxTool)`基于JavaFX的工具集` +- [Adding HTML Content to JavaFX Applications](https://docs.oracle.com/javafx/2/webview/jfxpub-webview.htm) ## 爬虫 - [Java网络爬虫实操](https://juejin.im/post/5a804b2cf265da4e9c63265b) `比较全面的讲述了如何使用Java写爬虫` @@ -84,10 +99,10 @@ ## Java语言讨论 > [冷眼看Java](http://swiftlet.net/archives/2530) -> [参考博客: 王垠:为Java说句公道话](http://www.techug.com/post/java-python.html) -> [参考博客: 比较 Rust 和 Java](http://www.techug.com/post/comparing-rust-and-java.html) -> [参考博客: Kotlin与Java的主客观比较](http://www.techug.com/post/kotlin-vs-java-the-whole-story.html) -> [参考博客: 10个实用的但偏执的Java编程技术](http://www.techug.com/post/10-java-tips.html) -> [参考博客: 王垠:编程的智慧](http://www.techug.com/post/programming-philosophy.html) +> [参考: 王垠:为Java说句公道话](http://www.techug.com/post/java-python.html) +> [参考: 比较 Rust 和 Java](http://www.techug.com/post/comparing-rust-and-java.html) +> [参考: Kotlin与Java的主客观比较](http://www.techug.com/post/kotlin-vs-java-the-whole-story.html) +> [参考: 10个实用的但偏执的Java编程技术](http://www.techug.com/post/10-java-tips.html) +> [参考: 王垠:编程的智慧](http://www.techug.com/post/programming-philosophy.html) diff --git a/Blog/Server.md b/Blog/Server.md index 822a16b..3c5004e 100644 --- a/Blog/Server.md +++ b/Blog/Server.md @@ -1,10 +1,17 @@ -`目录 start` - -- [Server](#server) - - [终端](#终端) - - [内存优化](#内存优化) +--- +title: Server +date: 2018-11-21 10:56:52 +tags: +categories: +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [Server](#server) + 1. [终端](#终端) + 1. [内存优化](#内存优化) + +**目录 end**|_2020-06-28 02:11_| **************************************** # Server > 服务器运维相关 diff --git a/Blog/Solution.md b/Blog/Solution.md index 443bb8e..212cc78 100644 --- a/Blog/Solution.md +++ b/Blog/Solution.md @@ -1,11 +1,56 @@ -`目录 start` - -- [解决方案](#解决方案) +--- +title: Solution +date: 2018-11-21 10:56:52 +tags: +categories: +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [解决方案](#解决方案) + - 1.1. [书籍](#书籍) + - 1.2. [Topic](#topic) +- 2. [消息推送](#消息推送) +- 3. [用户体系](#用户体系) + - 3.1. [账号安全认证体系](#账号安全认证体系) +- 4. [独立功能](#独立功能) + - 4.1. [延迟消息](#延迟消息) + +💠 2024-05-06 19:59:21 **************************************** # 解决方案 > 关于问题的解决方案 -[站内信设计思路之己见(基于上百万用户)](http://www.cnblogs.com/x-xk/archive/2012/11/17/2770935.html) +> [饿了么交易系统 5 年演化史 ](http://mp.weixin.qq.com/s?__biz=MzU4NzU0MDIzOQ==&mid=2247489228&idx=1&sn=9baeb5d2cfef853c80068ce8e830ccb2&chksm=fdeb24acca9cadba8ab2055243a97a13b561ee4b3ab0ffba1ab2c8fa7caeae2de0f13b80213e&mpshare=1&scene=1&srcid=&sharer_sharetime=1587047061042&sharer_shareid=246c4b52c1cb45eaa580c985c95107f3#rd) + +## 书籍 +淘宝技术这十年 +亿级流量网站架构核心技术 + +## Topic +1. 完整设计一个支持数十亿规模的用户系统 +1. 如何设计一个支持千万设备推送的消息系统 +1. 简单实现一个消息中间件保证消息不重复不丢失 +1. 如何设计供应链正逆向系统 +1. 如何设计财务对账系统 + +************************ + +# 消息推送 +站内信: [站内信设计思路之己见(基于上百万用户)](http://www.cnblogs.com/x-xk/archive/2012/11/17/2770935.html) + + +************************ +# 用户体系 + +## 账号安全认证体系 +> [被脱裤也不怕 - 反馈总结](https://blog.coderzh.com/2016/01/13/password-security-additional/) + +![](img/auth-and-store.drawio.svg) + + +************************ +# 独立功能 +## 延迟消息 +> [ 手把手实现一条延时消息 ](https://www.cnblogs.com/crossoverJie/p/11605814.html) diff --git a/Blog/View.md b/Blog/View.md index e34d13e..3a8c71c 100644 --- a/Blog/View.md +++ b/Blog/View.md @@ -1,15 +1,22 @@ -`目录 start` - -- [前端](#前端) - - [基础](#基础) - - [规范](#规范) - - [CSS](#css) - - [相关库](#相关库) - - [Jquery](#jquery) - - [Bootstrap](#bootstrap) - - [LayUI](#layui) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: View.md +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +**目录 start** + +1. [前端](#前端) + 1. [基础](#基础) + 1. [规范](#规范) + 1. [CSS](#css) + 1. [相关库](#相关库) + 1. [Jquery](#jquery) + 1. [Bootstrap](#bootstrap) + 1. [LayUI](#layui) + +**目录 end**|_2020-04-27 23:42_| **************************************** # 前端 > 半学半就 [个人导航页](https://kuangcp.github.io/main.html) diff --git a/Blog/img/auth-and-store.drawio.svg b/Blog/img/auth-and-store.drawio.svg new file mode 100644 index 0000000..c6159c4 --- /dev/null +++ b/Blog/img/auth-and-store.drawio.svg @@ -0,0 +1,482 @@ + + + + + + + +
+
+
+ 客户端公私钥(A,a) +
+
+
+
+ + 客户端公私钥(A,a) + +
+
+ + + + +
+
+
+ 服务端公私钥(B,b) +
+
+
+
+ + 服务端公私钥(B,b) + +
+
+ + + + + + +
+
+
+ Server +
+
+
+
+ + Server + +
+
+ + + + + + + + + +
+
+
+ Client +
+
+
+
+ + Client + +
+
+ + + + + + +
+
+
+ self call +
+
+
+
+ + self call + +
+
+ + + +
+
+
+ 用户输入 +
+
+
+
+ + 用户输入 + +
+
+ + + + + + +
+
+
+ 生成密钥对 +
+
+
+
+ + 生成密钥对 + +
+
+ + + + +
+
+
+ ECDH协商密钥 + + SharedKey + + = a * B +
+
+
+
+ + ECDH协商密钥 SharedKey = a * B + +
+
+ + + + + + +
+
+
+ DB存储 Salt SaltHash1 SaltHash2 +
+
+
+
+ + DB存储 Salt SaltHash1 SaltHash2 + +
+
+ + + + + + +
+
+
+ SaltHash1 = bcrybt(SHA512(password), uid+Salt, 10) +
+
+ SaltHash2 = SHA512(SaltHash1 + uid + Salf) +
+
+
+
+ + SaltHash1 = bcrybt(SHA512(password), uid+Salt, 10)... + +
+
+ + + + + +
+
+
+ 客户端公钥 A + AES( + + SharedKey + + , uid) +
+
+
+
+ + 客户端公钥 A + AES(SharedKey, uid) + +
+
+ + + + + +
+
+
+ AES( + + SharedKey + + , Salt) +
+
+
+
+ + AES(SharedKey, Salt) + +
+
+ + + + +
+
+
+ ECDH协商密钥 + + SharedKey + + = b * A +
+
+
+
+ + ECDH协商密钥 SharedKey = b * A + +
+
+ + + + + + +
+
+
+ AES( + + SharedKey + + , + + Ticket + + ) +
+
+
+
+ + AES(SharedKey, Ticket) + +
+
+ + + + + +
+
+
+ AES( + + SharedKey + + , AES(RandKey, + + TempKey + + + ...)) +
+
+
+
+ + AES(SharedKey, AES(RandKey, TempKey + ...)) + +
+
+ + + + +
+
+
+ + Ticket + + = AES(SaltHash2, uid+time+SaltHash1+RandKey) +
+
+
+
+ + Ticket = AES(SaltHash2, uid+time+SaltHash1+RandKey) + +
+
+ + + + + + + +
+
+
+ + AES ( + + + + TempKey + + + + , xxx) + +
+
+
+
+ + AES (TempKey, xxx) + +
+
+ + + + + +
+
+
+ return +
+
+
+
+ + return + +
+
+ + + + + +
+
+
+ 后续业务通信 +
+
+
+
+ + 后续业务通信 + +
+
+ + + + + +
+
+
+ 验证密码 +
+
+ 获取票据 +
+
+
+
+ + 验证密码获取票据... + +
+
+ + + + +
+
+
+ 请求 +
+ S +
+ a +
+ l +
+ t +
+
+
+
+ + 请求S... + +
+
+ + + + +
+
+
+ 1. 使用 SaltHash2 解密 + + Ticket + + 得到 SaltHash1 (解密失败则提前退出) +
+ 2. 使用 SaltHash1 计算出 SaltHash2 并校验是否和数据库中SaltHash2 一致(方便随时变更) +
+ 3. 生成临时密钥 + + TempKey + +
+
+
+
+ + 1. 使用 SaltHash2 解密 Ticket 得到 SaltHash1 (解密失败则提前退出)... + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/Book/CS/NetworkBooks.md b/Book/CS/NetworkBooks.md deleted file mode 100644 index 51fc1bd..0000000 --- a/Book/CS/NetworkBooks.md +++ /dev/null @@ -1,10 +0,0 @@ -`目录 start` - - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -> [Real time Webservice](http://ceur-ws.org/Vol-601/EOMAS10_paper13.pdf) - - -- webapi的设计与开发 -- 网络是怎样连接的 \ No newline at end of file diff --git a/Book/CS/Readme.md b/Book/CS/Readme.md deleted file mode 100644 index 1c4cf92..0000000 --- a/Book/CS/Readme.md +++ /dev/null @@ -1,12 +0,0 @@ -`目录 start` - - -`目录 end` |_2018-08-08_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -- 深入理解计算机系统 https://www.zhihu.com/question/20402534 https://www.zhihu.com/question/20354069 - - -- 算法导论 -- 信息论、推理与学习算法 -- 编程之美 - - [在线阅读](http://ishare.iask.sina.com.cn/f/18359238.html) \ No newline at end of file diff --git a/Book/Database/Readme.md b/Book/Database/Readme.md deleted file mode 100644 index 1ad57ac..0000000 --- a/Book/Database/Readme.md +++ /dev/null @@ -1,6 +0,0 @@ -`目录 start` - - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -高性能Mysql \ No newline at end of file diff --git a/Book/DockerBook.md b/Book/DockerBook.md deleted file mode 100644 index b361d1b..0000000 --- a/Book/DockerBook.md +++ /dev/null @@ -1,9 +0,0 @@ -`目录 start` - -- [Docker](#docker) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Docker - -- [Docker — 从入门到实践](https://www.gitbook.com/book/yeasy/docker_practice/details) diff --git a/Book/Front/Readme.md b/Book/Front/Readme.md deleted file mode 100644 index 29b151f..0000000 --- a/Book/Front/Readme.md +++ /dev/null @@ -1,7 +0,0 @@ -`目录 start` - - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -- [Web前端开发](http://caibaojian.com/gitbook/format/cover.html) `gitbook的使用以及相关技术的索引页` -- [深入浅出 Greasemonkey](http://www.ttlsa.com/docs/greasemonkey/#basic.what)`火狐一个辅助性插件` diff --git a/Book/GoBooks.md b/Book/GoBooks.md deleted file mode 100644 index d2d75cb..0000000 --- a/Book/GoBooks.md +++ /dev/null @@ -1,9 +0,0 @@ -`目录 start` - - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -- [《Go入门指南》](https://github.com/Unknwon/the-way-to-go_ZH_CN) -- [Learning-Go](https://github.com/mikespook/Learning-Go-zh-cn) -- [使用Go构建一个WebApplication](https://github.com/astaxie/build-web-application-with-golang) -- [Go语言圣经(中文版)](https://books.studygolang.com/gopl-zh/) diff --git a/Book/JavaBooks.md b/Book/JavaBooks.md deleted file mode 100644 index 92a2d84..0000000 --- a/Book/JavaBooks.md +++ /dev/null @@ -1,73 +0,0 @@ -`目录 start` - -- [Java书籍](#java书籍) - - [视频](#视频) - - [未读](#未读) - - [已阅读](#已阅读) - - [《Java程序员修炼之道》](#《java程序员修炼之道》) - - [《Java8实战》](#《java8实战》) - - [《Netty权威指南》](#《netty权威指南》) - - [《Netty in Action 中译》](#《netty-in-action-中译》) - -`目录 end` |_2018-09-01_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** - -# Java书籍 -- [从 Java 代码到 Java 堆](https://www.ibm.com/developerworks/cn/java/j-codetoheap/) `理解和优化您的应用程序的内存使用` - -## 视频 -- [用Java学编程 讲师:翁恺](http://study.163.com/course/introduction.htm?courseId=533006#/courseDetail?tab=1) - -## 未读 - -> [程序员必读书单](http://www.cnblogs.com/figure9/p/developer-reading-list.html) -> [参考博客: 一个合格的程序员应该读过哪些书(偏java)](https://www.jb51.net/article/35440.htm) -> [参考博客: 2018 年 Java 程序员必读的十本书](http://blog.jobbole.com/113955/) -> [参考博客: 程序员必读的六本书](https://droidyue.com/blog/2015/07/04/six-books-every-programer-must-read/) -> [参考博客: Java自学书籍Top 10](https://www.jb51.net/article/93337.htm) - - -- head first Java -- head first 设计模式 -- 设计模式 可复用面向对象软件的基础 `四人帮` -- Java编程思想 需要精读 -- Java核心技术 卷1 基础知识 卷2 高级特性 -- 实战java高并发程序设计 -- 深入理解Java虚拟机 -- Effective Java -- 图解Java多线程设计模式 - -大教堂和集市 -> [参考博客: 《大教堂和集市》笔记](http://www.ruanyifeng.com/blog/2008/02/notes_on_the_cathedral_and_the_bazaar.html) - -- Mybatis 从入门到精通 -- 深入实践SpringBoot -- 深入浅出 Mybatis 技术原理与实战 -- SpringCloud 与 Docker 微服务架构与实战 - -深入Java虚拟机(原书第2版) 译者: 曹晓钢 / 蒋靖 - -## 已阅读 -### 《Java程序员修炼之道》 -> Benjamin J.Evans | Martijn Verburg - -`2017-08-15 21:14:02` -- 这本书在理论上的讲解的确是挺扎实的,但是案例代码不敢恭维,本来Java就不是脚本语言,多个类的协作很常见,里面的demo都是不事先说明这里的案例 -- 引用的这些类源码是什么,干什么的,虽然是很简单的类,基本可以猜出来,那么既然如此,为什么不使用更为简单的,就单个类来讲解这个知识点 -- 而且代码又不规范,甚至出现了 l 1 要我去区分,这个是看的书里面第一次看到这样的源码,应试考试题目都没有这样的。。。 -`2017-08-27 10:49:46` -- 基本整理完了一遍,有些暂时没有用到的先不去学习,总的来说书挺好,对思想的理解有好处,代码就自己想一个更好,书上源码来自一个项目,不简单明了 - -### 《Java8实战》 -`2018-03-11 14:25:10` -- 开始正式的看这本书, 虽然图书馆有这本书, 也稍微看了下目录, 但是一直没有精读, 看前面的一部分就能看出这是一本不错的书, 讲述了Java8的特性, 以及带来的效率的巨大提升 - -### 《Netty权威指南》 -`2018-04-03 09:34:32` -- 版本1和2都有大致的看, 学习到了数据交换协议, 以及Netty的简单使用, -- NIO AIO的学习和使用, Netty封装后, 开发简洁了很多, 使用原生的写法要复杂很多 -- 并发的再一次学习和理解, 但还是没有开始动手实践 -- Netty自己定义协议栈 还没有学习, 感觉挺有难度 - -#### 《Netty in Action 中译》 -> [如何评价《Netty实战》这本书?](https://www.zhihu.com/question/58838575) diff --git a/Book/LinuxBooks.md b/Book/LinuxBooks.md deleted file mode 100644 index 37832c7..0000000 --- a/Book/LinuxBooks.md +++ /dev/null @@ -1,22 +0,0 @@ -`目录 start` - -- [Linux](#linux) - - [未读](#未读) - - [已读](#已读) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Linux -- [awk学习笔记](http://www.ttlsa.com/docs/awk/) -> [Linux 101 hacks](https://wrfly.gitbooks.io/linux-101-hacks/SUMMARY.html) - -## 未读 -- 鸟哥的Linux私房菜 -- 构建高可用 Linux 服务器(第 4 版) 余洪春 -- 快乐的命令行 https://github.com/billie66/TLCL -- 只是为了好玩 -- Linux命令行与shell脚本编程大全 -- Linux Shell脚本攻略 二 三 版 - -## 已读 - diff --git a/Book/Math/Readme.md b/Book/Math/Readme.md deleted file mode 100644 index 850a263..0000000 --- a/Book/Math/Readme.md +++ /dev/null @@ -1,14 +0,0 @@ -`目录 start` - - - [数学](#数学) - - [统计学](#统计学) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -- [数学妙啊!妙!](https://www.zhihu.com/pub/book/119554814) - - - -## 数学 -### 统计学 -- 统计学完全教程 \ No newline at end of file diff --git a/Book/PythonBooks.md b/Book/PythonBooks.md deleted file mode 100644 index 0d2ba86..0000000 --- a/Book/PythonBooks.md +++ /dev/null @@ -1,23 +0,0 @@ -`目录 start` - -- [Python](#python) - - [未读](#未读) - - [已读](#已读) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Python - -- [笨方法学Python](http://www.ttlsa.com/docs/learn-python-the-hard-way/) -- [简明 Python 教程](http://www.ttlsa.com/docs/jianming-python/python/) -- [Python3-cookbook](https://github.com/yidao620c/python3-cookbook) -- [深入Python中文版](http://www.ttlsa.com/docs/dive-into-python/) -- [深入Python3](http://www.ttlsa.com/docs/dive-into-python3/) -- [Python 手册](http://www.ttlsa.com/docs/python_hb/) -- [tornado教程](http://www.ttlsa.com/docs/tornado/) - -## 未读 -- Python Web 开发实战 - -## 已读 - diff --git a/Book/Readme.md b/Book/Readme.md deleted file mode 100644 index a452bb6..0000000 --- a/Book/Readme.md +++ /dev/null @@ -1,194 +0,0 @@ -`目录 start` - -- [书籍简单读后感](#书籍简单读后感) - - [计算机类](#计算机类) - - [算法](#算法) - - [《数据结构与算法分析 Java语言描述》](#《数据结构与算法分析-java语言描述》) - - [数据库](#数据库) - - [《Redis in action》](#《redis-in-action》) - - [Python](#python) - - [《Python从入门到实践》](#《python从入门到实践》) - - [Linux](#linux) - - [《Linux命令行与Shell脚本编程大全》](#《linux命令行与shell脚本编程大全》) - - [工具](#工具) - - [《Docker实战》](#《docker实战》) - - [《Docker全攻略》](#《docker全攻略》) - - [生活类](#生活类) - - [时事](#时事) - - [《活着》](#《活着》) - - [杂志](#杂志) - - [《新周刊 2012年度佳作 做点无用的事》](#《新周刊-2012年度佳作-做点无用的事》) - - [军事历史](#军事历史) - - [《亮剑》](#《亮剑》) - - [艺术类](#艺术类) - - [生活](#生活) - - [《一个人的朝圣》](#《一个人的朝圣》) - - [武侠](#武侠) - - [《鹿鼎记》](#《鹿鼎记》) - - [名著类](#名著类) - - [历史](#历史) - - [《史记》](#《史记》) - - [《西游记》](#《西游记》) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -> 在线书籍以及GitBook - -- [陈光剑的免费图书馆](https://universsky.github.io/)`Kotlin极简教程的作者` - - [陈光剑先生的学习方式](https://github.com/judasn/hexo-blog/blob/master/2016/02/My-Learning-Way.md) -- [Github某仓库](https://github.com/EbookFoundation/free-programming-books) -- [经典编程书籍大全](https://github.com/jobbole/awesome-programming-books) -- [bookstack](https://www.bookstack.cn/) -- [b-ok](http://b-ok.xyz) - - -> [jb51 编程专区](https://www.jb51.net/books/list422_1.html) -> [3322.cc电子书专区](http://www.3322.cc/sort/132-1.html) -> [java1234](java1234.com) -> [ 6 个高质量电子书网站](http://tech.ifeng.com/a/20180802/45098280_0.shtml) - -# 书籍简单读后感 -- [ ] Linux Shell 脚本攻略 -- [ ] 编写可读代码的艺术 - - -``` -魏楚阳书单: - 《算法》(第四版)图以前章节 - 《剑指 Offer》 - 《Java 编程思想》 - 《Java多线程编程核心技术》(高洪岩 著) - 《Java 并发编程实战》 - 《深入理解 Java 虚拟机》 - 《Java 8 实战》 - 《鸟哥的 Linux 私房菜》 - 《MySQL 必知必会》 - 《Maven 实战》 - 《图解 HTTP》 - 《敏捷软件开发》 - 《架构探险-从零开始写 Java Web 框架》 - 《Spring 3.x 企业应用开发实战》 - 《Head First 设计模式》 - 《大型网站技术架构》(李智慧 著) - 《大型网站系统与 Java 中间件实践》 -``` - - -自己Kindle选择的书籍 -``` -大设计 -果壳中的宇宙 -时间简史 -重构 -编程之美 -高性能Mysql -HTTP权威指南 -``` - - - -## 计算机类 -### 算法 -#### 《数据结构与算法分析 Java语言描述》 -> Mark Allen Weiss - -`2017-08-17 10:54:13` -- 算法逻辑很清晰,讲解简单,就是习题有点难以掌控 - -******** -### 数据库 -#### 《Redis in action》 -`2017-08-15 21:27:53` -- 因为使用的是Python,刚开始没仔细学Python,所以就只是当字典去查阅了reids的命令使用,过段时间看完了再做评定 - -******** - -### Python -#### 《Python从入门到实践》 -> Eric Matthess - -`2017-08-15 21:14:09` -- 个人认为太简单了,语句太通俗,以至于讲不清楚原理,让人更纠结于表面,而且这么厚一本书,没有详细讲解过哪个知识点,都是用demo一笔带过 -- demo没有涉及到的,但是可能是常用的只字不提,让人觉得,这是一本从未接触编程的人适合去看的书,已经有其他语言基础的就不要去浪费时间了, -- 在语法的说明上,还不急一本应试书(二级考试Python教材),虽然一门语言的精神很重要,但是这也是要先熟悉语法先吧,一堆的语法错误学什么。。。 - -********************** -### Linux - -#### 《Linux命令行与Shell脚本编程大全》 -`2018-01-21 19:09:26` -- 这本是女票送的, 虽然到手很久了,还是没有仔细看, 但是评价和大致浏览都感觉条理清晰, 只是细节确实有点多, 日后慢慢学 - - -### 工具 -#### 《Docker实战》 -`2017-09-02 20:54:28` -- 一直拖着,两个月才开始真正的学习这本书,感觉就一个字懵,杂乱无章,看不清重点,知识零散,示例纰漏众多。示例没有讲解清楚,原理没有深入 -- 大致的了解docker还是可以的,扩宽了知识面,比较详细的扩展面 - -#### 《Docker全攻略》 -`2017-10-03 16:48:02` -- 在原理方便比较详尽,从源码去理解docker运行机制,进行最优使用,但是太详细了,所以要多看多理解 -- 目录特别的不清楚,查阅困难,出版时间较早,有些只适合docker1.7,命令的API解释比较详细,但是太多了没有凸显重点 -- 具有一定的阅读价值,但是最好是结合其他的最新docker书对比阅读学习 - -`2017-10-04 09:51:12` -- 关于原理性讲的很清楚,从底层去理解docker的各个方面以及特性,确实有种茅塞顿开的感觉。 - -`2017-10-09 08:49:15` -- 学习的更为深刻,关于原理的学习。 - -******** - -## 生活类 -### 时事 -#### 《活着》 -`2018-01-19 20:17:49` -- 只是感觉到那个年代的生命的艰辛, 身边的亲人一个个的离去, 但是主人公还是活下来了, 心里有念想,就活下来了. - - 离去: 福贵的父亲, 母亲, 有庆, 凤霞, 家珍, 二喜, 苦根, 最后只有福贵和一头老牛活下来了 - -### 杂志 -#### 《新周刊 2012年度佳作 做点无用的事》 -`2017-10-18 14:41:33` -- 作为一个快节奏生活的现在,很多事情身不由己啊, 大学时候可以做一些“无用”的事,毕业后你就要为了生存为了更好的生活去打拼了 -- 如果再做那些世俗认为的无用的事就会显得你不懂事,有病。真是悲哀,不能活出自己的想法,个人认为,主要矛盾就是没有履行义务 -- 如果处理好了家庭以及社会上的责任之后,自己一个人想做什么就不会太影响别人了,人生的意义也将由你自己主宰了。孰对孰错就不是重点了 -- 自己的人生,在自己手上握着,怎么走,要好好考虑一番了 - - -### 军事历史 -#### 《亮剑》 -> 都梁 - -`2017-08-27 10:53:24` -- 战争很让人感染,关于文化政治的冲击带来的思考也很多,后半段的历史让人深思啊, 现在仍是以自然灾害盖过,为什么犯过的错就可以不承认, 这是在宣告我们是愚民 - -## 艺术类 -### 生活 -#### 《一个人的朝圣》 - - -### 武侠 -#### 《鹿鼎记》 -`2018-03-10 23:28:22` -- 花了好几天, 终于看完了,感觉陈小春版的电视剧已经完全的还原了, 书里面的所有情节都有在电视剧上看到, 只不过现在看了原著能看到更多心理上的活动 - - 金庸的封笔作, 虽然说有很多粗俗的话语, 但是对于人物的刻画, 以及人情世故的把握, 确实很好 -- 韦小宝 全无半点英雄的气概和作风, 但是完成了很多英雄任务, 以及奸雄都做不到的事情, 虽然喜欢耍滑头, 但是忠义还是很看重的 - - 面对一个问题的时候不是正面的光明的解决他, 而是绕过去, 并实现自己的利益最大化 - -## 名著类 -### 历史 -#### 《史记》 - -#### 《西游记》 -> 如果倒过来看 [剪辑的视频](http://v.youku.com/v_show/id_XMTI5NDUyOTQ3Ng==.html?x&sharefrom=android) -``` -如来派师徒四人与八部天龙小白龙去东土大唐去传教,在一路上遇到了各种妖怪,打来打去发现他们都是有后台的,无论怎么作恶都不受惩罚, -八戒和沙僧觉得太黑暗了,无奈一个躲进了高老庄,一个钻进了流沙河,只有悟空坚持正义一路斩妖除魔护送师傅东去传教。 -结果天庭对悟空实在忍无可忍就和如来达成协议——我们可以保证唐三藏平安到长安,不过你得把孙悟空这个刺儿头给办了, -如来同意了,在一翻阴谋之下,白龙重伤坠入山涧,悟空败了,被压在了五指山下,而唐三藏却抛弃了孙悟空,孤身来到长安,在长安传完教,被封为御弟,享受完荣华富贵,寿终正寝。 -就这样过了五百年, 悟空终于从五指山下逃了出来, 一声不吭 ,把天庭搅了个天翻地覆, 天庭被逼无奈许诺让猪八戒化为人身,封为天蓬元帅,沙和尚封为卷帘大将,只要他们能够杀掉孙悟空。 -最后的最后,因为兄弟相残而心灰意冷的悟空去寻找菩提祖师解惑,然后他封印了修为,回到花果山,陪着猴子猴孙过完了平凡的一生,最终在花果山的山顶化作了一块石头…… -``` -> [知乎 西游记倒过来看](https://www.zhihu.com/question/36392042) ->> 作为政治通吃一切的国家,是无法容纳一个英雄的独立存在,不可能给你妄想拯救世界的企图。我国的历史传统就是一山不容二虎,你可以足够优秀,足够明智,足够坚实,但你不可以超过某些人。 diff --git a/Book/Work/Project.md b/Book/Work/Project.md deleted file mode 100644 index 9a6bc51..0000000 --- a/Book/Work/Project.md +++ /dev/null @@ -1,9 +0,0 @@ -`目录 start` - -- [工程相关](#工程相关) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 工程相关 - -[The cathedral and the bazaar](https://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar) diff --git a/Book/Work/Readme.md b/Book/Work/Readme.md deleted file mode 100644 index a0a3919..0000000 --- a/Book/Work/Readme.md +++ /dev/null @@ -1,24 +0,0 @@ -`目录 start` - - - [机器学习](#机器学习) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -- [单核工作法](http://www.ituring.com.cn/book/1925) - - [单核工作法图解](http://www.ituring.com.cn/book/1925) -- [程序员面试金典](http://www.ituring.com.cn/book/1010) - -- The Art of Readable Code - -- 程序员健康指南 - - -- GitHub实践 [美] Chris Dawson , [美] Ben Straub (作者) 安道 (译者) - - -### 机器学习 -- [Blog:机器学习经典书籍](http://suanfazu.com/t/topic/15#0-tsina-1-51417-397232819ff9a47a7b7e80a40613cfe1) - - -- [Pro Git](https://git-scm.com/book/zh/v2) `git 学习的书籍` -- [Gradle User Guide](https://www.gitbook.com/book/dongchuan/gradle-user-guide-/details) diff --git a/C/CBase.md b/C/CBase.md new file mode 100644 index 0000000..a362390 --- /dev/null +++ b/C/CBase.md @@ -0,0 +1,87 @@ +--- +title: C语言 +date: 2019-01-08 23:43:07 +tags: + - 基础 +categories: + - C +--- + +💠 + +- 1. [C语言](#c语言) + - 1.1. [GCC 环境](#gcc-环境) + - 1.2. [资源](#资源) +- 2. [基础](#基础) + - 2.1. [基本语法](#基本语法) + - 2.2. [数据类型](#数据类型) + - 2.3. [变量和常量](#变量和常量) + - 2.4. [函数](#函数) + - 2.4.1. [main函数](#main函数) +- 3. [抽象设计](#抽象设计) + - 3.1. [使用C实现元组](#使用c实现元组) + - 3.2. [使用C实现面向对象思想](#使用c实现面向对象思想) + +💠 2024-09-02 17:14:24 +**************************************** +# C语言 +> 个人入门编程语言, 个人觉得比用Python入门更好点, Python入门简单是不错, 但是后面如果要入手别的语言, 有C语言基础更好 + +> [Github: 个人学习记录](https://github.com/Kuangcp/LearnC) + +- The C Programming Language, Second Edition, Prentice Hall, 1988 + +> [Cosmopolitan](https://github.com/jart/cosmopolitan) `一次编译,处处执行` + +## GCC 环境 +- [mingw64](https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/) + +## 资源 +> [C 语言教程](https://www.runoob.com/cprogramming/c-tutorial.html) + +> [玩转 C语言 基础课堂](https://study.163.com/course/introduction.htm?courseId=334013#/courseDetail?tab=1) +> [C语言入门](https://www.imooc.com/learn/249) + +> [翁恺:程序设计入门——C语言](https://www.icourse163.org/course/ZJU-199001) + +************************** + +# 基础 +> 语言规范 +- [C语言代码规范(编程规范)](http://c.biancheng.net/view/158.html) + +## 基本语法 +- 顺序 + - 按语句声明顺序进行执行 +- 选择 + - if switch 等条件判断语句 导致代码执行到该行会选择要执行的语句或语句块 +- 循环 + - 语句或语句块 多次执行,依据某条件退出循环 或者依据某条件进行循环 如果条件永远无法满足或永远满足则循环将不停止 也就是死循环 + +## 数据类型 +> [C 数据类型](https://www.runoob.com/cprogramming/c-data-types.html) + +## 变量和常量 + +## 函数 + +### main函数 +> [参考: C语言main()函数详解](http://c.biancheng.net/cpp/html/725.html) + +- 返回类型: 推荐第一种 + 1. `int main(){return 0;}` + 1. `void main(){}` +- 入参: 为空或者接收参数 + 1. `int main(){return 0;}` 有隐式的入参 void + 1. `int main(int argc, char *args[]){return 0;}` + +************************ + +# 抽象设计 + +## 使用C实现元组 +> [making a tuple in c](https://stackoverflow.com/questions/22727404/making-a-tuple-in-c) + +## 使用C实现面向对象思想 +> [C语言:春节回家过年,我发现只有我没有对象!](https://mp.weixin.qq.com/s/TPZ7yO0sVoneY1ezGtWK2g) + diff --git a/Database/DataBase.md b/Database/DataBase.md new file mode 100644 index 0000000..c778a07 --- /dev/null +++ b/Database/DataBase.md @@ -0,0 +1,272 @@ +--- +title: 数据库基础 +date: 2018-12-16 17:25:06 +tags: + - 数据库 + - 工具使用经验 +categories: + - 数据库 +--- + +💠 + +- 1. [数据库](#数据库) + - 1.1. [事务](#事务) + - 1.1.1. [事务的并发问题](#事务的并发问题) + - 1.2. [数据库并发控制](#数据库并发控制) + - 1.3. [SQL 解析&审计](#sql-解析&审计) + - 1.3.1. [Slow SQL](#slow-sql) +- 2. [关系型和非关系型](#关系型和非关系型) +- 3. [关系型数据库](#关系型数据库) + - 3.1. [Mysql](#mysql) + - 3.2. [PolorDB](#polordb) + - 3.3. [Oracle](#oracle) + - 3.4. [PostgreSQL](#postgresql) +- 4. [非关系型数据库](#非关系型数据库) + - 4.1. [Redis](#redis) + - 4.2. [RocksDB](#rocksdb) + - 4.3. [LevelDB](#leveldb) + - 4.4. [MangoDB](#mangodb) + - 4.5. [GemFire](#gemfire) +- 5. [内置型数据库](#内置型数据库) + - 5.1. [SQLite](#sqlite) + - 5.2. [duckdb](#duckdb) +- 6. [关系型数据库设计](#关系型数据库设计) + - 6.1. [范式](#范式) + - 6.1.1. [1NF](#1nf) + - 6.1.2. [2NF](#2nf) + - 6.1.3. [3NF](#3nf) + - 6.1.4. [BCNF](#bcnf) + - 6.1.5. [4NF](#4nf) + - 6.2. [基本表的设计](#基本表的设计) + - 6.2.1. [关于主键的设计](#关于主键的设计) + - 6.3. [视图的设计](#视图的设计) +- 7. [大数据](#大数据) + - 7.1. [Greenplum](#greenplum) + - 7.2. [Clickhouse](#clickhouse) + - 7.3. [TiDB](#tidb) + - 7.4. [Ignite](#ignite) +- 8. [向量数据库](#向量数据库) +- 9. [图数据库](#图数据库) +- 10. [数据库中间件](#数据库中间件) +- 11. [图形化工具](#图形化工具) + +💠 2024-10-13 18:30:08 +**************************************** +# 数据库 +> [码农翻身:爱炫耀的数据库老头儿](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665514001&idx=1&sn=17b72c3e69db6c4277e3045c699b7b6b&chksm=80d67c52b7a1f5446020826841869221873f4578524181384592839d19c4810dc68807117e13&scene=21#wechat_redirect) `事务,undo日志` + +> [DB-Engines Ranking](https://db-engines.com/en/ranking) `数据库评分排行` +> [墨天轮](https://www.modb.pro/)`数据库社区` + +## 事务 + +> ACID +1. `原子性(Atomicity)` + - 事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节。 + - 事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样。也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位。 +1. `一致性(Consistency)` + - 事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。 +1. `隔离性(Isolation)` + - 同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。 +1. `持久性(Durability)` + - 事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。 + +### 事务的并发问题 +1. `脏读` + - 事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据 +1. `不可重复读` + - 事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。 +1. `幻读` + - 系统管理员A将数据库中所有学生的成绩从具体分数值改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录 + - 当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。 + +> 小结:不可重复读的和幻读很容易混淆,不可重复读侧重于`修改`,幻读侧重于`新增或删除`。解决不可重复读的问题只需`锁住满足条件的行`,解决幻读需要`锁表` + +> [Algorithm for Recovery and Isolation Exploiting Semantics (ARIES)](https://www.geeksforgeeks.org/algorithm-for-recovery-and-isolation-exploiting-semantics-aries/) `事务隔离和恢复算法` + +************************ + +## 数据库并发控制 + +MySQL: MVCC + +## SQL 解析&审计 +- [SQL解析在美团的应用](https://tech.meituan.com/2018/05/20/sql-parser-used-in-mtdp.html) +- [美团点评SQL优化工具SQLAdvisor](https://github.com/Meituan-Dianping/SQLAdvisor) + - [Docker 版本](https://github.com/maxiaolin3366/SQLAdvisor-web) + - [Blog](https://tech.meituan.com/2017/03/09/sqladvisor-pr.html) +- [see](https://github.com/myide/see) `基于开源组件(Inception & SQLAdvisor & SOAR)的SQL审核&SQL优化的Web平台` + +### Slow SQL +> [Getting Help With A Slow Query](https://www.brentozar.com/archive/2009/03/getting-help-with-a-slow-query/) + +> [基于代价的慢查询优化建议](https://tech.meituan.com/2022/04/21/slow-query-optimized-advice-driven-by-cost-model.html) + +************************ + +# 关系型和非关系型 +> [为什么说SQL正在击败NoSQL,这对数据的未来意味着什么?](http://www.infoq.com/cn/news/2017/10/SQL-NoSQL-mean-what?utm_source=news_about_rdbms&utm_medium=link&utm_campaign=rdbms) + +************************ + +# 关系型数据库 +> 代表性: Oracle, MySQL, PostgreSQL, SQL Server + +> [List of Relational Database Management Systems (RDBMSs)](https://database.guide/list-of-relational-database-management-systems-rdbms/) + +> [learndb-py](https://github.com/spandanb/learndb-py) + +## Mysql +> [MySQL](/Database/MySQL.md) + +## PolorDB +> [Doc](https://help.aliyun.com/product/58609.html) + +## Oracle +> [Official Site](https://www.oracle.com/database/) + +## PostgreSQL +> [Official Site](https://www.postgresql.org/) + +************************ + +# 非关系型数据库 +> key-value 数据库: redis memcached +> 文档数据库: MongoDB +> 图数据库: Neo4j tugraph-db JanusGraph PG扩展 +> 时序数据库: InfluxDB TSDB + +- [sssdb](https://github.com/ideawu/ssdb) `键值对数据库` + +## Redis +> [Redis](/Database/Redis.md)数据类型丰富,单线程纯内存高性能, 且久经考验很稳定 + +- [Github Tendis](https://github.com/Tencent/Tendis)`兼容Redis访问协议,腾讯开源的存储版,已不维护,商业还有缓存版和混合版` + - [ Redis vs Tendis:冷热混合存储版架构揭秘 ](https://mp.weixin.qq.com/s/MeYkfOIdnU6LYlsGb24KjQ) +- [Dragonfly](https://github.com/dragonflydb/dragonfly) 兼容Redis和Memcached的 API,高吞吐量 + - 无共享式架构和VLL的选择,不使用互斥锁或自旋锁的情况下组合原子的多键操作 + - docker run --network=host --ulimit memlock=-1 docker.dragonflydb.io/dragonflydb/dragonfly + - 虽然宣称更高吞吐量,但是拿[实际应用场景](https://github.com/Kuangcp/GoBase/tree/master/toolbox/countzh)做测试对比发现Redis比Dragonfly消耗资源少且更快 + - 场景为统计字符频率,只高频执行 ZIncrBy 命令(累计执行了337849次,Redis7.0.5 稳定耗时13s 单核30% Dragonfly 6.2.11稳定耗时19s 等效于单核60%CPU) +- [KeyDB](https://github.com/Snapchat/KeyDB) Redis 的一个高性能分支,专注于多线程、内存效率和高吞吐量 + + +## RocksDB +> [RocksDB](https://github.com/facebook/rocksdb)`FaceBook开源` + +## LevelDB +> [Github](https://github.com/google/leveldb) + +> [LedisDB](https://github.com/ledisdb/ledisdb) 基于LevelDB构建Redis协议的数据库实例 + +## MangoDB +> [MongoDB](/Database/MongoDB.md) 文档性数据库, 混合类型: 关系型非关系型 + +## GemFire +> 分布式内存数据库 12306 采用的解决方案 + +************************ +# 内置型数据库 +> [Github: embedded-database](https://github.com/topics/embedded-database) + +## SQLite +> [Official Site](https://sqlite.org/index.html) + +1. 客户端 sqlitebrowser + +常见后缀 +- .db 数据文件 +- .db-wal 是写时日志[WAL](https://www.sqlite.org/wal.html) +- .db-shm 共享内存文件,只包含临时数据。 + +## duckdb +> [duckdb](https://duckdb.org/) in-process SQL OLAP Database Management System + +可基于CSV,JSON直接建表做数据分析 [CSV Import](https://duckdb.org/docs/data/csv/overview) + +************************ + +# 关系型数据库设计 +## 范式 +> 范式越高意味着数据冗余更低,表的划分更细,但是在查询数据时需要做大量表连接操作,会严重降低性能 + +1. 《数据库系统概论》 + +### 1NF +> 确保每列原子性 + +数据库表中的所有字段值都是不可分解的原子值 + +### 2NF +> 在1NF基础上,确保表中的每列都和主键相关,即在一个表中的字段都是仅构成一个实体,不可以把别的实体的字段放进来,会导致插入 删除 修改都很复杂 + +> 若 R∈1NF 且每一个非主属性完全函数依赖于任何一个候选码 则 R∈2NF + +所谓完全依赖是指不能存在仅依赖主关键字一部分的属性,如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。 + +### 3NF +> 在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖), 即引入`外键` + +1. 例如 学生成绩表 应该只存学号 课程id 成绩,不应存放学生信息,课程信息,能大大减少数据的冗余 + - 但是实际上为了系统的性能会做部分数据的冗余,例如改动较少的性别姓名等 + +### BCNF +Boyce-Codd Normal Form(巴斯-科德范式) + +> 在3NF基础上,任何非主属性不能对主键子集依赖(在3NF基础上消除对主码子集的依赖) + +### 4NF + +## 基本表的设计 +1. 应尽量避免 字段默认值和业务值发生重叠, 便于后期排查问题,减少一个值的含义 +1. 字段应尽量紧凑,达到业务要求的最小设计,利于索引和IO + +### 关于主键的设计 +> 为了不让数据库成为瓶颈,基本表中连主键的约束都不要了, 全部由后台的代码进行约束处理 + +- 如果使用的需要高并发,数据库经常迁移,拆分,分布式,使用UUID,GUID,雪花算法等。 +- 如果是小型项目,使用整型自增即可,排序方便节约内存 + +## 视图的设计 + + +************************ + +# 大数据 +## Greenplum +> [Official Site](https://cn.greenplum.org) + +## Clickhouse +> [Clickhouse](/Database/OLAP/Clickhouse.md) + +## TiDB +> [Official Doc](https://docs.pingcap.com/zh/) + +## Ignite +> [Github](https://github.com/apache/ignite) + +************************ +# 向量数据库 +- PostgreSQL: 支持向量插件 +- [milvus](https://milvus.io/) +- [chroma](https://github.com/chroma-core/chroma) + +> [向量数据库|一文全面了解向量数据库的基本概念、原理、算法、选型](https://cloud.tencent.com/developer/article/2312534) + +# 图数据库 +> [Note: 图数据库](/Database/Graph.md) + +*********************** + +# 数据库中间件 +> [MyCat:开源分布式数据库中间件](https://www.csdn.net/article/2015-07-16/2825228) + +# 图形化工具 + +- [Mysql-Font](https://github.com/NilsHoyer/MySQL-Front) `连接Mysql的客户端` +- [HeidiSQL](https://github.com/HeidiSQL/HeidiSQL) +- [sqlectron](https://github.com/sqlectron/sqlectron-gui) `简单直观的数据库图形化软件` +- [dbeaver](https://github.com/dbeaver/dbeaver) + - 配置文件 `/usr/share/dbeaver/dbeaver.ini` +- [dbgate](https://github.com/dbgate/dbgate) diff --git a/Database/Experience.md b/Database/Experience.md deleted file mode 100644 index 8374b3a2..0000000 --- a/Database/Experience.md +++ /dev/null @@ -1,68 +0,0 @@ -`目录 start` - -- [数据库的使用体会](#数据库的使用体会) - - [关系型数据库](#关系型数据库) - - [SQLServer](#sqlserver) - - [Mysql](#mysql) - - [Oracle](#oracle) - - [Postgresql](#postgresql) - - [非关系型数据库](#非关系型数据库) - - [Redis](#redis) - - [MangoDB](#mangodb) - - [两者的对比](#两者的对比) -- [关系型数据库设计](#关系型数据库设计) - - [结构设计](#结构设计) - - [基本表的设计](#基本表的设计) - - [关于主键的设计](#关于主键的设计) - - [视图的设计](#视图的设计) - - [数据库中间件](#数据库中间件) -- [非关系型数据库设计](#非关系型数据库设计) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 数据库的使用体会 -> [码农翻身:爱炫耀的数据库老头儿](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665514001&idx=1&sn=17b72c3e69db6c4277e3045c699b7b6b&chksm=80d67c52b7a1f5446020826841869221873f4578524181384592839d19c4810dc68807117e13&scene=21#wechat_redirect) `事务,undo日志` - -> [DB-Engines Ranking](https://db-engines.com/en/ranking) `数据库评分排行` - -## 关系型数据库 -> [参考博客: 什么是数据库ACID?](http://www.jdon.com/concurrent/acid-database.html) - -### SQLServer -### Mysql -> 结合docker配置很快,就是默认编码为什么不直接设置utf8,每次要改 - -### Oracle -> 十分的庞大, 学习了他理念的设计, 感受良多 - -### Postgresql -> 听说性能强劲, 但是自己没有测试实践过, 命令行倒是很简洁, 就是数据库的逻辑理念和MySQL不一样, 迁移过去要稍微看下基础 -> 实际使用上, 如果使用Docker开一个容器运行, 所消耗的资源比MySQL少多了, MySQL大概 300M PostgreSQL只要几十M - -## 非关系型数据库 - -### Redis -> 数据类型丰富,处理非关系型并且结构化的数据十分方便, 结合Python使用就行云流水一般了 - -### MangoDB -> 正准备学习的文档性数据库, 混合类型: 关系型非关系型 - -## 两者的对比 -> [为什么说SQL正在击败NoSQL,这对数据的未来意味着什么?](http://www.infoq.com/cn/news/2017/10/SQL-NoSQL-mean-what?utm_source=news_about_rdbms&utm_medium=link&utm_campaign=rdbms) -*********************** -# 关系型数据库设计 -## 结构设计 -### 基本表的设计 -#### 关于主键的设计 -> 我哥提出, 基本表中连主键的约束都不要了, 全部由后台的代码进行约束处理 - -- 如果使用的需要高并发,数据库经常迁移,拆分,分布式,使用UUID,GUID最佳 -- 如果是小型项目,使用整型自增即可,排序方便节约内存 - -### 视图的设计 - -## 数据库中间件 -> [MyCat:开源分布式数据库中间件](https://www.csdn.net/article/2015-07-16/2825228) - -# 非关系型数据库设计 - diff --git a/Database/Graph.md b/Database/Graph.md new file mode 100644 index 0000000..c122535 --- /dev/null +++ b/Database/Graph.md @@ -0,0 +1,51 @@ +--- +title: Graph +date: 2024-10-13 18:28:55 +tags: +categories: +--- + +💠 + +- 1. [图数据库](#图数据库) +- 2. [概念](#概念) +- 3. [QL 查询语言](#ql-查询语言) + - 3.1. [Cypher](#cypher) + - 3.2. [Gremlin](#gremlin) + +💠 2024-10-14 19:26:20 +**************************************** +# 图数据库 + +Neo4j、OrientDB、ArangoDB、JanusGraph、HugeGraph、Dgraph、TigerGraph + +> [DB-Engines Ranking - popularity ranking of graph DBMS](https://db-engines.com/en/ranking/graph+dbms) + +# 概念 + +一个属性图是有向图,由顶点(Vertex),边(Edge),标签(Lable),关系类型(Relationship Type)和属性(Property)组成。 + +在属性图形中,节点和关系是最重要的实体,顶点也称作节点(Node),边也称作关系(Relationship)。 +所有的节点是独立存在的,但是可以为节点设置标签,那么拥有相同标签的节点属于一个分组,也就是一个集合。关系通过关系类型来分组,类型相同的关系属于同一个集合。 +节点可以有0个、1个或多个标签,但是关系必须设置关系类型,并且只能设置一个关系类型。 + +关系是有向的,关系的两端是起始节点和结束节点,通过有向的箭头来标识方向,节点之间的双向关系通过两个方向相反的关系来标识。 + +************************ + +# QL 查询语言 +> [Neo4j - Cypher vs Gremlin query language - Stack Overflow](https://stackoverflow.com/questions/13824962/neo4j-cypher-vs-gremlin-query-language) +> [opencypher/cypher-for-gremlin](https://github.com/opencypher/cypher-for-gremlin) + +## Cypher +> [Cypher Cheat Sheet](https://neo4j.com/docs/cypher-cheat-sheet/5/aura-dbe/)`使用手册` +> [albertoventurini/graphdb-intellij-plugin](https://github.com/albertoventurini/graphdb-intellij-plugin) + +```c + MATCH (n) RETURN (n) + -- 查询 疾病 关联的 所有病征 + MATCH (d:疾病)-[:疾病的症状]->(s:疾病症状) WHERE d.名称 = '血栓形成' RETURN s +``` + +## Gremlin +> [Gremlin中文文档](https://tinkerpop-gremlin.cn/#traversal) diff --git a/Database/MangoDB.md b/Database/MangoDB.md deleted file mode 100644 index 1c2d49f..0000000 --- a/Database/MangoDB.md +++ /dev/null @@ -1,11 +0,0 @@ -`目录 start` - -- [MangoDB](#mangodb) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# MangoDB -> 非关系型和关系型混合文档型数据库 - -> [双刃剑MongoDB的学习和避坑](https://segmentfault.com/a/1190000013589617) - diff --git a/Database/Memcached.md b/Database/Memcached.md new file mode 100644 index 0000000..cb7e5ad --- /dev/null +++ b/Database/Memcached.md @@ -0,0 +1,15 @@ +--- +title: Memcache +date: 2019-05-13 11:15:40 +tags: +categories: +--- + +**目录 start** + +1. [Memcache](#memcache) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Memcached +> [memcached.org](https://memcached.org/) | [Github](https://github.com/memcached/memcached) diff --git a/Database/MongoDB.md b/Database/MongoDB.md new file mode 100644 index 0000000..01e25c2 --- /dev/null +++ b/Database/MongoDB.md @@ -0,0 +1,36 @@ +--- +title: MangoDB +date: 2018-12-16 17:26:05 +tags: + - MongoDB + - 基础 +categories: + - 数据库 +--- + +**目录 start** + +1. [MongoDB](#mongodb) + 1. [安装](#安装) + 1. [客户端](#客户端) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# MongoDB +> 非关系型和关系型混合文档型数据库 + +> [双刃剑MongoDB的学习和避坑](https://segmentfault.com/a/1190000013589617) + +> [release-notes](https://docs.mongodb.com/manual/release-notes/) + +> [参考: 数据存储设计](http://source.wiredtiger.com/10.0.0/tune_page_size_and_comp.html)`Mongo同样使用B+Tree` + +## 安装 +### 服务端 +1. [Docker 安装](https://hub.docker.com/_/mongo/) + +### 客户端 +> [参考: 最佳的MongoDB客户端管理工具](https://blog.csdn.net/chszs/article/details/51348248) + +1. MongoDB自带的Shell +1. [Robo 3T](https://robomongo.org/) diff --git a/Database/MongoDBAdvance.md b/Database/MongoDBAdvance.md new file mode 100644 index 0000000..48bfbed --- /dev/null +++ b/Database/MongoDBAdvance.md @@ -0,0 +1,7 @@ +# MongoDB + +## Storage +> [Official Doc](https://docs.mongodb.com/manual/storage/) + +- [WiredTiger data engine](http://source.wiredtiger.com) + diff --git a/Database/MySQL.md b/Database/MySQL.md index 7009494..437313e 100644 --- a/Database/MySQL.md +++ b/Database/MySQL.md @@ -1,71 +1,98 @@ -`目录 start` - -- [Mysql](#mysql) - - [安装](#安装) - - [Ubuntu安装配置MySQL](#ubuntu安装配置mysql) - - [Docker安装](#docker安装) - - [图形化客户端](#图形化客户端) -- [基本数据类型](#基本数据类型) - - [数值类型](#数值类型) - - [short](#short) - - [int](#int) - - [decimal](#decimal) - - [字符类型](#字符类型) - - [varchar](#varchar) - - [text](#text) - - [LongBlob](#longblob) -- [基本组成](#基本组成) - - [数据编码](#数据编码) - - [数据库](#数据库) - - [创建](#创建) - - [导出](#导出) - - [修改](#修改) - - [表](#表) - - [创建](#创建) - - [修改表定义](#修改表定义) - - [增删字段](#增删字段) - - [视图](#视图) - - [触发器](#触发器) - - [【创建单语句的触发器】](#创建单语句的触发器) - - [【创建多语句的触发器】](#创建多语句的触发器) - - [【NEW 和 OLD关键字】](#new-和-old关键字) - - [存储过程](#存储过程) - - [基本结构示例:](#基本结构示例) - - [函数](#函数) - - [【简单示例】](#简单示例) -- [1.mysql常用命令集合](#1mysql常用命令集合) - - [查看数据库参数](#查看数据库参数) - - [查看连接状况](#查看连接状况) - - [1.1【自增长】](#11自增长) - - [1.2【主键约束的修改】](#12主键约束的修改) - - [1.3【修改表名】](#13修改表名) - - [1.4【定界符】](#14定界符) - - [1.5【已有表数据,新建表】](#15已有表数据新建表) - - [1.6【查看所有连接状态】](#16查看所有连接状态) - - [1.7【查看表的状态】](#17查看表的状态) - - [1.8【关于时间 】](#18关于时间-) - - [1.8.1【常用函数】](#181常用函数) - - [1.8.2【获取当前时间与i个月之间的天数】](#182获取当前时间与i个月之间的天数) - - [1.8.3 datetime和timestamp区别](#183-datetime和timestamp区别) - - [1.9 【插入外码】](#19-插入外码) -- [2.【变量】](#2变量) -- [3.【基本流程语法】](#3基本流程语法) -- [7.【异常】](#7异常) -- [8.【用户管理】](#8用户管理) - - [查看](#查看) - - [创建](#创建) - - [修改](#修改) - - [【授权】](#授权) -- [查询](#查询) - -`目录 end` |_2018-09-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: MySQL +date: 2018-12-16 17:24:50 +tags: + - MySQL + - 基础 +categories: + - 数据库 +--- + +💠 + +- 1. [Mysql](#mysql) + - 1.1. [规约](#规约) +- 2. [安装](#安装) + - 2.1. [Ubuntu安装配置MySQL](#ubuntu安装配置mysql) + - 2.2. [Docker安装](#docker安装) + - 2.3. [图形化客户端](#图形化客户端) + - 2.4. [命令行辅助工具](#命令行辅助工具) +- 3. [基本数据类型](#基本数据类型) + - 3.1. [数值类型](#数值类型) + - 3.1.1. [short](#short) + - 3.1.2. [int](#int) + - 3.1.3. [decimal](#decimal) + - 3.2. [时间类型](#时间类型) + - 3.3. [字符类型](#字符类型) + - 3.3.1. [varchar](#varchar) + - 3.3.2. [text](#text) + - 3.3.3. [JSON](#json) + - 3.4. [LongBlob](#longblob) +- 4. [数据库](#数据库) + - 4.1. [创建](#创建) + - 4.2. [修改](#修改) + - 4.3. [导出和导入](#导出和导入) +- 5. [表](#表) + - 5.1. [创建](#创建) + - 5.2. [ALTER](#alter) + - 5.2.1. [增删字段](#增删字段) + - 5.3. [索引](#索引) + - 5.4. [临时表](#临时表) +- 6. [视图](#视图) +- 7. [触发器](#触发器) + - 7.1. [创建单语句的触发器](#创建单语句的触发器) + - 7.2. [创建多语句的触发器](#创建多语句的触发器) + - 7.3. [NEW 和 OLD关键字](#new-和-old关键字) +- 8. [存储过程](#存储过程) + - 8.1. [基本结构示例](#基本结构示例) +- 9. [函数](#函数) + - 9.1. [简单示例](#简单示例) +- 10. [常用命令集合](#常用命令集合) + - 10.1. [查看数据库参数](#查看数据库参数) + - 10.1.1. [查看连接状况](#查看连接状况) + - 10.2. [自增长](#自增长) + - 10.3. [主键约束的修改](#主键约束的修改) + - 10.4. [修改表名](#修改表名) + - 10.5. [定界符](#定界符) + - 10.6. [关于时间](#关于时间) + - 10.6.1. [常用函数](#常用函数) + - 10.6.2. [获取当前时间与n个月之间的天数](#获取当前时间与n个月之间的天数) + - 10.6.3. [datetime和timestamp区别](#datetime和timestamp区别) + - 10.7. [插入外码](#插入外码) +- 11. [变量](#变量) +- 12. [基本流程语法](#基本流程语法) +- 13. [异常](#异常) +- 14. [用户管理](#用户管理) + - 14.1. [查看](#查看) + - 14.2. [创建](#创建) + - 14.3. [修改](#修改) + - 14.3.1. [授权](#授权) + +💠 2024-10-09 16:33:39 **************************************** - # Mysql -> [MySQL官方下载地址](https://dev.mysql.com/downloads/mysql/) | [doc](https://dev.mysql.com/doc/) +> [Official Download](https://dev.mysql.com/downloads/mysql/) | [Official Doc](https://dev.mysql.com/doc/) + +> [key words](https://dev.mysql.com/doc/mysqld-version-reference/en/keywords-5-7.html) + +> 注意: utf8 最大字节为3, 非标准意义上的 utf8 实现, utf8mb4 才是真正意义上的 utf8 `5.5.3才开始支持` utf8 一般情况不会出问题, 除非有 emoji 等等 -## 安装 -### Ubuntu安装配置MySQL +> 书籍 +- MySQL技术内幕: InnoDB存储引擎 +- 高性能MySQL +- [MySQL 是怎样运行的:从根儿上理解 MySQL](https://juejin.cn/book/6844733769996304392) + +## 规约 +- 优先选择utf8字符集,需要存储emoji字符的,则选择utf8mb4字符集。不要单独定义字符集、校验集、存储引擎、行格式。 + - CREATE TABLE ... ENGINE = INNODB DEFAULT CHARSET = utf8 ROW_FORMAT = COMPACT,尽量不要单独指定这些选项。不同的字符集/校验集关联查询会导致索引失效,5.6、5.7默认的ROW_FORMAT不同,最好让其自行匹配当前版本。 +- 小数类型为 decimal,禁止使用 float 和 double。 +- varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率 +- 字段允许适当冗余,以提高查询性能,但必须考虑数据一致。冗余字段应遵循: + - 不是频繁修改的字段。 + - 不是 varchar 超长字段,更不能是 text 字段。 + +# 安装 +## Ubuntu安装配置MySQL - 更新列表` sudo apt-get update ` - 安装MySQL `sudo apt-get install mysql-server mysql-client` - 检查服务是否已经开启 : `sudo netstat -tap | grep mysql ` @@ -75,28 +102,42 @@ _配置_ - 打开配置文件: `sudo gedit /etc/mysql/mysql.conf.d/mysqld.cnf` - - `[mysqld]`下添加一行: `character-set-server=utf8` - - `[client]`下添加 `default-character-set = utf8` - 如果要允许远程访问,就注释掉 `bind-address` - 如果是服务器要配置远程访问 就 bind-address=服务器IP - 确保skip-networking被删除或者屏蔽,否则不支持TCP/IP 访问 +```ini +[mysqld] +character-set-server=utf8 +[client] +default-character-set = utf8 +``` + _重启_ - 重启MySQL :`sudo systemctl restart mysql` -### Docker安装 ->[Docker安装MySQL](/Linux/Container/Container/DockerSoft.md#mysql) | [博客:Mysql有没有必要Docker化](http://www.infoq.com/cn/articles/can-mysql-run-in-docker?utm_campaign=rightbar_v2&utm_source=infoq&utm_medium=articles_link&utm_content=link_text) +- 命令行连接 +> mysql -h host -P port -u username -p'password' database + +## Docker安装 +> [Docker安装MySQL](/Linux/Container/DockerSoft.md#MySQL) | [博客:Mysql有没有必要Docker化](http://www.infoq.com/cn/articles/can-mysql-run-in-docker) + +## 图形化客户端 +> Windows平台上 MySQL-Font HeidiSQL | [10个Mysql图形客户端](http://www.linuxidc.com/Linux/2015-01/111421.htm) -### 图形化客户端 -> windows上就直接 MySQL-Font HeidiSQL Linux就终端了..虽然wine也能装这俩 | [10个Mysql图形客户端](http://www.linuxidc.com/Linux/2015-01/111421.htm) +## 命令行辅助工具 +> [mycli](https://github.com/dbcli/mycli) `自动补全功能` ******************************** # 基本数据类型 > [MySQL 数据类型](http://www.cnblogs.com/bukudekong/archive/2011/06/27/2091590.html) + ## 数值类型 ### short + ### int -### decimal + +### decimal - The declaration syntax for a DECIMAL column is DECIMAL(M,D). The ranges of values for the arguments are as follows: - M is the maximum number of digits (the precision). It has a range of 1 to 65. - D is the number of digits to the right of the decimal point (the scale). It has a range of 0 to 30 and must be no larger than M. @@ -104,58 +145,96 @@ _重启_ 1. 当插入的整数部分的值超过了其表示范围后就直接忽略了小数部分的值,并以最大值填充。 2. 当整数部分合法,小数部分多余的位数,直接截断。 +## 时间类型 +- bigint 存入时间戳 +- date +- time +- datetime +- timestamp + +> 注意 只有 timestamp 是含时区信息的,因为客户端写入值时取会话时区转换为UTC值(例如 1997-07-16T19:20+08:00 ),查询时MySQL会从UTC转为客户端会话的时区。 +> datetime类型更像是存储了格式化的字符串。 + +> [MySQL日期类型选择建议](https://javaguide.cn/database/mysql/some-thoughts-on-database-storage-time.html) +- 空间效率timestamp更好,但是最大值到2038年,bigint可读性差但是兼容性好时区处理留给了应用层。 + + +> 报错: Zero date value prohibited +- MySQL数据库在面对0000-00-00 00:00:00日期的处理时,如果没有设置对应的对策,就会产生异常 + - 可以配置处理 策略 exception 异常(默认值), round 近似值, convertToNull(转为null) + - 例如 JDBC URL添加参数 zeroDateTimeBehavior=convertToNull + +************************ + ## 字符类型 ### varchar ### text -- [ ] 后期完善 +### JSON +> [The JSON Data Type](https://dev.mysql.com/doc/refman/8.4/en/json.html) + ## LongBlob - 这种数据类型可以直接把图像文件存到数据库中! 创建UTF8编码数据库 `CREATE DATABASE `test2` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci` ***************************** -# 基本组成 -## 数据编码 -utf8 最大字节为3的非标准utf8实现 -utf8mb4 才是真正意义上的 utf8 `5.5.3才开始支持` +# 数据库 -## 数据库 -### 创建 +## 创建 > create database name; -### 导出 +## 修改 +- 转换表所有字段编码 `alter table a convert to character set utf8mb4;` +- 修改单个字段编码 `alter table a modify name varchar(100) character set utf8mb4;` + +## 导出和导入 > 以下的 -p -h 参数依数据库的配置情况而定 1. 只导出数据库的结构 `mysqldump -uroot -pmysql -d dbname > /data/backup/sql/dbname.sql` - - 导出具体的表就在数据库名后加上 表名 -2. 导出结构和数据就去掉-d参数, 导出具体的表同理 -3. 导入就是执行这个SQL文件就行了 `source /path/to/dbname.sql` + - 导出具体的表就在 数据库名 后加上 表名 + - 导出结构和数据 去掉-d参数 +1. 导入 + - 执行SQL文件 `source /path/to/dbname.sql` 特别注意文件的路径问题, 是以MySQL客户端运行时的路径为根路径的 + - 或者 `mysql -uusername -ppassword database < /path/sqlfile.sql;` + +> [数据库迁移 Java工具的实现](https://blog.csdn.net/EASYgoing00/article/details/72885280) 主要的思路是Java调用系统命令行执行命令后得到导出文件, 然后读取导出的文件 进一步操作 -> [java操作:mysql数据库导入、导出](https://blog.csdn.net/EASYgoing00/article/details/72885280) 主要的思想是Java调用系统命令行执行命令后得到导出文件, 然后读取导出的文件 进一步操作 +大数据量表的导出: 常规使用分页分批加载到Excel中,改进版则使用长连接,流查询方式加载数据 -### 修改 -- [ ] TODO 修改数据库 +************************ -## 表 -### 创建 +# 表 +## 创建 - `create table name (field int, field varchar(32)....);` - 查看表的创建语句 `show create table name;` -### 修改表定义 -- [ ] TODO 修改表格 +## ALTER +> [Official Doc](https://dev.mysql.com/doc/refman/5.7/en/alter-table.html) _重命名表格_ `RENAME TABLE old TO new ` -#### 增删字段 +### 增删字段 - 增加字段 `alter table name add field1 int, field2 varchar(20);` - 删除字段 `alter table name drop column field1, drop column field2;` +- 重命名字段 `alter table name change old_name new_name bigint;` + +************************ +## 索引 +> [MySQL Index](/Database/MySQLIndex.md) + +## 临时表 +> [CREATE TEMPORARY TABLE](https://dev.mysql.com/doc/refman/8.0/en/create-temporary-table.html) -************** -## 视图 +场景: 依据计算需要,从原始表过滤聚合后的数据放入临时表,不同的交互流程后,将临时表删除。是否可在Session级别无感实现。 +方案: + +************************ + +# 视图 > 保障数据安全性,提高查询效率 -> [参考博客: ](http://www.jb51.net/article/36363.htm) +> [参考: ](http://www.jb51.net/article/36363.htm) ```sql CREATE [ALGORITHM]={UNDEFINED|MERGE|TEMPTABLE}] VIEW 视图名 [(属性清单)] @@ -173,12 +252,12 @@ _重命名表格_ `RENAME TABLE old TO new ` - LOCAL:更新视图时,要满足该视图本身定义的条件即可 > tips:创建试图时最好加上WITH CASCADED CHECK OPTION参数,这种方式比较严格,可以保证数据的安全性 -## 触发器 -### 【创建单语句的触发器】 +# 触发器 +## 创建单语句的触发器 - `CREATE TRIGGER ins_sum BEFORE INSERT ON account FOR EACH ROW SET @sum = @sum + NEW.amount;` - `CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt` -### 【创建多语句的触发器】 +## 创建多语句的触发器 ```sql CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW @@ -186,7 +265,7 @@ _重命名表格_ `RENAME TABLE old TO new ` ....... END ``` -### 【NEW 和 OLD关键字】 +## NEW 和 OLD关键字 - 使用OLD和NEW关键字,能够访问受触发程序影响的行中的列(OLD和NEW不区分大小写)。在INSERT触发程序中,仅能使用NEW.col_name,没有旧行。 - 在DELETE触发程序中,仅能使用OLD.col_name,没有新行。在UPDATE触发程序中,可以使用OLD.col_name来引用更新前的某一行的列,也能使用NEW.col_name来引用更新后的行中的列。 - 用OLD命名的列是只读的。你可以引用它,但不能更改它。对于用NEW命名的列,如果具有SELECT权限,可引用它。 @@ -194,10 +273,12 @@ _重命名表格_ `RENAME TABLE old TO new ` - 你可以使用触发程序来更改将要插入到新行中的值,或用于更新行的值。 - 在BEFORE触发程序中,AUTO_INCREMENT列的NEW值为0,不是实际插入新记录时将自动生成的序列号。 -## 存储过程 -### 基本结构示例: +************************ + +# 存储过程 +## 基本结构示例 ```sql - -- 【loop】 要有iterate 和leave才是完整的 + -- loop 要有iterate 和leave才是完整的 CREATE PROCEDURE doiterate(p1 INT) BEGIN label1: LOOP @@ -211,8 +292,10 @@ _重命名表格_ `RENAME TABLE old TO new ` select @x; ``` -## 函数 -### 【简单示例】 +************************ + +# 函数 +## 简单示例 ```sql ---函数部分,修改定界符 @@ -234,38 +317,39 @@ _重命名表格_ `RENAME TABLE old TO new ` select fun_test(8,'d'); ``` -*********************************** -# 1.mysql常用命令集合 +************************ + +# 常用命令集合 ## 查看数据库参数 ### 查看连接状况 > [查看mysql数据库连接数、并发数相关信息。](https://blog.csdn.net/caodongfang126/article/details/52764213)`show status like 'Threads%';` -## 1.1【自增长】 -- 【创建表时设置自增长,并设置起始值】 +- 查看连接 show processlist 如果是普通用户,只能查看自己当前的连接状态 +- 查看表的状态 show table status like 'assitant' 可以看到当前自动增长的id当前值 + +## 自增长 +- 创建表时设置自增长,并设置起始值 - create table cc( id int auto_increment,name varchar(20),primary key(id) ) auto_increment=1000; -- 【设置已有字段自增长】 +- 设置已有字段自增长 - alter table test MODIFY id INT UNSIGNED AUTO_INCREMENT; -- 【自增长的修改】 +- 自增长的修改 - alter table test auto_increment=10; 注意只能改的比当前的值大,不可以改的比当前小 -- 【自增长字段溢出】 +- 自增长字段溢出 - 设置自动增长的列,只能是int类型(包含了各种int),当出现了溢出就可以改成bigint 但是如果有外键约束,可能就会更改失败,还不如删库重建,实在太大了就删约束再建约束 -## 1.2【主键约束的修改】 +## 主键约束的修改 alter table 表名 add constraint (PK_表名) primary key (j,k,l); 关于一些约束条件constraint好像没有起到作用比如 check -## 1.3【修改表名】 + +## 修改表名 rename table table1 to table2; 切记不可随便修改表名,改了就要修改相应的 外键,触发器,函数,存储过程!!! -## 1.4【定界符】 + +## 定界符 delimiter 任意字符除了转义字符:\ -## 1.5【已有表数据,新建表】 -create table temp as select * from test; -## 1.6【查看所有连接状态】 -show processlist 如果是普通用户,只能查看自己当前的连接状态 -## 1.7【查看表的状态】 -show table status like 'assitant' 可以看到当前自动增长的id当前值 dev.mysql.com/downloads/mysql/#downloads - -***** -## 1.8【关于时间 】 -### 1.8.1【常用函数】 + +************************ + +## 关于时间 +### 常用函数 - **NOW()**函数以 'YYYY-MM-DD HH:MM:SS' 返回当前的日期时间,可以直接存到**DATETIME**字段中。 - **CURDATE()**以’YYYY-MM-DD’的格式返回今天的日期,可以直接存到**DATE**字段中。 - **CURTIME()**以’HH:MM:SS’的格式返回当前的时间,可以直接存到**TIME**字段中。 @@ -274,32 +358,30 @@ show table status like 'assitant' 可以看到当前自动增长的id当前值 d - select datediff(curdate(), date_sub(curdate(), interval i month)); - 一般函数是不能作为 default默认值的,使用只能在插入修改数据时使用 -### 1.8.2【获取当前时间与i个月之间的天数】 -- 问题:假设当前是5月19 且(提前月份)i=1 就是计算从4月19到今天的天数 - - 解答: - -```sql - -- 时间格式的简单操作: - select DATE_FORMAT(produceDate, '%Y') as yeahr from historybarcodesort - where DATE_FORMAT(produceDate, '%Y')='2013' - select date_format('1997-10-04 22:23:00','%y %M %b %D %W %a %Y-%m-%d %H:%i:%s %r %T'); - 显示结果:97 October Oct 4th Saturday Sat 1997-10-04 22:23:00 10:23:00 PM 22:23:00 - -- 查询指定时间: - get_date = "2006-12-07" - SELECT count(*) FROM t_get_video_temp Where DATE_FORMAT(get_date, '%Y-%d')='2006-07'; - SELECT count(*) FROM t_get_video_temp Where get_date like '2006%-07%'; -``` -### 1.8.3 datetime和timestamp区别 +### 获取当前时间与n个月之间的天数 +- 问题:假设当前是5月19 且(提前月份)n=1 就是计算从4月19到今天的天数 + ```sql + -- 时间格式的简单操作: + select DATE_FORMAT(produceDate, '%Y') as yeahr from historybarcodesort + where DATE_FORMAT(produceDate, '%Y')='2013' + select date_format('1997-10-04 22:23:00','%y %M %b %D %W %a %Y-%m-%d %H:%i:%s %r %T'); + 显示结果:97 October Oct 4th Saturday Sat 1997-10-04 22:23:00 10:23:00 PM 22:23:00 + -- 查询指定时间: + get_date = "2006-12-07" + SELECT count(*) FROM t_get_video_temp Where DATE_FORMAT(get_date, '%Y-%d')='2006-07'; + SELECT count(*) FROM t_get_video_temp Where get_date like '2006%-07%'; + ``` + +### datetime和timestamp区别 ```sql - -- 问题:为什么 5.5的环境下运行两句命令得到不同的结果(5.6不会有错误) - -- 没错误 - creata table test1(one_time timestamp not null default current_timestamp,two_time timestamp); - -- 报错:Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause - create table test2(one_time timestamp,two_time timestamp not null default current_timestamp); - 或者 将timestamp 改成datetime 也不会有错,那么问题来了 区别是什么? - -- 上面报错原因不明,大意是只能有一个timestamp的列有默认值 - + -- 问题:为什么 5.5的环境下运行两句命令得到不同的结果(5.6不会报错) + creata table test1(one_time timestamp not null default current_timestamp,two_time timestamp); + -- 报错:Incorrect table definition; there can be only one TIMESTAMP column with CURRENT_TIMESTAMP in DEFAULT or ON UPDATE clause + create table test2(one_time timestamp,two_time timestamp not null default current_timestamp); + 或者 将timestamp 改成datetime 也不会有错,那么问题来了 区别是什么? + -- 上面报错原因不明,大意是只能有一个timestamp的列有默认值 ``` + **DATETIME、DATE 和 TIMESTAMP 区别:** - **DATETIME** 类型可用于需要同时包含日期和时间信息的值。MySQL以'YYYY-MM-DD HH:MM:SS' 格式检索与显示DATETIME类型。 - 支持的范围是 '1000-01-01 00:00:00' 到 '9999-12-31 23:59:59'。 @@ -309,12 +391,12 @@ show table status like 'assitant' 可以看到当前自动增长的id当前值 d - **TIMESTAMP** 列类型提供了一种类型,通过它你可以以当前操作的日期和时间自动地标记 Insert 或Update 操作。 - 如果一张表中有多个 TIMESTAMP 列,只有第一个被自动更新。 ->“完整”TIMESTAMP格式是14位,但TIMESTAMP列也可以用更短的显示尺寸创造 +> “完整”TIMESTAMP格式是14位,但TIMESTAMP列也可以用更短的显示尺寸创造 最常见的显示尺寸是6、8、12、和14。 你可以在创建表时指定一个任意的显示尺寸,但是定义列长为0或比14大均会被强制定义为列长14 列长在从1~13范围的奇数值尺寸均被强制为下一个更大的偶数。 ->列如: +> 例如: 定义字段长度 强制字段长度 ``` TIMESTAMP(0) -> TIMESTAMP(14) @@ -322,7 +404,7 @@ TIMESTAMP(15)-> TIMESTAMP(14) TIMESTAMP(1) -> TIMESTAMP(2) TIMESTAMP(5) -> TIMESTAMP(6) ``` ->所有的TIMESTAMP列都有同样的存储大小, +> 所有的TIMESTAMP列都有同样的存储大小, 使用被指定的时期时间值的完整精度(14位)存储合法的值不考虑显示尺寸。 不合法的日期,将会被强制为0存储 @@ -336,12 +418,14 @@ TIMESTAMP(5) -> TIMESTAMP(6) - 以后当你对该记录行的其它列执行更新时,为 TIMESTAMP 列值明确地指定为它原来的值。 - 另一方面,你可能发现更容易的方法,使用 DATETIME 列,当新建记录行时以 NOW() 初始化该列,以后在对该记录行进行更新时不再处理它。 -## 1.9 【插入外码】 +## 插入外码 ```sql alter table `Bookinfo` add constraint `F_N` foreign key `F_N`(`classno`) references `Bookclass`(`classno`) on delete cascade on update cascade; ``` -********************************************* -# 2.【变量】 + +************************ + +# 变量 - 加了@ 的是用户变量, 限定当前用户,当前客户端, 在declare中声明的参数可以不加 @,那就是是局部变量 - 例如:declare a int ; 也可以直接就用不用声明,作为临时变量 例如这两种写法: - set @name = expr; @@ -349,17 +433,20 @@ alter table `Bookinfo` add constraint `F_N` foreign key `F_N`(`classno`) referen - 注意:MySQL中只有基本数据类型,没有Oracle中那个绑定类型:表类型或行类型,所以处理起来有点。。不如Oracle方便,不管是触发器还是存储过程 - set @a= select * from User;执行这句话就会报出 operand should contain 1 column(s)错误,就是说多值赋值的错误 -# 3.【基本流程语法】 +# 基本流程语法 ```sql if ... then elseif ... then (注意elseif中间没有空格) end if; ``` +************************ + +# 异常 -# 7.【异常】 +************************ -# 8.【用户管理】 +# 用户管理 > [参考博客](http://www.cnblogs.com/fslnet/p/3143344.html) ## 查看 @@ -386,7 +473,7 @@ alter table `Bookinfo` add constraint `F_N` foreign key `F_N`(`classno`) referen ## 修改 - 修改名字:`rename user feng to newuser;` -### 【授权】 +### 授权 1. grant all privileges ON databasename.tablename TO 'username'@'host' - all privileges 所有权限 - alter | alter routine @@ -402,13 +489,3 @@ alter table `Bookinfo` add constraint `F_N` foreign key `F_N`(`classno`) referen 2. 回收权限 revoke, 用法和 grant 一样 - 刷新权限缓存 `flush privileges;` - - -# 查询 -> 数据库中最主要的还是查询, 多角度复杂的查询 - -_全自段模糊查询_ -1. `select * from target where concat(ifnull(host, ''), ifnull(username, '')) like '%localhost%' > 0 limit 0,1;` - - 将全字段(空的替换为空串)连接成一个字符再模糊查询, -2. `select * from target where host like '%localhost%' or username like '%localhost%' limit 0,1;` - - 这种查询虽然也能实现, 但是性能差一些 diff --git a/Database/MySQLAdvance.md b/Database/MySQLAdvance.md index b1799c6..c6fbbf8 100644 --- a/Database/MySQLAdvance.md +++ b/Database/MySQLAdvance.md @@ -1,20 +1,240 @@ -`目录 start` - -- [MySQL Advanced](#mysql-advanced) - - [部署](#部署) - - [性能调优](#性能调优) +--- +title: MySQL进阶 +date: 2018-12-16 17:26:16 +tags: + - MySQL +categories: + - 数据库 +--- -`目录 end` |_2018-09-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [MySQL进阶](#mysql进阶) + - 1.1. [查询](#查询) + - 1.1.1. [SQL执行顺序](#sql执行顺序) + - 1.1.2. [性能优化场景](#性能优化场景) + - 1.1.3. [条件操作符](#条件操作符) + - 1.2. [事务](#事务) + - 1.2.1. [幻读](#幻读) + - 1.2.2. [事务隔离级别](#事务隔离级别) + - 1.2.3. [事务死锁](#事务死锁) + - 1.2.4. [隐含事务](#隐含事务) + - 1.3. [性能调优](#性能调优) + - 1.3.1. [Join](#join) + - 1.3.2. [查看状态变量](#查看状态变量) + - 1.4. [存储引擎](#存储引擎) + - 1.4.1. [InnoDB](#innodb) + - 1.4.2. [MyIsAM](#myisam) +- 2. [Tips](#tips) + - 2.1. [SQL 片段](#sql-片段) + +💠 2024-10-10 10:41:00 **************************************** -# MySQL Advanced +# MySQL进阶 +> [Github: MySQL Sever](https://github.com/mysql/mysql-server) + +> [Mysql 5.7.35 源码解释](https://github.com/shockerli/mysql-annotated-5.7.35) +> [参考: shell 下执行mysql 命令](http://www.cnblogs.com/wangkangluo1/archive/2012/04/27/2472898.html) + +> [JavaGuide: mysql](https://github.com/Snailclimb/JavaGuide/tree/main/docs/database/mysql) +> [图解MySQL介绍](https://xiaolincoding.com/mysql/) + +## 查询 +> [SQL通用优化方案(where优化、索引优化、分页优化、事务优化、临时表优化)](https://www.cnblogs.com/sochishun/p/7003513.html) +> [MySQL 索引](/Database/MySQLIndex.md) + +### SQL执行顺序 +> [SQL执行顺序(以MySQL为准)](https://segmentfault.com/a/1190000024577490) + +FROM, ON, JOIN,WHERE,GROUP BY,SUM,COUNT,HAVING,SELECT,DISTINCT,ORDER BY,LIMIT + +1. FROM:先去获取from里面的表,拿到对应的数据,生成虚拟表1。 +1. ON:对虚拟表1应用ON筛选,符合条件的数据生成虚拟表2。 +1. JOIN:根据JOIN的类型去执行相对应的操作,获取对应的数据,生成虚拟表3。 +1. WHERE:对虚拟表3的数据进行条件过滤,符合记录的数据生成虚拟表4。 +1. GROUP BY:根据group by中的列,对虚拟表4进行数据分组操作,生成虚拟表5。 +1. CUBE|ROLLUP(聚合函数使用):主要是使用相关的聚合函数,生成虚拟表6。 +1. HAVING:对虚拟表6的数据过滤,生成虚拟表7,这个过滤是在where中无法完成的,同时count(expr)返回不为NULL的行数,而count(1)和count(*)是会返回包括NULL在内的行数。 +1. SELECT:选择指定的列,生成虚拟表8。 +1. DISTINCT:数据去重,生成虚拟表9。 +1. ORDER BY:对虚拟表9中的数据进行指定列的排序,生成虚拟表10。 +1. LIMIT:取出指定行的记录,生成虚拟表11,返回给查询用户。 + +### 性能优化场景 +> 多字段模糊查询 +1. `select * from target where concat(ifnull(host, ''), ifnull(username, '')) like '%localhost%' > 0 limit 0,1;` + - 将多个字段(空的替换为空串)拼接成一个字符 或 提前拼接为一个新字段, 再模糊查询 +2. `select * from target where host like '%localhost%' or username like '%localhost%' limit 0,1;` + - 这种查询虽然也能实现, 但是性能差一些 + +> 分页查询性能优化 [MySQL分页查询的性能优化](https://www.cnblogs.com/scotth/p/7995856.html) +- 使用索引降低扫描总行数 +- 子查询法 +- 只查询索引内字段 + +1. 尽量少用 select *, 按需查询字段降低IO成本 +1. 尽量少用 or,同时尽量用 union all 代替 union + +### 条件操作符 +> [操作符](https://dev.mysql.com/doc/refman/8.0/en/non-typed-operators.html) + +> Tips +- [in](https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#operator_in) + - in的元素个数太多导致SQL长度超出 max_allowed_packet 参数值的问题 + - 类型强转 + - in 左侧表达式为null 或 右侧集合表达式为null时 该部分运算结果为null + - `AND id NOT IN (null)` 等价于 `AND id IN (null)` 等价于 `AND NULL` + +************************ + +## 事务 +- 当前会话隔离级别 + - 查看 select @@tx_isolation; + - 设置 SET TRANSACTION ISOLATION LEVEL repeatable read; +- 当前系统隔离级别 + - 查看 select @@global.tx_isolation; + - 设置 set global transaction isolation level repeatable read; + +[Doc 隔离级别](https://dev.mysql.com/doc/refman/8.0/en/innodb-transaction-isolation-levels.html) + +### 幻读 +> [Phantom Rows](https://dev.mysql.com/doc/refman/8.0/en/innodb-next-key-locking.html) + +### 事务隔离级别 +> [参考: MySQL的四种事务隔离级别](https://www.cnblogs.com/huanongying/p/7021555.html) + +InnoDB 默认隔离级别为 可重复读 + +| 事务隔离级别 | 脏读 | 不可重复读 | 幻读 | +|:---|:---:|:---:|:---:| +| 读未提交(read-uncommitted) | 会 | 会 | 会 | +| 提交读(read-committed) | | 会 | 会 | +| 可重复读(repeatable-read) | | | 会 | +| 串行化(serializable) | | | | + +需要结合InnoDB引擎具体的锁分析以上隔离级别产生和解决问题的方式 + +- `脏读` 同一事务内 读取到了其他未提交事务修改后的数据 +- `不可重复读` 同一事务内 前后多次读取,数据内容不一致 +- `幻读` 同一事务内 前后多次读取,数据总量不一致 + +************************ +InnoDB通过加间隙锁来防止幻读 + +> 可重复读 问题 +1. 当 事务T1, 对事务T2已提交数据A进行了修改,此时数据A 的 trx_id隐藏列就变成了T1事务id 此时 事务 T1 就能查出此条数据 + +### 事务死锁 + +> [deadlock](https://stackoverflow.com/questions/2332768/how-to-avoid-mysql-deadlock-found-when-trying-to-get-lock-try-restarting-trans) + +一个事务里 lock A lock B 另一个事务里 lock B lock A , 这时候两个事务都做了第一步, 然后做第二步会发生死锁 + +- 在业务层面上比较容易出现的场景 例如 + - 一个事务方法内更新两个用户的数据,一个线程先后更新 A B, 另一个线程 先后更新 B A, + - 此时如果能对 A B 做排序按相同的顺序做更新操作即可避免死锁 + - 一个事务方法更新A表 另一个事务方法 更新B表 A B 两个表有外键关联 然后两个方法更新的又恰好是关联的数据,因为 innodb引擎,更新A表也会锁住B表 从而导致死锁 -- [Mysql Redis UDF 复制](http://www.cnblogs.com/zhxilin/archive/2016/09/30/5923671.html) +### 隐含事务 +> 以下语句执行时会创建独立事务 -## 部署 -> 第一次看到MySQL内存上3G, 资源占用这么大, 还导致了内存不够, 直接MySQL自己退出了 +- DDL语句,ALTER DATABASE、ALTER EVENT、ALTER PROCEDURE、ALTER TABLE、ALTER VIEW、CREATE TABLE、DROP TABLE、RENAME TABLE、TRUNCATE TABLE等; +- 修改MYSQL架构的语句,CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD; +- 管理语句,ANALYZE TABLE、CACHE INDEX、CHECK TABLE、LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE等 +需要注意业务逻辑事务中不能包含这些语句,否则无法保证数据一致性,比如在线编辑表单的功能。 但是PG可以实现在同一事务内。 + +************************ ## 性能调优 -查看SQL运行效率: `explain` + SQL +> [Doc: Optimizing Queries with EXPLAIN](https://dev.mysql.com/doc/refman/5.7/en/using-explain.html)`依据 explain 输出结果调优` > [MySQL下INNODB引擎的SELECT COUNT(*)性能优化及思考](http://www.piaoyi.org/database/MySQL-INNODB-SELECT-COUNT.html) + +> `set max_execution_time=3000;`MySQL服务器设置SQL执行最大时间 (5.7.8 新增), 如果SQL执行超时则报错, 单位 ms + +1. 字段在满足业务需求前提下越小越好 +1. 使用 JOIN 代替子查询 +1. 使用 UNION 代替手动创建临时表 +1. 5.6及以上版本,存储`时间类型`时的效率: int > datetime > timestamp +1. limit 做分页时 记录上次分页最后一条记录的id使用上where进行过滤 提高性能, 前提id是int自增的 +1. 批量更新 `rewriteBatchedStatements` + +> 业务代码层面 `容易被忽视` +1. 减少不必要的SQL交互,例如 多次重复查询 +1. 精简SQL大小,避免操作无需操作的字段,例如更新仅更新一个字段,但是SQL写了更新所有字段 +1. for循环执行SQL + +### Join + +> JOIN 的SQL写法 +1. LEFT JOIN 左连接,左边为驱动表,右边为被驱动表. +1. RIGHT JOIN 右连接,右边为驱动表,左边为被驱动表. +1. INNER JOIN 内连接, mysql会选择数据量比较小的表作为驱动表,大表作为被驱动表. + +可通过EXPLANIN查看SQL语句的执行计划,EXPLANIN分析的第一行的表即是驱动表. + +> 尽量**小表驱动大表** 如果反过来则需要连接20w次 `for(20万){ for(20条){} }` + +> [MySQL Explain](/Database/MySQLIndex.md#explain) 中 Extra字段中会提到 MySQL内部使用到的Join类型 +- Using join buffer (Block Nested Loop) +- Using join buffer (Batched Key Access) +- Using join buffer (hash join) + +[Nested Join Optimization](https://dev.mysql.com/doc/refman/8.0/en/nested-join-optimization.html) + +> Join or +- `select apply a left join user b on a.name = b.name or a.addr = b.addr` + - 改写为 `select apply a left join user b on a.name = b.name left join user c on a.addr = c.addr` + - 使用到user表字段的地方需要改写判断 b 和 c。 + +************************ + +> [我们公司不让开发使用 join 包括 left join,不让用子查询,合理吗?](https://www.v2ex.com/t/678312) +> [业务多表 join,单条 SQL 梭哈一把好还是多次查询在代码整合好](https://www.v2ex.com/t/557498) + +************************ + +### 查看状态变量 +> [ SHOW VARIABLES](https://dev.mysql.com/doc/refman/5.7/en/show-variables.html) + +- 查看所有连接 `show processlist;` +- 查看最大连接数 `show variables like "max_conn%";` + - 设置最大连接数 `set global max_connections=5000;` + +************************ + +## 存储引擎 +> [Alternative Storage Engines](https://dev.mysql.com/doc/refman/8.0/en/storage-engines.html) + +### InnoDB +[InnoDB](/Database/MySQLInnodb.md) + +### MyIsAM + +***************************** + +# Tips +- 将需要执行的SQL写入文件 并将结果输出到文件 `mysql -u root -h 192.168.10.201 -p123 < query.sql > result.log` + +- [参考: 自增主键不连续的几种情况](https://cloud.tencent.com/developer/article/1634218) + - 事务回滚,插入语句报错,MySQL自增锁优化 + +- [Percona Doc](https://www.percona.com/) | [DockerHub](https://hub.docker.com/_/percona) + +## SQL 片段 + +1. 删除库下所有表 `select concat('drop table ',table_name,';') from information_schema.TABLES where table_schema='DATABASE_NAME';` + +> 统计表和索引的存储占用 +```sql + select table_schema as 'DB', + table_name as 'TABLE', + table_rows as 'TOTAL', + truncate(data_length / 1024 / 1024, 2) as 'Data MiB', + truncate(index_length / 1024 / 1024, 2) as 'Index MiB' + from information_schema.tables + where table_schema = 'test-db' + order by data_length desc, index_length desc; +``` +注意:table_rows是预估值,和实际值相差40%-50%,实际值需要看count(*), analyze table table_name 可提高近似率,但仍偏差较大 diff --git a/Database/MySQLIndex.md b/Database/MySQLIndex.md new file mode 100644 index 0000000..001330f --- /dev/null +++ b/Database/MySQLIndex.md @@ -0,0 +1,328 @@ +--- +title: MySQL索引 +date: 2021-05-27 21:36:58 +tags: +categories: +--- + +💠 + +- 1. [索引](#索引) + - 1.1. [Explain](#explain) + - 1.2. [为何选择 B+ 树结构](#为何选择-b+-树结构) + - 1.3. [基本DDL](#基本ddl) +- 2. [索引的类型](#索引的类型) + - 2.1. [普通索引](#普通索引) + - 2.2. [唯一索引](#唯一索引) + - 2.3. [主键索引](#主键索引) + - 2.4. [主键索引/聚集索引](#主键索引聚集索引) + - 2.5. [辅助索引/非聚集索引](#辅助索引非聚集索引) + - 2.6. [Hash 索引](#hash-索引) + - 2.6.1. [AHI 自适应哈希索引](#ahi-自适应哈希索引) + - 2.7. [全文索引](#全文索引) +- 3. [需要使用索引的场景](#需要使用索引的场景) + - 3.1. [索引优化](#索引优化) + - 3.1.1. [统计报表](#统计报表) +- 4. [SQL查询未命中索引的场景](#sql查询未命中索引的场景) + +💠 2024-06-21 10:47:46 +**************************************** + +# 索引 +> [Official Doc](https://dev.mysql.com/doc/refman/5.7/en/optimization-indexes.html) + +索引是采用特定的数据结构设计(B+Tree 或者 Hash), 为了对若干列进行快速访问 + +> 优点 +1. 加快查询速度 +1. 如果使用唯一索引,保证数据库表中每条数据的唯一性; +1. 加快表与表之间的连接操作 +1. 使用排序和分组检索数据时,可以显著的加快排序和分组的时间 + +> 缺点 +1. 需要额外占用存储空间,如果索引建立太多可能会导致索引空间大于数据空间反而降低性能 +1. 当对被索引的数据进行DML(增删改), 需要重建索引, 有一定性能影响 +1. 如果是MySQL5.7及以下版本,在大表上建索引会阻塞很长时间。 + +> 注意: [Avoiding Full Table Scans](https://dev.mysql.com/doc/refman/5.7/en/table-scan-avoidance.html) + +- 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引 +- 对长度大于50的 varchar 字段建立索引时,按需求恰当的使用前缀索引,或使用其他方法(例如增加int类型列col_crc32,然后对col_crc32建立索引) +- 合理创建联合索引(避免冗余),区分度最高的在`最左边`,单个索引的字段数`不超过5个` +- 单张表的索引数量控制在5个以内,若单张表多个字段在查询需求上都要单独用到索引,需要经过DBA评估。查询性能问题无法解决的,应从产品设计,数据建模,技术架构上进行重构 + +## Explain +> [Explain](https://dev.mysql.com/doc/refman/8.0/en/using-explain.html) + +- 使用 explain 判断SQL语句是否合理使用索引 + 1. id:数字越大越先执行,一样大则从上往下执行,如果为NULL则表示是结果集,不需要用来查询。 + 2. select_type: + + | | | + |:----|:----| + |simple | 不需要union的操作或者是不包含子查询的简单select语句。| + |primary | 需要union操作或者含有子查询的select语句。| + |union | 连接两个select查询,第一个查询是dervied派生表,第二个及后面的表select_type都是union。| + |dependent union | 与union一样,出现在union 或union all语句中,但是这个查询要受到外部查询的影响。| + |union result | 包含union的结果集。| + |subquery | 除了from字句中包含的子查询外,其他地方出现的子查询都可能是subquery。| + |dependent subquery| 与dependent union类似,表示这个subquery的查询要受到外部表查询的影响。| + |derived | from字句中出现的子查询,也叫做派生表,其他数据库中可能叫做内联视图或嵌套select。| + + 3. table: 表名,如果是用了别名,则显示别名 + 4. type 从上至下,好到差:除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一个索引。 + + | | | + |:----|:----| + | | | + |system | 表中只有一行数据或者是空表。| + |const | 使用唯一索引或者主键,返回记录一定是1行记录的等值where条件时,通常type是const。| + |eq_ref | 出现在要连接过个表的查询计划中,驱动表只返回一行数据,且这行数据是第二个表的主键或者唯一索引,且必须为not null,唯一索引和主键是多列时,只有所有的列都用作比较时才会出现eq_ref。| + |ref | 不像eq_ref那样要求连接顺序,也没有主键和唯一索引的要求,只要使用相等条件检索时就可能出现,常见与辅助索引的等值查找。| + |fulltext | 全文索引检索,要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引。| + |ref_or_null | 与ref方法类似,只是增加了null值的比较。实际用的不多。| + |unique_subquery | 用于where中的in形式子查询,子查询返回不重复值唯一值。| + |index_subquery | 用于in形式子查询使用到了辅助索引或者in常数列表,子查询可能返回重复值,可以使用索引将子查询去重。| + |range | 索引范围扫描,常见于使用>,<,is null,between ,in ,like等运算符的查询中。| + |index_merge | 表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引。| + |index | 索引全表扫描,把索引从头到尾扫一遍,常见于使用索引列就可以处理不需要读取数据文件的查询、可以使用索引排序或者分组的查询。| + |all | 这个就是全表扫描数据文件,然后再在server层进行过滤返回符合要求的记录。| + + 5. possible_keys: 查询可能使用到的索引。 + 6. key: 查询真正使用到的索引。 + 7. key_len: 用于处理查询的索引长度。 + 8. ref: 常数等值查询显示const,连接查询则显示表的关联字段。 + 9. rows: 执行计划中估算的扫描行数,不是精确值。 + 10. filtered: 表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例。 + 11. extra: 该字段信息较多,这里就不一一叙述了。 + +- 在实际的使用过程中, 需要重点去关注type、key、key_len、rows、extra这几个参数, type要努力优化到**range级别**,all要尽量少的出现,在查询的过程中要尽量使用索引 +- 在extra里面出现 Using filesort, Using temporary 是不太好的,要去优化提高性能。 + +- 通常情况下一个SQL语句只能在表上命中一个索引,但还有 索引合并 的情况 [参考: MySQL索引合并的使用与原理](https://blog.csdn.net/gentlezuo/article/details/107677543) + - intersect, union, sort-union + +> 注意Explain返回的是可能的查询器和优化器的执行策略。 +> `explain analyze` 会将SQL真正执行并记录被拆分后的SQL执行环节的大致耗时 + +## 为何选择 B+ 树结构 +> [参考:【原创】为什么Mongodb索引用B树,而Mysql用B+树?](https://www.cnblogs.com/rjzheng/p/12316685.html) `MongoDB PostgreSQL 都是使用B-Tree` +> [从 MongoDB 及 Mysql 谈B/B+树](https://blog.csdn.net/wwh578867817/article/details/50493940) +> [分布式数据库千亿级超大表性能优化实践](http://www.itpub.net/2020/02/28/5356/) + +- 相较于平衡二叉树:节点的多叉导致数据节点发生变化时,调整成本更低,IO次数也更低 +- MySQL Innodb 设计索引块是固定大小,中间节点上存放数据就会导致能用来存放指针数据的空间变小,也就意味着引用的子节点更少,树的高度更高(增加了IO次数),极端情况下退化为线性表。 +- 叶子节点用双向链表连接,方便范围查询,索引排序 + - InnoDB B+树索引的每一层节点间,都通过前后指针组成双向链表相互连接 +- 联合索引原理:依据字段顺序 例如 (a,b,c),会先依据a有序,然后b有序,最后c有序的方式来组织B+树,据此才有`最左原则`的限制 + - 如果中间某列没有值或使用like会导致后面的列不走索引 + +> 特点: +- 叶子节点: + - 如果是聚簇索引,则保存的是实际数据节点 + - 如果是非聚簇索引,保存的是主键id,需要走主键索引取数据 + +> [InnoDB索引同层非叶子节点间,也是双向链表吗?](https://juejin.cn/post/7259411780375085114) 数据结构层面展示MySQL的B+树实现 + +## 基本DDL +1. **创建** + - ALTER 方式 + - 普通索引 `ALTER table ADD INDEX index_name(column1, column2);` + - 唯一索引 ADD UNIQUE + - 主键索引 ADD PRIMARY KEY + - CREATE 方式 + - 普通方式 `CREATE INDEX index_name ON table_name (column_list)` + - 唯一索引 `CREATE UNIQUE INDEX index_name ON table_name(column_list)` +1. **删除** + - `DROP INDEX index_name ON talbe_name` + - `ALTER TABLE table_name DROP INDEX index_name` + - `ALTER TABLE table_name DROP PRIMARY KEY` + +1. **查看** + - `show index from tableName` + - [Official Doc](https://dev.mysql.com/doc/refman/5.7/en/show-index.html)`详解命令的输出内容` + +1. **强制使用索引** + - `select * from test force index(idx_name) where id = 1;` + +************************ + +# 索引的类型 +- 按「数据结构」分类:B+tree索引、Hash索引、Full-text索引。 +- 按「物理存储」分类:聚簇索引(主键索引)、二级索引(辅助索引)。 +- 按「字段特性」分类:主键索引、唯一索引、普通索引、前缀索引。 +- 按「字段个数」分类:单列索引、联合索引。 + +> 覆盖索引 +- 覆盖索引(covering index,或称索引覆盖),即`仅从辅助索引中就可以得到查询的信息`,而不需要查询聚集索引甚至回表。 + +> 联合索引 +- 两个或更多个列上的索引被称作联合索引,联合索引又叫复合索引 + +## 普通索引 +是最基本的索引,只在单列上建立索引,无特殊限制 + +## 唯一索引 +与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一 + +## 主键索引 +是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。 +- 当创建表时没有显示定义主键时: + 1. 首先判断表中是否有非空的整形唯一索引,如果有,则该列为主键(这时候可以使用 select _rowid from table 查询到主键列). + 2. 如果没有符合条件的则会自动创建一个6字节的主键(该主键是查不到的). + +## 主键索引/聚集索引 +聚集索引(Clustered Index) + +聚集索引:指该索引决定了表中数据行**物理存储**排序方式的索引,因此`一个表只能有一个聚集索引` + +而聚集索引(clustered index)就是按照每张表的主键构造一棵B+树,同时叶子节点中存放的即为`整张表的行记录数据`,也将聚集索引的叶子节点称为数据页。 +聚集索引的这个特性决定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行链接。 + +聚簇索引中的叶子节点记录了主键值、事务 id、用于事务和 MVCC 的回滚指针以及所有的剩余列。 + +## 辅助索引/非聚集索引 +辅助索引(Secondary Index,也称非聚集索引) + +叶子节点并不包含行记录的全部数据,只包含索引列 和 主键值(回查主键索引),辅助索引的存在并不影响数据聚集索引及数据存储,因此每张表上可以有多个辅助索引。 + +当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶节点级别的指针获得指向主键索引的`主键值`,然后再通过`主键索引`来找到一个完整的行记录,而 MyISAM 没有这个特性。 +```sql +-- MySQL 5.7 +create table report_user_date(id bigint primary key auto_increment, user_id bigint, + name varchar(32), avatar varchar(32), phone varchar(16), a_c int , b_c int, d_c int , e_c int, crea datetime); + +alter table report_user_date add index idx_user(user_id,crea); + +-- 如果只查询索引字段(user_id 或 crea),还能利用上联合索引,即使查询条件没有最左列 +-- innodb 引擎时,由上述特性 查id 也能使用上联合索引,myisam 就不能了 +explain select id from report_user_date where crea < '2021-12-26'; + +-- 如果查询非索引字段,就用不上联合索引了; 字段越多,查询越慢 +select name from report_user_date where crea < '2021-12-26'; +``` + +非聚集索引的使用场合为 查询所获数据量较少时 或者 某字段中的数据的唯一性比较高时, 非聚集索引必须是稠密索引`SQL查询的字段大部分有建索引` + +************************ + +## Hash 索引 +对于Innodb而言不支持完整的hash索引,只有AHI。MEMORY/HEAP/NDB 才支持 + +语法: `alter table xx add index idx_a(uid) using hash` + +> 适用场景 +1. 等值查询,索引列数据分散 + +> 限制和缺点 +1. 哈希索引只保存哈希码和指针,而不存储字段值,所以不能使用索引中的值来规避回表问题 +1. 哈希索引数据并不是按照索引值顺序存储的,所以无法用于排序和范围查询 +1. 哈希索引不支持 部分索引值 查找,因为哈希索引始终是使用`索引列的全部内容`来计算哈希码。 +1. HASH冲突(链地址法)会对整体维护加大负担(查询,新增,删除)。 + +> 优化 +1. 假如查询字段A较长,可用新列B存储字段A的hash值,再基于B列建立Hash索引,优化索引存储大小 + +### AHI 自适应哈希索引 +> [Adaptive Hash Index](https://dev.mysql.com/doc/refman/8.0/en/innodb-adaptive-hash.html) + +- 自适应哈希索引 默认开启 `show variables like "innodb_adaptive_hash_index";` +- 查看引擎状况,能看到hash索引使用情况 `show engine innodb status;` + +存储引擎会自动对个索引页上的查询进行监控,如果能够通过使用自适应哈希索引来提高查询效率,其便会自动创建自适应哈希索引,不需要开发人员或运维人员进行任何设置操作。 +自适应哈希索引是`对innodb的缓冲池的B+树页进行创建,不是对整张表创建`,因此适用于热点数据,如果查询是离散平均的还是建议直接建Hash索引,不适用AHI。 + +## 全文索引 + +************************ + +# 需要使用索引的场景 +1. 经常出现在 where 条件中的字段需添加索引。 +2. join 关联,被驱动表需要对关联字段添加索引。 +3. order by ,group by ,distinct的字段需要添加在索引的后面。 + +> 创建索引时避免有如下误解: +1. 宁滥勿缺。认为一个查询就需要建一个索引。 +1. 宁缺勿滥。认为索引会消耗空间、严重拖慢更新和新增速度。 +1. 抵制唯一索引。认为业务的唯一性一律需要在应用层通过“先查后插”方式解决。 + +## 索引优化 +### 统计报表 +> 如果使用MySQL存储 一定数据量的统计报表数据,使用 MyISAM 有更多优势, 没有事务,行锁等开销 + +```sql + CREATE TABLE `report_user_date` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `user_id` bigint(20) DEFAULT NULL, + `name` varchar(32) DEFAULT NULL, + `avatar` varchar(32) DEFAULT NULL, + `phone` varchar(16) DEFAULT NULL, + `a_c` int(11) DEFAULT NULL, + `b_c` int(11) DEFAULT NULL, + `d_c` int(11) DEFAULT NULL, + `e_c` int(11) DEFAULT NULL, + `crea` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_user` (`user_id`,`crea`) + ) +``` + +1. 构造测试数据 +```go + // 生成随机数据 + insertOne := "insert into report_user_date( user_id, name, avatar, phone, a_c, b_c, d_c, e_c,crea) value (?,?,?,?,?,?,?,?,?);" + stmt, _ := db.Prepare(insertOne) + now := time.Now() + for i := 0; i < 100000; i++ { + stmt.Exec(i%2000, fmt.Sprint(i), fmt.Sprint(i), fmt.Sprint(i), i, i+1, i+2, i+3, now.Add(time.Second*time.Duration(i))) + } + // 多次复制自身数据 扩大数据量 + // insert into report_user_date( user_id, name, avatar, phone, a_c, b_c, d_c, e_c,crea) select user_id, name, avatar, phone, a_c, b_c, d_c, e_c,crea from report_user_date; +``` +2. 对比查询效率 +```sql +-- MySQL 5.7 340w数据 + +select user_id, count(a_c ) from report_user_date where user_id between 100 and 700 group by user_id ; +-- innodb 2700ms +-- myisam 900ms + +select user_id, count(distinct a_c ),count(distinct b_c ) from report_user_date where user_id between 100 and 700 and crea < '2021-12-26 18:00:00' group by user_id ; +-- innodb 2700ms +-- myisam 1100ms + +select user_id from report_user_date where crea < '2021-12-26'; +-- 如果只查询索引字段,还能利用上联合索引,即使查询条件没有最左列字段, 如果查询非索引字段,就用不上联合索引了,字段越多,查询越慢 +-- 如果查询的是id 主键字段:innodb 会使用上索引,myisam 就不能了 +``` + +************************ + +# SQL查询未命中索引的场景 + +> 全局 +- 如果MySQL分析器估计使用全表扫描要比使用索引快,则不使用索引(例如 全部记录数量少于10 等等) +- 通过索引扫描的记录数超过20%-30%,可能会变成全表扫描。`和查询条件无关` + - 例如 使用 <> 或!= , is null 或 is not null [不影响使用索引](https://dev.mysql.com/doc/refman/5.5/en/is-null-optimization.html),通常是因为扫描行数过多 +- 注意: 索引列上存在 null 值, 索引仍能使用, 包括 is null 和 is not null, 但是 Null值可能会相比于默认值占用更多存储空间 +- 使用了索引字段做条件过滤,但是使用了非索引字段来做Order by + +> 联合索引 +- 第一个索引列使用范围查询 >,in,bewteen 等等,导致后续字段无法走索引 +- 最左前缀原则,查询条件不是最左索引列 + +> 单字段条件查询时 +- 使用 or `但是如果所有的or条件都必须是独立索引,会使用索引` + - 优化方向: 使用in代替or +- 模糊查询 条件列`最左`是通配符`%` +- 查询条件数据类型不匹配, 例如 `where name = 1`, 会导致索引失效,并且查询结果可能正常,可能非预期 + 1. `where int_col='123'` 不会发生 **类型隐式转换**,可使用索引 + 1. `where int_col='abc'` 'abc'被隐式转换为0,可使用索引 + 1. `where char_col=123` 发生类型隐式转换,不会使用索引 + +> 其他 +- HEAP表使用HASH索引时,使用范围检索或者ORDER BY +- 多表关联时,排序字段不属于驱动表,无法利用索引完成排序 +- 两个独立索引,其中一个用于检索,一个用于排序(只能用到一个) +- JOIN查询时,关联列`数据类型/以及字符集/检验集`不一致也会导致索引不可用 +- 对索引列进行运算(加减乘除等) diff --git a/Database/MySQLInnodb.md b/Database/MySQLInnodb.md new file mode 100644 index 0000000..fc35958 --- /dev/null +++ b/Database/MySQLInnodb.md @@ -0,0 +1,134 @@ +--- +title: MySQL InnoDB +date: 2022-06-27 14:23:03 +tags: +categories: +--- + +💠 + +- 1. [InnoDB](#innodb) + - 1.1. [核心参数](#核心参数) +- 2. [锁设计细节](#锁设计细节) + - 2.1. [共享/排他锁(Shared and Exclusive Locks)](#共享排他锁shared-and-exclusive-locks) + - 2.2. [意向锁(Intention Locks)](#意向锁intention-locks) + - 2.3. [记录锁(Record Locks)](#记录锁record-locks) + - 2.4. [间隙锁(Gap Locks)](#间隙锁gap-locks) + - 2.5. [临键锁(Next-key Locks)](#临键锁next-key-locks) + - 2.6. [插入意向锁(Insert Intention Locks)](#插入意向锁insert-intention-locks) + - 2.7. [自增锁(Auto-inc Locks)](#自增锁auto-inc-locks) + - 2.8. [粗粒度 锁类型](#粗粒度-锁类型) +- 3. [MVCC机制](#mvcc机制) +- 4. [行设计](#行设计) +- 5. [Buffer Pool](#buffer-pool) + +💠 2024-06-21 10:47:46 +**************************************** +# InnoDB +> [Doc: InnoDB](https://dev.mysql.com/doc/refman/8.0/en/innodb-storage-engine.html) + +## 核心参数 +- `show global variables like 'innodb_buffer_pool_%';` +- `show status like '%buffer_pool%';` + +************************ + +# 锁设计细节 +> [InnoDB Locking and Transaction Model](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking-transaction-model.html) +> [InnoDB Locking](https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html) + +## 共享/排他锁(Shared and Exclusive Locks) +> 共享锁(S)和排他锁(X)是InnoDB引擎实现的`行级别锁`。 + +拿共享锁是为了让当前事务去读某一行数据,排他锁则是为了修改或删除某一行数据。 + +## 意向锁(Intention Locks) +> 意向锁存在的意义在于,使得行锁和表锁能够共存。 `意向锁是表级别锁`,用来说明事务稍后会对表中的数据行加哪种类型的锁(共享锁或独占锁)。 + +当一个事务A对表加了意向排他锁(IX)时,另外一个事务在加锁前就会通过该表的意向排他锁知道前面已经有事务在对该表进行独占操作,从而等待。 + +| | X | IX | S | IS +|:---|:---|:---|:---|:---| +X | ❌ | ❌ | ❌ | ❌ +IX | ❌ | | ❌ | +S | ❌ | ❌ | | +IS | ❌ | | | + +## 记录锁(Record Locks) +> `记录锁是索引记录上的锁` + +例如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;会阻止其他事务对c1=10的数据行进行插入、更新、删除等操作。 + +记录锁总是锁定索引记录。如果一个表没有定义索引,那么就会去锁定隐式的“聚集索引”。 + +## 间隙锁(Gap Locks) +> 间隙锁是一个在索引记录之间的间隙上的锁。 + +一个间隙可能跨越单个索引值、多个索引值,甚至为空。 + +对于使用唯一索引 来搜索唯一行的语句,只加记录锁不加间隙锁(这并不包括组合唯一索引)。 + +## 临键锁(Next-key Locks) +> Next-Key Locks是行锁与间隙锁的组合。 + +当InnoDB扫描索引记录的时候,会首先对选中的索引记录加上记录锁(Record Lock),然后再对索引记录两边的间隙加上间隙锁(Gap Lock)。 + +## 插入意向锁(Insert Intention Locks) +> 插入意向锁是在数据行插入之前 设置的间隙锁定类型。 + +如果多个事务插入到相同的索引间隙中,并且它们不在间隙中的相同位置插入,则无需等待其他事务。 +例如:在4和7的索引间隙之间两个事务分别插入5和6,则两个事务不会发冲突阻塞。 + +## 自增锁(Auto-inc Locks) +> 自增锁是事务插入到有自增列的表中而获得的一种特殊的`表级锁`。 + +如果一个事务正在向表中插入值,那么任何其他事务都必须等待,保证第一个事务插入的行是连续的自增值。 + +## 粗粒度 锁类型 +> 表锁,页锁,行锁 + +- 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 +- 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 +- 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般 + +> [死锁原因和方案](https://zhuanlan.zhihu.com/p/267522634) + +************************ + +# MVCC机制 +> [InnoDB Multi-Versioning](https://dev.mysql.com/doc/refman/8.0/en/innodb-multi-versioning.html) + +> [MySQL InnoDB MVCC机制](https://www.jianshu.com/p/d67f0329d3bf) +> [参考: 轻松理解MYSQL MVCC 实现机制](https://blog.csdn.net/whoamiyang/article/details/51901888#commentBox) + +锁开销较大,因此引入MVCC(快照读): 读不加锁,读写不冲突。在读多写少的场景下极大的增加了系统的并发性能。`只在RC和RR下生效, 因为读未提交不需要(已经不在意一致性了),序列化同样不需要(绝对不会出现一致性问题)` + +Innodb 内部在每行有隐藏列: +| 名称 | 大小 | 说明 | +|:---|:---|:---| +| DB_TRX_ID | 6-byte | 事务id,每处理一个事务,值自动加一。 | +| DB_ROLL_PTR | 7-byte | 回滚指针, 指向 undo 记录 | +| DB_ROW_ID | 6-byte | 行id, 2^48,如果没有手动设置主键,rowId溢出时会发生数据覆盖(rowId循环使用) | + +> 每条记录的头信息(record header)里都有一个bit(deleted_flag)来表示当前记录是否已经被删除 + +在多个事务并行操作某行数据的情况下,不同事务对该行数据的UPDATE会产生多个版本,数据库通过DB_TRX_ID来标记版本,然后用DB_ROLL_PT回滚指针将这些版本以先后顺序连接成一条 Undo Log 链。 + +> [innodb-consistent-read](https://dev.mysql.com/doc/refman/8.0/en/innodb-consistent-read.html) +一致性视图的生成 ReadView + +在read committed级别下,readview会在事务中的每一个SELECT语句查询发送前生成(也可以在声明事务时显式声明START TRANSACTION WITH CONSISTENT SNAPSHOT),因此每次SELECT都可以获取到当前已提交事务和自己修改的最新版本。而在repeatable read级别下,每个事务只会在第一个SELECT语句查询发送前或显式声明处生成,其他查询操作都会基于这个ReadView,这样就保证了一个事务中的多次查询结果都是相同的,因为他们都是基于同一个ReadView下进行MVCC机制的查询操作。 + +************************ + +# 行设计 +> [row size limits](https://dev.mysql.com/doc/refman/8.0/en/column-count-limit.html#row-size-limits) +> [MySQL 一行记录是怎么存储的?](https://xiaolincoding.com/mysql/base/row_format.html) + +************************ + +# Buffer Pool +> [Buffer Pool](https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool.html) + +Insert Buffer +Double Write diff --git a/Database/Neo4j.md b/Database/Neo4j.md new file mode 100644 index 0000000..e8fe148 --- /dev/null +++ b/Database/Neo4j.md @@ -0,0 +1,102 @@ +--- +title: Neo4j +date: 2024-10-13 17:59:27 +tags: +categories: +--- + +💠 + +- 1. [Neo4j](#neo4j) + - 1.1. [安装](#安装) + - 1.1.1. [驱动](#驱动) +- 2. [使用](#使用) + - 2.1. [结构](#结构) + - 2.2. [Schema](#schema) + - 2.2.1. [索引](#索引) + - 2.2.2. [约束](#约束) + - 2.2.3. [统计信息](#统计信息) + - 2.3. [Pattern](#pattern) +- 3. [应用](#应用) + +💠 2024-10-14 19:26:20 +**************************************** +# Neo4j +> [Neo4j Graph Database & Analytics | Graph Database Management System](https://neo4j.com/) + +## 安装 + +- `docker run --publish=7474:7474 --publish=7687:7687 neo4j:5.24` +- [Neo4j: Can't log in: Neo.ClientError.Security.Unauthorized: The client is unauthorized due to authentication failure - Stack Overflow](https://stackoverflow.com/questions/53687901/neo4j-cant-log-in-neo-clienterror-security-unauthorized-the-client-is-unauth) +`neo4j-admin dbms set-initial-password pwdtest123` 然后重启 + - 或者运行时添加环境变量 `--env NEO4J_AUTH=neo4j/neo4jpassword` +- http://localhost:7474/browser/ bolt协议,用户名 neo4j +- 登录后 Favorites 菜单下的 Sample Scripts 可以快速了解常用查询语句 + +> 注意 +- 社区版本不支持命令 create database xxx 只能使用默认的 neo4j + +### 驱动 + +Python + +> [Neo4j Python Driver 5.25 — Neo4j Python Driver 5.25](https://neo4j.com/docs/api/python-driver/current/) + +************************ + +# 使用 +> [快速入门 Neo4J教程](https://juejin.cn/post/7146016720388358181) +> [Neo4j图数据库及Cypher语法基础 | Quantum Bit](https://www.eula.club/blogs/Neo4j%E5%9B%BE%E6%95%B0%E6%8D%AE%E5%BA%93%E5%8F%8ACypher%E8%AF%AD%E6%B3%95%E5%9F%BA%E7%A1%80.html) + + +> [Neo4j - 悦光阴 - 博客园](https://www.cnblogs.com/ljhdo/tag/Neo4j/) +> [Neo4j 第二篇:图形数据库 - 悦光阴 - 博客园](https://www.cnblogs.com/ljhdo/p/5178225.html) + + +## 结构 +使用Neo4j创建的图(Graph)基于属性图模型,在该模型中,每个实体都有ID(Identity)唯一标识,每个节点由标签(Lable)分组,每个关系都有一个唯一的关系类型。 + +标签用于对节点进行分组,相当于节点的类型,拥有相同标签的节点属于同一个分组。一个节点可以拥有零个,一个或多个标签,因此,一个节点可以属于多个分组。对分组进行查询,能够缩小查询的节点范围,提高查询的性能。 +属性是一个键值对(Key/Value),用于为节点或关系提供扩展的信息。一般情况下,每个节点都有name属性,用于命名节点,通常情况下,name属性的值是唯一的。 + +## Schema +Neo4j的模式(Schema)通常是指索引、约束和统计,通过创建模式,Neo4j能够获得查询性能的提升和建模的便利;Neo4j数据库的模式可选的,也可以是无模式的。 + +### 索引 +> [Indexes - Cypher Manual](https://neo4j.com/docs/cypher-manual/current/indexes/) + +``` + CREATE INDEX ON :Person(firstname) + CREATE INDEX ON :Person(firstname, surname) +``` + +### 约束 +> [Constraints - Cypher Manual](https://neo4j.com/docs/cypher-manual/current/constraints/) + +### 统计信息 + +当使用Cypher查询图形数据库时,Cypher脚本被编译成一个执行计划,执行该执行计划获得查询结果。为了生成一个性能优化的执行计划,Neo4j需要收集统计信息以对查询进行优化。当统计信息变化到一定的赋值时,Neo4j需要重新生成执行计划,以保证Cypher查询是性能优化的 + +## Pattern +模式(Pattern)和模式匹配(Match)是Cypher的核心,使用模式来描述所需数据的形状,该模式使用属性图的结构来描述,通常使用小括号()表示节点,-->表示关系,-[]->表示关系和关系的类型,箭头表示关系的方向。 + +节点模式 (Variable:Lable1:Lable2{Key1:Value1,Key2,Value2}) +- 节点可以有多个标签用于匹配,{}内则是匹配节点的属性值 + +关系模式 在属性图中,节点之间存在关系,关系通过[]表示,节点之间的关系通过箭头()-[]->()表示 +- 关联节点模式: 节点之间通过关系联系在一下,由于关系具有方向性,因此,-->表示存在有向的关系,--表示存在关联,不指定关系的方向 + - `(a)-[r]->(b)` :该模式用于描述节点a和b之间存在有向的关系r, + - `(a)-->(b)`:该模式用于描述a和b之间存在有向关系; +- 变长路径的模式: 从一个节点,通过直接关系,连接到另外一个节点,这个过程叫遍历,经过的节点和关系的组合叫做路径(Path),路径是由节点和关系的有序组合 + - `(a)-->(b)`:是步长为1的路径,节点a和b之间有关系直接关联; + - `(a)-->()-->(b)`:是步长为2的路径,从节点a,经过两个关系和一个节点,到达节点b; + - Cypher语言支持变长路径的模式,变长路径的表示方式是:`[*N..M]`,N和M表示路径长度的最小值和最大值 + +************************ + +# 应用 +> [hokaso/hocassian-people-neo4j: NoSQL可视化人脉图谱项目](https://github.com/hokaso/hocassian-people-neo4j) +> [NTDXYG/Neo4j: 基于电影知识图谱和微信小程序的智能问答系统](https://github.com/NTDXYG/Neo4j) +> [lonngxiang/Knowledge-map-of-family-tree (姓氏家族家谱知识图谱)](https://github.com/lonngxiang/Knowledge-map-of-family-tree) + + diff --git a/Database/OLAP/Clickhouse.md b/Database/OLAP/Clickhouse.md new file mode 100644 index 0000000..5e51fae --- /dev/null +++ b/Database/OLAP/Clickhouse.md @@ -0,0 +1,249 @@ +--- +title: Clickhouse +date: 2023-03-31 09:58:36 +tags: +categories: +--- + +💠 + +- 1. [Clickhouse](#clickhouse) + - 1.1. [安装](#安装) + - 1.2. [数据类型](#数据类型) + - 1.2.1. [bitmap](#bitmap) + - 1.2.2. [Decimal](#decimal) + - 1.3. [聚合函数](#聚合函数) +- 2. [数据库引擎](#数据库引擎) +- 3. [表引擎](#表引擎) + - 3.1. [MergeTree 引擎家族](#mergetree-引擎家族) + - 3.2. [分布式表引擎 Distributed](#分布式表引擎-distributed) +- 4. [表](#表) + - 4.1. [分区表](#分区表) +- 5. [客户端](#客户端) + - 5.1. [Java](#java) +- 6. [Explain](#explain) +- 7. [Tips](#tips) + +💠 2024-10-17 10:43:43 +**************************************** +# Clickhouse +> [Official Site](https://clickhouse.com) + +> [What is ClickHouse? ](https://medium.com/doublecloud-insights/what-is-clickhouse-a-comprehensive-guide-for-getting-started-5aae9afd38b0) + +- 关联开源项目: Sentry [clickvisual](https://github.com/clickvisual/clickvisual) + +************************ +## 安装 +> [Docker compose 安装](https://github.com/ClickHouse/examples/blob/main/docker-compose-recipes/README.md) +> [Clickhouse cluster built with docker-compose.](https://github.com/tetafro/clickhouse-cluster) + +> 例如以下配置为 两个分片,每个分片两个副本 +```xml + + + + + + false + + 10.0.3.27 + 9000 + + + 10.0.3.41 + 9000 + + + + true + + 10.0.3.46 + 9000 + + + 10.0.3.26 + 9000 + + + + + + + 10.0.3.12 + 2181 + + + 10.0.3.3 + 2181 + + + 10.0.3.23 + 2181 + + + +``` + +************************ + +## 数据类型 +> [ClickHouse Data Types](https://clickhouse.com/docs/en/sql-reference/data-types) + +注意Ck中建表时字段类型默认是非Null的,和常见的业务数据库MySQL等相反,支持Null数据需要显式指定,例如: Nullable(Int) + +### bitmap +> 并没有这个类型定义,只是在使用时由数据存储方式存在,类似于Redis的bitmap。 + +位图对象有两种构造方法。一个是由聚合函数groupBitmapState构造的,另一个是由Array Object构造的。同时还可以将位图对象转化为数组对象`bitmapToArray()`。 + +[Roaring bitmaps](https://github.com/RoaringBitmap/CRoaring) +[BitMap及其在ClickHouse中的应用](https://zhuanlan.zhihu.com/p/480345952)`CK针对数据的分布情况做了一些优化` + +### Decimal +> [Data TypesDecimal](https://clickhouse.com/docs/en/sql-reference/data-types/decimal) + +| min | max | type | +|:---|:---|:---| +| 1 | 9 | Decimal32 | +| 10 | 18 | Decimal64 | +| 19 | 38 | Decimal128 | +| 39 | 76 | Decimal256 | + +> Tips +- [cast as decimal is very slow](https://github.com/ClickHouse/ClickHouse/issues/30542) `Decimal128 256 相较于 64和32 有较大的性能差距,可以用其中SQL做测试` + - `SELECT sum(CAST(number + 1., 'Decimal(17, 1)')) FROM numbers(100000000);` 自建的CK集群内看到128耗时是64的三倍 **实际情况实际分析,仅供参考** + - 因为从Decimal128类型开始CK都要模拟计算来提高精度,CPU成本更大 + - CK22.3.5.5版本上 `ROUND(cast(AVG(ifNull(sale_amount, 0)) AS Decimal(76,38)), 4)` Round会不起作用,计算结果仍是很长的小数位 + +> `SELECT sumWithOverflow(CAST(number + 1., 'Decimal(3, 1)')) as res , toTypeName(res) FROM numbers(1000000);` +- 在做sum计算时,表的源字段大小不够时会自动增长类型, 但是如果使用 sumWithOverflow 就不会扩大类型,因此计算结果也是错误的 + +************************ + +## 聚合函数 +> [List of Aggregate Functions](https://clickhouse.com/docs/en/sql-reference/aggregate-functions/reference) + +- 注意avg函数固定返回Float64类型,需要按数据要求做类型转换处理规避后续计算的精度损失问题 + +************************ + +# 数据库引擎 +- Atomic +- MySQL 关联远程库表 +- MaterializedMySQL 原生实现MySQL引擎 支持从MySQL全量及增量实时同步 +- Lazy +- PostgreSQL 关联远程库表 +- MaterializedPostgreSQL 原生实现PG引擎 +- Replicated +- SQLite + +# 表引擎 +> [doc: Table Engines](https://clickhouse.com/docs/en/engines/table-engines) + +分为:MT家族,外部表(集成)引擎,日志引擎,特殊引擎(Distributed,File,URL,Memory等等) + +## MergeTree 引擎家族 + + +## 分布式表引擎 Distributed +> [doc: distributed](https://clickhouse.com/docs/en/engines/table-engines/special/distributed) + +这种表不存储数据,可以当作关联的表的一层代理,实现并行查询和数据写入分发. + +查询Distributed表引擎的过程是: 先查接收请求节点本地的表(和当前节点同分片下的Replication副本节点**不会接收到查询的请求**),对剩余全部分片发送请求(分片中的一个随机副本),然后再聚合各个分片返回的数据,最后返回最终结果。 + +![](./img/001-dis-send-query.webp) +![](./img/002-dis-merge-result.webp) + +```sql + -- 查看复制表数量 + select database,count(*) cnt + ,sum(case when engine ='ReplicatedMergeTree' then 1 else 0 end) cnt_rmt + from clusterAllReplicas('default_cluster', 'system.tables') + group by database order by count(*) desc + + -- 查看复制表明细 + select name ,engine, hostname(), metadata_modification_time, total_rows, total_bytes + from clusterAllReplicas('default_cluster', 'system.tables') + where database = 'db' and engine = 'ReplicatedMergeTree'; +``` +************************ + +> [ClickHouse案例:查询结果不一致](https://cloud.tencent.com/developer/article/1748216) +> 注意 设置有副本的集群,分布式表都需要关联副本表 Replicated MergeTree 作为数据表,如果使用普通的MT表引擎,会导致查询和写入都会遇到奇怪的问题。 +- 写入: 会有部分节点上没有数据,但是全部节点的数据总量是对的 +- 查询: 一条SQL每次查询的结果都不一样(各个分片内随机选择副本再合并查询结果而导致的) + +************************ +# 表 +> [doc: create table](https://clickhouse.com/docs/en/sql-reference/statements/create/table) + +## 分区表 +> [What is the actual use of partitions in clickhouse?](https://stackoverflow.com/questions/75439190/what-is-the-actual-use-of-partitions-in-clickhouse)`数据有明显的分区特征(例如时间序列数据),按分区做TTL,查询时通常不会跨多个分区,分区数在100以内(分区很多时很影响读写性能)` + +************************ + +# 客户端 +> [snuba](https://github.com/getsentry/snuba)`Sentry开发, CK的一个查询层` + +## Java +> [Connecting ClickHouse to external data sources with JDBC](https://clickhouse.com/docs/en/integrations/jdbc/jdbc-with-clickhouse) +> JDBC的驱动实现是通过HTTP协议和Clickhouse通信 [Github: clickhouse-java](https://github.com/ClickHouse/clickhouse-java)`com.clickhouse.client.internal.apache.hc.client5.http.impl.io.DefaultManagedHttpClientConnection` + +低版本驱动没有实现负载均衡,需要在数据节点前加一层 [chproxy](https://github.com/ContentSquare/chproxy), 但是实际上也可以自己实现负载均衡算法(例如:依据某个时间窗口内所有节点的负载,连接等情况来选择合适的节点) + +```java + Properties properties = new Properties(); + properties.setProperty("socket_keepalive", "true"); //socket_timeout时间由系统设置 + properties.setProperty("auto_discovery", "true"); // 节点自动发现 + properties.setProperty("load_balancing_policy", "roundRobin"); // 负载均衡 + properties.setProperty("health_check_interval", "1000"); // 健康检查间隔(以毫秒为单位) + properties.setProperty("health_check_query", "select 1"); // 健康检查语句 + properties.setProperty("node_check_interval", "1000"); // 节点检查间隔(以毫秒为单位) + properties.setProperty("failover", "2"); // 发生故障转移最大次数 + properties.setProperty("retry", "2"); // 故障重试最大次数 + + // 客户端负载均衡的方式 + String url = "jdbc:clickhouse://h1:p1,h2:p2,h3:p3,h4:p4/default?socket_timeout=6000000"; + ClickHouseDataSource dataSource = new ClickHouseDataSource(url, properties) +``` + +> 实践: +- 出现 `The target server failed to respond code: 1002` 报错 + - [Validate stale connection to fix the bug: failed to respond](https://github.com/ClickHouse/clickhouse-java/pull/760)`增加活跃连接校验逻辑,降低客户端获取到关闭连接的概率` + - [BatchUpdateException during inserts with jdbc driver](https://github.com/ClickHouse/clickhouse-java/issues/1444) `驱动作者认为: 关键点在于边界值,如果客户端设置的和服务端一样或者更大,就会出现客户端认为连接未超时可复用,但是服务端认为超时于是就关闭了连接` + - JDBC URL优化 socketTimeout参数 (ck0.6.0默认是30s), **应明显小于服务端**的 tcp_keep_alive_timeout (ms)值, `select * from system.settings where name like '%keep%';` + - JDBC 驱动版本低, 从0.2.4 升级到0.6.0后问题出现概率小很多 因为 [host failed to respond](https://github.com/ClickHouse/clickhouse-java/issues/452) 0.2.5 主动获取了服务端设置值 + - 注意socketTimeout参数同样作用于查询时间,如果SQL执行时间大于该值会报错 read timeout [http read timeout 30](https://github.com/ClickHouse/clickhouse-java/issues/159) + +- [驱动内错误码定义](https://github.com/ClickHouse/ClickHouse/blob/master/src/Common/ErrorCodes.cpp) + - 注意CK驱动从0.3左右开始,ClickHouseException不继承SQLException + +************************ + +# Explain +> [Clickhouse: Explain](https://clickhouse.com/docs/en/sql-reference/statements/explain) +> [Using Explain to analyze and improve Clickhouse queries performance](https://medium.com/datadenys/using-explain-to-analyze-and-improve-clickhouse-queries-performance-23dbcdf55a97) + +相比于MySQL的Explain,CK设计Explain能查看更多维度的指标 + +JSON格式查看 `EXPLAIN json = 1, indexes = 1 SQL` +- 关注最内层的Indexes结构里的 **Initial Parts** 全部块 **Selected Parts** 读取的块 **Initial Granules** 全部粒度 **Selected Granules** 读取的粒度。 +- 读取的指标越小越好,因此表要基于使用情况设计好分区利于查询效率 + +************************ + +# Tips + +- 合理使用排序键让数据均匀分片,避免数据倾斜导致集群计算时出现短板效应 +- 数据大量查询导入导出时 + - [ClickHouse SQL基本语法和导入导出实战](https://cloud.tencent.com/developer/article/1979184) + - 导出时需要注意传统的 limit offset 会导致结果集 重复和丢失,追加 order by 子句可以降低发生概率,但是排序字段不唯一的话还是会有可能出现重复或丢失的问题。 + +- 多表关联 如果能确认范围过滤的数据只会从一个表返回可以避免join来过滤, 转用in, 避免分布式的做数据复制,导致资源消耗放大 +- Global 优化 join 和in 避免读放大 + +> BUG +- [22.3.5.5](https://github.com/ClickHouse/ClickHouse/releases/tag/v22.3.5.5-lts)版本上 使用 UNION ALL 连接 A 和 B两段SQL时,CK偶现出现B段SQL没有正确的groupby聚合(有些数据没有聚合),导致整体执行结果条目数变多,非期望数据 + diff --git a/Database/OLAP/ClickhouseAdvance.md b/Database/OLAP/ClickhouseAdvance.md new file mode 100644 index 0000000..b0ef0b5 --- /dev/null +++ b/Database/OLAP/ClickhouseAdvance.md @@ -0,0 +1,87 @@ +--- +title: ClickhouseAdvance +date: 2024-04-26 01:17:19 +tags: +categories: +--- + +💠 + +- 1. [Clickhouse](#clickhouse) + - 1.1. [设计](#设计) + - 1.2. [事务](#事务) +- 2. [使用实践](#使用实践) + - 2.1. [写入](#写入) + - 2.2. [查询](#查询) + - 2.3. [监控](#监控) + +💠 2024-10-08 11:39:18 +**************************************** +# Clickhouse + +## 设计 +官网的介绍为面向OLAP的高性能列式数据库。 + +> 多主架构 +- Ck中数据节点是平等的,没有master/slave之分, 相较于Greenplum等特定主备架构具有更高的可用性 + - 操作请求(查询和DDL)会由客户端决定负载均衡选择一个节点发送指令,选中的这个节点会成为临时的业务协调节点,负责管理指令在集群内的执行 + - 正是多主特性,部署的主节点数等于1时就是单节点模式,同样可以高性能提供查询服务,只是不具备高可用 +- **重依赖Zookeeper**,所有节点数据一致性和分布式表,复制表数据的一致性,分片的游标都需要依赖zookeeper管理 + - 当一个集群内创建了大量的分布式表,复制表时(目前使用中发现到了4000+时),如果有高频率的建表/删表DDL,分布式表的数据写入等操作会容易超时,此时的瓶颈是因为ZK的OP/S下降了。 + - 因此官方在计划自建一个 `clickhouse-keeper` 替换ZK + +> 列式存储 +- OLAP业务具有的一个特性是基于多维度指标的大宽表数据,聚合计算出某些维度指标的结果,如果是行存储,io成本会高于列存储(按需加载列文件) +- 具有更高的压缩率,同列的数据类型是一致的,可以做更有效率的压缩,压缩比率高了以后,就可以cache更多的数据在内存中 + - [Compression in ClickHouse](https://altinity.com/blog/2017-11-21-compression-in-clickhouse)`支持LZ4(快大) Zstd(慢小)` + - 如果IO是瓶颈(IOPS不够高) Zstd更合适 + - 如果压缩解压逻辑时CPU是瓶颈 LZ4 更合适 + - 如果不差钱 IO和CPU都很强,不压缩最合适 + + +> 索引 +- 一级索引: 排序键 主键 物理存储, 用索引粒度 index_granularrity 跳表一样去连接多个块 +- 二级索引: 强依赖一级 命中一级情况下才能优化使用到二级. 不像MySQL可以单独命中二级回一级再回表查 + - minmax 表达式按块记录表达式的min max值, 用于加速扫描表达式 + - set 数据去重后存储到索引数据块(有容量限制) + +注意:MergeTree 主键索引 允许重复数据 + +## 事务 +> [Transactional (ACID) support](https://clickhouse.com/docs/en/guides/developer/transactional) + +************************ + +# 使用实践 + +> [Getting started with ClickHouse? Here are 13 "Deadly Sins" and how to avoid them](https://clickhouse.com/blog/common-getting-started-issues-with-clickhouse) + +## 写入 +> [Bulk Inserts](https://clickhouse.com/docs/en/optimize/bulk-inserts) + +- CK应尽量避免高并发小数据量写入, 应用层不好调整的话,可以前置一层Buffer引擎, 将小数据量合并后再写. +- 在使用Datax类同步工具做数据写入时,需要注意写入批次参数`batchSize` 设置10w-100w更好,避免CK做大量小文件的合并从而触发报错 `too many parts` +- 数据同步时,直接往分布式表写是简单的做法 + - 进一步优化是直接写local,手动按分片键拆分上游数据,往不同节点的local写,需要保证数据均衡和完整 + - 例如整数字段按节点数取余,如果不能达到均匀的分布,可以组合业务字段hash后转整型再按节点数取余 +- 数据同步写入分区表时,尽量在写入前做好手动分区,避免单次数据插入到多个分区,手动规避数据分布式分发带来的效率开销 + - 因此在一些并发同步的场景下,需要考虑每个数据任务是否使用分片键做的拆分,如果分片键无法保证数据的均匀分布而采用其他字段做拆分分片时 + - 注意CK端容易有写入报错,因为此时每个任务都会涉及到多个分区,zk ck 负载都会高 + - Can't get data for node /clickhouse/table/02/tgysg/xxx/blocks/xxxx: node doesn't exist (No node). (KEEPER_EXCEPTION) + - ZooKeeper session has been expired.. (NO_ZOOKEEPER) + + +> 数据文件方式同步 [百亿级数据同步,如何基于 SeaTunnel 的 ClickHouse 实现?](https://seatunnel.apache.org/zh-CN/blog/2022/05/10/ClickHouse/)`在上游数据端就生成ck的数据文件,然后传输文件,ck服务端attach挂载该文件` +> [Clickhouse写入问题汇总](https://www.cnblogs.com/yisany/p/14275785.html) + +[ClickHouse连接ZK频繁超时处理案例](https://www.modb.pro/db/159455)`数据小批次频繁写入导致part过多,大幅影响zk的性能` +> [How to configure ClickHouse for INSERT performance? ](https://dev.to/shiviyer/how-to-configure-clickhouse-for-insert-performance-4cof)`大批量写入,异步写入,按需取消写入约束,表引擎调优,压缩算法替换,结合监控数据寻找出最合适业务数据的一套配置` +> [Essential Monitoring Queries - part 1 - INSERT Queries](https://clickhouse.com/blog/monitoring-troubleshooting-insert-queries-clickhouse) + +## 查询 +不适合查单行的点查询, 最小查询数据量是索引粒度(index_granularity)的行数, 即使查询一条数据,CK也会按索引粒度加载整块数据进缓存 + + +## 监控 +> [查询日志 system.query_log](https://clickhouse.com/docs/zh/operations/system-tables/query_log) + diff --git a/Database/OLAP/Readme.md b/Database/OLAP/Readme.md new file mode 100644 index 0000000..7954ecc --- /dev/null +++ b/Database/OLAP/Readme.md @@ -0,0 +1,7 @@ + +[Clickhouse](/Database/OLAP/Clickhouse.md) +Doris +Starrocks +Greenplum + +> [awesome-olap](https://github.com/samber/awesome-olap) diff --git a/Database/OLAP/img/001-dis-send-query.webp b/Database/OLAP/img/001-dis-send-query.webp new file mode 100644 index 0000000..f644850 Binary files /dev/null and b/Database/OLAP/img/001-dis-send-query.webp differ diff --git a/Database/OLAP/img/002-dis-merge-result.webp b/Database/OLAP/img/002-dis-merge-result.webp new file mode 100644 index 0000000..eaf8ec3 Binary files /dev/null and b/Database/OLAP/img/002-dis-merge-result.webp differ diff --git a/Database/Oracle.md b/Database/Oracle.md index 985bd89..43c94e9 100644 --- a/Database/Oracle.md +++ b/Database/Oracle.md @@ -1,13 +1,22 @@ -`目录 start` - -- [Oracle](#oracle) - - [安装](#安装) - - [Linux](#linux) - - [Docker安装](#docker安装) - - [Windows](#windows) - - [使用](#使用) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Oracle +date: 2018-11-21 10:56:52 +tags: + - Oracle +categories: + - 数据库 +--- + +**目录 start** + +1. [Oracle](#oracle) + 1. [安装](#安装) + 1. [Linux](#linux) + 1. [Docker安装](#docker安装) + 1. [Windows](#windows) + 1. [使用](#使用) + +**目录 end**|_2020-04-27 23:42_| **************************************** # Oracle > 强大的数据库, 稍微理解一下他的设计都觉得精妙 diff --git a/Database/OraclePerformances.md b/Database/OraclePerformances.md index 4a4394e..2d30d73 100644 --- a/Database/OraclePerformances.md +++ b/Database/OraclePerformances.md @@ -1,55 +1,64 @@ -`目录 start` - -- [1.Oracle的体系结构](#1oracle的体系结构) - - [因为太详细,但是没有一个给出各个模块之间的关系,显得特别凌乱,所以还要参考大二下的书](#因为太详细但是没有一个给出各个模块之间的关系显得特别凌乱所以还要参考大二下的书) - - [内存讲解](#内存讲解) - - [Oracle服务器 = 实例+进程](#oracle服务器--=-实例+进程) - - [Oracle实例](#oracle实例) - - [数据库的文件:](#数据库的文件) - - [与实例的连接](#与实例的连接) - - [连接方式](#连接方式) - - [一个服务器进程就拥有单独的不共享的PGA 程序全局区](#一个服务器进程就拥有单独的不共享的pga-程序全局区) - - [共享池](#共享池) - - [数据库高速缓冲区](#数据库高速缓冲区) - - [内存缓冲区顾问](#内存缓冲区顾问) - - [重做日志缓冲区](#重做日志缓冲区) - - [大池和Java池](#大池和java池) - - [内存缓冲区](#内存缓冲区) -- [2.](#2) -- [3.Oracle实例的管理](#3oracle实例的管理) - - [初始化参数文件](#初始化参数文件) - - [内容 :](#内容-) - - [静态参数文件 (文本文件)](#静态参数文件-(文本文件)) - - [动态参数文件 (二进制文件)](#动态参数文件-(二进制文件)) - - [3.6 启动数据库](#36-启动数据库) - - [3.7 将数据库设为限制模式(杀死普通用户,利于维护)](#37-将数据库设为限制模式(杀死普通用户利于维护)) - - [3.9 关闭数据库的四种方式:](#39-关闭数据库的四种方式) -- [4 数据字典和控制文件](#4-数据字典和控制文件) - - [数据字典简介](#数据字典简介) - - [数据字典中存放的信息](#数据字典中存放的信息) - - [数据字典部分使用](#数据字典部分使用) - - [动态视图(v$开头)](#动态视图v$开头) - - [移动控制文件的示例](#移动控制文件的示例) -- [5.重做日志文件](#5重做日志文件) - - [5.2 重做日志组](#52-重做日志组) - - [5.5 获取重做日志的信息](#55-获取重做日志的信息) - - [5.11 重做日志应用示例](#511-重做日志应用示例) - - [1.添加重做日志组](#1添加重做日志组) - - [2.重建原有的group 3(不活动状态)](#2重建原有的group-3(不活动状态)) - - [3.强制切换日志组 将当前组(group 1)多切换几次成inactive状态时再删除](#3强制切换日志组-将当前组(group-1)多切换几次成inactive状态时再删除) - - [4.操作原本的当前组,删除group 1 再添加1 不能修改1](#4操作原本的当前组删除group-1-再添加1-不能修改1) - - [6.表空间和数据文件的管理](#6表空间和数据文件的管理) - - [6.5 创建本地管理的表空间](#65-创建本地管理的表空间) - - [查询](#查询) - - [6.8 默认临时表空间](#68-默认临时表空间) - - [6.9 设置表空间脱机](#69-设置表空间脱机) - - [6.10 设置只读表空间](#610-设置只读表空间) - - [6.11 重置表空间的大小](#611-重置表空间的大小) - - [6.13 移动数据文件的方法](#613-移动数据文件的方法) - - [方法一:](#方法一) - - [方法二:](#方法二) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Oracle性能优化 +date: 2018-12-16 17:26:53 +tags: + - Oracle +categories: + - 数据库 +--- + +**目录 start** + +1. [1.Oracle的体系结构](#1oracle的体系结构) + 1. [因为太详细,但是没有一个给出各个模块之间的关系,显得特别凌乱,所以还要参考大二下的书](#因为太详细但是没有一个给出各个模块之间的关系显得特别凌乱所以还要参考大二下的书) + 1. [内存讲解](#内存讲解) + 1. [Oracle服务器 = 实例+进程](#oracle服务器--=-实例+进程) + 1. [Oracle实例](#oracle实例) + 1. [数据库的文件:](#数据库的文件) + 1. [与实例的连接](#与实例的连接) + 1. [连接方式](#连接方式) + 1. [一个服务器进程就拥有单独的不共享的PGA 程序全局区](#一个服务器进程就拥有单独的不共享的pga-程序全局区) + 1. [共享池](#共享池) + 1. [数据库高速缓冲区](#数据库高速缓冲区) + 1. [内存缓冲区顾问](#内存缓冲区顾问) + 1. [重做日志缓冲区](#重做日志缓冲区) + 1. [大池和Java池](#大池和java池) + 1. [内存缓冲区](#内存缓冲区) +1. [2.](#2) +1. [3.Oracle实例的管理](#3oracle实例的管理) + 1. [初始化参数文件](#初始化参数文件) + 1. [内容 :](#内容-) + 1. [静态参数文件 (文本文件)](#静态参数文件-(文本文件)) + 1. [动态参数文件 (二进制文件)](#动态参数文件-(二进制文件)) + 1. [3.6 启动数据库](#36-启动数据库) + 1. [3.7 将数据库设为限制模式(杀死普通用户,利于维护)](#37-将数据库设为限制模式(杀死普通用户利于维护)) + 1. [3.9 关闭数据库的四种方式:](#39-关闭数据库的四种方式) +1. [4 数据字典和控制文件](#4-数据字典和控制文件) + 1. [数据字典简介](#数据字典简介) + 1. [数据字典中存放的信息](#数据字典中存放的信息) + 1. [数据字典部分使用](#数据字典部分使用) + 1. [动态视图(v$开头)](#动态视图v$开头) + 1. [移动控制文件的示例](#移动控制文件的示例) +1. [5.重做日志文件](#5重做日志文件) + 1. [5.2 重做日志组](#52-重做日志组) + 1. [5.5 获取重做日志的信息](#55-获取重做日志的信息) + 1. [5.11 重做日志应用示例](#511-重做日志应用示例) + 1. [1.添加重做日志组](#1添加重做日志组) + 1. [2.重建原有的group 3(不活动状态)](#2重建原有的group-3(不活动状态)) + 1. [3.强制切换日志组 将当前组(group 1)多切换几次成inactive状态时再删除](#3强制切换日志组-将当前组(group-1)多切换几次成inactive状态时再删除) + 1. [4.操作原本的当前组,删除group 1 再添加1 不能修改1](#4操作原本的当前组删除group-1-再添加1-不能修改1) + 1. [6.表空间和数据文件的管理](#6表空间和数据文件的管理) + 1. [6.5 创建本地管理的表空间](#65-创建本地管理的表空间) + 1. [查询](#查询) + 1. [6.8 默认临时表空间](#68-默认临时表空间) + 1. [6.9 设置表空间脱机](#69-设置表空间脱机) + 1. [6.10 设置只读表空间](#610-设置只读表空间) + 1. [6.11 重置表空间的大小](#611-重置表空间的大小) + 1. [6.13 移动数据文件的方法](#613-移动数据文件的方法) + 1. [方法一:](#方法一) + 1. [方法二:](#方法二) + +**目录 end**|_2020-04-27 23:42_| **************************************** # 1.Oracle的体系结构 @@ -73,7 +82,7 @@ ## 与实例的连接 - 输入用户名和密码,就是一个用户进程,通过用户进程连接服务器进程,再连接到Oracle数据库上 -- 一个用户可以同时拥有多个会话(用一套帐号登录多次) +- 一个用户可以同时拥有多个会话(用一套账号登录多次) ## 连接方式 - 基于主机 本地模式 diff --git a/Database/PostgreSQL.md b/Database/PostgreSQL.md index 8a351e1..44f6e44 100644 --- a/Database/PostgreSQL.md +++ b/Database/PostgreSQL.md @@ -1,102 +1,59 @@ -`目录 start` - -- [Postgresql](#postgresql) - - [概述](#概述) - - [和MySQL对比](#和mysql对比) - - [安装](#安装) - - [安装客户端](#安装客户端) - - [安装服务端](#安装服务端) - - [Docker方式安装服务端](#docker方式安装服务端) - - [pull完整版](#pull完整版) - - [pull精简版](#pull精简版) - - [Dockerfile构建](#dockerfile构建) - - [解释Dockerfile文件](#解释dockerfile文件) - - [使用](#使用) - - [Postgresql终端命令行使用](#postgresql终端命令行使用) - - [用户和角色权限](#用户和角色权限) - - [创建用户](#创建用户) - - [修改权限](#修改权限) - - [Java使用](#java使用) - - [基础数据](#基础数据) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: PostgreSQL +date: 2018-12-16 17:27:21 +tags: + - PostgreSQL +categories: + - 数据库 +--- + +💠 + +- 1. [Postgresql](#postgresql) +- 2. [安装](#安装) + - 2.1. [Docker方式](#docker方式) +- 3. [管理](#管理) + - 3.1. [终端命令行使用](#终端命令行使用) + - 3.2. [用户和角色权限](#用户和角色权限) + - 3.2.1. [创建用户](#创建用户) + - 3.2.2. [修改权限](#修改权限) +- 4. [基础数据类型](#基础数据类型) +- 5. [DDL](#ddl) +- 6. [图数据库](#图数据库) +- 7. [应用](#应用) + - 7.1. [Java使用](#java使用) + - 7.2. [导入导出](#导入导出) + +💠 2024-09-27 19:50:28 **************************************** # Postgresql -- [ ] [该公司对于PostgreSQL的缺点陈列是否属实](http://www.onexsoft.com/onesql.html) -## 概述 > [PostgreSQL](http://www.cnblogs.com/fcode/articles/PostgreSQL.html) | [wiki](https://wiki.postgresql.org/wiki/Main_Page) -- 严格SQL标准 +- 严格实现SQL标准 - Schemas 和表,用户的关系: - Schemas相当于是一个数据库进行分类的文件夹 -## 和MySQL对比 +`PostgreSQL和MySQL对比` > [PostgreSQL 与 MySQL 相比,优势何在?](https://www.zhihu.com/question/20010554) > [Converting MySQL to PostgreSQL](https://en.wikibooks.org/wiki/Converting_MySQL_to_PostgreSQL) -## 安装 -### 安装客户端 -> `sudo apt-get install postgresql-client` +# 安装 +安装客户端 `sudo apt install postgresql-client` +安装服务端 `sudo apt install postgresql` -### 安装服务端 -> `sudo apt install postgresql` +## Docker方式 +> [Dockerhub 官方镜像](https://hub.docker.com/_/postgres/) -### Docker方式安装服务端 -> [官方镜像](https://hub.docker.com/_/postgres/) - -#### pull完整版 -- 或者: `docker pull postgres` [官方镜像](https://hub.docker.com/_/postgres/) +- `docker pull postgres` - 运行容器 `docker run --name mypostgre -i -t -p 5432:5432 postgres` - 客户端连接 `psql -h localhost -p 5432 -U postgres` -#### pull精简版 -- 下拉镜像:`docker pull postgres:alpine` | 因为个人系统客户端是9.6, 所以用`9.6-alpine`镜像 -- 构建容器: -```sh - docker run -d --name postgre \ - -e POSTGRES_PASSWORD=jiushi \ - -v gitea-db-data:/var/lib/postgresql/data \ - -p 5432:5432 \ - postgres:9.6-alpine -``` -- 容器中连接 进入postgresql终端 `docker exec -it postgre psql -U postgres` - - 客户端连接 `psql -h localhost -U postgres` -- 连接后 输入`\l` 列出所有数据库 即可查看连接成功与否 - -#### Dockerfile构建 -`Dockerfile` -```dockerfile - FROM ubuntu:16.04 - RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8 - RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list - RUN apt-get update && apt-get -y -q install python-software-properties software-properties-common \ - && apt-get -y -q install postgresql-9.4 postgresql-client-9.4 postgresql-contrib-9.4 - USER postgres - RUN /etc/init.d/postgresql start \ - && psql --command "CREATE USER pger WITH SUPERUSER PASSWORD 'pger';" \ - && createdb -O pger pgerdb - USER root - RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/9.4/main/pg_hba.conf - RUN echo "listen_addresses='*'" >> /etc/postgresql/9.4/main/postgresql.conf - EXPOSE 5432 - RUN mkdir -p /var/run/postgresql && chown -R postgres /var/run/postgresql - VOLUME ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"] - USER postgres - CMD ["/usr/lib/postgresql/9.4/bin/postgres", "-D", "/var/lib/postgresql/9.4/main", "-c", "config_file=/etc/postgresql/9.4/main/postgresql.conf"] -``` -- 构建容器 `docker build -t mypostgresql:9.4 .` - - 运行容器 `docker run --name mypostgre -i -t -p 5432:5432 mypostgresql:9.4` - - 使用客户端连接`psql -h localhost -p 5432 -U pger -W pgerdb` 口令:`pger` - -#### 解释Dockerfile文件 -> 待学习解释 - ************************************ -## 使用 +# 管理 > [PostgreSQL 9.6.0 手册](http://postgres.cn/docs/9.6/index.html) -### Postgresql终端命令行使用 +## 终端命令行使用 > [PostgreSQL新手入门](http://www.ruanyifeng.com/blog/2013/12/getting_started_with_postgresql.html) `用熟悉的MySQL命令来解释` - `\l` show databases @@ -115,9 +72,9 @@ - 可以使用pg_dump和pg_dumpall来完成。比如备份sales数据库: - pg_dump drupal>/opt/Postgresql/backup/1.bak -### 用户和角色权限 +## 用户和角色权限 -#### 创建用户 +### 创建用户 - `createuser -P -D -R -e playboy` //创建一个用户,-P要设置密码,-R,不参创建其他用户,-D不能创建数据库 - `create user myth` 不带login属性 - `create role myth` 具有login属性 @@ -125,7 +82,7 @@ - [修改默认登录不需要密码的配置](http://www.linuxidc.com/Linux/2013-04/83564p2.htm) -#### 修改权限 +### 修改权限 > [参考博客](http://blog.csdn.net/beiigang/article/details/8604578) > [参考博客_角色](http://www.cnblogs.com/stephen-liu74/archive/2012/05/18/2302639.html) > [配置](http://www.linuxidc.com/Linux/2013-04/83564p2.htm) @@ -142,12 +99,72 @@ - 在PostgreSQL中,首先需要创建一个代表组的角色,之后再将该角色的membership 权限赋给独立的角色即可。 - `GRANT CONNECT ON DATABASE test to father;` 角色赋予数据库test 连接权限和相关表的查询权限。 +> 注意:如果一个库授权给了用户A,库里面新建了表C 需要再次单独授权给用户A 否则A没有C表的权限 + +# 基础数据类型 +> [Chapter 8. Data Types](https://www.postgresql.org/docs/current/datatype.html) +> [PostgreSQL 数据类型](https://www.runoob.com/postgresql/postgresql-data-type.html) + +> 自动增长 +- 相比于MySQL的 AUTO_INCREMENT 关键字标记, pg将该特性设计为数据类型SERIAL, 但是在使用上没有MySQL方便 +- SMALLSERIAL 2字节 SERIAL 4字节 BIGSERIAL 8字节 + - 注意这个自增序列值实际上是在系统表维护的 `SELECT nextval(pg_get_serial_sequence('the_table', 'the_primary_key'));` + +- 在insert时,如果手动指定了id的值,那这个序列值不会跟着更新,下一次不带id去insert的时候就会冲突报错。 +```sql + create table t_user(id BIGSERIAL primary key , name varchar(31), email varchar(64), deprecated boolean ); + INSERT INTO t_user (id, name, email, deprecated) VALUES (22, 'test6', null, false); + -- 如果当前序列值为21 这个insert会报错,id重复 + INSERT INTO t_user (name, email, deprecated) VALUES ('test5', null, false); +``` +- `id int8 GENERATED ALWAYS AS IDENTITY primary key` 这种字段就无法通过insert values指定id的值,会直接报错。 +- `id int8 GENERATED BY DEFAULT AS IDENTITY primary key` 等价于 BIGSERIAL primary key + +因此最好的方式是insert完,手动通过setval更新序列值到当前表的最大值。 [PostgreSQL更新所有表序列值到当前表中最大值](https://hhao.wang/archives/545) + +```sql + SELECT nextval(pg_get_serial_sequence('t_user', 'id')); -- 自增 + SELECT currval(pg_get_serial_sequence('t_phone', 'id')); -- get + SELECT setval(pg_get_serial_sequence('t_phone', 'id'), 1000); -- set +``` + -### Java使用 + +- 日期类型转bigint `select to_char(period,'yyyymmdd')::bigint as period_int` + +# DDL +> 注意PG的查看表,函数,视图的定义(DCL)时很复杂,没有直观的语句类似`show create table`可以用,通常使用工具来查看表定义和函数定义视图定义等等。 + +- 元数据存储: PostgreSQL将数据库对象(表、列、索引等)的元数据存储在系统目录(如pg_catalog)中。 +- 数据类型: PostgreSQL支持多种数据类型、约束、继承等特性,这些复杂性使得直接生成一个简单的CREATE TABLE语句变得困难。 + - 为了准确地反映表的定义,需要考虑各种情况,比如默认值、约束、继承关系等。 +- 性能: 对于大型数据库生成 show create table 很耗费性能。 + +```sql +-- 简单查询出列 +SELECT attname AS column_name, format_type(atttypid, atttypmod) AS data_type, attnotnull AS is_nullable +FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'table_name') AND attnum > 0; +``` + +************************ + +# 图数据库 +[PostgreSQL 图式搜索(graph search)实践 ](https://developer.aliyun.com/article/328141) +[edgedb](https://github.com/edgedb/edgedb) + + +************************ +# 应用 + +## Java使用 > [Postgresql JDBC Driver](https://github.com/pgjdbc/pgjdbc) - [官方:springboot使用](https://springframework.guru/configuring-spring-boot-for-postgresql/) - [参考博客](https://www.netkiller.cn/java/spring/boot/postgresql.html) -## 基础数据 -> [ PostgreSQL中的数据类型](https://blog.csdn.net/jpzhu16/article/details/52140048) +## 导入导出 +> 导出 + +copy 方式,单连接复制出查询语句的结果 + +[JDBC: 长连接流式导出数据](/Java/AdvancedLearning/JDBC.md#长连接流式导出数据) diff --git a/Database/PostgreSQLAdvance.md b/Database/PostgreSQLAdvance.md index e907a7d..a67944c 100644 --- a/Database/PostgreSQLAdvance.md +++ b/Database/PostgreSQLAdvance.md @@ -1,14 +1,89 @@ -`目录 start` - -- [PostgreSQL Advance](#postgresql-advance) - - [性能分析](#性能分析) +--- +title: PostgreSQL进阶 +date: 2018-12-16 17:28:33 +tags: + - PostgreSQL +categories: + - 数据库 +--- -`目录 end` |_2018-09-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [PostgreSQL Advance](#postgresql-advance) +- 2. [Query](#query) + - 2.1. [元数据](#元数据) + - 2.2. [硬解析和软解析](#硬解析和软解析) + - 2.3. [PREPARE](#prepare) + - 2.4. [JOIN](#join) +- 3. [索引](#索引) +- 4. [事务](#事务) +- 5. [Explain](#explain) + +💠 2024-09-27 11:12:37 **************************************** # PostgreSQL Advance +> [Blog: 励志成为postgresql大神](https://www.modb.pro/u/430972) + +************************ + +# Query +## 元数据 +```sql + -- 查询表元数据(唯一性,必填,字段类型) + select a.attname as fieldName, + d.typname as type, + (case + when atttypmod - 4 > 0 then atttypmod - 4 + else 0 + end) length, + + (case + when (select count(*) + from pg_constraint + where conrelid = a.attrelid and conkey[1] = attnum and contype = 'u') > 0 then 'Y' + else 'N' + end) as un, + (case + when a.attnotnull = true then 'Y' + else 'N' + end) as nullable, + col_description(a.attrelid, a.attnum) as comment + from pg_attribute a + left join pg_class c on a.attrelid = c.oid + left join pg_type d on a.atttypid = d.oid + where attstattarget = -1 and c.relname = 'table_test' +``` + +## 硬解析和软解析 + +## PREPARE +> [PostgreSQL Prepare](https://jdbc.postgresql.org/documentation/server-prepare/) + +************************ + +> 执行计划问题 +- [关于PostgreSQL的绑定变量窥视的问题详解](http://www.pgsql.tech/article_103_10000095) + - PG11及以下的版本 会话参数 prepareThreshold 默认为5 12可以设置plan_cache_mode参数 +- [PostgreSQL 硬解析和通用执行计划](https://www.modb.pro/db/48162) `在 Oracle中被称为绑定变量窥视。但 PostgreSQL中并没有这样的定义,更严格地说,PostgreSQL叫custom执行计划和通用执行计划` +- [What are the bennefits of prepareThreshold = 5 in pgjdbc](https://stackoverflow.com/questions/56261410/what-are-the-bennefits-of-preparethreshold-5-in-pgjdbc) + +************************ + +## JOIN +TODO 大表和小表 join顺序是否和MySQL一样有要求 + +************************ + +# 索引 +> [Official Doc](https://www.postgresql.org/docs/11/indexes.html) + +# 事务 +MVCC WAL -## 性能分析 +************************ -查询分析 同MySQL一样, 使用 `EXPLAIN` + SQL +# Explain +[Official Doc](https://www.postgresql.org/docs/current/sql-explain.html) +TODO 理解 diff --git a/Database/Readme.md b/Database/Readme.md new file mode 100644 index 0000000..50a255c --- /dev/null +++ b/Database/Readme.md @@ -0,0 +1,12 @@ +# 数据库 +> [Readme](/Database/DataBase.md) + +# TODO +Greenplumn +StarRocks +Doris + + +## 自然语言转SQL +> [vanna](https://github.com/vanna-ai/vanna) +> [supersonic](https://github.com/tencentmusic/supersonic) diff --git a/Database/Redis.md b/Database/Redis.md index ea57d33..11204a4 100644 --- a/Database/Redis.md +++ b/Database/Redis.md @@ -1,54 +1,70 @@ -`目录 start` - -- [Redis](#redis) - - [Book](#book) - - [【windows上的基本配置】](#windows上的基本配置) - - [Linux下的使用](#linux下的使用) - - [docker安装redis](#docker安装redis) - - [命令安装](#命令安装) - - [解压即用](#解压即用) - - [redis配置文件](#redis配置文件) - - [Redis命令行常规使用](#redis命令行常规使用) - - [过期策略](#过期策略) - - [数据类型](#数据类型) - - [字符串 String](#字符串-string) - - [列表 list](#列表-list) - - [集合 set](#集合-set) - - [有序集合 zset](#有序集合-zset) - - [散列 hash](#散列-hash) - - [HyperLogLog](#hyperloglog) - - [GEO【地理位置】](#geo地理位置) - - [Pub/Sub发布订阅](#pubsub发布订阅) - - [事务](#事务) - - [服务器](#服务器) - - [Run Configuration](#run-configuration) - - [数据安全和性能](#数据安全和性能) - - [持久化策略](#持久化策略) - - [复制](#复制) - - [数据迁移](#数据迁移) - - [【Redis的使用】](#redis的使用) - - [作为日志记录](#作为日志记录) - - [作为网站统计数据](#作为网站统计数据) - - [存储配置信息](#存储配置信息) - - [自动补全](#自动补全) - - [构建锁](#构建锁) - - [任务队列](#任务队列) - - [编程语言的使用](#编程语言的使用) - - [Java 使用](#java-使用) - - [Python使用](#python使用) - - [webdis](#webdis) - -`目录 end` |_2018-08-26_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Redis +date: 2018-12-16 17:28:55 +tags: + - Redis +categories: + - 数据库 +--- + +💠 + +- 1. [Redis](#redis) + - 1.1. [Book](#book) +- 2. [安装和配置](#安装和配置) + - 2.1. [Windows](#windows) + - 2.2. [Linux](#linux) + - 2.2.1. [Docker 方式](#docker-方式) + - 2.2.2. [解压即用](#解压即用) + - 2.3. [Redis配置文件](#redis配置文件) +- 3. [数据类型](#数据类型) + - 3.1. [String](#string) + - 3.1.1. [BitMap](#bitmap) + - 3.1.2. [HyperLogLog](#hyperloglog) + - 3.2. [List](#list) + - 3.3. [Set](#set) + - 3.4. [Zset](#zset) + - 3.5. [Hash](#hash) + - 3.6. [Stream](#stream) + - 3.7. [GEO地理位置](#geo地理位置) +- 4. [Scan](#scan) +- 5. [Pipelining](#pipelining) +- 6. [Pub/Sub发布和订阅](#pubsub发布和订阅) +- 7. [客户端](#客户端) + - 7.1. [Java](#java) + - 7.2. [Python](#python) + - 7.3. [GUI客户端](#gui客户端) + - 7.4. [Cli](#cli) +- 8. [Project](#project) + - 8.1. [Codis](#codis) + - 8.2. [webdis](#webdis) + - 8.3. [Redis Stack](#redis-stack) +- 9. [Redis的应用场景](#redis的应用场景) + - 9.1. [分布式锁](#分布式锁) + - 9.2. [消息队列](#消息队列) +- 10. [Redis 缓存相关问题](#redis-缓存相关问题) + - 10.1. [缓存雪崩](#缓存雪崩) + - 10.2. [缓存击穿](#缓存击穿) + - 10.3. [缓存穿透](#缓存穿透) + +💠 2024-09-14 11:51:16 **************************************** # Redis -> [Redis官网](https://redis.io/) | [Redis中文社区](http://www.redis.cn/) | [Redis教程](http://www.runoob.com/redis/redis-tutorial.html) +> [Official Site](https://redis.io/) | [Redis中文社区](http://www.redis.cn/) | [Redis教程](http://www.runoob.com/redis/redis-tutorial.html) -- [Redis中文文档](http://redisdoc.com/index.html) +- [Redis Official doc zh](http://redisdoc.com/index.html) + +> [参考: nodejs + redis/mysql 连接池问题](https://www.cnblogs.com/laozhbook/p/nodejs_redis_connection_pool.html)`单线程问题` +> [Redis 命令参考](http://doc.redisfans.com/index.html)`中文版,注意版本时效性` ## Book -> [Redis设计与实现 第二版](http://www.shouce.ren/api/view/a/13483) +> [Redis设计与实现 第二版](http://www.shouce.ren/api/view/a/13483) +> [Redis 设计与实现](http://redisbook.com)`作者自建网站` + +*********************** -## 【windows上的基本配置】 +# 安装和配置 +## Windows - 注册为服务 - `redis-server --service-install redis.windows.conf --loglevel verbose` - 使用cmder @@ -57,66 +73,49 @@ - `requirepass redis1104` - 客户端登录 `auth redis1104` -## Linux下的使用 -### docker安装redis -> [docker-install-redis](/Linux/Container/DockerSoft.md#redis) +## Linux +包管理器安装 redis 如 debian系`apt install redis` arch系`pacman -S redis` -### 命令安装 -> 这样不太好做多个redis, 个人不喜欢这种方式 - -- 安装 `apt install redis` -- 开启数据库服务 `redis-server` -- 打开客户端 `redis-cli` +### Docker 方式 +> [docker-install-redis](/Linux/Container/DockerSoft.md#redis) ### 解压即用 > [下载我打包好的(仅适用于Linux平台)](https://github.com/Kuangcp/Configs/tree/master/Database/redis) -> [4.0.2](http://cloud.kuangcp.top/redis-4.0.2.zip) | [3.2.8](http://cloud.kuangcp.top/redis-3.2.8.zip) +> [4.0.2](http://cloud.kuangcp.top/redis-4.0.2.zip) | [3.2.8](http://cloud.kuangcp.top/redis-3.2.8.zip) `个人配置步骤:` -- 官网下载源码,执行`make`进行编译,编译完成后,复制src目录中的`redis-cli redis-server`就可以用了,redis-benchmark可选,测性能 - - 再复制下面的简化配置文件,或者使用源码中根目录下的配置文件自己配置下 +1. 从源码编译: [官网下载源码](https://redis.io/) + - src下执行`make`进行编译,编译完成后,复制src目录中的`redis-cli redis-server`就可以用了 + - `redis-benchmark` 压测工具 + +1. 配置文件: 再复制下面的简化配置文件,或者使用源码中根目录下的配置文件自己配置下 - [简化配置文件](https://raw.githubusercontent.com/Kuangcp/Configs/master/Database/redis/simple_redis.conf) -- 再创建以下两个脚本就可以便捷的使用redis了 -`server_redis.sh` -```sh - basepath=$(cd `dirname $0`; pwd) - echo $basepath - $basepath/redis-server $basepath/redis.conf>redis.log & -``` -`client_redis.sh` -```sh - basepath=$(cd `dirname $0`; pwd) - $basepath/redis-cli -p 6379 -``` - -**************************** -## redis配置文件 -- [配置文件讲解](https://github.com/Kuangcp/Configs/blob/master/Database/redis/explain_redis.conf) | [原始配置文件](https://github.com/Kuangcp/Configs/blob/master/Database/redis/redis.conf) -- `使用ing`[简化配置文件](https://github.com/Kuangcp/Configs/blob/master/Database/redis/simple_redis.conf) +1. 再下载脚本就可以便捷的使用redis了 [shell辅助脚本](https://github.com/Kuangcp/Configs/tree/master/Database/redis/helper) + +************************ -******** -## Redis命令行常规使用 +## Redis配置文件 +- [配置文件讲解](https://github.com/Kuangcp/Configs/blob/master/Database/redis/explain_redis.conf) | [原始配置文件](https://github.com/Kuangcp/Configs/blob/master/Database/redis/redis.conf) -- 关闭数据库 `shutdown` 他会在关闭前保存数据 -- 保存内存中数据 `save` -- 认证 `auth` 口令 -- 测试联通性 `ping` 连接成功会返回pong -- 模糊删除 - - 删除 6666端口 的 2数据库中`detail-2018-07-0*`模式的数据: `./redis-cli -p 6666 -n 2 keys "detail-2018-07-0*" | xargs ./redis-cli -p 6666 -n 2 del` +- [简化配置文件](https://github.com/Kuangcp/Configs/blob/master/Database/redis/simple_redis.conf) -### 过期策略 -- `expire key seconds` 设置键的过期时间 -- `PTTL/TTL key ` 查看键剩余过期时间(生存时间) ms/s - - -1表示永久 -2表示没有该key +************************** -### 数据类型 -> [中文文档](http://redisdoc.com/index.html) +# 数据类型 +> [社区: 中文文档](http://redisdoc.com/index.html) -#### 字符串 String +## String > 字符串就是字节组成的序列 可以放字节串,整数,浮点数 -- `set key newval nx `存在则set失败 -- `set key newval xx `不存在则set失败 +> `SET key value [EX seconds] [PX millisecounds] [NX|XX]` + +- EX seconds: 设置键的过期时间为second秒 +- PX millisecounds: 设置键的过期时间为millisecounds 毫秒 +- NX: 只在键不存在的时候,才对键进行设置操作 +- XX: 只在键已经存在的时候,才对键进行设置操作 + +SET操作成功后,返回的是OK,失败返回NIL + - `incr incrby decr decrby` 只要存入的String能被解析为数值,就能执行这些命令: 递增或者递减 - `incr` 是原子操作即并发的情况下不会有脏读(可用于主键生成策略) - `getset key val` get旧值并且set新值 @@ -127,11 +126,40 @@ - `del key` 返回1被删除,0 key不存在 - `type key` 返回值的类型 - `expire key secondes` 设置或改变超时时间,精度是秒或毫秒 - - `set key val ex secondes` set时设置超时时间 - `persist key` 去除超时时间 - `ttl key` 查看剩余存活时间 -1表示永久 -2表示没有该key -#### 列表 list +************************ + +### BitMap +> [参考: redis的bitset实战](https://segmentfault.com/a/1190000016296106) + +基于string, 可以操作每个 bit 的值 +- setbit key offset value + - `set key 上偏移量offset(2^32) 的 值 value(0/1)` +- getbit key offset +- bitop + - 主要做bitset的and、or、xor、not操作,结果存在新的bitset中,注意时间复杂度为O(N) +- bitpos + - 返回指定bitset中在指定起始位置中第一个出现指定值的offset,不传start,end默认估计是0,-1 +- bitcount + - 统计bitset中出现1的个数 + +可以基于bitmap手动实现BloomFilter,也可以直接使用RedisStack的BloomFilter组件。 + +### HyperLogLog +> 用于做基数统计的算法 + +HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的 + +- PFADD 添加元素到制定 HyperLogLog 中 +- PFCOUNT 返回给定 HyperLogLog 的基数估算值。 +- PFMERGE 将多个 HyperLogLog 合并为一个 HyperLogLog + +************************ + +## List +- llen - `rpush key val val val `右/尾添加元素 lpush是左/头,若表不存在就新建 - `rpushx key value` 若表不存在就什么都不做,否则尾插元素 - `rpop key` 从list右/尾端中删除元素返回元素值 没有了就返回null @@ -149,7 +177,7 @@ - `bpoplpush` - `brpoplpush` 阻塞式先右弹再左进 -#### 集合 set +## Set - `SADD key member [member ...]` - `SCARD key` 返回集合 key 的基数(集合中元素的数量)。 - `SDIFF key [key ...]` 返回一个集合的全部成员,该集合是所有给定集合之间的差集。不存在的 key 被视为空集。 @@ -166,18 +194,21 @@ - `SUNIONSTORE destination key [key ...]` - `SSCAN key cursor [MATCH pattern] [COUNT count]` 参考 SCAN 命令 -#### 有序集合 zset +************************ + +## Zset > 元素是键值对,键是member成员,值是score分值必须是浮点数 -- _zadd_ 将一个给定分值的成员添加到有序集合里 -- ZCARD -- ZCOUNT -- _zincrby_ 自增 +- ZADD 将一个给定分值的成员添加到有序集合里 +- ZCARD 获取有序集合的成员数 +- ZCOUNT min max 计算在有序集合中指定区间分数的成员数 +- ZINCRBY key increment member 自增 -- _zrange_ 根据元素在有序集合中的位置,从有序集合中从小到大获取多个元素 +- ZRANGE 根据元素在有序集合中的位置,从有序集合中从小到大获取多个元素 - `zrange name 0 -1 withscores` 获取所有并获取分值 - `zrange name 0 3 withscores` 获取分数最少的4个键值对 -- _zrevrange_ 相反的, 从大到小 + +- ZREVRANGE 相反的, 从大到小 - _zrangebyscore_ 获取有序集合在给定范围中的所有元素 - `zrangebyscore name 0 200 withscores` @@ -187,7 +218,7 @@ - ZREMRANGEBYSCORE - ZREVRANGEBYSCORE - ZREVRANK -- ZSCORE +- `ZSCORE key member` 依据指定member获取score - ZUNIONSTORE - `zinterstore` 进行集合之间的并集(可以看作关系型数据库的多表连接) - ZSCAN @@ -195,8 +226,10 @@ - ZLEXCOUNT - ZREMRANGEBYLEX -#### 散列 hash -> (类似Map 嵌套,一个内置的微型redis) +************************ + +## Hash +> key-value 结构 - HDEL 删除散列中指定的K - HEXISTS @@ -214,12 +247,11 @@ - HSCAN - HSTRLEN -#### HyperLogLog -- PFADD -- PFCOUNT -- PFMERGE +## Stream + +************************ -#### GEO【地理位置】 +## GEO地理位置 - GEOADD - GEOPOS - GEODIST @@ -227,8 +259,37 @@ - GEORADIUSBYMEMBER - GEOHASH -*************** -### Pub/Sub发布订阅 +************************ +# Scan +- **SCAN** 命令用于迭代当前数据库中的数据库键 相较于 keys 降低阻塞进程的概率。 + - cursor 游标 + - 注意这个游标不是 常见的 fori 循环里的i规律递增,第一次 sscan 会返回 cursor(第一个参数) 需要下一次拿这个 cursor 作为参数继续获取 + - 直到 `返回 0` 表示迭代完成 如果数据发生变化游标也会变化,且 count 是不保证准确数量的 + - count 数量 + - redis 只保证返回的数据数量大于等于 count. **注意count不能小于1 否则报 syntax error** + - match pattern 匹配key的模式 + - 因为 这种不易理解的迭代方式, Spring 的 RedisTemplate 只提供了 count pattern 参数 cursor 默认为0 + +- **SSCAN** 命令用于迭代 Set 键中的元素。 +- **HSCAN** 命令用于迭代哈希键中的键值对。 +- **ZSCAN** 命令用于迭代有序集合中的元素(包括元素成员和元素分值) + +> 使用SCAN命令代替原有全查询命令更安全,因为是部分查询不容易像全查询命令那样阻塞Redis进程,因此往往生产环境会禁止全查询命令 keys smembers 等 + +> 注意 scan 命令只能顺序依据返回的cursor进行查找,而且由于实现方式,不一定每次查询是有数据的 +> 也就导致了在有大量key的db里面 找到 match pattern 的所有key 靠手工执行scan一次次找是不可能的 + +************************ + +# Pipelining +> 一次请求/响应服务器能实现处理新的请求即使旧的请求还未被响应。这样就可以将多个命令发送到服务器,而不用等待回复,最后在一个步骤中读取该答复。 + +- `(printf "PING\r\nPING\r\nPING\r\n"; sleep 1) | nc localhost 6379` + +************************ + +# Pub/Sub发布和订阅 +> 基于 阻塞 list 实现 - `PSUBSCRIBE pattern [pattern ...]` - 订阅一个或多个符合给定模式的频道。每个模式以 * 作为匹配符,比如 it* 匹配所有以 it 开头的频道( it.news 、 it.blog 、 it.tweets 等等), @@ -249,89 +310,127 @@ - 指示客户端退订给定的频道。如果没有频道被指定,也即是,一个无参数的 UNSUBSCRIBE 调用被执行, - 那么客户端使用 SUBSCRIBE 命令订阅的所有频道都会被退订。在这种情况下,命令会返回一个信息,告知客户端所有被退订的频道。 -************** -### 事务 - -- `DISCARD` 取消事务,放弃执行事务块内的所有命令。 -- `EXEC` - - 执行所有事务块内的命令。假如某个(或某些) key 正处于 WATCH 命令的监视之下,且事务块中有和这个(或这些) key 相关的命令, - - 那么 EXEC 命令只在这个(或这些) key 没有被其他命令所改动的情况下执行并生效,否则该事务被打断(abort)。 -- `MULTI` 标记一个事务块的开始。事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。 -- `UNWATCH` - - 取消 WATCH 命令对所有 key 的监视。如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。 - - 因为 EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD 命令在取消事务的同时也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行 UNWATCH 了。 -- `WATCH key [key ...]` - - 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 - -************* -### 服务器 - -- BGREWRITEAOF -- BGSAVE -- CLIENT GETNAME -- CLIENT KILL -- CLIENT LIST -- CLIENT SETNAME -- CONFIG GET -- CONFIG RESETSTAT -- CONFIG REWRITE -- CONFIG SET -- DBSIZE -- DEBUG OBJECT -- DEBUG SEGFAULT -- FLUSHALL -- FLUSHDB -- INFO -- LASTSAVE -- MONITOR -- PSYNC -- SAVE -- SHUTDOWN -- SLAVEOF -- SLOWLOG -- SYNC -- TIME -***************************** - -### Run Configuration -- *slaveof* - - `redis-server --port 9999 --slaveof 127.0.0.1 6379` 启动一个9999端口作为6379的从服务器进行同步 - - 或者服务启动后执行 `slaveof host port`(如果已经是从服务器,就丢去旧服务器的数据集,转而对新的主服务器进行同步) - - 从服务变成主服务 `slaveof no one` (同步的数据集不会丢失,迅速替换主服务器) -- *loglevel* - - `./redis-server /etc/redis/6379.conf --loglevel debug ` - -*********** -## 数据安全和性能 -### 持久化策略 -### 复制 - -### 数据迁移 -- 使用主从复制来进行数据, 或者自己写Py脚本? - -******* -## 【Redis的使用】 -### 作为日志记录 -### 作为网站统计数据 -### 存储配置信息 -### 自动补全 -- 搜索建议 - -### 构建锁 - -### 任务队列 -- 发送邮件 - -## 编程语言的使用 -*************************** -### Java 使用 -******************* -### Python使用 +************************ + +# 客户端 +> [program language client](http://www.redis.com.cn/clients) + +## Java +> [详细](/Java/Ecosystem/JavaRedis.md) + +************************ + +## Python > pip install redis 该模块和redis命令的用法几乎一模一样, 上手很快 - [redis文档](https://pypi.python.org/pypi/redis/) `python操作redis的库的文档` -### webdis +## GUI客户端 +> [官方收录 客户端](https://redis.io/clients) | [alternativeto 列表](https://alternativeto.net/software/redily/) + +> [Redis Desktop Manager](https://github.com/uglide/RedisDesktopManager/) +> [Another Redis DeskTop Manager](https://gitee.com/qishibo/AnotherRedisDesktopManager) + +> arch 上暂时存在这个问题导致无法使用 Redis Desktop Manager [Github Issues](https://github.com/uglide/RedisDesktopManager/issues/4826) +1. rm -rf ~/.cache/fontconfig +1. rm -rf ~/snap/redis-desktop-manager/common/.cache/fontconfig +1. fc-cache -r + +## Cli +- redis-cli +- redli + - go install github.com/IBM-Cloud/redli@latest + +************************ + +> [FastoRedis](https://fastoredis.com/) +> [Redis Plus](https://gitee.com/MaxBill/RedisPlus) +> Redily +> [Medis](https://github.com/luin/medis) +> [rdbtools](https://rdbtools.com) +> p3x-redis-ui + +********* + +# Project +> 衍生项目 + +## Codis +> [Github: Codis](https://github.com/CodisLabs/codis) +> [Kedis](https://gitee.com/kehaw9818/Kedis) + +## webdis > 将redis变为一个简单的web接口 > [官网](http://webd.is/) | [Github地址](https://github.com/nicolasff/webdis) +## Redis Stack +> [Github Redis Stack](https://github.com/redis-stack/redis-stack) + +Redis Stack 是一组软件套件,它主要由三部分组成。Redis Stack Server,RedisInsight,Redis Stack 客户端 SDK。 其中 Redis Stack Server 由 Redis,RedisSearch,RedisJSON,RedisGraph,RedisTimeSeries 和 RedisBloom 组成。 + +可支撑如下业务 +- 索引和查询Redis数据,聚合运算,`全文检索` +- 运行高级向量相似性搜索 `(KNN)` +- 有效地存储和操作嵌套的 `JSON 文档` +- 将关系构建和建模为`属性图` +- 存储、查询和聚合`时间序列数据` +- 利用快速、空间和计算高效的`概率数据结构` + +************************ + +# Redis的应用场景 +> [Redis的n种妙用,不仅仅是缓存 ](https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650123010&idx=2&sn=c17bd9192daa15c00502b7e27acacc61&chksm=f36bb623c41c3f35060bf244eddddc25ea6e2b96900f57299e0d8ffe548a08823b057dee5baf&mpshare=1&scene=1&srcid=0109PazxT49BtR2oCJ6Od32h&pass_ticket=ZX4WKje%2FJzbdB6LEivhrNCtzmljNugDZul02fl5SX4snt5QLMa6Cle9o1I5CumfQ#rd) + +> [参考: 为什么我们做分布式使用Redis?](https://my.oschina.net/u/3971241/blog/2221560)`缓存的场景和应对措施` + +## 分布式锁 +> [Doc: setnx](http://cndoc.github.io/redis-doc-cn/cn/commands/setnx.html)`包含以此命令设计锁的一些缺陷` +> [redisson](https://github.com/redisson/redisson) + +单机 使用 setnx, redis分布式部署的情况下使用RedLock + +> [基于Redis的分布式锁到底安全吗(上)?](https://mp.weixin.qq.com/s/JTsJCDuasgIJ0j95K8Ay8w) +> [基于Redis的分布式锁到底安全吗(下)?](https://mp.weixin.qq.com/s/4CUe7OpM6y1kQRK8TOC_qQ?) +> [参考: Redis 分布式锁进化史解读 + 缺陷分析](https://zhuanlan.zhihu.com/p/161078350) + +> [参考: redis分布式锁在MySQL事务代码中使用](https://blog.csdn.net/seapeak007/article/details/99337781) +> [参考: Lua脚本在redis分布式锁场景的运用](https://www.cnblogs.com/demingblog/p/9542124.html) + +## 消息队列 +> List, Pub/Sub, Stream 可实现, 可靠性依次增加,但依然会有消息丢失问题 + +> [asynq](https://github.com/hibiken/asynq) + +************************ + +`搜索` +> [RediSearch](https://github.com/RediSearch/RediSearch) + +************************ + +# Redis 缓存相关问题 +## 缓存雪崩 +同一时间大量缓存失效,请求都打到DB,导致DB负载过大甚至宕机。 + +与此同时,大量缓存集中失效会让Redis瞬时OPS很高,操作的延迟会突增。 + +1. 大量 key 使用了相同的过期时间 + - 过期时间加随机值或者特定算法分散过期时间 + - 使用本地缓存(JVM级别) + - 当请求过多,提供服务降级 +1. Redis发生重启(Redis 未做持久化) + - 启动时预先加载 热点Key + +## 缓存击穿 +针对缓存中没有但是DB中有的数据请求 + +1. 当某个Key失效后,瞬间涌入大量的请求同一个Key,这些请求不会命中Redis,都会请求到DB,导致数据库压力过大 + 1. 设置热点Key,自动检测热点Key,将热点Key的过期时间加大或者永不过期。 + 2. 在更新缓存时加互斥锁。当发现没有命中Redis,去查数据库的时候,在执行更新缓存的操作上加锁,当一个线程访问时,其它线程等待 + - 这个线程访问过后,缓存中的数据会被重建,这样其他线程就可以从缓存中取值。 + +## 缓存穿透 +针对缓存和 DB 都没有的数据 请求 + +1. 对查询结果为空的情况也进行缓存,这样,再次访问时,缓存层会直接返回空值。缓存时间设置短一点,或者该key对应的数据insert了之后清理缓存。 +2. 对一定不存在的key进行过滤。例如: 布隆过滤器 \ No newline at end of file diff --git a/Database/RedisAdvance.md b/Database/RedisAdvance.md new file mode 100644 index 0000000..8e583a0 --- /dev/null +++ b/Database/RedisAdvance.md @@ -0,0 +1,451 @@ +--- +title: Redis进阶 +date: 2018-12-16 17:29:06 +tags: + - Redis +categories: + - 数据库 +--- + +💠 + +- 1. [Redis底层数据结构](#redis底层数据结构) + - 1.1. [SDS](#sds) + - 1.2. [链表](#链表) + - 1.3. [字典](#字典) + - 1.4. [跳表](#跳表) + - 1.5. [整数集合](#整数集合) + - 1.6. [压缩列表](#压缩列表) + - 1.7. [对象](#对象) +- 2. [Redis常用命令](#redis常用命令) + - 2.1. [Run Configuration](#run-configuration) + - 2.2. [过期](#过期) + - 2.3. [事务](#事务) + - 2.4. [服务器](#服务器) + - 2.5. [实现原理](#实现原理) + - 2.5.1. [Scan](#scan) +- 3. [数据安全和性能](#数据安全和性能) + - 3.1. [Latency](#latency) + - 3.2. [big key](#big-key) + - 3.3. [hot key](#hot-key) + - 3.4. [Key eviction](#key-eviction) +- 4. [Lua](#lua) +- 5. [Tip](#tip) + - 5.1. [禁用 O(N) 命令](#禁用-on-命令) + - 5.2. [错误分析](#错误分析) +- 6. [部署方式](#部署方式) + - 6.1. [单机](#单机) + - 6.2. [主从](#主从) + - 6.3. [哨兵](#哨兵) + - 6.4. [Cluster 集群](#cluster-集群) +- 7. [Redis 持久化](#redis-持久化) + +💠 2024-09-13 10:39:04 +**************************************** +# Redis底层数据结构 +## SDS +> 简单动态字符串 [sds: Simple Dynamic Strings](https://github.com/antirez/sds) + +- [ ] Redis存储数值的方式,以下场景 +``` +0 00110000 +(short 0) 0011000001010011 +0L 0011000001001100 +"" 0010001000100010 +new byte[0] 011110000010011100100111 +``` + +## 链表 +## 字典 + +## 跳表 +> [跳表基础](/Skills/CS/DS/LinearList.md) + +> [Redis设计与实现: 跳跃表的实现](http://redisbook.com/preview/skiplist/datastruct.html) + +Redis 的跳跃表由 redis.h/zskiplistNode 和 redis.h/zskiplist 两个结构定义, 其中 zskiplistNode 结构用于表示跳跃表节点 +而 zskiplist 结构则用于保存跳跃表节点的相关信息, 比如节点的数量, 以及指向表头节点和表尾节点的指针 等等。 + +```C + typedef struct zskiplistNode + { + // 后退指针 + struct zskiplistNode *backward; + // 分值 + double score; + // 成员对象 + robj *obj; + + // 层 + struct zskiplistLevel { + // 前进指针 + struct zskiplistNode *forward; + // 跨度 + unsigned int span; + } level[]; + } zskiplistNode; +``` +## 整数集合 + +## 压缩列表 + +## 对象 + +************************ + +# Redis常用命令 + +- 关闭数据库 `shutdown` 该命令会在关闭数据库前保存数据 +- 保存内存中数据到文件 `save` +- 认证 `auth 口令` +- 测试联通性 `ping` 连接成功会返回pong + +- 模糊删除 + - 删除 6666端口 的 2数据库中`detail-2018-07-0*`模式的数据: `./redis-cli -p 6666 -n 2 keys "detail-2018-07-0*" | xargs ./redis-cli -p 6666 -n 2 del` + +- 查看所有连接 client list + +> [redis-stat](https://github.com/junegunn/redis-stat) + +## Run Configuration +- *slaveof* + - `redis-server --port 9999 --slaveof 127.0.0.1 6379` 启动一个9999端口作为6379的从服务器进行同步 + - 或者服务启动后执行 `slaveof host port`(如果已经是从服务器,就丢去旧服务器的数据集,转而对新的主服务器进行同步) + - 从服务变成主服务 `slaveof no one` (同步的数据集不会丢失,迅速替换主服务器) + +- *loglevel* + - `./redis-server /etc/redis/6379.conf --loglevel debug ` + +## 过期 +- `expire key seconds` 设置键的过期时间 +- `PTTL/TTL key ` 查看键剩余过期时间(生存时间) ms/s + - -1 表示永久 -2 表示没有该key + +## 事务 + +- `DISCARD` 取消事务,放弃执行事务块内的所有命令。 +- `EXEC` + - 执行所有事务块内的命令。假如某个(或某些) key 正处于 WATCH 命令的监视之下,且事务块中有和这个(或这些) key 相关的命令, + - 那么 EXEC 命令只在这个(或这些) key 没有被其他命令所改动的情况下执行并生效,否则该事务被打断(abort)。 +- `MULTI` 标记一个事务块的开始。事务块内的多条命令会按照先后顺序被放进一个队列当中,最后由 EXEC 命令原子性(atomic)地执行。 +- `UNWATCH` + - 取消 WATCH 命令对所有 key 的监视。如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 UNWATCH 了。 + - 因为 EXEC 命令会执行事务,因此 WATCH 命令的效果已经产生了;而 DISCARD 命令在取消事务的同时也会取消所有对 key 的监视,因此这两个命令执行之后,就没有必要执行 UNWATCH 了。 +- `WATCH key [key ...]` + - 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 + +## 服务器 + +- BGREWRITEAOF +- BGSAVE +- CLIENT GETNAME +- CLIENT KILL +- CLIENT LIST +- CLIENT SETNAME +- CONFIG GET +- CONFIG RESETSTAT +- CONFIG REWRITE +- CONFIG SET +- DBSIZE +- DEBUG OBJECT +- DEBUG SEGFAULT +- FLUSHALL +- FLUSHDB +- INFO + - [参考: redis info 命令查看redis使用情况](https://blog.csdn.net/kexiaoling/article/details/51810919) + - info stats 中 total_commands_processed 是实际请求, 还是说redis自己执行的命令 TODO +- LASTSAVE +- [MONITOR](https://redis.io/docs/latest/commands/monitor/) + - debug命令,可以将Redis执行的每一条指令都回传并输出,可以用来做Redis流量复制,注意对性能影响很大,生产慎用。 + - [Redis 流量复制、流量回放、流量镜像](http://www.kailing.pub/article/index/arcid/342.html) +- PSYNC +- SAVE +- SHUTDOWN +- SLAVEOF +- SLOWLOG +- SYNC +- TIME + +## 实现原理 +### Scan +> [Doc: Scan](https://redis.io/commands/scan/) + +由于 Redis 是单线程多路复用机制(Redis6引入多线程),使用 O(n) 复杂度的命令容易阻塞进程,因此需要 scan 命令来实现分批执行 (`注意 scan如果模式匹配的范围比较大,同样有 keys 一样的影响`) + +************************ + +# 数据安全和性能 +## Latency +缓存最重要的性能指标就是延迟,但是延迟会受到多项业务或功能影响。[Redis为什么变慢了?一文讲透如何排查Redis性能问题 ](https://mp.weixin.qq.com/s?__biz=Mzg4Nzc3NjkzOA==&mid=2247486198&idx=1&sn=e4b34ef7889bb95260e3a636662a7192&chksm=cf847933f8f3f025a1b00fc965781a33024158a4275ebaf2da393c403500a86d9c04af16ce40#rd) + +- 60 秒内的最大响应延迟 `redis-cli -h 127.0.0.1 -p 6379 --intrinsic-latency 60` +- 间隔 1 秒,采样 Redis 的平均操作耗时 `redis-cli -h 127.0.0.1 -p 6379 --latency-history -i 1` + +> SLOWLOG +- 命令执行耗时超过 5 毫秒,记录慢日志 `CONFIG SET slowlog-log-slower-than 5000` +- 只保留最近 500 条慢日志 `CONFIG SET slowlog-max-len 500` +- 查看最近5条慢日志 `SLOWLOG get 5` + +## big key +bigkey 在很多场景下都会产生性能问题,因此业务应用尽量避免写入。 + +例如,bigkey 在分片集群模式下,对于数据的迁移也会有性能影响,以及我后面即将讲到的数据过期、数据淘汰、透明大页,都会受到 bigkey 的影响。 + +- `redis-cli --bigkeys` + - 对线上实例进行 bigkey 扫描时,Redis 的 OPS 会突增,为了降低扫描过程中对 Redis 的影响,最好控制一下扫描的频率,指定 -i 参数即可,它表示扫描过程中每次扫描后休息的时间间隔,单位是秒 + - 扫描结果中,对于容器类型(List、Hash、Set、ZSet)的 key,只能扫描出元素最多的 key。但一个 key 的元素多,不一定表示占用内存也多,你还需要根据业务情况,进一步评估内存占用情况 +- `memory usage key` 返回字节数 +- `debug object key` +- `redis-memory-for-key -s localhost -p 6667 key` + - pip install rdbtools + +- 如果Redis 是 4.0 以上版本,用 UNLINK 命令替代 DEL,此命令可以把释放 key 内存的操作,放到后台线程中去执行,从而降低对 Redis 的影响 +- 如果Redis 是 6.0 以上版本,可以开启 lazy-free 机制(lazyfree-lazy-user-del = yes),在执行 DEL 命令时,释放内存也会放到后台线程中执行 + +## hot key + +> [大Key/热Key分析/过期Key扫描](https://support.huaweicloud.com/dcs_faq/dcs-faq-0805001.html) + +************************ + +## Key eviction +> [Key eviction](https://redis.io/docs/latest/develop/reference/eviction/) + +当Redis使用内存达到 maxmemory 时会依据**驱逐策略**(LRU,LFU等)删除key,回收内存。 + +> 参考:[Redis实例的数据逐出策略是什么?](https://support.huaweicloud.com/dcs_faq/dcs-faq-0427031.html) + +************************ + +# Lua +> [Scripting with Lua](https://redis.io/docs/latest/develop/interact/programmability/eval-intro/) + +- EVAL + - eval script keyNum key* arg* : keyNum是指key的数量 key和 arg 都是多值, 可以把eval理解为lambda函数,函数操作传入的key和变量 + - 注意第一次执行后就会将脚本缓存起来,后续可以替换为 EVALSHA 命令执行了 + - 可以看到 spring-redis 的 DefaultScriptExecutor#eval 优先会执行evalsha,由脚本内容计算出sha1 +- SCRIPT LOAD + - 注意 EVAL执行的脚本都会缓存在Redis的缓存中 通过INFO查看 used_memory_scripts_eval 和 number_of_cached_scripts, 所以动态拼接的脚本是不合理的,应该将变化都封装成参数 + - 所以将脚本复用会更高效(复用缓存,降低命令长度),SCRIPT LOAD 会返回一个SHA1信息摘要 可以用sha1sum命令验证返回值 + - 注意 该脚本缓存**是不可靠的**,Redis重启,Cluster模式,主从切换,故障恢复,SCRIPT FLUSH命令的执行,都会导致脚本缓存丢失,所以需要在EVALSHA做容错,缓存丢失后重新LOAD + - 所以通用的方案是 正常执行 EVALSHA 如果报错了就再Load一次,再执行 EVALSHA +- EVALSHA + - 与EVAL用法一致,但是脚本参数替换为了 LOAD 返回的 sha1 +- SCRIPT FLUSH +- SCRIPT EXISTS SHA1 1存在 0不存在 +- SCRIPT KILL 停止长时间运行的脚本 +- SCRIPT DEBUG + +> 注意 + +- Lua脚本在Redis执行时是原子性的,所以可用来做分布式锁等强一致性场景 [Why locks in Lua?](https://redis.io/ebook/part-3-next-steps/chapter-11-scripting-redis-with-lua/11-2-rewriting-locks-and-semaphores-with-lua/11-2-1-why-locks-in-lua/) +- [Redis functions](https://redis.io/docs/latest/develop/interact/programmability/functions-intro/) 从Redis7开始支持通过Lua扩展出自定义函数 FCALL 方式调用自定义函数 +- 当Redis是Cluster模式部署时,lua脚本操作的所有key需要保证在同一个slot中。`CROSSSLOT Keys in request don’t hash to the same slot` + - [Redis Pipeline中调用Lua脚本报错JedisMoveDataException的问题](https://blog.csdn.net/minghao0508/article/details/130827658) + +************************ + +> 限制数量的令牌桶限流机制 Java实现 +```java + public static final String JUDGE_SCRIPT = "local cnt = redis.call('incr', KEYS[1]);" + + " if (tonumber(cnt) > tonumber(ARGV[1]) ) then redis.call('decr', KEYS[1]); return 0;" + + " else return 1; end"; + + public void acquireBlock(String key, int maxConcurrency) { + while (!this.acquire(key, maxConcurrency)) { + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch (Exception e) { + log.error("", e); + } + } + } + + public int runCount(String key) { + Object val = redisTemplate.opsForValue().get(key); + if (Objects.isNull(val)) { + return 0; + } + return Integer.parseInt(val.toString()); + } + + public boolean acquire(String key, int maxConcurrency) { + // 指定 lua 脚本,并且指定返回值类型 + DefaultRedisScript redisScript = new DefaultRedisScript<>(JUDGE_SCRIPT, Integer.class); + // 参数一:redisScript,参数二:key列表,参数三:arg(可多个) + Object lockB = redisTemplate.execute(redisScript, Collections.singletonList(key), maxConcurrency); + if (Objects.isNull(lockB)) { + return false; + } + return Integer.parseInt(lockB.toString()) > 0; + } + + public Long release(String key) { + return redisTemplate.opsForValue().decrement(key); + } +``` + +************************ + +# Tip +## 禁用 O(N) 命令 +keys flushdb flushall + +- List: lindex、lset、linsert +- Hash: hgetall、hkeys、hvals +- Set: smembers、sunion、sunionstore、sinter、sinterstore、sdiff、sdiffstore +- Sorted Set: zrange、zrevrange、zrangebyscore、zrevrangebyscore、zremrangebyrank、zremrangebyscore + +在 redis.conf 中通过配置 rename-command 进行禁用 + +## 错误分析 + +1. `JedisConnectionException: Could not get a resource from the pool` cause by `java.util.NoSuchElementException: Unable to validate object` + - 多种原因, 由于设置了 testOnBorrow 为 true, 那么在每次获取数据时, 就会先测试性的获取一个数据, 然后校验能否正常拿到该数据 如果拿不到就抛出这个异常, 原因可能有: + 1. 根本没有连接上Redis, 配置有问题 端口 bind 什么的 + 1. Redis 存放数据的 rdb 文件所在目录 没有存储空间了 + 1. 没有内存空间了, 由于执行save操作时, 会进行fork子进程 然后进行持久化 TODO 验证 +1. `ERR 'EVAL' command keys must in same slot` + - 由于Lua脚本执行在Cluster模式下需要保证操作的key在相同的slot中。 + - 解决方案 强制加入花括号 指定计算slot的部分,保证key会分配到相同的slot。例如:`{prefix}a` 和 `{prefix}b` + +************************ + +# 部署方式 +> [参考: redis哨兵、集群](https://blog.csdn.net/u012129558/article/details/77146287) + +## 单机 +- 优点: + 1. 架构简单,部署方便; + 1. 高性价比:缓存使用时无需备用节点(单实例可用性可以用supervisor或crontab保证),当然为了满足业务的高可用性,也可以牺牲一个备用节点,但同时刻只有一个实例对外提供服务; + 1. 高性能,单线程多路复用。 + +- 缺点: + 1. 不保证数据的可靠性; + 1. 在缓存使用,进程重启后,数据丢失,即使有备用的节点解决高可用性,但是仍然不能解决缓存预热问题,因此不适用于数据可靠性要求高的业务; + 1. 高性能受限于单核CPU的处理能力(Redis是单线程机制),CPU为主要瓶颈,所以适合操作命令简单,排序、计算较少的场景。也可以考虑用Memcached替代。 + +## 主从 +Redis多副本,采用主从(replication)部署结构,相较于单副本而言最大的特点就是主从实例间数据实时同步,并且提供数据持久化和备份策略。 +主从实例部署在不同的物理服务器上,根据公司的基础环境配置,可以实现同时对外提供服务和读写分离策略。 + +- 优点: + 1. 高可靠性:一方面,采用双机主备架构,能够在主库出现故障时自动进行主备切换,从库提升为主库提供服务,保证服务平稳运行;另一方面,开启数据持久化功能和配置合理的备份策略,能有效的解决数据误操作和数据异常丢失的问题; + 1. 读写分离策略:从节点可以扩展主库节点的读能力,有效应对大并发量的读操作。 + +- 缺点: + 1. 故障恢复复杂,如果没有 Redis HA 系统(需要开发),当主库节点出现故障时,需要手动将一个从节点晋升为主节点,同时需要通知业务方变更配置,并且需要让其它从库节点去复制新主库节点,整个过程需要人为干预,比较繁琐; + 1. 主库的写能力受到单机的限制,可以考虑分片; + 1. 主库的存储能力受到单机的限制,可以考虑Pika; + 1. 原生复制的弊端在早期的版本中也会比较突出 + - 如:Redis复制中断后,Slave会发起psync,此时如果同步不成功,则会进行全量同步,主库执行全量备份的同时可能会造成毫秒或秒级的卡顿; + - 又由于COW机制,导致极端情况下的主库内存溢出,程序异常退出或宕机;主库节点生成备份文件导致服务器磁盘IO和CPU(压缩)资源消耗;发送数GB大小的备份文件导致服务器出口带宽暴增,阻塞请求,建议升级到最新版本。 + +************************ + +## 哨兵 + +Redis Sentinel是社区版本推出的原生高可用解决方案,其部署架构主要包括两部分:Redis Sentinel集群和Redis数据集群。 +其中Redis Sentinel集群是由若干Sentinel节点组成的分布式集群,可以实现故障发现、故障自动转移、配置中心和客户端通知。 +Redis Sentinel的节点数量 推荐 2n+1(n>=1)的奇数个。[为什么redis推荐奇数个节点](https://blog.csdn.net/qq32933432/article/details/105785571) 其主要原因还是从成本上考虑的,因为奇数个节点和偶数个节点允许宕机的节点数是一样的 + +- 优点: + 1. Redis Sentinel 集群部署简单; + 1. 能够解决Redis主从模式下的高可用切换问题; + 1. 很方便实现Redis数据节点的线形扩展,轻松突破Redis自身单线程瓶颈,可极大满足Redis大容量或高性能的业务需求; + 1. 可以实现一套Sentinel监控一组Redis数据节点或多组数据节点。 + +- 缺点: + 1. 部署相对Redis主从模式要复杂一些,原理理解更繁琐; + 1. 资源浪费,Redis数据节点中slave节点作为备份节点不提供服务; + 1. Redis Sentinel主要是针对Redis数据节点中的主节点的高可用切换,对Redis的数据节点做失败判定分为主观下线和客观下线两种,对于Redis的从节点有对节点做主观下线操作,并不执行故障转移。 + 1. 不能解决读写分离问题,实现起来相对复杂。 + +- 注意 + 1. 部署的各个节点服务器时间尽量要同步,否则日志的时序性会混乱。 + 1. Redis建议使用pipeline和multi-keys操作,减少RTT次数,提高请求效率。 + 1. 自行搞定配置中心(zookeeper),方便客户端对实例的链接访问。 + +************************ + +## Cluster 集群 +> [cluster-tutorial](https://redis.io/docs/manual/scaling/) | [docker-compose 部署](https://gitee.com/gin9/DockerfileList/tree/master/docker-compose/redis-cluster/third-nodes) + +Redis Cluster是社区版推出的Redis分布式集群解决方案,主要解决Redis分布式方面的需求,比如,当遇到单机内存,并发和流量等瓶颈的时候,Redis Cluster能起到很好的负载均衡的目的。 +Redis Cluster集群节点最小配置6个节点以上(3主3从),其中主节点提供读写操作,从节点作为备用节点,不提供请求,只作为故障转移使用。 +Redis Cluster采用虚拟槽分区,所有的键根据哈希函数映射到0~16383个整数槽内,每个节点负责维护一部分槽以及槽所印映射的键值数据。 + + +- 优点: + 1. 无中心架构; + 1. 数据按照slot存储分布在多个节点,节点间数据共享,可动态调整数据分布; + 1. 可扩展性:可线性扩展到1000多个节点,节点可动态添加或删除; + 1. 高可用性:部分节点不可用时,集群仍可用。通过增加Slave做standby数据副本,能够实现故障自动failover,节点之间通过**gossip**协议交换状态信息,用投票机制完成Slave到Master的角色提升; + 1. 降低运维成本,提高系统的扩展性和可用性。 + +- 缺点: + 1. Client实现复杂,驱动要求实现Smart Client,需缓存slots mapping信息并及时更新,提高了开发难度,客户端的不成熟影响了业务的稳定性。 + 1. 节点会因为某些原因发生阻塞(阻塞时间大于clutser-node-timeout),被判断下线,这种failover是没有必要的。 + 1. 数据通过异步复制,不保证数据的强一致性。 + 1. 多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容易出现相互影响的情况。 + 1. Slave在集群中充当“冷备”,不能缓解读压力,当然可以通过SDK的合理设计来提高Slave资源的利用率。 + 1. Key批量操作限制,如使用mset、mget目前只支持具有相同slot值的Key执行批量操作。对于映射为不同slot值的Key由于Keys**不支持跨slot**查询,所以执行mset、mget、sunion等操作支持不友好。 + 1. Key事务操作支持有限,只支持多key在同一节点上的事务操作,当多个Key分布于不同的节点上时无法使用事务功能。 + 1. Key作为数据分区的最小粒度,不能将一个很大的键值对象如hash、list等映射到不同的节点。 + 1. 不支持多数据库空间,单机下的redis可以支持到16个数据库,集群模式下只能使用1个数据库空间,即db 0。 + 1. 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。 + 1. 避免产生hot-key,导致主库节点成为系统的短板。 + 1. 避免产生big-key,导致网卡撑爆、慢查询等。 + 1. 重试时间应该大于cluster-node-time时间。 + 1. Redis Cluster不建议使用pipeline和multi-keys操作,减少max redirect产生的场景。 + +> 业务使用时注意事项: 操作多key时,需要保证多个key要在一个slot内(例如Lua脚本实现的一些复杂操作) + +cluster 命令使用: +- 查看key的slot `cluster keyslot key` +- 查看slot和Node关系 `cluster slots` + +cluster info +cluster nodes + +************************ + +# Redis 持久化 +[Redis persistence](https://redis.io/docs/management/persistence/) + +由于Redis的数据都存放在内存中,如果没有配置持久化,redis重启后数据就全丢失了,于是需要开启redis的持久化功能,将数据保存到磁盘上,当redis重启后,可以从磁盘中恢复数据。 +Redis提供两种方式进行持久化 +1. RDB(原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化) + - RDB是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。 +1. AOF(append only file)持久化(原理是将Reids的操作日志以追加的方式写入文件 类似于 MySQL binlog) + - AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。 + - + +![](img/redis-store-rule.drawio.svg) + +- RDB的优势 + 1. 一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这对于文件备份而言是非常完美的。比如,你可能打算每个小时归档一次最近24小时的数据,同时还要每天归档一次最近30天的数据。通过这样的备份策略,一旦系统出现灾难性故障,我们可以非常容易的进行恢复。 + 1. 对于灾难恢复而言,RDB是非常不错的选择。因为我们可以非常轻松的将一个单独的文件压缩后再转移到其它存储介质上。 + 1. 性能最大化。对于Redis的服务进程而言,在开始持久化时,它唯一需要做的只是fork出子进程,之后再由子进程完成这些持久化的工作,这样就可以极大的避免服务进程执行IO操作了。 + 1. 相比于AOF机制,如果数据集很大,RDB的启动效率会更高。 + +- RDB的劣势 + 1. 如果你想保证数据的高可用性,即最大限度的避免数据丢失,那么RDB将不是一个很好的选择。因为系统一旦在定时持久化之前出现宕机现象,此前没有来得及写入磁盘的数据都将丢失。 + 1. 由于RDB是通过fork子进程来协助完成数据持久化工作的,因此,如果当数据集较大时,可能会导致整个服务器停止服务几百毫秒,甚至是1秒钟。 + +- AOF的优势 + 1. 该机制可以带来更高的数据安全性,即数据持久性。Redis中提供了3中同步策略,即每秒同步、每修改同步和不同步。 + - 事实上,每秒同步也是异步完成的,其效率也是非常高的,所差的是一旦系统出现宕机现象,那么这一秒钟之内修改的数据将会丢失。 + - 而每修改同步,我们可以将其视为同步持久化,即每次发生的数据变化都会被立即记录到磁盘中。可以预见,这种方式在效率上是最低的。至于无同步,无需多言,我想大家都能正确的理解它。 + 1. 由于该机制对日志文件的写入操作采用的是append模式,因此在写入过程中即使出现宕机现象,也不会破坏日志文件中已经存在的内容。 + - 然而如果我们本次操作只是写入了一半数据就出现了系统崩溃问题,不用担心,在Redis下一次启动之前,我们可以通过redis-check-aof工具来帮助我们解决数据一致性的问题。 + 1. 如果日志过大,Redis可以自动启用rewrite机制。即Redis以append模式不断的将修改数据写入到老的磁盘文件中,同时Redis还会创建一个新的文件用于记录此期间有哪些修改命令被执行。因此在进行rewrite切换时可以更好的保证数据安全性。 + 1. AOF包含一个格式清晰、易于理解的日志文件用于记录所有的修改操作。事实上,我们也可以通过该文件完成数据的重建。 + +- AOF的劣势 + 1. 对于相同数量的数据集而言,AOF文件通常要大于RDB文件。RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。 + 1. 根据同步策略的不同,AOF在运行效率上往往会慢于RDB。总之,每秒同步策略的效率是比较高的,同步禁用策略的效率和RDB一样高效。 + +二者选择的标准,就是看应用场景是愿意牺牲一些性能,换取更高的缓存一致性(aof),还是愿意写操作频繁的时候,不启用备份来换取更高的性能,待手动运行save的时候,再做备份(rdb)。rdb这个就更有些 eventually consistent 的意思了。 + + diff --git a/Database/SQL.md b/Database/SQL.md index 5df5671..747e772 100644 --- a/Database/SQL.md +++ b/Database/SQL.md @@ -1,8 +1,66 @@ -`目录 start` - -- [SQL](#sql) +--- +title: SQL基础 +date: 2018-12-16 17:29:28 +tags: + - SQL +categories: + - 数据库 +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [SQL](#sql) + - 1.1. [条件语句](#条件语句) + - 1.2. [聚合函数](#聚合函数) + - 1.3. [分析函数](#分析函数) + - 1.3.1. [窗口函数](#窗口函数) +- 2. [安全](#安全) +- 3. [Tips](#tips) + +💠 2024-09-20 11:10:09 **************************************** # SQL -> 基础SQL语言的学习使用 +> [Wiki: SQL](https://en.wikipedia.org/wiki/SQL) + +> [database language SQL](https://archive.org/details/federalinformati127nati/page/n8/mode/1up) + +- SQL语言共分为四大类: + - 数据查询语言DQL: SELECT + - 数据操纵语言DML: UPDATE、INSERT、DELETE + - 数据定义语言DDL: CREATE、ALTER、DROP + - 数据控制语言DCL: GRANT,DENY,REVOKE + +> [sqlglot](https://github.com/tobymao/sqlglot) `Python SQL Parser and Transpiler ` + +## 条件语句 +`case when then else end ` +```sql + update table_test set mark = + case + when id = 2 then '2' + when id = 5 then '5' + else '' + end + where id in (2,5); +``` + +## 聚合函数 + + + +## 分析函数 + +### 窗口函数 +统计不止发生一次,而是发生多次。统计不止发生在记录集形成后,而是发生在记录集形成的过程中 + +> [窗口函数](https://blog.csdn.net/huozhicheng/article/details/5843782/) + +************************ + +# 安全 +> [SQL Injection Payload List](https://github.com/payloadbox/sql-injection-payload-list) + +************************ + +# Tips +> [soar](https://github.com/XiaoMi/soar)`SQL Optimizer And Rewriter ` \ No newline at end of file diff --git a/Database/SQLServer.md b/Database/SQLServer.md index 043ef31..9ca1d85 100644 --- a/Database/SQLServer.md +++ b/Database/SQLServer.md @@ -1,11 +1,20 @@ -`目录 start` - -- [SQLServer](#sqlserver) - - [安装配置](#安装配置) - - [Docker安装2017硬是不成功](#docker安装2017硬是不成功) - - [2000版本](#2000版本) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: SQLServer +date: 2018-12-16 17:29:45 +tags: + - SQLServer +categories: + - 数据库 +--- + +**目录 start** + +1. [SQLServer](#sqlserver) + 1. [安装配置](#安装配置) + 1. [Docker安装2017硬是不成功](#docker安装2017硬是不成功) + 1. [2000版本](#2000版本) + +**目录 end**|_2020-04-27 23:42_| **************************************** # SQLServer @@ -33,4 +42,4 @@ ``` #### 2000版本 -- [docker别人做的镜像](https://hub.docker.com/r/rsmoorthy/mssql/) \ No newline at end of file +- [docker别人做的镜像](https://hub.docker.com/r/rsmoorthy/mssql/) diff --git a/Database/img/redis-store-rule.drawio.svg b/Database/img/redis-store-rule.drawio.svg new file mode 100644 index 0000000..48534ec --- /dev/null +++ b/Database/img/redis-store-rule.drawio.svg @@ -0,0 +1,207 @@ + + + + + + + + + + +
+
+
+ fork 子进程 +
+
+
+
+ + fork 子进程 + +
+
+ + + + +
+
+
+ Redis Server +
+
+
+
+ + Redis Server + +
+
+ + + + + +
+
+
+ dump 覆盖式更新 +
+
+
+
+ + dump 覆盖式更新 + +
+
+ + + + +
+
+
+ Child Process +
+
+
+
+ + Child Process + +
+
+ + + + +
+
+
+ RDB 临时文件 +
+
+
+
+ + RDB 临时文件 + +
+
+ + + + + +
+
+
+ 定时执行 +
+
+
+
+ + 定时执行 + +
+
+ + + + + +
+
+
+ 发送 写命令 +
+
+
+
+ + 发送 写命令 + +
+
+ + + + +
+
+
+ Redis Client +
+
+
+
+ + Redis Client + +
+
+ + + + + +
+
+
+ 同步写命令 +
+
+
+
+ + 同步写命令 + +
+
+ + + + +
+
+
+ Redis Server +
+
+
+
+ + Redis Server + +
+
+ + + + +
+
+
+ AOF 记录文件 +
+
+
+
+ + AOF 记录文件 + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/Distributed/CloudNative.md b/Distributed/CloudNative.md new file mode 100644 index 0000000..76ba275 --- /dev/null +++ b/Distributed/CloudNative.md @@ -0,0 +1,17 @@ +--- +title: CloudNative +date: 2019-06-04 19:44:41 +tags: +categories: +--- + +💠 + +- 1. [云原生](#云原生) + +💠 2024-09-05 11:52:54 +**************************************** +# 云原生 +> [Pivotal对云原声的定义](https://pivotal.io/cn/cloud-native) + +> [faas](https://github.com/openfaas/faas) diff --git a/Distributed/ConfigCenter/Apollo.md b/Distributed/ConfigCenter/Apollo.md new file mode 100644 index 0000000..af23e34 --- /dev/null +++ b/Distributed/ConfigCenter/Apollo.md @@ -0,0 +1,21 @@ +--- +title: Apollo +date: 2018-12-16 17:30:10 +tags: + - 中间件 +categories: + - 分布式 +--- + +💠 + +- 1. [Apollo](#apollo) + +💠 2024-10-02 22:33:00 +**************************************** +# Apollo +> [Github:Apollo](https://github.com/ctripcorp/apollo) + +[assembly实现 ./xx.jar 直接执行](https://github.com/ctripcorp/apollo/blob/master/apollo-adminservice/pom.xml) + + diff --git a/Distributed/ConfigCenter/Nacos.md b/Distributed/ConfigCenter/Nacos.md new file mode 100644 index 0000000..48c3e6b --- /dev/null +++ b/Distributed/ConfigCenter/Nacos.md @@ -0,0 +1,28 @@ +--- +title: Nacos +date: 2024-01-11 23:00:53 +tags: +categories: +--- + +💠 + +- 1. [Nacos](#nacos) + - 1.1. [Design](#design) +- 2. [Tips](#tips) + +💠 2024-04-07 13:37:31 +**************************************** +# Nacos +> [Nacos](https://nacos.io/en-us/) + +## Design + +# Tips +> 集群模式出现节点数据不一致的情况 +- 检查日志看是否网络中断或超时导致 +- 重启集群 + +> Nacos 客户端 SocketTimeOut 异常 +- 检查网络问题 +- 检查应用端GC问题 diff --git a/Distributed/ConfigCenter/Readme.md b/Distributed/ConfigCenter/Readme.md new file mode 100644 index 0000000..72405bc --- /dev/null +++ b/Distributed/ConfigCenter/Readme.md @@ -0,0 +1,6 @@ +# 配置中心 +Apollo +disconf +Spring Cloud Config + +> [开源配置中心对比矩阵](https://github.com/ctripcorp/apollo/files/983064/default.pdf) diff --git a/Distributed/ConfigCenter/ZooKeeper.md b/Distributed/ConfigCenter/ZooKeeper.md new file mode 100644 index 0000000..e25b745 --- /dev/null +++ b/Distributed/ConfigCenter/ZooKeeper.md @@ -0,0 +1,176 @@ +--- +title: ZooKeeper +date: 2019-07-07 11:52:11 +tags: +categories: + - 分布式 +--- + +**目录 start** + +1. [安装并启动](#安装并启动) + 1. [使用](#使用) + 1. [Java API](#java-api) + +**目录 end**|_2022-10-22 22:59_| +**************************************** +# 安装并启动 + +- 进官网下载ZooKeeper,地址为`https://www.apache.org/dyn/closer.cgi/zookeeper/` +- 将下载好的压缩包,解压缩,并进入ZooKeeper的文件夹 +- 查看目录`conf`下是否含有`zoo.cfg`配置文件(因我下载的版本是3.4.12,发现conf下有文件`zoo_sample.cfg`,将文件`zoo_sample.cfg`在原有目录下复制并改名为`zoo.cfg`,因ZooKeeper的启动脚本默认是使用配置`conf/zoo.cfg`,若没有该配置,则会报错) +- 启动服务器命令为`bin/zkServer.sh start` +- 启动CLI的命令为`bin/zkCli.sh` +- 停止ZooKeeper服务的命令为`bin/zkServer.sh stop` + +## 使用 +- 创建Znodes:`create [参数] /path 数据`,例如:`create /FirstZnode "first ZooKeeper-app"`,起命令参数如下 + - `-s`: 创建连续的Znode + - `-e`: 创建一个零时的Znode +- 获取Znodes: `get /path`,`/path`要与创建时的一直 +- 监视znode的数据变化:`get /path [watch] 1`, 例如:`get /FirstZnode 1`,当`/FirstZnode`的数据发生变化时,将输出变化 +- 设置znode的数据:`set /path data`,例如:`set /FirstNode "Hello"` +- 创建子Znode的方法与创建Znode的方法一样,但需要在路径中包含父路径:`create /parent/path data`,例如:`create /FirstNode/TestNode "test"` +- 显示子Znode:`ls /parent`,例如列出上一步创建的Znode`ls /FirstNode` +- 检查状态:`stat /path`,例如:`stat /FirstNode` +- 删除Znode:`rmr /path`,仅适用于无子Znode + +## Java API + +> [Java API](https://github.com/dragonhht/ZooKeeper-study/blob/master/src/test/java/hht/dragon/TestZooKeeper.java) + +- 获取ZooKeeper连接 + + ``` + ZooKeeper zooKeeper = new ZooKeeper(host, 5000, new Watcher() { + public void process(WatchedEvent watchedEvent) { + if (watchedEvent.getState() == Event.KeeperState.SyncConnected) { + latch.countDown(); + } + } + }); + ``` + + - 参数说明 : `ZooKeeper(String connectionString, int sessionTimeout, Watcher watcher)` + + > `connectionString` : ZooKeeper集合主机。 + > `sessionTimeout` : 以毫秒为单位会话超时。 + > `watcher` : 一个执行对象“观察者”的接口。ZooKeeper 集合返回通过监控器对象的连接状态。 + + +- 创建Znode : `create(String path, byte[] data, List acl, CreateMode createMode)` + + ``` + public void createZnode() throws IOException, InterruptedException, KeeperException { + ZooKeeper zoo = connect("localhost"); + byte[] data = "Java API Test".getBytes(); + // 调用create方法创建Znode + zoo.create(ZNODE_PATH, data, ZooDefs.Ids.OPEN_ACL_UNSAFE, + CreateMode.PERSISTENT); + close(zoo); + } + ``` + + - 参数说明: + + > `path` : Znode的路径。例如 /myapp1, /myapp2, /myapp1/mydata1, myapp2/mydata1/myanothersubdata + > `data` : 在一个指定的znode路径存储数据 + > `acl` : 要创建节点的访问控制列表。 ZooKeeperAPI提供了一个静态接口ZooDefs.Ids得到一些基本的ACL列表。例如,ZooDefs.Ids.OPEN_ACL_UNSAFE返回ACL开放的 znodes 列表。 + > `createMode` : 节点的类型,可以是短暂的,连续的,或两者。这是一个枚举类型。 + +- 检查Znode是否存在:`exists(String path, boolean watcher)` + + - 参数说明 + + > `path` : Znode 路径 + > `watcher` : 布尔值,指定是否监视指定的znode与否 + + +- 获取数据:`getData(String path, Watcher watcher, Stat stat)` + + ```java + public void getData() throws IOException, InterruptedException, KeeperException { + ZooKeeper zoo = connect("localhost"); + byte[] data = zoo.getData(ZNODE_PATH, new Watcher() { + @Override + public void process(WatchedEvent watchedEvent) { + if (watchedEvent.getType() == Event.EventType.None) { + switch (watchedEvent.getState()) { + case Expired: + latch.countDown(); + break; + } + } else { + String path = "/MyFirstZnode"; + try { + byte[] bn = zoo.getData(path, + false, null); + String data = new String(bn, + "UTF-8"); + System.out.println(data); + latch.countDown(); + + } catch(Exception ex) { + System.out.println(ex.getMessage()); + } + } + } + }, null); + System.out.println(new String(data, "UTF-8")); + close(zoo); + } + ``` + + - 参数说明 + + > `path` : Znode 路径. + > `watcher` : Watcher类型的回调函数。ZooKeeper集合将通知通过观察者回调时指定的节点改变的数据。这是一次性的通知。 + > `stat` : 返回 znode 元数据。 + +- 修改数据: `setData(String path, byte[] data, int version)` + + ```java + public void setData() throws IOException, InterruptedException, KeeperException { + ZooKeeper zoo = connect("localhost"); + byte[] data = "修改数据".getBytes(); + zoo.setData(ZNODE_PATH, data, zoo.exists(ZNODE_PATH, true).getVersion()); + close(zoo); + } + ``` + + - 参数说明 + + > `path` : Znode 路径 + > `data` : 数据存储在一个指定的znode路径。 + > `version` : 当前znode的版本。ZooKeeper更新数据在znode的版本号改变了以后。 + +- 获取子节点: `getChildren(String path, Watcher watcher)` + + ```java + public void getChild() throws IOException, InterruptedException, KeeperException { + ZooKeeper zoo = connect("localhost"); + List children = zoo.getChildren("/FirstNode", false); + children.forEach(System.out::println); + close(zoo); + } + ``` + + - 参数说明 + + > `path` : Znode 路径. + > `watcher` : 调用“Watcher”类型函数. ZooKeeper集合将通知在指定的 znode 被删除或znode以下子节点创建/删除。 这是一次性的通知。 + +- 删除Znode节点 + + ```java + public void del() throws IOException, InterruptedException, KeeperException { + ZooKeeper zoo = connect("localhost"); + zoo.delete(ZNODE_PATH, zoo.exists(ZNODE_PATH, true).getVersion()); + close(zoo); + } + ``` + + - 参数说明 + + > `path` : Znode 路径 + > `version` : 当前 znode 的版本 diff --git a/Distributed/HA/RateLimit.md b/Distributed/HA/RateLimit.md new file mode 100644 index 0000000..676cf51 --- /dev/null +++ b/Distributed/HA/RateLimit.md @@ -0,0 +1,119 @@ +--- +title: 限流 +date: 2022-08-03 10:33:45 +tags: +categories: +--- + +💠 + +- 1. [限流](#限流) + - 1.1. [算法](#算法) + - 1.1.1. [令牌桶](#令牌桶) + - 1.1.2. [漏桶](#漏桶) + - 1.1.3. [固定窗口](#固定窗口) + - 1.1.4. [滑动窗口](#滑动窗口) +- 2. [组件方案](#组件方案) + - 2.1. [Nginx](#nginx) + - 2.2. [Guava](#guava) + - 2.3. [Redis](#redis) + - 2.4. [Hystrix](#hystrix) + - 2.5. [concurrency-limits](#concurrency-limits) +- 3. [分布式Semaphore](#分布式semaphore) + - 3.1. [Redis 实现](#redis-实现) + - 3.2. [Oracle Coherence](#oracle-coherence) + +💠 2024-09-20 11:10:09 +**************************************** +# 限流 + +> 目的 +- 保护系统稳定性:过多的并发请求可能导致服务器内存耗尽、CPU 使用率饱和,从而引发系统响应慢、无法正常服务的问题。 +- 防止资源滥用:确保有限的服务资源被合理公平地分配给所有用户,防止个别用户或恶意程序过度消耗资源。 +- 优化用户体验:对于网站和应用程序而言,如果任由高并发导致响应速度变慢,会影响所有用户的正常使用体验。 +- 保障安全:在网络层面,限流有助于防范 DoS/DDoS 攻击,降低系统遭受恶意攻击的风险。 +- 运维成本控制:合理的限流措施可以帮助企业减少不必要的硬件投入,节省运营成本。 + + +## 算法 +> 令牌桶,漏桶,固定窗口,滑动窗口 都是对流量整形,削峰填谷,适用于常见REST接口。 + + +如果是任务调度类场景,单个任务执行时间很长(分钟级),则不适用,可以考虑分布式Semaphore的实现,限制整个集群上下游的并行任务数。 + +### 令牌桶 +固定速率生成令牌放入桶中,并支持预取,通过是否获得令牌来实现限流 +- 允许当前请求获取超量资源(大于并发限制),下一次请求需要等待超额的时间 + +> [Guava ratelimiter 实现原理](https://cloud.tencent.com/developer/article/1408819) + +### 漏桶 +不支持突发流量, 通过限制流出速率,丢弃突发的流入流量来实现限流 + +### 固定窗口 +通过限制固定时间窗口(例如自然时间1分钟 10:00 到 10:01 )内请求数,超出部分丢弃,实现限流。 + +### 滑动窗口 +通过限制滑动时间窗口(例如过去1分钟)内请求数,超出部分丢弃,实现限流。 + +************************ + +# 组件方案 + +## Nginx + +## Guava +RateLimiter 令牌桶实现 +- 支持平滑发放令牌(例如限制每秒5并发,每个令牌的获取间隔大概在200ms左右) + +## Redis +简易:zset 使用时间戳值来做滑动窗口,如果服务器间时间不同步,会在边界情况下超出设定的最大阈值。 + +> [详解Redisson分布式限流的实现原理 ](https://juejin.cn/post/7199882882138898489) +> [分布式限流:基于 Redis 实现](https://pandaychen.github.io/2020/09/21/A-DISTRIBUTE-GOREDIS-RATELIMITER-ANALYSIS/) + +## Hystrix + +## concurrency-limits +[concurrency-limits](https://github.com/Netflix/concurrency-limits) 类似于 TCP拥塞控制算法 + +************************ + +# 分布式Semaphore + +作用类似于 [JDK中的Semaphore](/Java/AdvancedLearning/JavaConcurrency.md#semaphore),但是资源限制是分布式的,而不是单机,实现可以依赖Redis或MySQL等中间存储。 + +> [Ignite: Semaphore](https://ignite.apache.org/docs/latest/data-structures/semaphore) + +## Redis 实现 +> [分布式Semaphore](https://cloud.tencent.com/developer/article/1805219) + +1. 使用 Redission 中的 RSemaphore +1. **Lua脚本实现**,加一(获取资源),判断是否超阈值超过则撤销加一,减一(释放资源) `自旋等待` + - 命令: `EVAL "local cnt = redis.call('incr', KEYS[1]); if (tonumber(cnt) > tonumber(ARGV[1]) ) then redis.call('decr', KEYS[1]); return 0; else return 1; end " 1 lockA 3` + ```java + public static final String Judge = "local cnt = redis.call('incr', KEYS[1]);" + + " if (tonumber(cnt) > tonumber(ARGV[1]) ) then redis.call('decr', KEYS[1]); return 0;" + + " else return 1; end"; + + public boolean acquire() { + // 指定 lua 脚本,并且指定返回值类型 + DefaultRedisScript redisScript = new DefaultRedisScript<>(Judge, Integer.class); + // 参数一:redisScript,参数二:key列表,参数三:arg(可多个) + Object lockB = redisTemplate.execute(redisScript, Collections.singletonList("lockB"), 3); + if (Objects.isNull(lockB)) { + return false; + } + return Integer.parseInt(lockB.toString()) > 0; + } + + public String release() { + Long val = redisTemplate.opsForValue().decrement("lockB"); + return val + ""; + } + ``` + +> [分布式限流——Redis版分布式信号量原理](https://www.skypyb.com/2020/06/jishu/1538/)`负面参考:实现复杂有缺陷` + +## Oracle Coherence +[Coherence](https://docs.oracle.com/en/middleware/standalone/coherence/14.1.1.2206/develop-applications/implementing-concurreny-distributed-environment.html#GUID-8C7BBF82-EBF8-47A9-8EDC-E725221C1054) diff --git a/Distributed/HA/Readme.md b/Distributed/HA/Readme.md new file mode 100644 index 0000000..20c6f7d --- /dev/null +++ b/Distributed/HA/Readme.md @@ -0,0 +1,11 @@ +高可用 + +限流 +熔断 +降级 +请求合并 +自动扩缩容 + +Hystrix `Resilience4j` 限流 熔断 降级 +Sentinel + diff --git a/Distributed/MQ/Kafka.md b/Distributed/MQ/Kafka.md new file mode 100644 index 0000000..43c0d5f --- /dev/null +++ b/Distributed/MQ/Kafka.md @@ -0,0 +1,73 @@ +--- +title: Kafka +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [Kafka](#kafka) + - 1.1. [安装](#安装) + - 1.1.1. [Docker](#docker) + - 1.2. [使用](#使用) + - 1.3. [设计](#设计) + - 1.3.1. [Kraft](#kraft) + +💠 2024-04-24 22:46:48 +**************************************** +# Kafka +> [Apache Kafka](https://kafka.apache.org/) + +> [参考: 初探Kafka Streams](http://ifeve.com/%e5%88%9d%e6%8e%a2kafka-streams/) +- [ksql](https://github.com/confluentinc/ksql) +> [参考: Kafka Topic Architecture](http://cloudurable.com/blog/kafka-architecture-topics/index.html) + +> [参考: Apche Kafka 的生与死 – failover 机制详解](https://www.cnblogs.com/fxjwind/p/4972244.html) `解释 I wrote this conflicted ephemeral node` + +## 安装 + +### Docker +> [参考: docker部署kafka](https://blog.csdn.net/luanpeng825485697/article/details/81562755#commentBox) +[Docker: bitnami/kafka](https://hub.docker.com/r/bitnami/kafka) + +> Kafka 容器的创建强制性依赖 Zookeeper, 但是在使用中可以直接使用Kafka +```sh + # 启动 Zookeeper + docker run -d --name kafka-zookeeper -p 2181:2181 --volume /etc/localtime:/etc/localtime wurstmeister/zookeeper + + # 启动 Kafka + docker run -d --name kafka -p 9092:9092 --link kafka-zookeeper --env KAFKA_ZOOKEEPER_CONNECT=kafka-zookeeper:2181 --env KAFKA_ADVERTISED_HOST_NAME=localhost --env KAFKA_ADVERTISED_PORT=9092 --volume /etc/localtime:/etc/localtime wurstmeister/kafka +``` + +> Hello World + +/opt/kafka_xxx 目录下 + +1. 创建一个 topic `bin/kafka-topics.sh --create --zookeeper kafka-zookeeper:2181 --replication-factor 1 --partitions 1 --topic mykafka` +1. 运行一个消息生产者并指定topic `bin/kafka-console-producer.sh --broker-list localhost:9092 --topic mykafka` + - 此时会提供一个输入命令行, 就能输入发送的消息内容 +1. 查看所有的topic列表 `bin/kafka-topics.sh --list --zookeeper kafka-zookeeper:2181` + - 或者 `bin/kafka-topics.sh --list --bootstrap-server 127.0.0.1:9092` +1. 运行一个消费者并指定topic `bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic mykafka --from-beginning` + - 会收到消息生产者输入的内容 + +************************ + +## 使用 + + +************************ + +## 设计 + + +### Kraft +> [https://www.baeldung.com/kafka-shift-from-zookeeper-to-kraft](https://www.baeldung.com/kafka-shift-from-zookeeper-to-kraft) +> [KIP-500: Replace ZooKeeper with a Self-Managed Metadata Quorum](https://cwiki.apache.org/confluence/display/KAFKA/KIP-500%3A+Replace+ZooKeeper+with+a+Self-Managed+Metadata+Quorum) + +> [参考: 深度解读:Kafka 放弃 ZooKeeper,消息系统兴起二次革命](https://www.infoq.cn/article/phf3gfjutdhwmctg6kxe) + +2.8.0 开始支持 `2019-04` +3.3.1 release `2022-09` + diff --git a/Distributed/MQ/MQ.md b/Distributed/MQ/MQ.md new file mode 100644 index 0000000..49576cb --- /dev/null +++ b/Distributed/MQ/MQ.md @@ -0,0 +1,65 @@ +--- +title: 消息队列 +date: 2018-11-21 10:56:52 +tags: + - MQ +categories: +--- + +💠 + +- 1. [MQ](#mq) + - 1.1. [为何需要使用](#为何需要使用) + - 1.2. [风险分析](#风险分析) + - 1.3. [JMS](#jms) +- 2. [MQ中间件](#mq中间件) + - 2.1. [RocketMQ](#rocketmq) + - 2.2. [ActiveMQ](#activemq) + - 2.3. [Kafka](#kafka) + - 2.4. [RabbitMQ](#rabbitmq) + - 2.5. [Pulsar](#pulsar) + - 2.6. [nsq](#nsq) + +💠 2024-09-28 11:21:46 +**************************************** +# MQ + +> [参考: MQ消息中间件](https://blog.csdn.net/qq_29676623/article/details/85108070) + +## 为何需要使用 +解耦、异步、削峰 + +## 风险分析 +- 引入新系统, 增加了故障的风险 + +## JMS +> Java Message Service 规范而已,和JDBC一样, 具体实现由厂商来实现 + +[码农翻身:Java帝国之JMS的诞生](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513515&idx=1&sn=380bb1cb56d4151fd3acc5aa86f1da9a&chksm=80d67a68b7a1f37e3d98fe4495eab4db097eedd695c99fbd8704cc0464595842c4da598b99e3&scene=21#wechat_redirect) + +************************ + +# MQ中间件 +> [Kafka vs. Pulsar vs. RabbitMQ](https://www.confluent.io/kafka-vs-pulsar/) + +## RocketMQ +> [有关demo](https://github.com/lirenzuo/rocketmq-rocketmq-all-4.1.0-incubating) + +4.3 开始支持[事务消息](https://rocketmq.apache.org/zh/docs/featureBehavior/04transactionmessage/) +- [RocketMQ是如何实现事务消息的](https://github.com/Cicizz/binary/blob/master/RocketMQ/RocketMQ%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF/RocketMQ%E6%98%AF%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E4%BA%8B%E5%8A%A1%E6%B6%88%E6%81%AF%E7%9A%84.md) + +## ActiveMQ +> [Official Site](http://activemq.apache.org/) + +## Kafka +[Kafka](./Kafka.md) +[Redpanda](https://github.com/redpanda-data/redpanda) + +## RabbitMQ +采用 Erlang 开发 + +> [参考: 我为什么要选择RabbitMQ ](https://www.sojson.com/blog/48.html) + +## Pulsar + +## nsq diff --git a/Distributed/MQ/MQTheory.md b/Distributed/MQ/MQTheory.md new file mode 100644 index 0000000..be86c63 --- /dev/null +++ b/Distributed/MQ/MQTheory.md @@ -0,0 +1,52 @@ +--- +title: MQ理论基础 +date: 2022-08-03 10:03:18 +tags: +categories: +--- + +**目录 start** + +1. [MQ理论](#mq理论) +1. [问题和方案](#问题和方案) + 1. [消息丢失](#消息丢失) + 1. [生产端](#生产端) + 1. [MQ自身](#mq自身) + 1. [消费端](#消费端) + 1. [消息重复](#消息重复) + 1. [消费顺序](#消费顺序) + +**目录 end**|_2022-08-03 10:03_| +**************************************** +# MQ设计理论 + +# 问题和方案 +## 消息丢失 +### 生产端 +原因:异步发送 mq在生产端的client和MQ通信出现故障, 或者 上线时生产端未执行完就被重启了 + +发送消息的ack机制,规避掉MQ中的异步发送机制,生产端发送消息时,同步等待MQ确认收到消息后才返回 + +### MQ自身 + +### 消费端 + +原因: 自动确认消息机制,消费到消息就通知MQ消费完成,实际上消费者可能消费到消息正准备处理业务,节点突然down了。 +调整为消息消费完成才提交确认。消息的消费实现幂等利于重试 + +## 消息重复 +原因: MQ未收到消费端的消费确认消息,消费端宕机等 + +消费端实现幂等。 +- 构造业务id利用持久层(Redis MySQL等)来判断是否重复 + +## 消费顺序 +扩展高可用性和顺序消费是一个取舍的问题 + +每个MQ的特性会不一样,实现思路大体相似,让MQ的消息控制于一个物理队列或者逻辑队列中,并将消费端限制为一个。 +消费端改为多个就需要应用层自身实现消息的消费是并行,但是提交是有序的。 + +## 消息积压 +原因:生产端TPS异常升高、消费端TPS下降或故障 + + diff --git a/Distributed/Middlewave/Apollo.md b/Distributed/Middlewave/Apollo.md deleted file mode 100644 index 638e290..0000000 --- a/Distributed/Middlewave/Apollo.md +++ /dev/null @@ -1,11 +0,0 @@ -`目录 start` - -- [Apollo](#apollo) - -`目录 end` |_2018-08-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Apollo -> [Github:Apollo](https://github.com/ctripcorp/apollo) - -- [ ] 研究 https://github.com/ctripcorp/apollo/blob/master/apollo-adminservice/pom.xml 怎么实现 ./*.jar 就能运行的 - diff --git a/Distributed/README.md b/Distributed/README.md index 8cf2d65..4ecb8d0 100644 --- a/Distributed/README.md +++ b/Distributed/README.md @@ -1,3 +1,44 @@ # 分布式 +> [系统设计入门](https://github.com/donnemartin/system-design-primer) +> [分布式系统核心技术](https://yeasy.gitbook.io/blockchain_guide/04_distributed_system) +> [分布式系统 - 知识体系详解](https://pdai.tech/md/arch/arch-z-overview.html) -但是要考虑采用该种方式后带来的技术复杂度的问题, 要考量当前的问题需不需要上升到分布式的体量上 \ No newline at end of file +要考虑采用该种方式后带来的技术复杂度的问题, 当前的问题需不需要上升到分布式的体量上 + +- [ ] CAP Paxos Zab Raft Gossip Billy + + +[Paxos](https://en.wikipedia.org/wiki/Paxos_%28computer_science%29) +[Paxos H2O实现](https://github.com/h2oai/h2o-3/blob/master/h2o-core/src/main/java/water/Paxos.java) + + + +************************ + +### Raft +> [Wikipedia](https://en.wikipedia.org/wiki/Raft_(algorithm)) | [Github Raft](https://raft.github.io/) + +快速理解:基于状态复制机模式,所有节点从相同的状态开始通过一系列log达到一致的状态,例如Redis的RDS一样将数据变化日志化 +通过其中的选举算法实现集群里始终只有一个leader多follower的状态 +客户端发起的所有修改动作都会交由leader完成并复制给其他节点(当集群内N/2+1的节点确认复制成功后给客户端响应操作成功),如果请求到了follwer节点也会转发给leader节点处理 + +- 解决的问题: 实现分布式系统的数据一致性和高可用。例如 Etcd、Consul、Zookeeper 组件中用到 + + +[Raft 实战系列,日志复制是什么?怎么实现?日志不一致怎么办?](https://blog.51cto.com/u_15009384/2568224) + +https://zhuanlan.zhihu.com/p/28560167 +https://www.cnblogs.com/mindwind/p/5231986.html +[Raft](https://zhuanlan.zhihu.com/p/32052223) + + +[动态变更节点](https://segmentfault.com/a/1190000022796386) + +### CAP定理 +CAP: Consistency Availability Partition tolerance + +> [码农翻身:张大胖和CAP定理](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513560&idx=1&sn=ba861726537c57bd34253cbce010b5f) + +https://github.com/aliyun/alibabacloud-microservice-demo.git + +### BASE diff --git a/Distributed/RPC.md b/Distributed/RPC.md new file mode 100644 index 0000000..9cef5ee --- /dev/null +++ b/Distributed/RPC.md @@ -0,0 +1,53 @@ +--- +title: 远程调用 +date: 2018-11-21 10:56:52 +tags: + - RPC +categories: + - 分布式 +--- + +💠 + +- 1. [RPC](#rpc) + - 1.1. [架构设计](#架构设计) + - 1.2. [HTTP](#http) + - 1.3. [RPC vs MQ](#rpc-vs-mq) +- 2. [实践](#实践) + +💠 2024-09-20 17:30:23 +**************************************** +# RPC +> Remote Procedure Calls + +- 进程间通信(IPC)是在多任务操作系统或联网的计算机之间运行的程序和进程所用的通信技术。分为两种 + - 本地过程调用LPC,用在多任务操作系统中,使得同时运行的任务能互相会话。这些任务共享内存空间使任务同步和互相发送信息。 + - 远程过程调用RPC,类似于LPC,只是调用方和被调用方中间隔了网络这一层 + +- 通常使用 IDL(Interface Definition) 建立接口定义, 达成约束, 一般指一种开发方式和规范, 具体的实现可以多样 + +> [Github rpc-framework ](https://github.com/topics/rpc-framework?l=java)`常用为Dubbo,SpringCloud,gRPC` + +> [motan](https://github.com/weibocom/motan)`微博Java` + +gRPC 和 Thrift 虽然支持跨语言的 RPC 调用,但是它们只提供了最基本的 RPC 框架功能,缺乏一系列配套的服务化组件和服务治理功能的支撑。Dubbo 和 SpringCloud就有完善的服务治理(注册中心、熔断、限流、监控、分布式追踪等)。 + +## 架构设计 +> [如何手撸一个较为完整的RPC框架 ](https://juejin.cn/post/6992867064952127524) + +RPC 框架一般有这些组件:服务治理(注册发现)、负载均衡、容错、序列化/反序列化、编解码、网络传输、线程池、动态代理等,当然有的RPC框架还会有连接池、日志、安全等。 + +> [序列化](/Skills/Serialization/Serialization.md) + +## HTTP +可以将常见的http的web请求,看作是前端调用服务端的方,服务端之间自然也是可以用HTTP实现RPC + +针对RPC优化:长连接,HTTP2二进制协议 + +## RPC vs MQ + +************************ + +# 实践 + +> [参考: 良好的RPC接口设计,需要注意这些方面](https://www.jianshu.com/p/dca5b00e72e4) \ No newline at end of file diff --git a/Distributed/ServiceDiscovery/Readme.md b/Distributed/ServiceDiscovery/Readme.md new file mode 100644 index 0000000..18bf14f --- /dev/null +++ b/Distributed/ServiceDiscovery/Readme.md @@ -0,0 +1,8 @@ +# 服务发现 +Zookeeper +Nacos +Eureka + +> [参考: ZooKeeper、Eureka对比](https://www.cnblogs.com/jieqing/p/8394001.html) +> [Consul vs. Zookeeper vs. Eureka ](https://stackshare.io/stackups/consul-vs-eureka-vs-zookeeper) + diff --git a/Distributed/Transaction/Readme.md b/Distributed/Transaction/Readme.md new file mode 100644 index 0000000..e9f1d29 --- /dev/null +++ b/Distributed/Transaction/Readme.md @@ -0,0 +1,53 @@ +# 分布式事务 +> [凤凰架构: 分布式事务](https://icyfenix.cn/architect-perspective/general-architecture/transaction/distributed.html) + +TODO 实现方式 场景 + + Seata + LCN做分布式事务 + 柔性事务 + +在业务、范式、性能、维护发生冲突时,各自如何解决,其中有很多折中的思想 + +> [Seata AT 模式](https://seata.apache.org/zh-cn/docs/dev/mode/at-mode) +> [DTM](https://dtm.pub/guide/start.html) + +> 2PC +CanCommit、DoCommit + +单个协调者,多个参与者 +过程: +1. 所有参与者开始上报可处理 +1. 所有参与者确认能处理后,协调者通知参与者本地提交 + +规则: + +1. 协调者单点故障 +1. 出现参与者响应为不能处理或者事务提交失败,协调者通知所有参与者回滚 + +问题: +1. 性能问题。参与者越多,事务越复杂,提交过程就越耗时,对数据库性能影响大 +1. 协调者单点问题。 +1. 数据不一致。 第二阶段协调者通知参与者时,消息或调用丢失 + +> 3PC + +CanCommit、PreCommit、DoCommit + +三阶段提交又称3PC,其在两阶段提交的基础上增加了CanCommit阶段,并引入了超时机制。一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit + +> TCC + +try confirm commit + +补偿式事务 针对每个操作都要注册一个与其对应的确认和补偿(撤销操作) +- Try阶段:主要是对业务系统做检测及资源预留。 +- Confirm阶段:确认执行业务操作。 +- Cancel阶段:取消执行业务操作。 + + +TCC事务的处理流程与2PC两阶段提交类似,不过2PC通常都是在跨库的DB层面,而TCC本质上就是一个应用层面的2PC,需要通过业务逻辑来实现。 +这种分布式事务的实现方式的优势在于,可以让应用自己定义数据库操作的粒度,使得降低锁冲突、提高吞吐量成为可能。 + +而不足之处则在于对应用的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作。 +此外,其实现难度也比较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。为了满足一致性的要求,confirm和cancel接口还必须实现幂等。 diff --git a/DotNet/Readme.md b/DotNet/Readme.md new file mode 100644 index 0000000..42c6b21 --- /dev/null +++ b/DotNet/Readme.md @@ -0,0 +1,11 @@ +# .NET + +- [linux install script](https://learn.microsoft.com/en-us/dotnet/core/install/linux-scripted-manual#scripted-install) + +> first start +1. install dotnet `./dotnet-install.sh --version latest` +1. ./dotnet-install.sh --channel 6.0 +1. [install avaloniaui](https://docs.avaloniaui.net/docs/next/get-started/install) +1. dotnet new install Avalonia.Templates +1. dotnet new avalonia.app -o GetStartedApp +1. dotnet run diff --git a/FrontEnd/CSS3.md b/FrontEnd/CSS3.md deleted file mode 100644 index 1dbcab6..0000000 --- a/FrontEnd/CSS3.md +++ /dev/null @@ -1,78 +0,0 @@ -`目录 start` - -- [CSS](#css) - - [选择器](#选择器) - - [类选择器](#类选择器) - - [ID选择器](#id选择器) - - [类和ID选择器的区别](#类和id选择器的区别) - - [子选择器](#子选择器) - - [包含(后代)选择器](#包含后代选择器) - - [通用选择器](#通用选择器) - - [伪类选择符](#伪类选择符) - - [案例](#案例) - - [复选框](#复选框) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# CSS -## 选择器 -### 类选择器 - -- 类选择器在css样式编码中是最常用到的,如右侧代码编辑器中的代码:可以实现为“胆小如鼠”、“勇气”字体设置为红色。 -- 语法: - - `.类选器名称{css样式代码;}` - -- 注意: - - 1、英文圆点开头 - - 2、其中类选器名称可以任意起名(但不要起中文噢) - -第一步:使用合适的标签把要修饰的内容标记起来,如下: -`胆小如鼠` - -第二步:使用class="类选择器名称"为标签设置一个类,如下: -`胆小如鼠` - -第三步:设置类选器css样式,如下: -`.stress{color:red;}/*类前面要加入一个英文圆点*/` - -### ID选择器 -- 在很多方面,ID选择器都类似于类选择符,但也有一些重要的区别: - - 1、为标签设置id="ID名称",而不是class="类名称"。 - - 2、ID选择符的前面是井号(#)号,而不是英文圆点(.)。 - -#### 类和ID选择器的区别 -- 相同点:可以应用于任何元素 -- 不同点: - - 1、ID选择器只能在文档中使用一次。与类选择器不同,在一个HTML文档中,ID选择器只能使用一次,而且仅一次。而类选择器可以使用多次。 - - 2、可以使用类选择器词列表方法为一个元素同时设置多个样式。我们可以为一个元素同时设多个样式,但只可以用类选择器的方法实现,ID选择器是不可以的(不能使用 ID 词列表)。 - -#### 子选择器 -- 还有一个比较有用的选择器子选择器,即大于符号(>),用于选择指定标签元素的第一代子元素。如: -- `.food>li{border:1px solid red;} //就是指定一个CSS样式名为food,但是这个是对其子元素li实施的样式` -- 这行代码会使class名为food下的子元素li(水果、蔬菜)加入红色实线边框。 - -#### 包含(后代)选择器 -- 包含选择器,即加入空格,用于选择指定标签元素下的后辈元素。如右侧代码编辑器中的代码: -- `.first span{color:red;} //作用于所有嵌套关系的 span标签 )(first格式下的全部span标签) (犹如父子关系)` -- 这行代码会使第一段文字内容中的“胆小如鼠”字体颜色变为红色。 -- 请注意这个选择器与子选择器的区别,子选择器(child selector)仅是指它的直接后代,或者你可以理解为作用于子元素的第一代后代。 - - 而后代选择器是作用于所有子后代元素。后代选择器通过空格来进行选择,而子选择器是通过“>”进行选择。 -- 【总结】:>作用于元素的第一代后代,空格作用于元素的所有后代。 - -#### 通用选择器 -- 通用选择器是功能最强大的选择器,它使用一个(*)号指定,它的作用是匹配html中所有标签元素,如下使用下面代码使用html中任意标签元素字体颜色全部设置为红色: - -`* {color:red;}` -#### 伪类选择符 -- 更有趣的是伪类选择符,为什么叫做伪类选择符,它允许给html不存在的标签(标签的某种状态)设置样式,比如说我们给html中一个标签元素的鼠标滑过的状态来设置字体颜色: -- `a:hover{color:red;}` -- 上面一行代码就是为 a 标签鼠标滑过的状态设置字体颜色变红。这样就会使第一段文字内容中的“胆小如鼠”文字加入鼠标滑过字体颜色变为红色特效。 -- 关于伪选择符: - - 关于伪类选择符,到目前为止,可以兼容所有浏鉴器的“伪类选择符”就是 a 标签上使用 :hover 了 - - (其实伪类选择符还有很多,尤其是 css3 中,但是因为不能兼容所有浏览器,本教程只是讲了这一种最常用的)。 - - 其实 :hover 可以放在任意的标签上,比如说 p:hover,但是它们的兼容性也是很不好的,所以现在比较常用的还是 a:hover 的组合。 - -## 案例 -### 复选框 -> [纯CSS+HTML自定义checkbox效果](https://segmentfault.com/a/1190000003711140) - diff --git a/FrontEnd/Font.md b/FrontEnd/Font.md new file mode 100644 index 0000000..2eacf4e --- /dev/null +++ b/FrontEnd/Font.md @@ -0,0 +1,129 @@ +--- +title: 字体 +date: 2018-12-14 09:21:37 +tags: + - 字体 +categories: + - 基础知识 +--- + +💠 + +- 1. [字体](#字体) + - 1.1. [基础知识](#基础知识) + - 1.2. [资源](#资源) + - 1.3. [Tips](#tips) + - 1.3.1. [使用字体保护网页敏感信息](#使用字体保护网页敏感信息) +- 2. [符号](#符号) + - 2.1. [制表符](#制表符) +- 3. [个人习惯](#个人习惯) + - 3.1. [操作系统默认字体](#操作系统默认字体) + - 3.2. [编辑器](#编辑器) + - 3.3. [IDEA](#idea) + - 3.4. [终端](#终端) + +💠 2024-05-09 14:32:49 +**************************************** +# 字体 +> [Deepin wiki 字体](https://wiki.deepin.org/wiki/%E5%AD%97%E4%BD%93) +> [有哪些适合用于写代码的西文字体?](https://www.zhihu.com/question/20299865) +> [What are the best programming fonts?](https://www.slant.co/topics/67/~best-programming-fonts) +> [大家都用什么字体写代码的?](https://segmentfault.com/q/1010000000193004) + +## 基础知识 +> ttf otf eot woff woff2 + +> [参考: Web 字体简介: TTF, OTF, WOFF, EOT & SVG](https://zhuanlan.zhihu.com/p/28179203) + +1. TTF (TrueType Font) 字体格式是由苹果和微软为 PostScript 而开发的字体格式。 +1. OTF (OpenType Font) 由 TTF 演化而来,是 Adobe 和微软共同努力的结果。 +1. EOT (Embedded Open Type) 字体是微软设计用来在 Web 上使用的字体。 +1. WOFF (Web Open Font Format) 本质上是 metadata + 基于 SFNT 的字体(如 TTF、OTF 或其他开放字体格式)。 +1. WOFF2 是 WOFF 的下一代。 WOFF2 格式在原有的基础上提升了 30% 的压缩率。 +1. SVG (Scalable Vector Graphics font) 字体格式使用 SVG 的字体元素定义。 + +******************************** +## 资源 +> Github +- [IBM字体](https://github.com/IBM/plex)`2017年发布的新字体` +- [cascadia-code](https://github.com/microsoft/cascadia-code) + +- [nerd-fonts](https://github.com/ryanoasis/nerd-fonts)`系列字体图标` +- [Font-Awesome](https://github.com/FortAwesome/Font-Awesome)`一大堆字体图标` + +> website +- [https://www.fontsquirrel.com/](https://www.fontsquirrel.com/) +- [https://www.urbanfonts.com/](https://www.urbanfonts.com/) +- [https://www.1001fonts.com/](https://www.1001fonts.com/) +- [https://www.ffonts.net/](https://www.ffonts.net/) + +- ttf-ms-fonts +- ttf-wps-fonts + +************************ +## Tips +### 使用字体保护网页敏感信息 +场景:网页上需要公开展示一些敏感信息(例如手机号)避免被爬虫爬取(其实只能增加一点点难度) + +> 实现思路: +1. 网页直接静态化,不通过后端请求 +2. 展示的值是特殊字体(例如 LeeTreeshadow)渲染后的值,而不是普通的字符串,即无法直接通过复制粘贴,读取网页HTML得到真实值 +3. 字体文件还能再通过js用base64加载进来,规避F12直接看到字体ttf文件 +4. 每个网页使用不同的unicode和数字映射规则,加大数据字典构造复杂度 +4. 再对整体静态结果资源进行混淆 + +> 爬虫破解思路 +1. 得到字体实际unicode字符串值 +1. 数据字典构造 + 1. 人工去寻找unicode值和肉眼看到的数字组成数据字典(才10个数字),但是遇到多规则就无法人工完成了 + 1. 终极:通过unicode值的规律来推算出数据字典 0-9 是有序依次递增的unicode值,而手机号通常首位为1 + +************************ + +# 符号 +> [符号](http://www.bangnishouji.com/fuhao/) + +## 制表符 +``` + ┌ ┬ ┐ ┏ ┳ ┓ ╒ ╤ ╕ ╭ ─ ╮ + ├ ┼ ┤ ┣ ╋ ┫ ╞ ╪ ╡ │ ╳ │ + └ ┴ ┘ ┗ ┻ ┛ ╘ ╧ ╛ ╰ ─ ╯ + ┏ ┳ ┓ ┏ ━ ┓ ┎ ┰ ┒ ┍ ┯ ┑ + ┣ ╋ ┫ ┃ ┃ ┠ ╂ ┨ ┝ ┿ ┥ + ┗ ┻ ┛ ┗ ━ ┛ ┖ ┸ ┚ ┕ ┷ ┙ + ┏ ┱ ┐ ┌ ┲ ┓ ┌ ┬ ┐ ┏ ┳ ┓ + ┡ ╃ ┤ ├ ╄ ┩ ┟ ╁ ┧ ┞ ┴ ┦ + └ ┴ ┘ └ ┴ ┘ ┗ ╁ ┛ └ ┴ ┘ + ─ ━ ┄ ┅ ┈ ┈ ╲ + │ ┃ ┆ ┇ ┊ ┋ ╱ +``` + +************************ + +# 个人习惯 +## 操作系统默认字体 +- 微软雅黑 +- Adobe 楷体 Std + +## 编辑器 +- Fira Code Retina +- IBM Plex Mono SemiBold [Github](https://fontmeme.com/fonts/ibm-plex-mono-font/) +- Cascadia-Code [Github](https://github.com/microsoft/cascadia-code) + +## IDEA +- Roboto Mono Medium `Appearance custom font` +- IBM Plex Mono SemiBold `Editor` + - JetBrainsMono 开启连字符 + +## 终端 +- Cascadia Mono PL +- Source Code Pro for Powerline + - 并且 + [Powerline](https://github.com/powerline/powerline) + Awesonme 的 Bold 最适合ZSH的 Bullet Train 主题 +- Droid Sans Mono for Powerline +- Roboto Mono for Powerline Bold +- JetBrainsMono Nerd Font Mono Regular + +************************ + +- [fonts](https://github.com/powerline/fonts)`终端中常用字体` +- [FiraCode](https://github.com/tonsky/FiraCode) diff --git a/FrontEnd/Frame/LayUI.md b/FrontEnd/Frame/LayUI.md deleted file mode 100644 index e13ffae..0000000 --- a/FrontEnd/Frame/LayUI.md +++ /dev/null @@ -1,102 +0,0 @@ -`目录 start` - -- [LayUI](#layui) - - [使用](#使用) - - [模块化](#模块化) - - [非模块化](#非模块化) - - [组件](#组件) - - [Layer](#layer) - - [树形](#树形) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# LayUI - -## 使用 - -### 模块化 -> 写法稍微复杂了些,但是提高了页面加载速度 - -_引入核心文件:_ -```html - - -``` -_使用layer模块:_ -```js - layui.use(['layer'], function(){ - var layer = layui.layer; - layer.msg('Hello World'); - }); -``` - -### 非模块化 - -## 组件 - -### Layer -> [layer组件](http://layer.layui.com/?alone) 十分强大 -> [layer 移动版](http://layer.layui.com/mobile/api.html) - - -_弹出页面层_ -- [ ] 如何将script 标签内容直接引入 -```js - layer.open({ - type: 1, - area: ['600px', '360px'], - shadeClose: true, //点击遮罩关闭 - content: 'test' - }); -``` -```html - -``` -_弹出页面中异步提交表单_ -```html - - -``` -```js - - function string(){ - var set_content = $("#set_key").html() - layer.tab({ - area: ['500px', '520px'], - tab: [{ - title: 'set', - content: set_content - }] - }); - $("#set-form").submit(function(e){ - e.preventDefault(); - set(); - }); - } - function set(){ - var key = $("#key").val() - var value = $("#value").val() - handlePost('/key', { - key: key, - value: value - }, function(data){ - console.log(data) - }, function(data){ - console.log(data) - }) - } -} -``` -### 树形 -> [基于layui树形菜单写的树形列表(treetable)](https://segmentfault.com/a/1190000011812724) - - - diff --git a/FrontEnd/Frame/Vue.md b/FrontEnd/Frame/Vue.md index 97e5393..6a65569 100644 --- a/FrontEnd/Frame/Vue.md +++ b/FrontEnd/Frame/Vue.md @@ -1,9 +1,18 @@ -`目录 start` - -- [Vue](#vue) - - [构建前后端分离的应用](#构建前后端分离的应用) +--- +title: Vue +date: 2018-11-21 10:56:52 +tags: + - Vue +categories: + - 前端框架 +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [Vue](#vue) + 1. [构建前后端分离的应用](#构建前后端分离的应用) + +**目录 end**|_2020-06-24 02:06_| **************************************** # Vue > [vue.js guide](https://cn.vuejs.org/v2/guide/) | [iview](https://www.iviewui.com/) @@ -13,4 +22,4 @@ ### 构建前后端分离的应用 [springboot+gradle+vue+webpack 组合使用](https://segmentfault.com/a/1190000007021883) -[使用Gradle整合SpringBoot+Vue.js-开发调试与打包](https://segmentfault.com/a/1190000008968295) \ No newline at end of file +[使用Gradle整合SpringBoot+Vue.js-开发调试与打包](https://segmentfault.com/a/1190000008968295) diff --git a/FrontEnd/HTML5.md b/FrontEnd/HTML5.md index cdbed1f..4c3c8f5 100644 --- a/FrontEnd/HTML5.md +++ b/FrontEnd/HTML5.md @@ -1,36 +1,31 @@ -`目录 start` - -- [HTML5](#html5) - - [参考资料](#参考资料) - - [特俗字符](#特俗字符) - - [基础结构标签](#基础结构标签) - - [head](#head) - - [meta](#meta) - - [title](#title) - - [base](#base) - - [link](#link) - - [style](#style) - - [script](#script) - - [常用结构](#常用结构) - - [form](#form) - - [label](#label) - - [关于引用](#关于引用) - - [插入和删除](#插入和删除) - - [数据存储](#数据存储) - - [cookie](#cookie) - - [LocalStorage和SessionStorage](#localstorage和sessionstorage) - - [清除](#清除) -- [XML](#xml) - - [XML文件头含义](#xml文件头含义) - - [XML的元素](#xml的元素) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: HTML5 +date: 2018-11-21 10:56:52 +tags: +categories: + - 前端 +--- + +**目录 start** + +1. [HTML5](#html5) + 1. [参考资料](#参考资料) + 1. [特殊字符](#特殊字符) + 1. [数据存储](#数据存储) + 1. [Cookie](#cookie) + 1. [LocalStorage和SessionStorage](#localstorage和sessionstorage) + 1. [清除](#清除) + 1. [IndexDB](#indexdb) +1. [Notification](#notification) + +**目录 end**|_2020-11-09 22:52_| **************************************** # HTML5 ## 参考资料 > [HTML5 教程 | 菜鸟教程](http://www.runoob.com/html/html5-intro.html) > [HTML5 教程 | W3School](http://www.w3school.com.cn/html5/) -## 特俗字符 + +## 特殊字符 ```html 空格: 代表一个半角空格 @@ -41,100 +36,11 @@ × :× ÷ :÷ ``` -## 基础结构标签 -### head -```html - 元素是所有头部元素的容器。 内的元素可包含脚本,指示浏览器在何处可以找到样式表,提供元信息,等等。 - 以下标签都可以添加到 head 部分:、<base>、<link>、<meta>、<script> 以及 <style>。 - -``` -#### meta -```html - 元数据(metadata)是关于数据的信息。 - <meta> 标签提供关于 HTML 文档的元数据。元数据不会显示在页面上,但是对于机器是可读的。 - 典型的情况是,meta 元素被用于规定页面的描述、关键词、文档的作者、最后修改时间以及其他元数据。 - <meta> 标签始终位于 head 元素中。 - 元数据可用于浏览器(如何显示内容或重新加载页面),搜索引擎(关键词),或其他 web 服务。 - 针对搜索引擎的关键词 - 一些搜索引擎会利用 meta 元素的 name 和 content 属性来索引您的页面。 - 下面的 meta 元素定义页面的描述: - <meta name="description" content="Free Web tutorials on HTML, CSS, XML" /> - 下面的 meta 元素定义页面的关键词: - <meta name="keywords" content="HTML, CSS, XML" /> - name 和 content 属性的作用是描述页面的内容。 -``` -#### title -```html - <title> 标签定义文档的标题。 - title 元素在所有 HTML/XHTML 文档中都是必需的。 - title 元素能够: - 定义浏览器工具栏中的标题 - 提供页面被添加到收藏夹时显示的标题 - 显示在搜索引擎结果中的页面标题 -``` -#### base -```html - <base> 标签为页面上的所有链接规定默认地址或默认目标(target): -``` - -#### link -```html -<link> 标签定义文档与外部资源之间的关系。 -<link> 标签最常用于连接样式表: -``` - -#### style -> `<style>` 标签用于为 HTML 文档定义样式信息。您可以在 style 元素内规定 HTML 元素在浏览器中呈现的样式: -```html - <head> - <style type="text/css"> - body {background-color:yellow} - p {color:blue} - </style> - </head> -``` -### script -`<script> 标签用于定义客户端脚本,比如 JavaScript。` - -******************** -> 以上的link script style 等引用外部资源的标签要注意路径问题 -1. / 开头则是相对于项目的根路径来定位的 -2. 开头为空,就是相对于该页面的的相对路径.` 被URL尾部多输入一个/坑过,所以最好采用第一种稳妥` -3. ..开头就是相对于该页面的相对父路径 - -### 常用结构 -#### form -```html - <form action="save.php" method="post" > - <label>爱好:</label> - <select> - <option value="看书">看书</option> - <option value="旅游">旅游</option> - <option value="运动">运动</option> - <option value="购物">购物</option> - </select> - </form> -``` -#### label -label标签不会向用户呈现任何特殊效果,它的作用是为鼠标用户改进了可用性。如果你在 label 标签内点击文本,就会触发此控件。 -就是说,当用户单击选中该label标签时,浏览器就会自动将焦点转到和标签相关的表单控件上(就自动选中和该label标签相关连的表单控件上)。 -注意:标签的 for 属性中的值应当与相关控件的 id 属性值一定要相同。 - -#### 关于引用 -```html -<blockquote>这是长的引用。</blockquote> -<q>这是短的引用。</q> - -``` -`使用 blockquote 元素的话,浏览器会插入换行和外边距,而 q 元素不会有任何特殊的呈现。` -#### 插入和删除 -```html - <p>一打有 <del>二十</del> <ins>十二</ins> 件。</p> -``` ## 数据存储 -### cookie +### Cookie + ### LocalStorage和SessionStorage > [基础详细的一篇博客](http://www.cnblogs.com/st-leslie/p/5617130.html) @@ -142,382 +48,11 @@ label标签不会向用户呈现任何特殊效果,它的作用是为鼠标用 > [HTML5中的localStorage什么时候会被清空?](https://segmentfault.com/q/1010000000123500) > [翻译:清除各个浏览器中的数据研究](http://www.zhangxinxu.com/wordpress/2012/09/%E7%BF%BB%E8%AF%91%EF%BC%9A%E6%B8%85%E9%99%A4%E5%90%84%E4%B8%AA%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%AD%E7%9A%84%E6%95%B0%E6%8D%AE%E7%A0%94%E7%A9%B6/) -- 自己在火狐中尝试了下,清除 网络内容缓存 对localStorage没有影响 - -*********************************** -```html -1.为了确保浏览器能够正确读取字符的编码,整个字符编码必须放置在文档的前512个字符中 -2.HTML中不需要声明JavaScript的type属性 - -刷新iframe 的父页面 - iframe页面是内嵌到父页面的,当点击iframe页面的服务器控件时,默认只刷新iframe页面,父页面是不会刷新的。若想刷新父页面,可以使用js来实现,如 - 1. parent.location.reload(); - 这种方法会重新加载整个页面。但如果要在原页面的基础上传递参数,则可以使用下面的方法: - 2.top.document.location.href='xxx.aspx?id=xx'。 - 但这两种方法都有一个共同的缺点,就是iframe内嵌页面的状态不会保存了,刷新后会重新回到第一次加载的状态。 - - -=========================1、输入框点击清空============== - -<input name="textfield" type="text" id="textfield" value="请输入内容" onclick="this.value=''" /> +- 在火狐中 清除 网络内容缓存 对localStorage没有影响 -2、输入框点击显示提示内容 - -操作:鼠标点击输入框出现提示内容,再次点击清空内容可以进行输入。 - -<input type="text" name="textfield2" id="textfield2" onblur="note_click(this);" onclick="note_click(this)" /> - <script type="text/javascript"> - function note_click(target) - { - if(target.value=='') - { - target.style.color="#B0B0B0"; - target.value="请输入数字"; - } - else if(target.value=="请输入数字") - { - target.style.color="#000000"; - target.value=""; - } - } - </script> - - ====================================【HTML标签笔记】========================================== - -<i></i>:斜体 -<u></u>:下划线 -<b></b>:粗体 -<strong></strong>:更粗 -<s></s>:删除线 -<sup></sup>上标 -<sub></sub>下标 -face:字体设置 -<p align="right"></p> //align:设置水平对齐方式 -<hr>:水平线 -noshade:去掉阴影部分 -<pre></pre>:预排版标记 - - -HTML项目符号 -<ul> -<li> -</li> -</ul> -常用属性type,取值:disc代表小黑点,circle代表空心圆,square代表实心方块 -注意:<ul>和<li>是块元素 - -HTML是编号列表(有序列表) -<ol> -<li> -</li> -</ol> -常用属性:type和start - -滚动字幕标记<marquee> -常用属性:direction:滚动方向,取值:up,down,left,right - width:滚动宽度 - height:滚动高度 - bgcolor:滚动背景色 - scrollamount:滚动步长值 - scrolldelay:两步之间的停留时间,以毫秒为单位,1s=1000毫秒 - loop:循环滚动次数 -图片标记 -<img 属性="值"> -常用属性:height - width - align:left/center/right - src(图片路径/相对路径) - Hspace:图片与左右文字之间的距离(水平距离) - Vspace:图片与上下文字之间的距离(垂直距离) - - -超级链接 (行内元素) -<a 属性="值">.......</a> - href:目标文件的地址URL,该URL可以是相对地址,也可以是绝对地址 - target:目标文件的显示窗口 - _blank:在新窗口中打开目标文件 - _self:在当前窗口中打开(默认打开),相当于“替换”操作。 - -parent:在父级窗口来打开目标文件 - _top:在顶级窗口来打开目标文件。 - 1.(1)远程的绝对地址 - 访问远程的文件,总是以域名、开机名开头 - <a href="http://xxx.com">协议是http://的就是远程的绝对地址 - (2)本地的绝对路径 - 访问本地的绝对路径,是以file:///开头的绝对地址 -2.相对地址URL - (1)当前文件和目标文件是同级关系 - (2)当前文件与目标文件所在的文件夹是同级关系,先找“文件夹名”,然后再找“文件名”,也就是目标文件位于下一级。 - (3)目标文件位于上一层目录中,往上找对应的目录,再找目录中的文件。往上找使用../符号表示 - ../代表上一层目录 - ../../代表上两层目录 - ............ - ---------特殊的链接--------- - <a 属性="xx/Winrar.rar"></a>下载Winrar解压缩文件 - 邮箱链接 - <a 属性=“mailto:邮箱地址”></a> - 普通空链接 - <a 属性="#"></a> - js链接 - <a 属性="javascript:window.close()">关闭窗口的意思</a> - ------锚点链接------- - 含义:锚点链接,是在一个网页的不同区域进行跳转,锚点理解为“定义记号”。 - 定义锚点:<a name="锚点名称"></a> - 跳转到锚点(记号) - 语法:<a href="锚点名称">........</a> - 例如:<a href="#锚点名称">.........</a> - - --------<meta>标记------------ - <meta>的主要作用,是提供网页的元信息,比如,指定网页的搜索关键字。 - <meta>标记有两个属性1.http-equiv和name - (1)设置网页的字符集 - <meta http-equiv="Content-Type" content="text/html;charset="utf-8"/> - (2)网页自动刷新 - <meta http-equiv="refresh" content="2"> - <meta http-equiv="refresh" content="2;url=http://www.baidu.com">//二秒钟后,跳转到百度 - 2.name属性 - name属性主要用于设置网页的搜索关键字、版权信息、作者等。 - (1)设置网页搜索关键字 - <meta name="keywords" content="关键字内容"/> - (2)设置网页描述信息 - <title> - - - ------XHTML简介---------- - XHTML的目地是为了取代HTML - XHTML的标记和HTML一模一样 - XHTML的语法要比HTML严格的多 - XHTML是可扩展超文本标注预言 - ----------XHTML编写规范-------------- - 所有标记和属性要全小写 - 单边标记必须关闭如
- 所有的属性都必须有值,如:
----->
- 标记之间要顺序嵌套,外层套内层,一层套一层。 - XHTML网页必须要有DTD文档类型定义代码。 - - ------DTD文档类型定义------------ - DTD文档类型定义的目地:是一种验证机制,也就是说检验一下你写的XHTML标记语言是否合法。 - - - DTD一共有三大类型 - (1)严格型的DTD - 在严格的DTD中,不能再使用各种表现的标记,如:、等,(要求必须使用CSS来取代各种表现标记。) - 严格型DTD表现方式: - (2)过渡型DTD - 在过渡型的DTD中,可以继续使用HTML中的表现的写法。 - 这些表现标记,还可以使用。如:、 - 过渡型的DTD的表达方式: - (3)框架的DTD - 框架的DTD表现方式: - ----------表格学习-------------- - 表格标签-------块元素 - 1.表格的结构 - - //代表的行,这里代表一行 - //代表单元格 - -
- 2.属性 - 1.width:宽度 - 2.height:高度 - 3.Align:表格水平对其方式,left、center、right - 4.Border:边框粗细 - 5.bgcolor:表格背景色 - 6.background:背景图片URL - 7.cellpadding:单元格边线到内容间的距离(填充距离) - 8.cellspacing:单元格与单元格之间的距离(间距) - 9.bordercolor:边框颜色 - 10.rules:合并单元格边框线,取值:all //友情提醒因为学习的html没有接触css所以才用rules,rules兼容性不好,一般都用CSS取代他。 - - 3.属性----行标记 - 1.bgcolor: - 2.height: - 3.align: - 4.valign:垂直居中,取值top(上)、middle(中)、bottom(下) - - 4.
属性 - 1.width: - 2.height: - 3.bgcolor: - 4.background: - 5.align: - 6.valign: - 7.rowspan:上下单元格合并 - 8.colspan:左右单元格合并 - - -------表单学习------ - 1.表单的概念 - 表单主要用来获取客户端用户数据(信息)的,如注册表单、查询表单、登录表单等。 - 2.
标记属性------块元素 - name: - method:表单的提交方式,取值:get/post - action:制定表单的处理程序,一般是PHP文件 - enctype:制定表单数据的编码方式(解密方式),这个属性只能用在method="post"的情况下. - (1)application/x-www-form-urldecoded //默认的传递 - (2) multipart/form-data //如果你上传文件,该值必须它自己 - 注意:上传文件一定要有enctype="multipart/form-data" - - 3.GET方法和POST方法 - (1)GET提交方式 - GET方式,是将表单数据追加到action指定的处理程序的后面,然后向服务器发送请求。 - 注意:地址栏传数据的方式,默认就是GET方式。, - GET方式的特点:1.GET方式不能提交敏感数据,如:密码。。 - 2.GET方式值提交少量数据,因为地址栏的长度有限制,大约100外字符 - 3.GET方式下不能上传附件 - (2)POST表单提交方式 - POST提交方式,它不是将表单数据追加到地址上,而是直接传给表单处理程序。 - - POST方式的特点: - 1.POST提交的数据相对安全。 - 2.POST可以提交海量数据。 - 3.POST方式可以上传附件。 - - 单行文本域 - 语法格式 - 常用属性: - name:文本框的名字,只能以字母开头 - type:表单元素的类型 - value:文本框中的值 - size:文本框的长度,以“字符”为单位。 - maxLength:最多可以输入多少个字符,超出的就输不进去了。 - readonly:只读属性。可以选中,但不能修改,如readonly="readonly" - disabled:禁用属性,不能选中,不能修改,如:disabled="disabled" - - 单行密码域 - 语法格式 - 常用属性和单行文本域属性一样。 - - 单选按钮 - 语法格式 - 常用属性: - Name:元素的名称 - Value:元素的值,该value中数据将发往服务器。 - Cheaked:默认选择哪一项,如:cheacked="checked" - 注意:一组单选按钮,只能选择一个,但name的值必须一致。 - - 复选框 - 语法格式: - 常用属性: - name:元素名称 - value:元素的值 - checked:默认选中,如checked="checked" - - 下拉列表 - 语法格式: - 常用属性:name - option属性:value和selected - selected:默认选中,如selected="selected" - - 文本区域 - - 常用属性:name: - cols:宽度,是指多少个字符串 - rows:高度,是指几行高 - 提示:之间是默认文本 - - 各种按钮学习 - 提交按钮: - 重置按钮: - 图片按钮: - 普通按钮:要与js结合使用,如下代码: - - 上传文件域 - 语法格式: - 常用属性: - name:表单元素的名称 - value:表单元素的值,这个值其实就是上传的文件名。 - 语法格式例子: - - 隐藏域 - 功能:隐藏域就是看不见的一个框。传递一些值,而这个值又不想让别人看见。 - 用处:主要用于php后台程序。 - 语法格式: - - - -实战运用:制作网页http://www.chinayarn.com/mart/index2011.asp/http://www.nanshanski.com/index-cn.asp/http://www.jingying.com.cn/参考如下 - - - html的注释: //注意注释的内容是不会显示的,注释的目地是为了维护方便。 - - -----网页多媒体----- -网页上的视频大多数为flash格式的,因为flash的兼容性比较好。 - 以flash动画为例,播放flash动画的代码如下,这个代码不用记,看懂就行。 - - - - - - - - 以上标记的说明:标记,是IE中引入多媒体的标记。 - 标记:是netscape中引入多媒体的标记 - - - ----------图片热点(图像地图)--------- - 图片热点含义:绘一张图片加多个链接,默认情况下,一张图只能加一个链接。 - 标记结构: - - - - - (1)标记常用属性 - shape:热区的形状,取值:rect(矩形),circle(圆形),polygon(多边形) - coords:热区的坐标(位置) - 如果shape="rect"时,那么,coords="x1,y1,x2,y2" 例如:corrds=“50,50,200,150” - *(x1,y1)为矩形左上角的坐标值,(x2,y2)为矩形右上角坐标值。so (x2-x1)和(y2-y1)可知矩形的长和宽。 - 如果shape="circle"时,那么,coords="x,y,r",其中(x,y)为圆心坐标,而r是圆的半径。 - - -----------普通框架----------- - 1.框架的概念:框架技术:将一个浏览器窗口划分成若干个小窗口,每个小窗口显示一个独立的网页。 - - 2.框架集和框架页 - 框架集:主要用来划分窗口的 - 框架页:主要用来制定窗口默认显示的网页地址 - - 框架与窗户很像 - 打比方: 一个窗户由窗格(框架集)和玻璃(框架页)构成。 - 先规划窗格,然后再确定每个窗格中放的玻璃。(先有结构,后有内容。) - - 框架网页的DID必须是: - 代码如下: - - - - (1)常用属性属性 - cols:划分左右型框架 - 1.cols="200,*" //左框架的宽度为200px,剩下的都是右框架。 - 2.cols="180,*,180" //左框架和右框架的宽分别为180px,剩下的都是中间框架。 - 3.cols="20%,*" //另类写法,划分框架时,可以用百分比来表示 - rows:划分上下型框架 - 1.rows="200,*" //上框架的高度为200px,剩下的都是下框架。 - 2.row="180,*,180" //上框架和下框架的高度分别为180px,剩下的的都是中间框架。 - 注意:cols和rows不能两个同时用。 - (2)frameborder:是否显示框架的边框线,取值1或0,亦yes/no - border:边框线的粗细 - bordercolor:边框的颜色 - 3.框架页的属性 - (1)src:该小窗口中,默认显示的网页地址。 - noresize:不能调整小窗口的大小,如noresize="noresize" - scrolling:是否显示滚动条,取值:auto,no,yes - name:给当前小窗口起个名字,这个name就是给标记target属性来用的。 - 提示做网站后台时,返回首页的相关代码: -``` -# XML -## XML文件头含义 -``` - web-app 是web.xml的根节点标签名称 - version 是版本的意思 - xmlns是web.xml文件用到的命名空间 - xmlns:xsi是指web.xml遵循xml规范 - xsi:schemaLocation是指具体用到的schema资源(对文档的限制) -``` +### IndexDB -## XML的元素 -XML中元素的标记是自定义的,并具有明确的含义,其组织结构是树形的层次结构,每个元素及其 -子元素 并不是XML规范好的,而是用户根据自己需要来自定义的,就像是我写坦克大战时用的文件 -保存法,就需要有一定的格式来读取(辨认)所读取的数据是什么类型 - XML文档结构清晰,易读,而且是根据开放的标准建立的 +************************ +# Notification +> [MDN](https://developer.mozilla.org/en-US/docs/Web/API/notification) diff --git a/FrontEnd/Hexo.md b/FrontEnd/Hexo.md deleted file mode 100644 index eafedbe..0000000 --- a/FrontEnd/Hexo.md +++ /dev/null @@ -1,74 +0,0 @@ -`目录 start` - -- [配置Hexo的Github博客](#配置hexo的github博客) - - [安装 hexo](#安装-hexo) - - [主配置文件](#主配置文件) - - [插件](#插件) - - [文章相关](#文章相关) - - [配置](#配置) - - [tags](#tags) - - [categories](#categories) - - [新建文章](#新建文章) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 配置Hexo的Github博客 -> 参考 [博客](http://stonebegin.com/hexo+github.html) | [部署Hexo](http://letus.club/2016/04/04/deploy-hexo-and-change-theme/) - -- [ ] 在docker中下拉笔记仓库, 然后构建, 然后推送到github的page上 -- [ ] 其实可以更为简单, 使用travi.ci 进行构建和发布 [参考 ](https://notes.iissnan.com/2016/publishing-github-pages-with-travis-ci/) - -## 安装 hexo -1. `npm install -g hexo` -1. 新建一个目录然后初始化 `hexo init` -1. 根据md生成静态资源文件 `hexo generate` | ` hexo g` -1. 本地试运行 `hexo server` | ` hexo s` -1. 清除静态文件` hexo clean` -1. 发布到远程 `hexo deploy` | `hexo d` - -### 主配置文件 -> [官网配置说明](https://hexo.io/zh-cn/docs/configuration.html) -- 当前目录下的`_config.yml`是主配置文件 - -- Site 部分 - - subtitle: 对自己的描述或者对网站的描述 - - description: 对网站的描述,提供给搜索引擎看的 - - author: 作者 - - language: 中文是zh-CN - - timezone: 不填就好,系统自己会计算时区 -- URL - - url: 填写你自己网站的域名 - - root: 如果你的网站首页就在你github仓库的根目录下,则不用修改,否则改成你网站首页所在目录即可。 - - 后面默认。 - -- Extensions - - theme 填写`/thems`文件夹下的`主题文件夹名字` [官方主题](https://hexo.io/themes/) - - 选好对应的主题只要 `git clone` 在`/themes`文件夹下即可 - - 例如 next主题 [官方使用文档](http://theme-next.iissnan.com/getting-started.html) - -- Deployment - - type: git - - repo: 仓库URL - - branch: master 分支,一般是master - -> [更多发布方式](https://hexo.io/docs/deployment.html) -### 插件 -> [indigo](https://github.com/yscoder/hexo-theme-indigo) `一个Material Design风格的Hexo主题` - -> [next](https://github.com/iissnan/hexo-theme-next) - -## 文章相关 -### 配置 -#### tags -`source/tags/index.md` yml 语法 - -#### categories -`source/categories/index.md` - -### 新建文章 -- `hexo new "postName"` #postName是文章的名字 - - 文章的开头具有 时间,标题,分类, 标签等信息 -- 然后生成`hexo g` 发布 `hexo d` - -- [ ] 搭建一个可用的博客, 然后将博客通过该方式书写 - diff --git a/FrontEnd/JavaScript.md b/FrontEnd/JavaScript.md index 29426cb..84c1776 100644 --- a/FrontEnd/JavaScript.md +++ b/FrontEnd/JavaScript.md @@ -1,32 +1,37 @@ -`目录 start` - -- [JavaScript](#javascript) - - [数据类型](#数据类型) - - [字符串](#字符串) - - [函数](#函数) - - [函数传值](#函数传值) - - [JSON](#json) - - [常用功能小模块](#常用功能小模块) - - [输入校验](#输入校验) - - [Ajax](#ajax) - - [事件](#事件) - - [鼠标](#鼠标) - - [滚轮](#滚轮) - - [常用库和框架](#常用库和框架) - - [Jquery](#jquery) - - [Ajax](#ajax) - - [form插件](#form插件) - - [echarts](#echarts) - - [资源文件](#资源文件) - - [图片](#图片) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: JavaScript +date: 2018-11-21 10:56:52 +tags: + - 基础 +categories: + - JavaScript +--- + +💠 + +- 1. [JavaScript](#javascript) + - 1.1. [数据类型](#数据类型) + - 1.1.1. [字符串](#字符串) + - 1.2. [函数](#函数) + - 1.2.1. [函数传值](#函数传值) + - 1.3. [JSON](#json) + - 1.4. [常用功能小模块](#常用功能小模块) + - 1.4.1. [输入校验](#输入校验) + - 1.5. [Ajax](#ajax) + - 1.6. [事件](#事件) + - 1.6.1. [键盘](#键盘) + - 1.6.2. [鼠标](#鼠标) + - 1.7. [常用库和框架](#常用库和框架) + - 1.7.1. [Jquery](#jquery) + - 1.7.2. [echarts](#echarts) + - 1.8. [资源文件](#资源文件) + - 1.8.1. [图片](#图片) + +💠 2024-07-07 18:00:42 **************************************** # JavaScript ## 数据类型 -> 虽然是弱类型,但还是要注意一下 - ### 字符串 - 字符串转码: @@ -35,22 +40,22 @@ ## 函数 ### 函数传值 ```js -function handlerGet(url, role, success, fail) { - var request = $.ajax({ - method: 'GET', - url : 'xxx'+url - }); - request.done(success); - request.fail(fail); -} -function testRole() { - handlerGet('/world', 'student', - function (data) { - layer.msg('获取成功'); - }, function (data) { - layer.msg('身份认证已过期, 请重新登录'); - }) -} + function handlerGet(url, role, success, fail) { + var request = $.ajax({ + method: 'GET', + url : 'xxx'+url + }); + request.done(success); + request.fail(fail); + } + function testRole() { + handlerGet('/world', 'student', + function (data) { + layer.msg('获取成功'); + }, function (data) { + layer.msg('身份认证已过期, 请重新登录'); + }) + } ``` ********************** ## JSON @@ -93,22 +98,51 @@ function testRole() { - [Blog:关于Input的输入校验](http://yuncode.net/code/c_5039bb4a3fccf28)`数字,字母汉字等限制` ## Ajax -> [js 原生 post请求](https://segmentfault.com/q/1010000005162727) +> [参考: 使用 Fetch](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch) -> [参考博客: 使用 Fetch](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch) +```js +function get(url, handle) { + let httpRequest = new XMLHttpRequest(); + httpRequest.open('GET', url, true); + httpRequest.send(); + /** + * 获取数据后的处理程序 + */ + httpRequest.onreadystatechange = function () { + if (httpRequest.readyState === 4 && httpRequest.status === 200) { + handle(httpRequest) + } + }; +} -## 事件 +function post(url, data, handle) { + let xhr = new XMLHttpRequest(); + //使用HTTP POST请求与服务器交互数据 + xhr.open("POST", url, true); + //设置发送数据的请求格式 + xhr.setRequestHeader('content-type', 'application/json'); + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + handle(xhr.responseText) + } + } + //将用户输入值序列化成字符串 + xhr.send(JSON.stringify(data)); +} +``` +## 事件 +### 键盘 ### 鼠标 - -#### 滚轮 > [JavaScript 鼠标滚轮事件](https://www.web-tinker.com/article/20037.html) - +************************ ## 常用库和框架 - lozad.js 懒加载 +- [网页底部的浏览 ](https://www.logicbig.com/tutorials/java-ee-tutorial/jpa/group-by-criteria.html) +- [游戏手柄模拟](https://www.phaser-china.com/example-30.html) ### Jquery > jquery有是slim版(没有ajax的精简版 ) [JQuery官网](http://jquery.com/) | [Jquery教程](http://www.w3school.com.cn/jquery/index.asp) @@ -116,38 +150,19 @@ function testRole() { - 事件绑定 `$('#Button').on('click', function(){})` - 在HTML的DOM上绑定数据:设置 `data-*` 属性 然后jq拿到元素直接调用 `$(this).data('id')`拿到值就可以避免函数传值 -_原生方式异步提交_ +_原生方式异步提交Form_ ```js $("#set-form").submit(function(e){ e.preventDefault(); console.log('prepare submit') }); ``` -#### Ajax -> [ajax文档](https://api.jquery.com/jQuery.ajax/) - -#### form插件 -```js -// 使用jquery 的 form插件进行异步提交 -$(".submit").on('click', function () { - console.log('dfs') - // var jk = $("#contents").submit() - var options = { - // target:'#contents', //后台将把传递过来的值赋给该元素 - url:'../teacher/topic/add', //提交给哪个执行 - type:'POST', - success: function(data){ - console.log(data) - } //显示操作提示 - }; - $('#contents').ajaxSubmit(options); -}) -``` ### echarts > [官网](http://echarts.baidu.com/index.html) | 做图表展示很简单 +*************************************** ## 资源文件 ### 图片 -> [参考博客: JS 图片转Base64](http://www.cnblogs.com/wujingtao/p/5196836.html) +> [参考: JS 图片转Base64](http://www.cnblogs.com/wujingtao/p/5196836.html) diff --git a/FrontEnd/LearnPS.md b/FrontEnd/LearnPS.md index db4717a..f2f3a45 100644 --- a/FrontEnd/LearnPS.md +++ b/FrontEnd/LearnPS.md @@ -1,42 +1,54 @@ -`目录 start` - -- [快捷键](#快捷键) - - [Ctrl](#ctrl) - - [Alt](#alt) - - [钢笔抠图技巧](#钢笔抠图技巧) - - [Banner案例](#banner案例) - - [形状和路径](#形状和路径) - - [形状层编辑:](#形状层编辑) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: PS学习 +date: 2018-12-14 09:22:56 +tags: + - PS +categories: + - 工具 +--- + +**目录 start** + +1. [PS](#ps) + 1. [在线工具](#在线工具) + 1. [快捷键](#快捷键) + 1. [钢笔抠图技巧](#钢笔抠图技巧) + 1. [Banner案例](#banner案例) + 1. [形状和路径](#形状和路径) + 1. [形状层编辑:](#形状层编辑) + +**目录 end**|_2020-06-24 02:06_| **************************************** -#快捷键 - -###Ctrl -- Ctrl+N 新建文件 -- Ctrl+H 隐藏辅助线 视图菜单栏有清除所有辅助线 -- Ctrl+D 取消选框 -- Ctrl+Alt+Z 一直撤销 -- Ctrl+Z 只撤销上一步 -- Ctrl+T 拖放选框大小,若锁定当前比例 加上shift -- Ctrl+R 标尺 -- Ctrl+J 通过拷贝的图层 -- Ctrl+Alt 复制当前选择框 -- Ctrl 剪切当前选择框 -- Ctrl+Del 填充背景 -- Ctrl+Enter 路径变选框 -- Ctrl+Shift+N 新建图层 -###Alt -- Alt 移动当前选择框 -- Alt+Del 填充前景 -- Alt+鼠标 去掉钢笔的手柄 +# PS +## 在线工具 +- [www.uupoop.com](https://www.uupoop.com/)`在线PS` +- [PS在线工具](https://www.photoshop.com/tools?wf=editor)`官方在线版` + +## 快捷键 + +| Ctrl | Shift | Alt | Key | Action | +|:----: |:----:|:----:|:----:|:----| +|C| | | `H` | 隐藏辅助线 视图菜单栏有清除所有辅助线 | +|C| | | `D` | 取消选框 +|C| |A| `Z` | 一直撤销 +|C| | | `Z` | 只撤销上一步 +|C| | | `T` | 拖放选框大小,若锁定当前比例 加上shift +|C| | | `R` | 标尺 +|C| | | `J` | 通过拷贝的图层 +|C| | | `X` | 剪切当前选择框 +|C|S| | `N` | 新建图层 +|C| | | `Del` | 填充背景 +|C| | | `Enter` | 路径变选框 +| | |A| | 移动当前选择框 +| | |A| `Del` | 填充前景 +| | |A| `鼠标左键` | 去掉钢笔的手柄 * 使用时间轴可以制作gif动图 * 先羽化再抠图或者填充 * 渐变工具的使用 -##钢笔抠图技巧 -*** 一定要选择路径状态*** +## 钢笔抠图技巧 +***一定要选择路径状态*** 1. 钢笔定点要向对象轮廓内定1-2像素; 2. 钢笔拖动方向,沿着弧度方向相切; 3. 抠图出现错误时按撤销键 不能按Delete键,会删除锚点断开路径; @@ -44,17 +56,18 @@ 5. 路径变选框 Ctrl+Delete; 6. 钢笔的手柄不能太长,图片会乱窜; -##Banner案例 +## Banner案例 * 标尺 * 扩展画布 Ctrl+Alt+C * 存储路径: 扣好图像后,双击工作路径 就会保存路径了 * 使用文案 : - 复制粘贴过来 再提交就行了 - 修改文字(新建图层才能做一些美化操作) -##形状和路径 -** 路径: 只是回执一个路径(辅助功能) 需要后期制作(变选框,填充,描边等) -** 形状:会自动建立图层 (形状图层) 填充前景色;(UI构型) - -##形状层编辑: - 1 双击形状图缩略图,快速换色 - 2 属性栏 ,填色属性,描边属性,可以修改尺寸 + - 复制粘贴过来 再提交就行了 + - 修改文字(新建图层才能做一些美化操作) + +## 形状和路径 +* 路径: 只是回执一个路径(辅助功能) 需要后期制作(变选框,填充,描边等) +* 形状:会自动建立图层 (形状图层) 填充前景色;(UI构型) + +## 形状层编辑: +1. 双击形状图缩略图,快速换色 +2. 属性栏 ,填色属性,描边属性,可以修改尺寸 diff --git a/FrontEnd/Node/NodeJS.md b/FrontEnd/Node/NodeJS.md index a0ec025..ba9f82f 100644 --- a/FrontEnd/Node/NodeJS.md +++ b/FrontEnd/Node/NodeJS.md @@ -1,42 +1,35 @@ -`目录 start` - -- [NodeJs](#nodejs) - - [安装](#安装) - - [配置](#配置) - - [使用淘宝的镜像](#使用淘宝的镜像) - - [使用](#使用) - - [安装Vue](#安装vue) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: NodeJS +date: 2018-11-21 10:56:52 +tags: + - NodeJS +categories: + - JavaScript +--- + +**目录 start** + +1. [NodeJs](#nodejs) + 1. [安装](#安装) + 1. [配置](#配置) + 1. [镜像](#镜像) + +**目录 end**|_2020-12-18 15:50_| **************************************** # NodeJs ## 安装 1. [官网下载](https://nodejs.org/en/) -2. 进入解压的 `bin目录` 将所有的文件建立软链接到 `/usr/local/bin/` 目录下 +2. 进入解压的 `bin/node 和 npm ` 建立软链接到 `/usr/local/bin/` 目录下 3. 执行 node --version 和 npm -v 查看是否配置成功 4. 添加node的真正解压目录到环境变量中, 之后安装的模块才能被找到 ```sh -NODE_HOME=/home/kcp/Application/sdk/node-v8.11.1-linux-x64 -export PATH=$PATH:$NODE_HOME/bin + NODE_HOME=/home/kcp/Application/sdk/node-v8.11.1-linux-x64 + export PATH=$PATH:$NODE_HOME/bin ``` ## 配置 -### 使用淘宝的镜像 +### 镜像 > [镜像地址](http://npm.taobao.org/) `还包括各种常用软件` -1. 临时使用 `npm --registry https://registry.npm.taobao.org install express` -2. 永久使用 `npm config set registry https://registry.npm.taobao.org` -3. 通过cnpm使用 `npm install -g cnpm --registry=https://registry.npm.taobao.org` _emmm, 为什么我就配置不成功_ - -> 查看配置是否成功: `npm config get registry` - -## 使用 - -> [Hexo](/Skills/View/Hexo.md) - - -### 安装Vue -> `npm install -g vue-cli --registry=https://registry.npm.taobao.org` - - +可使用 nrm 管理 `npm instal -g nrm` diff --git a/FrontEnd/ResponseCode.md b/FrontEnd/ResponseCode.md index d5dfcf6..582daf0 100644 --- a/FrontEnd/ResponseCode.md +++ b/FrontEnd/ResponseCode.md @@ -1,145 +1,161 @@ -`目录 start` - -- [HTTP状态码大全](#http状态码大全) - - [标准扩展码](#标准扩展码) - - [1xx Informational 信息化](#1xx-informational-信息化) - - [2xx Success 成功](#2xx-success-成功) - - [3xx Redirection 重定向](#3xx-redirection-重定向) - - [4xx Client Error 客户端错误](#4xx-client-error-客户端错误) - - [5xx Server Error 服务器错误](#5xx-server-error-服务器错误) - - [非官方扩展码](#非官方扩展码) - - [互联网信息服务扩展状态码](#互联网信息服务扩展状态码) - - [NGINX 扩展状态码](#nginx-扩展状态码) - - [七牛扩展状态码](#七牛扩展状态码) +--- +title: HTTP状态码 +date: 2018-11-21 10:56:52 +tags: + - 基础 +categories: + - Web +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [HTTP状态码大全](#http状态码大全) + 1. [标准扩展码](#标准扩展码) + 1. [1xx-Informational-信息化](#1xx-informational-信息化) + 1. [2xx-Success-成功](#2xx-success-成功) + 1. [3xx-Redirection-重定向](#3xx-redirection-重定向) + 1. [4xx-ClientError-客户端错误](#4xx-clienterror-客户端错误) + 1. [5xx-ServerError-服务器错误](#5xx-servererror-服务器错误) + 1. [非官方扩展码](#非官方扩展码) + 1. [互联网信息服务扩展状态码](#互联网信息服务扩展状态码) + 1. [NGINX-扩展状态码](#nginx-扩展状态码) + 1. [七牛云扩展状态码](#七牛云扩展状态码) + +**目录 end**|_2020-12-18 15:50_| **************************************** # HTTP状态码大全 -> [HTTP状态码](http://www.runoob.com/http/http-status-codes.html) -> [HTTP状态码百度百科](https://baike.baidu.com/item/HTTP%E7%8A%B6%E6%80%81%E7%A0%81) -## 标准扩展码 +> [wiki: http status code](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes) +> [W3C RFC 2616 ](https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html) +> [MDN: HTTP 响应状态代码](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status) -### 1xx Informational 信息化 -``` -100 Continue 继续 -101 Switching Protocols 交换协议 -102 Processing 继续处理 -``` -### 2xx Success 成功 -``` -200 OK -201 Created 创建 -202 Accepted 已接受 -203 Non-Authoritative Information 非授权信息 -204 No Content 无内容 -205 Reset Content 重置内容 -206 Partial Content 部分内容 -207 Multi-Status 多状态 -208 Already Reported 已报告 -226 IMIM Used 使用的 -``` -### 3xx Redirection 重定向 +注意: 这个状态码本质上是一个行业标准,并不限制实现, 在某公司遇到过 后端Tomcat报错5xx, 应用里面过滤器硬改成404 的操作 + +> [HTTP状态码](http://www.runoob.com/http/http-status-codes.html) +> [常见状态码](https://blog.csdn.net/huwei2003/article/details/70139062) +> [HTTP状态码百度百科](https://baike.baidu.com/item/HTTP%E7%8A%B6%E6%80%81%E7%A0%81) + +## 标准扩展码 +### 1xx-Informational-信息化 +``` + 100 Continue 继续 + 101 Switching Protocols 交换协议 + 102 Processing 继续处理 +``` +### 2xx-Success-成功 +``` + 200 OK + 201 Created 创建 + 202 Accepted 已接受 + 203 Non-Authoritative Information 非授权信息 + 204 No Content 无内容 + 205 Reset Content 重置内容 + 206 Partial Content 部分内容 + 207 Multi-Status 多状态 + 208 Already Reported 已报告 + 226 IMIM Used 使用的 +``` +### 3xx-Redirection-重定向 > [HTTP返回码中301与302的区别 ](http://blog.163.com/darkness@yeah/blog/static/131774484201221495129735/) ``` -300 Multiple Choices 多种选择 -301 Moved Permanently 永久移动 -302 Found 发现 代表暂时性转移(Temporarily Moved )。 -303 See Other 查看其它 -304 Not Modified 未修改,使用缓存 -305 Use Proxy 使用代理 -306 Switch Proxy 开关代理 -307 Temporary Redirect 临时重定向 -308 Permanent Redirect 永久重定向 -``` -### 4xx Client Error 客户端错误 -``` -400 Bad Request 错误的请求 -401 Unauthorized 未授权 -402 Payment Required 需要付费 -403Forbidden 拒绝访问 -404 Not Found 未找到 -405 Method Not Allowed 不允许的方法 -406 Not Acceptable 不可接受 -407 Proxy Authentication Required 代理服务器需要身份验证 -408 Request Timeout 请求超时 -409 Conflict 冲突 -410 Gone 完成 -411 Length Required 需要长度 -412 Precondition Failed 前提条件失败 -413 Payload Too Large 负载过大 -414 URI Too Long 太长 -415 Unsupported Media Type 不支持的媒体类型 -416 Range Not Satisfiable 的范围不合适 -417 Expectation Failed 预期失败 -418 I'm a teapot 我是一个茶壶 -421 Misdirected Request 误导请求 -422 Unprocessable Entity 无法处理的实体 -423 Locked 锁定 -424 Failed Dependency 失败的依赖 -426 Upgrade Required 升级所需 -428 Precondition Required 所需的先决条件 -429 Too Many Requests 太多的请求 -431 Request Header Fields Too Large 请求头字段太大 -451 Unavailable For Legal Reasons 不可出于法律原因 + 300 Multiple Choices 多种选择 + 301 Moved Permanently 永久移动 + 302 Found 发现 代表暂时性转移(Temporarily Moved )。 + 303 See Other 查看其它 + 304 Not Modified 未修改,使用缓存 + 305 Use Proxy 使用代理 + 306 Switch Proxy 开关代理 + 307 Temporary Redirect 临时重定向 + 308 Permanent Redirect 永久重定向 +``` +### 4xx-ClientError-客户端错误 +``` + 400 Bad Request 错误的请求 + 401 Unauthorized 未授权 + 402 Payment Required 需要付费 + 403 Forbidden 拒绝访问 + 404 Not Found 未找到 + 405 Method Not Allowed 不允许的方法 + 406 Not Acceptable 不可接受 + 407 Proxy Authentication Required 代理服务器需要身份验证 + 408 Request Timeout 请求超时 + 409 Conflict 冲突 + 410 Gone 完成 + 411 Length Required 需要长度 + 412 Precondition Failed 前提条件失败 + 413 Payload Too Large 负载过大 + 414 URI Too Long 太长 + 415 Unsupported Media Type 不支持的媒体类型 + 416 Range Not Satisfiable 的范围不合适 + 417 Expectation Failed 预期失败 + 418 I'm a teapot 我是一个茶壶 + 421 Misdirected Request 误导请求 + 422 Unprocessable Entity 无法处理的实体 + 423 Locked 锁定 + 424 Failed Dependency 失败的依赖 + 426 Upgrade Required 升级所需 + 428 Precondition Required 所需的先决条件 + 429 Too Many Requests 太多的请求 + 431 Request Header Fields Too Large 请求头字段太大 + 451 Unavailable For Legal Reasons 不可出于法律原因 ``` -### 5xx Server Error 服务器错误 -``` -500 Internal Server Error 内部服务器错误 -501 Not Implemented 未执行 -502 Bad Gateway 错误的网关 -503 Service Unavailable 服务不可用 -504 Gateway Timeout 网关超时 -505 HTTP Version Not Supported 不支持HTTP版本 -506 Variant Also Negotiates 变体也进行协商 -507 Insufficient Storage 存储空间不足 -508 Loop Detected 检测到循环 -510 Not Extended 不延长 -511 Network Authentication Required 网络需要身份验证 +### 5xx-ServerError-服务器错误 +``` + 500 Internal Server Error 内部服务器错误 + 501 Not Implemented 未执行 + 502 Bad Gateway 错误的网关 + 503 Service Unavailable 服务不可用 + 504 Gateway Timeout 网关超时 + 505 HTTP Version Not Supported 不支持HTTP版本 + 506 Variant Also Negotiates 变体也进行协商 + 507 Insufficient Storage 存储空间不足 + 508 Loop Detected 检测到循环 + 510 Not Extended 不延长 + 511 Network Authentication Required 网络需要身份验证 ``` ## 非官方扩展码 ``` -103 Checkpoint 检查点 -420 Method Failure (Spring Framework) 故障的方法(Spring框架) -420 Enhance Your Calm (Twitter) 增强您的平静(微博) -450 Blocked by Windows Parental Controls (Microsoft) 被Windows阻止家长控制(微软) -498 Invalid Token (Esri) 无效的令牌(ESRI的) -499 Token Required (Esri) 令牌必需(ESRI的) -499 Request has been forbidden by antivirus 请求已被禁止反病毒 -509 Bandwidth Limit Exceeded (Apache Web Server/cPanel) 超出带宽限制(Apache的Web服务器/的cPanel) -530 Site is frozen 网站被冻结 + 103 Checkpoint 检查点 + 420 Method Failure (Spring Framework) 故障的方法(Spring框架) + 420 Enhance Your Calm (Twitter) 增强您的平静(微博) + 450 Blocked by Windows Parental Controls (Microsoft) 被Windows阻止家长控制(微软) + 498 Invalid Token (Esri) 无效的令牌(ESRI的) + 499 Token Required (Esri) 令牌必需(ESRI的) + 499 Request has been forbidden by antivirus 请求已被禁止反病毒 + 509 Bandwidth Limit Exceeded (Apache Web Server/cPanel) 超出带宽限制(Apache的Web服务器/的cPanel) + 530 Site is frozen 网站被冻结 ``` ## 互联网信息服务扩展状态码 ``` -440 Login Timeout 登录超时 -449 Retry With 重新发送带 -451 Redirect 重定向 + 440 Login Timeout 登录超时 + 449 Retry With 重新发送带 + 451 Redirect 重定向 ``` -## NGINX 扩展状态码 +## NGINX-扩展状态码 ``` -444 No Response 没有响应 -495 SSL Certificate Error 证书错误 -496 SSL Certificate Required证书要求 -497 HTTP Request Sent to HTTPS Port 发送到HTTPS端口请求 -499 Client Closed Request 客户端请求关闭 + 444 No Response 没有响应 + 495 SSL Certificate Error 证书错误 + 496 SSL Certificate Required证书要求 + 497 HTTP Request Sent to HTTPS Port 发送到HTTPS端口请求 + 499 Client Closed Request 客户端请求关闭 ``` -## 七牛扩展状态码 -``` -298 部分操作执行成功。 -419 用户账号被冻结。 -478 镜像回源失败。 主要指镜像源服务器出现异常。 -573 单个资源访问频率过高 -579 上传成功但是回调失败。 包括业务服务器异常;七牛服务器异常;服务器间网络异常。 -599 服务端操作失败。 -608 资源内容被修改。 -612 指定资源不存在或已被删除。 -614 目标资源已存在。 -630 已创建的空间数量达到上限,无法创建新空间。 -631 指定空间不存在。 -640 调用列举资源 (list) 接口时,指定非法的marker参数。 -701 在断点续上传过程中,后续上传接收地址不正确或ctx信息已过期。 +## 七牛云扩展状态码 +``` + 298 部分操作执行成功。 + 419 用户账号被冻结。 + 478 镜像回源失败。 主要指镜像源服务器出现异常。 + 573 单个资源访问频率过高 + 579 上传成功但是回调失败。 包括业务服务器异常;七牛服务器异常;服务器间网络异常。 + 599 服务端操作失败。 + 608 资源内容被修改。 + 612 指定资源不存在或已被删除。 + 614 目标资源已存在。 + 630 已创建的空间数量达到上限,无法创建新空间。 + 631 指定空间不存在。 + 640 调用列举资源 (list) 接口时,指定非法的marker参数。 + 701 在断点续上传过程中,后续上传接收地址不正确或ctx信息已过期。 ``` diff --git a/FrontEnd/SVG.md b/FrontEnd/SVG.md new file mode 100644 index 0000000..bbae6d6 --- /dev/null +++ b/FrontEnd/SVG.md @@ -0,0 +1,19 @@ +--- +title: SVG +date: 2018-12-14 09:24:04 +tags: + - 基础 +categories: + - Web +--- + +💠 + +- 1. [SVG](#svg) + +💠 2024-01-10 17:28:37 +**************************************** +# SVG +> [MDN:SVG ](https://developer.mozilla.org/zh-CN/docs/Web/SVG) + +> 可缩放矢量图形(Scalable Vector Graphics,SVG),是一种用来描述二维矢量图形的 XML 标记语言。 简单地说,SVG 面向图形,HTML 面向文本。 diff --git a/FrontEnd/ViewSolution.md b/FrontEnd/ViewSolution.md index ff5f2e6..fff1c0b 100644 --- a/FrontEnd/ViewSolution.md +++ b/FrontEnd/ViewSolution.md @@ -1,17 +1,36 @@ -`目录 start` - -- [与前端有关的问题](#与前端有关的问题) - - [资源文件](#资源文件) - - [图片](#图片) - - [使用图片还是BASE64](#使用图片还是base64) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: ViewSolution +date: 2018-11-21 10:56:52 +tags: +categories: + - 知识整理 +--- + +💠 + +- 1. [与前端有关的问题](#与前端有关的问题) + - 1.1. [资源文件](#资源文件) + - 1.1.1. [图片](#图片) + - 1.1.1.1. [使用图片还是BASE64](#使用图片还是base64) + - 1.2. [HTML调用exe程序](#html调用exe程序) + +💠 2024-04-24 22:46:48 **************************************** # 与前端有关的问题 ## 资源文件 +> [普通人的网页配色方案](https://www.ruanyifeng.com/blog/2019/03/coloring-scheme.html) + + ### 图片 #### 使用图片还是BASE64 - [知乎提问 前端开发中,使用base64图片的弊端是什么?](https://www.zhihu.com/question/31155574?sort=created) +## HTML调用exe程序 +> [前端网页如何打开一个PC本地应用](https://juejin.im/post/5dc396bbe51d453809085cb4) +> [URL protocol handlers in basic Ubuntu Desktop](https://askubuntu.com/questions/514125/url-protocol-handlers-in-basic-ubuntu-desktop) + +1. 注册表写入自定义协议,自定义协议的逻辑 +1. HTML a 标签地址为自定义协议 +如果是 IE 可以使用 ActiveXObject 方式打开 [How to launch an EXE from Web page (asp.net)](https://stackoverflow.com/questions/916925/how-to-launch-an-exe-from-web-page-asp-net) diff --git a/Functional/Clojure.md b/Functional/Clojure.md index 895a863..c95ebf4 100644 --- a/Functional/Clojure.md +++ b/Functional/Clojure.md @@ -1,17 +1,29 @@ -`目录 start` - -- [Clojure](#clojure) - - [安装](#安装) - - [基础](#基础) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Clojure +date: 2018-11-21 10:56:52 +tags: + - 基础 + - 函数式编程 +categories: + - Clojure +--- + +**目录 start** + +1. [Clojure](#clojure) + 1. [安装](#安装) + 1. [基础](#基础) + +**目录 end**|_2020-12-18 15:50_| **************************************** # Clojure -> 似乎是Lisp的方言 [参考博客 Clojure 学习笔记 :0 零基础教程](https://www.jianshu.com/p/32b7ef3659db) +> Lisp的方言 [参考博客 Clojure 学习笔记 :0 零基础教程](https://www.jianshu.com/p/32b7ef3659db) ## 安装 - [Clojure官网下载地址](https://clojure.org/community/downloads) - 解压后运行jar包进入REPL `java -cp clojure-1.8.0.jar clojure.main` +- Linux 下推荐 [leiningen](https://leiningen.org/) + - Arch `yay -S leiningen` ## 基础 @@ -26,8 +38,10 @@ Clojure的设计原则可以概括成5个词汇:简单、专注、实用、一 - Clojure是将值作为重点,Clojure的值一旦创建就不能修改。变量和值建立映射关系,如果再次赋值,不是修改变量,而是映射到新的值上 - (def <名称> <值>) +************************ + +> Hello World -`Hello World` `java -cp clojure-1.8.0.jar clojure.main`进入REPL终端 ```clojure user => (def hello(fn [] "hello")) diff --git a/Functional/FPBase.md b/Functional/FPBase.md index 7644763..f03f826 100644 --- a/Functional/FPBase.md +++ b/Functional/FPBase.md @@ -1,17 +1,25 @@ -`目录 start` - -- [函数式编程思想](#函数式编程思想) - - [和其他思想的对比](#和其他思想的对比) - - [函数式特性](#函数式特性) +--- +title: 函数式编程基础 +date: 2018-11-21 10:56:52 +tags: + - 函数式编程 +categories: + - 基础 +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [函数式编程思想](#函数式编程思想) + - 1.1. [函数式特性](#函数式特性) + - 1.2. [Lambda表达式](#lambda表达式) + +💠 2024-02-02 14:22:14 **************************************** # 函数式编程思想 > [码农翻身:函数式编程圣经](http://mp.weixin.qq.com/s/0gErQ3tjDLZuD1bYOhi0mQ) +> [解道: 面向函数范式编程](https://www.jdon.com/functional.html) -### 和其他思想的对比 - -- 面向对象的主要限制是不能在现有方法上增加额外的逻辑,函数式就能将方法(函数)作为参数传入进行扩展 +- 面向对象的主要限制是不能在现有方法上增加额外的逻辑,函数式就能将方法(函数)作为参数传入然后再扩展逻辑 - 和AOP的区别:AOP是重型的基于动态代理类去封装扩展原方法 _关于递归_ @@ -25,3 +33,14 @@ _关于递归_ ## 函数式特性 - `map 映射` - `filter 过滤` + +> [函数式编程实现设计模式](https://klose911.github.io/html/fdp/fdp.html)`从某种意义上说,GOF的设计模式是语言表达能力的缺陷` + +## Lambda表达式 +> [Lambda 演算](https://klose911.github.io/html/theory/lambda.html) + +λ 演算 可看做是一个简单的语义清楚的 形式语言 ,用来解释复杂的 程序设计语言 或者 计算模型 +λ 演算通常包含两部分 +- 语法: 合法表达式 (λ表达式)的形式系统 +- 语义: 变换规则 的形式系统 + diff --git a/Functional/Kotlin.md b/Functional/Kotlin.md index aeecf2f..025980d 100644 --- a/Functional/Kotlin.md +++ b/Functional/Kotlin.md @@ -1,17 +1,31 @@ -`目录 start` - -- [Kotlin](#kotlin) - - [教程资源](#教程资源) +--- +title: Kotlin +date: 2018-12-21 10:56:52 +tags: + - 基础 + - 函数式编程 +categories: + - Kotlin +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [Kotlin](#kotlin) + +💠 2024-08-14 15:45:26 **************************************** # Kotlin -> [Kotlin官网](https://kotlinlang.org/) +> [Kotlin官网](https://kotlinlang.org/) > 一个能够跨越多个平台和领域的语言 -## 教程资源 +> [mirai](https://github.com/mamoe/mirai)`Kotlin写的QQ客户端` + +**教程资源** +- [Learn Kotlin by Example](https://play.kotlinlang.org/byExample/overview) - [Kotlin For Android](https://github.com/wangjiegulu/kotlin-for-android-developers-zh)`中文教程` - [EasyKotlin组织](https://github.com/EasyKotlin) - [《Kotlin极简教程》书籍第一章](https://github.com/EasyKotlin/easy_kotlin_chapter_1) +> [Why Kotlin isn't becoming mainstream on server side ](https://www.reddit.com/r/Kotlin/comments/12o03tu/why_kotlin_isnt_becoming_mainstream_on_server_side/) + diff --git a/Game/MC.md b/Game/MC.md new file mode 100644 index 0000000..8abb5a4 --- /dev/null +++ b/Game/MC.md @@ -0,0 +1,22 @@ +--- +title: Minecraft +date: 2020-06-24 16:50:25 +tags: +categories: + - Game +--- + +**目录 start** + +1. [Minecraft](#minecraft) + 1. [Server](#server) + +**目录 end**|_2020-06-24 16:51_| +**************************************** +# Minecraft +> [HMCL: A Minecraft Launcher](https://github.com/huanghongxun/HMCL) + +## Server +> [Minecraft Server](https://github.com/itzg/docker-minecraft-server) + +1. 修改 `/data/server.properties` online-mode=false diff --git a/Go/GoBase.md b/Go/GoBase.md index a6a9fcd..255420b 100644 --- a/Go/GoBase.md +++ b/Go/GoBase.md @@ -1,139 +1,217 @@ -`目录 start` - -- [Go](#go) - - [社区](#社区) - - [书籍](#书籍) - - [安装](#安装) - - [Docker](#docker) - - [环境变量解释](#环境变量解释) - - [基本开发环境搭建](#基本开发环境搭建) - - [数据类型](#数据类型) - - [基本类型](#基本类型) - - [Array](#array) - - [Slice](#slice) - - [Map](#map) - - [Set](#set) - - [基本语法](#基本语法) - - [标准输入输出](#标准输入输出) - - [文件操作](#文件操作) - - [JSON](#json) -- [Tips](#tips) - - [通过字符串调用指定函数](#通过字符串调用指定函数) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Go基础 +date: 2018-12-14 09:25:49 +tags: + - 函数式编程 +categories: + - Go +--- + +💠 + +- 1. [Go](#go) + - 1.1. [Go Modules](#go-modules) + - 1.1.1. [配置](#配置) + - 1.1.2. [go get](#go-get) + - 1.1.3. [go.mod](#gomod) + - 1.1.3.1. [单个Git仓库发布多个包](#单个git仓库发布多个包) + - 1.1.4. [go.work](#gowork) + - 1.1.5. [现存问题](#现存问题) + - 1.1.6. [模板项目初始化](#模板项目初始化) + - 1.2. [数据类型](#数据类型) + - 1.2.1. [string](#string) + - 1.2.2. [int](#int) + - 1.2.3. [Array](#array) + - 1.2.4. [Slice](#slice) + - 1.2.5. [Map](#map) + - 1.2.6. [Set](#set) + - 1.3. [基本语法](#基本语法) + - 1.3.1. [标准输入输出](#标准输入输出) + - 1.3.2. [时间处理](#时间处理) + - 1.4. [泛型](#泛型) + - 1.4.1. [丑陋设计](#丑陋设计) + - 1.5. [函数](#函数) + - 1.5.1. [参数](#参数) + - 1.5.2. [返回值](#返回值) + - 1.5.3. [defer](#defer) + - 1.6. [接口](#接口) + - 1.7. [Channel](#channel) + - 1.8. [协程](#协程) + - 1.9. [序列化](#序列化) + - 1.9.1. [JSON](#json) +- 2. [应用](#应用) + - 2.1. [文件操作](#文件操作) + - 2.2. [http](#http) + - 2.3. [Test](#test) + - 2.4. [Debug](#debug) + - 2.4.1. [pprof](#pprof) + - 2.5. [部署](#部署) + - 2.5.1. [静态编译](#静态编译) +- 3. [常用库](#常用库) +- 4. [Tips](#tips) + - 4.1. [通过字符串调用指定函数](#通过字符串调用指定函数) + +💠 2024-09-24 15:38:50 **************************************** # Go -> [官网](https://golang.org) | [镜像网](https://golang.google.cn/) | [Github Repo](https://github.com/golang/go) | [Go Doc](https://godoc.org/) -Go 语言被设计成一门应用于搭载 Web 服务器,存储集群或类似用途的巨型中央服务器的系统编程语言。对于高性能分布式系统领域而言,Go 语言无疑比大多数其它语言有着更高的开发效率。它提供了海量并行的支持,这对于游戏服务端的开发而言是再好不过了。 +> [官网](https://golang.org) | [镜像官网](https://golang.google.cn/) | [Github Repo](https://github.com/golang/go) | [Go Doc](https://godoc.org/) +> [Rethinking Visual Programming with Go](https://divan.dev/posts/visual_programming_go/) +> [Goplus](https://github.com/qiniu/goplus) -- [Go语言资料收集](https://github.com/wonderfo/wonderfogo/wiki) -- [学习Go的知乎话题](https://www.zhihu.com/question/23486344) -- [Go相关书籍的知乎话题](https://www.zhihu.com/question/30461290) -- [Go1.0的吐槽](http://blog.csdn.net/liigo/article/details/23699459) -- [Java 20年:转角遇到Go](http://www.infoq.com/cn/news/2015/05/java20-from-language-to-platform) -> [参考博客: Golang官网被墙解决办法](https://golangtc.com/t/504072ca320b5276e2000004) -## 社区 -- [GoCN Forum](https://gocn.vip/) -- [Go语言中文网](https://studygolang.com) +************************ -- [Go Programming & Concurrency in Practice](https://github.com/hyper0x/goc2p) +`Go 项目结构规范` +> [project-layout](https://github.com/golang-standards/project-layout) +> [go-dev: layout](https://go.dev/doc/modules/layout) +> [Go 语言实战](https://github.com/llitfkitfk/go-best-practice) -## 书籍 +************************ +> [Rethinking Visual Programming with Go](https://divan.dev/posts/visual_programming_go/) -> [Go语言高级编程(Advanced Go Programming)](https://books.studygolang.com/advanced-go-programming-book/index.html) +## Go Modules +> 自 1.11 开始支持 [Wiki](https://github.com/golang/go/wiki/Modules) +### 配置 -## 安装 -> [下载](https://golang.google.cn/dl/)|[官方教程](https://golang.google.cn/doc/install) | [参考 教程](http://www.runoob.com/go/go-environment.html) | [_](http://cloud.kuangcp.top/go-1.10.3.tar.gz) +- `go env -w GOSUMDB=off` 关闭官方 sum 校验服务 +> 配置国内源 -1. sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz -2. *shrc或者 /etc/profile 中添加 ```sh -export GOROOT=/usr/local/go -export GOPATH=$HOME/Code/go # workspace -export GOBIN=$GOPATH/bin # 'go install' command install dir -export PATH=$PATH:$GOBIN:$GOPATH:$GOROOT/bin +export GO111MODULE=on +export GOPROXY=https://mirrors.aliyun.com/goproxy/ +export GOSUMDB=sum.golang.google.cn ``` -> **查看版本** `go version`正常输出go的版本则是配置成功 -3. 在 /home/kcp/code/go 下 新建 test.go -```go -package main -import "fmt" -func main() { - fmt.Printf("hello, world\n") -} + +> [wiki Modules](https://github.com/golang/go/wiki/Modules) +> [参考: Go模块简明教程](https://github.com/wuyumin/tutorial/blob/master/zh-cn/Modules/README.md) + +************************ + +1. `go mod init moduleName` 按名字初始化模块 + + 1. *注意*,如果想通过 `go get URL`方式进行安装,就必须使用代码托管的完整地址, 不需要就可以简化包名 + + - 例如 `module github.com/{username}/{repo}/path/to` +2. `go mod edit -replace github.com/kuangcp/gobase/cuibase=./../cuibase` + + - go.mod文件会新增: `replace github.com/kuangcp/gobase/cuibase => ./../cuibase` + - 多模块开发时,使用本地开发的模块取代发布的版本 + +- fork 别人项目后开发,可用来替换成自己的模块 `replace gihub.com/aaa/bbb => gihub.com/ccc/bbb` + +1. go clean -modcache + +| | | +| :------------ | :-------------------------- | +| go mod graph | 列出模块依赖(包含依赖传递) | +| go mod tidy | 删除错误或者不使用的modules | +| go mod vendor | 生成vendor目录 | +| go mod verify | 验证依赖是否正确 | +| go mod why | 查找某个依赖项被引入的路径 | + +### go get + +| | | +| :------------------------------------- | :---------------------------------- | +| go get golang.org/x/text@latest | 拉取最新的版本(优先择取 tag) | +| go get golang.org/x/text@master | 拉取 master 分支的最新 commit | +| go get golang.org/x/text@v0.3.2 | 拉取 指定 tag | +| go get golang.org/x/text@342b2e | 拉取 指定 commit | +| go get github.com/smartwalle/alipay/v3 | 拉取v3版本 `设计最坑` | +| | | +| go get -u | 更新 mod | +| go list -m -versions golang.org/x/text | 列出可安装版本 | +| go get -insecure | 不对依赖进行verify 常用于内网的依赖 | + +### go.mod + +> 关键字 + +- module 指定包的名字(路径) +- require 指定依赖项模块 +- replace 替换依赖项模块 +- exclude 忽略依赖项模块 + +注意依赖项后 有 // indirect 标记的意味着是传递依赖项 + +当有依赖包更换了路径后,可以此方式统一更换: `gofmt -w -r '"github.com/dgrijalva/jwt-go" -> "github.com/golang-jwt/jwt"' .` + +#### 单个Git仓库发布多个包 + +- go mod init github.com/username/repo-name/{path} +- git tag -a {path}/v1.0.0 + +例如 + +```sh + go mod init github.com/username/repo-name/pkg/app/util + git tag -a pkg/app/util/v1.0.0 ``` -4. go run test.go 或者 go build - -### Docker -> 使用Docker安装和部署 - -> [Docker image](https://hub.docker.com/_/golang/) `这里的镜像都是用于 从源码编译构建成可执行文件的 环境` -> [go 的 Docker镜像的讨论](https://gocn.vip/question/153) - -1. 实际运行的时候, 如果不需要调用外部Linux命令 就直接 `from scratch` -1. 需要则 `from alpine` 更精简一点 更好是使用 `frolvlad/alpine-glibc` - -## 环境变量解释 -> [ 关于GOROOT、GOPATH、GOBIN、project目录](https://blog.csdn.net/Alsmile/article/details/48290223) -> [GOPATH 深度解析 ](https://studygolang.com/articles/3493) - -- Go 开发环境依赖于一些操作系统环境变量,你最好在安装 Go 之间就已经设置好他们。如果你使用的是 Windows 的话,你完全不用进行手动设置,Go 将被默认安装在目录 c:/go 下。这里列举几个最为重要的环境变量: - - `$GOROOT` 表示 Go 在你的电脑上的安装位置,它的值一般都是 $HOME/go,当然,你也可以安装在别的地方。 - - `$GOARCH` 表示目标机器的处理器架构,它的值可以是 386、amd64 或 arm。 - - `$GOOS` 表示目标机器的操作系统,它的值可以是 darwin、freebsd、linux 或 windows。 - - `$GOBIN` 表示编译器和链接器的安装位置,默认是 `$GOROOT/bin`,如果你使用的是 Go 1.0.3 及以后的版本,一般情况下你可以将它的值设置为空,Go 将会使用前面提到的默认值。 - - 为了区分本地机器和目标机器,你可以使用 `$GOHOSTOS` 和 `$GOHOSTARCH` 设置目标机器的参数,这两个变量只有在进行交叉编译的时候才会用到, - - 如果你不进行显示设置,他们的值会和本地机器(`$GOOS` 和 `$GOARCH`)一样。 - - `$GOPATH` 默认采用和 `$GOROOT` 一样的值,但从 Go 1.1 版本开始,你必须修改为其它路径。它可以包含多个包含 Go 语言源码文件、包文件和可执行文件的路径, - - 而这些路径下又必须分别包含三个规定的目录:src、pkg 和 bin,这三个目录分别用于存放源码文件、包文件和可执行文件。 - - `$GOARM` 专门针对基于 arm 架构的处理器,它的值可以是 5 或 6,默认为 6。 - - `$GOMAXPROCS` 用于设置应用程序可使用的处理器个数与核数,详见第 14.1.3 节。 - -## 基本开发环境搭建 -> [Github:Golang](https://github.com/golang) - -入门时使用VSCode是比较方便的, VSCode 会推荐我们安装如下工具 -1. tools _工具集_ - 1. guru `golang.org/x/tools/cmd/guru` - 1. gorename `golang.org/x/tools/cmd/gorename` -1. lint `golang.org/x/lint` - 1. golint `golang.org/x/lint/golint` -1. go-outline `github.com/ramya-rao-a/go-outline` -1. go-symbols `github.com/acroca/go-symbols` -1. goreturns `github.com/sqs/goreturns` - -- [ ] godep 同样的方式 - -- 安装完后就会在GOPATH中能找到这些工具对应的命令了, 由于 golang.org 被墙 - - 所以 只有这几个工具不能直接 go get : guru gorename imports(goreturns要用到) lint golint - -https://github.com/golang/tools 是 tools 的Github地址, -https://github.com/golang/lint 是 lint 和 golint 的Github地址, - - -1. `mkdir -p src/golang.org/x/tools` -1. `mkdir -p src/golang.org/x/lint` -1. 将 https://github.com/golang/tools clone所有内容 放到 src/golang.org/x/tools 下 -1. 将 https://github.com/golang/lint clone所有内容 放到 src/golang.org/x/lint 下 -1. 此时再执行 go get 那五个工具即可全部安装成功 - -问题又来了, Github 由于飘忽不定的被墙, 网速特别慢, 就可以利用码云来加速下载 -1. lint https://gitee.com/gin9/golang-lint.git -1. tools https://gitee.com/gin9/golang-tools.git - -********************************* + +### go.work + +关键字和go.mod一致, 并追加了use关键字 + +use指定使用的模块目录,可以使用go work use添加模块,也可以手动修改go.work工作区添加新的模块,在工作区中添加了模块路径,编译时会自动使用use中的本地代码进行编译 +replaces替换依赖仓库地址,replaces命令与go.mod指令相同,用于替换项目中依赖的仓库地址,需要注意的是,replaces和use不能同时指定相同的本地代码路径 + +通常情况下 go.work不提交到git上, 可以让每个开发人员使用不同的构建规则. + +但是以下场景, 如果要实现 demo-gui模块 依赖 demo/util模块 下的 api.go, 有两种方式: + +- demo/ + - util/ + - api.go + - demo-gui/ + - main.go + - go.mod + - go.work + - go.mod + +1. `replace 方式`: demo-gui 中的 go.mod 显示依赖一个假版本 然后replace到本地目录 + ```go + require demo v1.0.0 + replace demo v1.0.0 => ../ + ``` +2. `go.work 方式`: 依赖父目录即可. 如果提交该文件到git, 会让依赖管理更简单 `看起来`, 但是这样就和go.work设计相违背了 + ```go + use ( + . + ../../demo + ) + ``` + +### 现存问题 + +- [ ] 待思考: 如何像Java一样管理多模块的大项目 +- [ ] 当需要从Github上fork一个包并修改了内容及API后,想给自己其他项目依赖时, 就必须要修改这个包的 go.mod 里的 module 为自己的url路径,否则就无法被使用 + - 这里会带来一个问题,无法直接pr回原项目 要倒腾下 go.mod + +### 模板项目初始化 + +> [gonew](https://pkg.go.dev/golang.org/x/tools/cmd/gonew) + +> [go-zero](https://github.com/zeromicro/go-zero) + +************************ + ## 数据类型 -_有关类型后置_ + +_类型后置的设计相关文章_ + > [螺旋形(C/C++)和顺序(Go)的声明语法](https://cxwangyi.wordpress.com/2011/03/14/%E8%9E%BA%E6%97%8B%E5%BD%A2%EF%BC%88cc%EF%BC%89%E5%92%8C%E9%A1%BA%E5%BA%8F%EF%BC%88go%EF%BC%89%E7%9A%84%E5%A3%B0%E6%98%8E%E8%AF%AD%E6%B3%95/) -> [Why do a lot of programming languages put the type *after* the variable name?](https://stackoverflow.com/questions/1712274/why-do-a-lot-of-programming-languages-put-the-type-after-the-variable-name) +> [Why do a lot of programming languages put the type after the variable name?](https://stackoverflow.com/questions/1712274/why-do-a-lot-of-programming-languages-put-the-type-after-the-variable-name) + +### string + +strings 包 提供了常用字符串API + +### int -### 基本类型 -#### int -#### int64 +> int8 int16 int32 int64 int(位数按操作系统字长而定 32/64) ```go // string到int @@ -147,34 +225,242 @@ _有关类型后置_ ``` ### Array + ### Slice + ### Map + +```go + // 判断 key 存在 + _, ok := dataMap["key"] +``` + ### Set -****************** + +> 官方没有提供set类型 可使用社区提供的库 [golang-set](https://github.com/deckarep/golang-set) + +************************ + ## 基本语法 ### 标准输入输出 -> [参考博客: golang中的格式化输入输出](https://blog.csdn.net/xiaoyida11/article/details/51554022) + +- 输出 fmt.Print* +- 输入 fmt.Scan* +- 打印结构体 `fmt.Printf("%v\n", object)` + +### 时间处理 + +> [Go: Format a time or date](https://programming.guide/go/format-parse-string-time-date-example.html) + +记住这个神奇的时间 `2006-01-02 03:04:05` Go 中不是寻常的 YYYY-mm-dd 这种格式 + +************************ + +## 泛型 +> 自1.18 开始支持 + +> [Github: Lightweight anonymous function syntax](https://github.com/golang/go/issues/21498) `讨论可简写的Lambda表达式,类似Js` + +> 类型约束 +```golang +type Integer interface{ + int | int64 +} +``` + +### 丑陋设计 +> [Crimes with Go Generics](https://xeiaso.net/blog/gonads-2022-04-24/) + +> 不支持成员方法泛型,只支持结构体附加泛型或函数泛型。 +- [no-parameterized-methods](https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md#no-parameterized-methods) | [proposal: spec: allow type parameters in methods](https://github.com/golang/go/issues/49085) +- go是编译型泛型,在编译器期确定所有的类型,跟go的反射冲突,想要解决只能像C#一样运行时支持泛型,或者像java用类型擦除,这个目前来看基本不可能 +- 导致了 map reduce 的库简洁的实现比较困难. [Github: go stream](https://github.com/Kuangcp/GoBase/tree/master/pkg/ctool/stream)`个人实现` + +> 泛型类型的值不能为nil +- 导致了零值具有歧义 + ```golang + func a[T any]() T{ + // 编译报错 + return nil + // new(T) 也编译报错 + + // 只能通过编译器来设置零值。 + var zero T + return zero + } + ``` + - [Golang 1.18 泛型:零值判断](https://blog.csdn.net/K346K346/article/details/130148416) + +************************ ## 函数 -基本结构 + ```go // 函数名 (参数 ) 返回值{函数体} -func functionName (param1 int) int { +func functionName (param int) int { } ``` -> 函数作为参数 `functionName func(string, string)` - ### 参数 + +> 函数作为参数传入函数 `func doAny(functionName func(string, string)){}` + ### 返回值 +> 可以多返回值 元组 + +### defer + +> 类似于 Java 中的 finally 语句 例如 `defer openFile.Close()` + +一个函数中可以定义多个 defer 语句, 执行的顺序是按定义次序的逆序, 也就是栈的概念 + +常见需要回收的是http请求 `defer http.Response.Body.Close()` 如果不Close会同时影响客户端和服务端资源泄漏 + +************************ + +## 接口 -*************** +> [参考:接口的定义和使用](http://www.cnblogs.com/yjf512/archive/2012/06/09/2543628.html) + +************************ + +## Channel + +> [参考 如何优雅地关闭Go channel](https://www.jianshu.com/p/d24dfbb33781) +> [Go Channel 详解 ](https://colobu.com/2016/04/14/Golang-Channels/) + +************************ + +## 协程 +> [Concurrency is not parallelism](https://go.dev/blog/waza-talk) + +> [刘丹冰Aceld 的博客 ](https://learnku.com/blog/Aceld) +> [Golang 调度器 GMP 原理与调度全分析 ](https://learnku.com/articles/41728)`《深入理解 Go 语言》` + +![](./img/gmp.drawio.svg) +- 全局队列(Global Queue):存放等待运行的 G。 +- P 的本地队列:同全局队列类似,存放的也是等待运行的 G,存的数量有限,不超过 256 个。 + - 新建 G’时,G’优先加入到 P 的本地队列,如果队列满了,则会把本地队列中一半的 G 移动到全局队列。 +- P 列表:所有的 P 都在程序启动时创建,并保存在数组中,最多有 GOMAXPROCS(可配置 例如[automaxprocs](https://github.com/uber-go/automaxprocs)) 个。 +- M:线程想运行任务就得获取 P,从 P 的本地队列获取 G,P 队列为空时,M 也会尝试从全局队列拿一批 G 放到 P 的本地队列,或从其他 P 的本地队列偷一半放到自己 P 的本地队列。 + - M 运行 G,G 执行之后,M 会从 P 获取下一个 G,不断重复下去。 + - go 程序启动时,会设置 M 的最大数量,默认 10000. `runtime/debug 中的 SetMaxThreads`,设置 M 的最大数量 + - 当一个 M 阻塞了,就会创建新的 M。 + +************************ + +*调度器的设计策略* +> Goroutine 调度器和 OS 调度器是通过 M 结合起来的,每个 M 都代表了 1 个内核线程,OS 调度器负责把内核线程分配到 CPU 的核上执行。 + +- **复用线程**:避免频繁的创建、销毁线程,而是对线程的复用。 + 1. work stealing 机制:​ 当本线程无可运行的 G 时,尝试从其他线程绑定的 P 偷取 G,而不是销毁线程, 类似于Java的Fork/Join的工作窃取。 + 1. hand off 机制: ​ 当本线程M因为 G 进行系统调用阻塞时,线程M释放绑定的 P,把 P 转移给其他空闲的线程M执行。 +- **并行**:GOMAXPROCS 设置 P 的数量,最多有 GOMAXPROCS 个线程分布在多个 CPU 上同时运行。GOMAXPROCS 也限制了并发的程度 + - 比如 GOMAXPROCS = 核数/2,则最多利用了一半的 CPU 核进行并行。 +- **抢占**:在 coroutine 中要等待一个协程主动让出 CPU 才执行下一个协程,在 Go 中,一个 goroutine 最多占用 CPU 10ms,防止其他 goroutine 被饿死,这就是 goroutine 不同于 coroutine 的一个地方。 +- **全局 G 队列**:在新的调度器中依然有全局 G 队列,但功能已经被弱化了,当 M 执行 work stealing 从其他 P 偷不到 G 时,它可以从全局 G 队列获取 G。 + +************************ + +> [协程究竟比线程能省多少开销?](https://zhuanlan.zhihu.com/p/80037638) + +线程的切换频率,基本取决于线程的数量,使用协程,需要指定每个线程的任务,同样的任务量,协程需要的线程数量应该始终高于自动分配的线程池。 因而: +- 使用线程 = 线程切换开销(小) +- 使用协程 = 线程切换开销(大)+ 协程切换开销 + +然后CPU开销: +- 线程的指令周期 = 中断检测 + 指令执行(包括取指、转换和执行) +- 协程的指令周期 = 中断检测 + 指令执行 + 中断检测 + 协程信号检测 + +所以:性能上,io多路复用 + 线程池是完全碾压协程的;协程胜在使用方便 + +> 思考如何实现:可中断的调度任务,池式限流(限制最大活跃任务数) + +************************ + +> [Go语言的跨协程异常处理](https://taoshu.in/go/goroutine-panic.html) + +************************ + +## 序列化 +> [Go json反序列化“null“结果为nil踩坑](https://blog.csdn.net/qq_39618369/article/details/125761089) + +### JSON + +> `结构体必须是大写字母开头的成员才会被处理(大写字母开头才有对外权限)` + +> [参考: Go操作JSON](https://blog.csdn.net/u011304970/article/details/70769949) +> [参考: go and json](https://eager.io/blog/go-and-json/) +> [参考: 在Go语言中使用JSON](https://blog.csdn.net/tiaotiaoyly/article/details/38942311) + +> [website: json to go struct](https://mholt.github.io/json-to-go/) + +```go + type GridConfig struct { + ID int `json:"id"` + Row int `json:"row"` + Col int `json:"col"` + Data []int `json:"data"` + } + +// 第一种 +func (*GenerateGrid) ReadConfig() []GridConfig { + var datas []GridConfig + fp, _ := os.Open("grid.json") + dec := json.NewDecoder(fp) + for { + err := dec.Decode(&datas) + if err != nil { + fmt.Println(err) + break + } + //use v + // fmt.Printf("%+v", datas) + for _, line := range datas { + fmt.Println(" ", line) + } + } + + // 第二种方式 + var datas []GridConfig + raw, err := ioutil.ReadFile("./grid.json") + // fmt.Println(raw) + if err != nil { + fmt.Println(err.Error()) + os.Exit(1) + } + err = json.Unmarshal(raw, &datas) + if err != nil { + fmt.Println("error:", err) + + } + for _, line := range datas { + fmt.Println(" ", line) + } + + return datas +} + +``` + +> 忽略空字段 +1. 字段是指针类型 且注明 omitempty + +```go +Msg struct{ + Text *Content `json:"text,omitempty"` +} +``` +************************ + +# 应用 ## 文件操作 **递归读取当前目录的文件** + ```go package main import ( @@ -193,14 +479,97 @@ func walkfunc(path string, info os.FileInfo, err error) error { } ``` -## JSON -> [参考博客: Go操作JSON](https://blog.csdn.net/u011304970/article/details/70769949) -> [参考博客: go and json](https://eager.io/blog/go-and-json/) -> [参考博客: 在Go语言中使用JSON](https://blog.csdn.net/tiaotiaoyly/article/details/38942311) `结构体必须是大写字母开头的成员才会被JSON处理到,小写字母开头的成员不会有影响。` +## http +> [优化 golang net/http client 客户端存在的性能瓶颈](https://xiaorui.cc/archives/5577)`http.Client 中 Transport对于连接池使用的锁太多` -> [website: json to go struct](https://mholt.github.io/json-to-go/) +> [req](https://github.com/imroc/req) + +************************ + +## Test + +> [Github: assert](https://godoc.org/github.com/stretchr/testify/assert) + +************************ + +## Debug + +### pprof +> [Google: pprof](https://github.com/google/pprof/blob/main/doc/README.md) + +```go + import _ "net/http/pprof" + go func() { + // 访问 http://ip:8899/debug/pprof/ + http.ListenAndServe("0.0.0.0:8899", nil) + }() +``` + +> [参考: 【实践】使用Go pprof做内存性能分析](https://cloud.tencent.com/developer/article/1489186) +> [参考: 实战Go内存泄露](https://www.codercto.com/a/79118.html) +> [参考: Go 程序内存泄露问题快速定位](https://zhuanlan.zhihu.com/p/368567370) + +> 分析内存 +- go tool pprof -alloc_space/-inuse_space http://ip:8899/debug/pprof/heap 后进入REPL 输入top查看内存占用 +- go tool pprof -inuse_space -cum -svg http://ip:8899/debug/pprof/heap > heap_inuse.svg 导出成svg图 +- go tool pprof -http=:7778 http://localhost:8899/debug/pprof/heap + +> 分析CPU + +[Flame Graphs for Go With pprof](https://www.benburwell.com/posts/flame-graphs-for-go-with-pprof/) CPU火焰图 + +自动方式 **推荐** +- `go tool pprof -raw 'http://localhost:8080/debug/pprof/profile?seconds=20'` 得到采样文件 *.pb.gz +- `go tool pprof -http=: 采样文件` http可指定端口 例如 :2345 +- 访问 web 地址 菜单 View 下的 Flame Graph + +手工方式 +- 先 Clone https://github.com/brendangregg/FlameGraph +- `go tool pprof -raw -output=cpu.txt 'http://localhost:8080/debug/pprof/profile?seconds=20'` +- `./stackcollapse-go.pl cpu.txt | flamegraph.pl > flame.svg` + +************************ + +## 部署 + +### 静态编译 +> [Go 静态编译机制 ](https://juejin.cn/post/7053450610386468894) + +- CGO_ENABLED=0 go build +- go build -ldflags '-linkmode "external" -extldflags "-static"' + +************************ + +> 打包的二进制文件在alpine中无法运行 + +报错: `/bin/sh: ./appName: not found` +方案: CGO_ENABLED=0 go build + +> 打开文件数超出限制或者tcp连接未及时关闭 + +报错: [cannot assign requested address](https://github.com/golang/go/issues/16012) +方案: `ulimit -n 10000 && ./app` + + +************************ +# 常用库 +- [groupcache](https://github.com/golang/groupcache) 分布式cache +- [zlsgo](https://github.com/sohaha/zlsgo)`Web应用脚手架` + + +************************ # Tips + +> [lorca](https://github.com/zserge/lorca.git) `H5 + chromium + Golang`桌面端 + +> interface{} 类型判断nil +- vo == nil || (reflect.ValueOf(vo).Kind() == reflect.Ptr && reflect.ValueOf(vo).IsNil()) + +> 字符串计算差异 +> [diffmatchpatch](https://github.com/sergi/go-diff/diffmatchpatch) + ## 通过字符串调用指定函数 -> [参考博客: Go 根据字符串调用指定函数](https://blog.csdn.net/HOOKTTG/article/details/52184500) -> [参考博客: WebAssembly 和 Go语言:对未来的观望](http://www.techug.com/post/webassembly-and-go-a-look-to-the-future.html) + +> [参考: Go 根据字符串调用指定函数](https://blog.csdn.net/HOOKTTG/article/details/52184500) +> [参考: WebAssembly 和 Go语言:对未来的观望](http://www.techug.com/post/webassembly-and-go-a-look-to-the-future.html) diff --git a/Go/GoDatabase.md b/Go/GoDatabase.md index e763a99..75962ee 100644 --- a/Go/GoDatabase.md +++ b/Go/GoDatabase.md @@ -1,13 +1,44 @@ -`目录 start` - -- [Go的数据库操作](#go的数据库操作) - - [Redis](#redis) +--- +title: Go操作数据库 +date: 2018-12-15 12:14:19 +tags: +categories: + - Go +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [Go的数据库操作](#go的数据库操作) + - 1.1. [Redis](#redis) + - 1.2. [MySQL](#mysql) + - 1.2.1. [gorm](#gorm) + - 1.2.2. [xorm](#xorm) + - 1.3. [LevelDB](#leveldb) + +💠 2024-02-02 14:22:14 **************************************** # Go的数据库操作 ## Redis -> [github:go-redis](https://github.com/go-redis/redis) +> [Github: go-redis](https://github.com/go-redis/redis) +> [Github: radix](https://github.com/mediocregopher/radix) + +************************ + +## MySQL +### gorm +### xorm + +************************ + +## LevelDB +> [Github: goleveldb](https://github.com/syndtr/goleveldb) + + +************************ + +> 搜索 +gofound +zincsearch \ No newline at end of file diff --git a/Go/GoRestful.md b/Go/GoRestful.md index 416c922..772b044 100644 --- a/Go/GoRestful.md +++ b/Go/GoRestful.md @@ -1,16 +1,42 @@ -`目录 start` - -- [Go Restful](#go-restful) +--- +title: GoRestful +date: 2018-12-14 09:26:23 +tags: + - Web + - Restful +categories: + - Go +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [Go-Restful](#go-restful) + 1. [Gin](#gin) + 1. [Iris](#iris) + +**目录 end**|_2020-07-05 14:58_| **************************************** -# Go Restful -> Go 的 Restful web框架 +# Go-microservices +> Go 的 web框架 `microservices` -> [参考博客: Building a REST Service with Golang - Part 2 (Basic Webserver)](https://stevenwhite.com/building-a-rest-service-with-golang-2/) +> [参考: Building a REST Service with Golang - Part 2 (Basic Webserver)](https://stevenwhite.com/building-a-rest-service-with-golang-2/) -> [参考博客: Making a RESTful JSON API in Go](https://thenewstack.io/make-a-restful-json-api-go/) +> [参考: Making a RESTful JSON API in Go](https://thenewstack.io/make-a-restful-json-api-go/) [Github:go-restful](https://github.com/emicklei/go-restful) +## Gin +> [Github](https://github.com/gin-gonic/gin) + +> [gin-example](https://github.com/EDDYCJY/go-gin-example) +> [demo: mybook](https://github.com/Kuangcp/GoBase/tree/master/mybook) + +## Iris +> [iris](https://github.com/kataras/iris) + +## go-kit +[go-kit](https://github.com/go-kit/kit) + +## go-zero +[doc](https://go-zero.dev/docs/concepts/overview) diff --git a/Go/Map/GoMap.md b/Go/Map/GoMap.md new file mode 100644 index 0000000..36442f8 --- /dev/null +++ b/Go/Map/GoMap.md @@ -0,0 +1,6 @@ +# Golang map + +> [参考: Java HashMap和Go map源码对比](https://juejin.im/post/5c020b145188250e8601f2a2) + +## sync.map +适合读多写少 diff --git a/Go/Readme.md b/Go/Readme.md new file mode 100644 index 0000000..3b28cb9 --- /dev/null +++ b/Go/Readme.md @@ -0,0 +1,202 @@ +--- +title: Go学习概览 +date: 2020-03-22 12:40:12 +tags: +categories: +--- + +💠 + +- 1. [Golang](#golang) + - 1.1. [社区](#社区) + - 1.2. [教程](#教程) + - 1.3. [书籍](#书籍) + - 1.4. [实践项目](#实践项目) +- 2. [安装](#安装) + - 2.1. [Linux 下安装](#linux-下安装) + - 2.1.1. [Hello World](#hello-world) + - 2.2. [Docker](#docker) +- 3. [环境变量解释](#环境变量解释) +- 4. [开发环境搭建 基于 VSCode](#开发环境搭建-基于-vscode) +- 5. [生态](#生态) + +💠 2024-03-20 17:27:12 +**************************************** + +# Golang + +> [go dev](https://learn.go.dev/) | [awesome-go](https://github.com/avelino/awesome-go) | [awesome-go-cn](https://github.com/yinggaozhen/awesome-go-cn) + +- [学习Go的知乎话题](https://www.zhihu.com/question/23486344) +- [Java 20年:转角遇到Go](http://www.infoq.com/cn/news/2015/05/java20-from-language-to-platform) + +> [Golang vs Rust vs Dlang 哪个更有前途](https://www.zhihu.com/question/27226962) `大致就是go偏向Java Python, Rust 偏向 C++ 的领域` +> [从 0 到 1 学习 Go 语言 ](https://mp.weixin.qq.com/s?__biz=MjM5NzM0MjcyMQ==&mid=2650087380&idx=1&sn=56c77443ae171e1091e146704798647a) + +- [go-gtk alternatives](https://go.libhunt.com/go-gtk-alternatives) `gui框架对比` + + +> 负面 + +- [Go1.0的吐槽](http://blog.csdn.net/liigo/article/details/23699459) +- [参考: 我为什么放弃Go语言](https://blog.csdn.net/liigo/article/details/23699459) +- [对 Go 语言的综合评价](http://www.yinwang.org/blog-cn/2014/04/18/golang) `设计角度:泛型,接口和排序的问题!!` + +工程角度: 包管理,错误处理 + +## 社区 + +- [GoCN Forum](https://gocn.vip/) +- [Go语言中文网](https://studygolang.com) + +## 教程 + +- [Go Programming & Concurrency in Practice](https://github.com/hyper0x/goc2p) +- [golang教程](http://c.biancheng.net/golang/) +- [build web application with golang](https://www.gitbook.com/book/astaxie/build-web-application-with-golang) +- [Go 语言学习资料与社区索引](https://github.com/Unknwon/go-study-index) +- [Go语言资料收集](https://github.com/wonderfo/wonderfogo/wiki) +- [go](http://www.runoob.com/go/go-tutorial.html) +- [在线编译执行 学习go](http://www.vaikan.com/go/a-tour-of-go/#1) `在线编辑运行` +- [go101](https://github.com/go101/go101) +- [build-web-application-with-golang](https://github.com/astaxie/build-web-application-with-golang) +- [golang-design-pattern](https://github.com/senghoo/golang-design-pattern) + +## 书籍 + +> [Go相关书籍的知乎话题](https://www.zhihu.com/question/30461290) +> [Go语言高级编程(Advanced Go Programming)](https://chai2010.cn/advanced-go-programming-book/index.html) + +- [The Go Programming Language](http://www.gopl.io/) +- [Go 语言设计与实现](https://draveness.me/golang/docs) +- [Go 语言原本](https://golang.design/under-the-hood/) + +## 实践项目 +- [PolarDB-ClusterManager](https://github.com/ApsaraDB/PolarDB-ClusterManager) +- Docker +- Kubernetes +- [Vitess](https://github.com/vitessio/vitess)`horizontal scaling of MySQL` + +************************ + +# 安装 + +## Linux 下安装 + +> [Official Download](https://golang.google.cn/dl/) | [Official Doc](https://golang.google.cn/doc/install) + +1. `sudo tar -C /usr/local -xzf go1.10.3.linux-amd64.tar.gz` 安装和升级都是如此 + - 注意: 升级前必须 `rm -rf /usr/local/go` 以免造成文件的混乱 +2. `*shrc` 或者 `/etc/profile` 中添加 + ```sh + export GOROOT=/usr/local/go + export GOPATH=$HOME/Code/go # workspace + export GOBIN=$GOPATH/bin # 'go install' command install dir + export PATH=$PATH:$GOBIN:$GOPATH:$GOROOT/bin + ``` +3. **查看版本** `go version`正常输出go的版本则是配置成功 + +### Hello World + +1. 在 `$HOME/Code/go` 下 新建 test.go + ```go + package main + import "fmt" + func main() { + fmt.Println("hello, world") + } + ``` +2. go run test.go 或者 go build 然后执行可执行文件 + +## Docker + +> [Docker image](https://hub.docker.com/_/golang/) +> [go 的 Docker镜像的讨论](https://gocn.vip/question/153) + +1. 部署运行的时候, 如果不需要调用外部Linux命令 就直接使用空镜像 `from scratch` 但是缺点是排查问题时无工具可用 +2. 需要外部命令则 `from alpine` 更精简一点 更好是使用 `frolvlad/alpine-glibc` + + + +# 环境变量解释 + +> [ +> 关于GOROOT、GOPATH、GOBIN、project目录](https://blog.csdn.net/Alsmile/article/details/48290223) +> [GOPATH 深度解析 ](https://studygolang.com/articles/3493) + +- Go 开发环境依赖于一些 `操作系统环境变量`,最好在安装 Go 之间就已经设置好他们。如果是 Windows系统 Go 将被默认安装在目录 c:/go 下。这里列举几个最为重要的环境变量: + - `$GOROOT` 表示 Go 在你的电脑上的安装位置,它的值一般都是 $HOME/go,当然,你也可以安装在别的地方。 + - `$GOARCH` 表示目标机器的处理器架构,它的值可以是 386、amd64 或 arm。 + - `$GOOS` 表示目标机器的操作系统,它的值可以是 darwin、freebsd、linux 或 windows。 + - `$GOBIN` 表示编译器和链接器的安装位置,默认是 `$GOROOT/bin`,如果你使用的是 Go 1.0.3 及以后的版本,一般情况下你可以将它的值设置为空,Go 将会使用前面提到的默认值。 + - 为了区分本地机器和目标机器,你可以使用 `$GOHOSTOS` 和 `$GOHOSTARCH` 设置目标机器的参数,这两个变量只有在进行交叉编译的时候才会用到, + - 如果你不进行显示设置,他们的值会和本地机器(`$GOOS` 和 `$GOARCH`)一样。 + - `$GOPATH` 默认采用和 `$GOROOT` 一样的值,但从 Go 1.1 版本开始,你必须修改为其它路径。它可以包含多个包含 Go 语言源码文件、包文件和可执行文件的路径, + - 而这些路径下又必须分别包含三个规定的目录:src、pkg 和 bin,这三个目录分别用于存放源码文件、包文件和可执行文件。 + - `$GOARM` 专门针对基于 arm 架构的处理器,它的值可以是 5 或 6,默认为 6。 + - `$GOMAXPROCS` 用于设置应用程序可使用的处理器个数与核数,详见第 14.1.3 节。 + - `$GOPROXY` 设置 mod 的代理. 例如: **GOPROXY=https://mirrors.aliyun.com/goproxy/** + + + +# 开发环境搭建 基于 VSCode + +> [Github:Golang](https://github.com/golang) | [基础学习项目](https://github.com/Kuangcp/GoBase) + +入门时使用 VSCode 是比较方便的, VSCode 会推荐我们安装如下工具 + +1. tools _工具集_ + 1. guru `golang.org/x/tools/cmd/guru` + 2. gorename `golang.org/x/tools/cmd/gorename` +2. lint `golang.org/x/lint` + 1. golint `golang.org/x/lint/golint` +3. go-outline `github.com/ramya-rao-a/go-outline` +4. go-symbols `github.com/acroca/go-symbols` +5. goreturns `github.com/sqs/goreturns` +6. dep `github.com/golang/dep` + +由于 golang.org 用的是Google的服务器, 所以..., 这几个工具不能直接安装:guru gorename imports(goreturns要用到) lint golint +但是本质上都是获取源码进行安装, 所以也可以从github获取并安装 + +**汇总一下命令** + +```sh + # 必须在 GOPATH 下执行 + cd $GOPATH + mkdir -p src/golang.org/x/tools + git clone --depth 1 https://github.com/golang/tools src/golang.org/x/tools + + mkdir -p src/golang.org/x/lint + git clone --depth 1 https://github.com/golang/lint src/golang.org/x/lint + + go get golang.org/x/tools/cmd/guru + go get golang.org/x/tools/cmd/gorename + go get golang.org/x/tools/cmd/goimports + + go get golang.org/x/lint + go get golang.org/x/lint/golint +``` + +```sh + # 可在任意目录执行 + go get github.com/ramya-rao-a/go-outline + go get github.com/acroca/go-symbols + + ## 仅为了提速 + mkdir -p src/github.com/golang/dep + git clone --depth 1 https://github.com/golang/dep src/github.com/golang/dep + go get github.com/golang/dep + + go get github.com/sqs/goreturns + go get github.com/rogpeppe/godef + go get github.com/uudashr/gopkgs/cmd/gopkgs + go get github.com/go-delve/delve/cmd/dlv + go get github.com/mdempsky/gocode +``` + +************************ + +# 生态 + +[cron](https://github.com/robfig/cron) +gorm xorm diff --git a/Go/img/gmp.drawio.svg b/Go/img/gmp.drawio.svg new file mode 100644 index 0000000..3b1acc9 --- /dev/null +++ b/Go/img/gmp.drawio.svg @@ -0,0 +1,587 @@ + + + + + + + + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + +
+
+
+ 操作系统 调度器 +
+
+
+
+ + 操作系统 调度器 + +
+
+ + + + +
+
+
+ CPU +
+
+
+
+ + CPU + +
+
+ + + + +
+
+
+ CPU +
+
+
+
+ + CPU + +
+
+ + + + +
+
+
+ CPU +
+
+
+
+ + CPU + +
+
+ + + + +
+
+
+ CPU +
+
+
+
+ + CPU + +
+
+ + + + + +
+
+
+ M +
+
+
+
+ + M + +
+
+ + + + + +
+
+
+ M +
+
+
+
+ + M + +
+
+ + + + + +
+
+
+ M +
+
+
+
+ + M + +
+
+ + + + + +
+
+
+ M +
+
+
+
+ + M + +
+
+ + + + +
+
+
+ M 即 内核线程 +
+
+
+
+ + M 即 内核线程 + +
+
+ + + + +
+
+
+ 全局队列 +
+
+
+
+ + 全局队列 + +
+
+ + + + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + +
+
+
+ P +
+
+
+
+ + P + +
+
+ + + + +
+
+
+ P +
+
+
+
+ + P + +
+
+ + + + +
+
+
+ P +
+
+
+
+ + P + +
+
+ + + + +
+
+
+ Gorountine 调度器 +
+
+
+
+ + Gorountine 调度器 + +
+
+ + + + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + +
+
+
+ P的本地队列 +
+
+
+
+ + P的本地队列 + +
+
+ + + + +
+
+
+ P的本地队列 +
+
+
+
+ + P的本地队列 + +
+
+ + + + +
+
+
+ P的本地队列 +
+
+
+
+ + P的本地队列 + +
+
+ + + + + + + +
+
+
+ GOMAXPROCS 个 +
+
+
+
+ + GOMAXPROCS 个 + +
+
+ + + + + + + + + + +
+
+
+ 出队 +
+
+
+
+ + 出队 + +
+
+ + + + + + +
+
+
+ G +
+
+
+
+ + G + +
+
+ + + + +
+
+
+ 工作窃取 +
+
+
+
+ + 工作窃取 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/Groovy/Groovy.md b/Groovy/Groovy.md index a04fcea..a46fcfd 100644 --- a/Groovy/Groovy.md +++ b/Groovy/Groovy.md @@ -1,38 +1,47 @@ -`目录 start` - -- [Groovy](#groovy) - - [书籍](#书籍) - - [语言特性](#语言特性) - - [安装配置](#安装配置) - - [在IDEA中](#在idea中) - - [Maven引入Groovy](#maven引入groovy) - - [Docker](#docker) - - [Groovy基础](#groovy基础) - - [Groovy特性](#groovy特性) - - [默认导入](#默认导入) - - [隐式return](#隐式return) - - [默认生成setter getter](#默认生成setter-getter) - - [数字处理](#数字处理) - - [变量,动态和静态类型,作用域](#变量动态和静态类型作用域) - - [列表和映射语法](#列表和映射语法) - - [动态调用函数](#动态调用函数) - - [函数](#函数) - - [闭包](#闭包) - - [测试](#测试) - - [调用系统命令行](#调用系统命令行) - - [强大的注解](#强大的注解) - - [与Java的差异](#与java的差异) - - [Java不具备的Groovy特性](#java不具备的groovy特性) - - [Groovy和Java的交互](#groovy和java的交互) - - [Maven中引入Groovy](#maven中引入groovy) - - [Groovy调用Java](#groovy调用java) - - [Java调用Groovy](#java调用groovy) - - [Groovy和Spring](#groovy和spring) - - [坑](#坑) - - [默认return](#默认return) - - [Grails](#grails) - -`目录 end` |_2018-09-01_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Groovy +date: 2018-12-15 12:13:52 +tags: + - 基础 +categories: + - Groovy +--- + +**目录 start** + +1. [Groovy](#groovy) + 1. [书籍](#书籍) + 1. [语言特性](#语言特性) + 1. [安装配置](#安装配置) + 1. [在IDEA中](#在idea中) + 1. [Maven引入Groovy](#maven引入groovy) + 1. [Docker](#docker) + 1. [Groovy基础](#groovy基础) + 1. [Groovy特性](#groovy特性) + 1. [默认导入](#默认导入) + 1. [隐式return](#隐式return) + 1. [默认生成setter getter](#默认生成setter-getter) + 1. [数字处理](#数字处理) + 1. [变量,动态和静态类型,作用域](#变量动态和静态类型作用域) + 1. [列表和映射语法](#列表和映射语法) + 1. [动态调用函数](#动态调用函数) + 1. [函数](#函数) + 1. [闭包](#闭包) + 1. [测试](#测试) + 1. [调用系统命令行](#调用系统命令行) + 1. [强大的注解](#强大的注解) + 1. [与Java的差异](#与java的差异) + 1. [Java不具备的Groovy特性](#java不具备的groovy特性) + 1. [Groovy和Java的交互](#groovy和java的交互) + 1. [Maven中引入Groovy](#maven中引入groovy) + 1. [Groovy调用Java](#groovy调用java) + 1. [Java调用Groovy](#java调用groovy) + 1. [Groovy和Spring](#groovy和spring) + 1. [坑](#坑) + 1. [默认return](#默认return) + 1. [Grails](#grails) + +**目录 end**|_2020-12-08 19:23_| **************************************** # Groovy > [Groovy 官网](http://www.groovy-lang.org/) | @@ -162,7 +171,7 @@ ``` ### 函数 -> [参考博客: Groovy进阶之函数、闭包和类](https://www.tuicool.com/articles/iEBJnqF) +> [参考: Groovy进阶之函数、闭包和类](https://www.tuicool.com/articles/iEBJnqF) ### 闭包 ```groovy @@ -174,7 +183,7 @@ ``` ### 测试 -[参考博客: 用 Groovy 更迅速地对 Java 代码进行单元测试](https://www.ibm.com/developerworks/cn/java/j-pg11094/) +[参考: 用 Groovy 更迅速地对 Java 代码进行单元测试](https://www.ibm.com/developerworks/cn/java/j-pg11094/) ************************* ### 调用系统命令行 @@ -189,7 +198,7 @@ > 和Java不同的是, Groovy提供具有功能性的注解, Java大多是接口规范性注解 @ToString 自动实现toString方法, 但是字符串有点冗余 -> [参考博客: Groovy中那些神奇注解之ToString](http://www.cnblogs.com/varlxj/p/5181788.html) +> [参考: Groovy中那些神奇注解之ToString](http://www.cnblogs.com/varlxj/p/5181788.html) _日志相关_ 只需要引入对应的依赖, 就和lombok一样的使用 ```java @@ -364,15 +373,14 @@ class Person{ > [示例代码](https://github.com/kuangcp/JavaBase/blob/master/src/main/java/com/classfile/JavaUseGroovy.java) ### Groovy和Spring -> [参考博客: Groovy 使 Spring 更出色,第 1 部分](https://www.ibm.com/developerworks/cn/java/j-groovierspring1.html) +> [参考: Groovy 使 Spring 更出色,第 1 部分](https://www.ibm.com/developerworks/cn/java/j-groovierspring1.html) ************************************** ## 坑 > 因为是动态的, 所以, 当Java或Groovy类更改了一些接口, 属性名, 调用方那里不会报错, 直到运行才报错, 而且 eclipse idea 都不报错, 只是会把错误的属性和调用变成带有下划线的灰色... ### 默认return -> 只要在方法最后一行放入表达式, 就会自动return, 这就导致了Groovy不会对方法进行检查, 逻辑复杂时如果少了一个return, 不会报编译错误, 方法直接返回null. -> 这个坑, 硬是Debug了近一个小时, debug功力要提升了 +> 只要在方法最后一行放入表达式, 就会自动return, 这就导致了Groovy不会对方法返回值进行检查, 逻辑复杂时如果少了一个return, 不会报编译错误, 方法直接返回null. *********************** ## Grails diff --git a/Groovy/GroovySpring.md b/Groovy/GroovySpring.md deleted file mode 100644 index e158ab5..0000000 --- a/Groovy/GroovySpring.md +++ /dev/null @@ -1,9 +0,0 @@ -`目录 start` - -- [Groovy Spring](#groovy-spring) - -`目录 end` |_2018-08-21_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Groovy Spring - -> [Groovy 使 Spring 更出色,第 2 部分 在运行时改变应用程序的行为](https://www.ibm.com/developerworks/cn/java/j-groovierspring2.html) diff --git a/Hardware/BIOS.md b/Hardware/BIOS.md new file mode 100644 index 0000000..d10be88 --- /dev/null +++ b/Hardware/BIOS.md @@ -0,0 +1,22 @@ +--- +title: BIOS +date: 2020-05-17 16:51:49 +tags: +categories: +--- + +**目录 start** + +1. [BIOS](#bios) + 1. [UEFI](#uefi) + +**目录 end**|_2020-05-17 16:51_| +**************************************** +# BIOS +> Basic Input Output System + +主板上的ROM软件,电脑启动时最先加载运行 ,各主板厂商提供的都有所不同但是大同小异 通常个人会用到的就是 引导方式和菜单,加锁,虚拟化 等功能 + +## UEFI +> [BIOS and UEFI](https://wiki.manjaro.org/index.php?title=BIOS_and_UEFI) + diff --git a/Hardware/Disk.md b/Hardware/Disk.md new file mode 100644 index 0000000..215c9ea --- /dev/null +++ b/Hardware/Disk.md @@ -0,0 +1,7 @@ +# Disk + +## SSD +SLC MLC TLC QLC PLC ... 单个存储单元能存储的位数 Single Multiple ... + +左至右 性能更低,容量更大,可靠性更低 + diff --git a/Hardware/Interface.md b/Hardware/Interface.md new file mode 100644 index 0000000..56bd7a5 --- /dev/null +++ b/Hardware/Interface.md @@ -0,0 +1,34 @@ +--- +title: 硬件接口 +date: 2019-12-10 13:32:10 +tags: +categories: + - 硬件 +--- + +**目录 start** + +1. [接口](#接口) + 1. [显示器接口](#显示器接口) + 1. [VGA](#vga) + 1. [DVI](#dvi) + 1. [HDMI](#hdmi) + 1. [DP DisplayPort](#dp-displayport) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# 接口 + +## 显示器接口 +> [HDMI vs DisplayPort vs DVI vs VGA](https://gadgetsenthusiast.com/hdmi-vs-displayport-vs-dvi-vs-vga/) + +### VGA + +### DVI + +### HDMI +- 按行传输图像可传输音频 如需 HDR需扩展线材 更适用于 电视 + +### DP DisplayPort +- 按自定义包传输 扩展性高(HDR ) 可传输音频 更适用于PC + diff --git a/Java/AdvancedLearning/Annotation.md b/Java/AdvancedLearning/Annotation.md deleted file mode 100644 index 2c76984..0000000 --- a/Java/AdvancedLearning/Annotation.md +++ /dev/null @@ -1,41 +0,0 @@ -`目录 start` - -- [注解](#注解) - - [读取](#读取) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 注解 - -> 参考博客 [全面解析Java注解](http://blog.csdn.net/chenxiang0207/article/details/8193980) | [Java注解(2)-运行时框架](http://blog.csdn.net/duo2005duo/article/details/50511476) - -> 参考项目 [AnnotationDemo](https://github.com/zhuifengshen/AnnotationDemo) - -注解定义包含四个元注解,分别为@Target,@Retention,@Documented,@Inherited。各元注解的作用如下: -- ① @Target - - 表示该注解用于什么地方,可能的 ElemenetType 参数包括: - - ElemenetType.CONSTRUCTOR 构造器声明。 - - ElemenetType.FIELD 域声明(包括 enum 实例)。 - - ElemenetType.LOCAL_VARIABLE 局部变量声明。 - - ElemenetType.METHOD 方法声明。 - - ElemenetType.PACKAGE 包声明。 - - ElemenetType.PARAMETER 参数声明。 - - ElemenetType.TYPE 类,接口(包括注解类型)或enum声明。 - -- ② @Retention - - 表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括: - - RetentionPolicy.SOURCE 注解将被编译器丢弃。 - - RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃。 - - RetentionPolicy.RUNTIME VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息。 -> 举一个例子,如@Override里面的Retention设为SOURCE,编译成功了就不要这一些检查的信息,相反,@Deprecated里面的Retention设为RUNTIME,表示除了在编译时会警告我们使用了哪个被Deprecated的方法,在执行的时候也可以查出该方法是否被Deprecated。 - -- ③ @Documented - - 将此注解包含在 javadoc 中 -- ④ @Inherited - - 允许子类继承父类中的注解 - -## 读取 -- 判断是否有指定注解类型的注解 - - 在类上的注解就是得到类对象, 然后判断 isAnnotationPresent(ExcelConfig.class) - - 在方法上的注解就是得到所有方法, 属性同理 -- [相关代码片段](https://gitee.com/kcp1104/codes/s148mbplxo06qgn25d3wc23) diff --git a/Java/AdvancedLearning/Basic/StringConcat.md b/Java/AdvancedLearning/Basic/StringConcat.md new file mode 100644 index 0000000..51535b5 --- /dev/null +++ b/Java/AdvancedLearning/Basic/StringConcat.md @@ -0,0 +1,30 @@ +--- +title: StringConcat +date: 2023-10-03 20:28:47 +tags: +categories: +--- + +💠 + +1. [字符串拼接](#字符串拼接) + 1. [StringBuffer 和 StringBuilder](#stringbuffer-和-stringbuilder) + +💠 2023-10-03 20:28 +**************************************** +# 字符串拼接 + +1. 当有少量连接操作时,直接使用 `+` 。 + - 因为如果都是字面量,编译器会直接连接,如果包含变量,编译器会自动替换为 StringBuilder append 方式 +1. 当`单线程`下有大量连接操作时,使用 StringBuilder +1. 当`多线程`下有大量连接操作时,使用 StringBuffer + +## StringBuffer 和 StringBuilder +> [参考博客](https://blog.csdn.net/rmn190/article/details/1492013) + +StringBuffer 是线程安全的, StringBuilder 不是 + +- 为何在拼接时, StringBuider 会比直接使用String更好(在循环体中) + - 因为用 String 会产生大量常量, StringBuilder StringBuffer 都是使用的字符数组来存储内容, 追加仅仅是扩容字符数组(实现在抽象类 AbstractStringBuilder 中) + - StringBuilder StringBuffer 都是继承于它, Buffer 和 Builder 区别仅仅是 append 方法上加了 synchronized 关键字 + diff --git a/Java/AdvancedLearning/Cache/Caffeine.md b/Java/AdvancedLearning/Cache/Caffeine.md new file mode 100644 index 0000000..99f6860 --- /dev/null +++ b/Java/AdvancedLearning/Cache/Caffeine.md @@ -0,0 +1,104 @@ +--- +title: Caffeine +date: 2019-05-13 11:15:40 +tags: +categories: +--- + +💠 + +- 1. [Caffeine](#caffeine) + - 1.1. [使用](#使用) + - 1.1.1. [SpringBoot集成](#springboot集成) + - 1.2. [设计](#设计) + - 1.2.1. [缓存类型](#缓存类型) + - 1.2.2. [驱逐策略](#驱逐策略) + - 1.2.3. [持久化](#持久化) + - 1.2.4. [统计](#统计) + +💠 2024-09-20 11:10:09 +**************************************** +# Caffeine +> [Github](https://github.com/ben-manes/caffeine) + +> [本地缓存无冕之王Caffeine Cache ](https://mp.weixin.qq.com/s?__biz=Mzg4Nzc3NjkzOA==&mid=2247486885&idx=1&sn=37c7a9461402bd97822295cf51361777&chksm=cf847e60f8f3f776eb3b477decfbac55dc8b7ae1cf607ef68fbee89dbe02d40a800a92fabec7#rd) + +## 使用 + +> [Introduction to Caffeine](https://www.baeldung.com/java-caching-caffeine) + +### SpringBoot集成 +配置文件方式 +```yml +spring: + cache: + type: caffeine + cache-names: + - userCache + caffeine: + spec: maximumSize=1024,refreshAfterWrite=60s +``` + +Bean方式 +```java + @Bean + public CacheManager cacheManager() { + CaffeineCacheManager cacheManager = new CaffeineCacheManager(); + cacheManager.setCaffeine(caffeineCacheBuilder()); + return cacheManager; + } + + Caffeine caffeineCacheBuilder() { + return Caffeine.newBuilder() + .initialCapacity(100) + .maximumSize(500) + .expireAfterAccess(10, TimeUnit.MINUTES) + .recordStats(); + } +``` + +```java + Cache flowNameCache = Caffeine.newBuilder() + .initialCapacity(100) + .maximumSize(1000) + .expireAfterAccess(10, TimeUnit.MINUTES) + .recordStats().build(); +``` + +************************ + +## 设计 +> [基于 W-TinyLFU 缓存淘汰算法](/Skills/Cache/Cache.md#缓存淘汰算法) +> [Caffeine Efficiency](https://github.com/ben-manes/caffeine/wiki/Efficiency) + +### 缓存类型 +- Cache 单纯实现缓存 写入和读取 + - 在获取缓存值时如果想要原子性实现 不存在就创建 则调用 `get(key, key -> value)` 方法,并发调用时后续的线程会阻塞等待,如果不想感知此类阻塞就调用`getIfPresent()`即刻返回 + - 可以据此实现缓存过期时一个线程去加载缓存,其他线程等待值的常见场景。 +- Loading Cache + - 缓存不存在时,调用get时将会触发调用指定的Loader逻辑去加载缓存`类似于CDN中的回源操作`。并发调用get时阻塞后续线程 + - 可以据此实现多级缓存。Loader逻辑实现去DB等源去获取数据再写入缓存 +- Async Cache + - 是Cache的一个变体,其响应结果均为CompletableFuture + - 默认情况下,缓存计算使用`ForkJoinPool.commonPool()`作为线程池,如果想要指定线程池,则可以覆盖并实现Caffeine.executor(Executor)方法。 + - 如果缓存的数据是`纯CPU计算`得到的,推荐默认的FJ线程池,如果是需要通过网络IO获取的数据,`建议使用独立的IO线程池` + - 并发调用 `get(key, k -> value)`时,会返回 **同一个CompletableFuture对象** + - 由于返回结果本身不进行阻塞,可以根据业务设计自行选择阻塞等待或者非阻塞。 +- Async Loading Cache + - Loading Cache和Async Cache的功能组合。 + - Async Loading Cache支持以异步的方式,对缓存进行自动加载。线程池设置同上 + +### 驱逐策略 +- 基于容量回收 `W-TinyLFU` +- 基于时间回收 +- 基于引用回收 + +### 持久化 + + +### 统计 +Caffeine内置了数据收集功能,通过Caffeine.recordStats()方法,可以打开数据收集。这样Cache.stats()方法将会返回当前缓存的一些统计指标,例如: +- hitRate:查询缓存的命中率。 +- evictionCount:被驱逐的缓存数量。 +- averageLoadPenalty:新值被载入的平均耗时 + diff --git a/Java/AdvancedLearning/Cache/EhCache.md b/Java/AdvancedLearning/Cache/EhCache.md new file mode 100644 index 0000000..21982d3 --- /dev/null +++ b/Java/AdvancedLearning/Cache/EhCache.md @@ -0,0 +1,30 @@ +--- +title: EhCache +date: 2024-05-04 22:06:19 +tags: +categories: +--- + +💠 + +- 1. [EhCache](#ehcache) + +💠 2024-05-04 23:18:27 +**************************************** +# EhCache +[Ehcache](https://www.ehcache.org/)被Hibernate选中的默认缓存实现框架 + +特性: +- 多级缓存,相较于GuavaCache Caffeine纯内存框架,Ehcache支持内存,堆外内存,磁盘 + - 堆外内存是为了`规避GC扫描成本`,但是相较于堆内存可以通过引用直接读取值,堆外内存则需要`序列化反序列化`来读写 +- 支持集群 +- 支持持久化 +- 支持分布式缓存 + - RMI 组播 + - JMS 消息 + - JGroups + - Terracotta +- 支持 `JSR107标准`以及使用非常广泛的`Spring Cache标准` + +> [JAVA中使用最广泛的本地缓存?Ehcache的自信从何而来](https://juejin.cn/post/7167259989826863112) + diff --git a/Java/AdvancedLearning/Cache/GuavaCache.md b/Java/AdvancedLearning/Cache/GuavaCache.md new file mode 100644 index 0000000..20ec93a --- /dev/null +++ b/Java/AdvancedLearning/Cache/GuavaCache.md @@ -0,0 +1,16 @@ +--- +title: GuavaCache +date: 2019-05-13 11:15:40 +tags: +categories: +--- + +💠 + +- 1. [GuavaCache](#guavacache) + +💠 2024-02-04 15:34:54 +**************************************** +# GuavaCache +> [Guava Cache](https://www.baeldung.com/guava-cache) + diff --git a/Java/AdvancedLearning/Cache/Readme.md b/Java/AdvancedLearning/Cache/Readme.md new file mode 100644 index 0000000..1d05607 --- /dev/null +++ b/Java/AdvancedLearning/Cache/Readme.md @@ -0,0 +1,8 @@ +# Java相关缓存框架 + +> [缓存系列专栏](https://github.com/veezean/JavaBasicSkills?tab=readme-ov-file#%E7%BC%93%E5%AD%98%E7%B3%BB%E5%88%97%E4%B8%93%E6%A0%8F) + + +如果只是本地简单、少量缓存数据使用的,选择Caffeine; +如果本地缓存数据量较大、内存不足需要使用磁盘缓存的,选择EhCache; +如果是大型分布式多节点系统,业务对缓存使用较为重度,且各个节点需要依赖并频繁操作同一个缓存,选择Redis。 diff --git a/Java/AdvancedLearning/ClassFile.md b/Java/AdvancedLearning/ClassFile.md deleted file mode 100644 index ef815ef..0000000 --- a/Java/AdvancedLearning/ClassFile.md +++ /dev/null @@ -1,337 +0,0 @@ -`目录 start` - -- [Java基础](#java基础) - - [【类和字节码】](#类和字节码) - - [类加载和类对象](#类加载和类对象) - - [类加载器](#类加载器) - - [加载和连接](#加载和连接) - - [Class对象](#class对象) - - [类加载器](#类加载器) - - [方法句柄](#方法句柄) - - [检查类文件](#检查类文件) - - [javap](#javap) - - [常量池](#常量池) - - [字节码](#字节码) - - [运行时环境](#运行时环境) - - [操作码介绍](#操作码介绍) - - [加载和存储操作码](#加载和存储操作码) - - [数学运算操作码](#数学运算操作码) - - [执行控制操作码](#执行控制操作码) - - [调用操作码](#调用操作码) - - [平台操作码](#平台操作码) - - [操作码的快捷形式](#操作码的快捷形式) - - [invokedynamic](#invokedynamic) - - [序列化](#序列化) - - [serialVersionUID](#serialversionuid) - - [其他业内主流编解码框架](#其他业内主流编解码框架) - - [MessagePack](#messagepack) - - [Protobuf](#protobuf) - - [proto文件定义](#proto文件定义) - - [Linux上安装](#linux上安装) - - [通过Docker使用](#通过docker使用) - - [对于Java的使用](#对于java的使用) - - [Thrift](#thrift) - - [Marshalling](#marshalling) - -`目录 end` |_2018-08-13_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Java基础 -## 【类和字节码】 -> [个人相关代码](https://github.com/Kuangcp/JavaBase/tree/master/java-classfile/src/main/java/com/github/kuangcp) - -### 类加载和类对象 -- 一个`.class`文件定义了JVM中的类型,包括了域,方法,继承信息,注解和其他元数据 - -#### 类加载器 -- [ ] TODO 学习类加载器 -> [类装载器、双亲委托模型、命名空间、安全性](https://blog.csdn.net/yuan22003/article/details/6839335\) -> [java ClassLoader类解析-双亲委托机制](https://blog.csdn.net/wangyang1354/article/details/49448007) - -#### 加载和连接 -![图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Java7Developer/p107.jpg) - -[参考博客: 第七章.虚拟机类加载机制](http://ifeve.com/%e7%ac%ac%e4%b8%83%e7%ab%a0-%e8%99%9a%e6%8b%9f%e6%9c%ba%e7%b1%bb%e5%8a%a0%e8%bd%bd%e6%9c%ba%e5%88%b6/) -`加载` -- 这个过程就是读取字节码文件,创建一个字节数组装在这些内容,加载结束后这个对象还不能直接调用 - -`连接` -- 加载完成后,类必须连接起来,分为三步:验证,准备,解析。 - - 验证: - - 验证文件的合理性,完整性检查,检查常量池,方法的字节码检查。主要的: - - 是否所有方法都遵守访问控制关键字的限定 - - 方法调用的参数个数和静态类型是否正确 - - 确保字节码不会试图滥用堆栈 - - 确保变量使用之前被正确初始化了 - - 检查变量是否仅被赋予恰当类型的值 - - 准备: - - 分配内存,准备初始化类中的静态变量,但不会现在就初始化,也不会执行任何VM字节码 - - 解析: - - 促使VM检查类文件中所引用的类型是不是都是已知的类型。如果有运行时有未知的类型,那又要引发一次类加载过程 - - 当需要加载的类全部加载解析完毕后,VM就可以初始化最初那个加载的类了。 - - 这时所有的静态变量都可以进行初始化,所有静态代码块都会运行,这一步完成后,类就能使用了 - -#### Class对象 -- 加载和连接过程的最终结果是一个Class对象,Class对象可以和反射API一起实现对方法,域构造方法等类成员的间接访问 - -> 所以一个类的定义就会有一个Class对象, 但是这个对象的类型呢?怎么判断, Class对象的类型就是他的值么? - -#### 类加载器 -![图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Java7Developer/p110.jpg) - -- Java平台经典类加载器: - - 根(引导)加载器: 通常在VM启动后不久就实例化,作用是加载系统的基础JAR(主要是rt.jar),并且不做验证工作 - - 扩展类加载器: 加载安装时自带的标准扩展,一般包括安全性扩展 - - 应用或系统类加载器: 应用最广泛的类加载器,负责加载应用类,在大多SE环境中主要工作是由他完成 - - 定制类载器: 为了企业框架定制的加载器 - -***** -### 方法句柄 -> 主要用于反射 用到再学 - -![图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Java7Developer/p118.jpg) - -****** -### 检查类文件 -#### javap -> JDK内置命令, 用来探视类文件内部和反编译class文件 - -**** -### 常量池 -> 常量池是为类文件中的其他常量元素提供快捷访问方式的区域。对于JVM来说常量池相当于符号表 -> [参考博客](http://www.cnblogs.com/LeonNew/p/5314731.html) - -- `javap -v class文件` 输出很多额外信息,# 开头的就是常量池信息 -![图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Java7Developer/p120.jpg) - -***** -### 字节码 - -- 字节码是程序的中间表达形式,源码和机器码之间的产物 -- 字节码是由源文件执行javac产生的 -- 某些高级语言特性(语法糖)在字节码中给去掉了,例如循环结构,会转换成为分支指令 -- 每个操作都由一个字节表示,因此叫做字节码 -- 字节码是一种抽象表示方法 -- 字节码进一步编译得到机器码 - -- `javap -c -p class文件` 反编译字节码文件,-p 能看到私有属性 - - 输出所有的属性以及类的定义信息 - - 静态块 - - 构造方法 - - 方法信息 - - 静态属性信息 - - 静态方法信息 - -#### 运行时环境 -> 因为JVM没有CPU那样的寄存器,所以是采用的堆栈来计算的,称为操作数栈或者计算堆栈 - -- 当一个类被链接进运行时环境时,字节码会受到检查,其中很多验证都可以归结为对栈中类型模式的分析 -- 方法需要一块内存区域作为计算堆栈来计算新值,另外每个运行的线程都需要一个调用堆栈来记录当前正在执行的方法,这两个栈会有交互 - -#### 操作码介绍 -- 字节码由操作码 opcode 序列构成,每个指令后可能会带参数,操作码希望看到栈处于指定状态中,然后他对栈进行操作处理,把参数移走,放入结果 -- 操作码表有四列: - - 名称:操作码类型的通用名称 - - 参数:操作码的参数,以i开头的是用来作为常量池或局部变量中的查询索引的几个字节,如果有更多的参数,将会合并 - - 如果参数出现在括号里,就表明不是所有形式的操作码都会使用他 - - 堆栈布局:他展示了栈在操作码执行前后的状态。括号中的元素表示是可选的 - - 描述:描述操作码的用处 - - -[ ] 下面的内容需要继续阅读Java7程序员修炼之道 -#### 加载和存储操作码 -#### 数学运算操作码 -#### 执行控制操作码 -#### 调用操作码 -#### 平台操作码 -#### 操作码的快捷形式 - -#### invokedynamic -> 这个特性是针对 框架开发和非Java语言准备的 - -**************** -## 序列化 -> [码农翻身:序列化: 一个老家伙的咸鱼翻身](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513589&idx=1&sn=d402d623d9121453f1e570395c7f99d7&chksm=80d67a36b7a1f32054d4c779dd26e8f97a075cf4d9ed1281f16d09f1df50a29319cd37520377&scene=21#wechat_redirect) `对象转化为二进制流` - -### serialVersionUID -> 简单的说就是类的版本控制, 标明类序列化时的版本, 版本一致表明这两个类定义一致 -> 在进行反序列化时, JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException) -[参考博客](http://swiftlet.net/archives/1268) - -- serialVersionUID有两种显示的生成方式: - - 一个是默认的1L - - 一个是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段 - -> 当你一个类实现了Serializable接口,如果没有定义serialVersionUID,Eclipse会提供这个提示功能告诉你去定义 。 -在Eclipse中点击类中warning的图标一下,Eclipse就会自动给定两种生成的方式。 -如果不想定义它,在Eclipse的设置中也可以把它关掉的,设置如下: -Window ==> Preferences ==> Java ==> Compiler ==> Error/Warnings ==>Potential programming problems -将Serializable class without serialVersionUID的warning改成ignore即可。 - -****************************** - -### 其他业内主流编解码框架 -> 因为Java序列化的性能和存储开销都表现不好,而且不能跨语言, 所以一般不使用Java的序列化而是使用以下流行的库 - -#### MessagePack -> [Github:msgpack](https://github.com/msgpack) | [参考博客: MessagePack:一种高效二进制序列化格式](http://hao.jobbole.com/messagepack/) - -#### Protobuf -> Google开源的库 全称 `Google Protocol Buffers` | [Github : Protobuf](https://github.com/google/protobuf) - -> [参考博客: Protobuf语言指南](http://www.cnblogs.com/dkblog/archive/2012/03/27/2419010.html) `较为详细, 只是版本有点旧` -> [参考博客: 详解如何在NodeJS中使用Google的Protobuf](https://juejin.im/entry/59c1214df265da0658151a2c) | [protocobuf](https://github.com/dcodeIO/protobuf.js) -> [Google 开源技术protobuf ](https://blog.csdn.net/hguisu/article/details/20721109) -> [Google Protocol Buffer 的使用和原理](https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html) - -- 他将数据结构以 proto后缀的文件进行描述, 通过代码生成工具, 可以生成对应数据结构的POJO对象和Protobuf相关的方法和属性 - - 特点: - - 结构化数据存储格式: XML JSON等 - - 高效的编解码性能 - - 语言无关, 平台无关, 扩展性好 - - 官方支持 Java C++ Python三种语言, 并且Js的支持也比较好[](https://github.com/dcodeIO/ProtoBuf.js/) - - 数据描述文件和代码生成机制优点: - - 文本化的数据结构描述语言, 可以实现语言和平台无关, 特别适合异构系统间的集成 - - 通过标识字段的顺序, 可以实现协议的前向兼容 _在不同版本的数据结构进程间进行数据传递_ - - 自动代码生成, 不需要手工编写同样数据结构的C++和Java版本; - - 方便后续的管理和维护,相比于代码, 结构化的文档更容易管理和维护 -- 习惯性规则: - - 命名: `packageName.MessageName.proto` - -> 只是编解码的工具, 不支持读半包, 粘包拆包 - -##### proto文件定义 -> [参考博客: 数据交换利器 Protobuf 技术浅析](http://blog.jobbole.com/107405/) - -``` -// 用户数据信息 -message Article { - required int32 articleId = 1; // 文章id - optinal string articleExcerpt = 4; // 文章摘要 - repeated string articlePicture = 5; // 文章附图 -} -``` -> 上面定义了一个消息, 消息具有三个属性, 且行末的注释都会变成Javadoc注释 - -1. message 是消息定义的关键字 -2. required 表示这个字段是必需的, 必须在序列化的时候被赋值。 -3. optional 代表这个字段是可选的,可以为0个或1个但不能大于1个。 -4. repeated 则代表此字段可以被重复任意多次包括0次。 -5. int32和string是字段的类型。后面是我们定义的字段名。 -6. 最后的1,2,3则是代表每个字段的一个唯一的编号标签,在同一个消息里不可以重复。这些编号标签用与在消息二进制格式中标识你的字段,并且消息一旦定义就不能更改。 - - 需要说明的是标签在1到15范围的采用一个字节进行编码。所以通常将标签1到15用于频繁发生的消息字段。编号标签大小的范围是1 到 2的29次幂–1。 - - 此外不能使用protobuf系统预留的编号标签(19000 -19999)。 - -![数据类型对应表](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Learn/java/protobuf/protobuf-type.jpeg) - -_复杂类型_ -> 定义了enum枚举类型,嵌套的消息。甚至对原有的消息进行了扩展,也可以对字段设置默认值。添加注释等 -``` -package "com.github.kuangcp"; -message Article { - required int32 article_id = 1; - optional string article_excerpt = 2; - repeated string article_picture = 3; - optional int32 article_pagecount = 4 [default = 0]; - enum ArticleType { - NOVEL = 0; - PROSE = 1; - PAPER = 2; - POETRY = 3; - } - optional ArticleType article_type = 5 [default = NOVEL]; - message Author { - required string name = 1; //作者的名字 - optional string phone = 2; - } - optional Author author = 6; - repeated int32 article_numberofwords = 7 [packed=true]; - reserved 9, 10, 12 to 15; - extensions 100 to 1000; -} -extend Article { - optional int32 followers_count = 101; - optional int32 likes_count= 102; -} -message Other { - optional string other_info = 1; - oneof test_oneof { - string code1 = 2; - string code2 = 3; - } -} -``` -> 此外reserved关键字主要用于保留相关编号标签,主要是防止在更新proto文件删除了某些字段,而未来的使用者定义新的字段时重新使用了该编号标签。这会引起一些问题在获取老版本的消息时,譬如数据冲突,隐藏的一些bug等。所以一定要用reserved标记这些编号标签以保证不会被使用 - -> 当我们需要对消息进行扩展的时候,我们可以用extensions关键字来定义一些编号标签供第三方扩展。这样的好处是不需要修改原来的消息格式。就像上面proto文件,我们用extend关键字来扩展。只要扩展的字段编号标签在extensions定义的范围里。 - -> 对于基本数值类型,由于历史原因,不能被protobuf更有效的encode。所以在新的代码中使用packed=true可以更加有效率的encode。注意packed只能用于repeated 数值类型的字段。不能用于string类型的字段。 - -> 在消息Other中我们看到定义了一个oneof关键字。这个关键字作用比较有意思。当你设置了oneof里某个成员值时,它会自动清除掉oneof里的其他成员,也就是说同一时刻oneof里只有一个成员有效。这常用于你有许多optional字段时但同一时刻只能使用其中一个,就可以用oneof来加强这种效果。但需要注意的是oneof里的字段不能用required,optional,repeted关键字 - -_导入另一个proto定义_ -`import "article.proto";` - -- 更新Protobuf文件的要求: - 1. 不能改变已有的任何编号标签。 - 2. 只能添加optional和repeated的字段。这样旧代码能够解析新的消息,只是那些新添加的字段会被忽略。但是序列化的时候还是会包含哪些新字段。而新代码无论是旧消息还是新消息都可以解析。 - 3. 非required的字段可以被删除,但是编号标签不可以再次被使用,应该把它标记到reserved中去 - 4. 非required可以被转换为扩展字段,只要字段类型和编号标签保持一致 - 5. 相互兼容的类型,可以从一个类型修改为另一个类型,譬如int32的字段可以修改为int64 - -*********************** ->- 使用上, 因为有多个消息类型, 那么会采用一个数值id作为code, 进行对应 方便沟通 - -##### Linux上安装 -> 只是安装2.5版本 [参考博客: linux下Google的Protobuf安装及使用笔记](http://www.cnblogs.com/brainy/archive/2012/05/13/2498671.html) | [参考:proto buffer 安装 及 调用](http://dofound.blog.163.com/blog/static/1711432462013524111644655/) - -- [下载2.5](https://github.com/google/protobuf/releases/tag/v2.5.0) 并解压 - - 进入目录 `./configure` - - `make` 然后 `make check` 然后 `sudo make install` - - `protoc --version` 有版本则安装成功 - -> 注意: ./configure 时, 默认会安装在/usr/local目录下,可以加`--prefix=/usr`来指定安装到/usr/lib下 ->> 如果不加, 上述参数就要执行 `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib` ->> 当然,可以将这个环境变量的设置加在 .zshrc 或者 .bashrc 里 ->> 不然就会报错: `protoc: error while loading shared libraries: libprotobuf.so.8: cannot open shared object file: No such file or directory` - -##### 通过Docker使用 -##### 对于Java的使用 -> [Google Protocol Buffer 的使用和原理](https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html) `C++ 但是原理差不多` - -`生成Java文件` -touch _hi.proto_ -```protobuf -package lm; -message helloworld{ - required int32 id = 1;//ID - required string str = 2;//str - optional int32 opt = 3;//optional field -} -``` -- 据此生成Java文件 `mkdir src && protoc --java_out=./src hi.proto` -_也可以使用该脚本更新协议_ -```sh - # proto文件中明确定义了一样的包结构就可以直接跑脚本 - basePath='minigame/proto/proto' - targetPath='ssss' - rm -rf $targetPath \ - && mkdir $targetPath \ - && protoc $basePath/*.proto --java_out=$targetPath \ -``` - -`使用` -```java - // 实例化一个构建器 - helloworld.Builder msg = helloworld.newBuilder(); - // 填充信息 - msg.setId(12); -``` -********************* - -#### Thrift -> [官网](https://thrift.apache.org/)源于Facebook, 支持多种语言: C++ C# Cocoa Erlang Haskell Java Ocami Perl PHP Python Ruby Smalltalk - -- 它支持数据(对象)序列化和多种类型的RPC服务, Thrift适用于静态的数据交换, 需要预先确定好他的数据结构, 当数据结构发生变化时,需要重新编辑IDL文件 - -#### Marshalling -> JBOSS 内部使用的编解码框架 diff --git a/Java/AdvancedLearning/Collection.md b/Java/AdvancedLearning/Collection.md deleted file mode 100644 index 9fbaded..0000000 --- a/Java/AdvancedLearning/Collection.md +++ /dev/null @@ -1,88 +0,0 @@ -`目录 start` - -- [集合](#集合) - - [集合继承和实现关系](#集合继承和实现关系) - - [Iterator](#iterator) - - [Map](#map) - - [HashMap 实现原理](#hashmap-实现原理) - - [List](#list) - - [Set](#set) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 集合 -> 重要的知识点,面试必问,使用频率也很高 - -## 集合继承和实现关系 - -- Collection 接口 - - List 接口 - - ArrayList - - LinkedList _也实现了Queue接口_ 双向链表实现 - - Vector - - Set 接口 _内容不允许重复_ - - Queue 接口 _队列接口_ - - SortedSet 接口 _单值排序接口_ - -- Map接口 - - HashMap接口 _无序, key不重复_ - - HashTable接口 _无序, key不重复_ - - TreeMap接口 _按key排序, key不重复_ - - IdentityMap接口 _key可重复_ - - WeakHashMap接口 _弱引用Map集合_ - -## Iterator -> 迭代器 - -******************** -## Map - - -### HashMap 实现原理 -- [HashMap 实现原理](http://www.importnew.com/27043.html) - -- [ ] 整理实现 - -主要看 addVal 方法 以及 resize方法的实现 -HashMap的数据结构是 数组加单链表 (数组是只放一个Node对象, 单链表是为了放通过hash计算得到的index一致的元素包装成的Node对象) - -n 是数组大小 -先 hash (高16和低16做异或)然后 hash&(n-1) 就是结果 (也就是数组的下标) putVal() - -Node数组 大小 是 使用容量达到0.75 就扩容(翻倍), 初始化大小也要是2的幂 - - 扩容就要重新分布单链表 resize() - - 如果是链表的最后一个节点 Hash & (32-1) 计算位数 - - 如果hash 二进制倒数第五位是0, 那么扩容后, 位置就不变 (16 -> 32) - - 如果是1 1+oldCap(16) - -链表就是内部类 Node list方式, 然后 如果节点大于8就转红黑树, 当减少到6后退回到list方式 -******************************************** - -## List -> interface - -包括的方法有: -![List method](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Java/Collection/List/List.png) - -List接口有众多实现, 最常用的ArrayList LinkedList - -****************************** -[stackoverflow: list add then unsupportedoperationexception](https://stackoverflow.com/questions/5755477/java-list-add-unsupportedoperationexception) -> 有时候会使用 Arrays.asList() 或者 Collections.singletonList() 来快速生成 List -> 但是 这两个生成的实例都是返回 AbstractList 的实现类, 其 add remove 方法是没有实现的, 如果调用了就会抛出异常 - -```java - public void add(int index, E element) { - throw new UnsupportedOperationException(); - } -``` -> 这是因为, 这个类设计就是采用的定长数组来实现List, 所以不能对其中元素进行更改 - -****************************************** -## Set -- Set是无序的,但是StringRedisTemplate的对象操作返回的set竟然是有序的 - - 因为有一个类是SortSet,顾名思义,所以是有序的,要继续多学习和使用Java原生的集合对象了 - -> [3分钟搞掂Set集合](https://segmentfault.com/a/1190000014391402?utm_source=channel-hottest) - - diff --git a/Java/AdvancedLearning/Collection/List.md b/Java/AdvancedLearning/Collection/List.md new file mode 100644 index 0000000..4e04c75 --- /dev/null +++ b/Java/AdvancedLearning/Collection/List.md @@ -0,0 +1,21 @@ +--- +title: List.md +date: 2019-05-26 21:38:24 +tags: +categories: +--- + +**目录 start** + +1. [List](#list) + 1. [ArrayList](#arraylist) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# List +> 线性表的接口 + +## ArrayList + +## CopyOnWriteArrayList + diff --git a/Java/AdvancedLearning/Concurrency/Atomic.md b/Java/AdvancedLearning/Concurrency/Atomic.md new file mode 100644 index 0000000..e828622 --- /dev/null +++ b/Java/AdvancedLearning/Concurrency/Atomic.md @@ -0,0 +1,59 @@ +--- +title: Java中的原子类 +date: 2019-06-04 19:44:41 +tags: +categories: +--- + +💠 + +- 1. [UnSafe](#unsafe) +- 2. [CAS](#cas) +- 3. [原子类](#原子类) + +💠 2024-09-24 16:47:51 +**************************************** + +> [CAS, Unsafe和原子类详解](https://pdai.tech/md/java/thread/java-thread-x-juc-AtomicInteger.html) + +# UnSafe + +Unsafe类使Java语言拥有了类似C语言指针一样操作内存空间的能力, 也随之带来了指针相关问题的风险。 + +************************ + +# CAS +CAS的全称为Compare-And-Swap,直译就是对比后交换,是一条CPU的原子指令。 +其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,其实现方式是基于硬件平台的汇编指令,JVM只是封装了汇编调用,这便是原子类的核心基础。 +CAS方式为乐观锁,synchronized 为悲观锁。 + + +> ABA问题 +如果一个值原来是A,变成了B,又变成了A,那么使用CAS操作时会直接执行Swap。 + +解决思路: +1. 可以加版本号, 1A->2B->3A +1. Java 1.5开始,JDK的Atomic包里提供了一个类 `AtomicStampedReference` 来解决ABA问题, compare值时并比较对象内存引用 + +> 竞争高时CPU高负载 +当并发激烈时 compare操作 大部分情况会失败,无休止的循环将导致CPU使用率飙升。类似的,Disruptor队列核心使用CAS,也有这个问题 + +如果JVM能支持处理器提供的pause指令,那么效率会有一定的提升,但是目前JVM无法避免该问题。pause指令有两个作用: +第一,它可以延迟流水线执行命令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零; +第二,它可以避免在退出循环的时候因内存顺序冲突(Memory Order Violation)而引起CPU流水线被清空(CPU Pipeline Flush),从而提高CPU的执行效率。 + +> 只能保证一个共享变量的原子操作 + +解决思路:从Java 1.5开始,JDK提供了AtomicReference类来保证引用对象之间的原子性,就可以把多个变量打包为一个对象 来进行CAS操作。 + +************************ + +# 原子类 + +> `java.util.concurrent.atomic` 提供适当的原子方法 避免在共享数据上出现竞争危害的方法 +> 使用Java自带的原子类, 可以避免同步锁带来的并发访问性能降低的问题, 减少犯错的机会. 对于 int, long, boolean 等成员变量大量使用原子类 +>> 但是使用者必须通过类似 compareAndSet或者set或者与这些操作等价的`原子操作`来保证更新的原子性. + +- 常见的操作系统的支持, 他们是非阻塞的(无需线程锁), 常见的方法是实现序列号机制(和数据库里的序列号机制类似),在`AtomicInteger`或`AtomicLong`上用原子 + - 操作`getAndIncrement()`方法, 并且提供了nextId 方法得到唯一的完全增长的数值 +- 注意: 原子类不是相似的类继承而来,所以 AtomicBoolean不能当Boolean用 diff --git a/Java/AdvancedLearning/Concurrency/ExecutorAndPool.md b/Java/AdvancedLearning/Concurrency/ExecutorAndPool.md new file mode 100644 index 0000000..41b6b36 --- /dev/null +++ b/Java/AdvancedLearning/Concurrency/ExecutorAndPool.md @@ -0,0 +1,247 @@ +--- +title: 线程池 +date: 2019-04-19 12:42:09 +tags: +categories: +--- + +💠 + +- 1. [线程池](#线程池) + - 1.1. [ExecutorService 接口](#executorservice-接口) + - 1.2. [Executors](#executors) + - 1.3. [CompletionService 接口](#completionservice-接口) + - 1.4. [ScheduledThreadPoolExecutor STPE](#scheduledthreadpoolexecutor-stpe) + - 1.5. [分支合并框架 Fork/Join](#分支合并框架-forkjoin) +- 2. [Spring](#spring) + - 2.1. [ThreadPoolTaskExecutor](#threadpooltaskexecutor) +- 3. [实践](#实践) + - 3.1. [线程池 参数优化 监控](#线程池-参数优化-监控) + - 3.2. [业务线程池](#业务线程池) + - 3.3. [停止线程池](#停止线程池) + +💠 2024-09-13 10:39:04 +**************************************** +# 线程池 + +> [Java线程池实现原理及其在美团业务中的实践](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html) +> [线程池 BlockingQueue synchronized volatile](https://segmentfault.com/a/1190000012916473) +> [参考: Java(Android)线程池](http://www.trinea.cn/android/java-android-thread-pool/) +> [参考: Java ThreadPoolExecutor线程池使用的一个误区](http://codefine.site/2941.html) +> [参考: 聊聊并发(三)Java线程池的分析和使用](http://ifeve.com/java-threadpool/) +> [参考: 线程池](http://ifeve.com/thread-pools/) + +> 快速创建命名策略的线程池 `依赖common-lang3` +```java +new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), new BasicThreadFactory.Builder().namingPattern("test-%d").build()); +``` + +## ExecutorService 接口 +> [Github Demo](https://github.com/Kuangcp/JavaBase/tree/master/concurrency/src/main/java/thread/pool) + +- `execute`:用于将任务提交给执行器执行 + - 参数为Runable + - 无返回,对于调用方来说无法感知异常,但是异常栈会被输出到 System.err ,依然有迹可查 +- `submit`:功能同`execute`,但该方法可以返回值或抛出异常 Future 对象 + - 参数为Callable + - 返回的Future对象如果不调用get方法,任务的异常栈在系统中**没有任何痕迹** + +- `shutdown()`:用于关闭执行器资源,执行器会拒绝后面的任务提交,并等待线程池中的任务结束后关闭资源 + - 应用关闭前尽量显式调用该方法关闭所有的线程池,避免资源泄漏 +- `shutdownNow()`:立即关闭执行器,返回等待队列的任务,正在执行的线程将收到interupt但是不一定会停止 +- `isShutdown()`:是否调用过`shutdown()` +- `awaitTermination(long timeout, TimeUnit unit)`:该方法会阻塞调用线程,等待执行器内任务完成直到超时 + +- `invokeAny(Collection> tasks)`:返回 任意的第一个完成任务的返回值 +- `invokeAll(Collection> tasks)`:返回所有任务对应的Future对象 + +> 注意 + +上述的 execute 和 submit 行为只针对 `ThreadPoolExecutor`. 对于 ScheduledThreadPoolExecutor 来说,execute行为不一样, execute提交的任务 抛出异常时也是**没有任何痕迹** + +## Executors +> 该处讲述的方法都为`java.util.concurrent.Executors`的方法 (静态工厂模式) + +- `newFixedThreadPool(int nThreads)`:用于创建固定大小的线程池 + - 传入的参数表示为线程池中最大的线程数 + - 当发送的任务大于该数量时,线程池中只会创建该数量的线程,剩下的任务将会被阻塞,直到有空闲的线程可用 + - 创建方式: `ExecutorService executor = Executors.newFixedThreadPool(3);` + +- `newSingleThreadExecutor()`:用于创建单线程化的线程池 + - 在该线程池中只有一个工作的线程 + - 该线程池可保证`任务会按任务的提交顺序进行` + - 创建方式: `ExecutorService executor = Executors.newSingleThreadExecutor();` + +- `newCachedThreadPool()`:用于创建一个可缓存的线程池 + - 该线程池的`工作线程的创建数量几乎没有限制` + - 当线程池中没有可用的线程时,新添加的任务将会再创建一个线程运行 + - 运行完的任务,在任务运行完的`60s`内不会被回收,当有新任务时将会重用这些没被回收的线程 + - 创建方式: `ExecutorService executor = Executors.newCachedThreadPool();` + +- `newScheduledThreadPool(int corePoolSize)`:用于创建一个定长的且支持定时及周期性运行任务的线程池 + - 传入的参数表示为线程池中最大的线程数 + - 创建方法: `ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);` + - 使用`schedule(Runnable command, long delay, TimeUnit unit)`方法提交任务时,可让任务延迟执行,如下延迟1分钟执行示例: + ```java + // 定义执行器,创建一个缓存线程池 + ScheduledExecutorService executor = Executors.newScheduledThreadPool(3); + // 提交任务 + executor.schedule(() -> System.out.println("hello: " + new Date()), 1, TimeUnit.SECONDS); + // 关闭执行器资源 + executor.shutdown(); + ``` + - 使用`scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)`方法提交任务,可让任务延迟并周期性执行,如下让任务延迟一秒后没3秒执行一次: + ```java + // 定义执行器,创建一个缓存线程池 + ScheduledExecutorService executor = Executors.newScheduledThreadPool(3); + // 提交任务 + executor.scheduleAtFixedRate(() -> System.out.println("hello: " + new Date()), 1, 3, TimeUnit.SECONDS); + // 周期性执行任务时不要关闭执行器,否则不会周期性执行 + //executor.shutdown(); + ``` + +- `newSingleThreadScheduledExecutor()`:功能与`newScheduledThreadPool(int corePoolSize)`方法创建的线程池类似,只是该方法创建的是单例化的线程池,即在该线程池中只有一个工作的线程 + +- `newWorkStealingPool()`:可创建一个拥有多个任务队列的线程池 + - 该方法实在`Java1.8`增加的方法 + - 它是线程池类`ForkJoinPool`的扩展 + - 该线程池能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中 + - 创建方式:`ExecutorService executor = Executors.newWorkStealingPool();` + +## CompletionService 接口 +> 实现类 ExecutorCompletionService 类JavaDoc上有使用示例 + +- submit +- take +- poll + +> [TimeoutExecPoolTest](https://github.com/Kuangcp/JavaBase/blob/master/concurrency/src/test/java/situation/timoutpool/TimeoutExecPoolTest.java)`限时并行消费任务获取结果,时间到期则丢弃所有未完成的任务` + +## ScheduledThreadPoolExecutor STPE +- 线程池的大小可以预定义, 也可自适应 +- 所安排的任务可以定期执行,也可只运行一次 +- STPE 扩展了 ThreadPoolExecutor 类,很相似但不具备定期调度能力 + - STPE 和并发包里的类结合使用是常见的模式之一 + +> 核心API: 提交任务 +- `schedule(Runnable command, long delay, TimeUnit unit)` +- `schedule(Callable callable, long delay, TimeUnit unit)` +- `scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)` + - 不管上一次Runnable执行结束的时间,总是以固定延迟时间执行 即 上一个Runnable执行开始时候 + 延时时间 = 下一个Runnable执行的时间点 +- `scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)` + - 当上一个Runnable执行结束后+固定延迟 = 下一个Runnable执行的时间点 + +> 如何实现调度: [ScheduledThreadPoolExecutor实现原理](https://juejin.cn/post/7035415187783942152) | [验证单元测试](https://github.com/Kuangcp/JavaBase/blob/master/concurrency/src/test/java/thread/schdule/SchedulerPoolTest.java) +- 核心依赖 DelayedWorkQueue 实现延迟调度 + - 全部线程繁忙时,调度会发生什么问题? + +************************ +## 分支合并框架 Fork/Join +> [Note: Fork Join](/Java/AdvancedLearning/Concurrency/ForkAndJoin.md) + +************************ + +# Spring +## ThreadPoolTaskExecutor +> Spring的线程池封装实现 + +- setTaskDecorator: 线程池装饰器,通常用来ThreadLocal值的传递,例如 TraceId,授权对象 +- setWaitForTasksToCompleteOnShutdown 等待线程正常执行完才退出全部线程 + +************************ +# 实践 +目标: 合理利用资源,让线程池安全可控的消费任务 + +> [About Pool Sizing](https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing) | [About Pool Sizing in distributed environments / microservices](https://github.com/brettwooldridge/HikariCP/issues/1023)`如何设置数据库连接池线程数` + +> [ 合理使用线程池以及线程变量 ](https://mp.weixin.qq.com/s/BdVqvm2wLNv05vMTieevMg) +> [ExecutorService - 10 tips and tricks](https://nurkiewicz.com/2014/11/executorservice-10-tips-and-tricks.html) +> [Tomcat 线程池](/Java/Tool/TomcatDesign.md#线程池) + +- 增加全局异常处理 `Thread.setUncaughtExceptionHandler()`, 或手动catch任务块全部代码 避免异常被吞 [测试代码](https://github.com/Kuangcp/JavaBase/blob/master/concurrency/src/test/java/thread/pool/PoolExceptionTest.java) +- 避免局部线程池,容易遗忘线程资源回收,注意线程是GCRoot对象 +- 依据业务和监控合理设置参数,动态调整 + - 监控指标核心诉求是 忙不忙,在忙什么,还有多少要忙。 + - 设置参数值(核心,最大,队列大小等等),活跃线程数,任务执行量,等待队列大小,执行拒绝策略次数。 +- 管理好上下文参数 + +## 线程池 参数优化 监控 +目的:观测线程池运行情况,优化吞吐量和延迟,规避资源分配不合理导致瓶颈甚至宕机 + +``` + Ncpu = cpu的核心数 + Ucpu = cpu的利用率 + W = 线程等待时间 + C = 线程执行计算时间 +``` + +> 公式1:Nthreads = Ncpu * Ucpu * W/C +- 此方案偏理论化,cpu的实际利用率(即分配多少cpu给线程池使用)和线程的计算,等待时间非常难评估,并且最后计算出来的结果也很容易偏离实际应用场景。 + +> 公式2:coreSize = 2 * Ncpu , maxSize = 25 * Ncpu +- 实际使用过程中不同的业务对线程池的需求不一样,所以统一采用cpu核心数来配置显然不太合理 + +> 公式3:coreSize = tps * C , maxSize = tps * C * (1.7~2) +- 依据tps和耗时来计算时刻内需要占用多少线程,这种适合资源充足时为了尽量降低等待时间 + +************************ + +[Java线程池实现原理及其在美团业务中的实践](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html) + - 场景设计具有一定的开拓性,将无法预估的业务负载通过监控和动态伸缩来及时发现异常应对异常。 + - [线程池动态监控](https://github.com/dromara/dynamic-tp)`支持修改和监控告警` + +[根据CPU核心数确定线程池并发线程数](https://www.cnblogs.com/dennyzhangdd/p/6909771.html) +[如何设置线程池参数?](https://www.cnblogs.com/thisiswhy/p/12690630.html) +[线程池实时管理与监控工具的实现与思考](https://www.jianshu.com/p/6f6e2bcb8128) + +[线程池如何监控,才能帮助开发者快速定位线上错误?](https://heapdump.cn/article/4012121)`将基准数据采集到数据库表里` + +************************ + +## 业务线程池 +在实际业务系统中,出于不同业务的吞吐量能力,故障影响,保障优先级 等方面的考虑,通常会对不同的业务模块划分不同的线程池,并依据对应的需求设置不同的参数和策略。 +例如: HTTP客户端线程池,WEB服务器NIO线程池,缓存同步线程池,Websocket消息推送线程池 等等。 + +基于以上的设计考量,会遇到一些问题 +1. 固定的线程参数无法应对动态的业务变化。 + - 方案: 上文的线程池监控告警以及动态参数调整,需要人为守护调整,或依据实际业务场景实现固定的动态扩缩容策略 +1. 不同线程池,上下文传递以及事务问题, 以及异步交错问题。 + - 异步交错问题: 例如一个业务方法需要做ABC先后完成,但是三件事在不同的线程池中,由于不同线程池的执行效率不同导致未能按期望顺序执行 + - 方案: 1. 通过 CompletableFuture 实现异步之间的依赖和组合 + - 上下文传递问题: 可以使用TTL线程池,或者在线程池使用装饰器,手动复制需要的上下文 + - 事务传递问题: TODO + +1. 随着业务需求的变化,线程池边界会模糊,导致吞吐量大的服务被低并发参数的线程池产生短板效应,吞吐量低的服务被高并发参数的线程池任务失败量突增甚至被打垮。 + - 例如HTTP请求任务被提交到了缓存同步线程池,大量的HTTP请求任务占用了很多资源导致系统缓存的实时性大大降低。 + - 方案: TODO + +************************ + +## 停止线程池 +> 如何实现JVM停止时等待线程池中任务执行完成 即 优雅停机 + +为了实现优雅停机的目标,应当先调用shutdown方法,调用这个方法也就意味着,这个线程池不会再接收任何新的任务,但是已经提交的任务还会继续执行。 +之后还应当调用awaitTermination方法,这个方法可以设定线程池在关闭之前的最大超时时间,如果在超时时间结束之前线程池能够正常关闭则会返回true,否则,超时会返回false。 +通常需要根据业务场景预估一个合理的超时时间。 + +如果awaitTermination方法返回false,但又希望尽可能在线程池关闭之后再做其他资源回收工作,可以考虑再调用一次shutdownNow方法,此时队列中所有尚未被处理的任务都会被丢弃,同时会设置线程池中每个线程的中断标志位。 +shutdownNow **并不保证**一定会让正在运行的线程停止工作,除非提交给线程的任务能够正确响应中断。 + +> 线程池停止时,如何感知到 被中断的 运行中和等待中的任务 +- 默认的shutdown接口返回的是Runnable匿名实例,无法明确获取业务特征 + - 可以自己实现 Runnable 附带业务信息进去 + ```java + public class Task implements Runnable { + private String id; + private Runnable task; + public Task(String id, Runnable task) { + this.id = id; + this.task = task; + } + @Override + public void run() { + this.task.run(); + } + } + ``` diff --git a/Java/AdvancedLearning/Concurrency/ForkAndJoin.md b/Java/AdvancedLearning/Concurrency/ForkAndJoin.md new file mode 100644 index 0000000..2848a11 --- /dev/null +++ b/Java/AdvancedLearning/Concurrency/ForkAndJoin.md @@ -0,0 +1,67 @@ +--- +title: Fork/Join +date: 2023-09-25 13:22:47 +tags: +categories: +--- + +💠 + +- 1. [Fork Join](#fork-join) + - 1.1. [最佳实践](#最佳实践) + - 1.2. [设计](#设计) + - 1.3. [使用](#使用) + +💠 2024-04-08 19:45:11 +**************************************** +# Fork Join +> 自 Java7 引入,业务开发时存在感不怎么高,但实际上很多地方用到的一个库(`Stream`,`VirtualThread`) + +> [Guide to the Fork/Join Framework in Java](https://www.baeldung.com/java-fork-join) + +## 最佳实践 +- 目的是为了`CPU密集型任务` 利用CPU全部资源全速执行,应`避免IO阻塞任务`提交执行 + - 如果启用了虚拟线程,可以不用考虑这点,IO阻塞任务会出让CPU +- 如无必要无需创建新Pool,应使用JVM内公共Pool 即 `ForkJoinPool.commonPool()` +- 任务拆分时需要考虑合理阈值,避免子任务拆分的过大`无法合理均匀分布`或过小`调度和竞争成本过大` + +## 设计 + +ForkJoinPool 服务处理一种比线程更小的并发单元 ForkJoinTask. 它是一种由ForkJoinPool以更轻量的方式所调度的抽象 + +- 通常使用两种任务 + - 小型 无需处理器耗时太久的任务 + - 大型 需要在直接执行前进行分解(可能多次)的任务 +- 提供了支持大型任务分解的基本方法,还有自动调度和重新调度的能力 + +- 由 RecursiveAction 或者 RecursiveTask 派生出来的才能作为任务单元 这俩也是派生ForkJoinTask而来 + - RecursiveAction 要重写的方法:`protected void compute()` + - RecursiveTask 要重写的的方法:`protected Object compute()` +- ForkJoinTask里的 invoke 和 invokeAll + - invoke 执行此任务的开始,如果有必要,等待它的完成,并返回其结果,或者在底层计算完成时抛出一个(未检查的)RuntimeException或错误。 + - invokeAll 提交多个任务执行,但是只有其中有一个出现了异常,就会取消所有的task + +`ForkJoinTask和工作窃取` +- ForkJoinTask作为RecursiveAction的超类,他是从动作中返回结果的泛型类型,所以这个类扩展了`ForkJoinTask` + - 这使得ForkJoinTask非常适合用MapReduce方式(Google提出的软件架构,用于大规模数据集的并行计算)返回数据集中归结出的结果 +- ForkJoinTask由ForkJoinPool调度安排,这个池是一个特殊的执行者服务。 + - 这个服务维护每个线程的任务列表,并且当某个任务完成的时候,他能把挂在满负荷线程上的任务重新安排到空闲线程上去 即 `工作窃取` + - 常见的线程池设计都是一个池一个等待队列,但是FJ是每个线程一个等待队列 `ForkJoinPool#workQueues`,大大降低竞争导致CPU占用的成本 + +`并行问题` +- 可以使用分支合并方法解决的问题: + - 模拟大量简单对象的运动,例如粒子效果 + - 日志文件分析 + - 从输入中计数的数据操作,比如mapreduce操作 + +- 以下的列表检查是否能用 FJ 来解决问题, 如果思考的结果是肯定的,就可以适用,如果思考结果是不确定的,用其他的方式更合适 + - 问题的子任务是否无需与其他子任务有显式的协作或同步也可以工作? + - 子任务是不是不会对数据进行修改,只是经过计算得出些结果? + - 对于子任务来说,分而治之是不是很自然的事?子任务是不是会创建更多的子任务,而且他们要比派生出他们的任务粒度更细? + +## 使用 + +[Detailed difference between Java8 ForkJoinPool and Executors.newWorkStealingPool?](https://stackoverflow.com/questions/41337451/detailed-difference-between-java8-forkjoinpool-and-executors-newworkstealingpool) + +[简单样例:Groovy 实现](https://github.com/Kuangcp/JavaBase/blob/master/concurrency/src/main/java/com/github/kuangcp/forkjoin/ForkJoinEasyDemo.groovy) + diff --git a/Java/AdvancedLearning/Concurrency/Lock.md b/Java/AdvancedLearning/Concurrency/Lock.md new file mode 100644 index 0000000..b1c8198 --- /dev/null +++ b/Java/AdvancedLearning/Concurrency/Lock.md @@ -0,0 +1,32 @@ +--- +title: Java中的锁 +date: 2019-04-22 15:49:53 +tags: +categories: + - Java +--- + +**目录 start** + +1. [Lock](#lock) + 1. [队列同步器](#队列同步器) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Lock + +```java + Lock lock = new ReentrantLock(); + // 不能放在 try中, 防止 获取锁失败, 并执行了释放锁 + lock.lock(); + try { + + } finally { + lock.lock(); + } +``` + +## 队列同步器 + +AbstractQueuedSynchronizer 队列同步器, 是构建锁和其他同步组件的基础框架 +AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。 \ No newline at end of file diff --git a/Java/AdvancedLearning/Concurrency/img/001-concurrency-base.km.svg b/Java/AdvancedLearning/Concurrency/img/001-concurrency-base.km.svg new file mode 100644 index 0000000..aac509d --- /dev/null +++ b/Java/AdvancedLearning/Concurrency/img/001-concurrency-base.km.svg @@ -0,0 +1 @@ +Java 并发分工Executor与线程池Fork/JoinFutureGuarded Suspension 模式Balking 模式Thread Per Message 模式生产者 消费者模式Worker Thread 模式两阶段终止模式互斥无锁不变模式线程本地存储Copy-On-Write原子类互斥锁 SynchronizedLock读写锁协作信号量Lock & ConditionSynchronized管程CountDownLatchCyclicBarrierPhaserExchanger \ No newline at end of file diff --git a/Java/AdvancedLearning/Deploy.md b/Java/AdvancedLearning/Deploy.md deleted file mode 100644 index 3bbee63..0000000 --- a/Java/AdvancedLearning/Deploy.md +++ /dev/null @@ -1,113 +0,0 @@ -`目录 start` - -- [部署运行](#部署运行) - - [可执行jar](#可执行jar) - - [用命令手动打包](#用命令手动打包) - - [Maven](#maven) - - [Gradle](#gradle) - - [打包war](#打包war) - - [Docker部署](#docker部署) - - [手动](#手动) - - [Maven](#maven) - - [Gradle](#gradle) - -`目录 end` |_2018-08-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 部署运行 -> 传统的可执行jar, war 以及Docker镜像 - -> [参考博客: JAR 文件揭密](https://www.ibm.com/developerworks/cn/java/j-jar/index.html) -> [参考博客: maven-assembly-plugin 入门指南](https://www.jianshu.com/p/14bcb17b99e0) - -## 打包可执行jar -### 用命令手动打包 -> [关于MANIFEST.MF文件](https://blog.csdn.net/baileyfu/article/details/1808023)`这个文件很重要, 如果自己手动配置就需要编写该文件` -_MANIFEST.MF示例_ -```yml - Manifest-Version: 1.0 - Archiver-Version: Plexus Archiver - Built-By: kcp - Created-By: Apache Maven 3.5.3 - Build-Jdk: 1.8.0_152 - Main-Class: com.youaishujuhui.minigame.Main -``` -- 编译文件 `javac -d *.java ` -- 打包字节码成jar `jar -cvf hello.jar com/test/*.*` -- 打包成可执行jar `jar -cvfm hello.jar mainfest *.*` - - 其中:`mainfest` 文本文件: `Main-Class: com.test.Main` 冒号后一定要有空格,文件最后一行一定留空行 - -- 运行jar包中指定的类`java -cp clojure.jar clojure.main` - - 多个jar运行 `java -cp jline-0.9.94.jar;clojure.jar jline.ConsoleRunner clojure.main` - - [参考博客: 用java –jar 命令运行Jar包](https://blog.csdn.net/paullinjie/article/details/53188943) - -### Maven - -**不依赖Jar的项目** -> [Demo项目](https://gitee.com/gin9/codes/ri4x8cut3awgh0e271lfb54) | [详情](/Java/Tool/Maven.md#31打包成可执行jar) - -**依赖Jar的项目** -- [ ] 完善 Maven 含 Jar 打包可执行jar - -> [Maven实战(九)——打包的技巧](http://www.infoq.com/cn/news/2011/06/xxb-maven-9-package) -> [Maven打包成可执行jar](https://blog.csdn.net/u013177446/article/details/53944424) -> [参考博客: 使用MAVEN打包可执行的jar包](https://www.jianshu.com/p/afb79650b606) - -1. [Demo项目的完整代码片段](https://gitee.com/gin9/codes/ri4x8cut3awgh0e271lfb54) - - 多个main的情况下运行指定的main - - `java -cp example03-1.0-SNAPSHOT.jar cn.zhouyafeng.itchat4j.main.TulingRobot` - -> war和jar一样使用 -- Springboot项目能够做到, 其实就是Main方法, 然后配置了一个Servlet的加载类就可以当war用了 - - [通过Maven构建打包Spring boot,并将config配置文件提取到jar文件外](http://lib.csdn.net/article/java/65574) - -### Gradle -> [参考博客: Building Java Applications](https://guides.gradle.org/building-java-applications/) - -**不依赖Jar的项目** -1. 依据模板新建项目 `gradle init --type java-application` -```groovy -// 主要是如下配置 -plugins { - // Apply the java plugin to add support for Java - id 'java' - // Apply the application plugin to add support for building an application - id 'application' -} -// Define the main class for the application -mainClassName = 'App' -``` -2. add this config to build.gradle -```groovy - jar { - manifest { - attributes 'Main-Class': 'base.Main' - } - } -``` -3. run : `gradle clean jar && java -jar file` - -**依赖Jar的项目** -> 有好几种插件可以实现 1.[shadow插件官网文档](http://imperceptiblethoughts.com/shadow/) - -- [ ] 完善 Gradle 含 Jar 打包可执行jar - -************************* - -## 打包war -> 最终将生成的war放到tomcat的webapps目录下即可 - -******************** - -## Docker镜像 -> 以一个基础镜像,然后将war放进去构建成一个镜像, 然后推送到服务器上构建容器进行运行 - -> [jib](https://github.com/GoogleContainerTools/jib) -> - 结合 Maven Gradle 方便的构建 Docker镜像 - -### 手动 - -### Maven - -### Gradle - - diff --git a/Java/AdvancedLearning/Exception.md b/Java/AdvancedLearning/Exception.md deleted file mode 100644 index f793826..0000000 --- a/Java/AdvancedLearning/Exception.md +++ /dev/null @@ -1,93 +0,0 @@ -`目录 start` - -- [异常](#异常) - - [异常的继承关系](#异常的继承关系) - - [异常常见问题](#异常常见问题) - - [应该使用大块的try还是细颗粒度的try?](#应该使用大块的try还是细颗粒度的try) - - [try和for谁包住谁更好?](#try和for谁包住谁更好) - - [异常的处理](#异常的处理) - - [Web应用中全局异常统一处理](#web应用中全局异常统一处理) - - [Restful应用的全局异常统一处理](#restful应用的全局异常统一处理) - - [异常和日志的结合](#异常和日志的结合) - - [自定义异常](#自定义异常) - - [自定义异常的错误码](#自定义异常的错误码) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 异常 -> 相关博客:[Java异常浅谈](http://www.cnblogs.com/focusj/archive/2011/12/26/2301524.html) -下面的部分文字内容来源于 JPM的GitChat 群里的交流 - -## 异常的继承关系 -![异常结构](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Java/Exception/structure.png) - -> Java将所有非正常情况分为两种 异常(Exception)和错误(Error) 它们都继承Throwable父类。 -Err错误,一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常应用程序无法处理这些错误; -因此应用程序不应该使用catch块来捕获Error对象,在定义该方法时,也无须在其throws子句中声明该方法可能抛出Error及任何子类。 -所以异常处理更多的是指Exception类,而经常处理的一般是RunTimeException 而Exception又分为2种:_校验异常_ 和 _运行时异常_ - -**************************** -## 异常常见问题 -### 应该使用大块的try还是细颗粒度的try? -> 为了避免我们遗漏掉一些可能会出现异常的代码, 所以建议使用大块的try, 因为检查型异常是容易发现的, 但是运行时异常却往往不能在编码的第一时间发现 - -> 检验异常在开发中如果不进行处理(捕获处理或声明抛出)编译器就会报错不让通过的, 如果平时没有注意异常的系统性学习, -开发就会有这样一种现象: 程序中通篇只有校验异常. 一般这种校验异常默认的处理方式(使用IDE)是颗粒度小的. -但是程序运行出问题的大多是运行时异常, 空指针, 数组越界, 类型不匹配, 除数为0 等等. -使用大try就不会遗漏运行时异常,但是不能仅仅依靠他, 运行时异常还是尽量使用好的编程习惯来规避的. -当然也是可以在大try中使用小try进行开发这样就能对异常进行具体的捕获和处理以及响应 - -> 按下葫芦浮起瓢, 关于异常, 可以从另一个角度: 性能方面的维度去考虑: -异常机制的设计初衷是用于不正常的情形, JVM很少对其生成的字节码进行优化, 把尽可能多的代码放在try块中就会阻止了JVM实行原本可以执行的某些特定优化 - -### try和for谁包住谁更好? -> 具体业务具体分析, 如果要求循环一出问题后续的循环还是要继续执行, 那么就把try写到for中; -如果要求后续的循环不执行就用try包住for - -另外: 使用多线程实现的定时任务在循环处理数据时出现异常必须要及时处理, 否则执行时就会退出 - -******************** -## 异常的处理 -> 一般来说, 异常都是层层上抛, 中央处理 针对 Service Dao Controller 这种结构的设计, 在Service层进行统一异常处理 -> 除非这个异常是无关大局的, 就是局部发生对其他模块和代码没有影响, 那么就可以就地捕获不需要上抛 - -不建议使用判断语句进行异常的处理, 这样的维护很费劲, 可读性也不好 -大的try块中 一些需要特别说明的就是 特别处理的就要看情况抛出了, 一般进行封装后, 抛出自定义异常, 上层接收后, 进行二次处理和转化, -直到最外层的调用处, 由最外层调用者决定最终如何处理 只要在使用对象前进行对象的非空判断, 基本就能杜绝空指针异常, - -- [ ] TODO 这样的话要引入大量的if块, 希望能找到更好的解决方案 - -### Web应用中全局异常统一处理 - - -### Restful应用的全局异常统一处理 -方法一,nginx上设置拦截,配置对应的跳转页面。 -方法二,添加监听器捕获输出异常,然后按自己需要组织返回Json - - - - -### 异常和日志的结合 -> 异常一定要和日志结合使用, 日志记录的简约优雅, 维护才越轻松, 日志的存在就是为了解决问题的方便和有迹可循, -> 所以要记录的任何信息都是为了解决问题, 无关信息就没有必要放进来了 - -目前个人用到的就是所在类 方法 行数 时间 报错信息, 根据业务需要还可以加上用户id, 订单id之类的 - -### 自定义异常 -> 虽然Java提供了大量的异常类, 但是这些异常类还是难以满足业务的需求, 这个需求可能是用户, 客服, 我们自身 - -通常自定义异常只要继承Exception 重写相关构造器即可. -一般来说自定义异常具有以下类型: 业务异常, 用户异常, 系统异常, 接口异常, 网络异常, 参数异常等等. -根据项目需要, 可以将异常细分, 比如写一个订单保存, 那么针对订单保存的业务可以在不同的代码逻辑里提示不同的异常信息, 接口级异常也是如此 -目的是为了将Exception进行封装, 形成易于理解的异常信息. - -_自定义异常的设计原则_ -1. 用户层面: 用户提示信息要优雅 -2. 系统层面: 让自己维护起来更方便 -3. 接口层面: 查询问题更直观, 轻松, 为自己留证据, 避免推诿扯皮 -4. 网络层面: 及时发现问题, 及时进行处理 - -#### 自定义异常的错误码 -> 正规项目中都会有接口文档, 也会有规范注释, 在项目中也会有一些静态常量 -假设定义一个错误码 00X1 表示空指针, 这个错误码不是给客户或者使用者看的, 而且定义要有规律不能让其他人猜到, 方便查询和管理 -规划的越详细, 就会有更为便利的维护方式 diff --git a/Java/AdvancedLearning/GC.md b/Java/AdvancedLearning/GC.md deleted file mode 100644 index 5f17bbf..0000000 --- a/Java/AdvancedLearning/GC.md +++ /dev/null @@ -1,11 +0,0 @@ -`目录 start` - -- [GC](#gc) - -`目录 end` |_2018-08-21_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# GC -> Garbage Collection 垃圾回收 - -> [参考博客: 译垃圾收集器Serial 、Parallel、CMS、G1 ](http://www.zicheng.net/article/55.htm) - diff --git a/Java/AdvancedLearning/GrammarAndType.md b/Java/AdvancedLearning/GrammarAndType.md deleted file mode 100644 index c2a3b97..0000000 --- a/Java/AdvancedLearning/GrammarAndType.md +++ /dev/null @@ -1,261 +0,0 @@ -`目录 start` - -- [基础语法](#基础语法) - - [代码风格](#代码风格) - - [结构](#结构) - - [判断](#判断) - - [循环](#循环) - - [用户输入输出](#用户输入输出) -- [数据类型](#数据类型) - - [基础数据类型](#基础数据类型) - - [byte](#byte) - - [char](#char) - - [boolean](#boolean) - - [short](#short) - - [int](#int) - - [long](#long) - - [float](#float) - - [double](#double) - - [包装类型](#包装类型) - - [String](#string) - - [StringBuffer和StringBuilder](#stringbuffer和stringbuilder) - - [Float](#float) - - [Double](#double) - - [Integer](#integer) - - [Long](#long) - - [Boolean](#boolean) - - [枚举类型](#枚举类型) - - [自动拆装箱](#自动拆装箱) - - [内部类](#内部类) - - [类型强转](#类型强转) - - [时间类型](#时间类型) -- [类的结构](#类的结构) - - [修饰符](#修饰符) - - [权限修饰符](#权限修饰符) - - [其他](#其他) - - [成员属性](#成员属性) - - [方法](#方法) -- [Object](#object) - - [VO](#vo) - - [PO](#po) - - [TO](#to) - - [BO](#bo) - - [POJO](#pojo) - - [DAO](#dao) -- [关键字](#关键字) - -`目录 end` |_2018-09-06_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 基础语法 - -## 代码风格 -> [Google Style Guide](https://github.com/google/styleguide) | [阿里巴巴开发手册](/Java/AlibabaJavaStandard.md) - -## 结构 -### 判断 -- if -- switch - -### 循环 -- while - - `while(true){}` - - `do{}while(true);` -- for 循环 - - `for(int a=0; i<10; i++){}` -- for each循环 - - `for(Object item:list){}` 其中list对象如果是通过调用一个对象的方法返回的,那么只会调用一次 - -## 用户输入输出 -- `System.out.println("")` 输出并在末尾追加换行 - - .print() 输出, 行末不换行 - - .printf() 格式化输出, 和C语法类似 - -*********************** -# 数据类型 - -## 基础数据类型 -> 八种基本数据类型 byte char boolean short int long float double - -> [参考博客: Java 有值类型吗?](http://www.yinwang.org/blog-cn/2016/06/08/java-value-type) - -确实, 这样来看Java没有值类型才是更统一的, 不过有没有对程序都是一样的, 因为Java没有解引用, 基本数据类型又没有成员, 所以值还是引用, 没差 - -### byte -> 字节 - -Java8以前是使用 char数组 来存放String, Java8开始就是 byte数组 了 - -### char - -### boolean -> [参考 你真的知道Java中boolean类型占用多少个字节吗?](https://www.jianshu.com/p/2f663dc820d0) - -### short - -### int -> 数值范围 `+- 2147483647` = 2^31-1 也就说明了int是占四个字节 32位 一位是符号位 (操作系统的不同也会有差异) - -### long -> 数值范围 `+- 9223372036854775807` = 2^63-1 也就是八个字节 64位 一位是符号位 - -### float - -### double - -************************** -## 包装类型 -> 基本类型和包装类型不能混为一谈 本质上的 class是不同的, 只不过自动拆装箱才让人感觉没差别 - -### String -> 该类是final修饰的, 原因:[知乎问题](https://www.zhihu.com/question/31345592) - -- 常量池的实现 - -- 常见编码转换 - - 一般Windows文件默认编码:`str = new String(str.getBytes("iso8859-1"), "gb2312"); ` - - properties文件中获取中文 `str = new String(str.getBytes("utf-8"), "utf-8");` - -#### StringBuffer和StringBuilder -> [参考博客](https://blog.csdn.net/rmn190/article/details/1492013) - -### Float -### Double -### Integer -### Long -### Boolean -**************************** -## 枚举类型 -> [official doc: enum](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html) - -枚举类的构造器必须是private 或者 package private (也就是缺省) - -> [参考博客: Java 语言中 Enum 类型的使用介绍](https://www.ibm.com/developerworks/cn/java/j-lo-enum/index.html) -从上面的定义形式来看,似乎 Java 中的枚举类型很简单,但实际上 Java 语言规范赋予枚举类型的功能非常的强大,它不仅是简单地将整形数值转换成对象,而是将枚举类型定义转变成一个完整功能的类定义。 - -- 简单定义 - - `public enum Color {RED, GREEN, GRAY, BLUE, YELLOW, WHITE, PURPLE, BLACK}` - -- 简单单例 -```java -public enum Tool{ - INSTANCE(12); - private int num; - Tool(int num){ - this.num = num; - } - public getNum(){ - return num; - } -} -// 使用的时候 -Tool.INSTANCE.getNum(); -``` - -**************************** -## 自动拆装箱 -> 基本数据类型和包装类型在Java中是可以视为等价的, 就是因为自动拆装箱的存在 - -*************************** -## 内部类 -> 其域可以和其他常见类型一样, 作为类的成员, 也可作为方法的局部变量, 其中包含的各种变量的域都是按原规则生效的 - -_但是内部类的属性不能用static修饰_ -归根结底,还是类与对象的区别,静态属性不依赖于对象,因为它保存在jvm的静态区,所以访问修改的时候不需要依赖当前有没有存活的对象,在虚拟机加载的时候也是优先于实例生成的。 -而实例对象则是保存在jvm的堆内存中,想要访问内部类,必须先实例化外部类,然后通过外部类才能访问内部类。内部类其实也可以认为是外部类的一个成员变量,只要是成员变量, -各个对象都是不依赖的,静态属性的出现破坏了这一逻辑,所以java语言在语义层面不允许我们那么做,这其实不是技术问题,是一个语言的逻辑和语义问题。 - -> [参考博客: 关于Java内部类字段和方法不能使用static修饰的原因](https://my.oschina.net/u/1027043/blog/1823113) - -************************** -## 类型强转 -- Double -> int 直接(int)num; - -********************** -## 时间类型 - -1. 最早常用是 Date 然后 Calendar 然后 Instant LocalDateTime ... - -_获取指定时间_ [获取指定时间的时间戳](https://blog.csdn.net/jssongwei/article/details/71403354) - -****************** -# 类的结构 -## 修饰符 -> [参考博客: java 权限修饰符](https://blog.csdn.net/yan8024/article/details/6426451) - -### 权限修饰符 -- `public` **任意范围**; -- `protected` **子类** 与 **同包**; 子类可以是任意包下 -- `缺省(package private)` **同包**; 限定了同一个包下, 才能访问 所修饰的属性 -- `private` 只能 **当前类** 或者 **内部类** 访问 - -### 其他 - -## 成员属性 -作为Java的bean, 或者大多数情况下, 属性都是私有的, 然后提供setter getter 方法,而且 一般来说, setter和getter方法是不能包含逻辑的, 也就是简单的赋值 取值 -乍一看相比于C语言, 似乎这是多此一举, 但是注意面向对象思想, 一个对象对外提供的应该只是行为, 具有较强的语义性, 什么对象执行了什么方法, 而直接引用就可能将对象属性和静态属性混淆 - -## 方法 -方法的签名: -- [ ] 方法签名的详解 - -************** -# Object - -> [参考博客: java的(PO,VO,TO,BO,DAO,POJO)解释](http://www.cnblogs.com/yxnchinahlj/archive/2012/02/24/2366110.html) -> | [VO DAO BO 等缩写的意义](https://zhuanlan.zhihu.com/p/35762537?group_id=969493512006373376) - -## VO -> (value object) 值对象 -1. 使用new关键字创建的, 由GC回收的, -2. VO是值对象, 业务对象, 存活在业务层的, 是业务逻辑使用的 - - 它存在的目的就是为数据提供一个生存的地方 -3. VO的属性是根据当前业务的不同而不同的 - - 也就是说,它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。 - -## PO -> (persistant object) 持久对象 -1. PO则是向数据库中添加新数据时创建,删除数据库中数据时削除的。 - - 并且它只能存活在一个数据库连接中,断开连接即被销毁。 -2. PO是持久化对象, 是有状态的, 每个属性代表其当前状态, 他是物理数据的对象表示 - - 使用它能够让我们的程序与物理数据解耦,并且可以简化对象数据与物理数据之间的转换。 -3. PO的属性是跟数据库表的字段一一对应的。 -4. PO对象需要实现序列化接口 - -> 首先说PO和VO吧,它们的关系应该是相互独立的,一个VO可以只是PO的部分,也可以是多个PO构成,同样也可以等同于一个PO(当然我是指他们的属性)。 -正因为这样,PO独立出来,数据持久层也就独立出来了,它不会受到任何业务的干涉。又正因为这样,业务逻辑层也独立开来,它不会受到数据持久层的影响,业务层关心的只是业务逻辑的处理,至于怎么存怎么读交给别人吧! -不过,另外一点,如果我们没有使用数据持久层,或者说没有使用hibernate,那么PO和VO也可以是同一个东西,虽然这并不好。 - -## TO -> (transfer Object) 数据传输对象 -- 在应用程序不同tie(关系)之间传输的对象 - -## BO -> (business object) 业务对象 -- 从业务模型的角度看,见UML元件领域模型中的领域对象。封装业务逻辑的java对象,通过调用DAO方法,结合PO,VO进行业务操作。 -- 它装满了业务逻辑的处理,在业务逻辑复杂的应用中有用。 - -## POJO -> [Wikipedia: POJO](https://en.wikipedia.org/wiki/Plain_old_Java_object) `Plain Old Java Object` - -> (plain ordinary java object) 简单无规则java对象 -- 纯的传统意义的java对象。就是说在一些Object/Relation Mapping工具中,能够做到维护数据库表记录的persisent object完全是一个符合Java Bean规范的纯Java对象,没有增加别的属性和方法。我的理解就是最基本的Java Bean,只有属性字段及setter和getter方法! - -## DAO -> (data access object) 数据访问对象 -- 通常和PO结合使用,DAO中包含了各种数据库的操作方法 - -************************* -# 关键字 -> Java关键字和保留字 -``` -abstract class extends implements null strictfp true -assert const false import package super try -boolean continue final instanceof private switch void -break default finally int protected synchronized volatile -byte do float interface public this while -case double for long return throw -catch else goto native short throws -char enum if new static transient -``` - -- [ ] transient 序列化时不进行序列化 \ No newline at end of file diff --git a/Java/AdvancedLearning/IO.md b/Java/AdvancedLearning/IO.md deleted file mode 100644 index a06dcb3..0000000 --- a/Java/AdvancedLearning/IO.md +++ /dev/null @@ -1,91 +0,0 @@ -`目录 start` - -- [IO操作的学习](#io操作的学习) - - [Java IO简史](#java-io简史) - - [Java1.0到1.3 BIO](#java10到13-bio) - - [Java1.4 NIO](#java14-nio) - - [Java1.7 AIO](#java17-aio) - - [关于普通IO的文件操作](#关于普通io的文件操作) - - [读取配置文件](#读取配置文件) - - [可执行jar读取外部配置文件](#可执行jar读取外部配置文件) - - [Maven项目](#maven项目) - - [NIO](#nio) - - [Buffer](#buffer) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# IO操作的学习 -> - [个人代码:IO流的相关学习](https://github.com/Kuangcp/JavaBase/tree/master/src/main/java/com/io) - -## Java IO简史 -> [BIO NIO AIO演变](http://www.cnblogs.com/itdragon/p/8337234.html) - -### Java1.0到1.3 BIO -同步阻塞式IO -但是能自己实现 伪异步IO -### Java1.4 NIO -> 非阻塞式IO, 虽然官方名称为New IO, 民间称为No-blocking IO - -> [参考博客: NIO基础详解](http://cmsblogs.com/?p=2467) - -- 对于调用者来说是异步的, 但是实际上是使用的多路复用和一个线程进行轮询, 真的吗? 到底是不是异步的呢? - -### Java1.7 AIO -> 真正的异步非阻塞IO, NIO2.0 - -- 引入了新的异步通道的概念, 以及异步文件通道和异步套接字通道的实现 - - -## 关于普通IO的文件操作 -### 读取配置文件 -- maven格式路径,会从resources下获取文件例如 /a.xml -- `InputStream is = this.getClass().getResourceAsStream(path);` - - 读取properties文件 :`new Properties().load(is);` - - 按行读取文件 `BufferedReader bf = new BufferedReader(new InputStreamReader(is));` - -************** -#### 可执行jar读取外部配置文件 -```java - Properties properties = new Properties(); - File file = new File("something.properties"); - FileInputStream fis = new FileInputStream(file); - properties.load(fis); - System.out.println(properties.getProperty("v")); - fis.close(); -``` -- 只要配置文件和打包的jar同级即可 - -#### Maven项目 -_读取resource目录下配置文件_ -```java - ClassLoader classLoader = MainConfig.class.getClassLoader(); - URL resource = classLoader.getResource("excel.main.yml"); - if(resource!=null){ - String path = resource.getPath(); - } -``` -- 这样也可以, 但是会有诡异的问题, 打包后运行是正常的, idea中运行就不正常, `new File("src/main/resources/excel.main.yml")` - -********************************** -## NIO -> [NIO](http://ifeve.com/overview/) -学习真是痛苦, 过程繁杂,又有各种并发 难以调试 - - -### Buffer -> [Java NIO系列教程(三) Buffer](http://ifeve.com/buffers/) - -- Buffer的基本用法: 使用Buffer读写数据一般遵循以下四个步骤: -> - 写入数据到Buffer - 调用flip()方法 - 从Buffer中读取数据 - 调用clear()方法或者compact()方法 - -- 当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。 - -- 一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区: - - 调用clear()或compact()方法。 -- clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。 - - diff --git a/Java/AdvancedLearning/JDBC.md b/Java/AdvancedLearning/JDBC.md index 2a0c9db..0cb817f 100644 --- a/Java/AdvancedLearning/JDBC.md +++ b/Java/AdvancedLearning/JDBC.md @@ -1,24 +1,156 @@ -`目录 start` - -- [JDBC](#jdbc) - - [Java内置数据库Derby](#java内置数据库derby) +--- +title: JDBC +date: 2018-12-16 17:28:17 +tags: +categories: + - Java +--- -`目录 end` |_2018-08-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [JDBC](#jdbc) + - 1.1. [Statement](#statement) + - 1.1.1. [PrepareStatement](#preparestatement) + - 1.2. [ResultSet](#resultset) + - 1.2.1. [长连接流式导出数据](#长连接流式导出数据) + - 1.3. [SQLException](#sqlexception) +- 2. [厂商驱动](#厂商驱动) + - 2.1. [MySQL](#mysql) + - 2.2. [Clickhouse](#clickhouse) +- 3. [Tips](#tips) + +💠 2024-10-08 11:23:38 **************************************** # JDBC +Java DataBase Connectivity + +核心思想是定义一套接口规范,让各个数据库厂商实现这套接口,从而让应用方调用数据库的能力时可以不关心底层数据库 +- 设计是美好的,但是现实是丑陋的,或者说无法对多类型的数据库做完全的抽象一致,基础功能确实能一致化,其他功能还是要特殊化处理 + - 举例: 获取某个表的元信息`表引擎,表索引,字段名,字段类型` MySQL和PostgreSQL实现完全不一致,pg实现成本很大,有些功能无法实现 + > [码农翻身:JDBC的诞生](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513438&idx=1&sn=2967d595bb7d4ffdd2dacd3ab7501bbd&chksm=80d6799db7a1f08b27dc97650434fb2fc0e2570628945db99d9300a99e52828fd05c42fdb441&scene=21#wechat_redirect) +> 基础流程 +- 注册 driver +- 建立 connection +- 创建 statement +- 执行获取 ResultSet +- 解析返回的 ResultSet + + +> Tips - 基础的批量操作SQL ` pstmt.executeBatch(); //批量执行` +- [在Java11中被移除了的 Derby](http://db.apache.org/derby/derby_comm.html) + +## Statement +主要分为 Statement 和 PrepareStatement, 在使用层面主要的区别为前者直接执行原始SQL,存在SQL注入风险, 后者是编译模板。 + +### PrepareStatement +> [Oracle: Using Prepared Statements](https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html) + +依赖于具体数据库,常见的 [MySQL](https://dev.mysql.com/doc/refman/8.3/en/sql-prepared-statements.html) [PostgreSQL](https://jdbc.postgresql.org/documentation/server-prepare/)都有SQL编译功能 + +> 权衡 +- [PrepareStatement的功与过](https://www.cnblogs.com/wangzhen3798/p/12206811.html)`最多的问题是 因为SQL只编译解析一次,执行计划的重用导致会忽略实际传入的参数对执行计划的影响` +- [MyBatis select query slow](https://groups.google.com/g/mybatis-user/c/Wubq26QCWYo?pli=1)`应该是一样的问题编译SQL在不同执行时,执行计划变更导致的慢` + - [Query is slow with JDBC parameters, fast with concatenated SQL](https://dba.stackexchange.com/questions/231109/query-is-slow-with-jdbc-parameters-fast-with-concatenated-sql) `MSSQL` + + +> 客户端参数调整 +- [Druid](https://github.com/alibaba/druid/blob/master/druid-spring-boot-starter/README_EN.md)`pool-prepared-statements` 连接池层面的缓存 + +************************ + +## ResultSet +> 仅为JDBC接口,具体行为细节来自实际数据库厂商提供的驱动 + +### 长连接流式导出数据 +常见的分页导出的缺点有 分页越来越慢和不稳定排序导致页之间数据重复或丢失,用长连接流方式可以规避 + +```java + // 阻塞模式 查数据和业务逻辑交替执行 + private void fetchBatchWithDataResource(DataSource ds, String sql, String where, int fetchSize, + Consumer>> handle) { + Connection connection = null; + Statement stmt = null; + ResultSet rs = null; + try { + connection = ds.getConnection(); + String query; + if (StringUtils.isNotBlank(where)) { + query = sql + " WHERE " + where; + } else { + query = sql; + } + + log.info("stream export: query={}", query); + + stmt = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + stmt.setQueryTimeout(3600); + stmt.setFetchSize(fetchSize); + + rs = stmt.executeQuery(query); + int counter = 0; + + List> data = new ArrayList<>(); + while (rs.next()) { + ResultSetMetaData meta = rs.getMetaData(); + int columnCount = meta.getColumnCount(); + LinkedHashMap row = new LinkedHashMap<>(); + for (int i = 1; i <= columnCount; i++) { + row.put(meta.getColumnName(i), rs.getObject(i)); + } + data.add(row); + + if (data.size() > fetchSize) { + handle.accept(data); + counter++; + data.clear(); + } + } + if (!data.isEmpty()) { + handle.accept(data); + counter++; + } + + log.info("stream export: count={} dataSize={} ", counter, (counter - 1) * fetchSize + data.size()); + } catch (Exception e) { + log.error("", e); + throw new RuntimeException(e); + } finally { + close(connection, stmt, rs); + } + } +``` +- Statement 设置了 fetchSize 或者 TYPE_FORWARD_ONLY 模式后,都会采用游标的方式获取全部的数据 +- 参数 handle 是解析ResultSet 去生成 CSV Excel 等业务逻辑方法引用 +- 优化版本 生产者-消费者模式,降低阻塞时间,从而降低大量任务的整体耗时,但是CPU毛刺会增多且明显 + - 生产者:查询,消费者:业务逻辑,队列:QueueChannel + - [样例代码](https://github.com/Kuangcp/JavaBase/blob/master/concurrency/src/test/java/com/github/kuangcp/queue/use/blocking/ReaderWriterTest.java) + +- Clickhouse可以直接使用, 不需要额外的配置 +- PostgreSQL 调整: + - executeQuery前 **关闭 autoCommit**,finally 开启,才会fetch指定的数据量,否则会拉取全部的数据到JVM。[pg jdbc doc](https://jdbc.postgresql.org/documentation/head/connect.html#connection-parameters) +- MySQL 调整: + - url配置需要添加 useCursorFetch=true 或者 关闭 autoCommit + +## SQLException +大部分数据库厂商都会由此派生出自定义的异常,CK除外,因此支持JDBC通用数据库的平台需要做特殊处理。 + +************************ +# 厂商驱动 +## MySQL -注册driver -创建 connection -创建 statement -执行获取 Resultset -处理返回结果 resultst +- [Java数据类型和MySQL数据类型对应](https://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-type-conversions.html)`简单来说就是基本数据类型加上String是有对应的MySQL基本数据类型` -Statement 和 PrepareStatement 的区别, 掌握PrepareStatement的主要用法(推荐使用) +## Clickhouse +> [clickhouse-java](/Database/OLAP/Clickhouse.md#java) -## Java内置数据库Derby +************************ -> [Derby](http://db.apache.org/derby/derby_comm.html) +# Tips +> [mysql-connector-java 插入 utf8mb4 字符失败问题处理分析](https://blog.arstercz.com/mysql-connector-java-%e6%8f%92%e5%85%a5-utf8mb4-%e5%ad%97%e7%ac%a6%e5%a4%b1%e8%b4%a5%e9%97%ae%e9%a2%98%e5%a4%84%e7%90%86%e5%88%86%e6%9e%90/) +> 批量插入性能优化 +- 关闭事务,或者手动管理事务 循环插入前开启,插入完一批再提交 +- 多条 `insert values()` 改为一条 `insert values (),()` \ No newline at end of file diff --git a/Java/AdvancedLearning/JDKAndJRE.md b/Java/AdvancedLearning/JDKAndJRE.md deleted file mode 100644 index 3b39c4b..0000000 --- a/Java/AdvancedLearning/JDKAndJRE.md +++ /dev/null @@ -1,65 +0,0 @@ -`目录 start` - -- [JDK and JRE](#jdk-and-jre) -- [JDK](#jdk) - - [目录结构](#目录结构) - - [jar](#jar) - - [jvisualvm](#jvisualvm) -- [JRE](#jre) - - [目录结构](#目录结构) -- [OpenJDK](#openjdk) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# JDK and JRE - -# JDK -> 指的是Oracle JDK - -## 目录结构 -``` -├── bin/ -├── COPYRIGHT -├── include/ -├── javafx-src.zip -├── jre/ -├── lib/ -├── LICENSE -├── man/ -├── README.html -├── release -├── src.zip -├── THIRDPARTYLICENSEREADME-JAVAFX.txt -└── THIRDPARTYLICENSEREADME.txt -``` - - -### jar - -### jvisualvm - -> [使用 VisualVM 进行性能分析及调优](https://www.ibm.com/developerworks/cn/java/j-lo-visualvm/index.html) -> [参考博客: JVisualVM简介与内存泄漏实战分析](http://www.cnblogs.com/belen/p/5573501.html) - - -******************* -# JRE - -## 目录结构 - -``` -├── bin/ -├── COPYRIGHT -├── lib/ -├── LICENSE -├── plugin/ -├── README -├── THIRDPARTYLICENSEREADME-JAVAFX.txt -├── THIRDPARTYLICENSEREADME.txt -└── Welcome.html -``` - -********************************************************* -# OpenJDK -> [官网](http://openjdk.java.net/) | [openJDK下载地址](https://adoptopenjdk.net/nightly.html) - diff --git a/Java/AdvancedLearning/JMS.md b/Java/AdvancedLearning/JMS.md deleted file mode 100644 index 9533673..0000000 --- a/Java/AdvancedLearning/JMS.md +++ /dev/null @@ -1,12 +0,0 @@ -`目录 start` - -- [JMS](#jms) - - [RocketMQ](#rocketmq) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# JMS -> Java Message Service 规范而已,和JDBC一样, 具体实现由厂商来实现 [码农翻身:Java帝国之JMS的诞生](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513515&idx=1&sn=380bb1cb56d4151fd3acc5aa86f1da9a&chksm=80d67a68b7a1f37e3d98fe4495eab4db097eedd695c99fbd8704cc0464595842c4da598b99e3&scene=21#wechat_redirect) - -## RocketMQ -> [有关demo](https://github.com/lirenzuo/rocketmq-rocketmq-all-4.1.0-incubating) \ No newline at end of file diff --git a/Java/AdvancedLearning/JMX.md b/Java/AdvancedLearning/JMX.md new file mode 100644 index 0000000..acb909d --- /dev/null +++ b/Java/AdvancedLearning/JMX.md @@ -0,0 +1,100 @@ +--- +title: JMX +date: 2018-11-21 10:56:52 +tags: +categories: + - Java +--- + +💠 + +- 1. [JMX](#jmx) + - 1.1. [概念](#概念) + - 1.2. [使用](#使用) + - 1.2.1. [远程JMX](#远程jmx) + - 1.2.2. [工具](#工具) +- 2. [MXBean](#mxbean) + - 2.1. [GarbageCollectorMXBean](#garbagecollectormxbean) + - 2.2. [自定义MXBean](#自定义mxbean) + +💠 2024-10-08 15:07:46 +**************************************** + +# JMX +> Java Management Extensions, 提供了一个可以动态修改资源的机制 + +> [Official Doc](https://www.oracle.com/technetwork/java/javase/tech/javamanagement-140525.html) | [wiki](https://en.wikipedia.org/wiki/Java_Management_Extensions) | [wici zh](https://zh.wikipedia.org/zh-hans/JMX) + +> [参考: JMX学习笔记](https://www.jianshu.com/p/414647c1179e) +> [参考: java常见命令及Java Dump介绍](http://www.cnblogs.com/kongzhongqijing/articles/5534624.html) + +> [What is JMX? 10 mins Quick Start JMX Tutorial](https://www.journaldev.com/1352/what-is-jmx-mbean-jconsole-tutorial) + +## 概念 + +| 名称 | 含义 | +|:----|:----| +| MBean | 全称为Managed Bean, 你可以实现一个MBean来JMX提供管理内容 | +| MBean Server(也叫JMX Agent) | 提供集中注册管理MBean功能,允许远程通过他代理操作MBean | +| JMX Connectors | 通过实现不同的通讯协议,来允许远程访问 | + +简而言之 MBean就是存放了一堆属性的对象, 通过JMX技术, 可以远程动态修改这些MBean的状态 + +## 使用 +> [JMXTerm](https://www.baeldung.com/java-jmxterm-external-debugging) + +### 远程JMX +JVM启动时追加参数启用,也可以对已存在的JVM进程启用 `jcmd $pid ManagementAgent.start [options]` [jcmd help](https://docs.oracle.com/en/java/javase/17/docs/specs/man/jcmd.html) + +| 参数 | 类型 | 描述 | +|:---|:---|:---| +| -Dcom.sun.management.jmxremote | 布尔 | 是否支持远程JMX访问,默认true | +| -Dcom.sun.management.jmxremote.port | 数值 | 监听端口号,方便远程访问 | +| -Dcom.sun.management.jmxremote.authenticate | 布尔 | 是否需要开启用户认证,默认开启 +| -Dcom.sun.management.jmxremote.ssl | 布尔 | 是否对连接开启SSL加密,默认开启 +| -Dcom.sun.management.jmxremote.access.file | 路径 | 对访问用户的权限授权的文件的路径,默认路径 `JRE_HOME/lib/management/jmxremote.access` +| -Dcom.sun.management.jmxremote.password.file | 路径 | 设置访问用户的用户名和密码,默认路径 `JRE_HOME/lib/management/jmxremote.password` + +```ini + -Dcom.sun.management.jmxremote.port=4433 + -Djava.rmi.server.hostname=192.168.9.155 + -Dcom.sun.management.jmxremote.ssl=false + + # 1. 不配置账户 + -Dcom.sun.management.jmxremote.authenticate=false + + # 2. 配置账户 + -Dcom.sun.management.jmxremote.authenticate=true + -Dcom.sun.management.jmxremote.password.file=jmxremote.password + -Dcom.sun.management.jmxremote.access.file=jmxremote.access +``` + +> jmxremote.password +``` +username1 pwd1 +username2 pwd2 +``` +> jmxremote.access +``` +username1 readonly +username2 readwrite +``` + +### 工具 +[Prometheus JMX Exporter](https://github.com/prometheus/jmx_exporter) + +************************ + +# MXBean +通过查看 `java.lang.management.PlatformManagedObject` 的子类可以快速预览所有的MXBean + +- OperatingSystemMXBean 操作系统信息 获取最大和free内存,但是无法获取available内存,简单做法是直接读取 `/proc/meminfo` + +## GarbageCollectorMXBean +> [Garbage Collection JMX Notifications](http://www.fasterj.com/articles/gcnotifs.shtml) + +通过监听 GarbageCollectorMXBean,应用可感知JVM GC动作。 + +## 自定义MXBean +> [集成JMX](https://www.liaoxuefeng.com/wiki/1252599548343744/1282385687609378) + diff --git a/Java/AdvancedLearning/JVM.md b/Java/AdvancedLearning/JVM.md index 503c0f4..4081fb5 100644 --- a/Java/AdvancedLearning/JVM.md +++ b/Java/AdvancedLearning/JVM.md @@ -1,23 +1,390 @@ -`目录 start` - -- [JVM](#jvm) - - [Hotspot JVM](#hotspot-jvm) - - [OpenJ9](#openj9) +--- +title: JVM +date: 2019-04-02 10:56:52 +tags: + - JVM +categories: + - Java +--- + +💠 + +- 1. [JVM](#jvm) + - 1.1. [JVM参数](#jvm参数) + - 1.2. [Unified JVM Logging](#unified-jvm-logging) + - 1.3. [JVM内存参数](#jvm内存参数) + - 1.3.1. [容器内的JVM](#容器内的jvm) + - 1.3.2. [内存参数实践](#内存参数实践) +- 2. [JVM 基本结构](#jvm-基本结构) +- 3. [内存区域](#内存区域) + - 3.1. [运行时数据区](#运行时数据区) + - 3.1.1. [程序计数器](#程序计数器) + - 3.1.2. [Java虚拟机栈](#java虚拟机栈) + - 3.1.3. [本地方法栈](#本地方法栈) + - 3.1.4. [Heap 堆](#heap-堆) + - 3.1.4.1. [堆内存分配策略](#堆内存分配策略) + - 3.1.5. [方法区](#方法区) + - 3.1.5.1. [运行时常量池](#运行时常量池) + - 3.1.6. [Direct Memory 直接内存](#direct-memory-直接内存) + - 3.1.7. [Code Cache](#code-cache) + - 3.2. [Metaspace 元空间](#metaspace-元空间) + - 3.3. [直接内存](#直接内存) +- 4. [JVM不同实现](#jvm不同实现) + - 4.1. [Hotspot JVM](#hotspot-jvm) + - 4.2. [OpenJ9](#openj9) + - 4.3. [GraalVM](#graalvm) -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 2024-08-06 11:01:51 **************************************** # JVM -> Oracle 默认采用的是 Hotspot JVM +> JVM结构及设计 -## Hotspot JVM +Oracle JDK 默认采用的是 Hotspot JVM + +> [Java Language and Virtual Machine Specifications](https://docs.oracle.com/javase/specs/) + +> [Github:jvm学习仓库](https://github.com/xwjie/jvm) +> [个人博客: JVM归类](https://vinoit.me/tags/jvm/) + +`书籍` +- 《深入理解 Java 虚拟机》(周志明 第二版) 大部分内容来源于此, 但是部分内容是依据Java8有所改动 + +`社区` +- [prefma](https://club.perfma.com/) + +## JVM参数 +> [Command Reference for JDK8](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html) | [Command Reference for JDK17](https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html) +> [Official: Java HotSpot VM Options](https://www.oracle.com/java/technologies/javase/vmoptions-jsp.html) +> [Guide to the Most Important JVM Parameters](https://www.baeldung.com/jvm-parameters) + +- [远程调试](/Java/AdvancedLearning/JavaDebug.md#远程调试) +- `-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false` + - 开启无需认证 非SSL的JMX端口: 9999 + +- `-XX:+TraceClassLoading -XX:+TraceClassUnloading` 输出类装载/卸载日志,可用于排查类从哪个jar加载进入JVM的 + - Java9及以后替代为: `-Xlog:class+load=info` + +> OOM +- `-XX:+HeapDumpOnOutOfMemoryError ` +- `-XX:HeapDumpPath=./java_pid.hprof` 注意路径需要存在,JVM不会创建不存在的目录 +- `-XX:OnOutOfMemoryError="< cmd args >;< cmd args >" ` +- `-XX:+UseGCOverheadLimit` + +> 字符串 +- -XX:+UseStringCache +- -XX:+UseCompressedStrings +- -XX:+OptimizeStringConcat +- -XX:+UseStringDeduplication + + +> 编译类参数 +- CICompilerCount是JIT进行热点编译的线程数,和并发标记线程数一样,热点编译也是CPU密集型任务,默认值为2。 +在CICompilerCountPerCPU开启的时候(JDK7默认关闭,JDK8默认开启),手动指定CICompilerCount是不会生效的,JVM会使用系统CPU核数进行计算。 +所以当使用JRE8并且版本小于1.8.0_131,采用默认参数时,CICompilerCount会在20左右,对业务性能影响较大,特别是启动阶段。建议升级Java版本,特殊情况要使用老版本Java 8,请加上`-XX:CICompilerCount=[n]`, 同时不能指定-XX:+CICompilerCountPerCPU ,下表给出了生产环境下常见规格的推荐值。 + +| CPU核数 | 1 | 2 | 4 | 8 | 16 | +|:---|:---|:---|:---|:---|:---| +| 推荐值 | 2 | 2 | 3 | 3 | 8 | + +## Unified JVM Logging +> [JEP 158: Unified JVM Logging](https://openjdk.org/jeps/158) + +Java9开始,整合了GC,类加载等日志配置方式,日志级别,输出方式 正交式声明使用,更统一及灵活。 + +## JVM内存参数 +> 堆(老年代 年轻代),堆外,元空间,栈 + +快速确认进程内存配置 +| 工具 | 命令 | +|:----|:----| +| Arthas | `jvm` | +| OpenJDK | `jcmd pid GC.heap_info` | +| OracleJDK | `jmap -heap pid` | + +- `-XX:CompressedClassSpaceSize=500m` 压缩的类元空间大小 默认是1g +- `-XX:SurvivorRatio` 配置 Edgen 和 单个Survivor 的比例, 如果配置为2 则是 2:1:1。 **默认是8** +- `-XX:NewRatio`old/new 内存的比值 **默认是2** +- `-Xmn` MaxNewSize 默认值是`Xmx`的1/3 即最大堆内存 MaxHeapSize 的1/3 +- `-Xss` 设置 ThreadStackSize 线程的栈内存大小 默认值 1024k + +> java -XX:+PrintFlagsFinal -version +- `-XX:+PrintFlagsInitial` 输出初始默认值 +- `-XX:+PrintFlagsFinal` 输出JVM最终属性值 + - MaxHeapSize 最大堆内存 + - MaxRAMFraction 默认最大内存占物理机内存的比例 JDK6,7,8 都是4 即1/4 + - NUMA 机制 + - `java -XX:+PrintFlagsFinal -version | grep "Use.*GC"` 查看默认GC实现 +- `-XshowSettings:VM` 展示VM和系统信息 + +************************ + +需要理解,但是不用,尽量使用明确的 Xmx Xms +> [JVM Parameters InitialRAMPercentage, MinRAMPercentage, and MaxRAMPercentage](https://www.baeldung.com/java-jvm-parameters-rampercentage) +- MinRAMPercentage, MaxRAMPercentage 其实都是**设置堆默认最大值**的, Max 和 Min 换成 Big Small可能更好理解(大内存环境和小内存环境 `200M划分`) +- `-XX:InitialRAMPercentage` 初始堆使用值 默认1.5625, 当配置了 `-Xms` 时,该配置将被忽略 +- InitialRAMFraction MaxRAMFraction MinRAMFraction DefaultMaxRAMFraction 4等分值 + +************************ + +### 容器内的JVM + +容器无法感知资源限制, 8U191/10b34 及以上版本才支持 + +- 快速实验某个Java版本的默认参数和限制 docker run -m 100MB openjdk:8 java -XX:MinRAMPercentage=80.0 -XshowSettings:VM -version +- [参考: Java和Docker限制的那些事儿](http://www.techug.com/post/java-and-docker-memory-limits.html)`天坑: 低版本的Jvm无法感知到Docker的资源限制` +- [ ] 但是有的Linux版本在后续的版本也无法感知,例如 1.8.0_342 10.0.2 仍取的主机内存, Linux 5.15内核 Manjaro23.1 + +1. [Java (prior to JDK8 update 131) applications running in docker container CPU / Memory issues?](https://stackoverflow.com/questions/64262912/java-prior-to-jdk8-update-131-applications-running-in-docker-container-cpu-m) +1. [Best Practices: Java Memory Arguments for Containers](https://dzone.com/articles/best-practices-java-memory-arguments-for-container) + +总结: **尽量使用 Xms Xmx**,而不是 RAMPercentage RAMFractionc参数(还要结合容器或宿主机计算实际值),降低维护和理解成本,控制更灵活精确,且支持所有版本JVM,不用考虑兼容性问题 + +参数: +- -XX:ActiveProcessorCount=$CONTAINER_CORE_LIMIT 强制设置CPU量 从downawrd_api获取 +- -Xlog:os+container=logLevel JVM报告容器信息 +- -XX:-UseContainerSupport 关闭容器支持,默认开启 + +************************ + +### 内存参数实践 +> [初始和最大堆内存设置为一样的好处](https://gceasy.ycrash.cn/gc-recommendations/benefits-of-setting-initial-and-maximum-memory-size.jsp) +> [Benefits of setting initial and maximum memory size to the same value](https://blog.ycrash.io/benefits-of-setting-initial-and-maximum-memory-size-to-the-same-value/) +- 避免扩容的暂停事件,提前调度充足资源的容器防止运行期扩容而被Linux被OOMKiller杀掉 + + +> [参考: JVM实用参数(一)JVM类型以及编译器模式](http://ifeve.com/useful-jvm-flags-part-1-jvm-types-and-compiler-modes-2/) +> [xxfox](http://xxfox.perfma.com/)`Jvm参数辅助工具` +> [参考: JVM动态反优化](https://blog.mythsman.com/post/5d2c12cc67f841464434a3ec/) + +************************ + +# JVM 基本结构 +![JVM基本结构](img/004-jvm-structure.drawio.svg) + +- 类加载器子系统 + - 负责从文件系统或者网络中记载Class信息,加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中可能还会存放运行时的常量池信息,包含字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射) +- Java堆 + - 在虚拟机启动的时候建立,他是Java程序最主要的内存工作区域。几乎所有Java对象实例都存放于Java堆中。堆空间是所有线程共享的,这是一块与Java应用密切相关的内存区间 + - Java的NIO库允许Java程序使用直接内存。直接内存是Java堆外的、直接向系统申请的内存区间。通常,访问直接内存的速度优于Java堆。因此出于性能考虑,读写频繁的场合可能会考虑使用直接内存。由于直接内存在Java堆外,因此他的大小不会直接受限于Xmx指定的最大堆大小,但是系统内存是有限的,Java堆和直接内存的总和依然受限于操作系统能给出的最大内存 +- 垃圾回收系统 + - 是Java虚拟机的重要组成部分,垃圾回收器可以对方法区、Java堆和直接内存进行回收。其中,Java堆是垃圾回收器的工作重点。和C/C++不同,Java中所有的对象空间释放都是隐式的。也就是说,Java中没有类似free()和delete()这样的函数释放指定的内存区域,对于不再使用的垃圾对象,垃圾回收系统会在后台默默工作,默默查找、标识并释放垃圾对象,完成Java堆、方法区和直接内存中的全自动管理。 +- Java虚拟机栈 + - 每一个Java虚拟机线程都有一个私有的Java栈。一个线程的Java栈在线程创建的时候被创建。Java栈中保存着帧信息,Java栈中保存着局部变量、方法参数,同时和Java方法的调用、返回密切相关。 +- 本地方法栈 + - 和Java栈非常类似,最大的不同在于Java栈用于Java方法的调用,而本地方法栈则用于本地方法调用。作为Java虚拟机的重要扩展,Java虚拟机允许Java直接调用本地方法。 +- PC寄存器 + - 也是每个线程私有的空间,Java虚拟机会为每一个Java线程创建PC寄存器。在任意时刻,一个Java线程总是在执行一个方法,这个正在被执行的方法称为当前方法。如果当前方法不是一个本地方法,PC寄存器就会指向当前正在被执行的指令。如果当前方法是本地方法,那么PC寄存器的值就是undefined。 +- 执行引擎 + - 是Java虚拟机的最核心组件之一,它负责执行虚拟机的字节码。 + +************************ + +# 内存区域 +> [谈JVM xmx, xms等内存相关参数合理性设置](https://developer.jdcloud.com/article/2740) + +内存参数设置的考量: +- Xmx * 110% + MaxDirectMemorySize + 系统预留内存 <= 容器内存 +- Xmx * 110% 中额外的10%是留给其他堆外内存的,是个保守估计,个别业务运行时线程较多,需自行判断,上式中左侧还需加上Xss * 线程数 +- 系统预留内存512M到1G,视容器规格而定 +- I/O较多的业务适当提高MaxDirectMemorySize比例 + +## 运行时数据区 +![](img/001-jvm-runtime-memory.drawio.svg) + +线程私有的内存区域: 程序计数器 本地方法栈 虚拟机栈. 生命周期与线程保持一致 + +### 程序计数器 +可以看作是当前线程所执行的字节码的行号指示器, 这个内存区域是唯一一个在JVM规范中没有规定任何 OutOfMemoryError 的区域 + +### Java虚拟机栈 +> HotSpot 中不区分Java虚拟机栈和本地方法栈, 虽然 -Xoss 存在(设置本地方法栈大小)但是是无效的, 只能通过 -Xss 设置 +![](img/002-method-stack.drawio.svg) + +- 虚拟机栈描述的是Java方法执行的内存模型: 每个方法在执行的同时, 都会创建一个`栈帧`(Stack Frame) + - 用于存储局部变量表, 操作数栈, 动态链接, 方法出口等信息 + - 在一个栈帧中,至少要包含局部变量表、操作数栈和帧数据。 +- 每个方法调用到执行完成的过程, 就对应着一个栈帧在虚拟机栈中入栈到出栈的过程 + +- 局部变量表 + - 存放了编译期可知的各种 基本数据类型, 对象引用, returnAddress 类型 + - 对象引用: reference 类型, 不等同于对象本身, 可能是一个指向对象地址的引用指针, 可能是一个代表对象的句柄, (可能是其他与此对象相关的位置?) + - returnAddress: 指向了一条字节码指令的地址 + - 只有 long double 类型 会占用 2 个局部变量空间, 其他类型都只占用 1 个 + - 局部变量表所需的内存空间在编译后就已经确定下来, 运行期是不会变的 + +- Java虚拟机规范中对该内存区域定义了两种异常状况 + - 如果线程请求的栈深度大于虚拟机所允许的最大深度, 将抛出 StackOverFlowError + - 如果虚拟机在扩展栈时, 无法申请到足够的内存, 则抛出 OutOfMemoryError 异常 + +### 本地方法栈 +Native Method Stack, 与虚拟机栈所发挥的作用是相似的, 只不过虚拟机栈是为虚拟机执行Java方法服务, 本地方法栈是为了虚拟机使用 Native 方法服务 + +### Heap 堆 +> Java虚拟机规范中的描述是 所有对象实例以及数组都是在堆上分配, 但是由于 JIT编译器 逃逸分析 栈上分配, 标量替换等技术, 就变得没那么绝对了 + +堆分为 新生代(包含: Eden, Survivor from, Survivor to) 老年代 +从堆和栈的功能和作用来通俗的比较,堆主要用来存放对象的,栈主要是用来执行程序的. +JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。 +堆栈以栈帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。 + +> [参考: Java中堆内存和栈内存详解](http://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html) + +#### 堆内存分配策略 +- 对象的内存分配, 粗略讲就是在堆上分配(但也可能经过JIT编译后被拆散成标量类型并间接地栈上分配) +- 对象主要分配在Eden; 如果启动了本地线程分配缓冲, 则优先在TLAB上分配; 也有直接分配在老年代的 (长字符串以及数组) + +1. `类变量`(static修饰的变量) 在程序加载时系统就为它在堆中开辟了内存,堆中的内存地址存放于栈以便于高速访问。 + - 生命周期: 从应用进程启动一直到进程停止 +2. `实例变量` 当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量(比如说类实例),然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的"物理位置"。 + - 生命周期: 当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存 +3. `局部变量` 局部变量,由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存 + - 生命周期: 当局部变量一但脱离作用域,内存立即释放 + +************************ + +- 如果对象在 Eden 出生, 并经过一次 MinorGC后存活, 并能被 Survivor 容纳, 将被移入 Survivor 且年龄为1. + - 对象在 Survivor 每经过一次 MinorGC 年龄加1, 当达到 MaxTenuringThreshold(默认15) 就会移入老年代 +- 如果 Survivor 空间中相同年龄所有对象大小的总和大于 Survivor 空间的一半, 年龄大于等于该年龄的对象都将进入老年代, 无需等到设置的 MaxTenuringThreshold + +> 堆内存配置: 新生代一般设置为整个堆空间的1/3到1/4左右最合适。 +新生代内存不能过大也不能过小, 过大则老年代内存过小, 导致频繁 FullGC +过小则导致对象全在老年代分配,新生代上无法分配(Allocation Failure) 也将导致频繁 Full GC + +### 方法区 +方法区存在于永久代 Perm Gen, 对应于Java8中的MetaSpace + +用于存放 Class 相关信息, 常量, 静态变量, 访问修饰符, 字段描述, 方法描述, JIT编译器编译后的代码等数据 +在 HotSpot 虚拟机上, 方法区也看做是 永久代 Permanent Gen, 两者关系是: 方法区是Java虚拟机规范, 永久代是方法区在Hotspot上的实现 +从Java8开始, 永久代已经被 MetaSpace(操作系统的直接内存) 取代 + +JDK7中符号表被移动到 Native Heap中,字符串常量池和类引用被移动到 Java Heap中。 + +#### 运行时常量池 +运行时常量池是方法区的一部分, 用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池存放. + +### Direct Memory 直接内存 +直接内存并不是虚拟机运行时数据区的一部分, 也不是Java虚拟机规范中定义的内存区域. 但是这部分内存也被频繁地使用, 而且也可能导致 OutOfMemoryError + +NIO 会经常使用, 提高性能 + +### Code Cache +> [Introduction to JVM Code Cache](https://www.baeldung.com/jvm-code-cache) + + +************************ + +## Metaspace 元空间 +> MetaSpace Java8 引入, 取代了以往的 Perm Gen + +- 存放内容: + - Klass结构 + - 匿名类, Lambda表达式 + - String.intern 的字符串 + +> 特性 +- 充分利用了Java语言规范:类及相关的元数据的生命周期与类加载器的一致。 +- 每个类加载器都有它的内存区域-元空间 +- 只进行线性分配 +- 不会单独回收某个类(除了重定义类 RedefineClasses 或类加载失败) +- 没有GC扫描或压缩 +- 元空间里的对象不会被转移 +- 如果GC发现某个类加载器不再存活,会对整个元空间进行集体回收 + +> [参考: Metaspace Architecture](https://stuefe.de/posts/metaspace/metaspace-architecture/) +> [参考: What is Compressed Class Space?](https://stuefe.de/posts/metaspace/what-is-compressed-class-space/) +> [深入理解堆外内存 Metaspace](https://www.javadoop.com/post/metaspace) + +[Metaspace 解密](https://heapdump.cn/article/210111) + +-XX:MaxMetaspaceSize 指定元空间的最大空间,默认值是无限(16EB)。 +-XX:MetaspaceSize 指定元空间首次扩充的大小,默认为20.75M + +由于MaxMetaspaceSize未指定时,默认无上限,所以需要特别关注内存泄露的问题,如果程序动态的创建了很多类,或出现过java.lang.OutOfMemoryError:Metaspace,建议明确指定-XX:MaxMetaspaceSize。 +另外Metaspace实际分配的大小是随着需要逐步扩大的,**每次扩大需要一次FGC**,-XX:MetaspaceSize默认的值比较小,需要频繁GC扩充到需要的大小。类似日志可以看到Metaspace引起的FGC:`[Full GC (Metadata GC Threshold) ...]` + +因此为减少预热影响,可以将-XX:MetaspaceSize,-XX:MaxMetaspaceSize指定成相同的值。 + +## 直接内存 +直接内存主要是JNI、Deflater/Inflater、DirectByteBuffer(nio中会用到)使用的, 当发现Java进程堆使用率不高,但是进程占用内存RSS很高,就要怀疑这块区域了 + +- [Github: 测试代码](https://github.com/Kuangcp/JavaBase/blob/master/class/src/test/java/jvm/oom/DirectMemoryOOMTest.java) +- [how to see memory useage of nio buffers](https://stackoverflow.com/questions/2689914/how-to-see-the-memory-usage-of-nio-buffers) + +> [参考: 聊聊JVM 堆外内存泄露的BUG是如何查找的](https://cloud.tencent.com/developer/article/1129904) +> [JAVA堆外内存排查小结](https://zhuanlan.zhihu.com/p/60976273) + +- `-XX:MaxDirectMemorySize` 限制最大内存,默认值为: MaxHeapSize - Survivor。 `通过工具查看的话,值为0` + +- 启用NMT: java -XX:NativeMemoryTracking=summary 或者 detail 开销更大一些 +- 查看NMT jcmd $pid VM.native_memory `[detail] 对应启用时设置,输出具体内存地址信息` + +> 示例 +```sh +Native Memory Tracking: + +Total: reserved=10019737KB, committed=997089KB reversed保留内存 commited实际提交内存 +- Java Heap (reserved=8222720KB, committed=514048KB) + (mmap: reserved=8222720KB, committed=514048KB) +- Class (reserved=1081845KB, committed=34165KB) 存储类元数据信息 + (classes #3085) + (malloc=14837KB #3960) + (mmap: reserved=1067008KB, committed=19328KB) +- Thread (reserved=54501KB, committed=54501KB) 线程占用的空间 即54线程 54M:默认栈为1M需-Xss修改 + (thread #54) + (stack: reserved=54272KB, committed=54272KB) + (malloc=178KB #318) + (arena=52KB #95) +- Code (reserved=250763KB, committed=9551KB) JIT代码缓存 + (malloc=1163KB #2588) + (mmap: reserved=249600KB, committed=8388KB) +- GC (reserved=316571KB, committed=291487KB) GC算法需要的内存空间 + (malloc=16151KB #155) + (mmap: reserved=300420KB, committed=275336KB) +- Compiler (reserved=183KB, committed=183KB) + (malloc=40KB #208) + (arena=142KB #15) +- Internal (reserved=84975KB, committed=84975KB) + (malloc=84943KB #16310) + (mmap: reserved=32KB, committed=32KB) +- Symbol (reserved=4984KB, committed=4984KB) + (malloc=3633KB #26295) + (arena=1351KB #1) +- Native Memory Tracking (reserved=982KB, committed=982KB) NMT自身占用内存 + (malloc=163KB #2304) + (tracking overhead=818KB) +- Arena Chunk (reserved=2214KB, committed=2214KB) + (malloc=2214KB) +``` + +********************** + +# JVM不同实现 +## Hotspot JVM +原先 SUN 公司开发, 现为 Oracle JDK 中默认JVM ## OpenJ9 IBM主导开发, 捐赠给Eclipse基金会 -> [官网](http://www.eclipse.org/openj9/) | [IBM原文](https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.vm.80.doc/docs/j9_intro.html) -- [Github:](https://github.com/eclipse/openj9) +> [Officail Site](http://www.eclipse.org/openj9/) | [IBM原文](https://www.ibm.com/support/knowledgecenter/SSYKE2_8.0.0/com.ibm.java.vm.80.doc/docs/j9_intro.html) | [技术文档](https://eclipse.dev/openj9/docs/) + +> [参考: IBM开源JVM实现OpenJ9,并提交Eclipse基金会托管)](http://www.infoq.com/cn/news/2017/09/IBM-JVM-OpenJ9-Eclipse) +> [参考: Eclipse Open J9:Eclipse OMR项目提供的开源JVM](http://www.infoq.com/cn/news/2018/03/OMR-OpenJ9) + +************************ + +## GraalVM +> [Official Site](https://www.graalvm.org/) + +> [Doc](https://www.graalvm.org/latest/docs/getting-started/) +- 安装 graalvm `sdk install java 17.0.11-graal` +- 跟随样例中执行 native-image HelloWorld, 可以发现会占满16核CPU`AMD 3700x`和1G左右内存,20s才可以编译完成 + +> [Accelerating Java performance](https://www.graalvm.org/java/advantages/)`基准测试宣称快于openjdk8和11 1.55倍` +> [GraalVM Native Image Support](https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html) + +************************ -> [参考博客: IBM开源JVM实现OpenJ9,并提交Eclipse基金会托管)](http://www.infoq.com/cn/news/2017/09/IBM-JVM-OpenJ9-Eclipse) -> [参考博客: Eclipse Open J9:Eclipse OMR项目提供的开源JVM](http://www.infoq.com/cn/news/2018/03/OMR-OpenJ9) +> [参考: Oracle 发布多语种虚拟机平台 GraalVM 1.0](https://www.infoq.cn/article/2018%2F05%2Foracle-graalvm-v1) +> [参考: 全栈虚拟机GraalVM初体验](https://zhuanlan.zhihu.com/p/35849246) diff --git a/Java/AdvancedLearning/Java7.md b/Java/AdvancedLearning/Java7.md deleted file mode 100644 index c00cd22..0000000 --- a/Java/AdvancedLearning/Java7.md +++ /dev/null @@ -1,60 +0,0 @@ -`目录 start` - -- [Java7](#java7) - - [异常处理](#异常处理) - - [TWR](#twr) - -`目录 end` |_2018-08-26_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Java7 -- [Java8](https://docs.oracle.com/javase/8/docs/api/) - -_小特性_ -- | Switch 支持 String -- | 二进制的实例化 原本是 `int x = Integer.parseInt("1010100", 2);`Java7之后`int x = 0b110110;` -- | 数字下划线 `10_0100__1000__0011` -- | 钻石语法: 泛型右部直接`<>`不用写类型变量 - - -## 异常处理 -- | 异常处理 - - 允许异常的`或`操作 `catch(IOException | NullPointException e)` - - final关键字: `catch (final Exception e){throw e;}` 抛出后的是原异常类型的异常而不是Exception - -## TWR -- | TWR(try with resources) -```java - // 从URL下载文件, 其中的资源都会自动关闭 - // 但是要注意发生异常后,资源也不会自动关闭, 所以确保TWR生效,正确的用法是为各个资源声明独立变量. - try(OutputStream out = new FileOutputStream(file); - InputStream is = url.openStream() - ){ - byte[] buf = new byte[1024]; - int len; - while ((len = is.read(buf)) > 0){ - out.write(buf, 0, len); - } - } -``` -- 另一个好处就是改善了错误跟踪的能力, 能够更准确地跟踪堆栈中的异常, 在Java7之前,处理资源时抛出的异常经常会被覆盖,TWR也可能会出现这种情况. -```java - try((InputStream in = getNullStream())){ - in.available(); - } -``` -- 在这种改进后的跟踪堆栈中能看到提示, 其中的NullPointException是能够抛出来看到的.没有被隐藏 - -> 目前TWR特性依靠一个接口来实现 AutoCloseable. TWR的try从句中出现的资源类都必须实现这个接口. Java7中大部分资源类都修改过 -> 但不是所有的资源类都采用了这项技术, JDBC是已经具备了这个特性. _官方提倡尽量采用TWR替代原有的方式_ - - -********************* -- | 简化变参方法调用: - - `HashMap[] array = new HashMap<>[2];` 不允许创建已知类型的泛型数组 - - 只能这样写 `HashMap array = new HashMap[2];` - - 这样的编写也只是一个敷衍, 编译器会警告: 可以将array定义为HashMap数组,但是又不能创建这个类型的实例. 所以这里只是将原始类型实例化了放进去. - - 现在能够这样编写: `public static Collection doSomething(T... entries){}` - -********************** -- [ ] 反射的简化和加强 - diff --git a/Java/AdvancedLearning/Java8.md b/Java/AdvancedLearning/Java8.md deleted file mode 100644 index dc47b29..0000000 --- a/Java/AdvancedLearning/Java8.md +++ /dev/null @@ -1,81 +0,0 @@ -`目录 start` - -- [Java8](#java8) - - [Optional](#optional) - - [Funcational](#funcational) - - [Lambda](#lambda) - - [利用Lambda开发DSL框架](#利用lambda开发dsl框架) - - [Stream](#stream) - - [集合](#集合) - - [时间处理](#时间处理) - - [Instant](#instant) - - [LocalDateTime](#localdatetime) - -`目录 end` |_2018-08-29_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Java8 -> [doc: Java8](https://docs.oracle.com/javase/8/) | [API](https://docs.oracle.com/javase/8/docs/api/) -> [Java8 JDK Readme](http://www.oracle.com/technetwork/java/javase/jdk-8-readme-2095712.html) | [Jre8 Readme](http://www.oracle.com/technetwork/java/javase/jre-8-readme-2095710.html)`有说明哪些是JRE运行不必要的文件` - -> [Java8 tools](https://docs.oracle.com/javase/8/docs/technotes/tools/)`介绍目录 bin/* 下的工具` -- [Oracle:Java8故障排除指南](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/) - -## Optional -## Funcational - -> [参考 Java8函数接口实现回调及Groovy闭包的代码示例](http://www.cnblogs.com/lovesqcc/p/6083759.html) -> [Function接口 – Java8中java.util.function包下的函数式接口](http://ifeve.com/jjava-util-function-java8/) - -- 常用函数接口主要有: - - Consumer (接收单参数无返回值的函数或lambda表达式), 方法是 void accept(T t); - - BiConsumer (接收双参数无返回值的函数或 lambda表达式),方法是 void accept(T t, U u) ; - - Function (接收单参数有返回值的函数或lambda表达式), 方法是 R apply(T t); - - BiFunction (接收双参数有返回值的函数或lambda表达式),方法是 R apply(T t, U u); - - Predicate (接收单参数返回布尔值的函数或lambda表达式),方法是 boolean test(T t); - - Supplier (无参数返回值的函数或 lambda), 方法是 T get(); -- 接受原子类型参数的函数接口,这里不一一列举了。可参考 java8 package java.util.function; - -- 为什么要使用 Function 以及闭包呢? - - 在语法上比定义回调接口、创建匿名类更加简洁; - - 尝试使用新的语言特性,理解多样化的编程思想,提升编程表达能力。 - -## Lambda -> [参考博客: 你真的了解lambda吗?一文让你明白lambda用法与源码分析 ](https://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247485682&idx=1&sn=f3fb281b49a029b607f9377853a644bf&chksm=9bd0a56aaca72c7c8beebbea8f9471446cb444bd8e1e7d21016e906d1227e8f87770e2f8f31e&mpshare=1&scene=1&srcid=0810geQnLXB2oMjfoAOEJ39L#rd) -> [参考博客: 级联 lambda 表达式的函数重用与代码简短问题](http://www.techug.com/post/java-lambda.html) - -- [ ] 排序 -> [参考博客: Java8:Lambda表达式增强版Comparator和排序](http://www.importnew.com/15259.html) - -### 利用Lambda开发DSL框架 -- [ ] 可以将mythpoi改造一下 - -## Stream -> [参考博客: Java 8 中的 Streams API 详解](https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/) - -1. filter 满足该条件的元素保留下来 -1. map 将一个流中的每个元素通过一种映射得到新的元素组成的流 -1. collect 将流收集起来 -1. forEach 遍历流 - -## 集合 -_集合的Lambda迭代方式_ -- [参考博客: List、Map的循环迭代](http://blog.csdn.net/xf_87/article/details/53931207) - -对集合中的对象进行求和 -******************************* -## 时间处理 - -### Instant -- [ ] 暂时没有学会怎么用上 - -### LocalDateTime -> 方便的新时间处理类, 用于替代Date - -```java - // LocalDateTime 获取毫秒以及秒 也可以手动指定中国的时区 ZoneOffset.of("+8") - ZonedDateTime zonedDateTime = datetime.atZone(ZoneOffset.systemDefault()); - Instant instant = zonedDateTime.toInstant(); - long seconds = instant.getEpochSecond(); - long millis = instant.toEpochMilli(); - Date date = Date.from(instant); -``` \ No newline at end of file diff --git a/Java/AdvancedLearning/JavaAnnotation.md b/Java/AdvancedLearning/JavaAnnotation.md new file mode 100644 index 0000000..685ad00 --- /dev/null +++ b/Java/AdvancedLearning/JavaAnnotation.md @@ -0,0 +1,63 @@ +--- +title: Java中的注解 +date: 2018-11-21 10:56:52 +tags: + - 注解 +categories: + - Java +--- + +**目录 start** + +1. [注解](#注解) + 1. [自定义Annotation](#自定义annotation) + 1. [读取](#读取) + +**目录 end**|_2021-05-17 00:15_| +**************************************** +# 注解 + +> 参考博客 [全面解析Java注解](http://blog.csdn.net/chenxiang0207/article/details/8193980) | [Java注解(2)-运行时框架](http://blog.csdn.net/duo2005duo/article/details/50511476) + +> 参考项目 [AnnotationDemo](https://github.com/zhuifengshen/AnnotationDemo) + +- 注解定义包含四个元注解,分别为@Target,@Retention,@Documented, @Inherited。各元注解的作用如下: + 1. **@Target** 表示该注解用于什么地方,可能的 ElemenetType 参数包括: + - `ElemenetType.CONSTRUCTOR` 构造器声明。 + - `ElemenetType.FIELD` 域声明(包括 enum 实例)。 + - `ElemenetType.LOCAL_VARIABLE` 局部变量声明。 + - `ElemenetType.METHOD` 方法声明。 + - `ElemenetType.PACKAGE` 包声明。 + - `ElemenetType.PARAMETER` 参数声明。 + - `ElemenetType.TYPE` 类,接口(包括注解类型)或enum声明。 + + 1. **@Retention** 表示在什么级别保存该注解信息。可选的 RetentionPolicy 参数包括: + - `RetentionPolicy.SOURCE` java + - `RetentionPolicy.CLASS` java + class + - `RetentionPolicy.RUNTIME` java + class + jvm, 因此可以通过反射机制读取注解的信息。 + + > 举一个例子,如@Override里面的Retention设为SOURCE,编译成功了就不要这一些检查的信息,相反,@Deprecated里面的Retention设为RUNTIME,表示除了在编译时会警告我们使用了哪个被Deprecated的方法,在执行的时候也可以查出该方法是否被Deprecated。 + + 1. **@Documented** 将此注解包含在 javadoc 中 + + 1. **@Inherited** 允许子类继承父类中的注解 + +## 自定义Annotation + +```java + @Retention(value = RetentionPolicy.RUNTIME) + public @interface GetItem { + // 设置属性内容 + String name() default "hello"; + String value(); + } +``` + +> [自定义 JSR303 Validation 注解](https://github.com/Kuangcp/JavaBase/tree/master/web/src/main/java/com/github/kuangcp/validation) + +## 读取 +> [相关代码片段](https://gitee.com/gin9/codes/s148mbplxo06qgn25d3wc23) + +- 判断是否有指定注解类型的注解 + - 在类上的注解就是得到类对象, 然后判断 isAnnotationPresent(ExcelConfig.class) + - 在方法上的注解就是得到所有方法, 属性同理 diff --git a/Java/AdvancedLearning/JavaBasicSyntax.md b/Java/AdvancedLearning/JavaBasicSyntax.md new file mode 100644 index 0000000..50d70b9 --- /dev/null +++ b/Java/AdvancedLearning/JavaBasicSyntax.md @@ -0,0 +1,644 @@ +--- +title: Java基础语法 +date: 2018-11-21 10:56:52 +tags: + - 基础 + - 语法 +categories: + - Java +--- + +💠 + +- 1. [基础语法](#基础语法) + - 1.1. [代码风格](#代码风格) + - 1.2. [结构](#结构) + - 1.2.1. [判断](#判断) + - 1.2.2. [循环](#循环) + - 1.3. [标准输入输出](#标准输入输出) + - 1.4. [Runtime](#runtime) +- 2. [数据类型](#数据类型) + - 2.1. [自动拆装箱](#自动拆装箱) + - 2.2. [基础数据类型](#基础数据类型) + - 2.2.1. [byte](#byte) + - 2.2.2. [char](#char) + - 2.2.3. [boolean](#boolean) + - 2.2.4. [short](#short) + - 2.2.5. [int](#int) + - 2.2.6. [long](#long) + - 2.2.7. [float](#float) + - 2.2.8. [double](#double) + - 2.3. [封装类型](#封装类型) + - 2.3.1. [String](#string) + - 2.3.2. [Float](#float) + - 2.3.3. [Double](#double) + - 2.3.4. [Integer](#integer) + - 2.3.5. [Long](#long) + - 2.3.6. [Boolean](#boolean) + - 2.3.7. [Void](#void) + - 2.3.8. [Decimal](#decimal) + - 2.4. [枚举类型](#枚举类型) + - 2.5. [内部类](#内部类) + - 2.6. [匿名类](#匿名类) + - 2.7. [类型强转](#类型强转) + - 2.8. [时间类型](#时间类型) + - 2.9. [非原生类型](#非原生类型) + - 2.9.1. [元组](#元组) +- 3. [运算](#运算) + - 3.1. [除法](#除法) + - 3.2. [取余](#取余) + - 3.3. [位运算](#位运算) +- 4. [类的结构](#类的结构) + - 4.1. [import](#import) + - 4.2. [修饰符](#修饰符) + - 4.2.1. [权限修饰符](#权限修饰符) + - 4.2.2. [final](#final) + - 4.2.3. [static](#static) + - 4.2.4. [abstract](#abstract) + - 4.3. [成员属性](#成员属性) + - 4.4. [方法](#方法) + - 4.4.1. [方法的传参方式](#方法的传参方式) + - 4.4.2. [equals](#equals) + - 4.4.3. [hashcode](#hashcode) + - 4.4.4. [finalize](#finalize) + - 4.5. [JavaDoc](#javadoc) +- 5. [抽象类](#抽象类) +- 6. [对象](#对象) +- 7. [继承和接口](#继承和接口) + - 7.1. [常见接口](#常见接口) + - 7.1.1. [Serializable](#serializable) +- 8. [Object](#object) + - 8.1. [VO](#vo) + - 8.2. [PO](#po) + - 8.3. [TO](#to) + - 8.4. [BO](#bo) + - 8.5. [POJO](#pojo) + - 8.6. [DAO](#dao) +- 9. [关键字](#关键字) + - 9.1. [try](#try) + - 9.2. [transient](#transient) + +💠 2024-10-08 15:07:46 +**************************************** +# 基础语法 + +![](/Java/AdvancedLearning/img/005-java-main.km.svg) + +## 代码风格 +> [Google Style Guide](https://github.com/google/styleguide) | [阿里巴巴开发手册](/Java/AlibabaJavaStandard.md) + +## 结构 +### 判断 +- if +- switch + +### 循环 +- while + - `while(true){}` + - `do{}while(true);` +- for 循环 + - `for(int a=0; i<10; i++){}` +- for each循环 + - `for(Object item:list){}` 注意 list对象如果是通过`调用一个对象的方法`返回的,那么只会调用一次该方法 + +## 标准输入输出 +> 系统标准输入 +- `Scanner scanner = new Scanner(System.in);` + +> 系统标准输出 +- `System.out.println("")` 并在末尾追加换行 + - .print() 输出, 行末不换行 + - .printf() 格式化输出, 和C语法类似 + +## Runtime +> 获取操作系统信息,JVM运行时操作 + +- 获取空闲堆内存 Runtime.getRuntime().freeMib(); +- + +*********************** +# 数据类型 +> [Primitive Data Types](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html) + +## 自动拆装箱 +> 在日常Java开发中, 基本数据类型和包装类型是可以视为等价的(唯一差别就是包装类型能表达null), 就是因为自动拆装箱的存在 + +| 基本数据类型 | 包装类型 +|:----|:----| +| byte | Byte +| char | Character +| boolean | Boolean +| short | Short +| int | Integer +| long | Long +| float | Float +| double | Double +| void | Void + +> 区别 +- 存储方式及位置的不同,基本类型是直接存储变量的值保存在堆栈中能高效的存取,包装类型需要通过引用指向实例,具体的实例保存在堆中。 +- 初始值的不同,封装类型的初始值为 null,基本类型的的初始值视具体的类型而定,比如int类型的初始值为0,boolean类型为false; +- 使用方式的不同, 在泛型场合只能使用包装类型, 基本类型无法表达 null + +> 存在的意义 +1. 基本类型无法作为对象看待, 扩充了语义 +1. 为了泛型 + +> 弊端 +1. 性能问题, 需要构造对象 + +************************ + +> 注意自动拆装箱是编译器的语法糖 +> 自动装箱都是通过包装类的 valueOf() 方法来实现的. +> 自动拆箱都是通过包装类对象的 xxxValue() 来实现的. + +## 基础数据类型 +> 八种基本数据类型 byte char boolean short int long float double + +以特定进制声明数值 0b 二进制 0 八进制 0x 十六进制 + +> [The Java™ Tutorials: Primitive Data Types](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html) + +> [参考: Java 有值类型吗?](http://www.yinwang.org/blog-cn/2016/06/08/java-value-type) + +确实, 这样来看Java没有值类型才是更统一的, 不过有没有对程序都是一样的, 因为Java没有解引用, 基本数据类型又没有成员, 所以值还是引用, 没差 + +### byte +> 字节 -128, 127, 表示 8位 一个字节 的值 + +### char + +用 2 个字节来表示 Unicode 值 取值范围: '\u0000' (or 0) , '\uffff' (or 65,535 inclusive). + +> [参考: isn't the size of character in java 2 bytes](https://stackoverflow.com/questions/5078314/isnt-the-size-of-character-in-java-2-bytes) + +### boolean +> [参考 你真的知道Java中boolean类型占用多少个字节吗?](https://www.jianshu.com/p/2f663dc820d0) + +### short + +### int +> 数值范围 -2^31,2^31-1 int占四个字节 32位 一位是符号位 + +Java8 以上可以使用无符号的 int, 值范围: 0, 2^32-1 + +### long +> 数值范围 -2^63 2^63-1 占八个字节 64位 一位是符号位 + +Java8 以上可以使用无符号的 long, 值范围: 0, 2^64-1 + +### float + +### double + +************************** +## 封装类型 +> `wrapper class` 基本类型和包装类型不能混为一谈 本质上的 class是不同的, 只不过自动拆装箱才让人感觉没差别 + +| | | +|:----|:----| +| Integer.TYPE | int.class | +| Byte.TYPE | byte.class | +| Boolean.TYPE | boolean.class | +| Double.TYPE | double.class | +| Void.TYPE | void.class | + +*************************** + +> 封装类型的缓存行为 + +Integer, 有 IntegerCache 类缓存了 [-128, 127] 范围内的值. 可以通过 `-XX:AutoBoxCacheMax` 参数修改上限值 +且 Byte Short Long Character 都有对应的缓存对象和缓存值范围, **但是只有Integer的缓存范围可变** + +Byte, Short, Long 有固定范围: [-128, 127] +Character 范围是 ‘\u0000’ 至 ‘\u007f’ 即 [0,127] + +true 和 false 也是缓存了的 + +> 可能造成的困惑 +```java + assert Integer.valueOf(1) == Integer.valueOf(1); + assert Integer.valueOf(128) != Integer.valueOf(128); +``` + +### String +> 该类是final修饰的, 原因:[知乎问题](https://www.zhihu.com/question/31345592) + +字符串对象是不可变的,这意味着一旦创建,它们的值就不能更改。 String类在技术上不是基本数据类型,但考虑到语言给予它的特殊支持,倾向于将其视为基本数据类型。 + +- 常量池 + - 存放于方法区(虚拟机规范中所声明)内,依据不同的JVM实现 + - 方法区实现的存储位置也不一致,在JDK 7以前的版本中,字符串常量池是放在永久代中的。在JDK 7中,放于堆内存中。 在JDK 8中,彻底移除了永久代,放在元空间中 + - [String:字符串常量池](https://segmentfault.com/a/1190000009888357) + +> [字符串拼接](/Java/AdvancedLearning/Basic/StringConcat.md) + +### Float +### Double +### Integer + +> [BigInteger](https://docs.oracle.com/javase/8/docs/api/java/math/BigInteger.html) + +### Long +> [Unsigned arithmetic in Java](https://www.javamex.com/java_equivalents/unsigned_arithmetic.shtml) +> [Java equivalent of unsigned long long?](https://stackoverflow.com/questions/508630/java-equivalent-of-unsigned-long-long) + +无符号Long: `Long.parseUnsignedLong();` `Long.toUnsignedString();` + +> [参考: Java 中的无符号类型是怎么回事儿?](https://www.cnblogs.com/yuanyq/p/java_unsigned_types.html) + +### Boolean +> 内含两个单例 TRUE FALSE + +但是能通过反射修改两个单例的值,导致逻辑错乱 [com.github.kuangcp.reflects.ReflectTargetObjectTest#testModifyStaticFinalBoolean](https://github.com/Kuangcp/JavaBase/blob/d43a97f458862f8ac5f9adf1b46ff98310518465/class/src/test/java/com/github/kuangcp/reflects/ReflectTargetObjectTest.java) + +### Void +- void 的包装类型, 常用于反射时对应上 返回值为void的方法(总得有个类型 Void.TYPE) 该类型在 jdk1.1就有了, 1.5出了泛型后, 又多了一个用途(因为泛型不支持原始类型) + +> The Void class is an uninstantiable placeholder class to hold a reference to the Class object representing the Java keyword void. + +> [参考: What is the need of Void class in Java](https://stackoverflow.com/questions/2352447/what-is-the-need-of-void-class-in-java) + +> [参考: Uses for the Java Void Reference Type?](https://stackoverflow.com/questions/643906/uses-for-the-java-void-reference-type) + +1. 在AOP中, 增强根据切点的返回值类型, 做出不同的逻辑, 有可能用到Void +1. Void 强调 the nothing, null 强调 nothing +1. Void 作为方法的返回值时, 只能返回 null + +- 案例: + - Future + - ResponseEntity + - A `Consumer` can be viewed as a `Function`, 没有返回值,只有入参 + - A `Supplier` can be viewed as a `Function`, 没有入参,只有返回值 + +> [official api](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) + +> When you use the visitor pattern it can be cleaner to use Void instead of Object when you want to be sure that the return value will be null. Example: +```java + public interface LeavesVisitor{ + OUT visit(Leaf1 leaf); + OUT visit(Leaf2 leaf); + } +``` + +> When you will implement your visitor you can explicitly set OUT to be Void so that you know your visitor will always return null, instead of using Object +```java + public class MyVoidVisitor implements LeavesVisitor{ + Void visit(Leaf1 leaf){ + //...do what you want on your leaf + return null; + } + Void visit(Leaf2 leaf){ + //...do what you want on your leaf + return null; + } + } +``` + +### Decimal +通常进行浮点数间运算的时候,如果没有太在意小数位的精度问题,可以直接使用Double,如果是金额之类的业务值则需要使用到BigDecimal + +注意事项: +- 构造方式 + - double入参构造器容易出现问题,例如 new BigDecimal(0.1) 最终不是期望的0.1 因为浮点数表示0.1是不精确的,应该改成 new BigDecimal("0.1") +- 计算 + - 参与计算的项不能出现null + - BigDecimal 计算过程需要使用BigDecimal的方法,而不是直接进行加减乘除。例如 `new BigDecimal(a/b);` + - divide方法注意无限循环小数(设置精度规模规避)以及除数为0的情况 +- 值比较 + - 不能直接用 == 或者 equals,而是走 Comparable接口方式 compareTo() + +- 常见值优先使用BigDecimal的枚举,例如BigDecimal.ZERO +************************ + +## 枚举类型 +> [official doc: enum](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html) + +枚举类的构造器必须是 private 或者 package private (也就是缺省) + +> [参考: Java 语言中 Enum 类型的使用介绍](https://www.ibm.com/developerworks/cn/java/j-lo-enum/index.html) + +从上面的定义形式来看,似乎 Java 中的枚举类型很简单,但实际上 Java 语言规范赋予枚举类型的功能非常的强大,它不仅是简单地将整形数值转换成对象,而是将枚举类型定义转变成一个完整功能的类定义。 + +- 简单定义 + - `public enum Color {RED, GREEN, GRAY, BLUE, YELLOW, WHITE, PURPLE, BLACK}` + +> 简单单例 +```java + public enum Tool{ + INSTANCE(12); + private int num; + Tool(int num){ + this.num = num; + } + public getNum(){ + return num; + } + } + // 使用的时候 + Tool.INSTANCE.getNum(); +``` + +> [参考: 关于java枚举类型的疑问 ](https://segmentfault.com/q/1010000000306839) +> [compilation-error-switch-with-enum](https://stackoverflow.com/questions/5551568/compilation-error-switch-with-enum) + +*************************** +## 内部类 +> 可看做一种特殊的成员变量, 其特征和成员属性是一致的 + +- https://www.tutorialspoint.com/java/java_innerclasses.htm +- https://www.geeksforgeeks.org/anonymous-inner-class-java/ + +## 匿名类 + +************************** + +## 类型强转 +> 数学运算时,数据类型自动往大数据类型转: int float double + +- Double -> int 直接(int)num; +- int/Integer -> Long 不能隐式转, 需要 Long.valueOf() + +********************** + +## 时间类型 +> java.time 包 + +> [Java8 JavaDoc: DateTimeFormatter](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)`注意格式化中字符的准确含义` + +1. 最早常用是 Date 然后 Calendar 目前Java8: Instant LocalDateTime Duration ... + +## 非原生类型 +### 元组 +> Groovy 中实现了一个元组系统 Tuple + +元组在语言层面, 适合应用于组合少量的多元的数据, 在多种语言中元组都是不可变的 + +优点: +1. 元组可以同时存储多种类型元素,且元素类型固定,以保证数据安全, 编译器会对赋值参数类型进行检查 +1. 元组的元素个数固定,不允许增加、删除,编译器会严格校验赋值参数个数 +1. 无需定义key,但是必要时可以为数据命名,方便数据访问 +1. 适合同时遍历多元数据 + +缺点: +1. 不适合存储大量数据,因为元组不支持append、remove等方法 +1. 考虑到工程实际情况,后端使用的语言可能不支持元组,需要转换为其他格式 + +************************ + +# 运算 +## 除法 +整数相除会向下取整, 浮点数相除则是正常数学运算 + +- `3 / 2 => 1` +- `3 / 2.0 => 1.5` + +## 取余 +> a % b => `a - (a / b) * b` + +取模运算(Modulo Operation)和取余运算(Complementation)两个概念有重叠的部分但又不完全一致。 +主要的区别在于对负整数进行除法运算时操作不同。取模主要是用于计算机术语中。取余则更多是数学概念。 + +对于整型数a,b来说,取模运算或者求余运算的方法都是: +1. 求整数商: c = a / b; +2. 计算模或者余数: r = a - c * b. + +求模运算和求余运算在第一步不同: 取余运算在取c的值时,向0 方向舍入(fix()函数);而取模运算在计算c的值时,向负无穷方向舍入(floor()函数)。 + +例如计算:-7 Mod 4 : a = -7;b = 4; +- 第一步:求整数商c,如进行求模运算c = -2(向负无穷方向舍入),求余c = -1(向0方向舍入); +- 第二步:计算模和余数的公式相同,但因c的值不同,求模时r = 1,求余时r = -3。 + +归纳: +当a和b符号一致时,求模运算和求余运算所得的c的值一致,因此结果一致。 +当符号不一致时,结果不一样。求模运算结果的符号和b一致,求余运算结果的符号和a一致。 + +另外各个环境下%运算符的含义不同,比如c/c++,java 为取余,而python则为取模。 + +## 位运算 +> [Bitwise and Bit Shift Operators](https://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html) + +当length是2的n次方: hash%length==hash&(length-1) + +****************** +# 类的结构 +## import +> import a.b.C; import a.b.*; 的区别在于前者是完整包路径,后者是通配,但是通常编译的结果是一样的 +> 但是后者会提高编译期成本,还可能造成 同名类在不同包混淆的情况。这也是为什么后者不建议用的原因 + +## 修饰符 + +### 权限修饰符 +> [参考: java 权限修饰符](https://blog.csdn.net/yan8024/article/details/6426451) + +| 权限修饰符 | 范围 | 注释 | +|:----|:----|:----| +| public | 任意范围 | | +| protected | 子类/同包 | 子类是可以在任意包的 | +| package private(缺省) | 同包 | 顾名思义就是包级别的private | +| private | 当前类 | 当前类级别private, 内部类从属于当前类 | + +> 同包是指 `package xxx;` 语句完全一样, 而 `package a;` 与 `package a.b;` 不是同包 + +### final + +> [What Are Compile-Time Constants in Java](https://www.baeldung.com/java-compile-time-constants) + +### static + +### abstract + +*************** + +## 成员属性 +作为Java的bean, 或者大多数情况下, 属性都是私有的, 然后提供setter getter 方法,而且 一般来说, setter和getter方法是不能包含逻辑的, 也就是简单的赋值 取值 +乍一看相比于C语言, 似乎这是多此一举, 但是注意面向对象思想, 一个对象对外提供的应该只是行为, 具有较强的语义性, 什么对象执行了什么方法, 而直接引用就可能将对象属性和静态属性混淆 + +## 方法 +- 方法签名 [Defining Methods](https://docs.oracle.com/javase/tutorial/java/javaOO/methods.html) + - 作用域 + - 泛型列表 可选 + - 返回类型 + - 方法名 + - 参数列表 可选 + - 异常列表 可选 + - 方法体 + +>1. 关于方法上参数使用 final 修饰的作用: 明确该方法内部不能对参数进行修改 + +### 方法的传参方式 +> [Java 有值类型吗?](http://www.yinwang.org/blog-cn/2016/06/08/java-value-type)`仅作思考` +> [这一次,彻底解决Java的值传递和引用传递](https://juejin.im/post/5bce68226fb9a05ce46a0476)`C++ C# 都支持,C Java 仅支持值传递` + +- `实参`:方法调用前就完成初始化或为null,方法调用时传入,在Java中就是调用时被复制 +- `形参`: 声明在方法参数列表内,仅在函数调用的时候进行申请内存空间,函数调用结束就释放该内存空间 +*** +- `值类型`: 存储在线程栈中的数据类型,如int型, 以及对象的引用变量 +- `引用类型` :存储在堆中数据的数据类型,如Date型,或自定义class对象; +*** +- `值传递`: 每次传递变量时,都是对栈里的原始值进行拷贝 + - 如栈地址0001处存放一个整数值4,值传递时,先copy整数值4,将之存放在栈地址0002处的内存空间,再将栈地址0002作为形参传入方法; +- `引用传递`: 每次传递变量时,直接传递栈地址,如栈地址0001处存放一个整数值4,引用传递时,直接传递栈地址0001,而不做复制。 + +> 总结: Java只有值传递 +- 值传递 会创建副本(copy) 所以 函数无法改变实参对象的`值`(值类型) + - 但是如果传入的是引用类型 会复制一份地址值,就可以通过地址值更改实参对象的`成员属性值` + +注意 如果是 引用传递 不创建副本, 实参和形参地址是一致,所以 函数中一定可以改变原始对象 + +### equals +> [Java提高篇——equals()与hashCode()方法详解](http://www.cnblogs.com/Qian123/p/5703507.html) +> [参考: equals()和hashCode()区别?](https://www.cnblogs.com/jesonjason/p/5492208.html) + +- Object中equals是比较内存地址, hashcode是比较散列函数的值, 后者性能更好,但是可能出现哈希碰撞 +- equals相等hashcode一定相等,equals不等 hashcode可能一致可能不一致 + +> 重写equals方法 +> [参考: 关于重写entity的equals()和hashCode()方法的必要性](https://blog.csdn.net/hiroyuki/article/details/6247244) + +Double, Integer, Math, String 都是重写了equals方法, 因此比较的都是值不是内存地址 + +### hashcode + +java.lnag.Object中对hashCode的约定: +1. 在一个应用程序执行期间,如果一个对象的equals方法做比较所用到的信息没有被修改的话,则对该对象调用hashCode方法多次,它必须始终如一地返回同一个整数。 +2. 如果两个对象根据equals(Object o)方法是相等的,则调用这两个对象中任一对象的hashCode方法必须产生相同的整数结果。 +3. 如果两个对象根据equals(Object o)方法是不相等的,则调用这两个对象中任一个对象的hashCode方法,不要求产生不同的整数结果。但如果能不同,则可能提高散列表的性能。 + +### finalize + +************************ + +## JavaDoc + +- @author +- @version +- @param +- @return +- @exception/@throws +- @see +- @since +- @serial/@serialField/@serialData +- @deprecated + +- {@link BurgersManager} 指向一个类 +- {@link BurgersManager burgers manager} 指向带有标签的类 +- {@link #eat(Burger, boolean)} 指向此类中的某个方法 +- {@link #eat(Burger, boolean) eat} 指向此类中带有标签的某个方法 +- {@link BurgersManagers#eat(Burger, boolean)} 指向其他类中的某个方法 +- {@link BurgersManagers#eat(Burger, boolean) burgers manager eat} 指向其他带有标签的类的某个方法 + +************************ + +# 抽象类 +1. Concrete and Abstract Class + +************************ + +# 对象 +> [参考: 计算Java对象内存大小](https://www.cnblogs.com/E-star/p/10222250.html) + +多个类的继承中初始化块、静态初始化块、构造器的执行顺序为:父类静态块 ——> 子类静态块 ——> 父类代码块 ——> 父类构造器 ——> 子类代码块 ——> 子类构造器 + +注意 static 静态代码块的执行是发生在该类创建对象的时候,而不是Golang和Python的init方法`import就会执行`。 + +************************ + +# 继承和接口 +> [Lesson: Interfaces and Inheritance](https://docs.oracle.com/javase/tutorial/java/IandI/index.html) + +## 常见接口 + +### Serializable +> 序列化接口 + +1. serialVersionUID + - 该属性可显式声明,若没有则编译器会 根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段 + - 该属性的作用:如果显式声明且值保持一致,那么类的变动(增加或删除属性)能被兼容(改动的属性忽略或没有值)),如果没有设置则会导致反序列化异常 + + +************************ + +# Object + +> [参考: java的(PO,VO,TO,BO,DAO,POJO)解释](http://www.cnblogs.com/yxnchinahlj/archive/2012/02/24/2366110.html) | [VO DAO BO 等缩写的意义](https://zhuanlan.zhihu.com/p/35762537?group_id=969493512006373376) + +- [ ] 原因? 场景是 类继承了一个实现了自定义接口的自定义抽象类 + +Warning:(18, 1) java: Generating equals/hashCode implementation but without a call to superclass, even though this class does not extend java.lang.Object. If this is intentional, add '@EqualsAndHashCode(callSuper=false)' to your type. + +## VO +> view object 前端展示对象 +1. 前后端分离项目中VO代表给前端展示接口使用 + +************************ + +> (value object) 值对象 +1. 使用new关键字创建的, 由GC回收的, +2. VO是值对象, 业务对象, 存活在业务层的, 是业务逻辑使用的 + - 它存在的目的就是为数据提供一个生存的地方 +3. VO的属性是根据当前业务的不同而不同的 + - 也就是说,它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。 + +## PO +> (persistant object) 持久对象 +1. PO则是向数据库中添加新数据时创建,删除数据库中数据时削除的。 + - 并且它只能存活在一个数据库连接中,断开连接即被销毁。 +2. PO是持久化对象, 是有状态的, 每个属性代表其当前状态, 他是物理数据的对象表示 + - 使用它能够让我们的程序与物理数据解耦,并且可以简化对象数据与物理数据之间的转换。 +3. PO的属性是跟数据库表的字段一一对应的。 +4. PO对象需要实现序列化接口 + +> 首先说PO和VO吧,它们的关系应该是相互独立的,一个VO可以只是PO的部分,也可以是多个PO构成,同样也可以等同于一个PO(当然我是指他们的属性)。 +正因为这样,PO独立出来,数据持久层也就独立出来了,它不会受到任何业务的干涉。又正因为这样,业务逻辑层也独立开来,它不会受到数据持久层的影响,业务层关心的只是业务逻辑的处理,至于怎么存怎么读交给别人吧! +不过,另外一点,如果我们没有使用数据持久层,或者说没有使用hibernate,那么PO和VO也可以是同一个东西,虽然这并不好。 + +## TO +> (transfer Object) 数据传输对象 +- 在应用程序不同tie(关系)之间传输的对象 +- 例如 RPC 接口中的对象 UserTO + +## BO +> (business object) 业务对象 +- 从业务模型的角度看,见UML元件领域模型中的领域对象。封装业务逻辑的java对象,通过调用DAO方法,结合PO,VO进行业务操作。 +- 它装满了业务逻辑的处理,在业务逻辑复杂的应用中有用。 + +## POJO +> [Wikipedia: POJO](https://en.wikipedia.org/wiki/Plain_old_Java_object) `Plain Old Java Object` + +> (plain ordinary java object) 简单无规则java对象 +- 纯的传统意义的java对象。就是说在一些Object/Relation Mapping工具中,能够做到维护数据库表记录的persisent object完全是一个符合Java Bean规范的纯Java对象,没有增加别的属性和方法。我的理解就是最基本的Java Bean,只有属性字段及setter和getter方法! + +## DAO +> (data access object) 数据访问对象 +- 通常和PO结合使用,DAO中包含了各种数据库的操作方法 + +************************* + +# 关键字 +> Java关键字和保留字 + +||||||||| +|:----|:----|:----|:----|:----|:----|:----|:----| +| abstract | class | extends | implements | null | strictfp | true +| assert | const | false | import | package | super | try +| boolean | continue | final | instanceof | private | switch | void +| break | default | finally | int | protected | synchronized | volatile +| byte | do | float | interface | public | this | while +| case | double | for | long | return | throw +| catch | else | goto | native | short | throws +| char | enum | if | new | static | transient + +## try +> try catch finally + +- [StackOverFlow: Try-catch-finally in java](https://stackoverflow.com/questions/7143788/try-catch-finally-in-java) + +- There are at least 3 OTHER cases where the finally block is not executed: + 1. if the try block or a catch block goes into an infinite loop, or blocks for ever + 1. if something (e.g. a JNI bug) causes the JVM to crash + 1. if there is a machine outage (power failure, hardware error, etc). + +## transient +> 该关键字修饰的属性 在对象序列化时不参与序列化 diff --git a/Java/AdvancedLearning/JavaClass.md b/Java/AdvancedLearning/JavaClass.md new file mode 100644 index 0000000..c6a8046 --- /dev/null +++ b/Java/AdvancedLearning/JavaClass.md @@ -0,0 +1,222 @@ +--- +title: Java的字节码 +date: 2018-11-21 10:56:52 +tags: + - 字节码 +categories: + - Java +--- + +💠 + +- 1. [字节码以及类加载](#字节码以及类加载) +- 2. [编译优化](#编译优化) + - 2.1. [JIT 即时编译](#jit-即时编译) +- 3. [字节码](#字节码) + - 3.1. [字节码相关框架](#字节码相关框架) + - 3.2. [常量池](#常量池) +- 4. [类加载机制](#类加载机制) + - 4.1. [类加载器](#类加载器) + - 4.1.1. [特殊场景](#特殊场景) + - 4.1.1.1. [Tomcat](#tomcat) + - 4.2. [加载和连接](#加载和连接) + - 4.3. [方法句柄](#方法句柄) +- 5. [Agent](#agent) +- 6. [反编译](#反编译) +- 7. [热部署](#热部署) + +💠 2024-06-13 11:01:29 +**************************************** +# 字节码以及类加载 +> [个人相关代码](https://github.com/Kuangcp/JavaBase/tree/master/class) + + +************************ +> 书籍 +- 深入理解Java虚拟机 周志明 +- Java 虚拟机字节码 从入门到实战 吴就业 + +# 编译优化 +> 由源文件 *.java 编译成 *.class 文件这个过程中做的调优 + +类中定义的常量 如果是字面值, 其他引用这个常量的地方编译后会被替换成字面值, 该常量属性的 get 方法也是直接返回字面值 +字面值就是无需运算的值, 而不是表达式 例如 `final int num = 2;` 反例 `final int num = 3>2?1:2;` + +## JIT 即时编译 +目前有三种:C1 C2 Graal + +| 类型 | JVM参数 | 特点 | +|:---|:---|:---| +| C1 | -client | 编译耗时短 | +| C2 | -server | 编译耗时长执行效率好`需要收集运行期profile信息来辅助编译也就是PGO` | +| Graal | | | + +> 注意 自JDK8起默认开启分层编译`C1 C2混用` -client -server参数**无效** + +> [Graal Compiler](https://www.graalvm.org/latest/reference-manual/java/compiler/) +> [Deep Dive Into the New Java JIT Compiler – Graal](https://www.baeldung.com/graal-java-jit-compiler) + +************************ + +# 字节码 +> [参考: 学会阅读Java字节码](https://www.cnblogs.com/beautiful-code/p/6425376.html) +> [参考: 字节码增强技术探索](https://tech.meituan.com/2019/09/05/java-bytecode-enhancement.html) + +字节码是程序的中间表达形式,源码和机器码之间的产物 字节码是由源文件执行javac产生的 + +某些高级语言特性(语法糖)在字节码中会进行简化,例如循环结构,会转换成为分支指令 + +- 每个操作都由一个字节表示,因此叫做字节码 +- 字节码是一种抽象表示方法 字节码进一步编译得到机器码 + +- `javap -c -p class文件` 反编译字节码文件,-p 能看到私有属性 + - 输出所有的属性以及类的定义信息 + - 静态块 + - 构造方法 + - 方法信息 + - 静态属性信息 + - 静态方法信息 + +## 字节码相关框架 +> [Apache bcel](http://commons.apache.org/proper/commons-bcel/index.html) + +asm +javassist +****************** + +## 常量池 +> 常量池是为类文件中的其他常量元素提供快捷访问方式的区域。对于JVM来说常量池相当于符号表 +> [参考博客](http://www.cnblogs.com/LeonNew/p/5314731.html) + +- `javap -v class文件` 输出很多额外信息,# 开头的就是常量池信息 +![图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Java7Developer/p120.jpg) + +- [ ] 理解常量池 以及使用场景 + +************************ + +# 类加载机制 +- 类的生命周期 + - `加载 Loading` -> `验证 Verification` -> `准备 Preparation` -> `解析 Resolution` -> `初始化 Initialization` -> `使用 Using` -> `卸载 Unloading` + - 验证、 准备、 解析,统称为 链接 Linking + +## 类加载器 +> [参考: 一文带你深扒ClassLoader内核,揭开它的神秘面纱! ](https://www.cnblogs.com/wmyskxz/p/13575224.html#_label4)`深入源码,举例清晰` + +> 双亲委派模型(`parents delegation model`) 实现代码:`java.lang.ClassLoader#loadClass(java.lang.String, boolean)` +> 其工作原理是,如果一个类加载器收到了类加载请求(只讨论首次加载,已经加载过的会走缓存), 它并不会自己先去加载,而是委托给父类的加载器去执行 +> 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器 +> 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载 + +- Java平台经典类加载器层级: + 1. `BootStrap ClassLoader` 根(引导)加载器:通常在VM启动后不久就实例化,最顶层的加载类,主要加载 核心类库 并且不做验证工作 + - 加载目录 `%JRE_HOME%\lib` 下的rt.jar、resources.jar、charsets.jar 和 class 文件 + 2. `Extendsion ClassLoader` 扩展类加载器:加载安装时自带的标准扩展,一般包括安全性扩展 + - 加载目录 `%JRE_HOME%\lib\ext` 下的 jar 包和 class 文件。 + 3. `Application ClassLoader` 应用或系统类加载器:应用最广泛的类加载器,负责加载应用类(当前应用的 classpath 的所有类) + 4. `自定义ClassLoader` 自定义类载器 + +> 注意: +>1. 例如在读取类路径下文件时,可以通过 `classA.getClassLoader().getResourceAsStream("app.properties")` 但是如果类classA对象是由 BootStrap 类加载器加载的, getClassLoader() 将返回 null +>1. 当出现jar包多版本时,先加载了其中一个版本就不会加载另一个版本,而这个加载顺序往往是由操作系统的文件排序决定的 [相关案例](/Java/Blog/Java-ClassLoad-Confuse.md) + +### 特殊场景 +#### Tomcat +> [参考: 图解Tomcat类加载机制](https://www.cnblogs.com/aspirant/p/8991830.html) + +- *CommonClassLoader* Tomcat最基本的类加载器,加载路径`/common/*`中的class可以被Tomcat容器本身以及各个Webapp访问; +- *CatalinaClassLoader* Tomcat容器私有的类加载器,加载路径`/server/*`中的class对于Webapp不可见; +- *SharedClassLoader* 各个Webapp共享的类加载器,加载路径`/shared/*`中的class对于所有Webapp可见,但是对于Tomcat容器不可见; +- *WebappClassLoader* 各个Webapp私有的类加载器,加载路径`/WebApp/WEB-INF/*`中的class只对当前Webapp可见; + +其中WebApp类加载器和Jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个JSP文件对应一个Jsp类加载器。 + +WebApp类加载器就为了类隔离而违背了双亲委派模型,仅自身负责加载类,不向上传递 + +************************ + +## 加载和连接 +> [参考: 第七章.虚拟机类加载机制](http://ifeve.com/%e7%ac%ac%e4%b8%83%e7%ab%a0-%e8%99%9a%e6%8b%9f%e6%9c%ba%e7%b1%bb%e5%8a%a0%e8%bd%bd%e6%9c%ba%e5%88%b6/) + +`加载` +- 这个过程就是读取字节码文件,创建一个字节数组装在这些内容,加载结束后这个对象还不能直接调用 + +`连接` +- 加载完成后,类必须连接起来,分为三步:验证,准备,解析。 + - 验证: + - 验证文件的合理性,完整性检查,检查常量池,方法的字节码检查。主要的: + - 是否所有方法都遵守访问控制关键字的限定 + - 方法调用的参数个数和静态类型是否正确 + - 确保字节码不会试图滥用堆栈 + - 确保变量使用之前被正确初始化了 + - 检查变量是否仅被赋予恰当类型的值 + - 准备: + - 分配内存,准备初始化类中的静态变量,但不会现在就初始化,也不会执行任何VM字节码 + - 解析: + - 促使VM检查类文件中所引用的类型是不是都是已知的类型。如果有运行时有未知的类型,那又要引发一次类加载过程 + - 当需要加载的类全部加载解析完毕后,VM就可以初始化最初那个加载的类了。 + - 这时所有的静态变量都可以进行初始化,所有静态代码块都会运行,这一步完成后,类就能使用了 + +## 方法句柄 +> 主要用于反射 用到再学 + +![图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Java7Developer/p118.jpg) + +************************ +# Agent +> [JDK: Interface Instrumentation](https://docs.oracle.com/en/java/javase/21/docs/api/java.instrument/java/lang/instrument/Instrumentation.html) + +> [Guide to Java Instrumentation](https://www.baeldung.com/java-instrumentation) +> [ Java Agent 探针技术](https://juejin.cn/post/7086026013498408973) + +Java Agent 主要有以下功能 +- Java Agent 在加载 Java 字节码之前拦截并对字节码进行修改; +- Java Agent 在 Jvm 运行期间修改已经加载的字节码; + +Java Agent 的应用场景 + +| 能力 | 案例 | +|:----|:----| +| IDE的调试功能 | Eclipse、IntelliJ IDEA ; +| 热部署功能 | JRebel、XRebel、spring-loaded; +| 各种线上诊断工具 | Btrace、Greys, Arthas; +| 各种性能分析工具 | Visual VM、JConsole 等; +| 全链路性能检测工具 | Skywalking、Pinpoint等; + + +> [agent](https://github.com/yxkong/agent)`线程池监控` + +************************ + +# 反编译 +> [JD](http://java-decompiler.github.io/) +> [https://varaneckas.com/jad/](https://varaneckas.com/jad/) +> [Java-Class-Viewer](https://www.codeproject.com/Articles/35915/Java-Class-Viewer) +> [classpy](https://github.com/zxh0/classpy) + +************************ + +# 热部署 +> 通过替换 class 实现不停机热更新 + +> [Spring hot swapping](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-hotswapping.html) + +1. Instrumentation +1. 自定义类加载器 +1. OSGI 热插拔接口 + +[Instrumentation 新功能](https://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html) +[基于Java Instrument的Agent实现](https://www.jianshu.com/p/b72f66da679f) +[Java 5 特性 Instrumentation 实践](https://www.ibm.com/developerworks/cn/java/j-lo-instrumentation/) +[java组件中的热插拔(osgi)](https://blog.csdn.net/javierhui111/article/details/3830833) +[agentmain 方式 ](https://www.cnblogs.com/cm4j/p/hot_deploy.html) + +相关项目: + +[game-hot-update](https://github.com/youxijishu/game-hot-update) | [游戏服务器之Java热更新](https://www.cnblogs.com/wgslucky/p/9127681.html) +[groovy hotswap demo](https://github.com/chaopeng/groovy-hotswap-demo) + +> [Java系列 | 远程热部署在美团的落地实践](https://tech.meituan.com/2022/03/17/java-hotswap-sonic.html)`未开源` + +************************ + diff --git a/Java/AdvancedLearning/JavaCollection.md b/Java/AdvancedLearning/JavaCollection.md new file mode 100644 index 0000000..c487570 --- /dev/null +++ b/Java/AdvancedLearning/JavaCollection.md @@ -0,0 +1,114 @@ +--- +title: Java的集合 +date: 2018-11-21 10:56:52 +tags: + - 数据结构 +categories: + - Java +--- + +💠 + +- 1. [JDK中的集合](#jdk中的集合) + - 1.1. [集合继承和实现关系](#集合继承和实现关系) + - 1.2. [Iterator](#iterator) + - 1.2.1. [规避 ConcurrentModificationException](#规避-concurrentmodificationexception) + - 1.3. [Map](#map) + - 1.3.1. [HashMap](#hashmap) + - 1.3.2. [TreeMap](#treemap) + - 1.4. [List](#list) + - 1.5. [Set](#set) +- 2. [第三方开源集合框架](#第三方开源集合框架) + - 2.1. [fastutil](#fastutil) + - 2.2. [Koloboke](#koloboke) + - 2.3. [Trove](#trove) + +💠 2024-07-13 00:44:21 +**************************************** +# JDK中的集合 + +[ Java集合必会14问(精选面试题整理)](https://www.cnblogs.com/wmyskxz/p/9381848.html) + +## 集合继承和实现关系 + +- Collection 接口 + - List 接口 + - ArrayList + - LinkedList _也实现了Queue接口_ 双向链表实现 + - Vector + - Set 接口 _内容不允许重复_ + - SortedSet 接口 _单值排序接口_ + - TreeSet + - Queue 接口 _队列接口_ + - PiorityQueue + - Dueue 双端队列 + +- Map接口 + - HashMap _无序, key不重复_ + - HashTable _无序, key不重复_ + - TreeMap _按key排序, key不重复_ + - IdentityMap _key可重复_ + - WeakHashMap _弱引用Map集合_ + +## Iterator +> 迭代器 + +### 规避 ConcurrentModificationException +1. 使用迭代器进行删除, 或者Java8的removeIf +1. 使用没有这个特性的容器,例如: LinkedBlockingQueue + 1. 关联此特性的容器可以查看 java.util.ConcurrentModificationException 的JavaDoc + +************************ + +## Map +> HashMap 键能为null, HashTable则不可以, 而且HashTable是线程安全的(依靠 synchronized 关键字实现) + +> [参考: Java Map 集合类简介 ](https://www.oracle.com/technetwork/cn/articles/maps1-100947-zhs.html) + +### HashMap + +### TreeMap +> [参考: TreeMap 红黑树算法实现](https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html) + +************************ + +## List +> interface + +包括的方法有: +![List method](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Java/Collection/List/List.png) + +List接口有众多实现, 最常用的 ArrayList LinkedList + +****************************** + +[stackoverflow: list add then unsupportedoperationexception](https://stackoverflow.com/questions/5755477/java-list-add-unsupportedoperationexception) +> 有时候会使用 Arrays.asList() 或者 Collections.singletonList() 来快速生成 List +> 但是 这两个生成的实例都是返回 AbstractList 的实现类, 其 add remove 方法是没有实现的, 如果调用了就会抛出异常 + +```java + public void add(int index, E element) { + throw new UnsupportedOperationException(); + } +``` +> 这是因为, 这个类设计就是采用的定长数组来实现List, 所以不能对其中元素进行更改 类似的还有 `Collections.emptyXxx()` + +****************************************** +## Set +- Set是无序的,但是StringRedisTemplate的对象操作返回的set竟然是有序的 + - 因为有一个类是SortSet,顾名思义,所以是有序的,要继续多学习和使用Java原生的集合对象了 + +> [3分钟搞掂Set集合](https://segmentfault.com/a/1190000014391402?utm_source=channel-hottest) + +************************ + +# 第三方开源集合框架 + +## fastutil +> [Github](https://github.com/vigna/fastutil) + +## Koloboke +> [Github](https://github.com/leventov/Koloboke) + +## Trove +> [BitBucket](https://bitbucket.org/trove4j/trove/src/master/) diff --git a/Java/AdvancedLearning/Concurrents.md b/Java/AdvancedLearning/JavaConcurrency.md similarity index 55% rename from Java/AdvancedLearning/Concurrents.md rename to Java/AdvancedLearning/JavaConcurrency.md index 9d38608..2373326 100644 --- a/Java/AdvancedLearning/Concurrents.md +++ b/Java/AdvancedLearning/JavaConcurrency.md @@ -1,47 +1,87 @@ -`目录 start` - -- [Java并发](#java并发) - - [Java内存模型](#java内存模型) - - [【理论知识】](#理论知识) - - [可能的问题](#可能的问题) - - [好的习惯](#好的习惯) - - [【块结构并发】 Java5之前](#块结构并发-java5之前) - - [synchronized](#synchronized) - - [正确使用锁](#正确使用锁) - - [volatile](#volatile) - - [正确使用](#正确使用) - - [【现代并发】JUC](#现代并发juc) - - [概念](#概念) - - [CAS指令](#cas指令) - - [原子类](#原子类) - - [读写锁](#读写锁) - - [具体实现](#具体实现) - - [线程锁](#线程锁) - - [CountDownLatch 锁存器](#countdownlatch-锁存器) - - [ConcurrentHashMap](#concurrenthashmap) - - [CopyOnWriteArrayList](#copyonwritearraylist) - - [【Queue】](#queue) - - [BlockingQueue](#blockingqueue) - - [TransferQueue](#transferqueue) - - [【控制执行】](#控制执行) - - [任务建模](#任务建模) - - [ScheduleThreadPoolExecutor](#schedulethreadpoolexecutor) - - [【分支合并框架】](#分支合并框架) - - [【Java内存模型】](#java内存模型) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Java并发 +date: 2018-11-21 10:56:52 +tags: + - Concurrency +categories: + - Java +--- + +💠 + +- 1. [Java并发](#java并发) + - 1.1. [JMM Java内存模型](#jmm-java内存模型) + - 1.2. [理论知识](#理论知识) + - 1.2.1. [可能的问题](#可能的问题) + - 1.2.2. [好的习惯](#好的习惯) +- 2. [关键字](#关键字) + - 2.1. [synchronized](#synchronized) + - 2.1.1. [正确使用](#正确使用) + - 2.2. [volatile](#volatile) + - 2.2.1. [正确使用](#正确使用) +- 3. [现代并发JUC包](#现代并发juc包) + - 3.1. [概念](#概念) + - 3.1.1. [读写锁](#读写锁) + - 3.2. [实现类](#实现类) + - 3.2.1. [原子类](#原子类) + - 3.2.2. [Lock](#lock) + - 3.2.3. [CountDownLatch](#countdownlatch) + - 3.2.4. [CyclicBarrier](#cyclicbarrier) + - 3.2.5. [Semaphore](#semaphore) + - 3.2.6. [Phaser](#phaser) + - 3.2.7. [Exchanger](#exchanger) + - 3.2.8. [ConcurrentHashMap](#concurrenthashmap) + - 3.2.9. [ConcurrentSkipListMap](#concurrentskiplistmap) + - 3.2.10. [CopyOnWriteArrayList](#copyonwritearraylist) +- 4. [结构化并发](#结构化并发) +- 5. [实践](#实践) + - 5.1. [任务建模](#任务建模) + - 5.2. [Queue](#queue) + - 5.2.1. [BlockingQueue interface](#blockingqueue-interface) + - 5.2.1.1. [TransferQueue interface](#transferqueue-interface) + +💠 2024-09-25 13:38:51 **************************************** # Java并发 -> [个人相关代码](https://github.com/Kuangcp/JavaBase/tree/master/src/main/java/com/concurrents) +> [个人相关代码](https://github.com/Kuangcp/JavaBase/tree/concurrency) + > 主要知识来源 Java程序员修炼之道 | [并发编程网](http://ifeve.com/) -> 该模块最早在1.5引入,由[Doug Lea](http://g.oswego.edu/)开发 | [doug lea博客中文版](http://ifeve.com/doug-lea/) -> [参考博客: 并发编程 ](http://www.jdon.com/concurrency.html) -> [参考博客: 不可变真的意味线程安全?](http://www.jdon.com/concurrent/immutable.html) +> 该模块最早在JDK1.5引入,由 [Doug Lea](http://g.oswego.edu/) 开发 | [doug lea博客中文版](http://ifeve.com/doug-lea/) + +> [参考: 并发编程 ](http://www.jdon.com/concurrency.html) +> [参考: 不可变真的意味线程安全?](http://www.jdon.com/concurrent/immutable.html) +> [Java Concurrency and Multithreading Tutorial](http://tutorials.jenkov.com/java-concurrency/index.html) + +![](/Java/AdvancedLearning/Concurrency/img/001-concurrency-base.km.svg) + +************************ + +## JMM Java内存模型 +> Java Memory Model + +> [Java内存模型是什么](https://mp.weixin.qq.com/s?__biz=MjM5OTMyNzQzMg==&mid=2257483738&idx=1&sn=856847463cc602962d027aa80dd55a6f&chksm=a447f47d93307d6b4f7bc74bf5dc502d306c01915c10da7ee924926dd8258c241b4265c23f4e&mpshare=1&scene=1&srcid=#rd)`借助CPU的多级缓存的概念理解线程间内存模型` + +- JMM 的主要规则: + - 在监测对象上的解锁操作与后续的所操作之间存在同步约束关系 + - 对易失性变量的写入与后续对该变量的读取之间存在同步约束关系 + - 如果动作A受到动作B的同步约束,则A在B之前发生 + - 如果在程序中的线程内A出现B之前,则A在B之前发生 + - 前两个简称为先存后取 +- 敏感行为: + - 构造方法要在那个对象的终结期之前完成(一个对象被终结之前必须已经构造完整) + - 开始一个线程的动作受到这个新线程的第一个动作的同步制约 + - Thread.join() 受到被合并的线程的最后一个动作的同步制约 + - 如果X在Y之前发生,并且Y在Z之前发生, 则X在Z之前发生(传递性) +- 重要概念: `如果对象不可改变,确保改变对所有线程可见的相关问题就不会出现` + +- 代码块之间的 `之前发生(Happens-Before)` 和 `同步约束(Synchronizes-With)`关系 + - 之前发生 这种关系表明一段代码在其他代码开始之前就已经全部完成了 + - 同步约束 这意味着动作继续执行之前必须把他的对象视图与主内存同步 -## Java内存模型 +************************ -## 【理论知识】 +## 理论知识 `线程模型` - 共享的,默认可见的可变状态 - 抢占式线程调度 @@ -64,6 +104,24 @@ - 测量系统用给定的资源能做多少工作 - 可重用性 +- 完全同步对象 策略 一个满足下面所有条件的类就是完全同步类: + - 所有域在任何公共构造方法中的初始化都能达到一致的状态 + - 没有公共域 + - 从任何非私有方法返回后,都可以保证对象实例处于一致的状态 假定调用方法时状态是一致的 + - 所有方法经证明都可在有限时间内终止 + - 所有方法都是同步的 + - 当处于非一致的状态时,不会调用其他实例的方法,以及调用非私有方法 + +- 不可变性: + - 这些对象或者没有状态(属性)或者只有final域。因为他们的状态不可变,所以是安全而又活泼,不会出现不一致的情况 + - 初始化就会遇上问题,如果是需要初始化很多属性,可以采用工厂模式,但是构建器模式更好。 + - 一个是实现了构建器泛型接口的内部静态类,另一个是构建不可变类实例的私有构造方法 + - [实现代码](https://github.com/Kuangcp/JavaBase/blob/master/concurrency/src/main/java/com/github/kuangcp/old/BuildFactory.java) + - 不可变对象中的final域特别要注意: + - final声明的对象的引用是不可变的, 但是如果引用的是对象,该对象自身的属性的引用是可变的 + - 不可变对象的使用十分广泛,但是开发效率不行,每修改对象的状态都要构建一个新对象 + + ### 可能的问题 - 安全性与活跃度相对立,安全性求稳定安全,活跃度是求进展 - 可重用的系统倾向于对外开放内核,这会引发安全问题 @@ -78,15 +136,13 @@ 4. 在文档中记录所要求的行为,这是最逊的方法,但如果代码要部署在非常通用的环境下,就必须采用这个方法 *********** -- 系统开销之源 - - 锁与监测 - - 环境切换的次数 - - 线程的个数 - - 调度 - - 内存的局部性 - - 算法设计 - -## 【块结构并发】 Java5之前 + +- 系统开销之源: 锁与监测, 环境切换的次数, 线程的个数, 调度, 内存的局部性, 算法设计 + +# 关键字 +## synchronized +在synchronized代码块执行完成后,对锁定对象所做的所有修改全部会在线程释放锁之前同步到内存中, 具有可重入性 + - 同步和锁 synchronized: - 只能锁定对象,不能锁定原始类型 - 锁的范围要尽可能的小 @@ -99,48 +155,39 @@ - 非同步的方法不查看或关心任何锁的状态,而且在同步方法运行时,他们仍能继续运行 - Java的线程锁是可重入的。也就是说持有线程锁的线程在遇到同一个锁的同步点 时是可以继续的 - 比如 一个同步方法调用另一个类的另一个同步方法 - -![线程状态模型](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Java/concurrent/Model.jpg) -- 线程的状态模型: - - 线程创建时处于准备(Ready)状态,然后调度器会准备执行 - -- 完全同步对象 策略 一个满足下面所有条件的类就是完全同步类: - - 所有域在任何公共构造方法中的初始化都能达到一致的状态 - - 没有公共域 - - 从任何非私有方法返回后,都可以保证对象实例处于一致的状态 假定调用方法时状态是一致的 - - 所有方法经证明都可在有限时间内终止 - - 所有方法都是同步的 - - 当处于非一致的状态时,不会调用其他实例的方法,以及调用非私有方法 - -### synchronized -- 在synchronized代码块执行完成后,对锁定对象所做的所有修改全部会在线程释放锁之前同步到内存中 - - 保证了在同一时刻,只有一个线程可以执行某一个方法或者代码块. - - 所以这个关键字的作用就是同步 在不同线程中锁定(操作)的对象的内存块 - - 同步的作用不仅仅是互斥,另一个作用就是共享可变性, 当某个线程修改了可变数据并释放锁,其他线程可以获取变量的最新值 - - 如果没有正确的同步,这种修改对其他线程是不可见的 - ->1. 如果锁定的是类的成员属性,或者this, 就是对该对象进行了加锁变成了'单线程', 就影响了整体性能 ->2. 使用局部变量就会多线程且保证了数据的一致性 ->3. 切记不能锁常量(或者显式声明的String)从而引起死锁 - -#### 正确使用锁 +- [JDK15 将移除 偏向锁](https://openjdk.java.net/jeps/374) + +- 保证了在同一时刻,只有一个线程可以执行某一个方法或者代码块. 保证了线程对变量访问的可见性和排他性 +- 所以这个关键字的作用就是同步 在不同线程中锁定(操作)的对象的内存块 + - 同步的作用不仅仅是互斥,另一个作用就是共享可变性, 当某个线程修改了可变数据并释放锁,其他线程可以获取变量的最新值 + - 如果没有正确的同步,这种修改对其他线程是不可见的 + +>1. 如果锁定的是类的成员属性,或者this, 就是对该对象进行了加锁, 该对象上的线程串行化, 影响了整体性能 +>2. 使用局部变量能保持多线程性能 且保证了数据的一致性 +>3. 切记不能锁常量(或者显式声明的String)从而引发死锁 + +对应于字节码中的指令是 monitorenter 和 monitorexit + +### 正确使用 > 查看JDK源码 ForkJoinTask 的 externalAwaitDone 方法 -- 1.wait方法用来使线程等待某个条件, 他必须在同步块内部被调用,这个同步块通常会锁定当前对象实例. -```java -// 这个模块的标准使用方式 -synchronized(this){ - while(condition){ - Object.wait(); - } -} -``` -- 2.始终使用wait循环来调用wait方法, 永远不要在循环之外调用wait方法 +1. wait方法用来使线程等待某个条件, 他必须在同步块内部被调用,这个同步块通常会锁定当前对象实例. + ```java + // 这个模块的标准使用方式 + synchronized(this){ + while(condition){ + Object.wait(); + } + } + ``` +2. 始终使用wait循环来调用wait方法, 永远不要在循环之外调用wait方法 - 因为有时候, 即使并不满足被唤醒条件,但是由于其他线程调用notifyAll()方法会导致被阻塞的线程意外唤醒,从而导致不可预料的结果 -- 3.唤醒线程,保守的做法是使用notifyAll唤醒所有等待的线程,从优化的角度看,如果处于等待的所有线程都在等待同一个条件,而每次只有一个线程可以从这个条件中被唤醒, 那么就应该选择调用notify +3. 唤醒线程,保守的做法是使用notifyAll唤醒所有等待的线程,从优化的角度看,如果处于等待的所有线程都在等待同一个条件,而每次只有一个线程可以从这个条件中被唤醒, 那么就应该选择调用notify > 当多个线程共享一个变量的时候,每个读写都必须加锁进行同步, 如果没有正确的同步,就容易造成程序的活性失败和安全性失败,这样的失败是很难复现的.所以务必要保证锁的正确使用 +************************ + ```java // 这个就是个错误使用的案例 int size = 0; @@ -154,71 +201,67 @@ public int current(){ > 这个案例保证了多线程下并发时,对size变量的正确修改,但是不能保证实时读取到的变量值是正确的 > 正确的做法是 current 方法也要加上synchronized关键字 -### volatile +## volatile > [Java多线程i++线程安全问题,volatile和AtomicInteger解释?](https://segmentfault.com/q/1010000006733274) +> [DCL的单例一定是线程安全的吗](https://www.cnblogs.com/amberJava/p/12546798.html)`如果new 还需要自定义初始化逻辑,需要使用本地变量初始化完成后赋值给对象属性` -- 线程所读的值在使用之前总会从内存中读出来 +- 线程所读的值在使用之前总会从内存中读入线程缓存 - 线程所写的值总会在指令完成之前同步回内存中 - - 可以把围绕该域的操作看成成是一个小的同步块 + - 可以把围绕该域的操作看成是一个小的同步块 - volatile 变量不会引入线程锁,所以不可能发生死锁 - - [ ] TODO 矛盾 - - volatile 变量是真正线程安全的,但只有写入时不依赖当前状态(读取的状态)的变量才应该声明为volatile变量 + - volatile 变量是真正线程安全的,但只有`写入时不依赖当前状态的变量`才应该声明为volatile变量 + +- volatile是Java提供的最轻量级的同步机制,Java内存模型为volatile专门定义了一些特殊的访问规则, 当一个变量被volatile修饰后: + - `线程可见性` 当一个线程修改了被volatile修饰的变量后,无论是否加锁,其他线程都能立即看到最新的修改 + - `禁止指令重排序优化` 普通的变量仅仅保证在该方法的执行过程中, 所有依赖赋值结果的地方都能获取正确的结果,而不能保证变量赋值操作的顺序和程序代码的执行顺序一致 + - 实现原理是在指令序列中插入了内存屏障: + - volatile 写操作前 StoreStore 后 StoreLoad + - volatile 读操作前 LoadLoad 后 LoadStore -#### 正确使用 + +### 正确使用 > 打开Netty中NioEventLoop的源码 有一个属性 `private volatile int ioRatio = 50;` 该变量是用于控制IO操作和其他任务运行比例的 -- volatile是Java提供的最轻量级的同步机制,Java内存模型为volatile专门定义了一些特殊的访问规则: - - 当一个变量被volatile修饰后: - - 线程可见性: 当一个线程修改了被volatile修饰的变量后,无论是否加锁,其他线程都能立即看到最新的修改 - - 禁止指令重排序优化, 普通的变量仅仅保证在该方法的执行过程中, 所有依赖赋值结果的地方都能获取正确的结果 - - 而不能保证变量赋值操作的顺序和程序代码的执行顺序一致 + ```java -public class ResortJavaDemo { - private static boolean stop; - public static void main(String[]s) throws InterruptedException { - Thread workThread = new Thread(() -> { - int i = 0; - while(!stop){ - i++; - try { - TimeUnit.SECONDS.sleep(1); - } catch (InterruptedException e) { - e.printStackTrace(); + public class ResortJavaDemo { + private static boolean stop; + public static void main(String[]s) throws InterruptedException { + Thread workThread = new Thread(() -> { + int i = 0; + while(!stop){ + i++; + try { + TimeUnit.SECONDS.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("i"+i); } - System.out.println("i"+i); - } - }); - workThread.start(); - TimeUnit.SECONDS.sleep(3); - stop = true; + }); + workThread.start(); + TimeUnit.SECONDS.sleep(3); + stop = true; + } } -} ``` > 我们预期程序会在3s后停止, 但是实际上它会一直执行下去, 原因就是虚拟机对代码进行了指令重排序和优化, 优化后的指令如下: ```java -if (!stop) - while(true) + if (!stop) + while(true) ``` > 所以需要在stop前加上volatile修饰符, 解决了如下两个问题 >> 1. main线程对stop的修改在workThread中可见 >> 2. 禁止指令重排序, 防止因为重排序导致的并发访问逻辑混乱 -- 以上示例代码在Java8中是正常运行的, 并不会一直执行下去, 所以还需要找个别的Demo过来 +- 注意 以上示例代码在Java8中是正常运行的, 并不会一直执行下去, 所以还需要找个别的Demo过来 > 一些人认为volatile可以替代传统锁,提升并发性能, 这个认识是错误的. volatile仅仅解决了可见性的问题, 并不能保证互斥性 >> volatile最适合使用的是一个线程写, 其他线程读的场景. >> 如果有多个线程并发`写`操作,仍然需要使用`锁`或者`线程安全的容器`或者`原子变量`来代替 **************************** -- 不可变性: - - 这些对象或者没有状态(属性)或者只有final域。因为他们的状态不可变,所以是安全而又活泼,不会出现不一致的情况 - - 初始化就会遇上问题,如果是需要初始化很多属性,可以采用工厂模式,但是构建器模式更好。 - - 一个是实现了构建器泛型接口的内部静态类,另一个是构建不可变类实例的私有构造方法 - - [思想实现代码](./src/main/java/com/concurrents/old/BuildFactory.java) - - 不可变对象中的final域特别要注意: - - final声明的对象的引用是不可变的, 但是如果引用的是对象,该对象自身的属性的引用是可变的 - - 不可变对象的使用十分广泛,但是开发效率不行,每修改对象的状态都要构建一个新对象 -## 【现代并发】JUC +# 现代并发JUC包 > 简称为J.U.C (java.util.concurrent) | [The j.u.c Synchronizer Framework中文翻译版](http://ifeve.com/aqs/) - 建议通过使用`线程池`,`Task(Runnable/Callable)`,`读写锁`,`原子类`和`线程安全容器`来代替传统的同步锁,wait和notify - 提升并发访问的性能, 降低多线程编程的难度, Netty就是这么做的 @@ -228,29 +271,25 @@ if (!stop) - ReentrantLock 和 sync 加解锁机制的区别? - 一个作用于线程一个作用于临界变量 - 不要依赖线程优先级 -### 概念 -#### CAS指令 -> 互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能额外损耗, 因此这种同步也被称为阻塞同步,悲观锁 ->> 与之对应的乐观锁是, 先进行操作, 操作完成之后再判断操作是否成功, 是否有并发问题, 如果有则进行失败补偿, 如果没有就算操作成功. -> Java中的非阻塞同步就是CAS 1.5就有了 +> [The java.util.concurrent Synchronizer FrameworkDoug Le](http://gee.cs.oswego.edu/dl/papers/aqs.pdf) `AQS` -#### 原子类 -> `java.util.concurrent.atomic` 提供适当的原子方法 避免在共享数据上出现竞争危害的方法 -> 使用Java自带的原子类, 可以避免同步锁带来的并发访问性能降低的问题, 减少犯错的机会. 对于 int, long, boolean 等成员变量大量使用原子类 ->> 但是使用者必须通过类似 compareAndSet或者set或者与这些操作等价的`原子操作`来保证更新的原子性. +## 概念 +### 读写锁 +> 在读多写少的场景下, 使用读写锁比同步块性能要好 -- 常见的操作系统的支持, 他们是非阻塞的(无需线程锁), 常见的方法是实现序列号机制(和数据库里的序列号机制类似),在`AtomicInteger`或`AtomicLong`上用原子 - - 操作`getAndIncrement()`方法, 并且提供了nextId 方法得到唯一的完全增长的数值 -- 注意: 原子类不是相似的类继承而来,所以 AtomicBoolean不能当Boolean用 +- 获取的读锁是共享锁 +- 获取写锁时会阻塞所有读锁和写锁 -#### 读写锁 -> 在读多写少的场景下, 使用同步锁比同步块性能要好 - -- 读锁 ReentranReadWriteLock 是共享锁 ***************** -### 具体实现 -#### 线程锁 + +## 实现类 +> [JUC - 类汇总和学习指南](https://pdai.tech/md/java/thread/java-thread-x-juc-overview.html) + +### 原子类 +> [原子类](/Java/AdvancedLearning/Concurrency/Atomic.md) + +### Lock > `java.util.concurrent.locks` - 块结构同步方式基于锁这样的的概念,具有缺点 - 锁只有一种类型 @@ -272,22 +311,33 @@ if (!stop) - `trylock()`: [官方API1.8 trylock](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/ReentrantLock.html#tryLock--) - Lock接口的实现类:ReentrantWriteLock 在需要读取很多线程而写入很少线程时,用这个性能更好 -#### CountDownLatch 锁存器 -- 是一种简单的同步模式,这种模式允许线程在通过同步屏障之前做少量的准备工作 - - 构建实例时,需要提供一个数值(计数器),通过两个方法来实现这个机制 - - `countDown()` 作用:计数器减一 - - 如果当前计数大于零,则将计数减少。然后什么都不做 - - 如果减后的计数为零,出于线程调度目的,将重新启用所有的等待线程 - - 如果当前计数等于零,则不发生任何操作。 - - - `await()` 作用:让线程在计数器到0之前一直等待, - - 如果大于 0 , 休眠这语句所处的当前线程 - - 例如 `a.await()` 如果锁存器a的Count不为0 ,就把当前线程休眠掉 - - 如果已经是小于等于0 就什么都不做 +### CountDownLatch +是一种简单的同步模式,这种模式允许线程在通过同步屏障之前做少量的准备工作, 构建实例时,需要提供一个数值(计数器),通过两个方法来实现这个机制 + +- `countDown()` 作用:计数器减一 + - 如果当前计数大于零,则将计数减少。然后什么都不做 + - 如果减后的计数为零,出于线程调度目的,将重新启用所有的等待线程 + - 如果当前计数等于零,则不发生任何操作。 +- `await()` 作用:让线程在计数器到0之前一直等待, + - 如果大于 0 , 休眠这语句所处的当前线程 + - 例如 `a.await()` 如果锁存器a的Count不为0 ,就把当前线程休眠掉 + - 如果已经是小于等于0 就什么都不做 -- 能做到: 当一堆线程之间的同步,为了确保有指定数量正常初始化的线程 创建成功,才能开始同步 +支持场景:一堆线程间的状态同步,为了确保有指定数量正常初始化的线程 创建成功 后 继续执行逻辑 + +### CyclicBarrier + +### Semaphore +> 最早用来解决进程同步与互斥问题的机制, 整体设计来自于操作系统。[Linux Semaphore](/Linux/Base/LinuxBase.md#信号量-semaphore) + +> [JUC工具类: Semaphore详解](https://pdai.tech/md/java/thread/java-thread-x-juc-tool-semaphore.html) + +### Phaser + +### Exchanger -#### ConcurrentHashMap + +### ConcurrentHashMap - `ConcurrentHashMap` 是 HashMap的并发版本 - 修改HashMap,并不需要将整个结构都锁住,只要锁住即将修改的桶(就是单个元素) - 好的HashMap 实现,在读取时不需要锁,写入时只要锁住要修改的单个桶 Java能达到这个标准,但是需要程序员去操作底层的细节才能实现 @@ -295,46 +345,80 @@ if (!stop) - `putIfAbsent()` 如果还没有对应键,就把键/值添加进去 - `remove()` 如果键存在而且值与当前状态相等,则用原子方式移除键值对 - `replace()` API 为HashMap中原子替换的操作方法提供了两种不同的形式 -- 例如之前的完全同步类里的公共 Map实现就是HashMap,如果换成ConcurrentHashMap 那些synchronized关键字修饰的方法就可以换成普通方法了 -- 该类不仅提供了多线程的安全性,性能也很好 +- key value 均不允许为null + +> 1.7 到 1.8 改动 +- 数据结构:取消了 Segment 分段锁的数据结构,取而代之的是数组+链表+红黑树的结构。 +- 保证线程安全机制:JDK1.7 采用Segment 的分段锁机制实现线程安全,其中 Segment 继承自 ReentrantLock 。JDK1.8采用CAS+synchronized保证线程安全。 +- 锁的粒度:JDK1.7 是对需要进行数据操作的 Segment 加锁,JDK1.8调整为对每个数组元素加锁(Node)。 +- 链表转化为红黑树:定位节点的 hash 算法简化会带来弊端,hash冲突加剧,因此在链表节点数量大于 8(且数据总量大于等于 64)时,会将链表转化为红黑树进行存储。 +- 查询时间复杂度:从JDK1.7的遍历链表O(n), JDK1.8 变成遍历红黑树O(logN)。 + +ConcurrentHashMap的迭代器是弱一致性 -#### CopyOnWriteArrayList +### ConcurrentSkipListMap +基于跳表实现的并发有序Map + +ConcurrentSkipListMap的迭代器是弱一致性的,它不会抛出ConcurrentModificationException异常,但是无法保证迭代器遍历的元素是一个完整的快照。因此,在迭代器遍历ConcurrentSkipListMap时,可能会遗漏或重复遍历某些元素。 + +### CopyOnWriteArrayList - 标准的ArrayList的替代,通过写时复制语义来实现线程安全性,也就是说修改列表的任何操作都会创建一个列表底层数组的新副本 - 这就意味着所有成形的迭代器都不会遇到意料之外的修改 (脏读) - - 这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更 有效。在不能或不想进行同步遍历 - - 当读操作大于写操作会比较好用, + - 适用于读操作多于写操作时,比如事件监听器、缓存等 - 但又需要从并发线程中排除冲突时,它也很有用。“快照”风格的迭代器方法在创建迭代器时使用了对数组状态的引用。此数组在迭代器的生存期内不会更改, - 因此不可能发生冲突,并且迭代器保证不会抛出 ConcurrentModificationException。创建迭代器以后,迭代器就不会反映列表的添加、移除或者更改。 - 在迭代器上进行的元素更改操作(remove、set 和 add)不受支持。这些方法将抛出 UnsupportedOperationException。 *********************** +# 结构化并发 +[Structured Concurrency](https://openjdk.org/jeps/453) -## 【Queue】 +************************ -- Queue接口全是泛型的,这样就更为方便, 自己再封装一个层 +# 实践 +目标:需要合理设计线程模型,尽量不要让线程阻塞,因为一阻塞,CPU 就闲下来了。 -`BlockingQueue>` -```java -public class Pro{ - private T pro; - public T getPro(){ - return pro; - } - public Pro(T pro){ - this.pro = pro; - } -} -``` -- 有了这层间接引用, 不用牺牲所包含类型(Author)在概念上的完整性,就能够添加额外的元数据了。方便统一性的修改 - - 用上额外元数据的用例: - - 测试: 比如 展示一个对象的修改历史 - - 性能指标: 比如 到达时间,服务质量 - - 运行时系统信息: 比如 Author实例是如何排到队列的 +> [Java线程](/Java/AdvancedLearning/JavaThread.md) [Java线程池](/Java/AdvancedLearning/Concurrency/ExecutorAndPool.md) - -### BlockingQueue -> 并发扩展类, +在技术和业务角度,都应该考虑抽象和分层,将近似的事情放在一个线程池内,更有利于针对性设置参数达到整体的效率优化。 +例如Tomcat中的 [NioEndpoint](/Java/Ecosystem/Servlet/TomcatDesign.md#nioendpoint) 将 接受连接,收连接数据,执行连接任务和发送响应,拆分成三个线程池 + +## 任务建模 +> 要把目标代码做成可调用(执行者调用)的结构,而不是单独开线程运行 [示例代码](https://github.com/Kuangcp/JavaBase/blob/master/concurrency/src/main/java/com/github/kuangcp/schedule/CreateModel.groovy) + +`Callable接口` +- 通常是匿名内部实现类 + +`Future接口` +- 用来表示异步任务,是还没有完成的任务的未来结果,主要方法: + - get() 用来获取结果,如果结果还没准备好就会阻塞直到它能去到结果,有一个可以设置超时的版本,这个版本永远不会阻塞 + - cancel() 运算结束前取消 + - isDone() 调用者用它来判断运算是否结束 + +`FutureTask类` +- FutureTask是Future接口的常用实现类, 并且是实现了Runnable接口。所以提供的方法是俩接口的方法 + - 提供了两个构造器,一个是Callable为参数,另一个以Runnable为参数 +- 可以基于FutureTask的Runnable特性,把任务写成Callable然后封装进一个有执行者地调度并在必要时可以取消的FutureTask + +************************ + +## Queue + +|队列| 有界性| 锁| 数据结构| +|:---|:---|:---|:---| +| ArrayBlockingQueue | bounded | 加锁| arraylist| +| LinkedBlockingQueue | optionally-bounded | 加锁| linkedlist| +| ConcurrentLinkedQueue | unbounded | 无锁| linkedlist| +| LinkedTransferQueue | unbounded | 无锁| linkedlist| +| PriorityBlockingQueue | unbounded | 加锁| heap| +| DelayQueue | unbounded | 加锁| heap| + +> [高性能队列——Disruptor](https://tech.meituan.com/2016/11/18/disruptor.html) +- [Github](https://github.com/LMAX-Exchange/disruptor) [User Guide](https://lmax-exchange.github.io/disruptor/user-guide/index.html) +- 经过验证可以发现由于控制是使用无锁的CAS实现,当队列空置时,CPU空耗高占用很明显。也就是说这个队列适合对繁忙且延迟敏感的业务。 + +### BlockingQueue interface - 基本方法 - put() 如果队列已满,会让放入的线程等待 队列腾出空间 @@ -349,100 +433,29 @@ public class Pro{ - BlockingQueue 不接受 null 元素。试图 add、put 或 offer 一个 null 元素时,某些实现会抛出 NullPointerException。 - BlockingQueue 的实现主要用于生产者-使用者队列 -### TransferQueue + +`BlockingQueue>` +```java + public class Pro{ + private T pro; + public T getPro(){ + return pro; + } + public Pro(T pro){ + this.pro = pro; + } + } +``` +- 有了这层间接引用, 不用牺牲所包含类型(Author)在概念上的完整性,就能够添加额外的元数据了。方便统一性的修改 + - 用上额外元数据的用例: + - 测试: 比如 展示一个对象的修改历史 + - 性能指标: 比如 到达时间,服务质量 + - 运行时系统信息: 比如 Author实例是如何排到队列的 + + +#### TransferQueue interface - 本质上是多了一项 transfer()操作的BlockingQueue, 如果接收线程处于等待状态, 该操作会马上把工作项传给他。 - 否则就会阻塞直到取走工作项的线程出现 即 正在处理工作项的线程在交付当前工作项之前不会开始其他工作项的处理工作, - 这样系统就可以调控上游线程获取新工作项的速度 用限定大小的阻塞队列也能达到同样的效果,TransferQueue 执行效率更高 - 但是这个只有链表的实现版本 - 相比于BlockingQueue 用法一致, offer() 等价于 tryTransfer() 参数也是一致的,代码基本不需要改动 - -************************ - -## 【控制执行】 -### 任务建模 -> 要把目标代码做成可调用(执行者调用)的结构,而不是单独开线程运行 -> [展示代码](./src/main/java/com/concurrents/schedule/CreateModel.groovy) - -`Callable接口` -- 通常是匿名内部实现类 - -`Future接口` -- 用来表示异步任务,是还没有完成的任务的未来结果,主要方法: - - get() 用来获取结果,如果结果还没准备好就会阻塞直到它能去到结果,有一个可以设置超时的版本,这个版本永远不会阻塞 - - cancel() 运算结束前取消 - - isDone() 调用者用它来判断运算是否结束 - -`FutureTask类` -- FutureTask是Future接口的常用实现类, 并且是实现了Runnable接口。所以提供的方法是俩接口的方法 - - 提供了两个构造器,一个是Callable为参数,另一个以Runnable为参数 -- 可以基于FutureTask的Runnable特性,把任务写成Callable然后封装进一个有执行者地调度并在必要时可以取消的FutureTask - -#### ScheduleThreadPoolExecutor -> ScheduleThreadPoolExecutor 简称 STPE 线程池类中很重要的类 - -- 线程池的大小可以预定义, 也可自适应 -- 所安排的任务可以定期执行,也可只运行一次 -- STPE扩展了ThreadPoolExecutor类,很相似但不具备定期调度能力 - - STPE和并发包里的类结合使用是常见的模式之一 - -************************** - -## 【分支合并框架】 -- 引入一种新的执行者服务,称为 ForkJoinPool -- ForkJoinPool 服务处理一种比线程更小的并发单元 ForkJoinTask - - ForkJoinTask是一种由ForkJoinPool以更轻量的方式所调度的抽象 -- 通常使用两种任务 - - 小型 无需处理器耗时太久的任务 - - 大型 需要在直接执行前进行分解(可能多次)的任务 -- 提供了支持大型任务分解的基本方法,还有自动调度和重新调度的能力 - -- 这个框架的关键特性之一就是:这些轻量的任务都能够生成新的ForkJoinTask实例,而这些实例仍然由执行他们父任务的线程池来安排调度,这就是分而治之 -- 工作窃取: -- [一个简单的例子](./src/main/java/com/concurrents/forkjoin/ForkJoinEasyDemo.groovy) - -- 由 RecursiveAction 或者 RecursiveTask 派生出来的才能作为任务单元 这俩也是派生ForkJoinTask而来 - - RecursiveAction 要重写的方法:`protected void compute()` - - RecursiveTask 要重写的的方法:`protected Object compute()` -- ForkJoinTask里的 invoke 和 invokeAll - - `public final V invoke()` - - invoke 执行此任务的开始,如果有必要,等待它的完成,并返回其结果,或者在底层计算完成时抛出一个(未检查的)RuntimeException或错误。 - - `public static > Collection invokeAll(Collection tasks)` - - invokeAll 方法的特点是多个执行,但是只有其中有一个是出现了异常,就会取消所有的task - -`ForkJoinTask和工作窃取` -- ForkJoinTask作为RecursiveAction的超类,他是从动作中返回结果的泛型类型,所以这个类扩展了ForkJoinTask - - 这使得ForkJoinTask非常适合用MapReduce方式(Google踢出的软件架构,用于大规模数据集的并行计算)返回数据集中归结出的结果 -- ForkJoinTask由ForkJoinPool调度安排,这个池是一个特殊的执行者服务。这个服务维护每个线程的任务列表,并且当某个任务完成的时候, - - 他能把挂在满负荷线程上的任务重新安排到空闲线程上去 这就是 `工作窃取` - -`并行问题` -- 可以使用分支合并方法解决的问题: - - 模拟大量简单对象的运动,例如粒子效果 - - 日志文件分析 - - 从输入中计数的数据操作,比如mapreduce操作 -- 以下的列表检查当前问题及其子任务是一个切实有效的方法,确认是否能用分支合并来解决问题 - - 问题的子任务是否无需与其他子任务有显式的协作或同步也可以工作? - - 子任务是不是不会对数据进行修改,只是经过计算得出些结果? - - 对于子任务来说,分而治之是不是很自然的事?子任务是不是会创建更多的子任务,而且他们要比派生出他们的任务粒度更细? - - 如果思考的结果是肯定的,就可以适用,如果思考结果是不确定的,用其他的同步方式更合适 - -## 【Java内存模型】 -> Java Memory Model JMM - -- 同步动作和被称为偏序的数据结构描述JMM, -- JMM 的主要规则: - - 在监测对象上的解锁操作与后续的所操作之间存在同步约束关系 - - 对易失性变量的写入与后续对该变量的读取之间存在同步约束关系 - - 如果动作A受到动作B的同步约束,则A在B之前发生 - - 如果在程序中的线程内A出现B之前,则A在B之前发生 - - 前两个简称为先存后取 -- 敏感行为: - - 构造方法要在那个对象的终结期之前完成(一个对象被终结之前必须已经构造完整) - - 开始一个线程的动作受到这个新线程的第一个动作的同步制约 - - Thread.join() 受到被合并的线程的最后一个动作的同步制约 - - 如果X在Y之前发生,并且Y在Z之前发生, 则X在Z之前发生(传递性) -- 重要概念: `如果对象不可改变,确保改变对所有线程可见的相关问题就不会出现` - -- 代码块之间的 `之前发生(Happens-Before)` 和 `同步约束(Synchronizes-With)`关系 - - 之前发生 这种关系表明一段代码在其他代码开始之前就已经全部完成了 - - 同步约束 这意味着动作继续执行之前必须把他的对象视图与主内存同步 diff --git a/Java/AdvancedLearning/JavaDebug.md b/Java/AdvancedLearning/JavaDebug.md new file mode 100644 index 0000000..0635251 --- /dev/null +++ b/Java/AdvancedLearning/JavaDebug.md @@ -0,0 +1,55 @@ +--- +title: Java中的Debug +date: 2019-07-21 18:08:40 +tags: +categories: + - Java +--- + +💠 + +- 1. [Debug](#debug) + - 1.1. [技巧](#技巧) + - 1.2. [方案](#方案) + - 1.2.1. [应用方法CPU耗时或线程异常](#应用方法cpu耗时或线程异常) + - 1.3. [远程调试](#远程调试) + +💠 2024-06-01 23:56:42 +**************************************** +# Debug + +> [参考: 深入 Java 调试体系](https://www.ibm.com/developerworks/cn/views/java/libraryview.jsp?search_by=%E6%B7%B1%E5%85%A5%20Java%20%E8%B0%83%E8%AF%95%E4%BD%93%E7%B3%BB) + +1. [jdb](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/jdb.html) + +> IDEA 的 debug 实现方式: JVM应用的启动命令追加 -javaagent: . `可查看JVM的完整启动命令找到` + +## 技巧 +1. 需要尝试不停机去修改代码去寻找问题踪迹 + - 可通过Arthas动态替换class +1. 业务代码复杂,寻找资源泄漏点难以找到业务上的触发点 + - 可在创建资源的最底层入口实例化一个异常,但是不抛出,仅打印日志,观察每次资源被创建时的调用栈,辅助分析出是哪个业务入口导致 + + +## 方案 +### 应用方法CPU耗时或线程异常 +表现: 应用启动慢,业务方法运行慢等等 + +思路: + +1. jstack 分析方法 在业务进入前,执行中,执行完毕后 等若干个节点上的线程差别,找出可能异常的线程和方法栈 +1. profile 查看CPU火焰图,找出耗时高的栈 +1. arthas 的 monitor 指令进行分析方法栈耗时监控,找出异常方法 + +> 项目启动慢 + +1. 场景: Linux系统 hostname 没有配置到 /etc/hosts 文件中, 启动过程中频繁jstack能看出某线程被 getLocalHost 方法所阻塞 + - 方案: hostname配置到 /etc/hosts + +## 远程调试 +- 服务端开启远程调试端口 8000 + - JDK9及以上 `-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000` + - JDK5-8 `-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000` + - If you want to debug from start of application use `suspend=y` , this will keep remote application suspended until you connect from eclipse. +- IDE中新增Remote运行配置填入IP端口,选择启动类所在模块即可远程Debug + - 注意尽量不要Debug所有线程从而导致K8S健康检查无响应下的pod重启 diff --git a/Java/AdvancedLearning/JavaDeploy.md b/Java/AdvancedLearning/JavaDeploy.md new file mode 100644 index 0000000..dde826a --- /dev/null +++ b/Java/AdvancedLearning/JavaDeploy.md @@ -0,0 +1,161 @@ +--- +title: Java应用的部署 +date: 2018-11-21 10:56:52 +tags: + - 部署 +categories: + - Java +--- + +💠 + +- 1. [部署运行](#部署运行) + - 1.1. [打包Jar](#打包jar) + - 1.2. [打包可执行Jar](#打包可执行jar) + - 1.3. [打包War](#打包war) + - 1.4. [打包Docker镜像](#打包docker镜像) +- 2. [配置文件](#配置文件) + - 2.1. [命令行参数](#命令行参数) +- 3. [Tips](#tips) + - 3.1. [Java在Linux上的时区问题](#java在linux上的时区问题) + - 3.2. [容器中Jvm信号及参数接收问题](#容器中jvm信号及参数接收问题) + +💠 2024-10-08 15:07:46 +**************************************** +# 部署运行 +> 传统的可执行jar, war 以及Docker镜像 + +> [参考: JAR 文件揭密](https://www.ibm.com/developerworks/cn/java/j-jar/index.html) +> [参考: maven-assembly-plugin 入门指南](https://www.jianshu.com/p/14bcb17b99e0) + +> [Maven 打包部署](/Java/Tool/Maven.md#打包部署) | [Gradle 打包部署](/Java/Tool/Gradle.md#打包部署) + +## 打包Jar +1. 平铺式 + - 所有依赖的jar和自身项目class都平铺在目录里 + - 运行main方法类 `java -cp . xxx.Main` 需要将资源所在目录都加入classpath +1. FatJar + - 所有依赖和自身项目class都打包在一个jar里 + - `java -jar app.jar` 或者 `java -cp app.jar xxx.Main`(没有mainfest文件) + +> 注意 +- -cp -jar 混合使用时 -cp 会被忽略 + +## 打包可执行Jar +> [关于MANIFEST.MF文件](https://blog.csdn.net/baileyfu/article/details/1808023)`这个文件很重要, 如果自己手动配置就需要编写该文件` +_MANIFEST.MF示例_ +```yml + Manifest-Version: 1.0 + Archiver-Version: Plexus Archiver + Built-By: kcp + Created-By: Apache Maven 3.5.3 + Build-Jdk: 1.8.0_152 + Main-Class: com.youaishujuhui.minigame.Main +``` +- 编译文件 `javac -d *.java ` +- 打包字节码成jar `jar -cvf hello.jar com/test/*.*` +- 打包成可执行jar `jar -cvfm hello.jar mainfest *.*` + - 其中 `mainfest` 文本文件: `Main-Class: com.test.Main` + - 冒号后一定要有空格,文件最后一行一定留空行 + +> FatJar 所有的依赖都在一个jar里 +- [Maven方式](/Java/Tool/Maven.md#打包部署) + +************************* + +## 打包War +> 最终将生成的war 放到 tomcat 的 webapps 目录下或者 Jetty的 webapps 目录下 + +## 打包Docker镜像 +> 以一个基础镜像,然后将war放进去构建成一个镜像, 然后推送到服务器上构建容器进行运行 + +1. 简要概括: from jdk基础镜像, 将jar 复制进去, 设置好 CMD + +> [jib](https://github.com/GoogleContainerTools/jib) +> - 结合 Maven Gradle 能更方便的构建 Docker镜像 + +************************ + +# 配置文件 +> 多目标应用环境的发布, 可以使用Maven 多 Profile; Spring 的多profiles; 环境变量; ... + +> 在环境中存储配置 +- 通常,应用的 配置 在不同 部署 (预发布、生产环境、开发环境等等)间会有很大差异。这其中包括: + - 数据库,Memcached,以及其他 后端服务 的配置 + - 第三方服务的证书,如 Amazon S3、Twitter 等 + - 每份部署特有的配置,如域名等 + +> [参考: 在环境中存储配置](https://12factor.net/zh_cn/config) +> [从jar包中读取资源文件](https://pdai.tech/md/develop/usage/dev-usage-jar-readfile.html) + + +```java + /** + * @param path 例如 传入 /redis/lock.lua 意味着: 读取SpringBoot打包成一个UberJar里某子模块的 resources 目录下的 redis/lock.lua 文件。 + * 即使子模块被打包成jar被主模块依赖也不影响,因为文件在classpath的路径都是 /redis/lock.lua + */ + private static String readFile(String path) { + try { + InputStream is = RedisSemaphore.class.getResourceAsStream(path); + if (Objects.isNull(is)) { + throw new RuntimeException("NULL"); + } + + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String s; + StringBuilder bb = new StringBuilder(); + while ((s = br.readLine()) != null) { + bb.append(s).append("\n"); + } + return bb.toString(); + } catch (Exception re) { + log.error("Load failed {}", path, re); + return ""; + } + } +``` + +## 命令行参数 +> [jcommander](https://jcommander.org/) + +************************ + +# Tips + +## Java在Linux上的时区问题 +- 表象 + - Docker容器中运行的Linux上时区是正确的, 但是Java应用的时区不对 +- 原因 + - JVM获取时区配置的顺序 + 1. 查看 环境变量 TZ + - `export TZ=Asia/Shanghai` + 1. /etc/sysconfig/clock 中查找 ZONE 的值 + ```conf + ZONE="Asia/Shanghai" + UTC=false + ARC=false + ``` + 1. /etc/localtime 或者 /usr/share/zoneinfo + - `ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime` + - 也可以加JVM参数 `-Duser.timezone=GMT+8` + - 或者硬编码设置时区 + +> 快速测试Java获取到的时区 +```java + import java.util.Date; + import java.time.ZoneOffset; + + public class TimeTest { + public static void main(String[] args){ + System.out.println(new Date()); + System.out.println(ZoneOffset.systemDefault()); + } + } +``` + +## 容器中Jvm信号及参数接收问题 +需要避免Java进程为容器中的1号进程,因为会带来很多问题,包括但不限: +- 无法正常接收Linux信号量 +- Arthas无法注入 +- 无法合理管理派生出的进程生命周期 +- JDK中的工具有些也会无法正常使用例如 jstack diff --git a/Java/AdvancedLearning/JavaException.md b/Java/AdvancedLearning/JavaException.md new file mode 100644 index 0000000..780869a --- /dev/null +++ b/Java/AdvancedLearning/JavaException.md @@ -0,0 +1,230 @@ +--- +title: Java异常处理 +date: 2018-12-13 16:15:05 +tags: + - 异常处理 +categories: + - Java +--- + +💠 + +- 1. [异常](#异常) + - 1.1. [异常的继承关系](#异常的继承关系) + - 1.2. [常见异常](#常见异常) + - 1.3. [常见问题](#常见问题) + - 1.3.1. [异常被吞](#异常被吞) + - 1.3.2. [异常栈被隐藏](#异常栈被隐藏) + - 1.3.3. [应该使用大块的try还是细颗粒度的try?](#应该使用大块的try还是细颗粒度的try) + - 1.3.4. [try和for谁包住谁更好?](#try和for谁包住谁更好) +- 2. [异常的处理](#异常的处理) + - 2.1. [全局异常处理](#全局异常处理) + - 2.2. [异常和日志的结合](#异常和日志的结合) + - 2.3. [自定义异常](#自定义异常) + - 2.3.1. [自定义异常的错误码](#自定义异常的错误码) +- 3. [实现机制](#实现机制) + +💠 2024-06-15 13:56:58 +**************************************** +# 异常 +> 相关博客:[Java异常浅谈](http://www.cnblogs.com/focusj/archive/2011/12/26/2301524.html) + +## 异常的继承关系 +![异常结构](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Java/Exception/structure.png) + +> Java将所有非正常情况分为两种 异常(Exception)和错误(Error) 它们都继承Throwable父类。 +Error一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断,通常应用程序无法处理这些错误; +因此应用程序通常不应该使用catch块来捕获Error对象,在定义方法时也无须在其throws子句中声明该方法可能抛出Error及任何子类。 + +非受检异常: 继承于RunTimeException或Error. 其他的都属于受检异常 + +## 常见异常 +> 常发生于集合并发修改和迭代时 [ConcurrentModificationException](https://docs.oracle.com/javase/8/docs/api/java/util/ConcurrentModificationException.html) + +**************************** +## 常见问题 +### 异常被吞 +>- catch 代码块中出现异常 + +会导致栈中看不到try块中的异常 + +### 异常栈被隐藏 +> [Java堆栈信息不见了 ](https://jjlu521016.github.io/2018/12/12/java%E5%A0%86%E6%A0%88%E4%BF%A1%E6%81%AF%E4%B8%8D%E8%A7%81%E4%BA%86.html) + +在使用服务器模式下,会默认开启 Fast Throw 机制, 以下异常被频繁抛出时(大于5000),会隐藏错误栈 +- NullPointerException +- ArithmeticException +- ArrayIndexOutOfBoundsException +- ArrayStoreException +- ClassCastException + +JVM参数关闭该特性 `-XX:-OmitStackTraceInFastThrow` + +### 应该使用大块的try还是细颗粒度的try? +> 为了避免我们遗漏掉一些可能会出现异常的代码, 所以建议使用大块的try, 因为检查型异常是容易发现的, 但是运行时异常却往往不能在编码的第一时间发现 + +> 检验异常在开发中如果不进行处理(捕获处理或声明抛出)编译器就会报错不让通过的, 如果平时没有注意异常的系统性学习, +开发就会有这样一种现象: 程序中通篇只有校验异常. 一般这种校验异常默认的处理方式(使用IDE)是颗粒度小的. +但是程序运行出问题的大多是运行时异常, 空指针, 数组越界, 类型不匹配, 除数为0 等等. +使用大try就不会遗漏运行时异常,但是不能仅仅依靠他, 运行时异常还是尽量使用好的编程习惯来规避的. +当然也是可以在大try中使用小try进行开发这样就能对异常进行具体的捕获和处理以及响应 + +> 按下葫芦浮起瓢, 关于异常, 可以从另一个角度: 性能方面的维度去考虑: +异常机制的设计初衷是用于不正常的情形, JVM很少对其生成的字节码进行优化, 把尽可能多的代码放在try块中就会阻止了JVM实行原本可以执行的某些特定优化 + +### try和for谁包住谁更好? +> 具体业务具体分析, 如果要求循环一出问题后续的循环还是要继续执行, 那么就把try写到for中; +如果要求后续的循环不执行就用try包住for + +另外: 手动创建线程实现的定时任务在循环处理数据时出现异常必须要及时处理, 否则线程会停止 `尽量使用Schduler线程池` + +******************** + +# 异常的处理 +> 一般来说, 异常都是层层上抛, 中央处理 针对 Service Dao Controller 这种结构的设计, 在Service层进行统一异常处理 +> 除非这个异常是无关大局的, 就是局部发生对其他模块和代码没有影响, 那么就可以就地捕获不需要上抛 + +不建议使用判断语句进行异常的处理, 这样的维护很费劲, 可读性也不好 +大的try块中 一些需要特别说明的就是 特别处理的就要看情况抛出了, 一般进行封装后, 抛出自定义异常, 上层接收后, 进行二次处理和转化, +直到最外层的调用处, 由最外层调用者决定最终如何处理 只要在使用对象前进行对象的非空判断, 基本就能杜绝空指针异常, + +这样的话要引入大量的if块, 如果的确很复杂了可以使用设计模式做优化 例如策略模式,否则不需要 + +## 全局异常处理 + +> JavaSE +[Java Global Exception Handler](https://www.baeldung.com/java-global-exception-handler) + +[线程默认异常处理](/Java/AdvancedLearning/JavaThread.md#观测异常) + +************************ + +> SpringMVC +```java + // 全局异常处理 + // 如果exception方法定义于具体Controller类中则会优先于全局异常处理 + @Slf4j + @RestControllerAdvice + public class RestExceptionHandler { + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public Resp exception(Exception e) { + log.error("EXCEPTION {}", e.getMessage(), e); + return Resp.fail(RespCode.ERROR.getCode(), e.getMessage()); + } + } +``` +源码入口 `org.springframework.web.servlet.DispatcherServlet#processDispatchResult` + +************************ + +## 异常和日志的结合 +> 异常一定要和日志结合使用, 日志记录的简约优雅, 维护才越轻松, 日志的存在就是为了解决问题的方便和有迹可循, +> 所以要记录的任何信息都是为了解决问题, 无关信息就没有必要放进来了(日志存储和解析搜索也是需要成本的) + +日志格式:类 方法 行数 时间 报错信息 +根据业务需要还可以加上traceId,用户id, 订单id等等 + +## 自定义异常 +> 虽然Java提供了大量的异常类, 但是这些异常类还是难以满足业务开发时的诉求。 + +通常自定义异常只要继承Exception 重写相关构造器即可. +一般来说自定义异常具有以下类型: 业务异常, 用户异常, 系统异常, 接口异常, 网络异常, 参数异常等等. + +根据项目需要, 可以将异常细分, 比如写一个订单保存, 那么针对订单保存的业务可以在不同的代码逻辑里提示不同的异常信息, 接口级异常也是如此,目的是为了将Exception进行封装, 形成易于理解的异常信息. + +_自定义异常的设计原则_ +1. 用户层面: 用户提示信息优雅 +2. 系统层面: 让自己维护起来更方便 +3. 接口层面: 查询问题更直观, 轻松, 为自己留证据, 避免推诿扯皮 +4. 网络层面: 及时发现问题, 及时进行处理 + +### 自定义异常的错误码 +> 正规项目中都会有接口文档, 也会有规范注释, 在项目中也会有一些静态常量 +假设定义一个错误码 00X1 表示空指针, 这个错误码不是给客户或者使用者看的, 而且定义要有规律不能让其他人猜到, 方便查询和管理 +规划的越详细, 就会有更为便利的维护方式 + +************************ + +# 实现机制 +> [Java 基础 - 异常机制详解](https://pdai.tech/md/java/basic/java-basic-x-exception.html) +> [透过JVM看Exception本质](https://www.cnblogs.com/newstar/archive/2011/01/04/Exception.html) +> [Java异常到底是个啥——一次异常引发的思考](https://blog.51cto.com/u_16202392/7798771) +> [Java 虚拟机:JVM是如何处理异常的?](https://cloud.tencent.com/developer/article/1786449) + +```java + @Test + public void testThrow() throws Exception { + try { + int a = 1, b = 1; + if (a == b) { + throw new RuntimeException(); + } + System.out.println(a + b); + } catch (IndexOutOfBoundsException e) { + throw e; + } catch (Exception e) { + log.error("", e); + throw e; + } + } +``` + +查看字节码: javap -v class 或者通过IDEA中插件 `jclasslib Bytecode Viewer` + +```java + Code: + stack=3, locals=3, args_size=1 + 0: iconst_1 + 1: istore_1 + 2: iconst_1 + 3: istore_2 + 4: iload_1 + 5: iload_2 + 6: if_icmpne 17 + 9: new #2 // class java/lang/RuntimeException + 12: dup + 13: invokespecial #3 // Method java/lang/RuntimeException."":()V + 16: athrow + 17: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream; + 20: iload_1 + 21: iload_2 + 22: iadd + 23: invokevirtual #5 // Method java/io/PrintStream.println:(I)V + 26: goto 46 + 29: astore_1 + 30: aload_1 + 31: athrow + 32: astore_1 + 33: getstatic #8 // Field log:Lorg/slf4j/Logger; + 36: ldc#9 // String + 38: aload_1 + 39: invokeinterface #10, 3 // InterfaceMethod org/slf4j/Logger.error:(Ljava/lang/String;Ljava/lang/Throwable;)V + 44: aload_1 + 45: athrow + 46: return + Exception table: + from to target type +0 26 29 Class java/lang/IndexOutOfBoundsException +0 26 32 Class java/lang/Exception + +``` + +流程: +1. 偏移9: new一个RuntimeException对象 +1. 12: dup 复制上诉对象到栈顶 +1. 13: invokespecial 调用异常类构造器 +1. 16: athrow + - 这个指令运作过程大致是首先检查操作栈顶,这时栈顶必须存在一个reference类型的值,并且是java.lang.Throwable的子类(虚拟机规范中要求如果遇到null则当作NPE异常使用) + - 然后暂时先把这个引用出栈,接着搜索本方法的异常表(Exception table)找一下本方法中是否有能处理这个异常的handler + - 如果能找到合适的handler就会重新初始化PC寄存器指针指向此异常handler的第一个指令的偏移地址。接着把当前栈帧的操作栈清空,再把刚刚出栈的引用重新入栈。 + - 如果在当前方法中找不到handler,那只好把当前方法的栈帧出栈(这个栈是VM栈,不要和前面的操作栈搞混了,栈帧出栈就意味着当前方法退出) + - 这个方法的调用者的栈帧就自然在这条线程VM栈的栈顶了,然后再对这个新的当前方法再做一次刚才做过的异常handler搜索 + - 如果还是找不到,继续把这个栈帧踢掉,这样一直到找 + - 要么找到一个能使用的handler,转到这个handler的第一条指令开始继续执行。 + - 要么把VM栈的栈帧抛光了都没有找到期望的handler,这样的话这条线程就只好被迫终止、退出了 `线程可设置默认handler setDefaultUncaughtExceptionHandler`。 + +Exception table: 异常匹配的表格 如果`from` 和 `to` 之间匹配到 `type`的异常抛出,就转到 `target` 处执行代码 + + + diff --git a/Java/AdvancedLearning/Generics.md b/Java/AdvancedLearning/JavaGenerics.md similarity index 57% rename from Java/AdvancedLearning/Generics.md rename to Java/AdvancedLearning/JavaGenerics.md index a9c7232..ad707b5 100644 --- a/Java/AdvancedLearning/Generics.md +++ b/Java/AdvancedLearning/JavaGenerics.md @@ -1,47 +1,91 @@ -`目录 start` - -- [泛型](#泛型) - - [入门](#入门) - - [简单使用](#简单使用) - - [类型擦除](#类型擦除) - - [约束和局限性](#约束和局限性) - - [泛型类型的继承规则](#泛型类型的继承规则) - - [通配符类型](#通配符类型) - - [子类型限定的通配符 extends](#子类型限定的通配符-extends) - - [超类型限定的通配符 super](#超类型限定的通配符-super) - - [应用](#应用) - - [无限定通配符](#无限定通配符) - - [通配符捕获](#通配符捕获) - - [反射和泛型](#反射和泛型) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Java泛型 +date: 2018-11-21 10:56:52 +tags: + - 泛型 +categories: + - Java +--- + +💠 + +- 1. [泛型](#泛型) + - 1.1. [简单使用](#简单使用) + - 1.2. [类型擦除](#类型擦除) + - 1.3. [约束和局限性](#约束和局限性) + - 1.4. [泛型类型的继承规则](#泛型类型的继承规则) + - 1.5. [通配符类型](#通配符类型) + - 1.5.1. [子类 类型限定的通配符 extends](#子类-类型限定的通配符-extends) + - 1.5.2. [基类 类型限定的通配符 super](#基类-类型限定的通配符-super) + - 1.5.3. [无限定通配符](#无限定通配符) + - 1.5.4. [通配符捕获](#通配符捕获) + - 1.6. [反射和泛型](#反射和泛型) + +💠 2024-07-10 00:40:24 **************************************** # 泛型 -> [开始学习的兴趣来源](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665514015&idx=1&sn=12409f705c6d266e4cd062e78ce50be0&chksm=80d67c5cb7a1f54a68ed83580b63b4acded0df525bb046166db2c00623a6bba0de3c5ad71884&scene=21#wechat_redirect) +> [Generics](https://docs.oracle.com/javase/tutorial/java/generics/index.html) -[参考博客: Java总结篇系列:Java泛型](http://www.cnblogs.com/lwbqqyumidi/p/3837629.html) +> 泛型程序设计划分为三个熟练级别 基本级别就是仅仅使用泛型类,典型的是像ArrayList这样的集合--不必考虑他们的工作方式和原因,大多数人会停留在这个级别.直到出现了什么问题. 当把不同的泛型类混合在一起的时候,或是对类型参数一无所知的遗留代码进行对接时,可能会看到含糊不清的错误消息.如果这样的话,就需要系统的进行学习Java泛型来系统地解决问题. +> 泛型类可以看作普通类的工厂 -- Java核心技术卷 2004(1.5) + +*************** + +> [开始学习的兴趣来源 Java帝国之泛型 ](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665514015&idx=1&sn=12409f705c6d266e4cd062e78ce50be0&chksm=80d67c5cb7a1f54a68ed83580b63b4acded0df525bb046166db2c00623a6bba0de3c5ad71884&scene=21#wechat_redirect) + +[参考: Java总结篇系列:Java泛型](http://www.cnblogs.com/lwbqqyumidi/p/3837629.html) 泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。 那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。 -[参考博客: Java深度历险(五)——Java泛型](http://www.infoq.com/cn/articles/cf-java-generics) -## 入门 ->泛型程序设计划分为三个熟练级别 基本级别就是仅仅使用泛型类,典型的是像ArrayList这样的集合--不必考虑他们的工作方式和原因,大多数人会停留在这个级别.直到出现了什么问题. 当把不同的泛型类混合在一起的时候,或是对类型参数一无所知的遗留代码进行对接时,可能会看到含糊不清的错误消息.如果这样的话,就需要系统的进行学习Java泛型来系统地解决问题. -> 泛型类可以看作普通类的工厂 -- Java核心技术卷 2004(1.5) +[参考: Java深度历险(五)——Java泛型](http://www.infoq.com/cn/articles/cf-java-generics) -### 简单使用 +## 简单使用 >- [简单泛型类示例](https://github.com/Kuangcp/JavaBase/blob/master/src/main/java/com/generic/simple/Pair.java) 例如该行定义 : `public abstract class RoomCache

, R extends RoomBO> extends AbstractCache {}` - - 类型变量使用大写的一个字母这是代表: - `E` 集合的元素类型 - `K V` 表示表的关键字和值的类型 - `T U S` 等就表示任意类型 - -#### 类型擦除 -- 不同于C++的泛型,C++是将模板类组合出来的生成一个新的类,Java则是进行类型擦除,然后再类型强转 +```Java + // 根据Class对象 获取泛型的类型信息 + Type superClass = getClass().getGenericSuperclass(); + type = ((ParameterizedType) superClass).getActualTypeArguments()[1]; +``` + +> [参考: 使用通配符简化泛型使用](https://www.ibm.com/developerworks/cn/java/j-jtp04298.html) + +- 场景1:`public static > T min(T[] list);` + - 限定了入参和返回值是 是实现了Comparable接口的某个类型 因为Comparable也是一个泛型类, 所以也进行限定类型 + - 这样的写法要比 T extends Comparable 更为彻底 + - 例如计算一个String数组的最小值 T 就是 String类型的, String是Comparable的子类型 + - 但是当处理GregorianCalendar, GregorianCalendar是Calendar的子类, 并且Calendar实现了`Comparable` + - 因此GregorianCalendar实现的是`Comparable`, 而不是Comparable + - 这种情况下 `public static > T min(T[] list)` 就是安全的 + +- 场景2: `public static List importExcel(Class target)` + - 该方法实现了, 传入继承了ExcelTransform接口的类对象, 得到该类的List集合 + - ` boolean` 这样写编译没报错, 那么就是说, 就是一个泛型的定义, 后面进行引用, 省的重复写 + - 简单的写法就是 `public static List importExcel(Class target)` + +- 场景3: Spring4.x 添加的泛型依赖注入 , 使用的JPA就是依赖该技术 [spring学习笔记(14)——泛型依赖注入](http://blog.csdn.net/u010837612/article/details/45582043) + +- 场景4: 泛型嵌套以及传递问题 [实际代码](https://github.com/Kuangcp/JavaBase/tree/generic/src/main/java/com/github/kuangcp/nesting) + - 本来的设想是只要声明了具有泛型约束的类, 就应该不用再声明该类中的泛型类型, 但是由于Java的泛型只是在编译前存在, 编译后就被擦除了, 所以没法做到这样简洁的约束 + +> 对于应用程序员, 可能很快的学会掩盖这些声明, 想当然地认为库程序员做的都是正确的, 如果是一名库程序员, 一定要习惯于通配符 +> 否则还要用户在代码中随意地添加强制类型转换直至可以通过编译. + +super 只能用于通配符 + +********************* + +## 类型擦除 +- 不同于C++的泛型,C++是将模板类组合出来生成一个新的类,Java则是进行类型擦除,然后再类型强转 + - 例如 `public static T min (T[] list)` - - 擦除后就只剩下一个方法 `public static Comparable min(Comparable[] list)` + - 擦除后 `public static Comparable min(Comparable[] list)` - [泛型类擦除示例](https://github.com/Kuangcp/JavaBase/blob/master/src/main/java/com/generic/simple/DateInterval.java) - 例如该方法签名 `public static T getMax(T[]list)` @@ -50,99 +94,116 @@ > 在Java的继承中,可以根据需要拥有多个接口超类型,但限定中至多只有一个类,如果用一个类作为限定,他必须是限定列表中的第一个 +注意:泛型记录在类字节码中的 Signature LocalVariableTypeTable 属性上, [参考: Java泛型-4(类型擦除后如何获取泛型参数)](https://www.jianshu.com/p/cb8ff202797c) + ****************************** -### 约束和局限性 +## 约束和局限性 > 以下代码示例:涉及的类Pair在上述的代码中已经定义, Human和Student是继承关系 -> 并且因为看的 Java核心技术卷 比较老 jdk是1.5的所以没有用7的菱形语法简化泛型 7可以省去右边的类型: `Pair pair = new Pair<>();` -- | _不能使用基本类型实例化类型参数_ - - 也就是说没有`Pair`只有`Pair` +- _不能使用基本类型 实例化类型参数_ + - 也就是说没有 `Pair` 只有 `Pair` - 因为类型擦除后,类型是Object并不能放double的值, 但是这样做与Java语言中基本类型的独立状态相一致. - - 当包装器类型(wrapper type)不能接受替换时,可以使用独立的类和方法处理他们 -- | _运行时类型查询(eq或者instanceof)只适用于原始类型_ - - 比如`Pair` 和`Pair`是等价的,因为类型擦除 - - `Pair pair1` `Pair pair2` pair1.getClass()和pair2.getClass()是等价的都是返回Pair.class -- | _不能抛出也不能捕获泛型类实例_ + - *但是* 可以使用 原始类型数组 例如 `byte[]` + - [valhalla项目正计划支持原始类型](http://openjdk.java.net/projects/valhalla/) + +- _运行时类型查询(eq或者instanceof)只适用于原始类型_ + - 比如 `Pair` 和 `Pair` 是等价的,因为类型擦除 + - `Pair pair1 和 Pair pair2` pair1.getClass() 和 pair2.getClass() 是等价的都是返回Pair.class + +- _不能抛出也不能捕获泛型类实例_ - 错误的示例: - `public class Problem extends Exception{}` - `public static void doWork(){try{}catch(T t){}}` - 正确示例: - 在异常声明中使用类型变量 - `public static void doWork() throws T{.. catch(){throw t;}}` -- | _参数化类型的数组不合法_ - - 例:`Pair[] list = new Pair[10];` - - 因为擦除后 list是Pair[]类型,然后就能转成Object[], 就失去了泛型的作用 - - 如果要使用的话最好直接使用集合 ArrayList:` ArrayList>`,安全又高效 -```java - Object[] array = list; - array[0] = "hi";// 编译错误 - array[0] = new Pair(); //通过数组存储的检测,但实际上类型错误了,所以禁止使用参数化类型的数组 -``` -- | _不能实例化类型变量(T)_ +- _参数化类型的数组不合法_ + - 例:`Pair[] list = new Pair[10];` + - 因为擦除后 list是 `Pair[]` 类型, 能转成 `Object[]` 这样就失去了泛型的作用 + - 如果要使用的话最好直接使用集合 ArrayList:` ArrayList>` 安全又高效 + ```java + Object[] array = list; + array[0] = "hi";// 编译错误 + array[0] = new Pair(); //通过数组存储的检测,但实际上类型错误了,所以禁止使用参数化类型的数组 + ``` + +- _不能实例化类型变量(T)以及数组_ - 非法 `new T(){}` -```java - public Pair(){ - first = new T(); - second = new T(); - } - first = T.class.newInstance() //非法 T.class是不合法的 - //要实例化一个Pair的对象就要如下: - public static Pair initPair(Class c){ - try{ - return new Pair(c.newInstance(), c.newInstance()); - }catch (Exception e){ - return null; + ```java + public Pair(){ + first = new T(); + second = new T(); } - } - // 如下调用 - Pair pair = Pair.initPair(String.class); - // 因为Class本身是泛型, String.class其实是Class的实例 - // 也不能实例化为一个数组 new T[5] -``` -- | _泛型类的静态上下文中类型变量无效_ + + //非法 T.class是不合法的 + first = T.class.newInstance() + + //要实例化一个Pair的对象就要如下: + public static Pair initPair(Class c){ + try{ + return new Pair(c.newInstance(), c.newInstance()); + }catch (Exception e){ + return null; + } + } + // 如下调用 + Pair pair = Pair.initPair(String.class); + // 因为Class本身是泛型, String.class其实是Class的实例 + // 也不能实例化为一个数组 new T[5] + ``` + +- _泛型类的静态上下文中类型变量无效_ - 不能在静态域中使用类型变量 如下: - 如果这段代码能执行,那就可以声明一个 Singleton 共享随机数生成类, - 但是声明之后,类型擦除,就只剩下了Singleton类,并不能做对应的事情,所以禁止这样的写法 -```java - private static T first; // 错误 - public static T getFirst(){ // 错误 - return first; - } -``` -- | _注意泛型擦除后的冲突_ + ```java + private static T first; // 错误 + public static T getFirst(){ // 错误 + return first; + } + ``` + +- _注意泛型擦除后的冲突_ - 当类型擦除时,不能创建引发冲突的相关条件 - 例如 新实现一个类型变量约束的equals方法就会和Object原方法冲突 补救方法就是重命名该方法了 - -```java - public class Pair{ - public boolean equals (T value){ - return .. + ```java + public class Pair{ + public boolean equals (T value){ + return .. + } } - } -``` + ``` + `泛型规范说明` - 要想支持擦除的转换,就需要强行限制一个类或类型变量不能同时成为两个接口类型的子类,而这两个接口是同一接口的不同参数化 - 以下代码就是非法的, GregorianCalendar 实现了两个接口,两个接口是Comparable接口的不同参数化,这是不允许的 -```java - class Calendar implements Comparable{} - class GregorianCalendar extends Calendar implements Comparable{} // 错误 -``` -- 但是如下又是合法的 -```java - class Calendar implements Comparable{} - class GregorianCalendar extends Calendar implements Comparable{} -``` -- 很有可能是桥方法有关,不可能有两个一样的桥方法(因为两个接口其实是一个接口的不同参数化,桥方法的方法签名是一致的) + ```java + class Calendar implements Comparable{} + class GregorianCalendar extends Calendar implements Comparable{} // 错误 + ``` + - 但是如下又是合法的 + ```java + class Calendar implements Comparable{} + class GregorianCalendar extends Calendar implements Comparable{} + ``` + - 很有可能是桥方法有关,不可能有两个一样的桥方法(因为两个接口其实是一个接口的不同参数化,桥方法的方法签名是一致的) + +> Tips +- Stream Optional结合泛型出现的极端问题 [JDK bugs:嵌套泛型](https://bugs.openjdk.org/browse/JDK-8313448) +- [Stream bug](https://github.com/Kuangcp/JavaBase/blob/master/java8/src/test/java/com/github/kuangcp/stream/bug/StreamGenericTest.java) +- [Lambda 多继承bug](https://github.com/Kuangcp/JavaBase/blob/master/java8/src/test/java/com/github/kuangcp/lambda/bug/MultipleExtendsTest.java) ******************************************* -### 泛型类型的继承规则 + +## 泛型类型的继承规则 > 例如 父子类: Human Student 那么 Pair Pair 是继承(inherit)关系么,答案是否定的!! ```java Pair humans = new Pair(man, woman); Pair classmates = humans;// illegal, but suppose it wasn't + classmates.setSecond(junior) // 如果上面合法,那么这里是肯定可以执行的, 因为泛型类型变成了Student //那么就有了问题了,原有的人类类型限制的对象中,出现了小学生 //所以不允许这样的类型变量约束的类进行多态 @@ -152,6 +213,7 @@ Student[] students = humans; students[0] = junior ;// 虚拟机将抛出 ArrayStoreException 异常 ``` +> 为何 `List list = Arrays.asList("1","2");` 能通过编译 ****************** > 永远可以将参数化类型转换为一个原始类型, Pair 是原始类型Pair的一个子类型,转换成原始类型也会产生错误 @@ -172,11 +234,29 @@ ************************************************************************** -### 通配符类型 -#### 子类型限定的通配符 extends -> 通配符上限 顾名思义,就是限定为该类及其子类, 例如: `Pair` 表示任何Pair泛型类型并且他的类型变量要为Human的子类 +## 通配符类型 +> [Guidelines for Wildcard Use](https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.html) + +- Producer extends, Consumer super. + - `? extends` : 数据的提供方 执行 get 操作 + - `? super` : 数据的存储方 执行 set 操作 + +- Tips + - 限定通配符总是包括自己 + - 如果你既想存,又想取,那就别用通配符 + - 不能同时声明泛型通配符上界和下界 + - `` 可以看作 `` + - `` 可以看作 `` + +> `注意` 通配符的泛型约束一般是出现在基础库的API上(接口上, 方法上) 常见应用逻辑代码用的较少 + +### 子类 类型限定的通配符 extends +> 通配符上限 顾名思义,就是限定为该类及其子类 + +- 例如: + - `Pair` 表示任何Pair泛型类型并且他的类型变量要为Human的子类 + - 编写一个方法 `public static void printMessage(Pair human){}` -> 例如编写一个方法 `public static void printMessage(Pair human){}` > 正如上面所说, Pair类型的变量是不能放入这个方法的,因为泛型变量是没有继承关系, 这时候就可以使用这个通配符: >> `public static void printMessage(Pair)` 可以get不能set ```java @@ -191,9 +271,15 @@ // 使用get方法就不会有问题, 泛型起作用了.将get返回值赋值给Human的引用也是完全合法的,这就是引入该统通配符的关键之处 ``` -#### 超类型限定的通配符 super +> 注意此情况无法编译, 目前理解为编译期无法确认T的实际类型 +```java + public Class getService(int serviceCode){} +``` + +### 基类 类型限定的通配符 super > 通配符下限 顾名思义就是限定为父类, 通配符限定和类型变量限定十分相似, 但是可以指定一个超类型限定(supertype bound) > `? super Student` 这个通配符就限定为Student的所有超类型(super关键字已经十分准确的描述了这种关系) + >> 带有超类型限定的通配符的行为和前者相反,可以为方法提供参数,但不能使用返回值即 可以 set 但是不能get ```java @@ -209,29 +295,8 @@ >> 子类型限定: 是限定了不能set,但是保证了get >> 超类型限定: 限定了不能正确get,但是保证了set. -##### 应用 -> [参考博客: 使用通配符简化泛型使用](https://www.ibm.com/developerworks/cn/java/j-jtp04298.html) - -- 示例1:`public static > T min(T[] list);` - - 限定了入参和返回值是 是实现了Comparable接口的某个类型 因为Comparable也是一个泛型类, 所以也进行限定类型 - - 这样的写法要比 T extends Comparable 更为彻底 - - 例如计算一个String数组的最小值 T 就是 String类型的, String是Comparable的子类型 - - 但是当处理GregorianCalendar, GregorianCalendar是Calendar的子类, 并且Calendar实现了`Comparable` - - 因此GregorianCalendar实现的是`Comparable`, 而不是Comparable - - 这种情况下 `public static > T min(T[] list)` 就是安全的 - -- 示例2: `public static List importExcel(Class target)` - - 该方法实现了, 传入继承了ExcelTransform接口的类对象, 得到该类的List集合 - - ` boolean` 这样写编译没报错, 那么就是说, 就是一个泛型的定义, 后面进行引用, 省的重复写 - - 简单的写法就是 `public static List importExcel(Class target)` - -- 示例3: Spring4.x 添加的泛型依赖注入 , 使用的JPA就是依赖该技术 [spring学习笔记(14)——泛型依赖注入](http://blog.csdn.net/u010837612/article/details/45582043) - -> 对于应用程序员, 可能很快的学会掩盖这些声明, 想当然地认为库程序员做的都是正确的, 如果是一名库程序员, 一定要习惯于通配符 -> 否则还要用户在代码中随意地添加强制类型转换直至可以通过编译. - -#### 无限定通配符 -> TODO 对其使用场景 尚有疑问,以后再解 +### 无限定通配符 +> [Unbounded Wildcards](https://docs.oracle.com/javase/tutorial/java/generics/unboundedWildcards.html) ```java // 例如 Pair @@ -241,8 +306,8 @@ ``` - 例如 [这个hasNull()方法](https://github.com/Kuangcp/JavaBase/blob/master/src/test/java/com/generic/simple/PairTest.java)用来测试一个pair是否包含了指定的对象, 他不需要实际的类型. -#### 通配符捕获 -> TODO 学习和理解使用场景 +### 通配符捕获 +> [Wildcard Capture and Helper Methods](https://docs.oracle.com/javase/tutorial/java/generics/capture.html) - 如果编写一个交换的方法 ```java @@ -262,7 +327,7 @@ ``` - swapHelper是一个泛型方法, 而swap不是, 它具有固定的Pair类型的参数, 那么现在就可以这样写: - `public static void swap(Pair p){swapHelper(p);}` - - 这种情况下, swapHelper方法的参数T捕获通配符, 它不知道是哪种类型的通配符,但是这是一个明确的类型 并且swapHelper 在T指出类型时,才有明确的含义 + - 这种情况下, swapHelper方法的参数T捕获通配符, 它不知道是哪种类型的通配符,但是这是一个明确的类型 并且`swapHelper` 在T指出类型时,才有明确的含义 - 当然,这种情况下并不是一定要用通配符, 而且我们也实现了没有通配符的泛型方法 > 但是下面这个通配符类型出现在计算结果中间的示例 @@ -276,10 +341,15 @@ // 对于编译器而言, 必须能够确信通配符表达的是单个, 确定的类型. ``` -### 反射和泛型 -> [官方Java7的Class文档](https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html) | []() -> 现在Class类是泛型的, 例如String.class实际上是Class类的对象(事实上是唯一的对象) -> 类型参数十分有用, 这是因为他允许Class方法的返回类型更加具有针对性.下面Class的方法就使用了类型参数 +******************* + +## 反射和泛型 +> [Official Doc: Class](https://docs.oracle.com/javase/7/docs/api/java/lang/Class.html) + +> JDK中Class类也泛型化了, 例如String.class实际上是`Class`类的对象(事实上是唯一的对象) +> 类型参数十分有用, 这是因为他允许`Class`方法的返回类型更加具有针对性. + +`Class`的方法就使用了类型参数 ```java T newInstance() T cast(Object obj) @@ -288,16 +358,16 @@ Constructor getConstructor(Class... paramterTypes) Constructor getDeclaredConstructor(Class... paramterTypes) ``` -- newInstance方法返回一个示例, 这个实例所属的类由默认的构造器获得, 它的返回类型目前被声明为T, 其类型与Class描述的类相同, 这样就免除了类型转换. +- newInstance方法返回一个示例, 这个实例所属的类由默认的构造器获得, 它的返回类型目前被声明为T, 其类型与`Class`描述的类相同, 这样就免除了类型转换. - 如果给定的类型确实是T的一个子类型, cast方法就会返回一个现在声明为类型T的对象, 否则, 抛出一个BadCastException异常 - 如果这个类不是enum类或类型T的枚举值的数组, getEnumConstants方法将返回Null. -- 最后, getConstructor与getDeclaredConstructor方法返回一个Constructor对象.Constructor类也已经变成泛型, 以便 newInstance方法有一个正确的返回类型. +- `getConstructor`与`getDeclaredConstructor`方法返回一个`Constructor`对象.Constructor类也加上了泛型, 方便newInstance方法有正确返回类型. TODO 还要继续看书 ```java // 传入一个Class对象, 得到Class对应类型的实例 - public T get(Class target, String name); - // 加上约束 - public T get(Class target, String name); -``` \ No newline at end of file + public T get(Class target); + // 类型加上约束 + public T get(Class target); +``` diff --git a/Java/AdvancedLearning/JavaIO.md b/Java/AdvancedLearning/JavaIO.md new file mode 100644 index 0000000..3cbb947 --- /dev/null +++ b/Java/AdvancedLearning/JavaIO.md @@ -0,0 +1,181 @@ +--- +title: Java中的IO +date: 2018-11-21 10:56:52 +tags: + - IO +categories: + - Java +--- + +**目录 start** + +1. [Java中的IO](#java中的io) + 1. [IO 简史](#io-简史) + 1. [BIO](#bio) + 1. [NIO](#nio) + 1. [AIO](#aio) + 1. [字节流](#字节流) + 1. [字符流](#字符流) + 1. [应用](#应用) + 1. [文件IO](#文件io) + 1. [读取配置文件](#读取配置文件) + 1. [可执行jar读取外部配置文件](#可执行jar读取外部配置文件) + 1. [Maven项目](#maven项目) + 1. [网络IO](#网络io) +1. [NIO](#nio) + 1. [Buffer](#buffer) + +**目录 end**|_2023-07-24 18:06_| +**************************************** +# Java中的IO +> [Note:操作系统中的IO模型](/Skills/CS/IO.md) +> [Oracle Doc: Java I/O, NIO, and NIO.2](https://docs.oracle.com/javase/8/docs/technotes/guides/io/index.html) + +> [Github: IO demo](https://github.com/Kuangcp/JavaBase/tree/io) + +> [参考: 五种IO模型](https://www.jianshu.com/p/6a6845464770) + +## IO 简史 +> [BIO NIO AIO演变](http://www.cnblogs.com/itdragon/p/8337234.html) + +### BIO +> Java1.0 到 Java1.3 + +同步阻塞式IO 但是能基于 BIO 手动实现 伪异步IO + +### NIO +> Java1.4 引入; 同步非阻塞式IO, 虽然官方名称为 New IO, 民间称为 `No-blocking IO` + +这个NIO和是基于操作系统NIO相关函数实现的, 所以称为`No-blocking IO` +```java + // 进入Selector.open(); 方法可以看到 + public static Selector open() throws IOException { + return SelectorProvider.provider().openSelector(); + } + // 进入 provider() 方法 + // 然后看到 sun.nio.ch.DefaultSelectorProvider.create(); + public static SelectorProvider create() { + String var0 = (String)AccessController.doPrivileged(new GetPropertyAction("os.name")); + if (var0.equals("SunOS")) { + return createProvider("sun.nio.ch.DevPollSelectorProvider"); + } else { + return (SelectorProvider)(var0.equals("Linux") ? + createProvider("sun.nio.ch.EPollSelectorProvider") : new PollSelectorProvider()); + } + } +``` + +在Linux上使用的是 epoll, Windows 则是 poll + +实现模型和操作系统的NIO也是一致的, 一个 Selector 注册多个 SelectionKey, SelectionKey 具有多个状态并且和Channel绑定 + +| 事件名 | 对应值 | +|:----|:----| +| 服务端接收客户端连接事件 | SelectionKey.OP_ACCEPT(16) +| 客户端连接服务端事件 | SelectionKey.OP_CONNECT(8) +| 读事件 | SelectionKey.OP_READ(1) +| 写事件 | SelectionKey.OP_WRITE(4) + +************************ + +类似的思想还有定时器 +- 需求: 实现一个 10s 后调用一个方法的定时器 +- 简单: `Thread.sleep(10000);` 但是这种方式下, 定时器和任务是一一对应的 +- 复用模式: 一个线程睡眠很短的时间, 不停去检查 方法的时间到了没有, 到了就执行, 这样就只要一个线程就能处理多个任务 + +### AIO +> Java1.7 引入; 真正的异步非阻塞IO + +- 引入了新的异步通道的概念, 以及异步文件通道和异步套接字通道的实现 + +Asynchronous*的类 读写操作都被Future封装了,均交给操作系统异步完成,需要应用系统手动处理 + +************** +## 字节流 +> OutputStream InputStream + +ByteArrayOutputStream, FileOutputStream, FilterOutputStream, ObjectOutputStream, OutputStream, PipedOutputStream + +> [参考: FilterInputStream 与 装饰器模式](https://blog.csdn.net/zhao123h/article/details/52826682) + +- FilterInputStream + - DataInputStream + - BufferedInputStream + +*************** + +## 字符流 +> Reader Writer + +Reader类的核心就是read()这个方法,由于这里直接操作InputStream进行read(),因此可以读取出2个字节,java中每两个字节转成一个字符。 +这就是Reader可以读取字符的原因,只不过是利用InputStream先将字节读取出来,再按照一定的编码方式转码. + +> Java8 快速读取字符流 +```java + String text = new BufferedReader( + new InputStreamReader(((Response.InputStreamBody) response.body).inputStream, StandardCharsets.UTF_8)) + .lines() + .collect(Collectors.joining("\n")); +``` +*************** + +## 应用 +### 文件IO +> [参考: Read a text file from Java classpath](https://www.java-success.com/read-a-text-file-from-java-classpath/) +> [Java:利用I/O流读取文件内容](https://blog.csdn.net/xuehyunyu/article/details/77873420) + +#### 读取配置文件 +- maven项目,从resources下获取文件 例如 /a.xml `InputStream is = this.getClass().getResourceAsStream("/a.xml");` + 1. 读取properties文件 :`new Properties().load(is);` + 1. 按行读取文件 `BufferedReader bf = new BufferedReader(new InputStreamReader(is));` + +************************ + +##### 可执行jar读取外部配置文件 +```java + Properties properties = new Properties(); + File file = new File("something.properties"); + FileInputStream fis = new FileInputStream(file); + properties.load(fis); + System.out.println(properties.getProperty("v")); + fis.close(); +``` +- 只要配置文件和打包的jar同级即可 + +##### Maven项目 +_读取resource目录下配置文件_ +```java + ClassLoader classLoader = MainConfig.class.getClassLoader(); + URL resource = classLoader.getResource("excel.main.yml"); + if(resource!=null){ + String path = resource.getPath(); + } +``` +- 这样也可以, 但是会有诡异的问题, 打包后运行是正常的, idea中运行就不正常, `new File("src/main/resources/excel.main.yml")` + +### 网络IO +> [参考博客:网络IO之阻塞、非阻塞、同步、异步总结 ](https://www.cnblogs.com/Anker/p/3254269.html) + +> [参考: Java IO: 网络](http://ifeve.com/java-io-network/) +当两个进程之间建立了网络连接之后,他们通信的方式如同操作文件一样:利用InputStream读取数据,利用OutputStream写入数据。换句话来说,Java网络API用来在不同进程之间建立网络连接,而Java IO则用来在建立了连接之后的进程之间交换数据。 + +********************************** +# NIO +> [Java NIO 系列教程](http://ifeve.com/java-nio-all/) + +## Buffer +> [Java NIO系列教程(三) Buffer](http://ifeve.com/buffers/) + +- Buffer的基本用法: 使用Buffer读写数据一般遵循以下四个步骤: + 1. 写入数据到Buffer + 1. 调用flip()方法 + 1. 从Buffer中读取数据 + 1. 调用clear()方法或者compact()方法 + +- 当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。 + +- 一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区: + - 调用clear()或compact()方法。 +- clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。 + + diff --git a/Java/AdvancedLearning/ExtendsAndInterface.md b/Java/AdvancedLearning/JavaInheritedAndInterface.md similarity index 76% rename from Java/AdvancedLearning/ExtendsAndInterface.md rename to Java/AdvancedLearning/JavaInheritedAndInterface.md index 76e1ba2..22f6277 100644 --- a/Java/AdvancedLearning/ExtendsAndInterface.md +++ b/Java/AdvancedLearning/JavaInheritedAndInterface.md @@ -1,21 +1,31 @@ -`目录 start` - -- [继承和接口](#继承和接口) - - [继承](#继承) - - [接口](#接口) - - [常用接口](#常用接口) - -`目录 end` |_2018-08-27_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Java中的继承与接口 +date: 2018-11-21 10:56:52 +tags: + - 接口 +categories: + - Java +--- + +**目录 start** + +1. [继承和接口](#继承和接口) + 1. [继承](#继承) + 1. [接口](#接口) + 1. [常用接口](#常用接口) + +**目录 end**|_2020-05-17 16:13_| **************************************** # 继承和接口 -> 要倾向于组合的使用多于继承 +> 应倾向于多使用组合而不是继承 -- [参考博客: java的重载、覆盖和隐藏的区别](http://www.cnblogs.com/xiaoQLu/archive/2013/01/07/2849869.html) +- [参考: java的重载、覆盖和隐藏的区别](http://www.cnblogs.com/xiaoQLu/archive/2013/01/07/2849869.html) ## 继承 + ## 接口 -个人理解为一个接口就表明一种协议, 实现一个接口就是实现一种协议, 表明实现方能完成协议所规定的相关行为.. +个人理解为一个接口就表明一种协议, 实现一个接口就是实现一种协议, 表明实现方可以完成协议所规定的相关行为 ### 常用接口 diff --git a/Java/AdvancedLearning/JavaNetwork.md b/Java/AdvancedLearning/JavaNetwork.md new file mode 100644 index 0000000..f75e3b9 --- /dev/null +++ b/Java/AdvancedLearning/JavaNetwork.md @@ -0,0 +1,38 @@ +--- +title: Java网络编程 +date: 2018-11-21 10:56:52 +tags: + - Network +categories: + - Java +--- + +💠 + +- 1. [Java网络编程](#java网络编程) + - 1.1. [基础](#基础) + - 1.1.1. [Socket](#socket) + - 1.2. [Tips](#tips) + +💠 2024-03-04 14:39:31 +**************************************** +# Java网络编程 + +> [参考: Java网络教程](http://ifeve.com/java-network/) +> [java proxy](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html) + +## 基础 +> 获取主机网络信息 + +- [获取本地ip](https://github.com/looly/hutool/issues/428) +- [Getting the IP address of the current machine using Java](http://stackoverflow.com/questions/9481865/getting-the-ip-address-of-the-current-machine-using-java) + +### Socket +> [码农翻身:张大胖的socket ](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513387&idx=1&sn=99665948d0b968cf15c5e7a01ffe166c&chksm=80d679e8b7a1f0febad077b57e8ad73bfb4b08de74814c45e1b1bd61ab4017b5041942403afb&scene=21#wechat_redirect) + + +## Tips + +- 得到URL指向文件的输入流 + - `new URL(url).openStream()` + diff --git a/Java/AdvancedLearning/JavaPerformance.md b/Java/AdvancedLearning/JavaPerformance.md deleted file mode 100644 index 10362d1..0000000 --- a/Java/AdvancedLearning/JavaPerformance.md +++ /dev/null @@ -1,55 +0,0 @@ -`目录 start` - -- [Java的性能调优](#java的性能调优) - - [JVM参数配置](#jvm参数配置) - - [内存优化](#内存优化) - - [处理内存泄露问题](#处理内存泄露问题) - - [内存监测工具](#内存监测工具) - - [jvisualvm](#jvisualvm) - - [MAT](#mat) -- [记录](#记录) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Java的性能调优 - -## JVM参数配置 - -## 内存优化 - -- [Blog:java优化占用内存的方法(一)](http://blog.csdn.net/zheng0518/article/details/48182437) - -- [GC 性能优化 专栏](https://blog.csdn.net/column/details/14851.html) - -### 处理内存泄露问题 -> [参考博客: java内存泄漏的定位与分析](https://blog.csdn.net/lc0817/article/details/67014499) - -### 内存监测工具 -#### jvisualvm -> [详情](/Java/AdvancedLearning/JDKAndJRE.md#jvisualvm) - -#### MAT -> Memory Analyzer tool(MAT) [官网](http://www.eclipse.org/mat/) - -> [参考博客: JAVA Shallow heap & Retained heap](http://www.cnblogs.com/lipeineng/p/5824799.html) - -************** - -# 记录 - -1. 表象 - - 使用Tomcat进行部署的, 然后机器上两个Tomcat都僵死了, 进程还在, 但已经不能提供服务了, 日志也停止了记录 -1. 分析 - - 由于第一次遇到这种情况, 没有把现场保留, 直接就重启Tomcat了, 然后老大经过分析 一个堆栈快照文件(?), 发现有几个对象大量存在, 没有被GC - - 然后启动本地Tomcat, 用 jvisualvm 进行调试, 发现有几个类的实例一直无法释放, - - 初步 分析这几个类的 **生命周期** , 以为是项目中使用的缓存, 没有好好清理, 又因为项目中缓存种类比较多, 调试分析了比较久 - - 最后是老大, 看到有个注册定时任务的地方, 当中的代码有比较严重的 隐患 -1. 原因 - - 在一个方法中调用了一个异步的定时任务, 并且声明了一个final 变量 给这个任务操作, 并且任务中的代码没有做好安全防护(try catch), 直接就一溜写下去 - - 这里就存在一个隐患了, 如果任务执行失败抛出异常 主线程并不能收到错误提示, 后面的资源回收就无法执行, 然后该任务在定时的报错, 自己和所持有的final 变量也无法释放 - - 主线程也不知情....... -1. 结论 - - final 使用时, 要考虑为什么要用, - - 如果是有了异步的行为, 就要将异步中的代码好好审视, 不能没有忽视代码所有可能的异常 - - diff --git a/Java/AdvancedLearning/JavaProxy.md b/Java/AdvancedLearning/JavaProxy.md new file mode 100644 index 0000000..aa9503a --- /dev/null +++ b/Java/AdvancedLearning/JavaProxy.md @@ -0,0 +1,116 @@ +--- +title: Java中的代理 +date: 2019-01-27 21:06:14 +tags: +categories: + - Java +--- + +**目录 start** + +1. [Java中的代理](#java中的代理) + 1. [静态代理](#静态代理) + 1. [动态代理](#动态代理) + 1. [JDK动态代理](#jdk动态代理) + 1. [cglib](#cglib) + 1. [ASM](#asm) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Java中的代理 + +> [参考: Java代理模式汇编](https://blog.csdn.net/JQ_AK47/article/details/85344634) + +在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不能看到的内容和服务或者添加客户需要的额外服务。 + +通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。 + +> 代理模式(Proxy Pattern) :给某一个对象提供一个代理,并由代理对象控制对原对象的引用。 + +按照代理类的创建时期,代理类可分为两种,即动态代理类和静态代理类. +就是我们经常提到的静态代理和动态代理中主要用到的类。静态代理和动态代理的主要区别就是代理类创建的时间不同。 + +静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。 + +## 静态代理 +> 静态代理类:由程序员创建或由特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 + +```java + // 接口以及实现类 (目标接口和目标对象) + public interface HelloSerivice { + public void say(); + } + public class HelloSeriviceImpl implements HelloSerivice{ + @Override + public void say() { + System.out.println("hello world"); + } + } + + // 代理类(同样实现目标接口), 并扩展了对应的方法 + public class HelloSeriviceProxy implements HelloSerivice{ + private HelloSerivice target; + public HelloSeriviceProxy(HelloSerivice target) { + this.target = target; + } + @Override + public void say() { + System.out.println("记录日志"); + target.say(); + System.out.println("回收资源"); + } + } + + // 单元测试 + @Test + public void testProxy(){ + //目标对象 + HelloSerivice target = new HelloSeriviceImpl(); + //代理对象 + HelloSeriviceProxy proxy = new HelloSeriviceProxy(target); + proxy.say(); + } +``` + +这就是一个简单的静态的代理模式的实现。代理模式中的所有角色(代理对象、目标对象、目标对象的接口)等都是在编译期就确定好的。 + +- 静态代理的用途 + - 控制真实对象的访问权限:通过代理对象控制对真实对象的使用权限。 + - 避免创建大对象:通过使用一个代理小对象来代表一个真实的大对象,可以减少系统资源的消耗,对系统进行优化并提高运行速度。 + - 增强真实对象的功能:通过代理可以在调用真实对象的方法的前后增加额外功能。 + +************************ + +## 动态代理 +> 动态代理类:在程序运行时,运用反射机制动态创建而成。动态代理中的代理类并不要求在编译期就确定,而是可以在运行期动态生成,从而实现对目标对象的代理功能。 + +> [参考: Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)](https://blog.csdn.net/luanlouis/article/details/24589193) + +静态代理的缺点是想用这样的代理就必须要手动硬编码实现, 比较繁琐, 尤其是接口中的方法很多的时候, 动态代理就能解决这个问题 +在我们平时使用的框架中,像servlet的filter、包括spring提供的aop以及struts2的拦截器, mybatis分页插件,以及日志拦截、事务拦截、权限拦截这些几乎全部有动态代理的身影。 + +- 往往实现动态代理有两种方式: + - JDK动态代理: `java.lang.reflect` 包中的`Proxy`类和`InvocationHandler`接口提供了生成动态代理类的能力。 + - Cglib: 是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。 + +### JDK动态代理 +> [Oracle api](https://docs.oracle.com/javase/8/docs/api/) + +jdk动态代理是由java内部的反射机制来实现的 +JDK动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口。因为JDK动态代理只能对接口产生代理,不能对类产生代理 + +### cglib +> `It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.` + +[Cglib (Byte Code Generation Library)](https://github.com/cglib/cglib) + +Cglib是针对类来实现代理的(所以无需声明接口),他的原理是对代理的目标类生成一个子类,并覆盖其中方法实现增强,因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理类的缺陷. +Cglib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它需要你对JVM内部结构包括class文件的格式和指令集都很熟悉。 + +这就不得不说到CgLib的特点:创建速度慢但执行速度快,而JDK的动态代理与其刚好相反:创建速度快但执行速度慢。 +如果在程序运行时不断地使用CgLib去创建代理的话,系统运行的性能会大打折扣,所以建议一般在系统初始化时采用CgLib来创建代理,并放入Spring的ApplicationContext中。 + +### ASM +> [Official site](https://asm.ow2.io/) | [Github](https://github.com/llbit/ow2-asm) + +> [使用 ASM 实现 Java 语言的“多重继承”](https://www.ibm.com/developerworks/cn/java/j-lo-asm/) diff --git a/Java/AdvancedLearning/JavaReflection.md b/Java/AdvancedLearning/JavaReflection.md new file mode 100644 index 0000000..6b305db --- /dev/null +++ b/Java/AdvancedLearning/JavaReflection.md @@ -0,0 +1,298 @@ +--- +title: Java反射原理以及使用 +date: 2018-11-21 10:56:52 +tags: + - Reflect +categories: + - Java +--- + +**目录 start** + +1. [反射](#反射) +1. [概念](#概念) +1. [实现原理](#实现原理) + 1. [Inflation](#inflation) +1. [基础类](#基础类) + 1. [AccessibleObject](#accessibleobject) + 1. [Annotation](#annotation) + 1. [Class](#class) + 1. [Field](#field) + 1. [Method](#method) + 1. [Constructor](#constructor) + 1. [Modifier](#modifier) +1. [使用](#使用) + 1. [获取Class对象的方式](#获取class对象的方式) + 1. [反射的基本使用](#反射的基本使用) + 1. [操作构造方法](#操作构造方法) + 1. [操作类中方法](#操作类中方法) + 1. [操作类的成员属性](#操作类的成员属性) + 1. [操作注解](#操作注解) +1. [反射的性能问题](#反射的性能问题) + +**目录 end**|_2023-08-25 15:50_| +**************************************** +# 反射 +> Reflection is powerful, but should not be used indiscriminately. +> If it is possible to perform an operation without using reflection, then it is preferable to avoid using it. + +> [Offcial Tutorials: reflection](https://docs.oracle.com/javase/tutorial/reflect/index.html) + +> [ Java反射异常处理之InvocationTargetException ](https://blog.csdn.net/zhangzeyuaaa/article/details/39611467) + +> [参考: java8--类加载机制与反射(java疯狂讲义3复习笔记)](https://www.cnblogs.com/lakeslove/p/5978382.html) +> [参考: Java8替代传统反射动态获取成员变量值的一个示例](https://segmentfault.com/a/1190000007492958) + +> [参考: java反射的性能问题](http://www.cnblogs.com/zhishan/p/3195771.html) + +# 概念 + +在运行时 反射使程序能够在运行时探知类的结构信息:构造器,方法,字段... 并且依赖这些结构信息完成相应的操作,比如创建对象,方法调用,字段赋值... +这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 + +# 实现原理 +> [参考: RednaxelaFX 关于反射调用方法的一个log ](https://www.iteye.com/blog/rednaxelafx-548536) +> [Java 虚拟机:JVM是如何实现反射的?](https://cloud.tencent.com/developer/article/1786456) + +## Inflation +考虑到许多反射调用仅会执行一次,Java 虚拟机设置了一个阈值 15(可以通过 -Dsun.reflect.inflationThreshold= 来调整),当某个反射调用的调用次数在 15 之下时,采用本地实现;当达到 15 时,便开始动态生成字节码,并将委派实现的委派对象切换至动态实现,这个过程我们称之为 Inflation。 + + +https://www.jianshu.com/p/20b7ab284c0a +https://cloud.tencent.com/developer/news/663586 + +************************ + +# 基础类 +> Java 反射的实现类 + +## AccessibleObject +> The AccessibleObject class is the base class for Field, Method and Constructor objects. It provides the ability to flag a reflected object as suppressing default Java language access control checks when it is used. + +> AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。它提供了将反射的对象标记为 具有在使用时禁止Java语言的`默认访问控制检查`的能力。 + +对于公共成员、默认(打包)访问成员、受保护成员和私有成员,在分别使用 Field、Method 或 Constructor 对象来设置或获得字段、调用方法,或者创建和初始化类的新实例的时候,会执行访问检查。 +在反射对象中设置 accessible 标志允许具有足够特权的复杂应用程序(比如 Java Object Serialization 或其他持久性机制)以某种通常禁止使用的方式来操作对象。 + +> 将此对象的 accessible 标志设置为指示的布尔值。 +> true 表示反射的对象在使用时应该取消 Java 语言访问检查, 反之则要进行。 +> 此标志不会告诉您java访问修饰符是否可以访问该字段,它会告诉您当前是否忽略这些修饰符。 + +实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问,为false就不能访问,一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外, +但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用`AccessibleObject`上的`setAccessible(true)`方法来允许这种访问, +而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在这些类上调用setAccessible()方法,我们可以实现对这些字段的操作。 +但有的时候这将会成为一个安全隐患,为此,我们可以启用java.security.manager来判断程序是否具有调用setAccessible()的权限。 + +> 默认情况下,`内核API`和`扩展目录`的代码具有该权限,而`类路径`或`通过URLClassLoader加载`的应用程序不拥有此权限。 + +- [ ] 仍然存疑, 什么情况下才是 默认可访问的。什么情况下 true 不能访问 + +## Annotation +## Class +## Field +## Method +## Constructor + +## Modifier +> The Modifier class provides static methods and constants to decode class and member access modifiers. +> [API: modifier](https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/reflect/Modifier.html) + +`Java的访问权限信息是以2的N次幂也就是bitmap的方式进行存储 一共有12个常用修饰符 也就使用了12位来标记` +```java + // 公共的, 意义一致 + PUBLIC = 0x00000001; + PRIVATE = 0x00000002; + PROTECTED = 0x00000004; + STATIC = 0x00000008; + FINAL = 0x00000010; + SYNCHRONIZED = 0x00000020; + VOLATILE = 0x00000040; + TRANSIENT = 0x00000080; + NATIVE = 0x00000100; + INTERFACE = 0x00000200; + ABSTRACT = 0x00000400; + STRICT = 0x00000800; + + // 不公开, 意义依据方法或者属性而定, 最大到16位 + BRIDGE = 0x00000040; + VARARGS = 0x00000080; + SYNTHETIC = 0x00001000; + ANNOTATION = 0x00002000; + ENUM = 0x00004000; + MANDATED = 0x00008000; +``` + +- 判断属性是否被 final 修饰 `Modifier.isFinal(field.getModifiers())` +- 移除 final 修饰符 `field.getModifiers() & ~Modifier.FINAL` + +```java + Field field = ReflectTargetObject.class.getDeclaredField("staticFinalString"); + Field modifiersField = Field.class.getDeclaredField("modifiers"); + modifiersField.setAccessible(true); + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); +``` + +***************************** + +# 使用 +> 具有的功能 +1. 在运行时获取任意一个类所具有的成员变量和方法以及泛型类型; +1. 在运行时构造任意一个类的对象; +1. 在运行时调用任意一个对象的方法; +1. 生成动态代理。 + +> 泛型擦除的存在, 但是泛型如果被用来进行声明, 类上,字段上,方法参数和方法返回值上,这些属于类的结构信息其实是会被编译进Class文件中的; +> 而泛型如果被用来使用,常见的方法体中带泛型的局部变量,其类型信息不会被编译进Class文件中。 +> 前者因为存在于Class文件中,所以运行时通过反射还是能够获得其类型信息的; + +## 获取Class对象的方式 +> 所有的反射操作的入口都是从Class对象开始的, 获取Class对象有多种方式 + +1. 通过类加载器加载class文件 + - `Class clazz = Thread.currentThread().getContextClassLoader().loadClass("com.takumiCX.reflect.ClassTest");` +1. 通过静态方法 Class.forName() 获取,需要传入类的全限定名字符串作参数 + - `Class clazz = Class.forName("com.takumiCX.reflect.ClassTest");` +1. 通过 类.class 获得类的Class对象 + - `Class clazz = ClassTest.class;` + +除了获得的Class对象的泛型类型信息不一样外,还有一个不同点值得注意。只有 forName() 方式 在获得class对象的同时会引起类的初始化 + +************************ + +## 反射的基本使用 + +### 操作构造方法 +使用newInstance()操作无参构造方法 +> 使用Class类中的newInstance()方法进行实例化操作, 但该方法必须要求类有无参构造方法 + +使用Class类中的getConstructors()获取所有构造方法 +```java + Class cls = Class.forName("first.Book"); + Constructor[] constructors = cls.getConstructors(); +``` + +使用Class类中的getConstructor获取指定参数类型的构造方法 +```java + Class cls = Class.forName("first.Book"); + Constructor constructor = cls.getConstructor(String.class, String.class); + // 实例化对象 + Book book = (Book) constructor.newInstance("java", "123"); + System.out.println(book); +``` + +### 操作类中方法 +`getDeclaredMethods()` 获取类本身定义的所有方法, 不包含由继承获取到的方法 +```java + Class cls = Class.forName("first.Book"); + Method[] methods = cls.getDeclaredMethods(); + for (Method method : methods) { + System.out.println(method); + } +``` + +获取指定的方法 +```java + // 传入方法名和参数类型 + Method method = cls.getDeclaredMethod("setName", String.class); +``` + +`getMethods()` 获取所有方法, 包含由继承获取到的方法, 但无法取得自身私有方法 +```java + Class cls = Class.forName("first.Book"); + Method[] methods = cls.getMethods(); + for (Method method : methods) { + System.out.println(method); + } +``` + +获取指定的方法 +```java + Method method = cls.getMethod("setName", String.class); +``` + +调用方法 +```java + Class cls = Class.forName("first.Book"); + Object object = cls.newInstance(); + Method method = cls.getMethod("setName", String.class); + // 调用方法 + method.invoke(object, "hello"); +``` +### 操作类的成员属性 + +> 取得所有成员 +```java + Class cls = Class.forName("first.Book"); + Field[] fields = cls.getDeclaredFields(); + for (Field field : fields) { + System.out.println(field); + } +``` + +> 获取单个成员 +```java + Class cls = Class.forName("first.Book"); + Field field = cls.getDeclaredField("name"); + System.out.println(field); +``` + +> 取得所有成员, 包含由继承获取的成员, 但无法取得自身私有成员 +```java + Class cls = Class.forName("first.Book"); + Field[] fields = cls.getFields(); + for (Field field : fields) { + System.out.println(field); + } +``` + +> set 和 get 属性的值 +```java + Class cls = Class.forName("first.Book"); + Object object = cls.newInstance(); + Field field = cls.getDeclaredField("name"); + // 取消Java安全性检查 + field.setAccessible(true); + // 设置值 + field.set(object, "你好"); + System.out.println(field.get(object)); +``` + +### 操作注解 +> 获取类的注解 +```java + Class cls = Class.forName("first.Book"); + Annotation[] as = cls.getAnnotations(); + for (Annotation a : as) { + System.out.println(a); + } +``` + +> 获取指定的Annotation +```java + Class cls = Class.forName("first.Book"); + GetItem annotation = cls.getAnnotation(Deprecated.class); + System.out.println(annotation.name()); + System.out.println(annotation.value()); +``` + +************************ + +[Github: 更多反射Demo](https://github.com/Kuangcp/JavaBase/tree/class/src/test/java/com/github/kuangcp/reflects) + +正常情况下 final修饰的类,变量,方法, 表示不可继承,不可修改,不可重写(override), 但是使用反射能在一定程度上进行修改 +被final修饰过的变量,只是说栈存储的地址不能再改变,但是却没有说地址指向的内容不能改变,所以反射可以破final,因为它修改该了以前地址的具体内容,但是没有改地址的信息。 +> 参考 [JavaDoc: Java8](https://docs.oracle.com/javase/8/docs/api/) `Field.set()`的文档说明 + +********************** + +# 反射的性能问题 +> [参考: java反射的性能问题 ](http://www.cnblogs.com/zhishan/p/3195771.html) +> [性能测试对比: 反射 set/get cglib mapstruct](https://github.com/Kuangcp/JavaBase/blob/class/src/test/java/com/github/kuangcp/reflects/ReflectPerformanceTest.java) + +Spring 中的 IOC 主要是依据反射来实现的, 只在启动阶段性能有所损耗, 关注性能以及热点代码最好避免使用反射 例如常见的BeanCopy + +[从一起GC血案谈到反射原理](https://club.perfma.com/article/54786)`总结: Method等Accessor对象每次get时会复制构造出新的对象,所以一般需要缓存; 反射数据是软引用` + +> 优化方案 +1. [使用 MapStruct 预生成转换代码避免反射](/Java/Tool/MapStruct.md) + diff --git a/Java/AdvancedLearning/JavaReleaseVersion.md b/Java/AdvancedLearning/JavaReleaseVersion.md deleted file mode 100644 index 48757f8..0000000 --- a/Java/AdvancedLearning/JavaReleaseVersion.md +++ /dev/null @@ -1,45 +0,0 @@ -`目录 start` - -- [Java主要发行版本](#java主要发行版本) - - [Java7](#java7) - - [Java8](#java8) - - [Java9](#java9) - - [Java10](#java10) - - [Java11](#java11) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Java主要发行版本 -> [官网 Release Note](http://www.oracle.com/technetwork/java/javase/jdk-relnotes-index-2162236.html) - -## Java7 -> [详情](/Java/AdvancedLearning/Java7.md) - -1. 语法糖:数字中的下划线 -1. 新的语言小特性:TWR(try with resources) -1. 类文件格式的变化:注解 -1. JVM的新特性: 动态调用 - - -## Java8 -> [详情](/Java/AdvancedLearning/Java8.md) - -1. 接口中新增 静态方法,默认方法 -1. Optional -1. Lambda -1. Stream -1. java.time 包 增强了日期时间的处理 - -- 181 版本移除了 Derby - -## Java9 -> [参考博客: Java9 新特性汇总](http://www.infoq.com/cn/news/2014/09/java9) -> [参考博客: Java9 新特性 详解](https://my.oschina.net/u/3209213/blog/1622984) - -1. 模块化 - -## Java10 -1. 类型推断 - -## Java11 - diff --git a/Java/AdvancedLearning/JavaSPI.md b/Java/AdvancedLearning/JavaSPI.md new file mode 100644 index 0000000..d0437d9 --- /dev/null +++ b/Java/AdvancedLearning/JavaSPI.md @@ -0,0 +1,56 @@ +--- +title: SPI +date: 2019-05-19 16:53:19 +tags: +categories: + - Java +--- + +**目录 start** + +1. [Java中的SPI](#java中的spi) + 1. [项目中的实现](#项目中的实现) + 1. [JDBC](#jdbc) + 1. [Lombok](#lombok) + 1. [SLF4J](#slf4j) + 1. [Dubbo](#dubbo) + 1. [Jigsaw](#jigsaw) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Java中的SPI +> Service Provider Interface + +> [Offcial Tutorials](https://docs.oracle.com/javase/tutorial/ext/basics/spi.html) +> [参考: Java SPI思想梳理](https://zhuanlan.zhihu.com/p/28909673) +> [参考: SPI 概述](https://zhoukaibo.com/2019/03/16/java-spi/) + + +Java spi的具体约定如下: +当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。 +该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名 +并装载实例化,完成模块的注入, 基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码里制定。 + +jdk提供服务实现查找的一个工具类:java.util.ServiceLoader + +## 项目中的实现 +> [参考: Slf4j框架理解与分析 ](https://blog.mythsman.com/2018/02/04/1/) + +### JDBC +我们在入门的时候都学过用jdbc包,用的时候我们都被要求写这行代码, 加载驱动类 `Class.forName("com.mysql.cj.jdbc.Driver");` +其实这段代码没有任何实际意义,只是显式的加载了一个类,告诉我们记得添加这个jar包,实际上只要将这个jar包放在了类路径里面,这段话其实就没有必要了。 + +我们去查 mysql-connector-java 这个包就会发现,他用的就是spi的方法,将自己的 com.mysql.cj.jdbc.Driver 这个类注册给了 java.sql.Driver 这个接口。加载的时候用的其实也是 ServiceLoader 。 + +### Lombok +lombok的原理也是类似,他用自己写的 AnnotationProcessor 去实现 javax.annotation.processing.Processor ,从而做到在编译期进行注解处理。 + +### SLF4J + +### Dubbo +> [参考: SPI Loading](http://dubbo.apache.org/zh-cn/docs/dev/SPI.html) + +### Jigsaw +Java9推出的模块化系统 JPMS Java Platform Module System + +通过改进的SPI机制来实现模块的依赖注入 diff --git a/Java/AdvancedLearning/JavaSerialize.md b/Java/AdvancedLearning/JavaSerialize.md new file mode 100644 index 0000000..03dbafc --- /dev/null +++ b/Java/AdvancedLearning/JavaSerialize.md @@ -0,0 +1,137 @@ +--- +title: Java中的序列化 +date: 2019-04-05 22:38:44 +tags: +categories: + - Java +--- + +💠 + +- 1. [Java中的序列化](#java中的序列化) + - 1.1. [Serializable](#serializable) + - 1.1.1. [JDK序列化和反序列化](#jdk序列化和反序列化) +- 2. [编解码框架](#编解码框架) + - 2.1. [LZ4](#lz4) + - 2.2. [fast-serialization](#fast-serialization) + - 2.3. [Snappy](#snappy) + - 2.4. [Kryo](#kryo) + - 2.5. [JSON](#json) + - 2.6. [Protobuf](#protobuf) + - 2.7. [Marshalling](#marshalling) +- 3. [Tips](#tips) + - 3.1. [JSON字符串反序列化时泛型丢失问题](#json字符串反序列化时泛型丢失问题) + +💠 2024-10-10 20:43:07 +**************************************** +# Java中的序列化 +> [码农翻身:序列化: 一个老家伙的咸鱼翻身](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513589&idx=1&sn=d402d623d9121453f1e570395c7f99d7&chksm=80d67a36b7a1f32054d4c779dd26e8f97a075cf4d9ed1281f16d09f1df50a29319cd37520377&scene=21#wechat_redirect) `对象转化为二进制流` + +- 序列化: 将数据结构或对象转换成二进制串的过程 +- 反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程 + - 反序列化生成对象时不会调用对应类的构造器 + +> [Note:序列化](/Skills/Serialization/Serialization.md)`语言无关` +> [jvm-serializers](https://github.com/eishay/jvm-serializers)`多种框架的基准测试` + +> [Java 序列化10倍性能优化对比测试-腾讯云开发者社区-腾讯云](https://cloud.tencent.com/developer/article/2189625) + +默认序列化, 显式序列化, 拷贝不变(trivially copyable) `和FlatBuffers思想类似` + +## Serializable +> 简单的说serialVersionUID就是类的版本控制, 标明类序列化时的版本, 版本一致表明这两个类定义一致 +> 在进行反序列化时, JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException) + +- serialVersionUID有两种显示的生成方式: + - 一种是固定常量值,例如1L + - 一种是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段 + +> 当你一个类实现了Serializable接口,如果没有定义serialVersionUID,可通过IDE进行提醒显示定义。 + +### JDK序列化和反序列化 +```java + TargetObject targetObject = new TargetObject("name"); + ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); + ObjectOutputStream output = new ObjectOutputStream(byteOutput); + output.writeObject(targetObject); + + ByteArrayInputStream byteInput = new ByteArrayInputStream(byteOutput.toByteArray()); + ObjectInputStream input = new ObjectInputStream(byteInput); + TargetObject result = (TargetObject) input.readObject(); + assertThat(result.getName(), equalTo("name")); +``` + +- 在做有多态结构的`对象深拷贝`时,使用JDK序列化方式能简单且快速实现。但如果使用JSON序列化方式来实现,需要解决节点**类型信息丢失**的问题 + - 例如一个多叉树上的节点是一个接口的多类型实例。 + ```java + public interface Node { + List getChildes(); + } + @Data + public class Dir implements Node { + private List childes; + } + @Data + public class File implements Node { + private List childes; + } + ``` +****************************** + +# 编解码框架 +> 因为Java序列化的性能和存储开销都表现不好,而且不能跨语言, 所以一般不使用Java的序列化而是使用以下流行的库 + +## LZ4 +> [Github](https://github.com/lz4/lz4-java) + +## fast-serialization +> [Github](https://github.com/RuedigerMoeller/fast-serialization) 10倍于JDK序列化性能而且100%兼容的编码 + +## Snappy +> [Github](https://github.com/xerial/snappy-java) + +## Kryo +> [Github](https://github.com/EsotericSoftware/kryo) + +基准测试中得分最高的框架 + +## JSON +- [JSR 367: JSON-B](https://jcp.org/en/jsr/detail?id=367) +- [Jackson](https://github.com/FasterXML/jackson) +- [Gson](https://github.com/google/gson) +- [fastjson](https://github.com/alibaba/fastjson) [FASTJSON2](https://github.com/alibaba/fastjson2) + +> [Github Topic: java-json](https://github.com/topics/java-json) + +## Protobuf +> [Note](/Skills/Serialization/Protobuf.md) +> [Protocol Buffer Basics: Java](https://protobuf.dev/getting-started/javatutorial/) + +`hi.proto` 快速试用 +```protobuf + package lm; + message helloworld{ + required int32 id = 1;//ID + required string str = 2;//str + optional int32 opt = 3;//optional field + } +``` +- 由 proto 编译生成 Java 类: `mkdir src && protoc --java_out=./src hi.proto` + +工程内使用流程简述: 通过插件将proto文件编译到指定目录(该目录设置为source并被git忽略)下的Java类, 项目编译和运行时就可以使用这些类,注意修改了协议文件就需要手动编译一次 +插件: maven-protoc-plugin 或 protobuf-gradle-plugin + +********************* + +## Marshalling +> JBOSS 内部使用的编解码框架 + +************************ + +# Tips +## JSON字符串反序列化时泛型丢失问题 + +1. Jackson 方式 需要先配置 `objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);` + 1. 第一种 `objectMapper.writeValueAsBytes` + 1. 第二种 `objectMapper.readValue(bytes, 0, bytes.length, Object.class)` + diff --git a/Java/AdvancedLearning/JavaThread.md b/Java/AdvancedLearning/JavaThread.md new file mode 100644 index 0000000..834050e --- /dev/null +++ b/Java/AdvancedLearning/JavaThread.md @@ -0,0 +1,222 @@ +--- +title: Java线程 +date: 2018-11-21 10:56:52 +tags: + - Thread +categories: + - Java +--- + +💠 + +- 1. [Java线程](#java线程) +- 2. [生命周期](#生命周期) + - 2.1. [创建](#创建) + - 2.2. [控制](#控制) + - 2.2.1. [yield](#yield) + - 2.2.2. [join](#join) + - 2.2.3. [interrupt](#interrupt) + - 2.2.4. [Signal](#signal) + - 2.3. [销毁](#销毁) + - 2.3.1. [观测异常](#观测异常) +- 3. [ThreadLocal](#threadlocal) + - 3.1. [Hook](#hook) + - 3.2. [优雅关机](#优雅关机) +- 4. [CompletableFuture](#completablefuture) +- 5. [线程池](#线程池) +- 6. [协程](#协程) + - 6.1. [Quasar](#quasar) + - 6.2. [Virtual Threads](#virtual-threads) + +💠 2024-09-13 10:39:04 +**************************************** +# Java线程 +> [个人学习代码](https://github.com/Kuangcp/JavaBase/tree/master/concurrency/src/main/java/thread) + +> [Java并发](/Java/AdvancedLearning/JavaConcurrency.md) 当开始使用多线程时,就要开始考虑并发安全了 + +- [码农翻身:我是一个线程](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=416915373&idx=1&sn=f80a13b099237534a3ef777d511d831a&scene=21#wechat_redirect) | [码农翻身:编程世界的那把锁](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513653&idx=1&sn=e30c18c0c1780fb3ef0cdb858ee5201e&chksm=80d67af6b7a1f3e059466302c2c04c14d097c1a5de01cf986df84d4677299542f12b974dfde3&scene=21#wechat_redirect) | [码农翻身:加锁还是不加锁,这是一个问题 ](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513692&idx=1&sn=ef2416a4bb96d64db77e32d5b4c7967e&chksm=80d67a9fb7a1f3898e513cc1d9e96841610bb84aed2dc24cab2d403e74e317e3c447e45e7611&scene=21#wechat_redirect) + +************************ + +> [参考: 面试官:Java如何绑定线程到指定CPU上执行? ](https://mp.weixin.qq.com/s?__biz=Mzg3NjU3NTkwMQ==&mid=2247515262&idx=1&sn=9f2314cffc3cca3744f63b418654a9c0&scene=21#wechat_redirect) +> [Thread Affinity](https://github.com/OpenHFT/Java-Thread-Affinity)`底层优化选项:更多复用缓存以及减少线程的上下文切换` + +还可以将应用做强定制化,网卡绑定CPU,计算绑定CPU。能避免调度开销,但同时这是双刃剑,资源没有经过操作系统统一调度无法做到资源的有效共享。类似于虚拟化和物理机的一种权衡,虚拟化可以让资源共享,但是降低了CPU执行效率。物理机可以独占CPU,没法让CPU资源充分利用。 + +************************ + +# 生命周期 +> [参考博客](https://segmentfault.com/a/1190000005006079) | [Blog: 线程详解](http://www.cnblogs.com/riskyer/p/3263032.html) | [参考Java-learning仓库](https://github.com/brianway/java-learning) + +> java.lang.Thread.State +- NEW +- RUNNABLE +- BLOCKED +- WAITING + - Object.wait() + - Thread.join() + - LockSupport.park() +- TIMED_WAITING + - Thread.sleep() + - Object.wait(timeout) + - Thread.join(timeout) + - LockSupport.parkNanos() + - LockSupport.parkUntil() +- TERMINATED + - 终止态,不管是正常执行结束还是异常中断。 + +## 创建 +- 创建线程有三种创建方式: 继承,实现接口,实例化匿名方法。[示例代码](https://github.com/Kuangcp/JavaBase/blob/master/concurrency/src/main/java/thread/HowToCreateThread.java) + +> Thread类源码 Runnable,target,run,start 关系 +- Runnable是一个接口 +- target是Thread类中类型为Runnable,名为target的属性 +- run是Thread类实现了Runnable的接口,重写的方法。 +- start是启动线程的方法 `native` +- 在Thread类中,调用关系为:_start->start0->run->target.run_ + +_Thread类的run方法源码_ +```java + public void run() { + if (target != null) { + target.run(); + } + } +``` +_Thread类的target属性_ +```java + /* What will be run. */ + private Runnable target; +``` +- target属性由 `private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc)`方法初始化。 + - init方法在Thread类的构造方法里被调用 + +## 控制 +- 当调用 `join()` 时,`当前调用线程`将会阻塞,直到`目标线程`完成为止。 + +Object.wait 转为两种Waiting状态 + +LockSupport.park + +[Can LockSupport.park() replace Object.wait()?](https://stackoverflow.com/questions/39415636/can-locksupport-park-replace-object-wait) + +[thread states](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr034.html) + +> 线程优先级: 多个线程同时运行时,由线程调度器来决定哪些线程运行,哪些等待以及线程切换的时间点. 由于各个操作系统的线程调度器的实现各不相同, 所以依赖JDK来设置线程优先级策略是错误和平台不可移植性的. + +### yield + +### join + +### interrupt +> [Oracle interrupt](https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html) + +- java.lang.Thread#interrupt + - 这个方法仅仅是标记下状态,在一些阻塞类方法调用时会检查该状态值(sleep wait join yield 等等), 如果线程一直在循环跑CPU计算,那这个线程不会停止 +- java.lang.InterruptedException + ```java + // 判断当前线程是否已发生中断 + if (Thread. interrupted()) // Clears interrupted status! + throw new InterruptedException(); + ``` + +中断机制是通过一个称为中断状态的内部标志来实现的。调用 Thread.interrupt 会设置该标志。当线程通过调用静态方法 Thread.interrupted 检查中断时,中断状态会被清除。一个线程用来查询另一个线程中断状态的非静态 isInterrupted 方法不会改变中断状态标志。 +按照惯例,任何通过抛出 InterruptedException 而退出的方法在退出时都会清除中断状态。不过,中断状态总是有可能被另一个调用中断的线程立即再次设置。 + +************************ + +### Signal +> 由于Java是跨平台语言,主要考虑Window和unix系平台,后者在生产中使用居多,因此重点关注 + +[Linux的Signal](/Linux/Base/LinuxPerformance.md#kill) +快速理解: +- Kill 9信号: 无法监听和屏蔽 +- TERM 15信号:默认退出进程信号 +- INT 2信号: IDEA中停止JVM时发出的就是该信号 + +相关JVM参数 -Xrs 忽略(1,2,3,4,5,6,7,8,11,15) [oracle java command](https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html)`注意Linux和Windows实现及信号量不一样` +- 忽略的逻辑实现为:JVM接收信号量然后什么都不做。 +- 注意此时Java应用代码无法手动监听对应的信号量,注册监听时会报错 + +************************ + +## 销毁 + +### 观测异常 +> java.lang.Thread.UncaughtExceptionHandler `Interface for handlers invoked when a Thread abruptly terminates due to an uncaught exception.` + +通过设置静态属性 `Thread.setDefaultUncaughtExceptionHandler()`,可以观测由于未捕获的异常导致Thread被销毁的情况,可加入监控和告警的逻辑 + +************************ + +# ThreadLocal +> [Oracle: ThreadLocal](https://docs.oracle.com/javase/8/docs/api/java/lang/ThreadLocal.html) + +设计: ThreadLocalMap 线程对象做key的一个封装Map(但是未实现Map接口),一个线程可以有多个ThreadLocal + +> [Alibaba TTL 使用场景](https://github.com/alibaba/transmittable-threalocal/issues/123)`可看作ThreadLocal的一种特殊实现` +- 主要流程: com.alibaba.ttl.TtlRunnable#run + - 提交任务时对run方法封装,先复制当前 TransmittableThreadLocal + - 等待要调度执行时,重放复制的TransmittableThreadLocal值,从而实现父子线程间上下文的传递 + - **注意**:因为只是处理了TransmittableThreadLocal,所以其他ThreadLocal值需要做传递时,需要通过装饰器去手动复制,例如SpringSecurity的SecurityContextHolder, slf4j的MDC + +> [一次「找回」TraceId的问题分析与过程思考](https://tech.meituan.com/2023/04/20/traceid-google-dapper-mtrace.html) + +************************ +## Hook +- 注册Hook:`Runtime.getRuntime().addShutdownHook(Thread thread)` +- 在JVM正常退出时会调用已注册的Hook逻辑 + 1. 例如 System.exit(), 或者 Java 进程收到退出的信号 SIGTERM SIGINT SIGQUIT 等等 + 1. 但是 SIGKILL、 Runtime.halt()、断电、系统Crash 等情况下, `没有时机执行Hook`。 + 1. 不能在Hook逻辑中调用`System.exit()`, 否则会阻塞JVM退出,但是可以调用`Runtime.halt()` + 1. 不能在Hook逻辑中增删Hook + 1. 在`System.exit()`执行后才注册的Hook逻辑不会被执行 + 1. `Hook逻辑执行时完整性不可控` 操作系统可控制当对JVM发出`TERM(15)`信号后一段时间未结束时可强制结束`KILL(9)`,此时Hook逻辑可能才执行了一半 + 1. 注册的Hook是按先后执行的,但是其中任意一个Hook抛出未处理的异常时会中断自身及后续Hook逻辑 + +## 优雅关机 +> Java层面 +1. 线程池设置关闭时等待已有任务线程执行完成 + - 但是通常等待是会有限制(容器的健康检查等)的,所以还是会造成任务的中断,队列中任务的丢失 +1. 手动接收信号量 追加资源关闭逻辑:MQ,缓存,数据库 + +> 环境层面 + +当关闭服务器A时,先将该服务器的入口流量屏蔽,防止新的请求进入,然后等服务器完成原有请求的响应,以及一些资源清理行为后,完全关闭。 + +[参考: Kubernetes 中如何保证优雅地停止 Pod](https://cloud.tencent.com/developer/article/1409225) +[参考: JVM安全退出(如何优雅的关闭java服务)](https://www.cnblogs.com/yuandluck/p/9517700.html) + +************************ +# CompletableFuture +> [CompletableFutureTest](https://github.com/Kuangcp/JavaBase/blob/master/java8/src/test/java/com/github/kuangcp/future/CompletableFutureTest.java) + +************************ + +# 线程池 +> [Note: 线程池](/Java/AdvancedLearning/Concurrency/ExecutorAndPool.md) + +************************ + +# 协程 +R大: JVM虚拟机未明确定义JVM线程和OS线程的关系,即可以1:1, N:1, M:N。 只是Hotspot实现为1:1。并且很早期的JDK就是N:1的绿色线程实现,后面才改成1:1和系统线程绑定 + +## Quasar +> [Github: Quasar](https://github.com/puniverse/quasar) + +## Virtual Threads +> [Virtual Threads](https://openjdk.org/jeps/444) 19预览 21Release | 来源于 [OpenJDK: Loom](https://wiki.openjdk.org/display/loom)`项目目标高吞吐量,轻量级并发模型,结构化并发&调度` + +试用总结:如果要引入生产,需要关注整个JEP的文档,调试确认细节后才能使用,不然就会陷入到各种诡异的问题上。 + +特性: +- 依赖一个公用的ForkJoin线程池执行任务 即 不推荐执行CPU密集型任务,只建议用来执行io密集类任务(21对有可能阻塞的api都加上了特定处理代码)从而提高吞吐量 +- 正常线程内代码无法感知 协程内代码的异常,反之也是一样,线程和协程间的局部变量也是隔离的 +- 协程的线程栈存储在堆内存中,为了规避大量协程导致的栈溢出 + +> [虚拟线程:Java的新利器?](https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&mid=2247538915&idx=1&sn=b9b6a303a79cea5225e0d445e10eddc8&scene=58&subscene=0) +> [Java19 正式 GA!看虚拟线程如何大幅提高系统吞吐量 ](https://mp.weixin.qq.com/s/yyApBXxpXxVwttr01Hld6Q) +> [虚拟线程 - VirtualThread源码透视 ](https://www.cnblogs.com/throwable/p/16758997.html) + + diff --git a/Java/AdvancedLearning/JvmGC.md b/Java/AdvancedLearning/JvmGC.md new file mode 100644 index 0000000..5814335 --- /dev/null +++ b/Java/AdvancedLearning/JvmGC.md @@ -0,0 +1,532 @@ +--- +title: Java之GC +date: 2021-05-14 20:36:48 +tags: +categories: +--- + +💠 + +- 1. [GC](#gc) + - 1.1. [GC类型](#gc类型) + - 1.2. [GC术语](#gc术语) + - 1.2.1. [STW](#stw) + - 1.2.2. [安全点](#安全点) + - 1.3. [内存分代](#内存分代) + - 1.4. [判断存活算法](#判断存活算法) + - 1.4.1. [引用计数算法](#引用计数算法) + - 1.4.2. [可达性分析算法](#可达性分析算法) + - 1.5. [GC算法](#gc算法) + - 1.5.1. [标记清除算法](#标记清除算法) + - 1.5.2. [复制算法](#复制算法) + - 1.5.3. [标记整理算法](#标记整理算法) + - 1.6. [GC Callback](#gc-callback) +- 2. [GC参数](#gc参数) +- 3. [GC日志](#gc日志) +- 4. [垃圾收集器](#垃圾收集器) + - 4.1. [默认垃圾收集器](#默认垃圾收集器) + - 4.2. [Serial](#serial) + - 4.3. [ParNew](#parnew) + - 4.4. [Parallel Scavenge](#parallel-scavenge) + - 4.5. [Serial Old](#serial-old) + - 4.6. [Parallel Old](#parallel-old) + - 4.7. [CMS](#cms) + - 4.8. [G1](#g1) + - 4.9. [ZGC](#zgc) + - 4.10. [ShenandoahGC](#shenandoahgc) + - 4.11. [Epsilon](#epsilon) +- 5. [最佳实践](#最佳实践) + +💠 2024-07-12 11:40:30 +**************************************** +# GC +> Java Garbage Collection + +GC 的目的是识别出不再使用的内存,并将其变为可用内存。现代垃圾收集器通常分为几个阶段,根据不同的分代使用不同的垃圾收集器来完成回收过程 + +- [你能不能谈谈,java GC是在什么时候,对什么东西,做了什么事情?” ](http://itindex.net/detail/54188-java-gc-%E4%B8%9C%E8%A5%BF) `什么时候, 对什么东西, 做了什么` +> 什么时候 +- 程序员不能具体控制时间,系统在不可预测的时间调用System.gc()函数的时候;可以通过调参数,用NewRatio 控制newObject和oldObject的比例,用MaxTenuringThreshold 控制 进入oldObject的次数,使得oldObject 存储空间延迟达到full gc,延迟gc时间 +> 对什么东西 +- 超出了作用域或引用计数为空的对象;从gc root开始搜索找不到的对象,而且经过一次标记、清理,仍然没有复活的对象。 +> 做了什么 +- 删除不使用的对象,回收内存空间;运行默认的finalize,当然程序员想立刻调用就用dipose调用以释放资源如文件句柄,JVM用from survivor、to survivor对它进行标记清理,对象序列化后也可以使它复活。 + +************************ +列表: CMS(JDK14中被移除),G1,Parallel,Serial,Epsilon,Shenandoah,ZGC +> [Github: OpenJDK 12 GC 算法源码](https://github.com/openjdk/jdk/tree/jdk-12+33/src/hotspot/share/gc) +************************ + +## GC类型 +> [RednaxelaFX](https://www.zhihu.com/question/41922036/answer/93079526) | [Major GC和Full GC的区别是什么?触发条件呢?](https://www.zhihu.com/question/41922036/answer/93079526) + +- `Partial GC`:收集部分堆 + - `Young GC`:只收集young gen的GC + - `Old GC`:只收集old gen的GC。只有CMS的concurrent collection是这个模式 + - `Mixed GC`:收集整个young gen以及部分old gen的GC。只有G1有这个模式 + +- `Full GC`:收集整个堆,包括young gen、old gen、perm gen(如果存在的话),metaspace等所有部分的模式。 + - gc日志中会有明确的 `[Full GC]` 字样 + +`新生代GC Minor GC` +也称 Young GC,会引发STW。发生在新生代的垃圾收集动作, 因为大多数对象都是存活时间很短, 所以 Minor GC 非常频繁, 一般回收速度也比较快. +扫描过后将 Eden 和 现在使用的 Survivor 两个区中的存活对象 全搬去空闲的 Survivor. +如果 存活的对象内存大小大于 Survivor 区大小, 则需要`分配担保机制`提前将对象转移到老年代中 + +`老年代GC Major GC` +发生在老年代的GC, 出现了 Major GC, 往往会伴随至少一次 Minor GC. Major GC 的速度一般会比 Minor GC 慢10倍以上. + +> [What causes a Full GC to run?](https://stackoverflow.com/questions/42226785/what-causes-a-full-gc-to-run) + +> [参考: Major GC和Full GC的区别是什么?](https://www.zhihu.com/question/41922036) +- HotSpot上的一次 Full GC: 针对 新生代 老生代 元空间 的全局范围的GC, 将会 STW(Stop The World) + +> 最简单的分代式GC策略,按HotSpot VM的serial GC的实现来看,触发条件是: +- *Young GC*:当young gen 中的 eden gen 分配满的时候触发。注意young GC中有部分存活对象会晋升到old gen,所以young GC后old gen的占用量通常会有所升高。 +- *Full GC*:当准备要触发一次young GC时,如果发现统计数据说之前young GC的平均晋升大小比目前old gen剩余的空间大,则不会触发young GC而是转为触发full GC + - 因为HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都会同时收集整个GC堆,包括young gen,所以不需要事先触发一次单独的young GC +- `perm gen` / `MetaSpace` 内存空间不足时,也会触发一次 Full GC; +- System.gc()、heap dump、jcmd pid GC.run 等指定触发GC时,默认触发 Full GC。 + +> 默认GC + +JDK 7,默认是 Parallel Scavenge + Serial Old。 +JDK 8 及 JDK 7u40 之后的版本,默认是 Parallel Scavenge + Parallel Old。 +JDK 9 到 JDK 17,默认是 G1。 + +************************ + +## GC术语 + +- `串行(Serial)` 只有单个 GC 线程在运行。与上面的并行阶段一样,规范中也没有说明 GC 线程是否可以与当前运行的应用程序线程重叠。 +- `并行(Parallel)` 运行中的 JVM 包含应用程序线程和 GC 线程。在并行阶段,会运行多个 GC 线程,也就是说任务被拆分给它们去完成。 + - 至于 GC 线程是否可以与正在运行的应用程序线程重叠,这个在规范中并没有特别说明。 +- `并发(Concurrent)` GC 线程和应用程序线程并发执行。 +- `增量(Incremental)` 在增量阶段,它可以运行一段时间,并基于某些条件提前终止,例如时间预算或执行更高优先级的 GC 阶段。 + +- `吞吐量 = 运行用户代码时间 / (用户代码时间 + 垃圾收集时间)` +- 并行和并发 : 并行:充分利用多核CPU来缩短STW的时间, 并发:部分其他收集器需要停顿的逻辑也和用户进程并发执行 + +### STW +> [参考: JVM中的STW和CMS](https://blog.csdn.net/zkkzpp258/article/details/80080764) + +Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。 +Java中一种全局暂停现象,全局停顿,所有Java代码停止,native代码可以执行,但不能与JVM交互。这些现象多半是由于GC引起。 + +除了GC,JVM下还有其他原因会触发停顿现象。[JVM Pauses - It's More Than GC ](https://www.reddit.com/r/java/comments/gsp4p4/jvm_pauses_its_more_than_gc/) + +### 安全点 + +JVM里有一种特殊的线程`VM Threads`,专门用来执行一些特殊的VM Operation,比如分派GC,thread dump等,这些任务,都需要整个Heap,以及所有线程的状态是静止的,一致的才能进行。 +所以JVM引入了安全点`Safe Point`的概念,在需要进行VM Operation时,通知所有的线程进入一个静止的安全点。 + +除了GC,其他触发安全点的VM Operation包括: +``` + 1. JIT相关,比如Code deoptimization, Flushing code cache ; + 2. Class redefinition (e.g. javaagent,AOP代码植入的产生的instrumentation) ; + 3. Biased lock revocation 取消偏向锁 ; + 4. Various debug operation (e.g. thread dump or deadlock check); +``` + +- 通过监控安全点,看看JVM到底发生了什么? + - 最简单的做法,在JVM启动参数的GC参数里追加: `-XX:+PrintGCApplicationStoppedTime` 它就会把全部的JVM停顿时间(不只是GC),打印在GC日志里。 +- 如何打印出是哪种原因导致的停顿呢? + - 再多加两个参数:`-XX:+PrintSafepointStatistics -XX: PrintSafepointStatisticsCount=1` + - 此日志分两段,第一段是时间戳,VM Operation 的类型,以及线程概况 + ``` + total: 安全点里的总线程数 + initially_running: 安全点时开始时正在运行状态的线程数 + wait_to_block: 在VM Operation开始前需要等待其暂停的线程数 + ``` + - 第二行是到达安全点时的各个阶段以及执行操作所花的时间,其中最重要的是vmop + ``` + spin: 等待线程响应 + safepoint号召的时间 + block: 暂停所有线程所用的时间 + sync: 等于 spin+block,这是从开始到进入安全点所耗的时间,可用于判断进入安全点耗时 + cleanup: 清理所用时间 + vmop: 真正执行VM Operation的时间 + ``` + +可见,那些很多但又很短的安全点,全都是RevokeBias,详见 偏向锁实现原理, 高并发的应用一般会干脆在启动参数里加一句`-XX:-UseBiasedLocking`取消掉它。 +另外还看到有些类型是no vm operation, 文档上说是保证每秒都有一次进入安全点(如果这秒已经GC过就不用了),给一些需要在安全点里进行,又非紧急的操作使用,比如一些采样型的Profiler工具,可用`-DGuaranteedSafepointInterval`来调整,不过实际看它并不是每秒都会发生,时间不定。 + +在实战中,我们利用安全点日志,发现过有程序定时调用Thread Dump等等情况。不过因为安全点日志默认输出到stdout,因为性能及stdout日志的整洁性等原因,我们平时默认没有开启它。只有在需要时才打开。 + +再再增加下面三个参数,可以知道更多VM里发生的事情。可惜JVM不会因为设了这三个参数,就把安全点日志转移到vm.log里面来,而是白白打印了两次。 + +`-XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=/dev/shm/vm.log` + +************************ + +## 内存分代 + +> [Oracle Java8 Doc: Generation](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/generations.html#sthref16) + +![](img/003-jvm-generation-design.drawio.svg) + +> [参考: JVM中新生代为什么要有两个Survivor(form,to)?](https://www.zhihu.com/question/44929481) +> [参考: 为什么新生代内存需要有两个Survivor区](https://blog.csdn.net/antony9118/article/details/51425581) + +> [聊聊JVM的年轻代](http://ifeve.com/jvm-yong-generation/) +> 我是一个普通的java对象,我出生在Eden区,在Eden区我还看到和我长的很像的小兄弟,我们在Eden区中玩了挺长时间。 +有一天Eden区中的人实在是太多了,我就被迫去了Survivor区的“From”区,自从去了Survivor区,我就开始漂了, +有时候在Survivor的“From”区,有时候在Survivor的“To”区,居无定所。 +直到我18岁的时候,爸爸说我成人了,该去社会上闯闯了。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的,我在这里也认识了很多人。 +在年老代里,我生活了20年(每次GC加一岁),然后被回收。 + +## 判断存活算法 +### 引用计数算法 +> 给对象添加一个引用计数器, 每当有一个地方引用该对象就加一, 引用失效就减一; 计数器值为零的对象就是不可能被使用的对象 + +但是该算法无法解决 对象间循环引用的问题, 例如:A 引用 B, B 引用 A, 此时两个对象的计数大于0,但是这两个对象都没被其他对象引用。 + +可引入 Recycler算法 进行解决 --《垃圾回收算法手册》 + +`思路大致是 找出循环引用的环,尝试遍历(可能有多个环混合,这里是一个图结构)并打破环并移除环内对象的内部引用,如果计数仍大于0表明该环有被其他对象引用,需要恢复破坏的引用关系,否则全部清除` + +### 可达性分析算法 +当一个对象到 GC Roots 对象没有任何引用链相连时(或者说从 GC Roots 到该对象的路径不可达), 则证明该对象是可回收的 + +GC Roots 对象包含: +- 虚拟机栈(栈帧中的本地变量表)中引用的对象 +- 方法区中类静态属性引用的对象 +- 方法去中常量引用的对象 +- 本地方法栈中 JNI (Native 方法) 引用的对象 +- 所有线程对象 +- 系统类加载器及自定义类加载器 +- 锁对象 + +> [Guide to Garbage Collector Roots](https://www.baeldung.com/java-gc-roots)`重点:每个JVM实现及GC实现没有强制的规范,只能通过MAT等工具分析,以上仅为常见的Root类型对象` + +************************ + +## GC算法 +### 标记清除算法 +> Mark-Sweep 首先标记出所有需要回收的对象, 在标记完成后统一回收 + + 回收过程主要分为两个阶段,第一阶段为追踪(Tracing)阶段,即从 GC Root 开始遍历对象图,并标记(Mark)所遇到的每个对象,第二阶段为清除(Sweep)阶段,即回收器检查堆中每一个对象,并将所有未被标记的对象进行回收,整个过程不会发生对象移动。整个算法在不同的实现中会使用三色抽象(Tricolour Abstraction)、位图标记(BitMap)等技术来提高算法的效率,存活对象较多时较高效。 + +`缺点` +1. 效率问题: 标记和清除两个过程的效率不高 +1. 空间问题: 容易引起内存碎片化问题, 碎片太多可能导致后期需要分配较大对象时找不到足够大的连续内存 + - 并因此触发一次垃圾收集动作 + +### 复制算法 +> Copying 将内存按容量划分为等大的两块, 每次只使用其中的一块, 当这块的内存用到需要回收了, 就将需要存活的对象复制到另一块上去, 将该块全部清理掉 +> 转而只使用另一个块 这样就不会有内存碎片化问题, 但是可使用的内存只有原来的一半 + +将空间分为两个大小相同的 From 和 To 两个半区,同一时间只会使用其中一个,每次进行回收时将一个半区的存活对象通过复制的方式转移到另一个半区。有递归(Robert R. Fenichel 和 Jerome C. Yochelson提出)和迭代(Cheney 提出)算法,以及解决了前两者递归栈、缓存行等问题的近似优先搜索算法。复制算法可以通过碰撞指针的方式进行快速地分配内存,但是也存在着空间利用率不高的缺点,另外就是存活对象比较大时复制的成本比较高。 + +适用于新生代, 因为新生代对象大部分是存活时间短的 + +标记-复制算法可以分为三个阶段: + +标记阶段,即从GC Roots集合开始,标记活跃对象; +转移阶段,即把活跃对象复制到新的内存地址上; +重定位阶段,因为转移导致对象的地址发生了变化,在重定位阶段,所有指向对象旧地址的指针都要调整到对象新的地址上。 + +### 标记整理算法 +> Mark-Compact 标记过程和标记清除算法是一致的, 但是后续是让存活的对象往一端移动, 清理掉端边界以外的内存. + +这个算法的主要目的就是解决在非移动式回收器中都会存在的碎片化问题,也分为两个阶段,第一阶段与 Mark-Sweep 类似,第二阶段则会对存活对象按照整理顺序(Compaction Order)进行整理。主要实现有双指针(Two-Finger)回收算法、滑动回收(Lisp2)算法和引线整理(Threaded Compaction)算法等。 + +适用于老年代 + +************************ + +## GC Callback +> [Letting the Garbage Collector Do Callbacks](https://dzone.com/articles/letting-garbage-collector-do-c) +> [Garbage Collection JMX Notifications](http://www.fasterj.com/articles/gcnotifs.shtml) + +************************ +# GC参数 +- `-Xloggc:/app/logs/gc_%t_%p.log` 指定GC日志 并 设置文件格式 **注意目录要已存在** + - %t 日期时间 + - %p 进程号 +- `-verbose:gc` +- `-XX:+PrintGCDetails` +- `-XX:+PrintGCDateStamps` +- `-XX:+UseGCLogFileRotation ` +- `-XX:NumberOfGCLogFiles=< number of log files > ` +- `-XX:GCLogFileSize=< file size >[ unit ]` +- `-XX:MaxTenuringThreshold=15` 年轻代对象晋升年龄阈值 默认值15 + +************************ + +# GC日志 +1. 默认第一列是**JVM启动的秒数**,为了可读性一般会加配置 `-XX:+PrintGCDateStamps`, +1. 路径可追加进程id `-Xloggc:/apps/logs/gc-%p.log` 以及 `%t` JVM启动时间 +1. 日志滚动策略 `-XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=14 -XX:GCLogFileSize=100M` **但是实际上不实用**,并非按Logback等框架的思路滚动。重启后会重新从0计数覆盖掉最旧的gc日志 [Try to Avoid -XX:+UseGCLogFileRotation](https://dzone.com/articles/try-to-avoid-xxusegclogfilerotation) + +> [Github: GCViewer](https://github.com/chewiebug/GCViewer) +> [GCView线条图解](https://blog.csdn.net/chy2z/article/details/88651810) + +************************ + +# 垃圾收集器 +> JVM垃圾收集器发展历程 + +- 第一阶段,Serial(串行)收集器 + - 在jdk1.3.1之前,java虚拟机仅仅能使用Serial收集器。 Serial收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅是说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。 +- 第二阶段,Parallel(并行)收集器 + - Parallel收集器也称吞吐量收集器,相比Serial收集器,Parallel最主要的优势在于使用多线程去完成垃圾清理工作,这样可以充分利用多核的特性,大幅降低gc时间。 +- 第三阶段,CMS(并发)收集器 + - CMS收集器在Minor GC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收。在Full GC时不再暂停应用线程,而是使用若干个后台线程定期的对老年代空间进行扫描,及时回收其中不再使用的对象。 +- 第四阶段,G1(并发)收集器 + - G1收集器(或者垃圾优先收集器)的设计初衷是为了尽量缩短处理超大堆(大于4GB)时产生的停顿。相对于CMS的优势而言是内存碎片的产生率大大降低。 + +> `java -XX:+PrintCommandLineFlags -version` 可以通过该命令快速知道当前版本JDK默认垃圾收集器 + +******************* + +> JVM垃圾收集器种类 + +根据设计, 往往是新生代和老年代使用不同的垃圾收集器并组合使用, 因为各分代的对象分配和释放特性不同 + +> 新生代 + +| 类型 | 说明 | +|:----|:----| +| Serial (第一代) | 单线程STW 复制算法 | +| PraNew (第二代) | 多线程并行STW 复制算法| +| Parallel Scavenge (第三代) | 多线程并行STW 吞吐量优化,复制算法| +| G1 (第四代) | 多线程并发,可以精确控制STW时间,整理算法 | + +> 老年代 + +| 类型 | 说明 | +|:----|:----| +| Serial Old (第一代) | | +| Parallel Old (第二代) | | +| CMS (第三代) | | +| G1 (第四代) | | +| ZGC/ShenandoahGC | | + +> 收集器搭配时的限制条件: +- CMS 不能和 Parallel Scavenge 一起用 +- Parallel Old 只能和 Parallel Scavenge 一起用 +- G1 ZGC ShenandoahGC 只能单独使用(独自处理新生代和老年代) + +************************ + +## 默认垃圾收集器 +- `-XX:+PrintCommandLineFlags` 或者查看GC日志中代的名称 `-XX:+PrintGCDetails` +- JDK 1.7 1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代) +- JDK1.9+ 默认垃圾收集器G1 + +************************ + +## Serial +> 单线程垃圾收集器 JDK1.3.1之前唯一选择, 仅用于新生代 + +单线程的收集器, 采用复制算法, client模式下默认收集器, 因为client的内存一般不会很大, 单线程反而效率更高, STW的时间也不会很长 + +************************ + +## ParNew +> Serial 收集器的多线程版本, 仅用于新生代 + +仅有该收集器和Serial收集器能和CMS收集器一起使用, 当使用CMS的时候默认新生代使用ParNew + +> 注: 单核服务器时, 该收集器性能必然比Serial差, 因为线程调度开销 + +************************ + +## Parallel Scavenge +> 并行多线程收集器, 同样使用标记复制算法 着重点是可控制的吞吐量, 可以高效率利用CPU时间, 仅用于新生代 + +`-XX:+UseParallelGC` + +- 控制最大垃圾收集停顿时间 `-XX:MaxGCPauseMillis` (大于0的整数 单位millis) + - 该值并不是越小越好, GC停顿时间缩短是牺牲吞吐量和新生代空间来换取的 + - 新生代空间越小则垃圾收集器回收时间则更短, 但是也更频繁, 停顿时间降下来了,但是吞吐量就下降了 +- 直接设置吞吐量大小 `-XX:GCTimeRatio` 值范围:(0,100) + - 收集器将尽可能保证内存回收的时间不超过设置值, 值为垃圾收集时间占总时间的比率, 相当于吞吐量的倒数 + - 如果设置为 49 则允许的最大GC时间占总时间的 1/(1+49) +- GC自适应策略 `-XX:+UseAdaptiveSizePolicy` + - 该参数启用后, 就无需手动设置新生代的大小(-Xmn)和Eden和Survivor的比例(-XX:SurvivorRatio) 晋升老年代对象大小(-XX:PretenureSizeThreshold) , 虚拟机将动态调整这些参数 + +************************ + +## Serial Old +> Serial收集器的老年代版本, 单线程收集器 + +主要用于 client 模式下 , server 模式下的话, 1.5之前的版本与Parallel Scavenge搭配使用, 或者作为CMS的备选方案 + +************************ + +## Parallel Old +> 是Parallel Scavenge 收集器的老年代版本 + +`-XX:+UseParallelOldGC` + +************************ + +## CMS +> Concurrent Mark Sweep 着重点是尽可能缩短垃圾收集时用户线程的停顿时间 [Oracle Doc](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html) + +`-XX:+UseConcMarkSweepGC` + +工作流程: +1. `初始标记` CMS initial mark **STW** +1. 并发标记 CMS concurrent mark +1. `最终标记` CMS final remark **STW** +1. 并发清除 CMS concurrent sweep + +> 例如: +```log +4936.782: [GC (CMS Initial Mark) [1 CMS-initial-mark: 747140K(1494272K)] 752384K(1800960K), 0.0043788 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] +4936.787: [CMS-concurrent-mark-start] +4936.942: [CMS-concurrent-mark: 0.156/0.156 secs] [Times: user=0.23 sys=0.01, real=0.16 secs] +4936.942: [CMS-concurrent-preclean-start] +4936.948: [CMS-concurrent-preclean: 0.005/0.005 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] +4936.948: [CMS-concurrent-abortable-preclean-start] +4938.832: [GC (Allocation Failure) 2020-11-23T17:06:01.905+0800: 4938.832: [ParNew: 277821K->4257K(306688K), 0.0088608 secs] 1024961K->751463K(1800960K), 0.0089994 secs] [Times: user= +4939.249: [CMS-concurrent-abortable-preclean: 0.774/2.301 secs] [Times: user=1.32 sys=0.09, real=2.31 secs] +4939.250: [GC (CMS Final Remark) [YG occupancy: 142153 K (306688 K)]2020-11-23T17:06:02.323+0800: 4939.250: [Rescan (parallel) , 0.0225236 secs]2020-11-23T17:06:02.346+0800: 4939.273: +4939.382: [CMS-concurrent-sweep-start] +4939.627: [CMS-concurrent-sweep: 0.235/0.245 secs] [Times: user=0.43 sys=0.03, real=0.24 secs] +4939.627: [CMS-concurrent-reset-start] +4939.631: [CMS-concurrent-reset: 0.004/0.004 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] +``` + +优点: 并发低停顿 +缺点: +1. 因为会和用户进程抢占CPU资源, 会导致应用程序变慢, 造成总吞吐量的下降. 默认启动的线程数为 (CPU数量+3)/4 +1. 无法处理浮动垃圾, 可能出现 Concurrent Mode Failure 从而引起新一次FullGC + - 并发清理阶段用户线程还在运行,这段时间就可能产生新的垃圾,新的垃圾在此次GC无法清除,只能等到下次清理 +1. 由于使用的是标记清除算法, 容易导致大量空间碎片, 这样的后果是分配大内存对象会很麻烦, 往往出现老年代总空间还有大量剩余, 但是没有足够大的连续空间 + - 为了解决该问题, 提供了参数 `-XX:+UseCMSCompactAtFullCollection` 默认开启, 用于在FullGC时进行内存碎片的合并, 该过程无法并发还是要 STW + - 还有一个参数 `-XX:CMSFullGCsBeforeCompaction` 默认为0, 设置多少次不压缩的FullGC后进行一次压缩的FullGC(内存合并的FullGC) + +CMS 垃圾收集器的另一个挑战是如何处理老年代中的空间碎片,也就是当老年代中对象间的空间碎片太小,以至于无法容纳从年轻代晋升上来的对象,因为在CMS 的并发收集循环中并不执行压缩,哪怕是增量或局部压缩。 +一旦无法找到可用空间,就会使CMS 回过来使用**Serial Old**,触发一次full收集,导致一个漫长的暂停。伴随CMS 碎片的另一个很不幸的挑战就是上述问题完全无法预测。同样都是老年代碎片,某些应用可能没有经历过一次 full GC,而有些可能时不时就要经历一次。 + +其中 初始标记 和 重新标记 仍然需要 STW, 两个并发的过程是和用户线程并发执行的对吞吐量有一定影响 +且由于是并发执行的, 那么并发的两个阶段用户进程是需要执行的, 就需要给这些线程预留足够的内存空间, 默认触发GC的阈值是 老年代使用了68%后(1.5) 1.6是92% +可通过 `-XXCMSInitiatingOccupancyFraction` 进行设置. 如果CMS执行期间发现剩余内存不足以让程序正常运行, 就会临时启用 **Serial Old** +所以该参数不可设置过高, 否则容易导致频繁采用 **Serial Old**, 大大延长 STW 时间 + +> [参考: JVM 源码解读之 CMS GC 触发条件 ](https://club.perfma.com/article/190389) +> [参考: JVM 源码解读之 CMS 何时会进行 Full GC](https://club.perfma.com/article/244846) + +CMS进入 full GC 的情况是并发收集模式跟不上应用分配内存的速度了,或者是碎片化开始变严重了。 +主要体现是GC日志里可以看到 concurrent mode failure 字样,然后就开始可以看到 `[Full GC ... ]` 的日志了 +这样就带来一个问题,如果CMS并发GC发生了,此时是无法利用 `-XX:+HeapDumpBeforeFullGC` 参数生成dump文件,因为不是发生 FullGC + +## G1 +> Garbage First 面向服务端应用的垃圾收集器, JDK7发布, JDK9作为默认GC [Oracle Doc](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html#garbage_first_garbage_collection) + +`-XX:+UseG1GC` + +- 分代收集 + - 虽然G1可以独立管理整个堆, 但同样具有分代的概念 +- 空间整合 + - 从整体上看是基于标记整理算法, 局部(两个Region之间)上基于标记复制算法, 相比于CMS不容易产生内存碎片 +- 可预测的停顿 + - G1除了追求低停顿, 还能建立可预测的停顿时间模型, 能让使用者明确指定在一个长度为M毫秒的时间片段内, 消耗在垃圾收集上的时间不得超过N毫秒 + - 几乎是RTSJ的特征 +- [JEP: 内存返还](https://openjdk.org/jeps/346)`JDK12发布` + +> [参考: JVM系列篇:深入剖析G1收集器](https://my.oschina.net/u/3959491/blog/3029276) + +> 字符串常量池去重 特性(8u20引入) [UseStringDeduplication - 优缺点](https://gceasy.ycrash.cn/gc-recommendations/stringdeduplication-solution.jsp) +- `-XX:+UseStringDeduplication` 适用于大量相似字符串的场景降低内存占用,但会增加GC负担,默认不开启 + - 查看字符串去重统计信息(调试用) `-XX:+PrintStringDeduplicationStatistics` `-XX:+PrintStringTableStatistics` + - 达到该年龄(经过GC次数)的String对象被认为是去重的候选对象 `-XX:StringDeDuplicationAgeThreshold` +- 该策略不会清除重复字符串对象本身。其只会替换底层 char[ ] 达到复用内存的目的 [Gitee 测试代码](https://gitee.com/gin9/JavaBase/blob/master/class/src/main/java/jvm/gc/g1/StringDeduplication.java) + +> JDK1.8时FullGC是单线程的, JDK10开始支持并行 + +> [参考: Java Hotspot G1 GC的一些关键技术](https://tech.meituan.com/2016/09/23/g1.html) +> [Welcome 20% less memory usage for G1 remembered sets](https://tschatzl.github.io/2021/02/26/early-prune.html) + +G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World的 +- Young GC:选定所有年轻代里的Region。通过控制年轻代的region个数,即年轻代内存大小,来控制young GC的时间开销。 +- Mixed GC:选定所有年轻代里的Region,外加根据 global concurrent marking 统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region。 + +由上面的描述可知,Mixed GC不是full GC,它只能回收部分老年代的Region,如果mixed GC实在无法跟上程序分配内存的速度,导致老年代填满无法继续进行Mixed GC,就会使用serial old GC(full GC)来收集整个GC heap。 +所以我们可以知道,G1是不提供full GC的。 + +上文中,多次提到了global concurrent marking,它的执行过程类似CMS,但是不同的是,在G1 GC中,它主要是为Mixed GC提供标记服务的,并不是一次GC过程的一个必须环节。 +global concurrent marking 的执行过程分为四个步骤: +- **初始标记**(initial mark,`STW`): 它标记了从GC Root开始直接可达的对象。 +- **并发标记**(Concurrent Marking): 这个阶段从GC Root开始对heap中的对象标记,标记线程与应用程序线程并行执行,并且收集各个Region的存活对象信息。 +- **最终标记**(Remark,`STW`): 标记那些在并发标记阶段发生变化的对象,将被回收。 +- **清除垃圾**(Cleanup): 清除空Region(没有存活对象的),加入到free list。 + +第一阶段initial mark是共用了Young GC的暂停,这是因为他们可以复用root scan操作,所以可以说global concurrent marking是伴随Young GC而发生的。 +第四阶段Cleanup只是回收了没有存活对象的Region,所以它并不需要STW。 + +Young GC发生的时机大家都知道,那什么时候发生Mixed GC呢?其实是由一些参数控制着的,另外也控制着哪些老年代Region会被选入CSet。 +- `G1HeapWastePercent`: 在global concurrent marking结束之后,我们可以知道old gen regions中有多少空间要被回收,在每次YGC之后和再次发生Mixed GC之前,会检查垃圾占比是否达到此参数,只有达到了,下次才会发生Mixed GC。 +- `G1MixedGCLiveThresholdPercent`: old generation region中的存活对象的占比,只有在此参数之下,才会被选入CSet。 +- `G1MixedGCCountTarget`: 一次global concurrent marking之后,最多执行Mixed GC的次数。 +- `G1OldCSetRegionThresholdPercent`: 一次Mixed GC中能被选入CSet的最多old generation region数量。 + +> G1 相关的重要参数 + +| 参数 | 描述 | +|:----|:----| +| `-XX:MaxGCPauseMillis=200` | 设置最大停顿时间值。默认值为 200 毫秒。| +| `-XX:G1HeapRegionSize=n` | 设置 G1 区域大小。值必须为 2 的 N 次幂,如:256、512、1024...范围是 1MB 至 32MB。| +| `-XX:GCTimeRatio=12` | 设置应用于 GC 的总目标时间与处理客户事务的总时间。| +| `-XX:ParallelGCThreads=n` | 设置 Stop-the-world 工作线程的数量。| +| `-XX:ConcGCThreads=n` | 设置并行标记线程的数量。将 n 值设为并行垃圾回收线程(ParallelGCThreads)数的大约 1/4。| +| `-XX:InitiatingHeapOccupancyPercent=45` | 当堆内存使用率超过此百分比时会触发 GC 标记周期。默认值为 45%。| +| `-XX:G1NewSizePercent=5` | 设置用作 Young 代空间大小的最低堆内存百分比。默认值为 Java 堆内存的 5%。| +| `-XX:G1MaxNewSizePercent=60` | 设置用作 Young 代空间大小的最高堆内存百分比。默认值为 Java 堆内存的 60%。| +| `-XX:G1OldCSetRegionThresholdPercent=10` | 设置混合垃圾回收周期中要收集的 Old 区域数量上限。默认为 Java 堆内存的 10%。| +| `-XX:G1ReservePercent=10` | 设置需保留的内存百分比。默认为 10%。G1 垃圾回收器会始终尝试保留 10% 的堆内存空间空闲。| + + +GCTimeRatio: 确定目标 GC 时间的实际公式为 [1 / (1 + GCTimeRatio)]。默认值 12 表示目标 GC 时间为 [1 / (1 + 12)],即 7.69%。 +这意味着 JVM 可将 7.69% 的时间用于 GC 活动,其余 92.3% 用于处理客户活动。 + +ParallelGCThreads: 如果逻辑处理器的数量M小于或等于 8 个,则将 n 值设置为M。如果M为5,则将 n 设为 5.如果M为 8 个以上,请将该值设置为大约 5/8 * M。 +这种设置在大多数情况下都有效,除了较大规模的 SPARC 系统——其中 n 值可以大约是 `5/16 * M`。 + +MaxGCPauseMillis:G1将根据先前收集的信息以及检测到的垃圾对象量预估可以执行回收的垃圾区域,尽量保证GC时间不超过设置的值。 + +ParallelGCThreads: 使用默认值就可以了。但是在JRE版本1.8.0_131之前,JVM无法感知Docker的CPU限制,会使用宿主机的逻辑核数计算默认值。 +这通常会远超过容器的核数, 过多的GC线程数抢占了业务线程的CPU时间,加上线程切换的开销,较大的降低了吞吐量。因此JRE 1.8.0_131之前的版本,未明确指定ParallelGCThreads会有较大的风险。 + +ConcGCThreads 一般称为并发标记线程数,为了减少GC的STW的时间,CMS和G1都有并发标记的过程,此时业务线程仍在工作,只是并发标记是CPU密集型任务,业务的吞吐量会下降,RT会变长。 +ConcGCThreads的默认值不同GC策略略有不同,CMS下是(ParallelGCThreads + 3) / 4 向下取整,G1下是ParallelGCThreads / 4 四舍五入。一般来说采用默认值就可以了,但是还是由于在JRE版本1.8.0_131之前,JVM无法感知Docker的资源限制的问题,ConcGCThreads的默认值会比较大(20左右),对业务会有影响。 + +************************ + +## ZGC +> [wiki: ZGC](https://wiki.openjdk.java.net/display/zgc/Main) | [JEP 377 ZGC](https://openjdk.org/jeps/377) | [ZGC Release note](https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html#JDK-8197831) + +- `-XX:+UseZGC` JDK11引入,JDK15正式使用,因此JDK11-14需要追加参数`-XX:+UnlockExperimentalVMOptions` + +> [参考: Oracle 即将发布的全新 Java 垃圾收集器 ZGC](https://www.infoq.cn/article/oracle-release-java-gc-zgc) +> [参考: 美团:新一代垃圾回收器ZGC的探索与实践](https://tech.meituan.com/2020/08/06/new-zgc-practice-in-meituan.html) + +- [JDK21 ZGC 支持内存分代](https://openjdk.org/jeps/439) +- [JDK13 ZGC 支持内存返还](https://openjdk.org/jeps/351) + +************************ + +## ShenandoahGC +> JDK12 [wiki: ShenandoahGC](https://wiki.openjdk.java.net/display/shenandoah/Main) + +> [参考: JDK12 ShenandoahGC小试牛刀](https://juejin.im/post/5c934a5d5188252dad05d82a) + +-XX:+UnlockExperimentalVMOptions -XX:+UseShenandoahGC + +## Epsilon +> JDK11 [A No-Op Garbage Collector](https://openjdk.org/jeps/318) + +- `-XX:+UseEpsilonGC -XX:+UnlockExperimentalVMOptions` 直至22尚未GA + +不做GC的GC,即不做垃圾回收,通常用于目标测试GC的对照组 + +************************ + +# 最佳实践 +[Choosing a GC Algorithm in Java](https://www.baeldung.com/java-choosing-gc-algorithm) +[Tuning Garbage Collection with Oracle JDK](https://docs.oracle.com/cd/E55119_01/doc.71/e55122/cnf_jvmgc.htm#WSEAD414) + diff --git a/Java/AdvancedLearning/JvmTool.md b/Java/AdvancedLearning/JvmTool.md new file mode 100644 index 0000000..cb533e8 --- /dev/null +++ b/Java/AdvancedLearning/JvmTool.md @@ -0,0 +1,369 @@ +--- +title: JVM 监控&诊断 +date: 2018-11-21 10:56:52 +tags: + - JVM +categories: + - Java +--- + +💠 + +- 1. [JVM 监控&诊断](#jvm-监控&诊断) +- 2. [JVMTI](#jvmti) +- 3. [JDK自带工具](#jdk自带工具) + - 3.1. [java](#java) + - 3.1.1. [环境变量的使用](#环境变量的使用) + - 3.2. [jps](#jps) + - 3.3. [jstat](#jstat) + - 3.4. [jinfo](#jinfo) + - 3.5. [jmap](#jmap) + - 3.6. [jhat](#jhat) + - 3.6.1. [OQL](#oql) + - 3.6.2. [HPROF](#hprof) + - 3.7. [jstack](#jstack) + - 3.7.1. [实现原理](#实现原理) + - 3.8. [jcmd](#jcmd) + - 3.9. [jhsdb](#jhsdb) +- 4. [终端类工具](#终端类工具) + - 4.1. [Arthas](#arthas) + - 4.2. [async-profiler](#async-profiler) +- 5. [jvm-sandbox](#jvm-sandbox) +- 6. [图形化工具](#图形化工具) + - 6.1. [JProfiler](#jprofiler) + - 6.2. [YourKit](#yourkit) + - 6.3. [Visualvm](#visualvm) + - 6.4. [MAT](#mat) + - 6.5. [JMC](#jmc) + - 6.6. [IBM Heap Analyzer](#ibm-heap-analyzer) + - 6.7. [IntelliJ IDEA](#intellij-idea) + +💠 2024-09-20 11:52:03 +**************************************** + +# JVM 监控&诊断 +命令行终端 +- 标准终端类:jps、jinfo、jstat、jstack、jmap +- 功能整合类:jcmd、vjtools、arthas、greys + +可视化界面 +- 简易:JConsole、JVisualvm、HA、GCHisto、GCViewer +- 进阶:MAT、JProfiler + +命令行推荐 arthas ,可视化界面推荐 JProfiler +此外还有一些在线的平台 [gceasy](https://gceasy.io/)、heaphero、fastthread 。 + +> [jvm-tools](https://github.com/aragozin/jvm-tools) + +# JVMTI +`JVM Tool Interface` + + +# JDK自带工具 +> 都是jdk的bin目录下的工具,注意使用时要和目标JVM同一个JDK版本,以及同一个用户。 + +## java +> 使用方式: +- 执行类: `java [-options] class [args...]` +- 执行包: `java [-options] -jar jarfile [args...]` 或 `java -jar [-options] jarfile [args...]` + +> **注意** 这些Java options都*不会*生效。 +`java -jar jarfile [-options] [args...]` +`java -jar jarfile [args...] [-options]` + +### 环境变量的使用 +> [What is the java -D command-line option good for? ](https://coderanch.com/t/178539/certification/java-command-line-option-good) +- 传入 `java -Dkey=true -jar xxx.jar` + - -D 参数 要在 -jar **之前** +- 获取 `System.getProperty("key", "defaultvalue");` + +## jps +> 主要用来输出JVM中运行的进程状态信息 +- option: + - -q 忽略输出的类名、Jar名以及传递给main方法的参数,只输出pid。 + - -m 输出传递给main方法的参数,如果是内嵌的JVM则输出为null。 + - -l 输出应用程序主类的完整包名,或者是应用程序JAR文件的完整路径。 + - -v 输出传给JVM的参数。 + - -V 输出通过标记的文件传递给JVM的参数(.hotspotrc文件,或者是通过参数-XX:Flags=指定的文件) + +## jstat +> [Oracle Doc: jstat](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.html) + +- option: + - -class 类加载情况 + - -compiler 编译统计 + - -printcompilation JVM编译方法统计 + - 查看内存相关指标 + - `-gcutil` 总gc统计情况 + - `-gc` gc统计情况 + - `-gccapacity` 堆内存空间 + - `-gcnew` 和 `-gcnewcapacity` 新生代gc和内存统计 + - `-gcold` 和 `-gcoldcapacity` 老年代gc和内存统计 + - `-gcpermcapacity` JDK7 永久代 + - `-gcmetacapacity` JDK8 元空间 + - -t 在第一列输出时间戳`(s)`。该时间戳从jvm启动后开始计时 + - -h3 每隔N行输出一次列表头 + - $PID 进程号 + - interval 输出间隔时间,单位毫秒 + - count 输出次数 + +> [CSDN: jstat](https://blog.csdn.net/achuo/article/details/107793361) + +> Demo: +- `jstat -gcutil -t -h5 7919 1000 50` + +## jinfo +> 观察运行中的 java 进程的运行环境参数:参数包括 Java System 属性和 JVM 命令行参数 + +> Demo: +- jinfo 14352 +- jinfo -sysprops 14352 +- 查看JVM参数 `jinfo -flags 14352` + - jinfo -flag MaxPermSize 14352 + +## jmap +> 用来查看堆内存使用状况 + +> Demo: +- `jmap -histo $PID` 展示实例和占用内存情况 + - `jmap -histo:live $PID` 展示存活实例情况 **注意会触发FullGC** +- `jmap -heap $PID` 展示Java堆的各内存区域大小及占用情况 +- `jmap -dump:live,format=b,file=heapLive.hprof $PID` dump下存活对象 **注意会触发FullGC** + - `jmap -dump:format=b,file=heapLive.hprof $PID` dump所有对象 + - 失败时 可以尝试 -F 参数,强制dump,但此时的dump文件不一定是完整可打开的。 + - 提示attach失败时 可修改 `echo 0 > /proc/sys/kernel/yama/ptrace_scope` jmap依赖ptrace实现,此选项放开ptrace仅支持父进程执行的限制 + +************************ + +## jhat +> Java Head Analyse Tool [Oracle: jhat](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr012.html) + +用于分析 jmap 转储出来的堆文件, 分析完后启动一个WebServer, 浏览器打开 127.0.0.1:7000 查看 + +> 参数 +- -J-mx2g 设置最大内存2g +- -J-d64 64位模式 +- -port 端口 + +> 使用 +- 网页 + - 首页 所有类,点击可查看类的实例列表 + - 底部 Other Queries 包含: histo,OQL查询,类实例 查看功能 +- 比较多个dump `jhat -baseline snapshot_1.hprof snapshot_2.hprof` 1,2文件是先后dump产生的 + - 在底部的类实例`Show instance counts` 中能看到多了一列 例如 `instances (111060 new) of class` +- [OQL查询](http://localhost:7000/oql/) + - [OQL使用手册](http://localhost:7000/oqlhelp/) + +### OQL +```sql + select + [ from [instanceof] + [ where ] ] +``` + +### HPROF +> [HPROF: A Heap/CPU Profiling Tool](https://docs.oracle.com/javase/8/docs/technotes/samples/hprof.html) +> [heapdumpstamp](https://github.com/bostrt/heapdumpstamp)`获取hprof创建时间戳` + +************************ + +## jstack +> jstack [option] pid 主要用来查看某个Java进程内的线程堆栈信息 +- Option: + - -F: 强制产生一个线程dump + - `注意`此方式得到的dump**缺失很多信息**, 只有线程栈和操作系统线程id,没有线程名,线程cid,锁等信息 + - 而且相对于没有-F的方式,实现原理完全不一样,见下文链接 + - -m: 打印java和native frames + - -l: 打印关于锁的附加信息 + - -J-d64: 64位模式 + +> 找出占用CPU最高的线程: [封装的Shell active_cpu_thread](https://github.com/Kuangcp/Script/blob/master/shell/assistant/java-tool.sh) +1. `jps 或者 ps aux | grep xxx` 得到对应Java进程id +1. `top -Hp 进程id` 查看 time 占用最长 或者 CPU占用最高 的线程id +1. `printf %x 线程id` 得到 16进制线程id +1. `jstack 进程id | grep -A 20 16进制线程id` 查看该线程的栈,进而分析到代码 + +> [How to Analyze Java Thread Dumps](https://www.baeldung.com/java-analyze-thread-dumps)`分析工具和思路` +> [OpenJDK11 jstack output explanation](https://stackoverflow.com/questions/76476637/openjdk11-jstack-output-explanation) +> [How to Analyze Java Thread Dumps](https://dzone.com/articles/how-analyze-java-thread-dumps) + +扩展:通过短时间内多次获取stack分析出 活锁,死循环,死锁等问题点,但是通常这类问题只能通过修复并重启解决 + +### 实现原理 +- [Jstack 源码分析](https://zhuanlan.zhihu.com/p/36224094) + +[jmap -F and jstack -F](https://stackoverflow.com/questions/26140182/running-jmap-getting-unable-to-open-socket-file)`jmap和jstack 默认及加-F选项背后实现机制及优缺点` +- [Dynamic Attach Mechanism](http://openjdk.java.net/groups/hotspot/docs/Serviceability.html#battach) +- [HotSpot Serviceability Agent](http://openjdk.java.net/groups/hotspot/docs/Serviceability.html#bsa) + +************************ + +## jcmd +> jcmd $pid command [Oracle jcmd doc](https://docs.oracle.com/en/java/javase/17/docs/specs/man/jcmd.html) + +- Compiler +- GC GC信息,触发GC,堆信息 + | 命令 | 说明 | + |:----|:----| + | GC.run | 触发一次Full GC + | GC.heap_info | 查看堆使用统计 + | GC.class_histogram -all | 类实例统计 + | GC.heap_dump -all filename | 创建所有对象的dump + - 参数 `-all` 指全部对象,如果去除,将**触发Full GC**来找到所有存活对象 + +- JFR + - JFR.start 会输出提示信息 + - JFR.stop name=1 filename=now.jfr `name`从start提示信息中获取 +- JVMTI +- ManagementAgent +- System +- Thread +- VM + - VM.command_line + +## jhsdb +> [jdk9 jhsdb](https://dzone.com/articles/jhsdb-a-new-tool-for-jdk-9) | [Oracle jhsdb](https://docs.oracle.com/javase/9/tools/jhsdb.htm) + +JDK9之前通过是Jar方式启动 HSDB CLHSDB。部分功能有被jmap等命令封装 例如 `jmap -heap` +- `java -cp .:sa-jdi.jar sun.jvm.hotspot.CLHSDB` HSDB需要和目标JVM同一个版本 + - help 查看帮助 + - jseval 执行javascript + - attach:连接到目标进程戒core + - universe:查看Java heap的情况 + - inspect:查看某个地址对应的数据结构的内容 + - scanoops:扫描某个地址段的Java对象 + +jstack jmap jinfo jsnap 等命令功能的迁移和加强 + +> 例如 +- `jmap -heap pid` => `jhsdb jmap --heap --pid pid` + +******************** + +# 终端类工具 + +## Arthas +> [Github: Arthas](https://github.com/alibaba/arthas)`阿里巴巴` + +## async-profiler +> [async-profiler](https://github.com/jvm-profiling-tools/async-profiler)`CPU和内存采样 渲染火焰图` + +# jvm-sandbox +> [jvm-sandbox](https://github.com/alibaba/jvm-sandbox) + +> [JVM SandBox 的技术原理与应用分析](https://www.infoq.cn/article/TSY4lGjvSfwEuXEBW*Gp) + +********************** + +> [vjtools](https://github.com/vipshop/vjtools)`唯品会` +> [github.com/dingjs/javaagent](https://github.com/dingjs/javaagent) + +************************ + +# 图形化工具 + +> [Heap Dump Analysers](http://www.fasterj.com/tools/heapdumpanalysers.shtml) +> [Java Monitoring Tools](https://sematext.com/guides/java-monitoring/)`Profile APM log 等多个解决思路` + +## JProfiler +> [Official Site](https://www.ej-technologies.com/products/jprofiler/overview.html)`收费` + +[OOM 踩坑日记](https://huminxi.netlify.app/2022/06/24/oom%20%E8%B8%A9%E5%9D%91%E6%97%A5%E8%AE%B0/#more) + +## YourKit +[YourKit Java Profiler](https://www.yourkit.com/java/profiler)`收费` + +## Visualvm +> [Github:visualvm](https://github.com/oracle/visualvm) +> [visualgc plugin](https://www.oracle.com/technetwork/java/visualgc-136680.html) + +> [参考: java内存泄漏的定位与分析](https://blog.csdn.net/lc0817/article/details/67014499) +> [使用 VisualVM 进行性能分析及调优](https://www.ibm.com/developerworks/cn/java/j-lo-visualvm/index.html) +> [参考: JVisualVM简介与内存泄漏实战分析](http://www.cnblogs.com/belen/p/5573501.html) + +- `Local` +- `Remote` + - 通常使用两种方式连接远程JVM: JMX jstatd + - **`jmx`** + - [JMX](/Java/AdvancedLearning/JMX.md) + - **`jstatd`** + 1. vim jstatd.all.policy + ``` + grant codebase "file:${java.home}/../lib/tools.jar" { + permission java.security.AllPermission; + + }; + ``` + 1. jstatd -J-Djava.security.policy=jstatd.all.policy -p 12028 -J-Djava.rmi.server.logCalls=true + 1. open jvisualvm create a remote with jstatd by above port 12028 + +> 提高效率的使用场景 +1. 可以使用 Profiler 下的JDBC,操作业务流程,获取所有执行的SQL,用来做索引优化,或排查问题 + - **注意可能不准确**,需要对监控到的SQL有质疑的想法 + - 真实案例: 监控到对MySQL执行的某条SQL为 `xxx in ('NULL', 2, 4)`. 应用写法不规范未过滤集合中的null值就拼接进了条件 + - 实际上MySQL驱动执行的SQL是 `xxx in (NULL, 2, 4)` 这会导致此子句永远是false,详见 [MySQL 条件操作符](/Database/MySQLAdvance.md#条件操作符) + - Clone Visualvm的代码后 通过GUI找功能实现,发现可疑方法 org.graalvm.visualvm.lib.jfluid.results.jdbc.SQLStatement#getFullSql + - 通过arthas watch该方法的返回后,确认是这个方法的问题, + - 结论为:基于 PreparedStatement 得到执行SQL的实现方式和MySQL驱动的不一样。 + +************************ + +## MAT +> Memory Analyzer tool(MAT) | [Official Site](http://www.eclipse.org/mat/) | [download](https://eclipse.dev/mat/downloads.php) + +> [参考: JAVA Shallow heap & Retained heap](http://www.cnblogs.com/lipeineng/p/5824799.html) +> [参考: 利用MAT分析JVM内存问题,从入门到精通](https://www.cnblogs.com/javaadu/p/11161380.html) +> [ Official Doc: OQL Syntax](https://help.eclipse.org/neon/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Freference%2Foqlsyntax.html) + +注意: 有这样的一种场景, 从数据库获取大量的数据创建为对象, 导致瞬间的OOM 这时候即使使用 jmap 去 dump 了快照, 也看不到占用大量内存的对象, 因为MAT默认展示的是GC可达对象,需要在菜单选择看不可达对象 + +分析思路: +- 对象: histogram, Top , +- 线程: +- 类加载器: histogram -> basic -> merge classloader +- 不可达对象: + +************************ + +> [mat用小内存解析超大堆快照的可行方法](https://baofeidyz.com/feasible-method-for-mat-to-analyze-super-large-heap-snapshots-with-small-memory) + +利用安装目录下的 ParseHeapDump.sh 命令行解析 dump的 hprof文件 +全部解析: ParseHeapDump.sh ~/Downloads/java_pidxxx.hprof org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components + +- ParseHeapDump.sh ~/Downloads/java_pidxxx.hprof org.eclipse.mat.api:suspects +- ParseHeapDump.sh ~/Downloads/java_pidxxx.hprof org.eclipse.mat.api:overview +- ParseHeapDump.sh ~/Downloads/java_pidxxx.hprof org.eclipse.mat.api:top_components + +并且可以发现结果文件为html,可以挂载到nginx等web服务器共享结果 + +************************ +> 比较多个dump文件 + +[MAT比较多个heap dump文件](https://blog.csdn.net/zhuxingchong/article/details/110449138) + +************************ + +## JMC +> [Java Mission Control](https://docs.oracle.com/en/java/java-components/jdk-mission-control/) + +1. 通过JMX连接目标JVM 实时监控应用指标 +1. 通过对运行中的JVM进行飞行记录`Flight Recorder`, 分析指定时间内代码的可优化点,指标值变化情况 + +指标值包括:JVM的 内存,CPU,GC,线程,类加载,网络和文件IO; 宿主机的CPU、内存等指标,联合做参考 + +> [目标JVM开启远程访问JMX](/Java/AdvancedLearning/JMX.md#JVM参数配置) `注意JDK6后就默认开启了进程访问JMX` +> [JMC 9](https://www.oracle.com/java/technologies/javase/jmc9-release-notes.html)`自身需要JDK17以上运行,可以监控JDK 7u40及往后的版本` + +> 实践场景 +- JFR分析某个业务场景的性能问题 + - 启动应用,启动JMC,JMC连接到业务JVM后,开启一段时间的JFR,然后直接操作业务逻辑,JFR结束后可以看到 + +************************ + +## IBM Heap Analyzer +> [Official Site](https://www.ibm.com/developerworks/community/alphaworks/tech/heapanalyzer) + +************************ + +## IntelliJ IDEA +[Analyze the memory snapshot](https://www.jetbrains.com/help/idea/read-the-memory-snapshot.html) diff --git a/Java/AdvancedLearning/Map/ConcurrentHashMap.md b/Java/AdvancedLearning/Map/ConcurrentHashMap.md new file mode 100644 index 0000000..50abd6b --- /dev/null +++ b/Java/AdvancedLearning/Map/ConcurrentHashMap.md @@ -0,0 +1 @@ +# ConcurrentHashMap diff --git a/Java/AdvancedLearning/Map/HashMap.md b/Java/AdvancedLearning/Map/HashMap.md new file mode 100644 index 0000000..c434f5a --- /dev/null +++ b/Java/AdvancedLearning/Map/HashMap.md @@ -0,0 +1,188 @@ +--- +title: Java中的HashMap +date: 2019-04-16 15:35:58 +tags: +categories: + - Java +--- + +💠 + +- 1. [HashMap](#hashmap) + - 1.1. [结构](#结构) + - 1.2. [构造函数](#构造函数) + - 1.3. [put](#put) + - 1.4. [putAll](#putall) + - 1.5. [resize](#resize) + - 1.6. [get](#get) + - 1.7. [remove](#remove) + - 1.8. [HashMap 与 HashTable](#hashmap-与-hashtable) + - 1.9. [总结](#总结) +- 2. [Tips](#tips) + - 2.1. [扩容死循环问题](#扩容死循环问题) + - 2.2. [栈溢出问题](#栈溢出问题) + +💠 2024-04-07 15:54:52 +**************************************** +# HashMap +> [API: HashMap](https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html) + +> [参考: Java 8系列之重新认识HashMap](https://tech.meituan.com/2016/06/24/java-hashmap.html) + +> [参考: 死磕 java集合之HashMap源码分析](https://juejin.im/post/5cb163bee51d456e46603dfe#heading-17) +> [Java HashMap工作原理及实现 ](http://yikun.github.io/2015/04/01/Java-HashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/) +> [HashMap 怎么 hash?又如何 map?](https://my.oschina.net/editorial-story/blog/2396106) +> [参考: Java 8系列之重新认识HashMap](https://zhuanlan.zhihu.com/p/21673805) + +> 注意: JDK1.8 才引入了红黑树的实现 +>> 最坏时间复杂度从O(n)到O(logn), 一定程度上避免了哈希碰撞导致的DOS攻击 + +## 结构 +HashMap的数据结构是 数组(称为bucket)加单链表 (数组是只放一个Node对象, 单链表是为了放通过hash计算得到的index一致的元素包装成的Node对象) + +![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Java/Collection/Map/HashMap.png) +![HashMap主要数据结构](https://github.com/dragonhht/GitImgs/blob/master/Notes/HashMap_1.png?raw=true) + +这种设计的好处是, 如果 hash 足够分散, get 时的时间复杂度为 O(1), 反之则是 链表 O(n) 红黑树 O(log n) + +## 构造函数 +- 空构造函数: 采用默认初始容量 16 和 默认负载因子 0.75 +- 初始容量参数: 采用传入的初始容量, 默认负载因子 +- 初始容量, 负载因子参数: 采用传入的两个参数 + +其中初始容量会根据 tableSizeFor 方法计算得到 大于初始容量的最小的2的指数值 3->4 4->8 ... + +## put +> 得到 key 的数组下标: `(n - 1) & key 的 hash 值`, 其中 n 为数组大小 + +学习队列的时候有种简单的思路就是直接将 key 的值 对数组大小取余, 就得到了key的正确下标 +但是HashMap采用这种设计的优点是性能更好,因为Java中的取余实际上是 `a - (a / b) * b` +使用与运算会更好, 并且为了数据足够分散, HashMap的数组大小都是2的指数, 是为了数据足够分散 + +1. 计算key的hash值(先计算hashCode 然后进行 高低位做异或运算 (高16和低16做异或) `(h = key.hashCode()) ^ (h >>> 16)`); +1. `if` 桶(数组)大小为0,则初始化桶; +1. `if` 桶中key的下标上没有节点对象,则直接插入; +1. `else` 如果有节点对象 + 1. `if` key的下标上有节点对象 转 `#` 流程; + 1. `else if` 有节点对象且为树节点,则调用树节点的putTreeVal()插入key对应的节点对象; + 1. `else` 则遍历桶对应的链表查找key是否存在于链表中; + 1. `if` 找到了对应key的节点,则转 `#` 流程; + 1. `if` 没找到对应key的节点,则尾插法插入节点 并判断是否需要树化; + +1. 每当插入新节点,则 ++size 并判断是否需要扩容, 扩容扩的是数组; + +- `#` 如果找到了对应key的元素,则判断是否需要替换旧值,并直接返回旧值; + +## putAll +1. 可用于复制map内容 + +## resize +1. 如果旧容量大于0,则新容量等于旧容量的2倍,但不超过最大容量2的30次方,新扩容阈值为旧扩容阈值的2倍; +1. 创建一个新容量的桶; +1. 移动元素 + 1. `if` 该下标只有一个节点, 就rehash下直接放过去 `newTab[e.hash & (newCap - 1)] = e;` + 1. `if` 该下标上是一个树节点 则打散成两棵树 `((TreeNode) e).split(this, newTab, j, oldCap);` + 1. `else` 也就是说将原链表分化成两个链表,低位链表存储在原来桶的位置,高位链表搬移到原来位置加旧容量偏移的位置上去; + - 例如桶的原大小4 , 节点的hash 3、7、11、15 `index = ((4-1) & hash)` + - 扩容一次 3和11保持不变(因为`hash&oldCap == 0`), 而 7和15要搬移到`(4-1) & hash + oldCap`中去 + +## get + +## remove + +- [ ] 树相关的方法 + +## HashMap 与 HashTable + +- HashMap 是线程不安全的,HashTable 线程安全,因为它在 get、put 方法上加了 synchronized 关键字。 +- HashMap 和 HashTable 的 hash 值是不一样的,所在的桶的计算方式也不一样。HashMap 的桶是通过 & 运算符来实现 (tab.length - 1) & hash, + - 而 HashTable 是通过取余计算,速度更慢(hash & 0x7FFFFFFF) % tab.length (当 tab.length = 2^n 时,因为 HashMap 的数组长度正好都是 2^n,所以两者是等价的) +- HashTable 的 synchronized 是方法级别的,也就是它是在 put() 方法上加的,这也就是说任何一个 put 操作都会使用同一个锁,而实际上不同索引上的元素之间彼此操作不会受到影响; + - ConcurrentHashMap 相当于是 HashTable 的升级,它也是线程安全的,只有在多个线程操作同一个数组索引的时候才出现锁等待,降低了锁竞争情况 + +## 总结 +1. HashMap是一种散列表,采用(数组 + 链表 + 红黑树)的存储结构; +1. HashMap的默认初始容量为16(1<<4),默认装载因子为0.75f,容量总是2的n次方; +1. HashMap扩容时每次容量变为原来的两倍; +1. 当桶的数量小于64时不会进行树化,只会扩容; +1. 当桶的数量大于64且单个桶中元素的数量大于8时,对应的桶进行树化; +1. 当单个桶中元素数量小于6时,对应的桶进行反树化; +1. HashMap是非线程安全的容器; +1. HashMap查找添加元素的时间复杂度都为O(1); + +# Tips +> 发生 ConcurrentModificationException 时: +1. 使用 ConcurrentHashMap 替换掉 HashMap (推荐) +1. 使用 synchronized 限制迭代或修改方法 + +hashmap的实现原理,从负载因子,冲突处理,equals,hashcode一口气讲下来,中间没卡壳儿的。虽然这不是什么太高深的东西,但还是可以感觉得到理论基础特别扎实。 +好多工作五六年的人解释不清楚equals和hashcode这两个函数在hashmap中干嘛用的。回答模式一般都是先从理论,原理层面把这个问题讲清楚,然后再从最佳实践方面讲一下不同的应用场景会涉及哪些问题,最后是他做过的项目怎么用的。 + +> [参考: HashMap为什么是线程不安全的?](https://blog.csdn.net/mydreamongo/article/details/8960667) + +数据竞争: put, resize, rehash, fail fast + +## 扩容死循环问题 +> [参考: HashMap的死循环](https://www.jianshu.com/p/1e9cf0ac07f4) + +```java + // JDK7 + void transfer(Entry[] newTable, boolean rehash) { + int newCapacity = newTable.length; + for (Entry e : table) { + while(null != e) { + Entry next = e.next; // 1 + if (rehash) { + e.hash = null == e.key ? 0 : hash(e.key); + } + int i = indexFor(e.hash, newCapacity); + + e.next = newTable[i]; + newTable[i] = e; + e = next; + } + } + } +``` +- 出现的场景 + - 假设容量为4 负载因子0.5, 插入三个节点, 且三个节点的hash值一致, 那么在插入第三个节点时就需要扩容 + - 如果有两个线程在执行这个插入操作, 也就是会同时进行扩容, 且线程1执行完 `1` 后被挂起了 + - 线程2执行完了这个while循环, 完成了扩容, 但是对于线程1来说还需要继续执行 + - 且转移节点时采用的是头插法, 于是就容易导致链表出现了环, 那么之后对这个下标的链表进行 get 时, CPU 就满载死循环了 + - 并且如果线程2执行完了该方法, 且将自己new的桶覆盖了原有的桶, 线程1才继续执行 还会导致数据丢失 + +```java + // JDK8 + Node loHead = null, loTail = null; + Node hiHead = null, hiTail = null; + Node next; + do { + next = e.next; + if ((e.hash & oldCap) == 0) { + if (loTail == null) + loHead = e; + else + loTail.next = e; + loTail = e; + } + else { + if (hiTail == null) + hiHead = e; + else + hiTail.next = e; + hiTail = e; + } + } while ((e = next) != null); + if (loTail != null) { + loTail.next = null; + newTab[j] = loHead; + } + if (hiTail != null) { + hiTail.next = null; + newTab[j + oldCap] = hiHead; + } +``` +JDK8 扩容时采用的方式是将一个链表按原有顺序拆分成两个链表 而且采用的是尾插法, 即使是出现了并发问题, 只是重复执行了操作, 不会出现环 + +## 栈溢出问题 +Flink 中 大量集合进行 union 操作时, 由于 HashMap空间不够而导致的 hashCode hash put 函数递归调用 diff --git a/Java/AdvancedLearning/Map/LinkedHashMap.md b/Java/AdvancedLearning/Map/LinkedHashMap.md new file mode 100644 index 0000000..9ee94c5 --- /dev/null +++ b/Java/AdvancedLearning/Map/LinkedHashMap.md @@ -0,0 +1,23 @@ +--- +title: LinkedHashMap +date: 2022-08-09 15:30:22 +tags: +categories: + - Java +--- + +**目录 start** + +1. [LinkedHashMap](#linkedhashmap) + +**目录 end**|_2022-08-09 15:46_| +**************************************** +# LinkedHashMap +> 关键点在于 java.util.LinkedHashMap.Entry + +大部分逻辑完全 继承于HashMap, 在Node上引入了两个引用,将key维护为一个双向链表,保证了遍历时key的有序性 + +> [参考: Java集合之LinkedHashMap](https://www.cnblogs.com/xiaoxi/p/6170590.html) + +利用 LinkedHashMap 实现LRU时可以让自己代码更简化,在访问key时只需要将key remove和put一次就好了,这样迭代map时靠前的元素都是可以被淘汰的。 + diff --git a/Java/AdvancedLearning/Map/Readme.md b/Java/AdvancedLearning/Map/Readme.md new file mode 100644 index 0000000..de4a6e3 --- /dev/null +++ b/Java/AdvancedLearning/Map/Readme.md @@ -0,0 +1,32 @@ +# Map + +AbstractMap 抽象基类,可以基于该类实现自己的Map + +## 通用Map + +| 类型 | 特征 | key | 并发安全 | +|:---|:---|:---|:---| +|HashMap | 链表和红黑树 | key和value均允许为空 | 否 | +|Hashtable | 单线程写入 | key和value的值均不允许为null | 是 | +|Properties | Hashtable 子类 | | 是 | +|LinkedHashMap | 保持了数据插入顺序 | key和value均允许为空 | 否 | +|IdentityHashMap | key非equals比较而是 == 比较 | key和value均允许为空 | 否 | +|TreeMap | 红黑树实现 | key不允许null | 否| +|WeakHashMap | key 弱引用 | | 否 | +|ThreadLocalMap | ThreadLocal, key 弱引用 | | 否 | +|ConcurrentHashMap | 分段锁 | | 是 | + +> IdentityHashMap +- java.util.HashMap#getNode `equals` +- java.util.IdentityHashMap#get `==` + +## 专用Map + +- java.util.jar.Attributes +- javax.print.attribute.standard.PrinterStateReasons +- java.security.Provider +- java.awt.RenderingHints +- javax.swing.UIDefaults + +## 第三方 +> [NonBlockingHashMap](https://github.com/h2oai/h2o-3/blob/master/h2o-core/src/main/java/water/nbhm/NonBlockingHashMap.java) diff --git a/Java/AdvancedLearning/Map/TreeMap.md b/Java/AdvancedLearning/Map/TreeMap.md new file mode 100644 index 0000000..c1d7943 --- /dev/null +++ b/Java/AdvancedLearning/Map/TreeMap.md @@ -0,0 +1,20 @@ +--- +title: TreeMap +date: 2019-04-16 15:53:31 +tags: +categories: +--- + +💠 + +- 1. [TreeMap](#treemap) + +💠 2024-08-28 10:59:31 +**************************************** +# TreeMap +> [参考: 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现](https://www.ibm.com/developerworks/cn/java/j-lo-tree/index.html) +> [参考: TreeMap详细介绍(源码解析)和使用示例](http://www.cnblogs.com/skywang12345/p/3310928.html) + +> [聊聊 HashMap 和 TreeMap 的内部结构](http://mp.weixin.qq.com/s?__biz=MzAxNjk4ODE4OQ==&mid=2247488637&idx=3&sn=ee2d6e2101368a424b6a485f0d644e1a&chksm=9bed350fac9abc1954d704d9b94edf805ef56ce82e3a99f6f316c899de1dc6f06722d7927eb5&mpshare=1&scene=1&srcid=&sharer_sharetime=1586600321631&sharer_shareid=246c4b52c1cb45eaa580c985c95107f3#rd) + +> [TreeMap in Java: A Must-Know Data Structure ](https://igorstechnoclub.com/java-treemap/) diff --git a/Java/AdvancedLearning/Reflect.md b/Java/AdvancedLearning/Reflect.md deleted file mode 100644 index b9beb14..0000000 --- a/Java/AdvancedLearning/Reflect.md +++ /dev/null @@ -1,41 +0,0 @@ -`目录 start` - -- [反射](#反射) - - [获取属性](#获取属性) - - [获得方法](#获得方法) - - [性能问题](#性能问题) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 反射 -> [ Java反射异常处理之InvocationTargetException ](https://blog.csdn.net/zhangzeyuaaa/article/details/39611467) - -> [参考博客: java8--类加载机制与反射(java疯狂讲义3复习笔记)](https://www.cnblogs.com/lakeslove/p/5978382.html) -> [参考博客: Java8替代传统反射动态获取成员变量值的一个示例](https://segmentfault.com/a/1190000007492958) -> [参考博客: java反射的性能问题](http://www.cnblogs.com/zhishan/p/3195771.html) - -## 获取属性 -_通过属性名得到对象属性的值_ -```java - PropertyDescriptor propertyDescriptor = new PropertyDescriptor(meta.getField().getName(), target); - Method method = propertyDescriptor.getReadMethod(); - Object result = method.invoke(model); -``` -或者如下方式更为简洁 -```java - // set - A a = new A(); - Field field = a.getClass().getDeclaredField("x"); - field.setAccessible(true); - field.set(a, 1); - // get - Field f = a.getClass().getDeclaredField("x"); - f.setAccessible(true); - System.out.println(f.get(a)); -``` - -## 获得方法 - -### 性能问题 -> [参考博客: java反射的性能问题 ](http://www.cnblogs.com/zhishan/p/3195771.html) - diff --git a/Java/AdvancedLearning/Release/JDKAndJRE.md b/Java/AdvancedLearning/Release/JDKAndJRE.md new file mode 100644 index 0000000..8f8e888 --- /dev/null +++ b/Java/AdvancedLearning/Release/JDKAndJRE.md @@ -0,0 +1,99 @@ +--- +title: Java的JDK以及JRE +date: 2018-11-21 10:56:52 +tags: + - JVM +categories: + - Java +--- + +💠 + +- 1. [JDK And JRE](#jdk-and-jre) +- 2. [Oracle](#oracle) + - 2.1. [Oracle JDK](#oracle-jdk) + - 2.2. [Oracle JRE](#oracle-jre) +- 3. [OpenJDK](#openjdk) +- 4. [商业组织JDK](#商业组织jdk) + - 4.1. [Corretto](#corretto) +- 5. [JDK源码](#jdk源码) + +💠 2024-10-02 22:33:00 +**************************************** +# JDK And JRE + +但是11发布后, Oracle修改了使用协议, JDK商用需付费, 仅个人开发演示免费 [License](https://www.oracle.com/technetwork/java/javase/terms/license/javase-license.html) + +- [www.injdk.cn](https://www.injdk.cn/)`镜像站` + +> [Java 有哪些不好的设计?](https://www.zhihu.com/question/25372706/answer/30589125) + +# Oracle +> [roadmap](https://www.oracle.com/java/technologies/java-se-support-roadmap.html) + +## Oracle JDK + +> 以下是Java8的结构 +``` + ├── bin/ + ├── COPYRIGHT + ├── include/ + ├── javafx-src.zip + ├── jre/ + ├── lib/ + ├── LICENSE + ├── man/ + ├── README.html + ├── release + ├── src.zip + ├── THIRDPARTYLICENSEREADME-JAVAFX.txt + └── THIRDPARTYLICENSEREADME.txt +``` + +> 以下是Java11的目录结构 +``` + ├── bin + ├── conf + ├── include + ├── jmods + ├── legal + ├── lib + ├── README.html + └── release +``` + +- bin目录下常用工具 [Useage: Java 性能分析](/Java/AdvancedLearning/JavaPerformance.md): + - java javac javap jar + - jconsole jmap jmc jps jstack jstat jstatd jvisualvm + +************************ + +## Oracle JRE +> Java运行时环境 + +Java11 开始, JDK内去掉了JRE模块 + +************************ + +# OpenJDK +> [Official Site](http://openjdk.java.net/) | [OpenJDK Source](http://hg.openjdk.java.net/jdk) | [Github:source](https://github.com/openjdk/jdk) + +> [Open JDK 11: Download](http://jdk.java.net/11/) +> [Issues](https://bugs.openjdk.org/projects/JDK/issues) + +# 商业组织JDK +## Corretto +[aws corretto](https://aws.amazon.com/corretto/) + + +************************ + +- 阿里 Dragonwell +- 华为 毕昇 JDK +- 腾讯 Kona +- 美团 MJDK + - [MJDK 如何实现压缩速率的 5 倍提升?](https://tech.meituan.com/2023/08/31/meituan-mjdk-mzlib.html) + +# JDK源码 +> [ 不瞒你说,我最近跟Java源码杠上了 ](https://mp.weixin.qq.com/s?__biz=MzU4ODI1MjA3NQ==&mid=2247485421&idx=1&sn=c4543020e3d347267dbd8491ee48d2d5&chksm=fdded129caa9583fefb160e091d741e40fa24da4f3f14aa46bd4fc9b0d77dee9b98583a6c68b&mpshare=1&scene=1&srcid=0411IUXF6tTOYRaj5I6ebZj8&sharer_sharetime=1586571385813&sharer_shareid=246c4b52c1cb45eaa580c985c95107f3#rd) + diff --git a/Java/AdvancedLearning/Release/Java11.md b/Java/AdvancedLearning/Release/Java11.md new file mode 100644 index 0000000..e8fc960 --- /dev/null +++ b/Java/AdvancedLearning/Release/Java11.md @@ -0,0 +1,63 @@ +--- +title: Java11 +date: 2018-11-21 10:56:52 +tags: +categories: + - Java +--- + +**目录 start** + +1. [Java11](#java11) + 1. [安装配置](#安装配置) + 1. [Linux](#linux) + 1. [新特性](#新特性) + 1. [ZGC](#zgc) + 1. [部分API转为内部API](#部分api转为内部api) + +**目录 end**|_2021-05-17 00:15_| +**************************************** +# Java11 +> [Official: JDK 11 Documentation](https://docs.oracle.com/en/java/javase/11/) | [Official:api](https://docs.oracle.com/en/java/javase/11/docs/api/index.html) + +> [Tool reference](https://docs.oracle.com/en/java/javase/11/tools/tools-and-command-reference.html) + +应该就是真正意义上的 Java9 了, 原先发布的 9 和 10 都不是 LTS, 并且很多原先属于9的特性也是推迟到了这个版本 + +## 安装配置 +> [Official site](https://www.oracle.com/technetwork/java/javase/downloads/index.html) + +> [ZGC](https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html#JDK-8197831) + +通过 JVM 参数启用 ZGC +``` +-XX:+UnlockExperimentalVMOptions +-XX:+UseZGC +``` + +### Linux +```sh +JAVA_HOME=/path/to/java11 +export CLASSPATH=.:${JAVA_HOME}/lib +export PATH=${JAVA_HOME}/bin:$PATH +``` + +## 新特性 +> [Official: migrate guide](https://docs.oracle.com/en/java/javase/11/migrate/index.html) +> [JDK 11 Release Notes](https://www.oracle.com/technetwork/java/javase/11-relnote-issues-5012449.html#JDK-8197831) + +### ZGC +> [ZGC](/Java/AdvancedLearning/JvmGC.md#ZGC) + +******************************* +> [参考: Java 11 Tutorial](https://winterbe.com/posts/2018/09/24/java-11-tutorial/) + +> [参考: Java 11 正式发布,带来ZGC、Http Client等重要特性! ](https://mp.weixin.qq.com/s/CA_snRZ0kw9i-p1YCnHRKA) + +> [Java11](https://blog.csdn.net/weixin_38055381/article/details/82865385) + + +### 部分API转为内部API +> [Compile Your Application if Needed](https://docs.oracle.com/en/java/javase/11/migrate/index.html#JSMIG-GUID-77874D97-46F3-4DB5-85E4-2ACB5F8D760B) + +例如 sun.misc.Unsafe , 如果应用有引用, 在 JDK11 中编译会报错 加 `--add-exports` 可避免报错 diff --git a/Java/AdvancedLearning/Release/Java7.md b/Java/AdvancedLearning/Release/Java7.md new file mode 100644 index 0000000..e798bcf --- /dev/null +++ b/Java/AdvancedLearning/Release/Java7.md @@ -0,0 +1,173 @@ +--- +title: Java7 +date: 2018-11-21 10:56:52 +tags: +categories: + - Java +--- + +**目录 start** + +1. [Java7](#java7) + 1. [异常处理](#异常处理) + 1. [TWR](#twr) + 1. [NIO 2.0](#nio-20) + 1. [Path](#path) + 1. [文件系统 I/O](#文件系统-io) + 1. [异步 I/O](#异步-io) + +**目录 end**|_2020-07-05 16:28_| +**************************************** + +> [参考: JDK各个版本比较 JDK5~JDK10](https://blog.csdn.net/tieselingzhi/article/details/79764048s) + +# Java7 +- [Official: Java 7 API](https://docs.oracle.com/javase/7/docs/api/) + +_小特性_ +- Switch 支持 String + - Java6之前case语句中的常亮只支持 byte, char, short, int或枚举变量,Java7中增加了String +- 二进制数值的转换 + - 原本是 `int x = Integer.parseInt("1010100", 2);`Java7之后`int x = 0b110110;` +- 数字下划线 `10_0100__1000__0011` +- 钻石语法: 泛型右部直接`<>`不用写类型变量 + +## 异常处理 +- 异常处理 + - 允许异常的`或`操作 `catch(IOException | NullPointException e)` + - final关键字: `catch (final Exception e){throw e;}` 抛出后的是原异常类型的异常而不是Exception + +## TWR +- TWR(try with resources) +```java + // 从URL下载文件, 其中的资源都会自动关闭 + // 但是要注意发生异常后,资源也不会自动关闭, 所以确保TWR生效,正确的用法是为各个资源声明独立变量. + try(OutputStream out = new FileOutputStream(file); + InputStream is = url.openStream()){ + byte[] buf = new byte[1024]; + int len; + while ((len = is.read(buf)) > 0){ + out.write(buf, 0, len); + } + } +``` +- 另一个好处就是改善了错误跟踪的能力, 能够更准确地跟踪堆栈中的异常, 在Java7之前,处理资源时抛出的异常经常会被覆盖,TWR也可能会出现这种情况. +```java + try((InputStream in = getNullStream())){ + in.available(); + } +``` +- 在这种改进后的跟踪堆栈中能看到提示, 其中的NullPointException是能够抛出来看到的.没有被隐藏 + +> 目前TWR特性依靠一个接口来实现 AutoCloseable. TWR的try从句中出现的资源类都必须实现这个接口. Java7中大部分资源类都修改过 +> 但不是所有的资源类都采用了这项技术, JDBC是已经具备了这个特性. _官方提倡尽量采用TWR替代原有的方式_ + +********************* +> 简化变参方法调用 +```java + // 不允许创建已知类型的泛型数组, 编译报错 + HashMap[] array = new HashMap<>[2]; + + // 只能这样写 这样的编写也只是权宜之计, 编译器会警告: + // 可以将array定义为HashMap数组,但是又不能创建这个类型的实例 所以这里只是将原始类型实例化了放进去. + HashMap[] array = new HashMap[2]; + + // 现在能够这样编写: + public static Collection doSomething(T... entries){} +``` +## NIO 2.0 +### Path +```java + // 创建路径 + Path list = Paths.get("xmlStudy"); + // 获取文件名 + System.out.println(list.getFileName()); + // 获取名称元素数量 + System.out.println(list.getNameCount()); + + File file = new File("test.xml"); + // 将旧API的File转换成新的Path + Path path = file.toPath(); + // 将Path转换成File + File fiel2 = path.toFile(); +``` + +### 文件系统 I/O +```java + // 创建文件 + Path path = Paths.get("test2.xml"); + Path file = Files.createFile(path); + // 删除文件 + Files.delete(path); + + // 复制 + Path source = Paths.get("test2.xml"); + Path path = Paths.get("src/test2.xml"); + Files.copy(source, path); + // 移动 + Path path = Paths.get("test2.xml"); + Path source = Paths.get("src/test2.xml"); + Files.move(source, path); +``` + +- 读写数据 (可指定文件的打开方式, WRITE, READ, APPEND (StandardOpenOption.WRITE)) +```java + Path path = Paths.get("test.txt"); + // 打开一个带缓冲区的读取器 + try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println(line); + } + } + + // 打开带有一个缓冲区的写入器 + try (BufferedWriter writer = Files.newBufferedWriter(path)) { + writer.write("测试"); + } + + // java7简化后的读取全部行和字节 + List lines = Files.readAllLines(path); + byte[] bytes = Files.readAllBytes(path); + System.out.println(lines); +``` + +### 异步 I/O +- 将来式 + +```java + Path path = Paths.get("test.txt"); + // 异步打开文件 + AsynchronousFileChannel channel = AsynchronousFileChannel.open(path); + // 读取100000字节 + ByteBuffer buffer = ByteBuffer.allocate(100_000); + Future result = channel.read(buffer, 0); + while (!result.isDone()) { + System.out.println("其他事"); + } + // 获取结果 + Integer bytesRead = result.get(); + System.out.println(bytesRead); +``` +- 回调式 + +```java + Path path = Paths.get("test.txt"); + // 异步打开文件 + AsynchronousFileChannel channel = AsynchronousFileChannel.open(path); + // 读取100000字节 + ByteBuffer buffer = ByteBuffer.allocate(100_000); + + channel.read(buffer, 0, buffer, new CompletionHandler() { + // 读取调取完成时的回调方法 + @Override + public void completed(Integer result, ByteBuffer attachment) { + System.out.println(result); + } + @Override + public void failed(Throwable exc, ByteBuffer attachment) { + System.out.println(exc.getMessage()); + } + }); +``` + diff --git a/Java/AdvancedLearning/Release/Java8.md b/Java/AdvancedLearning/Release/Java8.md new file mode 100644 index 0000000..d2808c9 --- /dev/null +++ b/Java/AdvancedLearning/Release/Java8.md @@ -0,0 +1,1204 @@ +--- +title: Java8 +date: 2018-12-13 16:05:51 +tags: +categories: + - Java +--- + +💠 + +- 1. [Java8](#java8) + - 1.1. [实战库项目](#实战库项目) +- 2. [接口的变化](#接口的变化) + - 2.1. [default方法](#default方法) + - 2.2. [static方法](#static方法) +- 3. [函数式](#函数式) + - 3.1. [函数式接口](#函数式接口) + - 3.1.1. [构造函数的引用](#构造函数的引用) + - 3.2. [函数式接口案例](#函数式接口案例) +- 4. [Lambda](#lambda) + - 4.1. [行为参数化](#行为参数化) + - 4.2. [Lambda基础](#lambda基础) + - 4.2.1. [Lambda 实现原理](#lambda-实现原理) + - 4.2.2. [Lambda 局限性](#lambda-局限性) + - 4.2.3. [Lambda BUG](#lambda-bug) + - 4.3. [原始类型特化](#原始类型特化) + - 4.4. [类型检查、类型推断以及限制](#类型检查、类型推断以及限制) + - 4.4.1. [类型检查](#类型检查) + - 4.4.2. [同样的Lambda 不同的函数式接口](#同样的lambda-不同的函数式接口) + - 4.4.3. [类型推断](#类型推断) + - 4.4.4. [使用局部变量](#使用局部变量) + - 4.5. [复合 Lambda 表达式](#复合-lambda-表达式) + - 4.5.1. [比较器复合](#比较器复合) + - 4.5.2. [谓词复合](#谓词复合) + - 4.5.3. [函数复合](#函数复合) + - 4.6. [利用Lambda开发DSL框架](#利用lambda开发dsl框架) +- 5. [Stream](#stream) + - 5.1. [Stream与集合](#stream与集合) + - 5.1.1. [只能遍历一次](#只能遍历一次) + - 5.1.2. [外部迭代和内部迭代](#外部迭代和内部迭代) + - 5.2. [构建流](#构建流) + - 5.2.1. [有限流](#有限流) + - 5.2.2. [无限流](#无限流) + - 5.3. [数值流](#数值流) + - 5.3.1. [原始类型特化](#原始类型特化) + - 5.3.2. [数值范围](#数值范围) + - 5.4. [Stream操作](#stream操作) + - 5.4.1. [中间操作](#中间操作) + - 5.4.2. [终端操作](#终端操作) + - 5.5. [Stream的使用](#stream的使用) + - 5.5.1. [筛选](#筛选) + - 5.5.2. [映射](#映射) + - 5.5.3. [查找和匹配](#查找和匹配) + - 5.5.4. [归约](#归约) + - 5.5.4.1. [求和](#求和) + - 5.5.4.2. [极值](#极值) + - 5.5.4.3. [归约的优势与并行化](#归约的优势与并行化) + - 5.6. [收集器的使用](#收集器的使用) + - 5.6.1. [预定义收集器](#预定义收集器) + - 5.6.1.1. [汇总 collector](#汇总-collector) + - 5.6.1.2. [规约 reduce](#规约-reduce) + - 5.6.1.3. [分组](#分组) + - 5.6.1.3.1. [多级分组](#多级分组) + - 5.6.1.3.2. [按子组收集数据](#按子组收集数据) + - 5.6.1.4. [分区](#分区) + - 5.6.2. [自定义 Collector](#自定义-collector) +- 6. [Optional](#optional) + - 6.1. [Optional类和Stream接口的相似之处](#optional类和stream接口的相似之处) + - 6.2. [实践:读取Properties某属性](#实践读取properties某属性) +- 7. [时间处理](#时间处理) + - 7.1. [ZoneId](#zoneid) + - 7.2. [Clock](#clock) + - 7.3. [Duration](#duration) + - 7.4. [Period](#period) + - 7.5. [Instant](#instant) + - 7.6. [LocalDateTime](#localdatetime) + - 7.6.1. [LocalDate](#localdate) + - 7.6.2. [LocalTime](#localtime) + - 7.7. [ZoneOffset](#zoneoffset) + - 7.8. [ZonedDateTime](#zoneddatetime) + - 7.9. [Clock](#clock) + +💠 2024-09-03 14:05:18 +**************************************** +# Java8 +> [Doc](https://docs.oracle.com/javase/8/) | [API](https://docs.oracle.com/javase/8/docs/api/) + +[doc下载](https://www.oracle.com/technetwork/java/javase/documentation/jdk8-doc-downloads-2133158.html) + +> [Java8 JDK Readme](http://www.oracle.com/technetwork/java/javase/jdk-8-readme-2095712.html) | [Jre8 Readme](http://www.oracle.com/technetwork/java/javase/jre-8-readme-2095710.html)`有说明哪些是JRE运行不必要的文件` + +> [Java8 tools](https://docs.oracle.com/javase/8/docs/technotes/tools/)`介绍目录 bin/* 下的工具` | [jdk structure](https://docs.oracle.com/javase/8/docs/technotes/guides/desc_jdk_structure.html) +- [Oracle:Java8故障排除指南](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/) + +> [参考: Java 8 特性 – 终极手册](http://ifeve.com/java-8-features-tutorial/) + +**`参考书籍`** +1. Java8 in action +1. 写给大忙人看的Java8 + +## 实战库项目 +- [vavr](https://github.com/vavr-io/vavr) `改进Java8函数式` +- [resilience4j](https://github.com/resilience4j/resilience4j) `Hystrix继任者` + +***************************** +# 接口的变化 + +## default方法 +> 接口中也能写具有方法体的方法了 + +1. 调用方式: `接口.super.方法` + +## static方法 +类似于Scala Ruby trait/mixin 这种默认接口实现 + +1. 调用方式: static 方法 `接口.方法` + +************************* + +# 函数式 +> [参考 Java8函数接口实现回调及Groovy闭包的代码示例](http://www.cnblogs.com/lovesqcc/p/6083759.html) +> [Function接口 – Java8中java.util.function包下的函数式接口](http://ifeve.com/jjava-util-function-java8/) + +An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface as defined by the Java Language Specification. + +如果你用 `@FunctionalInterface` 定义了一个接口, 接口中只能有一个方法声明, 否则会编译报错 +而且无论接口声明中是否使用该注解,编译器都会将接口只有一个方法声明的任何接口视为 `FunctionalInterface` + +> 常用函数接口: (详细可参考 java.util.function; 包下的类) + +| 函数式接口 | 函数描述符 | 原始类型特化 | +|:----|:----|:----| +| `Predicate` | T->boolean | IntPredicate
LongPredicate
DoublePredicate| +| `Consumer` | T->void | IntConsumer
LongConsumer
DoubleConsumer | +| Function | T->R | `IntFunction`
IntToDoubleFunction
IntToLongFunction
`LongFunction`
LongToDoubleFunction
LongToIntFunction
`DoubleFunction`
`ToIntFunction`
`ToDoubleFunction`
`ToLongFunction`| +| `Supplier` | ()->T | BooleanSupplier
IntSupplier
LongSupplier
DoubleSupplier| +| `UnaryOperator` | T->T |IntUnaryOperator
LongUnaryOperator
DoubleUnaryOperator| +| `BinaryOperator` | (T,T)-> T | IntBinaryOperator
LongBinaryOperator
DoubleBinaryOperator| +| BiPredicate | (L,R)->boolean | | +| BiConsumer | (T,U)->void | `ObjIntConsumer`
`ObjLongConsumer`
`ObjDoubleConsumer`| +| BiFunction | (T,U)->R | ToIntBiFunction
ToLongBiFunction
ToDoubleBiFunction| + +- `(T,U) -> R` 的表达方式展示了应当如何思考一个函数描述符。 + - 表的左侧代表了参数类型。这里它代表一个函数,具有两个参数,分别为泛型T和U,返回类型为R。 + +- 为什么要使用 Function 以及闭包呢? + - 在语法上比定义回调接口、创建匿名类更加简洁; + - 尝试使用新的语言特性,理解多样化的编程思想,提升编程表达能力。 + +## 函数式接口 +> 通过 :: 操作符 简化代码 + +| Lambda | 方法引用 | +|:----|:----| +| (Apple a) -> a.getWeight() | Apple::getWeight +| () -> Thread.currentThread().dumpStack() | Thread.currentThread()::dumpStack +| (str, i) -> str.substring(i) | String::substring +| (String s) -> System.out.println(s) | System.out::println + +1. 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt) +1. 指向任意类型实例方法的方法引用 ( 例 如 String 的 length 方 法 , 写作String::length)。 +1. 指向现有对象的实例方法的方法引用 + - 假设你有一个局部变量expensiveTransaction 用于存放Transaction类型的对象,它支持实例方法getValue, + - 那么你就可以写expensiveTransaction::getValue + +### 构造函数的引用 +1. 空构造函数 等价于 `() -> T` + - 例如 `Supplier c1 = Apple::new;` + - 之后 `Apple a1 = c1.get();` 调用接口的get方法实例化Apple对象 + +不将构造函数实例化却能够引用它,这个功能有一些有趣的应用。例如,你可以使用Map来将构造函数映射到字符串值。 +你可以创建一个giveMeFruit方法,给它一个String和一个Integer,它就可以创建出不同重量的各种水果: +```java + static Map> map = new HashMap<>(); + static { + map.put("apple", Apple::new); + map.put("orange", Orange::new); + // etc... + } + public static Fruit giveMeFruit(String fruit, Integer weight){ + return map.get(fruit.toLowerCase()).apply(weight); + } +``` + +> 利用JDK提供的函数式接口 可以实现将一个,两个参数的构造函数转变为构造函数引用, 那么可以自定义实现三个参数的接口 +```java + public interface TriFunction{ + R apply(T t, U u, V v); + } + TriFunction colorFactory = Color::new; +``` + +## 函数式接口案例 + +| 使用案例 | Lambda例子 | 对应的函数式接口 | +|:----|:----|:----| +| 布尔表达式 | (List list) -> list.isEmpty() | `Predicate>`| +| 创建对象 |() -> new Apple(10) |`Supplier` +| 消费一个对象 | (Apple a) ->System.out.println(a.getWeight())|`Consumer`| +| 从一个对象中 选择/提取 | (String s) -> s.length() | Function 或 `ToIntFunction` +| 合并两个值 | (int a, int b) -> a * b | IntBinaryOperator| +| 比较两个对象 | (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()) | Comparator< Apple > 或 BiFunction 或 ToIntBiFunction + +- 请注意,任何函数式接口都不允许抛出受检异常(checked exception)。如果你需要Lambda 表达式来抛出异常,有两种办法: + - 定义一个自己的函数式接口,并声明受检异常, + - 或者把Lambda 包在一个try/catch块中。 +- 比如函数式接口BufferedReaderProcessor,它显式声明了一个IOException: + ```java + @FunctionalInterface + public interface BufferedReaderProcessor { + String process(BufferedReader b) throws IOException; + } + BufferedReaderProcessor p = (BufferedReader br) -> br.readLine(); + ``` +- 但是你可能是在使用一个接受函数式接口的API,比如Function,没有办法自己创建一个。这种情况下, 你可以显式捕捉受检异常: + ```java + Function f = (BufferedReader b) -> { + try { + return b.readLine(); + } catch(IOException e) { + throw new RuntimeException(e); + } + }; + ``` + +******************************* + +# Lambda +> λ 表达式 + +1. Lambda表达式可以理解为一种匿名函数:它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常的列表。 +1. Lambda表达式让你可以简洁地传递代码。 +1. 函数式接口就是仅仅声明了一个抽象方法的接口。 +1. 只有在接受函数式接口的地方才可以使用Lambda表达式。 +1. Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例。 +1. Java 8自带一些常用的函数式接口,放在java.util.function包里,包括`Predicate、 Function、 Supplier、Consumer 和BinaryOperator` +1. 为了避免装箱操作,对`Predicate`和`Function`等通用函数式接口的原始类型特化: IntPredicate、 IntToLongFunction等。 +1. 环绕执行模式(即在方法所必需的代码中间,你需要执行点儿什么操作,比如资源分配和清理)可以配合Lambda提高灵活性和可重用性。 +1. Lambda表达式所需要代表的类型称为目标类型。 +1. 方法引用让你重复使用现有的方法实现并直接传递它们。 +1. Comparator、 Predicate和Function等函数式接口都有几个可以用来结合Lambda表达式的默认方法。 + +> [参考: 你真的了解lambda吗?一文让你明白lambda用法与源码分析 ](https://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247485682&idx=1&sn=f3fb281b49a029b607f9377853a644bf&chksm=9bd0a56aaca72c7c8beebbea8f9471446cb444bd8e1e7d21016e906d1227e8f87770e2f8f31e&mpshare=1&scene=1&srcid=0810geQnLXB2oMjfoAOEJ39L#rd) +> [参考: 级联 lambda 表达式的函数重用与代码简短问题](http://www.techug.com/post/java-lambda.html) +> [参考: Java8:Lambda表达式增强版Comparator和排序](http://www.importnew.com/15259.html) + +## 行为参数化 +>1. 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们, 完成不同行为的能力。 +>1. 行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量。 +>1. 传递代码,就是将新行为作为参数传递给方法。但在Java 8之前这实现起来很啰嗦。为接 +>1. 声明许多只用一次的实体类而造成的啰嗦代码,在Java 8之前可以用匿名类来减少。 +>1. Java API包含很多可以用不同行为进行参数化的方法,包括排序、线程和GUI处理。 + +> 这种模式可以把一个行为(一段代码)封装起来,并通过传递和使用创建的行为, 将方法的行为参数化。前面提到过,这种做法类似于策略设计模式 +> Java API中的很多方法都可以用不同的行为来参数化。这些方法往往与匿名类一起使用 + +## Lambda基础 +- 可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。 + - `匿名` 我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多! + - `函数` 我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法签名的组成是一致的 + - `传递` Lambda表达式可以作为参数传递给方法或存储在变量中。 + - `简洁` 无需像匿名类那样写很多模板代码。 + +- Lambda基础语法: `(parameters) -> expression` 或者 `(parameters) -> { statements; }` + +例如 `() -> void` 是一个参数列表为空 返回值为void的函数, Runnable 的 run 方法 +```java + public Callable fetch() { + // 返回的方法的方法签名是 () -> String + return () -> "Tricky example ;-)"; + } + + // 方法签名为 () -> void + execute(() -> {}); + public void execute(Runnable r){ + r.run(); + } +``` + +**`实践`** +- 资源处理(例如处理文件或数据库)时一个常见的模式就是打开一个资源,做一些处理,然后关闭资源。这个设置和清理阶段总是很类似 + - 并且会围绕着执行处理的那些重要代码。这就是所谓的环绕执行(execute around) 模式 +```java + // 简单的从文件读取一行 + public static String processFile() throws IOException { + try (BufferedReader br = new BufferedReader(new FileReader("data.txt"))) { + return br.readLine(); + } + } + + // 如果将行为参数化, 就能通用的完成需求 + @FunctionalInterface + public interface BufferedReaderProcessor { + String process(BufferedReader b) throws IOException; + } + public static String processFile(BufferedReaderProcessor p) throws IOException {} + // 处理一行 + String twoLines =processFile((BufferedReader br) -> br.readLine()); + // 处理两行 + String result = processFile((BufferedReader br) -> br.readLine() + br.readLine()); +``` + +### Lambda 实现原理 + +1. 初步分析 编译后生成匿名内部类替换lambda表达式 + +> [参考: Java Lambda表达式 实现原理分析](https://blog.csdn.net/jiankunking/article/details/79825928) + +> 为什么Lambda范围内的代码引用外部变量时得是final修饰的。 +因为匿名内部类的引用不允许此操作 +``` + int b = 0; + new Runnable(){ + @Override + public void run() { + System.out.println(i++);//类属性,正常 + System.out.println(b++);//编译错误 + } + }; +``` + + +### Lambda 局限性 +1. 当方法上具有`类范围`的泛型参数时无法使用 Lambda 写法 + ```java + public interface Test { + T execute(T param); + } + ``` + +### Lambda BUG +> [泛型内extends多个类 lambda 表达式方法引用时会报错](https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8142476) + +************************ + +## 原始类型特化 +> Primitive Specializations + +> Java数据类型要么是引用类型(比如Byte、 Integer、 Object、 List),要么是原始类型(比如int、 double、 byte、 char)。 +但是泛型(比如`List`中的T)只能绑定到引用类型。这是由泛型内部的实现方式造成的 [泛型 详细](/Java/AdvancedLearning/JavaGenerics.md)。 + +因此,在Java里有一个将原始类型转换为对应的引用类型的机制。这个机制叫作装箱 boxing ,反之为拆箱 unboxing 。 +Java还有一个自动装箱机制来帮助程序员执行这一任务:装箱和拆箱操作是自动完成的。但这在性能方面是要付出代价的。 + +装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。 +Java 8为我们前面所说的函数式接口带来了一个专门的版本,以便在输入和输出都是原始类型时避免自动装箱的操作。 + +比如,在下面的代码中,使用IntPredicate就避免了对值1000进行装箱操作,但要是用Predicate就会把参数1000装箱到一个Integer对象中 -- Java8 in action + +一般来说,针对专门的输入参数类型的函数式接口的名称都要加上对应的原始类型前缀,比如 DoublePredicate、LongBinaryOperator等。 +Function接口还有针对输出参数类型的变种: ToIntFunction、 IntToDoubleFunction等。 + +************************ + +## 类型检查、类型推断以及限制 + +当我们第一次提到Lambda表达式时,说它可以为函数式接口生成一个实例。然而, Lambda 表达式本身并不包含它在实现哪个函数式接口的信息。 + +### 类型检查 +> Lambda的类型是从使用Lambda的上下文推断出来的。上下文(比如,接受它传递的方法的参数,或接受它的值的局部变量)中Lambda表达式需要的类型称为目标类型。 +> 请注意,如果Lambda表达式抛出一个异常,那么抽象方法所声明的throws语句也必须与之匹配。 + +### 同样的Lambda 不同的函数式接口 +> 有了目标类型的概念,同一个Lambda表达式就可以与不同的函数式接口联系起来,只要它们的抽象方法签名能够兼容。 + +比如,前面提到的Callable和PrivilegedAction,这两个接口 都代表着什么也不接受且返回一个泛型T的函数。 因此,下面两个赋值是有效的: +```java + Callable c = () -> 42; + PrivilegedAction p = () -> 42; +``` + +**`特殊的void兼容规则`** +如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(当然需要参数列表也兼容)。 +例如,以下两行都是合法的,尽管List的add方法返回了一个 boolean,而不是Consumer上下文(T -> void) 所要求的void: +```java + // Predicate返回了一个boolean + Predicate p = s -> list.add(s); + // Consumer返回了一个void + Consumer b = s -> list.add(s); +``` + +1. `Object o = () -> {System.out.println("Tricky example"); };` 不能通过编译 + - Lambda表达式的上下文是Object(目标类型)。但Object不是一个函数式接口。 + - 为了解决这个问题,你可以把目标类型改成Runnable,它的函数描述符是() -> void + +既可以利用目标类型来检查一个Lambda是否可以用于某个特定的上下文. 也可以用来做一些略有不同的事:推断Lambda参数的类型。 + +### 类型推断 +> 你还可以进一步简化你的代码。 Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式, +> 这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通过目标类型来得到。 +> 这样做的好处在于,编译器可以了解Lambda表达式的参数类型,这样就可以在Lambda语法中省去标注参数类型。 + +```java + // 无类型推断 + Comparator c = (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); + // 有类型推断 + Comparator c = (a1, a2) -> a1.getWeight().compareTo(a2.getWeight()); +``` + +### 使用局部变量 +我们迄今为止所介绍的所有Lambda表达式都只用到了其主体里面的参数。但Lambda表达式也允许使用自由变量(不是参数,而是在外层作用域中定义的变量), +就像匿名类一样。 它们被称作捕获Lambda。例如,下面的Lambda捕获了portNumber变量: +```java + int portNumber = 1337; + Runnable r = () -> System.out.println(portNumber); +``` +尽管如此,还有一点点小麻烦:关于能对这些变量做什么有一些限制。 Lambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。 +但局部变量必须显式声明为final,或事实上是final。 +换句话说, Lambda表达式只能捕获指派给它们的局部变量一次。(注:捕获实例变量可以被看作捕获最终局部变量this。) + +例如,下面的代码无法编译,因为portNumber 变量被赋值两次: +```java + int portNumber = 1337; + Runnable r = () -> System.out.println(portNumber); + portNumber = 31337; +``` +**`对局部变量的限制`** +- 你可能会问自己,为什么局部变量有这些限制。 + 1. 第一,实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。 + - 如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。 + - 因此, Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。 + 1. 第二,这一限制不鼓励你使用改变外部变量的典型命令式编程模式(这种模式会阻碍很容易做到的并行处理)。 + +**`闭包`** + +你可能已经听说过闭包(closure,不要和Clojure编程语言混淆)这个词,你可能会想Lambda是否满足闭包的定义。 +用科学的说法来说,闭包就是一个函数的实例,且它可以无限制地访问那个函数的非本地变量。例如,闭包可以作为参数传递给另一个函数。 +它也可以访问和修改其作用域之外的变量。现在, Java 8的Lambda和匿名类可以做类似于闭包的事情: +它们可以作为参数传递给方法,并且可以访问其作用域之外的变量。但有一个限制:它们不能修改定义Lambda的方法的局部变量的内容。 +这些变量必须是隐式最终的。可以认为Lambda是对值封闭,而不是对变量封闭。如前所述,这种限制存在的原因在于局部变量保存在栈上, +并且隐式表示它们仅限于其所在线程。如果允许捕获可改变的局部变量,就会引发造成线程不安全的新的可能性, +而这是我们不想看到的(实例变量可以,因为它们保存在堆中,而堆是在线程之间共享的) 。 + +## 复合 Lambda 表达式 +在实践中,这意味着你可以把多个简单的Lambda复合成复杂的表达式。比如,你可以让两个谓词之间做一个or操作,组合成一个更大的谓词。 +而且,你还可以让一个函数的结果成为另一个函数的输入。 +窍门在于,我们即将介绍的方法都是**默认方法**,也就是说它们不是抽象方法。 + +### 比较器复合 +> [Java Comparator 珍藏版 ](https://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247487247&idx=1&sn=55dc46cdde683b79864997a183c258df&chksm=9bd0a297aca72b8106cb40bedade4a69d539dbdbb579bed6238a54cf30bb270b8b4a8e45ec04&mpshare=1&scene=1&srcid=#rd) + +1. 单一属性比较 `Comparator c = Comparator.comparing(Apple::getWeight);` (实用的**Comparator.comparing**方法) + - 顺序(小到大) 逆序则再调用下 reversed() +1. 按重量排序, 重量一致则再按国家排序 `inventory.sort(comparing(Apple::getWeight).thenComparing(Apple::getCountry));` +1. nullsFirst是Comparator功能接口的静态方法。Comparator.nullsFirst方法返回一个空值友好的比较器,该比较器将null视为小于非null。 + 1. nullLast 与之相反 + +### 谓词复合 +> 谓词接口包括三个方法: negate、 and和or,让你可以重用已有的Predicate来创建更复杂的谓词。 +> 请注意, and和or方法是按照在表达式链中的位置,从左向右确定优先级的。因此, a.or(b).and(c)可以看作(a || b) && c。 + +1. 不红 `Predicate notRedApple = redApple.negate();` +1. 红且重`Predicate redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);` +1. 红且重或者是绿的 `Predicate redAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));` + +### 函数复合 +可以把Function接口所代表的Lambda表达式复合起来。 Function接口为此配了andThen和compose两个默认方法,它们都会返回Function的一个实例。 + +```java + Function f = x -> x + 1; + Function g = x -> x * 2; + Function h = f.andThen(g); // g(f(x)) + int result = h.apply(1); + + Function f = x -> x + 1; + Function g = x -> x * 2; + Function h = f.compose(g); // f(g(x)) + int result = h.apply(1); +``` + +> 使用场景 用String表示的一封信做文本转换:现在你可以通过复合这些工具方法来创建各种转型流水线了 +> 比如创建一个流水线:先加上抬头,然后进行拼写检查,最后加上一个落款, +```java + Function addHeader = Letter::addHeader; + Function transformationPipeline = addHeader + .andThen(Letter::checkSpelling) + .andThen(Letter::addFooter); +``` + +**`和数学对比`** +例如 根据给定的某个函数 以及上下限, 求积分: 定义函数 integrate `integrate(f, 3, 7)` + +```java + public double integrate(DoubleFunction f, double a, double b) { + return (f.apply(a) + f.apply(b)) * (b-a) / 2.0; + } +``` +## 利用Lambda开发DSL框架 + +********************************** + +# Stream +> [参考: Java 8 中的 Streams API 详解](https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/) + +- Java 8中的Stream API可以让你写出这样的代码 + 1. 声明性, 更简洁, 更易读 + 1. 可复合, 更灵活 + 1. 可并行, 性能更好 + +- Stream 简短的定义就是 “从支持数据处理操作的源,生成的元素序列” + - **元素序列** + - 就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。 + - 因为集合是数据结构,所以它的主要目的是以特定的时间/空间复杂度存储和素(如ArrayList与LinkedList)。 + - 但流的目的在于表达计算,比如你前面见到的filter、sorted 和map。集合讲的是数据,流讲的是计算。 + - **源** + - 流会使用一个提供数据的源,如集合、数组或输入/输出资源。请注意,从有序集合生成流时会保留原有的顺序。由列表生成的流,其元素顺序与列表一致。 + - **数据处理操作** + - 流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行 + - **内部迭代** + - 与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的。 + +> 注意: +- 通常简单的迭代使用外部迭代性能会更好,复杂操作使用Stream能让JDK为你在迭代时做优化 +- 使用 parallel() 开启并行流时要注意此时并行是依赖JVM内全局共用的Forkjoin线程池实现的,也就是说理论上会发生: + - 使用了并行流的业务代码A耗时很久会卡住使用了并行流业务代码B,这种问题发生了就比较难排查了,所以并行流通常使用较少,有异步场景均按业务独立线程池统一管理会更好 + +> [JDFrame](https://github.com/burukeYou/JDFrame)`封装出类似Spark Frame 的API` + +************************ + +## Stream与集合 + +粗略地说,集合与流之间的差异就在于什么时候进行计算。集合是一个内存中的数据结构,它包含数据结构中目前所有的值——集合中的每个元素都得先算出来才能添加到集合中。 +(你可以往集合里加东西或者删东西,但是不管什么时候,集合中的每个元素都是放在内存里的,元素都得先算出来才能成为集合的一部分) +相比之下,流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的。 + +例如构建一个质数流, 对所有的质数处理, 如果使用集合就要把所有的质数构建出来, 然后做下一步操作, 但是流只会按需生成。 +这是一种生产者-消费者的关系. 从另一个角度来说,流就像是一个延迟创建的集合:只有在消费者要求的时候才会计算值 + +### 只能遍历一次 +> 和迭代器类似,流只能遍历一次, 遍历完之后,我们就说这个流已经被消费掉了 +> 从哲学的角度看, 集合是空间中分布的一组值, 而流是时间中分布的一组值 + +### 外部迭代和内部迭代 +- 使用Collection接口需要用户去做迭代(比如用for-each),这称为外部迭代。 +- 相反,Streams库使用内部迭代——它帮你把迭代做了,还把得到的流值存在了某个地方,你只要给出一个函数声明迭代中执行的操作即可。 + +- 外部迭代 + - 显式的迭代集合, 命令式的执行操作 +- 内部迭代 + - 将迭代的细节隐藏起来, 方便优化 + +************************ + +## 构建流 +> 从值序列、数组、文件来创建流,甚至由函数创建无限流 + +### 有限流 +> 由值创建流 +```java +Stream stream = Stream.of("Java 8 ", "Lambdas ", "In ", "Action"); +``` + +> 由数组创建流 +```java + int[] numbers = {2, 3, 5, 7, 11, 13}; + int sum = Arrays.stream(numbers).sum(); +``` + +> 由文件生成流 + +Java中用于处理文件等I/O操作的NIO API(非阻塞I/O)已更新,以便利用Stream API。java.nio.file.Files中的很多静态方法都会返回一个流。 + +```java + long uniqueWords = 0; + try(Stream lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset())){ + uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" "))).distinct() .count(); + }catch(IOException e){} +``` + +### 无限流 +> Stream API提供了两个静态方法来 **从函数生成流**:`Stream.iterate` 和 `Stream.generate`。这两个操作可以创建所谓的 **无限流** +> 同样,你不能对无限流做排序或归约,因为所有元素都需要处理,而这永远也完不成! + +**`迭代`** +```java + Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println); + + // 获取斐波那契序列 元组 + Stream.iterate(new int[]{0, 1}, t -> new int[]{t[1], t[0]+t[1]}) + .limit(20) + .forEach(t -> System.out.println("(" + t[0] + "," + t[1] +")")); +``` + +**`生成`** +```java + Stream.generate(Math::random).limit(5).forEach(System.out::println); + // 很重要的一点是,在并行代码中使用有状态的供应源是不安全的。因此下面的代码仅仅是为了内容完整,应尽量避免使用! + IntSupplier fib = new IntSupplier(){ + private int previous = 0; + private int current = 1; + public int getAsInt(){ + int oldPrevious = this.previous; + int nextValue = this.previous + this.current; + this.previous = this.current; + this.current = nextValue; + return oldPrevious; + } + }; + IntStream.generate(fib).limit(10).forEach(System.out::println); +``` + +> 判断无限流: `stream.spliterator().estimateSize()` + +## 数值流 + +### 原始类型特化 +Java8 引入了三个原始类型特化流接口来解决这个问题: `IntStream、DoubleStream 和 LongStream`,分别将流中的元素特化为int、long和double,从而避免了暗含的装箱成本。 + +**映射到数值流** +```java + // 例如求和, 里面有一个隐含的拆箱操作 再求和 + numbers.parallelStream().reduce(0, Integer::sum); + + // 请注意,如果流是空的,sum默认返回 0 + numbers.parallelStream().mapToInt(Integer::intValue).sum() +``` + +**映射到对象流** +使用 boxed() 方法即可 + +**默认值OptionalInt** +```java + OptionalInt maxCalories = menu.stream().mapToInt(Dish::getCalories).max(); +``` +### 数值范围 +IntStream和LongStream 的 range() 或者 rangeClose() 方法能产生一个数值流 +> 例如 IntStream.rangeClose(1,100).filter(num->num%2==0).count() 统计100以内的偶数 + +> **获取勾股数流** +```java + Stream pythagoreanTriples = IntStream.rangeClosed(1, 100).boxed() .flatMap(a -> + IntStream.rangeClosed(a, 100) + .filter(b -> Math.sqrt(a*a + b*b) % 1 == 0) + .mapToObj(b -> new int[]{a, b, (int)Math.sqrt(a * a + b * b)}) + ); + + pythagoreanTriples.limit(5) .forEach(t -> System.out.println(t[0] + ", " + t[1] + ", " + t[2])); +``` + +************************ + +## Stream操作 +因为filter、sorted、map 和collect 等操作是与具体线程模型无关的高层次构件, 所以它们的内部实现可以是单线程的,也可能透明地充分利用你的多核架构 + +### 中间操作 +诸如 filter 或 sorted map flatMap limit distinct 等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查询。 +重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理因为中间操作一般都可以合并起来,在终端操作时一次性全部处理 (循环合并) + +1. filter 满足该条件的元素保留下来 +1. map 将一个流中的每个元素通过 一种映射 得到新的元素组成的流 +1. flatMap 使用流时, flatMap方法接受一个函数作为参数,这个函数的返回值是另一个流。这个方法会应用到流中的每一个元素,最终形成一个新的流的流。 + +| 操作 | 类型 | 返回类型 | 参数 | 函数描述符 | +|:----|:----|:----|:----|:----| +| filter |无状态 |`Stream` | `Predicate` | T -> boolean | +| peek |无状态 |`Stream` | `Consumer` | T -> void | +| unordered |无状态 |`Stream` | | T -> boolean | +| map/mapToXxx |无状态 |`Stream` |`Function`| T -> R| +| flatMap/flatMapToXxx |无状态 |`Stream` |`Function>`| T -> `Stream`| +| sorted |有状态 无界|`Stream`| `Comparator` |(T,T) -> int| +| skip |有状态 有界|`Stream`|long|| +| limit |有状态 有界|`Stream`|long|| +| distinct |有状态 无界|`Stream`||| + +### 终端操作 +> 非短路操作 + +| 操作 | 类型 | 返回类型 | 参数 | 函数描述符 | 目的 | +|:----|:----|:----|:----|:----|:---| +| forEach/forEachOrdered |无状态 |void|``Consumer``| T -> void| 消费流中的元素 | +|collect |无状态 |R|`Collector`|| +|reduce |有状态 有界 |`Optional`|`BinaryOprator`|(T, T) -> T| 把流归约成一个集合,比如List、Map甚至是Integer| +|count |无状态 |long||| 返回流中元素的个数| +| toArray || +| min/max/count || + +> 短路操作 + +| 操作 | 返回类型 | 参数 | 函数描述符 | 目的 | +|:----|:----|:----|:----|:----| +| allMatch/anyMatch/noneMatch |boolean|``Predicate``| T -> boolean | | +| findFirst/findAny |`Optional`||| + +********** + +## Stream的使用 +- 流的使用一般包括三件事: + - 一个数据源(如集合)来执行一个查询; + - 一个中间操作链,形成一条流的流水线; + - 一个终端操作,执行流水线,并能生成结果。 + > 流的流水线背后的理念类似于构建器模式 在构建器模式中有一个调用链用来设置一套配置(对流来说这就是一个中间操作链),接着是调用 build方法 (对流来说就是终端操作) + +### 筛选 +1. 利用 filter 使用谓词 Predicate 筛选 +1. 去重 distinct() +1. 截断 limit(n) 只能按流的长度单向截断 +1. 跳过元素 skip(n) + +### 映射 +- 流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素 + - (使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”) + +```java +List dishNameLengths = menu.stream() + .map(Dish::getName) .map(String::length) .collect(toList()); +``` +**`flatMap`** +```java +List uniqueCharacters = words.stream() + .map(w -> w.split("")) // Stream + .flatMap(Arrays::stream) // 扁平化流 (合并) + .distinct() + .collect(Collectors.toList()); +``` + +**使用流的方式嵌套迭代** +```java +// 给定两个数字列表,如何返回所有的数对呢?例如,给定列表[1, 2, 3]和列表[3, 4],应该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)] +List numbers1 = Arrays.asList(1, 2, 3); +List numbers2 = Arrays.asList(3, 4); +List pairs = numbers1.stream() + .flatMap(i -> + numbers2.stream() .map(j -> new int[]{i, j}) + ).collect(toList()); +``` + +### 查找和匹配 +> allMatch、anyMatch、noneMatch、findFirst findAny + +- 都是接受一个 谓词 函数, 都用到了我们所谓的短路,不需要处理所有的流,这就是大家熟悉的Java中&&和||运算符短路在流中的版本 + - allMatch、anyMatch、noneMatch 是 匹配 返回值是boolean + - findFirst findAny 是查找 返回 ``Optional`` findFirst 针对有序的流 + - 如果你不关心返回的元素是哪个,请使用 findAny ,因为它在使用并行流时限制较少。 + +### 归约 +- 如何把一个流中的元素组合起来,使用 reduce 操作来表达更复杂的查询 此类查询需要将流中所有元素反复结合起来,得到一个值 +- 这样的查询可以被归类为归约操作(将流归约成一个值)。用函数式编程语言的术语来说,这称为折叠(fold) + +> map 和 reduce 的连接通常称为 map-reduce 模式,因 Google 用它来进行网络搜索而出名,因为它很容易并行化。 + +#### 求和 + +```java + int sum = 0; + for (int x : numbers){ + sum += x; + } + + // reduce 参数: 初始值 函数 + int sum = numbers.stream().reduce(0, (a, b) -> a + b); + + int sum = numbers.stream().reduce(0, Integer::sum); + + // 无初始值 返回 Optional 对象 + Optional sum = numbers.stream().reduce((a, b) -> (a + b)); +``` + +#### 极值 + +```java + // 最大值 + Optional max = numbers.stream().reduce(Integer::max); + + // 最小值 + Optional min = numbers.stream().reduce(Integer::min); + Optional min = numbers.stream().reduce((x, y) -> x < y ? x : y); +``` + +> `return Stream.of(1,2,5,6).summaryStatistics();` 包含: 最大值,最小值,平均值,总数 + +#### 归约的优势与并行化 +相比于前面写的逐步迭代求和,使用 reduce 的好处在于,这里的迭代被内部迭代抽象掉了,这让内部实现得以选择并行执行reduce 操作。 +而迭代式求和例子要更新共享变量 sum ,这不是那么容易并行化的。如果你加入了同步,很可能会发现线程竞争抵消了并行本应带来的性能提升! +这种计算的并行化需要另一种办法:将输入分块,分块求和,最后再合并起来。 + +`int sum = numbers.parallelStream().reduce(0, Integer::sum); ` + +但要并行执行这段代码也要付一定代价,传递给 reduce 的Lambda不能更改状态(如实例变量),而且操作必须满足结合律才可以按任意顺序执行。 + +************************ + +## 收集器的使用 +> 函数式编程相对于指令式编程的一个主要优势:你只需指出希望的结果——“做什么”,而不用操心执行的步骤——“如何做” + + +### 预定义收集器 +Collectors所提供的工厂方法 它们主要提供了三大功能:将流元素归约和汇总为一个值 元素分组 元素分区 + +- toList toMap toSet 等方法 + +> 注意: toMap 方法的使用, 当 key 重复时会抛出异常 +> `toMap(k->k, v->v, (a,b)->b);` 使用该方式能避免, 设置了遇到重复的策略, 后者覆盖前者 + +#### 汇总 collector +> Collectors类专门为汇总提供了一个工厂方法:Collectors.summingInt 它可接受一个把对象映射为求和所需int的函数,并返回一个收集器;该收集器在传递给普通的collect方法后即执行我们需要的汇总操作 +> 求平均数 Collectors.averagingInt + +> `summarizing` 操作你可以得出元素的个数,并得到总和、平均值、最大值和最小值 +```java + IntSummaryStatistics menuStatistics = menu.stream().collect(summarizingInt(Dish::getCalories)); + // toString(): IntSummaryStatistics{count=9, sum=4300, min=120, average=477.777778, max=800} +``` + +**`连接字符串`** +joining工厂方法返回的收集器会把对流中每一个对象应用toString方法得到的所有字符串连接成一个字符串。 +`String shortMenu = menu.stream().collect(joining()); ` + +Stream.collect 实现 +```java + public final R collect(Collector collector) { + A container; + if (isParallel() + && (collector.characteristics().contains(Collector.Characteristics.CONCURRENT)) + && (!isOrdered() || collector.characteristics().contains(Collector.Characteristics.UNORDERED))) { + container = collector.supplier().get(); + BiConsumer accumulator = collector.accumulator(); + forEach(u -> accumulator.accept(container, u)); + } + else { + container = evaluate(ReduceOps.makeRef(collector)); + } + return collector.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH) + ? (R) container + : collector.finisher().apply(container); + } +``` + +#### 规约 reduce +事实上,我们已经讨论的所有收集器,都是一个可以用reducing工厂方法定义的归约过程的特殊情况而已。Collectors.reducing工厂方法是所有这些特殊情况的一般化。 + +- 例如 计算总热量 `int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories, (i, j) -> i + j));` + - 第一个参数: 规约操作的起始值 也是当流为空时的返回值 + - 第二个参数: 转换函数 + - 第三个参数: BinaryOperator 将两个项目累积成一个同类型的值 + +你可以把单参数reducing工厂方法创建的收集器看作三参数方法的特殊情况,它把流中的第一个项目作为起点,把恒等函数(即一个函数仅仅是返回其输入参数)作为一个转换函数。 +这也意味着,要是把单参数reducing收集器传递给空流的collect方法,收集器就没有起点;它将因此而返回一个Optional对象。 + +> 1. 收集框架的灵活性:以不同的方法执行同样的操作 + +1. 使用Integer的sum方法简化以上的求和 `int totalCalories = menu.stream().collect(reducing(0, Dish::getCalories,Integer::sum));` +1. 将对象映射为 int 然后规约求和 `int totalCalories = menu.stream().map(Dish::getCalories).reduce(Integer::sum).get(); ` +1. 或者转为 IntStream `int totalCalories = menu.stream().mapToInt(Dish::getCalories).sum(); ` + +> 2. 根据情况选择最佳解决方案 + +函数式编程(特别是Java 8的Collections框架中加入的基于函数式风格原理设计的新API)通常提供了多种方法来执行同一个操作。 +这个例子还说明,收集器在某种程度上比Stream接口上直接提供的方法用起来更复杂,但好处在于它们能提供更高水平的抽象和概括,也更容易重用和自定义。 + +> joining 替换 字符串直接拼接 + +```java + // 该方案 效率不高, 所有字符串被反复连接, 每次迭代都需创建新String对象 + strings.stream().sorted().reduce("", (a,b) -> a+b); + // joining 内部会使用 StringBuilder + strings.stream().sorted().collect(Collectors.joining()); +``` + +> 场景: 一个对象(含时间和整数两个属性)集合, 完成的操作是获取到最大时间以及数值平均值...等等多个值 +- [ ] 设计和实现 + +#### 分组 +一个常见的数据库操作是根据一个或多个属性对集合中的项目进行分组。 + +- 使用 groupingBy `Map> dishesByType = menu.stream().collect(groupingBy(Dish::getType));` + - groupingBy 方法的入参为一个 Function 一般称为分类函数 + +```java + // 实现复杂的分类函数 + public enum CaloricLevel { DIET, NORMAL, FAT } + Map> dishesByCaloricLevel = menu.stream().collect( + groupingBy(dish -> { + if (dish.getCalories() <= 400){ + return CaloricLevel.DIET; + } + else if (dish.getCalories() <= 700){ + return CaloricLevel.NORMAL; + }else{ + return CaloricLevel.FAT; + } + })); +``` + +##### 多级分组 +> 要实现多级分组,我们可以使用一个由双参数版本的 Collectors.groupingBy 工厂方法创建的收集器,它除了普通的分类函数之外,还可以接受collector类型的第二个参数。 + +```java + Map>> dishesByTypeCaloricLevel = + menu.stream().collect( + groupingBy(Dish::getType, + groupingBy(dish -> { + if (dish.getCalories() <= 400){ + return CaloricLevel.DIET; + }else if (dish.getCalories() <= 700) { + return CaloricLevel.NORMAL; + }else{ + return CaloricLevel.FAT; + } + } ) + ) + ); +``` + +##### 按子组收集数据 +> 可以把第二个groupingBy收集器传递给外层收集器来实现多级分组。但进一步说,传递给第一个groupingBy的第二个收集器可以是任何类型 +>> 例如: `Map typesCount = menu.stream().collect(groupingBy(Dish::getType, counting())); ` + +```java + Map> mostCaloricByType = menu.stream() + .collect(groupingBy( + Dish::getType, + maxBy( + comparingInt(Dish::getCalories) + ) + )); +``` +- 这个分组的结果显然是一个map,以Dish的类型作为键,以包装了该类型中热量最高的Dish的`Optional`作为值 + - 但是这里的 Optional 存在的意义不大, 因为先有的类型 进行分组, 才会进行 maxBy 所以值是一定存在的 + +> 1. 把收集器的结果转换为另一种类型 + +因为前述例子中的Optional存在意义不是很大, 所以 把收集器返回的结果转换为另一种类型,你可以使用Collectors.collectingAndThen工厂方法返回的收集器 + +```java + Map mostCaloricByType = menu.stream() + .collect(groupingBy( + Dish::getType, + collectingAndThen( + maxBy(comparingInt(Dish::getCalories)), + Optional::get) + )); +``` +- 这个工厂方法接受两个参数——要转换的收集器以及转换函数,并返回另一个收集器。 +- 这个收集器相当于旧收集器的一个包装,collect操作的最后一步就是将返回值用转换函数做一个映射。 +- 在这里,被包起来的收集器就是用maxBy建立的那个,而转换函数Optional::get则把返回的Optional中的值提取出来。 +- 前面已经说过,这个操作放在这里是安全的,因为reducing收集器永远都不会返回Optional.empty()。 + +> 2. 与 groupingBy 联合使用的其他收集器的例子 + +一般来说,通过groupingBy工厂方法的第二个参数传递的收集器将会对分到同一组中的所有流元素执行进一步归约操作。 +```java + // 对每一组Dish求和 + Map totalCaloriesByType = menu.stream().collect(groupingBy(Dish::getType, + summingInt(Dish::getCalories))); +``` +然而常常和groupingBy联合使用的另一个收集器是mapping方法生成的。这个方法接受两个参数:一个函数对流中的元素做**变换**,另一个则将变换的结果对象**收集**起来。 +其目的是在累加之前对每个输入元素应用一个映射函数,这样就可以让接受特定类型元素的收集器适应不同类型的对象。我们来看一个使用这个收集器的实际例子。 +比方说你想要知道,对于每种类型的Dish,菜单中都有哪些CaloricLevel。我们可以把groupingBy和mapping收集器结合起来,如下所示: +```java + Map> caloricLevelsByType = menu.stream().collect( + groupingBy(Dish::getType, mapping(dish -> { + if (dish.getCalories() <= 400) return CaloricLevel.DIET; + else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; + else return CaloricLevel.FAT; + }, toSet()))); +``` +这里,就像我们前面见到过的,传递给映射方法的转换函数将Dish映射成了它的CaloricLevel:生成的CaloricLevel流传递给一个toSet收集器,它和toList类似, +不过是把流中的元素累积到一个Set而不是List中,以便仅保留各不相同的值。如先前的示例所示,这个映射收集器将会收集分组函数生成的各个子流中的元素 +```java + // 可以给它传递一个构造函数引用来要求 HashSet + Map> caloricLevelsByType = menu.stream().collect( + groupingBy(Dish::getType, mapping(dish -> { + if (dish.getCalories() <= 400) return CaloricLevel.DIET; + else if (dish.getCalories() <= 700) return CaloricLevel.NORMAL; + else return CaloricLevel.FAT; + }, toCollection(HashSet::new) ) + )); + + // 可抽象出工具方法 + static Map> groupToMapSet(Collection

params, + Function keyFunc, + Function valFunc) { + if (CollectionUtils.isEmpty(params)) { + return Collections.emptyMap(); + } + return params.stream().collect(Collectors.groupingBy(keyFunc, + Collectors.mapping(valFunc, Collectors.toCollection(HashSet::new)) + )); + } +``` + +#### 分区 + +************************ + +### 自定义 Collector +> [参考: 自定义收集器深度剖析:](http://www.cnblogs.com/webor2006/p/8353314.html) + +java.util.stream.Collector + +- creation of a new result container (supplier()) +- incorporating a new data element into a result container (accumulator()) +- combining two result containers into one (combiner()) +- performing an optional final transform on the container (finisher()) + +```java + // toList实现 + public static Collector> toList() { + return new CollectorImpl<>(ArrayList::new, // supplier + List::add, // accumulator + (left, right) -> { left.addAll(right); return left; }, // combiner + CH_ID); + } + // toMap 实现 + public static > Collector toMap(Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction, + Supplier mapFactory) { + BiConsumer accumulator = (map, element) -> map.merge(keyMapper.apply(element), valueMapper.apply(element), mergeFunction); + return new CollectorImpl<>(mapFactory, // supplier + accumulator, // accumulator + mapMerger(mergeFunction), // combiner + CH_ID); + } + // toUnmodifiableSet JDK10 + public static Collector> toUnmodifiableSet() { + return new CollectorImpl<>(HashSet::new, // supplier + Set::add, // accumulator + (left, right) -> { + if (left.size() < right.size()) { + right.addAll(left); return right; + } else { + left.addAll(right); return left; + } + },// combiner + // 其中还包含一个小优化,只往大集合中追加小集合,降低插入运算次数(hash 扩容的成本) + // 而toList不需要是因为直接操作的数组内存复制 大小集合没区别 + set -> (Set)Set.of(set.toArray()), // finisher + CH_UNORDERED_NOID); + } + + CollectorImpl(Supplier supplier, + BiConsumer accumulator, + BinaryOperator combiner, + Set characteristics) { + this(supplier, accumulator, combiner, castingIdentity(), characteristics); + } + // finish 空实现 只做类型强转 + private static Function castingIdentity() { + return i -> (R) i; + } +``` + +************************ + +# Optional +>1. null引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。 +>1. Java 8中引入了一个新的类`java.util.Optional`,对存在或缺失的变量值进行建模。 +>1. 你可以使用静态工厂方法Optional.empty、 Optional.of以及Optional.ofNullable创建Optional对象。 +>1. Optional类支持多种方法,比如map、 flatMap、 filter,它们在概念上与Stream类中对应的方法十分相似。 +>1. 使用Optional会迫使你更积极地解引用Optional对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。 +>1. 使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional类型的值。 + +1. 能够显式的在方法签名上就表明返回值可能为 "空" (下文中的空都指 Optional.empty()) + - 增强了建模的表达能力 域模型中使用Optional,将允许缺失或者暂无定义的变量值用特殊的形式标记出来 + - 约束调用方需判断才能使用, 但是这个并不是万能的, 例如集合类型的返回的时候, 就不用 Optional 包住 显得繁琐, 直接返回 new 空集合即可 +1. 如果Optional约束了返回值, 就在语义上表明该方法的返回值可能为空, 如果这个方法是初始化方法等具有特殊含义的方法, 不能返回空, 那就不能使用Optional + - 这样虽然代码安全了, 但是将该方法的语义混淆了.所以 不仅不能用 Optional, 还需在方法中加上 断言 以尽早暴露该方法的异常 + - 虽然该方法在设计上是不能返回空, 但是还是有可能返回空, 为了健壮性, 还是需要调用方做非空检查 + +- 常用方法 + - `of(T value)` 封装对象到Optional内部, 若对象为null会立即抛出 NPE + - `ofNullable(T value)` 同上但是允许放入null + - `get()` 获取封装内的对象值, 但是若Optional为空 抛出 NoSuchElementException 异常 + - `orElse(T other)` 当Optional为空时提供默认值 + - `orElseGet(Supplier other)` 延迟提供默认值, 当为空执行传入的函数得到默认值 + - `orElseThrow(Supplier exceptionSupplier)` 与 get() 一致,但是可以自定义异常 + - `ifPresent(Consumer)` 当不为空执行传入的函数 + +## Optional类和Stream接口的相似之处 +1. *map* + 1. 使用 map 从 Optional 对象中提取和转换值: 可以将 Optional 看成只有一个元素的集合, 像Stream一样的使用 map + 1. 处理两个Optional对象: `person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));` 原始的写法就是要判断两个对象同时存在(person 和 car )才调用find...方法 + +1. *flatMap* + 1. 流式获取 Optional 约束的属性 `Optional name = a.flatMap(A::getB).flatMap(B::getC).map(C::getName)` + - 其中 `Optional c;` 是B的成员属性 `Optional b;` 是A的成员属性 + - 如果直接使用 map 就会发生 `Optional>` 嵌套, 所以需要使用 flatMap + +1. *filter* + 1. persion 存在且满足条件就返回自身否则返回空 `person.filter(o -> "name".equals(o.getName()))` + +************************ + +1. **注意**: *Optional 无法序列化*, 也就是说不能作为 PO 的字段, 但是可以在get上下功夫: `public Optional getName(){return this.name}` + +1. 异常与Optional的对比 + - 当一个方法由于某些原因无法返回期望值, 常见的做法是抛出异常, 或者返回null(不建议). 但是这时候多了一个选择, 返回Optional + - 例如 Integer.parseInt() 方法 若参数字符串不合法就会抛出异常, 这时候就可以自己定义一个工具方法, catch住异常 返回 空 (如下 stringToInt 方法) + +1. 应该避免使用使用基础类型的 Optional 对象 + - OptionalInt OptionalLong OptionalDouble, 因为他们不支持 Stream 操作 + - 即使 OptionalInt 能简化 Optional + +## 实践:读取Properties某属性 +> 从properties文件中读取某个属性, 正整数就返回该值, 否则返回0 + +```java + // 原始写法 + public int readDuration(Properties props, String name) { + String value = props.getProperty(name); + if (value != null) { + try { + int i = Integer.parseInt(value); + if (i > 0) { + return i; + } + } catch (NumberFormatException nfe) { } + } + return 0; + } + // 改进 + public static Optional stringToInt(String s) { + try { + return Optional.of(Integer.parseInt(s)); + } catch (NumberFormatException e) { + return Optional.empty(); + } + } + + public int readDuration(Properties props, String name) { + return Optional.ofNullable(props.getProperty(name)) + .flatMap(OptionalUtility::stringToInt) + .filter(i -> i > 0) + .orElse(0); + } +``` + +******************************* + +# 时间处理 + +1. SimpleDateFormat `yyyy-MM-dd HH:MM:SS` 但是线程不安全, Java8 可使用 `DateTimeFormatter` 例如 DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS") + +## ZoneId +> time-zone ID + +## Clock +> 具有时区信息的时间类 + +a clock can be used instead of System.currentTimeMillis() and TimeZone.getDefault(). + +## Duration +This class models a quantity or amount of time in terms of seconds and nanoseconds. +It can be accessed using other duration-based units, such as minutes and hours. + +> [How to model java.time.duration in Mysql Database](https://stackoverflow.com/questions/28427525/how-to-model-java-time-duration-in-mysql-database) + +## Period +> 类似于Duration, 不同点是 date-based + +## Instant +> 用于表示时间线上的一个瞬时点, 可用于表示时间戳, API操作更友好 + +## LocalDateTime +> 方便的新时间处理类, 用于替代Java5的Date, LocalDateTime 就是 LocalDate LocalTime 的组合 + +```java + // LocalDateTime 获取毫秒以及秒 也可以手动指定中国的时区 ZoneOffset.of("+8") + ZonedDateTime zonedDateTime = datetime.atZone(ZoneOffset.systemDefault()); + Instant instant = zonedDateTime.toInstant(); + Date date = Date.from(instant); + + // LocalDateTime -> Date + Date.from(datetime.atZone(ZoneId.systemDefault()).toInstant()); + // Date -> LocalDateTime + LocalDateTime.ofInstant(date.toInstant(), ZoneOffset.systemDefault()); + + // 格式化 + DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + fmt.format(LocalDateTime.now()); + // 解析 + LocalDateTime parse = LocalDateTime.parse(timeStr, fmt); +``` + +### LocalDate +- 获取自然周开始时间 `LocalDate.now().with(WeekFields.ISO.dayOfWeek(), 1L);` + +```java + // 常见格式 + DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyyMMdd"); + LocalDate parse = LocalDate.parse("20171018", format); + + // 格式不全时(年月日)需特殊处理 补全对应默认值 + DateTimeFormatter monthFMT = new DateTimeFormatterBuilder() + .appendPattern("yyyy") + .parseDefaulting(ChronoField.MONTH_OF_YEAR, 1) + .parseDefaulting(ChronoField.DAY_OF_MONTH, 1) + .toFormatter(); + LocalDate parse = LocalDate.parse("2017", monthFMT); +``` + +### LocalTime + +## ZoneOffset +时区偏移量,比如:+8:00 + +## ZonedDateTime +带时区的时间 + +## Clock +> [Guide to the Java Clock Class](https://www.baeldung.com/java-clock) + diff --git a/Java/AdvancedLearning/Release/JavaReleaseVersion.md b/Java/AdvancedLearning/Release/JavaReleaseVersion.md new file mode 100644 index 0000000..5e00f2f --- /dev/null +++ b/Java/AdvancedLearning/Release/JavaReleaseVersion.md @@ -0,0 +1,130 @@ +--- +title: Java主要发行版概述 +date: 2018-11-21 10:56:52 +tags: +categories: + - Java +--- + +💠 + +- 1. [Java主要发行版本](#java主要发行版本) + - 1.1. [Java5](#java5) + - 1.2. [Java6](#java6) + - 1.3. [Java7](#java7) + - 1.4. [Java8 LST](#java8-lst) + - 1.5. [Java9](#java9) + - 1.6. [Java10](#java10) + - 1.7. [Java11 LTS](#java11-lts) + - 1.8. [Java12](#java12) + - 1.9. [Java13](#java13) + - 1.10. [Java14](#java14) + - 1.11. [Java15](#java15) + - 1.12. [Java16](#java16) + - 1.13. [Java17 LTS](#java17-lts) + - 1.14. [Java18](#java18) + - 1.15. [Java21 LTS](#java21-lts) + +💠 2024-06-21 16:17:04 +**************************************** +# Java主要发行版本 +> [官网 Release Note](http://www.oracle.com/technetwork/java/javase/jdk-relnotes-index-2162236.html) + +> [Java语言特性系列 5-最新](https://segmentfault.com/a/1190000004417288) + +> [参考: JDK的版本号解惑](https://blog.csdn.net/bisal/article/details/118947676) + +![](/Java/AdvancedLearning/Release/img/001-jdk-release.km.svg) + +## Java5 +泛型 枚举 装箱拆箱 静态导入 foreach + +## Java6 +JDBC4.0 JAX-WS 2.0 + +## Java7 +> [详情](/Java/AdvancedLearning/Release/Java7.md) + +1. 语法糖:数字中的下划线 +1. 新的语言小特性:TWR(try with resources) +1. 类文件格式的变化:注解 +1. JVM的新特性: 动态调用 +1. 引入G1收集器 + +## Java8 LST +> [详情](/Java/AdvancedLearning/Release/Java8.md) + +1. 接口中新增 静态方法,默认方法 +1. 新增 Optional +1. 新增 Lambda +1. 新增 Stream +1. java.time 包 增强了日期时间的处理 + +- 181 版本移除了 Derby + +## Java9 +> [参考: Java 9 新特性概览 ](http://www.runoob.com/java/java9-new-features.html) + +> [参考: Java9 新特性汇总](http://www.infoq.com/cn/news/2014/09/java9) +> [参考: Java 9 新特性概述](https://www.ibm.com/developerworks/cn/java/the-new-features-of-Java-9/index.html) +> [参考: 深入解读 Java 9 新特性 ](https://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=2653549131&idx=1&sn=77997b94cc91fb7cbead6b7888f26474&chksm=813a63d3b64deac5506a5c0080718eb759720ec17538223af71b865f260428cc7c644d2d97de&scene=21#wechat_redirect) + +1. Jigsaw 模块化 +1. Stream Optional 改进 +1. 内置 轻量级 JSON API +1. HTTP2 客户端 +1. 云原生适配 + +## Java10 +> [参考: Java10的新特性](https://segmentfault.com/a/1190000014076481) + +1. 类型推断 +1. String 类 API 增强 +1. 集合 API 增强 +1. Stream 增强 +1. Optional 增强 +1. java javac 合并 + +## Java11 LTS +> [详情](/Java/AdvancedLearning/Release/Java11.md) + +> [参考: Java11的新特性](https://segmentfault.com/a/1190000016527932#articleHeader5) + +1. Flight Recorder 开源 +1. Epsilon 空gc实现: 用于性能测试 +1. HttpClient 默认实现 + +> [Oracle JDK11 Migration Guide](https://docs.oracle.com/en/java/javase/11/migrate/index.html) +> [Jdk8到jdk11 Springboot 踩坑指南](https://blog.csdn.net/ab601026460/article/details/86062991) + +> Illegal reflective access by org.springframework.cglib.core.ReflectUtils +- 增加JVM参数 --add-opens java.base/java.lang=ALL-UNNAMED + +## Java12 +1. Shenandoah GC +1. Switch Expressions + +## Java13 + +## Java14 + +## Java15 + +## Java16 + +## Java17 LTS + +> [从JDK 8升级到JDK 17踩坑全过程](https://cloud.tencent.com/developer/article/2240195) + +`jdeps --jdk-internals --multi-release 17 --class-path . encloud-api.jar` 分析依赖的废弃api + +## Java18 + +## Java21 LTS +> [OpenJDK 21](https://openjdk.org/projects/jdk/21/) + +1. 字符串模板 `语法糖` +1. 分代ZGC +1. 虚拟线程`协程 轻量级线程 用户级线程` + +> [Java21新特性](https://segmentfault.com/a/1190000044238496) diff --git a/Java/AdvancedLearning/Release/img/001-jdk-release.km.svg b/Java/AdvancedLearning/Release/img/001-jdk-release.km.svg new file mode 100644 index 0000000..97a9608 --- /dev/null +++ b/Java/AdvancedLearning/Release/img/001-jdk-release.km.svg @@ -0,0 +1 @@ +Java 版本历史Java5注解泛型枚举自动拆装箱可变长参数foreach静态导入Java6JDBC4.0更新编译API,动态生成代码Java7Coin项目TWR钻石语法Swith中的String引入G1垃圾回收器fork/join框架捕获多异常Java8Lambda表达式函数式接口Stream日期时间API接口中添加default方法并发优化Java9Jigsaw 模块化JShellReactive Streams接口中添加私有方法IO,并发,JVM日志HTTP2进程控制APIJava10局部变量类型推断应用类数据的共享 CDS新Jit编译器GraalVMJava11引入不做垃圾回收的垃圾回收器稳定版Http2客户端Flight Recorderjava命令直接运行单个java文件TLS1.3ZGC(实验阶段)移除Java EE及CORBA模块移除 JavaFX移除 Java Mission Control移除 JRE部分API转为JDK内置APIJava12Java13Java14Java15Java16Java17Java18Java19Java20Java21虚拟线程 \ No newline at end of file diff --git a/Java/AdvancedLearning/Security/Readme.md b/Java/AdvancedLearning/Security/Readme.md new file mode 100644 index 0000000..095d6b7 --- /dev/null +++ b/Java/AdvancedLearning/Security/Readme.md @@ -0,0 +1,34 @@ +--- +title: Java加密 +date: 2023-09-22 19:03:18 +tags: +categories: +--- + +**目录 start** + +1. [Security](#security) + 1. [伪随机数生成器](#伪随机数生成器) + +**目录 end**|_2023-09-22 19:03_| +**************************************** +# Security +> [Java Cryptography Architecture Standard Algorithm Name Documentation for JDK 8](https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html)`列出各种用途的算法,以及加密模式,信息摘要算法` + +1. Provider 类 提供DSA,RSA,MD5算法 +1. Security 类 管理Provider +1. MessageDigest 类 提供了信息摘要算法:MD2 MD5 SHA-1(SHA) SHA-256 SHA-384 SHA-512. + +************************ + +## 伪随机数生成器 +Random +- 依据设置的seed作为初始值,随机序列采用[线性同余法](/Algorithm/Cryptography.md#伪随机数生成器)生成,特点是当前值需要依赖上一个随机数值更新seed。 +- 因此Random用了一个AtomicLong来保存当前seed,在并发高的时候会出现忙等待 + +ThreadLocalRandom +- 不将seed作为共享内存,而是跟随线程绑定,Java7在ThreadLocal中,Java8则内置在了Thread类上 + +SecureRandom +- 不使用构造的seed,而是从操作系统的 /dev/random 和 /dev/urandom 特殊设备获取字节序列作为seed `注意如果操作系统刚启动设备内熵值不够,这个类获取随机数的话会报错` + diff --git a/Java/AdvancedLearning/Socket.md b/Java/AdvancedLearning/Socket.md deleted file mode 100644 index 20cef14..0000000 --- a/Java/AdvancedLearning/Socket.md +++ /dev/null @@ -1,19 +0,0 @@ -`目录 start` - -- [Socket](#socket) - - [基础](#基础) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Socket - -## 基础 -> 其实就是Socket [码农翻身:张大胖的socket ](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513387&idx=1&sn=99665948d0b968cf15c5e7a01ffe166c&chksm=80d679e8b7a1f0febad077b57e8ad73bfb4b08de74814c45e1b1bd61ab4017b5041942403afb&scene=21#wechat_redirect) - -- 得到URL文件的输入流 - - `new URL(url).openStream()` - -- 使用Linux编程开启web容器时`java.net.SocketException: 权限不够` - - [参考博客](http://www.xuebuyuan.com/1432737.html) - - 快速解决,不使用小于1024的端口即可,或者提权 - diff --git a/Java/AdvancedLearning/Thread.md b/Java/AdvancedLearning/Thread.md deleted file mode 100644 index 28eff8c..0000000 --- a/Java/AdvancedLearning/Thread.md +++ /dev/null @@ -1,76 +0,0 @@ -`目录 start` - -- [线程的基础学习](#线程的基础学习) - - [TODO](#todo) - - [基础](#基础) - - [线程的意义](#线程的意义) - - [线程的生命周期](#线程的生命周期) - - [创建](#创建) - - [控制](#控制) - - [销毁](#销毁) - - [线程的优先级](#线程的优先级) - - [线程池](#线程池) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 线程的基础学习 -> [个人相关代码](https://github.com/Kuangcp/JavaBase/tree/master/src/main/java/com/threads) - -## TODO -- [ ] 线程的多种创建方式 -- [ ] 线程池的创建方式 -- [ ] 线程的状态转化 - -## 基础 -- [码农翻身:我是一个线程](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=416915373&idx=1&sn=f80a13b099237534a3ef777d511d831a&scene=21#wechat_redirect) | [码农翻身:编程世界的那把锁](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513653&idx=1&sn=e30c18c0c1780fb3ef0cdb858ee5201e&chksm=80d67af6b7a1f3e059466302c2c04c14d097c1a5de01cf986df84d4677299542f12b974dfde3&scene=21#wechat_redirect) | [码农翻身:加锁还是不加锁,这是一个问题 ](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513692&idx=1&sn=ef2416a4bb96d64db77e32d5b4c7967e&chksm=80d67a9fb7a1f3898e513cc1d9e96841610bb84aed2dc24cab2d403e74e317e3c447e45e7611&scene=21#wechat_redirect) - -## 线程的意义 -## 线程的生命周期 -> [参考博客](https://segmentfault.com/a/1190000005006079) | [Blog: 线程详解](http://www.cnblogs.com/riskyer/p/3263032.html) | [参考Java-learning仓库](https://github.com/brianway/java-learning) - - -### 创建 -- 创建线程有三种创建方式: 继承,实现接口,实例化匿名内部方法。-> [示例代码](https://github.com/Kuangcp/JavaBase/blob/master/src/main/java/com/threads/HowToCreateThread.java) - -> 查看Thread类源码 看看Thread类源码,捋清Runnable,target,run,start关系 -- Runnable是一个接口 -- target是Thread类中类型为Runnable,名为target的属性 -- run是Thread类实现了Runnable的接口,重写的方法。 -- start是启动线程的方法 -- 在Thread类中,调用关系为:_start->start0->run->target.run_ - -_Thread类的run方法源码_ -```java - public void run() { - if (target != null) { - target.run(); - } - } -``` -_Thread类的target属性_ -```java - /* What will be run. */ - private Runnable target; -``` -- target属性由 `private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc)`方法初始化。 - - init方法在Thread类的构造方法里被调用 - -### 控制 -- 当调用 `Thread.join()` 时,_调用线程_将阻塞,直到_目标线程_完成为止。 - -### 销毁 - -************************ -## 线程的优先级 -> 多个线程同时运行时,由线程调度器来决定哪些线程运行,哪些等待以及线程切换的时间点. 由于各个操作系统的线程调度器的实现各不相同, 所以依赖JDK来设置线程优先级策略是错误和非平台可移植性的. - -*************************** -## 线程池 -> [线程池 BlockingQueue synchronized volatile](https://segmentfault.com/a/1190000012916473) -> [参考博客: Java(Android)线程池](http://www.trinea.cn/android/java-android-thread-pool/) -> [参考博客: Java ThreadPoolExecutor线程池使用的一个误区](http://codefine.site/2941.html) -> [参考博客: 聊聊并发(三)Java线程池的分析和使用](http://ifeve.com/java-threadpool/) -> [参考博客: 线程池](http://ifeve.com/thread-pools/) - - - diff --git a/Java/AdvancedLearning/Tuning/JavaOOM.md b/Java/AdvancedLearning/Tuning/JavaOOM.md new file mode 100644 index 0000000..e737186 --- /dev/null +++ b/Java/AdvancedLearning/Tuning/JavaOOM.md @@ -0,0 +1,145 @@ +--- +title: JavaOOM +date: 2024-03-06 14:09:01 +tags: +categories: +--- + +💠 + +- 1. [OOM](#oom) + - 1.1. [简单案例](#简单案例) + - 1.2. [Heap space OOM](#heap-space-oom) + - 1.3. [Metaspace OOM](#metaspace-oom) + - 1.4. [Compressed Class Space OOM](#compressed-class-space-oom) + - 1.5. [Direct Memory OOM](#direct-memory-oom) + - 1.6. [GC overhead limit exceeded](#gc-overhead-limit-exceeded) +- 2. [分析](#分析) + +💠 2024-08-22 11:15:26 +**************************************** +# OOM +> 注意OOM并不代表Java进程一定会退出,如果导致OOM的地方能被catch,且泄漏点能随着这次任务的终止而可回收的话,JVM将继续正常运行。 +> [Why JVM can recovery from OOM Java heap space by itself](https://stackoverflow.com/questions/72865015/why-jvm-can-recovery-from-oom-java-heap-space-by-itself) + +## 简单案例 + +例如 +```java + public static void main(String[] args) { + try { + List data = new ArrayList<>(); + while (true) { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + log.error("", e); + } + log.info("size={}", data.size()); + data.add(new byte[1024 * 1024]); + } + } catch (Throwable e) { + log.error("", e); + } + + while (true) { + try { + TimeUnit.MILLISECONDS.sleep(500); + } catch (InterruptedException e) { + log.error("", e); + } + log.info("do something"); + } + } +``` + +又或者常见的SpringMVC服务 +```java + @GetMapping("/oom") + public String oom() { + List data = new ArrayList<>(); + while (true) { + try { + TimeUnit.MILLISECONDS.sleep(100); + } catch (InterruptedException e) { + log.error("", e); + } + log.info("size={}", data.size()); + data.add(new byte[1024 * 1024]); + } + } +``` + +注意 `org.springframework.web.servlet.DispatcherServlet` 中的 `doDispatch` catch了Error也包装成了Exception,方便统一异常处理器。 +这只会导致Tomcat的NIO线程终止了这次请求,局部变量 data 就可以回收掉了,整个服务仍正常进行,只是在快要OOM时高频的GC影响了系统的吞吐量而已。 + +```java + catch (Exception ex) { + dispatchException = ex; + } + catch (Throwable err) { + // As of 4.3, we're processing Errors thrown from handler methods as well, + // making them available for @ExceptionHandler methods and other scenarios. + dispatchException = new NestedServletException("Handler dispatch failed", err); + } +``` +************************ + +## Heap space OOM +异常信息: + +java.lang.OutOfMemoryError: Java heap space +java.lang.OutOfMemoryError: Requested array size exceeds VM limit + +[Error java.lang.OutOfMemoryError: GC overhead limit exceeded](https://stackoverflow.com/questions/1393486/error-java-lang-outofmemoryerror-gc-overhead-limit-exceeded)`常见于内存缓慢泄漏,GC成本越来越高时` + +## Metaspace OOM +[一次Metaspace OutOfMemoryError问题排查记录](https://juejin.cn/post/7114516283290288158)`很多GeneratedMethodAccessor类` + +原理理解比较复杂,但定位和解决问题会比较简单,经常会出问题的几个点有 Orika 的 classMap、JSON 的 ASMSerializer、Groovy动态加载类等,基本都集中在 反射、Javasisit字节码增强、CGLIB动态代理、OSGi自定义类加载器等技术点上 +> [参考: Metaspace 之一:Metaspace整体介绍](https://www.cnblogs.com/duanxz/p/3520829.html) + + +https://heapdump.cn/article/1924890 +https://heapdump.cn/article/54786?from=pc +https://heapdump.cn/article/2152817 + +-XX:+TraceClassLoading -XX:+TraceClassUnloading +-verbose:class + +https://developer.aliyun.com/article/780535 + +https://www.mastertheboss.com/java/solving-java-lang-outofmemoryerror-metaspace-error/#google_vignette + +https://javakk.com/805.html +https://www.dongcb.com/818.html + +https://juejin.cn/post/7114516283290288158 + + +## Compressed Class Space OOM + +## Direct Memory OOM + +[Netty堆外内存泄露排查盛宴](https://tech.meituan.com/2018/10/18/netty-direct-memory-screening.html) + + +## GC overhead limit exceeded +> [Error java.lang.OutOfMemoryError: GC overhead limit exceeded](https://stackoverflow.com/questions/1393486/error-java-lang-outofmemoryerror-gc-overhead-limit-exceeded) + + +# 分析 +重点是保存现场,获取到问题时间内多维度的信息辅助快速定位,首要是 dump文件 其次是 jstack历史 gc日志 应用日志 监控系统上问题时间段的指标变化情况 等等。 + +> [由JDK bug引发的线上OOM](http://ifeve.com/%e7%94%b1jdk-bug%e5%bc%95%e5%8f%91%e7%9a%84%e7%ba%bf%e4%b8%8aoom/) +> [Speeding up Java heap dumps with GNU Debugger](https://medium.com/platform-engineer/speeding-up-java-heap-dumps-with-gnu-debugger-c01562e2b8f0)`但是实测更慢,可能和环境有关吧 maybe` + +- [jmap](/Java/AdvancedLearning/JvmTool.md#jmap) +- jcmd 1 GC.heap_dump /tmp/docker.hprof + +通常使用 jmap或jcmd dump到文件,但是如果JVM已经发生OOM且进程占用CPU很高的情况下jmap会很慢甚至失败(例如attach失败)。 +此时可以使用gdb先dump下core,再转为hprof文件。 + +- ulimit -c unlimited +- gcore pid `core文件可能会很大,800M堆dump出了7G的文件` +- jmap -dump:format=b,file=heap.hprof /path/to/java core.${pid} `该过程是单线程的,会很慢` diff --git a/Java/AdvancedLearning/Tuning/JavaPGO.md b/Java/AdvancedLearning/Tuning/JavaPGO.md new file mode 100644 index 0000000..ff16273 --- /dev/null +++ b/Java/AdvancedLearning/Tuning/JavaPGO.md @@ -0,0 +1,70 @@ +--- +title: JavaPGO +date: 2024-04-30 22:26:23 +tags: +categories: +--- + +💠 + +- 1. [Java 中的 PGO](#java-中的-pgo) + - 1.1. [HotSpot](#hotspot) + +💠 2024-04-30 23:06:06 +**************************************** +# Java 中的 PGO + +> [Note: PGO base](/Skills/Councurrency/PGO.md) + +- HotSpot 虚拟机默认会在运行期启用PGO。 +- GraalVM 企业版本的NativeImage中才能应用PGO技术 `社区版不支持` + - [Profile-Guided Optimizations ](https://www.graalvm.org/22.0/reference-manual/native-image/PGO/) + - [Optimize a Native Executable with Profile-Guided Optimizations](https://www.graalvm.org/latest/reference-manual/native-image/guides/optimize-native-executable-with-pgo/)`样例` + +> [在Java应用程序中释放峰值性能:配置文件引导优化(PGO)概述](https://www.51cto.com/article/783879.html) [原文](https://dzone.com/articles/unleash-peak-performance-in-java-applications-over) + +## HotSpot +- 方法内联 +- 循环展开 +- 内存访问模式优化 将内存访问模式与硬件功能相结合来显著提高缓存性能 + +虽然已经支持在运行期实时收集信息做PGO,但是预先PGO可以省去这个预热过程,但是也有弊端业务变化频繁也就意味着代码变更频繁profile是否准确是否带来负优化是比较难衡量的。 +大型应用启动后做预热的话,可以避免出现应用刚启动时大量的接口或操作的延迟相较于长期运行时高出很多的问题。 + +> 采集Profiling +- VisualVM +- YourKit +- Java Flight Recorder +- Async Profiler + +> 预热/训练 + +通过进行全面的训练运行,可以捕获应用程序可能显示广泛的运行时行为。 +- 模拟代表常见用户操作的用户交互和工作流。 +- 模拟高负载条件的压力测试。 +- 探索性测试以覆盖不同的代码路径。 +- 负载测试以评估可扩展性。 + +> Profile文件 + +Profiling工具从训练运行中收集数据,并将其存储在配置文件数据库或日志文件中, 配置文件数据可能包括如下指标: +- 方法调用计数 +- 内存分配和垃圾收集统计 +- 线程活动和同步详细信息 +- 异常发生和处理 +- CPU和内存使用情况 + +> JIT使用 + +HotSpot JVM是使用最广泛的Java运行时环境,它通过“分层编译”机制支持PGO, `-XX:+UseProfiledCode` 和`-XX:ProfiledCodeGenerate`控制HotSpot中的PGO。 + +> 分析与调优 +- 识别性能瓶颈:分析性能分析数据以识别性能瓶颈,例如频繁调用的方法、热代码路径或内存密集型操作。 +- 优化决策:基于分析数据,做出关于代码优化的明智决策。常见的优化包括方法内联、循环展开、内存访问模式改进和线程同步增强。 +- 优化技术:使用适当的技术和编码实践实现所选的优化。 +- 基准测试:在进行优化后,对应用程序进行基准测试,以衡量性能改进。使用分析工具来验证优化是否对分析期间确定的瓶颈产生了积极影响。 + +> 定期重新分析和优化 +- 性能优化是一个持续的过程。随着应用程序的技术设计和业务需求的变化,定期重新分析和优化对于保持峰值性能至关重要。在应用程序生命周期的不同阶段继续收集配置文件数据,并相应地调整优化。 +- 一个思路便是每次需求升级时,考虑技术和业务变化的影响面,重新生成Profile文件。 + diff --git a/Java/AdvancedLearning/Tuning/JavaTroubleshoot.md b/Java/AdvancedLearning/Tuning/JavaTroubleshoot.md new file mode 100644 index 0000000..999cb7d --- /dev/null +++ b/Java/AdvancedLearning/Tuning/JavaTroubleshoot.md @@ -0,0 +1,152 @@ +--- +title: Java问题排查手册 +date: 2023-08-25 15:51:12 +tags: +categories: +--- + +💠 + +- 1. [Troubleshoot](#troubleshoot) + - 1.1. [GC](#gc) + - 1.1.1. [主要关注指标](#主要关注指标) + - 1.2. [Memory](#memory) + - 1.3. [CPU](#cpu) + - 1.3.1. [线程](#线程) + - 1.4. [ClassLoader](#classloader) + +💠 2024-06-01 13:47:36 +**************************************** +# Troubleshoot +当遇到需要对某个Java应用性能调优,故障处理时的技能或思路汇总 + +> Troubleshooting: [Oracle: Java8](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/) | [Oracle: Java11](https://docs.oracle.com/en/java/javase/11/troubleshoot/general-java-troubleshooting.html) + +> [目前最全的Java服务问题排查套路](https://juejin.cn/post/6844903816379236360) +> [完蛋,我被故障包围了](https://www.bilibili.com/video/BV1vc411U78U/?buvid=XXF1096F78012CCE01D64B283450438CC6206)`采用各种工具分析和排查` + +************************ + +![](./img/mind.drawio.svg) + +`不可用故障处理` **重要且紧急** + +> 基础设施层:寻求方式快速搭建新的一层(例如K8S的命名空间下全部服务重建),立马切换解析或网关流量 +> JVM层:记录好后续排查分析故障现场的必要信息后(dump,日志,linux系统日志),立马重启,释放本该释放的资源或中断已经异常的流程 + +排查思路: +- `Delta` 正式环境可复现问题,测试或灰度无法出现,且不能轻易重启正式环境,通过对生产的JVM做各类指标的记录,对比某个业务操作前后或故障前后的指标差异分析出问题的触发点 + - 限制:不能做太影响性能的指标记录和分析 +- `Debug` 在测试或灰度环境上可复现问题,可直接Debug接入调试代码,或本地采用高耗能的方式debug分析`抓包,strace,CPU火焰图,等方式` + - 限制:**可复现**,通常能有这个条件已经能直接通过debug代码就能解决问题了 + +************************ + +`性能调优` +> [Linux 性能分析](/Linux/Base/LinuxPerformance.md) +> [Linux 网络](/Linux/Base/LinuxNetwork.md) +> [JVM 分析工具](/Java/AdvancedLearning/JvmTool.md) + +## GC +> [Java GC](/Java/AdvancedLearning/JvmGC.md) + +> [Java中9种常见的CMS GC问题分析与解决](https://tech.meituan.com/2020/11/12/java-9-cms-gc.html) + +> [大量类加载器创建导致诡异FullGC](https://heapdump.cn/article/1924890) +> [参考: 译:谁是 JDK8 中最快的 GC](https://club.perfma.com/article/233480) +> [《沙盘模拟系列》JVM如何调优](https://my.oschina.net/u/4030990/blog/3149182) +> [深入浅出GC问题排查](https://blog.ysboke.cn/archives/242.html) +> [参考: CMS Deprecated. Next Steps?](https://dzone.com/articles/cms-deprecated-next-steps) + +- [Oracle JDK8 GC调优指南](https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/toc.html) +- [Oracle JDK11 GC调优指南](https://docs.oracle.com/en/java/javase/11/gctuning/introduction-garbage-collection-tuning.html) + +`工具` +> [gceasy.io](https://gceasy.io) +> [GCViewer](https://github.com/chewiebug/GCViewer) + +`实践` +> [从实际案例聊聊Java应用的GC优化](https://tech.meituan.com/2017/12/29/jvm-optimize.html)`观察监控指标调整JVM参数: 年轻代 晋升阈值等` +> 根本原则是每一次GC都回收尽可能多的对象,降低GC次数减少无用的扫描和暂停开销 + +### 主要关注指标 +> [garbage-collection-kpi](https://blog.gceasy.io/2016/10/01/garbage-collection-kpi/) +> [What are Throughput Performance, Latency Performance, and Memory Footprint in Java Programming? ](https://www.h2kinfosys.com/blog/what-are-throughput-performance-latency-performance-and-memory-footprint-in-java-programming/) + +> 三者不可兼得,通常兼顾两者舍弃另一方 +- `延迟(Latency)`: 也可以理解为最大停顿时间,即垃圾收集过程中单次 STW 的最长时间,越短越好,一定程度上可以接受频次的增多,是 GC 技术的主要发展方向。 +- `吞吐量(Throughput)`: 应用系统的生命周期内,由于 GC 线程会占用 Mutator 当前可用的 CPU 时钟周期,吞吐量即为 Mutator 有效花费的时间占系统总运行时间的百分比 + - 例如应用系统运行了 100 min,GC 累计耗时 1 min,则系统吞吐量为 99%,通常目标是**超过95%**。 + - 吞吐量优先的垃圾收集器会倾向于接受`单次耗时较长`的停顿,`累计停顿耗时短`的GC策略。 +- `内存占用(Footprint)`: 取决于不同的GC算法和内存设置,通常来说 Parallel CMS会消耗更多,Serial会消耗更少 + +延迟 + +- 响应时间目标是平均目标吗?是否以百分位数表示,例如第50、90、95 或 99 个百分位数的响应时间? `常见为使用后者` +- 响应时间目标是否是一个永远不应该被超越的绝对最小值? 是否有可能超过响应时间目标? 如果可以的话,可以添加多少? 又可以超过多长时间呢? `完全取决于业务,不过在产品学上400ms是肉眼感知的最慢阈值` +- 如何评估响应时间? 在哪里进行响应时间测量? `APM类系统实现监控和告警` + +吞吐量 + +- Java 编程性能目标是否被视为峰值性能目标? 或者吞吐量目标是应用程序必须始终满足的性能目标吗?`主要取决于业务(目前横向扩展成本很低,此项的考虑优先级通常会更低)` +- 应用程序预期处理的最高负载是多少?例如,预计有多少并发或活动用户、并发或活动事务?`业务预估,建立在完善的监控和稳定业务基础上` +- 如果应用程序的负载超过预计负载,吞吐量是否会下降到性能目标以下? + - 如果可以的话,它能低于绩效目标多久?或者,应用程序应在其最大容量或负载增加的情况下继续运行多长时间?`极限值的边界问题` +- 应用程序在不同负载级别下是否有可以消耗的最大 CPU 量,或者是否有预期的 CPU 量?如果 CPU 使用有上限,超出该限制还可以使用多少 CPU,允许使用多长时间? +- 如何评估应用程序的吞吐量? 吞吐量的计算在哪里进行? `APM系统` + +内存占用 + +- 应用程序中预期使用的内存量是否仅包含 Java 堆大小?或者这个总和是否也考虑了 JVM 或应用程序消耗的本机 RAM? + - 使用的 RAM 量如何计算? 该统计信息是否会考虑操作系统报告的 JVM 进程驻留内存大小?Java堆上当前数据的数量是否也包括在内?`RAM量严格来说包含 堆,堆外,元空间,二进制库` +- 有没有可能永远不会超过预期的内存消耗?如果有的话,可能会超过预期的内存消耗多少? 又可以超过多长时间呢?`取决于宿主机,通常不建议超限制运行,不利于容器调度` +- 何时评估内存使用情况? 是否会测量应用程序的空闲时间?当程序运行在稳定状态时? 负载何时达到峰值? `APM` + +************************ + +## Memory +- [Blog:java优化占用内存的方法(一)](http://blog.csdn.net/zheng0518/article/details/48182437) + +- [GC 性能优化 专栏](https://blog.csdn.net/column/details/14851.html) +- [Java调优经验谈](http://www.importnew.com/22336.html) + +- [Memory Footprint of A Java Process](https://zhuanlan.zhihu.com/p/158712025) + +> [Java OOM](/Java/AdvancedLearning/Tuning/JavaOOM.md) + +************************ + +## CPU + +> 问题:优化一个业务方法延迟,找出CPU成本高的点 +- Arthas trace 指定的方法 + - `偶现或者高并发时才出现怎么办` 考虑使用脚本将捕获的调用信息存入日志,在手动解析产生的大量日志统计分析 +- JMC,JProfiler,Visualvm 等工具捕获CPU火焰图 +- APM类监控系统。例如:CAT + +### 线程 +> [jstack.review Analyze java thread dumps](https://jstack.review) + +************************ + +## ClassLoader + +**加载错误的类** + +由于开源项目的 groupId artifactId 可能发生变化`asm netty commons-io 等`,且类结构和设计也有调整,容易引发隐式的类加载错误 + +> [【踩坑】 Maven中依赖的隐式冲突 可能导致的 NoClassDefFoundError NoSuchMethodException 等问题](https://blog.csdn.net/kcp606/article/details/92245936?spm=1001.2014.3001.5502) +> [使用easyexcel时遇到Could not initialize class cglib.beans.BeanMap怎么解决 ](https://mp.weixin.qq.com/s?__biz=MzAwMjk5NTY3Mw==&mid=2247483950&idx=1&sn=47c6c1fed54b134f46f6dedafd34db0c&chksm=9ac0a698adb72f8e769bcfbff5a4fb0450f181bb754a2ad615dc17002f14d7ec039c0e24a1d7&token=395785991&lang=zh_CN#rd) + +> 思路 +- `Maven Helper` IDE 插件检查依赖冲突 +- `lsof -p PID | grep jar` 项目启动后查看加载到进程的jar +- `-verbose:class` 输出运行期加载的class信息 + +************************ + +**类加载阻塞业务线程** + +由于类加载是JVM层面同步执行,如果业务行为中会高频用到类加载器的话会大大降低吞吐量,例如 [druid连接池引起的线程blocked](https://segmentfault.com/a/1190000041500544) + + diff --git a/Java/AdvancedLearning/Tuning/Practice.md b/Java/AdvancedLearning/Tuning/Practice.md new file mode 100644 index 0000000..3f8b330 --- /dev/null +++ b/Java/AdvancedLearning/Tuning/Practice.md @@ -0,0 +1,56 @@ +--- +title: Practice +date: 2024-03-06 14:10:23 +tags: +categories: +--- + +💠 + +- 1. [问题实践](#问题实践) + - 1.1. [IDEA调优](#idea调优) + - 1.2. [FD泄漏: Unable to Open Socket File](#fd泄漏-unable-to-open-socket-file) + - 1.2.1. [查找JVMSocket泄漏](#查找jvmsocket泄漏) + +💠 2024-04-12 17:19:59 +**************************************** +# 问题实践 +## IDEA调优 +```conf + -server + -Xms1700m # 最小堆 + -Xmx1700m # 最大堆 配成一样是为了避免扩容 + -XX:MetaspaceSize=350m # 只是一个阈值, 达到该阈值才进行 GC + -XX:MaxMetaspaceSize=350m # 最大值 + + -Xnoclassgc + -Xverify:none # 不进行字节码校验 + -XX:+AggressiveOpts # 激进式优化 + + -XX:ReservedCodeCacheSize=320m # IDEA JIT 缓存 +``` + +> [参考: Java’s -XX:+AggressiveOpts: Can it slow you down?](https://www.opsian.com/blog/aggressive-opts/) +> [参考: JVM参数MetaspaceSize的误解 ](https://mp.weixin.qq.com/s/jqfppqqd98DfAJHZhFbmxA?) + +************************ + +## FD泄漏: Unable to Open Socket File +> [jmap Error “Unable to Open Socket File”](https://www.baeldung.com/linux/jmap-unable-to-open-socket-file-heap-dump) +- 不是同用户及用户组 uid和gid +- 目标JVM不健康 +- 目标JVM使用了`-XX:+DisableAttachMechanism`JVM参数 +- 执行工具的JVM和目标JVM不是同一个版本(最好保持一致,如果版本相差过大,内存布局设计不一样,就会无法正常解析结果) +- /tmp 目录下无法创建命令使用的临时文件,或是来不及使用就被`systemd-tmpfiles`清理了 `/tmp/.java_pidXXX` + +### 查找JVMSocket泄漏 +- [一次由于网络套接字文件描述符泄露导致线上服务事故原因的排查经历](https://www.wangbo.im/posts/a-production-bug-leaking-sockets-fd-reproducing-practice/) +- `strace -t -T -f -p pid -e trace=network,close -o strace.out` + - 尝试找到创建socket并没有关闭socket的线程号, 然后进制转换后查看jstack找到线程持有栈关联到相关代码 + +- 处理过的案例: [Apache DolphinScheduler V1.3.6 channel 未关闭导致socket泄漏](https://github.com/apache/dolphinscheduler/blob/d21eb7b1809aa513ced920d5d08575502bde8911/dolphinscheduler-server/src/main/java/org/apache/dolphinscheduler/server/worker/processor/TaskCallbackService.java#L156) + - 单纯从服务器现场看只能看到worker对master建立了大量socket,而且fd的特殊性无法判断socket真实建立时间 + - 从worker和master的内存Dump入手,查看大量的socket(出问题时已4w+)会和哪些对象数量异常增多有关 + - 排查可能异常的对象(优先看Netty和Socket有关的对象),对比上下文代码(优先关注对象创建和销毁处代码),最终定位到泄漏对象为NettyRemoteChannel,以及上述泄漏点 + - 处理方式: remove前先关闭Channel + diff --git a/Java/AdvancedLearning/Tuning/Readme.md b/Java/AdvancedLearning/Tuning/Readme.md new file mode 120000 index 0000000..7ce2969 --- /dev/null +++ b/Java/AdvancedLearning/Tuning/Readme.md @@ -0,0 +1 @@ +JavaTroubleshoot.md \ No newline at end of file diff --git a/Java/AdvancedLearning/Tuning/img/mind.drawio.svg b/Java/AdvancedLearning/Tuning/img/mind.drawio.svg new file mode 100644 index 0000000..4d98004 --- /dev/null +++ b/Java/AdvancedLearning/Tuning/img/mind.drawio.svg @@ -0,0 +1,374 @@ + + + + + + + + +
+
+
+ 可用 +
+
+
+
+ + 可用 + +
+
+ + + + + +
+
+
+ 不 +
+ 可 +
+ 用 +
+
+
+
+ + 不可用... + +
+
+ + + + +
+
+
+ + + + + + + + + + + + + + + + + +
+
+
+
+ +
+
+ + + + + + + +
+
+
+ Dev +
+
+
+
+ + Dev + +
+
+ + + + + + + + + + + Linux 性能分析 + + + + + + + + + + + + +
+
+
+ CPU +
+
+
+
+ + CPU + +
+
+ + + + +
+
+
+ 内存或资源泄漏 +
+
+
+
+ + 内存或资源泄漏 + +
+
+ + + + +
+ +
+
+ + 网络分析 + +
+
+ + + + + +
+
+
+ 基础设施 +
+
+
+
+ + 基础设施 + +
+
+ + + + +
+
+
+ JVM +
+
+
+
+ + JVM + +
+
+ + + + +
+
+
+ 快速恢复 +
+
+
+
+ + 快速恢复 + +
+
+ + + + + +
+
+
+ 数据库 +
+
+
+
+ + 数据库 + +
+
+ + + + + +
+
+
+ 中间件 +
+
+
+
+ + 中间件 + +
+
+ + + + +
+
+
+ Redis +
+
+
+
+ + Redis + +
+
+ + + + +
+
+
+ MQ +
+
+
+
+ + MQ + +
+
+ + + + +
+
+
+ 慢SQL +
+
+
+
+ + 慢SQL + +
+
+ + + + +
+
+
+ 大事务 +
+
+
+
+ + 大事务 + +
+
+ + + + +
+
+
+ 基础设施层 +
+
+
+
+ + 基础设施层 + +
+
+ + + + +
+
+
+ JVM层:重启 +
+
+
+
+ + JVM层:重启 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/Java/AdvancedLearning/Web.md b/Java/AdvancedLearning/Web.md deleted file mode 100644 index 57cb0f4..0000000 --- a/Java/AdvancedLearning/Web.md +++ /dev/null @@ -1,83 +0,0 @@ -`目录 start` - -- [Java Web](#java-web) - - [【JSP/Servlet】](#jspservlet) - - [Servlet](#servlet) - - [JSP](#jsp) - - [九大内置对象](#九大内置对象) - - [四个作用域](#四个作用域) - - [Spring系](#spring系) - - [缓存](#缓存) - - [Tips](#tips) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Java Web -## 【JSP/Servlet】 - -### Servlet -### JSP -> [参考博客: JSP面试题及答案](http://www.cnblogs.com/iOS-mt/p/5717631.html) - -#### 九大内置对象 -``` - request 请求对象  类型 javax.servlet.ServletRequest 作用域 Request - response 响应对象 类型 javax.servlet.SrvletResponse 作用域 Page - pageContext 页面上下文对象 类型 javax.servlet.jsp.PageContext 作用域 Page -  session 会话对象 类型 javax.servlet.http.HttpSession 作用域 Session -  application 应用程序对象 类型 javax.servlet.ServletContext 作用域 Application -  out 输出对象 类型 javax.servlet.jsp.JspWriter 作用域 Page -  config 配置对象 类型 javax.servlet.ServletConfig 作用域 Page -  page 页面对象 类型 javax.lang.Object 作用域 Page -  exception 例外对象 类型 javax.lang.Throwable 作用域 page 来源: -``` -#### 四个作用域 -> [参考博客: JSP的四大作用域](http://www.cnblogs.com/featherfly/p/3513656.html) -``` -application 在所有应用程序中有效 -session 在当前会话中有效 -request 在当前请求中有效 -page 在当前页面有效 -``` -************************** -## Spring系 - -### 缓存 - -_如何做Etag缓存_ -1. 自定义了EtagCache注解 -2. 通过拦截器判断带EtagCache注解的Controller -3. 通过Spring Data Jpa自带的乐观锁 version, 针对每个资源就可以做到EtagCache -4. 将其值放在http的header中 -5. 还有另一种做法就是 自己针对内容进行hash code编码 - -**************************** -## Tips -- 1、JSP页面上的SQL标签以及EL标签是优先于文件头的那些JavaServlet语句运行的,所以要保证非法进入页面时重定向的问题 -- 2、如果想要获取异常来据此返回参数到页面弹窗提示,那么就要对一层层的方法调用,进行查找,所有的try catch 块 都要检查 - - 因为一般我的习惯就是把异常当场就处理了,而要实现这个要求就必须将异常层层上抛!!!! -* 3、中文乱码问题: - - **接收** - - 使用get方法,需要转换成gbk :`newString(s.getBytes("ISO-88511-1","gbk");` - - post方法需要转换成UTF-8 - - **回应** 均使用UTF-8 - -* 4、查询数据: 使用set集合,查询对象是否存在,使用contians -* 5、Servlet 是单例多线程的 -* 6、**eclipse中将java项目转成web项目** - * 经常在eclipse中导入web项目时,出现转不了项目类型的问题,导入后就是一个java项目,有过很多次经历,今天也有同事遇到类似问题,就把这个解决方法记下来吧,免得以后再到处去搜索。 - **解决步骤**: - -- 1、进入项目目录,可看到.project文件,打开。 -- 2、找到`...`代码段。 -- 3、在第2步的代码段中加入如下标签内容并保存: - -``` xml - org.eclipse.wst.common.project.facet.core.nature - org.eclipse.wst.common.modulecore.ModuleCoreNature - org.eclipse.jem.workbench.JavaEMFNature - -``` -- 4、在eclipse的项目上点右键,刷新项目。 -- 5、在项目上点右键,进入属性(properties) -- 6、在左侧列表项目中点击选择“Project Facets”,在右侧选择“Dynamic Web Module”和"Java",点击OK保存即可。 diff --git a/Java/AdvancedLearning/WebPerformance.md b/Java/AdvancedLearning/WebPerformance.md deleted file mode 100644 index d6f94ce..0000000 --- a/Java/AdvancedLearning/WebPerformance.md +++ /dev/null @@ -1,52 +0,0 @@ -`目录 start` - -- [Java性能调优](#java性能调优) -- [Web性能](#web性能) - - [测试工具](#测试工具) - - [压力测试](#压力测试) - - [ApacheBenchMark](#apachebenchmark) - - [Jmeter](#jmeter) - - [wrk](#wrk) - - [数据库性能](#数据库性能) - - [MySQL](#mysql) - - [主从复制以及读写分离](#主从复制以及读写分离) - -`目录 end` |_2018-08-05_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Java性能调优 -> [查看详情>>](/Java/JavaPerformance.md) - -# Web性能 - -## 性能基准 -> [参考博客: 系统吞吐量(TPS)、用户并发量、性能测试概念和公式](http://www.cnblogs.com/freeton/archive/2013/05/31/3109815.html) - -## 测试工具 -### 压力测试 - -#### ApacheBenchMark -> 简称 ab - -- 安装:`sudo apt install apache2-utils` -- 简单使用 `ab -c 并发数 -n 总请求数 URL` - - 查看文档:`man ab` 或 `ab -h` -- [ab](https://httpd.apache.org/docs/2.4/programs/ab.html) `apt安装这个包即可apache2-utils` - -#### Jmeter -> 具有图形化客户端 - -- [jmeter](http://jmeter.apache.org/download_jmeter.cgi) `apache 下的开源压测工具` - -#### wrk -> [Github地址](https://github.com/wg/wrk) -> [参考博客: wrk 压力测试 http benchmark POST接口](http://www.cnblogs.com/felixzh/p/8400729.html) -> [参考博客: 性能测试之-wrk(转)](http://www.cnblogs.com/rainy-shurun/p/5867946.html) - -1. 需要手动编译安装 make - -****************** -## 数据库性能 - -### MySQL -#### 主从复制以及读写分离 -- [读写分离博客](http://www.cnblogs.com/luckcs/articles/2543607.html) diff --git a/Java/AdvancedLearning/img/001-jvm-runtime-memory.drawio.svg b/Java/AdvancedLearning/img/001-jvm-runtime-memory.drawio.svg new file mode 100644 index 0000000..c8ea26a --- /dev/null +++ b/Java/AdvancedLearning/img/001-jvm-runtime-memory.drawio.svg @@ -0,0 +1,219 @@ + + + + + + + + +
+
+
+ 运行时数据区 +
+
+
+
+ + 运行时数据区 + +
+
+ + + + +
+
+
+ 方法区 +
+ Method Area +
+
+
+
+ + 方法区 Method Area + +
+
+ + + + +
+
+
+ 虚拟机栈 +
+ VM Stack +
+
+
+
+ + 虚拟机栈 VM Stack + +
+
+ + + + +
+
+
+ 本地方法栈 +
+ Native Method Stack +
+
+
+
+ + 本地方法栈 Native Method Stack + +
+
+ + + + +
+
+
+ 堆 +
+ Heap +
+
+
+
+ + 堆 Heap + +
+
+ + + + +
+
+
+ 程序计数器 +
+ Program Counter Register +
+
+
+
+ + 程序计数器Program Counter Regi... + +
+
+ + + + + + + + +
+
+
+ 执行引擎 +
+
+
+
+ + 执行引擎 + +
+
+ + + + + + + + +
+
+
+ 本地库接口 +
+
+
+
+ + 本地库接口 + +
+
+ + + + +
+
+
+ 本地方法库 +
+
+
+
+ + 本地方法库 + +
+
+ + + + +
+
+
+ 线程之间隔离的数据区 +
+
+
+
+ + 线程之间隔离的数据区 + +
+
+ + + + +
+
+
+ 所有线程共享的数据区 +
+
+
+
+ + 所有线程共享的数据区 + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/Java/AdvancedLearning/img/002-method-stack.drawio.svg b/Java/AdvancedLearning/img/002-method-stack.drawio.svg new file mode 100644 index 0000000..d416c7a --- /dev/null +++ b/Java/AdvancedLearning/img/002-method-stack.drawio.svg @@ -0,0 +1,296 @@ + + + + + + + + +
+
+
+ 栈帧4 +
+
+
+
+ + 栈帧4 + +
+
+ + + + +
+
+
+ + 栈帧3 + +
+
+
+
+ + 栈帧3 + +
+
+ + + + +
+
+
+ 栈帧2 +
+
+
+
+ + 栈帧2 + +
+
+ + + + +
+
+
+ + 栈帧1 + +
+
+
+
+ + 栈帧1 + +
+
+ + + + +
+
+
+ . +
+ . +
+ . +
+
+
+
+ + .... + +
+
+ + + + +
+
+
+ 函数4 +
+
+
+
+ + 函数4 + +
+
+ + + + + +
+
+
+ 调用 +
+
+
+
+ + 调用 + +
+
+ + + + + + +
+
+
+ 函数3 +
+
+
+
+ + 函数3 + +
+
+ + + + + +
+
+
+ 调用 +
+
+
+
+ + 调用 + +
+
+ + + + +
+
+
+ 函数2 +
+
+
+
+ + 函数2 + +
+
+ + + + + +
+
+
+ 调用 +
+
+
+
+ + 调用 + +
+
+ + + + +
+
+
+ 函数1 +
+
+
+
+ + 函数1 + +
+
+ + + + + + + + + + + + +
+
+
+ + 局部变量表 + +
+
+
+
+ + 局部变量表 + +
+
+ + + + +
+
+
+ 操作数栈 +
+
+
+
+ + 操作数栈 + +
+
+ + + + +
+
+
+ + 帧数据区 + +
+
+
+
+ + 帧数据区 + +
+
+ + +
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/Java/AdvancedLearning/img/003-jvm-generation-design.drawio.svg b/Java/AdvancedLearning/img/003-jvm-generation-design.drawio.svg new file mode 100644 index 0000000..4efa3e7 --- /dev/null +++ b/Java/AdvancedLearning/img/003-jvm-generation-design.drawio.svg @@ -0,0 +1,206 @@ + + + + + + + + +
+
+
+ Mete Space +
+
+ Java8 之前的版本 Perm Gen +
+
+
+
+ + Mete Space... + +
+
+ + + + + +
+
+
+ Old Gen +
+
+
+
+ + Old Gen + +
+
+ + + + +
+
+
+ Young Gen +
+
+
+
+ + Young Gen + +
+
+ + + + +
+
+
+ Eden Gen +
+
+
+
+ + Eden Gen + +
+
+ + + + +
+
+
+ Survivor 0 +
+
+
+
+ + Survivor 0 + +
+
+ + + + +
+
+
+ Survivor 1 +
+
+
+
+ + Survivor 1 + +
+
+ + + + +
+
+
+

+ 元空间 +

+

+ 默认 20M 左右,会无限制扩容 +

+
+
+
+
+ + 元空间 默认 20M 左右,会无限制扩容 + +
+
+ + + + +
+
+
+

+ 老年代 +

+

+ 发生的GC 为 Major GC +

+
+
+
+
+ + 老年代 发生的GC 为 Major GC + +
+
+ + + + +
+
+
+

+ 新年代 +

+

+ 发生的GC 为 Minor GC, 内存比默认为 8:1:1 +

+
+
+
+
+ + 新年代发生的GC 为 Minor GC, 内存比默认为 8:1:... + +
+
+ + + + + + + +
+
+
+ 堆 +
+
+
+
+ + 堆 + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/Java/AdvancedLearning/img/004-jvm-structure.drawio.svg b/Java/AdvancedLearning/img/004-jvm-structure.drawio.svg new file mode 100644 index 0000000..bc7d0e5 --- /dev/null +++ b/Java/AdvancedLearning/img/004-jvm-structure.drawio.svg @@ -0,0 +1,167 @@ + + + + + + + + +
+
+
+ 执行引擎 +
+
+
+
+ + 执行引擎 + +
+
+ + + + +
+
+
+ 垃圾回收系统 +
+
+
+
+ + 垃圾回收系统 + +
+
+ + + + +
+
+
+ 本地方法栈 +
+
+
+
+ + 本地方法栈 + +
+
+ + + + +
+
+
+ PC寄存器 +
+
+
+
+ + PC寄存器 + +
+
+ + + + +
+
+
+ Java栈 +
+
+
+
+ + Java栈 + +
+
+ + + + +
+
+
+ 方法区 +
+
+
+
+ + 方法区 + +
+
+ + + + +
+
+
+ Java堆 +
+
+
+
+ + Java堆 + +
+
+ + + + +
+
+
+ 直接内存 +
+
+
+
+ + 直接内存 + +
+
+ + + + +
+
+
+ 类加载子系统 +
+
+
+
+ + 类加载子系统 + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/Java/AdvancedLearning/img/005-java-main.km.svg b/Java/AdvancedLearning/img/005-java-main.km.svg new file mode 100644 index 0000000..f95c4bd --- /dev/null +++ b/Java/AdvancedLearning/img/005-java-main.km.svg @@ -0,0 +1 @@ +Java基础语法线程网络GUIWEB泛型反射类加载开发工具JVM多语言异常集合IOMavenGradleAnt构建工具GitSVNMercurialCVSPerForceVCSIDEAEclipseNetBeansVS CodeIDEGroovyScalaClojureKotlinJrubyJyphonCeylonArrayListLinkedListVectorList 接口TreeSetSortedSet 接口EnumSetHashSetSet 接口Deque接口PriorityQueueQueue 接口Collection 接口HashMapConcurrentHashMapHashTableIdentityMapWeakHashMapTreeMapNavigableMap 接口SortedMap 接口Map 接口OutOfMemoryErrorStactOverFlowErrorErrorNullPointerExceptionRuntimeExceptionIOExceptionSQLExceptionClassNotFoundExceptionExceptionThrowableInputStream 抽象类OutputStream 抽象类字节流Writer 抽象类Reader 抽象类字符流BootStrap ClassLoaderExtendsion ClassLoaderApp ClassLoaderCustom ClassLoader双亲委派模型基础使用子类型通配符父类型通配符无限定通配符和反射的结合使用JSP的语法基础EL表达式 & JSTLJSP内置对象使用Servlet 拦截器 过滤器Servlet/JSPFreemaker模板引擎RestFul前后端分离判断结构循环结构用户输入输出枚举 \ No newline at end of file diff --git a/Java/AlibabaJavaStandard.md b/Java/AlibabaJavaStandard.md index e6d71fc..6ffc359 100644 --- a/Java/AlibabaJavaStandard.md +++ b/Java/AlibabaJavaStandard.md @@ -1,694 +1,1117 @@ -`目录 start` - -- [阿里巴巴Java开发手册终极版](#阿里巴巴java开发手册终极版) -- [【编程规约】](#编程规约) - - [命名规约](#命名规约) - - [常量定义](#常量定义) - - [代码格式](#代码格式) - - [OOP规约](#oop规约) - - [集合处理](#集合处理) - - [并发处理](#并发处理) - - [控制语句](#控制语句) - - [注释规约](#注释规约) - - [其他](#其他) -- [【异常日志】](#异常日志) - - [异常处理](#异常处理) - - [日志规约](#日志规约) -- [MySQL规约](#mysql规约) - - [建表规约](#建表规约) - - [索引规约](#索引规约) - - [SQL规约](#sql规约) - - [ORM规约](#orm规约) -- [工程规约](#工程规约) - - [应用分层](#应用分层) - - [二方库规约](#二方库规约) - - [服务器规约](#服务器规约) -- [安全规约](#安全规约) - - [注意](#注意) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 阿里巴巴开发手册 +date: 2018-12-20 10:26:58 +tags: + - Manual +categories: + - Java +--- + +💠 + +- 1. [阿里巴巴Java开发手册](#阿里巴巴java开发手册) +- 2. [编程规约](#编程规约) + - 2.1. [命名规约](#命名规约) + - 2.2. [常量定义](#常量定义) + - 2.3. [代码格式](#代码格式) + - 2.4. [OOP规约](#oop规约) + - 2.5. [集合处理](#集合处理) + - 2.6. [并发处理](#并发处理) + - 2.7. [控制语句](#控制语句) + - 2.8. [注释规约](#注释规约) + - 2.9. [其他](#其他) +- 3. [异常日志](#异常日志) + - 3.1. [异常处理](#异常处理) + - 3.2. [日志规约](#日志规约) +- 4. [单元测试](#单元测试) +- 5. [安全规约](#安全规约) +- 6. [MySQL规约](#mysql规约) + - 6.1. [建表规约](#建表规约) + - 6.2. [索引规约](#索引规约) + - 6.3. [SQL规约](#sql规约) + - 6.4. [ORM映射](#orm映射) +- 7. [工程规约](#工程规约) + - 7.1. [应用分层](#应用分层) + - 7.2. [二方库依赖](#二方库依赖) + - 7.3. [服务器规约](#服务器规约) +- 8. [专有名词](#专有名词) + +💠 2024-06-18 15:17:36 **************************************** -# 阿里巴巴Java开发手册终极版 +# 阿里巴巴Java开发手册 +> [Github: p3c](https://github.com/alibaba/p3c)`含该手册PDF GitBook等版本` | [《阿里巴巴Java开发手册》详尽](https://yq.aliyun.com/articles/656256?spm=a2c4e.11155472.0.0.7ba85338l1Ef0N) +> [《唯品会Java开发手册》](https://vipshop.github.io/vjtools/#/standard/)`阿里手册的补充` > [FindBugs、PMD和CheckStyle对比](https://blog.csdn.net/ml5271169588/article/details/6975701) -# 【编程规约】 -********************************************* +# 编程规约 ## 命名规约 **`强制`** -1. 所有命名不能以`美元符和下划线`开始和结尾 -1. 禁止拼音混合英文,更不允许直接使用中文的方式。 -1. 类用UserDao `UpperCamelCase风格` 但以下情形例外: DO / BO / DTO / VO / AO 例如:`UserDO` -1. 方法,变量,参数用userDao `lowerCamelCase风格` -1. 常量名全部大写,下划线隔开,力求语义表达完整清楚,不要嫌名字长(枚举类中的也是)。 -1. 抽象类使用`Abstract或者Base`开头,异常类使用Exception结尾 +1. 所有命名不能以`美元符和下划线`开始和结束 + +1. 禁止拼音混合英文, 更不允许直接使用中文的方式。 + - 正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免采用。 + +1. 类名用`UpperCamelCase风格` 但以下情形例外: DO / BO / DTO / VO / AO / PO / UID 例如: `UserDO` + +1. 方法名、参数名、成员变量、局部变量 都统一使用 `lowerCamelCase风格` + +1. 常量名全部大写, 下划线隔开, 力求语义表达完整清楚, 不要嫌名字长(枚举类中的也是)。 + +1. 抽象类使用`Abstract或者Base`开头, 异常类使用Exception结尾 测试类命名以它要测试的类的名称开始,以Test结尾 + 1. 数组定义 `String[] name` 而不是 `String name[]` -1. POJO 类中布尔类型的变量不能is开头,否则部分框架解析会引起序列化错误。 -1. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。 - - 正例: 应用工具类包名为 com.alibaba.open.util、类名为 MessageUtils( 此规则参考spring 的框架结构) -1. 杜绝完全不规范的缩写, 避免望文不知义。 - - 反例: AbstractClass“ 缩写” 命名成 AbsClass; condition“ 缩写” 命名成 condi,此类随意缩写严重降低了代码的可阅读性。 + +1. POJO 类中布尔类型的变量不能is开头, 否则部分框架解析会引起序列化错误。 + +1. 包名统一使用小写, 点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式, 但是类名如果有复数含义, 类名可以使用复数形式。 + - 正例: 应用工具类包名为 com.alibaba.open.util、类名为 MessageUtils( 此规则参考spring 的框架结构) + +1. 杜绝完全不规范的缩写, 避免望文不知义。 + - 反例: AbstractClass“ 缩写” 命名成 AbsClass; condition“ 缩写” 命名成 condi, 此类随意缩写严重降低了代码的可阅读性。 **`推荐`** -- 为了达到代码自解释的目标,任何自定义编程元素在命名时,使用尽量完整的单词组合来表达其意。 - - 正例: 从远程仓库拉取代码的类命名为 PullCodeFromRemoteRepository。 -- 如果使用到了设计模式,建议在类名中体现出具体模式。将设计模式体现在名字中,有利于阅读者快速理解架构设计理念`LoginProxy` -- 接口类中的方法和属性不要加任何修饰符号( public 也不要加) ,保持代码的简洁性,并加上有效的 Javadoc 注释。 - - 尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。 +1. 为了达到代码自解释的目标, 任何自定义编程元素在命名时, 使用尽量完整的单词组合来表达其意。 + - 正例: 从远程仓库拉取代码的类命名为 PullCodeFromRemoteRepository。 + +1. 如果使用到了设计模式, 建议在类名中体现出具体模式。将设计模式体现在名字中, 有利于阅读者快速理解架构设计理念`LoginProxy` + +1. 接口类中的方法和属性不要加任何修饰符号( public 也不要加) , 保持代码的简洁性, 并加上有效的 Javadoc 注释。 + - 接口内的常量是与接口方法相关, 并且是整个应用的基础常量。 **`接口和实现类的命名有两套规则`** -- `强制` 对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。 - - 正例: CacheServiceImpl 实现 CacheService 接口。 -- `推荐` 如果是形容能力的接口名称,取对应的形容词做接口名 ( 通常是–able 的形式)。 - - 正例: AbstractTranslator 实现 Translatable。 + +1. `强制` 对于 Service 和 DAO 类, 基于 SOA 的理念, 暴露出来的服务一定是接口, 内部的实现类用 Impl 的后缀与接口区别。 + - 正例: CacheServiceImpl 实现 CacheService 接口。 + +1. `推荐` 如果是形容能力的接口名称, 取对应的形容词做接口名 ( 通常是–able 的形式)。 + - 正例: AbstractTranslator 实现 Translatable。 **`参考`** -- 枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。 - - 说明: 枚举其实就是特殊的常量类,且构造方法被默认强制是私有。 - - 正例: 枚举名字为 `ProcessStatusEnum` 的成员名称: `SUCCESS`或者`UNKOWN_REASON` -- MVC各层命名规约 + +1. 枚举类名建议带上 Enum 后缀, 枚举成员名称需要全大写, 单词间用下划线隔开。 + - 说明: 枚举其实就是特殊的常量类, 域成员均为常量, 且构造方法被默认强制是私有。 + - 正例: 枚举名字为 `ProcessStatusEnum` 的成员名称: `SUCCESS`或者`UNKOWN_REASON` + +1. MVC各层命名规约 - Service/Dao层 - 获取单个对象 get 做前缀 - - 获取多个对象 list + - 获取多个对象 list, 复数形式结尾 - 获取统计值的方法 count - 插入 save - 删除 remove - 修改 update - 领域模型 - - 数据对象 ***DO*** 是数据表名 - - 数据传输对象 ***DTO*** 是业务领域相关的名称 - - 展示对象 ***VO*** 是网页名称 使用了模板框架 - - POJO是 ***DO DTO BO VO*** 统称 - - 有关这些缩写的详细说明 [相关](/Java/AdvancedLearning/GrammarAndType.md#pojo) - -**`Tomcat组织推荐的代码风格`** -- 使用空格进行缩进,而不是制表符 -- 用于 Java 源的100个字符行宽度,用于文档源(.txt,.xml)的80个字符行宽度 -- Java 源代码:{在行末,4个空格缩进 -- XML 源文件:2个空格缩进 + - 数据对象 ***xxxDO*** xxx是数据表名 + - 数据传输对象 ***xxxDTO*** xxx是业务领域相关的名称 + - 展示对象 ***xxxVO*** xxx是网页名称 使用了模板框架 + - POJO是 ***DO DTO BO VO*** 统称, 禁止命名为 xxxPOJO + - 有关这些缩写的详细说明 [详细定义和解释](/Java/AdvancedLearning/JavaBasicSyntax.md#object) + + +1. **`Tomcat组织推荐的代码风格`** + 1. 使用空格进行缩进, 而不是制表符 + 1. 用于 Java 源的100个字符行宽度, 用于文档源(.txt, .xml)的80个字符行宽度 + 1. Java 源代码: {在行末, 4个空格缩进 + 1. XML 源文件: 2个空格缩进 + +> 引申思考 +- 由于组织变更,开发者变动等因素引起包的package和类名变动通常难以避免,例如MySQL,Netty,Dubbo,Nacos。但是越底层的包,影响范围会很大,很曲折(难以直接意识到是该变化引起的问题,例如MySQL驱动类名变更导致druid大量线程频繁Block问题) +- 所以良好的命名能大大降低改名引起的兼容风险 `因为不可能知道上层应用是怎么使用你的库的,是否遵守良好的开发规范和良好的设计` *************************************************** + ## 常量定义 -- 不允许出现魔法值(未经定义的常量)直接出现 - - 常量类存放常量要分门别类的放置 - - 缓存相关常量放在类 `CacheConsts` 下; 系统配置相关常量放在类 `ConfigConsts` 下 -- 常量的复用层次的安排 - - *跨应用共享常量* :二方库中 通常是client.jar中的`constant`目录下 - - *应用内共享常量* :一方库的modules中的`constant`目录下 - - *子工程内共享常量* :当前子工程的`constant`目录下 - - *包内共享常量*:当前包下单独的`constant`目录下 - - *类内共享常量*:直接在类内部 `private static final` 定义 +**`强制`** -``` -易懂变量也要统一定义成应用内共享常量,两位攻城师在两个类中分别定义了表示 “是”的变量: - 类 A 中: public static final String YES = "yes"; - 类 B 中: public static final String YES = "y"; - A.YES.equals(B.YES),预期是 true,但实际返回为 false,导致线上问题。 -``` -- `推荐` - - 如果变量值仅在一个范围内变化,且带有名称之外的延伸属性, 定义为枚举类。下面正例中的数字就是延伸信息,表示星期几。 +1. 不允许出现魔法值(未经定义的常量)直接出现 + +1. long 或者 Long 初始赋值时, 使用大写的 L , 不能是小写的 l, 小写容易跟数字 1 混淆, 造成误解 + + +1. 不要使用一个常量类维护所有的常量, 应该按常量的功能, 进行归类, 分开维护. + - 大而全的常量类, 只能用搜索 才能快速定位, 不利于理解和维护 + - 例如 缓存相关常量放在类 `CacheConsts` 下; 系统配置相关常量放在类 `ConfigConsts` 下 + + +1. 常量的复用层次的安排 + - *跨应用共享常量* : 二方库中 通常是client.jar中的`constant`目录下 + - *应用内共享常量* : 一方库的modules中的`constant`目录下 + - 易懂变量也要统一定义成 应用内共享变量, 比如两个开发者在两个模块定义了相同含义的常量, 但是值却不一样, 这样就很容易埋下隐患 + - *子工程内共享常量* : 当前子工程的`constant`目录下 + - *包内共享常量*: 当前包下单独的`constant`目录下 + - *类内共享常量*: 直接在类内部 `private static final` 定义 + +**`推荐`** + +1. 如果变量值仅在一个范围内变化, 且带有名称之外的延伸属性, 定义为枚举类。下面正例中的数字就是延伸信息, 表示星期几。 - `public Enum { MONDAY(1), TUESDAY(2), WEDNESDAY(3), THURSDAY(4), FRIDAY(5), SATURDAY(6),SUNDAY(7);}` +1. 枚举类的定义 不能直接使用枚举的 ordinal() 作为枚举常量的值在程序中使用, 而应该自己定义一个变量, ordinal 方法的Javadoc上也有说明 + - Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap + ************************************************* + ## 代码格式 -1. 大括号约定: - - 如果是大括号内为空,则简洁地写成{}即可,不需要换行; - - 如果是非空代码块则: - - 左大括号前不换行。 - - 左大括号后换行。 - - 右大括号前换行。 - - 右大括号后还有 else 等代码则不换行; 表示终止的右大括号后必须换行。 - -1. 小括号和字符之间不出现空格; - - 反例: `if (空格 a == b 空格)` -1. `if/for/while/switch/do` 后加空格: 例如 :if () +**`强制`** +1. 大括号约定: + - 如果是大括号内为空, 则简洁地写成{}即可, 不需要换行; + - 如果是非空代码块则: + - 左大括号前不换行, 左大括号后换行。 + - 右大括号前换行, 右大括号后还有 else 等代码则不换行; 表示终止的右大括号后必须换行。 + +1. 小括号和字符之间不出现空格; + - 反例: `if (空格 a == b 空格)` + +1. `if/for/while/switch/do`等保留字与括号之间必须加空格. + 1. 任何二目、 三目运算符的左右两边都需要加一个空格。 -1. 缩进采用4个空格,而不是tab字符,IDE要调整一下 + +1. 缩进采用4个空格(Google的风格是两个空格), 而不是tab字符, 对应的IDE要调整一下 + 1. 注释的双斜线与注释内容之间有且仅有一个空格。 `// 注释` -1. 单行字符不超过120个,超出需换行: - - 第二行比第一行缩进4个空格,第三行以后就和第二行平齐就可以了 + +1. 单行字符不超过120个, 超出需换行: + - 第二行比第一行缩进4个空格, 第三行以后就和第二行平齐就可以了 - 运算符与下文一起换行 - 方法调用的点符号与下文一起换行 - - 调用方法 多个参数,需在逗号后进行换行 + - 调用方法 多个参数, 需在逗号后进行换行 - 括号之前不要换行 -1. 方法参数在定义和传入时,多个参数逗号后边必须加空格 - - `method("a", "b", "c");` -1. 编码统一采用`UTF-8` IDE中换行符采用unix格式`使用别的编码的话会开心死的` -1. 没有必要增加若干空格来使某一行的字符与上一行对应位置的字符对齐。 - ``` - int a = 3; - long b = 4L; - float c = 5F; - StringBuffer sb = new StringBuffer(); - 增加 sb 这个变量,如果需要对齐,则给 a、 b、 c 都要增加几个空格,在变量比较多的情况下,是一种累赘的事情。 - ``` -1. 方法体内执行语句组,变量的定义语句组,不同的业务逻辑之间或者不同的语义之间插入一个空行,相同业务逻辑和语义之间不需要插入空行 +1. 方法参数在定义和传入时, 多个参数逗号后边必须加空格 : `method("a", "b", "c");` + +1. 文件编码统一采用`UTF-8` IDE中换行符采用Unix格式(LF) `使用别的编码的话会开心死的` + +**`推荐`** + +1. 方法体内执行语句组, 变量的定义语句组, 不同的业务逻辑之间或者不同的语义之间插入一个空行, 相同业务逻辑和语义之间不需要插入空行 + - 也没必要插入多个空行进行分隔 + +1. 没有必要增加若干空格来使某一行的字符与上一行对应位置的字符对齐。(很傻的做法) + +```java + public int a = 1; + public String b = 2; + public float c = 2.0; +``` ********************************************* ## OOP规约 -1. 避免使用对象来引用类的静态变量或方法。无谓增加编译器解析成本,直接用类名来访问即可。 -1. 所有的覆写方法,必须加注解 @Override -1. 相同参数类型,相同业务含义,才可以使用Java的可变参数。避免使用Object - - 说明: 可变参数必须放置在参数列表的最后。 ( 提倡同学们尽量不用可变参数编程) - - 正例: `public User getUsers(String type, Integer... ids) {...}` -1. 外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。 - - 接口过时必须加 `@Deprecated` 注解,并清晰地说明采用的新接口或者新服务是什么。 +**`强制`** +1. 避免使用一个类的对象来引用该类的静态变量或静态方法。无谓增加编译器解析成本, 直接用`类名`来访问即可。 + +1. 所有的覆写方法, 必须加 @Override 注解 + - 例如 getObject() 与 get0bject() 的问题, 前者是字母o,后者是数字0 通过使用覆盖的注解, 可以准确判断是否覆盖成功 + - 另外, 如果在抽象类中对方法签名进行修改, 其实现类 会立即编译报错 + +1. 相同参数类型, 相同业务含义, 才可以使用Java的可变参数, 避免使用Object. + - 说明: 可变参数必须放置在参数列表的最后。 `提倡尽量不用可变参数编程` + - 正例: `public User getUsers(String type, Integer... ids) {...}` + +1. 外部正在调用或者二方库依赖的接口, 不允许修改方法签名, 避免对接口调用方产生影响。 + - 接口过时必须加 `@Deprecated` 注解, 并清晰地说明采用的新接口或者新服务是什么。 + 1. 不能使用过时的类或方法 - - 说明: `java.net.URLDecoder` 中的方法 `decode(String encodeStr)` 这个方法已经过时, - - 应该使用双参数 `decode(String source, String encode)`。接口提供方既然明确是过时接口, - - 那么有义务同时提供新的接口; 作为调用方来说,有义务去考证过时方法的新实现是什么。 -1. Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。 + - 说明: `java.net.URLDecoder` 中的方法 `decode(String encodeStr)` 这个方法已经过时, + - 应该使用双参数 `decode(String source, String encode)`。接口提供方既然明确是过时接口, + - 那么有义务同时提供新的接口; 作为调用方来说, 有义务去考证过时方法的新实现是什么。 + +1. Object 的 equals 方法容易抛空指针异常, 应使用常量或确定有值的对象来调用equals。 - 使用` "t".equals(test)`方式 - - 推荐使用`java.util.Object#equals` (jdk7引入的工具类) -1. 所有的相同类型的包装类对象之间的 *值的比较* ,全部使用equals方法比较, - - 注意:对于 `Integer var = ?` 在-128 至 127 范围内的赋值, Integer 对象是在`IntegerCache.cache 产生`,会复用已有对象, - - 这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,所以 `==` 就会失效 - - 这是一个大坑!,推荐统一使用 equals 方法进行判断。 -1. 关于基本数据类型与包装数据类型的使用标准如下: + - 推荐使用`java.util.Object.equals` (jdk7引入的工具类) + +1. 所有的相同类型的包装类对象之间的 *值的比较* , 全部使用equals方法比较, + - 注意: 对于 `Integer var = ?` 在 -128 至 127 范围内的赋值, Integer 对象是在`IntegerCache.cache 产生`, 会复用已有对象, + - 这个区间内的 Integer 值可以直接使用 == 进行判断, 但是这个区间之外的所有数据, 都会在堆上产生, 并不会复用已有对象, 所以 `==` 就会失效 + - 这是一个大坑!, **推荐统一使用 equals 方法进行判断** + +1. 关于基本数据类型与包装数据类型的使用标准如下: - 所有的 POJO 类属性必须使用包装数据类型。 - - RPC方法的返回值和参数必须使用包装数据类型 + - RPC 方法的 返回值 和 参数 必须使用包装数据类型 - 所有的局部变量 推荐使用基本数据类型 - - `说明`: POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何NPE 问题,或者入库检查,都由使用者来保证。 - - `正例`: 数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。 - - `反例`: 比如显示成交总额涨跌情况,即正负 x%, x 为基本数据类型,调用的 RPC 服务,调用 - - 不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线。所以包装 - - 数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。 -1. 定义 `DO/DTO/VO`等POJO类时,不要设定任何属性的`默认值` - - `反例`: POJO 类的 gmtCreate 默认值为 new Date(); - - 但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。 -1. 序列化类新增属性时,不要修改`serialVersionUID`字段,避免反序列化失败 - - 如果要完全不兼容升级,为了避免反序列化混乱,就可以修改`serialVersionUID`的值 - - idea可以配置使用快捷键自动生成 - - `说明`: 注意 serialVersionUID 不一致会抛出序列化运行时异常。 -1. 构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。 -1. POJO 类必须写 toString 方法。 如果继承了另一个 POJO 类,注意在前面加一下 super.toString()。 - - 说明: 在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题 -1. 使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛 `IndexOutOfBoundsException` 的风险。 - - `System.out.println("a,b,c,,".split(",").length);` 预期是大于3 -1. 当一个类有多个构造方法,或者多个重名方法,这些方法应该按顺序放置在一起,优于下条规则 -1. 类内方法的定义顺序依次是 共有方法或保护方法 -> 私有方法 -> setter/getter方法 - - 公有方法是类的调用者和维护者最关心的方法,首屏展示最好; - - 保护方法虽然只是子类关心,也可能是“模板设计模式”下的核心方法; - - 而私有方法外部一般不需要特别关心,是黑盒实现; - - 因为承载的信息价值较低,所有 Service 和 DAO 的 `getter/setter` 方法放在类体最后。 -1. setter 方法中,参数名称与类成员变量名称一致, this.成员名 = 参数名。 - - 在`getter/setter` 方法中, 不要增加业务逻辑,增加排查问题的难度。 -1. 循环体中的字符串的连接方式,使用`StringBuffer`的`append`方法进行扩展 - - 说明: 反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行append 操作, - - 最后通过 toString 方法返回 String 对象,造成内存资源浪费。 -1. final 可以声明类、成员变量、方法、以及本地变量,下列情况使用 final 关键字: - - 不允许被继承的类,如: String 类。 - - 不允许修改引用的域对象,如: POJO 类的域变量。 - - 不允许被重写的方法,如: POJO 类的 setter 方法。 - - 不允许运行过程中重新赋值的局部变量。 - - 避免上下文重复使用一个变量,使用 final 描述可以强制重新定义一个变量,方便更好地进行重构。 - - 方法入参:对象参数前加final,表示不允许修改引用的指向 -1. 慎用Object的clone方法来拷贝对象 - - 说明: 对象的 clone 方法默认是浅拷贝,最好重写该方法,实现属性对象的拷贝。 -1. 类成员与方法访问控制从严: - - 如果不允许外部直接通过new来创建对象,那么构造方法显式声明并private - - ` 工具类`不允许有public或default构造方法 - - 类非static成员变量并且与子类共享,必须是protected - - 类非static成员变量并且仅在本类中使用,必须是private - - 若是static成员变量,必须考虑是否final - - 类static 成员变量如果仅在本类使用,必须是 private。 - - 类成员方法只供类内部调用,必须是private - - 类成员方法只对继承类公开,那么限制为protected - - **注意** 说明: 任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。 - - 思考:如果是一个 private 的方法,想删除就删除,可是一个 public 的 service 方法,或者一个 public 的成员变量,删除一下,不得手心冒点汗吗? - - 变量像自己的小孩,尽量在自己的视线内,变量作用域太大, 无限制的到处跑,那么你会担心的。 + - `说明`: POJO 类属性没有初值是提醒使用者在需要使用时, 必须自己显式地进行赋值, 任何NPE 问题, 或者入库检查, 都由使用者来保证。 + - 数据库的查询结果可能是 null, 因为自动拆箱, 用基本数据类型接收有 NPE 风险。 + - `反例`: 比如显示成交总额涨跌情况, 即正负 x%, x 为基本数据类型, 调用的 RPC 服务, 调用不成功时, 返回的是默认值, + - 页面显示为 0%, 这是不合理的, 应该显示成中划线。所以包装数据类型的 null 值, 能够表示额外的信息 + - 如: 远程调用失败, 异常退出。 + +1. 定义 `DO/DTO/VO`等POJO类时, 不要给任何成员属性设定 **默认值** + - `反例`: POJO 类的 createTime属性 默认值为 new Date(); + - 但是这个属性在数据提取时并没有置入具体值, 在更新其它字段时又附带更新了此字段, 导致创建时间被修改成当前时间。 + +1. 序列化类新增属性时, 不要修改`serialVersionUID`字段, 避免反序列化失败 + - 如果完全不兼容升级, 为了避免反序列化混乱, 就需要修改`serialVersionUID`的值 `(idea可以配置使用快捷键自动生成)` + - `说明`: 当 serialVersionUID 不一致会抛出序列化运行时异常。 + +1. 构造方法里面禁止加入任何业务逻辑, 如果有初始化逻辑, 请放在 init 方法中。 + +1. POJO 类必须写 toString 方法。 如果继承了另一个 POJO 类, 注意在前面加一下 super.toString() `lombok解救众生` + - 说明: 在方法执行抛出异常时, 可以直接调用 POJO 的 toString()方法打印其属性值, 便于排查问题 + +**`推荐`** +1. 使用索引访问用 String 的 split 方法得到的数组时, 需做最后一个分隔符后有无内容的检查, 否则会有 `IndexOutOfBoundsException` 的风险。 + - `System.out.println("a,b,c,,".split(",").length);` 预期是大于3,结果却是3 + +1. 当一个类有多个构造方法, 或者多个同名方法, 这些方法应该按顺序放置在一起, 优于下条规则 + +1. 类内方法的定义顺序依次是 共有方法或保护方法 -> 私有方法 -> setter/getter方法(lombok可省) + - 公有方法是类的调用者和维护者最关心的方法, 首屏展示最好; + - 保护方法虽然只是子类关心, 也可能是 **模板设计模式** 下的核心方法; + - 而私有方法外部一般不需要特别关心, 是一个黑盒实现; + - 因为承载的信息价值较低, 所有 Service 和 DAO 的 `getter/setter` 方法放在类的最后, 用了lombok就省去了。 + +1. setter 方法中, 参数名称与类成员变量名称一致, this.成员名 = 参数名。 + - 在`getter/setter` 方法中, 不要增加业务逻辑, 增加排查问题的难度。 + +1. 循环体中的字符串的连接方式, 使用`StringBuffer`的`append`方法进行扩展 + - 说明: 反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象, 然后进行 append 操作, + - 最后通过 toString 方法返回 String 对象, 造成内存资源浪费。 + + > 验证循环中String的拼接 + ```java + public class test{ + public void test(){ + String target = "1"; + for(int a=0; a<10; a++){ + target += a; + } + } + } + // 1. javac test.java 编译 + // 2. javap -c -l test 反编译, 就能大致看到new StringBuilder了 + + ``` + +1. final 可以声明类、成员变量、方法、以及本地变量, 下列情况使用 final 关键字: + 1. 不允许被继承的类, 如: String 类。 + 1. 不允许修改引用的域对象, 如: POJO 类的域变量。 + 1. 不允许被重写的方法, 如: POJO 类的 setter 方法。 + 1. 不允许运行过程中重新赋值的局部变量。 + 1. 避免上下文重复使用一个变量, 使用 final 描述可以强制重新定义一个变量, 方便更好地进行重构。 + - 这里就隐含了一个习惯, 不应该把一个变量到处传, 到处用,赋值, 很难追踪调试 + +1. 慎用Object的clone方法来拷贝对象 `详见API` + - 说明: 对象的 clone 方法默认是浅拷贝, 最好重写该方法, 实现属性对象的拷贝。 + - 例如[使用JDK序列化实现深拷贝](/Java/AdvancedLearning/JavaSerialize.md#序列化和反序列化),或者用JSON序列化的方式 + +1. 类成员与方法访问控制从严: + 1. 如果不允许外部直接通过 new 来创建对象, 那么构造方法显式声明并 private + 1. **工具类** 不允许有 public 或 default 的构造方法 + 1. 类非static成员变量 或者 成员方法 若 **只与** 子类共享, 必须是 protected + 1. 成员属性或方法 若 **仅** 本类中使用, 必须是 private + 1. 若是static成员变量, 必须考虑是否final + 1. 类static 成员变量如果仅在本类使用, 必须是 private。 + - **注意** 说明: 任何类、方法、参数、变量, 都需要严控访问范围。过于宽泛的访问范围, 不利于模块解耦。 + - 思考: 如果是一个 private 的方法, 想删除就删除, 可是一个 public 的 service 方法, 或者一个 public 的成员变量, 删除一下, 不得手心冒点汗吗? + - 变量像自己的小孩, 尽量在自己的视线内, 变量作用域太大, 无限制的到处跑, 那么你会担心的。 ********************************************************** + ## 集合处理 -1. 关于HashCode 和equals的处理 - - 只要重写equals,就必须重写HashCode - - 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的方法必须重写这两个方法 - - 如果自定义对象作为Map的键,那么必须重写HashCode和equals - - `说明`: String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用。 -1. ArrayList的subList 结果不可强转成ArrayList 否则会抛出 ClassCastException异常, +**`强制`** +1. 关于 hashCode 和 equals 的处理, 遵循如下规则: + - 只要重写 equals, 就必须重写 hashCode + - 因为 Set 存储的是不重复的对象, 依据 hashCode 和 equals 进行判断, 所以 Set 存储的方法必须重写这两个方法 + - 如果自定义对象作为 Map 的键, 那么必须重写 hashCode 和 equals + - `说明`: String 重写了 hashCode 和 equals 方法, 所以我们可以非常愉快地使用 String 对象作为 key 来使用。 + +1. ArrayList 的 subList 结果不可强转成 ArrayList 否则会抛出 ClassCastException异常, - 即 `java.util.RandomAccessSubList cannot be cast to java.util.ArrayList.` - - `说明`:subList返回的是ArrayList的内部类SubList,是ArrayList的一个视图,对于subList的所有操作最终都会反映到原列表上 -1. 在 `subList` 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生 `ConcurrentModificationException` 异常。 -1. 使用集合转数组的方法,必须使用集合的 `toArray(T[] array)` ,传入的是类型完全一样的数组,大小就是 `list.size() ` - - 使用 toArray 带参方法,入参分配的数组空间不够大时, toArray 方法内部将重新分配内存空间,并返回新数组地址; - - 如果数组元素大于实际所需,下标为[ list.size() ]的数组元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。 - - `String[] array = new String[list.size()];` - - `array = list.toArray(array);` - - *注意*直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误。 -1. 把数组转换成集合:使用工具类`Arrays.asList()`时,不能使用其修改集合相关的方法,其`add/remove/clear`方法会抛出`UnsupportedOperationException`异常。 - - *说明*:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。`Arrays.asList`体现的是适配器模式,只是转换接口,后台的数据仍是数组。 - - `String[] str = new String[] { "a", "b" };` - - `List list = Arrays.asList(str);` - - *第一种情况*: `list.add("c");` 运行时异常。 - - *第二种情况*: `str[0]= "gujin";` 那么 `list.get(0)` 也会随之修改。 -1. 泛型通配符``来接收返回的数据,此写法的泛型集合不能使用 add 方法。 - - 而``不能使用 get 方法,做为接口调用赋值时易出错。 - - *说明*: 扩展说一下 `PECS(Producer Extends Consumer Super)`原则: - - 第一、 频繁往外读取内容的,适合用``。 - - 第二、 经常往里插入的,适合用``。 - - 说明:苹果装箱后返回一个``对象,此对象就不能往里加任何水果,包括苹果。 -1. 不要在 foreach 循环里进行元素的 `remove/add` 操作。 remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。 - -*正例:* -```java - Iterator iterator = list.iterator(); - while (iterator.hasNext()) { - String item = iterator.next(); - if (删除元素的条件) { - iterator.remove(); + - `说明`: subList返回的是 ArrayList 的内部类 SubList, 并不是ArrayList, 而是ArrayList的一个视图, 对于SubList的所有操作最终都会反映到原列表上 + +1. 在 `subList` 场景中, 高度注意对原集合元素 **个数的修改**, 会导致子列表的 **遍历、增加、删除** 均会产生 `ConcurrentModificationException` + +1. 使用集合转数组的方法, 必须使用集合的 `toArray(T[] array)` , 传入的是类型完全一样的数组, 数组大小就是 `list.size() ` + - 使用 toArray 带参方法, 入参分配的数组空间不够大时, toArray 方法内部将重新分配内存空间, 并返回新数组地址, 原数组不做更改; + - 如果数组元素大于实际所需, 下标超出的数组元素将被置为 null, 其它数组元素保持原值, 因此最好将方法入参数组大小定义与集合元素个数一致。 + - *注意* 直接使用 toArray 无参方法存在问题, 此方法返回值只能是 Object[]类, 若强转其它类型数组将出现 ClassCastException 错误。 + +1. 把数组转换成集合: 使用工具类`Arrays.asList()`时, 不能使用其修改集合相关的方法, 其`add/remove/clear`方法会抛出`UnsupportedOperationException`异常。 + - 因为 asList 的返回对象是一个继承于 AbstractList 的内部类 Arrays, 实现集合的那些修改方法时 都是直接抛出异常 + - `Arrays.asList`体现的是适配器模式, 只是转换接口, 后台的数据仍是固定长度的数组。 + - 案例: `String[] str = new String[] { "a", "b" }; List list = Arrays.asList(str);` + - *第一种情况*: `list.add("c");` 运行时异常。 + - *第二种情况*: `str[0]= "gujin";` 那么 `list.get(0)` 也会随之修改。 + +1. 泛型通配符``来接收返回的数据, 此写法的泛型集合不能使用 add 方法。 + - 而``不能使用 get 方法, 做为接口调用赋值时易出错。 + - *说明*: 扩展说一下 `PECS(Producer Extends Consumer Super)`原则: + - 第一、 频繁往外读取内容的, 适合用``。 + - 第二、 经常往里插入的, 适合用``。 + - 说明: 苹果装箱后返回一个``对象, 此对象就不能往里加任何水果, 包括苹果。 + +1. 不要在 foreach 循环里进行元素的 `remove/add` 操作。 remove 元素请使用 Iterator方式, 如果并发操作, 需要对 Iterator 对象加锁。 + + *正例: * + ```java + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { + String item = iterator.next(); + if (删除元素的条件) { + iterator.remove(); + } } - } -``` -*反例:* -```java - List a = new ArrayList(); - list.add("1"); - list.add("2"); - for (String item : list) { - if ("1".equals(item)) { - list.remove(item); + ``` + *反例: * + ```java + List a = new ArrayList(); + list.add("1"); + list.add("2"); + for (String item : list) { + if ("1".equals(item)) { + list.remove(item); + } } - } -``` -*说明*: 以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的结果吗? + ``` + *说明*: 以上代码的执行结果肯定会出乎大家的意料, 那么试一下把“1”换成“2”, 会是同样的结果吗? -1. 在 JDK7 版本以上, Comparator 要满足自反性,传递性,对称性,不然 `Arrays.sort` ,`Collections.sort` 会报 `IllegalArgumentException` 异常。 - - 1 ) 自反性: x , y 的比较结果和 y , x 的比较结果相反。 - - 2 ) 传递性: x > y , y > z ,则 x > z 。 - - 3 ) 对称性: x = y ,则 x , z 比较结果和 y , z 比较结果相同。 -*反例: 下例中没有处理相等的情况,实际使用中可能会出现异常:* -```java - new Comparator() { - @Override - public int compare(Student o1, Student o2) { - return o1.getId() > o2.getId() ? 1 : -1; - } - }; -``` -1. 集合初始化时,尽量指定集合初始值大小。 - - 说明: HashMap 使用 HashMap(int initialCapacity) 初始化 - - 正例:`initialCapacity=(需要存储的元素个数/负载因子)+1`。注意负载因子(即 loaderfactor)默认为`0.75`, - - 如果暂时无法确定初始值大小,请设置为 16(即默认值) 。 - - 反例: HashMap 需要放置 1024 个元素, 由于没有设置容量初始大小,随着元素不断增加,容量`7`次被迫扩大, resize 需要重建 hash 表,严重影响性能。 -1. 使用 `entrySet` 遍历 Map 类集合 KV ,而不是 `keySet` 方式进行遍历 - - `keySet` 其实是遍历了 2 次,一次是转为 `Iterator` 对象,另一次是从 hashMap 中取出key 所对应的 value 。 - - 而 `entrySet` 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。 - - 如果是 JDK8,使用 `Map.foreach` 方法。 - - *正例:*values() 返回的是V值集合,是一个 list 集合对象;keySet()返回的是K值集合,是一个 Set集合对象;entrySet()返回的是 K - V 值组合集合。 -1. 高度注意 Map 类集合 `K/V` 能不能存储 null 值的情况,如下表格: - - 反例: 由于 HashMap 的干扰,很多人认为 ConcurrentHashMap 是可以置入 null 值,而事实上,存储 null 值时会抛出 NPE 异常。 - -| 集合类 | Key | Value | Super | 说明 | -| --- | --- | --- | --- | --- | -|Hashtable|! NULL|! NULL|Dictionary|线程安全| -|ConcurrentHashMap|! NULL|! NULL|AbstractMap|锁分段技术( JDK8:CAS)| -|TreeMap|! NULL|NULL|AbstractMap|线程不安全| -|HashMap|NULL|NULL|AbstractMap|线程不安全| - -1. 合理利用好集合的有序性 (sort) 和稳定性 (order) ,避免集合的无序性 (unsort) 和不稳定性 (unorder) 带来的负面影响。 +1. 在 JDK7 版本以上, Comparator 要满足自反性, 传递性, 对称性, 不然 `Arrays.sort` , `Collections.sort` 会报 `IllegalArgumentException` + - 1 ) 自反性: x , y 的比较结果和 y , x 的比较结果相反。 + - 2 ) 传递性: x > y , y > z ,则 x > z 。 + - 3 ) 对称性: x = y ,则 x , z 比较结果和 y , z 比较结果相同。 + + *反例: 下例中没有处理相等的情况, 实际使用中可能会出现异常: * + ```java + new Comparator() { + @Override + public int compare(Student o1, Student o2) { + return o1.getId() > o2.getId() ? 1 : -1; + } + }; + ``` + +**`推荐`** +1. 集合初始化时, 尽量指定集合初始容量值。 + - HashMap 使用 HashMap(int initialCapacity) 初始化 + - 正例: `initialCapacity=(需要存储的元素个数/负载因子)+1`。注意负载因子(即 loaderfactor)默认为`0.75` + - 如果暂时无法确定初始值大小, 请设置为 16(即默认值) 。 + - 反例: HashMap 需要放置 1024 个元素, 由于没有设置容量初始大小, 随着元素不断增加, 容量`7`次被迫扩大, resize 需要重建 hash 表, 严重影响性能。 + +1. 使用 `entrySet` 遍历 Map 类集合 KV , 而不是 `keySet` 方式进行遍历 + - `keySet` 其实是遍历了 2 次, 一次是转为 `Iterator` 对象, 另一次是从 hashMap 中取出key 所对应的 value 。 + - 而 `entrySet` 只是遍历了一次就把 key 和 value 都放到了 entry 中, 效率更高。 如果是 JDK8, 使用 lambda 的 foreach 方法。 + - 说明: values() 返回的是V值集合, 是一个 list 集合对象; keySet()返回的是K值集合, 是一个 Set集合对象;entrySet()返回的是 K - V 值组合集合。 + +1. 高度注意 Map 类集合 `K/V` 能不能存储 null 值的情况, 如下表格: + - 反例: 由于 HashMap 的干扰, 很多人认为 ConcurrentHashMap 是可以置入 null 值, 而事实上, 存储 null 值时会抛出 NPE 异常。 + + | 集合类 | Key | Value | Super | 说明 | + | --- | --- | --- | --- | --- | + |Hashtable|! NULL|! NULL|Dictionary|线程安全| + |ConcurrentHashMap|! NULL|! NULL|AbstractMap|锁分段技术( JDK8:CAS)| + |TreeMap|! NULL|NULL|AbstractMap|线程不安全| + |HashMap|NULL|NULL|AbstractMap|线程不安全| + +1. 合理利用好集合的有序性 (sort) 和稳定性 (order) , 避免集合的无序性 (unsort) 和不稳定性 (unorder) 带来的负面影响。 - 稳定性指集合每次遍历的元素次序是一定的。 - 有序性是指遍历的结果是按某种比较规则依次排列的。 - - 如: ArrayList 是 order / unsort; - - HashMap 是 unorder/unsort; - - TreeSet 是order / sort 。 -1. 利用 Set元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List的contains 方法进行遍历、对比、去重操作。 + - 如: ArrayList 是 order / unsort; + - HashMap 是 unorder/unsort; + - TreeSet 是 order / sort 。 + +1. 利用 Set元素唯一的特性, 可以快速对一个集合进行去重操作, 避免使用 List的contains 方法进行遍历、对比、去重操作。 - OrderSet也是一个很重要的类 -- [ ] 重新整理 ## 并发处理 -- 获取单例对象需要保证线程安全,其中的方法也要保证线程安全。 +**`强制`** +1. 获取单例对象需要保证线程安全, 其中的方法也要保证线程安全。 - 资源驱动类、工具类、单例工厂类都需要注意。 -- 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。 -- 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 -- 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 - - 1) FixedThreadPool 和 SingleThreadPool : - - 允许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而导致 OOM 。 - - 2) CachedThreadPool 和 ScheduledThreadPool : - - 允许的创建线程数量为 Integer.MAX_VALUE ,可能会创建大量的线程,从而导致 OOM 。 -- SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为static ,必须加锁,或者使用 DateUtils 工具类。 - - 如果是 JDK 8 的应用,可以使用 Instant 代替 Date , LocalDateTime 代替 Calendar ,DateTimeFormatter 代替 Simpledateformatter , - - 官方给出的解释: simple beautiful strongimmutable thread - safe 。 -- 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁 ; 能锁区块,就不要锁整个方法体 ; 能用对象锁,就不要用类锁。 -- 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。 - - 线程一需要对表 A 、 B 、 C 依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是 A 、 B 、 C ,否则可能出现死锁。 -- 并发修改同一记录时,避免更新丢失,要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据。 - - 如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于 3 次。 -- 多线程并行处理定时任务时, Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。 -- 使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown方法,线程执行代码注意 catch 异常,确保 countDown 方法可以执行,避免主线程无法执行至 countDown 方法,直到超时才返回结果。 - - 注意,子线程抛出异常堆栈,不能在主线程 try - catch 到。 -- 避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed 导致的性能下降。 - - Random 实例包括 java . util . Random 的实例或者 Math . random() 实例。 -- 通过双重检查锁 (double - checked locking)( 在并发场景 ) 实现延迟初始化的优化问题隐患 - - ( 可参考 The " Double - Checked Locking is Broken " Declaration) ,推荐问题解决方案中较为简单一种 ( 适用于 JDK 5 及以上版本 ) ,将目标属性声明为 volatile 型 。 -- volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。如果是 count ++操作,使用如下类实现: - - AtomicInteger count = new AtomicInteger(); count . addAndGet( 1 ); - - 如果是 JDK 8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好 ( 减少乐观锁的重试次数 ) 。 -- HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在开发过程中注意规避此风险。 -- ThreadLocal 无法解决共享对象的更新问题, ThreadLocal 对象建议使用 static修饰。这个变量是针对一个线程内所有操作共有的,所以设置为静态变量, - - 所有此类实例共享此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象 ( 只要是这个线程内定义的 ) 都可以操控这个变量。 +1. 创建线程或线程池时请指定有意义的线程名称, 方便出错时回溯。 + +1. 线程资源必须通过线程池提供, 不允许在应用中自行显式创建线程。 + - 使用线程池的好处是减少在创建和销毁线程上所花的时间, 以及系统资源的开销,解决资源不足的问题, 如果不使用线程池, 有可能造成系统创建大量同类线程而导致消耗完内存或者 过度切换 的问题 + +1. 线程池不允许使用 **Executors** 去创建, 而是通过 **ThreadPoolExecutor** 的方式, 这样的处理方式让写的同学更加明确线程池的运行规则, 规避资源耗尽的风险。 + - Executors 返回的线程池对象的弊端如下: + 1. FixedThreadPool 和 SingleThreadPool : + - 允许的请求队列长度为 Integer.MAX_VALUE , 可能会堆积大量的请求, 从而导致 OOM 。 + 1. CachedThreadPool 和 ScheduledThreadPool : + - 允许的创建线程数量为 Integer.MAX_VALUE , 可能会创建大量的线程, 从而导致 OOM 。 + +1. SimpleDateFormat 是线程不安全的类, 一般不要定义为 static 变量, 如果定义为static , 必须加锁, 或者使用 DateUtils 工具类。 + - 如果是 JDK 8 的应用, 可以使用 Instant 代替 Date , LocalDateTime 代替 Calendar , DateTimeFormatter 代替 Simpledateformatter , + - 官方给出的解释: simple beautiful strongimmutable thread - safe 。 + > 或者如下处理实例化 + + ```java + private static final ThreadLocal df = new ThreadLocal() { + @Override + protected DateFormat initialValue() { + return new SimpleDateFormat("yyyy-MM-dd"); + } + }; + ``` + +1. 高并发时, 同步调用应该去考量锁的性能损耗。 + - 能用无锁数据结构, 就不要用锁 ; + - 能锁区块, 就不要锁整个方法体 ; + - 能用对象锁, 就不要用类锁。 + - 尽可能使得加锁的代码块工作量尽可能的小, 避免在锁代码块中调用 RPC 方法; + +1. 对多个资源、数据库表、对象同时加锁时, 需要保持一致的加锁顺序, 否则可能会造成死锁。 + - 线程一需要对表 A 、 B 、 C 依次全部加锁后才可以进行更新操作, 那么线程二的加锁顺序也必须是 A 、 B 、 C , 否则可能出现死锁。 + +1. 并发修改同一记录时, 避免更新丢失, 要么在应用层加锁, 要么在缓存加锁, 要么在数据库层使用乐观锁, 使用 version 作为更新依据。 + - 如果每次访问冲突概率小于 20%, 推荐使用乐观锁, 否则使用悲观锁。乐观锁的重试次数不得小于 3 次。 + +1. 多线程并行处理定时任务时, Timer 运行多个 TimeTask 时, 只要其中之一没有捕获抛出的异常, 其它任务便会自动终止运行 + - 使用 ScheduledExecutorService 则没有这个问题。 + +**`推荐`** +1. 使用 CountDownLatch 进行异步转同步操作, 每个线程退出前必须调用 countDown方法, 线程执行代码注意 catch 异常, 确保 countDown 方法被执行到, 避免主线程无法执行至 await 方法, 直到超时才返回结果。 + - 注意, 子线程抛出异常堆栈, 不能在主线程 try - catch 到。 + +1. 避免 Random 实例被多线程使用, 虽然共享该实例是线程安全的, 但会因竞争同一 seed 导致的性能下降。 + - Random 实例包括 java.util.Random 的实例或者 Math.random() 实例。 + - 在JDK7之后, 可以直接使用 TreadLocalRandom, 而在JDK7以前, 需要编码保证每个线程持有一个实例 + +1. 在并发场景下, 通过双重检查锁 (double - checked locking) 实现延迟初始化的优化问题隐患 + - 可参考 The "Double - Checked Locking is Broken" Declaration , + - 推荐问题解决方案中较为简单一种 ( 适用于 JDK 5 及以上版本 ) , 将目标属性声明为 volatile 型 。 + +1. volatile 解决多线程内存不可见问题。对于一写多读, 是可以解决变量同步问题 + - 但是如果多写, 同样无法解决线程安全问题。如果是 count++ 操作, 使用如下类实现: + - AtomicInteger count = new AtomicInteger(); count . addAndGet( 1 ); + - 如果是 JDK 8, 推荐使用 LongAdder 对象, 比 AtomicLong 性能更好 ( 减少乐观锁的重试次数 ) 。 + +1. HashMap 在容量不够进行 resize 时由于高并发可能出现死链, 导致 CPU 飙升, 在开发过程中可以使用其他数据结构或加锁来规避此风险。 + +1. ThreadLocal 无法解决共享对象的更新问题, ThreadLocal 对象建议使用 static 修饰。 + - 这个变量是针对一个线程内所有操作共有的, 所以设置为静态变量, 所有此类实例共享此静态变量, + - 也就是说在类第一次被使用时装载, 只分配一块存储空间, 所有此类的对象 ( 只要是这个线程内定义的 ) 都可以操控这个变量。 + +************************** ## 控制语句 -- 在一个 switch 块内,每个 case 要么通过 break / return 等来终止,要么注释说明程序将继续执行到哪一个 case 为止 ; - - 在一个 switch 块内,都必须包含一个 default 语句并且放在最后,即使它什么代码也没有。 -- 在 if / else / for / while / do 语句中必须使用大括号,即使只有一行代码 -- 推荐尽量少用 else , if - else 的方式可以改写成: - - 逻辑上超过 3 层的 if-else 代码可以使用卫语句,或者状态模式来实现 -- 除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。 - - `boolean existed = (file.open(fileName, "w") != null) && (...) || (...);if (existed) {}` -- 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try - catch 操作 ( 这个 try - catch 是否可以移至循环体外) 。 -- 接口入参保护,这种场景常见的是用于做批量操作的接口。 -- 方法中需要进行参数校验的场景: - - 1 ) 调用频次低的方法。 - - 2 ) 执行时间开销很大的方法,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那得不偿失。 - - 3 ) 需要极高稳定性和可用性的方法。 - - 4 ) 对外提供的开放接口,不管是 RPC / API / HTTP 接口。 - - 5) 敏感权限入口。 -- 方法中不需要参数校验的场景: - - 1 ) 极有可能被循环调用的方法,不建议对参数进行校验。但在方法说明里必须注明外部参数检查。 - - 2 ) 底层的方法调用频度都比较高,一般不校验。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底层才会暴露问题。 - - 一般 DAO 层与 Service层都在同一个应用中,部署在同一台服务器中,所以 DAO 的参数校验,可以省略。 - - 3 ) 被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参数已经做过检查或者肯定不会有问题,此时可以不校验参数。 +**`强制`** +1. 在一个 switch 块内, 每个 case 要么通过 break/return 等来终止, 要么注释说明程序将继续执行到哪一个 case 为止 ; + - 在一个 switch 块内, 都必须包含一个 default 语句并且放在最后, 即使它什么代码也没有。 + +1. 在 if/else/for/while/do 语句结构中必须使用大括号, 即使只有一行代码 + +**`推荐`** +1. 表达异常的分支时, 少用 if-else 的方式, 这种方式可以改写: + - 逻辑上超过 3 层的 if-else 代码可以使用卫语句, 状态模式, 策略模式来实现 + ```java + // 卫语句就是将错误情况提前返回 + public void today(){ + if(isBusy()){ + System.out.println("change time."); + return; + } + if(isFree()){ + ... + return; + } + .... + } + ``` + +1. 除常用方法(如 getXxx/isXxx)等外, 不要在条件判断中执行其它复杂的语句 + - 并且将复杂逻辑判断的结果赋值给一个有意义的布尔变量名, 以提高可读性。 + - 很多 if 语句内的逻辑判断都是相当复杂, 不能一眼看出在判断什么, 需要停顿,分析一下表达式才能理解 if 语句存在的意义 + - 如果有个boolean变量接收, 变量名就起到了注释的作用 + ```java + // 正例 + final boolean existed = (file.open(fileName, "w") != null) && (...) || (...); + if(existed){...} + + // 反例 + if ((file.open(fileName, "w") != null) && (...) || (...);){...} + ``` + +1. 循环体中的语句要考量性能, 以下操作尽量移至循环体外处理, 如定义对象、变量、获取数据库连接 + - 进行不必要的 try-catch 操作 (需要考虑这个 try-catch 是否可以移至循环体外而不影响逻辑)。 + +1. 接口入参保护, 这种场景常见的是用于做批量操作的接口。 + +1. 方法中 **必须** 进行参数校验的场景: + 1. 调用频次低的方法。 + 1. 执行时间开销很大的方法. 此情形中,参数校验时间几乎可以忽略不计, + - 但如果因为参数错误导致中间执行回退, 或者错误, 那得不偿失。 + 1. 需要极高稳定性和可用性的方法。 + 1. 对外提供的开放接口, 不管是 RPC/API/HTTP 接口。 + 1. 敏感权限入口。 + +1. 方法中 **不需要** 参数校验的场景: + 1. 极有可能被循环调用的方法, 不建议对参数进行校验。但在方法说明里必须注明外部参数检查要求。 + 1. 底层的方法调用频度都比较高。毕竟是像纯净水过滤的最后一道, 参数错误不太可能到底层才会暴露问题。 + - 一般 DAO 层与 Service层都在同一个应用中, 部署在同一台服务器中, 所以 DAO 的参数校验, 可以省略。 + 1. 被声明成 private 只会被自己代码所调用的方法, 如果能够确定调用方法的代码传入参数已经做过检查或者肯定不会有问题, 此时可以不校验参数。 ## 注释规约 -- 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/** 内容 */格式,不得使用 // xxx 方式。 -- 所有的抽象方法 ( 包括接口中的方法 ) 必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。 -- 所有的类都必须添加创建者信息。 -- 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。 -- 所有的枚举类型字段必须要有注释,说明每个数据项的用途。 -- 与其“半吊子”英文来注释,不如用中文注释把问题说清楚。专有名词与关键字保持英文原文即可。 -- 代码修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑等的修改。 - - 代码与注释更新不同步,就像路网与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。 -- 注释掉的代码尽量要配合说明,而不是简单的注释掉。 - - 代码被注释掉有两种可能性: - - 1 ) 后续会恢复此段代码逻辑。 - - 2 ) 永久不用。 - - 情况(1)如果没有备注信息,难以知晓注释动机。(2)建议直接删掉 ( 代码仓库保存了历史代码 ) 。 -- 对于注释的要求: - - 第一、能够准确反应设计思想和代码逻辑 ; - - 第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息。 - - 完全没有注释的大段代码对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路 ; - - 注释也是给继任者看的,使其能够快速接替自己的工作。 -- 好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担。 -- 特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。 线上故障有时候就是来源于这些标记处的代码。 - - 1 ) 待办事宜 (TODO) : ( 标记人,标记时间, [ 预计处理时间 ]) - - 表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc还没有实现,但已经被广泛使用。只能应用于类,接口和方法 ( 因为它是一个 Javadoc 标签 ) 。 - - 2 ) 错误,不能工作 (FIXME) : ( 标记人,标记时间, [ 预计处理时间 ]) - - 在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。 +**`强制`** +1. 类、类属性、类方法的注释必须使用 Javadoc 规范, 使用 /** 内容 */ 格式, 不得使用 // xxx 方式。 + - 为了能在IDE中快速查看注释 + +1. 所有的抽象方法 (包括接口中的方法) 必须要用 Javadoc 注释、除了返回值、参数、异常说明外, 还必须指出该方法做什么事情, 实现什么功能。 + - 并且声明 对子类的实现要求, 或者调用注意事项 + +1. 所有的类都必须添加创建者和创建日期。 + +1. 方法内部单行注释, 在被注释语句上方另起一行, 使用//注释(而不是行尾注释)。方法内部多行注释使用 /* */ 注释, 注意与代码对齐。 + +1. 所有的枚举类型字段必须要有注释, 说明每个数据项的用途。 + +**`推荐`** +1. 与其“半吊子”英文来注释, 不如用中文注释把问题说清楚。专有名词与关键字保持英文原文即可。 + - 反例: TCP连接超时 解释成 传输控制协议连接超时, 反而更难以理解 + +1. 代码修改的同时, 注释也要进行相应的修改, 尤其是参数、返回值、异常、核心逻辑等的修改。 + - 代码与注释更新不同步, 就像路网与导航软件更新不同步一样, 如果导航软件严重滞后, 就失去了导航的意义。 + +1. 谨慎注释掉代码, 尽量要配合说明, 而不是简单的注释掉, 如果无用, 则删除即可 + - 代码被注释掉有两种可能性: + 1. 后续会恢复此段代码逻辑, 若无注释,难以知晓注释动机 + 1. 永久不用: 建议直接删除, 仓库有历史记录的 + +1. 对于注释的要求: + 1. 第一、能够准确反应设计思想和代码逻辑 + 1. 第二、能够描述业务含义, 使别的程序员能够迅速了解到代码背后的信息。 + 1. 完全没有注释的大段代码对于阅读者形同天书, 注释是给自己看的, 即使隔很长时间, 也能清晰理解当时的思路 ; + 1. 注释也是给继任者看的, 使其能够快速接替自己的工作。 + +1. 好的命名、代码结构是自解释的, 注释力求精简准确、表达到位。避免出现注释的一个极端:过多过滥的注释, 代码的逻辑一旦修改, 修改注释是相当大的负担。 + +1. 特殊注释标记, 请注明标记人与标记时间。注意及时处理这些标记, 通过标记扫描, 经常清理此类标记。 线上故障有时候就是来源于这些标记处的代码。 + 1. 待办事项 (TODO) : 标记人, 标记时间, [ 预计处理时间 ] + - 表示需要实现, 但目前还未实现的功能。这实际上是一个 Javadoc 的标签, 目前的 Javadoc还没有实现, 但已经被广泛使用。只能应用于类, 接口和方法 ( 因为它是一个 Javadoc 标签 ) 。 + 1. 错误, 不能工作 (FIXME) : 标记人, 标记时间, [ 预计处理时间 ] + - 在注释中用 FIXME 标记错误的代码, 不能工作, 需要及时纠正的情况。 ## 其他 -- 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。 - - 不要在方法体内定义: Pattern pattern = Pattern . compile( 规则 ); -- velocity 调用 POJO 类的属性时,建议直接使用属性名取值即可,模板引擎会自动按规范调用 POJO 的 getXxx() , - - 如果是 boolean 基本数据类型变量 (boolean 命名不需要加 is前缀 ) ,会自动调用 isXxx() 方法。 - - 注意如果是 Boolean 包装类对象,优先调用 getXxx() 的方法。 -- 后台输送给页面的变量必须加 $!{var} ——中间的感叹号。 - - 如果 var = null 或者不存在,那么 ${var} 会直接显示在页面上。 -- 注意 Math . random() 这个方法返回是 double 类型,注意取值的范围 0≤ x <1 ( 能够取到零值,注意除零异常 ) , - - 如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。 -- 获取当前毫秒数 System . currentTimeMillis(); 而不是 new Date() . getTime(); - - 如果想获取更加精确的纳秒级时间值,用 System . nanoTime() 。在 JDK 8 中,针对统计时间等场景,推荐使用 Instant 类。 -- 尽量不要在 vm 中加入变量声明、逻辑运算符,更不要在 vm 模板中加入任何复杂的逻辑。 -- 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。 -- 对于“明确停止使用的代码和配置”,如方法、变量、类、配置文件、动态配置属性等要坚决从程序中清理出去,避免造成过多垃圾。 - -# 【异常日志】 +**`强制`** +1. 在使用正则表达式时, 利用好其预编译功能, 可以有效加快正则匹配速度。 + - 不要在方法体内定义: Pattern pattern = Pattern.compile( 规则 ); + +1. velocity 调用 POJO 类的属性时, 建议直接使用属性名取值即可, 模板引擎会自动按规范调用 POJO 的 getXxx() , + - 如果是 boolean 基本数据类型变量 (boolean 命名不需要加 is前缀 ) , 会自动调用 isXxx() 方法。 + - **注意** 如果是 Boolean 包装类对象, 优先调用 getXxx() 的方法。 + +1. 后台输送给页面的变量必须加 $!{var} ——中间的感叹号。 + - 如果 var 为 null 或者不存在, 那么 ${var} 会直接显示在页面上。 + +1. 注意 Math.random() 这个方法返回是 double 类型, 注意取值的范围 0 <= x <1 ( 能够取到零值, 注意除零异常 ) , + - 如果想获取整数类型的随机数, 不要将 x 放大 10 的若干倍然后取整, 直接使用 Random 对象的 nextInt 或者 nextLong 方法。 + +1. 获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime(); + - 如果想获取更加精确的纳秒级时间值, 用 System.nanoTime(), 在 JDK8 中, 针对统计时间等场景, 推荐使用 Instant 类。 + +**`推荐`** +1. 尽量不要在视图模板中加入变量声明、逻辑运算符, 更不要加入任何复杂的逻辑。 + - 根据MVC理论, 视图的原则是展示, 不要抢模板和控制器的活 + +1. 任何数据结构的构造或初始化, 都应指定大小, 避免数据结构无限增长吃光内存。 + +1. 及时清理不再使用的代码段或配置信息, 避免程序过度臃肿, 代码冗余 + - 对于暂时被注释掉, 后续可能恢复使用的代码片段, 在注释代码上方, 统一规定使用三个斜杠来说明注释掉代码的理由 + +# 异常日志 ## 异常处理 -- 不要捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类 - - 如:IndexOutOfBoundsException / NullPointerException,这类异常由程序员预检查来规避,保证程序健壮性。 - - if(obj != null) {String name = obj.getName();} -- 异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。 -- 对大段代码进行 try - catch ,这是不负责任的表现。 catch 时请分清稳定代码和非稳定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分异常类型,再做对应的异常处理。 -- 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。 -- 有 try 块放到了事务代码中, catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。 -- finally 块必须对资源对象、流对象进行关闭,有异常也要做 try - catch 。 - - 如果 JDK 7,可以使用 try - with - resources 方式。 -- 不能在 finally 块中使用 return , finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。 -- 捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。 -- 方法的返回值可以为 null ,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回 null 值。**调用方**需要进行 null 判断防止 NPE 问题。 - - 本规约明确防止 NPE 是调用者的责任。即使被调用方法返回空集合或者空对象,对调用者来说,也并非高枕无忧,必须考虑到远程调用失败,运行时异常等场景返回 null 的情况。 -- 防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景: - - 1 ) 返回类型为包装数据类型,有可能是 null ,返回 int 值时注意判空。 - - 反例: public int f() { return Integer 对象}; 如果为 null ,自动解箱抛 NPE 。 - - 2 ) 数据库的查询结果可能为 null 。 - - 3 ) 集合里的元素即使 isNotEmpty ,取出的数据元素也可能为 null 。 - - 4 ) 远程调用返回对象,一律要求进行 NPE 判断。 - - 5 ) 对于 Session 中获取的数据,建议 NPE 检查,避免空指针。 - - 6 ) 级联调用 obj . getA() . getB() . getC(); 一连串调用,易产生 NPE 。 -- 在代码中使用“抛异常”还是“返回错误码”,对于公司外的 http / api 开放接口必须使用“错误码” ; 而应用内部推荐异常抛出 ; 跨应用间 RPC 调用优先考虑使用 Result 方式,封装 isSuccess 、“错误码”、“错误简短信息”。 - - 关于 RPC 方法返回方式使用 Result 方式的理由: - - 1 ) 使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。 - - 2 ) 如果不加栈信息,只是 new 自定义异常,加入自己的理解的 error message ,对于调用端解决问题的帮助不会太多。 - - 如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。 -- 定义时区分 unchecked / checked 异常,避免直接使用 RuntimeException 抛出,更不允许抛出 Exception 或者 Throwable ,应使用有业务含义的自定义异常。 - - 推荐业界已定义过的自定义异常,如: DAOException / ServiceException 等。 -- 避免出现重复的代码 (Don ’ t Repeat Yourself) ,即 DRY 原则。 - - 随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是共用模块。 +**`强制`** +1. Java类库中定义的一类 RuntimeException 可以通过预先检查进行规避, 而不应该通过 catch 来处理 + - 比如 IndexOutOfBoundsException NullPointerException 等等. + - 无法通过预检查的异常除外, 如在解析一个外部传来的字符串形式的数字时, 通过 catch NumberFormatException 来实现 + - 正例: if (obj != null){...} + - 反例: try{obj.method()} catch(NullPointerException e){...} + +1. 异常不要用来做流程控制, 条件控制, 因为异常的处理效率比条件分支低. + +1. 对大段代码进行 try-catch, 这是不负责任的表现。 + - catch 时请分清稳定代码和非稳定代码, 稳定代码指的是无论如何不会出错的代码。 + - 对于非稳定代码的 catch 尽可能进行区分异常类型, 再做对应的异常处理。 + - 但是! 新手来说,分不清稳不稳定, 最好用大 try 块, 避免有异常遗漏没有处理 + +1. 捕获异常是为了处理它, 不要捕获了却什么都不处理而抛弃之, 如果不想处理它, 请将该异常抛给它的调用者。 + - 最外层的业务使用者, 必须处理异常, 将其转化为用户可以理解的内容。 + +1. 有 try 块放到了事务代码中, catch 异常后, 如果需要回滚事务, 一定要注意手动回滚事务。 + +1. finally 块必须对资源对象、流对象进行关闭, 有异常也要做 try-catch 。 + - 对于 JDK7及以上, 可以使用 try-with-resources 方式。 + +1. 不能在 finally 块中使用 return, finally 块中的 return 返回后方法结束执行, 不会再执行 try 块中的 return 语句。 + - 这种返回语句可能会掩盖抛出的异常,并极大地使调试复杂化 + +1. 捕获与抛出的异常, 必须是完全匹配, 或者捕获的异常是抛出的异常的父类。 + - 说明: 如果预期对方抛的是绣球, 实际接到的是铅球, 就会产生意外情况。 + +**`推荐`** +1. 方法的返回值可以为 null, 不强制返回空集合, 或者空对象等, 必须添加注释充分说明什么情况下会返回 null 值。**调用方**需要进行 null 判断防止 NPE 问题。 + - 本规约明确 **防止 NPE 是调用者的责任**。即使被调用方法返回空集合或者空对象, 对调用者来说, 也并非高枕无忧, 必须考虑到远程调用失败, 运行时异常等场景返回 null 的情况。 + +1. 防止 NPE, 是程序员的基本修养, 注意 NPE 产生的场景: + 1. 返回类型为基本数据类型, return包装类型的对象时, 自动拆箱有可能产生 NPE. + - 反例: `public int f() { return Integer对象};` 如果为 null , 自动拆箱将抛出 NPE + 1. 数据库的查询结果可能为 null + 1. 集合里的元素即使 isNotEmpty , 取出的数据元素也可能为 null + 1. 远程调用返回对象时, 一律要求进行 NPE 检查 + 1. 对于 Session 中获取的数据, 建议 NPE 检查 + 1. 级联调用 `obj.getA().getB().getC();` 一连串调用, 易产生 NPE + +1. 在代码中使用“抛异常”还是“返回错误码” + - 对于公司外的 http/api 开放接口必须使用“错误码”; + - 而应用内部推荐异常抛出; + - 跨应用间 RPC 调用优先考虑 **使用 Result 方式**, 封装 isSuccess()方法, “错误码", “错误简短信息”。 + - 关于 RPC 方法返回方式使用 Result 方式的理由: + 1. 使用抛异常返回方式, 调用方如果没有捕获到就会产生运行时错误。 + 1. 如果不加栈信息, 只是 new 自定义异常, 加入自己的理解的 error message, 对于调用端解决问题的帮助不会太多 + - 如果加了栈信息, 在频繁调用出错的情况下, 数据序列化和传输的性能损耗也是问题。 + +1. 定义时区分 unchecked/checked 异常, 避免直接抛出 RuntimeException, 更不允许抛出 Exception 或者 Throwable, 应使用有业务含义的自定义异常 + - 推荐业界已定义过的自定义异常, 如: DAOException / ServiceException 等。 + +1. 避免出现重复的代码 (Don ’ t Repeat Yourself) , 即 DRY 原则。 + - 随意复制和粘贴代码, 必然会导致代码的重复, 在以后需要修改时, 需要修改所有的副本, 容易遗漏。必要时抽取共性方法, 或者抽象公共类, 甚至是组件化. + - 正例: 一个类中有多个public方法, 都需要进行数行相同的参数校验操作, 这个时候请抽取: + - `private boolean checkParam(DTO dto){...}` ## 日志规约 -- 应用中不可直接使用日志系统 (Log 4 j 、 Logback) 中的 API ,而应依赖使用日志框架SLF4J中的API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。 -- 日志文件推荐至少保存 15 天,因为有些异常具备以“周”为频次发生的特点。 -- 应用中的扩展日志 ( 如打点、临时监控、访问日志等 ) 命名方式:appName _ logType _ logName . log 。 logType : - - 日志类型,推荐分类有stats / desc / monitor / visit 等 ;logName :日志描述。 - - 这种命名的好处:通过文件名就可知道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。 - - 推荐对日志进行分类,错误日志和业务日志尽量分开存放,便于开发人员查看,也便于通过日志对系统进行及时监控。 -- 对 trace / debug / info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。 - - logger . debug( " Processing trade with id : " + id + " symbol : " + symbol);如果日志级别是 warn ,上述日志不会打印,但是会执行字符串拼接操作,如果 symbol 是对象,会执行 toString() 方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。 -- 避免重复打印日志,浪费磁盘空间,务必在 log 4 j . xml 中设置 additivity = false 。 -- 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么往上抛。 -- 可以使用 warn 日志级别来记录用户输入参数错误的情况,避免用户投诉时,无所适从。注意日志输出的级别, error 级别只记录系统逻辑出错、异常等重要的错误信息。如非必要,请不要在此场景打出 error 级别。 -- 谨慎地记录日志。生产环境禁止输出 debug 日志 ; 有选择地输出 info 日志 ; 如果使用 warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时删除这些观察日志。 - - 大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。 - - 记录日志时请思考: +**`强制`** +1. 应用中不可直接使用日志系统 (Log4j, Logback) 中的 API , 而应依赖使用日志框架 SLF4J 中的API, 使用门面模式的日志框架, 有利于维护和各个类的日志处理方式统一。 + ```java + import org.slf4j.Logger; + import org.slf4j.LoggerFactory; + private static final Logger logger = LoggerFactory.getLogger(A.class); + ``` + +1. 日志文件推荐至少保存 15 天, 因为有些异常具备以“周”为频次发生的特点。 + +1. 应用中的扩展日志 ( 如打点、临时监控、访问日志等 ) 命名方式: appName_logType_logName.log 。 + - logType: 日志类型 + - 推荐分类有stats / desc / monitor / visit 等 + - logName: 日志描述 + - 这种命名的好处: 通过文件名就可知道日志文件属于什么应用, 什么类型, 什么目的, 也有利于归类查找。 + - 推荐对日志进行分类, 错误日志和业务日志尽量分开存放, 便于开发人员查看, 也便于通过日志对系统进行及时监控。 + +1. 对 trace / debug / info 级别的日志输出, 必须使用条件输出形式或者使用占位符的方式。 + - `logger.debug("Processing trade with id : " + id + " symbol : " + symbol);` + - 如果日志级别是 warn , 上述日志不会打印, 但是会执行字符串拼接操作 + - 并且若 symbol 是对象, 会执行 toString() 方法, 浪费了系统资源, 执行了上述操作, 最终日志却没有打印。 + +1. 避免重复打印日志, 浪费磁盘空间, 务必在 log4j.xml 中设置 `additivity = false` + - `` + +1. 异常信息应该包括两类信息: 案发现场信息和异常堆栈信息。如果不处理,就要通过关键字 throws 上抛 + - `logger.error(各类参数或者对象toString + "_" + e.getMessage(), e)` + +**`推荐`** +1. 可以使用 warn 日志级别来记录`用户输入参数错误`的情况, 避免用户投诉时, 无所适从。 + - 注意日志输出的级别, error 级别只记录系统逻辑出错、异常等重要的错误信息。 + - 如非必要, 请不要在此场景打出 error 级别。 + +1. 谨慎地记录日志。生产环境禁止输出 debug 日志 ; 有选择地输出 info 日志 ; 如果使用 warn 来记录刚上线时的业务行为信息, 一定要注意日志输出量的问题, 避免把服务器磁盘撑爆, 并记得及时删除这些观察日志。 + - 大量地输出无效日志, 不利于系统性能提升, 也不利于快速定位错误点。 + - 记录日志时请思考: - 这些日志真的有人看吗? - 看到这条日志你能做什么? - 能不能给问题排查带来好处? +# 单元测试 +**`强制`** + +1. 好的单元测试必须遵守 AIR 原则 + - 单元测试在线上运行时, 就像空气 AIR 一样不存在, 但是测试质量的保障上, 却是非常关键的 + - 好的单元测试宏观上来说, 具有自动化, 独立性, 可重复执行的特点 + - A: Automatic + - I: Independent + - R: Repeatable + +1. 单元测试应该是全自动执行的, 而且是非交互式的. 测试框架通常是定期执行的, 执行过程中必须完全自动化才有意义. + - 输出结果需要人工检查的测试不是一个好的单元测试, 单元测试中不准使用 System.out 来进行人工验证, 必须使用 assert. + +1. 保持单元测试的独立性. 为了保证单元测试稳定可靠且便于维护, 单元测试用例之间决不能互相调用, 也不能依赖执行的先后次序. + - method2 需要依赖 method1 的执行, 将执行结果作为method2 的输入. + +1. 单元测试是可以重复执行的, 不能受到外界环境的影响. + - 说明: + - 单元测试通常会放到持续集成中, 每次有代码 check in 时单元测试都会被执行. + - 如果测试对外部环境(网络, 服务, 中间件等)有依赖, 容易导致持续集成机制的不可用. + - 正例: + - 为了不受外界环境影响, 要求设计代码时就把SUT的依赖改成注入, 在测试时用Spring这样的DI框架注入一个本地(内存)实现或者Mock实现. + +1. 对于单元测试, 要保证测试粒度足够小, 有助于精确定位问题. 单测粒度至多是类级别, 一般是方法级别. + - 只有测试粒度小才能在出错时尽快定位到出错位置. 单测不负责检查跨类或者夸系统的交互逻辑, 那是集成测试的领域. + +1. 核心业务, 核心应用, 核心模块的增量代码确保单元测试通过. + - 新增代码及时补充单元测试, 如果新增代码影响了原有单元测试, 请及时修正. + +1. 单元测试代码必须写在如下工程目录: src/test/java 不允许写在业务代码目录下. + - 源码构建时会跳过此目录, 而单元测试框架默认是扫描此目录. + +**`推荐`** +1. 单元测试的基本目标: 语句覆盖率达到70%; 核心模块的语句覆盖率和分支覆盖率都要达到100%. + - 在工程规约的应用分层中提高的DAO层, Manager层, 可重用度高的Service, 都应该进行单元测试. + +1. 编写单元测试代码遵守BCDE原则, 以保证被测试模块的交付质量. + - B: Border, 边界值测试, 包括循环边界 特殊取值 特殊时间点 数据顺序等. + - C: Correct, 正确的输入, 并得到预期的结果. + - D: Design, 与设计文档相结合, 来编写单元测试. + - E: Error, 强制错误信息输入 (如: 非法数据 异常流程 非业务允许输入等), 并得到预期的结果. + +1. 对于数据库相关的查询, 更新, 删除等操作, 不能假设数据库里的数据是存在的, 或者直接操作数据库把数据插入进行, 请使用程序插入或者导入数据的方式来准备数据.(使用内存数据库就容易些) + +1. 和数据库相关的单元测试, 可以设定自动回滚机制, 不给数据库造成脏数据. 或者对单元测试产生的数据有明确的前后缀标识. + - 在RDC内部单元测试中, 使用 RDC_UNIT_TEST_ 的前缀标识数据 + +1. 对于不可测的代码建议做必要的重构, 使代码变得可测, 避免为了达到测试要求而书写不规范测试代码. + +1. 在设计评审阶段, 开发人员需要和测试人员一起确定单元测试范围, 单元测试最好覆盖所有测试用例(UC). + +1. 单元测试作为一种质量保障手段, 不建议在项目发布后补充单元测试用例, 建议在项目提测前完成单元测试. + +1. 为了更方便地进行单元测试, 业务代码应避免以下情况: + - 构造方法中做的事情过多. + - 存在过多的全局变量和静态方法. + - 存在过多的外部依赖. + - 存在过多的条件语句 (多层条件语句建议使用卫语句,策略模式,状态模式等方式重构) + +1. 不要对单元测试存在如下误解: + - 那是测试要干的事情. 本文是开发手册, 凡是本文内容都是与开发强相关的. + - 单元测试代码是多余的 + - 单元测试不需要维护 + - 单元测试和线上故障没有辩证关系 + +# 安全规约 +**`强制`** +1. 隶属于用户个人的页面或者功能必须进行权限控制校验。 + - 防止没有做水平权限校验就可随意访问、修改 删除 别人的数据, 比如查看他人的私信内容, 修改他人的订单 + +1. 用户敏感数据禁止直接展示, 必须对展示数据脱敏。 + - 查看个人手机号码会显示成:158****9119, 隐藏中间 4 位, 防止隐私泄露。 + +1. 用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定, 防止 SQL 注入, 禁止字符串拼接 SQL 访问数据库。 + +1. 用户请求传入的任何参数必须做有效性验证。 + - 忽略参数校验可能导致: + - page size 过大导致内存溢出 + - 恶意 order by 导致数据库慢查询 + - 任意重定向 + - SQL 注入 + - 反序列化注入 + - 正则输入源串拒绝服务 ReDoS + - Java 代码用正则来验证客户端的输入, 有些正则写法验证普通用户输入没有问题, 但是如果攻击人员使用的是特殊构造的字符串来验证, 有可能导致死循环的效果。 + +1. 禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。 + +1. 表单, AJAX 提交必须执行 CSRF 安全过滤 + - CSRF(Cross-site request forgery) 跨站请求伪造是一类常见编程漏洞。 + - 对于存在CSRF 漏洞的应用/网站, 攻击者可以事先构造好 URL, 只要受害者用户一访问, 后台便在用户不知情情况下对数据库中用户参数进行相应修改。 + > **个人理解** 万一ajax接口的API暴露了, 被别有用心的人利用就能构造好页面, 用于钓鱼什么的 + +1. 在使用平台资源, 譬如短信、邮件、电话、下单、支付, 必须实现正确的防重放限制, 如数量限制、疲劳度控制、验证码校验, 避免被滥刷、资损。 + - 如注册时发送验证码到手机, 如果没有限制次数和频率, 那么可以利用此功能骚扰到其它用户, 并造成短信平台资源浪费。 + +1. 发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过滤等风控策略。 + # MySQL规约 ## 建表规约 -- 表达是与否概念的字段,必须使用 is _ xxx 的方式命名,数据类型是 unsigned tinyint( 1 表示是,0 表示否 ) ,此规则同样适用于 odps 建表。 - - 任何字段如果为非负数,必须是 unsigned 。 -- 表名、字段名必须使用小写字母或数字 ; 禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。 -- 表名不使用复数名词。 - - 表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于 DO 类名也是单数形式,符合表达习惯。 -- 禁用保留字,如 desc 、 range 、 match 、 delayed 等,请参考 MySQL 官方保留字。 -- 唯一索引名为 uk _字段名 ; 普通索引名则为 idx _字段名。 - - uk _ 即 unique key;idx _ 即 index 的简称。 -- 小数类型为 decimal ,禁止使用 float 和 double 。 - - float 和 double 在存储的时候,存在精度损失的问题,很可能在值的比较时,得到不正确的结果。如果存储的数据范围超过 decimal 的范围,建议将数据拆成整数和小数分开存储。 -- 如果存储的字符串长度几乎相等,使用 char 定长字符串类型。 -- varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text ,独立出来一张表,用主键来对应,避免影响其它字段索引效率。 -- 表必备三字段: id , gmt _ create , gmt _ modified 。 - - 其中 id 必为主键,类型为 unsigned bigint 、单表时自增、步长为 1。 gmt _ create ,gmt _ modified 的类型均为 date _ time 类型。 - - GMT 是指格林尼治时间,这样的话,就是会让表的数据和时间关联上,具有一定数据分析价值 -- 表的命名最好是加上“业务名称_表的作用”。 - - 例如 :tiger _ task / tiger _ reader / mpp _ config -- 库名与应用名称尽量一致。 -- 如果修改字段含义或对字段表示的状态追加时,需要及时更新字段注释。 -- 字段允许适当冗余,以提高性能,但是必须考虑数据同步的情况。冗余字段应遵循: - - 1 ) 不是频繁修改的字段。 - - 2 ) 不是 varchar 超长字段,更不能是 text 字段。 - - 商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储类目名称,避免关联查询。 -- 单表行数超过 500 万行或者单表容量超过 2 GB ,才推荐进行分库分表。 - - 如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。 -- 合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。 - - 人的年龄用 unsigned tinyint( 表示范围 0-255,人的寿命不会超过 255 岁 ); - - 海龟就必须是 smallint ,但如果是太阳的年龄,就必须是 int; - - 如果是所有恒星的年龄都加起来,那么就必须使用 bigint 。 +**`强制`** +1. 表达是与否概念的字段, 必须使用 is_xxx 的方式命名, 数据类型是 unsigned tinyint (1 表示是, 0 表示否) + - 任何字段如果为非负数, 必须是 unsigned 。 + - 正例: 表达逻辑删除的字段名 is_deleted, 1表示删除, 0表示未删除. + > 个人理解: 是不是简单的整型枚举也可以这么用呢 + +1. 表名、字段名必须使用小写字母或数字; 禁止出现数字开头, 禁止两个下划线中间只出现数字。数据库字段名的修改代价很大, 因为无法进行预发布, 所以字段名称需要慎重考虑。 + - MySQL 在 Windows 下不区分大小写, 但在 Linux 下默认是区分大小写, 因此 数据库名, 表名 字段名 都不允许出现任何大写字母. + +1. 表名不使用复数名词。 + - 表名应该仅仅表示表里面的实体内容, 不应该表示实体数量, 对应于 DO 类名也是单数形式, 符合表达习惯。 + +1. 禁用保留字, 如 desc 、 range 、 match 、 delayed 等, 请参考 MySQL 官方保留字。 + +1. 主键索引名为 pk_字段名, 唯一索引名为 uk_字段名, 普通索引名则为 idx_字段名。 + - pk_ 即 primary key, uk_ 即 unique key, idx_ 即 index 的简称。 + +1. 小数类型为 decimal , 禁止使用 float 和 double 。 + - float 和 double 在存储的时候, 存在精度损失的问题, 很可能在值的比较时, 得到不正确的结果。 + - 如果存储的数据范围超过 decimal 的范围, 建议将数据拆成整数和小数分开存储。 + +1. 如果某字段存储的字符串长度几乎是固定的, 使用 char 定长字符串类型。 + +1. varchar 是可变长字符串, 不预先分配存储空间, 长度不要超过 500, + - 如果存储长度大于此值, 定义字段类型为 text , 独立出来一张表, 用主键来对应, 避免影响其它字段索引效率。 + +1. 表必备三字段: id, gmt_create, gmt_modified 。 + - 其中 id 必为主键, 类型为 unsigned bigint 、单表时自增、步长为 1。 + - gmt_create ,gmt_modified 的类型均为 date_time 类型, 前者现在时表示主动创建, 后者过去分词表示被动更新. + - GMT 是指格林尼治时间, 这样的话, 就是会让表的数据和时间关联上, 具有一定数据分析价值 + +**`推荐`** +1. 表的命名最好是加上“业务名称_表的作用”。 + - 正例: alipay_task force_project trade_config + +1. 库名与应用名称尽量一致。 + +1. 如果修改字段含义或对字段表示的状态追加时, 需要及时更新字段注释。 + +1. 字段允许适当冗余, 以提高查询性能, 但必须考虑数据一致, 冗余字段应遵循: + 1. 不是频繁修改的字段。 + 1. 不是 varchar 超长字段, 更不能是 text 字段。 + - 正例: 商品类目名称使用频率高, 字段长度短, 名称基本一成不变, 可在相关联的表中冗余存储类目名称, 避免关联查询。 + +1. 单表行数超过 500 万行或者单表容量超过 2 GB, 才推荐进行分库分表。 + - 如果预计三年后的数据量根本达不到这个级别, 请不要在创建表时就分库分表。 + +1. 合适的字符存储长度, 不但节约数据库表空间、节约索引存储, 更重要的是提升检索速度。 + - 如下表, 其中无符号值可以避免误存负数, 且扩大了表示范围. + + | 对象 | 年龄区间 | 类型 | 字节 | 表示范围 | + |:----:|:----:|:----|:----:|:----| + | 人 | 150岁之内 | unsigned tinyint | 1 | 0 - 255 | + | 龟 | 数百年 | unsigned smallint | 2 | 0 - 65535 | + | 恐龙化石 | 数千万年 | unsigned int | 4 | 0 - 约42.9亿 | + | 太阳 | 约50亿年 | unsigned bigint | 8 | 0 - 约10的19次幂 | ## 索引规约 -- 业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。 - - 不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的 ; - - 另外,即使在应用层做了非常完善的校验和控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生。 -- 超过三个表禁止 join 。需要 join 的字段,数据类型保持绝对一致 ; 多表关联查询时,保证被关联的字段需要有索引。 - - 即使双表 join 也要注意表索引、 SQL 性能。 -- 在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度。 - - 索引的长度与区分度是一对矛盾体,一般对字符串类型数据,长度为 20 的索引,区分度会高达 90%以上, - - 可以使用 count(distinct left( 列名, 索引长度 )) / count( * ) 的区分度来确定。 -- 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决。 - - 索引文件具有 B - Tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。 -- 如果有 order by 的场景,请注意利用索引的有序性。 order by 最后的字段是组合索引的一部分, - - 并且放在索引组合顺序的最后,避免出现 file _ sort 的情况,影响查询性能。 - - where a =? and b =? order by c; 索引: a_b_c -- 利用覆盖索引来进行查询操作,来避免回表操作。 - - 能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的一种效果,用 explain 的结果, extra 列会出现: using index 。 - - 覆盖索引是select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖。 - - **理解方式一**:索引是高效找到行的一个方法,但是一般数据库也能使用索引找到一个列的数据,因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据;当能通过读取索引就可以得到想要的数据,那就不需要读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引 - - **理解方式二**:是非聚集复合索引的一种形式,它包括在查询里的Select、Join和Where子句用到的所有列(即建索引的字段正好是覆盖查询条件中所涉及的字段,也即,索引包含了查询正在查找的数据)。 - - 如果一本书需要知道第 11 章是什么标题,会翻开第 11 章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的作用。 -- 利用延迟关联或者子查询优化超多分页场景。 -- MySQL 并不是跳过 offset 行,而是取 offset + N 行,然后返回放弃前 offset 行,返回N 行, - - 那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。 - - 先快速定位需要获取的 id 段,然后再关联: -- SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts最好。 - - 1 )consts 单表中最多只有一个匹配行 ( 主键或者唯一索引 ) ,在优化阶段即可读取到数据。 - - 2 )ref 指的是使用普通的索引 (normal index) 。 - - 3 )range 对索引进行范围检索。 - - explain 表的结果, type = index ,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range 还低,与全表扫描是小巫见大巫。 -- 建组合索引的时候,区分度最高的在最左边。 - - 如果 where a =? and b =? , a 列的几乎接近于唯一值,那么只需要单建 idx _ a 索引即可。 - - 存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如: where a >?and b =? 那么即使 a 的区分度更高,也必须把 b 放在索引的最前列。 -- 创建索引时避免有如下极端误解: - - 1 ) 误认为一个查询就需要建一个索引。 - - 2 ) 误认为索引会消耗空间、严重拖慢更新和新增速度。 - - 3 ) 误认为唯一索引一律需要在应用层通过“先查后插”方式解决。 +**`强制`** +1. 业务上具有唯一特性的字段, 即使是多个字段的组合, 也必须建成唯一索引。 + - 不要以为唯一索引影响了 insert 速度, 这个速度损耗可以忽略, 但提高查找速度是明显的 ; + - 另外, 即使在应用层做了非常完善的校验控制, 只要没有唯一索引, 根据墨菲定律, 必然有脏数据产生。 + +1. 超过三个表禁止 join。需要 join 的字段, 数据类型必须保持绝对一致; 多表关联查询时, 保证被关联的字段需要有索引。 + - 即使双表 join 也要注意表索引、SQL 性能。 + +1. 在 varchar 字段上建立索引时, 必须指定索引长度, 没必要对全字段建立索引, 根据实际文本区分度决定索引长度即可。 + - 索引的长度与区分度是一对矛盾体, 一般对字符串类型数据, 长度为 20 的索引, 区分度会高达 90% 以上, + - 可以使用 count(distinct left(列名, 索引长度))/count(*) 的区分度来确定。 + +1. 页面搜索严禁左模糊或者全模糊, 如果需要请使用搜索引擎来解决。 + - 索引文件具有 B-Tree 的最左前缀匹配特性, 如果左边的值未确定, 那么无法使用此索引。 + +**`推荐`** +1. 如果有 order by 的场景, 请注意利用索引的有序性。 order by 最后的字段是组合索引的一部分, + - 并且放在索引组合顺序的最后, 避免出现 file_sort 的情况, 影响查询性能。 + - 正例: `where a = ? and b = ? order by c;` 索引: a_b_c + - 反例: 索引中有范围查找, 那么索引有序无法利用, 如: WHERE a > 10 order by b; 索引 a_b 无法排序. + +1. 利用覆盖索引来进行查询操作, 来避免回表操作。 + - 如果一本书需要知道第 11 章是什么标题, 会翻开第 11 章对应的那一页吗? 目录浏览一下就好, 这个目录就是起到覆盖索引的作用. + - 能够建立索引的种类: 主键索引、唯一索引、普通索引, 而覆盖索引是一种查询的一种效果, 用 explain 的结果, extra 列会出现: using index 。 + - 覆盖索引是select的数据列只用从索引中就能够取得, 不必读取数据行, 换句话说查询列要被所建的索引覆盖。 + - **理解方式一**: 索引是高效找到行的一个方法, 但是一般数据库也能使用索引找到一个列的数据, 因此它不必读取整个行。毕竟索引叶子节点存储了它们索引的数据; 当能通过读取索引就可以得到想要的数据, 那就不需要读取行了。一个索引包含了(或覆盖了)满足查询结果的数据就叫做覆盖索引 + - **理解方式二**: 是非聚集复合索引的一种形式, 它包括在查询里的Select、Join和Where子句用到的所有列(即建索引的字段正好是覆盖查询条件中所涉及的字段, 也即, 索引包含了查询正在查找的数据)。 + +1. 利用延迟关联或者子查询优化超多分页场景。 + - MySQL 并不是跳过 offset 行, 而是取 offset + N 行, 然后返回放弃前 offset 行, 返回 N 行, 那当 offset 特别大的时候, 效率就非常的低下, 要么控制返回的总页数, 要么对超过特定阈值的页数进行 SQL 改写. + - 正例: 先快速定位需要获取的id段, 然后再关联: + - select a.* from table_a a, (select id from table_a where 条件 limit 100000, 20) b where a.id = b.id + +1. SQL 性能优化的目标: 至少要达到 range 级别, 要求是 ref 级别, 如果可以是 consts最好。 + 1. consts 单表中最多只有一个匹配行 ( 主键或者唯一索引 ) , 在优化阶段即可读取到数据。 + 1. ref 指的是使用普通的索引 (normal index) 。 + 1. range 对索引进行范围检索。 + - 反例: explain 表的结果, type = index , 索引物理文件全扫描, 速度非常慢, 这个 index 级别比较 range 还低, 与全表扫描是小巫见大巫。 + +1. 建组合索引的时候, 区分度最高的在最左边。 + - 正例: 如果 `where a = ? and b = ?`, a 列的几乎接近于唯一值, 那么只需要单建 idx_a 索引即可。 + - 存在非等号和等号混合判断条件时, 在建索引时, 请把等号条件的列前置。 + - 如: `where a > ? and b = ?` 那么即使 a 的区分度更高, 也必须把 b 放在索引的最前列。 + +1. 防止因字段类型不同造成的隐式转换, 导致索引失效. + +1. 创建索引时避免有如下极端误解: + 1. 宁缺毋滥. 认为一个查询就需要建一个索引。 + 1. 宁缺毋滥. 认为索引会消耗空间、严重拖慢更新和新增速度。 + 1. 抵制惟一索引. 认为业务的惟一性一律需要在应用层通过 “先查后插” 方式解决。 ## SQL规约 -- 不要使用 count( 列名 ) 或 count( 常量 ) 来替代 count( * ) , count( * ) 就是 SQL 92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 - - count( * ) 会统计值为 NULL 的行,而 count( 列名 ) 不会统计此列为 NULL 值的行。 -- count(distinct col) 计算该列除 NULL 之外的不重复数量。注意 count(distinct col 1, col 2 ) 如果其中一列全为 NULL ,那么即使另一列有不同的值,也返回为 0。 -- 当某一列的值全是 NULL 时, count(col) 的返回结果为 0,但 sum(col) 的返回结果为NULL ,因此使用 sum() 时需注意 NPE 问题。 - - 可以使用如下方式来避免 sum 的 NPE 问题: SELECT IF(ISNULL(SUM(g)) ,0, SUM(g))FROM table; -- 使用 ISNULL() 来判断是否为 NULL 值。注意: NULL 与任何值的直接比较都为 NULL。 - - 1 ) NULL<>NULL 的返回结果是 NULL ,而不是 false 。 - - 2 ) NULL=NULL 的返回结果是 NULL ,而不是 true 。 - - 3 ) NULL<>1 的返回结果是 NULL ,而不是 true 。 -- 在代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句。 -- 不得使用外键与级联,一切外键概念必须在应用层解决。 - - ( 概念解释 ) 学生表中的 student _ id 是主键,那么成绩表中的 student _ id 则为外键。如果更新学生表中的 student _ id ,同时触发成绩表中的 student _ id 更新, - - 则为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群 ; 级联更新是强阻塞,存在数据库更新风暴的风险 ; 外键影响数据库的插入速度。 -- 禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。 -- 数据订正时,删除和修改记录时,要先 select ,避免出现误删除,确认无误才能执行更新语句。 -- in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内。 -- 如果有全球化需要,所有的字符存储与表示,均以 utf -8 编码,那么字符计数方法 - - **注意**: - - SELECT LENGTH( "轻松工作" ); 返回为 12 - - SELECT CHARACTER _ LENGTH( "轻松工作" ); 返回为 4 - - 如果要使用表情,那么使用 utfmb 4 来进行存储,注意它与 utf -8 编码的区别。 -- TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE无事务且不触发 trigger ,有可能造成事故,故不建议在开发代码中使用此语句。 +**`强制`** +1. 不要使用 count(列名) 或 count(常量) 来替代 count(*), count( * ) 就是 SQL 92 定义的标准统计行数的语法, 跟数据库无关, 跟 NULL 和非 NULL 无关。 + - count( * ) 会统计值为 NULL 的行, 而 count(列名) 不会统计此列为 NULL 值的行。 + +1. count(distinct col) 计算该列除 NULL 之外的不重复行数。注意 count(distinct col1, col2) 如果其中一列全为 NULL, 那么即使另一列有不同的值, 也返回为 0。 + +1. 当某一列的值全是 NULL 时, count(col) 的返回结果为 0, 但 sum(col) 的返回结果为NULL , 因此使用 sum() 时需注意 NPE 问题。 + - 正例: 可以使用如下方式来避免 sum 的 NPE 问题: `SELECT IF(ISNULL(SUM(g)) ,0, SUM(g))FROM table;` + +1. 使用 ISNULL() 来判断是否为 NULL 值。注意: NULL 与任何值的直接比较都为 NULL。 + + | 表达式 | 返回值 | + |:----|:----:| + | NULL<>NULL | NULL 而不是 false | + | NULL=NULL | NULL 而不是 true | + | NULL<>1 | NULL 而不是 true | + +1. 在代码中写分页查询逻辑时, 若 count 为 0 应直接返回, 避免执行后面的分页语句。 + +1. 不得使用外键与级联, 一切外键概念必须在应用层解决。 + - 说明: 以学生和成绩的关系为例, 学生表中的 student_id 是主键, 那么成绩表中的 student_id 则为外键。 + - 如果更新学生表中的 student_id , 同时触发成绩表中的 student_id 更新, 则为级联更新。 + - 外键与级联更新适用于单机低并发, 不适合分布式、高并发集群; 级联更新是强阻塞, 存在数据库更新风暴的风险; + - 并且外键影响数据库的插入速度。 + +1. 禁止使用存储过程, 存储过程难以调试和扩展, 更没有移植性。 + +1. 数据订正时, 删除和修改记录时, 要先 select, 避免出现误删除, 确认无误才能执行更新语句。 + +**`推荐`** +1. in 操作能避免则避免, 若实在避免不了, 需要仔细评估 in 后边的集合元素数量, 控制在 1000 个之内。 + +1. 如果有全球化需要, 所有的字符存储与表示, 均以 utf-8 编码, 注意字符统计函数的区别 + - **注意**: + - SELECT LENGTH( "轻松工作" ); 返回为 12 + - SELECT CHARACTER _ LENGTH( "轻松工作" ); 返回为 4 + - 如果需要存储表情, 那么使用 utfmb4(这才是真正的UTF-8) 来进行存储, MySQL 的 utf-8 编码只能存放3字节的字符 + +1. TRUNCATE TABLE 比 DELETE 速度快, 且使用的系统和事务日志资源少, 但 TRUNCATE 无事务且不触发 trigger, 有可能造成事故, 故不建议在开发代码中使用此语句。 - TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。 +## ORM映射 + +**`强制`** +1. 在表查询中, 一律不要使用 * 作为查询的字段列表, 需要哪些字段必须明确写明。 + 1. 增加查询分析器解析成本。 + 1. 增减字段容易与 resultMap 配置不一致。 + +1. POJO 类的 boolean 属性不能 is 开头, 而数据库字段必须加 is_, 要求在 resultMap 中进行字段与属性之间的映射。 + - 参见定义POJO类以及数据库字段定义的规定, 在 `` 中增加映射是必须的, 在 Mybatis Generator 生成的代码中, 需要进行对应的修改. + +1. 不要用 resultClass 当返回参数, 即使所有类属性名与数据库字段一一对应, 也需要定义; 反过来, 每一个表也必然有一个与之对应。 + - 说明: 配置映射关系, 使字段与DO类解耦, 方便维护. + +1. `sql.xml` 配置中参数注意使用: #{}, #param# 不要使用 ${} 此种方式容易出现 SQL 注入。 + +1. iBATIS 自带的 queryForList(String statementName , int start , int size) 不推荐使用。 + - 其实现方式是在数据库取到 statementName 对应的SQL语句的所有记录, 再通过 subList 取 start, size 的子集合 线上因为这个原因曾经出现过 OOM + - 正例: + - Map map = new HashMap(); + - map.put("start", start); + - map.put("size", size); + +1. 不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。 + - resultClass="Hashtable", 会置入字段名和属性值, 但是值的类型不可控 + +1. 更新数据表记录时, 必须同时更新记录对应的 gmt_modified 字段值为当前时间。 -## ORM规约 -- 在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。 - - 1 ) 增加查询分析器解析成本。 - - 2 ) 增减字段容易与 resultMap 配置不一致。 -- POJO 类的 boolean 属性不能加 is ,而数据库字段必须加 is _,要求在 resultMap 中进行字段与属性之间的映射。 -- 不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义 ; 反过来,每一个表也必然有一个与之对应。 -- 配置映射关系,使字段与 DO 类解耦,方便维护。 -- xml 配置中参数注意使用:#{},# param # 不要使用${} 此种方式容易出现 SQL 注入。 -- iBATIS 自带的 queryForList(String statementName , int start , int size) 不推荐使用。 -- 其实现方式是在数据库取到 statementName 对应的 SQL 语句的所有记录,再通过 subList取 start , size 的子集合,线上因为这个原因曾经出现过 OOM 。 - - 在 sqlmap . xml 中引入 #start#, #size# - - Map map = new HashMap(); - - map.put("start", start); - - map.put("size", size); -- 不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出。 -- 更新数据表记录时,必须同时更新记录对应的 gmt _ modified 字段值为当前时间。 -- 不要写一个大而全的数据更新接口,传入为 POJO 类,不管是不是自己的目标更新字段,都进行 update table set c1=value1,c2=value2,c3=value3; - - 这是不对的。执行 SQL时,尽量不要更新无改动的字段, - - 一是易出错 ; - - 二是效率低 ; - - 三是 binlog 增加存储。 -- @ Transactional 事务不要滥用。事务会影响数据库的 QPS ,另外使用事务的地方需要考虑各方面的回滚方案, +**`推荐`** +1. 不要写一个大而全的数据更新接口, 传入为 POJO 类, 不管是不是自己的目标更新字段, + - 都进行 `update table set c1=value1,c2=value2,c3=value3;` + - 这是不对的。执行 SQL时, 尽量不要更新无改动的字段, 一是易出错; 二是效率低 ; 三是 binlog 增加存储。 + +1. @Transactional 事务不要滥用。事务会影响数据库的 QPS, 另外使用事务的地方需要考虑各方面的回滚方案, - 包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。 -- < isEqual >中的 compareValue 是与属性值对比的常量,一般是数字,表示相等时带上此条件 ; - - < isNotEmpty >表示不为空且不为 null 时执行 ; - - < isNotNull >表示不为 null 值时执行。 + +1. `` 中的 compareValue 是与属性值对比的常量, 一般是数字, 表示相等时带上此条件; + - `` 表示不为空且不为 null 时执行 ; + - `` 表示不为 null 值时执行。 # 工程规约 ## 应用分层 -- 图中默认上层依赖于下层,箭头关系表示可直接依赖,如:开放接口层可以依赖于Web 层,也可以直接依赖于 Service 层,依此类推: - - **开放接口层**:可直接封装 Service 接口暴露成 RPC 接口 ; 通过 Web 封装成 http 接口 ; 网关控制层等。 - - **终端显示层**:各个端的模板渲染并执行显示层。当前主要是 velocity 渲染, JS 渲染, JSP 渲染,移动端展示层等。 - - **Web 层**:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。 - - **Service 层**:相对具体的业务逻辑服务层。 - - **Manager 层**:通用业务处理层,它有如下特征: - - 1 ) 对第三方平台封装的层,预处理返回结果及转化异常信息 ; - - 2 ) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理 ; - - 3 ) 与 DAO 层交互,对 DAO 的业务通用能力的封装。 - - **DAO 层**:数据访问层,与底层 MySQL 、 Oracle 、 Hbase 进行数据交互。 - - 外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。 -- ( 分层异常处理规约 ) 在 DAO 层,产生的异常类型有很多,无法用细粒度异常进行catch ,使用 catch(Exception e) 方式,并 throw new DAOException(e) ,不需要打印日志, - - 因为日志在 Manager / Service 层一定需要捕获并打到日志文件中去,如果同台服务器再打日志,浪费性能和存储。在 Service 层出现异常时,必须记录日志信息到磁盘, - - 尽可能带上参数信息,相当于保护案发现场。如果 Manager 层与 Service 同机部署, - - 日志方式与 DAO 层处理一致,如果是单独部署,则采用与 Service 一致的处理方式。 Web 层绝不应该继续往上抛异常, - - 因为已经处于顶层,无继续处理异常的方式,如果意识到这个异常将导致页面无法正常渲染,那么就应该直接跳转到友好错误页面, - - 尽量加上友好的错误提示信息。开放接口层要将异常处理成错误码和错误信息方式返回。 -- 分层领域模型规约: - - DO(Data Object) :与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。 - - DTO(Data Transfer Object) :数据传输对象, Service 和 Manager 向外传输的对象。 - - BO(Business Object) :业务对象。可以由 Service 层输出的封装业务逻辑的对象。 - - QUERY :数据查询对象,各层接收上层的查询请求。注:超过 2 个参数的查询封装,禁止使用 Map 类来传输。 - - VO(View Object) :显示层对象,通常是 Web 向模板渲染引擎层传输的对象。 - - -![ER图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Alibaba/ApplicationLevels.png) - -## 二方库规约 -- 定义 GAV 遵从以下规则: - - **GroupID** 格式: com .{公司/ BU }.业务线. [ 子业务线 ] ,最多 4 级。 - - {公司/ BU } 例如: alibaba / taobao / tmall / aliexpress 等 BU 一级 ; 子业务线可选。 - - com . taobao . jstorm 或 com.alibaba.dubbo.register - - **ArtifactID** 格式:产品线名-模块名。语义不重复不遗漏,先到仓库中心去查证一下。 - - dubbo - client / fastjson - api / jstorm - tool - - **Version** :详细规定参考下方。 -- 二方库版本号命名方式:主版本号.次版本号.修订号 - - 1 ) 主版本号 主版本号:当做了不兼容的 API 修改,或者增加了能改变产品方向的新功能。 - - 2 ) 次版本号 次版本号:当做了向下兼容的功能性新增 ( 新增类、接口等 ) 。 - - 3 ) 修订号 修订号:修复 bug ,没有修改方法签名的功能加强,保持 API 兼容性。 - - 起始版本号必须为: 1.0.0 ,而不是 0.0.1 -- 线上应用不要依赖 SNAPSHOT 版本 ( 安全包除外 ); 正式发布的类库必须使用 RELEASE版本号升级+1 的方式,且版本号不允许覆盖升级,必须去中央仓库进行查证。 - - 不依赖 SNAPSHOT 版本是保证应用发布的幂等性。另外,也可以加快编译时的打包构建。 -- 二方库的新增或升级,保持除功能点之外的其它 jar 包仲裁结果不变。如果有改变,必须明确评估和验证, - - 建议进行 dependency : resolve 前后信息比对,如果仲裁结果完全不一致,那么通过 dependency : tree 命令,找出差异点,进行< excludes >排除 jar 包。 -- 二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。 -- 依赖于一个二方库群时,必须定义一个统一版本变量,避免版本号不一致。 - - 依赖 springframework - core ,- context ,- beans ,它们都是同一个版本,可以定义一个变量来保存版本: - - ${ spring . version },定义依赖的时候,引用该版本。 -- 禁止在子项目的 pom 依赖中出现相同的 GroupId ,相同的 ArtifactId ,但是不同的Version 。 -- 在本地调试时会使用各子项目指定的版本号,但是合并成一个 war ,只能有一个版本号出现在最后的 lib 目录中。 - - 曾经出现过线下调试是正确的,发布到线上出故障的先例。 -- 所有 pom 文件中的依赖声明放在< dependencies >语句块中,所有版本仲裁放在< dependencyManagement >语句块中。 - - < dependencyManagement >里只是声明版本,并不实现引入,因此子项目需要显式的声明依赖, version 和 scope 都读取自父 pom 。 - - 而< dependencies >所有声明在主 pom 的< dependencies >里的依赖都会自动引入,并默认被所有的子项目继承。 -- 二方库尽量不要有配置项,最低限度不要再增加配置项。 -- 为避免应用二方库的依赖冲突问题,二方库发布者应当遵循以下原则: - - 1 ) **精简可控原则**。移除一切不必要的 API 和依赖,只包含 Service API 、必要的领域模型对象、 Utils 类、常量、枚举等。 - - 如果依赖其它二方库,尽量是 provided 引入,让二方库使用者去依赖具体版本号 ; 无 log 具体实现,只依赖日志框架。 - - 2 ) **稳定可追溯原则**。每个版本的变化应该被记录,二方库由谁维护,源码在哪里,都需要能方便查到。除非用户主动升级版本,否则公共二方库的行为不应该发生变化。 +**`推荐`** +1. 图中默认上层依赖于下层, 箭头关系表示可直接依赖, 如: 开放接口层可以依赖于Web 层, 也可以直接依赖于 Service 层, 依此类推: + > ![Application Level](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Alibaba/ApplicationLevels.png) + - **开放接口层**: 可直接封装 Service 接口暴露成 RPC(Remote Procedure Calls) 接口; 通过 Web 封装成 http 接口; 进行网关安全控制, 流量控制等. + - **终端显示层**: 各个端的模板渲染并执行显示的层。当前主要是 Velocity 渲染, JS 渲染, JSP 渲染, 移动端展示层等。 + - **Web 层**: 主要是对访问控制进行转发, 各类基本参数校验, 或者不复用的业务简单处理等。 + - **Service 层**: 相对具体的业务逻辑服务层。 + - **Manager 层**: 通用业务处理层, 它有如下特征: + 1. 对第三方平台封装的层, 预处理返回结果及转化异常信息 ; + 1. 对 Service 层通用能力的下沉, 如缓存方案、中间件通用处理 ; + 1. 与 DAO 层交互, 对 DAO 的业务通用能力的封装, 对多个 DAO 的组合复用 + - **DAO 层**: 数据访问层, 与底层 MySQL、Oracle、 Hbase 等进行数据交互。 + - **外部接口或第三方平台**: 包括其它部门 RPC 开放接口, 基础平台, 其它公司的 HTTP 接口。 + +**`参考`** +1. **分层异常处理规约** 在 DAO 层, 产生的异常类型有很多, 无法用细粒度异常进行catch, 使用 catch(Exception e) 方式, 并 throw new DAOException(e), 不需要打印日志, + - 因为日志在 Manager/Service 层一定需要捕获并打到日志文件中去, 如果同台服务器再打日志, 浪费性能和存储。 + - 在 Service 层出现异常时, 必须记录出错日志信息到磁盘, 尽可能带上必要的参数信息(能够描述出场景的大致状态), 相当于保护案发现场。 + - 如果 Manager 层与 Service 同机部署, 日志方式与 DAO 层处理一致; 如果是单独部署, 则采用与 Service 一致的处理方式。 + - Web 层绝不应该继续往上抛异常, 因为已经处于顶层, 无继续处理异常的方式, 如果意识到这个异常将导致页面无法正常渲染, 那么就应该直接跳转到友好错误页面, 尽量加上友好的错误提示信息。 + - 开放接口层要将异常处理成错误码和错误信息方式返回。 + +1. 分层领域模型规约: + + | 缩写 | 名称 | 作用 | + |:----|:----|:----| + | DO | Data Object | 与数据库表结构一一对应, 通过DAO层向上传输数据源对象 | + | DTO | Data Transfer Object | 数据传输对象, Service或Manager向外传输的对象 | + | BO | Business Object | 由Service层输出的封装业务逻辑的对象 | + | AO | Application Object | 应用对象, 在Web层与Service层之间抽象的复用对象模型, 极为贴近展示层, 复用度不高 | + | VO | View Object | 显示层对象, 通常是Web向模板引擎传输的对象 | + | Qurey | | 数据查询对象, 各层接收上层的查询请求, 注意超过2个参数的查询封装, 禁止使用Map类来传输 | + +## 二方库依赖 +**`强制`** +1. 定义 GAV 遵从以下规则: + - **GroupID** 格式: `com.{公司/BU}.业务线.[子业务线]`, 最多 4 级。 + - {公司/BU} 例如: alibaba/taobao/tmall/aliexpress 等 BU; 子业务线可选。 + - 正例: com.taobao.jstorm 或 com.alibaba.dubbo.register + - **ArtifactID** 格式: 产品线名-模块名。语义不重复不遗漏, 先到仓库中心去查证一下。 + - 正例: dubbo-client / fastjson-api / jstorm-tool + - **Version** : 详细规定参考下方。 + +1. 二方库版本号命名方式: 主版本号.次版本号.修订号 + 1. 主版本号: 产品方向改变, 或者大规模API不兼容, 或者架构不兼容升级. + 1. 次版本号: 保持相对兼容性, 增加主要功能特性, 影响范围极小的API不兼容修改. + 1. 修订号: 保持完全兼容性, 修复 bug, 新增次要功能特性等 + - 说明: 注意起始版本号必须为: 1.0.0 , 而不是 0.0.1, 正式发布的类库必须先去中央仓库进行查证, 使版本号有延续性, 正式版本号不允许覆盖升级 + - 如当前版本 1.3.3 那么下一个合理的版本号 1.3.4 或者 1.4.0 或 2.0.0 +1. 线上应用不要依赖 SNAPSHOT 版本(安全包除外); + - 不依赖 SNAPSHOT 版本是保证应用发布的幂等性. 另外, 也可以加快编译时的打包构建. + +1. 二方库的新增或升级, 保持除功能点之外的其它 jar 包仲裁结果不变。 + - 如果有改变, 必须明确评估和验证, 建议进行 dependency:resolve 前后信息比对, + - 如果仲裁结果完全不一致, 那么通过 dependency:tree 命令, 找出差异点, 进行``排除 jar 包。 + +1. 二方库里可以定义枚举类型, 参数可以使用枚举类型, 但是接口返回值不允许使用枚举类型或者包含枚举类型的 POJO 对象。 + +1. 依赖于一个二方库群时, 必须定义一个统一版本变量, 避免版本号不一致。 + - 说明: 依赖 springframework-core, -context, -beans, 它们都是同一个版本 + - 可以定义一个变量来保存版本: ${spring.version}, 定义依赖的时候, 引用该版本。 + +1. 禁止在子项目的 pom 依赖中出现相同的 GroupId , 相同的 ArtifactId , 但是不同的Version 。 + - 在本地调试时会使用各子项目指定的版本号, 但是合并成一个war, 只能有一个版本号出现在最后的lib目录中, 可能出现线下调试是正确的, 发布到线上却出故障的问题. + +**`推荐`** +1. 所有 pom 文件中的依赖声明放在 `` 语句块中, 所有版本仲裁放在`` 语句块中。 + - `` 里只是声明版本, 并不实现引入, 因此子项目需要显式的声明依赖, version 和 scope 都读取自父 pom 。 + - 而 `` 所有声明在主 pom 的 `` 里的依赖都会自动引入, 并默认被所有的子项目继承。 + +1. 二方库不要有配置项, 最低限度不要再增加配置项。 + +**`参考`** +1. 为避免应用二方库的依赖冲突问题, 二方库发布者应当遵循以下原则: + 1. **精简可控原则** 移除一切不必要的 API 和依赖, 只包含 Service API、必要的领域模型对象、 Utils类、常量、枚举等。 + - 如果依赖其它二方库, 尽量是 provided 引入, 让二方库使用者去依赖具体版本号; 无 log 具体实现的依赖, 只依赖日志框架(例如不依赖Logback而是依赖SLF4J)。 + 1. **稳定可追溯原则** 每个版本的变化应该被记录, 二方库由谁维护, 源码在哪里, 都需要能方便查到。除非用户主动升级版本, 否则公共二方库的行为不应该发生变化。 ## 服务器规约 -- 高并发服务器建议调小 TCP 协议的 time _ wait 超时时间。 - - 操作系统默认 240 秒后,才会关闭处于 time _ wait 状态的连接, - - 在高并发访问下,服务器端会因为处于 time _ wait 的连接数太多,可能无法建立新的连接,所以需要在服务器上调小此等待值。 - - 在 linux 服务器上请通过变更/ etc / sysctl . conf 文件去修改该缺省值 ( 秒 ) :net . ipv 4. tcp _ fin _ timeout = 30 -- 调大服务器所支持的最大文件句柄数 (File Descriptor ,简写为 fd) 。 - - 主流操作系统的设计是将 TCP / UDP 连接采用与文件一样的方式去管理,即一个连接对应于一个 fd 。 - - 主流的 linux 服务器默认所支持最大 fd 数量为 1024,当并发连接数很大时很容易因为 fd 不足而出现“ open too many files ”错误,导致新的连接无法建立。 - - 建议将 linux服务器所支持的最大句柄数调高数倍 ( 与服务器的内存数量相关 ) -- 给 JVM 设置- XX :+ HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到 OOM 场景时输出dump 信息。 - - OOM 的发生是有概率的,甚至有规律地相隔数月才出现一例,出现时的现场信息对查错非常有价值。 -- 服务器内部重定向使用 forward; 外部重定向地址使用 URL 拼装工具类来生成,否则会带来 URL 维护不一致的问题和潜在的安全风险。 +**`推荐`** +1. 高并发服务器建议调小 TCP 协议的 time_wait 超时时间。 + - 说明: 操作系统默认 240 秒后, 才会关闭处于 time_wait 状态的连接, 在高并发访问下, 服务器端会因为处于 time_wait 的连接数太多,可能无法建立新的连接, 所以需要在服务器上调小此等待值。 + - 正例: 在 linux 服务器上请通过变更 `/etc/sysctl.conf` 文件去修改该缺省值 ( 秒 ) + - `net.ipv4.tcp_fin_timeout = 30` +1. 调大服务器所支持的最大文件句柄数 (File Descriptor, 简写为 fd) 。 + - 说明: 主流操作系统的设计是将 TCP/UDP 连接采用与文件一样的方式去管理, 即一个连接对应于一个 fd 。 + - 主流的 linux 服务器默认所支持最大 fd 数量为 1024, 当并发连接数很大时很容易因为 fd 不足而出现“ open too many files ”错误, 导致新的连接无法建立。 + - 建议将 linux服务器所支持的最大句柄数调高数倍 (与服务器的内存数值相关) + +1. 给 JVM 设置 `-XX:+HeapDumpOnOutOfMemoryError` 参数, 让 JVM 碰到 OOM 场景时输出dump 信息。 + - OOM 的发生是有概率的, 甚至有规律地相隔数月才出现一例, 出现时的现场信息对查错非常有价值。 + +1. 在线上生产环境, JVM 的 Xms 和 Xmx 设置一样的内存容量, 避免在GC后调整堆大小带来的压力 + +1. 服务器内部重定向使用 forward; 外部重定向地址使用 URL 拼装工具类来生成, 否则会带来 URL 维护不一致的问题和潜在的安全风险。 + + +# 专有名词 +1. POJO (Plain Ordinary Java Object): 在手册中, POJO专指只有 setter/getter/toString 的简单类, 包括 DO/DTO/BO/VO等. + +1. GAV (GroupId, AritifactId, Version): Maven 坐标, 是用来唯一标识 jar 包. + +1. OOP(Object Oriented Programming): 泛指类,对象的编程处理方式 + +1. ORM(Object Relation Mapping): 对象关系映射, 对象领域模型与底层数据之间的转换, 本文泛指 iBATIS Mybatis 等框架 + +1. NPE(java.lang.NullPointerException): 空指针异常 + +1. SOA(Service-Oriented Architecture): 面向服务架构, 它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署, 组合和使用, 有利于提升组件可重用性, 可维护性. + +1. 一方库: 本工程内部子项目模块依赖的库 jar包 +1. 二方库: 公司内部发布到中央仓库, 可供公司内部其他应用依赖的库 jar包 +1. 三方库: 公司之外的开源库 jar包 -# 安全规约 -- 隶属于用户个人的页面或者功能必须进行权限控制校验。 - - 防止没有做水平权限校验就可随意访问、操作别人的数据,比如查看、修改别人的订单。 -- 用户敏感数据禁止直接展示,必须对展示数据脱敏。 - - 查看个人手机号码会显示成:158****9119,隐藏中间 4 位,防止隐私泄露。 -- 用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入,禁止字符串拼接 SQL 访问数据库。 -- 用户请求传入的任何参数必须做有效性验证。 - - 忽略参数校验可能导致: - - page size 过大导致内存溢出 - - 恶意 order by 导致数据库慢查询 - - 任意重定向 - - SQL 注入 - - 反序列化注入 - - 正则输入源串拒绝服务 ReDoS - - Java 代码用正则来验证客户端的输入,有些正则写法验证普通用户输入没有问题,但是如果攻击人员使用的是特殊构造的字符串来验证,有可能导致死循环的效果。 -- 禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。 -- 表单、 AJAX 提交必须执行 CSRF 安全过滤。 - - CSRF(Cross - site request forgery) 跨站请求伪造是一类常见编程漏洞。对于存在CSRF 漏洞的应用/网站, - - 攻击者可以事先构造好 URL ,只要受害者用户一访问,后台便在用户不知情情况下对数据库中用户参数进行相应修改。 - - **个人理解** 就是使用的ajax可以在别的网站得到使用,而且构造好了改你的密码,又伪装成别的,就等着获取你的用户名,然后就能立即更改你的密码,所以进行Session校验应该就能防范,还有好多种情况吧,这只是其中一种 -- 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放限制,如数量限制、疲劳度控制、验证码校验,避免被滥刷、资损。 - - 如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其它用户,并造成短信平台资源浪费。 -- 发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁词过滤等风控策略。 - -## 注意 -- 安全意识很重要,不然毫无实际意义 -- NPE是空指针异常 -- OOM是内存溢出 diff --git a/Java/Android/AndroidBase.md b/Java/Android/AndroidBase.md new file mode 100644 index 0000000..6f40b3f --- /dev/null +++ b/Java/Android/AndroidBase.md @@ -0,0 +1,37 @@ +--- +title: Android基础 +date: 2023-12-19 15:01:26 +tags: +categories: +--- + +💠 + +- 1. [Android](#android) + - 1.1. [adb](#adb) +- 2. [Tips](#tips) + - 2.1. [Android和Linux传输文件](#android和linux传输文件) + +💠 2023-12-19 15:01:38 +**************************************** +# Android +> [developer.android.google.cn](https://developer.android.google.cn/) + +1. [apktool](https://bitbucket.org/iBotPeaches/apktool/overview) + +## adb +> Android Debug Bridge + +> [Android 调试桥 (adb)](https://developer.android.com/studio/command-line/adb) | [Arch Wiki: Android Debug Bridge](https://wiki.archlinux.org/title/Android_Debug_Bridge) + +- adb version +- adb devices 查看连接的设备 `前提安卓要开启开发者adb调试 和 确认授权的弹窗提示` +- adb pull 手机存储绝对路径 电脑绝对路径 `从手机拉文件到pc` +- adb push 电脑绝对路径 手机存储绝对路径 + + +# Tips +## Android和Linux传输文件 +- Termux 安装ssh +- adb +- ftp webdav 协议互访问 diff --git a/Java/Blog/Java-ClassLoad-Confuse.md b/Java/Blog/Java-ClassLoad-Confuse.md new file mode 100644 index 0000000..12a11fe --- /dev/null +++ b/Java/Blog/Java-ClassLoad-Confuse.md @@ -0,0 +1,51 @@ +--- +title: Java ClassLoad Confuse +date: 2020-04-29 01:06:35 +tags: +categories: +--- + +**目录 start** + +1. [类加载导致的迷惑](#类加载导致的迷惑) + 1. [主题](#主题) + 1. [解决方案](#解决方案) + 1. [场景](#场景) + +**目录 end**|_2020-04-29 11:57_| +**************************************** + +# 类加载导致的迷惑 +> 记录一次因Jar包冲突导致的类加载问题 + +> [参考: 为什么SpringBoot的 jar 可以直接运行?](https://blog.csdn.net/b644ROfP20z37485O35M/article/details/105671696) +> [参考: 重新看待Jar包冲突问题及解决方案](https://www.jianshu.com/p/100439269148) +> [参考: jar包冲突与inode](https://www.bbsmax.com/A/gVdnYM985W/) + +> [Maven: Introduction to the Dependency Mechanism](https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html) + +## 主题 +1. Java 类加载 +1. Linux inode +1. Jenkins Maven package + +## 解决方案 +1. 借助 Maven Helper 插件尽量避免Maven中依赖的冲突 maven-enforcer-plugin 插件 配合extra-enforcer-rules工具 +1. 或者 使用脚本等工具分析出 不同jar包里 限定名一致的类 + +## 场景 +1. SpringBoot项目 本身已经依赖了servlet3.1 因二方依赖被动引入了 servlet 2.5 版本 +1. 上线途中项目启动失败,抛出 NoSuchMethodError 可知加载了低版本的 servlet +1. 经过分析可知依赖冲突导致,但是测试在未经排除依赖冲突时又做了重新上线,项目竟正常启动,因此分析底层原因 + +> 项目依赖 `mvn dependency:tree -Dverbose -Dincludes=javax.servlet:` +```log +[INFO] --- maven-dependency-plugin:3.0.2:tree (default-cli) @ project--- +[INFO] Verbose not supported since maven-dependency-plugin 3.0 +[INFO] com.project:war:1.0.0-SNAPSHOT +[INFO] +- com.xuxueli:xxl-job-core:jar:1.9.0:compile +[INFO] | \- javax.servlet:javax.servlet-api:jar:3.1.0:compile +[INFO] \- com.xxx2:jar:2.1.3-RELEASE:compile +[INFO] \- com.xxx3:xx-api:jar:1.3:compile +[INFO] \- javax.servlet:servlet-api:jar:2.5:compile +``` diff --git a/Java/Blog/WebSocket-demo.md b/Java/Blog/WebSocket-demo.md new file mode 100644 index 0000000..b04ea4d --- /dev/null +++ b/Java/Blog/WebSocket-demo.md @@ -0,0 +1,314 @@ +--- +title: Java WebSocket Demo +date: 2019-07-07 11:43:37 +tags: + - Websocket +categories: + - Java +--- + +💠 + +- 1. [Java中的Websocket](#java中的websocket) +- 2. [服务端](#服务端) + - 2.1. [Javax](#javax) + - 2.1.1. [事件处理](#事件处理) + - 2.1.2. [服务端推送消息](#服务端推送消息) + - 2.2. [SpringMVC](#springmvc) + - 2.3. [Netty](#netty) + - 2.3.1. [Reactor Netty](#reactor-netty) + - 2.4. [Undertow](#undertow) +- 3. [性能测试对比](#性能测试对比) +- 4. [集群设计](#集群设计) +- 5. [客户端](#客户端) + - 5.1. [Java](#java) + - 5.2. [JS](#js) + +💠 2024-07-12 11:40:30 +**************************************** +# Java中的Websocket +JSR-356 + +> [【Spring Boot】WebSocket 的 6 种集成方式 ](https://juejin.cn/post/7111132777394733064) + +# 服务端 +先说结论: 生产尽量使用Netty实现,Javax 和 SpringMvc只适合少量连接时使用(`但开发是真简单`),详情见下方[性能测试对比](#性能测试对比)。 + +## Javax +Javax规范,Tomcat Jetty等容器实现 + +```java +@Configuration +public class WebSocketAutoConfig { + @Bean + public ServerEndpointExporter endpointExporter() { + return new ServerEndpointExporter(); + } +} + +@Slf4j +@Component +@ServerEndpoint("/websocket/{id}") +public class WebsocketServer { + + // 用于存储所有连接的session对象,从而获取所有连接 + private static Map connections = new HashMap<>(); + private Session session; + + @OnOpen + public void onOpen(Session session, @PathParam("id") String id) { + this.session = session; + // 将有客户端连接时将session保存起来 + connections.put(id, session); + } + + @OnMessage + public void onMessage(String text) throws IOException { + log.info("WebSocket连接数:" + connections.size()); + String[] s = text.split("]#!]"); + // 获取指定连接的session + Session ses = connections.get(s[0]); + ses.getBasicRemote().sendText(s[1]); + } + + @OnError + public void onError(Throwable throwable) { + log.error(throwable.getMessage()); + } + + @OnClose + public void onClosing() throws IOException { + // 关闭时将连接的session去除 + connections.remove(session); + session.close(); + } +} +``` + +1. 使用类级别注解`@ServerEndpoint("uri路径")`,将类标注为一个WebSocket端点 +1. 使用方法级别注解`@OnMessage`,使方法在WebSocket事件发生,而不在WebSocket消息发生时被调用 + +### 事件处理 + +| 注解 | 方法中可使用的形参 | +| ---------- | ---------------------------------------- | +| @OnOpen | WebSocket Session对象,EndpointConfig对象,URI中的参数(需使用@PathParam) | +| @OnMessage | WebSocket Session对象,EndpointConfig对象,URI中的参数(需使用@PathParam),消息 | +| @OnError | Throwable对象,Session,URI中的参数(需使用@PathParam) | +| @OnClose | CloseReason,URI中的参数(需使用@PathParam) | + +### 服务端推送消息 + +WebSocket中 RemoteEndpoint 接口和它的子类( RemoteEndpoint.Basic 和 RemoteEndpoint.Async )提供了发送消息的所有方法,我们可以从Session中获取到RemoteEndpoint实例,从而发送消息 +如:`session.getBasicRemote().sendText(text);` + +************************ + +## SpringMVC +SpringMVC封装 Tomcat Jetty等容器实现 + +```java +@Slf4j +@Configuration +@EnableWebSocket +public class WebSocketConfig implements WebSocketConfigurer { + @Autowired + private MyWebSocketHandler socketHandler; + @Override + public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { + registry.addHandler(socketHandler, "/ws/*/").setAllowedOrigins("*"); + } + + + /** + * 该设置对 Javax 方式也生效 + * @see org.apache.tomcat.websocket.WsFrameBase#WsFrameBase + * @see java.nio.HeapByteBuffer + * @see java.nio.HeapByteBuffer + */ + @Bean + public ServletServerContainerFactoryBean createWebSocketContainer() { + ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); + // ws 传输数据的时候,单个消息过大会导致缓冲区溢出,接收不到该消息,所以按需设置bufferSize的大小 + // 注意: 这里设置的大小是每个连接初始化 HeapByteBuffer 的实际大小,也就是设置多大每个连接就会占用多大内存,要慎重考虑 + // https://my.oschina.net/xiaoshushu/blog/1586349 + + // 此时一个Ws连接会申请4Kib堆内存 + int kib = 1024; + int quota = 2; + container.setMaxTextMessageBufferSize(quota * kib); // HeapCharBuffer + container.setMaxBinaryMessageBufferSize(quota * kib); // HeapByteBuffer + + container.setMaxSessionIdleTimeout(15 * 60000L); + return container; + } +} + +@Slf4j +@Service +public class MyWebSocketHandler extends TextWebSocketHandler { + + /** + * ws建立连接 + * @param session websocket session + */ + @Override + public void afterConnectionEstablished(WebSocketSession session) { + } + + /** + * ws连接断开 + */ + @Override + public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { + } + + /** + * ws收到消息时的方法 + */ + @Override + protected void handleTextMessage(WebSocketSession session, TextMessage message) { + } +} +``` +> 使用 +1. 推送消息 session.sendMessage(new TextMessage("text")); + +## Netty +> [Gitee: Demo](https://gitee.com/gin9/JavaBase/tree/master/netty/src/main/java/netty/websocket) +> [Netty websocket springboot starter](https://gitee.com/gin9/netty-ws-starter) + +[SpringBoot整合Netty处理WebSocket(支持url参数)](https://blog.csdn.net/RisenMyth/article/details/104441155) + +通过go开发的客户端压测`在多个Docker容器中运行(规避65535个数的端口限制)`, 16G电脑可以轻松发起和支撑百万级ws活跃连接。 + +### Reactor Netty +> [Official Doc](https://projectreactor.io/docs/netty/release/reference/index.html#http-server) + +[Gitee: Demo](https://gitee.com/gin9/JavaBase/tree/master/netty/src/main/java/reactor/websocket) `设计和开发方式和传统Netty不一样,底层用的Netty` + +## Undertow + +> [doc](http://undertow.io/undertow-docs/undertow-docs-2.0.0/index.html#websockets) + +************************ + +# 性能测试对比 +- 相同的 JDK,JVM参数,处理逻辑 等。 +- 指标:最大连接数,请求应答模型 延迟分布情况、吞吐量 + +> 压测过程中遇到的问题 +1. 客户端发起连接需要设置最大打开文件数 ulimit -n 80000 +1. 服务端建立到 28232 个连接后 遇到 Cannot assign requested address + - `cat /proc/sys/net/ipv4/ip_local_port_range` 设置项代表Linux作为客户端(与服务端建立连接时会在区间内随机分配一个端口给客户端进程)可分配的端口范围(防止耗尽端口) + - 临时修改 `echo 1024 65000 > /proc/sys/net/ipv4/ip_local_port_range` + - 如果客户端在Docker容器中,需在 docker run 时加上 `--sysctl net.ipv4.ip_local_port_range="1024 65000"` + +************************ + +> 结论 Netty性能更好,javax SpringMVC 实现成本更低 +- 得益于Netty的IO架构,Buffer设计机制,资源占用和吞吐量远胜于Tomcat实现。 + - Tomcat 缓冲区实现为 `org.apache.tomcat.websocket.WsFrameBase#WsFrameBase` 使用的 ByteBuffer 直接按最大缓冲区分配容量 **堆内存** + - 缺点:当某个ws业务偶尔会大数据收发,平时使用数据包很小(例如启动游戏时数据初始化和游戏过程),比较难配置最大容量。 + - 配置太大则支撑连接数会下降,配置太小读不到数据无法支撑业务 或 降低业务体验(如果容量很小数据要多轮,游戏初始化的等待时间就会更长) + - Netty中使用到的是 池化内存`PooledByteBufAllocator` 和申请时扩缩容机制 `AdaptiveRecvByteBufAllocator` 大大降低了数据读取时占用的缓冲内存,平衡了缓存池利用率和数据读取效率 **堆外内存** +- javax MVC 这两种底层实现都是Tomcat等Web容器,性能没太大区别,优势是开发成本很低 + +> 基础环境 +- 硬件 i5-10400F CPU @ 2.90GHz +- JVM参数: -Xmx1000m +- 服务端:消息逻辑为收到文本ping返回文本pong, 设置最大读缓存大小为64K +- 客户端:连续创建连接,定时每分钟发送ping文本 + +> 结果 +- Javax 约2500个后OOM +- SpringMVC 约2600个后OOM +- Netty 正常建立3000个连接 + +************************ + +> 资源对比 + +CPU占用都不高 0.5%以下波动 + +| 连接数 | Javax | Mvc | Netty | Jetty | +|:---|:---|:---|:---|:---| +| 1000 | 占用300M | 占用300M | | | +| 3000 | 占用850M | 占用850M | 占用150M内存 | | + +************************ + +# 集群设计 +核心矛盾在于长连接是有状态的且无法共享,但通常应用服务端都是无状态的集群,方便横向快速扩容 + +> 简单实现 缺点:消息冗余推送 +1. 用Redis或者Nacos等注册中心维护一份用户id和服务器ip的映射 +1. 服务端主动推消息时从注册中心拿到用户id所在的服务器,再将消息转发过去做真正的推送 + +************************ + +# 客户端 +## Java +Java WebSocket客户端端点 + +```java +@ClientEndpoint +public class WebSocketClient { + + private Session session; + + @OnOpen + public void onOpen(Session session) { + this.session = session; + } + + @OnMessage + public void onMessage(String text) throws IOException { + session.getBasicRemote().sendText(text); + } + + @OnError + public void onError(Throwable throwable) { + log.error(throwable.getMessage()); + } + + @OnClose + public void onClosing() throws IOException { + log.info("连接关闭"); + session.close(); + } + + public void sendMessage(String toId, String text) throws IOException { + text = toId + "]#!]" + text; + onMessage(text); + } + + // 连接服务器端点 + public static WebSocketClient connect(String url) throws Exception { + WebSocketContainer wsc = ContainerProvider.getWebSocketContainer(); + WebSocketClient client = new WebSocketClient(); + wsc.connectToServer(client, new URI(url)); + return client; + } +} +``` + +## JS +```js + var ws = new WebSocket("ws:localhost:8080/websocket"); + ws.onopen = function () { + ws.send("hello"); + }; + + ws.onmessage = function (evt) { + console.log(evt.data) + }; + + ws.onclose = function (evt) { + console.log("error"); + }; + + ws.onerror = function (evt) { + console.log("error"); + }; +``` diff --git a/Java/Blog/instantiation-object.md b/Java/Blog/instantiation-object.md new file mode 100644 index 0000000..a7b250d --- /dev/null +++ b/Java/Blog/instantiation-object.md @@ -0,0 +1,41 @@ +--- +title: Java实例化对象的几种方式 +date: 2020-02-02 18:45:47 +tags: +categories: +--- + +💠 + +- 1. [Java 实例化对象的方式](#java-实例化对象的方式) + - 1.1. [new](#new) + - 1.2. [反射](#反射) + - 1.3. [clone](#clone) + - 1.4. [反序列化](#反序列化) + - 1.5. [Unsafe](#unsafe) + +💠 2024-06-02 17:50:57 +**************************************** +# Java 实例化对象的方式 +> [Github: 实例代码](https://github.com/kuangcp/JavaBase/blob/master/class/src/test/java/com/github/kuangcp/instantiation/InstantiationAndConstructorTest.java) + +> [Java创建对象的五种方式](https://juejin.im/post/5d44530a6fb9a06aed7103bd) + +## new +TODO + +## 反射 +1. 获取Class对象 +1. 反射获取构造器方法或者调用 newInstance 方法(实际上是调用空构造器方法) 进行实例化 + +## clone +需要规避深拷贝问题,默认只复制第一层的成员属性 + + +## 反序列化 +TODO + +## Unsafe +**sun.misc.Unsafe** 中提供 `allocateInstance` 方法,仅通过Class对象就可以创建此类的实例对象,而且不需要调用其构造函数、初始化代码、JVM安全检查等。 +它抑制修饰符检测,也就是即使构造器是private修饰的也能通过此方法实例化,只需提类对象即可创建相应的对象。 +由于这种特性,allocateInstance在 java.lang.invoke、Objenesis(提供绕过类构造器的对象生成方式)、Gson(反序列化时用到)中都有相应的应用。 diff --git a/Java/Blog/reduce-if-else-for-java.md b/Java/Blog/reduce-if-else-for-java.md new file mode 100644 index 0000000..d96acc3 --- /dev/null +++ b/Java/Blog/reduce-if-else-for-java.md @@ -0,0 +1,26 @@ +--- +title: 减少 Java 中的 if else +date: 2020-05-04 00:36:52 +tags: +categories: + - Java +--- + +**目录 start** + +1. [减少Java中的if else](#减少java中的if-else) + +**目录 end**|_2020-05-04 19:34_| +**************************************** +# 减少Java中的if else +> [如何解决代码中if…else 过多的问题 ](https://www.cnblogs.com/eric-shao/p/10115577.html) + +1. 表驱动 +1. 职责链模式 +1. 注解驱动 +1. 事件驱动 +1. 有限状态机 +1. Optional +1. Assert +1. 多态 + diff --git a/Java/DesignPattern.md b/Java/DesignPattern.md new file mode 100644 index 0000000..e382601 --- /dev/null +++ b/Java/DesignPattern.md @@ -0,0 +1,577 @@ +--- +title: 设计模式 +date: 2018-12-20 10:43:55 +tags: + - 设计模式 +categories: +--- + +💠 + +- 1. [软件设计的一些原则](#软件设计的一些原则) + - 1.1. [思维原则](#思维原则) + - 1.1.1. [奥卡姆剃刀原理](#奥卡姆剃刀原理) + - 1.2. [首要原则](#首要原则) + - 1.2.1. [不要复制粘贴](#不要复制粘贴) + - 1.2.2. [减法优于加法](#减法优于加法) + - 1.2.3. [抽象优于实现](#抽象优于实现) + - 1.2.4. [组合优于继承](#组合优于继承) + - 1.2.5. [查询与命令分离](#查询与命令分离) + - 1.2.6. [够用原则](#够用原则) + - 1.3. [面向对象的 S.O.L.I.D 原则](#面向对象的-solid-原则) + - 1.3.1. [单一职责原则](#单一职责原则) + - 1.3.2. [里氏代换原则](#里氏代换原则) + - 1.3.3. [接口隔离原则](#接口隔离原则) + - 1.3.4. [依赖倒置原则](#依赖倒置原则) + - 1.3.5. [迪米特法则](#迪米特法则) + - 1.3.6. [开闭原则](#开闭原则) + - 1.4. [衍生原则](#衍生原则) + - 1.4.1. [共同封闭原则](#共同封闭原则) + - 1.4.2. [共同重用原则](#共同重用原则) + - 1.4.3. [好莱坞原则](#好莱坞原则) + - 1.4.4. [高内聚低耦合](#高内聚低耦合) + - 1.4.5. [惯例优于配置原则](#惯例优于配置原则) + - 1.4.6. [关注点分离](#关注点分离) + - 1.4.7. [契约式设计](#契约式设计) + - 1.4.8. [无环依赖原则](#无环依赖原则) +- 2. [设计模式](#设计模式) + - 2.1. [设计模式概览](#设计模式概览) + - 2.1.1. [创建型设计模式](#创建型设计模式) + - 2.1.2. [结构型设计模式](#结构型设计模式) + - 2.1.3. [行为设计模式](#行为设计模式) + - 2.2. [设计模式详述](#设计模式详述) + - 2.2.1. [工厂模式](#工厂模式) + - 2.2.2. [抽象工厂模式](#抽象工厂模式) + - 2.2.3. [单例模式](#单例模式) + - 2.2.4. [建造者模式](#建造者模式) + - 2.2.5. [原型模式](#原型模式) + - 2.2.6. [策略模式](#策略模式) + - 2.2.7. [中介者模式](#中介者模式) + - 2.2.8. [观察者模式](#观察者模式) + - 2.2.9. [状态模式](#状态模式) + - 2.2.10. [命令模式](#命令模式) + - 2.2.11. [适配器模式](#适配器模式) + - 2.2.12. [桥接模式](#桥接模式) +- 3. [实践](#实践) + - 3.1. [经验之谈](#经验之谈) + - 3.2. [反模式](#反模式) + +💠 2024-08-09 15:23:34 +**************************************** +# 软件设计的一些原则 + +[tdd-training](https://github.com/Pragmatists/tdd-trainings) + +## 思维原则 +### 奥卡姆剃刀原理 + +## 首要原则 +### 不要复制粘贴 +`DRY Don’t Repeat Yourself ` + +是一个最简单的法则,也是最容易被理解的。但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,这并不是一件容易的事)。 +它意味着,当我们在两个或多个地方的时候发现一些相似的代码的时候,我们需要把他们的共性抽象出来形一个唯一的新方法,并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。 + +### 减法优于加法 +``` +Keep It Simple & Stupid +Keep It Slim & Sexy +Keep It Short & Safe +Keep It Silly & Sound +Keep It Simple, Spiritually +``` + +KISS原则在设计上可能最被推崇的,在家装设计,界面设计,操作设计上,复杂的东西越来越被众人所BS了,而简单的东西越来越被人所认可 + +“宜家”(IKEA)简约、效率的家居设计、生产思路;“微软”(Microsoft)“所见即所得”的理念;“谷歌”(Google)简约、直接的商业风格,无一例外的遵循了“kiss”原则, + +也正是“kiss”原则,成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad将这个原则实践到了极至。 + +> 把一个事情搞复杂是一件简单的事,但要把一个复杂的事变简单,这是一件复杂的事。 + +### 抽象优于实现 +`Program to an interface, not an implementation` + +- 这是设计模式中最根本的哲学,注重接口,而不是实现,依赖接口,而不是实现。接口是抽象是稳定的,实现则是多种多样的。 +- 后面提到的依赖倒置原则,就是这个原则的的另一种样子。 + +多思考,把业务归类,提取出规律:包括哪些是变化的,哪些是不会变化的。通过对这些内容的抽象的出实现流程,不变的内容就是代码框架, 变化的内容可以封装成接口 + +### 组合优于继承 +`Composition over inheritance` + +- 多使用组合而不是继承, 但是这个观点是存在一定的争议的, 还是要有度的,合理搭配最为重要 + - 组合就是将原来继承方式中的父类放到子类作为属性? + +> 组合 +1. (对象)组合是一种通过创建一个组合了其它对象的对象,从而获得新功能的复用方法。 +2. 将功能委托给所组合的一个对象,从而获得新功能。 +3. 有些时候也称之为"聚合"(aggregation)或"包容"(containment),尽管有些作者对这些术语赋予了专门的含义 + +> [参考: 组合、委托与继承,面向对象中类之间的基本关系漫游](http://www.cnblogs.com/narutow/p/8117352.html) +> [参考: 优先使用(对象)组合,而非(类)继承](https://www.xuebuyuan.com/1639556.html) + +### 查询与命令分离 +`CQS: Command-Query Separation` + +- 查询命令分离原则 + - 查询:当一个方法返回一个值来回应一个问题的时候,它就具有查询的性质; + - 命令:当一个方法要改变对象的状态的时候,它就具有命令的性质; + +### 够用原则 +`YAGNI You Ain’t Gonna Need It` + +- 这个原则简而言之为——只考虑和设计必须的功能,避免过度设计。只实现目前需要的功能,在以后需要更多功能时,可以再进行添加。 + - 如无必要,勿增复杂性。 软件开发先是一场沟通博弈。 + +******************* + +## 面向对象的 S.O.L.I.D 原则 +一般来说这是面向对象的五大设计原则, Solid(稳定的) + +| | | +|:----|:----| +| Single Responsibility Principle | 单一职责原则 +| Open Closed Principle | 开闭原则 +| Liskov Substitution Principle | 里氏替换 +| Law of Demeter | 迪米特法则 +| Interface Segregation Preciple | 接口隔离原则 +| Dependence Inversion Principle | 依赖倒置原则 + +### 单一职责原则 +`Single Responsibility Principle (SRP)` + +- 关于单一职责原则,其核心的思想是:`一个类,只做一件事,并把这件事做好,且只有一个引起它变化的原因。` + - Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。 + - Windows是这一原则的反面示例。几乎所有的程序都交织耦合在一起。 + +### 里氏代换原则 +`Liskov substitution principle (LSP) ` + +- 软件工程大师Robert C. Martin把里氏代换原则最终简化为一句话:`“Subtypes must be substitutable for their base types”。` +- 也就是,子类必须能够替换成它们的基类。 +> 即:子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。另外,不应该在代码中出现if/else之类对子类类型进行判断的条件。 +> 里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。 + +- 这么说来,似乎有点教条化,我非常建议大家看看这个原则个两个最经典的案例——`“正方形不是长方形”`和`“鸵鸟不是鸟”`。 +- 通过这两个案例,你会明白《墨子小取》中说的——`“娣,美人也,爱娣,非爱美人也….盗,人也;恶盗,非恶人也。”`——妹妹虽然是美人,但喜欢妹妹并不代表喜欢美人。 +- 盗贼是人,但讨厌盗贼也并不代表就讨厌人类。这个原则让你考虑的不是语义上对象的间的关系,而是实际需求的环境。 +- 在很多情况下,在设计初期我们类之间的关系不是很明确,LSP则给了我们一个判断和设计类之间关系的基准:需不需要继承,以及怎样设计继承关系。 + +### 接口隔离原则 +` Interface Segregation Principle (ISP) ` + +- 接口隔离原则意思是把功能实现在接口中,而不是类中,使用多个专门的接口比使用单一的总接口要好。 +- 举个例子,我们对电脑有不同的使用方式,比如:写作,通讯,看电影,打游戏,上网,编程,计算,数据等,如果我们把这些功能都声明在电脑的抽类里面,那么,我们的上网本,PC机,服务器, +- 笔记本的实现类都要实现所有的这些接口,这就显得太复杂了。所以,我们可以把其这些功能接口隔离开来,比如:工作学习接口,编程开发接口,上网娱乐接口,计算和数据服务接口,这样,我们的不同功能的电脑就可以有所选择地继承这些接口。 +- 这个原则可以提升我们“搭积木式”的软件开发。对于设计来说,Java中的各种Event Listener和Adapter,对于软件开发来说,不同的用户权限有不同的功能,不同的版本有不同的功能,都是这个原则的应用。 + +### 依赖倒置原则 +`Dependency Inversion Principle (DIP)` + +高层模块不应该依赖于低层模块的实现,而是依赖于高层抽象。 + +举个例子,墙面的开关不应该依赖于电灯的开关实现,而是应该依赖于一个抽象的开关的标准接口,这样,当我们扩展程序的时候,我们的开关同样可以控制其它不同的灯,甚至不同的电器。 + +也就是说,电灯和其它电器继承并实现我们的标准开关接口,而我们的开关产商就可不需要关于其要控制什么样的设备,只需要关心那个标准的开关标准。这就是依赖倒置原则。 + +这就好像浏览器并不依赖于后面的web服务器,其只依赖于HTTP协议。这个原则实在是太重要了,社会的分工化,标准化都是这个设计原则的体现。 + +下面有几点指导意见,帮助你避免在面向对象设计中违反依赖倒置原则: +1. 变量不能持有具体类的引用,就像订单方法代码中,你看不到new一样。 +1. 不要让派生自具体类,要派生就派生抽象类abstract +1. 不要覆盖基类中已实现的方法,除非你要覆盖的是比较特殊的一部分代码。 + +### 迪米特法则 +`Law of Demeter` + +又称 “最少知识原则” `(Principle of Least Knowledge)`其来源于1987年荷兰大学的一个叫做Demeter的项目。 +- Craig Larman把Law of Demeter又称作“不要和陌生人说话”。在《程序员修炼之道》中讲LoD的那一章叫作“解耦合与迪米特法则”。 +- 关于迪米特法则有一些很形象的比喻: + - 如果你想让你的狗跑的话,你会对狗狗说还是对四条狗腿说? + - 如果你去店里买东西,你会把钱交给店员,还是会把钱包交给店员让他自己拿? + +正式表述如下: +- 对于对象 ‘O’ 中一个方法’M',M应该只能够访问以下对象中的方法: + - 对象O; + - 与O直接相关的 Component Object; + - 由方法 M 创建或者实例化的对象; + - 作为方法 M 的参数的对象。 + +> [参考: 迪米特法则与重构](http://zhangyi.xyz/demeter-law-and-refactoring/) + +### 开闭原则 +`Open/Closed Principle (OCP)` + +- 关于开发封闭原则,其核心的思想是:模块是可扩展的,而不可修改的。也就是说,`对扩展是开放的,而对修改是封闭的。` + - 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。 + - 对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。 + +为何使用 +1. 开闭原则对单元测试的影响, 如果一个模块不具备开闭原则, 那么在改动该模块的时候对应的单元测试就要重写了, 当然 没有单元测试就另说 +1. 开闭原则可以提高复用性 +1. 开闭原则可以提高可维护性 + +如何使用 +1. 抽象约束 +1. 元数据控制模块行为 +1. 制定项目章程 +1. 封装变化 + +************************ + +## 衍生原则 +### 共同封闭原则 +`Common Closure Principle(CCP) ` + +一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中所有的类。 +一个更简短的说法是:一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。 + +CCP原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。 + +CCP延伸了开闭原则(OCP)的“关闭”概念,当因为某个原因需要修改时,把需要修改的范围限制在一个最小范围内的包里。 + +### 共同重用原则 +`Common Reuse Principle (CRP)` + +包的所有类被一起重用。如果你重用了其中的一个类,就重用全部。换个说法是,没有被一起重用的类不应该被组合在一起。CRP原则帮助我们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。 + +当一个包发生了改变,并发布新的版本,使用这个包的所有用户都必须在新的包环境下验证他们的工作,即使被他们使用的部分没有发生任何改变。因为如果包中包含有未被使用的类,即使用户不关心该类是否改变,但用户还是不得不升级该包并对原来的功能加以重新测试。 + +CCP则让系统的维护者受益。CCP让包尽可能大(CCP原则加入功能相关的类),CRP则让包尽可能小(CRP原则剔除不使用的类)。它们的出发点不一样,但不相互冲突。 + +### 好莱坞原则 +`Hollywood Principle` + +好莱坞原则就是一句话 —— `don’t call us, we’ll call you.`。 + +在好莱坞,演员和演艺公司建立合约后,只能在家等待,由演艺公司对整个娱乐项目完全控制,演员只能被动式的差使,在需要的环节完成自己的演出。 +也就是说,所有的组件都是被动的,所有的组件初始化和调用都由容器负责。组件处在一个容器当中,由容器负责管理。 + +简单的来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在: +1. 不创建对象,而是描述创建对象的方式。 +1. 在代码中,对象与服务没有直接联系,而是容器负责将这些联系在一起。 + +控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。 +好莱坞原则就是IoC(Inversion of Control)或DI(Dependency Injection)的基础原则。这个原则很像依赖倒置原则,依赖接口,而不是实例,但是这个原则要解决的是怎么把这个实例传入调用类中 + +你可能把其声明成成员,你可以通过构造函数,你可以通过函数参数。但是IoC可以让你通过配置文件,一个由Service Container 读取的配置文件来产生实际配置的类。 + +但是程序也有可能变得不易读了,程序的性能也有可能还会下降。 + +### 高内聚低耦合 +` High Cohesion & Low/Loose coupling ` + +- 这个原则是UNIX操作系统设计的经典原则,把模块间的耦合降到最低,而努力让一个模块做到精益求精。 + - 内聚:一个模块内各个元素彼此结合的紧密程度 + - 耦合:一个软件结构内不同模块之间互连程度的度量 +- 内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身 + +> 凝聚>松耦合>重用 [参考: 为什么我停止使用Spring?](http://www.jdon.com/forum/messageList.shtml?thread=45977#23144139) + +### 惯例优于配置原则 +`Convention over Configuration(CoC)` + +简单点说,就是将一些公认的配置方式和信息作为内部缺省的规则来使用。 + +例如,Hibernate的映射文件,如果约定字段名和类属性一致的话,基本上就可以不要这个配置文件了。你的应用只需要指定不convention的信息即可,从而减少了大量convention而又不得不花时间和精力啰里啰嗦的东东。配置文件很多时候相当的影响开发效率。 + +Rails 中很少有配置文件(但不是没有,数据库连接就是一个配置文件),Rails 的fans号称期开发效率是 java 开发的 10倍,估计就是这个原因。 + +Maven也使用了CoC原则,当你执行 `mvn compile` 命令的时候,不需要指源文件放在什么地方,而编译以后的class文件放置在什么地方也没有指定,这就是CoC原则。 + +### 关注点分离 +`Separation of Concerns (SoC)` + +是计算机科学中最重要的努力目标之一。这个原则,就是在软件开发中,通过各种手段,将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题,就是相对较易解决的。 + +问题太过于复杂,要解决问题需要关注的点太多,而程序员的能力是有限的,不能同时关注于问题的各个方面。正如程序员的记忆力相对于计算机知识来说那么有限一样, + +程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候,如果我们把所有的东西混在一起讨论,那么就只会有一个结果——乱。 + +> 我记得在上一家公司有一个项目,讨论就讨论了1年多,项目本来不复杂,但是没有使用SoC,全部的东西混为一谈,再加上一堆程序员注入了各种不同的观点和想法,整个项目一下子就失控了。最后,本来一个1年的项目做了3年。 + +实现关注点分离的方法主要有两种,一种是标准化,另一种是抽象与包装。 + +标准化就是制定一套标准,让使用者都遵守它,将人们的行为统一起来,这样使用标准的人就不用担心别人会有很多种不同的实现,使自己的程序不能和别人的配合。 +JavaEE就是一个标准的大集合。每个开发者只需要关注于标准本身和他所在做的事情就行了。就像是开发镙丝钉的人只专注于开发镙丝钉就行了,而不用关注镙帽是怎么生产的,反正镙帽和镙丝钉按标来就一定能合得上。 + +不断地把程序的某些部分抽像差包装起来,也是实现关注点分离的好方法。一旦一个函数被抽像出来并实现了,那么使用函数的人就不用关心这个函数是如何实现的,同样的,一旦一个类被抽像并实现了,类的使用者也不用再关注于这个类的内部是如何实现的。 + +诸如组件,分层,面向服务,等等这些概念都是在不同的层次上做抽像和包装,以使得使用者不用关心它的内部实现细节。 说白了还是“高内聚,低耦合”。 + +> [参考: 理论篇:关注点分离(Separation of concerns, SoC)](http://www.cnblogs.com/wenhongyu/p/7992028.html) + +### 契约式设计 +`Design by Contract (DbC)` + +DbC的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。例如: +1. 供应商必须提供某种产品(责任),并且他有权期望客户已经付款(权利)。 +1. 客户必须付款(责任),并且有权得到产品(权利)。 +1. 契约双方必须履行那些对所有契约都有效的责任,如法律和规定等。 + +同样的,如果在程序设计中一个模块提供了某种功能,那么它要: + +1. 期望所有调用它的客户模块都保证一定的进入条件:这就是模块的先验条件(客户的义务和供应商的权利,这样它就不用去处理不满足先验条件的情况)。 +1. 保证退出时给出特定的属性:这就是模块的后验条件——(供应商的义务,显然也是客户的权利)。 +1. 在进入时假定,并在退出时保持一些特定的属性:不变式。 + +契约就是这些权利和义务的正式形式。我们可以用“三个问题”来总结DbC,并且作为设计者要经常问: +1. 它期望的是什么? +1. 它要保证的是什么? +1. 它要保持的是什么? + +根据Bertrand Meyer氏提出的DBC概念的描述,对于类的一个方法,都有一个前提条件以及一个后续条件,前提条件说明方法接受什么样的参数数据等,只有前提条件得到满足时,这个方法才能被调用; +同时后续条件用来说明这个方法完成时的状态,如果一个方法的执行会导致这个方法的后续条件不成立,那么这个方法也不应该正常返回。 + +现在把前提条件以及后续条件应用到继承子类中,子类方法应该满足: +1. 前提条件不强于基类. +1. 后续条件不弱于基类. + +换句话说,通过基类的接口调用一个对象时,用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件,亦即,继承类方法必须接受任何基类方法能接受的任何条件(参数)。 + +同样,继承类必须顺从基类的所有后续条件,亦即,继承类方法的行为和输出不得违反由基类建立起来的任何约束,不能让用户对继承类方法的输出感到困惑。 + +这样,我们就有了基于契约的LSP,基于契约的LSP是LSP的一种强化。 + +### 无环依赖原则 +`Acyclic Dependencies Principle (ADP)` + +包之间的依赖结构必须是一个直接的无环图形,也就是说,在依赖结构中不允许出现环(循环依赖)。 +无环依赖原则(ADP)为我们解决包之间的关系耦合问题。在设计模块时,不能有循环依赖。 + +如果包的依赖形成了环状结构,怎么样打破这种循环依赖呢? 有2种方法可以打破这种循环依赖关系: +1. 第一种方法是创建新的包,如果 `A->B->C->A` 形成环路依赖,那么把这些共同类抽出来放在一个新的包D里。(`->`表示依赖) + - 这样就把`C->A`变成了`C->D`以及`A->D`,从而打破了循环依赖关系。 +1. 第二种方法是使用`DIP`(依赖倒置原则)和`ISP`(接口分隔原则)设计原则。 + +************************ + +# 设计模式 +> `较为全面的教程`: [java design patterns](https://java-design-patterns.com/) | [菜鸟教程: 设计模式简介](http://www.runoob.com/design-pattern/design-pattern-intro.html) | [《软件秘笈:设计模式那点事》](https://mubu.com/doc/explore/17351) + +> [java-design-patterns](https://github.com/iluwatar/java-design-patterns) + +书籍: +- 《设计模式之禅 第二版》 +- 《软件秘笈:设计模式那点事》 + +> [设计模式基础](http://www.cnblogs.com/x-xk/archive/2013/01/21/2864916.html) +[参考: GoF解释](http://www.baike.com/wiki/GoF) +[参考: 设计模式专栏](http://blog.csdn.net/column/details/zsxdesignpattern.html) + +> [23种经典设计模式UML类图汇总 ](http://blog.csdn.net/u011240877/article/details/45381071) +[参考: 23种设计模式UML表示形式](http://blog.csdn.net/chen4013874/article/details/51347535) +[参考: 23中设计模式类图和原理详解](http://blog.csdn.net/tingting256/article/details/52534663) +[参考: 23种设计模式类图总结 ](http://blog.csdn.net/qq_25827845/article/details/52510803) + +> [如何正确地使用设计模式?](https://www.zhihu.com/question/23757906) +> [设计模式有何不妥,所谓的荼毒体现在哪?](https://www.zhihu.com/question/23757237) + +- 如果你用的语言能把类型像变量一样赋值并传来传去,很多创建型模式就没用了。 +- 如果你用的语言能把函数像变量一样赋值并传来传去,很多行为模式就没用了。 +- 如果你用的语言 style 反对叠床架屋的 class hierarchy,很多结构模式就没用了。 + +> 模式和重构之间存在着天然联系,模式是你想到达的目的地,而重构则是从其他地方到达这个目的地的条条道路 —— `Martin Fowler《重构》` + +设计模式的本质是语言的不足导致的,你实际上在手工架构一些由于语言表达能力不够而产生的并不属于业务逻辑本身的逻辑。 +> [Python中无需存在的设计模式](http://cdn.oreillystatic.com/en/assets/1/event/12/_The%20Lack%20of_%20Design%20Patterns%20in%20Python%20Presentation.pdf) + + +面向设计模式来设计了,而没有面向问题本身来设计。面向问题本身来设计的时候,要面向这个领域问题。 +应该考虑的是更普遍的原则,比如Single Point Of Truth,关注点分离这些。应用的应该是人类思维的规律,穷举能解决吗,状态机呢,能不能设计成递归的。 +最后实现的时候才是设计模式,而这时候如果理解了程序设计语言静态类型,基本你都感觉不到你是在使用设计模式。因为一切自然就推理到了 + +在设计时分离稳定的部分和容易变化的部分,对易变的部分进行必要的抽象,这种变化有可能是一个过程,也有可能是一个类型。让稳定的部分依赖于这个稳定的抽象,把该抽象对应的实现的指定推迟到适合的时候,什么时候?有可能就在你的函数体外,也有可能在类外,还有可能在配置文件中。必须要结合当前的业务情景判断这样的指定位置是否稳定。而那些容易变化的部分应该作为一个一个的“输入”注入到稳定的模块中,呈现出各自不一样的形态。 + +变化,稳定,延迟和抽象。理解了这些真正的含义,再看23种设计模式你一定会发出惊叹,代码的可复用性和可拓展性这些东西根本就不需要特别关注,按照我的方法论,你会自然而然的发现自己的代码是高度可复用的。这样的设计可以快速的去匹配不断出现的变化。当你预测到没有变化时,如果再用设计模式就是愚蠢的行为了。因为它本身的目的只是依赖分离。 + +设计模式往往是前人在编程方面总是遇到类似问题,后来总结出来的一套方法论,没有人可以说这套方法论是绝对的对或者是绝对的错误。 +他的对错都是相对的,可能跟具体场景有关,可以是与其他方法论相比来说更显优势。其实我倒是觉得把“设计模式”叫做“方法论”或者“编程思想”更贴切一点。 + +编程人员成长之路的例子: + +`第一阶段`:刚刚入行不久,之前总是被某种(或者说某几类)问题困扰。后来通过学习了解到有一种设计模式可以解决这个问题,于是觉得设计模式这个东西很牛逼,并且认真学习各种设计模式,果然在编程方面有了进步,好多之前的问题都迎刃而解。随着你的成长,了解到有时候设计模式之间的搭配使用会更得心应手。好多问题通过单一模式解决不了,可以与其他模式配合。这个时候的状态在是在第一阶段的最佳时期,但还处于使用设计模式解决问题的阶段。 + +`第二阶段`:过了一段时间,要自己设计一个单独的相对复杂的模块,因为之前总是遇到问题解决问题,现在忽然感觉没有的入口,之前学习了那么模式完全不知道从何入手。后来经过高人指点,终于找到了一种设计模式作为这个复杂模块的主题编程思想,然后漂亮的完成这个任务。这个时候意识到当设计一个架构最主要的不是要了解多少设计模式,更多的是对问题的理解、抽象能力,能够在多种方案中找到适合当前需求的方案,并且如何与其他方案搭配。 + +`第三阶段`:慢慢的开始意识这一路走来,自己在编程思想上可能有了一些提高,但是总是有头疼的问题,或想不出的设计方案。感觉所有的模式都是前人设计出来的,正如我前面说到的他是前人针对自己遇到的这一类问题作的一套比较通用的方案,或许这个方案可以完美解决他的问题,但是问题总是不同的。开始意识到其实没有什么设计模式可言,能优雅解决问题与设计模块才是王道。 + +- 其实所有的编程思想,如工厂模式、装饰器、交换机等等它们都是跟现实生活有相对应的内容。前人就是发现了这种相似的地方,所以构想出来了能够解决自己问题的方法论。当有一个你在生活中看到一种什么现象,然后把他的思想编写成计算机语言,这也属于你自己的设计模式。 +- 再说到过度设计,其实过度设计也没有一个明显的界定。随心吧,自己如果把问题理解深刻了以后,感觉没有就是没有吧。 + +前面说了“是否精通设计模式”这个问题在我看来总是模棱两可的,为什么呢? +精通不在于所有别人想出来的方法论你都记在脑子里面了,更多的是说所有问题都能通过这些方法论迎刃而解,这怎么可能呢?还有很多复杂的设计方案或让人头疼的问题解决方案是自己“悟”到的,用自己的理解、提炼并且映射到编程语言中的。 +所有对于以后会遇到什么问题,谁又能清楚呢?感觉有点像“悟道”,只有参透万物才能所有问题迎刃而解。 + +![](/Java/img/001-design-pattern.km.svg) + +************************ + +## 设计模式概览 +### 创建型设计模式 +这些设计模式提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 关键字直接实例化对象。 +这使得程序在判断针对某个给定实例需要创建哪些对象时更加灵活。 + +> 单例模式(**Singleton**) +- 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 + +> 原型模式(**Prototype**) +- 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 + +> 建造者(**Builder**) +- Builder,是一种对象构建模式,模式通常包含Builder,ConcreteBuilder。Director 和 Product四部分. + +> 抽象工厂(**Abstract Factory**) +- 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类 + +> 工厂方法(**Factory Method**) +- 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类 + +### 结构型设计模式 +这些设计模式关注类和对象的组合。结构型模式采用继承机制来组合接口或实现, 使对象获得新功能的方式。 +结构型设计模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能. + +> 适配器模式(**Adapter**) +- 原有接口无法适应当前需要,加一个适配器来做兼容 + +> 桥接模式(**Bridge**) +- 将对象的抽象和实现分离,从而可以独立的改变他们。 + +> 组合模式(**Composite**) +- 它将对象组合成树形结构以表示“部分-整体” + +> 代理模式(**Proxy**) +- 倾向于从一个代理对象调用真实对象,但是实现上和装饰器模式一样,但是目的不同 + +> 享元模式(**Flyweight**) +- 该模式为共享对象定义了一个结构,强调对象的空间效率,自由共享 + +> 外观模式(**Facade**) +- 描述了如何用单个对象表示整个子系统(外部与其内部通信必须通过一个统一的对象进行交互),模式中的facade用来表示一组对象,外观设计模式提供一个高层次的接口是的子系统易于使用。 + +> 装饰模式(**Decorator**) +- 描述如何动态地为对象添加职责,模式采用递归方式组合对象,从而允许添加任意多的对象职责。 + +### 行为设计模式 +行为设计模式涉及算法和对象间职责的分配,行为模式描述对象和类的模式以及其通信模式, 行为模式使用继承机制在类间派发行为 + +> 策略模式(**Strategy**) +> 命令模式(**Command**) +> 状态模式(**State**) +> 解释器模式(**Interpreter**) +> 模板方法(**Template Method**) +> 责任链模式(**Chain of Responsibility**) +> 迭代器模式(**Iterator**) +> 中介者模式(**Mediator**) +> 备忘录模式(**Memento**) +> 观察者模式(**Observe**) +> 访问者模式(**Visitor**) + +**************************************** + +## 设计模式详述 +### 工厂模式 +### 抽象工厂模式 +- 提供一个创建一系列相关实例相互依赖的对象。 + - 当一个系统要独立于它的产品的创建,组合和表示时 + - 当一个系统要由多个产品系列中的一个来配置时 + - 当需强调一系列相关的产品对象的设计以便进行联合使用时 + - 想提供一组对象而不显示他们的实现过程,只显示他们的接口 + +![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/AbstractFactory.png) + +### 单例模式 +> Singleton 一个类只有一个实例供外界访问 Spring将该模式运用的出神入化 + +- 当某个单例对象中含有不具有并发性的对象 就会发生并发问题, 由于只有一个对象, 为了确保数据一致, 就需要加锁, 这样就带来了严重的性能下降 而Spring是怎么做的呢 + - [参考博客 Spring如何处理线程并发](https://blog.csdn.net/java_fancy/article/details/7439657) + - [参考: springmvc是单例的,开发的时候会不会影响性能呢?](https://bbs.csdn.net/topics/390873889) + - [参考: Spring并发访问的线程安全性问题](http://www.xuebuyuan.com/1628190.html) `Controller或者Service层中定义共享对象, 但是使用线程安全对象` + +### 建造者模式 + +![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Builder.png) + +使用场景: Protobuf消息类的创建都是使用的建造者模式 + +### 原型模式 +> struts2 就是采用该模式 +- **原型模式** : 对象创建模型: 允许一个对象创建另一个可定制的对象,封装实例化细节。 + - 实现Cloneable接口(Java自带接口),重写clone方法(在这里实例化对象,new或反射,按需求来修改) + - 该例,组合关系,在对方使用clone来代替构造器来实例化对象,并做好了绑定操作,大量减少代码量 + +![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Clone.png) + +************ + +### 策略模式 +> 定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的用户。 +[参考: 设计模式 ( 十八 ) 策略模式Strategy(对象行为型) ](http://blog.csdn.net/hguisu/article/details/7558249) +[参考: Java消除ifelse](http://www.cnblogs.com/zdd-java/p/6143935.html) + +也就是说将一种需求的多种实现算法分别封装起来, 然后利用多态, 让调用方选择任一实现 + +优点:灵活添加同一问题的不同解决方案 + +### 中介者模式 + +包装了一系列对象相互作用的方式,使得对象间的相互作用是独立变化,但是与中介者紧密联系 + +### 观察者模式 +一个目标物件管理相依于它的管理物件,并且在它本身的状态发生改变时发出通知,这种模式常用来实现事件处理系统。(也称发布-订阅,模型-视图,源-收听者模式) +- 观察者(接口):更新信息,展示信息,给 **被观察者(形参)** 注册上观察者 +- 被观察者(接口):发出更新通知(遍历观察者集合并注册),当自身发生改动时发出通知消息 + +![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Observer.png) + +### 状态模式 +允许对象在内部状态时变更其行为,并且修改其类: +- 环境类(Context):定义客户感兴趣的接口,维护一个子类的实例,这个实例就是当前状态 +- 抽象状态类(State):定义一个接口以封装与Context的一个特定状态相关的行为 +- 具体状态类(concreteState):每一子类实现与Context的一个状态相关的行为 + +- **例题**:纸巾售卖机:有四个状态! + - 【状态图】 + - ![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/State_zhijin.png) + - 【类图】 + - ![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/State_zhijin2.png) + +- **例题**:TCP连接状态:![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/State_TCP.png) + +### 命令模式 +- 行为请求者 与 请求实现者 之间 紧耦合 的关系 +- **将一个请求封装成一个对象**,从而可用不同的请求对客户进行参数化,支持可撤销的操作 +- 下例:使用了接口来实现多态,子类是多个的,方法同名并功能多样的 + - 代码复用好,代码结构清晰【参数类表最好不要出现标志变量,最好分离出另一个方法】 + +![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Command.png) + +******************************** +### 适配器模式 + +适配器是的一个接口与其他接口兼容,从而给出了多个不同接口的同一抽象。一般分类结构和对象结构两种: +- *类适配器*:适配器类继承被适配类,实现某个接口,在客户端类中可以根据需求来建立子类 +- *对象适配器*:适配器不是继承,是使用直接关联,或称委托方式 + +![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/UML/Adapter.png) + +### 桥接模式 +便于扩展,实现与抽象分离(解耦)对一个模块修改不影响别的模块 + +![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Bridge.png) + +************************ + +# 实践 +## 经验之谈 +- [IBM 社区 Java 设计模式专题](https://www.ibm.com/developerworks/cn/java/design/) +- [一个鸭子游戏引发的设计(多态,继承,抽象,接口,策略者模式)](http://www.cnblogs.com/x-xk/archive/2012/12/21/2823401.html) +- [不要再盲目的new了!你要学着针对接口编程!(具体方法,Factory,Abstract Factory](www.cnblogs.com/x-xk/archive/2013/01/06/2830742.html) + +- [参考: 为什么我墙裂建议大家使用枚举来实现单例。](https://mp.weixin.qq.com/s/aGMz1u0Oh4ZHTDBFvgq0lg)`但是枚举的实现不利于扩展` + +## 反模式 +- 末日金字塔: if while 代码块多层嵌套 +- 继承关系超过3层 +- 父类和子类同名成员属性 diff --git a/Java/EE.md b/Java/EE.md deleted file mode 100644 index b92e48c..0000000 --- a/Java/EE.md +++ /dev/null @@ -1,167 +0,0 @@ -`目录 start` - -- [JavaEE](#javaee) - - [2.【几大框架简述】](#2几大框架简述) - - [9 【SpringMVC】](#9-springmvc) - - [9.1 必要JAR包:](#91-必要jar包) - - [9.2 实现逻辑](#92-实现逻辑) - - [9.3 controller的配置](#93-controller的配置) - - [9.3.1类型转换(也可以使用Hibernate的convert)](#931类型转换(也可以使用hibernate的convert)) - - [SpringMVC的内置代理](#springmvc的内置代理) - - [Hibernate的covert包](#hibernate的covert包) - - [Controller层的异常处理(一般处理自定义异常)](#controller层的异常处理(一般处理自定义异常)) - - [拦截器机制](#拦截器机制) - - [视图解析](#视图解析) - - [上传下载](#上传下载) - - [JSON的解析](#json的解析) - - [10.【SSH框架的整合】](#10ssh框架的整合) - - [11.【SSM框架的整合】](#11ssm框架的整合) - - [12.【Redis的使用】](#12redis的使用) - - [12.1 【Java 使用 redis 配置】](#121-java-使用-redis-配置) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# JavaEE - -******************* -## 2.【几大框架简述】 -* MVC设计模式: - * M - * hibernarte (相应操作的SQL语句由Hibernate框架生成) - * mybatis(SQL用户根据需要去写的) - * JPA 和Hibernate是相同的内核,由Hibernate派生而来 - * C - * struts1.x - * struts2.x - * springmvc - * spring 模块的整合 - * V: - 视图层 -- **再度理解** Dao service模式的概念 - * dao : 基础单笔业务的功能模块 - * service : 将单个的dao组合一起,得到复杂的业务逻辑 - * 如果要实现AOP或者规范化,dao和service分别要有接口的存在(为了多态,代理,严谨) - -## 9 【SpringMVC】 -- 一般使用注解方式更方便书写 -### 9.1 必要JAR包: -- Spring的核心JAR包 -- spring-web-3.2.6.RELEASE.jar -- spring-webmvc-3.2.6.RELEASE.jar -- spring-webmvc-portlet-3.2.6.RELEASE.jar - -### 9.2 实现逻辑 - -- 核心类是DispatchServlet 由它来接收各种请求 -- 发出request请求,到controller解析器,得到Model和view等的名字 -- 发送到controller执行,返回view名字 -- 发送到视图解析器 -- 执行视图返回到dispatchServlet - -### 9.3 controller的配置 -- 类: - - @Controller - - @RequestMapping("/WebApplicationRootURL") -- 方法 - - @RequestMapping("/ActionURL") - - @RequestMapping("/action/{id}") 方法要使用(@PathVariable("id") String id) - - @ResponseBody 返回对象,自动解析成JSON - -#### 9.3.1类型转换(也可以使用Hibernate的convert) - -```xml - - - - - - - - - -``` - - -##### SpringMVC的内置代理 - -##### Hibernate的covert包 - -#### Controller层的异常处理(一般处理自定义异常) - -```java - 处理所有接收到的的异常 - @ControllerAdvice - public class ExceptionHandle{ - @EXceptionHandler({Exception.class}) - public ModelAndView dealException(Exception e){ - ModelAndView view = new ModelAndView("exception"; - Exception e = new Exception("错误信息"); - view.addObject("",e.getMessage()); - return view; - } - -``` - -#### 拦截器机制 - -```xml - implements HandleInterceptor 有三个方法 - - preHandle 返回true就继续往后,false就被拦截 - PostHandle 在渲染视图之前, - afterCompletion 渲染视图之后调用,释放资源 - - - 配置文件,需要配置: - ?如果这个路径大于springmvc拦截的路径? - - - - - -``` -#### 视图解析 -```java - ModelAndView view = ModelAndView("index"); - 使用这种写法是进入配置好的视图解析器,进行路径的拼接然后转发 - ModelAndView("redirect:/l/login.jsp"); - 就是使用重定向方式,注意路径要写全,因为不会拼接 - ``` - - -#### 上传下载 -jar包: -- common-upload -- common-io - ---- -配置文件 - ---- - -#### JSON的解析 -- 第三方的JSON工具包: - - jsonlib - - jackson : 三个包 annotion core databind - - gson - -- 发送JSON - - 只要有返回值,方法前加上这个注解就会自动返回JSON格式的数据而不是对象 - - @responseBody - - @ReqeustMapping("") -- 接收JSON - - 参数前 也加上@equestBody 就可以把JSON数据转成对象 - -******************** - -## 10.【SSH框架的整合】 - - -## 11.【SSM框架的整合】 - - -## 12.【Redis的使用】 - -### 12.1 【Java 使用 redis 配置】 -> [Java使用Redis](/Database/Redis.md#Java使用Redis) \ No newline at end of file diff --git a/Java/Ecosystem/Activiti.md b/Java/Ecosystem/Activiti.md index 3e468d7..98a29ec 100644 --- a/Java/Ecosystem/Activiti.md +++ b/Java/Ecosystem/Activiti.md @@ -1,22 +1,29 @@ -`目录 start` - -- [Activiti](#activiti) - - [相关书籍](#相关书籍) - - [相关仓库](#相关仓库) - - [基础概念](#基础概念) - - [什么是流程](#什么是流程) - - [流程引擎](#流程引擎) - - [BPMN流程图](#bpmn流程图) - - [数据库的设计](#数据库的设计) - - [1.资源库流程规则表](#1资源库流程规则表) - - [2.运行时数据库表](#2运行时数据库表) - - [3.历史数据库表](#3历史数据库表) - - [4.组织机构表](#4组织机构表) - - [5.通用数据表](#5通用数据表) - - [核心配置文件](#核心配置文件) - - [大致原理讲解](#大致原理讲解) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Activiti +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +**目录 start** + +1. [Activiti](#activiti) + 1. [相关书籍](#相关书籍) + 1. [相关仓库](#相关仓库) + 1. [基础概念](#基础概念) + 1. [什么是流程](#什么是流程) + 1. [流程引擎](#流程引擎) + 1. [BPMN流程图](#bpmn流程图) + 1. [数据库的设计](#数据库的设计) + 1. [1.资源库流程规则表](#1资源库流程规则表) + 1. [2.运行时数据库表](#2运行时数据库表) + 1. [3.历史数据库表](#3历史数据库表) + 1. [4.组织机构表](#4组织机构表) + 1. [5.通用数据表](#5通用数据表) + 1. [核心配置文件](#核心配置文件) + 1. [大致原理讲解](#大致原理讲解) + +**目录 end**|_2020-06-24 02:06_| **************************************** # Activiti diff --git a/Java/Ecosystem/Blade.md b/Java/Ecosystem/Blade.md deleted file mode 100644 index 525ae56..0000000 --- a/Java/Ecosystem/Blade.md +++ /dev/null @@ -1,11 +0,0 @@ -`目录 start` - -- [Blade](#blade) - -`目录 end` |_2018-08-05_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Blade -> [Blade项目首页](https://lets-blade.com/) | [Github 地址](https://github.com/lets-blade/blade) | [官方 demos](https://github.com/lets-blade/blade-demos) - -> [参考博客: Blade:一款简洁优雅、微内核设计的Java Web框架](http://hao.jobbole.com/bladejava/) - diff --git a/Java/Ecosystem/Dubbo/Dubbo.md b/Java/Ecosystem/Dubbo/Dubbo.md new file mode 100644 index 0000000..136f08b --- /dev/null +++ b/Java/Ecosystem/Dubbo/Dubbo.md @@ -0,0 +1,28 @@ +--- +title: Dubbo +date: 2019-05-09 16:47:23 +tags: +categories: + - Java +--- + +**目录 start** + +1. [Dubbo](#dubbo) + 1. [SPI](#spi) + 1. [Tips](#tips) + +**目录 end**|_2020-10-12 14:58_| +**************************************** +# Dubbo +> [Official Doc](http://dubbo.apache.org/zh-cn/docs/user/quick-start.html) | [Github](https://github.com/apache/incubator-dubbo) +> [Sample](https://github.com/apache/incubator-dubbo-samples) + +RegistryService 通过查看 Type Hierarchy 可以看到所有 Dubbo 支持的注册中心 + +## SPI +[META-INF 目录结构](https://docs.oracle.com/en/java/javase/11/docs/specs/jar/jar.html#the-meta-inf-directory) + + +## Tips +> SpringBoot 整合 Dubbo 使用 XML 方式 只需在Configuration注解类上引入 dubbo 配置文件 例如 引入 dubbo目录下的配置文件 `@ImportResource({"classpath:dubbo/*.xml"})` diff --git a/Java/Ecosystem/Dubbo/MulticaseRegistry.md b/Java/Ecosystem/Dubbo/MulticaseRegistry.md new file mode 100644 index 0000000..333b97f --- /dev/null +++ b/Java/Ecosystem/Dubbo/MulticaseRegistry.md @@ -0,0 +1,45 @@ +--- +title: MulticaseRegistry +date: 2019-05-09 20:31:03 +tags: +categories: +--- + +**目录 start** + +1. [Dubbo 的 Multicast 实现](#dubbo-的-multicast-实现) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Dubbo 的 Multicast 实现 +> [参考: Dubbo Multicast 注册中心即相关代码实现](http://www.cnblogs.com/ghj1976/p/5328376.html) + +- 通过 wireshark 抓包, 可以看到大致的流程 + - provider 或者 customer 都是先 register 然后 subscribe + +``` + register dubbo://UUUU?application=first-dubbo-provider + + subscribe provider://UUUU?application=first-dubbo-provider&bind.ip=172.17.0.1&bind.port=20880&category=configurators&check=false + + * register consumer://172.17.0.1/org.apache.dubbo.samples.api.GreetingsService?application=first-dubbo-consumer&category=consumers&check=false&default.generic=false&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.samples.api.GreetingsService&methods=sayHi&pid=27381&release=2.7.0&side=consumer×tamp=1557386946272 + + * subscribe consumer://172.17.0.1/org.apache.dubbo.samples.api.GreetingsService?application=first-dubbo-consumer&category=providers,configurators,routers&default.generic=false&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.samples.api.GreetingsService&methods=sayHi&pid=27381&release=2.7.0&side=consumer×tamp=1557386946272 + + // provider 进程开始停止 + + unregister dubbo://UUUU?application=first-dubbo-provider + + unregister provider://UUUU?application=first-dubbo-provider&bind.ip=172.17.0.1&bind.port=20880&category=configurators&check=false + + unsubscribe provider://UUUU?application=first-dubbo-provider&bind.ip=172.17.0.1&bind.port=20880&category=configurators&check=false + + unregister dubbo://UUUU?application=first-dubbo-provider + + unregister provider://UUUU?application=first-dubbo-provider&bind.ip=172.17.0.1&bind.port=20880&category=configurators&check=false + + unsubscribe provider://UUUU?application=first-dubbo-provider&bind.ip=172.17.0.1&bind.port=20880&category=configurators&check=false +``` +> UUU表示 172.17.0.1:20880/org.apache.dubbo.samples.api.GreetingsService +> * 是customer, 其他的是 provider + diff --git a/Java/Ecosystem/Elasticsearch.md b/Java/Ecosystem/Elasticsearch.md deleted file mode 100644 index 5f5f081..0000000 --- a/Java/Ecosystem/Elasticsearch.md +++ /dev/null @@ -1,10 +0,0 @@ -`目录 start` - -- [Elasticsearch](#elasticsearch) - -`目录 end` |_2018-08-23_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Elasticsearch - -> [参考博客: 用容器快速上手Elasticsearch](http://qinghua.github.io/elastic-search/) - diff --git a/Java/Ecosystem/Guava.md b/Java/Ecosystem/Guava.md index ca8faa7..a1a3d4d 100644 --- a/Java/Ecosystem/Guava.md +++ b/Java/Ecosystem/Guava.md @@ -1,17 +1,29 @@ -`目录 start` - -- [Guava](#guava) - - [基础部分](#基础部分) - - [EventBus](#eventbus) +--- +title: Guava +date: 2018-11-21 10:56:52 +tags: +categories: + - 工具类库 +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [Guava](#guava) + - 1.1. [基础部分](#基础部分) + - 1.2. [集合](#集合) +- 2. [独立组件](#独立组件) + - 2.1. [RateLimiter](#ratelimiter) + - 2.2. [EventBus](#eventbus) + +💠 2024-04-22 00:47:57 **************************************** # Guava -> [Github地址](https://github.com/google/guava) -> [官方手册](https://github.com/google/guava/wiki) | git地址:`https://github.com/google/guava.wiki.git` -> [翻译版](http://ifeve.com/google-guava/) +> [Github地址](https://github.com/google/guava) +> [官方手册](https://github.com/google/guava/wiki) `git clone https://github.com/google/guava.wiki.git` +> [翻译版](http://ifeve.com/google-guava/) +> [Guava Guide](https://www.baeldung.com/guava-guide) -- Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等等。 所有这些工具每天都在被Google的工程师应用在产品服务中。 +Guava工程包含了若干被Google的 Java项目广泛依赖 的核心库,例如:集合 collections 、缓存 caching 、原生类型支持 [primitives support] 、并发库 concurrency libraries 、通用注解 common annotations 、字符串处理 string processing 、I/O 等等。 所有这些工具每天都在被Google的工程师应用在产品服务中。 _包结构_ ``` @@ -24,44 +36,47 @@ _包结构_ com.google.common.util.concurrent ``` -``` - [Google Guava] 7-原生类型 - [Google Guava] 12-数学运算 - [Google Guava] 排序: Guava强大的”流畅风格比较器” - [Google Guava] 2.1-不可变集合 - [Google Guava] 10-散列 - [Google Guava] 9-I/O - [Google Guava] 1.2-前置条件 - [Google Guava] 4-函数式编程 - [Google Guava] 6-字符串处理:分割,连接,填充 - [Google Guava] 1.1-使用和避免null - [Google Guava] 8-区间 - [Google Guava] 2.4-集合扩展工具类 - [Google Guava] 1.3-常见Object方法 @Bean - public RedisTemplate redisTemplate(RedisConnectionFactory factory) { - RedisTemplate template = new RedisTemplate<>(); - Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); - template.setConnectionFactory(factory); - ObjectMapper om = new ObjectMapper(); - om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); - jackson2JsonRedisSerializer.setObjectMapper(om); - // 值序列化采用 jackson2JsonRedisSerializer - template.setValueSerializer(jackson2JsonRedisSerializer); - // 键序列化采用 StringRedisSerializer - template.setKeySerializer(new StringRedisSerializer()); - template.afterPropertiesSet(); - return template; - } - google Guava包的ListenableFuture解析 - [Google Guava] 11-事件总线 -``` - ## 基础部分 -> Optional的设计和Java8的Optional是差不多的, 只是方法的命名不一样而已 +> Optional的设计和Java8的Optional是差不多的,Java8可能是参考的Guava。 + +## 集合 + +************************ +# 独立组件 +## RateLimiter +> 令牌桶算法实现 -### EventBus +Beta 状态,官方TODO优化为nano级别,降低存储成本 + +> [RateLimiter限流原理解析](https://zhuanlan.zhihu.com/p/60979444) + +## EventBus > [官方文档](https://github.com/google/guava/wiki/EventBusExplained) | [Guava学习笔记:EventBus](http://www.cnblogs.com/peida/p/EventBus.html) > [并发编程网 event bus](http://ifeve.com/google-guava-eventbus/) | [走进Guava](https://www.yeetrack.com/?p=1177) +```java + // 快速使用 + EventBus eventBus = new EventBus(); + eventBus.register(new Object() { + @Subscribe + public void hehe(Integer num) throws InterruptedException { + System.out.println(num + ":" + System.currentTimeMillis()); + Thread.currentThread().sleep(100); + } + }); + + eventBus.post(1); +``` +> 注意 只使用 `@Subscribe`注解的话,如果有两个同类事件触发,也是要排队执行的,因为包装的是 `Subscriber.SynchronizedSubscriber` 实现,同类事件并发执行需要加上 `@AllowConcurrentEvents` + +基础组件 + +- Executor : EventBus#executor 默认是当前线程,通常指定自定义线程池 +- SubscriberRegistry : Subscriber注册器,每个带有@Subscribe的方法会被注册到该类中 +- Dispatcher : 调度器,负责将事件,分发给事件对应的Subscriber,使用Executor执行这些Subscriber + - PerThreadQueuedDispatcher 执行线程内将会按事件发布顺序进行消费, 执行线程间仍异步乱序。`ThreadLocal` 实现线程间队列隔离 + - LegacyAsyncDispatcher 默认用于 AsyncEventBus,异步实现即可能出现不同的线程不同的事件消费顺序,同一线程对先后发布的事件消费顺序也可能不一致,注释都说这个有没有必要用队列 emmm + - ImmediateDispatcher 无队列,立即投送事件给Subscriber,积压在Executor的队列中, 事件的消费可能有序可能无序取决于不同的Subscriber实现 +- SubscriberExceptionHandler : 异常处理器 + diff --git a/Java/Ecosystem/Hibernate.md b/Java/Ecosystem/Hibernate.md index 0ef8897..89a72b0 100644 --- a/Java/Ecosystem/Hibernate.md +++ b/Java/Ecosystem/Hibernate.md @@ -1,101 +1,100 @@ -`目录 start` - -- [Hibernate](#hibernate) - - [Hibernate基础配置](#hibernate基础配置) - - [JDBC和Hibernate比较](#jdbc和hibernate比较) - - [配置流程](#配置流程) - - [Hibernate必需JAR](#hibernate必需jar) - - [编写数据库表对应框架持久层的对象](#编写数据库表对应框架持久层的对象) - - [hibernate.cfg.xml文件](#hibernatecfgxml文件) - - [日志文件的配置](#日志文件的配置) - - [SessionFactory和Session比较](#sessionfactory和session比较) - - [OID的作用:](#oid的作用) - - [id生成策略](#id生成策略) - - [非普通类型](#非普通类型) - - [Hibernate实体关联配置](#hibernate实体关联配置) - - [一对多的配置](#一对多的配置) - - [**注意 :**](#注意-) - - [多对多的配置](#多对多的配置) - - [学生方配置](#学生方配置) - - [课程方配置](#课程方配置) - - [一对一的配置](#一对一的配置) - - [使用多对一的技巧](#使用多对一的技巧) - - [添加记录](#添加记录) - - [删除记录](#删除记录) - - [继承关系的配置](#继承关系的配置) - - [Hibernate异常](#hibernate异常) - - [could not find a getter for](#could-not-find-a-getter-for) - - [个人总结](#个人总结) - - [Hibernate对象的状态](#hibernate对象的状态) - - [Session的方法](#session的方法) - - [特别注意](#特别注意) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Hibernate +date: 2018-11-21 10:56:52 +tags: + - ORM +categories: + - Java +--- + +**目录 start** + +1. [Hibernate](#hibernate) +1. [Hibernate基础配置](#hibernate基础配置) + 1. [JDBC和Hibernate比较](#jdbc和hibernate比较) + 1. [配置流程](#配置流程) + 1. [Hibernate必需JAR](#hibernate必需jar) + 1. [编写数据库表对应框架持久层的对象](#编写数据库表对应框架持久层的对象) + 1. [hibernate.cfg.xml文件](#hibernatecfgxml文件) + 1. [SessionFactory和Session比较](#sessionfactory和session比较) + 1. [OID的作用:](#oid的作用) + 1. [id生成策略](#id生成策略) + 1. [非普通类型](#非普通类型) +1. [Hibernate实体关联配置](#hibernate实体关联配置) + 1. [一对多的配置](#一对多的配置) + 1. [注意](#注意) + 1. [多对多的配置](#多对多的配置) + 1. [学生方配置](#学生方配置) + 1. [课程方配置](#课程方配置) + 1. [一对一的配置](#一对一的配置) + 1. [使用多对一的技巧](#使用多对一的技巧) + 1. [添加记录](#添加记录) + 1. [删除记录](#删除记录) + 1. [继承关系的配置](#继承关系的配置) +1. [Hibernate对象的状态](#hibernate对象的状态) + 1. [Session的方法](#session的方法) + 1. [特别注意](#特别注意) +1. [常见Hibernate异常](#常见hibernate异常) + 1. [could not find a getter](#could-not-find-a-getter) + +**目录 end**|_2020-05-17 16:13_| **************************************** # Hibernate -## Hibernate基础配置 -### JDBC和Hibernate比较 +# Hibernate基础配置 +## JDBC和Hibernate比较 * JDBC - * 使用其简洁精悍,最快,但是使用时接收数据以及多方面的比较麻烦 + * 使用其简洁精悍,最快,但是使用时反序列化数据麻烦,手动管理容易出内存泄漏 * Hibernate - * 单表操作是很便捷的,但是涉及到多表复杂操作时比较麻烦 + * 单表操作是很便捷的,但是涉及到多表复杂操作时比较复杂,不便调优 -#### 配置流程 +### 配置流程 > 如果后续需要添加表的话,就这个顺序 -- **1 :** 先有数据库和表,建立cfg.xml文件配置好数据库的基本参数 -- **2 :** 使用工具建立POJO持久类 -- **3 :** 导入Hibernate所必需JAR包,最好使用Myeclipse的配置,自己导包总有一堆错误 -- **4 :** 使用MyEclipse自动创建hbm.xml文件,还有各种文件。配置好hbm文件里关于表间关系的映射,或者在Myeclipse配置时手动选择 -- **5 :** 配置好DAO类中事务开启和关闭,以及各种所必需的配置,若表没有设立主键,那么POJO类需要继承自动生成的抽象类(含有主键) -- **6 :** 调用DAO或者自己的Utils类,通过Hibernate来操作数据库 - -### Hibernate必需JAR +1. 先有数据库和表,建立cfg.xml文件配置好数据库的基本参数 +1. 使用工具建立POJO持久类 +1. 导入Hibernate所必需JAR包,最好使用Myeclipse的配置,自己导包总有一堆错误 +1. 使用MyEclipse自动创建hbm.xml文件,还有各种文件。配置好hbm文件里关于表间关系的映射,或者在Myeclipse配置时手动选择 +1. 配置好DAO类中事务开启和关闭,以及各种所必需的配置,若表没有设立主键,那么POJO类需要继承自动生成的抽象类(含有主键) +1. 调用DAO或者自己的Utils类,通过Hibernate来操作数据库 + +## Hibernate必需JAR > Hibernate 3.6 - required目录下所有JAR都要导入 - jpa的JAR包(做注解用) - 日志包: - slf4j-api-* .jar 该包是一个日志接口,需要一个JAR包的实现: - slf4j-log4j12.jar 该包是转换的JAR包 - - log4j-1.2.11.jar 实现的JAR包 + - log4j-1.2.11.jar 实现SLF4J接口的JAR包 - 数据库驱动包 mysql-connector-java-5.1.7-bin.jar - 在src同级目录下新建一个lib目录,把JAR包复制进去,然后右击将jar文件 Add to build path 加入到类搜索路径里 -### 编写数据库表对应框架持久层的对象 +## 编写数据库表对应框架持久层的对象 - 使用自己的工具类创建到对应的包下,或者用相关工具生成,类型要自己多加注意 -### hibernate.cfg.xml文件 +## hibernate.cfg.xml文件 > 作为默认的主配置文件 - 数据库连接属性 驱动,url,用户名,密码 - 数据库方言 - 辅助配置 - POJO类配置文件的映射 - etc/hibernate.properties里可以看到更多配置,数据库连接池,SQL优化等 -- 在:project/core/src/main/resources/org/hibernate/下有各种dtd文件, - - 可以为eclipse的xml配置自动提示功能 - -### 日志文件的配置 -> 默认是Log4j, -在etc下复制log4j.properties到src下,就可以了,本人ssh下复制log4j.xml就可以了 - -### SessionFactory和Session比较 -* 【SessionFactory】 -> 重量级容器:消耗大量资源,不能有太多实例,二级缓存 -通常将该工厂类是单例模式,一个工厂类实例表示一个数据库 -所以Hibernate一般是不能跨数据库来做事务操作。但是EJB和JPA可以实现 ->> 这个配置选项: -hibernate.hbm2ddl.auto create-drop 在一个数据库中创建,然后使用完关闭实例时就删除所有建立的表 -hibernate.hbm2ddl.auto create 清除数据库的表及数据,重新创建表 -hibernate.hbm2ddl.auto update 更改配置文件,能够在数据库进行操作(更新,建立) -hibernate.hbm2ddl.auto validate - -* 【session】 -> 轻量级的容器,一级缓存 -是非线程安全的对象 - -### OID的作用: + +## SessionFactory和Session比较 +* SessionFactory +> 重量级容器:消耗大量资源,不能有太多实例,二级缓存 +> 通常将该工厂类是单例模式,一个工厂类实例表示一个数据库 +> 所以Hibernate一般是不能跨数据库来做事务操作。但是EJB和JPA可以实现 如下配置选项: +>- hibernate.hbm2ddl.auto create-drop 在一个数据库中创建,然后使用完关闭实例时就删除所有建立的表 +>- hibernate.hbm2ddl.auto create 清除数据库的表及数据,重新创建表 +>- hibernate.hbm2ddl.auto update 更改配置文件,能够在数据库进行操作(更新,建立) +>- hibernate.hbm2ddl.auto validate + +* session +> 轻量级的容器,一级缓存 是非线程安全的对象 + +## OID的作用: > 在Hibernate中唯一标识对象的属性,每个实体都是必须要有OID的 -### id生成策略 +## id生成策略 * assigned:要求用户去手动指定对象的OID;该对象ID的类型可以是任意的 * identity:MySQL的自动生成 * native:数据类型是数值型,id的生成策略为数据库底层自增长(数据库自己去决定使用哪种方式,MySQL用identity,Oracle用序列等) @@ -119,7 +118,7 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ ``` -### 非普通类型 +## 非普通类型 * Set集合: ```xml @@ -140,8 +139,8 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ * 查询列 属性: `` -## Hibernate实体关联配置 -### 一对多的配置 +# Hibernate实体关联配置 +## 一对多的配置 * 注意:一定要两个都有oid的情况才能配置一对多的映射,不能是依赖于另一个主键类 * 一方: @@ -157,7 +156,7 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ * 多方维护:一方中set标签加inverse="true"一方就不会维护,代码一定要多方执行set**(*) * 一方维护:一方代码一定要执行**.add*() -#### **注意 :** +### 注意 - 1.在一的一方,修改xml文件,添加一个set 属性,表示 多方 的一个集合 ```xml @@ -176,7 +175,7 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ * 处于持久化状态的对象在Session中,客户端不需要做Session的save/update 操作,Hibernate会自动的去检查处于持久化的对象的状态的属性是否发生改变,改变了就发送update语句。 * 如果该对象是一方,在一的一方映射文件中有cascade=all时,Hibernate内部还会检查该持久化对象关联的集合,对此集合进行update操作,但是该操作和外键没有关系,只有当通过多方建立关系后,才能使外键有值。 -### 多对多的配置 +## 多对多的配置 * 关系在第三方表中,和两张表本身没有关系 * 多对多维护关系,谁都能维护关系(效率是一样的)维护一般是在页面上进行的 @@ -186,14 +185,14 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ * 如果使用了反转并使用了级联,就只会保存实体,但是关系是没有维护的(就是不会插入到第三方表),和一对多一样的(一对多是外键列没有值)。 * !!如果双方都级联了,必须要有一方inverse,不然会有重复维护的错误发生 -#### 学生方配置 +### 学生方配置 ```xml ``` -#### 课程方配置 +### 课程方配置 ```xml @@ -204,7 +203,7 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ *********************************** -### 一对一的配置 +## 一对一的配置 * 单向 只要配置单向的配置文件添加: `` @@ -215,21 +214,22 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ `` ******************** -### 使用多对一的技巧 -#### 添加记录 +## 使用多对一的技巧 +### 添加记录 1. 当需要添加一个多方时,一看成课程,多看成成绩。当然的首先得有相关课程,再添加成绩记录。 2. 那就先实例化一个课程对象,配置好信息 3. 实例化多个成绩实例,再 课程对象.get**Set().add(成绩对象); 将成绩对象添加到集合中, 4. session.save(课程对象); > 注意:既然实现了这样的操作,那就说明了在实例化成绩的时候,不需要指定课程的值,那就需要添加一个构造器 -#### 删除记录 + +### 删除记录 1. 如果删除一方,那就会将一删除,如果没有配置级联,就会将多方的外键置空,不会删除多方表 2. 如何通过一方修改多方的一条, 把一方的set中的要修改的一条,(查找之前需要对象 = session.load(对象.class,主键名)将多方的数据加载进来) - 注意多方不能有空列必须指定一个默认值(是和构造器有关么?) - 再查找出来,修改再update,新增也是如此增加多的一方的时候,就是在一方的set中新增一条记录,多方的操作都体现在了一方那里 ***************************************** -### 继承关系的配置 +## 继承关系的配置 > 两种方式,一般采用前者 ```xml @@ -248,25 +248,8 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ ``` ******************************************************* -### Hibernate异常 -#### could not find a getter for -原因:1 可能真的没写get方法,或者get方法不合规范 setget方法中不允许两个连续大写字母 - 2 *.hmb.xml文件中的属性名和pojo持久类中属性名不一致(一定不能在表名中添加下划线) - 3 方法名写错(基本不可能,都是自动生成的) - -#### 个人总结 -当使用了没有 主键的表,使用Myeclipse自动创建配置文件,使用自己的Table2Class来生成POJO持久类, -就要继承对应的自动创建的抽象类,因为没有主键的表默认是将所有列看成一个主键,并且还会有添加一个id属性, -这样也说明还有一点就是,这种表的字段不能有叫做id的列 - -是不是可以不用手动去使用那个类,好像这里自动生成的一切都有, - -自动生成会生成: - 对应POJO的抽象类,hbm配置文件,以及默认的几个类,HibernateSessionFactory,IBaseHibernateDao, - 对应的Dao(添加的时候默认是没有使用事务,所以需要手动修改),添加,删除,都是依据主键的, - 至少要初始化主键,当然还得满足数据库的要求 - -### Hibernate对象的状态 + +# Hibernate对象的状态 > 主要是对象内存和Session中的状态区别,而不是Session和数据库 - `临时态`:刚实例化对象。对象在数据库中不存在,Session中也不存在 @@ -277,7 +260,11 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ * 游离态delete后就成了删除态 * 持久态delete后就成了删除态 -#### Session的方法 +> [参考: merge attachDirty attachClean用法](http://www.cnblogs.com/zhangzhangkai/p/3434491.html) + +- [ ] 分析: JDK源码 DefaultMergeEventListener中的onMerge方法 + +## Session的方法 - save - update - delete @@ -290,7 +277,7 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ - clear 全部清除 - close -#### 特别注意 +## 特别注意 * 一个对象(内存)不能存在于多个Session中,一个存,一个改的情况是会错误的 * 但是数据库中同一条记录可以实例化为多个对象(内存),那么这些对象(内存)放在不同的Session中是可以的 @@ -302,3 +289,12 @@ sessionFactory实例化,高位就会加一,生成算法是:hi*(max lo +1)+ * 在你确定OID是一定有的时候使用load提高效率,但是实际开发过程中用的少,因为实际上没有这么确定。 * 懒加载如果Session关闭了或者是对象游离态。就会有懒加载初始化的异常 + +************* + +# 常见Hibernate异常 +## could not find a getter +> 原因: + +1. 可能真的没写get方法,或者get方法不合规范 setget方法中不允许两个连续大写字母 +2. *.hmb.xml文件中的属性名和pojo持久类中属性名不一致(一定不能在表名中添加下划线) diff --git a/Java/Ecosystem/JPA.md b/Java/Ecosystem/JPA.md index f65a74f..27125ed 100644 --- a/Java/Ecosystem/JPA.md +++ b/Java/Ecosystem/JPA.md @@ -1,19 +1,29 @@ -`目录 start` - -- [JPA](#jpa) - - [JPA和Mybatis](#jpa和mybatis) - - [配置](#配置) - - [使用](#使用) - - [注解](#注解) +--- +title: JPA +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +**目录 start** + +1. [JPA](#jpa) + 1. [JPA和Mybatis](#jpa和mybatis) + 1. [配置](#配置) + 1. [使用](#使用) + 1. [注解](#注解) -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 end**|_2020-06-28 02:11_| **************************************** # JPA ## JPA和Mybatis > [技术专题讨论第二期总结:如何对 JPA 或者 MyBatis 进行技术选型](http://www.spring4all.com/article/391) ## 配置 + ## 使用 + +> [参考: spring boot(五):spring data jpa的使用](https://www.cnblogs.com/ityouknow/p/5891443.html) ### 注解 > [Java、Hibernate(JPA)注解大全](http://www.cnblogs.com/zr520/p/5003478.html) > [jpa注解大全](http://www.cnblogs.com/cxyzl/archive/2012/12/05/2803548.html) diff --git a/Java/Ecosystem/JXls.md b/Java/Ecosystem/JXls.md deleted file mode 100644 index b0500cd..0000000 --- a/Java/Ecosystem/JXls.md +++ /dev/null @@ -1,9 +0,0 @@ -`目录 start` - -- [Jxls](#jxls) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Jxls -> [官方文档](http://jxls.sourceforge.net/getting_started.html) - diff --git a/Java/Ecosystem/JavaBoot.md b/Java/Ecosystem/JavaBoot.md new file mode 100644 index 0000000..f1d6484 --- /dev/null +++ b/Java/Ecosystem/JavaBoot.md @@ -0,0 +1,34 @@ +--- +title: Java快速开发框架 +date: 2018-11-21 10:56:52 +tags: +categories: + - Java +--- + +💠 + +- 1. [关于Java的快速开发框架](#关于java的快速开发框架) + - 1.1. [Springboot](#springboot) + - 1.2. [Jboot](#jboot) + - 1.3. [NuzBoot](#nuzboot) + - 1.4. [OpenLiberty](#openliberty) + - 1.5. [ActiveJ](#activej) + +💠 2024-04-22 00:47:57 +**************************************** +# 关于Java的快速开发框架 + +## Springboot +> 基于Spring + +## Jboot +> 基于JFinal + +## NuzBoot + +## OpenLiberty +[Github](https://github.com/OpenLiberty) + +## ActiveJ +[Github](https://github.com/activej/activej) diff --git a/Java/Ecosystem/JavaRedis.md b/Java/Ecosystem/JavaRedis.md index f2477a4..946a161 100644 --- a/Java/Ecosystem/JavaRedis.md +++ b/Java/Ecosystem/JavaRedis.md @@ -1,79 +1,68 @@ -`目录 start` - -- [Java使用redis](#java使用redis) - - [Jedis](#jedis) - - [jedis遇到的异常](#jedis遇到的异常) - - [SpringBoot使用Redis](#springboot使用redis) - - [Lettuce](#lettuce) - -`目录 end` |_2018-08-20_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: JavaRedis +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [Java 使用 Redis](#java-使用-redis) + - 1.1. [Jedis](#jedis) + - 1.1.1. [jedis遇到的异常](#jedis遇到的异常) + - 1.2. [Redisson](#redisson) + - 1.3. [Lettuce](#lettuce) + - 1.4. [vertx-redis-client](#vertx-redis-client) + +💠 2024-09-14 11:51:16 **************************************** -# Java使用redis +# Java 使用 Redis +> [Official List](https://redis.io/clients#java) + ## Jedis -> [Github:](https://github.com/xetorthio/jedis)简单直接, 但是项目很久没有更新了 - -- maven依赖(Spring 4.1.7): -```xml - - org.springframework.data - spring-data-redis - 1.6.0.RELEASE - - - redis.clients - jedis - 2.9.0 - jar - compile - - - org.apache.commons - commons-lang3 - 3.3.2 - -``` -`Spring配置文件` -```xml - - - - - - - - - - - - - - - - - - - -``` - -- java实际测试类[JedisUtilsTest.java](https://github.com/Kuangcp/Maven_SSM/blob/master/src/test/java/redis/JedisUtilTest.java) - -- jedis 使用后要disconnect释放连接,最新版本close就不用了,使用连接池就不用 +> [Github: Jedis](https://github.com/xetorthio/jedis) 简单直接 + +[JedisUtilsTest.java](https://github.com/Kuangcp/Maven_SSM/blob/master/src/test/java/redis/JedisUtilTest.java) + - jedis 的事务 使用exec释放事务 ### jedis遇到的异常 -- Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.springframework.core.serializer.support.DeserializingConverter +> Invocation of init method failed; nested exception is java.lang.NoSuchMethodError: org.springframework.core.serializer.support.DeserializingConverter - 版本对不上,要Spring和Spring-data-redis 和 redis和commons-lang3对应 - 目前是4.1.7 + 1.6.0 + 2.9.0 + 3.3.2 编译通过了 -### SpringBoot使用Redis -> [SpringBoot配置Redis](/Java/Spring/SpringBootDatabase.md) +> [Jedis连接池 资源泄露](https://mistray.github.io/2020/08/21/Jedis%E8%BF%9E%E6%8E%A5%E6%B1%A0%E7%AB%9F%E7%84%B6%E4%BC%9A%E8%B5%84%E6%BA%90%E6%B3%84%E9%9C%B2/)`2.9.1版本bug` + +************************ + +## Redisson +> [Github: Redisson](https://github.com/redisson/redisson) + +优势 +- 附带业务封装的API,限流(RSemaphore等),分布式锁 + +> WatchDog机制 +- org.redisson.RedissonBaseLock#renewExpiration 续约逻辑入口 + - 加锁时初始设置的过期时间为 异步线程续约的周期时间,所以不能设置太短,初始设置TTL后,异步线程来不及去续约key就过期删除了 + - Netty中的HashedWheelTimer实现定时调度 延时时使用的Lua脚本 + +- [watch dog](https://www.cnblogs.com/jelly12345/p/14699492.html) +- [Redis分布式锁过期了但业务还没有执行完](https://www.51cto.com/article/679902.html) + + +> 问题: 如果此时JVM发生大于TTL的FullGC,后续又恢复了,锁没有续约,被别的JVM进程抢到了锁 +- 方案: 尽可能让锁TTL大于业务操作时间,释放锁时绑定线程或业务,避免误释放 ********************* ## Lettuce > [Official](https://lettuce.io/) | [Github:](https://github.com/lettuce-io/lettuce-core) + +和 Spring 结合紧密,Spring Data Redis 的默认实现, 没有Jedis简洁 + +> 注意 +- 当Redis集群节点信息变更时,默认的策略不保证会使用最新的节点数据,需要设置为周期更新节点信息 [Refreshing the cluster topology view](https://github.com/redis/lettuce/wiki/Redis-Cluster#user-content-refreshing-the-cluster-topology-view) +- 这个问题只会发生在Redis集群扩缩容,以及发生故障的时候,问题就会暴露出来,即使Redis集群保证了高可用,应用仍无法正常使用 + +## vertx-redis-client +> [Github: vertx-redis-client](https://github.com/vert-x3/vertx-redis-client) + diff --git a/Java/Ecosystem/Kafaka.md b/Java/Ecosystem/Kafaka.md deleted file mode 100644 index 0de4498..0000000 --- a/Java/Ecosystem/Kafaka.md +++ /dev/null @@ -1,12 +0,0 @@ -`目录 start` - -- [Kafaka](#kafaka) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Kafaka -> Apache顶级项目 - -> [参考博客: 初探Kafka Streams](http://ifeve.com/%e5%88%9d%e6%8e%a2kafka-streams/) - -- [ksql](https://github.com/confluentinc/ksql) diff --git a/Java/AdvancedLearning/MultipleLanguage.md b/Java/Ecosystem/MultipleLanguageInJVM.md similarity index 63% rename from Java/AdvancedLearning/MultipleLanguage.md rename to Java/Ecosystem/MultipleLanguageInJVM.md index 8efc966..f17b4ca 100644 --- a/Java/AdvancedLearning/MultipleLanguage.md +++ b/Java/Ecosystem/MultipleLanguageInJVM.md @@ -1,49 +1,34 @@ -`目录 start` - -- [JVM上的多语言使用](#jvm上的多语言使用) - - [语言生态学](#语言生态学) - - [重新实现的语言和原生语言](#重新实现的语言和原生语言) - - [JVM上的多语言编程](#jvm上的多语言编程) - - [Groovy](#groovy) - - [Scala](#scala) - - [Clojure](#clojure) - - [为什么非要用Java语言](#为什么非要用java语言) - - [JVM对备选语言的支持](#jvm对备选语言的支持) - - [编译器小说](#编译器小说) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: JVM上的多语言 +date: 2018-11-21 10:56:52 +tags: +categories: + - Java +--- + +**目录 start** + +1. [JVM上的多语言使用](#jvm上的多语言使用) + 1. [重新实现的语言和原生语言](#重新实现的语言和原生语言) + 1. [JVM上的多语言编程](#jvm上的多语言编程) + 1. [Groovy](#groovy) + 1. [Scala](#scala) + 1. [Clojure](#clojure) + 1. [为什么非要用Java语言](#为什么非要用java语言) + 1. [JVM对备选语言的支持](#jvm对备选语言的支持) + 1. [编译器小说](#编译器小说) + +**目录 end**|_2020-11-25 20:35_| **************************************** # JVM上的多语言使用 -> 先把Java熟练先, - 喜欢Ruby => 用Groovy - 喜欢LISP,喜欢STM功能 => 用Clojure - 喜欢C++ => 用Kotlin +> 参考 Java程序员修炼之道 -## 语言生态学 -- 大致讨论 解释型和编译型, 动态和静态, 命令式和函数式 -- Java是运行时编译,静态类型的命令式语言。强调安全性,代码清晰,性能,并表现出一定程度的繁琐和死板(例如部署) - -`解释型和编译型` -- 在80 90 年代,边界较为清晰,类C语言是编译型,Perl和Python是解释型。但Java是两者都有 -- 基于JVM来划分的边界是:该语言是否将源码编译为类文件并且执行,不产生类文件的语言会由解释器逐行执行。有些语言既有编译器又有解释器,有些是既有解释器又有产生字节码的即时编译器JIT - -`动态和静态类型` -- 动态类型语言,变量在不同的时间可能会有不同的类型 动态类型语言是跟踪变量的值的类型信息,静态类型语言是跟踪变量的类型信息 -- 静态类型适合做编译型语言 - -`命令式和函数式` -- Java是典型的命令式语言,命令式语言把程序的运行状态建模为可修改的数据,用一系列的指令来改变状态。因此在命令式语言中,程序状态是核心概念 -- 命令式语言主要分为两类,一种是面向过程语言,一种是面向对象语言 - - 面向过程:Basic Fortran 这种语言将代码和数据完全分离开,有简单的代码操作数据范式 - - 面向对象:数据和代码(方法形式)封装在对象中,面向对象语言中或多或少会存在元数据(比如:类信息)引入的额外结构 -- 函数式语言他把计算本身当成最重要的概念。函数式语言和过程式语言一样对值进行操作,但他不会修改输入,而是像数学函数一样返回新值 - - 函数被看成是一个小处理机,输入值并输出值,他们没有自己的状态,并且将他们和任何外部状态绑定在一起也没有意义 -- `Groovy带一点函数式风格,Scala对FP的利用更为充分,Clojure是纯粹的函数式语言,没有丁点儿面向对象特性` - -### 重新实现的语言和原生语言 +## 重新实现的语言和原生语言 > 一般来说,以JVM为目标的语言较重新实现的语言能将自己的类型系统和JVM的类型系统结合的更紧密 +- Java是运行时编译,静态类型的命令式语言。强调安全性,代码清晰,性能,并表现出一定程度的繁琐和死板(例如部署) + - 重新实现已有语言的JVM语言: - JRuby:Ruby是一个动态类型的面向对象语言,有些函数式特性,在JVM上基本算解释型的 - Jython:动态的面向对象语言。运行方式是先生成Python字节码再转化成JVM字节码。这使得他能以看起来像是Python的典型解释型模式下运行 @@ -55,35 +40,41 @@ - 静态类型语言更倾向于稳定层的任务,能力不是那么强,通用性较低的技术在金字塔的顶部更好用 -> [Java 、Groovy、 Scala 的未来会怎样?](https://www.zhihu.com/question/21740715) +> [Java 、Groovy、 Scala 的未来会怎样?](https://www.zhihu.com/question/21740715) > [Java & Groovy & Scala & Kotlin - 16.方法,Lambda 与闭包](https://www.jianshu.com/p/3d01a98da9f9) -Scala有两个流派:FP和Better Java。FP派喜欢scalaz,喜欢shapeless,喜欢type level programming。这一派特点是程序高度抽象但可读性奇差。 +1. 喜欢Ruby => 用Groovy +1. 喜欢LISP,喜欢STM功能 => 用Clojure +1. 喜欢C++ => 用Kotlin + +Scala有两个流派:FP和Better Java。FP派喜欢scalaz,喜欢shapeless,喜欢type level programming。这一派特点是程序高度抽象但可读性奇差。 适合PL研究者验证概念,适合业余项目自嗨,也适合学习PL概念。不适合多人协作的工程项目。Better Java派以前之所以存在,单纯是因为Java语法设计太烂,烂到无法忍受。 -而JVM上当时也没有其它更好的选择。 -那些告诉你“写了n年Java以后,我切换到Scala,现在每天都活在幸福中”的人,基本都是这一派。但Scala as a better Java的工程性也不好,因为特性太多太复杂,除非有高手带队,否则很难只用到它“better java”的那个子集。 -解决一个问题的同时,往往引入更多的问题。所以会有Java8发布以后Linkedin所有新项目全部回归Java这种事情。Groovy是动态语言,工程性比Scala还差。 -但是因为有Gradle这种被广泛采用的项目,所以会存活下去。但是请记住爱因斯坦曾经说过:“任何超过两百行的新项目,都不应该采用动态语言开发,无论是Ruby,Python,Perl,Groovy还是Clojure”。 +而JVM上当时也没有其它更好的选择。 +那些告诉你“写了n年Java以后,我切换到Scala,现在每天都活在幸福中”的人,基本都是这一派。但Scala as a better Java的工程性也不好,因为特性太多太复杂,除非有高手带队,否则很难只用到它“better java”的那个子集。 +解决一个问题的同时,往往引入更多的问题。所以会有Java8发布以后Linkedin所有新项目全部回归Java这种事情。 + +Groovy是动态语言,工程性比Scala还差。但是因为有Gradle这种被广泛采用的项目,所以会存活下去。但是请记住爱因斯坦曾经说过:“任何超过两百行的新项目,都不应该采用动态语言开发,无论是Ruby,Python,Perl,Groovy还是Clojure”。 哦,对了,也不要用Clojure。因为它是动态语言,而且是Lisp系的动态语言。“Lisp系”意味着,读书的时候可以靠它开眼界。毕业工作以后,对于这一类语言,能躲多远就躲多远。 -刚刚发布的Kotlin看上去靠谱。它不讲究FP有多纯,目标就一个:“a better java”。Kotlin在“到底引入多少FP特性”上面做得恰到好处。 -看到Kotlin,我马上就想起了这个演讲:“Please stop polluting our imperative languages with pure concepts”。 + +刚刚发布的Kotlin看上去靠谱。它不讲究FP有多纯,目标就一个:“a better java”。Kotlin在“到底引入多少FP特性”上面做得恰到好处。 看到Kotlin,我马上就想起了这个演讲:“Please stop polluting our imperative languages with pure concepts”。 Kotlin有以下好处: 1. 强大的IDE。而且是JetBrains第一方支持,不是3年更新一次的第三方插件; 2. 库多生态强。Kotlin的设计者非常重视和Java的互操作,所以Kotlin号称可以无缝衔接所有Java库。 3. 宇宙第一运行时:JVM。 -4. Android上不能用Java8的新语法,Kotlin恰逢其时的出现,抓了一波完美的timing。如果Kotlin依靠Android开发爆发,那服务器端,大数据界,也会收益,最后多面开花,势不可挡。 +4. Android上不能用Java8的新语法,Kotlin恰逢其时的出现,抓了一波完美的timing。 + +如果Kotlin依靠Android开发爆发,那服务器端,大数据界,也会收益,最后多面开花,势不可挡。 但是Kotlin刚出来,到底有没有它自称的那么好用还待观察。另外,Kotlin社区现在集中力量攻坚Android,在服务器和大数据方向没什么靠谱项目。所以还是得用Java8。 总之,“魔镜啊魔镜,谁是JVM上最好的语言”之最后决战,将是Java10 vs Kotlin(Java9在语法特性上已经输了)。而在这场最终决战之前,C#已经靠着CoreCLR统一世界了。 -最后送上人生经验两则: -1. +> 最后送上人生经验两则: ``` match comment with | "X怎么不能Y?人家Z就是这样做的。" -> reply "卡马克能用haskell移植Wolf 3D,你能?" | _ -> reply "Thank you" ``` -2. + ``` match location with | Office -> use whatever your boss chose diff --git a/Java/Ecosystem/Mybatis.md b/Java/Ecosystem/Mybatis.md index 69b5f60..ab37c45 100644 --- a/Java/Ecosystem/Mybatis.md +++ b/Java/Ecosystem/Mybatis.md @@ -1,231 +1,87 @@ -`目录 start` - -- [Mybatis](#mybatis) - - [Mybatis](#mybatis) - - [xml文件配置:](#xml文件配置) - - [主配置文件:](#主配置文件) - - [操作配置文件:](#操作配置文件) - - [导入JAR包:](#导入jar包) - - [创建SqlSessionFactory类 内容:](#创建sqlsessionfactory类-内容) - - [maven Spring-mybaits 配置](#maven-spring-mybaits-配置) - - [**SessionFactory类,使用Spring注入一个工厂类,然后使用本地线程组,节省Session开销**](#sessionfactory类使用spring注入一个工厂类然后使用本地线程组节省session开销) - - [流程控制](#流程控制) - - [foreach 循环语句](#foreach-循环语句) - - [collection 有 arry list map 几种 还有item是必写,其他的是可选的](#collection-有-arry-list-map-几种-还有item是必写其他的是可选的) - - [if 判断语句:](#if-判断语句) - - [set 方便书写update语句](#set-方便书写update语句) - - [choose 相当于switch语句](#choose-相当于switch语句) - - [$和的区别:](#$和的区别) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Mybatis +date: 2018-11-21 10:56:52 +tags: + - ORM +categories: + - Java +--- + +💠 + +- 1. [Mybatis](#mybatis) + - 1.1. [流程控制](#流程控制) + - 1.1.1. [foreach 循环语句](#foreach-循环语句) + - 1.1.1.1. [collection](#collection) + - 1.1.2. [if 判断语句](#if-判断语句) + - 1.1.3. [choose 相当于switch语句](#choose-相当于switch语句) + - 1.2. [延迟加载](#延迟加载) + - 1.3. [缓存](#缓存) + - 1.3.1. [分布式缓存](#分布式缓存) + - 1.4. [Spring整合](#spring整合) +- 2. [Tips](#tips) + +💠 2024-03-26 21:19:24 **************************************** # Mybatis -> 一个灵活的数据库中间件框架 - - -## Mybatis -### xml文件配置: -- 创建mybatis-config.xml文件 - - 该文件是主配置文件,配置了sessionFactory -- 创建generatorConfig.xml文件 - - 是各种操作的配置,一个操作对应一个SQL的配置 - -#### 主配置文件: -```xml - - - - - - - - - - - - - - - - - - - - - - - - -``` -##### 操作配置文件: -```xml - - - - - - - - - - - - select LAST_INSERT_ID() - - insert into inserts (name) values(#{name}) - - - - delete from inserts where id=#{id} - - - - update inserts set name=#{name} where id=#{id} - - -``` -### 导入JAR包: -- **核心包** -- mybatis-3.4.1.jar 主包 -- dom4j-1.6.1.jar 日志记录 -- log4j-1.2.15.jar -- slf4j-api-1.5.8.jar -- slf4j-log4j12.jar - -### 创建SqlSessionFactory类 内容: -```java - private static SqlSessionFactory sessionFactory; - static{ - try{ - String resource = "cn/mybatis/test/mybatis-config.xml"; - InputStream inputStream = Resources.getResourceAsStream(resource); - sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); - }catch (Exception e) { - System.out.println("获取Session失败"); - } - } - /** - * 获取Session - */ - public static SqlSession getSession(){ - SqlSession session = null; - session = sessionFactory.openSession(); - return session; - } -``` +> [Official](https://mybatis.org/mybatis-3/) +> [mybatis-issues](https://github.com/harawata/mybatis-issues)`SSCCE: Short, Self Contained, Correct (Compilable), Example.` -#### maven Spring-mybaits 配置 -- 使用Spring自动注入对象,方便别名和SessionFactory的管理 -- pom引入必须的JAR包就可以了 +> 一个灵活的数据库中间件框架 +> [参考: 如何在MyBatis中优雅的使用枚举](https://segmentfault.com/a/1190000010755321) -```xml - - - - - - - - - - - - - - - - - - - - - - - - -``` +> [mybatis系统学习](https://github.com/brianway/springmvc-mybatis-learning) -##### **SessionFactory类,使用Spring注入一个工厂类,然后使用本地线程组,节省Session开销** - -```java - - @Component - public class MybatisSessionFactory { - @Autowired - private SqlSessionFactory sessionFactory; - // 日志 - private static org.slf4j.Logger Log = LoggerFactory.getLogger(MybatisSessionFactory.class); - //使用本地线程组能避免不必要的Session开支,加强性能 - private static final ThreadLocal THREAD_LOCAL = new ThreadLocal(); - /** - * 获取Session - * @return - */ - public SqlSession getSession(){ - SqlSession session = (SqlSession)THREAD_LOCAL.get(); - if(session==null ){ - session = this.sessionFactory.openSession(); - THREAD_LOCAL.set(session); - } - Log.info("__获取了一个Session__"+session); - return session; - } - /* - 关闭连接 - */ - public void closeSession(){ - SqlSession session = (SqlSession)THREAD_LOCAL.get(); - THREAD_LOCAL.set(null); - if(session!=null){ - session.close(); - } - } - public SqlSessionFactory getSessionFactory() { - return sessionFactory; - } - public void setSessionFactory(SqlSessionFactory sessionFactory) { - this.sessionFactory = sessionFactory; - } - } -``` +> $ 和 # 的区别 =-] +- `${}` 会有SQL注入的漏洞,`#{}`则没有 + - 使用 $ 是SQL进行String直接进行拼接,使用#是preparstatement的预处理然后注入 +- 都遵循 [OGNL](https://www.ibm.com/developerworks/cn/opensource/os-cn-ognl/) 语法 -### 流程控制 +## 流程控制 -#### foreach 循环语句 +### foreach 循环语句 ```xml ${params} ``` -##### collection 有 arry list map 几种 还有item是必写,其他的是可选的 -#### if 判断语句: -- `` +#### collection + +有 arry list map 几种 还有item是必写,其他的是可选的 -#### set 方便书写update语句 -- `col=#{col},` +### if 判断语句 +- `` -> mybatis会自动去除多余的逗号,但是每一行书写要写逗号 +- update 判空 set `col=#{col},` + - mybatis会自动去除多余的逗号 -#### choose 相当于switch语句 +### choose 相当于switch语句 - `` -#### $和#的区别: -- \$ 会有SQL注入的漏洞,#则没有 -- 使用$ 是SQL进行String的拼接,使用#是preparstatement的预处理然后注入 -- 使用#的时候出现这个问题 -2017-01-22 11:16:11.046 [main] DEBUG myth.book.getAll_Param_BookType - ==> Preparing: select * from book_type where ? and ? and 1=1; -2017-01-22 11:16:11.136 [main] DEBUG myth.book.getAll_Param_BookType - ==> Parameters: book_type<10 (String), 'father_type='2 (String) -- 条件不能使用数值, - 条件是单独使用时也是String但是是有效的 +************************ + +## 延迟加载 +需要使用到数据的时候才去查询和加载,没有使用到就不加载。 例如A对象有个属性是`List` +因为A对B是一对多,使用延迟加载就可以达到不使用A属性的B集合对象时不查询B表,使用到才触发查询 + +************************ + +## 缓存 +- 一级缓存 + - 一级缓存是SqlSession级别的缓存。在操作数据库时需要构造sqlSession对象,在对象中有一个数据结构(HashMap)用于存储缓存数据。不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。 +- 二级缓存 + - 二级缓存是mapper级别的缓存,多个SqlSession去操作同一个Mapper的sql语句,多个SqlSession可以共用二级缓存,二级缓存是跨SqlSession的。 + +### 分布式缓存 +mybatis提供了一个cache接口,可用于实现自己的缓存逻辑 + +> [整合ehcache](https://github.com/brianway/springmvc-mybatis-learning/blob/master/mybatis/mybatis%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0(16)-mybatis%E6%95%B4%E5%90%88ehcache.md)`其中使用ehcache是本地单机模式的,实际上ehcache是支持分布式的` + +## Spring整合 +> [spring和mybatis整合](https://github.com/brianway/springmvc-mybatis-learning/blob/master/mybatis/mybatis%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0(17)-spring%E5%92%8Cmybatis%E6%95%B4%E5%90%88.md) + + +************************ +# Tips +1. 展示执行SQL `logging.level.mapperAbsolutePackagePath=DEBUG` [Logging](https://mybatis.org/mybatis-3/logging.html) +1. Mybatis-Plus 有个 ActiveRecords 模式,想要让实体具有持久层的能力。整个框架都不利于寻找数据的流入 入口。 diff --git a/Java/Ecosystem/Netty.md b/Java/Ecosystem/Netty.md deleted file mode 100644 index 8b5c252..0000000 --- a/Java/Ecosystem/Netty.md +++ /dev/null @@ -1,60 +0,0 @@ -`目录 start` - -- [Netty](#netty) - - [原理](#原理) - - [编解码相关](#编解码相关) - - [Protobuf](#protobuf) - - [使用](#使用) - - [源码](#源码) - - [配置环境](#配置环境) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Netty -> [trustlin](https://github.com/trustin)`Netty mina 的作者` - -> [Netty4.x官方文档](http://netty.io/wiki/user-guide-for-4.x.html) -> [Netty权威指南](https://javablog.net/book/3/netty-authoritative-guide.html) - -- [为什么选择Netty作为基础通信组件? ](https://my.oschina.net/zhaky/blog/760469) -- [Reactive Extension (Rx) Adaptor for Netty ](https://github.com/ReactiveX/RxNetty) - -> [《Netty 实战》 Netty In Action 中文版](https://github.com/ReactivePlatform/netty-in-action-cn) -******************** -## 原理 -> [Netty核心组件](http://cmsblogs.com/?p=2467) -> [Netty 编解码技术 数据通信和心跳监控案例](https://segmentfault.com/a/1190000013122610) -> [Netty 拆包粘包和服务启动流程分析](https://segmentfault.com/a/1190000013039327) -> [Netty序章之BIO NIO AIO演变](https://segmentfault.com/a/1190000012976683) - -> [Netty构建游戏服务器(一) 有原理图](http://ju.outofmemory.cn/entry/278582) -[Netty高性能开发备忘录](http://www.10tiao.com/html/321/201611/2659763226/5.html) - -### 编解码相关 -> [更多](https://github.com/kuangcp/Notes/blob/master/Java/AdvancedLearning/ClassFile.md#其他业内主流编解码框架) - -#### Protobuf -> [Protobuf基础](/Java/AdvancedLearning/ClassFile.md#protobuf) | -> [Netty中的使用案例](https://github.com/Kuangcp/NettyBook2/blob/master/src/main/java/com/phei/netty/codec/protobuf/README.md) ->> 要搭配处理半包的解码器 - -1. 使用 ProtobufVarint32FrameDecoder -2. 继承自 LengthFieldBasedFrameDecoder -3. 继承自 ByteToMessageDecoder 自己处理 - -***************************** -## 使用 -> 部分内容参考自 Netty权威指南第二版 - -> [手淘、微博一直钟情的 Netty框架是个什么鬼?](https://yq.aliyun.com/roundtable/53346) -> [对于Netty的十一个疑问 ](https://news.cnblogs.com/n/205413/) -> [NettyServer与SpringBoot集成](https://segmentfault.com/a/1190000004919133) -> [Netty NIO 框架性能压测-短链接-对比Tomcat ](http://www.oschina.net/question/12_8749) - -### 源码 -> [官方Demo](https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example) -> [Netty实战配套源码](https://github.com/ReactivePlatform/netty-in-action-cn) -> [Netty权威指南2 源码](https://github.com/Kuangcp/NettyBook2) - -### 配置环境 - diff --git a/Java/Ecosystem/Netty/Netty.md b/Java/Ecosystem/Netty/Netty.md new file mode 100644 index 0000000..c50454c --- /dev/null +++ b/Java/Ecosystem/Netty/Netty.md @@ -0,0 +1,107 @@ +--- +title: Netty +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [Netty 使用](#netty-使用) + - 1.1. [基础构件](#基础构件) + - 1.2. [编解码相关](#编解码相关) + - 1.2.1. [Protobuf](#protobuf) +- 2. [Websocket](#websocket) +- 3. [衍生框架](#衍生框架) +- 4. [Reactor Netty](#reactor-netty) + +💠 2024-07-24 17:28:31 +**************************************** +# Netty 使用 +> [NettyServer与SpringBoot集成](https://segmentfault.com/a/1190000004919133) +> [Netty NIO 框架性能压测-短链接-对比Tomcat ](http://www.oschina.net/question/12_8749) + +> [Github: Netty Example](https://github.com/netty/netty/tree/4.1/example) + +## 基础构件 + +- Channel + - Channel 是 NIO 基本的结构:一个 用于连接到实体(硬件设备 、文件 、网络套接字或程序组件),能够执行一个或多个不同的 I/O 操作(读或写)的开放连接。 + +- Callback + - 回调方法,常用于通知其他模块操作已完成 + +- Future + - 提供了一种 通知应用操作已经完成 的方式: 这个 对象 作为一个 **异步操作结果的占位符**, 它在 将来的某个时候 完成并提供结果。 + - JDK java.util.concurrent.Future 提供的实现只允许您手动检查操作是否完成或阻塞了, Netty自己开发了ChannelFuture + - ChannelFuture 可注册多个 ChannelFutureListener + - 在Future操作完成时会调用 Listener的operationComplete + - 如果Future有执行异常返回的值是CauseHolder的实例包住了产生的Throwable + - ChannelFutureListener 提供的通知机制不需要手动检查操作是否完成的 每个 Netty 的 outbound I/O 操作都会返回一个 ChannelFuture,这样就不会阻塞 + - 这就是 Netty 所谓的 `自底向上的异步和事件驱动` + +- Event和Handler + - 事件驱动:使用不同的Event通知状态的变更,Handler响应不同的Event。 + - Event 大致分类: 活跃或非活跃连接,数据读取,用户事件,异常 + - Handler 大致分类: 日志,数据转换,流控制,应用程序逻辑 + - Netty 的 ChannelHandler 是各种 处理程序的基本抽象 。每个 处理器实例 就是一个 回调 ,用于 执行对各种事件的响应 + - Netty 也提供了一组丰富的预定义的处理程序, 比如,各种协议的编解码器包括 HTTP 和 SSL/TLS + +> 组合使用 +- Future, Callback 和 Handler + - Netty 的异步编程模型是建立在 future 和 callback 的概念上的 + - **拦截操作** 和 **转换入站或出站数据** 只需要 提供回调 或 获取 future 操作返回的数据 + - 一个 Netty 的设计的主要目标是促进 关注点分离 : `使业务逻辑从网络基础设施应用程序中分离` + +- Selector, Event, EventLoop + - 通过 触发事件 从 应用程序 中 抽象 出 Selector ,从而避免手写调度代码 + - EventLoop 分配给每个 Channel 来处理所有的事件 ,包括 + - 被注册关注的事件 + - 调度事件给 ChannelHandler + - EventLoop 本身是由**单线程**去处理 Channel 所有的 I/O 事件,并且在 EventLoop 的生命周期内不会改变 + - 这个简单而强大的线程模型,使得 ChannelHandler 无需关注线程同步问题 + +## 编解码相关 +> [Netty 编解码技术 数据通信和心跳监控案例](https://segmentfault.com/a/1190000013122610) +> [Netty 拆包粘包和服务启动流程分析](https://segmentfault.com/a/1190000013039327) +> [参考: Netty(三) 什么是 TCP 拆、粘包?如何解决?](https://crossoverjie.top/2018/08/03/netty/Netty(3)TCP-Sticky/) + +### Protobuf +> [Protobuf基础](/Java/AdvancedLearning/ClassFile.md#protobuf) | +> [Netty中的使用案例](https://github.com/Kuangcp/NettyBook2/blob/master/src/main/java/com/phei/netty/codec/protobuf/README.md) +>> 要搭配处理半包的解码器 + +1. 使用 ProtobufVarint32FrameDecoder +2. 继承自 LengthFieldBasedFrameDecoder +3. 继承自 ByteToMessageDecoder 自己处理 + +***************************** + +# Websocket + +> 接收数据buffer读取流程: 优势是新连接申请的内存低,实际使用中会对申请的buffer扩缩容,平衡缓存池利用率和读取效率 +1. 读取Socket中数据入口: `io.netty.channel.nio.AbstractNioByteChannel.NioByteUnsafe#read` + - 在 byteBuf = allocHandle.allocate(allocator); 调用中会依据以往读取值 `AdaptiveRecvByteBufAllocator.HandleImpl#guess()` 一个大小并使用 + - 其中 allocHandle 是 `AdaptiveRecvByteBufAllocator` allocator 是 `PooledByteBufAllocator` + - 每次读取完成后都会 `AdaptiveRecvByteBufAllocator.HandleImpl#record()` 方法记录,按 AdaptiveRecvByteBufAllocator.SIZE_TABLE 做梯度扩缩容 + +> [参考: Netty WebSocket 拆包浅析](https://www.jianshu.com/p/30c26a755a87) +- io.netty.handler.codec.http.websocketx.WebSocket08FrameDecoder#decode +- [ ] 文本数据达到多大,会遇到拆包问题 + +************************ + +# 衍生框架 +> [netty-socketio](https://github.com/mrniko/netty-socketio) +> [kcp-netty](https://github.com/szhnet/kcp-netty) +> [Reactive Extension (Rx) Adaptor for Netty ](https://github.com/ReactiveX/RxNetty) RxNetty +> [Netty Servlet](https://github.com/wangzihaogithub/spring-boot-protocol) + + +************************ + +# Reactor Netty +> [Doc](https://projectreactor.io/docs/netty/release/reference/index.html#about-doc) + +> [个人 样例代码](https://github.com/Kuangcp/JavaBase/tree/master/netty/src/main/java/reactor) + diff --git a/Java/Ecosystem/Netty/NettyDesign.md b/Java/Ecosystem/Netty/NettyDesign.md new file mode 100644 index 0000000..9c9b3fb --- /dev/null +++ b/Java/Ecosystem/Netty/NettyDesign.md @@ -0,0 +1,44 @@ +--- +title: NettyDesign +date: 2024-06-16 16:48:43 +tags: +categories: +--- + +💠 + +- 1. [Netty Design](#netty-design) + - 1.1. [线程模型](#线程模型) + - 1.2. [内存设计](#内存设计) + +💠 2024-09-09 10:22:38 +**************************************** +# Netty Design +> [Netty序章之BIO NIO AIO演变](https://segmentfault.com/a/1190000012976683) + +> [ 大白话聊聊Netty ](https://mp.weixin.qq.com/s?__biz=MzIzOTU0NTQ0MA==&mid=2247538543&idx=1&sn=bc9d1575e21b42f215cf61e0a9da264e&scene=58&subscene=0) +> [Netty 实战(精髓)](https://github.com/waylau/essential-netty-in-action) + +> 源码解读 +> [官方Demo](https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example) +> [Netty实战配套源码](https://github.com/ReactivePlatform/netty-in-action-cn) +> [Netty权威指南2 源码](https://github.com/Kuangcp/NettyBook2) + +## 线程模型 +[主次Reactor多线程模型](/Skills/CS/IO.md#reactor) + +> Netty +![](/Java/Ecosystem/Netty/img/001-reactor-netty.drawio.svg) + +> [参考: 从线程模型的角度看 Netty 为什么是高性能的? ](https://crossoverjie.top/2018/07/04/netty/Netty(2)Thread-model/) + + +## 内存设计 + +> 直接内存 + +- -Dio.netty.noPreferDirect 是否运行通过底层api直接访问直接内存,默认:允许 +- -Dio.netty.noUnsafe 是否允许使用sun.misc.Unsafe,默认:允许 +- -Dio.netty.maxDirectMemory 设置最大值 + +************************ diff --git a/Java/Ecosystem/Netty/Readme.md b/Java/Ecosystem/Netty/Readme.md new file mode 100644 index 0000000..2ae1cfb --- /dev/null +++ b/Java/Ecosystem/Netty/Readme.md @@ -0,0 +1,31 @@ +# Netty +> [Trustlin](https://github.com/trustin) `Netty Mina 的作者` + +Netty是由JBOSS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 + +> [Netty4.x官方文档](http://netty.io/wiki/user-guide-for-4.x.html) +> [Netty权威指南](https://javablog.net/book/3/netty-authoritative-guide.html) + +> [Netty 实战(精髓)](https://klose911.github.io/html/netty/netty.html) +> [《Netty 实战》 Netty In Action 中文版](https://github.com/ReactivePlatform/netty-in-action-cn) + +************************ + +> [手淘、微博一直钟情的 Netty框架是个什么鬼?](https://yq.aliyun.com/roundtable/53346) +> [对于Netty的十一个疑问 ](https://news.cnblogs.com/n/205413/) +> [知乎: 通俗地讲,Netty 能做什么?](https://www.zhihu.com/question/24322387) +> [为什么选择Netty作为基础通信组件? ](https://my.oschina.net/zhaky/blog/760469) + +RocketMQ、Elasticsearch、gRPC、Apache Dubbo、Spring5、HSF、 Zookeeper、Spark、Hadoop等等 的网络层均使用到Netty。 + +> 常见优势 + +开发门槛低:API 使用简单; +定制能力强:可以通过 ChannelHandler对通信框架进行灵活地扩展; +Handler强大:预置了多种编解码器,支持多种主流协议,对传输中粘包和拆包有现成解决方案,有完善的断连,idle(心跳检测)等异常处理; +高性能:与其他业界主流的 NIO 框架对比,Netty 的综合性能最优; +社区活跃,版本迭代周期短,发现的 BUG 可以被及时修复,同时更多的新功能会加入;经历了大规模的商业应用考验,质量有验证; + +> 实践 + +1. Tomcat 也有web连接组件 和 Netty 做的事情是类似的,是否可以替代? diff --git a/Java/Ecosystem/Netty/img/001-reactor-netty.drawio.svg b/Java/Ecosystem/Netty/img/001-reactor-netty.drawio.svg new file mode 100644 index 0000000..8b2df18 --- /dev/null +++ b/Java/Ecosystem/Netty/img/001-reactor-netty.drawio.svg @@ -0,0 +1,250 @@ + + + + + + + + + + +
+
+
+ 请求 +
+
+
+
+ + 请求 + +
+
+ + + + +
+
+
+ Client +
+
+
+
+ + Client + +
+
+ + + + + + +
+
+
+ Client +
+
+
+
+ + Client + +
+
+ + + + + + +
+
+
+ Client +
+
+
+
+ + Client + +
+
+ + + + +
+
+
+ Selector +
+
+
+
+ + Selector + +
+
+ + + + + + +
+
+
+ accept +
+
+
+
+ + accept + +
+
+ + + + +
+
+
+ BossGroup +
+
+
+
+ + BossGroup + +
+
+ + + + + + +
+
+
+ SocketChannel +
+
+
+
+ + SocketChannel + +
+
+ + + + + + + +
+
+
+ Selector +
+
+
+
+ + Selector + +
+
+ + + + +
+
+
+ WorkerGroup +
+
+
+
+ + WorkerGroup + +
+
+ + + + + + +
+
+
+ NIOSocketChannel +
+
+
+
+ + NIOSocketChannel + +
+
+ + + + +
+
+
+ Handler +
+
+
+
+ + Handler + +
+
+ + + + +
+
+
+ 业务逻辑 +
+
+
+
+ + 业务逻辑 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/Java/Ecosystem/POI.md b/Java/Ecosystem/POI.md index b29b335..73b5097 100644 --- a/Java/Ecosystem/POI.md +++ b/Java/Ecosystem/POI.md @@ -1,19 +1,32 @@ -`目录 start` - -- [POI](#poi) - - [Excel](#excel) - - [Word](#word) +--- +title: POI +date: 2018-11-21 10:56:52 +tags: +categories: +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [POI](#poi) + 1. [Excel](#excel) + 1. [Word](#word) + 1. [二次开发项目](#二次开发项目) + 1. [Easy excel](#easy-excel) + +**目录 end**|_2020-04-27 23:42_| **************************************** # POI -> Java操作ExcelWord等软件 +> Java 操作 Excel Word ## Excel - ## Word -> [参考博客: POI 操作word](http://blog.csdn.net/j_a_d_e/article/details/53945288) +> [参考: POI 操作word](http://blog.csdn.net/j_a_d_e/article/details/53945288) + +## 二次开发项目 +- [easypoi](https://gitee.com/lemur/easypoi) `虽然代码不是很规范但是开源精神要学习` +### Easy excel +> [Github](https://github.com/alibaba/easyexcel) diff --git a/Java/Ecosystem/Querydsl.md b/Java/Ecosystem/QueryDSL.md similarity index 64% rename from Java/Ecosystem/Querydsl.md rename to Java/Ecosystem/QueryDSL.md index 11092e9..442cbe5 100644 --- a/Java/Ecosystem/Querydsl.md +++ b/Java/Ecosystem/QueryDSL.md @@ -1,17 +1,20 @@ -`目录 start` - -- [Querydsl](#querydsl) - - [个人感想](#个人感想) +--- +title: Querydsl +date: 2018-11-21 10:56:52 +tags: +categories: +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [QueryDSL](#querydsl) + +**目录 end**|_2020-04-27 23:42_| **************************************** -# Querydsl +# QueryDSL > [Github 地址](https://github.com/querydsl/querydsl) | [最新文档地址](http://www.querydsl.com/static/querydsl/latest/reference/html/) > Querydsl 是一个通用的查询框架, 专注于通过Java API构建类型安全的SQL查询。 > Querydsl可以通过一组通用的查询API为用户构建出适合不同类型ORM框架或者是SQL的查询语句,也就是说QueryDSL是基于各种ORM框架以及SQL之上的一个通用的查询框架。 > 借助QueryDSL可以在任何支持的ORM框架或者SQL平台上以一种通用的API方式来构建查询。目前QueryDSL支持的平台包括JPA,JDO,SQL,Java Collections,RDF,Lucene,Hibernate Search。 -## 个人感想 -> 初接触, 看起来很是漂亮, 一如Java8的优美, 不用折腾SQL, 2018-06-13 20:48:18 - diff --git a/Java/Ecosystem/README.md b/Java/Ecosystem/README.md index ad8f707..3d45fd2 100644 --- a/Java/Ecosystem/README.md +++ b/Java/Ecosystem/README.md @@ -1,4 +1,14 @@ # 生态 -> [参考博客: 除了Guava,Java开发者还值得了解的5个谷歌类库](http://www.techug.com/post/forget-guava-5-google-libraries-java-developers.html) -`Guice ErrorProne Truth Kythe Protobuf` \ No newline at end of file +> [参考: 除了Guava,Java开发者还值得了解的5个谷歌类库](http://www.techug.com/post/forget-guava-5-google-libraries-java-developers.html) +`Guice ErrorProne Truth Kythe Protobuf` + +- [ ] canal + + +- [Hibernate](/Java/Ecosystem/Hibernate.md) | [Mybatis](/Java/Ecosystem/Mybatis.md) 对比 + - 有些设计思想是一致的,一二级缓存, Mybatis可以灵活自定义SQL,Hibernate也是可以的,相较现在这种分布式的环境下,单表操作两者没什么区别 + - 在Hibernate基础之上的[JPA](/Java/Ecosystem/JPA.md)能更简化开发, 在需要写复杂SQL的时候也是可以自定义的 + - 而且很多功能实际上用处不大了,一二级缓存,一对一一对多这样的映射 等等功能。缓存通常会交给分布式缓存实现,不会给表配置外键(为了性能以及避免频繁的死锁) + - 参考:数据库的诸多设计,账号,权限,约束,触发器,都是为 C/S 结构设计的,是以 C 端不可信做为假设前提的。B/S 模式安全边界前移到 web 服务层,应用与数据库之间是可信的,应用自行完成这些功能更加灵活。所以能不用就不用 [大家设计数据库时使用外键吗?](https://www.zhihu.com/question/19600081) + diff --git a/Java/Ecosystem/Scheduler/Quartz.md b/Java/Ecosystem/Scheduler/Quartz.md new file mode 100644 index 0000000..db36c6f --- /dev/null +++ b/Java/Ecosystem/Scheduler/Quartz.md @@ -0,0 +1,280 @@ +--- +title: Quartz +date: 2023-10-03 20:19:48 +tags: +categories: +--- + +💠 + +1. [Quartz学习](#quartz学习) + 1. [官方样例](#官方样例) + 1. [Quartz中常用的一些类](#quartz中常用的一些类) + 1. [有状态的Job与无状态的Job](#有状态的job与无状态的job) + 1. [Quartz中重要的几个监听器](#quartz中重要的几个监听器) + +💠 2023-10-03 20:22 +**************************************** +# Quartz学习 + +> 添加依赖 +```groovy + compile group: 'org.quartz-scheduler', name: 'quartz', version: '2.2.1' + compile group: 'org.quartz-scheduler', name: 'quartz-jobs', version: '2.2.1' +``` + +## 官方样例 + +- 定义需执行的任务类 + +```kotlin +/** +* 类实现Job接口中的execute方法 +*/ +class HelloJob: Job { + override fun execute(context: JobExecutionContext?) { + println("hello") + } +} +``` + +- 设置运行任务 + +```kotlin +fun main(args: Array) { + // 获取Scheduler用于调度任务 + var scheduler = StdSchedulerFactory.getDefaultScheduler() + // 开始执行任务 + scheduler.start() + + // 定义需执行的任务,指定我们定义的任务类 + val job = newJob(HelloJob::class.java) + .withIdentity("job1", "group1") + .build() + + // 定义任务的触发器 + val trigger = newTrigger() + .withIdentity("trigger1", "group1") + .startNow() + .withSchedule(simpleSchedule() + .withIntervalInSeconds(40) + .repeatForever()) + .build() + + // 告知Scheduler执行的任务及任务的触发器 + scheduler.scheduleJob(job, trigger) + // 留点时间等待任务执行 + Thread.sleep(6000) + // 关闭Scheduler + scheduler.shutdown() +} +``` + +> 在官方示例中,通过类通过实现`Job`接口来定义任务。 +> 在Quartz中,任务的运行需要定义任务与触发器;任务用于指定需任务,触发器用于定义任务的触发时机 + +## Quartz中常用的一些类 + +- `Job`: Job接口,任务类需要实现的接口。注意:`实现该接口的类必须存在默认的无参构造器` + +- `JobDetail`: 该类为`Job`提供了许多属性, 其中包含`JobDataMap`;Quartz不存储Job类的实际实例,而是通过使用JobDetail定义一个实例。JobDetails通过JobBuilder创建定义。 + +- `JobExecutionContext`: 在实现`Job`接口的类中,实现的`execute`方法会传入JobExecutionContext的实例。调用的Job通过JobExecutionContext的实例可以访问到Quartz运行的环境及Job的一些信息等 + +- `JobDataMap`: 可用于装载任何可序列化的数据。JobDataMap将存储在JobExecutionContext中,任务可通过JobExecutionContext实例来获取JobDataMap中的数据;(JobDataMap底层采用Map的数据结构),如: + + - 通过JobDataMap传递数据给Job + + ```kotlin + // 简单的传递键为hello,值为world的数据,该数据将会存放在JobDataMap中 + val job = newJob(HelloJob::class.java) + .withIdentity("job1", "group1") + .usingJobData("hello", "world") + .build() + ``` + + - Job通过JobExecutionContext获取JobDataMap中的数据 + + ```kotlin + class HelloJob: Job { + override fun execute(context: JobExecutionContext?) { + val name = context!!.jobDetail.key.name + // 获取JobDataMap中的数据,取值也可在Job类中定义与键对应的属性,这样也可获取数据 + val hello = context.jobDetail.jobDataMap["hello"] + println("$name is running, $hello") + } + } + ``` + +- `Trigger`: 任务的触发器,在此处只介绍常用的两种触发器 + + - `SimpleTrigger`: 它只能用于指定任务在一个特定时间内运行,可指定任务的重复(时间,次数)与间隔(时间,次数), 示例如下(简单介绍,具体请查看API) + + - 立即运行并每1秒运行一次,直到程序结束 + + ```kotlin + val trigger = newTrigger() + .withIdentity("trigger1", "group1") + .startNow() + .withSchedule(simpleSchedule() + .withIntervalInSeconds(1) + .repeatForever()) + .build() + ``` + + - 立即运行任务并每1秒运行一次,总共运行3次 + + ```kotlin + val trigger = newTrigger() + .withIdentity("trigger1", "group1") + .startNow() + .withSchedule(simpleSchedule() + .withIntervalInSeconds(1) + .withRepeatCount(2)) + .build() + ``` + + - `CronTrigger`: 该触发器可通过cron表达式来定义触发任务(不了解cron表达式的可百度,此处不做介绍) + + - 每秒执行一次任务 + + ```kotlin + val trigger = newTrigger() + .withIdentity("trigger1", "group1") + .startNow() + .withSchedule(CronScheduleBuilder.cronSchedule("0/1 * * * * ?")) + .build() + ``` + + +## 有状态的Job与无状态的Job + +> 在Quartz中,Job可能会持有某些状态信息,例如在Job中的index属性用于计算任务的调用次数,这些信息将被存储在JobDataMap,这时候便就是`有状态Job` +> 无状态Job在每次运行时都会创建新的JobDataMap,即同一任务的后一次调用无法获取前一次调用保存的信息 + +- 将设置Job为有状态 + +> 若需将Job设置为有状态,只需在Job类上使用`@PersistJobDataAfterExecution`注解,这样任务在多次运行时则不会重新创建新的JobDataMap + +- 示例 + + - 传递index值给Job + + ```kotlin + val job = newJob(HelloJob::class.java) + .withIdentity("job1", "group1") + .usingJobData("index", 0) + .build() + ``` + + - 将Job设置为有状态,并将index的值放入JobDataMap中(该示例通过在任务类中定义属性来获取JobDataMap的数据) + + ```kotlin + @PersistJobDataAfterExecution + class HelloJob: Job { + + var index = 0 + + override fun execute(context: JobExecutionContext?) { + val name = context!!.jobDetail.key.name + println("$name is running, index is $index") + index++ + // 存放数据到JobDataMap中 + context.jobDetail.jobDataMap["index"] = index + } + } + ``` + +## Quartz中重要的几个监听器 + +- `JobListener`: 任务调度中,与任务Job相关的监听器 + + - 定义JobListener(通过实现JobListener接口) + + ```kotlin + class MyJobListener : JobListener { + override fun getName(): String? { + println("JobListener getName") + return this.javaClass.name + } + + override fun jobToBeExecuted(context: JobExecutionContext?) { + println("${context!!.jobDetail.key.name} : 在Job将要被执行时执行") + } + + override fun jobWasExecuted(context: JobExecutionContext?, jobException: JobExecutionException?) { + println("${context!!.jobDetail.key.name} : 在Job被执行后执行") + } + + override fun jobExecutionVetoed(context: JobExecutionContext?) { + println("${context!!.jobDetail.key.name} : 在Job将要被执行时执行, 但又被TriggerListener否决时调用") + } + + } + ``` + + - 将JobListener与任务调度关联 + + - 定义全局JobListener + + ``` + scheduler.scheduleJob(job, trigger) + // 定义全局JobListener + scheduler.listenerManager.addJobListener(MyJobListener(), EverythingMatcher.allJobs()) + ``` + + - 定义局部JobListener + + ``` + scheduler.scheduleJob(job, trigger) + // 将JobListener与指定的任务Job相关联 + scheduler.listenerManager.addJobListener(MyJobListener(), KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1"))) + ``` + +- `TriggerListener`: 用于监听与Trigger相关的事件 + + - 定义TriggerListener(实现TriggerListener接口) + + ```kotlin + class MyTriggerListener : TriggerListener { + override fun triggerFired(trigger: Trigger?, context: JobExecutionContext?) { + println("相关联的Trigger被触发,Job的execute将被执行时触发") + } + + override fun getName(): String { + println("MyTriggerListener : getName") + return this::class.java.simpleName + } + + override fun vetoJobExecution(trigger: Trigger?, context: JobExecutionContext?): Boolean { + println("相关联的Trigger被触发,Job将被调用时,Scheduler调用该方法,可否决Job的执行,若返回true则该Job不会因此次Trigger触发而执行") + return false + } + + override fun triggerComplete(trigger: Trigger?, context: JobExecutionContext?, triggerInstructionCode: Trigger.CompletedExecutionInstruction?) { + println("相关联的Trigger被触发,并且完成Job调用时执行") + } + + override fun triggerMisfired(trigger: Trigger?) { + println("当Trigger错过触发时调用") + } + } + ``` + + - 将TriggerListener与任务调度关联 + + - 定义全局TriggerListener + + ``` + // 定义全局TriggerListener + scheduler.listenerManager.addTriggerListener(MyTriggerListener(), EverythingMatcher.allTriggers()) + ``` + + - 定义局部TriggerListener + + ``` + // 将riggerListener与指定的Trigger关联 + scheduler.listenerManager.addTriggerListener(MyTriggerListener(), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1", "group1"))) + ``` + +- `SchedulerListener`: 在Scheduler生命周期中的关键事件发生时调用 diff --git a/Java/Ecosystem/Scheduler/Readme.md b/Java/Ecosystem/Scheduler/Readme.md new file mode 100644 index 0000000..633b4ed --- /dev/null +++ b/Java/Ecosystem/Scheduler/Readme.md @@ -0,0 +1,6 @@ +# Scheduler + +[xxl-job](https://github.com/xuxueli/xxl-job) +[quartz](https://github.com/quartz-scheduler/quartz) +[db-scheduler](https://github.com/kagkarlsson/db-scheduler) + diff --git a/Java/Ecosystem/Servlet/Jetty.md b/Java/Ecosystem/Servlet/Jetty.md new file mode 100644 index 0000000..c499996 --- /dev/null +++ b/Java/Ecosystem/Servlet/Jetty.md @@ -0,0 +1,31 @@ +--- +title: Jetty +date: 2024-06-12 10:01:19 +tags: +categories: +--- + +💠 + +- 1. [Jetty](#jetty) + - 1.1. [配置](#配置) + +💠 2024-06-12 10:01:44 +**************************************** +# Jetty + +> [Jetty官网](http://www.eclipse.org/jetty/) +> [jetty-examples](https://github.com/jetty/jetty-examples) + + +[参考: Jetty使用教程(一)——开始使用Jetty ](http://www.cnblogs.com/yiwangzhibujian/p/5832597.html) + +## 配置 +> [相关](http://zetcode.com/java/jetty/logging/) 自身log配置 + +_resources/jetty-logging.properties_ 内容如下开启DEBUG +```conf + org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StrErrLog + org.eclipse.jetty.LEVEL=DEBUG + jetty.logs=logs +``` diff --git a/Java/Ecosystem/Servlet/JettyDesign.md b/Java/Ecosystem/Servlet/JettyDesign.md new file mode 100644 index 0000000..72957c6 --- /dev/null +++ b/Java/Ecosystem/Servlet/JettyDesign.md @@ -0,0 +1,46 @@ +--- +title: JettyDesign +date: 2024-06-15 11:01:15 +tags: +categories: +--- + +💠 + +- 1. [Jetty Design](#jetty-design) + - 1.1. [宏观架构](#宏观架构) + - 1.2. [线程模型](#线程模型) + +💠 2024-06-15 11:05:37 +**************************************** +# Jetty Design +> [Github: Jetty](https://github.com/jetty/jetty.project) + +## 宏观架构 + +## 线程模型 + +从抽象来看三者关系如下 + +![](/Java/Ecosystem/Servlet/img/001-jetty-selector.drawio.svg) + +但是由 ExecutionStrategy接口的实现来提供三者在运行时的多种组合方式 [strategy源码](https://github.com/jetty/jetty.project/tree/jetty-12.0.x/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/thread/strategy) + +> ProduceConsume + +一个线程顺序读请求和执行业务,即所有IO数据处理Connector和Handler 用的是一个线程(线程池里只有一个线程),实际上缺点很明显吞吐量不高 + +> ProduceExecuteConsume + +一个线程顺序读,线程池执行业务,类似于单Reactor多线程模型。 + +> ExecuteProduceConsume + +相较于传统的[NIO Reactor](/Skills/CS/IO.md#Reactor)多Reactor多线程模型,主要的区别是 Connector和Handler在一个事件触发后是在同一个线程执行。 +好处是IO接收的数据无需传递和线程上下文切换,充分利用CPU缓存,弊端是如果业务逻辑Handler阻塞或CPU计算消耗很长时间就会快速耗尽线程池线程,然后导致Connetcor没有线程资源执行,从而引发请求积压和拒绝。 + +************************ + +Jetty实际运行时采用的是 EatWhatYouKill 策略, 线程池线程充足时采用ExecuteProduceConsume和低线程资源时采用ProduceExecuteConsume,为了避免低线程资源情况时的请求拒绝问题,将请求积压起来等线程池平稳的消费执行完。 + + diff --git a/Java/Ecosystem/Servlet/Readme.md b/Java/Ecosystem/Servlet/Readme.md new file mode 100644 index 0000000..8b5eb82 --- /dev/null +++ b/Java/Ecosystem/Servlet/Readme.md @@ -0,0 +1,29 @@ +# Servlet容器 +> [Comparing Embedded Servlet Containers in Spring Boot](https://www.baeldung.com/spring-boot-servlet-containers) + +## Tomcat +> 更多查看 `Tomcat那些事儿` 公众号 +> [Tomcat目录部署与Context描述文件context.xml ](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650859355&idx=1&sn=2122baf040ae337dba90201a48b4e11c&chksm=f1329888c645119eec4473e11beaf988c48ce02c52151502086595de59b65dd4bd7cf129530e&scene=21#wechat_redirect) +> | [Tomcat配置文件解析与Digester](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650859293&idx=1&sn=3c017b2675bb59fda8ae037b7a1e6cb4&chksm=f13298cec64511d8183a23f1b3110bc6b65e8742c6e76391a51c552d86c0bc81a34fab8d0a60&scene=21#wechat_redirect) +> | [Servlet到底是单例还是多例你了解吗?](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=401278436&idx=1&sn=7d28750b7cff1f706efb82c7fcaa73c5&scene=21#wechat_redirect) +> | [Tomcat类加载器以及应用间class隔离与共享 ](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650859298&idx=1&sn=8856375f2268fc33a6bb3fbc6932eca7&chksm=f13298f1c64511e77ef1d77d28272840ca56f62da6e11928c78827e8ec53f937f812a4b49aa0&scene=21#wechat_redirect) +> | [啥,Tomcat里竟然还有特权应用? ](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650859476&idx=1&sn=8be7a37b59a5d167998f6695a1606d39&chksm=f1329807c6451111d2a1c379221655dc87dd105b067f894bfb202d1f9f283bad310a5cdc2277&scene=21#wechat_redirect) +> | [你了解JMX在Tomcat的应用吗?](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=401135587&idx=1&sn=610950fda2eceb3683a9fe45078f1a83&scene=21#wechat_redirect) + +************************ + +> [参考: Jetty和Tomcat的选择:按场景而定](http://www.open-open.com/lib/view/open1322622094390.html) +> [详解web容器 - Jetty与Tomcat孰强孰弱](https://developer.aliyun.com/article/441105) + +``` + 一个简单项目, 就是index.jsp 里面放了个 Hello 字符串 + 经过对比 8.5.29 jetty 9.2 + 启动时间 jetty花费时间是Tomcat2倍 + 启动后内存 Jetty480M Tomcat300M + 1000并发 20000总量 + Tomcat涨到 460M 第二次480M 连续5次后上660M了 10次900M 最长时间时而220ms 时而 70ms + Jetty涨到770M 第二次压测直接上900M了 十次后也是900M 最长响应时间稳定在 220ms +``` + +## Undertow +> [Official Site](http://undertow.io/) diff --git a/Java/Ecosystem/Servlet/Servlet.md b/Java/Ecosystem/Servlet/Servlet.md new file mode 100644 index 0000000..d10b021 --- /dev/null +++ b/Java/Ecosystem/Servlet/Servlet.md @@ -0,0 +1,41 @@ +--- +title: Servlet +date: 2024-06-12 09:59:36 +tags: +categories: +--- + +💠 + +- 1. [Servlet](#servlet) + - 1.1. [Web容器和Web服务器](#web容器和web服务器) + +💠 2024-06-12 10:01:44 +**************************************** +# Servlet +> [wiki: Jakarta Servlet](https://en.wikipedia.org/wiki/Jakarta_Servlet) + + +- Servlet(Server Applet),全称 Java Servlet,未有中文译文。是用 Java 编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。 +- 狭义的 Servlet 是指 Java 语言实现的一个接口, 广义的 Servlet 是指任何实现了这个 Servlet 接口的类,一般情况下,人们将 Servlet 理解为后者。 +- Servlet 运行于支持 Java 的应用服务器中。从实现上讲,Servlet 可以响应任何类型的请求,但绝大多数情况下 Servlet 只用来扩展基于 HTTP 协议的 Web 服务器。 + +## Web容器和Web服务器 +> Web容器 + +`何为容器:` +容器是一种服务调用规范框架,J2EE 大量运用了容器和组件技术来构建分层的企业级应用。在 J2EE 规范中,相应的有 WEB Container 和 EJB Container 等。 + +- WEB 容器给处于其中的应用程序组件(JSP,SERVLET)提供一个环境,使 JSP,SERVLET 直接跟容器中的环境变量交互,不必关注其它系统问题 +- (从这个角度来说,web 容器应该属于架构上的概念)。web 容器主要由 WEB 服务器来实现。例如:TOMCAT,WEBLOGIC,WEBSPHERE 等。 +- 若容器提供的接口严格遵守 J2EE 规范中的 WEB APPLICATION 标准。我们把该容器叫做 J2EE 中的 WEB 容器。 + - WEB 容器更多的是跟基于 HTTP 的请求打交道。而 EJB 容器不是。它是更多的跟数据库、其它服务打交道。 +- 容器的行为是 将其内部的应用程序组件与外界的通信协议交互进行了隔离,从而减轻内部应用程序组件的负担(实现方面的负担?)。 + - 例如:SERVLET 不用关心 HTTP 的细节,而是直接引用环境变量 session、request、response 就行、EJB 不用关心数据库连接速度、各种事务控制,直接由容器来完成。 + +> Web服务器 +- Web 服务器(Web Server)可以处理 HTTP 协议。当 Web 服务器接收到一个 HTTP 请求,会返回一个 HTTP 响应,例如送回一个 HTML 页面。 +- Web 服务器可以响应针对静态页面或图片的请求, 进行页面跳转(redirect),或者把动态响应(dynamic response)的产生委托(delegate)给一些其它的程序 + - 例如 CGI 脚本,JSP(JavaServer Pages)脚本,servlets,ASP(Active Server Pages)脚本,服务器端 JavaScript,或者一些其它的服务器端技术。 + - Web 服务器仅仅提供一个可以执行服务器端程序和返回(程序所产生的)响应的环境,而不会超出职能范围。 + - Web 服务器主要是处理需要向浏览器发送 HTML 的请求以供浏览。 diff --git a/Java/Ecosystem/Servlet/Tomcat.md b/Java/Ecosystem/Servlet/Tomcat.md new file mode 100644 index 0000000..08ab3b2 --- /dev/null +++ b/Java/Ecosystem/Servlet/Tomcat.md @@ -0,0 +1,185 @@ +--- +title: Tomcat +date: 2018-12-20 10:26:32 +tags: + - Tomcat +categories: + - Java +--- + +💠 + +- 1. [Tomcat](#tomcat) + - 1.1. [目录结构](#目录结构) + - 1.2. [配置运行](#配置运行) + - 1.2.1. [配置解压方式的Tomcat](#配置解压方式的tomcat) + - 1.2.1.1. [IDE中配置运行](#ide中配置运行) + - 1.2.2. [编码](#编码) + - 1.2.3. [虚拟目录](#虚拟目录) + - 1.2.3.1. [默认主页](#默认主页) + - 1.2.3.2. [虚拟主机](#虚拟主机) + - 1.2.3.3. [配置 GZip压缩](#配置-gzip压缩) + - 1.2.3.4. [配置IO方式](#配置io方式) +- 2. [Tomcat Native](#tomcat-native) +- 3. [Tips](#tips) + +💠 2024-06-12 10:01:44 +**************************************** +# Tomcat +> [官方网站](http://tomcat.apache.org/) + +- 官网上大致有: + - Tomcat `7 8 8.5 9` 大版本 + - Tomcat Native `优化Tomcat性能,提升数倍` + - Apache Standard Taglib `JSTL的实现` + - Tomcat Connectors `用于连接IIS Apache` [官方文档](http://tomcat.apache.org/connectors-doc/index.html) + +> [一款功能强大的Tomcat管理监控工具](https://zhuanlan.zhihu.com/p/35557373?group_id=967469270317457408) +> [psi-probe](https://github.com/psi-probe/psi-probe)`Tomcat监控管理工具` + +************************ + +## 目录结构 +``` +├── bin 二进制文件, Shell脚本 +├── conf 配置 +├── lib jar包 +├── logs 日志 +├── temp 缓存 +├── webapps 应用, war发布的目录 +└── work +``` + +查看Tomcat版本 `sh bin/version.sh` + +## 配置运行 +> 个人配置好的 + +- 精简版, 适合放在服务器 [tomcat-clean-8.5.31](http://cloud.kuangcp.top/tomcat-clean-8.5.31.zip) | [tomcat-clean-9.0.8](http://cloud.kuangcp.top/tomcat-clean-9.0.8.zip) +- 个人配置版,适合个人使用 [tomcat-admin-9.0.8](http://cloud.kuangcp.top/tomcat-admin-9.0.8.zip) | [tomcat-admin-8.5.31](http://cloud.kuangcp.top/tomcat-admin-8.5.31.zip) + +### 配置解压方式的Tomcat +`Windows 平台` +1. 在setclasspath中把前几行关于JAVA_HOME,JRE_HOME的路径改成自己的 +2. 系统中添加catalina_home环境变量 +3. 运行tomcatw.exe配置里面所有的路径( JDK JRE ) +4. 双击tomcat.exe启动Tomcat + +`Linux 平台` +- 下载解压,然后 bin 目录下执行 `chmod +x *.sh` + +************************ + +> 配置管理账户 + +`配置管理账号 tomcat-users.xml 中的 tomcat-users 节点` + +```xml +   +   +   +   + + +``` +- 其中admin-gui是为了能访问manger的界面,manager-secret是为了可以上传war文件 + +************************ + +`配置本机外可访问管理页面` + +/conf/Catalina/localhost/ 下添加 manager.xml 文件 +```xml + + + +``` + +#### IDE中配置运行 +> [你一定不知道IDE里的Tomcat是怎么工作的! ](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=401107149&idx=1&sn=908bd8ba76b38417570056795626c163&scene=21#wechat_redirect) + +- 虽然IDE也是引用到解压的Tomcat路径, 但是只是使用了可执行文件, 配置文件和一系列中间文件都是和原Tomcat隔离的, 这样也保证了原Tomcat能单独运行不受影响 + +### 编码 +- 编辑conf/下的server.xml,配置Connector项 `URIEncoding="UTF-8"` +- 浏览器表单utf-8 xml utf-8 乱码 服务器 浏览器 乱码 使用response.setContentType("text/html; charset=utf-8");。 +- 无效方法response.setChaoactorEncoding; xml文件里面有乱码,saxreader会生成document错误。 +- 浏览器表单get方式:需要重新编码获得字符串 浏览器表单post方式 request.setCharactorEncoding(utf-8); +- 自己建立的工程里面的web.xml继承了conf/web.xml.只需要重写自己的web.xml相关的配置的参数就可以覆盖其功能 + +### 虚拟目录 +`指定webapp目录外的可访问的文件` +1. 方法1:conf/server.xml + - 当中找到host标签里 添加一行 `` + +1. 方法2:conf/catalina/localhost/myxml.xml + - context中添加 `` + - 访问方式 `http://localhsot:8080/myxml/` + +#### 默认主页 +`web.xml` +```xml + + index.html + +``` + +#### 虚拟主机 +`server.xml` +```xml + + + < Context path="/" docBase="d:/webA" /> +``` + +- `File f=new File("/information.xml");`这个写法是错的,空指针异常 +- `request.getParameter`返回字符串,如果表单里面是空的,就返回长度为零的字符串。 + +#### 配置 GZip压缩 +> [tomcat nginx开启Gzip原博客](http://www.imooc.com/article/15304) + +- 修改配置文件:/conf/server.xml +`原文件` +```xml + +``` +`修改成` +```xml + +``` +#### 配置IO方式 +> 默认http1.1是nio, 还有aio ajp bio + + +************************ + +# Tomcat Native +> [官方文档](http://tomcat.apache.org/native-doc/) | [参考: tomcat安装与配置native,apr](https://blog.csdn.net/shangruo/article/details/52776212) + + +************************* + +# Tips +- servletContextLisner 和Spring环境的加载顺序要注意 +- [Tomcat启动卡住,因为random](https://www.jianshu.com/p/576d356dc163) + +************************ +> [Tomcat 启动报错SEVERE: Unable to process Jar entry](https://www.jqhtml.com/43116.html) + +- 表现 + - 启动Tomcat 大量的 Unable to process Jar entry + - 最后 Tomcat OOM +- 排查过程 + - 首先判断为Maven缓存导致的问题, 下载下来的jar是有问题的, 但是通过比较 md5 发现文件是一致的 + - 然后搜索相关信息, javassist jar包依赖冲突, 也不是 +- 技术原因分析 + - 在这次遇到的问题是 spring-boot-autoconfigure 2.0.1.RELEASE 依赖不能和 Tomcat 7.0.55 兼容, 导致了 Unable to process Jar entry EOFException 报错 + - 但是这个报错不影响应用 深层次原因是 这个 autoconfigure 会尝试将项目所有依赖都加载扫描一次 + - 如果物理机或者容器内存不够, 就会直接down掉, 但是! 内存够的话 就不影响后续的启动, 除非应用确实需要使用SpringBoot框架的 2.0.1 版本 +- 人为原因 + - 没有做好依赖管理, 导致了 SpringBoot 被错误的引入 + diff --git a/Java/Ecosystem/Servlet/TomcatDesign.md b/Java/Ecosystem/Servlet/TomcatDesign.md new file mode 100644 index 0000000..a05caef --- /dev/null +++ b/Java/Ecosystem/Servlet/TomcatDesign.md @@ -0,0 +1,61 @@ +--- +title: TomcatDesign +date: 2024-05-14 17:22:24 +tags: +categories: +--- + +💠 + +- 1. [Tomcat Design](#tomcat-design) + - 1.1. [架构设计](#架构设计) + - 1.2. [线程池](#线程池) + - 1.3. [连接器](#连接器) + - 1.3.1. [NioEndpoint](#nioendpoint) + +💠 2024-06-02 17:50:57 +**************************************** +# Tomcat Design +> [Github Tomcat](https://github.com/apache/tomcat) +> [Compiling Tomcat Source Code By Maven](https://programmer.group/tomcat-source-analysis-i-compiling-tomcat-source-code.html) | [9.0.48 Source Repo](https://gitee.com/gin9/tomcat9-source) + +> [♥Tomcat源码详解知识体系详解♥](https://pdai.tech/md/framework/tomcat/tomcat-overview.html) +> [深入拆解Tomcat](https://time.geekbang.org/column/intro/100027701?tab=catalog) + +## 宏观架构 + + +## 线程池 +> [Doc: Tomcat Executor](https://tomcat.apache.org/tomcat-9.0-doc/config/executor.html) + +> StandardThreadExecutor + +Tomcat没有直接使用ThreadPoolExecutor而是扩展了 `threads.ThreadPoolExecutor`,一是可以隔离依赖,二是做定制化调整(核心改动为队列和提交任务,适配Tomcat生命周期管理)。 + +1. 执行逻辑调整 `StandardThreadExecutor#execute(java.lang.Runnable)` 当提交的任务触发拒绝策略时,尝试一次重新入队列。可能这段时间就有任务被消费了,可以提高一些服务可用性。 + +2. 队列自定义为 TaskQueue `public class TaskQueue extends LinkedBlockingQueue` 默认无界 + +Tomcat在EndPoint中通过acceptCount和maxConnections两个参数作用后,Tomcat默认的无界任务队列通常不会造成过多任务积压导致OOM。 + +其中maxConnections为Tomcat在任意时刻接收和处理的最大连接数,当Tomcat接收的连接数达到maxConnections时,Acceptor不会读取accept队列`对应于TCP连接中的全连接accept队列`中的连接; +这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections(maxConnections默认为10000,如果设置为-1,则连接数不受限制)。 +acceptCount为accept队列的长度,当accept队列中连接的个数达到acceptCount时,即队列满,此时进来的请求一律被拒绝,默认值是100(基于Tomcat 8.5.43版本)。 + +3. 生命周期管理 LifecycleMBeanBase + +任务结束时,上下文关闭时,停止所有线程,设置线程池参数,清理依赖资源。 + +************************ + +> StandardVirtualThreadExecutor 需Java 21,Tomcat9.0.76可用,最早可追溯到2022年随Loom项目开始筹备 + +内部实现为 VirtualThreadExecutor,并依据JreCompat做不同JDK版本的实现适配,最终通过 VirtualThreadBuilder 创建虚拟线程。 + + +## 连接器 +### NioEndpoint +NioEndpoint 要完成三件事情:接收连接、检测 I/O 事件以及处理请求,那么最核心的就是把这三件事情分开,用不同规模的线程数去处理. +比如用专门的线程组去跑 Acceptor,并且 Acceptor 的个数可以配置; +用专门的线程组去跑 Poller,Poller 的个数也可以配置; +最后具体任务的执行也由专门的线程池Executor来处理,也可以配置线程池的大小。 diff --git a/Java/Ecosystem/Servlet/img/001-jetty-selector.drawio.svg b/Java/Ecosystem/Servlet/img/001-jetty-selector.drawio.svg new file mode 100644 index 0000000..a940569 --- /dev/null +++ b/Java/Ecosystem/Servlet/img/001-jetty-selector.drawio.svg @@ -0,0 +1,251 @@ + + + + + + + + + + + + + + + + + + +
+
+
+ 请求 +
+
+
+
+ + 请求 + +
+
+ + + + + +
+
+
+ Client +
+
+
+
+ + Client + +
+
+ + + + +
+
+
+ Jetty Server +
+
+
+
+ + Jetty Server + +
+
+ + + + + +
+
+
+ 响应 +
+
+
+
+ + 响应 + +
+
+ + + + +
+
+
+ Connector +
+
+
+
+ + Connector + +
+
+ + + + +
+
+
+ Handler +
+
+
+
+ + Handler + +
+
+ + + + +
+
+
+ ThreadPool +
+
+
+
+ + ThreadPool + +
+
+ + + + +
+
+
+ Connector +
+
+
+
+ + Connector + +
+
+ + + + +
+
+
+ Connector +
+
+
+
+ + Connector + +
+
+ + + + +
+
+
+ Handler +
+
+
+
+ + Handler + +
+
+ + + + +
+
+
+ Handler +
+
+
+
+ + Handler + +
+
+ + + + + +
+
+
+ ServletRequest +
+
+
+
+ + ServletRequest + +
+
+ + + + + +
+
+
+ ServletResponse +
+
+
+
+ + ServletResponse + +
+
+ + +
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/Java/Ecosystem/Solr.md b/Java/Ecosystem/Solr.md index 478ba4f..4c9938e 100644 --- a/Java/Ecosystem/Solr.md +++ b/Java/Ecosystem/Solr.md @@ -1,9 +1,16 @@ -`目录 start` - -- [Solr](#solr) +--- +title: Solr +date: 2018-11-21 10:56:52 +tags: +categories: +--- -`目录 end` |_2018-08-23_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [Solr](#solr) + +**目录 end**|_2020-04-27 23:42_| **************************************** # Solr -> [参考博客: 用容器快速上手Apache Solr](http://qinghua.github.io/solr/) \ No newline at end of file +> [参考: 用容器快速上手Apache Solr](http://qinghua.github.io/solr/) diff --git a/Java/Ecosystem/Struts.md b/Java/Ecosystem/Struts.md deleted file mode 100644 index 9dd264f..0000000 --- a/Java/Ecosystem/Struts.md +++ /dev/null @@ -1,597 +0,0 @@ -`目录 start` - -- [Struts2笔记](#struts2笔记) - - [常见问题](#常见问题) - - [1.【struts基本运行过程】](#1struts基本运行过程) - - [2.【搭建Struts2开发环境】](#2搭建struts2开发环境) - - [2.1 访问Action的URL:](#21-访问action的url) - - [2.2 URL 默认搜索特性 :](#22-url-默认搜索特性-) - - [2.3 如果没有给Action指定class:](#23-如果没有给action指定class) - - [2.4 如果找不到Action:](#24-如果找不到action) - - [2.5 URL的后缀名可以自定义(会覆盖原本的配置)](#25-url的后缀名可以自定义(会覆盖原本的配置)) - - [3.【action标签的配置】](#3action标签的配置) - - [3.1 【关于result的配置】](#31-关于result的配置) - - [3.2 【模式匹配】](#32-模式匹配) - - [4.【action类型转换】](#4action类型转换) - - [【注意】](#注意) - - [4.1【与Servlet解耦】](#41与servlet解耦) - - [4.2 【通过实现接口struts2自动注入】](#42-通过实现接口struts2自动注入) - - [5.【文件上传】](#5文件上传) - - [5.1 struts2框架的文件上传:](#51-struts2框架的文件上传) - - [6. 【文件下载】](#6-文件下载) - - [7.【校验】](#7校验) - - [7.1 【struts2手动验证】:](#71-struts2手动验证) - - [7.2【struts2框架验证(xml方式)】:](#72struts2框架验证xml方式) - - [8.【自定义拦截器】](#8自定义拦截器) - - [8.1 【如何自定义拦截器】](#81-如何自定义拦截器) - - [1、 所有的拦截器都需要实现Interceptor接口或者继承Interceptor接口的扩展实现类](#1、-所有的拦截器都需要实现interceptor接口或者继承interceptor接口的扩展实现类) - - [2、要重写init()、intercept()、destroy()方法](#2、要重写init、intercept、destroy方法) - - [3、 在struts.xml配置文件中,进行注册](#3、-在strutsxml配置文件中进行注册) - - [9.【ognl学习】](#9ognl学习) - - [9.1【valueStack】:](#91valuestack) - - [9.2 【理解OGNL Context】 上下文](#92-理解ognl-context-上下文) - - [9.3 【OGNL表达式】【示例JSP】](#93-ognl表达式示例jsp) - - [1.如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加前缀。
](#1如果访问其他context中的对象由于他们不是根对象所以在访问时需要添加前缀
) - - [2.如果要访问根对象(即ValueStack)中对象的属性,则可以省略命名对象,直接访问该对象的属性即可。
](#2如果要访问根对象(即valuestack)中对象的属性则可以省略命名对象直接访问该对象的属性即可
) - - [用法3:构造Map](#用法3构造map) - - [【“$”有两个主要的用途】](#“$”有两个主要的用途) - - [9.4【OGNL标签】](#94ognl标签) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Struts2笔记 -* 使用ognl表达式在页面debug, -* 使用的jar包不仅要放在library里,如果运行时需要,就要加载lib目录下 - -```xml - - ... - - -``` -* action没有result,执行的方法没有返回值(void),方法里写上out.write(JSON); 调用action方就能收到JSON字符串 -* result标签类型默认是转发,要显示说明是重定向 - -## 常见问题 -- action是单例还是多例的? - - 多例的,可以在构造器中证明 -- struts2有哪三个类重要的类? - - ActionContext Action的上下文 - - ServletActionContext 建立struts2与Servlet的通信基础 - - ActionInvocation struts2的总的上下文 -- struts2的数据都在值栈中,怎么保证数据的安全性?值栈的生命周期是? - - 因为ValueStack在ActionContext中,而ActionContext又是在ThreadLocal中,所以是线程安全的, - - 值栈的生命周期是一次请求,当前的action,actionContext,ValueStack 生命周期是一致的 -- 自己写的action是由谁调用的? -- ActionProxy和拦截器的意义? - -## 1.【struts基本运行过程】 -1.浏览器的各种事件,发起一个URL的请求, -2.被项目的默认过滤器监听到了,调用对应的action,(需要配置好xml文件的package和action标签) -3.若action有绑定拦截器,就先执行拦截器里的方法 -4.由action里运行方法(这里是真正的代码处理的地方)的return值来确定等会跳转的结果页面(配置xml文件的result标签) - -## 2.【搭建Struts2开发环境】 -(或者直接使用MyEclipse的快速搭建,struts2.1+Hibernate3.3.2+JSTL1.2.2(本机jdk7.45+tomcat7.08)) -* 1、找到Struts2应用所需要使用到的JAR包(特别注意不能和Hibernate的JAR包有重复的,不然就报错) - -Struts2.3.3版本的开发必需JAR包: -``` - sm-3.3.jar - sm-commons-3.3.jar - sm-tree-3.3.jar - ommons-fileupload-1.2.2.jar - ommons-io-2.0.1.jar - ommons-lang3-3.1.jar - ommons-logging-1.1.1.jar - reemarker-2.3.19.jar - avassist-3.11.0.GA.jar - gnl-3.0.5.jar - truts2-core-2.3.3.jar - work-core-2.3.3.jar -``` - -* 2、 创建JSP文件 -* 3、 创建Action文件(实现了Struts的Action接口的普通类,或者继承ActionSupport类) -* 4、 编写Struts2的配置文件Struts.xml -* 5、 在web.xml中加入Struts2 框架启动的过滤器配置(核心过滤器) - -### 2.1 访问Action的URL: -package的namespace加上Action的名字加上后缀 - -### 2.2 URL 默认搜索特性 : -`/a/b/c/d/df.action` -`/a/df.action` -- 这两个是等价的,上面长的那个同样分解为两部分,前半部分的路径是从d到a - - 依次向上搜索的,直到找到,否则才会报错,所以说,只要根是对的,中间可以乱写,但是会影响性能 - -### 2.3 如果没有给Action指定class: -- `struts.xml`继承的`struts-default.xml` 中配置了一个默认的class,所以说不会报错 - - `` - -### 2.4 如果找不到Action: -- 就需要配置默认的Action名称,没有的话,就会报错。有就执行那个了 - - `` - -### 2.5 URL的后缀名可以自定义(会覆盖原本的配置) -- 在struts.xml 中配置: - - (使用多个的话,用逗号分隔)默认是action和空 - - name : 框架自带配置文件 default.properties中常量名 - - value :常量值 - `` -- 或者: - - src下新建一个`default.properties` 里面只写需要修改的常量 - - `struts.action.extension=myth ` - -> 若两者都修改了按以下顺序: -1. struts-default.xml -2. struts-plugin.xml -3. struts.xml -4. struts.properties -5. web.xml -> 若有相同的常量配置好,后者覆盖前者 建议在struts.xml中配置 - -```xml - <-- 配置URL后缀 默认是action或空--> - - <-- 配置国际化资源文件被修改时,是否重新加载 默认是false --> - - <-- 配置struts2框架的配置文件修改时,是否重新加载 默认是false--> - - <-- - 配置struts2的模式 - false 生产模式 默认是false - true 开发模式 需要更多的调试信息 会自动将上面两个常量设置为true - --> - -``` - -- 自定义使用的 struts.xml 不仅路径,还有名字,方便多人开发 -- 规范一般是一个action对应于一个xml文件,所以在struts.xml中引入xml文件要: -- `` file的路径都是以src为起点,注意把点换成 / - -## 3.【action标签的配置】 -### 3.1 【关于result的配置】 - -```xml - - - - - - - - - /resulttype/success.jsp - - - /resulttype/Jqgrid.jsp - - - - - - - - - - - - - -``` -### 3.2 【模式匹配】 -```xml - - - - - - - - /pattern/success.jsp - /pattern/BookAction.jsp - - - - - - - - - - - - /pattern/success.jsp - /pattern/{1}.jsp - - - - - -``` -**2.3以上版本使用通配** -```xml - - /WEB-INF/jsp/login.jsp - login - -``` -- **总结**:在struts2.3之前的版本,正常的配置就可以了, - - 但在struts2.3版本之后,使用通配符调用方法时,内部会验证是否允许访问该方法, - - 所以要加上`方法名1,方法名2…`代码。 - -## 4.【action类型转换】 - -* 1、从JSP页面上的**输入框提交给action**时,只要在action中声明同名变量,定义setget方法,那之后直接使用get方法就能获取到值。 - * 封装成对象再setget也是一样可以的但是在JSP上的input的name上要加对象名做前缀。 - * 还可以批量添加数据,就使用Collection集合 Collection ts;同样的加setget方法, - * 但是在JSP上的多个input的name就要写成这种格式**ts[0].name ts[1].name......** - -* 2、当struts有些类型无法转换时,就需要自定义转换器 - * **【基于字段】(局部)** - * 在当前action包下新建 convert.properties 文件名是自定义的 - * 在该文件中 `xwork.default.fieldvalue=无效的字段值 "{0}".` - * 在struts.xml文件加载该资源文件 - -```xml - - -``` -* **【基于类】(全局):** - * 在src目录下新建一个`xwork-conversion.properties`文件 - * 内容: 待转换的类型=类型转换器的全类名 - * 例如:`java.util.Date=cn.itcast.convert.DataConverter` -* 3、result标签名为input的是默认为错误页面的跳转方向 - 在错误页面调出错误信息:`` - * 【针对每个字段给出提示信息】 - 在converte.properties中添加一行 `invalid.fieldvalue.createTime=****` - - -####【注意】 -JSP页面中引入struts标签 `<%@ taglib uri="/struts-tags" prefix="s" %>` -* 创建完文件后记得一定要添加到struts.xml文件中去,若没加,也可以在JSP页面中直接使用 - -### 4.1【与Servlet解耦】 -struts2 对 HttpServletRequest HttpSession ServletContext进行了封装成了Map对象 -* 【方法一】: 通过ServletActionContext类直接获取 - 这个类是action执行的上下文对象,包括了parameter request session application等。 - -```java - //分别三个属性的设置request session application - HttpServletRequest request = ServletActionContext.getRequest(); - request.setAttribute("username", "username_request"); - Map sessionMap = ServletActionContext.getContext().getSession(); - sessionMap.put("username", "username_session"); - ServletContext sc = ServletActionContext.getServletContext(); - sc.setAttribute("username", "username_application"); -``` -JSP页面的获取: -```jsp - ${requestScope.username}
- ${sessionScope.username}
- ${applicationScope.username}
-``` - -### 4.2 【通过实现接口struts2自动注入】 -1. 实现这四个接口:ServletRequestAware,ServletResponseAware,ServletContextAware,SessionAware -2. 实例化对象分别是HttpServletRequest,HttpServletResponse,Map,ServletContext -3. 重写四个set方法,方法体写上this.** = **; - -## 5.【文件上传】 -* 【1】套路一致,但是在配置时,action里一定有input的result才可以 -* 【2】<-- 配置文件上传的总大小 --> - < constant name="struts.multipart.maxSize" value="2097152000"> -* 【3】错误提示配置 -新建一个properties文件,名字自定义 加入到struts配置文件中去 -```conf - struts.messages.error.uploading=Error uploading: {0} - struts.messages.error.file.too.large=File too large: {0} "{1}" "{2}" {3} - struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3} - struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3} -``` -- {0}:< input>标签的name 属性值 -- {1}:上传文件的真实名称 -- {2}:上传文件保存到临时目录的名称 -- {3}:上传文件的类型(对于too.large来说是上传文件的大小) - -### 5.1 struts2框架的文件上传: -* 单文件上传: - * 在动作类action中声明相关属性: - * 在动作类action中,要声明与页面中表单name属性同名的属性,同名的属性的类型时File类型; - * 在动作类action中,要声明[同名的属性]ContentType,类型时String类型; - * 在动作类action中,要声明[同名的属性]FileName,类型时String类型 - * 给所有属性提供get和set方法 - * 在业务方法中,处理文件上传: - * 获取要上传文件的路径,保存的位置 - * 在目标文件夹内,创建一个与上传文件同名的文件 - * 通过FileUtils工具类提供copyFile()方法,将临时文件内容拷贝到目标文件夹下的那个同名的文件 - * 设置上传文件的总大小 - * 在struts.xml文件中, - * 设置上传文件的大小、类型和扩展名: - * 在自定义的配置文件中,在action标签下: -```xml - - - - - 20971520 - - text/plain,application/msword - - .txt - -``` -* 自定义上传文件的错误提示信息: - * 在动作类action同目录下,创建一个名为fileuploadmessage.properties资源文件(名为自定义) - * 改资源文件配置如下: -```conf - struts.messages.error.uploading=Error uploading: {0} - struts.messages.error.file.too.large=File too large: {0} "{1}" "{2}" {3} - struts.messages.error.content.type.not.allowed=Content-Type not allowed: {0} "{1}" "{2}" {3} - struts.messages.error.file.extension.not.allowed=File extension not allowed: {0} "{1}" "{2}" {3} -``` - -* 多文件上传: - * 所有流程于配置都与单文件上传一致。 - * 需要注意的是: - * 在页面中,虽然是多文件上传,但是页面中表单的name属性的值必须保持一致; - * 在动作类action中声明的相关属性,类型改成数组; - * 在业务方法中,相关处理流程改成单文件上传的循环。 - -## 6. 【文件下载】 - 1、下载文件时 压入值栈的名字如果含中文需要转码:fileName = new String(filename.getBytes(),"ISO-8859-1"); -配置文件 filename=${filename}.xls - -## 7.【校验】 -### 7.1 【struts2手动验证】: -也就是说手动的是直接在action里,重写个validate方法就是了 -方法里只要按需求写this.addFieldError( key, value);语句就行了,后续的由框架来处理 -* 首先要从页面中获取对应的标签name属性的值,在动作类action中声明 同名的属性,提供get和set方法 - -* 要继承ActionSupport类或者实现Validateable接口 - -* 重写Validateable接口的validate()方法 - * 前提是:要保证setUsername()、validate()、login()方法要按照这个先后顺序执行 - -* 如果登录失败,如何处理: - * this.addFieldError( key, value); - * key:错误提示字段 - * value:错误提示信息 - -* 什么时候才是验证通过? - * 验证通过:1、map集合不存在;2、map集合存在并为空 - * 验证不通过:map集合存在并且不为空 - -* 分析需求: - * 用户名不能为null ,"" - * 密码不能为null, "" ,并且密码的长度6-12之间 - -* 针对所有业务方法进行验证还是针对某个指定业务方法进行验证? - * 重写的validate()方法,针对所有业务方法进行验证 - * 重写的validate()方法加上要验证的指定的业务方法名(业务方法名的首字母大写),实现针对某个指定的业务方法进行验证 - * 为什么要这样进行拼接?因为struts2框架底层拼接,如果不这样写,底层就找不到对应方法名 - - -### 7.2【struts2框架验证(xml方式)】: -* 首先要从页面中获取对应的标签name属性的值,在动作类action中声明同名的属性,提供get和set方法 - -* 创建一个xml格式验证文件: - * 命名方式:ActionClassName-validation.xml,ActionClassName指的是动作类action的名称 - * 标签:根元素 - * field:指定action中要校验的属性,实际上就是页面中表单的name属性的值 - * name:指定页面中表单的name属性的值 - * field-validator:指定验证规则 - * type:指定验证规则名称,struts2框架提供的验证规则放在xwork-core-xxx.jar - 下的com\opensymphony\xwork2\validator\validators - 的default.xml配置文件。 - * param:向底层的验证规则传递的参数 - * message:验证失败时,提供的错误提示信息 - -* 如果要对指定方法进行验证的话: - * xml验证文件的命名方式:ActionClassName-ActionName-validation.xml, - ActionName对应的是struts.xml文件对应的action标签的name属性的值 - -## 8.【自定义拦截器】 -* 【拦截器 特性】: - - 拦截器一般是和对应的action绑定的,而原生的filter是对URL模式进行拦截的 - - * 执行顺序:执行完struts中配置的拦截器栈中所有intercept方法后再执行action的execute方法 - * 当拦截器的 intercept方法 返回null就会继续执行action - 如果在前面就调出来了action实例,并且执行了方法那么之后的action就不会再重复执行 - * 检查是否还有拦截器待执行,有就去执行,没有就会得到null,同样的继续执行action - String result = invocation.invoke(); - return result; - -### 8.1 【如何自定义拦截器】 -#### 1、 所有的拦截器都需要实现Interceptor接口或者继承Interceptor接口的扩展实现类 -#### 2、要重写init()、intercept()、destroy()方法 - -* init()是在struts2框架运行时执行,在拦截器的生命周期中只执行一次,可以做必要的内容的初始化工作 -* intercept(),是每一次请求就执行一次,做相关处理工作。 - * intercept()方法接收一个ActionInvocation接口的实例 - * 通过这个接口的实例,可以获取以下内容: - -```java - //cn.itcast.aop.UserAction @15b5783, 动作类的对象 -System.out.println("invocation.getAction() : "+invocation.getAction()); - //cn.itcast.aop.UserAction @15b5783, 与invocation.getAction()方法获取的是同一的对象 -System.out.println("invocation.getProxy().getAction() : "+invocation.getProxy().getAction()); - //userAction_save,自定义配置文件中的action标签的name属性的值 -System.out.println("invocation.getProxy().getActionName() : "+invocation.getProxy().getActionName()); - //save,对应动作类指定要执行的方法名 -System.out.println("invocation.getProxy().getMethod() : "+invocation.getProxy().getMethod()); - // /aop,自定义配置文件中的package标签的namespace属性的值 -System.out.println("invocation.getProxy().getNamespace() : "+invocation.getProxy().getNamespace()); -``` -* destroy()是在拦截器销毁前执行,在拦截器的声明周期中只执行一次。 - -#####3、 在struts.xml配置文件中,进行注册 -* 在配置文件中的package标签下,进行相关配置: -```xml - - - - - - - - - - - - -``` -后面跟着的就是action的配置了 - - -## 9.【ognl学习】 - -### 9.1【valueStack】: -ValueStack实际上是一个接口,在struts2中利用OGNL时,实际上是哦那个的是实现了该接口的OgnlValueStack类,这个类是利用OGNL的基础 -贯穿整个action生命周期,每个action类的对象都有一个valueStack对象,相当于一个数据的中转站,在其中保存了当前action对象和其他相关对象 -struts框架把valueStack对象保存在名为 “struts.valueStack”的请求属性中(request中) -```java - ValueStack vs = (ValueStack)request.getAttribute("struts.valueStack"); - vs.set("key","value");//实际上是放在了Map集合里再放在栈里的 - vs.getRoot().add(0,new Person());//把person对象压入List集合的0位置(栈顶) -``` -### 9.2 【理解OGNL Context】 上下文 -* OgnlValueStack类包含两个重要的属性:root 和 context - * 其中toot本质上是一个List集合 - * Context是一个Map(确切的说是一个OgnlContext对象) -* 在这个OgnlContext对象中,有一个默认的顶层对象 root OGNL访问context中这个默认顶层对象中的元素时不需要#号,直接通过名称来引用 -而访问其他对象时, request,session,attr 等则需要#号引用。 -* 总结:ognl Context包含ObjectStack属性和ContextMap属性 -* 在底层类中的部分代码: -```java -public class OgnlValueStack implements ValueStack { - CompoundRoot root; --- list集合 - transient Map context; --- map集合 -} -``` - -### 9.3 【OGNL表达式】【示例JSP】 -实际操作的不是值栈,而是值栈的属性:Context的上下文(就是一个Map集合) -* 使用EL表达式取值: -* 使用Ognl表达式取值: 【访问Map集合加#】 -```jsp -${requestScope.username}
-${sessionScope.username}
-${applicationScope.username}



-``` -#### 1.如果访问其他Context中的对象,由于他们不是根对象,所以在访问时,需要添加#前缀。
-```xml -
-
-

-
-
-

-``` -> 访问对象栈中对象可不加# - -#### 2.如果要访问根对象(即ValueStack)中对象的属性,则可以省略#命名对象,直接访问该对象的属性即可。
-```xml -

- -
-
-
-

-``` -```java -// 深入理解值栈中的 ObjectStack -// 【后台代码:】 -vs.getRoot().add(0,new Person()); -``` -【若有多个name属性名】只取出栈中第一个 - -#### 用法3:构造Map -```xml -



-
- -``` -%的用法:“%”符号的用途是在标签的属性值被理解为字符串类型时,告诉执行环境%{}里的是OGNL表达式。 -%{}是万能用法,无论里面的表达式是不是ognl表达式,都会强制理解为ognl表达式 -#### 【“$”有两个主要的用途】 - -1 * 用于在国际化资源文件中,引用OGNL表达式
-`

` -在properties文件中配置:`ognl=${error} ognl` -取的是值栈中的error属性 代码:`valueStack1.set("error", "error_valueStack");` - -2 * 在Struts 2配置文件中,引用OGNL表达式
-`

` -`ognl/ognl.jsp?msg=${msg}` -这里的msg是request的param 使用 ${} 访问的都是值栈里的 - -`` 能查看值栈状态 -### 9.4【OGNL标签】 diff --git a/Java/Ecosystem/Vertx.md b/Java/Ecosystem/Vertx.md index c7e4ae7..cee2c43 100644 --- a/Java/Ecosystem/Vertx.md +++ b/Java/Ecosystem/Vertx.md @@ -1,12 +1,19 @@ -`目录 start` - -- [Vert.x](#vertx) +--- +title: Vertx +date: 2018-11-21 10:56:52 +tags: +categories: +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [Vert.x](#vertx) + +**目录 end**|_2020-06-24 02:06_| **************************************** # Vert.x -> [参考博客: 用惯了高大上SpringBoot不妨试试小清新Vert.x](https://segmentfault.com/a/1190000011763020) -[参考博客: 基于Vert.x和SpringBoot实现响应式开发](http://www.jdon.com/47806) -[参考博客: Vert.x开发指南](http://blog.csdn.net/chszs/article/details/8949559) +> [参考: 用惯了高大上SpringBoot不妨试试小清新Vert.x](https://segmentfault.com/a/1190000011763020) +[参考: 基于Vert.x和SpringBoot实现响应式开发](http://www.jdon.com/47806) +[参考: Vert.x开发指南](http://blog.csdn.net/chszs/article/details/8949559) [使用Vertx构建微服务](http://www.cnblogs.com/luxiaoxun/p/7693640.html) diff --git a/Java/Ecosystem/WebFramework.md b/Java/Ecosystem/WebFramework.md new file mode 100644 index 0000000..364b455 --- /dev/null +++ b/Java/Ecosystem/WebFramework.md @@ -0,0 +1,23 @@ +--- +title: WebFramework +date: 2024-09-20 11:23:07 +tags: +categories: +--- + +💠 + +- 1. [Web框架](#web框架) + - 1.1. [Blade EOL](#blade-eol) + +💠 2024-09-20 11:52:03 +**************************************** +# Web框架 +> [minum](https://github.com/byronka/minum) +> [bootique](https://github.com/bootique/bootique)`类似框架` +> [solon](https://github.com/opensolon/solon) + +## Blade EOL +> [Blade项目首页](https://lets-blade.github.io/) | [Github 地址](https://github.com/lets-blade/blade) | [官方 demos](https://github.com/lets-blade/blade-demos) + +> [参考: Blade:一款简洁优雅、微内核设计的Java Web框架](http://hao.jobbole.com/bladejava/) diff --git a/Java/Ecosystem/http/OKHTTP.md b/Java/Ecosystem/http/OKHTTP.md new file mode 100644 index 0000000..deaa035 --- /dev/null +++ b/Java/Ecosystem/http/OKHTTP.md @@ -0,0 +1,33 @@ +--- +title: OKHTTP +date: 2024-02-02 14:22:14 +tags: +categories: +--- + +💠 + +- 1. [OKHTTP](#okhttp) + +💠 2024-06-17 19:57:32 +**************************************** +# OKHTTP +> [Official Site](https://square.github.io/okhttp/) + +https://square.github.io/okhttp/ +https://square.github.io/okhttp/calls/ +https://blog.csdn.net/sinat_36553913/article/details/104054028 +https://blog.csdn.net/sinat_36553913/article/details/104054160 +https://www.jianshu.com/p/da5c303d1df4?tdsourcetag=s_pcqq_aiomsg +https://blog.csdn.net/hdu2013/article/details/109229625 +https://zhuanlan.zhihu.com/p/116777864 + +https://www.jianshu.com/p/f038d2438fcf +https://www.jianshu.com/p/132733115f95 + +https://www.jianshu.com/p/d7eced552553 + +> 核心源码流程 +- okhttp3.Dispatcher 异步请求调度 +- okhttp3.internal.connection.RealConnectionPool TCP连接池 + - okhttp3.ConnectionPool diff --git a/Java/JavaReadme.md b/Java/JavaReadme.md new file mode 100644 index 0000000..620cdde --- /dev/null +++ b/Java/JavaReadme.md @@ -0,0 +1,139 @@ +--- +title: Java Readme +date: 2020-03-27 15:28:15 +tags: + - Readme +categories: + - Java +--- + +💠 + +- 1. [计算机基础](#计算机基础) +- 2. [Java](#java) + - 2.1. [JDK](#jdk) + - 2.2. [JavaFX](#javafx) +- 3. [相关资源](#相关资源) + - 3.1. [Java Programer Suggestion](#java-programer-suggestion) +- 4. [环境配置](#环境配置) + +💠 2024-02-03 10:54:46 +**************************************** +# 计算机基础 + +>- [算法](/Algorithm/Algorithm.md) +>- [计算机基础](/Skills/CS/Computer.md) +>- [并发基础](/Skills/Councurrency/) +>- [计算机网络](/Skills/Network/) +>- [测试理论](/Skills/Test/TestTheory.md) + +************************************* +# Java +[Oracle JavaSE Overview](http://www.oracle.com/technetwork/java/javase/overview/index.html) | [openjdk ](http://openjdk.java.net/) | [AdoptOpenJDK mirrors](https://mirrors.tuna.tsinghua.edu.cn/AdoptOpenJDK/) + +> [Oracle: java tutorial](https://docs.oracle.com/javase/tutorial/java/) `入门时首先通读一遍` +> [java turtorials](https://www.geeksforgeeks.org/java-tutorials/) `能在线运行简易Java代码` + +> [wiki: java language](https://en.wikipedia.org/wiki/Java_%28programming_language%29) + +> [如何快速打好Java基础?](https://www.zhihu.com/question/50904128) +> [tutorials](https://github.com/eugenp/tutorials) +> [Java 故障处理](/Java/AdvancedLearning/Tuning/Readme.md) +> [JSRs: Java Specification Requests](https://jcp.org/en/jsr/all)`社区确认的标准` + +******************** + +| 基础 | 进阶 | +|:----|:----| +| [基础语法](/Java/AdvancedLearning/JavaBasicSyntax.md) | [反射](/Java/AdvancedLearning/JavaReflection.md) | +| [继承和接口](/Java/AdvancedLearning/JavaInheritedAndInterface.md) | [JVM](/Java/AdvancedLearning/JVM.md) | +| [异常](/Java/AdvancedLearning/JavaException.md) | [字节码](/Java/AdvancedLearning/JavaClass.md) | +| [泛型](/Java/AdvancedLearning/JavaGenerics.md) | [测试](/Java/Test/JavaTest.md) | +| [集合](/Java/AdvancedLearning/JavaCollection.md) | [打包部署](/Java/AdvancedLearning/JavaDeploy.md) | +| [线程](/Java/AdvancedLearning/JavaThread.md) | [持续集成 CI](/Skills/DevOps/ContinuousIntegration.md) | +| [并发](/Java/AdvancedLearning/JavaConcurrency.md) | [网络编程](/Java/AdvancedLearning/JavaNetwork.md) | +| [IO](/Java/AdvancedLearning/JavaIO.md) | | +| [注解](/Java/AdvancedLearning/JavaAnnotation.md) | | +| [JDBC](/Java/AdvancedLearning/JDBC.md) | | + +## JDK +>- [JDK and JRE](/Java/AdvancedLearning/JDKAndJRE.md) +>- [Java 发行版大致特性](/Java/AdvancedLearning/JavaReleaseVersion.md) +>- [Java7](/Java/AdvancedLearning/Java7.md) +>- [Java8](/Java/AdvancedLearning/Java8.md) +>- [Java11](/Java/AdvancedLearning/Java11.md) + +## JavaFX +> [OpenJFX](https://wiki.openjdk.java.net/display/OpenJFX) | [official site](https://openjfx.io) + +# 相关资源 +> [Java核心知识思维导图](https://gitee.com/gin9/MindMap) +> [阿里巴巴Java开发手册](https://github.com/alibaba/p3c) | [阿里巴巴Java开发手册](/Java/AlibabaJavaStandard.md) + +> [effective-java-3rd ](https://github.com/sjsdfg/effective-java-3rd-chinese) + +>- [Github Topic: Java](https://github.com/topics/java) + +>- [Java成神之路](https://github.com/hollischuang/toBeTopJavaer) +>- [JCSprout](https://github.com/crossoverJie/JCSprout) +>- [awesome-java](https://github.com/akullpp/awesome-java) +>- [awesome-java-cn](https://github.com/jobbole/awesome-java-cn) +>- [architect-awesome](https://github.com/xingshaocheng/architect-awesome) +>- [advanced-java](https://github.com/doocs/advanced-java) +>- [java-learning](https://github.com/brianway/java-learning) +>- [tutorials](https://github.com/eugenp/tutorials)`Java生态Demo集` +>- [Java guide](https://github.com/Snailclimb/JavaGuide) +>- [LearningNotes](https://github.com/francistao/LearningNotes)`安卓` +>- [Java思维导图](https://gitee.com/java-mindmap/mapSource) + +>- [google: style guide](https://google.github.io/styleguide/javaguide.html) +>- [Java8 api 中文版 Google翻译](https://blog.fondme.cn/posts/21004/) +>- [唯品会的规范文档](https://github.com/vipshop/vjtools)`规范这种东西, 各有各家的说法, 适合自己就好` +>- [daydayup](https://github.com/ITDragonBlog/daydayup)`Java架构师成长之路` + +>- [99 Problems](https://github.com/shekhargulati/99-problems) + +>- [模式之禅](/Java/DesignPattern.md) +>- [Google: GWT](https://www.gwtproject.org/doc/latest/tutorial/index.html)`Java写前端,邪教` + +## 前辈建议 +Java 中有三大支柱(多线程、网络和安全),在 java.util.concurrent、java.security、javax.cropty、javax.security 四个包中就占了两个(多线程、安全) +还有一个网络在 java.net、javax.net 中 掌握了上面 6 个包及其子包中内容的话,那 Java 水平可以说达到了另一种境界。 + +**多线程(multi-threading and concurrent)** + +1. 关键词:volatile, sychronized +2. 传统的线程 API:java.lang.Thread, java.lang.Runnable, java.lang.ThreadGroup, Object#wait, Object#notify, Object#notifyAll +3. JDK 5 并发包(java.util.concurrent)API:线程池、任务执行器、计数信号量、倒计数门闩、并发集合(并发 Map、阻塞队列等)、基于 CPU CAS 指令的原子 API(java.util.concurrent.atomic)、锁 API(java.util.concurrent.lock)和条件对象等。 +4. 作为个人知识提升,还需要理解诸如自旋锁、分离锁、分拆锁、读写锁等的同步锁策略,以及可重入锁、锁的公平性的意义。以及各种并发锁的算法,比如:Peterson锁、Bakery锁 等等,以及现代 CPU 体系结构 + +涉及多线程及并发的 API 在 java.lang 中及 java.util.concurrent.* 中。 + +**网络(network communication)** + +1. 阻塞 TCP 通信、阻塞 UDP 通信、组播 +2. 非阻塞 TCP 通信、非阻塞 UDP 通信 +3. 客户端通信 API(java.net.URL, java.net.URLConnection 等类库) + +涉及网络通信的 API 都在 java.net 和 java.nio.channels 包中。这里的网络已经将 RMI 相关包 java.rmi, javax.rmi 都排除了。 + +**安全(security, cryptography and AAA)** + +1. Java 加密类库 JCA +2. Java 加密类库扩展 JCE +3. 涉及密码学知识点的消息摘要、消息认证码、对称加密、非对称加密、数字签名 +4. 涉及网络通信证书管理工具(keytool)及 API(PKI、X.509证书) +5. 基于 SSL/TLS 的安全网络通信 API(JSSE),包括:密钥库管理、信任库管理、阻塞 SSL 通信和非阻塞 SSL 通信等等 +6. Java 认证及授权服务(JAAS)API + +涉及安全的东西都在: + +- java.security(JCA、JCE、数字证书,以及 JCE 的 SPI) +- javax.net(SSL/TLS) +- javax.security(JAAS) +- javax.crypto(密码学) +- keytool 的 JDK 工具 + + +# 环境配置 +> [Linux搭建Java开发环境](/Linux/JavaDevInit.md) diff --git a/Java/JavaSE.md b/Java/JavaSE.md deleted file mode 100644 index c283280..0000000 --- a/Java/JavaSE.md +++ /dev/null @@ -1,66 +0,0 @@ -`目录 start` - -- [计算机基础](#计算机基础) -- [Java](#java) - - [【基础】](#基础) - - [【深入】](#深入) - - [【抽象】](#抽象) - - [相关资源](#相关资源) - -`目录 end` |_2018-09-13_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 计算机基础 - ->- [算法](/Skills/CS/Arithmetic.md) ->- [计算机相关](/Skills/CS/Computer.md) ->- [并发理论](/Skills/CS/Concurrent.md) ->- [网络](/Skills/CS/Network.md) ->- [测试理论](/Skills/CS/TestTheory.md) - -************************************* -# Java -> [oracle: java tutorial](https://docs.oracle.com/javase/tutorial/java/) - -> [Oracle JavaSE](http://www.oracle.com/technetwork/java/javase/overview/index.html) -- [openjdk](http://openjdk.java.net/)`开源的JDK, ` -- [oracle-jdk download](http://www.oracle.com/technetwork/java/javase/downloads/index.html) | [oracle-jdk中文版下载页面](http://www.oracle.com/technetwork/cn/java/javase/downloads/index.html) - -- [Java8 api 中文版 Google翻译](https://blog.fondme.cn/posts/21004/) - ->- [JDK and JRE](/Java/AdvancedLearning/JDKAndJRE.md) ->- [Java 发行版大致特性](/Java/AdvancedLearning/JavaReleaseVersion.md) ->- [Java7](/Java/AdvancedLearning/Java7.md) ->- [Java8](/Java/AdvancedLearning/Java8.md) - -## 【基础】 ->- [基础语法](/Java/AdvancedLearning/GrammarAndType.md) ->- [继承和接口](/Java/AdvancedLearning/ExtendsAndInterface.md) ->- [异常](/Java/AdvancedLearning/Exception.md) ->- [泛型](/Java/AdvancedLearning/Generics.md) ->- [集合](/Java/AdvancedLearning/Collection.md) ->- [线程](/Java/AdvancedLearning/Thread.md) ->- [并发](/Java/AdvancedLearning/Concurrents.md) ->- [IO](/Java/AdvancedLearning/IO.md) ->- [注解](/Java/AdvancedLearning/Annotation.md) - -## 【深入】 ->- [JDBC](/Java/AdvancedLearning/JDBC.md) ->- [网络编程](/Java/AdvancedLearning/Socket.md) ->- [类文件和反射](/Java/AdvancedLearning/ClassFile.md) ->- [测试](/Java/AdvancedLearning/JavaTest.md) ->- [打包部署](/Java/AdvancedLearning/Deploy.md) ->- [持续集成 CI](/Java/AdvancedLearning/ContinuousIntegration.md) ->- [JMS](/Java/AdvancedLearning/JMS.md) ->- [JVM上的多语言](/Java/AdvancedLearning/MultipleLanguage.md) ->- [Java性能调优](/Java/AdvancedLearning/JavaPerformance.md) ->- [Web性能调优](/Java/AdvancedLearning/WebPerformance.md) - -## 【抽象】 ->- [编程思想](/Java/AdvancedLearning/ProgramThinking.md) ->- [模式之禅](/Java/ZenOfPattern.md) - - -## 相关资源 -> [architect-awesome](https://github.com/xingshaocheng/architect-awesome) -> [Java资源大全](http://www.codeceo.com/article/java-resource-collection.html) -> [google: style guide](https://google.github.io/styleguide/javaguide.html) diff --git a/Java/LittleKnowledgePoint.md b/Java/LittleKnowledgePoint.md deleted file mode 100644 index cc91fa6..0000000 --- a/Java/LittleKnowledgePoint.md +++ /dev/null @@ -1,12 +0,0 @@ -`目录 start` - -- [字符串处理](#字符串处理) -- [时间处理](#时间处理) - -`目录 end` |_2018-08-19_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 字符串处理 - - -# 时间处理 -1. SimpleDateFormat : YYYY-MM-dd HH:MM:SS diff --git a/Java/Log.md b/Java/Log.md index 459bec7..effcff3 100644 --- a/Java/Log.md +++ b/Java/Log.md @@ -1,37 +1,53 @@ -`目录 start` - -- [日志系统](#日志系统) - - [slf4j 体系](#slf4j-体系) - - [Log4j](#log4j) - - [问题](#问题) - - [Log4j2](#log4j2) - - [LogBack](#logback) - - [Gradle中使用](#gradle中使用) - - [配置理解](#配置理解) - - [根节点 属性](#根节点--属性) - - [子节点](#子节点) - - [设置上下文名称:](#设置上下文名称) - - [设置变量: ](#设置变量-) - - [获取时间戳字符串:](#获取时间戳字符串) - - [设置loger:](#设置loger) - - [详解](#详解) - - [实践经验](#实践经验) - - [apache 体系](#apache-体系) -- [分析日志](#分析日志) - - [Linux上查看日志](#linux上查看日志) - - [lnav](#lnav) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Java中的日志 +date: 2023-10-04 11:46:08 +tags: + - Log +categories: + - Java +--- + +💠 + +1. [日志系统](#日志系统) + 1. [概念](#概念) + 1. [slf4j 接口](#slf4j-接口) + 1. [MDC](#mdc) +1. [Log4j](#log4j) + 1. [问题](#问题) +1. [Log4j2](#log4j2) +1. [Logback](#logback) + 1. [配置理解](#配置理解) + 1. [根节点 属性](#根节点--属性) + 1. [子节点](#子节点) + 1. [设置上下文名称:](#设置上下文名称) + 1. [设置变量: ](#设置变量-) + 1. [获取时间戳字符串:](#获取时间戳字符串) + 1. [设置loger](#设置loger) + 1. [详解](#详解) + 1. [Logback MDC](#logback-mdc) +1. [实践经验](#实践经验) +1. [分析日志](#分析日志) + 1. [Linux上查看日志](#linux上查看日志) + 1. [lnav](#lnav) +1. [日志采集](#日志采集) + 1. [Filebeat](#filebeat) + 1. [K8s](#k8s) + +💠 2023-10-04 11:47 **************************************** # 日志系统 > [码农翻身: 一个著名的日志系统是怎么设计出来的? ](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513967&idx=1&sn=5586ce841a7e8b39adc2569f0eb5bb45&chksm=80d67bacb7a1f2ba38aa37620d273dfd7d7227667df556d36c84d125cafd73fef16464288cf9&scene=21#wechat_redirect)`深刻的理解了日志系统的来源以及相关关系` -## slf4j 体系 -> SLF4J是一套简单的日志`外观模式`的Java API,帮助在项目部署时对接各种日志实现。 只是接口设计, 以下是具体实现库 +## 概念 +### slf4j 接口 +> SLF4J是一套简单的日志`外观模式`的Java API,帮助在项目部署时对接各种日志实现。 + +只是接口设计, 具体实现库: Log4j Log4j2 Logback > [lombok+slf4j+logback SLF4J和Logback日志框架详解](http://www.cnblogs.com/diegodu/p/6098084.html) -> 目前来说, LogBack要好于Log4j [参考博客: 从Log4j迁移到LogBack的理由](https://blog.csdn.net/gaojian881/article/details/53957961) +> 目前来说, LogBack要好于Log4j [参考: 从Log4j迁移到LogBack的理由](https://blog.csdn.net/gaojian881/article/details/53957961) - 个人体验: logback 1.1.3 log4j 1.7.25 - 在Java中 @@ -39,39 +55,40 @@ - 在Groovy中 - Log4j不能在Groovy中获取到正确的 类,方法,方法所在行 直接输出? - LogBack可以拿到正确的值, 但是在闭包中, 方法是混乱的 - + +### MDC +> 使用 ThreadLocal 存储一些信息, 然后能在xml的pattern中直接引用, 省去了重复手动写 log + +> [Improved Java Logging with Mapped Diagnostic Context (MDC)](https://www.baeldung.com/mdc-in-log4j-2-logback) + **************************** -### Log4j +# Log4j > [Log4J使用笔记](http://www.cnblogs.com/eflylab/archive/2007/01/11/618001.html) > [log4j.properties配置详解](http://www.cnblogs.com/ITEagle/archive/2010/04/23/1718365.html) -#### 问题 +## 问题 > `log4j:WARN No appenders could be found for logger` 这是路径下没有对应的配置文件, 那么这时就有了神奇的事情, maven项目按道理是resources下就行了, > 但如果你项目配置文件自己新建目录然后再复制过去什么的, 这么瞎搞的话,虽然在ide是能运行的, 但是一大包就没用了, 那么直接把log的配置单独放在 src/main/java 下就行了 -### Log4j2 +************************ + +# Log4j2 > [官方文档, 配置详解](https://logging.apache.org/log4j/2.x/manual/configuration.html) > 听说是为了解决Log4j无法在多环境使用的问题 , 也就是类似于 SpringBoot 多profile的功能 ************************** -### LogBack +# Logback - [logback简单示例](https://github.com/Kuangcp/Notes/blob/master/ConfigFiles/Log/logback.xml) > [xml to groovy config](https://logback.qos.ch/translator/asGroovy.html) -#### Gradle中使用 -1. 添加依赖 ` testCompile 'ch.qos.logback:logback-classic:1.2.3'` - - `compile 'org.projectlombok:lombok:1.16.16'` -2. 类上加注解 `@Slf4j` 然后 就能用了 - - -#### 配置理解 +## 配置理解 > [参考博客](http://www.cnblogs.com/lixuwu/p/5811273.html) -##### 根节点 属性 -- _scan_ : 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。 +### 根节点 属性 +- _scan_ : 当此属性设置为true时,配置文件如果发生改变,将会被`重新加载`,默认值为true。 - _scanPeriod_ : 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 - _debug_ : 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 @@ -80,37 +97,37 @@ ``` -##### 子节点 -###### 设置上下文名称: +### 子节点 +### 设置上下文名称: 每个logger都关联到logger上下文,默认上下文名称为“default”。但可以使用``设置成其他名字,用于区分不同应用程序的记录。一旦设置,不能修改。 ```xml - + myAppName ``` -###### 设置变量: +### 设置变量: 用来定义变量值的标签,`` 有两个属性,name和value;其中name的值是变量的名称,value的值时变量定义的值。通过``定义的值会被插入到logger上下文中。 定义变量后,可以通过`${}`来使用变量。例如使用``定义上下文名称,然后在``设置logger上下文时使用。 ```xml - + ${APP_Name} ``` -###### 获取时间戳字符串: +### 获取时间戳字符串: 两个属性 key:标识此`` 的名字;datePattern:设置将当前时间(解析配置文件的时间)转换为字符串的模式,遵循`java.txt.SimpleDateFormat`的格式。 例如将解析配置文件的时间作为上下文名称: ```xml - + ${bySecond} ``` ************ -##### 设置loger: +### 设置loger - `` - 用来设置某一个包或者具体的某一个类的日志打印级别、以及指定``。``仅有一个name属性,一个可选的level和一个可选的addtivity属性。 - `name:` @@ -122,14 +139,27 @@ - 是否向上级loger传递打印信息。默认是true。 - ``可以包含零个或多个``元素,标识这个appender将会添加到这个loger。 -******** +- 配置包日志级别 `` + +```xml + + + + + + +``` +************************ + - `` - 也是``元素,但是它是根loger。只有一个level属性,应为已经被命名为"root". - `level:` - 用来设置打印级别,大小写无关:TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF,不能设置为INHERITED或者同义词NULL。 - 默认是DEBUG。 - ``可以包含零个或多个``元素,标识这个appender将会添加到这个loger。 -********** + +************************ + `测试类:` ```java public class LogbackDemo { @@ -222,7 +252,7 @@ additivity属性为false,表示此loger的打印信息不再向上级传递, 如果将`` 修改为 ``那打印结果将是什么呢? 没错,日志打印了两次,想必大家都知道原因了,因为打印信息向上级传递,logger本身打印一次,root接到后又打印一次 -##### 详解 +### 详解 > 的子节点,是负责写日志的组件。 > 有两个必要属性name和class。name指定appender名称,class指定appender的全限定名。 @@ -346,16 +376,34 @@ _4.另外还有SocketAppender、SMTPAppender、DBAppender、SyslogAppender、Sif ![模式图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/pattern_type.jpg) -## 实践经验 +## Logback MDC +> [MDC](https://logback.qos.ch/manual/mdc.html) + +`简单使用` +1. 在合适的地方 MDC.put("appName", "myth"); +1. 在合适的地方清除 MGC.clear(); +1. logback.xml中的 pattern 里通过 %X{appName} 引用到 + +> 对于一个请求来讲, 请求的入口处 设置 MDC, 请求结束后清除 MDC +> 由于这个是利用 TreadLocal 实现的, 所以需要做清理, 而且没有并发问题 +********************* + +# 实践经验 > [Java 调整格式日志输出](https://www.jb51.net/article/88937.htm) 1. 日志记录方式, 注意格式的正确, 否则, 错误会被隐藏 - - [Github: CorrectLog.java](https://github.com/kuangcp/JavaBase/blob/master/java-classfile/src/main/java/log/CorrectLog.java) + - [Github: CorrectLog.java](https://github.com/Kuangcp/JavaBase/blob/class/src/test/java/log/CorrectLogTest.java) -1. 在Springboot中还能指定包的日志等级 `logging.level.com.github.kuangcp.service = DEBUG` -************ -## apache 体系 -- [apache的简单示例](https://github.com/Kuangcp/Notes/blob/master/ConfigFiles/Log/log4j.xml) +1. 在Springboot中指定包的日志等级 `logging.level.com.xxx = DEBUG` + +1. [日志包间互相依赖导致 StackOverflowError](https://www.slf4j.org/codes.html#log4jDelegationLoop) + - log4j-over-slf4j.jar AND slf4j-log4j12.jar + - jcl-over-slf4j.jar AND slf4j-jcl.jar + - [log4j-over-slf4j与slf4j-log4j12共存stack overflow异常分析](https://blog.csdn.net/kxcfzyk/article/details/38613861) + - 解决方案就是利用 Maven Helper, 分析所有的Dependency, 找到上述两组并存的情况, exclude一方就解决了 + - 或者 通过 `mvn dependency:tree` 手动分析和手动exclude + +******************** # 分析日志 ## Linux上查看日志 @@ -365,3 +413,8 @@ _4.另外还有SocketAppender、SMTPAppender、DBAppender、SyslogAppender、Sif > 一个专门用于浏览日志文件的软件 | [官网](http://lnav.org/) | [文档](http://lnav.readthedocs.io/en/latest/) > [博客: LNAV:基于 Ncurses 的日志文件阅读器 ](https://linux.cn/article-6677-1.html) +******************** +# 日志采集 +## Filebeat + +## K8s diff --git a/Java/MIS.md b/Java/MIS.md index 33d63ce..6736e95 100644 --- a/Java/MIS.md +++ b/Java/MIS.md @@ -1,24 +1,29 @@ -`目录 start` - -- [完整的MIS构建经验](#完整的mis构建经验) - - [MVC](#mvc) - - [持久化层](#持久化层) - - [数据库设计](#数据库设计) - - [Domain对象设计](#domain对象设计) - - [控制层](#控制层) - - [权限控制](#权限控制) - - [Session和Token的对比](#session和token的对比) - - [统一授权](#统一授权) - - [视图层](#视图层) - - [分布式](#分布式) - - [CAP定理](#cap定理) - -`目录 end` |_2018-08-26_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: MIS +date: 2018-12-20 10:42:29 +tags: + - MIS +categories: + - Java +--- + +**目录 start** + +1. [完整的MIS构建经验](#完整的mis构建经验) + 1. [MVC](#mvc) + 1. [持久化层](#持久化层) + 1. [数据库设计](#数据库设计) + 1. [Domain对象设计](#domain对象设计) + 1. [控制层](#控制层) + 1. [权限控制](#权限控制) + 1. [Session和Token的对比](#session和token的对比) + 1. [统一授权](#统一授权) + 1. [视图层](#视图层) + +**目录 end**|_2020-06-24 02:06_| **************************************** # 完整的MIS构建经验 - -- [OAuth 2.0授权框架](https://github.com/jeansfish/RFC6749.zh-cn/blob/master/index.md) - +> MIS: Management information system ## MVC ### 持久化层 @@ -43,13 +48,9 @@ - 如果登录和页面的跳转路由还是由后端控制的,那么Token的实现就有点没有那么必要了。(不过为了安全性能够防范CSRF) ##### 统一授权 -> [码农翻身:从密码到token,一个授权的故事](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513744&idx=1&sn=93d0db97cfd67422bcd21c8afd00f495&chksm=80d67b53b7a1f24537fdc7c10eb2783357c1f8c65ad55601a722216d2293ae3fb7b1c16e5449&scene=21#wechat_redirect) | [自己收集到的相关文档](/API_DOC.md#登录授权) +> [码农翻身:从密码到token,一个授权的故事](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513744&idx=1&sn=93d0db97cfd67422bcd21c8afd00f495&chksm=80d67b53b7a1f24537fdc7c10eb2783357c1f8c65ad55601a722216d2293ae3fb7b1c16e5449&scene=21#wechat_redirect) ### 视图层 *一种比较安全的iframe思路* - 在主页面上写form iframe页面用来展示,这样的话,截图截不了长图,也不能保存文件,也不能打印出来(试了好多种方式去修改教务系统得到的结论) - -## 分布式 -### CAP定理 -> [码农翻身:张大胖和CAP定理](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513560&idx=1&sn=ba861726537c57bd34253cbce010b5fe&chksm=80d67a1bb7a1f30df37905ce979504aa132dcaef59075577ff52f45f057734825a59f6de75c9&scene=21#wechat_redirect) diff --git a/Java/MSA/Quarkus.md b/Java/MSA/Quarkus.md new file mode 100644 index 0000000..ee1f03d --- /dev/null +++ b/Java/MSA/Quarkus.md @@ -0,0 +1,21 @@ +--- +title: Quarkus +date: 2024-09-03 21:27:16 +tags: +categories: +--- + + +💠 + +- 1. [Quarkus](#quarkus) + +💠 2024-09-03 21:27:16 +**************************************** +# Quarkus + +> [Get Started](https://quarkus.io/get-started/) + +> [A Guide to Micrometer in Quarkus](https://www.baeldung.com/quarkus-micrometer) +> [Observability in Quarkus 3](https://quarkus.io/blog/quarkus-observability-3-3/) + diff --git a/Java/MSA/README.md b/Java/MSA/README.md index 8edc2f9..6569276 100644 --- a/Java/MSA/README.md +++ b/Java/MSA/README.md @@ -1,10 +1,21 @@ -`目录 start` +--- +title: Java平台的微服务 +date: 2020-03-27 15:27:30 +tags: +categories: +--- + +**目录 start** -- [Java上的微服务](#java上的微服务) +1. [Java上的微服务](#java上的微服务) -`目录 end` |_2018-07-11_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) +**目录 end**|_2020-03-27 15:27_| **************************************** -# Java上的微服务 +# Java平台的微服务 > Dubbo 和 SpringCloud 的选择 > [在 Docker 上运行一个 RESTful 风格的微服务](https://segmentfault.com/a/1190000002930500) +> [参考: 链家网房源平台微服务架构实践 ](https://mp.weixin.qq.com/s?__biz=MjM5MDE0Mjc4MA==&mid=2650999401&idx=1&sn=5fde04f1b08309fb8efa0a87696e6838&chksm=bdbef23a8ac97b2ca4f7bd757b9032aae28022e8bd75784515297fe66f63fc14d2d61a4ecd68&mpshare=1&scene=1&srcid=11276sR0AJk0tgwlJRleKuaY#rd) + + +> [APM](/Skills/Ecology/APM.md) diff --git a/Java/README.md b/Java/README.md new file mode 120000 index 0000000..980c119 --- /dev/null +++ b/Java/README.md @@ -0,0 +1 @@ +JavaReadme.md \ No newline at end of file diff --git a/Java/Spring/SpringCloud.md b/Java/Spring/Cloud/Readme.md similarity index 63% rename from Java/Spring/SpringCloud.md rename to Java/Spring/Cloud/Readme.md index 218982e..8c606b9 100644 --- a/Java/Spring/SpringCloud.md +++ b/Java/Spring/Cloud/Readme.md @@ -1,15 +1,6 @@ -`目录 start` - -- [SpringCloud](#springcloud) - - [集成docker](#集成docker) - - [微服务](#微服务) - - [通信方式](#通信方式) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** # SpringCloud - -> [参考博客: Spring Cloud 入门系列](http://www.spring4all.com/article/320) +> [SpringCloudLearning](https://github.com/forezp/SpringCloudLearning) +> [参考: Spring Cloud 入门系列](http://www.spring4all.com/article/320) ## 集成docker > [boot in Alpine Linux](https://www.huangyunkun.com/2016/04/03/spring-boot-alpine-linux/) @@ -19,7 +10,6 @@ - 基于此镜像构建小于Ubuntu`frolvlad/alpine-oraclejdk8:slim` - [maven 和 gradle两种集成](https://github.com/waylau/docker-demos) - ## 微服务 > [《Spring Cloud实战》一书的配套示例代码](https://github.com/dyc87112/SpringCloudBook) > [《Spring Cloud与Docker微服务架构实战》1-11章配套代码](https://github.com/itmuch/spring-cloud-docker-microservice-book-code) diff --git a/Java/Spring/Cloud/SpringCloud-Gateway.md b/Java/Spring/Cloud/SpringCloud-Gateway.md new file mode 100644 index 0000000..12ff87d --- /dev/null +++ b/Java/Spring/Cloud/SpringCloud-Gateway.md @@ -0,0 +1,22 @@ +--- +title: SpringCloud-Gateway +date: 2023-10-10 16:32:15 +tags: +categories: +--- + +💠 + +- 1. [Spring Cloud Gateway](#spring-cloud-gateway) +- 2. [Tips](#tips) + +💠 2023-10-10 16:32 +**************************************** +# Spring Cloud Gateway +> [Official Doc](https://spring.io/projects/spring-cloud-gateway) + +************************ + +# Tips +> [Persisting Spring Cloud Gateway Routes in Database](https://stackoverflow.com/questions/50810765/persisting-spring-cloud-gateway-routes-in-database) + diff --git a/Java/Spring/Flux/FluxConcept.md b/Java/Spring/Flux/FluxConcept.md new file mode 100644 index 0000000..85cd9c1 --- /dev/null +++ b/Java/Spring/Flux/FluxConcept.md @@ -0,0 +1 @@ +# WebFlux 设计概念 diff --git a/Java/Spring/Flux/WebFlux.md b/Java/Spring/Flux/WebFlux.md new file mode 100644 index 0000000..2288a0c --- /dev/null +++ b/Java/Spring/Flux/WebFlux.md @@ -0,0 +1,24 @@ +--- +title: WebFlux +date: 2024-03-14 21:17:01 +tags: +categories: +--- + +💠 + +- 1. [WebFlux](#webflux) + +💠 2024-03-14 21:28:14 +**************************************** +# WebFlux +> [Official Doc](https://docs.spring.io/spring-framework/reference/web/webflux.html) + +相较于传统的SpringMVC框架体系,最大的区别在于全链路的无阻塞的事件驱动`注意数据库驱动层还不够阻塞驱动成熟` + +![](https://docs.spring.io/spring-framework/reference/_images/spring-mvc-and-webflux-venn.png) + +相关博客: +> [webflux的探索与实战](https://juejin.cn/column/7024464332566036517) +> [参考: 译:尝试使用Spring WebFlux](http://www.spring4all.com/article/1167) + diff --git a/Java/Spring/README.md b/Java/Spring/README.md index 3327a9f..0224daf 100644 --- a/Java/Spring/README.md +++ b/Java/Spring/README.md @@ -13,6 +13,6 @@ - SpringWebFlux.md - Transactional.md `事务处理` - ## Springboot -> 快速开发框架,但是。。使用注解的话,开发小项目是好的,大型项目是不好的 + +- [ ] 优化该目录 diff --git a/Java/Spring/Spring.md b/Java/Spring/Spring.md index e22ec72..98dd194 100644 --- a/Java/Spring/Spring.md +++ b/Java/Spring/Spring.md @@ -1,33 +1,49 @@ -`目录 start` - -- [Spring](#spring) - - [配置使用](#配置使用) - - [通过构建工具](#通过构建工具) - - [注解方式](#注解方式) - - [xml文件配置](#xml文件配置) - - [常用的注解](#常用的注解) - - [xml方式](#xml方式) - - [xml方式和注解方式的比较](#xml方式和注解方式的比较) - - [Spring技巧](#spring技巧) - - [获取Context上下文环境](#获取context上下文环境) - - [在JSP或Servlet中获取](#在jsp或servlet中获取) - - [Spring 和 ServletContextList](#spring-和-servletcontextlist) -- [基础](#基础) - - [生命周期](#生命周期) - - [IOC/DI 控制反转](#iocdi-控制反转) - - [AOP](#aop) - - [基本概念](#基本概念) - - [基本配置](#基本配置) - - [注意](#注意) - - [1 Spring AOP还是完全用AspectJ?](#1-spring-aop还是完全用aspectj) - - [2 Spring AOP中使用@AspectJ还是XML?](#2-spring-aop中使用@aspectj还是xml) - - [3 混合切面类型](#3-混合切面类型) - - [Scheduling](#scheduling) - - [Websocket](#websocket) - - [maven配置](#maven配置) - - [Web开发上的一些优秀的习惯](#web开发上的一些优秀的习惯) - -`目录 end` |_2018-09-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Spring +date: 2018-12-21 10:46:01 +tags: + - Spring +categories: + - Java +--- + +💠 + +- 1. [Spring](#spring) + - 1.1. [配置使用](#配置使用) + - 1.1.1. [通过构建工具](#通过构建工具) + - 1.1.2. [注解方式](#注解方式) + - 1.1.2.1. [xml文件配置](#xml文件配置) + - 1.1.2.2. [常用的注解](#常用的注解) + - 1.1.3. [xml方式](#xml方式) + - 1.1.3.1. [xml方式和注解方式的比较](#xml方式和注解方式的比较) + - 1.2. [Spring技巧](#spring技巧) + - 1.2.1. [获取Context上下文环境](#获取context上下文环境) + - 1.2.1.1. [在JSP或Servlet中获取](#在jsp或servlet中获取) + - 1.2.2. [Spring 和 ServletContextList](#spring-和-servletcontextlist) +- 2. [基础](#基础) + - 2.1. [Bean概述](#bean概述) + - 2.1.1. [Bean生命周期](#bean生命周期) + - 2.1.2. [Bean的作用域](#bean的作用域) + - 2.2. [容器的扩展点](#容器的扩展点) + - 2.2.1. [Aware](#aware) + - 2.2.2. [BeanPostProcessor](#beanpostprocessor) + - 2.2.3. [BeanFactoryPostProcessor](#beanfactorypostprocessor) + - 2.3. [IOC/DI 控制反转](#iocdi-控制反转) + - 2.3.1. [循环依赖](#循环依赖) + - 2.4. [Application Context](#application-context) + - 2.5. [Scheduling](#scheduling) + - 2.6. [Events](#events) + - 2.7. [异步](#异步) + - 2.8. [RestTemplate](#resttemplate) + - 2.9. [Utils](#utils) + - 2.9.1. [ReflectionUtils](#reflectionutils) + - 2.10. [SpEL](#spel) +- 3. [Web开发的最佳实践](#web开发的最佳实践) + - 3.1. [优雅部署](#优雅部署) +- 4. [Tips](#tips) + +💠 2024-10-08 16:06:24 **************************************** # Spring > [Spring官网](https://spring.io/) | [spring4all社区](http://www.spring4all.com/) @@ -86,8 +102,15 @@ _其他,可选_ - 自动注入 - `@Resource([value=]"id")` 按名字注入 - `@Autowried` 根据类型自动注入(只对单例起作用)和 `Resource(类名首字母小写)` 等价 - - `@Qualifier("id") `自动注入后的进一步精确(多个的情况:) + - 通过阅读源码还可以知道 可以将符合条件的Bean注入到 List 和 Map 中去, 甚至 Optional + - `@Qualifier("id") `自动注入后的进一步精确(多个Bean的情况) + - 如果同类型的Bean有明显的主次关系(或者说缺省值),可以在Bean的声明时加上 `@Primary` 注解,那就可以省去`Qualifier`的使用 + - **注意 :** 关于自动注入, 在属性上打 @Autowried 注解是不建议的, 作者建议采用构造器方式: [Why field injection is evil](http://olivergierke.de/2013/11/why-field-injection-is-evil/) + - 如果使用了 lombok 那么可以在类上使用 + - `@RequiredArgsConstructor(onConstructor = @__(@Autowired))` + - 然后注入的属性打上 `@NonNull` 注解 + - 本质上是帮你自动生成了一个将所有 `@NonNull` 注解属性作为参数的构造器 - AOP - @Aspect 注明是切面类 @@ -96,6 +119,7 @@ _其他,可选_ - bean扫描 - ComponentScan 扫描指定包下Spring注解的类 +> [参考: Why field injection is evil](http://olivergierke.de/2013/11/why-field-injection-is-evil/) *********************** ### xml方式 - 只用到bean的头,主要配置内容:`` @@ -164,12 +188,200 @@ _其他,可选_ } ``` +**************** + # 基础 -## 生命周期 -@PreDestroy -@PostConstruct -- [ ] 完善 +## Bean概述 + +在容器内bean的定义包含以下信息: + +- `包限定的类名`:通常定义ben的实现类 +- `bean的行为元素`:包含bean的范围、生命周期等 +- `依赖项`:该bean所引用的依赖项 +- `设置其他属性配置`:如配置连接池bean中使用的连接数等 + +### Bean生命周期 +- 初始化(当一个bean配置和了多个生命周期时,执行顺序如下顺序) + - 在方法上使用`@PostConstruct`注解(推荐使用,同xml中的`init-method`属性一致) + - 实现接口`InitializingBean`,在方法`afterPropertiesSet()`中可进行bean的初始化操作(在容器设置完bean的必须属性后执行,不建议使用接口,推荐使用注解或xml配置) + - 在bean的xml配置中在``标签上使用类似于属性`default-init-method="init"`的配置后,在beans下配置的bean会在初始化时调用bean中定义的方法名为`init`的方法 + - 实现接口`BeanPostProcessor`中的`postProcessBeforeInitialization`及`postProcessAfterInitialization`方法。该接口会处理他可以找到的所有回调接口 + +- 销毁(当一个bean配置和了多个生命周期时,执行顺序如下顺序) + - 在方法上使用`@PreDestroy`注解(同上,及与xml配置中的`destroy-method`属性一致) + - 实现接口`DisposableBean`,在方法`destroy()`中,可进行bean的销毁时的操作 + - 在bean的xml配置中在``标签上使用类似于属性`default-destroy-method="destroy"`的配置后,在beans下配置的bean会在销毁时调用bean中定义的方法名为`destroy`的方法 + +- 关闭与启动 + - 实现接口`Lifecycle` + +- 在非Web应用中关闭spring IOC容器 + - 调用`ConfigurableApplicationContext`中的`registerShutdownHook()`方法,这样便就能调用销毁的回调函数 + +- 为Bean提供`ApplicationContext`实例 + - 实现`ApplicationContextAware`,则就可以为该bean实例获取`ApplicationContext` + +- 让Bean获取自身在BeanFactory中的名称(id或name) + - 实现`BeanNameAware`接口中,则咎可以获取名称(该方法在初始化之前) + +### Bean的作用域 + +在Spring2.0之前spring中bean的作用域只有`singleton(单例)`及`prototype(原型)`两种。 +在Spring2.0后便又增加了`request`、`session`及`application`三种作用域,且这三种作用域都只用于基于web的Spring ApplicationContext。 +直到现在,Spring又增加了作用与`webSocket`的作用域,该作用域与2.0之后增加的三种作用域一样都只作用与基于web的Spring ApplicationContext。 + +- `singleton`: 该作用域是Spring bean默认的作用域;使用该作用域时,在Spring IOC容器中只会存在一个共享的bean实例。所有的对该bean的请求(如通过注入或getBean方法获取实例)都只会获取同一个实例。针对于该作用域,Spring容器可进行比较全面的生命周期的管理 +- `prototype`: 使用该作用域时,所有对于该bean的请求都会返回一个新的实例,即每次请求,都会创建一个新的实例 +- `request`: 该作用域将bean的作用范围限定在单个HTTP请求中,即每次HTTP请求都会创建一个新的bean实例,是的每次HTTP请求都有一个自己的实例。该作用域只用于基于web的Spring ApplicationContext。 +- `session`: 该作用域将bean的作用范围限定在HTTP请求中的Session的生命周期内。即bean的生命周期与Session一致,当Session存活时,该bean的实例也存活,但当Session销毁时,该Session内的bean实例也将被销毁。适合于基于web的Spring ApplicationContext +- `application`: 使用该作用域时,在整个web程序中,只会存在一个该bean的实例。如果只存在一个web应用,则该bean的作用域与`singleton`类似。适合于基于web的Spring ApplicationContext。 +- `websocket`: 该作用域是Spring新增的作用域,该作用域将该bean实例作用范围限定在一个生命周期的WebSocket中。适合于基于web的Spring ApplicationContext。 + + +## 容器的扩展点 + +### Aware +在Spring容器中,提供了许多Aware接口,使用这些接口可以更好的对bean进行扩展,获取许多与容器相关的组件;今天,我们大概来看看Spring中提供的一些Aware接口: +`BeanNameAware`: 该接口只有一个`setBeanName`方法,如果Spring容器检测到bean实现了该接口,则会将该bean实例的beanName属性注入到该实例中。 +- `ApplicationContextAware`: 该接口只有个`setApplicationContext`方法;如果Spring容器检测到bean实现了该接口,则会将Spring的ApplicationContext注入到bean实例中。 + - 但一般不建议通过实现该接口获取容器ApplicationContext,因为通过实现接口的方式会增加代码的耦合度,如果希望获取ApplicationContext实例,可以使用一般的注入方式,如使用注解`@Autowired`,这样便就可以获取ApplicationContext,如: + ```java + @Autowired + private ApplicationContext applicationContext; + ``` +- `BeanClassLoaderAware`: 该接口有个`setBeanClassLoader`方法,与前两个接口类似,实现了该接口后,可以向bean中注入加载该bean的ClassLoader +- `BeanFactoryAware`: 该接口有个`setBeanFactory`方法,用来将当前的beanFactory注入到该bean实例中 +- `ApplicationEventPublisherAware`: ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext的事件处理。 + - 其中`ApplicationEvent`为容器事件。实现接口`ApplicationEventPublisherAware`的bean可获取`ApplicationEventPublisher`实例(因为ApplicationContext已实现接口`ApplicationEventPublisher`接口,所以其实此处默认还是注入了`ApplicationContext`实例),用于发布事件 +- `MessageSourceAware`: 实现该接口可,可获取`MessageSource`实例,该实例用于解析消息的策略接口,支持该类消息的参数化与国际化(因为ApplicationContext已实现接口`MessageSource`接口,所以其实此处默认还是注入了`ApplicationContext`实例) +- `NotificationPublisherAware`: 实现该接口的bean,可获取JMX通知发布者 +- `ResourceLoaderAware`: 可获取Spring中配置的加载程序(ResourceLoader),用于对资源进行访问;可用于访问类l类路径或文件资源 +- `ServletConfigAware`: 该接口仅在wen应用中有效,用于获取ServletConfig +- `ServletContextAware`: 该接口仅在wen应用中有效,用于获取ServletContext +- `LoadTimeWeaverAware`: 可获取`LoadTimeWeaver`实例,用于在加载时处理类定义 + +### BeanPostProcessor + +在Spring中。我们可以定义bean的初始化方法,从而完成某些初始化动作。 + +可查看源码中对该接口 BeanPostProcessor 的注释定义 + +> 工厂钩子,允许自定义修改新的bean实例,例如 检查标记接口或用代理包装它们。 +> ApplicationContexts可以在其bean定义中自动检测 BeanPostProcessor bean,并将它们应用于随后创建的任何bean。bean factories允许对后处理器进行编程注册,适用于通过该工厂创建的所有bean。 + +简单来说,就是我们可以在Spring创建bean实例后,bean初始化之前和初始化之后完成一些自定义的操作。 + +顾名思义,这两个方法,一个是在bean初始化之前执行,一个是在bean初始化之后执行。 +- `postProcessBeforeInitialization` +- `postProcessAfterInitialization` + +假如有个定义好的Student,现在希望在不改变原有代码的情况下将它的address字段赋上某个值。 + +Student +```java + @Component + @Data + public class Student { + private int id; + private String name; + private String address; + } +``` + +扩展 +```java + @Component + public class StudentExpansion implements BeanPostProcessor { + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + if (bean instanceof Student) { + Student student = (Student) bean; + student.setAddress("中国"); + } + return bean; + } + } +``` + +### BeanFactoryPostProcessor +和 BeanPostProcessor 类似,都是Spring用于初始化Bean的扩展点,但是 `BeanFactoryPostProcessor`的执行时间是在Spring容器对bean进行实例化之前,而`BeanPostProcessor`则是在Spring容器对bean进行实例化之后的初始化环节。 + +`BeanFactoryPostProcessor`允许对bean的定义(配置的元数据)进行修改。例如我们常见的下列配置: + +```xml + + + + + + + + + + +``` + +在以上对于数据库的配置中,我们引用了配置文件`jdbc.properties`中的值 +```ini + jdbc.driver = com.mysql.jdbc.Driver + jdbc.url = jdbc:mysql:///BookManager + jdbc.user = root + jdbc.password =123 +``` + +那么问题来了,在Spring将bean实例化时是如何将配置元数据中的`${jdbc.driver}`替换成真实的`com.mysql.jdbc.Driver`的呢? +- 这便就是`BeanFactoryPostProcessor`在Spring容器中的最典型的使用场景之一。 +- 该处理的实现类为`PropertyPlaceholderConfigurer`,它实现了接口`BeanFactoryPostProcessor`中的`postProcessBeanFactory`方法, +- 负责在bean实例化之前将配置元数据中的如同`${jdbc.driver}`的配置替换为它真实的值,然后Spring便就可以正常的实例化了。 + +- 在`PropertyPlaceholderConfigurer`中`postProcessBeanFactory`方法的实现如下: + +```java + /** + * {@linkplain #mergeProperties Merge}, {@linkplain #convertProperties convert} and + * {@linkplain #processProperties process} properties against the given bean factory. + * @throws BeanInitializationException if any properties cannot be loaded + */ + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + try { + // 读取配置中配置的properties文件 + Properties mergedProps = mergeProperties(); + + // Convert the merged properties, if necessary. + convertProperties(mergedProps); + + // Let the subclass process the properties. + processProperties(beanFactory, mergedProps); + } + catch (IOException ex) { + throw new BeanInitializationException("Could not load properties", ex); + } + } +``` + +- 其中`processProperties`方法在`PropertyPlaceholderConfigurer`中的实现为 + +```java + /** + * Visit each bean definition in the given bean factory and attempt to replace ${...} property + * placeholders with values from the given properties. + */ + @Override + protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props) + throws BeansException { + + StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props); + doProcessProperties(beanFactoryToProcess, valueResolver); + } +``` + +************************ ## IOC/DI 控制反转 - DI 译为依赖注入 所有的bean都在IOC容器中(单例的)多例的不在该容器中进行管理 @@ -181,158 +393,111 @@ _其他,可选_ - **动态代理** - 针对一个方面编写一个InvocationHandler,然后借用JDK反射包中的Proxy类为各种接口动态生成相应的代理类 -属性上 @Autowired 即可, 但是现在不建议直接在属性上使用注解, 而是建议在构造器上, 为了避免 手动使用new 实例化Bean, 然后里面本该注入的属性全部为null -可以用lombok来协助 -```java -@Component -@RequiredArgsConstructor(onConstructor = @__(@Autowired)) -public class A{ - @NonNull - private B b; - } -``` -********************** -## AOP -> Aspect Oriented Programming 面向切面编程 - -### 基本概念 -| 英文 | 解释 | -|:----|:----| -|`JoinPoint`|切入面、连接点、切入点(所有方法) | -|`PointCut` |切点(特殊的连接点,需要增强的连接点)| -|`Advice`|增强(切入点的逻辑,待添加的功能)| -|`Aspect`|切面(切点和增强的合集)| -|`Target`|目标对象(被增强的实例)| -|`Weave`|织入(增强切点的过程)| -|`Proxy`|代理(增强后的类,一般是使用了代理类) 装饰器模式| -|`Introduction`|引介(为类添加属性和方法) 用的较少因为破坏了OOP思想| - -********************* -### 基本配置 -`XML文件头` -```xml - - -``` -- 方法级别的添加代理,Servlet中的过滤器也类似(但是那个是类级别的) - -```xml - - - - - - - - - - - - - - - - -``` -### 注意 -- 要注意环绕的写法 `public void around(ProceedingJoinPoint m)throws Throwable{` - - [Spring AOP中的around](https://www.oschina.net/code/snippet_246557_9205) - -- 然后在test类中直接getBean(基类)但是实际上是获取到的是装饰好的代理对象 - - [Spring AOP配置(转)](http://blog.csdn.net/yuqinying112/article/details/7335416) - - [aop:config详解](http://www.cnblogs.com/yangy608/archive/2010/11/14/1876833.html) - -- 善用debug 调试看是否获取到的是代理对象 $proxy - -- 在Spring的配置文件中,所有的切面和通知器都必须定义在` ` 元素内部。 一个`application context`可以包含多个 ``。 一个` ` 可以包含 `pointcut`, `advisor` 和 `aspect` 元素(注意它们必须按照这样的顺序进行声明)。 - -#### 1 Spring AOP还是完全用AspectJ? -做能起作用的最简单的事。Spring AOP比完全使用AspectJ更加简单,因为它不需要引入AspectJ的编译器/织入器到你开发和构建过程中。 -如果你仅仅需要在Spring bean上通知执行操作,那么Spring AOP是合适的选择。如果你需要通知domain对象或其它没有在Spring容器中 -管理的任意对象,那么你需要使用AspectJ。如果你想通知除了简单的方法执行之外的连接点(如:调用连接点、字段get或set的连接点等等), -也需要使用AspectJ。 -当使用AspectJ时,你可以选择使用AspectJ语言(也称为“代码风格”)或@AspectJ注解风格。 -如果切面在你的设计中扮演一个很大的角色,并且你能在Eclipse中使用AspectJ Development Tools (AJDT), 那么首选AspectJ语言 :- -因为该语言专门被设计用来编写切面,所以会更清晰、更简单。如果你没有使用 -Eclipse,或者在你的应用中只有很少的切面并没有作为一个主要的角色,你或许应该考虑使用@AspectJ风格 -并在你的IDE中附加一个普通的Java编辑器,并且在你的构建脚本中增加切面织入(链接)的段落。 - -#### 2 Spring AOP中使用@AspectJ还是XML? - -如果你选择使用Spring AOP,那么你可以选择@AspectJ或者XML风格。总的来说,如果你使用Java 5, 我们建议使用@AspectJ风格。 -显然如果你不是运行在Java 5上,XML风格是最佳选择。XML和@AspectJ 之间权衡的细节将在下面进行讨论。 -XML风格对现有的Spring用户来说更加习惯。它可以使用在任何Java级别中(参考连接点表达式内部的命名连接点,虽然它也需要Java 5) -并且通过纯粹的POJO来支持。当使用AOP作为工具来配置企业服务时(一个好的例子是当你认为连接点表达式是你的配置中的一部分时, -你可能想单独更改它)XML会是一个很好的选择。对于XML风格,从你的配置中可以清晰的表明在系统中存在那些切面。 -XML风格有两个缺点。第一是它不能完全将需求实现的地方封装到一个位置。DRY原则中说系统中的每一项知识都必须具有单一、无歧义、权威的表示。 -当使用XML风格时,如何实现一个需求的知识被分割到支撑类的声明中以及XML配置文件中。当使用@AspectJ风格时就只有一个单独的模块 -切面- -信息被封装了起来。 第二是XML风格同@AspectJ风格所能表达的内容相比有更多的限制:仅仅支持"singleton"切面实例模型,并且不能在XML中组合命名连接点的声 -明。 例如,在@AspectJ风格中我们可以编写如下的内容: +属性上 @Autowired 即可, 但是现在不建议直接在属性上使用注解, 而是建议用在构造器上, 这是为了避免NPE: 当使用new实例化时, 里面本该注入的属性会为null +`使用Lombok简化该方式` ```java - @Pointcut(execution(* get*())) - public void propertyAccess() {} - @Pointcut(execution(org.xyz.Account+ *(..)) - public void operationReturningAnAccount() {} - @Pointcut(propertyAccess() && operationReturningAnAccount()) - public void accountPropertyAccess() {} + @Component + @RequiredArgsConstructor(onConstructor = @__(@Autowired)) + public class A{ + @NonNull + private B b; + } ``` -在XML风格中能声明开头的两个连接点: -```xml - - -``` -但是不能通过组合这些来定义accountPropertyAccess连接点 -- @AspectJ风格支持其它的实例模型以及更丰富的连接点组合。它具有将将切面保持为一个模块单元的优点。 还有一个优点就是@AspectJ切面能被Spring AOP和AspectJ两者都理解 -- 所以如果稍后你认为你需要AspectJ 的能力去实现附加的需求,那么你非常容易转移到基于AspectJ的途径。总而言之,我们更喜欢@AspectJ风格只要你有切面 去做超出简单的“配置”企业服务之外的事情。 +### 循环依赖 +- [Spring循环依赖](https://cloud.tencent.com/developer/article/1769948) -#### 3 混合切面类型 -我们完全可以混合使用以下几种风格的切面定义:使用自动代理的@AspectJ 风格的切面,`schema-defined ` 的切面, -和用 `` 声明的advisor,甚至是使用Spring 1.2风格的代理和拦截器。 -由于以上几种风格的切面定义的都使用了相同的底层机制,因此可以很好的共存。 +************************ + +## Application Context +> [Spring Application Context Events](https://www.baeldung.com/spring-context-events) `通过监听Context的事件感知Spring上下文的启动和关闭` ***************** ## Scheduling > [Official Doc](https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/integration.html#scheduling) -> [参考博客: The @Scheduled Annotation in Spring](https://www.baeldung.com/spring-scheduled-tasks) -> [参考博客: Spring Scheduler的使用与坑](http://qinghua.github.io/spring-scheduler/) -> [参考博客: [Spring]支持注解的Spring调度器](https://www.cnblogs.com/jingmoxukong/p/5825806.html#%E5%AE%8C%E6%95%B4%E8%8C%83%E4%BE%8B) -> [参考博客: spring scheduled的动态线程池调度和任务进度的监控](https://blog.csdn.net/yyx1025988443/article/details/78698046) +> [参考: The @Scheduled Annotation in Spring](https://www.baeldung.com/spring-scheduled-tasks) +> [参考: Spring Scheduler的使用与坑](http://qinghua.github.io/spring-scheduler/) +> [参考: [Spring]支持注解的Spring调度器](https://www.cnblogs.com/jingmoxukong/p/5825806.html#%E5%AE%8C%E6%95%B4%E8%8C%83%E4%BE%8B) +> [参考: spring scheduled的动态线程池调度和任务进度的监控](https://blog.csdn.net/yyx1025988443/article/details/78698046) 其主体是 TaskExecutor 和 TaskScheduler 组成的, 也就是调度和执行 - [cron maker](http://www.cronmaker.com/) -- []() -******************* -## Websocket -### maven配置 -```xml - - org.springframework - spring-websocket - ${spring.version} - - - org.springframework - spring-messaging - ${spring.version} - -``` -- [ ] Spring方式, 现在用boot用多了, 都忘了怎么配置Spring了 +************************ + +## Events +> [Spring Events](https://www.baeldung.com/spring-events) + +> [Synchronous and Asynchronous Spring Events in One Application](https://www.keyup.eu/en/blog/101-synchronous-and-asynchronous-spring-events-in-one-application) +> [@EventListener with @Async in Spring](https://stackoverflow.com/questions/37179426/eventlistener-with-async-in-spring) + +> [参考: spring线程池(同步、异步)](http://www.cnblogs.com/duanxz/p/9435343.html) + +## 异步 +> 需要启动类或配置类上标注 @EnableAsync + +应用层面在方法上加上@Async就可以快速将普通方法转为异步方法。 + +但是便利就表示处理是通用的,实际业务场景多变的情况下就容易出问题了。 +- 线程池问题: 默认使用Spring声明的 +- 任务通信问题: + +************************ + +## RestTemplate +> [大文件OOM问题](https://github.com/spring-projects/spring-framework/issues/12564) 发送文件时将文件的字节全部读取到内存中再发送,文件大且多时容易OOM + +## Utils +### ReflectionUtils + + +## SpEL +> [Spring5.5 SpEL](https://docs.spring.io/spring-integration/docs/5.5.11/reference/html/spel.html) **************** -## Web开发上的一些优秀的习惯 + +# Web开发的最佳实践 + - 使用AOP来简化开发MVC的代码 - 繁杂的代码如何简化 + +## 优雅部署 +> 如何在用户影响最小的情况下,实现服务的升级部署 + +> [Spring环境中正确关闭线程池的姿势](https://blog.csdn.net/qq271859852/article/details/107442161) + +需要解决的问题: +- A 后端服务不可用,需要等新进程启动完后才能恢复服务 +- B 用户有感知到请求失败或无响应,请求超时,但是等几个请求后又会恢复正常 +- C 业务线程池里执行中的线程被中断,业务在任意的环节上中断,数据不一致 +- D 可能导致流量倾斜导致其他节点负载飙升,甚至引起雪崩效应(节点一个接一个down掉) +- E 用户请求分发到未启动完全或未初始化业务逻辑的节点上 + +> 简单实现 +1. 宿主机部署,Nginx 代理到 多个Java进程,手动逐个进程 kill和启动 +2. 解决的问题: A + +> 初步方案 +1. 采用K8S部署,svc下分发到多个pod,pod配置存活和就绪探针,滚动升级(启动新的就绪容器后才销毁已有旧容器) +2. 解决的问题: A D E + +> 优化方案 +1. 除了K8S配置外,应用本身增加 shutdownHook 线程对资源进行回收和限制 +2. 或者使用Spring的生命周期管理,监听 ContextClosedEvent 事件,对线程池,缓存等等,业务系统上需要等待执行或者销毁的数据。 +3. 解决的问题: A C D E + +> 网关 +1. 基于以上配置外, 引入 [Gateway](/Skills/Ecology/Gateway.md#功能) + - 进入销毁周期的服务器会从网关的反向代理列表中移除,新请求不会进入该服务器 +1. 解决的问题: ABCDE + +************************ + +# Tips +- 不要对有 @Configuration 注解的配置类进行 Field 级的依赖注入 否则容易引发循环依赖 [Spring循环依赖问题分析](https://blog.mythsman.com/post/5d838c7c2db8a452e9b7082c/) + +如果有两个maven模块, A依赖B 假如 A和B中有相同 package 的同名类 a b,此时A模块是main入口模块,配置了对应package注解扫描 +- [ ] 此时会是a还是b,将注册到IOC容器内 diff --git a/Java/Spring/Spring5.md b/Java/Spring/Spring5.md deleted file mode 100644 index eddcf74..0000000 --- a/Java/Spring/Spring5.md +++ /dev/null @@ -1,9 +0,0 @@ -`目录 start` - -- [Spring5](#spring5) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Spring5 - -> [Spring Framework 5 中的新特性](https://www.ibm.com/developerworks/cn/java/j-whats-new-in-spring-framework-5-theedom/index.html) \ No newline at end of file diff --git a/Java/Spring/SpringAOP.md b/Java/Spring/SpringAOP.md new file mode 100644 index 0000000..6fce72f --- /dev/null +++ b/Java/Spring/SpringAOP.md @@ -0,0 +1,140 @@ +--- +title: Spring AOP +date: 2018-12-21 10:49:23 +tags: + - AOP + - Spring +categories: + - Java +--- + +**目录 start** + +1. [AOP](#aop) + 1. [动态代理](#动态代理) + 1. [基本概念](#基本概念) + 1. [基本配置](#基本配置) + 1. [注意](#注意) + 1. [1 Spring AOP还是完全用AspectJ?](#1-spring-aop还是完全用aspectj) + 1. [2 Spring AOP中使用@AspectJ还是XML?](#2-spring-aop中使用@aspectj还是xml) + 1. [3 混合切面类型](#3-混合切面类型) + +**目录 end**|_2020-08-26 18:31_| +**************************************** +# AOP +> Aspect Oriented Programming 面向切面编程 + +## 动态代理 +> 这是AOP的起源, 最初是JDK的动态Proxy -> cglib/asm + +> [笔记: Java中的代理](/Java/AdvancedLearning/JavaProxy.md) + +## 基本概念 +| 英文 | 解释 | +|:----|:----| +|`JoinPoint`|切入面、连接点、切入点(所有方法) | +|`PointCut` |切点(特殊的连接点,需要增强的连接点)| +|`Advice`|增强(切入点的逻辑,待添加的功能)| +|`Aspect`|切面(切点和增强的合集)| +|`Target`|目标对象(被增强的实例)| +|`Weave`|织入(增强切点的过程)| +|`Proxy`|代理(增强后的类,一般是使用了代理类) 装饰器模式| +|`Introduction`|引介(为类添加属性和方法) 用的较少因为破坏了OOP思想| + + +1. 多个 AOP 时,切面可实现 Ordered 接口 自定义 AOP 顺序 + +********************* +## 基本配置 +`XML文件头` +```xml + + +``` +- 方法级别的添加代理,Servlet中的过滤器也类似(但是那个是类级别的) + +```xml + + + + + + + + + + + + + + + + +``` + +## 注意 +- 要注意环绕的写法 `public void around(ProceedingJoinPoint m)throws Throwable{` + - [Spring AOP中的around](https://www.oschina.net/code/snippet_246557_9205) + +- 然后在test类中直接getBean(基类)但是实际上是获取到的是装饰好的代理对象 + - [Spring AOP配置(转)](http://blog.csdn.net/yuqinying112/article/details/7335416) + - [aop:config详解](http://www.cnblogs.com/yangy608/archive/2010/11/14/1876833.html) + +- 善用debug 调试看是否获取到的是代理对象 $proxy + +- 在Spring的配置文件中,所有的切面和通知器都必须定义在` ` 元素内部。 一个`application context`可以包含多个 ``。 一个` ` 可以包含 `pointcut`, `advisor` 和 `aspect` 元素(注意它们必须按照这样的顺序进行声明)。 + +### 1 Spring AOP还是完全用AspectJ? +做能起作用的最简单的事。Spring AOP比完全使用AspectJ更加简单,因为它不需要引入AspectJ的编译器/织入器到你开发和构建过程中。 +如果你仅仅需要在Spring bean上通知执行操作,那么Spring AOP是合适的选择。如果你需要通知domain对象或其它没有在Spring容器中 +管理的任意对象,那么你需要使用AspectJ。如果你想通知除了简单的方法执行之外的连接点(如:调用连接点、字段get或set的连接点等等), +也需要使用AspectJ。 +当使用AspectJ时,你可以选择使用AspectJ语言(也称为“代码风格”)或@AspectJ注解风格。 +如果切面在你的设计中扮演一个很大的角色,并且你能在Eclipse中使用AspectJ Development Tools (AJDT), 那么首选AspectJ语言 :- +因为该语言专门被设计用来编写切面,所以会更清晰、更简单。如果你没有使用 +Eclipse,或者在你的应用中只有很少的切面并没有作为一个主要的角色,你或许应该考虑使用@AspectJ风格 +并在你的IDE中附加一个普通的Java编辑器,并且在你的构建脚本中增加切面织入(链接)的段落。 + +### 2 Spring AOP中使用@AspectJ还是XML? + +如果你选择使用Spring AOP,那么你可以选择@AspectJ或者XML风格。总的来说,如果你使用Java 5, 我们建议使用@AspectJ风格。 +显然如果你不是运行在Java 5上,XML风格是最佳选择。XML和@AspectJ 之间权衡的细节将在下面进行讨论。 +XML风格对现有的Spring用户来说更加习惯。它可以使用在任何Java级别中(参考连接点表达式内部的命名连接点,虽然它也需要Java 5) +并且通过纯粹的POJO来支持。当使用AOP作为工具来配置企业服务时(一个好的例子是当你认为连接点表达式是你的配置中的一部分时, +你可能想单独更改它)XML会是一个很好的选择。对于XML风格,从你的配置中可以清晰的表明在系统中存在那些切面。 +XML风格有两个缺点。第一是它不能完全将需求实现的地方封装到一个位置。DRY原则中说系统中的每一项知识都必须具有单一、无歧义、权威的表示。 +当使用XML风格时,如何实现一个需求的知识被分割到支撑类的声明中以及XML配置文件中。当使用@AspectJ风格时就只有一个单独的模块 -切面- +信息被封装了起来。 第二是XML风格同@AspectJ风格所能表达的内容相比有更多的限制:仅仅支持"singleton"切面实例模型,并且不能在XML中组合命名连接点的声 +明。 例如,在@AspectJ风格中我们可以编写如下的内容: + +```java + @Pointcut(execution(* get*())) + public void propertyAccess() {} + @Pointcut(execution(org.xyz.Account+ *(..)) + public void operationReturningAnAccount() {} + @Pointcut(propertyAccess() && operationReturningAnAccount()) + public void accountPropertyAccess() {} +``` +在XML风格中能声明开头的两个连接点: + +```xml + + +``` +但是不能通过组合这些来定义accountPropertyAccess连接点 +- @AspectJ风格支持其它的实例模型以及更丰富的连接点组合。它具有将将切面保持为一个模块单元的优点。 还有一个优点就是@AspectJ切面能被Spring AOP和AspectJ两者都理解 +- 所以如果稍后你认为你需要AspectJ 的能力去实现附加的需求,那么你非常容易转移到基于AspectJ的途径。总而言之,我们更喜欢@AspectJ风格只要你有切面 去做超出简单的“配置”企业服务之外的事情。 + +### 3 混合切面类型 +我们完全可以混合使用以下几种风格的切面定义:使用自动代理的@AspectJ 风格的切面,`schema-defined ` 的切面, +和用 `` 声明的advisor,甚至是使用Spring 1.2风格的代理和拦截器。 +由于以上几种风格的切面定义的都使用了相同的底层机制,因此可以很好的共存。 diff --git a/Java/Spring/SpringBoot.md b/Java/Spring/SpringBoot.md index c23cb4c..0308ab1 100644 --- a/Java/Spring/SpringBoot.md +++ b/Java/Spring/SpringBoot.md @@ -1,44 +1,53 @@ -`目录 start` - -- [SpringBoot](#springboot) - - [思考](#思考) - - [参考教程](#参考教程) - - [系列](#系列) - - [开始使用](#开始使用) - - [安装SpringBootCLI](#安装springbootcli) - - [测试模块](#测试模块) - - [配置文件](#配置文件) - - [多种配置文件并切换](#多种配置文件并切换) - - [yml方式](#yml方式) - - [yml和Properties结合](#yml和properties结合) - - [应用配置文件](#应用配置文件) - - [Web模块](#web模块) - - [Lisener](#lisener) - - [ServletContextListener](#servletcontextlistener) - - [上传下载文件](#上传下载文件) - - [错误页面跳转配置](#错误页面跳转配置) - - [跨域](#跨域) - - [全局异常处理](#全局异常处理) - - [HTTPS的配置](#https的配置) - - [线程池](#线程池) - - [项目部署](#项目部署) - - [生成指定文件](#生成指定文件) - - [war](#war) - - [jar](#jar) - - [构建docker镜像](#构建docker镜像) - - [手动方式](#手动方式) - - [gradle结合docker](#gradle结合docker) - - [热部署](#热部署) - -`目录 end` |_2018-09-12_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: SpringBoot +date: 2018-12-21 10:49:39 +tags: + - SpringBoot +categories: + - Java +--- + +💠 + +- 1. [SpringBoot](#springboot) + - 1.1. [思考](#思考) + - 1.2. [参考教程](#参考教程) + - 1.3. [使用SpringBootCLI](#使用springbootcli) + - 1.4. [Profils](#profils) + - 1.4.1. [多种配置文件并切换](#多种配置文件并切换) + - 1.4.1.1. [yml方式](#yml方式) + - 1.4.1.2. [yml和properties结合](#yml和properties结合) + - 1.4.2. [应用配置文件](#应用配置文件) + - 1.5. [Events](#events) + - 1.6. [Logging](#logging) + - 1.7. [Cache](#cache) + - 1.8. [Web模块](#web模块) + - 1.8.1. [Lisener](#lisener) + - 1.8.1.1. [ServletContextListener](#servletcontextlistener) + - 1.8.2. [上传下载文件](#上传下载文件) + - 1.8.3. [错误页面跳转配置](#错误页面跳转配置) + - 1.8.4. [跨域](#跨域) + - 1.8.5. [全局异常处理](#全局异常处理) + - 1.8.6. [Validator](#validator) + - 1.8.7. [Response](#response) + - 1.9. [测试模块](#测试模块) + - 1.10. [运行和部署](#运行和部署) + - 1.10.1. [mvn 运行](#mvn-运行) + - 1.10.2. [编译打包jar/war](#编译打包jarwar) + - 1.10.2.1. [war](#war) + - 1.10.2.2. [jar](#jar) + - 1.10.3. [构建Docker镜像](#构建docker镜像) + - 1.10.3.1. [手动方式](#手动方式) + - 1.10.4. [热部署](#热部署) + - 1.10.5. [运行性能优化](#运行性能优化) + +💠 2024-10-08 16:06:24 **************************************** # SpringBoot -> [首页](https://spring.io/projects/spring-boot#learn) +> [Doc](https://spring.io/projects/spring-boot#learn) -> 一个简化Spring开发的框架,微服务SpringCloud的基础 -> [1.5.14 官方文档](https://docs.spring.io/spring-boot/docs/1.5.14.RELEASE/reference/htmlsingle/) - -- [Github:Springboot](https://github.com/spring-projects/spring-boot/releases) +> [SpringBoot2](./SpringBoot2.md) +> [SpringBoot3](./SpringBoot3.md) ## 思考 - [SpringBoot优缺点](https://www.zhihu.com/question/39483566) @@ -47,80 +56,49 @@ - [SpringBoot启动流程解析](https://www.cnblogs.com/trgl/p/7353782.html)`原理才是王道` - [spring boot应用启动原理分析 ](https://yq.aliyun.com/articles/6056) -- Spring 是单例模式, 全部使用IOC容器进行管理, 那么怎么处理并发呢, - - 答案是 ? 多线程 然后 ThreadLocal 分别存储了各自的数据, 所以才说, 不能在Controller层 放置属性, 使其具有状态, 从而导致并发问题 +- Spring 是单例模式, 全部使用IOC容器进行管理, 那么怎么处理并发下数据共享安全性问题 + - 答案是 ThreadLocal 分别存储了各自的数据, 所以才说, 不能在Controller层 放置属性, 使其具有状态, 从而导致并发问题 - 那么WebSocket服务器, 处理并发会不会有并发问题? - [为什么说 Java 程序员到了必须掌握 Spring Boot 的时候?](http://www.ityouknow.com/springboot/2018/06/12/spring-boo-java-simple.html) -> [Guide](https://spring.io/guides/gs/actuator-service/) - ## 参考教程 - [SpringBoot中文索引](http://springboot.fun/) -- [参考博客: Spring Boot 入门系列](http://www.spring4all.com/article/246) +- [参考: Spring Boot 入门系列](http://www.spring4all.com/article/246) - [Springboot探索](https://juejin.im/post/598dd709f265da3e213f0c57) - [SpringBoot入门](http://blog.csdn.net/jsyxcjw/article/details/46763639) -### 系列 +> [Guide](https://spring.io/guides/gs/actuator-service/) +> [小马哥书籍《Spring Boot 编程思想》示例工程 ](https://github.com/mercyblitz/thinking-in-spring-boot-samples) + +`系列` + - [一系列专栏](https://github.com/guoxiaoxu/guo-projects/tree/master/guns-admin/note) - [个人博客专栏: SpringBoot干货系列](http://tengj.top/tags/Spring-Boot/) - [SpringBoot系列文章](http://www.ityouknow.com/spring-boot) -- [恒宇少年](https://www.jianshu.com/u/092df3f77bca)`大量Boot博客` +- [恒宇少年](https://www.jianshu.com/u/092df3f77bca) -## 开始使用 -> 使用Idea的话就可以直接创建项目 使用别的可以下载zip进行导入 | [官方初始项目构建 下载地址](http://start.spring.io/) +****************** -_目录结构最好如下_ `*Application类要处于所有用了Springboot注解的类的顶级目录, 这样默认才能扫描到` -``` - ├── config/ - ├── controller/ - ├── dao/ - ├── domain/ - ├── GraduateApplication.java - ├── service/ - ├── ServletInitializer.java - └── util/ -``` -## 安装SpringBootCLI +## 使用SpringBootCLI - 使用 SDKMAN 进行安装 - 使用git bash运行 `curl -s get.sdkman.io | bash` - `source "/Users/{yourname}/.sdkman/bin/sdkman-init.sh" `根据实际目录去运行 - spring --version - 官方下载地址 [所有版本](https://repo.spring.io/release/org/springframework/boot/spring-boot-cli/) -## 测试模块 -- [ ] 了解这些测试注解 的作用和使用场景 +## Profils +> [Spring Profiles](https://www.baeldung.com/spring-profiles) -```java - // 依赖于Springboot环境的测试类的必备注解 - @RunWith(SpringRunner.class) - @SpringBootTest - - // 使用内存数据库测试 - @ComponentScan("com.github.kuagncp") // 如果有类没注入需要手动设置扫面 - @RunWith(SpringJUnit4ClassRunner.class) - @DataJpaTest -``` - -- 可以使用MockMvc来测试Controller层的代码 -- 可以使用MockMvc的SpringSecurity支持来测试安全模块 -- 使用 WebIntegraionTest 测试运行中的Web容器 - - 启动嵌入式的Servlet容器来进行测试,下断言 -- 使用随机端口启动服务器 配置local.server.port=0 -- 使用Selenium来测试HTML页面,模拟浏览器的动作,查看系统运行状态 - -*********** -## 配置文件 - -> 配置文件(`application.properties或者yml`)加载顺序 [官方文档说明](https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#boot-features-external-config-application-property-files) ->> 1.当前Jar/War目录下的/config目录 `file:./config/` ->> 2.当前目录 `file:./` ->> 3.classpath 里的/config目录 `classpath:/config/` ->> 4.classpath 根目录 `classpath:/` +> 配置文件(`application.properties或者yml`) 加载顺序 | [官方文档说明](https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/htmlsingle/#boot-features-external-config-application-property-files) +>1. 当前Jar/War目录下的/config目录 `./config/` +>1. 当前目录 `./` +>1. classpath 里的/config目录 `classpath:/config/` +>1. classpath 根目录 `classpath:/` > 自定义配置文件名就要运行时加参数 ->> `java -jar myproject.jar --spring.config.name=myproject` ->> `java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties`
+>1. `java -jar myproject.jar --spring.config.name=myproject` +>1. `java -jar myproject.jar --spring.config.location=classpath:/default.properties,classpath:/override.properties` - [配置文件的使用](http://www.itwendao.com/article/detail/391009.html) - [Spring boot配置文件 application.properties](https://www.tuicool.com/articles/veUjQba) @@ -131,50 +109,54 @@ _目录结构最好如下_ `*Application类要处于所有用了Springboot注解 - [自定义配置文件](http://www.cnblogs.com/java-zhao/p/5542154.html)`将应用配置外置并注入成bean` - [配置文件外置](http://www.cnblogs.com/xiaoqi/p/6955288.html) +> [参考: Spring Boot(五) - 外化配置](https://www.hifreud.com/2017/06/23/spring-boot-05-Externalized-Configuration/) + ### 多种配置文件并切换 #### yml方式 -- 单文件`配置文件 application.yml` -```yml - spring: - profiles: - active: development # 选用开发模式 - --- - spring: - profiles: development - //一系列配置 - --- - spring: - profiles: production - //一系列配置 -``` -- 或者 多文件放 `application-{profile}.yml` - -#### yml和Properties结合 +- 单文件多环境 `配置文件 application.yml` + ```yml + spring: + profiles: + active: development # 选用开发模式 + --- + spring: + profiles: development + # 一系列配置 + --- + spring: + profiles: production + # 一系列配置 + ``` +- 多文件存放不同环境配置 `application-{profile}.yml` + +#### yml和properties结合 - 格式:`application-{profile}.properties` - 将上面的开发部分,发行部分的配置创建两个配置文件 `application-dev.properties` 和 `application-prod.properties` - 在主配置文件`application.yml`中指明 -```yml - spring: - profiles: - active: dev或者是prod -``` + ```yml + spring: + profiles: + active: dev # dev或prod,也可以 common,dev 启用两份 + ``` + ### 应用配置文件 -> 依赖于 `org.springframework.boot:spring-boot-configuration-processor` +> 依赖于 `org.springframework.boot:spring-boot-configuration-processor` + 配置对应的实体类 ```java -@Data -@Component -@ConfigurationProperties(prefix = "graduate.main") -public class MainConfig { - private String delimiter; -} + @Data + @Component + @ConfigurationProperties(prefix = "graduate.main") + public class MainConfig { + private String delimiter; + } ``` 应用配置类 ```java -@Configuration -@EnableConfigurationProperties(MainConfig.class) -public class AutoCustomConfig { -} + @Configuration + @EnableConfigurationProperties(MainConfig.class) + public class AutoCustomConfig { + } ``` application.yml ```yml @@ -182,7 +164,67 @@ graduate: main: delimiter: , ``` -************ +******************** + +## Events +> [Note: Spring Events](/Java/Spring/Spring.md#Events) + +配置成异步并使用线程池 +```java + @Configuration + public class AsynchronousSpringEventsConfig { + @Bean(name = "applicationEventMulticaster") + public ApplicationEventMulticaster simpleApplicationEventMulticaster() { + SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); + + SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(); + eventMulticaster.setTaskExecutor(taskExecutor); + return eventMulticaster; + } + } +``` + +*************** + +## Logging +默认可以通过 application.properties 配置框架的日志,以及应用具体到包和类的日志等级,日志文件等等 + +> [参考: Spring boot——logback 基础使用篇(一)](https://www.cnblogs.com/lixuwu/p/5804793.html) +> [参考: springboot use logback](https://springframework.guru/using-logback-spring-boot/)`能根据Profile配置,还能写if` +> [spring boot logging](https://www.baeldung.com/spring-boot-logging) + +使用logback时需要配置 logback.xml 或者 logback-spring.xml 建议使用后者 + +- 思考: 能否不同的包使用不同的pattern [pattern](https://stackoverflow.com/questions/30571319/spring-boot-logging-pattern) + - 但是不利于后续中间件做日志解析 +- 配置 pattern 并引用 MDC `logging.pattern.level=%X{mdcData}%5p` + +************************ + +## Cache +> [Caching Data with Spring](https://spring.io/guides/gs/caching) | [SpringBoot: Caching](https://docs.spring.io/spring-boot/reference/io/caching.html) + +> [Spring Boot Cache使用与整合](https://www.cnblogs.com/morganlin/p/12000223.html) + +- `@Cacheable`:表示该方法支持缓存。当调用被注解的方法时,如果对应的键已经存在缓存,则不再执行方法体,而从缓存中直接返回。当方法返回null时,将不进行缓存操作。 + - cacheNames/value:缓存组件的名字,即cacheManager中缓存的名称。 + - key:缓存数据时使用的key。默认使用方法参数值,也可以使用SpEL表达式进行编写。 + - 调用静态方法`获取用户id`拼接进SpEL,从而实现用户缓存隔离 + - keyGenerator: *和key二选一使用* + - 可以默认构造一个自定义的生成器,从线程上下文获取用户id拼接进去实现用户缓存隔离 + - cacheManager:指定使用的缓存管理器。 + - condition:在方法执行开始前检查,在符合condition的情况下,进行缓存。 + - unless:在方法执行完成后检查,在符合unless的情况下,不进行缓存。 + - sync:是否使用同步模式。若使用同步模式,在多个线程同时对一个key进行load时,其他线程将被阻塞。Spring 4.1引入,**规避缓存击穿** +- `@CachePut`:表示执行该方法后,其值将作为最新结果更新到缓存中。 +- `@CacheEvict`:表示执行该方法后,将触发清除同名value和key的缓存。 +- `@Caching`:可组合前三个注解 + +注意缓存的本质是将内存对象序列化到三方缓存(JVM,Redis,文件),使用时再反序列化, 所以需要缓存的接口的参数和响应值都需要实现Serializable接口 + +************************ + + ## Web模块 ### Lisener #### ServletContextListener @@ -205,17 +247,18 @@ graduate: - [Springboot上传文件](http://www.cnblogs.com/studyCenter/p/6665171.html) - 上传文件有大小限制,使用如下方法进行配置 [参考博客](http://makaidong.com/studyDetail/11882_45833.html) -```java -@Bean -public MultipartConfigElement multipartConfigElement() { - MultipartConfigFactory factory = new MultipartConfigFactory(); - //单个文件最大 - factory.setMaxFileSize("80MB"); //KB,MB - // 设置总上传数据总大小 - factory.setMaxRequestSize("102400KB"); - return factory.createMultipartConfig(); -} -``` + ```java + @Bean + public MultipartConfigElement multipartConfigElement() { + MultipartConfigFactory factory = new MultipartConfigFactory(); + //单个文件最大 + factory.setMaxFileSize("80MB"); //KB,MB + // 设置总上传数据总大小 + factory.setMaxRequestSize("102400KB"); + return factory.createMultipartConfig(); + } + ``` + ### 错误页面跳转配置 ```java @Configuration @@ -252,94 +295,62 @@ public class CorsConfig { } } ``` -## 全局异常处理 -1. 新建类, 加上注解 ControllerAdvice -2. 方法上使用注解 `ExceptionHandler(Exception.class)` 处理所有异常 + +### 全局异常处理 +1. 新建类 并加类注解 ControllerAdvice 或 RestControllerAdvice(省去方法ResponseBody) +2. 新建方法上添加注解 `ExceptionHandler(Exception.class)` 处理对应异常类型 3. 然后返回值的写法和普通Controller一样, 返回JSON就`ResponseBody` -******************* -## HTTPS的配置 -> [参考博客](https://www.drissamri.be/blog/java/enable-https-in-spring-boot/) +### Validator +> [Validation with Spring Boot](https://reflectoring.io/bean-validation-with-spring-boot/) -- 签发证书: -```sh -keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 -``` -```yml -server: - context-path: /myth - ssl: - key-store: classpath:keystore.p12 - key-store-password: demo1429336 - key-store-type: PKCS12 - key-alias: tomcat - port: 8888 - session: - timeout: 3000 +> [SpringBoot接口 - 如何对参数进行校验](https://pdai.tech/md/spring/springboot/springboot-x-interface-param.html) + +```xml + + org.springframework.boot + spring-boot-starter-validation + ``` -`任意的一个@Configuration注解类里添加` + +### Response +> 全局切面增强 + +1. 自定义一个切面 `implements ResponseBodyAdvice` + 1. 重写 supports 和 beforeBodyWrite 并依据 后者的 body和returnType参数自行封装成统一结构 + 1. 降低Mvc接口层 `Result>` 等结构,简化为 `List`, 异常返回可以用全局异常处理成Result结构 + +[Graceful Response](https://github.com/feiniaojin/graceful-response) + +************************ +## 测试模块 ```java -@Bean -public TomcatEmbeddedServletContainerFactory servletContainerFactory() { - TomcatEmbeddedServletContainerFactory factory = - new TomcatEmbeddedServletContainerFactory() { - @Override - protected void postProcessContext(Context context) { - //SecurityConstraint必须存在,可以通过其为不同的URL设置不同的重定向策略。 - SecurityConstraint securityConstraint = new SecurityConstraint(); - securityConstraint.setUserConstraint("CONFIDENTIAL"); - SecurityCollection collection = new SecurityCollection(); - collection.addPattern("/*"); - securityConstraint.addCollection(collection); - context.addConstraint(securityConstraint); - } - }; - factory.addAdditionalTomcatConnectors(createHttpConnector()); - return factory; -} + // 依赖于Springboot环境的测试类的必备注解 + @RunWith(SpringRunner.class) + @SpringBootTest -private Connector createHttpConnector() { - Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); - connector.setScheme("http"); - connector.setSecure(false); - connector.setPort(8887);//http端口(这是要新增加的一个端口) - connector.setRedirectPort(8888);// https 端口配置文件中tomcat启动的默认端口 - return connector; -} + // 使用内存数据库测试 + @ComponentScan("com.github.kuagncp") // 如果有类没注入需要手动设置扫面 + @RunWith(SpringJUnit4ClassRunner.class) + @DataJpaTest ``` -- 另一种方式 [参考博客](http://www.cnblogs.com/xinzhao/p/4950689.html) -`方式不一样,没有成功` -```sh - ############ 证书颁发机构 - # CA机构私钥 - openssl genrsa -out ca.key 2048 - # CA证书 - openssl req -x509 -new -key ca.key -out ca.crt - ############ 服务端 - # 生成服务端私钥 - openssl genrsa -out server.key 2048 - # 生成服务端证书请求文件 - openssl req -new -key server.key -out server.csr - # 使用CA证书生成服务端证书 关于sha256,默认使用的是sha1,在新版本的chrome中会被认为是不安全的,因为使用了过时的加密算法。 - openssl x509 -req -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out server.crt - # 打包服务端的资料为pkcs12格式(非必要,只是换一种格式存储上一步生成的证书) 生成过程中,需要创建访问密码,请记录下来。 - openssl pkcs12 -export -in server.crt -inkey server.key -out server.pkcs12 - # 生成服务端的keystore(.jks文件, 非必要,Java程序通常使用该格式的证书) 生成过程中,需要创建访问密码,请记录下来。 - keytool -importkeystore -srckeystore server.pkcs12 -destkeystore server.jks -srcstoretype pkcs12 - # 把ca证书放到keystore中(非必要) - keytool -importcert -keystore server.jks -file ca.crt -``` +- 可以使用MockMvc来测试Controller层的代码 +- 可以使用MockMvc的SpringSecurity支持来测试安全模块 +- 使用 WebIntegraionTest 测试运行中的Web容器 + - 启动嵌入式的Servlet容器来进行测试,下断言 +- 使用随机端口启动服务器 配置local.server.port=0 +- 使用Selenium来测试HTML页面,模拟浏览器的动作,查看系统运行状态 + +************************ -**************** -## 线程池 -- [参考博客](https://hacpai.com/article/1501152977477?p=1&m=0) -- 多线程以及异常处理 [参考博客](http://www.liuhaihua.cn/archives/496733.html) - - 因为多线程的特性,所以异常只能在子线程中处理不能抛出到主线程里,但是 Spring实现的线程池可以返回一个异常信息对象 +## 运行和部署 -*************************************** -## 项目部署 -### 生成指定文件 +### mvn 运行 +- [Spring Boot Maven Plugin](https://docs.spring.io/spring-boot/docs/2.1.9.RELEASE/maven-plugin/run-mojo.html) +- 例如开启远程调试 `mvn spring-boot:run -Dspring-boot.run.jvmArguments="-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8000"` + +### 编译打包jar/war #### war - 部署为war必须的类,一般在创建项目时选war就会自动生成,选jar就要手动添加 ```java @@ -354,23 +365,35 @@ private Connector createHttpConnector() { - gradle: `gradle war` 然后 `gradle bootRepackage` 即可 #### jar -- 没有特殊的配置,打包即用 +- 没有特殊的配置,打包即用 `java -jar app.jar` - maven: `mvn package` 即可生成可执行的jar - gradle:`gradle jar` 然后 `gradle bootRepackage` 也生成可执行jar -### 构建docker镜像 -> 方便监控应用状态,cpu 内存 流量, [官方文档](https://spring.io/guides/gs/spring-boot-docker/) +************************ + +二进制执行的Jar +> [Installing Spring Boot Applications](https://docs.spring.io/spring-boot/docs/current/reference/html/deployment.html#deployment.installing) +> [launch.script](https://github.com/spring-projects/spring-boot/blob/v3.0.6/spring-boot-project/spring-boot-tools/spring-boot-loader-tools/src/main/resources/org/springframework/boot/loader/tools/launch.script#start-of-content)`启动脚本` + +### 构建Docker镜像 +> [Official Doc: spring boot docker](https://spring.io/guides/gs/spring-boot-docker/) #### 手动方式 - 先构建得到war或jar,然后根据dockerfile构建一个镜像 ```Dockerfile -FROM frolvlad/alpine-oraclejdk8:slim -ADD weixin-1.0.0.war app.war -ENTRYPOINT ["java","-jar","/app.war"] + FROM frolvlad/alpine-oraclejdk8:slim + ADD weixin-1.0.0.war app.war + ENTRYPOINT ["java","-jar","/app.war"] ``` -#### gradle结合docker +### 热部署 +> [参考: SpringBoot热部署](https://nilzzzz.github.io/2017/11/SpringBoot1/) +> IDE调试时: getBean() 报错cannot be cast to class is in unnamed module of loader 'app' +[Spring Boot DevTools - RestartClassLoader problem](https://stackoverflow.com/questions/69990029/spring-boot-devtools-restartclassloader-problem) +spring boot dev tools 实现的 RestarterClassLoader类加载器 和 AppClassLoader 共存,会有一些Bean在Restart类加载器里 +方法: `-Dspring.devtools.restart.enabled=false` 禁用或者移除依赖 + +### 运行性能优化 +> [Runtime efficiency with Spring (today and tomorrow)](https://spring.io/blog/2023/10/16/runtime-efficiency-with-spring) -### 热部署 -> [参考博客: SpringBoot热部署](https://nilzzzz.github.io/2017/11/SpringBoot1/) diff --git a/Java/Spring/SpringBoot2.md b/Java/Spring/SpringBoot2.md index 6713347..9fd038f 100644 --- a/Java/Spring/SpringBoot2.md +++ b/Java/Spring/SpringBoot2.md @@ -1,20 +1,29 @@ -`目录 start` - -- [SringBoot2](#sringboot2) - - [从1迁移到2](#从1迁移到2) - - [新特性](#新特性) - - [Web模块](#web模块) - - [Web容器](#web容器) - - [Tomcat](#tomcat) - - [Jetty](#jetty) - - [跨域](#跨域) - - [SpringBoot上下文事件监听](#springboot上下文事件监听) - - [数据库模块](#数据库模块) - - [Relation Database](#relation-database) - - [多数据源](#多数据源) - - [No Relation Database](#no-relation-database) - -`目录 end` |_2018-08-20_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: SpringBoot2 新特性 +date: 2018-12-21 10:49:53 +tags: + - SpringBoot +categories: + - Java +--- + +💠 + +- 1. [SringBoot2](#sringboot2) + - 1.1. [从1迁移到2](#从1迁移到2) + - 1.2. [新特性](#新特性) + - 1.3. [Web模块](#web模块) + - 1.3.1. [Web容器](#web容器) + - 1.3.1.1. [Tomcat](#tomcat) + - 1.3.1.2. [Jetty](#jetty) + - 1.3.2. [跨域](#跨域) + - 1.3.3. [SpringBoot上下文事件监听](#springboot上下文事件监听) + - 1.4. [数据库模块](#数据库模块) + - 1.4.1. [Relation Database](#relation-database) + - 1.4.1.1. [多数据源](#多数据源) + - 1.4.2. [No Relation Database](#no-relation-database) + +💠 2024-05-04 22:39:50 **************************************** # SringBoot2 [官方文档](https://docs.spring.io/spring-boot/docs/2.0.3.RELEASE/reference/htmlsingle/) @@ -31,7 +40,7 @@ ## 新特性 > [Spring Boot 2.0系列文章(二):Spring Boot 2.0 新特性详解 ](http://www.54tianzhisheng.cn/2018/03/06/SpringBoot2-new-features/) -> [参考博客: Spring Boot 2.0 新特性和发展方向 ](https://mp.weixin.qq.com/s/EWmuzsgHueHcSB0WH-3AQw) +> [参考: Spring Boot 2.0 新特性和发展方向 ](https://mp.weixin.qq.com/s/EWmuzsgHueHcSB0WH-3AQw) ## Web模块 ### Web容器 @@ -61,25 +70,25 @@ } ``` ```java - @Configuration -public class Listener implements ApplicationListener { - @Override - public void onApplicationEvent(ApplicationEvent event) { - // 在这里可以监听到Spring Boot的生命周期 - if (event instanceof ContextRefreshedEvent) { - System.out.println("应用刷新"); - } - if (event instanceof ContextStartedEvent) { - System.out.println("应用启动"); - - } else if (event instanceof ContextStoppedEvent) { - System.out.println("应用停止"); - - } else if (event instanceof ContextClosedEvent) { - System.out.println("应用关闭"); + @Configuration + public class Listener implements ApplicationListener { + @Override + public void onApplicationEvent(ApplicationEvent event) { + // 在这里可以监听到Spring Boot的生命周期 + if (event instanceof ContextRefreshedEvent) { + System.out.println("应用刷新"); + } + if (event instanceof ContextStartedEvent) { + System.out.println("应用启动"); + + } else if (event instanceof ContextStoppedEvent) { + System.out.println("应用停止"); + + } else if (event instanceof ContextClosedEvent) { + System.out.println("应用关闭"); + } } } -} ``` @@ -92,6 +101,6 @@ public class Listener implements ApplicationListener { ### Relation Database #### 多数据源 -- [参考博客: Spring Boot 2.0 多数据源编程 原](https://my.oschina.net/chinesedragon/blog/1647846) | [源码](https://gitee.com/shupengluo/SpringBoot2.0-MultiDataSource) +- [参考: Spring Boot 2.0 多数据源编程 原](https://my.oschina.net/chinesedragon/blog/1647846) | [源码](https://gitee.com/shupengluo/SpringBoot2.0-MultiDataSource) -### No Relation Database \ No newline at end of file +### No Relation Database diff --git a/Java/Spring/SpringBoot3.md b/Java/Spring/SpringBoot3.md new file mode 100644 index 0000000..db32d86 --- /dev/null +++ b/Java/Spring/SpringBoot3.md @@ -0,0 +1,4 @@ +# SpringBoot 3 + +1. 包名变更 javax -> jakarta +1. httpclient5, hibernate and spring security \ No newline at end of file diff --git a/Java/Spring/SpringMVC.md b/Java/Spring/SpringMVC.md index cdd59ed..1e4405c 100644 --- a/Java/Spring/SpringMVC.md +++ b/Java/Spring/SpringMVC.md @@ -1,30 +1,44 @@ -`目录 start` - -- [SpringMVC](#springmvc) - - [MVC思想](#mvc思想) - - [原理](#原理) - - [API](#api) - - [SpringBoot中配置](#springboot中配置) - - [传统项目配置完整流程](#传统项目配置完整流程) - - [配置依赖](#配置依赖) - - [Maven](#maven) - - [Gradle](#gradle) - - [web.xml](#webxml) - - [ApplicationContext.xml](#applicationcontextxml) - - [全局异常处理](#全局异常处理) - - [自定义错误页面](#自定义错误页面) - - [中文编码问题](#中文编码问题) - - [创建Controller](#创建controller) - - [使用](#使用) - - [自定义拦截器](#自定义拦截器) - - [Q&A](#q&a) +--- +title: SpringMVC +date: 2018-12-21 10:53:11 +tags: + - Spring +categories: + - Java +--- -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [SpringMVC](#springmvc) + - 1.1. [MVC思想](#mvc思想) + - 1.1.1. [原理](#原理) + - 1.2. [API](#api) +- 2. [传统项目配置完整流程](#传统项目配置完整流程) + - 2.1. [配置依赖](#配置依赖) + - 2.1.1. [Maven](#maven) + - 2.1.2. [Gradle](#gradle) + - 2.2. [web.xml](#webxml) + - 2.3. [ApplicationContext.xml](#applicationcontextxml) + - 2.3.1. [全局异常处理](#全局异常处理) + - 2.3.2. [自定义错误页面](#自定义错误页面) + - 2.3.3. [中文编码问题](#中文编码问题) + - 2.4. [创建Controller](#创建controller) +- 3. [使用](#使用) + - 3.1. [配置类型转换](#配置类型转换) + - 3.2. [拦截器](#拦截器) + - 3.2.1. [拦截器机制](#拦截器机制) + - 3.2.2. [自定义拦截器](#自定义拦截器) +- 4. [Tips](#tips) + +💠 2024-03-30 11:43:28 **************************************** # SpringMVC > [Spring MVC 4.2.4.RELEASE 中文文档](https://legacy.gitbook.com/book/linesh/spring-mvc-documentation-linesh-translation/details) + +> [springmvc + mybatis](https://github.com/brianway/springmvc-mybatis-learning) + ## MVC思想 > [参考博客](http://blog.csdn.net/besley/article/details/8479943) ![图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/mvc.png) @@ -32,67 +46,65 @@ ### 原理 > 统一使用一个Servlet 进行请求的收发, 通过配置的URL对应的方法, 进行调用, 然后返回视图解析器进行渲染 +- 核心类是DispatchServlet 由它来接收各种请求 + - 实现路由转发 + - 全局异常处理 +- 发出request请求,到controller解析器,得到Model和view等的名字 +- 发送到controller执行,返回view名字 +- 发送到视图解析器 +- 执行视图返回到dispatchServlet + ************************ ## API > [简洁的API设计](http://www.csdn.net/article/2013-05-02/2815115-stop-designing-fragile-web-api) -*********** - -## SpringBoot中配置 -> 如果引入了别的模板引擎就不需要配置解析器,不然就要配 - -- 没有用模板引擎themleaf的依赖,配置前后缀并没有用, 这是什么原因??? classpath:/templates/ .html - - 单纯的想写个前后端分离不行? -> [SpringBoot 较完善的入门博客](https://www.tianmaying.com/tutorial/spring-mvc-quickstart) - *********************** -## 传统项目配置完整流程 +# 传统项目配置完整流程 > 也就是Maven的Web结构,甚至是Eclipse那样的DynamicWeb项目结构, [参考 博客](https://www.cnblogs.com/Sinte-Beuve/p/5730553.html) -### 配置依赖 - -#### Maven +## 配置依赖 +### Maven ```xml - - 4.3.9.RELEASE - -...... - - org.springframework - spring-web - ${spring.version} - - - org.springframework - spring-webmvc - ${spring.version} - - - - javax.servlet - javax.servlet-api - 3.1.0 - - - javax.servlet.jsp - jsp-api - 2.2 - - - javax.servlet - jstl - 1.2 - - + + 4.3.9.RELEASE + + ...... + + org.springframework + spring-web + ${spring.version} + + + org.springframework + spring-webmvc + ${spring.version} + + + + javax.servlet + javax.servlet-api + 3.1.0 + + + javax.servlet.jsp + jsp-api + 2.2 + + + javax.servlet + jstl + 1.2 + + ``` -#### Gradle +### Gradle ```groovy -compile('org.springframework:spring-web:4.3.9.RELEASE') -compile('org.springframework:spring-webmvc:4.3.9.RELEASE') + compile('org.springframework:spring-web:4.3.9.RELEASE') + compile('org.springframework:spring-webmvc:4.3.9.RELEASE') ``` -### web.xml +## web.xml ```xml @@ -114,53 +126,53 @@ compile('org.springframework:spring-webmvc:4.3.9.RELEASE') ``` -### ApplicationContext.xml +## ApplicationContext.xml ```xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ``` -#### 全局异常处理 +### 全局异常处理 ```java public class ExceptionHandler implements HandlerExceptionResolver { @Override @@ -182,12 +194,23 @@ public class ExceptionHandler implements HandlerExceptionResolver { return new ModelAndView("error", model); } } + +// 或者是在 Controller 层直接处理 + @ControllerAdvice + public class ExceptionHandle{ + @EXceptionHandler({Exception.class}) + public ModelAndView dealException(Exception e){ + ModelAndView view = new ModelAndView("exception"; + Exception e = new Exception("错误信息"); + view.addObject("",e.getMessage()); + return view; + } ``` > 但如果是前后端分离的话, 就只能统一处理异常然后然后对应的错误码和提示信息了 > [参考博客](http://www.cnblogs.com/exmyth/p/5601288.html) > [ResponseBody方案](https://blog.csdn.net/xin917480852/article/details/78023911) -#### 自定义错误页面 +### 自定义错误页面 ```java // 自定义错误页面 需要放在静态资源下面 @Bean @@ -200,7 +223,7 @@ public class ExceptionHandler implements HandlerExceptionResolver { }); } ``` -#### 中文编码问题 +### 中文编码问题 > [参考博客](http://www.cnblogs.com/dyllove98/p/3180158.html) `但是奇怪的是某些方法用第二种正常,有些还是要用第一种` 1. 单个方法:`@GetMapping(value = "/target/all", produces = "application/json; charset=utf-8")` 2. 或者整个应用 注意:`` 只能有一个,要将上面的覆盖掉 @@ -221,7 +244,8 @@ public class ExceptionHandler implements HandlerExceptionResolver { ``` -### 创建Controller +## 创建Controller + 包 com.test.controller 下创建一个类 ```java @RestController @@ -233,12 +257,53 @@ public class Hi { } } ``` +> 使用上 ResponseEntity 让响应结果规范 +```java + @RequestMapping("/handle") + public ResponseEntity handle() { + URI location = ...; + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.setLocation(location); + responseHeaders.set("MyResponseHeader", "MyValue"); + return new ResponseEntity("Hello World", responseHeaders, HttpStatus.CREATED); + } +``` ************************ -## 使用 +# 使用 > 在Springboot框架中,static templates 文件夹下分别代表了tomcat管理的静态文件和MVC负责跳转的HTML文件或JSP文件 > 在static中对于路径的使用一定要带上应用路径,而在templates中就只要写相对路径即可 +## 配置类型转换 + +```xml + + + + + + + + + +``` +## 拦截器 +### 拦截器机制 +implements HandleInterceptor 有三个方法 + +preHandle 返回true就继续往后,false就被拦截 +PostHandle 在渲染视图之前, +afterCompletion 渲染视图之后调用,释放资源 + +```xml + + + + + + +``` ### 自定义拦截器 - [相关博客](http://www.jianshu.com/p/f14ed6ca4e56)|[相关博客](http://blog.csdn.net/catoop/article/details/50501696) @@ -279,13 +344,10 @@ public class WebMvcConfig extends WebMvcConfigurerAdapter{ } ``` - -### Q&A +# Tips > URL 中带了 jsessionid 参数,导致页面各种问题 - 一种原因:禁用cookie导致的 - 最终解决: chrome中在设置里清除localhost的所有cookie和缓存 - [解决问题参考博客](https://yq.aliyun.com/articles/101169) - [jsessionid的作用](http://sxsoft.blog.163.com/blog/static/190412229200911103116773) - -> ModelAndView中的addObject 的值能用js获取到么 diff --git a/Java/Spring/SpringMessageQue.md b/Java/Spring/SpringMessageQue.md deleted file mode 100644 index abeabe5..0000000 --- a/Java/Spring/SpringMessageQue.md +++ /dev/null @@ -1,14 +0,0 @@ -`目录 start` - -- [消息队列](#消息队列) - - [简单的Websocket方式](#简单的websocket方式) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 消息队列 - -## 简单的Websocket方式 -- [Springboot中使用Websocket进行消息推送](https://gitee.com/kcp1104/codes/14ipgcbjyenxqu6tf9d0543) -- [SpringBoot 使用WebSocket](https://blog.csdn.net/kangkanglou/article/details/78253747) -- [SpringBoot系列 - 集成WebSocket实时通信](https://www.xncoding.com/2017/07/15/spring/sb-websocket.html) - diff --git a/Java/Spring/SpringSecurity.md b/Java/Spring/SpringSecurity.md index 81e7279..b6be4ba 100644 --- a/Java/Spring/SpringSecurity.md +++ b/Java/Spring/SpringSecurity.md @@ -1,43 +1,53 @@ -`目录 start` - -- [SpringSecurity](#springsecurity) - - [配置](#配置) - - [配置静态口令使用](#配置静态口令使用) -- [使用该配置即可自定义用户名密码](#使用该配置即可自定义用户名密码) - - [SpringBoot的集成Demo](#springboot的集成demo) - - [创建单用户单角色的安全控制](#创建单用户单角色的安全控制) - - [多用户多角色的实现思路](#多用户多角色的实现思路) - - [每个身份都使用一个登录实体类](#每个身份都使用一个登录实体类) - - [另一种思路:](#另一种思路) - - [JWT](#jwt) - - [跨域问题](#跨域问题) - - [Oauth](#oauth) - - [实现细节](#实现细节) - - [关于注解的几种使用方式](#关于注解的几种使用方式) - - [@Secured](#@secured) - - [@RolesAllowed](#@rolesallowed) - - [SpringSecurity3.0 开始提供了 SpEL表达式](#springsecurity30-开始提供了-spel表达式) - - [保护方法应用](#保护方法应用) - - [社交登录](#社交登录) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: SpringSecurity +date: 2018-12-21 10:53:23 +tags: + - Spring +categories: + - Java +--- + +💠 + +- 1. [SpringSecurity](#springsecurity) + - 1.1. [配置](#配置) + - 1.1.1. [配置静态口令使用](#配置静态口令使用) + - 1.2. [SpringBoot的集成Demo](#springboot的集成demo) + - 1.2.1. [创建单用户单角色的安全控制](#创建单用户单角色的安全控制) + - 1.2.2. [多用户多角色的实现思路](#多用户多角色的实现思路) + - 1.2.2.1. [每个身份都使用一个登录实体类](#每个身份都使用一个登录实体类) + - 1.2.2.2. [另一种思路:](#另一种思路) + - 1.3. [JWT 和 Spring](#jwt-和-spring) + - 1.3.1. [跨域问题](#跨域问题) + - 1.4. [Oauth](#oauth) + - 1.5. [实现细节](#实现细节) + - 1.5.1. [关于注解的几种使用方式](#关于注解的几种使用方式) + - 1.5.1.1. [@Secured](#@secured) + - 1.5.1.2. [@RolesAllowed](#@rolesallowed) + - 1.5.1.3. [SpringSecurity3.0 开始提供了 SpEL表达式](#springsecurity30-开始提供了-spel表达式) + - 1.5.2. [保护方法应用](#保护方法应用) + - 1.6. [社交登录](#社交登录) + +💠 2024-07-24 17:28:31 **************************************** # SpringSecurity > [Spring Security 5.0.6 doc](https://docs.spring.io/spring-security/site/docs/5.0.6.RELEASE/reference/htmlsingle) -[参考博客: Spring Security 入门系列](http://www.spring4all.com/article/428) +[参考: Spring Security 入门系列](http://www.spring4all.com/article/428) ## 配置 ### 配置静态口令使用 当添加了Security依赖之后, 只会生成一个默认的随机密码, 如下简单配置: ```ini -# 使用该配置即可自定义用户名密码 -spring.security.user.name=admin -spring.security.user.password=secret + # 使用该配置即可自定义用户名密码 + spring.security.user.name=admin + spring.security.user.password=secret ``` 如果要用 curl 访问则是 `curl -i -u admin:secret http://tomcat.kcp/hi` + > 注意 : 前提是要关闭 crsf 校验 + ```java @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @@ -48,7 +58,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { } ``` - ****************************************************************** ## SpringBoot的集成Demo ### 创建单用户单角色的安全控制 @@ -86,7 +95,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { >- 其实这个安全框架使用的是角色控制,而不是权限控制,目前的了解,达不到Oracle那样的权限加角色的精细化控制 ********* -## JWT + +## JWT 和 Spring > [JWT相关原理](/Skills/Base/WebSecurity.md#jwt) | [Github SpringBoot2使用Security整合Jwt案例项目](https://github.com/Kuangcp/SpringBoot2-Security-Jwt) - [个人代码片段](https://gitee.com/kcp1104/codes/kw31qf40iz9p8mt2x7bcd49) | @@ -100,7 +110,9 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { > [WEB应用前后端分离实践 关键词:rest架构+跨域访问+JWT校验](https://bbs.csdn.net/topics/392006333) [csrf](https://docs.spring.io/spring-security/site/docs/5.0.6.RELEASE/reference/htmlsingle/#csrf) + ******************* + ## Oauth > [oauth](https://github.com/spring-projects/spring-security-oauth) diff --git a/Java/Spring/SpringTest.md b/Java/Spring/SpringTest.md index a607d91..668e751 100644 --- a/Java/Spring/SpringTest.md +++ b/Java/Spring/SpringTest.md @@ -1,14 +1,23 @@ -`目录 start` - -- [Spring Test](#spring-test) - - [注解](#注解) +--- +title: SpringTest +date: 2018-12-21 10:53:35 +tags: + - Spring +categories: + - Java +--- -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [Spring Test](#spring-test) + 1. [注解](#注解) + +**目录 end**|_2020-04-27 23:42_| **************************************** # Spring Test -> [参考博客: Getting Started with Mockito @Mock, @Spy, @Captor and @InjectMocks](https://www.baeldung.com/mockito-annotations?utm_source=tuicool&utm_medium=referral) -> [参考博客: Mockito – Using Spies](https://www.baeldung.com/mockito-spy) +> [参考: Getting Started with Mockito @Mock, @Spy, @Captor and @InjectMocks](https://www.baeldung.com/mockito-annotations?utm_source=tuicool&utm_medium=referral) +> [参考: Mockito – Using Spies](https://www.baeldung.com/mockito-spy) ## 注解 - [ ] 学习 diff --git a/Java/Spring/SpringWebFlux.md b/Java/Spring/SpringWebFlux.md deleted file mode 100644 index 94fadf5..0000000 --- a/Java/Spring/SpringWebFlux.md +++ /dev/null @@ -1,10 +0,0 @@ -`目录 start` - -- [WebFlux](#webflux) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# WebFlux - -> [参考博客: 译:尝试使用Spring WebFlux](http://www.spring4all.com/article/1167) - diff --git a/Java/Spring/SpringbootDatabase.md b/Java/Spring/SpringbootDatabase.md index 3617d4e..f45d583 100644 --- a/Java/Spring/SpringbootDatabase.md +++ b/Java/Spring/SpringbootDatabase.md @@ -1,74 +1,141 @@ -`目录 start` - -- [数据库模块](#数据库模块) - - [Relational Database](#relational-database) - - [多数据源配置](#多数据源配置) - - [连接池](#连接池) - - [c3p0](#c3p0) - - [druid](#druid) - - [HikariPool](#hikaripool) - - [JPA](#jpa) - - [Mybatis](#mybatis) - - [自定义查询](#自定义查询) - - [HQL](#hql) - - [原生SQL](#原生sql) - - [Mysql](#mysql) - - [映射关系](#映射关系) - - [一对一](#一对一) - - [一对多](#一对多) - - [多对多](#多对多) - - [Restful设计](#restful设计) - - [【特别注意】](#特别注意) - - [Jpa数据分页](#jpa数据分页) - - [数据库上的事务支持](#数据库上的事务支持) - - [Non Relational database](#non-relational-database) - - [JPA](#jpa) - - [Redis的简单使用](#redis的简单使用) - - [关于StringRedisTemplate的方法使用](#关于stringredistemplate的方法使用) - - [消息订阅和发布](#消息订阅和发布) - -`目录 end` |_2018-09-05_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: SpringBoot数据库相关 +date: 2018-12-21 10:50:38 +tags: + - SpringBoot +categories: + - Java +--- + +💠 + +- 1. [数据库模块](#数据库模块) + - 1.1. [多数据源配置](#多数据源配置) + - 1.2. [连接池](#连接池) + - 1.2.1. [c3p0](#c3p0) + - 1.2.2. [druid](#druid) + - 1.2.3. [HikariCP](#hikaricp) + - 1.3. [Relational Database](#relational-database) + - 1.3.1. [JPA](#jpa) + - 1.3.1.1. [Configuration](#configuration) + - 1.3.1.2. [Jpa数据分页](#jpa数据分页) + - 1.3.1.3. [原生SQL](#原生sql) + - 1.3.1.4. [Mysql](#mysql) + - 1.3.1.5. [映射关系](#映射关系) + - 1.3.1.5.1. [一对一](#一对一) + - 1.3.1.5.2. [一对多](#一对多) + - 1.3.1.5.3. [多对多](#多对多) + - 1.3.2. [Mybatis](#mybatis) + - 1.4. [Non Relational database](#non-relational-database) + - 1.4.1. [Redis](#redis) + - 1.4.1.1. [消息订阅和发布](#消息订阅和发布) + +💠 2024-09-12 16:01:31 **************************************** # 数据库模块 -> 主要是采用的JPA,极大的缩减了代码量,但是要注意不要过度依赖框架,丧失了基本的能力 +> [Spring Data](https://spring.io/projects/spring-data) -## Relational Database -### 多数据源配置 -> 为什么要有多数据源? 思考 +## 多数据源配置 +> 为什么要有多数据源? +- 业务发展和划分拆解导致业务库被拆分(例如用户库,订单库),或者异构数据源支撑(订单库MySQL,订单快照库ES) > [Spring Boot多数据源配置与使用](https://www.jianshu.com/p/34730e595a8c) -### 连接池 -#### c3p0 -- [参考博客](http://www.cnblogs.com/520playboy/p/7526252.html) +## 连接池 +### c3p0 +> [Github](https://github.com/swaldman/c3p0) +- [参考博客: springboot 使用c3p0数据库连接池](http://www.cnblogs.com/520playboy/p/7526252.html) -#### druid +### druid - [druid连接池的配置](http://makaidong.com/L_Sail/1/40930_11573921.html) -#### HikariPool -> 性能最好的数据库连接池 +> [druid连接池引起的线程blocked](https://segmentfault.com/a/1190000041500544)`驱动改名引起的连锁反应` + +常见配置项 +- com.alibaba.druid.pool.DruidDataSource#configFromPropety 环境变量 可配置项 + +```yml + # 初始连接数 + initialSize: 6 + # 最小连接池数量 + minIdle: 6 + # 最大连接池数量 + maxActive: 200 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 驱逐连接:配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 驱逐连接:配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 200000 + # 驱逐连接:配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 280000 + # 心跳保活 + keepAlive: true + # 心跳保活间隔,keepAlive开启才生效 + keepAliveBetweenTimeMillis: 40000 + # 配置检测连接是否有效 创建连接和心跳保活时执行 + validationQuery: SELECT 1 + # 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis ,执行validationQuery检测连接是否有效 + testWhileIdle: true + # 申请连接时执行validationQuery检测连接是否有效 性能影响明显 + testOnBorrow: false + # 归还连接时执行validationQuery检测连接是否有效 性能影响明显 + testOnReturn: false +``` + +> validationQuery执行场景 + +周期检查 keepAlive +![](./img/001-druid.webp) + +获取连接时 闲置很久的连接触发检查 +![](./img/002-druid.webp) + +创建连接时的检查 +![](./img/003-druid.webp) + + +************************ + +> [Druid连接检查机制](https://blog.csdn.net/qq_37993902/article/details/124777056) +- `com.alibaba.druid.pool.DruidDataSource#createAndStartDestroyThread` 定时调度或单线程方式 周期性 检查连接 + - `com.alibaba.druid.pool.DruidDataSource#shrink(boolean, boolean)` keepAlive保活,补充新连接,关闭连接(异常,空闲超时) + - `com.alibaba.druid.pool.ValidConnectionChecker` 检查连接可用,注意MySQL PG都有协议层的ping方式,更省资源(类似ws协议中的Ping报文),其他数据库一般是配置校验SQL为 `select 1` + + +### HikariCP +> [HikariCP](https://github.com/brettwooldridge/HikariCP) -- [ ] 听说代码很精简, 阅读源码学习 ******************* +## Relational Database ### JPA > 连接池:1.x 默认是tomcat-jdbc连接池 2.x 是 HikariPool +> [参考: spring boot2 整合(二)JPA](https://www.jianshu.com/p/3b31270a44b1) +#### Configuration +> [Official Doc](https://docs.spring.io/spring-boot/docs/2.0.6.RELEASE/reference/htmlsingle/#howto-configure-jpa-properties) +**`ddl-auto`** +- JPA 默认是该配置 `spring.jpa.hibernate.ddl-auto` +- 但是如上配置没有生效的话就要用 这个 `spring.jpa.properties.hibernate.hbm2ddl.auto` + 1. none 什么都不做 + 1. create-only + 1. create 先删除, 然后建立新的表 + 1. create-drop 先删除, 然后建立新的表, 然后在SessionFactory实例关闭后再删除 + 1. update 创建和修改 + 1. validate 校验是否一致, 不一致就报错,启动失败 + - [Blog: 原生SQL的写法](http://blog.csdn.net/Amy_Queen/article/details/72454099) - [ ] 怎么映射视图到实体上? -### Mybatis - -> [IDEA下创建Springboot,thymeleaf,Mybatis,Postgresql,Gradle项目](https://blog.csdn.net/juewang_love/article/details/53769906) -#### 自定义查询 -##### HQL -- 使用Hibernate语法模式,将对象和数据库的表看成一个实体,方便书写SQL,但是在Controller层和Service层 - - 进行写代码的时候,参数的传递全是实体对象,要不停的new,这样真的没问题么(当有各种复杂的关联关系的时候,单个对象的CURD基本没有什么问题) - - `TODO` 所以还不如直接写原生SQL! 那么JPA就真的没有使用的必要性了,直接用Mybatis结合插件生成自动的CRUD的代码,这样更为轻量 - - 待后续使用后再回来填坑 +#### Jpa数据分页 +> [参考博客](https://www.tianmaying.com/tutorial/spring-jpa-page-sort) -##### 原生SQL +- 分页 page 从0开始 size是个数 sort可以不需要(如果本来就是id排序就没必要了) + - 原理就是 预编译SQL然后查询总数,然后再执行 必须有两条SQL执行 +- 查询的结果不包含实体的id属性 +#### 原生SQL - 涉及到数据的修改,就要加上前两个前缀,查询就直接写Query注解即可 ```java @Modifying @@ -143,70 +210,15 @@ public class TestMany { ##### 多对多 -************* -### Restful设计 -- 1.添加依赖 - -```xml - - org.springframework.boot - spring-boot-starter-data-rest - -``` -- 2.引入自动配置类 -```java - @Configuration - public class RestConfiguration extends RepositoryRestMvcConfiguration { - @Override - public RepositoryRestConfiguration config() { - return super.config(); - } - @Override - public ProfileResourceProcessor profileResourceProcessor(RepositoryRestConfiguration config) { - // 设置rest根目录是应用路径下的路径 : localhost:8080/rest - config.setBasePath("/rest"); - // 允许输出id - config.exposeIdsFor(Goods.class); - return super.profileResourceProcessor(config); - } - } -``` -- 3.配置repository的名字例如:(只要配置repository就能用REST了) - -```java - @RepositoryRestResource(path = "book") - public interface BookDao extends JpaRepository{} -``` - -- 4.启动应用,控制台有如下输出 -![输出](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Java/Spring/output.png) - -- 所有路径的使用方法: - - `GET` 查询单个 `/repo/id` 成功:200 失败404 - - `GET` 查询所有 `/repo` 成功200 失败404 - - `POST` 新增 `/repo` json数据发送 成功 201 失败404 - - `DELETE` 删除 `/repo/id` json数据 成功204 失败404 - - `PUT` 更新 `/repo/id` json 更新成功200 没有该id就插入201 失败404(使用主键自动增长就不会遇到404) - -#### 【特别注意】 -- rest得到的数据没有id - - 添加配置 `config.exposeIdsFor(Goods.class);` 即可查看到id [参考博客](http://tommyziegler.com/how-to-expose-the-resourceid-with-spring-data-rest/) - -### Jpa数据分页 -> [参考博客](https://www.tianmaying.com/tutorial/spring-jpa-page-sort) - -- 分页 page 从0开始 size是个数 sort可以不需要(如果本来就是id排序就没必要了) - - 原理就是 预编译SQL然后查询总数,然后再执行 必须有两条SQL执行 -- 查询的结果不包含实体的id属性 - -### 数据库上的事务支持 -- JPA对所有默认方法都开启了事务支持,查询类事务默认启用readOnly=true +### Mybatis +> [Mybatis](/Java/Ecosystem/Mybatis.md) **************** ## Non Relational database -### JPA -#### Redis的简单使用 +### Redis +> [Spring Data Redis](https://spring.io/projects/spring-data-redis) + _配置连接信息_ ```conf # REDIS (RedisProperties) @@ -266,21 +278,7 @@ _配置连接信息_ - 以上配置的template都是只是建立在最简单的键值对上,String-String,所以对象使用的是json来存储 - 但是使用的时候如同使用MySQL一样,是ORM框架自动处理数据的转换 - -#### 关于StringRedisTemplate的方法使用 -- 常见数据类型的中间对象 - - opsForValue() 操作简单键值对数据 - - hasKey() - - opsForHash() 操作含有hash的数据 - - opsForList() 操作含有list的数据 - - opsForZSet() 操作含有zset(有序)的数据 - - range()方法返回指定范围的数据 Java中Set类型的(诡异的是顺序保持了一致) - - opsForSet() 操作含有set的数据 - -- 设置超时时间 - - `redisTemplate.expire("max",tempTime,TimeUnit.SECONDS);` - #### 消息订阅和发布 -[参考博客: Spring Boot使用Redis进行消息的发布订阅](https://www.tianmaying.com/tutorial/springboot-redis-message) +[参考: Spring Boot使用Redis进行消息的发布订阅](https://www.tianmaying.com/tutorial/springboot-redis-message) diff --git a/Java/Spring/Transactional.md b/Java/Spring/Transactional.md index 9e1f542..0a08335 100644 --- a/Java/Spring/Transactional.md +++ b/Java/Spring/Transactional.md @@ -1,7 +1,57 @@ -`目录 start` - -- [事务](#事务) +--- +title: Spring 事务 +date: 2018-12-21 10:54:05 +tags: + - Spring +categories: + - Java +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [Spring 事务](#spring-事务) + - 1.1. [propagation](#propagation) + - 1.2. [isolation](#isolation) + - 1.3. [失效场景](#失效场景) + - 1.4. [异常场景](#异常场景) + - 1.5. [可编程事务管理](#可编程事务管理) + +💠 2024-01-31 11:40:19 **************************************** -# 事务 +# Spring 事务 +> [Doc: Transaction Management](https://docs.spring.io/spring-framework/docs/5.2.x/spring-framework-reference/data-access.html#spring-data-tier) + +## propagation + +## isolation + +************************ + +> 事务 和 AOP 同时使用 顺序问题 + +[Afterreturning 和 Transactional](https://stackoverflow.com/questions/39406242/afterreturning-aspect-executes-in-same-transaction-of-pointcut-method#) + +## 失效场景 +> [一口气说出 6种,@Transactional注解的失效场景 ](https://juejin.cn/post/6844904096747503629) + +1. 数据库引擎不支持事务,例如 MyISAM +1. 注解所在类没有进入IOC容器 +1. 注解所在方法没有public修饰, 从反射和代理技术角度是可以实现非public代理,只是在规范上要求public修饰 +1. 该方法在当前类被调用,AOP没有生效 +1. 数据源没有加载事务管理器 +1. propagation 设置不正确: PROPAGATION_SUPPORTS PROPAGATION_NOT_SUPPORTED PROPAGATION_NEVER +1. 异常被catch或者 rollbackFor 指定的类型不匹配(非自身且非子类)且该异常非RuntimeException和Error 子类 + - org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn + - org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn + +## 异常场景 +> 事务内语句 部分未回滚 部分失败 + +1. 使用 MySQL DML 和 DDL 混合使用 。`PG 支持DDL事务手动管理 MySQL的DDL会使用隐含事务不提供手动管理` + +## 可编程事务管理 + +- TransactionTemplate +- TransactionOperator +- TransactionManager +- ReactiveTransactionManager diff --git a/Java/Spring/Why/Readme.md b/Java/Spring/Why/Readme.md new file mode 100644 index 0000000..e27a6a9 --- /dev/null +++ b/Java/Spring/Why/Readme.md @@ -0,0 +1,2 @@ +# Spring的源码分析 +> [Spring Analysis](https://github.com/seaswalker/spring-analysis) diff --git a/Java/Spring/img/001-druid.webp b/Java/Spring/img/001-druid.webp new file mode 100644 index 0000000..831c875 Binary files /dev/null and b/Java/Spring/img/001-druid.webp differ diff --git a/Java/Spring/img/002-druid.webp b/Java/Spring/img/002-druid.webp new file mode 100644 index 0000000..82bdca0 Binary files /dev/null and b/Java/Spring/img/002-druid.webp differ diff --git a/Java/Spring/img/003-druid.webp b/Java/Spring/img/003-druid.webp new file mode 100644 index 0000000..dcdcd8d Binary files /dev/null and b/Java/Spring/img/003-druid.webp differ diff --git a/Java/TemplateEngine/README.md b/Java/TemplateEngine/README.md deleted file mode 100644 index 9ad64c4..0000000 --- a/Java/TemplateEngine/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# 模板引擎 - - -## Thymeleaf -- 语法比较晦涩,使用上感觉就是不如JSP来的直接,是不是该试下闲大赋的框架了 diff --git a/Java/TemplateEngine/Thymeleaf.md b/Java/TemplateEngine/Thymeleaf.md deleted file mode 100644 index 39212b4..0000000 --- a/Java/TemplateEngine/Thymeleaf.md +++ /dev/null @@ -1,27 +0,0 @@ -`目录 start` - -- [Thymeleaf](#thymeleaf) - - [流程控制](#流程控制) - - [if](#if) - - [集合处理](#集合处理) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Thymeleaf -> 辣鸡 - -## 流程控制 -### if -- lt 小于 -- eq 等于 -- gt 大于 -- le 小于等于 -- ge 大于等于 - -- 拼接参数:`th:href="@{/student/ChooseTopic/{id} (id=${pageNum}-2)}"` -- 判断块`` - - -## 集合处理 - -- 判断list大小:`th:if="${#lists.size(topicList) == 0}"` \ No newline at end of file diff --git a/Java/Test/JMH.md b/Java/Test/JMH.md new file mode 100644 index 0000000..4074911 --- /dev/null +++ b/Java/Test/JMH.md @@ -0,0 +1,41 @@ +--- +title: JMH +date: 2020-05-06 01:44:38 +tags: +categories: +--- + +💠 + +- 1. [JMH](#jmh) + - 1.1. [简易Demo](#简易demo) + - 1.2. [最佳实践](#最佳实践) + +💠 2024-02-22 14:23:17 +**************************************** +# JMH +> [Official Site](http://openjdk.java.net/projects/code-tools/jmh/) + +- [JMH 官方示例代码](http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/) + +> [Benchmark comparing serialization libraries on the JVM ](https://github.com/eishay/jvm-serializers) + +## 简易Demo +```java + @BenchmarkMode(Mode.Throughput) + @Warmup(iterations = 5) + @Measurement(iterations = 10, time = 1) + @Threads(2) + @OutputTimeUnit(TimeUnit.MILLISECONDS) + class Target{ + @Benchmark + public void max(){ + + } + } + + // 运行 JMH + new Runner(new OptionsBuilder().include(Target.class.getSimpleName()).build()).run(); +``` + +## 最佳实践 diff --git a/Java/Test/JavaCucumber.md b/Java/Test/JavaCucumber.md new file mode 100644 index 0000000..a0e6bde --- /dev/null +++ b/Java/Test/JavaCucumber.md @@ -0,0 +1,17 @@ +--- +title: JavaCucumber +date: 2019-03-01 15:38:47 +tags: + - 测试 +categories: + - Java +--- + +**目录 start** + +1. [JavaCucumber](#javacucumber) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# JavaCucumber +> [Official Tutorial](https://docs.cucumber.io/guides/10-minute-tutorial/) diff --git a/Java/Test/JavaHamcreset.md b/Java/Test/JavaHamcreset.md new file mode 100644 index 0000000..790702f --- /dev/null +++ b/Java/Test/JavaHamcreset.md @@ -0,0 +1,18 @@ +--- +title: JavaHamcreset +date: 2019-03-01 15:38:47 +tags: + - 测试 +categories: + - Java +--- + +**目录 start** + +1. [JavaHamcrest](#javahamcrest) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# JavaHamcrest + +> [Official Doc](http://hamcrest.org/JavaHamcrest/) | [Github](https://github.com/hamcrest/JavaHamcrest) diff --git a/Java/AdvancedLearning/JavaTest.md b/Java/Test/JavaTest.md similarity index 73% rename from Java/AdvancedLearning/JavaTest.md rename to Java/Test/JavaTest.md index 5aade05..99a4525 100644 --- a/Java/AdvancedLearning/JavaTest.md +++ b/Java/Test/JavaTest.md @@ -1,27 +1,39 @@ -`目录 start` - -- [Java的测试](#java的测试) - - [断言](#断言) - - [正式代码](#正式代码) - - [测试代码](#测试代码) - - [单元测试](#单元测试) -- [实现方案](#实现方案) - - [使用Junit](#使用junit) - - [Idea上Junit的使用](#idea上junit的使用) - - [TestNG](#testng) - - [Mock框架](#mock框架) - - [Mockito](#mockito) - - [DBUnit](#dbunit) -- [感悟](#感悟) - -`目录 end` |_2018-08-29_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Java中的测试 +date: 2018-11-21 10:56:52 +tags: + - 测试 +categories: + - Java +--- + +**目录 start** + +1. [Java的测试](#java的测试) + 1. [断言](#断言) + 1. [正式代码](#正式代码) + 1. [测试代码](#测试代码) + 1. [单元测试](#单元测试) +1. [测试框架](#测试框架) + 1. [Junit](#junit) + 1. [Idea上Junit的使用](#idea上junit的使用) + 1. [TestNG](#testng) + 1. [Mock框架](#mock框架) + 1. [Mockito](#mockito) + 1. [EasyMock](#easymock) + 1. [DBUnit](#dbunit) + 1. [基准测试](#基准测试) + 1. [JMH](#jmh) +1. [感悟](#感悟) + +**目录 end**|_2020-05-06 01:45_| **************************************** # Java的测试 > [测试的基础理论](/Skills/Base/Test.md) ## 断言 ### 正式代码 -[参考博客: Java陷阱之assert关键字详解_java](https://yq.aliyun.com/ziliao/131292) +[参考: Java陷阱之assert关键字详解_java](https://yq.aliyun.com/ziliao/131292) [java 中assert的使用](http://www.cnblogs.com/mylove7/articles/3457157.html) 首先可以用在单元测试代码中。junit侵入性是很强的,如果整个工程大量的代码都使用了junit,就难以去掉或者是选择另外一个框架。如果单元测试代码 很多,并且想复用这些单元测试案例,应该选择assert而不是junit,便于使用别的单元测试框架,比如TestNG。同理正式的功能代码根本就不应 该出现Junit,应该使用assert. @@ -52,8 +64,8 @@ assert应该是开发阶段打开,而在发布后关闭。 - assert(expression) 断言表达式为真 **************** -# 实现方案 -## 使用Junit +# 测试框架 +## Junit > [Junit4官网](https://junit.org/junit4/)|[Junit5官网](https://junit.org/junit5/)| [如何上手Junit](/MyBlog/how-to-use-junit.md) | [如何上手Junit5](/MyBlog/how-to-use-junit5.md) - Before Test 执行顺序: @@ -86,8 +98,11 @@ http://static.javadoc.io/org.mockito/mockito-core/2.19.0/org/mockito/Mockito.htm ### Mockito > [Officail Site](http://site.mockito.org/) | [Github:mockito](https://github.com/mockito/mockito) > [Mockito Tutorial](https://www.tutorialspoint.com/mockito/index.htm) +> [Unit tests with Mockito - Tutorial](http://www.vogella.com/tutorials/Mockito/article.html) -> [参考博客: TDD:什么是桩(stub)和模拟(mock)?](http://www.cnblogs.com/happyframework/p/3595547.html) +> [参考: TDD:什么是桩(stub)和模拟(mock)?](http://www.cnblogs.com/happyframework/p/3595547.html) +> [参考: mockito](http://www.testclass.net/mockito/) +> [mockito](http://static.javadoc.io/org.mockito/mockito-core/2.19.0/org/mockito/Mockito.html#1) > 官网提示: >1. Do not mock types you don’t own @@ -95,15 +110,32 @@ http://static.javadoc.io/org.mockito/mockito-core/2.19.0/org/mockito/Mockito.htm >1. Don’t mock everything >1. Show love with your tests! +1. **常规使用** when(mock.get(anyInt())).thenReturn(null); +1. **对void方法的mock** doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject()); +1. **使用规则去校验** verify(mock).someMethod(contains("foo")); -`when(player.hasEnoughScore(anyInt(), anyInt())).thenReturn(true);` +> 切忌 不可对非Mock对象使用 mock 的系列方法, 不然会报出南辕北辙的错误,这是首先要排查的一点 + +一般不用对 void 方法打桩, 事后 verify 就行 +测试代码针对 mock 对象的 void 方法调用本来就没有什么效果,所以一般也无须用 doNothing(), 况且 void 提供不了返回值作进一步 mock,只需要在事后用 verify() 进行验证一下。 + +### EasyMock +> [Github](https://github.com/easymock/easymock) + +************************ ## DBUnit > 基于Junit的一个数据库测试框架, 方便测试dao层 +> [Github](https://github.com/sebastianbergmann/dbunit)`但是已经停止维护了` + +************************ + +## 基准测试 +### JMH +> [详情](/Java/Test/JMH.md) ********************** # 感悟 >1. 好的测试能大大节省时间, 坏的测试大量延误时间 >1. 应该在项目主体架构明确后, 才大量书写测试, 验证程序, 避免编写大量无用测试代码 >1. 单元测试有多难写, 你的代码就有多高的耦合度 - diff --git a/Java/Test/Junit.md b/Java/Test/Junit.md new file mode 100644 index 0000000..ca8dfa0 --- /dev/null +++ b/Java/Test/Junit.md @@ -0,0 +1,244 @@ +--- +title: Junit +date: 2018-11-21 10:56:52 +tags: + - 测试 + - Junit +categories: + - Java +--- + +💠 + +- 1. [如何使用Junit](#如何使用junit) + - 1.1. [引入依赖](#引入依赖) + - 1.1.1. [Maven项目](#maven项目) + - 1.2. [编码规范](#编码规范) + - 1.3. [常用注解](#常用注解) + - 1.3.1. [Rule注解的使用](#rule注解的使用) + - 1.4. [断言的使用](#断言的使用) + - 1.4.1. [assertThat](#assertthat) + - 1.5. [参数化测试](#参数化测试) + - 1.6. [分类测试](#分类测试) + - 1.7. [测试套件](#测试套件) +- 2. [Junit源码解析](#junit源码解析) +- 3. [如何使用JUnit5](#如何使用junit5) + +💠 2024-09-27 11:12:37 +**************************************** +# 如何使用Junit +> [Official doc: 4.12](https://github.com/junit-team/junit4/blob/master/doc/ReleaseNotes4.12.md) + +> [单元测试 - JUnit4 详解](https://pdai.tech/md/develop/ut/dev-ut-x-junit.html) + +- Junit4已经停止更新了, 取而代之的是 Junit5 Jupiter, 但是Spring等众多框架仍使用Junit4 + +> 快速使用 +_JUnit_ +- 主要的三个特性: + - 用于测试预期结果和异常的断言, assertEquals() + - 设置和 _拆卸_ 通用测试数据的能力, @Before @After + - 运行测试套件的测试运行器 + +_一个基本的JUnit测试_ +- @Before 标记方法, 测试运行前准备测试数据 +- @After 标记方法, 测试运行完成后拆卸测试数据 +- @Test 测试方法 例如:预期的异常`@Test(expected=NullPointException.class)` + +## 引入依赖 +### Maven项目 +> [参考项目 Junit4Demo](https://github.com/zhuifengshen/Junit4Demo) + +> 添加依赖 +```xml + + junit + junit + 4.12 + test + +``` + +************************* + +## 编码规范 +1. 测试类所在的包结构要和被测试类保持一致 +1. 创建一个Java类, 命名为被测试类名字后加上Test +1. 测试具体的方法: test加上方法名 +1. 所有测试方法返回类型必须为void且无参数 +1. 测试方法里一般使用断言进行测试, 更为直观 +1. 单元测试需要切断和隔离外部系统依赖,包括但不限于 操作系统特定目录下的文件,当前时间,网络环境,外部系统调用。 + +***************** + +## 常用注解 +- [参考: JUnit4使用教程-快速入门](http://blog.csdn.net/chenleixing/article/details/44259453) | [参考: JUnit4单元测试入门教程](https://www.jianshu.com/p/7088822e21a3): + +1. @Test : 测试方法,测试程序会运行的方法,可设置参数 + - (expected=XXException.class) 期望该测试方法应该抛出某异常 + - (timeout=xxx) 限制该测试方法的执行时间, 超时视为失败 + - `注意被注解的方法 必须是 public 无参数 非静态 ` +1. @Ignore : 被忽略的测试方法 +1. @Before: 每一个测试方法之前运行 +1. @After : 每一个测试方法之后运行 +1. @BeforeClass: 所有测试开始之前运行, 在测试类还没有实例化就已经加载所以需要static修饰 +1. @AfterClass: 所有测试结束之后运行, + +1. @FixMethodOrder(MethodSorters.NAME_ASCENDING) 指定Junit方法执行顺序 `since 4.11` + +### Rule注解的使用 +> 也可以使用 `@Rule` 来规定测试类中所有测试方法 +```java + import org.junit.rules.Timeout; + + @Rule + public Timeout timeout = new Timeout(1000); +``` +********************* + +## 断言的使用 +> 使用 Hamcrest 工具能让断言更为简洁强大 + +1. 直接使用关键字 assert, 例如 `assert a == null` **(不推荐, 因为断言不通过时没有详细报错)** +2. 静态导入 `import static org.junit.Assert.*`, 使用其大量工具方法: + +| 方法签名 | 用法 | +|:----|:----| +| assertNull(java.lang.Object object) | 检查对象是否为空 | +| assertNotNull(java.lang.Object object) | 检查对象是否不为空 | +| assertEquals(double expected, double actual, double delta)| 检查 指定精度 的double值是否相等 | +| assertNotEquals(double expected, double actual, double delta)| 检查 指定精度 的double值是否不相等 | +| assertFalse(boolean condition)| 检查条件是否为假 | +| assertTrue(boolean condition)| 检查条件是否为真 | +| assertSame(java.lang.Object expected, java.lang.Object actual)| 检查两个对象引用是否引用同一对象(即地址是否相等) | +| assertNotSame(java.lang.Object unexpected, java.lang.Object actual)| 检查两个对象引用是否不引用统一对象(即地址不等) | +| assertArrayEquals(Object[] a, Object[] b)| 检查两个数组是否相等 | +| assertThat(T, Matcher)| 检查泛型是否匹配, 以及一系列复杂的表达式 | +| fail(String string)| 依据入参并宣告测试失败 | + + +```java +public class AssertTest { + @Test + public void testEquals(){ + String a = "hi"; + String b = "hi"; + // 使用assert关键字 + assert a.equals(b); + // 使用Assert类的静态工具方法 + assertEquals(a, b); + assert a == b; + assertSame(a, b); + // 因为trim 调用了SubString方法, 而这个方法是返回一个new的字符串 + String c = "h"+"i".trim(); + assertEquals(a, c); + assertSame(a, c); + } + @Test + public void testFail(){ + fail(); + fail("测试失败"); + } +} +``` + +### assertThat + +> [参考: assertThat详解](http://www.cnblogs.com/Firefly727/archive/2011/07/05/2098625.html) + +********************************** +## 参数化测试 +> Junit 4 参数化测试 允许通过变化范围的参数值来测试方法 | 个人认为: 将测试方法的入参集合数据和测试行为分离开, 简化书写逻辑 + +1. 对测试类添加注解 `@RunWith(Parameterized.class)` +2. 将需要使用变化范围参数值测试的参数定义为私有变量; +3. 使用上一步骤声明的私有变量作为入参,创建构造函数; +4. 创建一个使用`@Parameterized.Parameters`注解的公共静态方法,它将需要测试的各种变量值通过集合的形式返回; +5. 使用定义的私有变量定义测试方法; + +```java + // 1 + @RunWith(Parameterized.class) + public class CalculateTest { + // 2 + private double numA; + private double numB; + + // 3 + public CalculateTest(double numA, double numB) { + this.numA = numA; + this.numB = numB; + } + + // 4 + @Parameterized.Parameters + public static Collection data() { + Object[][] data = new Object[][]{ + {2, 4}, + {3, 5} + }; + return Arrays.asList(data); + } + + // 5 + @Test + public void testAdd() throws Exception { + Calculate calc = new Calculate(); + double result = calc.add(numA, numB); + System.out.println("input " + numA + " + " + numB + " = " + result); + assert result != 0; + } + + @Test + public void testDivide() { + Calculate calc = new Calculate(); + double result = calc.divide(numA, 3); + System.out.println("input " + numA + " / " + 3 + " = " + result); + assert result != 0; + } + } + +``` +> 执行效果: 将data方法返回的数据作为参数, 迭代执行单元测试方法 + +## 分类测试 + +```java + public interface FastTests { + } + public interface SlowTests { + } +``` +- 在测试方法或测试类上加注解 @Category 对类或方法做标记和分类,便于测试套件使用。 + - 例如 测试方法上添加 `@Category(SlowTests.class)` + - 测试套件类上添加 `@Categories.IncludeCategory(SlowTests.class)` 将会标记该测试套件只会运行 SlowTests 标记的方法 + +## 测试套件 +> Junit 4允许通过使用测试套件类批量运行测试类 | 批量执行测试类, 组装为一个套件,一起执行 + +- 在当前测试类上加上注解: @RunWith @Suite.SuiteClasses, 执行当前类时会依次执行注解中的测试类. +```java + @RunWith(Suite.class) + @Suite.SuiteClasses({AnnotationTest.class, EvenNumberCheckerTest.class}) + public class SuiteTest { + } +``` + +注意最好不要在当前测试类中写测试方法, 因为运行不了, 但是如果写了, 直接运行该测试类却又不会受影响 + + +************************ + +# Junit源码解析 +> [JUnit-3.8.1源码分析](http://ju.outofmemory.cn/entry/137525) +> [JUnit4源码初探](https://blog.csdn.net/baidu_33409651/article/details/51526582) + +> [Source: Junit 3.8.1](https://gitee.com/gin9/LearnJunit) + +************************ + +# 如何使用JUnit5 +> [Official doc](http://junit.org/junit5/docs/current/user-guide/) +> [单元测试 - Junit5 详解](https://pdai.tech/md/develop/ut/dev-ut-x-junit5.html) + +> [参考: JUnit5用户指南](http://junit5.doczh.cn/overview/) diff --git a/Java/Test/TestNG.md b/Java/Test/TestNG.md new file mode 100644 index 0000000..bf582d0 --- /dev/null +++ b/Java/Test/TestNG.md @@ -0,0 +1,45 @@ +--- +title: TestNG +date: 2018-11-21 10:56:52 +tags: + - 测试 + - TestNG +categories: + - Java +--- + +**目录 start** + +1. [TestNG](#testng) + 1. [使用](#使用) + 1. [基本注解](#基本注解) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# TestNG +> [Official Doc](http://testng.org/doc/documentation-main.html) + +> [易百: TestNG教程](https://www.yiibai.com/testng/) +> [TestNG 入门教程](http://www.cnblogs.com/TankXiao/p/3888070.html) +> [testNG官方文档](http://testng.org/doc/index.html) | [Github:TestNG](https://github.com/cbeust/testng) +> [tools](http://toolsqa.com/selenium-webdriver/testng-introduction/) + +## 使用 +> 基本使用 + +**Gradle使用** +```groovy + testCompile group: 'org.testng', name: 'testng', version: '6.14.3' +``` +然后和Junit使用是一致的, 在方法上打上 @Test 注解即可运行, 注意Test注解的包为 `import org.testng.annotations.Test;` + +### 基本注解 +1. @Test + - threadPoolSize + - invocationCount + - timeOut + - invocationTimeOut + +> 测试方法中使用多线程, 和Junit是一致的, 只要主线程退出了, 其中创建的线程也会立即退出 +> 但是 TestNG 并行执行测试方法会更方便 + diff --git a/Java/Tool/Eclipse.md b/Java/Tool/Eclipse.md new file mode 100644 index 0000000..e88f9bd --- /dev/null +++ b/Java/Tool/Eclipse.md @@ -0,0 +1,42 @@ +--- +title: Eclipse +date: 2018-12-17 21:30:46 +tags: + - Eclipse +categories: + - Java + - IDE +--- + +**目录 start** + +1. [Eclipse](#eclipse) + 1. [Mars](#mars) + 1. [Eclipse Che](#eclipse-che) + 1. [Install](#install) + +**目录 end**|_2020-06-04 19:41_| +**************************************** +# Eclipse +## Mars + +* 这里的Tomcat是使用了你所导入的必要执行文件,但是运行的必要配置文件在eclipse Server项目里另有一份 +* 而且运行时也是使用这份配置文件,这样的结果是可以使用一份Tomcat目录,在eclipse配置运行多个Tomcat +* 但是奇怪的是 访问不了Tomcat主页即:localhost:8080 所以也就不能管理Tomcat 查看运行状态 + +资源下载 archive.eclipse.org/eclipse/downloads/ + +********************* + +## Eclipse Che +> Next Generation IDE [Github](https://github.com/eclipse/che)|[Official Site](https://www.eclipse.org/che/) + +> [Quick Start](https://www.eclipse.org/che/docs/quick-start.html) + + +### Install +> [Install By Docker](https://www.eclipse.org/che/docs/docker-single-user.html) + +- docker pull eclipse/che +- docker run -ti -v /var/run/docker.sock:/var/run/docker.sock -v /home/kcp/App/eclipse/che:/data eclipse/che start + diff --git a/Java/Tool/Gradle.md b/Java/Tool/Gradle.md index db37b70..d4006a6 100644 --- a/Java/Tool/Gradle.md +++ b/Java/Tool/Gradle.md @@ -1,72 +1,104 @@ -`目录 start` - -- [Gradle](#gradle) - - [书籍](#书籍) - - [发行版本列表](#发行版本列表) - - [安装配置](#安装配置) - - [SDKMAN方式](#sdkman方式) - - [Chocolate](#chocolate) - - [命令行选项](#命令行选项) - - [守护进程](#守护进程) - - [Docker安装](#docker安装) - - [配置镜像源](#配置镜像源) -- [关键配置文件](#关键配置文件) - - [build.gradle](#buildgradle) - - [初始化一个新项目](#初始化一个新项目) - - [dependency](#dependency) - - [统一依赖管理](#统一依赖管理) - - [配置Wrapper](#配置wrapper) - - [插件](#插件) - - [常用插件](#常用插件) - - [setting.gradle](#settinggradle) - - [Gradle多模块的构建](#gradle多模块的构建) - - [另一种方式](#另一种方式) -- [部署](#部署) - - [War包](#war包) - - [Jar包](#jar包) - - [上传至构建仓库](#上传至构建仓库) - - [构建Docker镜像](#构建docker镜像) - - [第二种插件方式](#第二种插件方式) - -`目录 end` |_2018-09-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Gradle +date: 2018-12-12 21:29:59 +tags: + - Gradle +categories: + - Java +--- + +💠 + +- 1. [Gradle](#gradle) + - 1.1. [书籍](#书籍) + - 1.2. [发行版本列表](#发行版本列表) +- 2. [安装](#安装) + - 2.1. [Chocolate](#chocolate) + - 2.2. [解压配置](#解压配置) + - 2.3. [Wrapper](#wrapper) + - 2.4. [CUI使用](#cui使用) + - 2.4.1. [命令行选项](#命令行选项) + - 2.4.2. [动作](#动作) + - 2.4.2.1. [build](#build) + - 2.4.2.2. [test](#test) + - 2.4.3. [守护进程](#守护进程) +- 3. [配置](#配置) + - 3.1. [全局配置文件](#全局配置文件) + - 3.2. [build.gradle](#buildgradle) + - 3.2.1. [SourceSet](#sourceset) + - 3.2.2. [依赖管理](#依赖管理) + - 3.2.2.1. [依赖排除以及指定依赖版本](#依赖排除以及指定依赖版本) + - 3.2.2.2. [统一管理依赖](#统一管理依赖) + - 3.2.3. [配置镜像源](#配置镜像源) + - 3.2.4. [插件](#插件) + - 3.2.4.1. [Lombok](#lombok) + - 3.2.4.2. [Maven Publish](#maven-publish) + - 3.2.4.3. [shadowJar](#shadowjar) + - 3.2.4.4. [docker](#docker) + - 3.2.4.5. [protobuf-gradle-plugin](#protobuf-gradle-plugin) + - 3.3. [setting.gradle](#settinggradle) +- 4. [Gradle多模块的构建](#gradle多模块的构建) + - 4.1. [另一种多模块的构建方式](#另一种多模块的构建方式) +- 5. [使用](#使用) + - 5.1. [安装到本地仓库](#安装到本地仓库) + - 5.2. [上传至构件仓库](#上传至构件仓库) +- 6. [打包部署](#打包部署) + - 6.1. [构建Docker镜像](#构建docker镜像) + - 6.1.1. [插件方式构建Docker镜像](#插件方式构建docker镜像) + +💠 2024-05-02 00:46:30 **************************************** + # Gradle -> [官方 guide](https://gradle.org/guides/?q=JVM) | [其他 tutorial](https://www.tutorialspoint.com/gradle/index.htm) -> [参考博客: 零散知识点总结(1) - Gradle 使用配置总结](https://www.jianshu.com/p/47cbbb4eab13) +> [Official Guide](https://gradle.org/guides/?q=JVM) | [tutorials](https://www.tutorialspoint.com/gradle/index.htm) + +> [参考: 零散知识点总结(1) - Gradle 使用配置总结](https://www.jianshu.com/p/47cbbb4eab13) -**个人看法** -> [参考: Gradle在大型Java项目上的应用](www.infoq.com/cn/articles/Gradle-application-in-large-Java-projects) +> [Github: Gradle samples](https://github.com/gradle/gradle/tree/master/subprojects/docs/src/samples) +****************************** +个人决定弃用Gradle **优缺点** > [Gradle大吐槽](https://blog.csdn.net/MCL529/article/details/79341706) > [官方对比Gradle和Maven](https://gradle.org/maven-vs-gradle/) > 优点 -1. 相对于Maven, 配置文件简洁了很多, 所以才入坑学习使用的 -2. 对于一些需要自定义的任务,因为核心为Groovy,所以实现能力高 - - 例如:将一个SpringBoot项目构建成一个镜像,并tag上当前构建的镜像为release,然后删除旧有容器,使用新的镜像启动容器 +1. 相较Maven,Gradle配置文件更简洁,灵活度高(groovy语言实现各种自定义操作:多目标构建,多端发布) +1. 知名项目在使用,阅读调试源码需要使用到,例如:Srping全家桶、Andriod等等 +1. 支持编程式任务,相较于Maven的XML配置文件,Gradle的配置文件为Groovy或Kotlin脚本,更灵活 功能强大。 + - 常见的 多目标构建,多端发布 等等 + - 例如自定义的本地ci流程: 将SpringBoot项目打包构建Docker镜像,并打上 release且带上git commitId 的 tag, 然后删除应用旧有运行时容器,使用新的镜像启动新的容器 > 缺点 -1. 内存占用巨大,存在内存泄露问题, 以至于在IDEA上不敢使用自动导入, 不然每动一下build.gradle 就会卡半天, 8G内存都不够用!! -2. 编译速度慢, 如果和Maven进行对比, 编译速度和资源占用确实慢 +1. 内存和CPU等资源占用大于Maven,虽然新出的mvnd资源占用更大 emm。 +1. 引用依赖时对Maven兼容,发布依赖不支持,`需要第三方插件`。??? +1. Gradle本身设计使用的API和规范一直在变,有些改动不考虑兼容性,`不稳定`。 + - 当你想要捡起一个多年前的项目编译运行时发现要看文档,调整一堆才能正常用,当然,不更新Gradle就没问题 但是每年一个大版本,强迫症不适应 +1. 多项目管理没有Maven方便,多项目结构或依赖发生变更需要更复杂更慢的流程才能刷新重新加载完成。 +1. Gradle 缺省使用wrapper,并且Gradle发布非常频繁,容易导致本地一堆gradle版本占用磁盘,加载新项目还需要等待下载不同的gradle版本 + - 虽然可以通过手动快速取消IDEA自动下载,手动指定Gradle版本来避免,但是过程就很恶心。 +1. IDEA对Gradle的支持远没有Maven好(例如依赖跳转,依赖冲突,依赖树等功能 Gradle全没有),一方面也是Gradle变更太快,设计太灵活导致的 + - 报错信息全靠Google,没法直接定位,如果使用Maven的话报错基本发生在代码中,而不是构建工具本身,`时间不是可以这么浪费的`。 + +> [参考: Gradle在大型Java项目上的应用](www.infoq.com/cn/articles/Gradle-application-in-large-Java-projects) +> [我讨厌 Gradle!!!](https://gist.github.com/CrazyBoyFeng/936680de7dd0a7cdd5558c7ba6e8fe84) +> [唉,来吐槽一下 gradle](https://www.v2ex.com/t/735701) ******************** ## 书籍 > [Gradle in Action 中译](http://www.jb51.net/books/527811.html) `如果没有一点Groovy基础, 阅读自定义Task等一些高自定义的地方还是比较困惑` ## 发行版本列表 -> [官方网址](http://services.gradle.org/) 有各个版本的下载以及版本发行说明 > [Github地址](https://github.com/gradle/gradle/releases)`查看简洁的 Release Note 更方便` -## 安装配置 -> 和maven使用同一个本地库 只要加上 M2_HOME 环境变量即可, 值和 MAVEN_HOME 一样, 并没有用 +# 安装 +> 注意 Gradle 会默认使用Maven的本地库, 但是是复制过来使用而不是共用 +> 会将 `~/.m2/repository` 复制到 `~/.gradle/caches/modules-2/files-2.1/`, 目录结构也发生改变 +- [Gradle 使用Maven的本地仓库](https://blog.csdn.net/kcp606/article/details/81636426) -### SDKMAN方式 -- 先安装sdkman -- 使用Bash运行`curl -s "https://get.sdkman.io" | bash` -- `sdk install gradle` 即可安装 +> 或者 SDKMAN安装 `sdk install gradle` -### Chocolate +## Chocolate - windows 上安装 chocolate - PowerShell中运行 `wr https://chocolatey.org/install.ps1 -UseBasicParsing | iex` - 若操作系统默认禁止执行脚本,执行一次`set-executionpolicy remotesigned`后脚本顺利执行 @@ -74,59 +106,80 @@ 1. 执行“开始/运行”命令(或者WIN + R),输入“regedit”,打开注册表。 2. 展开注册表到下面的分支[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion],在右侧窗口中找到名为“ProgramFilesDir”的字符串,双击把数值“C:\Program Files”修改为“D:\ProgramFiles”,确定退出后,即可更改常用软件的安装路径了。 +## 解压配置 +> [官方下载网址](http://services.gradle.org/) 有各个版本的下载以及版本发行说明 + +- [腾讯云镜像](https://mirrors.cloud.tencent.com/gradle/) + +1. 解压到任意目录, 并将 bin 目录加入 环境变量即可 + +## Wrapper +> [The Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) + +类似于 Maven 的 mvnw 脚本 在使用IDE生成项目的时候,可以选择gradle的执行目录,可以选`gradle wrapper` 也可以选自己下载解压的完整包 +如果使用的不是这个wrapper,那么别人在下载项目后,运行gradle命令就要先安装gradle,使用wrapper更好 + +```groovy + task wrapper(type: Wrapper){ + gradleVersion = '4.8' + distributionUrl = '限定访问内网的URL' + distributionPath = '包装器被解压缩放的相对路径' + } +``` +- 运行 gradle wrapper 一次即可开始使用包装器的脚本来构建项目了 +- 生成gradle包管理器:`gradle wrapper --gradle-version 2.0` +- 下载的多版本gradle `~/.gradle/wrapper/dists` +************************ + +## CUI使用 ### 命令行选项 -- `gradle 构建文件中的task名`: 直接运行task +- `tasks` : 输出所有建立的task +- `properties` : 输出所有可用的配置属性 +- 执行 task `gradle taskName` +- 交互式新建项目 `gradle init` + - `-b,--build-file test.gradle` 指定运行脚本文件 - `--offline` 离线模式 - `-P ,--project-prop`:配置参数 -Pmyprop=value - `-i,--info` : 打印info级别的输出 - `-s,--stacktrace`: 输出错误栈 - `-q,--quiet`:减少构建出错时打印的错误信息 -- `tasks` : 输出所有建立的task -- `properties` : 输出所有可用的配置属性 + +### 动作 +#### build + +#### test + +- gradle test -Dtest.single=YourTestClass +- gradle test --tests org.somewhere.MyTestClass +- gradle test --tests org.somewhere.MyTestClass.my_test_case ### 守护进程 -- 命令加上 `--daemon`就会开启一个守护进程,只会开启一次, + +- 命令加上 `--daemon`就会开启一个守护进程,只会开启一次 - 守护进程会在空闲3小时后销毁 - 手动关闭 `gadle --stop ` - 构建时不采用守护进程 `--no--daemon` -### Docker安装 -> [Docker 文档](https://docs.docker.com/samples/library/gradle/) +************************ -**************************** -## 配置镜像源 -**阿里云** -> [参考博客: 配置Gradle的镜像为阿里云镜像](https://tvzr.com/change-the-mirror-of-gradle-to-aliyun.html) +# 配置 +## 全局配置文件 + +> 配置镜像源 -_当前项目的build.gradle_ -```Groovy - repositories { - def aliyun = "http://maven.aliyun.com/nexus/content/groups/public/" - def abroad = "http://central.maven.org/maven2/" - maven { - url = aliyun - artifactUrls abroad - } - // 马云上自己的库 - maven { - url = "https://gitee.com/gin9/MavenRepos/raw/master" - } - mavenCentral() - jcenter() - } -``` -**全局的配置** _~/.gradle/init.gradle_ -```Groovy +```groovy allprojects{ repositories { - def ALIYUN_REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public' - def ALIYUN_JCENTER_URL = 'http://maven.aliyun.com/nexus/content/repositories/jcenter' + def ALIYUN_REPOSITORY_URL = 'https://maven.aliyun.com/repository/public' + def ALIYUN_JCENTER_URL = 'https://maven.aliyun.com/repository/public' + def ALIYUN_GOOGLE_URL = 'https://maven.aliyun.com/repository/google' + def ALIYUN_GRADLE_PLUGIN_URL = 'https://maven.aliyun.com/repository/gradle-plugin' all { ArtifactRepository repo -> if(repo instanceof MavenArtifactRepository){ def url = repo.url.toString() - if (url.startsWith('https://repo1.maven.org/maven2')) { + if (url.startsWith('https://repo1.maven.org/maven2/')) { project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_REPOSITORY_URL." remove repo } @@ -134,20 +187,26 @@ allprojects{ project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_JCENTER_URL." remove repo } + if (url.startsWith('https://dl.google.com/dl/android/maven2/')) { + project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_GOOGLE_URL." + remove repo + } + if (url.startsWith('https://plugins.gradle.org/m2/')) { + project.logger.lifecycle "Repository ${repo.url} replaced by $ALIYUN_GRADLE_PLUGIN_URL." + remove repo + } } } - maven { - url ALIYUN_REPOSITORY_URL - url ALIYUN_JCENTER_URL - } + maven { url ALIYUN_REPOSITORY_URL } + maven { url ALIYUN_JCENTER_URL } + maven { url ALIYUN_GOOGLE_URL } + maven { url ALIYUN_GRADLE_PLUGIN_URL } } } ``` -************************ -# 关键配置文件 ## build.gradle -_Hello World_ +> _Hello World_ ```groovy task helloworld{ doLast { @@ -159,162 +218,234 @@ _Hello World_ println 'Hello world!' } ``` -- 运行:`gradle -q helloworld` +运行:`gradle -q helloworld` -### 初始化一个新项目 -> [doc:building java application](https://guides.gradle.org/building-java-applications/) +************************** +> 初始化新项目 [Doc:building java application](https://guides.gradle.org/building-java-applications/) 或者直接使用 gradle init 交互式新建一个项目 -### dependency -- 和Maven用的是同一种方式 groupId artifactId version -- 使用本地依赖 `compile files('lib/ojdbc-14.jar')` 相对的根目录是src同级目录 +******************************** -[Official doc: dependency management](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_plugin_and_dependency_management) +### SourceSet +> [SourceSet](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.SourceSet.html) -> 4.10 Deprecated: `compile runtime testCompile testRuntime` +```groovy + sourceSets{ + main{ + proto{ + srcDir 'proto/proto' + } + java{ + srcDir 'out/build/generated/main' + } + } + } +``` +*************** -- `compile(Deprecated)` - - Compile time dependencies. Superseded by implementation. +### 依赖管理 +和Maven用的是同一种方式: groupId artifactId version -- `implementation extends compile` - - Implementation only dependencies. +> 注意: Java项目中 compile 在 Gradle 已弃用, 取而代之的是新增的多种定义方式 implementation api 等等 +> 明确了各种定义方式在项目中依赖的范围, 看起来更完美, 但是复杂度大大提高了 -- `compileOnly` - - Compile time only dependencies, not used at runtime. +所以依据个人使用爱好, 简单易用就 compile testCompile 到底, 强迫症就 好好看官方文档 所有定义方式过一遍.... -- `compileClasspath extends compile, compileOnly, implementation` - - Compile classpath, used when compiling source. Used by task compileJava. +在定义项目时 +- 可以直接使用简单原始的 [Java plugin](https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_plugin_and_dependency_management) -- `annotationProcessor` - - Annotation processors used during compilation. +- 也可以根据使用场景的不同使用不同的方案 [Building Java & JVM projects](https://docs.gradle.org/5.2/userguide/building_java_projects.html) + 1. `Java libraries` 适用于: Java 库. + 1. `Java applications` 适用于: 可执行jar + 1. `Java web applications` 适用于: Java Web项目, 打包成 war + 1. `Java EE applications` 适用于: Java EE, 打包成 ear + 1. `Java Platforms` 适用于: Java套件, 本身不包含任何代码, 只是一组依赖的聚合 -- `runtime(Deprecated) extends compile` - - Runtime dependencies. Superseded by runtimeOnly. +*************** +> Java -- `runtimeOnly` - - Runtime only dependencies. +`implementation` +Gradle 中取代 compile 的方式, 使用范围比 compile 略小, 比如 +- B 项目中定义依赖: implementation A +- C 项目中定义依赖: implementation B -- `runtimeClasspath extends runtimeOnly, runtime, implementation` - - Runtime classpath contains elements of the implementation, as well as runtime only elements. +此时 C 项目不能在代码中使用 A 中的类, 因为在 C 项目中 A 是声明为 runtime的, 也就是只在运行时会用到 +如果 B 使用的 compile, 那么 C 就能直接访问 A 中的类, 但是这是官方不推荐的 -- `testCompile(Deprecated) extends compile` - - Additional dependencies for compiling tests. Superseded by testImplementation. +****************** -- `testImplementation extends testCompile, implementation` - - Implementation only dependencies for tests. +> Java Libraries -- `testCompileOnly` - - Additional dependencies only for compiling tests, not used at runtime. +`新增了 api 等定义方式` [Java Library plugin](https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_configurations_graph) -- `testCompileClasspath extends testCompile, testCompileOnly, testImplementation` - - Test compile classpath, used when compiling test sources. Used by task compileTestJava. +`api` +使用这种方式就可以更好的实现上文的需求, B 项目会被引用, 那么他就应该是一个库, 所以要考虑到 B 依赖的项目 是否也会被引用 +- B 项目中定义依赖: api A -- `testRuntime(Deprecated) extends runtime, testCompile` - - Additional dependencies for running tests only. Used by task test. Superseded by testRuntimeOnly. +C 项目就能使用 A 中的类了 -- `testRuntimeOnly extends runtimeOnly` - - Runtime only dependencies for running tests. Used by task test. +*************** + +> Java applications -- `testRuntimeClasspath extends testRuntimeOnly, testRuntime, testImplementation` - - Runtime classpath for running tests. +就是 Java 上加上了 MainClass 的配置, 使得打包的jar包可执行 -- `archives` - - Artifacts (e.g. jars) produced by this project. Used by tasks uploadArchives. +************************ -- `default extends runtime` - - The default configuration used by a project dependency on this project. Contains the artifacts - and dependencies required by this project at runtime. +> 其他依赖方式: +1. 使用本地jar依赖 `implementation files('lib/ojdbc-14.jar')` lib 与 src 为同级目录 +1. 项目间依赖 `implementation project(':projectName')` +1. 本地目录依赖 + ```groovy + repositories { + flatDir { + dirs 'libs' + } + } + ``` +********************* +#### 依赖排除以及指定依赖版本 -### 统一依赖管理 -新建一个文件 _dependency.gradle_ +1. 在 configuration 中排除 ```groovy - ext { - ver = [ - junit : '4.12', - ] - libs = [ - "junit" : "junit:junit:$ver.junit", - ] + configurations { + compile.exclude module: 'commons' + all*.exclude group: 'org.gradle.test.excludes', module: 'reports' } ``` -- 在 build.gradle 中引入 `apply from: 'dependency.gradle'` -- 使用依赖时 只需 `compile libs['junit']` 即使在子模块中也是如此使用 +1. 在具体的某个dependency中排除 +```groovy + dependencies{ + // 依赖排除 + compile(''){ + exclude group: '' // 按group排除 + exclude module: '' // 按 artifact 排除 + exclude grop: '', module: '' // 按 group artifact 排除 + } + // 全局依赖排除 + all*.exclude group:'org.unwanted', module: 'iAmBuggy' + // 禁用依赖传递 + compile('com.zhyea:ar4j:1.0') { + transitive = false + } + + configurations.all { + transitive = false + } -### 配置Wrapper -> 在使用IDE生成项目的时候,可以选择gradle的执行目录,可以选`gradle wrapper` 也可以选自己下载解压的完整包 -> 如果使用的不是这个wrapper,那么别人在下载项目后,运行gradle命令就要先安装gradle,使用wrapper更好 -```groovy - task wrapper(type: Wrapper){ - gradleVersion = '4.8' - distributionUrl = '限定访问内网的URL' - distributionPath = '包装器被解压缩放的相对路径' - } + // 强制使用指定版本的依赖 + compile('com.zhyea:ar4j:1.0') { + force = true + } + // 始终使用最新的依赖, 若 1.+ 则是 1.xx版本的最新版 + compile 'com.zhyea:ar4j:+' + + configurations.all { + resolutionStrategy { + force 'org.hamcrest:hamcrest-core:1.3' + } + } + } ``` -- 运行 gradle wrapper 一次即可开始使用包装器的脚本来构建项目了 -- 生成gradle包管理器:`gradle wrapper --gradle-version 2.0` +****************** + +#### 统一管理依赖 +> [完整示例 JavaBase](https://github.com/Kuangcp/JavaBase) + +1. 新建一个文件 _dependency.gradle_ + ```groovy + rootProject.ext { + ver = [ + junit : '4.12', + ] + libs = [ + "junit" : "junit:junit:$rootProject.ver.junit", + ] + } + ``` +1. 在 build.gradle 中引入 `apply from: 'dependency.gradle'` +1. 使用依赖时 只需 `implementation rootProject.libs['junit']` 即使在子模块中也是如此使用 -### 插件 -有多种方式: +### 配置镜像源 +**阿里云** +> [参考: 配置Gradle的镜像为阿里云镜像](https://tvzr.com/change-the-mirror-of-gradle-to-aliyun.html) -```groovy -// 1 -apply plugin: 'java' -// 2 -apply{ - 'java' -} -// 3 -plugins{ - id 'java' -} +> **当前项目的build.gradle** +```Groovy + repositories { + mavenLocal() + def aliyun = "http://maven.aliyun.com/nexus/content/groups/public/" + def abroad = "http://central.maven.org/maven2/" + maven { + url = aliyun + artifactUrls abroad + } + // 码云上自己的仓库 + maven { + url = "https://gitee.com/gin9/MavenRepos/raw/master" + } + mavenCentral() + jcenter() + } ``` -#### 常用插件 -- lombok -> [使用Lombok的正确方式](https://stackoverflow.com/questions/50519138/annotationprocessor-gradle-4-7-configuration-doesnt-run-lombok) | [gradle lombok plugin](https://projectlombok.org/setup/gradle) -[官方文档](https://docs.gradle.org/4.7-rc-1/userguide/java_plugin.html#sec:java_compile_avoidance) +### 插件 +> 引入一个插件有多种方式 + ```groovy - annotationProcessor 'org.projectlombok:lombok:1.18.2' - compileOnly 'org.projectlombok:lombok:1.18.2' - testAnnotationProcessor 'org.projectlombok:lombok:1.18.2' - testCompileOnly 'org.projectlombok:lombok:1.18.2' + // 1 + apply plugin: 'java' + // 2 + apply{ + 'java' + } + // 3 Gradle5 推荐 + plugins{ + id 'java' + } ``` -*************** +#### Lombok +> [详细](/Java/Tool/Lombok.md) -- maven - - `apply plugin: "maven"` 然后就能执行 install等命令了 - - gradle 4.8 用不了 [需要这种方式](https://blog.csdn.net/mxw2552261/article/details/78640338) +#### Maven Publish +- [Maven Publish Plugin](https://docs.gradle.org/current/userguide/publishing_maven.html) -- shadowJar 含依赖的jar进行打包 +#### shadowJar +> 打包为 fat jar 也就是包含所有依赖jar的jar包 -- docker 提供Docker操作 - - `apply plugin: 'docker'` +#### docker +> 提供Docker 的 API +1. 引入 `apply plugin: 'docker'` - buildscript dependencies 中添加`classpath('se.transmode.gradle:gradle-docker:1.2')` -**************** +#### protobuf-gradle-plugin +> [Github: protobuf-gradle-plugin](https://github.com/google/protobuf-gradle-plugin) + ## setting.gradle > 项目的配置信息, 一般存在这个文件的时候, Gradle就会认为当前目录是作为一个完整的根项目的, 并在当前目录添加 .gradle 目录 -> 一般默认内容为 `rootProject.name = ''` -### Gradle多模块的构建 -> [官网文档 creating multi project builds ](https://guides.gradle.org/creating-multi-project-builds/) +- 必须: `rootProject.name = '项目名'` +- 配置子项目 `include('A','B')` + +*************** -> 采用一个文件统一管理依赖, 然后各个子项目独立引用 | [完整示例 JavaBase](https://github.com/Kuangcp/JavaBase)`统一配置依赖, 管理多模块` +# Gradle多模块的构建 +> [Official Doc: creating multi project builds ](https://guides.gradle.org/creating-multi-project-builds/) -_如果要添加一个项目也简单_ -1. 直接新建一个目录 test -1. 目录下新建空的文件 build.gradle -1. 在根项目的 setting.gradle 中的include 加入 test (可以和文件夹不同名, build.gradle配置下就行了, 建议同名) -1. gradle build 整个项目, 就完成了 -1. 最后就是手动的新建项目结构 +> 手动增加一个子项目 +1. mkdir test +1. gradle init 然后删除自动创建的 setting.gradle +1. setting.gradle 中的include 加入 test(项目名不是目录名) ********************************** -#### 另一种方式 -> [参考博客:重拾后端之Spring Boot(六) -- 热加载、容器和多项目](https://www.jianshu.com/p/ac4c00a63750) -> 直接在build.gradle中配置 + +## 另一种多模块的构建方式 +> [参考博客:重拾后端之Spring Boot(六) -- 热加载、容器和多项目](https://www.jianshu.com/p/ac4c00a63750) +> 全部在父项目`build.gradle`中配置 ```groovy // 一个典型的根项目的构建文件结构 @@ -329,13 +460,11 @@ _如果要添加一个项目也简单_ version = "0.0.1" } // * 对于子项目的特殊配置 - project(':common') { - } - project(':api') { - } - project(':report') { - } + project(':common') {} + project(':api') {} + project(':report') {} ``` + ```groovy project(':common') { dependencies { @@ -366,15 +495,34 @@ _如果要添加一个项目也简单_ - [参考更为规范的多项目构建](https://github.com/someok/gradle-multi-project-example) ****************************************************** -# 部署 -## War包 +# 使用 +## 安装到本地仓库 +> 含源码 类似Maven的 install 或 deploy [The Maven Publish Plugin](https://docs.gradle.org/current/userguide/publishing_maven.html) + +```groovy + apply plugin: "maven-publish" + + // publish with source code + task sourceJar(type: Jar) { + from sourceSets.main.allJava + } + publishing { + publications { + mavenJava(MavenPublication) { + from components.java + artifact sourceJar { + classifier "sources" + } + } + } + } +``` + +> `gradle publishToMavenLocal` -## Jar包 -- Gradle默认是只会打包源码,并不会打包依赖(为了更方便依赖的作用) - - [shadow插件官网文档](http://imperceptiblethoughts.com/shadow/) -- 添加 `apply plugin: "maven"` 然后就能和mvn install 一样的执行 gradle install 了 +## 上传至构件仓库 +> [Official Doc](https://docs.gradle.org/current/userguide/publishing_overview.html) -## 上传至构建仓库 > 特别注意使用gpg, 如果按这下面的一堆文档跟着做的话你要保证你的gpg小于等于2.0版本, 不然就卡在这里了 > [参考项目 ](https://github.com/haiyangwu/sonatype) @@ -383,6 +531,58 @@ _如果要添加一个项目也简单_ > [参考博客](http://blog.csdn.net/h3243212/article/details/72374363#%E9%81%87%E5%88%B0%E7%9A%84%E9%97%AE%E9%A2%98) > [最简单的方式就是利用码云等平台创建私服 ](https://blog.csdn.net/kcp606/article/details/79675590) +************************ + +# 打包部署 +> [参考: Building Java Applications](https://guides.gradle.org/building-java-applications/) + +**不依赖Jar的项目** +1. 依据模板新建项目 `gradle init --type java-application` + ```groovy + // 主要是如下配置 + plugins { + // Apply the java plugin to add support for Java + id 'java' + // Apply the application plugin to add support for building an application + id 'application' + } + // Define the main class for the application + mainClassName = 'App' + ``` +1. add this config to build.gradle + ```groovy + jar { + manifest { + attributes 'Main-Class': 'base.Main' + } + } + ``` +1. run : `gradle clean jar && java -jar file` + +**依赖Jar的项目** +- Gradle默认是只会打包源码,并不会打包依赖 + +> 原生方式打包含依赖的Jar,并设置mainClass +```groovy + task uberJar(type: Jar) { + archiveClassifier = 'all-dependency' + + from sourceSets.main.output + + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.findAll { it.name.endsWith('jar') }.collect { zipTree(it) } + } + + manifest { + attributes 'Main-Class': 'com.xxx.Main' + } + } +``` + +> 通过插件 +- [shadow插件官网文档](http://imperceptiblethoughts.com/shadow/) + ## 构建Docker镜像 > [用 Docker、Gradle 来构建、运行、发布一个 Spring Boot 应用](http://www.importnew.com/24671.html) @@ -415,6 +615,7 @@ _build.gradle_ } } ``` + _Dockerfile_ ```dockerfile FROM frolvlad/alpine-oraclejdk8:slim @@ -427,6 +628,6 @@ _Dockerfile_ - `gradle buildDocker` 即可构建镜像 - 运行 `docker run --name web --link postgre:db -p 5678:8889 -it 镜像` 注意其中要关联PostgreSQL的容器 -### 第二种插件方式 +### 插件方式构建Docker镜像 > [参考 通过Gradle使用Docker部署 Spring Boot项目](https://www.jianshu.com/p/7571fa3b394c) diff --git a/Java/Tool/GradleAdvance.md b/Java/Tool/GradleAdvance.md index 1553053..4818743 100644 --- a/Java/Tool/GradleAdvance.md +++ b/Java/Tool/GradleAdvance.md @@ -1,32 +1,42 @@ -`目录 start` - -- [Gradle进阶知识](#gradle进阶知识) - - [Gradle 使用和配置](#gradle-使用和配置) - - [主配置目录](#主配置目录) - - [Gradle 构建块](#gradle-构建块) - - [task](#task) - - [task的依赖关系](#task的依赖关系) - - [终结器 task](#终结器-task) - - [Groovy的POGO类管理配置文件上的版本号](#groovy的pogo类管理配置文件上的版本号) - - [task 的inputs 和 outputs](#task-的inputs-和-outputs) - - [编写和使用自定义task](#编写和使用自定义task) - - [声明task规则](#声明task规则) - - [增量式构建特性](#增量式构建特性) -- [测试模块](#测试模块) - - [单元测试](#单元测试) - - [使用JUnit](#使用junit) - - [使用其他框架 TestNG Spock](#使用其他框架-testng-spock) - - [配置测试执行](#配置测试执行) -- [多语言编程](#多语言编程) - - [处理javascript](#处理javascript) - - [压缩javascript](#压缩javascript) - - [Java 和 Groovy的联合编译](#java-和-groovy的联合编译) - - [Java 和 Scala](#java-和-scala) - - [Jenkin 使用](#jenkin-使用) - - [下载安装和配置](#下载安装和配置) -- [发布自己的构件](#发布自己的构件) - -`目录 end` |_2018-09-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Gradle进阶 +date: 2018-12-11 21:29:29 +tags: + - Gradle + - Advanced +categories: + - Java +--- + +**目录 start** + +1. [Gradle进阶知识](#gradle进阶知识) + 1. [Gradle 使用和配置](#gradle-使用和配置) + 1. [主配置目录](#主配置目录) + 1. [Gradle 构建块](#gradle-构建块) + 1. [task](#task) + 1. [task的依赖关系](#task的依赖关系) + 1. [终结器 task](#终结器-task) + 1. [Groovy的POGO类管理配置文件上的版本号](#groovy的pogo类管理配置文件上的版本号) + 1. [task 的inputs 和 outputs](#task-的inputs-和-outputs) + 1. [编写和使用自定义task](#编写和使用自定义task) + 1. [声明task规则](#声明task规则) + 1. [增量式构建特性](#增量式构建特性) +1. [测试模块](#测试模块) + 1. [单元测试](#单元测试) + 1. [使用JUnit](#使用junit) + 1. [使用其他框架 TestNG Spock](#使用其他框架-testng-spock) + 1. [配置测试执行](#配置测试执行) +1. [多语言编程](#多语言编程) + 1. [处理javascript](#处理javascript) + 1. [压缩javascript](#压缩javascript) + 1. [Java 和 Groovy的联合编译](#java-和-groovy的联合编译) + 1. [Java 和 Scala](#java-和-scala) + 1. [Jenkin 使用](#jenkin-使用) + 1. [下载安装和配置](#下载安装和配置) +1. [发布自己的构件](#发布自己的构件) + +**目录 end**|_2020-06-04 19:41_| **************************************** # Gradle进阶知识 > [gradle api ](https://docs.gradle.org/4.9/dsl/org.gradle.api.Project.html) `所有{}结构 以及配置` @@ -253,15 +263,15 @@ task makeReleaseVersion(type:ReleaseVersionTask){ - 跳过测试 `gradle build -x test` 或者是 `--exclude-task test` 参数 - ## 单元测试 ### 使用JUnit > [使用Junit4](/MyBlog/how-to-use-junit.md) ### 使用其他框架 TestNG Spock -## 配置测试执行 -******************** +************* + +## 配置测试执行 ************************************** diff --git a/Java/Tool/IDEA.md b/Java/Tool/IDEA.md index 1f9c03b..53d1331 100644 --- a/Java/Tool/IDEA.md +++ b/Java/Tool/IDEA.md @@ -1,44 +1,52 @@ -`目录 start` - -- [IDEA 使用笔记](#idea-使用笔记) - - [常识](#常识) - - [常用技巧](#常用技巧) - - [Tomcat的使用](#tomcat的使用) - - [导出为可运行的JAR包 普通Java项目](#导出为可运行的jar包-普通java项目) - - [Springboot热加载](#springboot热加载) - - [Debug](#debug) - - [常用配置](#常用配置) - - [个人IDEA配置](#个人idea配置) - - [IDEA快捷键](#idea快捷键) - - [默认快捷键](#默认快捷键) - - [个人习惯](#个人习惯) - - [File](#file) - - [Coding](#coding) - - [Extract](#extract) - - [Jump](#jump) - - [Search](#search) - - [View](#view) - - [Setting](#setting) - - [常用插件](#常用插件) - - [启动配置](#启动配置) - - [Tips](#tips) - - [Error:Cannot compile Groovy files: no Groovy library is defined for module "XXX"](#errorcannot-compile-groovy-files-no-groovy-library-is-defined-for-module-"xxx") - -`目录 end` |_2018-08-29_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: IDEA +date: 2018-11-14 21:29:09 +tags: + - IDEA +categories: + - Java + - IDE +--- + +💠 + +- 1. [IDEA 使用笔记](#idea-使用笔记) + - 1.1. [常用技巧](#常用技巧) + - 1.1.1. [Tomcat的使用](#tomcat的使用) + - 1.1.2. [导出为可运行JAR](#导出为可运行jar) + - 1.1.3. [Springboot热加载](#springboot热加载) + - 1.1.4. [Debug](#debug) + - 1.2. [常用配置](#常用配置) + - 1.3. [IDEA快捷键](#idea快捷键) + - 1.3.1. [默认快捷键](#默认快捷键) + - 1.3.2. [个人习惯](#个人习惯) + - 1.3.2.1. [File](#file) + - 1.3.2.2. [Coding](#coding) + - 1.3.2.3. [Extract](#extract) + - 1.3.2.4. [Jump](#jump) + - 1.3.2.5. [Search](#search) + - 1.3.2.6. [View](#view) + - 1.3.2.7. [Setting](#setting) + - 1.4. [常用插件](#常用插件) + - 1.4.1. [HTTP Client](#http-client) +- 2. [运行优化](#运行优化) +- 3. [Tips](#tips) + - 3.1. [使用项目外配置文件](#使用项目外配置文件) + - 3.2. [Error:Cannot compile Groovy files](#errorcannot-compile-groovy-files) + - 3.3. [无法启动fcitx输入中文](#无法启动fcitx输入中文) +- 4. [Datagrip](#datagrip) + - 4.1. [Datagrip时区问题](#datagrip时区问题) + +💠 2024-10-02 22:33:00 **************************************** # IDEA 使用笔记 -> [学生授权申请方式](https://sales.jetbrains.com/hc/zh-cn/articles/207154369) | [click](http://idea.lanyus.com/) -> [rover12421博客](http://ju.outofmemory.cn/feed/2608/?page=1) | [IDEA主题](http://www.riaway.com/index.php) +> [Doc](https://www.jetbrains.com/help/idea/getting-started.html) -## 常识 -- project 就是一个工作空间,一个Module就是一个项目 -- 新建一个empty项目可以作为一个工作空间,放很多module,新建的时候不要选中任何目录,*光标选中JRE库*,再菜单栏点击File然后new, - - 不然项目结构很乱, 会在光标停留的文件夹下新建module -- 一个项目一个空间也是可以的,就是跨项目查看代码会有点麻烦,而且每个项目的IDEA的配置都是不一样的,同样的配置可能要配置很多遍 - -- 在新版中提供的load unload 模块功能,必须要重启idea, 才能让maven生效 emmm +> [学生授权申请方式](https://sales.jetbrains.com/hc/zh-cn/articles/207154369) +> [IDEA Tutorial](https://github.com/judasn/IntelliJ-IDEA-Tutorial) +> [W3C idea 教程](https://www.w3cschool.cn/intellij_idea_doc/intellij_idea_doc-q3ke2coy.html) ## 常用技巧 1. 鼠标方法上悬停显示javadoc 勾选 General -> show quick documentation on mouse move @@ -47,7 +55,7 @@ ### Tomcat的使用 > 直接配置解压的即可, 然后Idea会在 用户目录下的Idea主目录中配置一个专门放Tomcat的配置和日志等文件, 和原Tomcat进行了隔离, 这样就不会影响到该Tomcat. -### 导出为可运行的JAR包 普通Java项目 +### 导出为可运行JAR - File -> project structure ->artifact 里面设置好 引入的库,设置Main类,引用的jar包的相对classpath - Build artifact -> Build - Maven或者Gradle的话直接就能得到,不过也要配置一下main @@ -57,41 +65,47 @@ - Ctrl Shift A 快捷搜索 automatically 在Build下的 Compiler,勾选 `Build project automatically` 自动构建 - (如果旁边有提示说不会在运行和debug执行, 那么就要勾选并行) `Compile independent modules in parallel` -- **Ctrl Shift A 快捷搜索 Registry 进入后找到 compiler.automake.allow.when.app.running 勾选 +- **Ctrl Shift A** 快捷搜索 Registry 进入后找到 compiler.automake.allow.when.app.running 勾选 - 加入devtools依赖 | [DevTools的官方文档](https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-devtools.html#using-boot-devtools) -********************************* +************************ + ### Debug > debug还是比较强大的, 会在行末显示这一行的变量值 - [在Intellij IDEA中使用Debug](http://www.cnblogs.com/chiangchou/p/idea-debug.html) _横排的八个按钮_ -> `Show Execution Point (Alt + F10)`:如果你的光标在其它行或其它页面,点击这个按钮可跳转到当前代码执行的行。 -> `Step Over (F8)`:步过,一行一行地往下走,如果这一行上有方法不会进入方法。 -> `Step Into (F7)`:步入,如果当前行有方法,可以进入方法内部,一般用于进入自定义方法内,不会进入官方类库的方法,如第25行的put方法。 -> `Force Step Into (Alt + Shift + F7)`:强制步入,能进入任何方法,查看底层源码的时候可以用这个进入官方类库的方法。 -> `Step Out (Shift + F8)`:步出,从步入的方法内退出到方法调用处,此时方法已执行完毕,只是还没有完成赋值。 -> `Drop Frame (默认无)`:回退断点,后面章节详细说明。 -> `Run to Cursor (Alt + F9)`:运行到光标处,你可以将光标定位到你需要查看的那一行,点击按钮,代码会运行至光标行,而不需要打断点。 -> `Evaluate Expression (Alt + F8)`:计算表达式,后面章节详细说明。 +| 操作 | 备注 | +|:----|:----| +|`Show Execution Point (Alt + F10)`| 如果你的光标在其它行或其它页面,点击这个按钮可跳转到当前代码执行的行。 +|`Step Over (F8)` | 步过,一行一行地往下走,如果这一行上有方法不会进入方法。 +|`Step Into (F7)` | 步入,如果当前行有方法,可以进入方法内部,一般用于进入自定义方法内,不会进入官方类库的方法,如第25行的put方法。 +|`Force Step Into (Alt + Shift + F7)`| 强制步入,能进入任何方法,查看底层源码的时候可以用这个进入官方类库的方法。 +|`Step Out (Shift + F8)` | 步出,从步入的方法内退出到方法调用处,此时方法已执行完毕,只是还没有完成赋值。 +|`Drop Frame (默认无)` | 回退断点,后面章节详细说明。 +|`Run to Cursor (Alt + F9)` | 运行到光标处,你可以将光标定位到你需要查看的那一行,点击按钮,代码会运行至光标行,而不需要打断点。 +|`Evaluate Expression (Alt + F8)` | 计算表达式,后面章节详细说明。 _竖向的七个按钮_ -> Rerun 'xxxx':重新运行程序,会关闭服务后重新启动程序。 -> Update 'tech' application (Ctrl + F5):更新程序,一般在你的代码有改动后可执行这个功能。而这个功能对应的操作则是在服务配置里,如图2.3。 -> Resume Program (F9):恢复程序,比如,你在第20行和25行有两个断点,当前运行至第20行,按F9,则运行到下一个断点(即第25行),再按F9,则运行完整个流程,因为后面已经没有断点了。 -> Pause Program:暂停程序,启用Debug。目前没发现具体用法。 -> Stop 'xxx' (Ctrl + F2):连续按两下,关闭程序。有时候你会发现关闭服务再启动时,报端口被占用,这是因为没完全关闭服务的原因,你就需要查杀所有JVM进程了。 -> View Breakpoints (Ctrl + Shift + F8):查看所有断点,后面章节会涉及到。 -> Mute Breakpoints:哑的断点,选择这个后,所有断点变为灰色,断点失效,按F9则可以直接运行完程序。再次点击,断点变为红色,有效。如果只想使某一个断点失效,可以在断点上右键取消Enabled + +| 操作 | 备注 | +|:----|:----| +|`Rerun 'xxxx'` |重新运行程序,会关闭服务后重新启动程序。 | +|`Update 'tech' application (Ctrl + F5)`|更新程序,一般在你的代码有改动后可执行这个功能。而这个功能对应的操作则是在服务配置里,如图2.3。 | +|`Resume Program (F9)` |恢复程序,比如,你在第20行和25行有两个断点,当前运行至第20行,按F9,则运行到下一个断点(即第25行),再按F9,则运行完整个流程,因为后面已经没有断点了。 | +|`Pause Program` |暂停程序,挂起所有线程 | +|`Stop 'xxx' (Ctrl + F2)` |连续按两下,关闭程序。有时候你会发现关闭服务再启动时,报端口被占用,这是因为没完全关闭服务的原因,你就需要查杀所有JVM进程了。 | +|`View Breakpoints (Ctrl + Shift + F8)` |查看所有断点,后面章节会涉及到。 | +|`Mute Breakpoints` |哑的断点,选择这个后,所有断点变为灰色,断点失效,按F9则可以直接运行完程序。再次点击,断点变为红色,有效。如果只想使某一个断点失效,可以在断点上右键取消Enabled | **个人思考** -1. 当断点 F8 步过 到一行代码后, 这个方法没有抛出异常什么的, idea的面板上的那些属性, 断点什么的都没了, 只有一个 app is running , 这个意思就是这行代码还在执行中, 很有可能就是死循环... - - 没有断点的时候, 也就是说这个死循环一直挂在这边, 不会被回收, 后面的代码也被阻塞了, 所以代码才诡异的不符合自己构想的调用链执行 +1. 当断点 F8 步过 到一行代码后, 这个方法没有抛出异常结束, idea的面板上的属性, 断点状态都没了, 只有一个 app is running 。 + - 那很有可能是线程陷入死循环或者长久的锁/IO等待,后面的代码也不会执行了, 所以代码才会没按预期的调用链执行,可通过间隔一段时间的两次jstack 查看目标线程的栈,确认该问题 ************************* -## 常用配置 +## 常用配置 **自定义类文件头** - 依次找到配置项: `File->settings->Editor->File and Code Templates->` - 如果自己要每个文件都单独设置头部, 就依次点击Class Interface Enum ...进行设置 @@ -106,179 +120,211 @@ _例如修改为如下_ */ ``` -**自定义缩写模板** -在 `Setting -> Editor -> Live Templates` 设置项下可以看到已有的配置, 新建一个即可 +**自定义模板** +在 `Setting -> Editor -> Live Templates` 设置项下可以看到已有的配置 + +1. `$SELECTION$` surrounding with 功能标记 +1. `$END$` 光标结束位置 + +************************ + +- 关闭特定文件的自动格式化 Code Style -> Formatter 中设置 *.md *.sql +************************ + +> 个人IDEA配置 +- 代码字体 + 1. Fira Code Retina 14 0.9 + 1. IBM Plex Mono SemiBold 15 0.9 +- 设置字体 + 1. Robot Mono Medium for Powershell 14 + 1. Cascadia Code -### 个人IDEA配置 -- 字体Fira Code Retina 14 0.9 +[IDEA 自定义配置](https://github.com/Kuangcp/Configs/tree/master/IDEA) +- colors 代码配色方案 +- keymaps 快捷键映射 +- temlates 代码模板 ************************ ## IDEA快捷键 -> [参考博客: Intellij IDEA神器居然还有这些小技巧](https://my.oschina.net/samgege/blog/1808622) +> [参考: Intellij IDEA神器居然还有这些小技巧](https://my.oschina.net/samgege/blog/1808622) > 如果一时不习惯idea, 可以在 设置中 的keymap 选择eclipse系列即可 ### 默认快捷键 > 可以在 Help -> Keymap Reference 看到内置PDF文档 -> `个人觉得最简单就是打开 Setting -> keymap -> Find Action by shortcut, 任意的按键, 然后查看对应的内容` - -| Ctrl | Shift | Alt | Key | Action | -|:----:|:----:|:----:|:----:|:----| -|C| | | **E** |可以显示最近编辑的文件列表 -| |S| | **鼠标左击** | 可以关闭文件 -|C|S| | **Backspace** | 可以跳转到上次编辑的地方 -|C| | | **F12** |可以弹窗显示当前文件中类的结构(快速跳转方法和属性) -|C| | | **F7** | 可以查询当前元素在当前文件中的引用,然后按F3可以选择 -|C| | | **N** | 可以快速打开类 -|C|S| | **N** | 可以快速打开文件 -| | |A| **Q** | 可以看到光标处的元素的Javadoc -|C| | | **W** | 可以选择单词继而语句继而行继而函数 -| | |A| **F1** | 可以将正在编辑的元素在各个面板中定位 -|C| | | **P** | 可以显示参数信息 -|C|S| | **Insert** | 可以选择剪贴板内容并插入 -| | |A| **Insert** | 可以生成构造器/Getter/Setter等 -|C| |A| **V** | 重构代码, 将选中的代码抽离出称为一个变量 Variable -|C| |A| **T** | 可以把代码包在一块内,例如try/catch -| | |A| **Up/Down** | 可在方法间快速移动 -| |S| | **Escape** | 不仅可以把焦点移到编辑器上而且还可以隐藏当前(或最后活动的)工具窗口。 -|C|S| | **左/右** | 调节以上窗口分隔线 -|C|S| | **Enter** | 就能自动补全代码的分号,括号 -|C| | | **Space** | 代码提示 -|C| |A| **Space** | 代码提示 包括类,变量,方法等内容 -|C|S| | **Space** | 智能提示 -|C| | | **P** | 方法参数提示 -| | |A| **F1** | 查找当前文件所在位置(项目,结构,maven等等) -|C|S| | **F7** | 要先选中文本然后按键 高亮显示所有该文本,按Esc高亮消失。 -| | |A| **F3** | 要先选中文本然后按键,然后 F3逐个往下查找相同文本,并高亮显示。 -|C| | | **B** | 快速打开光标处的类或方法的 _声明或调用_ -|C| |A| **B** | 查看抽象类或接口的实现方法 等价的,B键 可以换成鼠标左键单击 -|C|S|A| **N** | 可以快速打开符号(方法名, 变量名等等,全局搜索) -|C| | | **O** | 可以选择父类的方法进行重写 -| |S| | **Shift** | 也就是双击, 就可以快速搜索类了 -|C|S| | **F** |全局搜索, 不含Shift 就是简单当前文件搜索- 快速打开类/文件/符号时,可以使用通配符,也可以使用缩写 -|C| | | **J** | Live Templates! 例如 fori 等快速模板代码 -|C|S| | **F7** | 可以高亮当前元素在当前文件中的使用 -|C| |A| **Up/Down** | 可以快速跳转搜索结果 -|C|S| | **J** | 可以整合两行 -| | |A| **F8** | 是计算变量值 +> `个人觉得最简单就是打开 Setting -> keymap -> Find Action by shortcut, 任意的按键, 然后查看对应的内容` + +| Ctrl | Shift | Alt | Key | Action | +|:----: |:----:|:----:|:----:|:----| +|C| | | `E` |可以显示最近编辑的文件列表 | +| |S| | `鼠标左击` | 可以关闭文件| +|C|S| | `Backspace` | 可以跳转到上次编辑的地方| +|C| | | `F12` |可以弹窗显示当前文件中类的结构(快速跳转方法和属性)| +|C| | | `F7` | 可以查询当前元素在当前文件中的引用,然后按F3可以选择| +|C| | | `N` | 可以快速打开类| +|C|S| | `N` | 可以快速打开文件| +| | |A| `Q` | 可以看到光标处的元素的Javadoc| +|C| | | `W` | 可以选择单词继而语句继而行继而函数| +| | |A| `F1` | 可以将正在编辑的元素在各个面板中定位| +|C| | | `P` | 可以显示参数信息| +|C|S| | `Insert` | 可以选择剪贴板内容并插入| +| | |A| `Insert` | 可以生成构造器/Getter/Setter等| +| |S|A| `Insert` | 进入/退出 列编辑模式 | +|C| |A| `V` | 重构代码, 将选中的代码抽离出称为一个变量 Variable | +|C| |A| `T` | 可以把代码包在一块内,例如try/catch| +| | |A| `Up/Down` | 可在方法间快速移动| +| |S| | `Escape` | 不仅可以把焦点移到编辑器上而且还可以隐藏当前(或最后活动的)工具窗口。| +|C|S| | `左/右` | 调节以上窗口分隔线| +|C|S| | `Enter` | 就能自动补全代码的分号,括号| +|C| | | `Space` | 代码提示| +|C| |A| `Space` | 代码提示 包括类,变量,方法等内容| +|C|S| | `Space` | 智能提示| +|C| | | `P` | 方法参数提示| +| | |A| `F1` | 查找当前文件所在位置(项目,结构,maven等等)| +|C|S| | `F7` | 要先选中文本然后按键 高亮显示所有该文本,按Esc高亮消失。| +| | |A| `F3` | 要先选中文本然后按键,然后 F3逐个往下查找相同文本,并高亮显示。| +|C| | | `B` | 快速打开光标处的类或方法的 _声明或调用_| +|C| |A| `B` | 查看抽象类或接口的实现方法 等价的,B键 可以换成鼠标左键单击| +|C|S|A| `N` | 可以快速打开符号(方法名, 变量名等等,全局搜索)| +|C| | | `O` | 可以选择父类的方法进行重写| +| |S| | `Shift` | 也就是双击, 就可以快速搜索类了| +|C|S| | `F` |全局搜索, 不含Shift 就是简单当前文件搜索- 快速打开类/文件/符号时,可以使用通配符,也可以使用缩写| +|C| | | `J` | Live Templates! 例如 fori 等快速模板代码| +|C|S| | `F7` | 可以高亮当前元素在当前文件中的使用| +|C| |A| `Up/Down` | 可以快速跳转搜索结果| +|C|S| | `J` | 可以整合两行| +| | |A| `F8` | 是计算变量值| ****** -1. 在调试程序时查看任何表达式值的一个容易的方法就是在编辑器中选择文本(可以按几次 **Ctrl-W 组合键更有效地执行这个操作)然后按 Alt-F8 。 -1. 要打开编辑器脱字符处使用的类或者方法 Java 文档的浏览器,就按 Shift-F1 (右键菜单的 External JavaDoc )。 +1. `Ctrl Enter`, 如果光标在一行的中间, 可以迅速让光标跳转到下一行 +1. 在调试程序时查看任何表达式值的一个容易的方法就是在编辑器中选择文本(可以按几次 `Ctrl-W` 组合键更有效地执行这个操作)然后按 `Alt-F8` 。 +1. 要打开编辑器脱字符处使用的类或者方法 Java 文档的浏览器,就按 `Shift-F1` (右键菜单的 External JavaDoc )。 1. 要使用这个功能须要把加入浏览器的路径,在“ General ”选项中设置( Options | IDE Settings ),另外还要把创建的 Java 文档加入到工程中( File | Project Properties )。 ************************************************************************** ### 个人习惯 -> 从eclipse风格继承而来和原生Idea快捷键结合, 自己修改的风格, 如果需要, 则在[个人配置文件夹](https://github.com/Kuangcp/Configs/tree/master/Idea)下找到对应的jar导入即可 +> 从eclipse风格继承而来和原生Idea快捷键结合, 自己修改的风格, 如果需要, 则在[个人配置文件夹](https://github.com/Kuangcp/Configs/tree/master/Idea)下找到对应的jar导入即可 > 个人的习惯特点是左手完成大部分快捷键的任务, 因为还没习惯脱离鼠标工作 | Ctrl | Shift | Alt | Key | Action | |:----:|:----:|:----:|:----:|:----| -| | |A| **Enter** | 自动修复 -|C| | | **Q** | 显示javadoc -|C| | | **B** | 显示定义处或者调用处 -|C|S| | **T** | 自动创建或跳转 Test -|C|S| | **F7** | 高亮显示光标所在元素所有出现过的地方(就是搜索) -|C| |A| **L** | 快速格式化代码 -|C| |A| **O** | 优化导入的类和包 -|C| | | **Esc** | 格式化并优化导入 -| | |A| **Esc** | 停止正在运行的运行项 -| | |A| **1** | 运行上次的运行项 -| | |A| **2** | Debug上次的运行项 -| | | | **F10** | 运行光标所在处的运行项 -| |S|A| **F10** | 运行选择的运行项 -| |S|A| **F9** | Debug选择的运行项 +| | |A| `Enter` | 自动修复| +|C| | | `Q` | 显示javadoc| +|C| | | `B` | 显示定义或者调用| +|C| |A| `B` | 显示其实现| +| | |A| `U` | 面板显示调用处 | +|C|S| | `T` | 自动创建或跳转 Test| +|C|S| | `F7` | 高亮显示光标所在元素所有出现过的地方(就是搜索)| +|C| |A| `L` | 快速格式化代码 | +|C| |A| `O` | 优化导入的类和包| +|C| | | `Esc` | 格式化并优化导入| +| | |A| `Esc` | 停止正在运行的运行项| +| | |A| `1` | 运行上次的运行项| +| |S|A| `1` | 显示运行项面板| +| | |A| `2` | Debug上次的运行项| +| |S|A| `2` | 显示Debug运行项面板| +| | | | `F10` | 运行光标所在处的运行项| +| |S|A| `F10` | 运行选择的运行项| +| |S|A| `F9` | Debug选择的运行项| #### File | Ctrl | Shift | Alt | Key | Action | |:----:|:----:|:----:|:----:|:----| -|C| | | **W** | 选中代码 | -|C| | | **E** | 显示最近打开的文件 | -| | |A| **E** | 显示最近打开的文件 | -| |A|S| **C** | 最近更改的文件| -|C| | | **N** | 快速搜索类 | -|C|S| | **N** | 搜索所有文件| -|C|S|A| **N** | 按文件内容字符的搜索,也能按类名首字母搜索| -| | |A| **左/右** | 左右切换打开的文件| -|C| | | **鼠标左键** | 在文件标签页上单击, 即可在文件管理器中打开该文件 | +|C| | | `W` | 选中代码 | +|C| | | `E` | 显示最近打开的文件 | +| | |A| `E` | 显示最近打开的文件 | +| |A|S| `C` | 最近更改的文件| +|C| | | `N` | 快速搜索类 | +|C|S| | `N` | 搜索所有文件| +|C|S|A| `N` | 按文件内容字符的搜索,也能按类名首字母搜索| +| | |A| `左/右` | 左右切换打开的文件| +|C| | | `鼠标左键` | 在文件标签页上单击, 即可在文件管理器中打开该文件 | +||S|A| `S` |保存当前窗口的context| +||S|A| `L` |加载保存过的context| +||S|A| `X` |清除当前所有窗口的context| #### Coding > [Doc: 2018.2](https://www.jetbrains.com/help/idea/2018.2/using-code-editor.html?utm_content=2018.2&utm_medium=link&utm_source=product&utm_campaign=IU) | Ctrl | Shift | Alt | Key | Action | |:----:|:----:|:----:|:----:|:----| -| | |A| **C** | build 项目 -|C|S| | **V** | 显示最近的粘贴板记录 -|C| | | **O** | 选择要重写的方法 -|C| | | **I** | 选择要实现的方法 -|C| |A| **Insert** | 生成代码(如get,set方法,构造函数等) -| |S|A| **K** | 重命名 -|C| | | **X** | 剪切一行 -|C| | | **D** | 删除一行 -|C| | | **Y** | 复制一行到下一行 -|C| | | **Q** | 显示注释文档 或者 Alt+鼠标中键 -|C|S| | **Space** | 智能提示代码的补全 -| |S|A| **上/下** | 代码上/下移动 -|C|S| | **上/下** | 代码上/下移动 | `光标在一行上就是移动一行, 光标在代码块的首行就是移动整个代码块` -|C| | | **J** | 提示代码片段 也就是 Live Template -| | |A| **J** | 选中字符 -|C| | | **Space** | 智能补全 -|C|S| | **Space** | 结合上下文补全 -|C| | | **W** | 选中代码,连续按会有其他效果 -|C|S| | **U** | 选中字符大小写转换 +| | |A| `C` | build 项目| +|C|S| | `V` | 显示最近的粘贴板记录| +|C| | | `O` | 选择要重写的方法| +|C| | | `I` | 选择要实现的方法 | +|C| |A| `Insert` | 生成代码(如get,set方法,构造函数等)| +| |S|A| `K` | 重命名| +|C| | | `X` | 剪切一行| +|C| | | `D` | 删除一行| +|C| | | `Y` | 复制一行到下一行| +|C| | | `Q` | 显示注释文档 或者 Alt+鼠标中键| +|C|S| | `Space` | 智能提示代码的补全| +| |S|A| `上/下` | `代码行`上/下移动| +|C|S| | `上/下` | `代码块`上/下移动 | +|C| | | `J` | 提示代码片段 也就是 `Live Templates` | +| | |A| `J` | 选中字符 Add Selection for Next Occurence| +|C| | | `J` | 提示 Live Templates | +|C| |A| `J` | 提示 Surround Live Templates | +|C| | | `Space` | 智能补全| +|C|S| | `Space` | 结合上下文补全| +|C| | | `W` | 选中代码,连续按会有其他效果| +|C|S| | `U` | 选中字符大小写转换 | +| |S|A| `3` | 查看方法完整调用链 | #### Extract | Ctrl | Shift | Alt | Key | Action | |:----:|:----:|:----:|:----:|:----| -|C| |A| **T** | 根据选中代码(或者光标前的代码片段)**包裹 在块中** 例如try/catch if for ... -|C| |A| **J** | 根据选中代码(或者光标前的代码片段)**包裹在自定义的模板中** -|C| |A| **M** | 根据选中代码(或者光标前的代码片段)抽离成 **函数** -|C| |A| **V** | 根据选中代码(或者光标前的代码片段)抽离成 **变量** 自定义为 `C A S J` -|C| |A| **C** | 根据选中代码(或者光标前的代码片段)抽离成 **常量** -|C| |A| **F** | 根据选中代码(或者光标前的代码片段)抽离成 **属性** -|C| |A| **P** | 根据选中代码(或者光标前的代码片段)抽离成 **方法参数** -|C|S|A| **P** | 根据选中代码(或者光标前的代码片段)抽离成 **函数式方法参数** +|C| |A| `T` | 根据选中代码(或者光标前的代码片段)**包裹 在块中** 例如try/catch if for ...| +|C| |A| `J` | 根据选中代码(或者光标前的代码片段)**包裹在自定义的模板中**| +|C| |A| `M` | 根据选中代码(或者光标前的代码片段)抽离成 **函数** | +|C| |A| `V` | 根据选中代码(或者光标前的代码片段)抽离成 **变量** 自定义为 `C A S J`| +|C| |A| `C` | 根据选中代码(或者光标前的代码片段)抽离成 **常量**| +|C| |A| `F` | 根据选中代码(或者光标前的代码片段)抽离成 **属性**| +|C| |A| `P` | 根据选中代码(或者光标前的代码片段)抽离成 **方法参数**| +|C|S|A| `P` | 根据选中代码(或者光标前的代码片段)抽离成 **函数式方法参数**| #### Jump | Ctrl | Shift | Alt | Key | Action | |:----:|:----:|:----:|:----:|:----| -| | |A| **上/下** | 在方法间快速移动定位 -|C| |A| **左/右** | 后退/前进 至光标的上一个位置 -| | | | **F2** | 高亮错误或警告快速定位 组合 Shift 前进 -|C| | | [ ] | 可以跳到大括号的开头结尾 -| | |A| **S** | 跳转类的方法或属性 +| | |A| `上/下` | 在方法间快速移动定位| +|C| |A| `左/右` | 后退/前进 至光标的上一个位置| +|C| | | `,` | 高亮错误或警告快速定位 组合 Shift 前进, 原为F2| +|C| | | `[ ]` | 可以跳到大括号的开头结尾| +| | |A| `S` | 跳转类的方法或属性| #### Search | Ctrl | Shift | Alt | Key | Action | |:----:|:----:|:----:|:----:|:----| -|C| | | **N** | 查找类(所有类范围,包括引用的包) -| | |A| **L** | 查找类 `原默认是 Ctrl N` -|C|S| | **N** | 查找文件名 -|C|S|A| **N** | 符号查找: 类中的方法或变量 -|C| | | **R** | 替换文本 -|C| | | **F** | 查找文本 -| | |A| **K** | 查找任意文件`原默认是双击Shift` +|C| | | `N` | 查找类(所有类范围,包括引用的包)| +| | |A| `L` | 查找类 `原默认是 Ctrl N`| +| | |A| `I` | 查找文件名| +|C| | | `R` | 替换文本| +|C| | | `F` | 查找文本| +|C|S| | `F` | 全局搜索字符串| +| | |A| `K` | 查找任意文件`原默认是双击Shift` | #### View | Ctrl | Shift | Alt | Key | Action | |:----:|:----:|:----:|:----:|:----| -|C| | | **H** | 生成类结构图并显示 -| | |A| **H** | 类结构图窗口 -| | |A| **A** | 目录结构窗口 -| | |A| **X** | Run窗口 -| | |A| **D** | Debug窗口 -| | |A| **T** | TODO的窗口 `Ctrl +/-` 显示和折叠TODO -| | |A| **G** | Gradle窗口 -| | |A| **M** | Maven窗口 -| | |A| **Z** | Spring窗口 -| | |A| **V** | VCS窗口 -| | |A| **N** | 定位当前文件在目录结构的位置 -| | |A| **3** | 数据库工具窗口 -| | |A| **.** | 终端窗口 -| |S|A| **V** | 数据库 Console 窗口 -|C|S| | **左/右** | 调节以上工具窗口与编辑器窗口的分隔线位置 +|C|S| | **左/右** | 调节以上工具窗口与编辑器窗口的分隔线位置| +| |S|A| `H` | 显示/隐藏 所有工具窗口| +|C| |A| **;** | 关闭活跃中的Tab| +|C| | | `H` | 生成类结构图并显示| +| | |A| `H` | 类结构图窗口| +| | |A| `A` | 目录结构窗口| +| | |A| `X` | Run窗口| +| | |A| `D` | Debug窗口| +| | |A| `T` | TODO的窗口 `Ctrl +/-` 显示和折叠TODO| +| | |A| `G` | Gradle窗口| +| | |A| `M` | Maven窗口| +| | |A| `Z` | Spring窗口| +| | |A| `V` | VCS窗口| +| | |A| `N` | 导航至文件所在目录等...| +| | |A| `3` | 数据库工具窗口| +| | |A| `.` | 终端窗口| +| |S|A| `V` | 数据库 Console 窗口| > 在任一工具窗口, 按`ESC`都会让焦点回到编辑器 `Shift ESC` 就能关闭工具窗口并让焦点回到编辑器 > 以上的窗口都是默认显示小bar的, 我为了窗口更大就设置为了默认隐藏, 如果想显示, 可以双击Alt, 在第二下按住不动, 鼠标就能进行点击了 @@ -286,44 +332,41 @@ _例如修改为如下_ #### Setting | Ctrl | Shift | Alt | Key | Action | |:-:|:-:|:-:|:-:|:--| -|C|S| | **A** | 搜索设置项的位置 -|C|S|A| **?** | 进行一些关键设置 +|C|S| | `A` | 搜索设置项的位置| +|C|S|A| `?` | 进行一些关键设置 | **Tips** - 代码模板 Live Template (**fori** **notnull**...) 输入完成后,按Tab或者Enter,生成代码。 ********************* ## 常用插件 -1. Alibaba Java Code Guidelineshuo +1. Atom Material Icons +1. rainbow brackets 将括号变成彩色, 更方便查看 +1. Alibaba Java Code Guideline - 阿里巴巴的代码规范插件 - [《阿里巴巴Java开发规约》IDEA插件与Eclipse插件使用指南](https://zhuanlan.zhihu.com/p/30191998) - -1. TestMe - - 快速创建测试类 - -1. TestNG - - TestNG 测试框架的集成 - +1. TestMe 快速创建测试类 +1. TestNG 测试框架的集成 +1. Junit4 Parallel Runner 并行执行单元测试 1. lombok - 插件商店中搜索 lombok 安装重启idea即可 - 配置 Build,Execution > Compiler > Annotation Processors 勾选上即可使用lombok的注解 - -1. Jrebel - - 热部署插件, 需要付费 - -1. GoogleTranslation - - **Ctrl Alt 1** 快速翻译选中的单词和语句 - -1. http-client - - https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html - +1. Jrebel 热部署插件, 需要付费 +1. GoogleTranslation **Ctrl Alt 1** 快速翻译选中的单词和语句 +1. FindBugs 1. Docker 1. Kubernates 1. Maven helper -1. rainbow brackets 将括号变成彩色, 更方便查看 +1. Maven Project Version 快速修改整个项目所有模块的版本号 +1. Grep console 控制台搜索工具 +1. Code with me 远程协作插件 +1. jclasslib Bytecode Viewer 字节码查看插件 +1. JarEditor Jar包编辑,无需解压 +1. GsonFormatPlus : json转Class定义 +1. POJO to JSON : Class定义转JSON +1. Sequence Diagram 查看代码时序图 ************************** - > 内置插件 - 为了节省内存, 禁用无关插件, 把插件列表中所有插件全看一遍 @@ -332,14 +375,59 @@ _例如修改为如下_ 1. cucumber java 1. cucumber groovy +### HTTP Client +[Jetbrain Help](https://www.jetbrains.com/help/idea/http-client-in-product-code-editor.html) + +1. 登录设置 cookie + ```sh + POST http://localhost/coolsoftware/rest/authentication?login=username&password=1234 + > {% client.global.set("yourVariable", response.headers.valueOf('Set-Cookie')); %} + + 按实际情况来设置,例如 提取接口返回(JSON类型)中的 data 字段: + > {% client.global.set("token", response.body.data); %} + ``` + ********************** -## 启动配置 + +# 运行优化 > [官网文档](https://www.jetbrains.com/help/idea/increasing-memory-heap.html) > [IntelliJ IDEA 内存优化最佳实践](http://blog.oneapm.com/apm-tech/426.html) +> [参考: 记一次idea性能调优](http://www.cnblogs.com/nevermorewang/p/10061377.html) + +- 如果有 node_modules 等大量文件的目录, 可以右键该目录设置忽略这个目录的文件索引 -## Tips +************************ + +# Tips + +## 使用项目外配置文件 +IDEA中Java项目启动时Console里灰色被折叠的第一行是完整的Java命令,可以复制出classpath参数,在头部追加自定义目录,然后把这一长串填回到VM Options中 +就能实现自定义目录下的文件对IDEA中classpath下的同名文件替换 + +## Error:Cannot compile Groovy files +> Error:Cannot compile Groovy files: no Groovy library is defined for module "XXX" -### Error:Cannot compile Groovy files: no Groovy library is defined for module "XXX" 1. Project Structure -> 找到 XXX 项目 右击 -> Add -> 选择 Groovy +## 无法启动fcitx输入中文 +1. 启动脚本 idea.sh 头部追加 `source ~/.xpfrofile` + +`~/.xpfrofile` +```sh +export XMODIFIERS=@im=fcitx +export QT_IM_MODULE=fcitx +``` + +# Datagrip +> 执行SQL时底部的耗时拆分为 execution fetching。 +- execution: JDBC提交Statement执行,到返回ResultSet的耗时 +- fetching: 读取ResultSet全部数据的耗时 + +> 大批量执行DDL语句时,卡顿 +- 由于特性设计是DDL执行后Datagrip会自动去拉库的元数据更新到本地,当有大批量的DDL要执行时(例如删除一批表),每一条DDL的执行都会有一个执行结果tab加上元数据的更新,Datagrip就会越来越卡直到卡死 + +## Datagrip时区问题 +> [DataGrip设置时区](https://blog.csdn.net/qiaominghe/article/details/82757206) + +`-Duser.timezone=Asia/Shanghai` diff --git a/Java/Tool/Jacoco.md b/Java/Tool/Jacoco.md new file mode 100644 index 0000000..2893306 --- /dev/null +++ b/Java/Tool/Jacoco.md @@ -0,0 +1,69 @@ +--- +title: Jacoco +date: 2018-12-17 21:27:14 +tags: + - CI +categories: + - Java + - Blog +--- +**目录 start** + +1. [Jacoco](#jacoco) + 1. [安装](#安装) + 1. [Maven插件方式](#maven插件方式) + 1. [Jenkins集成](#jenkins集成) + +**目录 end**|_2020-06-04 19:41_| +**************************************** + +# Jacoco +> 一款Java平台的代码覆盖率工具 + +## 安装 + +### Maven插件方式 +1. 添加插件 + +```xml + + org.jacoco + jacoco-maven-plugin + 0.8.2 + + + prepare-agent + + prepare-agent + + + + report + prepare-package + + report + + + + post-unit-test + test + + report + + + + target/jacoco.exec + + target/jacoco-ut + + + + + + +``` + +2. 执行: `mvn test` + +### Jenkins集成 +> [Official Doc](https://wiki.jenkins.io/display/JENKINS/JaCoCo+Plugin) diff --git a/Java/Tool/Jetty.md b/Java/Tool/Jetty.md deleted file mode 100644 index 9c2ea28..0000000 --- a/Java/Tool/Jetty.md +++ /dev/null @@ -1,22 +0,0 @@ -`目录 start` - -- [Jetty](#jetty) - - [配置](#配置) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Jetty - - -[参考博客: Jetty使用教程(一)——开始使用Jetty ](http://www.cnblogs.com/yiwangzhibujian/p/5832597.html) - -## 配置 -_自身log配置_ -> [相关](http://zetcode.com/java/jetty/logging/) -_resources/jetty-logging.properties_ 内容如下开启DEBUG -```conf - org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StrErrLog - org.eclipse.jetty.LEVEL=DEBUG - jetty.logs=logs -``` - diff --git a/Java/Tool/Lombok.md b/Java/Tool/Lombok.md index 4ebdb8f..f5569b1 100644 --- a/Java/Tool/Lombok.md +++ b/Java/Tool/Lombok.md @@ -1,75 +1,56 @@ -`目录 start` - -- [Lombok](#lombok) - - [何为Lombok](#何为lombok) - - [为什么要用](#为什么要用) - - [为什么不要用](#为什么不要用) - - [个人见解](#个人见解) -- [配置](#配置) - - [Maven](#maven) - - [普通Java项目](#普通java项目) - - [Groovy和Java项目使用Lombok](#groovy和java项目使用lombok) -- [使用](#使用) - - [注解使用](#注解使用) - - [POJO常用](#pojo常用) - - [日志相关](#日志相关) - - [异常相关](#异常相关) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Lombok +date: 2018-12-20 10:25:56 +tags: + - Lombok +categories: + - Java +--- + +**目录 start** + +1. [Lombok](#lombok) + 1. [何为Lombok](#何为lombok) + 1. [为什么要用](#为什么要用) + 1. [为什么不要用](#为什么不要用) + 1. [个人见解](#个人见解) +1. [配置](#配置) + 1. [Maven](#maven) + 1. [普通Java项目](#普通java项目) + 1. [Groovy和Java项目使用Lombok](#groovy和java项目使用lombok) + 1. [Gradle](#gradle) +1. [使用](#使用) + 1. [注解使用](#注解使用) + 1. [POJO常用](#pojo常用) + 1. [日志相关](#日志相关) + 1. [异常相关](#异常相关) +1. [实现原理](#实现原理) + +**目录 end**|_2020-04-27 23:42_| **************************************** # Lombok ## 何为Lombok -> [Lombok Project 官网](https://projectlombok.org/) | [Github : ](https://github.com/rzwitserloot/lombok) +> [Github: lombok](https://github.com/rzwitserloot/lombok) | [Official site](https://projectlombok.org/) ## 为什么要用 -> 简化JavaBean 更为直观 省去了Setter Getter toString hashCode 构造器等方法 -> 一定有人就会跳出来说 在IDE中几个快捷键的事情,何必这么复杂, ->> 那他们一定是没有遇到修改的时候吧, 改个属性的名字,类型, 对应的方法你需要改吧, 但是使用lombok就不用担心了 +> 简化 JavaBean 省去了 Setter Getter toString hashCode 等方法,提供 生成构造器 Builder Log 等功能 ## 为什么不要用 -> 暂时还没有想到 +> 破坏了阅读代码的完整性, 当使用了构造器这样的注解, 如果想通过看构造器的引用方来找到调用方, 这时候是没有办法的 只能通过查看类的所有引用方再一个个找 +> 常见IDE都没有原生支持, 必须要安装对应的插件才能正常编译运行项目 ## 个人见解 -> Lombok在IDE中安装插件是为了编译和构建中能够动态的添加Getter Setter 等方法, 而在Maven或者Gradle中添加是为了注解能够引用得到?? +> Lombok在IDE中安装插件是为了编译和构建中能够动态的添加Getter Setter 等方法 而在Maven或者Gradle中添加是为了引入注解的包 +> 取决于团队风格,用不用都不是大问题 `Java14` 新出的 `record` 类型也能满足lombok部分需求 + +************************************************ # 配置 ## Maven -> [官方文档](https://projectlombok.org/setup/maven) +> [Official Guide](https://projectlombok.org/setup/maven) + ### 普通Java项目 -```xml - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.8 - 1.8 - UTF-8 - - - - org.apache.maven.plugins - maven-surefire-plugin - - true - - - - org.projectlombok - lombok-maven-plugin - 1.16.8.0 - - - generate-sources - - delombok - - - - - -``` ```xml org.projectlombok @@ -77,7 +58,7 @@ 1.16.10 ``` -而static有还是有可能被new出多个实例的 + ### Groovy和Java项目使用Lombok _配置编译插件_ @@ -125,23 +106,63 @@ _添加依赖_ ``` +## Gradle + +> [使用Lombok的正确方式](https://stackoverflow.com/questions/50519138/annotationprocessor-gradle-4-7-configuration-doesnt-run-lombok) + +> [gradle lombok plugin](https://projectlombok.org/setup/gradle) +> [Official Guide](https://docs.gradle.org/4.7-rc-1/userguide/java_plugin.html#sec:java_compile_avoidance) + +```groovy + annotationProcessor 'org.projectlombok:lombok:1.18.2' + compileOnly 'org.projectlombok:lombok:1.18.2' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.2' + testCompileOnly 'org.projectlombok:lombok:1.18.2' +``` +************************* + # 使用 -- Lombok 注解在线帮助文档:http://projectlombok.org/features/index. +- [Lombok 注解在线帮助文档](http://projectlombok.org/features/index) ## 注解使用 + ### POJO常用 -- `@Data` :注解在类上;提供类所有属性的 getter 和 setter 方法,此外还提供了equals、canEqual、hashCode、toString 方法 -- `@Setter`:注解在属性上;为属性提供 setter 方法 -- `@Getter`:注解在属性上;为属性提供 getter 方法 -- `@NoArgsConstructor`:注解在类上;为类提供一个无参的构造方法 -- `@AllArgsConstructor`:注解在类上;为类提供一个全参的构造方法 + +| 注解 | 范围 | 功能 | +|:---|:--- |:--- | +| @Data| 类 | Getter Setter RequiredArgsConstructor ToString EqualsAndHashCode 的集合 +| @Setter| 类 | 为属性提供 setter 方法 +| @Getter| 类 | 为属性提供 getter 方法 +| @NoArgsConstructor| 类 | 为类提供一个无参的构造方法 +| @AllArgsConstructor| 类 | 为类提供一个全参的构造方法 +| @Builder| 类 方法 构造器 | 生成 构造器模式 模板代码 +| @Delegate| 属性,方法 | 将属性的方法委派到当前对象上 常用于嵌套的POJO ### 日志相关 -1. @Log4j -1. @Log4j2 -1. @Slf4j `用上logback, 但是这个不是一个接口规范么` TODO +> [Official log](https://projectlombok.org/features/log) + +- @CommonsLog + - `private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);` +- @Flogger + - `private static final com.google.common.flogger.FluentLogger log = com.google.common.flogger.FluentLogger.forEnclosingClass();` +- @JBossLog + - `private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);` +- @Log + - `private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());` +- @Log4j + - `private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);` +- @Log4j2 + - `private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);` +- @Slf4j + - `private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);` +- @XSlf4j +- `private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);` ### 异常相关 1. [@SneakyThrows](https://projectlombok.org/features/SneakyThrows) +# 实现原理 +> [参考: Lombok原理分析与功能实现 ](https://blog.mythsman.com/2017/12/19/1/) + +Lombok的注解都是编译期源码注解(RetentionPolicy.SOURCE), 运行期是拿不到这些注解的 diff --git a/Java/Tool/MapStruct.md b/Java/Tool/MapStruct.md new file mode 100644 index 0000000..3c5af82 --- /dev/null +++ b/Java/Tool/MapStruct.md @@ -0,0 +1,25 @@ +--- +title: MapStruct +date: 2021-05-17 00:27:57 +tags: +categories: +--- + +**目录 start** + +1. [MapStruct](#mapstruct) + +**目录 end**|_2021-05-17 00:27_| +**************************************** +# MapStruct +> [Official Site](https://mapstruct.org/) + +实现方式为 通过注解在编译期生成对应接口的实现类(模板代码 手动 get set) 到 target/generated-sources 目录下, 并将该目录加入 class-path + +优点: +- 生成get set 模板代码 性能好 + +缺点: +- 类改动需要重新清空并编译,可能造成新加字段没有正常生成对应的 get set 代码 +- 集合属性的null值转换为 空List,Map等需要手动注解声明。 +- 泛型集合会自动做类型转换可能引发问题 diff --git a/Java/Tool/Maven.md b/Java/Tool/Maven.md index 8d77697..6c888f8 100644 --- a/Java/Tool/Maven.md +++ b/Java/Tool/Maven.md @@ -1,61 +1,70 @@ -`目录 start` - -- [Maven](#maven) - - [1.安装](#1安装) - - [1.1.Maven常用命令](#11maven常用命令) - - [从jar安装到本地库](#从jar安装到本地库) - - [1.2.settings.xml配置](#12settingsxml配置) - - [配置镜像源](#配置镜像源) - - [阿里云](#阿里云) - - [配置本地仓库](#配置本地仓库) - - [2.maven配置](#2maven配置) - - [2.1.eclipse中配置](#21eclipse中配置) - - [2.2.配置插件](#22配置插件) - - [2.3.POM配置文件详解](#23pom配置文件详解) - - [2.4.配置代码编译版本](#24配置代码编译版本) - - [3.构建](#3构建) - - [3.1.使用maven构建多模块的项目](#31使用maven构建多模块的项目) - - [3.2.Maven多配置环境](#32maven多配置环境) - - [3.3.部署](#33部署) - - [4.maven的依赖](#4maven的依赖) - - [4.1 依赖的范围](#41-依赖的范围) - - [依赖的传递](#依赖的传递) - - [4.2.处理项目间依赖方法](#42处理项目间依赖方法) - - [4.3.依赖冲突](#43依赖冲突) - - [排除依赖](#排除依赖) - - [4.4.继承](#44继承) - - [5.使用maven新建Web3.0项目](#5使用maven新建web30项目) - - [5.1.添加web容器](#51添加web容器) - - [5.1.2.Jetty](#512jetty) - - [5.1.3.Tomcat](#513tomcat) - - [5.2.加入Servlet的API包](#52加入servlet的api包) - - [6.常用插件](#6常用插件) - - [6.1.lombok](#61lombok) - - [6.2 protobuf](#62-protobuf) - - [7.构建工具对比](#7构建工具对比) - - [7.1.Maven和Ant的区别一](#71maven和ant的区别一) - - [7.2.Maven的优势](#72maven的优势) - - [8.发布构件](#8发布构件) - - [9.配置私服](#9配置私服) - - [nexus](#nexus) - - [码云](#码云) - - [创建仓库](#创建仓库) - - [引用仓库中的构件](#引用仓库中的构件) - - [Gradle](#gradle) - - [Maven](#maven) - -`目录 end` |_2018-08-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Maven +date: 2018-12-20 10:25:26 +tags: + - Maven +categories: + - Java +--- + +💠 + +- 1. [Maven](#maven) + - 1.1. [安装](#安装) + - 1.1.1. [Maven常用命令](#maven常用命令) + - 1.1.1.1. [从jar安装到本地库](#从jar安装到本地库) + - 1.1.2. [settings.xml配置](#settingsxml配置) + - 1.1.2.1. [配置镜像源](#配置镜像源) + - 1.1.2.1.1. [阿里云](#阿里云) + - 1.1.2.2. [配置本地仓库](#配置本地仓库) + - 1.2. [maven配置](#maven配置) + - 1.2.1. [主配置文件详解](#主配置文件详解) + - 1.2.2. [配置代码编译版本](#配置代码编译版本) + - 1.3. [构建](#构建) + - 1.3.1. [使用maven构建多模块的项目](#使用maven构建多模块的项目) + - 1.3.2. [Profiles](#profiles) + - 1.3.3. [测试](#测试) + - 1.3.4. [打包部署](#打包部署) + - 1.3.4.1. [assembly](#assembly) + - 1.3.4.2. [shade](#shade) + - 1.4. [Maven 依赖](#maven-依赖) + - 1.4.1. [依赖类型](#依赖类型) + - 1.4.2. [依赖的范围](#依赖的范围) + - 1.4.3. [依赖的传递](#依赖的传递) + - 1.4.4. [处理项目间依赖方法](#处理项目间依赖方法) + - 1.4.5. [依赖冲突](#依赖冲突) + - 1.4.5.1. [排除依赖](#排除依赖) + - 1.4.6. [依赖本地jar](#依赖本地jar) + - 1.5. [常用插件](#常用插件) + - 1.5.1. [lombok](#lombok) + - 1.5.2. [protobuf](#protobuf) + - 1.5.3. [Maven Enforcer Plugin](#maven-enforcer-plugin) + - 1.5.4. [Maven Deploy plugin](#maven-deploy-plugin) + - 1.6. [构建工具对比](#构建工具对比) + - 1.6.1. [Maven和Ant的区别一](#maven和ant的区别一) + - 1.6.2. [Maven的优势](#maven的优势) + - 1.7. [发布构件到中央仓库](#发布构件到中央仓库) + - 1.7.1. [发布到 Github Package](#发布到-github-package) +- 2. [配置私服](#配置私服) + - 2.1. [nexus](#nexus) + - 2.2. [码云](#码云) + - 2.2.1. [创建仓库](#创建仓库) + - 2.2.2. [引用仓库中的构件](#引用仓库中的构件) + - 2.2.2.1. [Gradle](#gradle) + - 2.2.2.2. [Maven](#maven) + +💠 2024-08-06 11:01:51 **************************************** # Maven > [官网](https://maven.apache.org/) | [官网手册](https://maven.apache.org/guides/) | [http://takari.io/ 在线练习网](http://takari.io/) -> [关于Maven的完整博客](http://tengj.top/2018/01/01/maven/) +> [关于Maven的完整博客](http://tengj.top/2018/01/01/maven/) -## 1.安装 -* 下载zip包解压,将bin目录配置至PATH(最好是配置MAVEN_HOME然后引用) +## 安装 +- 下载zip包解压,将bin目录配置至PATH(最好是配置MAVEN_HOME然后引用) -### 1.1.Maven常用命令 -> mvn [插件]:[目标] [参数] -> mvn [阶段] +### Maven常用命令 +> `mvn [插件]:[目标] [参数]` +> `mvn [阶段]` * mvn archetype:generate :创建 Maven 项目 * -DgroupId=组织名/公司网址的反写+项目名 @@ -78,16 +87,21 @@ - `-DskipTests=true` 不执行测试用例,但编译测试用例类生成相应的class文件至target/test-classes下 - `-Dmaven.javadoc.skip=true` 跳过文档生成 +- 打包指定模块 `mvn package -pl a,b,c -am` + - -am 同时打包指定模块所依赖的上游模块 + - -amd 同时打包 使用了 指定模块 的下游模块 + _配置文件中配置_ ```xml - - org.apache.maven.plugins - maven-surefire-plugin - - true - - + + org.apache.maven.plugins + maven-surefire-plugin + + true + + ``` + #### 从jar安装到本地库 ``` mvn install:install-file @@ -98,7 +112,8 @@ mvn install:install-file -Dpackaging=jar ``` ***** -### 1.2.settings.xml配置 + +### settings.xml配置 > 要特别注意 `settings.xml` 后者覆盖前者 加载顺序是: >> `maven目录/conf/setting.xml` >> `用户目录下/.m2/setting.xml` @@ -118,17 +133,15 @@ mvn install:install-file ``` #### 配置本地仓库 `localRepository节点` -***************** -## 2.maven配置 -### 2.1.eclipse中配置 -> 高版本自带maven,需要注意的是eclipse的JRE运行环境目录要选择jdk下的JRE目录 -### 2.2.配置插件 +***************** +## maven配置 > [插件地址](http://maven.apache.org/plugins/index.html) -### 2.3.POM配置文件详解 +### 主配置文件详解 > [版本说明](http://www.blogjava.net/RomulusW/archive/2008/05/04/197985.html) +`pom.xml` ```xml @@ -197,32 +210,21 @@ mvn install:install-file ``` -### 2.4.配置代码编译版本 +### 配置代码编译版本 ```xml - - - - maven-compiler-plugin - 3.1 - - 1.8 - 1.8 - UTF-8 - - - - + + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + UTF-8 + + ``` -## 3.构建 -### 3.1.使用maven构建多模块的项目 -`.gitignore文件` -``` - .idea/ - *.iml - target/ - *.log -``` +## 构建 +### 使用maven构建多模块的项目 `父项目pom文件` ``` xml com.github.kuangcp @@ -239,6 +241,7 @@ mvn install:install-file 1.8 ``` + `子项目pom文件` ```xml website @@ -250,17 +253,135 @@ mvn install:install-file 1.0-SNAPSHOT ``` -### 3.2.Maven多配置环境 + +### Profiles +> [Official Doc](http://maven.apache.org/guides/introduction/introduction-to-profiles.html) +> [参考: Guide to Maven Profiles](https://www.baeldung.com/maven-profiles) + +`简单配置` +```xml + + + development + + true + + + + + production + + + +``` + > [Maven 如何为不同的环境打包](https://www.zybuluo.com/haokuixi/note/25985) `开发、测试和产品环境` -- [ ] 子项目编译打包各自独立,怎么整合成一个 +- 使用 test profile 执行命令 `mvn clean package -P test` + +### 测试 +> mvn test + +- 跳过测试 `mvn test -DskipTests` +- 执行指定测试类 `mvn test -Dtest=类名` -### 3.3.部署 -> [Java项目部署方式整理](/Java/AdvancedLearning/Deploy.md) +### 打包部署 +获取项目版本 `mvn help:evaluate -Dexpression=project.version -q -DforceStdout` + +> [deploy with source](https://stackoverflow.com/questions/4725668/how-to-deploy-snapshot-with-sources-and-javadoc) +```xml + + maven-source-plugin + + + attach-sources + verify + + jar-no-fork + + + + +``` + +**不依赖Jar的项目** +> [Demo项目](https://gitee.com/gin9/codes/ri4x8cut3awgh0e271lfb54) + +**依赖Jar的项目** +#### assembly +> [Maven Doc](http://maven.apache.org/plugins/maven-assembly-plugin/index.html) + +```xml + + maven-assembly-plugin + 3.0.0 + + + + com.xxx.Main + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + +``` + +> [Maven 引入 JDK 自带 tools.jar 注意事项](https://www.sunyongfei.cn/archives/305/) + +#### shade + +```xml + + org.apache.maven.plugins + maven-shade-plugin + 3.2.1 + + + package + + shade + + + + + com.xxx.Main + + + + + + +``` + +************************ + +> [Maven实战(九)——打包的技巧](http://www.infoq.com/cn/news/2011/06/xxb-maven-9-package) +> [Maven打包成可执行jar](https://blog.csdn.net/u013177446/article/details/53944424) +> [参考: 使用MAVEN打包可执行的jar包](https://www.jianshu.com/p/afb79650b606) + +> war和jar一样使用 +- Springboot项目能够做到, 其实就是 Main 方法, 然后配置了一个Servlet的加载类就可以当war用了 + - [通过Maven构建打包Spring boot,并将config配置文件提取到jar文件外](http://lib.csdn.net/article/java/65574) + +> [一个项目生成若干不同内容的Jar](https://stackoverflow.com/questions/2424015/maven-best-practice-for-generating-multiple-jars-with-different-filtered-classes) ****************** -## 4.maven的依赖 -### 4.1 依赖的范围 +## Maven 依赖 +### 依赖类型 +默认是jar类型。扩展了 pom(引入复合项目时使用 例如 groovy-all) war maven-plugin test-jar 等。 + +### 依赖的范围 > 依赖范围就是用来控制依赖和三种classpath(编译classpath,测试classpath、运行classpath)的关系 - `compile`:编译依赖范围。如果没有指定,就会默认使用该依赖范围。使用此依赖范围的Maven依赖,对于编译、测试、运行三种classpath都有效。 @@ -280,13 +401,13 @@ mvn install:install-file | runtime | | Y | Y | JDBC的实现Jar | | system | Y | Y | | Maven仓库之外的类库文件 | -#### 依赖的传递 +### 依赖的传递 - 比如一个account-email项目为例 - account-email有一个compile范围的spring-code依赖, - spring-core有一个compile范围的commons-logging依赖, - 那么commons-logging就会成为account-email的compile的范围依赖,commons-logging是account-email的一个传递性依赖 -### 4.2.处理项目间依赖方法 +### 处理项目间依赖方法 ``` 项目A依赖B A项目 pom.xml中配置依赖 (构件三要素) @@ -295,7 +416,7 @@ B项目 先clean package A 项目 compile ``` -### 4.3.依赖冲突 +### 依赖冲突 - 依赖路径短优先 - 1 A->B->C->X(jar文件) - 2 A->C->X(jar文件) @@ -305,137 +426,63 @@ A 项目 compile #### 排除依赖 `对应的标签中添加` ```xml - - - org.springframework.boot - spring-boot-starter-logging - - + + + org.springframework.boot + spring-boot-starter-logging + + ``` -### 4.4.继承 -> 新建一个项目作为父项目 -> 然后在需要引用父项目的子项目pom文件中, 加上parent 标签里面写上 父项目的三要素 +### 依赖本地jar +```xml + + xxx + name + 1.0.0 + system + ${project.basedir}/lib/xxx-name-1.0.0.jar + +``` ************************* -## 5.使用maven新建Web3.0项目 -> [网络maven仓库](http://mvnrepository.com/) -- 新建maven 选择webapp 然后输入三要素 -- 但是因为模板默认的是web2.3,所以要手动修改成3.0 -- 1. pom文件中添加插件 编译部分 -```xml - - maven-compiler-plugin - 3.0 - - 1.8 - 1.8 - - -``` -- 2.navigator目录模式下 修改相关文件,把2.3改成3.0 -- 3.eclipse中右击改动Facets 然后maven-update一下就可以了 +## 常用插件 -### 5.1.添加web容器 -#### 5.1.2.Jetty -- http://mvnrepository.com/ 里找到想要的版本,加入即可 -特别注意 NIO的原因,静态文件在服务器启动的时候不能更改,需要找到maven仓库下的org/eclipse/jettyjetty-webapp/ -下的jar包中的default配置文件,把useFileBuffer标签的 true 改成false +### lombok +> [详细](/Java/Tool/Lombok.md) -```xml - - org.mortbay.jetty - jetty-maven-plugin - 8.1.16.v20140903 - - - - package - - run - - - - - stop - 9999 - 1 - ${project.basedir}/src/main/resources/jetty-context.xml - - - / - - - - 80 - 60000 - - - - -``` -- 部署成功后,使用jetty:run 即可运行起服务器 +### protobuf +> [maven 插件 ](https://www.xolstice.org/protobuf-maven-plugin/) `本质还是要使用系统安装的protoc, 然后插件实现了自动编译文件` -#### 5.1.3.Tomcat -- 去Tomcat官网 找到maven plugins进入找到想要的版本即可 +- [protoc-jar](https://github.com/os72/protoc-jar-maven-plugin)`自动识别平台, 使用对应的编译器编译得到java文件, 但是目前还有一些bug` -```xml - - org.apache.tomcat.maven - tomcat6-maven-plugin - 2.2 - - - - package - - run - - - - - - http://localhost:8080/manager/text - tomcat6 - /mavenProject - - -``` -- 部署完成后 tomcat7:deploy 运行服务器 +### Maven Enforcer Plugin +> [Official Site](http://maven.apache.org/enforcer/maven-enforcer-plugin/) -### 5.2.加入Servlet的API包 -```xml - - javax.servlet - javax.servlet-api - 3.0.1 - provided - -``` +[Maven Enforcer Plugin - Baeldung](https://www.baeldung.com/maven-enforcer-plugin) -## 6.常用插件 -### 6.1.lombok -> 极大的简化了bean的代码量 +### Maven Deploy plugin +> [maven-deploy-plugin](https://maven.apache.org/plugins/maven-deploy-plugin/deploy-mojo.html) +**跳过 deploy** ```xml - - org.projectlombok - lombok - 1.16.10 - provided - + + org.apache.maven.plugins + maven-deploy-plugin + 3.0.0-M1 + + true + + ``` -### 6.2 protobuf -> [maven 插件 ](https://www.xolstice.org/protobuf-maven-plugin/) `本质还是要使用系统安装的protoc, 然后插件实现了自动编译文件` - **************************** -## 7.构建工具对比 +## 构建工具对比 > [码农翻身:小李的Build之路(上)](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513207&idx=1&sn=cbfad70e656fc50c4fff18678a282b95&scene=21#wechat_redirect) | [码农翻身:小李的Build之路(下)](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513212&idx=1&sn=280647b0c6cd265f85590f64d3216bee&scene=21#wechat_redirect) `这个小故事讲述了ant到maven的演化` -### 7.1.Maven和Ant的区别一 +### Maven和Ant的区别一 1. ant脚本是可以直接运行在maven中的。maven和ant最大的差别就是在于maven的编译以及所有的脚本都有一个基础,就是POM(project object model)。这个模型定义了项目的方方面面,然后各式各样的脚本在这个模型上工作,而ant完全是自己定义,显然maven更胜一筹。 @@ -447,7 +494,7 @@ A 项目 compile 5. maven目前不足的地方就是没有象ant那样成熟的GUI界面,不过mavengui正在努力中。目前使用maven最好的方法还是命令行,又快又方便 -### 7.2.Maven的优势 +### Maven的优势 - 协同开发的基本规范,为大家提供方便的协作的模式,能增加代码的复用,提高生产率。 - 提供方便,规范化的打包方法,是公司完成自动构建系统的核心部分,能帮助提高敏捷开发的效率(敏捷开发提倡尽早集成)。 @@ -457,30 +504,89 @@ A 项目 compile - 大量的开源项目使用了maven。 ******************** -## 8.发布构件到公共仓库 -- [ ] Jforg mvnrepository + +## 发布构件到中央仓库 +`mvn clean javadoc:jar source:jar deploy -U -DskipTests=true` 带文档和源码跳过测试发布,注意javadoc:jar对javadoc有严格要求,文档不符合规范会发布失败,可按需使用该插件。 + +跳过模块不deploy +```xml +true +``` > [将项目发布到 maven 中央仓库踩过的坑](http://blog.csdn.net/h3243212/article/details/72374363) -> [发布Maven构件到中央仓库](https://www.xncoding.com/2018/01/27/tool/maven-central.html) +> [发布Maven构件到中央仓库](https://www.xncoding.com/2018/01/27/tool/maven-central.html) +> [android-library-publish-to-jcenter](https://github.com/panpf/android-library-publish-to-jcenter) + +### 发布到 Github Package +> 1. 申请token(clasic) 具有package的读写权限 +> 2. settings.xml 配置 Github 源 +```xml + + github + + + central + https://repo1.maven.org/maven2 + + + github + https://maven.pkg.github.com/ + + true + + + + +``` +```xml + + + + github + GitHub OWNER Apache Maven Packages + https://maven.pkg.github.com/{username}/{repo} + + +``` ********************* -## 9.配置私服 + +# 配置私服 > 不用去跑审核流程, 私有, 快速, 便捷 -### nexus -> 需要运行软件, 一般公司内部局域网使用, 如果自己有服务器也能开放给公众使用 [参考博客: maven私服搭建及gradle上传](https://www.jianshu.com/p/b1fe26d5b8c8) -### 码云 -> 利用公开仓库来搭建私服 | [参考博客: 使用git仓库搭建maven私服 ](https://my.oschina.net/polly/blog/1649362) -#### 创建仓库 +## nexus +> 需要运行软件, 一般公司内部局域网使用, 如果自己有服务器也能开放给公众使用 [参考: maven私服搭建及gradle上传](https://www.jianshu.com/p/b1fe26d5b8c8) + +1. 设置 RELEASE 不可重复 deploy 管理后台 Repositories -> Releases -> ANALYZE -> Configuration -> Deployment Policy 设置为 Disable Redeploy + +```xml + + + nexus-releases + http://192.168.0.221:8081/nexus/content/repositories/releases + + + nexus-snapshots + http://192.168.0.221:8081/nexus/content/repositories/snapshots + + +``` + +************************ + +## 码云 +> 利用公开仓库来搭建私服 | [参考: 使用git仓库搭建maven私服 ](https://my.oschina.net/polly/blog/1649362) + +### 创建仓库 > 当然了在各个托管平台都可以的, 只不过码云是国内的, 毕竟要快 github gitlab bitbucket 就.... 1. 创建好一个空的公开仓库 2. 使用统一的groupId, 这样就会放到默认目录 `~/.m2/repository/` 下 只要在groupId对应的目录下 git init 3. 只需在项目中执行install, 然后在此目录进行提交即可 -#### 引用仓库中的构件 -##### Gradle +### 引用仓库中的构件 +#### Gradle build.gradle 中添加 ```groovy repositories { @@ -489,14 +595,14 @@ repositories { } } ``` -##### Maven +#### Maven pom.xml中添加 ```xml - - - mvnrepo - mvn repository - https://gitee.com/用户名/仓库/raw/master - - + + + mvnrepo + mvn repository + https://gitee.com/用户名/仓库/raw/master + + ``` diff --git a/Java/Tool/MavenAdvance.md b/Java/Tool/MavenAdvance.md new file mode 100644 index 0000000..9953210 --- /dev/null +++ b/Java/Tool/MavenAdvance.md @@ -0,0 +1,26 @@ +--- +title: Maven进阶 +date: 2018-12-20 10:25:45 +tags: + - Maven + - Advanced +categories: + - Java +--- + +**目录 start** + +1. [Maven Advance](#maven-advance) + 1. [Custom Maven plugin](#custom-maven-plugin) + +**目录 end**|_2020-06-04 19:41_| +**************************************** +# Maven Advance + +## Custom Maven plugin + +> [official doc](http://maven.apache.org/guides/mini/guide-configuring-plugins.html) + +> [github: maven plugin](https://github.com/search?q=maven+plugin) + +> [参考: custom plugin](https://javabeat.net/writing-a-custom-plugin-for-maven/) diff --git a/Java/Tool/Tomcat.md b/Java/Tool/Tomcat.md deleted file mode 100644 index 1a6ea57..0000000 --- a/Java/Tool/Tomcat.md +++ /dev/null @@ -1,235 +0,0 @@ -`目录 start` - -- [Tomcat](#tomcat) - - [Tips](#tips) - - [原理](#原理) - - [配置运行](#配置运行) - - [配置解压版 Tomcat](#配置解压版-tomcat) - - [IDE中配置运行](#ide中配置运行) - - [编码](#编码) - - [虚拟目录](#虚拟目录) - - [默认主页](#默认主页) - - [虚拟主机](#虚拟主机) - - [配置 GZip压缩](#配置-gzip压缩) - - [配置IO方式](#配置io方式) - - [Tomcat Native](#tomcat-native) - - [Web容器和Web服务器的区别](#web容器和web服务器的区别) - - [【web容器】](#web容器) - - [【Web服务器】](#web服务器) - - [【应用程序服务器(The Application Server)】](#应用程序服务器(the-application-server)) - - [【serverlet】](#serverlet) - - [【Tomcat】](#tomcat) - - [【Tomcat与Web服务器、应用服务器的关系】](#tomcat与web服务器、应用服务器的关系) - - [一、Tomcat 与应用服务器](#一、tomcat-与应用服务器) - - [二、Tomcat 与 Web 服务器](#二、tomcat-与-web-服务器) -- [优化](#优化) - - [Tomcat僵死问题](#tomcat僵死问题) -- [Tomcat和Jetty](#tomcat和jetty) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Tomcat -> [官方网站](http://tomcat.apache.org/) - -- 官网上大致有: - - Tomcat `7 8 8.5 9` 大版本 - - Tomcat Native `优化Tomcat性能,提升速倍` - - Apache Standard Taglib `JSTL的实现` - - Tomcat Connectors `用于连接IIS Apache` [官方文档](http://tomcat.apache.org/connectors-doc/index.html) - -> [一款功能强大的Tomcat管理监控工具](https://zhuanlan.zhihu.com/p/35557373?group_id=967469270317457408) -> [psi-probe](https://github.com/psi-probe/psi-probe)`Tomcat监控管理工具` - -## Tips -- servletContextLisner 和Spring环境的加载顺序要注意 -- [Tomcat启动卡住,因为random](https://www.jianshu.com/p/576d356dc163) -************* -## 原理 -> 更多查看 `Tomcat那些事儿` 公众号 -> [Tomcat目录部署与Context描述文件context.xml ](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650859355&idx=1&sn=2122baf040ae337dba90201a48b4e11c&chksm=f1329888c645119eec4473e11beaf988c48ce02c52151502086595de59b65dd4bd7cf129530e&scene=21#wechat_redirect) -> | [Tomcat配置文件解析与Digester](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650859293&idx=1&sn=3c017b2675bb59fda8ae037b7a1e6cb4&chksm=f13298cec64511d8183a23f1b3110bc6b65e8742c6e76391a51c552d86c0bc81a34fab8d0a60&scene=21#wechat_redirect) -> | [Servlet到底是单例还是多例你了解吗?](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=401278436&idx=1&sn=7d28750b7cff1f706efb82c7fcaa73c5&scene=21#wechat_redirect) -> | [Tomcat类加载器以及应用间class隔离与共享 ](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650859298&idx=1&sn=8856375f2268fc33a6bb3fbc6932eca7&chksm=f13298f1c64511e77ef1d77d28272840ca56f62da6e11928c78827e8ec53f937f812a4b49aa0&scene=21#wechat_redirect) -> | [啥,Tomcat里竟然还有特权应用? ](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650859476&idx=1&sn=8be7a37b59a5d167998f6695a1606d39&chksm=f1329807c6451111d2a1c379221655dc87dd105b067f894bfb202d1f9f283bad310a5cdc2277&scene=21#wechat_redirect) -> | [你了解JMX在Tomcat的应用吗?](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=401135587&idx=1&sn=610950fda2eceb3683a9fe45078f1a83&scene=21#wechat_redirect) - -## 配置运行 -- 精简版, 适合放在服务器 - - [tomcat-clean-8.5.31](http://cloud.kuangcp.top/tomcat-clean-8.5.31.zip) | [tomcat-clean-9.0.8](http://cloud.kuangcp.top/tomcat-clean-9.0.8.zip) -- 个人配置版,适合个人图形化使用 - - [tomcat-admin-9.0.8](http://cloud.kuangcp.top/tomcat-admin-9.0.8.zip) | [tomcat-admin-8.5.31](http://cloud.kuangcp.top/tomcat-admin-8.5.31.zip) - -### 配置解压版 Tomcat -`Windows 平台` -1. 在setclasspath中把前几行关于JAVA_HOME,JRE_HOME的路径改成自己的 -2. 系统中添加catalina_home环境变量 -3. 运行tomcatw.exe配置里面所有的路径( JDK JRE ) -4. 双击tomcat.exe启动Tomcat - -`Linux 平台` -- 下载解压,然后 bin 目录下执行 `chmod +x *.sh` - -> [参考博客](http://blog.csdn.net/kkgbn/article/details/52071109) - -`配置管理账号 tomcat-users.xml` -```xml -   -   -   -   - - -``` -- 其中admin-gui是为了能访问manger的界面,manager-secret是为了可以上传war文件 - -`配置本机外可访问管理页面` -- /conf/Catalina/localhost/下 添加manager.xml -```xml - - - -``` -#### IDE中配置运行 -> [你一定不知道IDE里的Tomcat是怎么工作的! ](https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=401107149&idx=1&sn=908bd8ba76b38417570056795626c163&scene=21#wechat_redirect) - -- 虽然IDE也是引用到解压的Tomcat路径, 但是只是使用了可执行文件, 配置文件和一系列中间文件都是和原Tomcat隔离的, 这样也保证了原Tomcat能单独运行不受影响 - -### 编码 -- 编辑conf/下的server.xml,配置Connector项 `URIEncoding="UTF-8"` -- 浏览器表单utf-8 xml utf-8 乱码 服务器 浏览器 乱码 使用response.setContentType("text/html; charset=utf-8");。 -- 无效方法response.setChaoactorEncoding; xml文件里面有乱码,saxreader会生成document错误。 -- 浏览器表单get方式:需要重新编码获得字符串 浏览器表单post方式 request.setCharactorEncoding(utf-8); -- 自己建立的工程里面的web.xml继承了conf/web.xml.只需要重写自己的web.xml相关的配置的参数就可以覆盖其功能 - - -### 虚拟目录 -`指定webapp目录外的可访问的文件` -- 方法1:conf/server.xml - - 当中找到host标签里 添加一行 `` - -- 方法2:conf/catalina/localhost/myxml.xml - - `context放置进来< Context docBase="c:/mydsadf"/>` - - `访问方式http://localhsot:8080/myxml/` - -#### 默认主页 -`web.xml` -```xml - < welcome-file-list> - < welcome-file>index.html< /welcome-file> - < /welcome-file-list> -``` - -#### 虚拟主机 -`server.xml` -```xml - - - < Context path="/" docBase="d:/webA" /> - > - -``` -- `File f=new File("/information.xml");`这个写法是错的,空指针异常 -- `request.getParameter`返回字符串,如果表单里面是空的,就返回长度为零的字符串。 - -#### 配置 GZip压缩 -> [tomcat nginx开启Gzip原博客](http://www.imooc.com/article/15304) - -- 修改配置文件:/conf/server.xml -`原文件` -``` - -``` -`修改成` -```xml - -``` -#### 配置IO方式 -> 默认http1.1是nio, 还有aio ajp bio - -## Tomcat Native -> [官方文档](http://tomcat.apache.org/native-doc/) | [参考博客: tomcat安装与配置native,apr](https://blog.csdn.net/shangruo/article/details/52776212) - - -************************* -## Web容器和Web服务器的区别 -### 【web容器】 - -`何为容器:` -- 容器是一种服务调用规范框架,J2EE 大量运用了容器和组件技术来构建分层的企业级应用。在 J2EE 规范中,相应的有 WEB Container 和 EJB Container 等。 -- WEB 容器给处于其中的应用程序组件(JSP,SERVLET)提供一个环境,使 JSP,SERVLET 直接跟容器中的环境变量交互,不必关注其它系统问题 -- (从这个角度来说,web 容器应该属于架构上的概念)。web 容器主要由 WEB 服务器来实现。例如:TOMCAT,WEBLOGIC,WEBSPHERE 等。 -- 若容器提供的接口严格遵守 J2EE 规范中的 WEB APPLICATION 标准。我们把该容器叫做 J2EE 中的 WEB 容器。 - - WEB 容器更多的是跟基于 HTTP 的请求打交道。而 EJB 容器不是。它是更多的跟数据库、其它服务打交道。 -- 容器的行为是 将其内部的应用程序组件与外界的通信协议交互进行了隔离,从而减轻内部应用程序组件的负担(实现方面的负担?)。 - - 例如:SERVLET 不用关心 HTTP 的细节,而是直接引用环境变量 session、request、response 就行、EJB 不用关心数据库连接速度、各种事务控制,直接由容器来完成。 - -#### 【Web服务器】 -- Web 服务器(Web Server)可以处理 HTTP 协议。当 Web 服务器接收到一个 HTTP 请求,会返回一个 HTTP 响应,例如送回一个 HTML 页面。 - - Web 服务器可以响应针对静态页面或图片的请求, 进行页面跳转(redirect),或者把动态响应(dynamic response)的产生委托(delegate)给一些其它的程序 - - 例如 CGI 脚本,JSP(JavaServer Pages)脚本,servlets,ASP(Active Server Pages)脚本,服务器端 JavaScript,或者一些其它的服务器端技术。 - - Web 服务器仅仅提供一个可以执行服务器端程序和返回(程序所产生的)响应的环境,而不会超出职能范围。 - - Web 服务器主要是处理需要向浏览器发送 HTML 的请求以供浏览。 - -#### 【应用程序服务器(The Application Server)】 -- 根据定义,作为应用程序服务器,要求可以通过各种协议(包括 HTTP 协议)把商业逻辑暴露给(expose)客户端应用程序。 - - 应用程序使用此商业逻辑就像你调用对象的一个方法或过程(语言中的一个函数)一样。 - -#### 【serverlet】 - -- Servlet(Server Applet),全称 Java Servlet,未有中文译文。是用 Java 编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态 Web 内容。 -- 狭义的 Servlet 是指 Java 语言实现的一个接口 -- 广义的 Servlet 是指任何实现了这个 Servlet 接口的类,一般情况下,人们将 Servlet 理解为后者。 -- Servlet 运行于支持 Java 的应用服务器中。从实现上讲,Servlet 可以响应任何类型的请求,但绝大多数情况下 Servlet 只用来扩展基于 HTTP 协议的 Web 服务器。 - -#### 【Tomcat】 - -- Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试 JSP 程序的首选。 -- 对于一个初学者来说,可以这样认为,当在一台机器上配置好 Apache 服务器,可利用它响应对 HTML 页面的访问请求。 -- 实际上 Tomcat 部分是Apache 服务器的扩展,但它是独立运行的,所以当你运行 tomcat 时,它实际上作为一个与 Apache 独立的进程单独运行的。 -- Apache Tomcat is an open source software implementation of the Java Servlet and JavaServer Pages technologies. - -#### 【Tomcat与Web服务器、应用服务器的关系】 ->Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器。因为 Tomcat 技术先进、性能稳定且免费,所以深受 Java 爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的 Web 应用服务器。 - -******************* -## 一、Tomcat 与应用服务器 - ->到目前为止,Tomcat 一直被认为是 Servlet/JSP API 的执行器,也就所谓的 Servlet 容器。然而,Tomcat并不仅仅如此,它还提供了 JNDI 和 JMX API 的实现机制。尽管如此,Tomcat 仍然还不能算是应用服务器,因为它不提供大多数 J2EE API 的支持。 - -很有意思的是,目前许多的应用服务器通常把 Tomcat 作为它们 Servlet 和 JSP API 的容器。由于 Tomcat允许开发者只需通过加入一行致谢,就可以把 Tomcat 嵌入到它们的应用中。遗憾的是,许多商业应用服务器并没有遵守此规则。 - -对于开发者来说,如果是为了寻找利用 Servlet、JSP、JNDI 和 JMX 技术来生成 Java Web 应用的话,选择Tomcat 是一个优秀的解决方案;但是为了寻找支持其他的 J2EE API,那么寻找一个应用服务器或者把 Tomcat作为应用服务器的辅助, -将是一个不错的解决方案;第三种方式是找到独立的 J2EE API 实现,然后把它们跟Tomcat 结合起来使用。虽然整合会带来相关的问题,但是这种方式是最为有效的。 - -## 二、Tomcat 与 Web 服务器 - -Tomcat 是提供一个支持 Servlet 和 JSP 运行的容器。Servlet 和 JSP 能根据实时需要,产生动态网页内容。而对于 Web 服务器来说, Apache 仅仅支持静态网页,对于支持动态网页就会显得无能为力;Tomcat 则既能为动态网页服务,同时也能为静态网页提供支持。 -尽管它没有通常的 Web 服务器快、功能也不如 Web 服务器丰富,但是 Tomcat 逐渐为支持静态内容不断扩充。大多数的 Web 服务器都是用底层语言编写如 C,利用了相应平台的特征,因此用纯 Java 编写的 Tomcat 执行速度不可能与它们相提并论。 - -一般来说,大的站点都是将 Tomcat 与 Apache 的结合,Apache 负责接受所有来自客户端的 HTTP 请求,然后将 Servlets 和 JSP 的请求转发给 Tomcat 来处理。Tomcat 完成处理后,将响应传回给 Apache,最后 Apache 将响应返回给客户端。 - -************************* -# 优化 - -## Tomcat僵死问题 -- [ ] 分析各种可能的原因 - -************************* -# Tomcat和Jetty -> [参考博客: Jetty和Tomcat的选择:按场景而定](http://www.open-open.com/lib/view/open1322622094390.html) - -- [Jetty官网](http://www.eclipse.org/jetty/) - -``` - 一个简单项目, 就是index.jsp 里面放了个 Hello 字符串 - 经过对比 8.5.29 jetty 9.2 - 启动时间 jetty花费时间是Tomcat2倍 - 启动后内存 Jetty480M Tomcat300M - 1000并发 20000总量 - Tomcat涨到 460M 第二次480M 连续5次后上660M了 10次900M 最长时间时而220ms 时而 70ms - Jetty涨到770M 第二次压测直接上900M了 十次后也是900M 最长响应时间稳定在 220ms -``` diff --git a/Java/ZenOfPattern.md b/Java/ZenOfPattern.md deleted file mode 100644 index 5796948..0000000 --- a/Java/ZenOfPattern.md +++ /dev/null @@ -1,436 +0,0 @@ -`目录 start` - -- [设计模式之禅](#设计模式之禅) - - [软件设计的一些原则](#软件设计的一些原则) - - [思维原则](#思维原则) - - [奥卡姆剃刀原理](#奥卡姆剃刀原理) - - [首要原则](#首要原则) - - [勿重复造轮子](#勿重复造轮子) - - [减法优于加法](#减法优于加法) - - [抽象优于实现](#抽象优于实现) - - [组合优于继承](#组合优于继承) - - [查询与命令分离](#查询与命令分离) - - [够用原则](#够用原则) - - [最少知识原则](#最少知识原则) - - [面向对象的S.O.L.I.D 原则](#面向对象的solid-原则) - - [单一职责原则](#单一职责原则) - - [开闭原则](#开闭原则) - - [里氏代换原则](#里氏代换原则) - - [接口隔离原则](#接口隔离原则) - - [依赖倒置原则](#依赖倒置原则) - - [其他原则](#其他原则) - - [共同封闭原则](#共同封闭原则) - - [共同重用原则](#共同重用原则) - - [好莱坞原则](#好莱坞原则) - - [高内聚低耦合](#高内聚低耦合) - - [惯例优于配置原则](#惯例优于配置原则) - - [关注点分离](#关注点分离) - - [契约式设计](#契约式设计) - - [无环依赖原则](#无环依赖原则) - - [设计模式](#设计模式) - - [基础](#基础) - - [策略者模式](#策略者模式) - - [1.创建型设计模式](#1创建型设计模式) - - [2.结构型设计模式](#2结构型设计模式) - - [3.行为设计模式](#3行为设计模式) - - [【常见设计模式】](#常见设计模式) - - [适配器模式](#适配器模式) - - [中介者模式](#中介者模式) - - [观察者模式](#观察者模式) - - [单例模式](#单例模式) - - [装饰器模式](#装饰器模式) - - [原型模式](#原型模式) - - [生成器模式](#生成器模式) - - [实践](#实践) - - [经验之谈](#经验之谈) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 设计模式之禅 -## 软件设计的一些原则 -### 思维原则 -#### 奥卡姆剃刀原理 - -### 首要原则 -#### 勿重复造轮子 --` DRY Don’t Repeat Yourself ` -是一个最简单的法则,也是最容易被理解的。但它也可能是最难被应用的(因为要做到这样,我们需要在泛型设计上做相当的努力,这并不是一件容易的事)。它意味着,当我们在两个或多个地方的时候发现一些相似的代码的时候,我们需要把他们的共性抽象出来形一个唯一的新方法,并且改变现有的地方的代码让他们以一些合适的参数调用这个新的方法。 -#### 减法优于加法 -- `KISS Keep It Simple, Stupid ` -KISS原则在设计上可能最被推崇的,在家装设计,界面设计 ,操作设计上,复杂的东西越来越被众人所BS了,而简单的东西越来越被人所认可,比如这些UI的设计和我们中国网页(尤其是新浪的网页)者是负面的例子。 -“宜家”(IKEA)简约、效率的家居设计、生产思路;“微软”(Microsoft)“所见即所得”的理念;“谷歌”(Google)简约、直接的商业风格,无一例外的遵循了“kiss”原则, -也正是“kiss”原则,成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad将这个原则实践到了极至。 - -把一个事情搞复杂是一件简单的事,但要把一个复杂的事变简单,这是一件复杂的事。 -#### 抽象优于实现 --`Program to an interface, not an implementation` -- 这是设计模式中最根本的哲学,注重接口,而不是实现,依赖接口,而不是实现。接口是抽象是稳定的,实现则是多种多样的。 -- 以后面我们会面向对象的SOLID原则中会提到我们的依赖倒置原则,就是这个原则的的另一种样子。 - -#### 组合优于继承 -`【Composition over inheritance】` -- 多使用组合而不是继承, 但是这个观点是存在一定的争议的, 还是要有度的,合理搭配最为重要 - - 组合就是将原来继承方式中的父类放到子类作为属性? - -组合 -1.(对象)组合是一种通过创建一个组合了其它对象的对象,从而获得新功能的复用方法。 -2.将功能委托给所组合的一个对象,从而获得新功能。 -3.有些时候也称之为"聚合"(aggregation)或"包容"(containment),尽管有些作者对这些术语赋予了专门的含义 -- [ ] 再度思考 - -> [参考博客: 组合、委托与继承,面向对象中类之间的基本关系漫游](http://www.cnblogs.com/narutow/p/8117352.html) -> [参考博客: 优先使用(对象)组合,而非(类)继承](https://www.xuebuyuan.com/1639556.html) -#### 查询与命令分离 -`【CQS Command-Query Separation】` -- 查询命令分离原则 - - 查询:当一个方法返回一个值来回应一个问题的时候,它就具有查询的性质; - - 命令:当一个方法要改变对象的状态的时候,它就具有命令的性质; - -#### 够用原则 -`【YAGNI You Ain’t Gonna Need It 】` -- 这个原则简而言之为——只考虑和设计必须的功能,避免过度设计。只实现目前需要的功能,在以后您需要更多功能时,可以再进行添加。 - - 如无必要,勿增复杂性。 - - 软件开发先是一场沟通博弈。 - -#### 最少知识原则 -> 【Law of Demeter – 迪米特法则】 -- 迪米特法则(Law of Demeter),又称“最少知识原则” `(Principle of Least Knowledge)`,其来源于1987年荷兰大学的一个叫做Demeter的项目。 -- Craig Larman把Law of Demeter又称作“不要和陌生人说话”。在《程序员修炼之道》中讲LoD的那一章叫作“解耦合与迪米特法则”。 -- 关于迪米特法则有一些很形象的比喻: - - 如果你想让你的狗跑的话,你会对狗狗说还是对四条狗腿说? - - 如果你去店里买东西,你会把钱交给店员,还是会把钱包交给店员让他自己拿? -- 正式表述如下: -- 对于对象 ‘O’ 中一个方法’M',M应该只能够访问以下对象中的方法: - - 对象O; - - 与O直接相关的Component Object; - - 由方法M创建或者实例化的对象; - - 作为方法M的参数的对象。 - -> [参考博客: 迪米特法则与重构](http://zhangyi.xyz/demeter-law-and-refactoring/) -******************* -### 面向对象的S.O.L.I.D 原则 -- 一般来说这是面向对象的五大设计原则,但是,我觉得这些原则可适用于所有的软件开发。 - -#### 单一职责原则 -`Single Responsibility Principle (SRP)` -- 关于单一职责原则,其核心的思想是:`一个类,只做一件事,并把这件事做好,且只有一个引起它变化的原因。` - - Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。 - - Windows是这一原则的反面示例。几乎所有的程序都交织耦合在一起。 - -#### 开闭原则 -`Open/Closed Principle (OCP)` -- 关于开发封闭原则,其核心的思想是:模块是可扩展的,而不可修改的。也就是说,`对扩展是开放的,而对修改是封闭的。` - - 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。 - - 对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。 -#### 里氏代换原则 -`Liskov substitution principle (LSP) ` -- 软件工程大师Robert C. Martin把里氏代换原则最终简化为一句话:`“Subtypes must be substitutable for their base types”。` -- 也就是,子类必须能够替换成它们的基类。 -> 即:子类应该可以替换任何基类能够出现的地方,并且经过替换以后,代码还能正常工作。另外,不应该在代码中出现if/else之类对子类类型进行判断的条件。 -> 里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类型的模块在无需修改的情况下就可以扩展。 - -- 这么说来,似乎有点教条化,我非常建议大家看看这个原则个两个最经典的案例——`“正方形不是长方形”`和`“鸵鸟不是鸟”`。 -- 通过这两个案例,你会明白《墨子小取》中说的——`“娣,美人也,爱娣,非爱美人也….盗,人也;恶盗,非恶人也。”`——妹妹虽然是美人,但喜欢妹妹并不代表喜欢美人。 -- 盗贼是人,但讨厌盗贼也并不代表就讨厌人类。这个原则让你考虑的不是语义上对象的间的关系,而是实际需求的环境。 -- 在很多情况下,在设计初期我们类之间的关系不是很明确,LSP则给了我们一个判断和设计类之间关系的基准:需不需要继承,以及怎样设计继承关系。 -- -#### 接口隔离原则 -` Interface Segregation Principle (ISP) ` -- 接口隔离原则意思是把功能实现在接口中,而不是类中,使用多个专门的接口比使用单一的总接口要好。 -- 举个例子,我们对电脑有不同的使用方式,比如:写作,通讯,看电影,打游戏,上网,编程,计算,数据等,如果我们把这些功能都声明在电脑的抽类里面,那么,我们的上网本,PC机,服务器, -- 笔记本的实现类都要实现所有的这些接口,这就显得太复杂了。所以,我们可以把其这些功能接口隔离开来,比如:工作学习接口,编程开发接口,上网娱乐接口,计算和数据服务接口,这样,我们的不同功能的电脑就可以有所选择地继承这些接口。 -- 这个原则可以提升我们“搭积木式”的软件开发。对于设计来说,Java中的各种Event Listener和Adapter,对于软件开发来说,不同的用户权限有不同的功能,不同的版本有不同的功能,都是这个原则的应用。 - -#### 依赖倒置原则 -`Dependency Inversion Principle (DIP) ` -高层模块不应该依赖于低层模块的实现,而是依赖于高层抽象。 -举个例子,墙面的开关不应该依赖于电灯的开关实现,而是应该依赖于一个抽象的开关的标准接口,这样,当我们扩展程序的时候,我们的开关同样可以控制其它不同的灯,甚至不同的电器。 -也就是说,电灯和其它电器继承并实现我们的标准开关接口,而我们的开关产商就可不需要关于其要控制什么样的设备,只需要关心那个标准的开关标准。这就是依赖倒置原则。 -这就好像浏览器并不依赖于后面的web服务器,其只依赖于HTTP协议。这个原则实在是太重要了,社会的分工化,标准化都是这个设计原则的体现。 - -> 下面有几点指导意见,帮助你避免在面向对象设计中违反依赖倒置原则: ->> 变量不能持有具体类的引用,就像订单方法代码中,你看不到new一样。 -不要让派生自具体类,要派生就派生抽象类abstract -不要覆盖基类中已实现的方法,除非你要覆盖的是比较特殊的一部分代码。 - -### 其他原则 -#### 共同封闭原则 -`Common Closure Principle(CCP) ` -一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包,便影响了包中所有的类。一个更简短的说法是:一起修改的类,应该组合在一起(同一个包里)。如果必须修改应用程序里的代码,我们希望所有的修改都发生在一个包里(修改关闭),而不是遍布在很多包里。CCP原则就是把因为某个同样的原因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密,它们通常一起发生改变,那么它们应该属于同一个包。 - -CCP延伸了开闭原则(OCP)的“关闭”概念,当因为某个原因需要修改时,把需要修改的范围限制在一个最小范围内的包里。 -#### 共同重用原则 -`Common Reuse Principle (CRP) ` -包的所有类被一起重用。如果你重用了其中的一个类,就重用全部。换个说法是,没有被一起重用的类不应该被组合在一起。CRP原则帮助我们决定哪些类应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变,并发布新的版本,使用这个包的所有用户都必须在新的包环境下验证他们的工作,即使被他们使用的部分没有发生任何改变。因为如果包中包含有未被使用的类,即使用户不关心该类是否改变,但用户还是不得不升级该包并对原来的功能加以重新测试。 - -CCP则让系统的维护者受益。CCP让包尽可能大(CCP原则加入功能相关的类),CRP则让包尽可能小(CRP原则剔除不使用的类)。它们的出发点不一样,但不相互冲突。 - -#### 好莱坞原则 -`Hollywood Principle ` -好莱坞原则就是一句话——“don’t call us, we’ll call -you.”。意思是,好莱坞的经纪人们不希望你去联系他们,而是他们会在需要的时候来联系你。也就是说,所有的组件都是被动的,所有的组件初始化和调用都由容器负责。组件处在一个容器当中,由容器负责管理。 - -简单的来讲,就是由容器控制程序之间的关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在: - - 不创建对象,而是描述创建对象的方式。 - 在代码中,对象与服务没有直接联系,而是容器负责将这些联系在一起。 - -控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。 - -好莱坞原则就是IoC(Inversion of Control)或DI(Dependency Injection)的基础原则。这个原则很像依赖倒置原则,依赖接口,而不是实例,但是这个原则要解决的是怎么把这个实例传入调用类中?你可能把其声明成成员,你可以通过构造函数,你可以通过函数参数。但是 -IoC可以让你通过配置文件,一个由Service Container -读取的配置文件来产生实际配置的类。但是程序也有可能变得不易读了,程序的性能也有可能还会下降。 - -#### 高内聚低耦合 -`【 High Cohesion & Low/Loose coupling & – 高内聚, 低耦合 】` -- 这个原则是UNIX操作系统设计的经典原则,把模块间的耦合降到最低,而努力让一个模块做到精益求精。 - - 内聚:一个模块内各个元素彼此结合的紧密程度 - - 耦合:一个软件结构内不同模块之间互连程度的度量 -- 内聚意味着重用和独立,耦合意味着多米诺效应牵一发动全身 - -> 凝聚>松耦合>重用 [参考博客: 为什么我停止使用Spring?](http://www.jdon.com/forum/messageList.shtml?thread=45977#23144139) - -#### 惯例优于配置原则 -`Convention over Configuration(CoC)` -简单点说,就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如,Hibernate的映射文件,如果约定字段名和类属性一致的话,基本上就可以不要这个配置文件了。你的应用只需要指定不convention的信息即可,从而减少了大量convention而又不得不花时间和精力啰里啰嗦的东东。配置文件很多时候相当的影响开发效率。 - -Rails 中很少有配置文件(但不是没有,数据库连接就是一个配置文件),Rails 的fans号称期开发效率是 java 开发的 10 -倍,估计就是这个原因。Maven也使用了CoC原则,当你执行mvn --compile命令的时候,不需要指源文件放在什么地方,而编译以后的class文件放置在什么地方也没有指定,这就是CoC原则。 - -#### 关注点分离 -`Separation of Concerns (SoC) ` -> 是计算机科学中最重要的努力目标之一。这个原则,就是在软件开发中,通过各种手段,将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题,就是相对较易解决的。 -> 问题太过于复杂,要解决问题需要关注的点太多,而程序员的能力是有限的,不能同时关注于问题的各个方面。正如程序员的记忆力相对于计算机知识来说那么有限一样, -> 程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候,如果我们把所有的东西混在一起讨论,那么就只会有一个结果——乱。 - -我记得在上一家公司有一个项目,讨论就讨论了1年多,项目本来不复杂,但是没有使用SoC,全部的东西混为一谈,再加上一堆程序员注入了各种不同的观点和想法,整个项目一下子就失控了。 -最后,本来一个1年的项目做了3年。 - -实现关注点分离的方法主要有两种,一种是标准化,另一种是抽象与包装。标准化就是制定一套标准,让使用者都遵守它,将人们的行为统一起来,这样使用标准的人就不用担心别人会有很多种不同的实现,使自己的程序不能和别人的配合。 -JavaEE就是一个标准的大集合。每个开发者只需要关注于标准本身和他所在做的事情就行了。就像是开发镙丝钉的人只专注于开发镙丝钉就行了,而不用关注镙帽是怎么生产的,反正镙帽和镙丝钉按标来就一定能合得上。不断地把程序的某些部分抽像差包装起来,也是实现关注点分离的好方法。 -一旦一个函数被抽像出来并实现了,那么使用函数的人就不用关心这个函数是如何实现的,同样的,一旦一个类被抽像并实现了,类的使用者也不用再关注于这个类的内部是如何实现的。 -诸如组件,分层,面向服务,等等这些概念都是在不同的层次上做抽像和包装,以使得使用者不用关心它的内部实现细节。 -说白了还是“高内聚,低耦合”。 - -> [参考博客: 理论篇:关注点分离(Separation of concerns, SoC)](http://www.cnblogs.com/wenhongyu/p/7992028.html) -#### 契约式设计 -` Design by Contract (DbC)` -DbC的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。例如: - - 供应商必须提供某种产品(责任),并且他有权期望客户已经付款(权利)。 - 客户必须付款(责任),并且有权得到产品(权利)。 - 契约双方必须履行那些对所有契约都有效的责任,如法律和规定等。 - -同样的,如果在程序设计中一个模块提供了某种功能,那么它要: - - 期望所有调用它的客户模块都保证一定的进入条件:这就是模块的先验条件(客户的义务和供应商的权利,这样它就不用去处理不满足先验条件的情况)。 - 保证退出时给出特定的属性:这就是模块的后验条件——(供应商的义务,显然也是客户的权利)。 - 在进入时假定,并在退出时保持一些特定的属性:不变式。 - -契约就是这些权利和义务的正式形式。我们可以用“三个问题”来总结DbC,并且作为设计者要经常问: - - 它期望的是什么? - 它要保证的是什么? - 它要保持的是什么? - -根据Bertrand -Meyer氏提出的DBC概念的描述,对于类的一个方法,都有一个前提条件以及一个后续条件,前提条件说明方法接受什么样的参数数据等,只有前提条件得到满足时,这个方法才能被调用;同时后续条件用来说明这个方法完成时的状态,如果一个方法的执行会导致这个方法的后续条件不成立,那么这个方法也不应该正常返回。 - -现在把前提条件以及后续条件应用到继承子类中,子类方法应该满足: - - 前提条件不强于基类. - 后续条件不弱于基类. - -换句话说,通过基类的接口调用一个对象时,用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件,亦即,继承类方法必须接受任何基类方法能接受的任何条件(参数)。同样,继承类必须顺从基类的所有后续条件,亦即,继承类方法的行为和输出不得违反由基类建立起来的任何约束,不能让用户对继承类方法的输出感到困惑。 - -这样,我们就有了基于契约的LSP,基于契约的LSP是LSP的一种强化。 -#### 无环依赖原则 -`Acyclic Dependencies Principle (ADP) ` -包之间的依赖结构必须是一个直接的无环图形,也就是说,在依赖结构中不允许出现环(循环依赖)。如果包的依赖形成了环状结构,怎么样打破这种循环依赖呢?有2种方法可以打破这种循环依赖关系:第一种方法是创建新的包,如果A、B、C形成环路依赖,那么把这些共同类抽出来放在一个新的包D里。这样就把C依赖A变成了C依赖D以及A依赖D,从而打破了循环依赖关系。第二种方法是使用DIP(依赖倒置原则)和ISP(接口分隔原则)设计原则。 - -无环依赖原则(ADP)为我们解决包之间的关系耦合问题。在设计模块时,不能有循环依赖。 - -*************************************** -## 设计模式 -### 基础 -> [设计模式基础](http://www.cnblogs.com/x-xk/archive/2013/01/21/2864916.html) -[参考博客: GoF解释](http://www.baike.com/wiki/GoF) -[参考博客: 设计模式专栏](http://blog.csdn.net/column/details/zsxdesignpattern.html) - -> [23种经典设计模式UML类图汇总 ](http://blog.csdn.net/u011240877/article/details/45381071) -[参考博客: 23种设计模式UML表示形式](http://blog.csdn.net/chen4013874/article/details/51347535) -[参考博客: 23中设计模式类图和原理详解](http://blog.csdn.net/tingting256/article/details/52534663) -[参考博客: 23种设计模式类图总结 ](http://blog.csdn.net/qq_25827845/article/details/52510803) - -### 策略者模式 -> 定义了算法族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的用户。 -[参考博客: 设计模式 ( 十八 ) 策略模式Strategy(对象行为型) ](http://blog.csdn.net/hguisu/article/details/7558249) -[参考博客: Java消除ifelse](http://www.cnblogs.com/zdd-java/p/6143935.html) - -也就是说将一种需求的多种实现算法分别封装起来, 然后利用多态, 让调用方选择任一实现 - -### 1.创建型设计模式 -> 单例模式(Singleton) -原型模式(Prototype) -建造者(Builder) -抽象工厂(Abstract Factory) -工厂方法(Factory Method) - -* 抽象了实例化过程,它们帮助一个系统独立于如何创建,组合和表示它的那些对象。 -* 一个类创建型模型使用继承改变被实例化的类,而一个对象创建型模型将实例化委托给另一个对象 -* 将一组固定行为的硬编码转移为定义一个较小的基本行为集,这些行为可以被组合成任意数目的更复杂性的行为。这样创建有特定行为的对象要求的不仅仅是实例化一个类 - -_Builder_ -- 生成器 Builder,是一种对象构建模式,模式通常包含Builder,ConcreteBuilder。Director 和 Product四部分 - -_Abstract Factory_ -- 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类 - -_Factory Method_ -- 定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类 - -_Prototype_ -- 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 - -_Singleton_ -- 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 - -### 2.结构型设计模式 -> 适配器模式(**Adapter**) -桥接模式(**Bridge**) -组合模式(**Compontent**) -代理模式(**Proxy**) -享元模式(**Flyweight**) -外观模式(**Facade**) -装饰模式(**Decorator**) - -* 结构型设计模式涉及如何组合类和对象以获得更大的结构 -* 结构型模式采用继承机制来组合接口或实现。 -* 结构型对象模式不是对接口和实现进行组合,而是描述了如何对一些对象进行组合,从而实现新功能 - -**** - -* **Composite模式** -它将对象组合成树形结构以表示“部分-整体” -* **Flyweight模式** -该模式为共享对象定义了一个结构,强调对象的空间效率,自由共享 -* **Facade模式**(外观模式) -描述了如何用单个对象表示整个子系统(外部与其内部通信必须通过一个统一的对象进行交互),模式中的facade用来表示一组对象, -外观设计模式提供一个高层次的接口是的子系统易于使用。 -**适用情况:** -> 1.为复杂的子系统提供一个简单的接口 -> 2.客户程序与抽象类的实现部分有很大依赖性 -> 3.构建一个层次结构的子系统时,适用外观模式定义子系统每层的入口 - -* **Bridge模式** 将对象的抽象和实现分离,从而可以独立的改变他们。 -* **Decorator模式** -描述如何动态地为对象添加职责,模式采用递归方式组合对象,从而允许添加任意多的对象职责。 - -### 3.行为设计模式 -> 策略模式(**Strategy**) -命令模式(**Command**) -状态模式(**State**) -解释器模式(**Interpreter**) -模板方法(**Template Method**) -责任链模式(**Chain of Responsibility**) -迭代器模式(**Iterator**) -中介者模式(**Mediator**) -备忘录模式(**Memento**) -观察者模式(**Observe**) -访问者模式(**Visitor**) - -* 行为设计模式涉及算法和对象间职责的分配,行为模式描述对象和类的模式以及其通信模式 -* 行为模式使用继承机制在类间派发行为 - -------------------------------------------------------------------------------- -### 【常见设计模式】 - -#### 适配器模式 -- **适配器 模式**(Adapter):适配器是的一个接口与其他接口兼容,从而给出了多个不同接口的同一抽象。一般分类结构和对象结构两种: -- *类适配器*:适配器类继承被适配类,实现某个接口,在客户端类中可以根据需求来建立子类 -- *对象适配器*:适配器不是继承,是使用直接关联,或称委托方式 -![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/UML/Adapter.png) - -#### 中介者模式 -- **中介者 模式**:包装了一系列对象相互作用的方式,使得对象间的相互作用是独立变化,但是与中介者紧密联系 - -#### 观察者模式 -- **观察者 模式 Observer**:一个目标物件管理相依于它的管理物件,并且在它本身的状态发生改变时发出通知,这种模式常用来实现事件处理系统。(也称发布-订阅,模型-视图,源-收听者模式) - - 观察者(接口):更新信息,展示信息,给 **被观察者(形参)** 注册上观察者 - - 被观察者(接口):发出更新通知(遍历观察者集合并注册),当自身发生改动时发出通知消息 - -![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Observer.png) - -##### 单例模式 -> Singleton 一个类只有一个实例易于外界访问 Spring将该模式运用的出神入化 - -- [单例模式与高并发](http://www.cnblogs.com/atwanli/articles/5104898.html)当某个单例对象中含有不具有并发性的对象 就会发生并发问题, 由于只有一个对象, 为了确保数据一致, 就需要加锁, 这样就带来了严重的性能下降, 而Spring是怎么做的呢 - - [参考博客 Spring如何处理线程并发](https://blog.csdn.net/java_fancy/article/details/7439657) - - [参考博客: springmvc是单例的,开发的时候会不会影响性能呢?](https://bbs.csdn.net/topics/390873889) - - [参考博客: Spring并发访问的线程安全性问题](http://www.xuebuyuan.com/1628190.html) `Controller或者Service层中定义共享对象, 但是使用线程安全对象` - -> [参考博客: 单例模式和Static的区别! ](https://bbs.csdn.net/topics/310136305) -- [ ] `static 有可能被实例化多个出来么` - -##### 装饰器模式 -- **装饰器模式** 创建一个新类为某一个类动态添加新功能或增强原有的功能,避免代码重复或具体子类的数量增加 - -![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Decorator.png) - -- **策略模式** 优点:灵活添加同一问题的不同解决方案 -- **状态模式** 允许对象在内部状态时变更其行为,并且修改其类: - - 环境类(Context):定义客户感兴趣的接口,维护一个子类的实例,这个实例就是当前状态 - - 抽象状态类(State):定义一个接口以封装与Context的一个特定状态相关的行为 - - 具体状态类(concreteState):每一子类实现与Context的一个状态相关的行为 - - **例题**:纸巾售卖机:有四个状态! - - 【状态图】 - - ![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/State_zhijin.png) - - 【类图】 - - ![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/State_zhijin2.png) - - **例题**:TCP连接状态:![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/State_TCP.png) -- **命令模式 command**: - - 行为请求者 与 请求实现者 之间 紧耦合 的关系 - - **将一个请求封装成一个对象**,从而可用不同的请求对客户进行参数化,支持可撤销的操作 - - 下例:使用了接口来实现多态,子类是多个的,方法同名并功能多样的 - - 代码复用好,代码结构清晰【参数类表最好不要出现标志变量,最好分离出另一个方法】 - -![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Command.png) - -- **桥接模式** : 便于扩展,实现与抽象分离(解耦)对一个模块修改不影响别的模块 - -![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Bridge.png) - -- **抽象工厂模式** : 提供一个创建一系列相关实例相互依赖的对象。 - - 当一个系统要独立于它的产品的创建,组合和表示时 - - 当一个系统要由多个产品系列中的一个来配置时 - - 当需强调一系列相关的产品对象的设计以便进行联合使用时 - - 想提供一组对象而不显示他们的实现过程,只显示他们的接口 - -![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/AbstractFactory.png) - -#### 原型模式 -> struts2 就是采用该模式 -- **原型模式** : 对象创建模型: 允许一个对象创建另一个可定制的对象,封装实例化细节。 - - 实现Cloneable接口(Java自带接口),重写clone方法(在这里实例化对象,new或反射,按需求来修改) - - 该例,组合关系,在对方使用clone来代替构造器来实例化对象,并做好了绑定操作,大量减少代码量 - -![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Clone.png) - -#### 生成器模式 -- **生成器模式**: - -![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Model/Builder.png) - -## 实践 -### 经验之谈 -- [IBM 社区 Java 设计模式专题](https://www.ibm.com/developerworks/cn/java/design/) -- [一个鸭子游戏引发的设计(多态,继承,抽象,接口,策略者模式)](http://www.cnblogs.com/x-xk/archive/2012/12/21/2823401.html) -- [不要再盲目的new了!你要学着针对接口编程!(具体方法,Factory,Abstract Factory](www.cnblogs.com/x-xk/archive/2013/01/06/2830742.html) - -- [参考博客: 为什么我墙裂建议大家使用枚举来实现单例。](https://mp.weixin.qq.com/s/aGMz1u0Oh4ZHTDBFvgq0lg) -- [ ] 自己用Java重写一下这个例子, 并做出自己的总结 - - -反模式 -末日金字塔: 多层嵌套 \ No newline at end of file diff --git a/Java/img/001-design-pattern.km.svg b/Java/img/001-design-pattern.km.svg new file mode 100644 index 0000000..a379fd2 --- /dev/null +++ b/Java/img/001-design-pattern.km.svg @@ -0,0 +1 @@ +设计模式创建型工厂模式 Factory Pattern抽象工厂模式 Abstract Factory Pattern单例模式 Singleton Pattern建造者模式 Builder Pattern原型模式 Prototype Pattern行为型责任链模式 Chain of Responsibility Pattern命令模式 Command Pattern解释器模式 Interpreter Pattern迭代器模式 Iterator Pattern中介者模式 Mediator Pattern备忘录模式 Memento Pattern观察者模式 Observer Pattern状态模式 State Pattern策略模式 Strategy Pattern模板模式 Template Pattern访问者模式 Visitor Pattern空对象模式 Null Object Pattern结构型适配器模式 Adapter Pattern桥接模式 Bridge Pattern组合模式 Composite Pattern装饰器模式 Decorator Pattern外观模式 Facade Pattern享元模式 Flyweight Pattern代理模式 Proxy Pattern过滤器模式 Filter Pattern \ No newline at end of file diff --git a/Linux/Alpine/AlpineBase.md b/Linux/Alpine/AlpineBase.md index 74fad99..a95c2ba 100644 --- a/Linux/Alpine/AlpineBase.md +++ b/Linux/Alpine/AlpineBase.md @@ -1,8 +1,35 @@ -`目录 start` - -- [Alpine](#alpine) +--- +title: Alpine基础 +date: 2018-11-21 10:56:52 +tags: + - Alpine + - 基础 +categories: + - Linux +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [Alpine](#alpine) + - 1.1. [BusyBox](#busybox) + +💠 2024-09-06 11:36:43 **************************************** # Alpine -> 精简的linux 垃圾系统,小有屁用 +> [Official Site](https://www.alpinelinux.org/) + +> 从K8S及应用层兼容考虑,都不推荐Alpine做基础镜像! +> [是否适合用作Docker基础镜像](https://cloud.tencent.com/developer/article/1632733) +> [Alpine Linux,一个只有 5M 的 Docker 镜像](https://www.infoq.cn/article/2016/01/Alpine-Linux-5M-Docker) + +- [Does Alpine have known DNS issue within Kubernetes?](https://stackoverflow.com/questions/65181012/does-alpine-have-known-dns-issue-within-kubernetes) +- 优点是镜像很小 但是缺点是工具集和相关底层库实现和Ubuntu等主流发行版不一致,可能导致应用出现不一致情况,终端内的工具集也是不统一的 + - alpine 使用的musl 主流使用的glibc + - 即使从优点来说 由于镜像底层的文件系统是多层,所以多次分发镜像时,基础镜像小也就没有了优势 + +## BusyBox +> 里面各种工具命令能满足大部分需求, 但是命令参数,实现的功能都和原始的工具有差异,有裁剪 + +- 真正的 ls 属于`coreutils`包 +- 真正的 ps 属于 `procps` 包 + diff --git a/Linux/Arch/Arch.md b/Linux/Arch/Arch.md index 411a090..cb9e9f2 100644 --- a/Linux/Arch/Arch.md +++ b/Linux/Arch/Arch.md @@ -1,13 +1,146 @@ -`目录 start` - -- [Arch](#arch) +--- +title: Arch +date: 2018-12-21 10:55:04 +tags: + - Arch + - 基础 +categories: + - Linux +--- -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [Arch](#arch) + - 1.1. [社区](#社区) + - 1.2. [包管理](#包管理) + - 1.2.1. [Pacman](#pacman) + - 1.2.2. [Yay](#yay) + - 1.2.3. [Snap](#snap) +- 2. [Tips](#tips) + +💠 2024-10-02 22:33:00 **************************************** # Arch -> [参考博客: 为什么 Archlinux 不适合服务器使用](https://www.tuicool.com/articles/byAFZr) -> [参考博客: Arch Linux的用户都有理想主义倾向吗?](https://www.zhihu.com/question/49439472) -> [参考博客: ArchLinux你可能需要知道的操作与软件包推荐](https://www.viseator.com/2017/07/02/arch_more/) -> [参考博客: 长期使用Arch,Gentoo等滚动更新的发行版是怎样的一种体验?](https://www.zhihu.com/question/37720991?sort=created) + +> [参考: Arch Linux的用户都有理想主义倾向吗?](https://www.zhihu.com/question/49439472) +> [参考: ArchLinux你可能需要知道的操作与软件包推荐](https://www.viseator.com/2017/07/02/arch_more/) +> [参考: 长期使用Arch,Gentoo等滚动更新的发行版是怎样的一种体验?](https://www.zhihu.com/question/37720991?sort=created) +> [Arch Linux 安装、配置、美化和优化](http://www.cnblogs.com/bluestorm/p/5929172.html) + +************************ + +衍生版: +- [EndeavourOS](https://endeavouros.com/) +- Manjaro + + +## 社区 + +- [arch cn bbs](https://bbs.archlinuxcn.org/viewforum.php?id=19) +- [archlinux 简明指南](https://arch.icekylin.online/) + +## 包管理 + +> [Creating Arch Linux Packages](https://www.theurbanpenguin.com/creating-arch-linux-packages/) +> [Arch archive packages](https://archive.archlinux.org/packages/)`软件包镜像站` + +### Pacman + +> [Arch wiki: pacman ](https://wiki.archlinux.org/index.php/Pacman_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#%E5%88%A0%E9%99%A4%E8%BD%AF%E4%BB%B6%E5%8C%85) +> Arch User Repository (常被称作 AUR),是一个为 Arch 用户而生的社区驱动软件仓库。Debian/Ubuntu 用户的对应类比是 PPA。 + +/etc/pacman.conf 追加 + +```conf + [archlinuxcn] + #The Chinese Arch Linux communities packages. + SigLevel = Optional TrustAll + #Server = http://repo.archlinuxcn.org/$arch + Server = https://mirrors.ustc.edu.cn/archlinuxcn/$arch +``` + +- `pacman-mirrors` generate pacman mirrorlist for Manjaro Linux +- -S 安装 +- -R 卸载 + - -Rs 卸载以及没有被其他软件依赖的软件包 +- -Q 查询 + - -Qdt 查询未被依赖的软件包 +- -U 升级或添加软件包 [archive](https://archive.archlinux.org/packages/) + - 例如 `pacman -U https://archive.archlinux.org/packages/c/curl/curl-8.4.0-1-x86_64.pkg.tar.zst` 安装curl历史版本 + +> 注意 +- pacman yay 升级某些包时需要留意是否需要全系统升级,单独升级某个包容易造成**依赖库版本不匹配** + - 比如 当前手动curl 8.4.0-2 升级到 8.6.0-3,发现安装失败, 报错 `pacman: /usr/lib/libssl.so.3: version 'OPENSSL_3.2.0' not found (required by /usr/lib/libcurl.so.4)` + - 由于yay pacman也是依赖的curl,这里就有点死锁了,没法降级了 + - 从curl官网下载源码编译安装,安装路径默认 `/usr/local/bin/curl` 不是pacman默认的`/bin/curl`,只好手动复制lib过去 `sudo cp /usr/local/lib/libcurl.so.4.8.0 /usr/lib/libcurl.so.4.8.0` 新的报错 `curl: /usr/lib/libcurl.so.4: no version information available (required by curl)` + - 搜索后添加参数 重新编译 `./configure --enable-versioned-symbols --with-openssl` 重新复制lib过去 还是一样报错信息 + - 但是发现这个报错好像是警告级别不影响实际功能,然后用上述的 pacman -U 安装指定的版本,才恢复了正常使用 + +> 安装deb包 [How to Install a .deb Package on Arch Linux](https://www.baeldung.com/linux/arch-install-deb-package) + +### Yay +缓存目录 ~/.cache/yay + +- `pacman -S yay` 下一代aur管理 +- `alias yay='/usr/bin/yay --color=always'` 默认开启颜色高亮 + +### Snap + +> 使用 pacman 安装 +1. sudo pacman -S snapd +2. sudo systemctl enable --now snapd.socket +3. sudo ln -s /var/lib/snapd/snap /snap + +- 例如安装 sudo snap install redis-desktop-manager + - 可执行文件 /snap/bin/redis-desktop-manager.rdm + +- 但是国内会很慢,此时可以手动下载安装 [参考: snapInstall](https://kuricat.com/gist/snap-install-too-slow-zmbjy) + - curl -H 'Snap-Device-Series: 16' http://api.snapcraft.io/v2/snaps/info/{{packageName}} 例如 `redis-desktop-manager` + - sudo snap install xxx.snap --dangerous + +************************ + +# Tips + +> 无法识别 USB设备(键盘 鼠标 移动硬盘) 可能原因 + +1. 查看是usb模块 `sudo modprobe usb-storage` + 1. 若报错 `modprobe: FATAL: Module usb-storage not found in directory /lib/modules/4.19**` + 2. 查看 `ls /lib/modules` +2. Linux内核滚动升级了 但是grub 没有更新, `update-grub`即可 +3. 滚动升级了,没有重启电脑 + +- sudo pacman -S net-tools dnsutils inetutils iproute2 + - ifconfig,route 在net-tools + - nslookup,dig 在dnsutils + - ftp,telnet等 在inetutils + - ip 在 iproute2 + +************************ + +> 键盘 F区 按键映射错误 + +- [Arch Wiki](https://wiki.archlinux.org/index.php/Apple_Keyboard#Function_keys_do_not_work) +- `echo 2 > /sys/module/hid_apple/parameters/fnmode` 注意重启会失效 + - `echo 2 | sudo tee /sys/module/hid_apple/parameters/fnmode` + +- 向 /sys/module/hid_apple/parameters/fnmode 文件中写入不同的值,可切换不同的模式: + ``` + 0 禁用功能键,按 ‘Fn’ + ‘F8’ 等同于 F8 + 1 默认功能键,按 ‘F8’ 触发功能键 (play/pause),按 ‘Fn’ + ‘F8’ 触发 F8 键 + 2 默认非功能键,按 ‘F8’ 触发 F8 键,按 ‘Fn’ + ‘F8’ 触发功能键 (play/pause) + ``` +- 以上方法重启后失效,如果要让配置永久生效: `Manjaro中没有这个文件` + ``` + # vi /etc/modprobe.d/hid_apple.conf + options hid_apple fnmode=2 + ``` +- 或者将命令写入登录shell /etc/profile + +************************ + +> keyring 错误 + +`yay -Sy archlinux-keyring` diff --git a/Linux/Arch/Manjaro.md b/Linux/Arch/Manjaro.md index a106b79..35d94e5 100644 --- a/Linux/Arch/Manjaro.md +++ b/Linux/Arch/Manjaro.md @@ -1,18 +1,85 @@ -`目录 start` - -- [Manjaro](#manjaro) +--- +title: Manjaro +date: 2018-12-21 10:55:08 +tags: + - Arch + - Manjaro +categories: + - Linux +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [Manjaro](#manjaro) + - 1.1. [Tips](#tips) +- 2. [安装](#安装) + - 2.1. [显卡驱动](#显卡驱动) + - 2.2. [多系统安装](#多系统安装) + +💠 2024-09-09 10:34:58 **************************************** # Manjaro +> [Gitlab source code](https://gitlab.manjaro.org/explore/groups) + +> [Manjaro Controversies](https://rentry.co/manjaro-controversies) + +> [Manjaro 安装体验小结 ](https://michael728.github.io/2019/08/03/linux-manjaro-install/) +> [Manjaro 安装配置简要](https://blog.csdn.net/ouening/article/details/79633966) +> [Manjaro安装后你需要这样做](https://www.cnblogs.com/haohao77/p/9034499.html) +> [参考: Manjaro Deepin安装使用分享](https://zhuanlan.zhihu.com/p/43442012) +> [参考: Manjaro Deepin 配置备忘](https://yifeitao.com/2019/06/xiaomi-pro-manjaro-deepin) + +> [参考: Manjaro 配置](https://blog.triplez.cn/manjaro-quick-start) + +## Tips +> U盘启动盘运行Live系统时, 默认用户名和密码都为 manjaro + +- 这次下载解压运行 VSCode 就是这样, 报错为 + - `error while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory` + - 尝试安装 libgconf libgconf2 ... + - 其实真正的包是 gconf , 而这个也是尝试过的, 但是还是说找不到package, 更新了下系统,才找到了这个包 + +- `VirtualBox 和内核是高度耦合的`,需要内核驱动版本匹配才能正常运行,`yay virtualbox-host-modules` 选择对应内核版本安装即可 +- 某应用开机自启动 `sudo ln -s /usr/share/applications/xxx.desktop /etc/xdg/autostart/` + +************************ + +# 安装 +> [Installation Guides](https://wiki.manjaro.org/index.php?title=Installation_Guides) + +参考 [Using livecd v17.0.1 (and above) as grub to boot OS with broken bootloader](https://forum.manjaro.org/t/using-livecd-v17-0-1-and-above-as-grub-to-boot-os-with-broken-bootloader/24916) + +## 显卡驱动 + +> [config NVIDIA](https://wiki.manjaro.org/index.php?title=Configure_NVIDIA_(non-free)_settings_and_load_them_on_Startup) `一键安装配置Nvidia显卡驱动` + +> 升级驱动 +- pacman -R linux-latest-nvidia-440xx +- sudo mhwd -r pci video-nvidia-440xx + +- install video-nvidia-450xx drivers. +- sudo mhwd -i pci video-nvidia-450xx + +## 多系统安装 +例如 Win10(先)和Majaro安装 + +1. 首先通过 rufus 制作器启动U盘[wiki](https://wiki.manjaro.org/index.php?title=Burn_an_ISO_File),切记注意所下载ISO的正确性 +1. `parted -l` 查看当前硬盘系统分区模式, 来判断安装Manjaro时BIOS配置和安装模式 + 1. msdos => legacy + 1. gpt => uefi + +************************ + +> Manjaro 安装 deb 包 -> pacman 文档 https://wiki.archlinux.org/index.php/Pacman_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87) +1. 安装工具 yaourt -S debtap 或者 yay debtap +1. 升级 sudo debtap -u +1. 转换deb包 sudo debtap xxxx.deb +1. 安装转换后的安装包 sudo pacman -U x.tar.xz -> [Manjaro 安装配置简要](https://blog.csdn.net/ouening/article/details/79633966) +************************ - 由于是基于arch的, 滚动更新的特性, 所以需要在每次在安装软件前 `pacman -Syu` 更新整个系统 - - 这次下载解压运行 VSCode 就是这样, 报错为 - - error while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory - - 尝试安装 libgconf libgconf2 ... - - 其实真正的包是 gconf , 而这个也是尝试过的, 但是还是说找不到package, 更新了下系统,才找到了这个包 +> 使用国内镜像源 +1. `sudo pacman-mirrors -i -c China -m rank` | [ustc.edu.cn](http://mirrors.ustc.edu.cn/help/manjaro.html) +foxit GitKraken deepin-screenshot \ No newline at end of file diff --git a/Linux/Base/LinuxBase.md b/Linux/Base/LinuxBase.md index 8f4817d..56d1963 100644 --- a/Linux/Base/LinuxBase.md +++ b/Linux/Base/LinuxBase.md @@ -1,175 +1,451 @@ -`目录 start` - -- [Linux系统](#linux系统) - - [系统管理](#系统管理) - - [窗口管理器](#窗口管理器) - - [文件系统对比](#文件系统对比) - - [桌面环境对比](#桌面环境对比) - - [文件管理器对比](#文件管理器对比) - - [终端模拟器对比](#终端模拟器对比) - - [用户管理](#用户管理) - - [用户组管理](#用户组管理) - - [时间管理](#时间管理) - - [自启服务管理](#自启服务管理) - - [软件管理](#软件管理) - - [软件源列表](#软件源列表) - - [包管理器](#包管理器) - - [源码编译安装](#源码编译安装) - - [终端命令](#终端命令) - - [Shell内建命令](#shell内建命令) - - [安装Linux发行版](#安装linux发行版) -- [Tips](#tips) - - [一行执行多条命令](#一行执行多条命令) - - [让命令在后台运行](#让命令在后台运行) - - [关闭ssh回话不能运行](#关闭ssh回话不能运行) - - [关闭ssh回话仍能运行](#关闭ssh回话仍能运行) - - [修改主机名](#修改主机名) -- [终端快捷键](#终端快捷键) - - [Delete](#delete) - - [Convert](#convert) - - [Jump](#jump) - - [Search](#search) - - [Control](#control) - -`目录 end` |_2018-08-29_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux基础 +date: 2018-12-15 11:11:23 +tags: + - 基础 +categories: + - Linux +--- + +💠 + +- 1. [Linux系统](#linux系统) + - 1.1. [Boot](#boot) + - 1.1.1. [grub](#grub) + - 1.2. [用户管理](#用户管理) + - 1.2.1. [用户](#用户) + - 1.2.2. [用户组](#用户组) + - 1.2.3. [sudo](#sudo) + - 1.2.4. [终端和登录](#终端和登录) + - 1.3. [环境变量](#环境变量) + - 1.4. [进程](#进程) + - 1.4.1. [信号 Signal](#信号-signal) + - 1.4.2. [信号量 Semaphore](#信号量-semaphore) + - 1.4.3. [孤儿进程和僵死进程](#孤儿进程和僵死进程) + - 1.4.3.1. [孤儿进程](#孤儿进程) + - 1.4.3.2. [僵死进程 defunct](#僵死进程-defunct) + - 1.4.4. [守护进程](#守护进程) + - 1.4.5. [文件描述符 FD](#文件描述符-fd) + - 1.4.6. [线程](#线程) + - 1.5. [时间](#时间) + - 1.6. [服务管理](#服务管理) + - 1.6.1. [init进程](#init进程) + - 1.6.2. [systemd 方式服务管理](#systemd-方式服务管理) + - 1.6.3. [service 方式管理](#service-方式管理) + - 1.6.3.1. [自定义 service](#自定义-service) + - 1.6.4. [update-rc.d 方式管理](#update-rcd-方式管理) +- 2. [系统资源管理](#系统资源管理) + - 2.1. [ulimit](#ulimit) + - 2.2. [CPU](#cpu) + - 2.3. [内存](#内存) + - 2.3.1. [overcommit](#overcommit) + - 2.3.2. [oom](#oom) + - 2.3.3. [虚拟内存](#虚拟内存) + - 2.3.4. [交换内存](#交换内存) + - 2.3.4.1. [清空交换内存](#清空交换内存) + - 2.3.5. [清空读写缓存](#清空读写缓存) +- 3. [终端快捷键](#终端快捷键) + - 3.1. [Delete](#delete) + - 3.2. [Convert](#convert) + - 3.3. [Jump](#jump) + - 3.4. [Search](#search) + - 3.5. [Control](#control) +- 4. [常见对比](#常见对比) + - 4.1. [文件系统对比](#文件系统对比) + - 4.2. [桌面环境对比](#桌面环境对比) + - 4.3. [窗口管理器对比](#窗口管理器对比) + - 4.4. [文件管理器对比](#文件管理器对比) + - 4.5. [包管理](#包管理) + - 4.5.1. [AppImage](#appimage) + - 4.5.2. [snap](#snap) + - 4.5.3. [flatpak](#flatpak) +- 5. [Tips](#tips) + - 5.1. [一行执行多条命令](#一行执行多条命令) + - 5.2. [让命令在后台运行](#让命令在后台运行) + - 5.3. [修改主机名](#修改主机名) + - 5.4. [文件类型默认打开方式 MIME](#文件类型默认打开方式-mime) + - 5.5. [熵池](#熵池) + +💠 2024-10-07 23:15:25 **************************************** + # Linux系统 -> 只是记录了debian系的Linux, 不过也是大同小异 -## 系统管理 -> sudo 其实是软件 早该意识到的,所有的命令都是可执行文件 -> [笔记: 发行版之别](/Linux/Release_Experience.md) +- [Arch wiki](https://wiki.archlinux.org/) +- [Deepin wiki](https://wiki.deepin.org/) +- [RUNOOB.COM](http://www.runoob.com) `各种技术学习 文档资源` +- [linux-tutorial](https://github.com/dunwu/linux-tutorial) +- [How-To-Secure-A-Linux-Server](https://github.com/imthenachoman/How-To-Secure-A-Linux-Server) -### 窗口管理器 -- [awesome window manager](https://awesomewm.org/) `平铺式的` +- [Linux中国开源社区](https://linux.cn/) +- [LinuxTOY 是一个致力于提供 Linux 相关资讯的专题站点。](https://linuxtoy.org/) + - [内容Github源](https://github.com/LinuxTOY/linuxtoy.org) +- [鳥哥的 Linux 私房菜](http://linux.vbird.org/linux_basic/) +- [阿里云系统组技术博客](https://kernel.taobao.org/) +- [Awesome Linux Software](https://github.com/luong-komorebi/Awesome-Linux-Software) -### 文件系统对比 -> [参考博客: 如何选择文件系统:EXT4、Btrfs 和 XFS ](https://linux.cn/article-7083-1.html) +> 新手的话 特别注意不要随意 root权限 直接更改配置文件,容易导致系统crash(除非你明确的知道这个更改操作的作用, 即使如此也需要先备份原文件) -目前 Linux 大多采用 ext3 +> [在线Linux终端](https://itsfoss.com/online-linux-terminals/) `有浏览器虚拟化,以及远程主机多种类型的实现` -### 桌面环境对比 -- gnome 占用资源中等,个人对该桌面不感冒 -- xfce 占用资源少,操作类似于xp -- kde 功能强大,占用资源中等 - - [知乎 KDE如何配置得漂亮大气?](https://www.zhihu.com/question/54147372) -- dde deepin设计的桌面环境,小bug略多,但是美观操作方便, 但是扩展性不好 +> [闪客:品读 Linux 0.11 核心代码](https://github.com/dibingfa/flash-linux0.11-talk) -*************************** -### 文件管理器对比 -> 有单窗口,双列,命令,简洁轻量,笨重完整 各种各样的选择 +![](/Linux/Base/img/001-linux-base-cmd.km.svg) -- `nautilus` Gnome默认 挺好用,但是不能自动挂载分区 -- `deepin-filemanager` deepin默认,较为方便,但是打开手机会卡根本打不开 -- `pcmanfm` 左边侧栏目录树 会同步nautilus的配置`5m` -- `rox-filer` 特别小,单击打开,迅速定位文件,适合找东西用 -- `thunar` 解决了nautilus的缺点,内存也很省 `21M` -- `dolphin` 多标签页,目录树方式查看 -- `nemo` mint默认的,功能齐全,会同步nautilus的配置,同样有目录树而且是两边都有 `21M` -- `tuxcmd` Tux Commander 双列,小,直接的目录树,学习成本高点 `2M` - -******************************* -### 终端模拟器对比 -> [详细](/Skills/Soft/Terminal.md) - -************************************** -### 用户管理 -- 添加用户 `sudo adduser username` - - 对比 `useradd`只是新建一个用户不会创建主目录 -- 添加到sudo组 ,使用命令更安全:`sudo gpasswd -a $USER sudo` 但是要注销或者重启才生效貌似 -- 或者:添加用户到用户组:`adduser user group` - - 或者:使用修改文件的方式:(不推荐) 但是在docker中跑Ubuntu新建用户时很有用,也可以不用动文件,添加进组是有效的,看情况吧 - - `chmod 777 /etc/sudoers` 然后直接`sudo visudo`就是调用vi来打开文件的简写 - - 找不到文件说明没有安装sudo -> root用户 `apt install sudo ` - - 添加一行 `kuang ALL=(ALL:ALL)ALL` Centos:`kuang ALL=(ALL) ALL` - - `chmod 440 /etc/sudoers` - - `rwx 对应一个三位的二进制数, 1/0 表示开关` -- 查看是否设置成功 : `groups username` -- 删除用户以及对应的home目录:`sudo deluser username --remove-home` - -***** -- _切换用户_ `su` -- `su -l username` 当前用户的环境下登录用户(当成一个程序一样可以退出登录) +## Boot +安装和启动Linux + +> [Ventoy](https://github.com/ventoy/Ventoy)`无需烧录,复制ISO进U盘即可使用` + +### grub +> [GNU GRUB](https://www.gnu.org/software/grub/) *GRand Unified Bootloader* + + +- [ ] Windows 自动更新后的故障,开机需要手动指定boot分区后启动 +```sh + set prefix=(hd0,msdos7)/grub + insmod normal + normal +``` + +************************ -***** +## 用户管理 + +### 用户 + +- 添加用户 test1 `sudo adduser test1` + - 注意 `useradd` 只新建用户不会创建对应的主目录 +- 删除用户以及对应的home目录:`sudo deluser username --remove-home` +- _切换用户_ `su` +- `su -l username` 当前用户的环境下登录用户(当成一个程序一样可以退出登录) +- `sudo su -` 相比于 sudo su 会加载root用户环境变量,切换到HOME工作目录[su & su -](https://www.geeksforgeeks.org/difference-between-su-and-su-command-in-linux/) - _修改密码_ `passwd` - - `passwd user` - - `echo "root:caishi" | chpasswd` 如果是普通用户就是 sudo chpasswd -***** -- _修改相关信息_ `usermod` - -| verb | long verb | comment | -|:----:|:----|:----| -| -d | --home HOME_DIR | 用户的新主目录 | -| -e | --expiredate EXPIRE_DATE | 设定帐户过期的日期为 EXPIRE_DATE | -| -f | --inactive INACTIVE | 过期 INACTIVE 天数后,设定密码为失效状态 | -| -g | --gid GROUP | 强制使用 GROUP 为新主组 | -| -G | --groups GROUPS | 新的附加组列表 GROUPS | -| -a | --append GROUP | 将用户追加至上边 -G 中提到的附加组中,并不从其它组中删除此用 |户 -| -l | --login LOGIN | 新的登录名称 | -| -L | --lock | 锁定用户帐号 | -| -m | --move-home | 将家目录内容移至新位置 (仅于 -d 一起使用) | -| -p | --password PASSWORD | 将加密过的密码 (PASSWORD) 设为新密码 | -| -R | --root CHROOT_DIR | chroot 到的目录 | -| -s | --shell SHELL | 该用户帐号的新登录 shell | -| -U | --unlock | 解锁用户帐号 | + - `passwd user` + - `echo "root:caishi" | chpasswd` 如果是普通用户就是 sudo chpasswd +- _修改相关信息_ `usermod` + +| verb | long verb | comment | +| :--: | :----------------------- | :----------------------------------------------------------- | +| -d | --home HOME_DIR | 用户的新主目录 | +| -e | --expiredate EXPIRE_DATE | 设定帐户过期的日期为 EXPIRE_DATE | +| -f | --inactive INACTIVE | 过期 INACTIVE 天数后,设定密码为失效状态 | +| -g | --gid GROUP | 强制使用 GROUP 为新主组 | +| -G | --groups GROUPS | 新的附加组列表 GROUPS | +| -a | --append GROUP | 将用户追加至上边 -G 中提到的附加组中,并不从其它组中删除此用 | +| -l | --login LOGIN | 新的登录名称 | +| -L | --lock | 锁定用户账号 | +| -m | --move-home | 将家目录内容移至新位置 (仅于 -d 一起使用) | +| -p | --password PASSWORD | 将加密过的密码 (PASSWORD) 设为新密码 | +| -R | --root CHROOT_DIR | chroot 到的目录 | +| -s | --shell SHELL | 该用户账号的新登录 shell | +| -U | --unlock | 解锁用户账号 | + > [所有参数说明](https://gitee.com/kcp1104/codes/gca14wtqvm67l9j5r0deb56#usermod.md) -****** +************************ + +- `less /etc/passwd` 查看全部用户及其用户组 - `passwd 选项 用户名` 更改口令(密码) - - `-l 锁定口令,禁用账号` `-u 口令解锁` `-d 账号无口令` `-f 强迫用户下次登录时修改口令` - - 当前用户 `passwd` 就是修改当前用户口令 超级用户就可以命令后接用户名,修改任意用户 + - `-l 锁定口令,禁用账号` `-u 口令解锁` `-d 账号无口令` `-f 强迫用户下次登录时修改口令` + - 当前用户 `passwd` 就是修改当前用户口令 超级用户就可以命令后接用户名,修改任意用户 + +************************ -****** - pwcov 注:同步用户从/etc/passwd 到/etc/shadow - pwck 注:pwck是校验用户配置文件/etc/passwd 和/etc/shadow 文件内容是否合法或完整; - pwunconv 注:是pwcov 的立逆向操作,是从/etc/shadow和 /etc/passwd 创建/etc/passwd ,然后会删除 /etc/shadow 文件; - finger 注:查看用户信息工具 - id 注:查看用户的UID、GID及所归属的用户组 - chfn 注:更改用户信息工具 -- visudo 注:visodo 是编辑 /etc/sudoers 的命令;也可以不用这个命令,直接用vi 来编辑 /etc/sudoers 的效果是一样的; +- `visudo` 注:visodo 是编辑 /etc/sudoers 的命令;也可以不用这个命令,直接用vi 来编辑 /etc/sudoers 的效果是一样的; +- `who /var/log/wtmp` 查看登录记录 -### 用户组管理 -> [相关总结网页](http://www.runoob.com/linux/linux-user-manage.html) +### 用户组 -- 修改用户至指定组 `sudo usermod -G 用户组 用户` -- _显示用户所在组_ `groups` - - 缺省是当前用户, 若指定即输出指定用户的用户组 -- _添加用户组_ `groupadd` - - 缺省参数 就是新建用户组 - - `-g GID` 指定新用户组的组标识号GID - - `-o` 一般和g共用 表示新用户组的GID可以与系统已有用户组的GID相同。 +> [相关 博客](http://www.runoob.com/linux/linux-user-manage.html) -- _删除用户组_ `groupdel` +- _添加用户组_ `groupadd` + - 缺省参数 就是新建用户组 + - `-g GID` 指定新用户组的组标识号GID + - `-o` 一般和g共用 表示新用户组的GID可以与系统已有用户组的GID相同。 +- _显示用户所在组_ `groups [user]` 缺省是当前用户, 或输出指定用户的用户组 +- _修改用户组_ `sudo usermod -G 用户组 用户` +- _删除用户组_ `groupdel` - `groupmod 选项 用户组` - - -g GID 为用户组指定新的组标识号。 - - -o 与-g选项同时使用,用户组的新GID可以与系统已有用户组的GID相同。 - - -n 新用户组 将用户组的名字改为新名字 - -- grpck 检查`/etc/group`文件是否正确 + - -g GID 为用户组指定新的组标识号。 + - -o 与-g选项同时使用,用户组的新GID可以与系统已有用户组的GID相同。 + - -n 新用户组 将用户组的名字改为新名字 + - -a `gpasswd -a user group` +- grpck 检查 `/etc/group`文件是否正确 - grpconv 注:通过/etc/group和/etc/gshadow 的文件内容来同步或创建/etc/gshadow ,如果/etc/gshadow 不存在则创建; -- 注:通过/etc/group 和/etc/gshadow 文件内容来同步或创建/etc/group ,然后删除gshadow文件 -### 时间管理 -> [同步Linux服务器时间](http://www.cnblogs.com/chenmh/p/5485829.html) +- 注:通过/etc/group 和/etc/gshadow 文件内容来同步或创建/etc/group ,然后删除gshadow文件 + +### sudo + +- 添加用户 test1 到sudo组 注意: *将用户加入sudo组,debian系有效 alpine和arch无效 只能改文件* + 1. 将用户 testUser 加入 sudo 组 `sudo gpasswd -a test1 sudo` *或者* `usermod -G sudo test1` + 2. *或者*:使用修改文件的方式:(不推荐) + - `chmod 777 /etc/sudoers` 然后直接 `sudo visudo`就是调用vi来打开文件的简写 + - 添加一行 Debian: `test1 ALL=(ALL:ALL)ALL` 注意 Centos:`test1 ALL=(ALL) ALL` + - 设置sudo无需密码 `test1 ALL=(ALL) NOPASSWD: ALL` + - `chmod 440 /etc/sudoers` + +> 绝对路径执行shell报错无权限 +> 环境: + +- a 和 b 用户都属于用户组 b +- 当前工作目录是 /home/b/app/ + 现象: + +1. sudo -u a sh run.sh 正常执行 +2. sudo -u a sh /home/b/app/run.sh 报错无权限 + +原因: + +1. /home/b/ 目录对于用户组b没有任何权限,chmod 740 b 加上组的读权限后仍报错,改成750后正常了 +2. [Commands don't have permission when using absolute path](https://askubuntu.com/questions/367176/commands-dont-have-permission-when-using-absolute-path) + +方案:逐级排查shell所有父目录对于 `sudo指定用户`是否有执行权限 + +> sudo: 没有终端存在,且未指定 askpass 程序 + +- 设置用户为NOPASSWD + +### 终端和登录 + +> [参考: linux终端相关概念解释及描述](https://www.cnblogs.com/xiangtingshen/p/10889195.html) +> [参考: 终端基本概念&终端登录过程详解](https://blog.csdn.net/summy_j/article/details/73870353) + +1. tty 终端设备的统称 + - 通常使用tty来简称各种类型的终端设备 +2. pty 虚拟终端 + - 远程登录,图形化终端模拟器等操作使用 + - pts(pseudo-terminal slave)是pty的实现方法,与ptmx(pseudo-terminal master)配合使用实现pty。 + +> 通常Linux平台的终端模拟器新建tab时都是新建 pty, 但是Mac平台上则是新建tty + +************************ + +## 环境变量 + +> [zsh 环境变量](http://zsh.sourceforge.net/Doc/Release/Files.html#Startup_002fShutdown-Files) +> +>> `.zshenv → [.zprofile if login] → [.zshrc if interactive] → [.zlogin if login] → [.zlogout sometimes].` +>> + +> Bash 环境变量加载顺序 + +1. /etc/profile +2. $HOME/.bash_profile +3. $HOME/.bashrc +4. $HOME/.bash_login +5. $HOME/.profile + +> [千万别混淆 Bash/Zsh 的四种运行模式](https://zhuanlan.zhihu.com/p/47819029) +> [ +> ssh连接远程主机执行脚本的环境变量问题](https://blog.csdn.net/whitehack/article/details/51705889) + +alpine 里的sh和ash 默认是不登录shell 需要使用 sh -l 或者 ash -l 才会加载对应的文件 + +************************ + +## 进程 + +> 进程是由多个线程(至少有一个)以及持有资源的组合体, 线程可以理解为进程的执行单元 + +> 参考 深入理解计算机系统 书籍 + +1. pid_t 来表示一个进程的 pid,因此能表示的进程的范围一定不会超过 pid_t 数据类型的范围 + - 查看 pid 最大数量 `cat /proc/sys/kernel/pid_max` + +> [doc: fork](http://pubs.opengroup.org/onlinepubs/7908799/xsh/fork.html) +> [wiki: fork bomb](https://en.wikipedia.org/wiki/Fork_bomb) + +> [参考: linux常见进程与内核线程](https://www.cnblogs.com/createyuan/p/3979142.html) `0 1 2 等内核进程` + +### 信号 Signal + +`进程通信的一种标准化的方式` + +> /bin/kill -L 可查看所有信号量 + +``` + 1 HUP 2 INT 3 QUIT 4 ILL 5 TRAP 6 ABRT 7 BUS + 8 FPE 9 KILL 10 USR1 11 SEGV 12 USR2 13 PIPE 14 ALRM +15 TERM 16 STKFLT 17 CHLD 18 CONT 19 STOP 20 TSTP 21 TTIN +22 TTOU 23 URG 24 XCPU 25 XFSZ 26 VTALRM 27 PROF 28 WINCH +29 POLL 30 PWR 31 SYS +``` + +编号为 `1 ~ 31`的信号为传统UNIX支持的信号,是不可靠信号(非实时的),编号为 `32 ~ 63`的信号是后来扩充的,称做可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,只是负责发送, 不负责存储和接收, 可能会造成信号丢失,而后者不会。 + +> [参考 Linux信号列表](https://blog.csdn.net/baobao8505/article/details/1115820) + +> 常用信号 + +- 1 HUP + - 终端关闭,Session 退出时会发出的信号 +- 2 INT + - interrupt 中断信号 +- 9 KILL + - kill 进程不可忽略该信号 +- 15 TERM + - terminate 终止信号 + +### 信号量 Semaphore + +Linux内核的信号量用来操作系统进程间同步访问共享资源 + +信号量在创建时需要设置一个初始值,表示同时可以有几个任务可以访问该信号量保护的共享资源,当初始值为1时就变作互斥锁(Mutex),即同时只能有一个任务可以访问信号量保护的共享资源。 + +PV操作由P操作原语和V操作原语组成(原语是指不可中断的过程) +- P(S): + 1. 将信号量S的值减1,即S=S-1; + 1. 如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列 +- V(S): + 1. 将信号量S的值加1,即S=S+1; + 1. 如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程 + +PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信 + +使用PV操作实现进程互斥时应该注意的是: + + 每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。若有多个分支,要认真检查其成对性 + P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环 + 互斥信号量的初值一般为1 + +### 孤儿进程和僵死进程 + +> [参考: 孤儿进程与僵死进程[总结]](http://www.cnblogs.com/Anker/p/3271773.html) + +#### 孤儿进程 + +> 一个父进程退出,而它的子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。 + +> 注意, 也有意外, 并不总是被 init 进程收养 [Ubuntu15.04 删除/sbin/upstart与孤儿进程收养的问题](https://blog.csdn.net/chilumanxi/article/details/47066331) + +#### 僵死进程 defunct + +> 一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中, 这种进程称之为僵死进程。 +> 因为直到父进程结束后, 该僵死子进程会成为孤儿进程且是僵死进程, 会被 init 收养, 从而被回收, 但是如果父进程一直没有结束, 僵死子进程会一直存在 + +- 危害: + - 占用系统内存 pid等资源, 无法被回收 + +> 常规解决方案 [Github: 代码示例](https://github.com/Kuangcp/LearnC/tree/master/exception/process) + +1. `处理信号`: 子进程退出时向父进程发送SIGCHILD信号,父进程处理SIGCHILD信号。在信号处理函数中调用wait进行处理僵死进程。 +2. `fork两次`: 原理是将子进程成为孤儿进程,从而其的父进程变为init进程,通过init进程可以处理僵死进程。 + +> 暴力方案: 直接 kill 掉父进程, 父进程和僵死状态的子进程就一起被回收了 + +### 守护进程 + +> [参考: 守护进程](https://blog.csdn.net/lianghe_work/article/details/47659889) + +### 文件描述符 FD +> [wikipedia: File descriptor](https://en.wikipedia.org/wiki/File_descriptor) +> [参考: Linux下 文件描述符(fd)与 文件指针(FILE*)](https://blog.csdn.net/mm_hh/article/details/71374474) + +每一个进程在PCB(Process Control Block)即进程控制块中都保存着一分文件描述符表. +文件描述符就是这个表的索引文件,描述符表中每个表项都有一个指向已打开文件的指针。现在我们明确一下:已打开的文件在内核中用file结构体表示,文件描述符表中的指针指向file结构体。 + +> FD方式管理的范畴 +- 每个进程默认有的标准输入输出: 0标准输入 1标准输出 2错误输出 +- 持有的文件(读/写) +- 网络连接 socket +- 管道 pipe + +> 问题 +- 线上的Centos7.9上运行的Java8进程,12月11日启动的进程,但是1月3日突然 /proc/pid/fd/下的fd都发生了更新`无法查看创建时间`,但是/proc/pid的目录时间是对的 + - 问题:为什么会发生修改,标准输入输出,以及依赖的jar的fd都发生了新创建和修改 + - 原因:proc是虚拟文件系统,属性值取决了查询或操作系统管理需要时构建出来 + +### 线程 + +1. 查看创建一个线程占用内存大小 `ulimit -s` +2. 查看进程下的线程 `ps -T pid` +3. 查看最大线程数 `cat /proc/sys/kernel/threads-max` 默认值 256287 + +************************ + +## 时间 +> [ntpdate manual](https://linux.die.net/man/8/ntpdate) + +- /etc/timezone 时区, /etc/localtime 时区及时间 +- `/usr/sbin/ntpdate -q cn.pool.ntp.org` 查看差异 + +> **时间同步** -**同步时间** 1. 修改时区 `cp -y /usr/share/zoneinfo/Asia/Shanghai /etc/localtime` -2. 同步时间 `/usr/sbin/ntpdate -u cn.pool.ntp.org` -3. 查看硬件时间 `hwclock -r` - - 如果不同步就需要写入时间 `hwclock -w` _因为系统重启是参考硬件时间的_ +2. 同步时间 `/usr/sbin/ntpdate -u cn.pool.ntp.org` | 没有就先安装 ntpdate + - 注意返回值 offset 为正说明本地比远程慢,负数则相反,本地比远程快 +3. 查看BIOS硬件时间 `hwclock -r` + - 如果不同步就需要写入时间 `hwclock -w` _因为系统重启后用硬件时间初始化的_ + +> 同步时间 扩展 + +- chrony 比 ntpupdate 功能更多,可定时同步时间 +- 服务器领域 ntpd 会更合适,因为 ntpupdate是立刻修改时钟 会带来时钟跃变的问题,但是ntpd是将误差的时间在一段时间内平缓的调整。[Linux 时间同步服务 -- ntp 和 chrony](https://blog.epurs.com/post/ntp-and-chrony/) -**自动同步时间** -1. 配置开机自动校验 `vim /etc/rc.d/rc.local` +- **自动同步时间** + 1. 配置开机自动校验 `vim /etc/rc.d/rc.local` - `/usr/sbin/ntpdate -u cn.pool.ntp.org> /dev/null 2>&1; /sbin/hwclock -w` -2. 配置定时任务 `crontab -e` + 2. 配置定时任务 `crontab -e` - `00 10 * * * root /usr/sbin/ntpdate -u cn.pool.ntp.org > /dev/null 2>&1; /sbin/hwclock -w ` -### 自启服务管理 +************************ + +## 服务管理 + +### init进程 + +> [参考: 服务相关命令](https://blog.csdn.net/qq_37993487/article/details/79868857) + +### systemd 方式服务管理 + +> [Arch Doc: systemd](https://wiki.archlinux.org/index.php/Systemd_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) + +- systemctl start/stop/restart/reload/edit serviceName **例如 sshd docker 等服务** + +| command | 作用 | +| :------------- | :----------------------------------------------------- | +| start/stop | 启动/停止服务 | +| enable/disable | 开机启用/禁用 | +| restart | 如果服务在运行中,则重启服务,若不在运行中,则将会启动 | +| try-restart | 只在服务已存在运行的状态下启动服务 | +| reload | 重新加载配置文件 | +| edit | 修改服务配置 | +| status | 查看运行状态 | + +> 系统电源管理 + +| systemctl 命令 | 作用 | +| ---------------------- | ---------------- | +| systemctl poweroff | 关闭系统 | +| systemctl reboot | 重启系统 | +| systemctl suspend | 进入待机模式 | +| systemctl hibernate | 进入休眠模式 | +| systemctl hybrid-sleep | 进入混合休眠模式 | + +************************ + +### service 方式管理 + > /etc/init.d/ 是服务的存放目录 -1. 列出所有服务的状态 service --status-all -1. 移除MySQL的自启 `sudo update-rc.d -f mysql remove` +1. 列出所有服务的状态 `service --status-all` +2. 启动/关闭服务 `service ssh start/stop` + +#### 自定义 service + +> [参考: Run a Java Application as a Service on Linux](https://www.baeldung.com/linux/run-java-application-as-service) + +### update-rc.d 方式管理 + +1. 移除MySQL的自启 `sudo update-rc.d -f mysql remove` 2. 设置MySQL随机启动 `sudo update-rc.d mysql defaults` 3. 设定MySQL启动顺序 `update-rc.d mysql defaults 90` 数字越小, 启动顺序越前 @@ -177,153 +453,137 @@ - chkconfig 简单的输出服务自启状态 _系统运行级别_ + ``` 0 系统停机状态 1 单用户或系统维护状态 2~5 多用户状态 6 重新启动 ``` -****************** -## 软件管理 -### 软件源列表 -- apt 的默认配置文件是 `/etc/apt/source.list` - - 以及 sources.list.d/ 目录下的 *.list 文件 (最好将list文件都进行备份 备份文件为 *.save) -- [参考博客 阿里云的软件源](https://hacpai.com/article/1482807364546?p=1&m=0) -- [wiki-源列表说明](http://wiki.ubuntu.com.cn/%E6%BA%90%E5%88%97%E8%A1%A8) +************************ -1. 源 URL 后的单词: - 1. main: 完全的自由软件。 - 1. restricted: 不完全的自由软件。 - 1. universe: Ubuntu官方不提供支持与补丁,全靠社区支持。 - 1. multiverse: 非自由软件,完全不提供支持和补丁。 +# 系统资源管理 -1. 添加私有源ppa - - 若不能添加私有源ppa: - - debain:`sudo apt install software-properties-common python-software-properties` - - Ubuntu `sudo apt install python-software-properties` - - 添加:`sudo add-apt-repository ppa:dotcloud/lxc-docker ` - - 删除ppa : `cd /etc/apt/sources.list.d/` 打开该目录下文件把对应的ppa的一行注释掉或删掉就行了 +- 查看系统PCI设备:`lspci` +- 查看CPU信息:`more /proc/cpuinfo` + - 查看物理CPU数:`cat /proc/cpuinfo | grep "physical id" | sort | uniq | wc -l` + - 查看每个物理CPU中的内核的个数:`cat /proc/cpuinfo | grep "cpu cores"` + - 查看系统所有逻辑CPU个数:`cat /proc/cpuinfo | grep "processor" | wc -l` +- 查看系统内存信息:`more /proc/meminfo` +- 查看磁盘分区信息:`df -l` +## ulimit -1. 添加一个源列表 +> [参考: Linux下设置最大文件打开数nofile及nr_open、file-max](https://www.cnblogs.com/zengkefu/p/5635153.html) -- 例如添加 nginx: 新建文件 `/etc/apt/sources.list.d/nginx.list` -``` - deb http://nginx.org/packages/mainline/debian/ jessie nginx - deb-src http://nginx.org/packages/mainline/debian/ jessie nginx -``` -- `curl http://nginx.org/keys/nginx_signing.key | apt-key add -` - - 把签名添加进来才能正常 apt update - -### 包管理器 -> dpkg -1. 查看已安装的应用 `dpkg --list` -1. 显示已安装包的详情 `dpkg -s package` -1. 安装deb包 - - ` sudo dpkg -i *.deb` - -> apt-get / apt -1. `install 包名` 安装指定包的最新版 - - `-y` 参数可以省去确认 - - `-s` 模拟安装 - - `package=version` 安装指定版本的包 -1. list 列出所有可安装的包 - - package 列出已安装的 该package 的信息 `加上 -a`: 所有版本 - -1. 只卸载程序,保留配置文件 `sudo apt remove 应用名` -1. 彻底卸载应用 `sudo apt--purge remove 应用名` - -- apt-cache showpkg/policy/madison/show package - - showpkg (特别详细) 列出所有版本以及来源, MD5 ... - - policy (基本信息) 列出所有版本以及来源 - - madison (简略显示) 内容同上 - - show 查询指定包的详情(已安装的版本信息) - -> snap -- [official doc: snap](https://snapcraft.io/docs/core/usage) - -### 源码编译安装 -1. make install 源代码安装 - - 1.解压缩 `tar -zxf nagios-4.0.2.tar.gz ` - - 2.进入目录 `cd nagios-4.0.2` - - 3.配置 `./configure --prefix=/usr/local/nagios ` - - 4.编译 `make all` - - 5.安装 `make install && make install-init && make install-commandmode && make install-config` - -********************************************** -## 终端命令 -> /bin/* 系统自带的命令 -例如 which命令, 查找到命令的位置 - -> /usr/bin/* 用户安装终端应用的目录 以下往往是系统自带的 -- wc -l file _统计文件行数_ -- md5sum 报文摘要算法 - -### Shell内建命令 -- whence 查看命令的真实面貌 (zsh中的内建命令) -- where 查找命令的位置 (Zsh中内建命令) - -> [更多常用工具列表](/Linux/Tool/Terminal.md) -***************************************************** -## 安装Linux发行版 -- 下载指定的镜像包,使用对应的刻录软件刻录U盘(Windows就是软碟通,Linux没怎么用过,只用过深度的U盘启动盘制作工具挺好的) -- 进入U盘安装模式,分区: - - 分配 1/5 的 `/` ext4 - - 分配 3/5 的 `/home` ext4 - - 分配 500-1000m 的 `/boot/efi` fat32格式 -- 如果是双系统: - - 如果新手直接全部` / `就行了,再加个交换分区 - - 如果为了日后重装系统方便,那么分两个区 `/` 和 `/home` - - 这样的话,就建议大量软件使用解压版,这样重装系统带来的影响最小,那么`/home`就要分大一点 - - 例如我: `/`只用了22G `/home`用了40G(公司的Deepin分了100G /home 用了75G了) - -> 新手的话特别注意不要随意用sudo然后更改配置文件,容易导致系统crash(除非你明确的知道这个更改操作的作用) - -***************************************************** -# Tips -> man help 后接使用的命令,就可以得到用户手册和帮助文档 +1. 查看某进程limit状态 `cat /proc/xxxpid/limits` +2. 执行ulimit修改命令只对当前终端(tty)生效 +3. 持久化修改设置: **/etc/security/limits.conf** 文件中添加,注销或重启后生效 -## 一行执行多条命令 -- ` && ` 第2条命令只有在第1条命令成功执行之后才执行 根据命令产生的退出码判断是否执行成功(0成功,非0失败) -- `|| ` 执行不成功(产生了一个非0的退出码)时,才执行后面的命令 -- ` ; ` 顺序执行多条命令,当;号前的命令执行完(不管是否执行成功),才执行;后的命令。 -- ` & ` 并行执行命令,没有顺序 + ```sh + * soft nofile 4096 + * hard nofile 4096 + ``` -- [tty 虚拟终端等概念](https://www.ibm.com/developerworks/cn/linux/l-cn-termi-hanzi/) +## CPU -- Centos上which并不是命令, 而是别名! - - `which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'` +> [linux cpu load](https://www.scalyr.com/blog/linux-cpu-load/) -************** -## 让命令在后台运行 -> [原博客](https://www.ibm.com/developerworks/cn/linux/l-cn-nohup/) +- Usage 和 Load 的区别, 使用率针对于Cpu 时间,负载针对于等待和进行中的线程 +- 使用uptime、top或者 `cat /proc/loadavg`都可以看到CPU的load 1 5 15 分钟的负载。 + - LOAD AVERAGE:一段时间内处于可运行状态和不可中断状态的进程平均数量,它是从另外一个角度体现CPU的使用状态。 + - 可运行分为正在`运行进程`和`正在等待CPU`的进程,**状态为R** + - 不可中断则是它正在做某些工作不能被中断比如等待磁盘IO等,**其状态为D** + > 注意: 一个逻辑核且负载为1时表示有线程一直在等待或运行(满载),四个逻辑核且负载为4时表示四个核心都一直有线程在等待或运行(满载) +- lscpu 展示CPU信息 -- 命令后接 & (只是让进程躲到当前终端的后台去了 hup信号仍然影响) +- taskset 将任务绑定在指定cpu核心上 -`nohup, disown, screen, setid ` -- 运行的命令不因 用户注销,网络中断等因素而中断 - - 让进程对hup信号免疫 nohup disown - - 让进程在新的会话中运行 setid screen +************************ -### 关闭ssh回话不能运行 -> 1.没有使用任何修饰原有命令 -> 2.只在原有命令后加& +## 内存 +对于Linux来说, 有内存就去分配使用, 只有内存不够申请的大小,才会去释放 buffer或cache, 对于服务器来说, 交换内存会带来性能的明显下降 一般是不会配置的 -### 关闭ssh回话仍能运行 -- 使用`nohup`就能屏蔽hup信号,默认输出到 nohup.out `nohup 命令 &` - - 将所有输出重定向到空设备 `nohup 命令>/dev/null 2>&1` - - 例如 在当前目录后台打开文件管理器 `(dde-file-manager . &) >/dev/null 2>&1` +内存组成 +- 空闲内存, 已使用, buffers, cached + - 读 cache 写 buffer -- `(命令 &)` 屏蔽了hup信号 +- Virtual Memory 虚拟内存 +- Resident Memory 持久内存 +- Shared Memory 共享内存(多进程间共享) -************* -## 修改主机名 -- `sudo hostname linux` 重启终端即可看到修改 -- 但是重启电脑会恢复原有名字修改如下文件永久: `sudo gedit /etc/hostname` 也许需要更改`/etc/hosts` -- 立即生效,也要重新登录 `hostname -F /etc/hostname ` +> [linux ate my ram](https://www.linuxatemyram.com/) +> [Empty the Buffer and Cache in Linux](https://www.baeldung.com/linux/empty-buffer-cache) + +查看内存大页设置 `cat /sys/kernel/mm/transparent_hugepage/enabled` +关闭内存大页 `echo never > /sys/kernel/mm/transparent_hugepage/enabled` + +### overcommit +> [参考: Linux Overcommit Modes](https://www.baeldung.com/linux/overcommit-modes) + +- 内核参数: vm.overcommit_memory + - 0 允许overcommit但是算法判断是否合理,不合理会拒绝对应进程的内存申请 + - 1 允许overcommit + - 2 禁止overcommit + +- `cat /proc/meminfo | grep commit` + - CommitLimit 就是overcommit的阈值,申请的内存总数超过CommitLimit的话就算是overcommit。 + - CommitLimit = (Physical RAM * vm.overcommit_ratio / 100) + Swap + - Committed_AS 表示所有进程已经申请的内存总大小,(注意是已经申请的,不是已经分配的),如果 Committed_AS 超过 CommitLimit 就表示发生了 overcommit + - 超出越多表示 overcommit 越严重。Committed_AS 的含义换一种说法就是,如果要绝对保证不发生OOM (out of memory) 需要多少物理内存。 + +### oom +当操作系统认为内存不足时,会选择分数值较高的进程kill掉(用户进程,非内核进程) +- /proc/pid/oom_score 操作系统所计算值 +- /proc/pid/oom_score_adj 可以修改的值,当前值加上oom_score后才是最终值 + - 降低分值 echo -50 > /proc/pid/oom_score_adj +- /proc/pid/oom_adj 对应进程的优先级 + +### 虚拟内存 + +> [参考: What does Virtual memory size in top mean?](https://serverfault.com/questions/138427/what-does-virtual-memory-size-in-top-mean) +> [参考: The Right Way to Monitor Virtual Memory on Linux](https://www.logicmonitor.com/blog/the-right-way-to-monitor-virtual-memory-on-linux/) + +### 交换内存 +> swapon, swapoff - enable/disable devices and files for paging and swapping + +> [交换内存文件](/Linux/Base/LinuxDirectoryStructure.md#设置交换内存文件) + +> 交换内存分析 +VIRT = SWAP + RES or equal +SWAP = VIRT - RES + +- 查看进程使用交换内存 `grep -i VmSwap /proc/*/status` +- 进程按交换内存使用大小排序`for file in /proc/*/status ; do awk '/VmSwap|Name/{printf $2 " " $3}END{ print ""}' $file; done | sort -k 2 -n -r | less` +- `smem` Report memory usage with shared memory divided proportionally + +#### 清空交换内存 +- 1.关闭交换分区 `sudo swapoff 交换分区文件` + - 2.开启交换分区 `sudo swapon 交换分区文件` +- `swapoff -a && swapon -a` + - 前提是交换分区已在 `/etc/fstab` 中配置 + +### 清空读写缓存 +注意: 读 cache 写 buffer, 设计是为了提高读写效率,如果内存不足时可以考虑释放这部分内存,但是也会带来读写缓存失效重新读磁盘的性能问题,需慎重考虑。 + +> [参考: 如何在 Linux 中清除缓存(Cache)?](https://linux.cn/article-5627-1.html) `注意要切换到root再运行命令` +> [参考: Linux 内存中的Cache,真的能被回收么?](https://www.cnblogs.com/276815076/p/5478966.html) + +************************ + +设置值 `sync; echo 1 > /proc/sys/vm/drop_caches` + +| 设置值 | 作用 | +|:----|:----| +| 1 | 仅清除 page cache | +| 2 | 表示清除回收 slab 分配器中的对象(包括目录项缓存和 inode 缓存) | +| 3 | 表示清除 page cache 和 slab 分配器中的缓存对象 | + +> 注意sync命令是为了将内存中buffer写入磁盘,避免这部分内存被直接释放导致数据不一致 + +************************ -************************* # 终端快捷键 - `鼠标中键` 粘贴鼠标左键已选择的文本 **VSCode中也适用** @@ -339,56 +599,209 @@ _系统运行级别_ - Ctrl Alt Fn: 切换屏幕。 根据默认设置,从 [F1] 到 [F6] 是 shell 提示屏幕, [F7] 是图形化屏幕。`但是deepin是F1为图形化` ## Delete -| Controller | Key | comment | -|:---|:----|:----| -| Ctrl | D | 删除光标后字符,等价于Delete键(命令行若无任何字符,则相当于exit;处理 多行标准输入时也表示EOF) | -| Ctrl | H | 退格删除一个字符,相当于通常的Backspace键 | -| Ctrl | U | 删除光标之前到 行首 的字符 (Zsh中是删除整行)| -| Esc | W | 删除光标之前到 行首 的字符| -| Ctrl | K | 删除光标之前到 行尾 的字符 | -| Ctrl | W | 删除光标之前的一个单词 | -| Alt | D | 删除光标之后的一个单词 | -| Ctrl | Y | 粘贴上次删除的所有字符 | -| Ctrl | _ | 撤销修改 等价于 `Ctrl x u` | - -************************** + +| Controller | Key | comment | +| :--------- | :-- | :---------------------------------------------------------------------------------------------- | +| Ctrl | D | 删除光标后字符,等价于Delete键(命令行若无任何字符,则相当于exit;处理 多行标准输入时也表示EOF) | +| Ctrl | H | 退格删除一个字符,相当于通常的Backspace键 | +| Ctrl | U | 删除光标之前到 行首 的字符 (Zsh中是删除整行) | +| Esc | W | 删除光标之前到 行首 的字符 | +| Ctrl | K | 删除光标之前到 行尾 的字符 | +| Ctrl | W | 删除光标之前的一个单词 | +| Alt | D | 删除光标之后的一个单词 | +| Ctrl | Y | 粘贴上次删除的所有字符 | +| Ctrl | _ | 撤销修改 等价于 `Ctrl x u` | + +************************ + ## Convert -| Controller | Key | comment | -|:---|:----|:----| -| Ctrl | T | 互换当前字符,光标后移 | -| Alt | T | 互换当前单词与前一个单词,光标后移 等价于 `Esc T`| -| Alt | D | 将当前单词全部转为大写,光标后移 | -| Alt | C | 将当前单词首字母转为大写,光标后移 | -| Alt | L | 将当前单词全部转为小写,光标后移(zsh无效) | - -******************* + +| Controller | Key | comment | +| :--------- | :-- | :------------------------------------------------- | +| Ctrl | T | 互换当前字符,光标后移 | +| Alt | T | 互换当前单词与前一个单词,光标后移 等价于 `Esc T` | +| Alt | D | 将当前单词全部转为大写,光标后移 | +| Alt | C | 将当前单词首字母转为大写,光标后移 | +| Alt | L | 将当前单词全部转为小写,光标后移(zsh无效) | + +************************ + ## Jump -| Controller | Key | comment | -|:---|:----|:----| -| Ctrl | C | 取消运行当前行输入的命令,相当于Ctrl + Break | -| Ctrl | A | 光标移动到行首(Ahead of line),相当于通常的Home键 | -| Ctrl | E | 光标移动到行尾(End of line) | -| Ctrl | F | 光标向前(Forward)移动一个字符位置 | -| Ctrl | B | 光标往回(Backward)移动一个字符位置 | -| Alt | F | 光标向前(Forward)移动一个单词位置 | -| Alt | B | 光标往回(Backward)移动一个单词位置 | -| Esc | F | 光标向前(Forward)移动到当前单词的头部 | -| Esc | B | 光标往回(Backward)移动到当前单词的尾部 | - -***************************** + +| Controller | Key | comment | +| :--------- | :-- | :-------------------------------------------------- | +| Ctrl | C | 取消运行当前行输入的命令,相当于Ctrl + Break | +| Ctrl | A | 光标移动到行首(Ahead of line),相当于通常的Home键 | +| Ctrl | E | 光标移动到行尾(End of line) | +| Ctrl | F | 光标向前(Forward)移动一个字符位置 | +| Ctrl | B | 光标往回(Backward)移动一个字符位置 | +| Alt | F | 光标向前(Forward)移动一个单词位置 | +| Alt | B | 光标往回(Backward)移动一个单词位置 | +| Esc | F | 光标向前(Forward)移动到当前单词的头部 | +| Esc | B | 光标往回(Backward)移动到当前单词的尾部 | + +************************ + ## Search -| Controller | Key | comment | -|:---|:----|:----| -| Ctrl | P | 调出命令历史中的前一条(Previous)命令,相当于通常的上箭头 | -| Ctrl | N | 调出命令历史中的下一条(Next)命令,相当于通常的下箭头 | -| Ctrl | O | 运行上翻下翻出来的命令, 并且自动将下一条命令填入 | -| Ctrl | R | 向上搜索相关命令(reverse-i-search)继续按 Ctrl R 则继续搜索上一条 | -| Ctrl | S | 与 Ctrl R 类似, 但是是向下搜索 | - -************************** + +| Controller | Key | comment | +| :--------- | :-- | :----------------------------------------------------------------- | +| Ctrl | P | 调出命令历史中的前一条(Previous)命令,相当于通常的上箭头 | +| Ctrl | N | 调出命令历史中的下一条(Next)命令,相当于通常的下箭头 | +| Ctrl | O | 运行上翻下翻出来的命令, 并且自动将下一条命令填入 | +| Ctrl | R | 向上搜索相关命令(reverse-i-search)继续按 Ctrl R 则继续搜索上一条 | +| Ctrl | S | 与 Ctrl R 类似, 但是是向下搜索 | + +************************ + ## Control -| Controller | Key | comment | -|:---|:----|:----| -|Ctrl |Z|暂停程序 | -| Ctrl | S | 停止回显当前Shell | -| Ctrl | Q | 恢复回显当前Shell | + +| Controller | Key | comment | +| :--------- | :-- | :---------------- | +| Ctrl | Z | 暂停程序 | +| Ctrl | S | 停止回显当前Shell | +| Ctrl | Q | 恢复回显当前Shell | + +************************ + +# 常见对比 + +## 文件系统对比 + +> [参考: 如何选择文件系统:EXT4、Btrfs 和 XFS ](https://linux.cn/article-7083-1.html) + +目前 Linux 大多采用 ext4, Btrfs + +Btrfs 的快照功能很适合 Arch 系统,滚动更新挂掉的话可以通过历史快照恢复回来 + +## 桌面环境对比 + +> [Arch Doc: desktop environment](https://wiki.archlinux.org/index.php/Desktop_environment_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) +> [参考: Linux下15款桌面环境](https://www.lulinux.com/archives/444) + +1. **gnome** 占用资源中等,个人对该桌面不感冒 +2. **xfce** 占用资源少且稳定,但是UI方面可定制的点较少,目前也是个人使用了5年+的桌面管理器 +3. **kde** 功能强大,占用资源中等 + - [Arch Doc: KDE](https://wiki.archlinux.org/index.php/KDE) + - [知乎 KDE如何配置得漂亮大气?](https://www.zhihu.com/question/54147372) +4. **dde-wm** Deepin 基于 `gnome mutter`设计的桌面环境,小bug略多,而且渲染会占用较多资源,容易卡顿,但是美观操作方便 + 1. 后续Deepin基于kwin更换了窗口管理器 `dde-kwin`,流畅了一些,但是窗口顶部有个大的Title [隐藏Deppin大标题栏](https://www.jianshu.com/p/f90526bbe0c9) + +- [dde kde gnome](https://bbs.deepin.org/forum.php?mod=viewthread&tid=38498) + +> [X窗口系统的协议和架构](http://www.cnblogs.com/noble/p/4144098.html) +> [Arch Doc: Xorg](https://wiki.archlinux.org/index.php/Xorg_(简体中文)) + +## 窗口管理器对比 + +> [Arch Doc: window manager](https://wiki.archlinux.org/index.php/Window_manager_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) + +- [awesome window manager](https://awesomewm.org/) `平铺式的` + +************************ + +## 文件管理器对比 + +> 有单窗口,双列,命令,简洁轻量,笨重完整 各种各样的选择 + +- `nautilus` Gnome默认 挺好用,但是不能自动挂载分区 +- `deepin-filemanager` deepin默认,较为方便,但是打开手机会卡根本打不开 +- `pcmanfm` 左边侧栏目录树 会同步nautilus的配置 +- `rox-filer` 特别小,单击打开,迅速定位文件,适合找东西用 +- `thunar` 解决了nautilus的缺点,内存也很省 +- `dolphin` 多标签页,目录树方式查看 +- `nemo` mint默认的,功能齐全,会同步nautilus的配置,同样有目录树而且是两边都有 +- `tuxcmd` Tux Commander 双列,小,直接的目录树,学习成本高点 +- `ranger` 命令行内文件浏览和操作 +- [Sigma File Manager](https://github.com/aleksey-hoffman/sigma-file-manager) + +## 包管理 +- 最通用的方式是编译安装,但是有门槛和编译环境兼容性问题 +- 其次是绑定发行版的包,常分为两类 Debian系的deb包对应apt管理,REHL系的rpm包对应yum dnf管理 +- 然后就是最近推行的 snap flatpak AppImage 等通用包,大致逻辑都是将软件包所有动态依赖项打包进去隔离,类似静态编译以支持跨发行版,当然缺点就是包的大小,但是相比于软件可用,存储也是小问题了。 + +### AppImage + +### snap +只能从Ubuntu公司私有运营的商店下载软件,且apt安装部分软件时会替换为snap安装,因此被抨击背离Linux文化。 +snap应用都是整个打成压缩包并且将每个软件单独挂载在只读的squashfs格式的分区下,应用启动时解压再执行 + +- [A universal app store for Linux](https://snapcraft.io/) +- [如何在Ubuntu中完全移除Snap](https://cloud.tencent.com/developer/article/2168090) +- [snap 已经在污染 apt](https://v2ex.com/t/1037576) + +### flatpak +- [Bubble wrap](https://wiki.archlinux.org/title/Bubblewrap) + +************************ + +# Tips + +> man help 后接使用的命令,就可以得到用户手册和帮助文档 + +## 一行执行多条命令 + +- `&&` 第2条命令只有在第1条命令成功执行之后才执行 根据命令产生的退出码判断是否执行成功(0成功,非0失败) +- `||` 执行不成功(产生了一个非0的退出码)时,才执行后面的命令 +- `;` 顺序执行多条命令,当;号前的命令执行完(不管是否执行成功),才执行;后的命令。 +- `&` 并行执行命令,没有顺序 +- [tty 虚拟终端等概念](https://www.ibm.com/developerworks/cn/linux/l-cn-termi-hanzi/) +- Centos上which并不是命令, 而是别名! + + - `which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'` + +************************ + +## 让命令在后台运行 + +> [参考 Linux 技巧:让进程在后台可靠运行的几种方法 ](https://www.ibm.com/developerworks/cn/linux/l-cn-nohup/) + +- 命令后接 & (只是让进程成为job并在当前终端的后台执行 hup信号仍然能影响到) + +运行的命令不因 用户注销,网络中断等因素而中断 `nohup, disown, screen, setid` + +- 将进程对 hang up 信号免疫: + - nohup, disown +- 将进程的父进程改为1号进程: + - setid 或者 `(command &)` +- screen + - 将进程托管给screen 类似tmux + +> 示例 +1. 使用 `nohup`就能屏蔽hup信号,标准输出会输出到当前目录下的nohup.out文件. `nohup 命令 &` + 1. 将标准输出重定向到空设备 并将错误输出重定向到标准输出 `nohup 命令>/dev/null 2>&1` +2. 例如 在当前目录后台打开文件管理器 `(dde-file-manager . &) >/dev/null 2>&1` + +- ssh登录后 执行命令 (./xxx.sh &) 然后断开ssh连接,此时该脚本进程的 1 标准输出会显示 pst 被删除 例如 `1 -> /dev/pts/10 (deleted)` + - 此时可使用 strace 命令得到标准输出 `strace -e write -p pid` (但局限于echo printf pwd等命令的输出,其他命令的输出不会被trace) + +************************ + +## 修改主机名 + +- `sudo hostname linux` 重启终端即可看到修改 +- 但是重启电脑会恢复原有名字修改如下文件永久: `sudo gedit /etc/hostname` 也许需要更改 `/etc/hosts` +- 立即生效,也要重新登录 `hostname -F /etc/hostname ` + +************************ + +## 文件类型默认打开方式 MIME + +> xdg-open 命令 + +************************ + +## 熵池 + +> [参考: Linux下熵池大小导致的一些问题](https://blog.csdn.net/chinoukin/article/details/102566755) + +机器的环境中充满了各种各样的噪声,如硬件设备发生中断的时间,用户点击鼠标的时间间隔等是完全随机的,事先无法预测,以此作为熵池来源。 + +查看当前熵池大小 cat /proc/sys/kernel/random/entropy_avail +熵池最大值 cat /proc/sys/kernel/random/poolsize + +当熵池不够时,会导致 gpg tomcat 等应用出现阻塞 + +可使用 [rng-tools](https://wiki.archlinux.org/index.php/Rng-tools) [Github](https://github.com/nhorman/rng-tools)进行补充熵池 + +- [ ] 但是 rng 项目的实现原理呢 diff --git a/Linux/Base/LinuxCommand.md b/Linux/Base/LinuxCommand.md index 23f5e07..77cd228 100644 --- a/Linux/Base/LinuxCommand.md +++ b/Linux/Base/LinuxCommand.md @@ -1,84 +1,152 @@ -`目录 start` - -- [系统常用基础命令](#系统常用基础命令) - - [输入输出](#输入输出) - - [重定向](#重定向) - - [管道](#管道) - - [xargs](#xargs) - - [time](#time) - - [date](#date) - - [grep](#grep) - - [定时任务](#定时任务) - - [crontab](#crontab) - - [Systemd](#systemd) -- [实用的工具](#实用的工具) - - [终端工具](#终端工具) - - [图形化工具](#图形化工具) - - [剪贴板管理](#剪贴板管理) - -`目录 end` |_2018-08-30_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux常用命令 +date: 2018-12-15 11:11:55 +tags: + - 工具 +categories: + - Linux +--- + +💠 + +- 1. [系统常用基础命令](#系统常用基础命令) + - 1.1. [系统内置命令](#系统内置命令) + - 1.1.1. [Shell内建命令](#shell内建命令) + - 1.2. [输入输出](#输入输出) + - 1.2.1. [重定向](#重定向) + - 1.3. [管道](#管道) + - 1.3.1. [xargs](#xargs) + - 1.4. [time](#time) + - 1.5. [date](#date) + - 1.6. [grep](#grep) + - 1.7. [script](#script) + - 1.8. [定时任务](#定时任务) + - 1.8.1. [crontab](#crontab) + - 1.8.2. [Systemd](#systemd) + +💠 2024-07-26 19:05:14 **************************************** # 系统常用基础命令 +> [Linux 命令大全](http://man.linuxde.net/) + +> [The Art of Command Line](https://github.com/jlevy/the-art-of-command-line) `高效优美的工具` + +## 系统内置命令 +> /bin/* 系统自带命令 + +- false 以失败码退出程序 +- `stty -a` 查看快捷键映射 + +- 终端内执行循环 + - `for i in $(seq 1 10); do echo $i; done` 语法和Shell差不多, 但是需要在循环体的每一句加上`;` + +### Shell内建命令 +- whence 查看命令的真实面貌 (zsh中的内建命令) +- where 查找命令的位置 (Zsh中内建命令) +- which 寻找命令的位置 +- type 展示命令的描述 + +************************ ## 输入输出 ### 重定向 -- 输出重定向 `> a.log 2>&1` 表示为将2也输出到标准输出 **为了方便记忆也可以将&理解为C中的取地址符:2重定向到1的地址** - - 2是错误输出 - - 1是标准输出 +- 输出重定向 `> a.log 2>&1` 表示为将2也输出到标准输出 **为了方便记忆也可以将 &1 理解为C语言中的取地址符:2重定向到1的地址** + - 2 是错误输出 1 是标准输出 ****************** + ## 管道 -> [参考博客: linux 管道 ](http://www.cnblogs.com/davidwang456/p/3839874.html) -> [参考博客: linux shell 管道命令(pipe)使用及与shell重定向区别](http://www.cnblogs.com/chengmo/archive/2010/10/21/1856577.html) -- [ ] 学习管道的使用 +> [参考: linux 管道 ](http://www.cnblogs.com/davidwang456/p/3839874.html) +> [参考: linux shell 管道命令(pipe)使用及与shell重定向区别](http://www.cnblogs.com/chengmo/archive/2010/10/21/1856577.html) + +- 接收输入流且输出流 类似于 Java8 中的 peek() 函数 + - `cat README.md | grep Java | tee > java.log | grep -v Maven` ### xargs -- [ ] xargs 让命令更为灵活 +> 常在管道中使用 能将输入流转为 命令 的参数 + +- 输出所有的md文件的内容 `find . -name "*.md" | xargs cat | less` +- 定义替换符 `xargs -I {} echo {}` *************************** + ## time > 可以用于统计命令运行消耗的时间 - bash内置简易time `time` 和 /usr/bin/time `\time` - `\time -v ls -al` + ## date -> 获取日期和时间 `date +%Y_%m_%d_%H:%M:%S` +> 获取日期和时间 `date +%Y_%m_%d_%H:%M:%S` 或者 `date +%F %T` - 获取前一天日期 `date --date='1 day ago' -R` - 将秒时间戳转换为日期 `date --date='@1524738626'` +- 设置日期或时间 `date -s '2020-12-01'` 或 `date -s '23:00:20'` +- 日期计算 `date -d "+10 days"` ## grep +> `g` (globally) search for a `re` (regular expression ) and `p` (print ) the results. + > egrep [相关网页](http://man.linuxde.net/grep) 与 grep -E 等价 -- 正则 `grep -E "[1-9]+"` 注意` [] 和 ()`的区别 `[]` 是里面单个字符 `()`是里面的全部 +- [正则表达式](/Skills/RegularExpression.md) + - 标准正则 `grep -E "[1-9]+"` 注意 `[]` 是里面单个字符 `()`是里面所有字符一起 用于匹配 + - Perl正则 `grep -P ''` -- -o 一行内多次匹配 `grep -o 的 total.md | wc -l` 统计所有`的`的数量 +- -o 只输出匹配值 `一行内多个匹配时多行输出` + - 统计所有main字符数量 `grep -o main test.log | wc -l` - -i 忽略大小写 -## 定时任务 -### crontab -> [参考博客 shell定时任务crontab](http://www.cnblogs.com/taosim/articles/2007056.html) -`minute hour day-of-month month-of-year day-of-week commands ` - -> cron 脚本中的操作命令 最好都使用绝对路径 +> eg: +- 匹配中文 `-P '[\p{Han}]'` +- 截取字符 `ping jd.com | grep -oP '(?<=time=).*(?=ms)'` 提取出ms值。 +- 两个文件的差异行 `grep -vwf 文件1 文件2` -### Systemd -> [参考博客: Systemd 定时器教程](http://www.ruanyifeng.com/blog/2018/03/systemd-timer.html) `配置和使用上比Crontab更繁杂, 但是有更多的可控制项` +************************ +## script +> make typescript of terminal session -******************************************* +录制终端 -# 实用的工具 -## 终端工具 -> [详细](/Skills/Application/Terminal.md) +************************ -## 图形化工具 -### 剪贴板管理 -> [参考博客: 面向 Linux 的 10 款最佳剪贴板管理器](https://linux.cn/article-7329-1.html) - -- CopyQ 比较好用 +## 定时任务 +### crontab +`minute hour day-of-month month-of-year day-of-week commands ` -> [参考博客: 这9个Linux命令非常危险 请大家慎用](https://www.jb51.net/LINUXjishu/498660.html) +> [为什么crontab不执行](https://segmentfault.com/a/1190000020850932) +- cron 脚本中的操作命令 都使用绝对路径, 必须注意环境变量问题 +- 以及每行配置都需要以换行符结尾 + +```sh + SHELL=/bin/sh + PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + + # Example of job definition: + # .---------------- minute (0 - 59) + # | .------------- hour (0 - 23) + # | | .---------- day of month (1 - 31) + # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... + # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat + # | | | | | + # * * * * * user-name command to be executed +``` + +- 编辑配置文件 `crontab -e` +- 时间配置格式 + - * 表示所有值; + - ? 表示未说明的值,即不关心它为何值; + - - 表示一个指定的范围; + - , 表示附加一个可能值; + - / 符号前表示开始时间,符号后表示每次递增的值; +- 例如: + - 每分钟执行二进制命令 + - 每分钟执行脚本 + +> 注意 +>> 如果command是shell脚本,注意执行环境和权限问题 -> [参考博客: 关于 Linux 你可能不是非常了解的七件事](https://linux.cn/article-8934-1.html) \ No newline at end of file +### Systemd +> [参考: Systemd 定时器教程](http://www.ruanyifeng.com/blog/2018/03/systemd-timer.html) `配置和使用上比Crontab更繁杂, 但是有更多的可控制项` diff --git a/Linux/Base/LinuxCompressFile.md b/Linux/Base/LinuxCompressFile.md index 743ef04..491be20 100644 --- a/Linux/Base/LinuxCompressFile.md +++ b/Linux/Base/LinuxCompressFile.md @@ -1,22 +1,52 @@ -`目录 start` - -- [Linux操作压缩文档](#linux操作压缩文档) - - [tar](#tar) - - [tar归档和压缩](#tar归档和压缩) - - [rar](#rar) - - [zip](#zip) - - [gz](#gz) - - [7Z](#7z) - - [总结](#总结) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux平台上压缩和解压工具 +date: 2018-12-15 11:12:34 +tags: + - 工具 +categories: + - Linux +--- + +💠 + +- 1. [Linux操作压缩文档](#linux操作压缩文档) + - 1.1. [tar](#tar) + - 1.1.1. [tar归档和压缩](#tar归档和压缩) + - 1.2. [rar](#rar) + - 1.3. [zip](#zip) + - 1.4. [gzip](#gzip) + - 1.5. [xz](#xz) + - 1.6. [7Z](#7z) + - 1.7. [Zstd](#zstd) +- 2. [压缩文件内容预览搜索](#压缩文件内容预览搜索) +- 3. [压缩文件密码](#压缩文件密码) + +💠 2024-09-11 14:55:54 **************************************** # Linux操作压缩文档 > Linux默认自带ZIP压缩,最大支持4GB压缩,RAR的压缩比大于4GB. +| 文件名模式 | 解压方式 | +|:----|:----| +| *.tar | tar -xvf 解压 +| *.tar.gz 和 *.tgz | tar -xzf 解压 +| *.tar.xz | tar -xJf 解压 +| *.tar.Z | tar -xZf 解压 +| *.tar.bz2 | tar -xjf 解压 +| | | +| *.gz | gzip -d 或者 gunzip 解压 +| *.bz2 | bzip2 -d或者用bunzip2 解压 +| *.Z | uncompress 解压 +| *.xz | xz -d 解压 +| *.rar | unrar e 解压 +| *.zip | unzip 解压 +| *.zst | unzstd 解压 + +************************ + ## tar -1 `这五个是独立的参数, 五个参数之间互斥` +1 `以下五个互斥独立参数,有且仅能使用一个` - c : 打包 压缩 - x : 解压 - t : 查看内容 不解压 @@ -24,22 +54,18 @@ - u : 更新原压缩包中的文件 2 `可选参数` -2.1 `下面的参数是根据需要在压缩或解压档案时可选的。` -- z:有gzip属性的 -- j:有bz2属性的 -- Z:有compress属性的 +2.1 `下面的参数是根据需要在压缩或解压档案时可选的` - v:显示所有过程 - O:将文件解开到标准输出 2.2 `其他可选参数` - `-p` 保留绝对路径符 - `-v` 将压缩或解压的过程输出 +- `-C` 解压到指定目录 3 `最后` - `-f 是必须的,-f: 使用档案名字,切记,这个参数是最后一个参数,后面只能接文件或目录` -> 以上则组合出了 tar 的所有使用场景 - *************************** **示例 :** - `tar -rf all.tar *.gif`这条命令是将所有.gif的文件增加到all.tar的包里面去。 @@ -52,20 +78,28 @@ ******************** ### tar归档和压缩 + +| 字母 | 压缩方式 | +|:----:|:----:| +| z | gz | +| Z | Z | +| j | bz2 | +| J | xz | + +************************** +本质上 tar 的压缩和解压都是调用对应的软件完成的, 例如 `tar cJf a.tar.xz a/` 就是先tar归档一下, 然后调用 xz 完成压缩 + > 压缩 - tar -cf a.tar *.txt **仅仅归档,没有压缩** - 1. `-czf` tar.gz **gz压缩** - 1. `-cjf` tar.bz2 - 1. `-cZf` tar.Z - 1. `-cJf` tar.xz + - tar -czf a.tar.gz *.txt`归档, 并使用gz格式压缩归档包, 以此类推` > 解压 -- `tar -xf file.tar` // 解压 tar -- `tar -xzf file.tar.gz` // 解压 tar.gz -- `tar -xjf file.tar.bz2` // 解压 tar.bz2 -- `tar -xZf file.tar.Z ` // 解压 tar.Z +- `tar -xf file.tar` // 解压 .tar 归档文件 + - tar -xzf a.tar.gz `解压使用gz格式压缩的压缩包, 以此类推` + + +************************ -**************** ## rar > 压缩 - `rar a jpg.rar *.jpg` // rar格式的压缩 @@ -93,39 +127,63 @@ - -q 终端不输出 - -d 指定解压目录 - -l 不解压,查看所有文件 - - -O 指定编码 + - -O 指定编码 例如 GBK `-O cp936` 注意有些发行版需安装 `unzip-iconv` 才支持该参数 + +- zipinfo 展示压缩包明细信息 *************************** -## gz -> gunzip +## gzip +> gzip gunzip. 常见压缩包格式: .tar.gz .tgz + +由于只能操作单个文件, 所以一般是借助于 tar 归档后再压缩 + +> 压缩 +- gzip 文件 + +> 解压 +- gzip -d 文件 或者 gunzip 文件 + +## xz +> xz. 常见压缩包格式: .xz .txz .lzma .tlz + +和 gzip 类似, 只能操作单个文件, 但是压缩率高于 gzip, 伴随的是压缩时间要长一些 + +> 压缩 +- xz 文件 + +> 解压 +- xz -d 文件 -- [ ] gunzip 命令的学习 *************************** ## 7Z > 安装 apt install p7zip-full 或者 p7zip > man 7z 查看帮助文档 - `7z [... ] [... ] [<@listfiles>... ]` - - b benchmark 评测分数 [个人电脑评测](https://gitee.com/kcp1104/codes/0r72axdcp1yewmnljhi8g38) + - `b`: benchmark 评测压缩和解压速率 > 压缩 - a 压缩包名 文件名 > 解压 - 7z x file - - -o 路径 + - `-o` 路径 -- [ ] 7z命令的 学习使用 +## Zstd +> [Zstandard](https://facebook.github.io/zstd/#benchmarks) -*************** -## 总结 -| 文件名模式 | 解压方式 | -|:----|:----| -| *.tar | 用 tar -xvf 解压 | -| *.gz|用 gzip -d或者gunzip 解压| -|*.tar.gz和*.tgz| 用 tar -xzf 解压| -|*.bz2|用 bzip2 -d或者用bunzip2 解压| -|*.tar.bz2|用tar -xjf 解压| -|*.Z|用 uncompress 解压| -|*.tar.Z| 用tar -xZf 解压| -|*.rar|用 unrar e 解压| -|*.zip|用 unzip 解压| +- `zstd` 压缩, 源文件压缩为源文件.zst +- `unzstd` / `zstd -d` 解压 + +************************ + +# 压缩文件内容预览搜索 +> [参考: Unix Z Commands – Zcat, Zless, Zgrep, Zegrep and Zdiff Examples ](https://linoxide.com/linux-how-to/z-commands-zcat-zless-zgrepzegrep-zdiff-examples/) + +- `zcat log.tgz | grep -a "pattern"` 等价于 `zgrep "pattern" log.tgz` + - 相关参数说明 man 文档 + + +************************ + +# 压缩文件密码 +- rarcrack 暴力破解 diff --git a/Linux/Base/LinuxDebug.md b/Linux/Base/LinuxDebug.md new file mode 100644 index 0000000..34975fb --- /dev/null +++ b/Linux/Base/LinuxDebug.md @@ -0,0 +1,27 @@ +--- +title: Linux上的调试 +date: 2020-12-23 16:07:56 +tags: +categories: +--- + +💠 + +- 1. [Linux上的调试](#linux上的调试) + - 1.1. [strace](#strace) + - 1.2. [radare2](#radare2) + +💠 2024-05-19 23:35:07 +**************************************** +# Linux上的调试 + +> [参考: Linux调试工具](https://www.cnblogs.com/lidabo/p/4377545.html) +> [参考: 在 Linux 上分析二进制文件的 10 种方法 ](https://linux.cn/article-12187-1.html) + +## strace +> [Github strace](https://github.com/strace/strace) + +- strace -p PID _查看系统调用_ + +## radare2 +> [Github](https://github.com/radareorg/radare2) diff --git a/Linux/Base/LinuxDirectoryStructure.md b/Linux/Base/LinuxDirectoryStructure.md index 26a5e14..7a34bca 100644 --- a/Linux/Base/LinuxDirectoryStructure.md +++ b/Linux/Base/LinuxDirectoryStructure.md @@ -1,40 +1,179 @@ -`目录 start` - -- [Linux 目录结构](#linux-目录结构) - - [/root](#root) - - [/home](#home) - - [/proc](#proc) - - [网络](#网络) - - [/etc](#etc) - - [/etc/alternatives](#etcalternatives) - - [/etc/apt](#etcapt) - - [使用](#使用) - - [查看发行版](#查看发行版) - - [查看系统所有用户信息](#查看系统所有用户信息) - - [/tmp](#tmp) - -`目录 end` |_2018-09-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux目录结构 +date: 2018-12-15 11:14:08 +tags: + - 基础 +categories: + - Linux +--- + +💠 + +- 1. [Linux 目录结构](#linux-目录结构) + - 1.1. [/boot](#boot) + - 1.2. [/bin 和 /sbin](#bin-和-sbin) + - 1.3. [/root](#root) + - 1.4. [/run](#run) + - 1.5. [/home](#home) + - 1.6. [/lost+found](#lost+found) + - 1.7. [/proc](#proc) + - 1.7.1. [process](#process) + - 1.7.2. [net](#net) + - 1.7.3. [swaps](#swaps) + - 1.7.3.1. [设置交换内存文件](#设置交换内存文件) + - 1.8. [/usr](#usr) + - 1.8.1. [/usr/local](#usrlocal) + - 1.9. [/etc](#etc) + - 1.9.1. [/etc/passwd](#etcpasswd) + - 1.9.2. [/etc/shadow](#etcshadow) + - 1.9.3. [/etc/alternatives](#etcalternatives) + - 1.9.4. [/etc/apt](#etcapt) + - 1.9.5. [/etc/fstab](#etcfstab) + - 1.9.6. [/etc/systemd](#etcsystemd) + - 1.10. [/lib](#lib) + - 1.11. [/dev](#dev) + - 1.12. [/tmp](#tmp) + - 1.13. [/usr](#usr) + - 1.14. [/var](#var) +- 2. [使用](#使用) + - 2.1. [查看发行版](#查看发行版) + - 2.2. [查看系统所有用户信息](#查看系统所有用户信息) + +💠 2024-09-12 16:01:31 **************************************** # Linux 目录结构 > Linux 系统目录结构的大致分布以及说明 +## /boot + +> 该目录存放的是启动Linux是的核心文件,具体包含一些镜像文件和链接文件,因此这个目录非常重要,,如果遭到破坏,系统将无法启动 + +- 查看引导项 `sudo efibootmgr -v` + +## /bin 和 /sbin + +> 这两个目录存放的是可执行的二进制文件。bin其实是binary的缩写,/bin目录下存放的就是我们经常使用的Linux命令。sbin中的s其实是Super User的意思,也就是,只有超级用户才可以执行这些命令 + ## /root > root用户的默认用户目录 +## /run + +> 该目录是外在设备的自动挂载点目录,用来自动挂载光驱和U盘。另外还有一个/media目录,与/run目录作用基本类似,在CentOS 7之前使用。还有一个/mnt目录,主要用来手动挂载一些移动设备 + ## /home -> 非root用户的默认用户目录 +> 非root用户的默认用户目录的父目录 + +## /lost+found + +> 该目录用于保存丢失的文件。不恰当的关机操作和磁盘错误均会导致文件丢失,这些丢失的文件会临时放在/lost+found下,系统重启后,引导进程会运行fsck程序,该程序就会发现这些文件爱你。除了’/‘分区上的这个目录外,在每个分区上均有一个lost+found目录 + +************************ -******************************** ## /proc -> 进程的目录, 一个个进程看起来是一个个目录(并不是真正的目录,这是一个虚拟文件系统), 使用进程号作为目录名 +> 此目录是虚拟目录,目录中所有信息都是内存的映射,通过这个虚拟的内存映射目录,可以和内核内部数据结构进行交互,获取有关进程的有用信息,同时也可以在系统运行中修改内核参数。 + +`man 5 proc` +[procstat](http://brokestream.com/procstat.html) + +特别的是 /proc存在于内存中,而不是硬盘上,是一个虚拟的文件系统。 + +| 文件或目录 | 说明 | +| ----------- | --------------------------------- | +| cpuinfo | 关于系统CPU的详细信息,包含CPU名称、型号和类型 | +| meninfo | 内存信息,包含物理内存和虚拟内存 | +| filesystems | 当前系统支持的文件系统类型 | +| devices | 内核中的设备驱动程序列表 | +| net | 网络使用协议以及状态信息 | +| dma | 当前使用的dma通道 | +| ioports | 当前使用的I/O端口 | +| modules | 当前系统加载的内核模块信息 | +| stat | 系统的各种状态信息 | +| uptime | 系统总的启动时间和空闲时间,以秒为单位 | +| version | 内核版本信息 | +| loadavg | 系统平均负载 | +| kcore | 系统物理内存的映像,与物理内存大小完全一样,但实际不占用那么大空间 | +| kmsg | 内核输出信息,同时被输出到rsyslog | + +- `/proc/sys/fs/inotify/max_user_watches` +- `/proc/sys/kernel/threads-max` +- `/proc/sys/kernel/pid_max` -### 网络 -1. ARP: /proc/net/arp cat该文件, 如果发现里面有重复的mac地址, 并且有机器伪装成了网关的mac 就说明遭受了ARP攻击 `或者 arp -a` +### process +> /proc/{pid} + +- oom_* : oom-killer 关注的指标,动态计算得到,当需要有进程被kill时会找出指标值高的进程kill掉 +- exe cmdline 进程的启动命令 + +************************ + +> /proc/interrupts +> /proc/irq + +[基础篇:经常说的 CPU 上下文切换是什么意思?(下)](https://freegeektime.com/100020901/70077/) + +### net +1. ARP: /proc/net/arp 若该文件内有重复的mac地址, 并且有机器伪装成了网关的mac 就说明遭受了ARP攻击 `或者 arp -a` - arping 10.91.255.254 能查看到真实的mac地址 +### swaps +`cat /proc/swaps` + +#### 设置交换内存文件 +```sh + dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon /swapfile && echo "/swapfile swap swap defaults 0 0" >> /etc/fstab +``` +- 创建一个4G交换文件 `dd if=/dev/zero of=/swapfile bs=1024k count=4096` +- 设置文件权限 `chmod 0600 /swapfile` +- 格式化成交换文件的格式 `mkswap /swapfile` +- 启用该文件作为交换分区的文件 `swapon /swapfile` +- `/etc/fstab` 中配置 `/swapfile swap swap defaults 0 0` 让交换分区在**开机后自动挂载** + +- 修改交换内存开始使用的阈值 + - `sudo sysctl vm.swappiness=15` 临时修改重启注销失效, 查看:`cat /proc/sys/vm/swappiness` + - 永久修改:`/etc/sysctl.conf ` 文件中设置开始使用交换分区的触发值: `vm.swappiness=10` + - 表示物理内存剩余`10%` 才会开始使用交换分区 + - `建议,笔记本的硬盘低于 7200 转的不要设置太高的交换分区使用,会大大影响性能,因为交换分区就是在硬盘上,频繁的交换数据` + +*************** + +## /usr + +### /usr/local +> 全局配置, 对应的局部配置目录是 `~/.local`, 惯例是局部覆盖全局配置 + +``` + ├── bin 可执行文件(Python安装应用的目录) + ├── lib 库 + └── share 应用的配置 +``` + +- share 目录下 存放大量应用配置: 主题,图标,字体,desktop文件 什么的 + ************************ ## /etc -> 系统以及应用的配置目录 +> 主要用于存放系统管理相关的配置文件以及子目录。其中比较重要的有系统初始化文件/etc/rc、用户信息文件/etc/passwd等,相关网络配置文件和服务启动文件也在该目录下 + +| 文件名和目录 | 主要作用 +| -------------------------------------- | ---------------------------------------- +| hosts | 设定用户自己的IP与名字的对应表 | +| resolv.conf | 客户端DNS配置文件 | +| systemd/system/*.wants | 此目录包含所有服务启动脚本,开机时系统将自动启动这些服务(CentOS 7新增) | +| sysconfig/network-scrripts/ifcfg--eth0 | IP地址配置文件(CentOS 7后网卡名从类似eth0、eth1的标识变为enp0s3、enps4标识) | +| X11 | X-Window的配置文件 | +| rsyslog.conf | 系统日志输出配置文件 | +| crontab | 系统级别的守护进程配置文件 | +| services | 定义系统服务与端口的对应关系 | +| profile | 系统全局环境变量配置文件 | +| sysctl.conf | 系统内核参数配置文件(在CentOS 7后,内核参数配置文件转移到了/usr/lib/sysctl.d目录下,但sysctl.conf文件仍有效,并且可覆盖/usr/lib/sysctl.d中的配置) | + +### /etc/passwd +> 用户的组,权限, Home目录, 默认shell 相关配置 + +- 禁止 Shell 登录, 将原有默认 shell `/bin/bash` 改为 `/sbin/nologin` + +### /etc/shadow +> 存放用户密码的文件,每个用户的密码加密后都放入此文件 ### /etc/alternatives alternative是可选项的意思. @@ -43,34 +182,110 @@ alternative是可选项的意思. update-alternatives 命令就是操作的这个目录, 实现的步骤往往是在该目录建立一个软链接, 然后又从这里建立软链接到 /usr/bin 下, 实现将命令加入到 PATH 中的目的 -- [ ] 学习 - ### /etc/apt > Debian 系 apt 包管理器的配置目录 1. /etc/apt/sources.list.d/ 这个目录是放别的应用需要的软件列表 -*************************** +### /etc/fstab +> static file system infomation -### 使用 -> 具体配置文件的使用 +> [A complete fstab guide ](http://www.linuxstall.com/fstab/) -### 查看发行版 -`cat /etc/issue` 通用 -`cat /etc/redhat-release` redhat系 +Use 'blkid' to print the universally unique identifier for a device; +this may be used with UUID= as a more robust way to name devices that works even if disks are added and removed. -_查看内核版本_ -`cat /proc/version` -`uname -a` +行内容结构 ` ` -### 查看系统所有用户信息 -> /etc/passwd 包含了用户,用户组,用户home目录 shell类型等信息 -> 看第三个参数:500以上的,就是后面建的用户了.其它则为系统的用户. +### /etc/systemd +systemd的配置文件目录,此目录是Linux启动的重要部分,用来完成对整个系统的基本初始化配置 + +************************ +## /lib + +> 该目录存放的是共享程序库和映像问津,,可供很多程序使用。通过这些共享映射文件,每个程序就不必分别保存自己的库文件,Linux提供一组可供所有程序使用的文件。在该目录中,还包含引导进程所需的静态库文件 + +## /dev +> 包含系统所有的设备文件 + +| 设备名 | 具体含义 | +| ------- | ---------------------------------------- | +| fd* | 代表软盘设备,fd0代表第一个软盘设备,fd1代表第二个软盘设备 | +| audio* | 代表声卡设备 | +| hd* | 代表IDE硬盘设备。hda代表第一块IDE硬盘,hdb代表第二块IDE硬盘 | +| sd* | 代表SCSI设备,sda代表第一块SCSI硬盘,sdb代表第二块SCSI硬盘 | +| lp* | 代表并行串口 | +| pty* | 代表网络中登录的远程终端设备 | +| ram* | 代表系统内存 | +| tty* | 代表Linux上的虚拟控制台,也叫字符控制台。tty1代表第一个虚拟控制台,tty2代表第二个虚拟控制台,以此类推,Linux上一共有6个虚拟控制台 | +| ttyS* | 代表串行端口。 | +| console | 代表系统控制台,也就是桌面控制台,可以直接连接到显示器 | +| null | 输出空设备 | + +*************************** ## /tmp -> 应用缓存目录, 在使用时存放缓存文件, 在计算机重启后就会被清理 +> 应用缓存目录, 存放缓存文件 + +系统重启时会被清空,以及操作系统运行时不定期创建和清除文件 + +- 在安装Linux时如果没有明确的分区, /tmp 就会属于 / 分区, 那么就要给 / 留有足够的大小, 不然 /tmp 分区不足会导致应用运行异常 + - 例如 Tomcat 在运行时就需要使用 +- 清理的机制 + - 如果新建文件在 /tmp 目录下, 文件的内容会随着系统重启而消失 但是文件依旧存在(空文件) + +- systemd内核下有 `systemd-tmpfiles` 管理临时目录的生命周期 +- tmpwatch `定时清理 tmp 目录,类似于 watchdog` -- 在安装Linux时如果没有明确的分区, 就会属于 / 分区, 那么就要给 / 留有足够的大小, 不然 /tmp 分区不足会导致应用运行异常 -- 例如 Tomcat 在运行时就需要使用, 然后Deepin的截图也会将截图缓存到该目录下 ... +************************ + +## /usr + +> 主要用于存放应用程序和文件。如果在系统安装的时候选择了很多安装软件包,那么这些软件包默认会安装到该目录下,平时安装的一些软件默认情况下也会安装在该目录内 + +| 文件或目录 | 主要作用 | +| ------------------ | ------------------------------- | +| lib64以及local/lib64 | 64位操作系统中的函数库目录 | +| src | 该目录包含所有程序的源代码,其中主要是Linux核心程序源代码 | +| local | 该目录存放本地安装的软件和其他文件,与LInux无关 | +| bin以及local/bin | 使用者可执行的二进制文件目录 | +| lib以及local/lib | 32位操作系统使用的函数库目录 | +| sbin以及local/sbin | 该目录存放系统管理员才能执行的指令 | +| include | 此目录包含c语言的头文件,文件扩展名大多数为.h | +| share | 该目录存放共享的文件和数据库 | + +## /var +> 主要用于存放系统运行以及软件运行的日志信息 + +| 文件或目录 | 主要作用 | +| ------- | ---------------------------------------- | +| log | 该目录存放各种应用程序的日志文件,这里的文件是经常变动的,因此需要定期清理 | +| lib | 该目录存放系统正常运行时需要改变的库文件 | +| spool | 该目录是mail、new、打印机队列和其他队列输入、输出的缓冲目录 | +| tmp | 该目录允许比/tmp存放更大的文件 | +| lock | 该目录存放被锁定的文件,很多程序都会在/var/lock下产生一个锁文件,以保证其他程序不能同时使用这个设备或文件 | +| local | 该目录存放/usr/local中所安装程序的可变数据 | +| account | 该目录存放已经格式化的man页 | +| run | 该目录包含到下次系统启动前的系统信息 | + + +************************ +# 使用 +> 具体配置文件的使用 + +## 查看发行版 + +1. `cat /etc/issue` 通用 +1. `cat /etc/redhat-release` redhat系 +1. screenfetch `先安装` +1. lsb_release -a + +_查看内核版本_ +- `cat /proc/version` +- `uname -a` + +## 查看系统所有用户信息 +> /etc/passwd 包含了用户,用户组,用户home目录 shell类型等信息 +> 看第三个参数:500以上的,就是后面建的用户了.其它则为系统的用户. diff --git a/Linux/Base/LinuxEffective.md b/Linux/Base/LinuxEffective.md new file mode 100644 index 0000000..bf63837 --- /dev/null +++ b/Linux/Base/LinuxEffective.md @@ -0,0 +1,521 @@ +--- +title: 高效的Linux +date: 2019-04-18 22:04:15 +tags: + - Effective +categories: + - Linux +--- + +💠 + +- 1. [高效的Linux](#高效的linux) + - 1.1. [Terminal](#terminal) + - 1.1.1. [Terminal 对比](#terminal-对比) + - 1.2. [效率工具](#效率工具) + - 1.2.1. [协作工具](#协作工具) + - 1.2.2. [目录跳转](#目录跳转) + - 1.2.3. [xdotool](#xdotool) + - 1.2.4. [rofi](#rofi) + - 1.3. [远程工具](#远程工具) + - 1.4. [进程管理](#进程管理) + - 1.5. [零散工具集合](#零散工具集合) + - 1.5.1. [剪贴板管理](#剪贴板管理) + - 1.6. [检测工具](#检测工具) + - 1.6.1. [硬盘](#硬盘) + - 1.7. [文本处理](#文本处理) + - 1.8. [文件操作](#文件操作) + - 1.9. [安全工具](#安全工具) + - 1.9.1. [gpg](#gpg) + - 1.9.2. [JumpServer](#jumpserver) +- 2. [多媒体](#多媒体) + - 2.1. [ffmpeg](#ffmpeg) + - 2.2. [图片处理](#图片处理) + - 2.2.1. [ImageMagick](#imagemagick) + - 2.2.1.1. [convert](#convert) + - 2.2.1.2. [多图操作](#多图操作) + - 2.2.2. [asciinema](#asciinema) + - 2.2.3. [图片浏览器](#图片浏览器) + - 2.2.4. [截图](#截图) + - 2.2.5. [录屏](#录屏) + - 2.3. [视频](#视频) + - 2.4. [音频](#音频) + - 2.5. [PDF](#pdf) +- 3. [运行 Windows 应用](#运行-windows-应用) +- 4. [日常应用](#日常应用) + - 4.1. [Office](#office) + - 4.1.1. [QQ](#qq) + - 4.1.2. [wechat](#wechat) + - 4.1.3. [wework](#wework) +- 5. [外设](#外设) + - 5.1. [鼠标](#鼠标) +- 6. [Tips](#tips) + +💠 2024-10-11 15:07:39 +**************************************** +# 高效的Linux + +> [Linux Desktop Setup](https://hookrace.net/blog/linux-desktop-setup/) `一整套工具` +> [Awesome Linux Software](https://github.com/luong-komorebi/Awesome-Linux-Software) + +> [命令行:增强版 ](https://linux.cn/article-10171-1.html) + +> [MAC平台 工具列表](https://github.com/hsdji/tools) `部分Linux可用` + +## Terminal + +> [ttyd](https://github.com/tsl0922/ttyd) + +************************ + +- [sixel](https://en.wikipedia.org/wiki/Sixel) `终端中渲染图片` | [libsixel](https://saitoha.github.io/libsixel/) | [Are We Sixel Yet?](https://www.arewesixelyet.com/) + - [Why Sixel? ](https://www.reddit.com/r/commandline/comments/zkg75e/why_sixel/) + +Manjaro Xfce 使用 sixel: mlterm 或者 konsole +1. yay libsixel, yay mlterm, mlterm -b '#292B2E' 安装和启动mlterm + 1. 查看图片 img2sixel xx.jpg `ImageMagick` + 1. 渲染结果图 [jagger](https://github.com/rs/jaggr) **konsole不支持** + +************************ + +[Terminals Are Sexy](https://github.com/k4m4/terminals-are-sexy) + +### Terminal 对比 + +> 列举出系统可安装终端 +> +> 1. Debian: `sudo apt search terminal | grep -E terminal.+amd64` +> 2. Arch: `yay terminal` +> 3. [Github Topic: terminal-emulator ](https://github.com/topics/terminal-emulator) + +终端可参考功能点: 终端透明化,终端背景图,快捷键设置,终端内颜色自定义,下拉式,标签水平垂直拆分,鼠标键盘交互性,资源占用少 +终极工具 [Tmux](/Linux/Tool/Tmux.md) 可以摆脱终端模拟器的对比和选择,选择最简单省资源的模拟器即可 + +| 终端 | 优点 | 缺点 | 备注 | +| :------------------ | :---------------------------------------------- | :-------------------------------------------------- | :-------------------------------------- | +| `xiki` | 鼠标和键盘高度交互`
` 交互性和复杂度比较高 | | | +| `qterminal` | 设置设计清晰,功能完备 | 终端内容显示兼容性略有问题 资源消耗中等 | | +| `xfce4-terminal` | 配合Xfce启动快 | 配置繁琐 | | +| `gnome-terminal` | 简洁 资源消耗少 | 缺 多标签时,标签栏太大,标签页底部有白边 无法透明化 | 鼠标中键无法复制时需安装 `parcellite` | +| `mate-terminal` | 标签栏更简洁,其余和 `gnome-terminal` 一致 | | | +| `sakura` | 外观上和前两个几乎一样,标签页可以更简洁 | 配置复杂 繁琐 | | +| `deepin-terminal` | 功能很多,主题很多,功能最为强大 | 字体仅可选择内置不可自定义 | | +| `tilda` | 内嵌于桌面上, 小命令方便 | 需要查看文件时不方便 | | +| `terminology` | 样式高度自定义 | | | + +- tilix +- vte + - 支持复制终端输出内容为HTML +- st 不支持中文,unicode字符支持良好 +- black box +- Alacritty + +> 备注 sakura xfce4-terminal 快捷键配置 +- `~/.config/xfce4/terminal/accels.scm` +- 配置语法: [doc](http://troubleshooters.com/linux/sakura.htm) | [config shortcut](https://unix.stackexchange.com/questions/102474/configuring-shortcuts-for-sakura) +- 例如 [修改 Ctrl C V 为复制快捷键](https://bbs.archlinux.org/viewtopic.php?id=260755) `Gtk3起 不支持所谓的鼠标悬浮改快捷键` +```lua + (gtk_accel_path "/terminal-window/copy" "c") + (gtk_accel_path "/terminal-window/paste" "v") +``` + +> 现代终端 +- [wezterm](https://wezfurlong.org/wezterm/index.html) +- [Warp](https://github.com/warpdotdev/Warp) `Rust+AI` +- Tabby +- WindTerm +- [zellij](https://github.com/zellij-org/zellij) +- [kitty](https://sw.kovidgoyal.net/kitty/) `GPU渲染` +- [darktile](https://github.com/liamg/darktile) + +> 终端工具 +- [terminalizer](https://github.com/faressoft/terminalizer)`录制终端` + +************************ + +## 效率工具 + +> 提高工作和开发效率 + +> `通知提醒` +> [Desktop notifications](https://wiki.archlinux.org/index.php/Desktop_notifications) | [xfce notify-send ](https://docs.xfce.org/apps/notifyd/preferences) +> [Desktop Notifications Specification](https://developer.gnome.org/notification-spec/#protocol) +> [Notification Development Guidelines](https://wiki.ubuntu.com/NotificationDevelopmentGuidelines) + +> [Github notify-send.sh](https://github.com/vlevit/notify-send.sh) + +### 协作工具 + +**synergy** + +> 多系统间共享键鼠 + +**scrpy** + +> PC远程操作安卓 + +[scrcpy](https://github.com/Genymobile/scrcpy) + +- [操作流程](http://blog.lujun9972.win/blog/2019/03/20/%E4%BD%BF%E7%94%A8scrcpy%E6%8E%A7%E5%88%B6%E4%BD%A0%E7%9A%84%E6%89%8B%E6%9C%BA/) + +> USB 连接方式 +> 推荐使用USB连接,这样操作起来比较流畅。手机通过USB连接到PC上,在弹出的USB用途中选择 传输文件(MTP) + +> WIFI 方式连接 + +- 确保PC和手机在同一Wifi中 +- 手机先通过USB与PC相连 +- 在PC上运行 `adb tcpip 端口`, 令手机开启端口 +- 断开手机和PC的USB连接 +- 在PC上运行 `adb connect 手机IP:端口` +- 运行scrcpy + +> 使用技巧 + +- 鼠标左键: 模拟点击 +- 鼠标右键/Ctrl+b: 返回上一页 +- Ctrl+s: 切换app +- 手机录屏: scrcpy --record file.mp4 +- 帮助信息: scrcpy --help +- 远程成功并关闭设备屏幕: scrcpy --turn-screen-off + +### 目录跳转 + +**`Autojump`** + +> 统计cd 目录,方便目录跳转 *shrc 中要有 : `. /usr/share/autojump/autojump.sh` + +- `apt install autojump` 设置为自动运行 `echo '. /usr/share/autojump/autojump.sh' >> ~/.bashrc` + - `j -v` 查看安装版本 + - `j --stat` 查看统计信息 + - `j --help` + - `jo code` 打开code文件夹 + - `jco c` 打开子目录 +- `ls -l ~/.local/share/autojump/` 统计信息的目录,清除就相当于卸载重装了 + +**`z.lua`** + +> [Github](https://github.com/skywind3000/z.lua) 与 Autojump 类似, 性能更好 + +- `pip install qrcode` + - *qr --help* 终端内生成二维码 + +`fd` + +Simple, fast and user-friendly alternative to find + +`skim` + +Fuzzy Finder in Rust! + +`alias cds='cd $(fd ".*" -t d | sk)'` 模糊搜索跳转进目录 + +### xdotool +command-line X11 automation tool `可以控制指定窗口激活关闭,最大最小化,输入快捷键等` + +> 将该脚本配置为快捷键后,实现效果:激活已有终端的窗口,或者启动终端 +```sh + #!/bin/bash + + tmd=xfce4-terminal + + PID=$(pgrep -x $tmd) + if [[ $PID -ne "" ]] + then + xdotool windowactivate `xdotool search --pid $PID | tail -1` + else + $tmd + fi +``` + +### rofi +[Github rofi](https://github.com/davatorium/rofi) + +设置 `rofi -show window` 快捷键为 右Alt + +************************ + +## 远程工具 + +[rdesktop and xfreerdp](https://www.joxrays.com/linux-rdp-windows/) + +rdesktop xfreerdp + +************************ +## 进程管理 +gnome-system-monitor +Supervisor 进程监控管理 + +************************ + +## 零散工具集合 +> 通常会安装到 /usr/bin/* 目录下 + +- sudo 是需要安装的 + 1. `alias sudo='sudo'` 能够在别名上使用 sudo *神奇* +- md5sum 报文摘要算法 Message-Digest Algorithm 5 的实现 + - `printf 'Who?123' | md5sum` + - `md5sum file` 计算出md5值 + - `md5sum -c file.md5` file 和 file.md5 在同一目录下, 执行这个命令就是检查md5是否匹配, 确保文件的完整性和正确性 +- sha1sum sha256sum *用法和 md5sum 一致* +- last _查看Linux登录信息_ + - last -n 5 最近五次登录 +- w | uptime _查看启动情况_ +- colrm + - ps | clorm 20 30 `colrm` _删除输出的20 到30 列_ + +- figlet 字符转ascii图 +- logkeys 记录键盘输入 [Github](https://github.com/kernc/logkeys) +- expect [用于自动输入密码](http://www.cnblogs.com/iloveyoucc/archive/2012/05/11/2496433.html) +- [WTF](https://wtfutil.com) | [Github Repo](https://github.com/senorprogrammer/wtf) + - 丰富的功能, 一个方便的终端控制面板 +- when-changed 监控文件变化 执行命令 pip install when-changed +- dircolors [Linux dircolors命令](http://www.runoob.com/linux/linux-comm-dircolors.html) `用于设置 ls 命令输出时的色彩` +- gtypist 用于练习打字 +- watch 周期执行命令并输出 + +- `uniq` 统计出现次数 `cat log.log | grep WARN | awk '{print $5}' | sort | uniq -c` +- `starDict` 终端内字典 +- [upx](https://github.com/upx/upx) 压缩构建的可执行文件 + +https://kbumsik.io/using-ipad-as-a-2nd-monitor-on-linux +https://snapdensing.com/2020/04/07/ipad-as-an-extended-screen-in-linux/ + +### 剪贴板管理 +> [参考: 面向 Linux 的 10 款最佳剪贴板管理器](https://linux.cn/article-7329-1.html) +- CopyQ,Manjaro 的 clipman + +- xclip + - `cat README.md | xclip -sel clip` 将文件复制到剪贴板 +- xsel + - `cat a.md | xsel -b` _将文件所有内容复制到剪贴板_ 但是处理大文件时会失效 xclip 更有效 +- [Clipboard](https://github.com/Slackadays/Clipboard)`终端操作剪贴板复制粘贴` + +************************ + +## 检测工具 +### 硬盘 + +duf +dust +gdu + +CrystalDiskMark +- [KDiskMark](https://github.com/JonMagon/KDiskMark) + +> smartmontools +- 检测健康状况 `smartctl -Hc /dev/sda9` + +************************ + +## 文本处理 + +- `wc` 单词 行数 统计 +- `ccze` 日志高亮 +- `lolcat` 给输出包装上彩虹颜色 有 c python ruby 版 +- choose _方便的cut_ + +> `在当前目录下, 快速全文内容搜索` + +- ag _The Silver Searcher_ + - ubuntu:silversearcher-ag alpine:the_silver_searcher + - [The Silver Searcher](https://github.com/ggreer/the_silver_searcher) +- rg _ripgrep_ +- glow markdown renderer + +************************ + +## 文件操作 + +`iconv` + +> 可以将一种已知的字符集文件转换成另一种已知的字符集文件 + +例如 将git仓库内所有Java文件 GBK 转 UTF8 `git ls-files | grep "\.java" | tee | xargs -I {} iconv -f GBK -t UTF-8 {} -o {}` + +`zssh` +> [参考 zssh, rz, sz互相传输](http://blog.csdn.net/ygm_linux/article/details/32321729) + +## 安全工具 + +### gpg +> [参考博客](http://www.ruanyifeng.com/blog/2013/07/gpg.html) + +- 生成的过程, 输入相关的提示信息, 最后输完密码后需要输入随机字符, 就也是按照提示, 但是1.4是正常的, 其他的直接假死,不是很理解这种操作 + +### JumpServer +> [Github](https://github.com/jumpserver/jumpserver) + +************************ + +# 多媒体 + +## ffmpeg + +> [Official Site](http://ffmpeg.org/ffmpeg.html) + +- 查看属性 `ffprobe -pretty target.mp4` + +> m3u8 URL 转换为mp4 + +- `ffmpeg -i http://xxx.m3u8 -c copy -bsf:a aac_adtstoasc output.mp4` +- 获取视频中的音频 `ffmpeg -i input.mp4 -vn -y -acodec copy output.m4a` +- 去掉视频中的音频 `ffmpeg -i input.mp4 -an output.mp4` +- 合并视频 `ffmpeg -f concat -safe 0 -i file.cfg -c copy result.mp4` + + - file.cfg 内容为多行文件 : `file '/path/to/file'` +- 截取视频 `ffmpeg -ss 00:00:00 -t 00:00:30 -i input.mp4 -vcodec copy -acodec copy output.mp4` + + - `-ss` 开始时间 `-t` 截取时长 `-q 0` 无损 `-c copy`表示不必重新编码 + +## 图片处理 + +- byzanz 录制屏幕为gif + +### ImageMagick +> ImageMagick® is a free, open-source software suite, used for editing and manipulating digital images +> [Github: ImageMagick](https://github.com/ImageMagick/ImageMagick) + +1. display + +#### convert +> convert between image formats as well as resize an image, blur, crop, despeckle, dither, draw on, flip, join, re-sample, and much more + +`convert 源文件 [参数] 目标文件` +- 格式转换: convert a.png a.jpg + +- 将图片转换成指定大小 这是保持比例的 `convert -resize 600X600 src.jpg dst.jpg` 中间是字母X + - 如果不保持比例,就在宽高后加上感叹号 + - 可以只指定高度,那么宽度会等比例缩放 `convert -resize 400 src.jpg dst.jpg` + - 还可以按百分比缩放 `convert page200.png -resize 50% page100.png` + +> svg to ico 两种方式 +- `magick convert -background none icon.svg -define icon:auto-resize icon.ico` +- `convert -background none icon.svg -define icon:auto-resize icon.ico` + +#### 多图操作 +- 若干图片合并并转PDF `convert origin1.jpg origin2.jpg target.pdf` +- [imagemagick 图片合并_convert 多图拼接-CSDN博客](https://blog.csdn.net/qq_24127015/article/details/86525305) + - 水平方向拼接,纵向则是 -append `magick convert +append 2024* aa.jpg` + - composite 方式 + - 生成空白图片 `magick -size 1920x1200 xc:none dest0.jpg` + - 按坐标放入两张图片 `magick composite -geometry +0+0 u-0.jpg dest0.jpg dest0.jpg` `magick composite -geometry +1000+0 u-1.jpg dest0.jpg dest0.jpg` + +> 批量修改 +如果没有 -path 语句,新生成的 png 文件将会覆盖原始文件 [参考博客](http://www.cnblogs.com/jkmiao/p/6756929.html) +- `mogrify -path newdir -resize 40X40 *.png` 把png图片全部转成40X40大小并放在新文件夹下 +- `mogrify -path newdir -format png *.gif` 将所有gif转成png放在新目录下 + +### asciinema + +- [asciinema](https://asciinema.org) `终端屏幕录制和分享网` +- 执行 `asciinema`或 `asciinema rec` 即可开始录制 +- 要注册就运行 `asciinema auth` 进入输出的网址,填邮箱和名字即可(每次登录都要这样。或者使用邮件来确认,麻烦ing) + +### 图片浏览器 + +1. Nomacs 快 +2. gThumb +3. Eye of GNOME Image Viewer 功能比上面多了一点 + +### 截图 + +- Flameshot 截图工具 类似于 snipaste + - Ctrl 鼠标滚动 调整线条粗细 + - 习惯: + - Ctrl Alt S 截图 + - Alt Q pin +- deepin-screenshot + +### 录屏 + +- `kazam` 支持选进程窗口,输出mp4 +- `peek` 顶层窗口选择录屏区域,输出 gif 有较高压缩比 + +************************ + +## 视频 + +> [参考: Top 10 Best Linux Video Players](https://www.ubuntupit.com/top-10-best-linux-video-players-enjoy-ultimate-movie-music/) + +- [百度网盘命令客户端](https://github.com/iikira/BaiduPCS-Go) `Go语言实现` +- [you-get](https://github.com/soimort/you-get) + +## 音频 + +- [netease-cloud-music-gtk](https://github.com/gmg137/netease-cloud-music-gtk) +- audacious 音频播放 +- lollypop GNOME 环境简单应用 +- Audacity 音频剪辑 + +************************ + +## PDF + +`ghostscript` + +> [ghostscript.com](https://ghostscript.com/) +> [参考: Ubuntu上压缩PDF文件的方法](https://blog.csdn.net/lx_ros/article/details/79887562) + +`gs -sDEVICE=pdfwrite -dCompatibilityLevel=1.4 -dPDFSETTINGS=/ebook -dNOPAUSE -dBATCH -dQUIET -sOutputFile=output.pdf input.pdf` + +************************ + +`pdftk` + +> [pdflabs](https://www.pdflabs.com/) | [Docs](https://www.pdflabs.com/docs/pdftk-cli-examples/) + +************************ + +`pdfunite` + +> Portable Document Format (PDF) page merger + +- pdfunite 1.pdf 2.pdf merged.pdf + +************************ + +> [smallpdf.com](https://smallpdf.com) 在线处理 + + +************************ +# 运行 Windows 应用 + +- [Bottles](https://github.com/bottlesdevs/Bottles) +- [wine](https://github.com/wine-mirror/wine) +- [deepin-wine](https://github.com/zq1997/deepin-wine) + +************************ + +# 日常应用 +## Office +### QQ +> [QQ Linux](https://im.qq.com/linuxqq/index.shtml) + +### wechat +[wechat-universal-bwrap](https://aur.archlinux.org/packages/wechat-universal-bwrap) + +### wework +- [企业微信](https://aur.archlinux.org/packages/deepin-wxwork/) + +************************ + +# 外设 +> [键鼠共享](https://github.com/debauchee/barrier) +> [xdotool](https://github.com/jordansissel/xdotool)`模拟键盘和鼠标操作的命令行工具` + +## 鼠标 +- solaar Logitech鼠标Options修改 + +************************ + +# Tips + +- 问题: `sudo echo "Text I want to write" > /path/to/file` 失败 + +> [参考: "sudo echo" does not work together in Ubuntu ](https://blogs.oracle.com/joshis/sudo-echo-does-not-work-together-in-ubuntu-another-waste-of-time-issue) +> [stack over flow](https://stackoverflow.com/questions/84882/sudo-echo-something-etc-privilegedfile-doesnt-work-is-there-an-alterna) + +- `sudo sh -c 'echo "Text I want to write" >> /path/to/file'` +- `echo "Text I want to write" | sudo tee -a /path/to/file > /dev/null` + diff --git a/Linux/Base/LinuxFile.md b/Linux/Base/LinuxFile.md index 4f24b3b..7383188 100644 --- a/Linux/Base/LinuxFile.md +++ b/Linux/Base/LinuxFile.md @@ -1,143 +1,138 @@ -`目录 start` - -- [【文件管理】](#文件管理) - - [Tips](#tips) - - [设置交换分区](#设置交换分区) -- [完整命令: root身份运行](#完整命令-root身份运行) - - [清空交换内存](#清空交换内存) - - [清除缓存](#清除缓存) - - [善用*shrc文件](#善用shrc文件) - - [善用alias](#善用alias) - - [基本命令](#基本命令) - - [查找文件](#查找文件) - - [查看文件](#查看文件) - - [比较两个文件](#比较两个文件) - - [不同](#不同) - - [diff](#diff) - - [相同](#相同) - - [文件管理命令](#文件管理命令) - - [合并文件](#合并文件) -- [磁盘](#磁盘) - - [文件系统](#文件系统) - - [分区介绍](#分区介绍) - - [设备列表](#设备列表) - - [常用命令](#常用命令) - - [dd](#dd) - - [mount](#mount) - - [fdisk](#fdisk) - - [df](#df) - - [du](#du) -- [日志](#日志) - - [用户日志](#用户日志) - - [系统日志](#系统日志) - - [应用日志](#应用日志) - -`目录 end` |_2018-08-22_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux文件系统 +date: 2018-12-15 11:14:54 +tags: + - 基础 +categories: + - Linux +--- + +💠 + +- 1. [IO](#io) + - 1.1. [IO中断](#io中断) + - 1.2. [DMA](#dma) + - 1.3. [零拷贝](#零拷贝) +- 2. [文件管理](#文件管理) + - 2.1. [查找文件](#查找文件) + - 2.1.1. [find](#find) + - 2.2. [查看文件](#查看文件) + - 2.2.1. [stat](#stat) + - 2.2.2. [file](#file) + - 2.2.3. [tree](#tree) + - 2.2.4. [ls](#ls) + - 2.2.5. [wc](#wc) + - 2.2.6. [cat](#cat) + - 2.2.7. [less](#less) + - 2.2.8. [tail](#tail) + - 2.2.9. [head](#head) + - 2.2.10. [ldd](#ldd) + - 2.3. [比较文件内容](#比较文件内容) + - 2.3.1. [diff](#diff) + - 2.3.2. [meld](#meld) + - 2.3.3. [VsCode](#vscode) + - 2.4. [文件变更命令](#文件变更命令) + - 2.4.1. [rename](#rename) + - 2.4.2. [chown](#chown) + - 2.4.3. [chgrp](#chgrp) + - 2.4.4. [ln](#ln) + - 2.4.5. [cp](#cp) + - 2.4.6. [rm](#rm) + - 2.4.7. [mv](#mv) + - 2.4.8. [文件的分割与合并](#文件的分割与合并) + - 2.4.9. [监控文件变更](#监控文件变更) + - 2.5. [默认字符编码](#默认字符编码) +- 3. [磁盘](#磁盘) + - 3.1. [文件系统](#文件系统) + - 3.1.1. [ext3 ext4](#ext3-ext4) + - 3.1.2. [Tmpfs](#tmpfs) + - 3.2. [安装系统时基本分区](#安装系统时基本分区) + - 3.3. [设备列表](#设备列表) + - 3.4. [常用命令](#常用命令) + - 3.4.1. [dd](#dd) + - 3.4.2. [truncate](#truncate) + - 3.4.3. [mount](#mount) + - 3.4.4. [fdisk](#fdisk) + - 3.4.5. [fsck](#fsck) + - 3.4.6. [df](#df) + - 3.4.7. [du](#du) +- 4. [日志](#日志) + - 4.1. [Systemd](#systemd) + - 4.2. [应用日志](#应用日志) +- 5. [文件共享](#文件共享) + - 5.1. [Samba](#samba) + - 5.1.1. [搭建匿名Samba服务器](#搭建匿名samba服务器) +- 6. [Tips](#tips) + - 6.1. [善用*shrc文件](#善用shrc文件) + - 6.1.1. [善用alias](#善用alias) + - 6.2. [desktop文件](#desktop文件) + +💠 2024-09-14 11:51:16 **************************************** -# 【文件管理】 -> Linux中认为万物皆文件 -- 清空文件内容 `true > a.txt ` -- 安装上传下载文件的工具 `sudo apt install lrzsz` -- `cat ~/.ssh/id_rsa.pub | xsel -b` 将文件复制到剪贴板 +# IO -## Tips -### 设置交换分区 -- 查看内存 `free -h` -- 创建一个4g 交换文件 `dd if=/dev/zero of=/swapfile bs=1024k count=4096` -- 格式化成交换文件的格式 `mkswap /swapfile` -- 启用该文件作为交换分区的文件 ` swapon /swapfile` -- `/swapfile swap swap defaults 0 0` 写入`/etc/fstab`文件中,让交换分区的设置开机自启 -- `sudo sysctl vm.swappiness=15` 临时修改重启注销失效, 查看:`cat /proc/sys/vm/swappiness` -- 永久修改:`/etc/sysctl.conf ` 文件中设置开始使用交换分区的触发值: `vm.swappiness=10` - - 表示物理内存剩余`10%` 才会开始使用交换分区 -- `建议,笔记本的硬盘低于 7200 转的不要设置太高的交换分区使用,大大影响性能,因为交换分区就是在硬盘上,频繁的交换数据` +对于一次Network IO (这里我们以read举例),它会涉及到两个系统对象: +- 调用这个 IO 的 用户线程 (process/thread) +- 系统内核(kernel) -```sh -# 完整命令: root身份运行 -dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon /swapfile && echo "/swapfile swap swap defaults 0 0" >> /etc/fstab -``` -#### 清空交换内存 -- 1.关闭交换分区 `sudo swapoff 交换分区文件` - - 2.开启交换分区 `sudo swapon 交换分区文件` -- 或者 `swapoff -a && swapon -a` - -### 清除缓存 -> [参考: 如何在 Linux 中清除缓存(Cache)?](https://linux.cn/article-5627-1.html) `注意要切换到root再运行命令` -> 仅清除页面缓存(PageCache) `sync; echo 1 > /proc/sys/vm/drop_caches` -> 清除目录项和inode `sync; echo 2 > /proc/sys/vm/drop_caches` -> 清除页面缓存,目录项和inode `sync; echo 3 > /proc/sys/vm/drop_caches` - -- 有时候, 因为缓存的问题会引发一些很诡异的问题, 有应用缓存和系统缓存的分别 - - 例如构建工具Maven, 也会因为在一个项目空间下, 多个同名项目的缓存问题 - - (巨诡异 `if(true){}` 都能不执行, 一个变量的值莫名其妙的时刻被修改 ) - - 然后操作系统的缓存问题也有出现: - - 例如 启动一个web项目, 运行到某一行, 突然运行不下去了, 前端阻塞在了等待请求这里 重新编译 和请求target目录都没有用, 重启IDEA也没有用 - - 然后找到了上面的博客, 清除了下缓存就OK了 - -### 善用*shrc文件 -> 注意加载顺序 /etc/profile -> ~/.*shrc `各种sh的rc文件` bash zsh ash +当一次 read 操作发生时,它会经历两个阶段: +1. `等待数据准备` (Waiting for the data to be ready) +1. `将数据从内核缓存中拷贝到用户缓存(用户进程)中` (Copying the data from the kernel to the process) -#### 善用alias +记住这两点很重要,因为这些 IO Model 的区别就是在两个阶段上各有不同的情况。对于常见的网络通信场景:网卡复制到内核空间再复制到用户空间 -```sh - if [ -f ~/.bash_aliases ]; then - . ~/.bash_aliases - fi -``` -- 在`~/.bashrc`添加这段,然后在 `.bash_aliases` 文件中设置别名 - - 例如 : `alias Kg.notes='cd ~/Documents/Notes/Code_Notes/'` - - 更改文件后,想当前终端就生效就 `source ~/.bashrc` 不执行命令就重启终端即可 -> 注意_ -> 你会发现 当前用户 下 Kg.notes 是正常运行的, 但是 sudo Kg.note 就会报错说找不到命令 -> 神奇的是 配置一个别名 `alias sudo='sudo '` 就可以解决这个问题了 [stackoverflow](https://askubuntu.com/questions/22037/aliases-not-available-when-using-sudo) -> 官方说明如下_ -``` - The first word of each simple command, if unquoted, is checked to see if it has an alias. If so, that word is replaced by the text of the alias. - The characters ‘/’, ‘$’, ‘`’, ‘=’ and any of the shell metacharacters or quoting characters listed above may not appear in an alias name. - The replacement text may contain any valid shell input, including shell metacharacters. The first word of the replacement text is tested for aliases, - but a word that is identical to an alias being expanded is not expanded a second time. This means that one may alias ls to "ls -F", for instance, - and Bash does not try to recursively expand the replacement text. If the last character of the alias value is a space or tab character, - then the next command word following the alias is also checked for alias expansion. -``` -- 如[我的配置文件](https://github.com/Kuangcp/Configs/tree/master/Linux/init) `将配置文件分类放` - - K.h就能显示出每个命令的说明 其实现脚本: [python3脚本](https://github.com/Kuangcp/Script/blob/master/python/show_alias_help.py) - - 在别名文件目录时, 建立链接就可以用了 `ln -s `pwd`/.bash_aliases ~/.bash_aliases` +同步和异步的概念描述的是用户线程与内核的交互方式 +阻塞和非阻塞的概念描述的是用户线程调用内核IO操作的方式 -************************* -`自定义桌面快捷方式文件` -```conf - [Desktop Entry] #每个desktop文件都以这个标签开始,说明这是一个Desktop Entry文件 - Version = 1.0 #标明Desktop Entry的版本(可选) - Name = Firefox #程序名称(必须),这里以创建一个Firefox的快捷方式为例 - GenericName = Web Browser #程序通用名称(可选) - Comment = A Web Browser #程序描述(可选) - Exec = firefox %u #程序的启动命令(必选),可以带参数运行,当下面的Type为Application,此项有效 - Icon = firefox #设置快捷方式的图标(可选) - Terminal = false #是否在终端中运行(可选),当Type为Application,此项有效 - Type = Application #desktop的类型(必选),常见值有“Application”和“Link” - Categories = GNOME;Application;Network; #注明在菜单栏中显示的类别(可选) -``` -- [示例文件](https://github.com/Kuangcp/Notes/tree/master/ConfigFiles/Linux/VSCode.desktop) -- 如要将快捷方式放在启动菜单内 将desktop文件放在 `/usr/share/applications/` 目录下即可 -- 注意:目录不能有空格 等特殊字符 +## IO中断 + +## DMA + +## 零拷贝 +> [Wiki](https://en.wikipedia.org/wiki/Zero-copy) + +> [什么是零拷贝?](https://www.xiaolincoding.com/os/8_network_system/zero_copy.html) +> [零拷贝(Zero-copy) 浅析及其应用](https://www.cnblogs.com/rickiyang/p/13265043.html) + +用户态IO +mmap + write +sendfile +sendfile + DMA gather copy +splice +写时复制 +缓冲区共享 + +| 拷贝方式 | CPU拷贝 | DMA拷贝| 系统调用| 上下文切换 +|:---|:---|:---|:---|:---| +|传统方式(read + write) | 2 | 2 | read / write |4| +|内存映射(mmap + write) | 1 | 2 | mmap / write |4| +|sendfile | 1 | 2 | sendfile |2| +|sendfile + DMA gather copy | 0 | 2 | sendfile |2| +|splice | 0 | 2 | splice |2| + +************************ + +# 文件管理 +> Linux中认为万物皆文件 -************************************** -## 基本命令 -> cd_ - `cd - ` 跳转到上一个目录 - `cd !$` 把上个命令的参数作为cd参数使用。 -- `cd //` 系统根目录 -### 查找文件 -> silversearcher-ag_ -- 快速搜索文件内容 -************** -> find +> [用文件头标识判断文件类型](https://blog.mythsman.com/post/5d301940976abc05b345469f/)`而不是Windows那样默认以文件后缀来判断` + +- [yazi](https://github.com/sxyazi/yazi) + +## 查找文件 + +> silversearcher-ag `快速搜索文件的内容` + +### find - `find . -name "*.txt"` 查找当前目录的txt后缀的文件 - `sudo find / -name a.java` 全盘查找 - `find -type f -name README.md` 默认当前目录查找 - d 文件夹 f 普通文件 l 符号链接文件 b 块设备 c 字符设备 p 管道文件 s 套接字 +- `find . -mmin -30` 查找最近30分钟修改的文件 - **exec** 嵌入一个命令 1. 找到所有pdf移动到指定目录 `find . -name "*.pdf" -exec mv {} /home/test \;` @@ -148,20 +143,40 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / **实践** 1. 递归删除目录下所有run后缀的文件 `find . -name "*.run" | xargs rm -f` + - 递归当前文件夹下所有 log 找到 ERROR日志 `find -name "*.log" | xargs grep ERROR` 1. 查找文件内容 `find etc/ |xargs grep -i java` -### 查看文件 -> stat +**fzf** + +**locate** +> 预先建立数据库,依此查询 速度较快,但是有时效问题 + +**Anything** +> 图形化 搜索文件 工具,也是预先建立数据库 + +**Synapse** +> 搜索文件 启动应用等功能 + +************************ + +## 查看文件 +### stat - 查看文件详细信息 `stat filename ` -> tree +### file +- file a.txt 查看文件类型 + - -i 输出文件的MIME类型 + - `file -b --mime-type filename | sed 's|/.*||'` 获取MIME短名称 + - -F "#" 修改输出分隔符 + +### tree - 展示目录结构 - -p 匹配 - -h 可读的显示文件大小 - -F 和ls一样 - -L 目录深度 -> ls +### ls - `参数` - `i` 详情 - `a` 全部包含隐藏文件 <> `A` 不显示当前目录和上级目录 `.` `..` @@ -175,6 +190,7 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / - `t` 按修改时间从顶至下,一般不单用,和 g|l 结合一起用 - `c` 按ctime(创建时间)一般是文件夹,文件则是修改时间排列 - 和 lt|gt 一起用 即 `ls -clt` 同上的排列顺序 + - `S` 按文件大小逆序展示 - `执行ls -l 命令后的输出` 1. 输出类型:d 目录 l 软链接 b 块设备 c 字符设备 s socket p 管道 - 普通文件 1. 输出权限信息:r 读权限 w 写权限 x 执行权限 @@ -188,32 +204,45 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / - `ls -lFh` 列出所有文件的详细信息, 并且文件大小是人类可阅读的 -> file -- file a.txt 查看文件类型 - - -i 输出文件的MIME类型 - - -F "#" 修改输出分隔符 +> 改进版 +- exa +- [lsd](https://github.com/lsd-rs/lsd) -> wc +### wc - `wc [-lmw] ` 参数说明: -l :多少行-m:多少字符 -w:多少字 - cat mul.sh | wc -l - wc -l mul.sh -> cat +### cat - 类似的还有 nl more less 带行号输出 `cat -n file` 或者 `nl file`但是空行不会编号, 除非这样: `nl -b a file` -> nl -- [参考博客: 每天一个linux命令(11):nl命令](http://www.cnblogs.com/peida/archive/2012/11/01/2749048.html#/) +> 改进版 +- bat +- nl [参考: 每天一个linux命令(11):nl命令](http://www.cnblogs.com/peida/archive/2012/11/01/2749048.html#/) -> less +### less - 该命令的导航是和Vi体系一样的, 建议打开大文件使用less或者more 如果用vim,文件全加载到内存了 - 诸多软件使用到了分页, 怀疑就是借助less实现的, 因为快捷键一模一样, 例如 man命令, 各个软件的-h, git的log 等等..优点很多 - [ less命令简介](https://blog.csdn.net/caihaijiang/article/details/6113419) - h 查看帮助文档 z/b 上下翻页 g/G 文件首/尾 - F 监听文件 -> tail +- 当打开多个文件时 `:n`和`:p` 表示 next pre 也就是 下一个,上一个文件 + +> [syntax-highlighting](https://unix.stackexchange.com/questions/90990/less-command-and-syntax-highlighting) +> [Make the less Command More Powerful](https://www.topbug.net/blog/2016/09/27/make-gnu-less-more-powerful/) + +1. install source-highlight +1. append to *sh.rc + ```sh + # sh 在不同的系统 路径和名字都有可能不一样 + export LESSOPEN="| /usr/bin/source-highlight-esc.sh %s" + export LESS=' -R' + ``` + +### tail - tail命令用于输入文件中的尾部内容。tail命令默认在屏幕上显示指定文件的末尾10行。 来自: http://man.linuxde.net/tail - `--retry`:即是在tail命令启动时,文件不可访问或者文件稍后变得不可访问,都始终尝试打开文件。此选项需要与选项“——follow=name”连用; @@ -231,19 +260,39 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / tail +20 file (显示文件file的内容,从第20行至文件末尾) tail -c 10 file (显示文件file的最后10个字符) ``` -> head + +### head - 查看文件头部, 默认前十行 使用 -n 指定行数 -### 比较两个文件 -#### 不同 -- `grep -vwf 文件1 文件2` -##### diff -> [参考博客](http://www.cnblogs.com/chenjianhong/archive/2012/09/26/4144940.html) +### ldd +查看二进制执行文件依赖的动态库 + +************************ + +## 比较文件内容 +> [阮一峰: 读懂diff](http://www.ruanyifeng.com/blog/2012/08/how_to_read_diff.html) + +### diff +> [参考博客 linux下比较两个文本文件的不同](http://www.cnblogs.com/chenjianhong/archive/2012/09/26/4144940.html) + +`diff a b` 输出的 < 表示a转为b文件时要删除的行 > 表示要增加的行 + +### meld +> [Github: meld](https://github.com/GNOME/meld) -#### 相同 +> 可用于 git svn 查看差异 +> 选两个或三个文件去查看差异,完成patch补入 -### 文件管理命令 -> rename +### VsCode +code --diff + +************************ + +kdiff3 +vimdiff + +## 文件变更命令 +### rename `rename命令的使用(基于perl)` - `rename "s/.html/.php/" * ` //把.html 后缀的改成 .php后缀 - `rename "s/$/.txt/" * ` //把所有的文件名都以txt结尾 @@ -251,21 +300,22 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / - `rename "s/AA/aa/" * ` //把文件名中的AA替换成aa - `rename "s/ - 副本/_bak/" *` 将文件`-副本`结尾改成`_bak`结尾 -> chown +### chown - `chown [-R] 账号名称 文件或目录` - `chown [-R] 账号名称:用户组名称 文件或目录` -> chgrp +### chgrp - 更改文件所属用户组 `chgrp group file` - -R 递归子目录 -> ln +### ln +- `ln path path` 默认创建硬链接 - `ln -s 源文件或目录 目标绝对路径` 生成软链接(快捷方式) -```sh - ln -s `pwd`/a.md ~/a.md -``` -> cp +硬链接: 只能链接文件,原始路径下文件删除后,能通过硬链接访问到原文件 即文件没有真正删除,只是引用数减一 +软链接: 相当于快捷方式,原始文件或目录删除后,软链接也会失效 + +### cp - cp `cp -ri 目录或正则 目录` 目录所有文件复制过去 - a 该选项通常在拷贝目录时使用。它保留链接、文件属性,并递归地拷贝目录,其作用等于dpR选项的组合。 - d 拷贝时保留链接。 @@ -275,7 +325,9 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / - r 若给出的源文件是一目录文件,此时cp将递归复制该目录下所有的子目录和文件。此时目标文件必须为一个目录名。 - l 不作拷贝,只是链接文件。 -> rm +> [progress](https://github.com/Xfennec/progress)`cp mv等命令执行时,输出 进度提示,剩余耗时` + +### rm - rm `rm -rf 目录` 不提示性删除 - f 忽略不存在的文件,从不给出提示。 - r 指示rm将参数中列出的全部目录和子目录均递归地删除。 @@ -284,28 +336,87 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / > 特别注意 rm -rf link 文件时, 如果只是想删除link文件 那么就不要在link文件后加上 / 例如: > `rm -rf linkDir/ ` 这个命令是将 link到的目录下的文件全部删除而不是 删除link文件本身 -> wc -- `wc [-lmw] ` 参数说明: -l :多少行-m:多少字符 -w:多少字 -- cat mul.sh | wc -l -- wc -l mul.sh - -> mv +### mv - mv `mv 目录或正则 目录` 移动 - I 交互方式操作。如果mv操作将导致对已存在的目标文件的覆盖,此时系统询问要求用户回答y或n,这样可以避免误覆盖文件。 - f 禁止交互操作。在mv操作要覆盖某已有的目标文件时不给任何指示,指定此选项后,i选项将不再起作用。 -#### 合并文件 +### 文件的分割与合并 +> [参考: 文件过滤分割与合并](http://man.linuxde.net/sub/%e6%96%87%e4%bb%b6%e8%bf%87%e6%bb%a4%e5%88%86%e5%89%b2%e4%b8%8e%e5%90%88%e5%b9%b6) + +> 分割 + +- split + 1. 指定行数分割 `split -l 300 log.txt newfile` + 1. 指定文件大小 `split -b 500m log.txt newfile` + +> 合并 + 1. 最简单就是 `cat file1 file2 > result` -****************************************** -****************************************** +### 监控文件变更 +> 原理是通过监听文件变更时发出的 signal + +- 借助 inotify-tool 包更容易使用 + - inotifywait + - inotifywatch + +- 持续监听某目录变更 `inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format '%T %w%f%e' -e modify,delete,create,attrib /home/kcp/test/git-test` + +************************ + +## 默认字符编码 +> 查看当前编码 locale 或者 echo $LANG + +1. 修改编码 `/etc/profile` +```sh +LC_ALL="zh_CN.UTF-8" +export LANG="zh_CN.UTF-8" +``` + +************************ + # 磁盘 -> 附: [Linux系统基本目录结构](/Linux/Base/LinuxDirectoryStructure.md) +> [Linux系统基本目录结构](/Linux/Base/LinuxDirectoryStructure.md) + +> [参考: 在 Linux 上检测硬盘上的坏道和坏块 ](https://linux.cn/article-7961-1.html) + +- bleachbit 应用占用磁盘 清理 ## 文件系统 -> [参考博客: Linux 文件系统剖析](https://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/index.htmlQ) -> [参考博客: 详解NTFS文件系统](http://www.blogfshare.com/detail-ntfs-filesys.html) -> [参考博客: 使用 FUSE 开发自己的文件系统](https://www.ibm.com/developerworks/cn/linux/l-fuse/) +> [参考: Linux 文件系统剖析](https://www.ibm.com/developerworks/cn/linux/l-linux-filesystem/index.htmlQ) +> [参考: 详解NTFS文件系统](http://www.blogfshare.com/detail-ntfs-filesys.html) +> [参考: 使用 FUSE 开发自己的文件系统](https://www.ibm.com/developerworks/cn/linux/l-fuse/) + +> [spacedrive](https://github.com/spacedriveapp/spacedrive) + +### ext3 ext4 + +### Tmpfs +> 虚拟内存文件系统 [wiki](https://wiki.archlinux.org/index.php/Tmpfs) + +手动创建挂载tmp文件系统: `mount -t tmpfs -o size=100m tmpfs /mnt/tmp` + +************************ + +> [/tmp临时目录定期清理机制](https://cloud-atlas.readthedocs.io/zh-cn/latest/linux/redhat_linux/systemd/tmp_directory_cleanup_periodically.html) + +安装系统时,如果没有将/tmp指定独立的分区,将会在/分区下建立 tmp 目录,此时会有一个隐患 当系统段时间大量创建tmp文件时可能导致 / 分区满掉,从而导致整个系统hang住。 +例如Java中使用EasyExcel大量导出Excel时需要临时文件落盘避免内存占用过大的问题, 当并发大量文件导出时容易引起tmp目录占满。 + +> systemd 方式来定期清理tmp [Configuration of Temporary Files with systemd-tmpfiles](https://www.baeldung.com/linux/systemd-tmpfiles-configure-temporary-files) + +例如:解决上述Excel临时文件的问题,最好是随用随删,该机制可作为兜底策略 +- Java应用中指定临时文件目录为 /tmp/excel-tmp +- 新建配置文件 `/etc/tmpfiles.d/excel-tmp.conf` +```ini + d /tmp/dir_clean 0755 baeldung baeldung 10s +``` +- 执行 `sudo systemd-tmpfiles --clean` 将删除最后修改时间超过当前时间10s的文件 **可以加入cron** +- 问题: 如果文件被打开,持续写入中,时间超过了10s这个时候是否会被删除? + - 如果进程占用在写入,不会被清除, 打开的句柄关掉后就不会修改文件的修改时间了,就会到期删除 + +************************ ## 安装系统时基本分区 - / 根目录, 操作系统安装的目录 @@ -329,6 +440,11 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / > [使用 dd 命令进行硬盘 I/O 性能检测 ](https://linux.cn/article-6104-1.html) - 例如创建一个空4G文件: `dd if=/dev/zero of=/testfile bs=1024k count=4096` +- 创建全0填充文件,标准输出后计算MD5 `dd if=/dev/zero bs=1M count=1024 | md5sum` + +### truncate + +创建一个 5G 全0文件 `truncate -s 5G test.file` ### mount - `mount [options] [source] [directory] ` @@ -339,42 +455,178 @@ dd if=/dev/zero of=/swapfile bs=1024k count=4096 && mkswap /swapfile && swapon / - 只读方式挂载 `mount -o loop --ro virtual.img /mnt` - 卸载挂载的磁盘 `sudo umount /mnt` -> 自动挂载分区 (root身份运行命令) -1. `blkid` 查看设备详情, 找到要挂载的硬盘的 UUID 文件系统类型 +> 设置自动挂载某分区 (root身份运行命令) +1. `blkid` 查看设备详情, 找到要挂载的硬盘的 UUID 以及 文件系统类型 1. `vim /etc/fstab` 在文件中添加, 记得要 先创建该目录 `/media/kcp/Data1` - `UUID=42168DE83BC5EDAD /media/kcp/Data1 ntfs defaults 0 1` 类似配置 - - `mount -a` 切记要先用该命令测试下该文件是否正确, 如果有错误, 系统关机后就开不了机了(可以使用U盘进系统进行修改该文件) + - `mount -a` 切记要先用该命令测试下该文件是否正确, 如果有错误, 系统关机后就开不了机了(但是可以使用U盘进系统 修改该文件) + +> 一个分区挂载到多个目录 +- 挂载分区到目录A sudo mount /dev/sda1 /mnt/partition +- 创建新目录B sudo mkdir /mnt/newdir +- 绑定分区到目录B sudo mount --bind /mnt/partition /mnt/newdir +- 结果: /mnt/partition /mnt/newdir 访问的是一个分区 ### fdisk - 查看磁盘分区表信息 :`sudo fdisk -l ` +### fsck +> check and repair a Linux filesystem + +当系统突然断电而导致文件系统不一致时, 可使用该命令进行修复, 例如:`fsck.ext4 -vy /dev/sdaXX` + ### df -- `df -h ` 查看挂载文件系统信息 +> 报告文件系统磁盘空间使用情况 + +- -h 可读性 human readable +- -T 查看挂载文件系统的类型信息 +- -a 所有文件系统 +- -l 只显示本地文件系统 + +> 改进版 +- duf 现代化 df +- pydf ### du - `du -sh 目录` 查看磁盘占用总大小 h 自动搭配单位(human read ) - `du --max-depth` 一级子目录使用情况 -- du -sm * | sort -n //统计当前目录大小 并安大小 排序 -- du -sk * | sort -n -- du -sk * | grep guojf //看一个人的大小 -- du -m | cut -d "/" -f 2 //看第二个/ 字符前的文字 +- `du -sm * | sort -n` 统计当前目录大小 并按大小(mib)排序 `-sk`则是换算成kib +- `du -m | cut -d "/" -f 2` 看第二个`/`字符前的文字 -- 获取当前目录最大的6个目录或文件 `du -hsx * | sort -rh | head -6` +- 案例: 获取当前目录最大的6个目录或文件 `du -hsx * | sort -rfh | head -6` - -hsx – (-h)更易读的格式,(-s)汇总输出,(-x)跳过其他文件系统的文件 - - sort – 对文本文件按行排序 - - -rf – (-r)将比较的结果逆序输出,(-f)忽略大小写 - - head – 输出文件的头几行 + - sort – 对文本文件按行排序 (-r)将比较的结果逆序输出,(-f)忽略大小写 -h 可读 + - head – 输出文件的前几行 + +> 改进版 +- [ncdu](https://dev.yorhel.nl/ncdu) +- [dust](https://github.com/bootandy/dust) +- [gdu](https://github.com/dundee/gdu) + +************************ -********************************* # 日志 > 基本都在 `/var/log` 下 -## 用户日志 - last 查看用户最后登录时间 +- logrotate 日志处理工具(切分,压缩,邮件通知等功能) + +## Systemd +> 通常使用 journalctl 查询 Systemd 的日志 + +> 查看日志 +- 当次启动日志 `journalctl -xe` +- 内核模块的日志 `journalctl -u systemd-modules-load.service` -## 系统日志 -- 查看系统启动日志 `less /var/log/boot.log` -- 查看系统消息日志 `less /var/log/messages` +1. `-r` 滚动到最后的日志 +1. `-b -1` 查看相对启动次日志 0标识当前 +1. `--since "2015-01-10"` +1. `--until "2015-01-11 03:00"` + +> 清理日志 +- `journalctl --vacuum-time=1w` 只保留1周日志 + - 路径:/var/log/journal ## 应用日志 > [处理Apache日志的Bash脚本](http://www.ruanyifeng.com/blog/2012/01/a_bash_script_of_apache_log_analysis.html) + +************************ +# 文件共享 +## Samba +> [参考: ](https://www.jianshu.com/p/b0fcf29a857a) + + +### 搭建匿名Samba服务器 + +1. /etc/samba/smb.conf + ```ini + [global] + workgroup = WORKGROUP + #所要加入的工作组或者域 + netbios name = Manjaro + #用于在 Windows 网上邻居上显示的主机名 + security = user + #定义安全级别 + map to guest = bad user + #将所有samba系统主机所不能正确识别的用户都映射成guest用户 + dns proxy = no + #是否开启dns代理服务 + + [share] + #共享显示的目录名 注意每级目录samba用户都要有权限 最简单就放最高层级的目录上 + path = /share + #实际共享路径 + browsable = yes + #共享的目录是否让所有人可见 + writable = yes + #是否可写 + guest ok = yes + #是否允许匿名(guest)访问,等同于public + create mask = 0777 + #客户端上传文件的默认权限 + directory mask = 0777 + #客户端创建目录的默认权限 + #注意共享文件在系统本地的权限不能低于以上设置的共享权限。 + ``` +1. smbpasswd -a share #共享显示的目录名 +1. pdbedit -L 查看所有Samba用户 +1. chmod 777 -R /share +1. systemctl restart smb nmb +1. 测试可用性 smbclient //192.168.0.10/share + +************************ + +# Tips +- 清空文件内容 `true > a.txt ` +- 安装上传下载文件的工具 `sudo apt install lrzsz` +- `cat ~/.ssh/id_rsa.pub | xsel -b` 将文件复制到剪贴板 + +## 善用*shrc文件 +> 注意加载顺序 /etc/profile -> ~/.*shrc `各种sh的rc文件` bash zsh ash + +### 善用alias + +```sh + if [ -f ~/.bash_aliases ]; then + . ~/.bash_aliases + fi +``` +- 在`~/.bashrc`添加这段,然后在 `.bash_aliases` 文件中设置别名 + - 例如 : `alias Kg.notes='cd ~/Documents/Notes/Code_Notes/'` + - 更改文件后,想当前终端就生效就 `source ~/.bashrc` 不执行命令就重启终端即可 +> 注意_ +> 你会发现 当前用户 下 Kg.notes 是正常运行的, 但是 sudo Kg.note 就会报错说找不到命令 +> 神奇的是 配置一个别名 `alias sudo='sudo '` 就可以解决这个问题了 [stackoverflow](https://askubuntu.com/questions/22037/aliases-not-available-when-using-sudo) +> 官方说明如下_ +``` + The first word of each simple command, if unquoted, is checked to see if it has an alias. If so, that word is replaced by the text of the alias. + The characters ‘/’, ‘$’, ‘`’, ‘=’ and any of the shell metacharacters or quoting characters listed above may not appear in an alias name. + The replacement text may contain any valid shell input, including shell metacharacters. The first word of the replacement text is tested for aliases, + but a word that is identical to an alias being expanded is not expanded a second time. This means that one may alias ls to "ls -F", for instance, + and Bash does not try to recursively expand the replacement text. If the last character of the alias value is a space or tab character, + then the next command word following the alias is also checked for alias expansion. +``` +- 如[我的配置文件](https://github.com/Kuangcp/Configs/tree/master/Linux/init) `将配置文件分类放` + - K.h就能显示出每个命令的说明 其实现脚本: [python3脚本](https://github.com/Kuangcp/Script/blob/master/python/show_alias_help.py) + - 在别名文件目录时, 建立链接就可以用了 `ln -s `pwd`/.bash_aliases ~/.bash_aliases` + +************************* + +## desktop文件 +> [freedesktop](https://www.freedesktop.org/wiki/) + +```conf + [Desktop Entry] #每个desktop文件都以这个标签开始,说明这是一个Desktop Entry文件 + Version = 1.0 #标明Desktop Entry的版本(可选) + Name = Firefox #程序名称(必须),这里以创建一个Firefox的快捷方式为例 + GenericName = Web Browser #程序通用名称(可选) + Comment = A Web Browser #程序描述(可选) + Exec = firefox %u #程序的启动命令(必选),可以带参数运行,当下面的Type为Application,此项有效 + Icon = firefox #设置快捷方式的图标 svg(更好) png + Terminal = false #是否在终端中运行(可选),当Type为Application,此项有效 + Type = Application #desktop的类型(必选),常见值有“Application”和“Link” + Categories = GNOME;Application;Network; #注明在菜单栏中显示的类别(可选) +``` +- [示例文件](https://github.com/Kuangcp/Configs/blob/master/Linux/desktop/VSCode.desktop) +- 如要将快捷方式放在启动菜单内 将 desktop 文件复制到 `/usr/share/applications/` 目录下即可 + - 注意:目录不能有空格 等特殊字符 diff --git a/Linux/Base/LinuxManual.md b/Linux/Base/LinuxManual.md new file mode 100644 index 0000000..060f28f --- /dev/null +++ b/Linux/Base/LinuxManual.md @@ -0,0 +1,23 @@ +--- +title: Linux 命令手册 +date: 2018-12-15 11:15:10 +tags: + - 基础 +categories: + - Linux +--- + +💠 + +- 1. [Linux Command Manual](#linux-command-manual) + +💠 2024-09-05 11:52:54 +**************************************** +# Linux Command Manual + +- man +- troff +- [pls](https://github.com/chenjiandongx/pls) `go 实现 终端man中文手册` +- tldr + - `Too Long, Don't Read` +- [cheat](https://github.com/cheat/cheat) diff --git a/Linux/Base/LinuxNet.md b/Linux/Base/LinuxNet.md deleted file mode 100644 index ac0a2df..0000000 --- a/Linux/Base/LinuxNet.md +++ /dev/null @@ -1,456 +0,0 @@ -`目录 start` - -- [【网络管理】](#网络管理) - - [DNS](#dns) - - [修改DNS](#修改dns) - - [刷新本地缓存](#刷新本地缓存) - - [IPv4和IPv6](#ipv4和ipv6) - - [Tips](#tips) - - [查看端口占用情况](#查看端口占用情况) - - [基础命令工具](#基础命令工具) - - [1.ping](#1ping) - - [2.curl](#2curl) - - [3.iproute2](#3iproute2) - - [4.tcpdump](#4tcpdump) - - [5.netcat](#5netcat) - - [6.scp](#6scp) - - [7.rsync](#7rsync) - - [8.wget](#8wget) - - [【常用网络服务】](#常用网络服务) - - [邮件服务器postfix和devecot](#邮件服务器postfix和devecot) - - [FTP](#ftp) - - [基础](#基础) - - [使用](#使用) - - [手机和电脑之间传输管理文件](#手机和电脑之间传输管理文件) - - [手机](#手机) - - [电脑](#电脑) - - [配置FTP服务器](#配置ftp服务器) - - [Ssh](#ssh) - - [telnet](#telnet) - - [VPN](#vpn) - - [shadowsocks](#shadowsocks) - - [防火墙](#防火墙) - - [iptables](#iptables) - -`目录 end` |_2018-09-13_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 【网络管理】 -## DNS -- 域名和资源转换的服务 -- 解析域名的顺序一般是, 先在本机找,找不到去找上连DNS服务器, 然后根域DNS服务器 - - 这时候就有了几种方式,递归, 迭代, 递归加迭代(为了减轻全球13台根的压力) - - 假设是访问这个域名 scs.bupt.edu.cn (bupt.三级 机构域名, edu 二级行业域名, cn 一级国家域名) - - 递归: 本机->上连->根->cn->edu.cn->bupt.deu.cn 然后得到解析结果后,递归返回到上连,上连DNS服务器会进行缓存该结果,再返回本机 - - 迭代:本机->上连,上连->根,根->cn cn->edu.cn, edu.cn->bupt.deu.cn 最终返回了结果 到上连 - - 递归加迭代, 区别在于,先迭代根, 得到下级一级服务器节点后,下级就是递归的入口和出口 -- 授权和非授权, 还是上面那个URL, 其他的都不是授权的, 只有离URL最近的DNS才是授权的 即 `bupt.deu.cn` - -`nslookup ` 强大的调试DNS工具 -- nslookup - 8.8.8.8 进入循环模式, 方便调试 8.8.8.8 是Google开放的DNS 备选 8.8.4.4 - - 结果解释:Non-authoritative answer: 表示这是从缓存得到的结果,不一定准确 - - Server:上连DNS服务器的IP, Address:`上连DNS的IP#端口` 通常是53 - - canonical name 即CNAME 别名 - `dig` 比nslookup更强大 Domain Information Groper -- 例如:`dig +tcp @8.8.8.8 www.baidu.com` 采用TCP进行DNS通信(默认UDP) - - +short 精简输出 - - +nocmd+nocomment+nostat 输出最核心内容 - -`drill` -### 修改DNS -- `sudo vim /etc/resolv.conf` 添加Google的DNS -``` - nameserver 8.8.8.8 - nameserver 8.8.8.4 -``` - -### 刷新本地缓存 -> [参考博客](https://linux.cn/article-3341-1.html) -****************** -## IPv4和IPv6 -- IPv4 只有32bit IPv6 有128bit - -`IPv6` -- 零省略 :如果有一位是 000C 可以直接写C -- 零压缩 :如果FE04:0:0:0:0:0:0:DA 写成 FE::DA - -******************* -## Tips -### 查看端口占用情况 -> netstat lsof fuser ps 都有一定效果 [ linux_performance ](./Linux/linux_performance.md) - -> [参考博客: linux下常用命令查看端口占用](http://blog.csdn.net/ws379374000/article/details/74218530) - -_netstat工具_ 或者 更好用的 [iproute2](#3iproute2) - -- `lsof -i:端口号` 用于查看某一端口的占用情况,缺省端口号显示全部 - - 或者 `cat /etc/services` 查看系统以及使用的端口 - -- `netstat -tunlp | grep 端口号` 用于查看指定的端口号的进程情况 - - `-t` (tcp) 仅显示tcp相关选项 - - `-u` (udp)仅显示udp相关选项 - - `-n` 拒绝显示别名,能显示数字的全部转化为数字 - - `-l` 仅列出在Listen(监听)的服务状态 - - `-p` 显示建立相关链接的程序名 - -- 查询端口占用的pid 三种: - - `netstat -aonp |grep "^[a-z]\+[ ]\+0[ ]\+0[ ]\+[0-9\.]\+:80[ ]\+"|awk -F" " {'print $0'}` - - `netstat -aonp |grep ":80[ ]\+"|awk -F" " {'print $0'}` - - `sudo netstat -aonp |grep ":6379[ ]\+"|awk -F" " {'print $0'}` - - `sudo kill -9 pid` 杀掉指定pid - - `ps aux` 查看当前执行中的程序 - -- 似乎能看到更多 `netstat -tpanl | grep 127.0.0.1` - -*************************** -## 基础命令工具 -> 参考书籍 《Linux 大棚命令百篇》 - -### 1.ping -- ping URL : Linux是默认无休止的 - - -c 次数 - - -q 安静模式 不输出 - - -s 默认64字节, 可以指定大小 - - -t 设定 TTL值,Linux默认是64或255 经过一个路由器就会减一 - - -i 每次ping的时间间隔 默认1s root用户才可以设置 0.2 以下 - - -f 暴力尽可能大量包的传送 至少每秒100个 - - 注意:得到的结果中的 mdev 表示ICMP包的RTT偏离平均值的程度,mdev 越大表示网速不稳定 Linux有,mac下叫stddev win系列没有 - -### 2.curl -1. 不输出,重定向到*黑洞设备* ` curl -s -o /dev/null URL` -1. 格式化返回的json数据:`curl xxxx|python -m json.tool ` -1. 使用基础认证 发送JSON数据 `curl -i -H "Content-Type:application/json" -u admin:secret -X POST --data '{"title":"1","content":"1"}' http://tomcat.kcp/email/content` -```sh - # 如果没有认证则会收到如下结果 -$ curl -i -u admin:secret -X POST http://tomcat.kcp/email/content - HTTP/1.1 401 - Server: nginx/1.13.3 - Date: Thu, 26 Jul 2018 12:17:18 GMT - Content-Length: 0 - Connection: keep-alive - Set-Cookie: JSESSIONID=D863FC575140E9B1A0A2505410617487; Path=/; HttpOnly - X-Content-Type-Options: nosniff - X-XSS-Protection: 1; mode=block - Cache-Control: no-cache, no-store, max-age=0, must-revalidate - Pragma: no-cache - Expires: 0 - X-Frame-Options: DENY - WWW-Authenticate: Basic realm="Realm" -``` - -- [curl cookie](https://curl.haxx.se/docs/http-cookies.html) | [curl使用Cookie](https://aiezu.com/article/linux_curl_http_cookie.html) - -> [参考博客: curl返回常见错误码](http://www.cnblogs.com/wainiwann/p/3492939.html) -- [56错误码](https://stackoverflow.com/questions/10285700/curl-error-recv-failure-connection-reset-by-peer-php-curl) -> [参考博客: 使用cURL和用户名和密码?](http://www.cnblogs.com/seasonzone/p/7527218.html) -### 3.iproute2 -> 代替 netstat 的强大工具 - -`替代方案` - -| 用途 | net-tool | iproute2 | -| :-----: | :------: | :--------------: | -| 地址和链路配置 | ifconfig | ip addr, ip link | -| 路由表 | route | ip route | -| ARP表 | arp | ip neigh | -| VLAN | vconfig | ip link | -| 隧道 | iptunnel | ip tunnel | -| 组播 | ipmaddr | ip maddr | -| 统计 | netstat | ss | - -_ss_ -> [参考博客: Linux网络状态工具ss命令使用详解](http://www.ttlsa.com/linux-command/ss-replace-netstat/) - -- 查看网络连接统计 `ss -s` -- 查看打开的端口 `ss -l` -- 查看打开的端口以及进程pid `ss -pl` -- 查看所有socket连接 `ss -a` -- 隧道术: 网络协议的数据包被封装在另一种网络协议的数据包之中 `这是VPN的技术理论基础` -> 别说的那么神乎其神, 用的时候, 连个Tomcat开的8080都查不到 - -`net-tools 和 iproute 对应关系` - -| 作用 | net-tools用法 | iproute2用法 | -| :----------: | :--------------------------------------: | :--------------------------------------: | -| 展示本机所有网络接口 | ifconfig | ip link [show] | -| 开启/停止某个网络接口 | ifconfig ech0 up/down | ip link up/down eth0 | -| 给网络接口设置/删除IP | ipconfig eth0 10.0.0.0.1/24 / ifconfig eth0 0 | ip addr add/del 10.0.0.1/24 dev eth0 | -| 显示某个网络接口的IP | ifconfig eth0 | ip addr show dev eth0 | -| 显示路由表 | route -n | ip route show | -| 添加删除默认网关 | route add/del default gw 192.168.1.2 eth0 | ip route default via 192.168.1.2 eth0 / ip route replace default via 192.168.1.2 dev eth0 | -| 添加ARP | arp -s 192.168.1.100 00:0c:29:c5:5a:ed | ip neigh add 192.168.1.100 lladdr 00:0c:29:c5:5a:ed dev eth0 | -| 删除ARP | arp -d 192.168.1.100 | ip neigh del 192.168.1.100 dev eth0 | -| 展示套接字状态 | netstat -l | ss -l | - -- 默认网关: 如果主机找不到准发规则, 就把数据包发给默认的网关 -- 增加/删除一条路由规则 `ip route add/del 192.168.2.0/24 via 192.168.1.254` - -### 4.tcpdump -- `tcpdump -i eth0 -nn -X 'port 53' -c 1` root用户才有运行权限 - - -i 指定监听的网络接口(网卡) - - -nn 将协议号或端口号,显示数字,而不是名称例如:21 而不显示 FTP - - -X 将协议头和包内容完整的显示出来 - - port 53 过滤,只显示53端口相关的包 - - -c 抓包的数量 - - -e 输出以太网帧头部信息输出 (能看到mac地址) - - -l 输出变为行缓冲 - - -t 输出不打印时间戳 - - -v 输出更详细信息 - - -F 指定过滤表达式所在的文件 - - -w 将流量保存到文件中 - - -r 读取raw packets 文件 - -- 列出可以选择的抓包对象 `tcpdump -D`(USB设备也能抓?) - -### 5.netcat -> sudo apt install netcat - -- 开始监听端口 : `nc -l 11044` - - 建立连接 `nc 127.0.0.1 11044` 任一方退出nc 就终止了连接 - -- 端口扫描 `nc -z -v -n -w 2 127.0.0.1 20-33` - - 扫描22-33端口, - - -z 一旦连接立马断开,不发送接收任何数据 - - -v 输出详细信息 - - -n 直接使用IP地址,不适用域名服务器来查询其域名 - - -w 设置连接超时时间 s - - -u 使用UDP 默认缺省则是TCP -- 连接开放的端口 `nc -v host port` - -- 传输文件 (相同的还有 ftp scp) - - 服务端开启端口,准备好发送的文件 `nc -v -l 12345 < temp_out.md` - - 客户端接收文件:`nc -v -n host port > temp_in.md` - - 单次连接,传输完毕自动断开 服务端也可以是接收文件,将`< >`互换即可 - - 没有进度提示,大文件也不支持 - -- 传输文件夹 - - 服务端 `tar -cvPf - /root/book/ | nc -l 12345` - - 客户端 `nc -n host port | tar -xvPf -` - - 这是未压缩的, 压缩再加上参数即可 例如 `gzip -czvPf -xzvPf` - -### 6.scp -> scp命令用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,认证用的是ssh 所以也能使用sshpass - -``` - -1:使用ssh协议版本1; - -2:使用ssh协议版本2; - -4:使用ipv4; - -6:使用ipv6; - -B:以批处理模式运行; - -C:使用压缩; - -F:指定ssh配置文件; - -l:指定宽带限制; - -o:指定使用的ssh选项; - -i: 指定私钥文件 - -P:指定远程主机的端口号; - -p:保留文件的最后修改时间,最后访问时间和权限模式; - -q:不显示复制进度; - -r:以递归方式复制。 -``` -- 远程到本地 `scp root@10.10.10.10:/opt/soft/nginx-0.5.38.tar.gz /opt/soft/` -- 本地到远程 `scp /opt/soft/nginx-0.5.38.tar.gz root@10.10.10.10:/opt/soft/scptest` - -> 注: scp rcp wget rsync 几种传输文件的方式 - -### 7.rsync -> 同步命令 (个人倾向于本地和远程, 书上称为源端和目的端) [命令参数详解](http://man.linuxde.net/rsync) -> | [本地和VPS0之间同步数据](https://www.digitalocean.com/community/tutorials/how-to-use-rsync-to-sync-local-and-remote-directories-on-a-vps) - -- 同步到 `rsync file user@host:path` 上, 是将这里的file文件覆盖远程的目录下的file文件,不像git那样 - - 同步当前目录 将file 换成 \`ls\` - - -t 不加该参数:不会同步文件的修改时间,采用的`quick check策略`。使用后:让修改时间也同步,如果修改时间一致,就不同步(它不考虑文件内容,这是个坑)。 - - -I 就能解决上面的问题,每个文件都进行同步,代价是速度慢 - - -v 输出更多信息 v可以多个,v越多输出的日志信息也越多 - - -r 文件夹递归同步,这种是采用`上面的I策略`的 - - -l 同步软链接文件,默认是忽略该类文件的 - - -L 同步软链接文件及其目标文件 - - -z 压缩数据,提高传输速度 - - -p 缺省该参数时,如果远程没有该文件,权限会和本地的文件一致, 如果远程已经有该文件,权限和本地的不同, 那么命令不作更改。使用参数后,就会让权限尽力保持一致 - - -a 这个命令等价于 -rlptgoD 归档选项,采用递归方式,尽可能保持各方面的一致,但是不能同步硬链接,得加上 `-H` - -- 只要文件不一样,就会触发同步,该命令确保远程的是和本地的一致,本地的直接覆盖远程的 -- 只要rsync命令对本地有读权限,对远程有写权限,就能确保目录是一致的 -- rsync只能以登录远程的账号来创建文件,它不可能将文件的组信息,用户信息也一致,除非是root用户可以做到 - -`【其他特别参数】` -- `--delete` 如果本地没有该文件 远程就会删掉 - - `--delete-exclude `删除远程指定的文件 - - ` --delete-after` 默认是先清理远程文件再同步,使用该选项就相反了先同步再删除需要删除文件 -- `--exclude` 排除掉某些文件不同步 可以使用多次 - - `--excule-from` 如果要排除的文件很多,可以将文件名放在一个文本文件里,然后使用该选项读取该文件 -- `--partial` 断点续传 可以简写-P -- `--progress` 显示传输进度信息 - -### 8.wget -> 特性和优势:支持 HTTP HTTPS FTP协议 -> - 能够跟踪 HTML 和 XHTML 即可以下载整站,但是注意wget会不停的去下载HTML中的外链,无休无止 -> - 遵守 robots.txt 标准的工具 -> - 支持慢速网路和不稳定的下载,当下载失败就会不断重试,直到下载成功 -> - 支持断点续传 - -- wget 配置文件 `/etc/wgetrc` `~/.wgetrc` 两个文件配置(区别是全局和当前用户)wget的默认行为 -- 例如 -X配置:`wget -X js,css URL` 排除两个文件夹不下载 - - 如果要默认排除,到`.wgetrc`文件里配置 `exclude_directories=js,css` - - 这时候就出了一个问题,你不知道配置文件的情况时,发现总有目录下载不下来,就可以排除两个文件的作用: - - `wget -X '' -X js,css URL` - - 注意:`-X`,两个配置文件。这三者的配置,wget是取并集的, 使用了`-X ''` 后就只看后面的`-X 参数` - -- 参数: - - 目录下载 `-r` 递归选项 - - 后台下载 `--background` 即使 你Ctrl D/exit也不会中断执行 - - -o 指定日志输出。默认当前目录的 wget-log - - 避开robots.txt 协议 `--execute robots=off` - - 尝试使用tomcat构建一个有robots协议的网站,然后wget还是绕过了协议。。。。。。 - - 对github测试这个参数是正常的 - - 简化wget获取到的文件 - - -nH 去除wget将域名作为文件夹的情况,只得到域名下相对路径的文件 - - --cut-dirs=number 去除前缀路径 - - 只用 `-r` : URL:a/b/c/ - - `-r` 再用上 `--cut-dirs=1` : URL:/b/c/ - - `-r` 再用上 `-nH` :a/b/c/ - - `-r` 再用上 `-nH --cut-dirs=1` : /b/c/ - - `-r` 再用上 `-nH --cut-dirs=2` : /c/ - - 平铺,不使用源站的目录结构: `-nd` 若有重名文件,自动重命名 - - 强制处处文件夹 `-x` 例如:com.github.com/a/b/ --> com/github/com/a/b/ - - 协议命名的根文件夹 --protocol-directories 例如 ftp://baidu.com/a/b/ - - 自动重试 `--tries=number` 设置下载失败后重试的次数 - - 输出到指定文件 `-O` 将下载的所有文件的内容追加到指定的文件,不会新建任何文件 和 -o 对比:这是是指定输出日志文件 - - 拒绝重复下载同名文件,即使这个文件不是最新的 `-nc`, wget会先比较时间戳,然后下载,且多次下载同名文件会自动添加.1.2这样的后缀 - - 自动分析是否下载同名文件, `-N` 会考虑时间戳以及文件大小,但是不能和 -nc 同时设置 - - 断点续传 -c 但是有潜在bug,当源站的文件头部分或者已下载部分修改了,wget是不知道的,只会继续下载之前没下载的内容 - - 限速 `--limit-rate=N` 默认单位是b,可以指定单位 k m , - - 这个限速的实现原理是通过在进行一次网络读取后,就线程睡眠一会儿,将速度降下来,如果下载是超小文件就可能无法达到限速的效果 - - 限制频率 -w 即 --wait=seconds 可以指定m h d 等单位,效果是每两个请求间隔指定时间 - - 请求重试 `--waitretry` 设置请求重试的秒数, 如果设置的是10秒, 第一次失败后就会等1s,然后第二次失败就等2s...直到递增到10s,然后结束 - - 其效果 其实应该是 设置值的累加 (理解为重试次数似乎更好) - -> [wget cookie](http://blog.csdn.net/adream307/article/details/47379149) -> [参考博客: wget命令详解](http://blog.csdn.net/RichardYSteven/article/details/4565931) - -- 镜像整站 `wget --mirror -p --convert-links -P . URL` - - –miror:开户镜像下载 - - -p:下载所有为了html页面显示正常的文件 - - –convert-links:下载后,转换成本地的链接 - - -P .:保存所有文件和目录 到当前目录 - -**************************** -## 【常用网络服务】 -### 邮件服务器postfix和devecot - -### FTP - -#### 基础 - -#### 使用 -- 登录`ftp host port` - -#### 手机和电脑之间传输管理文件 -> 前提是两个设备处于同一个局域网, 也就是说连同一个WIFI, 或者电脑开热点给手机连? - -##### 手机 -> 手机安装 FeelFTP , 然后设置编码为utf-8, 开启服务器 -> 或者安装ES文件浏览器, 也带有FTP服务器, 但是不稳定, 切出去就停了, 而且不能选择上SDK卡 - -##### 电脑 -> 安装FileZila 建立连接, 然后就能方便的用鼠标进行传输了 - -#### 配置FTP服务器 -- `sudo apt-get install vsftpd -y` -- `sudo systemctl start vsftpd.service` -- 创建用户 `sudo useradd -d /home/uftp -s /bin/bash uftp` -- 设置密码 `sudo passwd uftp` -- 删除掉 pam.d 中 vsftpd,因为该配置文件会导致使用用户名登录 ftp 失败:`sudo rm /etc/pam.d/vsftpd` -- 限制用户 uftp 只能通过 FTP 访问服务器,而不能直接登录服务器 `sudo usermod -s /sbin/nologin` uftp -- 修改配置文件 `sudo chmod a+w /etc/vsftpd.conf` - -`/etc/vsftpd.conf ` -```conf - # 限制用户对主目录以外目录访问 - chroot_local_user=YES - # 指定一个 userlist 存放允许访问 ftp 的用户列表 - userlist_deny=NO - userlist_enable=YES - # 记录允许访问 ftp 用户列表 - userlist_file=/etc/vsftpd.user_list - # 不配置可能导致莫名的530问题 - seccomp_sandbox=NO - # 允许文件上传 - write_enable=YES - # 使用utf8编码 - utf8_filesystem=YES -``` -- 新建文件 sudo touch /etc/vsftpd.user_list -- 修改权限 `sudo chmod a+w /etc/vsftpd.user_list` -- 添加用户名 `uftp` -- 设置用户目录只读 `sudo chmod a-w /home/common` -- 新建公共目录 设置权限 `mkdir /home/common/public && sudo chmod 777 -R /home/common/public` -- 重启服务 `sudo systemctl restart vsftpd.service` - -```sh - ~$ sudo mkdir /home/common - ~$ sudo touch /home/common/welcome.txt - ~$ sudo useradd -d /home/common -s /bin/bash common - ~$ sudo passwd common - ~$ sudo rm /etc/pam.d/vsftpd - ~$ sudo usermod -s /sbin/nologin common - ~$ sudo chmod a+w /etc/vsftpd.conf - ~$ sudo vim /etc/vsftpd.conf - ~$ sudo vim /etc/vsftpd.user_list - ~$ sudo chmod a-w /home/common - ~$ sudo mkdir /home/common/public && sudo chmod 777 -R /home/common/public - ~$ sudo systemctl restart vsftpd.service -``` - -****************************** -### Ssh -> [详细](/Linux/Base/Ssh.md) - -### telnet -> [linux telnet命令参数](http://www.linuxso.com/command/telnet.html) -> [每天一个linux命令(58):telnet命令](http://www.cnblogs.com/peida/archive/2013/03/13/2956992.html) - -*********** -### VPN -#### shadowsocks -_服务端_ -- 安装服务端`sudo pip install shadowsocks` -- 启动服务`sudo ssserver -p 443 -k sd -m aes-256-cfb` -- 后台运行`sudo ssserver -p 443 -k sd -m aes-256-cfb --user nobodu -d start` -- 停止 `sudo ssserver -d stop` -- 日志 `sudo less /var/log/shadowsocks.log` - -_客户端_ -- `sudo vim /etc/ss.json` -```json - { - "server":"127.0.0.1", - "server_port":443, - "localport":1080, - "password":"password", - "timeout":600, - "method":"aes-256-cfb" - } -``` -- `sslocal -c /etc/ss/json` -- 设置代理是1080端口即可 - -### 防火墙 - -#### iptables -> [参考博客: linux下IPTABLES配置详解](http://www.cnblogs.com/JemBai/archive/2009/03/19/1416364.html) - -> 其主要配置文件为: `/etc/sysconfig/iptables` - -- 查看配置情况 ` iptables -L -n` - -- 开启端口 `iptables -A INPUT -p tcp --dport 8000 -j ACCEPT` - - -A 参数表示添加规则,此外-D表示删除规则 - - -p 表示协议,一般都是tcp - - --dport 就是指定端口号 - - -j 指定是ACCEPT还是DROP,接收还是抛弃 - -_有时候会发生这样的事情_ -1. 服务器的服务是正常启动的, 但是客户端连不上, 然后使用curl 去访问那个端口, 报错说 curl: (7) Failed to connect to 192.168.10.201 port 16888: 没有到主机的路由 -2. 那么这时候就要检查防火墙了 - - - diff --git a/Linux/Base/LinuxNetwork.md b/Linux/Base/LinuxNetwork.md new file mode 100644 index 0000000..b442bfd --- /dev/null +++ b/Linux/Base/LinuxNetwork.md @@ -0,0 +1,828 @@ +--- +title: Linux网络管理 +date: 2018-12-15 11:15:55 +tags: + - 基础 + - 网络 +categories: + - Linux +--- + +💠 + +- 1. [Linux网络管理](#linux网络管理) + - 1.1. [内核配置](#内核配置) + - 1.2. [DNS](#dns) + - 1.2.1. [nslookup](#nslookup) + - 1.2.2. [dig](#dig) + - 1.2.3. [修改DNS](#修改dns) + - 1.3. [Route](#route) + - 1.3.1. [traceroute](#traceroute) + - 1.4. [IPv4和IPv6](#ipv4和ipv6) + - 1.5. [Bridge](#bridge) + - 1.6. [Socket](#socket) + - 1.7. [TCP/UDP 工具](#tcpudp-工具) + - 1.7.1. [进程和端口互查](#进程和端口互查) + - 1.7.2. [tcpdump](#tcpdump) + - 1.8. [复合工具](#复合工具) + - 1.8.1. [ping](#ping) + - 1.8.2. [tc 流量控制](#tc-流量控制) + - 1.8.3. [netstat](#netstat) + - 1.8.4. [iproute2](#iproute2) + - 1.8.5. [nmap](#nmap) + - 1.8.6. [netcat](#netcat) + - 1.8.7. [scp](#scp) + - 1.8.8. [rsync](#rsync) + - 1.8.9. [curl](#curl) + - 1.8.10. [wget](#wget) +- 2. [证书](#证书) + - 2.1. [自签发证书](#自签发证书) +- 3. [常用服务](#常用服务) + - 3.1. [邮件服务器](#邮件服务器) + - 3.2. [FTP](#ftp) + - 3.2.1. [客户端](#客户端) + - 3.2.1.1. [命令行](#命令行) + - 3.2.1.2. [Java](#java) + - 3.2.2. [服务端](#服务端) + - 3.2.2.1. [ftpserver](#ftpserver) + - 3.2.2.2. [vsftpd](#vsftpd) + - 3.3. [SSH](#ssh) + - 3.4. [Telnet](#telnet) + - 3.5. [VPN](#vpn) + - 3.5.1. [tun/tap](#tuntap) + - 3.5.2. [shadowsocks](#shadowsocks) + - 3.5.3. [OpenVPN](#openvpn) + - 3.5.4. [Fortivpn](#fortivpn) + - 3.6. [代理](#代理) + - 3.6.1. [proxychains](#proxychains) + - 3.7. [防火墙](#防火墙) + - 3.7.1. [iptables](#iptables) + - 3.7.1.1. [四层协议端口转发](#四层协议端口转发) + - 3.8. [远程桌面](#远程桌面) + - 3.8.1. [VNC](#vnc) + - 3.8.2. [Xrdp](#xrdp) +- 4. [Tips](#tips) + +💠 2024-09-29 23:35:37 +**************************************** +# Linux网络管理 + +> [计算机网络基础](/Skills/Network/Network.md) + +## 内核配置 +> ip_local_port_range [Linux increase ip_local_port_range TCP port range](https://ma.ttias.be/linux-increase-ip_local_port_range-tcp-port-range/) + +> [参考: Linux查看网络流量](https://tlanyan.me/linux-traffic-commands/) + +iftop + +- nethogs `流量监控` +- slurm 网卡带宽监控 + +************************ + +## DNS +> [Github: dns topic](https://github.com/topics/dns) +> [DnsServer](https://github.com/TechnitiumSoftware/DnsServer) + +### nslookup +> 来自 dnsutils 包 + +快速使用 `nslookup jd.com 223.5.5.5` + +- `nslookup - 8.8.8.8` 进入 REPL 方便调试, 8.8.8.8 是Google开放的DNS 备选 8.8.4.4 + - 结果解释:Non-authoritative answer: 表示这是从缓存得到的结果,不一定准确 + - Server:上连DNS服务器的IP, Address:`上连DNS的IP#端口` 通常是53 + - canonical name 即CNAME 别名 + +### dig +> 比 nslookup 更强大 **Domain Information Groper** + +快速使用 `dig @8.8.8.8 www.baidu.com` + +> dig [option] @8.8.8.8 www.baidu.com +- @xxx 指定DNS服务器 +- -p 53 指定DNS服务器端口 +- +tcp 采用TCP进行DNS通信(默认UDP) +- +short 精简输出 +- +nocmd+nocomment+nostat 输出最核心内容 +- -p + +`dog` 类似dig + +- `drill` + - [doc](https://linux.die.net/man/1/drill) + +- `host` + - host domain + +- `whois` + - 查询域名详细信息 +- `nali` [Github](https://github.com/zu1k/nali) + +*************************** + +### 修改DNS +- 在 `/etc/resolv.conf` 中添加Google的DNS (阿里云 DNS 223.5.5.5 223.6.6.6) +``` + nameserver 8.8.8.8 + nameserver 8.8.8.4 +``` + +> 刷新本地缓存 +1. sudo /etc/init.d/nscd restart 或者 service nscd restart , 其实就是重启 nscd 服务 + +********************** + +## Route +> [参考: 路由表的建立算法和有关的刷新协议](https://blog.csdn.net/qq_34328833/article/details/60583183) + +> [Linux-router](https://github.com/garywill/linux-router)`Linux作为路由器` + +### traceroute +> 显示网络数据包传输到指定主机的路径信息,追踪数据传输路由状况 + +- `traceroute [选项] [远程主机名或IP地址] [数据包大小]` + - -i<网络接口> : 使用指定的网络接口发送数据包 + - -n : 直接使用IP地址而不使用主机名 + - -v : 详细显示命令的执行过程 + - -w<超时秒数> : 设置等待远程主机回应的时间 + - -x : 开启或者关闭对数据包的正确性校验 + - -s<来源IP> : 设置本机主机发送数据包的IP地址 + - -g<网关地址> : 设置来源的路由网关,最多可设置8个 + +1. `traceroute -I stackoverflow.com` icmp 查看路由表 + +> [NTrace-core ](https://github.com/nxtrace/NTrace-core)`可视化` + +************************ + +## IPv4和IPv6 +- IPv4 只有32bit IPv6 有128bit + +`IPv6` +- 零省略 :如果有一位是 000C 可以直接写C +- 零压缩 :如果FE04:0:0:0:0:0:0:DA 写成 FE::DA + +## Bridge +> 网桥, 通常使用 bridge-utils 的 brctl 进行管理 + +- [ ] Learn + +**增加** + +**删除** + +**配置开机启动** + +## Socket +一个socket的五个必要元素: client_ip:client_port<----->server_ip:server_port + 协议 + +`/etc/sysctl.conf` +动态端口范围: net.ipv4.ip_local_port_range=32788 60000 `修改时不能超过[1024,65535]范围` + +*************************** +## TCP/UDP 工具 + +- [TCP 内网下载慢速分析](https://christmica.cc/archives/tcp-download-analysis) + +- 强制关闭tcp连接: killcx tcpkill +- `iperf3`: TCP UDP 测速, 在两个节点上使用iperf启动服务端和客户端进程,从而计算TCP和UDP指标信息 [Ethr](https://github.com/microsoft/ethr) Golang 仿写 + + +### 进程和端口互查 +> netstat lsof fuser + +> [参考: linux下常用命令查看端口占用](http://blog.csdn.net/ws379374000/article/details/74218530) + +- `lsof -i:端口号` 用于查看某一端口的占用情况,缺省端口号显示全部 + - 或者 `cat /etc/services` 查看系统以及使用的端口 +- 查询占用端口 `fuser -v -n tcp 22` 或者 `fuser -v 22/tcp` fuser中含三种协议: file 默认, tcp, udp + - 得到一些进程信息 `fuser -v -n tcp 0` + +- whatportis 可以通过服务查询默认端口,或者是通过端口查询默认服务的工具 + +************************ + +### tcpdump +- `tcpdump -i eth0 -nn -X 'port 53' -c 1` root用户才有运行权限 + - -i 指定监听的网络接口(网卡) + - -nn 将协议号或端口号,显示数字,而不是名称例如:21 而不显示 FTP + - -X 将协议头和包内容完整的显示出来 + - port 53 过滤,只显示53端口相关的包 + - -c 抓包的数量 + - -e 输出以太网帧头部信息输出 (能看到mac地址) + - -l 输出变为行缓冲 + - -t 输出不打印时间戳 + - -v 输出更详细信息 + - -F 指定过滤表达式所在的文件 + - -w 将流量保存到文件中 + - -r 读取raw packets 文件 + +- 列出可以选择的抓包对象 `tcpdump -D`(USB设备也能抓?) + +************************ + +## 复合工具 +> 参考书籍 《Linux 大棚命令百篇》 + +### ping +> inetutils-ping ICMP protocol + +- ping URL : Linux是默认无休止的 + - -c 次数 + - -q 安静模式 不输出 + - -s 默认64字节 + - -t 设定 TTL值,Linux默认是64或255 经过一个路由器就会减一 + - -i 每次ping的时间间隔 默认1s root用户才可以设置 0.2 以下 + - -f 暴力尽可能大量包的传送 至少每秒100个 + - 注意:得到的结果中的 mdev 表示ICMP包的RTT偏离平均值的程度,mdev 越大表示网速不稳定 Linux有,mac下叫stddev win系列没有 + - -r 记录经过的路由 (路由节点均支持ICMP协议) + +> [prettyping](http://denilson.sa.nom.br/prettyping/) +> [gping](https://github.com/orf/gping) + +- ping -s 1472 -M do 192.168.15.205 测试网络环境下最大可用MTU +- [Github: tcping](https://github.com/pouriyajamshidi/tcping) `测试tcp连接延迟` + +### tc 流量控制 +> Traffic Control + +- 限速 `tc qdisc add dev eno1 root tbf rate 400kbit latency 1ms burst 1000` +- 解除 `tc qdisc del dev eno1 root tbf rate 400kbit latency 1ms burst 1000` + - tbf : 令牌桶算法 + +- 网卡100%丢包 `tc qdisc add dev enp3s0 root netem loss 100%` 移除限制: add 换成 del +- 移除指定网卡添加的所有规则 `tc qdisc del dev enp3s0 root` +- 指定IP网段 丢包 +```sh + #! /bin/sh + interface=enp3s0 + ip=192.168.16.0/24 + delay=30ms + loss=90% + + tc qdisc add dev $interface root handle 1: prio + # 此命令立即创建了类: 1:1, 1:2, 1:3 ( 缺省三个子类 ) + tc filter add dev $interface parent 1:0 protocol ip prio 1 u32 match ip dst $ip flowid 2:1 + # 在 1:1 节点添加一个过滤规则 , 优先权 1: 凡是去往目的地址是 $ip( 精确匹配 ) 的 IP 数据包 , 发送到频道 2:1. + tc qdisc add dev $interface parent 1:1 handle 2: netem delay $delay loss $loss +``` + +> [wondershaper](https://github.com/magnific0/wondershaper) + +### netstat +> 相关 [iproute2](#iproute2) + +- `netstat -tunlp | grep 端口号` 用于查看指定的端口号的进程情况 +- 参数 + - `-p` 显示建立相关连接的程序名和PID **需要root** + - `-a` 显示本机所有连接和监听端口 + - `-n` 以网络IP地址的形式显示当前建立的有效连接和端口 + - `-r` 显示路由表信息 + - `-s` 显示协议的统计信息。 + - `-v` 显示当前的有效连接 + - `-t` 显示所有的TCP协议连接情况 + - `-u` 显示所有的UDP协议连接情况 + - `-c<秒数>` 后面跟的秒数,表示每个几秒就刷新一次显示 + - `-i` 显示自动配置接口的状态 + - `-l` 仅显示连接状态为“LISTEN”的服务的网络状态 + +- `netstat -an|awk '/tcp/ {print $6}'|sort|uniq -c| sort -hr` 查看TCP状态和数量 + +************************ + +### iproute2 +> 代替 netstat ifconfig 的强大工具 [基于iproute命令集配置Linux网络](https://cloud.tencent.com/developer/article/1183389) + +| 用途 | net-tool | iproute2 | +| :----- | :------ | :-------------- | +| 地址和链路配置 | ifconfig | ip addr/link | +| 路由表 | route | ip route | +| ARP表 | arp | ip neigh | +| VLAN | vconfig | ip link | +| 隧道 | iptunnel | ip tunnel | +| 组播 | ipmaddr | ip maddr | +| 统计 | netstat | ss | + +`net-tools 和 iproute 对应关系` + +| 作用 | net-tools用法 | iproute2用法 +| :---------- | :-------------------------------------- | :-------------------------------------- | +| 展示本机所有网络接口 | ifconfig | ip link +| 开启/停止某个网络接口 | ifconfig ech0 up/down | ip link set eth0 up/down +| 给网络接口设置/删除IP | ipconfig eth0 10.0.0.0.1/24 / ifconfig eth0 0 | ip addr add/del 10.0.0.1/24 dev eth0 +| 显示某个网络接口的IP | ifconfig eth0 | ip addr show dev eth0 +| 显示路由表 | route -n | ip route show +| 添加删除默认网关 | route add/del default gw 192.168.1.2 eth0 | ip route add/del via 192.168.1.2 eth0
ip route replace default via 192.168.1.2 dev eth0 +| 添加ARP | arp -s 192.168.1.100 00:0c:29:c5:5a:ed | ip neigh add 192.168.1.100 lladdr 00:0c:29:c5:5a:ed dev eth0 +| 删除ARP | arp -d 192.168.1.100 | ip neigh del 192.168.1.100 dev eth0 +| 展示套接字状态 | netstat -l | ss -l + +- 默认网关: 如果主机找不到转发规则, 就把数据包发给默认的网关(家用网络一般是路由器的ip) +- 增加/删除一条路由规则 `ip route add/del 192.168.2.0/24 via 192.168.1.254` + - 当使用 VPN 时,建立新的虚拟网卡 tun, 可以手动设置路由让指定ip走虚拟网卡 从而访问到VPN内局域网地址(网络号和真实网卡一样,默认会把数据包转发至本地局域网) +- 设置网卡 eno1 MAC 地址`ip link set eno1 address b4:xx:xx` + +- 关闭 启用 `ifconfig name down/up` +- 修改IP `ifconfig eth0 192.168.1.200/24` + +************************ +_iproute-ss_ + +> [参考: Linux网络状态工具ss命令使用详解](http://www.ttlsa.com/linux-command/ss-replace-netstat/) + +- 查看网络连接统计 `ss -s` +- 查看打开的端口 `ss -l` +- 查看打开的端口以及进程pid `ss -pl` +- 查看所有socket连接 `ss -a` +- 隧道术: 网络协议的数据包被封装在另一种网络协议的数据包之中 `这是VPN的技术理论基础` +- 按指定端口过滤 `ss -at '( dport = :3308 )'` + +************************ + +### nmap + +> 按主机扫描端口 + +> [参考博客](http://aaaxiang000.blog.163.com/blog/static/2063491220113284325531/) + +- 主机扫描 + - nmap -sS 192.168.1.1  //TCP、SYN扫描,使用最多,最快 `无参数扫描默认添加-sS参数` + - nmap -Pn 192.168.1.1   //当目标主机禁ping时使用,假设主机存活扫描端口(耗时长) + - nmap -p- 192.168.1.1   //扫描目标主机全部端口 + - nmap -sP 192.168.1.1  //只对目标进行ping检测,快速 + - nmap 192.168.1.1/24   //对网段进行扫描 + +- 进阶用法 + - nmap -V 192.168.1.1 //显示扫描细节 + - nmap -A 192.168.1.1 //综合扫描 + - nmap -sT 192.168.1.1 //进行tcp扫描 + - nmap -sU 192.168.1.1 //进行udp扫描 + - nmap -sV 192.168.1.1 //对目标上的服务程序版本进行扫描 + - nmap -T4 192.168.1.1 //设置扫描速度1~5 + - nmap -sn 192.168.1.1 //相比sP检验存活使用更多方式 + - nmap -O 192.168.1.1 //对目标主机的操作系统进行扫描(-A获得更多信息) + - nmap --data-length:55 192.168.1.1 //添加垃圾数据避免nmap被识别 + - nmap -D IP1,IP2... IP //发送参杂着假ip的数据包检测 + +- 使用环境 + - 扫描网段存活IP:nmap -sP 192.168.1.1/24 + - 扫描所有端口开放情况:nmap -sS -p 1-65535 192.168.1.1 + - 当目标主机禁ping时:nmap -Pn 192.168.1.1 + - 当目标可能存在waf拦截时:nmap -sS --data-length:55 192.168.1.1 + - 尽可能收集目标主机信息:nmap -p 1-65535 -sV -A -V 192.168.1.1 + +> 按端口扫描 + +masscan +Zmap `在千兆网卡状态下,45 分钟内扫描全网络 IPv4 地址` + + +### netcat +> sudo apt install netcat + +- 监听端口 `nc -l 11044` + - 建立连接 `nc 127.0.0.1 11044` 任一方退出 netcat 就终止了该连接 + +- 端口扫描 `nc -z -v -n -w 2 127.0.0.1 20-33` 扫描22-33端口 + - -z 一旦连接立马断开,不发送接收任何数据 + - -v 输出详细信息 + - -n 直接使用IP地址,不使用域名服务器来查询其域名 + - -w 设置连接超时时间 s + - -u 使用UDP 默认缺省则是TCP +- 连接开放的端口 `nc -v host port` + +- 传输文件 + - 服务端发送文件 `nc -v -l -p port < temp_out.md` + - 客户端接收文件 `nc -v -n host port > temp_in.md` + - *注意* 仅单次连接,传输完毕自动断开, 没有进度提示,大文件也不支持。也可以服务端接收文件客户端发,将 `< >` 互换即可 + +- 传输文件夹 + - 服务端 `tar -cvPf - /root/book/ | nc -l 12345` + - 客户端 `nc -n host port | tar -xvPf -` + - 这是未压缩的, 压缩再加上参数即可 例如 `gzip -czvPf -xzvPf` + +### scp +> scp命令用于在Linux下进行远程拷贝文件的命令,和它类似的命令有cp,认证用的是ssh 所以也能使用sshpass + +``` + -1:使用ssh协议版本1; + -2:使用ssh协议版本2; + -4:使用ipv4; + -6:使用ipv6; + -B:以批处理模式运行; + -C:使用压缩; + -F:指定ssh配置文件; + -l:指定宽带限制; + -o:指定使用的ssh选项; + -i: 指定私钥文件 + -P:指定远程主机的端口号; + -p:保留文件的最后修改时间,最后访问时间和权限模式; + -q:不显示复制进度; + -r:以递归方式复制。 +``` +- 远程到本地 `scp root@10.10.10.10:/opt/soft/nginx-0.5.38.tar.gz /opt/soft/` +- 本地到远程 `scp /opt/soft/nginx-0.5.38.tar.gz root@10.10.10.10:/opt/soft/scptest` + +> 注: scp rcp wget rsync 几种传输文件的方式 + +### rsync +> 同步命令 (个人倾向于本地和远程, 书上称为源端和目的端) [命令参数详解](http://man.linuxde.net/rsync) | [本地和VPS0之间同步数据](https://www.digitalocean.com/community/tutorials/how-to-use-rsync-to-sync-local-and-remote-directories-on-a-vps) + +- 同步到 `rsync file user@host:path` 上, 是将这里的file文件覆盖远程的目录下的file文件,不像git那样 + - 同步当前目录 将file 换成 \`ls\` + - -t 不加该参数:不会同步文件的修改时间,采用的`quick check策略`。使用后:让修改时间也同步,如果修改时间一致,就不同步(它不考虑文件内容,这是个坑)。 + - -I 就能解决上面的问题,每个文件都进行同步,代价是速度慢 + - -v 输出更多信息 v可以多个,v越多输出的日志信息也越多 + - -r 文件夹递归同步,这种是采用`上面的I策略`的 + - -l 同步软链接文件,默认是忽略该类文件的 + - -L 同步软链接文件及其目标文件 + - -z 压缩数据,提高传输速度 + - -p 缺省该参数时,如果远程没有该文件,权限会和本地的文件一致, 如果远程已经有该文件,权限和本地的不同, 那么命令不作更改。使用参数后,就会让权限尽力保持一致 + - -a 这个命令等价于 -rlptgoD 归档选项,采用递归方式,尽可能保持各方面的一致,但是不能同步硬链接,得加上 `-H` + +- 只要文件不一样,就会触发同步,该命令确保远程的是和本地的一致,本地的直接覆盖远程的 +- 只要rsync命令对本地有读权限,对远程有写权限,就能确保目录是一致的 +- rsync只能以登录远程的账号来创建文件,它不可能将文件的组信息,用户信息也一致,除非是root用户可以做到 + +**【其他特别参数】** +- `--delete` 如果本地没有该文件 远程就会删掉 + - `--delete-exclude `删除远程指定的文件 + - `--delete-after` 默认是先清理远程文件再同步,使用该选项就相反了先同步再删除需要删除文件 + - 例如 `mkdir blackdir && rsync -a --delete blackdir/ test/` 删除目录内大量小文件时相较rm速度更快 + +- `--exclude` 排除掉某些文件不同步 可以使用多次 + - `--excule-from` 如果要排除的文件很多,可以将文件名放在一个文本文件里,然后使用该选项读取该文件 +- `--partial` 断点续传 可以简写-P +- `--progress` 显示传输进度信息 + +************************ + +### curl +> [Official site](https://curl.haxx.se/) + +1. 不输出,重定向到*黑洞设备* ` curl -s -o /dev/null URL` +1. 使用基础认证 发送JSON数据 `curl -i -H "Content-Type:application/json" -u admin:secret -X POST --data '{"title":"1","content":"1"}' http://tomcat.kcp/email/content` + - 如果没有认证则会收到 401 返回码 + +- 使用Cookie `curl -v --cookie "USER_TOKEN=Yes" http://127.0.0.1:5000/` +- 使用代理 `-x, --proxy [protocol://]host[:port]` +- 设置 Header `-H "xxx:xxx"` 例如 `-H "Content-Type:application/json" -H "token:xxx"` +- 设置POST请求 body `-d '{"title":"1","content":"1"}'` + - 本地文件 `-d '@data.json'` +- 上传文件 `curl -X POST -H "Content-Type: multipart/form-data" -F "file=@index.html" URL` + +> [参考: curl返回常见错误码](http://www.cnblogs.com/wainiwann/p/3492939.html) +- [56错误码](https://stackoverflow.com/questions/10285700/curl-error-recv-failure-connection-reset-by-peer-php-curl) +> [参考: 使用cURL和用户名和密码?](http://www.cnblogs.com/seasonzone/p/7527218.html) + +- httpie `python` +- curlie +- [bat](https://github.com/astaxie/bat) `go` +- [xh](https://github.com/ducaale/xh)`rust` +- [hurl](https://github.com/Orange-OpenSource/hurl)`rust` IDEA中Http插件一样,按HTTP协议写脚本 hurl运行 + +************************ + +### wget +> 特性和优势:支持 HTTP HTTPS FTP协议 +>1. 能够跟踪 HTML 和 XHTML 即可以下载整站,但是注意wget会不停的去下载HTML中的外链,无休无止 +>1. 遵守 robots.txt 标准的工具 +>1. 支持慢速网路和不稳定的下载,当下载失败就会不断重试,直到下载成功 +>1. 支持断点续传 + +- wget 配置文件 `/etc/wgetrc` `~/.wgetrc` 两个文件配置(区别是全局和当前用户)wget的默认行为 + +- 例如 -X配置:`wget -X js,css URL` 排除两个文件夹不下载 + - 如果要默认排除,到`.wgetrc`文件里配置 `exclude_directories=js,css` + - 这时候就出了一个问题,你不知道配置文件的情况时,发现总有目录下载不下来,就可以排除两个文件的作用: + - `wget -X '' -X js,css URL` + - 注意:`-X`,两个配置文件。这三者的配置,wget是取并集的, 使用了`-X ''` 后就只看后面的`-X 参数` + +- 参数: + - 目录下载 `-r` 递归选项 + - 后台下载 `--background` 即使 你Ctrl D/exit也不会中断执行 + - `-o` 指定日志输出。默认当前目录的 wget-log + - `--content-disposition` 支持HTTP Content-Disposition标头,通常包含文件名信息 + - `-O` 将下载的所有文件的内容追加到指定的文件 + - `-c` 断点续传 但是有潜在问题, 例如当源站的文件头部分或者已下载部分修改了,但wget只从上次下载的进度开始继续下载 + - 避开robots.txt 协议 `--execute robots=off` + - 尝试使用 Tomcat 构建一个有robots协议的网站,然后wget还是绕过了协议 + - 对 Github 测试这个参数是正常的 + + - 简化wget获取到的文件 + - -nH 去除wget将域名作为文件夹的情况,只得到域名下相对路径的文件 + - --cut-dirs=number 去除前缀路径 + - 只用 `-r` : URL:a/b/c/ + - `-r` 再用上 `--cut-dirs=1` : URL:/b/c/ + - `-r` 再用上 `-nH` :a/b/c/ + - `-r` 再用上 `-nH --cut-dirs=1` : /b/c/ + - `-r` 再用上 `-nH --cut-dirs=2` : /c/ + + - 平铺,不使用源站的目录结构: `-nd` 若有重名文件,自动重命名 + - 强制多层次文件夹 `-x` 例如:github.com/a/b/ --> github/com/a/b/ + - 协议命名的根文件夹 `--protocol-directories` 例如 ftp://baidu.com/a/b/ + - 自动重试 `--tries=number` 设置下载失败后重试的次数 + - 拒绝重复下载同名文件,即使这个文件不是最新的 `-nc`, wget会先比较时间戳,然后下载,且多次下载同名文件会自动添加 .1 .2 这样的后缀 + - 自动分析是否下载同名文件, `-N` 会考虑时间戳以及文件大小,但是不能和 -nc 同时设置 + + - 限速 `--limit-rate=N` 默认单位是b,可以指定单位 k m + - 这个限速的实现原理是通过在进行一次网络读取后,就线程睡眠一会儿,将速度降下来,如果下载是超小文件就可能无法达到限速的效果 + - 限制频率 -w 即 `--wait=seconds` 可以指定 m h d 等单位,效果是每两个请求间隔指定时间 + - 请求重试 `--waitretry` 设置请求重试的秒数, 如果设置的是10秒, 第一次失败后就会等1s,然后第二次失败就等2s...直到递增到10s,然后结束 + - 其效果 其实应该是 设置值的累加 (理解为重试次数似乎更好) + +> [wget cookie](http://blog.csdn.net/adream307/article/details/47379149) +> [参考: wget命令详解](http://blog.csdn.net/RichardYSteven/article/details/4565931) + + +- 镜像整站 `wget --mirror -p --convert-links -P . URL` + - –miror: 镜像下载 + - -p: 下载所有为了html页面显示正常的文件 + - –convert-links: 下载后,转换成本地的链接 + - `-P .`: 保存所有文件和目录 到当前目录 + +- 镜像SPA等使用了前端动态路由的网站 `wget --mirror -w 2 -p --html-extension --tries=3 -k -P stackperl.html "https://docs.egret.com/uieditor/docs/api/eui/eui.AddItems"` + - 但是下载后无法正常使用子页面,还需要web服务器处理应用动态路由到静态资源文件上去 有点麻烦 + - [参考: how-do-i-completely-mirror-a-web-page](https://stackoverflow.com/questions/400935/how-do-i-completely-mirror-a-web-page) + +- 获取API返回数据 `wget -q url -O -` + +************************ + +> [axel](https://github.com/axel-download-accelerator/axel) +> [aria2](https://github.com/aria2/aria2) + +************************ + +# 证书 + +## 自签发证书 +```sh + ############ 证书颁发机构 + # CA机构私钥 + openssl genrsa -out ca.key 2048 + # CA证书 + openssl req -x509 -new -key ca.key -out ca.crt + ############ 服务端 + # 生成服务端私钥 + openssl genrsa -out server.key 2048 + # 生成服务端证书请求文件 + openssl req -new -key server.key -out server.csr + # 使用CA证书生成服务端证书 关于sha256,默认使用的是sha1,在新版本的chrome中会被认为是不安全的,因为使用了过时的加密算法。 + openssl x509 -req -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out server.crt + # 打包服务端的资料为pkcs12格式(非必要,只是换一种格式存储上一步生成的证书) 生成过程中,需要创建访问密码,请记录下来。 + openssl pkcs12 -export -in server.crt -inkey server.key -out server.pkcs12 +``` + +> Manjaro 导入自定义证书 + +sudo trust anchor --store my-root.crt +sudo update-ca-trust + +**************************** +# 常用服务 + +## 邮件服务器 + +postfix和devecot + +************************ + +## FTP + +### 客户端 +#### 命令行 +> [FTP Commands](https://www.javatpoint.com/ftp-commands) + +#### Java +> [Apache Commons Net](https://commons.apache.org/proper/commons-net/) + +### 服务端 + +> [sftp](https://hub.docker.com/r/atmoz/sftp/) +> [Filezilla](https://filezilla-project.org/)`服务端&客户端` + +#### ftpserver +> [ftpserver](https://github.com/fclairamb/ftpserver)`Golang` + +#### vsftpd + +- `sudo apt-get install vsftpd -y` +- `sudo systemctl start vsftpd.service` +- 创建用户 `sudo useradd -d /home/uftp -s /bin/bash uftp` +- 设置密码 `sudo passwd uftp` +- 删除掉 pam.d 中 vsftpd,因为该配置文件会导致使用用户名登录 ftp 失败:`sudo rm /etc/pam.d/vsftpd` +- 限制用户 uftp 只能通过 FTP 访问服务器,而不能直接登录服务器 `sudo usermod -s /sbin/nologin` uftp +- 修改配置文件 `sudo chmod a+w /etc/vsftpd.conf` + +`/etc/vsftpd.conf ` +```conf + # 限制用户对主目录以外目录访问 + chroot_local_user=YES + # 指定一个 userlist 存放允许访问 ftp 的用户列表 + userlist_deny=NO + userlist_enable=YES + # 记录允许访问 ftp 用户列表 + userlist_file=/etc/vsftpd.user_list + # 不配置可能导致莫名的530问题 + seccomp_sandbox=NO + # 允许文件上传 + write_enable=YES + # 使用utf8编码 + utf8_filesystem=YES +``` +1. 新建文件 sudo touch /etc/vsftpd.user_list +1. 修改权限 `sudo chmod a+w /etc/vsftpd.user_list` +1. 添加用户名 `uftp` +1. 设置用户目录只读 `sudo chmod a-w /home/common` +1. 新建公共目录 设置权限 `mkdir /home/common/public && sudo chmod 777 -R /home/common/public` +1. 重启服务 `sudo systemctl restart vsftpd.service` + +```sh + $ sudo mkdir /home/common + $ sudo touch /home/common/welcome.txt + $ sudo useradd -d /home/common -s /bin/bash common + $ sudo passwd common + $ sudo rm /etc/pam.d/vsftpd + $ sudo usermod -s /sbin/nologin common + $ sudo chmod a+w /etc/vsftpd.conf + $ sudo vim /etc/vsftpd.conf + $ sudo vim /etc/vsftpd.user_list + $ sudo chmod a-w /home/common + $ sudo mkdir /home/common/public && sudo chmod 777 -R /home/common/public + $ sudo systemctl restart vsftpd.service +``` + +************************ + +## SSH +> [详细](/Linux/Base/SSH.md) + +## Telnet + +> [linux telnet命令参数](http://www.linuxso.com/command/telnet.html) +> [每天一个linux命令(58):telnet命令](http://www.cnblogs.com/peida/archive/2013/03/13/2956992.html) + +- 测试端口连通性 `telnet ip port` 如果端口开放则提示 Connected, 否则会提示 refused + - netcat 具有同样效果 `nc -v -z -n ip port` + +## VPN +### tun/tap +> [参考: linux下TUN/TAP虚拟网卡的使用](https://blog.csdn.net/bytxl/article/details/26586109) + +`TUN TAP 区别` + +> TUN +1. 工作在IP层 第三层 + +> TAP +1. 工作在数据链路层,第二层 + +### shadowsocks +_服务端_ +- 安装服务端`sudo pip install shadowsocks` +- 启动服务`sudo ssserver -p 443 -k sd -m aes-256-cfb` +- 后台运行`sudo ssserver -p 443 -k sd -m aes-256-cfb --user nobodu -d start` +- 停止 `sudo ssserver -d stop` +- 日志 `sudo less /var/log/shadowsocks.log` + +_客户端_ +- `sudo vim /etc/ss.json` +```json + { + "server":"127.0.0.1", + "server_port":443, + "localport":1080, + "password":"password", + "timeout":600, + "method":"aes-256-cfb" + } +``` +- `sslocal -c /etc/ss/json` +- 设置代理是1080端口即可 + +### OpenVPN +> [arch wiki](https://wiki.archlinux.org/index.php/OpenVPN) + +以下文件都在同一目录下 +1. 服务端提供 ca 文件 +1. 配置文件 `connect.ovpn` + ``` + client + dev tun + proto tcp + remote IP PORT # 服务端IP地址映射的公网IP地址 端口 + resolv-retry infinite + nobind + persist-key + persist-tun + + ca ca.crt # ca 文件 + + auth-user-pass # 可选 password 文件 + + comp-lzo + verb 3 + ``` +1. 账户密码文件 `passwd` + ``` + 用户名 + 密码 + ``` +1. 启动 sudo openvpn --daemon --cd /etc/openvpn/client --config connect.ovpn --auth-user-pass /etc/openvpn/client/passwd --log-append /path/to/log.log + +> ERROR: Cannot open TUN/TAP dev /dev/net/tun: No such device +1. modinfo tun 查看内核模块是否存在 +1. 尝试 sudo pacman -S networkmanager-vpnc 并重启 + +### Fortivpn +> [openfortivpn](https://github.com/adrienverge/openfortivpn) 对应于 [fortinet.com](https://fortinet.com/) 的开源版本 + +`yay openfortivpn` + +1. 按官方文档新建配置文件 some_company.conf + - 填写正确的 host,port,username,password + - 注意: trusted-cert 可以用仓库Readme文档的值,通过报错信息获得公司内的证书,然后反向填入配置文件。。。 + - 相比于官方的包 `forticlient-vpn` GUI配置完,不像Windows平台会提示导入证书,只有无尽的连接中。。 +1. sudo openfortivpn -c some_company.conf +1. 手动追加dns `sudo sed -i '1 i\nameserver x.x.x.x' /etc/resolv.conf` 注意 dns的ip会从运行中的输出 ns 部分 + +************************ + +## 代理 + +### proxychains +- 安装 + - [编译安装](https://github.com/rofl0r/proxychains-ng) + - 包管理器 + ```shell + sudo pacman -S community/proxychains-ng # Arch + sudo apt install proxychains # apt + ``` +- 配置文件 `/etc/proxychains.conf` + ```conf + [ProxyList] + # add proxy here ... + # meanwile + # defaults set to "tor" + # socks4 127.0.0.1 9050 + # socks5 127.0.0.1 1080 + http 127.0.0.1 7890 + ``` + +************************ + +## 防火墙 + +### iptables +> [参考: linux下IPTABLES配置详解](http://www.cnblogs.com/JemBai/archive/2009/03/19/1416364.html) + +> 其主要配置文件为: `/etc/sysconfig/iptables` + +- 查看配置情况 ` iptables -L -n` + +- 开启/屏蔽 端口 `iptables -A INPUT -p tcp --dport 8000 -j ACCEPT` + - -A 参数表示添加规则,此外-D表示删除规则 + - -p 表示协议,一般都是tcp + - --dport 就是指定端口号 + - -j 指定 ACCEPT/DROP,接收还是抛弃 + +_问题场景_ +1. 服务器的端口服务是正常启动的, 但是客户端连不上;使用curl 去访问那个端口, 报错说 curl: (7) Failed to connect to xxxx port 8080: 没有到主机的路由 +1. curl 无响应 + +#### 四层协议端口转发 + +可实现场景:公网内服务器访问内网A域名(Nginx配置)。 +实现方案为公网服务器追加A域名的DNS到内网出口机的公网ip,出口机配置任意端口转发并修改上层应用层的请求头从而实现Nginx无感差异的访问(Nginx能正常匹配路由) + +************************ +## 远程桌面 +> [参考: 连接Linux远程桌面的四个方法](https://www.cnblogs.com/hw-1015/articles/5910969.html) +> [参考: 你会在linux服务器上安装远程桌面吗?](https://www.zhihu.com/question/20301978) + +### VNC +> Virtual Network Computing + +> [参考: Ubuntu远程SSH及x11vnc远程桌面连接](https://blog.csdn.net/ywueoei/article/details/79952727) + +1. 服务端安装 `x11vnc` +1. 设置密码 `x11vnc -storepasswd` +1. 使用密码启动 `x11vnc -auth guess -once -loop -noxdamage -repeat -rfbauth ~/.vnc/passwd -rfbport 5900 -shared` + - 设置分辨率 `-geometry 1280×1024` + +1. 客户端 vnc-viewer(任意) 输入 ip 即可连接 + +> [noVNC](https://github.com/novnc/noVNC) `VNC client web application` + +************************ + +### Xrdp +> [参考: Xrdp - 通过Windows的RDP连接Linux远程桌面](https://www.linuxidc.com/Linux/2018-10/155073.htm) + +************************ + +# Tips + diff --git a/Linux/Base/LinuxPerformance.md b/Linux/Base/LinuxPerformance.md index 4a9d69e..19beede 100644 --- a/Linux/Base/LinuxPerformance.md +++ b/Linux/Base/LinuxPerformance.md @@ -1,82 +1,184 @@ -`目录 start` - -- [Linux 性能分析和管理](#linux-性能分析和管理) - - [运行状况信息](#运行状况信息) - - [工具](#工具) - - [内存情况](#内存情况) - - [性能监测](#性能监测) - - [vmstat](#vmstat) - - [mpstat](#mpstat) - - [top](#top) - - [htop](#htop) - - [iostat](#iostat) - - [进程管理](#进程管理) - - [pidof](#pidof) - - [sar](#sar) - - [lsof](#lsof) - - [fuser](#fuser) - - [ps](#ps) - - [kill](#kill) - - [killall](#killall) - - [作业控制](#作业控制) - - [trap](#trap) - - [后台运行](#后台运行) - - [nohup](#nohup) - - [disown](#disown) - - [setid](#setid) - - [screen](#screen) - - [系统管理](#系统管理) - - [uname](#uname) - - [who](#who) - - [service](#service) - - [chkconfig](#chkconfig) - - [dmidecode](#dmidecode) - - [lsmod](#lsmod) - - [chroot](#chroot) - - [关机重启](#关机重启) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux性能分析 +date: 2018-12-15 11:16:27 +tags: + - 基础 +categories: + - Linux +--- + +💠 + +- 1. [Linux性能分析和管理](#linux性能分析和管理) +- 2. [基准测试](#基准测试) +- 3. [运行状况信息](#运行状况信息) + - 3.1. [分析工具](#分析工具) +- 4. [内核参数](#内核参数) +- 5. [内存情况](#内存情况) + - 5.1. [free](#free) + - 5.2. [smem](#smem) +- 6. [性能监测](#性能监测) + - 6.1. [perf](#perf) + - 6.2. [top](#top) + - 6.3. [smem](#smem) + - 6.4. [vmstat](#vmstat) + - 6.5. [pidstat](#pidstat) + - 6.6. [mpstat](#mpstat) + - 6.7. [iostat](#iostat) + - 6.8. [ifstat](#ifstat) +- 7. [进程管理](#进程管理) + - 7.1. [pidof](#pidof) + - 7.2. [pgrep](#pgrep) + - 7.3. [sar](#sar) + - 7.4. [lsof](#lsof) + - 7.4.1. [删除文件](#删除文件) + - 7.5. [fuser](#fuser) + - 7.6. [ps](#ps) + - 7.6.1. [procs](#procs) + - 7.6.2. [pstree](#pstree) + - 7.7. [kill](#kill) + - 7.7.1. [killall](#killall) + - 7.8. [trap](#trap) + - 7.9. [作业控制](#作业控制) + - 7.10. [守护进程](#守护进程) + - 7.10.1. [nohup](#nohup) + - 7.10.2. [disown](#disown) + - 7.10.3. [setid](#setid) + - 7.10.4. [screen](#screen) + - 7.11. [IPC](#ipc) +- 8. [系统管理](#系统管理) + - 8.1. [uname](#uname) + - 8.2. [who](#who) + - 8.3. [service](#service) + - 8.4. [chkconfig](#chkconfig) + - 8.5. [dmidecode](#dmidecode) + - 8.6. [lsmod](#lsmod) + - 8.7. [chroot](#chroot) +- 9. [关机/重启](#关机重启) + +💠 2024-10-09 16:33:39 **************************************** -# Linux 性能分析和管理 -## 运行状况信息 +# Linux性能分析和管理 + +# 基准测试 +目的:通过一致的工具及配置,跑不同的测试工具,看性能表现,对比不同设备间性能差异 + +> [几款优秀的Linux基准测试工具](https://blog.csdn.net/gatieme/article/details/54296440) +> [Arch wiki: Improving performance](https://wiki.archlinux.org/title/Improving_performance) + +1. [byte-unixbench](https://github.com/kdlucas/byte-unixbench) +1. [geekbench](https://www.geekbench.com) + +> 高负载测试 +1. [stress](https://github.com/cirocosta/stress) +1. [stress-ng](https://github.com/ColinIanKing/stress-ng) +- stress-ng --cpu 16 --timeout 180 `占满CPU` +- stress-ng --vm 4 --vm-bytes 10G --vm-hang 180 --timeout 180s +- stress-ng --hdd 5 --hdd-bytes 10G --timeout 180s + +> 简易评测 +- 单核CPU: `time echo "scale=9000; 4*a(1)" | bc -l -q` + +| 设备 | 耗时 | +|:----|:----| +| Mac Book Pro 2023 32G: | **4s** | +| i5-10400F CPU @ 2.90GHz: | **60s** | +| 笔记本 AMD Ryzen 7 6800H: | **59s** | +| 阿里云99年费双核服务器 2.5GHz | **79s** | +| Redmi K60 | **57s** | +| AMD 3700X 8-Core @ 16x 3.6GHz | **53s** | + +> 3700x 和 10400F 和 K60 的 8+ Gen 1 单核性能是差不多的 😅 + +************************ + +# 运行状况信息 > 系统实时状态信息 +- top +- [htop](https://github.com/hishamhm/htop)`Htop更好用` + - [你一定用過 htop,但你有看懂每個欄位嗎?](https://medium.com/starbugs/do-you-understand-htop-ffb72b3d5629) + - CPU: Task 进程 thr 线程 kthr 内核线程 running 执行中的线程 +- gotop +- ytop +- ctop +- [Glances](https://github.com/nicolargo/glances) `信息全面 资源消耗大些` +- nmon + - `uptime ` 执行结果 - 系统当前时间 | 主机已运行时间 | 用户连接数 | 1,5,15,分钟的系统平均负载 - `cat /proc/loadavg ` - - 运行结果 : 1,5,15分钟的平均负载 | 当前运行的进程/总进程 | 最近一个启动的进程的id -> 常规: 单核:平均负载0.7以下是安全的,大于就需要优化了,多核则是 0.7*N(核心数) + - 运行结果 : 1,5,15分钟的平均负载 | 当前运行的进程/总进程 | 最近一个启动的进程的id +> 常规: 单核:平均负载0.7以下是安全的,大于就需要优化了,多核则是 0.7*N(核心数) +> [从源码看Load计算方式](http://www.penglixun.com/tech/system/how_to_calc_load_cpu.html) + +- `lm-sensors` CPU等硬件温度等信息检测 [参考](https://www.ostechnix.com/view-cpu-temperature-linux/) -### 工具 +## 分析工具 > [vector](https://github.com/Netflix/vector) > [CPU-X ](http://x0rg.github.io/CPU-X/) | [Github:repo](https://github.com/X0rg/CPU-X)`简洁而详细` -## 内存情况 -> 对于Linux来说, 都是有内存就去分配, 然后就用, 只有内存不够了才会去回收, 对于服务器来说, 交换内存会带来性能的明显下降 一般是不会配置的 -空闲内存, 已使用, buffers, cached 共同构成了整个内存容量 +************************ -`free` -- 直接运行得到的就是内存情况,默认是kb为单位,可以指定 -b -m -g (后两种不推荐,因为向下取整的特性,出来的数据有点诡异,) +# 内核参数 +- sysctl -n name 读取配置 +- sysctl -w name value 写入配置 +- sysctl -p 不重启的情况下reload配置 + +************************ + +# 内存情况 +## free +- 直接运行得到的就是内存情况,默认是kb为单位,可以指定 -b -m -g (后两种不推荐,因为向下取整的特性) - -h 人类可读形式 推荐,能快速看到大略,精准的话还是用 -b -- 运行结果解析: - - 注意: 如果是新版的free, shared 那一栏总是为0, 因为shared本就是说明进程共享内存容量, - - free认为不能显示数有效信息, 就抛弃了这个指标,总是显示为0 - - buffers,cached: - - `buffers` 是为了写时,解决内存和硬盘巨大速度差存在的缓冲区(块设备IO相关的缓存页) - - `cache` 是为了读时,为了尽量减少内存从硬盘读数据的次数,缓冲区(普通文件相关的缓存页) - - `cached` 就是cache内存区域已经使用量 - - `used` 内存已使用量(不含buff/cache), free空闲内存, available 可用内存 - -- 设置交换分区:可以单独建立一个分区,也可以使用交换文件, - - 交换文件的设置在[Linux_file](/Linux/linux_file.md)中, - - 分区的设置就不多讲了, 主要是fdisk - -## 性能监测 -### vmstat -- 最初是设计为查看虚拟内存的,现在用于性能监测 + +**`输出解析`** + +- `used` 内存已使用量(不含buff/cache), `free` 空闲内存, `available` 可用内存 +- buffers,cached: + - `buffers` 是为了写时,解决内存和硬盘巨大速度差存在的缓冲区(块设备IO相关的缓存页) + - `cache` 是为了读时,为了尽量减少内存从硬盘读数据的次数,缓冲区(普通文件相关的缓存页) + - `cached` 就是cache内存区域已经使用量 + +>- 注意: 如果是新版的free, shared 那一栏总是为0, 因为shared本就是说明进程共享内存容量, free认为不能显示数有效信息, 就抛弃了这个指标,总是显示为0 + +## smem +较精准展示进程使用的内存和swap内存 + +************************** + +# 性能监测 +> 通过各类软件的输出,快速定位问题点 + +1. 监测CPU利用率 top,sysstat,mpstat,iostat,sar + +sysstat软件包:sysstat,mpstat vmstat iostat + +************************ +> 工程化管理多个Linux主机 + +[wgcloud](https://github.com/tianshiyeben/wgcloud) +[netdata](https://github.com/netdata/netdata) + +## perf +> [参考: Perf 使用说明](https://yoc.docs.t-head.cn/linuxbook/Chapter4/perf.html) + +> arch: yay linux-tools 选择 perf 进行安装 + +> [FlameGraph](https://github.com/brendangregg/FlameGraph) `结合使用` + +## top +> 来源 procps, 用于查看 进程详细信息, CPU占用率 内存 网络等... + +## smem +> Report memory usage with shared memory divided proportionally. + +## vmstat +> Report virtual memory statistics + +- 最初是设计为查看虚拟内存的,现在常用于性能监测 - `vmstat 1 4` 输出信息,间隔1s 共4次 特别注意第一行数据是指开机以来的平均值,后面的才是当前值 - - 输出内容: - procs 区域: - r 进程运行队列中的进程个数 - b 处于不可中断的睡眠状态的进程个数 @@ -93,7 +195,7 @@ - bo 每秒向块设备写入的块数量 - system 区域: - in 每秒中断数(含时钟中断) - - cs 每秒上下文切换次数 + - cs 每秒上下文切换次数 context switch - cpu 区域: - us 用户进程 cpu消耗时间百分比 - sy 内核进程 cpu消耗百分比 @@ -114,10 +216,20 @@ - ![p135](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p135.jpg) - ![p136](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p136.jpg) -### mpstat -> 对多处理器的统计, 和iostat同属于systat软件下,可能需要手动安装 +> 但是数值大一点就会列错位,可以用column来格式化 `vmstat 1 5 | column -t` 但是等到执行完了才能看到结果,此时可以输出到文件里,看的时候格式化 +> `vmstat 4 > run.log &` 然后 `less run.log | column -t` . 或者 `watch 'tail -n 20 run.log| column -t '` + +> 安装: apk add procps, pacman -S procps-ng + +## pidstat +> Report statistics for Linux tasks. + +- 当使用vmstat发现cs值很高时可以查看是哪些进程引起的 `pidstat -w 5` -- `mpstat -P ALL 1 1` 查询所有CPU信息,后两个参数是和vmstat一样的,如果只看0号CPU 就ALL改成0即可 +## mpstat +> 对多处理器的统计 + +- `mpstat -P ALL 1 1` 查询所有CPU信息,后两个参数是和vmstat一样的, `如果只看0号CPU 就ALL改成0即可` - 运行结果: - %user 用户进程 % - %nice 进程降级时CPU % @@ -128,17 +240,11 @@ - %steal 虚机管理程序占用的 CPU % - %guest 运行虚拟处理器占用的CPU % - %idle CPU空闲时间 -- 参数 +- 参数 - `-I ` 值可选, SUM CPU ALL - 分别表示 CPU总的中断数, 展示每一个CPU的中断数 SUM和CPU数据综合展示 -### top -> 感觉 htop 就是基于这个开发的, 使用htop更简单些, 这个强大但是好多参数 - -### htop -> 其中主要是看 RES 真实内存, VIRT 是虚拟内存(也就是进程和线程以为自己能拥有的内存大小) - -### iostat +## iostat - 执行`iostat`输出信息: - 第一部分, 系统信息 - 第二部分, CPU信息 @@ -158,18 +264,40 @@ - ![p163](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p163.jpg) - ![p164](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p164.jpg) +************************ + +## ifstat +> handy utility to read network interface statistics + +- -a 全部统计信息 +- -t sec 过去时间内流量信息 + +**************************** -## 进程管理 +# 进程管理 > 按程序名字找到id `ps -ef | grep "$NAME" | grep -v "grep" | awk '{print $2}'` -### pidof +## pidof +> find the process ID of a running program + - 查询ssh服务启动的进程的pid `pidof sshd` - 找出shell脚本执行的pid, `pidof -x 脚本文件名` - -s 只显示一个pid, 有的软件会有多个进程,就有多个pid - 忽略指定的pid `-o pid` - ![p167](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p167.jpg) -### sar +## pgrep +> pgrep, pkill - look up or signal processes based on name and other attributes + +1. pgrep java 查看Java `进程` + +## sar +> Collect, report, or save system activity information. + +**需要安装和启动 sysstat 服务 才能使用** + +> [ksar](https://sourceforge.net/projects/ksar/) + - 默认持续执行除非Ctrl C退出,指定参数后就和vmstat一样 `sar 2 3` - 输出到指定文件中: `-o filename` 注意这个不是文本结构,是特殊的结构化方式, 查看需要 `sar -f filename` - 多核的支持:`sar -P ALL 1 1 ` 与mpstat 大致相同 @@ -185,12 +313,13 @@ - ![p175](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p175.jpg) - ![p176](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p176.jpg) -### lsof -> 这个命令使用时最好是 sudo或者root用户, 不然就会警告说显示信息不完全 +## lsof +> list open files +> 这个命令使用时最好使用sudo或者root用户, 不然就会提示因权限问题导致显示信息不全 -1. `lsof -d 3` 查看打开标准错误输出的进程 (标准错误输出是3) +1. `lsof -d 3` 查看打开标准错误输出的进程 (标准错误输出是3) `正在报错的进程` 1. `lsof file/dir` 查看打开某文件或目录(不关注子文件夹)的进程 -1. **通过进程查询打开的文件** `sudo lsof -p pid` +1. `sudo lsof -p pid` **通过进程查询打开的fd** 1. `sudo lsof -u username` 查看某一用户打开的文件 输出结果说明: - Command 进程名过长会简略显示, PID 进程标识符, USER 进程拥有者 - FD 一般是文件描述符: @@ -205,42 +334,71 @@ - rtd 根目录 - DEL 文件已经被进程删除, 但是还在内存中存在 - TYPE 文件类型: - - DIR 目录, REG 普通文件, CHR 字符类型, BLK 块设备, UNIX unix域套接字 - - FIFO 先进先出 队列, IPv4/Ipv6 网际协议套接字 + - DIR 目录, REG 普通文件, CHR 字符类型, BLK 块设备 + - UNIX unix域套接字 + - FIFO 先进先出 pipe队列 + - IPv4/Ipv6 网络socket - DEVICE 磁盘的名称, SIZE 文件大小, NODE 索引节点(文件在磁盘上的标识), NAME 打开文件的确切名字 -1. 端口占用查询 ` lsof -i [4/6] [protocol][@hostname|hostaddr] [:service|port] ` - - 4/6 IPv4/Ipv6 - - protocol TCP/UDP 缺省TCP - - :service 服务名 可以多个 逗号分隔 - - :port 端口 可以多个 逗号分隔 +> socket使用情况 + +`lsof -Pni` 列出所有socket和 `netstat -pant` 类似 +`lsof -i [4/6] [protocol][@hostname|hostaddr] [:service|port]` 按条件列出所有socket +- 4/6 IPv4/Ipv6 +- protocol TCP/UDP 缺省TCP +- :service 服务名 可以多个 逗号分隔 +- :port 端口 可以多个 逗号分隔 + +************************ -**删除文件的问题,利用lsof解决** +- `lsof -i -a -p pid` 列出指定进程打开的socket +- `lsof +L1` 查看 已删除 但是**未被回收磁盘空间的文件**见下文删除文件 + +### 删除文件 + +> 真正删除文件 - 创建一个0填充的1g文件 `dd if=/dev/zero bs=1024 count=1000000 of=./1gb.file` - - 就能看到硬盘的显著变化 `df -h` -- 然后写一个简单的程序一直占用他, 例如 python - - `rm -rf` 删除他,ls 文件不见了, 但是硬盘的占用还在 -- 原因就是,Linux系统中,rm命令删除文件实际上只是减少文件的link数, 当link数为0时,文件才会被删掉,当进程打开某文件,该文件link就加1, 因为脚本一直占用着文件,所以删除没有看到硬盘的占用下降,只是目录中找不到该文件而已 + - 就能看到硬盘的被占用了1GB `df -h` +- 然后用一个简单的程序一直占用他, 例如 less + - 删除`rm -f 1gb.file` 再 ls 下能发现文件不见了, 但是对硬盘的占用还在 + - 原因就是,Linux系统中, rm命令删除文件实际上只是减少文件的link数, 当link数为0时,文件才会被删掉。 + - 当进程打开某文件,该文件link就加1, 因为脚本一直占用着文件, 所以删除没有看到硬盘的占用下降,只是目录中找不到该文件而已 - `lsof | grep 1gb.file`或者 `lsof 1gb.file` 就能找到占用该文件的进程了,杀掉就能真正的删除文件了 - - 可以试试两个多个Python脚本同时占用, 那么要将进程全部杀掉,才有用 + - 可以试试两个多个Python脚本同时占用, 那要将进程全部杀掉,才有用 + +> 恢复删除的文件 (前提是仍被其他进程引用) -### fuser -> 和lsof功能差不多,但是这个是符合posix标准的命令 POSIX:可移植操作系统接口 +1. echo "1 2 3" >> test.log +1. less test.log `打开后不退出` +1. rm test.log +1. sudo lsof | grep test.log `找出持有该文件的进程id` + > less 12008 kcp 4r REG 8,3 40239 4990940 /home/kcp/test/test.log (deleted) +1. ls -l /proc/12008/fd/ `查询持有的文件,找到对应的数字软链接,这里找到的是4` +1. cp /proc/12008/fd/4 test.log.save `复制回来` -- `fuser -v /home/kuang/sdk` 列出正在打开这个目录的进程(和lsof一样不关注子文件夹) +## fuser +> identify processes using files or sockets +> 和 lsof 功能差不多,但 fuser 是符合 POSIX 标准的命令 (POSIX:可移植操作系统接口) + +- `fuser -v /path/to/sdk` 列出正在打开这个目录的进程(和lsof一样不关注子文件夹) - 输出信息 详解: - USER 用户, PID 进程号, COMMAND 程序名 - ACCESS 访问关系: - c 作为当前目录使用, e 作为可执行对象使用, r 作为根目录使用, s 作为共享库或其他装载对象 使用 - m 作为映射文件或共享库使用, f 打开文件, 默认不显示, F 打开文件,用于写操作 默认不显示 + `常用选项` 1. -a 显示所有命令行中指定的文件,默认情况下被访问的文件才会被显示。 1. -c 和-m一样,用于POSIX兼容。 1. -k 杀掉访问文件的进程。如果没有指定-signal就会发送SIGKILL信号。 1. -i 杀掉进程之前询问用户,如果没有-k这个选项会被忽略。 1. -l 列出所有已知的信号名称。 -1. -m name 指定一个挂载文件系统上的文件或者被挂载的块设备(名称name)。这样所有访问这个文件或者文件系统的进程都会被列出来。如果指定的是一个目录会自动转换成"name/",并使用所有挂载在那个目录下面的文件系统。 -1. -n space 指定一个不同的命名空间(space).这里支持不同的空间文件(文件名,此处默认)、tcp(本地tcp端口)、udp(本地udp端口)。对于端口, 可以指定端口号或者名称,如果不会引起歧义那么可以使用简单表示的形式,例如:name/space (即形如:80/tcp之类的表示)。 +1. -m name 指定一个挂载文件系统上的文件或者被挂载的块设备(名称name) + - 所有访问这个文件或者文件系统的进程都会被列出来。 + - 如果指定的是一个目录会自动转换成"name/",并使用所有挂载在那个目录下面的文件系统。 +1. -n space 指定一个不同的命名空间(space).这里支持不同的空间文件(文件名,此处默认)、tcp(本地tcp端口)、udp(本地udp端口)。 + - 对于端口, 可以指定端口号或者名称,如果不会引起歧义那么可以使用简单表示的形式 + - 例如:name/space (即形如:80/tcp之类的表示)。 1. -s 静默模式,这时候-u,-v会被忽略。-a不能和-s一起使用。 1. -signal 使用指定的信号,而不是用SIGKILL来杀掉进程。可以通过名称或者号码来表示信号(例如-HUP,-1),这个选项要和-k一起使用,否则会被忽略。 1. -u 在每个PID后面添加进程拥有者的用户名称。 @@ -249,33 +407,44 @@ 1. -6 使用IPV6套接字,不能和-4一起应用,只在-n的tcp和udp的命名存在时不被忽略。 1. `-` 重置所有的选项,把信号设置为SIGKILL. -- 查询占用端口 `fuser -v -n tcp 22` 或者 `fuser -v 22/tcp` fuser中含三种协议, file默认, tcp, udp +- 查询占用端口 `fuser -v -n tcp 22` 或者 `fuser -v 22/tcp` fuser中含三种协议: file 默认, tcp, udp - 得到一些进程信息 `fuser -v -n tcp 0` -- 还能发送信号 `fuser -v -k /home/kuang/sdk` 就把占用该文件夹的进程全部杀掉了(如果是ssh登录的服务器,当前目录就是这个的话, 就直接下线了) +- 发送信号量 `fuser -v -k /home/kuang/sdk` 会把占用该文件夹的进程全部杀掉 (如果是ssh登录的服务器,当前目录就是这个的话, 会掉线) -### ps -> [参考博客: ps命令输出](http://www.cnblogs.com/lidabo/p/5505610.html) `输出的信息解释` +## ps +> [参考: ps命令输出](http://www.cnblogs.com/lidabo/p/5505610.html) `输出的信息解释` - 直接运行 `ps` 就会显示当前会话中的进程 + - `ps aux` 显示系统中所有进程的状态信息 `可根据需要自由组合` - a 显示各终端(会话)上的所有进程, u 会展示进程所属用户, x 对于没有关联到终端运行的进程也展示出来 - + - 输出列 + - VSZ + - RSS + - STAT + - `D` 无法中断的休眠状态(通常 IO 的进程); + - `R` 正在运行可中在队列中可过行的; + - `S` 处于休眠状态; + - `T` 停止或被追踪; + - `W` 进入内存交换 (从内核2.6开始无效); + - `X` 死掉的进程 (基本很少见); + - `Z` 僵尸进程; + - `<` 优先级高的进程 + - `N` 优先级较低的进程 + - `L` 有些页被锁进内存; + - `s` 进程的领导者(在它之下有子进程); + - `l` 多线程,克隆线程(使用 CLONE_THREAD, 类似 NPTL pthreads); + - `+` 位于后台的进程组; - `ps aux`和`ps -aux`的区别: - 虽然执行结果看起来是一模一样的, 但是 `ps -aux ` 其实应该理解为 `ps -a -u x` 显示用户名为 x 的用户的所有进程 - 当 x 用户不存在时ps就将其理解为 `ps aux` - - 原因,因为他的三种格式: - - BSD 选项前不加短横线 - `ps aux` - - UNIX 选项前加短横线 - `ps -aux ` - - GNU 选项前加双短横线 -- `ps --format` - - BSD格式的 `ps aux` 等价于 `ps -eF` - - e 显示全部进程, 包含了未在终端运行的进程 - - F 显示详尽的进程信息 - -- `-o` 输出指定列 `ps -eo pid,user,cmd,start` - - `man ps` 可以看到可以指定的列 - - 若要取别名 `pid=进程号` - -- ![p200](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p200.jpg) + - 原因,因为他的三种格式: BSD 选项前 不加短横线 `ps aux` UNIX 选项前 加短横线 `ps -aux ` GNU 选项前 加双短横线 `ps --format` + - BSD格式的 `ps aux` 等价于 `ps -eF` e 显示全部进程, 包含了未在终端运行的进程 F 显示详尽的进程信息 + - Debian 上 `ps -ef` 和 `ps ef` 执行效果不一样 + +- `-o` 输出指定列 `ps -eo pid,user,cmd,start ... ` 更多需要查看手册 `man ps` + - [p200](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p200.jpg) + - 查看进程启动日期,时间,时间差 `ps -eo pid,start,lstart,%cpu,start_time,etime,cmd` - 对范围进行筛选 - 根据用户 `ps -u root` @@ -283,78 +452,123 @@ - -U 实际用户 RUID - -u 有效用户 EUID - u 按用户名和进程号的顺序来显示进程, 多列构成 - - 根据命令名称查找pid `ps -C sshd` 这个实用!!! - -- 排序 : - - `ps aux --sort -pcpu/+pcpu/` 按CPU使用率,进行降序/升序排列 - - 多个条件 `--sort=+pcpu, -pmem` CPU升序,内存降序排列 - -- 查询线程信息: - - `ps -ef | grep mysql` - - `ps -L pid` 显示某id的线程的具体信息 其中的LWP (轻量级进程, 可以理解为用户进程) Light Weight Process - - `ps -T pid` 显示 将-L的LWP替换为SPID (系统中的线程ID) - -- 进程树: - - BSD格式 : `ps axjf` - - a 所有进程, x 显示没有控制终端的进程, j 任务格式显示进程, f ascaii字符显示树状结果 - - UNIX : `ps -ejH` - - e 显示所有进程, j 任务格式来显示进程, H 显示数状结构 + - 根据命令名称查找pid `ps -C sshd` + +> 排序 : +- `ps aux --sort -pcpu/+pcpu/` 按CPU使用率,进行降序/升序排列 +- 多个条件 `--sort=+pcpu, -pmem` CPU升序,内存降序排列 + +>查询线程信息: +- `ps -ef | grep mysql` +- `ps -L pid` 显示某id的线程的具体信息 其中的LWP (轻量级进程, 可以理解为用户进程) Light Weight Process + - `ps aux -L` 查看全部线程 +- `ps -T pid` 显示 将-L的LWP替换为SPID (系统中的线程ID) +- 查看进程下的线程 `ps huH p pid` + +> 进程树: +- BSD格式 : `ps axjf` a 所有进程 x 显示没有控制终端的进程 j 任务格式显示进程 f ascaii字符显示树状结果 +- UNIX : `ps -ejH` e 显示所有进程 j 任务格式来显示进程 H 显示数状结构 **实践** 1. 列出Java进程 `ps aux | grep RSS | grep -v "grep" && ps aux | egrep -v "grep" | grep -i java` -1. 统计所有java进程内存使用 `ps aux|grep java | grep -v grep | awk "{sum+=$6};END {print sum "K " sum/1024"M "}"` +1. 统计所有Java进程的内存 `ps aux | grep java | grep -v grep | awk '{sum+=$6};END {print sum "K " sum/1024"M "}'` + - `ps -a -x -o rss,comm | grep java | awk '{sum+=$1};END {print sum "K " sum/1024"M "}'` +1. 统计某个用户下进程所有内存 `ps -o pid,ppid,pgid,rss,comm -u deployer | awk '{sum+=$4};END {print sum "K " sum/1024"M "}'` +1. 统计某个应用进程所有内存(自己和所有子进程) ` ` + 1. 按内存排序 列出所有进程 `ps aux | grep -v RSS | awk "{print $6 "\t" $11 }" | sort --human-numeric-sort -r | less` +1. 按实际执行的二进制命令展示 `ps -ely` -### kill -- `kill -l` 或者 `trap -l` 显示kill可以向进程发送的信号 - - kill是通过发送信号让进程自己决定做什么,而不是kill去做什么 | 那要是有恶意屏蔽信号的进程怎么办 +- [Difference Between Resident Set Size and Virtual Memory Size](https://www.baeldung.com/linux/resident-set-vs-virtual-memory-size) + - RSS 驻留内存(共享库+堆+栈) 注意当前进程可能共用别的进程已加载的共享库,所以这部分内存是被重复计算了 + - VSZ 虚拟内存 -| 信号名称 | 信号编号 | 说明 -|:---:|:---:|:---| -|HUP|1| 终端断线,关闭所有其从属的子进程| -|INT|2|中断 同`Ctrl+C` 结束前台进程,输入阻塞的程序应该退出(自己做清理)并清除阻塞状态| -|QUIT|3|退出 同`Ctrl+\` 也有点强制退出的意思| -|FPE|8|发生算术运算错误时发出| -|TERM|15|终止 程序自己做清理工作,然后退出 **缺省的信号值**| -|KILL|9|强制终止 退出| -|CONT|18|继续 `fg/bg` 命令| -|STOP|19|暂停/停止 同 `Ctrl+Z`| +### procs +Rust 编写的 现代 ps -> 例如 reids的服务端: -> INT/TERM 信号就相当于在客户端的shutdown命令,是正常的退出 -> QUIT/KILL 信号是强制退出 -> STOP 信号就是暂停挂在后台 +### pstree +> 顾名思义 树状图展示进程 线程关系 + +> [参考: Linux下查看线程数的几种方法汇总](https://www.cnblogs.com/yinzhengjie/p/9998771.html) + +************************ + +## kill +- `kill -l` 或者 `trap -l`: 输出kill命令可向进程发送的信号 + - kill是通过发送信号让进程自己决定做什么,而不是kill去做什么 + - 那要是有恶意屏蔽信号的进程怎么办? `9号无法屏蔽` - kill命令格式`kill [选项] [进程号]` - - 选项: - - -l 列出所有的信号,如果-l后加上信号名称看到对应的数字,反之亦然 - - -s 可以指定发出的信号,等同于 -信号 向目标进程发送指定的信号类型 - - 缺省会发送默认的终止信号, SIGTERM 信号 15 - - 进程号: - - 大于0: 向目标进程发送指定信号,多个逗号隔开 - - 等于0: 向当前进程组的所有进程发送信号 - - 等于-1: 向除kill进程和init进程(1)之外的所有进程发送信号 - - 小于-1: 向进程组对应的PGID的所有进程发送信号 +- 选项: + - -l 列出所有的信号,如果-l后加上信号名称看到对应的数字,反之亦然 + - -s 可以指定发出的信号,等同于 -信号 向目标进程发送指定的信号类型 + - 缺省会发送默认的终止信号 **SIGTERM 15** +- 进程号: + - 大于0: 向目标进程发送指定信号,多个逗号隔开 + - 等于0: 向当前进程组的所有进程发送信号 + - 等于-1: 向除kill进程和init进程(1)之外的所有进程发送信号 + - 小于-1: 向进程组对应的PGID的所有进程发送信号 + +> 常用信号量 +| 信号名称 | 信号编号 | 说明 +|:---:|:---:|:---| +|HUP |1 | 终端会话断开,关闭所有其从属的子进程| +|INT |2 | 中断 同`Ctrl+C` 结束前台进程,输入阻塞的程序应该退出(自己做清理)并清除阻塞状态| +|QUIT |3 | 退出 同`Ctrl+\` 也有点强制退出的意思| +|FPE |8 | 发生算术运算错误时发出| +|KILL |9 | 强制终止 退出| +|TERM |15| 终止 程序自己做清理工作,然后退出 **缺省的信号值**| +|CONT |18| 继续 `fg/bg` 命令| +|STOP |19| 暂停/停止 同 `Ctrl+Z`| + +> [中文释义:全部信号](https://wker.com/linux-command/kill.html) `或者直接 man kill` - 9号信号: - 能对所有的进程起作用, 除了1号init进程 - - 副作用:进程运行中,突然终止,可能会导致系统资源无法释放, 数据没有同步到磁盘等情况(3信号就好点) + - 副作用:进程运行中,突然终止,可能会导致系统资源无法释放, 数据没有同步到磁盘等情况(3号就好点) - 杀掉指定id(需要sudo)`kill -9 pid` - - 0号信号: - 测试信号,测试目标进程是否存在,测试是否具有向指定进程发送信号的权限 -- 终结后台作业: - - 命令格式: `kill -信号 %作业号` 编号就是运行`jobs`后方括号内编号 +************************ +`Tips` + +> 例如 reids的服务端: +- INT/TERM 信号就相当于在客户端的shutdown命令,是正常的退出 +- QUIT/KILL 信号是强制退出 +- STOP 信号就是暂停挂在后台 + +>- 终结后台作业: 命令格式: `kill -信号 %作业号` 作业号就是运行`jobs`后方括号内数字 +>- dmesg 可以查看被Kill的进程的日志 ### killall > 通过名字来发送信号,其他和kill是一致的 -- 杀掉指定名字 不需要sudo `killall -9 name` +- 杀掉指定名字 不需要sudo `killall -9 name` 要十分谨慎的使用, 避免误杀进程 + +## trap +> 捕捉信号并响应 +`trap "commands" signal-list` + +> 用途 +- 动态读取并更新配置文件 +- 忽略信号对程序可能的影响 `trap "" 2`: 忽略 `Ctrl+C` +- 可以针对用户的退出操作做hook,询问用户是否真的确认要退出,或者关闭资源,清除临时文件等等 -- [ ] 确定使用注意事项, 避免误杀 +- 屏蔽信号 + - `trap "" INT` 屏蔽中断信号 + - `trap INT` 恢复 + +- 监控文件的变化,当按下快捷键Ctrl+C 就会执行trap中的命令 + ```sh + #!/bin/bash + trap 'echo "hello"' 2 + tail -f ~/.bashrc + ``` -### 作业控制 +************************ + +## 作业控制 > 在Linux中, 作业是由一个或多个进程构成的, 作业控制就是对作业的行为进行控制, 前后台的切换, 终止等操作 - 常用的操作: @@ -375,38 +589,22 @@ `指定作业` | 符号 | 含义 | 示例| -|:---|:---|:---:| -| %Number | 根据编号来指定作业 | fg %1 | -| %String| 匹配命令以String开头的作业,如果匹配到多个就会报错 | kill %deng | -|%?String|命令行中含有String字符串的作业,如果是通过管道连接的多个命令,则仅匹配第一个命令| kill %?ng | -|%%|指代作业列表中最近一个被切换到后台的作业| kill %% | -|%+|和%%作用完全相同| kill %+ | -|%-|排在%%所指代的作业前面的那个作业| kill %- | - -> 也就是说,这个匹配也是只能匹配一个作业,不能通配 - -### trap -> 捕捉信号并响应, 格式:trap "commands" signal-list -> 动态读取并更新配置文件 -> 定期清除临时文件 -> 忽略信号对程序可能的影响 `trap "" 2`: 忽略 `Ctrl+C` -> 可以针对用户的退出操作,询问用户是否真的确认要退出 - -```sh - #!/bin/bash - trap 'echo "hello"' 2 - tail -f ~/.bashrc -``` -- 监控文件的变化,当按下快捷键Ctrl+C 就会执行trap中的命令 -- 屏蔽信号 - - `trap "" INT` 屏蔽中断信号 - - `trap INT` 恢复 +|:---|:---|:---| +| %Number | 根据编号来指定作业 | fg %1 | +| %String | 匹配命令以String开头的作业,如果匹配到多个就会报错 | kill %deng | +| %?String |命令行中含有String字符串的作业,如果是通过管道连接的多个命令,则仅匹配第一个命令| kill %?ng | +| %% |指代作业列表中最近一个被切换到后台的作业| kill %% | +| %+ |和%%作用完全相同| kill %+ | +| %- |排在%%所指代的作业前面的那个作业| kill %- | -************* -## 后台运行 -- 运行的命令不因 用户注销,网络中断等因素而中断 - - 让进程对hup信号免疫 nohup disown - - 让进程在新的会话中运行 setid screen +> 也就是说,这个匹配也是只能匹配一个作业,不能通配使用 + +## 守护进程 +> 使得普通命令启动的进程变成类似于守护进程的工具 + +两种方案: +- 让进程对hup信号免疫 nohup disown +- 让进程在新的会话中运行 setid screen ### nohup - 在命令前 加上hohup @@ -455,9 +653,12 @@ |-ls或--list | | 显示目前所有的screen作业。 |-wipe | | 检查目前所有的screen作业,并删除已经无法使用的screen作业。 +## IPC +> 进程间通信: 管道/信号量/共享内存/Socket + **************** -## 系统管理 -### uname +# 系统管理 +## uname - `uname -a` 输出所有信息 - -s 内核名称 - -n 主机名称 @@ -468,7 +669,7 @@ - -i 硬件平台名称 - -o 操作系统名称 -### who +## who - `who` 和 `w` who是按照不同tty来显示信息 - 查看系统的真实用户, - 例如当普通用户 使用su 切换用户,这条命令就显示了真正的用户,而不是su切换后的用户 @@ -476,23 +677,25 @@ - `whoami` 查看有效用户,就是当前会话的拥有者 - `groups` 查看所有组 -### service +## service - `service 服务名 status/start/stop/restart` - 查看所有service掌控的服务 `cd /etc/init.d/ && ls -F` - `which service` 结果显示这是个脚本 - `service 服务名 stop` 等价于 `/etc/init.d/服务名 stop` -### chkconfig +## chkconfig > 掌控等级制度 LSB -### dmidecode +## dmidecode > 输出机器的 BIOS CPU 内存等硬件信息 (DMTF DMI) - 运行 `dmidecode -t `就会提示你后接类别 -### lsmod -### chroot +## lsmod +> Show the status of modules in the Linux Kernel + +## chroot > change root directory 更改root目录 最古老的容器技术 - `mkdir 目录 ` 复制相关目录过来,就能把系统迁移过来了 @@ -501,13 +704,12 @@ - ![p264](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p264.jpg) - ![p265](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Linux_DaPeng_mingling100/p265.jpg) -## 关机重启 -- shutdown reboot halt poweroff init +# 关机/重启 +> shutdown | reboot | halt | poweroff | init |命令|作用| |:---|:---| -|shutdown| 可用于关机,重启,支持定时和通知| -|reboot|重启系统| -|halt|停止系统| - +|shutdown | 可用于关机,重启,支持定时和通知| +|reboot | 重启系统| +|halt | 停止系统| diff --git a/Linux/Base/LinuxProblem.md b/Linux/Base/LinuxProblem.md index 2b3a222..601cbcc 100644 --- a/Linux/Base/LinuxProblem.md +++ b/Linux/Base/LinuxProblem.md @@ -1,27 +1,50 @@ -`目录 start` - -- [遇到的常见问题](#遇到的常见问题) - - [命令找不到](#命令找不到) - - [其他](#其他) - - [终端响铃](#终端响铃) - - [Ubuntu与Windows10时间相差8小时的解决](#ubuntu与windows10时间相差8小时的解决) - - [终端开启慢](#终端开启慢) - - [Deepin的NVIDIA驱动问题](#deepin的nvidia驱动问题) - - [笔记本突然断电导致开机报错](#笔记本突然断电导致开机报错) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux桌面发行版遇到的问题 +date: 2018-12-15 11:16:42 +tags: + - 工具使用经验 +categories: + - Linux +--- + +💠 + +- 1. [Linux桌面发行版遇到的问题](#linux桌面发行版遇到的问题) + - 1.1. [软件问题](#软件问题) + - 1.1.1. [命令找不到](#命令找不到) + - 1.1.2. [终端响铃](#终端响铃) + - 1.1.3. [输入法](#输入法) + - 1.1.3.1. [fcitx](#fcitx) + - 1.1.4. [Flash](#flash) + - 1.1.5. [tracker-extract 高CPU内存占用](#tracker-extract-高cpu内存占用) + - 1.2. [驱动问题](#驱动问题) + - 1.2.1. [显卡](#显卡) + - 1.2.1.1. [Nvidia](#nvidia) + - 1.2.1.2. [Manjaro 的NVIDIA驱动问题](#manjaro-的nvidia驱动问题) + - 1.2.1.3. [Deepin 的NVIDIA驱动问题](#deepin-的nvidia驱动问题) + - 1.3. [配置问题](#配置问题) + - 1.3.1. [Ubuntu与Windows10时间相差8小时的解决](#ubuntu与windows10时间相差8小时的解决) + - 1.3.2. [终端开启慢](#终端开启慢) + - 1.4. [数据问题](#数据问题) + - 1.4.1. [突然断电](#突然断电) + - 1.5. [启动问题](#启动问题) + - 1.5.1. [can't resume from suspend](#can't-resume-from-suspend) + - 1.5.2. [i386-pc not found](#i386-pc-not-found) + - 1.6. [崩溃](#崩溃) + +💠 2024-10-15 09:56:12 **************************************** -# 遇到的常见问题 +# Linux桌面发行版遇到的问题 -## 命令找不到 -- `sudo找不到` 就安装 sudo -- locale-gen 安装locales 使用`locale-gen --purge`命令进行更新编码 +## 软件问题 +### 命令找不到 +- `sudo找不到` 安装 sudo +- `locale-gen 找不到` 安装 locales 使用`locale-gen --purge`命令进行更新编码 > Linux上的报错, 提示说找不到共享库 | [参考解决方式 ](http://www.cnblogs.com/Anker/p/3209876.html) -## 其他 ### 终端响铃 -> [参考博客: Linux中关闭响铃](https://blog.csdn.net/u010691256/article/details/9048729) +> [参考: Linux中关闭响铃](https://blog.csdn.net/u010691256/article/details/9048729) 1. 临时关闭:`rmmod pcspkr` 临时开启:`modprobe pcspkr` 1. 编辑 `/etc/inputrc`,找到`#set bell-style none`这一行,去掉前面的注释符号 @@ -33,20 +56,207 @@ - 对于CentOS/Redhat/RHEL/Fedora系统,使用root身份执行: - `echo "alias pcspkr off" >> /etc/modprobe.conf ` +### 输入法 +#### fcitx +- fcitx单核满载:三种(搜狗拼音导致) + - 杀掉,fcitx -r + - 先把进程杀掉再fcitx-autostart & + - fcitx再fcitx-qimpanel +`相关网页:` +- [某引擎搜索结果页](https://ausdn.com/s/ubuntu+cpu+fcitx)| [几种方式](https://www.findhao.net/res/786)| [卸载搜狗安装拼音](http://tieba.baidu.com/p/3863217434) +- [知乎问题](https://www.zhihu.com/question/19839748) | [ubuntu论坛](http://forum.ubuntu.com.cn/viewtopic.php?f=122&t=173730&p=1299087) | [ubuntu论坛](http://forum.ubuntu.com.cn/viewtopic.php?f=8&t=194486&start=0) + +- 输入法没有显示打字窗口 + - 直接杀掉 sogou-qimpanel 然后点击图标进行启动 + +- [ ] 部分终端(Qterminal)无法输入中文 + +### Flash +- 点击[官网下载地址](https://get.adobe.com/cn/flashplayer/)下载,然后解压, +- 将文件复制进火狐插件目录:`sudo cp libflashplayer.so /usr/lib64/mozilla/plugins` +- 添加其他用户可执行权限`chmod 755 /usr/lib64/mozilla/plugins/libflashplayer.so` + +### tracker-extract 高CPU内存占用 +> [参考: Go Away, tracker-store](https://www.soimort.org/notes/171103/) +> [参考: tracker store](https://askubuntu.com/questions/346211/tracker-store-and-tracker-miner-fs-eating-up-my-cpu-on-every-startup) + +1. 复制 `cp /etc/xdg/autostart/tracker-miner-fs-3.desktop ~/.config/autostart/` 等若干文件 并追加 `Hidden=true` +1. 禁用服务 : `systemctl --user mask tracker-store` + +****************************************************** + +## 驱动问题 +### 显卡 +- 查看显卡列表 `lspci -vnn | grep '\''[030[02]\]'` +- 测试显卡 FPS `glxgears` + +休眠后的唤起 vscode vivaldi chrome 均出现假死半分钟后才恢复的情况,禁用硬件加速可避免 + +#### Nvidia +> [NVIDIA](https://wiki.archlinux.org/index.php/NVIDIA_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)#.E5.AE.89.E8.A3.85) + +常见驱动方案有: Nouveau, bumblebee, NV_Prime + +> [Bumblebee ](https://wiki.archlinux.org/index.php/Bumblebee_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) + +大多数笔记本都是 Intel集显和 Nvidia/AMD 组成双显卡, 双显卡的管理就成了问题(指的是Linux下) +> [参考: 使用 Bumblebee 控制 NVIDIA 双显卡](https://www.cnblogs.com/congbo/archive/2012/09/12/2682105.html) + +> [Serious Issue with NVIDIA Drivers: Compatibility Problems with Linux Kernel 6.10 | by Niemand | Aug, 2024 | Medium](https://medium.com/@TheNiemand/serious-issue-with-nvidia-drivers-compatibility-problems-with-linux-kernel-6-10-9cdb0791d204)`升级Manjaro到24.1后没注意到升级了内核和驱动,然后lightdm以及X都崩溃了` +************************ +#### Manjaro 的NVIDIA驱动问题 +> [参考: Manjaro NVIDIA驱动问题的解决方案](https://blog.csdn.net/qq_39828850/article/details/87919188) + +1. `inxi -G` 检查已安装的驱动程序 +1. `sudo mhwd -a pci nonfree 0300` 安装NVIDIA驱动 +1. 重启 +1. `mhwd -li` 执行确认驱动程序(Bumbee)已安装并且正在运行,此时不要着急使用nvidia-settings + +#### Deepin 的NVIDIA驱动问题 +- [论坛博客](https://bbs.deepin.org/forum.php?mod=viewthread&tid=132312) + - `sudo apt-get install bumblebee-nvidia nvidia-driver nvidia-settings` + +************************************************ + +## 配置问题 ### Ubuntu与Windows10时间相差8小时的解决 - `timedatectl set-local-rtc true ` -### 终端开启慢 +### 终端开启慢 - 检查 .bashrc 文件 看是否有可疑脚本, - - 这次就是因为sdkman的原因导致巨慢,那上次搞得我新建用户,重装系统是什么原因呢? + - 这次就是因为 sdkman 的原因(总是在检查自动更新, 虽然说关掉就好了)导致巨慢, 打开终端要一分钟 + - 那上次搞得我新建用户,重装系统是什么原因呢? -### Deepin的NVIDIA驱动问题 -- [论坛博客](https://bbs.deepin.org/forum.php?mod=viewthread&tid=132312) - - `sudo apt-get install bumblebee-nvidia nvidia-driver nvidia-settings` +********************************************* + +## 数据问题 + +### 突然断电 +> 由于Linux延迟写的特性,如果遇到操作系统突然断电,会导致文件损坏或缺失,从而引发各种诡异问题 -### 笔记本突然断电导致开机报错 -> 报错信息: fsck exited with status code 4 +************************ -1. 根据报错提示的分区, 进行修复, 由于我的Linux是ext3文件系统 +>1. 开机报错信息: fsck exited with status code 4 + +1. 根据报错提示的分区, 进行修复, 由于我的Linux是ext3文件系统 ext4 则是 `fsck.ext4` 1. `fsck.ext3 -y /dev/sda9` **分区根据实际情况** 1. 完成后重启即可 + +************************ +>2. 导致了 Git 仓库都损坏了 `fatal: loose object` + +ZSH: corrupt history file + +```shell +mv .zsh_history .zsh_history_bad +strings .zsh_history_bad > .zsh_history +fc -R .zsh_history +``` + +************************ +>3. 导致了终端输出中文乱码 Unicode乱码,但是应用内(firefox vscode)中文输入正常,且粘贴板复制中文内容 + +## 启动问题 +### can't resume from suspend + + +### i386-pc not found +- /boot/grub/i386-pc BIOS 安装的引导 +- /boot/grub/x86_64-efi EFI安装的引导 + +`/boot/grub/i386-pc/normal.mod` not found. + +[gist](https://gist.github.com/AndersonIncorp/3acb1d657cb5eba285f4fb31f323d1c3?permalink_comment_id=3310958) + + +************************ + +## 崩溃 + +> 高内存占用,桌面管理器失去响应 journalctl -b -2 --since "2024-01-18" `重启过两次` +```log + Jan 18 09:23:21 lightdm[2507179]: gkr-pam: unable to locate daemon control file + Jan 18 09:23:21 lightdm[2507179]: gkr-pam: stashed password to try later in open session + Jan 18 09:23:21 systemd[1]: Stopping Session c147 of User lightdm... + Jan 18 09:23:21 lightdm[2507112]: pam_unix(lightdm-greeter:session): session closed for user lightdm + Jan 18 09:23:21 systemd[1]: session-c147.scope: Deactivated successfully. + Jan 18 09:23:21 systemd[1]: Stopped Session c147 of User lightdm. + Jan 18 09:23:21 systemd[1]: session-c147.scope: Consumed 7.307s CPU time. + Jan 18 09:23:21 systemd-logind[660]: Removed session c147. + Jan 18 09:23:31 systemd[1]: Stopping User Manager for UID 966... + Jan 18 09:23:31 systemd[2507120]: Activating special unit Exit the Session... + Jan 18 09:23:31 systemd[2507120]: Stopped target Main User Target. + Jan 18 09:23:31 systemd[2507120]: Stopping Accessibility services bus... + Jan 18 09:23:31 gvfsd[2507153]: A connection to the bus can't be made + Jan 18 09:23:31 systemd[2507120]: Stopping D-Bus User Message Bus... + Jan 18 09:23:31 systemd[2507120]: Stopping Virtual filesystem service... + Jan 18 09:23:31 systemd[2507120]: Stopped Accessibility services bus. + Jan 18 09:23:31 systemd[2507120]: Stopped Virtual filesystem service. + Jan 18 09:23:31 systemd[2507120]: Stopped target Basic System. + Jan 18 09:23:31 systemd[2507120]: Stopped target Paths. + Jan 18 09:23:31 systemd[2507120]: Stopped target Sockets. + Jan 18 09:23:31 systemd[2507120]: Stopped target Timers. + Jan 18 09:23:31 systemd[2507120]: Closed D-Bus User Message Bus Socket. + Jan 18 09:23:31 systemd[2507120]: Closed GnuPG network certificate management daemon. + Jan 18 09:23:31 systemd[2507120]: Closed GCR ssh-agent wrapper. + Jan 18 09:23:31 systemd[2507120]: Closed GNOME Keyring daemon. + Jan 18 09:23:31 systemd[2507120]: Closed GnuPG cryptographic agent and passphrase cache (access for web browsers). + Jan 18 09:23:31 systemd[2507120]: Closed GnuPG cryptographic agent and passphrase cache (restricted). + Jan 18 09:23:31 systemd[2507120]: Closed GnuPG cryptographic agent (ssh-agent emulation). + Jan 18 09:23:31 systemd[2507120]: Closed GnuPG cryptographic agent and passphrase cache. + Jan 18 09:23:31 systemd[2507120]: Closed p11-kit server. + Jan 18 09:23:31 systemd[2507120]: Closed PipeWire Multimedia System Sockets. + Jan 18 09:23:31 systemd[2507120]: Closed Sound System. + Jan 18 09:23:31 systemd[2507120]: Removed slice User Application Slice. + Jan 18 09:23:31 systemd[2507120]: Reached target Shutdown. + Jan 18 09:23:31 systemd[2507120]: Finished Exit the Session. + Jan 18 09:23:31 systemd[2507120]: Reached target Exit the Session. +``` +- [System hangs on no input after "Reached system target shutdown"](https://www.reddit.com/r/archlinux/comments/16jimr2/system_hangs_on_no_input_after_reached_system/)`相近问题` + + + +************************ +> 崩溃,切换TTY5登录后终端疯狂输出日志无响应 + +Used+Cached高内存, `journalctl -b -1`查日志 +```log + Jan 29 11:28:49 zk-pc systemd[1]: Started Getty on tty5. + Jan 29 11:28:53 zk-pc dbus-daemon[668]: [system] Activating via systemd: service name='org.freedesktop.home1' unit='dbus-org.freedesktop.home1.service' requested by ':1.3512' (uid=0 pid=3819> + Jan 29 11:28:54 zk-pc dbus-daemon[668]: [system] Activation via systemd failed for unit 'dbus-org.freedesktop.home1.service': Unit dbus-org.freedesktop.home1.service not found. + Jan 29 11:28:57 zk-pc login[3819083]: pam_unix(login:session): session opened for user zk(uid=1000) by zk(uid=0) + Jan 29 11:29:01 zk-pc systemd-logind[671]: New session 279 of user zk. + Jan 29 11:29:01 zk-pc systemd[1]: Started Session 279 of User zk. + Jan 29 11:29:03 zk-pc login[3819083]: LOGIN ON tty5 BY zk + Jan 29 11:29:05 zk-pc kernel: general protection fault, probably for non-canonical address 0xff00000000000010: 0000 [#1] PREEMPT SMP NOPTI + Jan 29 11:29:06 zk-pc kernel: CPU: 8 PID: 3807890 Comm: Storage Diagnos Not tainted 5.15.139-1-MANJARO #1 096934fa2aab193b2a40cf54023e5b05e5276eb2 + Jan 29 11:29:07 zk-pc systemd-journald[375]: Missed 364 kernel messages + Jan 29 11:29:07 zk-pc kernel: ---[ end trace 56f99a5ae8056f6e ]--- + Jan 29 11:29:08 zk-pc systemd-journald[375]: Missed 910 kernel messages + Jan 29 11:29:08 zk-pc kernel: RSP: 002b:00007f6803a98590 EFLAGS: 00010217 + Jan 29 11:29:10 zk-pc systemd-journald[375]: Missed 1440 kernel messages + Jan 29 11:29:10 zk-pc kernel: RSP: 0000:ffff9ee142597c30 EFLAGS: 00010246 +``` + +```log + Mar 19 14:46:19 zk-pc sudo[2511882]: pam_unix(sudo:session): session opened for user root(uid=0) by zk(uid=1000) + Mar 19 14:46:19 zk-pc sudo[2511882]: pam_unix(sudo:session): session closed for user root + Mar 19 14:46:25 zk-pc dbus-daemon[584]: [system] Activating via systemd: service name='org.freedesktop.home1' unit='dbus-org.freedesktop.home1.service' requested by ':1.2243' (uid=0 pid=2512> + Mar 19 14:46:25 zk-pc dbus-daemon[584]: [system] Activation via systemd failed for unit 'dbus-org.freedesktop.home1.service': Unit dbus-org.freedesktop.home1.service not found. + Mar 19 14:46:25 zk-pc sudo[2512066]: zk : TTY=pts/47 ; PWD=/home/zk/Work/tg-assets-backend ; USER=root ; COMMAND=/bin/lsof -p 2453453 + Mar 19 14:46:25 zk-pc sudo[2512066]: pam_unix(sudo:session): session opened for user root(uid=0) by zk(uid=1000) + Mar 19 14:46:25 zk-pc sudo[2512066]: pam_unix(sudo:session): session closed for user root + Mar 19 14:48:49 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. + Mar 19 14:48:49 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. + Mar 19 14:49:33 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. + Mar 19 14:49:33 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. + Mar 19 14:49:33 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. + Mar 19 14:49:33 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. + Mar 19 14:49:33 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. + Mar 19 14:49:33 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. + Mar 19 14:51:35 zk-pc systemd-journald[375]: Under memory pressure, flushing caches. + Mar 19 14:51:45 zk-pc systemd-journald[375]: Under memory pressure, flushing caches. + Mar 19 14:51:46 zk-pc sshd[2517295]: ssh_dispatch_run_fatal: Connection from 192.168.131.11 port 9778: Broken pipe [preauth] + Mar 19 14:53:45 zk-pc sshd[2517303]: fatal: Timeout before authentication for 192.168.131.11 port 9791 + Mar 19 14:53:51 zk-pc rtkit-daemon[1023]: Supervising 7 threads of 6 processes of 1 users. +``` \ No newline at end of file diff --git a/Linux/Base/LinuxStreamEditor.md b/Linux/Base/LinuxStreamEditor.md index 58c6be3..c377553 100644 --- a/Linux/Base/LinuxStreamEditor.md +++ b/Linux/Base/LinuxStreamEditor.md @@ -1,59 +1,145 @@ -`目录 start` - -- [流编辑器](#流编辑器) - - [grep](#grep) - - [tr](#tr) - - [cut](#cut) - - [paste](#paste) - - [sed](#sed) - - [awk](#awk) - -`目录 end` |_2018-08-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux流编辑器 +date: 2018-12-15 11:17:35 +tags: + - 工具 +categories: + - Linux +--- + +💠 + +- 1. [流编辑器](#流编辑器) + - 1.1. [tr](#tr) + - 1.2. [colrm](#colrm) + - 1.3. [cut](#cut) + - 1.4. [paste](#paste) + - 1.5. [sed](#sed) + - 1.6. [awk](#awk) + +💠 2024-09-20 11:10:09 **************************************** # 流编辑器 -> [参考博客: 比较linux下各种流编辑器的用法](https://blog.csdn.net/havedream_one/article/details/45007449) - -## grep -> g (globally) search for a re (regular expression ) and p (print ) the results. +> [参考: 比较linux下各种流编辑器的用法](https://blog.csdn.net/havedream_one/article/details/45007449) ## tr > 转换字符 -- 替换:可以使用字符集的形式。如tr [a-z] [A-Z] 或者 tr a-z A-Z -- 压缩:-s 如echo “you are a man ”|tr -s ' ' ' ' 结果you are a man -- 删除:-d 如echo "you are a man"| tr -d ' '结果youareaman +- 替换:可以使用字符集的形式如tr `[a-z]` `[A-Z]` 或者 tr a-z A-Z +- 压缩:-s 如 `echo “you are a man ” | tr -s ' ' ' '` + - 结果 `you are a man` +- 删除:-d 如 `echo "you are a man"| tr -d ' ' ` + - 结果 `youareaman` +- 合并多行 `ps aux | grep firefox | grep -v PID | awk '{print $2}' | tr '\n' ','` + +## colrm ## cut > man cut +md5sum | cut -c 1-8 + ## paste -> 粘贴,也就是合并文件用的 +> 粘贴,合并文件用 + 使用制表符来合并多个文件对应的行,也可以使用 -d 指定合并符 -实例: -默认制表符 -paste p3.txt p2.txt p1.txt -指定 -paste -d ‘*‘ p3.txt p2.txt p1.txt -so,也不需要和其他的对比了,其他都是拆分,而paste是合并。 + +实例: 默认制表符 paste p3.txt p2.txt p1.txt + +指定 paste -d ‘*‘ p3.txt p2.txt p1.txt ## sed -> 组成模式: `参数 命令 文件` - -- `参数` - - `-n` 直接在控制台输出的操作的结果,源文件不变 - - `-i` 在源文件中进行修改 -- `命令` - - p 打印 `sed -n Np 文件名` - - a 新增 在下一行 - - i 插入 在上一行 将hello插入到第4行:`sed -in "4i hello" test.md` - - c 替换 整行 - - s 替换 字符串的替换 - - d 删除 行级别, 删除2-4行 `sed -i "2,4d" test.md` - -> 1. 截取指定行数到新文件 `sed -n ‘开始行数,结束行数p’ info.log > newFile.log` -> 2. 修改配置文件中name的值为123 `sed -i "s/name=.*/name=123/g" config.conf` - -> [参考博客: linux sed 命令单行任务快速参考](http://www.techug.com/post/linux-sed1line.html) +> 使用方式: `操作类型 命令 文件` | sed --help 查看详细 + +`操作类型` +- `-n` suppress automatic printing of pattern space +- `-e` 只在控制台输出的操作的结果内容(全部),源文件不变 **缺省选项** +- `-i` 直接在源文件中进行修改 +- `-f file` 执行一个 sed 脚本文件中的指令 + +`命令` + +| 命令 | 效果 | +|:----:|:----| +| b | label 将执行的指令跳至由 : 建立的参考位置 +| d | 删除行 删除2-4行 `sed -i "2,4d" file` +| D | 删除 pattern space 内第一个 newline 字母 前的资料 +| g | 拷贝资料从 hold space +| G | 添加资料从 hold space 至 pattern space +| h | 拷贝资料从 pattern space 至 hold space +| H | 添加资料从 pattern space 至 hold space +| l | 印出 l 资料中的 nonprinting character 用 ASCII 码 +| a | 新增 在下一 +| i | 插入添加使用者输入的行 将hello插入到第4行:`sed -in "4i hello" test.md` +| n | 读入下一笔资料 +| N | 添加下一笔资料到 pattern space +| p | 打印 `sed -n 1p file` +| P | 印出 pattern space 内第一个 newline 字母 前的资料 +| q | 跳出 sed 编辑 +| r | 读入它档内容 +| w | 写资料到它档内 +| x | 交换 hold space 与 pattern space 内容 +| y | 转换(transform)字元 + +- c 替换 整行 +- s 替换 行内字符串的替换 命令结构: `'s/pattern/relacement/flags'` + - 当替换内容含 / 时需要转义,或者更换分隔符为@ 即 `s@ab@12/g` + - pattern 是正则的 pattern 写法 + - **注意会匹配到首尾的空字符** `echo abc | sed 's/a*/l/g'` 会输出lblcl + - 分组匹配 `sed 's/\([0-9]\)\s/\1,/g' ` + - 边界符 `\b` 代表空格字符 + - 正则匹配 `sed 's/\b[0-9]\{2\}\b/number/g` 注意 {}\的转义 , 不支持\d \A \Z 等。 + + - replacement 是需要替换成的内容 + - 此处可以使用&表示匹配到的内容,例如对a字符后追加b字符 `sed 's/a/&b/g'` + - flags 是动作(可以为空) + - 数值: 一行中的第几处符合 pattern 将被替换 + - g : 全部替换 ; `3g` 第三处到最后 + - p : 输出修改的行内容 + - w filename : 替换后的文件写入到新文件 + - I : 忽略大小写; 例如 `gI` + +- 多指令执行 + - `sed 's/a//g;s/b//g'` + - `sed -e '' -e ''` + +>1. 截取指定行数到新文件 `sed -n ‘开始行数,结束行数p’ info.log > newFile.log` +>1. 文件内容倒置 `sed -i '1!G;h;$!d' filename` +>1. 新增内容到第一行 `sed -i '1 i\any text' file` + +>1. 修改配置文件中name的值为123 `sed -i "s/name=.*/name=123/g" config.conf` +>1. 修改第3行 `sed -i '3 s/name/1/g'` + +>1. 匹配行的行尾追加 `sed -i 's/end.*/& ;/g' file` +>1. 匹配行后第三行行尾追加`sed -i '/gradle/{n;n;n; s/.*/& 6.0/;}' file` +>1. CRLF -> LF `sed -i 's/\r//g' file` + > 配合 git 使用: `git ls-files| xargs sed -i 's/\r//g'` +>1. 注意特殊字符的转义 `git ls-files | xargs sed -i 's/@a.*/\//g'` +>1. 去除换行符 `sed -i ':label;N;s/\n/ /;b label'` [参考](http://www.cnblogs.com/lykm02/p/4479098.html) +>1. 处理管道流 `echo syx is a good body | sed 's/syx/zsf/'` + +- [Linux Sed 教程:有趣的 Sed 替换示例](https://bbs.huaweicloud.com/blogs/325484) +- [参考: linux sed 命令单行任务快速参考](http://www.techug.com/post/linux-sed1line.html) +- [sed 正则的精确控制](http://wiki.jikexueyuan.com/project/shell-learning/sed-accurate-control-of-regular.html) + - `echo Tolstoy is worldly | sed 's/T.*y/Camus/'` 这里的 pattern 就有问题, 会把整行替换掉 + - `echo Tolstoy is worldly | sed 's/T[a-z]*y/Camus/'` 只把第一个单词替换 + +************************ +> [sokoban sed](https://github.com/aureliojargas/sokoban.sed) `sed 写的推箱子游戏` + +************************ + ## awk -> awk有3个不同版本: awk、nawk和gawk,未作特别说明,一般指gawk,gawk 是 AWK 的 GNU 版本。 +> awk有多个不同版本: awk、mawk nawk和gawk,若未作特别说明,通常指gawk (gawk 是 AWK 的 GNU 版本) + +1. 输出指定列 `cat log.log | awk '{print $2}'` + 1. 忽略第一列:`awk '{$1="";print $0}'` + 1. 忽略1到4: `awk '{ for(i=1; i<=4; i++){ $i="" }; print $0 }'` +1. 按列求和 `awk '{sum += $1};END {print sum}'` +1. 添加行号 `awk '{printf("%2d %s\n", NR, $0);` +1. 读取标准输出 `awk '{print $0}' - ` + +> [参考: awk 入门教程](http://www.ruanyifeng.com/blog/2018/11/awk.html) +- 别名中双引号转义问题处理: `awk '\''{}'\'` + - `alias count.csv='sort | uniq -c | sort -hr | awk '\''{print $1","$2}'\'` \ No newline at end of file diff --git a/Linux/Base/LinuxUI.md b/Linux/Base/LinuxUI.md new file mode 100644 index 0000000..e81a95a --- /dev/null +++ b/Linux/Base/LinuxUI.md @@ -0,0 +1,158 @@ +--- +title: LinuxUI +date: 2024-02-03 10:39:52 +tags: +categories: +--- + +💠 + +- 1. [GUI](#gui) + - 1.1. [Display Manager](#display-manager) + - 1.2. [Window Manager](#window-manager) + - 1.3. [Desktop environment](#desktop-environment) +- 2. [UI](#ui) + - 2.1. [Font](#font) + - 2.1.1. [字体渲染](#字体渲染) + - 2.2. [Theme](#theme) + - 2.3. [Icon](#icon) + - 2.4. [Terminal](#terminal) + - 2.4.1. [彩色输出](#彩色输出) + - 2.4.1.1. [ls配置彩色输出](#ls配置彩色输出) + +💠 2024-10-15 09:56:12 +**************************************** + +# GUI +> [GUI Under Linux | Baeldung on Linux](https://www.baeldung.com/linux/gui) + +## Display Manager +- [LightDM](https://wiki.archlinux.org/title/LightDM) + +> [Computer instantly wakes after suspending](https://forums.linuxmint.com/viewtopic.php?t=408260) + +`sudo systemctl restart display-manager` + +## Window Manager +- xfwm4 `XFCE4` +- compiz + +## Desktop environment +> [Desktop environment](https://wiki.archlinux.org/title/desktop_environment) + +1. [Gnome](/Linux/DE/Gnome.md) +2. [Xfce](/Linux/DE/Xfce.md) + + +************************ + +- [Top 10 Best Linux Docks 2022](https://www.digitalocean.com/community/tutorials/top-best-linux-docks-2020) + - plank + + +************************ + +# UI + +> Linux UI: themes icons fonts + +## Font + +1. /usr/share/fonts/ +2. ~/.local/share/fonts + +- 刷新字体缓存 `fc-cache -fv` + - 注意还有一个32位命令 fc-cache-32 + +> [字体文件 详情](/FrontEnd/Font.md) + +- npm vue minikube 等命令行的工具输出的日志提示会包含emoji, 需要终端字体支持展示unicode + - 终端内 Emoji 支持 [emoji](https://blog.sebastian-daschner.com/entries/linux-terminal-font-alacritty-jetbrains-mono-emoji) `noto-color-emoji 字体 支持颜色` + +### 字体渲染 + +> [Debian8安装Infinality改善字体渲染,安装Ubuntu字体](https://www.linuxdashen.com/debian8%E5%AE%89%E8%A3%85infinality%E6%94%B9%E5%96%84%E5%AD%97%E4%BD%93%E6%B8%B2%E6%9F%93%EF%BC%8C%E5%AE%89%E8%A3%85ubuntu%E5%AD%97%E4%BD%93) +> [一条命令搞定Linux字体渲染](https://www.lulinux.com/archives/278) +> [Font Configuration/Chinese (简体中文)](https://wiki.archlinux.org/index.php/Font_Configuration/Chinese_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) +> [参考: Fcitx (简体中文)](https://wiki.archlinux.org/index.php/Fcitx_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) + + +字体库 + +[nerd fonts](https://www.nerdfonts.com/) + +************************ + +## Theme + +1. /usr/share/themes/ 系统级 +2. ~/.themes/ ~/.local/share/themes 用户级 + +> [McMojave](https://www.xfce-look.org/p/1275087/) + +- [Github:vimix](https://github.com/vinceliuice/vimix-gtk-themes) `material design theme` + +> [参考: 10 Great Linux GTK Themes For 2018 ](https://www.maketecheasier.com/gtk-themes-for-linux/) + +- [catppuccin](https://github.com/catppuccin/catppuccin) +- [pingguo](https://www.gnome-look.org/p/1239453/) +- [Sierra](https://www.gnome-look.org/p/1013714/) +- [GTK3主题:OSX-Arc](https://www.linuxidc.com/Linux/2017-01/139053.htm) + +- 某个应用运行时使用指定主题: `GTK_THEME=xxx COMMAND` + - 例如 `GTK_THEME=vimix-light-doder /Apps/IDE/mat/MemoryAnalyzer` + +************************ + +## Icon + +1. /usr/share/icons + - 例如: `/usr/share/icons/Papirus/128x128/apps/` + +> sudo apt search icon-theme 也能看到很多icon + +1. Halo-icon-theme + +## Terminal + +### 彩色输出 + +> [参考博客,比较详细](http://blog.csdn.net/magiclyj/article/details/72637666) +> [Linux Terminal Color](https://blog.csdn.net/y2701310012/article/details/40142809) + +```sh + red='\033[0;31m' + green='\033[0;32m' + yellow='\033[0;33m' + blue='\033[0;34m' + purple='\033[0;35m' + cyan='\033[0;36m' + white='\033[0;37m' + default='\033[0m' +``` + +> 256 color + +```sh + # 测试 terminal 是否支持 256 + for i in {0..255} ; do + printf "\x1b[48;5;%sm%3d\e[0m " "$i" "$i" + if (( i == 15 )) || (( i > 15 )) && (( (i-15) % 6 == 0 )); then + printf "\n"; + fi + done +``` + +#### ls配置彩色输出 + +[Gihub: LS_COLORS](https://github.com/trapd00r/LS_COLORS)[customize bash prompt](https://www.howtogeek.com/307701/how-to-customize-and-colorize-your-bash-prompt/) + +1. `curl https://raw.githubusercontent.com/trapd00r/LS_COLORS/master/LS_COLORS -o /etc/lscolor-256color` +2. 追加到 `*sh.rc` + ```sh + if [[ ("$TERM" = *256color || "$TERM" = screen* || "$TERM" = xterm* ) && -f /etc/lscolor-256color ]]; then + eval $(dircolors -b /etc/lscolor-256color) + else + eval $(dircolors) + fi + ``` diff --git a/Linux/Base/ReleaseExperience.md b/Linux/Base/ReleaseExperience.md index c0143fc..5bc1936 100644 --- a/Linux/Base/ReleaseExperience.md +++ b/Linux/Base/ReleaseExperience.md @@ -1,33 +1,45 @@ -`目录 start` - -- [Linux各个发行版本使用体验](#linux各个发行版本使用体验) - - [基础知识](#基础知识) - - [服务器系统之争](#服务器系统之争) - - [Debian系](#debian系) - - [Debian](#debian) - - [Ubuntu](#ubuntu) - - [Ubuntu Mint](#ubuntu-mint) - - [Deepin](#deepin) - - [关于显卡](#关于显卡) - - [双系统安装](#双系统安装) - - [raspberry-pi](#raspberry-pi) - - [arch系](#arch系) - - [manjaro](#manjaro) - - [redhat系](#redhat系) - - [Fedora](#fedora) - - [Centos](#centos) - - [openSUSE](#opensuse) - - [FreeBSD](#freebsd) - - [Solaris](#solaris) - - [alpine](#alpine) - - [Gentoo](#gentoo) - - [Mageia](#mageia) - - [CDLinux](#cdlinux) - -`目录 end` |_2018-09-02_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Linux发行版 +date: 2018-12-15 11:19:21 +tags: + - 工具使用经验 +categories: + - Linux +--- + +💠 + +- 1. [Linux常见发行版](#linux常见发行版) + - 1.1. [基础知识](#基础知识) + - 1.1.1. [安装系统](#安装系统) + - 1.2. [服务器系统之争](#服务器系统之争) + - 1.3. [Debian](#debian) + - 1.3.1. [Debain](#debain) + - 1.3.2. [Ubuntu](#ubuntu) + - 1.3.3. [Ubuntu Mint](#ubuntu-mint) + - 1.3.4. [Deepin](#deepin) + - 1.3.5. [Raspberry-pi](#raspberry-pi) + - 1.4. [Arch](#arch) + - 1.4.1. [Arch](#arch) + - 1.4.2. [Manjaro](#manjaro) + - 1.5. [RedHat](#redhat) + - 1.5.1. [Fedora](#fedora) + - 1.5.2. [Centos](#centos) + - 1.5.3. [openSUSE](#opensuse) + - 1.6. [FreeBSD](#freebsd) + - 1.7. [Solaris](#solaris) + - 1.8. [Alpine](#alpine) + - 1.9. [Gentoo](#gentoo) + - 1.10. [Mageia](#mageia) + - 1.11. [CDLinux](#cdlinux) + - 1.12. [Chromium OS](#chromium-os) + - 1.12.1. [Chrome OS](#chrome-os) + - 1.12.2. [Fyde OS](#fyde-os) + +💠 2024-09-09 10:34:58 **************************************** -# Linux各个发行版本使用体验 -> 一边用一边记录吧 +# Linux常见发行版 +> [Repology](https://repology.org/) > [发行版热度对比](https://distrowatch.com/dwres.php?resource=popularity) > [Linux的发行版本及不同版本的联系和区别。](https://www.jianshu.com/p/c88a62ac8ca3?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation) @@ -35,105 +47,71 @@ > [linux 的不同的发行版区别和联系](https://www.jianshu.com/p/b796ead65995?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation) - [谈 Linux,Windows 和 Mac](http://www.yinwang.org/blog-cn/2013/03/07/linux-windows-mac) -## 基础知识 -> 下载安装时要选平台 参考[相关博客](http://downtoearthlinux.com/posts/x86-i386-x86-64-x64-and-amd64-oh-my/) -``` - x86-64 = 64-bit = x64 = amd64 - x86 = 32-bit = i386 -``` - -_查看发行版_ -- `lsb_release -a` [查看更多方式](/Linux/Linux_File.md#查看发行版) -## 服务器系统之争 -> [服务器操作系统应该选择 Debian/Ubuntu 还是 CentOS?](https://www.zhihu.com/question/19599986) -> [CentOS vs CoreOS – Which OS to choose for your Docker web hosting services](https://bobcares.com/blog/centos-vs-coreos-os-for-docker-web-hosting/2/) - -> 2018-04-01 17:17:19 -> 个人来讲, 菜鸡一个,但是习惯了Ubuntu16,也尝试过centos7, 还行之匆匆的两个服务器都装了centos, 然后俩都出问题了,都不想去百度找解决方案了,正常操作都能报错? 很棒棒 -> 正在尝试Debian8 还是debian系习惯了 - -## Debian系 -### Debian -> 很古老但是很好用的系统 [官网](https://www.debian.org/index.zh-cn.html) - -> [参考博客: Debian8最小安装](https://www.howtoforge.com/tutorial/debian-8-jessie-minimal-server/) -- 奇怪的是我在虚拟机里装了好几个好几次装不上, 装完一登录就只有壁纸 -_服务器_ -- 2018-04-01 17:19:50 作为服务器系统安装完Debian8.2 85M内存占用 docker 是1.6 -- 2018-04-10 10:35:54 服务器安装Ubuntu16.04 71M内存 docker是1.13 +> [distrochooser](https://github.com/distrochooser/distrochooser)`通过问答给出推荐的发行版` -### Ubuntu -> 很多人的入门系统, 作为个人服务器也是首选, 软件比较新 - -> [Ubuntu Server Tutorial](https://tutorials.ubuntu.com/tutorial/tutorial-install-ubuntu-server#0) | [网易镜像源](http://mirrors.163.com/ubuntu-releases/)`只有网易有server版的镜像` - -### Ubuntu Mint -> 作为桌面版系统, 该有的都有了, 个人比较喜欢 +## 基础知识 +> 下载安装时要选平台 [相关博客](http://downtoearthlinux.com/posts/x86-i386-x86-64-x64-and-amd64-oh-my/) +>> 64: x86-64 = x64 = amd64 +>> 32: x86 = i386 -### Deepin -> [官方wiki](wiki.deepin.org) +> [查看发行版](/Linux/Base/LinuxDirectoryStructure.md#查看发行版) -- 优点: - - 界面美观,自带CrossOver深度家族的软件也挺好用,自定义命令的快捷键 -- 缺点: - - 基本是Linux的共性了,就是驱动问题, NVIDIA 显卡 因为驱动问题重装四五次系统,重启就不知道多少次了 - - 输入法现在这几天也在作妖 fcitxCPU占用高,输入窗口消失等问题 - - 蓝牙模块时隐时现 +### 安装系统 +> 制作U盘启动盘 -`遇到的bug记录` -- 2018-01-09 19:29:25 休眠结束系统卡死,然后重启输入法没有窗口,然后升级到最新重启还是没有,杀掉搜狗进程再启动解决 +- Manjaro + - rufus: 在windows上制作, 选用dd模式 +- Deepin + 1. 系统内置的 启动盘制作工具, 或者官网下Windows版 + 2. 或者用软碟通 +- Ubuntu + 1. 软碟通 -- 2018-03-15 09:25:47 [公司电脑安装Windows10 和 Deepin双系统](/MyBlog/2018-3-15-install-deepin.md) +> 系统安装 +- 现在大多电脑都是预装win10, 所以为了方便, 双系统更好用 +1. 首先一点就是引导模式 现在大多是 UEFI, 所以为了不影响 windows, 关闭 UEFI, 使用 Legacy模式安装Linux 这样的话, 打开UEFI就进了Windows 关闭就进了Linux 对Windows没造成任何影响 +1. 在Windows上 我的电脑-> 硬盘管理 -> 选择一个分区,压缩出空闲空间出来 用于安装Linux(日常用最少80g 尽管系统最低占用大概10g左右) +1. 将U盘插上, 进入系统安装的引导, 选好语言, 用户 密码什么的 +1. 分区 分为 / 和 /home 就行了, / 40g 其余给/home (个人分100g才够用) 千万注意不要选错分区 +1. 引导会自动追加到硬盘引导分区, 不会覆盖原有系统, 目前 manjaro deepin windows10 三系统双硬盘并存 +1. 安装完成, 重启前拔掉U盘 即可 -- 2018-05-24 15:08:49 `Gtk-WARNING **: 无法在模块路径中找到主题引擎:“adwaita”`, 安装 这个包 gnome-themes-standard +> [参考: 迁移到 GRUB 2](https://www.ibm.com/developerworks/cn/linux/l-grub2/) -- 2018-06-15 19:50:40 deepin-wm 进程, 也就是Deepin的桌面管理器, 启动久了之后就会发生内存占用非常大的情况, 关闭窗口特效, 再打开就好了 +> UEFI | [Doc](https://wiki.manjaro.org/index.php?title=UEFI_-_Install_Guide) -- 2018-08-21 20:34:07 更新到15.7, 然后就是一堆的小问题, 任务栏和屏幕边缘有空隙, 多任务切换方式的变化, 原先用Wine安装的企业QQ不能启动... 但是确实Deepin 现在更快了 - - 使用闭源驱动方案, 休眠一会就卡死了, 只能强制关机, 尝试了开源驱动后, 也是一样 显卡是 Nvidia GTX1050 +## 服务器系统之争 +> [服务器操作系统应该选择 Debian/Ubuntu 还是 CentOS?](https://www.zhihu.com/question/19599986) +> [CentOS vs CoreOS – Which OS to choose for your Docker web hosting services](https://bobcares.com/blog/centos-vs-coreos-os-for-docker-web-hosting/2/) -- 2018-08-23 09:55:15 遭遇用过的最大问题, 笔记本升级到15.7后有显卡明显不兼容, 各种显示上的卡顿, 切换Prime解决方案后, 内核load不进来, 启动不了了 - - 配置是 显卡 NVIDIA 840m 也许重装Deepin15.7, 也许装Manjaro-KDE - - 最终是进的恢复模式, 卸载了无用的包就成功进入了, 但是发现自动挂载分区的文件都被注释了, 如果手动添加, 即使mount -a 没有报错, 但是启动时就加载不了分区 - - 又得进恢复模式注释掉, 才能进入系统 +********************************************** -- 2018-09-02 21:44:21 ` Driver 'pcspkr' is already registered, aborting,` - >- [参考博客: 社区帖子](https://bbs.deepin.org/forum.php?mod=viewthread&tid=166517&highlight=pcspkr) - +## Debian +> Debian系 包含 Debian *buntu Deepin 等等 -#### 关于显卡 -> [参考博客: 显卡驱动作死录](https://www.jianshu.com/p/f53c8223bac6) +个人经历,大版本号升级时不稳定,升级失败或者搞挂系统 -> 个人折腾的整理 -当前系统为 Deepin15.7 已经支持多种解决方案了, 还有一个 `深度显卡驱动管理器` -1. Intel默认驱动(也就是集显) -1. NVIDIA开源驱动 性能不好, 解析闭源驱动而来 -1. 大黄蜂方案 采用闭源驱动, 省电 -1. PRIME方案 高性能 +### Debain +[Debian](/Linux/Debian/Debian.md) -但是和我笔记本完美兼容的是 大黄蜂方案, 也就是之前安装的 `nvidia-driver`, `nvidia-setting`, `bumblebee-nvidia` 这一系列包 -PRIME方案切换后差点把内核挂了, 一顿瞎操作把系统救活了 +### Ubuntu +[Ubuntu](/Linux/Debian/Ubuntu.md) -#### 双系统安装 -- 首先进入BIOS关闭 安全启动, 选择引导方式为Legacy关闭UEFI win8以上则要关闭快速启动, - - 制作启动U盘, 然后选择从U盘启动, 进行安装, 分区 / 和 /home / 30-40g就足够, 如果你所用的软件都习惯性解压运行的话 - - 安装完成后一般是Deepin的默认引导取代了winsows引导, 即可正常使用, 进入windows,Deepin的引导也有该入口 - - 如果想默认进windows, 那么修改BIOS 改回UEFI即可 +### Ubuntu Mint +> 作为桌面版系统, 基于Ubuntu, 使用更为简单 也有更多的窗口管理器可以选择 -- 固态加机械的电脑: - - 一样的关闭 安全启动, UEFI - - 在固态中划分出300M左右的空间出来, 在安装的时候设为 /Boot 然后将 / 和 /home照常放在机械上即可 - - 在启动时, 打开引导菜单, 选择固态即可正常启动Deepin - - 同样的修改BIOS 回 UEFI 就默认进WIndows了 +### Deepin +[Deepin](/Linux/Debian/Deepin.md) -> 但是有时候有的电脑打开UEFI也能正常安装, 所以装系统要大胆的尝试, Deepin安装没有造成过数据损失 +### Raspberry-pi +> [Official](https://www.raspberrypi.org) -### raspberry-pi - [树莓派桌面版下载](https://www.raspberrypi.org/downloads/raspberry-pi-desktop/) `分辨率不知道怎么调, 资源的消耗倒是低` ****************** -## arch系 +## Arch +### Arch > 滚动发行,包管理机制优秀 - [打造完美的 Linux 桌面 — Arch Linux 2007.08-2 (1)](https://linuxtoy.org/archives/the-perfect-linux-desktop-arch-linux-2007-08-2-1.html) @@ -141,7 +119,7 @@ PRIME方案切换后差点把内核挂了, 一顿瞎操作把系统救活了 - [打造完美的 Linux 桌面 — Arch Linux 2007.08-2 (3)](https://linuxtoy.org/archives/the-perfect-linux-desktop-arch-linux-2007-08-2-3.html) - [打造完美的 Linux 桌面 — Arch Linux 2007.08-2 (4)](https://linuxtoy.org/archives/the-perfect-linux-desktop-arch-linux-2007-08-2-4.html) -### manjaro +### Manjaro > [官网](https://manjaro.org/community-editions/) > [人生苦短我用Manjaro](https://www.manjaro.cn/451) | [什么Linux发行版软件最多?](https://www.lulinux.com/archives/2787) > | [Manjaro: 一种不同的野兽](https://www.manjaro.cn/195) | [为什么要用Manjaro?](https://www.manjaro.cn/150) @@ -150,16 +128,16 @@ PRIME方案切换后差点把内核挂了, 一顿瞎操作把系统救活了 - 因为滚动更新的特性, 所以在安装一个新软件的时候, 需要更新到最新版, 这样就比较烦, **************************** -## redhat系 -> 大厂支持 +## RedHat +> 大厂支持 侧重于服务器领域 + ### Fedora > redhat的试验场 不太感冒 ### Centos -> 在阿里云上装了一个, 开机82M Centos7.4 然后装个nginx就挂了 稳定? -> 不管,就是要黑一波, 命令都没有提示 ### openSUSE + ************************ ## FreeBSD @@ -167,8 +145,8 @@ PRIME方案切换后差点把内核挂了, 一顿瞎操作把系统救活了 ## Solaris ********************** -## alpine -> 特别小,在docker中使用有优势,但是坑多 +## Alpine +> 特别小,在docker中使用有优势 ## Gentoo > 入门难度大,适合资深玩家,据说是特能折腾的系统,处于鄙视链顶端 @@ -178,3 +156,11 @@ PRIME方案切换后差点把内核挂了, 一顿瞎操作把系统救活了 ## CDLinux > 小巧的Linux发行版, 带有很多工具 + +## Chromium OS +> [Chromium OS](https://www.chromium.org/chromium-os) + +### Chrome OS + +### Fyde OS +> [Official](https://fydeos.com) `基于 Chromium OS` diff --git a/Linux/Base/SSH.md b/Linux/Base/SSH.md new file mode 100644 index 0000000..b864214 --- /dev/null +++ b/Linux/Base/SSH.md @@ -0,0 +1,233 @@ +--- +title: SSH使用总结 +date: 2018-12-15 11:20:07 +tags: + - 工具使用经验 +categories: + - Linux +--- + +💠 + +- 1. [SSH](#ssh) + - 1.1. [安装](#安装) + - 1.2. [建立连接](#建立连接) + - 1.2.1. [复制粘贴建立密钥对](#复制粘贴建立密钥对) + - 1.2.2. [使用 ssh-copy-id 脚本](#使用-ssh-copy-id-脚本) + - 1.3. [SSH客户端配置](#ssh客户端配置) + - 1.3.1. [多密钥对](#多密钥对) + - 1.4. [服务端配置](#服务端配置) + - 1.5. [访问图形化](#访问图形化) + - 1.6. [SSH登录并执行一系列命令](#ssh登录并执行一系列命令) + - 1.6.1. [通过SSH执行命令时的环境变量问题](#通过ssh执行命令时的环境变量问题) + - 1.7. [SSH Tunnel](#ssh-tunnel) +- 2. [Tips](#tips) + - 2.1. [保持SSH连接稳定](#保持ssh连接稳定) +- 3. [Mosh](#mosh) + +💠 2024-09-11 14:55:54 +**************************************** +# SSH +> Secure Shell + +> [Linux启动或禁止SSH用户及IP的登录](https://blog.csdn.net/linghe301/article/details/8211305) +> [ssh和ssh2之间的免密码登陆详解](http://blog.chinaunix.net/uid-26517277-id-4055228.html) +> [SSH原理与运用(一):远程登录](http://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html) +> [SSH原理与运用(二):远程操作与端口转发](http://www.ruanyifeng.com/blog/2011/12/ssh_port_forwarding.html) + +- 默认22端口登录系统`ssh user@host` | 指定端口登录 `ssh -p port user@host` +- 测试能否登录上 `ssh -T user@host` + +> `ssh -i 私钥绝对路径 user@host` 采用指定私钥登录(一般默认是`.ssh/id_rsa`) +>> 私钥文件必须是 600 权限 +>> 去除私钥的口令 `openssl rsa -in ~/.ssh/id_rsa -out ~/.ssh/id_rsa_new` _在GitForWindows里面虽然有openssl,但是这个命令却执行不了_ +>> `ssh-add 私钥` 添加私钥到OpenSSH的认证代理 + +使用密码方式免去密码登录 +>1. 安装sshpass [完整教程](https://linux.cn/article-8086-1.html) +>2. sshpass -p '密码' 后接正常的ssh命令 ssh user@host + +> ssh登录然后执行一系列命令, sudo会执行不了 需要加 -t 参数才行 + +****************** + +## 安装 +_客户端_ +- `sudo apt-get install openssh-client` +- 生成密钥对 `ssh-keygen` 可以设置密码,为了方便也可以全部采用默认(不安全) + +_服务端_ +- 安装:`sudo apt-get install openssh-server` +- 启动:`sudo /etc/init.d/ssh start` 或者 `service ssh start` +- 更改配置文件修改默认端口 `/etc/ssh/sshd_config` +- 查看对否启动sshd`ps -e |grep ssh` +- 关闭服务 `/etc/init.d/ssh stop` + +当新增用户testA并开启ssh登录时 + +- /etc/ssh/sshd_config 新增 AllowUsers testA +- chmod 700 /home/testA/.ssh +- chmod 600 /home/testA/.ssh/authorized_keys + +## 建立连接 +### 复制粘贴建立密钥对 +_客户端_ +- 进入.ssh文件夹下 `gedit id_rsa.pub` 然后复制该公钥内容 + - 或者 `cat ~/.ssh/id_rsa.pub | xclip -sel clip` 将文件复制到剪贴板 + - 或者 `cat ~/.ssh/id_rsa.pub | xsel -b` 也是文件复制到剪贴板 +- 在各种平台服务上添加这个公钥即可免密登录 + +_服务器端_ +- 进入.ssh文件夹下 `sudo vim authorized_keys` 粘贴客户端公钥内容 +- 更改文件权限 `sudo chmod 600 authorized_keys` + - 注意 该文件 group 和 other 位不能有 w 权限 + +### 使用 ssh-copy-id 脚本 +- 两方安装好软件 客户端生成好了秘钥对之后 +- 默认端口:`ssh-copy-id "username@host"` 输密码就可以了 +- 指定端口 `ssh-copy-id ”-p port username@host“` + - 或者:`ssh-copy-id " username@host" -p port` + +- 成功后 客户端登录 `ssh -p 22 username@ip` + - root用户一般需要修改: + - `/etc/ssh/sshd_confg` 文件中PermitRootLogin no 改为yes 重新启动ssh服务。 + +- 注意: + - 一个端口和IP如果之前记录过相关信息,然后服务器重装了系统或者别的原因, 修改了服务器秘钥 + - 再次连接新的系统按着提示来运行一条命令即可 + - 例如 `ssh-keygen -f "/home/kcp/.ssh/known_hosts" -R 120.78.154.52` + +## SSH客户端配置 +`~/.ssh/config` +``` + Host aliyun + HostName www.ttlsa.com + Port 22 + User root + IdentityFile ~/.ssh/id_rsa.pub + IdentitiesOnly yes +``` + +_参数解释_ +``` + HostName 指定登录的主机名或IP地址 + Port 指定登录的端口号 + User 登录用户名 + IdentityFile 登录的公/私钥文件 奇怪的是有时候用公有时候用私?? + IdentitiesOnly 只接受SSH key 登录 + PubkeyAuthentication +``` +- `ssh aliyun` 即可登录 但是要输入生成公钥时的密码, _方便多公钥的情况_ + - 如果生成公钥时_没有_设置密码就要错三次,然后输入用户密码, + + +### 多密钥对 +> [参考博客](http://blog.csdn.net/black_ox/article/details/17753943) + +1. `ssh-keygen` 生成SSH密钥对 在询问中输入新的文件名 +2. `ssh-add 私钥文件绝对路径` + - 若执行ssh-add时出现Could not open a connection to your authentication agent + - 就先执行 `ssh-agent bash` 对应自己的解释器环境 +3. 如上 创建配置文件 config + - 在git项目中使用别名:正常的项目,我们clone下来之后,origin对应的URL假设为: `git@git.:Rusher/helloworld` + - 现在需要做个改动,将git, 要换成rusher_gitlab: + - `git remote set-url origin git@rusher_gitlab:Rusher/helloworld` + - 如果是root用户的项目: + - `git remote set-url origin git@root_gitlab:root/helloworld` + +_config_ +``` + Host default + HostName github.com + User git + IdentityFile ~/.ssh/default_id_rsa.pub +``` +- 测试配置是否正确: `ssh -T git@default` + +## 服务端配置 +> 修改登录后的欢迎信息 /etc/motd + +一般需要重启ssh服务才生效`/etc/ssh/sshd_config` +```conf + #禁用密码验证 + PasswordAuthentication no + #启用密钥验证 + RSAAuthentication yes + PubkeyAuthentication yes +``` + +## 访问图形化 + +在`/etc/ssh/sshd_config`添加以下信息,然后重启ssh服务 +``` + X11Forwarding yes + X11DisplayOffset 10 +``` +- `ssh -X -p port user@host` 登录即可 + - 使用过一次,发现了严重的内存泄露,也不知道是什么原因 + +## SSH登录并执行一系列命令 +```sh + ssh user@host 'cmd \ + && cmd \' +``` + +### 通过SSH执行命令时的环境变量问题 +详细在于不同的shell中 Linux 环境变量加载的不同 + +- 简单方式: 手动加载环境变量 `ssh name@host "source ~/.bashrc && java -version"` + +## SSH Tunnel +> [Wiki: Tunneling protocol](https://en.wikipedia.org/wiki/Tunneling_protocol#Secure_Shell_tunneling) + +简单来说就是可以建立一个双工通道,实现内网穿透,正向代理 + +> [Is it normal to use an SSH tunnel to access a production database? ](https://www.reddit.com/r/learnrust/comments/11poo5h/is_it_normal_to_use_an_ssh_tunnel_to_access_a/) +> [How does reverse SSH tunneling work?](https://unix.stackexchange.com/questions/46235/how-does-reverse-ssh-tunneling-work/118650#118650) + +- 创建独立的代理用户 localUser 并生成ssh公私钥,公钥注册到自身ssh的authorized_keys中去 +- 本地转发 `ssh localUser@localHost -L localHost:localPort:remoteHost:remotePort` + - 在localHost上启动localPort, 当其他客户端连到localPort时,tcp流量会转发到remotePort上去 +- 关闭隧道时 exit 退出交互式命令行 注意`不能Ctrl D` 无法正常关闭 + +************************ + +# Tips +> 终端抛出`ssh_exchange_identification: Connection closed by remote host` 错误: +```sh + echo "PermitRootLogin without-password" >> /etc/ssh/sshd_config ;\ + echo "PermitRootLogin yes" >> /etc/ssh/sshd_config ;\ +``` +- 或者尝试 `echo "sshd: ALL" >> /etc/hosts.allow && service sshd restart` + +## 保持SSH连接稳定 +> man ssh_config + +服务端和客户端配置 +```conf + ServerAliveInterval 60 + ServerAliveCountMax 3 +``` +************************ + +> SSH: Could not load host key: /etc/ssh/ssh_host_rsa_key +- `/usr/bin/ssh-keygen -A` 生成所有方式的密钥对 + +************************ + +# Mosh +> [Official Site](https://mosh.org/) + +采用UDP协议实现, 对带宽需求更小, 但是设计上有很明显的优点和缺点... + +- 优点: 占用带宽小, 速度快, 无连接的 + - 会话的中断不会导致当前正在前端执行的命令中断,相当于你所有的操作都是在screen命令中一样在后台执行。 + - 会话在中断过后,不会立刻退出,而是启用一个计时器,当网络恢复后会自动重新连接,同时会延续之前的会话,不会重新开启一个。 +- 缺点: 连接是一次性的, 往往需要额外配置UDP端口 + +简易使用 +1. 服务端使用指定端口启动, `mosh-server -p 6005` 默认是随机在 60001-61000 + - 复制好启动输出的秘钥 +1. 客户端 `MOSH_KEY=秘钥 mosh-client ip port` + + diff --git a/Linux/Base/Ssh.md b/Linux/Base/Ssh.md deleted file mode 100644 index 53e6c92..0000000 --- a/Linux/Base/Ssh.md +++ /dev/null @@ -1,156 +0,0 @@ -`目录 start` - -- [SSH](#ssh) - - [1.安装软件](#1安装软件) - - [2.复制粘贴建立密钥对](#2复制粘贴建立密钥对) - - [2.使用脚本更简单](#2使用脚本更简单) - - [3.遇到的问题](#3遇到的问题) - - [4.SSH配置文件](#4ssh配置文件) - - [5.多密钥对](#5多密钥对) - - [6.访问图形化](#6访问图形化) - - [7.ssh登录并执行一系列命令](#7ssh登录并执行一系列命令) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# SSH -> [Linux启动或禁止SSH用户及IP的登录](https://blog.csdn.net/linghe301/article/details/8211305) -> [ssh和ssh2之间的免密码登陆详解](http://blog.chinaunix.net/uid-26517277-id-4055228.html) -> [SSH原理与运用(一):远程登录](http://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html) -> [SSH原理与运用(二):远程操作与端口转发](http://www.ruanyifeng.com/blog/2011/12/ssh_port_forwarding.html) - -- `ssh user@host` 默认22端口登录系统 -- `ssh -p port user@host` 指定端口登录 -- `ssh -T user@host` 测试能否登录上 - -> `ssh -i 私钥绝对路径 user@host` 采用指定私钥登录(一般默认是`.ssh/id_rsa`) ->> 私钥一定要是 600 权限 ->> 去除私钥的口令 `openssl rsa -in ~/.ssh/id_rsa -out ~/.ssh/id_rsa_new` _在GitForWindows里面虽然有openssl,但是这个命令却执行不了_ - -> 使用密码方式免去密码登录(因为一些奇怪的需求, 又想省事) ->> 1. 安装sshpass [完整教程](https://linux.cn/article-8086-1.html) ->> 2. sshpass -p '密码' 后接正常的ssh命令 ssh user@host - -> ssh登录然后执行一系列命令, sudo会执行不了 需要加 -t 参数才行 - - -## 1.安装软件 -_客户端安装软件_ -- `sudo spt-get install openssh-client` -- 生成密钥对 `ssh-keygen` 可以设置密码,为了方便也可以全部采用默认 - -_服务端安装软件_ -- 安装:`sudo apt-get install openssh-server` -- 启动:`sudo /etc/init.d/ssh start` 或者 `service ssh start` -- 更改配置文件修改默认端口 `/etc/ssh/sshd_config` -- 查看对否启动sshd`ps -e |grep ssh` -- 关闭服务 `/etc/init.d/ssh stop` - -## 2.复制粘贴建立密钥对 -_客户端_ -- 进入.ssh文件夹下 `gedit id_rsa.pub` 然后复制该公钥内容 - - 或者 `cat ~/.ssh/id_rsa.pub | xclip -sel clip` 将文件复制到剪贴板 - - 或者 `cat ~/.ssh/id_rsa.pub | xsel -b` 也是文件复制到剪贴板 -- 在各种平台服务上添加这个公钥即可免密登录 - -_服务器端_ -- 进入.ssh文件夹下 `sudo vim authorized_keys` 粘贴客户端公钥内容 -- 更改文件权限 `sudo chmod 600 authorized_keys` 确保 其 group和other位没有 w 权限 - -## 2.使用脚本更简单 -- 两方安装好软件 客户端生成好了秘钥对之后 -- 默认端口:`ssh-copy-id "username@host"` 输密码就可以了 -- 指定端口 `ssh-copy-id ”-p port username@host“` - - 或者:`ssh-copy-id " username@host" -p port` - -- 成功后 客户端登录 `ssh -p 22 username@ip` - - root用户一般需要修改: - - `/etc/ssh/sshd_confg` 文件中PermitRootLogin no 改为yes 重新启动ssh服务。 - -- 注意: - - 一个端口和IP如果之前记录过相关信息,然后服务器重装了系统或者别的原因, 修改了服务器秘钥 - - 再次连接新的系统按着提示来运行一条命令即可 - - 例如 `ssh-keygen -f "/home/kcp/.ssh/known_hosts" -R 120.78.154.52` - -## 3.遇到的问题 -- 终端抛出`ssh_exchange_identification: Connection closed by remote host` 错误: -```sh - echo "PermitRootLogin without-password" >> /etc/ssh/sshd_config ;\ - echo "PermitRootLogin yes" >> /etc/ssh/sshd_config ;\ -``` -- 或者尝试 `echo "sshd: ALL" >> /etc/hosts.allow && service sshd restart` -******** -_这是什么问题,这么6的么, 配置好了公钥_ -```sh -$ ssh -p 8888 git@184.170.220.117 - The authenticity of host '[184.170.220.117]:8888 ([184.170.220.117]:8888)' can't be established. - ECDSA key fingerprint is SHA256:Ha9k9dsMxtTaDgN4maUy1VoNzzsm+uMb84zcib6U5jU. - Are you sure you want to continue connecting (yes/no)? yes - Warning: Permanently added '[184.170.220.117]:8888' (ECDSA) to the list of known hosts. - PTY allocation request failed on channel 0 - Welcome to GitLab, Carlsiry Chen! - Connection to 184.170.220.117 closed. -``` -_emmm.出现这样的输出竟然是连接上了,,,_ - -## 4.SSH配置文件 -`vim ~/.ssh/config` -``` - Host aliyun - HostName www.ttlsa.com - Port 22 - User root - IdentityFile ~/.ssh/id_rsa.pub - IdentitiesOnly yes -``` -_参数解释_ -``` - HostName 指定登录的主机名或IP地址 - Port 指定登录的端口号 - User 登录用户名 - IdentityFile 登录的公/私钥文件 奇怪的是有时候用公有时候用私?? - IdentitiesOnly 只接受SSH key 登录 - PubkeyAuthentication -``` -- `ssh aliyun` 即可登录 但是要输入生成公钥时的密码, _方便多公钥的情况_ - - 如果生成公钥时_没有_设置密码就要错三次,然后输入用户密码, - - 不觉得有多方便,还不如 alias进行配置 - -> 修改欢迎信息 /etc/motd -## 5.多密钥对 -> [参考博客](http://blog.csdn.net/black_ox/article/details/17753943) - -1. `ssh-keygen` 生成SSH密钥对 - - 然后在询问中输入新的文件名 -2. `ssh-add 私钥文件绝对路径` - - 若执行ssh-add时出现Could not open a connection to your authentication agent - - 就先执行 `ssh-agent bash` 对应自己的解释器环境 -3. 如上 创建配置文件 config - - 在git项目中使用别名:正常的项目,我们clone下来之后,origin对应的URL假设为: `git@git.:Rusher/helloworld` - - 现在需要做个改动,将git, 要换成rusher_gitlab: - - `git remote set-url origin git@rusher_gitlab:Rusher/helloworld` - - 如果是root用户的项目: - - `git remote set-url origin git@root_gitlab:root/helloworld` -_config_ -``` - Host default - HostName github.com - User git - IdentityFile ~/.ssh/default_id_rsa.pub -``` -- 测试配置是否正确: `ssh -T git@default` - -## 6.访问图形化 - -在`/etc/ssh/sshd_config`添加以下信息,然后重启ssh服务 -``` - X11Forwarding yes - X11DisplayOffset 10 -``` -- `ssh -X -p port user@host` 登录即可 - - 使用过一次,发现了严重的内存泄露,也不知道是什么原因 - -## 7.ssh登录并执行一系列命令 -```sh - ssh user@host 'cmd \ - && cmd \' -``` \ No newline at end of file diff --git a/Linux/Base/img/001-linux-base-cmd.km.svg b/Linux/Base/img/001-linux-base-cmd.km.svg new file mode 100644 index 0000000..1b457a1 --- /dev/null +++ b/Linux/Base/img/001-linux-base-cmd.km.svg @@ -0,0 +1 @@ +Linux 基础命令文件管理touchlsstatfindrmcplnmvcat/more/less网络管理nslookuptraceroutepingcurl/wgetscpsshtelnetrsyncnetstatip/ssiptables进程管理pidofpgreppslsofsarkill/killall用户管理adduseruseraddsupasswdusermodgroupmod硬盘设备管理fsckfdiskdudfddmount \ No newline at end of file diff --git a/Linux/Centos/CentosBase.md b/Linux/Centos/CentosBase.md deleted file mode 100644 index d699613..0000000 --- a/Linux/Centos/CentosBase.md +++ /dev/null @@ -1,64 +0,0 @@ -`目录 start` - -- [Centos](#centos) - - [安装](#安装) - - [docker安装](#docker安装) - - [基础命令](#基础命令) - - [用户管理](#用户管理) - - [新增](#新增) - - [BUG](#bug) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Centos -> 主流服务器 - -## 安装 -### docker安装 -> [hub的官方镜像](hub.docker.com/_/centos/) - -- `docker pull centos` 得到镜像,然后跑起来即可 - - `cat /etc/redhat-release` 查看当前centos版本(适用于redhat centos) [参考博客](www.cnblogs.com/hitwtx/archive/2012/02/13/2349742.html) - -- docker 中 centos7systemctl命令失效的解决方案: - - `docker run --name centos2 --privileged -ti -e "container=docker" -v /sys/fs/cgroup:/sys/fs/cgroup centos /usr/sbin/init` - -## 基础命令 -> 采用的是yum rpm 管理包 - -## 用户管理 - -### 新增 -> 和Ubuntu类似, 但是adduser会新建用户并且建立home目录,而且没有废话的交互, ubuntu就有 - -`adduser kuang` 新增用户和对应目录 -`passwd kuang` 修改密码 , 奇怪的是使用gpasswd就更改成功了用不了 - - -## BUG -> 2018-04-01 16:34:17 稳定?呵呵 -``` -已安装: - nginx.x86_64 1:1.12.2-2.el7 - -作为依赖被安装: - fontconfig.x86_64 0:2.10.95-11.el7 fontpackages-filesystem.noarch 0:1.44-8.el7 gd.x86_64 0:2.0.35-26.el7 gperftools-libs.x86_64 0:2.4-8.el7 - libX11.x86_64 0:1.6.5-1.el7 libX11-common.noarch 0:1.6.5-1.el7 libXau.x86_64 0:1.0.8-2.1.el7 libXpm.x86_64 0:3.5.12-1.el7 - libjpeg-turbo.x86_64 0:1.2.90-5.el7 libpng.x86_64 2:1.5.13-7.el7_2 libunwind.x86_64 2:1.2-2.el7 libxcb.x86_64 0:1.12-1.el7 - libxslt.x86_64 0:1.1.28-5.el7 lyx-fonts.noarch 0:2.2.3-1.el7 nginx-all-modules.noarch 1:1.12.2-2.el7 nginx-mod-http-geoip.x86_64 1:1.12.2-2.el7 - nginx-mod-http-image-filter.x86_64 1:1.12.2-2.el7 nginx-mod-http-perl.x86_64 1:1.12.2-2.el7 nginx-mod-http-xslt-filter.x86_64 1:1.12.2-2.el7 nginx-mod-mail.x86_64 1:1.12.2-2.el7 - nginx-mod-stream.x86_64 1:1.12.2-2.el7 - -失败: - nginx-filesystem.noarch 1:1.12.2-2.el7 - -完毕! -16:28:02 ~/frp/frp_0.16.1_linux_amd64 → nginx -nginx: relocation error: /lib64/libc.so.6: symbol _dl_starting_up, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference -16:28:06 ~/frp/frp_0.16.1_linux_amd64 → sudo yum install nginx -sudo: relocation error: /lib64/libc.so.6: symbol _dl_starting_up, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference -16:28:14 ~/frp/frp_0.16.1_linux_amd64 → sudo yum install git -sudo: relocation error: /lib64/libc.so.6: symbol _dl_starting_up, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference -16:28:30 ~/frp/frp_0.16.1_linux_amd64 → su root -su: relocation error: /lib64/libc.so.6: symbol _dl_starting_up, version GLIBC_PRIVATE not defined in file ld-linux-x86-64.so.2 with link time reference -``` \ No newline at end of file diff --git a/Linux/Container/Docker.md b/Linux/Container/Docker.md index 1851a2b..ff72e94 100644 --- a/Linux/Container/Docker.md +++ b/Linux/Container/Docker.md @@ -1,65 +1,87 @@ -`目录 start` - -- [Docker](#docker) - - [简介](#简介) - - [个人理解](#个人理解) - - [学习资源](#学习资源) -- [安装与卸载](#安装与卸载) - - [Linux](#linux) - - [包管理器安装](#包管理器安装) - - [安装包安装](#安装包安装) - - [不加sudo执行docker命令](#不加sudo执行docker命令) - - [卸载](#卸载) - - [【Windows】](#windows) -- [使用](#使用) - - [Docker镜像仓库](#docker镜像仓库) - - [在服务器上搭建私有仓库](#在服务器上搭建私有仓库) - - [基础命令](#基础命令) - - [镜像命令](#镜像命令) - - [容器命令](#容器命令) - - [docker create](#docker-create) - - [docker run](#docker-run) - - [docker exec](#docker-exec) - - [docker commit](#docker-commit) - - [docker port](#docker-port) -- [数据卷](#数据卷) - - [数据卷容器](#数据卷容器) -- [端口映射](#端口映射) -- [容器互联](#容器互联) -- [Dockerfile](#dockerfile) - - [dockerignore文件的使用](#dockerignore文件的使用) - - [使用启动脚本和多进程容器](#使用启动脚本和多进程容器) -- [容器编排](#容器编排) - - [Docker-Compose](#docker-compose) - - [安装](#安装) - - [Docker-Machine](#docker-machine) - - [Docker-Swarm](#docker-swarm) -- [网络](#网络) - -`目录 end` |_2018-09-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Docker +date: 2018-12-15 11:24:44 +tags: + - 基础 +categories: + - Docker +--- + +💠 + +- 1. [Docker](#docker) + - 1.1. [简介](#简介) + - 1.2. [学习资源](#学习资源) +- 2. [安装与卸载](#安装与卸载) + - 2.1. [Linux](#linux) + - 2.1.1. [安装包安装](#安装包安装) + - 2.1.2. [不加sudo执行docker命令](#不加sudo执行docker命令) + - 2.1.3. [Ubuntu](#ubuntu) + - 2.1.4. [Debian](#debian) + - 2.1.5. [Centos](#centos) + - 2.1.6. [Arch](#arch) + - 2.2. [Windows](#windows) + - 2.3. [图形化管理工具](#图形化管理工具) + - 2.3.1. [Portainer](#portainer) +- 3. [基础管理](#基础管理) + - 3.1. [配置代理](#配置代理) + - 3.2. [配置镜像源](#配置镜像源) + - 3.3. [搭建本地镜像仓库](#搭建本地镜像仓库) + - 3.3.1. [Push over HTTP](#push-over-http) + - 3.4. [基础命令](#基础命令) + - 3.5. [镜像](#镜像) + - 3.6. [容器](#容器) + - 3.6.1. [ps](#ps) + - 3.6.2. [create](#create) + - 3.6.3. [run](#run) + - 3.6.3.1. [资源限制](#资源限制) + - 3.6.4. [exec](#exec) + - 3.6.5. [port](#port) + - 3.7. [端口映射](#端口映射) +- 4. [数据存储](#数据存储) + - 4.1. [文件系统](#文件系统) + - 4.2. [数据卷](#数据卷) + - 4.3. [数据卷容器](#数据卷容器) +- 5. [容器编排](#容器编排) + - 5.1. [Docker-Compose](#docker-compose) + - 5.1.1. [配置文件](#配置文件) + - 5.1.2. [使用命令](#使用命令) + - 5.1.3. [Tips](#tips) + - 5.2. [Docker-Machine](#docker-machine) + - 5.3. [Docker-Swarm](#docker-swarm) +- 6. [网络](#网络) + - 6.1. [None](#none) + - 6.2. [Host](#host) + - 6.3. [Bridge](#bridge) + - 6.4. [User-defined](#user-defined) + - 6.5. [跨主机容器通信](#跨主机容器通信) + - 6.5.1. [overlay](#overlay) +- 7. [Dockerfile](#dockerfile) + +💠 2024-09-06 11:36:43 **************************************** # Docker -> [官方文档](https://docs.docker.com/) | [docker-cn](www.docker-cn.com)`Docker中国` +> [Official Doc](https://docs.docker.com/) | [docker-cn](www.docker-cn.com)`Docker中国` - [docker中文](http://www.docker.org.cn/)`社区` + +- [Gitbook: docker 从入门到实践](https://yeasy.gitbooks.io/docker_practice/content/) + +- [docker-android](https://github.com/budtmo/docker-android) + ## 简介 -- `Docker 是一个开源的应用容器引擎` 理解为加强版虚拟机 -- 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。 - -## 个人理解 -- docker中的容器是动态的,随时创建和销毁,只有镜像是持久化的 -- 而且容器是一个虚拟出来的功能完备的Linux操作系统可以进行登录运行命令 -- `docker images`来得到所有的本地镜像名 - - 使用`docker run --name {name} -d {image-name} `新命名一个容器来启动某个镜像 - - 然后`docker ps`查看容器运行状况 -- 镜像的命名: - - 如果要push到仓库就要遵循这个规范,本地用就无所谓了,而且以后也可以取新的名字 `docker tag 原名 新名` - - 官方的hub: `用户名/镜像名:tag` - - 非官方的例如阿里 `registry.cn-hangzhou.aliyuncs.com/myth/jdk8:alpine` jdk8是镜像名,前面的是仓库地址 +> `Docker 是一个开源的应用容器引擎` 理解为轻量版虚拟机(不模拟硬件层) ## 学习资源 -> [码云上Docke相关资源](https://gitee.com/explore/starred?lang=Docker) +- [PMD: player with docker](https://labs.play-with-docker.com/)`线上练习Docker环境` +- [docker-slim](https://github.com/docker-slim/docker-slim)`镜像瘦身` +- [ ] todo [Use multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) `17.05+` + +- [dockerlabs](https://github.com/collabnix/dockerlabs) +************************ + +> [码云上Docke相关资源](https://gitee.com/explore/starred?lang=Docker) - [docker-training 开源项目](https://gitee.com/dockerf/docker-training) - [第二课](https://gitee.com/dockerf/second) - [Dockerfile集锦](https://gitee.com/kennylee/docker) @@ -71,65 +93,75 @@ > [docker资源汇总 ](http://www.open-open.com/lib/view/open1443075440623.html) > [简述 Docker](http://www.importnew.com/24658.html) -- [ ] todo [Use multi-stage builds](https://docs.docker.com/develop/develop-images/multistage-build/) `17.05+` *************************************** # 安装与卸载 > [daocloud安装帮助](http://get.daocloud.io/#install-docker) | [Docker 加速器](http://guide.daocloud.io/dcs/daocloud-9153151.html) +> [中科大Docker仓库镜像源](https://lug.ustc.edu.cn/wiki/mirrors/help/docker) + ## Linux -> [Official doc](https://docs.docker.com/install/linux/docker-ce/) +> [Official doc](https://docs.docker.com/install/linux/docker-ce/) `所有的发行版` -### 包管理器安装 -如果装 docker.io 则是旧版本 docker-ce 才是新的, +> docker.io 是旧版本 现在新的Docker分为 docker-ce docker-ee +> 注意 Deepin上 如果通过 apt 去安装 docker-compose 它会把 docker-ce 卸掉, 装旧的 docker.io -`snap` -- 安装snap `sudo apt install snapd` -- 查看适用于当前系统的包:`snap install find` -- 安装: `snap install docker` +### 安装包安装 +> [官方文件地址](https://download.docker.com/linux/) -> Ubuntu +- _Debian系_ + - [deb包选择](https://download.docker.com/linux/debian/dists/) + - 进去后选择debain的版本,deepin15.4 的版本是stretch 然后pool/stable/amd64/选版本即可 + - 例如:[Deepin 15.4直接点这里](https://download.docker.com/linux/debian/dists/stretch/pool/stable/amd64/) + - `这两种方式装的是同一个版本号` + - 双击或者`sudo dpkg -i deb文件` + - 测试安装成功 `sudo docker run hello-world` + +### 不加sudo执行docker命令 +> [官方文档](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user) + +- 如果没有docker组,添加组 `sudo groupadd docker ` +- 将当前用户加入用户组 `sudo gpasswd -a $USER docker` +- 然后重新注销登录,或者退出会话重新登录即可 + +### Ubuntu - [Official: Ubuntu安装最新版](https://docs.docker.com/install/linux/docker-ce/ubuntu/#install-docker-ce-1) - `sudo apt install docker-ce` - 关闭服务则是标准服务操作, service docker stop -> debian 8 +`snap` +- 安装snap `sudo apt install snapd` +- 查看适用于当前系统的包:`snap install find` +- 安装: `snap install docker` + +### Debian > [参考](http://www.docker.org.cn/book/install/install-docker-on-debian-8.0-jessie-34.html) - `sudo echo "deb http://http.debian.net/debian jessie-backports main" >> /etc/apt/sources.list` - `sudo apt-get install docker-ce` -> yum +1. `前置软件` sudo apt-get install \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg2 \ + lsb-release \ + software-properties-common + +> [使用清华大学镜像源安装](https://mirrors.tuna.tsinghua.edu.cn/help/docker-ce/) + +### Centos - `sudo yum install docker` - Ubuntu的话,Docker没有启动, 只要一执行Docker相关命令就会自动启动, 但是Centos要手动启动 - `service docker start` 设置开机启动: `chkconfig docker on` -> arch +### Arch - `pacman -S docker` -### 安装包安装 -> [官方文件地址](https://download.docker.com/linux/) - -_Debian系_ -- [deb包选择](https://download.docker.com/linux/debian/dists/) -- 进去后选择debain的版本,deepin15.4 的版本是stretch 然后pool/stable/amd64/选版本即可 -- 例如:[Deepin 15.4直接点这里](https://download.docker.com/linux/debian/dists/stretch/pool/stable/amd64/) -- `这两种方式装的是同一个版本号` -- 双击或者`sudo dpkg -i deb文件` -- 测试安装成功 `sudo docker run hello-world` - -### 不加sudo执行docker命令 -> [官方文档](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user) - -- 如果没有docker组,添加组 `sudo groupadd docker ` -- 将当前用户加入用户组 `sudo gpasswd -a $USER docker` -- 然后重新注销登录,或者退出会话重新登录即可 +************************ -### 卸载 -- `sudo apt-get purge docker-ce` -- `sudo rm -rf /var/lib/docker` - -## 【Windows】 -> Windows上本质是用了VirtualBox创建虚拟机来跑Docker, 屎一般的体验 +## Windows +> Windows上本质是用了VirtualBox创建虚拟机来跑Docker, 屎一般的体验, 然而Win10的WSL因为不能模拟aufs 以及 cgroup 所以能装不能用 +> 只能装上docker for windows 然后把Docker守护进程的套接字文件配置给wsl用。。。。。 - [参考博客](http://www.cnblogs.com/linjj/p/5606687.html) - [官方toolbox 下载](https://www.docker.com/products/docker-toolbox) @@ -137,63 +169,85 @@ _Debian系_ - 安装完成后就会有三个图标在桌面上,然后进入Docker Quickstart Terminal后 `docker run hello-world` 有正常输出即可 ************************************** -# 使用 -- 如果出现命令执行失败,可以登录docker的控制台直接执行 `boot2docker ssh` -- 可以将镜像看成真正运行的程序,容器就是具体的一些配置,所以镜像是可以重复利用,容器出问题删掉就是了 +## 图形化管理工具 +> [lazydocker](https://github.com/jesseduffield/lazydocker) + +### Portainer +> [Official Site](https://www.portainer.io/) | [installation](https://www.portainer.io/installation/) + +1. `docker volume create portainer_data` +1. `docker run --name portainer -d -p 8000:8000 -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce` + + +# 基础管理 +> docker 所有的数据默认存储在 `/var/lib/docker` -## Docker镜像仓库 -> 默认的DockerHub因为在国外所以网络不太稳定 +> [ctop](https://github.com/bcicen/ctop)`Top-like interface for container metrics` -> Docker中国 -- [Official doc](https://www.docker-cn.com/registry-mirror) +- [bitnami](https://bitnami.com/)`非官方,但是维护了很多常用软件的镜像` + +## 配置代理 +2024-06-06 开始封禁Dockerhub及国内源,所以最稳妥的还是用代理 + +> 设置代理方式 +- mkdir -p /etc/systemd/system/docker.service.d +- vim /etc/systemd/system/docker.service.d/http-proxy.conf + ```conf + [Service] + Environment="HTTP_PROXY=http://localhost:7890" + Environment="HTTPS_PROXY=http://localhost:7890" + # 可选项,配置不走代理的仓库 + Environment="NO_PROXY=your-registry.com,10.10.10.10,*.example.com" + ``` +- systemctl daemon-reload +- systemctl restart docker +- 检查环境变量 systemctl show --property=Environment docker +- 查看代理 docker info + +## 配置镜像源 +> 默认的DockerHub因为在国外所以网络不太稳定,需要使用国内镜像源 + +- [Official doc](https://docs.docker.com/registry/recipes/mirror/) `三种使用的方式` 1. 使用指定的URL `docker pull registry.docker-cn.com/myname/myrepo:mytag` 2. 仅仅配置当前守护进程, 重启就失效了`docker --registry-mirror=https://registry.docker-cn.com daemon` 3. 修改 `/etc/docker/daemon.json`文件, 永久性更改 +```json + {"registry-mirrors": ["https://registry.docker-cn.com"]} ``` -{ - "registry-mirrors": ["https://registry.docker-cn.com"] -} -``` + > 时速云 - `sudo docker pull index.tenxcloud.com//:` - 下载后可以用别名 `docker tag index.tenxcloud.com/docker_library/node:lastest node:lastest` -- 然后为了控制台干净可以直接将原来的长命名tag直接删除 -> 阿里云 -- [开发者平台](https://dev.aliyun.com/search.html) -- 配置命名空间,仓库,然后使用文档的配置即可 +******************************** +## 搭建本地镜像仓库 +> [Official doc](https://docs.docker.com/registry/#requirements) -> 百度云 -- 个人较为推荐使用 | [官方文档](https://cloud.baidu.com/doc/CCE/GettingStarted.html#.E9.95.9C.E5.83.8F.E4.BB.93.E5.BA.93) +> [参考:Docker Registry V1 与 V2 的区别解析以及灵雀云的实时同步迁移实践](https://www.csdn.net/article/2015-09-09/2825651) -1. 登录百度云镜像仓库 - - sudo docker login --username=[username] hub.baidubce.com - - username:镜像仓库名称,即是`开通镜像仓库时填写的用户名`。输入密码后完成登录。 +> [Github:v1](https://github.com/docker/docker-registry) | [Github:v2](https://github.com/docker/distribution) -2. 上传镜像 - - sudo docker tag [ImageId] hub.baidubce.com/[namespace]/[ImageName]:[镜像版本号] - - sudo docker push hub.baidubce.com/[namespace]/[ImageName]:[镜像版本号] - - ImageId和镜像版本号根据镜像信息补充 - - namespace是开通镜像仓库时填写的命名空间 - - ImageName是在控制台创建的镜像名称 +> v1 +- 服务器上运行 并映射到本地目录 `docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry` +- 对服务器中docker已经有的镜像 设置别名 `docker tag 镜像 ip:port/镜像名` +- docker push ip:port/镜像名 +- 查看服务器上仓库的镜像 `curl http://IP:5000/v1/search ` -3. 下载镜像 - - 登录到镜像仓库,需输入密码 - - sudo docker pull hub.baidubce.com/[namespace]/[ImageName]:[镜像版本号] +> v2 +- 启动镜像 `docker run -d -p 5000:5000 --name registry registry:2` +- 一样的设置好别名, 然后push上去 +- 查看仓库中的镜像 `curl IP:5000/v2/_catalog` -4. 使用加速器 - - docker软件源地址:https://mirror.baidubce.com +### Push over HTTP +> **注意** 由于 docker client 默认是用的 HTTPS 方式通信, 但是这个本地的 registry 默认是 HTTP 的, 所以有几种解决方案 -******** -### 在服务器上搭建私有仓库 -- 服务器上运行 并映射到本地目录 `docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry` -- 对服务器中docker已经有的镜像 设置别名 `docker tag 镜像 ip:port/名字` -- docker push 别名 -- 查看服务器上仓库的镜像 `curl http://IP:5000/v1/search ` -- 这个需要SSL证书,所以要使用要么修改 docker daemon启动参数 要么手动生成SSL证书,或者申请真正SSL证书 - - 添加参数 DOCKER_OPTS="--insecure-registry ip:port" 重启docker服务 +1. 直接将本地仓库的IP和端口 设置为本地Docker的白名单 + - 给dockerd 添加参数 `DOCKER_OPTS="--insecure-registry ip:port"` + - 或者配置 `/etc/docker/daemon.json` 增加白名单 `{ "insecure-registries":["IP:PORT"] }` + - **重启**Docker服务 +1. 将 registry 配置为 HTTPS, 那么就需要配置SSL证书, 使用本地证书或者公网证书 ******************************** ## 基础命令 @@ -202,41 +256,30 @@ _Debian系_ _登录镜像仓库_ - 登录hub.docker :`docker login ` 或者 `docker login -u username -p password` - 登录时速云:`sudo docker login index.tenxcloud.com` -- 登录百度云: `docker login --username=[username] hub.baidubce.com` -## 镜像命令 +- 清理全部未使用的资源 docker system prune -a + +## 镜像 +> Docker 的镜像是采用分层文件系统, Dockerfile中每个RUN命令造成的修改或新增都是新的一层layer,旧文件不变 + +> [dive](https://github.com/wagoodman/dive)`查看镜像内各layer文件` + +- 查看所有 : `docker images` + - docker images -a 查看所有镜像(包括中间镜像) - 搜索 : `docker search 镜像名` - 安装 : `docker pull 镜像名` -- 查看所有 : `docker images` - 删除 : `docker rmi 镜像名` - 查看详细: `docker inspect [-f {{".Architesture"}}]` -f 查看JSON格式的具体节点的数据值 -- 查看历史:`docker history imagename` +- 查看Layer历史:`docker history imagename` 每一层的Layer id 和 执行的操作 - 添加标签(别名): `docker tag originname newname` - 导出镜像文件:`docker save -o ubuntu.tar ubuntu:14.04` - - 导入镜像文件: `docker load --input ubuntu.tar`或 `docker load < ubuntu.tar` + - 导入镜像文件: `docker load --input ubuntu.tar` 或 `docker load < ubuntu.tar` - 上传镜像: `docker push mythos/test:lastest` +- 删除所有未使用的image `docker image prune --all` -## 容器命令 -_ps_ -- 查看当前运行的容器:`docker ps ` - - 查看所有容器 :`docker ps -a` - - 查看占用 :`docker ps -s` - - [ps formatting](https://docs.docker.com/engine/reference/commandline/ps/#formatting) -``` -.ID Container ID -.Image Image ID -.Command Quoted command -.CreatedAt Time when the container was created. -.RunningFor Elapsed time since the container was started. -.Ports Exposed ports. -.Status Container status. -.Size Container disk size. -.Names Container names. -.Labels All labels assigned to the container. -.Label Value of a specific label for this container. For example '{{.Label "com.docker.swarm.cpu"}}' -.Mounts Names of the volumes mounted in this container. -.Networks Names of the networks attached to this container. -``` +************************ + +## 容器 - 查看所有容器的状态:`docker stats` 能看到正在运行的容器内存 cpu io net等信息 - `-a` 所有容器 - `--no-stream` 不阻塞标准输出流,只输出一次信息 @@ -255,29 +298,70 @@ _ps_ - 容器日志(终端所有输入输出):`docker logs 容器name或id` - 重命名 : `docker rename origin new` -- 导入导出 (容器快照): +- 导入导出 (容器快照): **注意此方式不会保留layer历史,无法回滚** - 导出: `docker export -o test.tar 容器名` `docker export 容器name > test.tar` - 导入: `docker import [-c |--change=[]] [-m | --message=[]] file|URL - [repository]:[tag]` - -c | --change=[] 选项在导入的同时执行对容器就行修改的Dockerfile指令。 +- 将容器导出为镜像: `docker commit container_name image:tag` + +> [Attach a volume to a container while it is running](http://jpetazzo.github.io/2015/01/13/docker-mount-dynamic-volumes/) + +> 修改端口映射 + +- 停止容器和Docker服务 +- cd /var/lib/docker/containers/{id} +- 修改 hostconfig.json + - `PortBindings` 节点 新增或修改 + ```json + "PortBindings":{"3306/tcp":[{"HostIp":"","HostPort":"3360"}]} + ``` +- config.v2.json + - `NetworkSettings.Ports` 节点下 新增或修改 + ```json + "Ports":{"8080/tcp":[{"HostIp":"0.0.0.0","HostPort":"8888"}],"8081/tcp":[{"HostIp":"0.0.0.0","HostPort":"8881"}]} + ``` + +************************ + +### ps +- 查看当前运行的容器:`docker ps ` + - 查看所有容器 :`docker ps -a` + - 查看占用 :`docker ps -s` + - [ps formatting](https://docs.docker.com/engine/reference/commandline/ps/#formatting) + +``` + .ID Container ID + .Image Image ID + .Command Quoted command + .CreatedAt Time when the container was created. + .RunningFor Elapsed time since the container was started. + .Ports Exposed ports. + .Status Container status. + .Size Container disk size. + .Names Container names. + .Labels All labels assigned to the container. + .Label Value of a specific label for this container. For example '{{.Label "com.docker.swarm.cpu"}}' + .Mounts Names of the volumes mounted in this container. + .Networks Names of the networks attached to this container. +``` -### docker create +### create > [官方文档](https://docs.docker.com/engine/reference/commandline/create) -### docker run +### run > [Docker run 命令的使用方法](http://www.open-open.com/lib/view/open1422492851548.html) > 等价于 docker create 再 docker start - - `docker run -d --name conrainer-name image-name touch a.md` ,如果镜像本地没有会自动pull - `--name` 配置容器名字 - `-d` 后台启动程序 - `-i` 交互模式运行容器(标准输入和标准输出) `docker run -it ubuntu /bin/bash` - `-t` 容器启动后进入其命令行 - `-v` 将本地文件夹建立映射到容器内 `-v 本机:容器` - - `-p` 端口映射左本机右容器:`-p 44:22`主机容器端口相同就:`-p 22` 将容器所有EXPOSE的端口映射到宿主机随机端口`-P` - - `-f` 文件? + - `-p` 端口映射左本机右容器:`-p 44:22`, 主机容器端口相同:`-p 22` + - 将所有EXPOSE的端口映射到宿主机上的随机端口`-P` + - 绑定udp端口 `-p 44:22/udp` - `--env name="tanky"` 设置环境变量 - - `--memory` 限制最大内存 - `--cpu-shares` 设置CPU的相对权重,只在link之间容器的权重比例 - `--cpuset-cpus` 限制只能运行在某标号的CPU上 - `--user` -u 限制用户 @@ -285,17 +369,22 @@ _ps_ - `--link` 链接其他容器 - `--rm` 容器运行结束退出就自动删除该容器 注意和`-d`不能共存 - `--restart=always` 设置该容器随dokcer 服务自启动 - - `--hostname 容器hostname` 指定容器的hostname + - `--hostname hostname` 指定容器的 hostname + - `--init` 能增加 docker-init 进程作为1号进程 entrypoint 或 cmd 中的命令成为docker-init 子进程 + +`-e TZ="Asia/Shanghai" -v /etc/localtime:/etc/localtime:ro` + +#### 资源限制 +> [Docker CPU 内存 资源限制](https://www.cnblogs.com/zhuochong/p/9728383.html) -- 常用的参数 - - `-e TZ="Asia/Shanghai"` 指定时区,可以解决时间不一致 - - `-v /etc/localtime:/etc/localtime:ro` 设置容器的时钟和宿主机一致,不一定有用 - -> [参考博客: Docker修改默认时区](https://www.jianshu.com/p/004ddf941aac) +> **`内存限制`** +- 限制最大内存100M `--memory 100M` 或者 `-m 100M` +- 配置交换内存不受限制 `--memory-swap -1` + - 不配置该项 或者 该项小于 --memory 则都是采用默认值, --memory 的两倍 -> `docker create` 是创建一个容器,不会运行,`docker run`是运行命令在一个新容器里 +> [参考: Docker 资源限制之内存](https://blog.opskumu.com/docker-memory-limit.html) -### docker exec +### exec - 登录容器: - `docker exec -it 容器name或id bash ` - `docker attach 容器id` 这个命令虽然简单,但是退出会话就自动关闭了容器 @@ -309,16 +398,33 @@ _ps_ - PID=${docker-pid 容器id} - nsenter --target $PID --mount --uts --ipc --net --pid -### docker commit -- `docker commit 容器id 镜像name` 将容器为id的当前容器 保存为name镜像 - -### docker port +### port > 查看容器的端口映射情况, 输出是左容器右本机, 和使用相反 +************************ + +## 端口映射 +- 当不指定对应的参数容器默认不开放任何端口给外部,可以使用 `-P` 或 `-p` 参数来开放 + - -P 随机映射一个 49000-49900 的端口到容器开放的端口 + - -p `IP:HostPort:ContainerPort | IP::ContainerPort | HostPort:ContainerPort` + - 映射到指定IP的指定端口`IP:HostPort:ContainerPort` + - 映射到指定IP的任意端口`IP::ContainerPort` + - 映射到所有接口的地址的指定端口`HostPort:ContainerPort` + - 还可以使用 udp来标记为udp类型 `docker run -d -p 127.0.0.1::5000/udp ubuntu apt update` +- 查看端口 + - 查看容器内5000对应的外端口 `docker port ubuntu17 5000` + - 查看容器的具体信息 `docker inspect 容器id` + ********************* -# 数据卷 + +# 数据存储 +## 文件系统 +- AUFS (AnotherUnionFS) `Ubuntu/Debian默认` +- Device Mapper:`CentOS/RedHat默认` + +## 数据卷 > [Docker 中管理数据](http://www.open-open.com/lib/view/open1403571027233.html) -> [参考博客: 给一个正在运行的Docker容器动态添加Volume](http://www.open-open.com/lib/view/open1421996521062.html) +> [参考: 给一个正在运行的Docker容器动态添加Volume](http://www.open-open.com/lib/view/open1421996521062.html) - 数据卷是一个可供容器使用的特殊目录,它将宿主机操作系统目录映射进容器 类似于 mount操作 - 数据卷可以在容器之间共享重用 @@ -329,6 +435,8 @@ _ps_ - `docker run -v dir:dir[:ro]` 一般是创建容器时使用,和-p类似可以多个,左本机右容器 默认rw权限可以指定 ro只读 - 可以将一个文件挂载为数据卷,但是文件夹更好,文件可能会有问题出现 +- 挂载宿主机时区及时间 `/etc/localtime:/etc/localtime` + ## 数据卷容器 - `docker run -it -v /test --name data ubuntu ` 运行一个挂载了数据卷的容器 - 引用数据卷容器 来挂载数据卷:`docker run -it --volumes-from data --name db1 ubuntu` @@ -346,22 +454,125 @@ _ps_ - 解压当前目录的tar文件到数据卷容器中 `docker run --volumes-from reuse -v $(pwd):/backup busybox tar xvf /backup/backup.tar` - 这个就是实现了将本地的归档数据放到指定的容器内,如果要从数据卷容器中恢复到别的容器就只要挂载对应的数据卷容器然后进目录直接解压即可 -************************** -# 端口映射 -- 当不指定对应的参数容器默认不开放任何端口给外部,可以使用 -P -p 参数来开放 - - -P 随机映射一个 49000-49900 的端口到容器开放的端口 - - -p `IP:HostPort:ContainerPort | IP::ContainerPort | HostPort:ContainerPort` - - 映射到指定IP的指定端口`IP:HostPort:ContainerPort` - - 映射到指定IP的任意端口`IP::ContainerPort` - - 映射到所有接口的地址的指定端口`HostPort:ContainerPort` - - 还可以使用 udp来标记为udp类型 `docker run -d -p 127.0.0.1::5000/udp ubuntu apt update` -- 查看端口 - - 查看容器内5000对应的外端口 `docker port ubuntu17 5000` - - 查看容器的具体信息 `docker inspect 容器id` +****************************************************** + +# 容器编排 +## Docker-Compose +> [Official](https://docs.docker.com/compose/) + +声明式环境,管理多容器, 并处理好相关资源的关系 + +> [Demo: 开源电商平台](https://github.com/fecshop/yii2_fecshop_docker/blob/master/docker-compose.yml) +> [Demo: 安装 Kafka](http://www.cnblogs.com/xuxinkun/p/5473952.html) + +- [安装](https://docs.docker.com/compose/install/) + - 最简单: `sudo pip install docker-compose` + +### 配置文件 +> 一个配置文件就表示了一组容器, 以及相关的网络,文件等配置, docker-compose 都是基于该配置文件进行基本命令操作 +> 语法上和 docker run 基本一致, 只不过以 yml 形式配置而已 + +> 声明一个 xxx 网络 供 service 使用 +```yml +networks: + xxx: + external: false + driver: bridge + ipam: + driver: default + config: + - subnet: 10.12.0.0/16 +``` + +```yml +version: "2.1" +services: + zookeeper: + image: ${IMAGE_NAME:-defaultImage} + expose: + - "6666" + ports: + - "6666:6666" + volumes: + - /etc/localtime:/etc/localtime + networks: # 可不配置,Docker会默认分配一个ip 172.xx 开头 + - xxx + command: ./bin/start.sh + links: + - "mysql:mysql" + environment: + - NAME=who +``` + +### 使用命令 +> 必须要在 docker-compose.yml 文件目录下执行 + +- help +- up # 自动完成构建镜像,`创建`服务,启动服务,并关联服务等操作, `-d` 后台执行 +- down # 停止并`删除`该服务的所有容器, 移除网络, `-v` 移除挂载的volume +- start # 启动存在的服务 +- stop # 停止 +- restart # 重启项目中服务 +- exec # 进入指定容器 +- image # 列出 Compose 文件中包含的镜像 +- kill [SERVICE...] +- pause [SERVICE...] +- unpause [SERVICE...] +- ps # 列出项目中所有容器 + +### Tips +> yml所在的目录名会作为容器名的前缀 + + +************************ + +## Docker-Machine +> 创建一个docker集群环境 [官方文档安装](https://docs.docker.com/machine/install-machine) + +Error with pre-create check: "VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path +Error with pre-create check: "This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory" + +## Docker-Swarm + +*********************************** + +# 网络 +> [Official Doc](https://docs.docker.com/network/) 分为 none host brige(缺省) user-defined 几种类型 + +> Connection reset by peer +1. 可能是 docker0 和本身网段冲突了 `docker network inspect bridge` 对比 `netstat -nr` 查看 + - [Docker: connection reset by peer](https://serverfault.com/questions/848075/docker-connection-reset-by-peer) +1. 重置网桥 [Connection reset by peer](https://blog.csdn.net/Alphr/article/details/107969190) `原因待寻找` + +[规避网段冲突](https://www.jb51.net/article/208255.htm) + +/etc/docker/daemon.json 顶级元素加入如下配置网段 +```json + "default-address-pools":[ + {"scope":"local","base":"172.80.0.0/16","size":24}, + {"scope":"global","base":"172.90.0.0/16","size":24} +``` +## None +> docker run -it --network none busybox + +- 不联网的容器, ifconfig 可以看到只有 lo + +## Host +> docker run -it --network host busybox + +- 采用宿主机的网络, 也就是说和宿主机使用同一个网络环境, hostname都是host的 + 1. 特点是性能, 但是不够灵活, 要考虑和host上的端口冲突问题 + 1. 直接配置host的网络: 例如配置防火墙容器 + +## Bridge +> 安装 Docker 的时候, 都会创建一个 docker0 的网桥 Linux bridge + +- 如果没有指定 `--network` 或者使用 `--network default` 创建容器 都会默认挂载到 docker0 上 +- 通过 `docker network inspect bridge` 命令可以看到子网掩码是 `172.17.0.0/16` 网关是 172.17.0.1 + - 也就是说能容纳 2的16次幂 -2 个容器 (65534), 容器创建时会依次分配ip + +> 注意: 此方式下容器之间是互通的, 通常使用的 `--link containerName:aliasName` 也只不过是在 /etc/hosts 文件中添加了容器的 dns 而已 -***************** -# 容器互联 -> 让多个容器中应用快速安全交互的方式,特别注意这是双向互联的, 这是简单的做法, 高级做法是建立[网络](#网络) > 特别容易出现锁,一个没有启动,其他的都启动不了 尝试? `sudo service docker restart` - 例如: `创建一个MySQL容器供一个Ubuntu容器使用` @@ -375,59 +586,50 @@ _ps_ - 例如:`创建一个Nginx和一个Springboot搭建的web服务` - 构建Springboot应用镜像,构建应用容器 开放8888端口 - - 新建nginx容器:`docker run --name youhuigo -d -p 80:80 -v /home/kuang/nginx/conf/:/etc/nginx/conf.d/:ro --link you:web nginx` + - 新建nginx容器:`docker run --name test-nginx -d -p 80:80 -v /home/kuang/nginx/conf/:/etc/nginx/conf.d/:ro --link you:web nginx` - 配置文件:`一样的cat /etc/hosts 查看容器的IP`, 其实最简单就是用link配置时的别名即可,因为Docker已经帮我们配置好了host。。。 -```conf -upstream youhui { - server 172.17.0.4:8888; -} - -server { - listen 80; - server_name youhui; - - location / { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header X-Nginx-Proxt true; - - proxy_pass http://youhui; - proxy_redirect off; - } -} ``` -*********************** -# Dockerfile ->[Dockerfile文件学习](/Linux/Container/DockerFile.md) - -## dockerignore文件的使用 -- .dockerignore文件是依据 Go的PathMatch规范来的,使用和.gitignore类似 - -## 使用启动脚本和多进程容器 - -****************************************************** -# 容器编排 -## Docker-Compose -> 声明式环境,管理多容器, 并处理好相关资源的关系 + upstream backend { + server 172.17.0.4:8888; + } + + server { + listen 80; + server_name backend; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-Nginx-Proxt true; + + proxy_pass http://backend; + proxy_redirect off; + } + } +``` -案例 [1](https://github.com/fecshop/yii2_fecshop_docker/blob/master/docker-compose.yml) -### 安装 -> sudo pip install -U docker-compose +> [weave](https://www.weave.works/) `能解决跨宿主机的容器互联问题` -*********** -## Docker-Machine -> 创建一个docker集群环境 [官方文档安装](https://docs.docker.com/machine/install-machine) +## User-defined +> Docker 提供三种 网络驱动 bridge overlay macvlan, 后两者可用于跨主机的容器通信 -Error with pre-create check: "VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path -Error with pre-create check: "This computer doesn't have VT-X/AMD-v enabled. Enabling it in the BIOS is mandatory" +> 容器分配独立ip -## Docker-Swarm +1. 宿主机新建网络 `docker network create --subnet=172.13.0.0/24 test-standby` +1. 宿主机新建容器并分配ip `docker run -it --net test-standby --ip 172.13.0.8 -p 6379 --name redis-stand redis:5.0.9-alpine` +1. 宿主机 配置为虚拟路由器 完成转发 + - `sysctl -w net.ipv4.ip_forward=1` + - ip route 查看路由表,并 ping 172.13.0.8 查看路由表是否正确 +1. 其他主机上加上这个路由,就可以访问 容器了 + - Windows: `route add 172.13.0.0 mask 255.255.255.0 192.168.7.110` + - Linux: `ip route add 172.13.0.0/24 via 192.168.7.110` -*********************************** -# 网络 -> 远没有前面的容器互联那样的简单, 更多还需学习 +## 跨主机容器通信 -- [ ] 学习Docker中的网络 +### overlay +> [参考: DOCKER的内置OVERLAY网络](http://dockone.io/article/2717) -> [weave](https://www.weave.works/) `能解决跨宿主机的容器互联问题` +*********************** +# Dockerfile +>[Dockerfile文件学习](/Linux/Container/DockerFiles.md) diff --git a/Linux/Container/DockerAdvance.md b/Linux/Container/DockerAdvance.md index 35ffc63..fcd77ef 100644 --- a/Linux/Container/DockerAdvance.md +++ b/Linux/Container/DockerAdvance.md @@ -1,29 +1,59 @@ -`目录 start` - -- [Docker Advance](#docker-advance) - - [AUFS](#aufs) - - [配置](#配置) - - [更改数据的存放目录](#更改数据的存放目录) - -`目录 end` |_2018-09-13_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Docker进阶 +date: 2018-12-15 11:25:27 +tags: +categories: + - Docker +--- + +**目录 start** + +1. [Docker Advance](#docker-advance) + 1. [文件系统](#文件系统) + 1. [AUFS](#aufs) + 1. [OverlayFS](#overlayfs) + 1. [配置](#配置) + 1. [更改数据的存放目录](#更改数据的存放目录) + 1. [提供底层接口访问](#提供底层接口访问) + 1. [暴露守护进程端口](#暴露守护进程端口) + 1. [持有套接字文件](#持有套接字文件) +1. [Tips](#tips) + 1. [孤儿进程以及僵死进程](#孤儿进程以及僵死进程) + +**目录 end**|_2021-02-03 17:25_| **************************************** # Docker Advance -## AUFS -> Docker 采用的是 AUFS 文件系统, go语言编写的程序 +## 文件系统 +> Docker支持 AUFS、Btrfs、Device mapper、OverlayFS、Overlay2FS、ZFS + +### AUFS +> Docker旧版本 采用的是 AUFS 文件系统 + +> [参考: 剖析Docker文件系统:Aufs与Devicemapper](http://www.infoq.com/cn/articles/analysis-of-docker-file-system-aufs-and-devicemapper) +> [参考: 理解Docker(7):Docker 存储 - AUFS](http://www.cnblogs.com/sammyliu/p/5931383.html) -> [参考博客: 剖析Docker文件系统:Aufs与Devicemapper](http://www.infoq.com/cn/articles/analysis-of-docker-file-system-aufs-and-devicemapper) -> [参考博客: 理解Docker(7):Docker 存储 - AUFS](http://www.cnblogs.com/sammyliu/p/5931383.html) +> [参考: Docker: Just Stop Using AUFS](https://sthbrx.github.io/blog/2015/10/30/docker-just-stop-using-aufs/) + +### OverlayFS +> 最新的Docker都是采用这种文件系统, 并具有 overlay overlay2 两代驱动 + +> [参考: docker 存储驱动之overlay](https://blog.csdn.net/u010278923/article/details/79215828) + +> 查看占用大小 docker system df + +**************** ## 配置 +> [官方检查配置的脚本](https://github.com/moby/moby/blob/master/contrib/check-config.sh) ### 更改数据的存放目录 > docker 默认是将数据放在了 `/var/lib/docker` 下, 包括所有的镜像, 容器, 卷... 1. `挂载新的目录到 /var/lib/docker 上` -> [参考博客: Docker数据将跟分区磁盘占满了 ](http://dockone.io/question/531) -> [参考博客: Docker 常见问题 (FAQ)](https://www.lsproc.com/post/docker-faq/#toc_1) +> [参考: Docker数据将跟分区磁盘占满了 ](http://dockone.io/question/531) +> [参考: Docker 常见问题 (FAQ)](https://www.lsproc.com/post/docker-faq/#toc_1) ```sh service docker stop cp -prf /var/lib/docker /data/ @@ -56,3 +86,42 @@ DOCKER_OPTS="--graph=/opt/docker " ``` +### 提供底层接口访问 +#### 暴露守护进程端口 + +1. systemctl edit docker.service +```ini + [Service] + ExecStart= + ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock +``` +> 以上所处文件为: /etc/systemd/system/docker.service.d/override.conf +>> 注意: `-H unix:///var/run/docker.sock` 如果少了这个配置, Docker客户端就失效了, 什么都干不了 + +2. systemctl restart docker + +> 而那些不是使用systemd管理服务的才要在 /etc/docker/ 下配置 daemon.json [official doc](https://docs.docker.com/engine/reference/commandline/dockerd/) + +#### 持有套接字文件 +> 将 `/var/run/docker.sock` 的访问权限 提供给使用方即可 + +# Tips +> WARNING: No swap limit support + +1. Edit the /etc/default/grub file. + - Set the GRUB_CMDLINE_LINUX value as follows: + - GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1" +1. sudo update-grub +1. Reboot your system. + +## 孤儿进程以及僵死进程 +> [进程相关知识](/Linux/Base/LinuxBase.md#进程) +> 当父进程结束后,原来的僵死子进程, 会成为孤儿进程且是僵死进程, 此时会被1号进程收养 + +> [参考: Docker和孤儿进程、僵死进程 ](https://yq.aliyun.com/articles/61894) +> 在 Docker 中, 由于没有 init 这个1号进程(往往是应用进程作为1号进程) 很有可能子进程称为僵死进程且一直存在 +> Docker1.11之前的版本,孤儿进程是否有可能成为僵死进程取决于容器内pid为1的进程是否在子进程退出时调用wait/waitpid +> Docker1.11版本之后孤儿进程不会成为僵死进程 + +**解决策略** +> [tini](https://github.com/krallin/tini#using-tini) `tini 轻量级init进程 更好的管理进程等资源` diff --git a/Linux/Container/DockerFile.md b/Linux/Container/DockerFiles.md similarity index 54% rename from Linux/Container/DockerFile.md rename to Linux/Container/DockerFiles.md index bea50e4..b9f383d 100644 --- a/Linux/Container/DockerFile.md +++ b/Linux/Container/DockerFiles.md @@ -1,32 +1,50 @@ -`目录 start` - -- [Dockerfile](#dockerfile) - - [使用入门案例](#使用入门案例) - - [【Dockerfile命令理解】](#dockerfile命令理解) - - [FROM](#from) - - [MAINTAINER](#maintainer) - - [RUN](#run) - - [CMD](#cmd) - - [ENTRYPOINT](#entrypoint) - - [USER](#user) - - [EXPOSE](#expose) - - [ENV](#env) - - [LABEL](#label) - - [ARG](#arg) - - [COPY](#copy) - - [ADD](#add) - - [VOLUME](#volume) - - [WORKDIR](#workdir) - - [STOPSIGNAL](#stopsignal) - - [ONBUILD](#onbuild) - - [Dockerfile案例](#dockerfile案例) - - [打包最新版git](#打包最新版git) - - [Dockerfile中新建用户](#dockerfile中新建用户) - -`目录 end` |_2018-09-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: DockerFile +date: 2018-12-15 11:27:03 +tags: + - 基础 +categories: + - Docker +--- + +💠 + +- 1. [Dockerfile](#dockerfile) + - 1.1. [使用入门案例](#使用入门案例) + - 1.2. [docker build](#docker-build) + - 1.2.1. [docker buildx](#docker-buildx) + - 1.3. [Dockerfile命令](#dockerfile命令) + - 1.3.1. [FROM](#from) + - 1.3.2. [MAINTAINER](#maintainer) + - 1.3.3. [RUN](#run) + - 1.3.3.1. [修改容器时区设置](#修改容器时区设置) + - 1.3.3.2. [修改 hosts](#修改-hosts) + - 1.3.3.3. [软件安装后缓存文件的清理](#软件安装后缓存文件的清理) + - 1.3.4. [CMD](#cmd) + - 1.3.5. [ENTRYPOINT](#entrypoint) + - 1.3.6. [USER](#user) + - 1.3.7. [EXPOSE](#expose) + - 1.3.8. [ENV](#env) + - 1.3.9. [LABEL](#label) + - 1.3.10. [ARG](#arg) + - 1.3.11. [COPY](#copy) + - 1.3.12. [ADD](#add) + - 1.3.13. [VOLUME](#volume) + - 1.3.14. [WORKDIR](#workdir) + - 1.3.15. [STOPSIGNAL](#stopsignal) + - 1.3.16. [HEALTHCHECK](#healthcheck) + - 1.3.17. [ONBUILD](#onbuild) + - 1.3.18. [中间镜像](#中间镜像) +- 2. [Practice](#practice) + - 2.1. [dockerignore文件的使用](#dockerignore文件的使用) + - 2.2. [Exercises](#exercises) + - 2.2.1. [打包最新版git](#打包最新版git) + - 2.2.2. [Dockerfile中新建用户](#dockerfile中新建用户) + +💠 2024-08-26 16:31:06 **************************************** - # Dockerfile + ## 使用入门案例 - `mkdir test && cd test && touch Dockerfile ` 输入如下文本 ```Dockerfile @@ -41,20 +59,29 @@ - `docker build -t image:tag .` 给镜像指定名字, 注意标签不设置就是默认的latest - 创建镜像成功后 `docker run --name ContainerName -d image:tag` 新建容器来运行镜像 -******************************************************************* -## 【Dockerfile命令理解】 -- Dockerfile是一个`镜像`的表示,可以通过Dockerfile来描述构建镜像的步骤,且可以自动构建一个容器 -- 所有的 Dockerfile 命令格式都是: `INSTRUCTION arguments` -- 最好在运行这个配置文件的时候新建一个空目录目录下放dockerfile,不要使用根目录,不然全部的东西都传到守护进程里去了 - - 因为生成过程的第一件事是将整个上下文 (递归) 发送到守护进程。 -- 同样的可以使用`.dockerignore`文件来忽略不要上传的文件 +************************ +## docker build -_docker build_ - 如果文件名是默认的`Dockerfile` 就使用 `.` - 否则就是 `docker build -t image:tag- < 文件名` 或者使用-f参数: - `-f` 指向任意位置的文件进行配置 `docker build -f /path/to/a/Dockerfile .` - `-t`如果构建成功 可以指定保存新镜像的image和tag (多个的话就多个 -t就行了,例如 `docker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .`) +### docker buildx +- [docker buildx](https://github.com/docker/buildx) + +************************ + +## Dockerfile命令 +- Dockerfile是一个`镜像`的表示,可以通过Dockerfile来描述构建镜像的步骤,且可以自动构建一个容器 +- 所有的 Dockerfile 命令格式都是: `INSTRUCTION arguments` + +> 注意 +- 在运行配置文件时,dockerfile所在目录应尽量减少无关文件,因为当前dockerfile所在目录递归传入Docker构建进程中 + - 或者使用`.dockerignore`文件来忽略不要上传的文件 +- 执行命令时,不常变的命令应放在前面,利用Docker的 build cache,因为每个命令会生成一层文件layer,如果layer一致就会复用 + - 例如 npm 对应的 package.json 配置,先COPY文件 然后 npm install, 如果 package.json 没有变动 npm install 也会复用旧 layer + ### FROM > 基于某镜像构建,这是整个文件的第一条指令,一定是基于某镜像构建的,如果是空镜像就使用特殊的 `FROM scratch` > 允许多个FROM命令,其后的命令就是基于该FROM的镜像构建的,但是一个dockerfile只能得到一个有名字的镜像(最后一个FROM构建的镜像),之前的FROM就是none:none @@ -63,17 +90,28 @@ _docker build_ - `FROM image:tag` - `FROM image@digest` -- 如果FROM使用中,找不到对应的版本的镜像,整个Dockerfile就会报错返回 +- 如果FROM使用中,找不到对应的版本的镜像,整个Dockerfile就会报错退出 + +************************ + +> 在 17.05 版本开始, 支持分步构建, Multiple stage + +例如基于一个编译环境镜像, 编译得到结果文件, 然后基于运行环境, 将结果文件复制过来, 构建成新的镜像, 最终镜像将不包含编译环境 +```dockerfile +FROM maven:3.5.0-jdk-8-alpine AS builder +RUN mvn clean package + +From openjdk:8-jre-alpine +COPY --from=builder target/demo.jar demo.jar +CMD ["java", "-jar", "demo.jar"] +``` -> 在 17.05 版本开始, 支持分步构建, ->1. 例如基于一个编译环境镜像, 编译得到结果文件, 然后基于运行环境, 将结果文件复制过来, 构建成新的镜像 ->1. 构建出来的镜像是不包含编译环境的 ### MAINTAINER - 留开发者名字 例如 `MAINTAINER kuangcp myth.kuang@gmail.com` - 可以放多个信息,但是建议只有开发者信息,其他的放在Labels里 ### RUN -> 每条RUN命令在当前镜像的基础上执行指定命令,并提交为新的镜像层,所以尽量将所有命令放在一个RUN里 +> 每条RUN命令在当前镜像的基础上执行指定命令,并提交为新的镜像层,所以尽量将所有命令放在一个RUN里, 便于后续构建时复用缓存和减少层数 - `RUN command` - 这种写法中的command是shell `/bin/sh -C`负责执行,所以就会有限制,必须要有 `/bin/sh` @@ -85,9 +123,60 @@ _docker build_ - 环境变量的问题: `RUN ["echo","$HOME"]` 是不会正常输出的,因为此时不会加载环境变量中的数据 - `RUN ["sh", "-c", "echo", "$HOME"]` 就可以正常输出了 - 当RUN命令执行完毕后,就会生成一个新的文件层。这个文件层会保存在缓存中作为下一个指令的基础镜像存在,如果不需要缓存就加上 `--no-cache` - - 所以就尽量是将所有的命令 放在一个RUN命令里减少镜像层数。 + +> 注意 apt update 会被Docker缓存,因为缓存的判定是依据命令字符,而不是文件的内容,会有这种[奇怪的问题](https://github.com/moby/moby/issues/3313) +- 但是遇到了一种奇怪的场景,有时候会使用到缓存,但是有时候会重新执行update命令和安装软件的命令就会很费时甚至超时失败。 + +#### 修改容器时区设置 +> [参考: Docker修改默认时区](https://www.jianshu.com/p/004ddf941aac) + +```sh + # Alpine + RUN apk --no-cache add tzdata && \ + ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" > /etc/timezone + + # Ubuntu 可能需要安装tzdata + RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ + echo "Asia/Shanghai" > /etc/timezone && \ + dpkg-reconfigure -f noninteractive tzdata + + # Centos + RUN echo "Asia/shanghai" > /etc/timezone; +``` +> 对于 alpine 以及 Ubuntu ln -s 建立到时间文件的软链接就已经够了, 但是确保没问题就最好还是修改下 时区文件 + +#### 修改 hosts +由于Docker动态分配pod的虚拟ip,所以hosts会动态生成,单纯使用RUN命令在Dockerfile中修改hosts文件是不生效的。 +正确方式为: +- Docker `docker run –add-host host:ip` +- K8S + ```yml + hostAliases: + - hostnames: + - api.xxxxx.com + ip: 192.168.1.5 + - hostnames: + - user.xxxx.cn + ip: 192.168.1.9 + ``` + +#### 软件安装后缓存文件的清理 +```sh + # Ubuntu + apt update && apt install tzdata && apt autoclean && rm -rf /var/lib/apt/lists/ + + # Alpine + apk update && apk add bash && +``` +************** + +> 关于Java的时区问题 +1. `docker run ... -e JAVA_OPTS=-Duser.timezone=Asia/Shanghai ` +1. java读取的是 /etc/timezone 文件 所以修改为 Asia/Shanghai 就可以了 + ### CMD > 指定 容器启动时默认执行的命令 @@ -95,15 +184,17 @@ _docker build_ - `CMD ["executable","param1","param2"]` (like an exec, preferred form) `推荐` - `CMD ["param1","param2"]` (as default parameters to ENTRYPOINT) 作为默认参数提供给ENTRYPOINT - `CMD command param1 param2` (as a shell) 作为shell命令 依靠`bin/sh -C`执行 + - 一个Dockerfile里只能有一个CMD,如果有多个,只有最后一个生效。 - 如果用户在`docker run` 中带了运行的命令,就会覆盖CMD命令 -- 与RUN命令一样如果要环境变量就要使用 `sh -C` +- 与RUN命令一样如果要环境变量就要使用 `sh -C` : `CMD ["sh", "-C", "echo $HOME"]` ### ENTRYPOINT -- `容器入口点` 命令设置在容器启动时执行命令 一般用来做初始化容器,或者运行持久软件 +> `容器入口点` 命令设置在容器启动时执行命令 一般用来做初始化容器,或者运行持久软件 + - `ENTRYPOINT echo "Welcome!"` 那么每次启动容器都有这个输出 -- `ENTRYPOINT cmd param1 param2 ...` -- `ENTRYPOINT ["cmd", "param1", "param2"...]` +- `ENTRYPOINT java param1 param2 ...` 注意这种写法会使用默认shell去解释执行这个命令,即sh进程下才是java进程 +- `ENTRYPOINT ["java", "param1", "param2"...]` 这种写法则会直接调用java命令 ### USER - 切换用户,其后的命令都将以该用户执行 @@ -149,8 +240,11 @@ _docker build_ ### ARG > 用来指定一些镜像中使用的参数,例如版本信息 -- `ARG [=]` -- 使用`docker build --build=-arg=` 来传入值 +- 定义 `ARG [=]` +- 使用 `$name` +- 传入 `docker build --build-arg=` + +ARG 指令有生效范围,如果在 FROM 指令之前指定,那么只能用于 声明到 FROM 指令中间的命令上。所以多阶段构建使用FROM分离变量 ### COPY > 当复制本地目录时,推荐使用copy @@ -177,19 +271,44 @@ _docker build_ ### STOPSIGNAL +### HEALTHCHECK +健康检查 ### ONBUILD - 注入下游镜像。如果生成的镜像是作为另一个镜像的基础镜像,则该指令定义了需要被执行的那些指令 + +### 中间镜像 + +```dockerfile + FROM imageA AS builder + RUN ... + + FROM imageB AS final + RUN ... + + # 可以引用builder镜像中的文件 + COPY --from=builder /path/to/a /app + ENTRYPOINT ["/app"] +``` ****************************************** -## Dockerfile案例 -- [alpine构建ssh](/Linux/Docker/alpine/Dockerfile) -- [docker-wordpress-nginx](https://github.com/eugeneware/docker-wordpress-nginx) -- [rails-meets-docker](https://github.com/gemnasium/rails-meets-docker) + +# Practice +> [docker: best-practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) - [官方文档 dockerfile](https://www.docker.io/learn/dockerfile/) - [官方文档 builder](http://docs.docker.io/reference/builder/) +> [Reducing Your Docker Image Size](https://blog.codeship.com/reduce-docker-image-size/) + +## dockerignore文件的使用 +- .dockerignore文件是依据 Go 的 PathMatch 规范来的,使用和.gitignore类似 + +## Exercises +- [alpine构建ssh](/Linux/Docker/alpine/Dockerfile) +- [docker-wordpress-nginx](https://github.com/eugeneware/docker-wordpress-nginx) +- [rails-meets-docker](https://github.com/gemnasium/rails-meets-docker) + ### 打包最新版git - 注意其运行环境是容器内,不是宿主机,入口点的命令运行完了就退出了,不能当成宿主机上的git使用,只能说是学习一些操作 - 所以不可能说在容器中安装软件然后在宿主机上交互运行 @@ -214,3 +333,7 @@ _docker build_ USER mythos WORKDIR /home/mythos ``` +************************** +> [参考: 在 Docker 上开发应用 - 编写 Dockerfile 的最佳实践](https://blog.csdn.net/kikajack/article/details/79366043) | [英文原文](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) + + diff --git a/Linux/Container/DockerSoft.md b/Linux/Container/DockerSoft.md index 5fbdb1c..bb8ec50 100644 --- a/Linux/Container/DockerSoft.md +++ b/Linux/Container/DockerSoft.md @@ -1,65 +1,76 @@ -`目录 start` - -- [使用Docker安装软件](#使用docker安装软件) - - [个人镜像](#个人镜像) - - [常用镜像](#常用镜像) -- [系统](#系统) - - [Ubuntu-ssh](#ubuntu-ssh) - - [alpine-ssh](#alpine-ssh) - - [Centos-ssh](#centos-ssh) -- [语言环境](#语言环境) - - [Java](#java) - - [Jib](#jib) -- [数据库](#数据库) - - [PostgreSQL](#postgresql) - - [Oracle](#oracle) - - [MySQL](#mysql) - - [Redis](#redis) -- [持续集成](#持续集成) - - [flow.ci](#flowci) - - [Jenkins](#jenkins) -- [工具](#工具) - - [数据协议工具](#数据协议工具) - - [Protobuf](#protobuf) - - [git服务器](#git服务器) - - [简易git-daemon](#简易git-daemon) - - [Gogs](#gogs) - - [Gitea](#gitea) - - [配置](#配置) - - [在线IDE](#在线ide) - - [Coding平台的WebIDE](#coding平台的webide) - - [图形化管理工具](#图形化管理工具) - -`目录 end` |_2018-09-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Docker的应用 +date: 2018-12-15 11:27:31 +tags: + - 工具使用经验 +categories: + - Docker +--- + +💠 + +- 1. [Docker应用](#docker应用) + - 1.1. [个人镜像](#个人镜像) + - 1.2. [Linux发行版](#linux发行版) + - 1.2.1. [Ubuntu-ssh](#ubuntu-ssh) + - 1.2.2. [Alpine-ssh](#alpine-ssh) + - 1.2.3. [Centos-ssh](#centos-ssh) + - 1.3. [Java](#java) + - 1.3.1. [Local](#local) + - 1.3.2. [Jib](#jib) + - 1.4. [NodeJs](#nodejs) + - 1.5. [中间件](#中间件) + - 1.6. [数据库](#数据库) + - 1.6.1. [PostgreSQL](#postgresql) + - 1.6.2. [Oracle](#oracle) + - 1.6.3. [MySQL](#mysql) + - 1.6.4. [MongoDB](#mongodb) + - 1.6.5. [Redis](#redis) + - 1.7. [持续集成](#持续集成) + - 1.7.1. [flow.ci](#flowci) + - 1.7.2. [Jenkins](#jenkins) + - 1.8. [git服务器](#git服务器) + - 1.8.1. [简易git-daemon](#简易git-daemon) + - 1.8.2. [Gogs](#gogs) + - 1.8.3. [Gitea](#gitea) + - 1.8.3.1. [配置](#配置) + - 1.9. [在线IDE](#在线ide) +- 2. [图形化应用](#图形化应用) +- 3. [工具](#工具) + - 3.1. [nextcloud](#nextcloud) + - 3.2. [Protobuf](#protobuf) + +💠 2024-04-15 11:04:31 **************************************** -# 使用Docker安装软件 +# Docker应用 +> [https://docs.docker.com/samples/](https://docs.docker.com/samples/) +> [Docker Hub: explore](https://hub.docker.com/explore/) + +- [Official: Docker in docker](https://hub.docker.com/_/docker/) +- [Official: registry](https://hub.docker.com/_/registry/) + > [如何创建尽可能小的Docker容器教程](http://www.open-open.com/lib/view/open1419760974078.html) +> [参考: 一次 Docker 容器内大量僵死进程排查分析](https://juejin.im/post/5e0002adf265da33dc7a3a1f?from=singlemessage) ## 个人镜像 `百度云` -- 配置好SSH服务器的 alpine 3.6 `但是很多人不建议这么做,所以简单的 docker exec 登录就行了` - - [docker hub地址](https://hub.docker.com/r/mythkuang/alpine-ssh/) | 百度镜像源: `hub.baidubce.com/mythos/alpine-ssh:1.0` - 1. 使用上, pull, 然后 `docker run --name sshd -p 8989:22 alpine-ssh` +- 配置好SSH服务器的 alpine 3.6 | [docker hub地址](https://hub.docker.com/r/mythkuang/alpine-ssh/) | 百度镜像源: `hub.baidubce.com/mythos/alpine-ssh:1.0` + 1. `docker run --name sshd -p 8989:22 hub.baidubce.com/mythos/alpine-ssh:1.0` 1. 设置root用户密码 `docker exec -it sshd passwd` 1. 登录 `ssh -p 8989 root@localhost` - Jenkins 镜像 `hub.baidubce.com/mythos/jenkins:2.138.1` -- redis的alpine版镜像 `hub.baidubce.com/mythos/redis-alpine:1211` - -- idea的注册机 `hub.baidubce.com/mythos/idea-register:1.0` - - protobuf的编译环境以及2.5的源码在内 `hub.baidubce.com/mythos/protoc-alpine-src:2.5` - protobuf 的 Alpine 的 2.5版本 `hub.baidubce.com/mythos/protoc-alpine:2.5` - protobuf 的 Ubuntu 的 2.5版本 `hub.baidubce.com/mythos/protoc:2.5` - protobuf 的 Alpine 的 3.5.1版本 `hub.baidubce.com/mythos/protoc-alpine:3.5.1` -## 常用镜像 -- jdk - - `frolvlad/alpine-oraclejdk8 slim` *********************************** -# 系统 -## Ubuntu-ssh +## Linux发行版 +> 只适合自己折腾, 不应该用于应用的镜像, 应用不该开放ssh + +### Ubuntu-ssh - 最为简单的是:`docker run -i -t --name ubuntu17 -p 34433:22 ubuntu /bin/bash` - 为这些软件预留端口 `ssh tomcat mysql postgresql mysql oracle nginx reids` - 直接跑一个Ubuntu出来,预留出要用的端口,容器运行不会退出 @@ -68,93 +79,143 @@ - 最好是一个服务(应用)一个容器 ********** -- [ ] 日后更新, Dockerfile现在还有bug -- 自己写构建文件,安装相应的软件 ```Dockerfile - FROM ubuntu - MAINTAINER kuangcp myth.kuang@gmail.com - ENTRYPOINT echo "Welcome login server by ssh" - ENV DEBIAN_FRONTEND noninteractive - - ADD id_rsa.pub /root/.ssh/authorized_keys - - RUN apt-get update; \ - apt-get install -y apt-utils debconf-utils iputils-ping wget curl mc htop ssh; \ - chmod 700 /root/.ssh; \ - chmod 600 /root/.ssh/authorized_keys; \ - service ssh start; \ - EXPOSE 22 -``` -- `docker build . -t myth:ssh` -- `docker run -d -t --name myth -p 8989:22 myth:ssh` -- `docker start myth` +FROM ubuntu +ENV DEBIAN_FRONTEND noninteractive + +ADD id_rsa.pub /root/.ssh/authorized_keys -## alpine-ssh -- [alpine-ssh](/Linux/Docker/alpine/alpine-ssh) +RUN apt-get update; \ + apt-get install -y apt-utils debconf-utils iputils-ping wget curl htop ssh tini; + +RUN chmod 700 /root/.ssh; \ + chmod 600 /root/.ssh/authorized_keys; + +ENTRYPOINT ["/usr/bin/tini", "--"] + +EXPOSE 22 +``` +1. `mkdir tmp && cd tmp && cp ~/.ssh/id_rsa.pub .` 复制公钥 +1. `docker build . -t myth:ssh` 构建镜像 +1. `docker run -i -t --name myth -p 30001:22 myth:ssh /bin/bash` +1. `docker start myth` +每次启动容器需要手动执行 service ssh start + +### Alpine-ssh +- [dockerfile: alpine-ssh](https://github.com/Kuangcp/DockerfileList/blob/master/alpine/alpine-ssh.dockerfile) - 也可以使用百度云镜像 `docker pull hub.baidubce.com/mythos/alpine-ssh:1.0` -## Centos-ssh +### Centos-ssh - [centos-ssh](https://github.com/jingniao/centos-ssh) -******************************* -# 语言环境 +************************ + ## Java -- 基础镜像: - - [官方镜像](https://hub.docker.com/_/java/) +- [Official: Java](https://hub.docker.com/_/java/) `Oracle` | [Official: OpenJDK](https://hub.docker.com/_/openjdk/)`从7开始` + +- [frolvlad alpine-java](https://hub.docker.com/r/frolvlad/alpine-java)`非常精简` + - `个人基于以上镜像 设置好CST中国时区`[jdk-alpine-cst](https://hub.docker.com/r/mythkuang/jdk-alpine-cst/) + - `可以学习一波Dockerfile` [Github: Dockerfile](https://github.com/frol/docker-alpine-java) + +- Java7 `docker pull java:7u121-jdk-alpine` +- Java8 `docker pull frolvlad/alpine-java:jdk8.202.08-slim` 或者配置好时区的镜像 `mythkuang/jdk-alpine-cst:8.181` -> [参考博客: Java和Docker限制的那些事儿](http://www.techug.com/post/java-and-docker-memory-limits.html)`描述了一个天坑` +> `Tips` +1. docker run 时加上 `--cap-add=SYS_PTRACE` 解决 jmap -heap 1 时报错: Can't attach to the process: ptrace + +1. [参考: 使用Docker 实现微服务并搭建博客,一文全掌握。 ](https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650121506&idx=1&sn=39e3ba8c5d9698bbfb8acfc6b7e772bf&chksm=f36bb803c41c3115371b69cbd1e626fcaf5a85c7034f96fe495cfbf6dc1630a42dfdd6e342da&mpshare=1&scene=1&srcid=06219wgtCPJNvZP66ccQXRCj#rd) + +### Local +> env.sh +```sh +JAVA_HOME=/path/to +export JRE_HOME=${JAVA_HOME}/jre +export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib +export PATH=${JAVA_HOME}/bin:$PATH +``` + +```Dockerfile +FROM myth:ssh + +ADD *.tgz jdk.tgz +ADD env.sh +RUN apt install vim unzip ; \ + cat env.sh >> /root/.bashrc; + +EXPOSE 22 + +ENTRYPOINT ["/usr/bin/tini", "--"] +``` ### Jib -> [参考博客: GOOGLE JIB](https://my.oschina.net/u/3666671/blog/1845065) | [Github:jib](https://github.com/GoogleContainerTools/jib) +> [参考: GOOGLE JIB](https://my.oschina.net/u/3666671/blog/1845065) | [Github:jib](https://github.com/GoogleContainerTools/jib) + +## NodeJs +- [Official](https://hub.docker.com/_/node/) +- [Dockerizing a Node.js web app](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/) + +********************************** + +## 中间件 + +- [Nacos](https://nacos.io/en-us/docs/quick-start-docker.html) ********************************** -# 数据库 -## PostgreSQL -- [Docker 安装 PostgreSQL](/Database/Postgresql.md) -## Oracle +## 数据库 +### PostgreSQL +- [Docker 安装 PostgreSQL](/Database/PostgreSQL.md) + +### Oracle - [社区文档](https://hub.docker.com/r/wnameless/oracle-xe-11g/)`简单粗暴` -## MySQL -- [官方文档](https://hub.docker.com/_/mysql/) -- `docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag` -- 挂载自定义配置文件 主要是配置编码 以及设定时区 - - `docker run --name mysql-5.6 -v 配置文件目录:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=mythos1104 -e TZ=Asia/Shanghai -p 3360:3306 -d mysql:5.6` -- 连接`mysql -h 127.0.0.1 -P 3360 -uroot -pmythos1104` +### MySQL +- [Docker hub: Mysql](https://hub.docker.com/_/mysql/) + +- 简单启动 `docker run --name some-mysql -p 3360:3306 -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag` + - 容器中默认配置文件为 `/etc/mysql/conf.d/docker.cnf` + +- 或者挂载自定义配置文件 主要是配置编码 以及设定时区 + - `docker run --name mysql-5.7 -v 配置文件目录:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=mythos1104 -e TZ=Asia/Shanghai -p 3360:3306 -d mysql:5.7` + +- 连接 `mysql -h 127.0.0.1 -P 3360 -uroot -pmythos1104` + +> 最简单方式, 前提是已经安装好 docker-compose -## Redis -> [官方镜像地址](https://hub.docker.com/_/redis/) +1. [Github](https://github.com/Kuangcp/DockerfileList/tree/master/docker-compose/mysql)`克隆项目,在该目录下执行命令` + - `docker-compose up -d` 既可创建 正确时区, utf8编码的数据库 -- 获取镜像:`docker pull redis ` 如果使用`redis:alpine`镜像可以更小,但是稳定就。。 -- 运行默认配置的容器:`docker run --name test-redis -d redis` -- 使用本地配置文件启动redis容器 -- `sudo docker run -v /myredis/conf/redis.conf:/usr/local/etc/redis/redis.conf --name myredis redis redis-server /usr/local/etc/redis/redis.conf` -- port-redis容器的端口映射:`sudo docker run -d -p 6379:6379 --name port-redis redis` 左本机右容器 +### MongoDB +> [Official](https://hub.docker.com/_/mongo/) + +### Redis +> [Official](https://hub.docker.com/_/redis/) + +1. 获取镜像:`docker pull redis ` 使用 `docker pull redis:alpine` 镜像可以更小 +1. 运行默认配置的容器:`docker run --name test-redis -d redis` +1. 或者 使用本地配置文件启动redis容器 + - `sudo docker run -v /myredis/conf/redis.conf:/usr/local/etc/redis/redis.conf --name myredis redis redis-server /usr/local/etc/redis/redis.conf` + +- `数据迁移(RDB方式)` 复制rdb文件到容器内 /data/dump.rdb , kill -9 redis进程,启动 redis ***************************************** -# 持续集成 +## 持续集成 + +> [参考: 如何Docker化端到端验收测试](https://www.tuicool.com/articles/YZJzAzF) -> [参考博客: 如何Docker化端到端验收测试](https://www.tuicool.com/articles/YZJzAzF) -## flow.ci +### flow.ci - [flow.ci](https://github.com/flowci/docker) 可以学习compose ****************** -## Jenkins +### Jenkins > [详情](/Skills/DevOps/Jenkins.md#docker) -**************************** -# 工具 -## 数据协议工具 -### Protobuf -1. 创建一个Ubuntu/alpine 容器运行起来 -1. 下载 https://github.com/google/protobuf/releases -2. 安装 g++ make -4. 编译安装下载的源码 进入目录 `./configure --prefix=/usr && make && make check && make install` +************************ ## git服务器 ### 简易git-daemon -> 基于git-daemon构建一个Docker镜像, 跑起来直接做git服务器 | [学习使用git-daemon命令](/Linux/Git_Action.md) +> 基于git-daemon构建一个Docker镜像, 跑起来直接做git服务器 | [学习使用git-daemon命令](/Skills/Vcs/GitAction.md#使用-git-daemon-搭建简易-server) ```sh # 创建一个挂载了本地文件夹的git仓库,并关联到nginx,目录结构和上文一致 @@ -167,6 +228,8 @@ # 启动服务 git daemon --export-all --base-path="/root/Repository" --port=55443 ``` +> 通过 daemon 能下拉提交代码, nginx 能在线浏览文件 + ### Gogs ### Gitea @@ -176,22 +239,43 @@ #### 配置 > 配置SSH -只要没有禁用掉SSH, 就能和Github一样使用SSH操作仓库, 但是,由于使用的Docker, 所以端口不是默认的22 -所以在本机需要配置下 例如我的就是 6002映射到了22上 + +只要没有禁用掉SSH, 就能和Github一样使用SSH操作仓库, 为了避免其他进程的端口冲突, 单独设置端口 例如: 6002映射到了22上 `~/.ssh/config` ```conf -Host git.kuangcp.com -HostName 111.111.111.111 -User git -Port 6002 -IdentityFile /home/kcp/.ssh/id_rsa + Host git.kuangcp.com + HostName 111.111.111.111 + User git + Port 6002 + IdentityFile /home/kcp/.ssh/id_rsa ``` 然后就能正常使用了 +************************ + ## 在线IDE -### Coding平台的WebIDE +- Coding平台的WebIDE +- eclipse che + +- [coder-sever](https://github.com/codercom/code-server)`BS模式的VSCode` + +************************ + +# 图形化应用 +> [Github Topic](https://github.com/search?p=4&q=docker+desktop&type=Repositories&utf8=%E2%9C%93) +> [Running GUI apps with Docker](https://www.tuicool.com/articles/ayIzI3) +> [在Docker for Windows中运行GUI程序 ](https://www.cnblogs.com/larva-zhh/p/10531824.html) -## 图形化管理工具 -DockerUI -- [Portainer](https://github.com/portainer/portainer) +************************ +# 工具 +## nextcloud +> [nextcloud](https://nextcloud.com/install/) + +`docker run -d --name nextcloud -p 8008:80 -v /data/nextcloud:/var/www/html nextcloud` + +## Protobuf +1. 创建一个Ubuntu/alpine 容器运行起来 +1. 下载 [Github Release](https://github.com/google/protobuf/releases) +2. 安装 g++ make +4. 编译安装下载的源码 进入目录 `./configure --prefix=/usr && make && make check && make install` diff --git a/Linux/Container/Helm.md b/Linux/Container/Helm.md new file mode 100644 index 0000000..607dc41 --- /dev/null +++ b/Linux/Container/Helm.md @@ -0,0 +1,17 @@ +--- +title: Helm +date: 2019-05-19 23:30:37 +tags: +categories: +--- + +**目录 start** + +1. [Helm](#helm) + +**目录 end**|_2021-02-03 17:25_| +**************************************** +# Helm +> [Github](https://github.com/helm/helm) + +> [参考: 利用 Helm 简化应用部署](https://help.aliyun.com/document_detail/58587.html) diff --git a/Linux/Container/K3s.md b/Linux/Container/K3s.md new file mode 100644 index 0000000..47cacc2 --- /dev/null +++ b/Linux/Container/K3s.md @@ -0,0 +1,34 @@ +--- +title: K3s +date: 2023-10-26 00:19:02 +tags: +categories: +--- + +💠 + +- 1. [K3s](#k3s) + - 1.1. [安装&配置](#安装&配置) + - 1.1.1. [单机安装](#单机安装) + +💠 2023-10-26 00:19 +**************************************** +# K3s + +## 安装&配置 +### 单机安装 +> [单机部署k3s](https://jasonkayzk.github.io/2022/10/21/%E5%8D%95%E6%9C%BA%E9%83%A8%E7%BD%B2k3s/) + +```sh + curl -sfL https://get.k3s.io | sh - + # 国内使用镜像源加速 + curl -sfL https://rancher-mirror.oss-cn-beijing.aliyuncs.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh - +``` + +- k3s 安装完成后会将 kubeconfig 文件写入到/etc/rancher/k3s/k3s.yaml,对于有多个集群的来说,需要配置这个; +- 同时 K3S_TOKEN 会存在你的服务器节点上的/var/lib/rancher/k3s/server/node-token路径下; +- 安装后会生成卸载脚本: `/usr/local/bin/k3s-uninstall.sh` + +> [k3s部署kuboard面板](https://www.cnblogs.com/sstu/p/16760138.html) + +添加Dockerhub作为ImagePullSecret: server字段配置为 https://docker.io \ No newline at end of file diff --git a/Linux/Container/Kubernetes.md b/Linux/Container/Kubernetes.md index 1ab00a5..be4c536 100644 --- a/Linux/Container/Kubernetes.md +++ b/Linux/Container/Kubernetes.md @@ -1,47 +1,101 @@ -`目录 start` - -- [Kubernetes](#kubernetes) - - [相关博客](#相关博客) - - [安装](#安装) - - [简单使用](#简单使用) - - [容器编排](#容器编排) - -`目录 end` |_2018-09-10_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Kubernetes +date: 2018-12-15 11:28:19 +tags: + - 基础 +categories: + - Kubernetes +--- + +💠 + +- 1. [Kubernetes](#kubernetes) + - 1.1. [相关博客](#相关博客) + - 1.2. [安装](#安装) + - 1.2.1. [minikube](#minikube) +- 2. [使用](#使用) + - 2.1. [网络](#网络) + - 2.2. [实践](#实践) +- 3. [安全](#安全) + +💠 2024-09-06 11:36:43 **************************************** + # Kubernetes -> 又称K8s [Official site](https://kubernetes.io/) | [Github:](https://github.com/kubernetes/kubernetes) +> [Official site](https://kubernetes.io/) | [Github](https://github.com/kubernetes/kubernetes) | [中文文档](https://kubernetes.io/zh-cn/docs/concepts/) + +> [kwok](https://github.com/kubernetes-sigs/kwok) `模拟 K8s 集群的工具。它可以在几秒钟内搭建一个由数千个节点组成的 Kubernetes 集群` ## 相关博客 -> [浅谈k8s+docker 资源监控](https://segmentfault.com/a/1190000003898140) | [基于Kubernetes构建Docker集群管理详解](http://www.csdn.net/article/2014-12-24/2823292-Docker-Kubernetes) -[Kubernetes 学习笔记 ](http://wdxtub.com/2017/06/05/k8s-note/) -> [参考博客: Kubernetes会不会被自身的复杂性压垮?](http://www.infoq.com/cn/articles/will-kubernetes-collapse-under-the-weight-of-its-complexity) +> [浅谈 k8s+docker 资源监控](https://segmentfault.com/a/1190000003898140) | [基于 Kubernetes 构建 Docker 集群管理详解](http://www.csdn.net/article/2014-12-24/2823292-Docker-Kubernetes) +> [Kubernetes 学习笔记 ](http://wdxtub.com/2017/06/05/k8s-note/) +> [Kubernetes 中文社区](https://www.kubernetes.org.cn/doc-45) +> [Kubernetes 使用教程](https://github.com/chaseSpace/k8s-tutorial-cn) +> [参考: Kubernetes 会不会被自身的复杂性压垮?](http://www.infoq.com/cn/articles/will-kubernetes-collapse-under-the-weight-of-its-complexity) +> [参考: 一文带你看透 kubernetes 容器编排系统](https://my.oschina.net/qcloudcommunity/blog/2998211) ## 安装 -> [official doc](https://kubernetes.io/docs/tasks/tools/install-kubectl/) - -> [参考博客: kubeadm 搭建 kubernetes 集群](https://mritd.me/2016/10/29/set-up-kubernetes-cluster-by-kubeadm/) -> [参考博客: Kubernetes国内镜像、下载安装包和拉取gcr.io镜像](https://blog.csdn.net/nklinsirui/article/details/80581286) +> [official doc](https://kubernetes.io/docs/tasks/tools/install-kubectl/) -> [参考博客: 国内服务器安装kubernetes一路坑,求大神指点 ](http://dockone.io/question/1225#!answer_form) +> [参考: kubeadm 搭建 kubernetes 集群](https://mritd.me/2016/10/29/set-up-kubernetes-cluster-by-kubeadm/) +> [参考: Kubernetes 国内镜像、下载安装包和拉取 gcr.io 镜像](https://blog.csdn.net/nklinsirui/article/details/80581286) +> [参考: 国内服务器安装 kubernetes 一路坑,求大神指点 ](http://dockone.io/question/1225#!answer_form) -- **注意** Deepin上不要安装 kubernetes-client 这个是 1.7 版本, 类似于 docker.io 这样的老旧版本 +- **注意** Deepin 上不要安装 kubernetes-client 这个是 1.7 版本, 类似于 docker.io 这样的老旧版本 > 使用阿里云的镜像进行安装 + ```sh -# 均以 root 运行 -apt update && apt install -y apt-transport-https + # 均以 root 运行 + apt update && apt install -y apt-transport-https + curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - + echo "deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list + apt install kubelet kubeadm kubectl +``` +> `deb http://apt.kubernetes.io/ kubernetes-xenial main` 虽然这才是官方源,奈何是 Google 服务器 -curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - +************************ -echo "deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main" > /etc/apt/sources.list.d/kubernetes.list +### minikube +> [minikube](https://minikube.sigs.k8s.io/docs/start/) -apt install kubelet kubeadm kubectl -``` -> `deb http://apt.kubernetes.io/ kubernetes-xenial main` 虽然这才是官方源,奈何是Google服务器 +************************ + +# 使用 +> 大多数命令和 Docker 是类似的,只不过加上了 namespace 的概念 + +- 查看日志: kubectl logs --namespace namespace pod + +> [Java client for Kubernetes](https://github.com/fabric8io/kubernetes-client) -## 简单使用 +## 网络 +> [Kubernetes 疑难杂症排查分享:神秘的溢出与丢包 ](https://tencentcloudcontainerteam.github.io/2020/01/13/kubernetes-overflow-and-drop/) + +## 实践 +> Pod调度资源倾斜 +- [你真的理解 K8s 中的 requests 和 limits 吗?](https://kubesphere.io/zh/blogs/deep-dive-into-the-k8s-request-and-limit) | [K8S: QoS](https://kubernetes.io/docs/tasks/configure-pod-container/quality-service-pod/) +- [管理容器的计算资源](https://kuboard.cn/learning/k8s-intermediate/config/computing-resource.html) +- [Kubernetes 节点标签和定向调度](http://zongming.net/read-1333/) + +结论:pod尽量按过往监控的情况设置合理的 requests 和 limits, 如果仍有明显的倾斜,可以配置节点亲和 nodeAffinity + +例如如下配置只会将pod调度到03和04两个Node上, 对应于Kuboard的操作路径为 高级设置 -> 节点调度策略 -> 根据【节点亲和性】选择节点 -> 选择标签和值 +```yml + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - node03 + - node04 +``` -## 容器编排 \ No newline at end of file +# 安全 +> [从零开始的Kubernetes攻防](https://github.com/neargle/my-re0-k8s-security) diff --git a/Linux/Container/Readme.md b/Linux/Container/Readme.md new file mode 100644 index 0000000..93d9b4e --- /dev/null +++ b/Linux/Container/Readme.md @@ -0,0 +1,4 @@ +# 容器化时代 +Docker创造性出世,利用cgroup namespace隔离提出了容器的概念,后续才发展出了丰富的体系。 + +> [Kubeless](https://kubeless.io/docs/quick-start/) diff --git a/Linux/Container/Vagrant.md b/Linux/Container/Vagrant.md new file mode 100644 index 0000000..94a0a8e --- /dev/null +++ b/Linux/Container/Vagrant.md @@ -0,0 +1,17 @@ +--- +title: Vagrant +date: 2021-02-03 17:28:18 +tags: +categories: +--- + +**目录 start** + +1. [Vagrant](#vagrant) + +**目录 end**|_2021-02-03 17:28_| +**************************************** +# Vagrant +[Official](https://www.vagrantup.com) + +基于虚拟化平台和描述性文件Vagrantfile 类似于 Dockerfile 实现开发环境完全一致(内核和操作系统级别) diff --git a/Linux/DE/Gnome.md b/Linux/DE/Gnome.md new file mode 100644 index 0000000..2d50ebd --- /dev/null +++ b/Linux/DE/Gnome.md @@ -0,0 +1,17 @@ +--- +title: Gnome +date: 2019-10-19 16:47:01 +tags: + - 桌面环境 +categories: + - Linux +--- + +**目录 start** + +1. [Gnome](#gnome) + +**目录 end**|_2022-09-14 13:34_| +**************************************** +# Gnome +> [www.gnome-look.org](https://www.gnome-look.org/) diff --git a/Linux/DE/KDE.md b/Linux/DE/KDE.md new file mode 100644 index 0000000..8e2d111 --- /dev/null +++ b/Linux/DE/KDE.md @@ -0,0 +1,16 @@ +--- +title: KDE +date: 2019-10-19 16:47:01 +tags: + - 桌面环境 +categories: + - Linux +--- + +**目录 start** + +1. [KDE](#kde) + +**目录 end**|_2022-09-14 13:34_| +**************************************** +# KDE diff --git a/Linux/DE/Xfce.md b/Linux/DE/Xfce.md new file mode 100644 index 0000000..021955b --- /dev/null +++ b/Linux/DE/Xfce.md @@ -0,0 +1,47 @@ +--- +title: Xfce +date: 2019-10-19 16:47:01 +tags: + - 桌面环境 +categories: + - Linux +--- + +💠 + +- 1. [Xfce](#xfce) + - 1.1. [Panel](#panel) + - 1.1.1. [Plugin](#plugin) +- 2. [Tips](#tips) + +💠 2024-09-14 15:32:50 +**************************************** +# Xfce +> [www.xfce-look.org](https://www.xfce-look.org) + +1. notify-send 发送新通知 [使用notify-send发送桌面通知](https://blog.csdn.net/lujun9972/article/details/53292620) + - notify-send -i iconFilePath title detail -t 1500 + - -A + - `notify-send Tips -A 1=Active -A 0=Reject` 消息弹窗带按钮(Active和Reject) 命令的返回是按钮的值 1或0 + - `notify-send Tips -A Active -A Reject` 不指定值默认返回下标,即返回0或1 + - -t 指定通知过期时长 单位s + - -p 打印出通知的id + - `-r id` 当前通知替换掉指定id的通知 + +1. xflock4 锁屏 +1. zenity GTK实现,各种输入组件,弹窗提示 + +[xfce4 install xrdp](https://learn.microsoft.com/en-us/azure/virtual-machines/linux/use-remote-desktop?tabs=azure-cli) + +## Panel + +### Plugin +> [Xfce Panel Plugins How To](https://wiki.xfce.org/dev/howto/panel_plugins) +> [Create my own panel plugin](https://askubuntu.com/questions/633952/create-my-own-panel-plugin-xubuntu) + +`可参考项目` +> [xfce4-weather-plugin](https://gitlab.xfce.org/panel-plugins/xfce4-weather-plugin) +> [xfce4-sensors-plugin](https://launchpad.net/ubuntu/+source/xfce4-sensors-plugin) + +# Tips +1. Dock相关软件 出现阴影 `Window Manager Tweaks -> Compositor -> 关闭 Show shadow under dock windows` diff --git a/Linux/Debian/Debian.md b/Linux/Debian/Debian.md index d21e68c..57abb49 100644 --- a/Linux/Debian/Debian.md +++ b/Linux/Debian/Debian.md @@ -1,20 +1,107 @@ -`目录 start` - -- [Debian](#debian) - - [Debain8](#debain8) - - [作为服务器系统](#作为服务器系统) - - [配置](#配置) - - [配置语言环境](#配置语言环境) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Debian基础 +date: 2018-12-15 11:29:14 +tags: + - Debian +categories: + - Linux +--- + +**目录 start** + +1. [Debian](#debian) + 1. [配置](#配置) + 1. [配置语言环境](#配置语言环境) + 1. [软件管理](#软件管理) + 1. [软件源列表](#软件源列表) + 1. [包管理器](#包管理器) + 1. [源码编译安装](#源码编译安装) + +**目录 end**|_2021-02-03 17:25_| **************************************** -# Debian +# Debian +> 很古老但是很好用的系统 [官网](https://www.debian.org/index.zh-cn.html) + +> [参考: Debian8最小安装](https://www.howtoforge.com/tutorial/debian-8-jessie-minimal-server/) +- 奇怪的是我在虚拟机里装了好几个好几次装不上, 装完一登录就只有壁纸 + +_服务器_ +- 2018-04-01 17:19:50 作为服务器系统安装完Debian8.2 85M内存占用 docker 是1.6 +- 2018-04-10 10:35:54 服务器安装Ubuntu16.04 71M内存 docker是1.13 -## Debain8 -### 作为服务器系统 -#### 配置 -##### 配置语言环境 +## 配置 +### 配置语言环境 1. apt install locales 2. dpkg-reconfigure locales 3. 进入选择界面 `zh_CN.UTF-8 UTF-8` 空格选择, 换行 继续 + +## 软件管理 +### 软件源列表 +- apt 的默认配置文件是 `/etc/apt/source.list` + - 以及 sources.list.d/ 目录下的 *.list 文件 (最好将list文件都进行备份 备份文件为 *.save) + +- [参考博客 阿里云的软件源](https://hacpai.com/article/1482807364546?p=1&m=0) +- [wiki-源列表说明](http://wiki.ubuntu.com.cn/%E6%BA%90%E5%88%97%E8%A1%A8) + +1. 源 URL 后的单词: + 1. main: 完全的自由软件。 + 1. restricted: 不完全的自由软件。 + 1. universe: Ubuntu官方不提供支持与补丁,全靠社区支持。 + 1. multiverse: 非自由软件,完全不提供支持和补丁。 + +1. 添加私有源ppa + - 若不能添加私有源ppa: + - debain:`sudo apt install software-properties-common python-software-properties` + - Ubuntu `sudo apt install python-software-properties` + - 添加:`sudo add-apt-repository ppa:dotcloud/lxc-docker ` + - 删除ppa : `cd /etc/apt/sources.list.d/` 打开该目录下文件把对应的ppa的一行注释掉或删掉就行了 + + +1. 添加一个源列表 + - 例如添加 nginx: 新建文件 `/etc/apt/sources.list.d/nginx.list` + ``` + deb http://nginx.org/packages/mainline/debian/ jessie nginx + deb-src http://nginx.org/packages/mainline/debian/ jessie nginx + ``` + - `curl http://nginx.org/keys/nginx_signing.key | apt-key add -` + - 把签名添加进来才能正常 apt update + +### 包管理器 +**`dpkg`** +1. 查看已安装的应用 `dpkg --list` +1. 显示已安装包的详情 `dpkg -s package` +1. 安装deb包 + - ` sudo dpkg -i *.deb` + +**`apt-get / apt`** +1. `install 包名` 安装指定包的最新版 + - `-y` 参数可以省去确认 + - `-s` 模拟安装 + - `package=version` 安装指定版本的包 + +1. list 列出所有可安装的包 + - --installed 已安装的包 + - package 列出已安装的 该package 的信息 `加上 -a`: 所有版本 + +1. 只卸载程序,保留配置文件 `sudo apt remove 包名` + - 彻底卸载应用 `sudo apt--purge remove 包名` + - 若已经卸载, 清理配置: `apt purge 包名` (不会清理home/.config里的内容) + +- apt-cache showpkg/policy/madison/show package + - showpkg (特别详细) 列出所有版本以及来源, MD5 ... + - policy (基本信息) 列出所有版本以及来源 + - madison (简略显示) 内容同上 + - show 查询指定包的详情(已安装的版本信息) + - search 搜索包 + +**`snap`** +- [official doc: snap](https://snapcraft.io/docs/core/usage) `提供一个类似容器的环境,将所有依赖打包,隔离运行` + +### 源码编译安装 +1. make install 源代码安装 + - 1.解压缩 `tar -zxf nagios-4.0.2.tar.gz ` + - 2.进入目录 `cd nagios-4.0.2` + - 3.配置 `./configure --prefix=/usr/local/nagios ` + - 4.编译 `make all` + - 5.安装 `make install && make install-init && make install-commandmode && make install-config` diff --git a/Linux/Debian/Deepin.md b/Linux/Debian/Deepin.md new file mode 100644 index 0000000..8029e96 --- /dev/null +++ b/Linux/Debian/Deepin.md @@ -0,0 +1,120 @@ +--- +title: Deepin +date: 2019-10-19 17:04:29 +tags: + - Deepin +categories: + - Linux +--- + +**目录 start** + +1. [Deepin](#deepin) + 1. [关于显卡](#关于显卡) + 1. [电源管理](#电源管理) + 1. [系统休眠](#系统休眠) + 1. [双系统安装](#双系统安装) + +**目录 end**|_2021-02-03 17:25_| +**************************************** +# Deepin + +> [官方wiki](wiki.deepin.org) +> [参考: 一些工具](https://bbs.deepin.org/forum.php?mod=viewthread&tid=143022z) +- [FAQ](https://bbs.deepin.org/forum.php?mod=viewthread&tid=146921&extra=page%3D1) + +- 优点: + - 界面美观,自带CrossOver深度家族的软件也挺好用,自定义命令的快捷键 +- 缺点: + - 基本是Linux的共性了,就是驱动问题, NVIDIA 显卡 因为驱动问题重装四五次系统,重启就不知道多少次了 + - 输入法现在这几天也在作妖 fcitxCPU占用高,输入窗口消失等问题 + - 蓝牙模块时隐时现 + +`遇到的bug记录` +- 休眠结束系统卡死,然后重启输入法没有窗口 2018-01-09 19:29:25 + - 杀掉搜狗进程再启动解决 + +- 2018-03-15 09:25:47 + [公司电脑安装Windows10 和 Deepin双系统](/MyBlog/2018-3-15-install-deepin.md) + +- `Gtk-WARNING **: 无法在模块路径中找到主题引擎:“adwaita”` 2018-05-24 15:08:49 + - 安装 这个包 gnome-themes-standard + +- 2018-06-15 19:50:40 deepin-wm 进程, 也就是Deepin的桌面管理器, 启动久了之后就会发生内存占用非常大的情况, 关闭窗口特效, 再打开就好了 + +- 2018-08-21 20:34:07 更新到15.7, 然后就是一堆的小问题, 任务栏和屏幕边缘有空隙, 多任务切换方式的变化, 原先用Wine安装的企业QQ不能启动... 但是确实Deepin 现在更快了 + - 使用闭源驱动方案, 休眠一会就卡死了, 只能强制关机, 尝试了开源驱动后, 也是一样 显卡是 Nvidia GTX1050 + +- 2018-08-23 09:55:15 遭遇用过的最大问题, 笔记本升级到15.7后有显卡明显不兼容, 各种显示上的卡顿, 切换Prime解决方案后, 内核load不进来, 启动不了了 + - 配置是 显卡 NVIDIA 840m 也许重装Deepin15.7, 也许装Manjaro-KDE + - 最终是进的恢复模式, 卸载了无用的包就成功进入了, 但是发现自动挂载分区的文件都被注释了, 如果手动添加, 即使mount -a 没有报错, 但是启动时就加载不了分区 + - 又得进恢复模式注释掉, 才能进入系统 + +- 2018-09-02 21:44:21 ` Driver 'pcspkr' is already registered, aborting,` + >- [参考: 社区帖子](https://bbs.deepin.org/forum.php?mod=viewthread&tid=166517&highlight=pcspkr) + +- 2018-11-22 10:19:27 + - 升级到 15.8 后 xorg 和 deepin-wm 内存泄露, 显卡是 GTX1060x 笔记本的 820m 没有这种情况出现 + - 用上半天, 这俩内存能占用到 3个g + +- 2019-01-07 10:45:37 + - 因为公司周末断电,系统没有关机, 导致无法开机, 直接黑屏, 原因应该是突然断电导致文件系统不一致 + - 解决方案, 用U盘进系统, 挂载系统分区, 使用 fsck 工具修复文件系统 `fsck.ext4 -vy /dev/sdaXXX` + - 由于我装了三个系统, windows10 deepin manjaro, 所以直接进manjaro, 执行的命令 + +- deepin-wm 有内存泄露, 打算关闭开启窗口效果来解决, 但是关掉后就打不开了 failed to enable... 2019-03-20 17:20:07 + - [issue](https://github.com/linuxdeepin/developer-center/issues/444) + - `(killall deepin-wm-switcher; deepin-wm --replace &)` 这样就能守护进程方式在运行了 + +## 关于显卡 +> [参考: 显卡驱动作死录](https://www.jianshu.com/p/f53c8223bac6) + +> 个人折腾的整理 +当前系统为 Deepin15.7 已经支持多种解决方案了, 还有一个 `深度显卡驱动管理器` +1. Intel默认驱动(也就是集显) +1. NVIDIA开源驱动 性能不好, 解析闭源驱动而来 +1. 大黄蜂方案 采用闭源驱动, 省电 +1. PRIME方案 高性能 + +但是和我笔记本完美兼容的是 大黄蜂方案, 也就是之前安装的 `nvidia-driver`, `nvidia-setting`, `bumblebee-nvidia` 这一系列软件包, PRIME方案切换后差点把内核挂了, U盘进系统切换会原有方案才把系统救活了 + +> Enable Window effect 失败 +尝试切换显卡驱动方案为闭源驱动, 重启下就挂掉了, [社区相关问题](https://bbs.deepin.org/forum.php?mod=viewthread&tid=159333) +``` + Failed to find module 'mincores' + Failed to insert 'bbswitch': No such device +``` +最后的解决方案是从 4.2 内核启动, 切换回了开源驱动 +版本: Deepin15.9.1 , 不知道哪一个版本升级了内核, 而且新旧内核都保留下来了, 所以有两个内核 4.16 4.2 , 幸好有两个内核 + +## 电源管理 +- [电源管理状态](https://wiki.deepin.org/index.php?title=Power_management&language=en) + - 睡眠:电源只给内存供电, 内存中数据仍保持。 + - 休眠:内存中数据保存到硬盘中,电源全部断开。 + - 重启:内存数据清除, 冷启动系统 + - 关机:开启快速启动时,所有当前的用户数据关闭,只保存系统核心数据到硬盘中方便下次快速启动系统,电源全部断开; + - 关闭快速启动时,等同于重启。 + +### 系统休眠 +分为 睡眠 Suspend 和 休眠 Hibernate + +> [How to enable hibernate in Deepin? ](https://bbs.deepin.org/forum.php?mod=viewthread&tid=145013) +> [ PowerManagement/Hibernate](https://help.ubuntu.com/community/PowerManagement/Hibernate) + +> [Hibernate without swap partition](https://wiki.debian.org/Hibernation/Hibernate_Without_Swap_Partition)`仅使用交换文件达到休眠效果` +> [Hibernation](https://wiki.archlinux.org/index.php/Power_management/Suspend_and_hibernate#Hibernation_into_swap_file) +> [Hibernate from swap file](https://askubuntu.com/questions/6769/hibernate-and-resume-from-a-swap-file) + +## 双系统安装 +- 首先进入BIOS关闭 安全启动, 选择引导方式为Legacy关闭UEFI win8以上则要关闭快速启动, + - 制作启动U盘, 然后选择从U盘启动, 进行安装, 分区 / 和 /home / 30-40g就足够, 如果你所用的软件都习惯性解压运行的话 + - 安装完成后一般是Deepin的默认引导取代了winsows引导, 即可正常使用, 进入windows,Deepin的引导也有该入口 + - 如果想默认进windows, 那么修改BIOS 改回UEFI即可 + +- 固态加机械的电脑: + - 一样的关闭 安全启动, UEFI + - 在固态中划分出300M左右的空间出来, 在安装的时候设为 /Boot 然后将 / 和 /home照常放在机械上即可 + - 在启动时, 打开引导菜单, 选择固态即可正常启动Deepin + - 同样的修改BIOS 回 UEFI 就默认进WIndows了 + +> 但是有时候有的电脑打开UEFI也能正常安装, 所以装系统要大胆的尝试, Deepin安装没有造成过数据损失 diff --git a/Linux/Debian/Ubuntu.md b/Linux/Debian/Ubuntu.md index 31d4abb..c1cd5aa 100644 --- a/Linux/Debian/Ubuntu.md +++ b/Linux/Debian/Ubuntu.md @@ -1,12 +1,24 @@ -`目录 start` - -- [Ubuntu](#ubuntu) - - [Tips](#tips) +--- +title: Ubuntu +date: 2018-12-15 11:30:32 +tags: + - Ubuntu +categories: + - Linux +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [Ubuntu](#ubuntu) + 1. [Tips](#tips) + +**目录 end**|_2021-02-03 17:25_| **************************************** # Ubuntu +> 很多人的入门系统, 作为个人服务器也是首选, 软件比较新 + +> [Ubuntu Server Tutorial](https://tutorials.ubuntu.com/tutorial/tutorial-install-ubuntu-server#0) | [网易镜像源](http://mirrors.163.com/ubuntu-releases/)`只有网易有server版的镜像` ## Tips diff --git a/Linux/JavaDevInit.md b/Linux/JavaDevInit.md index 786e823..6584e40 100644 --- a/Linux/JavaDevInit.md +++ b/Linux/JavaDevInit.md @@ -1,31 +1,37 @@ -`目录 start` - -- [配置Deepin的Java开发环境](#配置deepin的java开发环境) - - [新增用户](#新增用户) - - [安装Docker](#安装docker) -- [在Linux上配置Java环境](#在linux上配置java环境) - - [配置JDK](#配置jdk) - - [解压配置](#解压配置) - - [sdkman方式](#sdkman方式) - - [mythsdk](#mythsdk) - - [配置MySQL](#配置mysql) - - [配置Redis](#配置redis) - - [从源码编译运行并测试](#从源码编译运行并测试) - - [问题以及解决方案:](#问题以及解决方案) - - [双硬盘的折腾记录](#双硬盘的折腾记录) - -`目录 end` |_2018-08-21_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Java初始化环境搭建 +date: 2018-12-15 12:01:26 +tags: + - 工具使用经验 +categories: +--- + +💠 + +- 1. [配置Deepin的Java开发环境](#配置deepin的java开发环境) + - 1.1. [新增用户](#新增用户) + - 1.2. [安装Docker](#安装docker) +- 2. [在Linux上配置Java环境](#在linux上配置java环境) + - 2.1. [配置JDK](#配置jdk) + - 2.1.1. [解压配置](#解压配置) + - 2.2. [配置MySQL](#配置mysql) + - 2.3. [配置Redis](#配置redis) + - 2.4. [问题以及解决方案](#问题以及解决方案) + +💠 2024-01-22 09:40:06 **************************************** # 配置Deepin的Java开发环境 -修改Hostname需要重启, 设置java默认需要重启, docker添加用户组需要重启 +修改Hostname需要重启, 设置默认java需要重启, docker添加用户组需要重启 ## 新增用户 -> [详细](/Linux/Base/LinuxBase.md#用户管理) +> [详细文档](/Linux/Base/LinuxBase.md#用户管理) ## 安装Docker -> [详细文档](/Linux/Container/Docker.md) +> [详细文档](/Linux/Container/Docker.md#安装与卸载) # 在Linux上配置Java环境 +> [SDK Man 方式安装](/Skills/AppManual.md#sdkman) + ## 配置JDK ### 解压配置 - [下载地址](http://www.oracle.com/technetwork/java/javase/downloads/index.html) @@ -49,51 +55,16 @@ > 后期更新JDK版本, 普通用户的话, 就只是需要更改 `.bashrc` 文件, root用户就执行以上命令, 或者直接重建软链接文件 >> root 用户下 `which java` 然后 `ls -l 显示的路径` 一直往下找, 找到 `/etc/alternatives/java` 和 `/etc/alternatives/javac` 重建这两个软链接. -### sdkman方式 -> jdk不推荐使用sdkman安装,这里的jdk是开源版估计,会少javafx等一些闭源包 Oracle版本才是完整的 -> 但是最近SDKMAN出了一个oracle的版本貌似是完整的,因为有个同意协议的过程 `sdk install java 8u144-oracle` - -- 安装sdkman `curl -s "https://get.sdkman.io" | bash` +************************ -``` - Looking for a previous installation of SDKMAN... - Looking for unzip... - Not found. - Please install unzip on your system using your favourite package manager. - Restart after installing unzip. -``` -- 遇到这种提示就是需要安装zip `sudo apt install zip unzip` 然后重新执行命令 -- 执行脚本:`source "/home/kuang/.sdkman/bin/sdkman-init.sh"` 或者重启终端就可以使用 `sdk`命令了 -- 查看sdkman 版本 :`sdk version` -- 查看可用版本 `sdk list java` -- 不指定版本就是安装最新版 `sdk install java` -- 指定默认版本 `sdk default java 8u131-zulu` -- 验证是否成功:`java -version` - -### mythsdk -> 个人用py开发的脚本, 实现了和sdkman一致的内容, 并且很简单 | [使用文档](https://github.com/Kuangcp/Script/tree/master/python/mythsdk) - -******************************** ## 配置MySQL -> [安装MySQL](/Database/MySQL.md) +> [安装MySQL](/Database/MySQL.md#安装) -************************************** +************************ ## 配置Redis -> [安装Redis](/Database/Redis.md) +> [安装Redis](/Database/Redis.md#安装和配置) -### 从源码编译运行并测试 -> 新建文件夹将源码下拉下来运行,然后就可以将该目录删除 - -```sh - wget http://downloads.sourceforge.net/tcl/tcl8.6.1-src.tar.gz - sudo tar xzvf tcl8.6.1-src.tar.gz -C /usr/local/ - cd /usr/local/tcl8.6.1/unix/ - sudo ./configure - sudo make - sudo make install -``` -************** -## 问题以及解决方案: +## 问题以及解决方案 > QQ - `sudo apt-get install deepin-crossover deepinwine-qq` - [安装QQ](https://www.findhao.net/easycoding/1748) @@ -107,5 +78,5 @@ 虽然各种小bug, 也花费了很多时间来解决这些问题(因为自己有强迫症), 但是还是学到了很多东西 ******************** -## 双硬盘的折腾记录 -> [记录](/MyBlog/2018-3-15-install-deepin.md) + +> [双硬盘的折腾记录](/MyBlog/2018-3-15-install-deepin.md) diff --git a/Linux/LinuxReadme.md b/Linux/LinuxReadme.md new file mode 100644 index 0000000..95ee1b7 --- /dev/null +++ b/Linux/LinuxReadme.md @@ -0,0 +1,28 @@ +# Linux相关知识 + +* [ Linux基础 ](/Linux/Base/LinuxBase.md) +* [ Linux文件管理 ](/Linux/Base/LinuxFile.md) +* [ Linux处理压缩文件 ](/Linux/Base/LinuxCompressFile.md) +* [ Shell学习 ](/Script/ShellLearn.md) +* [ Linux系统工具命令 ](/Linux/Base/LinuxCommand.md) +* [ 高效的Linux ](/Linux/Base/EffectiveLinux.md) +* [ Linux目录结构 ](/Linux/Base/LinuxDirectoryStructure.md) +* [ Linux网络 ](/Linux/Base/LinuxNetwork.md) +* [ Linux性能 ](/Linux/Base/LinuxPerformance.md) +* [ 流编辑器 ](/Linux/Base/LinuxStreamEditor.md) +* [ Linux美化 ](/Linux/Base/LinuxUI.md) +* [ Linux各种发行版 ](/Linux/Base/ReleaseExperience.md) +* [ SSH ](/Linux/Base/SSH.md) +* [ Nginx ](/Linux/Tool/Nginx.md) + +## 容器 +* [ Docker基础 ](/Linux/Container/Docker.md) +* [ Docker进阶 ](/Linux/Container/DockerAdvance.md) +* [ DockerFile ](/Linux/Container/DockerFile.md) +* [ Docker与应用 ](/Linux/Container/DockerSoft.md) + +## Blog + +- [Ubuntu放弃战斗, Linux桌面的悲哀](http://www.jianshu.com/p/86dd6e34ce91)`deepin王总` +- [一些工具软件](https://bbs.deepin.org/forum.php?mod=viewthread&tid=134241&extra=) +- [Ubuntu安装我的世界](https://www.linuxidc.com/Linux/2016-04/129764.htm) diff --git a/Linux/README.md b/Linux/README.md deleted file mode 100644 index e5851ff..0000000 --- a/Linux/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Linux相关知识 - -- Docker -- Linux基础 -- Git \ No newline at end of file diff --git a/Linux/Readme.md b/Linux/Readme.md new file mode 120000 index 0000000..9bd71d4 --- /dev/null +++ b/Linux/Readme.md @@ -0,0 +1 @@ +LinuxReadme.md \ No newline at end of file diff --git a/Linux/RedHat/CentosBase.md b/Linux/RedHat/CentosBase.md new file mode 100644 index 0000000..70c775c --- /dev/null +++ b/Linux/RedHat/CentosBase.md @@ -0,0 +1,44 @@ +--- +title: Centos +date: 2018-12-15 11:24:04 +tags: + - 基础 + - Centos +categories: + - Linux +--- + +**目录 start** + +1. [Centos](#centos) + 1. [安装](#安装) + 1. [docker安装](#docker安装) + 1. [基础命令](#基础命令) + 1. [用户管理](#用户管理) + 1. [新增](#新增) + +**目录 end**|_2021-02-03 17:25_| +**************************************** +# Centos +> 主流服务器 + +## 安装 +### docker安装 +> [hub的官方镜像](hub.docker.com/_/centos/) + +- `docker pull centos` 得到镜像,然后跑起来即可 + - `cat /etc/redhat-release` 查看当前centos版本(适用于redhat centos) [参考博客](www.cnblogs.com/hitwtx/archive/2012/02/13/2349742.html) + +- docker 中 centos7systemctl命令失效的解决方案: + - `docker run --name centos2 --privileged -ti -e "container=docker" -v /sys/fs/cgroup:/sys/fs/cgroup centos /usr/sbin/init` + +## 基础命令 +> 采用的是yum rpm 管理包 + +## 用户管理 + +### 新增 +> 和Ubuntu类似, 但是adduser会新建用户并且建立home目录,而且没有废话的交互, ubuntu就有 + +- `adduser kuang` 新增用户和对应目录 +- `passwd kuang` 修改密码 , 奇怪的是使用gpasswd更改成功但是无法登录 diff --git a/Linux/RedHat/Readme.md b/Linux/RedHat/Readme.md new file mode 100644 index 0000000..861d542 --- /dev/null +++ b/Linux/RedHat/Readme.md @@ -0,0 +1,3 @@ +# Red Hat + +> [The Difference Between Fedora, Redhat, and CentOS](https://danielmiessler.com/study/fedora_redhat_centos/) diff --git a/Linux/Tool/Caddy.md b/Linux/Tool/Caddy.md deleted file mode 100644 index 1bfec07..0000000 --- a/Linux/Tool/Caddy.md +++ /dev/null @@ -1,6 +0,0 @@ -# Caddy -> [official website](https://caddyserver.com/) | [0.11.0](https://bin-1253378665.cos.ap-guangzhou.myqcloud.com/caddy_v0.11.0_linux_amd64.tar.gz) - -具有丰富的插件支持, 配置简洁 - -> [参考博客: 使用 Caddy 替代 Nginx,全站升级 https,配置更加简单](https://my.oschina.net/diamondfsd/blog/897301) \ No newline at end of file diff --git a/Linux/Tool/IME.md b/Linux/Tool/IME.md new file mode 100644 index 0000000..f6afaab --- /dev/null +++ b/Linux/Tool/IME.md @@ -0,0 +1,91 @@ +--- +title: 输入法 +date: 2018-12-15 12:04:24 +tags: +categories: + - 工具 +--- + +💠 + +- 1. [IME](#ime) +- 2. [fcitx](#fcitx) + - 2.1. [Tips](#tips) +- 3. [ibus](#ibus) +- 4. [常用输入法](#常用输入法) + - 4.1. [Rime](#rime) + - 4.2. [搜狗](#搜狗) + - 4.3. [Google拼音](#google拼音) + - 4.3.0.1. [小小输入法](#小小输入法) + +💠 2024-10-13 17:59:27 +**************************************** +# IME +> 输入法 + +主要的输入法框架分为 fcitx ibus + +# fcitx +> fcitx fcitx-im fcitx-configtool + +> [wiki: fcitx](https://wiki.archlinux.org/index.php/Fcitx_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) + +`~/.xprofile` +```sh +export GTK_IM_MODULE=fcitx +export QT_IM_MODULE=fcitx +export XMODIFIERS="@im=fcitx" +``` + +- 设置里面 Global config -> Program -> Active + +## Tips +> fcitx + sogou 输入法经常出现 `单CPU 100%满载` +- 在搜狗输入法 中打开 fcitx 设置, 插件中 关闭 搜狗云 插件, 即可解决问题 + +> 特定软件 无法输入中文,无法使用剪切板, 意味着fcitx未激活 +需要看软件是否支持进程内加脚本执行,例如 IDEA的 idea.sh WPS的 /usr/bin/et , 都可以通过在脚本首行添加 source ~/.xprofile 解决问题 + +************************ + +# ibus + +# 常用输入法 +## Rime +> [rime](https://rime.im/) 中州韵 + +响应速度快,配置方式均为配置文件方式,扩展性高 + +Ctrl ` 进入设置 + +> [Rime 输入法安装和使用指北](https://blog.mikelyou.com/2020/07/31/rime-input/) + +> 双拼方案 +- yay rime-double-pinyin +[rime 输入法小鹤双拼配置](https://blog.moe233.net/posts/3c46778c/) +[自然码双拼](https://jingyan.baidu.com/article/64d05a027cac09de55f73b18.html) + +> 自定义词库 +- [导入词库](https://gist.github.com/lotem/5443073) +- [rime 词库](https://github.com/mutoe/rime)`emoji,计算机等生活词库` +- [Rime 擴充詞庫](https://github.com/rime-aca/dictionaries) +- [Dict.yml](https://github.com/LEOYoon-Tsaw/Rime_collections/blob/master/Rime_description.md#dictyaml-%E8%A9%B3%E8%A7%A3) +- [RimeConfig](https://github.com/SaboZhang/RimeConfig) + +## 搜狗 +> [Official Site](https://pinyin.sogou.com/linux/) + +> [参考: Linux安装搜狗拼音和谷歌拼音输入法](https://www.jianshu.com/p/429b8f75af2c) + +比较良心, 一直希望百度输入法能出Linux版, 最后还是没有, 优点就是能同步账号, 云词库什么的, 但是bug比较多, 容易奔溃(可能和Deepin有关) +- Ctrl Alt B 显示/关闭 特殊字符面板 + + +## Google拼音 +> fcitx-googlepinyin + +速度比较快, 但是不够聪明, 打字舒适度上没有rime好用 + +#### 小小输入法 +[小小输入法在Deepin上的使用](https://bbs.deepin.org/forum.php?mod=viewthread&tid=138500&highlight=%E5%B0%8F%E5%B0%8F%E8%BE%93%E5%85%A5%E6%B3%95) + diff --git a/Linux/Tool/Nginx.md b/Linux/Tool/Nginx.md index 8d8c172..46d9001 100644 --- a/Linux/Tool/Nginx.md +++ b/Linux/Tool/Nginx.md @@ -1,33 +1,65 @@ -`目录 start` - -- [Nginx](#nginx) - - [Nginx的安装](#nginx的安装) - - [命令安装](#命令安装) - - [编译安装](#编译安装) - - [Docker安装并做反向代理](#docker安装并做反向代理) - - [命令使用](#命令使用) - - [配置使用](#配置使用) - - [本地静态文件Web服务器](#本地静态文件web服务器) - - [反向代理多个服务](#反向代理多个服务) - - [配置https](#配置https) - - [certbot来配置Https](#certbot来配置https) - - [配置Websocket反向代理](#配置websocket反向代理) - - [防盗链](#防盗链) - - [负载均衡](#负载均衡) - - [跨域问题的配置](#跨域问题的配置) - - [静态服务器将后台反代理](#静态服务器将后台反代理) - - [问题](#问题) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Nginx +date: 2018-12-15 12:05:09 +tags: + - Nginx +categories: + - 工具 +--- + +💠 + +- 1. [Nginx](#nginx) + - 1.1. [Nginx的安装](#nginx的安装) + - 1.1.1. [命令安装](#命令安装) + - 1.1.2. [编译安装](#编译安装) + - 1.1.3. [Docker安装](#docker安装) + - 1.2. [命令参数](#命令参数) +- 2. [可视化管理工具](#可视化管理工具) +- 3. [内置变量](#内置变量) +- 4. [配置使用](#配置使用) + - 4.1. [静态资源Web服务器](#静态资源web服务器) + - 4.2. [反向代理多个服务](#反向代理多个服务) + - 4.2.1. [静态资源+反代理后端](#静态资源+反代理后端) + - 4.2.2. [前后端分离时避免跨域](#前后端分离时避免跨域) + - 4.3. [配置https](#配置https) + - 4.3.1. [自签发证书](#自签发证书) + - 4.3.2. [通过 certbot 配置 HTTPS](#通过-certbot-配置-https) + - 4.4. [配置Websocket反向代理](#配置websocket反向代理) + - 4.5. [转发代理](#转发代理) + - 4.6. [防盗链](#防盗链) + - 4.7. [gzip](#gzip) + - 4.8. [负载均衡](#负载均衡) + - 4.8.1. [负载均衡策略](#负载均衡策略) +- 5. [Nginx Plus](#nginx-plus) +- 6. [Keepalived](#keepalived) +- 7. [同类应用](#同类应用) + - 7.1. [Caddy](#caddy) + - 7.2. [Squid](#squid) + - 7.3. [Varnish](#varnish) + - 7.4. [HAProxy](#haproxy) + - 7.5. [nuster](#nuster) +- 8. [Tips](#tips) + +💠 2024-09-20 17:30:23 **************************************** # Nginx +> [Official Site](https://www.nginx.com/) | [Official Doc](https://docs.nginx.com) +> [Tengine](http://tengine.taobao.org/) + +> [nginx-tutorial](https://github.com/dunwu/nginx-tutorial) + ## Nginx的安装 ### 命令安装 -- 安装`sudo apt install nginx` -- 启动服务 `sudo nginx`或者`sudo /etc/init.d/nginx start` -- 关闭 `sudo nginx -s quit` 或者 `sudo /etc/init.d/nginx stop` +- 安装 `sudo apt install nginx` +- 启动服务 `sudo nginx` + - 或者`sudo /etc/init.d/nginx start` + - 或者 systemd 方式 `systemctl start nginx` +- 关闭 `sudo nginx -s quit` + - 或者 `sudo /etc/init.d/nginx stop` + - 或者 systemd 方式`systemctl stop nginx` ### 编译安装 > 不建议使用这种方式进行安装,很容易出现兼容问题 @@ -47,221 +79,237 @@ --with-zlib=/home/kuang/zlib-1.2.11 ``` -### Docker安装并做反向代理 +### Docker安装 > [nginx hub 官方镜像](https://hub.docker.com/r/library/nginx/) -- 运行命令创建容器运行 `docker run --name youhuigo -d -p 80:80 -v /home/kuang/nginx/conf/:/etc/nginx/conf.d/:ro --link you:web nginx` - -`conf 基础配置文件` -``` -upstream gitea { - server 127.0.0.1:6001; -} -server { - listen 80; - server_name git.kuangcp.top; - location / { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header X-Nginx-Proxt true; - - proxy_pass http://gitea; - proxy_redirect off; - } -} -``` -> [配置多域名反向代理](http://www.ttlsa.com/nginx/use-nginx-proxy/) `其实也就是多了 俩 upstream 监听80的server` -## 命令使用 -`nginx -h 输出` -``` - nginx version: nginx/1.13.3 - Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives] - Options: - -?,-h : this help - -v : show version and exit - -V : show version and configure options then exit - -t : test configuration and exit - -T : test configuration, dump it and exit - -q : suppress non-error messages during configuration testing - -s signal : send signal to a master process: stop, quit, reopen, reload - -p prefix : set prefix path (default: /usr/share/nginx/) - -c filename : set configuration file (default: /etc/nginx/nginx.conf) - -g directives : set global directives out of configuration file -``` +## 命令参数 - `-s signal` - stop 停止 - quit 退出 - reopen 重新打开 - - reload 重载(修改配置文件常使用) -- `-t` 测试配置 - - 使用 指定配置文件,或者默认配置文件 进行测试 + - reload 重载配置(修改配置文件常使用) +- `-t` 测试配置文件 + +************************ +# 可视化管理工具 +> [nginxWebUI](https://github.com/cym1102/nginxWebUI) +> [nginx-proxy-manager ](https://github.com/NginxProxyManager/nginx-proxy-manager) + +************************ +# 内置变量 +> [Official Doc](https://nginx.org/en/docs/varindex.html) | [Nginx 内置变量](https://www.jianshu.com/p/deccac3a4fd0) + +- `$remote_addr` 客户端地址 +- `$remote_port` 客户端端口 + +************************ + +# 配置使用 +> [Official Doc](https://www.nginx.com/resources/wiki/start/#pre-canned-configurations) -*************** -## 配置使用 > [知乎专栏](https://zhuanlan.zhihu.com/p/24524057) > [nginx基本配置](https://segmentfault.com/a/1190000002797601) | [ngrok nginx docker本地搭建服务器](https://fengqi.me/unix/409.html) - [实验楼课程](https://www.shiyanlou.com/courses/95) -- 个人理解 - 1. server_name 是一个域名或者ip, 如果是ip和公网的域名就没什么好解释的, - - 但是如果只是局域网的修改host文件生成的域名, 自己使用该域名访问是没有问题的, 别人访问不了,但是如果也同样的修改host文件后, 也能正确使用域名访问 - 2. server是一个门路, 不会冲突, 所以才能有很多个监听80端口的配置而互不影响. +nginx 配置文件的语法是自己独有的语法, 比较像 shell, 里面有用到正则, 变量等概念 -### 本地静态文件Web服务器 -> 最简单的使用 [参考博客](http://blog.yuansc.com/2015/04/29/nginx%E9%85%8D%E7%BD%AE%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/) +- 读取自定义目录配置: + - nginx.conf 中 http 块内添加 `include /etc/nginx/conf.d/*.conf;` +- 错误页面重定向 `error_page 404 /404.html;` 也可以填完整URL -```conf +> [Nginx反向代理,当后端为Https时的一些细节和原理](https://blog.dianduidian.com/post/nginx%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86%E5%BD%93%E5%90%8E%E7%AB%AF%E4%B8%BAhttps%E6%97%B6%E7%9A%84%E4%B8%80%E4%BA%9B%E7%BB%86%E8%8A%82%E5%92%8C%E5%8E%9F%E7%90%86/) + +nginx提供Http服务,但是反向代理了HTTPS地址时 需要注意证书的一致性问题 + +************************ + +## 静态资源Web服务器 +> [参考 nginx配置静态文件服务器 ](http://blog.yuansc.com/2015/04/29/nginx%E9%85%8D%E7%BD%AE%E9%9D%99%E6%80%81%E6%96%87%E4%BB%B6%E6%9C%8D%E5%8A%A1%E5%99%A8/) + +```ini server { - # listen 6050; client_max_body_size 4G; - listen 80; ## listen for ipv4; this line is default and implied - server_name static.me.com; + listen 80; + # server_name static.me; # 如果需要使用域名 则需要在hosts文件配置 root /home/mini/Sync; location / { + autoindex on; # 显示索引 + autoindex_exact_size on; # 显示大小 + autoindex_localtime on; # 显示时间 } } ``` -再在 `/etc/hosts`文件中配置下域名即可访问 -> 在服务器中配置, 出现403错误, 将 /etc/nginx/nginx.conf 中第一行的 user 改成 root -#### 反向代理多个服务 -- 修改默认配置文件 `/etc/nginx/nginx.conf` - - 或者更好的就是在 `/etc/nginx/conf.d/`下新建 *.conf 文件,文件名任意 - -`该配置文件配置了服务器反向代理,80端口上:/路径的请求转发到9991端口 /myth转发到7898端口 ` -```conf -upstream xxxuthus { - server 127.0.0.1:9991; -} -upstream youhui { - server 127.0.0.1:7898; -} +1. 若出现403错误, 将 /etc/nginx/nginx.conf 中第一行的 `user nginx;` 改成可访问静态文件目录的用户即可 +1. `配置为文本文件类型` 即 text/plain; 类型。例: 浏览器直接查看 code 目录下所有源代码 + ```ini + location /code/ { + # All files in it + location ~* { + add_header Content-Type text/plain; + } + } + ``` +如果有编码问题可配置成 `add_header Content-Type 'text/plain;charset=UTF-8';` +`location ~* /.*\.(py|md|sql)${}` + +## 反向代理多个服务 +`配置反向代理` +1. nginx 的 80 端口下:`/` 路径的请求转发到9991端口 `/myth` 转发到7898端口 + +```ini + upstream one { + server 127.0.0.1:9991; + } + upstream two { + server 127.0.0.1:7898; + } -server { - listen 80; - server_name 1.1.1.1; + server { + listen 80; + server_name 1.1.1.1; - location / { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header X-Nginx-Proxt true; + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-Nginx-Proxt true; - proxy_pass http://xxxuthus; - proxy_redirect off; + proxy_pass http://one; + proxy_redirect off; + } + + location /myth{ + proxy_pass http://two; + proxy_redirect off; + } } - location /myth{ - proxy_pass http://youhui; - proxy_redirect off; - } -} ``` -- 测试配置文件 再 重启nginx `nginx -s reload` -#### 配置https -> 参考博客 [nginx搭建https服务](http://www.cnblogs.com/tintin1926/archive/2012/07/12/2587311.html) | [nginx http/2](http://letus.club/2016/04/08/nginx-http2-letsencrypt/) +### 静态资源+反代理后端 +> [Nginx反向代理解决跨域问题](https://segmentfault.com/a/1190000012859206) | [nginx简易使用教程,使用nginx解决跨域问题](https://www.jianshu.com/p/05415981e5e5) + +_配置统一出口_ +```ini + server { + client_max_body_size 4G; + listen 80; # listen for ipv4; this line is default and implied + server_name static.me; + + location / { + root /data/static; + # proxy_pass http://127.0.0.1:8889/; 如果静态资源在别的端口上,这样配置也可以 + } + + location /api/ { + # add_header 'Access-Control-Allow-Origin' '*'; + proxy_pass http://127.0.0.1:8889/; # 去除请求的 api 路径,并访问后端 + # proxy_pass http://127.0.0.1:8889; 这种方式不会去除 /api/ + } -- 先签发证书 `命令运行` -```sh - ############ 证书颁发机构 - # CA机构私钥 - openssl genrsa -out ca.key 2048 - # CA证书 - openssl req -x509 -new -key ca.key -out ca.crt - ############ 服务端 - # 生成服务端私钥 - openssl genrsa -out server.key 2048 - # 生成服务端证书请求文件 - openssl req -new -key server.key -out server.csr - # 使用CA证书生成服务端证书 关于sha256,默认使用的是sha1,在新版本的chrome中会被认为是不安全的,因为使用了过时的加密算法。 - openssl x509 -req -sha256 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 -out server.crt - # 打包服务端的资料为pkcs12格式(非必要,只是换一种格式存储上一步生成的证书) 生成过程中,需要创建访问密码,请记录下来。 - openssl pkcs12 -export -in server.crt -inkey server.key -out server.pkcs12 + location /api/a-service { + proxy_pass http://127.0.0.1:8889/a-service; # 移除 /api/ 路径,保留a-service (api路径下多个服务时使用此类型配置) + } + } ``` -`*.conf配置文件 配置HTTPS` +1. 将静态文件交由Nginx进行处理, 后台的服务统一用一个前缀和前台进行区分, 然后将服务端的真实host和ip或者域名配置进来 +2. 这样在于前端看来就是访问 static.me/api 而已, 实际上访问的是 127.0.0.1:8889/api +> 注意,原先使用nginx反向代理tomcat,尝试配置后端为一个本地dns解析的域名。然后发现这是无法生效的,所以应该使用真实IP或公网域名 + +### 前后端分离时避免跨域 +在需要被跨域访问的服务端,添加如下配置 +```ini + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Headers X-Requested-With; + add_header Access-Control-Allow-Methods GET,POST,OPTIONS; ``` -upstream youhui { - server 127.0.0.1:8888; -} -server { - listen 443; - server_name wx.jjyouhuigo.com; - # 主要就是添加了这一块 - ssl on; - ssl_certificate /home/youhuigo/https/server.crt; - ssl_certificate_key /home/youhuigo/https/server.key; - - location / { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header X-Nginx-Proxt true; - proxy_pass https://youhui; - proxy_redirect off; - } -} +************************ + +## 配置https +> [nginx搭建https服务](http://www.cnblogs.com/tintin1926/archive/2012/07/12/2587311.html) | [nginx http/2](http://letus.club/2016/04/08/nginx-http2-letsencrypt/) + +### 自签发证书 +- [Linux: 自签发证书](/Linux/Base/LinuxNetwork.md#自签发证书) + +`配置HTTPS` + +```ini + upstream one { + server 127.0.0.1:8888; + } + server { + listen 443; + server_name web.me; + + # 主要就是添加了这一块 + ssl on; + ssl_certificate /data/https/server.crt; + ssl_certificate_key /data/https/server.key; + + # http 转向 https + return 302 https://$host$request_uri; + + location / { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_set_header X-Nginx-Proxt true; + proxy_pass https://one; + proxy_redirect off; + } + } ``` -##### certbot来配置Https -> 免费的网站, 并且现在支持泛域名了! [参考博客](http://www.cnblogs.com/lidong94/p/7156839.html) | [参考博客](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04) + +### 通过 certbot 配置 HTTPS +> 免费的网站, 并且现在支持泛域名了[参考博客](http://www.cnblogs.com/lidong94/p/7156839.html) | [参考博客](https://www.digitalocean.com/community/tutorials/how-to-secure-nginx-with-let-s-encrypt-on-ubuntu-16-04) > [Nginx反向代理https](http://linux.it.net.cn/e/server/nginx/2015/0131/12745.html) + ```sh wget https://dl.eff.org/certbot-auto chmod a+x certbot-auto - ./certbot-auto 进行安装 但是过程中会有一些设置, - ./certbot-auto certonly --email kuangcp@aliyun.com --nginx -d wx.kuangcp.top 生成证书 + ./certbot-auto #进行安装 但是过程中会有一些设置, + ./certbot-auto certonly --email xxx@xxx --nginx -d xxx.domain # 生成 xxx.domain 证书 ``` _SSL 接收到一个超出最大准许长度的记录 要在端口后加上SSL nginx_ -```conf - upstream youhui { +```ini + upstream one { server 127.0.0.1:8080; } - upstream git{ - server 127.0.0.1:55443; - } - server { - listen 80; - server_name git.kuangcp.top; - location / { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header X-Nginx-Proxt true; - proxy_pass http://git; - proxy_redirect off; - } - access_log /home/kuang/log/youhui.log; - } + server{ listen 443 ssl; - server_name wx.kuangcp.top - ssl on; - ssl_certificate /etc/letsencrypt/live/wx.kuangcp.top/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/wx.kuangcp.top/privkey.pem; - ssl_trusted_certificate /etc/letsencrypt/live/wx.kuangcp.top/chain.pem; - ssl_dhparam /etc/nginx/ssl/dhparam.pem; - location / { - proxy_pass https://youhui; + server_name xxx.domain + access_log /data/log/https.log; + + # ssl配置 + ssl on; + ssl_certificate /etc/letsencrypt/live/xxx.domain/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/xxx.domain/privkey.pem; + ssl_trusted_certificate /etc/letsencrypt/live/xxx.domain/chain.pem; + ssl_dhparam /etc/nginx/ssl/dhparam.pem; + + location / { + proxy_pass https://one; } - access_log /home/kuang/log/https.log; } ``` -#### 配置Websocket反向代理 -``` +## 配置Websocket反向代理 +```ini # 配置连接的配置信息 map $http_upgrade $connection_upgrade{ default upgrade; '' close; } - upstream youhui { + upstream back_end { server 127.0.0.1:8888; } server { listen 80; server_name 127.0.0.1; location / { + # 设置转发真实ip proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; @@ -271,50 +319,173 @@ _SSL 接收到一个超出最大准许长度的记录 要在端口后加上SSL n proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; - proxy_pass http://youhui; + proxy_pass http://back_end; + # 默认是 1.0 不支持 keepAlive + proxy_http_version 1.1; proxy_redirect off; - proxy_read_timeout 300s; + proxy_read_timeout 300s; } } ``` -### 防盗链 - +> 绕过Grafana,免密登录,需要预先生成key +```ini +map $http_upgrade $connection_upgrade{ + default upgrade; + '' close; +} -### 负载均衡 -> [Nginx 反向代理 负载均衡 虚拟主机配置](https://segmentfault.com/a/1190000012479902) + # http://grafana-user.test/d/spring_boot_21/shang-shu-tai-jian-kong-mian-ban?orgId=1 +server { + listen 80; + server_name grafana-user.test; -### 跨域问题的配置 -需要被跨域访问的一端, 添加如下配置 -``` -add_header Access-Control-Allow-Origin *; -add_header Access-Control-Allow-Headers X-Requested-With; -add_header Access-Control-Allow-Methods GET,POST,OPTIONS; + location / { + proxy_pass http://192.168.1.1:9091/; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Connection ""; + proxy_set_header Authorization "Bearer xxxxx"; + # add_header X-Frame-Options SAMEORIGIN; + proxy_hide_header X-Frame-Options; + } + location /api/live/ws { + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forward-For $proxy_add_x_forwarded_for; + proxy_set_header Host $host; + proxy_set_header X-Nginx-Proxt true; + + proxy_set_header Authorization "Bearer cccc"; + + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + + proxy_http_version 1.1; + proxy_redirect off; + proxy_read_timeout 300s; + + proxy_pass http://192.168.1.1:9091; + } +} ``` -#### 静态服务器将后台反代理 -> 极大地省去了后台的配置!! -> [Nginx反向代理解决跨域问题](https://segmentfault.com/a/1190000012859206) | [nginx简易使用教程,使用nginx解决跨域问题](https://www.jianshu.com/p/05415981e5e5) -_配置静态端_ -``` -server { - client_max_body_size 4G; - listen 80; ## listen for ipv4; this line is default and implied - server_name view.kcp; - location /api/ { - # add_header 'Access-Control-Allow-Origin' '*'; - proxy_pass http://127.0.0.1:8889; - } +## 转发代理 +> 例如 aaa.com 需要VPN等方式才能访问,Nginx所在的主机能访问,就可以这么配置,然后配置DNS将 aaa.com 解析到Nginx的主机上,就可以实现其他客户机不安装VPN 直接访问 aaa.com + +```ini + server { + server_name aaa.com; + listen 80; location / { - root /home/kcp/IdeaProjects/Base/graduate/static; + proxy_pass http://aaa.com; + proxy_set_header Host $host:$server_port; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; } -} + } ``` -1. 将静态文件交由Nginx进行处理, 后台的服务统一用一个前缀和前台进行区分, 然后将服务端的真实host和ip或者域名配置进来 -2. 这样在于前端看来就是访问 view.kcp/api 而已, 实际上访问的是 127.0.0.1:8889/api -> 由于我原先用了nginx反向代理tomcat, 配置一个修改本地host得到的域名, 然后填在这里就没用了, 所以最好使用真实IP或者外网可访问的域名 -**************************** -## 问题 +## 防盗链 + +## gzip +> [nginx 启用 gzip压缩](https://www.jianshu.com/p/c5d1fc829855) + +```conf + gzip on; + gzip_comp_level 4; # 缺省值 + gzip_buffers 4 16k; + gzip_http_version 1.1; # 缺省值 + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; +``` + +## 负载均衡 +> [Nginx 反向代理 负载均衡 虚拟主机配置](https://segmentfault.com/a/1190000012479902) + +分为四层和七层: +- 在四层只依据ip的报文转发(修改进入时目的ip`从nginx改成upstream的IP`,修改返回时发送ip) +- 在七层依据数据内容做转发,例如按http请求后缀做转发 *.jpg 到A服务器 *.jsp到B服务器 + +### 负载均衡策略 +> [Doc: Http Load Balancer](https://docs.nginx.com/nginx/admin-guide/load-balancer/http-load-balancer/) + +1. Round Robin: + - 默认方式,对所有backend无差别按序轮询, 如果backend宕机会自动移除掉 +1. Weight: + - 对所有backend按权重进行轮询,权重越高分发到的请求就越多,默认值为1。适合硬件差别比较大的多个节点 + ```ini + #动态服务器组 + upstream dynamic_server { + server localhost:8080 weight=2; + server localhost:8081; + server localhost:8082 backup; + server localhost:8083 max_fails=3 fail_timeout=20s; + } + ``` +1. Least Connections(least_conn): + - 依据每个backend当前活跃连接数,将请求分发到连接数最少的backend上,此时会考虑backend的weight权重比例 + - upstream配置块中在首行添加 `least_conn;`即可 +1. IP Hash(ip_hash): 对请求来源IP地址计算hash值,通过某种映射(例如取余,详细可查看ip_hash相关源码)分发至对应backend + - upstream配置块中在首行添加 `ip_hash;`即可 +1. Generic Hash(hash): 用户自定义资源(例如URL)计算hash完成分发,可选consistent关键字支持`一致性hash`特性 + +************************ + +> Q & A +1. 如果负载均衡了A B两个节点,请求进入了A节点后,立马移除了对A节点的负载均衡,该请求是否能正常执行 + - 能正常执行,假如此时请求A失败,例如A节点宕机, 请求还会转移至B节点(failover) +1. 如果负载均衡了A B两个节点, A节点宕机了,后续请求是否还会分发到A节点 + - 不会,原理: TODO + +************************ + +# Nginx Plus +> 对标 F5 BIG-IP + +> [5-reasons-switch-f5-big-ip-to-nginx-plus](https://www.nginx.com/blog/5-reasons-switch-f5-big-ip-to-nginx-plus/) + +************************ + +# Keepalived +Keepalived软件起初是专为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可以实现高可用的VRRP功能。因此,Keepalived除了能够管理LVS软件外,还可以作为其他服务(例如:Nginx、Haproxy、MySQL等)的高可用解决方案软件。 + +> [参考: keepalived实现服务高可用](https://www.cnblogs.com/clsn/p/8052649.html) + +************************ + +# 同类应用 +## Caddy +> [official website](https://caddyserver.com/) + +具有丰富的插件支持, 配置简洁, 自动配置 HTTPS证书,相较于nginx资源消耗更多 吞吐量低一些 + +> [参考: 使用 Caddy 替代 Nginx,全站升级 https,配置更加简单](https://my.oschina.net/diamondfsd/blog/897301) + +## Squid +> [Official](http://www.squid-cache.org) + +## Varnish + +## HAProxy +> [Official](https://www.haproxy.org/) 企业级工具 + +## nuster +> [Github](https://github.com/jiangwenyuan/nuster)`基于 HAProxy` + + +************************ + +# Tips - 文件上传报错 413 - - http{}中添加 `client_max_body_size 80M;` + - http{} 中添加 `client_max_body_size 80M;` +- request_time 比实际时间长 [Nginx的延迟关闭](http://shibing.github.io/2016/11/18/nginx%E7%9A%84%E5%BB%B6%E8%BF%9F%E5%85%B3%E9%97%AD-lingering-close/)`lingering_timeout` + + +************************ +access 日志中 upstream 408状态码 本身返回的502 实际上upstream没收到请求 + +> [Random HTTP 408 on POST requests and no body is transferred](https://stackoverflow.com/questions/70856800/random-http-408-on-post-requests-and-no-body-is-transferred) + + diff --git a/Linux/Tool/NginxAdvance.md b/Linux/Tool/NginxAdvance.md new file mode 100644 index 0000000..cc6aa5e --- /dev/null +++ b/Linux/Tool/NginxAdvance.md @@ -0,0 +1,42 @@ +--- +title: NginxAdvance +date: 2021-02-03 17:28:26 +tags: +categories: +--- + +**目录 start** + +1. [Nginx设计与实现](#nginx设计与实现) + +**目录 end**|_2021-02-03 17:28_| +**************************************** +# Nginx设计与实现 +- 《深入理解Nginx:模块开发与架构解析(第2版)》 +- 《Nginx高性能Web服务器详解》 + +> [参考: Nginx从听说到学会](https://www.jianshu.com/p/630e2e1ca57f) + + + +> 参考 图 1 + +Nginx 采用多进程设计(避免锁),使用master进程管理若干worker进程 +- master 进程管理(生命周期管理)多个worker进程,用于升级nginx,更新nginx配置等行为 +- worker 进程则处理真正的请求,且 worker 进程间会通过通信协调完成负载均衡 + +*** + +> Nginx是事件驱动型的 +- 支持的常见事件驱动模型 + - select + - poll + - epoll + - rtsig + +*** + +> 参考 图3 + +对于传统Web服务器而言,采用的所谓事件驱动往往局限在TCP连接建立、关闭事件上,一个连接建立以后,在其关闭之前的所有操作都不再是事件驱动,这时会退化成按序执行每个操作的批处理模式,这样每个请求在连接建立后都将始终占用着系统资源,直到连接关闭才会释放资源。 + diff --git a/Linux/Tool/NginxIngress.md b/Linux/Tool/NginxIngress.md new file mode 100644 index 0000000..b88536f --- /dev/null +++ b/Linux/Tool/NginxIngress.md @@ -0,0 +1,17 @@ +--- +title: NginxIngress +date: 2023-09-27 19:08:28 +tags: +categories: +--- + +**目录 start** + +1. [Nginx ingress](#nginx-ingress) + +**目录 end**|_2023-09-27 19:08_| +**************************************** +# Nginx ingress +> [Github: ingress nginx](https://github.com/kubernetes/ingress-nginx) +> [nginx-configuration: configmap](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/) + diff --git a/Linux/Tool/Terminal.md b/Linux/Tool/Terminal.md deleted file mode 100644 index 985ae5e..0000000 --- a/Linux/Tool/Terminal.md +++ /dev/null @@ -1,134 +0,0 @@ -`目录 start` - -- [Terminal](#terminal) - - [终端模拟器对比](#终端模拟器对比) - - [终端工具命令](#终端工具命令) - - [Shell内建命令](#shell内建命令) - - [需用户安装](#需用户安装) - - [效率工具](#效率工具) - - [Autojump](#autojump) - - [tmux](#tmux) - - [notes](#notes) - - [todo.txt-cli](#todotxt-cli) - - [文本操作](#文本操作) - - [xclip](#xclip) - - [文件操作](#文件操作) - - [zssh](#zssh) - - [分享](#分享) - - [asciinema](#asciinema) - -`目录 end` |_2018-08-27_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Terminal -> 终端模拟器是吸引我放弃习惯的Windows而转投Linux怀抱的主要原因 - -## 终端模拟器对比 -> 列举出系统可安装终端 `sudo apt search terminal | grep -E terminal.+amd64` - -- `qterminal` 可定制标签页位置以及透明度,很简洁,挺好用,但是不能内容和窗体大小自适配, 0.7.1已没有这个bug, 还是很好用的模拟器, 但是多标签的时候, 会有内存泄露 -- `mate-terminal` 和gnome-terminal 基本配置什么的几乎一样,只是标题栏简洁一丢丢,如果使用选择即复制,那么在跨标签页复制粘贴有bug -- `gnome-terminal` 很简洁,但是多标签时,标签栏太大,标签页底部有白边 -- `sakura` 外观上和前两个几乎一样,标签页可以更简洁,但是设置不好调, 而且不能自定义快捷键 -- `deepin-terminal` 功能很多,主题很多,功能最为强大,但是字体可以选的很少 -- `terminator` 可以定制背景图片,但是在我这deppin系统里有bug,多标签是假的,命令全是在共享的,不能用。。 -- `tmux` 运维必备软件,入门有些繁琐 -- `tilda` 内嵌于桌面上, 小命令方便, 需要查看文件就不方便了 - -> [更多可安装终端](https://gitee.com/kcp1104/codes/gca14wtqvm67l9j5r0deb56#Terminals.md) - -************************ -## 终端工具命令 -> /bin/* 系统自带命令 - -- which 命令的位置 -- false 以失败码退出程序 -- `stty -a` 查看键映射 - -### Shell内建命令 -- whence 查看命令的真实面貌 (zsh中的内建命令) -- where 查找命令的位置 (Zsh中内建命令) - -### 需用户安装 -> 最终都会安装到 /usr/bin/* 目录下 - -- wc -l file _统计文件行数_ -- md5sum 报文摘要算法 Message-Digest Algorithm 5 的实现 - - `md5sum file` 计算出md5值 - - `md5sum -c file.md5` file 和 file.md5 在同一目录下, 执行这个命令就是检查md5是否匹配, 确保文件的完整性和正确性 - -- last _查看Linux登录信息_ - - last -n 5 最近五次登录 -- w | uptime _查看启动情况_ -- colrm - - ps | clorm 20 30 `colrm` _删除输出的20 到30 列_ -- xsel - - `cat a.md | xsel -b` _将文件所有内容复制到剪贴板_ 但是处理大文件时会失效 xclip 更有效 - -- htop _终端里的任务管理器_ -- strace -p PID _查看系统调用_ -- cmatrix _装13,字符雨_ -- logkeys 记录键盘输入 [Github](https://github.com/kernc/logkeys) -- expect [用于自动输入密码](http://www.cnblogs.com/iloveyoucc/archive/2012/05/11/2496433.html) - -- [WTF](https://wtfutil.com/posts/overview/) | [Github Repo](https://github.com/senorprogrammer/wtf) - - 丰富的功能, 一个方便的终端控制面板 - -- ag `快速当前目录下, 全文内容搜索, 快到可怕` ubuntu:silversearcher-ag alpine:the_silver_searcher - - [The Silver Searcher](https://github.com/ggreer/the_silver_searcher) - -### 效率工具 -> 提高工作和开发效率 - -#### Autojump -> 统计cd 目录,方便目录跳转 *shrc 中要有 : `. /usr/share/autojump/autojump.sh` - -- `apt install autojump` 设置为自动运行 `echo '. /usr/share/autojump/autojump.sh' >> ~/.bashrc` - - `j -v` 查看安装版本 - - `j --stat` 查看统计信息 - - `j --help` - - `jo code` 打开code文件夹 - - `jco c` 打开子目录 -- `ls -l ~/.local/share/autojump/` 统计信息的目录,清除就相当于卸载重装了 - -#### tmux -> 好用的管理会话的软件, 在服务器上是很有用的 - -- [ ] 学习使用 - -> [tmux 入门](http://blog.jobbole.com/87278/) - -- 新建会话 `tmux new -s myth` -- 连接会话 `tmux a -t test` -- 显示所有 `tmux ls` -- `Ctrl B` 再 `C` 新建一个窗口 `Ctrl B` `数字键`切换指定窗口 -- 断开会话但是后台运行 `ctrl+B D` - -#### notes -> 管理笔记 -> [Github](https://github.com/pimterry/notes) - -#### todo.txt-cli -> 终端内的 todo -> [Github](https://github.com/todotxt/todo.txt-cli) - -************* -### 文本操作 -#### xclip -> 便捷的文本复制 -- `cat README.md | xclip -sel clip` 将文件复制到剪贴板 - -*********** -### 文件操作 -#### zssh -> 便捷的文件传输 - -- [参考博客](http://blog.csdn.net/ygm_linux/article/details/32321729) - -*********** -### 分享 -#### asciinema -> 终端录制工具 - -- 执行 `asciinema`或`asciinema rec` 即可开始录制 -- 要注册就运行 `asciinema auth` 进入输出的网址,填邮箱和名字即可(每次登录都要这样。或者使用邮件来确认,麻烦ing) - diff --git a/Linux/Tool/Tmux.md b/Linux/Tool/Tmux.md new file mode 100644 index 0000000..54011a4 --- /dev/null +++ b/Linux/Tool/Tmux.md @@ -0,0 +1,156 @@ +--- +title: Tmux +date: 2019-02-28 17:43:53 +tags: +categories: + - 工具 +--- + +💠 + +- 1. [Tmux](#tmux) +- 2. [基本操作](#基本操作) + - 2.1. [编译安装](#编译安装) +- 3. [配置](#配置) + - 3.1. [个人配置](#个人配置) + - 3.2. [键绑定](#键绑定) + - 3.3. [切换](#切换) +- 4. [TPM插件管理](#tpm插件管理) + - 4.1. [tmux-resurrect](#tmux-resurrect) + - 4.2. [maglev](#maglev) + - 4.3. [copycat](#copycat) +- 5. [Advanced](#advanced) +- 6. [Tips](#tips) + +💠 2024-09-20 11:52:03 +**************************************** +# Tmux +> [Arch wiki: tmux](https://wiki.archlinux.org/index.php/Tmux_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87)) + +> [tmux简洁教程及config关键配置](https://www.jianshu.com/p/fd3bbdba9dc9) +> [参考: 程序员高效技巧系列](http://cenalulu.github.io/linux/professional-tmux-skills/) +> [Tmux](https://github.com/skywind3000/awesome-cheatsheets/blob/master/tools/tmux.txt) + +> [Byobu](https://github.com/dustinkirkland/byobu)`window manager and terminal multiplexer.` + +> [libtmux](https://github.com/tmux-python/libtmux)`Python lib操作Tmux动作` +************************ +# 基本操作 + +- 新建会话 `tmux new -s myth` +- 连接会话 `tmux a -t test` +- 显示所有 `tmux ls` +- 重新加载配置文件 `tmux source ~/.tmux.conf` + +> 快捷键 +- prefix + - ? 帮助 + - s 选择 session + - w 选择 window + - d deattach 脱离 + - j 下 panel + - k 上 panel + - ; 最近的 panel + - $ 重命名 session + - , 重命名 panel + - Alt+方向键 往指定方向扩展当前 panel 大小 +- Alt+方向键 跳转到对应方向的panel上 + +************************ + +## 编译安装 +> 场景: 目标机器Linux内核版本较低,或者是Debian Centos等发行版,源中没有高版本的Tmux,甚至没有Tmux,这个时候通过静态编译安装,能在影响最小的情况下使用上新版本的Tmux + +因为低版本Tmux不支持鼠标,导致无法使用滚轮上翻命令输出记录。 + +> [CentOS 静态编译](https://zhengzexin.com/archives/Tmux_static_compilation/)`但是在Centos6上没成功 内核3.10 gcc 4.6.8` + +************************ + +# 配置 +> [Oh My Tmux!](https://github.com/gpakosz/.tmux) + +## 个人配置 +> [Tmux配置文件](https://gitee.com/gin9/Configs/blob/master/Linux/tmux/tmux.conf) + +步骤 +1. `ln -s $(pwd)/tmux.conf ~/.tmux.conf` +1. `git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm` +1. `tmux source ~/.tmux.conf` +1. `Ctrl A, I` 等待插件安装完成 + +> [.tmux](https://github.com/gpakosz/.tmux) tmux配置(和前文的配置效果基本一致) + +******************* + +> 开启鼠标选择与复制 +```conf + set -g mouse on +``` + +1. 按住Shift即可照常使用鼠标选中文本 +1. 在顶部或底部的tab列表区域可用滚轮快速切换tab + +************************ + +## 键绑定 +> Prefix 默认是 C-b 也就是 Ctrl b + +[tmux: how to bind a key to launch shell command?](https://unix.stackexchange.com/questions/283759/tmux-how-to-bind-a-key-to-launch-shell-command) + +- `bind-key {key} {action}` + - `bind-key -T root {key} {action}` 无需prefix 即可触发key + +> action +- send-keys + - 例如 `bind-key -T root F9 send-keys 'cola' Enter` F9即可在终端运行 git-cola +- run-shell +- source 和 source-file +- select-pane +- split-window + +## 切换 +- prefix w 切换 window 或者 Session +- Prefix () 切换 Session + +************************ + +# TPM插件管理 +- [Tmux Plugin Manager](https://github.com/tmux-plugins/tpm) `查看Readme下载安装` +> `git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm` + +- [tmux-plugins list 插件列表](https://github.com/tmux-plugins/list) + +> [参考: 保存和恢复 Tmux 会话 ](https://liam.page/2016/09/10/tmux-plugin-resurrect/) + +[tmux-modal](https://github.com/whame/tmux-modal) 快速操作切换和创建 window panel + +- `prefix I` 安装新增的插件 + +## tmux-resurrect + +> [tmux-resurrect](https://github.com/tmux-plugins/tmux-resurrect) +> [tmux-continuum](https://github.com/tmux-plugins/tmux-continuum) + +prefix c-s 保存会话 +prefix c-r 加载历史会话 + +## maglev +> [Github](https://github.com/caiogondim/maglev) + +## copycat +> [Github](https://github.com/tmux-plugins/tmux-copycat) + +使用: `prefix /` 可用 less 一样的方式搜索 + +************************ + +# Advanced +[Github wiki: Advanced use](https://github.com/tmux/tmux/wiki/Advanced-Use) + +************************ + +# Tips +> [bash: append_path: command not found when open tmux](https://superuser.com/questions/1590651/bash-append-path-command-not-found-when-open-tmux) + +`set-option -g default-command '/bin/bash'` 追加到 tmux.conf 即可解决,如果使用 zsh 则是 /usr/bin/zsh diff --git a/Linux/Tool/Vim.md b/Linux/Tool/Vim.md index c86eda7..8baa7b3 100644 --- a/Linux/Tool/Vim.md +++ b/Linux/Tool/Vim.md @@ -1,73 +1,103 @@ -`目录 start` - -- [Vim](#vim) - - [Tips](#tips) - - [基本配置](#基本配置) - - [基础操作](#基础操作) - - [跳转](#跳转) - - [搜索匹配](#搜索匹配) - - [复制粘贴](#复制粘贴) - - [插入模式](#插入模式) - - [命令模式](#命令模式) - -`目录 end` |_2018-09-06_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Vim +date: 2018-12-15 12:05:58 +tags: + - Vim +categories: + - 工具 +--- + +💠 + +- 1. [Vim](#vim) + - 1.1. [Tips](#tips) + - 1.2. [基本配置](#基本配置) + - 1.2.1. [GVim](#gvim) + - 1.3. [基础操作](#基础操作) + - 1.3.1. [跳转](#跳转) + - 1.3.1.1. [高级跳转](#高级跳转) + - 1.3.2. [搜索和替换](#搜索和替换) + - 1.3.3. [复制粘贴](#复制粘贴) + - 1.3.4. [插入模式](#插入模式) + - 1.3.5. [命令模式](#命令模式) + - 1.4. [插件管理](#插件管理) +- 2. [定制化](#定制化) + - 2.1. [vim-init](#vim-init) + - 2.2. [spf13](#spf13) + - 2.3. [SpaceVim](#spacevim) + - 2.4. [space-vim](#space-vim) + +💠 2024-09-28 11:21:46 **************************************** # Vim -> 学习曲线很高,但是学会熟练使用后就效率很高 +> 学习曲线很高,但是学会熟练使用后就效率很高 `官方教程程序 vimtutor` +- [vim galore](https://github.com/mhinz/vim-galore) - [Vim galore 中文翻译](https://github.com/wsdjeg/vim-galore-zh_cn) -- [Space Vim](https://github.com/topics/spacevim)`把Vim玩上天了` - - [GitBook : Space Vim Guide](https://legacy.gitbook.com/book/everettjf/spacevimtutorial/details) - - [quick start](https://spacevim.org/quick-start-guide/) - - [中文 文档](https://spacevim.org/cn/documentation/) +> [bytefluent.com/vivify](http://bytefluent.com/vivify/) `方便的自制主题` + +> [vim教程网](https://vimjc.com/) ## Tips 1. 误按 `Ctrl S` 终止屏幕输出(即停止回显)你敲的依然有效,只是看不见 `Ctrl Q` 即可恢复 -************** -`vim输出的信息` -``` - 系统 vimrc 文件: "$VIM/vimrc" - 用户 vimrc 文件: "$HOME/.vimrc" - 第二用户 vimrc 文件: "~/.vim/vimrc" - 用户 exrc 文件: "$HOME/.exrc" - defaults file: "$VIMRUNTIME/defaults.vim" - $VIM 预设值: "/usr/share/vim" -``` +1. `/usr/share/vim/vim80/macros/less.sh` vim 版 less + - 具备语法高亮 路径中间是依据vim版本来的, 按实际情况改动 + +1. 设置默认编辑器 `export EDITOR=/usr/bin/vim` + +1. vim 会导致文件 inode 变更 [why inode value changes when we edit in “vi” editor?](https://unix.stackexchange.com/questions/36467/why-inode-value-changes-when-we-edit-in-vi-editor) + +1. 命令模式输入 `:%!xxd` 就可以16进制方式查看和编辑二进制文件了 *注意最左行号和最右的预览内容不能修改* + - `:%!xxd -r` 回到 ascii模式 +1. `:set fenc=utf8 nobomb ff=unix` 设置文件无BOM U8格式 +************************ + +`配置文件优先级` +- 系统 vimrc 文件: `$VIM/vimrc` +- 用户 vimrc 文件: `$HOME/.vimrc` +- 第二用户 vimrc 文件: `~/.vim/vimrc` +- 用户 exrc 文件: `$HOME/.exrc` +- defaults file: `$VIMRUNTIME/defaults.vim` +- $VIM 预设值: `/usr/share/vim` + ## 基本配置 -- 在文件 全局:`/etc/vim/vimrc` 先备份一下 `sudo cp /etc/vim/vimrc /etc/vim/vimrc.bak` - - 或者当前用户:`~/.vimrc` 中添加如下内容 -```sh - set showcmd " Show (partial) command in status line. - set autowrite " Automatically save before commands like :next and :make - set nocompatible - set number - filetype on - syntax on - set history=1000 - set autoindent - set smartindent - set tabstop=4 - set shiftwidth=4 - set showmatch - set guioptions=T - set ruler - set nohls - set backspace=2 - imap jj +1. 全局修改 :`/etc/vim/vimrc` +1. 或者配置放在 `/etc/vim/vimrc.local` + - 然后在 `/etc/vim/vimrc` 中添加: + ```sh + if filereadable("/etc/vim/vimrc.local") + source /etc/vim/vimrc.local + endif + ``` +1. 或者当前用户:`~/.vimrc` [个人vim配置](https://github.com/Kuangcp/Configs/blob/master/Linux/vimrc.local) + +************************ + +### GVim +**~/.gvimrc** +``` +:set guifont=IBM\ Plex\ Mono\ 12 +colorscheme desert +syntax enable +syntax on ``` ## 基础操作 > [参考博客](http://www.jianshu.com/p/bcbe916f97e1) > [高效率编辑器 Vim——操作篇,非常适合 Vim 新手](https://linuxtoy.org/archives/efficient-editing-with-vim.html) +- v 可视化操作 + +> [参考: vim中执行shell命令小结](https://blog.csdn.net/topgun_chenlingyun/article/details/8013115) + ### 跳转 -- k j h l 上下左右 -- Ctrl+f 上翻一页 -- Ctrl+b 下翻一页 -- H M L 跳转到屏幕 顶 中 尾 - - 2H 第二行 3L 倒数第三行 +- K J H L 上下左右 +- Ctrl+F 上翻一页 +- Ctrl+B 下翻一页 +- H M L 跳转到屏幕 顶 中 尾 + - 2H 第二行 3L 倒数第三行 - `*` 当光标在某单词上 会进行搜索跳转到下一个 - `#` 与`*` 一样,不过是跳转到上一个 @@ -76,7 +106,9 @@ - `g_` 跳转到最后一个不是空格的字符的位置 - `gg` 跳转到文件第一行的起始位置 - `G` 跳转到文件最后一行起始位置 + - `5gg`或`5G` `:5` 跳转到 5 行的起始位置 +- `number` 正数则是往下,负数则是往上 (相对) `行内移动` - `w` 右移到下一个字的开头 @@ -86,13 +118,31 @@ - `$` 右移光标到行末尾 - `^` 移动到本行第一个非空字符 -- [ ] 在文本文件中通过文件名字符串 跳转到对应的文件, 再跳转回来 +`fg` 在光标所在处(如果是有效的目录或者文件,就能直接跳转过去) -### 搜索匹配 +#### 高级跳转 +- fg 如果光标所在处是一个完整的路径,就跳转到该文件 + - `Ctrl Shift 6` 或者 `:e#` 跳回来 | [参考 stackoverflow](https://stackoverflow.com/questions/133626/how-do-you-return-from-gf-in-vim) + +### 搜索和替换 - `/name` 正向搜索字符串 name - `n` 搜索后跳下一个 - `N` 搜索后跳上一个 -- `?name` 方向搜索字符串 +- `?name` 反向搜索字符串 + +> 替换 `:[range]s/pattern/string/[c,e,g,i]` + +| 参数 | 含义 | +|:----|:----| +| range | 指的是范围 1,5 指的是1-5行; `1,$`或是`1,%` 则是第一行到最后一行; `.,5`当期行到第5行 +| pattern | 就是要被替换掉的字串,可以用 regexp 來表示。 +| string | 匹配到 pattern 的字符串替换为 string +| c | confirm,每次替换前先询问 +| e | 不显示error +| g | global 全局 +| i | ignore 不分大小写。 + +> % 是目前編輯的文章,# 是前一次編輯的文章, . 表示当前行 ### 复制粘贴 > `:reg` 查看寄存器 @@ -102,6 +152,10 @@ - `"+nyy` 同理复制n行,操作系统级的剪贴板 - `"+yn` 等效 +1. vim 中粘贴内容时被自动缩进, 导致大量空格 + 1. 先执行 `:set paste` 命令,然后粘贴 + 1. 关闭 paste 模式 `:set nopaste` + ********* - `P`/`p` 将剪贴板的内容粘贴在 前/后 @@ -125,3 +179,37 @@ - `:q!` 放弃所有修改,退出 - `wq` `x` 先保存后退出 +> 先 q 再 : 就会显示最近的命令 + + +************************ + +## 插件管理 +> [vim-plug](https://github.com/junegunn/vim-plug) + +> [参考: VIM插件推荐](https://zhuanlan.zhihu.com/p/58816186) + +> 语言插件 +- vim-python +- vim-go + +************************ + +# 定制化 +## vim-init +> [Github:](https://github.com/skywind3000/vim-init) + +## spf13 +- [official site](http://vim.spf13.com/) + +## SpaceVim +> [参考: SpaceVim 中文手册](https://ruby-china.org/topics/32020)`主要看评论, 两个作者理念不同` +> [参考: 如何评价Vim配置文件SpaceVim?](https://www.zhihu.com/question/54270182) + +- [Space Vim](https://github.com/topics/spacevim) + - [GitBook : Space Vim Guide](https://legacy.gitbook.com/book/everettjf/spacevimtutorial/details) + - [quick start](https://spacevim.org/quick-start-guide/) + - [中文 文档](https://spacevim.org/cn/documentation/) + +## space-vim +> [Github:](https://github.com/liuchengxu/space-vim) diff --git a/Linux/Tool/Zsh.md b/Linux/Tool/Zsh.md deleted file mode 100644 index f3a2b2e..0000000 --- a/Linux/Tool/Zsh.md +++ /dev/null @@ -1,75 +0,0 @@ -`目录 start` - -- [Zsh](#zsh) - - [为什么要使用](#为什么要使用) - - [安装](#安装) - - [配置](#配置) - - [oh-my-zsh](#oh-my-zsh) - - [插件](#插件) - - [主题](#主题) - - [自己定制](#自己定制) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Zsh -> [arch zsh wiki](https://wiki.archlinux.org/index.php/Zsh) - -## 为什么要使用 -> 知乎:[mac 装了 oh my zsh 后比用 bash 具体好在哪儿?](https://www.zhihu.com/question/29977255) -> [终极 Shell——ZSH](https://zhuanlan.zhihu.com/mactalk/19556676) - -## 安装 -> debian系 `apt install zsh` - -## 配置 -> [某人的配置](https://github.com/lilydjwg/dotzsh) - - -### oh-my-zsh -> [Github](https://github.com/robbyrussell/oh-my-zsh) | [参考博客进行安装](https://segmentfault.com/a/1190000004695131) - -> [关于PS1环境变量的折腾](https://gitee.com/kcp1104/codes/gca14wtqvm67l9j5r0deb56#Zsh.md) `因为含特殊字符GitBook构建通不过,只能放出去了` - -1. 安装好 zsh wget git -2. `sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"` -3. `vi ~/.zshrc` 进行配置 - -``` - plugins=( - git mvn gradle autojump - ) -``` -#### 插件 -> [wiki: plugins](https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins) -> [zsh oh-my-zsh 插件推荐 ](https://hufangyun.com/2017/zsh-plugin/) - -#### 主题 -> [官网主题列表](https://github.com/robbyrussell/oh-my-zsh/wiki/Themes) -- 自带主题: - - 个人偏好 amuse clean wedisagree, muse也还好,就是没时间 - -_额外安装_ -> [额外主题列表](https://github.com/robbyrussell/oh-my-zsh/wiki/External-themes) - -- powerlevel9k - - [官方文档](https://github.com/bhilburn/powerlevel9k/wiki/Install-Instructions#option-2-install-for-oh-my-zsh) - - `git clone https://github.com/bhilburn/powerlevel9k.git ~/.oh-my-zsh/custom/themes/powerlevel9k` - - `powerlevel9k/powerlevel9k` - -- Bullet Train (桌面在用) | [Github repo](https://github.com/caiogondim/bullet-train.zsh) - - Source Code Pro for Powerline + Powerline + Awesonme 的 Bold 字体搭配最合适 - -- spaceship - - [地址](https://www.ctolib.com/denysdovhan-spaceship-zsh-theme.html) - -> [安装步骤](https://github.com/caiogondim/bullet-train.zsh#for-oh-my-zsh-users) -1. mkdir $ZSH_CUSTOM/themes/ -2. wget http://raw.github.com/caiogondim/bullet-train-oh-my-zsh-theme/master/bullet-train.zsh-theme -3. config .zshrc to `ZSH_THEME="bullet-train" ` - -- Maglev - - [Github地址](https://github.com/caiogondim/maglev) - -##### 自己定制 -> [Github doc](https://github.com/robbyrussell/oh-my-zsh/wiki/Customization) - diff --git a/Linux/Tool/img/nginx.drawio.svg b/Linux/Tool/img/nginx.drawio.svg new file mode 100644 index 0000000..808dcf9 --- /dev/null +++ b/Linux/Tool/img/nginx.drawio.svg @@ -0,0 +1,630 @@ + + + + + + + + + + + + + + + +
+
+
+ master +
+
+
+
+ + master + +
+
+ + + + +
+
+
+ worker +
+
+
+
+ + worker + +
+
+ + + + +
+
+
+ worker +
+
+
+
+ + worker + +
+
+ + + + +
+
+
+ worker +
+
+
+
+ + worker + +
+
+ + + + +
+
+
+ 图1 - Nginx 进程模型 +
+
+
+
+ + 图1 - Nginx 进程模型 + +
+
+ + + + +
+
+
+ client +
+
+
+
+ + client + +
+
+ + + + +
+
+
+ client +
+
+
+
+ + client + +
+
+ + + + +
+
+
+ client +
+
+
+
+ + client + +
+
+ + + + + + + + + + +
+
+
+ 图2 - 传统Web服务器模型 +
+
+
+
+ + 图2 - 传统Web服务器模型 + +
+
+ + + + +
+
+
+ 事 +
+ 件 +
+ 收 +
+ 集 +
+ , +
+
+ 事 +
+ 件 +
+ 分 +
+ 发 +
+ 进 +
+ 程 +
+
+
+
+ + 事件收集,事件分发进程... + +
+
+ + + + +
+
+
+ 连接建立事件 +
+
+
+
+ + 连接建立事件 + +
+
+ + + + +
+
+
+ 连接关闭事件 +
+
+
+
+ + 连接关闭事件 + +
+
+ + + + +
+
+
+ 数据交互 +
+
+
+
+ + 数据交互 + +
+
+ + + + + + + + + + +
+
+
+ 事 +
+ 件 +
+ 消 +
+ 费 +
+ 进 +
+ 程 +
+
+
+
+ + 事件消费进程... + +
+
+ + + + + + + + +
+
+
+ 队列 +
+
+
+
+ + 队列 + +
+
+ + + + +
+
+
+ 椭圆为数据 矩形为进程 +
+
+
+
+ + 椭圆为数据 矩形为进程 + +
+
+ + + + +
+
+
+ 图3 - Nginx服务器模型 +
+
+
+
+ + 图3 - Nginx服务器模型 + +
+
+ + + + +
+
+
+ 事 +
+ 件 +
+ 收 +
+ 集 +
+ , +
+
+ 事 +
+ 件 +
+ 分 +
+ 发 +
+
+
+
+ + 事件收集,事件分发... + +
+
+ + + + +
+
+
+ TCP连接建立事件 +
+
+
+
+ + TCP连接建立事件 + +
+
+ + + + +
+
+
+ TCP连接关闭事件 +
+
+
+
+ + TCP连接关闭事件 + +
+
+ + + + +
+
+
+ 数据交互 +
+
+
+
+ + 数据交互 + +
+
+ + + + + + + + + + +
+
+
+ TCP连接可写事件 +
+
+
+
+ + TCP连接可写事件 + +
+
+ + + + +
+
+
+ TCP连接可读事件 +
+
+
+
+ + TCP连接可读事件 + +
+
+ + + + + + + + + + + + + + + + +
+
+
+ 连接建立事件消费者 +
+
+
+
+ + 连接建立事件消费者 + +
+
+ + + + +
+
+
+ 连接关闭事件消费者 +
+
+
+
+ + 连接关闭事件消费者 + +
+
+ + + + +
+
+
+ 连接读事件消费者 +
+
+
+
+ + 连接读事件消费者 + +
+
+ + + + +
+
+
+ 连接写事件消费者 +
+
+
+
+ + 连接写事件消费者 + +
+
+ + + + +
+
+
+ 多个Work进程 +
+
+
+
+ + 多个Work进程 + +
+
+ + + + +
+
+
+ Nginx +
+
+
+
+ + Nginx + +
+
+ + + + +
+
+
+ 消费者不会持有实际进程资源而是被事件驱动模型所调度 +
+
+
+
+ + 消费者不会持有实际进程资源而是被事件驱动模型所调度 + +
+
+ + + + +
+
+
+ Worker进程最好与CPU数量一致,更有利于避免发生抢占 +
+
+
+
+ + Worker进程最好与CPU数量一致,更有利于避免发生抢占 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/Linux/Vcs/GitAction.md b/Linux/Vcs/GitAction.md deleted file mode 100644 index 0526310..0000000 --- a/Linux/Vcs/GitAction.md +++ /dev/null @@ -1,439 +0,0 @@ -`目录 start` - -- [GitInAction](#gitinaction) - - [Tips](#tips) - - [配置记住密码](#配置记住密码) - - [【安装】](#安装) - - [Linux(debian系)](#linuxdebian系) - - [windows](#windows) - - [【简单使用】](#简单使用) - - [配置GPG](#配置gpg) - - [实验楼上使用Github](#实验楼上使用github) - - [码云](#码云) - - [【git初始化配置】](#git初始化配置) - - [【VI编辑器的使用】](#vi编辑器的使用) - - [【配置SSH连接上Github】](#配置ssh连接上github) - - [Github上fork别人项目的操作](#github上fork别人项目的操作) - - [Github上PR](#github上pr) - - [.gitingnore文件](#gitingnore文件) - - [终端中显示当前分支](#终端中显示当前分支) - - [命令的自动补全](#命令的自动补全) - - [搭建Git服务器](#搭建git服务器) - - [【使用git daemon搭建本地简易Git_Server】](#使用git-daemon搭建本地简易git_server) - - [【HTTP访问Git服务器】](#http访问git服务器) - - [【配置HTTPS】](#配置https) - - [【使用SSH登录GitServer】](#使用ssh登录gitserver) - - [基础命令概述](#基础命令概述) - - [reset使用方式](#reset使用方式) - - [1.回滚add操作](#1回滚add操作) - - [2.回滚最近一次commit](#2回滚最近一次commit) - - [3.回滚最近几次的commit并添加到一个新建的分支上去](#3回滚最近几次的commit并添加到一个新建的分支上去) - - [4.永久删除最近几次](#4永久删除最近几次) - - [5.回滚merge和pull操作](#5回滚merge和pull操作) - - [6.在被污染的working_tree中回滚merge或者pull](#6在被污染的working_tree中回滚merge或者pull) - - [7.被中断的工作流程](#7被中断的工作流程) - - [8.Reset一个单独的文件](#8reset一个单独的文件) - - [9.保留working_tree并且丢弃一些commit](#9保留working_tree并且丢弃一些commit) - -`目录 end` |_2018-09-01_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# GitInAction -> [try git](https://try.github.io/) - -> [Github: lazygit](https://github.com/jesseduffield/lazygit)`命令行的简易图形化` - -## Tips -1. 虽然在物理上本地仓库中所有文件是放在一起的,但是分支之间是互不能访问以及操作的 -2. 在本地的每次commit都是有index的,上传到github可以不用那么频繁,反正都是有记录的 -3. 出现了冲突,从而无法自动merge: -``` - git pull 对方的分支 - git checkout 自己的分支 - git merge --no-ff 对方的分支 - git push (自己的源+分支)origin master -``` -4. 切记:避免隐私的配置文件上传github时,将配置分离出来配置.gitignore中忽略掉配置文件,然后建立模板文件夹放待配置的文件即可 - - `大意的后果`:[程序员复仇记 | 这些年,GitHub 上泄露了些什么?](https://zhuanlan.zhihu.com/p/33424997) - - [不小心把密码上传到 GitHub 了,怎么办](https://www.bennythink.com/git-password.html) -5. `cat ~/.ssh/id_rsa.pub | xclip -sel clip` 复制公钥 -6. Linux下当大量文件出现mode的变化(因为你的目录移动,文件权限变化等影响的)可以设置忽略掉 `git config core.fileMode false` - * 当将目录备份出去,然后重装系统粘贴回来,权限就变了,mode也变了,可以设置忽略掉改变 -7. git的输出中文乱码 执行 `git config --global core.quotepath false`即可 - -## 配置记住密码 -- `Windows下记住密码` : - * 新建环境变量 HOME 值:`%USERPROFILE%` - * 在C盘User下你的当前用户目录下新建` _netrc `文本文件: - * `machine https://github.com/Kuangcp/` - * `login ***` - * `password ***` - * 成功配置,测试便知 - -- `Linux下记住密码`:(如果使用了多个github账号,设置这个后只能使用一个账号的自动登录,另一个账号将完全连不上github,ssh也只能一个账号配一个,不能多个账号用一个ssh) - * `touch .git-credentials` - * `vim .git-credentials` - * 输入: ` http://{username}:{password}@github.com` 或者是https开头 - * `git config --global credential.helper store` - * `~/.gitconfig` 文件中多了以下内容即可 - * [credential] - * helper = store - -- `ssh 方法:(推荐)` - - `ssh-keygen` 不设置密码 - - `cat ~/.ssh/id_rsa.pub | xclip -sel clip` 添加即可 - -********* -- 关于许可证 [Github许可证网](https://choosealicense.com/licenses/) - - 新建项目的时候可以选择 添加.gitignore和许可证类别 许可证大致分为 MIT Apache2.0 GPL - - `MIT` 简单宽松的许可证,任何人可以拿代码做任何事与我无关` eg: jQuery、Rails` - - `Apache` 关注于专利,这类似于MIT许可证,但它同时还包含了贡献者向用户提供专利授权相关的条款。 `Apache、SVN和NuGet` - - `GPL` 关注于共享改进,这是一种copyleft许可证,要求修改项目代码的用户再次分发源码或二进制代码时,必须公布他的相关修改。 `Linux、Git` - -********** -## 【安装】 -### Linux(debian系) -- `sudo apt-get install git` - -`安装最新版本Git` -- `sudo add-apt-repository ppa:git-core/ppa` - - 如果命令找不到就先安装这个 `sudo apt-get install software-properties-common` -- `sudo apt update` -- `sudo apt install git` - -### windows -- 直接搜索git-for-windows 建议使用360搜索,会有360的下载链接,无意间发现 毕竟官网的下载速度不敢恭维 - -************** -## 【简单使用】 - -*Github下拉到eclipse* -- 1.在GitHub上新建一个项目,不勾选初始化,复制下URL -- 2.在eclipse新建项目然后在eclipse里添加git remote -- 3.commit -》push 完成 -- 4.打开Git Bash 使用命令行再查看一下 - -*本地已有代码关联远程空仓库* -- 先在远程建立空仓库 一般各大平台也都有命令提示 - - 传送门: [Gitee](https://gitee.com) | [Github](https://github.com/) | [Bitbucket](https://bitbucket.org/) | [GitLab](https://gitlab.com/) ... -``` - git remote add origin https://github.com/Kuangcp/StudentManager.git - git push -u origin master -``` -- 说明下上面的命令 第一条是设置了一个远程仓库 仓库名为origin URL是后面那个,一般默认的远程仓库名都叫origin - - 名字可以随便取 但是提交就要标明仓库名了,而且分支也是一样的默认是master可以自己加别的分支. `git push -u 随便 随意` - -*建立本地空仓库并关联到远程仓库* -- 1.先在GitHub上创建一个仓库,不勾选README(不然添加远程仓库还得pull一下README文件才能push) -- 如果本地没有则 `mkdir 库名 `创建一个文件夹,最好和远程的库同名 -- 2.在某本地项目根目录下运行 `Git Bash` - - 2.1 `git init` 初始化(建立 `.git` 目录) - - 2.2 `touch README.md` - - 2.3 `git remote add origin master URL` 连上远程仓库 - - 2.4 `git push -u origin master` 输入用户名,密码 (若因为没有上游节点就按提示输入命令建立初始节点即可 git push --setupstream origin master) - - 原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:`git branch --set-upstream dev origin/dev` master同理 - -### 配置GPG -> [阮一峰:GPG入门教程](http://www.ruanyifeng.com/blog/2013/07/gpg.html) - -- 能够提高安全性,但是麻烦,不过向来这两者就是不可兼得的. - -********************* -### 实验楼上使用Github -> 方便学习使用 - -- `git clone URL` 复制下来,默认是master -- `git branch 新分支名` 新建一个分支,切换过去,使用的就是这个新分支放代码 -- `git push origin 新分支名` add commit 之后就push -- `git fetch origin 已有分支` 下拉别的分支代码 - -### 码云 -> [帮助文档](http://git.mydoc.io/) - -- [如何进行减少提交历史数量以及修改自己的commit中的邮箱](http://git.mydoc.io/?t=83152) -- [改写历史,永久删除git库的物理文件 ](https://my.oschina.net/jfinal/blog/215624?fromerr=ZTZ6c38X) -******************************** -## 【git初始化配置】 -```sh - git config --global user.name " " - git config --global user.email " " - git config --global color.ui auto -``` -> 如果是多个账号使用同一台电脑就不要配置这个,单独配置每个仓库下的用户名,邮箱即可
-> `git config user.name ""` - -### 【VI编辑器的使用】 -- 在pull或者合并分支的时候有时会遇到打开 VI编辑器 的状态 可以不输入(直接下面3,4步) -`如果要输入解释的话就需要:` -``` - 1.按键盘字母 i 进入insert模式 - 2.修改最上面那行黄色合并信息,可以不修改 - 3.按键盘左上角"Esc" - 4.输入`:wq`,按回车键即可 或者 :x -``` - -## 【配置SSH连接上Github】 -> 其他平台类似 - -- 【Markdown语法】: - - @用户名, @组织名 ;#编号 会连接到该仓库对应的Issue编号 。 - - 通过 用户名/仓库名 #编号 来指定仓库的指定Issue -- 【将Bash和GitHub绑定起来】: - - 1.在GItHub上设置SSH key, 有一个即可 - - 2.$ssh-keygen -t rsa -C "xxx@outlook.com" 生成一个具有指定邮箱的rsa密钥对,然后复制到平台上 - - 3.设置密钥对密码. 当然为了偷懒就不设置,不然每次提交都要输入.... - - 4.测试SSH连接 $ssh -T git@github.com 输入 密钥对 密码 - - 询问将github的ip加入已知列表中 选择yes - -### Github上fork别人项目的操作 - -**合并对方最新代码** -> 1.首先fork一个项目, 然后clone自己所属的该项目下来,假设原作者A自己为B -> 2.进入项目目录,添加原作者项目的URL到该项目的远程分支列表中 `git add remote A A_URL` -> 3.fetch源到本地 `git fetch A` -> 4.合并两个分支代码 `git merge --no-ff A/master` -> 5.push即可 - -### Github上PR -> [Using git to prepare your PR to have a clean history](https://github.com/mockito/mockito/wiki/Using-git-to-prepare-your-PR-to-have-a-clean-history) -- [ ] Learn -******************** -### .gitingnore文件 -> [Github: gitignore](https://github.com/github/gitignore) | 一行是一个配置, 是独占一行的 - -- 使用 `#` 注释一行 -- `test.txt` 忽略该文件 -- `*.html` 忽略所有HTML后缀文件 -- `*[o/a]` 忽略所有o和a后缀的文件 -- `!foo.html` 不忽略该文件 - -`示例文件` -```conf - # maven # - target/ - # IDEA # - .idea/ - *.iml - out/ - # eclipse # - bin/ - .settings/ - .metadata/ - .classpath - .project -``` -******************** -### 终端中显示当前分支 -> 使用 .git-prompt.sh 在Bash下显示当前分支 Windows环境不用看,安装的Git-for-windows软件已经会显示分支名了 - -- `wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh -O ~/.git-prompt.sh` 下载脚本 -- `chmod +x ~/.git-prompt.sh` 赋予可执行权限 -- 在 .bash_alases文件中添加 -```conf - lightgreen='\[\033[1;32m\]' - lightcyan='\[\033[1;36m\]' - lightpurple='\[\033[1;35m\]' - yellow='\[\033[1;33m\]' - nocolor='\[\033[0m\]' - source ~/.git-prompt.sh - set_bash_prompt(){ - #PS1="[e[32m]u[e[m]@[e[33m]W[e[36m]$(__git_ps1 ' (%s)')[e[31m]$[e[m]" - PS1="${lightcyan}\t${lightgreen}\w${lightpurple}$(__git_ps1 ' (%s)')${yellow} → \[\e[m\]" - } - PROMPT_COMMAND="set_bash_prompt; $PROMPT_COMMAND" -``` -******************** -### 命令的自动补全 -> [git自动补全脚本GitHub地址](https://github.com/git/git/tree/master/contrib/completion) - -- 下载脚本 `wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash -o ~/.git-completion.bash` - - 在 .bashrc 或者 .bash_aliases 中添加 source ~/.git-completion.bash - - 重启终端或者 `source .bashrc`即可 -- 双击tab可以得到命令建议 - -*************************************************** -## 搭建Git服务器 -### 【使用git daemon搭建本地简易Git_Server】 -> daemon可能要安装一下 `sudo apt install git-daemon` - -- 先创建一个目录结构 -- Repository - - Project1 - - .git - - Project2 - - Project3 - - 每个Project下都有`.git` 文件夹 -- Repository目录下执行:`git daemon --export-all --base-path='Repository目录' --port=8096` - - `--export-all` 开放当前目录下所有项目 - - `--enable=receive-pack` 为了安全,默认是仓库不能被修改,添加这个参数就可以push了 - - `--base-path=''` 指定开放的基本目录(指定开放别的路径) - - `--port=8096` 指定开放的端口 - - `--verbose` 启动看到的日志信息更多 - - `&` 末尾加上表示后台运行,默认是阻塞了当前git bash命令行 - -- 使用退出程序的操作即可, Ctrl+Shift+C 放在了后台就jobs或者ps 然后kill -- 在需要克隆的目录下` git clone git://localhost:8096/Project1` - -### 【HTTP访问Git服务器】 -- 安装Apache: Web服务器 -- 配置Apache服务器的开放的目录以及Git的路径 -```xml - - AuthType Basic - AuthName "GIT Repository" - AuthUserFile "/home/mythos/GitRemoteRepo/htpassed" - Require valid-user - -``` -- 切换到Apache的bin目录下:`htpasswd -cmb /home/mythos/GitRemoteRepo/htpsswd 账号名 密码` -- 到仓库目录下 `git init --bare 程序项目名称` -- `git clone http://localhost/git/程序项目名称` 输入用户名密码即可 - -#### 【配置HTTPS】 -- 切换到Apache主目录下 `bin\openssl genrsa -des3 -out server.key 2048 -config conf\openssl.cnf` 输入密码 -- `bin\openssl req -new -key server.key -out server.csr -config conf\openssl.cnf` 输入之前密码 -- `bin\openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt` 输入之前密码 -- 把server.key 更名为server.key.old :`bin\openssl rsa -in server.key.old -out server.key` -- 将server.key server.crt 移动到conf -- 修改 httpd.conf 去掉如下三行的注释 # 字符 -``` - LoadModule socache_shmcb_module.. - LoadModule ssl_module.. - Include conf/extra... -``` -- 因为是自己建立的SSL证书 所以要去掉SSL验证 `git -c http.sslVerify=false clone URL ` -- 或者直接改配置文件,省的每次输这么多 `git config http.sslVerify false` - -#### 【使用SSH登录GitServer】 -- [ ] 实践一下 - -********************* -## 基础命令概述 -- `git touch file1 file2 ` 新建三个文件 -- `echo " ">>file1 ` 修改文件file1 -- `git rm 文件名 ` : 删除文件至缓存区 -- `git commit -am " " `从缓存提交(切记要先 commit 才能 push) -- `git diff` : 查看当前工作树和暂存区的差别 -- `git diff --cached `:查看缓存中文件修改的痕迹和对比 输入q 退出 -- `git log --graph `:查看(图形化)提交日志 输入q退出 -- `git banrch `分支名 :创建新的分支 -- `git branch -a`查看当前分支信息 -- `git checkout -b`:创建一个分支,并立即切换 -- `git checkout -b feature-D origin/feature-D` 新建一个分支来接收同步后面那个远程仓库的分支 -- `git pull `:获取最新的远程仓库分支 -- `git pull origin feature-D `:只把本地的feature-D分支更新到最新 -- `git reset --hard 哈希值`:数据库的回滚操作似的 -- `git reflog ` 查看仓库的操作日志 -- `git mv -k oldName newName` :更改文件名字 - -```sh - usage: git [--version] [--help] [-C ] [-c name=value] - [--exec-path[=]] [--html-path] [--man-path] [--info-path] - [-p | --paginate | --no-pager] [--no-replace-objects] [--bare] - [--git-dir=] [--work-tree=] [--namespace=] - [] -``` - -************************** -## reset使用方式 -### 1.回滚add操作 -``` - edit (1) - git add a.txt b.txt - 看邮件(2) - git reset(3) - git pull URL - 1.1 编辑了两个文件并且添加到了index - 1.2 接收邮件,发现某人要你pull,有一些改变需要你merge 下来 - 1.3 然而你已经把index给改变了,因为当前的index 和 HEAD commit不匹配了,但是你知道,即将pull的东西不会影响 - 到a.txt 和 b.txt,因此你可以revert这两个文件的改变,revert后,那些改变依然在working directory中,因此需要执行git reset - 1.4 然后,执行了pull后 自动merge,两个文件依然在working directory中 -``` -### 2.回滚最近一次commit -``` - git commit ... - git reset --soft HEAD^(1) - edit (2) - git commit -a -c ORIG_HEAD(3) - 2.1 当提交后,你发现提交的代码不完善,需要重新编辑一下,执行 1 语句让working directory和reset之前一样,不做改变 - 2.2 对working tree下的文件做修改 - 2.3 然后使用reset之前那次commit的注释等相关信息都重新提交,注意老的HEAD会被备份到文件.git/ORIG_HEAD中,命令中就是引用了这个老的相关信息 - -a 表示自动将所有的修改的和删除的文件都放进 stage area(理解为代码区,未被git跟踪的文件不受影响) - -c 表示 拿已经提交的commit对象中的信息来做这次的提交 - 这条命令就是,将所有更改的文件加入到stage area中,并使用上次的提交信息来提交 -``` -### 3.回滚最近几次的commit并添加到一个新建的分支上去 -``` - git branch myth/test (1) - git reset --hard HEAD^3 (2) - git checkout myth/test (3) - 3.1 你已经提交了好几个commit,但是觉得不够成熟和完善,不足以添加到master分支上,所以在当前HEAD创建一个新分支 - 3.2 然后回滚掉最近三次提交(删除) - 3.3 切换到新分支上就能对代码进行润色了,等待之后的merge -``` -### 4.永久删除最近几次 -- `commit git reset --hard HEAD~3` - -### 5.回滚merge和pull操作 -``` - git pull URL (1) - git reset --hard (2) - git pull .topic/branch (3) - git reset --hard ORIG_HEAD (4) - 5.1 从origin上拉下来一些更新,但是产生了许多冲突,暂时又没时间去解决这些冲突,所以想撤销pull操作,等待以后来pull - 5.2 由于pull操作产生了冲突,因此所有pull下来的改变尚未提交,仍然在stage area中,这种情况下 - git reset --hard 与 git reset --hard HEAD 效果一样 - 都是清除那些使index和working directory乱套的东西 - 5.3 将topic/branch 合并到当前的branch,这次没有冲突,并且合并后的更改自动提交 - 5.4 但是此时又觉得将topic/branch合并过来又太早了,决定回滚merge操作,执行4语句 之前有说过,git reset操作会备份一个ORIG_HEAD, - pull和merge操作同样会,为了回滚操作 -``` - -### 6.在被污染的working_tree中回滚merge或者pull -``` - git pull (1) - git reset --merge ORIG_HEAD (2) - 6.1 即使在本地已经更改了tree,导致了index的变化,也可以放心的pull,前提是你知道将要pull的内容不会覆盖你的working tree中的内容 - 6.2 git pull 之后,你发现这次pull的有问题,想要撤销操作,如果使用git reset --hard ORIG_HEAD也可以,但是这会删除add的代码 - 使用 git reset --merge ORIG_HEAD 就可以避免回滚操作时删除add的代码 -``` - -### 7.被中断的工作流程 -``` -在实际开发中经常出现这样的情形:你正在开发一个大的feature,此时来了一个紧急的BUG需要修复,但是目前在working tree 中的内容还不足以commit - ,但是又必须切换到另外的branch去 fix bug - git checkout feature; - 码代码 - git commit -a -m "暂时中断OO" (1) - git checkout master - 修复bug - git commit ; - git checkout feature - git reset --soft HEAD^ #go back to OO's state (2) - git reset (3) - 7.1 属于临时提交。随便加点注释 - 7.2 这次reset删除了OO的commit,并且把working tree设置成提交OO之前的状态 - 7.3 此时,在index中仍然留有OO提交时所做的uncommit changes,git reset 将会清理index成为尚未提交时的状态,便于之后的工作 -``` - -### 8.Reset一个单独的文件 -``` -git reset -- a.txt (1) -git commit -am "Commit files inindex" (2) - git add a.txt (3) - 8.1 把文件单独从index中去除 - 8.2 将index中的文件提交 - 8.3 再次添加回文件 -``` -### 9.保留working_tree并且丢弃一些commit -``` - git tag start - git checkout -b branch 1 - 编写 - git commit .... (1) - 编写 - git checkout -b branch2 (2) - git reset --keep start (3) -``` -- 9.1 这次是把branch1中的改变提交了 -- 9.2 此时发现,之前的提交不属于这个branch,此时你新建了branch2,并切换到了该branch上 -- 9.3 此时你可以使用reset --keep 把在start之后的commit清除掉,但是保持了working tree的不变 diff --git a/Linux/Vcs/GitBase.md b/Linux/Vcs/GitBase.md deleted file mode 100644 index 50c8dbd..0000000 --- a/Linux/Vcs/GitBase.md +++ /dev/null @@ -1,465 +0,0 @@ -`目录 start` - -- [Git基础](#git基础) - - [版本控制系统(VCS)](#版本控制系统vcs) - - [Git常用命令](#git常用命令) - - [Tips](#tips) - - [清理仓库](#清理仓库) - - [gc](#gc) - - [fork相关操作](#fork相关操作) - - [仓库基本命令](#仓库基本命令) - - [config](#config) - - [status](#status) - - [rm](#rm) - - [commit](#commit) - - [提交行为准则](#提交行为准则) - - [remote](#remote) - - [submodule](#submodule) - - [show](#show) - - [push](#push) - - [log](#log) - - [对比两个分支的差异](#对比两个分支的差异) - - [diff](#diff) - - [tag](#tag) - - [分支操作](#分支操作) - - [开发流程的常用分支操作](#开发流程的常用分支操作) - - [clone](#clone) - - [branch](#branch) - - [checkout](#checkout) - - [fetch](#fetch) - - [pull](#pull) - - [merge](#merge) - - [rebase](#rebase) - - [grep](#grep) - - [常用文件](#常用文件) - - [.gitignore](#gitignore) -- [注释](#注释) - - [gitattributes](#gitattributes) - - [Tools](#tools) - - [git-svn](#git-svn) - - [Submodules](#submodules) - - [各个VCS工具的区别以及优缺点](#各个vcs工具的区别以及优缺点) - - [Git](#git) - - [SVN](#svn) - - [repos的使用](#repos的使用) - -`目录 end` |_2018-09-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Git基础 -> Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. --[git-scm.com](https://git-scm.com/) - -> [Github:git](https://github.com/git/git) -> [official doc: git](https://git-scm.com/docs) -> [Git官网中文教程](https://git-scm.com/book/zh/v2) | [对应的仓库](https://github.com/progit/progit2) - -*************************** -## 版本控制系统(VCS) -- [码农翻身:小李的版本管理系统](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513204&idx=1&sn=c4c493d771a167a84ace01c3e016417e&scene=21#wechat_redirect) - -********************** -## Git常用命令 -> [git-tips](https://github.com/521xueweihan/git-tips)`学习Git的仓库` -> [git权威指南的组织](https://github.com/gotgit)`完整书籍,以及相关测试题` - -> [使用原理视角看 Git](https://coding.net/help/doc/practice/git-principle.html) -> [如何高效地使用 Git](https://zhuanlan.zhihu.com/p/30561653) - -> [参考博客: 重看”Linus Torvalds on Git”视频](http://www.techug.com/post/review-of-linus-torvalds-on-git.html) -> [GUI客户端](https://git-scm.com/downloads/guisQ) - -- [tig](http://jonas.nitro.dk/tig/manual.html) `tig命令,git的加强版` -### Tips -- `git ls-files` 列出文件列表 - - `git ls-files | xargs wc -l` 计算文件中程序代码行数 通过工具:`xargs` `wc` (中文命名的文件编码问题无法计算行数) - - `git ls-files | xargs cat | wc -l` 计算行数总和 -- [二分查找捉虫记](http://www.worldhello.net/2016/02/29/git-bisect-on-git.html)`通过分析提交历史查到哪次提交引起的Bug然后检出,修复` - -- [API: github开发接口](https://developer.github.com/v3/) - -### 清理仓库 -> [bfg-cleaner](https://rtyley.github.io/bfg-repo-cleaner/) - -> [参考博客1 彻底删除](http://www.itwendao.com/article/detail/413282.html) | -> [参考博客2 彻底删除](http://blog.csdn.net/meteor1113/article/details/4407209) | -> [参考博客3 删除大文件](http://www.gzhphb.com/article/78/784131.html) -> [参考博客4 减小磁盘占用](http://zhongmingmao.me/2017/04/19/git-reduce/) -> [删除仓库的某个时间点之前的历史记录,减少.git 目录大小](https://www.v2ex.com/t/297802) -> [如何清洗 Git Repo 代码仓库](http://www.open-open.com/lib/view/open1414632626075.html) -- 因为删除的文件是会留在仓库,为了以后恢复用,但是这样的话仓库就会越来越大了 -- `强制删除`,并且从git索引中也去掉,相当于彻底删除 - - `git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch 文件的路径' --prune-empty --tag-name-filter cat -- --all` - - `git push origin --force --all` - - `git push origin --force --tags` - - 使用`git rebase`来更新分支,而不是 `git merge` 不然文件又回来了 - -- 然而这个笔记仓库,之前将图片文件也放在了仓库里,删除之前也改动了名字,现在根本找不到文件了,如果要减小仓库大小只能重建了 - - 猜测他的文件都在 `.git/objects/pack/` 里留有备份 - - 最简单的减小传输的大小的是clone时加上 `--depth 1` 对之前的分支延迟下载 - -#### gc -> 只能压缩一部分空间 -`git gc -h`: -- `--aggressive` 默认使用较快速的方式检查文档库,并完成清理,当需要比较久的时间,偶尔使用即可 -- `--prune[=<日期>]` 清除未引用的对 -- `--auto` 启用自动垃圾回收模式 -- `--force` 强制执行 gc 即使另外一个 gc 正在执行 - -### fork相关操作 -- fork之后,想要更新原作者的分支:`git remote add 名称 原作者URL` -- 拉取更新 :`git fetch 名称` -> 在合并别人仓库源码时,相当于两个不同源的分支之间的合并操作 - -*************** -### 仓库基本命令 -> 如果没有一个Github 码云这样的平台, 而只是单独的两个点, 两个用户或者IP之间要使用同一个仓库进行开发 -> 两个人互为对方的远程库, 互为服务器即可完成, 即使使用的是动态IP, 应该也不会受太大影响 -> 使用 `git help 加上命令`, 就能看到命令对应的文档 - -#### config -- `git config user.email ***` 和 `git config user.name ***` 这两个是必须的, - - 如果想统一配置不想每个仓库单独配置就 `git config --global user.name` email同理 -- `git config http.postBuffer 524288000` 设置缓存区大小为 500m -- `git config core.fileMode false` 忽略文件的mode变化,一般发生在文件的复制粘贴之后(跨系统?) - -#### status -- `-s --short` 简化输出 - - ?? 表示新添加未跟踪 - - A 新添加到暂存区 - - M 修改过的文件 - - MM 修改了但是没有暂存 - -#### rm -- 删除文件 `git rm 文件` -- 从git仓库中删除文件, 但是文件系统中保留文件 `git rm --cached 文件` - - 如果仅仅是想从仓库中剔除, 那么执行完命令还要在 `.gitignore` 文件中注明, 不然又add回去了 - -#### commit -- [官方文档](https://git-scm.com/docs/git-commit) -- `git commit -am "init" `: a git库已有文件的修改进行添加, m 注释 - - `git add * ` 如果有新建立文件就要add 再之后commit就不要a参数了 `git commit -m ""` - - 如果只是修改文件没有新建 `git commit -am ""` -- `git commit ` 会自动进入VI编辑器 - - 第一行:用一行文字简述提交的更改内容 - - 第二行:空行 - - 第三行:记述更改的原因和详细内容 - - 使用下面方法关闭退出 - -##### 提交行为准则 -> [参考博客: SVN提交更新的一个准则](http://www.cnblogs.com/chenlong828/archive/2008/09/22/1296193.html) -1. 提交之前先更新 - - SVN更新的原则是要随时更新,随时提交。当完成了一个小功能,能够通过编译并且并且自己测试之后,谨慎地提交。 - - 如果提交过程中产生了冲突,则需要同之前的开发人员联系,两个人一起协商解决冲突,解决冲突之后,需要两人一起测试保证解决冲突之后,程序不会影响其他功能。 - - 如果提交过程中产生了更新,则也是需要重新编译并且完成自己的一些必要测试,再进行提交。 -1. 保持原子性的提交 - - 每次提交的间歇尽可能地短,以一个小时,两个小时的开发工作为宜。如在更改UI界面的时候,可以每完成一个UI界面的修改或者设计,就提交一次。在开发功能模块的时候,可以每完成一个小细节功能的测试,就提交一次,在修改bug的时候,每修改掉一个bug并且确认修改了这个bug,也就提交一次。我们提倡多提交,也就能多为代码添加上保险。 -1. 提交时注意不要提交本地自动生成的文件 - - 对于Java来说, IDE自身配置文件, 和字节码文件是无需提交的 例如 .idea目录 iml文件 -1. 不要提交不能通过编译的代码 - - 代码在提交之前,首先要确认自己能够在本地编译。如果在代码中使用了第三方类库,要考虑到项目组成员中有些成员可能没有安装相应的第三方类库,项目经理在准备项目工作区域的时候,需要考虑到这样的情况,确保开发小组成员在签出代码之后能够在统一的环境中进行编译。 -1. 不要提交自己不明白的代码 - - 提交之后, 你的代码将被项目成员所分享。如果提交了你不明白的代码,你看不懂,别人也看不懂,如果在以后出现了问题将会成为项目质量的隐患。因此在引入任何第三方代码之前,确保你对这个代码有一个很清晰的了解。 -1. 提前协调好项目组成员的工作计划 - - 在自己准备开始进行某项功能的修改之前,先给工作小组的成员谈谈自己的修改计划,让大家都能了解你的思想,了解你即将对软件作出的修改,这样能尽可能的减少在开发过程中可能出现的冲突,提高开发效率。同时你也能够在和成员的交流中发现自己之前设计的不足,完善你的设计。 -1. 对提交的信息采用明晰的标注 - - +) 表示增加了功能 - - *) 表示对某些功能进行了更改 - - -) 表示删除了文件,或者对某些功能进行了裁剪,删除,屏蔽。 - - b) 表示修正了具体的某个bug - -#### remote -> [官方文档](https://git-scm.com/docs/git-remote) - -1. **常用参数** - - `add name URL地址` 添加远程关联仓库 不唯一,可以关联多个, 一般默认是origin - - `set-url name URL地址` 修改关联仓库的URL - - `rm URL` 删除和远程文档库的关系 - - `rename origin myth` 更改远程文档库的名称 - - `show origin` 查看远程分支的状态和信息 - -1. 删除远程库某分支`git push 远程名称 --delete 分支名称` -1. `git ls-remote` 显示本地仓库跟踪的那个远程仓库 -1. `git remote -v` 查看关联远程仓库的详情(push和pull的地址) - -- [删除,重命名远程分支](http://zengrong.net/post/1746.htm) - -#### submodule -> 子模块 - -- [ ] 完成 - -#### show -> 展示提交信息 - -- 显示当前提交的差异 `git show HEAD` HEAD替换成commit的sha值就是显示指定提交的修改 -- `git show -h` 查看更多 - -#### push -- _常用参数_ - - `-h` 查看所有参数和说明 - - `-q` 控制台不输出任何信息 - - `-f` 强制 **使用这个参数时要再三考虑清楚** - - `--all` 推送所有引用 - - `-u` upstream 设置 git pull/status 的上游 - - `git push origin master`和`git push -u origin master` 区别在于 前者是使用该远程和分支进行推送 - - 后者也是推送, 并设置origin为默认推送的远程, 以后push就不用注明远程名了(多远程的情况下要注意) - - `-d` 删除引用 - - `--tags` 推送标签(不能使用 --all or --mirror) - -- 出现 `RPC failed; result=22, HTTP code = 411` 的错误 - - 就是因为一次提交的文件太大,需要改大缓冲区 - - > 例如改成500m `git config http.postBuffer 524288000` - -- 提交本地所有分支 `git push --all` pull时同理 -- 删除远程分支 `git push 远程名称 --delete 分支名称` - -- _第一次与远程建立连接_ - - `git push -u origin master ` | `git push --set-uptream master` | `git push -all` - - 这几个都是可以的,最后那个简单, 还能将别的分支一起推上去 - -#### log -> 更多说明 查看 `git help log` | [官网文档](https://www.git-scm.com/docs/git-log) - -- `-p` 显示每次提交的内容差异 `git log -p -2` 仅显示最近两次提交的差异 -- `--stat` 查看提交对仓库修改的总览 -- `---pretty=[online/short/full/fuller/format]` 使用预定义格式显示 - - format 是可以自定义格式和占位符 -- `--graph` 图形的样子显示分支图 - -- `git log --author='A' `输出所有A开头的作者日志 -- `git log 文件名 文件名` 输出更改指定文件的所有commit 要文件在当前路径才可 -- `git log --after='2016-03-23 9:20' --before='2017-05-10 12:00' ` 输出指定日期的日志 - -- `git shortlog` 按字母顺序输出每个人的日志 - - `--numbered` 按提交数排序 - - `-s` 只显示每个提交者以及提交数量 - -**彩色输出** -```sh -alias glogc="git log --graph --pretty=format:'%Cred%h%Creset %Cgreen%ad%Creset | %C(bold cyan)<%an>%Creset %C(yellow)%d%Creset %s ' --abbrev-commit --date=short" # 彩色输出 -alias gloga='git log --oneline --decorate --graph --all' # 简短彩色输出 -alias glo='git log --oneline --decorate' # 最简单 -alias glol='git log --graph --pretty='\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\' -alias glola='git log --graph --pretty='\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --all' -``` -##### 对比两个分支的差异 -> [参考博客](http://blog.csdn.net/u011240877/article/details/52586664) - -- 查看 dev 有,而 master 中没有的 `git log dev ^master` 反之 `git log master ^dev` -- 查看 dev 中比 master 中多提交了哪些内容:`git log master..dev` -- 不知道谁提交的多谁提交的少,单纯想知道有什么不一样:`git log dev...master` -- 在上述情况下,再显示出每个提交是在哪个分支上:`git log --left-right dev...master` - - 注意 commit 后面的箭头,根据我们在 –left-right dev…master 的顺序,左箭头 < 表示是 dev 的,右箭头 > 表示是 master的。 - -#### diff -- `--cached` 查看已暂存起来的变化 等同于`--staged` -``` - git diff [options] [] [--] [...] - git diff [options] --cached [] [--] [...] - git diff [options] [--] [...] - git diff [options] - git diff [options] [--no-index] [--] -``` - -> [Github:diff-so-fancy](https://github.com/so-fancy/diff-so-fancy)`一个更方便查看diff的工具` -- 最简单的就是 `npm install -g diff-so-fancy` 安装 - -#### tag -> [官方文档](https://git-scm.com/docs/git-tag/2.10.2) - -- 查看所有标签 `git tag` - - `-l 'v1.0.*'` 列出v1.0.* - - 展示标签注释信息 `git show tagname` -- 新建一个标签并打上注释 `git tag -a v1.0.0 -m "初始版本"` - - 由指定的commit打标签 `git tag -a v1.2.4 commit-id` -- 切换标签 `git checkout tagname` 和切换分支一样的,但是标签只是一个镜像,不能修改 -- 如果要在某tag上新建一个分支, `git checkout -b branchname tagname` -- 提交指定的tag `git push origin tagname` (默认不会自动提交标签) - - 提交所有的tag `git push --tags` - -- 删除本地标签 `git tag -d tagname` -- 删除远程的tag `git push origin --delete tag ` - -****** -### 分支操作 -> [stash的争论](http://www.cppblog.com/deercoder/archive/2011/11/13/160007.html) - -#### 开发流程的常用分支操作 -- 一般的开发过程中会使用到三种临时分支(用完就删)和两个主分支 master develop - - 功能分支 `feature-*` - - 预发布分支 `release` - - 修复bug `fixbug` - -******* -- `git checkout -b feature-x develop` 从develop的分支生成一个功能分支,并切换过去 -- 完成功能后:`git checkout develop ` - - 合并: `git merge --no-ff feature-x` - - 删除: `git branch -d feature-x` - -***** -- `git checkout -b release-1.2 develop` 新建一个预发布分支 - - `git checkout master` 确认没有问题后 `git merge --no-ff release-1.2` 合并到master分支 - - `git tag -a 1.2` 打标签,这就是github上软件的版本控制 - - 没有问题后 合并到develop分支`git checkout develop` `git merge --no-ff release-1.2` - - 删除预发布分支 `git branch -d release-1.2` - -***** -- `git checkout -b fixbug-0.1 master` 新建修复bug的分支 -- `git checkout master ``git merge --no-ff fixbug-0.1 ``git tag -a 0.1.1` 修补结束后合并到master分支 -- `git checkout develop` ` git merge --no-ff fixbug-0.1` 再合并到develop分支 -- 删除分支 `git branch -d fixbug-0.1` -- 删除远程没有本地有的分支`git fetch -p` - -#### clone -- `git clone branchname URL` 克隆指定分支 -- `git clone URL 目录` 克隆下来后更名为指定目录 -- `git clone --depth 1 URL` 只克隆最近一次提交的历史, 能大大减小拉取的大小, 但是如果要用到之前的提交历史就还是要下拉下来的 类似于懒加载 - - 但是在新建一个远程仓库后, 推送时会报错:`shallow update not allowed` 因为本地库是残缺的 - - 所以需要新建一个目录, 把原仓库全拉下来, 再添加远程进行推送, 然后删除该目录, 残缺版的仓库也能正常向新远程推送提交了 - -> 克隆在指定tag状态的仓库 `git clone URL --branch=name ` 然后 Git会提示 -``` -您正处于分离头指针状态。您可以查看、做试验性的修改及提交,并且您可以通过另外 -的检出分支操作丢弃在这个状态下所做的任何提交。 -如果您想要通过创建分支来保留在此状态下所做的提交,您可以通过在检出命令添加 -参数 -b 来实现(现在或稍后)。例如: - git checkout -b -``` -按他提示来就行了 - -#### branch -- 列出远程分支 `git branch -r` -- 创建分支 `git branch name` - -#### checkout -> [官方文档 : git checkout](https://git-scm.com/docs/git-checkout) - -- `git checkout 文件名 文件名` git会在索引中找文件,有就取出,没有就从最新的commit回找,取出第一个找到的版本, - - 每个文件都是这样,也就是说如果有被删除的文件,是可以通过此来找回的 - - `git checkout . `取出文档库中所有文件的最新版本 -- `git checkout commit 节点标识符或者标签 文件名 文件名。。。` - - 取出指定节点状态的某文件,而且执行完命令后,取出的那个状态会成为head状态, - - 需要执行 `git reset HEAD` 来清除这种状态 - -> 撤销当前对文件的所有修改 `git checkout -- 文件名` 就会使用上次提交的文件来覆盖当前文件 - -- [ ] 有没有 svn cat 类似的功能 - - -#### fetch -> 访问远程仓库, 拉取本地没有的数据 -- `git fetch origin dev-test` 下拉指定远程的指定分支到本地, 本地没有就会自动新建 -- `git fetch --all` 下拉默认远程的所有分支的代码 - -#### pull -> 不仅仅是下拉代码, 还会进行merge合并, 所以安全起见, 是先fetch然后再进行合并操作 -- `git pull origin dev` 下拉指定远程的指定分支 -- `git pull --all` 下拉默认远程的所有分支代码并自动合并 - - -#### merge -- [官方文档](https://git-scm.com/docs/git-merge) - -> [Official Doc: 高级合并](https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%AB%98%E7%BA%A7%E5%90%88%E5%B9%B6) -> [参考博客: 解决 Git 冲突的 14 个建议和工具](http://blog.jobbole.com/97911/) - -- `git merge develop `默认会直接将当前分支指向Develop分支。(一条拐弯的分支线) -- 推荐: `git merge --no-ff develop` 在当前分支`主动合并`分支Develop,在当前分支上生成一个新节点(有一个环的线) - -1. merge 就是获取对方的修改, 与自己这一份进行合并(对 对方没有任何影响) - - `master merge dev` 就是 master 下载 dev 的那一份代码, 与自己的这份代码合并为一份 - -- 如果遇到冲突: - - `git mergetool` 使用工具进行分析冲突文件方便修改 - -> 配置mergetool工具: -- `git config --global merge.tool kdiff3` -- `git config --global mergetool.kdiff3.cmd "'D:/kdiff3.exe' \"\$BASE\" \"\$LOCAL\" \"\$REMOTE\" -o \"\$MERGED\""` -- `git config --global mergetool.prompt false` -- `git config --global mergetool.kdiff3.trustExitCode true` -- `git config --global mergetool.keepBackup false` - -#### rebase -> 衍和操作 [参考博客](http://blog.csdn.net/endlu/article/details/51605861) | -> [Git rebase -i 交互变基](http://blog.csdn.net/zwlove5280/article/details/46649799) | -> [git rebase的原理之多人合作分支管理](http://blog.csdn.net/zwlove5280/article/details/46708969) -> 他会将分支中的圈, 消除掉, 成为线性结构 - -- 效果和merge差不多,但是分支图更清晰?TODO 有待详细学习 -- 与master合并:`git merge master` 换成 `git rebase master` -- 当遇到冲突: - - `git rebase --abort` 放弃rebase - - `git rebase --continue` 修改好冲突后继续 - -#### grep -- 搜索文字 `git grep docker` - - `-n`搜索并显示行号 - - `--name-only` 只显示文件名,不显示内容 - - `-c` 查看每个文件里有多少行匹配内容(line matches): - - 查找git仓库里某个特定版本里的内容, 在命令行末尾加上标签名(tag reference): `git grep xmmap v1.5.0` - - `git grep --all-match -e '#define' -e SORT_DIRENT` 匹配两个字符串 - -************* -### 常用文件 -#### .gitignore -``` -# 注释 -*/ 忽略所有文件 -build/ 所有build目录 -/build 只忽略当前目录的build, 子目录的不忽略 -*.iml 所有iml文件 -?.log 忽略所有 后缀为log, 文件名字只有一个字母 -!*.java 不忽略所有java文件 -a.[abc] 忽略 后缀为 a或者b或者c 的文件 -doc/*.txt 忽略 doc一级子目录的txt文件, 不忽略多级子目录中txt - -``` - -#### gitattributes -> [gitattributes](http://schacon.github.io/git/gitattributes.html) - -***************** -### Tools - -#### git-svn -> [git-svn 文档](https://git-scm.com/docs/git-svn) - -#### Submodules -> [官方文档](https://git-scm.com/book/en/v2/Git-Tools-Submodules) -> [git submodule的使用](https://blog.csdn.net/wangjia55/article/details/24400501) - -- 能够在一个git仓库中将一个文件夹作为一些独立的子仓库进行管理 - -*************************************************** -## 各个VCS工具的区别以及优缺点 - -### Git -> 分布式的去中心化的, 个人也是习惯性用Git了 - -### SVN -> [Svn笔记](/Linux/Svn.md) - -1. 中心化的, 代码统一保存, 如果中心发生错误, 代码会全部毁掉, 提交是必须要和服务端通信才能完成 -2. 允许部分的进行修改, 下拉, 提交. 而对于Git来说一个仓库就是一个整体(Git submodule 目前也能完成, 但是还是没有SVN灵活) -3. 优点: 能够精确控制每个目录的每个人的访问权限 - -可以通过 git-svn 使用Git的命令与SVN服务器进行交互 -> [Official doc: git-svn](https://git-scm.com/docs/git-svn) - -> 但是个人目前在用的方式是直接 git 和 svn 一起用 -> [参考博客: 为啥要同时用 SVN 和 Git 管理项目](https://www.cnblogs.com/dasusu/p/7774469.html) - -1. IDEA中移除git相关插件, 防止混乱, 而且最好是先把SVN项目拉下来, - - 达到 先让IDEA使用SVN管理项目 的目的 然后 git init -1. 避免LRLF LF 问题 -```sh - git config --global core.autocrlf false - git config --global core.safecrlf false -``` -1. 互相忽略各自配置目录 .svn .git - -- 至此, 就能和团队保持一致的使用SVN, 然后自己多任务开发时, 又能使用git优秀的分支模型 -- 当然该场景是有限的, 也就是说只有你一个人在用git 而且团队中使用SVN时没有使用SVN的分支模型, 这个是没有问题的 - - 如果SVN也用了分支, 那么就要命了, 这么多分支和状态, 要靠大脑记住实时的状态就.... - -## repos的使用 -> 综合各个VCS的管理方式 diff --git a/Linux/Vcs/GitTeam.md b/Linux/Vcs/GitTeam.md deleted file mode 100644 index 3fe5c48..0000000 --- a/Linux/Vcs/GitTeam.md +++ /dev/null @@ -1,103 +0,0 @@ -`目录 start` - -- [基于Git进行团队开发](#基于git进行团队开发) - - [基础思想](#基础思想) - - [Git Flow](#git-flow) - - [本地和远程](#本地和远程) - - [模板化提交信息](#模板化提交信息) - - [小规模团队使用码云组织的总结](#小规模团队使用码云组织的总结) - - [最终方案](#最终方案) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** - -# 基于Git进行团队开发 -> [在阿里,我们如何管理代码分支?](https://blog.csdn.net/maoreyou/article/details/79877829) - -- [ ] 整理和学习这几种管理方式 - -> Github gitee gitlab bitbucket 等各大平台都是这样一种模式: -> 个人和个人开发者之间是并行master,只适合偶尔开发提交一些代码 -> 组织就是适合给多个人,等同的稳定开发时,分支就会比较明确,这个笔记就是记录组织中git的使用 - -## 基础思想 -### Git Flow -> [Vincent Driessen 提出了 A Successful Git Branching Model](http://nvie.com/posts/a-successful-git-branching-model/) - -- [依据以上思想开发的 git flow工具](https://github.com/nvie/gitflow) - - [介绍 Git Flow](https://datasift.github.io/gitflow/IntroducingGitFlow.html) -- [参考博客: Git 在团队中的最佳实践--如何正确使用Git Flow](http://www.cnblogs.com/cnblogsfans/p/5075073.html) - - [参考博客: Getting Started – Git-Flow](https://yakiloo.com/getting-started-git-flow/) - -`规范的分支图` -![规范的分支图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Git/git-team-model.png) - -- Git Flow常用的分支 - - Production 分支 - - 也就是我们经常使用的Master分支,这个分支最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直接修改 - - Develop 分支 - - 这个分支是我们是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支 - - Feature 分支 - - 这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release - - Release分支 - - 当你需要一个发布一个新Release的时候,我们基于Develop分支创建一个Release分支,完成Release后,我们合并到Master和Develop分支 - - Hotfix分支 - - 当我们在Production发现新的Bug时候,我们需要创建一个Hotfix, 完成Hotfix后,我们合并回Master和Develop分支,所以Hotfix的改动会进入下一个Release - -### 本地和远程 -- 指定本地开发分支和远程的绑定关系 `git branch --set-upstream dev origin/dev` master同理 - - 一个本地库是能够绑定多个远程的 - -- [分支图复杂的一个项目](https://github.com/Netflix/eureka/network) `只是演示分支的复杂度` - - -### 模板化提交信息 -> git commit message 的模板化 - -1. 新建 ~/.gitmessage 文件 -2. ~/.gitconfig 中添加 -``` -[commit] -template = ~/.gitmessage -``` - -> 那么效果就是 git commit 不指定-m 参数就会调用该模板显示 -******************* -## 小规模团队使用码云组织的总结 -> `master`发行分支 `dev`开发主分支 `dev-*`开发者分支 `fea-*`开发者自己的功能性分支 - -- 在码云上创建私有仓库,然后管理成员,将开发者一一邀请进来,然后这时候就有了一个问题: - - 所有的开发者都具有master的所有权限,所以这时候就会很容易出现冲突,所以就需要设置master和开发主分支dev为保护模式,只有管理员负责进行推送 - - 管理员, 新建若干分支:`git branch 分支` 提交到远程 `git push --all` - - 对应的开发者克隆项目,然后 `git checkout 对应的自己的分支` 就可以开始工作了 - - ( 如果没有分支就下拉命令`git fetch origin 对应的分支`) - - 然后各个开发者写自己的,然后提交`git push` 就行了 - - 管理员需要 `git fetch origin 分支`得到所有分支 - - 针对每个分支进行拉取: 切换过去`git checkout 开发者分支`,然后`git pull 开发者分支`下拉最新 - - 然后选择合并`git merge --no-ff 开发者分支` ,处理冲突然后提交 - - 开发者下拉自己的分支 或者开发主分支 dev 即可 - -******** - -`分支的处理的一次实验 2017-10-21 23:57:34` -- `git fetch --all` 获取远程所有分支(新分支) -- `git pull --all` 获取所有分支最新提交 这个就会自动合并???越来越不理解了 - -- dev-test 分支进行修改,然后提交一次,然后push -- master: `git merge --no-ff dev-test` 进行合并,就会在分支图上得到一个环 - - master 分支本地会多出2个提交 - -- dev-test 进行修改,然后1次提交,push -- master : `git pull origin dev-test ` 执行merge命令就会提示没有可以合并的修改。 - - 这是为什么???? - - -### 最终方案 -`双方都有修改` -- 开发人员提交完后,主分支管理人员切换到开发人员的分支然后 `git pull 开发人员分支`,然后切换回主分支上 `git merge --no-ff 开发人员分支`(填写注释) 然后push - - 然后切换到开发人员分支上执行 `git merge master 然后 git push` 还是`git pull origin master` - - 然后通知开发人员下拉其自己的开发分支即可 - -`只有一方修改` -- 主分支进行修改了开发分支没有动,那么开发分支 直接下拉 `git pull origin master`下拉修改代码即可 -- 如果是开发分支修改,主分支没有动,那么管理员负责切换到开发分支 然后pull 然后merge 然后 push 然后切换开发分支 然后 pull diff --git a/ML/Linear/Logistic.md b/ML/Linear/Logistic.md new file mode 100644 index 0000000..e69de29 diff --git a/ML/Platform.md b/ML/Platform.md new file mode 100644 index 0000000..453e2db --- /dev/null +++ b/ML/Platform.md @@ -0,0 +1,113 @@ +--- +title: 机器学习-工程平台 +date: 2024-01-20 22:56:10 +tags: +categories: +--- + +💠 + +- 1. [机器学习平台](#机器学习平台) +- 2. [商业平台](#商业平台) + - 2.1. [Azure](#azure) + - 2.2. [BML](#bml) +- 3. [开源](#开源) + - 3.1. [Anaconda](#anaconda) + - 3.2. [Posit](#posit) + - 3.3. [H2O](#h2o) + - 3.3.1. [部署](#部署) + - 3.3.2. [H2O Flow](#h2o-flow) + - 3.3.3. [H2O-Python](#h2o-python) + +💠 2024-08-13 11:47:10 +**************************************** +# 机器学习平台 +> 将机器学习工程化组织的平台 + +# 商业平台 +## Azure +[Azure 机器学习文档](https://learn.microsoft.com/zh-cn/azure/machine-learning/?view=azureml-api-2) + +[无需代码经验的机器学习入门第一天 - 训练AutoML 分类模型](https://developer.baidu.com/article/detail.html?id=293344) + +## BML +[BML 百度全功能AI开发平台](https://cloud.baidu.com/product/bml) + +************************ + +# 开源 +## Anaconda +[Anaconda](https://www.anaconda.com/) + +## Posit +[posit](https://posit.co/) + +## H2O +> [H2O-3](https://h2o.ai/platform/ai-cloud/make/h2o/)`开源 分布式内存机器学习平台` 类似于 Jupyter Notebook 的数据开发平台 +- [Github](https://github.com/h2oai/h2o-3) | [Youtube H2Oai](https://www.youtube.com/@H2Oai) +- 历史还有H2O-2版本, 14年开始做起 [两个项目Star History 对比](https://star-history.com/#h2oai/h2o-3&h2oai/h2o-2&Date) + +![](./img/h2o-structure.excalidraw.svg) + +建模支持的算法 +``` + Aggregator + ANOVA for Generalized Linear Model + Cox Proportional Hazards + Deep Learning + Distributed Random Forest + Extended Isolation Forest + Gradient Boosting Machine + Generalized Linear Modeling + Generalized Low Rank Modeling + Information Diagram + Isolation Forest + K-means + Model Selection + Naive Bayes + Principal Components Analysis + RuleFit + Stacked Ensemble + TargetEncoder + Uplift Distributed Random Forest + Word2Vec + XGBoost +``` + +### 部署 + +- 下载压缩包解压运行h2o.jar之后,可打开一个默认无认证体系的 H2O Flow. +- 加载数据库驱动 `java -cp h2o.jar:mysql-connector-java-8.0.25.jar -Dsys.ai.h2o.sql.jdbc.driver.mysql=com.mysql.cj.jdbc.Driver water.H2OApp` +- 启动集群 `-flatfile flatfile -name testEnv` + - 如果是同主机搭建需要不同的进程指定端口,例如 `-port 54323` 内部paxos端口则是参数值+1 54324 + ``` + 192.168.1.5:54321 + 192.168.1.6:54321 + 192.168.1.7:54321 + ``` + +### H2O Flow +[H2O Flow Tutorial](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/flow.html) [mirror](http://h2o-release.s3.amazonaws.com/h2o/rel-3.44.0/3/docs-website/h2o-docs/flow.html)`H2O Flow GUI操作方式完成全流程` + +- [MOJO](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/mojo-quickstart.html)`模型的抽象表示` 导出为GenModel即jar包可部署到应用中。 + +> 跑GLM的流程 +- [GLM Tutorial](https://github.com/h2oai/h2o-3/blob/master/h2o-docs/src/product/tutorials/glm/glm.md) +![](./img/h2o-glm.excalidraw.svg) + +> [H2o使用分布式随机森林建模分类](https://blog.csdn.net/gpwner/article/details/74058850) + +************************ + +1. [数据处理](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/data-munging.html#data-manipulation) +1. [模型评分](https://h2o-release.s3.amazonaws.com/h2o/rel-yates/1/docs-website/h2o-docs/performance-and-prediction.html) +1. [模型部署](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/productionizing.html) + - [使用Amazon SageMaker训练H2O模型并对其服务化](https://aws.amazon.com/cn/blogs/china/training-and-serving-h2o-models/) + +### H2O-Python + +************************ + +> 同组织项目 +- [H2O Wave](https://h2o.ai/platform/ai-cloud/make/h2o-wave/) `开源` 基于动态数据实时渲染仪表板WEBAPI服务 Python实现 +- [H2O Driverless AI](https://h2o.ai/platform/ai-cloud/make/h2o-driverless-ai/) `商业化` 将ML工程简化,在交互上构建清晰的工作流程 diff --git a/ML/Readme.md b/ML/Readme.md new file mode 100644 index 0000000..6752573 --- /dev/null +++ b/ML/Readme.md @@ -0,0 +1,11 @@ +# Machine Learning + + +- 线性模型: 线性回归,logistic回归 +- 时序模型:ARIMA,Prophet +- 树模型:XGBoost, LightGBM +- 深度学习模型:LSTM,Transformer + +[超参数调优](https://www.cvmart.net/community/detail/8044) + +> [doccano](https://github.com/doccano/doccano)`标注平台` diff --git a/ML/Tree/DecisionTree.md b/ML/Tree/DecisionTree.md new file mode 100644 index 0000000..16868f7 --- /dev/null +++ b/ML/Tree/DecisionTree.md @@ -0,0 +1,4 @@ +# 决策树 +> [机器学习——决策树模型](https://cloud.tencent.com/developer/article/2374508?areaId=106001) +> [CART决策树原理(分类树与回归树)](https://cloud.tencent.com/developer/article/1813348) + diff --git a/ML/Tree/Readme.md b/ML/Tree/Readme.md new file mode 100644 index 0000000..179e9bd --- /dev/null +++ b/ML/Tree/Readme.md @@ -0,0 +1,3 @@ +# 树 + +https://www.cnblogs.com/dogecheng/p/11588303.html diff --git a/ML/img/h2o-glm.excalidraw.svg b/ML/img/h2o-glm.excalidraw.svg new file mode 100644 index 0000000..cd1701f --- /dev/null +++ b/ML/img/h2o-glm.excalidraw.svg @@ -0,0 +1,21 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1d629cdTAwMWLXlf/uv8JQv0aT+35cdTAwMDQoXG49rJf1XHUwMDE2JUveLYRcdTAwMTE5lEjxJXJEPYpcdTAwMDDbhWO7dVxmt4tNUyTdoC26QFq0totFuruNk/4zoSx/6r/QM6SkXHUwMDE5XHUwMDBlOSSH4lAjoDQgk3eGnHPvnN/5nXPvuWd+dOfu3Vx1MDAxMfukZI18cHfEOk6auUyqbFx1MDAxZY2857RXrXIlUyzAIVL/XFwpXHUwMDFllpP1M/dsu1T54P3382Z537JLOTNpXHUwMDE51Uzl0MxV7MNUpmgki/n3M7aVr/zA+bto5q3vl4r5lF023IuMWqmMXSw3rmXlrLxVsCvw6/9cdTAwMDKf7979Uf2vR7qylbTNwm7Oqn+hfshcdTAwMTWQSuFvXSxcdTAwMTbqwnJEXHUwMDE0xlx1MDAxNF1cdTAwMWTPVCbharaVgoNpkNhyjzhNIzpTTSbHKqqgVmaOqD7Kn+xMm+5F05lcXG7NPsnVhapcdTAwMTShL+6xil0u7ltcdTAwMGYyKXvvctQ87UHfKlx1MDAxN1x1MDAwZnf3XG5Wxek8vmotlsxkxj5x2pArfGNcdTAwMDQ+uOu2XHUwMDFjO33U2uBXLc53Rlx1MDAxOUOGoJRpJVx1MDAxOdJEUp8wXHUwMDEzxVx1MDAxY4w9XGLzPZLWXHUwMDE2Y644O2Zyf1x1MDAxN2QqpK7OsctmoVIyy3CH3POOLrpcdIN71bZnZXb3bGhcdTAwMTTavZ5VXHUwMDFmai44l0ggd1Sci5RmU/V7/kN3fMugLbPON1xuh7mcd5BcbqmLQbrUXHJXO+hFy4duL5zz7/m1yqtZTdplW8du1zzKkMab24lcIr1vptbXrelcbsuv8c3dkavzPnyv/c82vpyavGejw+0suzelt7m0t1x1MDAxNtNV2XyVy+ub5XLxyPO7XHUwMDE379xhOSylzIbOYomcsURCaaaujucyhX3/mOWKyX1Xze94XHUwMDA0bkFXU/+9wFIoXGJYWDLMXHQlnPVcZq32o1x1MDAxOWtoXHQsfMDCckjA8lxm/Vx1MDAxNa5cYvXjinKBpVx1MDAxNGxcdTAwMDC4ajrQXHUwMDAyoGBdZFxccUpFXGJddKUqXHUwMDE27LXMqTPuXHUwMDA0NbVOmflM7qTpXteVXHUwMDE0Rq326k3to/8+++T12fOXI01Hx3KZXUdtR5IgtVVu0mg7XHUwMDAzzHN1Qj6TSnm5JFx0lzQzXHUwMDA1qzzbXHUwMDBiXHRcdTAwMTTLmd1MwcwlOkpcdTAwMDTjYM1cXN45bGDuUYWK5Vx1MDAxY3XaVUdgdqY9pfytl+hkgigpVFxicK6o3Nz4dHJ2dW9yeWN8OjEzbuHleIOTa+nnPVwitYF7gifWXHUwMDEyJ8k1eI9wQ3tfLlx1MDAwMDrQIEZcdTAwMDJrTqXbkdtAg/PZ1TlreS+zv0im9D1i7yXS1dkh0GDH310+SVWmudw8tmZOsJiy+ezW6fTA6FUhxmT09Fx1MDAxYei3YoWoXHUwMDA0eu3dcW1/k2JccmBBUFx1MDAwYoC5XHUwMDE4XHUwMDEygHvjV/BysFJcdTAwMTKz61x1MDAwM7Y/ftVYKtBcdTAwMDNBQyjjNfn1d4/e/uxxpPzahWxa+LWtRJHzK1x1MDAxM4Her1BSXHUwMDEyhjz47cqvYyfLeH62uJh/uJAtTqOKtYCO41xyT66ZXHUwMDFmnpjIJnhcdTAwMTLlk8bFZ1x1MDAxYakkQsMmWM5cdTAwMDTcXHUwMDE2Qlx1MDAwN4DXIVx1MDAxMuxx6Vx1MDAwMUJkcoeyRHnpxMLjc1x1MDAwYvntIVx1MDAxMGHH32VcdTAwMWJoTqbURrJSXHUwMDE5W19fY/ntyWJiYFx1MDAwNKu58Ny9iFxiXHUwMDE2lCGYYFx1MDAxOeWaY9I7hNvfpVhDXHUwMDE4gsNcdTAwMTZcYiM2LFxi90axXHUwMDE4sM2ZJjdcdTAwMWLCgkFcdTAwMWZeXGL77qPnb795efblb2pfPIuKYjvzjZ9i20tcdTAwMTQ5xXKC/a2XXHUwMDAwJZhcIk6Q1K7CdFx1MDAwM2hcdTAwMTkvrma3Z1ZcdTAwMTfX80tj06NcZqWm5tbiXHLQ1lx1MDAxOJZoQ1x0wVx1MDAxMfCfRlx1MDAxMvBcdTAwMTlcYk9cdTAwMGJRTPHQQ1gqwHgwxF27cVx1MDAxYihcdTAwMTZNXHUwMDFkmFuZLbpzvD1zlNhZXHUwMDFlPSpiNVx1MDAwNCrs+LuJykmxuoCzSVRdXFyeQom56bSwXHUwMDA2QrFcdTAwMWEzxbjw6F9UMWwwgrGkXHUwMDE4S8ZR71x1MDAxNNv+LsVcdTAwMWHBrUEsJ8NCcG9cdTAwMDSrpUJcYstBTDr1T7BaUk5CKOP1XGL27ItHta//XHUwMDFhKcF2IVx1MDAxYj/Btpco+lx1MDAxOFZpf6u7NIpcdTAwMTjWMsRcdTAwMWPxXHUwMDEyXHUwMDFhTaTHJjdG8/tJuziWf2CulGO+gCM9c+SNXHQm4SXclkkly/nXPyClamZUd1xu8Vxun8xz9cuYlUlJNL5dhDq/rnGWnWa3zfFddZBdn99fk+kwMSAjSstQ81p9XHUwMDExXHUwMDE0kYFcdTAwMDRcdTAwMDWOjFx1MDAwMll6p6f2fY41XHUwMDAwXHUwMDE0xlx1MDAwNpVCc1xudOSDXHUwMDAyXHUwMDE3XHUwMDExQoFcdTAwMTKDMIE0lbxcdTAwMTVcdTAwMDRtXHUwMDE2Mlx1MDAwNVx1MDAxNY45urGJ1n5cdTAwMTTyeiQ1V9yJiJu6XHUwMDE4aj83NVx0XHUwMDEyOSVcdTAwMTFcdTAwMWG4bIm1RoxcdTAwMTBcdTAwMWNiXnVuU+3kN9iidTidY2xld2fucLJcdTAwMTRvSErlKnljSoZcdTAwMDFWoiSlNizUOlxyg8FcdTAwMTZSifXtmjrdOVic2ZpcdTAwMWJdTYiJ2f10ilx1MDAxZpeyqVQoXHUwMDFh4kIoXHUwMDE1+VpcdTAwMWbBgVORglHwxLw5XHUwMDFj3ZS+fadjrfRcblx1MDAxMYNRxaTgwk9EmEaq/1xmrFx1MDAxOIEhXHUwMDE2qjcqXHUwMDAyXCJcdTAwMTBcdTAwMWNTj19wXHUwMDAz8VJYpbxcdTAwMWVcdTAwMTVN3t+IiIq62Gc/XHUwMDE1NVx0MoRcdTAwMTU+4m+9XHUwMDA0pWJcdTAwMWFcdTAwMTRViN6ZaCyRP0jnS3h99oGqLFVW8rP35Gq8QdlcdTAwMTJcdTAwMWQpYchcYolcYjdHR65F7MBLYC80XHUwMDA0SGxcdTAwMDBoXHUwMDFjXCItre/uzmazMrc6djw6qU9cdTAwMTemXHUwMDExxiyGtMRY8PSdoJyD2ZS9T1x1MDAxMLTvdawhoFx1MDAxMFxy5CVcdTAwMTkpXHUwMDFhwtKS1FqC7zyIaYLbQkuLy2tcdTAwMTHRUlx1MDAxN2Ptp6UmQaJfXHUwMDE1o8GJJ5IpLkLER8v6/mw2ncdy7qFVsqtcdTAwMDeL+UPzQbwhKbX7uZF0zXmUc3asKyvR1miJcaZcdTAwMTFFt2zSbnm1Ojm6dWTOTq1YO2i7uimyx8VQiVx1MDAxYkqzcFx1MDAxM/l90Vx1MDAxMkeB09ZEXG5cZkLo3lmpfadjXHJcdTAwMDGFhKFcdTAwMDNYiXFcdTAwMTQhXHUwMDFhwrJcdTAwMTIlXFw6bsJcclx1MDAwNkvhdTKurNTFWEfOSsFz6MH7gKimWlJcdTAwMWRcIlNZTNCVXHUwMDEztGfb946Q1qv7a/vTeVx1MDAxZHM8qpZccnbBaVPXnjnnXHUwMDEwJ1FcYnu0s41DozZpXHUwMDE5WFx1MDAxYYxcYsRg/DhcdTAwMDb4eXLVL3CpMLhrjIpcdTAwMWLLqtKYI0TCbVJrh0tMXHLNkKJEaoFcdCOeNK0mnLo39lx1MDAxMqfffftfZ89fnv3iyXdf/2V9dfbtZ49qz1+cv3z59zdcdTAwMWa//fPXtS+e1V789OyT1++evKi9vsgubuzkqT19vVhMWWe/+mPtV69cdTAwMDOgnrPSdlx1MDAwN6DbxVJcdTAwMTDKm/rvh/SgZfZZXHUwMDA10t4qkL6sXHUwMDAy5dTf6lpcdTAwMDUlNCeggT1bhZ1kcmf8YONww5y/Xy3vnFTHZGUz5lahvtyLkJNFWl9ga7ZcdTAwMTHQfUNiilxiYlpyXHRcXFx1MDAxZVx1MDAxZGtcdTAwMTO4t0JjsFxiiILFIG24XHUwMDFiXHUwMDBiXHUwMDAzUaxBXHUwMDEwsFx1MDAxOIxoz9bGy1xiU1x1MDAxMEaB0G8swtRcdTAwMTh8f1x1MDAwZcM1JJvRyu3LZrlirVn2Yenvbz67SIr89Zvam1x1MDAxN4C72p8+PXv5VVx1MDAwM4aAx/PffNlcdTAwMDDg2afPzr/5I7ScPXtcXHv6XHUwMDE43tR+9rT2/39x3jx9fP7q+XBNSMRd6M3PoFx1MDAxZC1Kl938gSslWlNcdTAwMDRcblx1MDAxZWJGatk+nl/ZLTB7TM9vkIdcdTAwMGJL25vMirdVkdpnR8CWRlx1MDAxOf62cS/axLsgXHUwMDA0JYTT21x1MDAxNe9cdTAwMWWfrlVcdTAwMTdGR1x1MDAxN/bolLk9bZ8upbdXUFxm413Kg2d8XHUwMDE4Q2DAPFx1MDAwM991n0LbTsda51x1MDAxNdKBs7BU4Vx1MDAxOMW7WFx1MDAxMSHZIPZcdTAwMTfdlnA3urXBLsY58rXBYDRcdTAwMDYuXHUwMDBiYqk4RFVcYvVcdTAwMWXuTlSq5Xme2M+uXHUwMDFlJMqlUpFWlovZmMNRkU6OLZV8WI4tOKNOvVxyXHUwMDE4dolcdTAwMDNyybr7tU5cIjZWgnim1Yfu2FxuITDjN+fYvvu3z87/9mRucnzi7NlPai9+0fD7wOk7e/7b7/7vr1NOz2t/+iVEksF4j8hh7VO0QTiiQVx1MDAxNlx1MDAwMPPAnflcdTAwMDTDqGvMet+WpKeTy6q0M2NuZdbuZZOpnc25k9OYXHUwMDFiXHUwMDAwT1Wni7o30c13XHUwMDExblx1MDAxMERcYtJMkmb+dzHuuXpcdTAwMDPTXHUwMDA0g23CiqlcdTAwMWKbeFZKXHUwMDEyhLm+7rZBzFxywVx0llSDSVx1MDAwNc/Ck67XXHUwMDA101NcdTAwMTkvuVx1MDAwZVx1MDAwM6rNV+xccoGsL1x1MDAwNFx1MDAxMlx1MDAxY5wlXG6EXHUwMDA0xIOwO+nZXHKCW3J0vsqW8nP26drkw+LK7u6p2oo7XHUwMDA03e81WJdH6Vx1MDAwN1NlXHUwMDAwgVxuLbVWQd4wXHUwMDA2Lq4rPlx1MDAwMVx1MDAwZkkz7Nmye8m0XFxcdTAwMTFcdTAwMDJ/+I0x7cBQKYyLXmIpmERcdTAwMWVcdTAwMWanXHUwMDBiKlx1MDAxM+bOsGHpu2RvuOR94rJDwlx1MDAxMFx1MDAxN0or75ajbricKiG9y8bXXHUwMDFmbtFqXCK/nV0ocOsk5rj0dK++269DiZrrYlx1MDAxMktcdTAwMDAlZT1uoVx1MDAwME3FnGHKb7BYXHLwsTNcdH1N8IUoVvP0ce3Fzy9cdTAwMTZCnr5+9/njt9/+rvb4o/P//fnbl5+e/c8ntW8/hfaFpbml4UKyL8FcIs8sXHUwMDEyKJBVgVBcdFx1MDAxMV7a7Vx1MDAwNl5rX1x1MDAxNIqLx8tcdTAwMWKS8dnxiWzyZHb6MN7ghejdt1lX0pvdXHUwMDBlSNvUiJNcXEtgnduV8TpRXdtfXV1cXLr/sHBvXCL3cOk0m13Nhcx4JSpUXHUwMDFkyb5cYozq4FxcXHUwMDA2JFx1MDAxOJKY9Z5d177TsYaAQspQbTdcdTAwMDRGOs9cdTAwMWFyOyDWjudF0c1cdTAwMTVe60chr8dl0e1cdTAwMDfsYqlvdj+gXGKO9KhCSFLwKXtcdTAwMDZkYq7Ai6vTe8dcdTAwMGJ0ZnPtftnck2bMS8BI5S8ggaNcdTAwMDSi8qW7uvzjbsJoySai2pnpvW3ZrvZcdTAwMDFaeHiQn99PzY4laD69tsPWw21RXHUwMDFmXHUwMDBlJTFcdTAwMWGY7co4VkKSXHUwMDEwe1x1MDAwM9t3OuZcYpBcdTAwMDZcdTAwMTJcdTAwMDB0ovycRFwidc+woVx1MDAwNCc9XHUwMDA3WMRJPoBcdTAwMTDn5lx1MDAxMmSGT0pcdTAwMGKrXHSzslx1MDAxZlx1MDAxMS91sdZ+XvLLXHUwMDEy6Vx1MDAxYaAn49NfOEVcdTAwMTLBeIi6KVx1MDAxNFUmT1x1MDAwZVx1MDAxNuZHN4uZiXQu/2B9vFx1MDAxMPPan8q/XHUwMDA3XHUwMDAzY2Rg4tSvXHUwMDE2RHN4YZ8sXHUwMDAzdFx1MDAxNWm7lFx1MDAxNGG0rFx1MDAwMWBNqMREXHUwMDBmXCIp5Wa3ROFcdTAwMTDreD85e/b78yd/qL345bsnL5xcdTAwMDStz3999f7sq2fnrz5pzDq8+/xx7T8+fveHj89f/fiq5V9cdTAwMGKNjLCLOYhcdTAwMTf/XHUwMDBlR4a92lx1MDAxN2FcdTAwMDd6slxitHO6a6PSWlx1MDAxYpNcdTAwMTCc7iqR0kJw1TtLd66KXHUwMDFlS5MguKvljVx1MDAwNVx04b/4XHUwMDAw11x1MDAwNFstgGyZKSFcXFx1MDAwM1x1MDAwNlE0XHUwMDEzJaS7V9qpvq3EWPeV+l6xzbI9nimkMoXdZsEunpLUoM7Oj6io25HkoSMlMpCTqOHYKYi/XHUwMDE5gVx1MDAwZthz2q5ZgpN000NNiGrpu1VIdZepc1lvj0yjjlCKODW0lULOXGY3dVx1MDAxN9yvhKK+MnotQuXMij1RzOczNoz9cjFTsP1jXFxcdTAwMWbMMVx1MDAwN9B7ltmiXHUwMDAw0CnvMT/yS84vNodcdTAwMWbuu7suOupcdTAwMWau3v/wvbZnXHUwMDEz79lSu6ff8f5cdTAwMWbaJuFcdTAwMGWpSlx1MDAwMktn+1xiXHUwMDBlkS3bsZR0TK2SMlx1MDAwNDhcdTAwMDJcdTAwMTCbSqpbqr0haUA7XHUwMDE3VFx1MDAwYk6Jd7RuxmIxLjnTOpI4+npcdTAwMTZcdTAwMGIjTEVfcUWPXHUwMDE2q2frXHUwMDAwxoExhLlgzlx1MDAwNicmPFx1MDAxOVx1MDAxMVe2wSlcdTAwMTijNWVScoFgTGWfXHUwMDE2q2OV5GaZXHUwMDA0xc7quPPYK1x1MDAxMK3VXFxhXHUwMDAzrCeCeJJAPM/gzz9cclZcdTAwMWKDXHUwMDA1iFxmNFjYqf+msadCYjeD1bkwb2xcclx1MDAxNlx1MDAxNlJcYmBb7F2Pq1x1MDAxYixOXHKH81x1MDAwNKKgb6D/0WVdNddiUUq0eY5fq1x1MDAwMaNcdTAwMTQp4OxBXHUwMDE0a1x1MDAxZqhcdTAwMDFzVp4x9lx1MDAxNk6IwICFMFx1MDAxNlxijLyznMrBhaFg9Sn3nNewXHUwMDE3zGBCOUnvlMFZXG7h/kxY5zq0zVJhRFx1MDAwNHhbSsP1NMZUtlx1MDAxMYpTsLzYiWwxXHUwMDE4PN1cItRtsmHBOu68Qlx1MDAxYrXORTt0cCl7RSBI8Zb961x1MDAxYVx1MDAxZS6mkDmzczLNk4fbeY1TKZyfirddg+jXkMDJXGb8LSeBrNmwYdGphIeQXGan9DVsXHUwMDE5715Zqk0le4jR4MYgz0OmbsOqxtyq3jpcdTAwMWTd3tuk+6eLyY09+FFrPFx1MDAwZVx1MDAxNedcdTAwMDW4RSRU7k9HtFx1MDAwNa+WXHUwMDA075XSVFwiykPUXCJoP5jxXHUwMDA2muaGVlhcdTAwMTDFsNQtQFMqSqBcdTAwMTGAsTOT0H6LcZt1fKQ1UlRcZlx1MDAwMmN9b71cYq2XrlR9XHUwMDE1n2/MVf720dlXz94/f/Wo9vRxROsnXWiipVx1MDAwNn0nwVx1MDAwNrGYXHUwMDEyPE2BgktcdTAwMDVIZ4Oj8D6qqetcIn9HI1x1MDAxNkvQXG6uXHKulKbcqbNcdTAwMGJcdTAwMGVWM2hxdKspyGCAXHUwMDAwuLJcdTAwMDReRoq3mafgTYusl1x1MDAwNa6Us9FcdTAwMDV5ljbi4ulfXHUwMDAzzz16+l196ouIn1x1MDAwNDxJy1x1MDAxN1x1MDAwYmiEKNxcdTAwMDCihHb20/U5V9FcdTAwMTnsrlBcdTAwMDFCOM+RlFxcOdrnTKvy2z07MVx1MDAxYajWzstR6EH69oJcdTAwMDUmaGAthXRcdTAwMWVU2nvSYHJ/UWRcdTAwMWXenyqnq7nJ5ZNJMzFTjX2hWGRI0CCmJcWM8ebdYVhFWZGszyRaXGauXHUwMDA3weR2ZSxcdTAwMTWPplx1MDAwZrbXxcqDUV2gJ2PmvZXig7UwPrizXFxcIvhcdTAwMTB88Fx1MDAwZU99Qlx1MDAxOKyLXG6xQ7p9r2NcclxihZiBINZFRFx1MDAwMVx1MDAxYrVcdTAwMDKik1x1MDAxMz7kVFrNkeNd0Vx1MDAxYq1cdTAwMDRcdTAwMTZWK6/ngUeXSdvFdkeeSVx1MDAxYpiuXHUwMDE0/HhcdTAwMDFwUYhcdTAwMDQlYL1cdTAwMDNy9f7U1ilNJ+mYPWlcdTAwMTZcdTAwMTfw5szK/fmYXHUwMDAzXHUwMDEyomJKnZRxiI3bXHUwMDAwMso8QoLhbmLanL3YXHSRgjslRPiNbV1cdTAwMDZEgulS9Lq1+XpH5Hff/O3tf37p5vX8+LO7vVx1MDAwNMhRVdnrWZpQmL1zwcwjZqm0ZsN4XzlcdTAwMGZwgzOpi0FzJVx1MDAxYqlmrKPxVmX7Xrr+cni/blx1MDAwN1x1MDAxY8BZdU/kwztcdTAwMWb+XHUwMDAzXHUwMDExXHUwMDE0+H8ifQ== + + + + + 导入数据处理数据配置模型构建模型JobDKVNPSNPS依据文件URI的协议,类型及数量将数据导入到Node本地ParseSetup:配置格式及字段类型,表数据旋转,拆分,合并,分词DKV通过JDBC拉取数据转换为Frame存入DKVFileTable分发数据到集群内跑算法得到MOJOJobMRTask选择自变量,因变量,测试数据集和验证数据集配置算法参数数据预测/评分Job使用数据集做 预测/评分 \ No newline at end of file diff --git a/ML/img/h2o-structure.excalidraw.svg b/ML/img/h2o-structure.excalidraw.svg new file mode 100644 index 0000000..07292b3 --- /dev/null +++ b/ML/img/h2o-structure.excalidraw.svg @@ -0,0 +1,21 @@ + + + eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nOy8ya7s6JpcdTAwMWQ2r6dIpIase9h3JVx1MDAxOFx1MDAwNskgg31cdTAwMWJcZkbQKFxcsO+bYFx1MDAxZlx1MDAxNFx1MDAwNMiWYVxiXHUwMDAyXHUwMDA0XHIsW1x1MDAxZXjgXHUwMDAyNNBEZchcdTAwMDZcdTAwMDTJZT2N61ZJb2Huk3nz9NlVnqxzVdqZcXZsRsO/+da31vr/j/xcdTAwMDd/9NVXX0/PPvn6T776OtmioC7iIVi//uOX40syjEXXXHUwMDFlLyGv/1x1MDAxZbt5iF6/M5+mfvxcdTAwMTNcdTAwMTBsgqFKpr5cdTAwMGWi5NVSjHNQj9NcdTAwMWNcdTAwMTfdq6hrwGJKmvG/fflXXHUwMDBmmuS/6bsmnoZXb07ymyQupm745lxcSZ00STuNx7f/d8ffX331XHUwMDBmXv/7VuuGJJqCNquT11x1MDAxZnj90ptcdTAwMDbCXHUwMDE0/f5RvWtfN1x1MDAxNiYoXHUwMDFhgmlcdTAwMTQjv3tHMZ6O801JfLycXHUwMDFlbU7evPJy6Ovlt5TedIHSXHUwMDE2PS07cVwiPrjCeHPatKhrZ3rWr5s1dkdv3rw2TkNXJV5cdTAwMTFP+e/H7a3jn/rU0M1Z3ibjS/fh7452fVx1MDAxMFx1MDAxNdPz5Vx1MDAxOFx1MDAwNH139Jsx+JOv3lx1MDAxY9lezkS8+dzLJ3CYeO/cXFxXXHUwMDFmg32c++/Byct/b85cdTAwMWVcdTAwMDZRlVx1MDAxZE1o4+/eM1xyQTv2wXBMyZv3rd/2XG6G0e+O5UmR5dNxkHgzXHUwMDAxY/J6ZHGMoCCMILDvXng5SS/Fryf5T99cZudwhIf08ol2ruu3x6SNv1x1MDAxZJPfXHUwMDA3w5twQL898lx1MDAwZt/04uX9/Pth9HYovVx1MDAxM05Tsr3p2ltzn9371Vx1MDAxOTv5VC+3Klxc14qntuTr7973XHUwMDBmv332pvlzXHUwMDFmXHUwMDA334RcdTAwMTJMQjhccmM0jSNvdbou2ur9vtVdVL2Jvj96qyNcdTAwMWaE/TvtfDviXHT8k1x1MDAxMU+RXHUwMDE4gpA4XHT/6Ij/eK+/7IiniVc0SmEkgVx1MDAxM1xi/m7wo/RnXHUwMDBiflxmflx1MDAwNSPoXHUwMDEx11x1MDAxNIl/iFx1MDAwMlx1MDAwNH1cdTAwMWZcdTAwMDXogVx1MDAwMYqASORvjoJ3Xvgg3H/JiHzTqq6dnGJ/XHUwMDE5fVx1MDAwNHrnqFx1MDAxMDRF/Xxnxl+H6jF+J+X69TtcdTAwMDeZusheYvbr6GhsMrxcdTAwMTPOU3HwwXdvaIo4fjvDR8eZgqJNXHUwMDA26cck5m4osqJccurLx1x1MDAxYXL0Olx1MDAxMX8/T8ck4m+FwJi8vPpcdTAwMWE631x1MDAwYsbv5SDkLby9j0hcZkVcdTAwMTGcXCJ/XHUwMDAyXCJFa0Dh5Vx1MDAwZfm/tWRcdKfRwnDk25eNSFxmelx1MDAwM7tvOIh8hb939l+QhVx1MDAxMPxcdTAwMTX99s9cdTAwMWLC+1x1MDAxZVJcImFcdTAwMWH5g+Ok4IlcXK5cdTAwMTh/ZyHVmNBcdTAwMGLTY53LfYGchODQJ1x1MDAxMUBS6EFZMPTjVdjHe/1lI1x1MDAwMIdfYZ/gJFxm+oxg+KmsRKBcdTAwMTRBXHUwMDFjgfF3iJV00/lMrPRcdTAwMDOp+n1Weqchn52VUFx1MDAxOHv/6O8xeShcdTAwMTNcdTAwMTg/nNOPJ6VHKJ4zS2uGyL7GOLJ3xtmxvmxI4sSbjPNcdTAwMWGHyOfE4c8jpSM5XHUwMDFluVx1MDAxMflDo6Vs0e+aZlx1MDAxN8ol9GFmg73CiYIvkJZQ6JPCjKRhmiTpn+KUPtrpL1x1MDAxYlx1MDAwMjT2XG5cIkiUQij4XHUwMDAzVkI/I1x1MDAxYUj4XHUwMDE1dcQ1iqE/jpXgwywhXHUwMDE0SSPU31x1MDAxZFrS7EswVp+JmX4gX7/PTO+35bOTXHUwMDEz9mlkXHUwMDFlYlx1MDAwNjliXHUwMDAxf3POXHUwMDFmQmaTzDFrl8/fmqaVwpeqKmmD/LKRieBvMPB8Lcs+J1x1MDAxY38mOVFcdTAwMTSKQFx1MDAwNPpLgPJXJKfRhPr1Xl6aq3CSiUlcdTAwMTZcdTAwMDXKcb5AcoIp9FNcdTAwMTBAYFx1MDAxOCFcdTAwMGZhQP94XGZ8vNdfNlx1MDAwNmj6XHUwMDE1RVx1MDAxMjSOvk9Oh4n6jGhAkVdcYkZA9I/lJog4LFx1MDAxM0W81Z//4rlJ7sLPREw/kKvfJ6Z3XHUwMDFh8vlZXHSn3j/6nV7EMfKQNPCPRyQyXHUwMDBljH7WfZVcdTAwMTFmuCE1aqvb8stGJFx1MDAwNr0hhm9Yif7yWFx0QSlcdTAwMThDaOhcdTAwMGbLMl1vz6vV1lvmM9llQcXfLpjAfoGshOCfXstGXHUwMDEwXHUwMDE0RjFcbv3xXHUwMDE4+Hivv2xcZiDkp1iJQD4jXHUwMDFhqJ/ISjSMkiSCvLXb+188K1x03VCBcle0n4mbfiBjv89NXHUwMDFmac4vwVCfRCb9aXLCiCNV/1x1MDAwNMfEKjB2b1x1MDAxZiVcdTAwMWXMTSlaec5Fsful4/Jdx0S+VYvwi2NcdTAwMTF/RdJcdTAwMThcdTAwMDFcdTAwMTNcdTAwMDRNI/BHV9bhI1x1MDAxN6AoTWM0gZJcdTAwMTBccr2Zm98vtOM49fLqXHUwMDFm/IpcdTAwMDZcZr2iaFx1MDAxYUZcYlxix49cdTAwMGUj77zne9D61//i38on5S///b/53b/6Z7/7J//ur/7Ff/jP/+j/+Fx1MDAwNHLrJJ2+XHUwMDA3t1PXf1xutO905n2E/kBcdTAwMTN+XHUwMDFjWqGfidZP74jRJELBJIr++LXHXHUwMDFlRi7Bb+xJ99d2eETXXHUwMDFlc2DzXHUwMDBixytKvIIhXG6lqXdcdTAwMTZcdTAwMDFfrz1S9Kuj+1x1MDAxMHSIjUPL4dh7jfrlgEzgr5CfXHUwMDAz3mOKYFx1MDAwNKKOn7+r6P3d//lcdTAwMWb+07/9s9/9m//td//Dv/6r//2f/e6f/tmvXHUwMDBi3e87/+fE7Vx1MDAxMZafZFmawFBcZsHfWJJcdTAwMWaCLYmYwpT8Rlx1MDAxNGVjZ51cdTAwMTGtf2MxX3o5Ifl26cavi1ZcdTAwMWF6RaBcdTAwMDfsIIh8Z7/iJ+BcdTAwMTZCMIgm8D94UfyzYftP/qff/ft//Lv/958r179F7P5QIz4vgIn3j/5cdTAwMWXAXHUwMDE4ecxcdTAwMTBcdTAwMDL/hHpgbmdKst6ss+1tzoVEXHUwMDEzU1S/8FXVXHUwMDE3uH5AuDT2K0GYQl9hMIRBXHUwMDEwSkPEz4MwdShnXHUwMDE4JqC/s9SrXHUwMDA1/Vd2XHUwMDEyz1Hyn/78z/76z//lr1x1MDAwYt5Pn/2XQG3RXHUwMDA02cfr+N9cbtn3d0MgXGaDUVx1MDAwNCHfTMhcdTAwMGbhNuSRJ1k2XHUwMDBmy8xcdTAwMWZzQc2cnFx1MDAwYsWXjVtcdTAwMTTCXuFcdTAwMTRFw1x1MDAxMIyih2Z+c+aXz/9cdTAwMDYhofea8v2A/MmbXCI0/opCXHUwMDEwmKRJ9GWZ7yPQxVx1MDAxMepcdTAwMTV0IJeiSfpcdTAwMDBcdPXW2HxcdTAwMGJdjEZcdIz6RTZcZn8udFx1MDAxMfSQ7m9VdfxcdTAwMDTojlMwzeNru5W0cdFm71x1MDAwNMw37f06XG5cdTAwMDOUwiEqoOGUpJAkJolcYlx1MDAwYmlcbo5cYlx1MDAxOEmPXHUwMDE3XCKChGPk7YA6UPky3W9cdTAwMTaOv2vd8fTbZ3/6s7hcdTAwMGXDPilWMVxiOVxc5k/ZQSyJxMvY4nLysIptUHOwp+35ZWNcdTAwMDY5uFx1MDAwZaExkqap9yogX69cdTAwMTBB5K/Eeu/U0vx4qjtQTtBcYvZ312T+1T/9j4fJ+8u/+ItfXaJ+/My/XHUwMDA0w33/1WrwJ1lcdTAwMGU+0EpAh2h6I19/XGKx+IbLXHUwMDFkddswN0A02i2axEbSL1x1MDAxYrHkWyj8hteI90/+y8FcdTAwMTKB8VfQ2z9cdTAwMWa7eo18d1x1MDAwZvKD/UZcdTAwMDI5XHUwMDA0LYriv0DB9K+43bhpg23xlSU+e/lcdTAwMTFcYrlFP9Lqre3GP/74137renpuqaxAaPfTw2NHmbxdXejds/z+/MEwdOtP2cb86fz8s5iR/KRcdIRRXHUwMDFhoVx1MDAwZT351vrsXHUwMDBm4ezjg/lF44w6uO8jlPhcdTAwMWLk8+7oQ9SrT/i+XHUwMDBmdzBpXGZ6Qdbf4oVcYn9cdTAwMDOt+LP2L8XLxfyKq4t3xu5cdTAwMTfdwfxcdTAwMDFGeJ9cdTAwMDU/2qDPXmVDf5JcdTAwMDMxhFwiKFx1MDAxY4Z+PFx1MDAwNVJcdMtE2W/F/rbp6PE1q1E/7S9cdTAwMWKaJP6etYNh+L2T/4JcdTAwMTT4VnS+MXJvne9b51x1MDAwNiM4ScI0+Vx1MDAwYiyc/oosl/fPhKjolqkkz5MywlB25vJcdTAwMDWyXHUwMDEx9cmde1x1MDAwNMJR6kiC2I9cdTAwMGb5j3f6y1x1MDAwZXlcbnuFQFxijH24XHUwMDE3+Fx1MDAxYpr8bMFcdTAwMGbj+KvDXHUwMDAxk1x1MDAxMPrR3YSPXHUwMDE11aAwgSH4LyH2/lBIyeady1eMKX3lJMPyNvf8osT0XHUwMDAzefp9Yvpkoz47OcHIJ6+aO7wtRZLkT4Bq4l9Lr9d4VevkxnHXOVx1MDAxZMr9XHUwMDBih+r77IR9vmVcdTAwMTNcdTAwMDQm3vVnXHUwMDFmKVx1MDAwMH2518P3+jNcdTAwMTjDUVx1MDAxYSXwX6LQ5lekrptcdTAwMWPC+Fx1MDAxY/w2pajmMm1z2sNcdTAwMTbzXHUwMDA1UtenjVx1MDAxNHEkVVx1MDAxYSd+PFx1MDAxYT7e5S9cdTAwMWFcctRb1Vx1MDAwMK/RQL5cdTAwMWKw0OcjL+Ijwu1DxkJgXHUwMDFhxmD8l9BtfyiE9dd//i//6v/+X373//zPn4mqfiBpf1Bj9mFzPj9JwZ9EJfJyN1x1MDAwMFx1MDAxOFwi0Tfp8Idw6TBj2pJ3buF+qzUnaMMv8m39snFJvmVhXi/1f75a0Fx1MDAwZlx1MDAxNlx1MDAxMT9CUjD04epcdTAwMDZ0yHrqXHUwMDE3ufvPr8hKkP2A3EAunLBIqId2R7P9XGZ9gaxcdTAwMDRDn1RprzU8XHUwMDBlYz9cdTAwMDFcdTAwMDBcdTAwMWbv9Zdcclx1MDAwMIp6r0xcdTAwMGL+jPXQMPajyFxiPuhcdTAwMTI9Qv5cdTAwMGZz+/dnkdE39U3/3z/6778plvjrv/hcdTAwMWb/8i/+3WdcIqZcdTAwMWbI01x1MDAxZlRhfX/TPj9JQZ/e64JxXGbCYZj68eKRz1x1MDAxMlx1MDAxNItVfinugy1cdTAwMTnK9Xwuv/C7YpHIuzcgQT7nOlx1MDAxZvGRva23bsr0XHUwMDFkQmH65SZYb+H5XHUwMDBmgZZcdTAwMDT3blx1MDAwZVx0zuEwTtRoyO+nxe6/QFr6npp/jKRoXHUwMDEyQn5CxH+80192xFx1MDAxM/gr8nD1JP3Bzlx1MDAxM4Jgn3PjicJf4YdcdTAwMTfFfuzmXHUwMDEzikI0TFF/m4XCvzpV/av/9T//43/91Xe0cPzx1//x//qsjPVcdTAwMDNJ+1x1MDAwM8b6US38/PtTn7wvXHUwMDAzTNEoTmLwT6jRwLwnxOS8IPVGYTtIVjBPSv7CUYy8ry1/XHUwMDAzf9Z7Z719ScV3mEXhXHUwMDBm6qVoXG6FYVx1MDAxOKK+rE2qX6tmXHUwMDAyeVx1MDAxYlx1MDAwMX9cdTAwMTP2+ub8XHUwMDFmXHR85HuKJkjoiHpcdTAwMTRBfnTgf/+AfJGBT+HQXHUwMDA3gU991vvyfFx1MDAxOPbEh1W1KErg0Fx1MDAxMVx1MDAwN5/lhlx1MDAwN8hcdTAwMGaH/S9cdTAwMTmYb1xyZTBMbPFNne07XHL79obwP6ag4TXpRa9rdqGXi1xyMYyGkENpIDDy1t3/Xlx1MDAwNizoX0fAK/yD7iZt/MPN+P4s/lYzflx1MDAwM72CXHUwMDEwmiBJnCRo7DA4xFx1MDAxYn56045cdTAwMGZcdTAwMWFRXHUwMDA348R1TVNMx/CaXdFO71x1MDAwZuPr8WJekJsnwVx1MDAwN3N8dOLt196HeP/yje+msDfPvnqDgtd/fPf8T//4o++G3343gbx5+1x1MDAxZr39+2eua37fzSdcdTAwMTBcdTAwMWGn4J9w0Sxz76C4XHUwMDFlXHUwMDE3sZDFRCckxlx1MDAxMm7Kl52BXHUwMDEw+N3yyCOeX5EvV6GTXHUwMDE4dWhcXFxm/3z39sffyu1vhDP14VwiXHUwMDBmTNLU0aC3JuLXZeFf/lx1MDAxNil/9G2sf1x1MDAxZPS9M1x1MDAxZF/5XdOOrlx1MDAxNfG3SvvN13y9XHUwMDE0ycp+OL5/L33980Ltr9v/+rKBN/388ddcdTAwMGW8XHUwMDE5mUNhN8nl7YtnwP7tXHUwMDBiXHUwMDEzip95UcIxcIFrqy9cdTAwMWZ+efon3331339cdTAwMTHRx1x1MDAxOFx1MDAxNlfWsFdIOWdcdTAwMWRz/OiOm/NudjzbXv60PI65XHUwMDFmv7mKhfH55Vxic9NcdTAwMWRcdTAwMWKSmGHEXCLCYlx1MDAxOIlhZZtcdTAwMTfcRCDH1oqpq+Z621x1MDAwMJAzUMmLOy1QO+pLXHUwMDFhXHUwMDE1onWl3MxcdTAwMTKCQM4s11x1MDAwYtzcXHUwMDE2eJ+Zs1x1MDAxZU1cdTAwMTIstbhcdTAwMTC6XHUwMDEx7rV/+DNN7jGK+qxcdTAwMWX3uk7PZOMvpk6CZ1x1MDAwZddcYr9JXHUwMDAx/5JcdTAwMDSkXHUwMDA2JJqszc9cdTAwMTN7dySG/+BcdTAwMTFJXHUwMDFjYzNiznAuYGVSZTHvPjqWyzKD5T/6WTlj2Pvl9DCzjPOP3r/3YK1LyzCN9f4j0zRe4ZjIXHUwMDFlXdG5f/jFx2tcdTAwMThcdTAwMGJcdTAwMTVcZsu/92DX41x1MDAwYsRcXOM6wGE+bGulXHUwMDE0zCyecqZ+oGFcdTAwMDUrMs9w7z/Eas9wP0638XFVNii4XHUwMDA3XHUwMDEyh01O7z7zS99tp3zJrFx1MDAwZvvCMkhhW0O94uKJXHUwMDA2UEU0XFyIwXKpMo9cdTAwMTPLXHUwMDE189ZDOrX8g2MoYU+jxvpwPE8vI1peq1j6cMxu2cxYnbSD7mRD91x1MDAwZtr+yI0sXHUwMDEzmFixXHUwMDEw54MxNVwip5NO602nnz5nM0zx9sPJjOxcdTAwMThW3bOPYX23vccj4bCXNpm0XHUwMDAzi/ZcdTAwMDcxoDx4gmNcXFx1MDAxNupPvvTeuHBcZr4pWWayXHUwMDExisrVXHUwMDA3460wXHUwMDA0c6euvMbDKctfur1bzoX1wbROm2H5zqlypUuIXHUwMDFjurjTXCKlXHUwMDEwVvxcdTAwMTTmO0xk8tNY+Vx1MDAwZsbqkp+k8+iZNlx1MDAwNkBjXHUwMDE4RnfqxotcdTAwMGaaXHUwMDE1qPRp1+DoYFx1MDAxZvSzZ1x1MDAxZWzBZ7524Z9cbneKMlx1MDAxZHgy9DlrXHUwMDBiXHUwMDAwXHUwMDE5Jlx1MDAwZu9cXFx1MDAxN7Gs9/uvPvimXGauY9CoXHTv8kZfTFx1MDAxZaIzo1x1MDAxNNd6dX5/PJS1tiy1cMJyXHUwMDBmXHUwMDA1lp1zXHJp3o/vXHUwMDFirzDH52tPrU6HkFx1MDAxYkOpe1x1MDAxN1x1MDAwM1x1MDAxYae/wFx1MDAwZWWK0Nh+xe/gupgnS5A55ZAtgadccmNKXHUwMDFi0Veqt2wkVFx1MDAxZm7gSLmiXGKxVzpcdTAwMWNTzOEkUd1ccsrZ8emHXHUwMDAw2V5hijtf9G6hgCDHXHUwMDA37dSRmCvFIFhcbrfzxN5K4nHtcFx1MDAwM4pcZsVcdTAwMTUnmpKfi1mmMG/3ZIRcdTAwMGY0aS0t2edYWl7EnOrd+3JKNVx1MDAwZYFDr6bcOPFcdTAwMDJxNlx1MDAwME1cdTAwMDJR8Fx1MDAxNsOJWndrNTktlvaamcdcdTAwMWGGgnTs5JeSWdzF4+eI3yqJyV7yxda2jGhl6Nr4XHUwMDE3sUIztIVkaafOwkJSoig5XHUwMDAxXHUwMDE5XHUwMDEwU7qv+9VarlxcMaDRTd1J8Fx1MDAwMVx1MDAwNSbqUtd2euK1ZeBcdTAwMTEyI4NcZlx1MDAxMWP3jKGHcNm9ZLeSXHUwMDAx8jLJ26vtflxu9uhcdTAwMGUpw4knbskz0nz3XGJcdTAwMWRcdTAwMWPMJ4rJ4n6hM7+X+m5cdTAwMTXRXHUwMDAxXU53scxBnz1cdTAwMTm1s/ggXHTQXHUwMDE5wj6EiOySnKYlgFx1MDAxZsQmVShQa/2KXW5cdTAwMTNxymlcdTAwMTNkt/XCOZbISC6vNTmeOPtcdTAwMTFjXHUwMDA209gwn7ErpTI3pL3i5TShuzTaN5iKnYFcdTAwMWPLWKljLndcdTAwMWOJXHUwMDFlXHUwMDBlzYUl8UOCkKTkbb+5TVx1MDAxN1xmSJpcdTAwMTVcdTAwMWJcIkY/11x1MDAxZGj7V1x1MDAwNWJ6SVBvPKWLRHqCaTbipJJlSnq6LSx/l7hboLmUx1x1MDAxYlpDXFxcdTAwMDSiXHUwMDE0Wlco0LFMtXbhWFmFrCvrXlx1MDAxZXvdIJdM6S93hls7KOkkdpWQXHUwMDE1wX2eXHUwMDFmavLWeYrOmTuJcGsyt5G8qvEl1lxuT1x1MDAwZvi1kDLGXFz2cYtkS2VcdTAwMDaUXFx3XHUwMDBiXHUwMDE3poLrSrch8LOEc6jHXma0NFxu/6aBgz5aw7w9VD+Lcz1OXHUwMDFkeFuTQWE4ZrncXHUwMDEzrOD8ZGFMlGxqrmhMzzgxV55cdTAwMThhf6pWzitYhkmEs1xus8zM8khcdTAwMDPKXHUwMDEy84DIZ13sZr36XGLlbJadwaZRxblcdTAwMGKeifKEcKBcdTAwMTni11x1MDAwZU5VgSwgfFx1MDAwZlx1MDAwZqy5I1U/6JjsXFxU3KBOpJtV39SxLONcdTAwMWPUXHUwMDE4RT8veU5cdTAwMDFcdTAwMDM0r4Zd81EnyVKxXHUwMDE5wCU7R5eXselQqpf0VWogXHUwMDE4XHUwMDE3JWEyVrhDNJlCh40+o9RcYlDeJkWPZ850cHnKN61W+uhsp1x1MDAxMvfwIDiwtMzj7yRcdTAwMGVtVz1O1jNcYtrNWeo8KnJEl6NBne/O4IVcdTAwMTAvzDU1fZ5cdTAwMDNcdTAwMTYzNMnb8jxVtjNwVVZcdTAwMTUqT+JcdTAwMDbjb1x1MDAwN8+IXFyZPqvX+eNyq6IsXGbi7HpcdTAwMWRW4nRz9lx1MDAxY0dOIFvdXHUwMDA3nj3aVm8kecJcdTAwMWZpoF8je6pcdTAwMWQ/O293MjKecOKju2w2XHUwMDFl64b6+Fx1MDAxMJ7gQJnQSIFpXCJae5U6bb6PKXzeJEM8SVx1MDAxOS4ztmTFeiE1tlx1MDAxYjw7sXZcdTAwMWZcdTAwMDR9Llx1MDAxYqpu/IdnbkC5mvzOIam2eSq1XVQxJTGIYVx1MDAxOedcdTAwMWVDrVL7t9XOwJCDXHUwMDBmyJ9cdTAwMTc1nk+dZtdieSgqSWC5PpWJq7Jm3bUupV2uJ1x1MDAxM0RAbV7og2dYXCJ3uFsxXHUwMDA16zMx9VtOIzemrKHmpuaZ1bI6s1xyLkToXHUwMDFi6JVcdTAwMTjQP2RTXHUwMDFj54PaXHUwMDFl2lx1MDAwNfPZXHUwMDE1kOnu5MhcdTAwMDd1Ie7T7DvEOXU4XHJkq5WWWUfk1IbFNHjh8vjZMc/+eFx1MDAxZnqRzra1+Cf4kdBcdTAwMDPEwlx1MDAwNKkqwnR2M3lcdTAwMTN30MZcdTAwMDLgauQr7Y9qLkHhiDRP5tLHlJuWy31rcFx1MDAwZYpcbmlUeCjQOCqtO1x1MDAxZXJcdTAwMTiwmoRcdTAwMTVhoGeegXLOpOXALjpZcVZXZmY61eKpXHUwMDFmN1x1MDAwM1xytaK/aY8j3dhoMMnc9Y5m4bRcdTAwMTVr+NC0jDrzx1xiTNTGXHUwMDAxnlxuylx1MDAwZSjSVaJtgJHYnO1cdTAwMGVngctcdTAwMTdcdTAwMDbTibHqbuNcdTAwMDXYp2dAW/6IXHUwMDE24WnfkrvG+ecmSFx1MDAwNrit+rSsj5RcdTAwMTSUNlBbXHUwMDBm7C5XPv+0W7go+2FG+bOxe35DRV4871wiht/SvTXQSLKYXHUwMDA1v4gmXHUwMDEyP3E/XFxBWNu1a87BT52E4LXbYERdXHUwMDAx9ax5pas/hOvFepjENlx1MDAxNbiwRPssNqHYLFx1MDAxZbM/4lKpLvvtjoZmyOFccnwm9XqwXHUwMDFlXHUwMDAyd1x1MDAxZldiOmdcdTAwMDVGuJSKrjU+XHKGacvcqVxuS7CFfNmdlV3oiUVcdTAwMTdPrFxigrhGXUtwZp/Z2c7kXHUwMDE33cpFrFxcRERuWc9AXHUwMDE0fKjNWOu88VrtQ+qzXHUwMDBiaFE7IVx1MDAwYugsW1dcdTAwMDTmuPR1vuondFfHwE2p86otKv2QJ2LFTucjpEw07i5cdTAwMWLzuJJs0mmw/+BcdTAwMTNwoiC2J1x1MDAwM8XhXHUwMDEwMpCwI+RcdTAwMWN46CM6fVqJqTogYixQUdLAXHLZJs3plVx1MDAxY6svXHUwMDE501xc2qf2eLY+OFwi0Y5cdTAwMTmTnHZcdTAwMWQ2XHUwMDFlXHUwMDFl/3qfWEKppUXHypc0xJwh0p5Nrlx1MDAwZjK+pO9cdTAwMDY9elx1MDAwNG6rs7lz2SWKpfb0uFBsbVx1MDAxMz6CMos4XHTmrGqtoD1MINP0J1x1MDAxMV5G+DHyXHUwMDE5XHUwMDE2o+xcdTAwMTFlkLXVQJq14lWZc+42mrZy8Fx1MDAxYUppondhXHUwMDFlIKVuNIXe6OxpLuddXHUwMDAxMadZ51x1MDAxZM+rXHUwMDE1Y5H9RlHRyXVkbL5vR8LLXHUwMDE0krekslx1MDAwNGmZx1x1MDAxZfe0ON901zqUyDEu6YQoa9TB+Gojx7zg9WpQIICo4aWq5Fx1MDAxYlx1MDAxYshMJKXgI42wXHUwMDEyoYJcdTAwMWVKwlx1MDAxNTFWXHJcdTAwMGWnulL4y1x1MDAwNrlcZlx1MDAwM4Bbhlxi1ONcdTAwMTC7cY7KqVmdmlx1MDAwYlx1MDAxM/BiXHUwMDFhi4d80Fx1MDAxMfKOSKF3K1x1MDAxZlx1MDAwNVMjd8ZK2frZLCNcdTAwMTmh3Zk26EnoWvbkXUZu51OJtK/PPoXXUKST+Vx1MDAxYVLWlYeLjdlp0qRM21+H2lx1MDAxOUJpgYt9d/KTSWvXW9KuK1C0vCjWyL5FJ3ae17vK4yNsRlUhSDnqXHUwMDBloHCexiNcdTAwMWJntGWaJCZcdTAwMTVcdTAwMWRI3ozGQadcdTAwMDJC+sTf542odCpjouhcdTAwMTBcdTAwMDJcZrZVzbV/ZtlVXHUwMDEzJFx0aKxVla9Ge+n8XHUwMDFhs3azm1x1MDAxOVx1MDAxYackoELRXHUwMDBlz9xKZ1xuRYsmu6OvlK3v+XqGXHUwMDE1cCjjgWU7Ryz2dIbiXHUwMDAyNmDZ16RawO6YXHUwMDBlPnJcdTAwMGaPblx1MDAwNVx1MDAxMUNmLJbIRVx1MDAwYoXnLt7nXHShXG5jsVFcdTAwMWJcdTAwMDTSJmCvSlOYbZjLXHUwMDBiXHUwMDFlLlx1MDAxYzxDW6tg9Vx1MDAxMfZiQ1x1MDAxZtiQJ1JVqdTpS5k9addcdTAwMDKVXHUwMDFjXHUwMDE5ydJQZiH+TqFcdTAwMDLHe7zNM+hcdTAwMDBcdTAwMDCqXHUwMDFiXHUwMDA05pNcdTAwMWRw3IUpaDiDTFx1MDAxMTN3VaCuQjgx4kVaXHUwMDE5dkTON1x1MDAxMUk5S1x1MDAxMUCbK5AwefbLTF4m3VO1WSuDLI299Fx1MDAxY5+JJJJcdTAwMDXyTqm0kYGEUjlccrdyyF1cdTAwMWaShzCHlvH0aT2nYayM5/WJLZ0+7oxccl5cdTAwMWLwJtPMU6j4XFzLzrFhNLx/tuXCg1x1MDAwM+Oq3Io8cqWD36CuR7pzh0ZcdTAwMDVvpmPpqk1xWslJL2jCTuv+YZ3pgy+E7c5cdTAwMWb8eVx1MDAwN4p9XkT2iPG8wL1cdOk8U7hcdTAwMGXdajbl5WlNrcOPRFKRwpRvvHg4oq6JlEBPQ4Gqr3mc3fiY1LTknGDzXHUwMDE1LIBrqKe+XHUwMDFil9zTl+2VUM9miVd5NHshYt2RxzJcdTAwMTXIrU+zIcRcdTAwMDXLLElcdTAwMDJn9lx1MDAxOcZxdIzFI/jrRTg3NYuV+VxcW0RzI5lKXHUwMDFkbzVcdTAwMDKEmkqzu+Jcclx1MDAxN0st1WBcdTAwMDJlfujR1lhcdTAwMTbHXHUwMDExe0DTWlxuwNaHpOYoTSXpMzpfzL5Ky3xcIjpcdTAwMGaeTVx1MDAwNU75sJLcskekXFyr+OlM5iV8XHUwMDA1PZF+qpbMcYphkCnknnfvXHUwMDAxPFhYwfkrV1x1MDAxN4jJdnn+NFxc4Opmy55cdTAwMTAxXHUwMDE4nlx1MDAwNdhIJSe3cuqup/G5qmzyNoz1fSm6XHUwMDAwf1x1MDAxMvdyPys4M6l4kcT9lNjtU6Dy7OIo65GqTzHRZipXhif/0Fv2+UT7OXaI6nU8XHUwMDE0+8goNjKeM6VIr/E2cXHEW6B4XHUwMDAys8JfyWFcIqaCj73Ot55Gey+Yg1x1MDAxMnFQYoA1np42omWbIU+wljatRWySPKIuljCJXHUwMDE2bWpNwVx1MDAxZG+gXHUwMDBlu5rl1fI8efJ8yCmB6a5cdTAwMWWzxvBiyFvzork3YMA2MMdsRakx2kFcdTAwMTdJPSPydi3Su7CPqGclWUtcdTAwMDLVwZ3Kvl22q1x1MDAwMiBcIukvo475RpvaXHUwMDE5M1xc22fF9bfueiN2ysKoXGKxtVx1MDAxOdV84Fx1MDAxMSXtnYKKXHUwMDE1XHUwMDBmu9HKJOhcdTAwMDB6XHUwMDAznvztqTBoY6NcdTAwMWJV2MDttFx1MDAxNd2l8Fx1MDAwZude8E//LNkoS1KWNyuSq1x1MDAxMFxcdctcdTAwMTJ/VDLmfr6b01xyIbuuZEbleWdjmm/U8fGknKKHenTLVmrW2mchXHUwMDA3gjCSvFx1MDAxNptEfZ72Uq9hosZcXO9Ft8Kil5Simqh3KGLzZNziwCTGXHUwMDEzXHUwMDBiublcdTAwMDKAk4g1YVx1MDAwYvLahoZcdTAwMDBiNlx1MDAxM9jxIUhOpMvekWqaT5LTXYlzpK7kXGbVbSw/RWDx4vRuio529ubbXHUwMDA1umTmjt/G1S4wbp3MhZ44jkIjmrhcdTAwMWOorMGTTlx1MDAwM37MeeLpXHRNXHUwMDFjoYhcZssnuGrq0q5cdTAwMWOxTaBh5F1cZoqtXHUwMDFjSeJvr33zKVx1MDAwYl/WXGI4edJcdTAwMTPbYemdeDlXkNyFXHUwMDE1Q5/Wblx1MDAxN3zNXHUwMDFjps9cdTAwMTLsRd5sIyUvOHhlXHUwMDBlWZknhIBcdTAwMDP9mvjBSF3tQJpcbjKOKptBVaS1cNG5R4/iOjx7lNyCXHUwMDEw924mQGLiTjqd+7xy+lx1MDAxM1x1MDAwZiCn5ydcZuM9afJmSeHEmVx1MDAxNFx1MDAwNFqHXHUwMDAxM3ZcdTAwMWTA8O+VZof+TCckdF5ObdTq43MySLD2xerUQzmoeMZMq7cso1x1MDAwMz6D1NEs7lxivXB2bFx1MDAwMMI4SbOmXHUwMDE0akqwnnrWjdRcdTAwMWNFirh0oCnv+Fx1MDAxNWhRulx1MDAxNVx1MDAwMc47slfqmlx1MDAxYzRcdTAwMDGJsichSu5cdTAwMWSLgEt+hlx1MDAxYdnh+ENcdTAwMTKmprBvjZvZM+CbXHUwMDAwlC52h3pYNC87M4Heqlx1MDAxNJE3+lx1MDAxZVx1MDAxOVxiXHUwMDEyop1FVzLZ4lx1MDAxZaA6grPgSdRcdTAwMDbdvOSrNrFSXHUwMDEwXHUwMDFkvInEXHUwMDEyYKhcdTAwMDepXGKGNLsyM1xikFdcdTAwMWLMvd3KpVx1MDAwYrTzVYEn9LBcdTAwMDFcdTAwMTSkZcBiXHUwMDEz6+UxZ+yJI1LHzlx1MDAxOZWCXHUwMDFiSGbdx5FcdTAwMGZcdTAwMGV2XHUwMDFiWJBRSVJ5XGJcdTAwMTbozbWYI2RcdTAwMDPEXHUwMDEwKPpcdTAwMGW1XHUwMDE4p9jNMtP+xuPQbzzO0Vx1MDAxMz49vFx1MDAxZHxcdTAwMDbNgphDIWNcdTAwMWN18S3jmkGs67T3XFwxRVx0t+LChMhU4NdcdTAwMDVAheGaVbbrXHUwMDFkXt9ZW4OZXHUwMDE2TuPWQVIlmkjEcrTO7CiFXHI4jkBcdTAwMTRCXCJgJVfeS1x1MDAwMaHh7VNxM1x1MDAxYp3CRKm+mLnmRHlSXHUwMDFkurxcdTAwMDBcdTAwMTAsXHUwMDA3t1wiXppdbYuLSuKzN7U3cr8u6Vx1MDAxOWWsq9pMwGNDwVNILKb0YHBcdTAwMWLjpaPpKne75yojc4W0XGb4KjmkKVUwSFx1MDAxNVx1MDAxYZzaWn3G0tMtXGKHwZuNOlx1MDAwNsGu5lx1MDAxNVx1MDAwMHJCglx1MDAwMc5nQ2woU1x1MDAwYnWZ5FxuXHUwMDE3UPH7aESL+HCjQzAvXHUwMDEyL5tcdTAwMDTMzY+2XHUwMDA15O12V33MJqXZS4NhvI7W7SbywnZoMVx1MDAxZuJaelxiTIboJkm7XHUwMDE0IL1tXHUwMDAwyFeHhlx1MDAxMzCtvvDQ3ThR1u2OQGqhUK1cdTAwMWadrCxcZkmPuFwiJ1x1MDAwNkt1m7aLfDH5Np6Lk1DvrNeQh5FayzNOYYNZQSwhVVxmi7n+4kJcdTAwMWPGX4KQKjJO2kHvfoQ73t3u8MVglTPbVj5cdTAwMTlZwZhLhnTGss6GQu9cdTAwMDLEXHUwMDFiXGZeetuJ8So0mkU6YjTMjVx1MDAwYj9vwYh3Kd3gqtHlUauiZl1SXHUwMDFhlvDNScRIXqe1dOC7cc2kKNg6cCVDXHUwMDFhUaRVXHUwMDA3j2lFNdVulNBcdTAwMTRp0qHoefE1hWN5UjNChkJBcEJv5ytTVc6mXG7DtJy6gSlDoOS3pzhmmeRRSVfN006lu2FeiVx1MDAwZaZtiqbMqLFcdTAwMWJ358nNQVx1MDAxZCg/bYLUW1x1MDAxOVxyp1x1MDAwMTBcdTAwMWJLb9Wh03RiNy3DqtBgzjWbhMdG4tx8+JJcdTAwMTJcdTAwMGX0YFwicWW1W1x1MDAxMzBcdFxy0ryFL+1tcFx1MDAxZuyJefSx8MxZ5CnNmPHwhXVCXHUwMDBijLGwRZTFZIw7sFDD68RcdTAwMWXyr63amVx1MDAwZlx1MDAwZVx1MDAwM7vq2ehcZj5zWL+87FxceyOfzSWU6dyoueBgc2G4T5AwU0zhXbyrMqahzisnXHUwMDFlXHUwMDEyWFa+VFx1MDAxMWAxivyyJlx1MDAxZVx1MDAxOVx1MDAxYd/dv11cdTAwMDO68jyWXHUwMDEzLVx1MDAxMlY9XHUwMDAzdPy8J1Iy0oDodeiYIycpxpt0lz23VMONQ1x1MDAwYlFcZuBniXeozqil3UtcdTAwMGaBh7eZ8FM34m1cdTAwMWQvILfPKHpa2PlEXHUwMDBlXHUwMDFipDJcblx1MDAxYymrL5aV23D8XHUwMDExvVKGJpmfodVcdTAwMWVOR04ttNuezVtcdTAwMTOrUivpJOhIoFxmXHUwMDAxW9tX/Zh1/b3yXHLxVmyEaqnctVx1MDAwZVx1MDAxZTW/Q2mmw41qZk0oW1x1MDAwYmpcbihcdTAwMTIoN09AsVx1MDAxMyNryFx1MDAxZMzpXHUwMDEyn0W3Z4Rm9zg6qTbJ0pM5rPmlt+WDbyfAIVx1MDAxZrVUXYZcdTAwMTF0XCKZlZxaXHUwMDA0pFx1MDAwZZHQ3bFGXHUwMDFiipU7d+kpfSH8Q3RaYJpcdTAwMGJ18VAzrXC8OyCm4HIn/UtgbKfdX295uqPjQer7btA5uV6KXHUwMDE3TiYrRlx1MDAxMiGLooaYXGZcdTAwMTVcdTAwMWFPkNOpXHUwMDFmmKrEXHUwMDA23GfwW9zcO0lWklx1MDAxMn1yLCuVr9dHl5p9Wf9CXHUwMDBle9at5Vx1MDAwMSvzQTCoW2bsXHUwMDFhM2nxXHUwMDE0eVrJkkOEmkg25IJrXHUwMDEwT+HWjbetqVW2m5jw7KVdKaBPQE1lK1x1MDAwMsCzKq1S4VN8h6rTsonTKdvZPtWlw+CPiKqO3sVeyCPJKIfW0YWM37ozlaVLeabzJZ5cdTAwMTm1mCjXh9LqiiD1ibRcdTAwMWEppskrXHUwMDE2zv6OX6BcdTAwMDatn1x1MDAwNlx1MDAxOXbTjcXnMEvOcFx1MDAwMiBnT26oXHUwMDEw3p+ekeghoD1lxlx1MDAxNNM8Om3AvVZcdTAwMGJcdTAwMGK6ayNcdTAwMGXN12uG8YdcdTAwMDVB2rhZIFx1MDAxMFQy7UxcdTAwMWbsP1x1MDAxY1x1MDAxZZLTjoBoXmSnXHUwMDAwWbFKT/NcdTAwMWVsOsCOTJHU2IJjxnNcdTAwMTPopuBcdTAwMDetY1x1MDAwNjNcdTAwMDVolso5lOpu9Kg/XHUwMDAyLLiuKZraSIOdsFxiqSxjsPX58JBripvkNKC1e2Vk8SbLzJWHXHUwMDE2w7rgXHUwMDFjI61WJ7HPvD+kiMAyjyxw/JiPXHUwMDE5X05l78I6XmpfVVx1MDAwM1AldJpEct5YyI/Yo19dW7PeXHUwMDEwQlx1MDAwNbZkXCLrWlinLH6DTuiq2Vx1MDAwYlxmb21cdTAwMGKKYUGPKPs8nfy5iJJGMVxcpHfOXHUwMDA2XHUwMDExk1OaLZlgrzbfXVV7PjLJeYDJQ1x1MDAwZswnXGZcdTAwMTN4P3n4muNHsKiAVn30XHUwMDE530hMuGvc0ov3TFx1MDAxODFzXHUwMDA1mlx1MDAxOVx1MDAxMVBcdTAwMTBhpUuNyO2RgsElvFx1MDAwMGSIYXFcdTAwMTNtXHUwMDBmrivqXHUwMDA1m0hZe1x1MDAwMlx1MDAxN1x1MDAxZbPpzYqQc0WTuOTwslx1MDAwZuhcYvgo0ZtaNaRcdTAwMWXVVr903OqzrdJUnsnu6UhuXHUwMDAzXHUwMDFmRrxTczCyXHUwMDE2UsJGTjO70Fx1MDAxMWviumF7R1x1MDAwN/3EO1x1MDAwM9HGlpGzXFxxL1LhNvBSwVx1MDAwNTqo7LxcdTAwMGbureJwTtvFVJZZh1E+eMPK6ton8kxg5Fx1MDAwM7CVs8rLKVx1MDAxNnI833fNXHUwMDExVf+aXHUwMDE42VxcXHUwMDA2XHUwMDFjWE5cdTAwMTfUNIAhh198XHUwMDBloaVcdTAwMDBcdTAwMTfLqjdvZntcdTAwMWWY/GAjtThcdTAwMTU0sIRcImjdzUXxo44+PVx1MDAwMFx1MDAxZvBYaDljVMk8ZF1GTvBcXHLSvDFzrnemPXhQms5cdTAwMDaTNopcdN+CUqZ7Jaw4aVx1MDAxMEtcdTAwMTm885t5hMBymUPER2KROl+5XCKkXHUwMDFhvVxysk56Pq9cdTAwMWJ22OvFX0RcZuxNVl+V/KHyJ1xu152KvUO92TzPJENGM3Dw21x1MDAxNUqFK6ZQVy5cdTAwMDHYgp1cdTAwMGVO7TqGerhcdTAwMTf5UeNp4ToyTMQ6pWK+XHUwMDFlMKOcXGZSI+iWx3c7eET8Klx1MDAwN5LGsHcrXHUwMDA0X8bNXHLvoTZcdTAwMWM9yUrr4T7yovaTea8rMoJcdTAwMDNRuFlh8zjUN+Ok/GF2KdVcdTAwMWNE0UhC2ue5lEl8IWxUv03kJpFqMudcdTAwMWJq9zF4XHUwMDAzt0PB85RFqL3vQCpcZknCXCKe8Mn07HbIOGxcdGtJnvwquc/QQ1xcPOTOTulgns7l1TRcdTAwMDVcdTAwMDHnk5S4XGL5uLTYJV2F5TTym4qS/FwieVx1MDAxYThcdTAwMGWj7aBCT8XmI1x1MDAxNOenqd+37umcgSvtZlx1MDAxM+NcYvw1JW7tc+jNiYr5Lut5XFxNnvcmvCSPXHUwMDEz757vXHUwMDA3fVx1MDAwYpdqc5hGzXd0oEhcYtlPwFx1MDAxNk/nyFx1MDAwNyk4NHKr54lQg8CM5MV0JnpcdTAwMTHEvUh5gjjNlOKOUFx1MDAxN3JJgUxcdTAwMWN5fTfjR2r6m3RcImns4J0hZyxj91x1MDAxYluHXHUwMDAz7lx1MDAxNt2wieZDZ7q3XHUwMDA312Ao7kbQqc9cdTAwMTlGyNkxvlhibk/5+Cw0eePRVinVXHUwMDEyPG3meDhyySSyl7Xi7kpcdTAwMWWkcHX0K99cXKiC4V09UdW99HtliylcdTAwMTOmXHUwMDBmJ6afS0DKM2zouMP70lx1MDAwMN2XWHpr6jNuh/l41TdDes5hXkO2tCw6abbsrYNcdTAwMWWVplxi63rYRCkhm9CRatyXaTSrlko9+WZrX3Hap/qzn4+iT2/5oSPS8Fx1MDAxY6F26mdwSIXL3Fx1MDAwNMs5Olx1MDAwYnMsV/f6XHUwMDE0RFx1MDAxOIGSeCydrpvnP7h7gUXWWaTXIHfbVrxXVTCd2Fx1MDAxY6AwXHRPwuWBxNS9jVx1MDAxY+zhXHUwMDEzk+2G1L5pOSbVa3/VLrPa3aLSyOWk826712FcdTAwMDDfotSGoteC2FxiyWzAXjtcdTAwMDO2XHUwMDEx+Fx1MDAxNVxm+IzslOm1a1x1MDAxNKg4Y/fwwrn5ms5o6Vx1MDAxY/l8X265o1rnTaVcdTAwMWNcdTAwMDZj/Fx1MDAxN1x1MDAxYZlcdTAwMTP7SCRQe1x1MDAxN9dOKsF12/mznb3eL1V0XHUwMDFl47m1O0SMb4v8YT7GutNva8Hx45WmNFx1MDAxN1x1MDAxM0m7v4dcdTAwMTIhXHUwMDE2XHUwMDA3bLntPmhcdIpCaYE111x1MDAxZOTPJdvi91x1MDAwNX4+jce9Klx1MDAxZoHUbWaxltdN5/Monu+hXGbhl5twuk48XHUwMDA0XXpAdyeyKXJcdTAwMTnsvMxmXHUwMDFixVx1MDAwNjVcdTAwMDM9p6fmvFY38Kmcu1xuaiOYXHUwMDA0eFc7aYeQj+ONRaVbN2mVr1x1MDAxOfaEkKNcdTAwMWE5IMdcdTAwMGbjWlx1MDAwMDHZoZZMIPFtmLyShJd8NkGGXHUwMDEzaVC8kyNHXVQ7QNLIT1x1MDAxOF5cdTAwMWQtP5jOyiim0kKWPVqxR3ZccuZcdTAwMDJcdTAwMDRBZoJcYudQybS/5lx1MDAwMTVJQeJlXFyemta9vVT3h46p4p4qLixFXHUwMDA36M/FJZhPh+pnk5CP9GPEe5P3KZ20cpFcYs3aMNdcdTAwMDRcYliv2OVKhYjHMTb3XG43veI+KPYyxTtcdTAwMTG6XHUwMDEzU0gj/4RcdTAwMDPdNZ+BXHUwMDEyaKxGkptccqZcdTAwMDY8I/It7JPFRe9cdTAwMDJcIjdKrLandr9cdTAwMDB5XHUwMDAzXHUwMDFmXHUwMDFhXHUwMDBi88ONT0KDXHUwMDE5xTjU1yvvloaKPlx1MDAxM3bi59fzuTxcZibi9FGfvY3VrOlJXHUwMDAwwD26nHf7vN0363BDKn6KiouNUaFcdTAwMGU9XHUwMDBmXHUwMDFmdVWMXHUwMDA0z5Wr+1x1MDAwMLzTJehRvcGZKVx1MDAwYpbCO8zQw/NhcbOmbkxzieaxRLNGUcdSvFx1MDAwM7bboZqxZ+c5Ny+0XHLTPYe6xduN+OxvsPWoXHUwMDFjY3qSIaXadyiI5o2bRHmRW4pcbm0wxiV3aoJV3Ei10UDNK6lDaeGjlyRcdTAwMWOOXnIk0FORayRgQoFccqCkyrhBK0xDLv+U71x1MDAwYoHBj6u1YUC1nzObKfeagmhIp0ggxc/e1lxcUOV2+Gn6ZTk9O93j9tmg2n0syvZ0XHUwMDFkjLM+XGJVJZ66KEOMvIp6o3BKRKueveRwRjtxuVuhoopcdTAwMTgqsPKOlZ6/25+6PSjhblxi41xigvzNPlx1MDAwYlx1MDAwZZTmJL402D5cdTAwMTU5IZ91XHUwMDBmPGS7poLrw0ZcdTAwMWX3fTxcdTAwMTOnwyMvueDoxIR1VygwNSAuRlx1MDAxM2CQhFrwVOCI7Vx1MDAwZTSn261cdTAwMTUwXHUwMDA0PFx1MDAxZiae18OpnfMwSVrOnE3fXHUwMDE3OPSRJ7s75eebalx1MDAxNYrWSrlnK10nTT21W1x1MDAxMGYtXG7hXHUwMDFk/e1cdTAwMGJqumqAWlx1MDAwNak5XHUwMDFmvHPqXVx1MDAwNmCgx1xyXHUwMDFhk4Z9XHUwMDA0XGalZ+m6rXfuxohrXHUwMDBm71x1MDAxOaNybXjx61wiumhyXHI16iSt7ZiaQvTU6GqQ2lx1MDAxMs7AlKKuYVx1MDAxYZglhqg4dYlLXCI8Pd1kW8d5Xb1cdTAwMDSQdoBcdTAwMWGLXHUwMDFideFcdJ3r+lx1MDAxNMqIZTRcdTAwMGZxXGL7o1j4a+tMcD9cdTAwMTbuXHUwMDEy4iOKWI/mXHUwMDE2XHUwMDFltpB3RTGNqSdKb6NcdTAwMTLQMEeditlqdpmDwz4yVprQXHUwMDBmZ9r7qmYta3w9TCs8qOQkxvraS/wl3Y5IX1x1MDAxM/rs3Vx1MDAxYmBcdTAwMTYhr1WpSEBcdTAwMTjUwajg4DlMYVx1MDAxMaBtXGJAXHUwMDAwRIVI7NSeyl5PTibunVx1MDAxNUbN6e2yIFXFbVxu7Uz1Xlx1MDAwMZZth49rKIyBJFfPq9aa0mEg+TRcdTAwMDZ37lx1MDAxOMBcdTAwMWIxXGLdw1wiym1/XG4lRlx1MDAxOfydXHUwMDFl++dKJ5DIYUerXHUwMDEw9VwiqHxZXGLrorZcdTAwMTOi66WePlx1MDAxYzez1bFYbupQzkFcdTAwMTn1d0eHx+uhRVpwuzSILpFMgjOzyJ9d/ps1XHUwMDAwJ49cdTAwMGXhp/JY7ZgjkDDRXHUwMDE2VYeydDJ8xWXWmpSHcOft9nCms7xVJZ+nXHUwMDE39HJBkTzJV+zMaPSpk0/JMrhcXISxXHUwMDBiXHTIXZbBrVx1MDAxN+6MUoXuaJdnOJyEJz2Mxmxe+mhpVFx1MDAwZfbGp9bcnO7Kslx1MDAxZC/h92hBsuKwf5RcdTAwMTOSk97SYVx1MDAwZXlWXFyeXHUwMDA0rqrO1m5cYjeX91x1MDAwMPCC71x1MDAwZVx1MDAxNmVcdTAwMWJmZJt3ba3r42U/Wlx1MDAxNjqxsFx1MDAxYv5y6Vx1MDAwYjxccu9cZmbUXHUwMDBlpFx1MDAxOVvyLNK7uqCn1kD6y3CSyVxmZe7jXHUwMDFlgtW0IFx1MDAwZu/09PAoXHUwMDFm2Fx1MDAxZFxiOjbHaMVcdTAwMTAliM2ohFx1MDAxOT2VuHLPXFxyznGERKTvwsSKYPTKdFx1MDAxNUN3XHUwMDFlXHUwMDA39ifeQUhlu98prkwxLrNcdTAwMTT2XHUwMDAyXHUwMDAwl9BcdTAwMTBho1xcNXG/XHUwMDE5mW2bvjKxsShXlzQ3+mxBaT9cXFDRKvIj8VjCiqVFJOreLj1qPcmDwo2V2pPcUuZKXHUwMDFmXHUwMDE4VypJXHUwMDFi1zNcYr9cdTAwMDa7ns6j694/hXOMtUv+XFyHWr5aXHUwMDBmldApxC7Ie13dXHUwMDA1MVx0RFx1MDAwNrhiXHUwMDA2VCMkdEghXHUwMDA1XHUwMDE0XHUwMDE2+lFOrjx0PL9cdTAwMDHM2Nq6OGpEXHUwMDA0XHUwMDAxXCLF2o3t2srTx01rXHUwMDFlatyGgupcdTAwMDaTz0Oe5tZZ3p1DIMHH/1x1MDAxYzPfXupxuNh7LHKmylx1MDAxMNnXl6askMOS320hvVx0XHUwMDFhZfiTxFMzXHUwMDE3QVx1MDAwMVx1MDAwMlE3ZT2SYajwz/xa19wyecymzUueP6uzY50k2eWZisezvYR1xuBccjN2XHUwMDFihuWzLLR5q/E9XmG9+lx1MDAxZVx1MDAxZcKVT29YXHUwMDA3NrvLO2m/g7dTZcRGooDukkD5XaD7O+9ozIW7ssysn4d36sY47srZK/hwXHUwMDEzgTjMJHEkXHUwMDE37pDtO/BcdTAwMDQpO9jM6KbYRbnxJzilo6Q4plx1MDAwZaeQg9xe10tK+Wt/P//e3z+V18dPNtNcdPzAk/5cXMJcdTAwMWOHXHUwMDFjtM1Bzu00hJc2JD2PXG6J0lx1MDAxM4HuziRcZsNkXGZf89b1orhBeEpW3V6yp1x0IyWTJiv3UvMmWpLLn4nonFtW2F30vnop7DtcdTAwMTTseuG1IFxmSkff6GtcdTAwMDfZSp9Wl6a/XHTV6dlfXHUwMDFjXHUwMDA1b3q1RrzRg0XBxOlQXCJVQMwrSfOBarGEeOvu52F6qV+0yuylrq60XHUwMDAx+91zSIeHzURCpevzprj0XHUwMDE0u3N1hGe2uCxFXHUwMDBiXHUwMDA0NGZcdTAwMGL7TV2rXHUwMDFjyK/XXHUwMDBlw+X3eUPKXpfB6fyFXHSy1LjRp1x1MDAxY/a69OIj8or3m/z0fVeSXHUwMDAx3nphIVx1MDAxZI1cdTAwMDLxWlx1MDAxZUKH2Fx1MDAxZsTrmkuLkbiXuWpZyzs8xbyjKHizKXRcdTAwMWGfXHUwMDFm1No5PFx1MDAxMzO10dpOdntIneqxXHUwMDE0XHUwMDAwVaF2XHUwMDFk6id+O1GAeFx1MDAxNtmilKPlObQw6Vx1MDAxYejtIWhcdTAwMGZXSVxmS7pFlM/y1qFcdTAwMDLV8IPa1Dc1h2RcdTAwMGIginOKXHUwMDE4udbfr32cX1xm8OHnNCa9UXxcdTAwMGLlNYPGmlA2RFx1MDAxZc1cdTAwMTas1Mqej4mu7qtJYmBcIlx1MDAxN9JG1T1mP05cdTAwMDPij2hqXHL+mFxcPdjxjP5cdTAwMDKcZFxiiu+TJz+INqrKNMhu4WhzzK60vE/uXGJcdTAwMTlK6GlB2+uIlIWaXHSbJJ1WXHJcdTAwMTGpw0y4k3EmgztGrcadYC7P4Laqdlx1MDAwZu1s9ZRP1XU29+DOMlOz+5R5ytf9YkHkxd5SlDz4jaCTvu9Ic+wjjVfrJ5ZcdTAwMTmqUOxcdTAwMGJlslx1MDAwMCS3J5xqXFxXqVx1MDAxZOnIjNoqY3RcdTAwMDJzZNxA5nnzxOOcr2uS7eFcdTAwMWLsnuEmeFx1MDAxY8PSXHUwMDA1xKBwJ1x1MDAxN1x1MDAwZqprsFnW3jZcdTAwMWMzSnhaP6aLdc6vXHUwMDExgPU15fJcbplcdTAwMTOT4ClcdTAwMGVDweQhrIyBqDOZu/l4MLp3XHUwMDE2O4j7pT5cdTAwMDQj0ityYMFbPC1cdTAwMDB7gUn4YMjxqT78XUTDKlx1MDAxM5zn8FllsOH1gje6vIbjWaZyxlxyXHUwMDBmXHUwMDA2XHUwMDE3YrvrzZxcdTAwMTU/XHUwMDE1WG7GksG+XHUwMDE2VWbqXHQtsKdcdTAwMDfQ3zmrRlx1MDAxMm9cdTAwMTDOTHKO5GeWtYbRwnHn8sJcdTAwMDCX54WwXHUwMDBmz3h+YEl4cXvmJpZbhkbLbbzZ9861klDbVcdGei1Xik7xKSDJr8RiY5Uj+85CZ52Qq0PiVFx1MDAxYtPwa1NI185cdTAwMTOKO5ZHoLnT8JlIYJ/VMXpe/b2nwFx1MDAwMdwjp9hcdTAwMGZTJlx1MDAxNYhXTEphNdJZLu7taUZP1b10wzpcdTAwMDLEiUzg6E7sky5cdId5gG5H1MdWh0WGzyy3nSDp4uhcdTAwMDdcdTAwMWbEfXZMgGL1MdKUI1bJXHUwMDA1XHUwMDBikfGDTVx1MDAwZceg2E+tkNrTXHUwMDEz52v3cXDmeYPNnIRcdTAwMGZlwbFjddLbXHUwMDFkXHUwMDAwtbbL5UP/ZFx1MDAxMndE/Vxivcxr61xcXHUwMDEwlrehWL08kSC/j4HqdOwpqvyzPd5wZ9pcdTAwMTLomFx1MDAwMDay60dmSVx1MDAwMs9m98fpXHQkYDzPW12/1KpL3lx1MDAwMJ5vI2ctz3FuSXoupMg+klx1MDAxN2lcdTAwMGZcdTAwMTSYXCK2eZtcdTAwMTB86yvSti6GuGOjKMujXHUwMDA0mCVcIpXkXGZcdTAwMWXu9N5LgqJcdTAwMTku7PVcdTAwMTIrVVxirqIuXHUwMDE58lx1MDAxYlvgT2FBh6clc1xyklx1MDAwMdLNTkQuv1x1MDAxYehcbjRYfFx1MDAxZEGSNUn2JU5cdTAwMTO8XHUwMDFhnSijZzTM8Vx1MDAwZVx1MDAwZeBcdTAwMGWup4K7xnBcdTAwMDL53cSMiSlCKU7N3rRcdTAwMTRXvq0zanOOuNVIXTrafp9To1xy8nBcIogrlaDHb1x1MDAxZFx1MDAwMNPB0KMyUJy7XHUwMDA1olx0pmqjtVx1MDAwMOOkXHUwMDEyt+J86F/nJDa60NhMIetzXHUwMDAxw1x1MDAwNlx1MDAwMptcdTAwMTa7XHUwMDEyMVeyYadhfSFcdTAwMDBcdTAwMThnIyHnwzZg+oNNYVx1MDAxMXdiTctB0qpcdTAwMGWVl0XN05iSL7fnxMaCslx1MDAwMio2WodoXHUwMDAzXHUwMDA2ztcmMkFcdTAwMDC07GU+PJyRdVJOikF6YvDD7FUyc7k+6us+VKJ3yXk2IYGXomSqUPrr41x1MDAxOayOdbalceunXFxcdTAwMTJsuYdvSVx1MDAwZqMywfBGXCJul7M2XGLNvrWhXHUwMDAxZ1x1MDAxMkLfXHUwMDFlXHRYP5DJ4p5yanWSsO46sMqceHYvwsb3k9nf86Vp4PBcdTAwMDZorP0gXHUwMDBm2+wqR1OvhbilYuBjQHp/RMZNazSeZzEnv1wiXHUwMDE06PPbocH5vX723qSMN7RAtWlE/SdcdTAwMTddQDElO8jRseRW5VxuV0hMoeDaXHRcdTAwMWST4/Qn1OhcZiy4eMyT53jGXHJBSVx1MDAxMYpnx0qFQt7A7dqPdWxcdTAwMTWSXHUwMDBllcXgM5j1fHKP+03iVsyYptEnK64jiJTPXHUwMDFlpeDtay6d5GaXilBHgMLuoGBMXHUwMDEy7YhcbmG41Fx1MDAxY6Y1XHUwMDE3ec+fuHQ8XHUwMDA0XHUwMDE1syhLLq6ANb1gndN58lx1MDAwMlxcPJaDXHJcdTAwMWRrXHUwMDExacmC9Ez2QjB5tc8hZON5l1x1MDAxOWfZo/9SaUFcdTAwMDLjKvBdu1x1MDAxZZn4esUsr5Fd7Ii2U2U/iSCc8COvQUNcYrqPXHUwMDFi6njX3OmvxnhcdTAwMTWu3EtcdTAwMWNI0lx1MDAwNmflZVZcdTAwMDbVfS7eS66k1stlPlxy4oWYYcXhIFwib2+0NHJreY3MuHuw90s799GR89DUPfr8hLVcdTAwMDF+yVx1MDAxNabGs3d7XHUwMDA3VTtojml84Ph2XHSpY173XHUwMDEzXHUwMDFjXHUwMDE4k1ux2NCOvaRfYNXy+Ll8wvdcdTAwMGU6vrc2X9dMQcAwOVU2XHUwMDE5nVx1MDAwZoOOeuR7+HZCXHUwMDAzJfN4nHzcmEqOUVhmXHUwMDFl2nRR6Jc9k31nXHUwMDE5j4/AJ+T1R1x1MDAxZXjMSeRcIqDCsdFKe+VwlZlEwcmDf13agY5cdTAwMWPaUZOnvFxcJ4BfYJYxtNh8kG4vqZfHOU6hYlU5/brSSHHMgZFcdTAwMTD10YdbTDuWUr41NpaQiyY1XHSPkKmUoW1cdTAwMDV9OfL9TTOvXHUwMDFkXHUwMDAxO5ZYzMlcdTAwMGKPmO7E3lx1MDAxZMJUr0V5cIzh0pZ1Kv/rmP70MSVcdTAwMWXdqVx1MDAwM6NcdTAwMTBcdTAwMWPPXHUwMDA3LWVcdTAwMGVAgSFiXHUwMDFk5lxiuJ6J67mFyDRqnP5grlA8qJi1YPI28yy7LDvEXHUwMDFjkmW+6OymPunFj1x0T1x1MDAxNVx1MDAwZb2fLrBcdTAwMDW0ju+vXHUwMDAxKitpSlx1MDAwNW2LkefmJJRU0lx1MDAwZlx1MDAwM9z65/L6kE2SwtD06N+KP/v7hMNcdTAwMTZcdTAwMTlcdTAwMDT882U+bpNcdTAwMTJkp1x1MDAxY9fyl7U8XHUwMDFiXHRcdTAwMDT20reEcMohXHUwMDAyXHUwMDFix2WhuLG50Fx1MDAxObqswkNcdTAwMDDAzNVhM4VyLstNmjzBXHUwMDAwl2VQn4BXu5n69SGVOZys/n1qcH6IXHUwMDEx8lx1MDAwMPZwXHUwMDFl2+GJM1Vits9cdTAwMGVU42SgXHUwMDEyM1x1MDAwMu6PXHUwMDA1YqjDfTNcdTAwMGZjusZrIYklnGzJkX9xZrg+Xz7bXG6n+dRDRPyah6lcdHBkQyRJ9Ig4kzX3pmyPYVFcdTAwMGamP9/n/nYjdLMlce9qRYdbnNVcdTAwMTTs5EMuLpbmKbUvl3pcdTAwMDVcdTAwMDHOKuatPkNywVx1MDAxOeKRLYDl9lin1DyhdZpcdTAwMTStvVpcdTAwMTWLXHUwMDA2PVx1MDAxY6A7QjjJXZ/hXHUwMDAxNENSRMpgkuvdOj0r/Yi5fC+d1Vx1MDAxM1x1MDAwZSxkUtFcdTAwMDG0ZJx6plHhZvC6WzhcdTAwMWJ1NJnt5SEjod7cXHUwMDBmzj2MgsKNqn1/Rpkwzcn1wFx1MDAwNGx6nbHeeFx1MDAxMlx1MDAwNanC8fWm7nTM2P1cdTAwMTHPSFx1MDAxY0opwNBHwGzxg5XsjipiXHUwMDE5XHUwMDAyXHUwMDE0tqpYypJC1nWUvmZHOYCUwlx1MDAxOK5cdTAwMGbEPG0jZJzuXHUwMDE4J1x1MDAxNXzDZYV4iFnR5C1cdTAwMTa7nOdYp1x1MDAxOehhMKPOTiiBTVNcdTAwMDOmeVx1MDAwMVx1MDAwMK1ksVxmjT9F1EIn12I7h+zgQ8zXk1x1MDAwMFx1MDAxMfRdOFLxtFx1MDAxMklaRuf1XHUwMDFl6Vx1MDAxN50/+nFkW4GHojPudpJuXHUwMDE1XHUwMDBmXHUwMDA3XCJcdTAwMGb9XHUwMDE4UF0yJJDGYt7AiU1cdTAwMDZcdTAwMWWuWfJe+j9fSFx1MDAxMlx1MDAxMbNOjNZ0WflcdTAwMTLU5pecolx1MDAxZYDLzLyHXHUwMDFjhaIonKFccjJmN5o22UGtXHUwMDE3RFCF5omcXHUwMDEwJVx1MDAwNsjIcNzE0S50wVwi6jFC6D1cdTAwMDaTJS33fVx1MDAwN63R01qYWY1rdYJJvVLNXHUwMDEyXHUwMDAzXG5x5p3IjkL9QXGeOiY3tbU7JHjZXHUwMDBlePjys9R6b74tqLpcdTAwMWOOXHUwMDBlS8tcciNZ/6TktyNIXHUwMDFmQlP2I1x1MDAxMvPTWXSJ2Oxu3lx1MDAwMI9FJFsjXHUwMDEy0lx1MDAwZv7AXHUwMDFkKnmJIfJcdTAwMWRZO9Mtr4jWpVx1MDAxM1l/bFx1MDAxYktJ+GbUgiWvwCFCc1x1MDAxNtJXYrZlX5tcdTAwMTPxaMRcdTAwMTU6Tcjg2mc279jba09htZg238KuiIDDs01cdTAwMTRcdTAwMTlcdTAwMWKZdcopIGdcdTAwMGZcdTAwMWJUyCC4bFx1MDAxZUpOXHUwMDE4XHUwMDE1yE9S9Vx1MDAwZn2qdC6RzFx1MDAxMYTb3eUu5ExnQTgjc11cdTAwMDbh3HOKj5TGYblcXJEkn5tiSWlnUUTE6ilcYkF0r1x1MDAxZmDAMS6D1PcqXHUwMDFimFkhiVx1MDAxOCwwjFx1MDAwMtdcdTAwMDeLsrOx71SQXeTamUVwXHUwMDExK9lsKyUxmdZMzXSCtlx0XHUwMDA0TZ5cdTAwMTRcbjrpuu20XHTtXamT+VZHnsneWSo6clx1MDAwNnBW1afPtS+VftXZ84NbcEZAaKvj9HK4ZumQyY1EmeZWXGKjyqxcbmhcdTAwMDDLQGK0bpqawU1cdTAwMTOBPlx1MDAxNlx1MDAxM0w47ZlcdTAwMWMxpKZ1p2lBcCCTXHUwMDFi4uFcdTAwMDFcdTAwMDL5XHUwMDAzXHUwMDAwUeL8PGzSS1x1MDAwZWs33Vx1MDAxNFx1MDAwZa1rdlRcdTAwMDTYXHUwMDA0XHUwMDFmsVFNNjGb6niz91x1MDAwZmFub8PmSu6hO/kn0fgjXHUwMDEylFx1MDAxZZnO15ZcdTAwMTVcIvPQrZfZbV1jy7ZcdTAwMDJNzYudgyUwXHUwMDAzRyZcdTAwMWRGn9DPe4/qhlx1MDAxYvDIXHUwMDAxXHUwMDBihddccusgXHUwMDEz6fg1XHUwMDBlN1x1MDAwYl5cdTAwMGW9xbtrfFO1M7iQJDtcdTAwMDWjd49cdTAwMGaBM1xym+M96LvGWaXnNjlv3UvhMsO+XvC3cCBBOntcdTAwMDFHI0aIRm1+1FepWdpcdTAwMWJagqAveFx1MDAxOUpcIiTbKIJcdTAwMDNxhzdK21x1MDAxM4wyiZhjYY6QTn/kf8B4XFyN9m6Wa0yLQH1NXFyjUPZcbsrG++HnrWp11Wvvemmhrrpcblx1MDAxM/RcdTAwMDF3rLpEhz6Wnc2nsimYmu6K0KH2kt9hrUSpWFaT+Hxo/1x1MDAxOFwiQOxxYu6ntG5cYlZcdTAwMDVcdTAwMDEgm49cdTAwMTidz26gXHUwMDAx64Fas3tO9423ulx1MDAxZTly25SvwrR03lx1MDAxMd9jZ1x1MDAxNF4ukvJ+fO945MBcdTAwMWW+TolNXHUwMDA1m22dXG5cdTAwMTHGNSFnXHUwMDBmP3WZ4KvhV/Uh4iijXHUwMDFmTN+JzZJHa/BJeXpLJyBJXHUwMDE512C55TNZPZOEjJ0tI6WTli6iXHUwMDExXG4rSVx1MDAwM2C5SYtcdTAwMDD9/+y9x67jWqOt90BsMKcmc85BpHpipigx56f31Kr/wuGcXHUwMDBi3JZhXHUwMDFiXHUwMDA29tpAoVRanGmMb8xE21xuQVx1MDAxN5KDqTttWjbDx1x1MDAxNDNcdMlAxUluJ0la11x1MDAxM7m//SRcdTAwMGLAkzW5VXqN52JJ6Y1HPM9cdTAwMTNcdTAwMDWS0eh0c0yRjOlQmVxyw0pTI9mi9qHfPuYwiyf06XNcdTAwMGW+qtZcdTAwMWO2I1x1MDAxOVwiNcijelx1MDAxMS4orvbi4Fx1MDAxMdGnRFx1MDAxZnuS3utcdTAwMDVV3lx1MDAwMjiZ9KL5Lje8cFx1MDAwM+868lx1MDAxNVhaiTFcdTAwMGb9TGaZc8rK/Vx1MDAxZbT61qm7xLRcdTAwMTWZXHUwMDAzXHLNTFx1MDAxNsow8/pcdTAwMTjfk20uvfaF56BcdTAwMTGFQ8ndQktRXHUwMDFl3u+KgnFvYcs95Flor1xml4XI/Mhjg/RcdTAwMTPfmnlcdTAwMGYpPfuxXHUwMDAz3DZcdTAwMDBHxEapirC7Z6uIKGe6PFwiXHUwMDFhXaRGbKBcIjCRj1x1MDAxZPuSKz1BXHUwMDAw/nSXVjZaczxfXG5cYqPUXHUwMDE2XHUwMDAzf/tqxVlcdTAwMWNZ+4ZN21x1MDAwNjJg7vPKx4pcdMbKWbMlkfsuw5RFJUSC3lx1MDAxZt36uP2wcbOy8vJcYvlt/khBzvRmXHUwMDBlZ5mKvNi83Vx1MDAxMeZcdTAwMWVcdTAwMTGah1x1MDAxOEclsvoxP45cdTAwMDJ8Q2ZDp3RcdTAwMDJJk/2lbYonULckXHUwMDFh6DUrvlx1MDAxNFx1MDAxZX7VSVx1MDAwZX7t9umUXlx1MDAxMv/aTYk+8zxSntZaxatw8IpcdTAwMWZ/+//GM0SfOtw7nVx0xunN8uajeJVcXGDcfbpcdTAwMTZcctTndyfY6nt9q858XHUwMDA36lx1MDAwN1x1MDAwNN1FuchcdTAwMDCMYElcdTAwMTUxXHUwMDE1wlW989rTOFx1MDAxZFOK/COUSlxiYmfGT397xMzHdFx1MDAwZcMjXHUwMDA2zHDmV1x1MDAwNcLWN7RUOn84sdNcdTAwMDZcdTAwMDCX2HnZpojtXidcdTAwMTBiTDMufjlcdTAwMDNBX+lhJ/OvXHUwMDFjjNGUQlx1MDAwYoZe1PBjRYg8hzjiOvs1XHUwMDAyN2MllKKLj1WBIN18Q3+ZfVxi4Fx1MDAxOPuZWYiIXHUwMDAzaP5swqfe8Gpv+3hG0enjfelwa2LBQ1x1MDAwMJtlg1xyPOvedv0pPZkn6CznXHUwMDBlXHUwMDEwjv/ukyMgsi9r92U6UFxi2OdRkciF1UxcdTAwMTJcdJpN1juZoKe538BL13Tb/F7PIPZS3f1Ko1x1MDAxZsPv28fmVsCralx1MDAxZJlyelx1MDAxNLVcdTAwMDbh4jEk5nBcdTAwMDfPfE53+1xcz3T+lCaahPR9XHUwMDA1dWUlNO9NQYFhzPQwXlx1MDAxOMOEjJcvtdG0QFx1MDAwYrfzOFVIfKF9elwimG+kJFx1MDAwMTxcdTAwMWGR0txRQco+7Vx1MDAwMVxittTgc2vYXHUwMDE0e/K5t+8w5ZDAelx1MDAxZmhcdTAwMTRyRfO0vkV0zuPMniSq+r2rgP6a4y1qtKJ0w83Pnm16467nUntcdTAwMWK9foGEalx1MDAwM5KSOeaLe3+TUnPg7XeNVNvKLIOD/eEkxFx1MDAxM3LhLiTR/SaZm18oXvzszFx1MDAwM25RXFzs2ocxYKC/jPWZXs+JLypcdOCBm5cnMZkmnflS4+DZXHUwMDE2XHLGvcbcXHUwMDAwMkPNV1x1MDAxNVqDvFS9XHUwMDBldmxj4E/iSVx1MDAxM5K3n970XHUwMDExIFVcdTAwMDHykEJAXCLiTP3etzJVjN2AnpOkXHUwMDEwXHUwMDE1IWK61Xm7XHUwMDE0suQj5bswulx1MDAwMHKrqsJV6lx1MDAxNvtP0tin9Xymiv0xWGjSaruF3cNcdTAwMDNS+kJen0yKXHUwMDFknzXC6neeXHUwMDE1xIZh4MosydhTaurjSavPUOD0k933o3vYj9+5XGZLa4tmbuRKolx1MDAwMFp9JO6lbrL60m3AloJS7XUmWkWiWkXpfM0ke7ZPXHUwMDA0Xjw33WGGq8TPuOxmPsdcdTAwMGJcdTAwMWVlY1x1MDAxNFx1MDAxNFx1MDAwMVx1MDAxOCXuM3/WN4mUVqlcdTAwMTZcdTAwMDQtxSixZXPru4pxQec1eiNAJVKoXHUwMDBm49j68HnHd6+nwtFGlcwgrzFcdTAwMTW9WdlcdTAwMTCMVn+cLunXYU5oUZVcYkoqUDA2QLOTnJ1cdTAwMWWgXHUwMDBmd/FcZjhkJoREWibpZb/eXHJMj1fQR51kM/n6/u2DxVgx8zpNei112jxcdTAwMDVcdTAwMTB3XHUwMDE0pjTlLNKSV4BW5oeOqK5ccnTv2cLVZF+dYYJ6KaBy2F4kMWlaoN1PZOZx83tcdTAwMWIx02xX3J/5fi5cdTAwMDKfXZ0uSl7LISCAXHUwMDA0S/qb8zzKaEZhfmIxgihDPWrT/mD3l7LxfsBLXGIgMk1cdTAwMDLtgvnU+jArkaLg3yFcdTAwMDLWKcgq676XyrW67vM0yoH86tN96Ka2/j7MXHUwMDE25Fx1MDAxY/Va11f3UN+k0d2XTrBcdTAwMWK+1sAhY5DH0PqxXHUwMDE2QFx1MDAxNuXEiVxy+enLu/WGoond1Fwi2PFDNGPqtDhL87lALd/D+NZUwGme1Czvr1xyJ4fl7j1iXY+5o5A2W4BcdTAwMWYxXHUwMDAxXap7rUlXaskyeVxuWo1OZDFcdTAwMWbB45ftXHUwMDFkXHUwMDE0Lp45XHUwMDFhgsxfo7T+XHUwMDAx+Ze/YYV81D5cdTAwMDK79Zt5dl5/YP4lOV0xR3lCm6BiqFx1MDAxNeQgUWOFM0Vcblx1MDAwNq7i5GHOSExcdTAwMDM6L/RcIuu2S1x1MDAwNXyl8zKJclx1MDAxNlx1MDAwMDz6XHUwMDEz7rmlv0FflORHL3fxXHUwMDEzzVnWIzQrSlxyQY1gXHUwMDBmXHUwMDFkXHUwMDEySTGQz8SPeXK/XHUwMDBmTVx1MDAwMoDh+i2UL/41K29/23Eh91cmYFx1MDAwM4tcdTAwMDDllzFcdTAwMTnxXHUwMDFlhirpff5cdTAwMTZ051ngrolQsVxyjOjwQVx1MDAxOGCPnVx1MDAxMHlU6UCDgd/x6J/T+PqVjym//Vx1MDAwMKv2XG5cdTAwMDBcIlx0vkCkXHUwMDAzncw+XHUwMDE36c1cYrV2XCJbzjFQhXg69Vx1MDAwM59RWlx1MDAwNma4iu9pxXc8aSN7mVx1MDAxZVp/Vm0pQ99Q/0ZcdTAwMDNcdTAwMTGfNXXLXHUwMDE2pDlaLyVd1UKXfXRFO0pk+1plICdz8lx1MDAxNtxcbqtrlXOfXHUwMDA0O+lcdTAwMTnIfJajipTtXHUwMDFm8Fx1MDAwMLR2lIXue87l+W9spNCXXHUwMDA37ZqN1Vx1MDAwNkaq/Sw5nvNcdTAwMTRcIneV645cdTAwMTJcdTAwMWRcdTAwMDI8jFx1MDAxOGgmQ1x0w0Z5VaVcdTAwMTdguL0lfe3z0YPw8iNvaHHT9bn2U17zTFNcdTAwMDaKz0gjU/tBap+A9Fx1MDAxOSgu9+RcdTAwMTNtL2ByeGxcdTAwMTBcdTAwMWOlbrB0rGC81JpcdTAwMTXIzVeD2lx1MDAwNqE7XHUwMDFmXHUwMDFiX4B/gs/YLNFezI+1J9O2bG3LP0H10CVuuD5cdTAwMGainvtMcGYsqG15ZGvZepvxYDzf0JUz5N79m+9uY4TlzWRcdTAwMDFSXCLU5Plhy1x1MDAwMmGMXHUwMDFkbVx1MDAxM/JVXHUwMDA2XHUwMDFjbia9yCZYXHUwMDAz6olNXHR21kA9gZSvNTU+/TL0QbTZvUChKlx1MDAxZPzwXCJwhDC4VrssXHUwMDFm4JKJWDlyWrhTwUdcciFpT7WD4UTW+nkzj3y2XHUwMDFmOOCap1x1MDAxNIC+s7TijVx1MDAxM++saF3xwCTeWzwubKnMXHUwMDFmbtYn3obkXHUwMDE5XG5cdTAwMTepRTnOKVx1MDAxYZ6cXHUwMDA3w/ctXmNscvBcdTAwMTn7/klcdTAwMTJcdTAwMGVcdTAwMDEhgOlcdTAwMTJQLFx1MDAxMDDSTVx1MDAxNfh8hE9cdTAwMWHTXHUwMDFmi/dcdTAwMDDawD7ob3LGT3JB2VxudrNsxlO4xfSe3ky5ZYZzpElcdTAwMTaiXHUwMDFlmEHhr5FcdTAwMTC9j7jmeNNcdTAwMTmCnr/Z+VBcXDnmZvnLPpYvgprPXHUwMDAyuL/OXFyG/UJcdTAwMGa3/WBafUlqZDaaVD6+ScTMv/WfcvVcbu5cdTAwMGJAdH9cdTAwMTDlXHUwMDE2XHUwMDAxJXhO71xu+CFVXGacgL5sXHUwMDBi5LRki1soXHI2pDFP0cpdPLVGXHUwMDA2ykHh/VZcdTAwMDTPxKjmdVx1MDAwMOjEYFx1MDAxMVx1MDAwYu1cdTAwMWUxZMC5XHUwMDFirXd+37hCauO5k2+ZMb5cdTAwMDLrXHUwMDFkXHUwMDAz+aIn2Vx1MDAwNTmccMGoYyZ7+7GgxFx1MDAxZrnJX1eMM9pAwj132E3UPL/36qrTulx1MDAwYpB0vqDUvfRhVVx1MDAxOJ81X8RcdTAwMTI67qTK2Cp4zemVgFx1MDAwMWBspFx1MDAwNVxuz4rdIXJFT5FMfTdoLO9eOydcIuMk+5dVutxVXHUwMDFiT0gzp28t9Vx1MDAxYfP9jU7oq91aXGYl/Fx1MDAxYqKLXHKtSFVPhyhcdTAwMDBYea9YJlx1MDAxNyCu+X3w2M6S9zyxY4o33nrcor1x5116STuB/rf7XHUwMDBmXGbza6WZP3CuXHUwMDA1Qk5nsqZen23lXHUwMDFmLVx1MDAxNIVD8FpweKFf7Fx1MDAxYVKg7p0yfs7cYpBIJmCNx8m+O72E0/OofK+m4Vx1MDAwMj6txW99QLy/79yDelx1MDAxMI6TfzBcdTAwMTBEaG+X7lwiPOJLo0aF+t2r0sJcdTAwMTNAXHUwMDE55uL1rW1heqJ3aFx1MDAxZYNcdTAwMDZQXHUwMDA2fVx1MDAxOUsk6fSc1INWJNhcdTAwMWN78knCz5V1YFjf6GInivvJwMLJ5ElDgDSfrEiIXCLTq2Bev6ztTsEvI1Tx97d8+Y4tilx1MDAxZeXjIb3+li8jQ7BY1Fx1MDAwNH/eZjqeXHUwMDA2gSg25Fx1MDAwNWxuXHUwMDE1U5evXfNjUGL1hbHYYo/l3Si/c1JWR2P/62ubdFx1MDAwNMK3Vzyr6fZrtektZtT0+z2yyUvm+Vx1MDAxN5ueM9RFoPzTpn6xmFv0XHUwMDFij1x1MDAxNjDeyf031/v/uXXYZyRcdTAwMWO9W1x1MDAwNFx1MDAwN+hcdTAwMTff31x1MDAxZFx1MDAxZuFn8uP5r0+l2m/+XHUwMDFjekyozb3k+KZbICqeN2/qhMTpqIl3svugj+3T4zd/3n+m869Ohd/9If+zOkVQy1NP0upCVVxcMVEmy2RcdTAwMTVn9tWAYVxy8muQoIJ+wNVJ4vR68aa1XGaRwXHC39Jofv+Vr+1OLlFntEU0IKSuT4J0XHUwMDFhXHUwMDBiVKF8/rNcdTAwMDYseT1cdTAwMDG9fSDaQGJzf7L8Z7494upsJ5TndG5d6yFcdTAwMGalk09cdTAwMDBPyJtRSF1cdTAwMWJ211x1MDAxOJkg6CZcdTAwMDVFnGj/MjzptH5nv9z6Pu1woJTmpy2CXCLyn1x1MDAwNfv1Y25Ev9NqcU/1m+hcdTAwMTmlZmmvWZ77udZk/rzZXHUwMDAzZNP0qIAgbFx0NKLOeCNeOpFcZlSZhonZfqPBklx0NS3IP09d0n5rqNJvXHI1WyHonYucx3kyn0fYplZ7UnsqT+1cdTAwMTlcdTAwMTeP91dcdTAwMTKZXHUwMDFljfCMJaVzOJ5yXGLLJmhcdTAwMTfx66DQi2NB3ZxEl3RAeSt8pcvgjTTUXHUwMDEyuos2xMY9J5qy9b/vuIlHXG5BuvyxYjX30NAzvDQnpyXgXHUwMDE0X1+xxzZimH5cdTAwMDFcdTAwMDe8IeBcdTAwMWHZ+7NVbs+47n0wVFx1MDAxZpCoXHUwMDAz/nvQJWpIqIXKMjeJq/+3lnpcdTAwMTNeRkNkJFx1MDAxYkmFXHUwMDFiXHUwMDBinjUnXHUwMDA391x1MDAxN5k50/KFK3dccpbH01x1MDAxMrR3V6vJ+9FcdTAwMDMtYrNvvagpN2lLKXnaZlxuh8V5XHUwMDEyXHUwMDFmgzZcdTAwMDahXHUwMDAw0zYtXHUwMDEx6jpxXCLj/Vx1MDAwNH3ddobUTCqzXHUwMDFmOKKE0+W37pFcdTAwMTZcdFxuwdXuXadhPmBuXHKPLYTubP78Wy5VX1uTXHUwMDE1XHUwMDEwpFag2emcTFx1MDAwMFx1MDAxYlx1MDAxNY+Sdz3lzKHCQVx1MDAwMdOg1HR2OufFxvpcYlx1MDAwYsRB9FnkXHUwMDAxaiz7W316ROFcdTAwMDBcXIpfI6jsxkIySaW9X3mhrNhgXHUwMDEwtEBT3+v3K0FjuIlcdTAwMDBwdq1cdTAwMGJcdTAwMWP1UyBAXHUwMDEzqVx1MDAxYVx1MDAwNM9cdTAwMDCM11x0QciMXHUwMDA3ZUDGXHUwMDE3bNtyVlx1MDAxZWDMXHUwMDAzoPhNzC5cdTAwMTXeSa8sKoov4KQyWqnpQT9Sf7jM0tC+95mlxm/uXHUwMDFinrVe41x0XHUwMDEwN25CS6pccpcmkZitUGlcdTAwMDaD8ZynnkTaqcJhVdK543tbXCK3OkmMVamFuuP/1ob536ZcdTAwMTV5pqA9733r89twIV9w1aByez7Rv3Xtt8JDJiyeXHUwMDExNWIqdrCx4TknN57j8TVQnVx1MDAxYqUvPj6oXjq29j20WsQ/XHUwMDA1YmuDUllcdTAwMWH8o1x1MDAxZuwlo3DwXHUwMDEwijx5ppukLpFcdTAwMTP99lwibCfW1rKYxEQnR1x1MDAxNYCbmyGqXG5k+JmbuXWXt4fxQIWoLlpVXCKRaElcdTAwMDH4+ThcdTAwMGJX5FJcZlx1MDAwM3/cXHUwMDFkpP/mklx1MDAxMuCh31x1MDAwYpVW/zcnZnb569+4rV1BxFRcdDyr2lx1MDAxYbqtmS1cdTAwMDep9K/e/HCy7fz5N/9aXqjDXHUwMDFjXGLQtL1mc9SzXHUwMDAwpye6NWDz/3Ht0NKw+f9Ba4D/t6+rhiOoXGLPUsU9XHUwMDFjrFeWXHUwMDFj6TeaXHSmdKNpP9CCxe1ccstKt1x1MDAxMKcxXGZcdTAwMTNM1Kb1XHRLy1x1MDAxZNFA/F+5+lt8i96GuyT146dL6eYq71x1MDAxMJ0j/rKT3948/cj/u7tcdTAwMTDF332CKpfKPtNxvlb/n3+MunZcdTAwMDBG/L/g3kfwd7xcdTAwMTP91/LpXHUwMDBld/zuU2x+u/H/S/nAz++uxfq/lk/jXHUwMDE09bdcdTAwMTfT0S5DJP+vd1x1MDAxZYJcdTAwMWbwdzlH/ZfycVxcR4Tg48dcdTAwMTdcZjn9v6k48HdcdTAwMDBcdTAwMTL+a/mkN5f+2omj3sRg/Tfl+2tD8f9vw//FNtT0d3/9rmPluCDy+fh/1qaP1q4loveCatSkLtC5zoOxityO0MSvmKsk6HFWbFx1MDAxZM20XHUwMDAyX1x1MDAwZVx1MDAwNJdmtYpPh1x1MDAxZPK/X7RKPmdcdTAwMDM2p4tcdTAwMDFcdTAwMTX5+vCk9Z2i7P94MKaR+ChcdTAwMTZC4VhMXGKCrZyoYFx1MDAxNn9411x1MDAxNJKCXHUwMDE2iVxm7Fx1MDAxZb7Ec1xcXHUwMDA1q6qLiORcdTAwMDYw7Vx1MDAwM40rXHUwMDEy/Oe+SilcdTAwMTJcdTAwMWFviFx1MDAwZsBcdTAwMWbWeU9A9k30tpb/vW74lluBXHUwMDA1MiWWxdtZXHUwMDE1XHUwMDBiRtvfW4GBV0W7N1xmkCveen9cdTAwMGa39ci0uoVil3w38E3qMZRUZ1XJuS95XHUwMDA0vPe9ep9cdTAwMTQrvcnX/nnDXHUwMDFiq0JuXHT40F7bXHUwMDEycM+PK6LK/r5j37rr1H4sXG5cdTAwMDE9klx1MDAwNH1iXHUwMDBl2vynvOHyuyeUcI7/tI1+XGKpMkzCXHUwMDFiPFSeKHexnVZqU7jaXHUwMDFj9WMuevNzXHUwMDBm6Fx1MDAwNZixyezvZcirU1x0mHpcZpG5YVx1MDAwN1+BMiRpoPRcdTAwMWSIXHUwMDA1QiOERLvruDhcIm1cIuGVqn2CJXnM/tCGz1x1MDAxNaN0p8/mfEhE/TZ4X2pcdTAwMDV+lSiOa421JEVWP/6zz9erXZFxf9s0ztdcdTAwMTZcdTAwMWKzXGbBYlx1MDAwN5WuLFM6S08tXdFHrqBcdTAwMWazYtdcdTAwMDeIXSPBmFRM3UhcdTAwMDKzIHe1LEZcdTAwMTeYmtwjQpShXHUwMDE2WYBug1x1MDAxOZbCrlx1MDAwNCFcdTAwMTT3p9jux/zHbXRcZjGL8+8+WO7x2/v0siST5qx/7apIQroobMpcdTAwMTRcdTAwMTjsZjMwXHSQZ1x1MDAxZqWLQ9vbetaW0OxMsWEvXCJ/3PHK0WIz7OH5271cblD65tsmMZfE7cXOV/1rLe5xwVV6xGmCTFx1MDAxMmhcdTAwMDE4S6XN32BcdFx1MDAwM+uQjNZccpcqpYq/cahcdTAwMWa1plx1MDAxMZJcdTAwMTlcdTAwMDNcdTAwMTbg9u/NgHbFYfXRjtVeXHUwMDFiVFFuitggtPaonF6ikM5cdTAwMWNjXHUwMDE1QG7rJOZcdTAwMDdScVx1MDAxY4fFusLpT3o6aVt+klx1MDAxOacoe8NVcsJfhqLp/7lnOFCY31x1MDAwNTzBl1x1MDAwMVxm2PzrXHUwMDBm3Vwifi4yP7NiTCAlXHUwMDE5o6EmeFT59Vx1MDAwMeX+YNTwLd2+beJtXHUwMDBlQjNsvCQhuMvOechnuapcdTAwMGU18VLMXHUwMDFkcVx1MDAxOdiTOigkavxIutchWs6l7cHnyV31qjlcdTAwMDFvYlx1MDAxZTe2d3vw0oeJK7Lj+5DkpH6x9fa2ZYwtm4dZoZOkR9uDsF/rl5lgMmcrTmFcXFVrSzdZRuWJpv/Zf8/+RC1cdTAwMTCgXHUwMDEyjFx1MDAxZP3XuVx1MDAwZqlcdTAwMTNcdTAwMDBrMXmpSaaMXbz0ekeZ9EiM5VHu1VbfoE92elx1MDAxN6m63j1cdTAwMThxfDte+Mo5XHUwMDA0+Wjj4nCVlX7CuMtuT1x1MDAxYybAgFx1MDAxZsLqPt5cdTAwMTNcdTAwMTl97i0zXG61xsv5Yp+W/iBcdTAwMDVaXHUwMDFhu/s5xFx1MDAxZZE7XHUwMDFly0Lw04+W4PjTTMLpfo/G4LmVq8c//1x0yt9cdTAwMGXJioCmbfyKn1lvXHUwMDAx5UPqTFx1MDAxMZvTh3NDZ7k69OFodeRrRdWCXHUwMDE1i1pcdTAwMTjkXHUwMDA1q5nxWed1j1x1MDAwNvDb97Ob45/4IVx1MDAwNPP7KYRYpaKs/UiZhyFHc12UXHUwMDAwTipRpyTYW+NcdTAwMTXnP1x1MDAxN0jqzNzVvzt2o9+e+PGI9kX7e6Zvx9WS74knXGa/T8ZKXHUwMDE0IV9Y4bfOgVx1MDAxOIWbXHUwMDFjJ0FhOJqee1x1MDAxN5CC/bpEeeZ0q0TtZlx1MDAxNT5L1E5STlZTul1Si7Y32lx1MDAxY330klx1MDAwNT+5TzJcYqNJlt5N9Hy+//p7gPzuXHUwMDFh9dZcdTAwMWRKy386rMmR1nJ75TY1Kn1BtupiL+I5RVRrkztKtkaupzX1gJqUXjKth+VcdTAwMTLyV3zNz7uMTVx1MDAwNzEvXHUwMDAwU+5on6TTrHrRvGjJZ+D9k5JW62tkTJ3K81+fXHUwMDEwI+mPWcn48eeLXHUwMDAyb0uN+PyINENw1uf9+iafsypLw8LxhiGD+lx1MDAxYsRTntbPpuhcdTAwMTRcdTAwMWKqsZBBkFx1MDAxN7fc0zrqadwhX1XPYJLYLd3KXHUwMDFh3D0nXHUwMDEwbVx1MDAxMokkIddn2HKPT8yLyen9TNa/epdcdTAwMTlQ7+KIwjnoKL967yWp4XtIqdzv8Fx1MDAwMX1cIp3Y+1x1MDAxZE1cdTAwMWYki6hcdTAwMDJcdTAwMDR3IKFTI1x1MDAwNlx1MDAxOWFiVa3qjtW+81ximUSHgeS0dFm1YYgv9DWZgWvTMDKLTY9uOGskQ1x1MDAxNt71t36On39cdTAwMWGT/7jm7zys7/3p20tXuW/h9Nbj7WF3w0BcInDiwzqz/ZB2l6Zg1idTjDCax5m/+NRcdTAwMTFcbmN8jVxuSMyI4ZSBeVx1MDAwNLblqLvTWsBcdTAwMWNpmkhcdTAwMWKeYt0zxy1szTpcdTAwMDf5d0e1vlx1MDAwMVx1MDAwZvx3Nv0/d1x1MDAxZv/8mFx1MDAxNkdcdTAwMDaCXHUwMDEwPFx1MDAxOHnkqXjd9Hmt7drB9yfpx29cdTAwMTImy9u3LlVuXHUwMDE126Ay8C+27szTYVx1MDAxZlx1MDAxN0jzpMtcdTAwMDJcdHTNNzejXHUwMDEyx7NcdTAwMTK3YDlMXHUwMDE5rpyXuvb+3Vx1MDAwYoVM91xmcs8/jVx1MDAxM7/gwcqdYFx1MDAxMFx1MDAxY8JMYKO4keXDXHUwMDFi75dKQ15cZlx03/Y1pWJnOHDdXHUwMDA0t7M9XG6GXHUwMDE1XtBBXHUwMDEzXHUwMDAzzND+54LTKGi4Ol16m3mRwejwqk+6SSipfFxuvEP4z13ioZt//o35VKu4a4xmxqLq2lx1MDAxN3FcdTAwMDdcbu3vwd7kQt7u9lx1MDAxOFx1MDAwMst6sd6j+1xuSdfXNS/5+8l++GqWu4RcdTAwMGJcdTAwMTlYzahKXHUwMDFlmsRcdTAwMTko6enUaqCSnX/81edY19xvXHUwMDFm9SHdq/53b/hChJKbU5Vr6i/3OFxifJdcdTAwMWau9VtcdTAwMGZcdTAwMTNF2vGcxP1++C05Lllccomi63VtsjzqMiUlIPVcdTAwMTD02Xhq36RcdTAwMGIxhEvQlNVYXZfC3lx1MDAwML6qipr7UOtqoH47ydm6++OxIHA5LpaY/n2+/jGE1lx1MDAxZN5vT6KG4jt6OsBmKTGhYNU/KdCf7OYgXHUwMDBm41x1MDAxM6YlPylcblx1MDAwN+mD8SyB0Go3XHUwMDE4/NqnPVdvnVx1MDAwZf6Z79o1Mpeeh1lVYma7PzLYVvs5qpnqot5cdMbAldhhQK5xXHUwMDE3aUr1MILH93NHjHRmuiZcdTAwMDWW+P6xY2leJmBUwJcqXHUwMDEyblwiYLN9QObu/TvCM2xcdTAwMDd261xirlx1MDAxZUhm95pcdTAwMTDcYUc5TOOnjCw5iqSW5WR+mKJ2c/htXHUwMDE5LHTLXHUwMDBmoIGK8IrNcpJfcYhN8aFM8Vx1MDAxZLVcdEtq5DneUYm9dfz217QkrEtT1GKm0zVDLLUhZe/B6IdKPVty+jz+h1x1MDAwNnU/XHJwmIe9/mH3yiH1U/AkNZyx5uuqb1T4rakuVZDgqFx1MDAwMIBXiVj0XHUwMDBiXHSd5/DRlUVfUiSfM073/fth1OWJ2KijqkHjWH1cbsyJzNpcdTAwMGaQNlF558ggfUQliefXY1OJXY9V+lx1MDAwZeL8hVJIimTlJfbQUzryvOdns1Gjplx1MDAxY1x1MDAwZWn0lLi1pfyPXe9/88qOim3hX65QUp2221MtXHUwMDAwY0Vx+uCbXHSlKPFcZpnde1x1MDAwMelcdTAwMDPVlED2pamDpNmss3lcYo5mN3xoPMvT12lcdTAwMWVhurKQ1FHlO1xyg9f7TkhcYllcdTAwMDeUXHUwMDFkVDebnpJ8vKPGOmWz2lx1MDAxNVvY2oPuzbPxd4pwPy1NhFA4vGpcdTAwMGWyXHUwMDBlcXyGpZ/e/3JYJ3q8MLSdUkn6X512zc2r2YZr0paEw1t7XHUwMDFmuVx1MDAxYa7YUTP3k1Ez7F4/XHUwMDA0ylx0Y15cYlZPhcs+vCY32dNcdTAwMWLBskpVtlx1MDAwZVx07C+kXHUwMDFmo9PrU5w8o5VOi6Z2i1x0LWsgc320XHUwMDE3+H5EXHUwMDFhjCXeZWDGJ8POXHI2h5kxyfTtQNytXHUwMDE2XHUwMDAyjni8Xlxm0p9cdTAwMTBx3C9cdTAwMDNpf5rEXHUwMDAwrW1cdTAwMGWi8NZcdTAwMDWbn0HR/NagMFx1MDAxNVhr71GJNypUMjqNiP5Mrp9NSpmOj9ln9IuIdnI5cGJcdTAwMTf9l/FQXHUwMDFib3lZSuDJgCuFtMQ/6CdcdTAwMWalJ8lqTIL1ue/TSou8hFLEyedcdTAwMGKIga7Ni+1TXHUwMDAxeoplOlwir/dXyX7tXHUwMDFj17/DXHUwMDBlQP3+3UkvXG7AjalcdTAwMTJcdTAwMTB/x3eJPzBProZg/MyvrugxYNF73Wsoqm9cdTAwMWVkoWCQ3GdcdTAwMTbu+uMulfrz5HdcdTAwMDNcdTAwMWGt6jhiekBCpFx1MDAwNYQmSTxcYlx1MDAwZdt4UN2BfN9cdTAwMTV6ROx9QPLH0pxI9KymTFx1MDAxOZUw4nPZjvJ3Vk7/3W3t8lx1MDAxZI3rfzmKSmeOjjdcdTAwMWMx/VFiL1wi91GxqWlQPVJcdTAwMWKgmtHCUq8p4zB9n8/gq7pcdTAwMTFcdTAwMWGup/CpQlx1MDAxZPmmRfzKw1xc7ESo4l62Ulx1MDAxYfJ8YuWYz75+qlx1MDAwMpJcdTAwMTmw2kS7T9mmLtNcdTAwMGUkppCNPkV0XHUwMDA2WFx1MDAwMY9pclOJhlCn9Hs/XHUwMDAz/3s/g1x1MDAxOFxySJ3+y1xiosGPXGJUXHL991x1MDAxZd9cckq1IcWeXHUwMDEwZ16FXHUwMDFkflx1MDAxNTfUhTf44Fx1MDAxNXZnXHUwMDAzZ35B5+czQ658WsrJLtOijbXPLopYdCMv1GNviOE3fNdcdTAwMTXkTq9spVxmUFx1MDAxYnhVqSTFXHUwMDE3bOc+Vi3pYUGqn7hN7HZX/41RmVx1MDAxN3Lu+nt/hHfWX02hXkCAv9ZcZupcdTAwMGKMqt5cdTAwMWNrXHUwMDE0YvLofP82XHUwMDE5XHUwMDEyLy7vXGKTQMI8o2mfoOl0OVx0XHUwMDAxjI1vXHS5LzuT+NOCbUo+vKoqdVxc3XZcdTAwMDF6rbfb9jhrf0VcdTAwMTO4XHUwMDE5XHUwMDBls/AtXHUwMDEzamBzT0aunVBcbktcdTAwMTNYlSb/Ml1cdTAwMDFcdTAwMDYg12qFM6KiXHUwMDBlMj8jXHUwMDFjwkHk9pmdZE7G0Zww64Okrkzar1x1MDAxY0gvXHUwMDFjqLLpWosjUc5zsb8ufjDn4Jky5TbUZTeYwlW/OyhcdTAwMDWf3Slmn3vl+FwiJYiUky2u2Fv6anxVwcaJ17hvezxTTYaWXHUwMDFikfjSXHUwMDE3wHe/9fpBXHUwMDBi6+FcdTAwMWZcdTAwMGJYllTtJ0jMRMZcdTAwMDTEb68jyFhAJpDtYVpd9a5cdTAwMGUrX72sU8yHPrObM9V+5vnfm6O50WBaxr3IN6/tXG4ssraIXHUwMDAwL2hwXGZqYChEOO54qVx1MDAxYWoravjaLYpcdTAwMWaGsMZr2ZY2XHUwMDBicf9cdTAwMGWxWrxVc4ZgXHUwMDEx//RcdTAwMDBpW35PdmyQMdpeJueAMvtcdTAwMDFyVmz03Vx1MDAxYvEhvlx1MDAwYiHxXHUwMDE21lx1MDAwMKeLQs7eNLmXijhcdTAwMDZvXHUwMDA2zbSvdFx1MDAwNGpcdTAwMDNvXHUwMDFjXFyq5Vx1MDAwNUGXXHUwMDA1KSVcdTAwMDBcIqyQXHUwMDA2xdNN7Fx1MDAwNMKOwvD9XHUwMDFln31sg2CsXHUwMDBi0tknm3z8uDdcdTAwMWEsjo/8KVLK7D/zN11cdTAwMWRuePZcdTAwMGKJxURUMMDL9sxcdTAwMWI1W7G2qoa5rs1pcEe9jq9cdTAwMTeUvb87s9pepeLEq31cdTAwMTXus1xiRlx1MDAwMIukhdxaXHUwMDE3uFx1MDAwNlx1MDAxOflt+vWRyo4qXGYvlOG79t2z152Zz9C5VEnBbWOgJ3iTY3/6bVx1MDAxMnokXHRdXHUwMDA2szf7ey/KXHUwMDE3eI0tXHUwMDBmvlx1MDAwZfQx43dcdTAwMWElrPKbXHUwMDFmJ1BcdTAwMTRcdTAwMTZw0MqAXHUwMDEw1lx1MDAxY5pRvnpSzjrmXHUwMDEwtsS1XHUwMDE1XHUwMDFhJihVRMqmuEhRbq9IK1xyslx1MDAwNN1cdTAwMDHXfD2Rs+KJK+V6QSlcdTAwMTNYXHUwMDE5fFx1MDAxZq8xJ7xcIjFcXF/ePVd72EQ2J4UnftvoL4arX7VVhd/U2C8oXHUwMDE0NW6/XHUwMDAxWSXGU1x1MDAwZea4Rmd0otRcdTAwMGZaXeb5XGJ/Klx08pqRY25cdTAwMTJccpX8LrK9KlvRXHUwMDBmWfWw2erYzyf+fT8wNz6lbMehSS4+15uAPIW2odVfNFx1MDAxMaWHYlG4XodOa1x1MDAxZFx1MDAxZXr16yeudnDFxTG3IeVi/fPgPMRMm7bZXHTLUpw7JHHBZyuBKvNcdTAwMTPCXHUwMDE091x1MDAwZVx1MDAxOTF6MN/9JGdMXGJcdTAwMGa4PLk3T4T8jDrHwMCQobMvaC24uFx1MDAwMKJ+mZx6o2HYXG4veKxZXHUwMDFm8lx1MDAxMadvT8nRwOdhjrvYxJaQ7D/vlKmpmvw3b1jXs895XHUwMDFmJpJR6mpw0WMzmFx1MDAxMFxu5+1Z1aewT0TSPlx1MDAwM5+7SGvD31wioFx1MDAxN7TaYVPEXHUwMDE4u001iTBcbtL9vsvTjJKb6K2IwzLoXHUwMDEykVj0viHMq/tdlmfq9lx1MDAwMtxunFx1MDAxYaDIRUYsQN5AXHUwMDEybt6sxSHuk0X5eb+x6VxyXHUwMDE3SodcdTAwMTNez6DVXHUwMDEwYaPHZYUgiLFOXHUwMDBlNuOpS/hcZifIi7xK6HfRlfbiVI0k7epzP1x1MDAwMt1cdTAwMTXVpVx1MDAxNlx1MDAxOCeo81x1MDAwNGh6cGbKRjHNVcx6XHUwMDA1iURcdTAwMDDi3usrQVdcdTAwMTNcXFx1MDAxMWJuifScNVTjXHUwMDBmJVx1MDAxN1x1MDAwN2tLKsdZ/r2LxtD/JlVdXHUwMDBmIJ55XHUwMDE4nFx1MDAwMfja7lxmXHUwMDEzuIOvbUk2SWA47cTGZDYsXHUwMDEyzMNiqv3YXHUwMDAxQpPDflx1MDAxNpWGaVx1MDAxNley0N6TXHUwMDFhumGZ1b7sx76g13nCI7G+7jQy4yHy0yWugeRcdTAwMDZ8XFxi9HP101wiwVq7v8c7XW9G+oZWz4L091x1MDAwMrl3Rlx1MDAxNONcdTAwMDFSM1x1MDAxYzH7O1x1MDAxZKwwslx1MDAxNd9cdTAwMTJVn8jhz1l7ieWgdfF4oFx1MDAwNlEksMrjfIdcdTAwMTHASLPiXHUwMDAx7bd0R/j0rPmjXHUwMDE3slb/7Z/7XCLCrfx4v8g9kHbc48csNE+jwI7d/khPgihiL1x1MDAxMPZA16Xfwd5GiDTPungu4S9myW6SgT20SZXfeW9cIjtcdTAwMTiV/8SM/mCq5PZgXHUwMDAw5zHAsVx1MDAwNvjD53j+zoJ4uVwi8IG8bLOhWcVriXz0t1gpXHUwMDEzS2xcIuZofzFcdTAwMGbXkYJcdTAwMTXynigzc15cIvNcdTAwMTLys1A0zldcdO9cclx1MDAwMrCK5otlyNWrXGLMXHUwMDE2Six2WsNcdTAwMDfBkIXGvnZGUtGp/siNclx1MDAxMipp4iPTVYKy/Zs/55E/X1XaPy1yaruJMfNcdTAwMWO1+1x0zKJlS7iS6DbQ6kdofLhebptYXFxcdTAwMDCzR68zzX1vftSXLDRFXHUwMDAwv5FcdTAwMDE8fVBcdTAwMTl3SJWX8/DPeevhXf3SxT7EmY3BLFx1MDAwYkdcdTAwMDBcdTAwMWVcYrpluXDIXHUwMDFhksEg8Jn+2jUwXHUwMDE2nlP9yNzvZEM1V90sXHUwMDFjJ/0r/1K6XHUwMDE2iFx1MDAxMi/q5lui66hunE0r8uiLafHz+FhigsDI+ew0Kd8yL4Wtl1xutOGdXHUwMDA0yGs2KlxijtxcbvZh/Vx1MDAwMKOjXHUwMDFm31x1MDAwNDJcdTAwMDBcdTAwMDaIe/nF71/lzub/zFuvdeN5Kl/+eydTJ4CeXFxcdTAwMWPDXHUwMDEwX7TMQKGRO3i+rVx1MDAwYkQykM/D1WBHXHUwMDA2W7rgX6CIXHTBQq9cdTAwMDWjZ3F7teE5XHUwMDAwOf53ddPX7ThLzZ3714t5XHJPMWz21+a3N4iynT6bdKGJQdWcXHUwMDE36Y1TbDDGXHUwMDFlU25N5Fx1MDAwZUE9XHUwMDEy5j2+gNTmMC9oL+tcIlx1MDAxYcj3IyhcdTAwMTm7lyVaXG7AS1W/WetcdTAwMTXrtSf7r7W1acL/zc9AXHUwMDBixuSO3tkq+SSVha7lZVx1MDAxMvdm46dgMbdOfP+07eXtXFzzb49cdTAwMTBAQn1cdTAwMTQ6XHUwMDAxhmnaxGmW9fNP7r67K1x1MDAxNj40pjVmSrhgbJbcgmU2Uo3IN1x1MDAwZYro8IlSTfdcdTAwMWWh0eks3iBzoZDJ10dhVLDZm4RQ7snUnNjCapLvXHUwMDA03eaqXHUwMDEz27KvMYacMLxvnoHMXbdbW929RN+E95ON33zp6W1aQPd8wY9ZW7lcdTAwMGVJIUiJXHUwMDFixfUp9DxxjH7LXCKKUr6hM49cdTAwMDe74CenXHUwMDA3Ty1cdTAwMTQlXHUwMDBlYzW3c3OcrFx1MDAwYkn4vVx1MDAxYm1rXHUwMDBijnuqgMI5vtNE7HdcdTAwMTiGPVDGxlx1MDAxMUpuyVxcUFUoYby0lfbQ6cOlcbM1QSWhzeJ0mIx2UVSxbG/o5Sug4J2QP1x1MDAxY0Qwx1xyfVxyKN5ft4vNcfghjfvfXHUwMDFkXHUwMDEw5HK9XGaF68iSa39nknSFfp68L6Kv3ZxcdTAwMTjlXHUwMDEzXHUwMDBiaSBThbDMzzLI+oSQRJaxllx1MDAxMaVcdTAwMTIrW+GQXHUwMDFmXq7/ME1OvLBcZnv06vN+1Fx1MDAxOSVOivK1fv5cdTAwMDP7IJtKXHLsbqVcdTAwMTZEwFx1MDAxN1x1MDAxYzrfUmeMruebyFx1MDAwMYtq69k9+lx1MDAwMS1cborQXHUwMDEwvGzXZ2ef4VPiXHUwMDFlJNNWXFyipZqIqKRcdTAwMGJDnn1cdTAwMTPUsWp1KnY8XHUwMDAzMF/wW3XxkyVrYCbJSFx1MDAxNFjtw2yAtqfbY2q6xjc+ldtcdTAwMTmKqCOGPaEvXG5eRsGPXHUwMDFm83N5XHUwMDAzkWK/ooOr4HdcdTAwMWKfO1x1MDAxYdisQiFIRDyEc7z5eu3qiEZ97ti7yvq7PNgqTLagpyiFhqTTqszP5tXkiLdm6Drp25ObuJ7K7sr5z3qBXHUwMDFm/ObytCGpXHUwMDFmXHUwMDFh8ruzhOVak0ayqlxuLVx1MDAxYsdcdTAwMTE0WVr5c5NEUVx1MDAxZrnrPSFcYo6P4KPcW1x1MDAxOXp1clx1MDAxNFZ8iOEzf19+1H5cdTAwMDVCXHUwMDFjXHUwMDFkTaBccr/Ji45PlNnYXHUwMDA0gYublzdcdTAwMGJR/ZF4LJSu9TFbgkXTsPrbxyFnK9C8kC8hyI1cdTAwMWTyXoS8+ztcdTAwMDf52644XHUwMDAxaEvMdEnsZNmskTTeTaddd/TmnLiX5DrHM9hWPv7hXc/Cb2407XbT5pvo+XlEJlx1MDAwYlqPUJtJJjOWXHUwMDFjcKLAXHUwMDBlNV7WvFx1MDAwYmRCjFx1MDAxZq2v/+6UsbWZ8yaNdUbyPS6SLfHcKK1cdTAwMDWzu67y20vl0OWebvUgXGKMhddcdTAwMWFcdTAwMGZEZDT8V5xVXHUwMDEyZ7xCzzqgy3hcdTAwMTadYmBcdTAwMWPcJHjw/O3DMbxcIkvqwI+PkmSl6Vx0rd6drcNcdTAwMTerf/OwmGl9TXlcdTAwMDDj+1mP0/qgXHUwMDEz6Xf9wKKG44y9/eWbXFzQt39xoJ3DmCjcXHK9uK/15Tdxzlx1MDAxONufXHTC/ezMK1xybecoPlx1MDAwZZBcdTAwMDVBxVx1MDAxMcU3eJ/DYGbL/Vx1MDAxMlrbvLZcdTAwMTnes+YtU50lXHUwMDA0rNtJ/9g4YopcdTAwMWJcdTAwMTNcdTAwMDJcdTAwMGarg98+JOV3Vtfoc7xcdTAwMTVbXHUwMDFlhn8vK7spQnI/2ruK2/2QLFx1MDAwN5NV05UrTXlVXHUwMDEyXHUwMDAyXHUwMDE4qsM/vKW+P6G8aZ9keez4K48thMpsS+2NmPeGSFx1MDAxMklcdTAwMWFxVHGSz+HIXGZ6cZ0k28IyZyWbXHUwMDE3/ajp8JZONZ/f+HFC2bzyXHRZeLqMVMI2SY34oqRcbv+N61x1MDAxZT3gNPUh+klcdTAwMGVMaFx1MDAxNn3aiFx1MDAwMsj5XHTUlvuCXHUwMDA0f3M2hPYrl3iz90dcYmrF01pDnVx1MDAxOchViTTPcEnNt1Zccvruetr+8lx0xuL36sfD+ZpP8f3p9Cur84O3UdqcuWtJV6xcdTAwMTJ9he9cdTAwMTJzv96qXHUwMDFkvVx1MDAxY4xg/q3zUCzO/DJcIqZvv2l2zFXD5TNjWe216kCXSUKs9qL14r63+GhcdTAwMGaUhHNcXIFRuorrvpxcZlx1MDAxOE1WfVq7+4CILjxTaTF20CFnj1x1MDAxY/AkI2yj31x1MDAwYrKDo7Sv/DL45+ebhvfJaGXXdVFcdTAwMDE6JzWOsHJcdTAwMGa4XHUwMDA1idFcZsZb3FxcKUTMeLbxWNFjSFFNJIqWU/BManx3XGL+4OV9XHUwMDA0YcithXBEokZrNeV+XHUwMDBl6LG+VoeU5lx1MDAxOFx1MDAwM8yVPHPsiZ6b1bp12rxE/cRcdTAwMTT5PbliQz5qUanOr1x1MDAxM7cow1RcdTAwMGVj2Vx1MDAxMc2UwSi20Vx1MDAxY4z8h7BOX/G7XHUwMDFiM8lnUqUvLv/Mb2dekes/78akXHUwMDAw4alHXHUwMDA10lHXOYN0SrRcYrQstJzeNMkwXHUwMDFhW+ONXHUwMDE4oVx1MDAxZZHcXHUwMDA3MHNcdTAwMGZdr6o+b1x1MDAxNk9YXFx9hcWXzNxcdTAwMDTmeZwum4RLv9e3rMQl8YiHXHUwMDEznlHpwPVcdTAwMTiwTtOhpSrSbe7JL1ScvWe5qdyo9DBLXGaOwuZ6TDO/RTQl3S/A51x1MDAwNH37XHUwMDE1XHUwMDAzsVx1MDAxZHC7RdwtQfxEM8r+vaty55eff/dcdTAwMWJavVx1MDAwN0KKRpxcdTAwMWKQ0kXMXHUwMDAyZOK72OroaWGZhLJcdTAwMGXk/c5HPfH9XHUwMDAzdcrTcfCfNnEvvyB8LvGVIFJcdTAwMWJcdTAwMTaiXHUwMDA2V/SRM2WnxflmOlpDXHUwMDFlyuk8cFnUrGpBipG14advd1eWXnifXHUwMDA121xm9X4jbZRNnYw/1/fwdJ74lmjGY1XrXHUwMDFk9Srsi6Qz68b7OqpeLJNR5nfupY5J8Py07s/P+t8+XHUwMDEwqe3fW3lcdTAwMDQ5V/BcdTAwMTZVuVx1MDAwNLQl4dz81lx1MDAxMiY5XHUwMDE4rVx1MDAwNsiqXHUwMDE2MlBl0ShcdTAwMWQyv45cdTAwMTNKjZK/nK3YrKznt6544E+E21x1MDAwMlx1MDAxNOW5cv1uj/AzXHRt+Fx1MDAxZbFcdTAwMDFcdTAwMGLQZdNXYWtPXHUwMDE2o7VZut4sXHUwMDEzmeVcdTAwMDOaXHUwMDE3r3juhCGur8fv3XioTjrn9znaXHUwMDE3R18yd21hi1x1MDAxNFx1MDAxYfNvblx1MDAwZfiuy4GsTlV75XDvyZyIvYGfc9or1Fxiwlx1MDAxMNWgLE9/UEJ+eYNhaPXAcN1U954vcz7v854sRVx1MDAwMbBcdTAwMTGIbVCqY8HP+K3Myqns5Xt8MexcdTAwMTWKRddcdTAwMTVcZq1cdTAwMDfFJ6H3jcVynL5/5z6awZS8uubq3Fx1MDAwNUbUv1x1MDAxYvyAXFxcdTAwMTC1nf4l/r1NyX5MXGKmNHHG7Zx3vL1EupjPoFPoXHUwMDAzjeSITZBccjSjjJM+ozJcdTAwMWGHPF/He6JcdTAwMTCvXHUwMDE0MTWMv/ea1Vx1MDAxZCEtLlx1MDAxMcrAUF2ojbSewLQj2VhWr5G7s5TMjVx1MDAxMM/GM1x1MDAxYUbDXHUwMDFhKJlg+Fx1MDAxNdd6NbLBObK0mmdcZj5mfm6eXHUwMDFlkepjXHUwMDFiwl6Ri8BcdTAwMWOF0/ZXZssut0uiVy5cdTAwMTRpJmE/XHJ3XHUwMDE58ahg8bGfc7uCSuPI8FxiI1wilji86pPRXbdxXHUwMDFiX4t9JbSPzUUk382hzb6fnsqaNVx1MDAwNznOTSNcdTAwMTE5tXXt1ZmvXHUwMDAyPpCwcbmXXHUwMDEx+J5cdTAwMTjwvzP+Q7Lh89C6bzLPx2TH6dGySlx1MDAwNPHC0OW/eSoyLifZfseOoPqGlXN6dVxiXHUwMDBlJ+fSz7tcdTAwMWJjeyyCplx1MDAwM/BcdTAwMWZQNm83XHUwMDE3mlx1MDAxMbTKmFx1MDAxNz6hsHyE0kSrPDdAXHUwMDBmzlxi61xiXHK9MJA9gCXOZ3K+3eFcdTAwMGKgeKF4XCJcdTAwMDY6y1dcdTAwMWYge1XdiIBcdTAwMDPvXGazkSeEtM5cdTAwMDS5xfVcdTAwMTmzWZlcZq/eLOTn9Vx1MDAxNIKLajXurqtcdTAwMGasvPk6XHUwMDE37rKMN5e+XpRcZiktw6VcdTAwMDRcdTAwMWSeQsBcdTAwMDFcdTAwMWHiZUBGnNRcYrpcdTAwMTVrtSZx8s7b7TlGVOEsWqRcdTAwMTJM9dwp7Vx1MDAxZfGn9FxcuqQ9+EQ86G636tZDPTvQOZ5veZ+AY0FAZz97La5F5GvSauzkXHSFkHP4mNp9QLRcdTAwMWX4fbWQXHUwMDBiuiBcdTAwMGVwy1x0viA+eVxyuHF6P+Z4aH/bR1x1MDAxMaqCa4fc92dcItlcdTAwMGLiXHUwMDExNFx1MDAxODoklZ7uXHUwMDE4vT3QTrknPXqQzaqqLJXzNUfZx+KYJiniqo5tOFx1MDAwNFx1MDAxMew3ycJ9iJ9cdTAwMWVcdTAwMWbtUFx1MDAxYlx1MDAxMie1hNZzZL4lyMs/PfjGcVx1MDAxY0otR1x1MDAwNsKy1Vx1MDAxNWtwbSx5xM0oT7i7X9VjlFx1MDAxYvBcdTAwMWQtoYc6+zk0wCh+fV1cdTAwMWG3N92LXHUwMDFhmSDm4IBWc5o8dUv79/2/hfmWstOak6L64vxXuyag9ueGYFx1MDAwMk1cdTAwMTH9yepetqJTyMp4nYeSW2tcblx1MDAwZltdo5ZrXGK9XHUwMDE3sd6jY4rx2nmwXo+1JHHN8JXCXvj9rfm6p4NcdTAwMDdcdTAwMDJcdTAwMWZcdTAwMDT5sv5cdTAwMTKe1nFS11x1MDAwNZxcdTAwMDNcdTAwMDT7ZTNAjZ+LXHUwMDBmqjKDhOT3P+J+V9ipVVxcL7Gcs2A4tEAzrGbqNSNcdTAwMTDzJq/XaHBcdTAwMWO9XHUwMDBiyOvUZvIzrc++JaLhWlx1MDAxMLJgLlx1MDAwZXZ75nvhKXooMHspKn5Whlx1MDAwNnU5rqDc92mML7LciePdMdP+XHUwMDEwfktSJVx0iEe/OZpIuzl9ylx1MDAxN7SfXHUwMDAzMH2zYVx1MDAxZV3wldziLcWxeDy6QXtINU7ZnXJKXHUwMDFhXHUwMDBmeEbj9EZeakVcdTAwMWI5RfBcdTAwMGXQXHUwMDAx4m/osuX1XHUwMDE22GJFKUr1KFx1MDAxY8mbNlx1MDAwNUyiPnpcdTAwMWGPQtRtPv3AQSpcdTAwMTJcdTAwMTCZYVx1MDAxN6/DeUBJXHLGMuRcdTAwMTSN5sVcZiFcclxiN1x0RINDuElkXHUwMDE5yeTJwuBae43VVTXC+ZRIQzEoNlx1MDAxZFx1MDAxMDNcdTAwMGJ3zVwiKVttXHUwMDFj0bzxI1x1MDAxY1JFXHUwMDE5LEjl94u1+Sd8r07zeNpcdTAwMDNcdTAwMGI9zyFcdTAwMTUhi0ffwuzpurppnsb9hpCf8rZANr+9WXxNXHUwMDAz5qDs1yrQen9cdTAwMTNEXHUwMDAxMn9TQFx1MDAwNLOPy+r2xKbOXHUwMDE5nbxcdTAwMGJfjFx1MDAxOKFcdTAwMDa9dy2+2mN9xCat657g08GqL1x1MDAxY/SJPffAcILnPcw9XHUwMDExL6fefN94kXB8XHUwMDBid+Xd9rDFTX2KgLI5bVxmXHTwXHUwMDEw1nCPXHKleebQ5k6FZUUwRFx1MDAxZer2Ny0vW1x1MDAwMlx1MDAwZjLFQjPlhsQhXHUwMDEwKyHM/MrM0NY275yPMsxQ+1xyYzaB8J0ywF3f4Vx1MDAxNm1HSO2rIN+dXHUwMDAzqZ9HXHUwMDE1tK3TLF58qudX3/am/Nm1r15kru9PR5+FWHMg9kCytfu/NVxymZOlXHUwMDA3g7wk9nfnYc9d/ct3pc2tuIef0e+0w7lcXKJcdTAwMWX7qeq9vzxRM9KPTvOf7+D5Ulx1MDAxMaNcdTAwMTWWuCHkdnc0+5Bwk2ybqP+m7kw8XHUwMDA213E3XHRK7KOzf1x1MDAxYc5cdTAwMDGPXHUwMDFk9I1cdTAwMTN4jj+9gCo2xCr6c1x1MDAwNNrrWuVlXHUwMDEy6TzvQTPsXi+tnikxNJ2wMS+2Ke9NYZ2LXHUwMDA3q1xcXHUwMDBms3zfhl+74sxcdTAwMWHBeNY67+lv+7A78zF6XHUwMDFiXHUwMDExxG03sF2JY/XJmp88flPjQdhcdTAwMTTe0efsiYSwk1x1MDAxOGfF+qFLmmGBrqH40eP1OkFcdTAwMWHpuk99XHUwMDEwqV2abqhcdTAwMWO1LeaPXHJPtlx1MDAwNn2M7Fx1MDAwYlUsftMobs066D46em1j8Im5zH8v83btVVx1MDAxObfDmuD9uf72htNcdTAwMDPsv7SGk07zkL3qTXRnvTwoX67p+ZrkXScqulx1MDAxMDlbeVx1MDAxM7D8XHUwMDE1YFx1MDAxNDLaXG7nOzjPtrZsNKLw9XtrQzUwRpNcdTAwMDdf9P29I7jV7ZC3noHnXHUwMDA3XHUwMDFmQtSfkmFTn/j8zVx0XHUwMDAwXHUwMDAzQpU+mVBcdTAwMDDPmihDlvi+w5tOXCLU5D8pX1c1O9lcdTAwMTl/v/GDX79cdTAwMTgrVfFUspTMaCW3qWV1Mol5WLFpv+js8jhcdTAwMGVcdTAwMTiP7ktypFxuZCEv6q1yQ0zKSrdwsSxcZr3GpCNeXHUwMDFkJ0Y7IeJUhFxcN8r7rpr+U906U2gw8IKVqttqYu1nZFx1MDAwNPVcXKLwTU350HWx+VxihHhbSNyhx/wwlrgrn5XBPqWHiahWhkA14tyXJmNJ+Lm/XHUwMDFmXHUwMDFlbe/yPDtfiC9qWIzPXHUwMDA1XbTswTBBckA5KoGPosisj+hWSvmxYaya7L18wXeroXeuuH5cdTAwMTMtcYH4frm32kl+btWiSv5cInPOnZFlJuvPx+PzKqLeTD34JIs9UFpcdTAwMDepeXaSw+Sy9ch9XHUwMDBlgsRG+ebO3b0rjbm1zN528FXrSbnTV307fdlInvdeXHUwMDBmXGZw50vHXGZcdFx1MDAwMG9cdTAwMDe6XHUwMDE48DgtMG07vVwiIVxivKOEnyDHz530O8vsa6a816lEJugrJqTTLYx0g8uJ7lx1MDAwMkl8TEBMyKFcdTAwMDE61VWavCrxgXvEXCK5XHUwMDA3JVx1MDAwYoXKKWd6l66Wu5ajvtOFsic8XHUwMDFlXHUwMDAxWL90YWLwZZ5QPXgp9aXQJFx1MDAxMZfpb5dcYjRcdTAwMWWFzq5avH3ea6K/t1x1MDAxOG5e/Fx1MDAxYcB5XHUwMDEzXHUwMDA0bFx1MDAxNb/kw0i6XHUwMDE0uVx1MDAxONjGTCS3oHbC6qd1keV7LFx1MDAwMW2houXMj3xmib1/XHUwMDE1J93qh1x1MDAwMcE+Y05pilx1MDAxNXpmXHUwMDE5/W+J02XpuKktvKneY26C58NcdTAwMWZpuvOH4E61R/lRoCtvWW6o+iQvmymU22nCXHUwMDBlyyrkXGZ4K1x1MDAxNE5Pp1jtK2HBkThAcl+0fH6KsedcdTAwMTJcdTAwMTX91tlnXCI//phpioJcdTAwMTbUPkDMfi+5/lwic4TiXHUwMDA1O7Sm+Fx1MDAwN7TXuNNcdTAwMTkhKFx1MDAwMsFF7lx1MDAxZfDP6MN1XHUwMDAwsE+u5p4yr4+TXHUwMDEyeEFcdTAwMTC03GTUv1x1MDAxZDRcdTAwMTF/3Vx1MDAxM5vhXGbRNulKfdaQaKO66cA42ZrM9VljOy3PdPWAPGkuQfYuhJDkrlFcdTAwMTA66U1mZV3y+SVcdTAwMGZnXCI6XHRcdTAwMTiGsqyeWV078e8th+81vFx1MDAxZFx1MDAwMVx1MDAwZTthePtcdTAwMTLeWkpbuItdwXAgWl/HhF5cdTAwMTWJW3htwVx1MDAxZr9cdTAwMWPR42mm9PqBmE3Xr9Jgc1x0XGa6MSH9/rrmXHUwMDFh/0Q2b8nkytitXHUwMDE0yuQ2XGJ+Slm+XHUwMDAyrYVkv+lrLJKaZGHEkHWPYUjQc/TWdlTx8S7NXHUwMDBm8TWde9W7gruBqVx1MDAwZnL5uynv1LMrjWRcZvY+XHUwMDAywkHuXHUwMDE018w6Ma9b4IGmj23DbuFBWy7duJ9cdTAwMDcpXGaDJltftSlcXMOrfy92UTdZg1iJl1x1MDAwMUT6l1BcdTAwMDNI5ruP/v3mvsGDXFySuu4ne/PIrbxRirVcdTAwMDKEpipcdTAwMWHThmerXCLiotDX8iT3M0T0VltcdTAwMTNHXHUwMDBmJkXDidLKhVx1MDAxOeV4O1wiqijr2Gtz5lp9X7FcdTAwMTS+P1x1MDAxZlx1MDAxMHZHXHJcdTAwMTnZMp+p6cjo3TFiucjrROpKXHUwMDBiXHUwMDE48KzJ8KvxQ48o2JttZ1x1MDAxZrKuXHKjcVx1MDAxZnJ6nFxceUlm6L1snjXqI1x1MDAwMvyGQna3kVVw7a9zLPG6ilx1MDAxYq65YrfFZa++XHUwMDBinDNcdTAwMTfH1tyIwFx1MDAwN5Td2XfXtNxL9XOrrWJcdTAwMDIxP5BH61ZcdTAwMTdLzu/eVlhcdTAwMTSMquhu8NG4RWOXp2hrXHUwMDBmk4JcdTAwMWXNXHUwMDEwwbKBXHUwMDE251x1MDAxNnl9hH7MsoKDqu3jvSN7/lx1MDAxYzfBPmWxk/mk1oyXSsixwkxcdTAwMTl5/tNcdTAwMTPln55Ysm1zQE/2xtHe2il9XHUwMDBi7fO7XvZcdTAwMWTYoHZkt19cdTAwMGLmxXhcYn/zLPl794lcdTAwMWViusCnOWFBIH84c3RD2PDmOfTYXHUwMDA39YglXHUwMDAzoTnAiU5cdTAwMGJpXHSzvf01MV6fYDv5V3hcdTAwMTi/N6NcdTAwMTP52yqfOSw7LUNcdTAwMThkXHUwMDEyplx1MDAxYlx1MDAxOavHXHUwMDEwlnlcIrXTXHUwMDFh36Zl7PgtNsjDIe/8YbwmNXFIkLCP/bM82lJ8aUO+NJXLXHUwMDEzeLZMlf1hWFx0XHUwMDAwvTnrk4vPXHUwMDE2UURcdTAwMDLIempcdTAwMWU3XHUwMDE5jiDGkYvQPMJMXHUwMDAx1YYpL41cbsOVgJiH/9l7pnWVRbFNpkFcdTAwMWP0TYT8cCZUrE2kOsaH+zWf402hxNGHJJ6l5vL6omlcdTAwMTZlkPPCl0A4mn1F/aBoeVxilkBMINSLwJWlZ/NMoflFe8HDpEf9xjxt21x1MDAxMbMpXHUwMDAzXHQrWditXHUwMDBmkSyuzIjeVlx1MDAwN5qOXHUwMDFjz1x1MDAxOXG7vUFuh1x1MDAwZn54VFx1MDAwN3NTVNBcdTAwMDdS7tJcdTAwMDHaipN++UarddvxrlRcYuqP9b2HWENKxzN+9zPJ5NVcdTAwMTfGxPbG48GIS1x1MDAxOHGuK6xJo1x1MDAxYiXQmVv5YoHzSbGHXHUwMDA2XHUwMDEyQMrLXGI7dsHjzolcdTAwMTJcdTAwMTK0Wq6OfoAnmqGy9XFhblx1MDAwMFx0PINaXHUwMDEwXGJ8XHUwMDFmupZKvID0g32OTFx1MDAxYbJIx0vhSX9gYz+35KDg+5nv+lx1MDAwMKlcbqpgXHUwMDFmd6HgkP5a5ZJAqc6yNauwTj8mSlx1MDAwNfVcdTAwMTcwknjfYEzoI1x1MDAxYdrEz54470VfTkym1Y9tzSmlpoyK0pEmjlqVsCNcdTAwMWNJwtostFqlQ4R8xUchXCIvdHTvVEFhiEaMlKZcZlx1MDAwZkRl2KhDhyFcdTAwMTFq5IzzK/ZQZYHqMmRGbeH49z5ySPNcdTAwMWWfUDLYjnha69REz/dcdTAwMGKFdJFcdJBvMFjKPltXpLBZsIjygoR7OI1cdTAwMDbdy1x1MDAxZoo4XHUwMDA2uapcdTAwMWXqnSdE5Vx1MDAxZqSDPSBn6uFcXFFMnlxigutcdTAwMDFTsVxmh1Op3Zfu9fbxqOPmXCI9q7VcdTAwMDZcdTAwMTCBXHJvXHUwMDA2ybmWmlx1MDAwNFx1MDAwMVx1MDAwNVx1MDAwMKBcdTAwMGVpJ5fIRHE9o/29XHUwMDAzZT/+N/a+Y9dxYInug7hgTkvmKOa8Y45iXHUwMDEws77e1J33XHUwMDAwXHUwMDFitj/AgFx1MDAxN1x1MDAxN1x1MDAwMmZEqkNVnXO6q6txqbtw4Vc3upJ6UF2EXeUr4a2IXHUwMDFk3lx1MDAxNpRcbkg3VmlcdTAwMDKYaIzTUzGpXHUwMDEzbsDVLWI/XHUwMDBl9Z30j3Fdckx3Xu7EyeJD+lx1MDAxN6igjFx1MDAwNlx1MDAxYl9cdTAwMGJMS1wii6K9UziczFx1MDAxOaOE6YEg+Fx1MDAxMVx1MDAxOK4j2CmdfiBd4yPWgYg3z0QkRf5y48mRJTelXHUwMDFioKxB4ZKF5lx1MDAxOVCnZDJwRlx1MDAxYpByIz5cIvRcdTAwMTZPejVcYoew7NWUVVx1MDAwNaRTzJ1X8224i3WhSExJXa40i+NX2tO7THhcXKxcdTAwMWLNylx1MDAxYWNmkpePiHfBgr/GXHLdQe53v1xcskyaTdKnOFxmXHUwMDA1StpcdTAwMDVSafH0QuSC5uZcdTAwMTf88dM3y1FETOjW16hcdTAwMWX4udS69izqYj/GSFx1MDAxONxcXONcdTAwMDWyXHUwMDA3NzN2g1ti5IPe5rlwP52k/+6eUlvZvnvPfik7ezD1XHUwMDAxXHUwMDAwjEVcdTAwMDNgwV9cdTAwMThRUNbryNHrg45T3moof7YgXHUwMDFltIA6J77SXHUwMDFhTTyJXHK+XCL39cVcdH/8wkvOf8xcdHZcdTAwMThQx+3uIXjjuqzkYZjCb8WLTe7i1YPa6K3qXHUwMDAwvOh99Tp18EA3r1x1MDAxMb394lx1MDAxOCxcdTAwMDaKMlx1MDAwMsb7+7KL0Yq6XHUwMDEysinPJsC+hVxmYTaN41x1MDAwM/xOXHLj9HV9TeDbe+iOLehcdTAwMWSceFrTOb58O0p/Z7aSbritm2r6wSiHXHUwMDA149VeZuJdgdPKOCDtwVx1MDAxOHi9ulx1MDAwZe/DXHUwMDAyulx0QTtllqL4MvNcdTAwMTP/Uz6RTKdGppKD7XtcdTAwMDOSOVCg8jGA9HuYzmipXHUwMDA2qenELDqgpfinPUX9bVTF7uLD0d11QupTrcagN8FQxUJuRycxS1x1MDAxYdnIT9X8W0TpX+dcdTAwMDMrhp40hnq2XG5cdTAwMDdyhjtH9jp6zfchXFzoXHUwMDEzO8X2LYBGO8rFKY5cdTAwMDCVXHUwMDBlZl5PXHUwMDFmweHWKsh18E1cdTAwMTQ1yMuy9eZcIuLNpIaaXlq8Ue9XrZ6YNEGXmiSSXHUwMDE4qoRcdTAwMWMpQZtXXHUwMDAwXHUwMDFi4/KuRbPjj/Ezzl12tEN7hVx1MDAwYj6WXHUwMDAy+VwixPstvF5cdTAwMWZjnSb50W2nQlx1MDAwN1x1MDAxMFx1MDAxNrDjVT+M/JBtbbThlVx1MDAxZtrb/sLu1lxm2YqUYFx1MDAxY4RcdTAwMWPA85XJfldcdTAwMWEvwUueqkqk6uijvCRWsjSYmuO1NF5XvmWjoIvfXHUwMDE53lx1MDAxY4Xe80Gu3Z6jXHUwMDAwOyaj3aP18TUqgvtcdTAwMTFTSzKxXa5cdTAwMWQqnT3xXHUwMDFjed0hXGaIc7B3m1x1MDAxNK7SfeRcdTAwMTbuo1x1MDAwMynMYDuOrGJeXHUwMDEyXHUwMDFmo2KCg1x1MDAxM1WMoMjUilx1MDAxY4Z9sC7h3UhsJ7hcZis40vtccqXaJdruL9mEvVx1MDAxZVx1MDAxMVhcYni+XHUwMDA0azh+gymEx6b8knXSRmC5Pcaoa5dvZy+xXFySOHEwilx1MDAxND/f9Y48TG8t7u7WMPUnwZFPqoNuXHUwMDFjl0RcdTAwMDXxpFxu7Vx1MDAxML56nO3ugmuXYVx1MDAxOVx1MDAxY7xqXGZo3q9DQfuSnXNcdTAwMTZcdTAwMWTCleZcdTAwMTVtbYSNpVx1MDAxNVtcdTAwMTdcdTAwMTXn1D089E3pakWQ7yxjyLKj6as1SLRtXHUwMDEzXGJccod2mHLU9HOqXHUwMDBijoXp0YxcdTAwMWHlskyml7LIWvOyefNcdTAwMTP5p0NZq0puXFzxO37/0IVH0qBQQ/Yzg7+A8cyiz7eR8mak0k5cdTAwMTcr7VNV87Xyto5GhdcgSkM+vFpK7OORsLg+2E8jbPtcdTAwMDRI7ZB3VdBYxXmMjF1cYuKYXHUwMDAzXHUwMDE1U1x1MDAwNEWEvIlcdTAwMTFcdTAwMWN+qWJ2Klx1MDAwNb/WaptcdTAwMDJBhlx1MDAwNb8qYL09Y5xcdTAwMWUpn3VcdTAwMTis5Fx0zEpFQsBmgbNJqLVsbOE7wr071/1gSlx1MDAxNrBWc8NcdTAwMWaZIVx1MDAxZsy2hVx1MDAxYlRzc73hXHUwMDEzXHUwMDE174s07taRtnfXXHUwMDFmxfxaXHUwMDE0kyxcdTAwMDWcXHUwMDFiW/6bpy/leoGAcGjiXWFnUF2Hs39cdTAwMWVcIufvafJcIstcdTAwMDVP3va6v41WOis2tOhcdTAwMTlcdTAwMWQ8IJC7s2NcdTAwMDdcdTAwMWNUO1x1MDAwMlx1MDAxN53qpVR9UHZccmLt4yZi12vB6805XHUwMDEynFwilzmE3jLIf4ZvgSjsXHUwMDE5O8XFI/TDu4JcdTAwMWTDXHUwMDBizm6ixFx1MDAwM5+BY96dJVvTRlx1MDAxNHqVPFhWoEvAQa252lx1MDAxYo+xrM6+j1x1MDAwML8xXHUwMDE5QJtcdTAwMTP3jiGrQ0Iqea0jgW0yXHUwMDA2N+dcdTAwMWOOK4SbYftcdTAwMWNcdTAwMWLfSlwis/I5nIKGiC3scvzXwsBbbGn+w+dLtpqYXHUwMDE4N+tHXHUwMDA3gLF/xo8zkic4xS38RmxcdTAwMTlcdTAwMTiSw7NcdTAwMDU9+aTiXHUwMDA311x1MDAxZW1nWuHHz1xcXHUwMDFkrUDeyy3ewlx1MDAxYS7ytkZcdTAwMTVcdTAwMWT+9Vlm+MM0Vs9cIplcdTAwMTPPRGG86sZZ6VJsXHJt9PNcdTAwMTiRkvdF5ytcdCGYTFHJet26hXnA2FAvlZe7L6g3ueyArjBFULwrJlx1MDAwNsbmXHUwMDAzXHUwMDA2PkBcbkj2XG7Wz3b1UEA1odeSmatmPlZcdTAwMDZcdTAwMWOA4WJs67dcdTAwMWLFXHUwMDExXHTjriqmPldvbzC/rONVP1xifYiYRldHfFpAxN568Vx1MDAwNG1fR5Z6XHKB7YzZzMCXkz2K+Fx1MDAxMMPgQt5cdTAwMWWYlfNSp1xcj339XtZcdTAwMDPFfmxUev0tQlxuaZo5T+xcZlhcdTAwMDYzXHUwMDAxUCRcZouEyVx03JLsV5N7npS0a9VcdTAwMTblj1pXNnrZTev8YI+ZWiC3NHHk4ObAwUJcdTAwMDeg45x/Vbf2hG2GXHUwMDE2Q2rDV7faXHUwMDBmkGZQeqsgXHUwMDFh15JpXsJcdTAwMTlQ5kN+aYMsPUDPeVxySSWSv8vKMy9cdTAwMWRzdHMsPvFK3KGEuVL/1Vx1MDAwN/iy2P7oRcDQiCrwXHUwMDA2a3F71tc1UMRcdTAwMWVkW+Gd3Ft6R8vsXG5cdTAwMDdcdI9FoVXU9ktcdTAwMTcmafYh+m5X9sy1X3JBW0DO+FCJXHUwMDE1W/aIXHUwMDEzuVx1MDAwZvhcYqPT+ICVZrNcdTAwMTZFmFeqV0XYpYj1xCCqZzaAtCdcdTAwMTRcdTAwMWRN2aF9XGZIfFx1MDAxMqpiR1xyjpydj6FyXHLVb35cdTAwMWGeuVx1MDAxZdhKpEfD75Jbu75gdi7uI2tIrqFcdTAwMDanu70hXHUwMDA0/TunmPjO91Suo1x1MDAxYjJcclx1MDAwMfNcdTAwMWNcdTAwMWEq1nY+nyBcdTAwMDflXHUwMDBmXGI8ou1cdTAwMDV2X681MFpXJuHSXGJa25xZZ3N7XHUwMDBij4myoFx1MDAwN31cdTAwMDHCipEpXHUwMDE3pO99V9ldvFlTTtzG/lx1MDAwNLhcdTAwMTWbYFvH/V3EfkosrFuYpqvTIG93rFx00DG9iTlcdTAwMWKJaVx1MDAwZYax6VCDXzX+oSPsYevXUVVmXHUwMDAzfsrqXHUwMDE0bnWWj5rZ38+Y3zcpNG2+q1x1MDAxMPjVxC2x3uXeXGLdQ4daZLp0WDF7rm5QXVx1MDAxYnKJMJiBWe/EXHUwMDA0XHUwMDFlXG6+XHUwMDFk7Nsyuru5hrUtvTdodnKDpuVcdTAwMDCuXHUwMDFh2oOAe9aNnNWbTE1p8vnbc+mVf3rSVlxyw370JFq359cwfvl9Qi4z53dGQE5cdTAwMTBtcHJig4HkXHUwMDA0VC4kOdH6clx1MDAxZpez2TPB31xynupD6083XHUwMDBl6131XHUwMDEwRJ5/t7KjXHRjNng8o6mvrqxMiOhOZcCVLVx1MDAxMVx1MDAxYlwiXHUwMDE4y23cr3rO3UC2eVx1MDAwNmye//N05dzegZZmcpudkDZE3urx3EjNa2TPzGdcdTAwMDZesb8rkzdTJt9nXHUwMDAwyJ5cdTAwMDAgtsD0W1x1MDAxYnIkQ/p2x6hzwFxuuOZZXHUwMDE4Yzf5XGJcdTAwMDFUJu7yfSlEOFx1MDAwNK74XHUwMDAxWlx1MDAxM+qD5iE+YVx1MDAwNs9oLS2LX6lyIFxuKFx1MDAwM7NkTFx1MDAwMFxu5fu8qyFeffLEUsihzFx1MDAxYlIwkjDP+HtcdTAwMTRmV1wiXHUwMDFlRVxu6o5cdTAwMTCGdJX4t1WgXHUwMDA3qU6OXGLc4TJhM/tb6JeE31x1MDAxZZpLma5D+Fx1MDAxZYOeTvhcdTAwMTludqNcdTAwMWbFdlx1MDAxY5daXHSzIFr7XHUwMDFjukXRRe/54Vx08PpIR+8rebhycK9Hv+eesjpi+dhrJpr5LEw5WVx1MDAwMNFcYib5xvUpXHUwMDE5XHUwMDFjeOXxKtnfrzq0UKD+dq/RhLF+hblPjVx1MDAxNaJyq/51vkJJQ5nvMXjr+1vBVlx1MDAwM9pUsZ/Qqlx1MDAwNIf69D38RMyadsZcdTAwMTiPmFx1MDAxNH4quUxcdTAwMTaYoZNj4Fx1MDAwMG5G4U+h7lx1MDAxYkL7lIFgQ3xcdTAwMTjhXoLabFx0jcSrtlXns+pysfdcZlx1MDAxM37wvFx1MDAwN+pcdTAwMDXzSq7vctqHYVx1MDAxNVx1MDAwNzpY2UY7MdCcYpqYbV8hmYF1Ljfi1oMg34nfSSptXHUwMDAziCrUnn6C5Fx1MDAwN7sndt9x7b3Y528ok/anXHUwMDA1mFx1MDAxM0I8x1d2zqh5lIBcdTAwMWFcdTAwMWHUXHUwMDA1T1x1MDAwZlx1MDAxNoRcdTAwMTKjkspcdTAwMDaMuXRgPPS7cu8ksPlbx0RcdOr406q/Ujzcgbfie1oyXHUwMDE1XHUwMDA3x1pcdTAwMGWm0Fx1MDAxZGL8IbDewFbNuNlzUa616L1cZlwiXFyuklx1MDAxOCdZIX/3XCJcdTAwMGIgQ5qRXHUwMDE5WPLl9Vx1MDAwMn9cdTAwMTm28iVcdTAwMDJcdTAwMWTOkqxHS996756pXHUwMDEyj3RIKbPI6cv6pFx1MDAxM3njRVSTolx1MDAwMNPy59gvlqRcdTAwMGbzsbtmZ+hD3OVOa1x1MDAxNVx1MDAxZLfKkqVeuaOOW69FqbBEXHUwMDBmXHUwMDEzNmFiT9gmzJ1I/4at61tPeKnBXHUwMDAzjk76q9DA4Vonxlx1MDAwMuX0pf1VujRccusnXHUwMDFlnLKvOVm/fV5DwVx1MDAxOEex0+jk7uhsMYHiZn9cdE9cdTAwMWQmXHUwMDBiVFx1MDAxZYjR/7tcdTAwMGbJy0uETe9sUCs0p96/gPK+prtcbtj6RPT1u3Erm1dLXvNagJuiXHUwMDEzJltVvUmBMKVyvLpqp4DOnWFcZkmENjgqNIL0M2iqqIXZiNl1pnuz/pI664VZ+ItkV+ihNuRcdTAwMWJcIouVU/IlLKJcdTAwMTP4pLn02Fx1MDAwNfZVWiCXMGWXXHUwMDBi6Fx1MDAxOIfhjcdPnFxiNaMrqr1cdTAwMDHkeZhaXHUwMDE1XHUwMDBiOPjT9COIR+hcdTAwMTd/2n3T92d+u+bFrVx1MDAxYtYok1x1MDAxY3dcdTAwMWNGVINx8KNcdTAwMWWgWY3fd1x1MDAwZVpcdTAwMWJ6u29wg49a4lx1MDAxYippXHUwMDExkSmaXHUwMDFhd1fGYkVbh1TcymXY73+YUv3SXHUwMDBmXHUwMDEzXHUwMDAxwPFAcmyHUkd+uJvA10QqXGbS2T/zxFxif9fi3lx1MDAxNPtcbsNggFx1MDAwMLZfe0d4zL+htcDjXHUwMDFiXHUwMDEwisz80UNcXF2jXCKcaiXowdf0SYVcdTAwMTPJ2imUv6V1kc6GjVx1MDAxNNRj61Ay7u6n0LbRtdXreLxcdTAwMDMyXHUwMDExp5QvVHheZMiYs8NCQ0fE9lxcYe7jo72ApOjK+Esjx0hZ5K/OXHUwMDEyXHUwMDAyVlx1MDAxMFwif9WeXHUwMDAyUFx1MDAwMlx1MDAxM5CDRk/AXHUwMDFk9TdcdTAwMWPuXHUwMDE1XHUwMDExXHUwMDEwmEpYd+g6KVx1MDAwN2/k2EewNHBko4uzXHUwMDFi9txcdL7BUlx1MDAxOLgtaq9cdTAwMWRN4vFC1627PuS2j2fYkEOOK1xcdr5RL8JQNOadiFxuWqx7XGLlXHUwMDBlXGaOwPcs6I5cdTAwMTGRqjCbP76qzvXPV3n7dEO7XHUwMDE1ojphLTzfXHUwMDE5/rHbSDxcdTAwMTWJf0lcdTAwMWZIelx1MDAwNUW7xNWpviWt/V0gXHUwMDE0esrNxZa/RYvFSlx1MDAwMfVwNVx1MDAxM1x1MDAxM66XIVuv2e/G6G5Cflx1MDAxM1tktVx1MDAxMzglhe1cdTAwMDGDKySZY1xcf0e4qtW6+L7oaX0uzW9cIk2uhlx1MDAxN+dhsVx1MDAxNvnSXHUwMDE5gJanslxij4+vQVx1MDAxYU/6ackhaK2X5N2UMJeMoDlcdTAwMDBiNMJFXHUwMDA3RvyxSrG1jXC2pqx9alx1MDAwYvwwXHUwMDEwrziyrHCYXHUwMDBlQj5cdTAwMDe/m+tDXHUwMDE5imRXXHUwMDEw3ap6e8zROtXvzVtcdTAwMGJcdTAwMWJaXHUwMDFhUJh6lsLK4Vx1MDAxMlx1MDAwME74XHUwMDA0XHUwMDBiXHJcdTAwMWbh68Y+Rv3bXHUwMDE3RrY0Up54ifqlyVx1MDAxMV1mXHUwMDFh8+xcdTAwMTDFmrowklxyOqlHO2TR1DfCTu2sKfAquehcdTAwMWQtXHUwMDE05JOq4MauYJG9h0DOY6zcL1x1MDAxN6acXHUwMDA3Vapzx+VSXHUwMDE2PKGy4YdvtcpcXFx1MDAxM1x1MDAxZvYh+DqeouJWc+tSbv07ONnTTrKFkFx1MDAwMzzXQ37VilrijoxGXHUwMDFhe3shgsclh1xuXHUwMDBlxVx1MDAxOYZjXHUwMDE5gVx1MDAxOFxcWVx1MDAwZcbGh1x1MDAxYoGLwV1vpGx80kz5z47eKlJcdTAwMGJgplx1MDAxNlx1MDAxZH2LXHUwMDE4IcB5lJYgonlyS/fhccVZeJdcdTAwMWZcdCtoXHUwMDEwbzPq+P5uXHUwMDA2XHUwMDFiolx1MDAwMfmIXHUwMDE4JYJuZUdcdTAwMGJcXIhccnR/StJ/RNWl2J15aFus+Z23XaWYv5ct3mNC2dOqXHUwMDE0qK6ssztcIiupY1Bo69ZIU8UxWO5cdTAwMDPCXCI9XHUwMDEzXHUwMDFhWycmXHUwMDEyh+0lfIKgqNBcdTAwMGZ02JpHamnMXG61OLDshCxKxTfnw/bU4fsgI7mRqlx1MDAwNjdcdTAwMWKTyeKQXHUwMDA05jnwlHBcdTAwMTlcdTAwMThvSVx1MDAxM1xcMGS19Fx1MDAwZbPyWrad41x1MDAwNLWvXHUwMDE0/KJcdTAwMTBZtpq7Rs18XHLSYe0hfYYwYl/+slx1MDAwN1x1MDAxNN1hTfimuHpcdTAwMGW3aFx1MDAwNTTQrob95FfQXCJ44kwmwyRprcDxx3NzXHUwMDBiv6twXFzdgCBcdTAwMWVcdTAwMGY8RX5HK8OUX+ZDS6/hXHUwMDFikEVccnA6aK6tgt1cdTAwMTZiXtSvNuRXSj0jkPhOXGKLXHUwMDBlJlhCQJGE7X2D5j5cdTAwMTidXGJcYkpqPE9+0IszgM1+N1xyW914PrFcdTAwMDBJrXdcdTAwMDdOf9y5c/72vyR1+rzUh6q4ObtsrmqRw6lwnV5ji/+5P+utn15cdTAwMGLc9TFCyvDoqfot9Vx1MDAwZm8w9OGzXHUwMDAyXCKB931+3vpE5Vx1MDAxNyBFeyCcb1x1MDAxMlx1MDAwMsuQTC+FU7HT9fU0yFx1MDAwYp6Xn/ktsPlLqcA1XHJ2q1BcdTAwMDV4SjyukSvIiHuq4+iOIIX/0Ob+1lx1MDAxNVx1MDAxM6XGsym6PYlB15ec8+3FpLSe3eHt/GpcdTAwMWGtsHozlJpGkerwyuSyRFx1MDAxYsJoaVx1MDAxOfgwtG5a3caaokcuXHUwMDEymVx1MDAxZVx1MDAxNEFccqzoV29oN7B90J4p3ueislx1MDAxNtyojTNiWlx1MDAxNYGRh99cdTAwMWSemlSIajwwe1x1MDAxYpx0OVx1MDAwN1x1MDAwM0dH7Cf4wJS0wOHqd7D5XCJP0CQ5XHUwMDExmVx1MDAwMS3sh/LxfFx1MDAxMCzx4SdCXHUwMDBmXHUwMDFkKLyuXHUwMDE232dcdTAwMTl+tPXOyWi6TORWWu1KXHUwMDBlZeXglSj2e8pNxs4tRFx1MDAxNqHyXHUwMDE1XHUwMDEy7tqe0+fBhUdKUmlcdTAwMTfyQsmXn3Nhv2/0kSnCJSFcdTAwMDIpdts1RfowXFzYXHUwMDE521wiXHUwMDAwnFx1MDAxYUpcdTAwMDF8bVx1MDAxMWVcdTAwMTP3XCJBn28r/ILMXHUwMDAwXHUwMDEwV8xcdTAwMTGbXHUwMDA3ZVxmpZff7V1v9y1tPrZJwFx1MDAxNjHMIG+lN9860lx1MDAxMNtcdKquXHUwMDAx/+6W2G5cIvMzXHUwMDE1a1x1MDAxNl8nXG5uz5T8nnPxXFznTHQo+d25wVx1MDAwNaJkN5O+9uY5yaTapvCxwk/FvbKGJHmgXX9EOVx1MDAxMshvnSSCKfztuVx1MDAxY/2DYcw43Fx1MDAxYbCCZUlcdF8yMJKWjWxbfkZLwVxu+NtcdTAwMWN5jjKoo/ubsqbzM1x1MDAxN4pcdTAwMDD9zlx1MDAwNDrsklx1MDAxYryWcNYjXHUwMDAzzMx4g6zUgFx0RtNw+bb0XCJQXCIjxKRcdTAwMTiaWMxcdTAwMGJFXFzrY6FcdTAwMTXYRfg0WFx1MDAxOMOa4VwiU9QjepGvXHUwMDAxwFOMoIQ7ptvgTGRcdMz/cEph/36HYU1uXHUwMDFlXHUwMDA0VsRcdTAwMTCmjKlvck/PICctRlx1MDAwMfLSriUo5IJoOGHQL47t/nJcdTAwMWa9UF9cdTAwMWJOhMlr57esmymQQDhBw1+2Vku1YVBjuSD9KT7itFx1MDAxNsTfXHUwMDE2qFwiwDJiXHUwMDFigl9cdTAwMDfMr/zBK3ukXCLdflx1MDAxN1x1MDAwMJx8UzZwL4lFjLuswlx1MDAwZeZEXHUwMDEz7EeTmZPwwLl+bu1cdTAwMTF/2aZ76CPQ1lxu5k5qq8u1oVx1MDAxOFx1MDAxZVx1MDAxYcX381x1MDAxYnadM63NKO36XHUwMDBiy5zALb5an1xuXHUwMDBmvC2QJOH2mzTwXHUwMDAyL8twM/FVdTZG3Y6YpL0tuMzzXaDG0VVcdTAwMDJ1S2subn+Uh+Fh3FJcdTAwMDJT5l3xL1x1MDAxNdD2f2SS8fxCZa6/XFzHX6GBZvirXHUwMDAxXHUwMDA1XHUwMDFlaKOJRNtwSnfpNXpcdTAwMDZDqN2z3vzl1zU004RLXHUwMDFh8fDeXGbR92FafXdsac9NI/iR+jP4N0Z/19I+Y1x1MDAxNNqvZ4xcbmaB023l9sjb6PtcdTAwMTRcdTAwMWNcdTAwMWZ/6KJojPvSRMrOfOu4tFxmrnlm+Muxj1x1MDAxZG5cdTAwMGYxKiMkdFx1MDAxNGzoXHRT/+VcdTAwMTUpM8cwTK/sXHLyy51kJiyD87KzleCmi9pMXkiFeL3zkDivp1x1MDAwM8J0XHUwMDFi5pdeY7NGslx1MDAxOVx1MDAxYat3cFx1MDAwM5FLx39MIDBcdTAwMDOGN1f25GhcdTAwMTXdp6dfz7j9ciPlv9xIpqm9p8GALLSlieVgXtLywlx1MDAxN98u9CF2XHJ+tz0y9p+92SHfm4z5XHUwMDA2XHUwMDFkTl9cbiqGdJPMKYVcdTAwMWOg7I5jdk8gPvagf2NcdTAwMWUyzsTe2olP5vpcdTAwMTjdbC8s8kkmrKxcdTAwMTSB3FximsfhkqQh6b7i+vOJ0HxwOY7l2Jrj7UnvQ1x1MDAwNyNcdTAwMWVq3Vo1p4lcdTAwMDNcdTAwMTc6XHUwMDAy7D8wNVx1MDAwMFx1MDAxY9ORt10/9mJz7fnwtFx1MDAxN5fypCZx+lx1MDAwNTHdI9V6Lnus5SRcbmVA9ZlcdTAwMTYhkbVcdTAwMGJ8e3GUhG3gXHUwMDE1Tfi/NYPHPsfuXGZ8vfYn48U+ZOtcdGCzYOlOStIj71SBkD9yxKdh4DUxj1/LjFx1MDAwMjGccvDvhjtbplx1MDAxZDTMlPlcdTAwMDbXeVr5nTJcdTAwMTG7XHUwMDBiTfJjvFx1MDAxMmJcdTAwMDI3S7zpsoGIbf2cxTy8XHUwMDE5XCJh0f7jXHUwMDEwv3xkxmbqXGK00oH7XHUwMDFlx1tU3SloXHUwMDEwVVxm3CZcbqbgTnxvXpDvbLL1K/i8YVFcdTAwMTFmRkotj2O4+u9ubKf+1lvi2Fx1MDAxZeW0XGZiNLHM7195W1x1MDAxZcpcdTAwMTehjz5cdTAwMTmjM1xcYN2xXHUwMDA12+eTdVx1MDAwZuxcdTAwMWTCt01cdTAwMWE9/i02cfwhRFx1MDAxZvZOdZA07n+y9W1DiYwldVxm8EBswvJC40w0L+pVR8Kca1L2XHQz+TL7mpD7mpSzob/IcWF6lOD6I4jcj1x1MDAwZlx1MDAwYnL951xm6Hzbjm/XiO2ZY/fmf7VcdTAwMGXi11x1MDAxZepF+y1Ouo1cdTAwMTG13MT3XHUwMDFh8rL/XHQ2XHUwMDBieKsqdjli5M9979SgPH/xXHUwMDA18TLXhv72Ms3a2y2Y71x1MDAxMKZcdTAwMGa6XHUwMDE57PPyXHUwMDE2WvM1enqDuyixur+dRsRT/aXxXHUwMDE3R39maGnioY7t3uDPyqCIpjFcdTAwMGVH+fnI+cNK9nX5PSdcborZP3aA0+2OZtsjQfBcdTAwMWWN17HXXHUwMDFmvf+enGJcdTAwMGIoXCI33fDydVx1MDAxYahcbvdcdTAwMTNcXFx1MDAxNJ5cdTAwMDOBVvaVlcJKSLztbmZSxX4xTmlxXHUwMDE115fWgsq9pFx1MDAwMI89WC1hXHRcdTAwMTOq02CEJONDP1x1MDAxNG1cYpfutVx1MDAwNFx1MDAxYWHcg/txv9CAc6x41nfJt1xm41BK/8vvrXJRbJ62PsTPcS5cdTAwMWRcdTAwMDTHVf/d7XClhSHJzGk19LCauM5UbLtcdTAwMDRcdTAwMDTAsjWU/u72aTbFc1x1MDAxNLJcdTAwMDOlXHR73lx1MDAwM2KLJ7VcdTAwMDJfXHUwMDBiXHUwMDA1szKis+WlJVx1MDAxNc5cdFRip1x0qDVBgvCra5+/PMm0WHU1blxyXG550f/Av6pcdTAwMWNbYJPBXHUwMDAyXHUwMDA3Um0/9i+ddOL8cqtF6i/nWYPwpWPaXmZkxENcdTAwMGaZbuD4QIlCZU+X7Fx1MDAxNF9cdTAwMGJcdTAwMTShcybYWVxyqVx1MDAwYrhP0MLD7tyPXHUwMDAymIrYnyRLm7VH63NMXHUwMDA1XHL163iQY1RcdTAwMTNVKaPJlVx1MDAxYmi19FXneYBPt3C/819qXHUwMDEzlP1e5ExIYsBa41xiwDeedaqKXHUwMDE3W2wku1x1MDAxM/zO+s3tbFbJnElcdTAwMGa7UVGS/rJAjX7WkNyk/fu7ulfUTytL0iZZrzg1kDdcdTAwMDfD9NFOishcdTAwMTFcdTAwMTlcdTAwMTW+Qrm2/Np5XHUwMDE0Um7GcC3Xb7zae3n6y7dcdTAwMTaZblHs985op2FqzHCqNleawUOKR7hcdTAwMWVhi+SBr1xmjij5IFJl9UONyIXwroRHQDrMI6RgP9tMuXBn/1FcdTAwMDfcVZiNJzrDLyZYrfeL91x1MDAxNa3129Qy8lmfT8Ph9In23/JcdTAwMTOY8lx1MDAwN+KM5lKj+Iqw+P0hXHUwMDAxc14o1CZRglx1MDAwZYVTe9Xx6Fxc5MU2jMV4zsFcdTAwMDB54Vx1MDAxNjVqXHUwMDA3jPzpXHUwMDA3M4e96N/ayuNPmOXoZJrDooN/3tPNVFxmXHUwMDEwNf2vzlx1MDAxN72DVUP+nY+0xlxywsrDnrDd6qLzi2xFdU8jxr8hKqQoUFx1MDAxYjuYlGqVnZvHr9ZfgjVXXHUwMDAz2EPGeYVVXHUwMDFiPpZK43ehWP07Q+v3XG5cbsBhm1xiPZTRcf5ccnbIeYWaXHUwMDE4WJikMMGqYpJurfUj2tiYfr8p95FcdTAwMTVcdTAwMDGP/lx1MDAwMVx1MDAwZney+oc2XHUwMDAyi1x1MDAxZk5LXHUwMDFjXHUwMDE4XHUwMDFkg04zOF9dTpjD5Ir675Df70jsdpJGfYXcjbRcdTAwMDL4alxilVx1MDAwMuz58b/6euxmV0bUXFwr3i2eOKY87fuLXHUwMDAzVkb1cjRcdTAwMGZcZq+fKFuDTyxcdC3BXG6Wx81zraor6Ju4glx1MDAxN5LJ2iSPKS6GPfmP3oA4ioc7xP54O/uukif0IbOsQj/u81xmsPu0+8PB0vRcdTAwMDD3hz+7IeN/XHUwMDA3uMlQfGjiRXnC9oaz2aXJV789g7AjmdTd5fCCUjpW2fZXLObRN8Ig2IH94p1cdHJk9sxV/TVcdP/2uvVcdTAwMWbn4EZ9gKeRaSnlZVxidrU/XHUwMDA0uaUrtX7Lv/uz3Fx1MDAwNbhmXHUwMDA0WtNB/1x1MDAxOG/U3ofgc2BOUsRgwYqtZWfuKcfpVnC3SvbUL+XNfVx1MDAwYox9XHUwMDFhpXCLOafCtrDrXG4zQlWI0+HnmZApn1c0y909ylx1MDAwZeZcdOXxXFxcdTAwMDTLXFzx3s+27exXXHUwMDEzVHhsNva3OUx/uck21z/DzGpuIODKzFxuzHpbcf521aBcdTAwMDUydVhnQkxcdTAwMWatMsFcdTAwMDRGI1x1MDAxZmfWvn0yb/Vg1cy632K6Yeb3w1x1MDAxOMLDXHUwMDA3XHUwMDE00dyfYFx1MDAxNnM/m9g5i1x1MDAxOeJ4glx1MDAxOU9uTsldmGboYK2HPtK1XHUwMDE2yjWD0Vx1MDAwN37LNHD4h9VcdTAwMTmBNkf9i5ZcdTAwMWH7LKH0V/aBpVx1MDAxZaz5nVxysstcdTAwMWY9XFzysix7UmbFbqVfoS5cIpYzntuF5Zoo3ub2YF6Ljl3HZ86BnsJcbkuyXHUwMDE2cUo91tnLPHk9eUR2xY5cbuBcckMomp9fXHUwMDFiVXVncI0t+OuqRIfnXHUwMDFlu/VcdTAwMTnd921FoWtJXHUwMDAynqFcdTAwMGY/0fdcbvOlhfhHpzX3r/yp+MvdPnP7X/v068eO66jFXHUwMDBmooVcblx1MDAwMWFcdTAwMWRnS/coNlx1MDAxZVx1MDAwMy3aXHUwMDBiI7dYKENE9cFcbsUh4rDf5lx1MDAwN00k/+lDXHUwMDBmNcwhMYlHXHUwMDBi9zc5h0/IlNxcIlx1MDAwMov4/k32Xy5/yVx1MDAwNi2jeFwi7zNTpX7tVOaIXGJcdTAwMDSvZ3JcXMI/dppcdTAwMTJaiIy2cEVcdTAwMGbL2neqzVx1MDAwNihcdTAwMTSnn+5UpMiOXHUwMDE54XrttScjfXoq61TsR1x1MDAwZSRPxL3x0uLr2nNC/Oii+bH3wjxSuXj6KbRcdCGM7YviXGKBUIfXt9NcdTAwMWZOTfVcdTAwMDSGQ1x1MDAwMPfwXVmZXHUwMDE4L+anyFx1MDAxNtirXHUwMDEyLm5h4LOReZximTVcdTAwMDJcdTAwMTVpcPGqvpPXXGK1pCBdXHUwMDEwXHUwMDE5V3xcct5cdTAwMDI0sIj6n7b5vzMsSlx1MDAxYs3+dszvlVf7Oz/B6jjUtJiGXHUwMDE5XHSiXCJcXILcyDIwYHYw2/w7sVx1MDAxOOh4J1svQZn+xLDPqYvQd15cdTAwMWaZ9Yud6qm8WEpcdTAwMGJcdTAwMWPjrCRUeIKxOGJHiL3KxIJcdTAwMWF5XHUwMDE0XHUwMDFiu1x1MDAxZFxiutxNXHUwMDFkc01bXHUwMDBiXu+6z1x1MDAxZl3HaL+Uif7RNUsxQi7GXHUwMDBmmVpDua0g2XZOYyFcdTAwMWIj33/NsdrrmTxcdTAwMGXlaK/4ICuAc1x1MDAwNdBcdTAwMGVbNYVcdTAwMWZ19Xmre1x1MDAwM4J7LU79XHUwMDFmh09cdTAwMTOVXHUwMDExXHUwMDA0t+F8d1x1MDAxN5x4q6blwpCq/T5cXCZ+jUv1SDv58j/NXHUwMDEyqX5cIjvOw61/51x1MDAwMznLTpRj5Zr1XHUwMDE5UCC/yodcdTAwMThp/lx1MDAwYtqxKdCwXGZcdTAwMDDtso6J0oTMg1x1MDAxZdkgRd5jzFx1MDAxYtJ9NzHe+lx1MDAwZX9cdTAwMWGnnDC/XHUwMDFjJ411b6ZduviXL8L170ieRbQ2wt9cdTAwMTVcdTAwMTDhMSl/a1x1MDAxM+CvTlx1MDAwZlx1MDAxNlx1MDAwMCUsToTKMolcdTAwMTS/j1x1MDAwMyeYXHUwMDExyWiNhYN4ezdcdTAwMTZ4rDnpPFxc46NN+4K62Fx1MDAxZCptquJzVOuvTTGs2tD54Fx1MDAxN7hcdTAwMTmnfZ2FXFzbyVxuqHYltmYmoihEnvZCvJqJl9zTcYffXHUwMDFhLuuJLoaMha2T9I6LJE3QuXmEXvYliGWNX6HS72P2wfuqUVx1MDAxOD20L964KTFcdTAwMTHnV1eIXHUwMDBiXHUwMDE451xy8Dv/xExcdTAwMWHzW1x1MDAxZvssXHUwMDExO4TbWzbgw37oQpZLM1q7wt7St/d8TqEtXGJ+2/1xXHUwMDEwQ1JcdTAwMTXS8Maa18VcdTAwMDeeYPJwP72Jklx1MDAxYnrBQVFcdTAwMDFcdTAwMTQ1e08geVtcdTAwMTSrWy82RTuNx1x1MDAxMkJh/s5f9U97Rlx1MDAxZGmcXHUwMDAz71x1MDAxMz9cdTAwMTBafSbbnkeyy0uIoVx1MDAxZTLY3uzCtrH8o5u2nlx1MDAwYnxcdTAwMTH9cVx1MDAxZoDtJyPJXHUwMDAxTn0kkKNcdTAwMTjEVnpFM7/BrtHd4/tcdTAwMWWgg1x1MDAxMlx0dWa87ms96Fx1MDAwN1x1MDAxYUpv+MqvXCLYL1x1MDAxZSDMIDrMSFJOXFzNL8A6abVcdTAwMGbAgvTiXFyfUimnXCJcdTAwMTFnXGaHtWmbwaxS6JjeY351L1x1MDAxNflcdTAwMTG6q/9lJ6m0XG5SskBqvVx1MDAxZfBFob5hXHUwMDEyXHUwMDAzXUNDK+/HZCfzq1x1MDAwYnDpw272p3Of8U1cdTAwMTi3ndDfPSVcdTAwMWb5/lx1MDAwNGFm1H5HNYrgTUV3SVx1MDAxNUFzR/F1MHTOfFxiiMTAdiHo5EpZQ+etVbijebCyXHUwMDE0dN1dsthuXHUwMDFlPaGov4uczt+VSJfgi6on5PHOJHbxq7dcdTAwMWFcdTAwMWJ3evHpLLHKPy3M2Vx1MDAwZvhcdTAwMDGMU/WtfNg5RvJbfSVCR8WtLlx1MDAxMn5rtoj8mVx1MDAxMVjiv0xnVPfFrYJ4Olx1MDAxM35aXHUwMDFjcX50/yv8hVPbYJ5hcXLB7Ehw3OLNbCxYXHUwMDAzK4ZcdTAwMDdfozLme2Vh1zpcdTAwMTCips7nYVJ8hlx1MDAwN3Pxm+/6p5mZlyae5OdMXHUwMDEzitJNXHUwMDE5YE96/Fx1MDAwNvNJZpRshEhcdTAwMTJcdTAwMGLIuVx1MDAxMJZPSVtcdTAwMTJNSODJ/WNcdTAwMGac+p33XG7gO5z9Mo+yOza/Pievjq/Xb23dmXFa+mzZo433SCg9dUKGRrCJ0SZh6eswbzrZIN1J8nu4RmPTOyWZ3rVngizEvC7hzfTu3zmnp7+vZOf3yIV3tFFPXG5PlTeZ46g+XHUwMDE5z9+GPd3VXHUwMDEwslxcd96JgbJzMJ15VK0juosvwtxlKdWNp+dcdTAwMTR5Slx1MDAxOfaw61xir/Y8L8dcdTAwMTCLX/+nX6y2W2HS5qdL/IVTtf2t8JZccn+XpurG+L1tRnuUZ5IvvvO7XHUwMDE2OnUo4YfTfnlha+io/lx1MDAxYdpcdTAwMGaxv3SIf0TmaIk1XHUwMDE0t0/M5lx1MDAxNaiOaq+DSiuQO0x6/vxcdTAwMTfPO1x1MDAxNNc537NcXFx1MDAwYqpqc1x1MDAxYdxcdTAwMDBcdTAwMWSoXHUwMDFjXHUwMDE5JYfjQiFTz9i8eWXf1zuqfS29uD5XqLbJ3r5mn4L4XHUwMDExXHUwMDFhQVx1MDAxMfWCXUCXXHRHKf5wPJ9/afbrKq+R+WC2wshccndDy/tcdTAwMTZURyjkXHUwMDE4sCQ+aPG4XHUwMDBlf9ekv341XHTNUTilXHRV3rnJyVx1MDAxYZBS5fSxZL1nRJxmo1x1MDAwZVx1MDAwZWxcZo9cdTAwMWHJeVx1MDAxMbloRmbce6FOXHUwMDE470aHXu7cXHUwMDBiXHUwMDFl4+rnq25cdTAwMTVlquKQY5Z3XXwthdCwx+OUmyHa2Vx1MDAwMLpcdH+0gqg4PVx1MDAwYnUmPFx1MDAxMiHtYIdcdTAwMTU5xmG9clx1MDAwMHS+O3BRWVKxnFx1MDAxZrWiJI/Do1ZcdTAwMThUXHUwMDA3XHUwMDBmU4HpUqiGXHUwMDEyw1x1MDAxOfC4RpyCXHUwMDE3W61cZlx1MDAxZH3Jw3JKaUa1TriPve/17fkgbUvoXHUwMDEzQu7lykosL3yZ9Fx1MDAxM7XAXHUwMDExfUxcdTAwMTlcdTAwMDRcdTAwMWVWkVtcdTAwMTj9KzHVgNb43WVcdTAwMWVcdTAwMTBZXGZ4XHUwMDE0m9bsL2aLXHUwMDE3fjK1OqjFxSiOXHUwMDAw07yDNME1wSOIXHUwMDAyrVxi1pPbXHUwMDFi6ShcdTAwMGbUSvy8lFx1MDAxNVx1MDAwYvmwmvbzrSHgecdcdTAwMGZbXHUwMDEw67081lx1MDAxMkSoiVXLXHUwMDFlgY4wlVx1MDAwMqNYtixQxV+uzf62a65u0dUrcVx1MDAxMMuk72ZcdTAwMGXcw3jpdbtVTbSB7PUlqTvPW1dcbrnRSqPj7Xuzhqyh9v3tcH9aW6u+41x1MDAwNauZ3/vhpHqHXHIpns1NPT29YPfFSb9aRXH9MktcYkrlh+iblqyUXHUwMDFm1SCb70BV235/8j23mm9cdTAwMDc5XHUwMDFlTl9+37GtUNf+MbmCM8xO7EFjXHUwMDA1707o+nBcboKwXGZcdTAwMTVcdTAwMTK2pXHHnIL/4s6hQH65gjPrc6Qpid5asZNe0ihmco/WVFwiSkZG0M9cdTAwMWZcdTAwMWVcdTAwMDF2XHUwMDE1WtBcdTAwMWZj0q68P9uOzlx1MDAxNJmNhdA+3UVlXHUwMDE0XFx3KaouXHUwMDFjTnR2XHUwMDFlU6eHXHUwMDFimPx3xqNZ+e7qbz3jt7yci1x1MDAwNscugcrsaqE85CA2kEq1zFx1MDAwMbUlnVFNtUdjXHUwMDFlkUu9icDLpDcjlqJHXHUwMDE1qNtFMVx1MDAwMuHWvlx1MDAwZTI2y21+ppZcdTAwMWNOuJVcdTAwMTlLcvjhXHUwMDE4y4H741xcKVx1MDAxZVx1MDAxMkTOvFx1MDAwZYtcdTAwMDGC+31/LvrQZlxyXHUwMDEybVU//db/rfPdMiNcdTAwMDSnWLK3mstcdTAwMTees4DxXGLG61vz7NtkXHUwMDE5JH44vPBcdTAwMGK/mi9cdTAwMDXIesfsy55O61x1MDAxY2NcdTAwMTUzWzb/qvUrNZxn/nuvTlx1MDAxY712e/0xWJGyI2ZiLFx1MDAxZc9EUHGu9ztcdTAwMDJiylx1MDAxN6mkq/9yNVDc8+qBy21cdTAwMGXuOCX4IHVm7ax2k1rqfO7+d1x1MDAwNP9v3exg9SZnxIfNzXmEyS/+Zt7mfY6naUsmmOMlTJSoX1x1MDAxOFgrcKny46Q9/Fx1MDAxMn61XHUwMDFi6rfSxyzGZmzZrC3Q4lx1MDAxNVx1MDAxM+ZcdTAwMTRcdTAwMDdvL2ZcXLx5SuTrXHUwMDA3ylx1MDAwMiOhrFozNp8y97k4MTh87ceaXHUwMDFmNfWueZOraVx1MDAxOTGvfFx1MDAwNW1yLsCsXHUwMDAwx5UpxY9Hs48u3zm3ZvNHPCrwxTLEXHUwMDBmb/3JZUSH65iMyYVLYm6dKK1cblx1MDAwNEROJyV0M4Bm9aRNrlx1MDAxZNZkSoa3XHUwMDBlaFx1MDAwN68vXG6G91mvR/GZXHUwMDE15dE0PnOC5lhcXFx1MDAxM4PsheNcdTAwMWE2c1x1MDAxMZqD6HRsOoFAhI/vgkmxWvDL2vI5XHUwMDFmXG77d6Pfi6l7QDltulBH1q7eRo6UgLRXXHUwMDE1UlZKeX9rXHUwMDEyNcXjWNvn8/5I4PrNJqVWXHUwMDA1xYRsTFx1MDAwNcCGhDNSXGKYRyCHxXFz+i/G/1x1MDAwN8f/jdVcdTAwMGaLPVfHxNrW2i2V615hnWbq/4uz/9P3OYb+y1x1MDAwZk3wcaBcdTAwMTLp//BcdTAwMWXK+9UsUFx1MDAwMdZcdTAwMWaN///8/3/+/73nOSZgX543Y9SZXHUwMDE2Klo1mGL4+X/4vlx1MDAwZShcdTAwMDP3XHTeLyiFMzmJXmQxk8w1wbKcXHUwMDAyqlxiooGeXHUwMDA1/H/fW7VqzVx1MDAxYnJcdTAwMGJB1FLvr1x1MDAwZlx1MDAxM5ymRVx1MDAwNkt2x89vuIaEl85cdTAwMDfg5P/tNzpcdTAwMTbzWeNcdTAwMDW+0/pcdTAwMDbit2ed+klcdTAwMTIxTlx1MDAwMPvCQn/1On75XHUwMDBljK3hOTFcXFx1MDAwNKGkvkKxgKpcdTAwMDLkJPwv/a8zx/pXP4XmpVr4y617Q1x1MDAxYyE2THWwpeCdxUgy7zfkvNh/2k1maiyntC189cLlaC3zkObbmo7+zWC13nCSzf/WWVju4SxA7Z6UTjeE4mRTKKg0XHUwMDE4r0403VBSfZR/9T56u97sMa8ovoFt9WnMW6kg0LTXgTtPXpg/XHUwMDE5XHUwMDBl85TzT8fVTGp0XFy7oJnr8/bXZ/6rs45nJtVWzZl39oBI/7dcdTAwMGV1/mGYfcfvNv/vXHUwMDFjyr9/K/j837xcdTAwMWJcbvAvL44yU9v5N1x1MDAxZZ7D/8bjPffLa7b/1VxiUf/VXGIx4//Yg8lccr/1XHUwMDA3qlBtXHUwMDFmKv4959jdb8/1Klj3hP5xfGXm4r89x7ZU/vr6fldcZvNWNlx1MDAxZZCN57t/XHUwMDFie+rU/J77+mcg+dNff3JhZ1jGK2ThP+85eJdTOaaU3tPivv49p0zOr0bB07k5/q9ccvvNo1FsXHUwMDAxdf5vc/vE/Vx1MDAxN/RX2yBZUo3n/P+s/Vx1MDAwNcyvXHUwMDAwXHUwMDAz/9/YXHUwMDFmNfNP+1x1MDAxZbznw+x/n/NcdTAwMWZRyiqvzPNff/P2J/Tej4rgyLr/1yZjXHUwMDFhf30hc6rp4n8+00vxv+dgQ2x+69qPXHKhv1x1MDAxZvGfj+s/79FP6G9NrplcdTAwMTdiqv/znPJbkO0lcV7e//VTVnncV2Tp0/7XJlx1MDAxM/J+fbn27DywnHs0Yl+EO2zbJdU3qE1QISNv76v/cUTu3br267uJXHUwMDFmM4FcdTAwMGbj0V6tXHUwMDE47LR99HjKe/u76vJdXbXMlL+EMlx1MDAxY58wMnjTwdSMXHUwMDFmz0/usCE5t3iByzyoTtDAv7AvW4fb6HzK1+/sznFcdTAwMWFcdTAwMTMo/Nmd/FdcdTAwMWJpq9eqe4dcdTAwMTfdXHUwMDFlXG5cdKdRpmHsgZZcdTAwMTAhR1xinv5cdTAwMTb5so3oznLfRqhpieIr1mBLl1xyYPjYKzHESlx0VjK46LFW2sa/XHUwMDAwmlC6ry9V7lxcXHUwMDEz06mMyGRIXHL30oJffmt33WlcdTAwMDOFVreWXHUwMDBlMWhQxMaN6Kz4ZO5kdVx1MDAxMlx1MDAxMedcdTAwMTOhofxyj6/v19V+c5x3f+v1sy7jwT3V5Ou1XHUwMDA0vsZHyVx1MDAxNtyqgudMRtCOzY5cdTAwMThw6rmji4ZV06xcdTAwMTR8qdSpLSZcdLNlX6VW5Vx1MDAwNphAXHUwMDE4Z8hcdTAwMWb7VNFcdTAwMWNcdTAwMTJcdTAwMTivKsjMNVL7mZnz4X1cXMLxj6n9+Vx1MDAwM/P6jU1fXHUwMDBih5F+UtZ331x1MDAwMktG32vr1aFeg/20RJJFaqyGXHRAPfFkVstcdTAwMGaszzezglx1MDAxY3x2m0BcdTAwMDFcZoglbCu7eL5cdTAwMDKyXHUwMDE2O918Y/tcdTAwMWW8n2HyXHUwMDEwXGb2XHUwMDFh0NE6NZnIhJthw1x1MDAxZphpqCH2t/PVc1x1MDAxNybz83Slj9FE3oeoemqY0H0h6tffUs+pae1fnHgsUPV/vJJcdTAwMTmvXHUwMDE5TopacszkpXVcYv1Bxelom29asNqVvlx1MDAxMSgwM1x1MDAxZnM4X5txQFx1MDAxY8JZc/n7WDKJjqzFkLOx11x1MDAwN0Hh/4rlKJ3c11x1MDAxYfYvhtyu+KtcdTAwMTmP06LWfFx1MDAwNoU9/avzlrBJ3FfTrpzf5kJMS/JcdTAwMTNzNuv5XpB15NLXXHUwMDFk9otzXHUwMDFjTzPzQ33/Ysoy/K1LaD3AfGiUVFo2RuDhvlx1MDAwMEv1v4ZUOdKrz82czV1cdTAwMWH4XlxiXHSYV0FSXHUwMDAxS+nGrd6JXHKKLTNP0CaE3zxcbuhcdTAwMTdcdTAwMWOKIZlvqVx1MDAxMeRNnMFcdTAwMWXf0N7Kp7Tz5Xh4OFx1MDAwMTJcdTAwMTafoVdcdTAwMTfRdVx1MDAxY3pcdTAwMGKTt2fi1pRcdTAwMDMzc5hq87I1oJRfi1/oqU6m7Iq8vqvwMPvlUVx1MDAxOLmrLFx1MDAwMew2SVxymXqypXllhpghqWabSicldNp+6lx0YN7YXHUwMDBiKzRs8rFAXHUwMDFh+okvrfRS8/ZGTVhcdTAwMThcdTAwMTO7aPFgetyxyq5uWFvrULxcdTAwMTf1RNgz5eF2U9pFpspcdTAwMGWZ+3ChgNfBrESxXHUwMDExKlx1MDAxMNniXHUwMDE2zs2FJ1x1MDAwYiOm6+PH13om/ieY7U25uVWZckRPr336XHUwMDEyxrjyXHUwMDFkXHUwMDE1vpyMxJpJnoJcdTAwMTeabUEof/h2N2U9ajFcdTAwMTBbXGat/oxcYoWwxPdcdTAwMDVOr/B5X3/lSPa8b9JcdTAwMWZcdTAwMTDWXHUwMDFjckOweupfwFeR/DaDXHUwMDAxrLXQRqmx0mhfUytUztmdRHE6SzBcdTAwMWHVXHUwMDAwXHUwMDA2JNlqJ3gyXHUwMDA37MFcYqqcuVx1MDAxOdU+f0U2caHmJ7eo5tUvqL6NdnaE8q/exoZcdTAwMDKkt+bwXHUwMDFibXPLjlO65DJjwFx1MDAwMu5rucRVZYhzXHUwMDEz399cdTAwMDVcZlx1MDAwYkN8yzW1MKJEgDgv1Yr5VfraWvxNo50lzaFIc6c1cPsmIVx1MDAwMCGr1j9cdTAwMWOB/1x1MDAxN1x1MDAxY5HmjY9OW1xiUTuLMES8Xa/dob6RjKY6ejZf/VTkXHUwMDA3qJ1fNfxZMESvglx1MDAxOFBXpoYkQGL2f7ndqFx1MDAxNJs8XHUwMDE2I5l1XHUwMDAxJt+5L080XTB+ayBzfnpcdNaGmaJcdTAwMDKlZEJamI4p5bZcdTAwMTCUYIFcdTAwMDCy23FyPah+WVx1MDAwNVaYxc2VXHUwMDA25cjX6Utepcax+omoXCLBT+rSOsXF+OpcdTAwMWT6tS59X787Slx1MDAxNHNVJfSt4MyuN8pFXHUwMDBly5o18Fx1MDAxYvLCzkfjV9DyzXf8WsiGrkxVQXHnXCJx0O2kp1Tg14mWb7HyRFfIWFwiMfYyVFx1MDAwM5RcdTAwMWGowWndyJZcdTAwMWYkU7S3tX1f4oRcdTAwMTRhUq6arVx1MDAwNlx1MDAwNJD8ilAqXHUwMDEwUa2lJo/Il0L0iPpcXFx1MDAxY5SVb8xcdTAwMDLIPCil4ngnK1wi2SQzV1x1MDAxOPSusYrvV06ljO3T5IVcdTAwMDXQkVx1MDAwMptoXHUwMDAyPF0rr1x1MDAxMjlcdTAwMDD5hVx1MDAwZrtcdTAwMTE62Om+3iVDIHPwXHUwMDA0XCJcdTAwMTlcdTAwMDTO5Fx1MDAwNSRcdTAwMTP/pY5jL7Qgk19cdTAwMDCdrHZl3ex6dVx1MDAwNOXkpf07/uF+0i1rJ1nLeFRcdTAwMDVcdTAwMDNNXHUwMDFjX+9cdTAwMDcz91/dXGbW1fPU5Hp3XHUwMDFkO1x1MDAxNKtcdTAwMWOXXHUwMDAxolx1MDAwZVx1MDAwNy1cdTAwMDakXHUwMDAwXHUwMDE5UtBxXHUwMDFkQedcZjzlbTnXf9ZcdTAwMDda+49DXHUwMDFj4vlQXHUwMDE0ttR3uqeBVK/V7mtiYFx0hmdcdTAwMTOhxcElMU178OlcbnyhXHUwMDBlsnt+aGxhs1xyQTp758ST6FxcOVxij26N4HHUXHUwMDFkJm9CXHTmsFx1MDAxYVH0XHUwMDBmZopx8qKeQYPLcnOnqzc7IVx1MDAxM8dcdTAwMDPrkvzBXHRcdTAwMDB7YFqT8Fx1MDAwMl7Pc/3oXCJcdTAwMTLtXHUwMDBmVVAvTUyt7NX7xlx1MDAxM1LDXG5VeHf9XHUwMDAyJmxxiimX0pnKXHUwMDEytancl1x1MDAwNuBcdTAwMDe1XHUwMDFkoqWs6n5cdTAwMTT9Ts4wvIiI50ua/Vx06PmKPsY8n4RQ53V9Uvx+avTaXHUwMDFm6lx1MDAwMyn19saTbE29YKKbwte+6vV2dLDM21hcdTAwMTfPY6InfMOyq4/tmthkYXVMiFOWm1x1MDAxM/vSum5cdTAwMDFrX1x1MDAxNXnEWi8nw0txdURu+IugLyzSJ5cnUzvKQK59xVx1MDAwNm6iXHUwMDE5m9tcdTAwMWYkXGIwSqWS39nyKYhcct6dI2HjLjkuODb80i9bjJYgzj9cdTAwMDNisejjJzTud1hcdTAwMDabsVx1MDAxZMZPXHUwMDE4O9VkU9/fTHBNY6Io0k5q2iZcdDv5rN2ca63AXHUwMDFhnZNCqflxlsnX4LvW9sgpryzORe3RNNgmdlx1MDAxY94qof6wpPM988lcdTAwMTGkxkd6pjvdOq2OIyjQZveLrfbMb1x1MDAxYlx1MDAxMuriktlXXHUwMDFiXHUwMDEzUFxmvvvzXHUwMDFhiafBLPXyXHUwMDFmufPIXHUwMDAxxEinZX1lZFx1MDAxMLn69uZByFtNhFx1MDAxN3VcdTAwMTJcdTAwMTRdb4OvN1OWZC5PqK3udntyZ4OgXHUwMDE3XHUwMDA0SNN+ct1cdTAwMDJlKcx16ZRcdTAwMWLv011L7jCNXHUwMDFiS1x1MDAxMz2WafhwjMHdWKshV9NkXHUwMDA191x1MDAwNk/YZqB/2iTgilx1MDAwN3/1YKZcdTAwMTiUwe2n43DGRt5cIrevrV1cdTAwMTdcdTAwMWba9bg6Ql0ls7VcdTAwMTU6UWpcdTAwMWX/uCSdPYKHZ1x1MDAwMVx1MDAxNqNcdTAwMDfpUXdUmVx1MDAxMlxmXHLOsq2h79xcZiDB0vui/Ybkw4vDgOh7JnlC/9umv1x1MDAxMV7vx27ZMvymXHUwMDFlNLd6LND29aTxrX7fXHIh4IxnwWzji/xcYo1tXFy8XHUwMDFmXHUwMDBiclx1MDAxNnVcdTAwMDCUtUBcXOuAcZDPKqqyKczGXHUwMDA0XHUwMDEz14KVYFxyw0JohXGOTY2yJIvuUjMz8YntzmI4zFx1MDAxN0I+TygltteuyPpcdTAwMTBIMiqOvaCL5fdcdTAwMTlAU8NcdTAwMTSXLLCXdFx1MDAwZvWP477YXHUwMDA2k1xy4uOAUmvsZPQ5XlN0w+F7oYEqVq6cmKk8PCNBO1dcdTAwMDKnLv3CXHUwMDE2XHUwMDFktCApXHUwMDA2JT1H3IVZt2mVZlx1MDAxYvkkhzGJrLTzZKxT1FVcdTAwMWQ061ZHkXWWTYA61nDH50wzcemcsGlwKjpcdTAwMWbD8ku9OFx1MDAxYd0lt8qxXHUwMDE0ekGL2U5ecSbvytvRVLbqXCJL3lx1MDAwNFxcXHUwMDA1e6LEXHUwMDA36WN7bul43WHr0fzQsVxcwJC3Z1/xRt/JMqLRl4tz3m1cXEiWq33bWUTeXHUwMDE5XHUwMDA3gKU+0WugXHUwMDAw5bNq3ofZQlx1MDAwMGVcYl9g37CRb/zuXFxcdTAwMDbj1VRcXG5cdTAwMDWxPp1jzJ7VZ0NccomxgErLQkP+nkvQvWTeueKXXHUwMDFjesIlXHUwMDBivfIjkFaWeH9nXHL+tJ76T+s5XHUwMDE5Y55cXN2hXHRJi8YxNcO580thXHUwMDFmgFx1MDAxNaLcmO1Ik1x1MDAxOb9aLVx1MDAxZrM2lkD2V2NcdTAwMWJcdTAwMTNaiLd4iaJcdTAwMDFEhlx1MDAxY+z9sUUgXG7L0qKRgFx1MDAwN0uEWidMNlx1MDAxMYtHbeSrXGbz6lxcyIPYZSZYTn5WqmQ43nhcdTAwMTcmqkypXHUwMDAwXHUwMDEwaLbOr1BcdTAwMWRob83prFx1MDAwNVx1MDAwYqEqJUipXHTveH5HOr6kN9DUfHM/vlx1MDAxOfPF7uGdJddcdTAwMDO2QqxhPlx1MDAxNE5GXHQu6XKwOcJldDKCXGYpPXJkXHUwMDFk1Fx1MDAxOcJcdTAwMDFcdTAwMWZcIlx1MDAxZr1veVx1MDAwZVx1MDAxMp6jXHUwMDE061wiVNdxUSRcdTAwMWJcdTAwMDK1XHUwMDA1a+nrtIckjKtcXLRNfuiOy9W1sNOmKn1BlHwtg7a1Jp5cIrlEKLp6z5H/cFx1MDAxMaaPvGaxKThcdTAwMWFcdTAwMWRcdTAwMTfXbzG9p1x1MDAxNb2qXHUwMDE1XGZjY6B7vNUj+1xmNOIjXHUwMDEy7susq3mPIMwtXHUwMDA1KCUytmnKXchcdTAwMWFcdTAwMTNcdTAwMTRA/oPyXHKUaodcdTAwMThcdTAwMTCvolx1MDAxONwlyeVmQmXXPsDsXHUwMDAzn5VRfoKOqvajkrFMqU1cdTAwMTJ1UEr2R8WcXHUwMDEwwVxykYwvelx1MDAxY0rxXoCweTJlOG1KXHUwMDEwXHUwMDFmhWwjrXfFXCJcdTAwMDFV0WmT26SKgbnHXHUwMDBiaVxccptn1dX3en+VpXfkpLRcdTAwMDZjZm/NXHUwMDAwi1eTY//yXHL0f/Vkulxy84+29OL6PVx1MDAxMlwig2pcdTAwMDMo056c9IPSvj/FcM1iQ9GCcM4j2lx1MDAxZIzhvG9UMcbRXHUwMDA0pK1cdTAwMTK4OGQ1THBcdTAwMDSdzUtCgMrQy8BccvJcdTAwMTJqwDRcdTAwMGJEYoeza5lcdTAwMWRXfdRcdTAwMWF7k1x1MDAxM7oxL0CvhpKmOEMhXHUwMDA3j7Qh5pRuvHyI41x1MDAxNdZnqyxQbKy2YuOcgqWvdlx1MDAwZVx1MDAxMDXnK3l5Qm7douH3wjyLIYYy2lx1MDAxY6pcdTAwMDFnyEhcdTAwMTnVXnOnhLhprKzY+lI1cIh7hUxcdTAwMWSuXGJcdTAwMTV0r8PDQNWQXCJ3flx1MDAxNVx1MDAwNvdcdTAwMTOTXHUwMDFm45iJ/kyM+nt7JVx1MDAwN9hfKsP4II0rXHUwMDFktVniWOlcdTAwMDZtaMfvNGx8LFx1MDAwM8shU5xLQFnIZX+4x0BcdTAwMDCLr7f+N0+MXHUwMDA1g6fAXHUwMDAwv9+eJ/WZRWbgXHUwMDEzwVx1MDAwZl9cdTAwMGbXks6c3Fx1MDAwZdxjXmVAJtBcdTAwMDOYYTog+KOWXHUwMDFl9PRNyz0u71xy01DeY6xCtrlcdTAwMDO7/9aKZlx1MDAxZXs0ITdbZ1x1MDAwZYQhXHUwMDEzXHTSNU5RYVx1MDAxZIFVYHe+ITr0xNKJ7r57xOe1j3UwpH03YPWA/KJcdTAwMDVvW2vIwegkN0V3lL2+hiHe+j58XHUwMDE5XHUwMDFiTfNiO+earupHXHUwMDFmi/d4nsFcdTAwMGJocc5cdTAwMWNLICG2XHUwMDBlK1x1MDAxZn+Vv+CEcZaFve1uxXNcdTAwMTNYcfPr636wQ52JTP5bXHUwMDE50+O4NDBOTdwo98VcdTAwMGW3cF9UXHUwMDE2MN5bJKjMqu1cdTAwMThbe1vzcLnRXHUwMDFhXHUwMDE31Ms/avdi+pFcdTAwMDBLMT28iYKdNOFfXHUwMDE5XHUwMDEySL9rXmMoLcdcdTAwMTTHXHUwMDExJlx1MDAxMeOv/ibK8tRLOaXIl6o2OIwwldzRUFx1MDAwNpFlSIxnZnyKXHUwMDE2OYEs8j93jEPheeVXXHUwMDEzkZ1cdTAwMDFF6J5xXHUwMDA2UegmLDmoMcGCfmNHZE8nlVx1MDAwZV704y1yXHUwMDE3LmvfxN++JEnTqKEsWFxmpVxyQGaMk6ryXHUwMDE28V04XHUwMDE4vMjcXHUwMDEy5jctRU8stUA6nMLDSnDdummTXGZcdTAwMTcwWVwikLSxXHUwMDAwRcky6Mj2qG6xJFx1MDAxOZ2DQKn/XHUwMDE1rlwiLINtZFx1MDAwN1wiqHl+QnSvyF9cItUq7j86XHUwMDFmcmJGcNQl1dffmdtSxV94JVxmXHUwMDFjXHUwMDE1KV6EMuC6P1x1MDAwMFx1MDAxYtkq8H5bXHUwMDEyNfvXOtU3Kf9yotX00WJHXHUwMDFmY6fq4WbcgZwhXHUwMDE1IWfg9FxyWjtbbyCQgZayOFwi+HrzXVuNn9pcdTAwMTN2oeec6X+w9Vx1MDAxZWuPKkva6Fx1MDAwNTHAu6Fwwlx0J/xcZu+95+pcdTAwMGaq2vs/3b3WqOqR+CBcdTAwMTRcdTAwMTn5msxcdTAwMDAu3ZSdQzTJhVav+7Ci0YxcdTAwMDBMKJnlu9PhnChOlIvB/qW9YFx1MDAxNFx1MDAwZqVdo4rE6SdpoJ++W1x1MDAwZWNJ71Jwe/3KXGKAmoL7jENcdTAwMTm3ldE0s3HHYFx1MDAwMlx1MDAwZsL5ss9yai8+ezR5lFPPiIe0Wlx1MDAwMVx1MDAwMOZapMxsXHUwMDE4d2hvup3g8qBWXG7raco1KFxuXHUwMDFh+sQoYfjT8iTG7OX6p/vpXHUwMDE5XHUwMDEw6Vx1MDAxZVL20OdcdTAwMTNnmN+m1ZItqC5dMcveXHUwMDA0QoZqXHUwMDAzMlx1MDAxNY2iqFpcdTAwMGZcdTAwMWbxgUIgRbAn31x1MDAwYqg/7Fx1MDAwYmVFwvOaOpKXulx1MDAwZlx1MDAwZi+IJZ1GyPNcdTAwMGJcdTAwMDNcdTAwMGJcdTAwMWSH1Vx1MDAwZfJ3vL06I1p5XHUwMDBihFx1MDAxM1xuzdX2UPE2rz9FI63gc0KHw0Ajqtjmr34rWO2n34px+d6mMyWT/PCoZVx1MDAwMc2FRPdcYpo6i3w7v1x1MDAxZYCTP1x1MDAwMDiLJVTd/PdEoUMpYFx1MDAwMG+5Lc5vtuRFmdBcdTAwMTjHXFzbXHKLVdXXQYxpXHUwMDFmj1x1MDAxYct5k79G91x1MDAwMecrY1x1MDAxMstHXHUwMDFm1Vx1MDAxMnRmc425i2VcdTAwMDVwXHUwMDFmanW5XHUwMDFmy/PVuO+xXHUwMDFlXHUwMDFkXHUwMDA0KeJcctZgXHUwMDEyMsXXg+RN6n5H8dAxYuZcdTAwMTa85OalK6DaI1x1MDAxOYVTMUTxM7p1kmtcdTAwMWVcdTAwMTn0ZHKcwF4wnEvKZD9cdTAwMDOkKVewwVx1MDAxNLVUjiFRvF7s6/V1LMY17oRcIsjvXGZqLqH5aJfaXHUwMDFk6ez6k4czqOmyXHUwMDA2vNnzOSpkXHUwMDBlIHuI2E9ph+yYbFW54bNTPHWlhW5cdTAwMTlcYvnWXHUwMDAwXHUwMDE2iVx1MDAxN6TTv1x1MDAxNtuRiGBcdTAwMDBcXPSI8F5jP1x1MDAwMuW3y1rfKF48NkSlt0s9gU+v1Vwim/Tigkxfl/xGOF9yz7xcdTAwMTCQmlx1MDAwNlxiQoT9gmvHXHUwMDE1QCHuc/tHbjkgoopHhVx1MDAxNftcYlx1MDAxN6dBXHUwMDAxUvG2eon+vXtaXHUwMDFlnFx1MDAxNP7bL6kwv35H6819XHUwMDFloze3r0Jq5+9iO1mcY6/ZonHhXHUwMDBi21x1MDAwM7TpofBcdTAwMTZGoLSQ+Fx1MDAxM09U01JNKnfK91x1MDAxYfRDtEBcdTAwMTAqbrmu9CHjc4dDPlx1MDAwMLRcYnpcdTAwMTWNXHUwMDAxJkzRklZiozKGRamQpIx5P7RcdTAwMGK5Ulx1MDAxNujRXHUwMDFiTr+6XHUwMDAxTfaIoV7Rl1xyJsOYhYpy1bhBKY+I2lRcdTAwMDFAcVx1MDAxN8owP1x1MDAwNumZO5xcdTAwMGVcdTAwMTBcdTAwMTWcv7D0XHUwMDEydHbMiGxoqFlpaUNcZlx1MDAxOChcdTAwMTZonolccpo6KYJJ2H1sXCLJbkeRvlv+jVwiollcdTAwMWGvRcqYqP27pvmBb4PGg1xy3dLSm1fAxVx1MDAxY3LF7uJGVlx1MDAwM6VZ0n9vZFx1MDAwNlx1MDAxZDK44izaUrRKXHUwMDBmSpBKLFx1MDAxNcyHjbbfhj0gtdO8+nKZj7h+ocivt49MWlx1MDAwN+wv3DDjlyF9/c9cdTAwMGJcdTAwMTRcdTAwMDVey1FcdTAwMTaAX4q61F/+NWDQt/27d6Iwvz0xi+WMutjj31x1MDAxONDrXHUwMDE3jZjOe/CdL3dcdTAwMWZG1DNcdTAwMDKrL1x1MDAxZZqJXHUwMDE10Y9cdTAwMWV/nWKUXHUwMDAxXzFwJneNXHUwMDEwU0x6SSusLlp7TeNcdTAwMWV/pzUndl/lXHUwMDEySp/8SWCX8yP2SWxcdTAwMDRP2aCaYbhcdTAwMTlsYeElIWdlhN9zXHUwMDFirKj0k+CX9iE/tXM6S6VcZv5cdTAwMTlcdTAwMGa0/7GJp1x1MDAwNrbGeGzgsEONOHrg4+1TJIxxXHUwMDAzOeNcdTAwMTizMo62loBfP9reztJcdTAwMTFcdTAwMTiJSLI/3ieVnqZzjVxiP2iV9e880TG7hlx1MDAxMLmQa3zBOOB16VxyXHUwMDFh8HOIWltcbplBR0CkK1RcdTAwMTfKzismXHUwMDFlaFx1MDAxM1x1MDAxYeyaXHUwMDEyxO1cdTAwMTnUg5i4XHUwMDE0fDDhc+9A96Bl61BInyF3XG66XHUwMDFmSvzaharatrpz9IaNXHLBOmDpXHUwMDAz6HXU2lSfjcdcdTAwMDJuQO3Zhlx1MDAwMX+euVpcdTAwMDaSkJTeXHUwMDFkXCL4yq3xLlx1MDAxYUhcdTAwMTdYS6Oqp7PL9Fro91x1MDAwYsx1q7JcdTAwMGKDy5nLkVlcdTAwMDM4R/n/rflcbr/1uGhcdTAwMTSMYKZcdTAwMTXmZc3d4Mvg+8RKw27otGAyT5mRXHUwMDAxWZvJgDFcdGA/fsdStOZcYotcdTAwMDKnj9OSOYlizVx1MDAxYUG2wpLy+vrQKcBcdTAwMWJB/U3uXHUwMDEyeLiyXUTfSfBUXHUwMDFj6vfsWFx1MDAwYlpOXFy1ebBTncC6lHouQ8SAy09+hvv1TaijM1xyNXmILaTosXfxWYpgl9BcdTAwMDGF6/G85qG3rr1cdTAwMWbPl1aa0TPCd1x1MDAxM75cdTAwMGVteonVfVwiY3noS4ZOL0gr/yZsPkM35N3SXHUwMDBmYSiG4fFcdTAwMDF8gCTTXHUwMDE5zzjSxpF2VH9cdTAwMWaOVOzWXHUwMDA0WU9cdTAwMTZMtnR24Yv1XHUwMDFkMqelXHUwMDEznklInZ1Fcmy0XHUwMDBlXHUwMDFlQtxcdDQ4yJfuzuF3W/rlW4iaolx1MDAxZpFI8HXUhVqBvyVcdTAwMWNXXHUwMDFhQLrYo6G76HgqrlxmI6l5Jd1cdTAwMTX6oHmSPrawqXRcdTAwMWJvhX1cdTAwMDZRXHUwMDFm8GEz9zqaqb7TX1x1MDAxOO/hXHUwMDA3XHUwMDAxmWA6KS+VSvkv10p2XHQhQ1tYJ1xiK69KkUW42Kbx/aJcdTAwMThajYbm71x1MDAxZZcw/NnjXHUwMDEy9qahp9UxuZM6dVx1MDAxZFx1MDAxZdan5FeSXHUwMDBm7J3kXHUwMDAw6UjQwEFe6oKIXHUwMDA0XHUwMDE3v0XwKd1cdTAwMDJcdTAwMDeXXG6eXGLjQVx1MDAxOFx1MDAwZVx0tV1cdTAwMGVuOUazKpzoW2TTlrHIs8t7IVx1MDAxOEHcXHUwMDEzXHUwMDBm0k7Yo7He31x1MDAxZHvPXHUwMDE3529cZm9jkkW6XHUwMDA0vX3zma+NXFxcdTAwMDMxzzVcdTAwMTC6s1o1JSyn07ffwzqDxr5CR1kuXHUwMDFmmtpWK1x1MDAxZlxcnHCe7tCjXHUwMDA01vT7cmHSamp4JVx1MDAwZc1cZnTiWtREXHUwMDFlbjbVJzPF0kRb/CWT5SxGt+9txT6cTbJTSGB2YFJ2SKC8XHUwMDBl0VozNUDhgcBcIsuRvWmlrDjRzmTfeVx1MDAxZHh3MUbE5Fx1MDAxZjbSXHUwMDE2sJtcdTAwMTCWRtBcdTAwMWV8YWw53m3zJmlcdTAwMWFoNvqrMLw5syjp2CryQfZcdTAwMWWguupmXCLEob9ndyG1dmqTXHUwMDA15mMorddcdTAwMDZ8KP73tG+7LLlS9OR+/6LElVxu7oYguZyJT36z7S1cdTAwMDZcdTAwMGJVXHUwMDFljlx1MDAxYaltY5bvUMNFQ1xcOtuf3rRcbsgx1slcdTAwMGX5Slx1MDAwNN5Jm0lu+mV5lXmC5LOjXHUwMDAyekCR4f468lx1MDAwMzFcdTAwMTRcdTAwMWFcdTAwMTa/Ri7iMlx1MDAxNIZxMXafo5NkXHUwMDExL5qAO8S7nFx1MDAxZVx1MDAxN/hn+9T8e1+tXHUwMDE5ssVpRd0reCM0dVx1MDAxM+Lm0/z3K6QxfqCSsHpcYq1cdTAwMTVt76Gx7lx1MDAwZVx1MDAxNlx1MDAxZoajWJOB5UCXvPlPwm5hPIBcYpcvRWV/b1x1MDAxNT6lOlxcK18gS72vwUZcYpZ0Rlx1MDAwNG6M3UJ0p65qZKAmXHUwMDBmQMtlVZn9xjNub+HfjNyGYtBLcknTI6TStVx1MDAxNn+FaFx1MDAxYXgvM7hDXHUwMDE3b0FmoSzDb+b0uVx1MDAxN1x1MDAwMKRcdTAwMTfMUV+k/07Qw6ZjXHUwMDFkXHUwMDE4oUNcXFx1MDAwMzO5XG7bcaLnXHUwMDEwWfBofWzyhGBN0ILkXG7399xcIlx1MDAxMdVelLiIt1m8XHUwMDA1XHUwMDFkWHs+XHUwMDA3n8lOXHUwMDEyofhdMo6rfMnskDzF42KGbzf6tFx1MDAxNqDDm1x1MDAxOVx1MDAxNiiCvFx1MDAxZvPBUlx1MDAwMFx1MDAxYVx1MDAxY0dcdTAwMDE8XmuExN+zYpUyslx1MDAwMu0jmZIucnZcdFxmMDZF7Ve27WBz+jX9nPJcdTAwMTQwRexcdTAwMWZX/cmbKFx1MDAwZWqrO/RjXHUwMDE554ZcdTAwMDXygvn0t13e91x1MDAxNkn3RqRasnG9JcmfWnQoXHUwMDE0mvFcboCAsDtcdTAwMWGVbb9EZ1x1MDAxOfhDJXiy6qJcdTAwMWapXHUwMDEyYlx1MDAwMVx1MDAxObP45aeaKPCqb2usrKRcZlx1MDAwNFBcdTAwMWSgaGy6ucleXHUwMDBlllGEklMg/yV/r7JFuVxmtaWWTkm5XHUwMDFiu1x1MDAxN7RcdTAwMWOIasG1+r2PwdTYmzFcdTAwMTXnXHUwMDEyXHUwMDE4XHUwMDEwkHXRxLhcdTAwMDbIkSySyChYuiXB8VxiKl6R2EBQXHUwMDFivtOJcT6R/J2cpVxiXHUwMDFmR/k903tcdTAwMTNZ+btsvmq9ZP81eDIjTqGUbH3Asptlr7WeLm1APDYgK3Q0iVx1MDAxZD/8XHUwMDE0apbDeXhcdTAwMTCAXHUwMDFmf1xyuL35P/v4XHUwMDAx+MD145Uz/mxAvSw4Y+epQJTMuUZE67XrXHUwMDE51e1afdZcdTAwMDGfv2tcdTAwMDaQpeypXHT4a9YyapSPJznyek4wXG7d8LpcdTAwMTmwVPbwnlCY5zygQ4yW24X+LbNcXEBcXMeHwDIumL5cdTAwMGL8ana8z8+ToFx0gd1cdTAwMTXJpU6PLDVcdTAwMTd+XHUwMDA3nqzcUPZZPVVN0srssMTTl+JcdTAwMDZcdTAwMTCJMVx1MDAxOLhPgVx1MDAwZcC2XG75Slx1MDAxN+fFp2ruMrTuXG7RXHUwMDFir1x1MDAxM2HQ3Fx1MDAwM1x1MDAwMVx1MDAwMPy9q0ydQ5B/NFx1MDAxY5X0L+l1xNKkolxmhpmLXHUwMDE4xkiiYCC4M2+OUliOJ0DQuC2HPbQ2W/mbXHUwMDE5vvLdfGXwXGJ2XHUwMDE4XCLLik++XHUwMDFmQWDnXHUwMDAyKKmAonxcZnQo+GxcdTAwMTApXHUwMDBi+dN483H8yruOoKBJyGMgLL5cdTAwMTSO4nlcdTAwMTmJMVx0qG1cdTAwMWZ9+Vx1MDAxNvxk/vx1r8lR2Vx1MDAxMLxTSC9VXHUwMDE0QP11caljqrKZp1x1MDAwZjuuw+WAXHUwMDA3NkPdQ1x1MDAxNNhcdTAwMTeDxZTxLSVcdTAwMTPsNFnNW73QISyvy+CQT1xc6ynSPVx1MDAxM1JfViSLqTCGeVx1MDAwN/tcYqiCy9mBk+fe/n32WZdbyMRcdTAwMDbWl/2tf3CynFdfgtTrr0nlXHUwMDBiYNVuaY6EgY66s2Fh6eCM3lxu+KdZffh7yZTnz4fU2/1bqoHE9NpHozD8XVxm2nJcIvKGXHUwMDAyvPlOoDpCYkTQXHUwMDFhvLFcdTAwMGWb5z+it7x1VVx1MDAxNVx1MDAwMzBcdTAwMDcyLFx1MDAxN3OTjnTh6z1cdTAwMDZcdTAwMGLzXHUwMDE3P0zSeb9wRrLad4lcdTAwMWUt8Vx1MDAxNn1cdTAwMGZP5F7s4kdTslFhibx1XHUwMDA2WzR6XHUwMDFmUmVVXHUwMDAz9zaE5Vx1MDAxZidDtoa6RPouX6Ciq5fgXHUwMDEyk0tUhZC4XHUwMDFjmudjZM8hXHUwMDE1kaJ9w33h4IlcdTAwMDEtXVx1MDAwNXyl+J3LfkGtJNdcdTAwMTkv8CNcdTAwMDSiXsOotCcl1FxiXGaX10zS4stZXHUwMDAyuVx1MDAxNXJ9vlVV+fV4W8basLmyXHUwMDEzYPR7cOJLNiks6bekXHUwMDBizPXDXG5cdTAwMTc5JHGY4GxcdTAwMDZX+yijXCL9hVN3XHUwMDEzTXlcdTAwMTO33Fx1MDAwNIhEfFxyvjZpx8uPqTs36Fx1MDAwYiQtn6yWc/LVQzdE9cVcdTAwMTcl/arcKSdcdTAwMTGNzNdsobDmSJLORCX00z3yZlx1MDAxMG+KuVvITHS+KqErtPRcdTAwMTZcYvNcdTAwMDfgcFx1MDAxZJeWXHUwMDEyKVx1MDAxMbWy3rZcdTAwMTH1z1B9XHUwMDFlgNZo8uDftVBcdTAwMThcYpG5KjiXXHUwMDE3YkFyxbNBXHUwMDE5nLNJ0IAhkrThqlFlXHUwMDFl69D4rlKNzvQ1XHUwMDBiZZ1cdTAwMTRK0rwoyP1+qb6kaVx1MDAxYmDSkyHtqyP/XHUwMDE46s7/jjNG/XqlzDxcdTAwMTPvw16MkkFemXC/YJtcdFx1MDAwZkVmXHUwMDAy2+DclyCVr+LvfeQlXHUwMDFm8q/b079YucCrTuefXHUwMDE0XHUwMDAxl5YgXGbl6ydxXHUwMDFlOFx1MDAxMMvDvKy8vi90gpVk/vWUXHUwMDEwKvlI0JfMyoAlx1x1MDAwYvtE30iE+X5ccuavf/zVXHUwMDA1xSeSXHUwMDAy+vvS1KXv6f5cdTAwMTPXVGQh/Vx1MDAwM8xu46qHXHUwMDEzbr2W4dx3p5f6/S3EOjyVXHUwMDA2TH/PXGbSWFx1MDAxYbP/9ohI0Iowz0eXXHUwMDEzSpz551xyQlx1MDAwZmVL1jtlP1ZcdTAwMDVcdTAwMGJKPsZcdTAwMGKdw566f0BbXHUwMDEwyyFcdTAwMTR+rVwi8Fx1MDAxOUrBXHUwMDA2ILzwSLBcdTAwMTUvO1x1MDAwYlx1MDAxOUMnVWL6rlx1MDAxYdZRS/DE7S/wePmduSV87mekMPsjqcFcdTAwMWK/NeV00fRRXHUwMDFmkMvrz7drfz9cdTAwMWFJM1xyglxu25Y3vlx1MDAxZeJYv1x1MDAxZSy5WvX0xtGvqvz2t1x1MDAxYVx0V1DAlV9iqeKqY3fDn34ppNbNJF93pIyDhW2izFfmJPOgczLy5pSVWOSBvFSMXHUwMDFlXHUwMDA1tHP+SCfKXHUwMDBmQkeQXHUwMDAxunBlXHUwMDBmdObWfMnBXHUwMDE3h+gzgVx1MDAxY6dcdTAwMTYz4J1Nab1cdTAwMDDYJ0I5yjn3XcCx1mvJ+ntWLn/bqks056Njk3S7lNbu1Fx1MDAxNPndXGZYWqI0+PST9ikwmTbpMOZdiNJm6Hkoi+BcIjyRJ5uXcr1oOcT6Tcu2+5pGazVX+tLFWqxrP5zYXHUwMDE17V93Mr37/z4r4WxcdTAwMDRpXHUwMDAz+ihmmzePiFilk4FxXHUwMDAwXHUwMDE0UNm4iPG9XFxcdTAwMDVcdTAwMWVcdTAwMTOv0kpiPqdJhempU82S8eL56ro1Zbw+UqiMbyqEYuResLLI/D0jXHUwMDEwQL0/OVuHyTCOXCJcdTAwMWLgXHUwMDE0XHUwMDFkXHUwMDFmkuov7+23b04ljrmFwFs7RObkXHI/vWtq/55KVGm3XFx6SkmMXHUwMDA3RVx1MDAwNoKGgOFQTlx1MDAxN65gpHaO5ujKRTnNmmKktVdS/eORIWPQXHUwMDFh4/zbXHUwMDFmyVl/nmv6lqst7ZLxeqXuXHUwMDFlLT5QkOJcdTAwMDO6j1x1MDAxMZI3T5+tVY5cdTAwMThAXGbWynhgx4Y8Z0ImK9mAw+dcdTAwMWWnXHSUzfdcdTAwMGJcdTAwMWG+a8lBKodB++qkPlxcOF7RpoFcdTAwMTT0Yjt/XHUwMDBmWvtAzbFcdTAwMWLpxj96XHUwMDFix18jboBLb7qYKPjo2dBIQ1x1MDAwZYWIYFx1MDAwMjs4VWkqrVW12WBU4kZuZKurm1Neilx1MDAwMKYs5cin18wn1lx1MDAwNomxTVx1MDAxMSiD+Z1TXUMhi1x1MDAxY+5pn6Ko8ZTZuvSfNs+zzu73xd3J7Fx1MDAxYaK+ZyP9mVpLXja+urxcdTAwMDUrt/mgvNVpgoYs1dPKKlx1MDAxMc1J3+O3w4erkTAgJlx1MDAxMtL2Yk18oytoK95MsIX1amDmXHUwMDEx1pVcdTAwMTLVo1jxXHUwMDA1I31f/Lv4virJtFx1MDAwM1nTgMdGxiA4xUxKTzhSPnhRmp9HXHUwMDExhGNsravzbuJcdTAwMThhXHUwMDExfFx1MDAxYuBv6M9cdTAwMDNcdTAwMDR8XHUwMDEw2+fGeeBcXI6mRlx1MDAwNFx1MDAxZVwir9dcdTAwMTeggVx1MDAwMzW04X3bXHUwMDEwKs4yrvVgXHUwMDFjNqBHQFxmXHUwMDEyQlx1MDAxYpyaKVx1MDAxYcVwlD3tdlx1MDAxZKRxkCaQJW6gWmVcclxyKoV0XHUwMDA2UCSoqdyWXHJcdTAwMTJ6XHUwMDFk4vsrJ9t1RSxZXkNcdTAwMTeFfcVoolCFXHUwMDAy+LNHkLI4SN1Liu/9yHfusT7F1bGW5jil9PrypspcdTAwMTfSS7ZUdrXQzcBcdTAwMDBVU7LHXHUwMDExvPu1w5mTpHA4TD2BXHUwMDEzR2+7SO/6QPTsoNljXHUwMDE1XHUwMDFmJTLkQouDbVJG21x1MDAxNl+16LdcdTAwMTJVc1x1MDAxOXdcdTAwMDM6glx1MDAxN2iE6zB3I1x1MDAwNThyilx1MDAwZVx1MDAwMFx1MDAxYUJbo1x1MDAxMMdANlx1MDAwNpBcdTAwMDWoSfV8hNetPX/SafZQXG5cdTAwMDH9niOcipdcdTAwMGZby9TSm5TrPkn3m9BcdTAwMDFVpPVYja1HXHUwMDEzpElcdTAwMWFcdTAwMDbz4q2KP02odlnz9fyE79vkXHUwMDBmi5dEiVx1MDAxZM5ZlkzZXHUwMDFlx0lcdTAwMTNbrX3gXHUwMDFlumFcdTAwMWOL4pCmXHUwMDFmj7mtsaHHes3ZXHUwMDFk3lcmRVx1MDAwYpVLxra401FnJzdcdTAwMDAoXHUwMDAy7K6eXHUwMDA3XHUwMDE1UDhTnihS6YKK1W3hQFx1MDAxMH9cdTAwMDFQX3XPiyc3z9MwTJGVcJSg2X0mudMt/1x1MDAxY0AnMKVGQ1x1MDAwNESSYPBcdTAwMWI/p3jGL7xKm229M6eN8niGoV459j919iniX51lgVx1MDAxNom2Y1x1MDAxNXGeV/S2ZVx1MDAxNPCcgti5jMbp/a1rXWV7pXximeem61x1MDAxNDbnijPj/Fx1MDAxNSyCa1wiraKXR9hcIuiyqT1yhjnMpl9gt55QXHUwMDExx5ZcdTAwMTHLkDhcdTAwMTJcXFx1MDAxNiVcdTAwMTPLXHUwMDBlhVwi0FhNXG7o31x1MDAwZvUgQzL19tJY6FJccisvydOVzrNovyx2g15cdTAwMTL74p9asa9yeEmWRlx1MDAxNG6Ta6bOzjGiIO5cbkbUdMQzXHUwMDBmbGZcdTAwMDdcdTAwMGZcck9/yVWf51x1MDAxOMW/23TLslxm21x1MDAwNMl6WS6SyjyP9LwgseO2gHqyz1x1MDAxNDBHb4Vj45tNuFx1MDAxNdkg4L5h3+dcZnepXHUwMDFjXHUwMDEzRU8/cpJEo7JMdL1cdTAwMWOj35ufnMSszVx1MDAxMbxccuGK4/O5XbqO5DGBgLrGvKXHapnod2BcdTAwMDdcdTAwMWL6m1vmxVx1MDAwMo72YlhcdTAwMTn0lK3PXG77vTNcdTAwMWW6WLTjKaun+fHwXG47V6TBXHUwMDFle01PveMjuV5ST1x1MDAxMkvyezu6j7Siv39DYdu+uEuDjye8ScX18+R8g6KZMsl7zZ9cdTAwMWN7t4FG44KJ0/3GLM73R3dcZmFcYti+XHUwMDBi+/nmOeWuZE7cvlh/h2F1kFx1MDAwZSNXUkhTbFx1MDAwZd/TrsU+N9H7XHUwMDE0w2s5x9RhXHUwMDE2SYlgvkW/XHUwMDBluylS9s0/cuVX50V8PGOhPMNQu47JMm+E0Fx1MDAxMERcdTAwMTBtlFx1MDAwN4Gspu2qMTJDR1x1MDAwMf/iXHUwMDAyhmQjhHaQfIrBbnFcdTAwMDUvsGX0e8zL0nLHe5qix8g+KnK3LNe1XHUwMDFmITwqYE9cdTAwMTmdX6LulcbzXHUwMDFiJU30g5OdjWKX8ftcdTAwMWSWPFL5XG5NW7epl1xc08TMcTDc7VwiXHUwMDE1XHUwMDEyTXjPn4rDzKzJgmdCqpZoWNB8vsxXI7/Y91coXiXv9tyIlUdJafv+cTdcdTAwMDRALd9cYjpz1Fx1MDAxZvDivONcdTAwMWFSeIEjMLPDXHJkr1x1MDAxYdWH+aaj90dcdTAwMTCBVV+nXHUwMDA0hoFTXHUwMDEyyXXO4lx1MDAxOVqBYTdcdTAwMDSXiteW05+YPlx1MDAwNbhP4+77k/dGXHUwMDFmxcsxSVxi6Fx1MDAxM0WvrFx1MDAxMVx1MDAxYVPabFxu7XlcdTAwMDFcdTAwMDV15aclhVx1MDAwNiPWSUtzgcBawLPka153cnfFKehf47u+Xa+7g/LV6lx1MDAwZsnQ49/8PzTww0tWs135KJOU91x1MDAxM7uEv1x1MDAwNOZcdTAwMDdhz8RcdTAwMWJKY7c2Idw3ecH0Ya6EZjz2XHUwMDEwoceFQGKchMVFIeeqh/aAzNRH1Ph1XHUwMDAzNlx1MDAwNEJm3NtcdTAwMWYsw5hcdTAwMDBcYiyfX1xugrROp1xiXHUwMDEyv1PUXHUwMDA2qLRA3qiloFx1MDAwN1x1MDAwNoRBXHSRp371Mz3gTsk7SiM/XHUwMDA0pGu1hdtcdTAwMWSlm1+W1fVK6GBcdTAwMWE74HyG406r/WlcdTAwMDA9TJ5Xulx1MDAwMs7NXHUwMDAxOI2gelx1MDAxYoVcdTAwMTP9XHUwMDAzg1xiw96vcDVSXHJcdTAwMDCfctrZXHUwMDA3iypcIiqwZ76LhqlcdTAwMDAyuadcdTAwMGLUXWJcdTAwMTVUSHPL8FxcLptcdTAwMWaqcqWfyL3Vc6fyXHUwMDAwNrA7VzvxxmhcImhcdTAwMDdkXHUwMDE4rcj8PqGPy5dcdFx1MDAwNPppq0Is0zFcdTAwMTb3Ucdl+4DVujymgFLTtPGPs4DLfuAtloXexyS8dPDMTWJb533vXHUwMDAw4n3wwrGn4Vx1MDAxOGbbJsYpXGZcdTAwMGZDXHUwMDE43lx1MDAxN1x1MDAwNfhcdTAwMTFcXPvtPfcodH2vea7jPvfrk3iMYFNmgPNcdTAwMTCtoyNO6uPA9Vx1MDAxMtdY29ZgXHLr1e9cdTAwMTFwrjAjXHUwMDE3oNpGIeJ1k1x1MDAwME7jalx1MDAwZlaPXHUwMDE2f45cdTAwMTOEqIZYXHUwMDE0pMzldL1cdTAwMWVcdG98tYOyMGLkT/1JXHUwMDFhjNM9imL5gubZh9y3su1cdTAwMTE8zW4/IFx1MDAwNY1cdTAwMDa1yDFcdTAwMGLj9Vx1MDAxZadpOlxm/4lcdTAwMWTT0UpruzedYZ+T3nZcdTAwMTbeylx1MDAxM4PPwPVFXHUwMDFmP1x1MDAxOHpcdTAwMDU6vFqRXHUwMDFl2epcdTAwMWMq4lx0XHUwMDAwXHUwMDAzP4bpVFvg7Yg89DqVPlPWouRcdTAwMDc/rKLcnF8tXHUwMDFmXHUwMDExdFlcYlXpxFa25I7QSSRNP2Pv+VN5fDtcdTAwMTGso3mt47RH2rZPVbZcdTAwMWPP0I/sLtfVdKLpXHUwMDE4gfxcdTAwMWQuwVx1MDAxMctlmpQ8PI65XFyIuvkz9Fx1MDAwMWYjv1usId1xJjQmSXxVPutcdTAwMDadXVx1MDAxNdQj0s06eb3Tkmk/gFh5alx1MDAxY1x1MDAwZaNcdTAwMGZcdTAwMDF7XHUwMDAz1Vx1MDAxNYzfyJrRm5M+2KuinTJcdTAwMGI3I7540Z660EVw37XjjExgjVwizVx1MDAxMco2du1YXHUwMDEw6ZN8t8clX6i3NvtcdTAwMGZ3fCrex8JeRNZiUm5cdTAwMWZyhPPwklNcdTAwMTfvXHUwMDAxJnxYXHUwMDE0aTJzt1wiWlx1MDAxZlx1MDAxYYjDn5JulVtcYrNcdTAwMDRcdTAwMGY1YPJcdTAwMDBVX3vxwbzWjVx1MDAxNm0lxDeCZ+R3WEqzdzudwPdcdTAwMTBNtf7KjFxcT5qpuNHbIz7Fw89B8UWZpOLVNodFk8fGylbGXHUwMDEzZuB2vtNcdTAwMThcdTAwMDHH+i2zXHUwMDA2OlCjVsF8x1x1MDAwYvW9TWdcdTAwMDJeXHUwMDAykLqm6JE0mISigSrnl4jN6lx1MDAxOSdcIjr7tstvzCH81IjUXGLtO1x1MDAxNJlJMlx1MDAxZkma0j9cdTAwMTRBXHUwMDFmeLgsu0Dq2/XwXHUwMDA1gEBTnOpcdTAwMWH+o1x1MDAwNZWEotHTRM43zoF4fipNf9ZcdTAwMTXX3DCRPIJbiVxu2v1oXfx4fc+AQUnfXHUwMDBmcDPdTdJOnFx1MDAwMODn83D9XYtwi/StiVx1MDAwZVwimTEv6va84iPstLLtaKerMT6gbyyFZ1bLQbynN2l287i6UGM9cWAmo3CeumuUvVVmK7fyUSRdLneKNVNdXHUwMDEw1FPVXHUwMDBmXHUwMDAzPiHsOX5cIsiXe7frVflcckL/OjrsdJs7hHRcdTAwMWYju77px1dlXHUwMDBlXGaH9X1QrFRy+GF3+26a8uOjn1x1MDAxYTXTlKo/XHUwMDEzxFxydqxcdTAwMWW9eH6kz5Cxupym9Eq2O3ri90WSy4SsO+qVca6H5Nj1zSv5aWVcdTAwMDTZ0duIXHUwMDE1pdrC1Vx1MDAxYl1Rmlx06Ju/SS0uIVx1MDAxOMLPx7dNPufalS1UP4wz3lvtXHUwMDA03X+0mPOy3pItv1x1MDAxNUh0mVcvpm2ILpSkPz7w/YQhXHUwMDFlyGGoziy2MWHH24i/fPFcdTAwMDSYe1x1MDAxZjtdKpisf/dGTSq6kpHWqeJcdTAwMGaoV4JcdTAwMWN+XHUwMDEzmGNfXHUwMDE1kzThi1Ek3oUpXtoztatcYl7JXHUwMDEzXHUwMDFhn0DBqV7mo1VUw3b8X1xmeKVcZlx1MDAwMOb90TDlb1x1MDAxZSnqXHUwMDEx4dzN21/RlNlcIk3ydI38Uls3XGap3K40OznS8tzXu0ne/d1cdTAwMDO0Slx1MDAxYj5Xhsafs6GOXGZNl/vjvX/Pv1xuUtirte5cdTAwMDWu3uNZXHUwMDFl/1x1MDAxYcWXKd5QlJxcdTAwMGZcdTAwMTG7XW/vLsLhpPyijK1cdTAwMWZcdTAwMWZcdTAwMTnbNWScIdtYm5haSp9JX1NcdTAwMWEwXHUwMDEwf99deKH8zXjDcYZ6XHUwMDE5XHK/55RcdTAwMDQh3aX3JVx1MDAwZidA67zNMGoxlbPpUYFcdTAwMTe5VTGKxkYkZ1xie6JcdTAwMGVcboe4sm1cdTAwMWFCIfd5JipcdTAwMTdJZZrnxvotdzRcYmBcdTAwMWFsPFiN109cdTAwMTmhloSEdNb6MT8s/Vxu4nmGXHUwMDE4yTQtc6ztXGZcdTAwMWPHXHUwMDFmaPVcdTAwMTE4TlxcepHVj7+TqDfGc7NcdHT2gLePenOE8k1cdTAwMDRBLcQlNMIl4F7deUmbT0ROXHUwMDA123T3TtRboEswn1wiOqPt2Nw//T1BkTFDnYpcdTAwMDLy/MZhXHUwMDEzi7GhJGfXrV/g7J4lXHUwMDBmgrDyWNNIQJxhdtfywfik51Xy8iZXXHUwMDAztHJcdTAwMDEyZ4pccrZcdTAwMDBGXHUwMDAwXHUwMDA0u6itrXVfl/uLL8PClzuFTcujJ2GidpA0WiM0pVx1MDAxZiCikmh8UShcdTAwMDD0/aaitD94tEHGUT6OTnCtcEW301x1MDAwNNuBVVx1MDAxOL3GXHUwMDE1tNbuy1x1MDAwNVxuxON/r4dQNM989Iorcz9fXHUwMDBiXHUwMDBisXPLI/dcdTAwMTPzXG5xICdJXHUwMDAy6Vx1MDAxY69cdTAwMWKSklx1MDAxYoi+XHUwMDEzxd1haXVcdTAwMWaPXHUwMDEx5ydMXHUwMDA0NLhEXHUwMDFjTMLsWvooZC9cdTAwMTAk3qS871x1MDAwNO3jg3SQZOoz5I4k3eb4cqSY2klZhJPmXHUwMDFmq3HpeSMgelx1MDAxMnKXXHUwMDBmsuvkZk++2cfM0M2YQu+Q5rfHXHUwMDA2xrBv7Fx1MDAxOFx1MDAwNHclXHLS6IaGyFx1MDAwNTqw1oI90Ms0WLFlqO1cckJRhCdn7fTgy/yYnVx1MDAwNWJWNCN9vyuprSpKrc4zt3v3vd1r4Igm3qfrZY0+LTJChyh3Sy1o49vPOSFHYsCqxWcykFx1MDAxNKLr/ntcdTAwMDJQ2mxbYmpcdTAwMDOY2kVRqFEr3tWGXHUwMDFmumck97048Fx1MDAxNiFML41lz42QXHUwMDBizb1cYkO2lFx1MDAxObJcdTAwMTg/wFx1MDAxNE74Tn5cdTAwMTQ1YX9uxOhZJM9BjuQj0J+FWUHXxmI2XHUwMDEyVEZcXNuvLIpjcd7Q5Vx1MDAwMa9ZvXdcbnlccnG6fyBcdTAwMDQlnn9jKc9cdTAwMWSQJrKLWyeoN1x1MDAxMVLpyJjk6rj1glx1MDAwZvsomMdcdTAwMDd/oSBKRFx1MDAxZavGhphcdTAwMTdXf5/C76nIpdLyjjfPtqpcIuO8XHUwMDAxQNJcdTAwMGbfxsPSXHUwMDBiiCHJUrtl1YORkayX8v1cdTAwMTSv9s1O9JK8XG4+XHThR9uQht4jU6pT/lx1MDAwMiNg7MlcdTAwMGJSO2JcdTAwMDRcdTAwMTAvZVx1MDAwM/Zf3MQ0oeuB5pNyPDxcdTAwMTWl4XxcYjfKdqlcIiFxrNnvXHUwMDA3f1x1MDAxYagn6Tu7hfb5XGJcdTAwMDFALVx1MDAxOeZ9MFNkeVx1MDAwYr7mPvHg8ivhSlx1MDAwMz1cdTAwMWWiyVVz7dCPVs7BWce5+iZBe1wiXHUwMDBlXHUwMDAxXHUwMDA2sthcdTAwMDMoK/aUaF1PXHUwMDAwjFx1MDAxMUxD4a5S+nLHqCgkNJDkXCIxO7kygOh0gkT8c9q323vQ+ZZcboxcdTAwMThcdTAwMTZh3ybWQSMwXHUwMDFjXHUwMDAyfF39dzfPrfzU8DNRXGItf6TVXivYutFZ78LrM1x0OsVcdTAwMWFL8jU/1PiMz1x1MDAxNIp2nEDZ7KGmPMHLlrlcdTAwMTNpNP5cdTAwMDQgOI7kXHUwMDExzj5/1nmhsq9Vrm9cdTAwMGI/nrf1ZZG3Znt9T59jpeFcdTAwMWSCfq9+2lx1MDAxY+8pqek6Q5x44Fx1MDAxNVx1MDAwNLQ1XHUwMDE5pLxcdTAwMTZcdTAwMDAj2timNP1cdTAwMWWi43H5zFx1MDAxNTJWvFx1MDAwMoB2uM7aXHUwMDAzXCJ5XHUwMDFjg+hcbkyqXHUwMDAzgOh5XHUwMDEyJH49NYonSOwxL4VcdTAwMDLBc+5cdTAwMTck8Tz9l/9IK88gcGk6zGPn0nrk8jK4fPu8b8ErfkJRXWzGZUzxXHUwMDFki+hJwdeakr436Vx1MDAwZlxcnmVcdTAwMWKhpvClv2u6oH1l+lx1MDAwMdCXQY/AlfNyXGJqXHUwMDFjft2hP966f1x1MDAwYkdJkUxUmSDuwVJcdIy2aVxiw0mvY/tcXFx1MDAxOCH8XGZcdTAwMThcdTAwMTR5u2zNTDx+XHUwMDE2vbPHvZ8/WC9cbvD+6Fxi/tXt2PtcdTAwMDLgeDJcdTAwMTe01lx1MDAxZsFcdTAwMTInO1x1MDAxOPA3RIHE+1x1MDAxMVxia5wjuMRjg21cdTAwMDd9jMOusVx1MDAxYcf+JnPPR9ZSUW5cdTAwMTOSUV0/gFx1MDAwNzxGjFx1MDAwMJZYXHUwMDE1o3vfXG5UtuMhqYRHp02i+GiU3vAmWouyXHUwMDE00UTdiPXOiSRYxpcs2Uynu1x1MDAxMfJsMGp8vF/S41xcmFx1MDAwNjarXHJcdTAwMTJcdTAwMDBcIsFcbihcYrpEMIaCXHUwMDE3XHUwMDE2XHUwMDEwXGKcRaFHWc9cdTAwMThzQ1x1MDAwNlx1MDAxYVx1MDAxYfJcYn9cdTAwMGJYaN24l1x1MDAxNqbmOG/dkZ4+79RFXHUwMDFlXHKTkppdXHUwMDEzdJaYwOl5SLbludiquv6psyyPOkRV09n8guJLQsVcdTAwMTigmLHeXiXiXHUwMDBmxSalMFxizPW6RrhDaTBpe1x1MDAwNbJisU5/XHUwMDEwZGJX/TomRSSNm6Pz0UaBQFxcYkhcdTAwMDFOXHUwMDBiQJLq8a2z8ek2TI/9vttcdTAwMTKggFx1MDAxYr898XdXSCWDv9o5/buGY0X8b92MXHT4XHUwMDE1ZrRH1uiWkbne2vTBuWTzfVx1MDAxNlxyPXORXHUwMDFhp9NcYtHapJa2jtPSXHUwMDFiJ8nHXHUwMDEzKNUyk59MXHUwMDE2s1FHePNR5eD5zojmuted8FiStL7bqVx1MDAxMFx1MDAxN9w+qtMwwnSLxlx1MDAwN2+XXHUwMDBmMO2R07CLY/t6XHUwMDFlk+KjJ6nh8cPCWyxcdTAwMDd6fmB/JlVcdTAwMDdccteyZEhcdTAwMGYlXGJ6+XJs9K04aXPLk7Z73ZnRO6f3sOI2mzxGcGO40mY+/SpmpeNiVOHVudrFeU6LutBeRWLVzntGeyRI8jtW61x1MDAxYcCLXHUwMDE2Rszsi/Palj5YxH1TnIJRdlx1MDAwMUqpO/c661x1MDAxZVx1MDAxYk6H2+d48vvIcoa0oOu+7ce3UdTDvVx1MDAwMCPj9jKaYE56XFy6Yd7OXHUwMDA1j65cdTAwMTQ/vYa3j1x1MDAwNiw3697b7Vx1MDAxM64mfSOP07tWr7GXq1x1MDAwM9eomVx1MDAwM+jSXHUwMDAwhZhhSyHOk1x1MDAxY3BRXHUwMDFhmqVcdTAwMDdKKl5cdTAwMWW67LxcdIBcdTAwMWaPQ86033FQ95WuXHUwMDE3O6RQ+UeTSlx1MDAwNfBbXHUwMDFmhFxckLNqQ2KCN1x1MDAxNvp1wtUjVc3u44e1fO/a+lx1MDAxNyZKIZ5O28Glp92b5ef7vr/Qe4rZ8CZF4S7tjf7UQrt/TcW64lI3VJRV53Xqit87qcO3QdOH30Atz2qim05cdTAwMWVCp4SvktQkf1b48nBpXHUwMDE4/IHalVx1MDAwNL6wXHUwMDFiZ2x9LXDzXHUwMDFj7EqFkVx1MDAxOKFRtLyyXHUwMDE2VVx1MDAwYiy8xI/qWm8hXCK2LFNMw0pcdTAwMDbS0/KvQb04xECpOfXWrseAQca1aKuP10t6MbLFXHUwMDBizqW/UlZ8R0G6rmiCZbtccuJfrofToG9HcqdcdTAwMWHvoHmZtb8ugHI2rNGPvFQ6XHUwMDA0Xr1Nvlx1MDAxYzpPp1Jccjlwb+ZC0/1NKa13d1xub1x1MDAwMITez0zQwXVAjFx1MDAxM1xyXHUwMDFm0lx1MDAxYlx1MDAxN32/VTJ/fJ7uIdg47T+tPVx1MDAwNtWL5U157ibTZF7XQmugXHUwMDAzXHUwMDAxV5rtk1XxNFxiXHUwMDExOvK4XHUwMDFk209L+U5TmOi3lVx1MDAxZlx1MDAwMLBZoT4jKzgxqrPUyKGfNcTgXXXIi+JcdTAwMDD7zX6gWyjiXHUwMDE0J1x1MDAwNo2LfFNjOivfXHUwMDFm0kZcdTAwMGVZn1x1MDAwYkQkadXDa4OoXHUwMDBl7opcdTAwMWRErN9aTHpfiFx1MDAwYuB+pFx1MDAxMbp8tMNcdTAwMTeSucx9nFx1MDAxMuCux7aJlnJOqVxywJfMpeFcdTAwMWVcXIAxM1x1MDAwMEdDr89OdjVcdTAwMTnD5Cb7QKV/XHUwMDFlXHUwMDFlm+J3qd+psCc8Vj5z4iwyXHUwMDAwsLqS8Vxct6lVTERN6Su4e1x1MDAwN5Q09F0p3eg8qbGeeVx1MDAxZdhcYuOJVYjksdijn5hUhVx1MDAwNqPhUkN1d1GlltetVy0uN8p9XHUwMDBlKtuw1N1obbdngHpInJZVOFTuO47GdG+SUpc0kWGzVVx1MDAwN0Xdy3Qkh1xmeItQVIRjmV7rXHUwMDE2qZJccpc3n7RcdTAwMWRcdTAwMTJyXG6vXHUwMDE4w/W7/Fx1MDAwMMy8mP/4xXvcXHUwMDFmn4QylGSkakor+Fk9J3290b1F3EGoucet41x1MDAwN4ZcdTAwMDSF2oNcdTAwMTY80S+eOGlgVlx1MDAxOVLE7jj/ap1cdTAwMTaNXHUwMDE4rFxiNKrBnNOt1dFLXHUwMDFiy3/F0oujVe/dwTRZqOfyXHUwMDAyiVHD5uKzJ82FXHUwMDE5jCp1mtwyOkG+9pNQRkZXXHUwMDBi6dpcdTAwMTd83XrEJON11kVcdTAwMTJCQMY/XHUwMDAyXGKZ5lVccmSSXHUwMDBlY+pGmLdcdTAwMDFTVF2TXHUwMDA3RsiIiaD2XHUwMDA3wdRcdTAwMWKIfN19MIR2XHUwMDFmXGZZXHUwMDBlkblMqs8ljEhjXHL2ojeS11x1MDAwNJC3XHUwMDAwXHUwMDEwXHUwMDEzrVx1MDAxMj82nYhcdTAwMDFcdTAwMWRzXHUwMDEwXHUwMDE5wWP3wZjArEJiw9jf7dDeU+PfXHUwMDFh7vfjOLOUaGaQbku0b1BMV8/IJUZ3f7uBdT9gQz3wRqt7pSmkWXwrWlx1MDAwYp3PKbHmxFE8wzLKh4fv02MyI8a3N3Kilunnu0HjJ5ya+5d4XHUwMDEx84rG/E1mXHUwMDFjrdOVXHUwMDA23199IcFcdTAwMWKdXHUwMDFmayqUq2XBrEWH2UJ6K3Ktm5Fmw6q6XHUwMDA0/ONy6jolXGYwdMhDZbPkkcPtkeVcbmuz53mKdpBcbidt31x1MDAwM4NcdTAwMWJ3PtmcTrRMzGWHrmm0LK7rdV3e+0x1J1x1MDAxN1x1MDAxMHXth02y2vrE+1x0T1x1MDAxZHJnLELvXHUwMDA1RyQ6nSsnWNOPfFx1MDAwNyv0feqSXHUwMDBipfBi+GRcdCSoII+vkZthg+mYSVx1MDAxZqhcdTAwMDNA3PEkR1R/fUqSXHUwMDEzqd+az3ZPXHUwMDA0+dkxfJiMXHUwMDA3kYu0XFxxMNqCNuqgOUdccn0jfD3G5Uf1edDsV1x1MDAwZetA3TipvzVv2Zx/uC3H9bV9JVOUXHUwMDFlPSGNj41Kc5yeXHUwMDA1XHUwMDFjzY7YjEViuuAg9Fx1MDAxNGPvKGiNXHUwMDE3I3n8xtf/4OpxXHUwMDA3wFNzkFx1MDAxZObcduM4kfelx2x6i0ylpa1cdTAwMTOprrG0XHUwMDFix+hcdTAwMTMqXHRKXpi6Izltxfb+yPzjXCLQXlx1MDAxM1x1MDAxMphtfGDYtap2XHUwMDE0ZHtn+2Mpt72jXHS76PiacPMgbz2tXHUwMDE32KxxoZZENbEvsa2D8EmBaDEpdjbEuCY5vz5SsI1bWVxuWW+XWfPBsmtp3eQuXGZ6XHUwMDFmr702hO4rXHUwMDAwnmtZ2ZAgOFx1MDAxMpPkXHUwMDE3XGZcdTAwMTBcZqCW2c+3iFx1MDAxM/i921xcqqtGs5Yrz4oq3pVf5mK9KGqWP/tMXHUwMDBlU/BcdTAwMTb7UlxudFPpcZhfXHUwMDBiX+Seer6nR5xHj1x1MDAxNFx1MDAxMk0v29TAOOk8tFx1MDAxY5dcdTAwMGI1vL4hPG7TVKOc74FcdTAwMDBsXHUwMDAzOFDAzy977CPgc1x0Lit8XHUwMDBlXHUwMDAwkFx1MDAxMVx1MDAxMvvtk/Sm9SGEb/Bw1SN+MWCqvcTgi2o/WVx1MDAwMLdkl1x1MDAwNpDW9SMvXHKYJaHVXHUwMDEwWuFcdTAwMWWhXGJcdTAwMTPXv475mEd/vTEuonJcYq+2dOaBXHUwMDFkN8QzXHUwMDA3oXS5sFx1MDAxOVx1MDAwZWNxz5Wv3cdcdTAwMWYs9/22mlx1MDAxOETE/YXKsv6NxVxcd9bqjdi7JFk4tixohHpcdTAwMWJuXHUwMDFhr16UXZsngfw7rlx1MDAwYkcl+VpcdTAwMDbr5sjoXHRcdTAwMDKimPQ9cMLzXG7Rb9H1QpSBXCJpLyp59Vx1MDAxZm//ZdL38Zu3zUtcbqX3j7u8gPdbqD2+1avLT1x1MDAwNVx1MDAxNmd4XHUwMDEw3m9/XCJRsEPiltPpIZRvT6doXHUwMDFjXHUwMDE2yY/F3a6eXHUwMDE0aIVcdTAwMTfAiCGZLDn3o/8uRKOBXHUwMDEwiSpKoTPgt27BpVWjtTFdXHUwMDFkTVx1MDAxYSd++1x1MDAwNmNDSa2ZXHUwMDFl+/lcdTAwMWGfglx1MDAxYYq6xsAwtVM9di1W44otvsQ+RHicXHUwMDE30uRcdTAwMTHztY23lUKgXHUwMDAxUpPkIL7bwbV706iOmSRcdTAwMWYhK/o+cS7pK+JFKimtx5eO6zpxj7WmZ/3dRrTSOi+Kbu3PutLtVK9ikNt37YDHQ1x1MDAxZJiW2SDwfZmK2qtpXGKWVNfpuvS1IUqn+cfAhlBnqvg7XHUwMDAwXHUwMDE4wqUhXHUwMDBmOK+GbTyQh0PK/O2xSUZcIlU814irzq54qb5cdTAwMTahKXpcdTAwMWa4LFx1MDAxMKNXg4hqn7lCXHUwMDFjjztIP/wrfdxcdTAwMDK7XHUwMDAx6MS3XG5e9uywXFyAhICdb6v+XHUwMDA00rnAXHUwMDE3PT+2u4+8lr1cZnPIXHUwMDEyhJ85WlxcKnhw4HpcdTAwMGZGUjZcdTAwMGWTJJCR1DVeVvXGXHUwMDAwM3SK1uQggYX5orDePEYnxKDuzviW3lx1MDAwNDDQ0kPM8lx1MDAxNVx0bUw2rSBpOLHSUkpcdTAwMTXBu39cdTAwMTBcdTAwMTe/oP+so1x1MDAxY9K9UFx1MDAxOFx1MDAxMVxcXHUwMDAxRWNcdTAwMDVcZpPWXHUwMDE1YNimvPuhNWVGJnKjMPptvlwiy8rG9UHRR9JNWeelj+buj2IgXHUwMDAxTFx1MDAxOedG9vai4EG/2Fx1MDAxOVx1MDAwM1x1MDAwMFxi9VGXu3JyTjNHIJmj330hvKzf0E+DlmSK9tMyXHUwMDE19pSyXHUwMDBm6Vx1MDAwNiXE6j36q01WXHUwMDEyK1x1MDAwYnrxw177ir5YTNHwXGZcdTAwMDbAUfhcdTAwMDGyMNWNXHUwMDEyzO+duIP3RyA+8/x4wOSKPZyJWtb2iUxQjc2I8vjnwe9hXHUwMDAwXHUwMDEynnlsOvpcYmBcdTAwMTT6xlx1MDAxZVxyevjjg7brgu3wXSOTr5WKNIhHXHUwMDFikENcdKyeXHUwMDFm+UrfX0O+Tz1cdTAwMTegcVx1MDAxOaZakobTMZnDjLVfcFx1MDAxYueEvpmUT3J0XHUwMDE2dNZcdTAwMDTeXHUwMDFmXHUwMDAzvVxy8rmut5NcZkYxXHUwMDA06SFrmn1cdTAwMTAgsSP6wvL9XHUwMDFj61xuuVx1MDAxMsVdl5afNyB/bN0hUdVGzHWsyflxrbfrxVxiulwi454m8P5cdTAwMTHGXG6UK49cdF6R7586ykPEKpuFqpYs+srh+EG5+dSROX74lbbT6cS4ZW9Wclx1MDAwM1cvOs2sLShcdTAwMTaK8PTWxm5cdTAwMTPy976+XHUwMDExdVx1MDAwNlx1MDAwNZ66Opx/XHUwMDFkIXkmrzvK8KUocYyEXGLaULKJtsNcdTAwMTik/U0/urx2YzknoVx1MDAwZVx1MDAxNTZuuaWSylieJlL6qo9ccnZL7lPC2+pHLuLVa1x1MDAxZlC5sZB+3iFcdTAwMWP4gMd+ZpKfauTFkfGXvvhPp17L/+rFKDL5/Vtz3LSs/0aGSlcvXHUwMDFjpdpcdTAwMWR9vF1ppVPOXHUwMDE3oVxiXHUwMDFlrYvXm2pYKKrZXHUwMDE2vTb1XHUwMDE2qdAtao5OjJrHXHUwMDEzTvqMTYBcdTAwMDGaJIGsrzyqceBcdTAwMGLjsdBiXHUwMDE2h1xuofVbu53EQmJcdTAwMTNcdTAwMDSnXHUwMDFm/J5y41x1MDAxYT/x2nksXHUwMDEyUoHsh3o99tn1aYhhfcbz5Gvf501cdTAwMDUmnUdavEpv6F9f+vxcdTAwMThcdTAwMGaHkuTNkySgXHUwMDFlSnMnXHT4dlx1MDAxM3uvf49vdWCsy1NUXHUwMDE22lx1MDAxNYrbgT1y39hha7Rw6rhuXCJH1IJcIj6S6qaDgJPfM8+zXHUwMDAzYtg+Z1gp5uJYw2lkfXv+lHjXZn5Zra5cZiziuKdSh4/9iTjPqLl0j9A49ZCp7d11PzJS2Y/168jvXHUwMDAxZF/1VMepi4TsPd7bg1bFw1x1MDAwNTlcdTAwMWNGyJKfgFVCN1pcdTAwMWYlPzb1qP/pW7Je1Ff6rfNRXHUwMDExbTSNxXufbVx1MDAwNpt7uiPvW1x1MDAxN7Yr81x1MDAxMGNcdTAwMWRKqs+xXGJcdTAwMDN0XHUwMDFhbGg0PvrqiHZcdTAwMGVjvbg+2zx4SE3bJrpZt2FcbpdcdTAwMTWdYt9L9nmvb9BDXHUwMDE0RYXXKprudKvATyT3mLvgzVx1MDAxZCG3XVvDXHUwMDFj+1x1MDAwNYGW8EyvfP9xt3Oiu1e+a1x1MDAwYpW4U+uPyn0pNDxcdTAwMTEnY6VcdTAwMDPCXHUwMDA1fJhI/cdaUHS7XGbjWlx1MDAxN9zD3LWZn6hSw3HbcKIsXHUwMDAwjP3wVWzBXHUwMDFjr8jGS4+/mLQ8XHUwMDE32jFHgexCrjeBgTA8ofP8KvBcdTAwMTkmb289kj49uLjcsDVJ/DJD1Sv0fl5cdTAwMDSjkGFtXW9cdTAwMTlteSW0bFbViejGOLwglbZUi4G9I9+LUo9ImV7kW6Xs+Vx1MDAxOD5cdTAwMTf6pTiY2JC1g+ePXHUwMDBm1mRYan17llxuQTtcdTAwMDFcZrNTh5+cl3G5IYe3NqH173fPfva7JcTXO1JEVFx1MDAwNG2r+yaJYbE8b0a5eTOuS0uwevm9XHUwMDE2xI/9PdVcdTAwMTZcdTAwMWNcdTAwMDSQ5Vx1MDAxNFB4XHUwMDA1V5xEJalcdTAwMGLojP7uXGKQ5u5aoShcdTAwMTUuQ+BcZvJ5ZftcdTAwMDfPclx1MDAwZv7MQbpcdTAwMTWHeJtcdTAwMGJcdTAwMDVjfmZSXHUwMDAzXHUwMDA1TLF6irBcdTAwMWbfN1x1MDAxMKsxnZS+m3dcdTAwMGa+NPe3l93PQ1XQn71cdTAwMDJcdTAwMGZcdTAwMTU19/izV8D/5q0gpG7y6Fx03nnApM4729ZcdTAwMWSQ+Oi2h1xuPpLRWVx1MDAxZVx1MDAwM5vQecRiz1x1MDAxNX+Rs5SMr1b0Llx1MDAwMpTZ49FwrPRwKOtcXLw8vVx1MDAxYqmoioJcdTAwMDLedZKuxE3PXHUwMDFj+0yUXHLnXHUwMDBlXCJcYiQxXFxfXGJIjnlcdTAwMTnCXGa/eHhcdTAwMGJsgfXllDRz4Ph66plcdTAwMWbk316Y047a7ThPdVx1MDAxZlxm1Vx1MDAwZe9YkOuYhMhcdTAwMDN3ZKNfpYxeXHUwMDAxXHUwMDAynEyNV8R4vlZoIfCBXHUwMDBixOI7evZ9XHUwMDA0i9NO21x1MDAwYjJ5STTL8vVHK1lcZsXwQT3MhNA91rKqzZeCR8yKr40o0kF3PyxK3JTqtlx1MDAwM1x1MDAxNe9x74PG1KzRY31cdTAwMWZcdTAwMDVEglx1MDAxMVKGocemnIX2jPVlq11cdTAwMWOi7U+/i1JcYj9cdTAwMTmrQ/xv+YZcdTAwMWSw15XchiGk5Vx1MDAwMD9j7/I7PGJcdTAwMTL5XHUwMDEwxno+3lx1MDAwNFx1MDAxM8lcdTAwMDeDuEeK9aK9OLlSRCb3sXhJXHUwMDE45IElfPPX3yaYzM9MMDYjdYX0i1/j7WIm6VrrXHUwMDE3JETuT+J6m6x43K/PhoiuNqwkvuQ5XFyqXqxcdTAwMTPj84w36uv75NRQ9N8xaCX4JfR3fbdnP8VrXHUwMDE07q/mXHUwMDE1vzWQ3/llXnsxvEyaR/GnVTA3n5BcdTAwMTRcdTAwMDUsx4n5c0z1utjfOyHe0ahcdK+//YSC9KeHQ1x1MDAwZv72XplcdTAwMTWz/47BX/CUfs0/XHUwMDFlPuL451x1MDAxOCbihFx1MDAwMf5cdTAwMWLQk7OheWqGXHUwMDE57ONv29ZD74+OtSFcdTAwMTh+3X97n0r+Z1xyqk+xtGYw/TmIr5TiVfHakJh/j6lcdTAwMWLOfI5RT2ZcdTAwMGWIv+etkPfvt1bQey5cdTAwMDfvPzG9fjdcdTAwMDNKpNn8jdkwLeW1POqHXHUwMDA1yFxmwVx1MDAwNtRcZtz8i05I8Fx1MDAwN09hRsQoglI8XHUwMDA0rFx1MDAxNVx1MDAwZovOXHUwMDEzISdcdTAwMTdcdTAwMDZcYvtdOuR8YEW+rJqxpypNXHUwMDAyS3fHadPO9/tG3He+L8H+J1a2kFx1MDAxOSNcdTAwMDAziaDBXHUwMDE0//zp5dh3fLqme+qNT7DCt6EmeriL8Keb7mGKurGK8dY1PviAypPC5zl4v9uWRCrp2mDn/+97zFx1MDAwZvOSLVx1MDAwZVx1MDAxN/GWXHUwMDEw/tsn5lx1MDAxY79al1x1MDAxONZNpeM/Oa5bznzqPHygSvr+5Vxu03pccropnVmu5OmGwO1cdTAwMTnwtuhM/+2dIyxxQP1lRpCqXHUwMDA2J97f995cdTAwMWLBeFwiXi28XHUwMDEzW0hDXCLB9J7w35rBXHUwMDE5j+FKaFwio5rb4FHIXHUwMDA1/a4q90LFhDHH8jJeXHUwMDFmWP5vPD+P/Po73k+dZr81h4vZXHUwMDFkoin+03P4P457yu+HXHUwMDAzhFx1MDAwMpRh9edL8789cf+p6T/jjE6MZ0H/47z/OU4yhl998flcdTAwMDHBr4v/f72Bf3Tpc1jDJb/7mi9mdp7C+Vx1MDAxZuf9z3HQ3z5cdTAwMTZcIlwiivBcdTAwMWaxvX5Fzj/z57m85ln/87x/j3suXzyX14TcpOB/xPavdfu/c1x1MDAwM/0mzE85RM/l/29s/zbH/lduXHUwMDE0xfjlhtZS83P+39iea/V/61wihtlZ+pfc8OLv8lx1MDAwZr79Lm/9n9j+rMkt7G+foYCHlDX/mVx1MDAxYuKHLaX45/L/iO2pQbR8/faWYlhcdTAwMWalf+bmufzx5Eav9sFcdTAwMGL+XHUwMDEx2//uS03/JTdcdTAwMWPxXHUwMDA3o0RK0/5cXP7/1F74Qn9bW1RcdTAwMTHBein9MzerIP5yXHUwMDAzdNvwNv9vbP+7XHUwMDFms/mX3FSPpJJKzvIhQ/tHbL/58kJcdTAwMTiReUsoXG7kIZfnOb9n5YTo/8zTn95cdTAwMTTwNUJkSpp1fVx1MDAxZtuAYfODXHUwMDEyUzTKKZAs+07XbVx1MDAwYlx1MDAwM5nzKOjvv1xci334RKpcdTAwMWZcdTAwMTFvXHUwMDE4MVx1MDAxMWV5/3vO/Pd1XHUwMDE1TeP/27hBjftiXHUwMDE0+cHg4OaPf85cdP3vnOhcdTAwMWJl/Edefny1XHUwMDE1v8Uve0O6sftHXlx1MDAxZl2AXHUwMDFmz/e6mTi1M/xzXFwtRfiNuyanXHUwMDFlXHUwMDA1f/5tzoi/vSz55KZcIlx1MDAxMv5lzox/eW+cJ/z54lx1MDAxZrl4J789lsbXJDj6l1xcs2X6q4tvn66a/W91898tUWJ8l+Y/62by1L88TKxhXHUwMDE1/HNOin85+K5cdTAwMDS3+Fx1MDAxN7xcdTAwMWHEXHUwMDFmJ1XMpDLhujKgUeGpy3l30/w4n5riNErc6LqWWpmB2JtAXHLtOlx1MDAwMFx1MDAxNGedtE5HKaBXTVHvT1x1MDAxZtFcdTAwMWFSeoHxgLh701x1MDAxMU5cdTAwMTGgXHUwMDE2N4n21Fx1MDAwMiXtXFxKXHUwMDFma7CBsapNvepOcPROJtd64mJNXHUwMDAx6mBcdTAwMWFcdTAwMDBcdTAwMTJvmojlfvchw/JWXHUwMDBiPlx1MDAxNFx1MDAxOelcdTAwMTX3NmFTODomaMRcdTAwMDL65jW9qFx1MDAwM1x1MDAxYT1/XHUwMDAwhiHn9aeVXHUwMDEyJ3ZgeUs79NZcdTAwMTaSwDyaXHUwMDAxXCJy9LqAL6mLUOyrXHUwMDEzXHUwMDFhS/lIwdtcdMG/eVxcJWx5XudE2utcblx1MDAxM+amo/lcdTAwMTZcdTAwMWIgXHUwMDAwIIBNrq5mTozJKlx1MDAxN4ZccvPjNPJcdTAwMWPRLjy5P1FRlLxV/8ZbKORCeWQuo//mfKtpU0RcdTAwMTDy7/9cdTAwMTFcdTAwMGY470OxpPNlXHUwMDE4jIJuXHUwMDAwqNY64iCIPO2Lx1x1MDAxY4/Rd9sq1uam5JXkU6W92FPYXFxcdTAwMGbtKPCPn1xuxE2B/UjOcp9cXFx1MDAwMO8hVlG09FtESVx1MDAwN1xc87dcdTAwMDJhPPx458jR9HlF8t96L1x1MDAxYY/wgnh+7Fx1MDAwZYMrvqtReLt8IcGa7SDRalx1MDAwZVx1MDAxYeFLfPD4/mFcdTAwMGZcdEfv21UmXCJcdTAwMTZry4V1UHqNSjBw9VxceHHatZxNZHSuULHvhlx1MDAxOFx1MDAwM/m10/hcdTAwMWF/TnWxgppXts1IXHUwMDEzXHUwMDAw8E5kStg2XHUwMDBizeC1Lp+421xmXHUwMDAzZi3nUE5ynmdvoDLDXHUwMDE5PoqD9C7zKlBA9DyEcotv5TKpQ1XF8tPDlVdcdTAwMDDauDnmT3/q3u0mO1//ep7B99bwWeD9eFx1MDAxONWVXHUwMDExXHUwMDFlrNfrd8+FapZX4TB/vITXa5/j7/1cclx1MDAxNfq391xiXHUwMDE1woSioPnvPVx1MDAwZW7cW475O8fr/2PuS5pcdTAwMWRcdTAwMDWa7X5cdTAwMTBcdTAwMGLEXGZL5nlcdTAwMDYx7JhHgSTE+Otfcft7tsNcdTAwMTFcdTAwMGUvbVx1MDAxNt3qe9VcdTAwMTJVlXnynMysYta3b7P++cGelixLN9ffe9y0wNIwvl9cdTAwMTN5XFxcdTAwMTHBkf/1NE2YXHUwMDE1u1x1MDAxZV+RIFjf9Vx0o3hcIth1dTf+S1T7fFx1MDAxNZIn3txRaD7Qyt/+XHUwMDFk1uz4YIVcdTAwMTNcZkD1NJx7YvXYJndf8+tXXpNzXHUwMDBmfpLKujgq7sHhs3WPx3z/jiCuQVC9OXG8Ws/0Pjyi8ZtcdTAwMDayPyT+x8M9mlx1MDAwZnzW+cebp916llx1MDAxNfBrdpQqXHUwMDEywezliDjdrp9cYnNIYH6Us3lw/621LJL+yzFKXHUwMDExgTxcdTAwMDf2L99ccqhcdTAwMDVp9M1tqlK8q5Zq/nuPXHUwMDFjc8hcdTAwMTPB//RcdTAwMTSCfWJcdTAwMWFcdTAwMDWfZ75cdCZ4YvdcdTAwMWM38f/Hc2yuK1x1MDAwM1x0XFyDoiCwZYfSIyRq64aBXHUwMDEywXs4jyVyXHUwMDE0dNq2Laq3XHK4ee/txGPqNTUti2KeXHUwMDE4aF23clx0n8/TLSWrfpBM7Vxi/baYzrbvO1x1MDAxNMS/6kpcdTAwMWZcdTAwMTDF/N7fOF4oyuCE84Gf6lx1MDAwNzVcdTAwMDCdXHUwMDFh5iAwpUG6Y5iUSFHvJefNIz1cdTAwMDMnZWn6o1RZ3jxadimqijysqb/Sv+dMplx1MDAwYl5k6m9zXHUwMDA0XHUwMDFlMCpHgTb/nSy9uC/rqlx1MDAwYtYrcFx1MDAxNKEyXHUwMDFjXHUwMDAwTVx1MDAwZtG3ZJSTXHUwMDE0RZNcdTAwMWWhXHUwMDAyw3BeW1x1MDAwZVxm0yBqWFx1MDAxNjO0XHUwMDAw0HzGeOc5hT2ZXHUwMDFmXHUwMDAz1/BcdTAwMDZBXHUwMDEwU8aHev2t3/QvVzZJXHUwMDE17NjFyo5yRZJESuxcdTAwMDOwl1x1MDAwZfja29mTYZpeLfg+9KHEgevWuWFQ9IOUe8/zKoTz0mRcdTAwMTcohrFsckJLXjVcdTAwMWH50UtcYjl6eW4oXHUwMDEyZkHa4zNcdTAwMDZcdTAwMWGxPLJUR3KGqVx1MDAwMCVle4CbJ3QgP2+27lg2fyVbfsXrP1x1MDAxZY5G2TBcdTAwMDCNqf3r30bBeOvqsUIkeeLhlOfTOLYjNvAv7iUg1K8znYpcdTAwMTd2j3d1PMTiXHUwMDE4g1x1MDAxOFx1MDAxYbtocMH0INDOXHUwMDAzXGI1bty8J5I0/3hBM7/eun77VcnKoqv+utt+bIpTXHUwMDAyhPw58Vx1MDAwNyl+UVx1MDAxNIVPXGLK8vwyVlUy+aKGXHUwMDE5hvmtK4S9NnZcdTAwMWLsztVXT8IthIDhOP6iVHVvX1x1MDAwMLaXUiFXgSBcZlxcR4+o3/ud2vczsq05dT+sdC/+yaO45IVPa3JegbJ9v3m54CbPRUFU6YmMg1x1MDAwYphcdTAwMTTprPCud+ysoU+CZCDqXHUwMDA39Vx1MDAwME1ccnvJmGr9/VxiksTHe3DDz2WQXCJcdTAwMWG6XHUwMDFiK1/r+on8J+CO91x1MDAxZSpT/fEs8G2e4OJcbqxZeoxcdLNu2/iy4Vx1MDAxZYDKkmkn0/7KZ8TDYVx1MDAwNFx0p5yzjeiXOtB8JIHFeZ7HOcu6dfhzV0JVvc9cdTAwMGLMzJJcdTAwMDB7XHUwMDFjXHUwMDFhUZ15/NZWK1M/o69cdTAwMGJwW1aR6f3+mYhy21x1MDAxNuR1XHUwMDFmXHUwMDBl6ETp5kvWsVxuw1x1MDAwN4G367qwXHUwMDFjQZ51blx1MDAxNkVcdTAwMTEyJVx1MDAwZcNcdTAwMTRcdTAwMTOHrTc3LsNbg1x1MDAwYrz/XHUwMDExY9MoPX/TXHUwMDE0P4vq2Z/K3Vx1MDAxMXiRsSMgXHUwMDFmhO/z/Lv9KJukqCPjbr1JuWmcXHUwMDExknRz5mVYpyYovFx1MDAxYodAXFyeUNxjtXvtZZ9cdTAwMGbXPFx1MDAxNk31UdfvR1x1MDAwNVNcdTAwMDSBomCQ2Xu9n+WOU9FcdTAwMTZv2LpBXHUwMDAwXHUwMDE3KLLcXHUwMDE0SbpcbtUtPpFrn+BcImRnXHSAbzHr/bzWhbRte5m98SFcdTAwMGJcXD+AyGt4hYVcck3R0sB39cdg3b3TXHUwMDBmZHQvf0bBQnhox3Gz6/7tXHUwMDE3dKihqOvKp9liXHUwMDAxxOMqZVx1MDAxN9K2kF2VXHSm6SjxOin7Rb9cdTAwMTH4XHUwMDFmNviLd4ZcdTAwMTamKFx1MDAxOLPkQDCX+eCAeI5cdTAwMDDc1mb1x1x1MDAxMlxuuX3oeFx1MDAwNmSQXHUwMDE3U1x1MDAxNHlcZu31t49GJvYn/+NVzr1jbJHvJ80qjcBcdTAwMGJg8lxmw3D77e7raZJ8TzRcdTAwMDL8t21cdTAwMTNQz54mRdehY7j3VdGiMdVYNFx1MDAxZp97XHUwMDBlRVx1MDAwZiGCYH13IEb9pJBQ/Fx1MDAxYehcdTAwMDP2nmtBXHUwMDE0dLrV9I9cdTAwMDLmNS2j5zNcdTAwMWWFt9KX9iuiRcElNvCFXHUwMDEwTKqTr9OnXHUwMDFjRa/5XHLBXHUwMDE4hj3Cxv2lpjI3i0Wpq3FcdTAwMGW3m+hZPyAv918+geW9Um387F88sD1cdTAwMWbD6t/vt2a0rVxiVG1PwZZcdTAwMDOKXHUwMDA35zxE06pcdTAwMDZcdTAwMDAvjo3FV2NcdTAwMTWw4j1Npotg5Dncw+f0XoqiyphcdTAwMDDfYFx1MDAxOMpcdTAwMTCvUlx1MDAwN4j3h1x1MDAxZm5Rp5tTiqa9t4TdaTqxPanSRGXuZFx1MDAxM8zzb1x1MDAwMXae9f/s/Ojf79H4p19LpPtovuLyf7ogzyVxWZbEK6v1NX5bw1x1MDAxZoZlRTO2XHUwMDE3ZomC1tdEVed1UVx0XGZvQFx1MDAxNLnEudg8z+OJTVq/bcNCQnpcdTAwMWVCx1x1MDAxOb/qN1x1MDAwMlvDXHUwMDE2PCnqX/NAUZOr6lx1MDAxYbBMwFx1MDAxZFwiqlxc+493g3nzYzddvnW8olVwrVx1MDAxN+veilVGUun7Mf3TmObjmXZRXGZ883Wlv9eGoShKo1x0nZmw4i/xOv/lJ9Ou/Xv/OU3IiTkr0I/jWpFZmeJ/ayAywue93mDGgtA7PFx1MDAxYfDyxlx1MDAxY9dcdTAwMTGeZSiC94gzb1+fn7T/vVx1MDAxZYk4csRcdTAwMTDcX/1/uT//sXbDP3rzpHkh+1x1MDAwMy1W4ji6fD7c+z18o1N294j/7SOORiukPd5G/7OvQS+eXHUwMDE0TVx1MDAxMyfOhynAyHGa+Pv7hJ37Rrje3zZjyrHLmrP67/bFXHUwMDE4XHUwMDAxn3Wvl2qw31x1MDAwMcDtv/tLs1x1MDAwMlx07tzjrN2ETJxcdCbcLuplgLgxra8tqsI7LmlcdTAwMGVA/7zp7r5mZddcdTAwMDC94ee/3i1uf9YxXHUwMDE33rFfbVx1MDAxNn1cdTAwMGKKfzygeI7fS1x1MDAwMnOs/D+a46ih5j0xXHUwMDE19KIoSjaZavN3IJZqTE/NV6RcdTAwMDHsI1x1MDAxMYYhqItcdTAwMDHxXGZ75HasXHUwMDBmdKLlNYwhVFx1MDAwZTD7ayWGlGAvfVxm3sxcdTAwMWI4LTKpj8xcXCxAXHUwMDA2qO276Vx1MDAwMFx1MDAxZbs3uu9cdTAwMGZcZnXAx0HoPOOEbidcblBcdTAwMDCbjYzHXHUwMDE1XHUwMDA0wONcdTAwMWOrrKFwuce9OZ9s3TuRXHUwMDE5eFx1MDAwMmhQPoxw8NbpJd3juFx1MDAwZlxuXHUwMDAwguv75fr+wlwibYCqXFxcdTAwMDa+naTWJPSeNFx1MDAxMnhSbcFcdTAwMWJnXHUwMDEwsjSkIVx1MDAxOSRcdLlcXGI50+RcdTAwMDFcdTAwMDLL7X9cdTAwMTCKMGvX32FcdTAwMTOlvHeCXHUwMDAznH13v5tLUD/45lx1MDAxNYVcdTAwMWLmwCW2iVx1MDAwM5yPXHUwMDAwvIopLcfBn3ZcdTAwMDJ8d6RyXHUwMDEwXHUwMDA37udcdTAwMDNQgEdWXGJe2MqPPlx1MDAwNpXRP0CTitLwXHUwMDE1cu5fTpmTXHUwMDEyJFrcv3WjK23OrH/5XHUwMDFiXHUwMDA2/eGnXHUwMDFmxaj6f+JSwf/GpUy5lfm29bLz5nNcdTAwMGKQvVx1MDAxMIh910HgOKvkJaB3+Vx1MDAxZP6pbJqmz2evY5qAbjzJrqufvyVngJ+v61x1MDAxNCuyXGZcIlx1MDAxMoxDNi9cdTAwMDYrQ72znv2XXHUwMDEzivzVsoR//Vx1MDAwNtxQIWrj8X+8xMZkVHFcdTAwMWOnQlx1MDAwMc9cdTAwMDHeXHUwMDE0XHUwMDAyMPuOozcjVP9Qu6ZcdTAwMTKAeCkrXHUwMDE0XHUwMDBmadd1Vfdh0FxmMJ8g1tsgbtpNqflcdTAwMTJp06h60Vx1MDAwMoTlv3+4bLCo9Fx1MDAwMu6c5mFZPvV/NVx1MDAxYdLUPtFdJVx1MDAwND5BtCxv/PU+euvuP+v5WW3xkFV7XHUwMDBmpl2zPJxcdTAwMWPUQ03y/NVsW+37VFx1MDAwZX4+5YpcdTAwMTJcdTAwMTdcdTAwMGaV43BcdTAwMDVgp1x1MDAxMn1cdTAwMDHntC2nnoZhx0j0XHUwMDA1uMHzUFx1MDAxMT2K4/VDq4CoiLOiZoLzmd+adPOTVCsj8bHydy7/NyduJv6tJaCghOQlMbCzKcAmQLk8wMtlQVx1MDAxNpJcdTAwMDL4V0NTv7mJ4q+mpcVjf1Nqv7ekqqVlOU5cdTAwMTQ+L1x1MDAxYmWGcYxeXHUwMDAz5XPb/ILr7ZtX1bpcdTAwMTKpvICPl9VeqFx1MDAwNqM7tH95XHUwMDFlLbWyIfzLXHUwMDE1KFx1MDAwYliKVPyLKVx1MDAxMLsuS3Nvx9825lx1MDAwYjg9s3BcdTAwMDdDXHUwMDE3XHUwMDEyXHUwMDA27uqzPzdqYVm2YcVRdJ9cdTAwMWWy/J5RRFwinncw9zlcdTAwMDJeXlxmQWeB1zQqaZxcdTAwMDR5qo586c/jXHUwMDExJjl65+yQXHUwMDAz2yhcYnKBUZdcdTAwMGLwXHUwMDBiprdtiFnOXHUwMDEzxWJcdOMtM+hIXHUwMDEygn9vwHlcdTAwMDGhWqH9hKDP9/twXHUwMDFiXHQo+Vx1MDAxNUfXsdO1W99pWmk1KC7f+H25nPPbP1xcef9D91x1MDAwNFx1MDAxZHhZSH5cdTAwMTGCYODfXHUwMDA3jOX3se0qS9Z2o1x1MDAxOIjZvlx1MDAxNFxmVVx1MDAxZlvmlbDbaDFg3/p2PyeXalx1MDAxYVFp6jVCgVx1MDAxZlZcdTAwMTdcdTAwMTg/9LjXXHUwMDFk69jEXHUwMDA1lIRT1aJ7lnVccmhG4z3OVO0p2Fx1MDAwNfMu839+8c2B/c9kXHUwMDAzbDx0P3TRmVx1MDAxN7Z88MW5uZf43oIg6pH7XHUwMDFl5U7hVTG6taU61Y14P7487iGoKst41uO+fVx1MDAwMFJYr1i1QVx1MDAxNzU4XHUwMDEywtBLsT5cdTAwMDE9UVx1MDAxNz743Fxc/epIXHUwMDE4VoyeOsH4XGbVa9JcdTAwMDbN0zeQXHUwMDBi42SMcrZ8UFx1MDAxMy9cdTAwMDDXozyAJ1g0JkdZb9Po+Vx1MDAwMqRgXHUwMDAz4MbXr8zD0PlX58LS9n3QXHUwMDA3e4N2VX/fOX+LXHUwMDE1YFx1MDAxM79jXHUwMDFiZpyGzOWziaMhyi7Tst9cdTAwMTGH76N6T9XFVCZuXHUwMDFlzng/f1d6zFxyraSsMeVJ4lx1MDAxNn1cdFx1MDAxOVx1MDAxY7CfXHUwMDA3x/RlVVUl0Fx1MDAwNFx1MDAxY/fn80j1RDfPXHUwMDFkwPjH7/ddiui/vHfaf7jsP3U17lxcr0GNgZzL+LlcdTAwMWViyNV34zhcdTAwMGVcdTAwMDZeNXbQtEZo3Io3z7CRM4hh3nMvXHUwMDAybcZ83XFcdTAwMWN7Pl3OXHUwMDA0SKNcdTAwMTYwW5UoXHUwMDAwpqNHf8zHMUdcdTAwMTQhsUSAyVx1MDAwML+qXHUwMDEzcFtiT0ltUFswWrPfqsr3vIe2QVx1MDAwNVx1MDAwM8Orc1Z9O97850lVKJBMZHBcdTAwMWOEl9dV9dmuXb43XG6/kYIhyV+ZZlx1MDAwMLxEsc9xwEPr9ed0djBBU1xcV0tCw3DfXHUwMDA3xPxF109cdTAwMTPwpnpcdTAwMWP4IyeYhHd3XHUwMDA0nk+hq57I61x1MDAxNcWF/DViWzkv1Fx1MDAwZW7tjFx1MDAxZD6BXHUwMDAz7k9cdTAwMTCaXCJZ39NXXHUwMDEyXHUwMDFh8Iph/fppJNy2ZHuo9Fx1MDAxNF5hQH7AVey911x1MDAxZGorK1HQN1x1MDAxOWtcclx1MDAxZsmxbVGU6DlcdTAwMDTkXHUwMDA0l6PSKdhcdTAwMDTQxYtcdTAwMGLZNnDqayRnsWn3fORfMVxyYlx1MDAwZpOjOFZr3+jWO9ZY8S/RXGb0XGItulx1MDAxMpB/ozf6XHUwMDFl+I9ticzD4/z8wrDpV86JuL+C53fsnFx1MDAxNCxquH9cdTAwMWTdpGLwu5V6XHUwMDE4TEhvgiBcXJvg2no7aJP5Rrq1XHUwMDBidFx1MDAwZZc4+1pcdTAwMTRcdTAwMDJcZlx1MDAxNlx1MDAwZWlNo35cckFcdTAwMTBH36pDNFxcXHUwMDFjiv3kkMiyc+pcXPhf3aecmKf++nutUO2PQdDoWKBcdTAwMWHcl09y5KNcdTAwMDE4Y/NcdTAwMWb1JY6aV3a7ynLBRzIuVWyuXfzKTFT8xfG4+Elpt2NcdTAwMWZcdTAwMTLH54+cvH3TlfiTSVx1MDAxZeCeefhgvjRUVcBcdCHDtfztXHUwMDBiuFxyw1x1MDAwZVx1MDAwMFx1MDAxZlRVdFx1MDAwM6ZcdTAwMDZcdTAwMDJyOk1Nwa69eW5DXHUwMDEw5OnhYIA/5N1uWEJcdTAwMDPmY4++1kJPV+Oxna+LQvHW8HLqK1Jd991cdTAwMDPYaUoli72aINjLMPXBKnS6/1x1MDAwMMHdpWTCQrRn/ns+XHUwMDEx7Emb/Fx0v/1cdTAwMGa8+kGAXihlTTaPbc/ns06B8l2jPjC0W4fr4Z23M6n1XHUwMDAzXGLl7ctd2oaZXHUwMDA2pCh2vZRGUe/zxtWH1Ic24JGP7smbKFxuwVshXCKEyLiOO4xcdTAwMDSpXHUwMDFixlx1MDAxN2jx7fOmtjJ57qOkVlx1MDAwM/mSKy6Cb41KfFxuNpO0gy+6XHUwMDA1e6P2XHUwMDFmr/K0llx1MDAwZq0w57SH71x1MDAxNFx1MDAwYlxiYN91VZmSU+NTWlx1MDAxMLHhhd1x4u/DtGGPtkqPSM5ds74t51x1MDAxNICTXFxulGFgfvJ5uNhFMoXo+1xcqf1cdTAwMTS1V6hcdTAwMWR9+lx1MDAxNodQaKxg+/0ss0A4ltxcdTAwMDE0ROh5gJG05dRRtKQ1X15xvuJcdTAwMWFcdTAwMWJDdFx1MDAwNLzqts9BXHUwMDA2+pPHdaI6d/cvx1x1MDAxNKPdx9RcdTAwMTV3Vjy3e8dcdTAwMDUnTtBfvVHIuF1X6TtX/cNkXqPMifp4wGaxsG2Ar06EeVx0IyGGYsttaJaRxOFcXN9hXHUwMDEwraKTwf1kXHUwMDFijsq2s1x1MDAxNiuvpU6APUvz3sZcdTAwMDdwXHUwMDE5kMPvpTtcdTAwMTggtPSKQDGIa1xmOn3K0P1RejxtP1wi6dtZlIVYaCczaYB+llwi1/U70+i+yKX9ndVcdTAwMTRwafY4r7w4eZ9cYllXXHUwMDE4XGbT5ph0wjxcdTAwMTPlXHUwMDEw3Vx1MDAwMoDiyFxcUj6V4dV9iEtVds6d8cHDNlx1MDAxOVx1MDAxMj9JqK9tyJDYzVHSfUSbX8XZmS6l61AsqiZej/CsUl1p/OfVNNfB82zSLmPnfbjG6jxcdTAwMTY3Y15kxIXzUP13vv2hTET0Z7k9hS10XHUwMDE2/lx1MDAxZYkqVk/s7nORXHUwMDFlpnM9slPh1op7aD5cdTAwMWSTnkc73Fx1MDAxNOxbXHUwMDAy1pNcdTAwMDfisOVcdTAwMTLe/zTv6VWJm7ZcdTAwMDLMnj+MQMk+3Vx1MDAxNHP8xFx1MDAwMVGr8/1cdTAwMGKg2tAwY7zyUlx1MDAxZvx7L6xIz1x1MDAxMp19Os+c1q/iS6REUOVcdTAwMDf5oCufXHUwMDEyXGb0YuFrf1lcdTAwMTZXSMCGPiNWfs1cdTAwMTMpVNhcdTAwMDH8XHJ5dDbSNqa4vFx1MDAxY2NcdTAwMDVcIluFSVxc2Fx1MDAxNUcywWrzXHUwMDE1gKU7b+VJ1o9PRPHmSj8+1jlXXCJU7k2CKT2dfT5cdTAwMTT4IVxifc9cdTAwMDLhsK5NXHUwMDBlmFx1MDAwZoNIwGu+gNv45TKMe9CdflX/dCB5l+/oOp1/vGRh/nz8a1eyZF5+lFx1MDAwNVx1MDAwNCOJUj1cdTAwMWac7UGQp4PWqXzZuTmGvISdWHPxvLbqfpYzRP0hdU5UJbmjhbNnNDGoa8exrkRcdTAwMTdowTu1lvVp9qXwbytcYvrpPmtcdTAwMGafhO6rnc1M4H6wvFxu3/dLQvZcdTAwMDNmIFx1MDAxNm9U4igqSve1mGLS2lFcdTAwMDA7wCZp0jiqtlJH3sG9XHUwMDA34No+54fFd//xXkCqLGrxa13x+ELS1lx1MDAwZnBCeGPAVVZcdTAwMWNRvUi+QTknXHUwMDFhoTlVSdr5Pt5d6Cqvn1x1MDAwM76HWnlcdTAwMTaVh+5VyNRcdTAwMDG3XHLkbmKVXHUwMDFhrf9cdTAwMTk0zzA9n8+Ysp/TTnblvz5cItT906+8SjQh17q8wrLbdtduiM+JRa5cZlx1MDAxZlx1MDAxYX+KgFx1MDAwMC6AXHUwMDBii+xn2VV/n1x1MDAxYnI8RYlrpqnv+KBcZk/W10etoKhvL0ypzOOzzVx1MDAwZuSE7SXkpN7iXGKZK1x1MDAxNq1cbuxb53beWGk/3tjKSFx1MDAxNHlYm4RcdTAwMWLci/BcdTAwMWXL66LTlHRBJGEkxV/Kz4XhVsTYKIaBkPw1e2M3NSdvXHUwMDEyyfzugzg2XHUwMDFmS1x1MDAxM9n+IywhXHUwMDAyxMNRPeG6XHUwMDEzkJQulLk11S69z1j4fLJcIjurXCLhOs5wrFwi0IZjZyD73nmQmC9fXHUwMDFkXHUwMDEySJHw0D/m3Wvmy2vkbV/VJ1E/XHUwMDA1sk5CoLmOrldcdTAwMWGRfnZnPjXyyy8qzdHDZlU1jvXVlJjEkvSC94NYt1x1MDAxY8He/fhV8iPmeC329blUPf6f/lx1MDAxOFwi4i93XHUwMDE34s3GadZq9ppGwMvI7qLmqlx1MDAxMkcvzVlY5YrlVrQqtpYoaqkxfOlcdTAwMDB+t0PTJXFcdTAwMWP4Vv9jpULSP6tOQ1C0m1x1MDAwMm1d11x1MDAxZHvrQvPUMDCgKrdWS6QxT1x1MDAxZNlQdnnyXHUwMDFheTVcdTAwMTlcdTAwMDc11FP3V1x1MDAxNuwqPjFcdTAwMThQaIZ+I4vCy3z3Tlx1MDAwMVx1MDAwYnj/foEoU2cpvFx1MDAxZqTFNZk2XHKuwDn0VLj0hKiUXsDG2VNcdTAwMTNcdTAwMGaJw1xmn1dcIreuXHUwMDFiwj7P7qzE4ixts9fx46KqXCJeXHUwMDAyXHUwMDAxZLk5PZ04T4FswqyWQFpJPW88rDJamzqWXHUwMDE2XHUwMDFimSglRblila9cdTAwMWVcdTAwMDAwXHUwMDAyr9xcdTAwMDSOu4q5cXdccq9KsCrsK7gw5Knau45DfX7YXHUwMDBlv42mxP2052MmXHUwMDFlVFx1MDAxOFx1MDAwMVx1MDAwZa2H37fCXHUwMDBiLveXXHUwMDBmnvlXdP7TLbYqNWbwmpqaNUZcdTAwMWRYvEB1Pil3X9nL959cdTAwMWKAOCm3XHUwMDFjMVx1MDAwMuW7+oZ2qZKO7N6Owyw6drmhKrpcdTAwMWFLpuaGzt76XGLBXZFnwu9cdTAwMGbw+6/WJjw7IFx1MDAxNbl8X/BglXJcdTAwMTdye9pouoVcdTAwMWJdx1x1MDAwMLMttVx1MDAwMkRXwtEtpcdcdTAwMTJccv1UQNN++jnkST7eeKCxn0pcdTAwMTHSuj2onDnTKsDhY+tzXHJcdTAwMWWmcvZcdTAwMTBcdTAwMDUyjFx1MDAwMm+mXrHlzVx1MDAwNqZcdTAwMDB0MNexQ2Wj/P6jXHUwMDEwqaZcdTAwMWL7ONaWKlx1MDAxYV07LXdpXHUwMDEyTbVcdTAwMDXoulCRhXVZNH3VkkU6iTTtzOiQbrl93DotbFx1MDAxYetH4PiROCzhKnM/On2FP9nX8JcjlLrhL1x1MDAwNzdcdTAwMDBcdTAwMTcyNFx1MDAxYfCztp9JlH1cdTAwMDPZKmiuufeyKtDFx++12ezkQ1x1MDAxY1x1MDAxNlx1MDAxOG9GMYTT5Ne4fOq+fPeD87bHzpFDLZJFw1x1MDAxM1xialx1MDAxYjttu7X635DTYVW2jUg8rjih0edea67B2cE7uVx1MDAwZTlM67QtSJ/gVD2p+91cdTAwMDCkpW0yIXF1vdkla61gRZJImoppr1x1MDAxZXCCXHUwMDFkLEgod6XzMYLTVzq6P0XjXHUwMDA1XGaLq5q/XHUwMDE4vml8vubzzD9cXJqFKfZRmLZNXHUwMDFmXHUwMDA0f6gjKlx1MDAxMIB9XHUwMDBl+9FcdTAwMTRVZXKEvjeYuuyHlE1cdTAwMDKpcljz+s3sQ1x1MDAxNyrW4JaHXHSC7lx1MDAxNFx1MDAxYv7QiTyqfX/zWv2r6UI84Vos4ECnO4iqXHUwMDA3vOMl88/U+1x1MDAwNPm2bd9fXHUwMDEwid36VjXPXFzF7DuEdONz0WT170D1k/J0c3aQXG77XHUwMDE3RYtF+FWWwoDu2tV7nnWlXHUwMDAzxNZzMppcdTAwMTlcdTAwMDJcdTAwMDfPhtfh5Sqgv0jpW9pcdTAwMGalMek3so2YRM0gdnZBXHUwMDEwkvbMuFC+94DqrEhR4KM6vOg3THBmS1x1MDAxY1x1MDAxMMjNTNGRXHUwMDFmXHUwMDA1XHSwkef9XG4vNNqyfk0zms30XHUwMDFiJvgpd50gyNpD7Fxc1zpcdTAwMDCPQmR2qjpOsVxmWlx1MDAwMlx1MDAwNtOX3imHXHLMTq3tVzBcdTAwMWJMNG3a5YGrn3RcdTAwMDfzKFx1MDAwYrpcdTAwMDFoqiS7gppBgin2org3XHUwMDFmx41cZplcbkK4rbiMXHUwMDFmXHUwMDA3JMNdUzA9XkaFfzFIL1xciYtMr/1OeVOHXHUwMDBlocHWgYCrPFlPXHUwMDFiXHUwMDEwkuCZhZrsfiEheFL3ii/em5iOtj8+Wd/tL7wm2r3hXHUwMDBl7Tu29CfUn19PyJpaXHUwMDEzXHUwMDFk5XMoklx1MDAxZZHe2Xm8oG6+ZHJcdTAwMWYxzDDzh6SDz2teZVx1MDAwMLLyTJtZXHUwMDBif3j4479tXHUwMDEyluIgz1x1MDAxOM1zLPLQXHUwMDEwgdO8k20wyVx1MDAxM/hW55/29nF8NVx1MDAxNFx1MDAxY0F0xKZp3qSQXGI/jt1cdTAwMTRARCDF2WqNZz2mfUay/61hXlx1MDAxNruRXHUwMDFmVdXkzWxsfsZdTUrQXHUwMDFk7Zjvv3zxlzjR71x0+H3LsarDomJ8+r7nyv6VPT+Zq580vXxsQVx1MDAxM5og3vHZQ6FA3lx1MDAxMltcdTAwMWP0I9V0Klx1MDAwNpKy0OzpgSNcdTAwMGXhfvWNdY8vkI/978U+Yu6LP3xgdzFcdTAwMTcttPhOg4FHk5dcdTAwMDf01bv0ctNUOVx1MDAwNfH9ZNpcdTAwMTWgyO+za7T6wG2+XHUwMDFlMMVx8tT952uCxbVcdTAwMTlcdTAwMTOubszZ8epwKc7T08tVXHUwMDAxzva8e2iPj+uK/KuOWo823lx1MDAwM6eq/lL7XHUwMDAwudRLudTn8NVcdTAwMTnr9JLjXHUwMDE3xTmNPqYsLbTdXHUwMDAwXHUwMDE00Hv0h6D+nSHAntzjXHUwMDE1efd+XHUwMDFl9X2wM+d6yLEjQFx1MDAwZf4qwnXxn6RRemBcdTAwMTLdum1cdTAwMDDZJkJvqPA3zV+vzVx1MDAwYlx1MDAwMqDbNFx1MDAxZYC0af/hXHUwMDFirFSFlaPWvOFwNFx1MDAxMb1cdTAwMDe9XHUwMDA0Up7hT27SqEHkxWLPfflcZjaVj+NpfuuJpG5cdTAwMWPZ7Vx1MDAwNNlnb9d5+Vx1MDAwZTohZzrrblx1MDAwZlx1MDAwNGmVqVx1MDAwNUNpzn1cYn9cXJ1cdTAwMTeO4oWuXHUwMDAxNoG7zIenkuDGaX42x7RGkNNtxKlcdTAwMDeTYstcXEOzXHUwMDBmXHUwMDBmSMbHh4vkXHUwMDFhNoz2e++8+jTLryu3r/tcdDEuqou776+5+/4kon/6nvj3XHUwMDFh6njij5Ncbq4/P0U7XGZU7ftcdTAwMTBcdTAwMGXfL1x1MDAwNpzUiqLHxVx1MDAxYXH8MT7lUjR8XHJ/ta04tYMvXHUwMDBm7XhcdTAwMWG6OIW86jlo8+yvXHUwMDBlUce831x1MDAxMFxcdK3V+PlproSXd1x1MDAxMmKP6Vx1MDAxOLX6XHUwMDA06ceHhOK9XHUwMDEy/2Hr+Z2bjOdl+9F+lV/i9lWUf3eFYDv3XHUwMDEyaTzceLJkXHUwMDAzfoIh1l1oyEfilqRpXHUwMDE4xoI2fPNcdTAwMWTOXHKpXHUwMDExPlx1MDAxYZjb5949Wr9cdTAwMGLDeFx1MDAwNZHH7Vx1MDAxMVx1MDAxYTa95taDlkqFqGX9q2fKKC9cdTAwMTON036hI5n9XHUwMDFkXHUwMDE1Yc1SvexY6C1cYlx1MDAwZXRxvYHxU2p+Vi9piJ4k3YmWOIl8XHUwMDEwhFx1MDAxZPLBwFWmq1x1MDAwMSlccs1cdTAwMTdKUP6cn92db2yz2ChcXFx1MDAxN8R+iVx1MDAwZqpl/GdcdTAwMTNcdTAwMWVFQEL2tFx1MDAxYXheezjCXHUwMDAynit3Ju448NWMXHUwMDEyxz9cdTAwMTCNOFx1MDAxN1x1MDAwZUdccq4tgStcdTAwMDCnqjV4t9lFpaR5ZVx1MDAwMlh1vlxy0F3X7vqAXHKGYlx1MDAxNvGvQL3zUFmOUMxOlMGk6syPoHK4olx1MDAwZUu/yNsmmkt/XHJaN8yYU0Jf4evwtFx1MDAxYuTZL9qm+lx1MDAxN/tcdTAwMDBcdTAwMGJVl/pcbnB82txHsp86fErFT1RcdTAwMDFOdKtUflx1MDAwNGX4q0VyO/eKwlx1MDAwYrF46K9Xxd9cdTAwMTHlMftu8CpcdTAwMWbIgkJpj6/bXHUwMDA0RVx1MDAwM1x1MDAxNVx1MDAwN/5hdF6ko5tHN7z//lx1MDAxZjpcXJiNNct5cdG1LfF8SHW25TfBkZ5oTaiq/UNpocp2nGNGmkLX37h1sVx1MDAxMlx1MDAwZu7Q1sZgXHUwMDBlvo9cdTAwMTDF6Un5PVx1MDAwMdDLXHUwMDFjL/mcPoxcdTAwMDbDy2G4t2VkJ+33TFwizjLY2nd8pWD9r1x1MDAwNVx1MDAwZlx1MDAxZnc77W5EtTRuWr4mXHUwMDA2gue5WWaglq70mNt4YtHVni+0p+BcdTAwMGI960rqXHUwMDBljVk8Kcnp6uI+XHUwMDA3OIdxgFx1MDAxMGjCZvshd7LF2e+Vw2JcdTAwMDSi4Vk08G5Au1k2T8WTrVLs2WDiXHUwMDFjXHUwMDA2n1x07cpcdTAwMDc9wziBX/Nfrr42ri9cIlb/6Vx1MDAxMXdcXL5E8HDgNUe6fJ6jXHUwMDFmYlx1MDAwZtDUeVx0W91cdTAwMTKoXHUwMDExSXJcdTAwMWM9qiP6mztcXFx1MDAxYprMm9u3XHUwMDE5TCaI9qPfXHUwMDFjmt7IXFy1/FrtpLlX8Lq0aaXYN1x1MDAxZuyd5v7kPmyOya97YVXbXHUwMDAzQ3XDqN68yVx1MDAxOEiEnnB/XHUwMDAyvFx1MDAwMqx1t/jWPVx1MDAxZYfKn+qDd21BXHUwMDAycfD5LGVrUlx1MDAxNFxmXiSF9l+jgqrph3Zxe/JK01x1MDAxZSdeVVx1MDAwNzVcdTAwMTE6n1x1MDAxM4XMn4ORYkNa4dr9I/jhK6zNy5FcdTAwMWXFu0lcdJz1xo6/qFZ5aEtcdTAwMDbklrNcdTAwMDKd8zWe/m7wn4AjdszVztKacVdcdTAwMTGIs89LXG5ubc3Dkkn34oZSRerRU1x1MDAxNSfy06tcdTAwMTNcbslcdTAwMGJcdTAwMWVcdFx0sJKdRYW7XGaJftRENLFNyu6OXHUwMDE4tGeloVx1MDAxYlwi+tWDXHUwMDAzOtxcdTAwMTJZ7lx1MDAwMKactF11Mlx1MDAxZsdE2DvnXCJXLIIzafhWWUfr4Pedl19cdTAwMWIo4kX3aliq0Fx1MDAxYtXLXY2bJ/9TfFx1MDAxZK1cdTAwMDQ4XHUwMDFmfSVb279RXHUwMDE2q++22dOwLWTxl8hxXHUwMDBmXCJlW1x1MDAxNoWoXGZ0S/I0PnyaiLU1m+HVn162Kn6e2Ed+ek+BjpLy456Rblx1MDAxY189QM15oP1yXHUwMDA0Ympo+JRwaFx1MDAxOFA4/8mA2KSqnq5h7Fx1MDAwMz5wU1x1MDAxNrBysOK+f1eFLvIuls3gQ+jTfVx1MDAxOI+9XHQlN0uOs6g9PaxBMIc6XHUwMDFiw/MgXGKGXHUwMDFmKTXnwH1oXHUwMDBmm79izYl/Nogpl4WEb+/Wh2Hajr5cdTAwMTjqXHUwMDFmIXbmMFxucd9Go93tLPRcdTAwMTd+XHUwMDAxv9a8ydDbXHUwMDFjnVx1MDAxZF2U0tpVuGB4Ss8nb8BsNMzq8euOhLIyQZzx5Fx1MDAxM4vJe9RKb8g0ynXcVFx1MDAxMeb1JdFcXFhTXHUwMDA0ZqlKV1x1MDAwMWtbXlx1MDAxNbBcdTAwMTLLZlx1MDAwZlxcXG5qdzyg2jBcZlx1MDAwNinosG+2jPOfpTue4LspQ1nx89lcdTAwMTi1iNNqVSeZorBcdTAwMDMxR89WXHIqnVxmKzU0XHUwMDE34Wr0qIaLicu53eVcdTAwMThZ/Cp8VpVccqHGXHJcdTAwMWTaJNF+Zbcw0fcj2TnqXGKcp6C2i9fzu4HgUzVcdTAwMDFxYzlCjlx1MDAxNk5RV1x0jlx1MDAxN/XQz1x1MDAxMkDNnrtcdTAwMGZM7l1tXHUwMDFhXHUwMDExni/NXHUwMDE2f6ZcXMZwe1LgWvjd5YZ848zoSGaTpFx1MDAxMZtqqD2AmJmYXCJ1Pt/6s8riUyTI8J2m2blcdTAwMDFpStBRTXrh8Y0l9ZkoxF9cdTAwMWaf1z57OtV9/tE0tPnejctccu8ypn5561NcdTAwMDd/XHUwMDBi0/jR7EbztIPzXHUwMDBlXHUwMDA07q/r39khXHUwMDA3sjNjK979WFJcdTAwMTRcdTAwMWRcdTAwMGU/fVHKXHUwMDFlP6hsXHUwMDEze6JLSLIkMUZcdTAwMDJvK1VVb9zh3c9duFx1MDAxND5cdTAwMTFcdTAwMGXOXVq2h+PRRuPf2ZH2a/1GSXT3NrRKJotAqf7V+VvgcFwiXHUwMDAyIEXC+fJZXHUwMDE2PELRLWBHsmKFhahZl1x1MDAwN/uI2lx1MDAwM97QKk/jX7+y43xGqC7u/W9cdTAwMDXzQMjr395cdTAwMDKNZIN1XHUwMDEzxWaYc0JcdTAwMTjcXHUwMDE2xppmh0hUartFfFXMqm/veFaw+S2q5am9JF/j1Vx1MDAwMG04Tr33RrROKq//elNXnaRcYqK9e3U7ev+hqsRcdTAwMDbGKqlcZlDh/MdJ+b5iRLaV6V/P9/2UbfTAiVx1MDAxNfx4QGvS3mc6g1x1MDAwMPMo4X+9N9j7/T44ib9xwVSX59i2Vlx1MDAxNOCfaOThiftOT1XjRtEvX1foN0Vj9a95fP3rXbqwIPi8776Nn/SgXHUwMDE0f1x1MDAxMsXur18+mtuMULXhLVx1MDAwYnc/XHUwMDFlyiHHXHUwMDAxgoGGXHUwMDAwXHUwMDFlviZWzfFcdTAwMWFcdTAwMTbpn5G899u0MmOVtvrCq3uv2EVbTnmftHqXgNpyaD75rlx1MDAwZmpcdOJPRuz43lx1MDAwMIbCXHUwMDEycmbddbSGtLLOT/7tzVx1MDAxMCprXe/9Z+xDXHS2UndcdTAwMDP/3jsweFknfYdG8sekKMInXHUwMDE2SdqXus6uM/1Y2STpWSRAP1x1MDAwZrG4Zv/2oFj7XHUwMDAzQcfm7n2VX8uMZp5924s29m9/lVxcK7n3IFxyQjquXHUwMDFk/9fr68VBY9nnf3pcdTAwMDDEXHUwMDA0XHUwMDExu7v2zXY/qcmy7X+ZZ1x1MDAxMfnr883y/v39aX9YbFmpt9BNcedTLkPxx1Btsr9+bF1cdTAwMGWEUvzr91ObdVKz8n+ejf/RxLuthZMyg30g1b/12K4g6N5cdTAwMWSY05/4wFx1MDAxNC/OfLb5O1uYbNNUJe/5fUVGnIx3rTd/XHUwMDFhOLtcdTAwMDfSq1x1MDAwN1x1MDAwNGVCmbKsUfiv16QsXHUwMDE54r3E8oyX+23bXvW4nskk/vXOTPbahVWo3PdcdTAwMGLb/Fx1MDAxMs/z32ugXHUwMDAwjJyCXHUwMDE55Pt+kKnh+XpTXHUwMDE0XGbHccxcdTAwMTjHXHUwMDE4+nzUXHUwMDEx+TH+9eJIXHUwMDEzjEw9/bdXq6WepFx1MDAwZTi1XHUwMDA3aE7+eDzmd/qvn6BWrI/ydIA6XHUwMDAzwMbzj1x1MDAwZlHa0FZt+ULZcX3XlLFccjL+1Sfv3kNcdTAwMDIy0V8ptfc58bq2o/o2wf96v+vNeubFP98pSFLxfsJ+r9uvx5RM6U9utUmShC5VxWlzXHUwMDBi8KhjJoBXzC8ljqPdNubu08hcdTAwMWVFWT6D7Dc9ueRfXHUwMDFm+Vx1MDAwMq8zU+/tXcNZQOhakVx1MDAwZlNvgHTe/XIxnFtcdTAwMTZcdTAwMDMnd62eWjRcdTAwMDWKONm9U3RekZsyz9a1wHFYblx1MDAxOHlcdTAwMWRcbumrvutte51/PiSc3TXlXHUwMDE0dl5XsaWl8UZINFx1MDAwNVxcXHUwMDA0fWRcdTAwMTC07zvxRWQyt15YYe1Xj1x1MDAxMM84fk2O41jmtc1rO6d6XHUwMDE3sjTsXGJcdTAwMDKzKYpy/Vx1MDAxMrCeRIr8wFx1MDAxZs/A7Y/xbH1cbnZcZlx1MDAwM1x1MDAwYqLiXHUwMDAz2zVcZp+TrGWDXHUwMDFmOfi9p4jSP9/vVyVcbijBlWi7+1x1MDAwZrdcdTAwMDeLt1x1MDAwNJiNXHUwMDFmM75k5HddQZgz9a2/4bygaSbFl2WZm7Jauzbw3sJTxoFcdTAwMDbu+LH4iK7Tgmnsu47KPa9dUlx1MDAwMTp5/qRm6/tcdTAwMWGqv8aNvvqVilrMP1ZcdTAwMDLwem4hx1x1MDAxZCcwaVx1MDAwZe8k2cQwcDvJXHSEhichXGJcdTAwMDNv3vZcdTAwMDVrRSdcdTAwMWLMUFx1MDAxOFxchlx1MDAwZfea4e6R/WJcdTAwMWSOIVx1MDAxMPyTY+sqYmhcdTAwMWVcdTAwMThmcVW1XHUwMDBlI4JcdTAwMWOSXHUwMDE3Rn061WBcdTAwMWQr6r4wXHUwMDEzhuHtt73RIEKsYPhsK/9DzFx1MDAwZidcdTAwMWJ3LTLaMFx1MDAxOF5cdTAwMDdcdTAwMTMpmftMu61pmp+Pb2BSMVxir1xi3lNcdTAwMWZcdTAwMDVcdTAwMWF+gbLs21Rg2Z5cbk3HqYJcdTAwMTX9V4O/a2nvQqgp2K7q+q/xXHUwMDBigqnfoMeTME1xQW5CtzP6a5qWT0dAbamDOMfJayQkSnLPKZWRXHUwMDE0Nem99oK7u44sXHUwMDEwXHUwMDE4J1x1MDAwZcuCh3r9XHUwMDFloJpq9b1cdTAwMDZcdTAwMDP4ke5G7nhpj+2YZrH16mPj7lx1MDAxM8CBeplMnq5KWSGe216dh42Z2yaHXHUwMDA1inxOSUKoZJ3yX/a3XHUwMDFm7U6gbTZcXEuMRMs+Ie/qXYpuXHUwMDEw1ZKCgVx1MDAwZv3EnURcdTAwMDFcInXbsVx1MDAwNlx1MDAxNldmiCohXHUwMDE22OvR0fRcdTAwMDOb4KWkyFx1MDAwZarrlWzaXFwyZFFkO1x1MDAxYdUo4YpDTvNMSWM2ZD5/ly8mTeXEM97v/ZCA+zTzu6dnQd7p2m1ZXHUwMDE58mZcdTAwMDDmXG7e1E5KtL2pMKyPPiiW/4bXYZwhj7tcdTAwMDM70FE+tM1TXHUwMDA1c7btiKiq36ZMXaspW3BP2HLXqUrdqsrfOE6V8WtduFKmbUn+ekfO3+/3/lxiUSZDMCaKXHUwMDBm6PFnV6VsevgjS9Pfun7GsliG8fo+TZ17Rlx1MDAxY+RcdTAwMGbDgzz9XHUwMDEzyD+brIDfXHUwMDE2j1x1MDAwMWeY0pPzif54haIuVTfS87dx8qnrut/ClM84v1j7NFdAm5xlb5bNklxi+e3/9bOfw/ut2MBJXHUwMDAynlXDcI7es9jdXHUwMDA3MkPUhIlMTv3treiB7UtOiGzmzi5cbmnkt6WlOC1z1D3vXHUwMDA1QqrqgXKzXHUwMDAyq7Is1mDmjtZcdTAwMDFyj9BDXHUwMDFlQHOBh1NcdTAwMTdjJ/paN7RcbrRcdTAwMDFCz/PEXHUwMDAxK2KGcJhF9nNcdTAwMWZOXHUwMDFiPGRj/5DSXHUwMDEzhK/bXHUwMDFmu1x1MDAwZoMkKKeqyaO/oVx1MDAwNWDRXHUwMDFiOKFT4kB2Lv7dJ3xcdTAwMTW0xDZVu1x1MDAwZoHWdlE9UlnFdbu2nlx1MDAwMMVcdLmWLddRXHUwMDE1Ni8rqkSZPPu8RkyHjt9cdTAwMWKh6WVYsd3gpLszaafJc/dX2qQhiD5fwVx1MDAxYvs438MwXHUwMDAwNkDX+Fx1MDAxM9FcIifmXHUwMDA3TqReMMmrQ1x1MDAxZVxyxsDw7Vx1MDAwZfaAi1hcdTAwMWJMf/1cZsDoXdWpPKOJNHmWWY9N+/1TWi80re+DXHUwMDE1g3RcdTAwMDcr+/h9f79OgvL97seyP2pmXHUwMDE1XG7ZZk+0XHUwMDBm4qizvu736WqtRKswRI0wtU9cdTAwMWT1XHUwMDFhyl7lXHUwMDFmqZ7rrVx0/jZcdTAwMTiJ+v7uc7497mrkUIdXXHUwMDFiXFzIjovTzDnUhiDjZjRcdTAwMDM/oI+iXHUwMDAzXGbVJetUjSeo1lx1MDAxMynJv9M4ybplXHUwMDEzWLJcdTAwMDKjxr7vdeJcdTAwMDNqsHYhu/tFYexHfbftuy3luWG/XHUwMDFmXHUwMDAzr8lIeF+2Kd1eeeDNIMJ4OYhcXEnh+Fx1MDAwM1t7krZIXHQhmVx1MDAxY913XHUwMDFhtkWdXHUwMDFmvlx1MDAwZXDlXHUwMDA1XUz+d/feM1vf99R6KHFdXHUwMDE0XHUwMDA1XHUwMDE5iJ7rL2FmyrZcdTAwMDS1wrM/Nba9909cXMbbUDlFPOB8QUtcdTAwMDdpdu3Jj9FrXHUwMDFlkkZVuGSgXHUwMDA0drHzZf3X45FcdTAwMWFfMVx1MDAxMdpcdTAwMDeJ2OiVXGb4K3WpxSovJ/RM1f5cdTAwMTGscqnBu5ZjXHUwMDFi2OFi6lx1MDAxNF+J5XktyCv3NozBUy/uXHUwMDE0PH1YXCKh62uSXHUwMDE2rNpcdTAwMTBnuqWE4bVyXHUwMDE1l1dcdTAwMGJVUaF7tO12q8+6rT6czvZusc/EJ/ZcdTAwMTBQlOF83N5oivZloJZcdTAwMTlN13FEgGmlJSpNwlx1MDAwYml9h1x1MDAwYlx1MDAxM3TrXHUwMDFlyVfsZGbPsNzMfDKPRpdcdTAwMTdcXPnRrVx1MDAwNHJ+XHUwMDFlmaXY2EOYT49qTI9cdTAwMDIhv1x1MDAxZD5cdTAwMGalmTB92mBcdTAwMTBcdTAwMWaJqTtR9CUv2KPUa2GqILdcdTAwMGVcZvrxhFx1MDAwNFTJW+k36kery+Gm9C1S4pNJZu6HazThXHUwMDEwXGY5rlx1MDAxMnjP5fBDym5cdTAwMTK9p2mel5Xc8MFcdTAwMTlhs6lcdTAwMGKFXHUwMDFi0UpmMiclu9HzJdNIkJ1bge5YROV8lin9LVx1MDAxMfJcdTAwMTdoqFx1MDAwMWBcdTAwMGY9993NZVx1MDAxOJvwj1x1MDAxY1x1MDAxYzfOreaW6Fx1MDAwMvBcdN3JbeGidbvS9jlcdTAwMWa54stcdTAwMDDbPIyp0cSmW3K5XHUwMDAysS/roX0goLtvicFcdTAwMWOg+qUo0zBcdTAwMTRcdTAwMDW2XHUwMDA011VcdTAwMDWhgK9cdTAwMDF5hTXjgFe5w46VpHqC6CVP7Xstd84v9qr8Rc7d081cXGl5jKHvukkxY3MsL4pcdTAwMGZt7o5cdTAwMTOHXGY4VZZ1e9BcdTAwMWWqJL5EzftcdTAwMTL9iN8227V5nbLmt3R1bl/h6yd0xoFcdTAwMTTY3jreXHUwMDA3KTZcdTAwMWJCgVhcdTAwMDFcdTAwMTOOXHUwMDEyXHUwMDE3XHUwMDEytbhcdTAwMDRrlTBSU/G8d1x1MDAwMEWFRmSuKFxcQjxya5Xh67a13ZvcJ5HW577H0MgpqrZsqd3nrsedXHUwMDAyfXK+r1x1MDAxMN19dlx1MDAxMFx1MDAxZD9wuFx1MDAwZYI4XHUwMDFm961bteal8+9jXHUwMDE02JT6zlxusFx1MDAxZq9e7zzERkFcXHtMzt3HTVx1MDAxMchj4zqyXHUwMDE21Hdec1CK7lx1MDAxOKZwwowtTTxcdTAwMDeNOFx1MDAxNdXX9lx1MDAwNXGIcaOk0f315VPrxFx1MDAwYlx1MDAxZqe7kStcdTAwMDWZXHUwMDFkNohcdTAwMDQxXHJYW5tWXHUwMDAw0Zyf+uhGdHlzrsaRgvFcdTAwMWNp3lxu5VxiXHUwMDEwi6ZcdTAwMDTQnuZcdTAwMDdcYt3mK3RFfjZ90s9cdTAwMGZ3OMlcdTAwMWTJXHUwMDFlgIrVq1SHXHUwMDA23Fx1MDAxYjEpLiHL8izrh1x1MDAxZffkLVx1MDAwZrXrxY417pOqkWTl8Eu27cKjeJNcdTAwMTfJpt+Lz1x1MDAxY3mIXHRf7tDg9CPY8Kiejd1UhMnH5uPHzT+Goq5HrLxcdPOuXSaPOlx1MDAxYlxud8h+M8wwxyVDOzE1XHUwMDEyNK7tm3tV5uJ2z4lrYoiZvtW2XHUwMDE34YxWXodH1GnSVEPTtMhcdTAwMGLpofdcblx1MDAwNCWHJFx1MDAxY7HNXHUwMDEwYtJcdTAwMTYq81x1MDAxMVx1MDAxOcAxffWGvoY1XHUwMDFl6lZbXHUwMDFkXHUwMDAwRIJiXHUwMDE4ZpqwXHUwMDA3+qmiSuS8Slx1MDAxNFx1MDAwZbriXHUwMDAwj2FV7kTufsIwq9ictvzgfjiMKVx1MDAxN3yeUyTxXGLPQ7W5t6u0XFzF0lx1MDAwMlx1MDAwZcWC/1E7wWLys9BcdTAwMWJ8taWTcFx1MDAxNVx1MDAxNo1cdTAwMTDtST2CZ9dpP1x1MDAwNkhcdTAwMTWXZ60mnzCK/DxcdTAwMTc4ntmZXHUwMDFjyb2ST6Az8rVNnlj3mJGNXHUwMDE1hNbR/br+PorE91HFqlx1MDAxNlH8XHUwMDFhPrNtXHUwMDFj7T4/Z7bJQSqc/sp9ke70kvHer5x3JyQglHqYdvd05GKQmyTQvfGSnk9ARXufwF3bXHUwMDEwajrPWtg+gY9cdTAwMTArpVx1MDAwNlx1MDAxNFx1MDAxZYV9XHJgQqpcdTAwMWXiXHUwMDBlP2eOjF1ccvZ4SLCZkZI7ViaBM+b2znJcdTAwMDeI11u9VGTFM81wlY+u/Vx1MDAxOVPIN6PT6IEojyhnqjxcdTAwMWLJSk/tvL3Z66Ul3jtC9tLRbWJ1XGZoXHUwMDE1TDd1XHUwMDE0mlx1MDAxNty20TRf02LxXHUwMDExSvKFiuFcXO4t1cuRXuPS+Fx1MDAxMlxirW5ETSmzXHUwMDEx6OpsXHJcdTAwMWVho/JJsvjtr3twMy8hg/Jmq+nhXHUwMDE5XHUwMDExu9VizUBcdTAwMDS2gqUjqDVe6TTwkth9LdNEkVx1MDAxOWBjT9bnU1xcR0n3x9m+R4svujiry1x1MDAxNFx1MDAxMrGZo/31nIRcdTAwMTNwXHUwMDExXHUwMDAyX1wiXHUwMDAxUcWxgFx1MDAwNIJ5pvXJOVx1MDAxNSSfev1cbs5N9EtXXHUwMDE2XHUwMDAwI0Ml2qFcdTAwMGWOM1x1MDAwNLGSjE826ynQ1Ki9uEFccq3t5TV9Uiz0SVxmjq/IlCpccpxqfFU7WvRcdTAwMDSgeIxcbiX+RVx1MDAxZP/FkTqHa8j34yRcdTAwMTJ7QVxcKrRv/nFcdTAwMDBcdH5QK4Oge+42ojfQg89cdTAwMGIswFx1MDAxZXF2OUfmcL43XHUwMDFlNHRcdTAwMWRcdTAwMGUpfJfAZzi2UVaCOK5++m3FjlxcjNVcdTAwMWZ14j5cdTAwMTL5XHUwMDAwv2kr56uKwVx1MDAxZfA0bCRcdE4jmVx1MDAwMFx1MDAwNXV6WZavumgmPN1f9VKP+1x1MDAxNE3aNC5yiVx1MDAxMO95XHUwMDBlQYJcdTAwMWLcJHFZYlx1MDAxNzPzOq9jhlTN4/WDdzLOSjBxxYBcdTAwMGUtXHUwMDBi5q3sXCKAhyNyi+eZc1x1MDAwN1x1MDAxZFx1MDAxON3+XHUwMDE4gVxy6opWS2SsvSCEgyPeTKLUXHUwMDFmRvBNZFwixyOLk5JcdTAwMTQuXHUwMDAz245NgFxyXHUwMDEyr1x1MDAxND+DPijq+5qMrlY2hlM9MTrost5cdTAwMGI4cfw5ja+xhePNnclcdTAwMWY5XoLTMKdcdTAwMTBr46dqpmVcdTAwMTBcdTAwMDHnO68oWEp+fWJf/HfQytlcdTAwMTSRnkGuiWaq8Fwi9k2m53LaXHUwMDFi1Fx1MDAwNLExyPHQ0baphVfm8Vx1MDAxZFx1MDAxYbeoXCKusN1rXHRdh5NEXHUwMDA3qF1s3pRDNHm+VVx1MDAxOFXmXHUwMDBmrKNn9fRgXHUwMDEwXHUwMDEzo0HTXyHPXHUwMDFhyWuM2Jrrr6csXHUwMDAzbdxUQv3l11b57D11IbxlnMtW51k+w1euXHUwMDBig4jlXHUwMDFml9Rx7qdr3K53zWDI9TCb9anWfcnSU0uHyvy2K5HeuLYwredvWZKwh/wmRT1W3TpUnC5VXHUwMDE5NaJ58cBTi2V9jvbecH57suE4XHUwMDFjp5pcdTAwMDKbZ77KOX+DM7fNkbbpXHUwMDE4WoCpJGFszijb5DVcdTAwMGX1XHUwMDEyzi4yROwwUZ1cdTAwMWSEmzYmXHUwMDFh55vCocZVNcvo4lx1MDAwZsdqPVx1MDAwN6262+JcdTAwMGZ44FxcNXdVvpvkQ/Q071x1MDAxNJJcdTAwMTDy6uTFcSQgfv3P+HGVM/B+VeC6OqiGmvityYjGXHUwMDA1XHUwMDA0gIxLi2H+9o7Zm83R6i2oqI13+KxcdTAwMDKI6WCHpWrL1IlcbpxcdTAwMDF6R4vqRDmaeX4nvOo1mahcdTAwMTI6u3Zhq1xyXFxxXHUwMDA0i1x1MDAwYpDTdVx1MDAxZIGmN2qS62JoLu4oXlx1MDAxY7EtsmfXgP5cdTAwMWWfesW/tmfgb4BcdTAwMTi7+fVcdMwx72c1iVx1MDAwYj7+Klx1MDAxN9hcblx1MDAwMSla1+1xkGrvr3y0XCLfJI60041yvpFCQVx1MDAxZsF7f8bTXHUwMDAxblxiU15cdTAwMWHs1pv68PVcdTAwMWSD+Lyv5ZaMXHUwMDEya2tcdTAwMDWSXHKdXHUwMDFhYWel6PClXHLDn85PWrlRXHUwMDFkpJBhZ7pf3vl2tc7LRFx1MDAxNCvq/O66z8A2UWRLLlx1MDAwNH/PpDmYz97mOClbXGLuxvq2pN8mUyU3SuslZdz7MVx1MDAxMrB5wrQ9KNm+rimh93xcZubrPbideCluyyTDU85Uc3VLOje3wSdo4knBWk9AXHUwMDBlf3jedOLDwMJn8WjtyqwyY6ZpWb6cPsFfrqi+LWrxXHUwMDBlXHUwMDAxnYTfXGZd7VZZU2S6SX7Occ2T1eg3XHUwMDAxL1CfLimBOMOHXHUwMDFl1SRChTFDZ01ItK+dR9T78Fx1MDAwZUhrJr6ZXHUwMDE3rnVDqfWLPK9Lq/dcdTAwMWRep705mJM6Z/dlg5hcdTAwMTlcYoRK1ZlOjyOddrhtbZTsolx1MDAwN3ZcdTAwMDF8ROOBbtftcI5P6oO+u9ppIEhwOJsrRfb8ZSzJufzU1Ynhs4zUTS9cdTAwMTHcYblcdTAwMWKf83SkkmvWUOOcoqy3vr1cdTAwMDbe1YUz/pn0NXTf5dNNjktcdTAwMDBCxM5cdTAwMTCJXHUwMDE3PNBcbijtcyy2RjRBy6aT4vNd061cYmpqN1SCXHUwMDE3XHTyj6B6Qs26ibZcdTAwMDdcXI+TXHUwMDBi99Ojhm3RXHUwMDFl9WmNXHUwMDFjrydBNmeMPXVcdTAwMTFb1Vx1MDAxMWA1Tywjcp/t8TNDrFx1MDAwN65cdTAwMTCWLSzbaqJiq79cdTAwMTFi00R8RUpcdTAwMDFHx6bFwKNcdTAwMGa33LZZnbb6U5U93Pzu9alHjFxuyin+VLjXXHUwMDE0XHIyXHUwMDBmyGww5kCOnoWjU8P6zrDvXHUwMDFiuHO1hzl6YSWglFtRTdbXjn5cXN9cdTAwMTlcdJqT5Fx1MDAwZTJtxXh3S4OtNiqRtfGQcqBcIvaaXHUwMDEzoKE7W1M/nLhxt1xyPk7xYH/T+NP7UNj3Ve1cdTAwMDN8P1x1MDAxNEwrjuqkXHUwMDBlY1eZJqaEyWj8Xb8sii/myVVEqNGSyHBcdTAwMTJVlialXHUwMDFkJ4FcdTAwMTclhMDnQuqSRexG9v1ccnNN+FxiaitqL1srXHUwMDAxULWG4lx1MDAxZOHGtkztoFn1pd1PSlx1MDAxMKzJ1S+JXHUwMDBitkchkb1cdTAwMTh+5ERRibuhsyf1SJFcYqRlQ3+8ssFcYvdcdTAwMTNcdTAwMTXXiXBnXHUwMDExb81cImTThm+Ubm9xXHUwMDEwXHUwMDBi7ZbXJTne9tjm08RkeE85XHUwMDEz99nBrfnm1LVcdTAwMTEySsDCjYO07lx1MDAxNGDYhIIoXHKTL3RcdTAwMDBf/Jpn6Oq8L2ZcdTAwMWNcdTAwMWZcdTAwMWSUnVPWi3+KXG4kxqpcdTAwMDfLk1x1MDAwNFx1MDAxYlx1MDAxMPhcdTAwMTBt4tVlneNVzEX+XHUwMDFj9pfMXHUwMDBmXHKtzKPYXHUwMDEwxSQqXHUwMDE3XHUwMDA2SJ+1vSy6XCLIWWRrx1HM953jklx1MDAxZFxctaqUaUJgsbmhcDSdtO3G71aXd+7k3PbHdUrUklxyT0+Wvz2rY+osjl0krEErtbyWKtdIITV1kiaosVx1MDAwZd+zXHUwMDEyXGZw3J9cXOJATVx1MDAwNLeClFx1MDAwMGadauygVoJ0coCz01x1MDAxMLeP51x1MDAxMdhcdTAwMDbHXHUwMDBlYtt1gNd6LYV9XHUwMDA19kfn7pb6ceK0J7ZMxXeqs3BcdTAwMTHVX6VaXHUwMDFmmSFaVVx1MDAxZPkxMbRcdTAwMTWEgMxhn6nidFx1MDAwNifs4jN+33pen1pktdsjPJtMV5thmsH0JbCKNr6jn8FWXHUwMDFlXHUwMDE5qz28mDE+d9mBj+jZkSPaN6ZcdTAwMDRwOVZcdTAwMTbjvWRcdTAwMTOfjyM/6Vx1MDAwNlx1MDAwNlx1MDAwZo6kKK49wFX+l/Juq+1MpXdeMrzeey2po0yzXHUwMDAzj0s8nJviqKiOoKfBXHUwMDFjYVx1MDAxN3W5gFNtXHUwMDFjr1xczVpwXHUwMDAyXHUwMDFkyoOWyoLTXHUwMDEzWKVJ2Vxm4jLfe1x1MDAwNDBcIl5AdD1y9Vx1MDAwMZCzVlZcdTAwMDObXHUwMDAyelx1MDAwN1JVXlx1MDAxYSNj8fZXzHYuXHUwMDEzvaxwXHUwMDFid8b9eKrq6Fx1MDAxZjE/jGinXHUwMDBiKMwnlOO1J+5cdTAwMTa1Mlx1MDAwNydcdDvi8FKc10+sq5NIaNaQmqh4i1x1MDAwZtPBM2p4oFx1MDAxZa9KbFZFqYtr5mih1Vx1MDAxOVx1MDAxMG9dVS5Ee+P59JWGi1xua5ZJn19rxFx1MDAwMU9cdTAwMWNQzlZcdTAwMGZL0HCrOUqOPZtRMXRcdTAwMTRNL1x1MDAxZVxyMlIzVaWDXHUwMDEzs9XSXpngQiSXujiUbShcdTAwMTSVMfTGaLdwTqKFhVxmZap/tH157+bB7Fx1MDAwMPvk7TufqVx1MDAwYlvD9EmCTWzx+dssmVx1MDAxYaDcrKN70suPl7/wbDByxr5R/VtcdIjTJEy7JSm0cl2OX19yuyfTniT3M2ai1atYVFx1MDAxY2wm8vFcdTAwMDbwtbVcdTAwMDFcdTAwMDIqv/zQXHUwMDA1P9+PjXVnvFx1MDAxObpcdTAwMWRcdTAwMDJcXJKM2izFVafDaflcdTAwMDah7NjwKSDMJ8u2nq6NiehcdTAwMGW4K37II9rkNngm5Z7X0lx1MDAwZaW9WL9mKbaJh3hGa+lcdTAwMDIwgV5cZlx1MDAxM5GdXHUwMDFk2mNr2YfqM/0ziqTTrZunz1fHdXaHZrXui87mgc98ZWkvXkzlSnrzposmp1x1MDAwNG8vNylcdTAwMDdHh0WgaNnBeJvzeHaVy1x1MDAwNted9I20oVx0qkH1+Fx1MDAxY6u6XHUwMDA1XHUwMDFiX1wi1LdcdTAwMWNO83rBu2H0zpZRK2Zxn6uXVlx1MDAxOcKKU5je5m5qqyBcdTAwMDatXCKD8YnofN87/jlcdTAwMDFTaF6Iv6uZnCyCX2tEtlx1MDAxOEnzSWIyXHUwMDE12W45e9JcdTAwMTGqyPKn1lx1MDAxYTLgXHUwMDBlOlx1MDAxY+LIpHn3UzDU5Fx1MDAxMFjGfKhq8TX10LdcdTAwMDJcdTAwMDCcfDaPja+0XHUwMDEz+lx1MDAxNdJcdTAwMTGbYode/z3TQKpIuD3I62PE25OD6umBUs43Kyukc8Yuh1rVOpiG0VxyN1Gn8yxsOa/c/Lh2alc/XHUwMDAwf1JcdTAwMTbZXGZXP1x1MDAwMvfS/GpXmvkxXHUwMDE5IGzRTteFXHUwMDE0XHUwMDE3q2X9blx1MDAxNSp57p1sSPDPkDpJXHUwMDBmJPOkqlRcdTAwMDdcdTAwMWNcdTAwMWPedFx1MDAwNe4y23o0gGf/oD4xxX2sWP+7Y/7dqepVPIK3LMfxS+8wo7QjzMPBhv/un0NcdTAwMDdcdTAwMWbVxd/Fu25cdTAwMTTwZ1pxXHUwMDE4f75JLqiFXHUwMDExa11cdTAwMWX3nlI2OJT4KtuX6aBJUXJAQ+tcdTAwMTZzKqK43FtcdTAwMWJfkVxu466mmW26QMCeXHUwMDE5ff2VXHUwMDE50OH5LkfPkVx1MDAxMinh14XFf7H3XHUwMDFlza5cdTAwMDLdluBcdTAwMGZiIIRnmHjvjWAmXHUwMDEwXnhJgH59Jzr3e1XvVXTPO6JuhFwidM/Jg8ht1l4rlex8fjd7fGhDtSBcXOVGnU1cdTAwMDBaIY5CX2ZYXHUwMDA11NE6PIVcdTAwMDDZO1x1MDAxNbfI5lI/SabJXCKCYOS7/JqEXHUwMDA2tTu6XHUwMDA1qUrlJmN4bStcdTAwMGaSnSdcdTAwMDdqySwryj48W13NwFxuwFPUPqWvbdOa1FXz3T+fXHUwMDFhXHUwMDExUuPA94vop67uUk0wPqJVPJZeIVx1MDAwNfb7wYlcdTAwMTeDXHUwMDE5pkYgXkpkmO3K2ZZr4mUrIXsta4H+wlx1MDAxNFBcdTAwMDNcdTAwMDfJ6i87gM3gh1x1MDAwN67al+yYOdUu+FBlxVx1MDAxNtWPTyM1oqCKXHUwMDA2gV+8oeK7+67DXHUwMDAyZLiCJz9cdTAwMDMuXHUwMDE4Llx1MDAxZrU48FR2o5DzLYZ3zfpyucvOczI9n0uiS7JWfY5cdTAwMWE887X5XHSVoVHOXHUwMDFh4repdkm6oo49upZLP9qyXHUwMDE2QcylsePA5bir6G+iyO2LVrmN/Oj0rnJ4dcD3rlx1MDAxN2i15rTHmKiebFVcdTAwMWY1LqUvu5PqU7d2REpcdTAwMDTNXHUwMDBiN0jTKVbHro1ebWWEeEo95cJdtFx1MDAxM33C2Vx1MDAwNtxcdTAwMDav02pcdTAwMTXt9SbhlKpcIp5cdTAwMDZcdTAwMWW92/XSXHUwMDAzqbCvU+hWiFx1MDAwMalg7sz1VTfRUeFrOVZjXHUwMDFiP9et35RnbKAroGCw3dZcdTAwMGWoXHUwMDFlRyPtk3ornNvneKDNXHUwMDAybEl/djWqP9VdSFv1xraaylNuK0e2KW3ZXHUwMDA0qazLe97tZnNsuUj9XHUwMDFh0bp4lbaLcagu4Vx1MDAwZSFhf7+lWd2Ytlx1MDAxM7FCd7ztXHTZXHUwMDFk4drsZD0mLo+5521oYH7eM6hcdTAwMDPjvm80XHUwMDBiimuGQp/Unpu+Y+VccubVXGZeR08jsfSFz91CovwhVD/gJSaRXHUwMDE3vDKRjWpcblbQy1x1MDAxZYhuZU5K9YJkfiU0ydZiZXBlII43XHUwMDAwaIwqaVxymYVcdTAwMTRBv1mhm9JcXE7k01x1MDAwMVx1MDAxMFHlgj16XHUwMDE51ieLT1x1MDAxMZ3o/SbroPRb+o9uaiykYXqaj1ZrWtOyVD5nf6ec3lx1MDAwM6hNfOGrtDpcdTAwMDfDaVx1MDAwMa+QXHKtJb2KKJRQ7TVcdTAwMDZiJS7tl09cdTAwMWRg5Um5jfRY0zVcdTAwMWZcdTAwMGJ5aTdjx4mIr1xyvs7tzPOpMpHU+GtybVx1MDAxMUZcdTAwMWNdXHUwMDA2JVx1MDAxM/6DdXpTZoHefsvhPYmg1ndPvvhTZUdDX/Ign0jxplxmqyFccvnU1lKsWfe7xXtmr0moMZB8XHUwMDFlWzIkXG5cdTAwMTnG01x1MDAxZPvSJFx1MDAxM96ZSq8v1NBAg1tMN5Y9yUSGXCJcdTAwMTJKXHUwMDEzhVx1MDAxYkQ375aMNbl8hGZcbqbP5IdcdTAwMDXHySpcdFx1MDAwMr7YPOKK2717X4ZaKSrnYkOuer1cdTAwMTiPVN5eLsFxLvTLRHhDe4NG5Ebuqmpvdt06klx1MDAxNfv3wdZdllx1MDAxY37v3JrYfZparV9Wb0DSWuLIXHUwMDA1K5vkVulJviBzmHmakXNeXG42+eOLkW04hi+altL0XHUwMDAxXHUwMDBmRaxH95+E4bjKWPyLLLO5TfdEJbHPm1x1MDAwYlCFeOWMsin12Vx1MDAxYlxyZsDz9tBcdTAwMTVxUElo1joksbvqau3XXHUwMDAzYYBL9itFI6tp8/657lWc7aGQd4KYUPZC6V5rX1x1MDAxYrKA2Ssvd1x0aNyb5i7uk4d0LFx1MDAwZlx1MDAxMFx1MDAwMtJtXHRcdTAwMTbvK9YwXCJfK3rqX6BcIqrElUZp/YplzMHgL0VcdTAwMDK9uFx1MDAxMJ5oR1x1MDAxOX+o7VPjXHUwMDBmq92+tXtcdTAwMDRwgqFCX/GmQimCS1x1MDAwNobk2cov1CfdIa+693jHwafe96VwxVx1MDAwNGFLXHUwMDBiPaz03/dShewl7TZcdTAwMWFZs+WPoudcdKdVt8BTZVx1MDAwMlx1MDAwYsNcdTAwMTbdqzmnooZZV/Xr7lVAIEJLWjizQ2uE9lx1MDAwMp41XHUwMDEwrlVuXHUwMDE4iVxuXHTepm4xhKf9KamaXHUwMDFh8JpHJov4Ze6E7JOjf3tcdTAwMDc30V3X53Ar1lxistvHwnPy+nk+iS7czmdIXCJVWUNJ50pcdTAwMDPHJF6pOV3TssfIy9ydfOmq0dxMzlxmY4u4Oa/tuShcZlNFUKaGXHUwMDE3dD9cdTAwMWJsOOxLhURTXHUwMDE08cOYW1x1MDAxMeLfsk5cdTAwMTmx1Gazt7R8t5/f6KVFiW+gT3E6XHUwMDE2jqXNj1xmLJYpn0XAvN1GS6NN21x1MDAxOD9ztfBwLEXjXGaN+1Y3WdJgifiC5opcdTAwMTS+K1x0UPfliaBcdTAwMTJPxlx1MDAwZuZUmNOA0uxnZdZcdTAwMDLqsvnZ+iElX0q9zIijXHUwMDAxXHUwMDA0xnAmMZLjQFx1MDAxYs+b9jHr+sG4Szd8gEJv/NskPU6EwbDe61x1MDAwNFf4mFAy4zqYt1x1MDAwMUC3r0AtOKHuY25cdTAwMDBhaMCUXHUwMDBieT7gfVx1MDAxNuFBdfZJ0ITebdhcdIOcq3MoT3utTPxErYfShUBMMKhcYqnNwZtcdTAwMTWxXGaCuXPcrYEyjCX3d9vpXHUwMDE2U437Zs16XHUwMDA366tmO8/20I2rJ/ObXHUwMDEyM5QjdW6wQV3Fp5v4XHUwMDA2r3xWdI/3XHUwMDAx/TRccs2+v81cdTAwMDRcdTAwMTZcIm//XGLU1OPrIbvnXu9ze7BcIlx1MDAxM9n1XHUwMDEzq/JeK1x1MDAxZL9cdTAwMDWWOcNLtoZaSpg4kFwi6iqVzvHV4oBcdTAwMWNpXHUwMDAzSu9cdTAwMTV3tFx1MDAxMFlcdTAwMTWwx3fJKWVsIC+GxZPRPPB1Pu4rLN/IdnvXj33NmKthXopGRJFcdTAwMWNv2KJMXHUwMDFlQkjS3PK8lq/Ouc7fzUtcdTAwMWVcdTAwMDVGsjDShFx1MDAxY0nu8UxfWqJsXplve1x1MDAwMkpebFpVZKZmnOJcdTAwMDGBXGJfLeLxeOKuuz74h9tcdTAwMTW704vX1H62pMtIosjc1Op8XHUwMDE2XHUwMDA0XHUwMDFi03F9IWpcdTAwMDPZllx1MDAxNUj7IOeRuk5fv62afYyq3Vx1MDAwZnqS/1x1MDAxNFx1MDAxZN8uzDHzdzJYVeH5yWzhnlxunVlaliqvN+N7W3b4J4Xtu3xX4lx1MDAwMbCGbkjqpk3zdcjAYDopMEFHTkslbu16+7hAg35cdTAwMTZnyL4n/Iq4XHUwMDBiuDS8eGFJOvCAUfvM9VxiaFx1MDAxNVOt3PXZpcFzLNVNcfaNrlx1MDAxMoWjfVx1MDAwMsl5jJTIi8FR1Vx1MDAwZcemiaK+8GB6ITfH1TxYjy1IXG5Xv6nsi4kpeFruQvxcdTAwMGVcdTAwMTiOgXSOfuvcwl3OTnJcdTAwMTDeokNcdTAwMTR0/jJcdTAwMWTtij2KY9rfRrbbup56iVx1MDAwNNU3UkpcdTAwMGX9fUWsrjjdneBcdTAwMWWorcnIXt1WbvkyMTJcdTAwMTVvXHUwMDAxVTdWzCtGRHQ+J/DbN91dd1x1MDAxZlx1MDAwN46bNVx1MDAxM3VcdTAwMDXeXHUwMDA2nVx1MDAwNkF+1vlZS+JGXHUwMDFj8dlRKt+7gdxcdTAwMGbxp1x1MDAxY9q7u8hNiVxub6bSOp8374rlxY7MMFx1MDAxZjVcdTAwMDOJIDdcdTAwMTlTgKgyv3Q+OVx1MDAxY7NoiICYXHUwMDBmUtmM4Tm8tW/ueMnrglx1MDAxY5NzuI1cdTAwMTh9N5LWuIpcdTAwMGK+j8S02UKW3KDpbMApPVx1MDAxZapQv7BcdTAwMTHtrLcqb99Zol5cdTAwMTVqRbqGOFSrOsi7Wm97e1FcdTAwMTVcdTAwMTdcdTAwMTlcdTAwMDAmtM6jQn26vpuSXHUwMDFlfam1sT+pT3rIRI0lXHUwMDA30pZEMFx1MDAxY0coavM5ySY6sXqxnpaMbDNxpnBRxFxctFx0y347blx1MDAwMtj9XHUwMDAyOafdiqH+yEVQqcZEJL6Q13h+XHUwMDA0KbuWejPyZ5NcdTAwMTk8tceZiCBDXGL0Jlx1MDAxNLp7bXbDXHUwMDE4uzOeXHUwMDFhb1x1MDAwZlx1MDAxNkNxNs1cdTAwMDJcdTAwMTKBUtTVcOpjechvttiASb67KvgqjcJcdTAwMTORIeWyI3lfUSMnIF2JynXHXHUwMDExaji634OIuFxcXHUwMDA0nyhcdTAwMTTBnTarKjhcdTAwMDRpbmJL8rGpS6zkPDs1z8eAXHUwMDE44jSsPOVsRpXIYHI+UJLb0lvk3MOIeFwi3EIxadehd1x1MDAxOWHZXHUwMDE3NT1cXDT2XHUwMDEzjU7uXHUwMDFhR3LZZotmVn3EiuW3XHUwMDBi4Lc8XFxEPlttt9o5NWLFLqlMVVx1MDAwM1x1MDAxOaw6Plx1MDAxNCftV3PkXHUwMDA3snVcdTAwMWaZVDlOjFx1MDAxN0GpLlchrSuYsbAgTMqjo5rlrycqjf16sT+BP62+0lx1MDAwNiCUxGZdmcDu9sbYqpK7272g6JaqXG6VKLrhbfdcdTAwMDIjeZrinkhbULtcbjFcclx1MDAwMVx1MDAxZst5XHLa2oRR1+GgXVxmXiRD8Fx1MDAwZVxywX/eL70zufV1woi83dvIMrj7erm7O61KvsrQZ+9HisnEcXTKyFx1MDAxMTetfFx1MDAwN9z2XHUwMDE5qlx1MDAwMOOJ/lODt1x1MDAxN8PEbFB7v/DSelx1MDAxNHf1tX2k9rbRXHUwMDFmLlx1MDAxYyePuytcdTAwMTQl1V710ZKpWVx1MDAwN6OIXHUwMDE1V4NcdTAwMDX6s02+uiBcdTAwMGaEbVx1MDAxZnVhXHUwMDBlR8FXXHUwMDEyT6bJ2kh24qp9YluGeX9kM7YjXG5cdTAwMWRcdTAwMGIkzzek6npMfayz5r2/792i+/DBW1x1MDAwM8pcdTAwMWSm33Xj4cySXFxz/O5t3eDXrlxuQTynRdl/O5Ivipolelx1MDAxOcatvpBcdTAwMTJcInJYnSmiesGXQ5/BeyxtwVx1MDAxYTmMSXNlvplcbq3eK0bHq/fF6sKmuFxuuIJOmVmbmNNdXG7FI19D2/N22ca5SDc2XHUwMDFmXHUwMDExXHUwMDAxXG7M8IXN7VxceFx1MDAwZjrgTaRe6HvoP1hcIsWPXHUwMDFhbLtcXFx1MDAxYszA9zBcdTAwMTiLXCIjv8iqKTP1yCE5pVx1MDAxNv5cdJ76u0tUi1x1MDAxM7nC51x1MDAwZm4j+Co6n7TKxzRAXHUwMDBlYqFcdTAwMDSHc+3mYsf3XHUwMDE0MdxO5CWeQXL+SpqyXHUwMDE58qG3L1QtS0conntpmLF7eepcclx1MDAxN21cdKqko1x1MDAxN1cqVectXCK6p9pTXHUwMDFl8WvYaFFXNK91cO7/tbdbl7vIotJaMC32Z71cdTAwMWTvxMy54yB2XHUwMDE1XHLMkZdcdTAwMDTk9fKR2OE08WyHXFyW93tqnf38eINcXN7cKrT7XCKrqYp2TF/wXHUwMDE041x1MDAxMbZYnnur33/9u1xu6yGrx/rb9+73wLOS2Pw9p1x1MDAwM6r+XHUwMDE2+c25YVaT2ISvblx1MDAwNECDXHUwMDFk0vVRrOJW9CWj5n2I+1x1MDAxZVx1MDAxNsuYlk24XHUwMDFhdKuoLlx1MDAxYYRcdTAwMThJXHUwMDAxZ/85vlm79Z7wv/3LkH+/ZlX/9XR93oyhlVx1MDAwMd3ZXHUwMDA1V1x1MDAxMu6XXHUwMDAwUW6prcYgZbZcdTAwMThvX1a0XHUwMDA3pl0j5aFRIZrV/86Qud/Gv77G41x1MDAxM024+9/zWVx0XHUwMDFmrVdcdTAwMTmc54s4geGp4Vx1MDAwN5ZJU+nc+0f9zIn3RabPgbNSXHUwMDE1b3vYiGa88fUkXHUwMDFjf+csXHUwMDFkPvXWtHO/9Cy+wpz47fF+fabQuFx1MDAxYrOsMESlZccnctdxddH0O3VxR2ArS6JRuDbnnvru3cx0pv16tdLGXHUwMDAzYO5v7zXmU5hWN+d1pVdcdTAwMTj/9YDGiGn5XGbPX3/VXHUwMDA155d1+9/6XHUwMDEy5uKvV1wiwmkjUJ7yOeZL8Mv7ov326H+sO1xyuVx1MDAwMiTLij1TXGJ5ft8ukG7211x1MDAwMzpr+p4tfk30wFx1MDAxN9fSR06sxFPVurdu+kg+j59cdHlcdTAwMWT3Vlx1MDAxZNok0V65S4ppcSdSmJTi/H4jXHUwMDA09TjU94SLvX3psGV/N7WwXHUwMDA2ld/W++NcdTAwMTaiZ5/SSYzV59VN/vr/JlU/UntcdTAwMTKfe8tbtmwhKLGvXa28x+p63jcgyIyrN+Trulx1MDAxYnKdSJI6fj20XHUwMDFleN73SW4h1+uVXnRcYuKFU1x1MDAwNsFcdTAwMTeRbkuJot0nY1n2QuNcdTAwMTR+7tPFPkK9XHUwMDExryUng+T+i8+ds1Js/YvP8X2LNv737IVcdJp7tUi/ZzrkRCRcdTAwMTbDMFx1MDAwNlvhXHUwMDEx6YrJ9yyL9lx1MDAxNyNcdTAwMDdccu/6XHUwMDE35/JZV1x1MDAwNIOYTeifMshzXHUwMDA2uZpcZkG4m9tAYaLwol9UUoJ7SX8+p+JcdTAwMTnjKl+TX789VDXqOT7nr7uDqLqL4HXHLpEk6d2yfF1fWX43Zb/D2yZ3uyO1zCdZXHUwMDE18IJcdTAwMTQx3aJnQtGoQ7Nv7JUjpT16lFxuXFxTjs+Yj49992B0kVxmXHUwMDAypUHgTS+OKOnic3tlXHUwMDFmnGZcdTAwMTdcdTAwMTnPXm+C+vBcdTAwMGauaq2NXHUwMDEwuVx1MDAwNc/ma5BW21a2ODW/P9lHXCJw6Uqf+7Sw/EhN5H2nLb6uLrdcdTAwMTfGlme/XHUwMDExNiFcdTAwMGUxSdDMYihcdTAwMDR5j8t5cFZTuoK6qVx1MDAxZVXcXHUwMDAyQnv/ejqYm1x1MDAxMtKp9OvLmnfvXHUwMDFlhPnvXHUwMDFjpytcYlx1MDAxYVKznNe4vt9rK7fcs622xDL7WZx269yzj1xiXHUwMDFjSl2VblxyXHUwMDFkSlhfV1xmY/u2/ZLz80pSc3oliLGzoF2jKFxyPWO4nX2Rk7NcdTAwMWbO4OuOKM1X9pFm2PX1+Vx1MDAxNDfkwz6Kgn2+XizelytimPXdeKdIyzrlfL2v71x1MDAxZMz+ar/fb3Ysy8te81x1MDAwN46WyPHgSC/YXHUwMDFmXHUwMDFmXHUwMDE5J4NcdTAwMTdcdTAwMTf6z1x1MDAwN+Xr1fPsUV1B9Fx1MDAwMTAr1Tjk92TVuVx1MDAwYrxcdTAwMTVPMqRmS9SI80TPXHJFT9I9w2A1Tlxm5bvweFx1MDAxNlx1MDAxZMIwXHUwMDFkcnmni6ORzT3DM1x1MDAxNC+KJG/piabZI8XxdzDEZWH2XHUwMDBm/d7Ps8aHzzl+Qc9cdTAwMTGv7DGj4V1V3T5I1z7SMzvoSL2myL0ysCm63Zrh7Iv5+FBdhnzyUFRcdTAwMTNM2Fx1MDAxZbe9gvFcdTAwMThcbr9cdTAwMWXca59Zclx1MDAxN0Y4hlx1MDAxM3RvXHUwMDFhSqJE87U0zt67OlWU5bLcXHUwMDFmgtaMXHUwMDA02MZnXGJRXHUwMDA21C5Q6lx1MDAxOFx1MDAxNVZcdTAwMTg5t7Mnk/Xoifxu5ee+6Ptbfk1Yx4+dLHCjipzfyXJITOYym7TySl+idnsjXHUwMDBm0HJcdTAwMWJcdTAwMTnOqdn7+jdlkPjsl378epe6vDJqLU6TLLpM1ztcdTAwMTJalZxcdTAwMGKfK0Lmc7zGhsSXqeRcZkLtnc9cZqqjKVx1MDAwYqmWrth98rCRT17F+1x1MDAxNmhcdTAwMDRcdTAwMDN/+uqx7Kq/bsFOXCKIrHAr5LXUQTxjQbCXJ0brk3zktsRrYbtcdTAwMTnaQSHns1xyZJ685ljx0ah0N8lDvaxijrdcdTAwMGbzUmtAXHUwMDE3aGs1XGb41lxc71PUIcjlciVfrcFL7SqYfWBFXHUwMDA1T2B9yeMjvlxcqVlZXHUwMDFj0WXDkFx1MDAxN3nep1VX+kBRWX3AqkKyc637yTyUi4t9Xa668dVcdTAwMDNcdTAwMDJUtCVcdTAwMTBcXCpL/iarrzXy6rRcdTAwMTDPLs/zmWSxgPRL+Kq3XHUwMDEw5M6wJ4VH3S2MLTRjU2/jXHUwMDE35vR7XHUwMDEx9eCF0Vx1MDAwZlpxiVx1MDAxONlBxyhbxZrTw+VcdTAwMDXTf7y9jWH3rZkw6p46XHUwMDAzRflcdTAwMWKZv2/3iVx1MDAxZlxmLqhJRIkpXHUwMDFi8EJh5C3FiWrVgVKB8Y9M9FcjoklcdTAwMWFcdTAwMWWraVmbzzevclZVWCGmXHUwMDA3jJF+joWagVxix71Ycmxdg79zXHUwMDE55Hk65Fx1MDAxYbGYbfKe5Kbs3uPuh4ffXHSnm33DimmiK705+2Xrzaa+MUVcdTAwMWRRjj77Mqb5kZjxhDtcdTAwMDf6WpJcdTAwMDbYNoaipJfJxkr3leBcdTAwMGXV01x1MDAwMeUkI5bNXHUwMDE0panXlV6DUebr4HKoNk9W4NGydutxrsrfqignWfhcdTAwMTch9+aELdaFgT5xbf5I1cfhqrf9UCitp7s7Uld8g35ffO2yNzvjgVx1MDAwNYbscvlgXHUwMDFjLmn9lSS/33bQXHUwMDBiOv2SKPHclWvk8e8vSJuNQCtftFx1MDAxMZYlXHRdQK928MyK+LZ9IMRnPMbMn9dcdTAwMDOCSdfP52EqyzNcdTAwMTPfl/U93JarYu95XHUwMDAyoSEzsUzjanE7csLtnTZBr1fJXHUwMDFkVbA/hYrocZh2rlLdRKutP4iu66z3dmBcdTAwMDK2O0pPnUauhi1d52tmXYYzr1xypqI38/iCXHTFsqnOSN22dS54XHUwMDFk2sRccpDOTFxypzBrwivE2av1aWroiN4tf2xUXHUwMDA19Jymxz62XHUwMDEwXHUwMDFikV8q+HHEXHUwMDFj326T8caz9YZcdTAwMWO2ONCrwdsuacn4/SU1eEyna1x1MDAxOEuxyHHjWWM/xPawr3fJJVGmiFTk4nDC07X5XHUwMDBi6L/WNqtnpzBFdHLuZVAswnxyJDP5wo/NZY1cdTAwMDeIXHUwMDA1RvSeLy9cXGDzZa2ggMh9mHtcdTAwMWHBXHUwMDE2x/gxvptcdTAwMTPPId1DXHUwMDEyevN3XG5N+axurcuxvOvAfjqrXHUwMDBigFxuOM1cdTAwMTOl8CY8+OChbC9UgWM/nzxcdJri0a7uXCI6vNIy32KA3sjHd3VNXHUwMDE1qPHV0NHarqlsIOqNrZmOiXCbqObv6zNxZNGrk+RcdTAwMTGajCSbXHUwMDE0VOpYXHUwMDAx3jqCIGTAsUJNgzp0XGJjeztD35O3hOFcXFdcdTAwMDQh/XhcdTAwMWauXHUwMDFjWD1cdTAwMGaolGGzSlxyuej0iTV3epBVtu2+R2pcZoaNuq1UooDCkZ30YoaiU7HJXHUwMDE4eo8qrNG7p4mXWJFtydFQKjedSGmA3qDKca+cXHUwMDFmP4osn2Dp8zyISXhcdTAwMWVkubyzwFxcXGJcdTAwMTFcdTAwMGbFT77Lqkd/2exw3cZcdTAwMGJBXHUwMDE1bEc99HVZOpLUXHUwMDE1XGL7YlkkxtHbeEg3wWbRXCJ7bGLNVVx1MDAxM1x1MDAxMV/UXHSqQLwwr1x1MDAxMLda2bXa5rbyXHUwMDEyXHUwMDA3XHUwMDEwLndcdTAwMTluqipXd3buiuM6xNFumlx1MDAwN5jP/lx1MDAxNM75Ozh73Ok1XHUwMDAxoDb/fqfo/oqbXHUwMDExkjNcdTAwMGJsVmgvW8hcYlx1MDAxY0g/9Vx1MDAwMrG5J/lL087CXHJtXGJncVx1MDAwNNSt3EpdfMR8nL3s/OVcdTAwMDJwpsM1tt0rhueg9lx1MDAwMYnsf6jWTbO7zO5HKVx1MDAxMPWrlu7pJ/e2ouRcdTAwMWRcdIj7o5hCtsSUSaLElp5cdTAwMTVel1x1MDAxOStIKcbCjFDiPkl6fWbJa1x1MDAxM3chiZVcdTAwMGaKho+ot1qKLV5RNl09uYrF0P9yeeI+XHUwMDAxTMXLpvLRwVx1MDAxNYpcdTAwMTVcdTAwMWKi/UmXJ0pLt89rxjudlzbFvYMn0yhcdTAwMGWQXGIoPlx1MDAxOa5KkTrg34y8VbQ7UPyLyCSR0Y1cdTAwMTCocTb0r/v98TzPWVx1MDAxY8VLXHUwMDE3XGbf88xcdTAwMDV9xnRcdTAwMTS/R8AhQSja5/k6+tW5M7fGc7n8PIeAalx1MDAwZexcIlx1MDAxMOrrYt55POeAy6Bb3bqdIzmX78Ps5rZcdTAwMTJ4XVx1MDAwYlx1MDAxYXvlPVx1MDAwMO6S49pgLnihalx1MDAxMo6o1HaDXHUwMDA1cFxyYFx1MDAxY+BcIqxcdTAwMWNdetX867prb+BcdTAwMWGV++FuiVx1MDAxNtqmseomXHUwMDAxcPD8unZh2l+xZKxcdTAwMWPWL4DqtWhcdTAwMTDi64vGXHUwMDE3uTnLXHUwMDE5VFx1MDAxNv5X5Tf9uFx1MDAwMYLbtIYvlSevWfynynQsnUlY/8JPwo83oz+KZy4shS5cdTAwMTWk4DZPQ9P5zaXGrFx1MDAwYsbNor9VLCVcIlo7ft1cdTAwMDGjT+17XHUwMDAztcGJSbIw6c9cdTAwMDGHuZXoLlxuuVx1MDAwN30jXm5cdTAwMTd6JMHkr6fwWkA+q1x1MDAwZcdcZk4s0Ihz3Jtu0v3JlCYmdcKhuGVcdTAwMTNcdTAwMWEmRT62teqn9Jep51x1MDAwYltveuOo/ff9kGC86LyD8uCZN9KmkqJfZ9yGgNeWcJU/ybO6dLdcdTAwMWGVm5RNap1nRitBQVwifdWFPo9cdTAwMWThhyvLMPltzkCicrdNS0ZZ8kq8gUzQIIbXjnI73+zAr5P7Q7WCpzfGj1f8uUmN51x1MDAwNXBcdTAwMTJxXHUwMDFlclx1MDAwNLEpXzpcdTAwMWRRX49cZoB/eFHjVInb3sstZsSce3tcdTAwMDIxYiXmQVx1MDAwMLcrmdudXHUwMDA1/nvZn8BSP2ZJqm1949RQRnluzGNaQ1HmsjyIz23DSi5cdFTiwd5tMPDdh0eIXGaX3lx1MDAxMOFcdTAwMWaRd9k62Y7rXHUwMDE10lRO99RcdTAwMTh4Y4guoSRcdTAwMTJfkvkuvLtD/nVFxFxcOr78alxiXCK5bVx1MDAxN+ZcdTAwMWHq8lxyr0cxXGa9kPN14NLOwKhcdTAwMWRjJKGGmkVYjlx1MDAwN7q9xMpcdTAwMTWbXHUwMDE2r967Y5lcdTAwMTeGc1x1MDAxOKtRbMaWK7relY64c10qu3FmXFygXHUwMDA2+mxa/1x1MDAxNVx1MDAxMtOExOeTKVx1MDAwNVx1MDAwMSwp/WohOfdcdTAwMWVcbqq3N1xyXFzck9mtKtxe2eWsPW1vXGZGXHUwMDBm6oA8mINHzKw8z1x1MDAxZbGoXHUwMDBicD9V/Fx1MDAxMUR3+1xifjEmXHUwMDFm4isvzu44XHUwMDFlo/WibVxupVxcXHUwMDFlL+ZcdTAwMTWjXHUwMDBlaEzAXHUwMDE4lq6i/NY0MSG0fFdcdTAwMDGpNlx1MDAxNKtZQ+YxUVtFTHpokZhrf8L5bvZVMdlTjFx1MDAxMlx1MDAxMqieSSl9LoJcIrc9RuzaI3A5XHUwMDFmUaLRf2SAXHUwMDE0pZpxQ7dNgVx1MDAxOO0+ONBSsFx1MDAxM+vh29ChSoOujIE9XGKHVlVcdTAwMDJcdTAwMTlFznrVu22UT79z/WdrqVVQ2Jhbb/ZnuKZXPrtcYqBqnnbFXHUwMDE0XHUwMDBlX1x1MDAxMVx1MDAxMVx1MDAxYS0x59WBUn9Wz9ZcdTAwMTH5K6a96TVcdTAwMDZx7Vx1MDAxMkNyvjuLd6pH+Iq4Mlxm5L7fyMjJzr2dLm+GrbGHOUIuZbP02PHnq49cdTAwMTHdvaBnXGKSotmmOkD2bo2o2VxcboSy8IvIV+JjlK5M1EJcdTAwMDNcbuaK9zZjXCJEUzhfgnFvXHUwMDBlb2bYXHUwMDA1XudsPkLO4zUyXHUwMDAzXHUwMDAxoV4vr1x1MDAxN1x1MDAxOZvnP16yxtyjqNA60Fx1MDAxZizk6tH6oN6ZdSdcdTAwMDRLXHUwMDFk6+ZcdTAwMDJcYoXCnlx1MDAxN4tcdTAwMDVcdTAwMWHT6Vx1MDAxYrhzJHlcdTAwMDEpVNl3umAsfFhV/VJaxqUgXHUwMDEz1ORkbNlcIu41rUTHq1OFP53CrUTiLFGUW4pcdTAwMDe621VcdTAwMDVcdTAwMWG2Mlx1MDAwNZ6vXlDoJVx1MDAxNTfxl+qCKNJGKXJsXGZSp3LqyrPb7c3de29kQMUwJlx1MDAxMOZp2+TR5ZlhdM25vSpUXHUwMDAzNlNVtjqPdlx1MDAwMbxcdTAwMWSwXHUwMDFh2laZLYjqXCKqx9E7vlx1MDAwNeHcRF3mMFWRXHUwMDBiXHUwMDFi8lx1MDAxMEC4jyGvybc4eaY6Ovw1aXVjNZetSvvGU3luk/lcdTAwMTTbdulNmrgwiVx1MDAwZuAqXHUwMDFlyCySIFxiXHUwMDFjgvDnUn9cdTAwMDXOJrot6L+9am5fTOObXHUwMDBl0s3X2irI5n+UiXJcdTAwMDRcdTAwMGaFMFmJuLrOXHUwMDA11cZcdTAwMTC7jFxialFcdTAwMDLVaiHwVv7Ns6y8XHUwMDExXHUwMDE2dtO65qZ3hMor0ihoRPXm5Yghu7sp61wic/1whHutq3XjS1x1MDAxNd8wXHUwMDAyyE5LMJ/r8sDoKtXr0GJkKoLVqJVEmMExp0P71saKa/6tglx1MDAxY5Vzb6JQfzkwIJd6nFv6SX++rsPZpDBzXHUwMDFhr363zFx1MDAxZFA3XHUwMDFjpVx1MDAwN1JcdTAwMTFcdTAwMGa7XHUwMDBlXHUwMDE4eVxc6ac1iVxiqGzP9OzPXFzceOLFXHUwMDFhqo/mmlx1MDAwNSCVKriReTymiv14bKLqki+g6mXl61x1MDAwM7/isuyKqpBcdTAwMDfEKtlcdTAwMTZ3f1xmLt5cdTAwMDbt94LMyn1VfZpi3/hcIpBMXdaMXHUwMDA2OVuW0aSX1C5NWZBcdTAwMTlcdTAwMTf0xkKucVNcdTAwMTnJqrM7XeH1wNdR88nlpFNcdTAwMTfJe4/5XHLVeGWTcz1zxtBWXCKx8V9XUNWOUV/AbILkhdXa0Vx1MDAxMFx1MDAxMaUmt6ZWg63CX2j2bViog1xuRtYoUo1cdTAwMTbfXHUwMDBlqPbQyy8r0Vx1MDAxNUNI++a++e1cbrzo3aa5XHUwMDAzpq0v9z7JVFP8XHUwMDFmNdyT4/RcdTAwMWRDksHpJch5J6xt/zBie1xib6JZXV1cdTAwMDUphGStaG1s2Hqmn8+JqO2U3VRieDSbo1xibfW4fzfQXHUwMDBig3jJZfVcdTAwMTAq/Vx1MDAwMWxa0S5A2DpeK3m3XHUwMDFhdtxvr010/6ZcdTAwMWRcdTAwMTa9XHRpXHUwMDEz/K9M6Mp2vVx0QNPnY8ylNnxqkTXDXHUwMDE44C3ONUFQT9u9dnCIloIzdVx1MDAwZqDK8FeY9lx1MDAxMTxibWRifdfKIaLf77lcdTAwMGX6MOJviXOJbpGXbMMuLkFkXHUwMDBl790pnn9viPx8c1xiKX54925LXHUwMDE4wuGQ/rjS9nm6Yz6zpY1cdTAwMWJcdTAwMDaNrDXUk35m9d9sQ0RcdTAwMDaW2nZnbstcdTAwMTFBbk3tmDzxKnP4s1xyYKo9bZXaNFx1MDAxMXBimvbkXHUwMDFkXHUwMDEyOMXVeDPAaHa+XHUwMDFlyIWu1lB6NPx1qjrVcEhWi2CMWzVcdTAwMDRUv77fi4OgmVx1MDAwYmJcdTAwMDNBPpdXpGlM5OI76ffI2UjZlbn8s7qaO6mbNlRcdTAwMGUhRsBFKoNYXHUwMDExTyFcdTAwMTSGQ77H81x1MDAxNVRTwm9MfW9I7zFy0eb656NcdTAwMTRY+8JvlrKE0F5cdTAwMWFcdTAwMGJ0enjd/SRcdTAwMTREQVWCvdUhqVx1MDAwYlx1MDAxY/tcdTAwMDZcdTAwMTD5XHUwMDE14FVcIvKz8j1mpiorXHUwMDA2XHUwMDA2R1x06+C9uuTcVpc3ol7wXHUwMDEyRFx1MDAxNmJyXHUwMDBi8aQxjL1cdTAwMTBhWlx0Xybgplx1MDAxYTx5+cpTmjeMXHUwMDA0XHUwMDE41ch0lyeHu2QoQtFGQ/KUkcTyKp1UmqFmky6z/3Ao8/tCgko6MZSj/FKk1t2qeMmuudCOPPX68NP9Ts9cdTAwMTjJb20nUIIveFx1MDAxM+ZyYlFbm8ju1zDD8Yow7zg+jEtcdTAwMDJcdTAwMThxfl8riZ1u6OaKtSbGK4U9Rs9cdTAwMThcdTAwMGKCfOD45/32S1x1MDAxN2NcdTAwMTZcdTAwMTbpYOGQRlx1MDAxYuRuLX4qtt9cdI7w31xuULDoSVxiIN1cIohcdTAwMDPB5dxRPXftXHUwMDFi1GS7hVx1MDAxMuFLJJYxjeBcbpT4vvLwXHUwMDEzhVFK74TokPenPznWWpNAgMJtU6XvJVxcd+Rr2JtdtyxcdTAwMThcdTAwMWIlw++j8bxMXHUwMDFh7WigaNRcdTAwMTmDlae8VjBeTOP+0Y5cXN6gXHUwMDFltIDBq+43sXNEXHUwMDA0Mse59MFdglx1MDAxNedtUkTErrqN1XlmxJNQdZulZbK7gtFQLWJZvfxcdTAwMWS/7pP/XHUwMDExdqtBy1x1MDAwYv65MOllMWP+tzbxXGJxVCPkSVx1MDAxN17xVPWDXHUwMDAySWhcdTAwMTOaQ/yh7eiNXHUwMDE5KtTVwI5tv1x1MDAxZCeFtolm+20yvG52g+8rUKpAXHUwMDA0VVqDXFxcdTAwMWHDUSVCSDevWcQnRa1cdTAwMGW5Urt4LqOqxniDkLJcdTAwMWRLbnkjaHHBiIcq2i5GKLah90UhRHHLcFuXoTaQ7/9cdTAwMWb4rnqFK0NcdTAwMWNcdJT01GhPOZO2ZFFcdTAwMDOUL9zuafHHqkfwhogsipdoRN6XpFc5XHUwMDAxq7yFYsRcdTAwMTaYsM5cdTAwMTmeKnKiXHUwMDExv1x1MDAxNPMu8rrSK1/8xI6vhtIvtNGujuqLZi248rMruOtW8IquUOYsXHUwMDBlXHUwMDE526VI+4rk1ptcdTAwMDdI5zaNN743RoBkSJVjSsW+6pX3pTVcdTAwMTRy9e6lqPnmXHUwMDE4XHUwMDAzVrrdretcdTAwMWKDLelrZuspSlx0t0pcdTAwMWNcdTAwMWXQhMhcdImwXHUwMDAxvC1cdTAwMGLset2+X/QuQ+zYJlvj/rAjIWMyPVx1MDAxOPE888rAwCZTXdTSfkf3XHUwMDFmkU/DooMzXHUwMDEwJ1x1MDAxZIrVZtJTknPVSDTb16ZSy4KklqtzsjOk/prXYlx1MDAxZpXrV02Dk01VXHUwMDAxn5/rNfNli2qDroEpXHUwMDAxLvFxgDSosy4+oKTI7HRCOebsemdLXHUwMDFj+Sw1vdWa86LFnSdAXHUwMDE0j6GK6qDy64df95V6L+9xkd05tFx1MDAxZDhwnmPgXHUwMDFjZ2821/tcYims+cCpfZSvTGDoyuF0onqYaHNqelVrXHUwMDFic+UjXHUwMDBl1HqU2n73W6NsebF6XHUwMDE5oDNcdTAwMWPNZd1b7b2Iu/RRiFvARb2qeqJV77tLYNLT2Tyd72ZcbpEh/0bvradGXXzVdVx1MDAxYvchjq2iXFw6XHUwMDAzXHUwMDFlylx1MDAxZUGwXHUwMDFiT7NhXHUwMDEyiGaTunnYWTD+7saH6fuinsRgcG5cdTAwMWZMUlqlXHUwMDA0vSr5r1x1MDAxOMp1XHUwMDAx7y1CvPLp8SxcdTAwMGbzXHUwMDE4t600ZTSJj4Ml7sdS8y3Po1/yXHIquVx1MDAxMfZNv9RcdTAwMTBcdTAwMTdvVdNaXHUwMDEw7sZrw1x1MDAxM1ttnEtnSfyMg3Hkx9XDVcnTrqZEqFx1MDAxY19Ut1x1MDAwNbNBd2v2t8hj3fO2RFxccpUlLsGw4jpcdTAwMTZKVTHypTMxTZK29/h8Xnc/1JdI8TO5LetjcGajlmSVXHUwMDBmbNTnRNfmuztN3YYq7qG0JtxpMVBXXHUwMDA0KIHjXG5CMMeSv29cdTAwMDa8+UdcdTAwMTA4INTdXHUwMDBllDV242NATcJYjre+YbgqflRbwlx1MDAwNkR2w3bOclxuJ1xmg1x0asTbcy61qKz9XHUwMDFhyLzCXHUwMDBiL5nh3Ua8yc9oRlx1MDAwNG+zutnru1BcdTAwMTTvO2fm3vOHYUu8mMzlQlx1MDAxZrOtm1dD3NyWmFx1MDAxZsqVoZdv+5SW19BwjqI24de8WtF15Vx1MDAxYoF4rkOVd9sjtkTU9Fx1MDAwZXraiFxcP4aU11xuKlfGXHUwMDE3R/QjXHUwMDA39jScQipcdTAwMWLXZsKsQVGldIr3NkxAotB4iDR7+2FR0XRmXHUwMDBmqJbpMVx1MDAwNO2NtVDUfa3p+XS/VaLwnGBcdTAwMGW8qMVR8plcdTAwMWPf7uqAa/JcdTAwMTJFXCLRZZHzXCKk3W1uXHLbp//wX1x1MDAwZlx1MDAwM72Qt9Bz3852SbpHwXtRRE3hXFxcYlx1MDAxZemjlSpcdTAwMDR7wO/dXHUwMDAz+SZjbmCdhlxi9bApLOLRhiveifo5xHcuNESK3pshXHUwMDE0uVx1MDAwNVwien2QakVR3rVcdTAwMDBcdTAwMTHDg1x1MDAwMW9cdTAwMTnCwqVcctNIqU1cYlx1MDAxY7Bf4n7RIEHyRPMtono0P++vXHUwMDE5Ylx1MDAwZlwiU2BcdTAwMTPBVXtjKVx1MDAwZWGYP1x1MDAxMGZUPfz8fu5qMbX6XHJ1nrfSXG4zNXtTQ1GOlvDz3V1cdTAwMWWqZKt2Zdc0hONsOlxiNT5cdTAwMDL/XHUwMDExo1x0bLLUXHUwMDE0pfF3tiScpq2wXHUwMDEx1n8l0Vx1MDAxMp2Wb3JP9zhfg35W+LS5SWI2mChcctxZdVx1MDAxYoqDf6XYiGj1gjW077l7XHUwMDAxlS9DgEJbtsPtMLhgjYNvO8nuVeujXG64o38762qOS5SVanr/fXxuXHUwMDE1MFx1MDAwN0yEaIqtMXBcdTAwMWJHhFQkzoivXHUwMDE469ymZsbr1LNhW1xuXHUwMDBmfpHn11x1MDAwN4Si3mmkTTA8X9Lbw+ZcdTAwMWZoPVx1MDAxZFnDuXlcdTAwMDRcdTAwMWVy5brSXHUwMDA3dXlyVoSho1x1MDAwZeVcdTAwMDKQa+9XXCJQJVQnUVxyfMckMovby+mi5mWU31x1MDAxZEmspDJcdTAwMWWCveXSxGztsNXRs90kTlxchMdcdTAwMWK/R26fnWfE+V+A/L/YmHOfPX3a9I1wXHUwMDFjWyxRuK17yfXtzVx1MDAxNrhzfzl9XHUwMDA1XHUwMDFjICOBXHUwMDAxufBGJeN5XHUwMDEwL7NtVFx1MDAwMdllXCJ/QbpcXL8k48M3XFypSF9YuuIsXHUwMDE1/WvGrFx1MDAxMT1BLpWqhsQz45d76Zut8jhcdTAwMDDoXHUwMDE1p+XNXHUwMDA1UFx1MDAxYjDj+1xyJes1mjSP9PDdNOz1kHJwqHeN3Vx1MDAxYjNcdCU+2IBvePJcItRYV0u31o095OhJUbBcdTAwMTJFXHUwMDFlr/RcdTAwMTIhYkiJQzSuiIWyJUk7u69ab+824lx1MDAxN+tb92vNfkTaMFXqYTfV/NaqTK3TnZLuosO4jpzOna9WXHTnrLSJZFx1MDAxNsUmcSQxL0RzLahcdTAwMDf3N0/LXHUwMDAw/2h11t2Vtlxctc97uUuHbGycqFwiXHUwMDA14t2KpWa+6bivWKZJfMhcdTAwMTKvXHUwMDBieLQvfUTsXHJC1PxNirzUKKulxfru9WGPqyYjY9tbzbvUvtBbalx1MDAwYq6tVHMp3Dqm4D7gmFx1MDAxZqr3dVwiximFpMqhKFx1MDAxY1x1MDAxNsKUhK5cdTAwMTZlXHUwMDBl0lx1MDAxOM/wiPgpuq7FpHRVevajsKCyXHUwMDFlb9uzfJYhSbj8OFx1MDAxYipDd56bXHUwMDAwfuy/XHUwMDE2LV5cdTAwMDGhPlV7Nd8mP8WbiJn182qVitzi7aNJnVx1MDAxML1YkrC7JGFkQuLe92Xli45cdTAwMWH6oj+/71x1MDAxMfg3m7mS1C/le1stw+Q2gtuYPKzEJankziV3wMiAS8k0PqB+XHUwMDAwtVxi5UG1XHUwMDFlqbvCT5NcdTAwMTHrsX1cdTAwMTk1OeZcdTAwMGbXMKo1dy47RySGTpS1XHUwMDFhpGpyXu5iXHKOQVx1MDAwNrlqo/SWiG1cdTAwMWNcdTAwMDFcdTAwMTdcdTAwMDZp/7JvlVxu1a1mZFx1MDAxZMogethSd7bZxvvSMeu4LFR1lMqYPVx1MDAwNmwriFx1MDAwZpHZnpZcYrlWxjL+1NHxXFyTTujN4/VcdTAwMWVQZkg3XHUwMDAzXGZsgT6umVx1MDAxN8xXqclfzZbk96RcdTAwMTKfilx1MDAxOHaQVqa8sMpY9vgw9YdPrtpcdTAwMTY5/b7mvVx1MDAxMvDjXHUwMDBiJcFQ06TwW/dG/S/PJG50YsvaxoOxyZxf75vsmyvnyFx1MDAxM8ZjQr/XtYQ2XGbKOpUxZqMrtPqmikJcdTAwMTRWSt4oXHUwMDE19Ur5oFxmUPFcdTAwMThuoVxiko1VVTyEeVx1MDAxN/m1m1by1snMXHUwMDAwy/q9g1DZXHUwMDA1gjA/XHUwMDA2XHUwMDA2XHUwMDEyz6+ZMCNRneurOL+vXHUwMDA3XHUwMDA279/mvrxjPXlv40zihoLocuDKyEqURr2XI9d5XHUwMDA1y1x1MDAwYi7UODFcdTAwMDagUcfMmOAkXZdXy/FKIaVYYer2SLHEkVx1MDAxNWGBQalcbvVtoD5cdTAwMTPIa6HG9+XRel5BiEI18JBWzEUs4O39z8ZcdTAwMDS0cWOxMtpcIjrncyVIXHT5oto593YltUiyK8Z5xHzdKkXqk4nyPG2GXHUwMDFjkn3aln6oXHUwMDFkdHVcdTAwMDRcdTAwMTW1z2hccqgkz+pcdTAwMTGuXHUwMDA2rq+RgiBcdTAwMTRzTPGwXCK54vxMqsO9XklL3nzTuKgsXHUwMDExTUhA3ZFKaFx1MDAwZbQrXHUwMDBlrfPvXHUwMDFmvypcXIqUQMWE1Ed0XHUwMDE0fFFcdTAwMDTScWNx9WGqorkj5bP4YOvMJapgbvHasrB14fdWeFDiSEFuqyuebXv+yWd1LkTwbuM1z64qvs2szeWJc19cZrNEzVx1MDAwMYsoU9hzXHUwMDA2WpKnXGJccnNcdTAwMTFmrdqqXHUwMDFhy1k6hLd0vVxiXHUwMDExYS53v39hL092y+huVfrJI2WbfVUyJ1fNXHUwMDA3cVNcdTAwMTVcdTAwMTVgtVwit/ip1/krnn26V4U97e+MXHUwMDE4SUA2mV6RlFSc4f1Ib4RcdTAwMTZw/rzG00ZccoJfle1jT7r41lBcdTAwMWUmcndns1x1MDAxYmM1xzvERDt/XHUwMDFiK6S3+GeO7GHu4qTYr3R5uUKexkwqb3DHdLTGWVx1MDAxM5RccldHXHUwMDE0OsLv4vT7NaL81LbP5vr+VHqdrlR2XHUwMDA0ttDdXG73KPR3ulx1MDAxMGhoXHUwMDE2OiG1N+KV2zwuXHUwMDE3nIjW3npPg1x0JeZl+lpcdTAwMWXEn6pneY2DtlPrjkJLWJm7Sn0570FcdMmffaWPW1Sh6I1cdTAwMTLFUjXdXCKTXHSJlFx1MDAxM22lXsSf++PsMX5Nmm1xaleYwrzTXHUwMDFl2XSIR5dw261cdTAwMDAnXHUwMDBmR1x1MDAwMCnKXHUwMDE11J9i87XtR7b8ON1cdTAwMTg/XHUwMDBlwKtNN/lF5lxmXHUwMDE0a1x1MDAwMzHmWfNITfamvahaRKnaU1x1MDAxOeWj4J/VvIWzwZD0xvczYlx1MDAxNHg9e6+CXyosXHUwMDEwsiZ7PlNPolRz1lxyZ71CQqPzsFx1MDAwNjWxaWou/FxcSfBTielcYlFcdTAwMDZg1tZcdTAwMWLxXHUwMDA050EtVX3I9c5cIrHS2XL7eC9cdTAwMGVcdTAwMDFh/naIWVwiXG7bTb8hOnpPN4q+v2uN3/Xt0Fx1MDAxY1BccnlcdTAwMDb2mmhXXHUwMDE3c+E8eCO7TFwiUWyz6lx1MDAxYjCur/v7nPNRQSlcdTAwMDYuvr+4jbFcdTAwMTlRz9a6R0Btblx1MDAxOTvB4j1cdTAwMTQzXFxcdTAwMTLTdF5Cxk1BXaZtKX1cdTAwMDBcdTAwMGU/r6n4LTGGvVx1MDAwNt2d3FQh/zTPX1x1MDAwZfpcdTAwMTPzsXrxvH4gV6jw6pjz/Vc2l3bx0PO9v1x1MDAxNs6DfGfnnoV3gSuo+7DP831z+c5Um4een4WqmC6dx7rBMYeLq0Ra6Fx1MDAwMERcIixye1qb/qiCbXyIROqY55f3W0uhXHUwMDA0+lI9+H5UgkuRxee6itC/alZRvlx1MDAxNcePLehL0Vx1MDAxNl+cVVx1MDAwMY23jqm4K+q5ft1bXHUwMDA3r9yOXHUwMDExvuef21pkQdhcdTAwMDCw6qL3tt1vaMP3KvN6a6qPXHUwMDE2p2/Qi9Lgb/dcdOtJm5XQOcRcdF5AXHUwMDFi9valukJcdTAwMGWn2D5mpamPvzF5xe3I7EKQ1FxcXcFcdTAwMThMhf7mOfPehiYmnHHgOEnTmC/ljIP/a/v/a/v/P9veXFzQrbtb8tqfPVY7/XhcdTAwMTmOn5N21M1cdTAwMTFFrPVGPPSheZPf6GnPYKO1jVerwzqaYTrtPnprZGxb8LeWoKnBaien7fRcdTAwMTfxVmBV6IxouTKzKVx1MDAwN1ZHWXx974AhXHUwMDFkVKi0V8q3XHUwMDFkuS3i21xyJ7yMfYRcdTAwMTeztSxXhZzFSVx1MDAxYsq0jJ+PhSOVL/Kf/7YsT9yfv0NIZNFzLZdkXHUwMDExXHUwMDFkQq3Ad42qjejdN+hz74zIXHTv6zVgXHUwMDBlXHUwMDEzvYJqVHO2N8Zccn2dvFxydbQuY5LBXHUwMDA1Ul1yr73mk/58L9hcdTAwMTZcdTAwMDfFXHUwMDFjUkrVYUI6XvFTNDf+MNzud0XYiVWGMviVJ1x0XHUwMDEy9PLZ1kV5rnsqy0NcdTAwMDUkz27StXu0Z0yYRsrawlx1MDAxY/y97zj79n2cNnnoTlPHknrGkKDiup5G8L3OXHUwMDFiW6U9eVx1MDAxOIpjddTm+y2EkKutmku+ueqYzsm6JEDKVp+2M3Clnlx1MDAwMFx1MDAwMi+ftVqoM1x0t78pNI5cdTAwMWKUNVrBeYje8bg7X+NcdTAwMTm4ot/jwbDkjVx1MDAwNkNSskenT/jmXHUwMDAx3yu19lx1MDAwNkCfwHltyFx1MDAwYopWJ9zftVx1MDAxM/shqET4x1x0tOU7qUJl74L229DwXHUwMDFhqC544iGUMs/4s669n5pf9bVf+6xa+XYkUV8pMNegXHUwMDEx7ChLo0Zr3u00QFBkmqUok1uP/pEjXG6K4Fx1MDAwN3LIXGK9iVx1MDAxY2nent7MvZVkNT+H5DjxXHUwMDA1xSxLzFRLv1x1MDAxNMeXapDAJrJP9MFcdTAwMTjkTlqXz7FcXCXsXHUwMDAwuzqKm2bJ10+HvF85fUFcdTAwMWXsNb7VtrdUw2jrq/+wh0jHozckbdh0W65U6OCbVqvjfdPs6WpiXHUwMDE5fn9cdTAwMDVXQWJMXHUwMDFlal79Rl5GXHUwMDEzkd9j1dZcdTAwMTgt1ZcnNZQxuVx1MDAxM93IYrRV7UXRP5+4d02NklVcdTAwMTnEQq7f2ys7VE7deShX+K2pv0RQq1x1MDAxYodwK+Syl4nWcNJcdTAwMDah24h+XHUwMDA3kjQgcm5cdTAwMWJcYlx1MDAxNbqLSFx1MDAxOLdEXHUwMDE53vXOdeTKV2Tme/Jcbl6y721i2uJcdTAwMTn7XHUwMDFjz6VC0asx0EEql8NhnSfGXG6WXHUwMDA0yJBTQVVseeeN1y8pj7NcdTAwMGVZtOx3l1x1MDAwM5v0JOfPb+c6lJNEptXaNKdcdTAwMWXpOyrqZtLzvFMv12QuXzV3eO5QeVx1MDAwN0Pq3WPCZTdAXHUwMDBmq03j+6t4K1x1MDAwZp/9qJ2ojzP4xvfgUn5w/qI2rOF3XHUwMDEz75NcYkLRZFx1MDAxOVx1MDAwNMHlQVx1MDAxN3nN986GNF7hXHUwMDAy0P+PV5d0XHUwMDFk1ySzbirC0z/XiKjhwUCeiYilyItccuD+26uqPyeWXHUwMDEw8bX8joToq+C/vdxuhjpcdTAwMDZmvNWlfFxuwH97JS5+5jdjJJ9cdTAwMTTNeU1cdTAwMDT/7aX6XHUwMDExXHUwMDAwnuRcdTAwMGb2YXL/84NNPTtxu7Det/X/+DXPdb/1/mpcboZUXHUwMDA2jVx1MDAwN/7b6y1ZZ05/bNzAUvG/31PKXHUwMDAxXHUwMDFmPM9T50Ptu7bAgyn/v72qXHUwMDEy/HJcdTAwMTb/ZE/6/zRGNZ2YLep6R2Le/7Stq1RcdTAwMDFcdTAwMDR0Nbu8jsf/vKfGdcNcbnJ8XpGOnPzvXHUwMDFmXG5fqlx1MDAwMVNB3MEg1LvHpax12Mz9JmE/W3Gg5lTiXHUwMDExkMXl0s1OZz2P/j/XXHUwMDFkRZfXepBcdTAwMGItXCJSn++E8sU3fml/13VN/82XXHUwMDFkVuo0UaxcXI0+ajT8s7/Ee1x1MDAxY7/FXHLrz1H4nqNcdTAwMTmfXHUwMDFme1x1MDAxNU+sM7HKNWq6XHUwMDE2uS/Ig1x1MDAwMeo/VyutiLbOwltK2yVZSYFcXHX/zVtccl2otUizisQ6XHUwMDE1XHUwMDA2XHUwMDE37aGtWvBcdTAwMTOlYJ9lQFx1MDAxOT/7gZ2vzq+n1Kwyf/ZsYVwiwTHq3vBcdCX8xqi7do7playe/+xebef3OZKbXHUwMDE0LZX8+WHnzjGSnDWq2P/GXFzAj6OU9y+l/cXPrp5jWilr+39jhNPvmlvflNT62a6HXHUwMDAy9Yf9X1X/XHUwMDE3XHUwMDFi+i82fF/7rM2/eGjPeCjb7Nn+u5/oXHUwMDE3XHUwMDAzs9R+i+A3f+vP78Vufat/vpbPvZkmRzDx+LNfI7gnT/DSQbGf2+mbgIHoXHUwMDAyXGL+xf+LXHUwMDExV4T6iTeI8j/54Z28y+Gl/6TbeGI/+i9/dP7Hg7Knz3R/vm66XG6wXCInTY+Ouv9soGzB+TdcdTAwMTgq/ifnXHUwMDFjzmL+K5z/Nz7WuVx1MDAwMIKLJoqUKkONY33Rf/ElbEH5X7lRXGL9oEBpXHUwMDA0oNnEp+hG0PNcIlx1MDAxMHfiisNcdTAwMGJonSgk4lxmXHUwMDE0td+tg+h/+Vx1MDAwMSd1XHUwMDA2PSymJlxyuaOvqlxuVLrw81x1MDAwM1xuI8X9d+GRP8UvXHUwMDFmctFccvNd9byXf1x1MDAxY9Jo86L63VtcYohT8OkgUEv3N0brT96Y+OmS37e/Mc7fmOHblz44x+jy33Xab66AvzG2eJp9hsCU/1x1MDAwN1x1MDAwZpvtt4dq3Vx1MDAxZfIvnitLPOeVfH01XHUwMDE3tnPMMzRPf4JcdTAwMTFcdTAwMDNcdTAwMWPr/PxV/fGVXHUwMDFjWHvB//lcdTAwMGb/ad45szVW+cOhpDnHXHUwMDE4w2t//Vx1MDAxYkP+6eLc9Ni//J3/uM79Y+1cZv9cdTAwMTc7xWkyXVx1MDAxYY6a/OWxqv3sI3bfkJT+TKaCXz3iQMrlf74zk3PuPGZAOlxc/eYlulx1MDAwNMTYup4/av5cdTAwMGIgdczP9bxcdTAwMGXTxrO2wVx1MDAxZkl/PEhWXHUwMDFhz//77PVP78astlx1MDAxMT+8vFVwnlxuZ1x1MDAxMeZfXHUwMDEw6qA7U61cdTAwMTTYf9jl6784XHUwMDA2L+6/sC6X8P/g2v/SXHUwMDE0Vf27Xuh24DZC7mKsg+BC91x1MDAwMFxyOX+Ry3i1/Vx1MDAwYjSOUD+V+ZtcdTAwMTf0IXWus1BiJvE/XFyBvOe8pV320nfR+ckvOVxumDfCf/Jmg7K/XHUwMDAyjS0jzF9cdTAwMGVnMKlhamDaXGZFgfmvfvBjXGaU2uFcdTAwMDP7XCKHNfT6X3zCy4mGknDEW/1E7njuZalcdTAwMTNcdTAwMDbvtn/xTdUuSCXqPq+5/Vx1MDAxZC80L/3Lec+9Ve5cdTAwMWFm+Vx1MDAwMrlBKPF4NFx1MDAxZqHMPlpcdTAwMWZqiJWWv+TZuH2P79/mcS3sVlxmrsAjxO1cdTAwMGZDK151lVx1MDAxZPlcXKrndb5cdTAwMDJuvd1cdTAwMTZu6lx1MDAxMPO4hTmmXf7qgLBz1pzfUPutsFWjXHUwMDAz6il5glx1MDAwMulRgJQ2aXtuWFqk+OdcdTAwMDexQ/VnoGFD8VE6tYA8XHRcIoyXz8H6XHUwMDBmL+ZcdTAwMWZPNm9ZxZt/+Z2MvegkPDGphoDO1v3DRpacI8OfX8wgXHUwMDE2YtxK54uYeKKNZy+ykFx1MDAwNe9975tOXHS2fzk58fD/S1k6tiVcdTAwMGZcdTAwMDa8WFu7bKo11D37xm7ZTuHPyIJ4XHUwMDAzVFx1MDAxNqgu4rxNzVx1MDAwN79zSChPqDdKJtaAO+OiXHUwMDEzYdJf2mWh2Vx1MDAwZZ097cev5cBu3d/98Ofalvp0O8xlfnHUazC+XHUwMDEzj8zywvvzXHUwMDE3T56cX1fKruTBb4ysXHUwMDEycExQXHUwMDA3psr9MEvl9t9aoJVjXG7X/cY8ufzUfYe64sO/+ziTXHUwMDBiQPVcdTAwMDBcdTAwMDXx+Icj4p/u48w2+8Ugz/unKPQ0/WpH+S+OXHUwMDE4kYL1Wftw1T9cdTAwMWbLp7bkqbCU+H/cKvvTaa97MG7/+Fx1MDAxNFx1MDAwMn1noFCetX98pbFOnMf8r8yk6q9GcSdH1WuAKFx1MDAxYvpcdTAwMWLjNeI5xnW/XFzy+1x1MDAxYlx1MDAxZcg/zd5yfr39u47701rr+2bg3V9cdTAwMWWdj0jBWlx1MDAxN9lBl/7dT/PTyqFV/4vLp6ida3pmg9rY469mvn9cdTAwMThLONM/PlWck5NreVx1MDAwZj/amYqqvGknvmf3nsz/cVx1MDAxYue3aMB/SZeFNVxu2lxcSH9cdTAwMDLavDZcbv/nu4hcdTAwMDen3tJrk1V+vlM57lx1MDAxYyObkFaB5M+/qlxifeemi+nuv+uIwv9aj9XQn/3+vrPwkj61g796I1fndTg1fmUyQeUvd6bSPq6E+XN7hdNcdTAwMTF2NtRAyFxyw2ecjlx1MDAxNptcdTAwMGZcdTAwMTH8XHUwMDEzf0MjYEo9X+O9e+U3hcw7iZ6u1fpcdTAwMWR5Olxi0cC+Zbw23DPPW+LvxaLL60yFXHUwMDA0Ubyoz0K9XHUwMDFluz/46OUzdORz6V6oXHUwMDE1P2JcdTAwMDTmU+8/7Vx1MDAxNs+LXGLb7l04k1/iONQ9XGapKbqWl8GcXjBGXHUwMDFje8hyY1x1MDAxZabqMVBfy/mSWaBLgXR3XHUwMDE2mznn8uRcdTAwMWWTs5YqXHUwMDA3iNzmXHUwMDA1XHUwMDEyKZ/+XHUwMDE0jpFd4iiKmtTVuFxcLs+XPbTbxJTKXHUwMDFlzq94SVx1MDAwNexcdTAwMGJ5pyr9+cJcdTAwMGJU8Vx1MDAxMu37KzTjXHUwMDBiXHSiUGSD6VxitEb7fK9cdTAwMDFcdTAwMTbX4HulXHUwMDEx5qaZw/NcdTAwMTZtXHUwMDE3XHUwMDFljWim7rNWYbDodrte+5eWr/mrKkuWKnJMXHUwMDBl1DW6z8U9zVx1MDAwM45yWOH9cZNkRe+a+Fx1MDAxNsZS1Vx1MDAxMGY4WI67dLFyfVx1MDAwZus7XHUwMDEyj8SupjWqRfaD5bqcXy2T5C93XHUwMDFjz1x1MDAwNO2I9Fx1MDAxMc1q94XgJMb7fvt6v8+dwPv1XHUwMDEy2VSMflx1MDAxY21dR7ScXuStKFx1MDAwNyhjz3j/f7h7jyVZlWhZ8INcdTAwMTig1Vx1MDAxMEi01mKGTHSiXHUwMDEz+PpH1j73vlx1MDAxZXT/QG+zbVZGZSVcdTAwMTBiuXuEr1x1MDAxNWwrXHT1qZxcdTAwMGWPUdT3Zu2D1kNLvDTLsurDfacvXHUwMDBix3HSWyaYuFJNuCgzLcCfXGL431hi05zl21x1MDAwMb8ge0GtMDqE81x1MDAxMzLi/jJcdTAwMWUts+3B+Suv+G5zXGL05/o6i77x6X2ttO8yXHUwMDEyTJVcdTAwMTeE4lx1MDAxZcLgqY5cdTAwMDO3SeG7/VVx/9ev3XB/c8P94mhdmD/s/a5DOvLr9/fze1x1MDAxNkLnKD7MrCz93eLxJ1x1MDAwNWrKk1x1MDAxMHvhSmpRXHUwMDE5UjhcdTAwMWJcdTAwMThcdTAwMTUthV+oiFx1MDAwZstcbk/226ZVkCnw4eFhfOVlnrbSvXtcIlx1MDAxN3qdIMbPQVx1MDAxNlx1MDAxY1dVo7hcdTAwMWXOXHUwMDA1/7VR5Vx1MDAxYS1cciGo/lx1MDAwMsBcdTAwMTdzXHUwMDEys1VJbDRcdTAwMDRcdTAwMDKziaunYsdIfPetwdagiUenQOAvK/WR89t7UetcdTAwMGZcdTAwMDe+cKdhf+GGV1FGXHUwMDE3/OHhMHJDRCtcdTAwMTWeulx1MDAxZKCu1lx1MDAwM+DAesR3rWX7XHUwMDFkXHUwMDAzXHUwMDFkwKIsx7SdXHUwMDFkuMd4oZNoP71Q9d1eUjuIKExIvKSzX3pE5uM5uJVQ3lx1MDAxMv07y+1cdTAwMGU3eFr056F1talcdTAwMTZcdFx1MDAwYrJ//iZcdTAwMTnLyO/nP11cdTAwMWFYd5N8fz+/PX1Z7/XRXHUwMDEzc3tEXCJ/cWWVvGvkW5RcdTAwMDelXHUwMDAxMll4hpxcdTAwMWVholx1MDAwZkxcblx1MDAxOD28XHUwMDE2fTLPb5NIXHUwMDE5jv1cdTAwMTTZbcs/av9+5Mj3h/M/2fEo89f9UMU/KVOzXCKkJNy7xphXslx1MDAxN92cUdf3StRcdTAwMTeqmG1VLZVCIFx1MDAwME7VezlcdTAwMDKMXHKA4FxiWPh5cvFxXHUwMDAwJFx1MDAwMcT2eIq7MD1N06VcdTAwMDCw5oPo+WSycYGl9uXlLjtcdTAwMTIsY5N/dihNoCp44G88KtqfNohcdTAwMDRgXHUwMDE1M1xi2vDF1KjSosKkR218yCA5za7qb7VcdTAwMWJcdTAwMGVp6csl2SPlXHSGe4CGXHUwMDE5qqlnXHUwMDA34/mhdq6+30uk7aaXTSid11x1MDAxM1xcndK1wOg1dnVR99edjplBpFxunC6O80HXboju+u+zUzBcdTAwMWOO1eM0dFx1MDAxMaCEavJ9XHUwMDFmV5JcdTAwMGbWXHUwMDEwqHjXI1wi6tSZruGSNDoq7JnajaCWNa9rXHUwMDE4poFcdTAwMTasxTpu1tg6n1ix7rBcdTAwMWZ77L1cdObiwWN+XHUwMDAzXHUwMDE42yzY+FwilyBcdTAwMGXyJfvUV508U/xnXHUwMDA0SLWLNjd+uJWDXHUwMDFjZy5wWGBnxVx1MDAxN+ioc2E29j1rtFxuLL5cdTAwMTGCoetN+bqtw9N621x0eriT51x1MDAwZjNcdTAwMDLOL51MQmDBfX9cdTAwMDezlFx1MDAxOZevIeVcdTAwMTOjgsFLz1x1MDAwYk78cK/eXHUwMDFjiVx1MDAxYcJ7pNW518VcdTAwMTfpqFxiaP2cMeJw3Vx1MDAxONxcZku1zlx1MDAxZTBcdTAwMDb1i9xy2DXBr5pJXHUwMDFlXXfoW3VoZ83XoTzs81x1MDAwM636hC/heVx1MDAxMVx1MDAxNIJXXHUwMDBiXHUwMDFjrHp0XHUwMDA1QZasXHUwMDA2X+9oNrogSlx1MDAwNEOPb52/XHUwMDFmtFxyXHUwMDFmUD39NGE8jOZ62Vx1MDAwZpQ6vNOlYmVcdTAwMWTbRipcdTAwMTN0PX87hJrJfk2zXHUwMDAyKYr2wZ/3rNdcdTAwMWSevHzOSsxcdTAwMGZcYqJohHd3guErorFssZO5+stvx1x1MDAxM4QsJqU9MXpcdTAwMTLE1mltn1x1MDAwYn7je5a5VdrBVOhVl92PXHJqIFwiXHUwMDFkL1xmfvFPqzxNr1FKsJCSomynXHLv6lx1MDAxM0W+U4N4xbb5Y9bAl1x1MDAwNkiAdbL5XHUwMDAykT3dw774Wlx1MDAxMVx1MDAwN422u+lQPa1cdTAwMDM8XHUwMDE5L1x1MDAxN1a7Xlx1MDAxZl1cZmwmPexys4aj2cj30MLI7tpNS9Enr1x1MDAwNbdy5mFcZm1Sq4WAIO2jjVorgGBkXHUwMDE2f2FcdTAwMWbtYW6vXHUwMDE1XGK8YL9cdTAwMGXnw3LgoWiUp3i+QWlcdTAwMTQ53kv1ULuiu1x1MDAxMlx1MDAxYSxtYEo3flxyN1x1MDAxYzShmNiaY8UoefeOfjrc1UGD41x1MDAxNvjcTmRcbsxcdTAwMTIhesKV3Vx1MDAxYlx1MDAxMlxmv5NCuOjjXHUwMDEwb1hcdTAwMWTr1XWG+b2F+eB3XHUwMDE3gW1cdTAwMTRcdTAwMTCdZuyFvVx1MDAxN+4ubp0h2GxTXHUwMDFmJMwzOtxcdTAwMTm4kbs+lHVHx0rqn1x1MDAxObSwxusroIdnkZLWLm6v0lK9lFwiWFxyl+xcbkFcdTAwMDS9p18zeDHlPXpAgzugND+XaFjTXFzjT9eJdKp0Z1We+XFaXHLoXHUwMDAyRmeaj1x1MDAxMNw3dII/SIR/huaYovBecegqltOIxo5egrZCRv179EWVW+9AtjNif/eEytq2/TRtXHUwMDFiXG78V1x1MDAwMVx1MDAxOMCSJFx0iTDK/CFcdTAwMWbLLqS5R9mOXHUwMDFjVlWWqPY7XHUwMDBm0lx1MDAwN0iEfCvvJ/5oXHUwMDEwWWzR74yjwLyIQXnHZSVcdFx1MDAwMlx1MDAxMO07QJkyqeZN3/3p5EtQhoeWfDZ6nm0yo4rvVLLkkVfdvkaSWzup2i6aglRcdTAwMTlB0biwUFx1MDAwNVXKfpOto1x1MDAxYuWRj16XaYVI5F7I6K5cdTAwMTLhg5rcXCJ7jlx1MDAwYjpqXXhOXHUwMDAzpNZcdTAwMWbL7uFK8GB79bBAqjCRSp5cdTAwMWRX4uvn/bHGedBj9WdkvjQ58PpcdTAwMTGyXHR8Rc2vKzS06SmyRF5oeWKVR3pcdTAwMWFVTJVsLlx1MDAxYr4x30LirO03jlx1MDAxYY964lx1MDAxNHtcdTAwMWXjTUZZRpW38lxcfCZvXHUwMDExuY44JqhcdTAwMTbEYPnV9vNLklx1MDAxM1fBbd/TUzXle5xcdTAwMTT1ePsqL2F7WrdvpWY3yZ9RXHUwMDAweFxihj/7i1x1MDAxM23QXHUwMDEyzeQnmYz7XHUwMDFlM1x1MDAxNeypdVx1MDAxZK/eJlx1MDAxMrBvXHUwMDA0vPTxXHRpa1x1MDAxMZ5cdTAwMDFdbd+aY5pcdTAwMDNnN/5cdTAwMWRH9lx1MDAxY0CL21x1MDAxMIDOL2aKIEHs68XvnmBcdTAwMGZSPtPJmX1jNOVHTndkxlx1MDAxOCewQU2vcsZcdTAwMDFcdTAwMGVPhIebLFx1MDAxZO5QyJjIXbpcdTAwMTOO3kfelCWVXHUwMDExTtdcdTAwMTlcdTAwMGbcvsCDu6A3RGtdV5Zk8/E8m4VSYVvEO+9cdTAwMWEkipzPXHUwMDAyf1x1MDAwMbn7ONgvIHVuqVx1MDAxZCvtMvNcdTAwMWT1lXjFgWmClnFcdTAwMWP9iaTxVydZautcdTAwMTNFsCbCUb5rlEDTvvRcdTAwMTZcdTAwMTGd+rFcdTAwMTJ5hMeakO8g8OpD4aWP1DPDbTpQyUKsx66EyDHTOlx1MDAwN63RtbHb7yVsN1x1MDAwNjhCfGx8nUel4l8gXHUwMDFkf+/a93k9PVm/cHPbUfma0YTmtFx1MDAxYpTIeVg8olx1MDAxNupcdTAwMTVjRrOLXG5cdTAwMWIoXG6XYFx1MDAxZCFcIjF4/Nu+q0cpR+bwYr/Yqte8wUjOxHePfn444uVvmlx1MDAwZu+7yYtEMT/dMVx1MDAxZFx1MDAxOJVcdTAwMWHEQsJcdTAwMDRdLsCVWGNzPUiGzCZbU3ln/+0ndkZcdTAwMDczkaQ4XHUwMDExf5FcdTAwMWVcdTAwMWF9XCKm34JcdTAwMGY88lx1MDAwMZLwm/ppsOVFuYx4nbNcdTAwMWRcXPTQw0DEOp6me59cdTAwMTFcdTAwMDW2vFx1MDAwYqtvXHKDR1kwu06T+EmQm3i34eJcdTAwMWXhJH9cdTAwMDTUX8lPwExpjVxi5L5TWfZqu8CaqONOqijVXsNcdTAwMTGk31x1MDAxNdiWuXBEY1XWUXmaP1BcdTAwMWPBmE+1XHUwMDE34o07y7VcdTAwMDaBr4tcdTAwMWQ39LynXHUwMDE1XHUwMDAwO4Hn75G8R1VITk7W5u65Qlx1MDAxZHstvVx1MDAwNrCIzzVk+0BRTsX7krxcYoAxnmCSvzrDSrVkdlx1MDAwNVx1MDAwNFx1MDAxZMdvVj2O98tRvMiSXHUwMDE3L1x1MDAxZq9l85pWc1x1MDAxZSWMoVx1MDAxMq59lptcdTAwMGV3pjammWFU6rBAXHUwMDAzTLrIIHqt+75A46E4Z2RAi7cnlDpM5i+vKeHhXGbPZ/b9wP7Q0CPymYXqRlx1MDAxZk7y477Y3DyS/4mbkalcdTAwMGV+9HpHns+9bygvPcVVvDMscFx1MDAxMFx1MDAwNVdq7Fxm+C518uFcXI2nXHUwMDAweqk2XHUwMDEzc3rPxN2TymVccuKywPhBKHSuXHUwMDFkQqWJLEl0T1x1MDAwZXjBXHUwMDA159PiZ8e6XHUwMDAxteFrglx1MDAxOEfo7lx1MDAwM5bMXHUwMDA1tzmsesXUm66K1KCl9T9PpzmpjFxusIzHtclcdTAwMDfNXGIsXHUwMDA0p6IoplFcdTAwMWa9Leom02u+X+89s48we5SHwTdz4E7dfqebvz1cdTAwMDPtTHJcdTAwMTlcdTAwMWFki3mV7Gifv3NN9/FEk260XHUwMDFmiddcdTAwMDfDncy9PVsoSUNcdTAwMDdMbP7CvctKxP7WMvXEcphcdTAwMWKT4oNEP3I49prLfqkxfChcdTAwMGaRv3HZLCnLXHUwMDA0yXGDaH/Pbubnr5vhq69Ku9olhf9kXHUwMDEwtlx1MDAxZt3LXHUwMDEzPss4slBrXHUwMDEx1KEt5KCRjjKCxMTXxdhncMY/SJBcdTAwMTVUMFZTod1cdTAwMWFKfO9cdPWM8pjwXHUwMDAykLU8T+lcdTAwMDBcdTAwMWQ70HNe+zps3qaD3lHnxyNcdTAwMTNcdTAwMGKa7ExWuckyvFjhq4EkRDOJaL9cdTAwMTNu88BcdTAwMDNcdTAwMWIjXHUwMDAwK4a05eLxgjdcdTAwMWFAf1x1MDAxNWWmeVx1MDAxNkgwNdxCbdJSaoJat6nXvaLOx4P1sXs7WLchQrCu01x1MDAwYlI+g6yo3oMzYInfkcRcdTAwMDD8XHUwMDAx+lx1MDAxOcFMNFx1MDAwMIqkqPbFXGa+OalcdTAwMTRyuJxG13+wXG5JqIFzX+zplJ/vINdSs0jGJ1LgwFTJhVx1MDAwNKnt0XJjXHUwMDBl1T5RR3yO1nR1PyGJUthfZoeV0/1cdTAwMTIsXHUwMDBmTpZhykBcdTAwMTiUoTXSXHUwMDFl6tuLvoTr4PhXqmJcdTAwMTjTjs/soVta+pR1blx1MDAwMaY9wnFcdTAwMDDkj8x9O1x1MDAxNjRejc811tuFXHUwMDE4XG7Ym1ZcdTAwMWalxpF8gk4wulx1MDAwNFxcXScvslx1MDAwNEGSvKnekLHqXHUwMDAxRFx1MDAxNJVE6bCiMIQx4TSaM0ZcIq5m+lx1MDAxMsdcdTAwMDe3m5VLlri3lDx/8rKsgH1bXHUwMDBmXHUwMDAzuP6tpbRD8rThTeKfPpNaaHbEXCJbn/cqgkyqXHUwMDEyqURcdTAwMTlcdTAwMTGKXHUwMDA0zM5cdTAwMTdZwMnrSltnrlRcIlxi1vzwop2TSdSunbmAsIBcblx1MDAxMVx1MDAwYqT1tT2hXGJcdTAwMTXOXHUwMDBmh4hcdTAwMDD8XHI0mKajRXNcdTAwMGXwTpWtKEdiXHUwMDE5KLCqQzaKxsloe89akrlcdTAwMTZIcz1cdTAwMTTqKDZwPdTy2NTqXHRcdTAwMDTqXHUwMDE3N+r+YW9cdTAwMTPvwEdHaTi2YNu+XHUwMDA0oL5cdTAwMDNSXHUwMDAyRNonlzGqklx1MDAwMNdTJjviOb26z2RcdTAwMWPf3a6JotsxPPGVXHUwMDFjfFx1MDAxYVJlVOpcdTAwMWW9k9XdsrG+MtXUS60k2F3Cmfd4XHUwMDEzn6qyP4Ux4/Pb3vFdVNZaXCJcdTAwMWaobLPVXHUwMDA2x9SJUnLuXHSZR1x1MDAxNv7FuVHKfI5ZXHUwMDA3pm6uXHUwMDE4+HBD6VV5XHUwMDAxT7P2b1nwu4baXHUwMDE3d1x1MDAxOeMr1cXrI+w2LLUnXHUwMDExKPiDzGtqiHDVkIpcYlJcdTAwMDb7ekG960+yy1x1MDAxNJJcdTAwMDOVXHUwMDEzcds6S34tYu7VvllpYFx1MDAxM3cn1OBcdTAwMDMlXHUwMDFm1p3Dk5j81rxcXHYxXHUwMDE1oGNPzqwu+GTsid9xJ3z/ctZcdTAwMWNo375cdTAwMDE582NcdTAwMTN8PDbWJWVt9Fx1MDAxNzSbXHUwMDAwnVxcLPaENFx0fIK3mbaF6lx1MDAwYohV4tSvXHUwMDEyZyg0gHZcdTAwMWOPZu0juOBA8lx1MDAwNeCqWqvs58Xx37a+jT57OGdcdTAwMWbA46Sk4TK9jIGGcV9dXHUwMDAxT0i0oalcdTAwMTR6Q1x1MDAxMNWB2bPMXHUwMDE4cne5XUPBYnxcdTAwMTh2XHUwMDA1sFx1MDAwZrY5XHUwMDA0ekHzNkH7yIXCeFx1MDAxZVx1MDAxNXBtrnWs1JSRRKLg2NEmxtB8XHUwMDBm5n3b1klcdTAwMTbfbs9UXHUwMDEyxJwpXHUwMDAzLfvBXHUwMDEwlbDIb1tR9vTxMJCTXHUwMDFi/zCYw9qGbjuJjsTOj7ZcdTAwMDT7XHUwMDFiddY9XHUwMDE4a+Qu+pmajVx1MDAwM75kNX+XRe/xYlc2r6J0k5TMXHUwMDBlfe/FXHUwMDBmdFx1MDAwYlLDr2O+fdG76lCYbftcdTAwMGJbXHUwMDAxSq5cdDN3jJXaPD6QXHUwMDA35SGU0tZcboCXwXiKYWr2PZBNSsHX/ZJZ57fcxPM1LNas9lVcdTAwMWHdlF7QUexjXHUwMDE07Fx1MDAwMotzRVx1MDAxOLrDRNJcdTAwMDBcdTAwMTH8ij7N/mZPaf87q3Q+7sD5W5uHhzPuQqWq7u/3XHUwMDBiulx1MDAwZk9BXHUwMDBiXHUwMDFmntN5sa5cdTAwMGVcdTAwMGK4z2q9oPXf1Fx1MDAxZDZBgYVcdTAwMDNccomIupd5oVx1MDAxNFxi9/xcdTAwMDa9ULNjqt1L3EiZ4nz98NfnYc9WXHUwMDFkQuxXXHUwMDE5iPaw1YDaLu5Ezo+BXHUwMDA0nWuRh3jlKo6+qSS8XHUwMDEybSX118ud4mOZPbVN8Vhyw2LHaXfMdm2G2EpCKVx1MDAwZZticqk09kRcdTAwMDaO69bbXHUwMDAytVxuuPUjNMNToc1cdTAwMGX2unNcdTAwMWSD7eO93KfvUVx1MDAwZdc6qiFcdGFcdTAwMDdK77tcdTAwMDX8XHUwMDExQsV1KG1s0j3tXHQ6zkPtqNIkSsQqTINcdTAwMTg0feD6Q+llmUsuXHUwMDAyStTkxVx1MDAwMeSN3eWvNlx1MDAxNdDQw1x1MDAxNid+XHUwMDA1qz3UOFx1MDAwZVx1MDAwMLnTWfZxXHUwMDAzXHUwMDFk5Fx1MDAxM7S0hHtcdTAwMWNWP3pcdTAwMWFbL1CIp9JP1FGmOaCV3jRcdTAwMWLbzpLLZ+/lbPZ0XHUwMDE1XHUwMDAwZlv+cFx1MDAxZJyYa/l584cgzoqNLZaH0LEugoXZu0Sh9u7Js3rY2rLir1GgZWtoSqNkmWb5cUvuwW21dtCmxZ7h2//28GTdiDaZjXeKXHUwMDFmXHUwMDA0+JrRr3HC2aOoq/SRXHUwMDFkI9l2XHUwMDBlXHUwMDBlpqErXHUwMDAwRfrq1K/0buI0x6Ni70CTj1x1MDAxZVn/XHUwMDEwLVx1MDAxNXDTQr9cdTAwMTdBpas5S4Eq1Fs0R7Pqm4SJmlx1MDAxMFx0+/CZSn10XHUwMDEwRlx1MDAwM/iHJmPAgEHhVukkwUNNXHUwMDFiLeGqaFTTXHUwMDA0WVx1MDAxZaj3gjMgk0p9WHmb5NJPv1lcdTAwMTVcdTAwMDVcdTAwMThWXHUwMDE4Rtz0cp5cdTAwMTBD6JlwXHUwMDA2XHUwMDExz6zjRHWoSywhI52Ytyn5YdgkYjIl+75QTqzuKFxyhlx1MDAxNPZMaCZcblxmdlx1MDAxMqyBTFx1MDAxZuxQTX9a69pipJRcdTAwMTZjj/HAYHrRIZ/YdubwfL3M5YhC51x08CxqIZyjlkB3QX5cdTAwMTFcdTAwMDNRMoEnKb+cJUiPIVx1MDAxMIujXHUwMDE29/bN1NGFKuXbeT29xVwiv1x1MDAxYXJaMEO+gbwrbp2VKfJ5LeWrjVxyl/47XHQwTeVcdTAwMGX/6Fx1MDAxNrqy0FxisKHOXHUwMDE434dcdTAwMDFcdTAwMTGCtDH25cC4xjT+z+P3hazN6d9aM23rSlx1MDAxZKJeaD2eylxuRXC6XHTKxk+3a1L1oXnO2lx1MDAxZl5BSk9cdTAwMTjfXGL8XFyPdvlsXHUwMDAxbGnC3EeOKl+s3Z5UrWLvu1x1MDAwMsEtYXvAxkl9XHUwMDEwXHUwMDAzXHUwMDBmh9a3XWVYhvKAi9SzXHUwMDEyYHRcZrTwcIBxnlx1MDAxZH5mOs2KnM5ndFx1MDAwMVNiXHUwMDBmdidh5WKGY3AjXHUwMDA3p2U/XHUwMDFlU1x1MDAxNq3I13nMhVx1MDAwYpmbvvlBylx1MDAwMyDhfHH4aVa2LmzxTcnMUpVtwGb1vapCOlxi3yPUgW2YXHUwMDFk9Xncr20yVVPvm0Z3nN+b7eH5XGZl0d1cZp57Nq1aWW1gi57JXHUwMDEzpcM7gnI2jmItP65aNlnD1rn2rU6qalx1MDAwMOrVdVx1MDAxZjyYXFzgoSEkXHUwMDExPDGNnLvGkNra8eNN4Vx1MDAwNuWDZOnHXVx1MDAxZi5LXHSM/oBfXHUwMDFjhFx1MDAxYle4JVm8lYckZ4rVNW+W+S1cdTAwMTckhJVPrm22XHRSpPPrKypcdTAwMGY+y1x1MDAxZZJcdTAwMTKvg+ZcdTAwMDZ9qFx1MDAwZrt3l3RBXHUwMDFkz1x1MDAwZiVcdTAwMTVcInB9RMZi6LlB4n77hCghm1xub9LgXHUwMDE1VaFcdTAwMWGMXHUwMDE2kVx1MDAxM1x1MDAxYSCGw6n1h1fQXHUwMDFhsFx1MDAxYyX9blx1MDAxNlxiV1x1MDAwMqRQSFx1MDAwM1xu8Sq0zVx1MDAxMlx1MDAwZWxOu6/NN+dITae8ZYuyKDCF2yykXGZUXbNcdTAwMWRonlx1MDAwM1x1MDAwNytweNHRm4mpynxCXHUwMDE4XGKwskOKylx1MDAwMJ5cdTAwMTV41MTtTkJI4KaN4bGBdkNcdTAwMTRDuixsQseffFx1MDAxOH2z3cMzk1x1MDAxMOq2KczBXHUwMDFjXHUwMDAzpFxcq+fdTVx1MDAxY9ewXHUwMDFkeCT4XHUwMDFlRTApXHUwMDA0r+Z5Wk1rj6p6+GEpqsQ7w693/VFZ37FcdTAwMTGp9lx1MDAwNpbHyMzzWCX0pZHE4lxcqDN4elx1MDAxMUGcy1u5XHUwMDA0XHUwMDE3kFgoXHI0mV7qXCLH3FxuRofwXGanTZBtyEOejJRuucMsV26CSDI1ilwil0FruFx1MDAwM72P8Xxcclx1MDAxZGXoX10h/+qPvDi78D+tzDJq3G1XXdSYi580biOLZXGgIURcdTAwMDKhv1x1MDAwZiiPTO2uv1x1MDAxMEdcdTAwMWHhnD9cdTAwMDBcdTAwMDAuvMPIXHUwMDEysWSKYOAs/1FjcPrsXHUwMDFkXCKiJZ4ySiYymERuXHUwMDFls1x1MDAwNHqSXHUwMDFjt6KYY2L6WKlcdTAwMDOVroKSxswr/rWlm1x1MDAxZVxmQaH2XHUwMDEzIy9cdTAwMWVWeTQjZ1x1MDAwNVxuQnSqvlR3jYYlYFx1MDAxZjdzMMxcdTAwMTdv+Fx1MDAxMVx1MDAwMFx1MDAxM0qnZGxIXFwzvfwqZMfDcDdcdTAwMWZ4J8NLXHUwMDBitLGqxmesXHUwMDE0ndWrXHUwMDAzuFE9hn240/NcdTAwMTVcdTAwMDX5QtOiXHUwMDA320TD39tcdTAwMTW9XHUwMDEyZK4p8FxmlT6oPMo9Mq5jNo/SzDJcdTAwMDZYIcpYN15DI+mNtlx1MDAxY67sXHUwMDFl0a4vTT+XzNfSofC2cVx1MDAxNV7ChfLqnFxutvZcdTAwMWTBsG3m+lx1MDAwMWYvMYpcIvJ32uCi21x1MDAwMoXaNiAwjfIptO/pMe83/Fx1MDAxNbWqd1xue1x1MDAxZEPnxPFJZFx1MDAwN56BaeBwnJpy6N8592eOXHUwMDFmXHUwMDEwh/P3avU9XGbswNFiXHUwMDE4XHUwMDA2XHUwMDA2diSk+Znbf3VFSzalzSrJVTb/eYRPU/he8jfRcXt8f6CHXHUwMDE0rL99UbwrsnM0ZJmzXHUwMDFl/IQ1rXBlyFx1MDAxYlx1MDAxNFx1MDAwMMiG5uMyWENIeOlcdTAwMTIslav35SmP4lx1MDAxM3ZiXHUwMDFkQceaXG5cdTAwMTJngpmU1XlDP7zz+rinPlx1MDAwMFwi+XqPXHUwMDA3t6tqUGrs+1FcdTAwMTWDXHUwMDE3KZekLlx1MDAwN/T+yJZmPFx1MDAwMeZO9pvOxlxcffh6+Vx1MDAxMa5cdTAwMTAkXHUwMDBmh7aXUKz2yzPGXHUwMDExqVx1MDAwM1x1MDAwN95cdTAwMGZcdTAwMTNzTqMnddOUJ0wmLVx1MDAxMlx1MDAxNfH3dIBIqVwiOdwqiZFcdTAwMTKqNrJcdTAwMWTTRYmy3Scsslxij1x1MDAxOPZEXHUwMDE16uHBfK1cdTAwMWbBa8e+eZ9OpnXicIAperXgn1winSyLrJROXHUwMDE09/U8Qm1eO7L4+N4jr1Znl7A5UIIpeaAq1XqfXHUwMDAyNr1PXvZC2n44I1x1MDAwMGk+cST4SL0mv1LtzdbQQ25W7Va7aoE+SKV6UVx1MDAxMulwMTtcdTAwMWY4TspuLzVhd6aorUG3P5hS2JvvSp76PEr00EdcdTAwMWLM8bFwm1x1MDAwMEHdxMgxoFDe1Upd5Vbn91x1MDAwN1x1MDAwMOdcdTAwMTSvXHUwMDE5jTPUrLF5g2FMQeZ4mDPR8ZpYWchIkN66Z5aJvINcdTAwMTJcdTAwMGaDvlx1MDAxNIvYXHUwMDEwclx1MDAwM1x1MDAxOIj4uDwrjX1cdTAwMWZcdTAwMTTdr1x1MDAxNrFcdTAwMTORIOTn1ZWhKDrvszJGOyp0XG7L7Vx1MDAwMG5cdTAwMTFgaj9DXHUwMDAx07E398IpXbdcdTAwMWHcuaNcZqd2b2GB1VV4VCvLslLqN5SdYuaNNFx1MDAxNTGtt8rrw4p4JiaYJ2DEL8ZcdTAwMTCxXCJNXCJ0XCJ73p5cIi+TXHUwMDE0eIFbXHUwMDFjM1GuTouwqH1tzFPjM1x1MDAwN1x1MDAwNXK98bx1KnhcdTAwMDB1P67YtoKrQ10+b1wiiWyRqyvLZ/xIwlKxzLPpl5Rqy1c/glx1MDAxYoc/LUpqQXxsJkJcdTAwMThcdTAwMWRSPtxyV95cdTAwMDYzv5BhoNCUWF/ljVxy3JfxclxmTjH6uVx1MDAxY5CFWlfZQ4OLmVJ8XHUwMDEwXHUwMDAxXHUwMDEzdVx1MDAxM+ZcdTAwMGWDXHUwMDAxTXIqoeRcdTAwMTHemTdcdTAwMTVO8GxcdTAwMDXpXHUwMDAwXHUwMDA22C48djRzun6Y1mbQ0kcwz3xcdTAwMDNrtzXqXHUwMDAx8jkoOk0k4va0gCvU70OSXGLfkaNcdTAwMTSXXHUwMDBlXHUwMDAxsiMzOld8uEj05XG4I3Ln1ypcdTAwMWLJSJHBXHUwMDAzq0/x6Kf+6dhcdTAwMWTKY2q4XHUwMDEyo23Qzbs5+c1zmX/UOj/1+sNcdTAwMTn2vsrWeFx1MDAwZUhDbjfEnJyRmcLb5zKsh8ErqaBcdTAwMDGFRj9J+Ncng18jirC+XHUwMDA2cyF+Z3q/mGnYRzG9XHUwMDEwOUZ0XHRZr1Sldo42guTOXvH3hozSQX/7XHUwMDAzVS2d75ZpVpEszH2M19SKxFx1MDAwMIbhIFBV6Y0ofN5uX165n/DQMo7FZF+ZYMCg2zg7lyGKs1xub7aE2/NfXGaAMbe8v4JG3rWOTDaEWPhMcWUnxPiDzbHnXHUwMDFlyTpE7vNf01XNnJN/XHUwMDFiye/09MO3cIKyY0hjy8uRNSTpW1xii3zPK+31XuVcIiqvXHUwMDEwXHUwMDFkU8tHXHUwMDEyXG7fJlx1MDAwM3JcdTAwMTZOXHUwMDFiMqtcdTAwMTH8kYRJ14sod4tikFx1MDAxY1x1MDAwZvyVrpTpinQsilx1MDAwYliSXHUwMDE03PLXw3lcdTAwMWaSUiuwjpPq62vXXzybeFErtCg+L4VCKVrwXHUwMDAwe79S7ysvN7j6flm5yqu3XHUwMDAzOLtrXHUwMDAxsJKM373TQdlm2+lb84k+NKNcdIe8PuBY6lqQooyT4my/vnpcdTAwMTlcdTAwMTJurXqMd1x1MDAwZn88L7zMi2XRXHUwMDA3SLtcbi2WXHUwMDA0On1LXHUwMDE04uHqlGM5XHUwMDAyXHUwMDFhXHUwMDAzlGnzXHUwMDA1ilx1MDAwZtP6JmzMvr2htreHMFx1MDAwNON8ioVV3vdcdTAwMTXLMqXncT26OHpcdTAwMTlcdTAwMWZDcPW3zmUxtmFcdTAwMTI1eFx1MDAwMrGrz4xgkKCJ6fhLXHUwMDExa4EzXHUwMDExJFx1MDAxMnUns1x1MDAxZnv1tO/HYzaMZ9ls+5VQ7d55VVx0r1x1MDAwNlx1MDAwM0z6bu1fTYWyNHlBKFW/L331W9jp6/PNrGiG+PesNPEokFv+XGKiRzfyLzYtbKzQiZsu78Sn8Vx1MDAwN6ZcdTAwMTBcdTAwMTBOSO5k3bxe//knL+5cdTAwMTWjPmndSsWX6uEvdZBcdTAwMTM+rLN0qL5qXHUwMDAwdlx1MDAxZm3nlYuvTSTWXHUwMDE0nP3ozS0/3Nin8IegXHUwMDA2d1xipJ8zxIepXHUwMDBiXHUwMDAwkNKFyeyTULlIYa0wst0whFx1MDAwYnixLSDgdZ7iI66eKZslOlx1MDAwNUyX3TeZ8yEnR3xcdTAwMTSBT/vv1Vx1MDAxYVx1MDAwYlx1MDAxMX37XGbDMYzrO2wgLbn64LuT43P4YFx1MDAxYtLTwPptXHUwMDFiXGazttfTTVx1MDAxZlxmeOTV5Vx1MDAwZcPsTVx1MDAxZJRDyzWtUCt3XHUwMDBiXHUwMDFkMVfekXSXKdGBmpdPIE1viP3ehaVONTFcblx1MDAxN0S10CBIpXhcYoIjf2a9UyncPO9i+4hDNM5cdTAwMTOeXHUwMDA2kPOiXHUwMDE0Vr7LyUyLzFejYFx1MDAxMW6YhyxTKcGUJIVw7DvfxT5WW1Vu7Fx1MDAwNJrrmEqj8Vx1MDAxMVdcdTAwMWOe4dVOhai1aMRHtS4l9jq60UJcdTAwMDB0tsrdXHLN0Xj1XHUwMDFht6ZmbUl6ppZ5mU2CN9OuyYLdtnu9SVx1MDAwNENcdTAwMTE/tFx1MDAxN4tmsaMzWJ3qzsmM0ZzveYRDWpNPXG71fV5MXHUwMDExac1LleHlt29Kr3nMfJOXsbdcdTAwMTj0y90rUruYl97aOGS3vtViL4FJUn/wkvlDcUn1svefPqJcdTAwMDNs8Mwxmmf9+K4wQlx1MDAxNuvAMt/Kv1x1MDAwMY6mLCdnX1x1MDAxZjTqKG5yhZ9cdTAwMDUtLZcr+jMpc6r+u1CzQ1x1MDAwZV3+3yXtaz/E4pe69M/TR/yMaCmfXHT/WaH/+T15Z5uz/vzPt0//ebY1q38h7j+P72/vkovvUM61/zy+8p/Hd+Zv6J+v+83/XHUwMDFixyfL5P95+OL3T+izw3q+RPef97v77Vx1MDAwMX1Cklxcxz9fosc1P1+l0CRcdTAwMWSu13/Pk/5cdFx1MDAxZr6Oof980mz38601/EXmdPznJ8T+vILawXz//+VcdTAwMTX89lx1MDAxMyO99lKZwIdcdHhSuz9cdTAwMDPezcfE36BeNo7/zZ1gXHUwMDE5XHUwMDFl25VcdTAwMWLPXHUwMDE3raBav71wvyBcdTAwMWbiiyW5RbJcdTAwMDL/QpRJrMLONaRcdTAwMGb2aKs7L0z0obcrUC1cdTAwMDBcdTAwMDGSy7JM/DM+O1x0PZ3/zX/g5PNcdTAwMTUyu7pcdTAwMTV3pi3fwmS7lFx1MDAwZnD9n3exendcdTAwMWTLVTLQJcsnXHUwMDA0fdvjf+dGyJ9BaTI7/Z98XHUwMDEx01x1MDAxMWRcdTAwMGZcdTAwMDKiX1x08pkoitJcdTAwMTRcdTAwMDVoUfpcdTAwMGJnwKeJ+qtX4bR/5now595cXHk07E/yMSxglDXVKjry1V1gYVx1MDAxOdRWVUC5SHkm4vFSRpTcMOVeXHT/vL1swppam1x1MDAxM05En+hcdTAwMDVcdTAwMWWuWdey3YW+cE/xX3+3vM804Fx1MDAwNVTVLlZcdTAwMDJcdTAwMTMoXHLz7VxiODOoh3S7/eyvprQoeuh5pVTJ2v/4fJs6Zpz84tZgXjGJOa02cdDO0n+//lxuXHUwMDFm6VdHWHs0fWHfjzz6NLaMhNK/XHUwMDFjXHUwMDEx5mTl70eWXHUwMDE2hOR+NajLQ3ywTn3/629ZXHUwMDE2vEdUKuOrv2H9S1x1MDAxNuWnu5NcdTAwMTV93Z3w//o9Ss1HnnQwXHUwMDEzrf685rvt5Vx1MDAxZNE4NZ2uYWba31+u6Kt3i+795+NcdTAwMTZ+OZRcdTAwMGb07JY5/X2PyHTPmOI+3dPqm4rlpWm/MIqTd0dGXFz+n4+U+bLPvZ9fXHUwMDEy4z+vKedwsou+JW7tUzhrn7Zcblx1MDAxMz+gXHUwMDAw4VXhfuhVXHUwMDE0wo+AWf/zXHUwMDE0607OTI9cdTAwMWX5UFky33w8YdTHVnqTflxiw/pcdTAwMWJcdTAwMGJcIsvI2Vd0gIfKoEncQEiJ5nYnXHUwMDA3XHUwMDAzr77hMvSvZ27Mtcx05EhUmr0zvM+zwcS+iOxfPJvj37xhnZ1pr/y/VFx1MDAxNfGnvb3Nq8P/UkwyZlx1MDAxMGh+iWxcdTAwMDbmkexcdTAwMGKTP79+I/xZWZ1cdTAwMTSdzPFf36q/XHUwMDBilOj/Z7fXbC9mOLvN5+i/Noz9/YlcdTAwMTHK6uW78l+OxuevhpaCTeVl/91//Xf/7PTq8u/+c838ed/spmqvfyk375P7fUaWvf9N3Xn9QqpcdTAwMGU3Svz3PVx1MDAxY7s2wfNqXCJSXHUwMDE5XHUwMDFk+V/+17+6XHUwMDFjL3lUIP3veew/b/5bXHUwMDE1kf+8+fkvv4N3hihcdTAwMWJcdTAwMTf+X97QL7GCNSjp/zNPRfuX61x1MDAwMC9uLv6LXHUwMDFm/P/N+fznvdbZleXezVx1MDAxZMZ3+5frIPPqr06aXHUwMDFmS18gfP/7XGbz/n3mJcra679cdTAwMWOb+29DVTZcdTAwMDOTj/+lO1xi619ccoaHxNH/POVs/OdB5LY7b+V/z8PavzH3kFxmfvh75k+t/9qvXHUwMDBie/SePn9fjP7yiiyW/p9cXFx1MDAxZYhHXHUwMDE4ppNRtcfl/3KRtF8uUvZcYq7mL2eNZYh/uUilXHUwMDFh/TeVM/1vXHJzoHwsqn/9rX7sXHUwMDFmtLC+PGLU8PeZ+F9ec8Px2Iv9s19//1x1MDAxZrVcdTAwMTL+w0f5t17tbOOdmP/whv/Zglx1MDAxYo25lKt4/T1Px//uJXP1djzK5Je3I/+rUdkjOHH+xdfvL1x02LeTg6FcdTAwMTKxSJojmLfXZzBe1VmDOFx1MDAwMV0qRlx1MDAwN0NPRiNu0+BGxGOK+KB2Z45cdTAwMWPggVx1MDAwN7LG4UJcdTAwMTNyJcHuby9cZvVw2eXQQ7kyQY0mQr6T+NA9MYy19YAj2Jwg/9Jh1Go/292svlx1MDAwYr5OzUhe4EGPXHUwMDBm5ZzCft9Apu4sU5bH81x1MDAwM5Lu8ElcdTAwMGL4amEyS63sXZJIj2xcdTAwMWREMzu1gD195EBeSCVk7zB6jHFcdTAwMTR9XHUwMDAzQmq3tqNEcTz1nJxcIryYYPdINlx1MDAxZW57joYw0TJcdTAwMTdt/8icrElrlmSxWUPmtXGgXHKetFx1MDAxZlx0V1x1MDAxZXI3XHUwMDAxLniXXGapXG7BI1x1MDAwNjpdIbkwsl3AXHUwMDA0Wid15aRJt2Mxq+/P11x1MDAwM4BwwPdl56pYOYxkMuPhUdrZXHUwMDE3VKqS0e5cdF/tTcbNb0f+wa9cdTAwMTdPQvc6XHUwMDEzwiHJLFJTL5nQ1lGHtrzdR0ZcYvvoRKkjQOhVyXd7TnCYbuts+1dn7ilLj+pZXHUwMDEy/cTXs1JcdTAwMTQp7Fx1MDAxZFx1MDAwN7T7J4FcdTAwMTmwUFx1MDAwZXNccnZlXHUwMDBiZNJcdTAwMTVt91x1MDAxYlxmUJK0gmqGsd2Nkr3hzkdMY1x1MDAwNHLt2zHlY3CUSoc2J1x1MDAxZIFcXD0oXHUwMDA1KaHbo5n2O2jG2cEqROn2XHUwMDAy+Vx1MDAxNe/vXHUwMDFjXHUwMDA2XGbRNJ1MRCDCuVx1MDAwNDGab4AxKFx1MDAxMlx1MDAxNED1XHUwMDAzQTxcdTAwMWQj+maD146G3fzG1y9cbuGFXHUwMDBluMdcdTAwMDGaXHUwMDFkMfVI+FxileNl01x1MDAxN1xi5DdCeFxueSC43jaAP7qfqfeINtB68KQm9Ma8Scstw0NcdTAwMDGgwzs2XFw+hHPTsDWfxFx1MDAxZSr6JEhVXGJcdTAwMDbf3ESG6GfyTYzGKPLR1I7dhFMoUlx1MDAwZddqcS2rX/Pgk9phuVx1MDAwNP29XHUwMDBlJVx1MDAxOCpccpOx8YJNi1x1MDAxOFx01OKWU/3lXHJ/o/KZie81yiZ/XHUwMDFmsy3cPSdCQXqrvFx1MDAxZC2OI2ewX1xybENs7U++IZ2bWFx1MDAxZFx1MDAxNj+q9mhcdTAwMTPSXHUwMDE47iR/XHUwMDA0iUeS756bgnRcdTAwMGZ/Z1VcdTAwMDRcdTAwMDKMfr9cdTAwMTDKMa5cdTAwMGVPj7bqusRCXHRcbiUlbN9cdTAwMWYhQVx1MDAxMC5rryErwaAq4is0u1xuaVx1MDAwMGBE41x1MDAwNHEhv4LMpL3xPfCt23/jJtldW+MoWIpcdTAwMWZcdTAwMDJE6Tw/tEuBR8VcdNAxXFxcdTAwMDBv/KvF7EnmQ9u4/v5h1MRt3vypU2RWqPNcdTAwMTFubKslfZapkLWWcKyGjCpcdTAwMTLG+ny3g9FcdK6qu1x1MDAxOWlfXHUwMDFhvqZevIKpoPrLXHUwMDFhNpBcdTAwMDOBXHUwMDAwhLtcdTAwMGVcIqJcdFpewPPLXG7ddWRcdTAwMWZcdTAwMGZ0zaeH5Si9iFx1MDAxN1x1MDAxOOKdMLhcYs+AQEPRm1mgv4L10etVcfdcInFcdTAwMDIg7Fx1MDAwZaC3j1x1MDAxN5kgcaiBm7kgnH1fV5eIsVx1MDAwZs6wTrojiVx1MDAxOX2aXHUwMDFhllxmaL+zOzSMfS9A+GBcdTAwMWZWLN/7mU4708iI0Se4jMWGeMVVZXHsM9aqsvSDsuRY9rJPPeBcdTAwMWY1o/nauL+ZVbvlf17aJ/4nyp18YDilXHUwMDFl7XGlb6fkxb9cdTAwMWM8JoNcdTAwMDWYsaxcdTAwMTR02Fx1MDAxZVx1MDAxYt0950L+U53XeYmUJEXa28DFXHUwMDAzXHUwMDFmymmMduiaopXa0Cgv1DorRIGvnt70Ukfx6OHKYPTMXHUwMDA3TE+lLLBcdTAwMTHh0dnytUFWI31cdFx1MDAxNV7FyMlcdTAwMTdcdTAwMWbGppc01G/uXHL+zrxR01x1MDAwMlwid8JcdTAwMWXRonPhK1x1MDAwZpzMbPDKXHUwMDE0gI1cZlx1MDAxMFx1MDAwMkBdzZvmStNcdTAwMDHdw1x1MDAwYslZxlx1MDAxNVx1MDAxOMIhXHUwMDEyXHUwMDEx2uCTw5A/XHUwMDE1cnaHIVx1MDAxZVx1MDAwNFx1MDAxZL3f1bzx31x1MDAwMFwin1x1MDAxZSgsa0o/XHUwMDAwjFxiPYCuxjReN1x04v5M75txXCIuaeu7XHUwMDExT8lcdTAwMWR8uU5cdTAwMDXggWRcdTAwMTLKbzKINDrNgyougIz5UvtmXHUwMDFlXHUwMDA1h/JY35VcdTAwMGbqpFxuPmd9j8iBZ4SxxldwXGZcdTAwMDCowqg8pP466368NKE9sZmXx7GFl/5OfnvkXHUwMDFmX5detX1cdTAwMWNtw0FcIuZ3XHUwMDFkh1x1MDAxZM8/PE+N8ONcdTAwMTdyZE8oStLvUaRM07KoZUezLTBcdTAwMDX3YedwdyVyr37u+b5BUOB+h7P4Z0xlMmiNd19cdTAwMTXNPX3xaFx1MDAwNthd7X9lXHSEYYLKhlmEjJwlXHUwMDFm4nhE9HZm0FScs1x1MDAxMzPXyKzpOeefP8ZqKcCmmUuKpeJRtZ/PgvBbqDaSOjrKgGBcdTAwMWZW9sKerM7f2Vx1MDAxMi32yzPrgiq5rnhcZlx1MDAwN4dcdTAwMGaSRCd1x+zpKVx1MDAwMi1cdTAwMDaKJSyQlkX8XHUwMDA2+uGR4++Eklx1MDAwMu0tf7FWfjbYenCHPbVcdTAwMWaYXHUwMDE5rthCgVx1MDAxZDwugt7yXHUwMDFkhed88uj9MFx1MDAxNbG3b5CiSyONXHUwMDE14r5QfntcdTAwMDL+tvQuXHUwMDA0nk98VjZCeFFqWz5cdTAwMWSsTkPyqrtT48XheFjMb5FG29DC919cdTAwMWZn9TVcdTAwMWJqmchn9fHGi/qNXHUwMDE187hI74N03Vx1MDAwYoRcdTAwMDHXXkmT799izVxmhvq2Y4viPvWr/uLK6E1wZaVcbqXgKDr2XHUwMDEy6OC1832D6jw+2sk0qLVcdTAwMWJHqFlcdTAwMWVB0Vx1MDAxMXS6yantXCIz9tPnmk6f5Fx1MDAwN2PkNY5g7t1ua0Hta/dAQTM2udzmRnuy6DhcdTAwMTYgglx1MDAxZqpcdTAwMWPn7luqM+/r86J/yFx1MDAxY/uE5W3usr5KsDZ43saBRVx1MDAxN8eiXHUwMDA1NG0wvVxmXHUwMDFjOy9cdTAwMTK1XHUwMDE0poFB91x1MDAxMXD0KNTosVx1MDAxYetGbVx1MDAxMlT8+SjPNepNSpVfI2l9LD7pblxy0vfdrc2+XHUwMDAxQ1x0TCAo9M0w03dcdTAwMTedoFx1MDAxNi+FXHUwMDFj2oCVKqxNnz2eljtljefqXHUwMDA0zIeK8p9cdTAwMTWo2Uv20e9cclx1MDAwMI++oXRAXHUwMDBlMLd8P+FcYtFOfD1l+zdYu/FcdTAwMTnPQVFWVVx1MDAxMIxfmvr5+vFcIskyPMJcdTAwMDCp7ZW09udAnVjfUd3PotZQrbm4lWUkXaDjQ1x1MDAwN351VJQ9t5xf7ZvgoaGQP8svXHUwMDA2XHUwMDAzXHUwMDAwXGa+luNQgMHuMiSTt1xic7Z0uH9O29Js+juGXFw+KppcdTAwMDYw0Vx1MDAwM3uR24qYl39cdTAwMDf7L6+hspEl2Pj5nuo40TdmuvxeWKgqnrpcdTAwMTNcdTAwMGWKXHUwMDE2j4U5JFxyXCKTcehoJ6O0XHUwMDAyJFB8jLxdXHUwMDExRc2GYKFhi1CsnyfNrKKqn1MvleAqTlxuI+y8XHUwMDA30PVl0chhXHUwMDA3gJjcgbtUUVx1MDAwNcmJXCJHMvhcdTAwMGLTvWerzFx1MDAwNSdaL2xcdTAwMDPmhj9zbVx1MDAwZaWI6lx1MDAxOHuR4YViXHUwMDFltdEm1HVdmdRcbsuI2XNcdTAwMDFlZkE76EFq87pcdTAwMDJcdTAwMWI2X3nXbF/41lx1MDAxMsyBSVx1MDAxYTn8XGJcdTAwMDC3te5g3PRv+mVcbkPvzoSQXHUwMDFi+f5qXHUwMDFmbDLl9Vx1MDAxYla08tlXXHUwMDE0aVx1MDAwMq04rt6rPiSbhHvKvSDggNGeKDSPzvCHYT7PXHUwMDA2iU5oOMaMrOn0lfdrre+PZI/boct4XHUwMDE0QZdbXHUwMDAyoL9cdTAwMDH70WZcdTAwMWJ8oneytXB2XHUwMDFj3Vx1MDAxNUbTgz5cdTAwMGWmXHTw3lx1MDAxM1x1MDAxOFx1MDAxZFVcdTAwMWRq3vFvaFx1MDAxYVx1MDAxNbN65UudXHKqXHUwMDBlTfs+jqnrNproqvNcdTAwMWW3XHUwMDE1iEhvv4/4XHUwMDEzeyRcdTAwMGVKmqTg3LDfVEZcdTAwMTfY4c/7YPeCu3hcdTAwMTbKJEWJolx1MDAxNOI+vETzsy5Ds51A92FO4KU7V6lUXHRrRUUzop3teZVcdTAwMTO+XHUwMDA0XHUwMDFhNFx1MDAwMS/PgfOqmp3KwMHlTbDgXHUwMDFmqX9AfliJq4fzZFlcdTAwMDR7yXVcdTAwMTCvot+d0nBcXIVcdTAwMWGGUjFjeDg6stzBJ3asizQ3aCFcZpueUdgpZmFXjzlQYOdcdP3qeFGm/+bGt9bMXHUwMDA0XHUwMDAw+N2oXHUwMDE15Vx1MDAxMVxurqNcdTAwMDDEXCL+zsTTwHFcdTAwMThcdTAwMTCgq0BcdTAwMTC43iZ7dVx1MDAwN2BPLVW6zLeO133v2nBcdTAwMTG6XHUwMDE2hnFysWdNnTuBwIRf6UZMM9ItvKonpFNcdTAwMTEuYVBG4/lvXHJS+eUuX+A5RCBFxcWKLI5iXGZkPvPqjsnBXHUwMDA0z1fy+pVu4KaI7kn2i1NSvFfmWGlFQVx1MDAwM9OvXHUwMDE47Dc5XsLS/eVSZtb2ZnX3xNBkgjjF0HKmLD3WUVo82NVcdTAwMDbb1e1cdTAwMTXM4NONU2RcdTAwMWR8v983UYu7g9ODXVvL6n87oUFcdTAwMTZcbtxpMJOUXHUwMDAxzdUuoWhSbFx1MDAwYo6a94XeM6r2slx0uulcdTAwMDDbzW1cdTAwMWRcbu3z/pZ9Yy7wUs9cdTAwMWSn5cTGlvBX2+p+XHUwMDEzplx1MDAxNdQ7Zf/5XHUwMDAwJfK0VVx0zVmq+PIz/CVVoVxigENYZzaD0H47o9XYtaX15XBFXHUwMDEzffQwXFzr1rikNWipXHUwMDA2JPV41/BcdTAwMDIn/KBoip+HXHUwMDE3l+pKUeSpmVK9oJ2mYcxAgFx0rlx1MDAwM6Up01QwgMjR4JlcdTAwMWaRdlBbXHUwMDEzScc1T1MtsdzWRZWMv3OPNGuvSlx1MDAxMVx1MDAwMmSDXHUwMDBlzFxy8LCB+qRcdTAwMTbbilU62iiM/kJVQ6F2J1x1MDAwNuNcckE1Y/9AXHUwMDEwUXREsI9hg+esOZzqQb5RnP2i1j1J1Vx1MDAwYojk//FccqAgXHUwMDAyXHUwMDAyuGBky7pcdTAwMDRlUElcdTAwMTb1XGKW+kuHncyXo1x1MDAxMyCeQ59cdTAwMGaOipz3So+mkuMvUC4tXHJcdTAwMTnhXFyA+UYgdFx1MDAxOXB2cUuzXHUwMDFjVzrQaK+UzFx1MDAxZm3gKb89XHUwMDFht1x1MDAwMoDTj7zl1SFcdTAwMGYha+fpPPdcdTAwMGVcclx1MDAxN+yKbK/qrmMylpVcdTAwMTYgKj6f+Lm4XHUwMDA0u7xSjYTb9X6enSVcdTAwMDEwXGI8tEn0z2LMfVx1MDAwNN+/hVx1MDAxNbWZXmV6XFzrwkxcdTAwMTjHNEFw3+MoWT6mKVq2ib1HXHUwMDFh4jSnvbuFU9BHL1x1MDAxYVx0y2p2XHUwMDFlYGFcdTAwMDaZojDSu9A9I0k8Ocbv90tro1x1MDAxNH+Zt1x1MDAxNnRcdTAwMWNcdTAwMDGCkspcdTAwMGWWXHUwMDE0/s6h/LTf6lLQb8ZcdTAwMDEgqMlcdTAwMTRgcq9oXHUwMDA3XHUwMDE2f1x1MDAwZVXLQiHoVi5cXK+b46JcdTAwMTVbPdPNX1xi2uxaXHUwMDA1XHUwMDAyyp6Nt3Btudreg1I0tei39sCJSOUxXHUwMDFj01lcdTAwMWY0oLtvLutpXHUwMDA074t447PHdqdSMqHlvHrhTmjcfS9WIEih2bvII8dcdTAwMWJEUlx1MDAxMG2i6idcdTAwMWXqzzuji1hD3VXK7ELFVUw3ZZW76HXgV/Q5YzjLXHUwMDAxq/PXI49cdTAwMDdRp4hHl4H7w7pcdTAwMTbObnm4KD9SXHUwMDEzSq89l4U+Llx1MDAxMKhSylx1MDAxNN5YeyOqr9rcUXfmR2NVQpDST9tju+d6T4Dc8Uw+V8hcdTAwMWM8y9p8VkCtbzReiXLcuCBcdTAwMTDjb13nmZvIVWj9y5tcYuiyXHUwMDBlL5JcdTAwMGZcckFopVftXHUwMDBlpq+DXGL1XHRbYVx1MDAxY0TMpWWbZOrbcK67voepUFx1MDAwYqB9zZfTgsJQW/egT7c7ML/09Vx1MDAxOfboXHUwMDE0XCKP996S7GtcdTAwMWG1XHUwMDA1N8eH5VHqXHUwMDFkRVx1MDAxNJm4oCVcYotcdTAwMTP6PVxyeDeldVx1MDAwYihcdTAwMTjAVME0Pp7NPfdDs0iLKpc8c1x1MDAxZCiaSktcdTAwMWKMP6yuypMkQq3us+ilkPCIXCJnn1x1MDAxOKPwUPpgvlx1MDAxOXnU2/ZIWtdcdTAwMTjbYlx1MDAxYXRcdTAwMDZn3Hm94lx1MDAwM8Sv1vKmIaB1yGqJQSEpXHUwMDBm8Fx1MDAxMlx1MDAwMb5/R4lFj77DWvhcZqijaThcdTAwMTipPo9cdTAwMTbSJFxyROOAaifg68aViZRcdTAwMTN9Llx1MDAxYu0lXHUwMDE09sPJKJ1LXCJxjFx1MDAxML8vOpZzoFxmXHUwMDE5XHUwMDE3qM3qNSnSOLyJ1z5x9ltOYZc0x5jySChcdTAwMDKEXHUwMDBiykJ9XHUwMDAxrPtccm79/UE8u01cdTAwMGK/XHUwMDE0NThcdTAwMWSz6YFjUVtcdTAwMGVfOUBcdTAwMWHH7f6RkE/HfvLeqVx1MDAwMbzukTDLXHUwMDFib6xKV27A9SFZ7VtuYtGlZNvmLcbnhYONL1FBn0ArSb2pxSuIXHUwMDAwZVmptMjk8ypHlNKYp83FK1BahXol/tFfyZsqM1VcdTAwMTefbj3ZTlRcdTAwMWLmXHUwMDFijO+4ep6huKNBJVxi7JlcdTAwMTLJr3aTXT9zZLYp6e7wp800LavkXHUwMDEyXHUwMDAw6OBpy4vp2T1YuzKqVkTg91x1MDAxZPheZlXlOfLIlFx1MDAwNpo552w4XHUwMDEyJFx01bRwNc2yZrqWi2LfkYpTr+ytMTBcdTAwMTZcdTAwMWQvkOeUtfOR395cdTAwMGZPv+nQfj3jyU0sS1x0567rbvDqqDwnordZXHUwMDEz/UNVtXV1SZHZXHUwMDA3Qafw406EZ1x1MDAxOFx1MDAxN/2VjkKWaFx1MDAwYri1XHJcdTAwMWOCW1C9PvYgv2xKu5FIYcD8Ia1cdTAwMTEyXHUwMDAxzNeC41lDwaBXT0C6V6Sn4TawhK9RfrK+7C1cdTAwMWFtkc+jnI05ppPvK69cdTAwMTh7frP9LKn1Rl++Kj5B7/gym0BcdTAwMDMr1FozavQgQeHRYMFcdTAwMTFerz1WSdNcXN9ii1x1MDAwM1x1MDAxYTTsXHUwMDA3QX7uw1x1MDAwN0F47mkywqnNIHq0Zjg4YZ7wXHUwMDE0TlLG8IGMJFx1MDAwMsdV1rVcdTAwMWadXHUwMDE1olx1MDAwMnjDL9dcdTAwMTneyK14libbXHUwMDA1qkDLXHUwMDE2blx1MDAwNM192ygtyFx1MDAxNFx1MDAwZoRka1onRcJfXs1cdTAwMDbNrLD3n666V3T23/FAXHUwMDEzXHUwMDE23P7naf7uNGdxZVFifFx1MDAxY+6eV1x1MDAxN1x0rnzDPfvwdYLlzYuMmvxcdTAwMDaNTt+Qh1x1MDAxYjRccoxcdTAwMDTv0IU+VZVcdTAwMDZiqUh1XHUwMDBmN6ehssowMZ18MyhHU3DUXHUwMDAxrlx1MDAxOWLL/cbLNixcdTAwMWZ+WtL++L5X6Vx1MDAwMr2pnTUpbeROvVx1MDAxNmBcdTAwMGY/RN9FxadcdHpcdTAwMGaMXHUwMDBiXHUwMDA3iSe8WMJcdTAwMDCWmEBcYlx1MDAxM6M/XFzbpbi4PjEqU3yIopVcdTAwMDdcdTAwMTOqLFtcdTAwMTFNbFx1MDAxMVx1MDAxM41cIlx1MDAxNHiw93qnm5XvKdbN9K+W41x1MDAxYl75V7WK4otrRoLMqmqSXHUwMDFlitTwXHUwMDAxL7RxXHUwMDA13ueJzJKEyCzma8+L7zczXGLZ8Cr53khcYnq/poOxilx1MDAwN4u9M14j4dwjfIJlt5vc+Fx1MDAwNmna0J/pyVx1MDAwZrLCvt6zd1x1MDAxZdqXOivJ825qy1x1MDAwZkxA6UX+q4FcdTAwMTBqXHUwMDE5e63vaEQ97chp3JiVXCLNJFx1MDAwMbvVXHUwMDFlYz9cdTAwMGaPiuDqXHUwMDA0y0BpOlx1MDAwN1x1MDAwZWlggFx1MDAxZVxyVlx1MDAxNTto9DEwPNyGqTo/MmNebjSCxpBof1x1MDAwMIYlXHUwMDFj3n7RQKUo+lBcbkBcdTAwMTfXUvDzalwiu4SO/IZzzHtcdTAwMGKdulx1MDAxY39GIOl+tKWpj8ZcdTAwMTOYo7+6XGJFgEdds26F7Fx1MDAwZXJGgJ5cIlx1MDAxNHjIZ0Xvct5cYpZcdTAwMDCDUd8tTTeod/pcdTAwMGbCXGIsm65QIYRCuYSfXHUwMDE0Xlx1MDAwZV3M5YM02YcvYTjmXHUwMDEzUdBcdTAwMWb69zAjJzLYh/BcdTAwMWOv6Lu/31xmXHUwMDAzXHUwMDFlXHKtwpD1WytcdTAwMTW/x3GmS3NmVEVLRbZcdTAwMTW1XHUwMDFlXHUwMDAwILI1SciCnY9FQItcdTAwMTVcYlx1MDAwZTC5XHUwMDBlLuGLXHUwMDE5w5ScMVx1MDAwZjlcdTAwMTi0MyRvXHUwMDE5LyDdkDx0v1x1MDAxYiSwOv5cdTAwMWQqkS3Qo6JJVogmxNX2XHUwMDEyaVx1MDAxNeFRKFx1MDAxMOBcdTAwMDGRXGI8U7HI5vl8ZUtHOM1I63Z1I8L0WtVGYd9cdJez/PGQXHUwMDBiLTlQXHUwMDAyR2BaQsiHyYfMw2eq7sVcdL9pZc5GI3VcdTAwMGVcdTAwMDEw4lx1MDAwYiZcdTAwMWWdiFx1MDAxNFx1MDAwMyirRYNkyfFcdTAwMWXKcVo+SJD8joevcZpcdTAwMTZEUP+OPIOJwl4nSO3pz8zxk4+qeb+1mUUhrDx8XHUwMDFiU6g4+ZbenoqSMlx1MDAwN2BcdTAwMTgjwz1aPWO7ZDiUPFSXXHUwMDE5vOn7+dX9/kknRd6s4empXFyUXHUwMDEx8Tswf/rXXHUwMDAwXjFcdTAwMTPxONc/01x1MDAxM6xcdTAwMDUtXFxcYr+9tYvZiUdcbkt4Mc5cdTAwMTnXXHUwMDAwkdFcdTAwMTRcdTAwMGWU4i1cdTAwMTnYjMdcdTAwMGY14OGxhqB1kf3OXHUwMDA3tLhcdCRlIIKIR1x1MDAxYi8mXqROUDtT7WcnUoZcdTAwMTJcdTAwMTb0ndZoXHUwMDBlNOFn8uD0I/xOwV5g6nCU9NBI8jtKbFx1MDAwMUsuQE+eOnpcdTAwMTNmXHUwMDEyrkeuJZjWQTQ+2rrQy5bJOVx1MDAxM9Bkn1x1MDAxZW5cct7wXCIjytF90/s7XGZcdTAwMDGMXHUwMDFkXHUwMDFm3ehr58PPqZ26casn5k1dtrmr/YPt0Gi1XHUwMDA194wq50b2SGv7h8ZcdTAwMWYzwEpjXHJSVutcdTAwMWWmY46ar65bXHUwMDE0hp6gzuVcbr57XFynR3UneJjTkCPZdvrowF/Ozjt2Ses94apvNy9cdTAwMDJcdTAwMWRcZlx1MDAxNTpXXHUwMDFhz/j39tWLr3hcdTAwMTZcdTAwMDHibuKUjFx1MDAxMr0vXTjDOVx1MDAxZCbIQyZQaVxc3ChLXHLW4DRQpcze0Fx1MDAxONRalyZiZnmdNcWqvk+8stv8/Yy3mnVwbWpOXHUwMDBlXHUwMDAzXHUwMDBiQHkgmlxygV4+VfH+0LxFiu+DK36+fr/0QYBfymKnWp6RapJcdTAwMWVTXHUwMDEyyy2JwC+NeETjMMHKJ1x1MDAwYpflvtwp+rzUXHUwMDA36j/oI/z2caNWjrHFmmFFjujTWMK1QEJBKi9QmtI1+VdOiFx1MDAwZmXyXHUwMDE3h1xmhuKFWcPnXG5cdTAwMTNcdTAwMWXSJzyBt7VcdTAwMDE0x3A8xMn2kl9TkSi+hrR7XHUwMDE3bqGLXHUwMDAx2u+o4TXQLMCrmCTcXHUwMDA1y5a8jW9yUONcdTAwMWF84YrmtHPdcqPf2excbvDakZgtst9cdTAwMWEguKpbQcSBm2qhZ36OmumUZSpcbqLSiPiVXHUwMDAzajp2eUimI3eQ1To4woo0SZk/1OxcdTAwMTmJYulcdTAwMDS4Olx1MDAxNNOMLu6GJ89Mc6dcclwiSiHtmfjn3W62iCbf/Vx1MDAxZCv+IaCNdZbmyDniXHUwMDE4nePOLUtcdTAwMTGVoI5cZqSL64Kv3DSIOtaYi0W1g1XZPVJcdTAwMGa5eqds7+5ugHT6mJpcdTAwMGY4JcZcdTAwMWFcIkL7/UVuXHUwMDAz25w9KN00llxymVmsM0SkuC2RrkmGSo+4iqZ7UutBLz/F0zGv6UdcdTAwMGLjSeDEy+xcbvzCz1vG90qRt77qd1x1MDAxNzZkXHUwMDEx+rLZLnE+fWJ7dSqK5FxiT+3/Yeo6ttjUguRcdTAwMDexIKclOWdcdTAwMTF35CRAXCLD11x1MDAwZrL9ZmZhXHUwMDFmnyMsXaC7ulxubnfZ0k1+Kpl7iqE3TGBGXHUwMDAxwZg93JkhXHUwMDE1XHUwMDE5fPDR4Vxm9I/X81VQLJL9fFx1MDAxZppcdTAwMTJcdTAwMTE41/R3m7ZtXHUwMDEw9lx1MDAxZiAg8bf1ZF30x3O+yV4hrTiHUlx1MDAxZHLp2lx1MDAxYu+pPVl8YaLPlVximbP1XHUwMDBib4jm/2zBYOdhp3RccmqIpTD2y7WZWJyfXHUwMDA1q1GjzXA6USbfXHUwMDFk5EfV9d+mk9fkW1x1MDAxZW/nh5ayh1x1MDAxMlxmXHUwMDE4VtrpzO1cdDd1waGbjf7Xo6tUUeVds3lR0ZZcdTAwMDShe9Fwm+Z8X9pcdTAwMWWFW1x1MDAwMMag6b+9zG5WlJKwRLhIruGnXHUwMDEx9T+qN2M8o3Q/udhWb/tQW0lAVaOWM7zMw0eTlPOaI0ZcYvc8XHLbulS30yO3KzlcdTAwMTGfOlx1MDAwNH5Tf2JcdTAwMWPB21x1MDAxY2tf36SYbl5Ub3mz3/NrLX6UXHUwMDEzRMLXJVx1MDAxMaO3QGVeWcZbpEJ6pGlghUZe/PovQVx1MDAxZkasdnRcdTAwMWHayIToRD39xHOApb992bBaKJ3/cTNcdTAwMTLJPsuc2jtcIuzjtsnefeFBnkBcdTAwMWL4LdXxTkvJkJvP3Lv5SNzU/GRYZXr31NBw0/tcdTAwMTRQXHUwMDA0gVVucpKwPf391lxuxeW1sICG/1x1MDAxNblcYiVCzawgulx1MDAwM9H3gN6woZd28k6Fk6+rTa1XM/9s0bIzn/5umMxab5aYMObYpojL0etPb3bJ8lZNRVx1MDAxN5ZbcpC9vYayXHUwMDFhcEJ6XGZ5QqTuQ0qcX8rFUe9IoIvy68NcdTAwMWFehDhK15r7UVx1MDAwNXDOckHhVG9wyzrgv1x1MDAxY29SXHUwMDFjrWSnRlx1MDAxMTdE0OWOWOMjolx1MDAwN83QrkCD81C6NIFHlVx1MDAwNePRc7a4/ueB5Vx1MDAwYsLh+oX8pVx1MDAwZreU9d8mXHUwMDAzbtrGwVx1MDAxOCG9blx1MDAwNKfVh1xyvc/o13+pXHUwMDA3sbV6XHUwMDBlO/mQkEDSXHUwMDEyWohcdTAwMWaEv+eyyTrX01x1MDAwN8pcdTAwMTVAsLWT4rZZld9+uVx1MDAxYbNcIrKrukM7eW9rqWMgXHUwMDEzyV3PeIEjfrz65IhNW2f13t+ufLOh7SutNVRPIZNCaFx1MDAxYVx1MDAxNZ3LQFx1MDAwM8tOf29cdTAwMDB3um02pMu5QUCXxk7uu+8qNvyd70Rpe96UXHUwMDE55F/FZoXz2iUxXHUwMDBmOVx1MDAwMstOz6WGvZSeIdi13K8hQZlKpJlbzaf1zc3gi/OfXHUwMDA2WMuiXHLUKlx1MDAwMUandPYlX1BGlENcdTAwMDYhXHUwMDA1fik/XHUwMDE0z93Tklr2Q9l7Wz2dau1cdTAwMGUrfF/Zl6ToeZuhVUvlwyA9jsDDT5puNP7UlHSlPi85LZT70cLHo4tZLCAlOqV5tKnIn1x1MDAwZthl9iN5VVx1MDAxZrdhXth1hq1/o3dcdTAwMTlgilx1MDAxM1xiVq5cdTAwMGbTMSlcdTAwMDCrOJvA6Vx1MDAwMFxu3veret/dvr/NcH6EryjCwFwiLk9cIpXkKNfOrNhMTitQUcYur3g51+Zcclx1MDAxMX5cdTAwMTmOaUIvt+vi211NxEFcdTAwMTRcdTAwMGJyxyQ6K9iN86zb/Tjum//qVbT2fpKpczFsXHUwMDEyYlx1MDAxMTxcdTAwMThHhYH6xUdIO31Mcp7y4deHbVxmXHUwMDFlkTpcZqMwrOpcbqJPvlwi2MKvivEpXHUwMDAzrKGdbVx1MDAxNnVjrZq9dIGEjOMpJ3q6XHUwMDA0zrvFjXdLRKJFLMRcdTAwMTNb49tkXHUwMDEwI39K5f4+g+JcdTAwMTWEXHUwMDBm2lx1MDAxMD5wQSujmHf/Qfz3iLwoKi1cdTAwMWX+ZSk+rjhcdTAwMWTJOYCiXHUwMDFl35go5EzdYjymjFx1MDAxYnflXHUwMDA2Xau8wtq0XFxcdTAwMWayc1x1MDAxZrF/6Fx1MDAwMf1++9vuiSauQlx1MDAwNeBVXHR+9qY6Y23oymd4IF2y01BcdTAwMDBv3rwl+aKnXGJz8EY9XHUwMDBlXHUwMDA0ZfpR8mj+0MhcdTAwMWVtm1wisS871XWUXHUwMDE1YoWhXHUwMDA1q1Droihw8q9cYtiWR+1M4IJlV/lZevfBKGFAo+/wpVanXHUwMDAyTptqm2J/SVx1MDAxY9lcdTAwMTHJ6Fx1MDAwMYFcdTAwMDZSv1e8rFx1MDAxY6bnXHUwMDBlmoNcdTAwMTf13Efp2yjq7Wl3mtZcciQ3XuRS8MMzbsozXHUwMDE2qoWbplZj6naPTcjZaNMxpZlpJZq0SduiQdAjs11d+z58WP5vppx+79XbXHUwMDA0jWzLcLyFpcxcdTAwMDTrXHUwMDAxsUvKXFxcZsBKyUJ3J3GLoE7Ps1ZK1NKbotajcYNywM9bN3UucvZcdTAwMWS6XHUwMDE2j7ve7SPr1tVcdTAwMTLgXHUwMDBmmttpRdmm/cr6P8/Ms63jt8G0mV7nNmXqKC+MXCJNsfZdR8BcdTAwMWNCUdjd7y06UnjVlJZdxixcdTAwMTNtiLtie/XaVqlVfUV+79z6ZMu+XGKmJVwi8TDwtn9cdTAwMWTjbFxyYGk/qHuM4qJ8k1x1MDAxZbWUXZlzqKBxXFz9zT55n85dpGq4T6UhPSz7i+ffXHUwMDEw+6qa9rJE4O6VJ1xiXHUwMDE5nbvRrfky6lx1MDAxM/QtQibeW+ZwsphAaXmDK09v25NiPVx1MDAxM2xcdTAwMTeqfCePMcqy4Gi0XHUwMDFhle1Asbj/XHUwMDEy+XxcdTAwMDdcdTAwMDL1+kBcdTAwMTc0M564x1x1MDAwZbi0dlx1MDAxZWMgkUqYVFVcdTAwMGLOoFx1MDAwZiSFvDdSXHUwMDFhfHXQtoR6P1xij1x1MDAwNlx1MDAxNicgOlx1MDAxZmIq6I+SkaDWilx1MDAxZSU8XHUwMDFltSq54/r6JFfzUuhcdTAwMTa5t1x1MDAwYjqp53ZcdTAwMTizxdxccmRcdTAwMTDm28RcYkJcdTAwMTfssUL3g8nmUa81YFx1MDAwMLZcXHJcdTAwMWGwa/RK7Fx1MDAwNITcXVx1MDAxMdipXHUwMDAzUl66Z6a7tafH923d0Ndcclx1MDAwZdTjK9BL++xcdTAwMDWdk+RsXCJI8Xb0XHJ0XFzKUVvyX5bNu8gkk1x0XHUwMDAwvKhX8EZ3xHskK0NcdTAwMDDn932k+Wwh5TTSXHUwMDA1XHUwMDE1Zlx1MDAwZiHtXHUwMDE0bIx7zN27Zbwzo8Ipt1x1MDAxOFA5XHUwMDA0xc+MPuQtfntcdTAwMDXXPrHiXGb4mHaZcaZcdTAwMDNIdc/dzyAwXHUwMDBmnjA3XHUwMDEydfYhXHUwMDExylBcbnxwXHUwMDA3XHUwMDA24t/eXHUwMDA1rcdQ3DO4V61yQvZRXCJnJcNcdTAwMTZcdTAwMTjMwepza3RcdTAwMTRhiSb4ve5ruTXqc1x1MDAxZqVtTparXGJnsW2yWuipj1x1MDAxNb/GfnZcdTAwMWatPUGlff76hOCw5FxcYZmnqT//zJv8aoZcdTAwMTSQzCUlXHUwMDFmIKx11MhcciQ1XHUwMDEwRFRD8Vx1MDAwMcxGXHUwMDE5glE1XHTDP7mPXHUwMDA16Wjff0RcdTAwMDQ/g+FKmDFdXHUwMDA3dM6ymbKF3+xjXHUwMDFlK1NJV1peoK3uSkkryImmp1xuXHUwMDA0UmvclOheZ945d6xxUlCy3Wa/6eVcdTAwMWJcdTAwMGajSzVUfK1cdTAwMDBFnixdwCP6JnxcdTAwMWV+8VW21lx1MDAxOV6Y7fvkW1+AxFwiP+32XHUwMDFhNFxu/FxyUFx1MDAxNCM5s/2EXHUwMDAyRz/dxtQ6v1x1MDAxZvHuQI+401x1MDAxNNKGsahZWPV4hOzK1nP3NZfhKbj0L1x1MDAxY8nDR13qID3rc0vhxSPnyzaJc9hxXHUwMDE0WENVe1xiXGKCXHUwMDE0qmTeXHUwMDEznKTULM/zNPL0771dXHUwMDE2XCLkrTVRkkO4lXxHcO/YXq+2fWZDKafxwFx1MDAxZVGWIYtmd42uN+H0o21mOClTXHUwMDAxolmNO0PkvmcgeL5IniBcdTAwMTO9nYpcdTAwMGZQo1x1MDAwZWDq5mI5WyWk9IZ6sutIXHUwMDE3XHUwMDE3XHUwMDA3IyMww8G4Mr5iXHUwMDAwoE5glvtcdFVCXGZcdTAwMTJcdTAwMWHtW3N5Y2rwXFznfJnHOfObslx1MDAwNVx1MDAxMlVcbr97XHUwMDE5af17c6G72N/fzVx1MDAwNVx1MDAwZlx1MDAwNDBO9yGtXHUwMDAyXHUwMDAycEz3IcF31EXd9nm58JdMtuR1pSYq03JcdTAwMDKdXHUwMDE3ds/N8Fx1MDAwNiyVXHUwMDAwLpRcdTAwMDRcdTAwMDbv4+an46i5LjfYqlx1MDAxZarF9FxcXHUwMDA2XHUwMDFmXHUwMDA0lY/iXHUwMDAzspH/VnuisLbQp7Sr/7m8XHUwMDFlXHUwMDFlS99cdTAwMGbHY/GiquYxktxAdIRcdTAwMTZcdTAwMGLzXHUwMDAxViwx+0BIhs/6+mfOZ1x1MDAxZkj9oMyK4am7bI/3Nj9JRFx1MDAxNFx1MDAwZu59XCI2OPUmXHUwMDFmKfJcdTAwMDNcdTAwMTOAfmC5IVx1MDAwNsH9rlx1MDAxM5afuqb2XGJvvj7+XHUwMDFjg+OFe/PO1ov6J1x1MDAwZVx1MDAxZFx1MDAxYVx1MDAxM1x1MDAxYbblRn1hdVx1MDAxNlx1MDAxZM+xuqlOvMV3ok1fbzBZin5cdTAwMGJt1qBbZTSH6nFLUFtEhGs2XHUwMDFjbndgeaHpb61v7uz4qKX3r3KlljfS0GUzePXSY1x1MDAxMFx1MDAxMTHwmJiq6s9o3dz53XxcdTAwMGKqXHUwMDAxMVx1MDAxZmJcdTAwMTSsrvYuL7OZnsbU35M4T1x1MDAwMlx1MDAxOFx0P1x1MDAxN9+ZXHUwMDAzlDRcdTAwMDRcdTAwMDJcIlSlwipSNVx1MDAxNL1fR2+RK1fmwHFBXCLnjvnlP2n/kLs4TWu1Q1x1MDAwMk+TeDlcdTAwMWI0WJKGudKDt2RKXWhcdTAwMWOLgifv079wMiBF0JBk9TV0X8ybNfx1XHUwMDAzkE02gEUjQpxcdTAwMTVfZ0vdXHUwMDA20E9k2pEheb0pXHUwMDE5XSHmXG7UoN/B3/6zQZNQ6VxmiOv9plx1MDAxNSNNn3VBbN2hO3TwXHR+5WNHals9OMb7oVxyLWY5S7vtXHUwMDE4JSvvib6wXHUwMDBi/XxcdTAwMDVAXHUwMDA3ISlpm6k7g3l4cYBatIZ1XHUwMDEyLWxlXHUwMDAzmaZ5uVx1MDAxNUj+XHUwMDA1XfRcZulvJkxGLD6lpkeJXHUwMDBm3u7Xm4NSVe8ywPVcdTAwMTC5XHUwMDFkXHUwMDEwKXOFXHUwMDBmfID6kcVcdTAwMDXizS/awM08SlV6PFx1MDAxZTL4MOhOwUOs767So3NcdPP1L76DqMrcXHUwMDA2I6FcdNcsb8GWXHUwMDFiT5eJhDhd31x1MDAxMb26Zlx1MDAwNiU5XHUwMDA2zPnxOlxcsJyd8VuBe6TW4O7vkXsuX/jEsGq0v9dtqdWKdrRcImepiy16bU1cIlxuLudJXHUwMDAy5eHKUDWl++11XHUwMDEyUnbLu6nUllx02ui1V8zVnVxuX2wnvGPvXHUwMDFjI5hYwylcIlx1MDAwMVx1MDAwMK1cdTAwMWFrvIl16d86u1x1MDAwMY2DVc9pXHUwMDAyXHUwMDAw9axcdTAwMGJcdTAwMTfskz65SpiHdbM4jsPdpKdvZlF+/lx1MDAxY09JIFx1MDAwMHCOOec3tFx1MDAxZTyBXHUwMDAwXG6VU+GhLqKHXHUwMDE5OZOzVlEoa15cdTAwMDRcdTAwMWQl61x1MDAwM5NcdTAwMTZcdTAwMGIpXHUwMDE3I1efXHUwMDFlM14qalx1MDAwNvT23Fx1MDAxMc92tsFcdTAwMTdcdTAwMWWctGtcdTAwMDHOclI8fVxmvyQ1wsBgXHUwMDA1WzT2JJGmY0pZXHUwMDBi5Cuj21x1MDAxY9xS5yHKTVx1MDAxNDmK4Yy9utS7YnrFyonCXHUwMDBlYdebp/Xrpmr2Pjp/lmSmWWD891x1MDAxMs09oVx1MDAxY/fQddl79bWJSlx1MDAxM4Y6Llx1MDAxZbn1UfgnNTaYXHUwMDEztEfXx4tUVmy6h7jWooBnSee3nZzvk77G1WVhrq9cdTAwMDOp+994upOPMPVToEAxXHUwMDE465k+cOfgqbl5vVx1MDAxObdcXFxcd1x1MDAxM5HMk1/f1NBBsJbD2S1kZLjgbIZJd7A3njXbeDtcIt/xXG7Z4sabdlrWXGZXOZY31FPdz0s74tRcdTAwMTCJO1x1MDAxOb6Z9K1RSKV6XpFlZ0yXYtVcdTAwMTDwXHUwMDA2YOqjvoN03Vx1MDAxNlW/ivbmXHUwMDFh01CGz3uvj5EnXlx1MDAxZIlmwuewllx1MDAxOVx1MDAwN9g8XHUwMDA1k1xced1IQ7rTl8RlXHJYv1VcdTAwMDSgQ8m9Slx1MDAwM5Kw8GPUKaytnbvDRPLAUjjL4CTFlipEXHUwMDAyLlx1MDAxN9VcdTAwMDZcdTAwMTHYZyuXXp68z5ycymml5kvu0FeunYl3SDJcdTAwMTesMfdcdTAwMWHsXHUwMDAyJlx1MDAxYo9N36GB6PVbXHUwMDA0XHUwMDE2XHUwMDA051x1MDAxYVdHbFxmS1x1MDAxNiySpVx1MDAwZlhVXnBcYk188i5cdTAwMDRcdTAwMDBcdTAwMTevhmZYP4fAiUuYetLp5jhVP1x1MDAwZUHL7GtzWmnQq1GwXG6R9bvht99Ja2ehvWV44X6503BcdTAwMDBspoCrSmPbWmfstVP94WFcdTAwMWO4XHUwMDAwlFGVzskv46G7ro59mV5cXF6Xd6wsXG65gqt5ga+dXlx1MDAwN87wXHUwMDEzrVe2XG6i8uTL9OuDPylFeMVPQjqagHlff5TUMXrTXHUwMDAz/+vv+7JcdTAwMWFcdTAwMTKr03iv6kPbnclcYu7csWaKZ1x1MDAwZmJa4rpySzRdk1BcdTAwMTFYY3rvv/4yucHJXHUwMDEwXHUwMDE13YeOfzI+1/NccnLrXHUwMDBi0bzcIKxcdTAwMTa18oqyXFxB9/S2jWSmuIFcdTAwMWW5XHUwMDFlZrYvwUl/bXSCkcErXHUwMDE4ZZ3zkllcdTAwMGXsqlaIW8WsfoLN9PKwgt6KRlx1MDAxMDaTVFV959WXYlxyJFx1MDAwZuhvhFwi2Zs3eVwiXHUwMDExLTksrOjw4555glx1MDAwN0fcXHUwMDA0/957hyBtn+lV2427vT3YRE1cdTAwMTnZNHKivb9O1JH4XHUwMDBmwlDkXHUwMDA0XHLVQW/ClnBuJMslb5mtOl3AOPCvSK1cYpg1ZZmR+HiAxppcdTAwMDZcdTAwMDOaKZuA2ziVt8c6M1x1MDAxNkK8+cv4kiutPqeFVp9cdTAwMTRcdTAwMTZXwlxuW90rukOJRlxyXHSmNqiir1x1MDAxMCNvirwzgvzMXHUwMDExqVxmvzP/9Fx1MDAxN/PRznRUuG2srlx1MDAxN1x1MDAwMMnYXHUwMDBlppJcdTAwMThYw8ct4/I1WFx1MDAwZXjs50z37lL0MIpoLTtRt8Lh+5ltXHUwMDA2VfWNqVx1MDAwZa9cdHOFmzowLYjWrFx1MDAwMrs+oPpcdTAwMDKnIexcYl9cdEx78dQq2quISSozou/ixlx1MDAxMVt4R1x1MDAwYlxuOSgo+SiTRnv34ZS8QNC0h9V68HAnxN6B/Fx1MDAxYVx1MDAxM+BHflx1MDAxOKZ0Z8JcdTAwMTFcdTAwMWIy7CFcdTAwMGauXHUwMDBmjkOaXHUwMDFkXHUwMDE1l1x1MDAwZlx1MDAxZMneXHUwMDAyh6BCTFiXRlxmXHUwMDAx67BKTu3RfMQwTIO7q7hd7+eD72OtbMHug9xiXHUwMDFjqFNcdTAwMGKqXXxcdHVcdTAwMTHs2nBnfcahQlx1MDAwZmtKtydi//D5bX4+b4RcdTAwMThXTy7cTzZcdTAwMTEyLsn+PEPEylx1MDAwMlx1MDAxNlHL+3hbzsZcdTAwMDKWiemWXHRcdTAwMTKsvVx1MDAxM3/e3GVcdTAwMDLwTbKVkKKORFx1MDAwM+aov6tsrmlcdTAwMGLrtzNLc/bX95x7pNl2UyRcdTAwMTWPynXkXHUwMDE1eVT94MU43Vx1MDAxYYNtL7b0kUJ2eclPIdrm+OFhnd8kr82fiPdcdTAwMWFwnYyj1WaUtq9561x1MDAxN1a4gmNcdTAwMTlcdTAwMTnfbHlOXHUwMDFmoHCipFxcgqYyjVBcdTAwMDHjYtHYpnLsdJ2lumpg/Fx1MDAwYlx1MDAxY/itLfk9VqtcdTAwMTnKd1DbXHUwMDAw6FZTL51cZnB5eOnIXHUwMDAwYFpcZlx1MDAxYW0m8U5rXHUwMDBmqqJ7MS+Mh1x1MDAxMZvR5WqtuO5O1kxcdTAwMTauipZHNUxsrdhMRJezdnIx5vxUmcBn8Nx/XHUwMDBird/x4Eh7XS86XHUwMDBl+tSmOLmBkmnwXHUwMDE3rY1ZVFx1MDAxMmBcdTAwMDVRgFV7Mk5cdTAwMTaaXHUwMDFkji/RQ/ZU+9aVXHUwMDFj94T+cfiQK/A2a1x1MDAwMMJNVXAlaZVo78IhXHUwMDFjUPe971x1MDAxOMha9WJjXCLHi1skZI7vLGLhvVaDOYZ9hKAxnrMuSsndh2K0XHUwMDBmZMNcdTAwMWE9n7lv8iFJ++dNXG62QSOF/SGC5N1szlx1MDAwNlx1MDAwNbjUfK+bKVZEsvm9urR6XHUwMDBlu1x1MDAwMFXU1CzQ/cpAwPzOJ1x1MDAxMZ70mNCCu27Igc3rRlxcStdptedZ9cvb6keYe3NcXO5gtd1yXHUwMDFkXG72U7D0gFIvTVxmhG+jplx1MDAwNVx1MDAwMOz54nznXHUwMDE1IVx1MDAwM1x1MDAxNeBJ6VxcQqxjp95cdTAwMTFcdTAwMDKjMnLBXHUwMDE1UibKMzJcdTAwMWOTXHUwMDA3cFwiXHUwMDAyXHUwMDEy6dW4suAg11x1MDAwM1x1MDAxN8vEp+/1MM9SnTmvdShccrjWhtaSIVx1MDAxOcKJymejtby6x8gsfyldVXfN1lx1MDAxZVx1MDAxNjqwiXpPcahcdTAwMDffJ3ZcdTAwMTXgfVx1MDAxZlx09ma25ytbvvXlXG5cdTAwMTWuXHUwMDAyiCj9hfmS2yfpc+uXfFdetKauXFyISrDeP7oojY+3/Wf+ezZuo1x1MDAxY8ZcdTAwMDLLy1iarNDbR3DWkJVcdTAwMGUti8lXhZ64mz+9v5PSK8BfvybmZ8LDKLVcdTAwMWb1otAxsiPQIfjieerVzZVcXC5cdTAwMTfwUpn3P184yLH/9OlxLMRcdTAwMTVcZuPKRPrkVtM9leAl6lx1MDAxZWm1fm5SI4VcdTAwMWNHU0dcdTAwMWZcdTAwMDD701x1MDAxNydf3Mn97Vx1MDAxOVx1MDAxNizmj1dPXHUwMDE1XHUwMDBihP2noc3+63E13q9E/OfVo1x1MDAxZc8xXHUwMDE2m5vYP++mR5j93mNuITt+rn/+YX/9XCK/1quO//VcdTAwMDQvf+beZ1x1MDAxMCCz0F/vXHUwMDEyxv/tWbom41x1MDAwMP40mnGs8OtcdTAwMTFsjIbLm3/eJcxcdTAwMWb/yndtXHUwMDE08l/vXHUwMDEyPv9zjLlpOYn960e+XHUwMDE4ZtLIuv7r4XH88TdkgFx0UcV/v5386S3JNmhk678+Pir181ZcdTAwMDGH7L9jauR3zJEl/+vz8eaMZ33N1Vx1MDAxOMD297dZ5o8/x2997F+vJ+FPw1x1MDAxYcfFiY+5zV+LqOhnNaVF+ptAXv98pf70p33L6F9DcluHf7zEvmLnXHUwMDAw7j8/tl9vtnKj0f/2XHUwMDE2vmtGZO2c5/71Pt+/XnL5lM54+tdbqPzZ01lcZj1u/O2pPOw/PqfUncTL32Okv+vL8v/68JTJ/T2msHxlhP+t74+TwHOwXHUwMDEy/WuYrGvpdzGYXHUwMDE3r42S8c+Djfjja/rJ9/BfjPWc4rSM2lJcdTAwMTbPpr/eXGK/OfHhn4+K+nygsFx1MDAxOFhFXHUwMDEx7HJkUW6S2iMpICOY6K2ha4bfIEOQlGTTOcjQNdBYkFx1MDAwZkqJlObeXlxcf07f8TbZXvmt3KdcdTAwMGV0RGqujbfyRTz8s2uaXHUwMDE0llOY7SFcdTAwMWVcdTAwMDTO/+ZcYm3C9M+HSX27XHUwMDA2j7BfVe5OfIK131x1MDAxM9ltqG2G9Lvkb1x1MDAwZuNcdTAwMTbwNaO1ld7ewfnfkrVG9/hLRPiieb+MQVx1MDAwZYF4JZro9f1cdTAwMDY5pKo2lnPH31x1MDAxY1x1MDAxMGNZe3s3Vlx009ss035tqvpstlRcdTAwMWN2ikhcdTAwMDY4tzX7klx1MDAxNFo7QPq3Z2vgzbFcdTAwMWK/wG/P8FdEyGKWM2z6zzdKXHUwMDE1XHUwMDE1nFx1MDAwYt9T/PbwdGrIXHUwMDBmSmKk1LlLNDtClT5lgo+d2NUhqqpRazTGK07axan/9nT6rMPhXv/AXHUwMDE2RDa1r4uN+960U//Pq42rX1/4i+jVjlx1MDAxMkCGvvPGdzKSoPlvYX2qlfaHf821hlY4cq+15lNHT/TGKcqmr6hcdTAwMDb/edKhj2hcdTAwMDQxXHUwMDFic6qWfKvbI3/eXHUwMDBiOisviCRBj0I+XHUwMDAxmEOwdrLClYD6zWfK8UtcdTAwMTmKe+ekfdBLlb6F8/o911enoyhnn3Og1GTah6+yxp/80ricS73hM+hcYmm+v6Imjy5cdTAwMDPVauh3n1x1MDAxZddcdTAwMDPX7EUtudBcdTAwMDFdxifXXHUwMDE16/lIg6SOfaNcdTAwMDXEqJyQNNz+WU/n7z2SYtkpvtpQM+1F5l3z67Fm+emX00x0SVb31ytKXHUwMDFij18/bn8nNfU3Z/i/eZW0PS78izXzNy9A0jpcZvt7z1x1MDAwNPmPt1x1MDAxMJdRXHUwMDEwXHUwMDFl/4lcdTAwMWb1XHUwMDBm5zle8D9vsVdt/FJPpd3FU/7gScP96fd2ouBj/TVzfFEvjOGOnTaQf8ewv17gqanDW/rPe0/rf1xysPzIJfQ//zSO+WPxlPxf36zC/nzYXGbp+H9zXHUwMDExtj9+tVukw//mXCJ8/jbY+fmoXFz/et3/YPKZ61JcdTAwMDP9zXv7T1x1MDAxZu/rKVR2+LePndV/PrNcdTAwMDAmXGL/mpXH3/+xv2br/kv10Pmzt5ZcXNUj/juroK1+c1x1MDAxYkB8uP47xvy7PiRujv3f+rrf+vLpZc//1uf8xVx1MDAxY8VcdTAwMTmZ+J+XpPHHa2aQ/l+PM/THy+rCy/Vfj7Pyp8dZXHUwMDFi37j072aJP3NCTmtiyP43w1x1MDAwMvvNsOBcZlx1MDAxYf6/fuD453uL/u9cZlx1MDAwYuNncchS7lx1MDAxMmp/f5u7fr/tjMnHev09dYr/2Tx3oIGE+r85XGLL3/WhomVil4pzWUpxp1x1MDAwMHE0Usdxl8RcdTAwMTeJU0XfM/XfNb5cdTAwMDJMbJTDplx1MDAwNGrXr4eyxUbhffwjXHUwMDFmempcdTAwMDTljFx1MDAxN/unxjCldHRcdTAwMGZeXGaoPFZlL4L3vH2DmipcdTAwMDVcdTAwMDA4qVx1MDAxMMBurKs+j/CuMtWvOvhcdTAwMDPwmVximbtcdTAwMTny7NTeeVx1MDAxMSxLNI4lXHUwMDA138Wd4uqA3lx1MDAxNJhRVGW7pa1h2ZtdXHUwMDBmgUvAbElfN5F2VO67oW77hG1j0KqtnvXNO1x1MDAxMJJzmrvL21x1MDAwYkFJsaRGPK8kXGIzk/BqYtk6Klx1MDAxYmV4oO9x3KvWXCJoXHUwMDAz9+k0sTu/rMYtXHUwMDA0u1x1MDAxNjBMwCpo2nb3cPGrfbaI09NcdTAwMDJLxlx1MDAwM+zEaMhcXPe31k5cdTAwMTiEbZzhXHUwMDE5L7lcZiP03p3etj9q80iSq3mJXHUwMDEz9HpBrFx1MDAxN4uPwEYkb4n6mMkjc0OyYOs/gOdcdTAwMDCPoK/O6iNFtL9vXGKC3OxcZv+ugWmaXHUwMDAz35xTXHUwMDA3pI1cdTAwMTG0zoO7cyRfuNAv4fxS1Hj/+dz5X1Hk6VfRXHUwMDFj9Uux7PPJTPRcdTAwMDHntfvNXSVmMJrFnl/ocZmBLHnVvzrOXHUwMDFloXF+bLw+aTXz61x1MDAxMViDNTRcdTAwMWNya1x1MDAwNbeawYGpWZD9QM95f0JcdTAwMDNcblrwXHUwMDA0IybV7H26to2xKuOiTJbOo4as94rvifWMYGZniVxuuFx1MDAxNsu/T+WshKachGuN2XuvpE768tInNeNe6Fx1MDAxNlx1MDAxNKjwbpPbhKugzYYr515cdTAwMDWaSlx1MDAxYtqPmFxc6HZkkXj60555hUDL/ohUXHUwMDAw11x1MDAxN1QypVx1MDAxNs1SdKfhoFYkmtvKwX6VPUDBY3OOx3fdR8Gqok6MbbOqXHUwMDAxzFx1MDAwMGD2JjtW2vcklFqm5vPRgqtcbnzR2IBcdTAwMDGjvlx1MDAwM2jlXGJcdTAwMTJcdTAwMWaOzC2cbOtcdTAwMGXpuDZcdTAwMDQ6XHUwMDBiq0Q0VTFWbpM6YtnUTDfhzkhcdTAwMWMuXHUwMDA0VGxcdTAwMDdcdTAwMTN0vkPdKThGk1GksjlYbFx1MDAxY88trDCKJMnj1V+FTfc7yp355togXHUwMDAwXHUwMDE2XHUwMDEyVUb28mgmPknWJFxmXHUwMDA2pY3f7oiix+GUXHUwMDE4TeMkWGioqavNcSTQzyd80pHCQsv2XHTWoVx1MDAxYbaKj33rx3BcdTAwMWEyhFx1MDAxZSA9cy/tP2XRgcKilvbZ0Fj++aVx8HDY5Fx1MDAxYrO7XHUwMDA3mif5NUO3pjQ2e33OpjZY107UXGK+j1hspKrdJm5cbpfQ4Fx1MDAxZUG6REEmXHUwMDBlWFwiT/mujmuJYoehXHUwMDEws3/3rPQgOT9Pt/i66dk8f1sqolxmK4dkjfiro9rWXHUwMDBl3Vx1MDAxN0BRIEh5uVx1MDAxNdn+XHUwMDBlnyhcdTAwMWFcdTAwMDVIg+OtSU1PwFwizFx1MDAxY7NcdTAwMTi9vkcoe99cdEF3xrOczfI8j7xDXG4tRehcdTAwMTFccsunXHUwMDE4b7VlV5pcdTAwMDakg6Jp8SlcdTAwMGZYl6Pi8Y7VeFxysfQ3w6jFcVx1MDAxYfw0w2u/XHUwMDBiq9VdmSlF1uHvM1VUXHUwMDAygFx1MDAwN7yvXHUwMDA3uYDyRilI+PvU6kYjmJ5cciOTIMHxldbQPrJ5ZdUkrGicSYmnYqNcdTAwMGaqfHBadYjoeL1cdTAwMWFErb95XHUwMDA2nDRobnIqUSCbMlx1MDAxZVx1MDAwMPK1a9LIhlx1MDAxNlx1MDAxZsdhX+t8KaJriljjr2t4yCeGUWC8cvVDlsnikVRcdTAwMWMv8993OIvTKU232lg7SFHUmjtWXYC/flri12OoM8tWMdfnx+VcdTAwMWSZiUSsOFSCXkCJpIqbMrSbi7ohL0pUYFx1MDAxZGfyoPra6TmcXHUwMDBm9/d+RG/ZUI/f7ISn6bf4emU8/nQ0beUvszjng35VXHUwMDFmwoxxaaFcdTAwMDApXHUwMDEwa6yPWT9zaa/+LZ1s/O73XFzjhVx1MDAxOfdcclx1MDAwMJZcdTAwMTGbw1x1MDAxMX96oiDGTPVoS9CCXHUwMDFikcpcIsl/te8pQacx8HnS3d/jjdo4p1HXN31wXHUwMDAwZblPmOtcdTAwMTlPv1x1MDAwM+ajYnfcb1x1MDAxYVx1MDAxM6xezVx1MDAxYeOjp9NT4ZA5XHUwMDE1cv0y2khdzeuR82y8wVx1MDAxYdPyM4VHdExcdTAwMDLZ8vJEP1uQXHUwMDAxZlskllxu66yd1JA0Pfl0qWG7XHUwMDAyYVx0wYZIXHUwMDE521xiKFx1MDAwZlxybGRcdTAwMGXn4I6Syy5Uclx1MDAxMFx1MDAxMJuLrDXYIFx1MDAwNsWypS1cdTAwMGZ71E7zuVx1MDAxZV/mwyehqXUjcO7Ao3hNRD3bw4x9jVbuhzk4KOZYV8TONUCaj7TbpIgoXHUwMDEzSJdAcFx1MDAxN+ltSDriXHUwMDEzrHyQ7MtXsCHq+ah12Vx1MDAxME7FXHQ/6ZBcdTAwMWFknm5cdTAwMWPFJNjWXHUwMDE5cV/D5lx1MDAwMN5cdTAwMGblXHUwMDEzXHUwMDA0TWu4o2uw3dSHMNNAlUV9LIpcdTAwMDYwNN6Y8TdZUVx1MDAwYrvQTFt8XHJcdTAwMDG/1ZaJoXw0Xlxca4qMQatmmKahZdJcdTAwMWT5vElqyMyJy1x1MDAwNOLEZlx1MDAxMKyFOJJo6du40dpfR0lcdTAwMTOssOAqnq3rdihEXHUwMDEx9K5cdTAwMThcdTAwMTZSvdcyXHJcdTAwMWN68CWH8k0811EmXHUwMDAwMdnQYWYpM+KRjlx1MDAxMVxu5Fx1MDAxMaf9XHUwMDA1iSlcdTAwMWP6I+Q+P5GCXZ+2RV5Qmbm5nVNtbV6SXHUwMDE52ZRcdTAwMDb+5unsk1x1MDAwMFx1MDAxNlxy2StcdTAwMGU8X2hcdTAwMGXvhlx1MDAxNlx1MDAxYe7C72I1Q11cdTAwMGbaXHUwMDA3VJHO98z9npMv+VTYrfIykajuu1x1MDAxMYBtq6OsoDimcFx1MDAxN+PBRb1dp+hKsFu0LndaLvIjR1x1MDAxZlx1MDAwNlx1MDAxNr16vEvhXHUwMDEwQe5o8J7EcVxuSVx1MDAxNiRTRGqmszX8vmXVyEyEWNDdI7XnmoDdUyPfiE5YzVx1MDAxNMkoXGans0b+XHUwMDFluby28fVBuYFcdTAwMTc7WKfxXHL7zZj62au97zhcdTAwMGLSt2dfQVBcdTAwMTRN3CqcWtDeXHUwMDA3frTL3Fx1MDAxY4rAhzpOWdFcZj3RXHUwMDE3MZF8PDRcdTAwMTKmXHQ4W68vyCVcdTAwMDNcdFxuXHUwMDA1eoxZ0L5cdTAwMWFLvmlAZlx1MDAxZU0gPHpxg7Tv96z2XHUwMDEzl7vmdKHkSaznzlx1MDAxOFHujUd/XHUwMDE2p1KJdo9SXHSFkeR9b8b46LnfvLOc9VP/gvRcdTAwMDbma6y6c/+OXHUwMDAx+zw622KPdf+9s3lcdTAwMGZcdTAwMDCdUaw+XHUwMDEz6XxcdTAwMTB491xyXGaSaYzx1Fx1MDAxYlxiXa/jimvLLnRfISqwNcSMxOdcdTAwMDNcdTAwMWHWz/IgeZdue2tE/o1B5ZihXHUwMDA1XHUwMDE5XHUwMDE22XNeqJwz5lx1MDAwMVpHXHUwMDE1lohcdTAwMGZcdTAwMDZcdTAwMTZcdTAwMTnaXHUwMDA3kFx1MDAxN0/lJSddTijBZVx1MDAxZoxcdTAwMDdcdTAwMTjDp1ifxFx1MDAxZnz5glx1MDAwNZe43UpUkvJcdTAwMWW6XHUwMDAyXHUwMDAxwVxuvd/s0J1A1mwnuChcdTAwMDCvUEfM5+bAhPpccrSNZOpkNpLCm2ktsIhcdTAwMWMqpqoxkrZqXHUwMDFjp1neuFxcXHUwMDFmT1x1MDAxYevcXHUwMDEw1JdOUFx1MDAwZVDEMDjidpqmeLbGv4eByqZsXHUwMDBiMVx1MDAxZFx1MDAxYlxmbFwiblx1MDAwN+NYbiH4ufhcdEUhPjXPs0Enl1x1MDAwNYakQFbVO1x1MDAwNmXvXHUwMDE4XHUwMDFhpqxccpOAqlx1MDAxNiBcdTAwMDZ34G27lYX981XvXHUwMDA2qFs34a9cdTAwMTXjjlA5sfypwnGq3+DHOGlgedVcdTAwMTBcdTAwMDVcdTAwMWGW1oZcdTAwMGJJOlx1MDAwZoeiSSWX+XO5obJsMVxixlx1MDAxNpGMd5kz4HhUQmhWtIROn2tpwsORXHRhKIfW64o5NIJAXHUwMDA2jPLgjkfP9znKNCQhwTGwgSdYQdklI1xc3cjc6etcZlGkSYlcdTAwMGVcIoB+4iTd9NTP0zjj9q1cXDllJuLQXHUwMDFk01uuLodA9Fx1MDAxM3yvklxuWlhAXHUwMDAz/leSPp5AaOdXw5Ppolx1MDAxMEypknvfgXj9uqXe0YjlX1xcOztcdTAwMGb+XHUwMDA331x1MDAxN788NY6Lm0cnk81WluVBPnVp270j+FxuXHJf11x1MDAwNTB6pzNCXHUwMDBmXHSmm3N9QFtcZr5cdTAwMGbsWaxcXFX7/lx1MDAxYlrNeFx1MDAxZmN4JV5McXFccu7jw6lPXHUwMDAwKEPHQYtnzdfz2SZpXHUwMDA0QEFcdTAwMWLStkM3welcdTAwMTZcdI1I0tvFXHUwMDAycohpJJk7Qp3RNIyO95xbZ/Ipwe7Aoo6mi8Kdg1x0/lDAQ1x1MDAwNXFcdTAwMDK0eZ4nu+dcdTAwMTbvx2/Orlx1MDAxOJ3AdSEopm37vmiK4NnHUS87Zn7mXHUwMDE5jtpTT1x1MDAxNcKer+uiz9+Ml8bKJf5rJj7EYLqerlx1MDAwMlx1MDAxOeWO6WGHMu2fz3RSX9xXuTj+1bTZNKpcdTAwMTKiOGfF42hM16dehSlTXHRwi3RQhsxhXHUwMDA2s+hT8+o20vORv3hcdTAwMDCxd8Bmg4o0g57PrVaxXCJcdTAwMTR5dV26Tis3fjAvY9l4vfJQuotHwJImZ+/5O9KqXHUwMDBlXFxj8bTC6Fx1MDAxY/SiXHUwMDE57ieOn3L8PVx1MDAxMVx1MDAwMlGlszKMUM9cdTAwMDH+2KJEWyfj9L0uaEmb+e3FXHUwMDE5QKvyXHUwMDA3xXqFnyeJMj6IXHUwMDE2emCiXHUwMDBlaaGXnDTWI+JcZlOyfHbUUOE9vV7jrTQ8XG6TVobEttxJdYjTi1jCqrJVXHUwMDEw6VwiJt2/h6Jgn3I82cSd9V9eXGa/j5i/lsAtel1cdTAwMWVjxlGBvjHh/aQuKknfR7WrXGJjIVx1MDAwMGxjXHIhnFxm/6jQmsKTXFzMhzHAqKlcdTAwMTBqJKbLsuLOtJ9cZlx1MDAxMFxcziCm0Ss8tNQkxZ1cdTAwMDZr1aA/8Fx1MDAxZPBNdlx1MDAxOVx1MDAxZF92kXX+XHUwMDFl7jzZ4EhLmJSymG6XwNKja9NcdTAwMDCUdKdcdTAwMGalOIfV5+uh/09ofoidz5exh4DduX7PTZkn0O7FTlHyXHUwMDA1p7QyXHTvPVx1MDAxY1x1MDAxYaGIsk7HWb7Y51VsXHUwMDAzXHUwMDFk58doXHUwMDE2pshcdTAwMWH9XHUwMDAzq4qvJv46g6MwW0daRlx1MDAxYs++ryzhbrp4+D5rdfarbc1HV0p0iL9cYn5HgVxy9k+4j9T7NrzcXGLf+1xuOlx1MDAxNvNVdic3UlNNLaDZQ/HNot6EP1x1MDAxZspcdTAwMTLIs7jtcWvGmdBcdTAwMTZMR+vIWfHOIKT4lsRaXGZXJ77seo3wh1Yk692r8OqUQFx0PfpcdTAwMTlGnDGUXHUwMDFkVlx0ZN5V3FxyUS/xbFxueYpcbttcdTAwMDBmJaYnl2GYmlx1MDAxMd6CXHUwMDEzvKKnuM7reCyRenX0RORcdTAwMDZJ0kYj8mxTmFx1MDAwZVx1MDAxYqHkeYiuLKvU611xdsM76oHnR1ZsY/BqUFxu15VVIUzv05+K8Fx1MDAwNO12qsrbo921MU/mksSIXzzEdXOnjVx1MDAwN/UrXHUwMDEytLVnO4pcdTAwMDHYxrdQWpjSXHUwMDFlhlFN5iPjXHSoZGnKLld7tFx1MDAwNbJikFx1MDAwMDRcdTAwMDOjMiQ6ipLkQ2jJXz7TQVx1MDAxN1xyXHUwMDE4XHUwMDFiL6FcIuozXG75XHUwMDBmVCetXHUwMDAy2FxyTlx1MDAwM2BcdTAwMTIoQX9cdTAwMWKddmQrRKjefFGPzDlcdTAwMWM/in/PYn3cnclD5aT3jpI4LajCYF5cdTAwMTFcdTAwMTdcdTAwMDDxN1x1MDAwZas33muY14HqlVx1MDAwZoDOjzPL9b9HXHUwMDFmXHUwMDE1q3jUXHUwMDE0XHUwMDEy4VsrLuZ1XHUwMDAwVkN2XHUwMDEx6YC4XFx7XHUwMDAyq87gPERcdTAwMTDtPIvS9TzqsGpfR+i2XHUwMDFl/Vx1MDAwMpVcdTAwMWNJVH0qWEy9j6pZXHUwMDE341x1MDAwMIk/L+f+oF65vZv0VPY4TMHY7ztWp6FNz5XUM1rClvSf60Z9PMgox4JVjjyOqFx1MDAxOMl0szFcdTAwMTYpSWd+o1x1MDAxYmCGm6lcdTAwMDL4U+X4/o5cIlx1MDAxYlx1MDAxY9/x+op1YVx1MDAxN1x1MDAwN2B1J9pqvjcg17FnbXuHy3suRZA+kGZfZdwr+EzwXtnaWYyYn4qQW+M/M3c7znGGj1x1MDAwYoJcdTAwMTku8Vx1MDAwYreGNsBpzCVwXHUwMDEzMZtcdTAwMGVJJa9cdTAwMTHXXHUwMDE5769E352wot9+Pjf3VDE8rLacMNBHS1x1MDAxNs3kjmeOXHUwMDA1S1x1MDAxN1x1MDAxNlx1MDAxYvHK9GNHwSu3wYKKuv1VLXiEXHUwMDFhXcA4lVx1MDAxMVx1MDAwM5n5giw022NcdTAwMTluQIDruZU+8VdcdTAwMWaRfaCV5iO+zzWYXHUwMDBmfoDdq6RcdTAwMDesJd113vlTRFx1MDAxZuXaf9PmnZFQmJGV/UHx8Zy6zH9HXG6EITOGw9ugppth2VdKWz6gLvdyKJtcZvOK81xcX2NcdTAwMDTUts3IY5ck587p7evuJptcdTAwMTl0h1x1MDAxOMvXYnpcdTAwMWVPi7yJnK1SXHLnjVx1MDAwMa+R3D1GxuEpSsF9vitBlWyYsbtDbTVcdTAwMDDsct4s8DNgdl0mT33F24D+cX3XLlx1MDAwMMLl7llcdTAwMWRfwsib7ziiXHRH5cAr1cNcIlx1MDAxMmBQadZcdTAwMGImgIJcdTAwMDJcdTAwMGYrikLVw1x1MDAwNKvawWLr8lxuxz8u1XSM1qY6sM9cIlx1MDAwNDE75lxinKKcSsHwXHUwMDBiXVW2ILK/tmg+V/IgXHJcdTAwMWYpNVx1MDAwN1x1MDAwNO4nXHUwMDFk3KKtuYN72HJHR4NcdTAwMThV2u70XHUwMDE08bbd+uHQU9xcdTAwMTjkbEUm/9HOWX0q/ev37N7cMnskQOPzOlx1MDAwMuFlPPiY/Hpq+s37XHUwMDA0j6DIXHUwMDFjpcH6hjuwyVnBXHUwMDBixrXm5rFpZp6cqmXj1XvCXHUwMDE1Zqwtla7hXHUwMDE4Xe3Kt1x1MDAxYYWo9Vx1MDAxZdUjh5PKwMo0y+Cs7bhcdTAwMDD6zb9cdTAwMDQ4pv4oU981XHUwMDEzQH1OQkJcdTAwMWKswK7iznyS8UTkXHUwMDA2wyy5lPnC9HGERmFBzGK/JN+mWppcdTAwMDWSXHUwMDBlyKflnVx1MDAxNjWAZVxu86b4R2+7qyz0xXAsoHEjiih11Tch2nudy6+a5edcdTAwMWJF6CZ09Fx1MDAxYor4q5Q2O1x1MDAwNcddpepcdTAwMGXay2tJn6Ttvlx1MDAxML23OTtcdTAwMTXg99Gj0TveXHUwMDFm0FxuO1x1MDAwMsxEW3vjXHUwMDFi6r4j8PeIS8RwMOLKlbZMRI7nsvKZcDahsYFSq3pi38jdfTFgTW9vXHUwMDE1Sdonf1PjKNj8pdrYXHUwMDEx56K/XHUwMDE1XHUwMDFiaVx1MDAwNVO5j0glTLIhw4B9uT5L2V3vrlx1MDAwN45GKyiPd9JcdTAwMWOLez9qhlwiW0RcdTAwMDS8wis+nfv5XHUwMDFhyL1cdTAwMDGjavFEXG7fdkJro7reZG9leVxiXHUwMDFmrOJU8SZ2O+K/buqCsDdcdTAwMTGfuXHweWFcdTAwMTTx+FxyXHUwMDE5XHUwMDFmkIsnhtFZxcWH8+jAvVx1MDAxM2VplvKbk1x1MDAxNkfINuiC6ep63fB4tNs+Ulx1MDAwNlx1MDAxY1BBi8+laNFB6FxijW+Kpcp3IH1NXHUwMDBlSXbImH1p70WFXHUwMDE47Vx1MDAxNVx1MDAxN2pcdTAwMDN/9pjPjXlcdTAwMWLVsJHFZ1x1MDAxOS+6XHUwMDA3vfj11Fx1MDAwM8c1X65cdTAwMWSJ2vXDU/7P5vH30N95XHUwMDFlLrk9klx1MDAxNZ3LwPne6tuWNolt9Vx1MDAxMaUmWZPWWuWy4/XAnkyTp3dcdTAwMWbYJ00sdf3m0cCH7lx1MDAwN9zauvYm44vQzsM+vlxu1/jPX9P3aq38p/k01HhcdTAwMTCnU3vz8H7OXG7AoGm4NitcYtqoXGJcdTAwMTdcdTAwMDMsXFzkuVx1MDAxZrJtovur6zvxlPbNsVwimVG4jtJVjf6CUVx1MDAxNFx1MDAwNLVcdTAwMDDQy8r+TWeCIXN79Vx1MDAxYWTb4HVYq6tH9Xs/XHUwMDBiw/zjXHUwMDBiXGLKaUpcdTAwMDCfmoRyylxua5KYKXpdd+KTSNlTw0DwvFH5oa5OXHUwMDE0VKzgxM2MXHUwMDFjjGFbfERD/cNZk/c4vmX3YzlD0/7NXHUwMDFiS7tW7TSGr6fuh7ghj/B0Olx1MDAxNDvkXHUwMDAxXHUwMDAwwrGLXHUwMDBiLq+L55qUqVx0ZFx1MDAxMiDkU1x1MDAwM1x1MDAwN3eymp6Ff9G0Xlx1MDAxNYiLr6VWe1xcObWOjJTFuVx1MDAxY48uUFxuofn5Knak4OmjkoY9Mlx1MDAxYicvZf9cdTAwMTdPXHUwMDFmXGbcOjvMXHUwMDFlnL9cdTAwMWZt/qhuS1x1MDAxZrBIxqA2KX85XHUwMDAzZVx1MDAxNjiwzVx1MDAxYps/M9axdLvl6MeyK2OT1EfONkpBKDtGSqJyijZ2qXdVQ/pFwact5t5wXHUwMDE5a8755/s3X6BcdTAwMGVrR4zwTY/Je5iqQOjxXHUwMDA1sFxuW0tcdTAwMTZcdTAwMTFcdTAwMWGzp5zf+vzU5Pmdj/B4v4JcdTAwMGVikFx1MDAwZjFyXHUwMDE5XHUwMDEwXHUwMDAyTVx1MDAxNNNcdTAwMWK/elPsvpDMjIxFPILytVx1MDAxN5Pglz/vXG49S1x1MDAxOc0g7TfxYP1DjJQ3202biFTgcUVcYub5n1B97dlcdTAwMDdR9GxcdTAwMDTzcVx1MDAwMGWGXHUwMDEwXZhcdTAwMDMr3D36OE/tW25iUMlHQnd0XHUwMDE33t65eLJXylx1MDAwM4/q8D1cdTAwMWJcdTAwMTWwbE1CXHUwMDAx/lmNTHJcdFx1MDAwM5p2XGJccpK12WqKVFxcpf1pXHUwMDBlyfFCz5mVO1x1MDAxNDr5XHUwMDA2zdxQL7gnpZ+z461RXHUwMDBmv+hcdTAwMDGrzjZuXHUwMDFiXGI26pXlJjTfXHUwMDFi2NBcdTAwMTEh3p9cdTAwMTRhXHUwMDEwW812XHUwMDE1vffNzmlcdTAwMWT2d1x1MDAwNOv5jYRsf2h17MzhVGI0XHUwMDBlw+0t1+tUXHUwMDE5+m1/fVx1MDAwZSrUYTyHdCdvrMhcXO9cdTAwMGVs58H73Cpccozu+3vRw6svbXlcdERcdTAwMTFG+u+6aFx1MDAxYyv08U5237X+/duCPrFcIoDiXHUwMDFisIZxvFx1MDAxOO5S87YtZmGGVKM2XHUwMDFj6lx1MDAxMtdTvXjhXHUwMDFi9EJNn9P+sOVcdTAwMGLtrsvLafNcdTAwMWLaKC5cdTAwMDXmcz3WO1x1MDAwMcHKf3Sf4o3mp9pG55NcYkh/KYhgXHUwMDExxZmhdo9jov3bp1x1MDAxMcdozjntR23jN3xQ4Fx1MDAxNzpOfiNcdTAwMTS/wlxcf33qsIw/9TO4SVx1MDAxM0FPxPYsXHUwMDFh63g++H6St2pXq8B8cDBcdTAwMWOXLvOWulx1MDAxOGA1t1x1MDAxNVx1MDAxYVKqap1NUc21lJKV9pxu7cDFTl3dOfw0LFx1MDAxMlx0c7vY1/JcdTAwMDZgXHUwMDA3ZzZFiI0uj6JcdTAwMTHtylVcdTAwMDOoOHdfXHUwMDE4rdQsXHLQWuxzZ4IwvWI2lVx1MDAxOUC4S1x1MDAxZFx1MDAxOL1cbpG8XHUwMDA1j9ZeUYzKfH07k6lcdTAwMDTyt9BuY2TVYOueYqRSem9cdTAwMDa3/ZDJV0+rTFs1eIt3r+fW2qw1f7PtlbN5XHUwMDE5vJekptEqej0/JMdcdTAwMDZcdTAwMWIkdMRaXHUwMDFjXHUwMDFkXFzn8f2ES8XR6XKh6nXLWXAtlj2A/KNcdTAwMThLTfd44ZGceU9j9rsvLWnk5+iRXHUwMDE1j0pTXHUwMDFmRJKAg06WVyQ2qrtcdTAwMDbDo0KnfFxyUfsgUTDdXHUwMDE3XGKQn1D3JNRuwWZCLyhcdTAwMDJcdTAwMGW8/5JcdTAwMTWa97a7orlQ7vkxXHI2WSn0YJJfJtdvdin9XHUwMDFl7Fx1MDAwMnhAWlx1MDAxOewtUnhLe/fgXHUwMDEzo2HCg6Gu0r0r1L9oYFC4c8K2nVx1MDAxZD03m1x1MDAwMTmxiPEpXHRcdTAwMTj81bTUU8ZcdTAwMTT6OMZcdTAwMDeJpVVcdTAwMWJ3XHUwMDFk9PinXHUwMDFlPVh5V13nS6JnXHUwMDA28MMjXHUwMDE0LfhcdTAwMWWhXHUwMDAwiSu4g6TtSIFcdTAwMDaOXHUwMDFiXHUwMDBi6DhcdTAwMDKQhH98XHUwMDE17M13XHUwMDA2iXlcdTAwMGLrOz8/6Vx1MDAxY1x1MDAxOPvM3EJcXPgoXj99XHUwMDAxrSDo8vvxXHUwMDA0XHRcdTAwMDBYXWGy4lx1MDAwMe/5gXl+nklqo5TouT9e7Gjh8Jk/cGrGNsZqq8d4istp3kGWXHUwMDEwXqKDy1x1MDAwMsfA0PBT74KhXCKVLK5HXHUwMDFjXHUwMDE5aSl6wre/TtXQPKCFqILpuONtJXa0IW2n/IYl0iWZMIJcXL9cdTAwMDRcdTAwMWQmaThcdTAwMDFUjdBQ6Z6uilx1MDAxNl5WnywoXFxUdKzLv+fuXGY+4/6buVjQWj/o3Wdcbn1RxIA+MVxiWcgnc6h8nDNcdC+KlMp36mWecHhcdTAwMWZA+c7Jyuo5p8ZLJK06VP2Mq/Xu/dIkMNpcdTAwMTdsW63thVx1MDAxOKRO/vhgjspNXHUwMDE1sEpSzidcdTAwMDRcdTAwMTWMcJJL/1x1MDAxY1P6OGXJ2TL9ppeBMlx1MDAwN1DhXHUwMDE4j4pNXHUwMDEyXGLWfYss4ruUPqmvbV2Qa3WNXHUwMDBmVqqlXHUwMDFjQTU6+Vx1MDAwMX3XyHWVXHUwMDEwXUl5XHUwMDAzrIlzxqM4tZdcdTAwMTHX8iazg9zRJCzvZPhovmNXpUvObcr1krzcfTxxyionXHUwMDEyx7GalkogVNj52k6IZL93tVHyUs9cYmazyTSUb/DXXHUwMDFjqTp9hFx1MDAxMFpR1Pvg9ZzSi7+et+5jXFxIbHp59qHAWKZf8G2h1875673hoZvrWzdcdTAwMWbjfHFxzWMpUlx1MDAxNIXVR12zSK7E6zExvjDYXHSFXHR7gj48LPlcdTAwMGW7qjNXVNXIT4iv6EE65Fx1MDAxM8Pk2+xL2qpRVFdKXHUwMDA2XFy17eZcdTAwMDeMcVRcdTAwMTDGOVxixbA8p1x1MDAxZW5cdTAwMTjhrie+wmog64RT3stcbpojTcPdoLPSRuxcdTAwMTdYy1x1MDAxYWVWXHUwMDEyQPTh7jiiW0hcdHWMOadWIStIxWhlncbHTFmvwYtcdTAwMDS0SmFIdp1UXjz51uGg5KahIPI5XHUwMDA1ejJcdTAwMGZcdTAwMDJJ6ypcdTAwMTFcdTAwMTmxQTLcX4r7XHUwMDA1KKr+lqWlqOOJ3+rFhLrY1iBcdTAwMDdcbm0wXG5cdTAwMWSfn8qcl/m+NG6u0cSMeehcdTAwMTOiY1SVbT68VHLJv1x1MDAxN6HgguEmjVx1MDAxNrSe/1x1MDAxZkeZ1Vx1MDAwN+ODsFx1MDAwMuMlXHUwMDBiYFwib7Xlq4DF2r71QTBcdTAwMGb5RJb9LKXLp89NuVxiXHUwMDAyjlecXHUwMDAyzkVcdTAwMGZcdTAwMGbJzYnM0Vxy97TuSWs1dVx0YPhcdTAwMDHgicOL1lwip9WRXHUwMDE5uSFcdTAwMDBcdTAwMDFcXFx1MDAwNm1AQ4SfYz7e+ZjNglx1MDAxZC96XHUwMDBlkrNt5Vxum6Yh57NcdTAwMWaK2lT79OVbXG58xFh/XHUwMDE2O7OoL4lcdTAwMTBaxXDAT1x1MDAwNVx1MDAwNMiT7t6x7yEpcZJcdTAwMGKRT05/VldcXFx1MDAxMahcdTAwMTVcdTAwMTRnee6x/3WR5dM9XHUwMDA0NHpcYrmwrKH3xYxcdTAwMDdzt4uSXHUwMDAw5n5rt/AzXHUwMDE3bcVcdTAwMWOxl1E7YaaUP++lqphBj6LOSlXqnblcdTAwMDCPgWOTh4bFvWFcbv32d/BghVx1MDAxNJfW9/gs6OvHbTSZiPChXHUwMDEz3uJvj1xmJ1x1MDAxN6s8XHJQpuB6OrDezcZbI1v+R3mkUftcdTAwMWJuXHUwMDEzW0X2WTZ+llvemDyQcTWt5XuDXHUwMDAzcVx1MDAxMb3NuEUt6FTfV1x1MDAxMjK91pjxd+7mreacRaPafXBcdTAwMWG+XHIqhvKWJy/zYlxyJEA/KktcdTAwMTJF4jOeXHUwMDAxzbDKc6NcdTAwMDKKc76qv4ad64zBp0Ts5uUzWDOWsPwyxIajZon9wFx1MDAxYcfk8Cplz2VCs1x1MDAxMyZwye7w9obZcb9KXHUwMDAwXHUwMDEzT4WbayfUjZEn9Ur+9ZzszFdlqfvXO6LxOE5YJ1mPpETVidqML8rvjFxchC3w0b5cblx1MDAwZfApgWxcdTAwMTbRKXyv0Vx1MDAwM7WbynpcdTAwMTaH0WRcdTAwMDTafZF0krBo33uS4YuCsPc7k+tMXGaDKVx1MDAxZqhcYlJI/PuuPziRj1xyYppcdTAwMTG0Smr4XHUwMDAxZNsyTUvuX4KqyNhioVFcbujWITU40lx1MDAxZG7OXHUwMDE58rBcdTAwMGKnXXntgHKBqWScuIxcdTAwMTXVk5T56qCseVx1MDAwZlrEuMI42kOYSq+qmFdI2FjD6Yalr52sXHUwMDBmtk1cZmg1YLn87Fx1MDAxMnNcbsZP6z51i7agooaCO9JcdTAwMDGVJkum51x1MDAwNSTDy1x1MDAxMVTRcsRcdTAwMWZxXHUwMDAwkIaPiFx1MDAxM/7a99TM+lfP9okjrtbwXHUwMDA2vFJzwPJ6+Vmz9ab/XHUwMDExUnMl63dkN4xJ9FF7MKSNXHUwMDBmJaL2Jyhjm4hr2OfOlaw1tf5cIlx1MDAxM/2JXHUwMDBmYVx1MDAxNGrrXHUwMDAwbLmDi2R8QYFcdTAwMTW3+kLK1FxuuWv/5r7+cN+OXbeK5Zm75fm9YqGofFxubp9+wfOBL42XwS1MXHUwMDAwXHUwMDAweH7DkPhA4066ap91LlximZhWnOjPyVx1MDAwN+1cdTAwMDVcdTAwMDCIZL2gb9XnXHUwMDFmJuNuOejXJdOKh1x1MDAwYlxiXHUwMDFlLnTGXHUwMDFhYD53rEXA2zghsqz4/PGqXHUwMDAy44yLINV3mHzK84Fr/nVcdTAwMDNFh+wu9Fx1MDAwMHLcXHUwMDE1gc4tpkKMmdF/qlx1MDAxYTGxqaNI9b9cdTAwMWVRek+QM1c01lx1MDAxM+gsgmrvvTJcdTAwMTff2iRYLVx1MDAwZk9cXMczQ1xuuzWXpLtST6BeXHUwMDEw6VdcdTAwMWVxx9Z46cxFXHUwMDE4XHUwMDBmx3/tNvUpXHUwMDA0O1BcYuuKpqw+XHUwMDFl4T+VT9HcYvBrqkm1rmh9KulcdTAwMTdSxXfRRLL/XHUwMDEy9zHv8XeSOq3Cnr/nfLCael9cdTAwMDTEiZX8oMVZlHVcdTAwMWOvyztvj02U/vTWmFx1MDAwM2E59r49XGK/XHUwMDE24IMjuuVcdTAwMTbnfpzuy4SFI1x1MDAxMiSdM0BYJl+7O5OVOGeASr6pnn6lj/j0SPPzwd+SMaTvfI7Op6bWXHUwMDAwqFx1MDAxYvqbgnr3a0JYUMCxXFzUSXMkMIdcdTAwMDDLb8/jkNmmxJ/+RvTUxlx1MDAxOZbp0ZCe3GQ0kGI/hIn8QSafjsfNgGP9dZQl9Vx1MDAxNWCaejQySKnVfarYwlx1MDAxZFxy5SNxTDKtXHUwMDFmXGKyklHmo1eJgV/tWii5yfv4b6E+r1x1MDAxYXdcdTAwMTImq61eXHUwMDE0XG6mXHUwMDE13nYxpojXYONrhVvgopOvXHUwMDExVIeIuEb+bVwiQoG9XHUwMDAyk1wibFtrnTOb1lx1MDAxZDxcXG3CXHUwMDFhR34zqmxcdTAwMTWn6fdgv/JpvfIrMeDI13axgJhFbDRqXHUwMDE2XG7yXHUwMDFmXHUwMDBlgVx1MDAxOVx1MDAxZrtcdTAwMGZtWPeW+qi5sVx1MDAxZpb0XHUwMDFhXHUwMDBmLWQsO4y4Kkw/6Y7RXHUwMDFmsszXh1x1MDAxZriM/Fx1MDAxMHe4XHRE/HqrzGVcdTAwMWXUXHUwMDEw7Mm+XHI5XHJcdTAwMWKcPiM9JbL+XHUwMDFlYlx1MDAxNrhPJFx1MDAwZtPgsNV+4uegrE1CWC/QiTSRW+CSXHTt2vn5XHUwMDFmvr5jwVUl2faDXHUwMDE4YIVgiPdcdTAwMWWEmeFcdTAwMDVcYu/5+oeq6vTtvuZcclx1MDAxOGxcdTAwMTVcdTAwMWKSzDBrQcaK9so93J5cdTAwMWHLQ1maOXeGiNqW8XZM2lx1MDAxMuRcdTAwMTWfqoVcdTAwMWTqMlx1MDAwYsvTXHUwMDA3eOX7LVx1MDAwMZdxkZVrwo/684V+wLBU1y02+9xhlHTX38pRkkZUTJI3fMzV+0imy86C1ijZXGZ/2N2asqHjV2eGKJ+zZVx1MDAwMe95fD374C3IU0B2n2NRXHUwMDFlVpBob7CFXHUwMDFmWDH6XHUwMDFmjLVAT2zkVHTDQ/duZIxMsXJEMUx25ec2XHUwMDEztVx1MDAwMqBYK5b9gUjs0un53Fxcu1x1MDAxNacjd8KaN4zfPXPC/ITRYtuua9pcdTAwMWLdR5zxiVx1MDAxZMuNmVx1MDAxZFDVXHUwMDA16iXEmf5BXHUwMDFjK+CGXHUwMDA2SV661Yykrygvo1x1MDAxZKWbyfc3qEjsXHUwMDE5eanZq6+2t76HuOJjd7LKeVx1MDAxOJC++3tHz1x1MDAxZcNcdTAwMDb7XHUwMDE0ZYt7T3Ex2GhGXHUwMDE0VntWdvDWjsiWzi0qzPZyJuUj26a5fbgonfxcdTAwMTPWTdNcbo7hYLuU6mqlV5lXJCRU2dLKs+lcdTAwMTJcdTAwMWGVXHUwMDA2XHUwMDA3+292Q1x1MDAwNlTMXHUwMDE1XCJjQVG++k1WQXK2XHJ0o8TwXHUwMDFiOodcYsNcZpaEJlx1MDAxMH372dOqp0tcdTAwMWNvtHaePEI4gIzrSD1zj+i7PyFcdTAwMDSnaGBLbSVEQFLhejmEIFx1MDAxOF+f5r7IbTy+sFwiaWTM0PnS315ERFx1MDAwMkZ8N8qjj1x1MDAwN8faXHUwMDA0XGLwb89cciA1vrZcdTAwMTKhP9BcdTAwMTNJ0i15XGLbWj1cdTAwMTI7IY9cdTAwMGZUrTZIXHUwMDEwWTF/u8SAhZ3z5IrOwIOT3Fx1MDAwNcVcdTAwMWbOmKRE282Fj1bUjXnEPFx1MDAxYTmfYO1l8IrrQ4Lr5VgtXHUwMDE2JTWJcNO33lx1MDAxZHkv+FxuyKbW2q3oruhCXHUwMDEycVx1MDAxYUBQmHeGoK3Q4O7CYaQ+l31cdTAwMTBcYnnyQ3B9OlNcdTAwMGWbkkz166WgsjhcdTAwMWW6LzrJI2iMi37m3715Xeo3Zp9cdTAwMTQof5qo11xiZXf4zfaRXHUwMDFkpkmgJl+DZav7XHUwMDExlyuCXHUwMDBljJeUOmlZXGZDfbAvXHUwMDE2Wpzcn7h991vwRHU2IT7e3ozKXHUwMDE3X3bYOiA3kFx1MDAxNNZD61x1MDAxOdsjjiCIXHUwMDE3gbhq0uTmjkeYV6EqXHUwMDEwmNiWNXi+/7RGuil7KfaMtDVelkplXHUwMDE44a3YY1x1MDAxOKanwd5gmFx1MDAwMM8mo7lxkl2PV5x+zFaPlGnJhmSxKfMtmatcdTAwMWJcdTAwMWZmY8VunDVDo1bjlvHT74P4vlx1MDAwZlx1MDAwMVx1MDAwN8hDqGcuNXuWLcuCf2boI3PyPeHI5JRVY7vcnPWOY3VgXHQ5cfxZ0WRAXGJcdTAwMTgg88k7g0jm0k59SiZcdTAwMDAqUtfEmso98f5bx9BcdTAwMGZ3WD/QIYs/ooyCblx1MDAxNWtSXHUwMDAznlx1MDAxOEDJqTFX5UU7nexkYo8uN7qcfN3RP+1cdTAwMWUrn7eH+9OClZ/Rp1x1MDAwNLYs6XXzbyRcdTAwMDW1wSG1er526dn7t5tm9jyfU/M0U6OaS2FcdTAwMGJsnOVcdTAwMWJ0YVx1MDAwMVx1MDAwMEWRjYtgckBcdTAwMTLG9Zu8fu3AXHUwMDE1OFHBJEG0vlJcdTAwMWasz9dcdTAwMDBKd6pVTnZu7EqC6G9v2Vx1MDAxZJP5XHUwMDFiKMutVYWGa1ZPWfXw4OAj0Vx1MDAxZLpYOENvXHUwMDA2KXDie1x1MDAxMTlcdTAwMTn6U1x1MDAxZXFPTCa0vFx1MDAwZVx1MDAwNHl4XGaQdsJcdTAwMWOk7yS7cXGAXHUwMDFi01x1MDAxYnlfXHUwMDFmkZlQ9DbZR1x1MDAxMU6Jxb0lN3ZzPO49pTinXHUwMDFixDtW3Llr3ETcwM7Rw1d36MZcdTAwMWO2vblcdTAwMTOHwVF6qcrTilx1MDAxOLa/MpX4XHUwMDA0WINgXHUwMDE5QMR18SAlkC1cbjie0Yej6lx0+FFMwGuxhMfyZXtySJipeMBcdTAwMTJPcZ9fUbzCyY0/mTdiXW3t3fyFXHUwMDA0tLBT92eY+enA4drrYIJcdTAwMWGfPYl+PTAmXFxcdTAwMGVVnq49brn+oaJcdLdcYn2ZoFx1MDAxOJFbLORIXHUwMDBmdk9v9FPOXHUwMDBmXHUwMDE0xu+kjbqXYXRPulx1MDAwNMOQmPS1fydcdTAwMGVcdTAwMTlcdTAwMDbUfFx1MDAwM9xcdTAwMTWNXHUwMDExijNcdFx1MDAxObFnp/+0XHUwMDE4mTevqphgVq/icFx1MDAxNlx0O+swjCBTOMW961BSWyRcdTAwMDcwbOuSKZe5vYLgns9O1j660qdT5TleSOTmy8J8UVA+y26lt1x1MDAwNbxcdTAwMGbygZ+kXGbTZy1cdTAwMGYvz9JK4tA+XHUwMDA1N1x1MDAwNTLrXHUwMDE1n3Pwj8LszrJ/i19NXHUwMDA370q/L/2kfVwiXHUwMDE1vlx1MDAxZNAoXHUwMDE3mbddwvHg4r6AobK2XqjykLhE67SAPFVG0WR6dFt2kKcyedw8OZHS5yettVxucFx1MDAxMlx1MDAwZVU+8eJcdTAwMGaKKdVFJaBv1nLA6on9+EJcdTAwMTkyu1x1MDAxZeV29LlcdTAwMWbE/KBnXHUwMDE47EhjsUFcdTAwMDdnXHUwMDFhkp0q6221xnKMy42R8qlcdTAwMTVvpomMJtPkXVx1MDAwZlR3r+HiJDB2R3anXHUwMDA0cvUm0PaG1YfBWfWytpb/oFx1MDAwNEvAmKxcdTAwMDXOzCT8Jlx1MDAwNu2wulQ0/fhcdTAwMWOKXHUwMDE1mz3SXHUwMDFmXHLMXHK4VVvd6Fx1MDAwZkxKiyBJ39H3U7B/h9K6ymnJ0WxcdTAwMWa/zfr8oJEqSUHXLpydmlxy/ezhR0w1anyj+Vx1MDAwMlx1MDAwMZ5cdTAwMGXQeXPmXHUwMDBmXCJmrlt/0eD8XHUwMDEyXCJIdlx1MDAwMiYyNIewjVx1MDAwN4xHzvR9O1x031x1MDAxOcuDPZniUV2QjVx1MDAxYWUrVTQpmiTbXHUwMDExc6DpM2KA+MQuXHUwMDExsINFxEDJ40R1+jbu1vDlvFx1MDAxNHypSdLlmUhml3bagPCe8bLxcslET7esYd2v1X2Cb/tq6NQgkDxJdXPh30GFJDfAy66GRDSVXHUwMDFj+81cdTAwMDT7z+RptHfj/kLHXFwqXHUwMDAwU4urn9jbJUhdNzFCM03YjT5vRN1Uv9CF1tFW/VDNm8tcdTAwMTlcdTAwMDNKXHUwMDEwpS07aqM3lLMpcLq+pFxmrkFcdTAwMDBcdTAwMTB0SWZ004+fsFx1MDAxNrZm2eqcfPA8tz1eUpO9xo8ry1x1MDAxZS7PomuaRdqXOTM6unJDXHUwMDAxrVvnw3MtkX7i5fb9kC8ytWxcdTAwMDU1r8xofiOO9tBcIrxcdTAwMWUwKHyEXHUwMDExnndxXHUwMDE5v0c+q1x1MDAxZrRbrqVcXKaJPlT7XHUwMDEwLftNi/jNXHUwMDEzllx1MDAxNp560VdNNY/iXHIkL6x6bMhUXHUwMDBiwUhcdTAwMWU+lDMsj1x1MDAwMjlcdTAwMDH2POLsXHUwMDBlplxyXHUwMDE1kITGXHJcXFxcfbQjXXi1nv+R4eVIwyVcdTAwMWGvgqH3R9yIV9SEjYx09YctoGc/cN3yee7SSVx1MDAwMqedaFx1MDAwMXtcdTAwMDeXXHUwMDA3KTr2XG7G8WJvnq+/jFxuTlx1MDAxY8dTaF9Xu1x1MDAxMKvDekaMYm6wRL9cdTAwMTOk2i94SFx1MDAwMuVWflx1MDAxNlx1MDAxZt6sMUFf0Vx1MDAxZFx1MDAxYrzOJO3ewL2AQ9W5zbR8XHUwMDAzJVx1MDAxML7ZzDRNpChIWME1KEFd0VfLRmVcZlx1MDAwN/42sD9QjFx1MDAxN3jCWFxcLLyz0EutXSpmXHUwMDFk7NJuJPPgXHUwMDE4pIhcbo0mK2DH+li3jr5DbOpAXHUwMDFjXHUwMDE16YitZtOJclx1MDAwNPG0oKlIcveTXlx1MDAxNlxiXHUwMDFlk1x1MDAxZKHxa3N7vL7ZmWBcdTAwMWJdXHLfo0JWUX7wkYBmycxcbpeXzOaDMYHN12I9YCo9/IqxitRbkLgh8s5cdTAwMTllXHUwMDAzaaGY3MDDO1x1MDAwYuFSSctcdTAwMTDY9vDwooA76cWyWaNnIMQub1xu041fXHUwMDE4XHUwMDA2L6NcdTAwMTSaXHUwMDBiuFx1MDAxOCRccq71Ls5ccoBcXPs7lmWPa1jL5+lcdTAwMWRXNj6Yc0jWZOtDWkFcZjZccla1eu9px32XklBD9YPUXHUwMDA1eM7bSWRcdTAwMGY+h1F8aiAgxZPZb9VcdTAwMWKSSTZwO+Rkj1RyY9VcdTAwMTdnw55NTZ/PZPNnuudcdTAwMThfvnHKepFcdTAwMTfi6MlyrEdoXHUwMDFie/pI+cxcdTAwMWaTRj5cdTAwMWErkJPcRHRi9dCeecPfWlx1MDAxZqu/XHRcdTAwMTI0UOikTlxc2HEsV5vsXHUwMDA39bLNxVx1MDAwN1xiz+5cdTAwMDXXXHUwMDEyvT1pX8Fl6aneXHUwMDE4ubljkJMur1dcdTAwMTOa4Hev+YvCWMM7n8opSVx1MDAwZiqi+zdcIitcdTAwMTEzSVvzgsdHNl74+O17dn6UrJN3jcg8i8ClaXpeXHUwMDBlRjrGPYmtqyDNoSVC/9VcdTAwMDKqXHUwMDA2umNcXIuAk2Zo1VxyneDxuUmFluXOIG6nXGKRKZrCKsgkUKqQ51x1MDAwZVx1MDAxOGPFd96311x1MDAxM+xcdTAwMTVVLzwmYX0tm7lcdTAwMTPrb21UkHdcdTAwMDIkeXJcdJyY+6kwVGImpbpJNnlcXDE5hs+S6Ypopl1t8qw9lVx1MDAwN5AlaHvnbEegvvW4g+5WfshcdTAwMThlhK1+xVx1MDAxMZW5nHIrMFx0T6RJ8S011uTtcruaYqiSXHUwMDBiKpxcXEQtvFx1MDAwZT6M1KRtI1lIJ/BhaCbXsaLqvcz+nVJcdTAwMTDgWVVTUdQsPTUmdoh7UnRLMXdcdTAwMTVF0XHRXHUwMDA2wiHu5H7n0pOgglx1MDAxYuNcdTAwMTlU/VN3XGZcdMeNb7FcdTAwMDZjiMNcdTAwMGWKamtU7cGEoeY8XHUwMDEzXCJ6n1x1MDAwMv30nlx1MDAxMe9AMIbr/PzU24CCunRBXHUwMDEx1o+6ZJYs4HudJ7JoVC5knD9zYslcdTAwMWbVO7zUQtBHXvk4K6U3jFZRNvGjicRI08vR2ILWrO6xKJPKV6BcdTAwMTNujZzxiDpcdTAwMGWD3ZxcdTAwMWGVZubDMCEhRduAUCR1IFxu/HCQmyfXYbDS1k/N3lx1MDAxMn839UeEXHUwMDE1/9bS0fFPfWw/z8jp0eL+ljJFXHUwMDFhoP3C8DxLVX616+xK9/LNS+9jXa/Gr6+dg9VOWlx1MDAwZlx1MDAwM2JJ5HBcdTAwMDbKyk0941x1MDAxZY+nYumLXHUwMDBmZdXhqUWnqWrcTlDplFx1MDAxM7Se4k+tJqVcdTAwMDTfmmtMwXT7iFqacXG4XHUwMDEwXFy5qZuuXHUwMDFkXpPyjlx1MDAwZqtHLU9lauq8rab8qVx1MDAxOYxd71uC9lMmJ/7UXHUwMDAxXHUwMDA3bJf89pm2eOqnXHUwMDA2+WyDuv+rQZ5++ld/ddiQ3zploXS/5zyL4PP8a67Kf6+jPqzwXHI5vz1Def5bMyj8UzMoUUzZrb9cclLv86NcdTAwMWaZNLxcdTAwMGVcdTAwMWLvt+ybb35KutfnPz1SO+xbk/k2gWj+51x1MDAxYza8z2Hppf2nXHUwMDA3dutJ3zp0UeEu/PFT0ydxv72rT07eXHUwMDA15rdcdTAwMGX9p8Bdkt9cdTAwMWb3LfyWXr+zrytcculcdTAwMWFMkfbTu9Shv31eq+GR281vkSFcdTAwMWR+17K60T77+O1vmtPKfU5tZnk+/9SqaoxPUVx1MDAxZUfu2k/dZiOx5s+GOC7hq+r33vPXnzhcdTAwMTmJ3ea3tfdGgd/hgJf+qf7q5JPf8S3/6mdbvcXv+Fx1MDAxZVZVXHUwMDA0f+P7dn69x2e9c2r/nVxmKvytQ9fGNNx/a4DV/lu/qHxOMpG/LbclwZKa+3ovVT3Tv77V6k+PcI855uKvT+qv1m/0nK5P+9tHnIG+NY4r/VtcdTAwMTNdUlx1MDAxMSHTikLh+oqqj1x1MDAxZU2A5yXylU3/1bFHlGt0bMuqT5Dw6sDpodTUhclcdTAwMWJcdTAwMDVLX7tcblx1MDAwZmdfXHUwMDE5+Vx1MDAxMVx1MDAxZZdcZmFf8Vx1MDAwNOcj8KpGayOiScKwl/eRkHBcdTAwMWShl1x1MDAxOZjSTVx1MDAxZWxSXHUwMDE3fGxK6Tfxrz7BXGblVKXB/quH+odcdTAwMDboXHUwMDFic1mPXHUwMDA3XHUwMDEwVHl9XHUwMDAzLKm+5D9cdTAwMWKgLFx0NPmuXHUwMDFjXHUwMDA23ltmNTU03VxyZ7hcdTAwMDddza/rh404Rzf4XCK4XZdLnDfWK8JcdTAwMGLU6X/694JcdTAwMDcluZZwJFx1MDAwZdBcdTAwMTbd5pjYV8fqb/4ogSbutbnx0GCIXHUwMDE3pjFSLVx1MDAxZNyLT18vyfo19W9cdPPHXHUwMDE5XlajMGSrylx1MDAxZP5K3ahGnlx1MDAwNWGmKFx1MDAxOD1cdPAxM//4XHRHudFNXHUwMDBlZlx1MDAxNmDDm9uNXHUwMDE2ymnB+KF+NVx1MDAwMb41zGzIn7xN3zGnYf96XHUwMDAz7z/r6sXqXHUwMDE1t79+a6QzuWnUXHUwMDFksynHs+lcdTAwMTf7u87U0Vx1MDAxNz/jknhL/v6fdJzOZ/vrx1pzx0PG61x1MDAxOD75XHUwMDE5y/9iL6X+XHUwMDFiM0WnSeSfc8Ty5zrZNVx1MDAxZKTwq1x1MDAxZCA033jQJ6k8dz/1ys5cdTAwMWRvKN6WqUWQst9cdTAwMWH09qV9a+1rSbiMv1r77Ouqklx1MDAwMFjvvfirXHUwMDA33r71wMRcdTAwMWGY//Rxx35zXtp0NPVXb2v9uELbP1xm+rcvslwiXHUwMDEy37jw2fYwpf7qvrVvrTZH52KI63/6XHUwMDE09DdWfvdxf/78zv71u352396fKavf20WeXHUwMDEwXHUwMDE1f73UiV+fz5fclH7r02nvR4yCXHUwMDFhfL3pf3pES9++35XcfFx1MDAwNsn9tVx1MDAwMVx1MDAxMfxcdTAwMDZaglwixaWRfs7pvfF7jvd6JNev3/HVt1x1MDAxZVlhQsxswr/e18r3XHUwMDFjjVx1MDAxZiZX/o2hxc/eP25USO//qM02fmOQfif631x1MDAxOET91GajNPGPrlx1MDAwMPKdr5Xh/5z5fma1XHUwMDE0/zG+n4nPuUv5hH+9lqlmp1dp5lx1MDAwZsT9p1x1MDAxNzRtlLfvrf/0gsYo6I551P9cdTAwMTnzPuVX10bwT+Ih/9RnU/T27/esmvzbJ1x1MDAxZEPN0vvruz1T2YN6XHUwMDFil5O2v3pcdTAwMDZcdTAwMTj91e+ggT19XHUwMDBif7og0K8uyCNOq/BP8+A33pXn9Geb3K9tpo564H+2STc/XHUwMDFjmFwi6vlXXHUwMDFixGGqn1p6ZuHnTPmzTfNrm0+5vew/24x/0ohAmvSe/tnm86dW/UMwbP/nuP1M0Z5dTlYmWvt33Fxubfe3PSy0/za09898XHUwMDA2Uvu93VVu1PPPpvfvXHUwMDFhUi5GdTz2i1x1MDAxYlj/O4fn0GPAqf3oamCMTFE2n+JEYP/0heeYgcNve1x1MDAxMVx1MDAwM+75N4/l0H/vb6lcdTAwMDchuj/3V4Fvi2ssXHUwMDAw2kT+zbtW33x7XveTk3S/Wlx1MDAxMTLw7SOAWbmU3Fx1MDAxM/ZzzlB+z5HUXHUwMDEz7+yfc1Re8r6XPsZCXG5/ztn79P7BxSb21Tm/mlx1MDAxM8C3np2gXHUwMDFhV/nLj8ivryRDXnC/+ZGCf7BZuf+DXHRM2txvj9ipual+dWPu+aCpunk8VutzpFxmXHUwMDA3cN4/duL0PnX6XHUwMDBlg/hcdTAwMTeCTMlXJCTesqii7T9T/s+DsiiRTjzqx5f+4zCo6zuOXHUwMDBlXHUwMDE27tX9jWv/eXj/Zc//fvxcdTAwMGbb/m/H18+of/Q7/uNcdTAwMDC+a0XQzUt53MDnx37+7bh9OqWA/2VcdTAwMWNcdTAwMWTm3qfvbeZNXFz5P5/jV1vif47jf9GZ+G/HV0Pmf47jf9GT+c9D5fmD9XuI9SfnnuCZVyWhZ0LuQori9F2PRVx1MDAwNFx1MDAxZJzmM7zD8kjjXHUwMDFhQVskUtXOsMlcdTAwMDP7LFx1MDAxNiFcdTAwMGZ9yGFcdTAwMTiL0cKZbs2bgrZcdTAwMWLi0OVsg1csv+e6TKQqZF9cdTAwMTnlmVxyTsvwa5fo+49Swlx1MDAxYtW1Nfvtd1OZv1x1MDAwYsrXUVx1MDAxNMtK3/o0QT48iyxsSo+qOVXjNdZcdTAwMWGRsT63kj6fXHUwMDE1XHUwMDE4a/ihUz3PSWY4U6pcdTAwMGXaXHUwMDBmN2KfzEr6QJJIXHUwMDEwd/ZcdTAwMTVcdTAwMDW7u/Keq8AhrYT2XHUwMDFjmTDFd/+dXHUwMDE0W/ROoqB5TPv6xNGbgt3/6ihBXHUwMDBlUcqAfPtBaIN5osApo399dFWI58Nr6J3F/vQs+Fx1MDAwM76W5lx1MDAwN3YmJeEoVPPV/HAgw7j2O45TlVx1MDAxNL4wuFar39+51b0kL/7pj8VcdTAwMGauq/jaN/Z9WPnKPu6PXHUwMDE2yFx1MDAxOeyS0dNf0Mx4XHUwMDE0aiNC+ovl88DDlpnqLIqzXHUwMDAxYeO4YlxmvrpcdTAwMWJcdTAwMDXanktOld/fRWPjvCiMvmPTXHUwMDFhU69cdTAwMWLF+eZCh8EmXHUwMDE58Omv/S9Q+qePkb1cdTAwMWWPnfxeXHUwMDFjM8OyjKSf+Mm4XHUwMDEwIGC/OkJp5mGPQfryhF7xOEBEfrVSXHUwMDEyjnt07m+8u/FMXHUwMDE0JL+44KtcdTAwMWZD/Xv+xfhcdTAwMWJcdTAwMGJIVcPf6XlfiF9NKFJcdTAwMTBcdTAwMTVcdTAwMWX8yXdy2vn7bPxqMfGGKPjRN9YorOk9mbL5nvM2nDP9SPk3J6Tiyv3yXHUwMDAwOs3SZ/QjxyRg5p3P5e8zN1x1MDAwYlvPxDf/SZXA53p4YsPP72+1IU5e+P5+0LFcdTAwMDedn5/+45F1pmjk/Ve8XHUwMDFmqfQ35+tQ9u6kXHUwMDFm/NF9YENNqG+85Yr3dNBcIv758lx1MDAxNZF8zFNQ/vRqsW+MxFx1MDAwYv5P321WZq/wwdyxnLe5clXO4nMvXHUwMDBlT6uZaPzGTfGV56lpM99cXIvcWMz5rsFtO6T5Xn81UDrxZVx1MDAxOLHzi1x1MDAxYj60+SarXHUwMDFmfZL2oI1cYjq/uc9LXHUwMDEzXG55IY+fXFxhplx1MDAxZoxilTvLWoxUaSxjtEVcdTAwMGJy8/v1MERqXHUwMDE3iZvjKjqu2+ZcYtdClVx1MDAwMIRUq7VdlqPMTylVwHC/z3nxwPV3YWvtJlx1MDAxZlx1MDAwNTvSKrr6N+GImoivXHUwMDBmVNfaRVx1MDAxOHKiblKznVx1MDAwNFx0MS9Yo4FOiKndp1x1MDAxNsCh73TpffXEQpvDb2Jz5z7Zqlx1MDAwYjHK1J93XHI9b1xc81x1MDAxMu0yLTFcZpKw+lx1MDAxYqLQXGIwYaz+LOFcdTAwMDP2IFxm2EysY1x1MDAxZiV/XHUwMDA2mfskXHUwMDBlyX3aXHUwMDAzzFx1MDAwMrjdTzw5qELRXHUwMDFm+Vx1MDAwNmz0jVx0RVxyYbFcdTAwMDVcdTAwMWNDXG4wX6g5n+WSQNjRv5AgXHUwMDA1etuB+5ROXXDHXHJK0fo1/EBO+uWJKUdy/p+eT+o9gpO7Kb5aoSCgxbr3XHUwMDE2zlx1MDAwMjy51zzRYrZCcZ2chNtcZobm6J91L5TXTWhcdTAwMDCtzLvrRrPaolBAormQsj7JT5ItXHUwMDFmmjJcdTAwMDRcdTAwMTK9OVVg7pzHmccg///Wt3zvlMvjXHUwMDEzzbkotLBhL9lfYt9vXCJ/tZFcdTAwMDI+IFx1MDAwNJOliZjoR3zdzEl4XG7oYPcyk4Tw/VxcvcTuZLwwN66blCysqVf69VtMJVx1MDAxNP1Ho02BsMcxc/rBwu1Nn1xinU9NkTZN/UHuT8TcsdaVa3toMFx1MDAwMVwixUPr5Fx1MDAxYuJcdTAwMTZFhEKyZX1F1cyRXHUwMDFmMHb/2O701SX6yS3WXHUwMDBmXGLlMe/GN1x1MDAwMlFy4/LBXmdM23ru3mzZS/HWSVwiVV+xXHUwMDBmXHUwMDA3J+CnX3PUMffrfuBcdTAwMDZcdTAwMTVcdTAwMTW2Nlx1MDAwMTDLKaMwT0LDRNx/XHUwMDEzqe3m3VEt5tvosFxmIzt9hfxcdTAwMTLCMs2hVKeLXHUwMDBiMl8jz3ePT1xmJS1cdTAwMGbjTrGTIPFTXHUwMDBmX+vvULlGXHUwMDE4JnuXXHUwMDA2/NeIQ1hKxlx1MDAxNFxcIUusm1xuvoiy/8DtoiRSvVXVV2lcdTAwMThiklf2S2ObwTw9xT/1Zilcbob90XdUs+2+bpeU4/BijUlcdTAwMTg+UFwisarjXHUwMDA3++tcYq1B3cEwI1x1MDAxOS3J1lx1MDAwNTAhLlx1MDAxNTm2XHUwMDE3X+r3PevVXHUwMDE1k4GKxJvunuwx7GjX+IuAvCYgRm8yaI9kXHUwMDE4NvKsLlx1MDAwMrxY8exb/DnniNBPOFx1MDAwMC1cdTAwMDHUzcK60VFg0Df31mP6o+1LXHUwMDE1btdcdTAwMDZwXHUwMDEwzYXYd8P7XHUwMDA17vVOzzKo6lx1MDAxZif9fVdgZLnUg958482As0P4TIuDU+u+tVx1MDAxMVP3akOzV0B0X1x1MDAwYvXV88eWlG9cdTAwMWQ8XHUwMDEyrKsy6WNcIlx1MDAxZo1Uf7b11GJEzD7GhbqLKKHyXHUwMDEzysvYMZ9cZldcdTAwMTeXy9bHsLhQTOeyXHUwMDFhbZVGkT3GbMOYJKZNXHUwMDA0MeFAXHUwMDEzoO9H7K/9mS0oXHUwMDEyMM+iaMLVvGd861wi6ajtKiiHgPNfXG6SPbNYXHUwMDA2XHUwMDE2u5CXXHUwMDAzSY5ne1x1MDAwNXlcdTAwMWWDXGLeXHUwMDE0N793aujpXHUwMDA3xYJcdTAwMTNcZjamXHUwMDBmuz9cdTAwMDCTXsT6ap5JlcNVqjxcdTAwMTMoLtO96LKgq0XXXHUwMDE0TLlVulx1MDAwNamJXFyDJChJo9nb2NWYXHUwMDAyreX8TK8hxa9x1HthXHUwMDAxu43spYFuwMYuaphcdTAwMDTARVx1MDAwZkDbnzD6YLRq78h7QlVmu4gjYdB/11x1MDAxZfzaPPtj83fORZmRIJTeV/I6XHUwMDFlXHUwMDEx88CyWXgpaFx1MDAwNSDicWSZhsijU5+6qHp11JW+XGYvzpXj9ujuz057XHUwMDEyS2A6JL1fPVTNKvhAXHL76Y/+XHUwMDA0t66Hhlx1MDAxYuOZI5k0o1xiXHUwMDA0XHUwMDE0XHUwMDAwXHUwMDFhriv0rynYd2VWK3aEJZLtsbKcg0fL1/PHxnVRv5e4U/nqNPmMb6Q791x1MDAwYqzbdlx1MDAxNZRcYlx1MDAxMdjwXHUwMDAwXG6Fbv7Bw1ojXHKmelx1MDAwN1x1MDAxYa5rXHUwMDEzN8JJXHUwMDFisHtDvdRcdTAwMGaQ9pRcdTAwMWI066cm1opSI1hb3I9cdTAwMTBeY1x1MDAxOEMxvN3uMJ9NxGzfmNjEb1x1MDAwYpU49n1pOza82pK6WutcYlenNp4ju1VcdTAwMTiN4XXjXHUwMDA23SfDYGBzS1x1MDAxYs+khVx1MDAwM4teeOrlwK77XHUwMDAzMLbNpCt9V3KX68QpNbdE5GFKZjJeusCi6tBnSlxusHNZeObSXHUwMDE1uHPTp3qax9iCxGCD51x1MDAwM1+0+H2UXHUwMDA1gj+X92JcdTAwMWJcdTAwMDTdXHUwMDE2dYeix2tAXGbRXHUwMDExba2eqW+fzv0xpcRTP7OeXHUwMDFjyUUktGJ4rKXezd1J7F98Z6jKK2VeN75cdTAwMTNW5Vx1MDAwMFx1MDAwMFx1MDAwMo8pRlx1MDAwN+6kk0f0s6JOXHJHsLFPWL7aXHUwMDFkcWbVMNuN6EJxXHUwMDFmXHUwMDE2nsdDkNVCt5522+TOkZtcdTAwMDbNmUbs1uh1L5DLd1x1MDAwM1SnXHUwMDAy02FCr3A63T5cdTAwMTRRmj5cdNsl23XnoYxw6/2ZZ3RoXHUwMDAx2+ZNXHUwMDE4ybRRLnbpXFyRq+DnxOpjXHUwMDAwXHUwMDE4Ya44dlx1MDAwNKvdXGJ9yjRcdTAwMGJ4kqtT+pEugWw/XHTTa+TfT067NH01vFXBXHUwMDEzU1x1MDAxNXf2edRcdTAwMDKZPWy7uXCZgqpQXHUwMDBmb1x1MDAxNGzhXHUwMDE1Tzx5831kZYgv0iasb5+X/JNkrFruXFx+b5jwXHUwMDFlXHUwMDAxVopvPZ+XMoM+pnnGXGJDuyDY9PdzMFx1MDAxYTJhsDhcdTAwMTMxXHUwMDFmkin/ZveNalnxeL/Tl7JYslZfmT10o/WOVeyh7rph8tY4ofzONtPUKC+u0S/gXHUwMDFhXHUwMDFly1x1MDAwZXZcdTAwMWRCtaZMlO9UP4/MLEBm3lx1MDAxYj5qt5zsXHUwMDE19Dqb8ra56nG6ZfWTfzJx/X1/i/J5Rry9XHUwMDFix+5lXHUwMDE0fkJfM/hFu/C0U8Q811e/pcua3lx1MDAxY/VcdTAwMTip6UrqU9tcbvlcdTAwMWWNzIhdgrrSw01g3Xxak9h5a1rHQ+t2Sa3xrEe+3oBcZtaa9Wo1sNff0jXBb/5cdTAwMGVCe33nUlx1MDAwN3TZbFx1MDAxNrHmu8+24Fx1MDAwM+BcdTAwMTlY7nuBcfeNUlx1MDAxOVx1MDAwMsj2XHUwMDEzwq9cdTAwMWGLpIaN70BXOtjQvlpvklxmpyxeuO4qduebbJ2BXFyYScG1SkSq4TxNoMbuS0zWXHUwMDFlmspubU5LLlx1MDAxZjbONfDXfKMknfP0mii8bpqIVZtcdTAwMTJju1x1MDAwMcHMNt8tmfJcdTAwMTFcdTAwMTMgn8VcdTAwMTdvs2YtoVwiQ1x1MDAxMFhccqDvXHUwMDAzgm/7gS/8zlx1MDAwMkKzdXw4JmdcdTAwMDQkaZGyY1LtdmkjXHT2Oatx/JCX2r1cdTAwMGVjXHUwMDE1xd2YS8J4Ylx1MDAwM0v6/cd90lwilVxuhf2WeuaLb2XVwp4m/X3PL73fQe1rq1x1MDAxMnIlR7M4b/j7JrRcdTAwMTOTLr4/0ediXHUwMDA1k45UMlxcWI6IXHUwMDE0jHyAQpjW5VxyXHToRtJ3t3/YhuU8V5KzXCLgM2aueyPsvz0gWWxO4VdcdTAwMDRCuqNO/1x1MDAxMFx1MDAxMTlcdTAwMDCRiFmez1x1MDAwZaZg5Vx1MDAxYdqwrPmiXHUwMDE40ZZH2Ytg34SRtWScJXNAnDeUXHUwMDFjvO9eKP1Mq1lxpVWHP1xy/oBT52F02sUvMNFiczItXCL/5N7smU3gXHUwMDE2+cUr//Y/bVxunSGh4pLzRVdMdIg7RTaum6C6XHUwMDFmn4FvrkxVZ/9cXO40XHUwMDEyvWd8TVZIXHUwMDEydSzzUuZcdTAwMTNkXHUwMDE5wUdcdLrGRa3lmHyh2JZOy4XxlIG+z6LUOfPL7Ugmp6+HTp3WTURkR1x1MDAxM1x1MDAxZoRh/uomotH7OE6r4ZqQ3jtlT4EhXzXDeIqEy8mwwaf0K1x1MDAwMVx1MDAxZiToaCxcdTAwMDVWnoqdY2snXHUwMDFiWlx1MDAxNVx1MDAwNNeoUvbNP++HuyBpL1rpmE9cdTAwMDXHXHUwMDFiTlx1MDAwN1kow9uMboCHWFx1MDAxM29cdTAwMDZMXHUwMDAzjUO6XHUwMDE0XHUwMDFjXHUwMDExXGYjZuJltI9gilx1MDAxMFx1MDAxNlx1MDAwNFx1MDAxMfdcclx1MDAwMIA7Uzr87EFQXHUwMDFhsTGgaXpSzk6EykRte4aXxSg52oRDjtDYkEI4dOjGSFx1MDAwM3eRJiWyycLihGOVXVx1MDAwN76+XGLFaVxm9Fr4cE3f16N9XHUwMDBlMzpliXmR23VcdTAwMTBAwDHJ+9GqJ1x1MDAxM+v9k5tmZFx1MDAxNW2Xs4o1lvy2f7lcdTAwMWGzXHUwMDAxoHtcdTAwMTOt+I5XMuzK78VSSWB2yqhETdAh82FrZVYlXHUwMDBl/qNcdTAwMDFq8Vx0yjq1ZL+Q+FmJfvioQlx1MDAwMrmGZzcnJ+BcdTAwMGJNo1x1MDAwM+WoWUbwJLnXWFx1MDAwNPmoXHUwMDFie/6qb3Seoq2PXHUwMDEwiLhHs9Isp2dcXC+j81x1MDAwYpasmeuzKXNgXHUwMDE0b/WjXHUwMDA0JI2anlx0oEnXMyqOrFx1MDAwNb4rVVRofcIv95q3td7uZ99o5uW5yquhuZf25oJcdTAwMDF7xY9EWLGjifQl3OL45jLSfJuj7K/TbFwiLlx1MDAwM1x1MDAwYsDSsv3ik/6hXHUwMDA2XHUwMDEyXHUwMDA2gjlYXGJ247PsMlx1MDAxMlx1MDAwZSNcdTAwMWODSa0qbWMwlsBKbIPaXHUwMDA3uTA8U9New0M0aFBK2sGYwSqaQtT9JFx1MDAwZaY41OSBPlx0JLSn5DrFXG5cdTAwMTl4bHnnrm+lpiBTO5AgXnyvRTQqXHKIaFx0elx1MDAwZXjSXHUwMDE4tOfKQqLw6e87XHUwMDEzYzyAPMXzXHUwMDFmfU1cdTAwMTbMojdWUY1cdTAwMTBcdTAwMTDFXHUwMDE4ylx1MDAxNvJ0Olx1MDAxMVx1MDAwZi6PpC5cdTAwMDTnqZn5qXnAxVwijVxiulx1MDAwYnB/uFP3XHUwMDE3gFx1MDAwNPVZNovLVWOINqrOatU5ePD1QGXntlx1MDAwMVxmZepcdTAwMWXmuYOxedewW2Tjz8aXcT2J0lx1MDAwN65cIj16vVirs89iJC/sxGJevPBcdTAwMDYlV6WQfN5FTj9qdDcuxWghvvugXHUwMDE3VDvglNby/iiJMZEyoVx1MDAxODp0XHUwMDAwvc1ZJo33n9lcdTAwMDdxglx1MDAwM4KQUFx1MDAxNKH624/swYW1ueAwcEc/OPTgyahCl9baJ77xQmVb7c/3vtFVl9mwf79vYMr7cGNbk0KKP05cdTAwMThcdTAwMTIus/kwXHUwMDAw4vbAmaYk9DaKVuRcXC+Jy4JYXc1Ns5baS+PxrftcdTAwMDFs60qm+flcbl904S+MyMXGzE6fTq9wNKyQLttVRm3fhnpcdTAwMWJB3Fx1MDAxYesxS2Z6m+1BfMLVksg7tUnv6DVcdTAwMGXtmlTLTKeOnzgx9HyEQkX4SkSELaaK9mssuERNWveJKZVa5Fx1MDAxYSDYc555kVJl7XWJ9uFcdTAwMTX1XHJcdTAwMGXnxdLfOFQvXGKUV4JcdTAwMGaudcSmncdKtrBx49qNXHUwMDA3s9SNsi8qYUD1Y5PEs1Sl4YnuXHUwMDAye75tXGZjaG+7wdUnRPrXnvfBTkrxgG7Ls8LNcu9CZZSeuXF+siF5U6P8muzt9XQ53eKuJpK0N3WnXHUwMDFh08VcdTAwMTKWclx1MDAxOJ6iZ7+dhDgzY4RcdTAwMDD9Z6GpxFvBz6060kXDZ6T50I9scrBcdTAwMGLapvjVdp6Bmn7Vo2BykCRcdTAwMTa+dmeRX0KHnK49M5Szl+jWWTXP197wKnYselx1MDAxMc6YzHFaoY+gmYaxnmNdbthv/Tby06slXHUwMDFh2ya5Oe7NbkxAzbTckKJnmLCwXHUwMDA1c6nlcrPMXHSFuXzbgEWA91x1MDAwNjv0JoBcdTAwMDOKxlx1MDAwMZowZU1cdTAwMGUjbU5HOXfrhrtcdTAwMDVSXHJOlKZ9gEzKt6delmuPvoudXHUwMDAz9Fx1MDAxZs+bqaJcdTAwMDBcdTAwMGabuiB+XHUwMDAyZVTquqivlLbG18Jcbt5rp6VcdTAwMTREnlx1MDAwMshXS5B36cIn7EWWQ8JcdTAwMDOP0LaUrd0kroczXHUwMDAypVxc9MlcdTAwMWZ6njMvMXBv9Pn0turm6G9cXD9cdTAwMTBcYlx1MDAwMY5Xs8KeXHUwMDEwhLlRkeZrOmcsk831XHUwMDEz7XmuvY9cdTAwMThcdF489eKRSf+4IX1cdTAwMWI5Jdo5TvEnXHUwMDFj9Vx1MDAxYmmue23j1FglXHUwMDBlV2VcdTAwMWJcdTAwMDby/KelPmZS4To/XGYy8Ez6TbRcdTAwMDFAwKw1gfpkXHUwMDFlXHUwMDBiiLpcdTAwMDFjXHUwMDA39awwXHUwMDA15nE8gIfSd9G8tXbWNZyopDDIhux3L2fg+OpNyK2Bh6Ch+Kfe3LkpaMw/7TW9wiOWjay+LzFv91RRb5w4wMZS3q3dPIdGkbKu2HvJzEbbZu9cdTAwMTVMXHUwMDFkWMSc1IlG9IRjY79cdCy2hk+2qPFcdTAwMTZ1U63ejeBZSVjloFx1MDAxNIziXHUwMDBmOudK1Xtx6lAvTydcIl+A1qWAXHUwMDA06is14uKZQKVP8L5aO49CMUlcdTAwMTTpXHUwMDE5/1x1MDAxOeg70ZMpKi6dyOKhT7/lsbJcdTAwMTiry1d+47BcZlx0XHR6sy/VOqBcdTAwMDCDlceNgXMx11x1MDAxMJTZ7KJVXGIrYVhcdTAwMDeRn+52iuNyc7iXmWreXHUwMDExa6LZXHUwMDA0uDzBq2c757a+vzLz4PpcIthcdTAwMTAt0s+s7NxcXD3hzX1MTcP86Fx1MDAwZity+upDqueBk++seC9lmlY/5MfaiFx0ejBuXGJcdTAwMDdcdTAwMDZcdTAwMTRcdTAwMTSZ2OvTVCqxTnJcdTAwMDaxyczLTKxcYlx1MDAwNj/845iCjvhw4Z1cdTAwMTJcdTAwMDTC0FxySNSwJG/G6VSdM1x1MDAxMTHSeOZfYUDuqYmvmH73cK2pN2lcdTAwMGKbg04rf1xu/Nrg6pm03n56jCDTlFx0XHUwMDA1jq/bz7RcIrh8/nJcdTAwMDaUnoFcdTAwMWSPNd/m2ClZTl5+e5VcdTAwMWaUXHUwMDAwpL+FxyirmCnANzXEXHUwMDFj/1x1MDAxM4SmeL1qXFyDXHUwMDFlSt3momZ/nmrIWJvMwsI6ot5BkF5zQ8GbRW+fWOu0LktcdTAwMWbe0LROYICTi8FcYtU81oKLb/RcdTAwMTc/pNZellx1MDAxNpCsMbhcdTAwMTiYoD6cTZ1yXHJxanouXHUwMDE4mFcnIZnnWjihQqZcdTAwMGVcdTAwMTFcdTAwMTHFlCDbe+mh9UXSXHUwMDBiT+WCgDHLJpiiZ2iI4GLDXFyNPCt9XHUwMDE4cCrF1/hpj/iZvvArpHAy6cJ1Q0TB7uLHQr/MdTxsoW/oXHUwMDFhT7C0l7TBuFx1MDAxN9RpNVx1MDAwMIRcdTAwMTZsvsxaXHUwMDE4u2dcdTAwMTPQ3qFcdTAwMWMzXHSPtfZULFx1MDAwMsBdW130LlxyiP1RQTv20Daio9+sZeZcdTAwMDWxkKzxrJY3oFxc2CcjlvaerYuwKFx1MDAwN8+eXCJYXHUwMDFlpkfg/m1cdTAwMTfFp8f+ZVx1MDAxN9idn2mwPsxOMd0m+kS5iVuN0t15ZLr9XHUwMDEzfCjdOVx1MDAwNSP+UN6JO1xcNajeXHUwMDE5eU1cdTAwMDY9LbfTnt9v01x1MDAxMfOSM56Lzry5SYFcdTAwMDM7X1hQYGN9xplNPHZcdTAwMDMjc/eNmHQqlDv31T8rm2GRiSiVm6dccmOjJZvOU/aGdrRcdTAwMWRHUl7OXHUwMDE32qfmoGzaXHUwMDA3UFFCn1SiqGHrXHUwMDEzxdt5r6FqKDiER4bZz6/9U396qIJYnYHQcLlcYpO+WaVSXHUwMDE2y1x1MDAxY95cdTAwMDSLiEzDfWOGlt3zW1x1MDAxN10wxLOA19msXHL6lboyYXlvQXhcdTAwMTWPXHUwMDE5fmF20K5SZThCrIdcdTAwMWamy1x1MDAwNuGpt8lcdTAwMWRcdTAwMTbEefBLLoquXGJcbp9tsFx1MDAwNi+OJYBcdTAwMDdhUzhoeKSQXHLaXHUwMDFiX5A56b1cdTAwMWRfXHUwMDAwVPLS7lx1MDAxYdh3OzvPQcf6m6K+aC9adVx1MDAxZlmUoFx1MDAxZlxiIce/71/Jlq07Vj1unDa3jzSZMJBdeJxcdTAwMDdPRaFcdTAwMTR2pvtsxc7tqZj2Nlx1MDAxMf3goTWbZSW4XHUwMDFmtrVM8rxV/otcdTAwMDAt0Z6eXlx1MDAxMlx1MDAxYydvvpbMJsB9ieF9fOXk9I5cXPA+j6tZXGJcdTAwMTjupL+Hmr5/v3U0yvdb024/PKqRXHUwMDA0gMhcdTAwMGK6uUm5VMvd4eeOf8mL39tvJy0qwi3HXHUwMDA3kt0wXHUwMDFlbzZ2hk7wXCLSdWudsSjJeo5ALFx0Mla7Y5VgmjpcdTAwMTP0otybXHUwMDBmOv1IUNA1XHUwMDAwtaKJ78JC6IxcdTAwMDaLufn7UTJmwc2E/lx0XHUwMDA0y0dFc3yDs46Gn6CHXHUwMDBifYyl7lx1MDAxMlx1MDAxNW1cdTAwMTfdYz7UKpjMN/DekCvcXHUwMDEy+GHo9mpALXOMXGLHiW+VYZv3apKOnWjtYCzcjDyMd6Z0XHRvPlx1MDAxMFx1MDAxMUGlnSnOPmr3SvGhgsP6V8cqb1x1MDAxZi66T9o0/G28N1x1MDAxOUd55Fx1MDAwMGJcIutvUPQmj+3pO9ylXHUwMDA1KFx1MDAwNuzVM/7UN1x1MDAwZrItS61KXHUwMDAwTHZbhUAreVx1MDAxM6twMOzqPyti6Vx1MDAwMaVSKp8koSz0blx1MDAwN0JOW8knXHUwMDEzJWafYeA7Uts+pVaVn+uCXHS0S0tcdTAwMTRrRVx1MDAxOFx0XHUwMDA07lx1MDAxZFZ9SqbGdYxg9j6K0HdGXVx1MDAxYsy/4YqTgZL1LDuc+Fx1MDAwMaJcdTAwMWF+p1x1MDAxMO6puFwia25+wDvZV69cdTAwMDKvqdtP4cWab4DzeJBdXHUwMDE2rTxiXHUwMDBiylwii1x1MDAwZlx1MDAxMMCLNFx1MDAxMVxymlx1MDAwNEHQMme0XbhMtWg2XHUwMDFmXCKGOM6H8PLtMUKcXHUwMDAzc+tQ6t1AZrtcbo4mSuTW9m3i3TZ5XHUwMDA3Wed9aNM7kVx1MDAxMGZRWFx1MDAwNlx1MDAxYVs6oj40XHUwMDA0JOvMNFx1MDAwN81cdTAwMTFj73OJ2ozVxlx1MDAxOWOE3EzCs7jJdcAzpMZcdTAwMWGwQS+qXHUwMDAyXHUwMDBiNj9cdTAwMTV0XHUwMDFhu+RJvScrXHUwMDAwVqQmXHRYckCwVsqogaabXHUwMDE2XHUwMDE42WOF/Vx1MDAwN/5Kc7dcdTAwMTFaXG7yac7FwGlbKnBcdTAwMWVcdTAwMThkyMDz+3rKXHUwMDE0aK5IIIE0XHUwMDFhz1RcZnH6YNrtY/5QZllI5Cvjmr68POg7WFx1MDAxMyBcdTAwMTnt8vQ8VyTqslwimFx0eYJcXKSNNJ5maOlkc3BmbqhcdTAwMDO6U3+IrITyr4NIXG5cdTAwMTOrsXQ1acvn1Ic74/BNo0ym/K5cdTAwMTXdydhcdTAwMGa39KJwLmPGvbl8ospEWzrE91tcXKrxXHUwMDEyi81cdTAwMWZcdTAwMTH9qC3yfK56KZi17Gvwq0dcdTAwMTeHgmFcdTAwMDB5PSQnR7un20C6jmBcdTAwMWOsIFx1MDAwYmz7yX5z9mi0Z1x1MDAxZGAlz9pG+DLKlzVPdy6V1L2+XHUwMDFlXHUwMDAxNCz2WKFLttBisuqrWlpB51LsvZI8Mzz0XHUwMDE1LtPCesuSX2RerIaPg8wnn1qWh5dbz+y97kmYXHUwMDEzZ8JWRJjDqZ3x1s3BzV1Dklx1MDAxM9Ggyn4z/Iwwu3DVtXz2W9Tsm7tktC6gYmQgr3euuZRvQjiLXHUwMDEymuJBLFx1MDAwNpTQPHEpzWU9Y63nXHUwMDAx45hIvlx0XHUwMDBl3Ca1qkf4XHUwMDExXHUwMDE0fsnGXHUwMDBi8Vx1MDAxMetyVDOwb2uMeGZReyVxKqyox7yU73edSkRcdTAwMWVZzPT0xf/o5/ooV3+gO1x1MDAwNlx1MDAwZvWuiCy/OLPi4XC4XHUwMDExOqzu+1x1MDAwNpebUDjoci1oRVwiTz5sZLq2J6VF21x1MDAxZeJIXHUwMDEyIVx1MDAxNzKDyuFcdTAwMWSpL+RcckbnXHUwMDEwaJX0cFxuzWw6sFi9XHUwMDAyYPvRlF7MwC3M+LbaxYdrz9dW8vt+XHUwMDA2OOSdkr57W1XmXGImaCNG5lx1MDAxZK5w/lxcbZ1cYupcdTAwMTnqgNXrrHCn1fVQSUtXQlx1MDAxN5y8iiuEodKm89mYczQn2tnn7lx1MDAwNbdZsGXRtVx1MDAxMXQ414T+dv2sXHUwMDAyNHL7qIO+zzhcdTAwMDBwXHUwMDEy9C5hXHUwMDE0xGyKhpVzyXr9RN3XXHUwMDAxmI9JjZMzhIBWcVx1MDAxN0lJwlx1MDAwMVx1MDAxN6FcdTAwMTcnyChiXHUwMDAySmJcdTAwMDSYrLzgXHUwMDEzqTXgbFx1MDAxY1x1MDAxZXbRjoPOWcXGpIncq5QrzJau6f0m3K2bKsVcdTAwMDSnp6ElI0nZd1xuJOOE67pcYi2cvdLq4FVpb0m650rZzvrpXHLUyfrrg0tcdTAwMWaF5lDjx9Ni3dpR/EVcdTAwMGXPXHUwMDA1/lAm28d6K7Lv3tNcdTAwMGKatZfQy9Pq2lNrdZVcdTAwMDAnMYW39pGJ7nRcdTAwMTDOO4pcdTAwMTZUZU4wsVx1MDAwNJfW5NF4qHeAvEbgc9zBolx1MDAwMz6T6E/ti8Kg+JEtPGtcdTAwMWFcYjzjXHUwMDFm4b5+c+h8jZV2Z6Um/prmKSGXLvKeXHUwMDE1xMSfV5jOoKlvN18vWulNv5Byflx1MDAwNFx1MDAxM75J45o/tMbuc9NcdTAwMGbEvVx1MDAxMWOMez9O1SZcIjzbtlx1MDAxYTuu68LYelx1MDAwML89/laKOIe+XcE4W1x1MDAxZFWM7PajNFXgWGg3v1x1MDAwNq7eKoFi+Vx1MDAxN6OUzJvfw6GkXsJILcpTuv/tc8woVYxcIj5cdTAwMTKT3JYlcotPQWBZQ+6NTjT66cfuXHUwMDE1dVx1MDAxZFx1MDAxMXyaXHUwMDE0XHUwMDAw0zVcdTAwMDc1VO7Qi+02KJa51J5HVNBudMS84/nJ59bO1TE0aGfpTtDbf7w6KC9cdTAwMTGBvaJcbqnsxGI+oTBcdTAwMDGqScolKJpcdTAwMTFgaptcYipq/Sj3+Nlnx/Pp91x1MDAxZl4lXHUwMDAyyJBccl1Jg91cdTAwMWTg8lx1MDAxMyBk8f5qUNaqyrF0V1x1MDAxYToglY/r0XZcdTAwMDfRLlG1zq/Ay4GHqueOJrCBXHUwMDAzXHUwMDAxp9U9cDqdXHUwMDE0QIG+nyBu7qT7TZONOmDFN3h0xkeVPlx1MDAxN8jVoilFvf71UrOAp+C6N8jqRtazVFx1MDAxZmTopVxy3r+okqIkipZtjvcoJ7SYNzo35lp1Tzu+r/qI6lx1MDAwYk+N0ufYlpZcdTAwMTSfej5cdJGf0M3rXHUwMDA2xclEplVUXkTJXHUwMDE4peQ9/oRd/qrYXHUwMDE2OIPoaVx1MDAxNnlze801O1ImboPMlDCyklQ75nmmaIhDYlx1MDAxYlx1MDAwNtyBm2YouPXNT9mam2Nw1UlcdTAwMWJmXGazbyhPqdfl2UqQOVx1MDAwYlx1MDAxY2ZcXFn348NcdTAwMGYprpycwX/apo2OgvtSXHUwMDE5RN0yjC6C9Vx1MDAxY+KBXG7wLcl0b2LCm2xcdTAwMWM+llx1MDAwZVx1MDAxMKnbXHUwMDEzhJ3s+oCB8KkwqfPA0FFcdTAwMDYkLLut3Fx1MDAxM1HTg+PoxSx4OkFitKJwXHUwMDEyXG7NZaIrwHfmt1Vx/X5faINO3ENqXHUwMDA2gX1Iea6na1x1MDAxY333OZdsofpqfoO5RVEsKOdWnKt9s/N8XHUwMDFiXHUwMDE54TeXTFx1MDAxOVx0aIbaxFx1MDAwZYqz81x1MDAwM8FcdTAwMDfVRYlmy+bxuiN6/iRnxSHcZvSzXHUwMDA1qlwizVx1MDAxY8G+XHUwMDE4Rld7kKY13KhmXHUwMDFkXHUwMDE0XHUwMDA0LVx1MDAwNkjBp1xcjVx1MDAxY8+WQ4lTIaSUsmg6vbJcdTAwMDA+K1iorluMmsFcclx1MDAxMlx1MDAxMWuL2TfvsTBcdTAwMWJfSdn7idYkd/VJYoGjP8hXU/FcdTAwMDLX4CivScqTXHUwMDE4OPH+W4uBijGhSDOjgXK7XHUwMDAxSCaR71x1MDAwNfT5ib4tl9jVwrmEe/hcdTAwMDZTXHUwMDE5z/KNec0wnVbcP4RcdTAwMWX1R0ynN56y5fJGbus+o3ZAcOGs+WbRXHUwMDFiZ0eN01VcdTAwMGJcdTAwMDCOoDhzk4qWqJdcdTAwMDL9fIhcdTAwMDNcdTAwMGInXHUwMDE3JDyR7iNcdTAwMDR/5CwjZ6nQQTjqvDTAILeVrFx1MDAwZVxc7NHr8MGEL1x1MDAxMMC0Oq5cdTAwMTcxWWBLXHUwMDAyzZpVmDJPffDb+7B62OtcdTAwMTRcdTAwMWTeXHUwMDAyXHUwMDE5XFx0WPa3eJfcSjz5z7mapvU17lx1MDAxYVx1MDAxMmyog1x1MDAxMdx73/a5srhcdTAwMWZRXFyk1+VXk6dccpLsWclcdTAwMDdcdTAwMDVcdFxyleRJL0GnXG4q9VtYnFFcblx1MDAxNVx1MDAxZKfMJ+H22TCjWlph1fTqXHUwMDFi51x1MDAxMcZ1XHUwMDFhPUMoTj7lndKpRbV6SaDAPoRy4t2LXHUwMDFjyzlcdTAwMDdPpkh+M1xyJsNcXL7mantP4Fx1MDAxNlbQj1upXHItyYZ57CrrXCLPwJ8z5VN4WI89XHUwMDEz5e04/nFSXHUwMDAzTHmGXHUwMDEyJFx1MDAxM+p20Vxc+Vx1MDAxZlx1MDAwYvBcdTAwMTXbTl6CXuVcdTAwMWbPotxcbjhI+M5cdTAwMDWVXHUwMDAwgiTqqEOag8WQZstcdTAwMDJcdTAwMGVou5dB6ddcdTAwMDXc049cdMmk8628lMopXaG5o27nl5tcdTAwMTako3c4/ORcdTAwMTlXmmxpXHUwMDE4kHlRgHtcdTAwMGU9gdHZz8VGJfqpXHUwMDAwy3tGPjvQXHUwMDFm0ptzeG5TZ1S5YKN67ZxUXHUwMDAxW4iGK/tJpIb0Ys08nr3cmDp744Cf2KFcdTAwMWRE9HFMo4o2j2s1KFx1MDAxZWhUTVx1MDAxZorM4/nNxyXsSUAul1x1MDAwMUiXXHUwMDE4jWtcdTAwMWRLrI0vLaYr2oVcdTAwMDGAQFx1MDAwNFxyyM7OyU73eoz7dERTXHUwMDAzgWNUQOLZS5dcdTAwMDOAj+aDQVrYyZ5vzTGeccvRpDcofWBi/lov3NVcdTAwMGZWWEX0tUTydVx1MDAxZORRKUNh01x1MDAwZinoejObnY2N60hoV9SlVeRdOUTMO4fleMrYvlDeXHUwMDA3jIiY/HBcdTAwMWOuXn9ys95tIexSjciRIN6+hp1Q7zvF5Es6VJau8jq6XHUwMDFjJVNcdTAwMTZOhrZJXHUwMDE3+rP4MLpsv6RcYtJzaciJ/lwi5kF7OqHmzWGjVGB3VG45c+yiYVx1MDAxYUTeaKlcdTAwMTVcdTAwMWZOy64jJJ3Qk29cdTAwMDFGs4BcdTAwMWSwvJxcdTAwMDevwbDfm+9cdTAwMTLaXHUwMDExUvCNsOJcIoS1cGPXaX/MXHUwMDEyMelgI1WYikI1fzKiXCJcdTAwMWKKaaJ+XHUwMDFmVKQo9ItX0W+9XHUwMDFiKsld0Vx1MDAxOeqbuouVb9s8dlx1MDAxZTxkM3lk+Fx1MDAxOOQs5iavXFzEbs43eEytI8cgh1x1MDAxYj5cdTAwMGLztDuRXFytSo51lZdcIrlrjy3K/ObMmjsuVljbdy7i8yadfE5yolx1MDAwZkMqJOGlYHBGy29cbjCqp4g/eii5XHUwMDE23c1UXHUwMDBlhnHr4Ehhsa6e40nquDEyMlx1MDAwNZ5Mkn6DlSPzjE9cdTAwMTV4SvSLXHUwMDA1qlx1MDAwNVxchVx1MDAwZos+z+VE+81Fripmo2hOh+Bcbsw755NFs46+zblAXm5cdTAwMDWMiF5ISDhel/hnI1b5YIztJHHt8Gq6Wu/AXHUwMDAy0Fx1MDAxMiVBoVxcq1ZcdTAwMDMlU5RcdTAwMTWZ0lx1MDAxY4eBe4T/flA2IylA5kKJrS+L2y2UXGKmU1x1MDAwMU6KYfB9zlps5Txlii9cdTAwMTZfpOeuQ16cIWZC/Vx1MDAwMu+gbi19m8xuao79ni4jOnrdLPLqp3WOIGkg2kGSNTLDK5t1gaUg7+phnDTVyvBV1M1cbraU6eu0lieMmXJras79a/24Qca9XHUwMDE2MP9wvG0rxWySqkfkdDxcdTAwMDP2QTyD7Ns1xuBJXHUwMDA2itJCc3BatqMvelHcU1x1MDAxYVFdcMOIh2x51VxilGp6vtlcIow4Tr4jdatbJkC4putcdTAwMWScpIvVR1x1MDAwM6RcdTAwMGasopfxjqFcdTAwMDJdvUTubSxuJbgonkZcdTAwMTlES5Pw3ZQ09480s+ODNr+Ysyg5o5Cf9afDxXDsn9mTfe9cdTAwMDaVQ012glx1MDAxZZpl5krm6eVcdIzmjjXx3XMjYN5RXFxP92P02MeR1/6OUf1cdTAwMDHW7YH5YK5TXWIu3iQ0pvSOYeVpXHUwMDFmXHUwMDAzKDM0ru+ydI5eeoBcIn/bmKd+hM/kRnKl3k+hXFwvXHUwMDEyVFx1MDAxZrD/VCtcZvU+XHUwMDA0V7bqK9Z33G3yXFxcdTAwMTBcZv9UXHUwMDEwXHUwMDA3XG67sVfLkftgZdmMXHUwMDFkiudm+2FxtVx1MDAwNTVRMLRn+/HMvUlg5E2sc2RLk2iwz167aZ88XHUwMDAyXHUwMDE2t2+CIZvnXHUwMDFlKfxZvOeKW5Hnt396ybBcdTAwMTFcdTAwMTZYXHUwMDEz2/ZZWEo2z1x1MDAxZkSa82JJtVxcdz1cdTAwMDHSTTO5XHUwMDAx0Fx1MDAxMOhOV83qXHUwMDAwbJYz/cSLcPpvXHUwMDA1z8i8TtH1ea3ytFx1MDAxMo5l9ViczFx1MDAwNv1cdTAwMDIvPIvJZIJiYzPUeFxyeH4+V0ulWmhvJ+81KPT1PuxcdTAwMTnQa5R5XGKfUZDh+W1cdTAwMTfNXHUwMDExI91cdERoXCK3+SFJg4Wkm1x1MDAxOFx1MDAxNbSkpqONJeRFXHUwMDBlKVx1MDAxNJjtvfpcdTAwMDa5fProXHUwMDEz01x1MDAxZVWe9EBcdTAwMDLrXHUwMDBlx7ZcdTAwMDEkcK5AiFx1MDAxNDG6vnPUXHUwMDFip29cdTAwMTTUQKkz0CV3QbJcdTAwMGaBy8jcgKqA8LFFZk6OzVx04uDG0Fx1MDAxMVAjvKlccvFaXaQwISP/UtusJY5cdTAwMWXOTtiIUreiwFxcS+ZcdTAwMGbRial6plZkoiA4XFx0TOhs61x1MDAxNerL5utMW/GXam9cdTAwMTjZY6b7yd+3XHUwMDE5utzNJsqPZDzYXHUwMDEyuvmyo7lNnDel/m0mf1x1MDAxOET3XHUwMDE2JIvegbExyGFtXHUwMDFkg/RcdTAwMTHSiVHy4dbOSrpcdTAwMTPeWpVcdTAwMTl6mfN+7aE0mUDAfHY/XHUwMDAyzUB9pa3vlpz5ZIV3sUA7ILNcIiXdnkGnXGZcdTAwMTg/OMnAp/BcdTAwMTG0Ylx1MDAxMVx1MDAwNO9Ma4e9XHUwMDEyvFx1MDAwZrypuvueXHUwMDAzsVqvY9io5lx1MDAxYaa6NrtcdTAwMTPiV5KUtFpatUJeXHJcdTAwMDVcZq4rYCsvkLvu9FCiu/8nUr/baX+t6ltexlx1MDAwYsBcdTAwMDZvOZNNpu1cdTAwMWLvXFxzgVv5yUcg4EWZXHUwMDFj5EHyaHmHbHctcPPKatdlf5vfPVxuXHUwMDE5sLFngZlR2+bEXHUwMDFkXHUwMDEzI87pM3640sA9NiZcdTAwMWIpL2alZ9F6TG+H41ZcdTAwMWRcdTAwMWHXckyPXHUwMDBi7/NcdTAwMDZbJIyxWX5eIfntXHUwMDBmhG9E1iluLjr/r7Pv6HVcdTAwMTDI1ty/X/HUW0ZcImOYXHUwMDFk0eRcdTAwMWNcZjvA5JyD9P774NutUbdmN5aurmS7cJ2qc75AXHUwMDE14PFcdTAwMGVcdMzEichcdTAwMDLJV2JNkjxyTZNcdTAwMWWepCzc90TeXHUwMDE0XG5XgoXs8zJcdTAwMTR1squdZ05Ccp6cXHUwMDE272ZcdTAwMWNcdTAwMDRcdTAwMWP9uyNB7d37XHUwMDA3b1x1MDAxZUIwSlxiN0+5wX/nyLRJttRcdTAwMTPkj+84Q4PQvNhcdTAwMTBcdTAwMTRYXHUwMDFlnFx1MDAxMbaGXHUwMDAxm/dQIT3aX04qyuNO4thySJMxrcSvKrCUSpbhJ0PPXHUwMDA1h/ueUe9suFx1MDAxZWGAXFy8XHUwMDFm3enuiexnXytHtEBVpf21+ujFx7NQfavw9SBcdTAwMGIz1vE6i5bNisNeI7lw0bWIaLaeZV6Lp3B8buUnz/uvTlx1MDAxNnxvWdok8HZGKb+1XHUwMDFkXG5cdTAwMWTMrGmw8rfPoFx1MDAxMY5LuT7l0ppcdTAwMGLGQVaRXHUwMDE1XHUwMDAwL1xipF2Zpbud2Hckg4R9nPZcdTAwMDcsuFxmPqdoz9NcYlx1MDAxN0mo6pHIWIjZ1/dPiMl8oubgtNmCXGJcclx1MDAxN3c3/JTy2MZ8grZaXHUwMDFmn5fvXHK2gnflpLyDK4NUeDq1fFx1MDAwMIrt/bNJrqXVXHUwMDE3XHJqUZC4t/pR4Tba7O/e6ZBPQEB9XHUwMDExs6fzdi9ioXBo6ZEpNsZJOEqS7jTTWFx1MDAwZT7W8r1+47PzNjKvXFxZ2bZ8l9iC3jnCXHUwMDE07K2h5snTZVx1MDAxZvZcIpf69Nl5MHxcdTAwMTkn/GF9XG7BXGLdkaywMa2CsaRtxt7ytoYkkuaxeiYqWlx1MDAxZKnt9cF2k9K3+/FeWSvygtN1W9Dgilx1MDAwZVxcUv3wc5M29ojrgVx1MDAxN+Q61HFllGhj79ZmicRcdTAwMTmLXHUwMDFjttVcdTAwMThJlKBcdTAwMWWWPj5oqa3P5zw8XHUwMDExjE20oYSdXCI27pU9vpJVnXX6gLVmXFxHy6P+g9lcdTAwMDKu19hnryyS3MJ4+Vx1MDAwND1cdTAwMTT7vk9S2Jl6XHUwMDEw8+DadVx1MDAwM8Wt8JqiKc4y7CvyXs/UR+9SKDA2dNWJNL5gs8MxXHUwMDBidVWTX8fk05ZG+l0j7lx1MDAwMvfJXHUwMDE07/CvXGLZY+Qxm/Xr/navJJStZa1jXHUwMDFkzFx1MDAxYlx1MDAxOOnnXHUwMDAxZzXHq4JI61xih/xdxFupaHK7xJ3c0ZAsJjM/slivzMZag1x1MDAxMbgpx8NjIDgsXHUwMDE0NVx1MDAxOHBuYFCzc9Ftb47GXHUwMDExIVdcYvKnXHUwMDBicT6CvUeVKlFUIezvwuDPUZN5lkeMYVx1MDAwN+XdmUI+UFx1MDAxYeya+DpwfbiGXHUwMDBio0br6CnG5EaCVr5947RYRtKze5izx1x1MDAwN8UxRVx1MDAxMZfpLURcdTAwMTMr1ZdWyrF4xVwiQ4JTmpXHXHUwMDFk9W7FXS9cdTAwMTeEtnhZXHUwMDA1Nnd3XHUwMDE5XHTF005WlIDBXHUwMDE3jpp6Zs9RXHUwMDAyvLymcFx1MDAwNLZ0O+1Mxlx1MDAwNXZSJZuzOc9zx5VqwrjWbe9A8fC491x1MDAxZUt39uFOsVq0WiCer6A2SuWGWIKHN6xcdTAwMTmn+Fx1MDAxNVxmi1x1MDAwMdzzjPXSlb09df+0fWdZn/6mXHUwMDFl4dCjk72yMYdSOLfz412FXHUwMDE27YjVw03q7/xGmJV7M7FcdNAoXHUwMDE5XHUwMDA0yKzvjC09hu5cdTAwMDM26ON59YYlsVwivlBK58HMXG4xSvbeXHUwMDFmXHUwMDFjgH7JxkJEfue6c1x1MDAxMWI/+/Boz7lxPVqsLylqzFx1MDAxM5m1nN9aqFx1MDAxN3e1S4GPnkFG/OBcdTAwMTjISlx1MDAwNf8xaby0XHUwMDAxucCe8axloUx6XcmFXHUwMDFhcr9cdTAwMWXEb2SFfldlQ2OgelKTy9iYUdsqeLhcdTAwMGWqJFtcdTAwMGVti8PjQOdNcVx1MDAxYqBcdTAwMGWjTNzQXHUwMDE3IbfMXHUwMDAwSXojPqlcdTAwMDXEkppD+iFcdTAwMTROwYW2zKP3+4e2Ps5r0u1cdTAwMGaDckbX1eL4dnlcdTAwMTOne7zo2lx1MDAxZTtcdTAwMTJcdTAwMTlRczd3zKGGOOtcbkR7gyEl0XiSXHUwMDAwRmFcdTAwMDBNr8qS1ya10DTW21jawqtD1qByysRZKGS2NLfF0FBYq4Jnl1x1MDAwMFx0XHUwMDFjeps4xWrQyNz0XCJnl+pyXHUwMDA0XG5Fxlx1MDAxM+v2l1ue16DWMmmP7Ts/UZ1TQ0ogmZyfV3/f3M2WJVx1MDAwZVx1MDAwZsfFLJpAvs3cjzRcdTAwMWNcdTAwMDfdXHUwMDE5hvtN9bPd+1BcdTAwMTiOI5KFfso31sTvxae9s8Y/7/V+9HbOXHUwMDEx8KhUbq9QZmR2xtB5XHUwMDBm8CCJ0232iTDDLGy74EuXLFx1MDAxZIdcdTAwMWWoZcdcdTAwMTI6aM9cdTAwMGZcdTAwMTfydSV+PKWwQlqyx+05NMjRvD88XHUwMDAy2lx1MDAwM7a3NZoy3H133WxP7fFu9PWBXHUwMDEwO8EqXHUwMDBmXHUwMDE0rK9cYi6tiHTHXHUwMDAzYWlcdTAwMGbPMa6sq/6eWklIp37Yllxc1j6q9MZefkzh73y93umZvELZxTA5eTSGXHUwMDA3P1Z1XHUwMDFjWKlkXGLT4cycx/pg2oJqPMR9XHUwMDE4YnpcdTAwMTNDy3z0JeNoXHUwMDAw6W0gXHUwMDFlgkZcIkCbfn1ccsVcdTAwMTfqz1hcdTAwMWTQzVx1MDAxY3RMbVxyxH1cdTAwMTJcbimd5ezb+1xyf/X2MsWM3DuHXG5zNdhcdTAwMDC5Olx1MDAxNabj4Lw8Olx1MDAxN6qCs9RCnYdcdTAwMWRz/Cwz4XJ3lnuba6r622uSN0j5sa/eltjSm6E/4lx1MDAwZXwlJYy85qRYXlxinpRKgcvzdVXHLfZks8ib1G1pXVx1MDAxN1x1MDAxZG8h7UiGWdNcdTAwMWJcdTAwMWWxRFxcbdcvXHUwMDAyT5eo10ZGXHUwMDEwOepcdTAwMTnBTy+Lamku7pRzXHUwMDBie0yk2md7IJAkXHUwMDFi2lxcNLbA8DrqXHUwMDAxyoZcdTAwMTey4qKMplx1MDAxNuvn3GtER1+H/djkskg0VJMufXLJ9/aRMrT5alxcJd9GXHUwMDFkm/ba0ObWSblZv19cdTAwMTTo1yRYIFx1MDAwZi5cdTAwMTNbZEmWYE9EQ7JcdTAwMWPNsIqWe53qXHUwMDAwQbwlNbZHXGZI5a77XHUwMDE0kcRRlGrngFxii2jjOMpcdTAwMWW0cHn7YoyIXHUwMDAw5FxyQDnAXHUwMDA3gfTWl1x0wWB+XHUwMDBi9Fx1MDAxZFxmXHUwMDA0rJRcdTAwMDY/87GSVebXJPq7ZINcdTAwMTgleWtA1DZItZMza35nilx1MDAxZbXlZqBcZlnHXHUwMDAxNEmhkClLlaomdmToRiAhXCItdtD9fk7CqF2xpqIvyVx1MDAxZqVwXHUwMDAwIM6i5Jjf99VcdTAwMWbU+cmzjFGEhaDtr7HdIedsnFxuJqApMlx1MDAwNoCwXHUwMDE2XHUwMDFhzJPz1rSem7IqKH04pIBcdTAwMWLsaPk0qM/NvahcdTAwMGL3pFLTXGKXftc7jFx1MDAxOUloVq/DYZv59U2ltPBlanW9zy2vmVieKlx1MDAxMClccpU2XHUwMDE3N3/3NVB5QsDTrVx1MDAwMkD7JDdl0FLu1Tfz3O3XyXaMJLx1eISv0WMm+3pfXHUwMDE2kyDd6umTIHwrfPZfcUnMXHUwMDA3hMPA5VTn4OeHy/boNUVcdTAwMTQo4bjXXHUwMDE3XHUwMDBlV7JLJo+CRNYzXHUwMDA1yHO/tiXCXHUwMDAzlFmATbh8qlx1MDAwMqFEiYVcdTAwMTLfZWv8O35cYlxiYmpvRFwiaURktHXVgqz7T1x1MDAxY4rk43e89sZJXHUwMDFmRagv7lx1MDAxYa3tyrGIvlxiZFx1MDAxY0jYpDxcdTAwMTCgmqLlIZk7udBLfnwh7uhcdTAwMGJYXHUwMDE34qNcdTAwMTaDOXGUuEpDvPav96leXHUwMDE2XHUwMDBiOFC0gFxiXHUwMDEy+L9cXGmRONSC97J1Zpe4K1x1MDAxY5tcdTAwMDCNPIRYrjqK9vajY0pqavuEe/fug1G0+F3aXHUwMDEwKGnzOyHXaHpWfatcdTAwMTJEXHJkXHUwMDFlv8JNc5LH/1x1MDAxZlx1MDAxMcxHRazzXHUwMDBmm3zp6aaER1x1MDAwNkgrq+OQ0vC01ZZuXHUwMDBlzV0vfle0Wlx1MDAxMGU/q2i79tbJZ+h1YUdIlTSeiD1cIrgvY+CLsK9PV5kqVDHqhKKiYcklLVx1MDAwN1xcUEnAfVx1MDAxY6rT261yU1xmqG5cdTAwMWS0XHUwMDE44OShS2ZcZvzZXHUwMDEzLv4p/o0v7YV+6DI0St0gvEdqtDlR+5Z2wSutd7BydzlGOPiX6mCocFx1MDAxNs930LRNXHUwMDA2lpvI1ltcdTAwMDFcdTAwMDOv1Fx1MDAxMVx1MDAwN47hwDVcdTAwMTBcdTAwMDJLwmZcdTAwMTRbO6LsXHUwMDAyyFx1MDAwMzvBq6GpXHUwMDFkXHUwMDA0oPRdMNj8nbwjJaLhfcewQ9aoac7ottmV+Mxls5RcdTAwMWK61JsrtOrWxm+OrzxfUz6kgmQgnJlZt8hcdTAwMTZcdTAwMDY1XHUwMDA12m7TXHUwMDAxMuLJlC+wbtyxJubXZFx1MDAwN3mrXHUwMDE2jsNcdTAwMDQzXHUwMDE1lNTB2ONccpa04lx1MDAwMe3rK/ad431Ue1OuXHUwMDAwUKhgljdcdTAwMTGzcu9kXGL78qf4ykTKXFyhXHUwMDE0L3KOP1cglGnjS7ihbVx1MDAwMz4+PbNcdTAwMTHeXfjGW5qRtOBzLlwiy7FcZmnUTVx1MDAxOP7mXHUwMDE3d5ZUXGKklzlcdTAwMTle0iRVkL0jsGUkuaH6aPON/C3F4FxyJ92YTJj0teckNZ2QfZhnuSRcdTAwMWZNdmxF3CDuKr+oiVx1MDAxMaczZlx1MDAxMOf2XHUwMDA02mZ4YPzyuLVaO9xfaL7o6Vx1MDAxYY+ZLlx0YMm4Xk2WdXz4ocFcdTAwMTcqWDveh7TZP6Vxv3o79uOimlx1MDAwN4dkrVx1MDAxNLxcdTAwMWZcdTAwMWb8qXhcdTAwMDVcdTAwMDZArVFcdTAwMWWE2yg0ZP1Zyy+tXHUwMDFlmKOZXCLCn1BZd1x1MDAwNlxmXHSjQ1YhxFx1MDAxNKV34eFPPbXlga9cdTAwMDfMmKqJzoGo6nJcdTAwMDVcdTAwMTFcdTAwMTFUlaxkcdWVt5pRcfooj2A0VFxyvVx1MDAwN4r+4bvtm1qWdMaUZNU88MggLbg8xyhFp8avoVxuclx1MDAxNzTzUblcdTAwMWapqrJHXHUwMDAwgFRcdIBDN2Rn9WZcdTAwMDCRXHUwMDE1aMtcdTAwMTDZa6G26F5Rpb6JXHUwMDBiXHUwMDFiQCp6JaPaLtbpwcBLJ9aYmVx1MDAxY3DQTKjVvlx1MDAxOIlcdTAwMTNcXLqnrVx1MDAwYv6eXHUwMDE2er9CXHUwMDAxrl6fklx1MDAxNviuXHUwMDFlkFx1MDAxNFx1MDAxNqJcdTAwMTLeasTRtl5cdTAwMDJcdTAwMWW6qjr0+D15W8St371WXHUwMDBlb1Gwhs3QKKk7ZliPrYM/7981VjlcdTAwMTWAXHUwMDFhJlic7edvhpQ0STxcYuOrI6g7gFxuhzm6Kb7youTJ6eNnVFx1MDAwNqmQubxcdFx1MDAwZWBqM//yXHUwMDE0iFx0pFx1MDAxN/MmOba14mNyWV+i6HfhRV1cdIGdXHRgRs30e1xcrCxou1x1MDAwZpHV7c77vbfoK+CbzfDnykIqwb0qiLFQi9Bzc1x1MDAwNJvepL7yJ3KDXHKE+GtXWNoqWU/A3WhQ19OvXHUwMDFlx/RB4Hf5SjbsISRbQTwrmYfXZ0yN0Wza+tKJrVx1MDAwMDmhQrzh6POHI85cbl912ZZcdTAwMDbPfLdYrTVcdTAwMGXF02pQe8wy+4wxqZNTPTaT+lx1MDAwMjTq+CdC/Fx1MDAxZVxyiW73mFx1MDAxZlxuip/HfU9cdHqlXHUwMDE2rMd6gH5cdTAwMDZoMcAyr0k7QTxcXOaviNLeaf7YqMteuZ5aP1e64bQsZTJj6nA7Usjrc3Q2hEK3XHUwMDAzXHUwMDFhYIReeMvFkfZIdbfWXiRccjmQ06ZcdTAwMGVpvZpwlpEyUbT4tYiSXGZbZYC6Qpjy4cpI9Vf3U30lNGpCyJ6qcL5cblx1MDAxZMOUXHUwMDFmOWXFVqzXX1x1MDAxMFxc+Ko/deCr7Vx1MDAwYlx1MDAwNcVcdTAwMWExt8g9IIffRF1alYo8w+/c5lxmXvNa/VxyNEQ6bJPP6yHl9SS2Ta2s5stcdTAwMGLDd6ZF59jmbspCeorQcWf6zj730GucaVx1MDAxMzWFtSVyRUhcdTAwMDWVXHUwMDAxulx1MDAwNFx1MDAwMYRkVnNE8zqOV1x1MDAxMmhcdTAwMTIvvXE79TKB/p1PtG/AYVx1MDAxMj/zjs1cdTAwMDDfuy9cdTAwMTP7XHUwMDE33Vx1MDAxMHlT9CFkfvyxwe+HXHUwMDBiXHUwMDExL1x1MDAxOHtunKzRV9RtXHUwMDFio1sqXFxcZn6NjiAgnpJnwpAyn1WByd5PttlzNflcdTAwMDOgUK5Fert+Vy6szEoxW1kyiijt0+3qtzOKIPm2XHUwMDA38azf3djGXHUwMDFlT9dsLSuYXHUwMDFlXFyZXHUwMDFhwFqsXHUwMDFheEYsNkV9XHUwMDExNZFHXHUwMDE5dFx1MDAwNlxiKpt+8k80pkx6nSDorYK0vVx1MDAwN3k3Mz7SR1x1MDAwM2vDMkTl5IPbXG5cdTAwMTfTV0mBweSTXFxcdTAwMDfHhM9cdTAwMDFiuVx1MDAxYf03Kl6fSU3RXHUwMDFiN2Y4IVx1MDAxM95cdTAwMDDD8IFcdTAwMGXWkW3VXHUwMDFhXHUwMDFkk1x1MDAxYSHd4Fx1MDAwNlh/39HGXHUwMDA3uGFLXHUwMDEyMrtcdTAwMDKefK3Kcrjhd79aV5L47qvI7lx1MDAwME9VVjuKJazBY1XwcnnBXHUwMDFkXHUwMDAwhXqW/faIXCKErWFSXG66MFx1MDAwYt1i8nj0PHh9O0eK5X5FLChRXHUwMDE4ZnMlQn+/iqoxXG5cXJy+XHUwMDE5YavNfKKCXHUwMDFl1CmWNl9cdTAwMDPmj5yOXHUwMDEzvVxczjDRN1x1MDAwZl9zcrhQcGUosLvJUqOAXHUwMDAyXHUwMDBicn8m1MKMV37kXHQ8ilx1MDAwNZZ6O92JvPRzPbNoXHK+8uuljynDXCJxWlxc/FxmXHUwMDAwqnlcdTAwMDbdpzDomsqUhPj2avkygT5Z9Eev0CCJycHbR0Wrc/WoourzhEpoXGKd71e6c1x0Ulx1MDAwMZ/V3Fx1MDAwYlx1MDAwZeTNi9vDvVx1MDAxNEOk7FdcdTAwMTQqn1x1MDAxNf9cdTAwMDSlXHUwMDFhfedHqVx1MDAwNThtyH5GiFx1MDAwNDWkXHUwMDFl6LvvVlx1MDAxZvDDj62vRue6ejFm3ET35oN7eiWPbouZ/Zr7ukrq0V5BXHUwMDA2Vlx1MDAxZFx1MDAwNJ6O/SP6srPPXCJcdLXATFx1MDAwMlx1MDAxZj20ZFsvXHUwMDBi2c9PXHUwMDFhy5ZNzS639Vx1MDAwMFx1MDAwZZfeXHUwMDFm0Fx1MDAwMS7j2J5eIaJcdTAwMDYkK7KfZ5TTxEdcdTAwMGY+KjzZSXdcdTAwMTmtM1xy/CcvgYOpk8+1bVx1MDAwNVx1MDAwNDZVzC1fI5BcdTAwMDXkltRcdTAwMWN8MbK2e3CMVO2mK7+tbVTNwSV9ZW+lk9jhy0RcdTAwMDJ3XCI5oGvjO5Nul1x1MDAxNIpcdTAwMDBOXHUwMDFioHwz9blL0GpcdTAwMDBcdTAwMDSBe63UK6OlPtqsf7VfnH/It1xy3PszrippqVk+Mz8u8E1Yd/RJkUmwM6SOSLiXXG41PIZONmiX236Yo1HdaNS1ZcTZVNeMwudqVpIvynjAYlxydsf0WHpuRd42u7uTUPE2z4fvYt+hyVxy0+DlKq39Llx1MDAxYTzvW3EyXHUwMDEz1lx1MDAxZcaDO2DIR69cdTAwMDbAalx1MDAwN6jnXHUwMDE5r1x1MDAwZlx1MDAxY1x1MDAwNunW6ICCKMfwJSO+JVx1MDAxMd9cdTAwMTfYcdpcdTAwMGLu6aVcdN1cdTAwMTOVO/pQ3qRcdTAwMDR3YPBUxKBK/aAwXHUwMDFmp+wpcLCFr9HXoFg1rPXRZ7w81sdqfpVcdTAwMDCOW36CXHUwMDAxXFw5srFhKNy218HdTOTxLVTCvlLfbt9O+Fx1MDAwNciWfZJ/RD9cdTAwMTFcdPdccra7wXTFaM+iV3zVR1x1MDAwNtyYK/XW83vEd7nSdphkjJSgXHUwMDE3oFx1MDAxZVxmr1x1MDAwMFieaVx1MDAxNq7X7t6Y5XBrqYJ9KXlxipjDin7fUqJtXHUwMDAxb9PsXHSfuFx1MDAxYpzMgvBcdTAwMWZKi3Z8gpaMeybeJFx1MDAxYvFeus0jzlx1MDAxMEbcXHUwMDAyO1x1MDAxMtV9T/YzXHUwMDFjPMbdJa762tFrSFx1MDAxN1x1MDAwYrXZs6rMlWJfd52TS0XZkMyAWjZTXHUwMDAxU0Qu31x1MDAwN/RcdTAwMTNcdTAwMTSVXHUwMDE5xlOBj4KobjCwJWaZvDe7e2eoJNpcXFmoici1zYV9Wu25f6iG7t/UJfZfXHUwMDFjKi1ElXGtj1Z2u1lcdTAwMWPlPuNcdTAwMWKIVMdcdTAwMTSmm1D4bEFhI9aY3CFcdTAwMGIzXHUwMDEydS1lc8vYwelcdTAwMTY6lUd0yJihZs+zYclm8lQ8XHUwMDA34fk7KYbpqP6WVFx1MDAwNyugUoz6SMuVW6dcXMTRJduwtMxcdTAwMDKbhTA0m7frZ1xcmFpcdTAwMWLyWsozXHUwMDFlKnc0upMm2yyIxUd7Ko7ZI75cXFx1MDAxZOBXnFx1MDAwZk8heVhZ55NA0Jh1VSzDKL2/IK3ZXHRe0FwiY2V56oyw/LbyXGaaVJ3CXHUwMDAyXHUwMDExzS7cdJd+sIwkbaZE+9jhPaROolx1MDAxN2g+prlcdHCXwMSPM7q0kz72o+HXsI9Bii5UXHUwMDE3j2TAXHUwMDEyXHUwMDFkXHUwMDE3rikqaY5Rq6VG4oslqadcdTAwMDbkXHUwMDAzoYbK1jstXHUwMDFkO1utYYGlIZa4WX1v4pyUtOiPv1x1MDAxNl1bO3gyXHUwMDFlULZe2nVcZnDj0q9cdTAwMTGGvXqzN5d9XG7KXHUwMDE1Y189LriU/I72p1x1MDAwYlx1MDAxObJ6PejVTXyJfoGqz5SnvFx1MDAwMOrcnLGnKLjwilx1MDAwNuLKp7WsOIBK0jdxR1OaX7yYfs3gzcKI/z6LXHUwMDAygydgOZc3mSVVVe1KqXWxKNyP/Ng/61x1MDAwMKnKk0DxV1x1MDAxON6jelx1MDAxNFx1MDAxML5Ab29BlVx1MDAwZUOi9Fx1MDAxNWA501x1MDAwZkjbiXWH+GquN9V3XCIsTHNlXHUwMDAywrJ0UjpcdTAwMDRcZlx1MDAwNk2istqeh1G4JplN55liO5zcOWSyvuulzOxV5DW0lruLokvlXHUwMDFhgFx1MDAxYVPukILwXFw0XHUwMDExXFzb2OfKWuflPr9cdTAwMDZmXHUwMDEwo52Ba62cI6Sc7N1RunWOz1x1MDAxYe73XHUwMDBitPaSXHUwMDFjXHUwMDA2XHKuM91Oelx1MDAwMU9cdTAwMWbs73yEOO7rUGWOvobrhDpe3HNFXGbFXHUwMDA1noJ2g+W0XHUwMDFigoQntNJluINcdTAwMTajMsDTWC5QXHUwMDE0/Fx1MDAxOVSGSzSWMYNgea2jXFxcdTAwMWVcdPzSmWn9SsRcIlx1MDAwNi8hojZBXG5zpLNdtJSmfVOsWL39eGSFXHUwMDFkpMyaXHUwMDEzlfZmXHUwMDE2j1EneE2Kvd/Xh6RcdTAwMTiFRF81nvRYflxivLWKXHUwMDFksa6P+1x1MDAwN/BTXHUwMDEz1PtcdTAwMTlEd/JH3JAky3CguGK43p/oXHUwMDBmXGJMqVhcdTAwMGWoTcSauJnf3lx1MDAxODJrXHUwMDE2seC3XHUwMDE3jcHGb05cdTAwMTJcdTAwMGVfrW7Ae5qTfsjvUj7+K1x1MDAxYrTUYC0x3LOblPNcdTAwMWPbcLfK+vMg410/9uKcQFx1MDAxOF3O30KHnb7mvadcdTAwMWSWU1x1MDAxMf3drM0gO1Lal36FQpvpXHUwMDA1j0xcdTAwMDXj1DA+dkRcdTAwMDI4XHUwMDE0XHUwMDEy5Pbp9Hc/XHUwMDAyJD8qj1Pil1x1MDAxZVx1MDAxM9xcdTAwMDFX2+zjjjDE90/Jan3ylbOEXCI/mUdEXHQn0TuIXbltes75XHUwMDE2udRcdTAwMWEmqZBLmTJcdTAwMDaFXc3BeOqfatfwQ17UXHUwMDE37JVrQCvrd9o8b1wipjhx01x1MDAxY7nN3HqIwoh3ZlfnIzZ0TeCjq1x1MDAxMb1cdTAwMTh3Z8r9oL5cdTAwMTW+g9dcdTAwMDVLZ1x1MDAxOKj+fFx1MDAxY1x1MDAwNkh5nexk7rFaOP6469RnXHUwMDFk46ReXHUwMDE44ZFvfILFyNpcdTAwMTRA5Vx1MDAxZp36fVx1MDAxObjZJMX2W4c9ZCbgOlx1MDAxNi3MXHUwMDEyniOYXYn4XHUwMDA1SyU5pZAt93yPo1XuqFx1MDAwM1x1MDAxMFx1MDAxZSVcct1cdTAwMDNcbmVcXPTUQ0HULYyTwFxifVB0ermpXGKuo28qUbg+jsF0dmRJzu9cdTAwMGWJyCy2XHUwMDFitFx1MDAxNVx1MDAxYjt/KqnRiDRcIlx1MDAxZlx1MDAwYnCVy2yEwoC6knzfwKfgX1/t+tiMXGJcYriooDO/0lMkXHSpOdJcXKCxfFBcdTAwMGZcdTAwMTOIUT1tV0eyXHUwMDEwtYizt7SYaC2oV1dl48fvXHI0otpcbsGrSGnSt1Jt2FvhuFx1MDAxYYVnWXpMXHUwMDA14/bH2HbgccDRglxcYmdcclwij1x1MDAxMeXb6Mk9oiWbXHUwMDE2qFx1MDAwYoQ7ylx1MDAxZnZU2FJcXM2/ynpae7Rw9YBrZzjU+kh+3Fx1MDAwNqJJKimEUeOpVKxcdTAwMWVcdTAwMTWbL0FQXHUwMDFkN1xune9cdTAwMGZMbO1mmTFQYyAnQy9cdTAwMWZ7YbqGY51cdTAwMDDKQ8dF2plcdTAwMTFRJtU+9ImfzFZcdTAwMDMhfd/ulSnQNb7h+Fx1MDAwYmyM7FBcdTAwMDZcdTAwMTlHxmlIoFx0s2mG2iXEpo9cdTAwMTC5Lv8+XHUwMDE3O3RHwMJsXHUwMDA3bXrPeDdxlrva21xyr6vg74ejnmi8yWvOXHUwMDFjXHUwMDFkXHUwMDEw7iRzb1xmPSNcdTAwMTDCJe/uWyjPNz1z5dU1XHUwMDE4RzOSuslAsGXB4+uN1G9cdTAwMWaltlx1MDAwYtilSFCcLE5Dur9cdTAwMTL7PSw12Xdk0rnRyG78oVx1MDAxOfmB4SijdFx1MDAwMTGBa37JhZExb1x1MDAxNYBcdTAwMTVMomlaLb2Wt3xXZCpogDuV1thcdTAwMWRcdTAwMTFBulOkTlx1MDAwZepcdTAwMWLO6KEzXHUwMDBi2I9cdTAwMWJ0QreQmFx1MDAxZr2X2kihfElHLWTm0UWP+7/lPUVyR3LT/uO+ykrOSr0vh6Zuvlx1MDAxNGVIwPyhPMq5fOSILzLRO1Rvilo8XV2etq+N8Kz2XHUwMDEwcWjRrs1cdTAwMDbuXG5cdTAwMDJvhWlcdTAwMWbHu+N3+z1PnS2jY4id0/WdRvVcdTAwMWS08HHKXHUwMDFi+KLH+jH67Dnhkvw5puC3M/gloDNyjlLhK1dcdTAwMWRsoSRccoKrW3agXHUwMDE2f9SC/91ryktCbXLNXGJcdTAwMTXPwsmb9lx1MDAxM59cZrRcdTAwMDJuXHUwMDAzQ0nGXHKKLEZ6mb82laj0eVx1MDAxMmVcdTAwMDdbXHUwMDE44atcdTAwMTLcy7KaL7lcdTAwMDLOQDymvJkjZFx1MDAwNzukxvmh41x1MDAwNdHhv5dWsbw1Wlx1MDAwZbv3YOM3c4uP6+Lwg8KVwpV/XGbPwIbpJD72XHUwMDBidapsx6rRXHUwMDAw4Vx1MDAxN0pZRUNcdTAwMTRcdTAwMWaheTllhM7WXHUwMDFiOvK69WE+eYZj375cdTAwMWT4/aKn6niPwd8+r69cdTAwMWFbfVx1MDAxMChcYow+WWClZFx1MDAxYtqaP6zmXGKRuFx1MDAwMepf+Fx1MDAwNsIhXHUwMDEwLpI1YVx1MDAwNFx1MDAxZYB6fXftV1x1MDAxMY1cdTAwMGZcdTAwMWNcdTAwMDBcdTAwMGZbg1xyoUOSXHUwMDAyj/2+g4za+baKXHUwMDFhKJc9umyySUV6K/wgXHUwMDFjM/v1Xmj60V3sZFx1MDAwNVx1MDAxYt33l8Gw8kJKsWrJdPxcdTAwMTmC6XFFXHUwMDFkRFx1MDAwNSOhjzbibzzVXHUwMDE2sn5cdTAwMTQo0T/iszMxSf+Ssirn5IgvV1x1MDAwNt1vo1x1MDAwNa5C8PZ2Qlx1MDAwNMUkXrre3eJcdTAwMDFcdTAwMWPTWITZpj9+/UkwXHUwMDE3XHUwMDBlz1x1MDAxY+pcdTAwMTNMsnXaJy3ULlx1MDAwZdqtjHpkXHUwMDExXFyOXHUwMDE4VvJcbsnuorxcdTAwMWYxRylY8sGdiVx1MDAwMj5cdTAwMTjIY9Vh2cf2XHUwMDEw7oavMFHBxoxhmSMrxu9ihqr/VHWqw9m6Vto+w2mHwqLQp1x1MDAxNy1SoF54rUPUaFVcdTAwMTanPehoi9jK3IrgXHUwMDE2cY1+fzVEY9Xhl6e8c2j4b32eW1x1MDAxY1x1MDAwZVFYz2lcdTAwMWEj6W16YV2WrUbnWy0lrElv99uukLIwwFx1MDAwM5mYNn7FUiqZXHRBuSU+4Um32U1AXHUwMDFlhf3gflx1MDAxYsEvS5RcdTAwMDWpzfdcdTAwMWJNeX9wXHUwMDFjXHUwMDEzN3nbnyDhSlx1MDAxZrlcdTAwMWVMib8sXHUwMDFmMuWlQaHOw6qK7nfzPkGTWEXQObJcdTAwMGZKpqBLfjXtt/tcbmj2sKxcbirs6kVbKz5cdTAwMDO2lHnZ1lx1MDAxZtuRIc0gOq0p3IBmgfWJXHUwMDBmR1xmRVx1MDAwN51cdTAwMGZejDiE67g9ZX9oMTJcXP9cdTAwMWTUv70uXGY7pO39Zlg6Uzw/fFx1MDAwMLlcdTAwMTiI87HtM1ZcdTAwMWa0VaFcdTAwMTe/3TRV0Fx1MDAwYs8/srRQ5FxudJR5Z9TCXHUwMDE42VxuqXL5XHUwMDAy0oHDXHUwMDAyXHUwMDAw8CTVj/tAkE2ARJyQNNL4K+snNpK1SO6yXHUwMDFhXHLnSzVcdTAwMGKsfGRlSjjIXHUwMDFkXHUwMDAxblV931x1MDAxM5wvKn3ePd3yjLjZrGfxR/jyht9cXNDjmDdcdTAwMDQkaTQvtSMhVUlyPHXIhlx1MDAwZWfR1quQtKdRXHUwMDE3fWt7iqPUu6cxcFLHXHUwMDA2rUgmeT9L5qK2yZFcdTAwMWJcdTAwMWFrg1Vrh83dN1k9nlx1MDAxZnDi03dcdTAwMTZakm0ldE05RcfLXHUwMDAy242kq1rFUYRV0Fx1MDAwZrX8rVkmfOBUYljPNrNcdTAwMTU+JbbeXGa9Q5BQc5Z6YjRJKoBvRlXnXHUwMDE3Jo2fzj1cdIGq4tK665Xetvq75fW6n4nWvef346NZXHUwMDAz41x1MDAwYjtRmu5WWlmT1E+t17DKXHUwMDE1r1x1MDAwMsSwVlx1MDAwMGjbkNhHL3zn8FxyQsvHcseOP4auQ0JcdTAwMWW9001cXDxeqlxiovMppyxol2UwQ3JcdTAwMTi6u1xiUWThqKB8aceSYzO571WNWphq145ohUNcdTAwMTOYQ5KkyK0nl4aemkRkN0hcdTAwMGYnfuo3MKBcdTAwMGK04HtVli9saIkyXGJZ6dxVpLSWaLJK+KOoJ49cdTAwMWOKXHUwMDAySVVoed/hRJUjtEJatqVcdTAwMTNcdTAwMTK49E7oZ9ysXCJphdPmWJH+YUpcdTAwMWZcdTAwMDU5YyizLsvInYrmXHUwMDBluOVYrjBiMjJbzlx1MDAwMYlClyZcdTAwMTJcdTAwMGXXm91cYphdh7pcdTAwMTjgK1eLbe6Of1x1MDAwMfzjfftxXG65YW5cdTAwMDFD61x1MDAwN5Jsjo7n1OUp2Fx1MDAwNytcIlx1MDAxYmXv36Xcj9k7hUp8UI2zh6dPWmGlhXdW9G9cdTAwMWZcdFx1MDAwM1x1MDAwNz1mV0Ywf+1cdTAwMDJcYlxuW2sv4FPR8My3b2A2QVx1MDAxYd+0/lx1MDAxYpfRXHUwMDA3qki1JbxqXHUwMDExKZj2WVx1MDAwZlC/7OotXpks81xiO6TRTYvGu9rpnU+ORPOmvO3K+8OHpd0wL+8kmjGknHY+XHUwMDEzhse/fpVcbjxcdTAwMWZcclRcXCy8kb2lf/pcdTAwMDH/RLKXfL3qXHUwMDFkLyCslEiN7L/Ldk2qi2tb5Fx1MDAxZlx1MDAwZo5+XHUwMDBlxNJ6XHUwMDFhrFbLJ0zzXHJd9PvM4XR6r/7Hvm+W6STP1yxeekq2iFx1MDAxNus4fnlbl/dmdJ8z/fVpY+9cdTAwMThQ0tD+rd+vXHUwMDA1psnYu1xiXHUwMDFmrLYwWu8g/03UvNndXGa4XHUwMDE2MLHnc1x1MDAwN1Ut33thtFxi81x1MDAxYetcdTAwMTjEZusnJjzM96n2/lxmXHUwMDE0UKDy8Shevb4xi1x1MDAxZYAgToAg/IzOk1x1MDAxM+UjJlx1MDAwMFx1MDAxZqrqXHUwMDFmpmtlfTfkw9pOaDHWXHUwMDAy7jdUNG+a9dwwpcnPY8rOqoy/2ZNcdTAwMGaw3M7Qe2LiSJdZpz0k1ZVj3q+GfVx1MDAxOY9jvb9y8cig4kJ99OGE3pQnQbWJ/Vx1MDAxNjR9g1x1MDAwMW7QLCjYLYpcXNiD08utOUU7f3krXHUwMDBmfffaQVFZs9lfma9Oz1xiadjQXHUwMDBml2AvM8byrul4UfyCXHUwMDFm8fVkVXGsXHSMOZJcdTAwMDUh+ah9gnfKaspYnXnYxHm/XHUwMDEyXHRYfHaiZjariq50sCtcdTAwMWV/O1xiQtbtXHUwMDE1rvOOV5ZcdTAwMWZcdTAwMDdcdTAwMGXZjWmoN6pcdTAwMDBUNihUpNdkLEN2vPbWjJlcYvic4Vx1MDAwYrrha6zFnjmQbfWGXHKV3KX0wbfuriVheDQpVEQxkenf7K1HVOecXWd62/pcdTAwMWWaUHdcdTAwMWWdXGbujVx1MDAxNaeqyC62vj55x0o/nVx1MDAxMirfq3nzyjPGlva1Q1R3XHUwMDBl6XdenMXO1Hn8w1x1MDAwZs9cdTAwMTNcdTAwMDZ+p9aGb/5cdTAwMTByYlxmLlZsMDDxippv17ZcdIFDXHUwMDEwurWXp6jf4Nu8jjuWijhcdTAwMDU0WCZgjzt4PDIxLFx1MDAxYzJcImuRlj1Hi2icittGv1x1MDAwN1x1MDAwN23gpXy7TzG8XHUwMDFlkuKfKj1irICeXHUwMDEy4E54M8m9P9gjMOhnXGbY31x1MDAxOHDH+JZcdTAwMTjrt8/ykdav/I0tT3/f0um/an3IXHUwMDE0WqboUlx1MDAxYVxuXHUwMDE0XHUwMDA2Jf6XOmnx0Fwi2Vx1MDAxZsUvz4tcdTAwMDMzL4xusudcdTAwMDOH+Vx1MDAxZTw60PRcdTAwMWZcdTAwMWPRXG53S4RcdTAwMTb9Ulx1MDAwZvpcdTAwMWUvVE1cdTAwMDb7N9ami/JJeuz/xKm9b88k+dvnICHA0CZvl3/6wvDce1x1MDAwNDxFeOap5YfsXGbyqlx1MDAwMX/rlJjB2/U/a0z8bF+UXHUwMDFhXHUwMDFlUjqKTFx1MDAxY05a+ecxXHUwMDA1M8FQoVx1MDAxNFx1MDAxZb5+q3tcdTAwMWGtXFzpPfGzpbTUqHBLv41cdTAwMTjeUrxaXHUwMDE4a1x1MDAwZfrJOelcdTAwMTdcdTAwMDbxovmH173sXHSDhp4w/mqGJsxbivn4N59cdTAwMTB1XHUwMDEwqJpcdTAwMGZ/dVxmflBetZ4wfjk7LD18hsmvf5VcdTAwMDRcdTAwMDNYXHUwMDFivTn6XHUwMDE3xpvrSspTXHUwMDE44S+MXHL386sg2Vx1MDAxZllkmlX/2mKz+Fx1MDAwMVwilPpxazGkovRcdTAwMGLjed9q3uZcdTAwMGKDhT9MfSt/Ydi/gVVcboncXHUwMDEx4e/4UrBYYFx1MDAwYkvD8dRTIdEkYSoo/Vx1MDAxN99fXHUwMDE4x7+HocS/+PhcdTAwMTShXHUwMDBlXHUwMDA0UfPi+YC3f/fkUFx1MDAwZmz5bYJ8/nXwaYW//lVcdTAwMDP+hGFcdTAwMGLcr+OnwnXM6Su/vlxi2rjh0Vx1MDAxM8bxx3lcdTAwMTlcdTAwMTlcdTAwMTZP21x1MDAxMnuJPlx1MDAxMN1/a4lcdTAwMDX2yKhSkn7vW5Chv8KT/43/ibz3XHUwMDE0b+m/8VNcdTAwMDaZzK9/XHUwMDFlX8pWK1x1MDAxZiHpt1x1MDAxN1NaXHUwMDE0mlx1MDAwNDRF/Fx1MDAwYuPzhHHRdMP+xp8pNMKs3+FffqTAXHUwMDEzXHUwMDA28YP4X35RXCKqqYf0hCHNJ5Z2yFn8LjzS60FG8PZk6OHRXHUwMDBmJ1x1MDAwZlxcOH+pyLJcdTAwMWaPcuNS9l04fcFD/tf/xDcpZ1x1MDAwMJZS3/xuw5Nuf1x1MDAxZkaFXHUwMDEw81x1MDAwM1x1MDAwZl/z/F2LVTx8lINB/bEhjySan9pcdTAwMTGH43feXHTAscMk8kVP5Nbl2caT6Vx1MDAwMYGUt1x1MDAxNItcdTAwMTRX6WhAoCVscSj5YFx1MDAwMa1oNGvST8r/nofxwPQzkv2TLlx1MDAwZl9tRjvlrfAunnxhmOL33IBnrJ5y8sLi+1x1MDAwYlRyXGbpm/9n22fun3F70lx1MDAwNnrmjavGaf/dyPHf2sa/vObfkmj9zb87vT+09lx1MDAxZm3/2SfN0v/qqkrmLfxE1r+1pf/VJ15cdTAwMTZ/80Uw5cWF/972X/HwXFz6p1x1MDAxMbtHXHUwMDE3tYD3XHUwMDFmbf9cdTAwMTVcdTAwMGbLQH+1XHUwMDE4klx1MDAwNfz/P1x1MDAxNi4gvjZcdTAwMDKvbIwgwDf9e/Gt4DbOZnUs+4//9V///ff6Rzpn8Zp9//G//1x1MDAxYn5BOIG9KFx1MDAwMkYx6P9+3sbLamfr/NjF//dbf1/6n//6/f3P/1x1MDAwMV6NnXIifQ== + + + + + DKVNPSMRTaskJobFork/Join由JDK中复制改造对象存储服务分布式KV存储服务Map Reduce计算抽象任务HTTP ClientREST API Server算法库存储、计算组件外部 存储、部署组件 \ No newline at end of file diff --git a/MyBlog/12306.md b/MyBlog/12306.md new file mode 100644 index 0000000..b901304 --- /dev/null +++ b/MyBlog/12306.md @@ -0,0 +1,47 @@ +--- +title: 12306 +date: 2019-01-13 18:01:46 +tags: +categories: + - Blog +--- + +**目录 start** + +1. [12306](#12306) + 1. [业务规则](#业务规则) + 1. [技术难点](#技术难点) + 1. [是否需要使用抢票软件](#是否需要使用抢票软件) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# 12306 + +## 业务规则 +1. 票是分期 批量放票的 +1. 全程票最后一段时间还会有保留, 短程票要时刻注意 +1. 每个站的放票时间都是不一样的 + +> 技巧 +1. 热门城市的票会更难买, 多买或者少买几站有票几率可能更大 +1. 错开出行高峰有利于买到票 +1. 12306新上线的 候车补票功能 +1. + +## 技术难点 + + +## 是否需要使用抢票软件 +> 抢票软件具有一定的能力能提高抢票成功率, 但是恶心的是购票时默认携带的套餐 + +1. 安全性: 大的软件及公司更有保障, 小作坊比较危险 + - 因为他们的做法都是保存你的用户名和密码, 使用多个ip源 时刻刷新系统, 并且绕过验证机制, 从而期望快速抢到票 + - 但是这里有一点就是万一这个抢票软件出现安全问题, 用户名密码以及身份信息就泄露了 +1. 稳定性: 由于众多的抢票软件, 导致了票更难抢, 于是他们就投入更多的资源, 用户也花更多的钱买套餐去抢票 + - 从而导致了恶性循环, 软件里提示的抢票概率更加不可信了, 而且重要的是有些抢票软件自己撑不住并发 自己卡住了, 反而浪费了时机 + - 因为是加入了第三方, 所以瓶颈会更有可能出现, 上次就经历过, 高铁管家自己刷新票信息都刷新不出来,报错 + - 直接使用12306的官方APP反而很快完成了购票 +1. 耗费更多资源: 不仅仅是金钱还有时间 + - 现在很多的抢票软件的套路都是让你分享给别人帮你加速(有效性值得商榷 但是信息不对等也没法预估) + - 所以即使花钱开了VIP 买了套餐, 还要到处求人帮你加速 (次数多了都会烦的) + diff --git a/MyBlog/2018-3-15-install-deepin.md b/MyBlog/2018-3-15-install-deepin.md index 3c3445b..3e33452 100644 --- a/MyBlog/2018-3-15-install-deepin.md +++ b/MyBlog/2018-3-15-install-deepin.md @@ -1,8 +1,17 @@ -`目录 start` - -- [在公司电脑上安装deepin](#在公司电脑上安装deepin) +--- +title: 双硬盘安装Deepin15.5 +date: 2018-03-15 16:11:42 +tags: + - 工具使用经验 +categories: + - Blog +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [在公司电脑上安装deepin](#在公司电脑上安装deepin) + +**目录 end**|_2020-06-04 19:41_| **************************************** # 在公司电脑上安装deepin > 由于是全新的电脑, 默认开机在配置Windows10, 然后也配置用了下windows10, 被他的卡顿吓到了 @@ -12,18 +21,22 @@ - 由于习惯Deepin, 所以一直备有一个Deepin15.4的安装盘, 然后试了一下, 发现U盘能进去, 点击安装Deepin就黑屏了 没有然后了 - 然后搜了一波一闪而过的报错信息, 朋友也和我提到了可能是内核版本太高 - [问题相关](https://www.systutorials.com/linux-kernels/95229/platform-x86-acer-wmi-only-supports-amw0_guid1-on-acer-family-linux-4-9-22/) -- 然后就刻录了Deepin15.1的启动盘, 最终完美安装 + +- 然后就刻录了Deepin15.1的启动盘, 最终完美安装, 但是还是想升到最新 - 分区: 因为该电脑是Intel的128固态加上1T的机械, 所以尝试了只在机械上放 / 和 /home失败后 - 尝试在固态上压缩了500M出来, 挂载 /boot 然后在机械上挂载 / 和 /home - 选择引导时选择的固态 + - 进入系统后, apt update apt upgrade 进行升级 - - 其中提到的所有询问, 都是默认, 然后说grub引导需要升级, 需要选引导设备, 选之前分的500M的那个区即可 + - 其中提到的所有询问, 都是默认, 然后说grub引导需要升级, 需要选引导设备, 选之前分的500M的那个 /boot 分区 + - 然后就发现升级完进不去桌面了, 能进tty, 密码是正确的, 但是图形化窗口进不去了 - 解决方案 `sudo apt-get update && sudo apt-get -f install && sudo apt-get dist-upgrade && sudo apt-get install dde` - [社区帖子](https://bbs.deepin.org/forum.php?mod=viewthread&tid=134486) + - 进去之后分辨率很低, 搜了一波怎么改, 然后说要自己用 xrandr 等命令来设置 1080P - 在社区的官方帖子上找到了解决方案, 装个驱动就完了 [官方社区](https://wiki.deepin.org/index.php?title=%E6%98%BE%E5%8D%A1) -- 最后完美运行了, 和Windows10也完美兼容, 启动的话就需要进BIOS进行设置, 和我笔记本一样的方式, +- 最后完美运行了15.5(现在已经是15.8), 和Windows10也完美兼容, 启动的话就需要进BIOS进行设置, 和我笔记本一样的方式 - 开启UEFI 关闭 Legacy 就是默认进 Windows10 - - 关闭所有UEFI 就是默认进Deepin \ No newline at end of file + - 关闭UEFI 就是默认进Deepin diff --git a/MyBlog/2020-05-16-install-manjaro.md b/MyBlog/2020-05-16-install-manjaro.md new file mode 100644 index 0000000..fd404ac --- /dev/null +++ b/MyBlog/2020-05-16-install-manjaro.md @@ -0,0 +1,46 @@ +--- +title: install manjaro20 with windows10 +date: 2020-06-04 19:42:04 +tags: +categories: + - Blog +--- + +**目录 start** + +1. [安装Manjaro20](#安装manjaro20) + +**目录 end**|_2020-06-04 19:42_| +**************************************** +# 安装Manjaro20 + +前提;首先系统已经安装Win10, 固态硬盘 + +1. 先做U盘(原先都是使用Deepin的制作器,这次不灵了,还尝试了 dd ImgWriter,最终使用rufus成功) +1. 确认BIOS关闭 Fast Boot +1. 按以往经验(曾安装Win10 Deepin Manjaro三系统均正常使用)直接以UEFI模式启动安装 进行到最后一步失败了 [和该楼主问题基本一致](https://forum.manjaro.org/t/manjaro-20-0-installation-failed-on-tongfang-s41b-boost-python-error-in-job-bootloader/138889) + 1. 但是通过该楼主的方式,能通过U盘启动系统后,手动找引导,进入在固态上安装引导失败的Manjaro系统 [Using livecd v17.0.1 (and above) as grub to boot OS with broken bootloader](https://forum.manjaro.org/t/using-livecd-v17-0-1-and-above-as-grub-to-boot-os-with-broken-bootloader/24916) 虽然这是不符合需求的。 + 1. 一些命令记下备用: `init -Fxxxz` 查看硬件和分区信息 partd -l 查看分区模式 +1. 最终发现不应该使用UEFI模式安装[Installation of msdos disk in UEFI - A Warning](https://forum.manjaro.org/t/installation-of-msdos-disk-in-uefi-a-warning/15120) 楼主比较熟悉 grub,因为Windows10使用的MBR方式安装 坑啊 +1. 所以只能使用legacy模式安装,一切重来,按部就班.. 分区 /boot 512M 其余都给 / +1. 安装成功 + +![在这里插入图片描述](https://img-blog.csdnimg.cn/2020051717181055.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2tjcDYwNg==,size_16,color_FFFFFF,t_70) + +************************ + + +- 顺带一提,公司电脑也是主力使用Manajro,某次滚动升级了内核到5.4后,由于没有升级virtualbox的对应包,导致virtualbox一运行就假死,然后尝试了多次后整个系统无任何响应 +- 然后就强制关机了,没想到这个强制关机引起了BIOS的错误,BIOS莫名其妙被设置成了 legacy +- 引导项直接就默认Win10了,Deepin和Manjaro引导都不见了,但是通过u盘进入Linux系统后能通过chroot-找到对应的Deepin和Manjaro系统 学习到了 以后修复系统用 + ```bash + mkdir /mnt/deepin + mount /dev/sda1 /mnt/deepin + # bash 语法 + for dir in dev proc sys etc bin sbin var usr lib lib64; do + mkdir /mnt/deepin/$dir && mount --bind /$dir /mnt/deepin/$dir + done + chroot /mnt/deepin + ``` +- 在Windows下EasyBCD瞎折腾了几下,没有生效,仔细看BIOS设置才发现原因 +Linux 掉电或者强制关机是个大坑!! diff --git a/MyBlog/Flink-with-batch.md b/MyBlog/Flink-with-batch.md new file mode 100644 index 0000000..27d7533 --- /dev/null +++ b/MyBlog/Flink-with-batch.md @@ -0,0 +1,184 @@ +--- +title: Flink-with-batch +date: 2019-06-17 17:00:27 +tags: +categories: + - Blog +--- + +**目录 start** + +1. [Flink 中的批处理](#flink-中的批处理) + 1. [为何有这次分享](#为何有这次分享) + 1. [Flink 是什么](#flink-是什么) + 1. [为何选用Flink](#为何选用flink) + 1. [基础概念和原理](#基础概念和原理) + 1. [流处理和批处理](#流处理和批处理) + 1. [部署](#部署) + 1. [批处理](#批处理) + 1. [Rest API](#rest-api) + +**目录 end**|_2020-06-04 19:41_| +**************************************** +# Flink 中的批处理 + +## 为何有这次分享 +最近实现的需求是采用的该架构设计实现, 也是一边学习一边思考整理 + +https://www.cnblogs.com/ken-io/p/knowledge-or-technology-share-guide.html + +https://blog.csdn.net/wenxueliu/article/details/89484375 + +************************ + +## Flink 是什么 +> [官网](https://flink.apache.org/) + +Flink 是一个批处理和流处理结合的统一计算框架,其核心是一个提供了数据分发以及并行化计算的流数据处理引擎。 + +最开始08年是柏林理工大学的一个研究项目,14年成为Apache孵化项目, 15年阿里fork并开发了Blink分支, 之后又并入社区 +在实践中做了诸多优化 例如 增量 checkpoint, 重构调度和资源管理, 以支持 Yarn K8s + +## 为何选用Flink +> 阿里, 滴滴,美团,字节跳动 等公司使用和开发, 其中在阿里双十一大屏的应用场景上达到了 4.72亿次/秒. + +`State` + +Flink的优势是支持有状态的计算, 如果一个事件或一条数据处理结果只与本身内容有关, 那么就是无状态的, 反之则是与之前处理过的事件有关, 称为有状态, 最常用的应用场景就是有状态的处理 比如 聚合,关联 等操作 + +`Checkpoint` + +利用了经典的 Chandy-Lamport 算法, 核心思想是 把这个流计算看成一个流式的拓扑, 定期从这个拓扑的头部 source 节点, 开始插入特殊的屏障, 从上游开始不断往下游广播这个屏障, 每一个节点收到屏障后, 会将State做一次快照, 当每个节点都做完了快照, 整个拓扑就算是完成了一次 CheckPoint + +Flink的容错机制的核心部分是生成分布式数据流和算子状态的一致快照。从而提供了 exactly-once 的语义(输入的数据作用在最终结果上有且仅有一次), 能更容易的管理状态, 就像操作普通集合 + +![](https://ci.apache.org/projects/flink/flink-docs-release-1.8/fig/stream_barriers.svg) + +`Windows` + +因为不可能计算流中的所有元素,因为流通常是无限的(无界)。所以流计算一般都会基于窗口来计算 +Flink提供了一套开箱即用的窗口操作, 例如翻滚窗口(没有重叠),滑动窗口(具有重叠)和会话窗口(由不活动间隙打断), 还支持自定义窗口。 + + +************************ + +>> 同类框架还有 经典的 Hadoop Map-Reduce, 架构简单, 不能支持复杂的应用逻辑, 需要应用逻辑 适配到 map-reduce算法, 仅支持批处理, + +Spark批处理, 流处理的实现是将批处理的批次分割成若干小 + +Flink基于每个事件一行一行地流式处理,真正的流式计算,流式计算跟Storm性能差不多,支持毫秒级计算,而Spark则只能支持秒级计算。 + +> [美团: 流计算框架 Flink 与 Storm 的性能对比](https://tech.meituan.com/2017/11/17/flink-benchmark.html) + +## 基础概念和原理 + +### 流处理和批处理 + +![](https://ci.apache.org/projects/flink/flink-docs-release-1.8/fig/levels_of_abstraction.svg) + +批处理主要操作大容量静态数据集,并在计算过程完成后返回结果。 + +- 批处理模式中使用的数据集通常符合下列特征: + 1. 有界:批处理数据集代表数据的有限集合 + 1. 持久:数据通常始终存储在某种类型的持久存储位置中 + 1. 大量:批处理操作通常是处理极为海量数据集的唯一方法 + +批处理非常适合需要访问全套记录才能完成的计算工作。例如在计算总数和平均数时 + +- 流处理中的数据集是“无边界”的: + 1. 完整数据集只能代表截至目前已经进入到系统中的数据总量。 + 1. 工作数据集也许更相关,在特定时间只能代表某个单一数据项。 + 1. 处理工作是基于事件的,除非明确停止否则没有“尽头”。处理结果立刻可用,并会随着新数据的抵达继续更新。 + +流处理系统可以处理几乎无限量的数据,但同一时间只能处理一条(真正的流处理)或很少量(微批处理,Micro-batch Processing)数据,不同记录间只维持最少量的状态。虽然大部分系统提供了用于维持某些状态的方法,但流处理主要针对副作用更少,更加功能性的处理(Functional processing)进行优化。 + +************************ + +### 部署 +> Local, Standalone Cluster, YARN Cluster + +- Local 模式的 JobManager 和 TaskManager 只使用一个 JVM 来完成整个计算, 常用于小数据量的开发测试 +- Standalone Cluster 模式的 JobManager 和 TaskManager 都是单点且各自独立的, 例如以下通过 docker-compose 部署的案例 +- Yarn Cluster 也就是完全没有单点问题 + +```yml + version: "2.1" + services: + jobmanager: + image: ${FLINK_DOCKER_IMAGE_NAME:-flink} + expose: + - "6123" + ports: + - "8081:8081" + command: jobmanager + environment: + - JOB_MANAGER_RPC_ADDRESS=jobmanager + + taskmanager: + image: ${FLINK_DOCKER_IMAGE_NAME:-flink} + expose: + - "6121" + - "6122" + depends_on: + - jobmanager + command: taskmanager + links: + - "jobmanager:jobmanager" + environment: + - JOB_MANAGER_RPC_ADDRESS=jobmanager +``` + +![](https://www.ibm.com/developerworks/cn/opensource/os-cn-apache-flink/img001.png) + + +- 我们可以了解到 Flink 几个最基础的概念,Client、JobManager 和 TaskManager。 + - Client 用来提交任务给 JobManager,JobManager 分发任务给 TaskManager 去执行,然后 TaskManager 会心跳的汇报任务状态。 + +- 在 Flink 集群中,计算资源被定义为 Task Slot。每个 TaskManager 会拥有一个或多个 Slots。JobManager 会以 Slot 为单位调度 Task。 + - 这里可以将 Slot 类比为线程池中的 Worker, 都是资源的抽象, 但是 Slot 更关注内存 + +对于应用开发层面的基础概念有 + +- Source(源)是指数据流进入系统的入口点 +- Stream(流)是指在系统中流转的,永恒不变的无边界数据集 +- Operator(操作方)是指针对数据流执行操作以产生其他数据流的功能 +- Sink(槽)是指数据流离开Flink系统后进入到的位置,槽可以是数据库或到其他系统的连接器 + +- Flink 最适合的应用场景是低时延的数据处理场景:高并发处理数据,时延毫秒级,且兼具可靠性: + 1. 低时延:提供 ms 级时延的处理能力。 + 1. Exactly Once:提供异步快照机制,保证所有数据真正只处理一次 + 1. HA:JobManager 支持主备模式,保证无单点故障。 + 1. 水平扩展能力:TaskManager 支持手动水平扩展。 + +## 批处理 +> [Doc](https://ci.apache.org/projects/flink/flink-docs-release-1.8/dev/batch/) + +> [数据转换接口](https://ci.apache.org/projects/flink/flink-docs-release-1.8/dev/batch/#dataset-transformations) + +批处理也被称为离线计算 实时性要求不高 但是数据量大 + +大致工作流程, 首先 JobManager 生成执行计划的 DAG , 然后发布 Task 给 TaskManager 并行执行 +由于是有状态的计算,数据不加以同步的话, 就会混乱, 所以 Flink 通过 [超级步骤同步来保证结果的正确](https://ci.apache.org/projects/flink/flink-docs-release-1.8/dev/batch/iterations.html#superstep-synchronization) 类似于 Java 并发类中的 CountDownLatch + +其中 这个同步就要求我们应用代码中的 Source Sink 组件中的成员属性 是必须为序列化的, 且不含 transient 关键字修饰的属性 +否则 就会报错, 因为无法做到 Task Slots 之间同步数据了, 后续的计算也就无意义了 + +### Rest API +> [Doc: REST](https://ci.apache.org/projects/flink/flink-docs-stable/monitoring/rest_api.html) + +由于批处理不需要实时处理, 所以设计上是按需使用 而不是常驻进程, 所以需要一种机制来调起批处理来执行, 因此有了Rest接口, 但是Rest接口还提供简单的监控 + +> 上传 + +- `/jars/upload` + - 返回结果 {"filename":"/tmp/flink-web-5f9f59f8-9f60-4ccc-a5ae-360cdde7f618/flink-web-upload/ae0dd296-b4ee-4667-9ba8-1c7b374d694c_flink-1.0.0-SNAPSHOT-all-dependency.jar","status":"success"} + - `curl -X POST -H "Expect:" -F "jarfile=@filepath" http://127.0.0.1:8081/jars/upload` + +> 启动 + +- post: `/jars/{jarid}/run` + - 参数可选 entry-class program-args/programArg + - curl -X POST http://127.0.0.1:8081/jars/ae0dd296-b4ee-4667-9ba8-1c7b374d694c_flink-1.0.0-SNAPSHOT-all-dependency.jar/run\?entry-class\=com.github.kuangcp.hi.SimpleStatistic + +> 注意: 这里的 参数 只能为单个字符串 或者逗号分割的参数列表, 不能传入 JSON 格式的字符串, 最后解决是用BASE64编码处理 + diff --git a/MyBlog/Readme.md b/MyBlog/Readme.md new file mode 100644 index 0000000..62680d2 --- /dev/null +++ b/MyBlog/Readme.md @@ -0,0 +1,4 @@ +# Blog + +> [小马哥技术周报](https://github.com/mercyblitz/tech-weekly) +> [科技爱好者周刊](https://github.com/ruanyf/weekly) diff --git a/MyBlog/how-to-design-notice-unread.md b/MyBlog/how-to-design-notice-unread.md new file mode 100644 index 0000000..489ef2a --- /dev/null +++ b/MyBlog/how-to-design-notice-unread.md @@ -0,0 +1,84 @@ +--- +title: how-to-design-notice-unread.md +date: 2021-01-13 19:47:52 +tags: +categories: +--- + +**目录 start** + +1. [设计公告通知未读](#设计公告通知未读) + 1. [1. 关联表存 已读或未读](#1-关联表存-已读或未读) + 1. [2. Redis Set 存储 未读/已读](#2-redis-set-存储-未读已读) + 1. [3. Redis bitmap 存储 未读/已读](#3-redis-bitmap-存储-未读已读) + 1. [4. 客户端存储已读](#4-客户端存储已读) + +**目录 end**|_2021-01-15 21:26_| +**************************************** +# 设计公告通知未读 + +> 需求: 发布公告,用户维度的已读未读,有一键已读功能,首条未读需求 + +## 1. 关联表存 已读或未读 +> 存储 +>- 公告表 +>- 公告和用户关联表 + +`存已读` +1. 优点: 新增无需操作关联表,利于统计分析 +1. 缺点: 一键已读就需要未读的关联数据批量写入,数据量会持续增长,数据不够稀疏不便建立索引 + +`存未读` +1. 优点: 新增无需操作关联表,利于统计分析 +1. 缺点: + - 新增公告时,大量数据写入(为每一个用户新增),新注册用户需要补全部数据 + - 已读则删除数据(使用物理删除可能存在缺页问题,使用逻辑删除则数据持续增长) + +************************ + +## 2. Redis Set 存储 未读/已读 +> 存储 +>- 公告表 +>- Redis存储 Set key为用户id,value为公告id + +`存已读` +1. 优点: + - 新增公告无需操作关联关系 + - 用户查询公告未读状态,只需一次请求 +1. 缺点: + - 全部已读需要查出所有公告id插入Redis Set + - Redis占用的内存会持续增长 + +`存未读` +1. 优点: 用户查询公告状态,只需一次请求,全部已读也只需一次请求 +1. 缺点: 新增公告时需要遍历请求所有用户插入公告id + +************************ + +## 3. Redis bitmap 存储 未读/已读 +> 存储 +>- 公告表 +>- Redis存储 key为公告id bitmap存已读的用户id + +- 所有公告分页查询时,先得到Mysql分页第一页的公告id,然后遍历查询Redis已读未读状态 +- 未读公告的分页,需要查询所有的公告id(组织注册时间后的公告), 遍历请求redis得到 未读的公告id, 再进入Mysql 使用 in 进行分页 + + +1. 优点: 新增公告无需维护关联关系 +1. 缺点: + - 分页查看所有未读的需求会随着公告的增多循环次数会增多 + - 全部已读需要遍历所有公告 + - 随着用户数增多单个key占用的内存持续增长 + +可以通过存储用户最后一次 所有公告已读 的时间戳,减少循环次数 + +************************ +## 4. 客户端存储已读 +> 存储 +>- 公告表 +>- 客户端 Cookie 或者 LocalStorage 存储已读的公告id + +1. 优点: 服务端存储运算压力减轻 +2. 缺点: + - 首条未读查询接口需要传入已读的id列表,公告多的时候会导致SQL超长 + - 无法多端同步状态(其实也可以做,定时同步已读id列表),全部已读需要写入所有公告id到本地 diff --git a/MyBlog/server-push.md b/MyBlog/server-push.md new file mode 100644 index 0000000..236302e --- /dev/null +++ b/MyBlog/server-push.md @@ -0,0 +1,81 @@ +--- +title: 服务端推送消息 +date: 2020-12-15 14:15:47 +tags: +categories: +--- + +💠 + +- 1. [Server push](#server-push) + - 1.1. [企业平台](#企业平台) + - 1.1.1. [GoEasy](#goeasy) + - 1.2. [轮询](#轮询) + - 1.3. [长连接](#长连接) + - 1.3.1. [SSE](#sse) + - 1.3.2. [Mercure](#mercure) + - 1.3.3. [Comet](#comet) + - 1.3.4. [Websocket](#websocket) + - 1.4. [DWR](#dwr) + - 1.5. [HTTP2协议](#http2协议) + +💠 2024-09-20 11:52:03 +**************************************** +# Server push + +## 企业平台 + +### GoEasy +- [官网](http://goeasy.io/cn/started) 免费12个月,后续要收费,这个的使用示例比较简单。 + +************************ + +## 轮询 +前端使用 ajax 通过定时器的方式 的发起请求(最简单也是最容易耗尽服务器资源) + +## 长连接 +客户端发起一个HTTP请求,服务端不关闭,持续持有,等到数据准备好了或特定事件发生后才发送response并关闭这个连接 + +### SSE +> 本质是使用HTTP流式长连接(和下载文件类似) + +- [Server-Sent Events(服务器推送) 教程](https://blog.p2hp.com/archives/7660) +- [sse demo](https://github.com/jokerwangJL/ssedemo) + +- 优点 + - 当数据变更不频繁时大大减少请求次数,即使数据变更频繁也近似于轮询 +- 缺点 + - 维持长连接占用资源 +- 案例 + - QQ邮箱 + +### Mercure +> [mercure](https://github.com/dunglas/mercure) + +Mercure 是一种向网络浏览器和其他 HTTP 客户端推送数据更新的协议 + +### Comet +- [github: comet4j 项目](https://github.com/jwangkun/comet4j) 可以直接下载配置jar到tomcat下使用 +- [参考博客:comet4j java服务端推送消息到web页面实例](http://blog.csdn.net/shadowsick/article/details/9014139) + +- 优点 +- 缺点 + +### Websocket +> [Websocket 详解](/Skills/Network/Network.md#websocket) + +- 优点 + - 标准协议,兼容性好,使用广泛 +- 缺点 + +## DWR +> [官网](http://directwebremoting.org/dwr/index.html) + +- 使用xmpp协议的一种技术,能够做到js中调用服务器的Java方法 + - [参考博客:Spring整合DWR comet 实现无刷新 多人聊天室](http://www.cnblogs.com/hoojo/archive/2011/06/08/2075201.html#top) + +- 优点 +- 缺点 + +## HTTP2协议 +> 刚开始提出 Server Push 特性的时候很多看好,但是现在HTTP3趋势下 该特性又沉寂了 diff --git a/MyBlog/twenty-years-of-weak-japan.md b/MyBlog/twenty-years-of-weak-japan.md new file mode 100644 index 0000000..59591eb --- /dev/null +++ b/MyBlog/twenty-years-of-weak-japan.md @@ -0,0 +1,37 @@ +--- +title: 日本失落的二十年 +date: 2018-12-23 22:29:47 +tags: + - 历史 +categories: + - Blog +--- + +**目录 start** + +1. [日本失落的二十年](#日本失落的二十年) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# 日本失落的二十年 + +> [为何中国不学当年日本那样主动刺破房地产泡沫?](https://www.cnblogs.com/findumars/p/9249102.html) +> [参考: 日本失去的20年](https://wenku.baidu.com/view/5bc3e1add15abe23482f4dbd.html) +> [找回“失去的20年”?日本股市创27年新高,但一个数字不容乐观](http://www.nbd.com.cn/articles/2018-09-29/1259326.html) +> [日本,失落的二十年?](https://www.jianshu.com/p/a212fb7c5ac2) + +泡沫破裂致“失去的 20 年” 日本的房地产泡沫起因,普遍认为始于 1985 年“广场协议”签订后的日元 升值, 给日本经济带来繁荣的制造业出口, 陷入了困境, 要保持日本经济继续高 速增长,必须寻找新的经济增长点,房地产成了不二选择。 +于是, 日本政府开始加速全面推行住宅商品化, 商业银行大量为居民购房发 放贷款,贷款利率大幅下调。日本的房价、地价开始急速攀升。 + +同期, 政府为了纠正日元过度升值大量地干预汇市并且大幅放松银根, 造成 了流动性过剩的局面, 房产、 土地成为居民寻求货币保值的替代品。 当大笔资金 涌入不动产市场而使其升值时, 保有需求被打开了, 房子、 土地不再是一种使用 需求,而是投资品种。 + +从历史上看, 1961 年、 1973 年和 1990 年,是日本房价、地价涨幅的三个峰 值。 但前两次大涨, 日本处在 8% 以上的经济高增长期, 同时伴随着城镇化进程, 有经济面和自住需求面的支撑,并没有形成大泡沫。 +但到 1974 年时,日本的城镇化率已经达到 74.9% ,此后 20 年,日本的城镇 化进程实际上已停滞, 此时的房地产脱离了自住需求的有效支撑, 由投资性质支 撑的市场形成了大泡沫。 + +在日本意识到不动产的高速发展已经严重影响到了日本经济的未来发展方 向后,采取了紧急更正。 1990 年 3 月,日本银行出台的《控制不动产融资总量 的通知》 成为刺破日本泡沫经济的导火索。 + +从 1989 年 3 月到 1990 年 8 月, 日本银行连续 5 次提高贴现率,过于急剧的货币紧缩加速了资产市场崩溃。 1992 年 大藏省又出台了地价税,征收额度为财产继承税评估值的 0.3% ,让已经一蹶不 振的不动产业更是雪上加霜。 +从金融到土地的双管齐下的政策调控, 导致了泡沫 的快速破灭。 + +此外,由于`日元升值`,外资涌入日本吹大了股市和楼市的泡沫,但从 1990 年起, 外资开始撤离日本, 许多境外投资机构通过挤破日本股市和楼市泡沫赚得 盆满钵满。 +日本经济陷入泥潭至今无法自拔,这 20 年被称为“失去的 20 年”。 diff --git a/MyBlog/why-put-netty-in-tomcat.md b/MyBlog/why-put-netty-in-tomcat.md deleted file mode 100644 index 1433b97..0000000 --- a/MyBlog/why-put-netty-in-tomcat.md +++ /dev/null @@ -1,28 +0,0 @@ -`目录 start` - -- [为何要将Netty放在Tomcat下](#为何要将netty放在tomcat下) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 为何要将Netty放在Tomcat下 -> 2018-03-19 17:41:28 - -> [stackoverflow上的相关问题](https://stackoverflow.com/questions/3194508/is-hosting-a-netty-server-inside-tomcat-feasible-desirable/3200624#3200624) -[知乎相关提问](https://www.zhihu.com/question/21472041) -[Tomcat中如何使用netty的功能?](https://bbs.csdn.net/topics/390763179) -[netty导致tomcat假死](http://blog.csdn.net/aishangyutian12/article/details/52251357) -[tomcat为什么没有用netty作为底层通信框架?](https://www.zhihu.com/question/58796648) -[请教,netty如何跟运行在tomcat中的web应用交互呢? ](http://www.oschina.net/question/2762305_2191710) - -- 然后我们老大就说 是因为Tomcat的环境具有一致性, 例如日志的方式以及日志文件的位置等等, - - 因为是部署到甲方那边去, 所以不能由着自己性子来, 就不能用Docker来做环境的统一化部署了 - - 日志的一些配置什么的,能够在部署的时候进行修改和切换 -- 例如运行环境 - - 感觉这个是最大的一个原因了, 因为甲方的电脑环境, 肯定都是乱七八糟什么玩意都有的, 对于我这个war来说的话, 只要有能正常运行的tomcat, 我就没有什么问题了 - - 但是如果抛开Tomcat, 弄成jar运行, 就比较麻烦了(主要还是为了日志和启动脚本,emmm) -- 还有就是为了使用丰富的协议, 例如Websocket Hybrid 等等, 就还是不能脱离Tomcat - - -> 2018-06-23 21:01:11 -- 其实是可以直接将 Tomcat有关的依赖全部去除 Maven打包成jar, 配置下Main方法就能直接启动了, 而且资源消耗还少 - diff --git a/Platform.md b/Platform.md deleted file mode 100644 index 018fd46..0000000 --- a/Platform.md +++ /dev/null @@ -1,339 +0,0 @@ -`目录 start` - -- [代码托管平台](#代码托管平台) - - [Gitee](#gitee) - - [URL规则](#url规则) - - [Github](#github) - - [URL规则](#url规则) - - [MarkDown规则](#markdown规则) - - [wiki](#wiki) - - [Bandage图标](#bandage图标) - - [Gitea](#gitea) - - [自建](#自建) -- [二进制包仓库](#二进制包仓库) -- [代码质量检测平台](#代码质量检测平台) - - [codeclimate](#codeclimate) - - [codebeat](#codebeat) - - [codacy](#codacy) -- [综合开发平台](#综合开发平台) - - [华为云](#华为云) - - [阿里云](#阿里云) - - [ECS](#ecs) - - [ACM](#acm) - - [OSS](#oss) - - [百度开发平台](#百度开发平台) - - [CCE](#cce) - - [BOS](#bos) - - [BAE](#bae) - - [其他应用](#其他应用) - - [翻译](#翻译) - - [腾讯](#腾讯) - - [域名](#域名) - - [CVM](#cvm) - - [COS](#cos) - - [微信公众号](#微信公众号) - - [网易](#网易) - - [有道智云](#有道智云) - - [微软](#微软) - - [翻译](#翻译) -- [云存储](#云存储) - - [NextCloud](#nextcloud) - - [坚果云](#坚果云) - - [七牛云](#七牛云) -- [智能机器人平台](#智能机器人平台) - - [图灵机器人](#图灵机器人) -- [评论系统](#评论系统) -- [消息推送](#消息推送) - - [极光推送](#极光推送) - - [GoEasy](#goeasy) -- [文档](#文档) - - [文档托管](#文档托管) - - [文档转换](#文档转换) -- [接口平台](#接口平台) - - [今日头条](#今日头条) -- [测试平台](#测试平台) -- [培训](#培训) - -`目录 end` |_2018-08-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** - -# 代码托管平台 -## Gitlab -> [Site](https://gitlab.com/) - -功能丰富, 集成了 CI/CD, 也能自己搭建 社区版, 免费的私有库, 用于团队协作的功能比较多 - -## Gitee -> 码云,国内的github [帮助文档](http://git.mydoc.io/) - - -也是有私有库, 但是私有库有数量限制, 不过那个数挺大的, 1000 还是多少, 但是分支图的显示不喜欢, 分支都没有闭合, 真难受 - -### URL规则 -- HTTPS & SSH - - `HTTPS:` https://gitee.com/kcp1104/MythRedisClient.git - - `SSH:` git@gitee.com:kcp1104/MythRedisClient.git - -*********************************** -## Github -> 全球范围性的网站 [开发者文档](https://developer.github.com/v3/) - -1. 在github上修改了项目后,或者以后是和别人一起开发,就要先git pull origin (master)将别人的分支和自己的分支都拉下来确保是最新, - - 再进行`git push -u origin master` 才能正确提交代码,如果不pull,提交是注定失败的,还会扰乱分支图 - -- [少有人知的 GitHub 使用技巧](https://segmentfault.com/a/1190000000475547) -### URL规则 -> github仓库的URL规则 ->> git.io 是短网址服务 -- HTTP & SSH - - `SSH:` git@github.com:Kuangcp/Script.git - - `HTTPS:` https://github.com/Kuangcp/Script.git - -- 目录: - - `https://github.com/用户/项目/tree/分支/相对根目录的目录` -- 文本文件: - - `https://github.com/用户/项目/blob/分支/文件目录` -- 二进制文件,例如图片: - - `https://raw.githubusercontent.com/用户/项目/分支/文件目录` - -> 例如同仓库下的这个文件`/Linux/Docker.md` 可以直接这样写,方便调用,最好最前面不要加`.`这个表示当前目录的 - -### MarkDown规则 -> [更多详情>>](/Skills/Document/MarkDown.md#github) - -### wiki -> 其实也是一个git仓库, 以特定格式进行显示而已 -- 侧栏的目录依赖于 `_Sidebar.md` 文件 -其显示规则类似于Markdown语法: -``` - * [[Introduction|Home]] - * Basic Utilities - * [[Using/avoiding null|UsingAndAvoidingNullExplained]] -``` -> 其中 [[名称|URL]] 类似于 []() URL的md后缀也要去掉,不然就跳转到md文件的源路径了 -> 允许md以文件夹多结构的形式存在, 但是最终的目录规则是扁平的, 直接就是文件名,没有目录名 - -### Bandage图标 -> [shields](https://shields.io/) - -- 规则 https://img.shields.io/badge/note-JavaSE-blue.svg - - 如果是中文则要转码 - - 颜色: brightgreen green yellowgreen yellow orange red lightgrey blue ff69b4 (也就是说可以设置任意颜色) - -********************************************* -## Gitea -> [官网](https://gitea.io/zh-cn/) - -### 自建 -- 使用docker安装比较简单 - - 配置数据库,一定要是外网的。或者容器互联 -`/data/gitea/conf/app.ini` Docker中要修改的配置,都是改成对外的配置 -```conf -ROOT_URL = http://git.kuangcp.top/ -DOMAIN = git.kuangcp.top -SSH_PORT = 22 -SSH_DOMAIN = kuangcp.top -``` -然后还需修改本地的SSH配置才能正常使用 -******************************************************** -# 二进制包仓库 -- [JFrog Bintray](https://bintray.com/kuangcp) -- [Maven Central Repository ](http://mvnrepository.com/) - -# 代码质量检测平台 -## codeclimate -> [官网](https://codeclimate.com/dashboard) - -要求比较严格, 有详细的说明 -## codebeat -> [官网](https://codebeat.co/) - -要求比较严格 -## codacy -> [官网](https://app.codacy.com/projects) - -要求比较宽松 - -***************************** -# 综合开发平台 -## 华为云 -- [CSE微服务相关文档](http://support.huaweicloud.com/devg-cse/cse_03_summary.html) - -******************************************** -## 阿里云 - -### ECS -> 阿里云主机, 学生有优惠 9.9每月 1核2g - -> 域名因为是兼并了万网,所以功能更强大, 现在还支持搭建免费的DNS服务器了 -### ACM -> 应用配置管理 [入门文档](https://help.aliyun.com/document_detail/59964.html?spm=a2c4g.11186623.6.546.yaM0cp) -> [如何用ACM简化你的Spring Cloud微服务环境配置管理](https://zhuanlan.zhihu.com/p/33525168?group_id=942728800581259264) - -### OSS -> 对象存储 - -``` -资费项 计费项 标准型单价 低频访问型单价 归档型单价 -存储费用 (注①) 数据存储 0.148元/GB/月 0.08元/GB/月 0.033/GB/月 -流量费用 (注①) 内/外网流入流量(数据上传到OSS) 免费 免费 免费 - 内网流出流量(通过ECS云服务器下载OSS的数据) 免费 免费 免费 - 外网流出流量 00:00-08:00(闲时):0.25元/GB 8:00-24:00(忙时):0.50元/GB - CDN回源流出流量 0.15元/GB 0.15元/GB 0.15元/GB - 跨区域复制流量 0.50元/GB 0.50元/GB 0.50元/GB -请求费用 - 所有请求类型 0.01元/万次 0.1元/万次 0.1元/万次 -数据处理费用 (注②) - 图片处理 每月0-10TB:免费>10TB:0.025元/GB 每月0-10TB:免费>10TB:0.025元/GB 无 - 视频截帧 0.1元/千张 0.1元/千张 无 - 数据取回 免费 0.0325元/GB 0.06元/GB -``` -************************************** -## 百度开发平台 - -********** -### CCE -> 容器引擎 -> [入门必看](https://cloud.baidu.com/doc/CCE/GettingStarted.html) -> 其实就是镜像仓库,比阿里云的好用 域名很短 - -### BOS -> 对象存储 [计价方式](https://cloud.baidu.com/doc/BOS/Pricing.html#.E6.8C.89.E9.9C.80.E8.AE.A1.E8.B4.B9) -> 关于流量定价中的CDN回源流量, 大致是当你开了CDN加速, 然后CDN为了刷新缓存要去你对象存储源获取最新的文件, 这个消耗的流量 -> 同样支持文件夹 - -_存储空间价格_ -``` -计费项 标准存储单价(元/GB/月) 低频存储单价(元/GB/月) 冷存储单价(元/GB/月) -存储空间 0.128 0.08 0.048 -``` -_请求次数价格_ -``` -计费项 规格 标准存储单价(元/万次) 低频存储单价(元/万次) 冷存储单价(元/万次) -写请求次数 PUT,COPY,DELETE 0.01 0.25 0.5 -读请求次数 GET Bucket,GET OBJECT及其他所有请求 0.01 0.05 0.1 -``` -_数据取回价格_ -``` -计费项 规格 标准存储单价(元/GB) 低频存储单价(元/GB) 冷存储单价(元/GB) -数据取回 - NA 0.03 0.15 -``` -_流量价格_ -``` -计费项 标准存储 & 低频存储 & 冷存储单价(元/GB) -外网数据流出 0.6 -CDN回源流出 0.14 -跨区域数据流出 0.6 -``` -**************** -### BAE -> 应用引擎,简单的说就是一个提供了环境,你只需上传打包好的可执行文件就可以运行起来了 -> 本来的话是比较简单容易上手的, 但是现在要备案了, 就玩不了了 - -- 短期使用收费没有很高,十分灵活,就是前期学习入门 配置略麻烦。适合演示使用,例如毕设。 - - 并且还提供一定免费额度的 MySQL Redis MongoDb (只能BAE的内网访问) - - 还有自动测试 - -******************* -### 其他应用 -- [百度脑图](http://naotu.baidu.com/)`在线思维导图创作` -- [站内搜索](https://zn.baidu.com/cse/home/index)`不用自己写搜索了` - -#### 翻译 -> [官网](http://api.fanyi.baidu.com/api/trans/product/index) `免费注册, 每月有 2000,000 字符 免费额度` - -******************************************************* -## 腾讯 -### 域名 -> [域名检测工具](https://cloud.tencent.com/product/tools) -### CVM -> 云服务器 -> 学生优惠是 10元每月1核1g 60则是2核2g - -### COS -> 对象存储 [官方文档](https://cloud.tencent.com/document/product/436) -> 同样的支持文件夹,还可以拖动文件夹上传,算是最好用的一个了 - -_免费额度_ -``` - 资源类型 资源子类型 每月免费额度 - 存储空间 存储空间 50 GB - 流量 外网下行流量 10 GB - 流量 腾讯云 CDN 回源流量 10 GB - 请求 读请求 100 万次 - 请求 写请求 100 万次 -``` - -### 微信公众号 -`2017-12-21 21:41:43` -- 不说了反正都是Shit一样的接口设计和返回值 希望会变好,碰过就不想再弄了!!! - -## 网易 -### 有道智云 -> [官网](https://ai.youdao.com/gw.s) - -## 微软 - -### 翻译 -`每月 2000,000 字符免费` - -> [文档](https://azure.microsoft.com/en-us/services/cognitive-services/translator-text-api/) -> [api](https://docs.microsoft.com/en-us/azure/cognitive-services/translator/reference/v3-0-reference) -> [定价](https://azure.microsoft.com/en-us/pricing/details/cognitive-services/translator-text-api/) - -************************************************* -# 云存储 -## NextCloud -> 当自己具有服务器的时候, 搭建自己的私有云盘 - -## 坚果云 - -## 七牛云 -> 免费存储额度10g流量额度10g, 但是不能创建文件夹 - -**************************************************** -# 智能机器人平台 -## 图灵机器人 - -************** -# 评论系统 -- [畅言](http://changyan.kuaizhan.com/) - -************************************************ -# 消息推送 -## 极光推送 -> [官网](https://www.jiguang.cn/) `做Android IOS的消息推送和短信等推送` - -## GoEasy -- [示例](http://goeasy.io/cn/started) - -********************************* -# 文档 -## 文档托管 -- [看云](https://www.kancloud.cn/dashboard) - -_showdoc_ -- [showdoc](https://www.showdoc.cc/web/#/)`开源,也具有Docker方式` | [示例](https://www.showdoc.cc/web/#/demo?page_id=7) -- Docker安装: [官方文档](https://www.showdoc.cc/web/#/help?page_id=65610) `跑起来也就几十M内存占用` - - `git clone https://github.com/star7th/showdoc` - - 进入项目目录 然后 `docker build -t showdoc ./` - - `docker run -d --name showdoc -p 4999:80 showdoc` - - `http://localhost:4999/install/` 然后为了保险起见进容器删除项目根目录的install目录即可 - - 数据与备份 Sqlite/showdoc.db.php 是数据库; 如果有图片就还要备份图片,所以解耦就还是不上传图片了 - -## 文档转换 -- [pandoc](http://pandoc.org/try/) - -# 接口平台 -## 今日头条 -> 今日头条灵犬系统 API `https://m.toutiao.com/detector/api/detect/?text=`URL - -************************************************************ -# 测试平台 -- [自动API测试](https://www.eolinker.com/#/index) -- [吆喝科技](http://www.appadhoc.com/)`A/B测试 灰度上线` - -************************************************* -# 培训 -- [咕泡](http://www.gupaoedu.com/)`看起来很有深度, 就是有点贵` -- [集智](https://jizhi.im/index) - diff --git a/Process.md b/Process.md deleted file mode 100644 index c05c95d..0000000 --- a/Process.md +++ /dev/null @@ -1,242 +0,0 @@ -## 日记归档 -- [2017年](Article/backup/Process_2017.md) -- [2016年](Article/backup/Process_2016.md) - -## 2018-09-09 00:23:11 -- 在花钱方面也要有计划了, 面对需要的物品, 但是不是急需, 就应该先在各个平台挑好, 等做活动降价的时候买下来 -- 一方面让自己的生活和学习有了计划的习惯, 一方面也是省钱的方法, 首先从买书开始, 先去找好需要看的书, 加好购物车 - -## 2018-09-02 22:00:55 -- 不是说每天代码写得多就是厉害的, 有能力的, 而是要尽量简洁, 可维护性高的代码才是正确的, 目标不要放低到能运行就行 - - 所以就要提高抽象能力, 总结和归纳能力, 透过问题和需求, 看到实现这个业务逻辑的本质, 分析出合理的数据结构设计 - - 使用更为高效, 简易实现的方式 -1. 而且这么久了, 还是没有理解好OOP, 遇到一个需求, 一个问题, 首先需要划分好对象实体 - - 然后设计需要用的属性(也就是数据结构), 但是最重要的还是对象的行为, 也就是生命周期 -1. 至此才能在良好的设计下, 更简洁的实现逻辑, 否则就是采用模糊混乱的对象和过程, 来管理逻辑, 也就会遇到一堆的硬编码 - - 代码行数自然就少不了, 测试也更为麻烦, 所以还是那句话, 测试有多难写, 代码就有多垃圾 - - 测试要放在心上了 -1. 最后就是一直在说的命名问题了, 类, 方法的命名 - - -## 2018-08-26 22:13:56 -- 专注和责任感很重要, 当一个工作任务下发时, 合理安排时间, 尽可能早点完成, 改掉拖拉的毛病 -- 在设计上, 应该尽量追求低耦合和正交性 - -## 2018-08-05 13:35:53 -- 时间已经很快了, 一个月没有记录了, 这段时间里学到了编码规范和OOP的实践应用, 在编码时的思考和抽象 -- 确实需要每个星期整理下自己的所学所为了 -1. 这周完善了火狐的插件, 配置上更友好了, 但是随之而来的想要更丰富的功能时,就发现项目已经写到死了, 扩展很麻烦了, 所以需要使用新的架构的方式来重写了 -1. 整理了一堆要看的电子书, 立下了更多的flag, 还是要好好安排自己的学习计划, 首先将JDK的集合和常用类源码过一遍, 然后看虚拟机 -1. 学会更倾向于阅读官方的文档, 而不是别人二次消化的博客, 逼迫自己阅读英文 - -## 2018-07-06 19:51:42 -- 时间毫不留情的往前走, 而自己的能力并没有跟随时间一起积淀, 要更加努力了 -- 大学生涯结束了, 已经不是学生了, 要考虑现实问题了 - -## 2018-07-02 09:14:08 -- 这段时间的奔波, 学会了要教好自己小孩不要太吵闹, 小孩子玩也要适度, 学会体谅他人 -- 遇事不要怂, 不管是不是对方的过错, 气势上不能怂, 但也不是说就飚脏话什么的, 人都是欺软怕硬的! -- 银行体系中出现的漏洞, 普通百姓还真的就只能无奈面对了, 主导权在别人手上 - -## 2018-06-26 23:05:48 -- 你以为自己又挤进了人流的中心, 但只是你一个人的表演而已, 各自都有自己的诉求, 利益才是永恒, 不平等的利益交互都是在浪费时间 - -## 2018-06-24 11:42:54 -- 一点点学习怎么工程化的完成一个任务, 注意开发过程是相互协作的, 所以需要时刻注意, 别人如果要使用这个接口, 使用这个功能, 会不会感到很不舒服 - -## 2018-06-21 19:41:57 -- 别人的过失不能用来折磨自己, 要做好防范之心, 先小人后君子这句话是多么重要, - - 专注很重要, 专注于一件事, 并且迅速全力解决他, 越是拖拉越让自己觉得难以完成, 从而形成恶性循环 - -## 2018-06-18 00:07:40 -- 出行备好移动电源, 现金, 证件, 电子账户余额分开存放, 应对多种场景 - -## 2018-06-11 09:41:27 -- 枚举实现单例, 真是开眼界了, 枚举又多一个用处了, 但是在可读性上是不是有点不是很舒服, 相比于static的关系又如何? - -## 2018-06-05 23:36:39 -- 关于学习, 还是太浮躁了呢, 多思考, 多实践, 多总结, 才能在一步步的犯错和矫正中学习 - - 当看到复杂的逻辑和概念时, 需要借助自己的思维方式去理解, 利用作图等方式, 原理和设计思想清楚之后, 才能游刃有余的书写逻辑代码啊 -- 关于情绪, 要懂得如何释放自己的情绪, 而不是压抑着, 运动, 看感人的视频, 怎样都行吧, 控制自己生活的节律性 - -## 2018-06-01 11:44:31 -> https://www.amazon.com/Art-Readable-Code-Practical-Techniques/dp/0596802293 -- 命名规范和类设计的规范 - - 方法用动词, 变量用名词, 判断方法采用is has开头 - - 类的行数不是重点, 而是业务的内聚性, 归属于一个业务流程就要放在一起, 不要因为这个文件行数多, 就强行拆分一个类, - - 拆分出来的类,就一个地方引用了(原地方), 就没有拆分的必要了, -- 设计之初就要考虑重用性和扩展性 - -## 2018-05-28 16:19:31 -- 问题? 不存在的, 有的只有幸灾乐祸, 呵呵, 真忙, 忙到什么都不在乎, 只有进度和钱, 所以能学到什么 - -## 2018-05-27 23:26:00 -- 对待一个事物,需要表述严谨清晰, 面对复杂的事物更需如此, 你自己能明白表述所代表的含义, 但是和别人交流的时候就会有问题.从而浪费无谓的协调时间 - - 比如前端页面将非关键信息存放到storage中不是缓存, 而是静态配置数据, 缓存是读写的, 静态配置是只读的 -- 学习一项技术需要扎实, 将基础全都搞定了再去谈写工具,写项目 - -## 2018-05-20 21:49:30 -- 事情没有做好就该遭受这种烦心事?? -- 那么就需要做好这些事情了,尽量先摆脱这个事情!!! 很恶心?那有什么办法,没法逃避的,除非你不要毕业了,呵呵 -- 提需求一方是真的爽 TMD - -## 2018-05-09 23:39:29 -- 最后还是完成了,遇到的问题是我意想不到的,所以早就应该看清什么态势,合理推论,达到一个行为的最优解,这个能力真的是很需要的,相当于未仆先知了 -- 分析了下这几年,确实没有做好为人处世, 所以才是现在这个局面。 - -## 2018-05-08 19:29:39 -- 主动!! 没有人会为你考虑的,人人都在忙着自己的事情,凭什么要为你着想!! - -## 2018-05-07 21:30:38 -- 一个有人认真有人不认真的事情,结果怎么样就只能看缘分了, 勿气勿恼,做好自己的事情,能达到自己的目标即可, - - 即使如此,自己还是有很多不足的地方, 也没有达到想要的目标, 所以还是要好好加油啊,不要自己感动自己了 - - 任何事情只看结果的,人人都是如此!! -- 技术能力需要看重,业务能力也很重要, 这个系统的开发流程走下来, 自己的业务能力还是没有找到好的方式梳理和理解 - - 在开发过程中甚至出现了两次 功能模块的错误划分,开发完了才意识到,前期的准备太匆忙,很多抽象和组合没有好好设计 - - 虽然在开发过程中竭尽所能的将复制粘贴 操作次数 降到最低, 还是有部分功能源码复制粘贴再修改的。 - - 这是一个大问题!! - -## 2018-05-03 00:27:13 -- 自己作的, 现在是剧烈的透支自己的身体, 但是在这高压下也在锻炼自己的思维能力, 增强自己的工程意识,培养好的工作习惯. - -## 2018-04-30 23:18:42 -- 一种思路是从实现规范去学习, 在看抽象的规范的文档学习设计思想,并熟悉规范的实现的基础知识,从而更好的掌握对应的实现工具的使用 - -## 2018-04-28 08:28:02 -- 一直想到的是Gradle插件可能出问题了,没想到是IDEA的lombok插件不认识这个最新的IDEA,还好升级就解决了 - - 所以问题来了 Maven 和 Gradle中添加的Lombok依赖是干嘛的。 - -## 2018-04-24 13:35:42 -- 面对一个新的框架, 如果要学习使用, 就找demo, 然后看文档, 最简单的还是看源码看接口. -- 英文的文档多看看也是挺好的, 所以以后上下班就看文档吧 - -## 2018-04-16 18:09:36 -- 对代码结构和业务流程要尽量快的搞清楚, 分析问题的时候才能一步步分析,快速找到问题所在点 -- 身体是最重要的 - -## 2018-03-27 14:44:28 -- 不要为了技术而技术, 能够利用并解决问题就行了, 如果能更好的解决问题那么就是应该学习使用的, 但是要考虑学习成本, 迁移成本 - - 技术栈的更新没有那么容易的 - -## 2018-03-24 08:41:20 -- 昨晚的加班让我明白了, 不要轻易相信别人的代码, 当你的代码构建在一些别人所操作的对象上时, 你需要仔细想想,这个对象是否会符合你的预期 - - 当验证好自己的逻辑没有错的时候, 就需要检查下自己的依赖对象了 - -## 2018-03-21 22:57:11 -- 今天遇到了更为诡异的几个bug: - - 因为多个项目用的pom文件我估计是复制粘贴过去的, 然后基本没有改动, 就是改了下依赖 - - 然后我都导入IDEA了, 目录这边没有问题, Maven插件这边就懵逼了, 三个一模一样的项目出现了, 根本分不清, - - 虽然用package命令看输出 最终能看得出来哪个是哪个项目 - - 但是, 却有一个更骚操作的bug等着我, 在调试某一个功能的时候, 需要判断XXX执行XXX, 然后判断这里, 始终得到的值是true if里竟然不执行! - - 这个就6了, 别人环境还是正常的, 我这就不执行了 - - 解决方法: 直接删除该项目, 新建一个空Project 然后只把一个当前在开发的项目(Module)导入, 一切都好了 - - 我怀疑是Maven的依赖疯了, 编译就很诡异了 - - 然后写bug...写bug... 测试过程中, 刚才启动客户端还能连上我这, 重启下就不行了, 也没有报错, 执行完某个方法的`log.debug()`后就没然后了 - - 客户端在等服务端消息, 我也在等.... - - 各种重启, 就差重启电脑了, 因为开的东西比较多, 就不想重启电脑 - - 然后突然想到, 以前火狐和Chrome开发的时候也有遇到诡异的问题, 深度清理缓存就OK了 - - 那么这里, 清理一下操作系统的缓存是不是也能搞定了, 事实证明是正确的猜想 - -## 2018-03-20 17:57:16 -- Tomcat启动没有报错, 也没有端口占用, 用lsof查也查不到端口占用, 但是浏览器访问就访问不了, 一直转圈等待, - - 删掉这个tomcat, 然后终端窗口开的有点乱, 竟然在已经是在回收站里的tomcat的一个目录下进行命令操作 - - 还正常使用了, 难道这就是Linux的万物皆文件么, 回收站也只是一个目录而已 - - 最后用了 古老的netstat查到了端口的占用进程, kill掉了,号称取代netstat的iproute2里的 ss 反而找不到 emmm - -## 2018-03-19 09:00:47 -- ajax是异步的, 但是在页面上, 该请求的发送肯定是异步的, 但是在一个函数内, 代码块内, 还区分了同步异步, 据说是有个参数可以控制 -- http://blog.csdn.net/bKMk01MZ3w/article/details/79597595 - -## 2018-03-10 08:42:41 -- 突然意识到, 这是一个赌博, 赌这家公司能不能让我成长, 加班有没有很严重, 如果很严重的话就完了, 一是没有时间写毕设 二是身体吃不消 - -## 2018-03-08 19:15:27 -- 广州的地铁 体育西路的环形地下通道有点晕, 然后就是客村, 地铁的出口和入口都是商业街, 地标都没有商业标识明显了, 着实绕了好大一个圈 - -## 2018-02-25 02:02:47 -- 这么晚是因为一点才到广州, 稍微整理下就很晚了, -- POI的封装和下一步的流程引擎的学习使用, 只要找到兴趣的攻克点就有成就感, 才能继续学习下去 - -## 2018-02-17 21:25:52 -- 恍惚间,又过去这么久了, 没有好好规划自己, 今天以这个面貌去见了阿姨, 惨不忍睹.. - - 规划, 实施, 总结, 提高 - -## 2018-02-09 11:09:26 -- 突然意识到代码的优化是一场旷日持久的攻坚战, 是时刻在做的,而不是一开始就完全的做好了,后面就高枕无忧了,如果在一开始是随性的进行开发 - - 当然基础的一些规范还是一定要遵守的, 然后添加功能,添加代码再进行不断的抽象,封装等优化, 这样又保证了代码质量,也让开发初期没有那么繁杂 - - 但是也有一个观点就是:在开发初期进行大致的规划和抽象, 后期开发更为清晰简洁, 重构难度要小一些, 这个还是很有道理的 - - 然而由于目前开发经验不够,只能先实现前面的, 这个预先规划要慢慢感悟和学习了 - -## 2018-02-03 23:33:33 -- 不要再沉迷于无用的方向了! - -## 2018-02-02 18:06:22 -- 今天查到了考试成绩, 虽然是意料之中, 但是还是有点意料之外, 后悔也没有用了 - -## 2018-01-24 16:45:05 -- 机会总是稍纵即逝的, 已经不是校招的时候了, 好的公司都没了, 网上有人看到你的博客什么的,想内推都没有机会了 -- 今天发现了安卓手机上几乎是可以运行所有Python代码的, 可以做一些实用的脚本,给手机用了 - -## 2018-01-21 20:57:24 -- 问题是一直有的,就看你有没有意识到这是一个问题,连问题都没意识到, 就像这个git中文乱码的问题,出现了大半年了,一直没有当问题,现在就刚好正面的遇到了这个问题 - - 这才去找解决方案, 然后发现很简单, 所以,得到的结论是,有时候缺的就是提问题的能力, 连问题都找不到,怎么解决问题, 怎么做的更好呢 - -## 2018-01-20 21:05:41 -- 今天到购物, 一天又这么晃悠过去了, 离过年不久了, 要抓紧一切时间了!! -> 在这么长的人生里, 我体会到的真理是: 公平实在是个稀缺货,不公平才是常态!所以年轻人不要老是抱怨这个社会, 没用的, 还是老老实实的奋斗吧。 -- 码农翻身 - -## 2018-01-19 19:22:35 -- 今天到干活, 很晚才吃饭, 日子不好过啊, 下午又把 活着 看完了, 真是一个时代和生活的巨大悲剧 - -## 2018-01-16 19:09:33 -- 关于冷的定义: - - `< 0 ` 洗洗睡吧,别碰电脑了 - - `0 - 10` 很冷,手伸出来都冷 - - `10 - 20` 穿厚实的衣服不会冷,但是手还是会有点僵 - - `20 - 30` 薄外套即可 - - 科学的定义: 我们能感受到室温很冷是因为有空气在做热交换, 将体温给带走了, 所以会有这样的一个情况 - - 在大气层的高层地区,温度非常高, 甚至几千摄氏度, 但是人体暴露在那里后,是被冷死而不是热死, 因为空气很稀薄,接近真空(颠覆了认知) - - 思考源于 `网易云 《科学有声音》 汪洁 `的 `宇宙生命自然简史 37 维持地球生命的条件` 节目 - -## 2018-01-14 00:49:02 -- 很怕自己猝死,很怕自己找不到工作,完不成毕设,我都怕!!! - -## 2018-01-11 17:30:46 -- 自以为是的人总觉得自己什么都会都懂,有的人就知道要学的东西好多,学无止境. 真是可怜,都可怜 - -## 2018-01-09 17:26:05 -- 最后一天了,能够在图书馆安逸的看书码代码,真的是很后悔这两年没有好好利用图书馆的资源,现在想要看本书都要去一本本的买了 -- 总结: 书海和实践中寻求变强的途径. - -## 2018-01-07 13:03:26 -- 很多培训机构仅仅教授某工具的用法,却不去探索一种模式,教育学生“如何寻找解决方案” - -## 2018-01-06 00:48:33 -- 伴随着这么多次的搭环境,真的比较熟练了,关键的一点是思考的方式变了,怎么简单怎么来.不行了再上大家伙. -- 时间过得很快了 希望大家都好好的 - -## 2018-01-04 15:19:54 -- 突然意识到文档以及图都是开发期的表,所以可以现在就开始画,没必要做完了再象征性补!!! - -## 2018-01-03 22:44:48 -- 尽量在明天将所有线程相关的东西看完,然后着手毕设。时间真的不等人! - -## 2018-01-02 23:02:01 -``` -不管任何学习语言,更需要注意的是提高自己的内力和素质, 举个简单的例子: -面向对象的设计, 好多人自以为掌握了,但其实没有, 因为大部分人都是所谓的html填空人员, 不具备自己设计一个类库或者框架让别人使用。 -要彻底的理解”面对接口编程,而不是面向实现编程“ , "优先使用组合而不是继承" 的含义,并且能在项目中实践。 -要能写出简洁,易懂的代码 做一个clean code 程序员, 做到这一点就能超过绝大部分人, 很多人的代码都是惨不忍睹的。 但是做到这一点很难, 你得有良好的抽象能力, 优秀的编程能力, 以及对语言的熟悉程度。 - -所以我觉得这些才是我们码农应该修炼的内功, 语言只是兵器而已, 你掌握了编程的精髓, 换个语言难吗? - -不要浪费时间尝试不同的语言 , 先把一个常用的,成熟的,流行的搞精通, 同时把自己的内力提上去, 你会发现看别的语言就像黑客帝国的Neo 一样, 把整个系统都看透了, 语言只是一层浮云而已, 到时候你只有有时间,每年都可以学好几门计算机语言。 -``` -``` -我们有的时间就那么多,只能够投入到有限的事情上面,我们不可能同时驾驭所有的语言,如果你觉得你能很好的驾驭go,那么就换,如果不确定那么就继续scala,否则换了也一样,因为编程的基本思想就那些和语言没有关系,而语言都是写得多了就自然就熟练。 -别人觉得好的未必对你也同样是好的,比如从我的角度来讲会建议你换Python试试,但这完全没必要。 -``` - -## 2018-01-01 23:41:14 -- 将Groovy和Springboot结合使用,看起来代码将为更清爽 - - 难道说所有和Groovy有关系的都要有内存占用过大的问题,还是说有内存泄露 Gradle的内存就是居高不下!! diff --git a/Python/Python.md b/Python/Python.md index 4a859a1..f8b50f0 100644 --- a/Python/Python.md +++ b/Python/Python.md @@ -1,77 +1,73 @@ -`目录 start` - -- [Python](#python) - - [简介](#简介) - - [关于Python2.x与3.x的使用](#关于python2x与3x的使用) - - [安装配置](#安装配置) - - [Docker](#docker) - - [基础](#基础) - - [代码风格](#代码风格) - - [基础语法](#基础语法) - - [基础数据类型](#基础数据类型) - - [virtualenv](#virtualenv) - - [pip](#pip) - - [Requirements files](#requirements-files) - - [发布包到 pypi](#发布包到-pypi) - - [变量](#变量) - - [基本运行结构](#基本运行结构) - - [序列](#序列) - - [列表](#列表) - - [元组](#元组) - - [字符串](#字符串) - - [字符串编码问题(python2问题)](#字符串编码问题python2问题) - - [字典(键值对)](#字典(键值对)) - - [运算符](#运算符) - - [模块](#模块) - - [输入输出](#输入输出) - - [输入](#输入) - - [输出](#输出) - - [读取命令行参数](#读取命令行参数) - - [docopt](#docopt) - - [Python Fire](#python-fire) - - [函数](#函数) - - [类](#类) - - [继承](#继承) - - [异常](#异常) - - [文件操作](#文件操作) - - [JSON](#json) - - [conf或者ini](#conf或者ini) - - [测试](#测试) - - [数据库](#数据库) - - [MySQL](#mysql) - - [Redis](#redis) - - [部署](#部署) - - [Docker部署](#docker部署) - - [绘图](#绘图) - - [matplotlib](#matplotlib) - - [常见函数](#常见函数) - - [常见库](#常见库) - - [内置库](#内置库) - - [时间处理](#时间处理) - - [三方库](#三方库) - - [QT](#qt) - -`目录 end` |_2018-09-12_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Python 基础 +date: 2018-12-13 16:00:45 +tags: + - 基础 +categories: + - Python +--- + +💠 + +- 1. [Python](#python) +- 2. [基础](#基础) + - 2.1. [代码风格](#代码风格) + - 2.2. [序列](#序列) + - 2.2.1. [列表 list](#列表-list) + - 2.2.2. [元组 tuple](#元组-tuple) + - 2.2.3. [字符串 str](#字符串-str) + - 2.2.3.1. [字符串编码问题](#字符串编码问题) + - 2.2.4. [字典 dict](#字典-dict) + - 2.3. [运算符](#运算符) + - 2.4. [函数](#函数) + - 2.5. [包](#包) + - 2.6. [类](#类) + - 2.6.1. [继承](#继承) + - 2.7. [异常](#异常) + - 2.8. [读取命令行参数](#读取命令行参数) + - 2.8.1. [docopt](#docopt) + - 2.8.2. [Python Fire](#python-fire) +- 3. [应用](#应用) + - 3.1. [模块](#模块) + - 3.1.1. [http](#http) + - 3.1.2. [virtualenv](#virtualenv) + - 3.1.3. [pip](#pip) + - 3.1.3.1. [Requirements files](#requirements-files) + - 3.1.3.2. [发布包到 pypi](#发布包到-pypi) + - 3.1.4. [matplotlib](#matplotlib) + - 3.2. [文件操作](#文件操作) + - 3.2.1. [JSON](#json) + - 3.2.2. [conf或者ini](#conf或者ini) + - 3.3. [日志](#日志) + - 3.4. [测试](#测试) + - 3.5. [数据库](#数据库) + - 3.5.1. [MySQL](#mysql) + - 3.5.2. [Redis](#redis) + - 3.6. [部署](#部署) + - 3.6.1. [Docker部署](#docker部署) + - 3.7. [常见库](#常见库) + - 3.7.1. [内置库](#内置库) + - 3.7.2. [时间处理](#时间处理) + - 3.7.3. [三方库](#三方库) + +💠 2024-10-13 17:59:27 **************************************** # Python -> [官网](https://www.python.org/) +> [Official Site](https://www.python.org/) +> [Doc: Python2](https://docs.python.org/2/) | [Doc: Python3](https://docs.python.org/3/) > [Python初学者(零基础学习Python、Python入门)书籍、视频、资料、社区推荐](https://github.com/Yixiaohan/codeparkshare) -> [参考博客: Python3的主要应用](http://www.techug.com/post/what-can-you-do-with-python-the-3-main-applications.html) +> [参考: Python3的主要应用](http://www.techug.com/post/what-can-you-do-with-python-the-3-main-applications.html) - [Python中的多态](http://blog.csdn.net/shangzhihaohao/article/details/7065675) -- [python输出带颜色的字体](http://www.cnblogs.com/oleli/p/5228880.html) - > [Anaconda](https://docs.anaconda.com/anaconda/install/linux)`一站式集成环境` -- [Python2](https://docs.python.org/2/) | [Python3](https://docs.python.org/3/) - [python-gtk3](https://python-gtk-3-tutorial.readthedocs.io/en/latest/introduction.html) `python-gtk3的开发` -- [httpie](http://python.ctolib.com/httpie.html) `好用的类curl工具 文档` - [一译](http://python.usyiyi.cn/)`翻译了大量Python文档` -## 简介 +> [Python 项目工程化开发指南](https://github.com/pyloong/pythonic-project-guidelines) -### 关于Python2.x与3.x的使用 -> 摘自 Python核心编程 第三版 Wesley Chun著 +关于Python2与3的变化 +> 摘自 Python核心编程 第三版 Wesley Chun著 - print 变为 print() - 默认字符的编码是 Unicode @@ -80,21 +76,61 @@ - 更新了整数 - 迭代无处不在 -> 列出所有已安装模块 pydoc pydoc3 +> 列出所有已安装模块: pydoc pydoc3 + +# 基础 +> 运行脚本 +1. python 源文件 +1. 源文件 第一行声明 `#!/usr/bin/python` 和shell脚本一样的用法, 然后 ./源文件 + +- 缩进来表示代码块的嵌套关系 +- 单行注释:`#` 多行注释: `""" """` +- 空行的重要性,代码段之间有空行,Python之禅 + +> 基础数据类型 +- 数值类型 + - 整型 (`-2^31 ~ 2^31-1`) `0b`,`0`,`0x` 2,8,16 进制 + - 浮点型 `1.2e2` `13.34e-2` + - 复数 `3+4j` `0.1-0.5j` + +- 布尔型 + - 0 或 0.0 视作 False + - `""` `''` 视作 False + - () [] {} 视作 False + +- 字符串 + - 单引号 双引号: 单行字符串 + - 三引号 多行字符串 + +- 空值 None -## 安装配置 +- 局部变量: + - `_` 标识变量隐藏 +- 全局变量: 定义在函数外的变量,也称公用变量,函数中 `global x` 声明引用全局变量x -**Debian系安装3.6** -1. sudo add-apt-repository ppa:jonathonf/python-3.6 -1. sudo apt update -1. sudo apt install python3.6 +- 逻辑运算符 + - and or not +- 选择: + - if elif else +- for 循环: + - `for in ` `while ` 例如:`for i in range(1,10,2):` 范围 [1,10) 增量为2 + - pass 语句,当某个子句没有任何操作,,用pass保持程序结构完整性 不影响下一句 不像continue +- while 循环 + - `while True:`` while ‘2’ in nums:`` while num<2:` + - `while 列表: ` 直到列表为空退出循环 -### Docker -> [docker hub](https://hub.docker.com/_/python/) +```python + if (b==0) and (a==1) : + pass + print("pass") + else: + print("Hi") +``` +1. 优化代码 `python -O -m py_compile test.py` +## 代码风格 +> [PEP8](https://www.python.org/dev/peps/pep-0008/) `官方建议` -## 基础 -### 代码风格 - 一行只写一句 - 表达式尽量不要省略括号,有助于理解 - 函数的行数不要超过100行 @@ -130,105 +166,24 @@ 命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召) ``` -### 基础语法 -- 缩进来表示代码块的嵌套关系 -- 单行注释:`#` 多行注释: `""" """` -- 空行的重要性,代码段之间有空行,Python之禅 - -### 基础数据类型 -- 数值类型 - - 整数 各种进制 `0 八` `0x 十六` `0b 二` - - 浮点数 `1.2e2` `13.34e-2` - - 复数 `3+4j` `0.1-0.5j` -- 布尔型 - - 0或0.0 :看成false - - `""` `''` :false - - () [] {} :false -- 字符串 - - 单引号 双引号: 单行字符串 - - 三引号 多行字符串 -- 空值 None - -### virtualenv -> [廖雪峰 virtualenv](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432712108300322c61f256c74803b43bfd65c6f8d0d0000) - -`sudo apt install python3-venv` - -- 创建环境 `python3 -m venv web` 或者 `virtualenv --no-site-packages web` 不将系统中安装的包带入该环境 -- 启动环境 `source web/bin/activate` - - 在环境中使用的pip python 都是环境中的, 其实就是修改了系统的环境变量指向 -- 停用环境 `deactivate` - -### pip -> [pip](https://pip.readthedocs.io/en/stable/) | Python的包管理器 - -> [doc](https://pip.pypa.io/en/stable/reference/pip_install/) -#### Requirements files -> [pip官方文档 Requirements files](https://pip.readthedocs.io/en/1.1/requirements.html) - -1. 导出 `pip freeze > requirements.txt` _这个命令会将当前环境安装的包全部列出来, 适合env环境下使用_ - - 如果没有使用虚拟环境, 然后只想导出某项目的依赖 [Github pipreqs](https://github.com/bndr/pipreqs) - - 安装 : `pip install pipreqs` 然后 `pipreqs /path/to/project` -2. 使用 `pip install -r requirements.txt` - -#### 发布包到 pypi -> [Official : about package](https://packaging.python.org/guides/distributing-packages-using-setuptools/?highlight=pypirc#id78) - -`$HOME/.pypirc` -``` -[pypi] -username = -password = -``` -pip3 install wheel twine +## 序列 +> 序列通用操作(包含:字符串,列表,元组) -rm -rf dist build *.egg-info - -python3 setup.py bdist_wheel -twine upload dist/* - -******************************* -## 变量 -- 局部变量: -- 全局变量:定义在函数外的变量,也称公用变量,函数中 `global x` 声明引用全局变量x -## 基本运行结构 -- 输入输出: - - input("") 默认当成字符串输入 数值:int() 强转一下 - - print("") - - 输出不带换行 print('', '', end='') -- 选择: - - if elif else -- for循环: - - `for in ` `while ` 例如:`for i in range(1,10,2):` 范围 [1,10) 增量为2 - - pass 语句,当某个子句没有任何操作,,用pass保持程序结构完整性 不影响下一句 不像continue -- while 循环; - - `while True:`` while ‘2’ in nums:`` while num<2:` - - `while 列表: ` 直到列表为空退出循环 - -```python - if (b==0) and (a==1) : - pass - print("pass") - else: - print("Hi") -``` - -### 序列 -`序列通用操作(包含:字符串,列表,元组)` -- `​索引`,从左至右:`0,1,2...n` 从右至左:`-1,-2...-n` +- `索引` 从左至右:`0,1,2...n` 从右至左:`-1,-2...-n` - `切片`(截取序列的部分) `temp[:]` 返回一个副本 - - `temp[2:4]`就是`[2,4)` - - `temp[1:]`1到最后 `temp[-3:]` *[-3,-1]* - - `temp[:4]` *[0,4)* `temp[:-3]` *[0,-3]* + - `temp[2:4]` 数学中的 `[2,4)` + - `temp[1:]` 1到最后 `temp[-3:]` *[-3,-1]* + - `temp[:4]` [0,4) `temp[:-3]` *[0,-3]* - `加 `:lista+listb 直接连接 - `乘`:lista*4 - `判断是否存在`:`in` `not int` -- len() +- len() - min() max() sum() 要求元素全是数值 -***** -#### 列表 +************************ + +### 列表 list - 元素可包含 字符串,浮点,整型,列表,布尔 - 操作: - 增加 + ,`append()/extend()`尾部加入元素/列表 `insert(index, "")` 元素插入到任意位置,其后元素后移 @@ -244,20 +199,25 @@ twine upload dist/* - 二维数组的定义: - 原始: lists = [[1, 2], [3, 4]] - 仿造一维的定义: lists = [[0 for x in range(10)] for y in range(10)] 10*10 初始为0的列表 - - 简便但是不可行的方法: lists = [[0]*10]*10 这是个坑, 只是声明了一维数组,然后多次引用, 虽然看起来是二维, 引用数据就会发现是一维 + - 简便但是不可行的方法: `lists = [[0]*10]*10` 这是个坑, 只是声明了一维数组,然后多次引用, 虽然看起来是二维, 引用数据就会发现是一维 + +> set() 函数, 返回结果则是不重复的元素集合 + +************************ -#### 元组 +### 元组 tuple - 元组和列表类似但是元组是创建不可更改的 - 和列表相比,相同点:按定义的顺序排序,负索引一致,可以使用分片 - 不同点:元组使用的是(),不能增加删除元素,没有index方法但是有in,可以在字典中作为键,列表不可以 - 由于具有写保护,代码安全,操作速度略快列表 - 操作: - - 访问: 和列表一样的索引和分片, + - 访问:和列表一样, 使用索引和分片 - 连接:+ 连接得到新的元组 - 删除:del 删除整个元组 -***** -#### 字符串 +************************ + +### 字符串 str - str() 将对象转化成字符串 (注:Python中不能像Java一样字符串和数值直接+) - repr() 注意和str()的区别 - `r"d:\python27\"` r前缀表示转义字符看成普通字符 @@ -280,16 +240,15 @@ twine upload dist/* - 字典方式 - `title()` 首字母大写 -**** +************************ + `字符串,列表,元组相互转换:` - 字符串-列表 : list("python") - 字符串-元组 : tuple("python") - 列表或元组-字符串 join(obj) 参数是列表或元组类型,其元素只能是字符串类型 - -*************************************** -#### 字符串编码问题(python2问题) -> [ Python 3的bytes/str之别 ](http://www.ituring.com.cn/article/1116) +#### 字符串编码问题 +> [ Python3 的 bytes str 之别 ](http://www.ituring.com.cn/article/1116) ![str和bytes的关系](https://raw.githubusercontent.com/Kuangcp/ImageRepos/masters/Tech/python/str_bytes.jpeg) @@ -308,19 +267,21 @@ twine upload dist/* ``` - 因为文件不是UTF8:`UnicodeDecodeError: 'utf-8' codec can't decode byte 0xb9 in position 2: invalid start byte ` -************************************* -#### 字典(键值对) +************************ + +### 字典 dict - 通过用空间来换取时间,与列表相比,键的增加不影响查找插入速度,需要占用大量内存 - 特性: - 值是可以是任意的,甚至是字典嵌套 - 键必须不可变,只能由 数值,字符串,元组,不能用列表 - 操作: - - 定义字典 dict={} - - 添加 : `dict['a'] = 2323 ` - - 返回所有key:keys() - - 查询是否有这个键 :has_key() - - get() keys() values() - - 转化元组并返回 :items() + - 定义 dict={} + - 添加 `dict['count'] = 1` + - 获取 `count = dict['count]` 但是如果字典中没有该key, 会抛出异常 + - 获取 `get(key)` 获取不到返回 None + - 返回所有key/value `keys() values()` + - 转化元组 :items() + - 可用于遍历 `for key,value in dict.items():` - 删除指定键:del() `del dict['name']` - 删除所有:clear() - 删除指定键并返回值 :pop() @@ -332,9 +293,7 @@ twine upload dist/* - 列表套字典,当成普通类型包含即可 - 字典套字典 -- [ ] 字典的排序问题 - -### 运算符 +## 运算符 - 算术运算符 - 加减一样,`*` 乘,不仅可以用于数字,还可以用于字符串 ,`/` 除,和Java不一样,整数相除也会得到浮点数 - `//` 取整除,得到商的整数部分 ,`%` 取余数 ,`**` 幂运算 可以用来开根 @@ -360,96 +319,23 @@ twine upload dist/* - \f 换页 - \ 续行符(行尾) - -### 模块 -> [参考博客](http://blog.csdn.net/hansel/article/details/8975663) - -- 关于同级,子级目录是比较方便的,涉及到上级目录的就麻烦点了 - -************ -## 输入输出 -### 输入 -### 输出 -- python3 `print('Hi')` - - 格式化输出 `print("%10s - %-10s"%(name, addr))` - - print会默认追加换行符, 取消需要参数 `end=''` -```python -def show_help(): - start='\033[0;32m' - end='\033[0m' - print("%-26s %-20s"%(start+"-h"+end, "帮助")) -``` - -- python2 `print 'Hi'` - -### 读取命令行参数 -> [参考博客](http://www.sharejs.com/codes/python/6121) - -`只有输入参数,没有选项` -```python - import sys - print("脚本名:", sys.argv[0]) - for i in range(1, len(sys.argv)): - print("参数", i, sys.argv[i]) -``` -`python tree.py hi op ` 顺序是python,第一个参数是文件,之后才是别的参数 - 结果>> `脚本名 tree.py 参数1 hi 参数2 op` - -`有选项` -`getopt.getopt(args, options[, long_options])` -```python - import sys, getopt - opts, args = getopt.getopt(sys.argv[1:], "hi:o:") - for op, value in opts: - -``` -- `sys.argv[1:]`为要处理的参数列表,`sys.argv[0]`为脚本名,所以用`sys.argv[1:]`过滤掉脚本名。 -- `"hi:o:"`: 当一个选项只是表示开关状态时,即后面不带附加参数时,在分析串中写入选项字符。当选项后面是带一个附加参数时,在分析串中写入选项字符同时后面加一个":"号。 - - 所以"hi:o:"就表示"h"是一个开关选项(单单的-h);"i:"和"o:"则表示后面应该带一个参数。 -- 调用getopt函数。函数返回两个列表:`opts和args`。opts为分析出的格式信息。args为不属于格式信息的剩余的命令行参数。 - - opts是一个两元组的列表。每个元素为:(选项串,附加参数)。如果没有附加参数则为空串''。 - - getopt函数的第三个参数[, long_options]为可选的长选项参数,上面例子中的都为短选项(如-i -o) -- 长选项格式举例: - - `--version` - - `--file=error.txt` -- 让一个脚本同时支持短选项和长选项 `getopt.getopt(sys.argv[1:], "hi:o:", ["version", "file="]) ` - -#### docopt -> [Github地址](https://github.com/docopt/docopt) | 在脚本头部添加文档来实现读取参数的便捷 -会读取输入返回字典对象,可以很方便的读取输入的参数,但是需要书写大量文档, 适合参数比较多的时候,一眼过去简洁明了 - -#### Python Fire -> [Github地址](https://github.com/google/python-fire)快速的简洁的生成CLI -> 不过要自己书写帮助文档输出,小量参数的话,开发十分的便利 可以和类一起,也可以和方法一起 - -```python -import fire -def main(action=None): - print(action) - if action == '-h': - show_help() - -fire.Fire(main) -// 使用时 py filename.py -h -``` -***************************************** ## 函数 - - 形参赋值传递方式 - 按位置 `就是直接用看起来和Java一样,但不是按类型和位置,只是位置` - - 按指定名称 调用的时候 `create(name='df')` - - 缺省默认值(参数缺省之后,调用可以不传这个参数,否则必须要传) `def create(name='df')` + - 按指定名称 调用的时候 `create(name='hi')` + - 缺省默认值(参数缺省之后,调用可以不传这个参数,否则必须要传) 声明: `def create(name='hi')` - 列表类型,不想形参改变实参 传递副本过去即可 `list[:]` -- `以下两种情况(* 和 **),都必须放在形参列表的最后 (两者同时使用时:* 只能在 ** 前 )` - - 多个实参 `create(age, *name)` `create(12, 's','d')` - - 所以这是名为name的`元组` 不能指定没有的名称 错误:create(12,d=2, 2,3,4) - - 多个指定名称实参 `create(age, **name)` `create(12, name='d', lo=23)` - - 必须要指定名称 这是名为name的键值对`字典` - - 错误:create(12,d=23,3,3,3) - - 注意: - - `def hi(name, age=0, *names, **s)` `hi('d', 23,34, d=6) ` age会被赋值23 - - `def hi(name, *names, age=0, **s)` `hi('d', 23,34, d=6)` 这样写age就不会赋值,除非指定名称 age=23 + +- 多个实参 `create(age, *name)` `create(12, 's','d')` + - 所以这是名为name的`元组` 不能指定没有的名称 错误:create(12,d=2, 2,3,4) +- 多个指定名称实参 `create(age, **name)` `create(12, name='d', lo=23)` + - 必须要指定名称 这是名为name的键值对`字典` + - 错误:create(12,d=23,3,3,3) +- 注意: + - `def hi(name, age=0, *names, **s)` `hi('d', 23,34, d=6) ` age会被赋值23 + - `def hi(name, *names, age=0, **s)` `hi('d', 23,34, d=6)` 这样写age就不会赋值,除非指定名称 age=23 + - `以上两种情况(* 和 **),都必须放在形参列表的最后 (两者同时使用时:* 只能在 ** 前 )` - 返回值 - 返不返回 看需求 没有像Java一样的强制性约束类型 @@ -459,10 +345,24 @@ fire.Fire(main) - 导入指定的函数 `from create import create_aliens, type_button` 多个就,分隔 同理 as给函数加别名 * 通配所有 - 注意:递归深度,Python中递归默认深度是 989, 要么更改实现,要么就 `sys.setrecursionlimit(10000000)` +> [参考: Magic Method](https://segmentfault.com/a/1190000007256392) `__xxx__ 方法` + +> 内置函数 + +- `id()` 查看内存地址 +- `help(方法名)` 展示方法的说明文档 +- `dir(对象)` 展示对象的方法API + +************************ +## 包 +> [official tutorial](https://docs.python.org/3/tutorial/modules.html#packages) + +When importing the package, Python searches through the directories on `sys.path` looking for the package subdirectory. + +> 1. 注意不能出现 import 循环依赖 `A.py import B.py` then `B.py import A.py` -******************* ## 类 -`Python 不存在多态,存在鸭子类型` [博客介绍](http://blog.csdn.net/shangzhihaohao/article/details/7065675) +`Python 不存在多态,存在鸭子类型` [博客介绍](http://blog.csdn.net/shangzhihaohao/article/details/7065675) | [python中的多态与鸭子类型](https://www.jianshu.com/p/650485b78d11) - 写在一个py文件里,默认构造器,可以加参数 `def __init__(self):` - 属性: @@ -508,7 +408,7 @@ fire.Fire(main) - 导入和函数一样 注意继承中类的依赖 -#### 继承 +### 继承 - Python是支持多重继承的 - 同文件 父类定义要在子类之前 @@ -523,7 +423,8 @@ fire.Fire(main) - 方法重载: 子类覆盖父类的方法 - 运算符重载: 加`__add__(self, x)` 减`__sub__(self, x)` -****************************************** +************************ + ## 异常 ```python try: @@ -535,18 +436,20 @@ fire.Fire(main) finally: print('finally') ``` + - 基本语法 `try except else finally` - - else是无异常执行 - - 有异常就执行 except, except 超类Exception,也可以多个except (和Java一致) - - 最终执行finally 和 Java的结构是一致的 - -| except 分句使用形式 | 说明 | -| :--- | :---| -|except | 捕获所有类型| -|except name| 只捕获指定类型| -|except name, value|捕获指定类型,并获得抛出的异常对象| -|except (name1, name2)|捕获列出的异常| -|except (name1, name2), value |捕获列出的异常,获得抛出的异常对象| + - else 是无异常时执行 + - 有异常就会执行 except, 可以多个except (和Java一致) + - `except Exception as e:` 捕获所有异常 + - 最终执行 finally 和 Java的结构是一致的 + + | except 分句使用形式 | 说明 | + | :--- | :--- | + | except | 捕获所有类型 | + | except name | 只捕获指定类型 | + | except name, value | 捕获指定类型,并获得抛出的异常对象| + | except (name1, name2) | 捕获列出的异常| + | except (name1, name2), value | 捕获列出的异常,获得抛出的异常对象| - raise 语句 和Java的throw关键字 一致 , 不过raise只是抛出一个通用异常类型 Exception - dir(exceptions) 查看所有异常类型 @@ -558,28 +461,158 @@ fire.Fire(main) | 常见异常类 | 描述 | |:---|:---| -|NameError/UnboundLocalError | 引用不存在的变量/或者引用在声明之前| -|ZeroDivisionError|除数为0| -|SyntaxError|语法错误| -|IndexError|索引错误| -|KeyError|使用不存在的字典关键字| -|IOError|输入输出错误| -|ValueError|搜索列表中不存在的值| -|AtrributeError|调用不存在的方法| -|TypeError|未强制转换就混用数据类型| -|EOPError|文件结束标识错误| - -***************** +| NameError/UnboundLocalError | 引用不存在的变量/或者引用在声明之前 | +| ZeroDivisionError | 除数为0 | +| SyntaxError | 语法错误 | +| IndexError | 索引错误 | +| KeyError | 使用不存在的字典关键字 | +| IOError | 输入输出错误 | +| ValueError | 搜索列表中不存在的值 | +| AtrributeError | 调用不存在的方法 | +| TypeError | 未强制转换就混用数据类型 | +| EOPError | 文件结束标识错误 | + +************************ + +## 读取命令行参数 +> [参考博客](http://www.sharejs.com/codes/python/6121) + +`只有输入参数,没有选项` +```python + import sys + print("脚本名:", sys.argv[0]) + for i in range(1, len(sys.argv)): + print("参数", i, sys.argv[i]) +``` +`python tree.py hi op ` 顺序是python,第一个参数是文件,之后才是别的参数 + 结果>> `脚本名 tree.py 参数1 hi 参数2 op` + +`有选项` +`getopt.getopt(args, options[, long_options])` +```python + import sys, getopt + opts, args = getopt.getopt(sys.argv[1:], "hi:o:") + for op, value in opts: + +``` +- `sys.argv[1:]`为要处理的参数列表,`sys.argv[0]`为脚本名,所以用`sys.argv[1:]`过滤掉脚本名。 +- `"hi:o:"`: 当一个选项只是表示开关状态时,即后面不带附加参数时,在分析串中写入选项字符。当选项后面是带一个附加参数时,在分析串中写入选项字符同时后面加一个":"号。 + - 所以"hi:o:"就表示"h"是一个开关选项(单单的-h);"i:"和"o:"则表示后面应该带一个参数。 +- 调用getopt函数。函数返回两个列表:`opts和args`。opts为分析出的格式信息。args为不属于格式信息的剩余的命令行参数。 + - opts是一个两元组的列表。每个元素为:(选项串,附加参数)。如果没有附加参数则为空串''。 + - getopt函数的第三个参数[, long_options]为可选的长选项参数,上面例子中的都为短选项(如-i -o) +- 长选项格式举例: + - `--version` + - `--file=error.txt` +- 让一个脚本同时支持短选项和长选项 `getopt.getopt(sys.argv[1:], "hi:o:", ["version", "file="]) ` + +### docopt +> [Github地址](https://github.com/docopt/docopt) | 在脚本头部添加文档来实现读取参数的便捷 +会读取输入返回字典对象,可以很方便的读取输入的参数,但是需要书写大量文档, 适合参数比较多的时候,一眼过去简洁明了 + +### Python Fire +> [Github地址](https://github.com/google/python-fire)快速的简洁的生成CLI +> 不过要自己书写帮助文档输出,小量参数的话,开发十分的便利 可以和类一起,也可以和方法一起 + +```python + import fire + def main(action=None): + print(action) + if action == '-h': + show_help() + + fire.Fire(main) + # 使用时 py filename.py -h +``` + +****************** +# 应用 +## 模块 +python -m module_name + +### http +- 快速启动一个 HTTP Web 服务器 `http.server [port]` + +### virtualenv +> [廖雪峰 virtualenv](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/001432712108300322c61f256c74803b43bfd65c6f8d0d0000) + +**虽然也可以用apt安装 python-venv, 但是最好不要这样,避免后续模块升级后不必要的冲突** + +- 创建环境 `python3 -m venv web` 或者 `virtualenv --no-site-packages web` 不将系统中安装的包带入该环境 +- 启动环境 `source web/bin/activate` + - 在环境中使用的pip python 都是环境中的, 其实就是修改了系统的环境变量指向 +- 停用环境 `deactivate` + +### pip +> [pip](https://pip.readthedocs.io/en/stable/) | [doc](https://pip.pypa.io/en/stable/reference/pip_install/) | [guide](https://packaging.python.org/tutorials/installing-packages/) + +1. 作为Python的包管理器, 包的可执行文件默认在 /usr/local/bin 目录下(全局) + - 如果安装时加了该参数 --user 就是安装在 ~/.local/bin 目录下 + +> install +- `pip install name` 安装最新版本 +- `pip install name==version` 安装指定版本 +- 镜像源 豆瓣 `-i https://pypi.doubanio.com/simple/` 清华 `-i https://pypi.tuna.tsinghua.edu.cn/simple` + - [修改Pip 管理工具默认下载源](https://blog.csdn.net/JQ_AK47/article/details/77944444) +- 代理 `--proxy 192.168.1.24:1234` +- 强行使用HTTP `-i http://pypi.doubanio.com/simple/ --trusted-host pypi.doubanio.com` pip版本高于20.3后默认使用HTTPS +- 安装tar.gz: pip install xxxx.tar.gz + +> 注意:自PEP668开始限制默认安装为全局依赖,否则会报错 externally-managed-environment +- 可以设置默认全局 python3 -m pip config set global.break-system-packages true +- 或者单次安装到全局 --break-system-packages + +> 3.10 后 pip 作为子模块 +- 安装 `python -m ensurepip --upgrade` +- 使用 `python -m pip install pkgName` +- 升级 `python -m pip install --upgrade pip` + +#### Requirements files +> [pip官方文档 Requirements files](https://pip.readthedocs.io/en/1.1/requirements.html) + +1. 导出 `pip freeze > requirements.txt` _这个命令会将当前环境安装的包全部列出来, 适合env环境下使用_ + - 如果没有使用虚拟环境, 然后只想导出某项目的依赖 [Github pipreqs](https://github.com/bndr/pipreqs) + - 安装 : `pip install pipreqs` 然后 `pipreqs /path/to/project` + +1. 使用 `pip install -r requirements.txt` + +#### 发布包到 pypi +> [Official : about package](https://packaging.python.org/guides/distributing-packages-using-setuptools/?highlight=pypirc#id78) + +1. edit `$HOME/.pypirc` to save authorization info + ``` + [pypi] + username = + password = + ``` +1. pip3 install wheel twine +1. rm -rf dist build *.egg-info +1. python3 setup.py bdist_wheel +1. twine upload dist/* + +> [可以参考该项目: 终端内使用百度翻译](https://gitee.com/gin9/baidu-trans-cli) + +由于Readme 使用的是 [reStructuredText](https://rest-sphinx-memo.readthedocs.io/en/latest/ReST.html) 语法(要求严格,所以需要借助工具) +> 1. pip install collective.checkdocs Pygments +> 1. python3 setup.py checkdocs + +### matplotlib + +************************ + + ## 文件操作 + - 注意路径,Windows系统中要使用反斜杠 \ - 最简单:`file = open('')` 只读打开 -- `使用with来操作 好处是Python自动关闭文件` -```python - with open('filename') as name: - name.read() -``` +- `使用with来操作 好处是Python自动关闭文件` 类似于Java的TWR + ```python + with open('filename') as file: + lines = file.readlines() + ``` - 为写打开新文本文件只读 `file = open('a.txt','w+'[,coding='utf-8'])` 打开删空 - `file.write('')` + - `os模块` - `os.rename('filename1','filename2') ` mv - `os.remove('filename.py')` rm @@ -588,7 +621,7 @@ fire.Fire(main) - `os.makedirs(r'path')` mkdir - `os.chdir('')` 改变一个目录 - `os.rmdir('')` 删除该目录,前提是空目录 - + - `os.path模块` - abspath('') 获取绝对路径 - exists('') 是否存在 @@ -601,37 +634,59 @@ fire.Fire(main) - dir() 复制单个文件 - shultil.copytree(r'',r'') 复制目录树 +```python + # 读取大文件 + for line in fileinput.input("test.txt"): + print(line) +``` +************************ + `b 表示字节流(二进制文件) 不加表示字符流(文本文件)` -|方式 |意义 |当存在 |当不存在 | -|:-----:|:-----:|:------:|:-----:| +|字符流方式 |意义 |当存在 |当不存在 | +|:-----|:-----|:------|:-----| |r |只读打开 |打开 |返回空指针 | -|w |只写打开新 |打开删空 |新建打开 | +|w |只写打开 |打开删空 |新建打开 | |a |追加打开 |打开 |新建打开 | |r+ |读打开可写 |打开 |返回空指针 | -|w+ |写打开新可读 |打开删空 |新建打开 | +|w+ |写打开可读 |打开删空 |新建打开 | |a+ |追加打开可读 |打开 |新建打开 | + +**************** + +|字节流方式 |意义 |当存在 |当不存在 | +|:-----|:-----|:------|:-----| |rb |只读打开 |打开 |返回空指针 | -|wb |只写打开新 |打开删空 |新建打开 | +|wb |只写打开 |打开删空 |新建打开 | |ab |追加打开 |打开 |新建打开 | |rb+ |读打开可写 |打开 |返回空指针 | -|wb+ |写打开新可读 |打开删空 |新建打开 | +|wb+ |写打开可读 |打开删空 |新建打开 | |ab+ |追加打开可读 |打开 |新建打开 | +************************ + ### JSON ```python - alien = {'color': 'green', 'age': '23'} - files = 'a.json' - with open(files, 'w') as o: - json.dump(alien, o) - data = json.load(files) - # 引用 - data['root']['name'] + import json + file_name='result.json' + + def write_json(): + global file_name + person = {'color': 'green', 'age': '23'} + with open(file_name, 'w') as o: + json.dump(person, o) + + def read_json(): + global file_name + with open(file_name) as file: + datas = json.load(file) + for data in datas : + # 引用的时候就当做是字典 + print(data, datas[data]) ``` -- json.dump()持久化 和 load() 装载 ### conf或者ini -> [参考博客: python操作ini文件](https://www.oschina.net/code/snippet_782578_14344) +> [参考: python操作ini文件](https://www.oschina.net/code/snippet_782578_14344) ```python import os @@ -646,27 +701,39 @@ fire.Fire(main) # 写 但是要有write节点 cf.set('write', 'add', '12') cf.write(open(mainConf, 'r+')) - ``` - _对应的conf_ - ```conf - [redis] - host=127.0.0.1 ``` -****************************** + +_对应的conf_ +```conf + [redis] + host=127.0.0.1 +``` + +************************ +## 日志 +loguru + +> [Effective Logging in Threaded or Multiprocessing Python Applications ](https://www.loggly.com/blog/effective-logging-in-threaded-or-multiprocessing-python-applications/) + +但是 FastApi 里的 BackgroundTasks 是跨线程的,但是同样支持log, 需要找找怎么实现的 +- 实际上是因为他是协程,不是线程 + +************************ + ## 测试 -- 文件名test开头就当做是测试类,不会直接运行 + +- 文件名test_开头, 需要 unittest.main() 方式运行 - 类继承 unittest.TestCase, 所有test_开头的方法都将自动运行 - 断言 self.assertEqual assertNotEquals assertIn(item, list) -- 直接运行 unittest.main() -- 输出结果,`. 测试通过` `E 测试运行错误` `F 测试断言不通过` +- 输出结果: `. 测试通过` `E 测试运行错误` `F 测试断言不通过` + +************************ -************ ## 数据库 ### MySQL -- python3环境下: `sudo apt install python3-mysqldb` -- `sudo apt install libmysqlclient-dev` -- `sudo pip install mysql-python` +> pip install mysqlclient +- import MySQLdb ### Redis _安装模块_ @@ -680,7 +747,7 @@ _使用_ ## 部署 ### Docker部署 > [参考官方文档](https://hub.docker.com/_/python/) -Create a Dockerfile in your Python app project + ```dockerfile FROM python:3 WORKDIR /usr/src/app @@ -689,7 +756,7 @@ Create a Dockerfile in your Python app project COPY . . CMD [ "python", "./your-daemon-or-script.py" ] ``` -_or (if you need to use Python 2)_ + ```dockerfile FROM python:2 WORKDIR /usr/src/app @@ -702,23 +769,6 @@ _or (if you need to use Python 2)_ - $ docker build -t my-python-app . - $ docker run -it --rm --name my-running-app my-python-app -******************** -## 绘图 -### matplotlib -`python 3.5 安装` -```sh - sudo apt install python3-matplotlib - sudo apt install python3.5-dev python3.5-tk tk-dev - sudo apt install libfreetype6-dev g++ -``` - -******** -## 常见函数 - -- `id()` 查看内存地址 -- `help(方法名)` 展示方法的说明文档 -- `dir(对象)` 展示对象的方法API - ****************************** ## 常见库 ### 内置库 @@ -728,27 +778,23 @@ _or (if you need to use Python 2)_ - 获取脚本绝对路径 `os.path.split(os.path.realpath(__file__))[0]` - 获取用户目录 `os.environ['HOME']` | `os.path.expandvars('$HOME')` | `os.path.expanduser('~')` -- `subprocess` [代码](https://gitee.com/kcp1104/codes/9ytejo7fl2xmqsr5zwkv380) +- platform 操作系统信息 + - 获取当前操作系统名称 platform.system() -#### 时间处理 +- `subprocess` [代码](https://gitee.com/gin9/codes/9ytejo7fl2xmqsr5zwkv380) + +### 时间处理 _time_ -> [参考博客: Python 日期和时间](http://www.runoob.com/python/python-date-time.html) +> [菜鸟教程: Python 日期和时间](http://www.runoob.com/python/python-date-time.html) + +1. 格式化当前时间 `time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())` + ### 三方库 +- `qrcode` 终端生成二维码 - `redis` 和Redis命令完美融合 - `httpie` HTTP方便的交互 [doc](https://httpie.org/doc) - POST时 特别注意:`参数==值` - [python-excel](http://www.python-excel.org/) - `python-docx` [文档](https://python-docx.readthedocs.io/en/latest/) - `chef` [文档](https://docs.chef.io/resource_python.html) `Use the python resource to execute scripts using the Python interpreter` - -******************** -## QT -- 在Terminal中输入:sudo apt-get install qt4-dev-tools qt4-doc qt4-qtconfig qt4-demos qt4-designer -``` - qt4-dev-tools 中包括了Qt Assistant,Qt Linguist,Qt Creator - qt4-doc 这个是帮助文档 - qt4-qtconfig Qt的配置工具,这个装好默认好 - qt4-demos 官方的一些Demo - qt4-designer 可视化窗体设置工具 -``` diff --git a/Python/PythonConcurrency.md b/Python/PythonConcurrency.md new file mode 100644 index 0000000..5f756a3 --- /dev/null +++ b/Python/PythonConcurrency.md @@ -0,0 +1,68 @@ +--- +title: Python Concurrency +date: 2018-12-13 16:00:40 +tags: + - Concurrency +categories: + - Python +--- + +💠 + +- 1. [Concurrency](#concurrency) + - 1.1. [GIL](#gil) + - 1.2. [协程 asyncio](#协程-asyncio) + - 1.3. [多线程 threading](#多线程-threading) + - 1.4. [多进程 multiprocessing](#多进程-multiprocessing) +- 2. [实践](#实践) + - 2.1. [Ray](#ray) + +💠 2024-10-10 10:41:00 +**************************************** +# Concurrency + +Python中的并发编程可大致分为: 协程,多线程,多进程 + +协程Coroutine(asyncio) +- 优点: 任务单元和内核线程数是多对多关系,任务调度是用户态级别,线程切换开销小 +- 缺点: 相比于同步式需要换成异步的写法,现有的库支持不完善 +- 适用场景: 海量IO密集型任务 + +多线程Thread(threading) +- 优点: 任务单元和实际内核线程绑定,同步代码换成多线程实现时调整小,无须依赖的库做调整`只能说相对小,比如多线程里日志问题` +- 缺点: 相比协程有更大的线程切换开销,相比进程更轻量,GIL的存在导致只能实现并发而不是并行 +- 适用场景: 大量IO密集型任务 + +多进程Process(multiprocessing) +- 优点:多核并行计算,同步代码换成多进程时调整中等,无须依赖库调整,但需要全局考虑存在进程内数据共享的场景不支持 +- 缺点:资源占用重,应用数据共享时需要使用到进程间通信 +- 适用场景: CPU密集型任务 + +通常来说 +- IO绑定的场景适用 协程和多线程(存在GIL),例如用户输入,数据库,文件,网络; +- CPU绑定的场景适用多进程,例如 矩阵乘法,搜索,加解密,正则匹配,图像处理 + +## GIL +> [What Is the Python Global Interpreter Lock (GIL)?](https://realpython.com/python-gil/) +> [Python的GIL是什么鬼,多线程性能究竟如何](http://cenalulu.github.io/python/gil-in-python/)`讲解了GIL以及使用其他并发库` +> [Celery](https://docs.celeryq.dev/en/stable/) + +简单来说,GIL在早期将C的库集成入Python时,GIL的存在使得应用开发无须考虑并发安全问题,也就无须考虑锁的开销,单线程的性能也很高。 +而且Python有多种解释器实现,只有CPython中有GIL + +## 协程 asyncio + + +## 多线程 threading +创建并绑定操作系统的内核线程,但是无法像Java,Go那样并行执行,每个线程在执行前都需要获取GIL锁,即至多只有一个线程能使用CPU计算。 + + +思考: 如何实现像Java一样完备的状态管理和异常管理。 如何实现线程池,如何实现生产者消费者队列加多线程。 + +## 多进程 multiprocessing + +************************ + +# 实践 +## Ray +分布式多进程框架 diff --git a/Python/PythonGUI.md b/Python/PythonGUI.md index 2905283..a663da2 100644 --- a/Python/PythonGUI.md +++ b/Python/PythonGUI.md @@ -1,25 +1,36 @@ -`目录 start` - -- [【GUI】](#gui) - - [GTK3](#gtk3) - - [Tkinter](#tkinter) - - [PyQt](#pyqt) - - [kivy](#kivy) - - [wxPython](#wxpython) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: PythonGUI +date: 2018-12-13 16:02:20 +tags: + - GUI +categories: + - Python +--- + +💠 + +- 1. [GUI](#gui) + - 1.1. [GTK3](#gtk3) + - 1.2. [Tkinter](#tkinter) + - 1.3. [PyQt](#pyqt) + - 1.4. [kivy](#kivy) + - 1.5. [wxPython](#wxpython) + - 1.6. [NiceGUI](#nicegui) + +💠 2024-04-22 16:57:46 **************************************** -# 【GUI】 +# GUI ## GTK3 -> [教程](https://python-gtk-3-tutorial.readthedocs.io/en/latest/) | [pygtk](http://www.pygtk.org/) +> [Official Doc](https://python-gtk-3-tutorial.readthedocs.io/en/latest/) | [pygtk](http://www.pygtk.org/) > [pygtk wiki ](https://wiki.python.org/moin/PyGtk) -- 自从忘掉如何安装后, 就不会安装了!!! ## Tkinter -> [官网](https://wiki.python.org/moin/TkInter/) | [官网教程](https://docs.python.org/3.5/library/tkinter.html) +> [Offcial Site](https://wiki.python.org/moin/TkInter/) | [Official Doc](https://docs.python.org/3.5/library/tkinter.html) -> [教程网](https://www.tutorialspoint.com/python/python_gui_programming.htm) +> [Python - GUI Programming (Tkinter)](https://www.tutorialspoint.com/python/python_gui_programming.htm) + +> [Python GUI examples (Tkinter Tutorial)](https://likegeeks.com/python-gui-examples-tkinter-tutorial/) `安装` - python2: `sudo apt install python-tk` @@ -30,6 +41,14 @@ - 但是python3.5的环境下,`import tkinter` 才是正确的 ## PyQt +在Terminal中输入:sudo apt-get install qt4-dev-tools qt4-doc qt4-qtconfig qt4-demos qt4-designer +``` + qt4-dev-tools 中包括了Qt Assistant,Qt Linguist,Qt Creator + qt4-doc 这个是帮助文档 + qt4-qtconfig Qt的配置工具,这个装好默认好 + qt4-demos 官方的一些Demo + qt4-designer 可视化窗体设置工具 +``` ## kivy > [官网](https://kivy.org/#home) @@ -37,4 +56,7 @@ ## wxPython -> 坑多 \ No newline at end of file +> 坑多 + +## NiceGUI +[Official](https://nicegui.io/) \ No newline at end of file diff --git a/Python/PythonGame.md b/Python/PythonGame.md index 7eae25b..fe4b21c 100644 --- a/Python/PythonGame.md +++ b/Python/PythonGame.md @@ -1,9 +1,18 @@ -`目录 start` - -- [pygame](#pygame) - - [安装](#安装) +--- +title: PythonGame +date: 2018-12-13 16:01:52 +tags: + - Game +categories: + - Python +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [pygame](#pygame) + 1. [安装](#安装) + +**目录 end**|_2020-04-27 23:42_| **************************************** # pygame ## 安装 diff --git a/Python/PythonNet.md b/Python/PythonNet.md deleted file mode 100644 index 7f8d701..0000000 --- a/Python/PythonNet.md +++ /dev/null @@ -1,36 +0,0 @@ -`目录 start` - -- [网络编程](#网络编程) - - [Socket](#socket) - - [基于TCP](#基于tcp) - - [基于UDP](#基于udp) - - [邮件](#邮件) - - [Web工具](#web工具) -- [爬虫](#爬虫) - - [安装所需模块](#安装所需模块) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 网络编程 -## Socket -> 作为网络编程的抽象概念,用于描述IP地址和端口,表示打开了一个网络连接,一个socket绑定到一个端口上 -> 基于Socket的编程,需要知道目标计算机的IP地址,端口,以及协议类型 - -### 基于TCP - -### 基于UDP - -## 邮件 - -## Web工具 -- `pip3 install httpie` 我的用不了,奇怪?? 这个`sudo apt install httpie`才能用 - - `http --json URL` 格式化输出json - - URL会转小写。。。 -- `curl URL|python -m json.tool ` 格式化输出JSON - -# 爬虫 -### 安装所需模块 - -`解析HTML` -- bs4 :`sudo pip3 install bs4` -- lxml :`sudo pip3 install lxml` diff --git a/Python/PythonOffices.md b/Python/PythonOffices.md new file mode 100644 index 0000000..16fe3c1 --- /dev/null +++ b/Python/PythonOffices.md @@ -0,0 +1,55 @@ +--- +title: Python 操作 Offices文档 +date: 2018-12-15 12:08:39 +tags: + - Offices +categories: + - Python +--- + +💠 + +- 1. [Offices文档](#offices文档) + - 1.1. [Word](#word) + - 1.2. [Excel](#excel) + +💠 2024-04-23 13:56:29 +**************************************** +# Offices文档 +## Word + +## Excel +> [Working with Excel Files in Python](http://www.python-excel.org/) +> [参考: Python-Excel 模块哪家强?](https://zhuanlan.zhihu.com/p/23998083) + +> 大文件读取性能优化 +- 问题: pandas读取 200M+ Excel时会耗时很久(分钟级),思路将Excel转换为CSV再读取 +[Fast excel python](https://hakibenita.com/fast-excel-python)`calamine性能最快且保留类型` +[polars.read_excel](https://docs.pola.rs/py-polars/html/reference/api/polars.read_excel.html)`读取Excel为DataFrame,同样使用calamine` + +************************ + +> Openpyxl +> DuckDB +> LibreOffice +> Tablib + +************************ + +> [xlrd](https://github.com/python-excel/xlrd) + +```python + import xlrd + + data = xlrd.open_workbook('monster.xlsx') + table = data.sheets()[0] + nrows = table.nrows + for i in range(nrows): + for cell in table.row_values(i): + print(cell, ' | ', end='') + print() +``` + +************************ +> [Pandas](http://pandas.pydata.org/) + diff --git a/Python/PythonWeb.md b/Python/PythonWeb.md index ade5c3f..1291d0e 100644 --- a/Python/PythonWeb.md +++ b/Python/PythonWeb.md @@ -1,29 +1,42 @@ -`目录 start` - -- [Web](#web) - - [简单Web服务器](#简单web服务器) - - [Django](#django) - - [Flask](#flask) - - [开发一个简易RESTful风格的服务器](#开发一个简易restful风格的服务器) - - [跨域](#跨域) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: PythonWeb +date: 2018-12-15 12:08:21 +tags: + - Web +categories: + - Python +--- + +💠 + +- 1. [Web](#web) + - 1.1. [FastAPI](#fastapi) + - 1.2. [Django](#django) + - 1.3. [Flask](#flask) +- 2. [Tool](#tool) +- 3. [爬虫](#爬虫) + +💠 2024-05-22 22:00:47 **************************************** # Web -> [对比五种Web框架](https://www.csdn.net/article/2011-02-17/292058) +> [Awesome Python Web Frameworks](https://github.com/sfermigier/awesome-python-web-frameworks) -## 简单Web服务器 -> python内置一个简易的Web服务器 只需在静态资源的根目录下执行 -- python2 `python -m SimpleHTTPServer [8000]` 即可启动 缺省端口为8000 +> python内置一个简易的W静态eb服务器 只需在静态资源的根目录下执行 +- python2 `python -m SimpleHTTPServer [8000]` - python3 `python3 -m http.server [8000]` +## FastAPI +[Github](https://github.com/tiangolo/fastapi) + +> [FastAPI增加traceId](https://segmentfault.com/a/1190000041438570) +> [FastApi结合loguru日志使用](https://blog.csdn.net/qq_51967017/article/details/134045236) + +[异步任务](https://fastapi.tiangolo.com/zh/tutorial/background-tasks/) + +************************ + ## Django -`python3.5 建立虚拟环境` -- `sudo apt install python3-venv` -- 在某目录 `python3 -m venv first_env` -- 激活环境 `source first_env/bin/activate` - - 停用环境 `deactivate` - 安装Django `pip install Django` - 创建项目 `django-admin.py startproject first_pro . ` - `ls first_pro`查看到创建的默认文件 @@ -31,23 +44,28 @@ - 启动项目`python3 manage.py runserver` ## Flask -### 开发一个简易RESTful风格的服务器 -> [官方文档 ](http://www.pythondoc.com/flask-restful/first.html#python-flask-restful-api) `但是这个内置的web服务器性能很渣` -> [教程文档](https://www.tutorialspoint.com/flask/index.htm) +开发一个简易RESTful风格的服务器 +> [官方文档 ](http://www.pythondoc.com/flask-restful/first.html#python-flask-restful-api) +> [教程文档](https://www.tutorialspoint.com/flask/index.htm) -### 跨域 +> 跨域问题 > [解决方式](https://blog.csdn.net/yannanxiu/article/details/53036508) `pip install flask-cors` + ```python -from flask_cors import * + from flask_cors import * -app = Flask(__name__) -CORS(app, supports_credentials=True) + app = Flask(__name__) + CORS(app, supports_credentials=True) ``` +# Tool +- `pip install httpie` + - `http --json URL` 格式化输出json +- `curl URL|python -m json.tool ` 格式化输出JSON - - - - +# 爬虫 +`解析HTML` +- bs4 :`sudo pip3 install bs4` +- lxml :`sudo pip3 install lxml` diff --git a/Python/Readme.md b/Python/Readme.md new file mode 100644 index 0000000..6a71795 --- /dev/null +++ b/Python/Readme.md @@ -0,0 +1,64 @@ +## 安装配置 + +**Debian系安装3.6** +1. sudo add-apt-repository ppa:jonathonf/python-3.6 +1. sudo apt update +1. sudo apt install python3.6 + +**Centos7安装3.11** + +[在 CentOS 7 / RHEL 7 上安装 Python 3.11](https://blog.csdn.net/zhezhebie/article/details/132499755) +[pip install报错"Can't connect to HTTPS URL because the SSL module is not available"](https://www.cnblogs.com/world-of-yuan/p/17855748.html) + +编译安装 libssl +1. ./config--prefix=/opt/openssl +1. make -j && make install + +编译安装 python +1. make clean +1. /configure --prefix=/opt/python3.11 --with-openssl=/opt/openssl --with-openssl-rpath=auto +1. make -j && make altinstall + +### Docker安装 +> [docker hub](https://hub.docker.com/_/python/) + +### Conda +> [Conda](https://conda.io/projects/conda/en/latest/user-guide/install/index.html)`简单理解为 venv + pip` +> [Miniconda 安装](https://docs.anaconda.com/miniconda/) + +- 安装 conda create -n py39 python=3.9 + - conda create -n py311 python=3.11 +- 激活指定环境 conda activate py39 +- 退出环境 conda deactivate + +### sys.path +> [Doc: Python path](https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPATH) + +- sys.path 是 指定模块的搜索路径的字符串列表。`类似于Java的 ClassPath, Go的 GOPATH, 让解释器知道去哪找包` + - 查看系统的 sys.path 进入交互解释器 + ```python + import sys + print("\n".join(sys.path)) + ``` + +**修改sys.path** +1. 代码中直接添加, 执行就生效, 程序结束就失效 + ```python + # 假如有如下两个文件 在不同的包下 + # /src/configs/config.py + # /src/common/Database.py + + parent_path = os.path.dirname(sys.path[0]) + # 避免重复添加 + if parent_path not in sys.path: + sys.path.append(parent_path) + import configs.config + ``` + +1. 添加 *.pth 文件 + - 在 `/usr/local/lib/` 目录下有多个 Python 版本,配置自己需要的版本 + - 例如在 `python2.7/site-packages` 中添加 test.pth 文件,文件内容为项目的绝对路径 + - python3.x 则是在 `dist-packages` 目录下 + +1. 修改环境变量 + - 修改或添加 环境变量 PYTHONPATH 路径用分号分隔 diff --git a/Python/Tool/PyCharm.md b/Python/Tool/PyCharm.md index 417135a..9348b3b 100644 --- a/Python/Tool/PyCharm.md +++ b/Python/Tool/PyCharm.md @@ -1,11 +1,20 @@ -`目录 start` - -- [PyCharm](#pycharm) +--- +title: PyCharm +date: 2018-12-15 12:08:04 +tags: + - IDEA +categories: + - IDE +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [PyCharm](#pycharm) + +**目录 end**|_2020-06-04 19:41_| **************************************** # PyCharm -> [参考博客: PyCharm安装及使用](https://www.jianshu.com/p/042324342bf4) +> [参考: PyCharm安装及使用](https://www.jianshu.com/p/042324342bf4) diff --git a/Python/Tool/Selenium.md b/Python/Tool/Selenium.md new file mode 100644 index 0000000..494edfd --- /dev/null +++ b/Python/Tool/Selenium.md @@ -0,0 +1,18 @@ +--- +title: Selenium +date: 2019-11-10 01:49:33 +tags: +categories: + - Python +--- + +**目录 start** + +1. [Selenium](#selenium) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Selenium + +> [参考: Selenium Python](http://www.testclass.net/selenium_python) + diff --git a/Reactive/ProjectReactor.md b/Reactive/ProjectReactor.md new file mode 100644 index 0000000..1053907 --- /dev/null +++ b/Reactive/ProjectReactor.md @@ -0,0 +1,32 @@ +--- +title: Project Reactor +date: 2023-10-10 11:28:24 +tags: +categories: +--- + +💠 + +- 1. [Project Reactor](#project-reactor) + - 1.1. [Reactive Streams](#reactive-streams) + - 1.2. [Concept](#concept) +- 2. [WebFlux](#webflux) + +💠 2023-10-12 11:48 +**************************************** +# Project Reactor +> [Official Site](https://projectreactor.io) + +## Reactive Streams +> [Official Site](https://www.reactive-streams.org/) + +## Concept + +- Publisher: Flux(0-N) Mono(0/1) + - 无结果值时 可用`Mono` 类似于 Runable +- + +************************ + +# WebFlux +- [Note: WebFlux](/Java/Spring/WebFlux.md) diff --git a/Reactive/README.md b/Reactive/README.md index b9306c6..9703739 100644 --- a/Reactive/README.md +++ b/Reactive/README.md @@ -1,5 +1,18 @@ # 响应式编程 -> [project reactor](http://projectreactor.io/) +> 规范 [Reactivex](https://reactivex.io/) -> [参考博客: Reactor模式详解](http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html) +> [参考: Reactor模式详解](http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html) +优势:在有高IO处理时间的任务中,响应式比同步命令式能带来更高的吞吐量和扩展性。例如:网关 + +## Java领域 +> [Blog: Reactive Systems in Java](https://www.baeldung.com/java-reactive-systems) + +[Project Reactor](http://projectreactor.io/) | [RxJava](https://github.com/ReactiveX/RxJava) + + +Reactive框架 : RxJava, Reactor, Akka, Kotlin Coroutines & Flow +Web框架 : Spring WebFlux, Vert.x, Micronaut, Helidon +DAL层 : Spring Data Reactive (Redis: Lettuce MongoDB: ) +通信层 : RSocket, Reactor Netty, Reactor Aeron, Reactive Dubbo +消息队列: Reactor Kafka, Reactor RabbitMQ, RocketMQ diff --git a/Readme.md b/Readme.md index c7b85d7..8352be3 100644 --- a/Readme.md +++ b/Readme.md @@ -114,29 +114,3 @@ _有自制力的人, 能够坚持的人才更有可能成功!_ 一年多,积累了很多读者,也通过写的东西造福了很多找工作的学生、迷茫的大学生、想转行的就业者,也让我有动力的不断继续更新下去。 > -- 知乎: 路人甲 -## 【学习历程】 - -| 时间 | 语言或工具 | -| :--------: | :------: | -| 2014.09 | 接触C | -| 2015.07 | 接触Java | -| 2016.02 | 接触Maven | -| 2016.03 | 接触Gradle | -| 2017.03 | 接触Python | -| 2017.04 | 接触Groovy | -| 2017.05 | 接触Linux | -| 2017.08 | 接触Docker | -| 2018.07 | 接触Go | - -************** -## 个人备忘 -- [阿里Java编程规范](Java/AlibabaJavaStandard.md) -- [常用SDK自动下载配置工具 mythsdk](https://github.com/Kuangcp/Script/tree/master/python/mythsdk) - - `功能仿sdkman,但简单,不过是提供自行搭建使用的,毕竟七牛云下载超量了是要收费的` -- 克隆笔记仓库可以加上 `--depth 1` 参数, 减小克隆的大小 -- [notes](https://github.com/pimterry/notes)`终端内的note工具` - -## 涉及的工具 -1. [创建 SUMMARY.md 目录文件脚本](create_tree.py) -1. [统计仓库所有中文字数](wordRecord.sh) `count 命令是 go 写的工具 [项目地址](https://gitee.com/gin9/GoBase/tree/master/count)` -1. [为每个md文件创建目录头部](https://gitee.com/gin9/script/blob/master/shell/text/deal_md.sh) diff --git a/Repository.md b/Repository.md index 2c5d847..c418a65 100644 --- a/Repository.md +++ b/Repository.md @@ -1,49 +1,54 @@ -`目录 start` - -- [【GitRepos】](#gitrepos) - - [【Organization】](#organization) - - [User](#user) - - [翻译和搬运](#翻译和搬运) - - [【算法】](#算法) - - [【Java】](#java) - - [综合索引](#综合索引) - - [源码学习](#源码学习) - - [Java8](#java8) - - [框架以及高级特性](#框架以及高级特性) - - [应用广泛](#应用广泛) - - [个人团体开发](#个人团体开发) - - [二次框架](#二次框架) - - [进阶](#进阶) - - [Demo](#demo) - - [微服务](#微服务) - - [Android](#android) - - [Web](#web) - - [前后端综合](#前后端综合) - - [BuildTool](#buildtool) - - [Tool](#tool) - - [【Docker】](#docker) - - [web](#web) - - [Demo](#demo) - - [【Python】](#python) - - [【Linux】](#linux) - - [Database](#database) - - [前端](#前端) - - [Vue](#vue) - - [有趣](#有趣) - - [Tools](#tools) - - [微信相关](#微信相关) - - [网络有关](#网络有关) - - [其他](#其他) - - [工作](#工作) - - [简历](#简历) - -`目录 end` |_2018-08-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 开源项目列表 +date: 2017-11-21 10:56:52 +tags: +categories: +--- + +**目录 start** + +1. [Git Repository](#git-repository) + 1. [Organization](#organization) + 1. [User](#user) +1. [翻译和搬运](#翻译和搬运) +1. [算法](#算法) +1. [Java](#java) + 1. [源码学习](#源码学习) + 1. [Java8](#java8) + 1. [框架以及高级特性](#框架以及高级特性) + 1. [应用广泛](#应用广泛) + 1. [团队协作](#团队协作) + 1. [二次框架](#二次框架) + 1. [进阶](#进阶) + 1. [Demo](#demo) + 1. [微服务](#微服务) + 1. [Android](#android) + 1. [Web](#web) + 1. [前后端综合](#前后端综合) + 1. [Tool](#tool) +1. [Docker](#docker) + 1. [web](#web) + 1. [Demo](#demo) +1. [Python](#python) +1. [Linux](#linux) +1. [Database](#database) +1. [前端](#前端) + 1. [Vue](#vue) + 1. [有趣](#有趣) +1. [Tools](#tools) + 1. [微信相关](#微信相关) + 1. [网络有关](#网络有关) +1. [其他](#其他) +1. [工作](#工作) + 1. [简历](#简历) + +**目录 end**|_2020-06-24 02:06_| **************************************** -# 【GitRepos】 -> 收集的一些优秀的git仓库, 一般是参考学习为主, 用为主的工具放在了AppList下 +# Git Repository +> 收集的一些优秀的git仓库, 参考学习为主 -## 【Organization】 +## Organization - [ApacheCN](https://github.com/apachecn)`含有一些学习的文档和视频资料` - [稀土](https://github.com/xitu) `掘金的组织` - [docker-library](https://github.com/docker-library)`docker仓库组织,含有大量软件的Dockerfile` @@ -67,32 +72,32 @@ - [xuxiaodong](https://github.com/xuxiaodong)`活跃在开源社区, 17年开始就不活跃了,不知道为什么` - [mank319](https://github.com/mank319)`go for it 作者` - [cycleuser](https://github.com/cycleuser)`博士, 翻译了很多东西` +- [crossoverJie](https://github.com/crossoverJie) -## 翻译和搬运 +************************ + +# 翻译和搬运 - [translations](https://github.com/oldratlee/translations)`翻译外国的文章` - [掘金翻译计划](https://github.com/xitu/gold-miner) ******************************************* -## 【算法】 +# 算法 - [魏楚阳的算法学习](https://github.com/brianway/algorithms-learning) - [interview](https://github.com/kdn251/interviews)`面试所需算法练习` *********************************** -## 【Java】 -### 综合索引 -> [Java资源大全中文版](https://github.com/jobbole/awesome-java-cn) - -### 源码学习 +# Java +## 源码学习 - [tiny-spring](https://github.com/code4craft/tiny-spring) `Spring精简版` -### Java8 +## Java8 - [little-java-functions](https://github.com/shekhargulati/little-java-functions)`Java8的函数式编程` - [作者博客](https://shekhargulati.com/) - [java8指南](https://github.com/winterbe/java8-tutorial) - [vavr](https://github.com/vavr-io/vavr)`基于Java8的库, 简化代码` -### 框架以及高级特性 -#### 应用广泛 +## 框架以及高级特性 +### 应用广泛 - [druid](https://github.com/alibaba/druid) `阿里数据库连接池` - [dubbo](https://github.com/alibaba/dubbo) `RPC 框架` - [Eclipse Collections](http://www.eclipse.org/collections/)`更丰富的集合操作` @@ -105,8 +110,7 @@ - [webmagic](https://github.com/code4craft/webmagic) `爬虫框架` - [zxing](https://github.com/zxing/zxing) `二维码处理的项目` -#### 个人团体开发 -- [easypoi](https://gitee.com/lemur/easypoi) `虽然代码非常不规范,也没人维护的样子,但是这个开源精神要学习` +### 团队协作 - [uflo](https://gitee.com/youseries/uflo) `国内的流程引擎` - [jeewx](https://gitee.com/jeecg/jeewx)`微信管家平台` - [禅道](https://gitee.com/wwccss/zentaopms)`项目管理软件` @@ -116,17 +120,17 @@ - [java-repl](https://github.com/albertlatacz/java-repl)`Java REPL环境` - [JsonPath](https://github.com/json-path/JsonPath) ` Java DSL for reading JSON` -### 二次框架 +## 二次框架 - [jeesite](https://gitee.com/thinkgem/jeesite)`Spring基础构建的maven jsp项目 bootstrap` - [Guns](https://gitee.com/naan1993/guns)`基于Springboot开发了大量模块` - [MyRestUtil](https://github.com/xwjie/MyRestUtil)`Rest` -### 进阶 +## 进阶 - [全栈?](https://github.com/phodal/growth-v2) - [Java设计模式](https://github.com/iluwatar/java-design-patterns) `设计模式详解` - [编码规范 知乎上的专栏对应源码](https://github.com/xwjie/PLMCodeTemplate)`程序员为什么这么累` -### Demo +## Demo - [springboot-action](https://github.com/lianggzone/springboot-action) `Springboot常见的模块都有,文档也多` - [Java-learing](https://github.com/brianway/java-learning) `Java基础学习仓库, 代码量比较大也比较详细, 但是没有逻辑和注释, 看起来费劲` @@ -138,16 +142,16 @@ - [netty-in-action-cn](https://github.com/ReactivePlatform/netty-in-action-cn) `Netty实战中文版配套代码` - [日志系统](https://github.com/ZhongFuCheng3y/JournalSystem)`SpringBoot 开发的` -#### 微服务 +### 微服务 - [shop](https://github.com/lrwinx/shop)`SpringClod的实战案例` - [daijie](https://github.com/daijiejay/daijie)`基于spring-cloud系列整合的依赖jar包` **************** -### Android +## Android - [安卓进阶学习指南](https://github.com/iwannabetop/Awesome-Android-Learning-Guide) **************** -### Web +## Web - [Springboot实战](https://github.com/lianggzone/springboot-action) `Springboot的案例` - [weixin-java-tools](https://github.com/chanjarster/weixin-java-tools) `微信的java工具项目 包含企业号 微信支付 小程序` - [zheng](https://github.com/shuzheng/zheng) `SSM框架整合的一个丰富解决方案` @@ -155,17 +159,11 @@ - [java-jwt](https://github.com/auth0/java-jwt)`JSON WEB TOKEN` - [blade](https://github.com/biezhi/blade)`轻量级Web框架` -#### 前后端综合 +### 前后端综合 - [vue vuex Springboot ](https://github.com/xwjie/ElementVueSpringbootCodeTemplate)`代码模板` -******************* -### BuildTool -- ant maven gradle -- [sbt](https://github.com/sbt/sbt) - - ************************ -### Tool +## Tool - [IDEA教程](https://github.com/judasn/IntelliJ-IDEA-Tutorial) - [SeleniumHQ](https://github.com/SeleniumHQ/selenium)`自动化测试` - [Springboot搭建的简易博客](https://github.com/wchstrife/blog) @@ -174,23 +172,21 @@ - [notes](https://github.com/pimterry/notes)`notes shell 软件` ******************************************************* -## 【Docker】 +# Docker - [Docker官方github组织](https://github.com/docker-library) `docker的官方Dockerfile以及doc文档` - [openoffice in docker](https://github.com/tobegit3hub/dockerized-openoffice) -- [shipyard](https://github.com/shipyard/shipyardhttps://github.com/shipyard/shipyard) `docker图形化,功能稍多,配置略麻烦` -- [ui-for-docker](https://github.com/kevana/ui-for-docker) `docker 简单易用图形化` - [openjdk](https://github.com/docker-library/openjdk) `openjdk的dockerfile仓库` - [docker-alpine-java](https://github.com/anapsix/docker-alpine-java) `java 运行环境 alpine为基础镜像` -### web +## web - [webdis](https://github.com/anapsix/docker-webdis) `基于redis构建的使用http请求存取数据` -### Demo +## Demo - [gin-example](https://github.com/EDDYCJY/go-gin-example) - [对应博客](https://segmentfault.com/a/1190000013297625) ********* -## 【Python】 +# Python - [python](https://github.com/zhanghe06/python) - [python](https://github.com/xxg1413/python.git)`某书的案例源码` - [跟老齐学Python](https://github.com/qiwsir/StarterLearningPython)`书籍配套源码` @@ -201,56 +197,59 @@ - [习题的一些Python](https://github.com/qiwsir/StarterLearningPython.git ) - [区块链Python](https://github.com/dvf/blockchain)`附加Python中Docker的用法` - [pyspider](https://github.com/binux/pyspider)`爬虫框架` -- [httpie]()https://github.com/jakubroztocil/httpie *********** -## 【Linux】 +# Linux - [Java程序员眼中的Linux](https://github.com/judasn/Linux-Tutorial) ************ -## Database +# Database - [ssdb](https://github.com/ideawu/ssdb) `一个类似redis的键值对数据库` -## 前端 +# 前端 - [amazeUI](http://amazeui.org/) - [hui](http://h-ui.net/Hui-overview.shtml) - [LayUI](https://github.com/sentsin/layui/) `国产layUI框架` - [js代码块集合](https://github.com/Chalarangelo/30-seconds-of-code) - [flowchart](https://github.com/adrai/flowchart.js)`绘制svg flow图形` -### Vue +- [mathjax](https://github.com/mathjax/mathjax)`渲染数学公式` + +## Vue - [iview](https://github.com/iview/iview)`vue 的前端组件` | [文档](https://www.iviewui.com/docs/guide/start) - [vue-rap](https://gitee.com/tengzhinei/Vue-rap) | [文档](https://www.kancloud.cn/tengzhinei/vue-rap) - [vue-layui](https://github.com/IBAS0742/vue-layui)`vue和layui的结合` - [ssr-web](https://github.com/xiaofuzi/ssr-web)`基于vue的静态博客生成器` -### 有趣 +## 有趣 - [一个在线游戏](https://github.com/memdealloc/memdealloc.github.io) - [插件项目, 搜图](https://github.com/AInoob/NooBox) - [完整项目网址](https://ainoob.com/zh/projects) *************************************** -## Tools +# Tools - [oneinstack](https://github.com/lj2007331/oneinstack) `一个在Linux上管理web,数据库,ftp, 等服务的软件` -- [codefont](https://github.com/zhenruyan/codefont) `编程使用的等宽字体` `code的不错` - [学生的优惠资源](https://github.com/OpenGenus/Best-student-discount-services) - [JVM分析优化的定制JDK](https://www.yourkit.com/java/profiler/features/) - [acme](https://github.com/Neilpang/acme.sh)`可以自动更新https证书从lets上` - [acme-tiny](https://github.com/diafygi/acme-tiny)`同样支持acme协议然后更新证书` - [noVNC](https://github.com/novnc/noVNC)`js VNC client` -### 微信相关 +## 微信相关 - [微信小程序Demo](https://gitee.com/yuzeng84/wxapp) -### 网络有关 +视频目录: \Android\data\com.tencent.mm\MicroMsg\xxx\video + +## 网络有关 - [frp](https://github.com/fatedier/frp/blob/master/README_zh.md)`内网穿透` -## 其他 +# 其他 - [HelloGibhub](https://github.com/521xueweihan/HelloGitHub)`有趣的项目` - [serverless](https://github.com/phodal/serverless)`serverless学习手册` - [marp](https://github.com/yhatt/marp/)`markdown书写PPT` - [build your own ](https://github.com/danistefanovic/build-your-own-x)`使用各种语言和技术栈,实现一个东西` +- [Hello Github](https://github.com/521xueweihan/HelloGitHub)`一个分享 GitHub 上有趣、入门级的开源项目` -## 工作 -### 简历 +# 工作 +## 简历 - [简历模板](https://github.com/geekcompany/ResumeSample) diff --git a/Rust/Readme.md b/Rust/Readme.md new file mode 100644 index 0000000..27a5c01 --- /dev/null +++ b/Rust/Readme.md @@ -0,0 +1,24 @@ +--- +title: Rust 概览 +date: 2020-03-22 12:41:43 +tags: +categories: +--- + +**目录 start** + +1. [Rust](#rust) + 1. [安装](#安装) + 1. [Terminal App](#terminal-app) + +**目录 end**|_2020-03-22 12:41_| +**************************************** +# Rust +- [ ] 理解和学习 它甚至是用同一套抽象解决了内存安全和数据竞争这两个不同领域的问题。 + +## 安装 +> [install](https://www.rust-lang.org/zh-CN/tools/install) + +## Rust Terminal App +> [参考: Making Terminal Applications in Rust with Termion](http://ticki.github.io/blog/making-terminal-applications-in-rust-with-termion/) + diff --git a/Rust/RustBase.md b/Rust/RustBase.md new file mode 100644 index 0000000..f411f1a --- /dev/null +++ b/Rust/RustBase.md @@ -0,0 +1,16 @@ +--- +title: Rust基础 +date: 2020-03-22 12:34:33 +tags: +categories: +--- + +**目录 start** + +1. [Rust](#rust) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Rust +> [get started](https://www.rust-lang.org/zh-CN/learn/get-started) + diff --git a/SUMMARY.md b/SUMMARY.md index cc2b705..935f8b2 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -2,65 +2,70 @@ * [ Introduction ](README.md) -* [ Platform ](./Platform.md) -* [ Process ](./Process.md) * [ Repository ](./Repository.md) -* [ SUMMARY ](./SUMMARY.md) -* [ Website ](./Website.md) -* 【 Article 】 - * [ Font ](/Article/Font.md) - * [ HistoricalBias ](/Article/HistoricalBias.md) - * [ Memorandum ](/Article/Memorandum.md) - * [ NotCode ](/Article/NotCode.md) - * [ Science ](/Article/Science.md) +* 【 Algorithm 】 + * 【 Algorithm/DS 】 + * [ LinearList ](/Algorithm/DS/LinearList.md) + * [ Tree ](/Algorithm/DS/Tree.md) + * [ Algorithm ](/Algorithm/Algorithm.md) + * [ Sort ](/Algorithm/Sort.md) +* 【 BSD 】 + * [ FreeBSD ](/BSD/FreeBSD.md) + * [ OpenBSD ](/BSD/OpenBSD.md) * 【 Blog 】 * [ Blog ](/Blog/Blog.md) * [ Java ](/Blog/Java.md) * [ Server ](/Blog/Server.md) * [ Solution ](/Blog/Solution.md) * [ View ](/Blog/View.md) -* 【 Book 】 - * 【 Book/CS 】 - * [ NetworkBooks ](/Book/CS/NetworkBooks.md) - * 【 Book/Database 】 - * 【 Book/Front 】 - * 【 Book/Math 】 - * 【 Book/Work 】 - * [ Project ](/Book/Work/Project.md) - * [ DockerBook ](/Book/DockerBook.md) - * [ GoBooks ](/Book/GoBooks.md) - * [ JavaBooks ](/Book/JavaBooks.md) - * [ LinuxBooks ](/Book/LinuxBooks.md) - * [ PythonBooks ](/Book/PythonBooks.md) +* 【 C 】 + * [ CBase ](/C/CBase.md) * 【 Database 】 - * [ Experience ](/Database/Experience.md) - * [ MangoDB ](/Database/MangoDB.md) + * [ DataBase ](/Database/DataBase.md) + * [ GraphQL ](/Database/GraphQL.md) + * [ MongoDB ](/Database/MongoDB.md) * [ MySQL ](/Database/MySQL.md) * [ MySQLAdvance ](/Database/MySQLAdvance.md) * [ Oracle ](/Database/Oracle.md) * [ OraclePerformances ](/Database/OraclePerformances.md) * [ PostgreSQL ](/Database/PostgreSQL.md) + * [ PostgreSQLAdvance ](/Database/PostgreSQLAdvance.md) * [ Redis ](/Database/Redis.md) + * [ RedisAdvance ](/Database/RedisAdvance.md) * [ SQL ](/Database/SQL.md) * [ SQLServer ](/Database/SQLServer.md) * 【 Distributed 】 - * 【 Distributed/Middlewave 】 - * [ Apollo ](/Distributed/Middlewave/Apollo.md) + * 【 Distributed/ConfigCenter 】 + * [ Apollo ](/Distributed/ConfigCenter/Apollo.md) + * [ ZooKeeper ](/Distributed/ConfigCenter/ZooKeeper.md) + * 【 Distributed/DataProcessingEngine 】 + * [ Flink ](/Distributed/DataProcessingEngine/Flink.md) + * 【 Distributed/MQ 】 + * [ MQ ](/Distributed/MQ/MQ.md) + * 【 Distributed/ServiceDiscovery 】 + * 【 Distributed/Transaction 】 + * [ CloudNative ](/Distributed/CloudNative.md) + * [ RPC ](/Distributed/RPC.md) * 【 FrontEnd 】 * 【 FrontEnd/Frame 】 * [ LayUI ](/FrontEnd/Frame/LayUI.md) * [ Vue ](/FrontEnd/Frame/Vue.md) * 【 FrontEnd/Node 】 * [ NodeJS ](/FrontEnd/Node/NodeJS.md) + * [ Extjs ](/FrontEnd/Extjs.md) + * [ Font ](/FrontEnd/Font.md) * [ Hexo ](/FrontEnd/Hexo.md) * [ JavaScript ](/FrontEnd/JavaScript.md) * [ LearnPS ](/FrontEnd/LearnPS.md) * [ ResponseCode ](/FrontEnd/ResponseCode.md) + * [ SVG ](/FrontEnd/SVG.md) * [ ViewSolution ](/FrontEnd/ViewSolution.md) * 【 Functional 】 * [ Clojure ](/Functional/Clojure.md) * [ FPBase ](/Functional/FPBase.md) * [ Kotlin ](/Functional/Kotlin.md) +* 【 Game 】 + * [ MC ](/Game/MC.md) * 【 Go 】 * [ GoBase ](/Go/GoBase.md) * [ GoDatabase ](/Go/GoDatabase.md) @@ -68,84 +73,137 @@ * 【 Groovy 】 * [ Groovy ](/Groovy/Groovy.md) * [ GroovySpring ](/Groovy/GroovySpring.md) +* 【 Hardware 】 + * [ BIOS ](/Hardware/BIOS.md) + * [ Interface ](/Hardware/Interface.md) * 【 Java 】 * 【 Java/AdvancedLearning 】 - * [ Annotation ](/Java/AdvancedLearning/Annotation.md) - * [ ClassFile ](/Java/AdvancedLearning/ClassFile.md) - * [ Collection ](/Java/AdvancedLearning/Collection.md) - * [ Concurrents ](/Java/AdvancedLearning/Concurrents.md) - * [ Deploy ](/Java/AdvancedLearning/Deploy.md) - * [ Exception ](/Java/AdvancedLearning/Exception.md) - * [ ExtendsAndInterface ](/Java/AdvancedLearning/ExtendsAndInterface.md) - * [ GC ](/Java/AdvancedLearning/GC.md) - * [ Generics ](/Java/AdvancedLearning/Generics.md) - * [ GrammarAndType ](/Java/AdvancedLearning/GrammarAndType.md) - * [ IO ](/Java/AdvancedLearning/IO.md) + * 【 Java/AdvancedLearning/Basic 】 + * [ StringConcat ](/Java/AdvancedLearning/Basic/StringConcat.md) + * 【 Java/AdvancedLearning/Cache 】 + * [ Caffeine ](/Java/AdvancedLearning/Cache/Caffeine.md) + * [ EhCache ](/Java/AdvancedLearning/Cache/EhCache.md) + * [ GuavaCache ](/Java/AdvancedLearning/Cache/GuavaCache.md) + * [ Memcache ](/Java/AdvancedLearning/Cache/Memcache.md) + * 【 Java/AdvancedLearning/Collection 】 + * [ List ](/Java/AdvancedLearning/Collection/List.md) + * 【 Java/AdvancedLearning/Concurrency 】 + * [ Atomic ](/Java/AdvancedLearning/Concurrency/Atomic.md) + * [ ExecutorAndPool ](/Java/AdvancedLearning/Concurrency/ExecutorAndPool.md) + * [ ForkAndJoin ](/Java/AdvancedLearning/Concurrency/ForkAndJoin.md) + * [ Lock ](/Java/AdvancedLearning/Concurrency/Lock.md) + * [ Sync ](/Java/AdvancedLearning/Concurrency/Sync.md) + * 【 Java/AdvancedLearning/Map 】 + * [ HashMap ](/Java/AdvancedLearning/Map/HashMap.md) + * [ TreeMap ](/Java/AdvancedLearning/Map/TreeMap.md) * [ JDBC ](/Java/AdvancedLearning/JDBC.md) * [ JDKAndJRE ](/Java/AdvancedLearning/JDKAndJRE.md) - * [ JMS ](/Java/AdvancedLearning/JMS.md) + * [ JMX ](/Java/AdvancedLearning/JMX.md) * [ JVM ](/Java/AdvancedLearning/JVM.md) + * [ Java11 ](/Java/AdvancedLearning/Java11.md) * [ Java7 ](/Java/AdvancedLearning/Java7.md) * [ Java8 ](/Java/AdvancedLearning/Java8.md) - * [ JavaPerformance ](/Java/AdvancedLearning/JavaPerformance.md) + * [ JavaAnnotation ](/Java/AdvancedLearning/JavaAnnotation.md) + * [ JavaBasicSyntax ](/Java/AdvancedLearning/JavaBasicSyntax.md) + * [ JavaClass ](/Java/AdvancedLearning/JavaClass.md) + * [ JavaCollection ](/Java/AdvancedLearning/JavaCollection.md) + * [ JavaConcurrency ](/Java/AdvancedLearning/JavaConcurrency.md) + * [ JavaDebug ](/Java/AdvancedLearning/JavaDebug.md) + * [ JavaDeploy ](/Java/AdvancedLearning/JavaDeploy.md) + * [ JavaException ](/Java/AdvancedLearning/JavaException.md) + * [ JavaGenerics ](/Java/AdvancedLearning/JavaGenerics.md) + * [ JavaIO ](/Java/AdvancedLearning/JavaIO.md) + * [ JavaInheritedAndInterface ](/Java/AdvancedLearning/JavaInheritedAndInterface.md) + * [ JavaNetwork ](/Java/AdvancedLearning/JavaNetwork.md) + * [ JavaProxy ](/Java/AdvancedLearning/JavaProxy.md) + * [ JavaReflection ](/Java/AdvancedLearning/JavaReflection.md) * [ JavaReleaseVersion ](/Java/AdvancedLearning/JavaReleaseVersion.md) - * [ JavaTest ](/Java/AdvancedLearning/JavaTest.md) - * [ MultipleLanguage ](/Java/AdvancedLearning/MultipleLanguage.md) - * [ ProgramThinking ](/Java/AdvancedLearning/ProgramThinking.md) - * [ Reflect ](/Java/AdvancedLearning/Reflect.md) - * [ Socket ](/Java/AdvancedLearning/Socket.md) - * [ Thread ](/Java/AdvancedLearning/Thread.md) - * [ Web ](/Java/AdvancedLearning/Web.md) - * [ WebPerformance ](/Java/AdvancedLearning/WebPerformance.md) + * [ JavaSerialize ](/Java/AdvancedLearning/JavaSerialize.md) + * [ JavaThread ](/Java/AdvancedLearning/JavaThread.md) + * [ JavaWeb ](/Java/AdvancedLearning/JavaWeb.md) + * [ JvmPerformance ](/Java/AdvancedLearning/JvmPerformance.md) + * [ MultipleLanguageInJVM ](/Java/AdvancedLearning/MultipleLanguageInJVM.md) + * 【 Java/Android 】 + * [ AndriodDB ](/Java/Android/AndriodDB.md) + * [ BasicConcept ](/Java/Android/BasicConcept.md) + * 【 Java/Blog 】 + * [ Java-ClassLoad-Confuse ](/Java/Blog/Java-ClassLoad-Confuse.md) + * [ WebSocket-demo ](/Java/Blog/WebSocket-demo.md) + * [ instantiation-object ](/Java/Blog/instantiation-object.md) + * [ reduce-if-else-for-java ](/Java/Blog/reduce-if-else-for-java.md) + * [ use-maven-create-web-project ](/Java/Blog/use-maven-create-web-project.md) * 【 Java/Ecosystem 】 + * 【 Java/Ecosystem/Dubbo 】 + * [ Dubbo ](/Java/Ecosystem/Dubbo/Dubbo.md) + * [ MulticaseRegistry ](/Java/Ecosystem/Dubbo/MulticaseRegistry.md) * [ Activiti ](/Java/Ecosystem/Activiti.md) * [ Blade ](/Java/Ecosystem/Blade.md) * [ Elasticsearch ](/Java/Ecosystem/Elasticsearch.md) * [ Guava ](/Java/Ecosystem/Guava.md) * [ Hibernate ](/Java/Ecosystem/Hibernate.md) * [ JPA ](/Java/Ecosystem/JPA.md) - * [ JXls ](/Java/Ecosystem/JXls.md) + * [ JavaBoot ](/Java/Ecosystem/JavaBoot.md) * [ JavaRedis ](/Java/Ecosystem/JavaRedis.md) - * [ Kafaka ](/Java/Ecosystem/Kafaka.md) + * [ Kafka ](/Java/Ecosystem/Kafka.md) * [ Mybatis ](/Java/Ecosystem/Mybatis.md) * [ Netty ](/Java/Ecosystem/Netty.md) * [ POI ](/Java/Ecosystem/POI.md) - * [ Querydsl ](/Java/Ecosystem/Querydsl.md) + * [ QueryDSL ](/Java/Ecosystem/QueryDSL.md) + * [ SPI ](/Java/Ecosystem/SPI.md) * [ Solr ](/Java/Ecosystem/Solr.md) - * [ Struts ](/Java/Ecosystem/Struts.md) * [ Vertx ](/Java/Ecosystem/Vertx.md) * 【 Java/MSA 】 * 【 Java/Spring 】 + * 【 Java/Spring/Why 】 + * [ SpringSession ](/Java/Spring/Why/SpringSession.md) + * [ Spring-Bean学习 ](/Java/Spring/Spring-Bean学习.md) + * [ Spring-Cloud ](/Java/Spring/Spring-Cloud.md) * [ Spring ](/Java/Spring/Spring.md) * [ Spring5 ](/Java/Spring/Spring5.md) + * [ SpringAOP ](/Java/Spring/SpringAOP.md) * [ SpringBoot ](/Java/Spring/SpringBoot.md) * [ SpringBoot2 ](/Java/Spring/SpringBoot2.md) * [ SpringCloud ](/Java/Spring/SpringCloud.md) * [ SpringMVC ](/Java/Spring/SpringMVC.md) - * [ SpringMessageQue ](/Java/Spring/SpringMessageQue.md) * [ SpringSecurity ](/Java/Spring/SpringSecurity.md) + * [ SpringShell ](/Java/Spring/SpringShell.md) * [ SpringTest ](/Java/Spring/SpringTest.md) * [ SpringWebFlux ](/Java/Spring/SpringWebFlux.md) * [ SpringbootDatabase ](/Java/Spring/SpringbootDatabase.md) + * [ Spring官方文档学习 ](/Java/Spring/Spring官方文档学习.md) + * [ Spring容器扩展点之Aware接口 ](/Java/Spring/Spring容器扩展点之Aware接口.md) + * [ Spring容器扩展点之BeanFactoryPostProcessor ](/Java/Spring/Spring容器扩展点之BeanFactoryPostProcessor.md) + * [ Spring容器扩展点之BeanPostProcessor ](/Java/Spring/Spring容器扩展点之BeanPostProcessor.md) * [ Transactional ](/Java/Spring/Transactional.md) - * 【 Java/TemplateEngine 】 - * [ Thymeleaf ](/Java/TemplateEngine/Thymeleaf.md) + * 【 Java/Test 】 + * [ JMH ](/Java/Test/JMH.md) + * [ JavaCucumber ](/Java/Test/JavaCucumber.md) + * [ JavaHamcreset ](/Java/Test/JavaHamcreset.md) + * [ JavaTest ](/Java/Test/JavaTest.md) + * [ Junit ](/Java/Test/Junit.md) + * [ TestNG ](/Java/Test/TestNG.md) * 【 Java/Tool 】 + * [ Eclipse ](/Java/Tool/Eclipse.md) * [ Gradle ](/Java/Tool/Gradle.md) * [ GradleAdvance ](/Java/Tool/GradleAdvance.md) * [ IDEA ](/Java/Tool/IDEA.md) - * [ Jetty ](/Java/Tool/Jetty.md) + * [ Jacoco ](/Java/Tool/Jacoco.md) * [ Lombok ](/Java/Tool/Lombok.md) + * [ MapStruct ](/Java/Tool/MapStruct.md) * [ Maven ](/Java/Tool/Maven.md) + * [ MavenAdvance ](/Java/Tool/MavenAdvance.md) * [ Tomcat ](/Java/Tool/Tomcat.md) * [ AlibabaJavaStandard ](/Java/AlibabaJavaStandard.md) - * [ EE ](/Java/EE.md) - * [ JavaSE ](/Java/JavaSE.md) - * [ LittleKnowledgePoint ](/Java/LittleKnowledgePoint.md) + * [ DesignPattern ](/Java/DesignPattern.md) + * [ Java-NIO ](/Java/Java-NIO.md) + * [ JavaReadme ](/Java/JavaReadme.md) * [ Log ](/Java/Log.md) * [ MIS ](/Java/MIS.md) - * [ RESTful ](/Java/RESTful.md) - * [ ZenOfPattern ](/Java/ZenOfPattern.md) + * [ Quartz ](/Java/Quartz.md) +* 【 Kotlin 】 + * [ Dubbo ](/Kotlin/Dubbo.md) + * [ Shiro ](/Kotlin/Shiro.md) + * [ kotlin-base ](/Kotlin/kotlin-base.md) * 【 Linux 】 * 【 Linux/Alpine 】 * [ AlpineBase ](/Linux/Alpine/AlpineBase.md) @@ -153,81 +211,118 @@ * [ Arch ](/Linux/Arch/Arch.md) * [ Manjaro ](/Linux/Arch/Manjaro.md) * 【 Linux/Base 】 + * [ EffectiveLinux ](/Linux/Base/EffectiveLinux.md) * [ LinuxBase ](/Linux/Base/LinuxBase.md) * [ LinuxCommand ](/Linux/Base/LinuxCommand.md) * [ LinuxCompressFile ](/Linux/Base/LinuxCompressFile.md) * [ LinuxDirectoryStructure ](/Linux/Base/LinuxDirectoryStructure.md) * [ LinuxFile ](/Linux/Base/LinuxFile.md) - * [ LinuxNet ](/Linux/Base/LinuxNet.md) + * [ LinuxManual ](/Linux/Base/LinuxManual.md) + * [ LinuxNetwork ](/Linux/Base/LinuxNetwork.md) * [ LinuxPerformance ](/Linux/Base/LinuxPerformance.md) * [ LinuxProblem ](/Linux/Base/LinuxProblem.md) * [ LinuxStreamEditor ](/Linux/Base/LinuxStreamEditor.md) + * [ LinuxUI ](/Linux/Base/LinuxUI.md) * [ ReleaseExperience ](/Linux/Base/ReleaseExperience.md) - * [ Ssh ](/Linux/Base/Ssh.md) - * 【 Linux/Centos 】 - * [ CentosBase ](/Linux/Centos/CentosBase.md) + * [ SSH ](/Linux/Base/SSH.md) * 【 Linux/Container 】 * [ Docker ](/Linux/Container/Docker.md) + * [ DockerAdvance ](/Linux/Container/DockerAdvance.md) * [ DockerFile ](/Linux/Container/DockerFile.md) * [ DockerSoft ](/Linux/Container/DockerSoft.md) + * [ Helm ](/Linux/Container/Helm.md) * [ Kubernetes ](/Linux/Container/Kubernetes.md) + * [ Vagrant ](/Linux/Container/Vagrant.md) * 【 Linux/Debian 】 * [ Debian ](/Linux/Debian/Debian.md) + * [ Deepin ](/Linux/Debian/Deepin.md) * [ Ubuntu ](/Linux/Debian/Ubuntu.md) + * 【 Linux/RedHat 】 + * [ CentosBase ](/Linux/RedHat/CentosBase.md) * 【 Linux/Tool 】 - * [ Caddy ](/Linux/Tool/Caddy.md) + * [ IME ](/Linux/Tool/IME.md) * [ Nginx ](/Linux/Tool/Nginx.md) - * [ Terminal ](/Linux/Tool/Terminal.md) + * [ Tmux ](/Linux/Tool/Tmux.md) * [ Vim ](/Linux/Tool/Vim.md) - * [ Zsh ](/Linux/Tool/Zsh.md) - * 【 Linux/Vcs 】 - * [ GitAction ](/Linux/Vcs/GitAction.md) - * [ GitBase ](/Linux/Vcs/GitBase.md) - * [ GitTeam ](/Linux/Vcs/GitTeam.md) - * [ Svn ](/Linux/Vcs/Svn.md) + * 【 Linux/Window 】 + * [ Gnome ](/Linux/Window/Gnome.md) + * [ KDE ](/Linux/Window/KDE.md) + * [ Xfce ](/Linux/Window/Xfce.md) * [ JavaDevInit ](/Linux/JavaDevInit.md) + * [ LinuxReadme ](/Linux/LinuxReadme.md) * 【 MyBlog 】 + * [ 12306 ](/MyBlog/12306.md) * [ 2018-3-15-install-deepin ](/MyBlog/2018-3-15-install-deepin.md) - * [ why-put-netty-in-tomcat ](/MyBlog/why-put-netty-in-tomcat.md) + * [ 2020-05-16-install-manjaro ](/MyBlog/2020-05-16-install-manjaro.md) + * [ Flink-with-batch ](/MyBlog/Flink-with-batch.md) + * [ Out-of-memory ](/MyBlog/Out-of-memory.md) + * [ twenty-years-of-weak-japan ](/MyBlog/twenty-years-of-weak-japan.md) +* 【 OpenCV 】 * 【 Python 】 * 【 Python/Tool 】 * [ PyCharm ](/Python/Tool/PyCharm.md) + * [ Selenium ](/Python/Tool/Selenium.md) * [ Python ](/Python/Python.md) + * [ PythonConcurrency ](/Python/PythonConcurrency.md) * [ PythonGUI ](/Python/PythonGUI.md) * [ PythonGame ](/Python/PythonGame.md) * [ PythonNet ](/Python/PythonNet.md) + * [ PythonOffices ](/Python/PythonOffices.md) * [ PythonWeb ](/Python/PythonWeb.md) + * [ Python核心学习 ](/Python/Python核心学习.md) * 【 Reactive 】 +* 【 Rust 】 + * [ RustBase ](/Rust/RustBase.md) * 【 Scala 】 * [ SBT ](/Scala/SBT.md) * [ Scala ](/Scala/Scala.md) * 【 Script 】 - * [ Dos ](/Script/Dos.md) + * [ Bash ](/Script/Bash.md) + * [ Bat ](/Script/Bat.md) + * [ Lua ](/Script/Lua.md) * [ ShellLearn ](/Script/ShellLearn.md) + * [ Zsh ](/Script/Zsh.md) * 【 Skills 】 * 【 Skills/Application 】 * [ Editor ](/Skills/Application/Editor.md) * [ VirtualBox ](/Skills/Application/VirtualBox.md) * [ WebBrowser ](/Skills/Application/WebBrowser.md) * 【 Skills/CS 】 - * [ Arithmetic ](/Skills/CS/Arithmetic.md) * [ CharacterEncoding ](/Skills/CS/CharacterEncoding.md) + * [ CompilingPrinciple ](/Skills/CS/CompilingPrinciple.md) * [ Computer ](/Skills/CS/Computer.md) - * [ Concurrent ](/Skills/CS/Concurrent.md) - * [ Network ](/Skills/CS/Network.md) + * [ IO ](/Skills/CS/IO.md) * [ Profile ](/Skills/CS/Profile.md) - * [ WebSecurity ](/Skills/CS/WebSecurity.md) + * [ ProgramThinking ](/Skills/CS/ProgramThinking.md) + * [ Time ](/Skills/CS/Time.md) + * [ Virtualization ](/Skills/CS/Virtualization.md) + * 【 Skills/Cache 】 + * [ Cache ](/Skills/Cache/Cache.md) + * 【 Skills/Councurrency 】 + * [ Concurrency ](/Skills/Councurrency/Concurrency.md) * 【 Skills/DevOps 】 * [ ContinuousDelivery ](/Skills/DevOps/ContinuousDelivery.md) * [ ContinuousDeployment ](/Skills/DevOps/ContinuousDeployment.md) * [ ContinuousIntegration ](/Skills/DevOps/ContinuousIntegration.md) + * [ ELK ](/Skills/DevOps/ELK.md) + * [ Jenkins ](/Skills/DevOps/Jenkins.md) + * [ Percona ](/Skills/DevOps/Percona.md) * 【 Skills/Document 】 + * [ License ](/Skills/Document/License.md) * [ MarkDown ](/Skills/Document/MarkDown.md) * [ RequirementsDocument ](/Skills/Document/RequirementsDocument.md) + * [ UML ](/Skills/Document/UML.md) * 【 Skills/Ecology 】 * [ MSA ](/Skills/Ecology/MSA.md) - * 【 Skills/FrameWork 】 - * [ JavaBoot ](/Skills/FrameWork/JavaBoot.md) + * 【 Skills/Media 】 + * [ PictureFormat ](/Skills/Media/PictureFormat.md) + * 【 Skills/Network 】 + * [ HTTP ](/Skills/Network/HTTP.md) + * [ MITM ](/Skills/Network/MITM.md) + * [ Network ](/Skills/Network/Network.md) + * [ WebSecurity ](/Skills/Network/WebSecurity.md) + * 【 Skills/Search 】 + * [ Elasticsearch ](/Skills/Search/Elasticsearch.md) * 【 Skills/SoftwareEngineering 】 * [ CodeExcellentCode ](/Skills/SoftwareEngineering/CodeExcellentCode.md) * 【 Skills/Spider 】 @@ -235,18 +330,29 @@ * 【 Skills/Test 】 * [ Cucumber ](/Skills/Test/Cucumber.md) * [ Hamcrest ](/Skills/Test/Hamcrest.md) - * [ Junit ](/Skills/Test/Junit.md) - * [ Junit5 ](/Skills/Test/Junit5.md) - * [ TestNG ](/Skills/Test/TestNG.md) * [ TestTheory ](/Skills/Test/TestTheory.md) + * [ WebPerformance ](/Skills/Test/WebPerformance.md) + * 【 Skills/Vcs 】 + * [ GitAction ](/Skills/Vcs/GitAction.md) + * [ GitAdvance ](/Skills/Vcs/GitAdvance.md) + * [ GitBase ](/Skills/Vcs/GitBase.md) + * [ GitTeam ](/Skills/Vcs/GitTeam.md) + * [ Svn ](/Skills/Vcs/Svn.md) + * 【 Skills/Web 】 + * [ FaceBook-GraphQL ](/Skills/Web/FaceBook-GraphQL.md) + * [ RESTful ](/Skills/Web/RESTful.md) + * [ WebAssembly ](/Skills/Web/WebAssembly.md) * 【 Skills/Work 】 + * [ Cooperation ](/Skills/Work/Cooperation.md) + * [ EffectiveWork ](/Skills/Work/EffectiveWork.md) * [ InterviewSkill ](/Skills/Work/InterviewSkill.md) - * [ PomodoroTechnique ](/Skills/Work/PomodoroTechnique.md) - * [ WorkThinking ](/Skills/Work/WorkThinking.md) * [ AppManual ](/Skills/AppManual.md) * [ CelebrityQuotes ](/Skills/CelebrityQuotes.md) - * [ Miscellaneous ](/Skills/Miscellaneous.md) * [ Problem ](/Skills/Problem.md) + * [ ProgrammingParadigm ](/Skills/ProgrammingParadigm.md) + * [ Protobuf ](/Skills/Protobuf.md) * [ RegularExpression ](/Skills/RegularExpression.md) * [ SoftwareDesignEngineer ](/Skills/SoftwareDesignEngineer.md) + * [ Website ](/Skills/Website.md) * 【 Windows 】 + * [ WindowsWithHibernate ](/Windows/WindowsWithHibernate.md) diff --git a/Scala/SBT.md b/Scala/SBT.md index ed31da9..1c1a721 100644 --- a/Scala/SBT.md +++ b/Scala/SBT.md @@ -1,10 +1,29 @@ -`目录 start` - -- [SBT](#sbt) +--- +title: SBT +date: 2018-12-15 12:09:32 +tags: +categories: + - Scala +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [SBT](#sbt) + +**目录 end**|_2020-04-27 23:42_| **************************************** # SBT -> Scala 的构建工具 +> Scala 的构建工具 + +[Official doc](https://www.scala-sbt.org/documentation.html) | [scaladex](https://index.scala-lang.org/)`类似于 MvnRepos` + +> [参考: Building Java Projects With Sbt](http://xerial.org/blog/2014/03/24/sbt/) + +**配置代理** [Official Doc](https://www.scala-sbt.org/1.x/docs/Proxy-Repositories.html) +**`~/.sbt/repositories`** +```ini + [repositories] + local + aliyun: http://maven.aliyun.com/nexus/content/groups/public/ +``` -> [参考博客: Building Java Projects With Sbt](http://xerial.org/blog/2014/03/24/sbt/) \ No newline at end of file diff --git a/Scala/Scala.md b/Scala/Scala.md index a5b5991..054e5d5 100644 --- a/Scala/Scala.md +++ b/Scala/Scala.md @@ -1,25 +1,38 @@ -`目录 start` - -- [Scala](#scala) - - [安装](#安装) - - [基础了解](#基础了解) - - [Scala基础语法](#scala基础语法) - - [适合Scala使用的场景](#适合scala使用的场景) - - [Scala和Java的比较](#scala和java的比较) - - [Scala特性](#scala特性) - - [类型推断](#类型推断) - - [方法](#方法) - - [导入](#导入) - - [循环控制结构](#循环控制结构) - - [函数式编程](#函数式编程) - - [Scala对象模型](#scala对象模型) - - [数据结构和集合](#数据结构和集合) - - [actor](#actor) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Scala +date: 2018-12-15 12:09:46 +tags: + - 基础 +categories: + - Scala +--- + +**目录 start** + +1. [Scala](#scala) + 1. [安装](#安装) + 1. [基础了解](#基础了解) + 1. [基础语法](#基础语法) + 1. [适合Scala使用的场景](#适合scala使用的场景) + 1. [Scala和Java的比较](#scala和java的比较) + 1. [Scala特性](#scala特性) + 1. [类型推断](#类型推断) + 1. [方法](#方法) + 1. [导入](#导入) + 1. [循环控制结构](#循环控制结构) + 1. [函数式编程](#函数式编程) + 1. [Scala对象模型](#scala对象模型) + 1. [数据结构和集合](#数据结构和集合) + 1. [actor](#actor) + +**目录 end**|_2020-04-27 23:42_| **************************************** # Scala +> [Official tour](https://docs.scala-lang.org/tour/tour-of-scala.html) +> [Groovy创始人:Java面临终结 Scala将取而代之](http://developer.51cto.com/art/200907/134785.htm) + +> [参考: 20 Best Scala Books To Go From Beginner To Expert](https://whatpixel.com/best-scala-books/) ## 安装 - 通过sdkman安装,或者下载解压配置环境变量 [sdkman使用](/Skills/usually_app.md) @@ -51,16 +64,15 @@ - 不必声明hello的类型,编译器会自行推断 - 无需声明main方法的返回类型 编译器会自动设为 Unit 等价于Java中的void - 和Java Groovy不一样的是,变量的类型在变量之后 -- 方括号 [] 表示泛型,所以类型参数的表示方法是Array[String] 不是 String[] +- 方括号 [] 表示泛型,所以类型参数的表示方法是`Array[String]` 不是 String[] - Array是纯正的泛型 - 集合类型必须指明泛型 不能像Java那样声明生类型(指不带类型参数的泛型类或接口。) - - 例如泛型类 Box 创建其参数化类型时要指明类型参数的真实类型 BoxintBox = new Box<>(); + - 例如泛型类 `Box` 创建其参数化类型时要指明类型参数的真实类型 BoxintBox = new Box<>(); - 如果忽略了类型参数,Box rawBox = new Box();则是创建了一个生类型 - 分号绝对是可选的 - val 就相当于Java中的final变量,用于声明一个不可变量 - Scala应用程序的初始入口总是在Object中 - `match表达式` - 最简单的match用法跟Java的switch差不多,但是match表达力更强 ```scala @@ -71,6 +83,7 @@ } println(transFer) ``` + - 从语言的纯粹性来看,Scala语法比Java更清晰,也更正规: - 默认case 不需要另外一个不同的关键字 - 单个case 不会像Java那样进入下一个case,所以也就不需要break @@ -102,24 +115,24 @@ - Scala选择actor机制来实现并发编程。提供了一个异步并发模型,通过在代码单元间传递消息实现并发。这种并发模型比Java提供的基于锁的机制,默认共享的并发模型更易用,不过Scala的底层模型也是JVM ******************************* -## Scala基础语法 +## 基础语法 `运行` -- 可以进入REPL终端,和Python似的 -- 也可以使用`scalac scala`就像`javac java`先进行编译然后再运行字节码 -- 或者`Scala 文件`解释运行 +1. 可以进入REPL终端,和Python类似 +1. 也可以使用`scalac scala`就像`javac java`先进行编译然后再运行字节码 +1. 或者`scala 文件`解释运行 `包` -- 第一种方法和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。 比如: -```scala -package com.runoob -class HelloWorld -``` -- 第二种方法有些类似 C#,如: -```scala -package com.runoob { - class HelloWorld -} -``` +1. 第一种方法和 Java 一样,在文件的头定义包名,这种方法就后续所有代码都放在该包中。 比如: + ```scala + package com.runoob + class HelloWorld + ``` +1. 第二种方法有些类似 C#,如: + ```scala + package com.runoob { + class HelloWorld + } + ``` `Scala数据类型` |数据类型|描述| diff --git a/Script/Bash.md b/Script/Bash.md new file mode 100644 index 0000000..1551154 --- /dev/null +++ b/Script/Bash.md @@ -0,0 +1,26 @@ +--- +title: Bash +date: 2018-12-15 12:10:18 +tags: + - Shell +categories: + - Linux +--- + +💠 + +- 1. [Bash](#bash) + +💠 2024-05-01 14:34:46 +**************************************** + +# Bash + +- 数组使用 字符串空格即是 `list='a b c'; for i in $list; do echo $i; done` + +> [参考: bash实现“多进程”](http://www.cnitblog.com/sysop/archive/2008/11/03/50974.aspx) + +[Bash字符串操作](https://www.cnblogs.com/chengmo/archive/2010/10/02/1841355.html) + +> [Bash](https://github.com/skywind3000/awesome-cheatsheets/blob/master/languages/bash.sh) + diff --git a/Script/Dos.md b/Script/Bat.md similarity index 69% rename from Script/Dos.md rename to Script/Bat.md index 2a2067f..15b63e0 100644 --- a/Script/Dos.md +++ b/Script/Bat.md @@ -1,17 +1,26 @@ -`目录 start` - -- [DOS批处理](#dos批处理) - - [Tips](#tips) - - [基础命令](#基础命令) - - [常用基础](#常用基础) - - [基础语法](#基础语法) - - [变量](#变量) - - [服务](#服务) - - [实用性脚本](#实用性脚本) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Dos +date: 2018-12-15 12:10:06 +tags: + - DOS +categories: + - Windows +--- + +**目录 start** + +1. [Bat](#bat) + 1. [Tips](#tips) + 1. [基础命令](#基础命令) + 1. [常用基础](#常用基础) + 1. [基础语法](#基础语法) + 1. [变量](#变量) + 1. [服务](#服务) + 1. [实用性脚本](#实用性脚本) + +**目录 end**|_2020-04-27 23:42_| **************************************** -# DOS批处理 +# Bat ## Tips - windows下使用VmWare创建的虚拟机如果报错 @@ -43,4 +52,4 @@ netstat -ano :查看端口占用 ******************************************* ## 实用性脚本 - [本人写的bat](https://github.com/Kuangcp/Script/tree/master/bat)`因为后期转用Linux了,所以就没有写了` - - 其中用的多的就是服务管理的脚本 \ No newline at end of file + - 其中用的多的就是服务管理的脚本 diff --git a/Script/Lua.md b/Script/Lua.md new file mode 100644 index 0000000..519d139 --- /dev/null +++ b/Script/Lua.md @@ -0,0 +1,40 @@ +--- +title: Lua +date: 2019-02-27 09:54:04 +tags: +categories: + - Lua +--- + +💠 + +- 1. [Lua](#lua) + - 1.1. [安装](#安装) + - 1.2. [luarocks](#luarocks) + +💠 2024-09-04 13:54:11 +**************************************** +# Lua +> [Official ](https://www.lua.org/) | [wiki: lua](https://en.wikipedia.org/wiki/Lua) + +> [Lua 菜鸟教程](http://www.runoob.com/lua/lua-tutorial.html) +> [lua-in-one-pic](https://github.com/coodict/lua-in-one-pic) + +因为该项目 [z.lua](https://github.com/skywind3000/z.lua) 而安装了Lua, 他的执行速度确实非常快 + +**使用场景** +- 网络领域 Redis,Nginx 等扩展 +- 游戏开发 动态扩展 +- 脚本开发 +- 数据库 触发器和存储过程 + +## 安装 +> [Official: install](https://www.lua.org/start.html#installing) + +`lua` + +`luac` +> [doc: luac](https://www.lua.org/manual/5.3/luac.html) + +## luarocks +> [Official Site](https://luarocks.org/#quick-start) lua 的包管理 diff --git a/Script/ShellLearn.md b/Script/ShellLearn.md index d2db615..b422a88 100644 --- a/Script/ShellLearn.md +++ b/Script/ShellLearn.md @@ -1,131 +1,154 @@ -`目录 start` - -- [学习Shell](#学习shell) - - [shell类别](#shell类别) - - [Shell内建命令](#shell内建命令) - - [Tips](#tips) - - [执行](#执行) - - [输入输出](#输入输出) - - [输入](#输入) - - [输出](#输出) - - [彩色输出](#彩色输出) - - [变量](#变量) - - [变量作用域](#变量作用域) - - [嵌套](#嵌套) - - [数据类型](#数据类型) - - [整型](#整型) - - [字符串](#字符串) - - [数组](#数组) - - [结构](#结构) - - [参数读取](#参数读取) - - [判断](#判断) - - [if](#if) - - [case](#case) - - [循环](#循环) - - [函数](#函数) - - [配置文件](#配置文件) - - [脚本的参数自动补全](#脚本的参数自动补全) - - [Bash](#bash) - - [Zsh](#zsh) - - [常用模块](#常用模块) - - [时间](#时间) - - [工具](#工具) - - [shyaml](#shyaml) - -`目录 end` |_2018-08-29_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Shell学习 +date: 2018-12-15 12:10:46 +tags: + - Shell +categories: + - Linux +--- + +💠 + +- 1. [学习Shell](#学习shell) + - 1.1. [shell类别](#shell类别) + - 1.2. [执行方式](#执行方式) +- 2. [基础结构](#基础结构) + - 2.1. [输入输出](#输入输出) + - 2.1.1. [输入](#输入) + - 2.1.2. [输出](#输出) + - 2.2. [变量](#变量) + - 2.2.1. [变量作用域](#变量作用域) + - 2.2.2. [嵌套](#嵌套) + - 2.3. [数据类型](#数据类型) + - 2.3.1. [整型](#整型) + - 2.3.2. [字符串](#字符串) + - 2.3.3. [数组](#数组) + - 2.4. [结构](#结构) + - 2.4.1. [传递参数](#传递参数) + - 2.4.2. [判断](#判断) + - 2.4.2.1. [if](#if) + - 2.4.2.2. [case](#case) + - 2.4.3. [循环](#循环) + - 2.5. [函数](#函数) + - 2.6. [多线程](#多线程) + - 2.7. [定时执行](#定时执行) + - 2.7.1. [watch](#watch) + - 2.7.2. [sleep](#sleep) +- 3. [集成](#集成) + - 3.1. [文件处理](#文件处理) + - 3.1.1. [配置文件](#配置文件) + - 3.1.1.1. [ini和conf](#ini和conf) + - 3.1.2. [shyaml](#shyaml) + - 3.2. [脚本的参数自动补全](#脚本的参数自动补全) + - 3.2.1. [Bash](#bash) + - 3.2.2. [Zsh](#zsh) +- 4. [Tips](#tips) + - 4.1. [常用代码片段](#常用代码片段) + +💠 2024-09-20 11:10:09 **************************************** # 学习Shell -> 首先语法不像别的语言可读性好,比如Python,然后方言众多,学习比Python2,3还恶心 > [Shell 编程之语法基础](https://linuxtoy.org/archives/shell-programming-basic.html) | [Shell 编程之执行过程](https://linuxtoy.org/archives/shell-programming-execute.html) -> [Shell 教程](http://www.runoob.com/linux/linux-shell.html) + +> [菜鸟教程: Shell 教程](http://www.runoob.com/linux/linux-shell.html) +> [C语言中文网: Shell教程](http://c.biancheng.net/shell/) + +> [参考: 编写 Bash Shell 脚本的最佳实践](https://blog.mythsman.com/post/5d2ab67ff678ba2eb3bd346f/) + +************************ + +> [shellcheck](https://github.com/koalaman/shellcheck)`Shell语法检测` +> [cmd-wrapped](https://github.com/YiNNx/cmd-wrapped)`统计命令执行历史` ## shell类别 +> 切换默认shell `chsh -s /bin/zsh` + - sh - - 大多Linux都支持的shell类别 + - 大多Linux都支持的shell类别 - bash - zsh - - 十分现代化 [配置oh my zsh](https://segmentfault.com/a/1190000004695131) + - 高扩展性 [配置oh my zsh](https://segmentfault.com/a/1190000004695131) - dash - - 它主要是为了执行脚本而出现,而不是交互,它速度更快,但功能相比bash要少很多,语法严格遵守POSIX标准 - - 速度确实要快,输入上的交互确实交互不了 + - 它主要是为了执行脚本而出现,而不是交互,它速度更快,但功能相比bash要少很多,语法严格遵守POSIX标准 + - 速度确实要快,输入上的交互确实交互不了 - fish + - 交互式的, 补全功能比较好 -> [linux shell dash&bash](http://blog.csdn.net/zengqiang1/article/details/61916697) - -***************** -## Shell内建命令 -- [ ] 学习内建命令的使用 - -**************** -## Tips -> 常用代码片段 - -1. 获取当前shell脚本的绝对路径 ```basepath=$(cd `dirname $0`; pwd)``` -1. 命令嵌套 只要在 命令中用 两个反引号 `` 将子命令包住即可 -1. 检查当前用户为Root用户 -```sh - if [ $(id -u) != "0" ]; then - printf $red"Please use root to run this script\n"$end - exit 1 - fi -``` -1. kill 脚本进程 -```sh - id=`ps -ef | grep "WithRedis.py" | grep -v "grep" | grep -v "\-d" | awk '{print $2}'` - if [ "${id}1" = "1" ];then - printf $red"not exist background running script\n"$end - else - kill -9 $id - fi -``` +> [参考: 常见shell类型](http://www.cnblogs.com/happycxz/p/7840077.html) +> [Github: zsh guide](https://github.com/goreliu/zshguide) ******************* -## 执行 + +## 执行方式 - [source命令](http://blog.csdn.net/xiaolang85/article/details/7861441) | [点和source命令](http://www.cnblogs.com/my_life/articles/4323528.html) - 文件头部 `#!/bin/sh`表示要使用sh解释器来执行, 可以替换成bash dash - - 只要该文件具有执行权限就可以直接运行了 `./a.sh` 或者绝对路径 + - 只要该文件具有执行权限就可以直接运行了 `./a.sh` 或者绝对路径 + +- [pueue](https://github.com/Nukesor/pueue)`shell后台执行队列` ********************** +# 基础结构 ## 输入输出 ### 输入 - `read answer` -### 输出 -echo printf +处理管道的输入也是使用 read +```sh +while read line; do + echo $line +done +``` -#### 彩色输出 -> [参考博客,比较详细](http://blog.csdn.net/magiclyj/article/details/72637666) +- `select` ```sh - red='\033[0;31m' - green='\033[0;32m' - yellow='\033[0;33m' - blue='\033[0;34m' - purple='\033[0;35m' - cyan='\033[0;36m' - white='\033[0;37m' - default='\033[0m' + echo "What is your favourite OS?" + select var in "Linux" "Gnu Hurd" "Free BSD" "Other"; do + break; + done + echo "You have selected $var" ``` +### 输出 +echo printf + +> printf +1. 原样输出字符串: + - printf("%s", str); +2. 输出指定长度的字符串, 超长时不截断, 不足时右对齐: + - printf("%Ns", str); N 为指定长度的10进制数值 +3. 输出指定长度的字符串, 超长时不截断, 不足时左对齐: + - printf("%-Ns", str); N 为指定长度的10进制数值 +4. 输出指定长度的字符串, 超长时截断, 不足时右对齐: + - printf("%N.Ms", str); N 为最终的字符串输出长度 M 为从参数字符串中取出的子串长度 +5. 输出指定长度的字符串, 超长时截断, 不足时左对齐是: + - printf("%-N.Ms", str); N 为最终的字符串输出长度 M 为从参数字符串中取出的子串长度 +6. 上述N,M是可以动态指定的,方法是用*代替M或者N,然后在参数列表里加上一个数字参数。 + - `printf("%-*.*s", 5, 2, "123");` 等价于 `printf("%-5.2s", "123");` + - `printf("%*s", 5, "123");` `printf("%5s", "123");` ****************** ## 变量 +- 获取命令输出作为变量 + - `$(ls)` + - ``` `ls` ``` + ### 变量作用域 -> 比Python的作用域更加恶心 +> 比Python的作用域更加宽泛和不可控,引用一个变量时需要注意值的来源。 ### 嵌套 ```sh - # 实现了读取 A_host变量的值 - perfix='A_' - name=${perfix}host - host=${!name} + # 实现了读取 A_host变量的值 + perfix='A_' + name=${perfix}host + host=${!name} ``` > [shell将变量当命令执行问题](http://www.bitscn.com/os/linux/201505/506409.html) -1. `${command}` -2. `echo ${command}|awk '{run=$0;system(run)}'` 最好 +1. `${command}` 执行 command变量 内容 + - `echo ${command} |awk '{run=$0;system(run)}'` 推荐方式 ***************** ## 数据类型 @@ -157,22 +180,27 @@ _判断变量是否为数值_ echo 'no.' fi ``` -************ + +************************ + ### 字符串 - [字符串截取](https://www.2cto.com/os/201305/208219.html) | [Blog:变量字符串截取](http://www.jb51.net/article/56563.htm) | [Shell正则](http://man.linuxde.net/docs/shell_regex.html) +> [字符串操作](https://www.cnblogs.com/gaochsh/p/6901809.html) -```sh - ${varible##*string} 从左向右截取最后一个string后的字符串 - ${varible#*string} 从左向右截取第一个string后的字符串 - ${varible%%string*} 从右向左截取最后一个string后的字符串 - ${varible%string*} 从右向左截取第一个string后的字符串 - - vars=${vars%%#*} # 截取#左边 - vars=${vars#*cd} # 截取最左的cd的右边 - vars=${vars%\'*} # 截取 右边引号 之左 -``` +| Pattern | 描述 || +|:----|:----|:---| +| `${varible#*str}` | 截取 `首个` |str `右` 的字符串 | +| `${varible##*str}` | 截取 `最末` |str `右` 的字符串 | +| `${varible%%str*}` | 截取 `首个` |str `左` 的字符串 | +| `${varible%str*}` | 截取 `最末` |str `左` 的字符串 | + +1. `${varible:start:end}` 定长截取 + - `${varible:4}` 第四个字符到结束 +1. `ls -al | cut -d "." -f 2` 取常规文件后缀名 + +************************ -`获取命令的输出` +**`获取命令的输出`** - 使用 保存结果的变量名=`需要执行的linux命令` 这种方式来获取命令的输出时,注意的情况总结如下: - 1)保证反单引号内的命令执行时成功的,也就是所命令执行后$?的输出必须是0,否则获取不到命令的输出 - 2)即便是命令的返回值是0,也需要保证结果是通过标准输出来输出的,而不是标准错误输出,否则需要重定向 @@ -194,7 +222,7 @@ _判断变量是否为数值_ echo $x } ``` -_求长_ `${#var}` +_求长_ ```${#var}``` _字符串拆分成数组_ > [修改分隔符](http://www.cnblogs.com/FlyFive/p/3640243.html) | [三种方法概述](https://blog.csdn.net/bitcarmanlee/article/details/50973454) @@ -203,21 +231,28 @@ _字符串拆分成数组_ - 直接 `for element in $target` ************************ + ### 数组 -********************* -## 结构 -### 参数读取 -> [参考博客](http://www.cnblogs.com/FrankTan/archive/2010/03/01/1634516.html) `命令行选项 参数处理` +> [shell处理时间格式](http://blog.csdn.net/superbfly/article/details/52453334) +************************ -- 只是 $1 $2 .... - - 脚本退出运行 `exit 0` +## 结构 -> 得到脚本绝对路径; 如果只是执行 pwd 只是得到执行脚本时的当前绝对路径而已 -```sh -basepath=$(cd \`dirname $0\`; pwd) -``` +### 传递参数 +> [参考博客](http://www.cnblogs.com/FrankTan/archive/2010/03/01/1634516.html) `命令行选项 参数处理` +| 参数 | 说明 | +|:----:|:----| +| `$#` | 传递到脚本的参数个数 +| `$*` | 以一个单字符串显示所有向脚本传递的参数。以"$1 $2 … $n"的形式输出所有参数。 +| `$$` | 脚本运行的当前进程ID号 +| `$!` | 后台运行的最后一个进程的ID号 +| `$@` | 与$*相同,但是使用时加引号 以"$1" "$2" … "$n" 的形式输出所有参数。 +| `$-` | 显示Shell使用的当前选项,与set命令功能相同。 +| `$?` | 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 + +> 读取脚本参数 ```sh # 1. 简单的方式 case $1 in @@ -228,6 +263,7 @@ basepath=$(cd \`dirname $0\`; pwd) echo "default" ;; esac + # 2. 规范化的参数 while getopts "hup:" opt; do case "$opt" in @@ -244,14 +280,20 @@ basepath=$(cd \`dirname $0\`; pwd) esac done ``` + ### 判断 #### if - [参考博客](http://www.cnblogs.com/276815076/archive/2011/10/30/2229286.html) +注意方括号格式时,内部要留一个空格,否则会报 语法错误 + _判断文件_ -- 文件 `if [ -f path ]` -- 链接 `if [ -L path ]` -- 目录 `if [ -d path ]` +- 文件存在 `if [ -e path ]` + - -r 可读 + - -f 存在,而且是普通文件 + - -s 存在且文件大小大于0字节 +- 链接存在 `if [ -L path ]` +- 目录存在 `if [ -d path ]` - 整数比较 - `-eq` 等于,如:if [ "$a" -eq "$b" ] @@ -262,6 +304,16 @@ _判断文件_ - `-le` 小于等于,如:if [ "$a" -le "$b" ] - `大于` (需要双括号),如:(("$a" > "$b")) - `>= `大于等于(需要双括号),如:(("$a" >= "$b")) + +- 字符串比较 + - `if [ "$root"x = 'x' ]` 判断是否为空 + - `if test -z "$repo_path";` + - `if [ $(echo $str | grep -e '$str1') ]` 判断包含 + - `if [[ $a == z* ]]` 模式匹配,但是不是严格正则表达式格式 + +- 判断数组包含 `if [[ "${ary[@]}" =~ "$a" ]]` + - 其中 ary=(1 2 3); a=2; + #### case ```sh @@ -277,12 +329,18 @@ _判断文件_ ### 循环 - [参考博客](http://www.cnblogs.com/fhefh/archive/2011/04/15/2017233.html) -`简易循环` +> for ```sh - for j in $(seq 1 5); do - echo $j + for i in $(seq 1 5); do + echo $i + done + + for (( a=0; a<10; a++)) do + echo $a; done ``` + +> while ```sh i=1 while [ "$i" -le 10 ];do @@ -290,22 +348,130 @@ _判断文件_ i=$(($i+1)) done ``` + +> 逐行遍历命令的输出 +```sh + while read -r proc; do + #do work + done <<< "$(ps -ewo pid,cmd,etime | grep python | grep -v grep | grep -v sh)" +``` + +> Tips +1. break continue: break能跳出多层循环,只需带上数字 `break num`,也可以理解为无参的break默认带了1参数 +1. done 后面可以重定向,将循环的输出转到文件而不是终端: `for do done > loop.log` + ***************** + ## 函数 > Shell的函数只能返回整型数据类型 +- 定义函数 + - `function a {}` + - `a(){}` + - 注意: + - Shell是解释执行,必须先定义,然后调用,而且函数没有重载,只覆盖 + - 命令行内定义函数: `function hi { echo hi;};` 注意左括号和命令的空格,以及命令;结尾 + ```sh - simple(){ - echo "simple" - } + simple(){ + echo "simple" + } + + zero(){ + return 0 + } + # 函数退出状态码(0-255) + echo "return "$? + + # 使用输出返回值 + result=$(simple) ``` +- 引用 shell 文件 `source shell文件相对路径` source可以简写为 `.` + +## 多线程 +> [参考: shell如何实现多线程](https://www.cnblogs.com/signjing/p/7074778.html) + +************************ + +## 定时执行 +### watch +> execute a program periodically, showing output fullscreen + +- -d 高亮差异数据 + +watch 等待命令对应进程执行完成后才进入计时到下一个周期执行,可以利用这个特性来执行异步shell + +> demo.sh +```sh +for i in $(seq 1 100); do + doSomething & +done +``` + +watch demo.sh 达到的效果为:等到sh中的100个子进程执行结束后,主进程退出,才会等2s再执行一次demo.sh + +### sleep + ********************** -## 配置文件 -> [参考博客](http://blog.csdn.net/xinfuqizao/article/details/21812003) +# 集成 +## 文件处理 +> 读取当前目录文件 + +```bash + for file in ./* + do + if test -f "$file" + then + echo "$file 是文件" + fi + if test -d "$file" + then + echo "$file 是目录" + fi + done +``` + +> 读取文件行 +```bash + # 1 + while IFS= read -r -u3 line; do + echo "$line" + done 3< "$2" + + # 2 + cat a.log | while read line; do + echo "line: "$line; + done +``` + +1. 当前目录创建临时文件,并输出创建的文件名 `mktmp data.XXXXXX` + - `-t` 在 /tmp/目录下创建,并返回全路径 + - `-d` 创建目录 + +1. 输出到终端并写入文件 `echo "test" | tee a.log` +1. 基于模板快速创建多份配置文件 + ```sh + REPLICA=01 SHARD=01 envsubst < config.xml > clickhouse01/config.xml + REPLICA=02 SHARD=01 envsubst < config.xml > clickhouse02/config.xml + ``` + - config.xml 中使用`${}`做占位符 例如: `clickhouse${REPLICA}` + +### 配置文件 + +#### ini和conf +```conf + [block] + name=myth +``` +- 如果没有 `[block]` 这样的声明就可以当sh用, 直接 source file 就加载了配置内容 + +### shyaml +> [参考](https://linuxtoy.org/archives/shyaml.html) ******************** + ## 脚本的参数自动补全 -> [参考博客: 命令行自动补全原理 ](http://www.cnblogs.com/wang_yb/p/5969451.html) +> [参考: 命令行自动补全原理 ](http://www.cnblogs.com/wang_yb/p/5969451.html) ### Bash @@ -314,14 +480,35 @@ _判断文件_ 学习怎么使用的话, 可以看上面的博客(虽然有点简陋), 但是如果是 oh-my-zsh 的用户, 可以直接看别人的插件, 模仿就行了, 例如 redis-cli 插件的自动补全, 就很简单直接 1. `#compdef redis-cli rec` 这第一行很重要, 定义了是对哪个命令或脚本的自动补全 -***************** -## 常用模块 -### 时间 -> [shell处理时间格式](http://blog.csdn.net/superbfly/article/details/52453334) -********************************* -## 工具 -> [更多工具](/Skills/Soft_Manual.md#终端工具) -### shyaml -> [参考](https://linuxtoy.org/archives/shyaml.html) +************************ + +# Tips +> [hyperfine](https://github.com/sharkdp/hyperfine)命令压测工具 + +- set -x 开启调试 每条实际执行的命令都会输出到控制台 + - set +x 关闭调试 +## 常用代码片段 + +1. 获取命名或函数标准输出: 反引号 **\`cmd\`** 或者 **$(cmd)** +1. 检查当前用户为Root用户 + ```sh + if [ $(id -u) != "0" ]; then + printf $red"Please use root to run this script\n"$end + exit 1 + fi + ``` +1. kill 脚本进程 + ```sh + id=`ps -ef | grep "WithRedis.py" | grep -v "grep" | grep -v "\-d" | awk '{print $2}'` + if [ "${id}1" = "1" ];then + printf $red"not exist background running script\n"$end + else + kill -9 $id + fi + ``` +1. 得到脚本绝对路径; `如果脚本内执行 pwd 只会得到执行脚本时会话的绝对路径,而不是脚本的路径` + ```sh + basepath=$(cd \`dirname $0\`; pwd) + ``` diff --git a/Script/Zsh.md b/Script/Zsh.md new file mode 100644 index 0000000..30724d3 --- /dev/null +++ b/Script/Zsh.md @@ -0,0 +1,113 @@ +--- +title: Zsh +date: 2018-12-15 12:06:10 +tags: + - Shell +categories: + - Linux +--- + +💠 + +- 1. [Zsh](#zsh) + - 1.1. [为什么要使用](#为什么要使用) + - 1.2. [Tips](#tips) +- 2. [基础结构](#基础结构) +- 3. [oh-my-zsh](#oh-my-zsh) + - 3.1. [插件](#插件) + - 3.2. [主题](#主题) + - 3.2.1. [自己定制](#自己定制) + +💠 2024-09-14 11:51:16 +**************************************** +# Zsh +> [arch zsh wiki](https://wiki.archlinux.org/index.php/Zsh) + +## 为什么要使用 +> [mac 装了 oh my zsh 后比用 bash 具体好在哪儿?](https://www.zhihu.com/question/29977255) +> [终极 Shell——ZSH](https://zhuanlan.zhihu.com/mactalk/19556676) + +> [某人的配置](https://github.com/lilydjwg/dotzsh) + +- [Zsh 开发指南](https://www.zhihu.com/column/zshguide) + +- 提供较为强大的历史命令match功能 +- 较为丰富的扩展实现 +- 更现代化的支持 更多语言,unicode字符 + +## Tips + +- 数组使用 `list=(a b c); for i in $list; do echo $i; done` + +# 基础结构 +************************ + +# oh-my-zsh +> [Github](https://github.com/robbyrussell/oh-my-zsh) +> [关于PS1环境变量的折腾](https://gitee.com/kcp1104/codes/gca14wtqvm67l9j5r0deb56#Zsh.md) `因为含特殊字符GitBook构建通不过,只能放出去了` + +1. 安装好 zsh wget git +2. `sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"` +3. `vi ~/.zshrc` 进行配置 + +``` + plugins=( + git mvn docker + ) +``` + +## 插件 +> [wiki: plugins](https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins) +> [zsh oh-my-zsh 插件推荐 ](https://hufangyun.com/2017/zsh-plugin/) + +- 个人常用 git gitfast mvn gradle golang docker kubectl sudo colored-man-pages + +********************** + +## 主题 +> [官网主题列表](https://github.com/robbyrussell/oh-my-zsh/wiki/Themes) + +- 自带主题: amuse clean wedisagree, muse也还好,就是没时间 + +> [额外主题列表](https://github.com/robbyrussell/oh-my-zsh/wiki/External-themes) + +- `推荐` powerlevel10k **性能强劲,交互式配置** + - [Github](https://github.com/romkatv/powerlevel10k) + - install nerd-fonts-meslo-lg + +- powerlevel9k + - [Github Doc](https://github.com/bhilburn/powerlevel9k/wiki/Install-Instructions#option-2-install-for-oh-my-zsh) + - `git clone https://github.com/bhilburn/powerlevel9k.git ~/.oh-my-zsh/custom/themes/powerlevel9k` + - `powerlevel9k/powerlevel9k` + +- Bullet Train `桌面在用 bullet-train` + - [Github repo](https://github.com/caiogondim/bullet-train.zsh) | [必需的符号字体](https://github.com/powerline/powerline) + - Source Code Pro for Powerline + Powerline + Awesonme 的 Bold 字体搭配最合适 + +```sh + wget https://raw.githubusercontent.com/caiogondim/bullet-train.zsh/master/bullet-train.zsh-theme -O /home/kcp/.oh-my-zsh/custom/themes + + wget https://github.com/powerline/powerline/raw/develop/font/PowerlineSymbols.otf + wget https://github.com/powerline/powerline/raw/develop/font/10-powerline-symbols.conf + mv PowerlineSymbols.otf ~/.local/share/fonts/ + fc-cache -vf ~/.local/share/fonts/ + mv 10-powerline-symbols.conf ~/.config/fontconfig/conf.d/ +``` + +- Maglev `Tmux 主题 和上面的Zsh主题搭配使用` + - [Github地址](https://github.com/caiogondim/maglev) + +- spaceship + - [地址](https://www.ctolib.com/denysdovhan-spaceship-zsh-theme.html) + +> [安装步骤](https://github.com/caiogondim/bullet-train.zsh#for-oh-my-zsh-users) +1. mkdir $ZSH_CUSTOM/themes/ +2. wget http://raw.github.com/caiogondim/bullet-train-oh-my-zsh-theme/master/bullet-train.zsh-theme +3. config .zshrc to `ZSH_THEME="bullet-train" ` + +### 自己定制 +> [Github doc](https://github.com/robbyrussell/oh-my-zsh/wiki/Customization) + +`基于muse的主题` 用在服务器上挺好 +> ~/.oh-my-zsh/custom/themes/muse-myth.zsh-theme [源码](https://github.com/Kuangcp/Script/blob/master/zsh/themes/muse-mythos.zsh-theme) + diff --git a/Skills/AppManual.md b/Skills/AppManual.md deleted file mode 100644 index a1096ed..0000000 --- a/Skills/AppManual.md +++ /dev/null @@ -1,161 +0,0 @@ -`目录 start` - -- [软件使用记事](#软件使用记事) - - [【包管理】](#包管理) - - [使用sdkman](#使用sdkman) - - [【服务管理】](#服务管理) - - [oneinstack](#oneinstack) -- [【常用工具】](#常用工具) - - [网络工具](#网络工具) - - [nmap](#nmap) - - [apache benchmark](#apache-benchmark) - - [日常工具](#日常工具) - - [百度网盘](#百度网盘) - - [输入法](#输入法) - - [搜狗输入法](#搜狗输入法) - - [rime](#rime) - - [小小输入法](#小小输入法) - - [qgit](#qgit) - - [convert](#convert) - - [todo.txt](#todotxt) - - [todo.txt-cli](#todotxt-cli) - - [【IDE】](#ide) - - [Idea](#idea) - - [eclipse](#eclipse) - - [绘图工具](#绘图工具) - - [在线版](#在线版) - - [安装版](#安装版) - - [安全工具](#安全工具) - - [gpg](#gpg) - -`目录 end` |_2018-09-01_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 软件使用记事 -## 【包管理】 -### 使用sdkman -> 但是总会莫名其妙的冒出问题,sdk命令掉线始终连不上网,终端打开巨慢 - -`安装` -- 安装sdkman `curl -s "https://get.sdkman.io" | bash` 遇到提示zip 就是需要安装zip `sudo apt install zip` 然后重新执行命令 -- 执行脚本:`source "/home/kuang/.sdkman/bin/sdkman-init.sh"` 或者重启终端就可以使用了,查看sdkman 版本:`sdk version` -`使用` -- [官网文档](http://sdkman.io/usage.html) -- 查看所有 `sdk list` - - 查看某sdk的版本 `sdk list java ` -- 不指定版本则默认安装最新版 `sdk install java` 安装指定版本 `sdk default java 8u131-zulu` -- 开始使用指定版本(for the current shell only) `sdk use scala 2.12.1` -- 查看当前版本 `sdk current java` -- 验证是否成功:`java -version` -- 移除 `sdk uninstall scala 2.11.6` - -****************** -## 【服务管理】 -### oneinstack -> 一键配置环境 [官方文档](https://oneinstack.com/install/) - -![配图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Linux/install_oneinstack.png) -- `apt -y install wget screen curl python` -- 下载源码: - - `wget http://aliyun-oss.linuxeye.com/oneinstack-full.tar.gz` #阿里云经典网络下载 - - `wget http://mirrors.linuxeye.com/oneinstack-full.tar.gz` #包含源码,国内外均可下载 - - `wget http://mirrors.linuxeye.com/oneinstack.tar.gz` #不包含源码,建议仅国外主机下载 -- `tar xzf oneinstack-full.tar.gz` -- `cd oneinstack` #如果需要修改目录(安装、数据存储、Nginx日志),请修改options.conf文件 -- `screen -S oneinstack` #如果网路出现中断,可以执行命令`screen -R oneinstack`重新连接安装窗口 -- `sudo ./install.sh` #注:请勿sh install.sh或者bash install.sh这样执行 - -****************** -# 【常用工具】 -> 基本是Linux工具,因为主力是用Linux - -## 网络工具 -### nmap -> 端口扫描 [参考博客](http://aaaxiang000.blog.163.com/blog/static/2063491220113284325531/) - -- 扫描`nmap IP` - - -sP - - -sT - - -sR - - -n `最简单直接的参数` - -### apache benchmark -> 压力测试工具 - -- 测试本机超过100连接报错 104: - - [Blog:解决问题](http://www.cnblogs.com/archoncap/p/5883723.html) - -************************************ -## 日常工具 -### 百度网盘 -- [百度网盘命令客户端](https://github.com/iikira/BaiduPCS-Go) `Go语言实现` - -### 输入法 -#### 搜狗输入法 -> 唯一一个大厂支持Linux - -- Ctrl Alt B 显示/关闭 特殊字符面板 - -#### rime -- [rime](http://rime.im/) `用过一下子有莫名其妙的bug就卸载了` - -#### 小小输入法 -[小小输入法在Deepin上的使用](https://bbs.deepin.org/forum.php?mod=viewthread&tid=138500&highlight=%E5%B0%8F%E5%B0%8F%E8%BE%93%E5%85%A5%E6%B3%95) - -### qgit -- git查看仓库的图形化界面 - -*********************************** -### convert -- [参考博客](http://blog.csdn.net/mybelief321/article/details/9969949) -- 将图片转换成指定大小 这是保持比例的 `convert -resize 600X600 src.jpg dst.jpg` 中间是字母X -- 如果不保持比例,就在宽高后加上感叹号 -- 可以只指定高度,那么宽度会等比例缩放 `convert -resize 400 src.jpg dst.jpg` -- 还可以按百分比缩放 - -_批量修改_ -> 如果没有 -path 语句,新生成的 png 文件将会覆盖原始文件 [参考博客](http://www.cnblogs.com/jkmiao/p/6756929.html) - -- `mogrify -path newdir -resize 40X40 *.png` 把png图片全部转成40X40大小并放在新文件夹下 -- `mogrify -path newdir -format png *.gif` 将所有gif转成png放在新目录下 - -### todo.txt -> [官网](http://todotxt.org/) 一个简约的 TODO 软件 - -#### todo.txt-cli -> 终端中的TODO - -- [todo.txt-cli](https://github.com/todotxt/todo.txt-cli) - -****************************** -## 【IDE】 -### Idea -> [更多](/Java/Tool/IDEA.md) - -### eclipse - -【eclipse EE Mars】 -* 这里的Tomcat是使用了你所导入的必要执行文件,但是运行的必要配置文件在eclipse Server项目里另有一份 -* 而且运行时也是使用这份配置文件,这样的结果是可以使用一份Tomcat目录,在eclipse配置运行多个Tomcat -* 但是奇怪的是 访问不了Tomcat主页即:localhost:8080 所以也就不能管理Tomcat 查看运行状态 - -资源下载 archive.eclipse.org/eclipse/downloads/ - -*********************************************** -## 绘图工具 -### 在线版 -- [processon](https://www.processon.com/) - -### 安装版 -***************** -## 安全工具 -### gpg -> [参考博客](http://www.ruanyifeng.com/blog/2013/07/gpg.html) - -常用参数 -``` -gpg --list-key - --gen-key -``` -- 生成的过程, 输入相关的提示信息, 最后输完密码后需要输入随机字符, 就也是按照提示, 但是1.4是正常的, 其他的直接假死,不是很理解这种操作 - - diff --git a/Skills/Application/Android.md b/Skills/Application/Android.md new file mode 100644 index 0000000..cecefb2 --- /dev/null +++ b/Skills/Application/Android.md @@ -0,0 +1,25 @@ +--- +title: Android +date: 2024-04-22 20:04:53 +tags: +categories: +--- + +💠 + +- 1. [Android](#android) + - 1.1. [Dev](#dev) + - 1.2. [Tool](#tool) + +💠 2024-05-06 19:59:21 +**************************************** +# Android + +> [Android,开源还是封闭?](http://www.ruanyifeng.com/blog/2010/02/open_android_or_not.html) + +## Dev +F-Driod +Termux + +## Tool +李跳跳 diff --git a/Skills/Application/AppManual.md b/Skills/Application/AppManual.md new file mode 100644 index 0000000..f5443c9 --- /dev/null +++ b/Skills/Application/AppManual.md @@ -0,0 +1,136 @@ +--- +title: 常用工具手册 +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [软件使用记事](#软件使用记事) + - 1.1. [包管理](#包管理) + - 1.1.1. [sdkman](#sdkman) + - 1.2. [服务管理](#服务管理) + - 1.2.1. [oneinstack](#oneinstack) + - 1.3. [日常工具](#日常工具) + - 1.3.1. [Termux](#termux) + - 1.3.2. [iSH](#ish) + - 1.3.3. [kite](#kite) + - 1.4. [开发调试工具&技巧](#开发调试工具&技巧) + - 1.4.1. [网络](#网络) + - 1.4.2. [Debug](#debug) + - 1.5. [IDE](#ide) + - 1.5.1. [Idea](#idea) + - 1.5.2. [Eclipse](#eclipse) + - 1.6. [绘图工具](#绘图工具) + - 1.6.1. [思维导图](#思维导图) + +💠 2024-01-22 09:40:06 +**************************************** +# 软件使用记事 +## 包管理 +### sdkman + +`安装` +- 安装sdkman `curl -s "https://get.sdkman.io?rcupdate=false" | bash` 依赖 zip unzip curl sed +- 依据提示配置 sdkman-init.sh 或者重启终端就可以使用了 +- 查看sdkman版本 `sdk version` + +`使用` +- [官网文档](http://sdkman.io/usage.html) +- 查看所有 `sdk list` + - 查看某sdk的版本 `sdk list java ` +- 安装最新稳定版 `sdk install java` 安装指定版本 `sdk default java 8u131-zulu` +- 使用指定版本 `sdk use scala 2.12.1` +- 查看当前版本 `sdk current java` +- 验证是否成功 `java -version` +- 移除 `sdk uninstall scala 2.11.6` + +****************** +## 服务管理 +### oneinstack +> 一键配置环境 [官方文档](https://oneinstack.com/install/) + +![配图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Linux/install_oneinstack.png) +- `apt -y install wget screen curl python` +- 下载源码: + - `wget http://aliyun-oss.linuxeye.com/oneinstack-full.tar.gz` #阿里云经典网络下载 + - `wget http://mirrors.linuxeye.com/oneinstack-full.tar.gz` #包含源码,国内外均可下载 + - `wget http://mirrors.linuxeye.com/oneinstack.tar.gz` #不包含源码,建议仅国外主机下载 +- `tar xzf oneinstack-full.tar.gz` +- `cd oneinstack` #如果需要修改目录(安装、数据存储、Nginx日志),请修改options.conf文件 +- `screen -S oneinstack` #如果网路出现中断,可以执行命令`screen -R oneinstack`重新连接安装窗口 +- `sudo ./install.sh` #注:请勿sh install.sh或者bash install.sh这样执行 + +****************** +## 日常工具 +> iOS 相关 + +[a shell](https://holzschu.github.io/a-Shell_iOS/) `iOS13` + +### Termux +> 安卓上安装的Linux模拟器, 几乎完整的运行时,只有Docker等虚拟化不支持,常见命令和开发环境均支持(前提是技术组件支持arm架构指令) +> 只建议 Google Play中安装,通过换机软件导入的运行时,文件及环境变量会有缺失和损坏 + +> [wiki FAQ](https://wiki.termux.com/wiki/FAQ) + +> [参考: Hello,Termux](https://tonybai.com/2017/11/09/hello-termux/) +> [参考: Termux:让Android手机摇身一变成为高级Linux终端](https://www.asmodeus.cn/archives/769) + +- 开启ssh服务 pkg install openssh 对应端口默认 8022 +- 执行 termux-setup-storage 命令,建立常用目录软链接 + +### iSH +> iOS体系内终端模拟器。耗电发热大,且必须前台运行才能保持软件内开启的各种服务线程(ssh,http,tcp等),因为iOS会冻结后台 + +- apk add openssh +- ssh root@ip 方式登录iOS设备 + - 但是 ip只能通过设置中的wifi信息看机子的局域网ip `ifconfig命令在iSH内执行无效` + +### kite +> [Official Site](https://www.kite.com/) +> Free AI Coding Assistant and Code Auto-Complete + +支持 VS Code IDEA Vim 等等 + +************************ +## 开发调试工具&技巧 +### 网络 +- **xswtich** `Chrome插件` + - 浏览器层面通过劫持ajax请求,修改请求的实际地址从而达到访问某网站时指定替换加载的静态资源(js css),或者是替换请求的服务端地址 + - 场景:访问前后端分离的某应用服务,浏览器打开测试环境时,修改配置使前端对测试环境后端发起请求实际被替换请求到本地,方便调试 + - 由于Firefox不兼容,自己基于go开发的替代品 [dev-proxy](https://github.com/Kuangcp/GoBase/tree/master/toolbox/dev-proxy) + +- **Multi-Account-Container** `Firefox 插件` + - 同一网站同时登录多个账号,账号之间cookie等信息会隔离 + +- **potatso** iOS体系内代理软件 iOS 15兼容有问题 + +### Debug + +************************ + + +## IDE +### Idea +> [详细内容](/Java/Tool/IDEA.md) + +### Eclipse +> [详细内容](/Java/Tool/Eclipse.md) + +************************ + +## 绘图工具 +### 思维导图 +> [参考: 这 7 款开源思维导图工具真的很神奇](https://blog.csdn.net/zuochao_2013/article/details/68928381) + +1. [processon](https://www.processon.com/)`免费额度比较小, 但是使用很方便` + +1. [百度脑图在线版](https://github.com/fex-team/kityminder) + - [百度脑图客户端](https://github.com/NaoTu/DesktopNaotu) + - vscode-mindmap 百度脑图VSCode插件版 + +1. [my-mind](https://github.com/ondras/my-mind)`简单轻巧` +1. FreeMind +1. [freeplane](https://github.com/freeplane/freeplane)`Java编写的, FreeMind衍生` +1. XMind 收费,占用资源大, 文件是二进制,不方便做版本控制 diff --git a/Skills/Application/Editor.md b/Skills/Application/Editor.md index d458952..f4fc078 100644 --- a/Skills/Application/Editor.md +++ b/Skills/Application/Editor.md @@ -1,47 +1,62 @@ -`目录 start` - -- [文本编辑器](#文本编辑器) - - [Ghex](#ghex) - - [Kate/KWrite(Kate的轻量版)](#katekwritekate的轻量版) - - [Geany](#geany) - - [scite](#scite) - - [textadept](#textadept) - - [快捷键](#快捷键) - - [Sublime](#sublime) - - [快捷键](#快捷键) - - [crack](#crack) - - [VSCode](#vscode) - - [快捷键](#快捷键) - - [代码片段](#代码片段) - - [插件](#插件) - - [实践](#实践) - - [Atom](#atom) - - [Gedit](#gedit) - - [小书匠](#小书匠) - - [Moeditor|Typora|CuteMarkEd](#moeditor|typora|cutemarked) - - [Cmd](#cmd) -- [终端中的文本编辑器](#终端中的文本编辑器) - - [Vi/Vim](#vivim) - - [Nano](#nano) - - [fte-terminal](#fte-terminal) - - [在线编辑器](#在线编辑器) - -`目录 end` |_2018-09-01_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 编辑器 +date: 2018-12-15 12:11:10 +tags: + - 编辑器 +categories: + - 工具 +--- + +💠 + +- 1. [文本编辑器](#文本编辑器) + - 1.1. [Kate](#kate) + - 1.2. [Geany](#geany) + - 1.3. [scite](#scite) + - 1.4. [textadept](#textadept) + - 1.4.1. [快捷键](#快捷键) + - 1.5. [Sublime](#sublime) + - 1.5.1. [快捷键](#快捷键) + - 1.5.2. [crack](#crack) + - 1.6. [VSCode](#vscode) + - 1.6.1. [快捷键](#快捷键) + - 1.6.2. [代码片段](#代码片段) + - 1.6.3. [插件](#插件) + - 1.6.4. [实践](#实践) + - 1.6.5. [vscode server](#vscode-server) + - 1.7. [Atom](#atom) + - 1.8. [Gedit](#gedit) + - 1.9. [notepadqq](#notepadqq) + - 1.10. [MousePad](#mousepad) + - 1.11. [Xed](#xed) + - 1.12. [小书匠](#小书匠) + - 1.13. [Moeditor/Typora/CuteMarkEd](#moeditortyporacutemarked) +- 2. [终端中的文本编辑器](#终端中的文本编辑器) + - 2.1. [Vi/Vim](#vivim) + - 2.2. [helix](#helix) + - 2.3. [Nano](#nano) + - 2.4. [Micro](#micro) + - 2.5. [BS在线编辑器](#bs在线编辑器) +- 3. [十六进制 Hex](#十六进制-hex) + +💠 2024-09-06 11:36:43 **************************************** # 文本编辑器 -## Ghex -- 十六进制文件编辑器 -************************************ -## Kate/KWrite(Kate的轻量版) +## Kate +> [Official site](https://kate-editor.org/) 相关: KWrite(Kate的轻量版) + - [安装markdown预览插件](https://github.com/antonizoon/kate-markdown) - 码Python也挺方便,也有常用快捷键,自动提示,终端整合,而且是自动切目录 -********************************* + +************************ + ## Geany - 码C 编译方便 有Ctag辅助 -********************************* +************************ + ## scite > 简洁的编辑器,可配置挺多,打开速度快 @@ -65,11 +80,12 @@ _个人配置_ ### 快捷键 > Alt Shift 列编辑 - ## Sublime > [常用配置](https://segmentfault.com/a/1190000002596724) -- 如果出现小bug,就直接删除 ~.config 下的 sublime文件夹注意注册证书拷出来 +- 直接下载压缩包 `wget https://download.sublimetext.com/sublime_text_3_build_3211_x64.tar.bz2` + +- 如果出现小bug,就直接删除 ~/.config 下的 sublime文件夹注意注册证书拷出来 - 中文不兼容解决方法: 3143版本号下: - 搜索安装插件 ChineseLocalizations 就能汉化 - 修改配置文件 添加`"font_face": "DeJaVu Sans Mono",` 就解决了字体错位的问题 @@ -79,12 +95,13 @@ _个人配置_ **关闭自动检查升级** - setting 中 "update_check":false + ### 快捷键 -> [参考博客: Sublime Text 3 快捷键](http://www.cnblogs.com/roadone/p/7745641.html) +> [参考: Sublime Text 3 快捷键](http://www.cnblogs.com/roadone/p/7745641.html) > [sublime的常用快捷键](http://www.cnblogs.com/kristen-zou/p/7641158.html) ### crack -> [3143码](https://gitee.com/kcp1104/codes/89xfugn5dwoyr23vchikb54#sublime-3143-Key) +> [3143码](https://gitee.com/gin9/codes/89xfugn5dwoyr23vchikb54) - [参考博客](http://www.cnblogs.com/hollow/p/6496469.html) - [3143 1](https://fatesinger.com/100121) | [3143 2](https://fatesinger.com/100227) | [3176](https://fatesinger.com/100237) @@ -93,11 +110,13 @@ _个人配置_ ## VSCode -> [官网](https://code.visualstudio.com/) 码笔记,码Python 比较方便,目录树,预览,整合终端 | [中文手册](https://jeasonstudio.gitbooks.io/vscode-cn-doc/) +> [Official Site](https://code.visualstudio.com/) | [中文手册](https://jeasonstudio.gitbooks.io/vscode-cn-doc/) | [code-server](https://github.com/cdr/code-server) +> 用户自定义配置目录 `~/.config/Code/User/` -1. 其所有用户自定义配置都缓存在此目录 `~/.config/Code/User/` 1. vscode 书写 markdown [官方文档](https://code.visualstudio.com/Docs/languages/markdown) - +1. 禁用GPU加速 ctrp+shift+p 中的 Preferences: Configure Runtime Arguments 命令 + - 加上 "disable-hardware-acceleration": true +1. 设置tab多行展示 `workbench.editor.wrapTabs` ### 快捷键 > [快捷键官方PDF说明](https://code.visualstudio.com/shortcuts/keyboard-shortcuts-linux.pdf) @@ -106,6 +125,7 @@ _个人配置_ - 直接输入文件名就是搜文件 - `>` 作为前缀则等同 `Ctrl Shift P` - `#` 作为前缀则等同 `Ctrl T` + - `@` 当前文件内标题 版本1.71.1+ - `Ctrl T ` 搜索打开所有Markdown文件的所有标题 1.25+ - `Ctrl Shift P ` 执行命令 - `Ctrl+K Ctrl+S` 设置用户快捷键 Keyboard Shortcuts @@ -113,35 +133,54 @@ _个人配置_ - `Ctrl Space` 智能提示 变量,代码片段... **需要注意这个快捷键和Windows以及Linux上切换输入法快捷键有冲突,修改即可** - `Alt Shift` 列编辑 - `C S .` 显示面包屑 版本:1.26+ +- `workbench.editor.wrapTabs` 启用多tab同时展示 -> [参考博客: 快捷键大全](https://blog.csdn.net/crper/article/details/54099319) -> [参考博客: VS Code 使用小技巧](https://zhuanlan.zhihu.com/p/22880087) +> [参考: 快捷键大全](https://blog.csdn.net/crper/article/details/54099319) +> [参考: VS Code 使用小技巧](https://zhuanlan.zhihu.com/p/22880087) ### 代码片段 > 配置地点 文件-首选项-用户代码片段 可以新建一个代码片段 > 默认是放在用户的配置目录下 `~/.config/Code/User/snippets/` -- [参考博客: VS Code 折腾记 - (6) 基本配置/快捷键定义/代码片段的录入(snippet)](https://juejin.im/post/58aeeca22f301e006cf65c8b) +- [参考: VS Code 折腾记 - (6) 基本配置/快捷键定义/代码片段的录入(snippet)](https://juejin.im/post/58aeeca22f301e006cf65c8b) - [巧用VScode“用户代码片段”来提高效率](https://www.dogxu.cn/2017/06/10/vscode-snippet/) - 然后自定义一下insert snippet的快捷键,就很方便使用了 个人配置为`Ctrl L`, **其实 直接 Ctrl Space 直接提示就行了** - 注意,每次修改片段配置文件,都需要重启Vscode才会生效最新修改...emm ### 插件 **美化** +>1. Material Icon Theme +>1. Snazzy Operator + 1. vscode-icons -1. Material Icon Theme 1. One Dark Pro -1. snazzy operator +1. Gruvbox Theme **工具** 1. Beautify +1. Prettier 1. Auto Rename Tag 1. Todo Tree -1. GitLens `方便查看更改` +1. GitLens +1. LeetCode +1. vscode-proto3 +1. PlantUML +1. Markdown PDF +1. Draw.io Integration +1. vscode-mindmap +1. rainbow csv +1. Office Viewer 类似 Typora +1. Docker 微软推出 + - 可直接修改容器内文件 ### 实践 -> [参考博客: 用Git在Visual Studio Code内进行版本控制[指导]](https://sdk.cn/news/4041) -> [参考博客: 使用vscocd进行python开发 ](http://www.cnblogs.com/bloglkl/archive/2016/08/23/5797805.html) +> [参考: 用Git在Visual Studio Code内进行版本控制[指导]](https://sdk.cn/news/4041) +> [参考: 使用vscocd进行python开发 ](http://www.cnblogs.com/bloglkl/archive/2016/08/23/5797805.html) + +### vscode server +> [vscode-server](https://code.visualstudio.com/docs/remote/vscode-server) + +[Docker: vscode-server](https://hub.docker.com/r/ahmadnassri/vscode-server) *********************************** ## Atom @@ -161,12 +200,20 @@ strict-ssl=false - 安装markdown预览插件 `该插件早已经停止维护了,还是只用来简单的查看修改文件就好了` +## notepadqq + +## MousePad + +## Xed +> [Github](https://github.com/linuxmint/xed) + ****************************** ## 小书匠 -- [在线使用](http://markdown.xiaoshujiang.com/) | [github地址](https://github.com/suziwen/markdownxiaoshujiang) -- 本来是很合适的,但是对文件操作不干净,总有些bug不好用,文件闪退出错,终端不方便 - - 不适合编程适合写作,所支持的md的格式非常强大 +> [在线使用](https://markdown.xiaoshujiang.com/) | [github地址](https://github.com/suziwen/markdownxiaoshujiang) + +- 本来是很合适的,但是对文件操作不干净(引入自定义的文本和格式),文件偶尔闪退出错,终端不方便,资源占用大 + - 不适合编程适合写作,所支持的md的格式非常方便 - 快捷键 - 加粗 `Ctrl + B` - 斜体 `Ctrl + I` @@ -183,34 +230,50 @@ strict-ssl=false ************ -## Moeditor|Typora|CuteMarkEd -> [Moeditor](https://github.com/Moeditor/Moeditor) +## Moeditor/Typora/CuteMarkEd +> [Github:Moeditor](https://github.com/Moeditor/Moeditor) - 书写单个md文件方便,美观,没有目录树侧栏是硬伤, 但是typora 导出很强大 -***************************** - -## Cmd -> [官网](https://www.zybuluo.com/cmd/) ************************** # 终端中的文本编辑器 ## Vi/Vim -> [Github地址](https://github.com/vim/vim) -> [Vim 学习笔记](/Linux/vim.md) +> [Github: Vim](https://github.com/vim/vim) +> [Vim 学习笔记](/Linux/Tool/Vim.md) + +## helix +[Github: helix](https://github.com/helix-editor/helix) ************ ## Nano -- 模式没有vi系列复杂,使用简单,安装占用小 +- 使用简单,安装占用小 类似 emacs 的快捷键操作方式 -************ -## fte-terminal -- 文件树浏览,快速打开文件进行修改是比较方便的 +## Micro +> [Github: micro](https://github.com/zyedidia/micro) ***************** -## 在线编辑器 -_[stackedit.io](https://stackedit.io/)_ +## BS在线编辑器 +- _[stackedit.io](https://stackedit.io/)_ - [Github地址](https://github.com/benweet/stackedit/) +- [小书匠](https://markdown.xiaoshujiang.com/) +- CMD编辑器 + +************************ + +# 十六进制 Hex +> 十六进制方式查看和修改二进制文件 + +> [What's the best hex editor in 2023? ](https://www.reddit.com/r/hacking/comments/105mzw5/whats_the_best_hex_editor_in_2023/) + +**终端** +- 查看 hexdump xxd od hexyl +- 编辑 hexedit vim + +**GUI** +Ghex +[HexWalk](https://github.com/gcarmix/HexWalk) +[010Editor](https://www.sweetscape.com/) +[ImHex](https://github.com/WerWolv/ImHex) -CMD编辑器 \ No newline at end of file diff --git a/Skills/Application/Offices.md b/Skills/Application/Offices.md new file mode 100644 index 0000000..f64e8f7 --- /dev/null +++ b/Skills/Application/Offices.md @@ -0,0 +1,46 @@ +--- +title: Offices +date: 2024-09-23 11:44:36 +tags: +categories: +--- + + +💠 + +- 1. [Excel](#excel) +- 2. [CSV](#csv) + +💠 2024-09-23 11:44:36 +**************************************** + +# Excel +主要分为 xls 和 xlsx + +- xls 专有二进制 +- xlsx zip包 + +| | xls | xlsx | +|:---|:---|:---| +| 年份 | 2003之前 | 2007及以后 | +| 格式 | 专有二进制CBF格式 | zip+xml | +| 兼容性 | 只能Office,其他软件有兼容问题 | 开放性格式,兼容更好 | +| 安全性 | 定制化 | 复用zip加密,更成熟 | +| 容量 | 65536行 乘 256列 | 1,048,576行 乘 16,384列 | + +> [Excel 规范与限制](https://support.microsoft.com/zh-cn/office/excel-%E8%A7%84%E8%8C%83%E4%B8%8E%E9%99%90%E5%88%B6-1672b34d-7043-467e-8e27-269d656771c3) + +由于Excel最大行数为104w行,导出超量数据时,通常会拆分Sheet,或者退而使用csv格式 + +************************ + +# CSV +注意Windows平台会对文件带上BOM头,用于区分字符集编码 [BOM](/Skills/CS/CharacterEncoding.md#关于-bom) +对csv文件追加 EF BB BF 三个字节 以实现对Office的兼容,WPS会自动检测和切换解析的字符集编码 +```java +FileOutputStream fos = new FileOutputStream(new File(this.csvFileAbsolutePath)); + byte [] bs = { (byte)0xEF, (byte)0xBB, (byte)0xBF}; //new added + fos.write(bs); + fos.close(); +``` + diff --git a/Skills/Application/VirtualBox.md b/Skills/Application/VirtualBox.md index 55d352b..3ab7e20 100644 --- a/Skills/Application/VirtualBox.md +++ b/Skills/Application/VirtualBox.md @@ -1,27 +1,64 @@ -`目录 start` - -- [VirtualBox](#virtualbox) - - [安装Linux](#安装linux) - - [Manjaro](#manjaro) - - [Deepin](#deepin) - - [KDE](#kde) - - [安装Windows](#安装windows) - - [Windows7](#windows7) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: VirtualBox +date: 2018-12-15 12:11:48 +tags: + - VirtualBox +categories: + - 工具 +--- + +**目录 start** + +1. [VirtualBox](#virtualbox) + 1. [网络管理](#网络管理) + 1. [硬盘管理](#硬盘管理) + 1. [安装Linux](#安装linux) + 1. [Manjaro](#manjaro) + 1. [Deepin](#deepin) + 1. [安装Windows](#安装windows) + 1. [Windows7](#windows7) + 1. [安装 Android](#安装-android) + 1. [Android-x86](#android-x86) + +**目录 end**|_2020-04-27 23:42_| **************************************** # VirtualBox -## 安装Linux +## 网络管理 +> [How to configure port forwarding in VirtualBox](https://www.simplified.guide/virtualbox/port-forwarding) + +- Network -> Advanced -> Port forwarding -> host ip port(宿主机) guest ip port (虚拟机内系统) + +************************ + +## 硬盘管理 +> 添加新分区到虚拟机 +1. Storge -> add Hard Dish -> create Disk 选择大小, 创建完成后, 启动虚拟机就能看到挂载了, 再改下 /etc/fstab 就能自动挂载了 + +************************ + +## 安装Linux ### Manjaro > 官网下载好对应的镜像文件 -#### Deepin -#### KDE -> 创建好系统, 选好ISO, 进入后, 选择图形化安装方式, 也就是选 Boot:***ISO 那一项 +### Deepin ## 安装Windows ### Windows7 + +************************ + +## 安装 Android +> [参考: 5 Best Android Emulators for Linux](https://beebom.com/android-emulators-linux/) + +### Android-x86 +> [下载](https://www.fosshub.com/Android-x86.html) +> [参考: ](https://www.cnblogs.com/wynn0123/p/6288344.html) + +> 启动后不进图形化: +1. `mount –o remount,rw /mnt` +1. vi /mnt/grub/menu.lst 第一个块 kernel /android-8.1-rc2/kernel quiet 后空格,加上 nomodeset +1. 修改分辨率也是在这一行 UVESA_MODE 的值 diff --git a/Skills/Application/WebBrowser.md b/Skills/Application/WebBrowser.md index b22b03e..68e31af 100644 --- a/Skills/Application/WebBrowser.md +++ b/Skills/Application/WebBrowser.md @@ -1,23 +1,34 @@ -`目录 start` - -- [浏览器](#浏览器) - - [FireFox](#firefox) - - [开发版本](#开发版本) - - [必备插件](#必备插件) - - [配置](#配置) - - [使用](#使用) - - [seamonkey](#seamonkey) - - [Chrome](#chrome) - - [主题](#主题) - - [Vivaldi](#vivaldi) - - [Opera](#opera) - -`目录 end` |_2018-09-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 浏览器 +date: 2018-12-15 12:11:56 +tags: +categories: + - 工具 +--- + +💠 + +- 1. [浏览器](#浏览器) + - 1.1. [FireFox](#firefox) + - 1.1.1. [开发版本](#开发版本) + - 1.1.2. [必备插件](#必备插件) + - 1.1.3. [配置](#配置) + - 1.1.4. [使用](#使用) + - 1.1.5. [Tips](#tips) + - 1.2. [Seamonkey](#seamonkey) + - 1.3. [Chrome](#chrome) + - 1.3.1. [主题](#主题) + - 1.3.2. [插件](#插件) + - 1.4. [Vivaldi](#vivaldi) + +💠 2024-10-10 20:43:07 **************************************** # 浏览器 +[neko](https://github.com/m1k1o/neko)`runs in docker and uses WebRTC` + ## FireFox > [所有桌面版](https://www.mozilla.org/zh-CN/firefox/channel/desktop/) | [所有正式版](https://www.mozilla.org/en-US/firefox/releases/) -> [正式版本和夜更新版FTP下载地址](http://ftp.mozilla.org/pub/firefox) | [所有开发者版本](http://ftp.mozilla.org/pub/devedition/releases/) +> [正式版本和夜更新版FTP下载地址](http://ftp.mozilla.org/pub/firefox/) | [所有开发者版本](http://ftp.mozilla.org/pub/devedition/releases/) `57为全新的Quantum版本, 因为插件标准的缘故和之前的56版本插件不兼容` @@ -28,7 +39,10 @@ - 火狐和Chrome都支持在控制台的网络中直接右击一个请求然后复制, 就可以出来复制成cURL命令的选项, 比较好用 -> [火狐性能优化贴](https://www.xzcblog.com/post-47.html) +> [火狐性能优化贴](https://www.xzcblog.com/post-47.html) +> [Firefox uses too much memory or CPU resources - How to fix](https://support.mozilla.org/en-US/kb/firefox-uses-too-much-memory-or-cpu-resources) + +> [Floorp](https://github.com/floorp-Projects/floorp/)`FF衍生品,支持工作区,多行tab,单窗口多tab` 但是没有循环tab切换 ### 开发版本 > [开发者版本链接](https://www.mozilla.org/zh-CN/firefox/developer/) | [开发工具](https://firefox-dev.tools/) @@ -37,79 +51,116 @@ ### 必备插件 > [开发自己的插件](https://github.com/Kuangcp/LearnWebExtension) -- `Saka Key` 快捷键神器 大幅度脱离鼠标 [官方文档](https://key.saka.io/) - 1. 浏览器默认: 脱离输入框焦点 _Esc_ | 切换标签 _ctrl-Tab_ _shift-ctrl-Tab_ | 关闭标签 _ctrl-w_ - 1. 滑动: 下滑 `d/j` 上滑 `s/k ` - - 上下滑半屏幕 `Shift d/s` | 上下滑全屏 `Shift j/k` - - 滑到底/顶 `Shift-g` / `gg` - - 滑左/右 `alt-s or alt-k` / `alt-d or alt-j` - 1. 缩放: 放大/缩小 `z/shift-z` | 重置大小 `shift-alt-z` - 1. 前进/后退: `cc/vv` | 跳上级URL `uu` | 跳URL域名 `u shift-u` - 1. 标签页: 新建 `t` | 恢复关闭 `shift t` | 复制 `b` - - 关闭 `xx` | 关闭其他 `x shift x` | 关闭左边 `x [` | 关闭右边 `x ]` - - 刷新 `rr` | 刷新全部标签 `r shift r` | 深度刷新 `shift r shift r ` - - 切换: 左右 `q/w` 或者 `[/]` | 第一个/最后一个 `1/0`或者`shift-q`/`shift-w or 0 ` - - 移动: 左右 `i/o` 或者 `shift-[` / `shift-]` 第一个/最后一个 `shift-i/shift-o` 或者 `alt-[/alt-]` - - 静音: `m` 静音所有标签 `shift-m` - 1. 窗口: 新建 `n` | 新建隐私 `shift n` - 1. 页面上所有页面链接 `ff` _神操作_ [文档](https://key.saka.io/tutorial/clicking_and_link_hints) - 1. 传递快捷键即绕过插件的事件监听 `;` [文档](https://key.saka.io/tutorial/pass_keys) - - 比如要在网页上敲英文的时候,就需要每次都要输入分号,才能绕过监听, 真是麻烦 - 1. [剪贴板](https://key.saka.io/tutorial/clipboard): 复制当前页面的URL:`yy` - - 当前标签页打开链接 `yf`| 后台打开 `yb` | 新窗口打开 `yn` | 隐私窗口 `y shift-n` - -1. `Greasemonkey` 传说中的油猴, 可以自己写脚本 [wiki](https://wiki.greasespot.net/User_Script_Hosting) -1. `New Tab Tools` 新建标签页的自定义工具 有一定bug +1. 附加组件管理器: 只有正式版会内置该插件, 别的版本都没有, 插件的功能是 地址栏二维码,拖拽链接, + - 如果想在开发版以及Nightly上用上该插件, 只需要去 ~/.mozilla/ 下找到正式版的配置文件里的 extension 目录就能找到 cpmanager.xpi 了, 拖入浏览器就可以了 + - 但是这个组件只保证正式版是正常的, 其他版本则要看运气 + +- `Vimium C - All by Keyboard` + - Vim风格操作浏览器日常操作 +1. `Dark Reader` 设置网页黑夜模式 +1. `Greasemonkey` Tampermonkey 传说中的油猴, 可以自己写脚本 [wiki](https://wiki.greasespot.net/User_Script_Hosting) +1. `New Tab Tools` 新建标签页的自定义工具 1. `cliget` 能将下载中的任务转化为 curl wget命令 牛 1. `eolinker` 接口测试工具 -1. `Simple Tab Groups` 58版本有bug -1. `rester` rest客户端工具 +1. `Simple Tab Groups` 懒加载式隔离标签组 +1. `RESTer` rest客户端工具 1. `Download all Images`下载图片 1. `octotree` github 目录查看 1. `Web Developer` 各种Web调试开发工具 -1. `One Tab` tab归组,十分好用 1. `Remove Cookies Button` -1. `围脖是个好图床哟` 方便的图床,但是要登录微博 1. `滴答清单` 全平台可使用 1. `Auto Reload Tab` 定时自动刷新标签页 +1. `ReloadMatic` 定时自动刷新 1. `轻灵划译` 即刻翻译, 多种平台 +1. `Tab Counter` Tab计数 开发者 WaldiPL +1. `Elasticvue` Elasticsearch 插件 +1. `HeaderEditor` 修改请求响应的Header和Body +1. [TechStack](https://github.com/Get-Tech-Stack/TechStack) ### 配置 -> 主要是 about:config +> 大多是通过 about:config 页面配置 1. 配置火狐访问80以外的端口 - 1. 地址栏 : `about:config` - 1. 右键新建字符串 `network.security.ports.banned.override` - 1. 输入值 81,88,98 也可以是 6000-6005, 省事就 0-65535(不建议) + 1. 打开 `about:config?filter=network.security.ports.banned.override` 新建字符串类型 + 1. 输入值 81,88,98, 也可以是 6000-6005, 省事就 0-65535(不建议) 1. 对于自己喜欢多开火狐的习惯, 整理如下习惯 - - 安装开发版本, 使用一个默认的配置 - - 使用开发版本的可执行文件, 配置一个新的配置目录, 也就是一个新的火狐 -1. 前者是重度使用(往往很多标签20+), 常用的标签页全部固定, 一些TODO的tab也放在这里, 用于开发和娱乐(1000M-2000M) -1. 后者是轻度使用(开10个以下标签), 仅在内存不够时, 只用于内存不足时开发必需 (一般400M左右) + - 安装开发版本, 使用默认的配置 + - 使用开发版本的可执行文件, 通过 -P 参数配置一个新的配置目录 + 1. 前者是重度使用(往往很多标签20+), 常用的标签页全部固定, 一些TODO的tab也放在这里, 用于开发和娱乐(1000M-2000M) + 1. 后者是轻度使用(开10个以下标签), 仅在内存不够时, 只用于内存不足时开发必需 (一般400M左右) + +1. 当前标签页右边打开新标签页: `about:config?filter=browser.tabs.insertAfterCurrent` 新建Bool类型, 设置为true + +1. 网页重定向次数限制 默认 20 `network.http.redirection-limit` 设置为0就禁止了网页的重定向 + +1. 内存资源占用大 + 1. `about:memory` 查看内存情况 + 1. `dom.ipc.processCount` 降低进程数 + 1. `browser.tabs.remote.autostart` 设置 false + 1. `about:unloads` 手动触发tab卸载 + 1. `about:processes` 查看tab进程 **Shift + Esc** + 1. [Auto Tab Discard 插件](https://addons.mozilla.org/en-US/firefox/addon/auto-tab-discard/) ### 使用 -1. 地址栏 `@bing` @baidu... 即可使用指定的搜索引擎进行搜索 +1. 地址栏 `@bing @baidu...` 即可使用指定的搜索引擎进行搜索 1. 地址栏 `* Java` 即可在所有书签中搜索 Java +1. 地址栏 `% Java` 就可以在已打开的标签页中搜索Java + +### Tips +> 在B站看视频 看久了就会发现内存爆炸, 曾经全屏看LOL直播连续6个小时, 然后结束的时候发现出不去了, 要等好久 +> 等了半天打开htop一看firefox 占用内存 6g, 负载 297, 怪不得风扇转这么大声... +> 原以为是Firefox 的问题, 用 Chrome 看B站一样的场景, 看了没多久就是CPU负载高 内存泄露严重, 所以是操作系统问题还是B站问题.... + +************************ +> firefox 突然crash并且无法重新打开 124.07 版本,删Profiles重置也不生效,降级到122.0b3后可正常使用 + +论坛里提到可能和滚动升级的共享库版本不一致有关,但是近一个月没更新底层库和软件了(因为另一个安全验证的问题),感觉可能是打开了阿里云盘和百度云盘两个站点导致的 + ********************* -## seamonkey + +## Seamonkey > Mozilla基金会另一个项目 [seamonkey](https://www.seamonkey-project.org/) 亮点在于内置IRC **************************************** + ## Chrome - 的确快,几乎没有各种兼容和诡异问题,就是内存占用高, 还有就是主题被墙,fq才能配置好 +1. `Removing keychain login from Chormium` 启动命令添加如下参数 chromium --password-store=basic +1. `设置代理` chrome追加启动参数 --proxy-server=192.168.7.77:8888 --ignore-certificate-errors + - PAC设置 `--proxy-pac-url=http://localhost:1235/pac` + +- 切换最近标签 CTRL+PgUp 和 CTRL+PgDn + +使用Profiles实现多账户共存,但是保存的帐号密码都会跟随其他Profile,还是没有Firefox的Multiple Accounts丝滑。 + ### 主题 1. Aero Trans Brushed Metal Theme 1. Material Dark 1. Morpheon Dark 1. 炭黑+銀色金屬 1. Modern Flat -************ -## Vivaldi -- 感觉采用的是chrome内核,做的更漂亮了,而且是内置了很多常用插件,的确很方便,相比于chrome更符合国内使用 +### 插件 +- [插件网](https://extfans.com/) +- [chromefor](https://www.chromefor.com/) +- [Chrome插件英雄榜](https://github.com/zhaoolee/ChromeAppHeroes) -## Opera +1. Vimium C - All by Keyboard +1. crxMouse +1. TabsFolder +1. Cluster Window & Tab Manager +1. Chrome Download Manager +1. Fatkun 图片批量保存 +1. Stylized Scrollbar 滚动条美化 +1. Tab Position Options 当前tab右侧打开新tab +1. Auto Tab Discard 冻结最少使用的tab +1. Open Last Tab 按最近使用标签切换 +************************ + +## Vivaldi +- 采用的是chrome内核 内置了很多常用插件(但是安装插件的入口关闭了) 相比于chrome更符合国内使用 +vivaldi://settings diff --git a/Skills/CS/Arithmetic.md b/Skills/CS/Arithmetic.md deleted file mode 100644 index a8b16b6..0000000 --- a/Skills/CS/Arithmetic.md +++ /dev/null @@ -1,41 +0,0 @@ -`目录 start` - -- [算法](#算法) - - [时间复杂度](#时间复杂度) - - [匹配算法](#匹配算法) - - [排序算法](#排序算法) - - [安全](#安全) - - [密码学](#密码学) - - [Diffie-Hellman Key Exchange算法](#diffie-hellman-key-exchange算法) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 算法 - -> [《编程之法》](https://github.com/julycoding/The-Art-Of-Programming-By-July) - -## 时间复杂度 -> [Java中的实践](http://www.baeldung.com/java-algorithm-complexity) - -********************* -## 匹配算法 -- [字符串相似度匹配](http://zjwyhll.blog.163.com/blog/static/75149781201281142630851/) - -## 排序算法 -> [参考博客: 九种排序算法的可视化及比较](https://zhuanlan.zhihu.com/p/34421623?group_id=955945213303250944) - -********* -## 安全 - -### 密码学 - -#### Diffie-Hellman Key Exchange算法 -> Whitfield Diffie 和 Martin Hellman ,他们于2015年获得了计算机科学领域的最高奖:图灵奖 - -![码农翻身](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/arithmetic/Diffie-HellmanKeyExchange.png) - -`最后神奇的魔法发生了, 我们两个得到了同样的值 s = 10!` -- 这个s 的值只有我们两个才知道, 其实就是密钥了, 可以用来做加密解密了( 当然,这只是一个例子,实际的密钥不会这么短), 我们俩的通讯从此就安全了。 - - “数学家小帅哥说了, 原因很简单,(gx mod p)y mod p 和 (gy mod p)x mod p 是相等的! ” - - “那黑客不能从公开传输的 p = 17, g = 3, a = 6 , b = 12 推算出s = 10 吗?” 我问道。 - - “当然不能, 不过前提是需要使用非常大的p , x, y, 这样以来,即使黑客动用地球上所有的计算资源, 也推算不出来。 ” diff --git a/Skills/CS/BasicOperator.md b/Skills/CS/BasicOperator.md new file mode 100644 index 0000000..125cc3f --- /dev/null +++ b/Skills/CS/BasicOperator.md @@ -0,0 +1,118 @@ +--- +title: 基础运算 +date: 2023-09-10 21:37:59 +tags: +categories: +--- + +**目录 start** + +1. [基础运算](#基础运算) + 1. [位运算](#位运算) + 1. [模运算](#模运算) + 1. [加法](#加法) + 1. [减法](#减法) + 1. [乘法](#乘法) + 1. [除法](#除法) + 1. [指数](#指数) + 1. [分治法计算](#分治法计算) + 1. [快速模幂运算](#快速模幂运算) + 1. [对数运算](#对数运算) +1. [相关思想](#相关思想) + 1. [扩展欧几里德算法](#扩展欧几里德算法) + +**目录 end**|_2023-09-17 15:44_| +**************************************** +# 基础运算 + +## 位运算 +且 +或 +非 +异或 + +## 模运算 +整数除法:A / B = C 余 D +定义: A mod B = D 。 模运算不关注除法的结果而是关注余数。计算机上通常标识为 A%B + +> 同余 +`A≡B(mod C)`:A 与 B 模 C 同余 + +等效于 +- A mod C = B mod C +- C ∣ (A − B) `| 符号意味着整除,或者前者是后者的因子` +- A = B + K * C `其中 K 为整数` + +同余具有数学性质:反射性,对称性,传递性 +- 具有反射性:A与A有关 +- 具有 对称性: 如果A与B有关,那么B与A有关。 +- 具有 传递性: 如果A与B有关且B与C有关,那么A与C有关。 + +> 商余定理: 给出任何整数 A 和一个正整数 B ,存在唯一整数Q和R ,满足 `A = B * Q + R 其中 0 ≤ R < B`. 即 `A mod B = R` + +### 加法 +(A + B) mod C = (A mod C + B mod C) mod C + +### 减法 +(A - B) mod C = (A mod C - B mod C) mod C + +可以看作加法的逆运算 + +### 乘法 +(A * B) mod C = (A mod C * B mod C) mod C + +### 除法 +又称模逆运算。 + +### 指数 +A^B mod C = ( (A mod C)^B ) mod C + +#### 分治法计算 +> 当B数值很大时,中间计算值通常会溢出所有编程语言的整数类型, 此时可将幂拆分多个幂相乘。 + + 例如: 计算 2^90 mod 13 的值。 + + 先对指数部分做拆分: 2^50 mod 13 = 4 2^40 mod 13 = 3。简化为: + = (2^50 * 2^40) mod 13 + = (2^50 mod 13 * 2^40 mod 13) mod 13 + = ( 4 * 3 ) mod 13 + = 12 mod 13 + +#### 快速模幂运算 +相较于分治法,更利于计算机计算,效率更高。 + +> 当B的值是2的幂时: + +A^2 mod C = (A * A) mod C = ((A mod C) * (A mod C)) mod C + + 例如: 计算 7^256 mod 13 的值。 + 可以采用递归方式迭代计算: 先计算 7^1 mod 13 代入到 7^2 mod 13 代入到 7^4 mod 13 ...中,最终得到 7^256 mod 13 的值 + +> 当B的值是任意值时: 可以将B转换为二进制数相加 例如将 7 转换为 111 即 1+2+4 + + 例如计算 31^7 mod 5 的值。 + = 31^(1+2+4) mod 5 + = 31^1 * 31^2 * 31^4 mod 5 + = (31^1 mod 5 * 31^2 mod 5 * 31^4 mod 5) mod 5 +问题又变成了B的值是2的幂问题进行求解,同样采用上述方式迭代计算,即可得到 31^7 mod 5 的值。 + + +### 对数运算 +乘方的逆运算称为对数,数学运算中求大整数的对数不会很困难,但是模运算中求的对数被称为离散对数,大整数时,离散对数计算困难和耗时,暂未发明高效的算法。 + +Diffie-Hellman 密钥交换协议 和 EleGamal 公钥算法中就运用了离散对数 + +例如:51^x mod 23 = 12 求取 x,只能暴力求解得到x=20 + +************************ + +# 相关思想 +## 扩展欧几里德算法 +又称 辗转相除法,求解两个整数的最大公约数 + +计算 GCD(A,B) 的值,算法如下: + + 如果 A = 0, GCD(A,B)=B,因为 GCD(0,B)=B,我们就到此为止。 + 如果 B = 0,GCD(A,B)=A,因为 GCD(A,0)=A,我们就到此为止。 + 用商和余数的表达方式 (A = B⋅Q + R) 写出 A。 + 用扩展欧几里德算法计算 GCD(B,R),因为 GCD(A,B) = GCD(B,R) diff --git a/Skills/CS/CharacterEncoding.md b/Skills/CS/CharacterEncoding.md index 3bff012..2a3fa66 100644 --- a/Skills/CS/CharacterEncoding.md +++ b/Skills/CS/CharacterEncoding.md @@ -1,29 +1,46 @@ -`目录 start` - -- [字符编码](#字符编码) - - [ascii](#ascii) - - [ANSI](#ansi) - - [Unicode](#unicode) - - [UTF](#utf) - - [UTF-8](#utf-8) - - [UTF-16](#utf-16) - - [汉字编码发展史](#汉字编码发展史) - -`目录 end` |_2018-08-13_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 字符编码 +date: 2018-10-21 10:56:52 +tags: +categories: + - 计算机基础 +--- + +💠 + +- 1. [字符编码](#字符编码) + - 1.1. [ASCII](#ascii) + - 1.2. [ANSI](#ansi) + - 1.3. [Unicode](#unicode) + - 1.3.1. [UTF](#utf) + - 1.3.1.1. [关于 BOM](#关于-bom) + - 1.3.1.2. [UTF-8](#utf-8) + - 1.3.1.3. [UTF-16](#utf-16) + - 1.3.1.4. [UTF-32](#utf-32) +- 2. [汉字编码发展史](#汉字编码发展史) +- 3. [Java中的编码](#java中的编码) +- 4. [乱码](#乱码) + +💠 2024-09-09 17:25:40 **************************************** # 字符编码 - > [字符编码笔记:ASCII,Unicode 和 UTF-8](http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html) | [阮一峰的文章有哪些常见性错误](https://www.v2ex.com/t/343634) -> [字符编码](http://blog.jobbole.com/39309/) -> [参考博客: Ansi,UTF8,Unicode,ASCII编码的区别](https://blog.csdn.net/xiongxiao/article/details/3741731) +> [参考: Ansi,UTF8,Unicode,ASCII编码的区别](https://blog.csdn.net/xiongxiao/article/details/3741731) +> [参考: 字符编码的前世今生](https://tgideas.qq.com/webplat/info/news_version3/804/808/811/m579/201307/218730.shtml) - 字符内码(charcter code)指的是用来代表字符的内码.读者在输入和存储文档时都要使用内码,内码分为 - 单字节内码 -- Single-Byte character sets (SBCS), 也就是第一个字节 0-127 - - 双字节内码 -- Double-Byte character sets)(DBCS), 也就是第二个字节 128-255 -**************** -## ascii -> ascii American Standard Code for Information Interchange 美国信息交换标准代码 属于单字节内码 并等同于国际标准ISO/IEC 646 + - 双字节内码 -- Double-Byte character sets (DBCS), 也就是第二个字节 128-255 + +字符集编码发展史:ASCII,IOS-8859家族, GB2312和GBK等双字节家族, Unicode字符, UTF 编码家族 + +************************ + +## ASCII +> ASCII (American Standard Code for Information Interchange) 美国信息交换标准代码 属于单字节内码 并等同于国际标准ISO/IEC 646 + +> [Wiki: ASCII](https://en.wikipedia.org/wiki/ASCII) 1. 0-31 以及 127 是控制字符或通信专用字符 (其余为可显示字符) 1. 32~126(共95个)是字符(32是空格) @@ -32,9 +49,13 @@ - 97~122号为26个小写英文字母 - 其余为一些标点符号、运算符号等。 -同时还要注意,在标准ASCII中,其最高位(b7)用作奇偶校验位。 -Linux `man ascii` 就可以查看, 没有这个手册就安装 `ascii` 用这个程序来展示 -> [参考博客: ASCII码表](http://www.cnblogs.com/xmxu/archive/2012/07/10/2584032.html) +> 注意 +>- 在标准ASCII中,其最高位(b7)用作奇偶校验位。 Linux上 `man ascii` 就可以查看完整表 +>- 经过扩充后 ascii 可大于127 `ASCII扩展字符` + +1. ascii 160 不间断空格 non-breaking space + +************************ ## ANSI > ANSI/ISO8859-1-1987 或称 Latin 1 @@ -42,71 +63,127 @@ Linux `man ascii` 就可以查看, 没有这个手册就安装 `ascii` 用这个 这个编码就是 ascii 的扩展, 但是只是扩展了一个字节, 然后各个国家的编码又不一致(不同的代码页), 导致了十分混乱 至于简体中文编码GB2312,实际上它是 ANSI 的一个代码页 936 -******************* + +************************ ## Unicode -> [unicode.org](http://www.unicode.org/) | [wikipedia](https://en.wikipedia.org/wiki/Unicode) +> [wikipedia](https://en.wikipedia.org/wiki/Unicode) | [unicode.org](http://www.unicode.org/) | [unicode table](https://unicode-table.com/cn/) -Unicode 是一个囊括了世界上所有字符的字符集,其中每一个字符都对应有唯一的编码值, 但是并不是一个具体实现的编码方案, 不能直接使用 -其实现有 UTF-8 UTF-16 UTF-32 ... -目前最新版本 11 已经包括 137,439 个字符 +Unicode 是一个囊括了世界上所有字符的字符集,其中每一个字符都对应有唯一的编码值, 但是并不是一个已实现的编码方案, 不能直接使用 +基于此套编码值的**编码实现方案**有 UTF-8 UTF-16 UTF-32 ... 目前最新版本 Unicode11 已经包括 137,439 个字符 -****************** +> 问题: 直接看, 看不到的字符, 可能带来一些坑 + +| | | +|:----|:----| +| 零宽间隔 zero-width space | `U+200B` | +| NO-BREAK SPACE | `U+C2A0` | +| 零宽不折行空格 | `U+FEFF` html: `` | +| 零宽度连字符 (zero-width joiner) | \u200D| +| 零宽度断字符 (zero-width non-joiner) | \u200C | +| 左至右符 (left-to-right mark) | \u200E | +| 右至左符 (right-to-left mark) | \u200F | + +************************ ### UTF > UTF: UCS Transformation Format, UCS: Unicode Character Set 它是将Unicode编码规则和计算机的实际编码对应起来的一个规则。现在流行的UTF有2种:UTF-8和UTF-16. +#### 关于 BOM +- [wiki: bom](https://en.wikipedia.org/wiki/Byte_order_mark) +- [知乎: 「带 BOM 的 UTF-8」和「无 BOM 的 UTF-8」有什么区别?](https://www.zhihu.com/question/20167122)`微软的习惯` + +1. 找出含BOM的文件 `grep -r $'\xEF\xBB\xBF'` +1. 通过 vim 进行转换, 去除 `:set nobomb` 加上 BOM `:set bomb` + +思考: 对于有格式的文件来说,无BOM会更省事,但是无格式的文本处理,有BOM会更省事。 +- 例如Java的class文件,已经有魔数 CAFEBABE, 不需要BOM来标记字符集编码,因为是二进制的。 以及Linux世界的代码和脚本默认是UTF-8,也不需要BOM +- 但是无格式的文本文件要判断文件的字符编码就会很麻烦了,某文本如果是有中文和英文,直到读取到中文的字节串才能直到整体编码是什么,意味着读完整个文件才能判断出编码,这一点是有风险的。 + - [CharsetDetector](https://tika.apache.org/0.8/api/org/apache/tika/parser/txt/CharsetDetector.html#detect())`如detect方法的注释所述` + Return the charset that best matches the supplied input data. Note though, that because the detection only looks at the start of the input data, there is a possibility that the returned charset will fail to handle the full set of input data. + + #### UTF-8 -> UTF-8 是一种Unicode的实现方式, 是一种变长编码方案(1-6), 在表示中文时是采用三字节的方式, 已基本覆盖WEB领域 +> UTF-8 是一种Unicode的实现方式, 是一种`变长编码方案`(1-6 字节), 在表示中文时是采用 `三字节 四字节` 的方式, 已成为WEB领域事实标准编码 +>> 占3个字节的:基本等同于GBK,含21000多个汉字 +>> 占4个字节的:中日韩超大字符集里面的汉字,有5万多个 -**依据首字节的最高位来限定** -- 1字节 0xxxxxxx -- 2字节 110xxxxx 10xxxxxx -- 3字节 1110xxxx 10xxxxxx 10xxxxxx -- 4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx -- 5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -- 6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx +注意: 依据 2003年的标准 UTF8 仅使用 1-4 字节长度 -> [参考博客: UTF-8编码规则(转) ](http://www.cnblogs.com/chenwenbiao/archive/2011/08/11/2134503.html) +**依据首字节的最高位表示** +- 1 字节 0xxxxxxx +- 2 字节 110xxxxx 10xxxxxx +- 3 字节 1110xxxx 10xxxxxx 10xxxxxx +- 4 字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx +- 5 字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx +- 6 字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx + +> [参考: UTF-8编码规则(转) ](http://www.cnblogs.com/chenwenbiao/archive/2011/08/11/2134503.html) #### UTF-16 - [CESU-8](https://en.wikipedia.org/wiki/CESU-8) +#### UTF-32 +> 每个字符4字节 + +************************ -## 汉字编码发展史 -最早是制定的GB2312-80 兼容 ascii 采用的是双字节编码方式, 其中一共编码了6763个常用简体汉字, Big5,是台湾使用的编码标准,编码了台湾使用的繁体汉字,大概有8千多个。HKSCS,是中国香港使用的编码标准,字体也是繁体,但跟Big5有所不同。 -后来,由于各方面的原因,国际上又制定了针对中文的统一字符集GBK和GB18030,其中GBK已经在Windows、Linux等多种操作系统中被实现。 +# 汉字编码发展史 +最早是制定的 `GB2312-80` 兼容 `ASCII` 采用的是双字节编码方式, 其中一共编码了6763个常用简体汉字, Big5,是台湾使用的编码标准,编码了台湾使用的繁体汉字,大概有8千多个。 +`HKSCS` 是中国香港使用的编码标准,字体也是繁体,但跟 `Big5` 有所不同。 +后来,由于各方面的原因,国际上又制定了针对中文的统一字符集 `GBK` 和 `GB18030` ,其中GBK已经在Windows、Linux等多种操作系统中被实现。 GBK兼容GB2312,并增加了大量不常用汉字,还加入了几乎所有的Big5中的繁体汉字。但是GBK中的繁体汉字和Big5中的几乎不兼容。 -GB2312、GBK到GB18030都属于双字节字符集 (DBCS) +**GB2312 GBK GB18030 都属于`双字节`字符集 (DBCS)** > [字体编辑用中日韩汉字Unicode编码表](http://www.chi2ko.com/tool/CJK.htm) -> [参考博客: Unicode中文和特殊字符的编码范围](http://www.cnblogs.com/sosoft/p/3456631.html) -> [参考博客: 中文标点符号具体unicode码](https://blog.csdn.net/yuan892173701/article/details/8731490) +> [参考: Unicode中文和特殊字符的编码范围](http://www.cnblogs.com/sosoft/p/3456631.html) +> [参考: 中文标点符号具体unicode码](https://blog.csdn.net/yuan892173701/article/details/8731490) > [汉字 Unicode 编码范围](https://www.qqxiuzi.cn/zh/hanzi-unicode-bianma.php) -| 类别 | 字数 | 范围 | -|:----:|:----:|:----:| -| 基本汉字 |20902字| 4E00-9FA5| -| 基本汉字补充 | 74字| 9FA6-9FEF -| 扩展A |6582字| 3400-4DB5 -| 扩展B |42711字| 20000-2A6D6 -| 扩展C |4149字| 2A700-2B734 -| 扩展D |222字| 2B740-2B81D -| 扩展E |5762字| 2B820-2CEA1 -| 扩展F |7473字| 2CEB0-2EBE0 -| 康熙部首 |214字| 2F00-2FD5 -| 部首扩展 |115字| 2E80-2EF3 -| 兼容汉字| 477字| F900-FAD9 -| 兼容扩展 |542字| 2F800-2FA1D -| PUA(GBK)部件 | 81字| E815-E86F -| 部件扩展 |452字| E400-E5E8 -| PUA增补 |207字| E600-E6CF -| 汉字笔画 |36字| 31C0-31E3 -| 汉字结构 |12字| 2FF0-2FFB -| 汉语注音 |43字| 3105-312F -| 注音扩展 |22字| 31A0-31BA -| 〇 |1字| 3007 +| 类别 | 字数 | Unicode 范围 | +|:----|:----|:----| +| 基本汉字 | 20902字 | 4E00-9FA5 +| 基本汉字补充 | 74字 | 9FA6-9FEF +| 扩展A | 6582字 | 3400-4DB5 +| 扩展B | 42711字 | 20000-2A6D6 +| 扩展C | 4149字 | 2A700-2B734 +| 扩展D | 222字 | 2B740-2B81D +| 扩展E | 5762字 | 2B820-2CEA1 +| 扩展F | 7473字 | 2CEB0-2EBE0 +| 康熙部首 | 214字 | 2F00-2FD5 +| 部首扩展 | 115字 | 2E80-2EF3 +| 兼容汉字 | 477字 | F900-FAD9 +| 兼容扩展 | 542字 | 2F800-2FA1D +| PUA(GBK)部件 | 81字 | E815-E86F +| 部件扩展 | 452字 | E400-E5E8 +| PUA增补 | 207字 | E600-E6CF +| 汉字笔画 | 36字 | 31C0-31E3 +| 汉字结构 | 12字 | 2FF0-2FFB +| 汉语注音 | 43字 | 3105-312F +| 注音扩展 | 22字 | 31A0-31BA +| 〇 | 1字 | 3007 + +************************ + +# Java中的编码 +> [字符、编码和Java中的编码](https://www.jianshu.com/p/1b00ca07b003) +> [Guide to Character Encoding](https://www.baeldung.com/java-char-encoding) + +> [Java : How to determine the correct charset encoding of a stream](https://stackoverflow.com/questions/499010/java-how-to-determine-the-correct-charset-encoding-of-a-stream) +- [juniversalchardet](https://github.com/albfernandez/juniversalchardet) 通过字节流识别文件的字符集编码 + - UniversalDetector.detectCharset 原理是读取4K字节长度的二进制流,识别编码方案,还会尝试解析BOM头。 +- GuessEncoding +- ICU4j +- cn.hutool.core.io.CharsetDetector 字节流识别文件的字符集编码 + - 原理为读取512字节长度后,穷举字符集来解码 + +************************ + +# 乱码 +> 顾名思义则是用了错误的编码方案去对二进制流解码 + +![](./img/char-error-decode-situation.webp) diff --git a/Skills/CS/CompilingPrinciple.md b/Skills/CS/CompilingPrinciple.md new file mode 100644 index 0000000..3758b23 --- /dev/null +++ b/Skills/CS/CompilingPrinciple.md @@ -0,0 +1,69 @@ +--- +title: 编译原理 +date: 2019-01-06 14:33:13 +tags: +categories: + - 计算机基础 +--- + +💠 + +- 1. [编译原理](#编译原理) + - 1.1. [编译技术](#编译技术) + - 1.1.1. [JIT](#jit) + - 1.1.2. [AOT](#aot) +- 2. [词法分析](#词法分析) +- 3. [语法分析](#语法分析) + - 3.1. [AST](#ast) +- 4. [工具](#工具) + - 4.1. [Antlr](#antlr) + +💠 2023-10-22 02:47 +**************************************** +# 编译原理 + +最简单: 词法分析 -> 语法分析 -> 语义分析 -> 代码生成 + +| 前端 | 中端 | 后端 | +|:---|:---|:---| +| 词法分析 -> 语法分析 -> 语义分析 | -> 平台无关优化 -> 平台相关优化 | -> 寄存器分配 -> 代码生成 | + +![](./img/compile.drawio.svg) + +## 编译技术 +### JIT +> Just in time + +在运行时才将源码编译成机器码 + +### AOT +> Ahead of time + +预先将所源码编译成机器码 + +************************ + +# 词法分析 + +************************ + +# 语法分析 + +## AST +> Abstract Syntax Tree + +用处: 错误提示、自动补全、重构、语法检查, 代码混淆, 静态代码分析, 自动生成测试代码 ... + +> [参考: 从现在起-彻底学会 js ast](https://segmentfault.com/a/1190000017992387) +> [参考: Java代码分析器(一): JDT入门](https://segmentfault.com/a/1190000000609246) + +- [Github: java parser](https://github.com/javaparser/javaparser)`Java生成AST` + +# 工具 +## Antlr +> [Antlr](https://www.antlr.org/) `ANother Tool for Language Recognition` + +> 基于ANTLR的语法制导翻译(内嵌在语法里的语义动作(semantic action),或者是ANTLRv4的回调),还可以实现AST生成、语义检查等功能。 +这样借助ANTLR就可以基本覆盖整个编译器前端的需求了。 +>- [ANTLR涉及编译原理中的哪些部分? - RednaxelaFX的回答 - 知乎](https://www.zhihu.com/question/21580602/answer/93736714) + diff --git a/Skills/CS/Computer.md b/Skills/CS/Computer.md index 3f8c5a1..ac64f2b 100644 --- a/Skills/CS/Computer.md +++ b/Skills/CS/Computer.md @@ -1,29 +1,122 @@ -`目录 start` - -- [计算机历史](#计算机历史) - - [起源](#起源) - - [硬件系统](#硬件系统) - - [CPU](#cpu) - - [操作系统](#操作系统) - - [编程语言](#编程语言) - - [语言的的演化](#语言的的演化) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 计算机组成 +date: 2018-11-21 10:56:52 +tags: + - 基础 +categories: + - 计算机基础 +--- + +💠 + +- 1. [计算机历史](#计算机历史) + - 1.1. [起源](#起源) +- 2. [硬件系统](#硬件系统) + - 2.1. [内存](#内存) + - 2.2. [硬盘](#硬盘) + - 2.3. [CPU](#cpu) + - 2.3.1. [中断](#中断) + - 2.3.2. [MESI](#mesi) + - 2.4. [键盘](#键盘) +- 3. [操作系统](#操作系统) +- 4. [编程语言](#编程语言) + - 4.1. [语言的的演化](#语言的的演化) +- 5. [通用概念](#通用概念) + - 5.1. [缓存](#缓存) +- 6. [Tips](#tips) + - 6.1. [死机](#死机) + +💠 2024-09-30 00:05:08 **************************************** # 计算机历史 ## 起源 -## 硬件系统 +# 硬件系统 > [博客:【不周山之读薄 CSAPP】零 系列概览 ](http://wdxtub.com/2016/04/16/thin-csapp-0/) `CSAPP:深入理解计算机系统` -### CPU +## 内存 + +## 硬盘 +> 存储单位 kb kilobyte; kib kibibyte 的区别: [wikipedia](https://en.wikipedia.org/wiki/Kilobyte) + +## CPU > [码农翻身:CPU阿甘](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513017&idx=1&sn=5550ee714abd36d0b580713f673e670b&scene=21#wechat_redirect) `梳理了CPU 内存 操作系统 BIOS 硬盘之间的关系和特性` -## 操作系统 +> [versus 中央处理器对比](https://versus.com/cn/cpu)`CPU对比和天梯图` +> [cpu.userbenchmark.com](https://cpu.userbenchmark.com/)`用户基准测试公开数据` + +### 中断 +最早的CPU设计是轮询设备去处理事件(轮询键盘 鼠标 网卡等等),导致多设备多任务无法并行,响应速度也会慢 + +TODO 时钟周期 + +> 中断模式设计 +1. 硬件中断 + 1. 可屏蔽中断:常由计算机的外设或一些接口功能产生,如键盘、打印机、串行口等这种类型的中断可以在CPU要处理其它紧急操作时,被软件屏蔽或忽略 + 1. 非屏蔽中断:由意外事件导致,如电源断电、内存校验错误等 对于这种类型的中断事件,无法通过软件进行屏蔽,CPU必须无条件响应 +1. 软件中断(又称内部中断 软中断 异常): 在程序中调用INTR中断指令引起的中断 + 1. 陷阱 是一种有意的,预先安排的异常事件,一般是在编写程序时故意设下的陷阱指令,而后执行到陷阱指令后,CPU将会调用特定程序进行相应的处理,处理结束后返回到陷阱指令的下一条指令。如系统调用,程序调试功能等。 + 1. 故障 故障是在引起故障的指令被执行,但还没有执行结束时,CPU检测到的一类的意外事件。出错时交由故障处理程序处理,如果能处理修正这个错误,就将控制返回到引起故障的指令即CPU重新执这条指令。如果不能处理就报错。 + 1. 终止 执行指令的过程中发生了致命错误,不可修复,程序无法继续运行,只能终止,通常会是一些硬件的错误。终止处理程序不会将控制返回给原程序,而是直接终止原程序 + +> 中断处理过程 +1. 中断请求触发: 中断请求是由中断源向CPU发出中断请求信号。外部设备发出中断请求信号要具备以下两个条件: + 1. 外部设备的工作已经告一段落。 例如输入设备只有在启动后,将要输入的数据送到接口电路的数据寄存器(即准备好要输入的数据)之后,才可以向CPU发出中断请求。 + 1. 系统允许该外设发出中断请求。 如果系统不允许该外设发出中断请求,可以将这个外设的请求屏蔽。当这个外设中断请求被屏蔽,虽然这个外设准备工作已经完成,也不能发出中断请求。 +1. 中断响应、处理、返回 + 1. 关闭中断信号接收器 + 1. 保存现场(或称 Context、上下文) + 1. 给出中断入口,转入中断服务程序执行 + 1. 处理完成,返回并恢复现场 + 1. 开启中断信号接收器 + +> 中断排队和中断判优 + +中断申请是随机的,有时会出现多个中断源同时提出中断申请, CPU每次只能响应一个中断源的请求。 +CPU不可能对所有中断请求一视同仁,它会根据各中断源工作性质的轻重缓急,预先安排一个优先级顺序。当多个中断源同时申请中断时,即按此优先级顺序进行排队,等候CPU处理。 + +多核CPU的中断处理和单核有很大不同。多核的各处理器核心之间需要通过中断方式进行通信,所以CPU芯片内部既有各处理器核心的本地中断控制器,又有负责仲裁各核之间中断分配的全局中断控制器。 +现今的多核处理器在中断处理和中断控制方面主要使用的是APIC(Advanced Programmable Interrupt Controllers),即高级编程中断控制器。它是基于中断控制器两个基础功能单元——本地单元以及I/O单元的分布式体系结构。在多核系统中,多个本地和I/O APIC单元能够作为一个整体通过APIC总线互相操作 + +> 作用和优点 +1. 可以使CPU和外设同时工作,使系统可以及时地响应外部事件。 +1. 可允许多个外设同时工作,大大提高了CPU的利用率,也提高了数据输入、输出的速度。 +1. 可以使CPU及时处理各种软硬件故障(比如计算机在运行过程中,出现了难以预料的情况或一些故障,如电源掉电、存储出错、运算溢出等等。计算机可以利用中断系统自行处理,而不必停机或报告工作人员。) + + +### MESI +> MESI(Modified Exclusive Shared Or Invalid) + +************************ + +## 键盘 +> [Keyboard input](https://wiki.archlinux.org/index.php/Keyboard_input) + +> [go evdev](https://github.com/gvalkov/golang-evdev) +> [Python evdev](https://python-evdev.readthedocs.io/en/latest/) + +> [https://keyboardtestt.com/](https://keyboardtestt.com/)`在线测试键盘无冲` + +************************ + +# 操作系统 > [码农翻身:操作系统是个大骗子? ](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513894&idx=1&sn=3cf8faef41800f0dd52f84a0ae2d8065&chksm=80d67be5b7a1f2f31833dc71f8c67dc50e64b14bb5a25678155a7b39927b63db7c17510793d0&scene=21#wechat_redirect)`描述了程序从硬盘中读取到内存执行的整个过程` -## 编程语言 -### 语言的的演化 +************************ + +# 编程语言 +## 语言的的演化 > [码农翻身:一个翻译家族的发家史](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513576&idx=1&sn=0fd7ba43902ff7b10376810118f68d62&chksm=80d67a2bb7a1f33d90a95be040987bc03033b0174cef6ccb9018203673c8c1fe192103d3ae41&scene=21#wechat_redirect) `低级语言到高级语言的演化` +************************ + +# 通用概念 + +## 缓存 +> 常见缓存淘汰策略 先进先出策略 FIFO(First In,First Out)、最少使用策略 LFU(Least Frequently Used)、最近最少使用策略 LRU(Least Recently Used) + +# Tips +## 死机 +[计算机死机的时候,它在干什么?](https://mp.weixin.qq.com/s/XR3k0Ka2klg6LpOVw_EASg) + diff --git a/Skills/CS/Concurrent.md b/Skills/CS/Concurrent.md deleted file mode 100644 index 13421a1..0000000 --- a/Skills/CS/Concurrent.md +++ /dev/null @@ -1,37 +0,0 @@ -`目录 start` - -- [并发](#并发) - - [同步](#同步) - - [锁](#锁) - - [异步](#异步) - - [线程和进程](#线程和进程) - - [协程](#协程) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 并发 -> 无关语言 涉及 同步 异步 线程 协程 - -## 同步 -> [码农翻身:那些烦人的同步和互斥问题](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513371&idx=1&sn=c875f64af83306bffca8dd748f1462ff&chksm=80d679d8b7a1f0ce98a0e3a12409805757cd2e958586c54049121f961cf5b2d236530cd019c7&scene=21#wechat_redirect) - -> 这种对`共享变量, 共享内存,共享资源`进行访问的程序片段叫做`临界区`, 代码在进入临界区之前一定要做好同步或者互斥的操作。 -- 例如在Java JDK中, 已经对线程的同步做了封装了, 对于生产者-消费者问题,可以直接使用BlockingQueue - - 非常简单, 完全不用你去考虑这些 wait ,signal , full, empty - -### 锁 -> 锁是用来锁临界区资源的 , 而不是锁代码块, 锁函数. 那么在Java中: `synchronized` 锁住的是不同线程对同一个对象的访问 [知乎: 锁代码块和锁方法有啥区别啊?](https://www.zhihu.com/question/21295770) - -********************** -## 异步 - -**************** -## 线程和进程 - -***************** -## 协程 - -- [知乎:协程的讨论](https://www.zhihu.com/question/20511233) -- [协程以及Python实现](http://www.cnblogs.com/zingp/p/5911537.html) - - diff --git a/Skills/CS/FloatingPoint.md b/Skills/CS/FloatingPoint.md new file mode 100644 index 0000000..ba82963 --- /dev/null +++ b/Skills/CS/FloatingPoint.md @@ -0,0 +1,19 @@ +--- +title: 浮点数 +date: 2023-10-12 13:52:00 +tags: +categories: +--- + +💠 + +- 1. [Floating-Point](#floating-point) + +💠 2023-10-12 13:52 +**************************************** +# Floating-Point +> [wiki: IEEE_754](https://en.wikipedia.org/wiki/IEEE_754) + +> [Fast inverse square root](https://en.wikipedia.org/wiki/Fast_inverse_square_root)`0x5f3759df` +> [参考: 开方算法优化](https://www.cnblogs.com/virusdefender/p/3366536.html) + diff --git a/Skills/CS/Generics.md b/Skills/CS/Generics.md new file mode 100644 index 0000000..1fac7a1 --- /dev/null +++ b/Skills/CS/Generics.md @@ -0,0 +1,38 @@ +--- +title: 泛型设计 +date: 2019-04-20 12:16:10 +tags: +categories: + - 计算机基础 +--- + +💠 + +- 1. [Generics](#generics) + - 1.1. [协变 逆变](#协变-逆变) +- 2. [元编程](#元编程) + +💠 2024-05-17 19:49:18 +**************************************** + +# Generics + +> [泛型和元编程的模型:Java, Go, Rust, Swift, D等](https://zhuanlan.zhihu.com/p/287965990) +> [generics](https://thume.ca/2019/07/14/a-tour-of-metaprogramming-models-for-generics/) + +## 协变 逆变 +协变(covariant)和逆变(contravariant) [.NET 泛型中的协变和逆变](https://learn.microsoft.com/zh-cn/dotnet/standard/generics/covariance-and-contravariance) + +- 协变 是指能够使用与原始指定的派生类型相比,派生程度更大的类型。 + - 例如 String -> Object +- 逆变 是指能够使用派生程度更小的类型。 + - 例如 Object -> String + +> [Java 数组协变带来的静态类型漏洞](https://www.iteye.com/blog/rednaxelafx-379703) + +************************ + +# 元编程 + +![Alt text](./img/generic_and_meta.webp) + diff --git a/Skills/CS/IO.md b/Skills/CS/IO.md new file mode 100644 index 0000000..357562d --- /dev/null +++ b/Skills/CS/IO.md @@ -0,0 +1,257 @@ +--- +title: 计算机中的IO +date: 2019-04-20 12:16:10 +tags: + - IO +categories: + - 计算机基础 +--- + +💠 + +- 1. [计算机中的IO](#计算机中的io) + - 1.1. [IO模型](#io模型) + - 1.1.1. [Blocking IO](#blocking-io) + - 1.1.2. [Nonblocking IO](#nonblocking-io) + - 1.1.3. [IO multiplexing](#io-multiplexing) + - 1.1.4. [Signal driven IO](#signal-driven-io) + - 1.1.5. [Asynchronous IO](#asynchronous-io) + - 1.1.6. [经典比喻](#经典比喻) + - 1.2. [阻塞和非阻塞](#阻塞和非阻塞) + - 1.3. [同步和异步](#同步和异步) + - 1.4. [同异步和阻塞](#同异步和阻塞) +- 2. [多路复用](#多路复用) + - 2.1. [多路复用模型](#多路复用模型) + - 2.1.1. [Reactor](#reactor) + - 2.1.2. [Proactor](#proactor) + - 2.1.3. [忙轮询](#忙轮询) + - 2.1.4. [无差别轮询](#无差别轮询) + - 2.1.5. [最小轮询](#最小轮询) + - 2.2. [IO多路复用函数](#io多路复用函数) + - 2.2.1. [select](#select) + - 2.2.2. [poll](#poll) + - 2.2.3. [epoll](#epoll) + +💠 2024-06-16 16:40:46 +**************************************** +# 计算机中的IO +> [参考: IO - 同步,异步,阻塞,非阻塞 ](https://blog.csdn.net/historyasamirror/article/details/5778378) + +IO在计算机中指 Input/Output,也就是输入和输出。由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接口。 + +> [Linux IO](/Linux/Base/LinuxFile.md#io) + +## IO模型 + +- Blocking IO +- Nonblocking IO +- IO multiplexing +- Signal driven IO +- Asynchronous IO + +> [参考: 使用异步 I/O 大大提高应用程序的性能](https://www.ibm.com/developerworks/cn/linux/l-async/) + +### Blocking IO +> Blocking IO的特点就是在IO执行的两个阶段都被 block了 + +### Nonblocking IO +> 用户进程不断询问内核数据有没有好, 一旦kernel中的数据准备好了,并且又再次收到了用户进程的询问(system call),那么它马上就将数据拷贝到了用户内存,然后返回。 + +### IO multiplexing +> 当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。 + +在IO multiplexing Model中,实际中,对于每一个socket,一般都设置成为non-blocking,但是 整个用户的process其实是一直被block的。只不过process是被select这个函数block,而不是被socket IO被block。 + +如果处理的连接数不是很高的话,使用select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。 +select/epoll的优势`不在于单个连接能处理得更快,而在于能处理更多的连接`。 + +### Signal driven IO +> 使用较少 + +在信号驱动IO模型中,当用户线程发起一个IO请求操作,会给对应的socket注册一个信号函数,然后用户线程会继续执行 +当内核数据就绪时会发送一个信号给用户线程,用户线程接收到信号之后,便在信号函数中调用IO读写操作来进行实际的IO请求操作。 + +### Asynchronous IO +> [参考: 异步AIO的研究](http://rango.swoole.com/archives/282) + +用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。 +然后kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。 + +AIO适用于 IO操作量大,读写过程长的场景,但是缺点是应用系统处理异步API麻烦。 + +### 经典比喻 +有 A,B,C,D 四个人在钓鱼 (BIO, NIO, IO multiplexing, AIO) : +- A 用的是最老式的鱼竿(只有线和竿),所以得一直守着,等到鱼上钩了再拉杆; +- B 用的鱼竿有浮漂,B就能旁边泡茶,隔会再看看有没有鱼上钩,有的话就迅速拉杆; +- C 用的鱼竿和B差不多,但他想了一个好办法,`就是同时放好几根鱼竿`,然后守在旁边,一旦提示鱼上钩了,它就将对应的鱼竿拉起来; 这样一个人就能处理好多个鱼竿 +- D 是个有钱人,干脆雇了一个人帮他钓鱼,一旦那个人把鱼钓上来了,就给D发个短信。 + +************************ + +## 阻塞和非阻塞 +阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态. + +阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。 +非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。 + +调用 blocking IO 会一直 block 住对应的进程直到操作完成, 该操作就是阻塞的 +而 non-blocking IO 在 kernel 还未准备好数据的情况下会立刻返回, 该操作就是非阻塞的 + +## 同步和异步 +同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication) + +所谓同步,就是在发出一个*调用*时,在没有得到结果之前,该*调用*就不返回。但是一旦调用返回,就得到返回值了。 换句话说,就是由*调用者*主动等待这个*调用*的结果。 +异步则是调用者调用就返回了,等操作系统来完成IO行为,并通知调用者(由于应用场景多变,这种方式下会受限于操作系统的调度不可控,所以一直没有得到广泛应用)。 + +在说明synchronous IO和asynchronous IO的区别之前,需要先给出两者的定义。Stevens给出的定义(其实是POSIX的定义)是这样子的: +``` + A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes; + An asynchronous I/O operation does not cause the requesting process to be blocked; +``` + +两者的区别就在于 synchronous IO 做 `IO operation` 的时候会将应用的 process 阻塞。 +按照这个定义,之前所述的 blocking IO,non-blocking IO,IO multiplexing `都属于 synchronous IO`。 +`在处理 IO 的时候,阻塞和非阻塞都是同步 IO`,只有使用特定API才是异步 IO: Linux aio, Windows IOCP + +有人可能会说,non-blocking IO 并没有被 block, 这是因为在第一阶段, 数据没有准备好就直接返回这里没有阻塞, 等数据准备好了就要执行第二阶段 数据复制 的操作, 这个操作是会阻塞对应的进程 +而 asynchronous IO 则不一样,当进程发起 IO 操作之后,就直接返回,直到 kernel 发送一个信号,通知应用进程说IO完成 `也就是数据复制完成后才通知` +在这整个过程中,进程完全没有被block。 + +************************ +## 同异步和阻塞 +> [Linux AIO](https://oxnz.github.io/2016/10/13/linux-aio/) + +| | 阻塞 | 非阻塞 | +|:---|:---|:---| +| 同步 | read/write | read/write
(O_NONBLOCK) | +| 异步 | I/O multiplexing
(select/poll/epoll) | AIO | + +阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待; +同步,异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。 + +> [知乎: 阻塞非阻塞和同步异步的区别](https://www.zhihu.com/question/19732473/answer/117012135) + +讨论究竟是异步还是同步,一定要严格说明说的是哪一部分。 +`非阻塞是同步而不是异步`,这毫无疑问是正确的,然而说某个框架是异步IO的框架,这也是正确的,因为说的其实是框架提供给业务代码的接口是异步的 +不管是回调还是协程,比如说我们可以说某个库是异步的HTTPClient,并没有什么问题,因为说的是给业务代码的接口。 + +> [参考](https://www.zhihu.com/question/19732473/answer/88599695) + +我认为同步、异步、阻塞、非阻塞,是分3个层次的:CPU层次;线程层次;程序员感知层次。 这几个概念之所以容易混淆,是因为没有分清楚是在哪个层次进行讨论。 + +**CPU层次** + +在CPU层次,或者说操作系统进行IO和任务调度的层次,现代操作系统通常使用异步非阻塞方式进行IO(有少部分IO可能会使用同步非阻塞轮询),即发出IO请求之后,并不等待IO操作完成,而是继续执行下面的指令(非阻塞),IO操作和CPU指令互不干扰(异步),最后通过中断的方式来通知IO操作完成结果。 + +**线程层次** + +在线程层次,或者说操作系统调度单元的层次,操作系统为了减轻程序员的思考负担,将底层的异步非阻塞的IO方式进行封装,把相关系统调用(如read,write等)以同步的方式展现出来。然而,同步阻塞的IO会使线程挂起,同步非阻塞的IO会消耗CPU资源在轮询上。为了解决这一问题,就有3种思路:多线程(同步阻塞);IO多路复用(select,poll,epoll)(同步非阻塞,严格地来讲,是把阻塞点改变了位置);直接暴露出异步的IO接口,如kernel-aio和IOCP(异步非阻塞)。 + +**程序员感知层次** + +在Linux中,上面提到的第2种思路用得比较广泛,也是比较理想的解决方案。然而,直接使用select之类的接口,依然比较复杂,所以各种库和框架百花齐放,都试图对IO多路复用进行封装。此时,库和框架提供的API又可以选择是以同步的方式还是异步的方式来展现。如python的asyncio库中,就通过协程,提供了同步阻塞式的API;如node.js中,就通过回调函数,提供了异步非阻塞式的API。 + +总结因此,我们在讨论同步、异步、阻塞、非阻塞时,必须先明确是在哪个层次进行讨论。比如node.js,我们可以说她在程序员感知层次提供了异步非阻塞的API,也可以说在Linux下,她在线程层次以同步非阻塞的epoll来实现。 + +************************ + +# 多路复用 +> [参考: IO多路复用](https://blog.csdn.net/chewbee/article/details/78108223) + +## 多路复用模型 +Reactor 和 Proactor: 前者 使用同步IO, 后者使用异步IO + +> [Comparing Two High-Performance I/O Design Patterns](https://www.artima.com/articles/io_design_patternsP.html) + +> [高性能网络模式:Reactor 和 Proactor](https://www.xiaolincoding.com/os/8_network_system/reactor.html) + +Reactor 可以理解为「来了事件操作系统通知应用进程,让应用进程来处理」,而 Proactor 可以理解为「来了事件操作系统来处理,处理完再通知应用进程」。 +因此,真正的大杀器还是 Proactor,它是采用异步 I/O 实现的异步网络模型,感知的是已完成的读写事件,而不需要像 Reactor 感知到事件后,还需要调用 read 来从内核中获取数据。 +不过,无论是 Reactor,还是 Proactor,都是一种基于「事件分发」的网络编程模式,区别在于 Reactor 模式是基于「待完成」的 I/O 事件,而 Proactor 模式则是基于「已完成」的 I/O 事件。 + +### Reactor +- Redis:单 Reactor 单进程 +- Netty & Memcache: 主从多Reactor 多线程 +- Nginx: 主从Reactor 多进程 *进程职责做了调整* + +> [【Netty】模型篇一:Netty 线程模型架构 & 工作原理 解读](https://blog.csdn.net/qq_36389060/article/details/124232377)`包含了Reactor多种模式的图` + +![](/Skills/CS/img/001-reactor-multiple.drawio.svg) + +> [TCP Server处理多Client请求的方法—非阻塞accept与select](http://velep.com/archives/1137.html)`可以看到系统调用层面也是先调用select发现新连接后调用accept和read write` [Github: Code](https://github.com/Kuangcp/LearnC/blob/master/network/tcp/nio-tcp.c) + +### Proactor + + +### 忙轮询 +忙轮询方式是通过不停的把所有的流从头到尾轮询一遍,查询是否有流已经准备就绪,然后又从头开始。如果所有流都没有准备就绪,那么只会白白浪费CPU时间。 + +### 无差别轮询 +为了避免白白浪费CPU时间,我们采用另外一种轮询方式,无差别的轮询方式。即通过引进一个代理,这个代理为 select/poll ,这个代理可以同时观察多个流的I/O事件。 +当所有的流都没有准备就绪时,会把当前线程阻塞掉;当有一个或多个流的I/O事件就绪时,就从阻塞状态中醒来,然后轮询一遍所有的流,处理已经准备好的I/O事件。 + +如果I/O事件准备就绪,那么我们的程序就会阻塞在select处。我们通过select那里只是知道了有I/O事件准备好了,但不知道具体是哪几个流(可能有一个,也可能有多个),所以需要无差别的轮询所有的流,找出已经准备就绪的流。 +可以看到,使用select时,我们需要O(n)的时间复杂度来处理流,需要处理的流越多,消耗的时间也就越多。 + +### 最小轮询 + +通过epoll方式来观察多个流,epoll`只把发生了I/O事件的流`通知我们,我们对这些流的操作都是有意义的,时间复杂度降低到O(k),其中k为产生I/O事件的流个数。 + +## IO多路复用函数 +> [参考: IO多路复用之select、poll、epoll详解](https://www.cnblogs.com/jeakeven/p/5435916.html) +> [参考: select、poll、epoll之间的区别总结](https://www.cnblogs.com/Anker/p/3265058.html) + +IO复用机制可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立即通知相应程序进行读或写操作。 +select/poll/epoll都是采用I/O多路复用机制的,其中select/poll是采用无差别轮询方式,而epoll是采用最小的轮询方式。 +但 select,poll,epoll本质上都是同步I/O, 因为他们都需要在读写事件就绪后`自己`负责进行读写,也就是说这个读写过程是阻塞的 +所以 select poll epoll 都是 Reactor 模型 + +************************ + +### select + +系统提供Select函数来实现多路复用输入/输出模型,Select系统调用是用来让我们的程序监视多个文件句柄的状态变化。程序会阻塞在select函数上,直到被监视的文件句柄中有一个或多个发生了状态变化。 + +`缺点` +1. 每次调用select,都需要把fd集合(存放所有fd)从用户态拷贝到内核态,这个开销在fd很多时会很大 +1. 同时每次调用select, 都需要在内核`遍历`传递进来的所有fd,这个开销在fd很多时也很大 +1. select支持的文件描述符数量太小了,默认是1024, 由FD_SETSIZE设置 + +************************ + +### poll +Poll的处理机制与Select类似,只是Poll选择了 pollfd 结构体 而不是 select 的 fd_set 结构来处理文件描述符的相关操作 + +但是它没有最大连接数的限制,原因是它是`基于链表`来存储的, 但是同样的需要将所有的文件描述符复制来复制去 + +poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。 + +************************ + +### epoll +> [参考: 从 linux 源码看 epoll](https://my.oschina.net/alchemystar/blog/3008840) + +epoll是在Linux内核2.6引进的,是select和poll函数的增强版。与select相比,epoll没有文件描述符数量的限制。 +epoll使用一个文件描述符管理多个文件描述符,将用户关心的文件描述符事件存放到`内核的一个事件列表`中 +这样在用户空间和内核空间`只需拷贝一次`, 因为用户空间和内核空间共用一块内存 + +epoll提供了三个函数: +- epoll_create是创建一个epoll句柄; +- epoll_ctl是注册要监听的事件类型; +- epoll_wait则是等待事件的产生。 + +epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。 +还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。 + +epoll的优点: +1. 没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存能监听约10万个端口)。 +2. 效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。只有活跃可用的FD才会调用callback函数; + - 即Epoll最大的优点就在于它`只处理“活跃”的连接`,而跟连接总数无关,因此在实际的网络环境中,epoll 的效率就会远远高于 select和poll。 +3. 内存拷贝,利用 mmap() 文件映射内存加速与内核空间的消息传递;即 epoll 使用 mmap 减少复制开销。 + +> 如果没有大量的 idle-connection 或者 dead-connection,epoll 的效率并不会比 select/poll 高很多 +> 但是当遇到大量的 idle-connection,就会发现 epoll 的效率大大高于 select/poll。 + +> [百万 Go TCP 连接的思考2: 百万连接的吞吐率和延迟 ](https://colobu.com/2019/02/27/1m-go-tcp-connection-2/) + + diff --git a/Skills/CS/Network.md b/Skills/CS/Network.md deleted file mode 100644 index 043dc28..0000000 --- a/Skills/CS/Network.md +++ /dev/null @@ -1,137 +0,0 @@ -`目录 start` - -- [网络](#网络) - - [基础](#基础) - - [ISO七层模型和TCP/IP五层模型](#iso七层模型和tcpip五层模型) - - [TCP和UDP](#tcp和udp) - - [IPv4 & IPv6](#ipv4-&-ipv6) - - [ARP](#arp) - - [网络延迟](#网络延迟) - - [TTFB](#ttfb) - - [URL](#url) - - [移动通信技术规格](#移动通信技术规格) - - [Web安全](#web安全) - - [HTTP](#http) - - [HTTP的返回码](#http的返回码) - - [HTTP的缓存](#http的缓存) - - [Session和Cookie](#session和cookie) - - [HTTP1.1 和 HTTP2](#http11-和-http2) - - [HTTPS](#https) - - [HSTS](#hsts) - - [CORS](#cors) - - [Websocket](#websocket) - - [WSS](#wss) - - [WebDAV](#webdav) - - [WebAssembly](#webassembly) - -`目录 end` |_2018-09-06_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 网络 - -## 基础 -> [码农翻身:小白科普:从输入网址到最后浏览器呈现页面内容,中间发生了什么?](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665514196&idx=1&sn=ca26d258fcc4a35fc6d9a539b7d71dd7&chksm=80d67c97b7a1f58198b2e6ae436f73c677c0df4c05c2a8a4aad2b9e2d523da57dd5cd3d0a8ee&mpshare=1&scene=1&srcid=0122nnRpNb6OvRJubkSfKfsZ&pass_ticket=%2B%2FAmfhAaNv2sKw6192eqEL9hoW%2F6BrLxlzHIsKC0k6lPQsM4%2FFo08R%2FZowzw3821#rd) | -> [码农翻身:我是一个路由器](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513173&idx=1&sn=6ec5281b12ed5195070fa4df22383595&scene=21#wechat_redirect) | -> [码农翻身:我是一个网卡](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513160&idx=1&sn=d938db4f1a2d62514b57e92fd8d3d749&scene=21#wechat_redirect) - -### ISO七层模型和TCP/IP五层模型 -ISO制定的OSI参考模型的过于庞大、复杂招致了许多批评。与此对照,由技术人员自己开发的TCP/IP协议栈获得了更为广泛的应用。 -> [参考博客: 对比](https://www.cnblogs.com/qishui/p/5428938.html) - -### TCP和UDP -[参考博客: 区别](http://www.cnblogs.com/bizhu/archive/2012/05/12/2497493.html) - -### IPv4 & IPv6 -> [参考博客: 浏览器访问IPv6地址](http://www.cnblogs.com/cuihongyu3503319/p/7422877.html) - -### ARP -> 将IP和MAC对应起来的协议 - -### 网络延迟 -> [如何彻底解决「网络延迟」这个问题?](https://www.zhihu.com/question/34689035) - -- [MOBA类游戏是如何解决网络延迟同步的?](https://www.zhihu.com/question/36258781) -- [状态同步与帧同步](http://www.cnblogs.com/sevenyuan/p/5283265.html) - -#### TTFB -> `Time to first byte` 网络请求被发起到从服务器接收到第一个字节这段时间,它包含了 TCP连接时间,发送HTTP请求时间和获得响应消息第一个字节的时间。 - -### URL -> [维基百科](https://en.wikipedia.org/wiki/URL) | [百度百科](https://baike.baidu.com/item/URL) - -- 统一资源定位符 特别注意URL的组成和编解码 [url中的特殊字符问题](http://www.cnblogs.com/xmphoenix/archive/2011/04/20/2022945.html) - - 不能在URL的关键位置出现%号,作为参数的值是允许的。 - -#### 移动通信技术规格 -> 1g 2g 2.5g 2.75g 3g 4g 5g - -> [参考: 1G, 2G, 3G, 4G, & 5G Explained ](https://www.lifewire.com/1g-vs-2g-vs-2-5g-vs-3g-vs-4g-578681) -> [参考: Difference Between 1G, 2G, 3G vs. 4G and 5G](Difference Between 1G, 2G, 3G vs. 4G and 5G) - -******************************* -## Web安全 -> [完整的系统化信息](/Skills/CS/WebSecurity.md) - -************************** -## HTTP -> HyperText Transfer Protocol 超文本传输协议 具体格式为: head + body - -HTTP/1.1协议中共定义了八种方法(有时也叫“动作”)来表明Request-URI指定的资源的不同操作方式: -  OPTIONS 返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。  -  HEAD 向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在不必传输整个响应内容的情况下,就可以获取包含在响应消息头中的元信息。  -  GET 向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在web app.中。其中一个原因是GET可能会被网络蜘蛛等随意访问。  -  POST 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。  -  PUT 向指定资源位置上传其最新内容。  -  DELETE 请求服务器删除Request-URI所标识的资源。  -  TRACE 回显服务器收到的请求,主要用于测试或诊断。  -  CONNECT HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。  -  方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed);当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。 - -- [ ] Header中一些主要属性的含义和使用场景 -### HTTP的返回码 -> [完整列表 ](/FrontEnd/ResponseCode.md) - -### HTTP的缓存 - -### Session和Cookie - -### HTTP1.1 和 HTTP2 -> 目前大多国内厂商默认的还是1.1, aws和google使用了2 而且2一般也都会使用上https - -HTTP2的特点: -1. 无阻塞的Multiplexing请求队列 _可以异步加载CSS和JS_ -2. server端可以push资源给client端 - -[参考博客: HTTP/2](http://www.hollischuang.com/archives/2066) - -### HTTPS -> [SSL/TSL](/Skills/CS/WebSecurity.md#ssl和tsl) - -#### HSTS -> HTTP Strict Transport Security 强制让客户端使用HTTPS进行通信 - -### CORS -> 跨域 - -> [mozilla CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS) -> [阮一峰 跨域资源共享 CORS 详解](http://www.ruanyifeng.com/blog/2016/04/cors.html) -> [CORS详解.md](https://github.com/hstarorg/HstarDoc/blob/master/%E5%89%8D%E7%AB%AF%E7%9B%B8%E5%85%B3/CORS%E8%AF%A6%E8%A7%A3.md) -********************************** -## Websocket -> 本质就是TCP的简单封装, 不像HTTP那样应答模式, 一次连接后就保持全双工模式 - -1. 单一的TCP连接, 采用全双工模式通信 -2. 对代理, 防火墙和路由器透明 -3. 无头部信息, Cookie, 身份验证 -4. 无安全开销 -5. 通过 ping/pong 帧 保持链路激活 -6. 服务器可以主动传递消息给客户端, 不需要客户端轮询 - -### WSS - -## WebDAV - -WebDAV (Web-based Distributed Authoring and Versioning) 一种基于 HTTP 1.1协议的通信协议.它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可直接对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的版本控制。 -## WebAssembly -> 字节码技术 -> [ WebAssembly 实践:如何写代码 ](https://segmentfault.com/a/1190000008402872) -> [MDN](https://developer.mozilla.org/en-US/docs/WebAssembly) diff --git a/Skills/CS/OperatingSystem/Readme.md b/Skills/CS/OperatingSystem/Readme.md new file mode 100644 index 0000000..b676822 --- /dev/null +++ b/Skills/CS/OperatingSystem/Readme.md @@ -0,0 +1,12 @@ +# 操作系统 +虚拟化 +并发 +持久性 + + +《深入理解计算机系统》 +《操作系统导论》 +《现代操作系统》 + +> [图解系统](https://www.xiaolincoding.com/os/) + diff --git a/Skills/CS/Profile.md b/Skills/CS/Profile.md index 56710d0..65ff2d1 100644 --- a/Skills/CS/Profile.md +++ b/Skills/CS/Profile.md @@ -1,44 +1,101 @@ -`目录 start` - -- [配置文件](#配置文件) - - [conf或者ini](#conf或者ini) - - [properties](#properties) - - [XML](#xml) - - [YAML](#yaml) - - [Java使用](#java使用) - - [JSON](#json) - - [Jackson](#jackson) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 多种配置文件格式 +date: 2018-11-21 10:56:52 +tags: + - 基础 +categories: + - 计算机基础 +--- + +**目录 start** + +1. [配置文件格式](#配置文件格式) + 1. [conf或者ini](#conf或者ini) + 1. [Toml](#toml) + 1. [HOCON](#hocon) + 1. [Properties](#properties) + 1. [Java中的使用](#java中的使用) + 1. [XML](#xml) + 1. [YAML](#yaml) + 1. [Java中的使用](#java中的使用) + 1. [JSON](#json) + 1. [BSON](#bson) + 1. [Smile](#smile) + +**目录 end**|_2020-07-05 14:58_| **************************************** -# 配置文件 +# 配置文件格式 ## conf或者ini + +```ini + [main] + debug=true + [client] + timeOut=10 ``` -[main] -debug=true -[client] -timeOut=10 + +************************ +## Toml +> [Github: toml](https://github.com/toml-lang/toml) + +## HOCON +Human-Optimized Config Object Notation + +> [Official Doc](https://docs.spongepowered.org/stable/zh-CN/server/getting-started/configuration/hocon.html) + +> Nginx 的配置文件就是使用该格式 + +************************ +## Properties + +### Java中的使用 + +通过ResourceBundle获取classPath下的属性文件 +```java + ResourceBundle bundle = ResourceBundle.getBundle("test"); + String city = bundle.getString("name"); +``` + +通过Properties对象获取配置文件 +```java + Properties pro = new Properties(); + pro.load(new FileInputStream(new File("./test.properties"))); + String name = (String) pro.get("name"); ``` -## properties +使用Properties保存配置文件 +```java + Properties pro = new Properties(); + pro.setProperty("name", "java"); + pro.setProperty("study", "sdf"); + pro.store(new FileOutputStream(new File("test.properties")), "one file"); +``` +************************ ## XML > 可阅读性强, 结构清晰, 但是太繁杂, 信息承载比重小 +************************ ## YAML > yaml is ain't markup language - [入门博客](http://blog.csdn.net/liukuan73/article/details/78031693) - [Python使用YML](http://www.cnblogs.com/c9com/archive/2013/01/05/2845539.html) -### Java使用 +### Java中的使用 - Springboot将这种配置文件引入了我的视野,使用这个用来自定义配置文件要特别注意采用小写(不然影响反射中set方法) - [Jackson操作yaml](https://dzone.com/articles/read-yaml-in-java-with-jackson) +************************ + ## JSON > [Google 规范](https://github.com/darcyliu/google-styleguide/blob/master/JSONStyleGuide.md) -### Jackson -- [Jackcon 注解的讲解](http://blog.csdn.net/sdyy321/article/details/40298081) +> [JSON in Java](https://www.baeldung.com/java-json) + +### BSON + +### Smile +> 二进制的JSON [Wikipedia: Smile](https://en.wikipedia.org/wiki/Smile_%28data_interchange_format%29) diff --git a/Java/AdvancedLearning/ProgramThinking.md b/Skills/CS/ProgramThinking.md similarity index 70% rename from Java/AdvancedLearning/ProgramThinking.md rename to Skills/CS/ProgramThinking.md index b0b613a..237d2e8 100644 --- a/Java/AdvancedLearning/ProgramThinking.md +++ b/Skills/CS/ProgramThinking.md @@ -1,40 +1,54 @@ -`目录 start` - -- [开发思想](#开发思想) - - [抽象](#抽象) - - [命令式编程和响应式编程](#命令式编程和响应式编程) - - [响应式编程](#响应式编程) - - [面向过程](#面向过程) - - [面向对象](#面向对象) - - [OOP](#oop) - - [面向过程和面向对象的对比](#面向过程和面向对象的对比) - - [DDD 领域驱动设计](#ddd-领域驱动设计) - - [聚合](#聚合) - - [参考实践项目](#参考实践项目) - - [数据的操作](#数据的操作) - - [CURD](#curd) - - [CQRS](#cqrs) - - [组件模型](#组件模型) - - [SOA](#soa) - - [MSA](#msa) - - [Other](#other) - - [国际化的配置](#国际化的配置) -- [设计软件的方法](#设计软件的方法) - - [契约式设计](#契约式设计) - - [精益思想](#精益思想) -- [编程习惯](#编程习惯) - - [晓风轻的经验](#晓风轻的经验) - - [接口定义](#接口定义) - - [日志建议](#日志建议) - - [异常处理](#异常处理) - - [工具类规范](#工具类规范) - - [代码质量分析](#代码质量分析) - - [Checkstyle](#checkstyle) - - [FindBugs](#findbugs) - - [阿里巴巴的代码检查](#阿里巴巴的代码检查) - - [配置文件](#配置文件) - -`目录 end` |_2018-09-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: ProgramThinking +date: 2018-11-21 10:56:52 +tags: +categories: + - Engineering +--- + +**目录 start** + +1. [开发思想](#开发思想) + 1. [抽象](#抽象) + 1. [编程范式](#编程范式) + 1. [编程思想](#编程思想) + 1. [面向过程](#面向过程) + 1. [面向对象](#面向对象) + 1. [OOP](#oop) + 1. [面向过程和面向对象的对比](#面向过程和面向对象的对比) + 1. [DDD 领域驱动设计](#ddd-领域驱动设计) + 1. [聚合](#聚合) + 1. [参考实践项目](#参考实践项目) + 1. [数据的操作](#数据的操作) + 1. [CURD](#curd) + 1. [CQRS](#cqrs) + 1. [组件模型](#组件模型) + 1. [SOA](#soa) + 1. [MSA](#msa) + 1. [Other](#other) + 1. [国际化的配置](#国际化的配置) +1. [设计软件的方法](#设计软件的方法) + 1. [契约式设计](#契约式设计) + 1. [精益思想](#精益思想) +1. [编程习惯](#编程习惯) + 1. [晓风轻的经验](#晓风轻的经验) + 1. [接口定义](#接口定义) + 1. [日志建议](#日志建议) + 1. [异常处理](#异常处理) + 1. [工具类规范](#工具类规范) + 1. [代码质量分析](#代码质量分析) + 1. [Checkstyle](#checkstyle) + 1. [FindBugs](#findbugs) + 1. [阿里巴巴的代码检查](#阿里巴巴的代码检查) + 1. [配置文件](#配置文件) + 1. [软件版本](#软件版本) +1. [编程语言](#编程语言) + 1. [类型之分](#类型之分) + 1. [解释型和编译型](#解释型和编译型) + 1. [强类型和弱类型](#强类型和弱类型) + 1. [动态和静态类型](#动态和静态类型) + +**目录 end**|_2020-04-27 23:42_| **************************************** # 开发思想 > 有关开发的理论性思想,编写,测试,部署等 @@ -53,31 +67,22 @@ - [计算机科学中抽象的好处与问题—伪共享实例分析](http://ifeve.com/%e8%ae%a1%e7%ae%97%e6%9c%ba%e7%a7%91%e5%ad%a6%e4%b8%ad%e6%8a%bd%e8%b1%a1%e7%9a%84%e5%a5%bd%e5%a4%84%e4%b8%8e%e9%97%ae%e9%a2%98-%e4%bc%aa%e5%85%b1%e4%ba%ab%e5%ae%9e%e4%be%8b%e5%88%86%e6%9e%90/)`计算机科学中的任何问题都可以通过加上一层间接层来解决,这是很正确的,但是也正是因为一层一层的抽象和包装,导致出了问题后很难定位,你都不知道问题究竟是出现在哪一层。所以要想提高技术水平不仅要知其然(看得见最顶层的包装)也要知其所以然(看得见底层的包装),每一层如果都懂或者说了解一些,那么出了问题很大程度上都可以凭直觉定位,即使不能凭直觉也可以通过各种手段debug,只会最顶层的抽象很多时候就只能望bug兴叹了。` +************************ -## 命令式编程和响应式编程 -### 响应式编程 -> [ReactiveX](http://reactivex.io/intro.html) - -组合异步的序列 -设计模式是 观察者模式的扩展, 数据结构是序列串流, 避免了并发, 是非阻塞的 - -数据流驱动 - - -异步 -- 非阻塞 - - 不是 (同步非阻塞 : 当时不阻塞后续回调) 而是异步 - -多路复用 +## 编程范式 +> [编程范式详情](/Skills/ProgrammingParadigm.md) ********************* -## 面向过程 +## 编程思想 +### 面向过程 > 只有数据和函数, 使用函数改变数据状态 -## 面向对象 +一种以过程为中心的编程思想 + +### 面向对象 > OO Object Oriented -> [参考博客: 再见面向对象编程?](http://www.jdon.com/48231) +> [参考: 再见面向对象编程?](http://www.jdon.com/48231) > 思考: >- 遇到需求时, 先分析需要哪些独立的实体, 然后分析用户的行为, 行为就是API @@ -86,9 +91,10 @@ >>- 例如 `活动` 具有 开始时间和结束时间 的需求, 如果只用时间去设计行为的话, 测试的时候就需要去模拟那些时间, 如果引入`状态`这个属性(开启,关闭) >>- 就可以方便的调试了, 只需更改这个状态即可控制 活动 这个对象的行为, 当然, 变化的时间也是通过控制 `状态` 来控制 活动 的 -`编写出完成需求的代码不难, 难的是写出, 优雅, 简洁, 设计良好, 可读性, 扩展性高的代码` +- 面向对象被广泛应用于大型项目, 但是大型项目也不应只有这一种设计思想 +- `编写出完成需求的代码不难, 难的是写出, 优雅, 简洁, 设计良好, 可读性, 扩展性高的代码` -### OOP +#### OOP > [维基 OOP](https://en.wikipedia.org/wiki/Object-oriented_programming) | [中文版](https://zh.wikipedia.org/wiki/%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1) ### 面向过程和面向对象的对比 @@ -106,14 +112,15 @@ ************************************************* ## DDD 领域驱动设计 > [领域驱动设计(DDD:Domain-Driven Design)](http://www.jdon.com/ddd.html)`入门贴` +> [领域驱动设计:软件核心复杂性应对之道](https://github.com/gdut-yy/Domain-Driven-Design-zh) > [领域驱动设计 软件核心复杂性应对之道 Eric J. Evans 在线阅读](http://ishare.iask.sina.com.cn/f/69200951.html) > [领域驱动设计精简版 ](http://www.infoq.com/cn/minibooks/domain-driven-design-quickly) > [参考博客](http://kb.cnblogs.com/page/117717/) | [讨论](http://www.cnblogs.com/netfocus/p/3307971.html) | [基础](http://www.cnblogs.com/netfocus/archive/2011/10/10/2204949.html) -> [参考博客: 危险的DDD聚合根](http://www.cnblogs.com/netfocus/archive/2012/09/08/2676985.html) 初步感受是DDD禁不起变化, 必须要在起初就设计好一个完备的体系 -> [参考博客: DDD应用的思考](http://www.jdon.com/47313)`提出了关于领域设计的困惑` +> [参考: 危险的DDD聚合根](http://www.cnblogs.com/netfocus/archive/2012/09/08/2676985.html) 初步感受是DDD禁不起变化, 必须要在起初就设计好一个完备的体系 +> [参考: DDD应用的思考](http://www.jdon.com/47313)`提出了关于领域设计的困惑` ### 聚合 聚合根的修改行为应该属于聚合根实体对象自己,用聚合根行为守护其内部状态的一致性是DDD设计核心,如果聚合根内部的状态直接暴露给外界(通过领域服务)任意修改,那么会导致状态变化混乱,难以调试和跟踪。 @@ -134,8 +141,8 @@ > [event-sourcing](https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing)| [中文版](https://docs.microsoft.com/zh-cn/azure/architecture/patterns/event-sourcing) `微软关于azure的技术性文档` > [event-sourcing-in-practice](https://ookami86.github.io/event-sourcing-in-practice/) -> [参考博客: CQRS & Event Sourcing ](https://www.cnblogs.com/netfocus/category/361988.html) -> [参考博客: 领域驱动设计的实践 – CQRS & Event Sourcing](https://www.jianshu.com/p/9a3f8d514fcd) `图文并茂的讲解CQRS思想` +> [参考: CQRS & Event Sourcing ](https://www.cnblogs.com/netfocus/category/361988.html) +> [参考: 领域驱动设计的实践 – CQRS & Event Sourcing](https://www.jianshu.com/p/9a3f8d514fcd) `图文并茂的讲解CQRS思想` > [eventapis](https://github.com/kloiasoft/eventapis)`Java实现的CQRS` > [CQRS journey](http://cqrsjourney.github.io/) `微软团队的项目` @@ -143,7 +150,7 @@ ************************* ## 组件模型 ### SOA -> [参考博客: SOA面向服务架构](http://www.jdon.com/soa.html) +> [参考: SOA面向服务架构](http://www.jdon.com/soa.html) _[Spring Web 应用的最大败笔](http://www.jdon.com/45857)_ - 传统意义上的SOA 内部封装的是数据表的DTO 也被称为 失血模型,贫血模型, 从而导致SOA服务内部腐烂堵塞,违背SOA自治和可用性等原则约束 @@ -157,7 +164,7 @@ _[Spring Web 应用的最大败笔](http://www.jdon.com/45857)_ ### MSA > 微服务 -> [参考博客: SOA 与 MSA(微服务架构)](https://blog.csdn.net/ztguang/article/details/52894794) +> [参考: SOA 与 MSA(微服务架构)](https://blog.csdn.net/ztguang/article/details/52894794) > [码农翻身:从SOA到微服务](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513674&idx=1&sn=fbc727b7c8ff6d03f5d53478b6d4e585&chksm=80d67a89b7a1f39ff0c3589a4a4076e323fab18379fc8d085c133b88e4db104f87988b29d246&scene=21#wechat_redirect) - [码农翻身:我是一个函数](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513873&idx=1&sn=2383f099fb353e59649167e723575158&chksm=80d67bd2b7a1f2c4ae61704b8a2bd330764d20f0e2fafa6fdff55c99ea68272b3cff851684cc&scene=21#wechat_redirect) `详解了RPC, 也就是RMI(远程过程调用)规范的实现` @@ -235,7 +242,45 @@ _[Spring Web 应用的最大败笔](http://www.jdon.com/45857)_ ### 阿里巴巴的代码检查 -********************************* +************************ ## 配置文件 + 千万业务代码里面不要和读取配置的代码耦合在一起。切记! + +************************ + +1. 优秀工程师所具备的能力: + - 将现实问题转换为计算机问题的数学能力 + - 对于语言本身的工具链的熟悉程度(最简单) + - 计算机体系知识的掌握能力 + - 对所有经验进行形而上的能力 + +************************ + +## 软件版本 +> [语义化版本 SemVer ](https://semver.org/lang/zh-CN/) + +************************ + +# 编程语言 + +## 类型之分 +> [弱类型、强类型、动态类型、静态类型语言的区别是什么?](https://www.zhihu.com/question/19918532) + +### 解释型和编译型 +- 在 80 90 年代,边界较为清晰,类C语言是编译型,Perl和Python是解释型。但Java是两者都有 +- 基于JVM来划分的边界是:该语言是否将源码编译为类文件并且执行,不产生类文件的语言会由解释器逐行执行。有些语言既有编译器又有解释器,有些是既有解释器又有产生字节码的即时编译器JIT + +### 强类型和弱类型 +- `强类型` 偏向于不容忍隐式类型转换。 + - 例如Python中int就不能直接转为string,Java同理 但是Java有(编译期)语法糖会在需要的时候隐式添加toString +- `弱类型` 偏向于容忍隐式类型转换。 + - 例如C语言中 int 和 char 可以互换 + +### 动态和静态类型 +- 动态类型语言,变量在不同的时间可能会有不同的类型 动态类型语言是跟踪变量的值的类型信息,静态类型语言是跟踪变量的类型信息 +- 静态类型 在编译期就确定下变量的类型,因为类型错误而无法做的事情是语法错误 +- 动态类型 在运行期才能确定变量的类型,编译期不能得以确定,因为类型错误而无法做的事情是运行时错误 + +************************ diff --git a/Skills/CS/Time.md b/Skills/CS/Time.md new file mode 100644 index 0000000..c74a392 --- /dev/null +++ b/Skills/CS/Time.md @@ -0,0 +1,71 @@ +--- +title: 时间 +date: 2018-12-20 10:44:57 +tags: + - Time +categories: + - 计算机基础 +--- + +💠 + +- 1. [时间](#时间) + - 1.1. [基础概念](#基础概念) + - 1.1.1. [GMT](#gmt) + - 1.1.2. [UTC](#utc) + - 1.2. [时区](#时区) + - 1.2.1. [CDT](#cdt) + - 1.2.2. [CST](#cst) + - 1.2.3. [DST](#dst) + +💠 2024-09-12 16:01:31 +**************************************** +# 时间 +> 上下四方为宇,古往今来为宙 + +计算机内部的计时器为石英钟,存在误差,所以需要依靠NTP服务来对时(例如: `sudo /usr/sbin/ntpdate -u cn.pool.ntp.org`) + + +## 基础概念 +### GMT +> 格林尼治标准时间(Greenwich Mean Time,GMT)是指位于伦敦郊区的皇家格林尼治天文台的标准时间,因为本初子午线被定义在通过那里的经线。 + +为了方便,在不需要精确到秒的情况下,通常将 GMT 和 UTC 视作等同。但 UTC 更加科学更加精确,它是以原子时为基础,在时刻上尽量接近世界时的一种时间计量系统。它的出现是现代社会对于精确计时的需要。 + +### UTC +> [Coordinated Universal Time](https://en.wikipedia.org/wiki/Coordinated_Universal_Time) + +协调世界时(为了协调世界时和原子时 采用闰秒实现),又称世界统一时间、世界标准时间、国际协调时间, 也会被称为"Zulu time" + +常见格式为 1997-07-16T19:20+01:00 末尾标识了时间的时区 + +> 实际地区无关的夏令时缩写: +- 主要的夏令时包括EDT(Eastern Daylight Time),CDT(Central Daylight Time ),PDT(Pacific Daylight Time)。非夏令时包括CST(Central Standard Time),PST(Pacific Standard Time)。 + +************************ + +## 时区 +### CDT + +中国标准时间(CST)和中国夏令时(CDT) + +1986年至1991年,中华人民共和国在全国范围实行了六年夏令时,每年从4月中旬的第一个星期日2时整(北京时间)到9月中旬第一个星期日的凌晨2时整(北京夏令时)。除1986 +年因是实行夏令时的第一年,从5月4日开始到9月14日结束外,其它年份均按规定的时段施行。1992年4月5日后不再实行。 + +### CST +> 中国处于东八区 所以是 UTC+8 + +但是CST其实可以表示四个时区, 被MySQL中的CST坑了才发现到这个`十四个小时问题`, 但是Linux以及大多数软件中的CST都是指UTC+8 + +| 时间 | 全称 | 时区 | +|:----|:----|:----| +| 美国中部时间 | Central Standard Time (USA) | UTC-6:00 +| 澳大利亚中部时间 | Central Standard Time (Australia) | UTC+9:30 +| 中国标准时间 | China Standard Time | UTC+8:00 +| 古巴标准时间 | Cuba Standard Time | UTC-4:00 + +### DST +> DST是Daylight Saving Time的缩写,称为阳光节约时,在我国称为夏时制,又称夏令时,是一种为节约能源而人为调整地方时间的制度。 + +> [Daylight saving time](https://en.wikipedia.org/wiki/Daylight_saving_time) + diff --git a/Skills/CS/Virtualization.md b/Skills/CS/Virtualization.md new file mode 100644 index 0000000..a1ffb54 --- /dev/null +++ b/Skills/CS/Virtualization.md @@ -0,0 +1,36 @@ +--- +title: 虚拟化技术 +date: 2019-05-27 13:44:23 +tags: +categories: + - 虚拟化 +--- + +**目录 start** + +1. [虚拟化技术](#虚拟化技术) + 1. [QEMU](#qemu) + 1. [KVM](#kvm) + 1. [XEN](#xen) + 1. [HVM](#hvm) + 1. [Proxmox](#proxmox) + +**目录 end**|_2020-06-24 02:06_| +**************************************** +# 虚拟化技术 + +> 虚拟硬盘格式 +- vhd: VirtualPC, Hyper-V, Xen, VirtualBox +- vmdk: VMWare +- qcow2: Qemu, KVM + +## QEMU + +## KVM + +## XEN + +## HVM + +## Proxmox VE +> [Official](https://www.proxmox.com/en/)`虚拟机管理平台` diff --git a/Skills/CS/WebSecurity.md b/Skills/CS/WebSecurity.md deleted file mode 100644 index ff41379..0000000 --- a/Skills/CS/WebSecurity.md +++ /dev/null @@ -1,89 +0,0 @@ -`目录 start` - -- [Web安全](#web安全) - - [Authenticate](#authenticate) - - [SSL和TSL](#ssl和tsl) - - [ARP断网攻击](#arp断网攻击) - - [SYNFlood攻击](#synflood攻击) - - [CSRF](#csrf) - - [XSS](#xss) - - [JWT](#jwt) - -`目录 end` |_2018-09-12_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Web安全 -> 关注常见的比如 XSS CSRF SQL注入 上传等问题的原理和修复方案。还有密码安全也基本上是面试必考点。 -> 作为开发人员,需要详细了解安全问题的原理。 比如XSS的原理是因为用户将它的数据变成了代码,在页面中跑起来了,所以就可以为所欲为。 CSRF则是当用户不知情时,被黑客的网页通过图片、表单等请求时, -> 用户的登录态(Cookies)在不知情的情况下会被发送到服务器,导致用户在不知情的情况下被利用身份。 点击支持则是网页被嵌入到了其他网站中,并通过视觉隐藏的方式引导用户进行一些不知情的操作。 -> 上传导致的漏洞是因为用户的文件没有做好判断和处理,导致传上来的文件被当成程序执行了。 SQL注入是用户的数据被当成了表示SQL语义的部分,改变了原来的查询语句的语义,从而产生意料之外的结果。 -> 反向代理服务器,构建在web服务器与 客户端之间,保护web服务器,服务器发送到客户端的请求被代理 - -## Authenticate - -> [WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) - -## SSL和TSL -> [SSL/TLS协议运行机制的概述](http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html) - -## ARP断网攻击 -> [ARP 断网攻击的原理是什么?如何完全防护?](https://www.zhihu.com/question/20338649) - -******************** -## SYNFlood攻击 -> 洪水攻击 [参考博客](http://xfocus.net/articles/200106/208.html) SYN Flood是当前最流行的DoS(拒绝服务攻击)与DDoS(分布式拒绝服务攻击)的方式之一,这是一种利用TCP协议缺陷,发送大量伪造的TCP连接请求,从而使得被攻击方资源耗尽(CPU满负荷或内存不足)的攻击方式。 -> [参考博客](http://www.cnblogs.com/popduke/p/5823801.html) - -- Linux: - - 修改文件 `sudo vim /etc/sysctl.conf ` - - 将注释取消 修改值: `net.ipv4.tcp_syncookies = 0` - - 就能提高并发总量,但是并发量还是不能提高 -```conf - net.ipv4.tcp_syncookies = 0 - #此参数是为了防止洪水攻击的,但对于大并发系统,要禁用此设置 - net.ipv4.tcp_max_syn_backlog=1024 - #参数决定了SYN_RECV状态队列的数量,一般默认值为512或者1024,即超过这个数量,系统将不再接受新的TCP连接请求,一定程度上可以防止系统资源耗尽。可根据情况增加该值以接受更多的连接请求。 - net.ipv4.tcp_tw_recycle=0 - #参数决定是否加速TIME_WAIT的sockets的回收,默认为0。 - net.ipv4.tcp_tw_reuse=0 - #参数决定是否可将TIME_WAIT状态的sockets用于新的TCP连接,默认为0。 - net.ipv4.tcp_max_tw_buckets - #参数决定TIME_WAIT状态的sockets总数量,可根据连接数和系统资源需要进行设置。 -``` -### CSRF -> CSRF (Cross Site Request Forgery) `跨站请求伪造` ,它讲的是你在一个浏览器中打开了两个标签页,其中一个页面通过窃取另一个页面的 cookie 来发送伪造的请求, -> 因为 cookie 是随着请求自动发送到服务端的。 - -> [维基百科定义 CSRF](https://www.owasp.org/index.php/Cross-Site_Request_Forgery) | -> [百度百科 CSRF](https://baike.baidu.com/item/CSRF) - -> [[Web 安全] 如何通过JWT防御CSRF](https://segmentfault.com/a/1190000003716037) -> [web安全之token和CSRF攻击](https://blog.csdn.net/qq_15096707/article/details/51307024) -> [博客:CSRF漏洞的原理](https://www.zhuyingda.com/blog/b5.html) -> [浅谈CSRF攻击方式](http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html) -> [参考提问下的回答](https://segmentfault.com/q/1010000000713614) - -- [ ] 问题是 CSRF 只是非法获取Cookie做操作么, 自己用Nginx配置两个域名的web页面试试 CSRF - -### XSS -> Cross Site Scripting `跨站脚本攻击` - -> [xss攻击入门](http://www.cnblogs.com/bangerlee/archive/2013/04/06/3002142.html) -> [ XSS攻击及防御 ](https://blog.csdn.net/ghsau/article/details/17027893) -> [最新的黑客技术:详解XSS跨站脚本攻击 ](http://soft.yesky.com/security/hkjj/136/2233136.shtml) - - -## JWT -> [理解JWT的使用场景和优劣](http://www.qingpingshan.com/rjbc/java/384762.html) - -- [Blog:通过使用JWT来防御CSRF](https://segmentfault.com/a/1190000003716037) -- [Blog:介绍JWT](blog.leapoahead.com/2015/09/06/understanding-jwt/)`其实JWT还经常用于设计用户认证和授权系统,甚至实现Web应用的单点登录。` -- [Blog:单点登录](http://blog.leapoahead.com/2015/09/07/user-authentication-with-jwt/) -- [Web 安全之 XSS、CSRF 和 JWT](https://juejin.im/entry/58e67673a22b9d00588e7148) - -> [参考博客: 开箱即用 - jwt 无状态分布式授权](http://www.cnblogs.com/grissom007/p/6294746.html) - -> 需要注意的是,不是什么数据都适合放在 Cookie、localStorage 和 sessionStorage 中的。使用它们的时候,需要时刻注意是否有代码存在 XSS 注入的风险。 -> 因为只要打开控制台,你就随意修改它们的值,也就是说如果你的网站中有 XSS 的风险,它们就能对你的 localStorage 肆意妄为。所以千万不要用它们存储你系统中的敏感数据 - - - diff --git a/Skills/CS/img/001-reactor-multiple.drawio.svg b/Skills/CS/img/001-reactor-multiple.drawio.svg new file mode 100644 index 0000000..bd82b32 --- /dev/null +++ b/Skills/CS/img/001-reactor-multiple.drawio.svg @@ -0,0 +1,540 @@ + + + + + + + + + + + + + + + +
+
+
+ 请求 +
+
+
+
+ + 请求 + +
+
+ + + + +
+
+
+ Client +
+
+
+
+ + Client + +
+
+ + + + + + +
+
+
+ Client +
+
+
+
+ + Client + +
+
+ + + + + + +
+
+
+ Client +
+
+
+
+ + Client + +
+
+ + + + + +
+
+
+ 建立连接 +
+
+
+
+ + 建立连接 + +
+
+ + + + + + + +
+
+
+ MainReactor +
+
+
+
+ + MainReactor + +
+
+ + + + +
+
+
+ select +
+
+
+
+ + select + +
+
+ + + + +
+
+
+ dispatch +
+
+
+
+ + dispatch + +
+
+ + + + + +
+
+
+ Acceptor +
+
+
+
+ + Acceptor + +
+
+ + + + +
+
+
+ accept +
+
+
+
+ + accept + +
+
+ + + + +
+
+
+ Reactor 主线程 +
+
+
+
+ + Reactor 主线程 + +
+
+ + + + + +
+
+
+ 处理请求 +
+
+
+
+ + 处理请求 + +
+
+ + + + + +
+
+
+ SubReactor +
+
+
+
+ + SubReactor + +
+
+ + + + +
+
+
+ select +
+
+
+
+ + select + +
+
+ + + + +
+
+
+ dispatch +
+
+
+
+ + dispatch + +
+
+ + + + + + + + + +
+
+
+ Handler +
+
+
+
+ + Handler + +
+
+ + + + +
+
+
+ read +
+
+
+
+ + read + +
+
+ + + + +
+
+
+ Reactor 子线程 +
+
+
+
+ + Reactor 子线程 + +
+
+ + + + +
+
+
+ send +
+
+
+
+ + send + +
+
+ + + + + + +
+
+
+ Worker 线程 +
+
+
+
+ + Worker 线程 + +
+
+ + + + +
+
+
+ Worker 线程 +
+
+
+
+ + Worker 线程 + +
+
+ + + + + + + +
+
+
+ Handler +
+
+
+
+ + Handler + +
+
+ + + + +
+
+
+ read +
+
+
+
+ + read + +
+
+ + + + +
+
+
+ send +
+
+
+
+ + send + +
+
+ + + + + +
+
+
+ 处理请求 +
+
+
+
+ + 处理请求 + +
+
+ + + + + +
+
+
+ write +
+
+
+
+ + write + +
+
+ + + + +
+
+
+ 业务逻辑 +
+
+
+
+ + 业务逻辑 + +
+
+ + + + + +
+
+
+ write +
+
+
+
+ + write + +
+
+ + + + +
+
+
+ 业务逻辑 +
+
+
+
+ + 业务逻辑 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/Skills/CS/img/char-error-decode-situation.webp b/Skills/CS/img/char-error-decode-situation.webp new file mode 100644 index 0000000..45af232 Binary files /dev/null and b/Skills/CS/img/char-error-decode-situation.webp differ diff --git a/Skills/CS/img/compile.drawio.svg b/Skills/CS/img/compile.drawio.svg new file mode 100644 index 0000000..ce739e4 --- /dev/null +++ b/Skills/CS/img/compile.drawio.svg @@ -0,0 +1,437 @@ + + + + + + + + +
+
+
+ 记号 +
+
+
+
+ + 记号 + +
+
+ + + + +
+
+
+ 词法分析 +
+
+
+
+ + 词法分析 + +
+
+ + + + + +
+
+
+ 动作 +
+
+
+
+ + 动作 + +
+
+ + + + +
+
+
+ 语法分析 +
+
+
+
+ + 语法分析 + +
+
+ + + + + +
+
+
+ 抽象语法树 +
+
+
+
+ + 抽象语法树 + +
+
+ + + + +
+
+
+ 语法树构造 +
+
+
+
+
+ + 语法树构造 + +
+
+ + + + + +
+
+
+ 中间树 +
+
+
+
+ + 中间树 + +
+
+ + + + +
+
+
+ 语义分析 +
+
+
+
+ + 语义分析 + +
+
+ + + + + +
+
+
+ 中间树 +
+
+
+
+ + 中间树 + +
+
+ + + + +
+
+
+ 翻译 +
+
+
+
+ + 翻译 + +
+
+ + + + + +
+
+
+ 中间树 +
+
+
+
+ + 中间树 + +
+
+ + + + +
+
+
+ 正规化 +
+
+
+
+ + 正规化 + +
+
+ + + + + +
+
+
+ 抽象汇编 +
+
+
+
+ + 抽象汇编 + +
+
+ + + + +
+
+
+ 指令选择 +
+
+
+
+ + 指令选择 + +
+
+ + + + + +
+
+
+ 控制流图 +
+
+
+
+ + 控制流图 + +
+
+ + + + +
+
+
+ 控制流分析 +
+
+
+
+ + 控制流分析 + +
+
+ + + + + +
+
+
+ 寄存器指派 +
+
+
+
+ + 寄存器指派 + +
+
+ + + + +
+
+
+ 寄存器分析 +
+
+
+
+ + 寄存器分析 + +
+
+ + + + + +
+
+
+ 汇编代码 +
+
+
+
+ + 汇编代码 + +
+
+ + + + +
+
+
+ 代码生成 +
+
+
+
+ + 代码生成 + +
+
+ + + + + + +
+
+
+ 汇编器 +
+
+
+
+ + 汇编器 + +
+
+ + + + + + +
+
+
+ 连接器 +
+
+
+
+ + 连接器 + +
+
+ + + + + +
+
+
+ 可重定位目标代码 +
+
+
+
+ + 可重定位目标代码 + +
+
+ + + + +
+
+
+ 机器代码 +
+
+
+
+ + 机器代码 + +
+
+
+ + + + + Text is not SVG - cannot display + + + +
\ No newline at end of file diff --git a/Skills/CS/img/generic_and_meta.webp b/Skills/CS/img/generic_and_meta.webp new file mode 100644 index 0000000..f2d6b04 Binary files /dev/null and b/Skills/CS/img/generic_and_meta.webp differ diff --git a/Skills/Cache/Cache.md b/Skills/Cache/Cache.md new file mode 100644 index 0000000..34349da --- /dev/null +++ b/Skills/Cache/Cache.md @@ -0,0 +1,133 @@ +--- +title: Cache +date: 2019-05-13 11:15:40 +tags: +categories: +--- + +💠 + +- 1. [缓存](#缓存) + - 1.1. [缓存淘汰算法](#缓存淘汰算法) +- 2. [应用场景](#应用场景) + - 2.1. [客户端](#客户端) + - 2.2. [服务端](#服务端) + - 2.3. [数据库](#数据库) + - 2.4. [分布式缓存](#分布式缓存) + - 2.4.1. [Ehcache](#ehcache) + - 2.4.2. [Redis cluster](#redis-cluster) +- 3. [场景](#场景) + - 3.1. [Hot Key](#hot-key) + - 3.2. [BigKey](#bigkey) + - 3.3. [缓存雪崩 Cache Avalanche](#缓存雪崩-cache-avalanche) + - 3.4. [缓存穿透 Cache Penetration](#缓存穿透-cache-penetration) + - 3.5. [缓存击穿/崩溃 Cache Breakdown](#缓存击穿崩溃-cache-breakdown) + - 3.6. [缓存一致性](#缓存一致性) + +💠 2024-09-06 11:36:43 +**************************************** +# 缓存 +> 用时效和空间换时间 + +> 从Web应用系统的流程上可分为 客户端 服务端 数据库 + +## 缓存淘汰算法 +- FIFO `First In First Out` 先进先出 + - 优点:实现简单 + - 缺点:未考虑数据访问频率和时间,和实际场景不贴和。 +- LRU `Least Recently Used` 最近最久未使用 + - 实现方式:维护一个所有数据的链表,新数据插入到链表的头部,如果缓存满了,就会从链表尾部开始移除数据。 + - 优点:LRU考虑了最近的数据访问模式,对于局部性原理的表现优秀,简单实用 + - 缺点:不能体现数据的访问频率,如果数据最近被访问过,即使访问频度低也不会被淘汰。 + - 如果一个数据在一分钟的前59秒被频繁访问,而在最后一秒无任何访问,但是有一批冷门数据在最后一秒进入缓存,那么热点数据可能会被淘汰掉。 +- SLRU `Segmented LRU` 分段LRU + - 实现方式:类似于内存分代,将缓存分为淘汰段,保护段,两个段都是LRU实现。 + - 数据访问第一次被放入淘汰段,访问第二次才会升级到保护段 + - 保护段的数据要淘汰时,数据复制到淘汰段。淘汰段的数据要淘汰时就会被直接删除。 + - 优点:抵御冷门数据引起热门数据被淘汰 + - 缺点:两段的容量设计需要更参考业务场景做调优 + +- LFU `Least Frequently Used` 最近最少频率使用 + - 实现方式:对每个在缓存中的数据进行计数,记录其被访问的次数 + - 优点:LFU能够较好地处理长期访问稳定、频率较高的情况,因为这样可以确保频繁访问的数据不容易被淘汰。 + - 缺点:对于一些暂时高频访问但之后不再访问的数据,LFU无法有效处理。因为这些数据的访问次数已经非常高,不容易被淘汰,可能造成缓存空间的浪费。 + - 并且LFU需要维护所有对象的访问计数,这可能会消耗比较多的存储空间和计算资源。 +- W-TinyLFU ` Window Tiny Least Frequently Used` [论文 TinyLFU: A Highly Efficient Cache Admission Policy](http://arxiv.org/pdf/1512.00727v2) + - 实现方式: [W-TinyLFU缓存淘汰策略 ](https://juejin.cn/post/7144327955353698334) + - 优点: + - 平衡了最近性和频率:与 LRU 相比,不仅考虑了使用频率,还计算了缓存的热门程度。与 LFU 相比,不会让以前非常热门但现在很少使用的数据长时间占用缓存。 + - 额外内存占用小: TinyLFU 使用一个固定大小的计数滤波器来跟踪访问频率,这使得其内存占用远低于传统的 LFU 策略。 + - 适应性强:可以更好地适应工作负载的变化,因为它对频率的计数有一个时间窗口 + - 避免缓存污染:由于它维护了一个 admission window,它可以避免一次性的、大规模的请求可能带来的缓存污染。 + - 缺点: + - 需要维护额外的频率信息,SLRU等,增加了一些开销。 + - 不如 LRU 算法实现简单。对于不同的使用场景,需要调整参数以获得最佳性能。 + + +************************ + +# 应用场景 +## 客户端 +> 浏览器 +- 静态资源缓存,Cookie,各种 *Storage, IndexDB + +> PC端原生 +- 配置文件,SQLite,LevelDB + +## 服务端 + +- 应用内存: Java的 GuavaCache Caffeine + +- [Redis](/Database/Redis.md) +- [Memcache](/Database/Memcache.md) + +************************ + +## 数据库 +会话缓存 + +MySQL Buffer Pool + +************************ + +## 分布式缓存 +> 使用分布式共识算法构建出一个集群,将读写压力摊分到集群内节点上。 + +- [tinykv](https://github.com/talent-plan/tinykv) 构建分布式 Key-Value 数据库的教程 + +### Ehcache + +### Redis cluster +一致性Hash实现数据在分区上的均匀分布 + +************************ + +# 场景 +## Hot Key +海量用户请求同一个key,引发整个链路的 网卡 带宽 CPU 出现瓶颈 + +- 统计出hot key 将这批key分散在集群内多个节点上 +- 多级缓存,将流量的请求链路缩短 + +## BigKey +Value的内存占用很大,读写,加载容易超时的key + +- 拆分Key + +## 缓存雪崩 Cache Avalanche +缓存层大量缓存过期,或者整个缓存层崩溃, 导致大量流量直接访问上游资源层 + +## 缓存穿透 Cache Penetration +缓存层和上游资源层都没有当前查询的数据 + +## 缓存击穿/崩溃 Cache Breakdown +缓存层部分数据过期了,并且突增大量流量直接访问上游资源层,去查询这部分过期的数据 + +## 缓存一致性 +> [缓存和数据库一致性问题,看这篇就够了 ](http://kaito-kidd.com/2021/09/08/how-to-keep-cache-and-consistency-of-db/) + +> [参考: 关于缓存可靠性、关乎数据一致性 ](https://juejin.cn/post/7152670651302543397) + +- 数据库更新 缓存更新 +- 数据库更新 缓存删除 + diff --git a/Skills/CelebrityQuotes.md b/Skills/CelebrityQuotes.md index 0edd6ca..374f24e 100644 --- a/Skills/CelebrityQuotes.md +++ b/Skills/CelebrityQuotes.md @@ -1,14 +1,22 @@ -`目录 start` - -- [名言](#名言) +--- +title: 名人名言 +date: 2018-11-21 10:56:52 +tags: +categories: +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [名言](#名言) + +**目录 end**|_2020-11-01 00:05_| **************************************** # 名言 +> [对开发人员有用的定律、理论](https://github.com/nusr/hacker-laws-zh) 1. [David Wheeler](https://en.wikipedia.org/wiki/David_Wheeler_%28computer_scientist%29) -2. All problems in computer science can be solved by another level of indirection. -3. 计算机科学中的任何问题都可以通过加上一层间接层来解决 + 1. All problems in computer science can be solved by another level of indirection. + 1. 计算机科学中的任何问题都可以通过加上一层间接层来解决 -**** +1. 安迪-比尔定理 (Andy and Bill’s Law)是对IT产业中软件和硬件升级换代关系的一个概括。原话是 “Andy gives, Bill takes away.(安迪提供什么,比尔拿走什么。) diff --git a/Skills/Councurrency/Concurrency.md b/Skills/Councurrency/Concurrency.md new file mode 100644 index 0000000..05eda78 --- /dev/null +++ b/Skills/Councurrency/Concurrency.md @@ -0,0 +1,105 @@ +--- +title: 并发 +date: 2019-01-16 00:00:04 +tags: +categories: +--- + +💠 + +- 1. [并发](#并发) + - 1.1. [同步](#同步) + - 1.2. [异步](#异步) + - 1.3. [线程和进程](#线程和进程) + - 1.4. [协程](#协程) + - 1.5. [锁](#锁) + - 1.5.1. [死锁](#死锁) + - 1.6. [伪共享](#伪共享) +- 2. [并发实践](#并发实践) + - 2.1. [C100K](#c100k) + +💠 2024-10-17 10:43:43 +**************************************** +# 并发 +> 并发编程的理论基础 无关语言 + +> [高并发的哲学原理](https://github.com/johnlui/PPHC) + +## 同步 +> [码农翻身:那些烦人的同步和互斥问题](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513371&idx=1&sn=c875f64af83306bffca8dd748f1462ff&chksm=80d679d8b7a1f0ce98a0e3a12409805757cd2e958586c54049121f961cf5b2d236530cd019c7&scene=21#wechat_redirect) + +> 这种对`共享变量, 共享内存,共享资源`进行访问的程序片段叫做`临界区`, 代码在进入临界区之前一定要做好同步或者互斥的操作。 +- 例如在Java JDK中, 已经对线程的同步做了封装了, 对于生产者-消费者问题,可以直接使用BlockingQueue + - 非常简单, 完全不用你去考虑这些 wait, signal, full, empty + +************************ + +## 异步 +Future +promise async 与 await + +************************ + +## 线程和进程 + +************************ + +## 协程 + +- [知乎:协程的讨论](https://www.zhihu.com/question/20511233) +- [协程以及Python实现](http://www.cnblogs.com/zingp/p/5911537.html) + +- [Go 实现](/Go/GoBase.md#协程) +- [Kotlin 实现](https://github.com/Kotlin/kotlinx.coroutines) +- [Python 实现](/Python/PythonConcurrency.md#协程-asyncio) +- [Java 实现](/Java/AdvancedLearning/JavaThread.md#协程) + +************************ + +## 锁 +> 锁是用来锁临界区资源的 , 而不是锁代码块, 锁函数. 那么在Java中: `synchronized` 锁住的是不同线程对同一个对象的访问 [知乎: 锁代码块和锁方法有啥区别啊?](https://www.zhihu.com/question/21295770) + +### 死锁 + +1. 什么是死锁 +1. 死锁的四个必要条件 + 1. 资源互斥 + - `资源`指线程对已经获取到的资源进行排它性使用,即该资源同时只由一个线程占用。如果此时还有其他线程请求获取该资源,则请求者只能等待,直至占有资源的线程释放该资源。 + 1. 不可剥夺 + - `资源`指线程获取到的资源在自己使用完之前不能被其他线程抢占,只有在自己使用完毕后才由自己释放该资源。 + 1. 持有资源与请求新资源 + - `线程`指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新资源已被其他线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放自己已经获取的资源。 + 1. 环路等待 + - `线程`指在发生死锁时,必然存在一个线程—资源的环形链,即线程集合{T0,T1, T2, …, Tn}中的T0正在等待一个T1占用的资源,T1正在等待T2占用的资源,……Tn正在等待已被T0占用的资源。 + +************************ + +## 伪共享 +缓存行,字节对齐 + +************************ + +# 并发实践 +> 在抽象角度考虑并发 + +- [并发编程网](http://ifeve.com/) + +> [参考: 解道 并发编程](https://www.jdon.com/concurrency.html) + +> 在过去的30年里,计算机的性能是在摩尔定律的推动下,从现在开始,这将由Amdahl定律决定。编写代码,有效地利用多个处理器可以是非常具有挑战性的。" -- Doron Rajwan + +进行多核多服务器时代,并行并发模式更是对程序员的挑战,现在所谓的Thread Programmer世界上也是为数不多,因为线程表面上好像很容易,但在实际应用中真正应付大负荷运算时,原来的线程设计方案漏洞百出。 + +并发concurrency属于问题域(problem domain), 并行parallelism属于( solution domain)。 +并行和并发的区别在于有无状态,并行计算适合无状态应用,而并发解决的是有状态的高性能; 有状态要着力解决并发计算,无状态要着力并行计算 + +并发主要是要解决资源争夺,并发一般发生在数据聚合的地方,只要有聚合,就有争夺发生,传统解决争夺的方式采取线程锁机制,这是强行对CPU管理线程人为干预,线程唤醒成本高,新的无锁并发策略来源于Java的NIO或Node.js,通过队列+单线程操作资源的方式巧妙避免了多线程,由于只有一个线程,在多核情况下增加了并行计算的机会。 + +并发模型分两种,并发基础线程和并发组件模型,并发组件模型让使用者接触不到基础线程概念,是一种简化。作为一个高级集成架构师,应该在程序范式的高级层次定义并发,基于组件级别的并发,并且尽可能地避免涉及线程概念以及线程池等底层概念。 + +实现并发的途径有两种,基于线程和基于事件 [基于线程与基于事件的并发编程之争](https://www.jdon.com/46921) + + +## C100K +> [参考: 使用四种框架分别实现百万websocket常连接的服务器](https://www.cnblogs.com/cnsanshao/p/4652305.html) + diff --git a/Skills/DevOps/ContinuousDelivery.md b/Skills/DevOps/ContinuousDelivery.md deleted file mode 100644 index 5b0a54c..0000000 --- a/Skills/DevOps/ContinuousDelivery.md +++ /dev/null @@ -1,9 +0,0 @@ -`目录 start` - -- [Continuous Delivery](#continuous-delivery) - -`目录 end` |_2018-08-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# Continuous Delivery -> [Continuous Delivery Overview](https://github.com/mockito/mockito/wiki/Continuous-Delivery-Overview) - diff --git a/Skills/DevOps/ContinuousDeployment.md b/Skills/DevOps/ContinuousDeployment.md deleted file mode 100644 index b466059..0000000 --- a/Skills/DevOps/ContinuousDeployment.md +++ /dev/null @@ -1,13 +0,0 @@ -`目录 start` - -- [传统部署](#传统部署) -- [Continuous Deployment](#continuous-deployment) - -`目录 end` |_2018-08-09_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 传统部署 -- 打包成jar或者war,下载JDK或者JRE 绿色解压即用,将jar/war文件复制到bin目录下 - - 后台运行 `start /b java -jar fileName` - -# Continuous Deployment - diff --git a/Skills/DevOps/ContinuousIntegration.md b/Skills/DevOps/ContinuousIntegration.md index 46c9ebd..ee718e4 100644 --- a/Skills/DevOps/ContinuousIntegration.md +++ b/Skills/DevOps/ContinuousIntegration.md @@ -1,25 +1,33 @@ -`目录 start` - -- [持续集成](#持续集成) - - [Jenkins](#jenkins) - - [GoCD](#gocd) - - [Drone](#drone) - - [flow.ci](#flowci) - - [三方平台](#三方平台) -- [代码质量管理](#代码质量管理) - - [sonarqube](#sonarqube) - - [小型项目目前使用的方案](#小型项目目前使用的方案) - -`目录 end` |_2018-09-13_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: ContinuousIntegration +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [持续集成](#持续集成) + - 1.1. [Jenkins](#jenkins) + - 1.2. [GoCD](#gocd) + - 1.3. [Drone](#drone) + - 1.4. [flow.ci](#flowci) + - 1.5. [三方平台](#三方平台) +- 2. [代码质量管理](#代码质量管理) + - 2.1. [Bug跟踪](#bug跟踪) + - 2.2. [sonarqube](#sonarqube) + - 2.2.1. [项目端](#项目端) +- 3. [测试平台](#测试平台) + +💠 2024-09-03 14:05:18 **************************************** # 持续集成 -> 参考博客: [持续集成](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html) | [持续集成服务 Travis CI 教程](http://www.ruanyifeng.com/blog/2017/12/travis_ci_tutorial.html) +> 参考: [持续集成](http://www.ruanyifeng.com/blog/2015/09/continuous-integration.html) | [持续集成服务 Travis CI 教程](http://www.ruanyifeng.com/blog/2017/12/travis_ci_tutorial.html) > [廖雪峰 使用Travis进行持续集成](https://www.liaoxuefeng.com/article/0014631488240837e3633d3d180476cb684ba7c10fda6f6000) > 目前个人理解: 使用jenkins 结合gradle docker ,一键上传代码之后自动构建得到镜像 > [利用Travis CI更新github page](https://github.com/steveklabnik/automatically_update_github_pages_with_travis_example) - 使用bitbucket配置私有仓库,在hub上配置docker文件的目录,进行构建,这样就会得到一个可用的镜像 - - 源码是过去了,构建呢,这是个问题,可以使用Jenkins么? ************************** ## Jenkins @@ -28,28 +36,88 @@ ## GoCD > [Github:GoCD](https://github.com/GoCD) -> [参考博客: GoCD的正确打开方式](https://insights.thoughtworks.cn/the-right-interpretation-of-gocd/) +> [参考: GoCD的正确打开方式](https://insights.thoughtworks.cn/the-right-interpretation-of-gocd/) -> [参考博客: GoCD概念篇](http://www.cnblogs.com/elisun/p/7071536.html) +> [参考: GoCD概念篇](http://www.cnblogs.com/elisun/p/7071536.html) ************************ ## Drone > [官网](https://drone.io/) +go语言实现,一个原生支持 docker 的 CI + +> [参考: Drone 一个原生支持 docker 的 CI](https://aisensiy.github.io/2017/08/04/drone-best-ci/) +> [参考: Drone CI + GitLab持续集成的基础设施搭建](https://zmcdbp.com/drone-ci-gitlab-base-build/) | [参考: Drone CI的持续集成的基本使用](https://zmcdbp.com/drone-ci-basic-use/) ******************* ## flow.ci > [官网](https://flow.ci/) | [文档](https://github.com/FlowCI/docs/blob/master/intro_base.md) +************************ +- [gopub](https://gitee.com/dev-ops/gopub) +- [gokins](https://gitee.com/gokins/gokins) ## 三方平台 - [appveyor](https://ci.appveyor.com/projects) +> [Gradle + Travis CI 学习笔记](https://upupming.site/2018/04/03/gradle-travis/#travis-ci) **************************** # 代码质量管理 +## Bug跟踪 +- [bugzilla](https://bugzilla.readthedocs.io/en/latest/installing/quick-start.html) +- redmine + ## sonarqube -> [官网](https://www.sonarqube.org/) +> [官网](https://www.sonarqube.org/) | [Docker Hub](https://hub.docker.com/_/sonarqube/) + +> 快速使用 (H2 内存数据库存储) +1. `docker run -d --name sonarqube -p 9000:9000 sonarqube:8-community` + +> 使用 PG 数据库存储 +- docker run --name db_sonar -e POSTGRES_USER=sonar -e POSTGRES_PASSWORD=sonar -d postgres +- docker run --name sonarqube_test --link db_sonar -e SONARQUBE_JDBC_URL=jdbc:postgresql://db_sonar:5432/sonar -e SONARQUBE_JDBC_USERNAME=sonar -e SONARQUBE_JDBC_PASSWORD=sonar -p 9000:9000 -d sonarqube:8-community + +> 调整虚拟内存 +- sysctl -w vm.max_map_count=524288 + +> [Sonarqube badge not working on github README](https://stackoverflow.com/questions/58908668/sonarqube-badge-not-working-on-github-readme) +- 公开项目并且设置:Force user authentication" in Administration -> Configuration -> General settings -> Security, has to be set to false +- 私有项目支持Badges [sonarqube-badges](https://github.com/taptap/sonarqube-badges) + +### 项目端 +1. [sonarscanner](https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-maven/) + 1. Maven构建的过程会输出当前分析项目的结果URL + +> 第一种 sonar-scanner 命令工具 方式 +1. 配置 基础配置 `/etc/sonar-scanner/sonar-scanner.properties` +```ini +sonar.host.url=http://localhost:9000 +sonar.sourceEncoding=UTF-8 +sonar.login=admin +sonar.password=admin +``` +2. 配置环境变量 +``` +export SONAR_SCANNER_HOME="/opt/sonar-scanner" +export PATH="${SONAR_SCANNER_HOME}/bin:${PATH}" +``` +3. 配置项目根路径 `sonar-project.properties` +```ini +sonar.projectKey=com.github.kuangcp.gobase +sonar.projectName=GoBase +sonar.sources=. +sonar.java.binaries=. +``` + +> 第二种 [Maven插件方式](https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-maven/) + +************************ + +> [参考: 有赞 GO 项目单测、集成、增量覆盖率统计与分析](https://cloud.tencent.com/developer/article/1684515) +> [sonar Go](https://docs.sonarqube.org/latest/analysis/languages/go/) + +************************ -### 小型项目目前使用的方案 -- 在开发机上进行开发,然后使用脚本将war上传scp到指定文件夹下,然后执行docker命令进行构建镜像,然后运行容器 +# 测试平台 +> [metersphere.io](https://metersphere.io/) \ No newline at end of file diff --git a/Skills/DevOps/ELK.md b/Skills/DevOps/ELK.md new file mode 100644 index 0000000..b2a0ab4 --- /dev/null +++ b/Skills/DevOps/ELK.md @@ -0,0 +1,67 @@ +--- +title: ELK.md +date: 2019-03-19 17:59:18 +tags: +categories: +--- + +**目录 start** + +1. [ELK](#elk) + 1. [ElasticSearch](#elasticsearch) + 1. [Logstash](#logstash) + +**目录 end**|_2020-08-18 17:49_| +**************************************** +# ELK + +> [5 Good Reasons to Use a Log Server ](https://reflectoring.io/log-server/) + +- 整体架构为Logstash采集日志输出文件,制定解析格式,存入ES,Kibana用于展示 +- 此时可以优化日志输出,区分不同的字段(时间,线程,方法),方便解析为不同字段,索引优化 + +************************ + +## ElasticSearch +> [ElasticSearch](/Skills/Search/Elasticsearch.md) + +> UI +1. [appbaseio/dejavu](https://github.com/appbaseio/dejavu) +1. [cars10/elasticvue](https://github.com/cars10/elasticvue) +1. cerebro + +************************ + +## Logstash +> [Official Doc](https://www.elastic.co/guide/en/logstash/current/index.html) +> [Logstash 最佳实践](https://doc.yonyoucloud.com/doc/logstash-best-practice-cn/index.html) + +常用于 数据的采集 过滤,配置文件使用 Ruby 编写 + +1. input 数据源 通过 input plugin 可支持 JDBC Kafka 文件流 HTTP 等等 +1. filter 数据源做处理 类似于 Java8 中 Stream 表达式 中的map filter等函数,对数据进行处理 +1. ouput 输出数据源 对应 output plugin + +> 解析嵌套[JSON](https://www.elastic.co/guide/en/logstash/current/plugins-filters-json.html) message内content +```ruby +filter{ + json { + source => "message" + target => "parsed" + add_field => {"content" => "%{[parsed][content]}"} + remove_field => ["parsed"] + } +} +``` + +> 增减字段 +```ruby +filter { + mutate { + add_field => { "[@metadata][program]" => "%{program}" } + remove_field => "[program]" + } +} +``` + +- 使用指定配置文件且自动重载 bin/logstash -f app.conf --config.reload.automatic diff --git a/Skills/DevOps/Jenkins.md b/Skills/DevOps/Jenkins.md index 7ed34d6..5e429a4 100644 --- a/Skills/DevOps/Jenkins.md +++ b/Skills/DevOps/Jenkins.md @@ -1,20 +1,30 @@ -`目录 start` - -- [Jenkins](#jenkins) - - [安装](#安装) - - [直接运行jar](#直接运行jar) - - [Docker](#docker) - - [配置](#配置) - - [配置Gradle](#配置gradle) - - [使用](#使用) - - [个人经验](#个人经验) - -`目录 end` |_2018-09-14_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: Jenkins +date: 2018-11-21 10:56:52 +tags: +categories: + - DevOps +--- + +**目录 start** + +1. [Jenkins](#jenkins) + 1. [安装](#安装) + 1. [直接运行jar](#直接运行jar) + 1. [Docker](#docker) + 1. [配置](#配置) + 1. [配置Gradle](#配置gradle) + 1. [配置Docker插件](#配置docker插件) + 1. [使用](#使用) + 1. [Pipeline](#pipeline) + 1. [个人经验](#个人经验) + +**目录 end**|_2020-04-27 23:42_| **************************************** # Jenkins > [官网](https://jenkins.io/) -> [参考博客: 用 Docker, maven, jenkins 完成 CI](http://www.open-open.com/lib/view/open1436922756240.html) +> [参考: 用 Docker, maven, jenkins 完成 CI](http://www.open-open.com/lib/view/open1436922756240.html) ## 安装 > [官方下载地址](https://jenkins.io/download/) | 由于是一个Java的Web服务, 所以也有war版本, 资源消耗都挺大的 @@ -23,85 +33,119 @@ 直接下载最新LTS版本, java -jar XXX.war 就可以运行了 ### Docker -> [DockerHub : official ](https://hub.docker.com/_/jenkins/) | [长期支持版](https://hub.docker.com/r/jenkins/jenkins/)[长期支持版文档](https://github.com/jenkinsci/docker/blob/master/README.md) +> [Dockerhub: official](https://hub.docker.com/_/jenkins/) `已废弃? 版本太老了` -- `sudo docker pull jenkins` 下拉镜像(600M+) `jenkins:alpine` 更小点(200M+) -- `sudo docker run --name myjenkins -p 8080:8080 -p 50000:50000 -v /home/kcp/docker/jenkins:/var/jenkins_home jenkins` 构建容器 -- 确保目录是开放了权限的, `chmod 777 jenkins` 最简单直接 -- 容器启动后, 第一次访问需要初始化, 之后就是正常的容器的启动关闭了 +> [Dockerhub: LTS](https://hub.docker.com/r/jenkins/jenkins/) | [长期支持版文档](https://github.com/jenkinsci/docker/blob/master/README.md) -- [h1kkan/jenkins-docker](https://hub.docker.com/r/h1kkan/jenkins-docker/) `由于官方镜像更新太慢,这个镜像有最新的版本` +- `docker pull jenkins/jenkins` 下拉镜像(600M+) + - Alpine版: `jenkins/jenkins:alpine` 更小点(200M+) +- 构建容器 `sudo docker run --name myjenkins -p 8080:8080 -p 50000:50000 -v /home/kcp/docker/jenkins:/var/jenkins_home jenkins` +- 确保 `/home/kcp/docker/jenkins` 目录是开放了权限的, `chmod 777 jenkins` 最简单直接 +- 容器启动后, 第一次访问需要配置管理员账号, 插件等等, 配置完成之后就可以任意重启容器了 -> 但是以上镜像都太大,更新不一定及时, 所以完全可以自动手动构建镜像 -1. 现取[下载](https://jenkins.io/download/)好想要的版本的jar, 然后在一个空目录下 新建一个文件 jenkins.dockerfile +******************************** + +**`手动构建镜像`** +> 但是以上镜像都太大,更新不一定及时, 所以完全可以手动构建镜像 + +1. 去 [下载](https://jenkins.io/download/) 对应版本的war, 然后在一个空目录下 新建一个文件 `jenkins.dockerfile` ```Dockerfile -FROM frolvlad/alpine-oraclejdk8:slim -COPY . . -EXPOSE 8080 -EXPOSE 5000 -CMD ["java", "-jar", "jenkins.war"] + FROM frolvlad/alpine-oraclejdk8:slim + COPY . . + EXPOSE 8080 + EXPOSE 5000 + CMD ["java", "-jar", "jenkins.war"] ``` -1. `docker build -t jenkins:xxx -f jenkins.dockerfile .` 注意最后有一个点, 是表明当前目录 +1. 构建镜像 `docker build -t jenkins:xxx -f jenkins.dockerfile .` ******************************** + +- [h1kkan/jenkins-docker](https://hub.docker.com/r/h1kkan/jenkins-docker/) `依据LTS的war包加上一些常用插件的镜像版本` + ## 配置 +`配置时区` + +[Official Wiki](https://wiki.jenkins.io/display/JENKINS/Change+time+zone) | [参考: Jenkins修改时区(Docker)](https://blog.csdn.net/k_zombie/article/details/50754253) +或者在 Script Console 中 运行 `System.setProperty('org.apache.commons.jelly.tags.fmt.timeZone', 'Asia/Shanghai');` + ### 配置Gradle > 系统管理 -> Global Tool Configuration 下 配置gradle, 然后新建项目的时候选择新建的gradle配置, 执行构建的时候才会去下载Gradle +### 配置Docker插件 + *********************** ## 使用 - -### 个人经验 -> 从Gitlab私有库(Maven SpringBooot项目)建好一个任务, 并构建好镜像和容器, 更新容器 -> 做法是在运行Docker的服务器上建立一个目录专门用来更新该项目, 然后在Jenkins构建完成后将 jar 包传过去, 执行该目录下的脚本完成容器和镜像的更迭 - -1. 复制项目URL, Credentials 中添加一个 username/password 类型的授权即可(就是gitlab上的用户名密码) - - 可以选择指定的 分支 进行构建 -1. 添加一个构建, 选择Maven的版本, Goal 中添加 命令 clean package -1. 在Build 这里添加一个Post-build step 这里 选择添加一个 Shell, 写入要执行的脚本即可 -```sh -echo "...构建完成, 开始建立镜像..." -jarFile=$WORKSPACE/target/app.jar -if [ -f $jarFile ];then - scp $WORKSPACE/target/app.jar xx@xxx:/home/xxx/ - ssh xx@xxx "cd /home/xx/ && sh deploy.sh dev" -fi -``` - -`deploy.sh` -```sh -version=`date +%Y-%m-%d-%H-%M` -num=`docker ps | grep app | wc -l` -if [ $num = 1 ];then - docker logs app >> /home/ai/xx/log/$version.log - docker rm -f app -fi -if [ "$1" = "dev" ];then - docker build -t app:$version -f /home/xx/build-dev.dockerfile . -else - docker build -t app:$version -f /home/xx/build-prod.dockerfile . -fi -echo "版本:"$version"镜像构建完成" -docker run --name app -d -p 16888:16888 -p 3070:3070 app:$version -echo "容器启动成功" -``` - -`build-dev.dockerfile` -```dockerfile -FROM frolvlad/alpine-oraclejdk8:slim -COPY . . -EXPOSE 16888 -EXPOSE 3070 -CMD ["java", "-jar", "app.jar", "--spring.profiles.active=jenkins"] +### Pipeline +Jenkins 通过配置好的 slave 镜像启动对应容器, 在slave 容器中完成构建过程, 然后更换应用容器 销毁slave容器 + +```groovy +pipeline { + agent { + label 'docker-slave' + } + stages { + stage('init') { + steps { + echo 'init..' + script { + echo "PATH = ${PATH}" + echo "env.version = ${env.version}" + } + } + } + stage('package') { + steps { + echo "start to build" + checkout changelog: false, poll: false, scm: [$class: 'SubversionSCM', additionalCredentials: [], excludedCommitMessages: '', excludedRegions: '', excludedRevprop: '', excludedUsers: '', filterChangelog: false, ignoreDirPropChanges: false, includedRegions: '', locations: [[cancelProcessOnExternalsFail: true, credentialsId: '22f6f4c9-f19', depthOption: 'infinity', ignoreExternalsOption: true, local: '.', remote: 'http://xxxx/svn/project-name/dev/server/trunk']], quietOperation: true, workspaceUpdater: [$class: 'UpdateUpdater']] + sh "mvn -B -V -U clean package -DskipTest=true" + } + } + stage('test') { + steps { + echo 'Testing..' + sh "mvn -B test" + } + } + stage('build docker image and publish into local registry') { + steps { + echo "starting to build docker image..." + script { + /* This builds the actual image; synonymous to + * docker build on the command line */ + sh "pwd && ls . && docker info" + withDockerRegistry(url: 'http://xxx:5000/') { + def app = docker.build "xxx:5000/project-dev:${env.BUILD_ID}" + app.push() + echo "pushed into local registry" + } + } + } + } + stage('deploy') { + steps { + echo 'killing old server and start new server....' + script { + sh "docker container rm -f project-dev && docker run -d -p 3070:3070 -p 16888:16888 --name project-dev xxx:5000/project-dev:${env.BUILD_ID}" + } + } + } + stage('clean local images') { + steps { + echo "cleaning dangling images..." + script { + sh "docker images --filter \"dangling=true\" -q |xargs --no-run-if-empty docker rmi" + } + } + } + } +} ``` -`build-prod.dockerfile` -```dockerfile -FROM frolvlad/alpine-oraclejdk8:slim -COPY upload/* . -EXPOSE 16888 -EXPOSE 3070 -CMD ["java", "-jar", "app.jar", "--spring.profiles.active=production", ">>/var/log/game.log"] +slave 配置 ``` + /home/ai/rs/jenkins-slave/m2:/home/jenkins/.m2 + /var/run/docker.sock:/var/run/docker.sock + /usr/bin/docker:/usr/bin/docker +``` \ No newline at end of file diff --git a/Skills/DevOps/README.md b/Skills/DevOps/README.md index 63ba7f5..b16604f 100644 --- a/Skills/DevOps/README.md +++ b/Skills/DevOps/README.md @@ -1,5 +1,9 @@ # DevOps +> [Awesome Ops](https://github.com/eryajf/awesome-ops) -> [参考博客: 谈谈持续集成,持续交付,持续部署之间的区别](https://www.jianshu.com/p/2c6ebe34744a) +> [参考: 谈谈持续集成,持续交付,持续部署之间的区别](https://www.jianshu.com/p/2c6ebe34744a) https://www.cnblogs.com/gudi/p/6667102.html -https://www.zhihu.com/question/23444990 \ No newline at end of file +https://www.zhihu.com/question/23444990 + + +> [www.terraform.io](https://www.terraform.io/)`跨云管理基础设施` diff --git a/Skills/Document/Dot.md b/Skills/Document/Dot.md new file mode 100644 index 0000000..bc7522c --- /dev/null +++ b/Skills/Document/Dot.md @@ -0,0 +1,27 @@ +--- +title: Dot +date: 2020-10-28 15:22:01 +tags: +categories: +--- + +**目录 start** + +1. [Graphviz 绘图 和 Dot 语言](#graphviz-绘图-和-dot-语言) + 1. [工具](#工具) + +**目录 end**|_2020-10-28 15:22_| +**************************************** +# Graphviz 绘图 和 Dot 语言 +> [Official Site](http://www.graphviz.org/) + +> [参考: Graphviz绘图 - DOT语言](https://itopic.org/graphviz.html) +> [参考: graphviz dot语言学习笔记](https://www.jianshu.com/p/e44885a777f0) + +1. 编译输出 dot -Tsvg test.dot -o test.svg + +plantUML是基于Graphviz开发的 + +## 工具 +1. vimdot +1. VSCode 的 Graphviz 插件 diff --git a/Skills/Document/License.md b/Skills/Document/License.md new file mode 100644 index 0000000..df7846b --- /dev/null +++ b/Skills/Document/License.md @@ -0,0 +1,21 @@ +--- +title: License +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +# License + +> [参考: 开源协议及知识共享协议简单介绍](https://www.cnblogs.com/huaxia283611/p/LicenseIndex.html) + +关于许可证 [Github许可证网](https://choosealicense.com/licenses/) +新建项目的时候可以选择 添加.gitignore和许可证类别 许可证大致分为 MIT Apache2.0 GPL + +- `MIT` 简单宽松的许可证,任何人可以拿代码做任何事与我无关` eg: jQuery、Rails` +- `Apache` 关注于专利,这类似于MIT许可证,但它同时还包含了贡献者向用户提供专利授权相关的条款。 `Apache、SVN和NuGet` +- `GPL` 关注于共享改进,这是一种copyleft许可证,要求修改项目代码的用户再次分发源码或二进制代码时,必须公布他的相关修改。 `Linux、Git` + + +# Tips +> [解读我国首个明确开源软件协议性质的判决](https://www.ipeconomy.cn/index/news/magazine_details/id/4111.html) diff --git a/Skills/Document/MarkDown.md b/Skills/Document/MarkDown.md index 685b044..629dc01 100644 --- a/Skills/Document/MarkDown.md +++ b/Skills/Document/MarkDown.md @@ -1,33 +1,70 @@ -`目录 start` - -- [Markdown](#markdown) - - [基础格式](#基础格式) - - [链接](#链接) - - [图片](#图片) - - [列表](#列表) - - [头信息](#头信息) - - [流程图](#流程图) - - [Github](#github) - -`目录 end` |_2018-08-30_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: MarkDown +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [Markdown](#markdown) + - 1.1. [基本格式](#基本格式) + - 1.1.1. [居中](#居中) + - 1.2. [基本元素](#基本元素) + - 1.2.1. [文本](#文本) + - 1.2.2. [分割线](#分割线) + - 1.2.3. [链接](#链接) + - 1.2.4. [图片](#图片) + - 1.2.5. [列表](#列表) + - 1.2.6. [头信息](#头信息) + - 1.2.7. [代码块](#代码块) + - 1.3. [流程图](#流程图) + - 1.4. [SVG](#svg) + - 1.5. [Github](#github) + +💠 2024-03-23 17:52:21 **************************************** # Markdown -> [markup](https://github.com/github/markup)`丰富的标记性文本格式` +> [Github: markdown](https://guides.github.com/features/mastering-markdown/) +> [ Markdown 编辑器语法指南](https://segmentfault.com/markdown) > [Markdown教程](http://www.markdown.cn/) > [CSDN的Markdown案例](https://github.com/kuangcp/Notes/blob/master/Article/CSDN的Markdown案例.md) -> [ Markdown 编辑器语法指南](https://segmentfault.com/markdown) + +> [Github: markup](https://github.com/github/markup)`丰富的标记性文本格式` | + +> [Github: emoji character](https://www.webfx.com/tools/emoji-cheat-sheet/) ************** -## 基础格式 + +## 基本格式 +### 居中 +``` +
+ +
+``` +> 注意这俩标签前后都需要一行空行 + +## 基本元素 +### 文本 +1. 斜体 `*content*` 或者 `_content_` +1. 粗体 `**content**` +1. 删除 `~content~` +1. 粗斜 `***content***` 或者 `___content___` + +### 分割线 +- `***` +- `---` +- `___` ### 链接 -1. [name](url) 推荐 -1. [[name|url]] +1. `[name](url)` 推荐 +1. [[name|url] 1. [name][targetNum] - 末尾: [targetNum]: url ### 图片 -1. ![description](url) +1. `![description](url)` 1. ![description][targetNum] - 末尾: [targetNum]: url "description" @@ -37,9 +74,22 @@ - **无序列表** : `- ` 或 `* ` - **有序列表** : `1. ` (markdown渲染的时候会自动排序 1 也可以换成任意非0正整数) -- 列表中还能嵌套 引用, 例如: `- > ` +- 列表中还能嵌套 引用, 例如: `- > content` 或者 `>1. content` + +> 关于有序列表中的块状结构破坏有序性的问题 +- 当需要出现代码块, 图片, 表格 等块状结构时, 只需将整体进行缩进, 就可以保证后续的有序性 + +例如: +1. a + ```sh + echo hi + ``` +1. b + ### 头信息 -``` +> 仅仅是github的语法, gitlab 当成了代码块进行渲染, gitee 则完全是乱的. + +```yml --- layout: post title: "关于WEB开发中引入javascript文件方式的一点建议" @@ -55,21 +105,37 @@ - 建议 --- ``` +- 可以有多种数据结构 yml ini + - 一般用于描述文件元信息; 静态博客生成器用于生成博客的标签等信息; + +### 代码块 +1. 行级 反引号 +1. 多行 三个反引号独立行进行包裹 + +************************ + ## 流程图 -```flow -st=>start: Start -e=>end -op=>operation: My Operation -cond=>condition: Yes or No? - -st->op->cond -cond(yes)->e -cond(no)->op -``` +- [plantUML](http://plantuml.com) + +## SVG +- 作为图片引入 `![]()` +- HTML代码块引入 + ``` +
+ + SVG Sample + This is a sample to use SVG in markdown on the website cnblogs. + + +
+ ``` + +************************ -************** ## Github -> [比较全面的Github格式 GFM](https://github.com/guodongxiaren/README) +> [比较全面的Github格式 GFM](https://github.com/guodongxiaren/README) +> [Github readme stats](https://github.com/anuraghazra/github-readme-stats) +> [Awesome Badges](https://github.com/Envoy-VC/awesome-badges) _目录规则(页内跳转)_ @@ -78,7 +144,6 @@ _目录规则(页内跳转)_ - 同理还有 `/` 中英文的 逗号 句号 冒号 小数点 问号 - 空格会变成 - -**** _文件内容_ - 一行显示上 58列 就要换行 - 行末加上两个空格即是换行, 直接回车键换行是没有用的 @@ -93,7 +158,6 @@ _列表的折叠写法_ ``` -***** _md文件的头属性_ ``` --- @@ -103,6 +167,6 @@ _md文件的头属性_ ``` ```diff -+ fsd -- 发的所发生的 ++ python +- java ``` diff --git a/Skills/Document/Process.md b/Skills/Document/Process.md new file mode 100644 index 0000000..6b18888 --- /dev/null +++ b/Skills/Document/Process.md @@ -0,0 +1,30 @@ +--- +title: Process +date: 2024-01-13 12:22:37 +tags: +categories: +--- + +💠 + +- 1. [流程图绘制](#流程图绘制) + - 1.1. [drawio](#drawio) + - 1.2. [excalidraw](#excalidraw) + - 1.3. [plantuml](#plantuml) + - 1.4. [process on](#process-on) + +💠 2024-01-13 12:22:42 +**************************************** +# 流程图绘制 + +## drawio +一板一眼的设计,基础图标够用 + +## excalidraw +手绘风格,支持很多插件库,图标丰富 + +## plantuml +使用DSL来描绘出一个图, 类似于Graphviz + +## process on +在线网站 协作时十分方便,但是单纯个人使用时,更倾向于本地绘制,同步交给Git diff --git a/Skills/Document/RequirementsDocument.md b/Skills/Document/RequirementsDocument.md index 8fde6c7..080e2e7 100644 --- a/Skills/Document/RequirementsDocument.md +++ b/Skills/Document/RequirementsDocument.md @@ -1,21 +1,51 @@ -`目录 start` - -- [需求文档](#需求文档) -- [技术文档](#技术文档) - - [Java](#java) - - [SpringBoot](#springboot) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 需求到实现所需文档 +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [需求文档](#需求文档) +- 2. [技术文档](#技术文档) + - 2.1. [架构图](#架构图) + - 2.2. [Java](#java) + - 2.2.1. [SpringBoot](#springboot) +- 3. [上线CheckList](#上线checklist) + +💠 2024-09-20 11:52:03 **************************************** # 需求文档 -> 做一个完善的需求分析,并书写一个需求文档 - -- [参考博客](http://www.kejilie.com/woshipm/article/6Bri6b.html) +> 贴近多方实际诉求调研,分析有效需求,输出需求文档 +- [参考 如何写一份程序员爱看的需求文档?](http://www.kejilie.com/woshipm/article/6Bri6b.html) *********************** # 技术文档 +- 技术方案组成 5W2H + - 背景: why: 现状流程, 为什么要做, 是否有替代方式 + - 目标: waht: 目的和做什么 量化指标 + - 方案: how : 多个方案 优劣势分析 how much: 做到不同程度的成本和产出 + - 结论: 选择的方案, 选择原因及注意事项 + - 备忘/执行计划: who when where + +## 架构图 +> [diagrams](https://github.com/mingrammer/diagrams)`Python代码生成架构图,类似于plantuml的DSL` + ## Java ### SpringBoot > 可以使用 swagger2 和 spring data rest hal-browser 两者是接口文档 showdoc是文档的共享更方便 + + +************************ + +# 上线CheckList +> 梳理上线需求的项目范围,按顺序罗列事项及负责人,降低上线的风险。 + +常见问题: +- 前端/后端/运维 漏合并分支,漏配置(SQL,Nginx,K8S,网络) +- 上线发现配置项或逻辑不生效或要花费很大成本,但是在测试和验收时都正常 + - 开发,测试,灰度,生产 由于各种客观原因导致环境不一致`例如只有生产对接了付费或定制的第三方API,生产环境数据量和数据倾斜的压力远大于测试环境,测试环境在内网生产环境在公网等等`,未及时对当前版本的修改做演练。 + diff --git a/Skills/Document/UML.md b/Skills/Document/UML.md new file mode 100644 index 0000000..5b02477 --- /dev/null +++ b/Skills/Document/UML.md @@ -0,0 +1,34 @@ +--- +title: UML +date: 2019-04-25 14:42:15 +tags: +categories: +--- + +**目录 start** + +1. [UML](#uml) + 1. [Tool](#tool) + +**目录 end**|_2020-07-05 14:58_| +**************************************** +# UML +> [个人整理](https://github.com/Kuangcp/TechGraph/tree/master/UML) + +> [参考: 五分钟读懂UML类图](https://www.cnblogs.com/shindo/p/5579191.html) + +- [wiki: list of uml tools](https://en.wikipedia.org/wiki/List_of_Unified_Modeling_Language_tools) +- [免费UML软件统计 博客](http://blog.csdn.net/s464036801/article/details/8469166) +- [bouml](http://www.bouml.fr/download.html#Debian) `官方网站下载` +- [argouml](http://argouml.tigris.org/) `argouml官网` +- [visual paradigm](https://online.visual-paradigm.com) + +yed astah dia + +- VSCode 安装 plantUML 插件 + +## Tool +> [UML相关工具](https://alternativeto.net/software/staruml/) + +- [umlet](https://www.umlet.com) +- [plantuml](http://plantuml.com) diff --git a/Skills/Document/img/001-dev-process.km.svg b/Skills/Document/img/001-dev-process.km.svg new file mode 100644 index 0000000..ef12f12 --- /dev/null +++ b/Skills/Document/img/001-dev-process.km.svg @@ -0,0 +1 @@ +软件开发流程产品需求确认产品经理对接需求, 了解需求并画原型图需求确认以及原型沟通确认会(产品经理,UI,研发,测试等人员)开发规划项目负责人和开发人员, 根据需求和功能点,讨论并制定开发计划和周期项目负责人和管理者根据提交的开发周期商讨, 并做出最后的确认和调整产品研发每天前端和后端的开发人员根据自己制定的计划表,开发对应的功能点开发人员在开发完每一个功能模块 开发业务上的自测和单元测试测试测试人员按测试计划和验收计划执行项目完成后, 大家集中一个时间段内测,找出其中的BUG产品发布前端检查代码, 是否代码切换到正式环境(避免测试服务器URL上传)后端检查发布的环境是否为正式环境负责人确定发布版本号发布前检查由于Demo定理的存在, 发布后需要严谨的测试业务流程的完整验证发布后测试验证发布后代码留存记档回滚预案的准备和执行 \ No newline at end of file diff --git a/Skills/Ecology/APM.md b/Skills/Ecology/APM.md new file mode 100644 index 0000000..5c4381a --- /dev/null +++ b/Skills/Ecology/APM.md @@ -0,0 +1,101 @@ +--- +title: APM +date: 2024-03-23 17:52:21 +tags: +categories: +--- + +💠 + +- 1. [APM](#apm) + - 1.1. [OpenTelemetry](#opentelemetry) + - 1.2. [SkyWalking](#skywalking) + - 1.3. [Sentry](#sentry) + - 1.4. [CAT](#cat) +- 2. [采集客户端](#采集客户端) +- 3. [Monitoring](#monitoring) + - 3.1. [Prometheus](#prometheus) + +💠 2024-09-03 21:27:16 +**************************************** +# APM +> Application performance Management `分布式链路追踪,技术或业务指标监控告警` + +核心为 可观测性(Observability) 监测(Monitoring) + +> [Github: APM](https://github.com/topics/apm) + +[Glowroot](https://github.com/glowroot/glowroot)`技术指标 Web SQL JVM JMX监控,agent方式支持单机和集群(存储使用cassandra)` + +Pinpoint +[JavaMelody](https://github.com/javamelody/javamelody) +[Scouter](https://github.com/scouter-project/scouter) +[Stagemonitor](https://github.com/stagemonitor/stagemonitor) +[MoSKito](https://github.com/anotheria/moskito) + +## OpenTelemetry +[Github: OpenTelemetry](https://github.com/open-telemetry) + +CNCF组织的项目,脱离语言和技术架构,更有发展前景的可观测性项目。 + +## SkyWalking +> [Official Site](http://skywalking.apache.org/) | [Downloads](https://skywalking.apache.org/downloads/)] + +[server](https://hub.docker.com/r/apache/skywalking-oap-server) | [ui](https://hub.docker.com/r/apache/skywalking-ui) + +```sh +docker run --name oap -p 12340:1234 -p 11800:11800 -p 12800:12800 -d apache/skywalking-oap-server:8.3.0-es6 +docker run --name oap-ui -p 8080:8080 -d -e SW_OAP_ADDRESS=http://192.168.7.54:12800 apache/skywalking-ui +``` + +应用启动 java -javaagent:/opt/apache-skywalking-apm-bin/agent/skywalking-agent.jar -Dskywalking.agent.service_name=xxxtest -Dskywalking.collector.backend_service=127.0.0.1:11800 -jar application.jar + +************************ + +## Sentry +[Github](https://github.com/getsentry/sentry) + +[Docker compose 部署](https://github.com/getsentry/self-hosted) + +感知应用异常: + +[UncaughtExceptionHandlerIntegration](https://github.com/getsentry/sentry-java/blob/main/sentry/src/main/java/io/sentry/UncaughtExceptionHandlerIntegration.java) + LogAppender + +************************ + +## CAT +> [陆金所 CAT 优化实践](https://www.infoq.cn/article/XvGZcW312MdatCKFMR8b) + +> [Github: cat-docker](https://github.com/lghuntfor/cat-docker) +> [深度剖析开源分布式监控CAT](https://tech.meituan.com/2018/11/01/cat-in-depth-java-application-monitoring.html) + +感知应用异常: + +1. 实现日志框架的Appender基类,捕获Error到 `Cat.logError`,并手动声明该Appender到配置文件中 +2. 手动设置**静态**的默认全局异常处理`Thread.setDefaultUncaughtExceptionHandler`,防止异常漏捕获。 + - SpringBoot项目里发生机率较小,因为基本都有Controller层的全局异常处理,且大部分请和逻辑从web端进入。 + - 只有自定义线程池,Scheduler线程池,Junit等地方,未捕获运行时异常,才会走默认逻辑异常栈被输出到标准错误 System.err 中。 + +************************ +# 采集客户端 +- [Prometheus: JMX Exporter](https://github.com/prometheus/jmx_exporter) +- [micrometer](https://github.com/micrometer-metrics/micrometer)`门面框架类似于SLF4J 支持多种采集` + - [Quick Guide to Micrometer](https://www.baeldung.com/micrometer) + +- [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) + +************************ + +# Monitoring +技术指标监控告警,离业务指标比较远,例如 主机,数据库,容器,网络 + +## Prometheus +[Github: Prometheus](https://github.com/prometheus/prometheus) + +通常和 Grafana 结合使用 + +> [Prometheus+Grafana监控SpringBoot项目JVM信息](https://developer.aliyun.com/article/890169) `JMX Exporter` +> [JVM 接入 Prometheus](https://cloud.tencent.com/document/product/1416/56032)`手动声明HTTP` + +> [Prometheus running on Kubernetes ](https://github.com/prometheus-operator/kube-prometheus) + diff --git a/Skills/Ecology/Gateway.md b/Skills/Ecology/Gateway.md new file mode 100644 index 0000000..d559630 --- /dev/null +++ b/Skills/Ecology/Gateway.md @@ -0,0 +1,52 @@ +--- +title: Gateway +date: 2024-04-19 10:47:31 +tags: +categories: +--- + +💠 + +- 1. [网关](#网关) + - 1.1. [功能](#功能) +- 2. [对比](#对比) + - 2.1. [Spring Gateway](#spring-gateway) + - 2.2. [Zuul](#zuul) + - 2.3. [Kong](#kong) + - 2.4. [Nginx](#nginx) + - 2.5. [Traefik](#traefik) + +💠 2024-04-19 10:49:10 +**************************************** +# 网关 +> 概览 + +## 功能 +动态路由 +负载均衡 +统一鉴权 +审计&监控 +协议转换 +限流熔断 +黑白名单 +灰度发布 蓝绿 +流量染色 + + +************************ + +# 对比 + +## Spring Gateway + +## Zuul + + +## Kong + + +## Nginx + +## Traefik + + diff --git a/Skills/Ecology/HA/Readme.md b/Skills/Ecology/HA/Readme.md new file mode 100644 index 0000000..d6ec0e0 --- /dev/null +++ b/Skills/Ecology/HA/Readme.md @@ -0,0 +1,3 @@ +常见容错机制:failover、failfast、failback、failsafe + +K8S快速HA:配置CPU,内存阈值扩容和缩容 diff --git a/Skills/Ecology/MSA.md b/Skills/Ecology/MSA.md deleted file mode 100644 index 67628fd..0000000 --- a/Skills/Ecology/MSA.md +++ /dev/null @@ -1,13 +0,0 @@ -`目录 start` - -- [MSA](#msa) - - [概念](#概念) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# MSA -> 微服务 - -## 概念 - - diff --git a/Skills/Ecology/MinIO.md b/Skills/Ecology/MinIO.md new file mode 100644 index 0000000..068235a --- /dev/null +++ b/Skills/Ecology/MinIO.md @@ -0,0 +1,2 @@ +# MinIO +> [min.io](https://min.io/) diff --git a/Skills/Ecology/OSS.md b/Skills/Ecology/OSS.md new file mode 100644 index 0000000..b9e7092 --- /dev/null +++ b/Skills/Ecology/OSS.md @@ -0,0 +1,10 @@ +# OSS 对象存储 +> [什么是对象存储?](https://cloud.google.com/learn/what-is-object-storage?hl=zh-CN) + +各大云厂商有商业的OSS方案,七牛云Kodo,阿里云OSS,腾讯云COS,华为云OBS等等。 + +> [WebDAV vs S3 vs SFTP](https://forum.duplicati.com/t/webdav-vs-s3-vs-sftp/817) + +自建选择:FTP,HDFS,MinIO。 + + diff --git a/Skills/Ecology/PGO.md b/Skills/Ecology/PGO.md new file mode 100644 index 0000000..f67d4f1 --- /dev/null +++ b/Skills/Ecology/PGO.md @@ -0,0 +1,24 @@ +--- +title: PGO +date: 2024-04-30 22:26:23 +tags: +categories: +--- + +💠 + +- 1. [Profile-guided optimization](#profile-guided-optimization) + +💠 2024-04-30 23:06:06 +**************************************** +# Profile-guided optimization +> [Profile-guided optimization](https://en.wikipedia.org/wiki/Profile-guided_optimization) + +- PGO Profile-guided optimization +- PDO feedback-directed optimization +- PDF profile-directed feedback + + +核心思想为记录一份可靠的运行期profile,然后指导后续的应用对运行效率做针对性的优化。 +相较于静态代码分析(通常由语言的编译包(编译器 优化器等)实现),运行期的分析能感知不同代码的运行频率和分支执行情况,有选择性的做JIT等优化。 + diff --git a/Skills/Ecology/WebPerformance.md b/Skills/Ecology/WebPerformance.md new file mode 100644 index 0000000..9a2d87e --- /dev/null +++ b/Skills/Ecology/WebPerformance.md @@ -0,0 +1,121 @@ +--- +title: Web性能调优 +date: 2018-12-13 16:02:57 +tags: + - Performance +categories: +--- + +💠 + +- 1. [Web应用性能优化](#web应用性能优化) +- 2. [客户端](#客户端) +- 3. [服务端](#服务端) + - 3.1. [压力测试准备](#压力测试准备) + - 3.2. [性能基准 指标](#性能基准-指标) + - 3.2.1. [性能监控平台](#性能监控平台) + - 3.3. [测试工具](#测试工具) + - 3.3.1. [Apache BenchMark](#apache-benchmark) + - 3.3.2. [Jmeter](#jmeter) + - 3.3.3. [wrk](#wrk) + - 3.3.4. [ali](#ali) + - 3.3.5. [Httperf](#httperf) + - 3.3.6. [k6](#k6) +- 4. [数据库](#数据库) + - 4.0.1. [MySQL](#mysql) + - 4.0.1.1. [主从复制以及读写分离](#主从复制以及读写分离) + +💠 2024-09-06 11:36:43 +**************************************** +# Web应用性能优化 + +> [参考: 怎样正确做 Web 应用的压力测试?](https://www.zhihu.com/question/19867883) + +> 负载测试、压力测试和性能测试 +- 测试手段相似,但是目的不同,负载测试是为了发现系统性能问题,性能测试是为了获取性能指标,压力测试是高负载下的负载测试 + +对于web应用来说 从前端渲染速度,到页面大小,session存储效率,ajax性能,缓存命中率,数据库设计及访问速度 都要考虑。 + +# 客户端 + +渲染和计算分离 + +************************ + +# 服务端 +1. 对于web后端来说就是请求过多, 数据库连接池不够用, 线程池大量等待的线程, 请求非常缓慢, 直接返回 5xx 错误码... + +## 压力测试准备 + +1. 软硬件版本要和正式服务器保持一致。 +2. 网络也需要和正式服务器保持一致。 +3. 在测试高并发的场景下,也要修改linux的默认并发数。 + +## 性能基准 指标 +> [参考: 系统吞吐量(TPS)、用户并发量、性能测试概念和公式](http://www.cnblogs.com/freeton/archive/2013/05/31/3109815.html) + +> 外在指标 +1. 吞吐量。每秒钟处理的请求数量; +2. 响应时间。应用处理一个请求的耗时; +3. 错误率。一批请求中出错的请求占比。 + +> 内在指标 +1. CPU。linux下使用top命令; +2. 内存。linux下使用top命令; +3. 服务器负载。linux下使用uptime命令,按照经验值,服务器负载应位于阀值的70%-80%; +4. 网络。主要包括网络流量和网络连接状态的监控,使用nmon工具; +5. 磁盘IO linux下可以用iostat监控磁盘状态。 + +### 性能监控平台 +- [CAT](https://github.com/dianping/cat) +- Prometheus + +************************ + +## 测试工具 +> [Github: HTTP(S) Benchmark Tools](https://github.com/denji/awesome-http-benchmark) +> [load-testing](https://github.com/topics/load-testing) +> [dperf](https://github.com/baidu/dperf) + +可以通过压力测试工具或者流量重放,复制 等方式模拟高并发业务场景 + +### Apache BenchMark +> 简称 ab [Doc](https://httpd.apache.org/docs/2.4/programs/ab.html) + +- 安装 `sudo apt install apache2-utils`| `sudo pacman -S apache-tools` +- 简单使用 `ab -c 并发数 -n 总请求数 URL` + - 查看文档:`man ab` 或 `ab -h` + +- 测试本机超过100连接报错 104: + - [Blog:解决问题](http://www.cnblogs.com/archoncap/p/5883723.html) + +- `ab -c 5 -n 1000 -X 127.0.0.1:8888 -T application/json -p list.json -C 'JSESSIONID=xxx' URL` 使用 Cookie并使用代理 对参数为json的接口发起请求 +- 设置Header`-H “AUTHORIZATION: Basic YWRtaW46YWRtaW4=“` + +### Jmeter +- [jmeter](http://jmeter.apache.org/download_jmeter.cgi) `apache 下的开源压测工具` +- [Jmeter中文网](http://www.jmeter.com.cn/) + +### wrk +> [Github地址](https://github.com/wg/wrk) +> [参考: wrk 压力测试 http benchmark POST接口](http://www.cnblogs.com/felixzh/p/8400729.html) +> [参考: 性能测试之-wrk(转)](http://www.cnblogs.com/rainy-shurun/p/5867946.html) + +1. 需要手动编译安装 make + +### ali +> [ali](https://github.com/nakabonne/ali)`终端内图形化展示结果` + +### Httperf +> [Github](https://github.com/httperf/httperf) `比 ab 更强大,能测试出 web 服务能承载的最大服务量及发现潜在问题;比如:内存使用、稳定性。最大优势:可以指定规律进行压力测试,模拟真实环境。` + +### k6 +> [Github k6](https://github.com/grafana/k6) `像单元测试一样做性能测试` + +************************ + +# 数据库 + +### MySQL +#### 主从复制以及读写分离 +- [读写分离博客](http://www.cnblogs.com/luckcs/articles/2543607.html) diff --git a/Skills/FrameWork/JavaBoot.md b/Skills/FrameWork/JavaBoot.md deleted file mode 100644 index 094494c..0000000 --- a/Skills/FrameWork/JavaBoot.md +++ /dev/null @@ -1,14 +0,0 @@ -`目录 start` - -- [关于Java的快速开发框架](#关于java的快速开发框架) - - [Springboot](#springboot) - - [Jboot](#jboot) - - [NuzBoot](#nuzboot) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 关于Java的快速开发框架 - -## Springboot -## Jboot -## NuzBoot \ No newline at end of file diff --git a/Skills/GUI/GTK.md b/Skills/GUI/GTK.md new file mode 100644 index 0000000..72de560 --- /dev/null +++ b/Skills/GUI/GTK.md @@ -0,0 +1,21 @@ +--- +title: GTK +date: 2021-02-19 10:14:10 +tags: +categories: +--- + +**目录 start** + +1. [GTK](#gtk) + +**目录 end**|_2021-02-19 10:14_| +**************************************** +# GTK + +> [Official Doc](https://www.gtk.org/docs/) + +[C++使用gtkmm快速开发](https://www.gtk.org/docs/language-bindings/cpp/) +``` + g++ -std=c++11 helloworld.cc main.cc -o helloworld `pkg-config gtkmm-3.0 --cflags --libs` +``` diff --git a/Skills/Media/AudioFormat.md b/Skills/Media/AudioFormat.md new file mode 100644 index 0000000..91edfd5 --- /dev/null +++ b/Skills/Media/AudioFormat.md @@ -0,0 +1,7 @@ +# 音频格式 + +## 无损 +### WAV + +## 有损 +### MP3 \ No newline at end of file diff --git a/Skills/Media/ImageFormat.md b/Skills/Media/ImageFormat.md new file mode 100644 index 0000000..99bb420 --- /dev/null +++ b/Skills/Media/ImageFormat.md @@ -0,0 +1,53 @@ +--- +title: 图像格式 +date: 2020-05-04 19:34:08 +tags: +categories: +--- + +💠 + +- 1. [图片格式](#图片格式) + - 1.1. [有损](#有损) + - 1.1.1. [Webp](#webp) + - 1.1.2. [JPEG](#jpeg) + - 1.1.3. [HEIF](#heif) + - 1.2. [无损](#无损) + - 1.2.1. [BMP](#bmp) + - 1.2.2. [PNG](#png) + - 1.2.3. [SVG](#svg) + +💠 2024-09-06 11:36:43 +**************************************** +# 图片格式 +> [参考: 图片格式 jpg、png、gif各有什么优缺点?什么情况下用什么格式的图片呢?](https://www.zhihu.com/question/20028452) + +图片的格式,表面上是后缀不同,实际上是图片的压缩标准不一样 + +## 有损 + +### Webp +> [webp_server_go](https://github.com/webp-sh/webp_server_go) + +### JPEG + +JPEG文件必须以`0xFF DB`开头和以 `0xFF D9` 结尾(EOI) + +### HEIF +High Efficiency Image File Format + +************************ + +## 无损 +### BMP + +### PNG +> [PNG文件格式详解](https://blog.mythsman.com/post/5d2d62b4a2005d74040ef7eb/) + +- 数据标识总是IEND(49 45 4E 44),因此,CRC码也总是AE 42 60 82。 `00 00 00 00 49 45 4E 44 AE 42 60 82` + +> [oxipng](https://github.com/shssoichiro/oxipng)`PNG压缩` + +### SVG + +> 作为网站icon `` diff --git a/Skills/Media/Readme.md b/Skills/Media/Readme.md new file mode 100644 index 0000000..813a6cb --- /dev/null +++ b/Skills/Media/Readme.md @@ -0,0 +1,4 @@ +# 多媒体资源 + +> [Linux 多媒体工具](/Linux/Base/LinuxEffective.md#多媒体) + diff --git a/Skills/Media/VideoFormat.md b/Skills/Media/VideoFormat.md new file mode 100644 index 0000000..fcc13aa --- /dev/null +++ b/Skills/Media/VideoFormat.md @@ -0,0 +1,18 @@ +--- +title: 视频格式 +date: 2021-04-14 14:37:45 +tags: +categories: +--- + +💠 + +- 1. [视频格式](#视频格式) + +💠 2024-09-05 15:59:26 +**************************************** +# 视频格式 +TODO + +> [digital_video_introduction](https://github.com/leandromoreira/digital_video_introduction) + diff --git a/Skills/Miscellaneous.md b/Skills/Miscellaneous.md deleted file mode 100644 index 64b0543..0000000 --- a/Skills/Miscellaneous.md +++ /dev/null @@ -1,104 +0,0 @@ -`目录 start` - -- [首先我想说三句话:](#首先我想说三句话) - - [学习方式](#学习方式) - - [一些建议](#一些建议) - - [无论做什么,原则最重要](#无论做什么原则最重要) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 学习杂记 - -- [一个牛人给Java初学者的建议(必看篇)](https://www.jb51.net/article/113819.htm) - - - -# 首先我想说三句话: ->- 1、欢迎入坑,现在爬上去还来得及。 ->- 2、没有毅力之人一事无成。 ->- 3、正确的选择会使你的事业一帆风顺。 - -## 学习方式 -* 1、要想成大神,必须自己下功夫,俗话说老师引进门,修行靠个人。编程也不例外。没有哪个培训机构能100%保证能把你培养成啥样。 - - 不要纠结是否要选培训机构、要选哪个培训机构。实际上如果你强迫自己学一门语言一年,如果还是懵懂的很厉害(譬如连hello world都写不出), - - 那么基本请考虑换个事情做做吧。一般好的程序员都是靠自学的。就像老板大部分都没上过MBA. - -* 2、选择开发语言 - - ????选啥语言? - - Php?你会发现学了一半,好像java做的事情更多 - - Java?你会发现学了一半好像ios开发更有前景 - - Ios?你会发现还不如android好 - - Andoid?你会发现其实还是php容易找工作 - - 所以初学者其实很难选择语言。既然难,索性不要急于选。 - -> 还有一个故事发生在好多人身上: -有个人全力自学c++,突然觉得c++不如java学的快,而且相对工作岗位也少,在c++学了很大一块了,想换方向选择java,纠结了很长时间,后来终于想通了,还是坚持学习c++。 -其实选择语言并不困难,你觉得什么适合你,就学那个。 -我看到过一个学校的专业班级(和企业合作,有php、java和.net三个方向),学生到了大四可以选择进入这个班级,作为企业定向培训。这个班级分类学生很有意思: ->>1、 老师在网上下载了php、java和.net三个语言的基础教程,分发给大家 -2、 每个人分别看入门教程各一天。(只许看第一章,不许动手写程序) -3、 然后选择自己“感觉看的最爽”的语言进行班级语言分类选择。 ->- 这就是一个很实践的帮助学生选语言的方式。 - -* 3、选一本好书 - -如果语言确定了,那么选一本书就很重要。 -如果你没有项目经验,那么学一门语言基本上靠看网络文章是学不好的。必须买书,因为网络上很多技术文章都有各种错误,我写的也不例外。 -一本书好不好,主要看第一章、中间一章和最后两章。基础讲得好,一般不会差。 -高级章讲的好,一般也不会差。差的书在于基础部分讲的冗余、啰嗦、拖沓,到了高级章节一掠而过(实际上是写书的人自己也不会)。 -入门书籍不需要太多,1,2本足够,要真正的吃透。 - -* 4、以正确的方式看视频 - -可以在网上找点对应的视频看着学习,能听懂视频里面的东西后,开始回归教材,不建议一直对着视频学习,因为视频都是别人咀嚼过的东西, -要建立自己的知识体系还得靠大脑自己去一遍遍的破除疑惑的过程,看书是最枯燥也是最有效的,看书学习,前提就是要对理论有个大概的了解, - -* 5、懂得实践(在实践中创新) - -试问一句,当你遇到了一个很大的问题,你自己去解决了他,百度也好,问人也好。可是最终还是你自己解决了问题。你当时心情绝对是极好的。 -尝试在电脑上去实践,很多人拿着题目不断在刷, 其实没那个必要,在理论掌握的差不多的情况下,把对应的课后题目独立在电脑上能实践好就够了, -如此反复的去做,不厌其烦的去做。遇到不明白的理论,重复第三和第四条。书读百遍,其义自现。 - -* 6、最终还是要有恒心有毅力 - -你要真正的拿出一段时间把一本基础的书籍看的差不多,这个打基础的阶段,真的需要十足的耐心,这点都做不到,后面会带来一系列的连锁反应,最后可能导致你最后的放弃。 -一栋大楼,地基最重要,编程同理,基础知识最重要。 - -* 7、主意横向扩展 - -现在都知道,社会上会的东西越多,你就越能在社会立足。没错,前期是要抱着必死的入坑的心态学好一门语言。 -到后期,学Android可以尝试学学数据库,xml html 之类的,学web前端编程的可以尝试学学后端,或者java 或者把你的 css/js 学的更精通! -学后端的人可以尝试学学其他的后端语言 ?比如 ruby asp.net jsp 或者可以学学 python? - -## 一些建议 - -* 1、Linux学习 - -微软不做老大已经很多年,大家一定很怀念当年vb6雄霸天下的年代。这个年代如果你还只会windows而对linux一点不会,那么已经不能称之为合格的程序员了。 -据统计,学习linux很有助于你学习真正的开发语言。所以如果你一点基础都没有,不妨抛开一切所谓的语言,先把linux学一学,学到一定程度后你必然会觉得豁然开朗。 -除非你记性太差,那么linux其实并不难学。 - -* 2、不要过早玩开源 - -这点其实很多忽视了,都以为学编程先弄个开源框架学习。一般来说,好的开源框架集结了多个技术大神多年来的技术经验和思想,你想在毫无基础的情况下学会必然会让你一头雾水。 -正所谓:零基础学开源框架属于杀敌一千自伤八百,很不上算。当然也不排除有些奇葩能搞定。 -太早玩开源好比早恋,弊总是大于利,但是运气好你成为大神的时间会缩短很多。 - -* 3、反向思维写程序 - -大部分程序员初学编程有个习惯,写完一段代码后想着如何优化。实际上这个办法适合大部分人,但凡事都有例外,并不一定所有人都能这么干并能干成功,反而越写越觉得没有信心,当你也有这种情况时请看下面方法: -这里有个我在一个技术大牛“回忆录”中看到的办法: -先按照教程学习,然后合上书、拔了网线,把刚才学到的内容用程序写出来,注意不要写太多。 -编译通过后,进行反向思维,写一段性能更烂的程序,并时刻关注内存耗费情况。 -再次编译通过后,重复上述步骤,再写一段性能更烂的程序,并时刻关注cpu耗费情况。 -………… - -重复上述步骤数次后,你会发现你再也写不出比最后一段更烂的程序了,然后你会发现为了写更烂的程序你竟然把各种知识点都掌握了。 - -## 无论做什么,原则最重要 - -无论什么时候,你都不能以身体为代价去做一些事。 -熬夜是不好的习惯(哎,虽然我在熬夜写稿),你为了追求一个安静的环境、你完全可以背上电脑,去图书馆。 -最后一点 时间都是挤出来的,没有时间只是找理由逃避。 -怎么样,你还想入坑吗? - diff --git a/Skills/Network/CDN.md b/Skills/Network/CDN.md new file mode 100644 index 0000000..dcda145 --- /dev/null +++ b/Skills/Network/CDN.md @@ -0,0 +1,20 @@ +--- +title: CDN +date: 2024-09-09 10:22:38 +tags: +categories: +--- + + +💠 + +- 1. [CDN](#cdn) + +💠 2024-09-09 10:22:38 +**************************************** +# CDN +例如自己注册了域名 xxx.com, 此时需要CDN服务商帮你对网站静态资源做CDN加速, 通常会提供一个域名X,让 xxx.com CNAME到 X 域名上。 +使用CNAME而不是A解析 好处是,X域名的A解析掌控权在CDN服务商,其可以依据地域,时间做动态调整,而不需要去 xxx.com 的控制台改解析。 + +> [CDN Up and Running](https://github.com/leandromoreira/cdn-up-and-running) + diff --git a/Skills/Network/HTTP.md b/Skills/Network/HTTP.md new file mode 100644 index 0000000..5680806 --- /dev/null +++ b/Skills/Network/HTTP.md @@ -0,0 +1,345 @@ +--- +title: HTTP +date: 2019-03-17 14:23:29 +tags: + - 网络 +categories: + - 计算机基础 +--- + +💠 + +- 1. [HTTP](#http) + - 1.1. [URI URL](#uri-url) + - 1.2. [请求方法](#请求方法) + - 1.2.1. [CONNECT](#connect) + - 1.2.2. [GET](#get) + - 1.2.3. [POST](#post) + - 1.3. [请求 Content-Type](#请求-content-type) + - 1.3.1. [Form](#form) + - 1.4. [Response响应](#response响应) + - 1.4.1. [HTTP的响应状态码](#http的响应状态码) + - 1.4.2. [Header](#header) + - 1.5. [HTTP 缓存](#http-缓存) + - 1.6. [Session 和 Cookie](#session-和-cookie) + - 1.6.1. [Cookie](#cookie) + - 1.6.2. [Session](#session) + - 1.7. [Auth](#auth) + - 1.7.1. [Basic-Auth](#basic-auth) +- 2. [HTTP各个实现版本](#http各个实现版本) + - 2.1. [HTTP/0.9](#http09) + - 2.2. [HTTP/1.0](#http10) + - 2.3. [HTTP/1.1](#http11) + - 2.4. [HTTP/2](#http2) + - 2.5. [HTTP/3](#http3) +- 3. [HTTPS](#https) + - 3.1. [HTTPS 证书认证流程](#https-证书认证流程) + - 3.2. [HSTS](#hsts) +- 4. [Tips](#tips) + - 4.1. [CORS 跨域](#cors-跨域) + - 4.2. [相关工具](#相关工具) + +💠 2024-10-10 10:41:00 +**************************************** +# HTTP +> HyperText Transfer Protocol (超文本传输协议) 他是一种用于分布式、协作式和超媒体信息系统的应用层协议 + +- [MDN HTTP教程](https://developer.mozilla.org/zh-CN/docs/Web/HTTP) + +> [参考: HTTP详解](https://mp.weixin.qq.com/s?__biz=MzI3NzE0NjcwMg==&mid=2650123318&idx=1&sn=a0b20cfeda4bf0445de8c981533aca1b&chksm=f36bb117c41c3801a951407743aa9e850aa2d5e834fb87ecdc472c871b74d60254fe7d03cd4d&mpshare=1&scene=1&srcid=&pass_ticket=C2ojewcjO3f%2B94ARPux0b29jNh%2BTyGIKOzdhalFniunqbWndDpBOllbB79D9AqM0#rd) + +- 其实HTTP协议主要就是用来进行客户端和服务器之间进行通信的标准协议。 +- HTTP主要规定了客户端如何与服务器建立链接、客户端如何从服务器请求数据、服务器如何响应请求,以及最后连接如何关闭 + +## URI URL +> [维基百科](https://en.wikipedia.org/wiki/URL) | [百度百科](https://baike.baidu.com/item/URL) + +- 统一资源定位符 特别注意URL的组成和编解码 [url中的特殊字符问题](http://www.cnblogs.com/xmphoenix/archive/2011/04/20/2022945.html) + - 不能在URL的关键位置出现%号,作为参数的值是允许的。 +> [What is the maximum length of a URL in different browsers?](https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers) 不同浏览器实现不一样 + +## 请求方法 +- HTTP/1.1协议中共定义了八种方法(有时也叫“动作”)来表明Request-URI指定的资源的不同操作方式: + - `OPTIONS` 返回服务器针对特定资源所支持的HTTP请求方法。也可以利用向Web服务器发送'*'的请求来测试服务器的功能性。  + - `GET` 向特定的资源发出请求。注意:GET方法不应当被用于产生“副作用”的操作中,例如在web app.中。其中一个原因是GET可能会被网络蜘蛛等随意访问。  + - `HEAD` 向服务器索要与GET请求相一致的响应,只不过响应体将不会被返回。这一方法可以在获取包含在响应消息头中的元信息而不传输内容。 + - `POST` 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或已有资源的修改。  + - `PUT` 向指定资源位置上传其最新内容。  + - `DELETE` 请求服务器删除Request-URI所标识的资源。  + - `TRACE` 回显服务器收到的请求,主要用于测试或诊断。  + - `CONNECT` HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。  + +> 方法名称是区分大小写的。当某个请求所针对的资源不支持对应的请求方法的时候,服务器应当返回状态码405(Method Not Allowed);当服务器不认识或者不支持对应的请求方法的时候,应当返回状态码501(Not Implemented)。 + +- [ ] Header中一些常用属性的含义和使用场景 + +HTTP请求实际是就是文本协议,报文样例: +``` +POST /pageList HTTP/1.1\r\n +Host: www.jd.com\r\n +User-Agent: xx\r\n +Content-Length: 121\r\n +\r\n +{"pageSize":2} +``` + +### CONNECT +代理服务器代理 HTTPS请求时,客户端会先发起一个 `CONNECT host:443 HTTP/1.1` 请求尝试建立连接 [参考Doc: mitmproxy](https://docs.mitmproxy.org/stable/concepts-howmitmproxyworks/) + +### GET +> [参考: GET 请求中 URL 的最大长度限制](https://blog.csdn.net/dream_weave/article/details/105143562) + +get 方式下的http请求会限制URL长度,会有多方面不同的限制 请求经过的三层后的短板效应:客户端 代理端 服务端 +- 客户端 各大浏览器会有实现上的差异 从 2083 到20000 不等 +- 代理端 Nginx Apache IIS +- 服务端 Java的SpringMVC 等 + +### POST + +- 标准的HTTP使用规范是参数全部使用body来传递,但是为了实现授权等功能的通用性,某些大厂会折腾出特殊的接口格式 + - https://api.com/getUserInfo?token=xxx body传输JSON格式的userId等参数 + +************************ +## 请求 Content-Type +[MDN: Content-Type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type) + +### Form +[form-data vs -urlencoded](https://gist.github.com/joyrexus/524c7e811e4abf9afe56) +[application/x-www-form-urlencoded or multipart/form-data?](https://stackoverflow.com/questions/4007969/application-x-www-form-urlencoded-or-multipart-form-data) + +- `application/x-www-form-urlencoded` 常用于表单提交 +- `multipart/form-data` 常用于文件上传 + +`文件上传` +1. 创建文件 echo "sss" > b.docx +1. 上传文件 +```sh + # firefox 请求Body + -----------------------------234019508041567584373971060997 + Content-Disposition: form-data; name="file"; filename="b.docx" + Content-Type: application/wps-office.docx + + sss + + -----------------------------234019508041567584373971060997-- + + # chrome 请求Body + ------WebKitFormBoundarybgjKLu2gfBqPLex4 + Content-Disposition: form-data; name="file"; filename="b.docx" + Content-Type: application/wps-office.docx + + + ------WebKitFormBoundarybgjKLu2gfBqPLex4-- +``` +- 可以看出 Body 组成部分: 开始标记,文件元信息,结束标记 +- Header中会声明边界标记 `Content-Type: multipart/form-data; boundary=-----------------------------234019508041567584373971060997` + +************************ + +## Response响应 +### HTTP的响应状态码 +> [HTTP 状态码 完整列表](/FrontEnd/ResponseCode.md) + +### Header + +> 注意: +- Header的Key和Value统一用`: `分隔,value部分没有格式约束,可以由应用层沟通协定,多值时有用,或;分隔的 + +> 常用Key +- Transfer-Encoding: chunked + - `异常` curl: (56) Illegal or missing hexadecimal sequence in chunked-encoding + - [MDN: Transfer-Encoding](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Transfer-Encoding) + - [wiki ](https://en.wikipedia.org/wiki/Chunked_transfer_encoding#Format) +- Range 请求指定的字节分段数据 + - [MDN: Range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range) + - [Listing the contents of a remote ZIP archive, without downloading the entire file](https://rhardih.io/2021/04/listing-the-contents-of-a-remote-zip-archive-without-downloading-the-entire-file/) 无需获取完整压缩包就得到文件列表以及压缩包内部分文件下载 + - 通过传输部分字节解析zip包元信息再多次依据偏移量读取文件,从而获得文件列表和对应字节偏移值实现特定文件的下载 + - 大文件时会有效率的改善,小zip包以及文件数过多时都不适合。 并且要为每种压缩算法定制实现解析逻辑,而且可行的只有zip和7z + +************************ + +## HTTP 缓存 + +************************ + +## Session 和 Cookie +> 最简单的区别是session存储在服务端,cookie是存储在客户端 + +例如在Tomcat的实现:Session存储在Tomcat内存中,客户端与服务端建立会话后,生成Session并返回JSESSIONID给客户端 +客户端将值存储在cookie中供请求使用(请求会默认携带同域名的cookie),使得无状态的HTTP请求状态化 + +### Cookie +- Cookie具有不可跨站性(但这是浏览器安全标准,主流浏览器都实现了,但也意味着可以开发恶意浏览器无视该规范) + - [MDN SameSite](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie/SameSite) + - [参考: Cookie 的 SameSite 属性](http://www.ruanyifeng.com/blog/2019/09/cookie-samesite.html) + +************************ + +> 禁用Cookie如何正常使用Session +首先明确场景,客户端禁用Cookie后,服务端无差别的在response的Header中携带Cookie信息,但是客户端会拒绝写入 +1. URL 重写的方式,将SessionId编码进URL +1. 是否可以使用LocalStorage 或者 SessionStorage 存储SessionId,因为本质上Cookie内的SessionId只是为了能对应于在服务器保存的Session实现有状态的HTTP + +### Session +> 通常各种语言,Web服务器的实现逻辑都不一样 + +1. 例如在Tomcat中,Session的实现默认是存储在内存中(也可以存储在文件,数据库中),具有过期时间 [Tomcat 中的 Session 和 Cookie ](https://www.cnblogs.com/chuonye/p/10846998.html) + 1. SessionInitializerFilter 过滤器中会调用 request的getSession方法 + - 设置为过滤器的原因 该类的JavaDoc 有说明,例如为了 Websocket + 1. 最终调用 org.apache.catalina.connector.Request#doGetSession 判断是否有Session没有就创建 + - 创建Session的同时会在 request 关联的 response中写入对应的Cookie + +1. [参考: PHP中Session 的实现](https://www.runoob.com/w3cnote/php-session-login.html) + +************************ + +## Auth +### Basic-Auth + +************************ + +# HTTP各个实现版本 +目前HTTP协议主要的版本有3个,分别是HTTP/1.0、HTTP/1.1和HTTP/2 + +## HTTP/0.9 +> HTTP于1990年问世,那时候HTTP非常简单:只支持GET方法;没有首部;只能获取纯文本。 + +************************ + +## HTTP/1.0 +> 1996年5月,HTTP/1.0 版本发布,为了提高系统的效率,HTTP/1.0规定浏览器与服务器只保持短暂的连接 + +- 浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。 +- HTTP/1.0中浏览器与服务器只保持短暂的连接,连接无法复用。也就是说每个TCP连接只能发送一个请求。发送数据完毕,连接就关闭,如果还要请求其他资源,就必须再新建一个连接。 +- 我们知道TCP连接的建立需要三次握手,是很耗费时间的一个过程。所以,HTTP/1.0版本的性能比较差。 +- 对于同一个tcp连接,所有的http1.0请求放入队列中,只有前一个请求的响应收到了,然后才能发送下一个请求。可见,http1.0的队首组塞发生在客户端。 + +************************ + +## HTTP/1.1 +> HTTP/1.1于1999年诞生。相比较于HTTP/1.0来说,最主要的改进就是引入了持久连接(PersistentConnection)和请求的流水线(Pipelining)处理 + +1. 还提供了与身份认证、状态管理和Cache缓存等机制相关的请求头和响应头。 +1. 强制客户端提供 host 头域 +1. 加入了一个新的状态码100(Continue) : 为了节约带宽 + - 客户端事先发送一个只带头域的请求,如果服务器因为权限拒绝了请求,就回送响应码401(Unauthorized); + - 如果服务器接收此请求就回送响应码100,客户端就可以继续发送带实体的完整请求了。 + - 100 状态代码的使用,允许客户端在发request消息body之前先用request header试探一下server,看server要不要接收request body,再决定要不要发request body。 +1. HTTP/1.1在1.0的基础上加入了一些cache的新特性,当缓存对象的Age超过Expire时变为stale对象,cache不需要直接抛弃stale对象,而是与源服务器进行重新激活(revalidation) + +> 存在的问题: +>1. 队首阻塞:TCP连接上只能发送一个请求,前面的请求未完成前,后续的请求都在排队等待 +>1. 多个TCP连接: 虽然HTTP/1.1管线化可以支持请求并发,但是浏览器很难实现,chrome、firefox等都禁用了管线化。所以1.1版本请求并发依赖于多个TCP连接,建立TCP连接成本很高,还会存在慢启动的问题。 +>1. 头部冗余,采用文本格式 HTTP/1.X版本是采用文本格式,首部未压缩,而且每一个请求都会带上cookie、user-agent等完全相同的首部。 +>1. 客户端需要主动请求 + +*** + +`持久连接` 即TCP连接默认不关闭,可以被多个请求复用。HTTP 1.1的持久连接,也需要增加新的请求头来帮助实现 例如 +- Connection请求头的值为Keep-Alive时,客户端通知服务器返回本次请求结果后保持连接; + - 使用 Keep-Avlive 方式时, 在传送数据时要么响应头指定 Content-Length,要么用 chunked 编码,这两种方法都可以判断一次请求的数据发送完成。 + - 在一次请求结束之后,TCP 链接才可被复用进行下一次请求,否则根本无法判断当前传送的数据属于哪次请求的 +- Connection请求头的值为close时,客户端通知服务器返回本次请求结果后关闭连接。 + +*** + +请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟。例如: +> 一个包含有许多图像的网页文件上的多个资源请求和应答可以在一个连接中传输 +> 但每个单独的网页文件的请求和应答仍然需要使用各自的连接。 + +- 对于同一个 tcp 连接,http1.1 允许一次发送多个 http1.1 请求,也就是说,不必等前一个响应收到,就可以发送下一个请求. +- 这样就解决了 http1.0 的客户端的队首阻塞。但是,http1.1 规定,服务器端的响应的发送要根据请求被接收的顺序排队. +- 也就是说,先接收到的请求的响应也要先发送。这样造成的问题是,如果最先收到的请求的处理时间长的话,响应生成也慢,就会阻塞已经生成了的响应的发送。 +- 也会造成队首阻塞。队首阻塞是发生在服务端的 + +************************ + +## HTTP/2 +> HTTP/2 是 HTTP 协议自 1999 年 HTTP 1.1 发布后的首个更新,主要基于 SPDY 协议(2012年google提出) +> [wiki HTTP/2](https://en.wikipedia.org/wiki/HTTP/2) + +> [参考: 面试官问:你了解HTTP2.0吗?](https://juejin.im/post/5c0ce870f265da61171c8c66) +> [参考: HTTP/2 幕后原理](https://www.ibm.com/developerworks/cn/web/wa-http2-under-the-hood/index.html) +> [参考: HTTP/2](http://www.hollischuang.com/archives/2066) +> [参考: HTTP/2 服务器推送(Server Push)教程](http://www.ruanyifeng.com/blog/2018/03/http2_server_push.html) + +- [http2 golang](https://http2.golang.org/) +- [golang gin http2](https://github.com/gin-gonic/examples/tree/master/http2) +- [h2o](https://h2o.examp1e.net/) + +> 新概念 +1. 流(Stream):已建立的TCP连接上的双向字节流,可以承载一个或多个消息。 一个TCP连接上可以有任意数量的流。 +1. 消息(Message):一个完整的HTTP请求或响应,由一个或多个帧组成。特定消息的帧在同一个流上发送,这意味着一个HTTP请求或响应只能在一个流上发送。 +1. 帧(Frame):通信的基本单位。 + +> 新实现 +1. 二进制格式(Binary Format) + - HTTP2性能提升的核心就在于二进制分帧层。HTTP2是二进制协议 + - 1.1响应是文本格式,而2.0把响应划分成了两个帧,HEADERS(首部)和DATA(消息负载) 是帧的类型 + +1. 多路复用(MultiPlexing) + - HTTP2建立一个TCP连接,一个连接上面可以有任意多个流(stream),消息分割成一个或多个帧在流里面传输。帧传输过去以后,再进行重组,形成一个完整的请求或响应。这使得所有的请求或响应都无法阻塞。 + +1. header压缩 (HPACK算法实现) + - 在1.X版本中,首部用文本格式传输,通常会给每个传输增加500-800字节的开销。现在打开一个网页上百个请求已是常态 + - 而每个请求带的一些首部字段都是相同的,例如cookie、user-agent等。HTTP2为此采用HPACK压缩格式来压缩首部。头部压缩需要在浏览器和服务器端之间: + - 维护一份相同的静态字典([Static Table Definition](https://httpwg.org/specs/rfc7541.html#static.table.definition)),包含常见的头部名称,以及常见的头部名称和值的组合 + - 维护一份相同的动态字典,可以动态的添加内容 + - 通过静态Huffman编码对传输的首部字段进行编码 + +1. 服务端推送(server push) + - 服务器端推送使得服务器可以预测客户端需要的资源,主动推送到客户端。 + - 例如:客户端请求index.html,服务器端能够额外推送script.js和style.css。 + - 实现原理就是客户端发出页面请求时,服务器端能够分析这个页面所依赖的其他资源,主动推送到客户端的缓存,当客户端收到原始网页的请求时,它需要的资源已经位于缓存。 + - [Github: http2-chat-example](https://github.com/ebakhtarov/http2-chat-example ) + +> HTTP 2.0 才是正常的 ISO 七层协议的实现,增加了第六层表示层 +> 如果一开始就接触 HTTP 2.0,也就不会有所谓的“粘包”问题了,因为TCP发送数据时,数据本身就是结构化数据,实现了自我的边界定位 + +Java: JDK9 才正式支持 + +************************ + +## HTTP/3 +> [wiki: HTTP/3](https://en.wikipedia.org/wiki/HTTP/3) + +> [参考: HTTP3.0(QUIC的实现机制)](https://www.cnblogs.com/chenjinxinlove/p/10104854.html) +> [参考: 一文看完 HTTP3 的演化历程](https://www.infoq.cn/article/IgME_4ebP3d46m3tHbaT) +> [参考: HTTP3](https://http3-explained.haxx.se/zh/) +> [HTTP/1 to HTTP/2 to HTTP/3](https://medium.com/@sandeep4.verma/http-1-to-http-2-to-http-3-647e73df67a8) + +HTTP/3 是一种基于 IETF QUIC(一种基于 UDP 的多路复用和安全传输)的新 HTTP 语法。 + + +************************ + +# HTTPS +> [SSL & TLS](/Skills/Network/WebSecurity.md#ssl-tls) + +## HTTPS 证书认证流程 +1. 服务器生成一对密钥,私钥自己留着,公钥交给数字证书认证机构(CA) +1. CA进行审核,并用CA自己的私钥对服务器提供的公钥进行签名生成数字证书 +1. 在 HTTPS 建立连接时,客户端从服务器获取数字证书,用CA的公钥(根证书)对数字证书进行验证,比对一致,说明该数字证书确实是CA颁发的 + - 得此结论有一个前提就是:客户端的CA公钥确实是CA的公钥(反例:中间人攻击),即该CA的公钥与CA对服务器提供的公钥进行签名的私钥确实是一对。 + - 而CA又作为权威机构保证该公钥的确是服务器端提供的,从而可以确认该证书中的公钥确实是合法服务器端提供的。 + +注:为保证第3步中提到的前提条件,CA的公钥必须要安全地转交给客户端(CA根证书必须先安装在客户端) +因此,CA的公钥一般来说由浏览器开发商内置在浏览器的内部。于是,该前提条件在各种信任机制上,基本保证成立。 + +## HSTS +> HTTP `Strict Transport Security` 强制让客户端使用HTTPS进行通信 + +通常能在请求的 Response 的 Header 中看到 `Strict-Transport-Security: max-age=` + +> [MDN HSTS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) + +************************ +# Tips +## CORS 跨域 +> [浏览器的同源策略](https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy)`重点在于这个是浏览器的实现规范,众多浏览器厂商约定实现,如果是自定义的HTTP客户端,可以不按此规范实现也就不用考虑这个限制` + +> [mozilla CORS](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS) +> [阮一峰 跨域资源共享 CORS 详解](http://www.ruanyifeng.com/blog/2016/04/cors.html) +> [CORS详解.md](https://github.com/hstarorg/HstarDoc/blob/master/%E5%89%8D%E7%AB%AF%E7%9B%B8%E5%85%B3/CORS%E8%AF%A6%E8%A7%A3.md) + +## 相关工具 diff --git a/Skills/Network/MITM.md b/Skills/Network/MITM.md new file mode 100644 index 0000000..6fbf6c6 --- /dev/null +++ b/Skills/Network/MITM.md @@ -0,0 +1,17 @@ +--- +title: Man in the middle +date: 2020-03-27 15:24:01 +tags: +categories: +--- + +**目录 start** + +1. [Man in the middle](#man-in-the-middle) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Man in the middle +> 中间人攻击 + +TODO 2020-03-27 Github疑似遭遇此攻击 diff --git a/Skills/Network/Network.md b/Skills/Network/Network.md new file mode 100644 index 0000000..efee2e2 --- /dev/null +++ b/Skills/Network/Network.md @@ -0,0 +1,684 @@ +--- +title: 计算机网络 +date: 2018-11-21 10:56:52 +tags: + - 基础 + - 网络 +categories: + - 计算机基础 +--- + +💠 + +- 1. [网络](#网络) + - 1.1. [相关书籍和资源](#相关书籍和资源) +- 2. [网络分层架构](#网络分层架构) + - 2.1. [物理层](#物理层) + - 2.2. [数据链路层](#数据链路层) + - 2.3. [网络层](#网络层) + - 2.3.1. [SDN](#sdn) + - 2.3.2. [IP协议](#ip协议) + - 2.3.2.1. [IP地址分类](#ip地址分类) + - 2.3.2.2. [无分类编址 CIDR](#无分类编址-cidr) + - 2.3.3. [ARP协议](#arp协议) + - 2.3.4. [ICMP协议](#icmp协议) + - 2.4. [传输层](#传输层) + - 2.4.1. [TCP](#tcp) + - 2.4.2. [UDP](#udp) + - 2.4.3. [TCP UDP 对比](#tcp-udp-对比) + - 2.4.4. [SCTP](#sctp) + - 2.5. [应用层](#应用层) + - 2.5.1. [HTTP & HTTPS](#http-&-https) + - 2.5.2. [Websocket](#websocket) + - 2.5.3. [FTP](#ftp) + - 2.5.4. [WebDAV](#webdav) + - 2.5.5. [TTFB](#ttfb) + - 2.5.6. [DNS](#dns) + - 2.5.7. [MDNS](#mdns) + - 2.5.7.1. [安全性问题](#安全性问题) + - 2.5.8. [VPN](#vpn) + - 2.5.9. [SSDP](#ssdp) + - 2.5.10. [KCP](#kcp) + - 2.5.11. [QUIC](#quic) + - 2.5.12. [P2P](#p2p) + - 2.5.13. [网络隧道](#网络隧道) +- 3. [Socket](#socket) +- 4. [单播 组播 广播](#单播-组播-广播) + - 4.1. [单播](#单播) + - 4.2. [组播](#组播) + - 4.3. [广播](#广播) +- 5. [代理 Proxy](#代理-proxy) + - 5.1. [代理协议](#代理协议) + - 5.1.1. [HTTP代理](#http代理) + - 5.1.2. [SOCKS](#socks) + - 5.2. [PAC](#pac) + - 5.3. [正向代理](#正向代理) + - 5.4. [反向代理](#反向代理) + - 5.5. [透明代理](#透明代理) + - 5.6. [应用的代理设置](#应用的代理设置) + - 5.6.1. [Java](#java) +- 6. [网络工具](#网络工具) + - 6.1. [抓包 代理工具](#抓包-代理工具) + - 6.1.1. [Clash](#clash) + - 6.1.2. [Fiddler](#fiddler) + - 6.1.3. [Charles](#charles) + - 6.1.4. [mitmproxy](#mitmproxy) + - 6.1.5. [tinyproxy](#tinyproxy) + - 6.1.6. [Mars](#mars) + - 6.1.7. [camilla](#camilla) + - 6.1.8. [dev-proxy](#dev-proxy) + - 6.1.9. [ProxyPin](#proxypin) + - 6.1.10. [eCapture](#ecapture) + - 6.2. [Wireshark](#wireshark) +- 7. [Tips](#tips) + - 7.1. [移动通信技术规格](#移动通信技术规格) + - 7.2. [网络延迟](#网络延迟) + +💠 2024-10-11 15:07:39 +**************************************** +# 网络 + +## 相关书籍和资源 +> [Real time Webservice](http://ceur-ws.org/Vol-601/EOMAS10_paper13.pdf) + +> [Beej's Guide to Network Programming](http://beej.us/guide/bgnet/) + +- webapi的设计与开发 +- 网络是怎样连接的 +- 计算机网络 谢希仁 + +> [码农翻身:小白科普:从输入网址到最后浏览器呈现页面内容,中间发生了什么?](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665514196&idx=1&sn=ca26d258fcc4a35fc6d9a539b7d71dd7&chksm=80d67c97b7a1f58198b2e6ae436f73c677c0df4c05c2a8a4aad2b9e2d523da57dd5cd3d0a8ee&mpshare=1&scene=1&srcid=0122nnRpNb6OvRJubkSfKfsZ&pass_ticket=%2B%2FAmfhAaNv2sKw6192eqEL9hoW%2F6BrLxlzHIsKC0k6lPQsM4%2FFo08R%2FZowzw3821#rd) | +> [码农翻身:我是一个路由器](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513173&idx=1&sn=6ec5281b12ed5195070fa4df22383595&scene=21#wechat_redirect) | +> [码农翻身:我是一个网卡](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513160&idx=1&sn=d938db4f1a2d62514b57e92fd8d3d749&scene=21#wechat_redirect) + +# 网络分层架构 + +开放系统互连基本参考模型 OSI/RM Open Systems Interconnection Reference Model, 简称 OSI + +| OSI | TCP/IP | +|:----|:----:| +| 应用层
表示层
会话层 | 应用层 | +| 传输层 | 传输层 | +| 网络层 | 网络层 | +| 数据链路层
物理层 | 链路层 | + +OSI制定的OSI七层参考模型的过于庞大、复杂。与此对照,由技术人员设计的 TCP/IP协议栈(`四层`: 网络接口 网络层 传输层 应用层) 获得了更为广泛的应用。 +谢希仁所著的 **计算机网络** 书中 为了教学, 融合成了 `五层模型`, 事实上, 现在应用的实际使用并不严格按照OSI分层, 应用层可以使用 传输层协议(TCP UDP), 也可以直接使用网络层(IP)。 + +> [参考: 以太网帧结构](https://blog.csdn.net/wdkirchhoff/article/details/43915825) + +应用层的单个内容 叫 message 消息 +传输层的单个内容 叫 segment 段 +网络层的单个内容 叫 datagram 报文 +链路层的单个内容 叫 frame 数据帧 + +![](/Skills/Network/img/001-network-base.km.svg) + +************************ + +## 物理层 +> 集线器 工作在这一层 + +## 数据链路层 +> 常规交换机 工作在这一层 + +> 通常在帧的前面插入 8 字节,其中的第一个字段共 7 个字节,是前同步码,用来迅速实现 MAC帧的比特同步。第二个字段是帧开始定界符,表示后面的信息就是MAC帧 + +- MAC帧 组成为 `首部(MAC地址等信息) 数据(IP数据报) 尾部` +- 首部构成为 + 1. 目的地址 **6字节** + 1. 源地址 **6字节** + 1. 类型 **2字节** 标志上一层使用的是什么协议 +- 数据部分 (46-1500字节) +- 尾部:**4字节**长,包含的信息是帧校验序列FCS(使用CRC校验) + +- PPP帧 + +************************ + +## 网络层 +> 网络接口卡(网卡)、交换机、路由器、网关、集线器、网桥等硬件工作在这一层 + + +### SDN +> [wiki](https://en.wikipedia.org/wiki/Software-defined_networking) +> 相比于传统的路由器查路由表的工作方式,更灵活可控,成本更低。 + +传统网络设备紧密耦合了管理平面(命令行,图形界面),数据平面(报文处理和转发),控制平面(路由表,MAC交换表等等)。 + +> [SDN概述](https://drobp.github.io/2019/08/02/SDN%E6%A6%82%E8%BF%B0/) +> [RedHat: 什么是软件定义网络?](https://www.redhat.com/zh/topics/hyperconverged-infrastructure/what-is-software-defined-networking) + +************************ + +### IP协议 + +> [IP数据报格式详解](https://blog.csdn.net/wangzhen209/article/details/74453548) + +- IP数据报 组成为 `首部(IP地址等信息) 数据(TCP/UDP报文)` +- 首部固定部分构成为: + 1. `版本号`:占用**4位**二进制数,表示该IP数据报使用的IP协议版本。目前Internet中使用的主要是TCP/IP协议族中版本号为4的IP协议。 + 1. `首部长度`:占用**4位**二进制位,此域指出整个报头的长度(包括选项),该长度是以32位二进制数为一个计数单位的, + - 接收端通过此域可以计算出报头在何处结束及从何处开始读数据。普通IP数据报(没有任何选项)该字段的值是5(即20个字节的长度)。 + 1. `服务类型(TOS、type of service)`:占用**8位**二进制位,用于规定本数据报的处理方式。 + 1. `总长度`:占用**16位**二进制位,总长度字段是指整个IP数据报的长度(报头区+数据区),以字节为单位。 + - 利用头部长度字段和总长度字段就可以计算出IP数据报中数据内容的起始位置和长度。由于该字段长度为16位二进制数, + - 因此理论上IP数据报最长可达65536个字节(事实上受物理网络的限制,要比这个数值小很多)。 + 1. `标识` **16位** IP软件在存储器中维持一个计数器,每产生一个数据报,计数器就加1,并将此值赋给标识字段。 + - 但这个“标识”并不是序号,因为IP是无连接服务,数据报不存在按序接收的问题。当数据报由于长度超过网络的MTU而必须分片时, + - 这个标识字段的值就被复制到所有的数据报的标识字段中。相同的标识字段的值使分片后的各数据报片最后能正确地重装成为原来的数据报。 + 1. `标志` **3位** 但只有2位有意义。 + - 标志字段中的最低位记为MF(More Fragment)。MF=1即表示后面“还有分片”的数据报。MF=0表示这已是若干数据报片中的最后一个。 + - 标志字段中间的一位记为DF(Don’t Fragment),意思是“不能分片”。只有当DF=0时才允许分片。 + 1. `片偏移` **13位** 片偏移指出:较长的分组在分片后,某片在原分组中的相对位置。也就是说,相对用户数据字段的起点,该片从何处开始。 + - 片偏移以8个字节为偏移单位。这就是说,除了最后一个分片,每个分片的长度一定是8字节(64位)的整数倍。 + 1. `生存时间(TTL,time to live)`:占用**8位**二进制位,它指定了数据报可以在网络中传输的最长时间。 + - 实际应用中把生存时间字段设置成了数据报可以经过的最大路由器数。TTL的初始值由源主机设置(通常为32、64、128或256), + - 一旦经过一个处理它的路由器,它的值就减1。当该字段为0时,数据报就丢弃,并发送ICMP报文通知源主机, + - 因此可以防止进入一个循环回路时,数据报无休止地传输下去。 + 1. `上层协议标识`:占用**8位**二进制位,IP协议可以承载各种上层协议,目标端根据协议标识就可以把收到的IP数据报送到TCP或UDP等处理此报文的上层协议了。 + 1. `校验和`:占用**16位**二进制数,用于协议头数据有效性的校验,可以保证IP报头区在传输时的正确性和完整性。 + - 头部检验和字段是根据IP协议头计算出的检验和,它不对头部后面的数据进行计算。 + - 原理:发送端首先将检验和字段置0,然后对头部中每16位二进制数进行反码求和的运算,并将结果存在校验和字段中。 + - 由于接收方在计算过程中包含了发送方放在头部的校验和,因此,如果头部在传输过程中没有发生任何差错,那么接收方计算的结果应该是全1。 + 1. `源地址`:占用32位二进制数,表示发送端IP地址。 + 1. `目的地址`:占用32位二进制数,表述目的端IP地址。 + +- IPv4 & IPv6 + +> [参考: 浏览器访问IPv6地址](http://www.cnblogs.com/cuihongyu3503319/p/7422877.html) + +#### IP地址分类 +> IP地址的由 网络号 主机号 组成 +- IP ::= {<网络号>, <主机号>} + +IPv4 地址由 32 位标识符组成,目前由 ICANN 进行分配 且在 2011 年已经耗尽 + +| 类别 | 标识位 | 网络号 | 主机号 | 最大可分配网络数 | 最小可分配网络号 | 最大可分配网络号 +|:---|:---|:---|:---|:---|:---|:---| +| A | 0 | 8位 | 24位 | 126 2^7 - 2 | 1 | 126 +| B | 10 | 16位 | 16位 | 16383 2^14 - 1 | 128.1 | 191.255 +| C | 110 | 24位 | 8位 | 2097151 2^21 - 1 | 192.0.1 | 223.255.255 +| D | 1110 | | | +| E | 1111 | | | + +> 特殊IP地址 + +| 网络号 | 主机号 | 源地址使用 | 目的地址使用 | 含义 | +|:---|:---|:---|:---|:---| +| 0 | 0 | 可以 | 不可以 | 在本网络上的本主机 | +| 0 | host-id | 可以 | 不可以 | 在本网络上的某个主机 host-id | +| 全1 | 全1 | 不可以 | 可以 | 只在本网络上进行广播 路由器不转发 | +| net-id | 全1 | 不可以 | 可以 | 对 net-id 的所有主机进行广播 | +| 127 | 非全0且非全1 | 可以 | 可以 | 作本地软件回环测试用 | + +- A类 +- B类 +- C类 +- D类 用于组播(多播) +- E类 保留地址 + +#### 无分类编址 CIDR +> 超网(supernetting),也称无类别域间路由选择,为了解决IPv4地址耗尽的问题 + +- IP ::= {<网络前缀>, <主机号>} +- 例如 Docker 使用的网段 `172.17.0.1/16` + +### ARP协议 +> 同一局域网内,通过已知的IP地址和找到对应的硬件地址 + +1. 每个主机都会有一个 ARP高速缓存,存储本局域网内各主机和路由器的IP地址和硬件地址的映射 +1. 当A要向主机B发送IP数据报就会查找该缓存,如果找不到 + 1. ARP 进程在 本局域网广播 发送ARP请求分组 信息大致为我的IP地址A 硬件地址B,想知道IP地址C的硬件地址 + 1. 本局域网内所有 ARP进程都会收到请求,IP地址与询问中的IP地址一致的才会响应请求,其他主机会丢弃 + 1. C 响应内容大致为 我的IP地址 C 硬件地址 D,注意该响应是单播,显然此时只需要点对点通信即可无需发送广播而产生大量无用数据报占据网络 + 1. A 收到响应后 将映射存入缓存 + +### ICMP协议 + +************ + +## 传输层 +> TCP/IP 运输层的两个主要协议都是因特网的正式标准 + +用户数据报协议 UDP `User Datagram Protocol` +控制传输协议 TCP `Transmission Control Protocol` + +按照 OSI 的术语, 两个对等运输实体在通信时传送的数据单位叫做 运输协议数据单元 TPDU Transport Protocol Data Unit. +但在 TCP/IP 体系中, 则根据使用的所使用的协议是 TCP 或 UDP, 分别称之为 TCP报文段 segment, UDP用户数据报 + +- Socket接口: 并不是一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。 +- Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部 + +### TCP +> 传输控制协议(英语:Transmission Control Protocol,缩写为 TCP, 是一种面向连接的、可靠的、基于**字节流**的传输层通信协议,由IETF的RFC 793定义。 + +- [wireshark tcp](https://www.wireshark.org/docs/wsug_html_chunked/ChAdvTCPAnalysis.html) + +> TCP 段的头部信息 20字节 也就是160位 +``` + 16位源端口号,16位目的端口号 + 32位序列号 + 32位确认序列号 + 4位首部长度, 保留6位, URG, ACK, PSH, PST, SYN, FIN, 16位窗口大小 + 16位校验和, 16位紧急指针 + 选项 + 数据... +``` + +> [Why do we need a 3-way handshake? Why not just 2-way?](https://networkengineering.stackexchange.com/questions/24068/why-do-we-need-a-3-way-handshake-why-not-just-2-way) + +> [TCP 重传、滑动窗口、流量控制、拥塞控制](https://www.xiaolincoding.com/network/3_tcp/tcp_feature.html) | [TCP congestion control](https://en.wikipedia.org/wiki/TCP_congestion_control) + +************************ +TCP状态 [详解TCP的11种状态](https://cloud.tencent.com/developer/article/1648432) + +握手阶段 CLOSED, LISTEN, SYN_RCVD, SYN_SENT, ESTABLISHED +挥手阶段 FIN_WAIT_1, FIN_WAIT_2, CLOSE_WAIT, LAST_ACK, TIME_WAIT, CLOSING + +> [大量的 TIME_WAIT 状态连接怎么处理?](https://cloud.tencent.com/developer/article/1675933) +- TIME_WAIT 由于需要等待 2ML(Maximum Segment Life,报文段最大生存时间) 时间才能关闭TCP连接, 频繁请求会导致创建出大量TIME_WAIT的TCP连接 + - 会占用一个IP层五元组:(协议,本地IP,本地端口,远程IP,远程端口) + - 对于 Web 服务器,协议是 TCP,本地 IP 通常也只有一个,本地端口默认的 80 或者 443。只剩下远程 IP 和远程端口可以变了。 + - 如果远程 IP 是相同的话,就只有远程端口可以变了。这个只有几万个,所以当同一客户端向服务器建立了大量连接之后,会耗尽可用的五元组导致无法建立连接。 + +> 大量 CLOSE_WAIT 如何处理 +- 表示正在等待关闭,该状态只在被动端出现,即当主动断开的一端调用close()后发送FIN报文给被动端,被动段必然会回应一个ACK(这是由TCP协议层决定的),这个时候,TCP连接状态就进入到CLOSE_WAIT +- 出现大量close_wait的原因就是:server接收到了client的FIN信号后进入close_wait状态,但后续并未发送FIN信号给client而是长期滞留在close_wait状态当中,而client一般会设置超时时间,所以即便最终server发出了FIN信号,此时很大概率client已经判断超时导致TCP连接异常。更严重的是,如果client还设置了重试策略,就会在server内部产生更多close_wait。 +- 那么server在什么情况下FIN包会发送失败? +- 程序问题:如果代码层面忘记了 close 相应的 socket 连接,那么自然不会发出 FIN 包,从而导致 CLOSE_WAIT 累积;或者代码不严谨,出现死循环之类的问题,导致即便后面写了 close 也永远执行不到。 +- 响应太慢或者超时设置过小:如果连接双方不和谐,一方不耐烦直接 timeout,另一方却还在忙于耗时逻辑,就会导致 close 被延后。响应太慢是首要问题,不过换个角度看,也可能是 timeout 设置过小。 +- BACKLOG 太大:此处的 backlog 不是 syn backlog,而是 accept 的 backlog,如果 backlog 太大的话,设想突然遭遇大访问量的话,即便响应速度不慢,也可能出现来不及消费的情况,导致多余的请求还在队列里就被对方关闭了。 + +异常状态多时可以查看Linux内核参数以及 连接队列和半连接队列 + +************************ + +> [漫画 | 一台Linux服务器最多能支撑多少个TCP连接? ](https://mp.weixin.qq.com/s/RdsNsanjeyVIkY6UuaJS4w) +- TCP连接四元组是源IP地址、源端口、目的IP地址和目的端口。只要任一元素发生了改变,那么就代表的是一条完全不同的连接。 + - 拿Nginx开启的80端口举例,目的IP地址、目的端口都是固定的。剩下源IP地址、源端口是可变的。 + - 所以理论上Nginx上最多可以建立 2的32次方(ip数) × 2的16次方(port数) 个连接。 +- 这是`两百多万亿`的一个大数字,想要建立这个量级的连接数,首先会被 可打开文件句柄,可用内存,CPU等资源制约。 + +************************ + +> KeepAlive +- [聊聊 TCP 长连接和心跳那些事 ](https://juejin.cn/post/6844903765674295309) + - HTTP 协议的 KeepAlive 意图在于tcp的连接复用,同一个连接上串行方式传递请求-响应数据。 + - TCP 的 KeepAlive 机制意图在于保活、心跳,检测连接错误。 + +### UDP + +> UDP 段的头部信息 8 字节 +``` + 16位源端口号,16位目的端口号 + 16位用户数据库包长度, 16位检查和 + 数据... +``` + +### TCP UDP 对比 +> [参考: TCP和UDP的最完整的区别](https://blog.csdn.net/li_ning_/article/details/52117463) +> [参考: TCP和UDP的区别和优缺点](https://blog.csdn.net/xiaobangkuaipao/article/details/76793702) +> [参考: TCP、UDP数据包大小的限制](https://blog.csdn.net/caoshangpa/article/details/51530685) + +- 可使用 wireshark 抓包对比的方式进行学习: 基于udp(默认)的dns方式,对比 基于tcp的dns方式 更直观看出 tcp 三次握手 四次挥手 -- 《Wireshark 网络分析就这么简单》 + +- TCP与UDP基本区别 + 1. 面向连接 与 无连接 + 1. TCP要求系统资源较多,UDP较少 + 1. UDP程序结构较简单 + 1. 流模式(TCP)与数据报模式(UDP); + - 开发者在使用TCP服务的时候,不必去关心数据包的大小,只需将SOCKET看作一条数据流的入口,直接传入数据,TCP协议本身会进行拥塞/流量控制 + - 但是也因此要处理粘包拆包的问题(此处属于概念混淆,正确描述应该是**应用层**上数据正确的序列化反序列化,包的概念仅处于网络层) + - UDP的最大包长度是2^16-1的个字节。由于udp包头占8个字节,而在ip层进行封装后的ip包头占去20字节,所以这个是udp数据包的最大理论长度是2^16-1-8-20=65507。 + - UDP包的大小受限于 操作系统中 MTU 的值(单位字节byte) 。 + - 查看lo设备:`cat /sys/class/net/lo/mtu` + - 试探出口MTU `ping -s 1472 jd.com` + - UDP数据报的数据区通常小于等于1472字节(通常设备MTU为1500,同样减去28) + - 如果超过1472很可能被分包,而其中一部分丢包,会导致整体无法被接受方组合数据被丢失,整体看来丢包概率会大大升高 + 1. TCP保证数据正确性,UDP 需要自己处理丢包和数据值被干扰 + 1. TCP保证数据顺序,UDP不保证 +   +- UDP应用场景: + 1. 面向数据报方式 + 1. 网络数据大多为短消息 + 1. 拥有大量Client + 1. 对数据安全性无特殊要求 + 1. 网络负担非常重,但对响应速度要求高 + +> Tips +- TCP和UDP可以共用一个接口,由于TCP和UDP对于IP层来说是不同的协议,因此五元组不冲突 + +### SCTP +> [Stream Control Transmission Protocol](https://es.wikipedia.org/wiki/Stream_Control_Transmission_Protocol) + +************************ + +## 应用层 + +### HTTP & HTTPS +> [HTTP & HTTPS](/Skills/Network/HTTP.md) + +************************ + +### Websocket +> Websocket协议 本质就是TCP的简单封装, 不像HTTP的单次应答模式, 而是建立连接后就保持全双工模式 +> [ietf websocket protocol](https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-17) + +1. 单一封装TCP连接, 采用全双工模式通信 +1. 对代理, 防火墙和路由器透明 +1. 无头部信息, Cookie, 身份验证 + - 生产使用时还是会做,通常在握手的HTTP请求中实现 将认证信息(Cookie/Token)放在Header或URL参数上 +1. 通过 ping/pong 二进制帧 保持链路激活 `可规避中间件关闭不活跃连接 例如Nginx` + - 但是客户端为js时不支持,可与服务端协商直接发ping文本 + +> 4个生命周期事件 +1. 打开事件:此事件发生在端点建立新连接时并且在任意其他事件发生之前 +2. 消息事件:此事件接收WebSocket对话中另一端发送的消息。他可以发生在WebSocket端点接收了打开事件之后并且在接收关闭事件关闭连接之前的任意时刻 +3. 错误事件:此事件在WebSocket连接或者端点发生错误时产生 +4. 关闭事件:此事件表示WebSocket端点的连接目前正在部分的关闭,他可以有参与连接的任意一个端点发出 + +************************ + +> 建立和断开流程 +- [理解websocket](https://zhuanlan.zhihu.com/p/149680021) +- 三次握手建立 TCP 连接(如果是 wss 还需要建立 tls 连接), 并从HTTP协议协商升级到WS具体的子协议 + - 客户端在HTTP请求Header中的`sec-websocket-version`设置协议版本 + - Netty中是 `io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory#newHandshaker` 中实现多版本 + - Spring-Websocket 中定义了接口: `org.springframework.web.socket.server.RequestUpgradeStrategy#getSupportedVersions` 在不同的Web容器实现中做声明支持 + - Spring5 有 Jetty Jetty10 Tomcat Undertow WebSphere +- 正常关闭时 TCP 的四次挥手,异常关闭则是 TCP 协议 发送 rst 包 + +> Tips +- 客户端和服务端建立连接后 客户端网络发生变化(例如VPN关闭,服务端在VPN网络下才可访问),此时客户端的定时ping会累积起来,等恢复后,一次发送多条数据,可以通过抓包观察到 +- 关闭状态码 + 1. [WebSocket RFC](https://tools.ietf.org/html/rfc6455#section-7.4) + 1. [WebSocket断开原因分析](https://wdd.js.org/websocket-close-reasons.html) + + +> [websocat](https://github.com/vi/websocat)`终端连接websocket` + +************************ + +> 安全问题 +- [WebSocket 安全性:8 大漏洞及其解决方法](https://blog.p2hp.com/archives/11444) `DDOS, 未验证的握手,未加密的TCP,数据脱敏,隧道技术,嗅探攻击` + +************************ +### FTP + +************************ + +### WebDAV +> WebDAV (Web-based Distributed Authoring and Versioning) 一种基于 HTTP 1.1协议的通信协议. [Wikipedia: webdav](https://en.wikipedia.org/wiki/WebDAV) + +它扩展了HTTP 1.1,在GET、POST、HEAD等几个HTTP标准方法以外添加了一些新的方法,使应用程序可直接对Web Server直接读写,并支持写文件锁定(Locking)及解锁(Unlock),还可以支持文件的版本控制。 + +云盘类平台(例如坚果云)会提供 WebDAV 协议接口,从而让操作云盘上的文件达到与本地目录和文件的使用体验。 + +> 相关工具 + +客户端: +- Windows: RaiDrive +- Ios: ES文件浏览器 + +服务端: +- [go开发WebDAV服务端](https://pkg.go.dev/golang.org/x/net/webdav) +- [go: simple webdav server](https://github.com/Kuangcp/GoBase/tree/master/toolbox/kwebdav) + +************************ +### TTFB +> `Time to first byte` 网络请求被发起到从服务器接收到第一个字节这段时间,它包含了 TCP连接时间,发送HTTP请求时间和获得响应消息第一个字节的时间。 + +### DNS +> 域名和资源转换的服务;完成域名解析需要依赖 基于UDP实现的 DNS 协议 + +> [www.ipaddress.com](https://www.ipaddress.com/)`查询域名的真实IP` 可协助解决DNS污染攻击 + +> [Wikipedia: DNS](https://en.wikipedia.org/wiki/Domain_Name_System) +> [面试官:讲讲DNS的原理?](https://zhuanlan.zhihu.com/p/79350395) + +- 解析域名的顺序一般是,先在本机找,找不到去找上连DNS服务器,然后根域DNS服务器 + - 几种查询方式:递归、迭代、递归加迭代(为了减轻全球13台根的压力) + - 假设是访问这个域名 scs.bupt.edu.cn (bupt.三级 机构域名, edu 二级行业域名, cn 一级国家域名) + - 递归: 本机->上连->根->cn->edu.cn->bupt.deu.cn 然后得到解析结果后,递归返回到上连,上连DNS服务器会进行缓存该结果,再返回本机 + - 迭代:本机->上连,上连->根,根->cn cn->edu.cn, edu.cn->bupt.deu.cn 最终返回了结果 到上连 + - 递归加迭代, 区别在于,先迭代根, 得到下级一级服务器节点后,下级就是递归的入口和出口 +- 授权和非授权, 还是上面那个URL, 其他的都不是授权的, 只有离URL最近的DNS才是授权的 即 `bupt.edu.cn` + +> 自建DNS服务器 +- smartDNS +- dnsmasq + +### MDNS +mDNS multicast DNS , 使用5353端口,组播地址 224.0.0.251 +每个进入局域网的主机,如果开启了mDNS服务的话,都会向局域网内的所有主机组播一个消息,我是谁,和我的IP地址是多少。 +然后其他也有该服务的主机就会响应,也会告诉你,它是谁,它的IP地址是多少。mDNS的域名与普通DNS的域名是通过后缀.local区分开来的。 +如果一台终端需要访问一个mDNS域名,他就会向局域网内发送组播,询问该域名的IP是多少。 + +#### 安全性问题 +DOH `DNS over HTTPS` 443端口 +DOT `DNS over TLS` 853端口 + +************************ + +### VPN +> Virtual Private Network (VPN) + +************************ + +### SSDP +> Simple Service Discovery Protocol + +### KCP +> [kcp](https://github.com/skywind3000/kcp) +> [kcp-go](https://github.com/xtaci/kcp-go) + +### QUIC + +### P2P +- eMule +- [BitTorrent](https://en.wikipedia.org/wiki/BitTorrent) + - [Search Plugins](https://github.com/qbittorrent/search-plugins) +- HCDN 爱奇艺所设计:按地域粒度建立多个CDN服务器,区域内的C端用户优先在区域内的不同C端用户找所需的视频资源并由区域CDN做兜底。2021年直接降低网站的带宽成本20%,白用所有C端用户的上传带宽。 + +### 网络隧道 +在特定的网络协议内传输新的协议内容,原网络协议就称为隧道 + +例如基于 SSH,DNS,HTTP 等协议构建隧道,传输TCP,HTTP协议的内容。 + +> [iodine](https://github.com/yarrick/iodine)`DNS隧穿` + +************************ + +# Socket +> [what is socket](https://unix.stackexchange.com/questions/16311/what-is-a-socket) + +Socket是应用层与TCP/IP协议族通信的中间软件抽象层,是对TCP/IP协议的封装,Socket本身并不是协议,而是一套接口(API),通过Socket,我们才能使用TCP/IP协议。 + +在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。 +而我们所说的socket编程指的是利用soket接口来实现自己的业务和协议, Socke接口属于软件抽象层,而sokcket编程却是标准的应用层开发。 + +> [参考: Socket编程](http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html) +> [参考: Linux socket 编程](https://www.ibm.com/developerworks/cn/education/linux/l-sock/l-sock.html) + +Socket通常翻译为套接字,那么为什么不直译为插座呢? +可能借用了工程领域的套接管(连接两个管道的一个构件),再结合管道内传输的都是字节流,因而命令为套接字 + +************************ + +# 单播 组播 广播 +广播通信只能在局域网内传播,组播通信能在公网内传播 + +组播通信相当于把主机与主机之间的通信压力转嫁到了路由器上面,所以要得到路由及网络的支持才能进行组播通信,整个传输过程中涉及的路由器或交换机都要支持组播通信,否则将无法使用组播通信 + +## 单播 + +## 组播 + +> [参考: 组播(Multicast)传输](http://www.cnblogs.com/ghj1976/p/5276452.html) + +## 广播 + +************************ + +# 代理 Proxy +> [wikipedia](https://en.wikipedia.org/wiki/Proxy) +> [Proxy servers and tunneling](https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling) + +## 代理协议 +### HTTP代理 + +例如 mitmproxy + +### SOCKS +由于SOCKS作用在会话层上,因此它是一个提供会话层到会话层间安全服务的方案,不受高层应用程序变更的影响。 +Socks代理只是简单地传递数据包,而不必关心是何种应用协议(比如FTP、HTTP和NNTP请求),所以Socks代理服务器比应用层代理服务器要快。 + +## PAC +> proxy auto config + +> [MDN: PAC File](https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling/Proxy_Auto-Configuration_(PAC)_file) + +本质上是一个js文件,提供了FindProxyForURL函数的自定义实现(依据不同的URL选择不同的Proxy或者不使用Proxy)。 +大多数操作系统的WIFI设置中都可以设置该地址,从而在操作系统层面让网络请求走这个代理规则`常见的软件可切换是否使用该代理设置 浏览器 IM工具等`。 + +> 示例1:全部走代理 +```js +function FindProxyForURL(url, host) { + return "PROXY 127.0.0.1:8080"; +} +``` + +> 示例2: 局域网使用A代理,域名通配使用B代理,直连不走代理 +```js +function FindProxyForURL(url, host) { + url = url.toLowerCase(); + host = host.toLowerCase(); + + if (url.startsWith("http:")) { + return "PROXY localhost:1234"; + } + + if (shExpMatch(url, "*github.com*")) { + return "PROXY 127.0.0.1:7890"; + } + + return "DIRECT"; +} +``` + +## 正向代理 +> 隐藏 客户端 地址, 去访问地址明确的服务端 + +- 应用: 科学上网, VPN +- 安全: 正向代理允许客户端通过它访问任意网站并且隐蔽客户端自身,因此你必须采取安全措施来确保仅为经过授权的客户端提供服务 + +## 反向代理 +> 隐藏 服务端 地址, 客户端通过明确地址访问的实际上是代理 + +- 应用: 保护和隐藏原始资源服务器, 负载均衡, 加密和SSL加速, 缓存静态内容, 安全(DDos的防护) +- 安全: 对外是透明的,访问者并不知道自己访问的是代理。对访问者而言,他以为访问的就是原始服务器 + +## 透明代理 +> 客户端根本不需要知道有代理服务器的存在,它改变你的 request fields(报文),并会传送客户端真实IP给服务端,多用于路由器的NAT转发中 + +透明代理的这层可以同时部署正向代理和反向代理 + +应用方无感使用缓存技术提高访问速度,能提高网络安全性(内网中的硬件防火墙。企业中的行为管理软件) + +## 应用的代理设置 +### Java +1. JVM Connection 方式 + - JVM参数 -Dhttp.proxyHost=127.0.0.1 -Dhttp.proxyPort=1234 `注意不是应用参数,是JVM参数和内存设置等参数同等级` +2. RestTemplate + - [Proxies With RestTemplate](https://www.baeldung.com/java-resttemplate-proxy) +3. HttpClient + - HttpClientBuilder.setProxy(new HttpHost("127.0.0.1", 1234)); + +************************ +# 网络工具 +## 抓包 代理工具 +> [Alternatives to Charles for Linux](https://alternativeto.net/software/charles/?platform=linux) +- [whistle](https://github.com/avwo/whistle) `nodejs 平台的抓包工具` + +### Clash +[Github](https://github.com/Dreamacro/clash) | [Fork copy](https://github.com/Ieooo/clash) + +[clash-dashboard](https://github.com/Dreamacro/clash-dashboard) + +### Fiddler +> [fiddler](https://www.telerik.com/fiddler)`由C#开发, 自定义脚本为C#` + +> [fiddler-everywhere](https://www.telerik.com/fiddler-everywhere) `Linux 免费` + +### Charles +> [Offcial Site](https://www.charlesproxy.com/) | [_](http://charles.iiilab.com/) + +### mitmproxy +> [Official Site](https://mitmproxy.org/) | [Docker Hub](https://hub.docker.com/r/mitmproxy/mitmproxy/) | [Github](https://github.com/mitmproxy/mitmproxy) + +自定义脚本为Python + +> 简评:过滤和搜索功能强大且支持重放但是用久了占用内存大,因为抓包的数据都在内存里 + +- docker 启动 `docker run --name mitmproxy -d -p 8888:8080 -p 8081:8081 mitmproxy/mitmproxy mitmweb --web-host 0.0.0.0` + - 5.0 版本之前 使用 `--web-iface 0.0.0.0` + +- **配置证书** 访问 [mitm.it](http://mitm.it) 选择对应的平台即可 + - 实际上是安装了 mitmproxy-ca-cert.pem 文件 进而信任了 mitmproxy 这个CA + +> [gomitproxy](https://github.com/zboya/gomitmproxy) `Go 实现` + +### tinyproxy +[Github](https://github.com/tinyproxy) + +### Mars +> [Github](https://github.com/ouqiang/mars) + +`虽然有使用LevelDB做存储,但是前端目前有BUG不能持久记录` + +### camilla +> [Offcial Site](https://www.camillaproxy.com/docs/) + +> [docker-compose](https://gitee.com/gin9/DockerfileList/tree/master/docker-compose/camilla) + +功能简单只能查看抓包的数据,数据只缓存浏览器,刷新就会消失,但是占用内存小 + +### dev-proxy +> [Github: dev-proxy](https://github.com/Kuangcp/GoBase/tree/master/toolbox/dev-proxy)`个人开发, 功能有: 代理转发, 抓包, 重放, 统计QPS` + +其实 xswitch 会更简单易用,但是不兼容firefox,即便使用debug方式安装上插件也会有报错和API不兼容. 而且只是浏览器级别的代理转发,不支持系统级 + +### ProxyPin +[ProxyPin](https://github.com/wanghongenpin/network_proxy_flutter) + +### eCapture +> [ecapture](https://github.com/gojue/ecapture) + +一款无需 CA 证书即可抓取 HTTPS 明文的工具。该项目基于 eBPF 技术实现了 TLS 加密的明文捕获 + +************************ + +## Wireshark +> [Official Site](https://www.wireshark.org/) + +> 问题 `Error during loading:[string "/usr/wireshark/init.lua"]:44:` +- 这是由于Wireshark为了防止以root用户身份执行Lua脚本,避免对系统造成损坏,而显示警告弹窗。通常,用户只需要确认 +- 如果不想每次都看到 修改 `/usr/wireshark/init.lua` 第一行(非注释,有效代码) 改成 `disable_lua = true` + +> TCP HTTP 抓包可选中右击 Follow 查看 TCP和HTTP流完整字符 + +************************ + +# Tips +## 移动通信技术规格 +> 1g 2g 2.5g 2.75g 3g 4g 5g + +> [参考: 1G, 2G, 3G, 4G, & 5G Explained ](https://www.lifewire.com/1g-vs-2g-vs-2-5g-vs-3g-vs-4g-578681) +> [参考: Difference Between 1G, 2G, 3G vs. 4G and 5G](Difference Between 1G, 2G, 3G vs. 4G and 5G) + +## 网络延迟 +> [如何彻底解决「网络延迟」这个问题?](https://www.zhihu.com/question/34689035) + +- [MOBA类游戏是如何解决网络延迟同步的?](https://www.zhihu.com/question/36258781) +- [状态同步与帧同步](http://www.cnblogs.com/sevenyuan/p/5283265.html) diff --git a/Skills/Network/Readme.md b/Skills/Network/Readme.md new file mode 100644 index 0000000..19b1aed --- /dev/null +++ b/Skills/Network/Readme.md @@ -0,0 +1,3 @@ +# 计算机网络 + +ECDH diff --git a/Skills/Network/Security.md b/Skills/Network/Security.md new file mode 100644 index 0000000..eee84a6 --- /dev/null +++ b/Skills/Network/Security.md @@ -0,0 +1,41 @@ +--- +title: 网络安全 +date: 2023-08-01 13:42:57 +tags: +categories: +--- + +💠 + +- 1. [网络安全](#网络安全) + - 1.1. [发现](#发现) + - 1.1.1. [网络](#网络) + - 1.1.1.1. [fping](#fping) + - 1.2. [攻击](#攻击) + - 1.2.1. [tcp syn flood](#tcp-syn-flood) + - 1.2.2. [上传文件](#上传文件) + +💠 2024-09-06 11:36:43 +**************************************** + +# 网络安全 +## 发现 +### 网络 +#### fping + +`fping -a -g 192.168.0.1/24` +`nmap -sP 192.168.0.1/24` + +************************ + +## 攻击 + +### tcp syn flood +调整内核参数,加快tcp连接回收 + +sysctl -w net.ipv4.tcp_fin_timeout=30 # 默认60s +sysctl -w net.ipv4.tcp_tw_reuse=1 + +### 上传文件 +> [upload-labs](https://github.com/c0ny1/upload-labs)`使用php语言编写的,专门收集渗透测试和CTF中遇到的各种上传漏洞的靶场` + diff --git a/Skills/Network/WebSecurity.md b/Skills/Network/WebSecurity.md new file mode 100644 index 0000000..d95e221 --- /dev/null +++ b/Skills/Network/WebSecurity.md @@ -0,0 +1,223 @@ +--- +title: 网络安全 +date: 2018-11-21 10:56:52 +tags: + - 网络 +categories: + - 计算机基础 +--- + +💠 + +- 1. [Web应用网络安全](#web应用网络安全) + - 1.1. [中间人攻击](#中间人攻击) + - 1.2. [运营商劫持](#运营商劫持) +- 2. [Web安全](#web安全) + - 2.1. [Authenticate](#authenticate) + - 2.1.1. [OAuth 2.0](#oauth-20) + - 2.1.2. [JWT](#jwt) + - 2.1.2.1. [修改密码](#修改密码) + - 2.1.2.2. [退出登录](#退出登录) + - 2.2. [Verfication](#verfication) + - 2.3. [盗链与防盗链](#盗链与防盗链) + - 2.4. [工具或平台](#工具或平台) +- 3. [攻击手段](#攻击手段) + - 3.1. [ARP断网攻击](#arp断网攻击) + - 3.2. [DOS](#dos) + - 3.2.1. [CC challenge collapsar attack](#cc-challenge-collapsar-attack) + - 3.2.2. [SYNFlood攻击](#synflood攻击) + - 3.3. [ClickJacking](#clickjacking) + - 3.4. [CSRF](#csrf) + - 3.4.1. [解决方案](#解决方案) + - 3.5. [XSS](#xss) + +💠 2024-06-17 19:57:32 +**************************************** + +# Web应用网络安全 +> [PHP安全新闻早8点](https://github.com/Micropoor/Micro8) + +## 中间人攻击 + +> [Man in the middle](/Skills/Network/MITM.md) + +## 运营商劫持 + +> 整站使用HTTPS,即可避免 + +> [参考: 运营商劫持(DNS/HTTP302)](https://blog.csdn.net/yangyangblog/article/details/80554159) +> [参考: 运营商劫持](https://www.cnblogs.com/xywq/p/7207843.html) + +************************ + +# Web安全 + +> 关注常见的比如 XSS CSRF SQL注入 上传等问题的原理和修复方案。还有密码安全也基本上是面试必考点。 +> 作为开发人员,需要详细了解安全问题的原理。 比如XSS的原理是因为用户将它的数据变成了JS代码,在页面中跑起来了,所以就可以为所欲为(例如A用户发布了一个包含恶意js的博客,只要该博客被其他用户打开浏览,就会在对应用户浏览器端执行)。 +> CSRF则是当用户不知情时,被黑客的网页通过图片、表单等请求时,用户的登录态(Cookies)在不知情的情况下会被发送到服务器,导致用户在不知情的情况下被利用身份。 +> 点击劫持则是网页被嵌入到了其他网站中,并通过视觉隐藏的方式引导用户进行一些不知情的操作。 +> 上传导致的漏洞是因为用户的文件没有做好判断和处理,导致传上来的文件被当成程序执行了。 +> SQL注入是用户的数据被当成了表示SQL语义的部分,改变了原来的查询语句的语义,从而产生意料之外的结果。 +> 反向代理服务器,构建在web服务器与 客户端之间,保护web服务器,服务端发送到客户端的请求被代理 + +## Authenticate +> [WWW-Authenticate](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) + +> [参考: 设计安全的账号系统的正确姿势](https://blog.coderzh.com/2016/01/03/security-design/) +> [参考: 即使被拖库,也可以保证密码不泄露](https://blog.coderzh.com/2016/01/10/a-password-security-design-example/) + +### OAuth 2.0 +- [OAuth 2.0授权框架](https://github.com/jeansfish/RFC6749.zh-cn/blob/master/index.md) + +> [参考: 理解OAuth 2.0](http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html) + +************************ + +### JWT +> [jwt](https://jwt.io) `JSON WEB TOKEN` + +> [理解JWT的使用场景和优劣](http://www.qingpingshan.com/rjbc/java/384762.html) + +- [Blog:通过使用JWT来防御CSRF](https://segmentfault.com/a/1190000003716037) +- [Blog:介绍JWT](blog.leapoahead.com/2015/09/06/understanding-jwt/) +- [Blog:单点登录](http://blog.leapoahead.com/2015/09/07/user-authentication-with-jwt/) +- [Web 安全之 XSS、CSRF 和 JWT](https://juejin.im/entry/58e67673a22b9d00588e7148) + +> [参考: 开箱即用 - jwt 无状态分布式授权](http://www.cnblogs.com/grissom007/p/6294746.html) + +> 需要注意的是,不是什么数据都适合放在 Cookie、localStorage 和 sessionStorage 中的。使用它们的时候,需要时刻注意是否有代码存在 XSS 注入的风险。 +> 因为只要打开控制台,你就随意修改它们的值,也就是说如果你的网站中有 XSS 的风险,它们就能对你的 localStorage 肆意妄为。所以千万不要用它们存储你系统中的敏感数据 + +- [jwt token 过期刷新](https://blog.csdn.net/weixin_39581652/article/details/110801338) + +#### 修改密码 +- Token中存入: userId 密码的部分Hash值 + - 弊端:Token变成了有状态的,需要每次请求都要去数据库比对 + +#### 退出登录 +由于JWT的设计是无变化状态的,所以理论上退出登录后,Token仍是有效可用的,此时只能由服务端再加一层逻辑来实现Token失效 +- 白名单 登录存入,退出时删除, 类似于Session +- 黑名单 存入需要失效的Token,但是理论上会占用更大空间(只能移除黑名单中过期的Token,如果Token有效期都很长,会缓存大量的Token) + +************************ + +> token vs session +1. [JWTs vs. sessions: which authentication approach is right for you?](https://stytch.com/blog/jwts-vs-sessions-which-is-right-for-you/) +1. TODO CSRF 问题? 浏览器跨源访问 读写策略 引起 cookie泄漏的问题,假如使用token,安装了恶意插件,一样获取到token,如何避免? + +************************ + +## Verfication +> 最常见和简单的就是 数字验证码, 通常能在一些公共服务的API中发现校验码的存在 + +**`CAPTCHA`** + +> CAPTCHA 全称 “全自动区分计算机和人类的图灵测试”(Completely Automated Public Turing Test to Tell Computers and Humans Apart) +> 它是一种区分用户是计算机还是人的计算程序,这种程序生成人类能很容易通过但计算机通不过的测试,并进行判定,人/机进行测试的过程称为一次“挑战”。 + +************************ + +## 盗链与防盗链 +> [referer 教程](https://www.ruanyifeng.com/blog/2019/06/http-referer.html) + +> [防盗链](https://www.jianshu.com/p/c02064db8b5b) + +1. 利用 referer 来控制 + 1. 实现简单,绕过也简单 +2. 设想 中间使用一个认证中间件(请求静态文件需要携带token,token需要js方式计算获取,且有有效期),提高盗链难度 + +************************ + +## 工具或平台 +> [Official Site](https://www.zaproxy.org/) + +************************ + +# 攻击手段 + +## ARP断网攻击 + +> [ARP 断网攻击的原理是什么?如何完全防护?](https://www.zhihu.com/question/20338649) + +************************ + +## DOS + +- [Denial-of-service_attack](https://en.wikipedia.org/wiki/Denial-of-service_attack) +> [DDoS attack using HOIC](https://github.com/Samsar4/Ethical-Hacking-Labs/blob/master/9-Denial-of-Service/2-DDoS-using-HOIC.md) + +### CC challenge collapsar attack + +CC攻击是攻击者借助代理服务器生成指向受害主机的合法请求,实现DDoS和伪装攻击。攻击者通过控制某些主机不停地发送大量数据包给对方服务器,造成服务器资源耗尽,直至宕机崩溃。 + +例如对站点的部分接口或页面发起大量客户端线程访问。 + +### SYNFlood攻击 +> 洪水攻击 [参考博客](http://xfocus.net/articles/200106/208.html) SYN Flood是当前最流行的DoS(拒绝服务攻击)与DDoS(分布式拒绝服务攻击)的方式之一,这是一种利用TCP协议缺陷,发送大量伪造的TCP连接请求,从而使得被攻击方资源耗尽(CPU满负荷或内存不足)的攻击方式。 + +> [参考博客 什么是SYN Flood攻击?](http://www.cnblogs.com/popduke/p/5823801.html) +> [SYN Flooding](https://github.com/Samsar4/Ethical-Hacking-Labs/blob/master/9-Denial-of-Service/1-SYN-Flooding.md) + +- hping: Sync攻击 `hping -S -p 80 --flood 192.168.1.234` +- 修改文件 `sudo vim /etc/sysctl.conf ` + - 将注释取消 修改值: `net.ipv4.tcp_syncookies = 0` 就能提高并发总量,但是并发量还是不能提高 + +```conf + net.ipv4.tcp_syncookies = 0 + #此参数是为了防止洪水攻击的,但对于大并发系统,要禁用此设置 + net.ipv4.tcp_max_syn_backlog=1024 + #参数决定了SYN_RECV状态队列的数量,一般默认值为512或者1024,即超过这个数量,系统将不再接受新的TCP连接请求,一定程度上可以防止系统资源耗尽。可根据情况增加该值以接受更多的连接请求。 + net.ipv4.tcp_tw_recycle=0 + #参数决定是否加速TIME_WAIT的sockets的回收,默认为0。 + net.ipv4.tcp_tw_reuse=0 + #参数决定是否可将TIME_WAIT状态的sockets用于新的TCP连接,默认为0。 + net.ipv4.tcp_max_tw_buckets + #参数决定TIME_WAIT状态的sockets总数量,可根据连接数和系统资源需要进行设置。 +``` + +************************ + +## ClickJacking + +点击劫持是一种视觉上的欺骗手段。攻击者使用一个透明的iframe,覆盖在一个网页上,然后诱使用户在网页上进行操作,此时用户将在不知情的情况下点击透明的iframe页面。通过调整iframe页面的位置,可以诱使用户恰好点击在iframe页面的一些功能性按钮上。 + +> [X-Frame-Options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) + +1. DENY:不能被嵌入到任何iframe或者frame中。 +2. SAMEORIGIN:页面只能被本站页面嵌入到iframe或者frame中 +3. ALLOW-FROM uri:只能被嵌入到指定域名的框架中 + +## CSRF + +> CSRF (Cross Site Request Forgery) `跨站请求伪造` + +指在一个浏览器中打开了两个标签页,其中一个页面通过窃取另一个页面的 cookie 来发送伪造的请求 + +例如: A站点某网页 a.html,被插入了指向B站点的URL的image标签利用 cookie 会随着当前页面的请求自动放入请求 Header 发送到服务端的特性,A站点的cookie会发送至B站点 + +> [维基百科定义 CSRF](https://www.owasp.org/index.php/Cross-Site_Request_Forgery) | +> [百度百科 CSRF](https://baike.baidu.com/item/CSRF) + +> [[Web 安全] 如何通过JWT防御CSRF](https://segmentfault.com/a/1190000003716037) +> [web安全之token和CSRF攻击](https://blog.csdn.net/qq_15096707/article/details/51307024) +> [博客:CSRF漏洞的原理](https://www.zhuyingda.com/blog/b5.html) +> [浅谈CSRF攻击方式](http://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html) +> [参考提问下的回答](https://segmentfault.com/q/1010000000713614) + +### 解决方案 + +> token + +- 打开当前页面时服务端先传递一个token给前端,前端后续的请求都需要携带该token(作请求参数或者Header),否则拒绝请求,这样能避免img标签方式的 CSRF + +> Cookie 中的 SameSite属性 [Cookie详情](/Skills/Network/HTTP.md#Cookie) + +************************ + +## XSS + +> Cross Site Scripting `跨站脚本攻击` + +> [xss攻击入门](http://www.cnblogs.com/bangerlee/archive/2013/04/06/3002142.html) +> [ XSS攻击及防御 ](https://blog.csdn.net/ghsau/article/details/17027893) +> [最新的黑客技术:详解XSS跨站脚本攻击 ](http://soft.yesky.com/security/hkjj/136/2233136.shtml) diff --git a/Skills/Network/img/001-network-base.km.svg b/Skills/Network/img/001-network-base.km.svg new file mode 100644 index 0000000..e805283 --- /dev/null +++ b/Skills/Network/img/001-network-base.km.svg @@ -0,0 +1 @@ +NetworkOSI应用层表示层数据格式转换,数据加密会话层建立,管理,维护回话传输层建立,管理,维护 端到端的连接网络层IP选址 路由选择数据链路层提供介质访问和链路管理物理层物理设备 光纤 同轴电缆 集线器 等TCP/IP应用层应用程序HTTP, Telnet,FTP,TFTP,DNS,SMTP运输层四层交换机,四层路由器TCP, UDP,KCP网际层路由器, 三层交换机IP, ICMP, RIP, IGMP网络接口层数据链路层网桥,以太网交换机,网卡ARP, RARP, IEEE802.3, PPP, CSMA/CD物理层中继器, 集线器,双绞线FE自协商 Manchester, MLT-5,4A,PAM5代理正向代理反向代理透明代理隐藏 客户端 地址, 去访问地址明确的服务端 \ No newline at end of file diff --git a/Skills/OpenCV/Readme.md b/Skills/OpenCV/Readme.md new file mode 100644 index 0000000..bfd7145 --- /dev/null +++ b/Skills/OpenCV/Readme.md @@ -0,0 +1,52 @@ +# OpenCV +> 运作模式就是大量正负相关的样本训练精确度更好的分类器(xml文件 CascadeClassifier),再以此编写逻辑 + +## 安装试运行 图片浏览器 +> [arch安装opencv](https://my.oschina.net/u/4125051/blog/3071866) + +1. sudo pacman -S opencv vtk hdf5 cmake + - `CMakeLists.txt` + ```c + cmake_minimum_required(VERSION 2.10) + project(test) + find_package(OpenCV REQUIRED) + add_executable(test main.cpp) + target_link_libraries(test ${OpenCV_LIBS}) + ``` + ```cpp + #include + #include + #include + #include + #include + using namespace cv; + using namespace std; + int main() + { + string filename = "/path/to/pic.jpg"; + Mat img = imread(filename); + auto size = img.size(); + int w = size.width; + int h = size.height; + Mat re_image; + resize(img,re_image,Size(w*2, h*2)); + while(true) + { + imshow(filename,re_image); + int k = waitKey(0); + if(k == 27){ + break; + } + } + destroyAllWindows(); + } + ``` +1. cmake . +1. make + +************************ + +## gocv +> [gocv linux](https://gocv.io/getting-started/linux/) + +> [facedetect demo](https://github.com/hybridgroup/gocv/blob/master/cmd/facedetect/main.go) diff --git a/Skills/Problem.md b/Skills/Problem.md index f753d9e..b12bfc9 100644 --- a/Skills/Problem.md +++ b/Skills/Problem.md @@ -1,78 +1,40 @@ -`目录 start` - -- [问题解决方案](#问题解决方案) - - [终端](#终端) - - [JDK](#jdk) - - [IDE](#ide) - - [IDEA](#idea) - - [Docker](#docker) - - [内存高占用](#内存高占用) - - [Linux](#linux) - - [Deepin](#deepin) - - [输入法](#输入法) - - [fcitx](#fcitx) - - [Flash](#flash) +--- +title: 问题及解决方案 +date: 2018-11-21 10:56:52 +tags: +categories: + - WIKI +--- -`目录 end` |_2018-08-21_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** +💠 + +- 1. [问题及解决方案](#问题及解决方案) + - 1.1. [Linux](#linux) + - 1.2. [JDK](#jdk) -# 问题解决方案 -## 终端 +💠 2024-03-19 10:33:48 +**************************************** +# 问题及解决方案 +## Linux +> [详细](/Linux/Base/LinuxProblem.md) ## JDK > `Picked up _JAVA_OPTIONS: ` 例如这样的提示, 由于设置了 _JAVA_OPTIONS 或者 JAVA_OPTIONS 就一定会输出这个提示 -- [参考博客: Disabling Java_Options On Java Console Apps in Linux](https://nixmash.com/post/disabling-java_options-on-java-console-apps-in-linux) -- [参考博客: Suppressing the “Picked up _JAVA_OPTIONS” message](https://superuser.com/questions/585695/suppressing-the-picked-up-java-options-message) -- [参考博客: 理解环境变量 JAVA_TOOL_OPTIONS](https://segmentfault.com/a/1190000008545160) +- [参考: Disabling Java_Options On Java Console Apps in Linux](https://nixmash.com/post/disabling-java_options-on-java-console-apps-in-linux) +- [参考: Suppressing the “Picked up _JAVA_OPTIONS” message](https://superuser.com/questions/585695/suppressing-the-picked-up-java-options-message) +- [参考: 理解环境变量 JAVA_TOOL_OPTIONS](https://segmentfault.com/a/1190000008545160) -但是又不能直接 unset 这个变量似乎是用来解决字体锯齿问题的, 所以需要如下配置 +但是又不能直接 unset, 这个变量似乎是用来解决字体锯齿问题的, 所以需要如下配置 ```sh -_SILENT_JAVA_OPTIONS="$_JAVA_OPTIONS" -unset _JAVA_OPTIONS -alias java='java "$_SILENT_JAVA_OPTIONS"' + _SILENT_JAVA_OPTIONS="$_JAVA_OPTIONS" + unset _JAVA_OPTIONS + alias java='java "$_SILENT_JAVA_OPTIONS"' ``` -只需将该配置加到 `/etc/profile` 文件尾部, 这样的话, 终端不会有如上提示, 但是IDEA中输出控制台仍带有该提示, 这时候可以在IDEA的启动脚本 `bin/idea.sh` 中也添加如上配置即可(在最后一段之前) +- 只需将该配置加到 `/etc/profile` 文件尾部, 这样的话, 终端不会有如上提示 +- 但是IDEA中输出控制台仍带有该提示, 在 `bin/idea.sh` 中也添加如上配置即可(在最后一段启动命令之前) ******************************* > Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=gasp - 原因是linux自带的OpenJDK影响了安装的java, 同样的也是可以采用如上的方法, 或者: - `sudo mv /etc/profile.d/java-awt-font-gasp.sh /etc/profile.d/java-awt-font-gasp.sh.bak` - -************************* -## IDE -### IDEA -- [调整参数,解决CPU满载](https://intellij-support.jetbrains.com/hc/en-us/articles/206544869) - - [同样的](https://intellij-support.jetbrains.com/hc/en-us/articles/207241235) - - -## Docker -### 内存高占用 -- 明明是一样的docker配置,构建出来的镜像按道理也应该是一样的,所以运行出来的容器也应该是一样的才对,但是结果却是两倍的差别 - - 优化jvm? -- 修改基础镜像? - -## Linux -### Deepin - -- [FAQ](https://bbs.deepin.org/forum.php?mod=viewthread&tid=146921&extra=page%3D1) - - -#### 输入法 -##### fcitx -- fcitx单核满载:三种(搜狗拼音导致) - - 杀掉,fcitx -r - - 先把进程杀掉再fcitx-autostart & - - fcitx再fcitx-qimpanel -`相关网页:` -- [某引擎搜索结果页](https://ausdn.com/s/ubuntu+cpu+fcitx)| [几种方式](https://www.findhao.net/res/786)| [卸载搜狗安装拼音](http://tieba.baidu.com/p/3863217434) -- [知乎问题](https://www.zhihu.com/question/19839748) | [ubuntu论坛](http://forum.ubuntu.com.cn/viewtopic.php?f=122&t=173730&p=1299087) | [ubuntu论坛](http://forum.ubuntu.com.cn/viewtopic.php?f=8&t=194486&start=0) - -- 输入法没有显示打字窗口 - - 直接杀掉 sogou-qimpanel 然后点击图标进行启动 - -#### Flash -- 点击[官网下载地址](https://get.adobe.com/cn/flashplayer/)下载,然后解压, -- 将文件复制进火狐插件目录:`sudo cp libflashplayer.so /usr/lib64/mozilla/plugins` -- 添加其他用户可执行权限`chmod 755 /usr/lib64/mozilla/plugins/libflashplayer.so` - diff --git a/Skills/Product/User.md b/Skills/Product/User.md new file mode 100644 index 0000000..88e0f26 --- /dev/null +++ b/Skills/Product/User.md @@ -0,0 +1,51 @@ +--- +title: User +date: 2024-04-25 22:11:30 +tags: +categories: +--- + +💠 + +- 1. [项目POC](#项目poc) +- 2. [用户](#用户) + - 2.1. [活跃度](#活跃度) + - 2.2. [行为日志分析](#行为日志分析) + - 2.3. [推荐](#推荐) + +💠 2024-04-25 22:16:44 +**************************************** +# 项目POC +> PoC(Proof of Concept),即概念验证。 + +> [PoC对于创新意味着什么?](https://www.woshipm.com/operate/4243656.html) + +# 用户 +> 产品设计中关于用户维度的考虑 + +## 活跃度 +> 网页端 +- v 访问次数 +- pv 页面浏览量 +- 页面停留时长 +- uv 访客数 +- 网站停留时长 +- 跳出率 +- 退出率 +- 转化率 + +> 移动端 +用户获取 +用户留存 +用户转化 + +> 收入 +- 激活率 +- 付费转换率 +- ROI 投资回报率 + +## 行为日志分析 + +## 推荐 +推荐系统一种实现方式: 每个实际用户在系统内的行为依据抽象的规则得到不同的标签,每个标签都可以看作是一个向量,找系统内两个向量距离最近的一些用户,互相推荐他们近期看过,做过的事情。 + diff --git a/Skills/ProgrammingParadigm.md b/Skills/ProgrammingParadigm.md new file mode 100644 index 0000000..73a83bb --- /dev/null +++ b/Skills/ProgrammingParadigm.md @@ -0,0 +1,50 @@ +--- +title: 编程范式 ProgrammingParadigm +date: 2019-10-19 17:04:34 +tags: +categories: +--- + +**目录 start** + +1. [编程范式](#编程范式) + 1. [命令式编程](#命令式编程) + 1. [函数式编程](#函数式编程) + 1. [响应式编程](#响应式编程) + 1. [逻辑式编程](#逻辑式编程) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# 编程范式 +- Imperative Programming (IP) 命令式编程 +- Functional Programming (FP) 函数式编程 +- Logic Programming (LP) 逻辑式编程 + +## 命令式编程 +> 编写改变状态的一条条命令 + +## 函数式编程 + +- 函数式语言他把计算本身当成最重要的概念。函数式语言和过程式语言一样对值进行操作,但他不会修改输入,而是像数学函数一样返回新值 + - 函数被看成是一个小处理机,输入值并输出值,他们没有自己的状态,并且将他们和任何外部状态绑定在一起也没有意义 + +> Groovy带一点函数式风格,Scala对FP的利用更为充分,Clojure是纯粹的函数式语言,没有丁点儿面向对象特性 + +**`命令式和函数式`** +- Java是典型的命令式语言,命令式语言把程序的运行状态建模为可修改的数据,用一系列的指令来改变状态。因此在命令式语言中,程序状态是核心概念 +- 命令式语言主要分为两类,一种是面向过程语言,一种是面向对象语言 + - 面向过程:Basic Fortran 这种语言将代码和数据完全分离开,有简单的代码操作数据范式 + - 面向对象:数据和代码(方法形式)封装在对象中,面向对象语言中或多或少会存在元数据(比如:类信息)引入的额外结构 + +## 响应式编程 +> [ReactiveX](http://reactivex.io/intro.html) + +组合异步的序列 设计模式是 观察者模式的扩展, 数据结构是序列串流, 避免了并发, 是非阻塞的,常见于UI层 + +数据流驱动 + +异步 非阻塞 不是同步非阻塞 (当时不阻塞后续回调) 而是异步 + +多路复用 + +## 逻辑式编程 diff --git a/Skills/README.md b/Skills/README.md index 438d772..cbabbb1 100644 --- a/Skills/README.md +++ b/Skills/README.md @@ -1,5 +1 @@ -# 技能 -> 一些比较杂乱的东西会放在这里,前端也放这里了 - -- 前端准备学习 Angular Vue - +# 基础技能 diff --git a/Skills/RegularExpression.md b/Skills/RegularExpression.md index 8074f78..dc45b4a 100644 --- a/Skills/RegularExpression.md +++ b/Skills/RegularExpression.md @@ -1,49 +1,202 @@ -`目录 start` - -- [正则表达式学习](#正则表达式学习) - - [基本字符](#基本字符) - - [特殊字符](#特殊字符) - - [Python](#python) - - [Shell](#shell) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 正则表达式 +date: 2018-11-21 10:56:52 +tags: +categories: + - 基础知识 +--- + +💠 + +- 1. [正则表达式](#正则表达式) + - 1.1. [基本字符](#基本字符) + - 1.2. [特殊字符](#特殊字符) + - 1.3. [分组捕获](#分组捕获) + - 1.4. [变量](#变量) + - 1.5. [零宽断言](#零宽断言) +- 2. [正则表达式引擎](#正则表达式引擎) + - 2.1. [NFA匹配模式](#nfa匹配模式) + - 2.2. [性能陷阱](#性能陷阱) +- 3. [语言实现](#语言实现) + - 3.1. [Java](#java) + - 3.2. [Python](#python) + - 3.3. [Shell](#shell) +- 4. [Tips](#tips) + +💠 2024-09-20 11:10:09 **************************************** -# 正则表达式学习 +# 正则表达式 +> [Regular Expression Language - Quick Reference](https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference) + +> [Grex](https://github.com/pemistahl/grex) +> [Regex-Vis](https://github.com/Bowen7/regex-vis) ## 基本字符 -- `^` 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。 -- `$` 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。 -- `( )` 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用:`([0-9]{3})?` 匹配出现三个数字连着的字符串 -- `{N}` 匹配前面出现的子表达式 N 次 -- `{N,M}` 匹配前面的子表达式 N-M 次 -- `[...]` 匹配字符集的任一单个字符 : `[abcdejk]`就是匹配里面任一单个字符 -- `[^...]` 不匹配此集合中任一字符,包括某一范围的字符:`[^0-9]` 字符串中不含数字 -- `[x-y]` 匹配x-y范围内单一字符 : `[0-9] [a-z]` -- `*` 匹配前面的子表达式 0或多次。 -- `+` 匹配前面的子表达式 1或多次。 -- `?` 匹配前面的子表达式 0或1次,或指明一个非贪婪限定符。 -- `.` 匹配除换行符 `\n`之外的任何 单个字符。 -- `\` 将下一个字符标记为或转义字符、或原义字符、或向后引用、或八进制转义符。'\n' 匹配换行符。 -- `|` 指明两项之间的一个选择。 - -`以上字符 要想表示原字符都需转义` +| 模式 | 作用 | +|:----|:----| +| `^` | 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。 +| `$` | 匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。 +| `( )` | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用:`([0-9]{3})?` 匹配出现三个数字连着的字符串 +| `{N}` | 匹配前面出现的子表达式 N 次 +| `{N,M}` | 匹配前面的子表达式 N-M 次 +| `[...]` | 匹配字符集的任一单个字符 : `[abcdejk]`就是匹配里面任一单个字符 +| `[^...]`| 不匹配此集合中任一字符,包括某一范围的字符:`[^0-9]` 字符串中不含数字 +| `[x-y]` | 匹配x-y范围内单一字符 : `[0-9] [a-z]` +| `*` | 匹配前面的子表达式 0或多次。 +| `+` | 匹配前面的子表达式 1或多次。 +| `?` | 匹配前面的子表达式 0或1次,或指明一个非贪婪限定符。 +| `.` | 匹配除换行符 `\r` `\n` 之外的任何 单个字符。 +| `\` | 将下一个字符标记为或转义字符、或原义字符、或向后引用、或八进制转义符。'\n' 匹配换行符。 +| `|` | 指明两项之间的一个选择。 + +`以上字符需要匹配字面值时都需转义` + +************************ ## 特殊字符 -- `\d` 匹配任何十进制数字 与`[0-9]`相同 `\D`与之相反 -- `\w` 匹配任何字母数字字符 与 `[a-zA-Z0-9_]` 相同, `\W`与之相反 -- `\s` 匹配任何空格字符 与 `[\n\t\r\v\f]` 相同 `\S`与之相反 -- `\b` 匹配任何单词的边界 `\B`与之相反 -- `\N` 匹配已保存的子组N次 : `name\100` -- `\c` 逐字匹配任何特殊字符`c` ,就是转义字符的使用! -- `\A \Z` 匹配字符串的开始或结束 +| 模式 | 作用 | +|:----|:----| +| `\d` | 匹配任何十进制数字 与 `[0-9]` 相同 `\D`与之相反 | +| `\w` | 匹配任何字母数字字符 与 `[a-zA-Z0-9_]` 相同, `\W`与之相反 | +| `\s` | 匹配任何空字符(空格 tab 回车) 与 `[\n\t\r\v\f]` 相同,`\S`与之相反 | +| `\b` | 匹配任何单词的边界 `\B`与之相反 | +| `\N` | 匹配已保存的子组N次 `name\100` | +| `\c` | 逐字匹配任何特殊字符 `c` 就是转义字符的使用 | +| `\A \Z` | 匹配字符串的开始或结束 | > 构造正则表达式的方法和创建数学表达式的方法一样。也就是用多种元字符与操作符将小的表达式结合在一起来创建更大的表达式。 > 正则表达式的组件可以是单个的字符、字符集合、字符范围、字符间的选择或者所有这些组件的任意组合。 +************************ + +> Pattern +- (?i) 开启忽略大小写 (?-i) 关闭忽略大小写 + - 但是i只处理 ASCII 字符,如果需要处理Unicode字符需要加上u或U 例如 `(?iu)`, `(?iU)` + +``` +i - Ignore case +m - Treat a newline as a character matched by . +x - Ignore whitespace and comments in the pattern +o -> Perform #{} interpolation only once +``` + +************************ +## 分组捕获 +> [Grouping Constructs](https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-language-quick-reference#grouping-constructs) + +正则中的()都是一个分组捕获,库通常使用下标来定位匹配到的分组 + +同时可以使用命名分组 Named groups。 + +```java + Pattern idxGroup = Pattern.compile("(\\d{4})-(\\d{2})"); + Matcher matcher = idxGroup.matcher("2012-12"); + System.out.println(matcher.matches()); + assertThat(matcher.group(1), equalTo("2012")); + assertThat(matcher.group(2), equalTo("12")); + + Pattern nameGroup = Pattern.compile("(?\\d{4})-(?\\d{2})"); + matcher = nameGroup.matcher("2012-12"); + System.out.println(matcher.matches()); + assertThat(matcher.group("year"), equalTo("2012")); + assertThat(matcher.group("month"), equalTo("12")); +``` + +## 变量 + +常用于正则替换 +1. `(\w*)_(\w*)_(\w*)` => `\l$1\u$2\u$3` + - user_name_flag => userNameFlag +1. `(.*)/\d+\.\d+\.\d+\.\d+(.*)` => `$1/1.1.1.1/$2` + - http://1.2.3.4/path/url => http://1.1.1.1/path/url + +## 零宽断言 +> 用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像 \b, ^, $, <, >, 那样用于指定一个位置 +> 这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言 + +> [参考: 零宽断言](https://www.cnblogs.com/shangdawei/p/4673117.html) +> [正则表达式的先行断言(lookahead)和后行断言(lookbehind)](https://www.runoob.com/w3cnote/reg-lookahead-lookbehind.html) + +- `(?=exp)` 零宽正向先行断言(zero-width positive lookahead assertion) +- `(?!exp)` 零宽负向先行断言(zero-width negative lookahead assertion) +- `(?<=exp)` 零宽正向后行断言(zero-width positive lookbehind assertion) +- `(? eg: +- `(?<=a).*(?=b)` 提取ab字符之间的值 + +*************************** + +# 正则表达式引擎 +正则表达式是一个用正则符号写出的公式,程序对这个公式进行语法分析,建立一个语法分析树,再根据这个分析树结合正则表达式的引擎生成执行程序(这个执行程序我们把它称作状态机,也叫状态自动机),用于字符匹配。 + +而这里的正则表达式引擎就是一套核心算法,用于建立状态机。 + +目前实现正则表达式引擎的方式有两种:DFA [确定有限状态自动机](https://en.wikipedia.org/wiki/Deterministic_finite_automaton) 和 NFA [非确定有限状态自动机](https://en.wikipedia.org/wiki/Nondeterministic_finite_automaton)。 + +对比来看,构造 DFA 自动机的代价远大于 NFA 自动机,但 DFA 自动机的执行效率高于 NFA 自动机。 + +假设一个字符串的长度是 n,如果用 DFA 自动机作为正则表达式引擎,则匹配的时间复杂度为 O(n);如果用 NFA 自动机作为正则表达式引擎,由于 NFA 自动机在匹配过程中存在大量的分支和回溯,假设 NFA 的状态数为 s,则该匹配算法的时间复杂度为 O(ns)。 + +NFA 自动机的优势是支持更多功能。例如:捕获group、环视、占有优先量词等高级功能。这些都是基于子表达式独立进行匹配,因此在编程语言里,正则表达式库**都是基于 NFA 实现的**。 + +## NFA匹配模式 +> [what are possessive quantifiers in Java regular expression used for?](https://stackoverflow.com/questions/26006653/what-are-possessive-quantifiers-in-java-regular-expression-used-for) + +**贪婪模式(Greedy)** + +如果单独使用 +、?、*或{min,max}等量词,正则表达式会匹配尽可能多的内容。 + +例如 文本 abbc 模式 `ab{1,3}c`, NFA会先读取最大匹配范围3,匹配失败后回溯到2匹配成功,如果文本是 abbbc 就不会回溯直接匹配成功了 +当有多个匹配组时,性能下降会更明显 例如 `^([A-Za-z0-9]+)([A-Z0-9][A-Z0-9])(.*)` 可以看到有三个匹配分组第一个可能将匹配失败的字符给到第二个第三个组匹配,分支数会大量膨胀 + +**懒惰模式(Reluctant)** + +尽可能少地重复匹配字符,如果匹配成功,它会继续匹配剩余的字符串。 + +例如 文本 abbc 模式 `ab{1,3}?c`, NFA自动机会先读取最小匹配范围1再继续匹配,*避免了回溯问题*。 +当多匹配组时 改进前文中表达式 `^([A-Za-z0-9]+?)([A-Z0-9][A-Z0-9])(.*)` 降低了分支数。 + +**独占模式(Possessive)** + +和贪婪模式一样,独占模式一样会最大限度地匹配更多内容;不同的是,在独占模式下,匹配失败就会结束匹配,不会发生回溯问题。多组匹配时一个组的匹配失败的字符不会给到另一个组 + +例如 文本 abbc,模式为`ab{1,3}+c`。 +- JavaScript中匹配会报错 **Invalid regular expression Nothing to repeat**。 +- Java中能匹配成功,但没有发生所谓的独占模式引发匹配失败的场景, 奇怪? +- Golang中会报错 **invalid nested repetition operator** +当多匹配组时 例如 `^([A-Z]++)([H-Zw])(.*)` + +> 注意:JavaScript,Python和Go 的标准库*不支持*独占模式 + +## 性能陷阱 +> [如何优化正则表达式性能?](https://segmentfault.com/a/1190000039941785) + +1. 少用贪婪模式 +1. 减少分支选择带来的组合情况笛卡尔积 +1. 减少捕获`()`嵌套 +1. +************************ + +# 语言实现 +## Java + +```java + Pattern pt = Pattern.compile("\\d+"); + pt.matcher("12,3"); +``` + +注意: +- 因为compile很耗CPU资源,所以Pattern对象需要尽可能复用,最好成为静态属性(它是不可变实例,是并发安全的) + +## Python +- ![re.jpg](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/python/re.jpg) + +## Shell +- [Shell正则表达式](http://man.linuxde.net/docs/shell_regex.html) -### Python -- ![re.jpg](https://raw.githubusercontent.com/Kuangcp/ImageRepos/masters/Tech/python/re.jpg) +************************ -### Shell -- [Shell正则表达式](http://man.linuxde.net/docs/shell_regex.html) \ No newline at end of file +# Tips +- 连续重复字符的匹配 `(.)\1+` [正则表达式匹配重复的字符串](http://www.aijquery.cn/Html/jqueryjiqiao/181.html) diff --git a/Skills/Search/Elasticsearch.md b/Skills/Search/Elasticsearch.md new file mode 100644 index 0000000..8358972 --- /dev/null +++ b/Skills/Search/Elasticsearch.md @@ -0,0 +1,115 @@ +--- +title: Elasticsearch +date: 2019-05-10 18:10:21 +tags: +categories: + - ELK +--- + +💠 + +- 1. [Elasticsearch](#elasticsearch) +- 2. [Install](#install) + - 2.1. [单节点](#单节点) + - 2.2. [集群](#集群) + - 2.3. [客户端](#客户端) + - 2.3.1. [Java](#java) +- 3. [Index](#index) +- 4. [Mapping](#mapping) +- 5. [DSL](#dsl) +- 6. [分词器](#分词器) +- 7. [向量搜索](#向量搜索) + +💠 2024-06-11 16:32:25 +**************************************** +# Elasticsearch +> [Official Guide](https://www.elastic.co/guide/en/elasticsearch/reference/current/getting-started.html) +> [参考: Elasticsearch 快速开始](https://www.cnblogs.com/cjsblog/p/9439331.html) + +使用场景: + +> [七个生产案例告诉你BATJ为何选择ElasticSearch!应用场景和优势!](https://segmentfault.com/a/1190000022799288) +> [Elasticsearch技术方案选型的10个注意点](https://time.geekbang.org/column/article/108196?utm_campaign=geektime_search&utm_content=geektime_search&utm_medium=geektime_search&utm_source=geektime_search&utm_term=geektime_search) + + +中文教程: + +> [一起学Elasticsearch系列](https://github.com/BookaiCode/JavaRecord?tab=readme-ov-file#lock-elasticsearch) +> [ElasticSearch知识体系详解](https://pdai.tech/md/db/nosql-es/elasticsearch.html) + +************************ + +# Install +> [Installing Elasticsearch](https://www.elastic.co/guide/en/elasticsearch/reference/current/install-elasticsearch.html) +> [Command line tools](https://www.elastic.co/guide/en/elasticsearch/reference/current/commands.html) + +************************ +## 单节点 +> [Run Elasticsearch locally](https://www.elastic.co/guide/en/elasticsearch/reference/current/run-elasticsearch-locally.html) + +```sh + # es8 + docker network create elastic + # 可追加内存设置 -e ES_JAVA_OPTS="-Xms2560m -Xmx2560m" 避免启动占用大量内存 32G内存占用了17G 用visualvm查看实际内存占用才700M + docker run --name es8 --net elastic -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -t docker.elastic.co/elasticsearch/elasticsearch:8.13.2 + # kibana + docker run --name kibana --net elastic -p 5601:5601 docker.elastic.co/kibana/kibana:8.13.2 + + # ES启动完成后会有如下输出 elastic的初始密码以及Kibana的Token + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✅ Elasticsearch security features have been automatically configured! + ✅ Authentication is enabled and cluster connections are encrypted. + + ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` +- 重新生成token bin/elasticsearch-create-enrollment-token --scope kibana +- 重置初始用户的密码 bin/elasticsearch-reset-password -u elastic + +> [参考: 用容器快速上手Elasticsearch](http://qinghua.github.io/elastic-search/) + +## 集群 +> [docker compose install cluster](https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html#docker-compose-file) + +## 客户端 +- Kibana 官方支持 +- Elasticvue 浏览器插件 + +### Java +> [Guide to Elasticsearch in Java](https://www.baeldung.com/elasticsearch-java)`使用elasticsearch包访问ES` +> [Spring Boot整合Elasticsearch](https://github.com/cloudgyb/es-spring-boot)`使用 SpringData` + + +# Index +- `PUT /{indexName}?pretty` 创建索引 +- `DELETE /{indexName}?pretty` 删除索引 `异步,不可撤销,不可逆` + +- `GET /{indexName}/_search` 搜索 +- `GET /{indexName}/_doc/doc_id` 查询指定文档id +- `GET /{indexName}/_doc/doc_id` 新增或覆盖文档 +- `POST /{indexName}/_update/doc_id` 新增或更新文档 + +- `GET _cat/indices?v` 获取所有索引信息 + +************************ + +# Mapping +> [Mapping](https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping.html) + +************************ + +# DSL +> [Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html) + +> [EQL](https://www.elastic.co/guide/en/elasticsearch/reference/current/eql.html)`Event Query Language` + +************************ + +# 分词器 + +************************ + +# 向量搜索 +版本 8.5+ + + + diff --git a/Skills/Search/ElasticsearchAdvance.md b/Skills/Search/ElasticsearchAdvance.md new file mode 100644 index 0000000..64b379e --- /dev/null +++ b/Skills/Search/ElasticsearchAdvance.md @@ -0,0 +1,33 @@ +--- +title: ElasticsearchAdvance +date: 2024-05-03 12:21:37 +tags: +categories: +--- + + +💠 + +- 1. [Elasticsearch](#elasticsearch) + - 1.1. [设计](#设计) +- 2. [最佳实践](#最佳实践) + - 2.1. [优化写入](#优化写入) + +💠 2024-05-03 12:21:37 +**************************************** +# Elasticsearch +[Elasticsearch Best Practice Architecture](https://www.elastic.co/cn/pdf/architecture-best-practices.pdf) + +> [Elasticsearch cluster load balancing best practices](https://stackoverflow.com/questions/66098115/elasticsearch-cluster-load-balancing-best-practices) + +## 设计 + + +************************ + +# 最佳实践 + +> [滴滴基于 ElasticSearch 的一站式搜索中台实践](https://www.infoq.cn/article/ug*cbrk9303MiNZPrSEO) + +## 优化写入 +> [提升 Elasticsearch 写入速度的案例分享](https://www.infoq.cn/article/t7b52mbzxqkwrrdpVqD2) diff --git a/Skills/Search/Readme.md b/Skills/Search/Readme.md new file mode 100644 index 0000000..c6ea8d8 --- /dev/null +++ b/Skills/Search/Readme.md @@ -0,0 +1,6 @@ +# 搜索 + +[从零开始构建全文搜索引擎](https://mojotv.cn/go/golang-full-text-search-enginec) + +> [typesense](https://github.com/typesense/typesense) + diff --git a/Skills/Serialization/Protobuf.md b/Skills/Serialization/Protobuf.md new file mode 100644 index 0000000..96461cd --- /dev/null +++ b/Skills/Serialization/Protobuf.md @@ -0,0 +1,161 @@ +--- +title: Protobuf +date: 2019-04-20 13:27:20 +tags: +categories: +--- + +💠 + +- 1. [Protobuf](#protobuf) + - 1.1. [proto文件定义](#proto文件定义) + - 1.2. [数据类型](#数据类型) + - 1.2.1. [Protobuf3](#protobuf3) + - 1.3. [Linux上安装Protobuf](#linux上安装protobuf) + - 1.4. [Java中的使用](#java中的使用) + - 1.5. [实现原理](#实现原理) + +💠 2024-04-21 16:35:07 +**************************************** +# Protobuf +> Google开源的序列化框架 全称 `Google Protocol Buffers` | [Github : Protobuf](https://github.com/google/protobuf) | [wikipedia](https://en.wikipedia.org/wiki/Protocol_Buffers) + +- 将数据结构用`*.proto`文件进行描述, 通过代码生成工具, 生成对应数据结构的 POJO 对象和 Protobuf 用到的方法和属性 + - 特点: + - 结构化数据存储格式,类似于 XML JSON等 + - 高效的编解码性能 + - 独立的IDL,语言无关, 平台无关, 扩展性好 + - 官方支持 Java C++ Python Objective-C C# JavaScript Ruby + - 数据描述文件和代码生成机制优点: + - 文本化的数据结构描述语言, 可以实现语言和平台无关, 特别适合异构系统间的集成 + - 通过标识字段的顺序, 可以实现协议的前向兼容 _在不同版本的数据结构进程间进行数据传递_ + - 自动代码生成, 不需要手工编写同样数据结构的C++和Java版本; + - 方便后续的管理和维护,相比于代码, 结构化的文档更容易管理和维护 +- 习惯性规则: + - 命名: `packageName.MessageName.proto` + +> protobuf 只是编解码的工具, 本身不支持处理TCP中读半包、粘包、拆包问题,只是序列化后的二进制流能更方便处理这些问题 + +> [参考: Protobuf语言指南](http://www.cnblogs.com/dkblog/archive/2012/03/27/2419010.html) `较为详细, 只是版本有点旧` +> [参考: Protobuf3语言指南](https://blog.csdn.net/u011518120/article/details/54604615) + +## proto文件定义 + +```protobuf + // 用户数据信息 + message Article { + required int32 articleId = 1; // 文章id + optional string articleExcerpt = 2; // 文章摘要 + repeated string articlePicture = 3; // 文章附图 + } +``` +> 上面定义了一个消息, 消息具有三个属性, 且行末的注释 经 protoc 编译后都会变成Javadoc注释 + +1. `message` 是定义消息的关键字 +2. `required` 表示这个字段是必需的, 必须在序列化的时候被赋值。 +3. `optional` 代表这个字段是可选的,可以为0个或1个但不能大于1个。 +4. `repeated` 则代表此字段可以被重复任意多次包括0次。 +5. `int32` 和 `string` 是字段的类型。后面是我们定义的字段名。 +6. 等号右边 1,2,3 则是代表每个字段的一个唯一的编号标签,在同一个消息里不可以重复 + - 这些编号标签用与在消息二进制格式中标识你的字段,并且消息一旦定义就不能更改 + - 需要说明的是标签在1到15范围的采用一个字节进行编码。所以通常将标签1到15用于频繁发生的消息字段 + - 编号标签大小的范围是1 到 2的29次幂–1。此外不能使用protobuf系统预留的编号标签(19000 -19999) + +## 数据类型 + +![数据类型对应表](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Learn/java/protobuf/protobuf-type.jpeg) + +> [Issue: Protobuf 不支持Java的BigDecimal](https://github.com/protocolbuffers/protobuf/issues/4406) + +_复杂类型_ +> 定义了enum枚举类型,嵌套的消息。甚至对原有的消息进行了扩展,也可以对字段设置默认值。添加注释等 +```protobuf + package "com.github.kuangcp"; + // 导入另一个proto定义 + import "article.proto"; + + message Article { + required int32 article_id = 1; + optional string article_excerpt = 2; + repeated string article_picture = 3; + optional int32 article_pagecount = 4 [default = 0]; + + enum ArticleType { + NOVEL = 0; + PROSE = 1; + PAPER = 2; + POETRY = 3; + } + + optional ArticleType article_type = 5 [default = NOVEL]; + message Author { + required string name = 1; //作者的名字 + optional string phone = 2; + } + + optional Author author = 6; + repeated int32 article_numberofwords = 7 [packed=true]; + + reserved 9, 10, 12 to 15; + extensions 100 to 1000; + } + extend Article { + optional int32 followers_count = 101; + optional int32 likes_count= 102; + } + message Other { + optional string other_info = 1; + oneof test_oneof { + string code1 = 2; + string code2 = 3; + } + } +``` +> 此外reserved关键字主要用于保留相关编号标签,主要是防止在更新proto文件删除了某些字段,而未来的使用者定义新的字段时重新使用了该编号标签。这会引起一些问题在获取老版本的消息时,譬如数据冲突,隐藏的一些bug等。所以一定要用reserved标记这些编号标签以保证不会被使用 + +> 当我们需要对消息进行扩展的时候,我们可以用extensions关键字来定义一些编号标签供第三方扩展。这样的好处是不需要修改原来的消息格式。就像上面proto文件,我们用extend关键字来扩展。只要扩展的字段编号标签在extensions定义的范围里。 + +> 对于基本数值类型,由于历史原因,不能被protobuf更有效的encode。所以在新的代码中使用packed=true可以更加有效率的encode。注意packed只能用于repeated 数值类型的字段。不能用于string类型的字段。 + +> 在消息Other中我们看到定义了一个oneof关键字。这个关键字作用比较有意思。当你设置了oneof里某个成员值时,它会自动清除掉oneof里的其他成员,也就是说同一时刻oneof里只有一个成员有效。这常用于你有许多optional字段时但同一时刻只能使用其中一个,就可以用oneof来加强这种效果。但需要注意的是oneof里的字段不能用required,optional,repeted关键字 + +- 修改Protobuf文件的建议: + 1. 不能改变已有的任何编号标签。 + 2. 只能添加optional和repeated的字段。这样旧代码能够解析新的消息,只是那些新添加的字段会被忽略。但是序列化的时候还是会包含哪些新字段。而新代码无论是旧消息还是新消息都可以解析。 + 3. 非required的字段可以被删除,但是编号标签不可以再次被使用,应该把它标记到reserved中去 + 4. 非required可以被转换为扩展字段,只要字段类型和编号标签保持一致 + 5. 相互兼容的类型,可以从一个类型修改为另一个类型,譬如int32的字段可以修改为int64 + +>- 使用上, 因为有多个消息类型, 那么会采用一个数值id作为code, 进行对应 方便沟通 + +### Protobuf3 +[why remove required and optional](https://stackoverflow.com/questions/31801257/why-required-and-optional-is-removed-in-protocol-buffers-3) + +************************ + +## Linux上安装Protobuf +> [参考: linux下Google的Protobuf安装及使用笔记](http://www.cnblogs.com/brainy/archive/2012/05/13/2498671.html) | [参考:proto buffer 安装 及 调用](http://dofound.blog.163.com/blog/static/1711432462013524111644655/) + +- `下载二进制(推荐)` [各个版本,平台的 protoc](https://repo1.maven.org/maven2/com/google/protobuf/protoc/) + +- `编译安装` + - [下载2.5](https://github.com/google/protobuf/releases/tag/v2.5.0) 并解压 + - 进入目录 `./configure` + - `make` `make check` `sudo make install` + - `protoc --version` + +> 注意: ./configure 时, 默认会安装在/usr/local目录下,可以加`--prefix=/usr`来指定安装到/usr/lib下 +>- 如果不加, 上述参数就要执行 `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib` +>- 当然,可以将这个环境变量的设置加在 .zshrc 或者 .bashrc 里 +>- 不然就会报错: `protoc: error while loading shared libraries: libprotobuf.so.8: cannot open shared object file: No such file or directory` + +************************ +## Java中的使用 +> [Java Protobuf](/Java/AdvancedLearning/JavaSerialize.md#protobuf) + +************************ + +## 实现原理 +> [参考: 图解Protobuf编码](https://blog.csdn.net/zxhoo/article/details/53228303) +> [参考: protobuf 编码实现解析(java)](https://www.cnblogs.com/onlysun/p/4574850.html) +> [Google Protocol Buffer 的使用和原理](https://www.ibm.com/developerworks/cn/linux/l-cn-gpb/index.html) `C++ 但是原理差不多` diff --git a/Skills/Serialization/Serialization.md b/Skills/Serialization/Serialization.md new file mode 100644 index 0000000..b73f610 --- /dev/null +++ b/Skills/Serialization/Serialization.md @@ -0,0 +1,145 @@ +--- +title: Serialization +date: 2024-04-21 16:35:34 +tags: +categories: +--- + +💠 + +- 1. [序列化](#序列化) + - 1.1. [序列化协议特性](#序列化协议特性) + - 1.2. [序列化/反序列化 编码/解码](#序列化反序列化-编码解码) + - 1.3. [网络通信中序列化和反序列化的组件](#网络通信中序列化和反序列化的组件) +- 2. [编码方式](#编码方式) + - 2.1. [TLV](#tlv) +- 3. [解决方案](#解决方案) + - 3.1. [XML](#xml) + - 3.2. [JSON](#json) + - 3.3. [MessagePack](#messagepack) + - 3.4. [Protobuf](#protobuf) + - 3.5. [FlatBuffers](#flatbuffers) + - 3.6. [SBE](#sbe) + - 3.7. [capnproto](#capnproto) + - 3.8. [Thrift](#thrift) + - 3.9. [Avro](#avro) + - 3.10. [Kyro](#kyro) + +💠 2024-09-20 17:30:23 +**************************************** +# 序列化 +> [参考: 序列化和反序列化](https://tech.meituan.com/2015/02/26/serialization-vs-deserialization.html) + +- [Hessian](http://hessian.caucho.com/) +- [Redisson 数据序列化](https://github.com/redisson/redisson/wiki/4.-%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97%E5%8C%96)`对象编码 方案列表` + +## 序列化协议特性 +1. 通用性: 跨语言,跨平台,普及流行程度 +1. 强健性: 成熟度,语言/平台的公平性 +1. 可调试性/可读性: 可读性高调试的成本低,例如JSON和Protobuf在查看序列化后数据,前者直接阅读后者需要工具和IDL文件做反序列化 +1. 性能: 时间和空间成本 +1. 扩展性:功能和业务的发展需要调整对象的字段,扩展性高的协议可以兼容共存新旧版本 + +************************ + +## 序列化/反序列化 编码/解码 + +> [关于序列化和编码这两个概念的疑惑](https://www.v2ex.com/t/838587)`序列化就是编码的一个实现,序列化强调结果,编码强调方式` +- 编码 (一种信息方式转换为另一种信息方式), 例如音视频的编解码就是音视频信号和二进制之间的转换,DNA编解码则是将遗传信息和碱基序列的转换 +- 序列化 通常指各平台或语言将内存中的对象转换为跨平台的二进制串或字符串, 例如 C中的Struct Java中的对象 转换为XML/JSON/Protobuf + +## 网络通信中序列化和反序列化的组件 +面对实际应用场景里,典型的网络传输(例如 RPC,Websocket)中,序列化和反序列化流程往往需要如下组件: + +- IDL(Interface description language)文件 + - 参与通讯的各方需要对通讯的内容需要做相关的约定(Specifications)。为了建立一个与语言和平台无关的约定,这个约定需要采用与具体开发语言、平台无关的语言来进行描述。这种语言被称为接口描述语言(IDL),采用IDL撰写的协议约定称之为IDL文件。 +- IDL Compiler:IDL文件中约定的内容为了在各语言和平台可见,需要有一个编译器,将IDL文件转换成各语言对应的动态库。 +- Stub/Skeleton Lib:负责序列化和反序列化的工作代码。 + - Stub是一段部署在分布式系统客户端的代码,一方面接收应用层的参数,并对其序列化后通过底层协议栈发送到服务端,另一方面接收服务端序列化后的结果数据,反序列化后交给客户端应用层; + - Skeleton部署在服务端,其功能与Stub相反,从传输层接收序列化参数,反序列化后交给服务端应用层,并将应用层的执行结果序列化后最终传送给客户端Stub。 +- Client/Server:指的是应用层程序代码,他们面对的是IDL所生存的特定语言的class或struct。 +- 底层协议栈和互联网:序列化之后的数据通过底层的传输层、网络层、链路层以及物理层协议转换成数字信号在互联网中传递。 + +************************ + +> [CyberChef](https://github.com/gchq/CyberChef) `encryption, encoding, compression and data analysis` + +# 编码方式 +由于通信方式通常是流式的,需要考虑二进制流和结构化信息的正确编码方式来实现可用可靠,例如TCP流中“拆包粘包问题” + +## TLV +TLV 即 Tag - Length - Value。Tag 作为该字段的唯一标识,Length 代表 Value 数据域的长度,最后的 Value 便是数据本身 + +HTTP协议中有使用到类似的设计思想(在Header部分会声明Body的Length) + +# 解决方案 +XML序列化(Xstream)无论在性能和简洁性上比较差,JSON和Protobuf使用更为广泛, Protobuf压缩率和性能更好。 +常见的Web服务优先选择JSON有更大普适性(跨语言,跨系统),或者后端使用Protobuf,在网关层转为JSON。 + +> FlatBuffers 和 Cap'n Proto、simple-binary-encoding,支持“零拷贝”反序列化. + +## XML +XML历史悠久,其1.0版本早在1998年就形成标准,并被广泛使用至今。 +XML的最初产生目标是对互联网文档(Document)进行标记,所以它的设计理念中就包含了对于人和机器都具备可读性。 但是,当这种标记文档的设计被用来序列化对象的时候,就显得冗长而复杂(Verbose and Complex)。 +XML本质上是一种描述语言,并且具有自我描述(Self-describing)的属性,所以XML自身就被用于XML序列化的IDL。 标准的XML描述格式有两种:DTD(Document Type Definition)和XSD(XML Schema Definition)。 + + +SOAP(Simple Object Access protocol) 是一种被广泛应用的,基于XML为序列化和反序列化协议的结构化消息传递协议 +SOAP是一种采用XML进行序列化和反序列化的协议,它的IDL是WSDL. 而WSDL的描述文件是XSD,而XSD自身是一种XML文件,此时产生了递归定义 + +## JSON +> Javascript Object Notation + +- 优点:具备可读性,自描述性(序列化时无需IDL),数据相较XML更简洁,解析成本低,原生支持JavaScript(已是Ajax事实标准) +- 缺点:数据信息占比仍较低 + +************************ +> 二进制JSON +- JSONB JSON字符串二进制化, 例如MongoDB,PostgreSQL有使用到 + - [ PostgreSQL JSON Types](https://www.postgresql.org/docs/current/datatype-json.html) +- [CBOR](http://cbor.io/) JSON二进制协议,多语言实现 +- [Amazon Ion](https://amazon-ion.github.io/ion-docs/index.html) 多语言实现 +- [Smile](https://github.com/FasterXML/smile-format-specification) + +## MessagePack +> [Github](https://github.com/msgpack) | [Site](https://msgpack.org/) + +MessagePack 是一种高效的二进制序列化格式。它能让你在多种语言之间交换数据,就像 JSON 一样。但它的速度更快,体积更小。小整数被编码成一个字节,而典型的短字符串除了字符串本身外,只需要一个额外的字节。 可以理解为按特定规则压缩的JSON + +## Protobuf +[Note: Protobuf](/Skills/Serialization/Protobuf.md) + +## FlatBuffers +[Github](https://github.com/google/flatbuffers) | [Doc](https://flatbuffers.dev/index.html) + +更轻量快速,适用于性能敏感的应用场景,例如移动端游戏。兼容protocolbuffer的proto文件 + +> [深入浅出 FlatBuffers 之 Schema](https://halfrost.com/flatbuffers_schema/) + +FlatBuffer 是一个二进制 buffer,它使用 offset 组织嵌套对象(struct,table,vectors,等),可以使数据像任何基于指针的数据结构一样,就地访问数据。然而 FlatBuffer 与大多数内存中的数据结构不同,它使用严格的对齐规则和字节顺序来确保 buffer 是跨平台的。此外,对于 table 对象,FlatBuffers 提供前向/后向兼容性和 optional 字段,以支持大多数格式的演变。 + +FlatBuffers 的主要目标是避免反序列化。这是通过定义二进制数据协议来实现的,一种将定义好的将数据转换为二进制数据的方法。由该协议创建的二进制结构可以 wire 发送,并且无需进一步处理即可读取,即无临时对象和额外内存分配。相比较而言,在传输 JSON 时,我们需要将数据转换为字符串,通过 wire 发送,解析字符串,并将其转换为本地对象。Flatbuffers 不需要这些操作。你用二进制装入数据,发送相同的二进制文件,并直接从二进制文件读取。 + +## SBE +> [Simple Binary Encoding](https://github.com/real-logic/simple-binary-encoding) + +## capnproto +> [Cap’n Proto](https://github.com/capnproto/capnproto) + +## Thrift +> [官网](https://thrift.apache.org/)源于Facebook, 支持多种语言: C++ C# Cocoa Erlang Haskell Java Ocami Perl PHP Python Ruby Smalltalk + +它支持数据(对象)序列化和多种类型的`完整RPC服务`, Thrift适用于静态的数据交换, 需要预先定义IDL文件。 +但是由于Thrift的序列化被内嵌于Thrift框架,Thrift框架本身并没有提供序列化和反序列化接口扩展,这导致其很难和其他应用协议共同使用(例如HTTP)。 + +## Avro +Avro的产生解决了JSON的冗长和没有IDL的问题,Avro属于Apache Hadoop的一个子项目。 +Avro提供两种序列化格式:JSON格式或者Binary格式。Binary格式在空间开销和解析性能方面可以和Protobuf媲美,JSON格式方便测试阶段的调试。 + +由于Avro的设计理念偏向于动态类型语言,对于动态语言为主的应用场景,Avro是更好的选择。 + +Avro在做序列化时会将IDL定义(Schema)和数据一起传输,因此序列串具有自描述性,非常适合于做Hive、Pig和MapReduce的持久化数据格式 + + +## Kyro +> [Github](https://github.com/EsotericSoftware/kryo) diff --git a/Skills/SoftwareDesignEngineer.md b/Skills/SoftwareDesignEngineer.md index 6c8f1c2..2aa6a73 100644 --- a/Skills/SoftwareDesignEngineer.md +++ b/Skills/SoftwareDesignEngineer.md @@ -1,72 +1,80 @@ -`目录 start` - -- [软考知识点](#软考知识点) - - [【1.计算机组成与结构】](#1计算机组成与结构) - - [【计算机中数据的表示】:](#计算机中数据的表示) - - [【CPU】:](#cpu) - - [【存储系统】:](#存储系统) - - [【例题】](#例题) - - [【输入输出系统】:](#输入输出系统) - - [【总线系统】:](#总线系统) - - [【加密算法】:](#加密算法) - - [【补码】:](#补码) - - [【访问方式】:](#访问方式) - - [【2.程序设计语言】](#2程序设计语言) - - [【基本概念】:](#基本概念) - - [编译和解释:](#编译和解释) - - [KMP模式匹配算法:](#kmp模式匹配算法) - - [高级程序设计语言翻译流程:](#高级程序设计语言翻译流程) - - [文法分析:](#文法分析) - - [【考点】:正规式和DFA或NFA的转换](#考点正规式和dfa或nfa的转换) - - [【3.操作系统】](#3操作系统) - - [定义](#定义) - - [进程管理](#进程管理) - - [存储管理](#存储管理) - - [处理机](#处理机) - - [设备管理](#设备管理) - - [文件管理](#文件管理) - - [作业管理](#作业管理) - - [【4.软件工程基础知识】](#4软件工程基础知识) - - [成本估算](#成本估算) - - [敏捷开发方法](#敏捷开发方法) - - [【5.系统开发与运行】](#5系统开发与运行) - - [【模块】](#模块) - - [【测试方法】](#测试方法) - - [【6.网络与多媒体基础知识】近几年没有考题](#6网络与多媒体基础知识近几年没有考题) - - [【硬件设备】](#硬件设备) - - [【网络安全】](#网络安全) - - [动画与视频](#动画与视频) - - [媒体类型:](#媒体类型) - - [【7.数据库技术,基础】](#7数据库技术基础) - - [关系模式(不是指表,但是是包含了表,在表的层次以上)](#关系模式(不是指表但是是包含了表在表的层次以上)) - - [【数据依赖】](#数据依赖) - - [【码】](#码) - - [【范式】 应用于关系模式R](#范式-应用于关系模式r) - - [闭包](#闭包) - - [模式分解](#模式分解) - - [【8.算法与数据结构】](#8算法与数据结构) - - [【9.面向对象技术】](#9面向对象技术) - - [三大特性](#三大特性) - - [关联关系:](#关联关系) - - [面向对象分析(OOA):](#面向对象分析(ooa)) - - [设计模式【主要分三类】](#设计模式主要分三类) - - [1.创建型设计模式](#1创建型设计模式) - - [2.结构型设计模式](#2结构型设计模式) - - [3.行为设计模式](#3行为设计模式) - - [【常见设计模式】:](#常见设计模式) - - [【10.标准化与知识产权】](#10标准化与知识产权) - - [标准化](#标准化) - - [【11.专业英语】](#11专业英语) - - [【12.数据流图】](#12数据流图) - - [【13.UML设计与分析】](#13uml设计与分析) - - [图形元素讲解](#图形元素讲解) - - [UML概念图](#uml概念图) - - [【14.数据库设计】](#14数据库设计) - - [【15.数据结构与算法】](#15数据结构与算法) - - [【16.Java程序设计】](#16java程序设计) - - [大学生们,你们知道软考的意义吗?(转)](#大学生们你们知道软考的意义吗(转)) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 软考知识点 +date: 2018-11-21 10:56:52 +tags: +categories: + - 应试 +--- + +**目录 start** + +1. [软考知识点](#软考知识点) + 1. [【1.计算机组成与结构】](#1计算机组成与结构) + 1. [【计算机中数据的表示】:](#计算机中数据的表示) + 1. [【CPU】:](#cpu) + 1. [【存储系统】:](#存储系统) + 1. [【例题】](#例题) + 1. [【输入输出系统】:](#输入输出系统) + 1. [【总线系统】:](#总线系统) + 1. [【加密算法】:](#加密算法) + 1. [【补码】:](#补码) + 1. [【访问方式】:](#访问方式) + 1. [【2.程序设计语言】](#2程序设计语言) + 1. [【基本概念】:](#基本概念) + 1. [编译和解释:](#编译和解释) + 1. [KMP模式匹配算法:](#kmp模式匹配算法) + 1. [高级程序设计语言翻译流程:](#高级程序设计语言翻译流程) + 1. [文法分析:](#文法分析) + 1. [【考点】:正规式和DFA或NFA的转换](#考点正规式和dfa或nfa的转换) + 1. [【3.操作系统】](#3操作系统) + 1. [定义](#定义) + 1. [进程管理](#进程管理) + 1. [存储管理](#存储管理) + 1. [处理机](#处理机) + 1. [设备管理](#设备管理) + 1. [文件管理](#文件管理) + 1. [作业管理](#作业管理) + 1. [【4.软件工程基础知识】](#4软件工程基础知识) + 1. [成本估算](#成本估算) + 1. [敏捷开发方法](#敏捷开发方法) + 1. [【5.系统开发与运行】](#5系统开发与运行) + 1. [【模块】](#模块) + 1. [【测试方法】](#测试方法) + 1. [【6.网络与多媒体基础知识】近几年没有考题](#6网络与多媒体基础知识近几年没有考题) + 1. [【硬件设备】](#硬件设备) + 1. [【网络安全】](#网络安全) + 1. [动画与视频](#动画与视频) + 1. [媒体类型:](#媒体类型) + 1. [【7.数据库技术,基础】](#7数据库技术基础) + 1. [关系模式(不是指表,但是是包含了表,在表的层次以上)](#关系模式(不是指表但是是包含了表在表的层次以上)) + 1. [【数据依赖】](#数据依赖) + 1. [【码】](#码) + 1. [【范式】](#范式) + 1. [闭包](#闭包) + 1. [模式分解](#模式分解) + 1. [【8.算法与数据结构】](#8算法与数据结构) + 1. [【9.面向对象技术】](#9面向对象技术) + 1. [三大特性](#三大特性) + 1. [关联关系:](#关联关系) + 1. [面向对象分析(OOA):](#面向对象分析(ooa)) + 1. [设计模式【主要分三类】](#设计模式主要分三类) + 1. [1.创建型设计模式](#1创建型设计模式) + 1. [2.结构型设计模式](#2结构型设计模式) + 1. [3.行为设计模式](#3行为设计模式) + 1. [【常见设计模式】:](#常见设计模式) + 1. [【10.标准化与知识产权】](#10标准化与知识产权) + 1. [标准化](#标准化) + 1. [【11.专业英语】](#11专业英语) + 1. [【12.数据流图】](#12数据流图) + 1. [【13.UML设计与分析】](#13uml设计与分析) + 1. [图形元素讲解](#图形元素讲解) + 1. [UML概念图](#uml概念图) + 1. [【14.数据库设计】](#14数据库设计) + 1. [【15.数据结构与算法】](#15数据结构与算法) + 1. [【16.Java程序设计】](#16java程序设计) + 1. [大学生们,你们知道软考的意义吗?(转)](#大学生们你们知道软考的意义吗(转)) + +**目录 end**|_2020-06-24 02:06_| **************************************** # 软考知识点 ## 【1.计算机组成与结构】 @@ -75,18 +83,17 @@ * **定点数**:所有数据的小数点位置是固定的,小数点位置在数据最高位是定点小数,在最低位是定点整数,会有溢出的情况发生 * **浮点数**:阶符,阶码,数符,尾数 组成, 尾数决定精度,阶码决定数据范围,最适合浮点数阶码的数字编码是移码 - 数的机器码表示: - - **原码**:符号位表示该数的符号,0正1负 。原码中分+0和-0 - - **反码**:符号位表示法和原码一样,正数不变,负数要取反(除掉符号位) - - **补码**:正数和原码相同,负数=反码加一,最适合加减运算的数字编码 - - **移码**:在数X上增加一个偏移量来定义的,常用于表示浮点数的阶码部分,如果机器字长n,规定偏移量是2的n-1次方, - 移码定义:[X移]=2(n-1次方)+X(-2(n-1)<=X<2(n-1)) + - **原码**:符号位表示该数的符号,0正1负 。原码中分+0和-0 + - **反码**:符号位表示法和原码一样,正数不变,负数要取反(符号位不变) + - **补码**:正数则和原码相同,负数补码=反码加一,最适合加减运算的数字编码 + - **移码**:在数X上增加一个偏移量来定义的,常用于表示浮点数的阶码部分,如果机器字长n,规定偏移量是2的n-1次方,移码定义:`[X移]=2(n-1次方)+X(-2(n-1)<=X<2(n-1))` * **校验码**: * **奇偶检验码**:通过在编码中增加一位校验位来使编码中1的个数为奇数(奇校验)偶数(偶校验) * **海明码**:在数据位中插入k个校验码,通过扩大码距来实现检错和纠错。数据位n 校验位k 2(k次方)-1>=n+k * **循环冗余校验码**(CRC):利用生成多项式的k个数据位和产生的r个校验码来进行编码,编码长度是k+r - 数制之间转换:2,8,10,16 -#### 【CPU】: +### 【CPU】: - **运算器**:执行所有算术运算,执行所有逻辑运算并进行逻辑测试(与或非,值比较,零值测试) - 指令寄存器(IR):CPU执行指令的时候,先把它从内存中加载到缓冲寄存器中,再送入IR暂存,指令译码器根据IR的内容来产生各种微操作指令 - **程序计数器**(PC):PC具有寄存信息和计数两种功能,又称为指令计数器。程序执行分为:顺序执行,转移执行。 @@ -102,7 +109,7 @@ - **寄存器组**: 专用寄存器,通用寄存器。上面的都是专用的,通用的可以给程序猿控制(提高速度,貌似没卵用) -####【存储系统】: +### 【存储系统】: - **分类**: * 读写存储器(RAM): * 只读存储器(ROM): @@ -136,34 +143,35 @@ 解析:DCFFFH - A5000H = 38000H (16进制) 转换成二进制 0011 1000 0000 0000 0000b 去掉十个0 单位就成了kb,然后就是2的5,6,7次方求和 得到224KB -#### 【输入输出系统】: +### 【输入输出系统】: DMA方式: 无需CPU介入,流程:向CPU申请DMA传送,获取CPU允许后,DMA控制器接管系统总线的控制权;过程中不需要CPU结束回到CPU控制 -#### 【总线系统】: +### 【总线系统】: - **内部总线**: 用于芯片一级的互联,分为芯片内总线和元件级总线。芯片内总线用在集成多芯片,元件级总线用于一块电路板内元器件的连接 - **系统总线**:插件板一级的互联,用于构成计算机各组成部分(CPU,内存,接口) - **外部总线**:又称通信总线,用于设备一级的互联,通过该总线与其他设备进行信息与 -#### 【加密算法】: +### 【加密算法】: - **非对称加密的算法中使用了** 私钥和公钥,私钥用于解密和数字签名,公钥用于加密和认证证书的真实性 - **数字证书**:CA组织会给用户颁发数字证书, 由CA的私钥进行创建数字证书,数字签名,然后接收方使用CA的公钥来检查其真实性和数字签名 (检查文件内容是否被篡改) -#### 【补码】: +### 【补码】: 一般用于浮点数的阶码,简化计算机运算部分设计,因为 符号位可以运算,减作加处理 -#### 【访问方式】: + +### 【访问方式】: 寻址方式访问: 直接 , 顺序 , 随机 内容方式访问:相联存储器 ************************************************************************* ## 【2.程序设计语言】 ### 【基本概念】: -#### 编译和解释: +### 编译和解释: - **编译**:词法分析,语法分析,语义分析,中间代码生成,代码优化,生成目标代码(汇编语言/机器语言) - **解释**:分析部分:词法分析,语法分析,语义分析,然后把源程序翻译成中间代码(常用逆波兰表现形式,树,后缀式,四元,三元式) - 解释部分:对中间代码进行解释运行 - +解释部分:对中间代码进行解释运行 + #### KMP模式匹配算法: 求解模式串 p 中的next 函数值 next={ @@ -207,7 +215,7 @@ next(j): 0 1 1 2 2 3 4 1 ## 【3.操作系统】 -####定义 +### 定义 * **分类** * 批处理 @@ -230,7 +238,7 @@ next(j): 0 1 1 2 2 3 4 1 * 设备管理 * 作业管理 -#### 进程管理 +### 进程管理 * 同步是进程间的直接制约---:进程合作的等待问题 * 互斥是进程间的间接制约---:进程竞争一个资源(进程独占) * PV操作: @@ -242,24 +250,24 @@ next(j): 0 1 1 2 2 3 4 1 * 临界资源值 = 线程数 × ( 最大需求-1) + 1 * 不发生死锁的临界:即有一个不是阻塞,其他的线程都是只差一个资源(阻塞等待中) -#### 存储管理 +### 存储管理 * 分页 * 分段 * 页段混合 -#### 处理机 +### 处理机 * 图灵机里:有限和无限的区别就是后继码是否唯一,唯一就是有限 * 要特别注意表达式的写法,一般这种题目看读取字符结尾就可以快速选择答案 -#### 设备管理 +### 设备管理 * **磁盘读取** * SCAN 扫描算法 磁头按当前运动方向,至最大/最小再逆序折回读取(一来一返) * CSCAN 单向扫描算法 磁头按当前运动方向,至最大/小,立马到最小/大又按初始的运动方向进行读取(两个单向) * 注意:当柱面是一样的时候,比较扇区的顺序 -#### 文件管理 +### 文件管理 * **位视图存储** * **概括:用某号物理块除以字长得到第几个字,容量就要再除以物理块的大小再除字长** @@ -282,7 +290,7 @@ next(j): 0 1 1 2 2 3 4 1 * 相对路径从当前路径(当前工作目录)开始的路径 eg:d\ * 绝对路径从根目录开始的路径不含文件名 eg: \d\d\ -#### 作业管理 +### 作业管理 * 调度级别:高级调度(作业调度),中级调度(交换调度),低级调度(进程调度) * 优先级调度算法:将给出的图看成树,左上为根。同层就是并发,父子关系就体现了优先级 @@ -318,7 +326,7 @@ next(j): 0 1 1 2 2 3 4 1 **** -#### 成本估算 +### 成本估算 * **估算策略** * 自顶向下 先确定整体时间,再按模块,分配时间 * 自底向上 按模块分析需求时间,再向上累积得出全部时间 @@ -372,7 +380,7 @@ next(j): 0 1 1 2 2 3 4 1 * 构建阶段: 剩余的构建和功能开发,集成为产品,此时是beta版 * 移交阶段: 测试,基于反馈调整,交付系统 -#### 敏捷开发方法 +### 敏捷开发方法 * **极限编程** * 将项目细分小模块,迭代编写, * 先测试再编程 @@ -446,7 +454,7 @@ next(j): 0 1 1 2 2 3 4 1 * 数据流必须和加工有关,必须经过加工 * 数据流图的顶层数据流图,描述了系统的输入和输出,只有一个加工表示系统 -#### 【模块】 +### 【模块】 * **模块独立**: * 耦合:**模块之间**的紧密程度 * 聚合:**模块内部**各元素之间联系的紧密程度 @@ -470,9 +478,9 @@ next(j): 0 1 1 2 2 3 4 1 * 当一个模块使用另一个模块内部的控制和控制信息等数据; * 一个模块直接转移到另一个模块; -**** +****************** -#### 【测试方法】 +### 【测试方法】 * **白盒测试**:由低到高 * 语句覆盖 * 冒泡排序只要一个用例就可以达到语句覆盖 @@ -492,7 +500,6 @@ next(j): 0 1 1 2 2 3 4 1 * **软件测试步骤**:。。。。。。 * **单元测试**,集成测试,确认测试,系统测试 - **** * **软件维护**: @@ -534,7 +541,7 @@ next(j): 0 1 1 2 2 3 4 1 * 应用层协议:NFS,Telnet,SMTP,DNS,SNMP,FTP **** -#### 【硬件设备】 +### 【硬件设备】 * **物理层互联设备** * 中继器 Repeater : 常用于两个网络节点之间物理信号的双向转发工作 * 集线器 Hub :对接收到的信号进行再生整形放大,以扩大网络传输距离(是相当于将各个主机直接联通,在逻辑上构成了一个共享的总线,所以就构成了一个广播域,一个冲突域) @@ -574,7 +581,7 @@ next(j): 0 1 1 2 2 3 4 1 **** -#### 【网络安全】 +### 【网络安全】 * **防火墙技术分类** * 包过滤型防火墙 * 代理服务器型防火墙 @@ -636,12 +643,12 @@ next(j): 0 1 1 2 2 3 4 1 * **例题**:使用150 DPI扫描一幅3×4英寸的图,原始24位的图像占用多少字节 * 150DPI 表示每英寸150像素点 所以 150×150×3×4×24/8 得到占用字节数 -#### 动画与视频 +### 动画与视频 * **彩色电视的制式**:NTCS和PAL,前者用于日美加拿大,墨西哥等,后者用于中国,欧洲,中东 * **视频文件格式**:GIF,FLC/FLI,AVI,.MOV/QT * **视频压缩标准**:H.261,JEPG,MPEG,DVI -#### 媒体类型: +### 媒体类型: * **感觉媒体**:使人感觉得到的媒体,音乐,图像等 * **表示媒体**:为加工处理传输感觉媒体而来的一种媒体,例如文本编码,图像编码,声音编码 * **表现媒体**:进行信息的输入输出的媒体:键盘,鼠标,扫描仪,话筒 @@ -652,7 +659,7 @@ next(j): 0 1 1 2 2 3 4 1 ************************************************** ## 【7.数据库技术,基础】 -#### 关系模式(不是指表,但是是包含了表,在表的层次以上) +### 关系模式(不是指表,但是是包含了表,在表的层次以上) * **一个关系模式应该是五元组** R(U,D,DOM,F) * R 符号化的元组语义 * U 一组属性 @@ -663,7 +670,7 @@ next(j): 0 1 1 2 2 3 4 1 * 当且仅当 U 上的一个关系r满足F时,r称为关系模式R的一个关系。 -#### 【数据依赖】 +### 【数据依赖】 * 数据依赖是一个关系内部属性与属性之间的一种约束关系 * 1.**函数依赖** FD * 若U的子集X,Y,X和Y形成唯一标识,X函数确定Y,Y函数依赖于X。记X -> Y @@ -676,7 +683,7 @@ next(j): 0 1 1 2 2 3 4 1 * 1.2 部分函数依赖:X->Y 但是Y不完全函数依赖于X 。 * 1.3 传递函数依赖:X->Y(Y不属于X),Y不能->X, Y->Z(Z不属于Y)则称Z对X传递函数依赖 -#### 【码】 +### 【码】 * 设 K是R中的属性或属性组,K能完全函数依赖推出U,则K是R的候选码。 * 如果上面的K是部分函数依赖,则称K是超码,候选码是最小的超码 * 主属性:被包含在任一候选码中的属性 @@ -684,7 +691,9 @@ next(j): 0 1 1 2 2 3 4 1 * 全码:所有属性一起构成了码 * 外码:关系模式R中属性或属性组 X 不是R的码,但是X是另一个关系模式的码,则X是外码 -#### 【范式】 应用于关系模式R +### 【范式】 +> 应用于关系模式R + * **1NF**:每一个分量都是不可分的数据项 * **2NF**:R满足1NF,且每一个非主属性完全函数依赖任何一个候选码 * **3NF**:R满足1NF,R中不存在码X,属性组Y以及非主属性Z(Z不属于Y) 使得X->Y Y->Z Y不->X 则称R是满足3NF(取消了传递函数依赖) @@ -693,11 +702,11 @@ next(j): 0 1 1 2 2 3 4 1 * 所有主属性对每一个不包含它的码也是完全函数依赖 * 没有任何属性完全依赖于非码的任何一组属性 -#### 闭包 +### 闭包 * 在关系模式R中为F所逻辑蕴含的函数依赖的全体叫做F的闭包 * 设F是属性集U上的一组函数依赖,X,Y属于U,![](http://i.imgur.com/pChaMD3.png)={A|X->A} 称为![](http://i.imgur.com/pChaMD3.png)属性集X关于函数依赖集F的闭包 -#### 模式分解 +### 模式分解 * 具有无损连接性(粗略概述:不减少字段和改变元组) * 要保持函数依赖(子模式里满足依赖关系) * 既要保持函数依赖性,又要具有无损连接性 @@ -734,7 +743,7 @@ next(j): 0 1 1 2 2 3 4 1 * **类**:对象的抽象 * **消息**:对象之间通信的一种构造(对象调用方法) -#### 三大特性 +### 三大特性 * **继承**:可分单重继承,多重继承 * **多态**: * 参数多态:指同一个对象,函数,过程能以一致的形式用于不同类型 @@ -758,7 +767,7 @@ next(j): 0 1 1 2 2 3 4 1 * **聚合** : 体现的是整体与部分的关系,整体与部分之间是可分离的,他们各自有各自的声明周期,部分可以属于多个整体对象,也可以为多个整体对象共享 * **组合** : 也称强聚合,整体和部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束 -#### 面向对象分析(OOA): +### 面向对象分析(OOA): * 1.分析问题域,建立用例模型。 * 2.发现和定义对象和类 * 3.识别对象的内部特征 @@ -847,7 +856,7 @@ next(j): 0 1 1 2 2 3 4 1 ### 设计模式【主要分三类】 -##### 1.创建型设计模式 +#### 1.创建型设计模式 * 抽象了实例化过程,它们帮助一个系统独立于如何创建,组合和表示它的那些对象。 * 一个类创建型模型使用继承改变被实例化的类,而一个对象创建型模型将实例化委托给另一个对象 @@ -858,7 +867,7 @@ next(j): 0 1 1 2 2 3 4 1 - ``` -##### 2.结构型设计模式 +#### 2.结构型设计模式 > 适配器(**Adapter**)模式,桥接(**Bridge**)模式,组合(**Compontent**)模式,代理(**Proxy**)模式,享元(**Flyweight**)模式,**Facade模式**,装饰(**Decorator**)模式,命令(**Command**)模式,状态(**State**)模式 * 结构型设计模式涉及如何组合类和对象以获得更大的结构 @@ -883,7 +892,7 @@ next(j): 0 1 1 2 2 3 4 1 * **Decorator模式** 描述如何动态地为对象添加职责,模式采用递归方式组合对象,从而允许添加任意多的对象职责。 -##### 3.行为设计模式 +#### 3.行为设计模式 * 行为设计模式涉及算法和对象间职责的分配,行为模式描述对象和类的模式以及其通信模式 * 行为模式使用继承机制在类间派发行为 @@ -953,7 +962,7 @@ next(j): 0 1 1 2 2 3 4 1 ## 【10.标准化与知识产权】 -#### 标准化 +### 标准化 - 标准分类 - 按适用范围:国际标准,国家标准,行业标准,企业标准,项目标准 - 按性质分类:技术标准,管理标准,工作标准 @@ -1003,7 +1012,7 @@ next(j): 0 1 1 2 2 3 4 1 - **候选类的删除** 使用了接口隔离原则(ISP): - 不应该强迫类实现依赖于他们不同的方法(不需要的方法不实现) -#### 图形元素讲解 +### 图形元素讲解 * 用例图: * 消息传递:实线箭头 @@ -1049,10 +1058,9 @@ next(j): 0 1 1 2 2 3 4 1 **状态图:** ![](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/UML/UML_10.jpg) -#### UML概念图 +### UML概念图 * 有点像ER图,也是描述了实体之间关系(有一对多等关系),有面向对象特有的关系 - ************************************************** ## 【14.数据库设计】 diff --git a/Skills/SoftwareEngineering/CodeExcellentCode.md b/Skills/SoftwareEngineering/CodeExcellentCode.md index c72abe9..cf78744 100644 --- a/Skills/SoftwareEngineering/CodeExcellentCode.md +++ b/Skills/SoftwareEngineering/CodeExcellentCode.md @@ -1,4 +1,54 @@ -# 代码的坏味道 +--- +title: 写出优秀的代码 +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [写出优秀的代码](#写出优秀的代码) + - 1.1. [代码的坏味道](#代码的坏味道) + - 1.2. [《代码整洁之道》](#代码整洁之道) + - 1.2.1. [命名](#命名) + - 1.2.2. [函数](#函数) + - 1.2.3. [注释](#注释) + - 1.2.4. [格式](#格式) + - 1.2.5. [异常](#异常) + - 1.3. [编写可读代码的艺术](#编写可读代码的艺术) + +💠 2024-09-06 11:36:43 +**************************************** +# 写出优秀的代码 +> 代码整洁之道 + +> [Clean Code Notes](https://github.com/JuanCrg90/Clean-Code-Notes) + +*************************** + +- 《7 QUESTIONS TO ASK YOURSELF ABOUT YOUR CODE》 -- Bozhidar Bozhanov + +1. 代码是正确的吗? + - 是不是实现了规格说明书中的需求?如果没有的规格说明书,你自己是不是付出了足够的努力来找出软件期待的行为, 并且把它测试了一遍? --- 最好是自动的,至少也得有手工的测试。 +2. 代码是完整的吗? + - 不管你的需求文档中写没写, 你的代码是不是仔细考虑了边界条件? 很多边界条件都是技术相关的:连接断开,内存不足,硬盘已满等等。 +3. 代码是安全的吗? + - 它是不是遵循了安全的最佳实践,是否验证了输入数据,防止了数据注入? 它是否经过了对已知攻击的安全测试? 安全当然不仅仅是代码, 但是代码的确可以引入不少安全漏洞。 +4. 代码是可读、可维护的吗? + - 其他人是不是可以轻松地理解你写的代码? 有没有适当的注释来描述一小部分代码在一个大场景中的位置?有没有把代码拆分成小的,可以读的单元。 + +5. 代码是可以扩展的吗? + - 代码是否允许添加新的功能而不破坏老的代码? 是不是参数化的,或者可以配置的? 有没有使用恰当的设计模式来支持扩展? +6. 代码是不是高效的? + - 在高负荷下能否工作正常? 是否避免了一次性读入大量数据到内存中,是否适当地使用了异步的处理? +7. 有没有一些让你可以自豪的地方? + - 你觉得你的代码会让你很自豪,还是说你想把它藏起来不让别人看到? + - 大部分的代码都是平凡的,不是光芒四射的,但是你的代码是不是展示了一些比较好的实践?你是否愿意把他放到GitHub上去? + +其实这些问题不仅仅要在提交代码之前思考,在Code Review的时候也完全可以借鉴。 +高质量的软件依赖很多因素,程序员可以说是最重要的一环。我觉得经常问问自己这些问题并且采取行动,你最终会变得与众不同。 + +## 代码的坏味道 如果在代码中,存在下面的情况之一者,则说明代码需要清除腐败。 @@ -17,12 +67,7 @@ 1. 添加新功能时没有提供任何支持文档:现有的文档过时了。 1. 你发现有的注释说:“不要动这段代码......” - -# 项目编码时优秀的习惯 - - ## 《代码整洁之道》 - ### 命名 - 有意义,短而精悍 - 类用名词,方法用动词,还要特别注意语境 @@ -54,4 +99,7 @@ ### 异常 - 避免null:避免函数返回值是null以及函数入参是null - 尽量避免可控异常(throws)的出现,因为破坏了封装 - + +************************ + +## 编写可读代码的艺术 diff --git a/Skills/SoftwareEngineering/README.md b/Skills/SoftwareEngineering/README.md index 465205a..9770955 100644 --- a/Skills/SoftwareEngineering/README.md +++ b/Skills/SoftwareEngineering/README.md @@ -1,6 +1,6 @@ # 软件工程 -> [参考博客: 禅道的敏捷开发模式](https://blog.csdn.net/gnicky/article/details/72722166) +> [参考: 禅道的敏捷开发模式](https://blog.csdn.net/gnicky/article/details/72722166) ## 设计的思考 -> [参考博客: 什么是“对用户友好”](http://www.yinwang.org/blog-cn/2012/05/18/user-friendliness)`高度抽象, 简洁` +> [参考: 什么是“对用户友好”](http://www.yinwang.org/blog-cn/2012/05/18/user-friendliness)`高度抽象, 简洁` diff --git a/Skills/SoftwareEngineering/Situation.md b/Skills/SoftwareEngineering/Situation.md new file mode 100644 index 0000000..472f50a --- /dev/null +++ b/Skills/SoftwareEngineering/Situation.md @@ -0,0 +1,110 @@ +--- +title: 场景&方案 +date: 2023-09-22 16:56:45 +tags: +categories: +--- + +💠 + +- 1. [方向需求](#方向需求) + - 1.1. [系统重构](#系统重构) +- 2. [业务需求](#业务需求) + - 2.1. [千万级数据导入](#千万级数据导入) + - 2.2. [千万级数据导出](#千万级数据导出) + - 2.3. [商品秒杀](#商品秒杀) +- 3. [数据需求](#数据需求) + - 3.1. [数据血缘方案](#数据血缘方案) + - 3.2. [汉字-拼音 处理](#汉字-拼音-处理) + - 3.3. [敏感词匹配](#敏感词匹配) + - 3.4. [OCR](#ocr) + +💠 2024-10-02 22:33:00 +**************************************** +# 方向需求 +## 系统重构 +> [知乎社区核心业务 Golang 化实践](https://zhuanlan.zhihu.com/p/48039838)`Python所开发的模块使用Go重写` + +场景: + +思考点: + +************************ + +# 业务需求 +## 千万级数据导入 +场景: 将数据库外的千万或亿级Excel CSV txt等文件导入到数据库 + +思考点: +1. 导入时 用户并发,数据库资源控制 + - 操作的频率, 导入的效率 +1. 应用层资源优化,JVM内存 CPU占用 + - 需要按实际导入端,考虑是经过JVM应用层处理,还是直接利用数据库的附属工具或机制 +1. 落库的类型和特点: NoSQL 或 关系型数据库 + - + +方案: + +> NoSQL +- MongoDB +- Elasticsearch + +> 关系型数据库 +1. MySQL + - +1. Clickhouse + - +1. GreenPlum + - copy 方式, 不经过应用端去做 数据解析和写入 + +## 千万级数据导出 +场景: 数据库内大表输出为 csv,Excel + +思考点: +- 导出时 用户并发及数据库资源控制 + - 如何在应用层接入端去感知数据库的资源状态, 协同控制并发频率 (比如: 应用层接受到请求,参考数据库资源状态设置并发数和导出数据量的约束, 而不是设置抽象的限流策略) +- 字段多且大存储高 + - 如何平衡数据导出效率 和 应用JVM投入资源 +- 使用独占长连接完整SQL查询数据库 和 共享连接查询分批SQL 的取舍 `简单理解为分页和不分页` + - 独占连接: 完整SQL查询然后不关闭连接使用游标分批去数据库fetch数据, 最后汇总 + - 共享连接: SQL层加分页或者条件范围 查询小批结果集, 最后汇总 + +方案: +- Java应用层面可以使用JDBC长连接 游标方式查询数据分批读写 + +## 商品秒杀 +[Seckill](https://github.com/hfbin/Seckill) + + +************************ + + +# 数据需求 +## 数据血缘方案 +核心流程为 采集 解析 图计算 存储 展示 + +- 采集: 依据数据库的类型 采集到完备的DML DDL语句(create select, insert,update), 例如MySQL可解析binlog Greenplum可解析审计日志 等等。 +- 解析: ANTLR4将SQL解析为AST,遍历得到每层子查询,每句SQL的有向图 +- 图计算: 将前文的若干有向图 合并为一个大图 得到复杂SQL 和 一批独立SQL 完整正确的图,从而得到完整的血缘关系(入度为0 源表,出度为0 目标表) + - Gremlin Gephi +- 存储: 图数据库存储以上解析出的全部的图,需考虑节点的唯一标识 +- 展示: 图查询,需前端解决海量节点数时的性能问题 +- 生命周期或快照: 从业务和存储上来看,此类数据会一直累积,最后图会成为垃圾堆,可监听删除类型的DDL,移除对应的节点和边。以及依据业务特点定期快照历史数据,重建图 + +> Greenplum/Hive 血缘解析落地方案: 采集SQL,ANTLR4解析为AST 调试规则文件,解析合并图,存入Atlas,查询Atlas + +> [讲讲元数据](https://ganjiacheng.cn/article/2020/article_3_%E8%AE%B2%E8%AE%B2%E5%85%83%E6%95%B0%E6%8D%AE/) + +## 汉字-拼音 处理 +汉字转换拼音,首字母模糊搜索等功能。 + +> [汉字转拼音项目pinyin-plus开源](http://www.kailing.pub/article/index/arcid/326.html) + +## 敏感词匹配 +DFA Bloom过滤器 + +## OCR +Optical Character Recognition + +- 云厂商 例如阿里云,腾讯云等。 +- [tesseract](https://github.com/tesseract-ocr/tesseract) diff --git a/Skills/Spider/SpiderBase.md b/Skills/Spider/SpiderBase.md index bd24dd6..83034cb 100644 --- a/Skills/Spider/SpiderBase.md +++ b/Skills/Spider/SpiderBase.md @@ -1,17 +1,21 @@ -`目录 start` - -- [爬虫基础](#爬虫基础) - - [何为爬虫](#何为爬虫) - - [个人实践](#个人实践) +--- +title: 爬虫基础 +date: 2018-11-21 10:56:52 +tags: +categories: +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +💠 + +- 1. [爬虫基础](#爬虫基础) + - 1.1. [何为爬虫](#何为爬虫) + - 1.2. [个人实践](#个人实践) + +💠 2023-10-09 17:53 **************************************** # 爬虫基础 ## 何为爬虫 ## 个人实践 -2018-02-03 15:06:35 -- 爬到了一个带有cookie验证的网站才发现以前做的爬虫都只是简单的进行分析拆分得到URL然后下载而已 -- 然后就了解到了wget curl等工具使用cookie的情况, Python的cookie还不太会用, 不太直观 diff --git a/Skills/Test/Benchmark.md b/Skills/Test/Benchmark.md new file mode 100644 index 0000000..dff2449 --- /dev/null +++ b/Skills/Test/Benchmark.md @@ -0,0 +1,130 @@ +--- +title: 基准测试 +date: 2023-03-27 20:10:23 +tags: +categories: + - 测试 +--- + +💠 + +- 1. [基准测试](#基准测试) + - 1.1. [目标](#目标) + - 1.2. [测试对象](#测试对象) + - 1.3. [准备](#准备) + - 1.4. [关注指标项](#关注指标项) + - 1.4.1. [client](#client) + - 1.4.2. [server](#server) + - 1.5. [测试方式](#测试方式) + - 1.6. [测试环境准备](#测试环境准备) + +💠 2023-10-09 19:16 +**************************************** + +# 基准测试 +## 目标 +1 挖掘系统瓶颈点,优化系统性能 + +尤其对新系统上线,缺乏性能基线数据,此时压测一般没有明确的qps/rt等指标,而是通过不断施压,不断逼近系统的极限,从而暴露问题,修复问题。 +2 建立性能基线 + +主要是为了收集系统当前的最大性能指标,一般会根据业务特点,先确定对rt和错误率的容忍度,然后通过压测推算出能够支持的最大qps, 并发量等。 + +同时可以结合性能指标和监控数据,来建立合理的预警机制,设立系统水位报警项,限流阈值,弹性策略等。 + +量化系统能力/SLA等 (比如在竞标中引用)。 +3 性能回归 + +对于已上线系统,或者性能需求明确的系统,可以根据线上实际的运行情况,确定系统需要支撑的qps/rt, 然后在涉及性能影响前做回归校验,确保性能满足预期。 +4 系统稳定性 + +更侧重在一定压力的情况下,系统是否能长时间稳定持续的提供SLA保障。 + +一般可以考虑将压力设定到业务峰值的80%,持续施压。 +5 网络/线路延迟稳定性等 + +在一些特殊的业务场合,对延迟的容忍度极小,比如DNS解析,CDN服务,多人实时在线游戏,高频交易等等,需要网络质量,尤其是不同线路(电信/联通/教育网/...)间的差异。 + +## 测试对象 +- 后端 + - 单api + - 单业务逻辑场景 +- 前端 + - 单request + - 单操作 + - 单页 + - 整体页面平均情况 + +## 准备 + +## 关注指标项 + +QPS +TPS +PV +RT +CPU占用率 +CPU Load 逻辑核心数1.2以下? +内存占用率 +出向和入向流量 + +### client +- [前端框架基准测试](https://www.infoq.cn/article/uhsl0goghtl2bm*vjios) + - TTI(Time to Interactive):让一个页面变得可交互需要多长时间。 + - 速度指数(Speed Index):页面处理内容的速度,分数越低也好。 + - FCP(First Contentful Paint):从导航一个页面到浏览器开始渲染 DOM 第一个字节的时间。 + - FCI(First CPU Idle):页面达到最小化可交互的时间(不需要等到页面上的所有元素都可交互,只要可以对大部分用户输入做出响应即可)。 + - FMP(First Meaningful Paint):用户感知到页面主要内容可见的时间。 + - 预估的输入延迟(Estimated Input Latency)。 + +### server +- PV +- QPS +- RT +- 成功率 +- cpu usage +- load + - 最大用户同时在线数 (用户登录系统,一般不需要额外压测,除非业务场景特殊)。 +- mem +- jvm/fullGC +- 连接数(netstat) +- disk io (iostat / iotop) + +## 测试方式 +1. 寻找一个初步的并发数, 执行测试, 观察记录以上指标值 +1. 调整并发数,观察记录指标值, 观察不同并发数导致的数据变化规律 +1. 依据以上多份数据找到并发数增长情况下 哪些指标值出现明显瓶颈,对症分析和优化 + +> 注意: +>- 首次确认的并发数可以通过预估 以及 历史项目的经验值 +>- 输入为 并发数 总请求量, 输出为观测指标值, 相同的输入情况需要多次测量降低误差 +>- 测试时需要考虑每次测试的环境差异( 数据库缓存, 应用缓存, CPU负载波动, ) + + +## 测试环境准备 +硬件配置信息: CPU,内存,硬盘,网络 +软件配置情况: 集群节点数, 集群策略, 缓存策略,网络拓扑 + +> Clickhouse 性能瓶颈 + +[Zookeeper在Clickhouse中的应用](https://zhuanlan.zhihu.com/p/366421463) +[ReplicatedMergeTree 引擎](https://developer.aliyun.com/article/762917) + +需要协调分布式 DDL 的执行(once语义) + - 单个节点A收到用户的分布式DDL指令 + - 节点A校验 分布式DDL 请求合法性, 在ZK 任务队列中创建Znode 上传 DDL LogEntry,节点下创建 finish和 active节点 + - 集群内所有节点消费 LogEntry队列, 将节点自身注册到active上,将处理结果写入finish节点 + - 节点A 轮询ZK检查 finish是否全部节点都完成. 以及active节点是否有超时现象. + +ReplicatedMergeTree表主备节点之间的状态同步 + - 重度依赖ZK + + + +CK架构是 每个节点都是平等的, 没有master worker的这种角色差异. 也导致了数据的协调和管理工作落在了CK上, +用户发出的指令可以落在任意的节点上执行, 默认命令都是单机执行,需要加 on cluster才会触发集群内所有节点执行对应的指令. + + +1. 数据工具体系项目 产品介入, 标准开发流程, 全局视图 + + diff --git a/Skills/Test/Cucumber.md b/Skills/Test/Cucumber.md index 95dba6b..ba7464d 100644 --- a/Skills/Test/Cucumber.md +++ b/Skills/Test/Cucumber.md @@ -1,7 +1,20 @@ -# Cucumber -> [official](https://docs.cucumber.io/) +--- +title: Cucumber +date: 2018-11-21 10:56:52 +tags: + - Cucumber + - BDD +categories: + - 测试 +--- + +**目录 start** -## For Java +1. [Cucumber](#cucumber) +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Cucumber +> [official](https://docs.cucumber.io/) -## For Groovy +BDD 测试框架 diff --git a/Skills/Test/Hamcrest.md b/Skills/Test/Hamcrest.md index e2e86b1..822876c 100644 --- a/Skills/Test/Hamcrest.md +++ b/Skills/Test/Hamcrest.md @@ -1,8 +1,17 @@ -`目录 start` - -- [Hamcrest](#hamcrest) +--- +title: Hamcrest +date: 2018-11-21 10:56:52 +tags: + - Hamcrest +categories: + - 测试 +--- -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +**目录 start** + +1. [Hamcrest](#hamcrest) + +**目录 end**|_2020-04-27 23:42_| **************************************** # Hamcrest > [官网](http://hamcrest.org/) | [开源中国介绍](https://www.oschina.net/p/hamcrest) diff --git a/Skills/Test/Junit.md b/Skills/Test/Junit.md deleted file mode 100644 index 39d6f40..0000000 --- a/Skills/Test/Junit.md +++ /dev/null @@ -1,211 +0,0 @@ -`目录 start` - -- [如何使用Junit](#如何使用junit) - - [在Maven项目中](#在maven项目中) - - [编码规范](#编码规范) - - [常用注解](#常用注解) - - [Rule注解的使用](#rule注解的使用) - - [断言的使用](#断言的使用) - - [assertThat](#assertthat) - - [参数化测试](#参数化测试) - - [测试套件](#测试套件) - - [分类测试](#分类测试) - -`目录 end` |_2018-09-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 如何使用Junit -> [Official doc: 4.12](https://github.com/junit-team/junit4/blob/master/doc/ReleaseNotes4.12.md) - -- Junit4已经停止更新了, 取而代之的是 Junit5 Jupiter, 但是Spring等众多框架仍使用Junit4 - -> 基本使用 -_JUnit_ -- 主要的三个特性: - - 用于测试预期结果和异常的断言, assertEquals() - - 设置和 _拆卸_ 通用测试数据的能力, @Before @After - - 运行测试套件的测试运行器 - -_一个基本的JUnit测试_ -- @Before 标记方法, 测试运行前准备测试数据 -- @After 标记方法, 测试运行完成后拆卸测试数据 -- @Test 测试方法 例如:预期的异常`@Test(expected=NullPointException.class)` - -## 在Maven项目中 -> [参考项目](https://github.com/zhuifengshen/Junit4Demo) - -> 添加依赖 -```xml - - junit - junit - 4.12 - test - -``` - -> 例如该项目结构 -``` -├── pom.xml -└── src - ├── main - │   └── java - │   └── com - │   └── github - │   └── kuangcp - │   └── Caculate.java - └── test - └── java - └── com - └── github - └── kuangcp - ├── AssertTest.java - └── CaculateTest.java -``` - -> 如果是Idea然后使用快捷键Ctrl Shift T即可自动创建测试类 - -## 编码规范 -- 手动创建则一般按照规范是: - 1. 包结构要和被测试类保持一致 - 2. 创建一个Java类, 命名为被测试类名字后加上Test - 3. 测试具体的方法: test加上方法名 - 4. 所有测试方法返回类型必须为void且无参数 - 5. 测试方法里一般使用断言进行测试, 更为直观 - -***************** -## 常用注解 -- [参考博客: JUnit4使用教程-快速入门](http://blog.csdn.net/chenleixing/article/details/44259453) | [参考博客: JUnit4单元测试入门教程](https://www.jianshu.com/p/7088822e21a3): - 1. @Test : 测试方法,测试程序会运行的方法,可设置参数 - - (expected=XXException.class) 期望该测试方法应该抛出某异常 - - (timeout=xxx) 限制该测试方法的执行时间, 超时视为失败 - - `注意被注解的方法 必须是 public 无参数 非静态 ` - 2. @Ignore : 被忽略的测试方法 - 3. @Before: 每一个测试方法之前运行 - 4. @After : 每一个测试方法之后运行 - 5. @BeforeClass: 所有测试开始之前运行, 在测试类还没有实例化就已经加载所以需要static修饰 - 6. @AfterClass: 所有测试结束之后运行, - -### Rule注解的使用 -> 也可以使用 `@Rule` 来规定测试类中所有测试方法 -```java - @Rule - public Timeout timeout = new Timeout(1000); -``` - -********************* -## 断言的使用 -> 使用 Hamcrest 工具能让断言更为简洁强大 - -1. 直接使用关键字 assert, 例如 `assert a == null` -2. 静态导入 `import static org.junit.Assert.*`, 使用其大量工具方法, 完整方法请查看源码 - - `assertNull(java.lang.Object object)` 检查对象是否为空 - - `assertNotNull(java.lang.Object object)` 检查对象是否不为空 - - `assertEquals(double expected, double actual, double delta)` 检查 指定精度 的double值是否相等 - - `assertNotEquals(double expected, double actual, double delta)` 检查 指定精度 的double值是否不相等 - - `assertFalse(boolean condition)` 检查条件是否为假 - - `assertTrue(boolean condition)` 检查条件是否为真 - - `assertSame(java.lang.Object expected, java.lang.Object actual)` 检查两个对象引用是否引用同一对象(即地址是否相等) - - `assertNotSame(java.lang.Object unexpected, java.lang.Object actual)` 检查两个对象引用是否不引用统一对象(即地址不等) - - `assertArrayEquals(Object[] a, Object[] b)` 检查两个数组是否相等 - - `assertThat(T, Matcher)` 检查泛型是否匹配, 以及一系列复杂的表达式 - - `fail(String string)` 依据入参并宣告测试失败 - - -```java -public class AssertTest { - @Test - public void testEquals(){ - String a = "hi"; - String b = "hi"; - // 使用assert关键字 - assert a.equals(b); - // 使用Assert类的静态工具方法 - assertEquals(a, b); - assert a == b; - assertSame(a, b); - // 因为trim 调用了SubString方法, 而这个方法是返回一个new的字符串 - String c = "h"+"i".trim(); - assertEquals(a, c); - assertSame(a, c); - } - @Test - public void testFail(){ - fail(); - fail("测试失败"); - } -} -``` -### assertThat -> [参考博客: assertThat详解](http://www.cnblogs.com/Firefly727/archive/2011/07/05/2098625.html) - - -********************************** -## 参数化测试 -> Junit 4 参数化测试 允许通过变化范围的参数值来测试方法 | 个人认为: 将测试方法的入参集合数据和测试行为分离开, 简化书写逻辑 - -1. 对测试类添加注解 `@RunWith(Parameterized.class)` -2. 将需要使用变化范围参数值测试的参数定义为私有变量; -3. 使用上一步骤声明的私有变量作为入参,创建构造函数; -4. 创建一个使用`@Parameterized.Parameters`注解的公共静态方法,它将需要测试的各种变量值通过集合的形式返回; -5. 使用定义的私有变量定义测试方法; - -```java -// 1 -@RunWith(Parameterized.class) -public class CaculateTest { - // 2 - private double numA; - private double numB; - // 3 - public CaculateTest(double numA, double numB) { - this.numA = numA; - this.numB = numB; - } - // 4 - @Parameterized.Parameters - public static Collection data(){ - Object[][] data = new Object[][]{ - {2, 4}, - {3, 5} - }; - return Arrays.asList(data); - } - // 5 - @Test - public void testAdd() throws Exception { - Caculate caculate = new Caculate(); - double result = caculate.add(numA, numB); - System.out.println("input "+numA+" + "+numB+" = "+result); - assert result != 0; - } - // 别的方法也是可以一样的使用, 而且所有的测试方法都受到了影响 都会迭代多次 - @Test - public void testDevide(){ - double result = caculate.devide(numA, 3); - System.out.println("input "+numA+" + "+3+" = "+result); - assert result != 0; - } -} -``` -> 最后执行testAdd 测试方法的结果是: 将data方法返回的数据迭代执行testAdd, - -## 测试套件 -> Junit 4允许通过使用测试套件类批量运行测试类 | 批量执行测试类, 组装为一个套件,一起执行 - -- 在当前测试类上加上如下注解: - - `@RunWith(Suite.class)` - - `@SuiteClasses(TestClass1.class, TestClass2.class)` -- 那么在执行当前测试的时候会依次执行注解中那些测试类 - -```java -@RunWith(Suite.class) -@Suite.SuiteClasses({AnnotationTest.class, EvenNumberCheckerTest.class}) -public class SuiteTest { -} -``` -_注意最好不要在该测试类中书写测试方法, 因为运行不了, 但是如果写了, 直接运行该测试类却又不会受影响_ - -## 分类测试 -> [参考博客](http://blog.csdn.net/wanghantong/article/details/28897103) | [JUnit4--- @Annotation注解总结](http://blog.csdn.net/neven7/article/details/42836413) - - diff --git a/Skills/Test/Junit5.md b/Skills/Test/Junit5.md deleted file mode 100644 index 66aeedf..0000000 --- a/Skills/Test/Junit5.md +++ /dev/null @@ -1,13 +0,0 @@ -`目录 start` - -- [如何使用JUnit5](#如何使用junit5) - -`目录 end` |_2018-09-12_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 如何使用JUnit5 -> [Official doc](http://junit.org/junit5/docs/current/user-guide/) - -> [参考博客](http://blog.csdn.net/bitgnu/article/details/78715836) -> [参考博客: JUnit 5 新特性](https://www.ibm.com/developerworks/cn/java/j-junit5/index.html) - -> [参考博客: JUnit5用户指南](http://junit5.doczh.cn/overview/) diff --git a/Skills/Test/Readme.md b/Skills/Test/Readme.md new file mode 100644 index 0000000..77e5486 --- /dev/null +++ b/Skills/Test/Readme.md @@ -0,0 +1,6 @@ +# 测试 + +- [testcontainers](https://testcontainers.com/)`将依赖组件运行在容器内,实现完备的集成测试` + +## 自动化测试平台 +- [gauge](https://github.com/getgauge/gauge) diff --git a/Skills/Test/TestNG.md b/Skills/Test/TestNG.md deleted file mode 100644 index 816ef08..0000000 --- a/Skills/Test/TestNG.md +++ /dev/null @@ -1,28 +0,0 @@ -`目录 start` - -- [TestNG](#testng) - - [使用](#使用) - - [基本注解](#基本注解) - -`目录 end` |_2018-08-23_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# TestNG -> [Official Doc](http://testng.org/doc/documentation-main.html) - -> [易百: TestNG教程](https://www.yiibai.com/testng/) -> [TestNG 入门教程](http://www.cnblogs.com/TankXiao/p/3888070.html) -> [testNG官方文档](http://testng.org/doc/index.html) | [Github:TestNG](https://github.com/cbeust/testng) -> [tools](http://toolsqa.com/selenium-webdriver/testng-introduction/) - -## 使用 -> 基本使用 -**Gradle使用** -```groovy -testCompile group: 'org.testng', name: 'testng', version: '6.14.3' -``` -然后和Junit使用是一致的, 在方法上打上 @Test 注解即可运行`注意Test注解的包为 import org.testng.annotations.Test;` - - -### 基本注解 -1. @Test - diff --git a/Skills/Test/TestTheory.md b/Skills/Test/TestTheory.md index 92a0b2f..0c22000 100644 --- a/Skills/Test/TestTheory.md +++ b/Skills/Test/TestTheory.md @@ -1,33 +1,70 @@ -`目录 start` - -- [测试](#测试) - - [为何要使用测试](#为何要使用测试) - - [基础知识](#基础知识) - - [测试替身](#测试替身) - - [虚设对象](#虚设对象) - - [存根对象](#存根对象) - - [伪装替身](#伪装替身) - - [模拟对象](#模拟对象) - - [测试类别](#测试类别) - - [单元测试](#单元测试) - - [【A/B测试】](#ab测试) - - [A/B测试的应用场景](#ab测试的应用场景) - - [元素/控件层面](#元素控件层面) - - [功能层面](#功能层面) - - [产品层面](#产品层面) - - [公司层面](#公司层面) - - [相关问题](#相关问题) - - [【冒烟测试】](#冒烟测试) - - [TDD 测试驱动开发](#tdd-测试驱动开发) - - [什么是TDD](#什么是tdd) - - [TDD实践](#tdd实践) - - [Mock](#mock) - - [BDD](#bdd) - -`目录 end` |_2018-08-29_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 测试基础理论 +date: 2018-12-20 10:25:07 +tags: + - 测试 + - 基础 +categories: + - 测试 +--- + +💠 + +- 1. [测试](#测试) + - 1.1. [为何要使用测试](#为何要使用测试) + - 1.2. [基础知识](#基础知识) + - 1.2.1. [测试替身](#测试替身) + - 1.2.1.1. [虚设对象](#虚设对象) + - 1.2.1.2. [存根对象](#存根对象) + - 1.2.1.3. [伪装替身](#伪装替身) + - 1.2.1.4. [模拟对象 Mock](#模拟对象-mock) + - 1.3. [软件测试方法](#软件测试方法) + - 1.3.1. [黑盒测试](#黑盒测试) + - 1.3.2. [白盒测试](#白盒测试) + - 1.3.3. [静态测试](#静态测试) + - 1.3.4. [动态测试](#动态测试) + - 1.3.5. [GUI测试](#gui测试) + - 1.4. [测试级别](#测试级别) + - 1.4.1. [单元测试](#单元测试) + - 1.4.2. [组件测试](#组件测试) + - 1.4.3. [集成测试](#集成测试) + - 1.4.4. [系统测试](#系统测试) + - 1.4.5. [Alpha测试](#alpha测试) + - 1.4.6. [Beta测试](#beta测试) + - 1.4.7. [验收测试](#验收测试) + - 1.5. [测试类型](#测试类型) + - 1.5.1. [安装测试](#安装测试) + - 1.5.2. [开发测试](#开发测试) + - 1.5.3. [可用性测试](#可用性测试) + - 1.5.4. [完整性测试](#完整性测试) + - 1.5.5. [回归测试](#回归测试) + - 1.5.6. [破坏性测试](#破坏性测试) + - 1.5.7. [恢复测试](#恢复测试) + - 1.5.8. [自动化测试](#自动化测试) + - 1.5.9. [兼容性测试](#兼容性测试) + - 1.5.10. [性能测试](#性能测试) + - 1.5.11. [安全测试](#安全测试) + - 1.5.12. [可访问性测试](#可访问性测试) + - 1.5.13. [国际化和本地化测试](#国际化和本地化测试) + - 1.5.14. [A/B测试](#ab测试) + - 1.5.14.1. [A/B测试的应用场景](#ab测试的应用场景) + - 1.5.14.1.1. [元素/控件层面](#元素控件层面) + - 1.5.14.1.2. [功能层面](#功能层面) + - 1.5.14.1.3. [产品层面](#产品层面) + - 1.5.14.1.4. [公司层面](#公司层面) + - 1.5.14.2. [相关问题](#相关问题) +- 2. [TDD 测试驱动开发](#tdd-测试驱动开发) + - 2.1. [什么是TDD](#什么是tdd) + - 2.2. [TDD实践](#tdd实践) + - 2.3. [BDD](#bdd) + +💠 2024-10-17 10:43:43 **************************************** # 测试 - > [参考博客: 测试的道理](http://www.yinwang.org/blog-cn/2016/09/14/tests) + +> [参考: 测试的道理](http://www.yinwang.org/blog-cn/2016/09/14/tests) +> [参考: Everything You Need to Know About Software Testing Methods](https://www.thebalancecareers.com/all-you-need-to-know-about-software-testing-methods-4019921) + ## 为何要使用测试 1. 帮助理解需求 - 单元测试应该反映Use Case,把被测单元当成黑盒测试其外部行为。 @@ -44,10 +81,13 @@ 1. 对设计的反馈 - 一个模块很难进行单元测试通常是不良设计的信号,单元测试可以反过来指导设计出高内聚、低耦合的模块。 +************************ + ## 基础知识 > [码农翻身: 张大胖和单元测试](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513524&idx=1&sn=8967771a4ac2a2b3b9bca019207e275a&chksm=80d67a77b7a1f36167ca66f9bdad810202d8790be1f15977dacd03348ee2b139f65ba58e4187&scene=21#wechat_redirect) -- [个人笔记: TDD 测试驱动开发](/Java/AdvancedLearning/ProgramThinking.md#tdd-测试驱动开发) +- [个人笔记: TDD 测试驱动开发](/Java/AdvancedLearning/ProgramThinking.md#tdd-测试驱动开发) +> [参考: Unit testing code with a file system dependency](https://stackoverflow.com/questions/129036/unit-testing-code-with-a-file-system-dependency)`如何编写脱离外部依赖的真正单元测试` ### 测试替身 > 泛指任何出于测试目的的替换真实对象的假冒对象,为了解决测试代码的依赖项问题。 @@ -67,7 +107,7 @@ - 伪装替身,可以看作是存根的升级。他做的工作几乎是和生产代码是一样的,单位了满足测试需求也会使用便捷的方式 - 例如 内存数据库HSQLDB 的使用, -#### 模拟对象 +#### 模拟对象 Mock - 存根对象的调用通常会返回形同的结果。所以不能模拟任何与状态有关的行为,模拟对象就能够更好的胜任 - 在准备要用的模拟对象时,告诉他会有哪些调用,以及对应的相应,模拟会和DI结合更好。可以用一个虚拟的对象,这个对象完全按照已知方式行动 @@ -75,21 +115,117 @@ - 调用mock()方法创建模拟对象,并将模拟目标类型的class对象作为参数传进去。 - 然后要把模拟对象需要的行为记录下来,通过 when方法表明要记录哪些方法的行为,然后用thenReturn方法指定期望结果。 -## 测试类别 -- [ ] 单元测试, 集成测试 +> [参考: 面向开发的测试技术(一):Mock ](http://www.uml.org.cn/Test/201708143.asp?artid=19745) -### 单元测试 +既然使用模拟技术, 就要让模块的设计更易于测试, 也就是耦合更低, 之间的依赖更少, 一个正向循环呢 + +1. Mockito [Official Site](https://site.mockito.org/) 该框架有多种语言的实现 Java Python ... +1. Easy Mock [Official Site](http://easymock.org/) + +************************************** +## 软件测试方法 +以下是用于判断产品行为和性能的不同方法。黑盒子和白盒测试是两种基本方法。 + + +### 黑盒测试 +> 也称为功能或基于规范的测试,此方法侧重于输出。测试人员不关心内部机制。他们只检查软件是否符合预期。编码知识不是必需的,测试人员在用户界面级别工作。 + +### 白盒测试 +> 此方法使用编码技术作为测试过程的一部分。当产品发生故障时,测试人员会根据需要深入到代码中以找到原因。软件开发人员自己这样做,因为他们确定产品应该如何工作。基于结构和玻璃盒测试是此方法的其他名称。 + +### 静态测试 +> 测试人员检查软件的代码和文档,但不执行程序。在验证过程中,静态测试在产品开发的早期开始。 + +### 动态测试 +> 软件使用各种输入执行,测试人员使用此方法将输出与预期行为进行比较。 +### GUI测试 +> 测试GUI特性 - 文本格式,文本框,按钮,列表,布局,颜色,字体,字体大小等。GUI测试非常耗时,第三方公司经常承担这项任务而不是开发人员。 + +## 测试级别 +这些对于识别软件开发生命周期的每个阶段中的弱点和重叠区域是必要的。 + +### 单元测试 +> 开发人员测试代码的最基本部分,如类,接口和函数/过程。他们知道他们的代码应该如何响应,并可以根据输出进行调整。 - 3A 原则: - Arrange: 初始化测试对象或者准备测试数据 - Act : 调用被测方法 - Assert: 断言 + +### 组件测试 +> 其他名称是模块或程序测试。它与单元测试类似,但包含更高级别的集成。对软件模块进行缺陷测试,以验证其各自的功能。 + +### 集成测试 +> 这可以在集成模块时识别错误。不同的集成测试是自下而上,自顶向下和功能增量。 + +> [The correct way to use integration tests in your build process](https://zeroturnaround.com/rebellabs/the-correct-way-to-use-integration-tests-in-your-build-process/) + +### 系统测试 +> 使用此方法在不同环境中对项目的组件进行整体测试。它属于黑盒方法,是该过程中的最终测试之一。它确定系统是否能够满足业务和用户需求。 + +### Alpha测试 +> 内部员工在模拟或实际环境中在开发人员的站点测试软件。之后,开发人员纠正错误和其他问题。 + +### Beta测试 +> 也称为现场测试,客户端在实际条件下在自己的站点上测试产品。客户可以为一组最终用户提供通过预发布版或测试版测试软件的机会。然后将关于可能改进的反馈发送给开发人员。 + +### 验收测试 +> 在黑盒测试的范围内,客户端测试软件以确定开发人员是否已根据所需规范创建程序。 + +## 测试类型 +> 这些软件测试侧重于特定目标。 + +### 安装测试 +> 软件测试工程师和配置管理器进行此测试以确保最终用户可以安装和运行该程序。它涵盖了安装文件,安装位置和管理权限等区域。 + +### 开发测试 +> 这实现了一系列同步策略来检测和防止缺陷。它包括静态代码分析,同行代码审查,可追溯性和指标分析。目的是降低风险并节省成本。 + +### 可用性测试 +> 通过此测试,用户体验受到关注。它衡量GUI的设计和易用性。该测试检查功能的准确性和效率以及测试对象的情绪反应。 -### 【A/B测试】 +### 完整性测试 +> 这表明软件是否值得花时间和成本继续进一步测试。太多的缺陷和更激进的测试不遵循。 + +### 回归测试 +> 当系统进行修改时,回归测试会监控意外行为。它指出了对模块或组件的不利影响。 + +> [wiki: regression testing](https://en.wikipedia.org/wiki/Regression_testing) + +### 破坏性测试 +> 测试人员输入异常条目并识别软件管理意外输入的能力。这向开发人员展示了程序在错误管理方面的稳健性。 + +### 恢复测试 +> 当硬件或其他功能出现故障时,此测试显示软件恢复和继续运行的程度。 + +### 自动化测试 +> 这会执行难以手动实现的功能。它使用特定的软件来运行测试并提供实际与预期结果的数据。 + +> [Aqua: The IDE for test automation](https://www.jetbrains.com/aqua/) + +### 兼容性测试 +> 软件必须在不同的计算环境中运行,因此这将检查与不同系统的兼容性。例如,该软件是否适用于各种操作系统和Web浏览器? + +### 性能测试 +> 这是一项深入测试,用于检查不同场景下的软件性能。收集有关响应性,稳定性,资源分配和速度的信息。此外,诸如体积,容量和尖峰测试等子测试在此过程中起作用。 + +### 安全测试 +> 衡量软件保护用户安全的能力。这意味着授权功能,身份验证,机密性,完整性,可用性和不可否认性。 + +### 可访问性测试 +> 这与可用性测试不同。这决定了包含学习和身体残疾的不同能力的用户可以使用该软件的程度。 + +### 国际化和本地化测试 +> 结果显示软件如何适应不同的语言和区域需求。这包括为特定位置添加组件和翻译文本。 + +### A/B测试 > 对照实验,也叫随机实验和 A/B测试 [参考来源:知乎问题](https://www.zhihu.com/question/20045543) | [相关三方平台](http://www.appadhoc.com/) ->A/B测试其实是一种“先验”的实验体系,属于预测型结论,与“后验”的归纳性结论差别巨大。A/B测试的目的在于通过科学的实验设计、采样样本代表性、流量分割与小流量测试等方式来获得具有代表性的实验结论,并确信该结论在推广到全部流量可信。 +> [参考 ab-testing](https://www.optimizely.com/optimization-glossary/ab-testing/) + +> A/B测试其实是一种“先验”的实验体系,属于预测型结论,与“后验”的归纳性结论差别巨大。A/B测试的目的在于通过科学的实验设计、采样样本代表性、流量分割与小流量测试等方式来获得具有代表性的实验结论,并确信该结论在推广到全部流量可信。 + 说句题外话,大量的大数据公司都在尝试通过“后验”结论进行未来行为预测,个人觉得然并卵,主要是因为数据不全、脏数据、随机事件、建模人为因素等等影响,方向无比正确,现实无比残酷 #### A/B测试的应用场景 @@ -101,33 +237,30 @@ ##### 元素/控件层面 灰度发布 和 A/B测试:重要页面的修改和流程上的调优,通过灰度发布到1%或者5%的用户,看其实际对用户的数据影响(访问时间增加、留存提高、下单率提高等),决定此修改到底是100%发布还是被砍掉 -Google: -每个月从上百个A/B测试中找到十几个有效方案,月营收提升2%左右,10亿美元的规模 -广告位左移一个像素带来X%的增收,左移两个像素带来Y%的亏损 -任何产品改动需要A/B测试才能上线 +Google: +- 每个月从上百个A/B测试中找到十几个有效方案,月营收提升2%左右,10亿美元的规模 +- 广告位左移一个像素带来X%的增收,左移两个像素带来Y%的亏损 +- 任何产品改动需要A/B测试才能上线 Facebook: -6个月内所有版本完全线上灰度发布,通过不断进行用户流量分割的方式进行实验,获得无Bug口碑 +- 6个月内所有版本完全线上灰度发布,通过不断进行用户流量分割的方式进行实验,获得无Bug口碑 -> 灰度发布就是,先把一部分线上的业务分流到新系统上, 然后看情况, 再判断是否全部上线 +> 灰度发布: 先把一部分线上的业务分流到新系统上, 然后看运营情况, 再判断是否全部上线 ##### 功能层面 -无论是推荐算法还是定价策略 -为了简单理解,说个定价策略,如上图。 -一个价格包含这个几个因素: +无论是推荐算法还是定价策略 为了简单理解 一个价格包含这个几个因素: + >1.价格区间: 用我最朴素的理解,人类是喜欢折扣的不理性动物:人们明显更乐意花45折买一个价值900块钱的东西而不是花67折买一个价值600块的东西,尽管东西一样,最终价格一样都是400块。 所以你看电商广告都是打折配合几个垫背的低价来卖。。。 >2.价格精度: -以前去超市经常能发现2.99元或者8.99,现在都变成2.32或者4.23,这是弄啥嘞? -这里面太多心理学与营销的东西就不说了,在某些情况下,即使几分钱的价格变化对用户转化的影响是巨大的,比如一个东西原来卖400元,那现在改成399还是401可能对总营收的影响并不巨大,但是配合用户转化率的变化,可能营收的差异就天差地别了。 +在某些情况下,即使几分钱的价格变化对用户转化的影响是巨大的,比如一个东西原来卖400元,那改成399还是401可能对当下总营收的影响不明显,但是配合用户转化率的变化,营收的差异就天差地别了。 >3.价格周期: 伴随着产品迭代、促销等等因素影响,什么时候降价是对自己最有利的策略,完全可以A/B测试来解决 ##### 产品层面 -A/B测试在产品层面的应用主要是通过“灰度发布”来实现的。 -就目前移动端的产品来说,iOS的应用商店审核期是个大大大坑,任何BUG打补丁还得再来一遍,也就意味着补丁的审核期内用户带着BUG使用,这个太致命了,用户的获取成本高的吓人,因为这个流失太不值得了,基于A/B测试的灰度发布更重要的不是优化,而是保护性发布,先通过小流量的实际用户测试,有BUG或者新版本体验不好,可以立即回滚到老版本,简单有效。 +A/B测试在产品层面的应用主要是通过“灰度发布”来实现的。 就目前移动端的产品来说,iOS的应用商店审核期是个大大大坑,任何BUG打补丁还得再来一遍,也就意味着补丁的审核期内用户带着BUG使用,这个太致命了,用户的获取成本高的吓人,因为这个流失太不值得了,基于A/B测试的灰度发布更重要的不是优化,而是保护性发布,先通过小流量的实际用户测试,有BUG或者新版本体验不好,可以立即回滚到老版本,简单有效。 ##### 公司层面 A/B测试其实也是谷歌管理方法论,具体文章请参考: @@ -145,19 +278,16 @@ A/B测试其实也是谷歌管理方法论,具体文章请参考: > 相关专栏: [[A/B]那些年,我们追过的AB Testing (一)](https://zhuanlan.zhihu.com/milmax/20394728) | [[A/B]那些年,我们追过的AB Testing (二)从“People you may know”到growth hacking](https://zhuanlan.zhihu.com/milmax/20507473) -**************************** -### 【冒烟测试】 -> 冒烟测试源自硬件行业,对一个硬件或者硬件组件改动后,直接给设备加电,看看设备会不会冒烟,没冒烟,就表示待测组件是通过了测试。 -> 目的是确认软件基本功能正常,可以进行后续的正式测试工作。 - *************************** -## TDD 测试驱动开发 +# TDD 测试驱动开发 > Java程序员修炼之道 测试驱动开发章节 ![p276.jpg](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Java7Developer/p276.jpg) +[辩证看待QA人员和Unittest](https://mp.weixin.qq.com/s/EFMhFazZGVAoKkfwegm_TQ) + `TDD带来的好处` - 更清晰的代码 只写需要的代码 - 更好的设计 有些开发人员管TDD叫测试驱动的设计 @@ -186,7 +316,7 @@ A/B测试其实也是谷歌管理方法论,具体文章请参考: ![p283.jpg](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Book/Java7Developer/p283.jpg) - 应该尽可能的遵守单个测试循环的开发模型,不要同时开多个测试循环,一堆红色 -### 什么是TDD +## 什么是TDD > [百度百科词条](http://baike.baidu.com/item/TDD/9064369), 简单来讲就是红绿循环, 红 绿 红... > TDD是测试驱动开发(Test-Driven Development)的英文简称,是敏捷开发中的一项核心实践和技术,也是一种设计方法论。 > TDD的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么产品代码。TDD虽是敏捷方法的核心实践,但不只适用于XP(Extreme Programming),同样可以适用于其他开发方法和过程。 @@ -206,21 +336,8 @@ A/B测试其实也是谷歌管理方法论,具体文章请参考: 输入非数值类型,比如None、[]、{},期待抛出TypeError。 把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。 -### TDD实践 -[java测试驱动开发(TDD)之《井字游戏》 ](https://my.oschina.net/bysu/blog/1635549) - - - -**************************** -## Mock -> [参考博客: 面向开发的测试技术(一):Mock ](http://www.uml.org.cn/Test/201708143.asp?artid=19745) - -既然使用模拟技术, 就要让模块的设计更易于测试, 也就是耦合更低, 之间的依赖更少, 一个正向循环呢 - -1. Mockito [Official Site](https://site.mockito.org/) 该框架有多种语言的实现 Java Python ... -1. Easy Mock [Official Site](http://easymock.org/) - -******************** +## TDD实践 +[Java测试驱动开发(TDD)之《井字游戏》 ](https://my.oschina.net/bysu/blog/1635549) ## BDD > 行为驱动开发 behavior-driven development 作为TDD的一个变种 diff --git a/Skills/Vcs/GitAction.md b/Skills/Vcs/GitAction.md new file mode 100644 index 0000000..adc9d85 --- /dev/null +++ b/Skills/Vcs/GitAction.md @@ -0,0 +1,300 @@ +--- +title: Git实践技巧 +date: 2018-11-21 10:56:52 +tags: +categories: + - 版本控制 +--- + +💠 + +- 1. [GitInAction](#gitinaction) + - 1.1. [安装](#安装) + - 1.1.1. [Linux(debian系)](#linuxdebian系) + - 1.1.2. [Windows](#windows) + - 1.2. [配置记住密码](#配置记住密码) + - 1.3. [配置GPG签名](#配置gpg签名) + - 1.4. [简单使用](#简单使用) + - 1.4.1. [配置GPG](#配置gpg) + - 1.4.2. [码云](#码云) + - 1.5. [git初始化配置](#git初始化配置) + - 1.5.1. [终端中显示当前分支](#终端中显示当前分支) + - 1.5.2. [命令的自动补全](#命令的自动补全) + - 1.6. [Git服务器](#git服务器) + - 1.6.1. [使用 git daemon 搭建简易 Server](#使用-git-daemon-搭建简易-server) + - 1.6.2. [HTTP 方式的 Git 服务器](#http-方式的-git-服务器) + - 1.6.2.1. [配置HTTPS](#配置https) + - 1.6.2.2. [使用SSH登录GitServer](#使用ssh登录gitserver) + - 1.7. [Tips](#tips) + - 1.7.1. [清理仓库大文件](#清理仓库大文件) + - 1.7.2. [CRLF与LF](#crlf与lf) + - 1.7.3. [仓库统计](#仓库统计) + +💠 2024-01-23 19:08:38 +**************************************** +# GitInAction +> [try git](https://try.github.io/) + +> [Github: lazygit](https://github.com/jesseduffield/lazygit)`命令行的简易图形化` +> [Github: Git History](https://githistory.xyz/) + +## 安装 +### Linux(debian系) +- `sudo apt-get install git` + +> 安装最新版本Git +- `sudo add-apt-repository ppa:git-core/ppa` + - 如果命令找不到就先安装这个 `sudo apt-get install software-properties-common` +- `sudo apt update` +- `sudo apt install git` + +- 从源码安装 [Github:git](https://github.com/git/git) + - make prefix=/usr/local all + - sudo makeprefix=/usr/local install + +- 安装文档(可选): + - sudo apt-get installasciidoc + - make prefix=/usr/local docinfo + - sudo makeprefix=/usr/local install + +- 卸载 + - sudo find /usr/local -depth -iname 'git*' -exec rm -rf {} \; + +### Windows +- [git-for-windows 淘宝镜像源](https://npm.taobao.org/mirrors/git-for-windows/) + +******************* + +## 配置记住密码 +- `Windows下记住密码` : + * 新建环境变量 HOME 值:`%USERPROFILE%` + * 在C盘User下你的当前用户目录下新建` _netrc `文本文件: + * `machine https://github.com/Kuangcp/` + * `login ***` + * `password ***` + * 成功配置,测试便知 + +- `Linux下记住密码` + - 这种情况下一个域名只能使用一个账号 + - `git config --global credential.helper store` + - 那么下一次输入账号和密码就会被持久化保存, 后续无需输入 + +- `ssh 方法:(推荐)` + - `ssh-keygen` 不设置密码 + - `cat ~/.ssh/id_rsa.pub | xclip -sel clip` 添加即可 + +## 配置GPG签名 +> [Github Doc](https://docs.github.com/en/github/authenticating-to-github/managing-commit-signature-verification) + +- [Git error - gpg failed to sign data](https://stackoverflow.com/questions/41052538/git-error-gpg-failed-to-sign-data) + - `git config --global user.signingkey 指纹` + +因为Github等代码托管网站通常是使用 commit 信息里的邮箱来标记提交者的,但是这个信息是可以任意填的,这个时候就需要GPG签名来对该次提交签名,确认是本人提交 + +************************ + +## 简单使用 + +*Github下拉到eclipse* +- 1.在GitHub上新建一个项目,不勾选初始化,复制下URL +- 2.在eclipse新建项目然后在eclipse里添加git remote +- 3.commit -> push 完成 +- 4.打开Git Bash 使用命令行再查看一下 + +*本地已有代码关联远程空仓库* +- 先在远程建立空仓库 一般各大平台也都有命令提示 + - 传送门: [Gitee](https://gitee.com) | [Github](https://github.com/) | [Bitbucket](https://bitbucket.org/) | [GitLab](https://gitlab.com/) ... +```sh + git remote add origin https://github.com/Kuangcp/StudentManager.git + git push -u origin master +``` +- 说明下上面的命令 第一条是设置了一个远程仓库 仓库名为origin URL是后面那个,一般默认的远程仓库名都叫origin + - 名字可以随便取 但是提交就要标明仓库名了,而且分支也是一样的默认是master可以自己加别的分支. `git push -u 随便 随意` + +*建立本地空仓库并关联到远程仓库* +- 1.先在GitHub上创建一个仓库,不勾选README(不然添加远程仓库还得pull一下README文件才能push) +- 如果本地没有则 `mkdir 库名 `创建一个文件夹,最好和远程的库同名 +- 2.在某本地项目根目录下运行 `Git Bash` + - 2.1 `git init` 初始化(建立 `.git` 目录) + - 2.2 `touch README.md` + - 2.3 `git remote add origin master URL` 连上远程仓库 + - 2.4 `git push -u origin master` 输入用户名,密码 (若因为没有上游节点就按提示输入命令建立初始节点即可 git push --setupstream origin master) + - 原因是没有指定本地dev分支与远程origin/dev分支的链接,根据提示,设置dev和origin/dev的链接:`git branch --set-upstream dev origin/dev` master同理 + +### 配置GPG +> [阮一峰:GPG入门教程](http://www.ruanyifeng.com/blog/2013/07/gpg.html) + +- 能够提高安全性,但是麻烦,不过向来这两者就是不可兼得的. + +### 码云 +> [帮助文档](http://git.mydoc.io/) + +- [如何进行减少提交历史数量以及修改自己的commit中的邮箱](http://git.mydoc.io/?t=83152) +- [改写历史,永久删除git库的物理文件 ](https://my.oschina.net/jfinal/blog/215624?fromerr=ZTZ6c38X) +******************************** +## git初始化配置 +```sh + git config --global user.name " " + git config --global user.email " " + git config --global color.ui auto +``` +> 如果是多个账号使用同一台电脑就不要配置这个,单独配置每个仓库下的用户名,邮箱即可 +> `git config user.name ""` + +******************** +### 终端中显示当前分支 +> 使用 .git-prompt.sh 在Bash下显示当前分支 Windows环境不用看,安装的Git-for-windows软件已经会显示分支名了 + +- `wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh -O ~/.git-prompt.sh` 下载脚本 +- `chmod +x ~/.git-prompt.sh` 赋予可执行权限 +> 在 .bash_alases文件中添加 +```conf + lightgreen='\[\033[1;32m\]' + lightcyan='\[\033[1;36m\]' + lightpurple='\[\033[1;35m\]' + yellow='\[\033[1;33m\]' + nocolor='\[\033[0m\]' + source ~/.git-prompt.sh + set_bash_prompt(){ + #PS1="[e[32m]u[e[m]@[e[33m]W[e[36m]$(__git_ps1 ' (%s)')[e[31m]$[e[m]" + PS1="${lightcyan}\t${lightgreen}\w${lightpurple}$(__git_ps1 ' (%s)')${yellow} → \[\e[m\]" + } + PROMPT_COMMAND="set_bash_prompt; $PROMPT_COMMAND" +``` + +如果使用 zsh 加上 oh-my-zsh 这就是换个主题的事 下面的自动补全也是默认就有 + +******************** +### 命令的自动补全 +> [git自动补全脚本GitHub地址](https://github.com/git/git/tree/master/contrib/completion) + +- 下载脚本 `wget https://raw.githubusercontent.com/git/git/master/contrib/completion/git-completion.bash -o ~/.git-completion.bash` + - 在 .bashrc 或者 .bash_aliases 中添加 source ~/.git-completion.bash + - 重启终端或者 `source .bashrc`即可 +- 双击tab可以得到命令建议 + +*************************************************** +## Git服务器 +### 使用 git daemon 搭建简易 Server + +*`目录结构`* +``` + root + ├── a + │   └── .git + └── b + └── .git +``` +> 也就是说在仓库目录的父级目录 root 作为基础目录 (base-path) + +- `git daemon --export-all --base-path=$(pwd) --port=8080` 在 当前目录 启动一个Git守护进程 + - `--enable=receive-pack` 为了安全,默认是仓库不能被修改, 添加这个参数就可以push了 + - `--export-all` 开放当前目录下所有项目 + - `--base-path=''` 指定开放的基本目录(指定开放别的路径) + - `--port=8080` 指定开放的端口 + - `--verbose` 启动看到的日志信息更多 + +- 直接克隆 `git clone git://localhost:8080/a` +- 或者作为已有代码的远程 `git remote add hub git://localhost:8080/a` + - 然后 git fetch 对应的分支就可以达到将某个分支直接给对方的目的 + +### HTTP 方式的 Git 服务器 +- 安装Apache: Web服务器 +- 配置Apache服务器的开放的目录以及Git的路径 +```xml + + AuthType Basic + AuthName "GIT Repository" + AuthUserFile "/home/mythos/GitRemoteRepo/htpassed" + Require valid-user + +``` +- 切换到Apache的bin目录下:`htpasswd -cmb /home/mythos/GitRemoteRepo/htpsswd 账号名 密码` +- 到仓库目录下 `git init --bare 程序项目名称` +- `git clone http://localhost/git/程序项目名称` 输入用户名密码即可 + +#### 配置HTTPS +- 切换到Apache主目录下执行 + 1. `bin\openssl genrsa -des3 -out server.key 2048 -config conf\openssl.cnf` 输入密码 + 1. `bin\openssl req -new -key server.key -out server.csr -config conf\openssl.cnf` 输入之前密码 + 1. `bin\openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt` 输入之前密码 + +- 把server.key 更名为server.key.old :`bin\openssl rsa -in server.key.old -out server.key` +- 将server.key server.crt 移动到conf +- 修改 httpd.conf 去掉如下三行的注释 # 字符 + +``` + LoadModule socache_shmcb_module.. + LoadModule ssl_module.. + Include conf/extra... +``` +- 因为是自己建立的SSL证书 所以要去掉SSL验证 `git -c http.sslVerify=false clone URL ` +- 或者写入配置文件 `git config http.sslVerify false` + +#### 使用SSH登录GitServer + +****************** + +## Tips +1. 虽然在物理上本地仓库中所有文件是放在一起的,但是分支之间是互不能访问以及操作的 +2. 在本地的每次commit都是有index的,上传到github可以不用那么频繁,反正都是有记录的 +3. 出现了冲突,从而无法自动merge: +``` + git pull 对方的分支 + git checkout 自己的分支 + git merge --no-ff 对方的分支 + git push (自己的源+分支)origin master +``` +4. 切记:避免隐私的配置文件上传github时,将配置分离出来配置.gitignore中忽略掉配置文件,然后建立模板文件夹放待配置的文件即可 + - `大意的后果`:[程序员复仇记 | 这些年,GitHub 上泄露了些什么?](https://zhuanlan.zhihu.com/p/33424997) + - [不小心把密码上传到 GitHub 了,怎么办](https://www.bennythink.com/git-password.html) +5. `cat ~/.ssh/id_rsa.pub | xclip -sel clip` 复制公钥 +6. Linux下当大量文件出现mode的变化(因为你的目录移动,文件权限变化等影响的)可以设置忽略掉 `git config core.fileMode false` + * 当将目录备份出去,然后重装系统粘贴回来,权限就变了,mode也变了,可以设置忽略掉改变 +7. git status 中文文件名乱码, 执行 `git config --global core.quotepath false`即可 + +- `git ls-files` 列出文件列表 + - `git ls-files | xargs wc -l` 计算文件中程序代码行数 通过工具:`xargs` `wc` (中文命名的文件编码问题无法计算行数) + - `git ls-files | xargs cat | wc -l` 计算行数总和 + +- [API: github开发接口](https://developer.github.com/v3/) +- [Github: Search 技巧](https://docs.github.com/en/github/searching-for-information-on-github/searching-on-github) + +### 清理仓库大文件 +- [official:7.6 Git 工具 - 重写历史](https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E5%86%99%E5%8E%86%E5%8F%B2) + +> [Tool: bfg-cleaner](https://rtyley.github.io/bfg-repo-cleaner/) + +> [参考博客 从git中永久删除文件以节省空间](http://blog.csdn.net/meteor1113/article/details/4407209) | +> [参考博客4 减小磁盘占用](http://zhongmingmao.me/2017/04/19/git-reduce/) +> [删除仓库的某个时间点之前的历史记录,减少.git 目录大小](https://www.v2ex.com/t/297802) +> [如何清洗 Git Repo 代码仓库](http://www.open-open.com/lib/view/open1414632626075.html) + +> [参考: 寻找并删除Git记录中的大文件](https://www.tuicool.com/articles/vAVVZrA) +1. 找出大文件 `git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -10 | awk '{print$1}')"` +1. 删除文件, 重写提交 `git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch 文件的路径' --prune-empty --tag-name-filter cat -- --all` +1. 强制推送 `git push origin --force --all` + - `git push origin --force --tags` +1. 使用`git pull rebase`来更新分支,而不是 `git merge` 不然大文件又从别的分支回来了 + +> 要注意, 所有的分支都必须 pull rebase , 只要还有一个人留有对大文件的引用, 大文件就一直在仓库 + +### CRLF与LF +> 由于系统的不同 Windows是 CRLF *nix 是 LF Mac 是 CR | [wiki: CRLF](https://en.wikipedia.org/wiki/Newline) + +Git提供了一个“换行符自动转换”功能。这个功能默认处于“自动模式”,当你在签出文件时,它试图将 UNIX 换行符(LF)替换为 Windows 的换行符(CRLF); +当你在提交文件时,它又试图将 CRLF 替换为 LF。Git 的“换行符自动转换”功能听起来似乎很智能、很贴心,因为它试图一方面保持仓库内文件的一致性(UNIX 风格),一方面又保证本地文件的兼容性(Windows 风格)。但遗憾的是,这个功能是有 bug 的 + +```sh + git config --global core.autocrlf false + git config --global core.safecrlf true +``` + +> [参考: CRLF和LF](https://www.tuicool.com/articles/IJjQVb) +> [参考: git 换行符LF与CRLF转换问题](https://www.cnblogs.com/sdgf/p/6237847.html) + +>1. CRLF -> LF `sed -i 's/\r//g' file` 配合git 就是 `git ls-files| sed -i 's/\r//g' ` + +### 仓库统计 +> [https://github.com/hoxu/gitstats](https://github.com/hoxu/gitstats) +> gogitstats \ No newline at end of file diff --git a/Skills/Vcs/GitAdvance.md b/Skills/Vcs/GitAdvance.md new file mode 100644 index 0000000..55d9705 --- /dev/null +++ b/Skills/Vcs/GitAdvance.md @@ -0,0 +1,34 @@ +--- +title: Git深入学习 +date: 2018-11-21 10:56:52 +tags: + - Advanced +categories: + - 版本控制 +--- + +**目录 start** + +1. [Git Advance](#git-advance) + 1. [版本控制系统(VCS)](#版本控制系统vcs) +1. [Git实现原理](#git实现原理) + +**目录 end**|_2020-07-05 14:58_| +**************************************** +# Git Advance + +## 版本控制系统(VCS) +- [码农翻身:小李的版本管理系统](https://mp.weixin.qq.com/s?__biz=MzAxOTc0NzExNg==&mid=2665513204&idx=1&sn=c4c493d771a167a84ace01c3e016417e&scene=21#wechat_redirect) + +- [SVN](https://subversion.apache.org/) + - [submin](https://supermind.nl/submin/)`SVN管理的Web界面` +- [git](https://git-scm.com/) `最好用的vcs` + +_自建Git服务_ +- [gogs](https://github.com/gogits/gogs) `自建git服务器` +- [gitea](https://github.com/go-gitea/gitea) `gogs加强` + - [docker安装](https://docs.gitea.io/zh-cn/install-with-docker/) + +********************* + +# Git实现原理 diff --git a/Skills/Vcs/GitBase.md b/Skills/Vcs/GitBase.md new file mode 100644 index 0000000..d7ccd99 --- /dev/null +++ b/Skills/Vcs/GitBase.md @@ -0,0 +1,1036 @@ +--- +title: GitBase +date: 2024-01-04 10:45:28 +tags: +categories: +--- + +💠 + +- 1. [Git基础](#git基础) +- 2. [开源许可证](#开源许可证) +- 3. [基本命令](#基本命令) + - 3.1. [config](#config) + - 3.2. [clone](#clone) + - 3.2.1. [Shallow Clone](#shallow-clone) + - 3.2.2. [sparse checkout 稀疏检出](#sparse-checkout-稀疏检出) + - 3.3. [add](#add) + - 3.4. [rm](#rm) + - 3.5. [status](#status) + - 3.6. [commit](#commit) + - 3.7. [restore](#restore) + - 3.8. [revert](#revert) + - 3.9. [show](#show) + - 3.10. [log](#log) + - 3.10.1. [对比两个分支的差异](#对比两个分支的差异) + - 3.10.2. [查看文件的修改记录](#查看文件的修改记录) + - 3.10.3. [全分支搜索字符串](#全分支搜索字符串) + - 3.10.4. [查看目录或文件修改频次](#查看目录或文件修改频次) + - 3.11. [blame](#blame) + - 3.12. [diff](#diff) + - 3.12.1. [diff 创建 patch](#diff-创建-patch) + - 3.13. [apply](#apply) + - 3.14. [format-patch](#format-patch) + - 3.15. [am](#am) + - 3.16. [tag](#tag) + - 3.17. [notes](#notes) + - 3.18. [reset](#reset) + - 3.18.1. [回滚add操作](#回滚add操作) + - 3.18.2. [回滚最近一次commit](#回滚最近一次commit) + - 3.18.3. [回滚最近几次的commit并添加到一个新建的分支上去](#回滚最近几次的commit并添加到一个新建的分支上去) + - 3.18.4. [回滚merge和pull操作](#回滚merge和pull操作) + - 3.18.5. [在index已有修改的状态回滚merge或者pull](#在index已有修改的状态回滚merge或者pull) + - 3.18.6. [被中断的工作流程](#被中断的工作流程) + - 3.19. [gc](#gc) + - 3.20. [clean](#clean) +- 4. [本地分支](#本地分支) + - 4.1. [show-branch](#show-branch) + - 4.2. [stash](#stash) + - 4.2.1. [stash 创建 patch](#stash-创建-patch) + - 4.2.2. [恢复被drop的stash](#恢复被drop的stash) + - 4.3. [branch](#branch) + - 4.4. [checkout](#checkout) + - 4.5. [分支合并](#分支合并) + - 4.5.1. [分支问题排查](#分支问题排查) + - 4.6. [merge](#merge) + - 4.7. [rebase](#rebase) + - 4.8. [cherry-pick](#cherry-pick) + - 4.9. [bisect](#bisect) + - 4.10. [worktree](#worktree) +- 5. [远程操作](#远程操作) + - 5.1. [remote](#remote) + - 5.2. [push](#push) + - 5.3. [fetch](#fetch) + - 5.4. [pull](#pull) +- 6. [Submodule](#submodule) +- 7. [其他](#其他) + - 7.1. [gitk](#gitk) + - 7.2. [grep](#grep) + - 7.3. [archive](#archive) + - 7.4. [reflog](#reflog) + - 7.5. [rev-parse](#rev-parse) + - 7.6. [scalar](#scalar) + - 7.7. [githooks](#githooks) +- 8. [配置文件](#配置文件) + - 8.1. [gitignore](#gitignore) + - 8.2. [gitattributes](#gitattributes) +- 9. [自定义插件](#自定义插件) + +💠 2024-10-12 11:35:37 +**************************************** + +# Git基础 +> Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. -- [git-scm.com](https://git-scm.com/) + +> [Official Doc: git](https://git-scm.com/docs) | [Github:git](https://github.com/git/git) | [Arch Wiki: Git](https://wiki.archlinux.org/index.php/Git) | [Gitee: about git](https://gitee.com/all-about-git) | [git-for-windows 安装包镜像源](https://npm.taobao.org/mirrors/git-for-windows/) + +- index stage work 三个逻辑分区 + - index: 已经 commit 的内容, 不可更改历史commit + - stage: 执行 add 命令, 将文件缓存到该区 + - work: 工作目录, 日常做修改的就是该分区 + +> [gitworkflows Documentation](https://git-scm.com/docs/gitworkflows) + +************************ + +- [Git LFS](https://git-lfs.github.com/) large file system + +> 防止Git仓库存储爆炸 +- 不提交源码无关文件: 编译后结果,二进制文件,日志 +- 源码中单文件不要太大: 每次修改都会存储该文件的快照,文件大且修改频繁的话会快速占用空间 + +> [清理大文件](/Skills/Vcs/GitAction.md#清理仓库大文件) + +# 开源许可证 + +> [License](/Skills/Document/License.md) + +************************ + +# 基本命令 + +> [git-tips](https://github.com/521xueweihan/git-tips) `学习Git的仓库` +> [git权威指南的组织](https://github.com/gotgit) `完整书籍,以及相关测试题` + +> [使用原理视角看 Git](https://coding.net/help/doc/practice/git-principle.html) +> [如何高效地使用 Git](https://zhuanlan.zhihu.com/p/30561653) + +> [参考: 重看”Linus Torvalds on Git”视频](http://www.techug.com/post/review-of-linus-torvalds-on-git.html) +> [GitHub Cheat Sheet](https://github.com/tiimgreen/github-cheat-sheet) + +> 使用 `git help 子命令`, 就能看到子命令对应的文档 + +## config + +- 三种配置方式 作用范围越大, 生效优先级越低 + - `--system` 作用所有用户, 对应文件 `/etc/gitconfig` + - `--global` 作用当前用户, 对应文件 `~/.gitconfig` + - (缺省) `--local`作用当前项目, 对应文件 `./.git/gitconfig` +- `git config user.email ***` 和 `git config user.name ***` 这两个是必须的, +- `git config http.postBuffer 524288000` 设置缓存区大小为 500m +- `git config core.fileMode false` 忽略文件的mode变化,一般发生在文件放在挂载盘的时(默认755) +- `git config branch.master.description` **查看**master分支描述信息,命令后附带信息则是**设置** + +打开 `~/.gitconfig`文件能够发现这是 ini 格式的配置文件 + +```ini +[user] + email = kuangcp@aliyun.com + name = kuangcp +[core] + quotepath = false # 配置路径显示为中文 + autocrlf = false + safecrlf = false +[credential] + helper = store +``` + +************************ + +`diff`配置 +> 可用: opendiff kdiff3 tkdiff xxdiff meld kompare gvimdiff diffuse diffmerge ecmerge p4merge araxis bc codecompare smerge vimdiff emerge +> [工具 详细](/Linux/Base/LinuxFile.md#比较文件内容) + +> [delta](https://github.com/dandavison/delta) `diff和分页查看git差异` + +************************ + +1. git config pull.rebase false # merge (the default strategy) +2. git config pull.rebase true # rebase +3. git config pull.ff only # fast-forward only + +************************ + +## clone + +- `git clone URL 目录` 克隆下来后更名为指定目录 +- `-b branch` 克隆远程仓库的指定分支 **从Git 1.7.10开始支持** +- `--single-branch` 只克隆当前分支 +- `git clone --depth n URL` 只克隆最近n次提交的历史, 能大大减小拉取的大小 + +只克隆 指定标签或分支 且不包含内容 `git clone -b --single-branch --depth 1 ` **大大缩减需下载的仓库大小** + +### Shallow Clone + +Shallow Clone: `git clone --depth n URL` 克隆的本地仓库 + +> 限制: + +- 但是如果要新建一个分支, 并推送过去,会报错:`shallow update not allowed` +- 拉取远程分支到本地不能直接用 `git checkout -b branch origin/branch` 的方式, + - 只能用 `git fetch origin branch:branch` + - 并且跟踪远程也需手动执行 `git push -u origin branch` + - 并且 git log 的输出不会显示 origin/branch 的指针信息,需要在对应分支上手动执行 `git remote set-branches origin branch` 再 `git fetch` + +> 转为完整库的方案: + +1. `git fetch --unshallow` 转换为完整仓库 +2. 补全历史提交 `git remote set-branches origin '*'` 然后 `git pull` 就会拉取最新所有分支成为可正常checkout的仓库,但仍旧残缺 +3. 篡改初始提交,丢弃残缺提交前的提交历史 + - 残缺库的第一个提交会有一个 `graft`的标记 + - START_COMMIT=$(git rev-list master|tail -n 1) + - git checkout --orphan temp_branch + - git commit -m "Initial commit" + - git rebase --onto temp_branch $START_COMMIT master + - 此时第一个提交hash变化了,graft也消失了,这个提交就成了正常的原始提交 + - 但是注意这个问题:假如master分支做了以上操作,其他同样是残缺提交作为第一个提交的分支(例如dev分支)会无法merge和rebase,push 即已作废,无法修复。 所以需要找一个有最完整提交的分支执行以上操作,然后作废其他同源分支。 + - 如果其他分支(feature/xxx-1.0)都是残缺提交节点后创建的,那就不受影响,因为 git merge-base 会检查到两个分支的祖先节点是一致的,能正常merge和push。 +4. 简单粗暴:删除 .git 目录,从头开始 + +### sparse checkout 稀疏检出 + +> [参考: git sparse checkout (稀疏检出)](https://www.jianshu.com/p/680f2c6c84de) + +1. git init name +2. cd name +3. git remote add origin URL +4. git config core.sparsecheckout true +5. echo "path1/" >> .git/info/sparse-checkout +6. echo "path2/" >> .git/info/sparse-checkout +7. git pull origin master + +此时,只会从remote端pull下来符合 sparse-checkout 文件内规则(与 .gitignore 写法一致)的目录或文件,适合拉取大仓库中的局部目录和文件 + +************************ + +## add + +- 添加文件或目录 `git add file dir ...` +- 添加当前文件夹以及子文件夹 `git add .` +- 交互式添加每个文件的每部分修改 `git add -p` + +************************ + +## rm + +- 删除文件 `git rm file1 file2 ...` +- 仅从git仓库中删除文件, 但是文件系统中保留文件 `git rm --cached 文件` + - 如果仅仅是想从仓库中剔除, 那么执行完命令还要在 `.gitignore` 文件中注明, 不然又add回去了 + +************************ + +## status + +> git status --help 查看详细介绍 + +- `-s --short` 简化输出 + - ?? 表示新添加未跟踪 + - A 新添加到暂存区 + - M 修改过的文件 + - MM 修改了但是没有暂存 + +************************ + +## commit + +> [Official Doc](https://git-scm.com/docs/git-commit) + +- `git commit -am "init" `: a git库已有文件的修改进行添加, m 注释 + - `git add * ` 如果有新建立文件就要add 再之后commit就不要a参数了 `git commit -m ""` + - 如果只是修改文件没有新建 `git commit -am ""` +- `git commit ` 会自动进入VI编辑器 + - 第一行:用一行文字简述提交的更改内容 + - 第二行:空行 + - 第三行:记述更改的原因和详细内容 + - 使用下面方法关闭退出 +- `--amend` 追加文件到上次commit + - 如果上次提交漏了文件, 只需把漏的文件加入到 index区中, 然后执行 git commit --amend 即可 + - 注意: 如果没有将前一个提交推送到远程, 那么没有任何影响, + - 如果已经推送上去了, 就相当于该次 --amend 操作是新开了个分支完成的修改, git log 里会出现一个分支的环 +- `--no-edit` 沿用上次 commit msg +- `--allow-empty` 提交空提交 + +> [git-cliff](https://github.com/orhun/git-cliff)`从commit信息中提取 changelog` + +************************ + +## restore + +- 丢弃所有改动,将 Readme.md + - 回滚到 master倒数第三个 commit `git restore -s master~2 Readme.md` + - 回滚至指定提交 `git restore -s commitid filepath` +- 撤销所有Java文件修改 `git restore '*.java'` 注意支持 regex +- 撤销工作目录所有修改 `git restore :/` + +************************ + +## revert + +> [Doc](https://git-scm.com/docs/git-revert) + +1. 取消所有暂存 `git revert .` +2. 回滚上一次提交 `git revert HEAD` +3. 撤销某次提交 `git revert commitId` 注意该操作可嵌套 即 撤销撤销某次提交 +4. 回滚代码至指定提交 `git revert --no-commit 032ac94ad...HEAD` + - `git commit -m "rolled back"` + +> 场景: 一个特性分支不该合并到主开发分支, 但是已经合并了, 并且合并后又做了很多其他修改, 这时候怎么影响最小地撤销这次错误的合并? + +1. 找到 merge 的 commitId,git show commitId 找到 Merge: 后两个commitId 分别记为 1 2 +2. 如果保留1, 删除2节点提交的内容 则 `git revert commitId -m 1` + +************************ + +## show + +> 展示提交的详细信息 注意show和 diff 的输出仅仅相似 不可用于 patch + +- 显示当前提交的差异 `git show HEAD` + - HEAD替换成具体的 commit id就是显示指定提交的修改内容 + - 注意这里有个 `^` 语法 HEAD^ 就是HEAD的前一次,两个就是前两次,commit id 同理 + - 还有一个 `~` 语法 例如 ~2 ~3 就等价于 ^^ ^^^ + - 特别注意 `git show HEAD~2^2` 表示取第前两次提交的第二个父提交, 如果这是一个merge节点的话,否则会报错 + - `第一父提交`是合并时所在分支,`第二父提交`是所合并的分支 + - 可借助 git reflog 命令的输出找到对应的位置 例如 `HEAD{10}` +- 模糊搜索 `git show :/query` + +************************ + +## log + +> 更多说明 查看 `git help log` | [Official Doc](https://www.git-scm.com/docs/git-log) + +- `-g` 包含 reflog 信息 +- `-p` 显示所有提交的修改内容 `git log -p -2` 则仅显示最近两次提交的差异 +- `--stat` 查看每一次提交的修改文件修改概述 (pull时看到的那些++--的内容) + - 默认超过80会折叠成.. 可以手动指定宽度 +- `---pretty=[online/short/full/fuller/format]` 使用预定义格式显示 + - format 可自定义格式和占位符 详情查看 -h +- 图形的样子显示分支图 `--graph` +- 显示每个分支最近的提交 `--simplify-by-decoration` +- 输出简短且唯一的 SHA-1 值 `--abbrev-commit` + - 注意 SHA-1 20 byte长度 出现冲突的概率是 (n*(n-1)/2) / 2^160 +- `git log --author='A' `输出所有A开头的作者日志 +- `git log 文件名 文件名` 输出更改指定文件的所有commit 要文件在当前路径才可 +- `git log --after='2016-03-23 9:20' --before='2017-05-10 12:00' ` 输出指定日期的日志 + +- `git shortlog` 按字母顺序输出每个人的日志 + - `--numbered` 按提交数排序 + - `-s` 只显示每个提交者以及提交数量 + +> **`彩色输出Log`** + +```sh + alias glogc="git log --graph --pretty=format:'%Cred%h%Creset %Cgreen%ad%Creset | %C(bold cyan)<%an>%Creset %C(yellow)%d%Creset %s ' --abbrev-commit --date=short" # 彩色输出 + alias gloga='git log --oneline --decorate --graph --all' # 简短彩色输出 + alias glo='git log --oneline --decorate' # 最简单 + alias glol='git log --graph --pretty='\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\' + alias glola='git log --graph --pretty='\''%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'\'' --all' +``` + +### 对比两个分支的差异 +> [参考博客 git 对比两个分支差异](http://blog.csdn.net/u011240877/article/details/52586664) + +> commit差异 + +- 查看**dev有,master没有**的那些提交 + - `git log master..dev` 或 `git log dev ^master` (^表示非,等价于 --not) + - 且支持多个分支 `git log dev ^master ^fea/feature1` 表示:在dev有后两个分支没有的commit + - 还可对比远程分支和本地分支的差别 `git log origin/master..master` + +- 对比分支的差异: `git log dev...master` 即 非两个分支共有的commit + - 显示出每个提交是在哪个分支上 `git log --left-right dev...master` + - 注意输出: commit 后面的箭头,根据我们在 `–left-right dev…master` 的顺序,左箭头 < 表示是dev的提交,右箭头 > 表示是 master的。 + +- 对比两个tag差异 `git log -s "v1.1.0" "^v1.0.6"` + +> 内容差异 + +- `git diff dev master` 查看 从dev分支切换到master分支将会发生的所有修改内容 + - 第一个分支可省略,缺省为当前分支 + +### 查看文件的修改记录 + +1. `git log fileName` 或者 `git log --pretty=oneline fileName` 更容易看到 sha-1 值 +2. git show sha-1的值 就能看到该次提交的所有修改 + +### 全分支搜索字符串 +git log --oneline -S "search keyword" --source --all + +### 查看目录或文件修改频次 +> 查看所有提交修改的模块分布 `git --no-pager log --format=format:'%h' --no-merges | awk '{system(" git --no-pager diff --stat-name-width=300 --name-only "$1" "$1"~") }' | sed 's/\/.*//g' | sort | uniq -c | sort -hr` +- `--stat-name-width=300` 规避路径过长被折叠成... +- awk 中的 system() 调用命令 +- `sed 's/\/.*//g'` 只保留第一级目录 +- `--after='2022-01-01 0:00' --before='2023-01-01 0:00'` 追加时间过滤 + +************************ +## blame +> 查看文件提交记录 + +`git blame file` + +************************ +## diff +- 默认是将 work 区 和 index 区 进行比较 + - `--cached` stage 区 和 index 区 进行比较, 等同于 `--staged` + +``` + git diff [options] [] [--] [...] + git diff [options] --cached [] [--] [...] + git diff [options] [--] [...] + git diff [options] + git diff [options] [--no-index] [--] +``` + +> [Github:diff-so-fancy](https://github.com/so-fancy/diff-so-fancy) `一个更方便查看diff的工具` 安装: `npm install -g diff-so-fancy` + +************************ + +> 查看当前分支和master的文件差异列表 `git diff master --stat=200 --compact-summary`, 在一个时间周期长,改动范围大的功能分支上可以在上线前快速确认下有没有漏SQL执行,漏配置项 + +[--stat 参数防止路径被折叠](https://git-scm.com/docs/git-diff-files/zh_HANS-CN#git-diff-files---statltgtltgtltgt) + +### diff 创建 patch + +- 创建分支之间的patch `git diff branch1 branch2 > first.patch` +- 创建分支之间具体文件的patch `git diff branch1 branch2 path/file1 path/file2 > first.patch` + - 注意文件是命令行当前路径的相对路径 +- 创建单文件的patch `git diff filePath > first.patch` 路径为Git项目根路径的相对路径 + +************************ + +## apply + +> 将patch文件应用到 index区。 Apply a patch to files and/or to the index + +- `git apply --ignore-space-change --ignore-whitespace first.patch` +- `patch -p1 < first.patch` git apply失败可以尝试这个方式 + +************************ + +## format-patch + +> 将patch文件应用为commit。 Prepare patches for e-mail submission +> [参考: How To Create and Apply Git Patch Files](https://devconnected.com/how-to-create-and-apply-git-patch-files/) + +> 创建 patch + +- `git format-patch -1 commit-sha` 指定commit 创建 patch + - 参数选项可以为 `-2` `-3`... 数字表示 commit id 之前的 几个 commit 也创建 patch +- `git format-patch master -o patches` 对那些 master分支 中有而当前分支没有的 commit 创建 patch 到 patches 目录 +- `git format-patch master --stdout > total.patch` 将所有patch文件合并为一个 + +> 使用 patch + +使用[am](#am) 或者 [apply](#apply) 命令 + +************************ + +## am + +> Apply a series of patches from a mailbox + +- git am patches/1.patch +- 如果是单纯的搬运 commit 使用 format-patch 创建 patch 然后 使用 am 应用的方式 比 diff 然后 apply 更好, 因为会保留原有commit信息 + +************************ + +## tag + +> [Official Doc](https://git-scm.com/docs/git-tag/2.10.2) + +- 查看所有标签 `git tag` + - `-l 'v1.0.*'` 列出v1.0.* + - `git show tagname` 展示标签注释信息 +- 新建一个标签并打上注释 `git tag -a v1.0.0 -m "初始版本"` + - 由指定的commit打标签 `git tag -a v1.2.4 commit-id` +- 切换标签 `git checkout tagname` 和切换分支一样的,但是标签只是一个镜像,不能做提交 +- 在某tag上新建一个分支 `git checkout -b branchname tagname` +- 删除本地标签 `git tag -d tagname` +- 删除远程的tag + - `git push origin -d tag ` + - 如果本地已经删除了标签, 就可以 `git push origin :refs/tags/` + +## notes + +> [doc](https://git-scm.com/docs/git-notes) + +************************ + +## reset + +> git reset -h + +``` +用法:git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<提交>] + 或:git reset [-q] [<树或提交>] [--] <路径>... + 或:git reset --patch [<树或提交>] [--] [<路径>...] + + -q, --quiet 安静模式,只报告错误 + --mixed 重置 HEAD 和索引 + --soft 只重置 HEAD + --hard 重置 HEAD、索引和工作区 + --merge 重置 HEAD、索引和工作区 + --keep 重置 HEAD 但保存本地变更 + --recurse-submodules[=] control recursive updating of submodules + -p, --patch 交互式挑选数据块 + -N, --intent-to-add 将删除的路径标记为稍后添加 +``` + +> [参考: 使用reset回滚代码](https://www.v2ex.com/t/296286) + +### 回滚add操作 + +- 当执行了 git add 命令, 将文件存入暂存区 +- 可以使用 `git reset 文件` 将指定文件 或者 `git reset .` 当前目录(递归) 都取消暂存 +- 文件内容没有改变, 这个用于选指定文件提交时 + +### 回滚最近一次commit + +1. `git reset --soft HEAD^` 撤销最近那次 commit 行为 +2. 修改代码的内容 +3. `git commit -c ORIG_HEAD` 使用撤销的那次 commit 的注释进行提交 + +> 注意 reset 操作会将老的HEAD会备份到文件 .git/ORIG_HEAD 中,命令中就是引用了这个老的相关信息 +> -c 参数是复用指定节点的提交信息 + +### 回滚最近几次的commit并添加到一个新建的分支上去 + +1. 新建分支 `git branch feature/new` +2. 删除master分支最近3次提交 `git reset --hard HEAD^3` +3. 切换到新分支上 `git checkout feature/new` + +> 相当于是将master上这三次的修改都转移到了这个分支上, master 从来没有过这三次提交一样 +> 如果没有在 执行 reset --hard 之前新建分支的话, 这三次提交就永远删除了 + +> 注意: 这个操作在多人的协作中, reset --hard 比较危险, 可能引起别人分支的混乱 + +### 回滚merge和pull操作 + +1. 执行了merge 或者 pull 操作后 +2. `git reset --hard ORIG_HEAD` 注意: 该命令会将 index 和 stage 的修改清空 + +### 在index已有修改的状态回滚merge或者pull + +1. `git pull` +2. `reset --merge ORIG_HEAD` + +> 使用 --hard 会直接回滚,直接丢失当前未提交的所有更改 + +### 被中断的工作流程 + +> 在开发一个功能的时候, 突然有别的需求插进来了, 就可以通过 commit 一次, 然后回滚该次 commit 的方式 +> 将工作状态暂存, 且不会产生垃圾提交 + +************************ + +## gc + +`git gc -h`: + +- `--aggressive` 默认使用较快速的方式检查文档库,并完成清理,当需要比较久的时间,偶尔使用即可 +- `--prune[=<日期>]` 清除未引用的对 +- `--auto` 启用自动垃圾回收模式 +- `--force` 强制执行 gc 即使另外一个 gc 正在执行 + +************************ + +## clean + +> Remove untracked files from the working tree `git clean --help` + +`-n` 参数预览删除文件列表 + +************************ + +# 本地分支 + +> Git 的分支是轻量型的, 能够快速创建和销毁 + +- `@{-1}` 表示checkout的上一个分支 [Release V1.6.2](https://github.com/git/git/blob/master/Documentation/RelNotes/1.6.2.txt) + - `git rev-parse --symbolic-full-name @{-1}` 展示上一个分支 + - `git merge @{-1}` 将上一个分支合并进来 + - `git branch --track mybranch @{-1}` 设置当前分支track上一个分支 + +************************ + +- 获取当前分支名 `git symbolic-ref --short -q HEAD` +- 拉取远程分支到本地并建立同名分支 + + - 拉取元数据 `git fetch --all` + - 建立和远程分支对应的本地分支 `git pull <远程主机名> <远程分支名>:<本地分支名>` + +## show-branch + +> 按颜色列出分支上的提交和图示 + +可以查看到每次提交所属的分支 + +************************ + +## stash + +> [Official Doc](https://git-scm.com/docs/git-stash) + +> 将当前修改缓存起来, 避免不必要的残缺提交 stash命令的缓存都是基于某个提交上的修改, 是一个栈的用法 + +> [参考: Git Stash的用法](http://www.cppblog.com/deercoder/archive/2011/11/13/160007.html) `底下的评论也很有价值, 值得思考` +> [参考: git-stash用法小结](https://www.cnblogs.com/tocy/p/git-stash-reference.html) + +> git stash --help 查看完整的使用说明 + +`基本动作` + +- push + - save命令的进化版,该动作是缺省动作 +- list + - 输出大致为: `stash@{num}: On branchName : comment` +- save + - save comment 已被废弃 +- pop + - 将最近的stash 应用到当前仓库上, 原有的 stash 就丢弃了,如果pop缓存时发生了冲突 则不会丢弃对应的缓存 +- apply + - 将指定的stash 应用到仓库上, 不丢弃原有的stash +- drop + - 丢弃指定的stash, 如果想丢弃当前项目所有更改就可以将所有更改 save stash 然后 drop +- clear + - 清除所有 stash +- branch + - 从创建缓存处创建新分支出来并pop 默认栈顶缓存,相比于pop和apply,这种方式更贴近缓存被创建时的场景 + +> push动作 实用参数 + +1. `--keep-index` `-k` stash 将不缓存 已经被add进index区的内容 +2. `--include-untracked` 或 `-u` stash 将缓存未被track的文件 +3. `--patch` 交互式选择哪些内容需stash缓存哪些进入index区 +4. 如果需要恢复 `stash@{0}: On feature-test: test` + - 就在 feature-test 分支上建立新分支, 然后 apply stash@{0} + - 不推荐用 pop, 当stash多了以后 人不一定都记得每个stash都改了啥, 可能会有冲突以及修改覆盖的问题 + - 最好用新分支装起来, 然后合并分支, 或者是 cherry-pick, 修改也不会丢失 + +> *注意* stash 是一个项目范围内的栈结构, 所以如果多个分支执行了stash, 那缓存都是共用的 +> 要先确定好当前分支 stash 的 id (通过记录comment的方式会更好) 再 pop 或者 apply (不能无脑pop 血泪教训) + +- 使用该别名能展示当前分支的stash `alias wip='git stash list | grep $(git branch --show-current)' ` + +### stash 创建 patch + +- 查看stash栈某下标(提交)的差异 `git stash show -p stash@{0}` + - 简化别名 `alias gsh.st='__gshst(){ index=$1; if test -z $index; then index=0; fi; git stash show -p stash@{$index} }; __gshst'` +- 创建 patch `gsh.st > dev.patch` + +### 恢复被drop的stash + +> [How to recover a dropped stash in Git?](https://stackoverflow.com/questions/89332/how-to-recover-a-dropped-stash-in-git) + +可以恢复 stash drop 或者 clean 的内容。stash drop后会输出 `Dropped refs/stash@{0} (......)`, 括号内就是该次stash对应的commitId + +- `git fsck --no-reflog | awk '/dangling commit/ {print $3}'` + - 使用 gitk 显示 `gitk --all $(git fsck --no-reflog | awk '/dangling commit/ {print $3}')` + - 或者在命令后接管道 ` | xargs git show`, 查找代码内容 +- WIP 开头的就是 stash 对应的 commit , 找到对应的 sha1 id 建立新分支即可 + - 也就是说 stash 仍然是采用 分支 来实现的, 在某个分支stash 就相当于在该分支进行 commit + +************************ + +## branch +> 查看所有参数 `git branch --help` + +- 列出所有分支(包含本地和远程) `-a --all` +- 按条件显示分支 `--list 'feature*'` +- 列出远程分支 `-r --remote` +- 查看分支详细信息 `-vv` 本地分支和远程分支的关联状态 +- 查看包含指定 commit(可以多个) 的分支 `--contains []` + - 对应的则是不包含 `--no-contains []` commit 缺省为 HEAD(也就是最近的一次提交) +- 创建分支 `git branch name` 并设置当前分支的对应远程分支 `-t /` +- 重命名分支 `-m old new` 对于远程来说就是先要删除再新建分支 +- 删除分支 `-d 分支` + - 如果该分支没有被完全合并, 就会提醒使用 `-D` 强制删除. 等价于 `--delete --force` +- 设置当前分支跟踪的远程分支 `--set-upstream-to=/ ` +- 查看当前分支合并/未合并的其他分支 `--merged` `--no-merged` + +************************ + +## checkout + +> [Official Doc: git checkout](https://git-scm.com/docs/git-checkout) + +1. 切换分支 `git checkout feature/a` +1. 切换至上一个分支 `git checkout -` 等价于 `git checkout @{-1}` +1. 切换分支并设置该分支的远程分支 `gh feature/a origin/feature/a` + +> 撤销文件修改 + +- `git checkout .` 取出最近的一次提交, 覆盖掉 work 区下当前目录(递归)下所有已更改(包括删除操作), 且未进入 stage 的内容, 已经进入 stage 区的文件内容则不受影响 + - `git checkout 文件1 文件2...` 同上, 但是只操作指定的文件 + +- `git checkout [commit-hash] 文件1 文件2...` 根据指定的 commit 对应hash值, 作如上操作, 但是区别在于 从 index 直接覆盖掉 stage 区, 并丢弃 work 区 + - `git checkout [commit-hash] .` + - **`如在项目根目录执行该命令, 会将当前项目的所有未提交修改全部丢失, 不可恢复!!!!`**, 所以应尽量使用 stash 命令,即使pop也能恢复 + +- `git checkout [commit-hash] 节点标识符或者标签 文件名 文件名 ...` + - 取出指定节点状态的某文件,而且执行完命令后,取出的那个状态会成为head状态, + - 需要执行 `git reset HEAD` 来清除这种状态 + +> 实验性命令: git switch branch + +## 分支合并 +> merge rebase squash 三种合并策略 + +- Merge会创建合并节点形成环 +- Rebase是通过调整两个分支链上的提交,合并成一个链没有环 +- Squash不是具体命令,做法是将需要合并过去的那些提交撤销得到文件修改,基于这些修改再创建一个新提交。好处是分支图上只有主要合并提交,没有中间提交信息的干扰 + +[这才是真正的 Git——分支合并](https://zhuanlan.zhihu.com/p/192972614) + +Git 在合并分支的时候使用的是 三向合并策略,即当前分支和目标分支的共同祖先commit节点, 和两个分支的当前commmit节点进行比较确定哪一方发生修改需要纳入,如果两方都修改就要提示冲突 + +根据 Git 的合并策略,在合并两个有分叉的分支(上图中的 D、E‘)时,Git 默认会选择 Recursive 策略。找到 D 和 E’的最短路径共同祖先节点 B,以 B 为 base,对 D,E‘做三向合并。 + +B 中有 http.js,D 中有 http.js 和 main.js,E’中什么都没有。根据三向合并,B、D 中都有 http.js 且没有变更,E‘删除了 http.js,所以合并结果就是没有 http.js,没有冲突,所以 http.js 最终会被删除。 + +### 分支问题排查 + +- `git merge-base 分支1 分支2` 查看两个分支共同祖先(前提:两个分支通过merge命令发生的合并,如果是rebase则找不到真正的祖先节点) +- `git show-branch 分支1 分支2 分支3` 查看若干分支差异提交情况 + +## merge + +- [官方文档](https://git-scm.com/docs/git-merge) + +> [Official Doc: 高级合并](https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%AB%98%E7%BA%A7%E5%90%88%E5%B9%B6) +> [参考: git-merge完全解析](https://www.jianshu.com/p/58a166f24c81) + +- `git merge develop` 默认 是 ff(fast forward) 不生成新节点,直接将当前分支指向Develop分支。(一条拐弯的分支线) + - 推荐: `git merge --no-ff develop` 在当前分支 `主动合并`分支Develop,生成一个新节点,分支图的合并路径清晰 +- `--squash` 和 `--no-squash` 该参数和 `--no-ff` 冲突 + - 使用 `--squash` 时,当一个合并发生时,从当前分支和对方分支的共同祖先节点,一直到对方分支的顶部节点内的所有提交内容将修改当前工作区,使用者可以经过审视后进行提交,产生一个新的节点。 + - 这种情况下分支图看不到合并的环,只会看作一个简单的提交 +- 如果遇到冲突: + - `git mergetool` 使用工具进行分析冲突文件方便修改 + +> 配置mergetool工具kdiff3, 同类的还有meld: + +- `git config --global merge.tool kdiff3` +- `git config --global mergetool.kdiff3.cmd "'D:/kdiff3.exe' \"\$BASE\" \"\$LOCAL\" \"\$REMOTE\" -o \"\$MERGED\""` +- `git config --global mergetool.prompt false` +- `git config --global mergetool.kdiff3.trustExitCode true` +- `git config --global mergetool.keepBackup false` + +> [merge 策略](https://git-scm.com/docs/merge-strategies) + +- Git 2.34 新增 ort 策略 + +************************ + +## rebase + +> [Official Doc](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) + +> 衍和操作 [参考博客](http://blog.csdn.net/endlu/article/details/51605861) | +> [Git rebase -i 交互变基](http://blog.csdn.net/zwlove5280/article/details/46649799) | +> [git rebase的原理之多人合作分支管理](http://blog.csdn.net/zwlove5280/article/details/46708969) +> 这种合并方式,不会像merge方式那样在分支图上出现多个圈,而是线性演进, 但是遇到冲突后会改动历史提交,导致提交不是按时间演进,不利于分布式协作 + +- 与master合并:`git merge master` 换成 `git rebase master` +- 当遇到冲突: + - `git rebase --abort` 放弃rebase + - `git rebase --continue` 修改好冲突后继续 + +> master 提交了 b,c +> dev 提交了 d,e + +``` +merge master: + +master: a - b - c + \ \ +dev: d - e + +rebase master: + +master: a - b - c - d' - e' +``` + +merge 会保留分支图, rebase 会保持提交记录为单分支 + +************************ + +## cherry-pick + +> [Official Doc](https://git-scm.com/docs/git-cherry-pick) + +- `git cherry-pick ` + +简单来讲, 就是将指定的某个提交(任意分支上的)上的修改, 重放到当前分支上 和 stash pop 命令相比, 在重放上是一致的, 使修改内容生效,commitId会变化 + +> 用途 + +1. 可用于合并已有的若干个提交, 为了改动最小, 一般新建分支来做这件事 + - 例如 功能分支 `fea/something` 上的四个提交其实可以合并, 使得提交信息更清晰, 不冗余, 就可以从 `fea/something` + - 创建处新建一个分支, 将该分支所有提交进行重放, 需要合并的那几个放一起重放 然后 将四个提交 reset, 再次提交即可 + +## bisect + +- [git bisect 命令教程](http://www.ruanyifeng.com/blog/2018/12/git-bisect.html) +- [二分查找捉虫记](http://www.worldhello.net/2016/02/29/git-bisect-on-git.html) `通过分析提交历史查到哪次提交引起的Bug然后检出,修复` + +## worktree + +> Manage multiple working trees [doc](https://git-scm.com/docs/git-worktree) + +************************ + +# 远程操作 + +> Git大部分命令都是本地的, 所以执行效率很高, 但是协同开发必须有同步的操作 + +其实单独的两个主机也能完成同步, 两个主机之间 使用同一个仓库进行开发 +两个人互为对方的远程库(使用 git daemon 即可搭建简易服务端), 添加为 remote 即可操作 + +指定本地开发分支和远程的绑定关系 `git branch --set-upstream dev origin/dev` 而且 一个本地库是能够绑定多个远程的 + +> Github上fork的项目 + +**合并对方最新代码** + +1. 首先fork一个项目, 然后clone自己所属的该项目下来,假设 原作者为A 自己为B +2. 添加原作者项目的URL 到该项目的远程分支列表中 `git add remote A A_URL` +3. fetch作者的代码到本地 `git fetch A` +4. 在B分支上合并作者分支代码 `git merge --no-ff A/master` +5. push即可 + +> Github上PR + +[Using git to prepare your PR to have a clean history](https://github.com/mockito/mockito/wiki/Using-git-to-prepare-your-PR-to-have-a-clean-history) + +************************ + +## remote + +> [Official Doc](https://git-scm.com/docs/git-remote) + +1. **常用参数** + - `add name URL地址` 添加远程关联仓库 不唯一,可以关联多个, 一般默认是origin + - `set-url name URL地址` 修改关联仓库的URL + - `rm URL` 删除和远程文档库的关系 + - `rename origin myth` 更改远程文档库的名称 + - `show origin` 查看远程分支的状态和信息 +2. 显示本地仓库跟踪的那个远程仓库 `git ls-remote` +3. 查看关联远程仓库的详情(push和pull的地址) `git remote -v` + +- [参考: 删除,重命名远程分支](http://zengrong.net/post/1746.htm) + +************************ + +## push + +- _常用参数_ + - `-q` 控制台不输出任何信息 + - `-f` 强制推送提交 **使用这个参数时要再三考虑清楚** + - `--all` 推送所有分支 + - `-u` upstream 设置 git pull/status 的上游 + - `git push origin master`和 `git push -u origin master` 区别在于 前者是使用该远程和分支进行推送 + - 后者也是推送, 并设置origin为默认推送的远程, 以后push就不用注明远程名了(多远程的情况下要注意) + - `-d --delete` 删除引用(分支或标签) + +- 删除远程分支 + - `git push origin -d 分支名称` + - 如果本地已经删除了该分支,就可以 `git push origin :分支名称` +- 第一次将本地分支与远程建立关系 + - `git push -u origin master ` | `git push --set-uptream master` | `git push -all` (会将所有分支一起push) + +- 提交指定tag `git push origin tagname` + - 提交所有tag `git push --tags` + +- 出现 `RPC failed; result=22, HTTP code = 411` 的错误 + - 就是因为一次提交的文件太大,需要改大缓冲区 例如改成500m `git config http.postBuffer 524288000` + +************************ + +## fetch + +> 访问远程仓库, 拉取本地没有的远程数据 + +- 注意 fetch 是一个分支一个分支进行拉取的, 在此基础上可以优化网络不稳定时clone代码的问题 + - 关键是分支之间独立拉取不会像clone拉取所有分支,有分支拉取失败就要从头再来 + - 操作过程: 创建空目录并进入, `git init` 然后 `git fetch URL` + - 创建 msater分支 `git checkout -b master FETCH_HEAD` + - 拉取其他分支 `git fetch --all` +- 拉取本地没有的分支(两种方式) + 1. **推荐** 拉取 origin 信息 `git fetch --all` 由远程分支创建新分支并设定跟踪 `git checkout -b dev origin/dev` + 2. 拉取 origin 的 dev 分支 并在本地创建 dev 分支 `git fetch origin dev:dev` + - 但此时本地的分支并没有 track 远程分支,需要执行 `git push -u origin dev` 进行设置 +- 删除远程没有但本地有的那些分支 `git fetch -p` +- `git fetch origin dev-test` 下拉指定远程的指定分支 至 origin/dev-test 但不会创建本地分支 + +> fetch 不到所有远程分支的原因和解决方案 + +- 查看fetch的源 `git config --get remote.origin.fetch` +- 需要配置为通配方式 `git config --add remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*"` + +************************ + +## pull + +> 不仅仅是 fetch 代码, 还会进行 merge 操作, 所以安全起见, 是先 fetch 然后再手动 merge + +- `git pull origin dev` 下拉指定远程的指定分支 +- `git pull --all` 下拉默认远程的所有分支代码并自动合并 +- 拉取项目所有远程分支到本地 + +```sh + git branch -r | grep -v '\->' | while read remote; do git branch --track "${remote#origin/}" "$remote"; done + git fetch --all + git pull --all +``` + +> git pull 默认策略配置 + +- git config pull.rebase false # merge +- git config pull.rebase true # rebase +- git config pull.ff only # fast-forward only + + +> error: cannot lock ref 'refs/remotes/origin/test/v1.0.0' : 'refs/remotes/origin/test' exists; + +原因: 由于git的分支都是以目录和文件形式存储,分支名包含 / 时会创建对应的目录, 因此无法同时创建 test 分支和 test/v1.0.0 分支,目录test和文件test冲突了 + +需要手动删除test分支,或者重命名test/v1.0.0 为 test_v1.0.0 . + +上诉错误容易出现在Clone多份仓库时,某份仓库删了test分支并push, 但是这份仓库的 remote 引用管理未清理 + +清理远程引用: `git update-ref -d refs/remotes/origin/test` + + +************************ + +# Submodule + +> [Official Doc](https://git-scm.com/book/en/v2/Git-Tools-Submodules) + +> [git submodule的使用](https://blog.csdn.net/wangjia55/article/details/24400501) +> [参考: Git Submodule使用完整教程](http://www.kafeitu.me/git/2012/03/27/git-submodule.html) + +- 能够在一个git仓库中将一个文件夹作为一些独立的子仓库进行管理 +- 添加子模块 `git submodule add url dir` 目录为可选项 + +当主仓库 clone 时 只会将子模块作为空目录克隆下来 + +- 读取 .gitmodules 文件完成子模块的注册 `git submodule init` +- 拉取子模块代码 `git submodule update` + +以上两条命令等价于 `git submodule update --init --recursive` + +`删除子模块` + +1. 删除.gitsubmodule里相关部分 +2. 删除.git/config 文件里相关字段 +3. 删除子仓库目录 + +************************ + +# 其他 + +## gitk + +> 图形化展示分支 需要依赖 tcl tk + +## grep + +- 搜索文字 `git grep docker` + - `-n`搜索并显示行号 + - `--name-only` 只显示文件名,不显示内容 + - `-c` 查看每个文件里有多少行匹配内容(line matches): + - 查找git仓库里某个特定版本里的内容, 在命令行末尾加上标签名(tag reference): `git grep xmmap v1.5.0` + - `git grep --all-match -e '#define' -e SORT_DIRENT` 匹配两个字符串 + +## archive + +1. 将某版本打包成压缩包 `git archive -v --format=zip v0.1 > v0.1.zip` + +## reflog + +- 查看仓库的本地操作日志 仅记录HEAD以及所有分支引用所指向的历史 + +1. `git reflog` 显示commit操作详情,仅本地保存 + +## rev-parse + +> 该工具是Git内部命令 往往被其他子命令使用 + +1. 查看分支指向具体的commit id `git rev-parse fea/new` +1. 查看上一个分支 `git rev-parse --symbolic-full-name @{-1}` + +## scalar +> [Git scalar](https://git-scm.com/docs/scalar) 自2.42.1起支持,原理为先稀疏检出,然后定时任务拉取变更 + +## githooks +实现机制: 一组在`.git/hook/`目录下的shell,在git完成特定行为后会触发执行对应的脚本 + +- pre-commit:在执行提交操作之前触发。这是一个非常有用的钩子,可以用来进行代码风格检查、静态代码分析、运行测试等操作,确保提交的代码质量。 +- pre-push:在执行推送操作之前触发。在这个钩子中,我们可以运行更严格的测试,如集成测试、端到端测试等,以确保准备推送的代码符合质量标准。 +- post-commit:在执行提交操作后触发。该钩子可以用于执行一些后续操作,如自动构建、生成文档等。 +- post-checkout:在执行检出操作后触发。这个钩子适用于更新依赖、重置配置等与项目状态相关的任务。 +- post-merge:在执行合并操作后触发。我们可以在该钩子中执行一些与合并后操作相关的任务。 + +> 注意hook脚本不会被git纳入版本管理,所以需要手动维护 +> 注意脚本执行的工作目录是仓库根目录 + +************************ + +# 配置文件 + +## gitignore + +> [Github: gitignore](https://github.com/github/gitignore) | 一行则是一个配置,且可以仓库和目录存在不同的配置,git会按就近原则匹配 + +- 使用 `#` 注释一行 +- `test.txt` 忽略该文件 +- `*.html` 忽略所有HTML后缀文件 +- `*[o/a]` 忽略所有o和a后缀的文件 +- `!foo.html` 不忽略该文件 + +```conf + */ #忽略所有文件 + build/ #所有build目录 + /build #只忽略当前目录的build, 子目录的不忽略 + *.iml #所有iml文件 + ?.log #忽略所有 后缀为log, 文件名字只有一个字母 + !*.java #不忽略所有java文件 + a.[abc] #忽略 后缀为 a或者b或者c 的文件 + doc/*.txt #忽略 doc一级子目录的txt文件, 不忽略多级子目录中txt +``` + +************************ + +## gitattributes + +> [gitattributes](http://schacon.github.io/git/gitattributes.html) + +1. 配置文件的换行符 eol +2. working-tree-encoding +3. ident +4. filter +5. merge +6. whitespace +7. export-ignore +8. delta +9. encoding + +************************ + +# 自定义插件 + +> [how-to-create-git-plugin](https://adamcod.es/2013/07/12/how-to-create-git-plugin.html) diff --git a/Skills/Vcs/GitTeam.md b/Skills/Vcs/GitTeam.md new file mode 100644 index 0000000..394289c --- /dev/null +++ b/Skills/Vcs/GitTeam.md @@ -0,0 +1,286 @@ +--- +title: Git在团队协作时的使用 +date: 2018-12-20 10:29:41 +tags: + - 软件工程 +categories: + - 版本控制 +--- + +💠 + +- 1. [使用Git进行团队协作](#使用git进行团队协作) + - 1.1. [基础思想](#基础思想) + - 1.1.1. [Git Flow](#git-flow) + - 1.1.2. [Github Flow](#github-flow) + - 1.1.3. [Trunk-Based](#trunk-based) + - 1.2. [提交准则](#提交准则) + - 1.2.1. [commit template](#commit-template) + - 1.3. [Tips](#tips) + - 1.3.1. [master作为线上分支时,误提交功能并推送怎么处理](#master作为线上分支时误提交功能并推送怎么处理) +- 2. [GUI](#gui) + - 2.1. [git-cola](#git-cola) + - 2.2. [Gitnuro](#gitnuro) + - 2.3. [GitBlade](#gitblade) + - 2.4. [gitg](#gitg) + - 2.5. [tig](#tig) + - 2.6. [Guitar](#guitar) + - 2.7. [Gittyup](#gittyup) + - 2.8. [SourceTree](#sourcetree) +- 3. [小规模团队使用码云组织的总结](#小规模团队使用码云组织的总结) + - 3.1. [最终方案](#最终方案) + +💠 2024-09-20 18:39:25 +**************************************** +# 使用Git进行团队协作 + +> [在阿里,我们如何管理代码分支?](https://blog.csdn.net/maoreyou/article/details/79877829) +> [版本控制最佳实践](https://www.git-tower.com/blog/version-control-best-practices/) + +> [阮一峰 Git 使用规范流程](http://www.ruanyifeng.com/blog/2015/08/git-use-process.html) + +> Github gitee gitlab bitbucket 等各大平台都是这样一种模式: +> 个人和个人开发者之间是并行master,只适合偶尔开发提交一些代码 +> 组织就是适合给多个人,等同的稳定开发时,分支就会比较明确,这个笔记就是记录组织中git的使用 + +- 《Git团队协作》 + +## 基础思想 + +### Git Flow + +> [Vincent Driessen 提出了 A Successful Git Branching Model](http://nvie.com/posts/a-successful-git-branching-model/) +> [Gitflow workflow ](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow) + +- [依据以上思想开发的 git flow工具](https://github.com/nvie/gitflow) + - [介绍 Git Flow](https://datasift.github.io/gitflow/IntroducingGitFlow.html) +- [参考: Git 在团队中的最佳实践--如何正确使用Git Flow](http://www.cnblogs.com/cnblogsfans/p/5075073.html) + - [参考: Getting Started – Git-Flow](https://yakiloo.com/getting-started-git-flow/) + +![规范的分支图](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/Git/git-team-model.png) + +- Git Flow常用的分支 + - Production 分支 + - 也就是我们经常使用的Master分支,这个分支最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直接修改 + - Develop 分支 + - 这个分支是我们是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支 + - Feature 分支 + - 这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release + - Release分支 + - 当你需要一个发布一个新Release的时候,我们基于Develop分支创建一个Release分支,完成Release后,我们合并到Master和Develop分支 + - Hotfix分支 + - 当我们在Production发现新的Bug时候,我们需要创建一个Hotfix, 完成Hotfix后,我们合并回Master和Develop分支,所以Hotfix的改动会进入下一个Release + +### Github Flow + +> [Understanding the GitHub flow](https://guides.github.com/introduction/flow/?from=singlemessage) + +- [分支图复杂的一个项目](https://github.com/Netflix/eureka/network) `只是演示分支的复杂度` + +### Trunk-Based +> [Trunk-Based Development](https://trunkbaseddevelopment.com/)`基于主干的单分支模型,不使用多分支` + +## 提交准则 + +> [参考: SVN提交更新的一个准则](http://www.cnblogs.com/chenlong828/archive/2008/09/22/1296193.html) + +1. 提交之前先更新 + - SVN更新的原则是要随时更新,随时提交。当完成了一个小功能,能够通过编译并且并且自己测试之后,谨慎地提交。 + - 如果提交过程中产生了冲突,则需要同之前的开发人员联系,两个人一起协商解决冲突,解决冲突之后,需要两人一起测试保证解决冲突之后,程序不会影响其他功能。 + - 如果提交过程中产生了更新,则也是需要重新编译并且完成自己的一些必要测试,再进行提交。 +2. 保持原子性的提交 + - 每次提交的间歇尽可能地短,以一个小时,两个小时的开发工作为宜。如在更改UI界面的时候,可以每完成一个UI界面的修改或者设计,就提交一次。在开发功能模块的时候,可以每完成一个小细节功能的测试,就提交一次,在修改bug的时候,每修改掉一个bug并且确认修改了这个bug,也就提交一次。我们提倡多提交,也就能多为代码添加上保险。 +3. 提交时注意不要提交本地自动生成的文件 + - 对于Java来说, IDE自身配置文件, 和字节码文件是无需提交的 例如 .idea目录 iml文件 +4. 不要提交不能通过编译的代码 + - 代码在提交之前,首先要确认自己能够在本地编译。如果在代码中使用了第三方类库,要考虑到项目组成员中有些成员可能没有安装相应的第三方类库,项目经理在准备项目工作区域的时候,需要考虑到这样的情况,确保开发小组成员在签出代码之后能够在统一的环境中进行编译。 +5. 不要提交自己不明白的代码 + - 提交之后, 你的代码将被项目成员所分享。如果提交了你不明白的代码,你看不懂,别人也看不懂,如果在以后出现了问题将会成为项目质量的隐患。因此在引入任何第三方代码之前,确保你对这个代码有一个很清晰的了解。 +6. 提前协调好项目组成员的工作计划 + - 在自己准备开始进行某项功能的修改之前,先给工作小组的成员谈谈自己的修改计划,让大家都能了解你的思想,了解你即将对软件作出的修改,这样能尽可能的减少在开发过程中可能出现的冲突,提高开发效率。同时你也能够在和成员的交流中发现自己之前设计的不足,完善你的设计。 +7. 对提交的信息采用明晰的标注 + - `+)` 表示增加了功能 + - `*)` 表示对某些功能进行了更改 + - `-)` 表示删除了文件,或者对某些功能进行了裁剪,删除,屏蔽。 + - `b)` 表示修正了具体的某个bug + +> [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) + +### commit template + +> git commit message 的模板化 + +commit message 包含三个部分,header, body和footer,其中header必须有,body和footer可以按情况省略。 + +- `type` 字段 + - feat:新功能(feature) + - fix:修补bug + - docs:文档(documentation) + - style: 格式(不影响代码运行的变动) + - refactor:重构(即不是新增功能,也不是修改bug的代码变动) + - test:增加测试 + - chore:构建过程或辅助工具的变动 +- `scope` 用于说明 commit 影响的范围,比如数据层、控制层、视图层等等,视项目不同而不同。 也就是写用户会感觉到改变在哪个地方。 +- `subject` 是 commit 目的的简短描述,不超过50个字符 + - 以动词开头,使用第一人称现在时,比如change,而不是 changed 或 changes 第一个字母小写 结尾不加句号(.) +- `Body` 部分是对本次 commit 的详细描述,可以分成多行 + - 使用第一人称现在时,比如使用change而不是changed或changes。 + - 应该说明代码变动的动机,以及与以前行为的对比。 +- `Footer` + - 如果当前代码与上一个版本不兼容,则 Footer 部分以BREAKING CHANGE开头,后面是对变动的描述、以及变动理由和迁移方法。 + - 关闭 Issue + +1. 新建 ~/.gitmessage 文件 +2. ~/.gitconfig 中添加 + +``` + [commit] + template = ~/.gitmessage +``` + +> 配置好后 git commit 不指定-m 参数就会调用该模板 + +--- + +## Tips + +### master作为线上分支时,误提交功能并推送怎么处理 + +> master 分支 + +```log +* 259cf80 2021-01-23 18:20:46 五 (HEAD -> master) kcp fea2: remove +* 46e3af2 2021-01-23 18:20:32 五 kcp Merge branch 'fea-1' +|\ +| * abf3d18 2021-01-23 18:12:14 五 (fea-1) kcp fea1: add +* | 7d7d5a3 2021-01-23 18:10:50 五 kcp fea2: update +* | e5d95da 2021-01-23 18:10:40 五 kcp fea2: add +|/ +* a75d6fd 2021-01-23 18:10:16 五 kcp release 1.0 +* ebffe4d 2021-01-23 18:09:53 五 kcp init +``` + +> fea-1 分支 + +```log +* abf3d18 2021-01-23 18:12:14 五 (HEAD -> fea-1) kcp fea1: add +* a75d6fd 2021-01-23 18:10:16 五 kcp release 1.0 +* ebffe4d 2021-01-23 18:09:53 五 kcp init +``` + +目标: master分支撤销所有 fea2 提交信息的提交(*错误提交*) + +1. 从 master 分支新建功能分支 dev +2. 切换 dev 分支。 通过 `git reset --soft HEAD^` 把 *错误提交* 全部撤销到工作区 并 git stash +3. 切换 master分支。 使用 git revert 撤销 master 分支上的所有 *错误提交* + - 在回滚 merge 时,需要 通过 -m 指定 revert到哪个commit + - (git log 中看到的合并commit下的 `commit id` 信息 左为1 右为2) 此处 1 为 7d7d5a3 2 为 abf3d18 +4. 切换 dev 分支 合并master, pop stash 的内容,并提交, 此时就已经把 fea2 代码 拆分到 dev 分支上,保证了master分支的正确性 +5. *注意* 此时 fea-1 的代码 已经被回滚了,master 上 fea-1 的修改全部消失 +6. 切换到 fea-1 分支,依次 `git reset --soft HEAD^` 所有功能提交, 使得fea-1 指向master旧提交节点(此时 fea-1等价于master,但是落后了若干个commit) + - stash 所有修改, 合并 master, pop 并提交 +7. 切换 dev 合并 fea-1 等待下次上线 +8. 此时就保证了 master 分支和 错误提交前 基本一致,但是丢失了 fea-1 的功能,其他人需要开发新功能 合并入 fea-1 即可 +9. dev 上线后, 合并入master, master分支就具有了 fea-1 fea-2 分支的功能 + +> 经过这系列操作后,master分支会多出很多 revert 提交,但是不会对其他人的部分产生影响,最后功能上线后,也能正确的合入master + +--- + +# GUI + +> 诚然, 命令行是高效的, 在单兵作战或者说没有使用多分支的情况下是没有问题的 +> 当多人协作时, 需要Review代码,处理代码冲突, 查看每个人每次提交的修改内容时, 图形化工具会更高效 + +> [Git官方收纳的GUI列表](https://git-scm.com/downloads/guis) +> [client on linux ](https://unix.stackexchange.com/questions/144100/is-there-a-usable-gui-front-end-to-git-on-linux) + +giggle + +## git-cola + +> [Github: repo](https://github.com/git-cola/git-cola) `轻量, 简洁, 跨平台` + +从源码安装是最快最简单的, 而且能安装到最新的 + +1. git clone --depth 1 git://github.com/git-cola/git-cola.git +2. sudo make prefix=/usr install + +> Tips + +- [X] 无法输入中文问题: 需要安装 fcitx-qt5 模块 + +## Gitnuro + +> [Github: Gitnuro](https://github.com/JetpackDuba/Gitnuro) `Java17 + Compose` + +## GitBlade + +- 功能强大 付费软件 Sublime作者所开发 + +## gitg + +> [Official](https://wiki.gnome.org/Apps/Gitg) + +## tig + +> [Github](https://github.com/jonas/tig) + +## Guitar + +> [Github](https://github.com/soramimi/Guitar) + +## Gittyup + +> [Gittyup](https://github.com/Murmele/Gittyup) + +> [Github](https://github.com/gitahead/gitahead) | [Official Site](https://gitahead.com) + +## SourceTree + +> [Official site](https://sourcetreeapp.com) 仅支持 Windows 和 Mac + +--- + +# 小规模团队使用码云组织的总结 + +> `master`发行分支 `dev`开发主分支 `dev-*`开发者分支 `fea-*`开发者自己的功能性分支 + +- 在码云上创建私有仓库,然后管理成员,将开发者一一邀请进来,然后这时候就有了一个问题: + - 所有的开发者都具有master的所有权限,所以这时候就会很容易出现冲突,所以就需要设置master和开发主分支dev为保护模式,只有管理员负责进行推送 + - 管理员, 新建若干分支:`git branch 分支` 提交到远程 `git push --all` + - 对应的开发者克隆项目,然后 `git checkout 对应的自己的分支` 就可以开始工作了 + - ( 如果没有分支就下拉命令 `git fetch origin 对应的分支`) + - 然后各个开发者写自己的,然后提交 `git push` 就行了 + - 管理员需要 `git fetch origin 分支`得到所有分支 + - 针对每个分支进行拉取: 切换过去 `git checkout 开发者分支`,然后 `git pull 开发者分支`下拉最新 + - 然后选择合并 `git merge --no-ff 开发者分支` ,处理冲突然后提交 + - 开发者下拉自己的分支 或者开发主分支 dev 即可 + +--- + +`分支的处理的一次实验 2017-10-21 23:57:34` + +- `git fetch --all` 获取远程所有分支(新分支) +- `git pull --all` 获取所有分支最新提交 这个就会自动合并???越来越不理解了 +- dev-test 分支进行修改,然后提交一次,然后push +- master: `git merge --no-ff dev-test` 进行合并,就会在分支图上得到一个环 + + - master 分支本地会多出2个提交 +- dev-test 进行修改,然后1次提交,push +- master : `git pull origin dev-test ` 执行merge命令就会提示没有可以合并的修改。 + + - 这是为什么???? + +## 最终方案 + +`双方都有修改` + +- 开发人员提交完后,主分支管理人员切换到开发人员的分支然后 `git pull 开发人员分支`,然后切换回主分支上 `git merge --no-ff 开发人员分支`(填写注释) 然后push + - 然后切换到开发人员分支上执行 `git merge master 然后 git push` 还是 `git pull origin master` + - 然后通知开发人员下拉其自己的开发分支即可 + +`只有一方修改` + +- 主分支进行修改了开发分支没有动,那么开发分支 直接下拉 `git pull origin master`下拉修改代码即可 +- 如果是开发分支修改,主分支没有动,那么管理员负责切换到开发分支 然后pull 然后merge 然后 push 然后切换开发分支 然后 pull diff --git a/Skills/Vcs/Readme.md b/Skills/Vcs/Readme.md new file mode 100644 index 0000000..3f9916f --- /dev/null +++ b/Skills/Vcs/Readme.md @@ -0,0 +1,45 @@ +# 常见VCS + +## Git +> 分布式的去中心化的, 大多数操作是本地化操作, 速度快, 更方便 +> 缺点有 处理大仓库时会很慢, 没有访问权限的控制, 不适用二进制文件管理, submodules使用不方便 +- 最大的区别是其他的 VCS 都是 一个增量式的文件集合, git 是文件的一系列快照, 类似于 AUFS 文件系统一层一层那样 + +## Repo +Repo 简化了跨多个代码库运行的流程,与 Git 相辅相成 + +## SVN +> [Svn笔记](/Skills/Vcs/Svn.md) + +1. 中心化的, 代码统一保存, 如果中心发生错误, 代码会全部毁掉, 提交是必须要和服务端通信才能完成 +2. 允许部分的进行修改, 下拉, 提交. 而对于Git来说一个仓库就是一个整体(Git submodule 目前也能完成, 但是还是没有SVN灵活) +3. 优点: 能够精确控制每个目录的每个人的访问权限 + +************************ + +**`Git和SVN同时使用`** + +可以通过 git-svn 使用Git的命令与SVN服务器进行交互 +> [Official doc: git-svn](https://git-scm.com/docs/git-svn) + +> 但是个人目前在用的方式是直接 git 和 svn 一起用, 因为项目只能用SVN的原因 +> [参考: 为啥要同时用 SVN 和 Git 管理项目](https://www.cnblogs.com/dasusu/p/7774469.html) + +1. 避免 CRLF LF 问题 +```sh + git config --global core.autocrlf false + git config --global core.safecrlf false +``` +1. 互相忽略各自配置目录 .svn .git + +- 至此, 就能和团队保持一致的使用SVN, 然后自己多任务开发时, 又能使用git优秀的分支模型 +- 当然该场景是有限的, 也就是说只有你一个人在用git 而且团队中使用SVN时没有使用SVN的分支模型, 这个是没有问题的 + - 如果SVN也用了分支, 那么就要命了, 这么多分支和状态, 要靠大脑记住实时的状态就.... +- 还有一个点就是分工比较明确,开发中没有互相依赖,不然就需要在Git SVN都需要频繁解决冲突 +- 所以这只是权宜之计 + +## PerForce + +## Mercurial + +## CVS diff --git a/Linux/Vcs/Svn.md b/Skills/Vcs/Svn.md similarity index 50% rename from Linux/Vcs/Svn.md rename to Skills/Vcs/Svn.md index 9075fbc..7e6ca0e 100644 --- a/Linux/Vcs/Svn.md +++ b/Skills/Vcs/Svn.md @@ -1,25 +1,42 @@ -`目录 start` - -- [SVN](#svn) - - [服务端安装](#服务端安装) - - [svnadmin使用](#svnadmin使用) - - [备份和恢复](#备份和恢复) - - [远程](#远程) - - [客户端安装](#客户端安装) - - [使用](#使用) - - [添加文件](#添加文件) - - [配置忽略文件](#配置忽略文件) - - [提交](#提交) - - [查看仓库](#查看仓库) - - [处理冲突](#处理冲突) - - [树冲突](#树冲突) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: SVN +date: 2018-11-21 10:56:52 +tags: + - 基础 +categories: + - 版本控制 +--- + +**目录 start** + +1. [SVN](#svn) + 1. [服务端安装](#服务端安装) + 1. [svnadmin使用](#svnadmin使用) + 1. [备份和恢复](#备份和恢复) + 1. [远程](#远程) + 1. [客户端安装](#客户端安装) + 1. [GUI](#gui) + 1. [使用](#使用) + 1. [添加文件](#添加文件) + 1. [配置忽略文件](#配置忽略文件) + 1. [提交](#提交) + 1. [查看仓库](#查看仓库) + 1. [处理冲突](#处理冲突) + 1. [树冲突](#树冲突) + 1. [回滚到指定版本](#回滚到指定版本) +1. [Tips](#tips) + 1. [下载Github子目录](#下载github子目录) + +**目录 end**|_2020-04-27 23:42_| **************************************** # SVN > 传统的中心化版本控制工具,能够精确控制每个目录的权限, Apache顶级项目 > [SVN 官网](http://subversion.apache.org/) | [SVN中文网](http://www.svn.org.cn) [Subversion 与版本控制 书籍](http://svnbook.red-bean.com/) +> [参考: SVN与Git比较的优缺点差异](https://www.cnblogs.com/Sungeek/p/9152223.html) + +> svn 不能提交单个文件里的部分提交, 要么就整个文件提交, 要么不提交, git则可以 + ## 服务端安装 > 安装 svnadmin @@ -48,9 +65,11 @@ **Ubuntu** > `sudo apt install subversion` 安装后可使用的命令就是svn +### GUI + ********************************* ## 使用 -> [参考博客: linux-svn命令](http://blog.csdn.net/gexiaobaohelloworld/article/details/7752862) | [SVN常用命令](http://www.cnblogs.com/SanMaoSpace/p/5102878.html) +> [参考: linux-svn命令](http://blog.csdn.net/gexiaobaohelloworld/article/details/7752862) | [SVN常用命令](http://www.cnblogs.com/SanMaoSpace/p/5102878.html) > | [Linux下SVN客户端使用教程(全)](https://blog.csdn.net/qq_27968607/article/details/55253997) - _下拉代码_ `svn co URL` @@ -63,20 +82,35 @@ - _更新本地代码_ `svn up` > svn update如果后面没有目录,默认将当前目录以及子目录下的所有文件都更新到最新版本 -- _删除文件_ `svn remove path` +- _删除文件_ `svn remove|rm path` -> [参考博客: svn下忽略文件和文件夹](http://blog.sina.com.cn/s/blog_6e165cc101017m0j.html) -> [参考博客: svn 忽略文件、文件夹](https://ztgame.shenyu.me/svn/svn-ignore.html) +> [参考: svn下忽略文件和文件夹](http://blog.sina.com.cn/s/blog_6e165cc101017m0j.html) +> [参考: svn 忽略文件、文件夹](https://ztgame.shenyu.me/svn/svn-ignore.html) ### 配置忽略文件 +> svn:ignore 和 svn:global-ignores + +1. 作用范围 + - svn:ignore:只对当前目录有效; + - global-ignores:是全局有效,就是所有目前都有效; +1. 配置方式 + - svn:ignore:必须在项目的每个工作目录都要设置;相同配置时,优先级高于全局的 + - global-ignores:只需要配置一次; + - `svn propedit svn:ignore 项目文件夹` 会打开默认配置, 和gitignore一样的配置, 然后保存即可 - - 文件夹就是项目, 所以要在项目根目录的上级目录之心这个命令 + - 文件夹就是项目, 所以要在项目根目录的上级目录执行这条命令 - 如果上面没有调起编辑器, 就要在 .bashrc 中 `export SVN_EDITOR=vim` -- 然后提交到仓库, 即可完成 忽略文件的配置, 为了可见性, 一般和.gitignore一样的配置即可 + +- 然后提交到仓库( svn co -m "xxx" ), 即可完成 忽略文件的配置, 为了可见性, 一般和.gitignore一样的配置即可 - 导入忽略文件 `svn propset -F .svnignore .` ### 提交 -> [参考博客: SVN提交注意点](http://www.cnblogs.com/masb/archive/2012/01/12/2320182.html) +> [参考: SVN提交注意点](http://www.cnblogs.com/masb/archive/2012/01/12/2320182.html) + +> 部分提交 [参考](https://blog.mimvp.com/article/15666.html) + +- 提交指定文件以及目录 `svn ci readme.md src/* -m "1.update readme 2.add src change"` +- `svn ci -m "msg"` 提交所有已添加到版本库的操作(新建, 修改, 删除) ### 查看仓库 > [查看最后修改的文件](https://java-er.com/blog/svn-last-files/) | [svn历史版本对比以及还原到历史版本](http://www.cnblogs.com/simonote/articles/3086717.html) @@ -85,15 +119,50 @@ - `svn cat -r 版本号 文件` 输出某个版本的某文件(文件必须在本地存在) - `svn diff -r 版本号:版本号 文件` 对比两个版本的某文件 +> svn st + +![svn code](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Tech/svn/svn_codes.png) + ### 处理冲突 > 冲突的产生: 因为多个开发人员进行修改了同一个文件夹(修改,删除文件夹), 同一个文件. #### 树冲突 > 多个开发人员修改了同一个文件夹, 并且一方修改, 一方做了删除 -> [参考博客: SVN 树冲突解决详解](https://blog.csdn.net/xgf415/article/details/75196714) -> [参考博客: 使用SVN命令行解决树冲突(tree conflict)](https://www.jianshu.com/p/e3cc83ca512d) +> [参考: SVN 树冲突解决详解](https://blog.csdn.net/xgf415/article/details/75196714) +> [参考: 使用SVN命令行解决树冲突(tree conflict)](https://www.jianshu.com/p/e3cc83ca512d) 1. 标记冲突已解决(使用本地的状态, 本地该文件的状态是Delete, 提交后服务端对应的文件就会被删除) - `svn resolve --accept=working file/dir` +### 回滚到指定版本 + +```sh + svn update + svn merge -r 100:99 . + svn ci -m "rolled back to r99" +``` + +# Tips +## 下载Github子目录 +> [Doc](https://help.github.com/en/articles/support-for-subversion-clients) + +- svn co URL + - /tree/master/ 换成 /trunk/ + - /tree/master/ 换成 /branches/branchname/ + +************************ + +> 删除认证信息 + +`rm -rf ~/.subversion/auth` + +*********************** + +1. 表现: IDEA 报错 uncongnized character scheme in 2019-01-03_15:25:42.log +1. 后果: svn 所有操作都无响应, 相当于 idea的 svn 功能废了 +1. 解决方案: 直接删除 .svn 目录, svn co 另一个目录, 把那份 .svn 目录拿过来 +1. 分析: 很有可能文件名, 或者日志内容具有 svn 不认识的编码, 导致的问题 + +********** + diff --git a/Skills/Vcs/img/git-merge-situation.drawio.svg b/Skills/Vcs/img/git-merge-situation.drawio.svg new file mode 100644 index 0000000..1a4dd2b --- /dev/null +++ b/Skills/Vcs/img/git-merge-situation.drawio.svg @@ -0,0 +1,254 @@ + + + + + + + + + +
+
+
+ D +
+
+
+
+ + D + +
+
+ + + + + + + + +
+
+
+ B +
+
+
+
+ + B + +
+
+ + + + + + +
+
+
+ E +
+
+
+
+ + E + +
+
+ + + + + + +
+
+
+ C +
+
+
+
+ + C + +
+
+ + + + + + + + +
+
+
+ A +
+
+
+
+ + A + +
+
+ + + + + + +
+
+
+ E' +
+
+
+
+ + E' + +
+
+ + + + +
+
+
+ ? +
+
+
+
+ + ? + +
+
+ + + + + + +
+
+
+ 时间推进 +
+
+
+
+ + 时间推进 + +
+
+ + + + +
+
+
+ master +
+
+
+
+ + master + +
+
+ + + + +
+
+
+ dev +
+
+
+
+ + dev + +
+
+ + + + + + +
+
+
+ Revert +
+
+
+
+ + Revert + +
+
+ + + + +
+
+
+ add http.js +
+
+
+
+ + add http.js + +
+
+ + + + +
+
+
+ add main.js +
+
+
+
+ + add main.js + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + +
\ No newline at end of file diff --git a/Skills/Web/GraphQL.md b/Skills/Web/GraphQL.md new file mode 100644 index 0000000..6196e75 --- /dev/null +++ b/Skills/Web/GraphQL.md @@ -0,0 +1,26 @@ +--- +title: GraphQL +date: 2018-11-21 10:56:52 +tags: + - GraphQL + - 基础 +categories: + - 数据库 +--- + +💠 + +- 1. [GraphQL](#graphql) + +💠 2024-10-12 11:35:37 +**************************************** +# GraphQL +> [Official Site](https://graphql.cn/) Graph Query Language 简称 GQL 面向图结构的查询语言 + +相较于传统的API,此方案创造性了提出 要什么就给什么 将接口的主导方从提供方转到调用方,优劣也是很明显,因此争议也很多,酌情按需使用。 + + +> [相关社区: GQL Standard](https://www.gqlstandards.org/) + +> [参考: 30分钟理解GraphQL核心概念](https://segmentfault.com/a/1190000014131950) +> [参考: Github 为什么开放了一套 GraphQL 版本的 API?](https://www.oschina.net/news/78302/why-github-open-graphql-api?p=1) diff --git a/Java/RESTful.md b/Skills/Web/RESTful.md similarity index 86% rename from Java/RESTful.md rename to Skills/Web/RESTful.md index 26abee9..0004322 100644 --- a/Java/RESTful.md +++ b/Skills/Web/RESTful.md @@ -1,21 +1,33 @@ -`目录 start` - -- [RESTful风格](#restful风格) - - [Rest](#rest) - - [资源 Resources](#资源-resources) - - [表现层 Representation](#表现层-representation) - - [状态转化 State Transfer](#状态转化-state-transfer) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: RESTful +date: 2018-12-20 10:43:20 +tags: + - Restful +categories: + - Web +--- + +**目录 start** + +1. [RESTful风格](#restful风格) + 1. [Rest](#rest) + 1. [资源 Resources](#资源-resources) + 1. [表现层 Representation](#表现层-representation) + 1. [状态转化 State Transfer](#状态转化-state-transfer) + +**目录 end**|_2020-04-27 23:42_| **************************************** # RESTful风格 > 要理解RESTful架构,最好的方法就是去理解Representational State Transfer这个词组到底是什么意思,它的每一个词代表了什么涵义。 +> [知乎: 使用通俗的语言解释RESTful](https://www.zhihu.com/question/28557115) + +> [参考: RPC vs REST vs GraphQL ](https://segmentfault.com/a/1190000013961872) + ## Rest > Representational State Transfer 表现层状态转化 > 如果一个架构符合REST原则,就称它为RESTful架构。 - > [参考自 使用 Python 和 Flask 设计 RESTful API](http://www.pythondoc.com/flask-restful/first.html) - 六条设计规范定义了一个 REST 系统的特点: - 客户端-服务器: 客户端和服务器之间隔离,服务器提供服务,客户端进行消费。 @@ -26,9 +38,7 @@ - 按需编码: 服务器可以提供可执行代码或脚本,为客户端在它们的环境中执行。这个约束是唯一一个是可选的。 -> [参考博客: 理解RESTful架构](http://www.ruanyifeng.com/blog/2011/09/restful.html) -"QuestionSeconds":1, -"Question":"请按顺序查找数字", +> [参考: 理解RESTful架构](http://www.ruanyifeng.com/blog/2011/09/restful.html) > [RESTful Best Practices](https://segmentfault.com/a/1190000002949234) > [理解本真的REST架构风格](http://www.infoq.com/cn/articles/understanding-restful-style) @@ -52,7 +62,9 @@ URI只代表资源的实体,不代表它的形式。严格地说,有些网 互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。 而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。 它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。 + ***************** + > [阮一峰 RESTful API 设计指南](http://www.ruanyifeng.com/blog/2014/05/restful_api.html)`很细致的说明该规范` diff --git a/Skills/Web/WebAssembly.md b/Skills/Web/WebAssembly.md new file mode 100644 index 0000000..f9a48e9 --- /dev/null +++ b/Skills/Web/WebAssembly.md @@ -0,0 +1,17 @@ +--- +title: WebAssembly +date: 2020-02-16 22:03:34 +tags: +categories: +--- + +**目录 start** + +1. [WebAssembly](#webassembly) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# WebAssembly +> 字节码技术 +> [ WebAssembly 实践:如何写代码 ](https://segmentfault.com/a/1190000008402872) +> [MDN WebAssembly](https://developer.mozilla.org/en-US/docs/WebAssembly) diff --git a/Website.md b/Skills/Website.md similarity index 56% rename from Website.md rename to Skills/Website.md index 50b0561..51ad4ec 100644 --- a/Website.md +++ b/Skills/Website.md @@ -1,64 +1,57 @@ -`目录 start` - -- [常用的网站](#常用的网站) - - [软件下载站](#软件下载站) - - [国内镜像源站点](#国内镜像源站点) - - [镜像站点](#镜像站点) - - [特别网站](#特别网站) -- [社区网站](#社区网站) - - [娱乐](#娱乐) -- [资源网](#资源网) - - [字典网站](#字典网站) - - [图书馆](#图书馆) -- [学习网站](#学习网站) - - [并发](#并发) - - [语言](#语言) - - [Java](#java) - - [Python](#python) - - [Erlang](#erlang) - - [Elixir](#elixir) - - [Linux](#linux) - - [Go](#go) - - [Git](#git) - - [Github相关](#github相关) - - [持续集成](#持续集成) - - [前端](#前端) - - [素材网](#素材网) - - [web站点分析](#web站点分析) - - [人工智能](#人工智能) - - [在线工具](#在线工具) -- [工具软件](#工具软件) - - [IDE](#ide) - - [VCS](#vcs) - - [网络](#网络) - - [穿透工具](#穿透工具) - - [抓包工具](#抓包工具) - - [Web服务器](#web服务器) - - [镜像](#镜像) - - [安全](#安全) - - [授权](#授权) - - [构建工具](#构建工具) - - [文档处理](#文档处理) - - [UML](#uml) - - [Log文件](#log文件) - - [异步](#异步) - - [响应式](#响应式) - - [Database](#database) - - [前端](#前端) - - [富文本编辑器](#富文本编辑器) - - [模拟器](#模拟器) - - [Other](#other) -- [安卓手机](#安卓手机) - - [Google](#google) - -`目录 end` |_2018-09-12_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) +--- +title: 常用网站 +date: 2018-11-21 10:56:52 +tags: +categories: +--- + +💠 + +- 1. [常用的网站](#常用的网站) + - 1.1. [国内镜像源站点](#国内镜像源站点) + - 1.2. [特别网站](#特别网站) + - 1.3. [托管](#托管) + - 1.3.1. [代码托管](#代码托管) +- 2. [社区网站](#社区网站) + - 2.1. [国外社区](#国外社区) + - 2.2. [娱乐](#娱乐) +- 3. [下载资源网](#下载资源网) + - 3.0.1. [软件下载站](#软件下载站) + - 3.1. [字典网站](#字典网站) + - 3.1.1. [英文字典](#英文字典) + - 3.2. [图书馆](#图书馆) +- 4. [学习网站](#学习网站) + - 4.1. [并发](#并发) + - 4.2. [语言](#语言) + - 4.2.1. [Java](#java) + - 4.2.2. [Python](#python) + - 4.2.3. [Erlang](#erlang) + - 4.2.3.1. [Elixir](#elixir) + - 4.3. [持续集成](#持续集成) + - 4.4. [前端](#前端) + - 4.4.1. [素材网](#素材网) + - 4.5. [web站点分析](#web站点分析) + - 4.6. [人工智能](#人工智能) + - 4.7. [在线工具](#在线工具) +- 5. [工具软件](#工具软件) + - 5.1. [网络](#网络) + - 5.1.1. [穿透工具](#穿透工具) + - 5.1.2. [Web服务器](#web服务器) + - 5.1.3. [镜像](#镜像) + - 5.2. [文档处理](#文档处理) + - 5.2.1. [Log处理](#log处理) + - 5.3. [异步](#异步) + - 5.4. [响应式](#响应式) + - 5.5. [前端](#前端) + - 5.5.1. [富文本编辑器](#富文本编辑器) + - 5.6. [模拟器](#模拟器) + - 5.7. [Other](#other) +- 6. [安卓手机](#安卓手机) + - 6.1. [Google](#google) + +💠 2024-09-20 18:39:25 **************************************** # 常用的网站 -## 软件下载站 -- [https://en.softonic.com/](https://en.softonic.com/)`windows软件居多` -- [VirtualBox和VMWare的镜像网](http://www.osboxes.org/) -- [安卓在X86设备上的ISO下载](http://www.android-x86.org/download) - ## 国内镜像源站点 - [网易](http://mirrors.163.com/)`下载系统是 *-cd 的文件` - [清华大学](https://mirrors.tuna.tsinghua.edu.cn/) @@ -71,12 +64,6 @@ - [mycat镜像站](http://dl.mycat.io/) -### 镜像站点 -> Google -- [1](https://rain.likeso.ml/) -- [2](https://plus.likeso.ml/) -- [3](http://www.kaseng.top/) - ****************************** ## 特别网站 - [ping domain true ip](ping.eu/ping/) @@ -99,6 +86,21 @@ - [手机参数对比网站](http://product.pconline.com.cn/mobile/pk/param/1062508_1091427_1101075_604928_1035872.html) - [文档汉化](http://doczh.cn/) +- [www.differencebetween.com](https://www.differencebetween.com/) +- [alternativeto](https://alternativeto.net/) `需找一个程序的可替代程序` + +- [免费图片](https://pixabay.com/) + +## 托管 + +> [libraries.io](https://libraries.io/)`三方库` + +### 代码托管 +- [Github](https://github.com/) + - [www.gitmemory.com](https://www.gitmemory.com)`分析用户,仓库数据` + +********************************* + # 社区网站 - [掘金门户网](http://e.xitu.io) - [吾爱破解](https://www.52pojie.cn/) @@ -118,30 +120,54 @@ - [IBM 开发社区](https://www.ibm.com/developerworks/cn/) - [daocloud社区](http://open.daocloud.io/) - [techug.com](http://www.techug.com)`技术博客` +- [https://www.yxgapp.com/](https://www.yxgapp.com/) +- [www.52im.net](http://www.52im.net/)`IM社区` +- [freebuf](https://www.freebuf.com/)`安全` + +## 国外社区 +Podcast、Reddit 以及TechRadar +- [Hacker News](https://news.ycombinator.com) ## 娱乐 -- [数字尾巴](http://www.dgtle.com/portal.php) `小清新的数字技术` +- [数字尾巴](http://www.dgtle.com) `小清新的数字技术` - [粤语影院](http://www.yueyuyy.com/) +- [子米影院](http://zimiyy.com/) +- [tv](http://tv.freeshadow.cn/) + ******************************** -# 资源网 + +# 下载资源网 - [脚本之家](http://www.jb51.net/)`用windows的时候一直以为是下盗版软件的地方, 现在才知道还有各种编程学习资源` -- [河东软件园](http://www.pc0359.cn/)`和上面一样` -- [搜网盘](https://www.57fx.com/search/) + +### 软件下载站 +- [https://en.softonic.com/](https://en.softonic.com/)`windows软件居多` +- [VirtualBox和VMWare的镜像网](http://www.osboxes.org/) +- [安卓在X86设备上的ISO下载](http://www.android-x86.org/download) + ## 字典网站 -> [参考博客: 搜索列表](http://www.lingoes.cn/zh/translator/webengine.htm) +> [参考: 搜索列表](http://www.lingoes.cn/zh/translator/webengine.htm) - [海词词典](http://dict.cn/) - [汉典](http://www.zdic.net/) -**英文字典** +### 英文字典 - [https://www.merriam-webster.com](https://www.merriam-webster.com) - [https://www.dictionary.com](https://www.dictionary.com) +- [https://www.macmillandictionary.com/](https://www.macmillandictionary.com/) ## 图书馆 - [维基教科书](https://zh.wikibooks.org) +- [经典技术书籍 PDF 文件](https://github.com/awesome-programming-books/awesome-programming-books.github.io) ********************************* # 学习网站 + +- [杭电ACM题库](http://acm.hdu.edu.cn/listproblem.php?vol=1) +- [hackerrank](https://www.hackerrank.com) +- [leetcode 中国](http://leetcode-cn.com/) +- [lintcode](http://www.lintcode.com) +- [javatpoint](https://www.javatpoint.com) + ## 并发 - [并发编程网 http://ifeve.com](http://ifeve.com/) - [IBM社区](https://www.ibm.com/developerworks/cn/) @@ -163,36 +189,12 @@ #### Elixir > [官网](https://elixir-lang.org/) -## Linux -- [运维生存时间](http://www.ttlsa.com)`含大量运维干货` -- [撸Linux](https://www.lulinux.com/)`非理性言论?` -- [杭电ACM题库](http://acm.hdu.edu.cn/listproblem.php?vol=1) -- [Linux命令大全](http://man.linuxde.net/) `Linux命令教程` -- [RUNOOB.COM](http://www.runoob.com) `各种技术学习 文档资源` -- [asciinema](https://asciinema.org) `终端屏幕录制和分享网` -- [Linux中国开源社区](https://linux.cn/) -- [LinuxTOY 是一个致力于提供 Linux 相关资讯的专题站点。](https://linuxtoy.org/) - - [内容Github源](https://github.com/LinuxTOY/linuxtoy.org) - -### Go -- [go](http://www.runoob.com/go/go-tutorial.html) -- [在线编译执行 学习go](http://www.vaikan.com/go/a-tour-of-go/#1) `在线编辑运行` - -******************************************** -## Git -- [廖雪峰学习网站](https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000) - -### Github相关 -- [Github-page优秀示例](https://github.com/collections/github-pages-examples)`大厂的网页` - ## 持续集成 - [国内一个产品](https://flow.ci/) | [文档](https://github.com/FlowCI/docs/blob/master/intro_base.md) ************************************** ## 前端 - [json.cn](https://www.json.cn/)`json工具` -- [pixlr](https://pixlr.com/editor/)`简直神器啊, 在线使用PS, woc!!!` -- [PS在线工具](https://www.photoshop.com/tools?wf=editor)`功能有限,也可以玩玩` - [bootcdn](http://www.bootcdn.cn/) `常用js css的cdn` - [填充图](https://placeholder.com/) - [jshell](https://fiddle.jshell.net/)`可以在线练习HTML CSS Js` @@ -215,43 +217,22 @@ - [开源中国](http://tool.oschina.net/) - [脚本之家](http://tools.jb51.net/) - [tool.lu](https://tool.lu/)`小而全` +- [codeif](https://unbug.github.io/codelf/)`搜索变量类等命名单词` -************************************ ************************************ # 工具软件 - -## IDE -- [IDEA](https://www.jetbrains.com/idea/) - - [lanyus](http://blog.lanyus.com/6.html) -- [Eclipse](http://www.eclipse.org/) - - [中科大eclipse镜像源](http://mirrors.ustc.edu.cn/eclipse/technology/epp/downloads/release/) - -************************** -## VCS -- [SVN](https://subversion.apache.org/) - - [submin](https://supermind.nl/submin/)`SVN管理的Web界面` -- [git](https://git-scm.com/) `最好用的vcs` - -_自建Git服务_ -- [gogs](https://github.com/gogits/gogs) `自建git服务器` -- [gitea](https://github.com/go-gitea/gitea) `gogs加强` - - [docker安装](https://docs.gitea.io/zh-cn/install-with-docker/) - +> [runnable.com](https://runnable.com) +- [coder.com](https://coder.com/)`BS模式的 VsCode` +- [腾讯QQ浏览器在线工具箱平台](https://tool.browser.qq.com/) *************** ## 网络 ### 穿透工具 - [lanproxy](https://github.com/ffay/lanproxy) `将局域网个人电脑、服务器代理到公网的内网穿透工具` -- [ngrok](https://ngrok.com/)`随机域名到穿透到内网 eg: ngrok http 8080` +- [ngrok](https://ngrok.com/)`随机域名穿透到内网 eg: ngrok http 8080` - [frp](https://diannaobos.com/frp/)`自己服务器外网到内网穿透` - [下载地址](https://file.diannaobos.com/frp_releases/)[使用帮助](https://diannaobos.com/post/470.html) -### 抓包工具 -- [whistle](https://github.com/avwo/whistle) `nodejs 平台的抓包工具` -- [fiddler](https://www.telerik.com/fiddler)`windows平台抓包工具` -- [charles](https://www.charlesproxy.com/)`跨平台收费` - - [咳咳](http://charles.iiilab.com/) - ### Web服务器 - [tomcat](http://tomcat.apache.org/)`Apache基金会` - [jetty](https://www.eclipse.org/jetty/)`Eclipse基金会` @@ -260,29 +241,9 @@ _自建Git服务_ ### 镜像 > [浙大镜像工具](https://github.com/aploium/zmirror) -## 安全 -### 授权 -- [jwt](https://jwt.io)`JSON WEB TOKEN` - -### 构建工具 -- [Maven](http://maven.apache.org/) -- [service.gradle.org](http://service.gradle.org)`Official download` - - [gradle-tomcat-plugin](https://github.com/bmuschko/gradle-tomcat-plugin)`gradle中tomcat插件` -- [sbt](https://github.com/sbt/sbt) - ## 文档处理 -- [Gitbook](https://github.com/GitbookIO/gitbook) - - [gitbook-use](https://github.com/zhangjikai/gitbook-use)`Gitbook的使用` -- [pandoc](https://github.com/jgm/pandoc) `一个文档格式转换的软件,markdown docx` -- [retext](https://github.com/retext-project/retext)`markdown书写软件` -- [API文档管理](http://doclever.cn/controller/index/index.html) - -### UML -- [免费UML软件统计 博客](http://blog.csdn.net/s464036801/article/details/8469166) -- [bouml](http://www.bouml.fr/download.html#Debian) `官方网站下载` -- [argouml](http://argouml.tigris.org/) `argouml官网` - -### Log文件 + +### Log处理 > 方便的查看Log文件 - [otroslogviewer](https://github.com/otros-systems/otroslogviewer) - [chainsaw](http://logging.apache.org/chainsaw/2.x/) @@ -295,14 +256,6 @@ _自建Git服务_ ## 响应式 - [vertx](http://vertx.io/)`构建响应式应用程序` -**************************** -## Database -- [sssdb](https://github.com/ideawu/ssdb) `键值对数据库` -- [MySQL官方下载地址](https://dev.mysql.com/downloads/mysql/) - - [Mysql-Font](https://github.com/NilsHoyer/MySQL-Front) `连接Mysql的客户端` - - [HeidiSQL](https://github.com/HeidiSQL/HeidiSQL)`十分好用` -- [sqlectron](https://github.com/sqlectron/sqlectron-gui) `简单直观的数据库图形化软件` - ******************************* ## 前端 - [Framework7](http://framework7.cn/)`开发类似ios7的web应用(HTML)` @@ -317,15 +270,13 @@ _自建Git服务_ - [ckeditor](https://ckeditor.com/)`开源中国在用的富文本编辑器` - [github地址](https://github.com/ckeditor/ckeditor-dev) - ## 模拟器 - [genymotion](https://www.genymotion.com/) `安卓模拟器` ## Other - [crash](http://www.crashub.org/)`Java中的Shell环境` - # 安卓手机 ## Google -> [google安装器](http://www.goplaycn.com/) \ No newline at end of file +> [google安装器](http://www.goplaycn.com/) diff --git a/Skills/Work/Cooperation.md b/Skills/Work/Cooperation.md new file mode 100644 index 0000000..199eb73 --- /dev/null +++ b/Skills/Work/Cooperation.md @@ -0,0 +1,114 @@ +--- +title: 开发中的团队协作 +date: 2019-01-27 23:37:38 +tags: +categories: + - 团队 +--- + +💠 + +- 1. [协作](#协作) + - 1.1. [代码规范](#代码规范) + - 1.2. [Code Review](#code-review) + - 1.3. [Presentation 技术分享](#presentation-技术分享) + - 1.4. [技术划分 与 负责人制度](#技术划分-与-负责人制度) + +💠 2024-09-20 18:39:25 +**************************************** +# 协作 + +> [那些年,我见过的那些「废柴」](https://wsa.jianshu.io/p/8024880f842b) +- 不会划分优先级 + - 个人成为信息孤岛 + - 太多并行任务,无法真正完成某一项任务,WIP队列严重堆积,成为团队瓶颈 +- 祥林嫂式的抱怨 + - 提出问题 -- 特别是不需要自己去费力解决的问题 -- 是很容易的,而能想到可能的解决方案则相对困难。更进一步,想到一个直观的方案很容易,而给出多个选项,并对比各自的优劣则很困难。最后,如果你想要真正解决问题,或者让工作实际有所推进,那就需要尝试多做一些困难的事情(比如罗列并对比可能的方案,并给出可行的计划),并尝试简化他人的工作。 +- 眼高手低 +- 致命的舒适区 + - 为什么不用redux”(因为redux我比较熟),或者为什么要用“Material-UI”(因为我不会) +- 历史遗留问题 + - 不了解业务(没有意愿理解业务) + - 不了解技术决策(可能由于系统中缺乏诸如ADR(架构决策记录)等) + - 重构能力欠缺 + - 高层次的自动化测试缺失 + + +## 代码规范 +> [Google Guide](https://google.github.io/styleguide/javaguide.html)| [FaceBook Guide](https://github.com/facebook/jcommon/wiki/Coding-Standards) | [阿里巴巴Java开发手册](https://github.com/alibaba/p3c) | [Twitter Java Style Guide](https://github.com/twitter/commons/blob/master/src/java/com/twitter/common/styleguide.md) + +************************ + +## Code Review +> [知乎: 大家的公司的 Code Review 都是怎么做的?遇到过哪些问题?](https://www.zhihu.com/question/41089988/) + +> 相关工具 +- Crucible Atlassian 内部使用 +- Gerrit Google 开源 +- PullRequest, LGTM, Reviewable, Sider : Github体系基于PullRequest模型 +- Phabricator Facebook 开源的 +- [Upsource](https://www.jetbrains.com/upsource/download/#section=docker)`JetBrain` + +> [Code Review 是一场苦涩但有意思的修行](https://mp.weixin.qq.com/s?__biz=MzU4NzU0MDIzOQ==&mid=2247489170&idx=1&sn=e47dcf2227517172ff97105e8a0543d0&chksm=fdeb24f2ca9cade4985b11abd05d4c8e2fdf2cf9b5a73dbe27d320a036d684563679e8d5c565&mpshare=1&scene=1&srcid=&sharer_sharetime=1586018672545&sharer_shareid=246c4b52c1cb45eaa580c985c95107f3#rd) + +- 代码是讲道理的 + - 优秀的代码,就是在小流量、单线程没有问题,在高流量、高并发时还是没有问题,你的限流,你的容灾,你的降级各种导弹防御系统一样自动打开并正确地发挥价值。很多人的思维觉得,代码只要在场景和逻辑上没有问题就行,那是因为夜路走得不够多,还没有碰到鬼。 +- 每一行代码的存在是有意义的 + - 更加严格地说,每一个字符的存在都应该是有意义的。如果某行代码的存在完全是可有可无的,这个时候,我们考虑过 JVM 的感受吗?凭白无故地要编译这些字节码,然后栈进栈出的忙活一阵子,然后告诉它,你的劳动是没有任何价值的。 +- 多个 return 的语句,概率高的一定先进行判定 + - if(condition1) return; if(condition2) return; if(condition3) return ; + - 那么需要评估一下 condition1/2/3 出现概率的大小,概念大的在最前边,尽可能快地进行 return ,不需要进行后续无谓的匹配。 +- 我们比拼的不是代码行数 + - 如果在同样的效果上, 3 行代码能够实现功能的价值,就不应该用 4 行来实现。我们经常说晒出代码行数,并非是单纯地鼓励代码行数多,而是提倡大家去写代码,写优质的代码,优质的代码一定是少即是多的原则。 +- 用户视角的成功与失败 + - 后台调用服务失败,就应该明确告诉前台,服务出错了,这个用户有没有数据。 + - 系统出错的信息给用户看,合适吗?不合适。前后端的用户交界面上,往往飞着两类信息:错误码、错误信息。这样够了吗? + - 用户提示需要额外地再给出来,往往根据不同的错误码,有不同的用户提示,可能是一个多对多的关系。 + - 多个错误码,提示给用户的信息:请输入必填项。多个用户信息,可能也对应一个错误码。一般来说后台承包这三者的联动关系, json 串推送给前端时,前端拿来主义即可。 +- 有重复使用的量一定要找个地方集中隔离 + - 不管是变量,还是常量,工具类,如果是多个地方同时用到,那么如果硬编码在代码或者沉淀在包里,未来一定是一个灾难。 +- 单测没必要代码 Code Review ? + - 单测是否需要 MOCK ,是否进行边界值测试,是否用例覆盖到业务场景,这都也是 CR 的一部分。单测写得好, BUG 肯定少。 +- 良好的日志和异常机制,是不应该出现调试的。 + - 打日志和抛异常,一定要把上下文给出来,否则,等于在毁灭命案现场,把后边处理问题的人,往歪路上带。 + - 别人传一个参数进来,发现是 null ,立马抛出来一个参数异常提示,然后也不返回哪一个参数是 null ,这在调用参数很多的情况下,简直就是字谜游戏一样。 + - 到底是抛异常,还是抛错误码?我不管抛什么,反正错了什么东西,都应该透明出来。到底是抛受检异常,还是非受检异常,我只想说,没有充足的理由,不要乱抛受检异常。异常抛出时,一定要自己消化干净,告诉别人说我的方法签名抛的是 AbcException ,实际运行中,代码某个地方直接抛出 EfgException ,这也是不负责任的。 + +- 吝啬空行 + - 感觉空行是廉价的,到处乱扔是一种;另一种是感觉空行是昂贵的,舍不得用,这种情况更多见。50 行代码没有一个空行,就像英语 50 句话,没有任何标点符号一样。既然标点符号起到隔断和语义区分作用,我们的空行不是同一个道理吗? + - 在以下情形: + 1. 在方法的 return、break、continue、这样断开性语句后必须是空行。 + 2. 在不同语义块之间。 + 3. 循环之前和之后一般有空行。另外,方法和类定义下方就不需要空行了吧。 +- 命名太随意 + - 英语不好的同学,要么用错英文单词,要么翻词典,整出一个专八的词汇,任何人都不认得这个单词,在 CR 时,还需要打开在线翻译时的命名,绝对不是好命名。 当然如果在线翻译都翻不出来的时候,那更头疼。如果表意错误,那更要命。 + + +************************ + +## Presentation 技术分享 +从团队管理角度上运营此活动的目的,氛围,机制,以及个人角度上此活动的ROI,正反馈机制的循环。 + +当下理解: +- 团队: 营造技术精进氛围,集众家所长提前规避技术或管理风险。 +- 个人: 精确学习理解能力,抽象总结能力,表达能力的一次应试。情绪价值的实现,个人影响力的培养。 + +此活动容易陷入的困境: +1. KPI + - 优化措施: +1. 分享人和参会人走过场,不尊重双方的时间成本。 + - 优化措施: +1. 分享质量无法满足团队不同层次成员的需求。 + - 优化措施: + +************************ + +## 技术划分 与 负责人制度 +技术划分: 单纯从技术角度对工作内某个项目各个环节按工种划分协同落地 + - 要求: + - 优点: + - 缺点: +负责人制度: 从项目推进和价值呈现角度选定负责人推送协同人落地 + - 要求: + - 优点: + - 缺点: diff --git a/Skills/Work/EffectiveWork.md b/Skills/Work/EffectiveWork.md new file mode 100644 index 0000000..a2fe856 --- /dev/null +++ b/Skills/Work/EffectiveWork.md @@ -0,0 +1,232 @@ +--- +title: 高效工作 +date: 2018-11-21 10:56:52 +tags: +categories: + - 团队 +--- + +💠 + +- 1. [高效工作](#高效工作) + - 1.1. [高效学习方法](#高效学习方法) + - 1.1.1. [接收信息](#接收信息) + - 1.1.2. [整体理解](#整体理解) + - 1.1.2.1. [细节理解](#细节理解) + - 1.1.2.2. [知识框架](#知识框架) + - 1.1.2.3. [回顾](#回顾) + - 1.1.3. [实践](#实践) + - 1.1.3.1. [费曼技巧](#费曼技巧) + - 1.2. [番茄工作法](#番茄工作法) + - 1.2.1. [规则](#规则) + - 1.2.2. [工作流程](#工作流程) + - 1.2.2.1. [细节](#细节) + - 1.2.3. [相关软件](#相关软件) + - 1.3. [Scrum 敏捷开发](#scrum-敏捷开发) + +💠 2024-05-09 00:18:04 +**************************************** +# 高效工作 +> [知乎: 超强的学习能力是怎样练就的?](https://www.zhihu.com/question/35103080/answer/414223605) +> [思维导图: 高效学习](https://gitee.com/gin9/MindMap/blob/master/Skill/EffictiveLearn.km) + +`不能用战略的懒惰, 掩盖战术的勤奋` + +> TODO 工具 +1. 滴答清单 +1. Todoist +1. `TODO (MicroSoft)` +1. go-for-it + +************************ + +![](/Skills/Work/img/001-effective-learn.km.svg) + +1. 快速进入学习状态 + - 首先,这部分的内容只有一个目的:更快地进入学习的状态。这个时间一定不要太久,准备时间越久,越难长期做下去。 + - 无论做什么事情,立刻,马上,当下,现在就进入它,不要犹豫! + - 不要等到状态再去学习, 而是学习进入状态从而高效率完成 +1. 不要拖延, 可以使用番茄工作法实现 +1. 环境极其重要, 安静的环境, 所以独处是提高自己的能力的最佳环境 + +************************ + + +## 高效学习方法 +> 学习其实是一套完整的流程:接收信息--联想理解--实践反刍。 + +### 接收信息 + +1. 信息的类别 + 1. 随意信息: 一系列事实、日期、定义和规则、缺少逻辑分类,需要死记硬背的知识。 + - 可以通过联想,与其他事物挂钩来增强记忆。 + 1. 观点信息: 存在争论,大家意见不一致的信息,最大的难点是获取信息。 + - 在争论中辩证的思考, 从而接近事情的真相 + 1. 过程信息: 讲述一系列动作、操作,教导行动的信息,如何游泳、编程序、背英语等。 + - 学习这类信息最重要的是 反复的实践 练习 + 1. 具体信息: 能通过感官进行感知的信息 在实际中可以通过看、听、摸等感受到的信息。 + 1. 抽象信息: 缺少与感官的直接联系、逻辑性极强,比如微积分, 编程中的算法 +1. 获取信息的三个目标: 简化 容量 速度 + - 简化表示在获取的信息中将无用的东西尽量删除,只有那些对你形成模型非常有帮助的例子才需要真正学习。如果你觉得不需要掌握,就大胆地跳过它。 + - 明白了简化后,就该获取尽可能多的信息,只读一句话当然没有读完一页理解得透彻,知道得越多,理解得就越深。一年读50本书,总比一年读2、3本强。 + - 获取信息最后一个目标是速度,30分钟读完一本书比一小时读完效率高。速度看起来与容量简化相反,读得越快,漏掉的信息越多,信息获取也越差。 + - 但我是这么认为的:大脑很难读取哪些很难识别的东西,这也是刚接触一门新的学科觉得晦涩难懂,需要花时间理解,读起来慢的原因。但有些人能够做到快速阅读,原因在于他对所阅读的内容有了知识储备,通过自己的知识框架进行联想,不需要花大量时间再来做艰难的认知障碍跨越。 + +1. 康奈尔笔记法 + 1. 按比例将笔记分为三部分: 线索 笔记 总结 + 1. 在第一部分线索中记录知识大纲或关键词,在第二部分记录笔记,第三部分写自己的总结和学习心得(及时把自己的想法和收获记录下来很重要,说不定这就是你以后的写作素材来源)。 + 1. 通过线索中的关键词和知识大纲来回顾和复习具体的内容。 + +******************** +### 整体理解 + +大多数情况下,“知道这是什么,可以用来干嘛,什么时候会用到它”已经足够了。 你要知道自己要读的是哪一类的书;不同的书有不同的阅读方法;使用一个单一的句子,或者最多几句话,来描述整本书的内容;将书中的重要篇章列举出来,说明他们如何按照顺序组成一个整体的架构; + +找出作者要问的问题。 +1. 编者在这张讲了哪几个方面? +1. 他想要回答什么问题? +1. 他为什么要这么安排第一章的内容,这逻辑是什么? + +- 这样做了之后,我无论阅读哪章,哪段,我都知道自己到底在读什么。对“我需要解决一个什么问题? 这个部分处于整体系统的哪个位置?” +- 都能有一个清晰的认识。针对比较难读的书,从头到尾读一遍,碰到不懂的地方不要停下来查询或者思考,这样做的目的有两个, + - 一是为了能够不纠结于细节,第二遍去读的时候能够更好的去理解; + - 二是为了能大体上理解作者较大层次的思想,不纠结于细节。 + +#### 细节理解 + +- 人们更喜欢叙述性学习(Narrative Learning),也就是说人们更喜欢看段子,案例,故事。而不是枯燥的新名词新概念 +- 从理解名词,再到理解最重要的句子中,抓住作者的重要主旨,知道作者的论述是什么,从内容中找出相关的句子,再重新架构出来; +- 最后确定作者已经解决了哪些问题,还有哪些是没解决的。再判断哪些是作者知道他没解决的问题。 + +- 另外,你学习某一本书,如果看不进去,可以就牢牢抓住书本的例题、案例、图表。因为例题讲具体情境、图表具有可视化、案例就是讲具体的运用——这些都比理解文字描述容易的多。 +- 而且,例题里面包含了对关键知识点的运用,案例和图表其实都是为了辅助你理解正文文字内容的。所以,只要我们配合最少量的文字看懂了案例、图表,就达到了对知识的了解,接着我们再去看例题就知道了知识运用场景,之后,我们再反复地做题目,从而达到了对知识点的掌握。 + +> 总结 +1. 根据笔记大纲回顾一下都学了什么知识点,不用具体到每一句话,有收获、有思考即可。 +1. 初期理论不懂是正常的,这个时候不要自我怀疑。往后学,随着知识的积累和认知的完善,这时候再回过去复习,你会有新的理解,当时没想明白的问题也可能就迎刃而解了。 +1. 学了一段时间,发现输入的内容太多了,脑子有点凌乱,这时候正确的做法是:停下来,整理一下思路,看一看笔记,理清楚知识之间的连接,把零散的知识点整理成块,形成整体的认知。也就是接下来的内容——知识框架。 + +#### 知识框架 + +- 诺贝尔奖得主、神经系统科学家埃里克.理查德.坎德尔(EricR.Kandel)在其著作《寻找回忆》(In Search of Memory)中写道: + - “要想得到长久的记忆,大脑在处理接收到的信息时必须足够透彻且深入,这就要求大脑在处理信息时集中精力,并且要将这一信息有意图且系统性地与记忆中已经完善的知识联系起来” +- 只是把知识点摘录下来,没有进行深入的理解,不成体系的孤立知识是很难被记住的,更别说20章笔记,100多个只是点了!这也能解释,为什么很多人明明用心做了读书笔记,还是和没读、泛读一样! + > 在《如何高效学习》中,作者强调的“大脑如何工作“的规律来学习。根据认知神经学领域的知识,我们的记忆在大脑中存储的方式是按照神经元网络存在的。作者这里强调的整体性,是站在整个神经元网络的角度,将新的知识融入整个神经元网络,也即作者一直强调的建立联系(高速公路)。 +- 知识树构建出来后,书中的精华已经被吸收到知识树中,书本完全可以丢在一边了。整个笔记算是初步构建完毕, +- “读的书很快就忘,读书还有什么意义”这种困扰算是解决了一半,但还没有最终结束; 因为,这种构建可能会比较粗糙,接下来要做的就是运用、更新。 +- 读其他书有了更深层的触类旁通,这就是运用; 经常翻阅读书笔记,对现有的知识树不断更新 梳理, 将错误的思想摒弃,这是一个长期的过程。 +- 经常提醒自己读书时候多联结自己的经验,不只是书,还可以推广开来,比如一部电影,看知乎,以及别人的一些经验等等,都要学会拿来为己所用。 + + +- 记笔记最大的作用,就是将整本书的精华进行提炼、压缩,几十万字的书,有用的知识可能才一两万字,甚至更少其他无用的信息被丢弃,这极大方便了我们对知识的加工、理解。 +- 笔记最终整理成什么样子,阅读、理解、加工起决定作用。在多次阅读笔记之后,对全书知识点有了整体的把握,就能根据知识的关联性进行整合,有点类似合并同类项,合并的效果如何,完全依赖自己框架的建立。 +- 现在你手上有一套自己的笔记,检查一下确保自己没有从原材料中借用任何行话。将这些笔记用简单的语言组织成一个流畅的故事。 +- 将这个故事大声读出来,如果这些解释不够简单,或者听起来比较混乱,很好,这意味对这块知识理解不够,还需要做一些工作。 + +#### 回顾 +复习也是有套路的。对大多数人来说,复习不过是再回顾一下自己的所作所为,然后做个总结。但其实,这还不够。 + +在《复盘+:把经验转化为能力》一书中,复盘的基本程序包括四步: +1. 回顾目标:清晰、明确、共识的目标为我们树立了评估结果、分析差异的基准; +1. 评估结果:回想实际的过程,对照目标,看看哪些地方做得好,哪些地方有待改进,找到值得深入挖掘的点; +1. 分析原因:对于差异,深入分析,找到根因,发现真正起作用的关键点,是学习的重要一环; +1. 总结经验:基于差异分析,可以找出利弊得失,从中学到经验教训以及未来行动改善建议。 + +这其实为我们的回顾提供参考: +1. 看全局。整个学习过程我有哪些不理解的地方?为什么理解不了?有哪些地方我是很快理解的?为什么能这么快理解? +2. 看同学。他学习得怎么样了?有哪些思考方式可以借鉴?又有哪些自己一定要避免? +3. 看自己。100分的话,给自己打多少分?哪里需要提升? +而完成这一系列之后,把文档整理封装,划掉一个个知识盲点。 + +**************** + +一是看全局。即对内容重新梳理。 +二是作对比。对比什么呢?看过的其他类似的书,有没有相关的理论,或者类似的观点。谁对谁错? +三是对比自己。其实这就是拆书的原理。将自己看到的、遇到的与书中提到的进行结合,然后找到共鸣,或者指引——只有找到与自己的相关点,才能真正作用于自己的生活工作。 +结合之前看的一些关于整理的文章,并与自己的现状联系,最后完善自己的知识框架。 + +### 实践 + +重述:如果你真的想确保你的理解没什么问题,就把它教给另一个人(理想状态下,这个人应该对这个话题知之甚少,或者就找个 8 岁的孩子)。检测知识最终的途径是你能有能力把它传播给另一个人。 +讨论:找准机会,和同学和朋友就这块知识进行讨论,往往会有意外收获。通过对比,对这个工具有了更全面的认识。 +分享:当对一个问题思考得较为充分之后,不如把它写出来。现在的自媒体有很多,公众号、简书、微博、博客,在一个公共平台上表达出来,因为有“潜在观众”的监督,文字会更有逻辑,思考会更有深度,输出也更有质量。说不定输出多了,还能成为专业领域的IP。 + +#### 费曼技巧 +> [知乎: 号称终极快速学习法的费曼技巧,究竟是什么样的学习方法?](https://www.zhihu.com/question/20576786) + +1. 费曼技巧, 是一种学习方法. +1. 利用费曼技巧可以学习哪些东西? + - 你可以用这种方法学习各种理论概念、操作技能、思维方法、工作方法等。理论概念举例: + - 什么是质量(物理学),怎样理解质量?操作技能举例:什么是五笔输入法,怎样使用五笔输入法? + - 思维方法举例:什么是费曼技巧,怎样使用费曼技巧? + - 工作方法举例:什么是结对编程法,怎样实践结对编程法? +1. 那么, 要想运用费曼技巧学习, 应该如何入手呢? + - 答案是: 从一个你想学习的知识点开始. 比如 "费曼技巧" +1. 入手以后, 下一步该做什么? + - 回答是: 找到与 "费曼技巧" 有关的书籍或阅读材料, 尝试理解它. +1. 要理解到什么程度才算完成呢? + - 回答是: 要经历一个检验理解程度的过程. 设想一下, 如果你要给别人讲解 "费曼技巧" 这个知识点, 你能否顺利地讲出来? + - 如果自认为可以顺利地讲出来, 是不是就算完成呢? 回答是: 不要仅仅是在头脑中假想, 直接把自己要如何向别人讲解 "费曼技巧" 的过程简要地写下来/说出来. +1. 有必要这样做吗? + - 回答是: 有必要, 在写下来/说出来的过程中, 仔细判断自己是顺利地把 "费曼技巧" 讲出来了, 还是卡壳了?如果顺利地讲出来固然好, + - 但如果卡壳了, 也不是坏事. 可以反思一下为何会卡壳, 然后回到 "费曼技巧" 的书籍和阅读材料, 专门阅读出现卡壳的那部分内容, 直到能够顺利讲出来为止. +1. 如果顺利地讲出来了, 是否就算学会了? + - 答案是: 基本学会了, 但还可以试着用更简练的语言解说, 并且尽量去掉书籍和阅读材料中已有的词汇, 完全用自己的话来解读. + +> 我们需要放下书本,去实践,去体验,去观察,去琢磨,去尝试,去创造,去设计,去stay hungry, stay foolish + +费曼的办法是:仔细审阅这篇论文的辅助材料,直到他掌握了相关的知识基础、足以理解其中的艰深想法为止。他是一位教师,能用浅显易懂的语言像初学者阐述一个名词、公式、学科的概念,但这种方法适用于我们任何想学的东西。它能让你的学习能力有质的跃升。 +使用重述、分享、讨论等方式,不仅是学习的妙方,还是窥探不同思维方式的窗口,它让你将想法撕开揉碎,从头重组。这种学习方法会让你对观点和概念有更为深入的理解。重要的是,以这种方式解决问题,你可以在别人不知道他们自己在说什么的情况下,理解这个问题。 + +![学习效率](https://raw.githubusercontent.com/Kuangcp/ImageRepos/master/Learn/learn_rate.jpg) + +**************** + +## 番茄工作法 +> 参考: [知乎:分秒时](https://www.zhihu.com/question/20189826/answer/14283234) +> [知乎专栏:什么是番茄工作法?该如何运用?](https://www.zhihu.com/question/20189826) + +### 规则 +- 规则一:一个番茄时间共30分钟,包括25分钟的工作时间和5分钟的休息时间。 +- 规则二:一个番茄时间是不可分割的。番茄工作法中,时间的的最小单位是一个番茄时间。一个番茄时间不能被划分: + - 不存在半个番茄时间或者一刻钟的番茄时间这样的事情。 +- 规则三:每四个番茄时间后,停止你的工作,进行一次较长时间的休息,大约15到30分钟 +- 规则四:完成一个任务,划掉一个 + +### 工作流程 + +- 计划 + - 在每一天的开始,从Activity inventory 活动清单中选出你今天要完成的任务,优先考虑他们,并把他们记在to do today 工作计划表中; +- 追踪 + - 按照to do today 完成任务,加上计划外的紧急事件(当日需要做的),记录每项任务所耗用的番茄时间; +- 记录档案 + - 每天的结束时,把已经完成的任务记录在档案(自己建,可以用书本,也可用电子表格)中。 +- 分析 + - “你要跟踪和记录什么,取决于你要观察什么”(!每天结束时,记录档案和思考如何提高不应该超过一个番茄时间,否则,再好的工作法也会成为累赘) + +#### 细节 + +1. 第一阶段:弄清完成某项任务所需的时间&把“下一步动作(计划任务)”详细些(路径划)。Eg, 不是“看一本书”而是看这本书的哪个部分“看某本书到第几页或者某章” + +1. 第二阶段:减少被打断的次数我们如何避免来自内部因素的打断: + - 每当你意识到潜在的打断将要来临时,比如肚子饿了,想吃东西,判断是否是今日要做的,是的话,记在计划外事件清单那列,不是的话,记在活动清单。(记录时,写出截至日期或者什么时间去做)·继续当前的番茄时间 。 + - 直到完成这个番茄时间后,再去处理刚才的记录,如果不影响休息,就在休息时间处理,如果超过两分钟的话,就计划一个新的番茄时间。 + - 然后按照重要紧急优先原则,处理to do today 清单上的任务外部因素——被他人打断·方法同内部因素 判断事情的紧急性,记录这件事情,继续自己的番茄时间,当这个番茄时间完成后处理。 + - 让打断变成任务·成功地拖延打断,越迟越好,尽量减轻这些打断的紧急程度,增加这些打断的可控制性。·逐渐地减少专用番茄数&“计划赶不上变化快”,控制打断次数 + +1. 第三阶段:估测某项任务所需要的时间 + - 规则一:如果一项任务的估测值大于5到7个番茄数,那么就打散它 + - 规则二:如果估测值小于一个番茄数,就把几个小任务合成一个大任务·从表中找出相似的任务,把他们加合在一起成一个番茄时间为止·表中没有标识估测值得任务, + - 规则三:估测结果的记录日期、时间、任务类型 、任务描述、估测值、实际、差别 + +### 相关软件 +> [各大平台的相关软件](https://www.douban.com/note/141322878/) 目前使用的是 go for it 感觉比较好用,简洁 + +极简番茄 番茄TODO 番茄土豆 番茄时间钟 + +************************ + +## Scrum 敏捷开发 +> Scrum是敏捷开发的一种,是一种以人为本,迭代式增量软件开发的过程,以英式橄榄球争球队形(Scrum)为名,因此可以想象,整个团队是高效而富有激情的。以人为本,即Scrum开发特别强调沟通,要求团队所有人员都坐着一起工作,通过高效的沟通解决问题。 + +- [Info Q](https://www.infoq.cn/topic/scrum) diff --git a/Skills/Work/InterviewSkill.md b/Skills/Work/InterviewSkill.md deleted file mode 100644 index 28cb6cf..0000000 --- a/Skills/Work/InterviewSkill.md +++ /dev/null @@ -1,174 +0,0 @@ -`目录 start` - -- [面试技巧](#面试技巧) - - [铺垫](#铺垫) - - [简历](#简历) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 面试技巧 -> [参考博客: 面试的时候问:你的期望薪资多少?怎么谈?](http://blog.csdn.net/Z1XpIYDj9sn/article/details/79049373) -> [一个两年Java的面试总结](https://segmentfault.com/a/1190000013550405#articleHeader5) - -## 铺垫 -- 很多同学是0基础开始学习java开发: - * java开发,我到底能不能学好? - * 答案是肯定的。 - 概念很朦胧,软件开发到底是什么? - 软件开发只是一份工作,和送快递的没什么区别。 - 学习软件开发只有一个目的,赚钱! - - * 如果学不好,转行,干别的去!! - 学习软件开发,一定要抱一个目的。死地而后生!!就是没有退路。 - - * 学习的同学,有很多的只是去听、看,但是不练! - 软件开发,最重要的就是练!! - - * 很多人只是在想这些事,很容易。但是真正做到的应该没几个!! - 如果让你通宵玩游戏,你会觉得累吗?但是,现在要你通宵敲代码。 - 心态问题。 - * 沿袭了上学时的状态,老师讲多少,我学多少。 - * 很多不是自己想来,不是主动的。 - - * 每个人都曾经犯过错,也是好事。 - - * 学习过程中,都很努力,但是效果不是特别好? - * 用功不如用心的。 - * 大家一定要培养自己做事情要有套路!! - * 以目的为驱动!!通过目的去分析怎么做!!然后再去做!! - * 练习的时候,要去想。为什么这样做?? - * 举一反三。 - * 必须要练,练代码! - - * 只知道学习,但是到底学的内容是什么?能做什么?有什么效果? - * java,内容多,杂!所有的内容在将来的工作不一定能用!因为不知道将来那些能用的上。 - * 要了解一下将来的工作内容。 - * 市场调查、需求分析、合同定义、需求分析(需求里的难度、风险)、概要设计、详细设计、 - 数据库设计、开发阶段(我们要从事的)、测试阶段(有可能参与)、修改过程、 - 再测试(这个阶段至少要经历3次)、试运行、正式运行 - * 成本/回报比 - * 软件开发工程师,那将来的发展如何? - * 项目经理、数据库设计、系统设计(需要用到哪些技术) - * 部门经理、系统架构师、1.5线(前期规划方案)、配合销售人员做市场(售前) - * 项目总监、大部门经理 - - * 管理方面:人际沟通、管理能力(1、人的管理;2、事的管理) - * 项目方面:人际沟通、项目进度把控、人的管理 - * 市场方面:性格要外向、人际沟通、适应销售的环境 - - * 一般应届毕业生,能掌握80%,将来的薪资在5K左右 - - - * 软件开发,到底要怎么学? - * 程序员都是实践家,用代码来探路? - * 软件开发,没有捷径。压缩时间!! - * 抗压能力 - * 应变能力 - * 沟通能力 - * 技术能力 -## 简历 -> 之前的看法是只要有一页即可, 现在和亲戚交流下之后, 两页是刚好的, 第一页是教育经历,经验, 第二页是技能 - -_面试:(站在自己的角度看面试,成功都是站在对方角度看问题)_ -- 第一阶段:准备 - * 写简历: - * 篇幅:建议最多不要超过3页(不包含封皮) - * 模板:不建议使用模板,使用word里的画表格。 - * 格式:1)基本信息(记得写联系方式、联系方式放在一起); - 2)项目经验(工作经历) - 3)工作经历(项目经验) - 4)个人能力,作用:充数的 - 5)相关认证,oracle认证、PMP认证(项目管理认证)、weblogic认证、IBM认证 - * 字体:宋体;大小:小四或者四号;行间距:1.5倍 - - * 投简历:建议每天早上8:30(改系统时间);海投(每天200);搜“java”,“J2EE”;邮件形式:申请职位+姓名+联系方式 - - * 笔试题:1)会的;2)会的但是不熟;3)不会的 ———— 背(2000,20%) - - * 项目经验怎么写? - * 写模板:(总分总) - * 基于。。(用到的技术,J2EE + Oracle 9i + Linux)的。。。系统(软件),实现了。。。的功能,满足了。。。的需求。 - * 在该项目你负责那些模块,完成了哪些功能,实现了哪些效果,使用哪些技术。在该项目中遇到哪些问题,如何解决的。 - * 该项目经历了多长时间(项目周期),团队人数,你在团队中的角色。 - - * 项目经验雷同,如何能写出不一样的项目经验? - * 上网搜! - * 搜到的项目,项目简介或者系统简介。实际上是满足“系统(软件),实现了。。。的功能,满足了。。。的需求。” - * 无论什么项目或者系统,在我的简历里,使用的技术都是一样的。 - * J2EE+ORACLE+LINUX - * JSP/SERLVET/STRUTS/SPRING/HIBERATE - * 项目周期、团队人数你在团队中的角?编出来的!只要编的合理就OK。 - * 写完的简历,给就业老师、任课老师帮你看。 - - * 心理问题: - - * 如何能把背下来的题说成让对方感觉像是你会的题一样? - * 如何能把别人的项目能说成让对方感觉像是你的项目? - - * 1)面部表情;2)语速;3)语调! - * 每天晚上回家,进卫生间,对着镜子:说! - * 说什么?1)准备一段背下来的内容;2)说一段平时说话的内容 - * 用手机录下来,放在电脑上,作对比! - - * 就开始练 - * 第一阶段:一直练到你自己把自己给骗了! - * 找别人练, - * 第二阶段:你说一段背下来的内容,对方会感觉是你会的! - - * 有同学是南方,普通话不是很好。 - - * 1)一定要站在对方角度看问题;2)一定要尽力为对方着想! - - -- 第二阶段:面试 - * 组团面试 - * 面试:1)技术类(1、笔试题;2、上面两个问题练好);2)非技术类,面试陷阱 - -- 第三阶段:谈价格 - * 自我评估(真实) —— 5000(应届毕业,掌握80%,5K左右) - * 期望值 —— 7000(期望值和自我评估相差1000至2000左右) - - * 你想要多少啊? - * 直接说出你的期望值。10000 - - * 实在太高了,根本给不了 - * 你们能给多少啊? - * 对方给一个标准,6000 - * 表示出非常为难,第二次要价:(期望值-对方的价格)/2+对方的价格 - * 实在太高了,根本给不了 - * 第三次要价:(第二次要价-对方的价格)/2+对方的价格 - * 开始犹豫,不能给对方犹豫的机会,想尽一切办法告诉对方:你值这个价 - * 你这么大领导,连这点权利都没有? - * 这么大一家公司,每个月也不差这1000或2000吧 - * 我相信我为公司能创造出来的价值会远远超过公司给我的这个待遇 - - * 有点高,在犹豫 - * 你们能给多少啊? - * 根据对方的价格来判断:对方的上线 - * 非这个价不谈 - * 不要那么执着,找一些理由来说明你不值这个价格 - * 那好,既然我那么多不足。那算了 - * 那好吧 - - * 刚好在对方的范围内,对方很高兴 - * 考虑能不能接受 - - * 在要价格的时候,尽量高标准 - * 在讨价还价的时候,一定要注意的是:`每一次对方的表情,通过表情判断对方的想法` - - * 行为心理学,HR(行政、财务等) - - * 铁打的心、自我调节能力 - - -> 我的第一份工作, 钱不是重点, 要的是长远的发展, 希望是7k 因为同学在上海深圳都是78k, 而且我的水平和他们差不多, 所以我值这个价 - -- 将来在公司如何发展? - * 遍历公司里的所有的技术人员包括技术领导:一定要找到公司的技术大牛(公司所用的) - * 遍历公司里的所有的业务人员包括技术人员,一定要找到对公司业务非常熟悉那些人 - * 在公司里的将来发展就靠这两种人 - * 想尽一切办法和这些人搞好关系:一定不要在公司里上班的时间。一定要投其所好 - * 直接领导和主要领导 - * 和主要领导搞好关系的同时,要注意不要让直接领导感觉到 - * 直接领导搞好关系到什么程度?就是让直接领导把你当自己人 - * HR、行政:尽量不要与这些人有交集 diff --git a/Skills/Work/PomodoroTechnique.md b/Skills/Work/PomodoroTechnique.md deleted file mode 100644 index 7a0ef99..0000000 --- a/Skills/Work/PomodoroTechnique.md +++ /dev/null @@ -1,48 +0,0 @@ -`目录 start` - -- [番茄工作法](#番茄工作法) - - [规则](#规则) - - [工作流程](#工作流程) - - [细节](#细节) - - [相关软件](#相关软件) - -`目录 end` |_2018-08-04_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 番茄工作法 -> 参考: [知乎:分秒时](https://www.zhihu.com/question/20189826/answer/14283234) -> [知乎专栏:什么是番茄工作法?该如何运用?](https://www.zhihu.com/question/20189826) - - -## 规则 -- 规则一:一个番茄时间共30分钟,包括25分钟的工作时间和5分钟的休息时间。 -- 规则二:一个番茄时间是不可分割的。番茄工作法中,时间的的最小单位是一个番茄时间。一个番茄时间不能被划分: - - 不存在半个番茄时间或者一刻钟的番茄时间这样的事情。 -- 规则三:每四个番茄时间后,停止你的工作,进行一次较长时间的休息,大约15到30分钟 -- 规则四:完成一个任务,划掉一个 - -## 工作流程 - -- 计划 - - 在每一天的开始,从Activity inventory 活动清单中选出你今天要完成的任务,优先考虑他们,并把他们记在to do today 工作计划表中; -- 追踪 - - 按照to do today 完成任务,加上计划外的紧急事件(当日需要做的),记录每项任务所耗用的番茄时间; -- 记录档案 - - 每天的结束时,把已经完成的任务记录在档案(自己建,可以用书本,也可用电子表格)中。 -- 分析 - - “你要跟踪和记录什么,取决于你要观察什么”(!每天结束时,记录档案和思考如何提高不应该超过一个番茄时间,否则,再好的工作法也会成为累赘) - -### 细节 - -第一阶段:弄清完成某项任务所需的时间&把“下一步动作(计划任务)”详细些(路径划)。Eg, 不是“看一本书”而是看这本书的哪个部分“看某本书到第几页或者某章” - -第二阶段:减少被打断的次数我们如何避免来自内部因素的打断:·每当你意识到潜在的打断将要来临时,比如肚子饿了,想吃东西,请你在工作记录番茄的地方上一个['],&然后,判断是否是今日要做的,是的话,记在计划外事件清单那列,不是的话,记在活动清单。(记录时,写出截至日期或者什么时间去做)·继续当前的番茄时间 。&直到完成这个番茄时间后,再去处理刚才的记录,如果不影响休息,就在休息时间处理,如果超过两分钟的话,就计划一个新的番茄时间。然后按照重要紧急优先原则,处理to do today 清单上的任务外部因素——被他人打断·方法同内部因素&判断事情的紧急性,记录这件事情,继续自己的番茄时间,当这个番茄时间完成后处理。让打断变成任务·成功地拖延打断,越迟越好,尽量减轻这些打断的紧急程度,增加这些打断的可控制性。·逐渐地减少专用番茄数&“计划赶不上变化快”,控制打断次数 - -第三阶段:估测某项任务所需要的时间 -规则一:如果一项任务的估测值大于5到7个番茄数,那么就打散它 -规则二:如果估测值小于一个番茄数,就把几个小任务合成一个大任务·从表中找出相似的任务,把他们加合在一起成一个番茄时间为止·表中没有标识估测值得任务, -规则三:估测结果的记录日期、时间、任务类型 、任务描述、估测值、实际、差别 - -## 相关软件 -> [各大平台的相关软件](https://www.douban.com/note/141322878/) 目前使用的是 go for it 感觉比较好用,简洁 - -极简番茄 番茄TODO 番茄土豆 番茄时间钟 \ No newline at end of file diff --git a/Skills/Work/WorkThinking.md b/Skills/Work/WorkThinking.md deleted file mode 100644 index 93037cd..0000000 --- a/Skills/Work/WorkThinking.md +++ /dev/null @@ -1,43 +0,0 @@ -`目录 start` - -- [工作思考](#工作思考) - - [如何学习](#如何学习) - - [如何完成任务](#如何完成任务) - - [交流](#交流) - -`目录 end` |_2018-08-08_| [码云](https://gitee.com/gin9) | [CSDN](http://blog.csdn.net/kcp606) | [OSChina](https://my.oschina.net/kcp1104) | [cnblogs](http://www.cnblogs.com/kuangcp) -**************************************** -# 工作思考 - -> [参考博客: 如何提升你的能力?给年轻程序员的几条建议](http://tech.glowing.com/cn/advices-to-junior-developers/) - -1. 一套高效的开发环境 -1. 一个信息采集器和一本笔记本 -1.目标要够大,这样你才能看到更多的风景。 - - 目标应该设定在解决哪一类问题,而不是精通哪一类技术。技术只是手段,不是目的。 -1. 打造自己的 portfolio - - 建议每个程序员都应该经营一款自己的产品,它可以是一款app,一个网站或是一个开源软件。 - -https://insights.thoughtworks.cn/five-steps-of-developer/ - -## 如何学习 -1. 没有人教代表你发挥的余地很大, 提升空间空前, 不该抱怨, 应该看到这个机会所在. -2. 占据一个位置, 就能够享有这个位置的资源和眼界, 尽快在这样的环境里学习, 能力提升, 比那些有能力却没有机会的人进步不知道快到哪里. -3. 所以, 要看到机会, 不要抱怨, 哪怕不行, 也要给别人留下自己认真学习可以做好的印象, 占据一个位置才是要紧. -4. 职场工作里, 人们需要一种核心的价值观指导他们面对恐惧. -5. 不管你想解决任何事情, 你都需要先调整成已经成功解决这个问题的人的思维. 然后学习他的做法, 先COPY下来, 然后针对性的调整细节. -6. 赶紧尝试, 赶紧行动, 过程中看看错误的地方是哪里. 总结成功的经验, 把每一次学习和实践中宝贵的思维方式, 行动方法记录下来. -7. 每天定时定量的学习, 这样可以保证每天都有只是摄入. -8. 能COPY就先COPY, 切勿贪多, 不要没有积累的时候就开始发挥. -9. 调整好自己的思维, 不对周围包邮过对的元气, 靠学习和工作的结果积累第七, 不表现出畏缩悲观, 才能有一个平和的心态去积累去发展, 去实现更为远大的目标. - -## 如何完成任务 -1. 首先明确需求, 不要过多实现也不要缺斤少两, 让自己的任务完成的质量高而又不浪费时间. 当然扩展性最好是要时刻考虑好的. - - -## 交流 - -职责明确 配合问题 - -> [参考博客: 如何更好地控制情绪,不「抬杠」?](https://www.zhihu.com/question/27306335) -> [参考博客: 看清程序员怒打产品经理的本质](http://www.techug.com/post/progrmmer-fight-with-pm.html) diff --git a/Skills/Work/img/001-effective-learn.km.svg b/Skills/Work/img/001-effective-learn.km.svg new file mode 100644 index 0000000..1c17581 --- /dev/null +++ b/Skills/Work/img/001-effective-learn.km.svg @@ -0,0 +1 @@ +高效学习快速进入学习状态番茄工作法选择没有干扰的环境获取信息结构随意信息联想法挂钩法观点信息理解模式而不是记忆细节速读技巧对于收集信息很重要图表法提取出关键思想过程信息不断练习, 反复执行建立正确的背景概念来节省时间具体信息抽象信息获取信息的目标简化容量速度记笔记的习惯笔记本手写(康奈尔笔记法)有道云, 印象笔记Github+markdown/org理解整体理解 - What Why How细节理解知识框架创建自己的学习框架思维导图回顾看全局类比, 展开联想自我回顾提取资源精华, 进行整合应用重述编码实现讨论分享 \ No newline at end of file diff --git a/Windows/README.md b/Windows/README.md index e5647e9..400a9cc 100644 --- a/Windows/README.md +++ b/Windows/README.md @@ -1,6 +1,47 @@ +# Windows -常用路径 Windows 所有字体 C盘Windows/Fonts 目录下 +## 安装激活 +> win10 + +https://www.microsoft.com/en-gb/software-download/windows10ISO +https://pureinfotech.com/create-windows-10-virtual-machine-virtualbox/ + +https://blog.csdn.net/qq_37700257/article/details/126284196#commentBox + +> 常用路径 +1. Windows 所有字体 C盘Windows/Fonts 目录下 + +- git-for-windows +- [cygin](http://x.cygwin.com/) + +> tasklist +- 相当于 Linux的 ps 命令, 查看帮助为 `tasklist /?` +- 按可执行文件查找运行的进程 `TASKLIST /FI "IMAGENAME eq idea64.exe"` + +- 资源管理器插件, 支持多标签, 类似于Chrome的使用 [clover](http://cn.ejie.me/)`但是有广告` +- 可以用来根据鼠标查询进程 [process-explorer](https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer)`有效定位广告窗口` + +## 工具 +> Windows Terminal +- Alt Shift -/= 横向/竖向切分窗口 + +## 性能测试 +- Msi after burner 显卡超频 硬件监控 +- Aida64 电脑信息检测,稳定性测试 +- As ssd Benchmark 硬盘测试 +- HD Tune 硬盘测试 +- Ctystal Disk Mark 更专业硬盘测试 +- 3D Mark 电脑性能测试 + + +## 网络 +- tracert 等同于Linux的 traceroute +- ipconfig 等同于Linux的 ifconfig + +## 影音 +[Potplayer](http://potplayer.tv/?lang=zh_CN) + +# Tips +## 寻找占用文件的进程 +> 任务管理器 -> 性能tab 右上角打开 资源监视器 -> CPU的tab 关联的句柄窗口 搜索文件名 -模拟Linux环境 -git-for-windows -[cygin](http://x.cygwin.com/) diff --git a/Windows/WindowsWithHibernate.md b/Windows/WindowsWithHibernate.md new file mode 100644 index 0000000..c460d03 --- /dev/null +++ b/Windows/WindowsWithHibernate.md @@ -0,0 +1,34 @@ +--- +title: Windows的快速启动 +date: 2019-01-16 20:49:28 +tags: +categories: + - Windows +--- + +**目录 start** + +1. [Windows的快速启动](#windows的快速启动) + 1. [解决Linux下挂载hibernate状态分区的问题](#解决linux下挂载hibernate状态分区的问题) + +**目录 end**|_2020-04-27 23:42_| +**************************************** +# Windows的快速启动 +> [参考: Windows快速启动背后的功臣:休眠](https://zhuanlan.zhihu.com/p/28639474) + +- 从8开始, Windows开机速度显著加快了, 因为引入了快速启动这个东西, 也就是在关机的时候将一些数据缓存到 hiberfil.sys 文件上, 下次开机能加载该文件从而快速开机 + +- 开了快速启动之后, 之后的关机就不是纯粹的关机, 而是混合模式关机 先进入休眠状态, 然后关机 +1. `shutdown /s /full / t 0` +1. 或者选择重启 + +> 分区为 hibernate 状态后, Linux挂载就只能是只读模式了 + +## 解决Linux下挂载hibernate状态分区的问题 +> [Unable to mount Windows (NTFS) filesystem due to hibernation](https://askubuntu.com/questions/145902/unable-to-mount-windows-ntfs-filesystem-due-to-hibernation) `原因以及解决方案` + +1. 尝试该方法 `sudo ntfsfix /dev/sdXY` +1. 强制挂载为读写模式 `sudo mount -t ntfs-3g -o ro /dev/xx /media/xx` +1. 强制解除 hibernate 状态 `sudo mount -t ntfs-3g -o remove_hiberfile /dev/xxx /media/xxx` 依赖 ntfs-3g 包 + +> 最后一种最为暴力, 也是通常能解决问题, 但是会造成Windows数据的丢失 diff --git a/Windows/Wsl.md b/Windows/Wsl.md new file mode 100644 index 0000000..da6714b --- /dev/null +++ b/Windows/Wsl.md @@ -0,0 +1,37 @@ +--- +title: Wsl +date: 2024-09-05 11:52:54 +tags: +categories: +--- + +💠 + +- 1. [WSL](#wsl) +- 2. [WSL2](#wsl2) + - 2.1. [GUI](#gui) +- 3. [WSA](#wsa) + +💠 2024-10-15 09:56:12 +**************************************** +# WSL +> [Official Doc](https://learn.microsoft.com/zh-cn/windows/wsl/install) + +> [利用WSL打造Arch开发环境](https://zhuanlan.zhihu.com/p/51270874) + +# WSL2 +- `启动发行版` wsl -d Name + +> 更换磁盘地址 + +- 导出已有发行版 `wsl --export Ubuntu D:/wsl/Ubuntu-disk.tar` +- 导入成为新的发行版并设置数据存放目录 `wsl --import Ubuntu2 D:/wsl/Ubuntu2 D:/wsl/Ubuntu-disk.tar` +- 删除旧发行版 `wsl --unregister Ubuntu` + +## GUI +> [wslg](https://github.com/microsoft/wslg) +> [Run Linux GUI apps with WSL | Microsoft Learn](https://learn.microsoft.com/en-us/windows/wsl/tutorials/gui-apps) + + +# WSA +[适用于 Android™️ 的 Windows 子系统](https://learn.microsoft.com/zh-cn/windows/android/wsa/) diff --git a/_config.yml b/_config.yml deleted file mode 100644 index e969cef..0000000 --- a/_config.yml +++ /dev/null @@ -1,2 +0,0 @@ -theme: jekyll-theme-merlot -encoding: UTF-8 diff --git a/create_tree.py b/create_tree.py old mode 100755 new mode 100644 index ed939a0..9c488c2 --- a/create_tree.py +++ b/create_tree.py @@ -19,10 +19,10 @@ 使用: python3 create_tree.py -h 查看帮助 ''' # 忽略的文件夹 -ignoreFolder=['.git', 'backup', '.vscode'] +ignoreFolder=['.git', 'backup', '.vscode', 'ARTS'] # 所有要被忽略的文件 ignoreFile=['PULL_REQUEST_TEMPLATE.md', 'ISSUE_TEMPLATE.md', 'CODE_OF_CONDUCT.md','README.md', - 'Readme.md', 'CSS3.md', 'HTML5.md', '_Sidebar.md'] + 'Readme.md', 'CSS3.md', 'HTML5.md', '_Sidebar.md', 'SUMMARY.md'] result = [] @@ -102,11 +102,11 @@ def main(verb=None): # 追加到SUMMARY if verb == "-a": readAll() - subprocess.call('mv SUMMARY.md SUMMARY.md.bak',shell=True) + subprocess.call('mv SUMMARY.md SUMMARY.md.bak', shell=True) with open('SUMMARY.md','w+') as dest: dest.write('# Summary\n\n* [ Introduction ](README.md)\n\n') for res in result: dest.write(res+'\n') - logInfo('重新生成目录树完成!') + logInfo('complete refresh catalog file') fire.Fire(main) diff --git a/wordRecord.sh b/wordRecord.sh index 6761af8..9378551 100755 --- a/wordRecord.sh +++ b/wordRecord.sh @@ -1,9 +1,25 @@ +hasCommandByType(){ + if type $1 2>/dev/null; then + echo 1 + else + echo 0 + fi +} + +result=$(hasCommandByType countzh) +result=$(echo $result | grep is) +if test -z "$result" ; then + echo "countzh not install, start install" + go install github.com/kuangcp/gobase/toolbox/countzh@latest +fi + logFile=submission.log if [ "$1"z = "z" ];then - count -s >> $logFile && date +%y-%m-%d_%H:%M:%S >> $logFile - echo "------------------------------------------" >> $logFile - less $logFile -else + word=$(countzh -s) + line=$(git ls-files | grep -v "ARTS" | xargs cat | wc -l) + # echo "$word $line lines on $time " >> $logFile + echo "$word $line lines" >> $logFile +else less $logFile fi