├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .nojekyll ├── Artificial-Intelligence ├── AI.md ├── RoadMap.md ├── llama_train.md └── math.md ├── BigData ├── Elasticsearch.md ├── Flink.md ├── 商业智能.md ├── 大数据.md └── 联邦学习.md ├── CNAME ├── Database ├── MYSQL基础及优化.md ├── PostgreSQL.md └── 数据库.md ├── DevOps ├── Kubernetes.md ├── MQ │ └── RabbitMQ.md ├── SSO.md ├── 分布式系统数据一致性.md ├── 反编译.md ├── 微服务.md ├── 缓存.md ├── 网络.md └── 运维.md ├── Java ├── JAVA响应式编程.md ├── JAVA基础.md ├── JAVA字节码.md ├── JVM.md ├── Spring.md ├── 分布式锁.md ├── 分析工具.md └── 如何限流.md ├── LICENSE ├── Linux ├── CommandLine.md ├── Computer.md ├── HTTP.md ├── HTTPS证书.md ├── NetworkNamespace.md ├── Openconnect.md ├── SS.md ├── incommonuselist.md ├── mac.md └── ocservauto.sh ├── Notepad ├── APP签名.md ├── Sales.md ├── emoji.md ├── opensource-awesome.md ├── 产品设计.md ├── 公式.md ├── 开源协议.md ├── 数据源.md ├── 日常杂项.md └── 软件架构师.md ├── Python ├── JupyterNotebook.md ├── WinHidden.md ├── pandas.md └── pySpark.md ├── README.md ├── _coverpage.md ├── algorithm ├── BloomFilter.md └── 算法学习.md ├── assets ├── code │ ├── BloomFilter.java │ └── cloudflare-workers-kv.js └── img │ ├── DevOps-BD-FLINK-1.png │ ├── ETL-ELT.png │ ├── Federated-Learning-classify.png │ ├── Federated-Learning-opensource.png │ ├── HTLC.png │ ├── HTTPS.png │ ├── JAVA-BASE-LOCK-INDEX.png │ ├── JAVA-BASE-TABLE-1.bmp │ ├── JAVA-BASE-TABLE-2.bmp │ ├── JAVA-BASE-TABLE-3.bmp │ ├── JAVA-BASE-TABLE-MAP-PUT.png │ ├── JAVA-JVM-GC-heap.png │ ├── MPT.png │ ├── PBFT-3stage.png │ ├── SAML.png │ ├── atlas-architecture.png │ ├── bc-merkle-tree.png │ ├── big-data.png │ ├── bigdata.png │ ├── bitcoin-1.png │ ├── bitcoin-2.png │ ├── bitcoin-disk-space.png │ ├── bitcoin-pow.png │ ├── bitcoin-privacy.png │ ├── bitcoin-simplified-payment.png │ ├── bitcoin-transaction.png │ ├── bloomfilter.webp │ ├── contract-oracle.png │ ├── design-birdge.png │ ├── distributed-transaction-2-phase-1.png │ ├── distributed-transaction-2-phase-2.png │ ├── distributed-transaction-3-phase-1.png │ ├── distributed-transaction-3-phase-2.png │ ├── distributed-transaction-3-phase-3.png │ ├── git.png │ ├── javabean.png │ ├── kubernetes-ecosystem.png │ ├── merkle_tree_bitcoin.png │ ├── mysql-explain.png │ ├── paxos-flow.png │ ├── pbft_process.png │ ├── pbft_view.png │ ├── rpc.jpg │ ├── springinit.webp │ └── 解一元二次.png ├── blockchain ├── 区块链与随机数.md ├── 区块链概念汇总.md ├── 区块链设计.md ├── 智能合约-solidity.md ├── 智能合约-设计模式.md ├── 白皮书-以太坊.md ├── 白皮书-比特币.md └── 联盟链.md ├── design-patterns ├── a.sh ├── 代理.md ├── 单例.md ├── 外观.md ├── 桥接.md ├── 模板.md ├── 状态模式.md ├── 策略.md ├── 装饰者.md ├── 观察者.md ├── 设计模式.md ├── 责任链.md ├── 迭代器.md └── 适配器.md ├── golang └── 协程并发控制.md ├── hacker ├── blackchain-bug-erc.md ├── blackchain-bug-parity-wallets.md ├── github-bug.md ├── mail-security-SPF.md └── web-security.md ├── head.png ├── index.html ├── solution └── 渲染字体库到图片识别输出文字.md ├── sw.js └── web ├── CSS.md ├── README.md └── tools.md /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: 4 | # schedule: 5 | # - cron: '0 0 * * *' 6 | push: 7 | branches: 8 | - master 9 | # release: 10 | # types: [created,edited] 11 | 12 | jobs: 13 | listify-auto: 14 | runs-on: ubuntu-latest 15 | # if: "! contains(github.event.head_commit.message, 'listify Actions automatic')" 16 | name: listify auto 17 | steps: 18 | - name: listify-actions 19 | uses: ifuture-pro/listify-actions@master 20 | with: 21 | listify: ./ --exclude='^_\S*' --package --exclude_dir=draft --maxlevel=6 22 | github_token: ${{ secrets.GH_TOKEN }} 23 | directory: ./developer-notes 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | draft/ 4 | *.iml 5 | *.log -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/.nojekyll -------------------------------------------------------------------------------- /Artificial-Intelligence/RoadMap.md: -------------------------------------------------------------------------------- 1 | AI工程师成长 2 | ------- 3 | 4 | ## 编程语言 5 | 6 | ### Python 7 | * 数据分析:[pandas](https://github.com/pandas-dev/pandas), [sklearn](https://github.com/automl/auto-sklearn) 8 | 9 | > 《Effective Python》 10 | 11 | ### Scala/Java 12 | * 大数据领域:Hadoop,Spark,Flink 13 | 14 | > 《深入理解Java虚拟机》、《Spark快速大数据分析》、《Programming in Scala》 15 | 16 | > 使用Spark的Scala API来进行大规模的数据分析及处理,完成lag feature之类的特征工程处理 17 | 18 | ### C/C++/Rust 19 | > 当前流行的算法框架,例如TensorFlow, PyTorch, LightGBM等,底层都是基于C++为主要语言进行实现的 20 | 21 | ## 操作系统 22 | > 硬件结构,CPU调度,进程,线程,内存管理,文件系统,IO,网络等 23 | 24 | ### Linux 25 | * CentOS 26 | * MacOS 27 | * Ubuntu 28 | 29 | ## 算法与数据结构 30 | > 《统计思维》,《贝叶斯方法》,《程序员的数学2》 31 | 32 | > 机器学习:周志华《机器学习》、《PRML》,《ESL》、《统计学习方法》 33 | 深度学习 34 | 35 | * 通用机器学习:scikit-learn,Spark ML,LightGBM, XGBoost 36 | * 通用深度学习:Keras/TensorFlow,PyTorch 37 | * 特征工程:tsfresh, Featuretools,Feast 38 | * AutoML:hyperopt,SMAC3,nni,autogluon 39 | * 可解释机器学习:shap,aix360,eli5,interpret 40 | * 异常检测:pyod,egads 41 | * 可视化:pyecharts,seaborn 42 | * 数据质量:cerberus,pandas_profiling,Deequ 43 | * 时间序列:fbprophet,sktime,pyts 44 | * 大规模机器学习:Horovod,BigDL,mmlspark 45 | * Pipeline:MLflow, metaflow,KubeFlow,Hopsworks 46 | * 爬虫:scrapy 47 | 48 | ### 大规模算法运行 49 | > 分布式训练 50 | 51 | > 高性能计算 52 | 硬件:超线程技术,向量化指令集,GPGPU,TPU 53 | 软件:OpenBLAS,OpenMP,JIT 54 | Nvidia-cuDNN, LightGBM 55 | 模型加速:MobileNet,TensorRT,二值网络 56 | 57 | 58 | ## 可视化 59 | 60 | ## 软件工程 61 | * 代码规范 62 | * 设计模式 63 | * 质量保障,自动化测试 64 | 65 | > sklearn,LightGBM 66 | 67 | * 项目管理 68 | 69 | > 敏捷开发,设计评审,代码评审,版本管控,任务看板管理 70 | 71 | * 软件架构师 72 | 73 | > 技术选型与系统架构设计,架构设计原则与模式,宽广的研发知识视野,高性能,高可用,可扩展性,安全性, 微服务 74 | 75 | ## MLOps 76 | > 框架工具:Airflow,DolphinScheduler,Cadence 77 | > 开源系统:MLflow,Kubeflow,Metaflow,TFX 78 | > 数据质量:TFX Data Validation,Cerberus,Deequ 79 | > 实验管理:MLflow,fitlog,wandb 80 | > Serving:mmlspark,TF Serving,MLeap,H2O,PredictionIO,PMML/PFA/ONNX 81 | > CI/CD:Jenkins,CircleCI,GoCD,VerCD 82 | > 系统监控:elasicsearch + kibana 83 | 84 | 85 | ## 数据库 86 | Delta Lake 87 | [Feature Store](https://www.hopsworks.ai/) 88 | 89 | ## 云计算 90 | Docker、k8s 91 | 92 | ---------- 93 | [参考](https://zhuanlan.zhihu.com/p/192633890) 94 | 95 | ## 文献 96 | * NLP TF-IDF(Term Frequency - Inverse Document Frequency) [TF-IDF与余弦相似性的应用(一):自动提取关键词](https://www.ruanyifeng.com/blog/2013/03/tf-idf.html) 97 | * LDA [pyLDAvis](https://nbviewer.jupyter.org/github/bmabey/pyLDAvis/blob/master/notebooks/sklearn.ipynb) 98 | -------------------------------------------------------------------------------- /Artificial-Intelligence/llama_train.md: -------------------------------------------------------------------------------- 1 | # LLAMA2 Chinese 2 | 3 | ```SHELL 4 | conda create -n qlllama2 python=3.8 5 | conda activate qlllama2 6 | conda deactivate 7 | conda remove -n qlllama2 --all 8 | 9 | pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 10 | 11 | torchrun --nproc_per_node=8 train.py --train_args_file train_args/llama2-13b-ext.yaml 12 | 13 | ``` 14 | 15 | ## 报错记录 16 | 17 | * libbitsandbytes_cpu 版本不对导致的 18 | 19 | ```shell 20 | python3.8/site-packages/bitsandbytes/libbitsandbytes_cpu.so: undefined symbol: cquantize_blockwise_fp16_nf4 21 | ``` 22 | 用自己的CUDA版本覆盖 23 | ```SHELL 24 | cd python3.8/site-packages/bitsandbytes 25 | cp libbitsandbytes_cuda121.so libbitsandbytes_cpu.so 26 | ``` 27 | -------------------------------------------------------------------------------- /Artificial-Intelligence/math.md: -------------------------------------------------------------------------------- 1 | # Math 2 | ## 希腊字母 3 | 4 | | 大写 | 小写 | 英文注音 | 国际音标注音 | 中文注音 | 5 | | :--- | :--- | :------- | :----------- | :------- | 6 | | Α | α | alpha | alfa | 阿耳法 | 7 | | Β | β | beta | beta | 贝塔 | 8 | | Γ | γ | gamma | gamma | 伽马 | 9 | | Δ | δ | deta | delta | 德耳塔 | 10 | | Ε | ε | epsilon | epsilon | 艾普西隆 | 11 | | Ζ | ζ | zeta | zeta | 截塔 | 12 | | Η | η | eta | eta | 艾塔 | 13 | | Θ | θ | theta | θita | 西塔 | 14 | | Ι | ι | iota | iota | 约塔 | 15 | | Κ | κ | kappa | kappa | 卡帕 | 16 | | ∧ | λ | lambda | lambda | 兰姆达 | 17 | | Μ | μ | mu | miu | 缪 | 18 | | Ν | ν | nu | niu | 纽 | 19 | | Ξ | ξ | xi | ksi | 可塞 | 20 | | Ο | ο | omicron | omikron | 奥密可戎 | 21 | | ∏ | π | pi | pai | 派 | 22 | | Ρ | ρ | rho | rou | 柔 | 23 | | ∑ | σ | sigma | sigma | 西格马 | 24 | | Τ | τ | tau | tau | 套 | 25 | | Υ | υ | upsilon | jupsilon | 衣普西隆 | 26 | | Φ | φ | phi | fai | 斐 | 27 | | Χ | χ | chi | khai | 喜 | 28 | | Ψ | ψ | psi | psai | 普西 | 29 | | Ω | ω | omega | omiga | 欧米 | 30 | 31 | ## 基础概念 32 | * 自然数。从0开始算 33 | * 质数。只能被1和他本事整除的正整数(即只有1和他本事两个约数)叫质数,反之为合数,1既不是质数也不是合数。 34 | 35 | > 质数:2,3,5,7,11,13,17,19 36 | > 合数:4,6,8,10 37 | > 2是唯一一个即使质数也是偶数的正整数,即是唯一是偶质数,大于2的质数必定是奇数 38 | > 若正整数a,b的乘积是质数p,则a = p,b = 1或 b = p, a = 1 39 | > 若两个质数的和或差是奇数,那么其中一个质数必然是2。因为只有偶±奇才能等于奇数 40 | > 若两个质数的乘积是偶数,那么其中一个质数必然是2。因为只有奇乘偶才能是偶数 41 | 42 | * 互质数。公约数只有1的两个数叫互质数。如:9的约数{1,3,9},16的约数{1,2,4,8,16} 它们的公约数1,所以它们是互质数。 43 | 44 | * 最小公倍数 45 | 46 | ## 方程 47 | ### 一元二次 48 | * 直接开平方法 49 | 50 | > 若 $x^2=a(a \geq 0)$ ,则 $x=\pm \sqrt{a}$ 51 | 52 | * 配方法 53 | 54 | > 但二次项系数为1的时,方程两边都加上一次项系数一半的平方。 55 | > 构成一个完全平方公式即(a+b)²=a²+2ab+b²、(a-b)²=a²-2ab+b² 56 | > 平方差公式:(a+b)(a-b)=a²-b² 57 | 58 | 解方程:$x^2 + 3x = 0$ 59 | $x^2 + 3x + (\frac{3}{2})^2 = (\frac{3}{2})^2$ 60 | $(x+\frac{3}{2})^2 = \frac{9}{4}$ 61 | $x+\frac{3}{2} = \pm \frac{3}{2}$ 62 | $x = -\frac{6}{2} = -3$ 或 x = 0 63 | 64 | * 因式分解法 65 | 66 | > 若(x-a)(x-b)=0,则x-a=0或x-b=0 67 | 68 | * 公式法 69 | 70 | > 方程$ax^2 + bx + c = 0(a \neq 0, 判别式:b^2-4ac\geq0)$,则解是$x=\frac{-b\pm \sqrt{b^2-4ac}}{2a}$ 71 | 72 | * 根与系数的关系 73 | 74 | > 若$ax^2 + bx + c = 0(a \neq 0)$的两个跟为$x_1,x_2$ 75 | > 则$x_1+x_2=- \frac{b}{a}$,$x_1x_2=\frac{c}{a}$ 76 | 77 | ![](../assets/img/解一元二次.png) 78 | 79 | 80 | ## 集合 81 | * 集合元素的性质:确定性、无序性、互异性 82 | * 元素与集合的关系 83 | * 属于 $\in$ , 不属于 $\notin$ 84 | 元素a在集合A里面:a $\in$ A 85 | 元素a不再集合A里面: a $\notin$ A 86 | * 常见数集符合 87 | * $N$ 自然数集 `0,1,2,3……` 88 | * $N^*$ 或 $N_+$ 正整数集 `1,2,3……` 89 | * $Z$ 整数集,包含正整数,负整数,零 90 | * $Q$ 有理数集,有理数是整数(正整数、0、负整数)和分数的统称 91 | * $R$ 实数集,实数是有理数和无理数的总称 92 | * 集合之间的关系 93 | * A=B,相等。集合A和集合B中说有元素都相同 94 | * A$\subseteq$B,子集。B大,其实也有可能相等 95 | * A$\subseteq$B,真子集,符号有错误,下面是一个不等于号$\neq$。也是B大,而且不可能相等。 96 | * $\emptyset$,空集。空集是任何集合在子集,是任何非空集的真子集 97 | * A$\cup$B,并集。取两个集合的所有元素 98 | * A$\cap$B,交集。取两个集合相等的部分 99 | * $\complement_UA$,补集。A集合在全集U中,取U中除A集合剩下的部分 100 | * 必然结论 101 | * A$\cup$B = A 可推理出 A$\supseteq$B 102 | * A$\cap$B = A 可退理出 B$\supseteq$A 103 | * A$\cap$A = A 104 | * $A\cap \emptyset = \emptyset$ 105 | * $A\cap\complement_UA = \emptyset$ 106 | * $A\cup\complement_UA = U$ 107 | * $\complement_U(\complement_UA) = A$ 108 | * $A\subseteq B$ 可推理出 $A\cap B = A$ 可推理出 $A\cup B = B$ 可推理出 $\complement_UB \subseteq \complement_UA$ 可推理出 $A \cap (\complement_UB) = \emptyset$ 109 | * 集合A中有n个元素,那么他的子集个数为$2^n$,真子集个数为$2^n-1$,非空真子集个数为$2^n-2$ 110 | > 例:集合A = {1,2,3} 111 | 子集有:空集、{1}、{2}、{3}、{1,2}、{1,3}、{2,3}、{1,2,3} 共8个 = $2^3$ 112 | 真子集去掉{1,2,3} 共7个 113 | 非空真子集去掉{1,2,3}和空集,共6个 114 | 115 | ## 函数 116 | 函数三要素:定义域,对应关系,值域。 117 | 118 | 函数:$y=f(x),x \in A$ 119 | 120 | * 定义域:自变量x取值范围构成的集合 121 | * 值域:函数值的集合 {$f(x), x\in A$} 122 | 123 | 124 | ## 方差 125 | 126 | 方差是各个数据与平均数之差的平方的和的平均数。 127 | 128 | $S^2 = \frac{1}{n}[(x_1-x)^2 + (x_2-x)^2 + ... + (x_n-x)^2]$ 129 | 130 | $S^2 = \frac{\sum^n_{i=1}(x_i - x)^2}{n}$ 131 | 132 | 其中,x表示样本的平均数,n表示样本的数量,$x_i$表示个体,而$S^2$就表示方差。 133 | 134 | > 两人的5次测验成绩如下:X: 50,100,100,60,50,平均值E(X)=72;Y:73, 70,75,72,70 平均值E(Y)=72。平均成绩相同,但X 不稳定,对平均值的偏离大。方差描述随机变量对于数学期望的偏离程度。 135 | > 方差越小越稳定 136 | 137 | ## 标准差 138 | 139 | $δ=\sqrt(\frac{(x_1-x)^2 +(x_2-x)^2 +......(x_n-x)^2)}{n})$ 140 | 141 | 方差=标准差的平方 142 | 143 | ## 数据标准化 Normalization Method 144 | > 机器学习算法中要求样本间的距离就要使用数据归一化,把数据映射到同一尺度。 145 | > 数据归一化是为了解决量纲的问题,使数据映射到同一尺度。举2个例子:比如两个特征为月收入和和身高。月收入范围5000元-30000元,身高为1m-2.5m,在计算两个特征的欧式距离时,由于取值范围身高这一特征被忽略了,这样就让身高这一特征的信息失效了。所以要使用数据归一化把数据映射到同一尺度 146 | 147 | * 标准归一化 Z-score标准化 148 | 149 | $x∗ = \frac{x−μ}{δ}$ 150 | 151 | 其中 μ为所有样本数据的均值(mean), δ为所有样本数据的标准差(standard deviation)。 152 | 153 | * 最大最小归一化 154 | 155 | 也称为离差标准化,是对原始数据的线性变换,使结果值映射到 [0 - 1] 之间。 156 | 157 | $x∗=\frac{x − x_{min}}{x_{max} − x_{min}}$ 158 | 159 | 其中 $x_{max}$为样本数据的最大值, $x_{min}$为样本数据的最小值。这种方法有个缺陷就是当有新数据加入时,可能导致 $x_{max}$和 $x_{min}$的变化,需要重新定义。 160 | -------------------------------------------------------------------------------- /BigData/Elasticsearch.md: -------------------------------------------------------------------------------- 1 | ## Elasticsearch 2 | ## 关键字 3 | * Cluster:集群。 4 | 5 | ES可以作为一个独立的单个搜索服务器。不过,为了处理大型数据集,实现容错和高可用性,ES可以运行在许多互相合作的服务器上。这些服务器的集合称为集群。 6 | 7 | * Node:节点。 8 | 形成集群的每个服务器称为节点。 9 | 10 | * Shard:分片。 11 | 12 | 当有大量的文档时,由于内存的限制、磁盘处理能力不足、无法足够快的响应客户端的请求等,一个节点可能不够。这种情况下,数据可以分为较小的分片。每个分片放到不同的服务器上。 13 | 当你查询的索引分布在多个分片上时,ES会把查询发送给每个相关的分片,并将结果组合在一起,而应用程序并不知道分片的存在。即:这个过程对用户来说是透明的。 14 | 15 | * Replia:副本。 16 | 17 | 为提高查询吞吐量或实现高可用性,可以使用分片副本。 18 | 副本是一个分片的精确复制,每个分片可以有零个或多个副本。ES中可以有许多相同的分片,其中之一被选择更改索引操作,这种特殊的分片称为主分片。 19 | 当主分片丢失时,如:该分片所在的数据不可用时,集群将副本提升为新的主分片。 20 | 21 | * 全文检索。 22 | 23 | 全文检索就是对一篇文章进行索引,可以根据关键字搜索,类似于mysql里的like语句。 24 | 全文索引就是把内容根据词的意义进行分词,然后分别创建索引,例如”你们的激情是因为什么事情来的” 可能会被分词成:“你们“,”激情“,“什么事情“,”来“ 等token,这样当你搜索“你们” 或者 “激情” 都会把这句搜出来。 25 | 26 | |Elasticsearch|MySQL| 27 | |------|------| 28 | |Index|Database| 29 | |Type| Table| 30 | |Mapping|Schema| 31 | |Document| Row| 32 | |Field| Column| 33 | |Query DSL| SQL| 34 | |Restful API|insert select update delete| 35 | 36 | ### 安装 37 | * Docker 38 | ```bash 39 | docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 --restart=always elasticsearch:6.8.5 40 | ``` 41 | * 极限网关 42 | http://gateway.infinilabs.com/zh/ 43 | 44 | ### 常用API 45 | ```shell 46 | -- 所有节点 47 | curl http://127.0.0.1:9200/_cat/nodes?v 48 | -- 所有 index 49 | curl 'localhost:9200/_cat/indices?v' 50 | 51 | -- 导入导出 https://github.com/elasticsearch-dump/elasticsearch-dump 52 | elasticdump --input=http://localhost:9200/index-law --output=my_index.json --type=data 53 | 54 | ``` 55 | 56 | #### 备份 57 | ```shell 58 | vim /etc/elasticsearch/elasticsearch.yml 59 | path.repo: "/data/elasticsearch/backup 60 | 61 | curl -XPOST -H 'Content-Type: application/json' http://127.0.0.1:9200/_snapshot/my_es_backup -d ' 62 | { 63 | "type": "fs", 64 | "settings": { 65 | "location": "/home/sdb1/my_es_backup", 66 | "max_snapshot_bytes_per_sec" : "100mb", 67 | "max_restore_bytes_per_sec" : "100mb", 68 | "compress" : true 69 | } 70 | }' 71 | 72 | -- 查看 73 | curl -XGET "localhost:9200/_snapshot/my_es_backup/_all?pretty" 74 | -- 恢复 75 | curl -XPOST http://127.0.0.1:9200/_snapshot/my_es_backup/snapshot_1/_restore?wait_for_completion=true 76 | ``` 77 | 78 | ## elasticsearch-head 79 | > A web front end for an elastic search cluster 80 | ### 安装 81 | 82 | * Docker 83 | ```bash 84 | docker run -d --name es-head -p 9100:9100 mobz/elasticsearch-head:5 85 | ``` 86 | * 跨域问题 `No 'Access-Control-Allow-Origin'` 87 | 88 | **修改 elasticsearch 配置文件** 89 | ```bash 90 | vim /usr/share/elasticsearch/config/elasticsearch.yml 91 | 92 | #开启跨域访问支持,默认为false 93 | http.cors.enabled: true 94 | #跨域访问允许的域名地址,(允许所有域名)以上使用正则 95 | http.cors.allow-origin: /.*/ 96 | 97 | ``` 98 | 99 | * 请求方式错误 `406 Not Acceptable` 100 | 101 | 一般在使用elasticsearch 6.x 才会出现,因为`elasticsearch-head` 只适配到了5,而6.X 请求方式变成 `json` 方式请求 102 | **修改 elasticsearch-head 源码** 103 | ```bash 104 | vim /usr/src/app/_site/vendor.js 105 | 106 | 将 107 | contentType: "application/x-www-form-urlencoded" 108 | 改为 109 | contentType: "application/json" 110 | 111 | ``` 112 | -------------------------------------------------------------------------------- /BigData/Flink.md: -------------------------------------------------------------------------------- 1 | Flink 2 | ------ 3 | 大数据计算技术演进: 4 | 1. Hadoop 承载的 MapReduce 5 | 1. 支持 DAG(有向无环图)框架的计算引擎 Tez 和 Oozie,主要还是批处理任务 6 | 1. 支持 Job 内部的 DAG(有向无环图),以 Spark 为代表 7 | 1. 大数据统一计算引擎,包括流处理、批处理、AI、Machine Learning、图计算等,以 Flink 为代表 8 | 9 | ## 场景 10 | * 业务数据处理,聚合业务数据,统计分析 11 | * 流量日志,动态数据监控 12 | * 交通信号灯数据 13 | * 道路上车流量统计(拥堵状况) 14 | * 公安视频监控 15 | * 服务器运行状态监控 16 | * 金融证券公司实时跟踪股市波动,计算风险价值 17 | * 数据实时 ETL 18 | * 银行或者支付公司涉及金融盗窃的预警 19 | * 扩展库:CEP(复杂事件处理)、机器学习、图形处理 20 | 21 | ## 大数据 22 | ### 数据集类型 23 | * 无穷数据集:无穷的持续集成的数据集合 24 | > 用户与客户端的实时交互数据 25 | 应用实时产生的日志 26 | 金融市场的实时交易记录 27 | 28 | * 有界数据集:有限不会改变的数据集合 29 | 30 | ### 数据运算模型 31 | * 流式:只要数据一直在产生,计算就持续地进行 32 | * 批处理:在预先定义的时间内运行计算,当计算完成时释放计算机资源 33 | 34 | Flink 其特点就是处理 **流式数据** ,但是他其实是一个针对 **流数据**(DataStream)和 **批数据**(DataSet)的分布式处理引擎,是一个 **流批统一** 的计算引擎。 35 | 36 | ## 部署 37 | * Local 38 | > 直接在 IDE 中运行 Flink Job 时则会在本地启动一个 mini Flink 集群 39 | 40 | * Standalone 41 | > 在 Flink 目录下执行 bin/start-cluster.sh 脚本则会启动一个 Standalone 模式的集群 42 | 43 | * YARN 44 | > YARN 是 Hadoop 集群的资源管理系统,它可以在群集上运行各种分布式应用程序,Flink 可与其他应用并行于 YARN 中 45 | 46 | * Kubernetes 47 | * AWS、MapR、Aliyun OSS 48 | 49 | ## 结构 50 | ### API 51 | 至下而上: 52 | * Stateful Stream Processing 最底层提供了有状态流 53 | > 它将通过 Process Function 嵌入到 DataStream API 中。它允许用户可以自由地处理来自一个或多个流数据的事件,并使用一致性、容错的状态。除此之外,用户可以注册事件时间和处理事件回调,从而使程序可以实现复杂的计算。 54 | 55 | * DataStream/DataSet API 是 Flink 提供的核心 API 56 | > DataSet 处理有界的数据集,DataStream 处理有界或者无界的数据流。用户可以通过各种方法(map/flatmap/window/keyby/sum/max/min/avg/join 等)将数据进行转换或者计算。 57 | 58 | * Table API 表为中心的声明式 DSL 59 | > 其中表可能会动态变化(在表达流数据时)。Table API 提供了例如 select、project、join、group-by、aggregate 等操作,使用起来却更加简洁(代码量更少)。 你可以在表与 DataStream/DataSet 之间无缝切换,也允许程序将 Table API 与 DataStream 以及 DataSet 混合使用。 60 | 61 | * SQL 62 | >这一层抽象在语法与表达能力上与 Table API 类似,但是是以 SQL查询表达式的形式表现程序。SQL 抽象与 Table API 交互密切,同时 SQL 查询可以直接在 Table API 定义的表上执行。 Flink 除了 DataStream 和 DataSet API,它还支持 Table/SQL API,Flink 也将通过 SQL API 来构建统一的大数据流批处理引擎,因为在公司中通常会有那种每天定时生成报表的需求(批处理的场景,每晚定时跑一遍昨天的数据生成一个结果报表),但是也是会有流处理的场景(比如采用 Flink 来做实时性要求很高的需求),于是慢慢的整个公司的技术选型就变得越来越多了,这样开发人员也就要面临着学习两套不一样的技术框架,运维人员也需要对两种不一样的框架进行环境搭建和作业部署,平时还要维护作业的稳定性。 63 | 64 | ### 模拟程序与数据流结构 65 | [![](../assets/img/DevOps-BD-FLINK-1.png)](https://gitbook.cn/gitchat/column/5dad4a20669f843a1a37cb4f/topic/5db69938f6a6211cb96164da) 66 | 67 | * Source:数据输入 68 | > Flink 在流处理和批处理上的 source 大概有 4 类:基于本地集合的 source、基于文件的 source、基于网络套接字的 source、自定义的 source。自定义的 source 常见的有 Apache kafka、Amazon Kinesis Streams、RabbitMQ、Twitter Streaming API、Apache NiFi 等,当然你也可以定义自己的 source。 69 | 70 | * Transformation:数据转换的各种操作 71 | > 有 Map/FlatMap/Filter/KeyBy/Reduce/Fold/ Aggregations/Window/WindowAll/Union/Window join/Split/Select/Project 等,操作很多,可以将数据转换计算成你想要的数据。 72 | 73 | * Sink:数据输出 74 | > Flink 将转换计算后的数据发送的地点,你可能需要存储下来,Flink 常见的 Sink 大概有如下几类:写入文件、打印出来、写入 socket、自定义的 sink 。自定义的 Sink 常见的有 Apache kafka、RabbitMQ、MySQL、ElasticSearch、Apache Cassandra、Hadoop FileSystem 等,同理你也可以定义自己的 sink。 75 | 76 | ### 事件时间&处理时间语义 77 | * Event time 事件自身时间 78 | Ingestion Time 事件进入flink的时间 79 | Processing Time 事件被处理时的机器事件 80 | 81 | ### 窗口机制 82 | Flink 支持多种 Window,比如 Time Window、Count Window、Session Window,还支持自定义 Window 83 | 84 | ### 并行执行任务 85 | stream partitions 86 | operator subtasks 87 | 88 | ### 状态存储和容错 89 | Flink 是一款有状态的流处理框架,它提供了丰富的状态访问接口,按照数据的划分方式,可以分为 Keyed State 和 Operator State,在 Keyed State 中又提供了多种数据结构: 90 | 91 | * ValueState 92 | * MapState 93 | * ListState 94 | * ReducingState 95 | * AggregatingState 96 | 97 | 另外状态存储也支持多种方式: 98 | 99 | * MemoryStateBackend:存储在内存中 100 | * FsStateBackend:存储在文件中 101 | * RocksDBStateBackend:存储在 RocksDB 中 102 | 103 | Flink 中支持使用 Checkpoint 来提高程序的可靠性,开启了 Checkpoint 之后,Flink 会按照一定的时间间隔对程序的运行状态进行备份,当发生故障时,Flink 会将所有任务的状态恢复至最后一次发生 Checkpoint 中的状态,并从那里开始重新开始执行。 104 | 105 | 另外 Flink 还支持根据 Savepoint 从已停止作业的运行状态进行恢复,这种方式需要通过命令进行触发。 106 | 107 | ### 内存管理机制 108 | Flink 在 JVM 中提供了自己的内存管理,使其独立于 Java 的默认垃圾收集器。它通过使用散列、索引、缓存和排序有效地进行内存管理 109 | 110 | // TODO Flink 111 | -------------------------------------------------------------------------------- /BigData/商业智能.md: -------------------------------------------------------------------------------- 1 | BI (Business Intelligence) 商业智能 2 | ------------------ 3 | 4 | ## ETL Extract-Transform-Load 5 | ETL是将业务系统的数据经过抽取(Extract)、清洗转换(Transform)之后加载(Load)到数据仓库(Data Warehouse)或数据集市(Data Mart)中,成为联机分析处理(On-Line Analytical Processing,OLAP)、数据挖掘(Data Mining)的基础。目的是将企业中的分散、零乱、标准不统一的数据整合到一起,为企业的决策提供分析依据。 6 | > **抽取** 是将数据从已有的数据源中提取出来,例如通过 JDBC/Binlog 方式获取 MySQL 数据库的增量数据;**转换** 是对原始数据进行处理,例如将用户属性中的手机号替换为匿名的唯一 ID、计算每个用户对商品的平均打分、计算每个商品的购买数量、将 B 表的数据填充到 A 表中形成新的宽表等;**加载** 是将数据写入目的地。 7 | 8 | ![](../assets/img/ETL-ELT.png) 9 | 10 | * [Apache Camel](http://camel.apache.org/) 11 | * [Apatar](http://apatar.com/) 12 | * [Heka](http://hekad.readthedocs.io) 13 | * [ElasticSearch](https://www.elastic.co/products/logstash) 14 | * [Scriptella](http://scriptella.org/) 15 | * [Talend](http://www.talend.com/) 16 | * [Kettle](https://github.com/pentaho/pentaho-kettle) 17 | * [Informatica](https://www.informatica.com/) 18 | * [DataStage](https://www.ibm.com/products/infosphere-datastage) 19 | 20 | 21 | ## ELT 22 | ELT 和 ETL 相比,ETL 在数据源抽取后首先进行转换,然后将转换的结果写入目的地。ELT 则是在抽取后将结果先写入目的地,然后由下游应用利用数据库的聚合分析能力或者外部计算框架,例如 Spark 来完成转换的步骤。最大的区别是“重抽取和加载,轻转换”,从而可以用更简单的技术栈、更轻量的方案搭建起一个满足现代企业应用的数据集成平台。AI 应用内在的特点也使得 ELT 特别适合这个场景。 23 | 24 | * [Kafka](https://cwiki.apache.org/confluence/display/KAFKA/Ecosystem) 25 | * [Kafka Connect](https://docs.confluent.io/current/connect/index.html) 26 | * [DataX](https://github.com/alibaba/DataX) 27 | * [DataPipeline](https://www.datapipeline.com/) 28 | 29 | 30 | ## [案例](https://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655830097&idx=1&sn=a637be12d879ab6f43bc35a74cdc70c6) 31 | 设计要求在用户输入搜索内容时,要能从商家名称和商品名称两个维度去搜索,搜索出来的结果,按照准确率排序,并按商家所属商品的关联关系,来组合数据结构,同时提供 API 给业务系统调用。 32 | * 商家数据库和商品数据库是多台不同的服务器,并且数据量达百万级,如何才能实现跨数据库的数据同步呢? 33 | * 商家和商品的数据是有从属关系的,不然就会把肯德基的香辣鸡腿堡挂到麦当劳去,这就尴尬了! 34 | * 商家商品数据是经常更新的,比如修改价格、库存、上下架等,那搜索服务可不能搜出一堆过时的数据,如果客户明明搜出来的商品,点进去后却已下架了,那么客户就要吐槽了!如何实现搜索数据与源数据库增删改均实时同步呢? 35 | ### 架构设计 36 | * 首先,商家数据和商品数据分别存储在 2 个独立的 MySQL8 数据库,为满足商家数据和商品数据的关联,我们需要将两个库中所需要的表实时 ETL 到我们的搜索系统数据库。 37 | * 其次,数据从商家、商品数据库 ETL 到搜索系统数据库后,需要实时的组合成为商家关联商品数据结构,并以父子文档的格式,存储到 ES 中。 38 | * 最后,商家、商品数据库的增删改操作,需要实时的同步到 ES 中,也就是 ES 中的数据,需要支持实时的增加、删除和修改。 39 | * 为此,我们设计了 2 个 Canal 组件,第一个 Canal 实现数据 ETL,把商家、商品数据库的某些表及字段,抽取到搜索服务数据库。 40 | * 再利用第二个 Canal,读取搜索服务 MySQL 数据库的 Binlog,实时传输到 Kafka 消息队列,再由 canal adapter 对数据进行关联、父子文档映射等,将处理好的数据存储到 ElasticSearch 中。 41 | 42 | ## BI商业软件 43 | * [tableau](https://www.tableau.com/) 44 | * [powerbi](http://www.powerbi.com.cn/) 45 | * [powerbi microsoft](https://powerbi.microsoft.com/) 46 | * [tempo](http://www.meritdata.com.cn/) 47 | * [dataojo](https://www.dataojo.com/index.html) 48 | * [DataWorks](https://www.aliyun.com/product/bigdata/ide) 49 | -------------------------------------------------------------------------------- /BigData/大数据.md: -------------------------------------------------------------------------------- 1 | Big Data 2 | -------------- 3 | 4 | [![](../assets/img/big-data.png)](https://www.processon.com/view/link/5ee2424be0b34d4dba347744) 5 | ![](../assets/img/bigdata.png) 6 | 7 | ## 涉及关键知识点 8 | ### 存储引擎 9 | 10 | #### LSM 11 | Log Structured Merge Tree 日志结构合并树 12 | > Apache HBase, Apache Cassandra, MongoDB(Wired Tiger), LevelDB , RocksDB 都使用 LSM 存储引擎 13 | 14 | LSM 类存储引擎,将对数据的修改增量保存在内存中,达到指定大小限制之后批量把数据flush到磁盘中,磁盘中树定期可以做merge操作,合并成一棵大树,以优化写性能。因为数据是先写到内存中,所以为了防止内存数据丢失,会先把数据写入hlog中,也符合了数据库中标准,先写日志,再写数据。 15 | 16 | > 不过读取的时候稍微麻烦一些,读取时看这些数据在内存中,如果未能命中内存,则需要访问较多的磁盘文件。极端的说,基于LSM树实现的存储引擎(如:hbase)写性能比`B+树`(如:mysql)高了一个数量级,读性能却低了一个数量级。 17 | 18 | 19 | ### 容错 数据冗余技术 20 | #### 副本策略 21 | Hadoop2.x 中使用了副本方案(Scheme)。存储开销200% 22 | 如果有6个块,将有18个块占用空间 23 | #### 纠删码 Erasure Code 24 | Hadoop3.x 中使用Erasure编码处理容错。存储开销50% 25 | 如果有6个块,将有9个块占用空间,6块block,3块用于奇偶校验。 26 | 27 | [Erasure Coding Library](http://jerasure.org/) 28 | 29 | #### 磁盘阵列 30 | RAID,Redundant Arrays of Independent Disks,独立磁盘构成的具有冗余能力的阵列 31 | 32 | ## 生态系统 33 | 34 | * Hadoop 35 | * HBase 36 | * MapReduce 37 | * Pig 38 | > 脚本方式去描述MapReduce 39 | 40 | * Hive 41 | > SQL方式去描述MapReduce 42 | 43 | * Impala,Presto,Drill 44 | > 轻量级MapReduce 45 | 46 | * Hive on Tez, Spark, SparkSQL 47 | * Ambari 集群管理 48 | * Sqoop 49 | > SQL to Hadoop 用于在不同存储系统之间实现数据导入导出 50 | 51 | * Cassandra 52 | * Druid, ClickHouse 53 | * Flink 54 | * Kafka 55 | * MPP (Massively Parallel Processing) 大规模并行处理 56 | * Ranger, Kerberos, Knox 安全鉴权 57 | * Alluxio 58 | > 为数据驱动型应用和存储系统构建了桥梁, 将数据从存储层移动到距离数据驱动型应用更近的位置从而能够更容易被访问 59 | 60 | * Hudi 61 | > 使得您能在hadoop兼容的存储之上存储大量数据,同时它还提供两种流原语(插入更新,增量拉取),使得除了经典的批处理之外,还可以在数据湖上进行流处理 62 | 63 | * Altas 64 | > [元数据](https://mp.weixin.qq.com/s/0-k5PYkUCU1HCZCBPIWiAw) 65 | 66 | 67 | ### Hadoop 68 | #### HDFS 69 | 分布式文件系统 70 | 71 | * NameNode 72 | * DataNode 73 | 74 | #### YARN 75 | 集群资源管理器 76 | 77 | * ResourceManager 78 | * NodeManager 79 | 80 | #### MapReduce 81 | 批处理框架 82 | 83 | ### Zookeeper 84 | > 应用于大数据开发中的,统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等场景 85 | 86 | * Znode 87 | * 持久节点 88 | * 临时节点 89 | * 有序节点 90 | 91 | ### HBase 92 | 基于 Google 的 BigTable 论文,基于 HDFS 的面向列的数据库 93 | 94 | * HMaster 95 | * HRegionServer 96 | * HLog 97 | * HRegion 98 | * Store 99 | 100 | 101 | ### Apache Atlas 102 | [Apache Atlas](http://atlas.apache.org/) , Hadoop的数据治理和元数据框架。它为Hadoop集群提供了包括 数据分类、集中策略引擎、数据血缘、安全和生命周期管理在内的元数据治理核心能力。 103 | 104 | * 数据分类 105 | - 为元数据导入或定义业务导向的分类注释 106 | - 定义,注释,以及自动捕获数据集和底层元素之间的关系 107 | - 导出元数据到第三方系统 108 | * 集中审计 109 | - 捕获与所有应用,过程以及与数据交互的安全访问信息 110 | - 捕获执行,步骤,活动等操作的信息 111 | * 搜索与血缘 112 | - 预定义的导航路径用来探索数据分类以及审计信息 113 | - 基于文本的搜索特性来快速和准确的定位相关联的数据和审计事件 114 | - 对数据集血缘关系的可视化浏览使用户可以下钻到操作,安全以及数据起源相关的信息 115 | * 安全与策略引擎 116 | - 基于数据分类模式,属性以及角色的运行时合理合规策略 117 | - 基于分类-预测的高级策略定义以防止数据推导 118 | - 基于cell的属性和值的行/列级别的masking 119 | 120 | #### 架构 121 | [![](../assets/img/atlas-architecture.png)](http://atlas.apache.org/#/Architecture) 122 | 123 | ##### Core 124 | * Ingest / Export 125 | Ingest 组件允许将元数据添加到 Atlas, Export 组件暴露由 Atlas 检测到的元数据更改,以作为事件引发,消费者可以使用这些更改事件来实时响应元数据更改。 126 | 127 | * Type System 128 | Atlas 允许用户为他们想要管理的元数据对象定义一个模型。该模型由称为 "类型" 的定义组成。"类型" 的 实例被称为 "实体" 表示被管理的实际元数据对象。类型系统是一个组件,允许用户定义和管理类型和实体。由 Atlas 管理的所有元数据对象(例如Hive表)都使用类型进行建模,并表示为实体。要在 Atlas 中存储新类型的元数据,需要了解类型系统组件的概念。 129 | > 例如:Atlas 本身自带的 hive_table 类 130 | ``` 131 | Name: hive_table 132 | TypeCategory: Entity 133 | SuperTypes: DataSet 134 | Attributes: 135 | name: string 136 | db: hive_db 137 | owner: string 138 | createTime: date 139 | lastAccessTime: date 140 | comment: string 141 | retention: int 142 | sd: hive_storagedesc 143 | partitionKeys: array 144 | aliases: array 145 | columns: array 146 | parameters: map 147 | viewOriginalText: string 148 | viewExpandedText: string 149 | tableType: string 150 | temporary: boolean 151 | ``` 152 | ```json 153 | guid: "9ba387dd-fa76-429c-b791-ffc338d3c91f" 154 | typeName: "hive_table" 155 | status: "ACTIVE" 156 | values: 157 | name: “customers” 158 | db: { "guid": "b42c6cfc-c1e7-42fd-a9e6-890e0adf33bc", 159 | "typeName": "hive_db" 160 | } 161 | owner: “admin” 162 | createTime: 1490761686029 163 | updateTime: 1516298102877 164 | comment: null 165 | retention: 0 166 | sd: { "guid": "ff58025f-6854-4195-9f75-3a3058dd8dcf", 167 | "typeName": 168 | "hive_storagedesc" 169 | } 170 | partitionKeys: null 171 | aliases: null 172 | columns: [ { "guid": "65e2204f-6a23-4130-934a-9679af6a211f", 173 | "typeName": "hive_column" }, 174 | { "guid": "d726de70-faca-46fb-9c99-cf04f6b579a6", 175 | "typeName": "hive_column" }, 176 | ... 177 | ] 178 | parameters: { "transient_lastDdlTime": "1466403208"} 179 | viewOriginalText: null 180 | viewExpandedText: null 181 | tableType: “MANAGED_TABLE” 182 | temporary: false 183 | ``` 184 | 实体类型的每个实例都由唯一标识符 GUID 标识。 185 | 定义对象时,此 GUID 由 Atlas 服务器生成,并且在实体的整个生命周期内保持不变。在任何时间点,都可以使用其 GUID 访问此特定实体。 186 | 187 | * Graph Engine 188 | 在内部,Atlas 持久化它使用图形模型管理的元数据对象。 这种方法提供了极大的灵活性,并支持高效地处理元数据对象之间的丰富关系。 图形引擎组件负责在 Atlas 类型系统和底层图形持久化模型的类型和实体之间进行转换。 除了管理图对象之外,图引擎还为元数据对象创建适当的索引,以便有效地搜索它们。 Atlas 使用 JanusGraph 来存储元数据对象。 189 | 190 | ##### Integration 191 | * API 192 | Atlas 的所有功能都通过 REST API 向最终用户公开,该 API 允许创建、更新和删除类型和实体。 它也是查询和发现 Atlas 管理的类型和实体的主要机制。 193 | 194 | * Messaging 195 | 除了 API 之外,用户还可以选择使用基于 Kafka 的消息传递界面与 Atlas 集成。 这对于与 Atlas 通信元数据对象以及使用 Atlas 中可以构建应用程序的元数据更改事件都很有用。 如果希望使用与 Atlas 更松散耦合的集成,从而实现更好的可伸缩性、可靠性等,则消息传递界面尤其有用。 Atlas 使用 apache kafka 作为元数据通知事件的钩子和下游消费者之间的通信通知服务器。 事件是由 hook 和 Atlas 写到不同的 Kafka topics。 196 | * ATLAS_HOOK: 来自 各个组件的Hook 的元数据通知事件通过写入到名为 ATLAS_HOOK 的 Kafka topic 发送到 Atlas 197 | * ATLAS_ENTITIES:从 Atlas 到其他集成组件(如Ranger)的事件写入到名为 ATLAS_ENTITIES 的 Kafka topic 198 | 199 | ##### Metadata source 200 | Atlas 支持集成多种源的元数据开箱即用 201 | - [HBase](http://atlas.apache.org/#/HookHBase) 202 | - [Hive](http://atlas.apache.org/#/HookHive) 203 | - [Sqoop](http://atlas.apache.org/#/HookSqoop) 204 | - [Storm](http://atlas.apache.org/#/HookStorm) 205 | - [Kafka](http://atlas.apache.org/#/HookKafka) 206 | 207 | 元数据源需要实现以下两点: 208 | 首先,需要基于 atlas 的类型系统定义能够表达大数据组件元数据对象的元数据模型(例如 Hive 的元数据模型实现在 `org.apache.atlas.hive.model.HiveDataModelGenerator`; 209 | 然后,需要提供 hook 组件去从大数据组件的元数据源中提取元数据对象,实时侦听元数据的变更并反馈给 210 | atlas; 211 | 212 | ##### Apps 213 | * Admin UI 214 | 这个组件是一个基于 web 的应用程序,允许数据管理员和科学家发现和注释元数据。 这里最重要的是一个搜索界面和类似 SQL 的查询语言,可用于查询 Atlas 管理的元数据类型和对象。 Admin UI 使用 Atlas 的 REST API 来构建其功能。 215 | 216 | * Ranger Tag Based Policies 217 | Apache Ranger 是 Hadoop 生态系统的高级安全管理解决方案,与各种 Hadoop 组件进行了广泛的集成。 通过与 Atlas 集成,Ranger 允许安全管理员定义元数据驱动的安全策略,以实现有效的治理。 Ranger 是 Atlas 通知的元数据更改事件的使用者。 218 | 219 | 220 | ### 数据湖 Data lake 221 | 数据湖是一个以 **原始格式(通常是对象块或文件,数据不做预处理,保存数据的原始状态)存储数据的系统或存储库**。数据湖通常是所有企业数据的单一存储(存储库中会汇总多种数据源,是一个单一库)。用于报告、可视化、高级分析和机器学习等任务。数据湖可以包括来自 **关系数据库的结构化数据**(行和列)、**半结构化数据**(CSV、日志、XML、JSON)、**非结构化数据**(电子邮件、文档、pdf)和 **二进制数据**(图像、音频、视频)。 222 | 223 | > 大数据刚兴起的时候,数据主要用途是 BI 、报表、可视化。因此数据需要是结构化的,并且需要 ETL 对数据进行预处理。这个阶段数据仓库更适合完成这样的需求,所以企业大部分需要分析的数据都集中到数据仓库中。而机器学习的兴起对数据的需求更加灵活,如果从数据仓库中提数会有一些问题。比如:数据都是结构化的;数据是经过处理的可能并不是算法想要的结果;算法同学与数仓开发同学沟通成本较大等。我在工作中就遇到这种情况,做算法的同学需要经常理解我们的数仓模型,甚至要深入到做了什么业务处理,并且我们的处理可能并不是他们的想要的。基于上面遇到的各种问题,数据湖的概念应运而生。下面的表格对比一下数据湖和数据仓库的区别,主要来自 AWS 。 224 | 225 | > Hudi是一个用于构建数据湖的开源工具。在GitHub是很活跃的。我们构建数仓很多时候会选Hive。而构建数据湖我们可以选择Hudi。它提供了数据湖所必要的数据提取、数据管理、血缘追踪等功能,而且可以Hadoop、Spark等大数据框架很好地结合起来。 226 | 227 | > [优秀文章](https://mp.weixin.qq.com/s/2VQQo2y5OWHIAmelmJVD2Q) 228 | 229 | |特性|数据仓库|数据湖| 230 | |-----|-----|----| 231 | |数据|经过预处理的结构化关系数据|来自IoT设备、网站、移动应用程序、社交媒体、企业应用的非关系型和关系型数据| 232 | Schema|设计在数据仓库之前,写入型 Schema | 分析的时候临时建立 scheam 和表,用 SQL分析, 读取型 schema| 233 | 234 | * Data Lake Analytics Aliyun 235 | > 通过标准JDBC直接对阿里云OSS,TableStore,RDS,MongoDB等不同数据源中存储的数据进行查询和分析。DLA 无缝集成各类商业分析工具,提供便捷的数据可视化。阿里云OSS 可以存储各种结构化、半结构化、非结构化的数据,可以当做一个数据湖的存储库。DLA 使用前需要创建 Schema 、定义表,再进行后续分析。 236 | 237 | * Lake Formation AWS 238 | > 可以识别 S3 或关系数据库和 NoSQL 数据库中存储的现有数据,并将数据移动到 S3 数据湖中。使用 EMR for Apache Spark(测试版)、Redshift 或 Athena 进行分析。支持的数据源跟阿里云差不多。 239 | 240 | 241 | 工业数据空间(Industrial Data Space) 242 | -------------- 243 | -------------------------------------------------------------------------------- /BigData/联邦学习.md: -------------------------------------------------------------------------------- 1 | 联邦学习(Federated Learning) 2 | ----------- 3 | 在进行机器学习的过程中,各参与方可借助其他方数据进行联合建模。各方无需共享数据资源,即数据不出本地的情况下,进行数据联合训练,建立共享的机器学习模型。 4 | 5 | * 各方数据都保留在本地,不泄露隐私也不违反法规。 6 | * 多个参与者联合数据建立虚拟的共有模型,并且共同获益的体系 7 | * 在联邦学习的体系下,各个参与者的身份和地位平等 8 | * 联邦学习的建模效果和将整个数据集放在一处建模的效果相同,或相差不大 9 | > 在各个数据的用户 user alignment 或 特征 feature alignment 对齐的条件下 10 | 11 | * 迁移学习是在用户或特征不对齐的情况下,也可以在数据间通过交换加密参数达到知识迁移的效果 12 | * 联邦学习使多个参与方在保护数据隐私、满足合法合规要求的前提下继续进行机器学习,解决数据孤岛问题 13 | * 带动跨领域的企业级数据合作,催生基于联合建模的新业态和模式,降低技术提升成本和促进创新技术发展 14 | 15 | ## 联邦学习与差分隐私理论 16 | 联邦学习的特点使其可以被用来保护用户数据的隐私,但是他和大数据、数据挖掘领域中常用的隐私保护理论如差分隐私保护理论 Differential Privacy 、K匿名 K-Anonymity 和 l多样化 l-Diversity 等方法还是有较大差别的。首先联邦学习与传统隐私保护法的原理不同,联邦学习通过加密机制下的参数交换方式保护用户数据隐私,加密手段包括同态加密等。与 Differential Privacy 不同,其数据和模型本事不会进行传输,因此在数据层面上不存在泄露的可能,也不违法更严格的数据保护法方案,如GDPR(欧盟 2018 年正式施行的法案《通用数据保护条例》General Data Protection Regulation)等。而差分隐私保护理论、K匿名 和 l多样化等方法是通过在数据里加噪音,或采用概括化的方法模糊某些敏感属性,直到第三方不能区分个体为止,从而以较高的概率使数据无法还原,以此来保护用户隐私。但是,从本质上来说这些方法还是进行了原始数据的传输,存在着潜在被攻击的可能性,并且在 GDPR 等更严格的数据保护法案下这种数据隐私的保护方式可能不再适用。与之对应的联邦学习是对用户数据隐私保护更为有力的手段。 17 | 18 | ## 联邦学习与分布式机器学习 19 | 横向联邦学习中多方联合训练的方式与分布式机器学习 Distributed Machine Learning 有部分相似的地方。分布式机器学习涵盖了多个方面,包括把机器学习中的训练数据分布式存储、计算任务分布式运行、模型结果分布式发布等,参数服务器 Parameter Server 是分布式机器学习中一个典型的例子。参数服务器作为加速机器学习模型训练过程的一种工具,他将数据存储在分布式的工作节点上,通过一个中心式的调度节点调配数据分布和分配计算资源,以便更高效的获得最终的训练模型。而对于联邦学习而言,首先在于横向联邦学习中的工作节点代表的是模型训练的数据拥有方,其对本地的数据具有完全的自治权限,可以自主决定何时加入联邦学习进行建模,相对地在参数服务器中,中心节点始终占据着主导地位,因此联邦学习面对的是一个更复杂的学习环境;其次,联邦学习则强调模型训练过程中对数据拥有方的数据隐私保护,是一种应对数据隐私保护的有效措施,能更好地应对未来愈加严格的数据隐私和数据安全监管环境。 20 | 21 | ## 联邦学习与联邦数据库 22 | ## 联邦学习与区块链技术 23 | 区块链是一个机遇密码学安全的分布式账本,其方便验证,不可篡改。区块链2.0是一个去中心化的应用,通过使用开源的代码及分布式的存储和运行,保证极高的透明度和安全性,使数据不会被篡改。区块链的经典应用包括比特币、以太坊等。区块链与联邦学习都是一种去中心化的网络,区块链是一种安全P2P(peer to peer) 的网络结构,在联邦学习中,第三方会承担汇聚模型、管理等功能。联邦学习与区块链中,均有涉及到密码学、加密算法等基础技术。 24 | * 根据技术的不同,区块链技术使用的加密算法包括哈希算法,非对称加密等;联邦学习中使用同态加密等。 25 | * 从数据角度上看,区块链上通过加密的方式在各节点上记录了完整的数据,而联邦学习中,各方的数据均仅保留在本地。 26 | * 从奖励机制上看,区块链中,不同阶段之间通过竞争记账来获取奖励;在联邦学习中,多个参与方通过共同学习,提高模型训练结果,依据每一方的贡献来分配奖励。 27 | 28 | ## 联邦学习与多方安全计算 29 | 在联邦学习中,用户的隐私与安全是重中之重。为了保护用户隐私,防止联邦学习应用被恶意方攻击,多方安全计算技术可以在联邦学习中被应用,成为联邦学习技术框架中的一部分。学术界已经展开利用多方安全计算来增强联邦学习的安全性的研究。McMahan 指出,联邦学习可以通过差分隐私,多方安全计算,或它们的结合等技术来提供更强的安全保障。 Bonawitz 指出,联邦学习中,可以利用多方安全计算以安全的方式计算来自用户设备的模型参数更新的总和。 Truex 中提出了一种利用差分隐私和多方安全计算来保护隐私的联邦学习方法。Liu 提出将加性同态加密(AHE)应用与神经网络的多方计算。 30 | 31 | ## 联邦学习分类 32 | 针对不同数据集,联邦学习分为横向联邦学习(horizontal federated learning)、纵向联邦学习(vertical federated learning)与联邦迁移学习(Federated Transfer Learning,FmL)。 33 | ![](../assets/img/Federated-Learning-classify.png) 34 | * **横向联邦学习在两个数据集的用户特征重叠较多,而用户重叠较少的情况下**,我们把数据集按照横向(即用户维度)切分,并取出双方用户特征相同而用户不完全相同的那部分数据进行训练。这种方法叫做横向联邦学习。比如有两家不同地区的银行,它们的用户群体分别来自各自所在的地区,相互的交集很小。但是,它们的业务很相似,因此,记录的用户特征是相同的。此时,我们就可以使用横向联邦学习来构建联合模型。谷歌在2016年提出了一个针对安卓手机模型更新的数据联合建模方案:在单个用户使用安卓手机时,不断在本地更新模型参数并将参数上传到安卓云上,从而使特征维度相同的各数据拥有方建立联合模型。 35 | 36 | * **纵向联邦学习在两个数据集的用户重叠较多而用户特征重叠较少的情况下**,我们把数据集按照纵向(即特征维度)切分,并取出双方用户相同而用户特征不完全相同的那部分数据进行训练。这种方法叫做纵向联邦学习。比如有两个不同的机构,家是某地的银行,另一家是同一个地方的电商。它们的用户群体很有可能包含该地的大部分居民因此用户的交集较大。但是,由于银行记录的都是用户的收支行为与信用评级,而电商则保有用户的浏览与购买历史,因此它们的用户特征交集较小。纵向联邦学习就是将这些不同特征在加密的状态下加以聚合,以增强模型能力。目前,逻辑回归模型、树形结构模型和神经网络模型等众多机器学习模型已经逐渐被证实能够建立在此联邦体系上。 37 | 38 | * **联邦迁移学习在两个数据集的用户与用户特征重叠都较少的情况下**,我们不对数据进行切分,而利用迁移学习国来克服数据或标签不足的情况。这种方法叫做联邦迁移学习。比如有两个不同机构,一家是位于中国的银行,另一家是位于美国的电商。由于受地域限制,这两家机构的用户群体交集很小。同时,由于机构类型的不同,二者的数据特征也只有小部分重合。在这种情况下,要想进行有效的联邦学习,就必须引入迁移学习,来解决单边数据规模小和标签样本少的问题,从而提升模型的效果。 39 | 40 | 41 | ## 联邦学习开源框架 42 | * FATE。 微众银行 43 | * Pysyft。 OpenMinded 44 | * TensorFlow Federated。 Google 45 | * PaddleFL。 百度 46 | 47 | ![](../assets/img/Federated-Learning-opensource.png) 48 | 49 | ## 参考 50 | https://www.fedai.org/ 51 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | developer.ifuture.pro -------------------------------------------------------------------------------- /Database/PostgreSQL.md: -------------------------------------------------------------------------------- 1 | # PostgreSQL 2 | PostgreSQL 是一个免费的对象-关系数据库服务器(ORDBMS),在灵活的BSD许可证下发行。 3 | 4 | ## 基础操作 5 | 6 | ```shell 7 | sudo apt-get update 8 | sudo apt-get install postgresql postgresql-client 9 | 10 | sudo -i -u postgres 11 | ~$ psql -U postgres -h 127.0.0.1 -p 5432 12 | psql (9.5.17) 13 | Type "help" for help. 14 | 15 | postgres=# 16 | 17 | \q 退出 18 | ``` 19 | 20 | ```shell 21 | -- 查看所有数据库 22 | postgres=# \l 23 | List of databases 24 | Name | Owner | Encoding | Collate | Ctype | Access privileges 25 | ---------------------+----------+----------+---------+---------+----------------------- 26 | mastodon_production | mastodon | UTF8 | C.UTF-8 | C.UTF-8 | 27 | postgres | postgres | UTF8 | C.UTF-8 | C.UTF-8 | 28 | template0 | postgres | UTF8 | C.UTF-8 | C.UTF-8 | =c/postgres + 29 | | | | | | postgres=CTc/postgres 30 | template1 | postgres | UTF8 | C.UTF-8 | C.UTF-8 | =c/postgres + 31 | | | | | | postgres=CTc/postgres 32 | (4 rows) 33 | 34 | -- 切换数据库 35 | postgres=# \c mastodon_production 36 | You are now connected to database "mastodon_production" as user "postgres". 37 | mastodon_production=# 38 | 39 | -- 列出所有的表 40 | \dt 41 | 42 | -- 查看数据表 43 | select * from pg_tables; 44 | select tablename from pg_tables where tableowner='mastodon'; 45 | select tablename from pg_tables where schemaname='public'; 46 | 47 | -- 查看表结构 48 | \d 表名 49 | 50 | ``` 51 | -------------------------------------------------------------------------------- /Database/数据库.md: -------------------------------------------------------------------------------- 1 | Database 2 | ------- 3 | 4 | ## 分类与出色产品 5 | 6 | * SQL 7 | * 关系型数据库(RDB,Relational DateBase) 8 | > MySQL, Oracle 9 | 10 | * NoSQL 11 | * key-values数据库 12 | > Redis, Memcache, RocksDB, LevelDB, ArangoDB 13 | 14 | * Document数据库 15 | > MongoDB 16 | 17 | * 列存储数据库 18 | > HBase, ClickHouse 19 | 20 | * 时序数据库 21 | > [IoTDB](https://iotdb.apache.org/), [LinDB](https://github.com/lindb/lindb) ,KariosDB, OpenTSDB, [InfluxDB](https://github.com/influxdata/influxdb), Prometheus, [TDengine](https://github.com/taosdata/TDengine) 22 | 23 | * 图数据库 24 | > Neo4j, JanusGraph, [HugeGraph](https://hugegraph.github.io/hugegraph-doc/), [NebulaGraph](https://github.com/vesoft-inc/nebula) 25 | 26 | * NewSQL 27 | * [TiDB](https://github.com/pingcap/tidb) 28 | 29 | 30 | ## 关键字 31 | * RDBMS(Relational DataBase Management System)关系型数据库管理系统 32 | * ORDBMS 对象关系数据库系统,如:PostgreSQL 33 | * OODBMS 面向对象数据库管理系统 34 | * ACID事务 35 | > Atomic, Consistency, Isolation, Durability 36 | > **Atomic(原子性)**:指整个数据库事务是不可分割的工作单位。只有使据库中所有的操作执行成功,才算整个事务成功;事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。 37 | > **Consistency(一致性)**:指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。例如对银行转帐事务,不管事务成功还是失败,应该保证事务结束后ACCOUNTS表中Tom和Jack的存款总额为2000元。 38 | > **Isolation(隔离性)**:指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。 39 | > **Durability(持久性)**:指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。 40 | 41 | * OLTP、OLAP 42 | > 联机事务处理OLTP(on-line transaction processing) 43 | 联机分析处理OLAP(On-Line Analytical Processing) 44 | [比较](https://www.cnblogs.com/hhandbibi/p/7118740.html) 45 | -------------------------------------------------------------------------------- /DevOps/分布式系统数据一致性.md: -------------------------------------------------------------------------------- 1 | 分布式系统数据一致性 2 | -------------- 3 | 分布式系统中存在 CAP 问题 4 | 一致性( C onsistency)、可用性( A vailability),以及分区容错性( P artition Tolerance) 5 | 6 | ## 分布式系统 7 | ### 两阶段提交协议(2PC:Two-Phase Commit) 8 | 该协议存在一个 **协调者** 单点,与多个 **参与者** 节点 9 | 10 | #### 第一阶段:投票 11 | 该阶段的主要目的在于打探数据库集群中的各个参与者是否能够正常的执行事务,具体步骤如下: 12 | * 协调者向所有的参与者发送事务执行请求,并等待参与者反馈事务执行结果; 13 | * 事务参与者收到请求之后,执行事务但不提交,并记录事务日志; 14 | * 参与者将自己事务执行情况反馈给协调者,同时阻塞等待协调者的后续指令。 15 | 16 | #### 第二阶段:事务提交 17 | 在经过第一阶段协调者的询盘之后,各个参与者会回复自己事务的执行情况,这时候存在 3 种可能性: 18 | 1. 所有的参与者都回复能够正常执行事务。 19 | ![正常执行事务](../assets/img/distributed-transaction-2-phase-1.png) 20 | 2. 一个或多个参与者回复事务执行失败。 21 | 3. 协调者等待超时。 22 | ![需要回归事务](../assets/img/distributed-transaction-2-phase-2.png) 23 | 24 | #### 缺点与存在的问题 25 | * 同步阻塞问题 26 | > 执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态 27 | 28 | * 单点故障 29 | > 由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题) 30 | 31 | * 数据不一致 32 | > 在阶段二中,当协调者向参与者发送commit请求之后,发生了局部网络异常或者在发送commit请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了commit请求。而在这部分参与者接到commit请求之后就会执行commit操作。但是其他部分未接到commit请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。 33 | 34 | 针对上述问题可以引入 **超时机制** 和 **互询机制** 在很大程度上予以解决。 35 | 36 | 对于协调者来说如果在指定时间内没有收到所有参与者的应答,则可以自动退出 WAIT 状态,并向所有参与者发送 rollback 通知。对于参与者来说如果位于 READY 状态,但是在指定时间内没有收到协调者的第二阶段通知,则不能武断地执行 rollback 操作,因为协调者可能发送的是 commit 通知,这个时候执行 rollback 就会导致数据不一致。 37 | 38 | 此时,我们可以介入互询机制,让参与者 A 去询问其他参与者 B 的执行情况。如果 B 执行了 rollback 或 commit 操作,则 A 可以大胆的与 B 执行相同的操作;如果 B 此时还没有到达 READY 状态,则可以推断出协调者发出的肯定是 rollback 通知;如果 B 同样位于 READY 状态,则 A 可以继续询问另外的参与者。只有当所有的参与者都位于 READY 状态时,此时两阶段提交协议无法处理,将陷入长时间的阻塞状态。 39 | 40 | ### 三阶段提交协议(3PC:Three-Phase Commit) 41 | 针对两阶段提交存在的问题,三阶段提交协议通过引入一个 预询盘 阶段,以及超时策略来减少整个集群的阻塞时间,提升系统性能 42 | 43 | #### 第一阶段:预询盘 44 | 该阶段协调者会去询问各个参与者是否能够正常执行事务,参与者根据自身情况回复一个预估值,相对于真正的执行事务,这个过程是轻量的,具体步骤如下: 45 | * 协调者向各个参与者发送事务询问通知,询问是否可以执行事务操作,并等待回复; 46 | * 各个参与者依据自身状况回复一个预估值,如果预估自己能够正常执行事务就返回确定信息,并进入预备状态,否则返回否定信息。 47 | 48 | #### 第二阶段:预提交 49 | 本阶段协调者会根据第一阶段的询盘结果采取相应操作,询盘结果主要有 3 种: 50 | 1. 所有的参与者都返回确定信息。 51 | > 继续进入第三阶段 52 | 2. 一个或多个参与者返回否定信息。 53 | 3. 协调者等待超时。 54 | ![需要终止事务](../assets/img/distributed-transaction-3-phase-1.png) 55 | 56 | #### 第三阶段:事务提交 57 | 如果第二阶段事务未中断,那么本阶段协调者将会依据事务执行返回的结果来决定提交或回滚事务,分为 3 种情况: 58 | 1. 所有的参与者都能正常执行事务。 59 | ![正常执行](../assets/img/distributed-transaction-3-phase-2.png) 60 | 2. 一个或多个参与者执行事务失败。 61 | 3. 协调者等待超时。 62 | ![需要回滚事务](../assets/img/distributed-transaction-3-phase-3.png) 63 | 64 | 虽然 3PC 对数据强一致性更有保障,但基于效率问题,2PC在各大分布式系统中使用更广泛。 65 | 66 | ## 区块链分布式系统 67 | ### 哈希时间锁定 68 | 哈希时间锁定合约 HTLC(Hash TimeLock Contract) 69 | 70 | [FISCO BCOS WeCross HTLC](https://github.com/JimmyShi22/WeCross/blob/ca56c8a3d432b48dc9bc69f400240964abaf9211/src/main/resources/chains-sample/bcos/htlc/HTLC.sol) 71 | 72 | 哈希时间锁定合约的处理流程基于哈希算法和超时机制,假设有两个区块链 A 和 B,试图交换位于链 A 的资产 α 和位于链 B 的资产 β,则整个哈希时间锁定的流程如下: 73 | * A 首先选取一个秘密随机数 S,使用特定的哈希算法计算出 S 的哈希值 H,之后 A 将 H 发给 B,同时 A 和 B 协商两个时间点 T0 和 T1,确保 T0 >T1。 74 | * A 基于 H 和 T0 创建资产锁定智能合约 LockContractA,该智能合约会锁定资产 α,其可以使用 S 来解锁并将资产 α 转移给 B,如果在 T0 前仍未解锁,则会自动撤销锁 75 | 定,且不会发生任何资产转移。 76 | * B 基于 H 和 T1 创建资产锁定智能合约 LockContractB,该智能合约会锁定资产 β,其可以使用 S 来解锁并将资产 β 转移给 A,如果在 T1 前仍未解锁,则会自动撤销锁 77 | 定,且不会发生任何资产转移。 78 | * A 使用秘密随机数 S,调用 B 上的智能合约 LockContractB,将资产 β 转移给 A 。 79 | * 经过上述步骤,B 获得了秘密随机数 S,B 使用 S 调用 A 上的智能合约 LockContractA,将资产 α 转移给 B,资产交换完成。 80 | * 如果 A 或 B 任意一方超时未执行操作,则在 T1 时间点后,B 资产会撤销锁定,T0时间点后,A 资产会撤销锁定,还原初始状态。 81 | * T0 和 T1 用于避免 A 或 B 单方延误交易,所以这其中的交易包 α 和交易包 β 都需要设定时间限制,超出这个时间限制后,相关资产立即撤销锁定,原路返回。整体操作时序流程 82 | 如下图所示。 83 | ![](../assets/img/HTLC.png) 84 | 85 | 86 | 87 | // TODO 延伸知识:比特币闪电网络,RSMC,跨链 88 | 89 | ### Paxos 90 | 91 | > 准确的说 Paxos 并不非**一致性算法**,它应该属于**共识算法**。一致性(consistency)和共识(consensus)并不是同一个概念 92 | 93 | #### 角色 94 | * proposers 提出提案,提案信息包括提案编号和提议 95 | * acceptor 收到提案后可以接受提案 96 | 97 | ![paxos](../assets/img/paxos-flow.png) 98 | 99 | #### 第一阶段:Prepare阶段 100 | 101 | A把申请修改的请求Prepare Request发给所有的结点A,B,C。注意,Paxos算法会有一个Sequence Number(你可以认为是一个提案号,这个数不断递增,而且是唯一的,也就是说A和B不可能有相同的提案号),这个提案号会和修改请求一同发出,任何结点在“Prepare阶段”时都会拒绝其值小于当前提案号的请求。所以,结点A在向所有结点申请修改请求的时候,需要带一个提案号,越新的提案,这个提案号就越是是最大的。 102 | 103 | 如果接收结点收到的提案号n大于其它结点发过来的提案号,这个结点会回应Yes(本结点上最新的被批准提案号),并保证不接收其它 可以理解成 FaaS(Function as a Service,函数即服务) 9 | 代表产品:AWS Lambda 10 | 其实,最初“无服务器”意在帮助开发者摆脱运行后端应用程序所需的服务器设备的设置和管理工作。这项技术的目标并不是为了实现真正意义上的“无服务器”,而是指由第三方供应商负责后端基础结构的维护,以服务的方式为开发者提供所需功能,例如数据库、消息,以及身份验证等。这种服务基础结构通常可以叫做后端即服务(Backend-as-a-Service,BaaS),或移动后端即服务(MobileBackend-as-a-service,MBaaS)。 11 | 现在,无服务器架构是指大量依赖第三方服务(也叫做后端即服务,即“BaaS”)或暂存容器中运行的自定义代码(函数即服务,即“FaaS”)的应用程序,函数是无服务器架构中抽象语言运行时的最小单位,在这种架构中,我们并不看重运行一个函数需要多少CPU或RAM或任何其他资源,而是更看重运行函数所需的时间,我们也只为这些函数的运行时间付费。无服务器架构中函数可以多种方式触发,如定期运行函数的定时器、HTTP请求或某些相关服务中的某个事件。 12 | 13 | 14 | > 2014 年可以认为是微服务 1.0 的元年,当年有几个标志性事件,一是 Martin Fowler 在其博客上发表了”Microservices”一文,正式提出微服务架构风格;二是 Netflix 微服务架构经过多年大规模生产验证,最终抽象落地形成一整套开源的微服务基础组件,统称 NetflixOSS,Netflix 的成功经验开始被业界认可并推崇;三是 Pivotal 将 NetflixOSS 开源微服务组件集成到其 Spring 体系,推出 Spring Cloud 微服务开发技术栈。 15 | > 2017,微服务技术生态又发生了巨大变化,容器,PaaS,Cloud Native,gRPC,ServiceMesh,Serverless 等新技术新理念你方唱罢我登场,不知不觉我们又来到了微服务 2.0 时代。 16 | 17 | 18 | 19 | ## Spring cloud 与 Kubernetes 组件 20 | 21 | 分别使用Spring cloud Kubernetes微服务的实现组件,_好多也是可以共用的_ 22 | 23 | | | Spring Cloud | Kubernetes | 24 | | :--- | :--- | :--- | 25 | | 服务发现 | Eureka , Consul , Nacos | kube-dns Services | 26 | | 服务通讯 | Ribbon , Feign | Thrift , grpc | 27 | | 配置管理 | [Config](https://spring.io/projects/spring-cloud-config) , [Bus](https://spring.io/projects/spring-cloud-bus) ,Nacos Config , [Apollo](https://github.com/ctripcorp/apollo) | ConfigMap | 28 | | 负载均衡 | Ribbon RestTemplate | Services , Ingress | 29 | | 网关 | Zuul , Gateway | Kong | 30 | | 链路跟踪 | Sleuth , Zipkin, [Jaeger](https://github.com/jaegertracing/jaeger), [CAT](https://github.com/dianping/cat), [Pinpoint](https://github.com/naver/pinpoint) | OpenTracing | 31 | | 容错熔断器 | Hystrix, Sentinel | | 32 | | 安全模块 | Security | | 33 | | 分布式日志 | ELK | ELK, [ElastAlert](https://github.com/Yelp/elastalert) | 34 | | 任务管理 | Batch | Jobs | 35 | | 分布式事务 | seata | | 36 | 37 | 其他微服务DevOps相关优秀产品 38 | * NewSQL 39 | > [TiDB](https://github.com/pingcap/tidb) 40 | * 时间序列数据库(TSDB) 41 | > [IoTDB](https://iotdb.apache.org/), [LinDB](https://github.com/lindb/lindb) ,KariosDB, OpenTSDB, InfluxDB, Prometheus, [TDengine](https://github.com/taosdata/TDengine) 42 | * 分布式数据访问 43 | > shardingjdbc, MyCAT 44 | * 任务调度系统 45 | > xxl-job, elastic-job 46 | * Redis 运维 47 | > [Cachecloud](https://github.com/sohutv/cachecloud) Redis 私有云平台 48 | > codis , [twemproxy](https://github.com/twitter/twemproxy) proxy for redis 49 | > Ansible 50 | * 报表 51 | > Grafana 52 | 53 | 各大网关 54 | 55 | | | 支持公司 | 实现语言 | 亮点 | 不足 | 56 | | -------------------------- | --------------- | ------------- | --------------------------------- | ---------------------- | 57 | | Nginx(2004) | Nginx Inc | C/Lua | 高性能,成熟稳定 | 门槛高,偏运维,可编程弱 | 58 | | Zuul1(2012) | Netflix/Pivotal | Java | 成熟,简单门槛低 | 门槛较高 | 59 | | Spring Cloud Gateway(2016) | Pivotal | Java | 异步,配置灵活 | 早期产品 | 60 | | Envoy(2016) | Lyft | C++ | 高性能,可编程API/ServiceMesh集成 | 门槛较高 | 61 | | Kong(2014) | Kong Inc | OpenResty/Lua | 高性能,可编程API | 门槛较高 | 62 | | Traefik(2015) | Containous | Golang | 云原生,可编程API/对接各种服务发现 | 生产案例不太多 | 63 | 64 | #### 优秀案例 65 | 66 | [Microservice Architectures With Spring Cloud and Docker](https://dzone.com/articles/microservice-architecture-with-spring-cloud-and-do) 67 | 68 | [Microservice Architecture with Spring Boot, Spring Cloud and Docker ](https://github.com/sqshq/PiggyMetrics) 69 | 70 | 71 | ## 负载均衡 72 | * OSPF(开放式最短链路优先)是一个内部网关协议(Interior Gateway Protocol,简称IGP)。OSPF通过路由器之间通告网络接口的状态来建立链路状态数据库,生成最短路径树,OSPF会自动计算路由接口上的Cost值,但也可以通过手工指定该接口的Cost值,手工指定的优先于自动计算的值。OSPF计算的Cost,同样是和接口带宽成反比,带宽越高,Cost值越小。到达目标相同Cost值的路径,可以执行负载均衡,最多6条链路同时执行负载均衡。 73 | * LVS (Linux VirtualServer),它是一种集群(Cluster)技术,采用IP负载均衡技术和基于内容请求分发技术。调度器具有很好的吞吐率,将请求均衡地转移到不同的服务器上执行,且调度器自动屏蔽掉服务器的故障,从而将一组服务器构成一个高性能的、高可用的虚拟服务器。 74 | * Nginx实现负载均衡的方式主要有三种:轮询、加权轮询、ip hash轮询 75 | 76 | 77 | ## 关键问题字 78 | * C10K ----> epoll 79 | -------------------------------------------------------------------------------- /DevOps/缓存.md: -------------------------------------------------------------------------------- 1 | 缓存 cache 2 | ------- 3 | 4 | ## Cache Aside Pattern 5 | 缓存+数据库读写的模式 6 | * 读数据:先查缓存,不过不存在,查数据库,并更新缓存。 7 | * 更新数据:先更新数据库,再删除缓存。 8 | > 1 . 直接删除就行,不要更新缓存。缓存的数据可能是数据库取出后需要进行业务整合的,应该在查数据的时候再做此操作,提高更新性能。 9 | > 还有可能此数据更新比较频繁,更新了10次,却只查了2次。那么如果你每次更新数据的时候都重新更新缓存,则非常浪费资源。hibernate的lazy加载有点类似,只要需要的时候才去加载。 10 | > 2 . 顺序不应颠倒。在高并发下可能会发生缓存了旧数据的情况,导致数据不一致。 11 | > 一个更新操作过来后,如果先删除缓存,在执行更新数据库逻辑时,另一个线程来查询,发现缓存没数据,去数据库取出了还未更新的旧数据,然后放置缓存,就发生了数据不一致问题。 12 | 13 | ## 缓存穿透 14 | 15 | 大多情况下缓存key是以ID为基础设置的,那么黑客故意删除他的一条数据,并记住这个ID,或者使用任意方式查询一个不存在的ID,比如,自增ID,使用负数取查询等等。那么他使用这个ID去查数据,缓存为空,导致查数据库,还是空,缓存不更新,那么他每次请求都能穿透缓存进入数据库。导致所有流量全部打入数据库,导致数据库重压甚至奔溃。 16 | 当然整体的系统设计还会加上API网关进行限流,熔断,黑名单,参数合法验证等保护措施,不一致压垮,但必定还是会受到影响。 17 | **解决方案:** 18 | * [BloomFilter](../algorithm/BloomFilter.md),BitMap 19 | * 对查询出来为空的数据也设置缓存。设置一个常量,标志是空数据,并设置过期时间,在未过期之前,不用担心被穿透的问题。这种方法非常简单,高效,但存在一棒子打死全部的问题,缓存的过期时间设置也是非常难判断,时间短导致穿透次数的增加,数据库压力的增加;时间长,可能会影响正常数据的查询。所以还是推荐上面的那种解决方案。 20 | 21 | 22 | ## Redis 23 | 24 | ### 数据结构 25 | 26 | * 基本数据类型:字符串String、字典Hash、列表List、集合Set、有序集合SortedSet 27 | * HyperLogLog、Geo、Pub/Sub 28 | * Redis Module : BloomFilter, RedisSearch, Redis-ML, BitMap 29 | 30 | ### 淘汰策略 31 | 当 Redis 用于缓存的内存不足时,需要怎么处理。就是根据淘汰策略决定的 32 | * **noeviction**:新写入操作会报错。 33 | * **allkeys-lru**:在键空间中,移除最近最少使用 (less recently used ,LRU) 的key。 34 | * **allkeys-random**:在键空间中,随机移除某个key。 35 | * **allkeys-lfu** : LFU (Least Frequently Used)最不常用的 36 | * **volatile-lfu** :在设置了过期时间的键空间中,移除最不常用的 37 | * **volatile-lru**:在设置了过期时间的键空间中,移除最近最少使用的key。 38 | * **volatile-random**:在设置了过期时间的键空间中,随机移除某个key。 39 | * **volatile-ttl**:在设置了过期时间的键空间中,有更早过期时间(time to live,TTL) 的key优先移除。 40 | 41 | 默认是抛出异常 [`redis.cnf`](https://github.com/antirez/redis/blob/unstable/redis.conf#L841) 42 | ```bash 43 | maxmemory-policy noeviction 44 | ``` 45 | 46 | ### 持久化机制 47 | * RDB:快照形式是直接把内存中的数据保存到一个 dump 的文件中,定时保存。(默认机制) 48 | * AOF:把所有的对 Redis 的服务器进行修改的命令都存到一个文件里。 49 | * 混合模式:since Redis4.0 50 | 51 | ### 应用 52 | * 分布式锁 53 | -------------------------------------------------------------------------------- /DevOps/网络.md: -------------------------------------------------------------------------------- 1 | 网络 2 | --------- 3 | 4 | ## 七层模型 5 | OSI(Open System Interconnection)7层**参考**模型 6 | 7 | 7. 应用层 8 | 网络服务与最终用户的一个接口。 9 | 协议有:HTTP FTP TFTP SMTP SNMP DNS TELNET HTTPS POP3 DHCP 10 | 6. 表示层 11 | 数据的表示、安全、压缩。(在五层模型里面已经合并到了应用层) 12 | 格式有,JPEG、ASCll、EBCDIC、加密格式等 13 | 5. 会话层 14 | 建立、管理、终止会话。(在五层模型里面已经合并到了应用层) 15 | 对应主机进程,指本地主机与远程主机正在进行的会话 16 | 4. **传输层** 17 | 定义传输数据的协议端口号,以及流控和差错校验。 18 | 协议有:TCP UDP,数据包一旦离开网卡即进入网络传输层 19 | 3. **网络层** 20 | 进行逻辑地址寻址,实现不同网络之间的路径选择。 21 | 协议有:ICMP IGMP IP(IPV4 IPV6) 22 | 2. **数据链路层** 23 | 建立逻辑连接、进行硬件地址寻址、差错校验等功能。(由底层网络定义协议) 24 | 将比特组合成字节进而组合成帧,用MAC地址访问介质,错误发现但不能纠正。 25 | 1. 物理层 26 | 建立、维护、断开物理连接。(由底层网络定义协议) 27 | 28 | 29 | ## 基础 30 | ```shell 31 | exec 9<> /dev/tcp/www.baidu.com/80 32 | echo -e "GET / HTTP/1.0\n" 1>& 9 33 | cat 0<& 9 34 | 35 | netstat -natp # t代表TCP 36 | 37 | tcpdump -nn -i eth0 port 80 or arp 38 | 39 | route -n 40 | arp -a 41 | ``` 42 | 43 | ## MTU 44 | 以太网(Ethernet)数据帧的长度必须在46-1500字节之间,这是由以太网的物理特性决定的。这个1500字节被称为链路层的MTU(最大传输单元Maximum Transmission Unit) 45 | 46 | 回环(loopback)的MTU不受这个限制,但UDP最大理论长度65507 47 | 48 | ```shell 49 | lo0: flags=8049 mtu 16384 50 | options=1203 51 | inet 127.0.0.1 netmask 0xff000000 52 | inet6 ::1 prefixlen 128 53 | inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 54 | nd6 options=201 55 | 56 | en0: flags=8863 mtu 1500 57 | options=400 58 | ether f8:ff:c2:2e:91:1d 59 | inet6 fe80::c1e:3f6b:4f59:1552%en0 prefixlen 64 secured scopeid 0x6 60 | inet 192.168.0.101 netmask 0xffffff00 broadcast 192.168.0.255 61 | nd6 options=201 62 | media: autoselect 63 | status: active 64 | ``` 65 | 66 | > 由于网络接口卡的制约,mtu的长度被限制在1500字节,这个长度指的是链路层的数据区。对于大于这个数值的分组可能被分片,否则无法发送,而分组交换的网络是不可靠的,存在着丢包。IP 协议的发送方不做重传。接收方只有在收到全部的分片后才能 reassemble并送至上层协议处理代码,否则在应用程序看来这些分组已经被丢弃。 67 | > 假定同一时刻网络丢包的概率是均等的,那么较大的IP datagram必然有更大的概率被丢弃,因为只要丢失了一个fragment,就导致整个IP datagram接收不到。不超过MTU的分组是不存在分片问题的。 68 | > MTU的值并不包括链路层的首部和尾部的18个字节。所以,这个1500字节就是网络层IP数据报的长度限制。因为IP数据报的首部为20字节,所以IP数据报的数据区长度最大为1480字节。而这个1480字节就是用来放TCP传来的TCP报文段或UDP传来的UDP数据报的。又因为UDP数据报的首部8字节,所以UDP数据报的数据区最大长度为1472字节。这个1472字节就是我们可以使用的字节数。 69 | > 当我们发送的UDP数据大于1472的时候会怎样呢?这也就是说IP数据报大于1500字节,大于MTU。这个时候发送方IP层就需要分片(fragmentation)。把数据报分成若干片,使每一片都小于MTU。而接收方IP层则需要进行数据报的重组。而更严重的是,由于UDP的特性,当某一片数据传送中丢失时,接收方便无法重组数据报。将导致丢弃整个UDP数据报。因此,在普通的局域网环境下,将UDP的数据控制在1472字节以下为好。 70 | > 进行Internet编程时则不同,因为Internet上的路由器可能会将MTU设为不同的值。如果我们假定MTU为1500来发送数据的,而途经的某个网络的MTU值小于1500字节,那么系统将会使用一系列的机制来调整MTU值,使数据报能够顺利到达目的地。鉴于Internet上的标准MTU值为576字节,所以在进行Internet的UDP编程时,最好将UDP的数据长度控件在548字节(576-8-20)以内。 71 | 72 | ## 关键字 73 | * RTT(Round Trip Time)往返时间 74 | * RTO(Retransmission TimeOut) 重传超时时间 75 | * ARQ (Automatic Repeat-reQuest) 自动重传请求 76 | * MTU (Maximum Transmission Unit) 最大传输单元 77 | 78 | ## 可靠UDP 79 | * [KCP](https://github.com/skywind3000/kcp) 80 | * [UDT](https://github.com/dump247/udt-net) 81 | -------------------------------------------------------------------------------- /DevOps/运维.md: -------------------------------------------------------------------------------- 1 | ## Linux 2 | 3 | 系统查看 4 | ```shell 5 | # 查看PCI总线信息 6 | lspci 7 | # 硬件组件 8 | lshw [options] 9 | # 显卡GPU 10 | nvidia-smi 11 | ``` 12 | 13 | 14 | ## 监控 15 | DataDog、Sysdig、Prometheus 16 | 17 | ## Nginx 18 | 19 | ```shell 20 | location ^~/user/ { 21 | proxy_set_header Host $host; 22 | proxy_set_header X-Real-IP $remote_addr; 23 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 24 | proxy_set_header X-NginX-Proxy true; 25 | 26 | proxy_pass http://user/; 27 | } 28 | location ^~/user/ { 29 | proxy_set_header Host $host; 30 | proxy_set_header X-Real-IP $remote_addr; 31 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 32 | proxy_set_header X-NginX-Proxy true; 33 | 34 | rewrite ^/user/(.*)$ /$1 break; 35 | proxy_pass http://user; 36 | } 37 | ``` 38 | 39 | ## mysql 扩容 40 | ```SHELL 41 | sudo umount /mnt/mydisk 42 | 43 | 44 | # 查看磁盘(显示所有磁盘) 45 | fdisk -l 46 | # 查看磁盘使用情况(未挂载的磁盘不会显示) 47 | df -h 48 | 49 | 50 | # dev/vdb是前面查出来的 51 | diskNumber=dev/vdb 52 | mkdir /data 53 | parted /$diskNumber mklabel gpt 54 | parted /$diskNumber mkpart opt 0% 100% 55 | sleep 1 56 | mkfs -t ext4 /${diskNumber}1 57 | mount /${diskNumber}1 /data 58 | df -h 59 | blkid /${diskNumber}1 |awk '{print $2 " /'$diskNumber' ext4 defaults 0 0"}' >> /etc/fstab 60 | cat /etc/fstab 61 | 62 | 63 | # 创建文件夹 64 | mkdir -p /vdb/mysql_data/mysql 65 | # 复制mysql未挂载前的数据 66 | cp -rf /var/lib/mysql/* /vdb/mysql_data/mysql 67 | # 授权mysql用户组 68 | chown -R mysql:mysql /vdb/mysql_data 69 | # 授权文件夹 70 | chmod -R 700 /vdb/mysql_data 71 | 72 | vim /etc/apparmor.d/usr.sbin.mysqld 73 | 74 | # Allow data dir access 75 | 将 /var/lib/mysql/ r, 76 | /var/lib/mysql/** rwk, 77 | 修改为 78 | # Allow data dir access 79 | /vdb/mysql_data/mysql/ r, 80 | /vdb/mysql_data/mysql/** rwk, 81 | 82 | 83 | service mysql restart 84 | 85 | # 查看binlog是否关闭 86 | show variables like 'log_%'; 87 | # 查看端口是否修改 88 | show global variables like 'port'; 89 | # 查看挂载目录是否生效 90 | show global variables like 'datadir'; 91 | 92 | ``` 93 | -------------------------------------------------------------------------------- /Java/JAVA响应式编程.md: -------------------------------------------------------------------------------- 1 | Reactive 响应式编程 2 | -------- 3 | * [Reactive Streams](https://github.com/reactive-streams/reactive-streams-jvm) 4 | * [RxJava](https://github.com/ReactiveX/RxJava) 5 | * [Alibaba RSocket Broker](https://github.com/alibaba/alibaba-rsocket-broker) 6 | * Actor模型, Akka 7 | * Spring-webflux, [Spring Cloud Gateway](https://spring.io/projects/spring-cloud-gateway)直接替代传统的 Servlet Web 8 | * Project Reactor 9 | * Java9 Flow API 10 | 11 | //TODO 12 | -------------------------------------------------------------------------------- /Java/JAVA字节码.md: -------------------------------------------------------------------------------- 1 | Java 字节码 Bytecode 2 | ---------- 3 | 4 | 从写 Java 文件到通过编译器编译成 class 文件(也就是字节码文件),再到虚拟机执行 class 文件。不论该字节码文件来自何方,由哪种编译器编译,甚至是手写字节码文件,只要符合java虚拟机的规范,那么 jvm 就能够执行该字节码文件。 5 | 6 | ## 简述 7 | Demo.java 8 | ```java 9 | package pro.ifuture.myday.utils; 10 | 11 | public class Demo { 12 | private String text = "hello world"; 13 | 14 | public static void main(String[] args) { 15 | System.out.print("hello world"); 16 | } 17 | } 18 | ``` 19 | 20 | 执行 `javac Demo.java` 得到 `Demo.class` 21 | 22 | ``` 23 | CAFEBABE 00000037 00210A00 07001208 24 | 00130900 06001409 00150016 0A001700 25 | 18070019 07001A01 00047465 78740100 26 | 124C6A61 76612F6C 616E672F 53747269 27 | 6E673B01 00063C69 6E69743E 01000328 28 | 29560100 04436F64 6501000F 4C696E65 29 | 4E756D62 65725461 626C6501 00046D61 30 | 696E0100 16285B4C 6A617661 2F6C616E 31 | 672F5374 72696E67 3B295601 000A536F 32 | 75726365 46696C65 01000944 656D6F2E 33 | 6A617661 0C000A00 0B01000B 68656C6C 34 | 6F20776F 726C640C 00080009 07001B0C 35 | 001C001D 07001E0C 001F0020 01001C70 36 | 726F2F69 66757475 72652F6D 79646179 37 | 2F757469 6C732F44 656D6F01 00106A61 38 | 76612F6C 616E672F 4F626A65 63740100 39 | 106A6176 612F6C61 6E672F53 79737465 40 | 6D010003 6F757401 00154C6A 6176612F 41 | 696F2F50 72696E74 53747265 616D3B01 42 | 00136A61 76612F69 6F2F5072 696E7453 43 | 74726561 6D010005 7072696E 74010015 44 | 284C6A61 76612F6C 616E672F 53747269 45 | 6E673B29 56002100 06000700 00000100 46 | 02000800 09000000 02000100 0A000B00 47 | 01000C00 00002700 02000100 00000B2A 48 | B700012A 1202B500 03B10000 0001000D 49 | 0000000A 00020000 00070004 00080009 50 | 000E000F 0001000C 00000025 00020001 51 | 00000009 B2000412 02B60005 B1000000 52 | 01000D00 00000A00 02000000 0B000800 53 | 0C000100 10000000 020011 54 | ``` 55 | 56 | 一个16进制的代码文件,其实很多需要编译的编程语言的虚拟机都是这样的,编译器先将源文件编译成16进制的文件,有着自己的字节码解析规则,比如区块链以太坊的EVM,非常类似。 57 | 58 | java字节码解读表 59 | 60 | |占位| 名称 | 解释| 61 | |----|----|----| 62 | |4个字节| Magic Number | 魔数| 63 | |2+2个字节| Version| 次主版本号| 64 | |2+n个字节| Constant Pool | 常量池| 65 | |2个字节| Access Flags | 访问标志| 66 | |2个字节| This Class Name| | 67 | |2个字节| Super Class Name| | 68 | |2+n个字节| Interfaces| | 69 | |2+n个字节| Fields| | 70 | |2+n个字节| Methods| | 71 | |2+n个字节| Attributes| | 72 | 73 | ## JavaAgent 74 | 在JDK1.5后开始出现了 javaagent 它提供了操作运行时字节码的机制,包:`java.lang.instrument`。可以把它理解为虚拟机级别的AOP 75 | 76 | **部署** 77 | `-javaagent:/path/myAgent.jar` 78 | 79 | **代码** 80 | ```java 81 | import java.lang.instrument.Instrumentation; 82 | 83 | public class Agent { 84 | // main 方法前执行 85 | public static void premain(String args, Instrumentation inst) { 86 | try { 87 | //KeyTransformer implements java.lang.instrument.ClassFileTransformer 88 | inst.addTransformer(new KeyTransformer()); 89 | } catch (Exception e) { 90 | throw new RuntimeException(e); 91 | } 92 | } 93 | // main 方法后执行 94 | public static void agentmain(String agentArgs, Instrumentation inst){ 95 | 96 | } 97 | } 98 | ``` 99 | **打包** 100 | META-INF/MANIFEST.MF 101 | ``` 102 | Manifest-Version: 1.0 103 | Implementation-Title: myAgent 104 | Premain-Class: pro.ifuture.agent.Agent 105 | Implementation-Version: 1.0-SNAPSHOT 106 | Built-By: ifuture 107 | ``` 108 | 使用maven时 109 | ```xml 110 | 111 | org.apache.maven.plugins 112 | maven-assembly-plugin 113 | 3.0.0 114 | 115 | 116 | 117 | false 118 | 119 | 120 | pro.ifuture.agent.Agent 121 | pro.ifuture.agent.Usage 122 | true 123 | 124 | 125 | 126 | jar-with-dependencies 127 | 128 | 129 | 130 | 131 | make-assembly 132 | package 133 | 134 | single 135 | 136 | 137 | 138 | 139 | ``` 140 | 141 | ## 应用 142 | * [asm](https://asm.ow2.io/) ASM库可以用来生成、转换和分析编译后的java类 143 | * [Arthas](https://github.com/alibaba/arthas) Alibaba Java诊断利器Arthas 144 | * [MyPerf4J](https://github.com/LinShunKang/MyPerf4J) 性能分析 145 | * [Bistoury](https://github.com/qunarcorp/bistoury) 去哪儿网的java应用生产问题诊断工具 146 | * idea debug -javaagent:/Users/xxx/Library/Caches/IntelliJIdea2019.3/captureAgent/debugger-agent.jar 147 | * [Idea破解](https://zhile.io/2018/08/17/jetbrains-license-server-crack.html) 148 | -------------------------------------------------------------------------------- /Java/Spring.md: -------------------------------------------------------------------------------- 1 | ## Spring 问答 2 | 3 | ### Spring Bean 生命周期 4 | 5 | 1. **实例化 Instantiation** 6 | 2. **属性赋值 Populate** 7 | 8 | 3. BeanNameAware 9 | 4. BeanFactoryAware, ApplicationContextAware 10 | 5. BeanPostProcessor#postProcessBeforeInitialization 11 | 6. InitializingBean#afterPropertiesSet 12 | 7. @PostConstruct 13 | 3. **初始化 Initialization** 14 | 8. BeanPostProcessor#postProcessAfterInitialization 15 | 9. DisposableBean 16 | 10. @PreDestroy 17 | 4. **销毁 Destruction** 18 | > `ConfigurableApplicationContext#close()` 19 | > `((AbstractApplicationContext)context).registerShutdownHook(); ` 此代码将注册 jvm系统钩子 `Runtime.getRuntime().addShutdownHook(...)` 20 | 21 | > 主线生命周期参考源码 22 | ``` 23 | org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory 24 | doCreateBean 25 | createBean 26 | ``` 27 | 28 | ![spring](../assets/img/springinit.webp) 29 | 30 | 31 | ### PostConstruct,PreDestroy 实现原理 32 | 在 xml 配置中这两个注解分别对应 init-method, destory-method 33 | 34 | Constructor > @Autowired > @PostConstruct 35 | 36 | 具体参考 [Spring Bean 生命周期](#Spring-Bean-生命周期) 源码参考部分 37 | 38 | ### Spring 事务隔离级别与传播机制 39 | #### 事务传播机制 40 | ``` 41 | @Transactional(propagation=Propagation.REQUIRED) 42 | ``` 43 | * REQUIRED : 如果有事务则加入事务,如果没有事务,则创建一个新的(默认值) 44 | * NOT_SUPPORTED : Spring不为当前方法开启事务,相当于没有事务 45 | * REQUIRES_NEW : 不管是否存在事务,都创建一个新的事务,原来的方法挂起,新的方法执行完毕后,继续执行老的事务 46 | * MANDATORY : 必须在一个已有的事务中执行,否则报错 47 | * NEVER : 必须在一个没有的事务中执行,否则报错 48 | * SUPPORTS : 如果其他bean调用这个方法时,其他bean声明了事务,则就用这个事务,如果没有声明事务,那就不用事务 49 | * NESTED : 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作 50 | 51 | #### 隔离级别 52 | ``` 53 | @Transactional(isolation=Isolation.DEFAULT) 54 | ``` 55 | * ISOLOCATION_DEFAULT: 数据库默认级别。 ORACLE(读已提交) MySQL(可重复读) 56 | * ISOLOCATION_READ_UNCOMMITTED: 允许读取未提交的读, 可能导致脏读,不可重复读,幻读 57 | * ISOLOCATION_READ_COMMITTED: 允许读取已提交的读,可能导致不可重复读,幻读 58 | * ISOLOCATION_REPEATABLE_READ : 不能能更新另一个事务修改单尚未提交(回滚)的数据,可能引起幻读 59 | * ISOLOCATION_SERIALIZABLE: 序列执行效率低 60 | 61 | ### Bean的作用域 62 | ```java 63 | @Service 64 | @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) 65 | public class MilestoneServiceImpl implements MilestoneService { 66 | } 67 | ``` 68 | 69 | * org.springframework.beans.factory.config.ConfigurableBeanFactory#SCOPE_PROTOTYPE 70 | 71 | 唯一bean实例,Spring中的bean默认都是单例的。 72 | 73 | * org.springframework.beans.factory.config.ConfigurableBeanFactory#SCOPE_SINGLETON 74 | 75 | 每次请求都会创建一个新的bean实例。 76 | 77 | * org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST 78 | 79 | 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效 80 | 81 | * org.springframework.web.context.WebApplicationContext#SCOPE_SESSION 82 | 83 | 每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。 84 | 85 | 默认单例情况下,有可能发生线程安全问题。当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。 86 | 87 | 1. 在bean对象中尽量避免定义可变的成员变量 88 | 2. 使用 ThreadLocal。Spring 源码中多种上下文都是这么使用的 89 | 90 | ### Spring框架中用到了哪些设计模式 91 | * 工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。 92 | * 代理设计模式:Spring AOP功能的实现。 93 | * 单例设计模式:Spring中的bean默认都是单例的。 94 | * 模板方法模式:Spring Security中的AbstractUserDetailsAuthenticationProvider#authenticate 95 | * 包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。 96 | * 观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。spring中Observer模式常用的地方是listener的实现。如ApplicationListener。 97 | * 适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。 98 | 99 | 100 | ### Spring 三级缓存解决循环依赖 101 | 循环依赖:类A 依赖 类B ,类B 又依赖 类A 102 | 103 | 关闭循环依赖: 104 | ```java 105 | @Component 106 | public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { 107 | @Override 108 | public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { 109 | ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(false); 110 | } 111 | } 112 | ``` 113 | 114 | 无法解决的循环依赖 115 | 116 | 一般可以处理 `setter` 注入或 属性 `@Autowired` 117 | 无法处理`构造器注入` 和 `@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)` 118 | 119 | org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.java 120 | ```java 121 | // 一级缓存:缓存已经初始化完成,可以暴露的 bean 对象,已经完成初始化,属性设置 122 | /** Cache of singleton objects: bean name to bean instance. */ 123 | private final Map singletonObjects = new ConcurrentHashMap<>(256); 124 | // 三级缓存:存储回调方法,可以调用回调方法 getObject 获取正在创建 bean 对象 125 | /** Cache of singleton factories: bean name to ObjectFactory. */ 126 | private final Map> singletonFactories = new HashMap<>(16); 127 | // 二级缓存:缓存的是已经完成初始化但还没设置属性的 bean 对象,正在创建中 128 | /** Cache of early singleton objects: bean name to bean instance. */ 129 | private final Map earlySingletonObjects = new HashMap<>(16); 130 | ``` 131 | 132 | * 使用 context.getBean(A.class),旨在获取容器内的单例A 133 | * 先从一级缓存 singletonObjects 中去获取。(如果获取到就直接return) 134 | * 如果获取不到或者对象正在创建中(isSingletonCurrentlyInCreation()),那就再从二级缓存 earlySingletonObjects 中获取。(如果获取到就直接return) 135 | * 如果还是获取不到,且允许 singletonFactories(allowEarlyReference=true)通过 getObject() 获取。就从三级缓存 singletonFactory.getObject() 获取。(如果获取到了就从 singletonFactories 中移除,并且放进 earlySingletonObjects 。其实也就是从三级缓存移动(是剪切、不是复制哦~)到了二级缓存) 136 | * 显然初次获取A是不存在的,因此走A的创建之路 137 | * 实例化A,放入三级缓存 138 | * 初始化A,设置属性,发现有 @Autowired B类 139 | * 依旧去三个缓存中查找,显然还是没有,走B的创建之路 140 | * 实例化B,放入三级缓存 141 | * 初始化B,设置属性,发现有 @Autowired A类 142 | * 缓存中查找,在三级缓存中发现,获取完在三级缓存中删除,保存到二级缓存中 143 | ```java 144 | protected Object getSingleton(String beanName, boolean allowEarlyReference) { 145 | Object singletonObject = this.singletonObjects.get(beanName); 146 | if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { 147 | synchronized (this.singletonObjects) { 148 | singletonObject = this.earlySingletonObjects.get(beanName); 149 | if (singletonObject == null && allowEarlyReference) { 150 | ObjectFactory singletonFactory = this.singletonFactories.get(beanName); 151 | if (singletonFactory != null) { 152 | singletonObject = singletonFactory.getObject(); 153 | this.earlySingletonObjects.put(beanName, singletonObject); 154 | this.singletonFactories.remove(beanName); 155 | } 156 | } 157 | } 158 | } 159 | return singletonObject; 160 | } 161 | ``` 162 | * B初始化成功,删除二级三级缓存,保存到一级中 163 | ```java 164 | protected void addSingleton(String beanName, Object singletonObject) { 165 | synchronized (this.singletonObjects) { 166 | this.singletonObjects.put(beanName, singletonObject); 167 | this.singletonFactories.remove(beanName); 168 | this.earlySingletonObjects.remove(beanName); 169 | this.registeredSingletons.add(beanName); 170 | } 171 | } 172 | ``` 173 | * B实例已经成功返回了,因此最终A也初始化成功 174 | * B持有的已经是初始化完成的A,A持有的也是初始化完成的B 175 | 176 | 177 | 178 | ## Spring Security 179 | [SSO OAuth关键概念](../DevOps/SSO.md) 180 | ### Spring-Security-OAuth 181 | 关键源码位置,结合[官网文档](https://projects.spring.io/spring-security-oauth/docs/oauth2.html)效果更好 182 | #### 授权 183 | 184 | * 授权端点 `/oauth/authorize` 185 | ```java 186 | org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint 187 | ``` 188 | 189 | * 授权确认端点 `/oauth/confirm_access` 190 | ```java 191 | org.springframework.security.oauth2.provider.endpoint.WhitelabelApprovalEndpoint 192 | ``` 193 | 客户端是否自动确认授权取决于 `org.springframework.security.oauth2.providerClientDetails#isAutoApprove` 194 | 195 | * 授权失败端点 `/oauth/error` 196 | ```java 197 | org.springframework.security.oauth2.provider.endpoint.WhitelabelErrorEndpoint 198 | ``` 199 | 200 | * 自定义上面两个URL 201 | ```java 202 | org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration#authorizationEndpoint 203 | org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer#pathMapping 204 | ``` 205 | 206 | 207 | #### 获得 token 208 | * 获得令牌端点 `/oauth/token` 209 | ```java 210 | org.springframework.security.oauth2.provider.endpoint.TokenEndpoint 211 | ``` 212 | * 生产 Token 213 | ```java 214 | org.springframework.security.oauth2.provider.token.AbstractTokenGranter 215 | ``` 216 | 217 | #### 获得 Token Key 218 | 返回 JWT具体算法和公钥,如果没有使用 `KeyPair` 直接用 `SigningKey` 将直接这个 `SigningKey` 这是很危险的 219 | * 获得令牌签名(公钥)端点 `/oauth/token_key` 220 | ```java 221 | org.springframework.security.oauth2.provider.endpoint.TokenKeyEndpoint 222 | ``` 223 | 224 | #### 验证解析 Token 225 | * 验证解析令牌端点 `/oauth/check_token` 226 | ```java 227 | org.springframework.security.oauth2.provider.endpoint.CheckTokenEndpoint 228 | ``` 229 | 官方文档介绍,主要考虑授权服务与资源服务分开的情况,`RemoteTokenServices` 它将允许资源服务器通过HTTP请求来解码令牌(也就是授权服务的 `/oauth/check_token` 端点)。如果你的资源服务没有太大的访问量的话,那么使用`RemoteTokenServices` 将会很方便(所有受保护的资源请求都将请求一次授权服务用以检验token值),或者你可以通过缓存来保存每一个token验证的结果 230 | 231 | * 验证流程 232 | ```java 233 | org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter 234 | ``` 235 | 从 `header(Bearer Token Header)` 、`query(access_token)` 里获取 token 进行认证处理 236 | 237 | #### 异常 238 | * 默认处理 239 | ```java 240 | org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator 241 | ``` 242 | 243 | * Spring MVC 处理 244 | `@ExceptionHandler` `HttpMessageConverters` 245 | -------------------------------------------------------------------------------- /Java/分布式锁.md: -------------------------------------------------------------------------------- 1 | 分布式锁 2 | ---------- 3 | ## 常见的实现方式 4 | * 基于数据库 5 | * 基于缓存(redis,memcached) 6 | * 基于Zookeeper 7 | 8 | 期望: 9 | > 这把锁要是一把可重入锁(避免死锁) 10 | 这把锁最好是一把阻塞锁(根据业务需求考虑要不要这条) 11 | 有高可用的获取锁和释放锁功能 12 | 获取锁和释放锁的性能要好 13 | 14 | ## Redis 实现分布式锁 15 | 16 | 17 | > setnx(key, value):**SET** if **N**ot e**X**ists,若该key-value不存在,则成功加入缓存并且返回1,否则返回0。 18 | > get(key):获得key对应的value值,若不存在则返回nil。 19 | > getset(key, value):先获取key对应的value值,若不存在则返回nil,然后将旧的value更新为新的value。 20 | > expire(key, seconds):设置key-value的有效期为seconds秒。 21 | > set(key, value, options) 22 | > options: 23 | > * EX seconds -- Set the specified expire time, in seconds. 24 | > * PX milliseconds -- Set the specified expire time, in milliseconds. 25 | > * NX -- Only set the key if it does not already exist. 26 | > * XX -- Only set the key if it already exist. 27 | 28 | 29 | Redis原生命令[`setnx`](http://redis.io/commands/setnx) 可解决加锁,配合上面命令实现可超时锁,当然使用 `set` + options 更简单 30 | 31 | ### 在 Spring 的实现 32 | ``` 33 | redisTemplate.opsForValue().setIfAbsent(key,value,timeout,timeUnit); 34 | ``` 35 | 对应 Redis 命令 `SET key value EX/PX timeout NX` 36 | 37 | ``` 38 | redisTemplate.opsForValue().setIfAbsent.setIfAbsent(key, value); 39 | ``` 40 | 对应 Redis 命令 `SETNX key value` 41 | 42 | 43 | ### 实现代码 参考 44 | Spring Data Redis + Spring AOP + Java annotation 45 | 46 | RedisCacheStore.java 47 | ```java 48 | @Component 49 | public class RedisCacheStore implements CacheStore { 50 | 51 | @Autowired 52 | private StringRedisTemplate redisTemplate; 53 | 54 | @Override 55 | public Optional get(String key) { 56 | return Optional.of(redisTemplate.opsForValue().get(key)); 57 | } 58 | 59 | @Override 60 | public void put(String key, String value, long timeout, TimeUnit timeUnit) { 61 | redisTemplate.opsForValue().set(key,value,timeout,timeUnit); 62 | } 63 | 64 | @Override 65 | public Boolean putIfAbsent(String key, String value, long timeout, TimeUnit timeUnit) { 66 | return redisTemplate.opsForValue().setIfAbsent(key,value,timeout,timeUnit); 67 | } 68 | 69 | @Override 70 | public void put(String key, String value) { 71 | redisTemplate.opsForValue().set(key,value); 72 | } 73 | 74 | @Override 75 | public void delete(String key) { 76 | redisTemplate.delete(key); 77 | } 78 | } 79 | ``` 80 | 81 | ```java 82 | @Slf4j 83 | @Aspect 84 | @Configuration 85 | public class CacheLockInterceptor { 86 | 87 | private final static String CACHE_LOCK_PREFOX = "cache_lock_"; 88 | 89 | private final static String CACHE_LOCK_VALUE = "locked"; 90 | 91 | @Autowired 92 | private RedisCacheStore cacheStore; 93 | 94 | 95 | @Around("@annotation(pro.ifuture.lbt.cache.lock.CacheLock)") 96 | public Object interceptCacheLock(ProceedingJoinPoint joinPoint) throws Throwable { 97 | // Get method signature 98 | MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); 99 | 100 | log.debug("Starting locking: [{}]", methodSignature.toString()); 101 | 102 | // Get cache lock 103 | CacheLock cacheLock = methodSignature.getMethod().getAnnotation(CacheLock.class); 104 | 105 | // Build cache lock key 106 | String cacheLockKey = buildCacheLockKey(cacheLock, joinPoint); 107 | 108 | log.debug("Built lock key: [{}]", cacheLockKey); 109 | 110 | 111 | try { 112 | // Get from cache 113 | Boolean cacheResult = cacheStore.putIfAbsent(cacheLockKey, CACHE_LOCK_VALUE, cacheLock.expired(), cacheLock.timeUnit()); 114 | 115 | if (cacheResult == null) { 116 | throw new ServiceException("Unknown reason of cache " + cacheLockKey).setErrorData(cacheLockKey); 117 | } 118 | 119 | if (!cacheResult) { 120 | throw new FrequentAccessException("Visit too often, please try again later!").setErrorData(cacheLockKey); 121 | } 122 | 123 | // Proceed the method 124 | return joinPoint.proceed(); 125 | } finally { 126 | // Delete the cache 127 | if (cacheLock.autoDelete()) { 128 | cacheStore.delete(cacheLockKey); 129 | log.debug("Deleted the cache lock: [{}]", cacheLock); 130 | } 131 | } 132 | } 133 | 134 | private String buildCacheLockKey(@NonNull CacheLock cacheLock, @NonNull ProceedingJoinPoint joinPoint) { 135 | Assert.notNull(cacheLock, "Cache lock must not be null"); 136 | Assert.notNull(joinPoint, "Proceeding join point must not be null"); 137 | 138 | // Get the method 139 | MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); 140 | 141 | // Build the cache lock key 142 | StringBuilder cacheKeyBuilder = new StringBuilder(CACHE_LOCK_PREFOX); 143 | 144 | String delimiter = cacheLock.delimiter(); 145 | 146 | if (StringUtils.isNotBlank(cacheLock.prefix())) { 147 | cacheKeyBuilder.append(cacheLock.prefix()); 148 | } else { 149 | cacheKeyBuilder.append(methodSignature.getMethod().toString()); 150 | } 151 | 152 | // Handle cache lock key building 153 | Annotation[][] parameterAnnotations = methodSignature.getMethod().getParameterAnnotations(); 154 | 155 | for (int i = 0; i < parameterAnnotations.length; i++) { 156 | log.debug("Parameter annotation[{}] = {}", i, parameterAnnotations[i]); 157 | 158 | for (int j = 0; j < parameterAnnotations[i].length; j++) { 159 | Annotation annotation = parameterAnnotations[i][j]; 160 | log.debug("Parameter annotation[{}][{}]: {}", i, j, annotation); 161 | if (annotation instanceof CacheParam) { 162 | // Get current argument 163 | Object arg = joinPoint.getArgs()[i]; 164 | log.debug("Cache param args: [{}]", arg); 165 | 166 | // Append to the cache key 167 | cacheKeyBuilder.append(delimiter).append(arg.toString()); 168 | } 169 | } 170 | } 171 | 172 | if (cacheLock.traceRequest()) { 173 | // Append http request info 174 | cacheKeyBuilder.append(delimiter).append(ServletUtils.getRequestIp()); 175 | } 176 | 177 | return cacheKeyBuilder.toString(); 178 | } 179 | } 180 | 181 | ``` 182 | 183 | ```java 184 | @Target(ElementType.METHOD) 185 | @Retention(RetentionPolicy.RUNTIME) 186 | @Documented 187 | @Inherited 188 | public @interface CacheLock { 189 | 190 | /** 191 | * Cache prefix, default is "" 192 | * 193 | * @return cache prefix 194 | */ 195 | @AliasFor("value") 196 | String prefix() default ""; 197 | 198 | /** 199 | * Alias of prefix, default is "" 200 | * 201 | * @return alias of prefix 202 | */ 203 | @AliasFor("prefix") 204 | String value() default ""; 205 | 206 | /** 207 | * Expired time, default is 5. 208 | * 209 | * @return expired time 210 | */ 211 | long expired() default 5; 212 | 213 | /** 214 | * Time unit, default is TimeUnit.SECONDS. 215 | * 216 | * @return time unit 217 | */ 218 | TimeUnit timeUnit() default TimeUnit.SECONDS; 219 | 220 | /** 221 | * Delimiter, default is ':' 222 | * 223 | * @return delimiter 224 | */ 225 | String delimiter() default ":"; 226 | 227 | /** 228 | * Whether delete cache after method invocation. 229 | * 230 | * @return true if delete cache after method invocation; false otherwise 231 | */ 232 | boolean autoDelete() default true; 233 | 234 | /** 235 | * Whether trace the request info. 236 | * 237 | * @return true if trace the request info; false otherwise 238 | */ 239 | boolean traceRequest() default false; 240 | } 241 | 242 | ``` 243 | 244 | -------------------------------------------------------------------------------- /Java/分析工具.md: -------------------------------------------------------------------------------- 1 | * Jprofiler 2 | * HPROF 3 | * jvisualvm 4 | * jstack 5 | * jconsole 6 | * MyPerf4J 7 | * Bistoury 8 | 9 | //TODO 使用案例 10 | -------------------------------------------------------------------------------- /Java/如何限流.md: -------------------------------------------------------------------------------- 1 | 如何限流 2 | --------------- 3 | > 限流一般是一种保护系统稳定性的一种措施。在开放API中比较常见。 4 | 对某一用户,或指定IP,或具体到某个API,在一个具体的时间里限制请求次数。 5 | 6 | 关键词 7 | * 熔断 8 | * 服务降级 9 | * 延迟处理 10 | * 特权处理 11 | 12 | ## 主流框架的实现 13 | 首先看看主要的框架是使用,再讨论自己怎么实现 14 | 15 | ### Nginx 16 | Nginx 自带的两个限流模块 17 | * ngx_http_limit_conn_module 连接数限流模块 18 | * ngx_http_limit_req_module 请求数限流模块 19 | * ngx_lua 20 | * ngx_lua_waf模块 21 | * [OpenResty](https://openresty.org/) 22 | 23 | ngx_http_limit_conn_module 24 | ``` 25 | http { 26 | limit_conn_zone $binary_remote_addr zone=addr:10m; 27 | limit_conn_log_level error; 28 | limit_conn_status 503; 29 | ... 30 | server { 31 | ... 32 | location /download/ { 33 | limit_conn addr 1; 34 | } 35 | 36 | ab test 37 | ab -n10 -c3 https://ifuture.pro/test/ 38 | ``` 39 | * limit_conn_zone: 配置限流的key以及存储这些key共用的共享内存的大小; 40 | * key 是$binary_remote_addr,表示IP地址,如果如果需要对总域名进行限流,key就应该使用 $server_name $host等等,能唯一表示该域名的Nginx变量; 41 | * zone=addr:10m中,addr表示连接数限流的区域名称,10m表示可以分配的共享空间的大小。 42 | * binary_remote_addr变量在64位平台中占用64字节。1M共享空间可以保存1.6万个64位的,10m就可以保存16万个。如果超过16万个,共享空间被用完,服务器将会对后续所有的请求返回 503。 43 | * limit_conn:配置指定key的最大连接数。样例中指定的最大连接数是1,表示Nginx最多同时允许1个连接进行location /limit 的行为操作。 44 | * limit_conn_status:配置被限流后返回的状态码,样例中设置的是503. 45 | * limit_conn_log_level:配置被限流后的日志级别,设置的是error级别 46 | 47 | ngx_http_limit_req_module 48 | ``` 49 | http { 50 | limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; 51 | limit_req_log_level error; 52 | limit_req_status 503; 53 | ... 54 | server { 55 | ... 56 | location /search/ { 57 | limit_req zone=one burst=5; 58 | } 59 | ``` 60 | rate=1r/s 固定请求速率设置,每秒1个请求。 61 | 62 | [抵御CC攻击](https://www.nenew.net/use-nginx-configure-to-protect-cc-attack.html) 63 | ``` 64 | http{ 65 | ... 66 | limit_req_zone $cookie_token zone=session_limit:3m rate=1r/s; 67 | limit_req_zone $binary_remote_addr $uri zone=auth_limit:3m rate=1r/m; 68 | } 69 | location /{ 70 | limit_req zone=session_limit burst=5; 71 | rewrite_by_lua ' 72 | local random = ngx.var.cookie_random 73 | if (random == nil) then 74 | return ngx.redirect("/auth?url=" .. ngx.var.request_uri) 75 | end 76 | local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random) 77 | if (ngx.var.cookie_token ~= token) then 78 | return ngx.redirect("/auth?url=".. ngx.var.request_uri) 79 | end 80 | '; 81 | } 82 | location /auth { 83 | limit_req zone=auth_limit burst=1; 84 | if ($arg_url = "") 85 | { 86 | return403; 87 | } 88 | access_by_lua ' 89 | local random = math.random(9999) 90 | local token = ngx.md5("opencdn" .. ngx.var.remote_addr .. random) 91 | if (ngx.var.cookie_token ~= token) then 92 | ngx.header["Set-Cookie"] = { 93 | "token=" .. token, "random=" .. random 94 | } 95 | return ngx.redirect(ngx.var.arg_url) 96 | end 97 | '; 98 | } 99 | ``` 100 | 101 | ### spring-cloud-gateway 102 | 103 | maven `pom.xml` 104 | ```xml 105 | 106 | org.springframework.cloud 107 | spring-cloud-starter-gateway 108 | 109 | 110 | org.springframework.boot 111 | spring-boot-starter-data-redis-reactive 112 | 113 | ``` 114 | appliaction.yml 115 | ```yaml 116 | spring: 117 | cloud: 118 | gateway: 119 | routes: 120 | - id: zzz-transfer 121 | uri: lb://zzz-transfer 122 | order: 8002 123 | predicates: 124 | - Path=/api/tengine/** 125 | - CacheBody={*} 126 | filters: 127 | - StripPrefix=2 128 | - name: RequestRateLimiter 129 | args: 130 | redis-rate-limiter.replenishRate: 1 # 允许用户每秒处理多少个请求 131 | redis-rate-limiter.burstCapacity: 3 # 令牌桶的容量,允许在一秒钟内完成的最大请求数 132 | key-resolver: "#{@hostNameKeyResolver}" #SPEL表达式去的对应的bean 133 | enabled: true 134 | redis: 135 | host: ${REDIS_HOST:localhost} 136 | port: ${REDIS_PORT:6379} 137 | 138 | ``` 139 | 140 | ```java 141 | /** 142 | * IP 限流 143 | * @return 144 | */ 145 | @Bean 146 | @Primary 147 | KeyResolver hostNameKeyResolver() { 148 | return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName()); 149 | } 150 | 151 | /** 152 | * API 地址限流 153 | * @return 154 | */ 155 | @Bean 156 | KeyResolver apiKeyResolver() { 157 | return exchange -> Mono.just(exchange.getRequest().getPath().value()); 158 | } 159 | ``` 160 | 161 | 162 | ### Alibaba Sentinel 163 | Spring Cloud Netflix 诸多组件不在更新维护后 Spring Cloud也正式加入了[spring-cloud-alibaba](https://spring.io/projects/spring-cloud-alibaba) 164 | [Sentinel](https://github.com/alibaba/spring-cloud-alibaba#components)就是其中一员,它把流量作为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性 165 | 166 | pom.xml 167 | ```xml 168 | 169 | org.springframework.cloud 170 | spring-cloud-starter-alibaba-nacos-discovery 171 | 172 | 173 | org.springframework.cloud 174 | spring-cloud-starter-alibaba-nacos-config 175 | 176 | 177 | org.springframework.cloud 178 | spring-cloud-starter-alibaba-sentinel 179 | 180 | ``` 181 | 182 | #### 配置方式 183 | 184 | * 基于sentinel-dashboard 界面化配置 185 | 186 | 下载 sentinel-dashboard https://github.com/alibaba/Sentinel/releases 187 | ```shell 188 | java -jar sentinel-dashboard.jar --server.port=8080 189 | 190 | 可能在sentinel dashboard上不显示,请求一下个个服务试试 191 | ``` 192 | 193 | application.yml 194 | ``` 195 | spring: 196 | application: 197 | name: zzz-gateway 198 | cloud: 199 | nacos: 200 | discovery: 201 | server-addr: z.com:8848 202 | sentinel: 203 | transport: 204 | dashboard: z.com:8080 205 | enabled: true 206 | ``` 207 | * 基于nacos config 208 | 209 | application.yml 210 | ``` 211 | spring: 212 | application: 213 | name: zzz-gateway 214 | cloud: 215 | nacos: 216 | discovery: 217 | server-addr: ifuture.pro:8848 218 | config: 219 | server-addr: ifuture.pro:8848 220 | file-extension: yaml 221 | sentinel: 222 | transport: 223 | dashboard: ifuture.pro:8080 224 | datasource: 225 | ds: 226 | nacos: 227 | server-addr: ifuture.pro:8848 228 | dataId: spring-cloud-sentinel-nacos 229 | groupId: DEFAULT_GROUP 230 | rule-type: flow 231 | namespace: zzzgateway 232 | ``` 233 | 234 | ```yaml 235 | - resource: "/api" 236 | limitApp: default 237 | grade: 1 238 | count: 1 239 | strategy: 0 240 | controlBehavior: 0 241 | clusterMode: true 242 | 243 | resource:资源名,即限流规则的作用对象。 244 | limitApp:流控针对的调用来源,若为 default 则不区分调用来源。 245 | grade:限流阈值类型,QPS 或线程数模式,0代表根据并发数量来限流,1代表根据QPS来进行流量控制。 246 | count:限流阈值 247 | strategy:判断的根据是资源自身,还是根据其它关联资源 (refResource),还是根据链路入口 248 | controlBehavior:流控效果(直接拒绝 / 排队等待 / 慢启动模式) 249 | clusterMode:是否为集群模式 250 | ``` 251 | * 基于代码 252 | https://github.com/alibaba/Sentinel/wiki/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8 253 | 254 | 255 | 256 | ## 自己实现 257 | 258 | ### 计数器 259 | 系统维护一个计数器,来一个请求就加1,请求处理完成就减1,当计数器大于指定的阈值,就拒绝新的请求。 260 | 基于这个简单的方法,可以再延伸出一些高级功能,比如阈值可以不是固定值,是动态调整的。另外,还可以有多组计数器分别管理不同的服务,以保证互不影响等。 261 | 262 | ### 队列 263 | 就是基于FIFO队列,所有请求都进入队列,后端程序从队列中取出待处理的请求依次处理。 264 | 基于队列的方法,也可以延伸出更多的玩法来,比如可以设置多个队列以配置不同的优先级。 265 | 266 | ### **令牌桶** 267 | 首先还是要基于一个队列,请求放到队列里面。但除了队列以外,还要设置一个令牌桶,另外有一个脚本以持续恒定的速度往令牌桶里面放令牌,后端处理程序每处理一个请求就必须从桶里拿出一个令牌,如果令牌拿完了,那就不能处理请求了。我们可以控制脚本放令牌的速度来达到控制后端处理的速度,以实现动态流控。 268 | * 一个固定容量的桶。如:每分钟能请求的最大量 269 | * 一个永续线程以匀速的方式将令牌放入桶中,超过容量丢弃 270 | * 有请求了,将桶内令牌取出,如果没有足够的令牌则限流 271 | 272 | ### Spring Redis Lua 273 | `Spring Cloud Gateway` 就是基于 `Redis + Lua` 的方式实现限流的,主要是减少网络开销,使用Lua脚本,无需向Redis 发送多次请求,执行一次即可。 274 | 275 | Lua 脚本位置 276 | ``` 277 | spring-cloud-gateway-core-2.1.0.RELEASE.jar/META-INF/scripts/request_rate_limiter.lua 278 | ``` 279 | 可以使用`DefaultRedisScript` 去调用。模仿spring cloud gateway 自己写一个限流吧 280 | ```java 281 | String luaScript = getLuaScript(); 282 | RedisScript redisScript = new DefaultRedisScript<>(luaScript, Number.class); 283 | Number count = limitRedisTemplate.execute(redisScript, keys, limitCount, limitPeriod); 284 | ``` 285 | -------------------------------------------------------------------------------- /Linux/Computer.md: -------------------------------------------------------------------------------- 1 | 计算机 2 | ------- 3 | ## CPU 4 | CPU 主要由两部分构成:**控制单元** 和 **算术逻辑单元**(ALU) 5 | * 控制单元:从内存中提取指令并解码执行 6 | * 算数逻辑单元(ALU):处理算数和逻辑运算 7 | 8 | 从功能上来看,CPU内部由**寄存器** 、**控制器** 、**运算器** 和 **时钟** 四部分组成,各部分之间通过电信号连通。 9 | - 寄存器是中央处理器内的组成部分。它们可以用来暂存指令、数据和地址。可以将其看作是内存的一种。根据种类的不同,一个 CPU 内部会有 20 – 100个寄存器。 10 | - 控制器负责把内存上的指令、数据读入寄存器,并根据指令的结果控制计算机 11 | - 运算器负责运算从内存中读入寄存器的数据 12 | - 时钟 负责发出 CPU 开始计时的时钟信号 13 | 14 | | 寄存器| 个数 |描述| 15 | |-----|-----|---| 16 | |累加寄存器| 1 |存储运行的数据和运算后的数据| 17 | |标志寄存器| 1 |用于反应处理器的状态和运算结果的某些特征以及控制指令的执行| 18 | |程序计数器| 1 |程序计数器是用于存放下一条指令所在单元的地址的地方| 19 | |基址寄存器| 多个 |存储数据内存的起始位置| 20 | |变址寄存器| 多个 |存储基址寄存器的相对地址| 21 | |通用寄存器| 多个 |存储任意数据| 22 | |指令寄存器| 1 |储存正在被运行的指令,CPU内部使用,程序员无法对该寄存器进行读写| 23 | |栈寄存器| 1| 存储栈区域的起始位置| 24 | 25 | 26 | * 缓存 27 | * 指令重排,乱序执行 28 | * 分支预测 29 | * KPTI Kernel page-table isolation 内核页表隔离 30 | * 虚拟内存 31 | * 内存分页交换 内存使用硬盘空间 32 | * 系统总线锁住 ----> 缓存一致性协议 MESI 33 | 34 | ## 内存 35 | -------------------------------------------------------------------------------- /Linux/HTTP.md: -------------------------------------------------------------------------------- 1 | ## 握手 2 | ### 位码即tcp标志位,有6种标示: 3 | 4 | - SYN(synchronous建立联机) 5 | - ACK(acknowledgement 确认) 6 | - PSH(push传送) 7 | - FIN(finish结束) 8 | - RST(reset重置) 9 | - URG(urgent紧急) 10 | - Sequence number(顺序号码) 11 | - Acknowledge number(确认号码) 12 | 13 | ### 状态 14 | - LISTEN - 侦听来自远方TCP端口的连接请求; 15 | - SYN-SENT -在发送连接请求后等待匹配的连接请求; 16 | - SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认; 17 | - ESTABLISHED- 代表一个打开的连接,数据可以传送给用户; *[ə'stæblɪʃt]adj. 确定的;已制定的,已建立的* 18 | - FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认; 19 | - FIN-WAIT-2 - 从远程TCP等待连接中断请求; 20 | - CLOSE-WAIT - 等待从本地用户发来的连接中断请求; 21 | - CLOSING -等待远程TCP对连接中断的确认; 22 | - LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认; 23 | - TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认; 24 | - CLOSED - 没有任何连接状态 25 | 26 | ### 建立连接 27 | > 三次握手 28 | 29 | TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。 30 | 31 | - 第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。 32 | - 第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。 33 | - 第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。 34 | 35 | 完成三次握手,客户端与服务器开始传送数据。 36 | 37 | 38 | ### 关闭连接 39 | > 四次分手 40 | 41 | 由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。 42 | 43 | TCP的连接的拆除需要发送四个包,因此称为四次挥手(four-way handshake)。客户端或服务器均可主动发起挥手动作,在socket编程中,任何一方执行close()操作即可产生挥手操作。 44 | 45 | - (1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。 46 | - (2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。 47 | - (3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A。 48 | - (4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。 49 | 50 | [参考](https://www.cnblogs.com/Jessy/p/3535612.html) 51 | -------------------------------------------------------------------------------- /Linux/HTTPS证书.md: -------------------------------------------------------------------------------- 1 | ## 关键字 2 | 3 | ### SSL 4 | Secure Socket Layer 5 | 安全套接字层 6 | 7 | 位于可靠的面向连接的网络层协议和应用层协议之间的一种协议层。SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL记录协议和SSL握手协议。 8 | 9 | ### TLS 10 | Transport Layer Security 11 | 传输层安全协议 12 | 13 | 用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。 14 | 15 | 16 | 众所周知,网景公司 20 世纪 90 年代在和微软的竞争中最终败下阵来,之后网景公司将 SSL 协议的管理权转交给 IETF(Internet Engineering Task Force, www.ietf.org)。 17 | 于是 IETF 将 SSL 作了标准化,重新命名为 TLS(Transport Layer Security)。在 1999 年,TLS 1.0 诞生了(其实也就是 SSL 3.1) 18 | HTTPS(HyperText Transfer Protocol Secure)是建立在 SSL/TLS 协议之上,信息通信通过 SSL/TLS 进行加密,最后一个 S 就是 Secure 的缩写,代表安全的意思,HTTPS = HTTP+SSL/TLS。 19 | 20 | ### ACME 21 | [Automatic Certificate Management Environment](https://tools.ietf.org/html/rfc8555) 22 | 23 | ACME 协议最初由 Let's Encrypt 团队开发并被认为是其提供的 CA 服务的核心。ACMEv1 协议旨在确保验证、发布和管理方法是完全自动化、一致、符合合规性和安全的。 24 | 25 | ACME成为标准对证书颁发和管理的重要性体现在两个方面。第一是提高了软件生态系统的质量。因为标准确定后,开发者可以专注于针对单个协议开发优秀的软件,而不是为定制的 API 提供许多维护不良的软件。第二,标准化协议大大降低了技术依赖性锁定所带来的影响,使得可从一个 CA 更容易地切换到另一个 CA —— 给予了使用者更自由和便利的选择。 26 | 27 | ## 申请证书 28 | 29 | ### 云产商 30 | 如阿里云等各大云产商都集成了国内外各种高大上的 CA 机构证书,可以直接购买使用。 31 | 32 | ### 免费证书 33 | 34 | #### 云服务 35 | `cloudflare` `netlify` 等过个国外的 CND PAAS服务商都有提供免费的HTTPS服务,将域名DNS解析指向他们即可。 36 | ![](../assets/img/HTTPS.png) 37 | 38 | #### 开源力量 39 | 40 | * https://github.com/letsencrypt/letsencrypt 41 | * https://github.com/certbot/certbot 42 | 43 | ```bash 44 | $ ./letsencrypt-auto --help 45 | 46 | run:获取并安装证书到当前的Web服务器 47 | certonly:获取或续期证书,但是不安装 48 | renew:在证书快过期时,续期之前获取的所有证书 49 | -d DOMAINS:一个证书支持多个域名,用逗号分隔 50 | --apache:使用 Apache 插件来认证和安装证书 51 | --standalone:运行独立的 web server 来验证 52 | --nginx:使用 Nginx 插件来认证和安装证书 53 | --webroot:如果目标服务器已经有 web server 运行且不能关闭,可以通过往服务器的网站根目录放置文件的方式来验证 54 | --manual:通过交互式方式,或 Shell 55 | 56 | $ ./letsencrypt-auto run -d ifuture.pro -d www.ifuture.pro --nginx 57 | 58 | ``` 59 | 60 | https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx 61 | 62 | 63 | * https://github.com/acmesh-official/acme.sh 64 | 65 | [使用教程](https://github.com/acmesh-official/acme.sh/wiki/%E8%AF%B4%E6%98%8E) 66 | 67 | ## 配置证书 68 | [SSL Configuration Generator](https://ssl-config.mozilla.org/#server=nginx) 69 | 70 | ## 证书格式 71 | * PEM 72 | > 适用于Apache、Nginx、Candy Server等Web服务器 73 | 常见的文件后缀为.pem、.crt、.cer、.key 74 | 可以存放证书或私钥,或者两者都包含 75 | .key后缀一般只用于证书私钥文件 76 | 77 | * PFX 78 | > 适用于IIS等Web服务器 79 | 常见的文件后缀为.pfx、.p12 80 | 同时包含证书和私钥,且一般有密码保护 81 | 82 | * JKS 83 | > 适用于Tomcat、Weblogic、JBoss、Jetty等Web服务器 84 | 常见的文件后缀为.jks 85 | 86 | > Let's Encrypt颁发的HTTPS证书一般包括以下几个文件: 87 | cert.key(PEM格式):私钥文件 88 | cert.cer(PEM格式):证书文件 89 | fullchain.cer(PEM格式):包含证书和中间证书 90 | 91 | **PEM ===> PFX** 92 | ` openssl pkcs12 -export -out cert.pfx -inkey cert.key -in fullchain.cer` 93 | 94 | **PFX ===> JKS** 95 | ` keytool -importkeystore -srckeystore cert.pfx -destkeystore cert.jks -srcstoretype PKCS12 -deststoretype JKS 96 | ` 97 | 98 | **PEM ===> JKS** 99 | 先将PEM文件转换为PFX文件,然后再将PFX文件转换为JKS文件 100 | 101 | **PFX ===> PEM** 102 | 1. 使用cert.pfx文件生成临时文件temp.cer,temp.cer中包含了证书和私钥 103 | `openssl pkcs12 -in cert.pfx -nodes -out temp.cer` 104 | 1. 使用临时文件temp.cer文件生成私钥文件cert.key 105 | `openssl rsa -in temp.cer -out cert.key` 106 | 1. 使用临时文件temp.cer文件生成证书文件cert.cer 107 | `openssl x509 -in temp.cer -out cert.cer` 108 | 1. 使用cert.pfx生成中间证书文件chain.cer,合并cert.cer、空白行、chain.cer即可得到fullchain.cer 109 | ```shell 110 | openssl pkcs12 -in cert.pfx -cacerts -nokeys -chain | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > chain.cer 111 | echo '\n' > emptyline.cer 112 | cat cert.cer emptyline.cer chain.cer> fullchain.cer 113 | ``` 114 | 115 | **JKS ===> PFX** 116 | ` keytool -importkeystore -srckeystore cert.jks -destkeystore cert.pfx -srcstoretype JKS -deststoretype PKCS12 117 | ` 118 | 119 | ## DNS 服务器 120 | BIND (Berkeley Internet Name Domain) 121 | 122 | `yum -y install bind bind-chroot bind-utils` 123 | 124 | 125 | ## 泛域名 126 | ```shell 127 | chmod +x certbot-auto wget https://dl.eff.org/certbot-auto 128 | 129 | ./certbot-auto certonly --email 邮箱 -d *.xx.com -d 域名2 --manual --preferred-challenges dns --server https://acme-v02.api.letsencrypt.org/directory 130 | 131 | 132 | certonly 表示安装模式,Certbot 有安装模式和验证模式两种类型的插件。 133 | --manual 表示手动交互模式,Certbot 有很多插件,不同的插件都可以申请证书,用户可以根据需要自行选择 134 | 135 | -d 为那些主机申请证书,如果是通配符,输入 *.xxxx.com (可以替换为你自己的域名) 136 | 137 | --preferred-challenges 使用 DNS 方式校验域名所有权 138 | 139 | --server Let's Encrypt ACME v2 版本使用的服务器不同于 v1 版本,需要显示指定。 140 | ``` 141 | 142 | * [用Certbot申请免费Let's Encrypt泛域名证书+自动续期](https://www.willh.cn/articles/2018/07/27/1532676216270.html) 143 | * [泛域名自动续签](https://github.com/ywdblog/certbot-letencrypt-wildcardcertificates-alydns-au) 144 | * [官方支持的插件](https://certbot.eff.org/docs/using.html#dns-plugins) 145 | -------------------------------------------------------------------------------- /Linux/NetworkNamespace.md: -------------------------------------------------------------------------------- 1 | # Network Namespace 网络命名空间 2 | 3 | 4 | > 一般情况下,所有数据流量默认通过有线网卡走。然后,对于特定的程序或 Terminal 窗口使用无线网卡,可以通过在启动它们之前设置路由表实现。 5 | > 这种操作涉及到 Linux 系统的路由管理和命名空间的概念。Linux 命名空间(namespace)是内核的一种特性,可以隔离进程组,使其只能看到特定命名空间中的全局资源。这里我们使用网络命名空间(network namespace)来实现进程网络隔离。 6 | 7 | 创建一个新的网络命名空间 8 | 9 | ```shell 10 | sudo ip netns add mynamespace 11 | sudo ip netns list # 查看 12 | ``` 13 | 14 | 可以将你的无线网卡连接到新创建的网络命名空间。假设你的无线网卡是 wlan0: 15 | 16 | ```shell 17 | sudo ip link set wlan0 netns mynamespace 18 | ``` 19 | 20 | 可以进入到这个网络命名空间中,并配置网络: 21 | 22 | ```shell 23 | sudo ip netns exec mynamespace bash 24 | sudo ifconfig wlan0 up # 启动网卡 25 | sudo dhclient wlan0 # 网络DHCP 26 | # 无线密码 27 | sudo wpa_supplicant -B -i wlan0 -c /path/to/your/wpa_supplicant.conf 28 | 29 | # 添加本地回环网络 30 | sudo ip netns exec mynamespace ip link set lo up 31 | # 设置DNS 32 | sudo nano /etc/resolv.conf 33 | nameserver 8.8.8.8 34 | ``` 35 | -------------------------------------------------------------------------------- /Linux/Openconnect.md: -------------------------------------------------------------------------------- 1 | Openconnect 2 | ----------------- 3 | 4 | ## 安装 client 5 | 6 | ### Ubuntu 7 | ```bash 8 | apt-get install openconnect vpnc network-manager-openconnect 9 | ``` 10 | 11 | ### Mac 12 | ```bash 13 | brew install openconnect 14 | 15 | brew cask install openconnect-gui 16 | ``` 17 | 18 | ### Mobile 19 | ISO、Andorid : [Cisco AnyConnect](https://apps.apple.com/cn/app/cisco-anyconnect/id1135064690) 20 | 21 | ## Server 22 | [ocservauto.sh](./ocservauto.sh) 23 | 24 | 25 | 26 | ``` 27 | You could use ' sudo ocpasswd -c /etc/ocserv/ocpasswd username ' to add users. 28 | You could stop ocserv by ' /etc/init.d/ocserv stop '! 29 | Boot from the start or not, use ' sudo insserv ocserv ' or ' sudo insserv -r ocserv '. 30 | ``` 31 | 32 | Tor(The Onion Router) 33 | ---------- 34 | 洋葱路由 35 | 36 | 37 | Other 38 | ---------- 39 | 40 | * V2ray 41 | * Shadowsocks 42 | * lantern 43 | -------------------------------------------------------------------------------- /Linux/SS.md: -------------------------------------------------------------------------------- 1 | # SS 2 | 3 | ## 服务器使用ss 4 | ```shell 5 | # 安装SS 6 | apt-get update 7 | apt install shadowsocks-libev 8 | 9 | # 安装 obfs plugin 10 | apt-get install --no-install-recommends build-essential autoconf libtool libssl-dev libpcre3-dev libev-dev asciidoc xmlto automake 11 | git clone https://github.com/shadowsocks/simple-obfs.git 12 | cd simple-obfs 13 | git submodule update --init --recursive 14 | ./autogen.sh 15 | ./configure && make 16 | make install 17 | 18 | # 使Obfs-server能够监听端口443 19 | setcap cap_net_bind_service+ep /usr/local/bin/obfs-server 20 | ``` 21 | 22 | 编辑`/etc/shadowsocks-libev/config.json` 23 | ```json 24 | { 25 | "server": "0.0.0.0", 26 | "server_port": 13003, 27 | "password": "mypassword", 28 | "timeout": 60, 29 | "method": "aes-256-gcm", 30 | "mode": "tcp_and_udp", 31 | "plugin": "obfs-server", 32 | "plugin_opts": "obfs=http;obfs-host=db5801e05e.microsoft.com", 33 | /* "plugin_opts": "obfs=tls;obfs-host=www.bing.com", 34 | "workers":8, 35 | "method":"chacha20-ietf-poly1305", 36 | "local_port":1080, 37 | "fast_open":true, 38 | "reuse_port":true */ 39 | } 40 | ``` 41 | 防火墙与日志 42 | ```SHELL 43 | iptables -I INPUT 1 -p tcp --dport 13003 -j ACCEPT 44 | 45 | journalctl -u shadowsocks-libev-obfs.service -n 50 46 | ``` 47 | 48 | ## 客户端使用clash 49 | ```yaml 50 | #port: 7890 51 | #socks-port: 7891 52 | #listen: 192.168.1.25 53 | mixed-port: 7890 54 | socks-port: 7891 55 | port: 7892 56 | allow-lan: true 57 | mode: Rule 58 | log-level: info 59 | external-controller: ':9090' 60 | proxies: 61 | - { name: SG|SS, type: ss, server: ipaddress, port: 13003, cipher: aes-256-gcm, password: 'mypassword', plugin: obfs, plugin-opts: { mode: http, host: db5801e05e.microsoft.com }, udp: true } 62 | 63 | ``` 64 | -------------------------------------------------------------------------------- /Linux/incommonuselist.md: -------------------------------------------------------------------------------- 1 | # 常用列表 2 | ## linux 3 | * ls 显示文件年份 4 | ```shell 5 | ls -l --time-style=full-iso 6 | ls -l --time-style=long-iso 7 | ls -l --time-style="+%Y-%m-%d %H:%I:%S" 8 | ls -lc filename --列出文件的 ctime (最后权限时间) 9 | ls -lu filename --列出文件的 atime(最后读取时间) 10 | ls -l filename --列出文件的 mtime (修改文件内容) 11 | ``` 12 | -------------------------------------------------------------------------------- /Linux/mac.md: -------------------------------------------------------------------------------- 1 | ## homebrew 2 | 3 | 替换brew.git 4 | ``` 5 | cd "$(brew --repo)” 6 | git remote set-url origin https://mirrors.aliyun.com/homebrew/brew.git 7 | ``` 8 | 替换homebrew-core.git: 9 | ``` 10 | cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core" 11 | git remote set-url origin https://mirrors.aliyun.com/homebrew/homebrew-core.git 12 | ``` 13 | 14 | 替换homebrew-bottles 15 | ``` 16 | echo $SHELL # 看输出结果是/bin/zsh还是/bin/bash 17 | 18 | echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.aliyun.com/homebrew/homebrew-bottles' >> ~/.zshrc 19 | source ~/.zshrc 20 | 21 | echo 'export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles' >> ~/.bash_profile 22 | source ~/.bash_profile 23 | ``` 24 | 25 | 关闭brew每次执行命令时的自动更新 26 | ``` 27 | export HOMEBREW_NO_AUTO_UPDATE=true 28 | ``` 29 | 30 | ``` 31 | brew search go 32 | brew info go 33 | brew install go@1.1 34 | brew services start mariadb@10.1 35 | ``` 36 | 37 | [aliyun mirror](https://developer.aliyun.com/mirror/homebrew?spm=a2c6h.13651102.0.0.3e221b11s6RwUT) 38 | -------------------------------------------------------------------------------- /Notepad/APP签名.md: -------------------------------------------------------------------------------- 1 | # APP Signing 2 | 3 | ## Android 4 | Android 应用签名是应用打包过程的重要步骤之一,Google 要求所有的应用必须被签名才可以安装到 Android 操作系统中。Android 的签名机制也为开发者识别和更新自己应用提供了方便。 5 | 6 | * [Android](https://developer.android.com/studio/publish/app-signing) 7 | * [Google pepk.jar](https://www.gstatic.com/play-apps-publisher-rapid/signing-tool/prod/pepk.jar) 8 | * [Google pepk-src.jar](https://www.gstatic.com/play-apps-publisher-rapid/signing-tool/prod/pepk-src.jar) 9 | * [pepk-src](https://github.com/yongjhih/pepk) 10 | 11 | ### KeyStore 12 | 数字证书,是一个存储了证书的文件。文件包含证书的私钥,公钥和对应的数字证书的信息。 13 | 14 | * keytool 15 | 16 | jdk 工具包,在 JAVA_HOME 下查找 17 | 18 | ```shell 19 | # 生成 keystore 20 | # validity 有效天数 21 | keytool -genkey -alias ifuture -keyalg RSA -validity 365 -keystore ifuture.keystore 22 | 23 | # 查看 24 | keytool -list -v -keystore ifuture.keystore 25 | ``` 26 | 27 | * Android Studio 28 | 29 | Build -> Generate signed Bundle/APK 30 | 31 | 一般使用 `keytool` 生成的密码库类型是 `PKCS12` ,Android Studio 生成的是 `JKS` 32 | 33 | 互相转换 34 | ```shell 35 | # PKCS12 -> JKS 36 | keytool -importkeystore -srckeystore ifuture.keystore -srcstoretype PKCS12 -deststoretype JKS -destkeystore ifuture.jks 37 | # JKS -> PKCS12 38 | keytool -importkeystore -srckeystore ifuture.jks -srcstoretype JKS -deststoretype PKCS12 -destkeystore ifuture.keystore 39 | ``` 40 | 41 | 导入导出cert 42 | ```shell 43 | keytool -export -alias cert0001 -keystore trust.jks -storepass 123456 -file cert0001.cer 44 | keytool -import -v -alias cert001 -file cert001.cer -keystore trust.jks -storepass 123456 -noprompt 45 | 46 | ``` 47 | 48 | ### 签名 49 | 50 | * Android Studio 51 | 52 | Build -> Generate signed Bundle/APK 53 | 54 | * apksigner 55 | 56 | Android SDK 工具包,在 Android 安装目录下查找 57 | ``` 58 | /Users/zzz/Library/Android/sdk/build-tools/29.0.3/apksigner 59 | C:\\用户\zzz\AppData\Local\Android\Sdk\build-tools\29.0.3\apksigner.bat 60 | ``` 61 | 62 | ```shell 63 | # APK签名 64 | apksigner sign --ks ifuture.jks --ks-key-alias ifuture --out app_sign.apk app-debug-unsign.apk 65 | # 验证签名 66 | apksigner verify -v C:\Users\fanqi\app_sign.apk 67 | Verifies 68 | Verified using v1 scheme (JAR signing): true 69 | Verified using v2 scheme (APK Signature Scheme v2): true 70 | Verified using v3 scheme (APK Signature Scheme v3): false 71 | Number of signers: 1 72 | # 代表使用了 v2 进行的签名 73 | ``` 74 | 75 | ### 签名机制 76 | * V1 JAR signing 77 | > 第一代是基于 JAR 文件签名,它主要的缺陷是只保护了一部分文件,而不是对整个 APK 文件做保护。这是因为所有文件都不可能包含了自身的签名,因为它不可能为自己签名后再把签名信息保存到自己内部,这是一个鸡生蛋蛋生鸡的问题,因为这个问题的存在,第一代签名机制会忽略所有以 .SF/.DSA/.RSA 的文件以及 META-INFO 目录下的所有文件。 所以攻击者就可以解压缩后在 APK/META-INF 目录新增一个含有恶意代码的文件,然后再压缩成 APK,同样是可以覆盖安装正版应用的,这样一来好好的应用就会被杀毒软件标记为恶意软件,从而达到攻击应用的目的。 除了容易被攻击外,应用安装起来也比较慢,因为安装器在校验时需要解压计算所有文件的数字摘要,确认没有被恶意修改。 78 | 79 | * V2 80 | > Android 7.0 引入了第二代签名,避免了第一代签名模式的问题,主要改进在于它在验证过程中,将整个 APK 文件当作一个整体,只校验 APK 文件的签名就可以了,从而一方面更严苛的避免了 APK 被篡改,另外一方面也不用加压缩后对所有文件进行校验,从而极大提升了安装速度。第二代签名向后兼容,使用新签名的 APK 可以安装到 <7.0 的系统上,但要求 APK 同时也进行 v1 的签名。 具体来说,第二代签名将整个 APK 文件进行签名,并将签名信息保存在了 APK 文件的的尾部 Central Directory 的前边。它可以对第一三四,以及第二块除了签名部分的其他区域提供一致性保护。 81 | 在计算签名的时候,会将这些部分的数据切割成 1MB 大小的 CHUNK,分别计算签名,然后汇总后再计算一个总签名,这么做的主要目的是为了并行计算,加快速度。 82 | 为了避免攻击者在 7.0 以上系统中绕过 v2 签名机制(比如删除 APK Signing Block?),v2 签名要求如果 APK 同时提供了 V1 签名的话,需要在 META-INF/*.SF 文件中增加一行 X-Android-APK-Signed 属性,这样一来,支持 V2 签名的系统在回滚到 V1 签名的时候就会校验是否存在这个属性,如果存在,就会拒绝安装 APK,这一切都是建立在 *.SF 文件被 V1 签名保护的基础之上。 83 | 84 | * V3 85 | > Android 9.0 引入了第三代签名机制,主要增加一个功能叫 APK key rotation. 意思是允许开发者在更新 APK 的时候更换签名。签名的主要机制跟 V2 其实是一样的,只是重新设计了 APK Signing Block 的存储结构,以支持更换签名。这里就不再细说,可以参考官方的 文档 下图是安装一个 APK 时,系统对三代签名校验的流程示意。 86 | -------------------------------------------------------------------------------- /Notepad/Sales.md: -------------------------------------------------------------------------------- 1 | # 销售 2 | > 我一直认为销售这两字非常重,销售这个职业也非常重要甚至是伟大。在我们生活中也是不断的出现,找对象,找工作,面试,考试等都是在销售自己。而你研发的产品,你的idea更需要好的销售去让更多的人使用或知道她的存在和价值。我认为我们人人都应该是销售。 3 | 4 | > 决策链,参与链,预算,竞争对手,时间节点 5 | 6 | ## 理论 7 | ### 六个要素 8 | 9 | * 痛苦 - Pain 10 | 11 | > 很多销售机会产生于客户世界中的一个个痛点。这种痛苦可能是错过的机会,难以适应行业变化,或者是正在出现的竞争威胁。虽然挑战的可能性是无穷无尽的,但他们都汇聚成三个基本需求:赚钱,省钱或管理风险。在今天已经变化的环境下,销售人员需要重新确认他们对客户痛苦的理解,因为大多数人的痛苦已经改变了。例如,客户的目标可能已经从增加收入变成了保持收入。 12 | > 如果不与客户重新讨论这个话题,销售人员就有可能向客户提供一个只能满足客户过去需求的价值主张,在某些情况下,还可能会交付过时的业务模型。销售人员必须和客户的利益相关者共同协作,从定义客户所面临的核心挑战这一步开始。 13 | 14 | * 客户会采取行动吗? 15 | * 是否找到了最优先级的痛苦? 16 | * 我们与这个有痛苦的人验证了痛苦了么? 17 | * 我们是否了解其他人是如何受这个痛苦影响的? 18 | * 预算到位了吗? 19 | * 有时间表解决这个痛苦吗? 20 | 21 | * 权利 - Power 22 | 23 | > 在这里,权力是指客户方被授权做出购买决策的利益相关者。销售人员需要确保他们正在接触这个群体,并以一种能与决策者产生共鸣的方式来定位解决方案。当遇到经济停滞或市场下行,这种方法会因预算缩减且购买权利集中于少数人手中而更具挑战。在这种情况下,预算的权力往往会上升到组织架构的高层。因此,销售人员必须寻找新的高层领导关系,发现谁可能没有参与最初的谈话。此外,当一些利益相关者计划暂停或取消现有交易时,销售人员可能需要克服这些反对意见。 24 | > 销售人员可能无法依赖他们现有的联系人或在业务中已确定的支持者。这些人员对销售成功的影响可能较小,但却可以作为引荐结识高层决策者的起点。 25 | 26 | * 我们是否在和恰当的人合作? 27 | * 我们是否理解这个机会中的关键人物的角色? 28 | * 我们是否理解谁及如何影响决策? 29 | * 我们是否和能够决策的人建立了联系? 30 | * 我们有关键人物支持吗? 31 | * 我们与决策资金的人有联系吗? 32 | 33 | * 构想 - Vision 34 | 35 | > 大多数情况下,销售人员将需要更改解决方案以确保它符合客户当前面临的挑战。销售人员需要具备创造性,再次思考解决方案的构成以及交付方式。一个真正一致的解决方案必须是差异化的,定位于满足客户的长期需求。由于这个原因,销售人员作为长期合作伙伴和协作者的角色很重要,客户可能在实施后还需要调整解决方案,因为他们面临的挑战还在变化。 36 | > 最精干的销售专家不仅会跟踪客户当前的挑战——他们还帮助客户清楚地了解这些挑战。这样做,销售人员就有了一个影响客户思考的宝贵机会。当企业在变化的经济环境中努力寻找自己的位置时,销售人员就有影响客户制定策略的机会。 37 | 38 | * 客户倾向于我们的方案吗? 39 | * 我们帮助客户建立了最初的需求? 40 | * 我们的解决方案符合他们的需求? 41 | * 我们是否为关键人士建立或重述了差异化的构想? 42 | * 关键人物是否支持我们方案的方法? 43 | 44 | * 价值 - Value 45 | 46 | > 客户比以往任何时候都更关注价值。预算已经大幅减少,所有支出都需要经过投资回报率的彻底审查。因此,销售人员需要将解决方案的价值体现在可度量的业务因素中,比如预期收益、毛利率或长期可节省的开支。解决方案的价值必须超越现状和客户正在考虑的其他竞争方案的价值。 47 | > 锚定解决方案的价值可能需要销售人员提供有量化结果的案例。量化价值是困难的,因为销售人员要能够将一些软性因素转换成数字,这种对很多人来说是一种挑战。从长期来看,销售人员还必须兑现自己的承诺,帮助客户实现可衡量结果。 48 | 49 | * 我们的解决方案是否为双方提供了价值? 50 | * 我们理解每一个关键人物/公司的价值? 51 | * 关键人物量化和预测了我们方案的价值了吗? 52 | * 已经同意了公司的价值分析吗? 53 | * 价值分析能够保证动用资金吗? 54 | * 对我们有足够的价值吗?利润?战略? 55 | 56 | * 协作 - Collaborate 57 | 58 | > 随着商业环境愈发动态化,销售人员与客户的协作需求也逐渐增加。客户在不断调整和适应。当全球商业增速放缓,他们在探索新的收入渠道。这是个迭代的过程。因此,销售人员需要与利益相关者步调一致地开展工作。长期业务方向上,他们还必须确保自己能够接触到项目所涉及的所有利益相关者。由于业务在不确定中探索前行,利益相关者群体可能会发生变化,甚至还会缩减。 59 | > 面对充满挑战的时局和新的解决方案,客户在评估购买时需要更多的安全感。要降低销售的风险,作为回应,销售人员需要重点关注如何评估新的或已更改的解决方案的协作方式。 60 | > 销售人员必须了解客户购买流程的后续步骤。在这个阶段,销售人员应该开始查看达成销售的时间表。协作是衡量客户紧迫感和对参与解决方案承诺的机会。 61 | 62 | * 迫切采取行动的原因 Compelling reason to act 63 | 64 | > 有时候,客户可能会找到不采取行动的有力理由。这时,销售人员需采用解决异议模型,客观中立地理解异议,通过提问以确定异议背后的原因,最后以化解异议的方式来定位解决方案。 65 | 66 | * 我们是否能够影响购买流程? 67 | * 我们理解关键人物的决策流程和标准? 68 | * 我们理解关键人物证实能力及满意的要求吗? 69 | * 我们清楚客户的购买习惯、政治因素和过程吗? 70 | * 客户已经同意和我们一起进行评估吗? 71 | * 我们能够控制评估过程吗? 72 | * 我们能够成功的管理我们的风险吗? 73 | 74 | ### 学会问问题 75 | 学会问有用的问题 76 | 学会从侧面问问题 77 | 学会问客户乐意回答的不厌烦的问题 78 | 79 | * 了解客户在组织中的职责和项目中的角色 80 | * 您都管哪些部门和业务? 81 | * 这个项目您主抓吗?由谁决定是否做这个事儿? 82 | * 这个项目的钱怎么申请呀?您是申请发起人吗?都有经过谁批呀? 83 | * 这个项目还有什么人需要参与? 84 | * 了解客户需求认知方式 85 | * 您办这些业务为了什么? 86 | * 您工作中目前最急需解决的问题是什么? 87 | * 您最近一两年的工作重点是什么? 88 | * 了解客户对销售的态度,确定需求细节 89 | * 您能把您要做的几件事的具体要求告诉我吗? 90 | * 您这个事要完成的目标是什么?做到什么程度? 91 | * 我们还有竞争对手吗? 92 | -------------------------------------------------------------------------------- /Notepad/产品设计.md: -------------------------------------------------------------------------------- 1 | 产品设计 2 | ---------- 3 | 4 | ## KISS 原则 5 | * Keep it Simple and Stupid 保持简单和愚蠢 6 | * Keep It Sweet & Simple 7 | * Keep It Short & Simple 8 | * Keep it Simple, Sweetheart 9 | * Keep it Simple, Sherlock 10 | 11 | MVP (Minimum Viable Product 最简化可实行产品) 12 | > 1. 它有用吗?(Is it useful?) 13 | 2. 它易用吗?(Is it easy to use?) 14 | 3. 它用起来令人愉悦吗?(Is it delightful to use?) 15 | 16 | > 1. 给谁用? 17 | 2. 他们用这个产品来解决什么问题? 18 | 3. 这个问题对他们而言有多重要? 19 | 4. 我们的方法是否足够简单方便? 20 | 5. 他们要付出的代价与所得是否匹配? 21 | 22 | > * 第一步:谁来购买你的产品?为什么购买?市场有多大? 23 | * 第二步:客户愿意付多少钱购买你的产品?竞争对手是什么价格? 24 | * 第三步:客户在什么地方能买到你的产品? 25 | * 第四步:为了让客户买到你的产品,你要付出多少销售成本? 26 | * 第五步:你要生产出这些产品,能够负担的最高生产成本是多少? 27 | 28 | > 通过这一系列的步骤,你就可以整理出你的创业的大概轮廓,其中包括这样几个要点:产品定位(最终消费者如何看待你的该产品)、原料(你的上游厂商是谁)、生产过程(产品如何生产出来)、定价(你卖给批发商、零售商、消费者的价格分别是多少)、经销渠道(你怎样向最终消费者出售你的产品?存在哪些中间商?你如何向他们付酬?)、营销(你如何让消费者了解你的产品)、市场的进入壁垒(你的竞争对手会不会轻易地复制你的经营模式)、规模的扩大(如何扩大业务)等等。 29 | 30 | 31 | ## POC 32 | Proof of Concept 验证性测试 33 | 34 | ## 图片素材 35 | * https://unsplash.com/ 36 | * http://www.pexels.com/ 37 | * http://pixabay.com/ 38 | * http://www.gratisography.com  39 | * https://visualhunt.com/ 40 | * http://finda.photo 41 | * http://cupcake.nilssonlee.se/ 42 | * https://www.photock.jp/ 43 | * http://pngimg.com/ 44 | * http://www.designerspics.com 45 | 46 | 软件版本 47 | --------- 48 | ## 通用 49 | Alpha:软件或系统的内部测试版本,会有很多Bug,仅内部人员使用 50 | 51 | Beta:软件或系统的测试版本,这一版本通常是在Alpha版本后,会有很多新功能,同时也有不少Bug 52 | 53 | Stable:稳定版本,这一版本基于Beta版,已知Bug都被修复,一般情况下,更新比较慢 54 | 55 | Refeleases:发布版 56 | 57 | LTS(Long Term Support):长期演进版,Ubuntu会对这一版本的支持时间更长。目前Java也在运用这种方式 58 | 59 | ## 微软常用 60 | 61 | RC(Release Candidate):候选版本,这一版本不会增加新功能,多要进行Debug 62 | 63 | GA(General Available):正式发布版本,这个版本就是正式的版本 64 | 65 | RTM(Release to Manufacture):给工厂大量生产的压片版本,与正式版内容一样 66 | 67 | OEM(Original Entrusted Manufacture):给计算机厂商的出场销售版本,不零售只预装 68 | 69 | RVL:号称是正式版,其实RVL根本不是版本的名称。它是中文版/英文版文档破解出来的 70 | 71 | EVAL:而流通在网络上的EVAL版,与“评估版”类似,功能上和零售版没有区别 72 | 73 | RTL(Retail):零售版是真正的正式版,正式上架零售版 74 | -------------------------------------------------------------------------------- /Notepad/公式.md: -------------------------------------------------------------------------------- 1 | # 公式 2 | 3 | ## 发言汇报 4 | * ORID 5 | 6 | O(objective):事实/信息 7 | R(reflective):感受/体验 8 | I(interpretive):理解/思考 9 | D(decision):决定/行动 10 | 11 | ## 销售 12 | * PPVVC 13 | * Pain 痛苦,没有痛苦就没有购买 14 | * Power 权力,有权利的人向有权利的人购买 15 | * Vison 构想,没有构想和愿景就不要呈现方案和产品 16 | * Value 价值,解决方案为双方提供可以量化的价值 17 | * Control 控制,要控制客户的购买流程 18 | 19 | SPI中影响销售的关键因素。 20 | > Sales Performance International(SPI)是解决方案销售(Solution Selling®)方法的知识产权所有者 21 | 22 | 23 | ## 项目管理 24 | * SWOT分析法 25 | * strengths 优势 26 | * weaknesses 劣势 27 | * opportunities 机会 28 | * threats 威胁 29 | 30 | 认清自己的能力、资源长短优劣状态。 31 | 认清环境、趋势的利弊和危机。 32 | 防范可能存在的潜在的风险和威胁。 33 | 34 | * PDCA循环法 35 | * plan 制定目标与机会 36 | * do 任务展开,组织实施 37 | * check 对过程中的关键点和最终结果进行检查 38 | * action 纠正偏差,对成果进行标准化,并确定新的目标,制定下一轮机会 39 | 40 | 每项工作都是一个PDCA循环,一个闭环,有始有终。 41 | 并且进入下一循环,在日积月累的精进与改善中,实现质的飞越。 42 | 人生就是一个螺旋上升的PDCA循环 43 | 44 | * 6W2H 45 | * what 你的人生愿景是什么?你的使命是什么? 46 | * way 做这项工作的原因 47 | * who 参加这项工作的具体人员,以及负责人 48 | * when 在什么时间、什么时间段进行工作 49 | * where 工作发生的地点 50 | * which 哪一种方法或途径 51 | * how 用什么方法进行 52 | * how much 需要多少成本?多大代价? 53 | 54 | 做汇报、写报告、看报告 55 | 56 | * SMART法 57 | * specific 具体的 58 | * measurable 可测量的 59 | * attainable 可达到的 60 | * relevant 相关的 61 | * time based 有时限的 62 | 63 | * 时间管理任务四象限 64 | * A重要且紧急。紧急状态,迫切问题,限期完成,你不做其他人也不能做 65 | * B重要不紧急。准备工作,预防措施,价值观的澄清,计划,正真的再创造,增进自己的能力,健康、锻炼 66 | * C紧急不重要。 67 | * 不重要不紧急 68 | 69 | * 二八法则 70 | * 80%的销售额是源自20%的顾客 71 | * 80%的电话是来自20%的朋友 72 | * 80%的总产量是源自20%的产品 73 | * 80%的业绩是来自20%的员工/工作 74 | * 80%的财富集中在20%的人手中 75 | -------------------------------------------------------------------------------- /Notepad/开源协议.md: -------------------------------------------------------------------------------- 1 | Opensource License 2 | -------- 3 | # GUN 4 | ## GPL 5 | [GNU General Public Licence](http://www.opensource.org/licenses/gpl-2.0.php) 6 | 有可能是开源界最常用的许可模式。GPL 保证了所有开发者的权利,同时为使用者提供了足够的复制,分发,修改的权利: 7 | * 可自由复制 8 | > 你可以将软件复制到你的电脑,你客户的电脑,或者任何地方。复制份数没有任何限制。 9 | 10 | * 可自由分发 11 | > 在你的网站提供下载,拷贝到U盘送人,或者将源代码打印出来从窗户扔出去(环保起见,请别这样做)。 12 | 13 | * 可以用来盈利 14 | > 你可以在分发软件的时候收费,但你必须在收费前向你的客户提供该软件的 GNU GPL 许可协议,以便让他们知道,他们可以从别的渠道免费得到这份软件,以及你收费的理由。 15 | 16 | * 可自由修改 17 | > 如果你想添加或删除某个功能,没问题,如果你想在别的项目中使用部分代码,也没问题,唯一的要求是,使用了这段代码的项目也必须使用 GPL 协议。 18 | 19 | 需要注意的是,分发的时候,需要明确提供源代码和二进制文件,另外,用于某些程序的某些协议有一些问题和限制,你可以看一下 @PierreJoye 写的 Practical Guide to GPL Compliance 一文。使用 GPL 协议,你必须在源代码代码中包含相应信息,以及协议本身。 20 | ## AGPL 21 | GNU Affero General Public License 22 | 23 | AGPL 可以说是最严格的 GPL 了,强传染性,即使是 RPC 调用也会被感染,不发行软件而是作为 web 服务对外提供也必须开放源代码。 24 | 25 | 简单理解可以商用可以修改,但必须都开源出来 26 | 27 | ## LGPL 28 | [Lesser General Public Licence](http://www.opensource.org/licenses/lgpl-2.1.php) 29 | 对产品所保留的权利比 GPL 少,总的来说,LGPL 适合那些用于非 GPL 或非开源产品的开源类库或框架。因为 GPL 要求,使用了 GPL 代码的产品必须也使用 GPL 协议,开发者不允许将 GPL 代码用于商业产品。LGPL 绕过了这一限制。 30 | 31 | # BSD 32 | BSD 在软件分发方面的限制比别的开源协议(如 GNU GPL)要少。该协议有多种版本,最主要的版本有两个,新 BSD 协议与简单 BSD 协议,这两种协议经过修正,都和 GPL 兼容,并为开源组织所认可。 33 | 34 | 新 BSD 协议(3条款协议)在软件分发方面,除需要包含一份版权提示和免责声明之外,没有任何限制。另外,该协议还禁止拿开发者的名义为衍生产品背书,但简单 BSD 协议删除了这一条款。 35 | # MIT 36 | 该软件及其相关文档对所有人免费,可以任意处置,包括使用,复制,修改,合并,发表,分发,再授权,或者销售。唯一的限制是,软件中必须包含上述版 权和许可提示。 37 | 这意味着: 38 | 你可以自由使用,复制,修改,可以用于自己的项目。 39 | 可以免费分发或用来盈利。 40 | 唯一的限制是必须包含许可声明。 41 | MIT 协议是所有开源许可中最宽松的一个,除了必须包含许可声明外,再无任何限制。 42 | # Apache 43 | Apache 协议 2.0 和别的开源协议相比,除了为用户提供版权许可之外,还有专利许可,对于那些涉及专利内容的开发者而言,该协议最适合(这里有 一篇文章阐述这个问题)。 44 | Apache 协议还有以下需要说明的地方: 45 | * 永久权利 46 | > 一旦被授权,永久拥有。 47 | 48 | * 全球范围的权利 49 | > 在一个国家获得授权,适用于所有国家。假如你在美国,许可是从印度授权的,也没有问题。 50 | 51 | * 授权免费,且无版税 52 | > 前期,后期均无任何费用。 53 | 54 | * 授权无排他性 55 | > 任何人都可以获得授权 56 | 57 | * 授权不可撤消 58 | > 一旦获得授权,没有任何人可以取消。比如,你基于该产品代码开发了衍生产品,你不用担心会在某一天被禁止使用该代码。 59 | 60 | 分发代码方面包含一些要求,主要是,要在声明中对参与开发的人给予认可并包含一份许可协议原文。 61 | ## Creative Commons 62 | CC 并非严格意义上的开源许可,它主要用于设计。Creative Commons 有多种协议,每种都提供了相应授权模式,CC 协议主要包含 4 种基本形式: 63 | * 署名权 64 | > 必须为原始作者署名,然后才可以修改,分发,复制。 65 | 66 | * 保持一致 67 | > 作品同样可以在 CC 协议基础上修改,分发,复制。 68 | 69 | * 非商业 70 | > 作品可以被修改,分发,复制,但不能用于商业用途。但商业的定义有些模糊,比如,有的人认为非商业用途指的是不能销售,有的认为是甚至不能放在有广告的网 站,也有人认为非商业的意思是非盈利。 71 | 72 | * 不能衍生新作品 73 | > 你可以复制,分发,但不能修改,也不能以此为基础创作自己的作品。 74 | 75 | 这些许可形式可以结合起来用,其中最严厉的组合是“署名,非商用,不能衍生新作品”,意味着,你可以分享作品,但不能改动或以此盈利,而且必须为原 作者署名。在这种许可模式下,原始作者对作品还拥有完全的控制权,而最宽松的组合是“署名”,意味着,只要为原始作者署名了,就可以自由处置。 76 | 77 | # WTFPL 78 | Do What The Fuck You Want To Public License 79 | 你他妈的想干嘛就干嘛公共许可证 80 | 81 | # 木兰宽松许可证 82 | 83 | 与Apache License V2差不多相比下 [MulanPSL](https://license.coscl.org.cn/MulanPSL/) 拥有两大优势: 84 | 85 | * 解决联盟存在互诉漏洞,也就是A想诉B,A授权C,由C可以诉B 的问题。 86 | * 比Apache License更友好一些,Apache License要求列出每个修改文件,其实很多项目做不到这一点,所以MulanPSL直接取消了这项要求 87 | -------------------------------------------------------------------------------- /Notepad/数据源.md: -------------------------------------------------------------------------------- 1 | 1.国家数据:data.stats.gov.cn,包含了我国经济民生等多个方面的数据,并且在月度、季度、年度都有覆盖,很权威很全面。 2 | 3 | 2.CEIC:www.ceicdata.com,涵盖20个主要行业以及18个主要宏观经济方面的数据库,能够精确查找多个国家和地区的GDP、CPI、进口、出口、外资直接投资、零售、销售以及国际利率等数据。 4 | 5 | 3.搜数:www.soshoo.com,已加载到搜数网站的统计资料达到10867本,涵盖2566317张统计表格和504990536个统计数据,汇集了中国资讯行自92年以来收集的44个行业所有统计和调查数据。 6 | 7 | 4.中国人民银行:www.pbc.gov.cn,可查询社会总资规模、货币供应量、外汇储备、利率变化、汇率、shibor等金融领域的权威数据。 8 | 9 | 5.CNNIC:www.cnnic.net.cn,可查询互联网网络相关的调查报告,如网民数量、分领域用户规模、细分互联网领域调查报告等信息。 10 | 11 | 6.金融信息网:dc.xinhua08.com,提供多个国家和地区的宏观经济数据,包括CPI、GDP、外汇储备、失业率、进出口等。 12 | 13 | 7.证监会官网:www.csrc.gov.cn,提供各行业的发展情况、统计数据,最新动态、相关监管政策。 14 | 15 | 8.中国信通院:www.caict.ac.cn,提供互联网、通讯、手机市场等相关数据和分析报告。 16 | 17 | 9.产业信息网:www.chyxx.com,主要提供实体经济数据,还包括行业分析和研究,基本上涵盖了各个细分领域的数据,非常全面。 18 | 19 | 10.收视率排行:www.tvtv.hk,提供日/周/月/年的前25名的收视率以及市场份额数据。 20 | 21 | 11.镝数聚:www.dydata.io,提供12大领域、100+行业、6000+权威数据源,基本覆盖了所有的细分行业,大部分数据都可以免费下载。 22 | 23 | 12.艾瑞网:www.iresearch.cn,里面有各种行业的报告、白皮书等内容,大多数都可以直接下载。 24 | 25 | 13.199IT:www.199it.com,权威的互联网数据平台,涵盖新兴产业、电商互联网、共享经济、社交营销、移动互联网以及其他互联网服务的数据及分析报告。 26 | 27 | 14.世界银行公开数据:data.worldbank.org.cn,各国相关的发展数据,而且资金等数据有多个维度可以查看,免费公开。 28 | 29 | 15.清博大数据:www.gsdata.cn,提供各类新媒体平台的榜单数据,各种研究报告、数据报告、舆情报告及行业热点。 30 | 31 | 16.东方财富网:data.eastmoney.com,用于上市企业研究,内容较多,可以了解到一个公司的概况、经营分析、行业相关情况。 32 | 33 | 17.IT桔子:www.itjuzi.com,各种创投数据,包括风险投资、收购、竞争情报、细分行业信息、产品信息数据服务等内容。 34 | -------------------------------------------------------------------------------- /Notepad/软件架构师.md: -------------------------------------------------------------------------------- 1 | # 什么是软件架构师 2 | 3 | - 软件架构师是一位软件专家,他可以进行高级设计选择并决定技术标准,包括软件编码标准,工具和平台。 *(来源:维基百科:软件架构师)* 4 | - 软件体系结构是系统的基本组织,由其组件,它们之间的相互关系以及与环境的关系以及确定系统设计和演进的原理来表示。 *(来源:软件体系结构手册)* 5 | 6 | # 架构师级别 7 | 8 | 软件架构可以被抽象的分为几个层次 9 | 10 | - **项目级别**:最低的架构级别。专注于单个项目。非常详细的底层设计。通常在一个开发团队中进行沟通。 11 | - **解决方案级别**:中级架构。专注于满足业务需求(业务解决方案)。解析需求并进行系统的低层设计。多个开发团队之间的协调。 12 | - **企业级别**:最高的体系结构级别。专注于多种解决方案。高层次的抽象设计,需要解决方案或项目架构师对其进行详细说明。整个组织的沟通。相当于CTO。请参阅[链接](https://github.com/justinamiller/EnterpriseArchitecture)以了解更多信息。 13 | 14 | 有时,架构师也被视为不同利益相关者之间的“桥梁”。 15 | 16 | - **水平**:在业务和开发人员或不同的开发团队之间架起沟通桥梁。 17 | - **垂直**:在开发人员和管理人员之间架起沟通桥梁。 18 | - **技术**:不同的技术栈或其他产品的集成和融合。 19 | 20 | # 基本工作内容 21 | 22 | - 定义和决策开发技术和平台 23 | - 定义开发标准,例如编码标准,工具,审查流程,测试方法等。 24 | - 认识和理解业务需求 25 | - 根据需求设计系统并做出决策 26 | - 记录并传达整体架构定义,设计和决策 27 | - 检查和审查架构与代码,例如,检查定义的模式和编码标准是否正确实施 28 | - 与其他架构师和利益相关者合作 29 | - 指导开发人员 30 | - 询问开发人员,项目进度或遇到的问题,甚至包括心理咨询“心灵鸡汤”灌输。 31 | - 详细说明并将上级需求转为具体工作内容 32 | 33 | # 应具备的技能 34 | 35 | - 设计 36 | - 决策 37 | - 简化 38 | - 编码 39 | - 文档 40 | - 沟通 41 | - 评估 42 | - 平衡 43 | - 指导 44 | - 市场 45 | 46 | ## 1. 设计 47 | 48 | 系统设计是最重要也是具挑战性的一项技能。我们需要借助理论和实践相结合,完成一个适合当下环境的整体系统架构设计。 49 | 50 | - **学习设计模式**:设计模式是架构师开发可维护系统所需的最重要工具之一。使用设计模式,您可以重用设计,以通过可靠的解决方案解决常见问题。 51 | > John Vlissides,Ralph Johnson,Richard Helm和Erich Gamma撰写的《设计模式:可重用的面向对象软件的要素》一书是所有软件开发人员必读的。尽管这些模式已经发布了20多年,但它们仍然是现代软件体系结构的基础。例如,本书描述了模型-视图-控制器(MVC)模式,该模式在许多领域都得到了应用,或者是更新模式的基础,例如模型-视图-视图模型(MVVM)。 52 | 53 | - **深入研究设计模式**:如果您已经了解所有基本的GoF模式(Gang of Four,4人组,也就是上面提到的《设计模式》),则可以通过更多的软件设计模式来扩展您的知识,或者更深入地研究您感兴趣的领域。 54 | > 学习 Spring 源码中的各种设计模式的巧妙应用;学习现代的微服务,无服务器架构,大型的分布式架构等。保持时代的脚步。 55 | 56 | - **学习质量衡量指标**:出于代码质量和非功能性要求定于编码标准。达到系统的可维护,可靠,适应性强,安全,可测试,可扩展,高可用。 57 | > 在 Java 项目可以使用 Alibaba Java Coding Guidelines 要求每位开发人员先自测。 58 | 59 | - **了解不同的技术栈**:要成为一名更好的架构师,学习新技术栈,从浅到深的学习。不同的(新)技术具有不同的架构设计,值得学习。学习新技术不应只是简单阅读文档(但这也是必须的),而是自己尝试发现其中的痛点和存在的意义。架构师不仅应该具有广泛的知识,而且在某些领域还应该具有深刻的知识。 60 | > 好奇并尝试新事物。甚至还去尝试一些之前不喜欢的东西。保持学习 61 | 62 | - **分析框架源码**:阅读大型的开源框架源码,尝试了解它如何在框架中应用,为什么要这样做。更深入地研究代码并了解其实现方式。 63 | > 不仅可以学习实现方式,设计模式。还包括大型开源的项目管理及可借鉴的工具等。 64 | 65 | - **交互设计**: 不仅需要设计软件架构,有时候你还需要了解交互设计,有基本的审美,可以让设计师发挥他们的设计能力,保持对创造的热情,有时候这也有助于你更好的设计软件架构。 66 | 67 | ## 2. 决策 68 | 69 | 架构师必须做出决策并指导项目团队朝正确的方向发展。 70 | 71 | - **专注于你需要专注的** :不同阶段的架构师有不同的职责,专注于你当前的职责,尽早与其他架构师沟通完成决策。 72 | 73 | - **优先级**:有时最好做出“错误”的决策而不是没有决策。系统是也是循序渐进的,不一定一开始你就需要考虑到象淘宝一样量级的技术选型与决策。 74 | > 使用一些技巧,如:加权最短作业优先(WSJF)模型。在时间紧迫性和风险降低措施对于评估架构决策的优先级至关重要。 75 | 76 | - **能力边界**:不要决定能力之外的事情。这很关键,因为如果不考虑的话,它可能会严重破坏您作为架构师的地位。为避免这种情况,请与您的同伴明确您要承担的责任以及角色的一部分。如果架构师不止一个,那么您应该尊重当前部署的架构级别。作为较低级别的架构师,您最好提出有关高层架构的建议,而不是决策。此外建议始终与同伴一起检查关键决策。 77 | 78 | - **评估多个选项**:在做决策是,如果发现你没有一个以上的选项可选,那这肯定是有问题的:首先,可能是你的工作做得不好,知识边界过窄;其次,它必然阻碍了你做出正确的决定。此外,还可以与项目成员或利益相关者展开讨论,发现更多的可选项,在讨论中可能会发现你遗漏的一些因素。 79 | 80 | 81 | ## 3. 简化 82 | 83 | “如无必要,勿增实体”,即“简单有效原理”。切勿浪费较多东西去做,用较少的东西,同样可以做好的事情。这就是 `奥卡姆剃刀定律` 。 84 | 85 | - **解决方案**:简化解决方案可能需要“摇动”现有的解决方案,从不同角度思考,问自己几个问题 86 | 1. 在理想环境中您的解决方案会发生什么? 87 | 2. 竞争对手会怎么做? 88 | 3. 大厂(BTA,GAFA)会怎么做?BAT(Baidu、Alibaba、Tencent),GAFA(Google、Apple、Facebook、Amazon) 89 | 90 | 有时候你需要在心里塑造一个不同的人格来判断问题。比如是乔布斯遇到这个问题会怎么做等。多问自己几个这样的问题可以迫使你减少假设。 91 | 92 | - **不忘初心**:经过长时间深入的讨论,通常会得出高度复杂的草案。永远不要将这些视为最终结果。停顿一下回到最初,是不是有过多的附加?你可以尝试明天再回头看看这些草案,有时候大脑需要一些时间来处理并提出更好,更优雅,更简单的解决方案。 93 | 94 | - **分而治之**:将问题分成小块以简化问题。然后独立解决它们。然后验证小块是否匹配。回到最初的需求查看总体情况。 95 | 96 | - **重构并不是坏事**:如果找不到更好的主意,则可以从更复杂的解决方案开始。 97 | > 重构并不是坏事。但是在开始重构之前,请记住要进行以下工作:(1)进行足够的自动化测试,以确保系统的正确功能;以及(2)从利益相关者的支持。要了解有关重构的更多信息,建议阅读《重构。改进现有代码的设计》,作者是Martin Fowler。 98 | 99 | ## 4. 编码 100 | 101 | 即使作为企业架构师,你仍然应该了解开发人员的日常工作。不然你可能会遇到:开发人员不接受你的提议;你也无法了解开发人员面临的挑战和问题。 102 | 103 | 不断去尝试新技术和工具,以了解当今以及未来的开发方式。我们必须了解大量的编程语言,框架,工具,过程和实践。只有对主要趋势有一定的经验和粗略的了解,才能参与对话并引导开发朝正确的方向发展。 104 | 105 | 106 | ## 5. 文档 107 | 108 | 架构决策或代码指南文档必不可少。 在开始编码之前通常需要初始项目文档,并且需要不断地完善。 也有一些文档在规范的代码下可自动生成,代码也可以是文档,例如 UML 类图。 109 | 110 | - **整洁的代码**:代码是最好的文档。 111 | > 《码出高效》,《阿里巴巴Java开发手册》 112 | 113 | - **生成文档**:系统正在快速迭代变化,很难更新文档。规范的编码习惯可以直接从源代码自动生成文档。 114 | > 可以利用一些工具如 Swagger 、RAML 115 | 116 | - **简明**: 简明的 wiki 也是最初文档的好选择。过长的文档不仅需要大量的编写时间,也大大增加后者的学习时间。 117 | > 一些大型的开源项目,如: Vue.js 文档写作风格值得学习 118 | 119 | * [Overleaf, 在线LaTeX编辑器](https://www.overleaf.com/project) 120 | 121 | ## 6. 沟通 122 | 123 | 沟通非常重要,而很多架构师都低估它的价值。你对软件架构设计精湛,却无法很好的表达你的思想,这可能会使项目成员偏离你的思想,导致项目失败。 124 | 125 | - **学习演讲**:架构师,通常不仅需要参加会议,而且通常需要主持会议。 126 | > 如果面对百人演讲会感到不适,可以尝试向面向镜子,面向朋友,慢慢一步步的走出自己的舒适区来学习演讲,不要过分的求速成,要有耐心 127 | 128 | - **沟通需要因人而异**:不同的利益相关者有不同的兴趣和看法。需要对他们进行单独处理。表达前也需要再次确认一下自己需要表达内容,目标,动机等。 129 | > 开发人员通常对解决方案的细节感兴趣,而管理人员则更喜欢知道哪个选项节省的钱最多。 130 | 131 | - **保持沟通**:定期主动的向上级和项目成员汇报项目进度和成果,保持沟通也保持了信任关系。 132 | 133 | - **透明**:如果成员过少的沟通,也不参与到决策的过程,那么他们也很难理解和遵循其背后的决策和理由。 134 | 135 | - **总结输出**:多做总结并输出写作、运营、品牌。 136 | 137 | ## 7. 评估 138 | 139 | 架构师或首席开发人员,经常会要求估计项目预期,如:多长时间,多少人,多少技能等?当然,如果您打算引入新的工具或框架,则需要为它们提供使用的依据。最初,你应该能够进行粗略的估算,例如几天,几个月或几年。但是要记住,不仅需要考虑开发时间,还有更多其他工作需要考虑,比如需求分析,测试和修复BUG等。因此,你需要了解软件开发的整个“闭环”过程。 140 | 141 |   1. 设计实践:项目架构遵循哪些模式?它们是否正确使用?项目预期需求边界是否受控制?是否有清晰的结构?是否高度耦合? 142 |   2. 开发实践:制定并遵循了代码准则?代码如何版本化?部署实践? 143 |   3. 质量保证:测试自动化范围?codeReview如何? 144 |   4. 安全性:有哪些安全概念?存在那些安全隐患?渗透测试或自动安全分析工具是否到位并经常使用? 145 | 146 | > 使用工具对代码进行质量、安全等的分析 147 | > * [SonarQube](https://www.sonarqube.org/) 148 | * [codereview](https://www.gerritcodereview.com/) 149 | * [git stats](http://gitstats.sourceforge.net/) 150 | * [phabricator](https://github.com/phacility/phabricator) 151 | * [infer](https://github.com/facebook/infer) 152 | * [load test](https://jmeter.apache.org/) 153 | 154 | ## 8. 平衡 155 | 156 | - **高质量的代价**:如果您过度关注架构,则会增加成本并可能降低开发速度。您需要平衡架构和功能需求,应避免过度设计。 157 | 158 | - **解决矛盾的目标** :项目的短期和长期目标通常是矛盾。项目通常倾向于构建最简单的解决方案,而架构师则应具有长远的眼光。通常,简单的解决方案不适合长期解决方案,并且有被以后抛弃的风险(降低成本)。为了避免实现错误的方向,需要考虑两点: 159 | 160 |   1. 开发人员和企业需要了解长期愿景及其收益,以适应其解决方案 161 |   2. 负责预算的经理需要参与以了解财务影响。不必直接将100%的愿景图放置在当前,但是也应考虑为后期其他愿景打下基础。 162 | 163 | - **冲突管理**:架构师通常是不同团队之间的粘合剂。这可能会发生一些沟通冲突。架构师需要帮助克服冲突,找到平衡的解决方案。 164 | > 这时候学习一些商业战略、管理学就显得很重要,平衡上级的预期与项目成员的利益,并获得他们的支持。 165 | 166 | ## 9. 指导、答疑 167 | 168 | 积极主动去询问项目成员遇到的问题与进度。 169 | 170 | 定期组织交流会,大家互相学习分享近期遇到的问题和学习心得,讨论过去和当前的挑战以及如何解决它们或采用新的方法论和方法。架构师也可以分享,讨论和调整其愿景,开发人员可以共享经验并向同行学习。这样的回合不仅可以为企业带来极大的好处,而且对个人本身也非常有利,因为它有助于建立更强大的网络并传播思想。 171 | 172 | > 项目管理[redmine](https://www.redmine.org/) 173 | 174 | ## 10. 市场 175 | 176 | 您的想法很棒,您已经很好地传达了他们的想法,但仍然没人愿意遵循吗?那么您可能缺乏营销技巧。 177 | 178 | - **包装**:了解创投市场,并运用一些工具,搜集与展示数据。 179 | 180 |   1. 原型:使用原型去展示你的想法。 181 |   2. 视频:制作视频可以更加生动的演示您的想法或方向。但是不要过度营销:从长远来看,内容为王。如果无法实现你承诺的,从长远来看,这将损害你的声誉。 182 | 183 | - **相信自己**:或许你的想法并不被大家认可,但如果你真的对自己的想法深信不疑,则应不断追求并为之奋斗。 184 | > 具有长期目标的体系结构决策通常不是最容易的:开发人员不喜欢它们,因为它们的开发更加复杂。经理们不喜欢它们,因为它们在短期内更昂贵。这是您要坚持不懈并进行谈判的工作。 185 | 186 | - **寻找同盟**:尝试找到可以支持和说服他人的盟友。 187 | > 可以从与同事头脑风暴你的想法开始。如果他们喜欢它,或者至少喜欢它的一部分,那么如果别人提出来,他们很可能会支持您的想法。如果他们不喜欢,问为什么:也许是你忽视了什么细节?还是你的故事不够令人信服?如果你不善于表达讨论,请记住,有时您需要离开舒适区,不断挑战自己。 188 | 189 | - **重复**:“研究表明,反复接触某个观点会使人们相信该观点更为普遍。如果你发布的信息足够频繁,则可以更轻松地说服人们。但请注意:应该明智地使用这种策略,因为它可能适得其反,成为糟糕的营销技巧。 190 | 191 | - **学习心理学,统计学**: 了解各利益相关者和客户群体心理。 192 | 193 | # 路线图 194 | 195 | ![frontend.png](https://i.loli.net/2020/02/26/SCR7NGnFoQD9YTg.png) 196 | 197 | ![backend.png](https://i.loli.net/2020/02/26/1L9tf3DWTVJkxdU.png) 198 | 199 | ![devops.png](https://i.loli.net/2020/02/26/PVkfYN5J4umZzSW.png) 200 | 201 | > 内容大多来自 [SoftwareArchitect](https://github.com/justinamiller/SoftwareArchitect)、 202 | > [Developer Readmap](https://github.com/kamranahmedse/developer-roadmap) 203 | > 添加了自己的一些理解与本地化 204 | -------------------------------------------------------------------------------- /Python/JupyterNotebook.md: -------------------------------------------------------------------------------- 1 | Jupyter Notebook 2 | ---------- 3 | 4 | * [jupyter notebook](https://jupyter.org/) 5 | * [JupyterHub](https://github.com/jupyterhub/jupyterhub) 6 | * [Beaker Extensions](https://github.com/twosigma/beakerx) 7 | * [zeppelin notebook](https://zeppelin.apache.org/) 8 | * [metaflow](https://github.com/Netflix/metaflow) 9 | 10 | ## 安装 11 | ```shell 12 | pip install notebook 13 | 14 | jupyter notebook --port 8888 --ip 0.0.0.0 15 | 16 | jupyter notebook --help 17 | 18 | 19 | ``` 20 | 21 | 使用虚拟环境 22 | ```shell 23 | # 安装 24 | pip install virtualenv 25 | # 创建虚拟环境 26 | virtualenv --no-site-packages my_venv 27 | # 进入虚拟环境 28 | source my_venv/bin/activate 29 | 30 | pip install ipykernel 31 | # 将 Virtualenv 加入IPykernel中 32 | # ipython kernel install --user --name=my_venv 33 | python -m ipykernel install --user --name=my_venv 34 | 35 | # 启动jupyter 36 | jupyter notebook 37 | 38 | ``` 39 | 40 | ```SHELL 41 | conda create -n name python=3.6 42 | conda info --envs 43 | conda activate name 44 | conda deactivate 45 | conda remove -n name --all 46 | ``` 47 | 48 | ## pip源 49 | ```shell 50 | pip install <包名> -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn 51 | ``` 52 | 53 | ## 插件 54 | ``` 55 | pip install jupyter_contrib_nbextensions 56 | jupyter contrib nbextension install --user 57 | pip install jupyter_nbextensions_configurator 58 | jupyter nbextensions_configurator enable --user 59 | 重启 Jupyter Notebook, 便可以看到nbextension 60 | jupyter nbextension enable execute_time/Exec 61 | ``` 62 | 63 | ``` 64 | %magic #显示所有魔术命令的详细文档 65 | %prun statement #通过 cProfile 执行对 statement 的逐行性能分析 66 | %time statement #测试 statement 的执行时间 67 | %timeit statement #多次测试 statement 的执行时间并计算平均值 68 | ``` 69 | -------------------------------------------------------------------------------- /Python/pySpark.md: -------------------------------------------------------------------------------- 1 | # Spark in Python 2 | 3 | * [pyspark tutorial](https://sparkbyexamples.com/pyspark-tutorial/) 4 | * [Spark Python examples](https://github.com/apache/spark/blob/master/examples/src/main/python/ml/lda_example.py) 5 | * [Koalas: pandas API on Apache Spark](https://github.com/databricks/koalas) 6 | -------------------------------------------------------------------------------- /_coverpage.md: -------------------------------------------------------------------------------- 1 | ![logo](https://blog.ifuture.pro/assets/img/qrcode_for_mpwechat.jpg) 2 | # Developer notes 3 | ## 开发者笔记 4 | 5 | * 一本众多开发者共同编写的开发笔记 6 | * 记录我们记录的 7 | 8 | 9 | 访问量: 10 | 访客数: 11 | 12 | 13 | [GitHub](https://github.com/ifuture-pro/developer-notes) 14 | [Get Started](README.md) 15 | -------------------------------------------------------------------------------- /algorithm/BloomFilter.md: -------------------------------------------------------------------------------- 1 | 布隆过滤器 Bloom Filter 2 | ------ 3 | ## 应用场景 4 | 在大量是数据池中找出某元素是否存在。 5 | * 字处理软件中,需要检查一个英语单词是否拼写正确 6 | * 在 FBI,一个嫌疑人的名字是否已经在嫌疑名单上 7 | * 在网络爬虫里,一个网址是否被访问过 8 | * yahoo, gmail等邮箱垃圾邮件过滤功能 9 | 10 | 联想到的解决方案 11 | * 数组 12 | * 链表 13 | * 树、平衡二叉树、Trie 14 | * Map (红黑树) 15 | * 哈希表 16 | 17 | 对于这些方案 BloomFilter 具有时间和空间上的优势 18 | 19 | 20 | ## 原理 21 | 22 | 布隆过滤器(Bloom Filter)的核心实现是一个超大的位数组和几个哈希函数。假设位数组的长度为m,哈希函数的个数为k 23 | 24 | ![BloomFilter](../assets/img/bloomfilter.webp) 25 | 26 | 假设集合里面有3个元素{x, y, z},哈希函数的个数为3。首先将位数组进行初始化,将里面每个位都设置位0。对于集合里面的每一个元素,将元素依次通过3个哈希函数进行映射,每次映射都会产生一个哈希值,这个值对应位数组上面的一个点,然后将位数组对应的位置标记为1。查询W元素是否存在集合中的时候,同样的方法将W通过哈希映射到位数组上的3个点。如果3个点的其中有一个点不为1,则可以判断该元素一定不存在集合中。反之,如果3个点都为1,则该元素可能存在集合中。注意:此处不能判断该元素是否一定存在集合中,可能存在一定的误判率。可以从图中可以看到:假设某个元素通过映射对应下标为4,5,6这3个点。虽然这3个点都为1,但是很明显这3个点是不同元素经过哈希得到的位置,因此这种情况说明元素虽然不在集合中,也可能对应的都是1,这是误判率存在的原因。 27 | 28 | ## 实现 29 | 30 | ### 基础实现 31 | 对于程序员来说概念不好理解,代码更好理解。下面就是基础的实现方式,目的就是更好的理解实现原理。 32 | 33 | ```java 34 | import java.util.BitSet; 35 | 36 | public class SimpleBloomFilter { 37 | 38 | private static final int DEFAULT_SIZE = 2 << 24; 39 | private static final int[] seeds = new int[] {7, 11, 13, 31, 37, 61,}; 40 | 41 | private BitSet bits = new BitSet(DEFAULT_SIZE); 42 | private SimpleHash[] func = new SimpleHash[seeds.length]; 43 | 44 | public static void main(String[] args) { 45 | String value = "Hello World"; 46 | SimpleBloomFilter filter = new SimpleBloomFilter(); 47 | System.out.println(filter.contains(value)); 48 | filter.add(value); 49 | System.out.println(filter.contains(value)); 50 | } 51 | 52 | public SimpleBloomFilter() { 53 | for (int i = 0; i < seeds.length; i++) { 54 | func[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]); 55 | } 56 | } 57 | 58 | public void add(String value) { 59 | for (SimpleHash f : func) { 60 | bits.set(f.hash(value), true); 61 | } 62 | } 63 | 64 | public boolean contains(String value) { 65 | if (value == null) { 66 | return false; 67 | } 68 | boolean ret = true; 69 | for (SimpleHash f : func) { 70 | ret = ret && bits.get(f.hash(value)); 71 | } 72 | return ret; 73 | } 74 | 75 | public static class SimpleHash { 76 | 77 | private int cap; 78 | private int seed; 79 | 80 | public SimpleHash(int cap, int seed) { 81 | this.cap = cap; 82 | this.seed = seed; 83 | } 84 | 85 | public int hash(String value) { 86 | int result = 0; 87 | int len = value.length(); 88 | for (int i = 0; i < len; i++) { 89 | result = seed * result + value.charAt(i); 90 | } 91 | return (cap - 1) & result; 92 | } 93 | 94 | } 95 | } 96 | ``` 97 | 98 | ### 生产实现 99 | * [Guava](https://mvnrepository.com/artifact/com.google.guava/guava) com.google.common.hash.BloomFilter 100 | * [BloomFilter.java](../assets/code/BloomFilter.java) [usedby](https://github.com/MagnusS/Java-BloomFilter/blob/master/src/com/skjegstad/utils/BloomFilter.java) 101 | -------------------------------------------------------------------------------- /algorithm/算法学习.md: -------------------------------------------------------------------------------- 1 | 算法学习 algorithm 2 | ------ 3 | 记录常用解决方案算法 4 | 收藏一下算法的使用案例 5 | 6 | ## 基础 7 | [All Algorithms implemented in Java 8 | ](https://github.com/TheAlgorithms/Java) 9 | ![GitHub stars](https://img.shields.io/github/stars/TheAlgorithms/Java?style=flat-square) 10 | 11 | 12 | ## 寻路算法 13 | * A\*(A-Star) [A\* Pathfinding for Beginners](https://www.gamedev.net/articles/programming/artificial-intelligence/a-pathfinding-for-beginners-r2003/) 14 | [fast-astar](https://github.com/sbfkcel/fast-astar) ![GitHub stars](https://img.shields.io/github/stars/sbfkcel/fast-astar?style=flat-square) 15 | 16 | ## 搜索 17 | * DFS 深度优先搜索 18 | * BFS 宽度/广度优先搜索 19 | 20 | ## 协同编辑 21 | * OT (Operational Transformation) 22 | * [1](https://blog.csdn.net/pheecian10/article/details/78496854) 23 | * [2](https://yafeilee.com/blogs/100) 24 | -------------------------------------------------------------------------------- /assets/code/cloudflare-workers-kv.js: -------------------------------------------------------------------------------- 1 | /** 2 | cloudflare workers and workers-kv 3 | */ 4 | 5 | 6 | const html = todos => ` 7 | 8 | 9 | 10 | 11 | 12 | Todos 13 | 14 | 15 | 16 |
17 |
18 |

Todos

19 |
20 | 21 | 22 |
23 |
24 |
25 |
26 | 27 | 73 | 74 | ` 75 | 76 | const defaultData = { todos: [] } 77 | 78 | const setCache = (key, data) => KRISTIAN_TODOS.put(key, data) 79 | const getCache = key => KRISTIAN_TODOS.get(key) 80 | 81 | async function getTodos(request) { 82 | const ip = request.headers.get('CF-Connecting-IP') 83 | const cacheKey = `data-${ip}` 84 | let data 85 | const cache = await getCache(cacheKey) 86 | if (!cache) { 87 | await setCache(cacheKey, JSON.stringify(defaultData)) 88 | data = defaultData 89 | } else { 90 | data = JSON.parse(cache) 91 | } 92 | const body = html(JSON.stringify(data.todos || [])) 93 | return new Response(body, { 94 | headers: { 'Content-Type': 'text/html' }, 95 | }) 96 | } 97 | 98 | async function updateTodos(request) { 99 | const body = await request.text() 100 | const ip = request.headers.get('CF-Connecting-IP') 101 | const cacheKey = `data-${ip}` 102 | try { 103 | JSON.parse(body) 104 | await setCache(cacheKey, body) 105 | return new Response(body, { status: 200 }) 106 | } catch (err) { 107 | return new Response(err, { status: 500 }) 108 | } 109 | } 110 | 111 | async function handleRequest(request) { 112 | if (request.method === 'PUT') { 113 | return updateTodos(request) 114 | } else { 115 | return getTodos(request) 116 | } 117 | } 118 | 119 | addEventListener('fetch', event => { 120 | event.respondWith(handleRequest(event.request)) 121 | }) 122 | -------------------------------------------------------------------------------- /assets/img/DevOps-BD-FLINK-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/DevOps-BD-FLINK-1.png -------------------------------------------------------------------------------- /assets/img/ETL-ELT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/ETL-ELT.png -------------------------------------------------------------------------------- /assets/img/Federated-Learning-classify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/Federated-Learning-classify.png -------------------------------------------------------------------------------- /assets/img/Federated-Learning-opensource.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/Federated-Learning-opensource.png -------------------------------------------------------------------------------- /assets/img/HTLC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/HTLC.png -------------------------------------------------------------------------------- /assets/img/HTTPS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/HTTPS.png -------------------------------------------------------------------------------- /assets/img/JAVA-BASE-LOCK-INDEX.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/JAVA-BASE-LOCK-INDEX.png -------------------------------------------------------------------------------- /assets/img/JAVA-BASE-TABLE-1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/JAVA-BASE-TABLE-1.bmp -------------------------------------------------------------------------------- /assets/img/JAVA-BASE-TABLE-2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/JAVA-BASE-TABLE-2.bmp -------------------------------------------------------------------------------- /assets/img/JAVA-BASE-TABLE-3.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/JAVA-BASE-TABLE-3.bmp -------------------------------------------------------------------------------- /assets/img/JAVA-BASE-TABLE-MAP-PUT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/JAVA-BASE-TABLE-MAP-PUT.png -------------------------------------------------------------------------------- /assets/img/JAVA-JVM-GC-heap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/JAVA-JVM-GC-heap.png -------------------------------------------------------------------------------- /assets/img/MPT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/MPT.png -------------------------------------------------------------------------------- /assets/img/PBFT-3stage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/PBFT-3stage.png -------------------------------------------------------------------------------- /assets/img/SAML.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/SAML.png -------------------------------------------------------------------------------- /assets/img/atlas-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/atlas-architecture.png -------------------------------------------------------------------------------- /assets/img/bc-merkle-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bc-merkle-tree.png -------------------------------------------------------------------------------- /assets/img/big-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/big-data.png -------------------------------------------------------------------------------- /assets/img/bigdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bigdata.png -------------------------------------------------------------------------------- /assets/img/bitcoin-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bitcoin-1.png -------------------------------------------------------------------------------- /assets/img/bitcoin-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bitcoin-2.png -------------------------------------------------------------------------------- /assets/img/bitcoin-disk-space.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bitcoin-disk-space.png -------------------------------------------------------------------------------- /assets/img/bitcoin-pow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bitcoin-pow.png -------------------------------------------------------------------------------- /assets/img/bitcoin-privacy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bitcoin-privacy.png -------------------------------------------------------------------------------- /assets/img/bitcoin-simplified-payment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bitcoin-simplified-payment.png -------------------------------------------------------------------------------- /assets/img/bitcoin-transaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bitcoin-transaction.png -------------------------------------------------------------------------------- /assets/img/bloomfilter.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/bloomfilter.webp -------------------------------------------------------------------------------- /assets/img/contract-oracle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/contract-oracle.png -------------------------------------------------------------------------------- /assets/img/design-birdge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/design-birdge.png -------------------------------------------------------------------------------- /assets/img/distributed-transaction-2-phase-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/distributed-transaction-2-phase-1.png -------------------------------------------------------------------------------- /assets/img/distributed-transaction-2-phase-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/distributed-transaction-2-phase-2.png -------------------------------------------------------------------------------- /assets/img/distributed-transaction-3-phase-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/distributed-transaction-3-phase-1.png -------------------------------------------------------------------------------- /assets/img/distributed-transaction-3-phase-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/distributed-transaction-3-phase-2.png -------------------------------------------------------------------------------- /assets/img/distributed-transaction-3-phase-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/distributed-transaction-3-phase-3.png -------------------------------------------------------------------------------- /assets/img/git.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/git.png -------------------------------------------------------------------------------- /assets/img/javabean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/javabean.png -------------------------------------------------------------------------------- /assets/img/kubernetes-ecosystem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/kubernetes-ecosystem.png -------------------------------------------------------------------------------- /assets/img/merkle_tree_bitcoin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/merkle_tree_bitcoin.png -------------------------------------------------------------------------------- /assets/img/mysql-explain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/mysql-explain.png -------------------------------------------------------------------------------- /assets/img/paxos-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/paxos-flow.png -------------------------------------------------------------------------------- /assets/img/pbft_process.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/pbft_process.png -------------------------------------------------------------------------------- /assets/img/pbft_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/pbft_view.png -------------------------------------------------------------------------------- /assets/img/rpc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/rpc.jpg -------------------------------------------------------------------------------- /assets/img/springinit.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/springinit.webp -------------------------------------------------------------------------------- /assets/img/解一元二次.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/assets/img/解一元二次.png -------------------------------------------------------------------------------- /blockchain/区块链与随机数.md: -------------------------------------------------------------------------------- 1 | 区块链与随机数 2 | -------- 3 | ## 计算机的随机数 4 | 理论上计算机是无法真正生成随机数的,给它相同的输入,它都应该是同样的输出。 5 | 为了生成随机数(Random numbers),计算机将依赖于 seed (种子):用于获取输出的输入 (input)。 6 | 种子可以是: 7 | **屏幕上鼠标光标的移动的像素位置** (在500万像素屏幕中,需要重复完全一样的鼠标运动轨迹,这种任务对于人类完成度几乎是不可能的); 8 | 大气的噪音、宇宙背景辐射、放射物的衰变期、量子塌缩现象熔岩灯。 9 | 在种子之上,计算机将执行数学运算并最终得出一个人类无法预测的数字。 10 | 11 | > [BitAddress](https://www.bitaddress.org/) 一个开源的比特币钱包工具,他的 seed 值就是采集鼠标轨迹来完成 12 | > [random.org](https://www.random.org/)大气噪音生成随机数,他用录音设备获得大气中的声波,再检测其细微变化作为生成随机数的熵源 13 | > [Cloudflare](https://gizmodo.com/one-of-the-secrets-guarding-the-secure-internet-is-a-wa-1820188866) 内部不定形状的蜡滴的缓慢流动。[Cloudflare](https://www.cloudflare.com/zh-cn/learning/ssl/lava-lamp-encryption/) 利用摄像头监视熔岩灯,拍摄视频和照片,然后将其转变成无法预测的随机比特流,再用它生成密钥 14 | 15 | 区块链中没有鼠标、传感器或者运动比赛结果预测。更重要的是,即使一个节点到达某个随机数,但该随机数必须与区块链的所有其他节点的随机数相同(必须产生具有一致性的随机性),不然无法验证。 16 | 17 | ## 区块链中的随机数 18 | > 区块链中随机数的应用场景: 19 | > * 公平的决定出块权力,维持一致性共识。部分PoW与PoS机制下,依靠随机数选定出块者或者出块组的,包括DPoS机制下的循环出块的顺序,也是依靠随机数决定 20 | > * 私钥的生成。目前私钥只要由各钱包自定随机数方法生成,存在较大安全隐患 21 | > * 链上应用的随机数源。保证博彩、游戏、抽奖、分发、调查等应用的公平公正,此类容易被黑客攻击 22 | > * 数据加密。链上数据因为是公开审查的,需要强度较高的加密,通过足够强的随机数确保链上数据的隐私与安全 23 | ### 区块哈希 24 | 依赖区块哈希 (block hash) 来处理随机性。因为哈希值是未知的、随机的,但在所有节点上都是相同的,所以对于基本可证明公平的赌博来说,哈希值可以作为随机性的来源。 25 | 26 | 但是如果操作区块 hash 的利益大于旷工挖到区块后的奖励,那么旷工完全就有动机进行恶意行为。更严重的是,在权益证明 (PoS) 系统中,由于生成一个区块几乎不需要计算时间或能量,矿工 (验证者) 可以很容易地继续生成数千个区块,直到获得一个他们喜欢的哈希值,并将这个哈希值进行提交。 27 | 这也适用于验证者的选择。如果当前负责生产某个区块的验证者能够通过某种方式进行操纵,使得该区块哈希成为一个能够再次选中该验证者 (或者由该验证控制的另一个验证者客户端) 的种子,使该验证者继续成为提议区块的验证者,那么该验证者就可以一直提议区块,将其他的验证者挡在主要的利润之外! 28 | 29 | ### 可验证随机函数 VRF 30 | Verifiable Random Function 31 | > 生成一对公私钥,分别为pri_key和pub_key 32 | > 计算hash_value=VRF_HASH(pri_key, message) 33 | > 计算proof_value=VRF_PROOF(pri_key, message) 34 | > 现在将hash_value和proof_value提供给验证者 35 | > 验证者拿到hash_value和proof_value计算hash_value=VRF_P2H(proof_value) 36 | > 如果成功,则验证者计算VRF_VERIFY(pub_key, message, proof) 37 | > 如果返回true则表示验证通过,否则验证失败 38 | 39 | 就是在不暴露私钥的情况下,验证hash_value和message是否匹配 40 | 41 | #### 案例分析 42 | * [可信预言机Truora VRF](https://mp.weixin.qq.com/s/6v-PCIEpspfUX8sqS6mq6w) 43 | * [可信预言机Truora](https://mp.weixin.qq.com/s/5ZYKv0BBX1j18H6AAePZaQ) 44 | * [Truora](https://truora.readthedocs.io/) 45 | * [Truora Github](https://github.com/WeBankBlockchain/Truora-Service) 46 | 47 | ### ETH2.0 方案 RANDAO + VDF 48 | 49 | **RANDAO** 是一个基于以太坊的项目,可以理解是一个智能合约,简单理解一下:当用户通过储存质押(m个ETH)成为验证者之,该用户将选择一个由自己选择的随机数。当需要为某个区块公布其随机数时,将系统中所有随机数加起来就可以得到一个全新的随机数。 50 | 但即使在这种情况下,最后公布的数字对最终的随机数也会有一定程度的影响。房间里的最后一个人会记住之前每个人公布的数字,如此一来,就可以知道加上 (或者不加上) 他提供的数字之后的最终随机数的结果。 51 | 这里只是快速了解`randao`项目,他其中还包含有经济惩罚等的各种措施保证,更具体的设计可以找白皮书。 52 | 53 | **VDF** (可验证延迟函数) Verifiable Delay Function 54 | 它具有几个特性: 55 | * 串行性,抗矿池,任务不可分配。矿池将任务合理的分配给每个加入矿池的旷工,以达到并行计算的能力,大大提高解题速度。而VDF,如果要计算出第三级结果,那就必须先计算出第一级和第二级的结果,无法在多台计算机上并行地进行此计算,因为每个新的输入都依赖于之前的输出。 56 | * 计算难度高,结果有延迟,预先确定的计算时间。每个输出都需要预先确定的计算时间。以太坊2.0阶段,这个 VDF 被定义为102分钟,通过引入这种延迟,并使计算的时间要长于验证者可以通过影响某个随机数而获得利益的时间,我们就可以消除最后一级的随机性偏差。 57 | * 验证简单。VDF的运行过程可以是困难且耗时的,但是检验结果时是足够快的 58 | 59 | ### 其他随机数项目 60 | * Algorand 61 | * Cardano 62 | * Dfinity 63 | -------------------------------------------------------------------------------- /blockchain/区块链设计.md: -------------------------------------------------------------------------------- 1 | ## P2P网络 2 | **早期的P2P网络**,P2P 网络需要解决的一个最重要的问题就是, 如何知道用户请求的资源位于哪个节点上. 在第一代 P2P 网络中, 人们设置了一台中央服务器来管理资源所处的位置. 当一个用户想要发布资源, 他需要告诉中央服务器它发布的资源信息和自己的节点信息; 当其他用户请求资源的时候, 需要先请求中央服务器以获取资源发布者的节点信息, 再向资源发布者请求资源. 3 | 这种 P2P 网络的好处是效率高, 只需要请求一次中央服务器就可以发布或获取资源. 然而它的缺点也很明显: 中央服务器是这个网络系统最脆弱的地方, 它需要存储所有资源的信息, 处理所有节点的请求; 一旦中央服务器失效, 整个网络就无法使用. 4 | 另外一种 P2P 网络采取了不同的策略, 它不设置中央服务器; 当用户请求资源时, 它会请求它所有的邻接节点, 邻接节点再依次请求各自的邻接节点, 并使用一些策略防止重复请求, 直到找到拥有资源的节点. 也就是说, 这是一种泛洪搜索(Flooding Search). 5 | 这种 P2P 网络去除了中央服务器, 它的稳定性就强多了. 然而它太慢了. 一次查找可能会产生大量的请求, 可能会有大量的节点卷入其中. 一旦整个系统中的的节点过多, 性能就会变得很差. 6 | 7 | ### 分布式哈希表 8 | DHT(Distributed Hash Table) 9 | 全网维护一个巨大的文件索引哈希表,这个哈希表的条目形如(key,value)。这里key通常是文件的哈希值,而value则是存储文件的IP地址。根据key就能找到存储到节点地址并返回给查询节点。这个表是按照一定规则分割存储到全网各个节点上。 10 | 11 | * Kademlia 12 | * Chord 13 | 14 | 15 | ## 共识算法 16 | * PBFT(Practical Byzantine Fault Tolerance) 拜占庭容错技术 17 | > 应用代表: 联盟链. fabric; fisco-bcos; 18 | * POW(Proof of Work) 工作量证明 19 | > Bitcoin(比特币); Ethereum(以太访) 20 | * POS(Proof of Stake) 股权证明 21 | > Peercoin(点点币); 日后以太坊逐渐替换使用 22 | * DPOS(Delegated Proof of Stake) 委任权益证明 23 | > EOS; Bitshare(比特股) 24 | * Paxos Raft 25 | * Ripple 瑞波 26 | 27 | ### PBFT 28 | > f: 无效或恶意节点数 29 | > R: 总节点数 30 | > s: 正常节点数 31 | > v: 系统的view编号,每次选举是触发 view change ,view number++ 32 | 33 | 前提 f <= (R-1)/3 或 s >= 2f+1 或 R = 3f + 1 34 | 通俗讲就是可以容忍**小于1/3的节点无效或恶意节点** 35 | 36 | #### leader 选举 37 | ``` 38 | leader_idx = (block_number + current_view) % consensus_node_num 39 | ``` 40 | > 前三轮共识: node0、node1、node2为leader,且非恶意节点数目等于2*f+1,节点正常出块共识; 41 | 第四轮共识:node3为leader,但node3为拜占庭节点,node0-node2在给定时间内未收到node3打包的区块,触发视图切换,试图切换到view_new=view+1的新视图,并相互间广播viewchange包,节点收集满在视图view_new上的(2*f+1)个viewchange包后,将自己的view切换为view_new,并计算出新leader; 42 | 为第五轮共识:node0为leader,继续打包出块。 43 | 44 | ![view_change](../assets/img/pbft_view.png) 45 | 46 | 47 | 采用PBFT共识算法的系统,节点规模在百级左右,再增加就会导致TPS下降,确认时延增加。目前业界有通过随机数算法选择记账组的共识机制,可以改善这个问题。 48 | > RPBFT(rotating PBFT)就是其中的一个解决方案目的是将共识算法网络复杂度与共识节点规模解耦,提升区块链系统的可扩展性。在大节点规模下,随机选取部分节点作为“共识委员节点”参与每轮PBFT共识,由于共识委员节点数目固定、与节点规模无关,因此RPBFT共识算法可扩展性更强。 49 | epoch_sealer_num:每轮共识过程中参与共识的节点数目。 50 | epoch_block_num: 共识节点替换周期,为防止选取的共识节点联合作恶,RPBFT每出epoch_block_num个区块,会替换若干个共识委员节点。 51 | 引入VRF可验证随机数算法来实现私密、随机、非交互式的共识委员节点选取方法。 52 | 53 | #### 角色 54 | * replica 副本节点,备份节点,共识节点,所有参与的节点,接收并检查主节点的排序信息,如果主节点作恶可以进行换选。总数:R 55 | * primary 主节点,共识节点,负责将client的请求排序,发给所有的备份节点。负责将交易打包成区块和区块共识,每轮共识过程中有且仅有一个leader,为了防止leader伪造区块,每轮PBFT共识后,均会切换leader。 56 | * Observer 观察者节点,负责从共识节点或副本节点获取最新区块,执行并验证区块执行结果后,将产生的区块上链。 57 | > 主节点选举:p = v 求余 R 58 | 59 | #### 共识流程 60 | PBFT共识主要包括Pre-prepare、Prepare和Commit三个阶段。 61 | ![pbft process](../assets/img/pbft_process.png) 62 | 63 | ![pbft 共识阶段](../assets/img/PBFT-3stage.png) 64 | 65 | PBFT共识算法中,共识节点轮流出块,每一轮共识仅有一个leader打包区块。节点计算当前leader索引与自己索引相同后,就开始 66 | ##### 打包区块 生成Prepare 67 | > **产生新的空块**: 通过区块链(BlockChain)获取当前最高块,并基于最高块产生新空块(将新区块父哈希置为最高块哈希,时间戳置为当前时间,交易清空); 68 | **从交易池打包交易**: 产生新空块后,从交易池中获取交易,并将获取的交易插入到产生的新区块中; 69 | **组装新区块**: Sealer线程打包到交易后,将新区块的打包者(Sealer字段)置为自己索引,并根据打包的交易计算出所有交易的transactionRoot; 70 | **产生Prepare包**: 将组装的新区块编码到Prepare包内,广播给组内所有共识节点,其他共识节点收到Prepare包后,开始进行三阶段共识。 71 | 72 | ##### Pre-prepare 73 | 共识节点收到Prepare包后,进入pre-prepare阶段,此阶段的主要工作流程包括: 74 | > **Prepare包合法性判断**:主要判断是否是重复的Prepare包、Prepare请求中包含的区块父哈希是否是当前节点最高块哈希(防止分叉)、Prepare请求中包含区块的块高是否等于最高块高加一; 75 | **缓存合法的Prepare包**: 若Prepare请求合法,则将其缓存到本地,用于过滤重复的Prepare请求; 76 | **空块判断**:若Prepare请求包含的区块中交易数目是0,则触发空块视图切换,将当前视图加一,并向所有其他节点广播视图切换请求; 77 | **执行区块并缓存区块执行结果**: 若Prepare请求包含的区块中交易数目大于0,则调用BlockVerifier区块执行器执行区块,并缓存执行后的区块; 78 | **产生并广播签名包**:基于执行后的区块哈希,产生并广播签名包,表明本节点已经完成区块执行和验证。 79 | 80 | ##### Prepare 81 | 共识节点收到签名包后,进入Prepare阶段,此阶段的主要工作流程包括: 82 | 83 | > **签名包合法性判断**:主要判断签名包的哈希与pre-prepare阶段缓存的执行后的区块哈希相同,若不相同,则继续判断该请求是否属于未来块签名请求(产生未来块的原因是本节点处理性能低于其他节点,还在进行上一轮共识,判断未来块的条件是:签名包的height字段大于本地最高块高加一),若请求也非未来块,则是非法的签名请求,节点直接拒绝该签名请求; 84 | **缓存合法的签名包**:节点会缓存合法的签名包; 85 | **判断pre-prepare阶段缓存的区块对应的签名包缓存是否达到2*f+1,若收集满签名包,广播Commit包**:若pre-prepare阶段缓存的区块哈希对应的签名包数目超过2*f+1,则说明大多数节点均执行了该区块,并且执行结果一致,说明本节点已经达到可以提交区块的状态,开始广播Commit包; 86 | **若收集满签名包,备份pre-prepare阶段缓存的Prepare包落盘**:为了防止Commit阶段区块未提交到数据库之前超过2*f+1个节点宕机,这些节点启动后重新出块,导致区块链分叉(剩余的节点最新区块与这些节点最高区块不同),还需要备份pre-prepare阶段缓存的Prepare包到数据库,节点重启后,优先处理备份的Prepare包。 87 | 88 | ##### Commit 89 | 共识节点收到Commit包后,进入Commit阶段,此阶段工作流程包括: 90 | 91 | > **Commit包合法性判断**:主要判断Commit包的哈希与pre-prepare阶段缓存的执行后的区块哈希相同,若不相同,则继续判断该请求是否属于未来块Commit请求(产生未来块的原因是本节点处理性能低于其他节点,还在进行上一轮共识,判断未来块的条件是:Commit的height字段大于本地最高块高加一),若请求也非未来块,则是非法的Commit请求,节点直接拒绝该请求; 92 | **缓存合法的Commit包**:节点缓存合法的Commit包; 93 | **判断pre-prepare阶段缓存的区块对应的Commit包缓存是否达到2*f+1,若收集满Commit包,则将新区块落盘**:若pre-prepare阶段缓存的区块哈希对应的Commit请求数目超过2*f+1,则说明大多数节点达到了可提交该区块状态,且执行结果一致,则调用BlockChain模块将pre-prepare阶段缓存的区块写入数据库; 94 | 95 | ##### ViewChange 96 | 当PBFT三阶段共识超时或节点收到空块时,PBFTe会试图切换到更高的视图(将要切换到的视图toView加一),并触发ViewChange处理流程;节点收到ViewChange包时,也会触发ViewChange处理流程: 97 | 98 | > **判断ViewChange包是否有效**: 有效的ViewChange请求中带有的块高值必须不小于当前节点最高块高,视图必须大于当前节点视图; 99 | **缓存有效ViewChange包**: 防止相同的ViewChange请求被处理多次,也作为判断节点是否可以切换视图的统计依据; 100 | **收集ViewChange包**:若收到的ViewChange包中附带的view等于本节点的即将切换到的视图toView且本节点收集满2*f+1来自不同节点view等于toView的ViewChange包,则说明超过三分之二的节点要切换到toView视图,切换当前视图到toView。 101 | 102 | 103 | 104 | ## 轻节点 105 | 106 | ### 默克尔树 Merkle tree 107 | > 在比特币的白皮书中提到了轻节点也就是简易支付验证(Simplified Payment Verification,SPV),它正是基于默克尔可信树利用区块中的所有交易 hash 生成的一个默克尔根节点,保存至区块头,可用于验证交易是否打包到某区块中。 108 | 109 | ![](../assets/img/bc-merkle-tree.png) 110 | 111 | 每个块都会有一个Merkle树,它从叶子节点(树的底部)开始,一个叶子节点就是一个交易哈希,叶子节点的数量必须是双数(如果区块里交易数是单数,就复制最后一份凑成双数)。从下往上,两两成对,连接两个节点哈希,将组合哈希作为新的哈希。新的哈希就成为新的树节点。重复该过程,直到仅有一个节点,也就是树根。根哈希然后就会当做是整个块交易的唯一标示,将它保存到区块头,然后用于工作量证明。 112 | 113 | ![img](../assets/img/merkle_tree_bitcoin.png) 114 | 左:仅提供默克尔树(Merkle tree)上的少量节点已经足够给出分支的合法证明。 115 | 右:任何对于默克尔树的任何部分进行改变的尝试都会最终导致链上某处的不一致。 116 | 117 | Merkle树的好处就是一个节点可以在不下载整个块的情况下,验证是否包含某笔交易。并且这些只需要一个交易哈希,一个Merkle树根哈希和一个Merkle路径 118 | 119 | ### MPT(Merkle Patricia Tree) 120 | 梅克尔帕特里夏树 = 默克尔树 + 压缩前缀树(Patricia trie) 121 | ![img](../assets/img/MPT.png) 122 | 123 | 我们将如下数据插入MPT中 124 | 125 | |key | values| 126 | |-------|-----| 127 | |a711355 | 45.0 ETH| 128 | |a77d337 | 1.00 WEI| 129 | |a7f9365 | 1.1 ETH| 130 | |a77d397 | 0.12 ETH| 131 | 132 | 插入第一个,由于只有一个key,直接用leaf node既可表示 133 | 134 | 接着插入a77d337,由于和a711355共享前缀’a7’,因而可以创建’a7’扩展节点。 135 | 136 | 接着插入a7f9365,也是共享’a7’,只需新增一个leaf node. 137 | 138 | 最后插入a77d397,这个key和a77d337共享’a7’+’d3’,因而再需要创建一个’d3’扩展节点 139 | 140 | 将叶子节点和最后的short node合并到一个节点了,事实上源码实现需要再深一层,最后一层的叶子节点只有数据 141 | 142 | ## 加密算法 143 | - 大数分解困难问题:对应RSA系列算法等。 144 | - 离散对数困难问题:对应Diffie-Hellman密钥交换协议、ElGamal加密、DSA算法等。 145 | - 椭圆曲线上的离散对数困难问题:对应ECDSA、EdDSA、国密SM2等。 146 | 147 | 密钥长度,美国国家标准与技术研究院NIST作如下推荐。 148 | 基于各大类的公钥加密算法,单位(比特、位) 149 | 150 | |安全参数等级|大数分解|离散对数|椭圆曲线上的离散对数| 151 | |-----|------|------|------| 152 | |1|1024|1024|160| 153 | |2|2048|2048|224| 154 | |3|3072|3072|256| 155 | |4|7680|7680|384| 156 | |5|15360|15360|512| 157 | 158 | 159 | > 1. 一个隐私保护技术方案,安全性取决于其使用的密码学算法实现中最低的安全参数等级。 160 | 2. 在未指明安全参数的前提下,进行密码学算法的安全性比较没有实际意义。 161 | 3. 如果安全参数值很小,一般表现为对应的密钥长度很短时,无论密码学算法设计多么精妙,实际效果可能都是不安全的。 162 | 4. 由于困难问题选用上的差异,密码学算法的理论强度没有最强,只有在满足特定安全假设下的够强,强行比较基于不同困难问题的密码学算法是否有效通常没有实际意义。 163 | 164 | > 计算困难问题归根结底还是一个计算问题,随着计算机计算能力的增强,或是算法理论研究进展的推进,这些困难问题的安全性可能就会发生变化。 165 | 比如RSA加密算法,NIST密钥管理准则认为,2010年后,1024位的密钥不再安全,需要增加到2048位的密钥长度,预计其安全有效性可以保持至2030年。 166 | 对于企业而言,这里的启示在于,不能简单地认为,隐私保护技术方案现在有效,就保证了10年后依旧有效。 167 | 168 | 169 | ## 跨链 170 | * 公证人机制 171 | * 侧链/中继 172 | * 哈希锁定 173 | 174 | > [WeCross](https://github.com/WeBankFinTech/WeCross) 175 | 176 | ### 跨链应用场景 177 | * 资产交换 178 | * 原子交易 179 | * 预言机 180 | * 信息互通 181 | -------------------------------------------------------------------------------- /blockchain/智能合约-solidity.md: -------------------------------------------------------------------------------- 1 | Solidity 2 | ----------- 3 | [Solidity](https://solidity.readthedocs.io/) 是一门面向合约的、为实现智能合约而创建的高级编程语言,运作在以太坊虚拟机(EVM)上。 4 | 5 | [官方文档](https://solidity.readthedocs.io/) 6 | [非官方中文文档](https://learnblockchain.cn/docs/solidity/) 7 | [Openzeppelin合约类库](https://github.com/OpenZeppelin/openzeppelin-contracts) 8 | 9 | > 以太坊虚拟机(EVM)是一个 “The World Machine” 所以请原谅它简陋的语法特性、令人抓狂的debug体验、近乎贫瘠的类库支持、动不动就需要插入汇编语句来解决一下问题。Solidity 语言在这种环境下必须严格遵循的设计原则以及权衡后必须付出的代价。 10 | 11 | ## ABI 12 | Application Binary Interface(ABI) 13 | 14 | ## 存储 15 | **栈** 用于存储字节码指令的操作数。在Solidity中,局部变量若是整型、定长字节数组等类型,就会随着指令的运行入栈、出栈。对于这类变量,无法强行改变它们的存储方式,如果在它们之前放置memory修饰符,编译会报错。 16 | **内存** 类似java中的堆,它用于储存"对象"。在Solidity编程中,如果一个局部变量属于变长字节数组、字符串、结构体等类型,其通常会被memory修饰符修饰,以表明存储在内存中。 17 | **状态存储** 用于存储合约的状态字段。从实现而言,不同的链可能采用不同实现,比较经典的是以太坊所采用的MPT树。由于MPT树性能、扩展性等问题,FISCO BCOS放弃了这一结构,而采用了分布式存储,通过rocksdb或mysql来存储状态数据,使存储的性能、可扩展性得到提高。 18 | ```javascript 19 | contract Demo{ 20 | //状态存储 21 | uint private _state; 22 | 23 | function set(uint state) public { 24 | //栈存储 25 | uint i = 0; 26 | //内存存储 27 | string memory str = "aaa"; 28 | } 29 | } 30 | ``` 31 | 32 | ## Event/Log 33 | 34 | ```javascript 35 | 36 | pragma solidity >=0.4.21 <0.7.0; 37 | contract Demo { 38 | // 定义事件 39 | event Transfer(address indexed from, address indexed to, uint256 value); 40 | 41 | function bid() public payable { 42 | //触发事件 43 | emit Transfer(msg.sender, _to, _value); 44 | } 45 | } 46 | ``` 47 | 48 | 最多三个参数可以使用 `indexed` 属性 49 | * 这些参数会使用 `keccak-256` 哈希后得到值被作为 `topic` 保存在区块的 Event 里 50 | * API调用时可以使用这些 `indexed` 参数的特定值来进行过滤 51 | ```javascript 52 | var event = demoContract.Transfer({from: ["xxxx","xxxx","xxxxx"]}); 53 | ``` 54 | 55 | 高效使用: 56 | * 异步获取执行结果,并可提供过滤器,支持参数的检索和过滤。 57 | * 提供一种回调机制,在事件执行成功后,由节点向注册监听的SDK发送回调通知,触发回调函数被执行。Oracle 的原理 58 | * 存储合约数据,巧用日志去存储数据,可以大大减少交易费用 59 | > storage存储的大概价格为:每32字节需要消耗20000Gas,而日志存储价格大概为每字节8Gas 60 | 61 | 62 | ## 修饰符 63 | 64 | **修饰函数** 65 | * `pure` - 不允许修改和访问状态 66 | * `view` - 不允许修改状态 67 | * `payable` - 允许调用接收 以太币 68 | 69 | **修饰函数和变量** 70 | 71 | * `constant` 72 | 73 | 函数:与`view`等价。 74 | 变量:不允许赋值(初始化除外),它不会占据 `storage slot` 75 | 76 | * `external` - 可以从其他合约和交易中调用,不可内部调用,在接收大量数据时更为高效。 77 | 78 | 当函数的某个参数非常大时,如果显式地将函数标记为external,可以强制将函数存储的位置设置为 `calldata`,这会节约函数执行时所需存储或计算资源。 79 | 80 | * `public` - 可以在内部或通过消息调用。没指定默认为 public 81 | 82 | 变量:会自动生成一个 `getter` 函数 83 | 84 | * `internal` - 只能是内部访问 85 | 86 | 变量:状态变量的修饰符默认是`internal` 87 | 88 | * `private` - 仅在当前定义它们的合约中使用,并且不能被派生合约使用 89 | 90 | **修饰事件参数** 91 | 92 | * `indexed` - 可作为索引 93 | * `anonymous` - 事件签名的哈希值不会在 `topic` 中存储 94 | 95 | **修饰器** 96 | * `modifier` - [修饰器](https://solidity-cn.readthedocs.io/zh/develop/contracts.html#modifier) 97 | 98 | 有点面向切面编程的感觉。 99 | ```javascript 100 | event LogStartMethod(); 101 | event LogEndMethod(); 102 | modifier logMethod { 103 | emit LogStartMethod(); 104 | _;//表示所修饰函数中的代码,也就是在函数的最前与最后插入代码 105 | emit LogEndMethod(); 106 | } 107 | function doing(address _owner) public logMethod { 108 | // something... 109 | } 110 | ``` 111 | 112 | ## 抽象类与接口 113 | [docs](https://solidity-cn.readthedocs.io/zh/develop/contracts.html#index-17) 与其他语言类似。但需要注意合适地使用接口或抽象合约有助于增强合约设计的可扩展性。但是,由于区块链EVM上计算和存储资源的限制,切忌过度设计,这也是从高级语言技术栈转到Solidity开发的老司机常常会陷入的天坑。 114 | 115 | 116 | ## 常见问题 117 | * 一个合约中,入参、返回值、内部变量不能超过了16个 118 | ```javascript 119 | Compiler error: Stack too deep, try removing local variables. 120 | ``` 121 | EVM所设计用于最大的栈深度为16。所有的计算都在一个栈内执行,对栈的访问只限于其顶端,限制方式为:允许拷贝最顶端16个元素中的一个到栈顶,或者将栈顶元素和下面16个元素中的一个交换。所有其他操作都只能取最顶的几个元素,运算后,把结果压入栈顶。当然可以把栈上的元素放到存储或内存中。但无法只访问栈上指定深度的那个元素,除非先从栈顶移除其他元素。 122 | 123 | **建议** 使用结构体或数组来封装入参或返回值,达到减少栈顶元素使用的目的,从而避免此错误。对于智能合约也应该[避免过多的设计](./智能合约-设计模式.md)。需要分布式协作的重要数据才上链,非必需数据不上链;链上验证,链下授权。 124 | -------------------------------------------------------------------------------- /blockchain/智能合约-设计模式.md: -------------------------------------------------------------------------------- 1 | ## 设计模式 2 | 《Design Patterns For Smart Contracts In the Ethereum Ecosystem》 3 | ### 安全 Security 4 | * Checks-Effects-Interaction 保证状态完整,再做外部调用 5 | 6 | Checks:参数验证,Effects:修改合约状态,Interaction:外部交互 7 | 这个模式要求合约按照Checks-Effects-Interaction的顺序来组织代码。它的好处在于进行外部调用之前,Checks-Effects已完成合约自身状态所有相关工作,使得状态完整、逻辑自洽,这样外部调用就无法利用不完整的状态进行攻击 8 | 9 | * Mutex - 禁止递归 10 | 11 | 使用修饰符防止函数被递归调用。防止重放攻击 12 | ```javascript 13 | contract Mutex { 14 | bool locked; 15 | modifier noReentrancy() { 16 | //防止递归 17 | require(!locked, "Reentrancy detected"); 18 | locked = true; 19 | _; 20 | locked = false; 21 | } 22 | 23 | //调用该函数将会抛出Reentrancy detected错误 24 | function some() public noReentrancy{ 25 | some(); 26 | } 27 | } 28 | ``` 29 | 30 | ### 可维护性 Maintaince 31 | 32 | 高度模块化、高内聚低耦合 33 | 34 | * Data segregation - 数据与逻辑相分离 35 | ```javascript 36 | contract DataRepository{ 37 | 38 | uint private _data; 39 | 40 | function setData(uint data) public { 41 | _data = data; 42 | } 43 | 44 | function getData() public view returns(uint){ 45 | return _data; 46 | } 47 | } 48 | contract Computer{ 49 | DataRepository private _dataRepository; 50 | constructor(address addr){ 51 | _dataRepository = DataRepository(addr); 52 | } 53 | 54 | //业务代码 55 | function compute() public view returns(uint){ 56 | return _dataRepository.getData() * 10; 57 | } 58 | } 59 | ``` 60 | 61 | * Satellite - 分解合约功能 62 | ```javascript 63 | contract Base { 64 | uint public _variable; 65 | 66 | function setVariable(uint data) public { 67 | _variable = _satellite.compute(data); 68 | } 69 | 70 | Satellite _satellite; 71 | //更新子合约(卫星合约) 72 | function updateSatellite(address addr) public { 73 | _satellite = Satellite(addr); 74 | } 75 | } 76 | contract Satellite { 77 | function compute(uint a) public returns(uint){ 78 | return a * 10; 79 | } 80 | } 81 | ``` 82 | 83 | * Contract Registry - 跟踪最新合约 84 | ```javascript 85 | contract Registry{ 86 | 87 | address _current; 88 | address[] _previous; 89 | 90 | //子合约升级了,就通过update函数更新地址 91 | function update(address newAddress) public{ 92 | if(newAddress != _current){ 93 | _previous.push(_current); 94 | _current = newAddress; 95 | } 96 | } 97 | 98 | function getCurrent() public view returns(address){ 99 | return _current; 100 | } 101 | } 102 | contract Base { 103 | uint public _variable; 104 | 105 | function setVariable(uint data) public { 106 | Satellite satellite = Satellite(_registry.getCurrent()); 107 | _variable = satellite.compute(data); 108 | } 109 | 110 | Registry private _registry = //...; 111 | } 112 | ``` 113 | 114 | * Contract Relay - 代理调用最新合约 115 | ```javascript 116 | contract Base { 117 | uint public _variable; 118 | 119 | function setVariable(uint data) public { 120 | _variable = _proxy.compute(data); 121 | } 122 | SatelliteProxy private _proxy = //...; 123 | } 124 | contract SatelliteProxy{ 125 | address _current; 126 | function compute(uint a) public returns(uint){ 127 | Satellite satellite = Satellite(_current); 128 | return satellite.compute(a); 129 | } 130 | 131 | //子合约升级了,就通过update函数更新地址 132 | function update(address newAddress) public{ 133 | if(newAddress != _current){ 134 | _current = newAddress; 135 | } 136 | } 137 | } 138 | contract Satellite { 139 | function compute(uint a) public returns(uint){ 140 | return a * 10; 141 | } 142 | } 143 | ``` 144 | 145 | ### 生命周期 Lifecycle 146 | * Mortal - 允许合约自毁 147 | ```javascript 148 | contract Mortal{ 149 | //自毁 150 | function destroy() public{ 151 | //selfdestruct指令,用于销毁合约 152 | selfdestruct(msg.sender); 153 | } 154 | } 155 | ``` 156 | 157 | * Automatic Deprecation - 允许合约自动停止服务 158 | 159 | 当用户调用service,notExpired修饰符会先进行日期检测,这样,一旦过了特定时间,调用就会因过期而被拦截在notExpired层。 160 | ```javascript 161 | contract AutoDeprecated{ 162 | 163 | uint private _deadline; 164 | 165 | function setDeadline(uint time) public { 166 | _deadline = time; 167 | } 168 | 169 | modifier notExpired(){ 170 | require(now <= _deadline); 171 | _; 172 | } 173 | 174 | function service() public notExpired{ 175 | //some code 176 | } 177 | } 178 | ``` 179 | 180 | ### 权限 Authorization 181 | 182 | * Ownership 183 | ```javascript 184 | contract Ownable { 185 | address public owner; 186 | 187 | event OwnershipRenounced(address indexed previousOwner); 188 | event OwnershipTransferred( 189 | address indexed previousOwner, 190 | address indexed newOwner 191 | ); 192 | 193 | constructor() public { 194 | owner = msg.sender; 195 | } 196 | 197 | modifier onlyOwner() { 198 | require(msg.sender == owner); 199 | _;//表示所修饰函数中的代码,也就是将这个 require 在最前面执行 200 | } 201 | 202 | function transferOwnership(address newOwner) public onlyOwner { 203 | require(newOwner != address(0)); 204 | emit OwnershipTransferred(owner, newOwner); 205 | owner = newOwner; 206 | } 207 | 208 | function renounceOwnership() public onlyOwner { 209 | emit OwnershipRenounced(owner); 210 | owner = address(0); 211 | } 212 | } 213 | //继承并添加修饰器 214 | contract Biz is Owned{ 215 | function manage() public onlyOwner{ 216 | } 217 | } 218 | ``` 219 | 220 | ### 行为控制 Action And Control 221 | 222 | * Commit - Reveal - 延迟秘密泄露 223 | Commit And Reveal模式允许用户将要保护的数据转换为不可识别数据,比如一串哈希值,直到某个时刻再揭示哈希值的含义,展露真正的原值。 224 | 以投票场景举例,假设需要在所有参与者都完成投票后再揭示投票内容,以防这期间参与者受票数影响 225 | ```javascript 226 | contract CommitReveal { 227 | struct Commit { 228 | string choice; 229 | string secret; 230 | uint status; 231 | } 232 | mapping(address => mapping(bytes32 => Commit)) public userCommits; 233 | event LogCommit(bytes32, address); 234 | event LogReveal(bytes32, address, string, string); 235 | 236 | function commit(bytes32 commit) public { 237 | Commit storage userCommit = userCommits[msg.sender][commit]; 238 | require(userCommit.status == 0); 239 | userCommit.status = 1; // comitted 240 | emit LogCommit(commit, msg.sender); 241 | } 242 | 243 | function reveal(string choice, string secret, bytes32 commit) public { 244 | Commit storage userCommit = userCommits[msg.sender][commit]; 245 | require(userCommit.status == 1); 246 | require(commit == keccak256(choice, secret)); 247 | userCommit.choice = choice; 248 | userCommit.secret = secret; 249 | userCommit.status = 2; 250 | emit LogReveal(commit, msg.sender, choice, secret); 251 | } 252 | } 253 | ``` 254 | 255 | * Oracle - 读取链外数据 256 | 获取外部数据会通过名为Oracle的链外数据层来执行。当业务方的合约尝试获取外部数据时,会先将查询请求存入到某个Oracle专用合约内;Oracle会监听该合约,读取到这个查询请求后,执行查询,并调用业务合约响应接口使合约获取结果。 257 | ![oracle](../assets/img/contract-oracle.png) 258 | ```javascript 259 | contract Oracle { 260 | address oracleSource = 0x123; // known source 261 | 262 | struct Request { 263 | bytes data; 264 | function(bytes memory) external callback; 265 | } 266 | 267 | Request[] requests; 268 | event NewRequest(uint); 269 | modifier onlyByOracle() { 270 | require(msg.sender == oracleSource); _; 271 | } 272 | 273 | function query(bytes data, function(bytes memory) external callback) public { 274 | requests.push(Request(data, callback)); 275 | emit NewRequest(requests.length - 1); 276 | } 277 | 278 | //回调函数,由Oracle调用 279 | function reply(uint requestID, bytes response) public onlyByOracle() { 280 | requests[requestID].callback(response); 281 | } 282 | } 283 | contract BizContract { 284 | Oracle _oracle; 285 | 286 | constructor(address oracle){ 287 | _oracle = Oracle(oracle); 288 | } 289 | 290 | modifier onlyByOracle() { 291 | require(msg.sender == address(_oracle)); 292 | _; 293 | } 294 | 295 | function updateExchangeRate() { 296 | _oracle.query("USD", this.oracleResponse); 297 | } 298 | 299 | //回调函数,用于读取响应 300 | function oracleResponse(bytes response) onlyByOracle { 301 | // use the data 302 | } 303 | } 304 | ``` 305 | 306 | ## 闪电网络 307 | ### RSMC 308 | Revocable Sequence Maturity Contract(序列到期可撤销合约) 309 | 310 | ### HTLC 311 | Hashed Timelock Contract(哈希时间锁定) 312 | -------------------------------------------------------------------------------- /blockchain/联盟链.md: -------------------------------------------------------------------------------- 1 | 联盟链 consortium blockchain 2 | ----------- 3 | * BSN 区块链服务网络(Blockchain-based Service Network) 4 | > 是一个跨云服务、跨门户、跨底层框架,用于部署和运行区块链应用的全球性公共基础设施网络,由国家信息中心、中国移动通信集团公司、中国银联股份有限公司、北京红枣科技有限公司共同发起 5 | [BSN培训](http://kb.bsnbase.com/webdoc/view/Pub4028813e711a7c39017185cacb7b5640.html) 6 | 7 | * Hyperledger Fabric (有国密版Fabric) 8 | * FISCO BCOS (支持国密) 9 | * [CITA](https://github.com/citahub/cita) 10 | * 梧桐链 11 | * Brochain 12 | * [XuperChain](https://github.com/xuperchain/xuperchain) 13 | 14 | ## [FISCO BCOS](https://github.com/FISCO-BCOS/FISCO-BCOS) 15 | 16 | * 保障信息保密性、认证性、完整性、不可抵赖性,需采用CA的准入机制,并支持任意多级的证书结构。 17 | 默认采用三级的证书结构,自上而下分别为链证书、机构证书、节点证书。 18 | * 链证书: 联盟链拥有唯一的链证书 19 | > 证书`ca.crt` , 私钥`ca.key` , 签发证书的序列号记录文件`ca.srl` 20 | 21 | * 机构证书: 22 | > 机构证书`agency.crt` , 机构私钥`agency.key` , `agency.srl` 23 | 24 | * 节点证书:由机构自己生产,每个机构都可以拥有多个节点 25 | 26 | 27 | * 引入多群组架构,支持区块链节点启动多个群组,群组间交易处理、数据存储、区块共识相互隔离,保障区块链系统隐私性的同时,降低了系统的运维复杂度 28 | > 机构A、B、C所有节点构成一个区块链网络,运行业务1;一段时间后,机构A、B启动业务2,且不希望该业务相关数据、交易处理被机构C感知,有何解? 29 | 30 | 一般理解群组个数可以达到整数上限,但由于硬件的限制,应采用这种极端的设计,机构可以为不同的业务使用不同的节点去加了对应群组来解决多业务问题。 31 | > 100个群组都落在一个节点上可能需要。64核,128g内存,百兆外网带宽,4T硬盘以上。这是基本的。如果业务有上量还得加 32 | 33 | ### 节点准入机制 34 | CA黑白名单 35 | 36 | * 群组节点:共识节点,观察节点 37 | * 游离节点:完成网络准入但没有加入群组的节点,不参与共识和同步 38 | 39 | #### 网络准入机制 40 | #### 群组准入机制 41 | 42 | **交易流程** 43 | * 用户通过操作SDK或直接编写curl命令向所连接的节点发起交易。 44 | * 节点收到交易后,若当前交易池未满则将交易附加至TxPool中并向自己所连的节点广播该交易;否则丢弃交易并输出告警。 45 | * Sealer(打包器)会不断从交易池中取出交易,并立即将收集到的交易打包为区块并发送至共识引擎。 46 | * 共识引擎调用BlockVerifier对区块进行验证并在网络中进行共识,BlockVerifier调用Executor执行区块中的每笔交易。当区块验证无误且网络中节点达成一致后,共识引擎将区块发送至BlockChain。 47 | * BlockChain收到区块,对区块信息(如块高等)进行检查,并将区块数据与表数据写入底层存储中,完成区块上链。 48 | 49 | **交易同步** 50 | * 一笔交易通过channel或RPC发送到某节点上 51 | * 收到交易的节点全量广播此交易给其它节点 52 | * 其它节点收到交易后,为了保险起见,选择25%的节点再广播一次 53 | * 节点收到广播过的交易,不会再次广播 54 | 55 | ### 国密算法 56 | | |标准版FISCO BCOS| 国密版FISCO BCOS| 57 | |----|-----|----| 58 | |SSL链接| Openssl TLSv1.2协议| 国密TLSv1.1协议| 59 | |签名验证| ECDSA签名算法| SM2签名算法| 60 | |消息摘要算法 |SHA-256 SHA-3| SM3消息摘要算法| 61 | |落盘加密算法| AES-256加密算法| SM4加密算法| 62 | |证书模式| OpenSSL证书模式| 国密双证书模式| 63 | |合约编译器| 以太坊solidity编译器| 国密solidity编译器| 64 | 65 | [详细](https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/design/features/guomi.html) 66 | 67 | ### [WeBASE](https://github.com/WeBankFinTech/WeBASE) 68 | ### [WeIdentity](https://github.com/WeBankFinTech/WeIdentity) 69 | ### [WeDPR](https://github.com/WeBankBlockchain/WeDPR-Lab-Core) 70 | -------------------------------------------------------------------------------- /design-patterns/a.sh: -------------------------------------------------------------------------------- 1 | 2 | function step(){ 3 | 4 | echo -e "\n\033[4;31m $1\033[0m - \033[4;36m${@:2}\033[0m \n" 5 | } 6 | 7 | function highlight(){ 8 | echo -e "\033[36m${@:1}\033[0m" 9 | } 10 | 11 | function info(){ 12 | echo -e "\033[32m${@:1}\033[0m" 13 | } 14 | 15 | function warn(){ 16 | echo -e "\033[33m${@:1}\033[0m" 17 | } 18 | 19 | function print_line() 20 | { 21 | name=${@:1} 22 | l=`echo $name |awk -F "" '{print NF}'` 23 | outword=- 24 | shellwidth=`stty size|awk '{print $2}'` 25 | ll=`expr $shellwidth - $l` 26 | lll=`expr $ll / 2` 27 | tmp=3 28 | lll=`expr $lll - $tmp` 29 | yes `echo -e "\033[36m \033[0m"` | sed $tmp'q' | tr -d '\n' 30 | printf "`echo -e "\033[36m$name\033[0m"`" 31 | yes `echo -e "\033[36m \033[0m"` | sed $tmp'q' | tr -d '\n' 32 | yes `echo -e "\033[36m$outword\033[0m"` | sed $lll'q' | tr -d '\n' 33 | } 34 | 35 | function split_file(){ 36 | split -l 1000 $1 split- 37 | } 38 | 39 | folder="." 40 | files=$(ls $folder) 41 | for filename in $files;do 42 | if [[ $filename = "*md" ]]; 43 | then 44 | data_file=`basename $filename` 45 | split_file $filename 46 | info "$filename" 47 | sleep 2 48 | split_files=$(ls $folder) 49 | for filename in $split_files;do 50 | info "$filename" 51 | if [[ $filename =~ "split-" ]]; 52 | then 53 | info "$filename" 54 | #curl -H 'Content-Type: application/json' -XPOST "$api/_bulk?pretty" --data-binary "@$filename" > test.log 55 | fi 56 | done 57 | rm -rf split-* 58 | fi 59 | done 60 | -------------------------------------------------------------------------------- /design-patterns/代理.md: -------------------------------------------------------------------------------- 1 | 代理模式 2 | -------- 3 | 4 | 重点等级::star::star::star::star::star: 5 | 6 | ## 场景 7 | 代理既间接对目标对象进行访问的方式;即通过代理对象访问目标对象。 8 | 可以在目标对象实现的功能上,增加额外的功能补充,也可以对目标对象进行一些限制等扩展。 9 | 10 | * [访问控制](http://java-design-patterns.com/blog/controlling-access-with-proxy-pattern/) 11 | * 代理服务器[Apache Commons Proxy 代理服务器](https://commons.apache.org/proper/commons-proxy/) 12 | * Mocking frameworks Mockito, Powermock, EasyMock 框架 Mockito,Powermock,EasyMock 13 | * Spring AOP 面向切面编程。可以在指定方法中切一刀添加新的逻辑。 14 | 15 | ## 静态代理 16 | 在使用静态代理时,被代理对象与代理对象需要一起实现相同的接口或者是继承相同父类,因此要定义一个接口或抽象类. 17 | ```java 18 | // 接口 19 | interface IStar { 20 | void sing(); 21 | } 22 | 23 | // 被代理对象 24 | class LDHStar implements IStar { 25 | @Override 26 | public void sing() { 27 | System.out.println("刘德华唱歌"); 28 | } 29 | 30 | } 31 | 32 | // 代理类 33 | class ProxyManger implements IStar { 34 | 35 | // 真实对象的引用 36 | private IStar star; 37 | 38 | public ProxyManger() { 39 | super(); 40 | } 41 | 42 | public ProxyManger(IStar star) { 43 | super(); 44 | this.star = star; 45 | } 46 | 47 | @Override 48 | public void sing() { 49 | //TODO 唱歌前准备工作 50 |       System.out.println("唱歌前准备"); 51 |     star.sing(); 52 |     System.out.println("善后工作"); 53 | } 54 | } 55 | class ProxyDemo{ 56 | public static void main(String[] args) { 57 | // 创建明星对象 58 | IStar ldh = new LDHStar(); 59 | ProxyManger proxy = new ProxyManger(ldh); 60 | proxy.sing(); 61 | } 62 | } 63 | ``` 64 | 优点:可以做到在不修改目标对象的功能前提下,对目标功能扩展。 65 | 缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。 66 | 67 | ## 动态代理 68 | 69 | 动态代理的主要特点就是能够在程序运行时JVM才为被代理对象生成代理对象。 70 | 常说的动态代理也叫做JDK代理也是一种接口代理`java.lang.reflect.Proxy`,JDK中生成代理对象的代理类就是Proxy 71 | 72 | ```java 73 | //同样引用上面代码中的 接口 与 被代理对象 74 | class MyInvocationHandle implements InvocationHandler{ 75 | private Object target; 76 | public void setTarget(Object target) { 77 | this.target = target; 78 | } 79 | @Override 80 | public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 81 | //TODO 唱歌前准备工作 82 |       System.out.println("唱歌前准备"); 83 | method.invoke(target, args); 84 | System.out.println("善后工作"); 85 | return null; 86 | } 87 | } 88 | 89 | //生产代理对象的工厂 90 | class MyProxyFactory{ 91 | public static Object getProxy(Object target) { 92 | MyInvocationHandle handle = new MyInvocationHandle(); 93 | handle.setTarget(target); 94 | //JDK 生产代理对象 95 | Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),handle); 96 | return proxy; 97 | } 98 | } 99 | public class ProxyDemo { 100 | public static void main(String[] args) { 101 | IStar ldh = new LDHStar(); 102 | IStar proxy =(IStar) MyProxyFactory.getProxy(ldh); 103 | proxy.sing(); 104 | } 105 | } 106 | ``` 107 | 代理对象不需要实现接口,但是目标对象一定要实现接口,否则不能使用动态代理。 108 | 109 | ## cglib 110 | 111 | 静态代理和动态代理模式都要求目标对象是实现一个接口的对象。 112 | 如何对没有实现接口的对象进行代理?那就是 `cglib` 113 | 114 | Cglib代理:使用继承目标类以目标对象子类的方式实现代理,叫作子类代理。它是在内存中构建一个子类对象从而实现对目标对象功能的扩展。 115 | 116 | 最典型的就是 Spring AOP 动态代理也使用了 cglib 117 | -------------------------------------------------------------------------------- /design-patterns/单例.md: -------------------------------------------------------------------------------- 1 | 单例模式 singleton 2 | --------- 3 | 4 | 重点等级::star::star::star::star::star: 5 | 6 | ## 场景 7 | 单例类只能有一个实例;必须自己创建自己的唯一实例;必须给所有其他对象提供这一实例; 8 | 单例可以解决全局使用的类频繁地创建与销毁的问题,以节省系统资源 9 | 10 | * 日志类 11 | * 管理数据库连接 12 | * 文件管理 13 | 14 | ## JDK源码中的案例 15 | * `java.lang.Runtime.getRuntime()` 饿汉式 16 | * `java.awt.Desktop.getDesktop()` 懒汉式,synchronized块 17 | 18 | ## Best Way 19 | 单元素的枚举类型是实现单例的最佳方法。 20 | > **Joshua Bloch, Effective Java 2nd Edition p.18** 21 | > A single-element enum type is the best way to implement a singleton 22 | > enum since jdk1.5 23 | 24 | ``` 25 | public enum EnumIvoryTower { 26 | INSTANCE; 27 | // Other methods 28 | } 29 | 30 | EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE; 31 | EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; 32 | assertEquals(enumIvoryTower1, enumIvoryTower2); // true 33 | ``` 34 | 35 | ## Other Way 36 | ### 懒饿汉及双重检查锁 37 | 38 | **关键点** 39 | * 构造方法私有化 40 | * 变量 `_instance` 使用 `volatile` 修饰,所有的写(write)都将先行发生于读(read)。 41 | [volatile,能保证先行发生关系(happens-before relationship)](/Questions/JAVA基础.md#Java内存模型)。 42 | 43 | ```java 44 | /* 45 | * 单例 - 双重检查锁 46 | * DCL (double-checked locking) 47 | */ 48 | 49 | final class Singleton { 50 | 51 | private volatile static Singleton _instance; 52 | // 饿汉式,直接初始化,线程安全 53 | // private static Singleton _instance = new Singleton(); 54 | 55 | private static boolean flag = true; 56 | 57 | private Singleton() { 58 | // 构造方法私有化 59 | // 阻击反射攻击,此方法只能调用一次 60 | if (flag) { 61 | flag = false; 62 | } else { 63 | throw new IllegalStateException("Already initialized."); 64 | } 65 | } 66 | 67 | /* 68 | * 版本1: 懒汉模式,当多个线程同时访问,发生线程安全问题 69 | */ 70 | public static Singleton getInstance() { 71 | if (_instance == null) {//饿汉式,因为已经初始化无需判断 72 | _instance = new Singleton(); 73 | } 74 | return _instance; 75 | } 76 | 77 | /* 78 | * 版本2 : 对Singleton进行双重检查锁定的实现。 79 |      * 目的是最大程度地减少同步成本并提高性能, 仅通过锁定代码的关键部分,不在方法上使用 synchronized 80 |      * 如果我们不对 _instance 使用 volatile,那么这仍然是不安全的,因为另一个线程可以查看Singleton的一半初始化实例。 81 | */ 82 | public static Singleton getInstanceDC() { 83 | if (_instance == null) { 84 | synchronized (Singleton.class) { 85 | if (_instance == null) { 86 | _instance = new Singleton(); 87 | } 88 | } 89 | } 90 | return _instance; 91 | } 92 | } 93 | ``` 94 | 95 | 这种实现模式有几个**问题** 96 | * 可以使用反射调用私有构造方法。 97 | `AccessibleObject.setAccessible(true)` 。 使用反射创建出来的来个对象指定是不一样的。 98 | 如果要抵御这种攻击,可以修改构造方法,让它在被要求创建第二个实例的时候抛出异常。 99 | * 使用序列化前后的两个对象不相等。任何一个 `ObjectInputStream.readObject` 方法,它都会返回一个新建的实例。 100 | 101 | 而使用枚举实现单例可以避免以上问题。 102 | -------------------------------------------------------------------------------- /design-patterns/外观.md: -------------------------------------------------------------------------------- 1 | 外观 Facade 2 | ---------- 3 | 4 | 重点等级::star::star::star: 5 | 6 | 为子系统中的一组接口提供统一的接口。 Facade 定义了一个更高级别的接口,使得子系统更易于使用。 7 | 8 | ## 场景 9 | * 你想为一个复杂的子系统提供一个简单的接口。 随着子系统的发展,子系统往往变得更加复杂。 大多数模式在应用时会导致类越来越多和越来越小。 这使得子系统更容易重用和定制,但是它也变得更难用于不需要定制它的客户端。 Facade 可以为子系统提供一个简单的默认视图,这对于大多数客户来说已经足够好了。 只有需要更多定制性的客户才需要看到立面之外的东西 10 | * 客户端和抽象的实现类之间有许多依赖关系。 引入 facade 将子系统与客户端和其他子系统解耦,从而提高子系统的独立性和可移植性 11 | * 你想把子系统分层。 使用 facade 定义每个子系统级别的入口点。 如果子系统是相互依赖的,那么您可以简化它们之间的依赖关系,方法是让它们仅仅通过外观进行相互通信 12 | 13 | ## 示例代码 14 | 15 | ```java 16 | /** 17 | * 矿工挖矿示例 18 | * 矿工抽象类 19 | * 提供矿工日常工作及作息方法 20 | */ 21 | public abstract class DwarvenMineWorker { 22 | 23 | private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenMineWorker.class); 24 | 25 | public void goToSleep() { 26 | LOGGER.info("{} goes to sleep.", name()); 27 | } 28 | 29 | public void wakeUp() { 30 | LOGGER.info("{} wakes up.", name()); 31 | } 32 | 33 | public void goHome() { 34 | LOGGER.info("{} goes home.", name()); 35 | } 36 | 37 | public void goToMine() { 38 | LOGGER.info("{} goes to the mine.", name()); 39 | } 40 | 41 | private void action(Action action) { 42 | switch (action) { 43 | case GO_TO_SLEEP: 44 | goToSleep(); 45 | break; 46 | case WAKE_UP: 47 | wakeUp(); 48 | break; 49 | case GO_HOME: 50 | goHome(); 51 | break; 52 | case GO_TO_MINE: 53 | goToMine(); 54 | break; 55 | case WORK: 56 | work(); 57 | break; 58 | default: 59 | LOGGER.info("Undefined action"); 60 | break; 61 | } 62 | } 63 | 64 | public void action(Action... actions) { 65 | Arrays.stream(actions).forEach(this::action); 66 | } 67 | 68 | /** 69 | * 细分工种矿工,不同的实现方式 70 | */ 71 | public abstract void work(); 72 | 73 | public abstract String name(); 74 | 75 | static enum Action { 76 | GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK 77 | } 78 | } 79 | 80 | /** 81 | * 隧道挖掘工 82 | */ 83 | public class DwarvenTunnelDigger extends DwarvenMineWorker { 84 | 85 | private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenTunnelDigger.class); 86 | 87 | @Override 88 | public void work() { 89 | LOGGER.info("{} creates another promising tunnel.", name()); 90 | } 91 | 92 | @Override 93 | public String name() { 94 | return "Dwarven tunnel digger"; 95 | } 96 | } 97 | /** 98 | * 黄金挖掘工 99 | */ 100 | public class DwarvenGoldDigger extends DwarvenMineWorker { 101 | 102 | private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenGoldDigger.class); 103 | 104 | @Override 105 | public void work() { 106 | LOGGER.info("{} digs for gold.", name()); 107 | } 108 | 109 | @Override 110 | public String name() { 111 | return "Dwarf gold digger"; 112 | } 113 | } 114 | /** 115 | * 黄金运输工 116 | */ 117 | public class DwarvenCartOperator extends DwarvenMineWorker { 118 | 119 | private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenCartOperator.class); 120 | 121 | @Override 122 | public void work() { 123 | LOGGER.info("{} moves gold chunks out of the mine.", name()); 124 | } 125 | 126 | @Override 127 | public String name() { 128 | return "Dwarf cart operator"; 129 | } 130 | } 131 | 132 | /** 133 | * 管理所有矿工的作息 134 | */ 135 | public class DwarvenGoldmineFacade { 136 | 137 | private final List workers; 138 | 139 | public DwarvenGoldmineFacade() { 140 | workers = List.of( 141 | new DwarvenGoldDigger(), 142 | new DwarvenCartOperator(), 143 | new DwarvenTunnelDigger()); 144 | } 145 | 146 | public void startNewDay() { 147 | makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE); 148 | } 149 | 150 | public void digOutGold() { 151 | makeActions(workers, DwarvenMineWorker.Action.WORK); 152 | } 153 | 154 | public void endDay() { 155 | makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP); 156 | } 157 | 158 | private static void makeActions(Collection workers, 159 | DwarvenMineWorker.Action... actions) { 160 | workers.forEach(worker -> worker.action(actions)); 161 | } 162 | } 163 | 164 | DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade(); 165 | facade.startNewDay(); 166 | // Dwarf gold digger wakes up. 167 | // Dwarf gold digger goes to the mine. 168 | // Dwarf cart operator wakes up. 169 | // Dwarf cart operator goes to the mine. 170 | // Dwarven tunnel digger wakes up. 171 | // Dwarven tunnel digger goes to the mine. 172 | facade.digOutGold(); 173 | // Dwarf gold digger digs for gold. 174 | // Dwarf cart operator moves gold chunks out of the mine. 175 | // Dwarven tunnel digger creates another promising tunnel. 176 | facade.endDay(); 177 | // Dwarf gold digger goes home. 178 | // Dwarf gold digger goes to sleep. 179 | // Dwarf cart operator goes home. 180 | // Dwarf cart operator goes to sleep. 181 | // Dwarven tunnel digger goes home. 182 | // Dwarven tunnel digger goes to sleep. 183 | 184 | ``` 185 | -------------------------------------------------------------------------------- /design-patterns/桥接.md: -------------------------------------------------------------------------------- 1 | Bridge 2 | ------- 3 | 4 | 重点等级::star::star::star: 5 | 6 | ## 场景 7 | 将抽象与其实现分离开来,这样两者就可以独立地变化。 8 | 重点需要理解如何将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化。 9 | 10 | 11 | ## 代码示例 12 | ```java 13 | 14 | //实现化角色 15 | interface Implementor 16 | { 17 | public void OperationImpl(); 18 | } 19 | //具体实现化角色 20 | class ConcreteImplementorA implements Implementor 21 | { 22 | public void OperationImpl() 23 | { 24 | System.out.println("具体实现化(Concrete Implementor)角色被访问" ); 25 | } 26 | } 27 | //抽象化角色 28 | abstract class Abstraction 29 | { 30 | protected Implementor imple; 31 | protected Abstraction(Implementor imple) 32 | { 33 | this.imple=imple; 34 | } 35 | public abstract void Operation(); 36 | } 37 | //扩展抽象化角色 38 | class RefinedAbstraction extends Abstraction 39 | { 40 | protected RefinedAbstraction(Implementor imple) 41 | { 42 | super(imple); 43 | } 44 | public void Operation() 45 | { 46 | System.out.println("扩展抽象化(Refined Abstraction)角色被访问" ); 47 | imple.OperationImpl(); 48 | } 49 | } 50 | 51 | 52 | Implementor imple=new ConcreteImplementorA(); 53 | Abstraction abs=new RefinedAbstraction(imple); 54 | abs.Operation(); 55 | ``` 56 | 57 | ## 项目案例 58 | **画笔** 需要根据细度分为 `小` `中` `大` 三种,根据颜色可以分为多种,但根据业务不同的画笔不提供所有现有的颜色。 59 | ![pen](../assets/img/design-birdge.png) 60 | 61 | ## Tutorial 62 | * [journaldev](https://www.journaldev.com/1491/bridge-design-pattern-java) 63 | -------------------------------------------------------------------------------- /design-patterns/模板.md: -------------------------------------------------------------------------------- 1 | Template method 2 | ----------- 3 | 4 | 重点等级::star::star::star::star: 5 | 6 | 在操作中定义算法的框架,将一些步骤推迟到子类。 模板方法允许子类在不改变算法结构的情况下重新定义算法的某些步骤。 7 | 8 | 为了确保子类不重写 template 方法,应该声明 template 方法为 `final`。 9 | 10 | ## 场景 11 | * 一个大的算法,需要顺序的执行 D->C->B->A 四个过程,并对每个过程的结果做加工处理,一次性实现这不变的部分,把可变的 ABCD 交给子类实现。 12 | * 控制子类扩展。 您可以定义一个模板方法,该方法在特定的点上调用“钩子”操作,从而只允许在这些点上进行扩展 13 | 14 | ## 示例 15 | 16 | ```java 17 | public abstract class AblstractClass { 18 | //模板方法,固定不变 19 | //使用 final 不允许之类修改 20 | public final void algorithm(){ 21 | D(); 22 | C(); 23 | B(); 24 | A(); 25 | } 26 | 27 | //可变方法,交由子类实现 28 | abstract void A(); 29 | abstract void B(); 30 | abstract void C(); 31 | abstract void D(); 32 | } 33 | ``` 34 | 35 | 36 | ## JDK及各大框架源码中的应用 37 | * Tomcat `javax.servlet.http.HttpServlet#service` 38 | 39 | 我们的 `servlet` 中只需要实现 `doGet`, `doPost`, `doPut` 等方法不需要处理这个请求是属于那种请求方式,那是因为在 `service` 里做了处理,这是最典型的模板方法。 40 | 41 | * Spring Security 42 | 43 | `org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate` 44 | 45 | 认证时,具体的怎么获取用户信息,用何种方式判断密码是否正确等等需要之类去实现。 46 | 此处也是自定义认证器的读源码入口,结合spring-security-oauth 一起看,非常值得学习 47 | -------------------------------------------------------------------------------- /design-patterns/状态模式.md: -------------------------------------------------------------------------------- 1 | 状态 State 2 | ------------- 3 | 4 | 重点等级::star: 5 | 6 | ## 一句话 7 | * 替代 if ... else 。把复杂的“判断逻辑”提取到不同的状态对象中,允许状态对象在其内部状态发生改变时改变其行为 8 | * 对象的行为取决于其状态,并且必须根据该状态在运行时更改其行为 9 | 10 | 11 | ```java 12 | //各种动物的不同状态所对应不一样的处理 13 | //状态接口类 14 | public interface State { 15 | 16 | void onEnterState(); 17 | 18 | void observe(); 19 | } 20 | 21 | //安静状态实现类 22 | public class PeacefulState implements State { 23 | 24 | private static final Logger LOGGER = LoggerFactory.getLogger(PeacefulState.class); 25 | 26 | private final Animal animal; 27 | 28 | public PeacefulState(Animal animal) { 29 | this.animal = animal; 30 | } 31 | 32 | @Override 33 | public void observe() { 34 | LOGGER.info("{} 平静祥和.", animal); 35 | } 36 | 37 | @Override 38 | public void onEnterState() { 39 | LOGGER.info("{} 安静下来.", animal); 40 | } 41 | } 42 | 43 | public class AngryState implements State { 44 | 45 | private static final Logger LOGGER = LoggerFactory.getLogger(AngryState.class); 46 | 47 | private final Animal animal; 48 | 49 | public AngryState(Animal animal) { 50 | this.animal = animal; 51 | } 52 | 53 | @Override 54 | public void observe() { 55 | LOGGER.info("{} 更生气了!", animal); 56 | } 57 | 58 | @Override 59 | public void onEnterState() { 60 | LOGGER.info("{} 生气了!", animal); 61 | } 62 | } 63 | 64 | public class Animal { 65 | 66 | private State state; 67 | private String name; 68 | 69 | public Animal(String name) { 70 | this.name = name; 71 | this.state = new PeacefulState(this); 72 | } 73 | 74 | public void timePasses() { 75 | if (state.getClass().equals(PeacefulState.class)) { 76 | changeStateTo(new AngryState(this)); 77 | } else { 78 | changeStateTo(new PeacefulState(this)); 79 | } 80 | } 81 | 82 | private void changeStateTo(State newState) { 83 | this.state = newState; 84 | this.state.onEnterState(); 85 | } 86 | 87 | @Override 88 | public String toString() { 89 | return name; 90 | } 91 | 92 | public void observe() { 93 | this.state.observe(); 94 | } 95 | } 96 | 97 | var animal = new Animal("小猫"); 98 | animal.observe(); 99 | animal.timePasses(); 100 | animal.observe(); 101 | animal.timePasses(); 102 | animal.observe(); 103 | 104 | //小猫 平静祥和. 105 | //小猫 生气了! 106 | //小猫 更生气了! 107 | //小猫 安静下来. 108 | //小猫 平静祥和. 109 | ``` 110 | -------------------------------------------------------------------------------- /design-patterns/策略.md: -------------------------------------------------------------------------------- 1 | 策略 Strategy 2 | -------- 3 | 4 | 重点等级::star::star::star: 5 | 6 | 定义一个算法组,封装每一个算法,并使其可互换。策略让算法独立于使用它的客户端而独立地变化。 7 | 8 | 在有多种算法相似的情况下,避免过多if else语句的尴尬。 9 | 10 | ## 示例代码 11 | 以一个支付场景为例,支付是可以使用 Paypal、信用卡等等,可以使用不同的策略来进行代码设计。 12 | 13 | ```java 14 | //支付接口 15 | public interface PaymentStrategy { 16 | 17 | public void pay(int amount); 18 | } 19 | 20 | //信用卡支付 21 | public class CreditCardStrategy implements PaymentStrategy { 22 | 23 | private String name; 24 | private String cardNumber; 25 | private String cvv; 26 | private String dateOfExpiry; 27 | 28 | public CreditCardStrategy(String nm, String ccNum, String cvv, String expiryDate){ 29 | this.name=nm; 30 | this.cardNumber=ccNum; 31 | this.cvv=cvv; 32 | this.dateOfExpiry=expiryDate; 33 | } 34 | @Override 35 | public void pay(int amount) { 36 | System.out.println(amount +" paid with credit/debit card"); 37 | } 38 | 39 | } 40 | 41 | //Paypal 支付 42 | public class PaypalStrategy implements PaymentStrategy { 43 | 44 | private String emailId; 45 | private String password; 46 | 47 | public PaypalStrategy(String email, String pwd){ 48 | this.emailId=email; 49 | this.password=pwd; 50 | } 51 | 52 | @Override 53 | public void pay(int amount) { 54 | System.out.println(amount + " paid using Paypal."); 55 | } 56 | 57 | } 58 | 59 | //购物车 60 | public class ShoppingCart { 61 | 62 | //List of items 63 | List items; 64 | 65 | public ShoppingCart(){ 66 | this.items=new ArrayList(); 67 | } 68 | 69 | public void addItem(Item item){ 70 | this.items.add(item); 71 | } 72 | 73 | public void removeItem(Item item){ 74 | this.items.remove(item); 75 | } 76 | 77 | public int calculateTotal(){ 78 | int sum = 0; 79 | for(Item item : items){ 80 | sum += item.getPrice(); 81 | } 82 | return sum; 83 | } 84 | 85 | public void pay(PaymentStrategy paymentMethod){ 86 | int amount = calculateTotal(); 87 | paymentMethod.pay(amount); 88 | } 89 | } 90 | 91 | 92 | public class ShoppingCartTest { 93 | 94 | public static void main(String[] args) { 95 | ShoppingCart cart = new ShoppingCart(); 96 | 97 | Item item1 = new Item("1234",10); 98 | Item item2 = new Item("5678",40); 99 | 100 | cart.addItem(item1); 101 | cart.addItem(item2); 102 | 103 | //pay by paypal 104 | cart.pay(new PaypalStrategy("myemail@example.com", "mypwd")); 105 | 106 | //pay by credit card 107 | cart.pay(new CreditCardStrategy("Pankaj Kumar", "1234567890123456", "786", "12/15")); 108 | 109 | //50 paid using Paypal. 110 | //50 paid with credit/debit card 111 | } 112 | 113 | } 114 | ``` 115 | 116 | ## 枚举避免ifelse 117 | 118 | ```java 119 | public enum ChannelRuleEnum { 120 | 121 | /** 122 | * 头条 123 | */ 124 | TOUTIAO("TOUTIAO",new TouTiaoChannelRule()), 125 | /** 126 | * 腾讯 127 | */ 128 | TENCENT("TENCENT",new TencentChannelRule()), 129 | ; 130 | 131 | public String name; 132 | 133 | public GeneralChannelRule channel; 134 | 135 | ChannelRuleEnum(String name, GeneralChannelRule channel) { 136 | this.name = name; 137 | this.channel = channel; 138 | } 139 | 140 | //匹配 141 | public static ChannelRuleEnum match(String name){ 142 | ChannelRuleEnum[] values = ChannelRuleEnum.values(); 143 | for (ChannelRuleEnum value : values) { 144 | if(value.name.equals(name)){ 145 | return value; 146 | } 147 | } 148 | return null; 149 | } 150 | public String getName() { 151 | return name; 152 | } 153 | 154 | public GeneralChannelRule getChannel() { 155 | return channel; 156 | } 157 | } 158 | 159 | public static void main(String[] args) { 160 | String sign = "TOUTIAO"; 161 | ChannelRuleEnum channelRule = ChannelRuleEnum.match(sign); 162 | GeneralChannelRule rule = channelRule.channel; 163 | rule.xxx(); 164 | } 165 | ``` 166 | -------------------------------------------------------------------------------- /design-patterns/装饰者.md: -------------------------------------------------------------------------------- 1 | 装饰者 Decorator 2 | -------- 3 | 4 | 重点等级::star::star::star::star::star: 5 | 6 | 动态的将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的替代方案。 7 | 装饰者模式中装饰者和被装饰者具有相同的超类,在这利用继承达到了“类型匹配”,而不是利用继承获得“行为”。 8 | 装饰者和被装饰者拥有同一超类。装饰者实现同一超类的接口并持有被装饰者。 9 | 10 | ## 场景 11 | * 在不影响其他对象的情况下,动态地、透明地向各个对象添加职责 12 | 13 | 14 | 15 | 16 | ## JDK及各大框架源码中的案例 17 | * JAVA IO `java.io.InputStream, java.io.OutputStream, java.io.Reader 、java.io.Writer` 18 | * `java.util.Collections#synchronizedXXX()` 19 | * `java.util.Collections#unmodifiableXXX()` 20 | * `java.util.Collections#checkedXXX()` 21 | * Spring-web 对 ServletRequest 使用 ServletRequestWrapper 进行包装装饰 22 | * Spring Security oauth2 23 | ```java 24 | org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter //被装饰者 25 | org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter //装饰者 26 | ``` 27 | 28 | 29 | ## 代码案例 30 | ```java 31 | public interface Troll { 32 | void attack(); 33 | int getAttackPower(); 34 | void fleeBattle(); 35 | } 36 | 37 | //被装饰者 38 | public class SimpleTroll implements Troll { 39 | 40 | private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTroll.class); 41 | 42 | @Override 43 | public void attack() { 44 | LOGGER.info("The troll tries to grab you!"); 45 | } 46 | 47 | @Override 48 | public int getAttackPower() { 49 | return 10; 50 | } 51 | 52 | @Override 53 | public void fleeBattle() { 54 | LOGGER.info("The troll shrieks in horror and runs away!"); 55 | } 56 | } 57 | 58 | //装饰者 59 | public class ClubbedTroll implements Troll { 60 | 61 | private static final Logger LOGGER = LoggerFactory.getLogger(ClubbedTroll.class); 62 | 63 | private Troll decorated; 64 | 65 | public ClubbedTroll(Troll decorated) { 66 | this.decorated = decorated; 67 | } 68 | 69 | @Override 70 | public void attack() { 71 | decorated.attack(); 72 | LOGGER.info("The troll swings at you with a club!"); 73 | } 74 | 75 | @Override 76 | public int getAttackPower() { 77 | return decorated.getAttackPower() + 10; 78 | } 79 | 80 | @Override 81 | public void fleeBattle() { 82 | decorated.fleeBattle(); 83 | } 84 | } 85 | 86 | ``` 87 | 88 | ```java 89 | // simple troll 90 | var troll = new SimpleTroll(); 91 | troll.attack(); // The troll tries to grab you! 92 | troll.fleeBattle(); // The troll shrieks in horror and runs away! 93 | 94 | // 通过装饰器来更改加强 troll 95 | var clubbedTroll = new ClubbedTroll(troll); 96 | clubbedTroll.attack(); // The troll tries to grab you! The troll swings at you with a club! 97 | clubbedTroll.fleeBattle(); // The troll shrieks in horror and runs away! 98 | ``` 99 | -------------------------------------------------------------------------------- /design-patterns/观察者.md: -------------------------------------------------------------------------------- 1 | Observer 2 | ------ 3 | 4 | 重点等级::star::star::star: 5 | 6 | 观察者模式(又被称为发布-订阅(Publish/Subscribe)模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(被观察者)。这个主题对象在状态变化时,会主动通知所有的观察者对象,使他们能够自动更新自己。 7 | 8 | ## 应用案例 9 | 比较广泛的类似微信公众号的订阅。我们(观察者)订阅某公众号(被观察者),公众号发布文章,我们即可接受到新文章通知,而被观察者无需关心有谁,几个人在观察他。观察者与被观察者完全解耦。 10 | 11 | 之前 JDK 中提供的 `java.util.Observer` 已经在 JDK1.9后标记为 `@Deprecated`。因为推荐使用 Flow API [JAVA响应式编程](../面试/JAVA反应式编程.md) 12 | 13 | ## JDK源码及各大框架中的应用 14 | * `java.util.EventListener` 众多框架对于它的实现事件驱动都属于观察者模式。如:`HttpSessionBindingListener` 15 | * [RxJava](https://github.com/ReactiveX/RxJava) 16 | * [JAVA响应式编程](../面试/JAVA反应式编程.md) 17 | -------------------------------------------------------------------------------- /design-patterns/设计模式.md: -------------------------------------------------------------------------------- 1 | ## 23种设计模式 2 | 《Design Patterns: Elements of Reusable Object-Oriented Software》 3 | 4 | **创建型模式** 5 | 用于描述怎样创建对象,将对象的创建与使用分离。 6 | 1. 单例(Singleton)模式:某个类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例,其拓展是有限多例模式。 7 | 1. 原型(Prototype)模式:将一个对象作为原型,通过对其进行复制而克隆出多个和原型类似的新实例。 8 | 1. 工厂方法(Factory Method)模式:定义一个用于创建产品的接口,由子类决定生产什么产品。 9 | 1. 抽象工厂(AbstractFactory)模式:提供一个创建产品族的接口,其每个子类可以生产一系列相关的产品。 10 | 1. 建造者(Builder)模式:将一个复杂对象分解成多个相对简单的部分,然后根据不同需要分别创建它们,最后构建成该复杂对象。 11 | **结构型模式** 12 | 用于描述如何将类或对象按某种布局组成更大的结构。 13 | 1. 代理(Proxy)模式:为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。 14 | 1. 适配器(Adapter)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。 15 | 1. 桥接(Bridge)模式:将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。 16 | 1. 装饰(Decorator)模式:动态的给对象增加一些职责,即增加其额外的功能。 17 | 1. 外观(Facade)模式:为多个复杂的子系统提供一个一致的接口,使这些子系统更加容易被访问。 18 | 1. 享元(Flyweight)模式:运用共享技术来有效地支持大量细粒度对象的复用。 19 | 1. 组合(Composite)模式:将对象组合成树状层次结构,使用户对单个对象和组合对象具有一致的访问性。 20 | **行为型模式** 21 | 用于描述类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。 22 | 1. 模板方法(TemplateMethod)模式:定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重定义该算法的某些特定步骤。 23 | 1. 策略(Strategy)模式:定义了一系列算法,并将每个算法封装起来,使它们可以相互替换,且算法的改变不会影响使用算法的客户。 24 | 1. 命令(Command)模式:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。 25 | 1. 职责链(Chain of Responsibility)模式:把请求从链中的一个对象传到下一个对象,直到请求被响应为止。通过这种方式去除对象之间的耦合。 26 | 1. 状态(State)模式:允许一个对象在其内部状态发生改变时改变其行为能力。 27 | 1. 观察者(Observer)模式:多个对象间存在一对多关系,当一个对象发生改变时,把这种改变通知给其他多个对象,从而影响其他对象的行为。 28 | 1. 中介者(Mediator)模式:定义一个中介对象来简化原有对象之间的交互关系,降低系统中对象间的耦合度,使原有对象之间不必相互了解。 29 | 1. 迭代器(Iterator)模式:提供一种方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。 30 | 1. 访问者(Visitor)模式:在不改变集合元素的前提下,为一个集合中的每个元素提供多种访问方式,即每个元素有多个访问者对象访问。 31 | 1. 备忘录(Memento)模式:在不破坏封装性的前提下,获取并保存一个对象的内部状态,以便以后恢复它。 32 | 1. 解释器(Interpreter)模式:提供如何定义语言的文法,以及对语言句子的解释方法,即解释器。 33 | 34 | ## 关键字 35 | ### 开闭原则 36 | OCP (Open Closed Principle) 37 | 38 | 软件实体应当对扩展开放,对修改关闭 39 | Software entities should be open for extension,but closed for modification 40 | 41 | 当应用的需求改变时,在不修改软件实体的源代码或者二进制代码的前提下,可以扩展模块的功能,使其满足新的需求。 42 | 43 | 高内聚,低耦合。 44 | 高内聚:相近的功能放到同一个类中 45 | 低耦合:类与类之间的关系简单清晰 46 | 47 | ### 里氏替换原则 48 | Liskov Substitution Principle,LSP 49 | 50 | 继承必须确保超类所拥有的性质在子类中仍然成立 51 | Inheritance should ensure that any property proved about supertype objects also holds for subtype objects 52 | 53 | 里氏替换原则主要阐述了有关继承的一些原则,也就是什么时候应该使用继承,什么时候不应该使用继承,以及其中蕴含的原理。里氏替换原是继承复用的基础,它反映了基类与子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。 54 | 55 | 通俗讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。 56 | 比如:定义了一个 「鸟」 的接口,包含 「飞」 方法。燕子,老鹰可以顺利的继承这个接口,因为他们都属于鸟类,也都会飞,但是企鹅、鸵鸟和几维鸟从生物学的角度来划分,它们也属于鸟类,但它们并不会飞。根据里氏替换原则它们就不适合继承 「鸟」 接口。正确的做法是定义一个鸟与鸵鸟更一般的接口,比如动物接口,鸵鸟继承动物接口而非鸟接口 57 | 58 | ### 依赖倒置原则 59 | Dependence Inversion Principle,DIP 60 | 61 | 高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象 62 | High level modules shouldnot depend upon low level modules.Both should depend upon abstractions.Abstractions should not depend upon details. Details should depend upon abstractions 63 | 64 | 要面向接口编程,不要面向实现编程。 65 | 1. 每个类尽量提供接口或抽象类,或者两者都具备。 66 | 2. 变量的声明类型尽量是接口或者是抽象类。 67 | 3. 任何类都不应该从具体类派生。 68 | 4. 使用继承时尽量遵循里氏替换原则。 69 | 70 | ### 单一职责原则 71 | Single Responsibility Principle,SRP 72 | 73 | 对象不应该承担太多职责,如果一个对象承担了太多的职责,至少存在以下两个缺点: 74 | 1. 一个职责的变化可能会削弱或者抑制这个类实现其他职责的能力; 75 | 2. 当客户端需要该对象的某一个职责时,不得不将其他不需要的职责全都包含进来,从而造成冗余代码或代码的浪费。 76 | 77 | 单一职责原则的核心就是控制类的粒度大小、将对象解耦、提高其内聚性。如果遵循单一职责原则将有以下优点。 78 | * 降低类的复杂度。一个类只负责一项职责,其逻辑肯定要比负责多项职责简单得多。 79 | * 提高类的可读性。复杂性降低,自然其可读性会提高。 80 | * 提高系统的可维护性。可读性提高,那自然更容易维护了。 81 | * 变更引起的风险降低。变更是必然的,如果单一职责原则遵守得好,当修改一个功能时,可以显著降低对其他功能的影响。 82 | 83 | ### 接口隔离原则 84 | Interface Segregation Principle,ISP 85 | 86 | 要为各个类建立它们需要的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。 87 | 88 | ### 迪米特法则 89 | Law of Demeter,LoD 又叫做最少知识原则(Least Knowledge Principle,LKP) 90 | 91 | 如果两个软件实体无须直接通信,那么就不应当发生直接的相互调用,可以通过第三方转发该调用。其目的是降低类之间的耦合度,提高模块的相对独立性。 92 | 93 | 它可以降低了类之间的耦合度,提高了模块的相对独立性。 94 | 由于亲合度降低,从而提高了类的可复用率和系统的扩展性。 95 | 但是,过度使用迪米特法则会使系统产生大量的中介类,从而增加系统的复杂性,使模块之间的通信效率降低。所以,在釆用迪米特法则时需要反复权衡,确保高内聚和低耦合的同时,保证系统的结构清晰。 96 | 97 | ### 合成复用原则 98 | Composite Reuse Principle,CRP 又叫组合/聚合复用原则(Composition/Aggregate Reuse Principle,CARP) 99 | 100 | 软件复用时,要尽量先使用组合或者聚合等关联关系来实现,其次才考虑使用继承关系来实现。 101 | 如果要使用继承关系,则必须严格遵循里氏替换原则。合成复用原则同里氏替换原则相辅相成的,两者都是开闭原则的具体实现规范。 102 | -------------------------------------------------------------------------------- /design-patterns/责任链.md: -------------------------------------------------------------------------------- 1 | 责任链模式 Chain of Responsibility Pattern 2 | ------------- 3 | 4 | 重点等级::star::star::star::star: 5 | 6 | 在这种模式中,~~~通常每个接收者都包含对另一个接收者的引用~~~。如果一个对象不能处理该请求,那么它会把相同的请求传给下一个接收者,依此类推。 7 | > 我觉得对象中包含下一个需要处理的对象,这种形式不是很好,好多的框架源码使用时都有稍稍的改进,比如 Spring 也有使用责任链模式,他是将所有处理对象放进集合中,并使用可实现 `org.springframework.core.Ordered` 接口进行排序实现顺序的执行。 8 | 9 | 职责链上的处理者负责处理请求,用户只需要将请求发送到职责链上即可,无须关心请求的处理细节和请求的传递,所以职责链将请求的发送者和请求的处理者解耦了。 10 | 11 | ## 代码示例 12 | 上传文件到不同的平台 13 | 14 | ```java 15 | @Slf4j 16 | public class Test { 17 | 18 | public class Request { 19 | 20 | private final RequestType requestType; 21 | private final String requestDescription; 22 | private boolean handled; 23 | 24 | public Request(final RequestType requestType, final String requestDescription) { 25 | this.requestType = Objects.requireNonNull(requestType); 26 | this.requestDescription = Objects.requireNonNull(requestDescription); 27 | } 28 | 29 | public String getRequestDescription() { return requestDescription; } 30 | 31 | public RequestType getRequestType() { return requestType; } 32 | 33 | public void markHandled() { this.handled = true; } 34 | 35 | public boolean isHandled() { return this.handled; } 36 | 37 | @Override 38 | public String toString() { return getRequestDescription(); } 39 | } 40 | 41 | public enum RequestType { 42 | 本地存储, 阿里云存储, 腾讯云存储, 其他 43 | } 44 | 45 | public abstract class RequestHandler { 46 | private RequestHandler next; 47 | 48 | public RequestHandler(RequestHandler next) { 49 | this.next = next; 50 | } 51 | 52 | public void handleRequest(Request req) { 53 | if (next != null) { 54 | next.handleRequest(req); 55 | }else { 56 | log.error("{} 未找到合适的处理器",req); 57 | } 58 | } 59 | 60 | protected void printHandling(Request req) { 61 | log.info("{} 处理了 \"{}\"", this, req); 62 | } 63 | 64 | @Override 65 | public abstract String toString(); 66 | } 67 | 68 | public class Commander1 extends RequestHandler { 69 | public Commander1(RequestHandler handler) { 70 | super(handler); 71 | } 72 | 73 | @Override 74 | public void handleRequest(Request req) { 75 | if (req.getRequestType().equals(RequestType.本地存储)) { 76 | printHandling(req); 77 | req.markHandled(); 78 | } else { 79 | super.handleRequest(req); 80 | } 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return RequestType.本地存储.name(); 86 | } 87 | } 88 | public class Commander2 extends RequestHandler { 89 | public Commander2(RequestHandler handler) { 90 | super(handler); 91 | } 92 | 93 | @Override 94 | public void handleRequest(Request req) { 95 | if (req.getRequestType().equals(RequestType.腾讯云存储)) { 96 | printHandling(req); 97 | req.markHandled(); 98 | } else { 99 | super.handleRequest(req); 100 | } 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return RequestType.腾讯云存储.name(); 106 | } 107 | } 108 | public class Commander3 extends RequestHandler { 109 | public Commander3(RequestHandler handler) { 110 | super(handler); 111 | } 112 | 113 | @Override 114 | public void handleRequest(Request req) { 115 | if (req.getRequestType().equals(RequestType.阿里云存储)) { 116 | printHandling(req); 117 | req.markHandled(); 118 | } else { 119 | super.handleRequest(req); 120 | } 121 | } 122 | 123 | @Override 124 | public String toString() { 125 | return RequestType.阿里云存储.name(); 126 | } 127 | } 128 | public class Commander { 129 | RequestHandler chain; 130 | 131 | public Commander() { 132 | buildChain(); 133 | } 134 | 135 | private void buildChain() { 136 | chain = new Commander1(new Commander2(new Commander3(null))); 137 | } 138 | 139 | public void makeRequest(Request req) { 140 | chain.handleRequest(req); 141 | } 142 | } 143 | 144 | public void run(){ 145 | var king = new Commander(); 146 | king.makeRequest(new Request(RequestType.本地存储, "文件1")); 147 | king.makeRequest(new Request(RequestType.阿里云存储, "文件2")); 148 | king.makeRequest(new Request(RequestType.其他, "文件3")); 149 | king.makeRequest(new Request(RequestType.腾讯云存储, "文件4")); 150 | } 151 | 152 | public static void main(String[] args) throws Exception { 153 | Test test = new Test(); 154 | test.run(); 155 | } 156 | } 157 | // 输出 158 | INFO - 本地存储 处理了 "文件1" 159 | INFO - 阿里云存储 处理了 "文件2" 160 | ERROR - 文件3 未找到合适的处理器 161 | INFO - 腾讯云存储 处理了 "文件4" 162 | ``` 163 | 164 | ## JDK源码在的案例 165 | * `java.util.logging.Logger#log()` 166 | * (Apache Commons Chain)[https://commons.apache.org/proper/commons-chain/index.html] 167 | * `javax.servlet.Filter#doFilter()` 168 | > 可以延伸到 Spring Security 中的 `org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter()` 169 | -------------------------------------------------------------------------------- /design-patterns/迭代器.md: -------------------------------------------------------------------------------- 1 | Iterator 迭代器模式 2 | -------- 3 | 4 | 重点等级::star::star::star::star::star: 5 | 6 | 迭代器模式提供了一种方法顺序访问一个聚合对象中的各个元素,而又无需暴露该对象的内部实现,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。 7 | 8 | ## 场景 9 | * 访问一个集合对象的内容而无需暴露它的内部表示 10 | * 为遍历不同的集合结构提供一个统一的接口 11 | 12 | 13 | ## 代码示例 14 | 定义几个角色: 15 | * Iterator(抽象迭代器)。这里使用`java.util.Iterator`代替,也可以自己重新定义 16 | * BookIterator(具体迭代器) 17 | * Aggregate(抽象聚合类)。它用于存储和管理元素对象,充当抽象迭代器工厂角色。 18 | * BookAggregateImpl(具体聚合类) 19 | 20 | ```java 21 | public class Book { 22 | 23 | private String name; 24 | 25 | public Book(){ 26 | 27 | } 28 | public Book(String name){ 29 | this.name = name; 30 | } 31 | 32 | public String getName(){ 33 | return name; 34 | } 35 | } 36 | 37 | 38 | public class BookIterator implements Iterator { 39 | 40 | private Aggregate bookAggregate; 41 | private int index; 42 | 43 | public BookIterator(Aggregate bookAggregate){ 44 | this.bookAggregate = bookAggregate; 45 | index = 0; 46 | } 47 | 48 | @Override 49 | public boolean hasNext() { 50 | if (index < bookAggregate.length()) return true; 51 | return false; 52 | } 53 | 54 | @Override 55 | public Book next() { 56 | return bookAggregate.get(index++); 57 | } 58 | } 59 | 60 | 61 | 62 | public interface Aggregate { 63 | 64 | Iterator iterator(); 65 | 66 | T get(int i); 67 | 68 | void add (T t); 69 | 70 | int length(); 71 | } 72 | 73 | 74 | 75 | public class BookAggregateImpl implements Aggregate { 76 | 77 | private Book[] books; 78 | private int position; 79 | 80 | public BookAggregateImpl(int max_length){ 81 | books = new Book[max_length]; 82 | position = 0; 83 | } 84 | 85 | @Override 86 | public Iterator iterator() { 87 | return new BookIterator(this); 88 | } 89 | 90 | @Override 91 | public void add(Book t) { 92 | books[position++] = t; 93 | } 94 | 95 | @Override 96 | public Book get(int i) { 97 | return books[i]; 98 | } 99 | 100 | @Override 101 | public int length(){ 102 | return position; 103 | } 104 | } 105 | 106 | 107 | 108 | public static void main(String[] args) { 109 | 110 | Book book1 = new Book("book1"); 111 | Book book2 = new Book("book2"); 112 | Book book3 = new Book("book3"); 113 | Book book4 = new Book("book4"); 114 | Book book5 = new Book("book5"); 115 | 116 | Aggregate aggregate = new BookAggregateImpl(5); 117 | aggregate.add(book1); 118 | aggregate.add(book2); 119 | aggregate.add(book3); 120 | aggregate.add(book4); 121 | aggregate.add(book5); 122 | Iterator it = aggregate.iterator(); 123 | while (it.hasNext()) { 124 | Book book = it.next(); 125 | System.out.println(book.getName()); 126 | } 127 | } 128 | ``` 129 | 130 | ## JDK源码中的案例 131 | * `java.util.Iterator` 132 | * `java.util.Enumeration` 133 | -------------------------------------------------------------------------------- /design-patterns/适配器.md: -------------------------------------------------------------------------------- 1 | 适配器模式 Adapter 2 | ------- 3 | 4 | 重点等级::star::star::star: 5 | 6 | ## 场景 7 | * 拿到一个接口,不匹配你现有的类。 8 | > 支付宝支付,微信支付,银联支付,接口都不统一,利用适配器,适配成我们自己所需要的接口,在业务调用时无需关心不同的厂家,调用统一接口会适配不同的厂家。 9 | 10 | * 混合云,适配不同的云厂家 11 | * 大多使用第三方库的时候,都使用适配器作为自己应用与第三方库之间的中间层,以解决应用与第三方库之间的耦合问题。 12 | 13 | ## JDK源码中的案例 14 | * `javax.xml.bind.annotation.adapters.XmlAdapter` 15 | * `java.util.Collections#enumeration()` 16 | -------------------------------------------------------------------------------- /golang/协程并发控制.md: -------------------------------------------------------------------------------- 1 | # 协程并发控制方法 2 | 3 | ## 1、WaitGroup 4 | ```go 5 | func main() { 6 | var wg sync.WaitGroup 7 | 8 | wg.Add(2) 9 | go func() { 10 | time.Sleep(2*time.Second) 11 | fmt.Println("1号完成") 12 | wg.Done() 13 | }() 14 | go func() { 15 | time.Sleep(2*time.Second) 16 | fmt.Println("2号完成") 17 | wg.Done() 18 | }() 19 | wg.Wait() 20 | fmt.Println("好了,大家都干完了,放工") 21 | } 22 | ``` 23 | 24 | ## 2、chan + select 25 | ```go 26 | func main() { 27 | stop := make(chan bool) 28 | 29 | go func() { 30 | for { 31 | select { 32 | case <-stop: 33 | fmt.Println("监控退出,停止了...") 34 | return 35 | default: 36 | fmt.Println("goroutine监控中...") 37 | time.Sleep(2 * time.Second) 38 | } 39 | } 40 | }() 41 | 42 | time.Sleep(10 * time.Second) 43 | fmt.Println("可以了,通知监控停止") 44 | stop<- true 45 | //为了检测监控过是否停止,如果没有监控输出,就表示停止了 46 | time.Sleep(5 * time.Second) 47 | 48 | } 49 | ``` 50 | 51 | ## 3、Context 52 | 53 | ```go 54 | func main() { 55 | ctx, cancel := context.WithCancel(context.Background()) 56 | go func(ctx context.Context) { 57 | for { 58 | select { 59 | case <-ctx.Done(): 60 | fmt.Println("监控退出,停止了...") 61 | return 62 | default: 63 | fmt.Println("goroutine监控中...") 64 | time.Sleep(2 * time.Second) 65 | } 66 | } 67 | }(ctx) 68 | 69 | time.Sleep(10 * time.Second) 70 | fmt.Println("可以了,通知监控停止") 71 | cancel() 72 | //为了检测监控过是否停止,如果没有监控输出,就表示停止了 73 | time.Sleep(5 * time.Second) 74 | 75 | } 76 | ``` 77 | 78 | ```go 79 | func main() { 80 | ctx, cancel := context.WithCancel(context.Background()) 81 | go watch(ctx,"【监控1】") 82 | go watch(ctx,"【监控2】") 83 | go watch(ctx,"【监控3】") 84 | 85 | time.Sleep(10 * time.Second) 86 | fmt.Println("可以了,通知监控停止") 87 | cancel() 88 | //为了检测监控过是否停止,如果没有监控输出,就表示停止了 89 | time.Sleep(5 * time.Second) 90 | } 91 | 92 | func watch(ctx context.Context, name string) { 93 | for { 94 | select { 95 | case <-ctx.Done(): 96 | fmt.Println(name,"监控退出,停止了...") 97 | return 98 | default: 99 | fmt.Println(name,"goroutine监控中...") 100 | time.Sleep(2 * time.Second) 101 | } 102 | } 103 | } 104 | ``` 105 | 106 | ```go 107 | var key string="name" 108 | 109 | func main() { 110 | ctx, cancel := context.WithCancel(context.Background()) 111 | //附加值 112 | valueCtx:=context.WithValue(ctx,key,"【监控1】") 113 | go watch(valueCtx) 114 | time.Sleep(10 * time.Second) 115 | fmt.Println("可以了,通知监控停止") 116 | cancel() 117 | //为了检测监控过是否停止,如果没有监控输出,就表示停止了 118 | time.Sleep(5 * time.Second) 119 | } 120 | 121 | func watch(ctx context.Context) { 122 | for { 123 | select { 124 | case <-ctx.Done(): 125 | //取出值 126 | fmt.Println(ctx.Value(key),"监控退出,停止了...") 127 | return 128 | default: 129 | //取出值 130 | fmt.Println(ctx.Value(key),"goroutine监控中...") 131 | time.Sleep(2 * time.Second) 132 | } 133 | } 134 | } 135 | ``` 136 | -------------------------------------------------------------------------------- /hacker/blackchain-bug-parity-wallets.md: -------------------------------------------------------------------------------- 1 | Parity Wallet Multisig Hack 2 | ------- 3 | 4 | ## 第一次攻击 5 | 2017年7月19日,Parity multisig Contract 发生黑客事件。 6 | 7 | 攻击者从 Parity 多重签名合约中转走153,037 ETH 8 | > 相关交易 9 | https://etherscan.io/tx/0x9dbf0326a03a2a3719c27be4fa69aacc9857fd231a8d9dcaede4bb083def75ec 10 | https://etherscan.io/tx/0xeef10fc5170f669b86c4cd0444882a96087221325f8bf2f55d6188633aa7be7c 11 | https://etherscan.io/tx/0x97f7662322d56e1c54bd1bab39bccf98bc736fcb9c7e61640e6ff1f633637d38 12 | https://etherscan.io/tx/0x0e0d16475d2ac6a4802437a35a21776e5c9b681a77fef1693b0badbb6afdb083 13 | 14 | 可以看到第一个交易从 [`initWallet`](https://github.com/paritytech/parity/blob/4d08e7b0aec46443bf26547b17d10cb302672835/js/src/contracts/snippets/enhanced-wallet.sol#L216) 开始,它可以更改合约的所有者。 15 | 16 | ```javascript 17 | // constructor - just pass on the owner array to the multiowned and 18 | // the limit to daylimit 19 | function initWallet(address[] _owners, uint _required, uint _daylimit) { 20 | initDaylimit(_daylimit); 21 | initMultiowned(_owners, _required); 22 | } 23 | ``` 24 | 25 | 攻击者调用了合约的 `initWallet` 方法,为什么呢?主要是这个[`delegatecall`](https://solidity.readthedocs.io/en/v0.6.1/types.html#members-of-addresses)。 26 | 合约使用 `delegatecall` 将所有不匹配的函数调用转发给库,这使得来自库的所有公共函数都可以被任何人调用,包括 `initWallet` 27 | 28 | ```javascript 29 | function() payable { 30 | // just being sent some cash? 31 | if (msg.value > 0) 32 | Deposit(msg.sender, msg.value); 33 | else if (msg.data.length > 0) 34 | _walletLibrary.delegatecall(msg.data); 35 | } 36 | ``` 37 | 38 | ### 解决方案 39 | 解决方案也非常简单,添加了一个修饰限制 `initWallet` 方法,确保它只能在构建过程中被调用一次。 40 | 41 | ``` 42 | modifier only_uninitialized { if (m_numOwners > 0) throw; _; } 43 | ``` 44 | 45 | ## 第二次攻击 46 | 2017年11月7号,事隔4个月又一次攻击,估计损失可能超过500,000 ETH (1.5亿美元) ,其中包括来自 Web3基金会团队的300,000 ETH。 47 | 48 | 最新的攻击并不是针对单个的钱包合同,而是袭击了库合约,这将影响所有依赖于它的钱包合同。 49 | 50 | 11月7号一名 Github 用户 devops199 创建了一个[issues#6995](https://github.com/paritytech/parity-ethereum/issues/6995)ー“任何人都可以取消你的合同”。用户声称,他意外地杀死了合同。 51 | 52 | 这导致所有依赖于它的钱包合约,将所有调用指向了一个没有代码的合约地址(因为合约已经被 suicide),从而变得无法执行任何操作,因为它们的所有逻辑都依赖于库合约。 换句话说所有的钱包合同立即冻结。 53 | 54 | ## 最后 55 | 56 | 其实将逻辑抽象到一个库合约中的技术非常有用。有助于提高代码的可重用性,并降低气体部署成本。但是 bug 无处不在,学习历史,知道历史,是为了少踩坑。**前辈们的成功我们可能无法复制,但学习他们的失败可以帮助我们绕开那些坑** 57 | 58 | 59 | ------------- 60 | 相关报道 61 | 62 | * https://blog.openzeppelin.com/on-the-parity-wallet-multisig-hack-405a8c12e8f7/ 63 | * https://blog.openzeppelin.com/parity-wallet-hack-reloaded/ 64 | * https://blog.springrole.com/parity-multi-sig-wallets-funds-frozen-explained-768ac072763c 65 | -------------------------------------------------------------------------------- /hacker/github-bug.md: -------------------------------------------------------------------------------- 1 | GitHub Security Bug Bounty 2 | -------- 3 | [Github 安全漏洞赏金计划](https://bounty.github.com/) 4 | 5 | 记录 GitHub 历史 bug 。 6 | > 与成功学一样,一味地学习他人怎么成功是不可取的方法。成功路上的坎坷、失败、经验、教学更值得大家学习。 7 | 学习他人的错误,避免自己再犯错 8 | 9 | ## 绕开 GitHub OAuth 10 | [GitHub enterprise 2.17.3修复了此 bug](https://enterprise.github.com/releases/2.17.3/notes) 11 | 12 | ### 滥用 HTTP HEAD 请求 13 | 14 | [HTTP HEAD](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/HEAD) 自 HTTP 最初创建就一直存在,但好像开发者们对他使用的并不多。简单讲它只请求页面的首部,它与 GET 方法几乎是一样的,对于 HEAD 请求的回应部分来说,它的HTTP头部中包含的信息与通过 GET 请求所得到的信息是相同的。利用这个方法,不必传输整个资源内容,就可以得到 Request-URI 所标识的资源的信息。该方法常用于测试超链接的有效性,是否可以访问,以及最近是否更新,检查大文件大小(使用 Content-Length 响应头) 15 | 大多情况开发者不需要特意的处理它,因为像 `Rails` 以及其他的 web 框架一样,他们已经帮我们处理了,它们尝试将 HEAD 请求路由到一个地址一样的 GET 请求那里去处理,然后省略响应体。这确实是一个聪明的做法,并符合 HTTP HEAD 规范。 16 | 17 | 非常巧 github 认证正好符合这一点。GET 返回 html 认证页面,POST 请求授权应用,参考代码: 18 | 19 | ```ruby 20 | # In the router 21 | 22 | match "/login/oauth/authorize", # For every request with this path... 23 | :to => "[the controller]", # ...send it to the controller... 24 | :via => [:get, :post] # ... as long as it's a GET or a POST request. 25 | 26 | 27 | # In the controller 28 | 29 | if request.get? 30 | # serve authorization page HTML 31 | else 32 | # grant permissions to app 33 | end 34 | 35 | ``` 36 | 37 | 控制器根据 HTTP 请求 分别处理 GET 与 POST 看似并没有什么问题。 38 | 但是如果我们向 `https://github.com/login/oauth/authorize` 发出 `HEAD` 已认证请求,通过上面的分析,我们可以得知路由会将它当成 `GET` 请求来处理,但到了控制器时,他终究不是个 `GET` 所以他将执行本应 POST 执行的代码段,那么这个经过认证的假 POST 请求, GITHUB 将授权请求中指定应用并给与用户数据。在这里 `CSRF` 为什么没有起作用,因为 `HEAD` 请求没有这个限制。因此我们可以发送一个跨站并进过身份认证的 `HEAD` 请求,这个请求将授权任意 OAuth 权限,而不需要向用户展示确认页面。 39 | 40 | ### 参考 41 | * https://blog.teddykatz.com/2019/11/05/github-oauth-bypass.html 42 | * https://hackerone.com/hacktivity 43 | -------------------------------------------------------------------------------- /hacker/mail-security-SPF.md: -------------------------------------------------------------------------------- 1 | # SPF (Sender Policy Framework) 2 | SMTP(SimpleMail Transfer Protocol)即简单邮件传输协议,正如名字所暗示的那样,它其实是一个非常简单的传输协议,无需身份认证,而且发件人的邮箱地址是可以由发信方任意声明的,利用这个特性可以伪造任意发件人。 3 | 4 | [SPF](https://www.ietf.org/rfc/rfc4408.txt)一种以IP地址认证电子邮件发件人身份的技术,是为了防范垃圾邮件而提出来的一种DNS记录类型,它是一种TXT类型的记录。 接收邮件方会首先检查域名的SPF记录,来确定发件人的IP地址是否被包含在SPF记录里面,如果在,就认为是一封正确的邮件,否则会认为是一封伪造的邮件进行退回。 5 | 6 | ## 检测 7 | ```shell 8 | ➜ ~ dig -t txt 163.com 9 | 10 | ; <<>> DiG 9.10.6 <<>> -t txt 163.com 11 | ;; global options: +cmd 12 | ;; Got answer: 13 | ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33760 14 | ;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 7, ADDITIONAL: 1 15 | 16 | ;; OPT PSEUDOSECTION: 17 | ; EDNS: version: 0, flags:; udp: 1280 18 | ;; QUESTION SECTION: 19 | ;163.com. IN TXT 20 | 21 | ;; ANSWER SECTION: 22 | 163.com. 18000 IN TXT "google-site-verification=hRXfNWRtd9HKlh-ZBOuUgGrxBJh526R8Uygp0jEZ9wY" 23 | 163.com. 18000 IN TXT "v=spf1 include:spf.163.com -all" 24 | 163.com. 18000 IN TXT "qdx50vkxg6qpn3n1k6n1tg2syg5wp96y" 25 | 163.com. 18000 IN TXT "57c23e6c1ed24f219803362dadf8dea3" 26 | 27 | ;; AUTHORITY SECTION: 28 | 163.com. 172800 IN NS ns8.166.com. 29 | 163.com. 172800 IN NS ns1.nease.net. 30 | 163.com. 172800 IN NS ns2.166.com. 31 | 163.com. 172800 IN NS ns3.nease.net. 32 | 163.com. 172800 IN NS ns4.nease.net. 33 | 163.com. 172800 IN NS ns5.nease.net. 34 | 163.com. 172800 IN NS ns6.nease.net. 35 | 36 | ;; Query time: 53 msec 37 | ;; SERVER: 240e:472:8010:7fb::f#53(240e:472:8010:7fb::f) 38 | ;; WHEN: Tue Nov 03 20:57:54 CST 2020 39 | ;; MSG SIZE rcvd: 390 40 | ``` 41 | 42 | 在结果中发现存在**v=spf1 include:spf.163.com** 表示配置了SFP记录 43 | 44 | 检测工具[SPF Record Testing Tools](https://www.kitterman.com/spf/validate.html) 45 | 46 | 47 | ## 伪造工具 48 | Swaks(SWiss Army Knife Smtp) 49 | https://github.com/jetmore/swaks 50 | -------------------------------------------------------------------------------- /hacker/web-security.md: -------------------------------------------------------------------------------- 1 | # Web 安全 2 | * [SQL 注入](https://github.com/ning1022/SQLInjectionWiki) 3 | 4 | # Docker file 5 | * [dockerfile-security-best-practices](https://cloudberry.engineering/article/dockerfile-security-best-practices/) 6 | -------------------------------------------------------------------------------- /head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ifuture-pro/developer-notes/5d2f02297673a343693a38fe5d79f34db7db3b53/head.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Developer Notes 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 22 | 23 | 24 | 25 |
Developer notes
26 | 27 | 57 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /solution/渲染字体库到图片识别输出文字.md: -------------------------------------------------------------------------------- 1 | # 渲染字体库到图片并识别图片输出具体文字 2 | 3 | > 主要为一个网友需要抓取斗鱼主播的关注人数,但斗鱼采用特殊手段进行反爬虫。 4 | > https://github.com/cj1128/douyu-crawler-demo 5 | > 在这里记录关键知识点 6 | 7 | ## 反爬虫混淆字体 8 | ### 字体格式 9 | * ttf 10 | * otf 11 | * woff 是一个包装格式,里面的字体不是 ttf 就是 otf 的 12 | * ttx XML可读字体 13 | 14 | ### 字体操作 15 | * [fonttools](https://github.com/fonttools/fonttools) 这个强大的字体操作 Python 库 16 | * [fonttools - pyftsubset](https://fonttools.readthedocs.io/en/latest/subset/index.html?highlight=pyftsubset) 17 | * [FontEditor](http://fontstore.baidu.com/static/editor/) 18 | 19 | ```shell 20 | # 裁剪字体自需要 0 ~ 9 21 | pyftsubset hack.ttf --text="0123456789" 22 | ``` 23 | 24 | ```shell 25 | # 转换字体为可读的 ttx 格式 26 | # 使用 fonttools 自带了一个工具叫做 ttx 27 | ttx hack.subset.ttf 28 | Dumping "hack.subset.ttf" to "hack.subset.ttx"... 29 | 30 | # 打开可读的 ttx 修改映射关系,混淆比如输入1显示成6等 31 | 32 | # 导出 ttx 到 ttf 33 | ttx -o fake.ttf fake.ttx 34 | Compiling "fake.ttx" to "fake.ttf"... 35 | ``` 36 | 37 | 或使用代码形式 38 | ```Python 39 | #!/usr/bin/env python 40 | # 生成用于数字混淆的字体文件用于反爬 41 | # 即字体对于数字的渲染是错误的,例如数字 1 会渲染成 5 42 | # ./genfont.py 43 | # 生成字体在 result/generated 目录中 44 | 45 | import sys 46 | import os 47 | import subprocess 48 | from pathlib import Path 49 | import random 50 | from bs4 import BeautifulSoup 51 | import copy 52 | import hashlib 53 | 54 | names = ["zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"] 55 | 56 | # must contain glyphs with name "zero" "one" .. "nine" 57 | def check_font(ttx): 58 | for name in names: 59 | if ttx.find("TTGlyph", attrs={"name": name}) is None: 60 | return False 61 | 62 | return True 63 | 64 | def gen(ttx): 65 | mapping = names[:] 66 | random.shuffle(mapping) 67 | 68 | target = copy.copy(ttx) 69 | 70 | for name in names: 71 | target.find("TTGlyph", {"name": name})["id"] = name 72 | 73 | for idx, name in enumerate(names): 74 | tmp = target.find("TTGlyph", attrs={"id": mapping[idx]}) 75 | 76 | tmp.attrs = {} 77 | 78 | for k, v in ttx.find("TTGlyph", attrs={"name": name}).attrs.items(): 79 | tmp[k] = v 80 | 81 | content = target.prettify() 82 | name = hashlib.md5(content.encode("utf8")).hexdigest()[:10] + "." + "".join([str(names.index(x)) for x in mapping]) 83 | 84 | print(f"Generate temporary ttx: {name}.ttx") 85 | target_ttx_path = os.path.join("result", "tmp", f"{name}.ttx") 86 | with open(target_ttx_path, "w") as f: 87 | f.write(content) 88 | 89 | target_ttf_path = os.path.join("result", "generated", f"{name}.ttf") 90 | print(f"Generate target ttf: {target_ttf_path}") 91 | subprocess.run(f"ttx -o {target_ttf_path} {target_ttx_path}", shell=True, check=True) 92 | 93 | def run(font_file, count): 94 | ttx_name = os.path.splitext(font_file)[0] + ".ttx" 95 | ttx_path = os.path.join("result", "tmp", ttx_name) 96 | 97 | if not Path(ttx_path).exists(): 98 | print("Convert ttf to ttx..") 99 | subprocess.run(f"ttx -o {ttx_path} {font_file}", shell=True, check=True) 100 | 101 | with open(ttx_path) as f: 102 | ttx = BeautifulSoup(f, "xml") 103 | 104 | if not check_font(ttx): 105 | print("font must contain glyphs with name 'zero', 'one', 'two' .. 'nine'") 106 | exit(1) 107 | 108 | for _ in range(count): 109 | gen(ttx) 110 | 111 | if __name__ == "__main__": 112 | if len(sys.argv) < 3: 113 | print(f"usage: ./genfont.py ") 114 | exit(1) 115 | 116 | # create necessary dirs 117 | os.makedirs(os.path.join("result", "generated"), exist_ok=True) 118 | os.makedirs(os.path.join("result", "tmp"), exist_ok=True) 119 | 120 | run(sys.argv[1], int(sys.argv[2])) 121 | ``` 122 | 123 | ## 破解 124 | ### HAR HTTP Archive format 125 | 126 | 是用来记录浏览器加载网页时所消耗的时间的工具 127 | 128 | 使用浏览器打开,或 http://www.softwareishard.com/har/viewer/ 129 | 130 | 131 | ### 需要js处理后的网页爬虫方案 132 | * [Selenium](https://www.selenium.dev/) 133 | * [Puppeteer](https://github.com/puppeteer/puppeteer) 134 | 135 | ### 在网页中加载代码 136 | * [Tampermonkey](https://www.tampermonkey.net/) 137 | 138 | 监听任意 DOM 的修改事件,通过使用 `MutationObserver` 139 | ```javascript 140 | new MutationObserver((mutations, observer) => { 141 | const el = document.querySelector("span.Title-followNum") 142 | if (el != null) { 143 | observer.disconnect() 144 | new MutationObserver((mutations, observer) => { 145 | debugger 146 | }).observe(el, {childList: true, subtree: true}) 147 | } 148 | }).observe(document, {childList: true, subtree: true}) 149 | ``` 150 | 151 | ### 渲染字体到图片 152 | * [SDL](http://www.libsdl.org/) 153 | * [go-SDL2](https://github.com/veandco/go-sdl2) 154 | 155 | ```c++ 156 | #include 157 | #include 158 | #include 159 | #include 160 | 161 | int 162 | main(void) 163 | { 164 | if(TTF_Init() == -1) { 165 | printf("error: %s\n", TTF_GetError()); 166 | return 1; 167 | } 168 | 169 | TTF_Font *font = TTF_OpenFont("test.woff", 50); 170 | if(font == NULL) { 171 | printf("error: %s\n", TTF_GetError()); 172 | return 1; 173 | } 174 | 175 | SDL_Color black = { 0x00, 0x00, 0x00 }; 176 | SDL_Surface *surface = TTF_RenderText_Solid(font, "0123456789", black); 177 | if(surface == NULL) { 178 | printf("error: %s\n", TTF_GetError()); 179 | return 1; 180 | } 181 | 182 | IMG_SavePNG(surface, "test.png"); 183 | return 0; 184 | } 185 | ``` 186 | 187 | ### 文字识别 188 | 189 | OCR, Optical Character Recognition, 光学字符识别 190 | 191 | * [EasyOCR](https://github.com/JaidedAI/EasyOCR) 192 | * [tesseract](https://github.com/tesseract-ocr/tesseract) 193 | * [tesseract AddOns](https://tesseract-ocr.github.io/tessdoc/AddOns) 194 | * [go tesseract](https://github.com/otiai10/gosseract) 195 | -------------------------------------------------------------------------------- /sw.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * docsify sw.js 3 | * =========================================================== 4 | * Copyright 2016 @huxpro 5 | * Licensed under Apache 2.0 6 | * Register service worker. 7 | * chrome://serviceworker-internals/ 8 | * ========================================================== */ 9 | 10 | const RUNTIME = 'docsify' 11 | const HOSTNAME_WHITELIST = [ 12 | self.location.hostname, 13 | 'fonts.gstatic.com', 14 | 'fonts.googleapis.com', 15 | 'unpkg.com' 16 | ] 17 | 18 | // The Util Function to hack URLs of intercepted requests 19 | const getFixedUrl = (req) => { 20 | var now = Date.now() 21 | var url = new URL(req.url) 22 | 23 | // 1. fixed http URL 24 | // Just keep syncing with location.protocol 25 | // fetch(httpURL) belongs to active mixed content. 26 | // And fetch(httpRequest) is not supported yet. 27 | url.protocol = self.location.protocol 28 | 29 | // 2. add query for caching-busting. 30 | // Github Pages served with Cache-Control: max-age=600 31 | // max-age on mutable content is error-prone, with SW life of bugs can even extend. 32 | // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string. 33 | // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190 34 | if (url.hostname === self.location.hostname) { 35 | url.search += (url.search ? '&' : '?') + 'cache-bust=' + now 36 | } 37 | return url.href 38 | } 39 | 40 | /** 41 | * @Lifecycle Activate 42 | * New one activated when old isnt being used. 43 | * 44 | * waitUntil(): activating ====> activated 45 | */ 46 | self.addEventListener('activate', event => { 47 | event.waitUntil(self.clients.claim()) 48 | }) 49 | 50 | /** 51 | * @Functional Fetch 52 | * All network requests are being intercepted here. 53 | * 54 | * void respondWith(Promise r) 55 | */ 56 | self.addEventListener('fetch', event => { 57 | // Skip some of cross-origin requests, like those for Google Analytics. 58 | if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) { 59 | // Stale-while-revalidate 60 | // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale 61 | // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1 62 | const cached = caches.match(event.request) 63 | const fixedUrl = getFixedUrl(event.request) 64 | const fetched = fetch(fixedUrl, { cache: 'no-store' }) 65 | const fetchedCopy = fetched.then(resp => resp.clone()) 66 | 67 | // Call respondWith() with whatever we get first. 68 | // If the fetch fails (e.g disconnected), wait for the cache. 69 | // If there’s nothing in cache, wait for the fetch. 70 | // If neither yields a response, return offline pages. 71 | event.respondWith( 72 | Promise.race([fetched.catch(_ => cached), cached]) 73 | .then(resp => resp || fetched) 74 | .catch(_ => { /* eat any errors */ }) 75 | ) 76 | 77 | // Update the cache with the version we fetched (only for ok status) 78 | event.waitUntil( 79 | Promise.all([fetchedCopy, caches.open(RUNTIME)]) 80 | .then(([response, cache]) => response.ok && cache.put(event.request, response)) 81 | .catch(_ => { /* eat any errors */ }) 82 | ) 83 | } 84 | }) -------------------------------------------------------------------------------- /web/CSS.md: -------------------------------------------------------------------------------- 1 | # CSS 2 | * clamp() 3 | ```CSS 4 | img { 5 | width: clamp(15vw, 800%, 100%); 6 | } 7 | h1 { 8 | font-size: clamp(20px, 5vw, 35px); 9 | } 10 | p { 11 | font-size: clamp(10px, 4vw, 20px); 12 | } 13 | ``` 14 | [clamp(最小值, 属性值, 最大值)](https://dev.to/dip15739/responsive-website-with-only-1-css-property-3ea9) 比 min() 和 max() 都方便 15 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # 汇编 2 | ## WebAssembly 3 | “性能大战”诞生了 Just-in-time(JIT) 编译器,许多浏览器引入了 [JIT](https://segmentfault.com/a/1190000008632441) 使得 JavaScript 的性能达到了一转折点,JS 的执行速度快了10陪。也使 JavaScript 进入了以前根本没想过的领域,比如后端开发 NodeJS 。现在通过 WebAssembly 它很有可能进入第二个转折点。 4 | ## asm.js 5 | -------------------------------------------------------------------------------- /web/tools.md: -------------------------------------------------------------------------------- 1 | ## 简单暴露服务 2 | ``` 3 | npm install -g serve 4 | cd project_dir 5 | serve . 6 | # https://ngrok.com/ 7 | ngrok http 3000 8 | ``` 9 | --------------------------------------------------------------------------------