├── .gitattributes ├── .gitbook.yaml ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── .gitignore ├── .meta ├── ABOUT.md └── SUMMARY.md ├── .nojekyll ├── 01~服务器 ├── Apache │ └── README.md ├── Caddy │ └── README.md ├── Nginx │ ├── HTTP 服务器配置.md │ ├── README.md │ ├── 基础配置.md │ └── 进程模型.md ├── OpenResty │ └── README.md └── README.md ├── 04~接入网关 ├── 02~负载均衡 │ ├── LVS │ │ └── README.md │ ├── README.md │ ├── Scratch │ │ ├── README.md │ │ └── 基于 Go 的简单负载均衡.md │ ├── 分流算法.md │ └── 负载分层.md ├── 04~接入网关 │ └── Kong │ │ └── Kong.md ├── 05~长连接服务 │ ├── DeepStream │ │ └── DeepStream.md │ ├── README.md │ ├── WebSocket 网关 │ │ └── README.md │ └── 多机房多网络.md ├── 10~实践案例 │ ├── 2021-喜马拉雅-自研网关架构演进过程.md │ ├── 2021-爱奇艺-基于 Netty 的长连接网关.md │ ├── 2022-How Tinder Built Their Own API Gateway.md │ └── Scratch │ │ ├── README.md │ │ ├── 基于 Netty 与 Webflux 的网关 │ │ └── README.md │ │ └── 网关特性.md └── README.md ├── INTRODUCTION.md ├── LICENSE ├── README.md ├── _sidebar.md └── index.html /.gitattributes: -------------------------------------------------------------------------------- 1 | *.xmind filter=lfs diff=lfs merge=lfs -text 2 | *.zip filter=lfs diff=lfs merge=lfs -text 3 | *.pdf filter=lfs diff=lfs merge=lfs -text 4 | -------------------------------------------------------------------------------- /.gitbook.yaml: -------------------------------------------------------------------------------- 1 | root: ./ 2 | 3 | structure: 4 | readme: ./README.md 5 | summary: ./.meta/SUMMARY.md 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | - OS: [e.g. iOS] 29 | - Browser [e.g. chrome, safari] 30 | - Version [e.g. 22] 31 | 32 | **Smartphone (please complete the following information):** 33 | 34 | - Device: [e.g. iPhone6] 35 | - OS: [e.g. iOS8.1] 36 | - Browser [e.g. stock browser, safari] 37 | - Version [e.g. 22] 38 | 39 | **Additional context** 40 | Add any other context about the problem here. 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore all 2 | * 3 | 4 | # Unignore all with extensions 5 | !*.* 6 | 7 | # Unignore all dirs 8 | !*/ 9 | 10 | .DS_Store 11 | 12 | # Logs 13 | logs 14 | *.log 15 | npm-debug.log* 16 | yarn-debug.log* 17 | yarn-error.log* 18 | 19 | # Runtime data 20 | pids 21 | *.pid 22 | *.seed 23 | *.pid.lock 24 | 25 | # Directory for instrumented libs generated by jscoverage/JSCover 26 | lib-cov 27 | 28 | # Coverage directory used by tools like istanbul 29 | coverage 30 | 31 | # nyc test coverage 32 | .nyc_output 33 | 34 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 35 | .grunt 36 | 37 | # Bower dependency directory (https://bower.io/) 38 | bower_components 39 | 40 | # node-waf configuration 41 | .lock-wscript 42 | 43 | # Compiled binary addons (https://nodejs.org/api/addons.html) 44 | build/Release 45 | 46 | # Dependency directories 47 | node_modules/ 48 | jspm_packages/ 49 | 50 | # TypeScript v1 declaration files 51 | typings/ 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Optional REPL history 60 | .node_repl_history 61 | 62 | # Output of 'npm pack' 63 | *.tgz 64 | 65 | # Yarn Integrity file 66 | .yarn-integrity 67 | 68 | # dotenv environment variables file 69 | .env 70 | 71 | # next.js build output 72 | .next 73 | -------------------------------------------------------------------------------- /.meta/ABOUT.md: -------------------------------------------------------------------------------- 1 | [](https://github.com/wx-chevalier/SoftwareEngineering-Notes) 2 | 3 | # About 4 | -------------------------------------------------------------------------------- /.meta/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | ## [协作与项目管理](../协作与项目管理/README.md) 4 | 5 | - [Introduction](../协作与项目管理/README.md) 6 | 7 | - [Git](../协作与项目管理/Git/README.md) 8 | - [GitLab](../协作与项目管理/Git/GitLab/README.md) 9 | - [CI](../协作与项目管理/Git/GitLab/CI.md) 10 | - [Github](../协作与项目管理/Git/Github/README.md) 11 | - [Workflow](../协作与项目管理/Git/Workflow/README.md) 12 | - [功能分支](../协作与项目管理/Git/Workflow/功能分支.md) 13 | - [基础](../协作与项目管理/Git/基础/README.md) 14 | - [Rebase](../协作与项目管理/Git/基础/Rebase.md) 15 | - [仓库](../协作与项目管理/Git/基础/仓库.md) 16 | - [分支](../协作与项目管理/Git/基础/分支.md) 17 | - [提交](../协作与项目管理/Git/基础/提交.md) 18 | - [运行机制](../协作与项目管理/Git/基础/运行机制.md) 19 | - [IDE](../协作与项目管理/IDE/README.md) 20 | - [OnlineIDE](../协作与项目管理/IDE/OnlineIDE/README.md) 21 | - [VSCode](../协作与项目管理/IDE/VSCode/README.md) 22 | - [断点调试](../协作与项目管理/IDE/VSCode/断点调试.md) 23 | - [Markdown](../协作与项目管理/Markdown/README.md) 24 | - [图表](../协作与项目管理/Markdown/图表.md) 25 | - [大厂架构](../协作与项目管理/大厂架构/README.md) 26 | - [阿里](../协作与项目管理/大厂架构/阿里.md) 27 | - [开源社区](../协作与项目管理/开源社区/README.md) 28 | - [开源协议](../协作与项目管理/开源社区/开源协议.md) 29 | - [技术管理](../协作与项目管理/技术管理/README.md) 30 | - [技术管理](../协作与项目管理/技术管理/技术管理.md) 31 | - [需求管理](../协作与项目管理/技术管理/需求管理.md) 32 | - [项目规划](../协作与项目管理/技术管理/项目规划.md) 33 | - [技术趋势](../协作与项目管理/技术趋势/README.md) 34 | - [持续交付](../协作与项目管理/持续交付/README.md) 35 | - [敏捷开发](../协作与项目管理/敏捷开发/README.md) 36 | - [Scrum](../协作与项目管理/敏捷开发/Scrum/README.md) 37 | - [XP](../协作与项目管理/敏捷开发/XP/README.md) 38 | - [瀑布流开发](../协作与项目管理/瀑布流开发/README.md) 39 | - [版本管理](../协作与项目管理/版本管理/README.md) 40 | - [绩效管理](../协作与项目管理/绩效管理/README.md) 41 | - [KPI](../协作与项目管理/绩效管理/KPI/README.md) 42 | - [OKR](../协作与项目管理/绩效管理/OKR/README.md) 43 | - [软件交付](../协作与项目管理/软件交付/README.md) 44 | 45 | ## [数据结构与算法](../数据结构与算法/README.md) 46 | 47 | - [Introduction](../数据结构与算法/README.md) 48 | 49 | - [LeetCode](../数据结构与算法/LeetCode/README.md) 50 | - [剑指 Offer](../数据结构与算法/剑指 Offer/README.md) 51 | - [动态规划](../数据结构与算法/动态规划/README.md) 52 | - [动态规划概述](../数据结构与算法/动态规划/动态规划概述.md) 53 | - [股票买卖](../数据结构与算法/动态规划/股票买卖.md) 54 | - [背包问题](../数据结构与算法/动态规划/背包问题.md) 55 | - [压缩](../数据结构与算法/压缩/README.md) 56 | - [GZip](../数据结构与算法/压缩/GZip.md) 57 | - [哈希](../数据结构与算法/哈希/README.md) 58 | - [HashTable](../数据结构与算法/哈希/HashTable/README.md) 59 | - [哈希函数](../数据结构与算法/哈希/哈希函数/README.md) 60 | - [C++](../数据结构与算法/哈希/哈希函数/C++.md) 61 | - [Java](../数据结构与算法/哈希/哈希函数/Java.md) 62 | - [碰撞解决](../数据结构与算法/哈希/哈希函数/碰撞解决.md) 63 | - [过滤器](../数据结构与算法/哈希/过滤器/README.md) 64 | - [BloomFilter](../数据结构与算法/哈希/过滤器/BloomFilter.md) 65 | - [CuckooFilter](../数据结构与算法/哈希/过滤器/CuckooFilter.md) 66 | - [图论](../数据结构与算法/图论/README.md) 67 | - [图基础](../数据结构与算法/图论/图基础/README.md) 68 | - [图遍历](../数据结构与算法/图论/图遍历/README.md) 69 | - [广度优先搜索](../数据结构与算法/图论/图遍历/广度优先搜索.md) 70 | - [深度优先搜索](../数据结构与算法/图论/图遍历/深度优先搜索.md) 71 | - [最小生成树](../数据结构与算法/图论/最小生成树/README.md) 72 | - [最小生成树](../数据结构与算法/图论/最小生成树/最小生成树.md) 73 | - [最短路径](../数据结构与算法/图论/最短路径/README.md) 74 | - [最短路径](../数据结构与算法/图论/最短路径/最短路径.md) 75 | - [字符串](../数据结构与算法/字符串/README.md) 76 | - [回文字符串](../数据结构与算法/字符串/回文字符串.md) 77 | - [正则表达式](../数据结构与算法/字符串/正则表达式.md) 78 | - [Trie](../数据结构与算法/字符串/Trie/README.md) 79 | - [模式匹配](../数据结构与算法/字符串/模式匹配/README.md) 80 | - [单模式匹配](../数据结构与算法/字符串/模式匹配/单模式匹配.md) 81 | - [多模式匹配](../数据结构与算法/字符串/模式匹配/多模式匹配.md) 82 | - [导论](../数据结构与算法/导论/README.md) 83 | - [复杂度分析](../数据结构与算法/导论/复杂度分析.md) 84 | - [数据的结构](../数据结构与算法/导论/数据的结构.md) 85 | - [排序](../数据结构与算法/排序/README.md) 86 | - [Java](../数据结构与算法/排序/Java.md) 87 | - [REAMDE](../数据结构与算法/排序/REAMDE.md) 88 | - [分治排序](../数据结构与算法/排序/分治排序.md) 89 | - [分配排序](../数据结构与算法/排序/分配排序.md) 90 | - [外部排序](../数据结构与算法/排序/外部排序.md) 91 | - [树形排序](../数据结构与算法/排序/树形排序.md) 92 | - [简单排序](../数据结构与算法/排序/简单排序.md) 93 | - [搜索回溯](../数据结构与算法/搜索回溯/README.md) 94 | - [数组](../数据结构与算法/数组/README.md) 95 | - [数列](../数据结构与算法/数组/数列/README.md) 96 | - [斐波那契数列](../数据结构与算法/数组/数列/斐波那契数列.md) 97 | - [滑动窗口](../数据结构与算法/数组/滑动窗口/README.md) 98 | - [矩阵](../数据结构与算法/数组/矩阵/README.md) 99 | - [时间日期](../数据结构与算法/时间日期/README.md) 100 | - [查找](../数据结构与算法/查找/README.md) 101 | - [二分查找](../数据结构与算法/查找/二分查找.md) 102 | - [二叉查找树](../数据结构与算法/查找/二叉查找树.md) 103 | - [预排序遍历树](../数据结构与算法/查找/预排序遍历树.md) 104 | - [AVL](../数据结构与算法/查找/AVL/README.md) 105 | - [AVL](../数据结构与算法/查找/AVL/AVL.md) 106 | - [B-Tree](../数据结构与算法/查找/B-Tree/README.md) 107 | - [B+Tree](../数据结构与算法/查找/B-Tree/B+Tree.md) 108 | - [B-Tree](../数据结构与算法/查找/B-Tree/B-Tree.md) 109 | - [红黑树](../数据结构与算法/查找/红黑树/README.md) 110 | - [红黑树](../数据结构与算法/查找/红黑树/红黑树.md) 111 | - [栈与队列](../数据结构与算法/栈与队列/README.md) 112 | - [LRU](../数据结构与算法/栈与队列/LRU.md) 113 | - [多级反馈队列](../数据结构与算法/栈与队列/多级反馈队列.md) 114 | - [树](../数据结构与算法/树/README.md) 115 | - [二叉树](../数据结构与算法/树/二叉树/README.md) 116 | - [堆](../数据结构与算法/树/堆/README.md) 117 | - [Java](../数据结构与算法/树/堆/Java.md) 118 | - [堆](../数据结构与算法/树/堆/堆.md) 119 | - [简单数论](../数据结构与算法/简单数论/README.md) 120 | - [位运算](../数据结构与算法/简单数论/位运算.md) 121 | - [大数操作](../数据结构与算法/简单数论/大数操作.md) 122 | - [数组序列](../数据结构与算法/简单数论/数组序列.md) 123 | - [矩阵乘法](../数据结构与算法/简单数论/矩阵乘法.md) 124 | - [科学计算](../数据结构与算法/简单数论/科学计算.md) 125 | - [索引](../数据结构与算法/索引/README.md) 126 | - [LSM-Tree](../数据结构与算法/索引/LSM-Tree.md) 127 | - [全文索引](../数据结构与算法/索引/全文索引.md) 128 | - [动态全文索引](../数据结构与算法/索引/动态全文索引.md) 129 | - [文件索引](../数据结构与算法/索引/文件索引.md) 130 | - [线性表](../数据结构与算法/线性表/README.md) 131 | - [SkipList](../数据结构与算法/线性表/SkipList.md) 132 | - [列表](../数据结构与算法/线性表/列表.md) 133 | - [跳表](../数据结构与算法/线性表/跳表.md) 134 | - [链表](../数据结构与算法/线性表/链表.md) 135 | - [集合](../数据结构与算法/线性表/集合.md) 136 | 137 | ## [整洁与重构](../整洁与重构/README.md) 138 | 139 | - [Introduction](../整洁与重构/README.md) 140 | 141 | - [Code Review](../整洁与重构/Code Review/README.md) 142 | - [Go](../整洁与重构/Go/README.md) 143 | - [Java](../整洁与重构/Java/README.md) 144 | - [PHP](../整洁与重构/PHP/README.md) 145 | - [Python](../整洁与重构/Python/README.md) 146 | - [Rust](../整洁与重构/Rust/README.md) 147 | - [TS](../整洁与重构/TS/README.md) 148 | - [函数重构](../整洁与重构/函数重构/README.md) 149 | - [坏味道](../整洁与重构/坏味道/README.md) 150 | - [对象重构](../整洁与重构/对象重构/README.md) 151 | - [数据重构](../整洁与重构/数据重构/README.md) 152 | - [整洁代码](../整洁与重构/整洁代码/README.md) 153 | - [异常处理](../整洁与重构/整洁代码/异常处理.md) 154 | - [重复代码](../整洁与重构/整洁代码/重复代码.md) 155 | - [条件选择重构](../整洁与重构/条件选择重构/README.md) 156 | - [if-else](../整洁与重构/条件选择重构/if-else.md) 157 | - [基于策略与工厂模式的重构](../整洁与重构/条件选择重构/基于策略与工厂模式的重构.md) 158 | - [概括关系重构](../整洁与重构/概括关系重构/README.md) 159 | - [注释](../整洁与重构/注释/README.md) 160 | - [代码的自解释性](../整洁与重构/注释/代码的自解释性.md) 161 | - [函数注释](../整洁与重构/注释/函数注释.md) 162 | - [过程重构](../整洁与重构/过程重构/README.md) 163 | 164 | ## [编程范式与设计模式](../编程范式与设计模式/README.md) 165 | 166 | - [Introduction](../编程范式与设计模式/README.md) 167 | 168 | - [Go](../编程范式与设计模式/Go/README.md) 169 | - [Java](../编程范式与设计模式/Java/README.md) 170 | - [依赖注入](../编程范式与设计模式/Java/依赖注入.md) 171 | - [创建型模式](../编程范式与设计模式/Java/创建型模式.md) 172 | - [单例](../编程范式与设计模式/Java/单例.md) 173 | - [PHP](../编程范式与设计模式/PHP/README.md) 174 | - [Python](../编程范式与设计模式/Python/README.md) 175 | - [Rust](../编程范式与设计模式/Rust/README.md) 176 | - [SOLID](../编程范式与设计模式/SOLID/README.md) 177 | - [依赖倒置](../编程范式与设计模式/SOLID/依赖倒置.md) 178 | - [单一职责](../编程范式与设计模式/SOLID/单一职责.md) 179 | - [开放封闭](../编程范式与设计模式/SOLID/开放封闭.md) 180 | - [接口隔离](../编程范式与设计模式/SOLID/接口隔离.md) 181 | - [最少知识](../编程范式与设计模式/SOLID/最少知识.md) 182 | - [里氏替换](../编程范式与设计模式/SOLID/里氏替换.md) 183 | - [TS](../编程范式与设计模式/TS/README.md) 184 | - [SOLID](../编程范式与设计模式/TS/SOLID.md) 185 | - [事件驱动编程](../编程范式与设计模式/事件驱动编程/README.md) 186 | - [元编程](../编程范式与设计模式/元编程/README.md) 187 | - [其他模式](../编程范式与设计模式/其他模式/README.md) 188 | - [函数式编程](../编程范式与设计模式/函数式编程/README.md) 189 | - [函数式编程导论](../编程范式与设计模式/函数式编程/函数式编程导论.md) 190 | - [函数组合](../编程范式与设计模式/函数式编程/函数组合.md) 191 | - [创建型模式](../编程范式与设计模式/创建型模式/README.md) 192 | - [单例](../编程范式与设计模式/创建型模式/单例.md) 193 | - [原型](../编程范式与设计模式/创建型模式/原型.md) 194 | - [工厂方法](../编程范式与设计模式/创建型模式/工厂方法.md) 195 | - [抽象工厂](../编程范式与设计模式/创建型模式/抽象工厂.md) 196 | - [构建器](../编程范式与设计模式/创建型模式/构建器.md) 197 | - [反应式编程](../编程范式与设计模式/反应式编程/README.md) 198 | - [函数反应式编程](../编程范式与设计模式/反应式编程/函数反应式编程.md) 199 | - [反压](../编程范式与设计模式/反应式编程/反压.md) 200 | - [基础范式](../编程范式与设计模式/基础范式/README.md) 201 | - [命令式编程](../编程范式与设计模式/基础范式/命令式编程.md) 202 | - [声明式编程](../编程范式与设计模式/基础范式/声明式编程.md) 203 | - [结构型模式](../编程范式与设计模式/结构型模式/README.md) 204 | - [享元](../编程范式与设计模式/结构型模式/享元.md) 205 | - [代理](../编程范式与设计模式/结构型模式/代理.md) 206 | - [外观](../编程范式与设计模式/结构型模式/外观.md) 207 | - [桥接](../编程范式与设计模式/结构型模式/桥接.md) 208 | - [组合](../编程范式与设计模式/结构型模式/组合.md) 209 | - [装饰](../编程范式与设计模式/结构型模式/装饰.md) 210 | - [适配器](../编程范式与设计模式/结构型模式/适配器.md) 211 | - [行为型模式](../编程范式与设计模式/行为型模式/README.md) 212 | - [中介者](../编程范式与设计模式/行为型模式/中介者.md) 213 | - [命令](../编程范式与设计模式/行为型模式/命令.md) 214 | - [备忘录](../编程范式与设计模式/行为型模式/备忘录.md) 215 | - [模板方法](../编程范式与设计模式/行为型模式/模板方法.md) 216 | - [状态](../编程范式与设计模式/行为型模式/状态.md) 217 | - [策略](../编程范式与设计模式/行为型模式/策略.md) 218 | - [职责链](../编程范式与设计模式/行为型模式/职责链.md) 219 | - [观察者](../编程范式与设计模式/行为型模式/观察者.md) 220 | - [访问者](../编程范式与设计模式/行为型模式/访问者.md) 221 | - [迭代器](../编程范式与设计模式/行为型模式/迭代器.md) 222 | - [面向对象编程](../编程范式与设计模式/面向对象编程/README.md) 223 | - [OOP 的缺陷](../编程范式与设计模式/面向对象编程/OOP 的缺陷.md) 224 | - [继承与组合](../编程范式与设计模式/面向对象编程/继承与组合.md) 225 | 226 | ## [软件架构设计](../软件架构设计/README.md) 227 | 228 | - [Introduction](../软件架构设计/README.md) 229 | 230 | - [GUI 应用程序架构](../软件架构设计/GUI 应用程序架构/README.md) 231 | - [单向数据流](../软件架构设计/GUI 应用程序架构/单向数据流/README.md) 232 | - [Clean Architecture](../软件架构设计/GUI 应用程序架构/单向数据流/Clean Architecture.md) 233 | - [Flux](../软件架构设计/GUI 应用程序架构/单向数据流/Flux.md) 234 | - [双向数据流](../软件架构设计/GUI 应用程序架构/双向数据流/README.md) 235 | - [MVC](../软件架构设计/GUI 应用程序架构/双向数据流/MVC.md) 236 | - [MVP](../软件架构设计/GUI 应用程序架构/双向数据流/MVP.md) 237 | - [MVVM](../软件架构设计/GUI 应用程序架构/双向数据流/MVVM.md) 238 | - [组件化](../软件架构设计/GUI 应用程序架构/组件化/README.md) 239 | - [Atomic Design](../软件架构设计/GUI 应用程序架构/组件化/Atomic Design.md) 240 | - [TOGAF](../软件架构设计/TOGAF/README.md) 241 | - [UML](../软件架构设计/UML/README.md) 242 | - [PlantUML](../软件架构设计/UML/PlantUML.md) 243 | - [时序图](../软件架构设计/UML/时序图.md) 244 | - [类关系](../软件架构设计/UML/类关系.md) 245 | - [类图](../软件架构设计/UML/类图.md) 246 | - [WebAPI](../软件架构设计/WebAPI/README.md) 247 | - [API 的过去,现在与未来](../软件架构设计/WebAPI/API 的过去,现在与未来.md) 248 | - [WebAPI 风格变迁](../软件架构设计/WebAPI/WebAPI 风格变迁.md) 249 | - [OpenAPI](../软件架构设计/WebAPI/OpenAPI/README.md) 250 | - [中台](../软件架构设计/中台/README.md) 251 | - [业务定义](../软件架构设计/中台/业务定义.md) 252 | - [业务挑战](../软件架构设计/中台/业务挑战.md) 253 | - [业务模型](../软件架构设计/中台/业务模型.md) 254 | - [中台架构](../软件架构设计/中台/中台架构.md) 255 | - [大厂中台](../软件架构设计/中台/大厂中台/README.md) 256 | - [阿里](../软件架构设计/中台/大厂中台/阿里.md) 257 | - [技术负债](../软件架构设计/技术负债/README.md) 258 | - [架构图](../软件架构设计/架构图/README.md) 259 | - [C4](../软件架构设计/架构图/C4.md) 260 | - [架构图分类](../软件架构设计/架构图/架构图分类.md) 261 | - [架构域与复杂性](../软件架构设计/架构域与复杂性/README.md) 262 | - [复杂性应对](../软件架构设计/架构域与复杂性/复杂性应对.md) 263 | - [复杂性来源](../软件架构设计/架构域与复杂性/复杂性来源.md) 264 | - [架构域划分](../软件架构设计/架构域与复杂性/架构域划分.md) 265 | - [腐败的软件系统](../软件架构设计/架构域与复杂性/腐败的软件系统.md) 266 | - [架构师](../软件架构设计/架构师/README.md) 267 | - [架构框架](../软件架构设计/架构框架/README.md) 268 | - [DODAF](../软件架构设计/架构框架/DODAF.md) 269 | - [ITSA](../软件架构设计/架构框架/ITSA.md) 270 | - [TOGAF](../软件架构设计/架构框架/TOGAF.md) 271 | - [Zachman](../软件架构设计/架构框架/Zachman.md) 272 | - [架构模式](../软件架构设计/架构模式/README.md) 273 | - [CQRS](../软件架构设计/架构模式/CQRS/README.md) 274 | - [EventSourcing](../软件架构设计/架构模式/CQRS/EventSourcing.md) 275 | - [Spring 与 CQRS](../软件架构设计/架构模式/CQRS/Spring 与 CQRS.md) 276 | - [EDA](../软件架构设计/架构模式/EDA/README.md) 277 | - [UDLA](../软件架构设计/架构模式/UDLA/README.md) 278 | - [COLA](../软件架构设计/架构模式/UDLA/COLA/README.md) 279 | - [RARF](../软件架构设计/架构模式/UDLA/RARF/README.md) 280 | - [AARF-ch](../软件架构设计/架构模式/UDLA/RARF/AARF-ch.md) 281 | - [AARF-en](../软件架构设计/架构模式/UDLA/RARF/AARF-en.md) 282 | - [RARF.en](../软件架构设计/架构模式/UDLA/RARF/RARF.en.md) 283 | - [RARF](../软件架构设计/架构模式/UDLA/RARF/RARF.md) 284 | - [类 Redux 的代码组织](../软件架构设计/架构模式/UDLA/RARF/类 Redux 的代码组织.md) 285 | - [扩展](../软件架构设计/架构模式/UDLA/扩展/README.md) 286 | - [反应式](../软件架构设计/架构模式/UDLA/扩展/反应式.md) 287 | - [扩展点](../软件架构设计/架构模式/UDLA/扩展/扩展点.md) 288 | - [业务逻辑架构](../软件架构设计/架构模式/业务逻辑架构/README.md) 289 | - [事务脚本](../软件架构设计/架构模式/业务逻辑架构/事务脚本.md) 290 | - [活动记录](../软件架构设计/架构模式/业务逻辑架构/活动记录.md) 291 | - [表模块](../软件架构设计/架构模式/业务逻辑架构/表模块.md) 292 | - [架构设计原则](../软件架构设计/架构设计原则/README.md) 293 | - [不确定性原则](../软件架构设计/架构设计原则/不确定性原则.md) 294 | - [单源一致性原则](../软件架构设计/架构设计原则/单源一致性原则.md) 295 | - [可测试性原则](../软件架构设计/架构设计原则/可测试性原则.md) 296 | - [复用与依赖原则](../软件架构设计/架构设计原则/复用与依赖原则.md) 297 | - [抽象与隔离原则](../软件架构设计/架构设计原则/抽象与隔离原则.md) 298 | - [架构思维](../软件架构设计/架构设计原则/架构思维.md) 299 | - [架构风格](../软件架构设计/架构风格/README.md) 300 | - [CRUD](../软件架构设计/架构风格/CRUD/README.md) 301 | - [REST](../软件架构设计/架构风格/REST/README.md) 302 | - [Microsoft API 设计标准](../软件架构设计/架构风格/REST/Microsoft API 设计标准.md) 303 | - [Paypal API 设计标准](../软件架构设计/架构风格/REST/Paypal API 设计标准.md) 304 | - [RESTful 接口](../软件架构设计/架构风格/REST/RESTful 接口.md) 305 | - [分层架构](../软件架构设计/架构风格/分层架构/README.md) 306 | - [六边形架构](../软件架构设计/架构风格/分层架构/六边形架构.md) 307 | - [洋葱架构](../软件架构设计/架构风格/分层架构/洋葱架构.md) 308 | - [软件工程](../软件架构设计/软件工程/README.md) 309 | - [代码与工程化](../软件架构设计/软件工程/代码与工程化.md) 310 | - [领域驱动设计](../软件架构设计/领域驱动设计/README.md) 311 | - [代码架构](../软件架构设计/领域驱动设计/代码架构/README.md) 312 | - [分层架构](../软件架构设计/领域驱动设计/代码架构/分层架构.md) 313 | - [基础理念](../软件架构设计/领域驱动设计/基础理念/README.md) 314 | - [名词与术语](../软件架构设计/领域驱动设计/基础理念/名词与术语.md) 315 | - [数据视图](../软件架构设计/领域驱动设计/基础理念/数据视图.md) 316 | - [领域事件](../软件架构设计/领域驱动设计/基础理念/领域事件.md) 317 | - [领域服务](../软件架构设计/领域驱动设计/基础理念/领域服务.md) 318 | - [应用案例](../软件架构设计/领域驱动设计/应用案例/README.md) 319 | - [银行转账](../软件架构设计/领域驱动设计/应用案例/银行转账.md) 320 | - [设计与建模](../软件架构设计/领域驱动设计/设计与建模/README.md) 321 | - [问题域分析](../软件架构设计/领域驱动设计/设计与建模/问题域分析.md) 322 | - [领域划分](../软件架构设计/领域驱动设计/设计与建模/领域划分.md) 323 | - [领域协作](../软件架构设计/领域驱动设计/设计与建模/领域协作.md) 324 | - [领域模型](../软件架构设计/领域驱动设计/设计与建模/领域模型.md) 325 | -------------------------------------------------------------------------------- /.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/System-Notes/66ed1f216d8988b2294f59f7490fc6f4739ae97a/.nojekyll -------------------------------------------------------------------------------- /01~服务器/Apache/README.md: -------------------------------------------------------------------------------- 1 | # Apache 2 | -------------------------------------------------------------------------------- /01~服务器/Caddy/README.md: -------------------------------------------------------------------------------- 1 | # 清新脱俗的 Web 服务器 Caddy 2 | 3 | 作为新兴 Web 服务器,Caddy 提供了很多简单易用的功能而没有历史的包袱,其默认支持并且能帮你自动配置 HTTP/2、HTTPS,对于 IPV6、WebSockets 都有很好的支持。基于 Go 编写的 Caddy 天生对于多核具有很好的支持,并且其丰富的插件系统提供了文件管理、文件上传、基于 MarkDown 的博客系统等等开箱即用的扩展功能。我们可以在[官方下载界面](https://caddyserver.com/download)选择你需要的插件功能定制个性化二进制文件,下载完毕之后即可以使用`caddy`命令直接运行。其默认监听 2015 端口,在浏览器中打开 [http://localhost:2015](http://localhost:2015) 即可以查看其运行情况。我们也可以通过`-conf`参数指定配置文件: 4 | 5 | ``` 6 | $ caddy -conf="/path/to/Caddyfile" 7 | ``` 8 | 9 | 下文我们会详细介绍 Caddyfile 的配置语法,Caddy 的一大特性在于其使用所谓指令(Directives)来描述功能进行配置,相较于 Nginx 或者 Apache 其配置会简化很多。如果我们希望支持多配置文件,可以使用`import`指令: 10 | 11 | ``` 12 | import config/common.conf 13 | ``` 14 | 15 | 或者引入整个文件夹: 16 | 17 | ``` 18 | import ../vhosts/* 19 | ``` 20 | 21 | # 站点配置 22 | 23 | 典型的 Caddyfile 配置文件如下所示: 24 | 25 | ``` 26 | localhost 27 | 28 | 29 | gzip 30 | browse 31 | websocket /echo cat 32 | ext.html 33 | log/var/log/access.log 34 | proxy/api 127.0.0.1:7005 35 | header /api Access-Control-Allow-Origin * 36 | ``` 37 | 38 | 每个 Caddyfile 的第一行必须描述其服务的地址: 39 | 40 | ``` 41 | localhost:2020 42 | ``` 43 | 44 | 之后的每一行都是官方提供的指令,譬如我们需要为服务器添加 gzip 压缩支持,只需要直接添加一个指令: 45 | 46 | ``` 47 | localhost:2020 48 | gzip 49 | ``` 50 | 51 | 我们可以使用 `bind` 指令来指定当前服务器绑定的地址: 52 | 53 | ``` 54 | bind host 55 | 56 | bind 127.0.0.1 57 | ``` 58 | 59 | ## 虚拟主机 60 | 61 | 如果我们需要配置独立的虚拟主机,需要将配置信息移动到站点名之后的大括号内: 62 | 63 | ``` 64 | mysite.com { 65 | root /www/mysite.com 66 | } 67 | 68 | 69 | sub.mysite.com { 70 | root /www/sub.mysite.com 71 | gzip 72 | log ../access.log 73 | } 74 | ``` 75 | 76 | 注意,左括号必须与站点名位于同一行,而右括号则是必须单起一行。对于共享相同配置的站点,我们可以用逗号来声明多个站点: 77 | 78 | ``` 79 | localhost:2020, https://site.com, http://mysite.com { 80 | ... 81 | } 82 | ``` 83 | 84 | 当 Caddy 检测到站点名符合下列条件时会自动使用 Let's Encrypt 脚本来为站点添加 HTTPS 支持,并且自动监听 80 与 443 端口: 85 | 86 | - 主机名不可为空并且没有 localhost 与 IP 地址 87 | - 端口号未明确指定为 80 88 | - Scheme 未明确指定为 http 89 | - TLS 未被关闭 90 | - 未指明证书 91 | 92 | ## 缓存设置 93 | 94 | 我们可以通过 `expires` 指令来设置相较于请求时间的过期头,其基本语法为: 95 | 96 | ```yml 97 | expires { 98 | match regex duration 99 | } 100 | ``` 101 | 102 | regex 是用于匹配请求文件的正则表达式,而 duration 则是 0y0m0d0h0i0s 格式的描述时长的表达式,常用的匹配语法为: 103 | 104 | ``` 105 | expires { 106 | match some/path/.*.css$ 1y # expires 107 | css files in some/path after one year 108 | match .js$ 1m # expires 109 | js files after 30 days 110 | match .png$ 1d # expires 111 | png files after one day 112 | match .jpg$ 1h # expires 113 | jpg files after one hour 114 | match .pdf$ 1i # expires 115 | pdf file after one minute 116 | match .txt$ 1s # expires 117 | txt files after one second 118 | match .html$ 5i30s # expires 119 | html files after 5 minutes 30 seconds 120 | } 121 | ``` 122 | 123 | ## 反向代理 124 | 125 | `proxy` 指令提供了基本的反向代理功能,其支持 Health Checks 以及 Failovers,并且支持对于 WebSocket 的反向代理。其基本语法为: 126 | 127 | ``` 128 | proxy from to 129 | ``` 130 | 131 | from 即是请求匹配的基本路径,to 则是请求转发到的端点地址。我们也可以使用更复杂的配置: 132 | 133 | ``` 134 | proxy from to... { 135 | policy random | least_conn | round_robin | ip_hash 136 | fail_timeout duration 137 | max_fails integer 138 | try_duration duration 139 | try_interval duration 140 | health_check path 141 | health_check_interval interval_duration 142 | health_check_timeout timeout_duration 143 | header_upstream name value 144 | header_downstream name value 145 | keepalive number 146 | without prefix 147 | except ignored_paths... 148 | upstream to 149 | insecure_skip_verify 150 | preset 151 | } 152 | ``` 153 | 154 | 将所有发往 /api 的请求转发到后端系统: 155 | 156 | ``` 157 | # 这里会将完整的路径转发到后端,如果需要过滤掉前缀,则是以 without 指令 158 | proxy /api localhost:9005 159 | ``` 160 | 161 | 使用随机策略将所有请求负载均衡到三个后端服务器: 162 | 163 | ``` 164 | proxy / web1.local:80 web2.local:90 web3.local:100 165 | ``` 166 | 167 | 使用循环机制: 168 | 169 | ``` 170 | proxy / web1.local:80 web2.local:90 web3.local:100 { 171 | policy round_robin 172 | } 173 | ``` 174 | 175 | 添加健康检查并且透明转发主机名、地址与上游: 176 | 177 | ``` 178 | proxy / web1.local:80 web2.local:90 web3.local:100 { 179 | policy round_robin 180 | health_check /health 181 | transparent 182 | } 183 | ``` 184 | 185 | 转发 WebSocket 请求: 186 | 187 | ``` 188 | proxy /stream localhost:8080 { 189 | websocket 190 | } 191 | ``` 192 | 193 | 避免对于部分静态请求的转发: 194 | 195 | ``` 196 | proxy / backend:1234 { 197 | # 这里取消 /static /robots.txt 方向的请求 198 | except /static /robots.txt 199 | } 200 | ``` 201 | 202 | 或者首先进行静态资源解析,然后进行二次跳转: 203 | 204 | ``` 205 | 0.0.0.0 206 | root /srv/build/ 207 | log / stdout 208 | rewrite {path} /proxy/{path} 209 | proxy /proxy backend { 210 | without /proxy 211 | } 212 | ``` 213 | 214 | ## WebSocket 215 | 216 | Caddy 内建支持 WebSocket 连接,其允许客户端发起 WebSocket 连接的时候客户端执行某个简单的指令,其基本语法如下: 217 | 218 | ``` 219 | websocket [path] command 220 | ``` 221 | 222 | 我们可以在客户端内构建简单的 WebSocket 客户端请求: 223 | 224 | ```js 225 | if (window.WebSocket != undefined) { 226 | var connection = new WebSocket("ws://localhost:2015/echo"); 227 | connection.onmessage = wsMessage; 228 | 229 | connection.onopen = wsOpen; 230 | 231 | function wsOpen(event) { 232 | connection.send("Hello World"); 233 | } 234 | function wsMessage(event) { 235 | console.log(event.data); 236 | } 237 | } 238 | function wsMessage(event) { 239 | console.log(event.data); 240 | } 241 | ``` 242 | 243 | 然后在服务端接收该请求并且将客户端输入的内容返回: 244 | 245 | ```js 246 | var readline = require("readline"); 247 | var rl = readline.createInterface({ 248 | input: process.stdin, 249 | output: process.stdout, 250 | terminal: false, 251 | }); 252 | 253 | rl.on("line", function (line) { 254 | console.log(line); 255 | }); 256 | ``` 257 | 258 | 最后 Caddy 文件配置如下: 259 | 260 | ``` 261 | websocket /echo "node tmp.js" 262 | ``` 263 | 264 | ## 文件上传 265 | 266 | 我们可以使用 Caddy 提供的扩展指令 upload 来搭建简单的文件上传服务器: 267 | 268 | ``` 269 | upload path { 270 | to"directory" 271 | yes_without_tls 272 | filenames_formnone|NFC|NFD 273 | filenames_inu0000–uff00 [u0000–uff00| …] 274 | hmac_keys_inkeyID_0=base64(binary) [keyID_n=base64(binary) | …] 275 | timestamp_tolerance 0..32 276 | silent_auth_errors 277 | } 278 | ``` 279 | 280 | 直接添加如下配置: 281 | 282 | ``` 283 | upload /web/path { 284 | to "/var/tmp" 285 | } 286 | ``` 287 | 288 | 然后使用 curl 上传文件: 289 | 290 | ``` 291 | # HTTP PUT 292 | curl \ 293 | -T /etc/os-release \ 294 | https://127.0.0.1/web/path/from-release 295 | ``` 296 | 297 | 或者同时上传多个文件: 298 | 299 | ``` 300 | # HTTP POST 301 | curl \ 302 | -F gitconfig=@.gitconfig \ 303 | -F id_ed25519.pub=@.ssh/id_ed25519.pub \ 304 | https://127.0.0.1/web/path/ 305 | ``` 306 | 307 | 我们也可以使用指令来移动或者删除这些文件: 308 | 309 | ``` 310 | # MOVE is 'mv' 311 | curl -X MOVE \ 312 | -H "Destination: /web/path/to-release" \ 313 | https://127.0.0.1/web/path/from-release 314 | 315 | 316 | # DELETE is 'rm -r' 317 | curl -X DELETE \ 318 | https://127.0.0.1/web/path/to-release 319 | ``` 320 | 321 | # 访问控制 322 | 323 | ## 权限认证 324 | 325 | ### Basic Auth 326 | 327 | Caddy 内建支持 HTTP Basic Authentication,能够强制用户使用指定的用户名与密码访问某些目录或者文件。其基本配置语法如下: 328 | 329 | ``` 330 | basicauth username password { 331 | resources 332 | } 333 | ``` 334 | 335 | 如果我们希望为 /secret 目录下所有文件添加权限认证: 336 | 337 | ``` 338 | basicauth /secret Bob hiccup 339 | ``` 340 | 341 | 也可以指明某些文件: 342 | 343 | ``` 344 | basicauth "Mary Lou" milkshakes { 345 | /notes-for-mary-lou.txt 346 | /marylou-files 347 | /another-file.txt 348 | } 349 | ``` 350 | 351 | ### JWT 352 | 353 | jwt 指令是 Caddy 的扩展功能,我们需要在官网上选择添加该功能并且获取编译后的版本,其基本语法为: 354 | 355 | ``` 356 | jwt path 357 | // 或者 358 | jwt { 359 | pathresource 360 | allow claim value 361 | denyclaim value 362 | } 363 | ``` 364 | 365 | 譬如我们预设了两个令牌:`user: someone` 与 `role: member`,我们的配置项如下: 366 | 367 | ``` 368 | jwt { 369 | path/protected 370 | denyrole member 371 | allow user someone 372 | } 373 | ``` 374 | 375 | 该中间件会拒绝所有 `role: member` 的访问,除了用户名为 `someone` 的用户。而另一个 `role: admin` 或者 `role: foo` 的用户则可以正常访问。我们可以通过三种方式来提交令牌: 376 | 377 | | Method | Format | 378 | | -------------------- | ----------------------------- | 379 | | Authorization Header | Authorization: Bearer _token_ | 380 | | Cookie | "jwt*token": \_token* | 381 | | URL Query Parameter | /protected?token=_token_ | 382 | 383 | ## 跨域请求 384 | 385 | 我们可以使用 cors 指令来为服务器添加跨域请求的能力: 386 | 387 | ``` 388 | cors / { 389 | originhttp://allowedSite.com 390 | originhttp://anotherSite.org https://anotherSite.org 391 | methods POST,PUT 392 | allow_credentials false 393 | max_age 3600 394 | allowed_headers X-Custom-Header,X-Foobar 395 | exposed_headers X-Something-Special,SomethingElse 396 | } 397 | ``` 398 | 399 | 我们也可以添加 JSONP 的支持: 400 | 401 | ``` 402 | jsonp /api/status 403 | ``` 404 | 405 | 譬如某个端点返回类似于`{"status":"ok"}`这样的 JSON 响应,请求格式如下: 406 | 407 | ``` 408 | $ wget 'http://example.com/api/status?callback=func3022933' 409 | ``` 410 | 411 | 其会返回如下格式的响应: 412 | 413 | ``` 414 | func3022933({"status":"ok"}); 415 | ``` 416 | 417 | ## 地址过滤 418 | 419 | 我们可以使用 ipfilter 指令来基于用户的 IP 来允许或者限制用户访问,其基本语法为: 420 | 421 | ``` 422 | ipfilter paths... { 423 | rule block | allow 424 | ip list or/and range of IPs... 425 | countrycountries ISO codes... 426 | database db_path 427 | blockpageblock_page 428 | strict 429 | } 430 | ``` 431 | 432 | 仅允许某个 IP 访问: 433 | 434 | ``` 435 | ipfilter / { 436 | rule allow 437 | ip 93.168.247.245 438 | } 439 | ``` 440 | 441 | 禁止两段 IP 地址与某个具体的 IP 访问,并且向他们返回默认界面: 442 | 443 | ``` 444 | ipfilter / { 445 | rule block 446 | ip 192.168.0.0/16 2E80::20:F8FF:FE31:77CF/16 5.23.4.24 447 | blockpage/local/data/default.html 448 | } 449 | ``` 450 | 451 | 仅允许来自法国的视野固定 IP 地址的客户端访问: 452 | 453 | ``` 454 | ipfilter / { 455 | ruleallow 456 | country FR 457 | database/local/data/GeoLite2-Country.mmdb 458 | ip99.23.4.24 2E80::20::FEF1:91C4 459 | } 460 | ``` 461 | 462 | 仅支持来自美国与日本的客户端访问: 463 | 464 | ``` 465 | ipfilter / { 466 | ruleallow 467 | country US JP 468 | database/local/data/GeoLite2-Country.mmdb 469 | } 470 | ``` 471 | 472 | 禁止来自美国与日本的客户端对于/notglobal 与 /secret 的访问,直接返回默认地址: 473 | 474 | ``` 475 | ipfilter /notglobal /secret { 476 | rule block 477 | countryUS JP 478 | database /local/data/GeoLite2-Country.mmdb 479 | blockpage/local/data/default.html 480 | } 481 | ``` 482 | 483 | ## 请求限流 484 | 485 | 我们可以使用 ratelimit 这个扩展指令来为资源添加请求限流的功能,对于单资源可以使用如下指令: 486 | 487 | ``` 488 | ratelimit path rate burst unit 489 | 490 | // 限制客户端每秒最多对于 /r 资源发起两个请求,突发上限最多为 3 个 491 | ratelimit /r 2 3 second 492 | ``` 493 | 494 | 对于多资源可以使用如下指令: 495 | 496 | ``` 497 | ratelimit rate burst unit { 498 | resources 499 | } 500 | // 限制对于资源文件的访问时长为 2 分钟 501 | ratelimit 2 2 minute { 502 | /foo.html 503 | /dir 504 | } 505 | ``` 506 | -------------------------------------------------------------------------------- /01~服务器/Nginx/HTTP 服务器配置.md: -------------------------------------------------------------------------------- 1 | # Nginx 初窥与部署 2 | 3 | # 环境部署 4 | 5 | ```s 6 | # CentOS 7 | yum install nginx; 8 | # Ubuntu 9 | sudo apt-get install nginx; 10 | # Mac 11 | brew install nginx; 12 | ``` 13 | 14 | ## Management 15 | 16 | ``` 17 | # 启动 18 | nginx -s start; 19 | # 重新启动,热启动,修改配置重启不影响线上 20 | nginx -s reload; 21 | # 关闭 22 | nginx -s stop; 23 | # 修改配置后,可以通过下面的命令测试是否有语法错误 24 | nginx -t; 25 | ``` 26 | 27 | ### Start 28 | 29 | ``` 30 | cd usr/local/nginx/sbin 31 | ./nginx 32 | ``` 33 | 34 | ### Reload 35 | 36 | ``` 37 | 更改配置重启nginx 38 | kill -HUP 主进程号或进程号文件路径 39 | 或者使用 40 | cd /usr/local/nginx/sbin 41 | ./nginx -s reload 42 | 判断配置文件是否正确 43 | nginx -t -c /usr/local/nginx/conf/nginx.conf 44 | 或者 45 | cd /usr/local/nginx/sbin 46 | ./nginx -t 47 | ``` 48 | 49 | ### Stop 50 | 51 | 查询 nginx 主进程号   ps -ef | grep nginx 52 |   从容停止 kill -QUIT 主进程号  快速停止 kill -TERM 主进程号  强制停止 kill -9 nginx 53 |   若 nginx.conf 配置了 pid 文件路径,如果没有,则在 logs 目录下   kill -信号类型 '/usr/local/nginx/logs/nginx.pid' 54 | 55 | ## Configuration 56 | 57 | ``` 58 | events { 59 | # 需要保留这一个段落,可以为空 60 | } 61 | http { 62 | server { 63 | listen 127.0.0.1:8888; 64 | location / { 65 | root /home/barret/test/; 66 | } 67 | } 68 | } 69 | ``` 70 | 71 | # Basic Configuration(基本配置) 72 | 73 | ## 运行配置 74 | 75 | `#定义 Nginx 运行的用户和用户组 76 | 77 | user www www; ` 78 | 79 | ``` 80 | #nginx进程数,建议设置为等于CPU总核心数。 81 | 82 | worker_processes 8; 83 | #全局错误日志定义类型,[ debug | info | notice | warn | error | crit ] 84 | 85 | error_log /var/log/nginx/error.log info; 86 | #进程文件 87 | 88 | pid /var/run/nginx.pid; 89 | #一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(系统的值ulimit -n)与nginx进程数相除,但是nginx分配请求并不均匀,所以建议与ulimit -n的值保持一致。 90 | 91 | worker_rlimit_nofile 65535; 92 | #工作模式与连接数上限 93 | 94 | events 95 | 96 | { 97 | 98 | #参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型是Linux 2.6以上版本内核中的高性能网络IO模型,如果跑在FreeBSD上面,就用kqueue模型。 99 | 100 | use epoll; 101 | 102 | #单个进程最大连接数(最大连接数=连接数*进程数) 103 | 104 | worker_connections 65535; 105 | 106 | } 107 | ``` 108 | 109 | ## HTTP 服务器配置 110 | 111 | ``` 112 | 设定http服务器 113 | http 114 | { 115 | include mime.types; #文件扩展名与文件类型映射表 116 | default_type application/octet-stream; #默认文件类型 117 | #charset utf-8; #默认编码 118 | server_names_hash_bucket_size 128; #服务器名字的hash表大小 119 | client_header_buffer_size 32k; #上传文件大小限制 120 | large_client_header_buffers 4 64k; #设定请求缓 121 | client_max_body_size 8m; #设定请求缓 122 | sendfile on; #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络IO处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。 123 | autoindex on; #开启目录列表访问,合适下载服务器,默认关闭。 124 | tcp_nopush on; #防止网络阻塞 125 | tcp_nodelay on; #防止网络阻塞 126 | keepalive_timeout 120; #长连接超时时间,单位是秒 127 | 128 | #FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。下面参数看字面意思都能理解。 129 | fastcgi_connect_timeout 300; 130 | fastcgi_send_timeout 300; 131 | fastcgi_read_timeout 300; 132 | fastcgi_buffer_size 64k; 133 | fastcgi_buffers 4 64k; 134 | fastcgi_busy_buffers_size 128k; 135 | fastcgi_temp_file_write_size 128k; 136 | 137 | #gzip模块设置 138 | gzip on; #开启gzip压缩输出 139 | gzip_min_length 1k; #最小压缩文件大小 140 | gzip_buffers 4 16k; #压缩缓冲区 141 | gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0) 142 | gzip_comp_level 2; #压缩等级 143 | gzip_types text/plain application/x-javascript text/css application/xml; 144 | #压缩类型,默认就已经包含text/html,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。 145 | gzip_vary on; 146 | #limit_zone crawler $binary_remote_addr 10m; #开启限制IP连接数的时候需要使用 147 | } 148 | ``` 149 | 150 | ## 内置全局变量 151 | 152 | 名称 版本 说明(变量列表来源于文件 ngx_http_variables ) 153 | 154 | \$args 1.0.8 请求中的参数; 155 | 156 | \$binary_remote_addr 1.0.8 远程地址的二进制表示 157 | 158 | \$body_bytes_sent 1.0.8 已发送的消息体字节数 159 | 160 | \$content_length 1.0.8 HTTP 请求信息里的"Content-Length"; 161 | 162 | \$content_type 1.0.8 请求信息里的"Content-Type"; 163 | 164 | \$document_root 1.0.8 针对当前请求的根路径设置值; 165 | 166 | $document_uri 1.0.8 与$uri 相同; 比如 /test1/test2/test.php 167 | 168 | \$host 1.0.8 请求信息中的"Host",如果请求中没有 Host 行,则等于设置的服务器名; 169 | 170 | \$hostname 1.0.8 171 | 172 | \$http_cookie 1.0.8 cookie 信息 173 | 174 | \$http_post 1.0.8 175 | 176 | \$http_referer 1.0.8 引用地址 177 | 178 | \$http_user_agent 1.0.8 客户端代理信息 179 | 180 | \$http_via 1.0.8 最后一个访问服务器的 Ip 地址。http://www.cnblogs.com/deng02/archive/2009/02/11/1387911.html 181 | 182 | \$http_x_forwarded_for 1.0.8 相当于网络访问路径。http://www.cnblogs.com/craig/archive/2008/11/18/1335809.html 183 | 184 | \$is_args 1.0.8 185 | 186 | \$limit_rate 1.0.8 对连接速率的限制; 187 | 188 | \$nginx_version 1.0.8 189 | 190 | \$pid 1.0.8 191 | 192 | $query_string 1.0.8 与$args 相同; 193 | 194 | \$realpath_root 1.0.8 195 | 196 | \$remote_addr 1.0.8 客户端地址; 197 | 198 | \$remote_port 1.0.8 客户端端口号; 199 | 200 | \$remote_user 1.0.8 客户端用户名,认证用; 201 | 202 | \$request 1.0.8 用户请求 203 | 204 | \$request_body 1.0.8 205 | 206 | \$request_body_file 1.0.8 发往后端的本地文件名称 207 | 208 | \$request_completion 1.0.8 209 | 210 | $request_filename 1.0.8 当前请求的文件路径名,比如$request_filename:D:\nginx/html/test1/test2/test.php 211 | 212 | \$request_method 1.0.8 请求的方法,比如"GET"、"POST"等; 213 | 214 | \$request_uri 1.0.8 请求的 URI,带参数; 比如http://localhost:88/test1/test2/test.php 215 | 216 | $scheme 1.0.8 所用的协议,比如 http 或者是 https,比如 rewrite^(.+)$$scheme://example.com$1redirect; 217 | 218 | \$sent_http_cache_control 1.0.8 219 | 220 | \$sent_http_connection 1.0.8 221 | 222 | \$sent_http_content_length 1.0.8 223 | 224 | \$sent_http_content_type 1.0.8 225 | 226 | \$sent_http_keep_alive 1.0.8 227 | 228 | \$sent_http_last_modified 1.0.8 229 | 230 | \$sent_http_location 1.0.8 231 | 232 | \$sent_http_transfer_encoding 1.0.8 233 | 234 | \$server_addr 1.0.8 服务器地址,如果没有用 listen 指明服务器地址,使用这个变量将发起一次系统调用以取得地址(造成资源浪费); 235 | 236 | \$server_name 1.0.8 请求到达的服务器名; 237 | 238 | \$server_port 1.0.8 请求到达的服务器端口号; 239 | 240 | \$server_protocol 1.0.8 请求的协议版本,"HTTP/1.0"或"HTTP/1.1"; 241 | 242 | \$uri 1.0.8 请求的 URI,可能和最初的值有不同,比如经过重定向之类的。 243 | 244 | # 虚拟主机与负载均衡 245 | 246 | ``` 247 | upstream blog.ha97.com { 248 | #upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。 249 | server 192.168.80.121:80 weight=3; 250 | server 192.168.80.122:80 weight=2; 251 | server 192.168.80.123:80 weight=3; 252 | } 253 | 254 | # 反向代理 255 | server { 256 | listen 80; 257 | server_name www.001.com; 258 | location / { 259 | proxy_pass http://192.168.84.129; //后端ip地址 260 | proxy_redirect off; //关闭后端返回的header修改 261 | proxy_set_header Host $host; //修改发送到后端的header的host 262 | proxy_set_header X-Real-IP $remote_addr; //设置真实ip 263 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 264 | } 265 | } 266 | 267 | server { 268 | listen 80; 269 | server_name www.002.com; 270 | location / { 271 | proxy_pass http://192.168.84.128; //后端ip地址 272 | proxy_redirect off; //关闭后端返回的header修改 273 | proxy_set_header Host $host; //修改发送到后端的header的host 274 | proxy_set_header X-Real-IP $remote_addr; //设置真实ip 275 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 276 | } 277 | } 278 | 279 | 多个域名可以绑定到一个反向代理中。 280 | 281 | #虚拟主机的配置 282 | server 283 | { 284 | #监听端口 285 | listen 80; 286 | #域名可以有多个,用空格隔开 287 | server_name www.ha97.com ha97.com; 288 | index index.html index.htm index.php; 289 | root /data/www/ha97; 290 | location ~ .*\.(php|php5)?$ 291 | { 292 | fastcgi_pass 127.0.0.1:9000; 293 | fastcgi_index index.php; 294 | include fastcgi.conf; 295 | } 296 | #图片缓存时间设置 297 | location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ 298 | { 299 | expires 10d; 300 | } 301 | #JS和CSS缓存时间设置 302 | location ~ .*\.(js|css)?$ 303 | { 304 | expires 1h; 305 | } 306 | #日志格式设定 307 | log_format access '$remote_addr - $remote_user [$time_local] "$request" ' 308 | '$status $body_bytes_sent "$http_referer" ' 309 | '"$http_user_agent" $http_x_forwarded_for'; 310 | #定义本虚拟主机的访问日志 311 | access_log /var/log/nginx/ha97access.log access; 312 | 313 | #对 "/" 启用反向代理 314 | location / { 315 | proxy_pass http://127.0.0.1:88; 316 | proxy_redirect off; 317 | proxy_set_header X-Real-IP $remote_addr; 318 | #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP 319 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 320 | #以下是一些反向代理的配置,可选。 321 | proxy_set_header Host $host; 322 | client_max_body_size 10m; #允许客户端请求的最大单文件字节数 323 | client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数, 324 | proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时) 325 | proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时) 326 | proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时) 327 | proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小 328 | proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的设置 329 | proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2) 330 | proxy_temp_file_write_size 64k; 331 | #设定缓存文件夹大小,大于这个值,将从upstream服务器传 332 | } 333 | 334 | #设定查看Nginx状态的地址 335 | location /NginxStatus { 336 | stub_status on; 337 | access_log on; 338 | auth_basic "NginxStatus"; 339 | auth_basic_user_file conf/htpasswd; 340 | #htpasswd文件的内容可以用apache提供的htpasswd工具来产生。 341 | } 342 | 343 | #本地动静分离反向代理配置 344 | #所有jsp的页面均交由tomcat或resin处理 345 | location ~ .(jsp|jspx|do)?$ { 346 | proxy_set_header Host $host; 347 | proxy_set_header X-Real-IP $remote_addr; 348 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 349 | proxy_pass http://127.0.0.1:8080; 350 | } 351 | #所有静态文件由nginx直接读取不经过tomcat或resin 352 | location ~ .*.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt|pdf|xls|mp3|wma)$ 353 | { expires 15d; } 354 | location ~ .*.(js|css)?$ 355 | { expires 1h; } 356 | } 357 | ``` 358 | 359 | # 代理与重定向 360 | 361 | ## Rewrite(重定向) 362 | 363 | **nginx rewrite 正则表达式匹配** 364 | 365 | \*\*]大小写匹配\*\*\* 366 | 367 | ~ 为区分大小写匹配 368 | 369 | ~\* 为不区分大小写匹配 370 | 371 | !~和!~\*分别为区分大小写不匹配及不区分大小写不匹配 372 | 373 | \*\*]文件及目录匹配\*\*\* 374 | 375 | -f 和!-f 用来判断是否存在文件 376 | 377 | -d 和!-d 用来判断是否存在目录 378 | 379 | -e 和!-e 用来判断是否存在文件或目录 380 | 381 | -x 和!-x 用来判断文件是否可执行 382 | 383 | \*\*]flag 标记\*\*\* 384 | 385 | last 相当于 Apache 里的[L]标记,表示完成 rewrite 386 | 387 | break 终止匹配, 不再匹配后面的规则。 388 | 389 | redirect 返回 302 临时重定向 地址栏会显示跳转后的地址。 390 | 391 | permanent 返回 301 永久重定向 地址栏会显示跳转后的地址。 392 | 393 | logcation 的几个使用实例: 394 | 395 | 1)location / { }:匹配任何查询,因为所有请求都以 / 开头。但是正则表达式规则将被优先和查询匹配。 396 | 397 | 2)location =/ {}:仅仅匹配/ 398 | 399 | 3)location ~\\\\\\\\\\\* \.(gif|jpg|jpeg)\$ 400 | 401 | { 402 | 403 | rewrite \.(gif|jpg)$ /logo.png; 404 | 405 | }:location不区分大小写,匹配任何以gif,jpg,jpeg结尾的文件。 406 | 407 | 几个实例: 408 | 409 | **多目录转成参数 ** 410 | 411 | 要求:abc.domian.com/sort/2 => abc.domian.com/index.php?act=sort&name=abc&id=2 412 | 413 | 规则配置: 414 | 415 | if (\$host ~_ (._)\.domain\.com) { 416 | 417 | set $sub_name $1; 418 | 419 | rewrite ^/sort\/(\d+)\/?$ /index.php?act=sort&cid=$sub_name&id=$1 last; 420 | 421 | } 422 | 423 | **目录对换** 424 | 425 | 要求:/123456/xxxx -> /xxxx?id=123456 426 | 427 | 规则配置: 428 | 429 | rewrite ^/(\d+)/(.+)/ /$2?id=$1 last; 430 | 431 | 再来一个针对浏览器优化的自动 rewrite,这里 rewrite 后的目录可以是存在的; 432 | 433 | 例如设定 nginx 在用户使用 ie 的使用重定向到/nginx-ie 目录 434 | 435 | 规则如下: 436 | 437 | if (\$http_user_agent ~ MSIE) { 438 | 439 | rewrite ^(.*)$ /nginx-ie/$1 break; 440 | 441 | } 442 | 443 | **目录自动加“/”,这个功能一般浏览器自动完成** 444 | 445 | if (-d \$request_filename){ 446 | 447 | rewrite ^/(.\*)([^/])$ http://$host/$1$2/ permanent; 448 | 449 | } 450 | 451 | 以下这些可能就跟广义的 rewrite 重写无关了 452 | 453 | **禁止 htaccess** 454 | 455 | location ~/\.ht { 456 | 457 | deny all; 458 | 459 | } 460 | 461 | **禁止多个目录 ** 462 | 463 | location ~ ^/(cron|templates)/ { 464 | 465 | deny all; break; 466 | 467 | } 468 | 469 | **禁止以/data 开头的文件,可以禁止/data/下多级目录下.log.txt 等请求** 470 | 471 | location ~ ^/data { 472 | 473 | deny all; 474 | 475 | } 476 | 477 | 禁止单个文件 478 | 479 | location ~ /data/sql/data.sql { 480 | 481 | deny all; 482 | 483 | } 484 | 485 | 给 favicon.ico 和 robots.txt 设置过期时间; 这里为 favicon.ico 为 99 天,robots.txt 为 7 天并不记录 404 错误日志 486 | 487 | location ~(favicon.ico) { 488 | 489 | log_not_found off; 490 | 491 | expires 99d; 492 | 493 | break; 494 | 495 | } 496 | 497 | location ~(robots.txt) { 498 | 499 | log_not_found off; 500 | 501 | expires 7d; 502 | 503 | break; 504 | 505 | } 506 | 507 | 设定某个文件的浏览器缓存过期时间;这里为 600 秒,并不记录访问日志 508 | 509 | location ^~ /html/scripts/loadhead_1.js { 510 | 511 | access_log off; 512 | 513 | expires 600; 514 | 515 | break; 516 | 517 | } 518 | 519 | Nginx 还可以自定义某一类型的文件的保质期时间,具体写法看下文的代码: 520 | 521 | location ~\* \.(js|css|jpg|jpeg|gif|png|swf)\$ { 522 | 523 | if (-f \$request_filename) { 524 | 525 | expires 1h; 526 | 527 | break; 528 | 529 | } 530 | 531 | } 532 | 533 | //上段代码就将 js|css|jpg|jpeg|gif|png|swf 这类文件的保质期设置为一小时。 534 | 535 | **防盗链的设置:** 536 | 537 | 防盗链:如果你的网站是个下载网站,下载步骤应该是先经过你的主页找到下载地址,才能下载,为了防止某些网友直接访问下载地址完全不通过主页下载,我们就可以使用防盗链的方式,具体代码如下: 538 | 539 | location ~\* \.(gif|jpg|swf)\$ { 540 | 541 | valid_referers none blocked start.igrow.cn sta.igrow.cn; 542 | 543 | if (\$invalid_referer) { 544 | 545 | rewrite ^/ http://$host/logo.png; 546 | 547 | } 548 | 549 | } 550 | 551 | **文件反盗链并设置过期时间--<盗链多次请求也会打开你的站点的图片啊,所以设置下缓存时间,不会每次盗链都请求并下载这张图片>** 552 | 553 | location ~\* ^.+\.(jpg|jpeg|gif|png|swf|rar|zip|css|js)\$ { 554 | 555 | valid_referers none blocked *.jjonline.cn *.jjonline.com.cn *.lanwei.org *.jjonline.org localhost 42.121.107.189; 556 | 557 | if ($invalid_referer) { 558 | 559 | rewrite ^/ http://img.jjonline.cn/forbid.gif; 560 | 561 | return 417; 562 | 563 | break; 564 | 565 | } 566 | 567 | access_log off; 568 | 569 | break; 570 | 571 | } 572 | 573 | 说明: 574 | 575 | 这里的 return 417 为自定义的 http 状态码,默认为 403,方便通过 nginx 的 log 文件找出正确的盗链的请求地址 576 | 577 | “rewrite ^/ http://img.jjonline.cn/forbid.gif;”显示一张防盗链图片 578 | 579 | “access_log off;”不记录访问日志,减轻压力 580 | 581 | “expires 3d”所有文件 3 天的浏览器缓存 582 | 583 | **只充许固定 ip 访问网站,并加上密码;这个对有权限认证的应用比较在行** 584 | 585 | location \ { 586 | 587 | allow 22.27.164.25; #允许的ipd 588 | 589 | deny all; 590 | 591 | auth_basic “KEY”; #认证的一些设置 592 | 593 | auth_basic_user_file htpasswd; 594 | 595 | } 596 | 597 | 说明:location 的应用也有各种变化,这里的写法就针对了根目录了。 598 | 599 | **文件和目录不存在的时重定向** 600 | 601 | if (!-e \$request_filename) { 602 | 603 | #proxy_pass http://127.0.0.1; #这里是跳转到代理ip,这个代理ip上有一个监听的web服务器 604 | 605 | rewrite ^/ http://www.jjonline.cn/none.html; #跳转到这个网页去 606 | 607 | #return 404; #直接返回404码,然后会寻找root指定的404.html文件 608 | 609 | } 610 | 611 | **域名跳转 ** 612 | 613 | server { 614 | 615 | listen 80; 616 | 617 | server_name jump.jjonline.cn #需要跳转的多级域名 618 | 619 | index index.html index.htm index.php; #入口索引文件的名字 620 | 621 | root /var/www/public_html/; #这个站点的根目录 622 | 623 | rewrite ^/ http://www.jjonline.cn/; 624 | 625 | #rewrite到这个地址,功能表现:在浏览器上输入jump.jjonline.cn并回车,不会有任何提示直接变成www.jjonline.cn 626 | 627 | access_log off; 628 | 629 | } 630 | 631 | **多域名转向** 632 | 633 | server { 634 | 635 | listen 80; 636 | 637 | 638 | 639 | server_name www.jjonline.cn www.jjonline.org; 640 | 641 | index index.html index.htm index.php; 642 | 643 | root /var/www/public_html/; 644 | 645 | if ($host ~ “jjonline\.org”) { 646 | 647 | rewrite ^(.*) http://www.jjonline.cn$1 permanent; 648 | 649 | } 650 | 651 | } 652 | 653 | **三级域名跳转 ** 654 | 655 | if ($http_host ~_ “^(._)\.i\.jjonline\.cn$”) { 656 | 657 | rewrite ^(.*) http://demo.jjonline.cn$1; 658 | 659 | break; 660 | 661 | } 662 | 663 | **域名镜向** 664 | 665 | server { 666 | 667 | listen 80; 668 | 669 | server_name mirror.jjonline.cn; 670 | 671 | index index.html index.htm index.php; 672 | 673 | root /var/www/public_html; 674 | 675 | rewrite ^/(.*) http://www.jjonline.cn/$1 last; 676 | 677 | access_log off; 678 | 679 | } 680 | 681 | **某个子目录作镜向,这里的示例是 demo 子目录** 682 | 683 | location ^~ /demo { 684 | 685 | rewrite ^.+ http://demo.jjonline.cn/ last; 686 | 687 | break; 688 | 689 | } 690 | 691 | 以下在附带本博客的 rewrite 写法,emlog 系统的 rewrite 692 | 693 | location ~ { 694 | 695 | if (!-e $request_filename) { 696 | 697 | rewrite ^/(.+)$ /index.php last; 698 | 699 | } 700 | 701 | } 702 | -------------------------------------------------------------------------------- /01~服务器/Nginx/README.md: -------------------------------------------------------------------------------- 1 | ![](http://blog.commando.io/content/images/2014/11/1-Tg9FYCN99FlNj0gn9u8s7A-5-2.jpeg) 2 | 3 | # Nginx 4 | -------------------------------------------------------------------------------- /01~服务器/Nginx/基础配置.md: -------------------------------------------------------------------------------- 1 | ![](http://ww4.sinaimg.cn/large/6c0378f8gw1f9yyq7qehrj20p00dwt94.jpg) 2 | 3 | # Nginx 配置详解与生产环境实践 4 | 5 | 在了解具体的 Nginx 配置项之前我们需要对于 Nginx 配置文件的构成有所概念,一般来说,Nginx 配置文件会由如下几个部分构成: 6 | 7 | ``` 8 | # 全局块 9 | ... 10 | # events块 11 | events { 12 | ... 13 | } 14 | # http块 15 | http 16 | { 17 | # http全局块 18 | ... 19 | # 虚拟主机server块 20 | server 21 | { 22 | # server全局块 23 | ... 24 | # location块 25 | location [PATTERN] 26 | { 27 | ... 28 | } 29 | location [PATTERN] 30 | { 31 | ... 32 | } 33 | } 34 | server 35 | { 36 | ... 37 | } 38 | # http全局块 39 | ... 40 | } 41 | ``` 42 | 43 | 在上述配置中我们可以看出,Nginx 配置文件由以下几个部分构成: 44 | 45 | - 全局块:配置影响 nginx 全局的指令。一般有运行 nginx 服务器的用户组,nginx 进程 pid 存放路径,日志存放路径,配置文件引入,允许生成 worker process 数等。 46 | - events 块:配置影响 nginx 服务器或与用户的网络连接。有每个进程的最大连接数,选取哪种事件驱动模型处理连接请求,是否允许同时接受多个网路连接,开启多个网络连接序列化等。 47 | - http 块:可以嵌套多个 server,配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置。如文件引入,mime-type 定义,日志自定义,是否使用 sendfile 传输文件,连接超时时间,单连接请求数等。 48 | - server 块:配置虚拟主机的相关参数,一个 http 中可以有多个 server。 49 | - location 块:配置请求的路由,以及各种页面的处理情况。 50 | 51 | ``` 52 | ########### 每个指令必须有分号结束。################# 53 | #user administrator administrators; #配置用户或者组,默认为nobody nobody。 54 | #worker_processes 2; #允许生成的进程数,默认为1 55 | #pid /nginx/pid/nginx.pid; #指定nginx进程运行文件存放地址 56 | error_log log/error.log debug; #制定日志路径,级别。这个设置可以放入全局块,http块,server块,级别以此为:debug|info|notice|warn|error|crit|alert|emerg 57 | events { 58 | accept_mutex on; #设置网路连接序列化,防止惊群现象发生,默认为on 59 | multi_accept on; #设置一个进程是否同时接受多个网络连接,默认为off 60 | #use epoll; #事件驱动模型,select|poll|kqueue|epoll|resig|/dev/poll|eventport 61 | worker_connections 1024; #最大连接数,默认为512 62 | } 63 | http { 64 | include mime.types; #文件扩展名与文件类型映射表 65 | default_type application/octet-stream; #默认文件类型,默认为text/plain 66 | #access_log off; #取消服务日志 67 | log_format myFormat '$remote_addr–$remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for'; #自定义格式 68 | access_log log/access.log myFormat; #combined为日志格式的默认值 69 | sendfile on; #允许sendfile方式传输文件,默认为off,可以在http块,server块,location块。 70 | sendfile_max_chunk 100k; #每个进程每次调用传输数量不能大于设定的值,默认为0,即不设上限。 71 | keepalive_timeout 65; #连接超时时间,默认为75s,可以在http,server,location块。 72 | 73 | # 定义常量 74 | upstream mysvr { 75 | server 127.0.0.1:7878; 76 | server 192.168.10.121:3333 backup; #热备 77 | } 78 | error_page 404 https://www.baidu.com; #错误页 79 | 80 | #定义某个负载均衡服务器 81 | server { 82 | keepalive_requests 120; #单连接请求上限次数。 83 | listen 4545; #监听端口 84 | server_name 127.0.0.1; #监听地址 85 | location ~*^.+$ { #请求的url过滤,正则匹配,~为区分大小写,~*为不区分大小写。 86 | #root path; #根目录 87 | #index vv.txt; #设置默认页 88 | proxy_pass http://mysvr; #请求转向mysvr 定义的服务器列表 89 | deny 127.0.0.1; #拒绝的ip 90 | allow 172.18.5.54; #允许的ip 91 | } 92 | } 93 | } 94 | ``` 95 | 96 | ``` 97 | include /etc/nginx/conf.d/*.conf; 98 | include /etc/nginx/sites-enabled/*; 99 | ``` 100 | 101 | # 虚拟主机与静态站点 102 | 103 | - [SERVING STATIC CONTENT](https://www.nginx.com/resources/admin-guide/serving-static-content/) 104 | 105 | 本部分概述如何配置 Nginx 进行静态内容服务,Nginx 的静态内容分发能力还是非常强大的。 106 | 107 | ``` 108 | http { 109 | server { 110 | listen 80; 111 | server_name www.domain1.com; 112 | access_log logs/domain1.access.log main; 113 | location / { 114 | index index.html; 115 | root /var/www/domain1.com/htdocs; 116 | } 117 | } 118 | server { 119 | listen 80; 120 | server_name www.domain2.com; 121 | access_log logs/domain2.access.log main; 122 | location / { 123 | index index.html; 124 | root /var/www/domain2.com/htdocs; 125 | } 126 | } 127 | } 128 | ``` 129 | 130 | ## 虚拟主机配置详解 131 | 132 | ### 主机与端口 133 | 134 | ``` 135 | listen 127.0.0.1:8000; 136 | listen *:8000; 137 | listen localhost:8000; 138 | # IPV6 139 | listen [::]:8000; 140 | # other params 141 | listen 443 default_server ssl; 142 | listen 127.0.0.1 default_server accept_filter=dataready backlog=1024 143 | ``` 144 | 145 | ### 服务域名 146 | 147 | ``` 148 | # 支持多域名配置 149 | server_name www.barretlee.com barretlee.com; 150 | # 支持泛域名解析 151 | server_name *.barretlee.com; 152 | # 支持对于域名的正则匹配 153 | server_name ~^\.barret\.com$; 154 | ``` 155 | 156 | ### URI 匹配 157 | 158 | ``` 159 | location = / { 160 | # 完全匹配 = 161 | # 大小写敏感 ~ 162 | # 忽略大小写 ~* 163 | } 164 | location ^~ /images/ { 165 | # 前半部分匹配 ^~ 166 | # 可以使用正则,如: 167 | # location ~* \.(gif|jpg|png)$ { } 168 | } 169 | location / { 170 | # 如果以上都未匹配,会进入这里 171 | } 172 | ``` 173 | 174 | ## 文件路径配置 175 | 176 | ### 根目录 177 | 178 | ``` 179 | location / { 180 | root /home/barret/test/; 181 | } 182 | ``` 183 | 184 | ### 别名 185 | 186 | ``` 187 | location /blog { 188 | alias /home/barret/www/blog/; 189 | } 190 | location ~ ^/blog/(\d+)/([\w-]+)$ { 191 | # /blog/20141202/article-name 192 | # -> /blog/20141202-article-name.md 193 | alias /home/barret/www/blog/$1-$2.md; 194 | } 195 | ``` 196 | 197 | ### 首页 198 | 199 | ``` 200 | index /html/index.html /php/index.php; 201 | ``` 202 | 203 | ### 重定向页面 204 | 205 | ``` 206 | error_page 404 /404.html; 207 | error_page 502 503 /50x.html; 208 | error_page 404 =200 /1x1.gif; 209 | location / { 210 | error_page 404 @fallback; 211 | } 212 | location @fallback { 213 | # 将请求反向代理到上游服务器处理 214 | proxy_pass http://localhost:9000; 215 | } 216 | ``` 217 | 218 | ### try_files 219 | 220 | ``` 221 | try_files $uri $uri.html $uri/index.html @other; 222 | location @other { 223 | # 尝试寻找匹配 uri 的文件,失败了就会转到上游处理 224 | proxy_pass http://localhost:9000; 225 | } 226 | location / { 227 | # 尝试寻找匹配 uri 的文件,没找到直接返回 502 228 | try_files $uri $uri.html =502; 229 | } 230 | ``` 231 | 232 | ## 缓存配置 233 | 234 | - [HTTP 缓存的四种风味与缓存策略](https://segmentfault.com/a/1190000006689795) 235 | 236 | ### Expire:过期时间 237 | 238 | 在 Nginx 中可以配置缓存的过期时间: 239 | 240 | ``` 241 | location ~* \.(?:ico|css|js|gif|jpe?g|png)$ { 242 | expires 30d; 243 | add_header Vary Accept-Encoding; 244 | access_log off; 245 | } 246 | ``` 247 | 248 | 我们也可以添加更复杂的配置项: 249 | 250 | ``` 251 | location ~* ^.+\.(?:css|cur|js|jpe?g|gif|htc|ico|png|html|xml|otf|ttf|eot|woff|svg)$ { 252 | 253 | access_log off; 254 | expires 30d; 255 | ## No need to bleed constant updates. Send the all shebang in one 256 | ## fell swoop. 257 | tcp_nodelay off; 258 | ## Set the OS file cache. 259 | open_file_cache max=3000 inactive=120s; 260 | open_file_cache_valid 45s; 261 | open_file_cache_min_uses 2; 262 | open_file_cache_errors off; 263 | } 264 | ``` 265 | 266 | # 反向代理 267 | 268 | ``` 269 | events{ 270 | 271 | } 272 | http{ 273 | upstream ggzy { 274 | server 127.0.0.1:1398 weight=3; 275 | server 127.0.0.1:1399; 276 | } 277 | # 80端口配置,可配置多个Virtual Host 278 | server { 279 | listen 80; 280 | index index index.htm index.py index.html; 281 | 282 | server_name app.truelore.cn; 283 | 284 | location / { 285 | proxy_pass_header Server; 286 | proxy_set_header Host $http_host; 287 | proxy_set_header X-Real-IP $remote_addr; 288 | proxy_set_header X-Scheme $scheme; 289 | proxy_pass http//ggzy; 290 | } 291 | } 292 | } 293 | ``` 294 | 295 | ## NodeJS Application 296 | 297 | ``` 298 | const http = require('http'); 299 | http.createServer((req, res) => { 300 | res.end('hello world'); 301 | }).listen(9000); 302 | ``` 303 | 304 | 任何请求过来都返回 hello world,简版的 Nginx 配置如下, 305 | 306 | ``` 307 | events { 308 | # 这里可不写东西 309 | use epoll; 310 | } 311 | http { 312 | server { 313 | listen 127.0.0.1:8888; 314 | # 如果请求路径跟文件路径按照如下方式匹配找到了,直接返回 315 | try_files $uri $uri/index.html; 316 | location ~* ^/(js|css|image|font)/$ { 317 | # 静态资源都在 static 文件夹下 318 | root /home/barret/www/static/; 319 | } 320 | location /app { 321 | # Node.js 在 9000 开了一个监听端口 322 | proxy_pass http://127.0.0.1:9000; 323 | } 324 | # 上面处理出错或者未找到的,返回对应状态码文件 325 | error_page 404 /404.html; 326 | error_page 502 503 504 /50x.html; 327 | } 328 | } 329 | ``` 330 | 331 | 首先 try_files,尝试直接匹配文件;没找到就匹配静态资源;还没找到就交给 Node 处理;否则就返回 4xx/5xx 的状态码。 332 | 333 | ## Upstream Cache 334 | 335 | - [A Guide to Caching with NGINX and NGINX Plus](https://www.nginx.com/blog/nginx-caching-guide/) 336 | 337 | ``` 338 | http { 339 | ,,,,, 340 | proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=imgcache:100m inactive=1d max_size=10g; 341 | server { 342 | ........ 343 | location ~* ^.+\.(js|ico|gif|jpg|jpeg|png|html|htm)$ { 344 | log_not_found off; 345 | access_log off; 346 | expires 7d; 347 | proxy_pass http://img.example.com ; 348 | proxy_cache imgcache; 349 | proxy_cache_valid 200 302 1d; 350 | proxy_cache_valid 404 10m; 351 | proxy_cache_valid any 1h; 352 | proxy_cache_use_stale error timeout invalid_header updating http_500 http_502 http_503 http_504; 353 | } 354 | } 355 | } 356 | ``` 357 | 358 | # HTTPS 359 | 360 | - [HTTPS 理论详解与实践 ](https://segmentfault.com/a/1190000004985253) 361 | 362 | ## Let's Encrypt 证书申请 363 | 364 | Let's Encrypt 为我们提供了非常方便的命令行工具[certbot](https://certbot.eff.org/#ubuntuxenial-nginx),笔者是在 Ubuntu 16.04 的机器上进行配置,因此只要执行如下命令即可: 365 | 366 | ``` 367 | # 安装letsencrypt命令行 368 | 369 | $ sudo apt-get install letsencrypt 370 | # 独立的为example.com与www.example.com申请证书 371 | $ letsencrypt certonly --standalone -d example.com -d www.example.com 372 | # 自动执行证书刷新操作 373 | $ letsencrypt renew --dry-run --agree-tos 374 | ``` 375 | 376 | ## 基本 HTTPS 配置 377 | 378 | 基本的 HTTPS 支持配置如下: 379 | 380 | ``` 381 | server { 382 | listen 192.168.1.11:443 ssl; #ssl端口 383 | server_name test.com; 384 | #为一个server{......}开启ssl支持 385 | #指定PEM格式的证书文件 386 | ssl_certificate /etc/nginx/test.pem; 387 | #指定PEM格式的私钥文件 388 | ssl_certificate_key /etc/nginx/test.key; 389 | ssl_session_cache shared:SSL:1m; 390 | ssl_session_timeout 5m; 391 | 392 | ssl_ciphers HIGH:!aNULL:!MD5; 393 | ssl_prefer_server_ciphers on; 394 | 395 | location / { 396 | root html; 397 | index index.html index.htm; 398 | } 399 | } 400 | ``` 401 | 402 | 在真实的生产环境中,我们的配置如下: 403 | 404 | ``` 405 | server { 406 | # 如果需要spdy也可以加上,lnmp1.2及其后版本都默认支持spdy,lnmp1.3 nginx 1.9.5以上版本默认支持http2 407 | listen 443 ssl; 408 | # 这里是你的域名 409 | server_name www.vpser.net; 410 | index index.html index.htm index.php default.html default.htm default.php; 411 | # 网站目录 412 | root /home/wwwroot/www.vpser.net; 413 | # 前面生成的证书,改一下里面的域名就行 414 | ssl_certificate /etc/letsencrypt/live/www.vpser.net/fullchain.pem; 415 | # 前面生成的密钥,改一下里面的域名就行 416 | ssl_certificate_key /etc/letsencrypt/live/www.vpser.net/privkey.pem; 417 | ssl_ciphers "EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5"; 418 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 419 | ssl_prefer_server_ciphers on; 420 | ssl_session_cache shared:SSL:10m; 421 | 422 | #这个是伪静态根据自己的需求改成其他或删除 423 | include wordpress.conf; 424 | 425 | #error_page 404 /404.html; 426 | 427 | location ~ [^/]\.php(/|$) 428 | { 429 | # comment try_files $uri =404; to enable pathinfo 430 | try_files $uri =404; 431 | fastcgi_pass unix:/tmp/php-cgi.sock; 432 | fastcgi_index index.php; 433 | # lnmp 1.0及之前版本替换为include fcgi.conf; 434 | include fastcgi.conf; 435 | #include pathinfo.conf; 436 | } 437 | 438 | location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ 439 | { 440 | expires 30d; 441 | } 442 | 443 | location ~ .*\.(js|css)?$ 444 | { 445 | expires 12h; 446 | } 447 | 448 | access_log off; 449 | } 450 | ``` 451 | 452 | ## 强制 HTTP 转到 HTTPS 453 | 454 | ### Nginx Rewrite 455 | 456 | ``` 457 | server { 458 | listen 192.168.1.111:80; 459 | server_name test.com; 460 | rewrite ^(.*)$ https://$host$1 permanent; 461 | } 462 | ``` 463 | 464 | ### Nginx 497 错误码 465 | 466 | 利用 error_page 命令将 497 状态码的链接重定向到https://test.com这个域名上 467 | 468 | ``` 469 | server { 470 | listen 192.168.1.11:443; #ssl端口 471 | listen 192.168.1.11:80; #用户习惯用http访问,加上80,后面通过497状态码让它自动跳到443端口 472 | server_name test.com; 473 | #为一个server{......}开启ssl支持 474 | ssl on; 475 | #指定PEM格式的证书文件 476 | ssl_certificate /etc/nginx/test.pem; 477 | #指定PEM格式的私钥文件 478 | ssl_certificate_key /etc/nginx/test.key; 479 | 480 | #让http请求重定向到https请求 481 | error_page 497 https://$host$uri?$args; 482 | } 483 | ``` 484 | 485 | ### Meta 刷新,前端跳转 486 | 487 | 在 HTTP 正常返回的页面中添加 meta 属性: 488 | 489 | ``` 490 | 491 | 492 | 493 | ``` 494 | 495 | ``` 496 | server { 497 | listen 192.168.1.11:80; 498 | server_name test.com; 499 | 500 | location / { 501 | #index.html放在虚拟主机监听的根目录下 502 | root /srv/www/http.test.com/; 503 | } 504 | #将404的页面重定向到https的首页 505 | error_page 404 https://test.com/; 506 | } 507 | ``` 508 | 509 | ## 反向 HTTPS 转发到内部 HTTP 510 | -------------------------------------------------------------------------------- /01~服务器/Nginx/进程模型.md: -------------------------------------------------------------------------------- 1 | # Nginx 进程与处理模型 2 | 3 | # Links 4 | 5 | - https://zhuanlan.zhihu.com/p/67636582 6 | -------------------------------------------------------------------------------- /01~服务器/OpenResty/README.md: -------------------------------------------------------------------------------- 1 | # OpenResty: 基于 Nginx 的全功能 Web 应用服务器 2 | 3 | OpenResty (也称为 ngx_openresty)是一个全功能的 Web 应用服务器。它打包了标准的 [Nginx](undefined) 核心,很多的常用的[第三方模块](http://wiki.nginx.org/3rdPartyModules),以及它们的大多数依赖项。通过众多进行良好设计的 Nginx 模块,OpenResty 有效地把 Nginx 服务器转变为一个强大的 Web 应用服务器,基于它开发人员可以使用 Lua 编程语言对 Nginx 核心以及现有的各种 Nginx C 模块进行脚本编程,构建出可以处理一万以上并发请求的极端高性能的 Web 应用。OpenResty 致力于将你的服务器端应用完全运行于 Nginx 服务器中,充分利用 Nginx 的事件模型来进行非阻塞 IO 通信。不仅仅是和 HTTP 客户端间的网络通信是非阻塞的,与 MySQL、PostgreSQL、Memcached、以及 Redis 等众多远方后端之间的网络通信也是非阻塞的。因为 OpenResty 软件包的维护者也是其中打包的许多 Nginx 模块的作者,所以 OpenResty 可以确保所包含的所有组件可以可靠地协同工作。 4 | 5 | # 安装与部署 6 | 7 | # HttpClient 8 | 9 | ## [lua-resty-http](https://github.com/pintsized/lua-resty-http) 10 | 11 | - HTTP 1.0 and 1.1 12 | - Streaming interface to reading bodies using coroutines, for predictable memory usage in Lua land. 13 | - Alternative simple interface for singleshot requests without manual connection step. 14 | - Headers treated case insensitively. 15 | - Chunked transfer encoding. 16 | - Keepalive. 17 | - Pipelining. 18 | - Trailers. 19 | 20 | ```lua 21 | lua_package_path "/path/to/lua-resty-http/lib/?.lua;;"; 22 | 23 | server { 24 | 25 | 26 | location /simpleinterface { 27 | resolver 8.8.8.8; # use Google's open DNS server for an example 28 | 29 | content_by_lua ' 30 | 31 | -- For simple singleshot requests, use the URI interface. 32 | local http = require "resty.http" 33 | local httpc = http.new() 34 | local res, err = httpc:request_uri("http://example.com/helloworld", { 35 | method = "POST", 36 | body = "a=1&b=2", 37 | headers = { 38 | ["Content-Type"] = "application/x-www-form-urlencoded", 39 | } 40 | }) 41 | 42 | if not res then 43 | ngx.say("failed to request: ", err) 44 | return 45 | end 46 | 47 | -- In this simple form, there is no manual connection step, so the body is read 48 | -- all in one go, including any trailers, and the connection closed or keptalive 49 | -- for you. 50 | 51 | ngx.status = res.status 52 | 53 | for k,v in pairs(res.headers) do 54 | -- 55 | end 56 | 57 | ngx.say(res.body) 58 | '; 59 | } 60 | 61 | 62 | location /genericinterface { 63 | content_by_lua ' 64 | 65 | local http = require "resty.http" 66 | local httpc = http.new() 67 | 68 | -- The generic form gives us more control. We must connect manually. 69 | httpc:set_timeout(500) 70 | httpc:connect("127.0.0.1", 80) 71 | 72 | -- And request using a path, rather than a full URI. 73 | local res, err = httpc:request{ 74 | path = "/helloworld", 75 | headers = { 76 | ["Host"] = "example.com", 77 | }, 78 | } 79 | 80 | if not res then 81 | ngx.say("failed to request: ", err) 82 | return 83 | end 84 | 85 | -- Now we can use the body_reader iterator, to stream the body according to our desired chunk size. 86 | local reader = res.body_reader 87 | 88 | repeat 89 | local chunk, err = reader(8192) 90 | if err then 91 | ngx.log(ngx.ERR, err) 92 | break 93 | end 94 | 95 | if chunk then 96 | -- process 97 | end 98 | until not chunk 99 | 100 | local ok, err = httpc:set_keepalive() 101 | if not ok then 102 | ngx.say("failed to set keepalive: ", err) 103 | return 104 | end 105 | '; 106 | } 107 | } 108 | ``` 109 | 110 | # 存储 111 | 112 | ## [Redis](https://github.com/openresty/lua-resty-redis) 113 | 114 | ```conf 115 | # you do not need the following line if you are using 116 | # the OpenResty bundle: 117 | lua_package_path "/path/to/lua-resty-redis/lib/?.lua;;"; 118 | 119 | server { 120 | location /test { 121 | content_by_lua ' 122 | local redis = require "resty.redis" 123 | local red = redis:new() 124 | 125 | red:set_timeout(1000) -- 1 sec 126 | 127 | -- or connect to a unix domain socket file listened 128 | -- by a redis server: 129 | -- local ok, err = red:connect("unix:/path/to/redis.sock") 130 | 131 | local ok, err = red:connect("127.0.0.1", 6379) 132 | if not ok then 133 | ngx.say("failed to connect: ", err) 134 | return 135 | end 136 | 137 | ok, err = red:set("dog", "an animal") 138 | if not ok then 139 | ngx.say("failed to set dog: ", err) 140 | return 141 | end 142 | 143 | ngx.say("set result: ", ok) 144 | 145 | local res, err = red:get("dog") 146 | if not res then 147 | ngx.say("failed to get dog: ", err) 148 | return 149 | end 150 | 151 | if res == ngx.null then 152 | ngx.say("dog not found.") 153 | return 154 | end 155 | 156 | ngx.say("dog: ", res) 157 | 158 | red:init_pipeline() 159 | red:set("cat", "Marry") 160 | red:set("horse", "Bob") 161 | red:get("cat") 162 | red:get("horse") 163 | local results, err = red:commit_pipeline() 164 | if not results then 165 | ngx.say("failed to commit the pipelined requests: ", err) 166 | return 167 | end 168 | 169 | for i, res in ipairs(results) do 170 | if type(res) == "table" then 171 | if res[1] == false then 172 | ngx.say("failed to run command ", i, ": ", res[2]) 173 | else 174 | -- process the table value 175 | end 176 | else 177 | -- process the scalar value 178 | end 179 | end 180 | 181 | -- put it into the connection pool of size 100, 182 | -- with 10 seconds max idle time 183 | local ok, err = red:set_keepalive(10000, 100) 184 | if not ok then 185 | ngx.say("failed to set keepalive: ", err) 186 | return 187 | end 188 | 189 | -- or just close the connection right away: 190 | -- local ok, err = red:close() 191 | -- if not ok then 192 | -- ngx.say("failed to close: ", err) 193 | -- return 194 | -- end 195 | '; 196 | } 197 | } 198 | ``` 199 | -------------------------------------------------------------------------------- /01~服务器/README.md: -------------------------------------------------------------------------------- 1 | # Web 应用服务器 2 | 3 | Web Server 的概念太宽泛了。严格的来说,Apache/Nginx 应该叫做 HTTP Server;而 Tomcat 则是一个 Application Server,或者更准确的来说,是一个 Servlet/JSP 应用的容器(Ruby/Python 等其他语言开发的应用也无法直接运行在 Tomcat 上)。一个 HTTP Server 关心的是 HTTP 协议层面的传输和访问控制,所以在 Apache/Nginx 上你可以看到代理、负载均衡等功能。客户端通过 HTTP Server 访问服务器上存储的资源(HTML 文件、图片文件等等)。通过 CGI 技术,也可以将处理过的内容通过 HTTP Server 分发,但是一个 HTTP Server 始终只是把服务器上的文件如实的通过 HTTP 协议传输给客户端。而应用服务器,则是一个应用执行的容器。它首先需要支持开发语言的 Runtime(对于 Tomcat 来说,就是 Java),保证应用能够在应用服务器上正常运行。其次,需要支持应用相关的规范,例如类库、安全方面的特性。对于 Tomcat 来说,就是需要提供 JSP/Sevlet 运行需要的标准类库、Interface 等。为了方便,应用服务器往往也会集成 HTTP Server 的功能,但是不如专业的 HTTP Server 那么强大,所以应用服务器往往是运行在 HTTP Server 的背后,执行应用,将动态的内容转化为静态的内容之后,通过 HTTP Server 分发到客户端。 4 | 5 | # 正向代理与反向代理 6 | 7 | 两者的区别在于代理的对象不一样:正向代理代理的对象是客户端,反向代理代理的对象是服务端。我们常说的代理也就是只正向代理,正向代理的过程,它隐藏了真实的请求客户端,服务端不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替来请求,知名的科学上网工具 shadowsocks 扮演的就是典型的正向代理角色。在天朝用浏览器访问 www.google.com 时,被残忍的拒绝了,于是你可以在国外搭建一台代理服务器,让代理帮我去请求 google.com,代理把请求返回的相应结构再返回给我。反向代理隐藏了真实的服务端,当我们请求 www.baidu.com 的时候,就像拨打 10086 一样,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,也不需要知道,你只需要知道反向代理服务器是谁就好了,www.baidu.com 就是我们的反向代理服务器,反向代理服务器会帮我们把请求转发到真实的服务器那里去。Nginx 就是性能非常好的反向代理服务器,用来做负载均衡。 8 | 9 | # Links 10 | 11 | - http://blog.51cto.com/z00w00/1031287 12 | -------------------------------------------------------------------------------- /04~接入网关/02~负载均衡/LVS/README.md: -------------------------------------------------------------------------------- 1 | # LVS 2 | 3 | LVS 有如下的组成部分: 4 | 5 | - Direct Server(以下简称 DS):前端暴露给客户端进行负载均衡的服务器。 6 | 7 | - Virtual Ip 地址(以下简称 VIP):DS 暴露出去的 IP 地址,做为客户端请求的地址。 8 | 9 | - Direct Ip 地址(以下简称 DIP):DS 用于与 Real Server 交互的 IP 地址。 10 | 11 | - Real Server(以下简称 RS):后端真正进行工作的服务器,可以横向扩展。 12 | 13 | - Real IP 地址(以下简称 RIP):RS 的地址。 14 | 15 | - Client IP 地址(以下简称 CIP):Client 的地址。 16 | 17 | ![](https://tva3.sinaimg.cn/large/007DFXDhgy1g5us7zazn4j30ly0f03zb.jpg) 18 | 19 | 客户端进行请求时,流程如下: 20 | 21 | - 使用 VIP 地址访问 DS,此时的地址二元组为。 22 | 23 | - DS 根据自己的负载均衡算法,选择一个 RS 将请求转发过去,在转发过去的时候,修改请求的源 IP 地址为 DIP 地址,让 RS 看上去认为是 DS 在访问它,此时的地址二元组为。 24 | 25 | - RS 处理并且应答该请求,这个回报的源地址为 RS 的 RIP 地址,目的地址为 DIP 地址,此时的地址二元组为。 26 | 27 | - DS 在收到该应答包之后,将报文应答客户端,此时修改应答报文的源地址为 VIP 地址,目的地址为 CIP 地址,此时的地址二元组为。 28 | -------------------------------------------------------------------------------- /04~接入网关/02~负载均衡/README.md: -------------------------------------------------------------------------------- 1 | # 负载均衡 2 | 3 | 为了解决应用实例的可扩展、可用性、安全性问题,在不同的应用实例间分配传入的流量,需要依赖于一些负载均衡解决方案。我们常见的负载均衡会分以下几层进行部署: 4 | 5 | - 二层负载均衡会通过一个虚拟 MAC 地址接收请求,然后再分配到真实的 MAC 地址; 6 | 7 | - 三层负载均衡会通过一个虚拟 IP 地址接收请求,然后再分配到真实的 IP 地址; 8 | 9 | - 四层通过虚拟 IP 与端口接收请求,然后再分配到真实的服务器; 10 | 11 | - 七层通过虚拟的 URL 或主机名接收请求,然后再分配到真实的服务器。 12 | 13 | 最为常用的是四层负载均衡与七层负载均衡。四层负载均衡,也就是主要通过报文中的目标地址和端口,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。而所谓七层负载均衡,也称为内容交换,也就是主要通过报文中的真正有意义的应用层内容,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。比如公有云 AWS ELB 提供了两种类型的负载均衡器: 14 | 15 | - Classic 负载均衡器,可基于应用程序或网络级信息路由流量,适用于在多个 EC2 实例之间进行简单的流量负载均衡 16 | 17 | - 应用程序负载均衡器,可基于包括请求内容的高级应用程序级信息路由流量,适用于需要高级路由功能、微服务和基于容器的架构的应用程序。应用程序负载均衡器可将流量路由至多个服务,也可在同一 EC2 实例的多个端口之间进行负载均衡 18 | 19 | # Links 20 | 21 | - https://zhuanlan.zhihu.com/p/36054372 22 | -------------------------------------------------------------------------------- /04~接入网关/02~负载均衡/Scratch/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/System-Notes/66ed1f216d8988b2294f59f7490fc6f4739ae97a/04~接入网关/02~负载均衡/Scratch/README.md -------------------------------------------------------------------------------- /04~接入网关/02~负载均衡/Scratch/基于 Go 的简单负载均衡.md: -------------------------------------------------------------------------------- 1 | # 基于 Go 的简单负载均衡 2 | 3 | # Links 4 | 5 | - https://mp.weixin.qq.com/s/4MsiS6xEnJo66ZQESePzTQ 6 | -------------------------------------------------------------------------------- /04~接入网关/02~负载均衡/分流算法.md: -------------------------------------------------------------------------------- 1 | # Load Balancing | 负载均衡 2 | 3 | ## 负载均衡算法 4 | 5 | 常用的负载均衡算法有:轮询,随机,最少链接,源地址哈希,加权等方式。 6 | 7 | | 方式 | 优点 | 缺点 | 8 | | ---------- | ------------------------------------------------------------------------ | -------------------------------------------- | 9 | | 随机 | 简单 | 不适合机器配置不同的场景 | 10 | | 轮询 | 每台服务器的请求数目相同 | 服务器压力不一样,不适合服务器配置不同的情况 | 11 | | 最少连接 | 根据服务器当前的请求处理情况,动态分配 | 算法实现相对复杂,需要监控服务器请求连接数 | 12 | | 源地址哈希 | 将来自同一 IP 地址的请求,同一会话期内,转发到相同的服务器;实现会话粘滞 | 目标服务器宕机后,会话会丢失 | 13 | | 加权 | 根据权重,调节转发到后端服务器的请求数目 | 使用相对复杂 | 14 | 15 | ### 轮询 16 | 17 | 将所有请求依次分发到每台服务器上,适合服务器硬件配置相同的场景。 18 | 19 | ### 随机 20 | 21 | 请求随机分配到各个服务器。 22 | 23 | ### 最少连接 24 | 25 | 将请求分配到连接数最少的服务器(目前处理请求最少的服务器)。 26 | 27 | ### Hash | 源地址哈希 28 | 29 | 根据 IP 地址进行 Hash 计算,得到 IP 地址。 30 | 31 | ### 加权 32 | 33 | 在轮询,随机,最少连接,Hash 等算法的基础上,通过加权的方式,进行负载服务器分配。 34 | -------------------------------------------------------------------------------- /04~接入网关/02~负载均衡/负载分层.md: -------------------------------------------------------------------------------- 1 | # 负载均衡分层 2 | 3 | 前文我们提及,负载均衡可以部署在二层、三层、四层与七层等不同的网络层,而实际应用中,我们常常会进行 IP 层、HTTP 层与应用层的负载均衡。 4 | 5 | # IP 层负载均衡 6 | 7 | 以常见的 TCP 为例,负载均衡设备在接收到第一个来自客户端的 SYN 请求时,即通过上述方式选择一个最佳的服务器,并对报文中的目标 IP 地址进行修改(改为后端服务器 IP),直接转发给该服务器。TCP 的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类似路由器的转发动作。在某些部署情况下,为保证服务器回包可以正确返回给负载均衡设备,在转发报文的同时可能还会对报文原来的源地址进行修改。 8 | 9 | 该层负载均衡的特点如下: 10 | 11 | 1、抗负载能力强、是工作在网络 4 层之上仅作分发之用,没有流量的产生,这个特点也决定了它在负载均衡软件里的性能最强的; 12 | 13 | 2、配置性比较低,这是一个缺点也是一个优点,因为没有可太多配置的东西,所以并不需要太多接触,大大减少了人为出错的几率; 14 | 15 | 3、工作稳定,自身有完整的双机热备方案,如 LVS+Keepalived 和 LVS+Heartbeat,不过我们在项目实施中用得最多的还是 LVS/DR+Keepalived; 16 | 17 | 4、无流量,保证了均衡器 IO 的性能不会收到大流量的影响; 18 | 19 | 5、应用范围比较广,可以对所有应用做负载均衡; 20 | 21 | 6、软件本身不支持正则处理,不能做动静分离,这个就比较遗憾了;其实现在许多网站在这方面都有较强的需求,这个是 Nginx/HAProxy+Keepalived 的优势所在。 22 | 23 | 7、如果是网站应用比较庞大的话,实施 LVS/DR+Keepalived 起来就比较复杂了,特别后面有 Windows Server 应用的机器的话,如果实施及配置还有维护过程就比较复杂了,相对而言,Nginx/HAProxy+Keepalived 就简单多了。 24 | 25 | ![](https://tva3.sinaimg.cn/large/007DFXDhgy1g5us7zazn4j30ly0f03zb.jpg) 26 | 27 | 客户端进行请求时,流程如下: 28 | 29 | - 使用 VIP 地址访问 DS,此时的地址二元组为。 30 | 31 | - DS 根据自己的负载均衡算法,选择一个 RS 将请求转发过去,在转发过去的时候,修改请求的源 IP 地址为 DIP 地址,让 RS 看上去认为是 DS 在访问它,此时的地址二元组为。 32 | 33 | - RS 处理并且应答该请求,这个回报的源地址为 RS 的 RIP 地址,目的地址为 DIP 地址,此时的地址二元组为。 34 | 35 | - DS 在收到该应答包之后,将报文应答客户端,此时修改应答报文的源地址为 VIP 地址,目的地址为 CIP 地址,此时的地址二元组为。 36 | 37 | # HTTP 层负载均衡 38 | 39 | 以常见的 TCP 为例,负载均衡设备如果要根据真正的应用层内容再选择服务器,只能先代理最终的服务器和客户端建立连接(TCP 三次握手)后,才可能接收到客户端发送的真正应用层内容的报文,然后再根据该报文中的特定字段,再加上负载均衡设备设置的服务器选择方式,决定最终选择的内部服务器。负载均衡设备在这种情况下,更类似于一个代理服务器。负载均衡和前端的客户端以及后端的服务器会分别建立 TCP 连接。所以从这个技术原理上来看,七层负载均衡明显地对负载均衡设备的要求更高,处理七层的能力也必然会低于四层模式的部署方式。 40 | 41 | 七层应用负载均衡的好处,是使得整个网络更“智能化”, 例如访问一个网站的用户流量,可以通过七层的方式,将对图片类的请求转发到特定的图片服务器并可以使用缓存技术;将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术。当然这只是七层应用的一个小案例,从技术原理上,这种方式可以对客户端的请求和服务器的响应进行任意意义上的修改,极大的提升了应用系统在网络层的灵活性。很多在后台(例如 Nginx 或者 Apache )上部署的功能可以前移到负载均衡设备上,例如客户请求中的 Header 重写,服务器响应中的关键字过滤或者内容插入等功能。 42 | 43 | 七层负载均衡在安全性方面也有一定的考量,以网络中最常见的 SYN Flood 攻击,即黑客控制众多源客户端,使用虚假 IP 地址对同一目标发送 SYN 攻击,通常这种攻击会大量发送 SYN 报文,耗尽服务器上的相关资源,以达到 Denial of Service(DoS) 的目的。从技术原理上也可以看出,四层模式下这些 SYN 攻击都会被转发到后端的服务器上。而七层模式下这些 SYN 攻击自然在负载均衡设备上就截止,不会影响后台服务器的正常运营。另外负载均衡设备可以在七层层面设定多种策略,过滤特定报文,例如 SQL Injection 等应用层面的特定攻击手段,从应用层面进一步提高系统整体安全。现在的 7 层负载均衡,主要还是着重于应用广泛的 HTTP 协议,所以其应用范围主要是众多的网站或者内部信息平台等基于 B/S 开发的系统。4 层负载均衡则对应其他 TCP 应用,例如基于 C/S 开发的 ERP 等系统。 44 | 45 | 以常见的 Nginx 服务器为例,七层负载均衡的特性在于: 46 | 47 | 1、工作在网络的七层之上,可以针对 http 应用做一些分流的策略,比如针对域名、目录结构,它的正则规则比 HAProxy 更为强大和灵活; 48 | 49 | 2、Nginx 对网络的依赖非常小,理论上能 ping 通就就能进行负载功能,这个也是它的优势所在; 50 | 51 | 3、Nginx 安装和配置比较简单,测试起来比较方便; 52 | 53 | 4、也可以承担高的负载压力且稳定,一般能支撑超过几万次的并发量; 54 | 55 | 5、Nginx 可以通过端口检测到服务器内部的故障,比如根据服务器处理网页返回的状态码、超时等等,并且会把返回错误的请求重新提交到另一个节点,不过其中缺点就是不支持 url 来检测; 56 | 57 | 6、Nginx 仅能支持 http 和 Email,这样就在适用范围上面小很多,这个它的弱势; 58 | 59 | 7、Nginx 不仅仅是一款优秀的负载均衡器/反向代理软件,它同时也是功能强大的 Web 应用服务器。LNMP 现在也是非常流行的 web 架构,大有和以前最流行的 LAMP 架构分庭抗争之势,在高流量的环境中也有很好的效果。 60 | 61 | 8、Nginx 现在作为 Web 反向加速缓存越来越成熟了,速度比传统的 Squid 服务器更快。 62 | 63 | 此时一个提供七层 HTTP 访问接口的服务架构大体是这样的: 64 | 65 | ![](https://tva2.sinaimg.cn/large/007DFXDhgy1g5us9afmd7j30qo0e4q3t.jpg) 66 | 67 | # 应用层负载均衡 68 | 69 | 在实际的部署中,我们往往又会在 HTTP 层之上架设专属的应用层负载均衡,其特性在于: 70 | 71 | HAProxy 的特点是: 72 | 73 | 1、HAProxy 是支持虚拟主机的,以前有朋友说这个不支持虚拟主机,我这里特此更正一下。 74 | 75 | 2、能够补充 Nginx 的一些缺点比如 Session 的保持,Cookie 的引导等工作 76 | 77 | 3、支持 url 检测后端的服务器出问题的检测会有很好的帮助。 78 | 79 | 4、它跟 LVS 一样,本身仅仅就只是一款负载均衡软件;单纯从效率上来讲 HAProxy 更会比 Nginx 有更出色的负载均衡速度,在并发处理上也是优于 Nginx 的。 80 | 81 | 5、HAProxy 可以对 Mysql 读进行负载均衡,对后端的 MySQL 节点进行检测和负载均衡,不过在后端的 MySQL slaves 数量超过 10 台时性能不如 LVS,所以我向大家推荐 LVS+Keepalived。 82 | 83 | 6、HAProxy 的算法现在也越来越多了,具体有如下 8 种: 84 | 85 | - roundrobin,表示简单的轮询,这个不多说,这个是负载均衡基本都具备的; 86 | - static-rr,表示根据权重,建议关注; 87 | - leastconn,表示最少连接者先处理,建议关注; 88 | - ource,表示根据请求源 IP,这个跟 Nginx 的 IP_hash 机制类似,我们用其作为解决 session 问题的一种方法,建议关注; 89 | - ri,表示根据请求的 URI; 90 | - rl_param,表示根据请求的 URl 参数 'balance url_param' requires an URL parameter name; 91 | - hdr(name),表示根据 HTTP 请求头来锁定每一次 HTTP 请求; 92 | - rdp-cookie(name),表示根据据 cookie(name) 来锁定并哈希每一次 TCP 请求。 93 | -------------------------------------------------------------------------------- /04~接入网关/04~接入网关/Kong/Kong.md: -------------------------------------------------------------------------------- 1 | # 基于 Kong 的微服务网关架构 2 | 3 | Kong = OpenResty + Nginx,我们可以直接在 Docker 上启动 Kong 实例: 4 | 5 | ```sh 6 | # 使用 Postgres 作为数据存储 7 | $ docker run -d --name kong-database \ 8 | -p 5432:5432 \ 9 | -e "POSTGRES_USER=kong" \ 10 | -e "POSTGRES_DB=kong" \ 11 | postgres:9.4 12 | 13 | # 进行数据库迁移 14 | $ docker run --rm \ 15 | --link kong-database:kong-database \ 16 | -e "KONG_DATABASE=postgres" \ 17 | -e "KONG_PG_HOST=kong-database" \ 18 | -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \ 19 | kong:latest kong migrations up 20 | 21 | # 启动 Kong 22 | $ docker run -d --name kong \ 23 | --link kong-database:kong-database \ 24 | -e "KONG_DATABASE=postgres" \ 25 | -e "KONG_PG_HOST=kong-database" \ 26 | -e "KONG_CASSANDRA_CONTACT_POINTS=kong-database" \ 27 | -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \ 28 | -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \ 29 | -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \ 30 | -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \ 31 | -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \ 32 | -e "KONG_ADMIN_LISTEN_SSL=0.0.0.0:8444" \ 33 | -p 8000:8000 \ 34 | -p 8443:8443 \ 35 | -p 8001:8001 \ 36 | -p 8444:8444 \ 37 | kong:latest 38 | 39 | # 判断 Kong 是否启动成功 40 | $ curl -i http://localhost:8001/ 41 | 42 | 43 | # 使用 Konga 作为界面化管理 44 | # Admin login: admin | password: adminadminadmin 45 | $ docker run -p 1337:1337 \ 46 | --link kong:kong \ 47 | --name konga \ 48 | -e "NODE_ENV=production" \ 49 | pantsel/konga 50 | ``` 51 | 52 | Kong 使用的各个端口的作用依次为: 53 | :8000 on which Kong listens for incoming HTTP traffic from your clients, and forwards it to your upstream services. 54 | :8443 on which Kong listens for incoming HTTPS traffic. This port has a similar behavior as the :8000 port, except that it expects HTTPS traffic only. This port can be disabled via the configuration file. 55 | :8001 on which the Admin API used to configure Kong listens. 56 | :8444 on which the Admin API listens for HTTPS traffic. 57 | Kong 启动完毕后,可以在 Konga 或者直接以 API 请求的方式注册与消费 API: 58 | 59 | ```sh 60 | # 注册新的 API 61 | $ curl -i -X POST \ 62 | --url http://localhost:8001/apis/ \ 63 | --data 'name=example-api' \ 64 | --data 'hosts=example.com' \ 65 | --data 'upstream_url=http://mockbin.org' 66 | 67 | # 添加插件 68 | $ curl -i -X POST \ 69 | --url http://localhost:8001/apis/example-api/plugins/ \ 70 | --data 'name=rate-limiting' \ 71 | --data 'config.minute=100' 72 | 73 | # 尝试进行访问 74 | $ curl -i -X GET \ 75 | --url http://localhost:8000/ \ 76 | --header 'Host: example.com' 77 | ``` 78 | -------------------------------------------------------------------------------- /04~接入网关/05~长连接服务/DeepStream/DeepStream.md: -------------------------------------------------------------------------------- 1 | # DeepStream 2 | -------------------------------------------------------------------------------- /04~接入网关/05~长连接服务/README.md: -------------------------------------------------------------------------------- 1 | # 长连接网关 2 | 3 | 随着科技的飞速发展,技术的日新月异,长连接的运用场景日益增多。不仅在后端服务中被广泛运用,比较常见的有数据库的访问、服务内部状态的协调等,而且在 App 端的消息推送、聊天信息、直播弹字幕等场景长连接服务也是优选方案。长连接服务的重要性也在各个场合被业界专家不断提及,与此同时也引起了更为广泛地关注和讨论,各大公司也开始构建自己的长连接服务。 4 | 5 | 连接服务的产生是为了解决云端和终端的数据通信问题。不同的业务场景对通信的实时性、可用性和可靠性的要求都不一样,也就使得连接服务要适应各种各样的场景。 6 | 7 | 有些场景对实时性要求相对较高,对可靠性要求不是很严格,如对实时交通路 况的推送消息,该消息就要求在一定时间内送达,没有按时送达的消息,就没有推送的意义了。 8 | 9 | 而对于常见的 IM 场景就刚好相反的策略,用户希望接收到消息,尽管消息投递时用户不在线,这种情况下就对消息的可靠性要求提出更高的要求。 10 | 11 | 从以上常见场景出发,结合物联网的特殊性:既有业务对数据的实时性要求较高,又有设备对数据的可靠性有苛刻的要求,所以连接服务会结合不同的应用场景做出不同的应对策略。 12 | 13 | ![连接服务部署](https://s2.ax1x.com/2019/10/30/K4lQm9.png) 14 | 15 | # 背景特性 16 | 17 | ## 设计目标 18 | 19 | - 实时性:满足云端能及时地将数据传达给设备,进而达到云端能够实时控制设备的目的。 20 | - 低功耗:在一些物联网受限的设备上,功耗也是一个重要的考量标准,所以要针对功耗要求严格的设备,进行特殊的处理以满足功耗的要求。 21 | - 多协议的支持:物联网设备类型繁多,不同的厂商、不同类型的设备可能采用不同的传输协议,所以连接服务要能够处理不同的协议,做到多协议的互通。 22 | - 多网络运营商:作为和终端的接入点,保证不同网络运营商的用户都能正常,高效的接入服务,也是服务设计的一个重要指标,如果网络都不能介入的话,其它都将无从谈起。 23 | - 多机房的部署:因为连接服务的定位是面向大量的物联网设备,而高可用性应放在一个非常重要的位置上,所以要求服务能够支持异地多机房的部署。说到多机房的方案必须要跟具体的业务相结合才有意义,连接服务之所以能够相对较简单的实现跟我们的服务本身定位有直接关系,服务没有持久化的消息数据,只是作为一个传输通道。另一方面,这个层面达到了多机房,但是如果具体的业务线不能达到多机房的部署(这个又是非常困难的,具体业务一般都有持久化的数据),对最终的终端来说依然是不能够达到机房宕机完全无影响的效果。假如每个点都提高一点高可用性,整体的可用性就会越来越好。 24 | - 多业务线:由于各业务线的重要程度不同,对数据服务质量的要求也是不同的,所以要针对不同的业务线做不同的处理,并且要做好不同业务线之间的隔离。 25 | 26 | 且不同的业务线的技术栈的不同,业务回调的实时性要求不同,也决定了我们需要采用不同的业务数据回调方式。 27 | 28 | 7、数据安全性 29 | 30 | 物联网设备的数据都是直接和硬件相关的信息,所以对数据的安全性要求比传统的互联网模式下的要求要高。 31 | 32 | ## 技术要求 33 | 34 | 当我们在设计长连接网关时候,需要关注以下方面: 35 | 36 | - 集中实现长连接管理和推送能力。统一技术栈,将长连接作为基础能力沉淀,便于功能迭代和升级维护。 37 | - 与业务解耦。将业务逻辑与长连接通信分离,使业务系统不再关心通信细节,也避免了重复开发,浪费研发成本。 38 | - 使用简单。提供 HTTP 推送通道,方便各种开发语言的接入。业务系统只需要简单的调用,就可以实现数据推送,提升研发效率。 39 | - 分布式架构。实现多节点的集群,支持水平扩展应对业务增长带来的挑战;节点宕机不影响服务整体可用性,保证高可靠。 40 | - 多端消息同步。允许用户使用多个浏览器或标签页同时登陆在线,保证消息同步发送。 41 | - 多维度监控与报警。自定义监控指标与现有微服务监控系统打通,出现问题时可及时报警,保证服务的稳定性。 42 | -------------------------------------------------------------------------------- /04~接入网关/05~长连接服务/WebSocket 网关/README.md: -------------------------------------------------------------------------------- 1 | # WebSocket 网关 2 | 3 | HTTP 协议是一种无状态的、基于 TCP 的请求/响应模式的协议,请求只能由客户端发起、服务端进行响应。在大多数场景,这种请求/响应的 Pull 模式已经可以满足需求。但在某些情形,例如消息推送、通知等应用场景,需要实时将数据同步到客户端,这就要求服务端支持主动 Push 数据。 4 | 5 | 服务端推送技术历史悠久,经历了短轮询、长轮询的发展,一定程度上能够解决问题,但也存在着不足,例如时效性、资源浪费等。HTML5 标准带来的 WebSocket 规范基本结束了这一局面,成为目前服务端推送技术的主流方案。 6 | 7 | 在系统中集成 WebSocket 十分简单,相关讨论与资料很丰富。但如何实现一个通用的 WebSocket 推送网关尚未有成熟的方案。目前的云服务厂商主要关注 iOS 和安卓等移动端推送,也缺少对 WebSocket 的支持。 8 | -------------------------------------------------------------------------------- /04~接入网关/05~长连接服务/多机房多网络.md: -------------------------------------------------------------------------------- 1 | # 多机房多网络部署 2 | 3 | ![多机房连接部署示意图](https://s2.ax1x.com/2019/10/30/K4l00A.png) 4 | 5 | # Links 6 | 7 | - https://mp.weixin.qq.com/s/QZwXDGDUInJPX5h1BfHH2A 8 | -------------------------------------------------------------------------------- /04~接入网关/10~实践案例/2021-喜马拉雅-自研网关架构演进过程.md: -------------------------------------------------------------------------------- 1 | # 喜马拉雅自研网关架构演进过程 2 | 3 | 网关除了要实现最基本的功能反向代理外,还有公有特性,比如黑白名单,流控,鉴权,熔断,API 发布,监控和报警等,我们还根据业务方的需求实现了流量调度,流量 Copy,预发布,智能化升降级,流量预热等相关功能,下面就我们网关在这些方便的一些实践经验以及发展历程,下面是喜马拉雅网关的演化过程: 4 | 5 | ![喜马拉雅网关演化](https://pic.imgdb.cn/item/6054476c524f85ce29082b65.jpg) 6 | 7 | # Tomcat NIO + AsyncServlet 8 | 9 | 网关在架构设计时最为关键点,就是网关在接收到请求,调用后端服务时不能阻塞 Block,否则网关的吞吐量很难上去,因为最耗时的就是调用后端服务这个远程调用过程,如果这里是阻塞的,Tomcat 的工作线程都 block 了,在等待后端服务响应的过程中,不能去处理其他的请求,这个地方一定要异步: 10 | 11 | ![架构图](https://pic.imgdb.cn/item/60544795524f85ce2908423e.jpg) 12 | 13 | 这版我们实现单独的 Push 层,作为网关收到响应后,响应客户端时,通过这层实现,和后端服务的通信是 HttpNioClient,对业务的支持黑白名单,流控,鉴权,API 发布等功能。 14 | 15 | 但是这版只是功能上达到网关的要求,处理能力很快就成了瓶颈,单机 qps 到 5k 的时候,就会不停的 full gc,后面通过 dump 线上的堆分析,发现全是 Tomcat 缓存了很多 HTTP 的请求,因为 Tomcat 默认会缓存 200 个 requestProcessor,每个 prcessor 都关联了一个 request,还有就是 Servlet 3.0 Tomcat 的异步实现会出现内存泄漏,后面通过减少这个配置,效果明显。但性能肯定就下降了,总结了下,基于 Tomcat 做为接入端,有如下几个问题: 16 | 17 | ## Tomcat 自身的问题: 18 | 19 | - 缓存太多,Tomcat 用了很多对象池技术,内存有限的情况下,流量一高很容易触发 gc。 20 | - 内存 copy,Tomcat 的默认是用堆内存,所以数据需要读到堆内,而我们后端服务是 Netty,有堆外内存,需要通过数次 copy。 21 | - Tomcat 还有个问题是读 body 是阻塞的, Tomcat 的 NIO 模型和 reactor 模型不一样,读 body 是 block 的。 22 | 23 | ![Tomcat buffer 的关系图](https://pic.imgdb.cn/item/6054497d524f85ce2909aa86.jpg) 24 | 25 | 通过上面的图,我们可以看出,Tomcat 对外封装的很好,内部默认的情况下会有三次 copy 26 | 27 | ## HttpNioClient 的问题 28 | 29 | 获取和释放连接都需要加锁,对应网关这样的代理服务场景,会频繁的建连和关闭连接,势必会影响性能。 30 | 31 | # 第二版 Netty + 全异步 32 | 33 | 基于 Netty 的优势,我们实现了全异步,无锁,分层的架构,先看下我们基于 Netty 做接入端的架构图: 34 | 35 | ![Netty 架构](https://pic.imgdb.cn/item/605449d5524f85ce2909e176.jpg) 36 | 37 | ## 接入层 38 | 39 | Netty 的 IO 线程,负责 HTTP 协议的编解码工作,同时对协议层面的异常做监控报警 40 | 41 | 对 HTTP 协议的编解码做了优化,对异常,攻击性请求监控可视化。比如我们对 HTTP 的请求行和请求头大小是有限制的,Tomcat 是请求行和请求加在一起,不超过 8k,Netty 是分别有大小限制。假如客户端发送了超过阀值的请求,带 cookie 的请求很容易超过,正常情况下,Netty 就直接响应 400 给客户端。 42 | 43 | 经过改造后,我们只取正常大小的部分,同时标记协议解析失败,到业务层后,就可以判断出是那个服务出现这类问题,其他的一些攻击性的请求,比如只发请求头,不发 body 或者发部分这些都需要监控和报警。 44 | 45 | ## 业务逻辑层 46 | 47 | 负责对 API 路由,流量调度等一序列的支持业务的公有逻辑,都在这层实现,采样责任链模式,这层不会有 IO 操作。在业界和一些大厂的网关设计中,业务逻辑层基本都是设计成责任链模式,公有的业务逻辑也在这层实现,我们在这层也是相同的套路,支持了: 48 | 49 | - **用户鉴权和登陆校验**,支持接口级别配置 50 | - **黑白名单**,分全局和应用,以及 ip 维度,参数级别 51 | - **流量控制**,支持自动和手动,自动是对超大流量自动拦截,通过令牌桶算法实现 52 | - **智能熔断**,在 histrix 的基础上做了改进,支持自动升降级,我们是全部自动的,也支持手动配置立即熔断,就是发现服务异常比例达到阀值,就自动触发熔断 53 | - **灰度发布**,我对新启动的机器的流量支持类似 tcp 的慢启动机制,给机器一个预热的时间窗口 54 | - **统一降级**,我们对所有转发失败的请求都会找统一降级的逻辑,只要业务方配了降级规则,都会降级,我们对降级规则是支持到参数级别的,包含请求头里的值,是非常细粒度的,另外我们还会和 varnish 打通,支持 varnish 的优雅降级 55 | - **流量调度**,支持业务根据筛选规则,对流量筛选到对应的机器,也支持只让筛选的流量访问这台机器,这在查问题/新功能发布验证时非常用,可以先通过小部分流量验证再大面积发布上线。 56 | - **流量 copy**,我们支持对线上的原始请求根据规则 copy 一份,写入到 mq 或者其他的 upstream,来做线上跨机房验证和压力测试。 57 | - **请求日志采样**,我们对所有的失败的请求都会采样落盘,提供业务方排查问题支持,也支持业务方根据规则进行个性化采样,我们采样了整个生命周期的数据,包含请求和响应相关的所有数据。 58 | 59 | 上面提到的这么多都是对流量的治理,我们每个功能都是一个 filter,处理失败都不影响转发流程,而且所有的这些规则的元数据在网关启动时就会全部初始化好。在执行的过程中,不会有 IO 操作,目前有些设计会对多个 filter 做并发执行,由于我们的都是内存操作,开销并不大,所以我们目前并没有支持并发执行。 60 | 61 | 还有个就是规则会修改,我们修改规则时,会通知网关服务,做实时刷新,我们对内部自己的这种元数据更新的请求,通过独立的线程处理,防止 IO 在操作时影响业务线程。 62 | 63 | ## 服务调用层 64 | 65 | 服务调用对于代理网关服务是关键的地方,一定需要异步,我们通过 Netty 实现,同时也很好的利用了 Netty 提供的连接池,做到了获取和释放都是无锁操作。 66 | 67 | ## 异步 Push 68 | 69 | 网关在发起服务调用后,让工作线程继续处理其他的请求,而不需要等待服务端返回,这里的设计是我们为每个请求都会创建一个上下文,我们在发完请求后,把该请求的 context 绑定到对应的连接上,等 Netty 收到服务端响应时,就会在给连接上执行 read 操作。 70 | 71 | 解码完后,再从给连接上获取对应的 context,通过 context 可以获取到接入端的 session,这样 push 就通过 session 把响应写回客户端了,这样设计也是基于 HTTP 的连接是独占的,即连接和请求上下文绑定。 72 | 73 | ## 连接池 74 | 75 | 连接池的原理如下图: 76 | 77 | ![连接池的原理](https://pic.imgdb.cn/item/60544a45524f85ce290a2bed.jpg) 78 | 79 | 服务调用层除了异步发起远程调用外,还需要对后端服务的连接进行管理,HTTP 不同于 RPC,HTTP 的连接是独占的,所以在释放的时候要特别小心,一定要等服务端响应完了才能释放,还有就是连接关闭的处理也要小心,总结如下几点: 80 | 81 | - Connection:close 82 | - 空闲超时,关闭连接 83 | - 读超时关闭连接 84 | - 写超时,关闭连接 85 | - Fin,Reset 86 | 87 | 上面几种需要关闭连接的场景,下面主要说下 Connection:close 和空闲写超时两种,其他的应该是比较常见的比如读超时,连接空闲超时,收到 fin,reset 码这几个。 88 | 89 | - Connection:close 90 | 91 | 后端服务是 Tomcat,Tomcat 对连接重用的次数是有限制的,默认是 100 次,当达到 100 次后,Tomcat 会通过在响应头里添加 Connection:close,让客户端关闭该连接,否则如果再用该连接发送的话,会出现 400。 92 | 93 | 还有就是如果端上的请求带了 connection:close,那 Tomcat 就不等这个连接重用到 100 次,即一次就关闭,通过在响应头里添加 Connection:close,即成了短连接,这个在和 Tomcat 保持长连接时,需要注意的,如果要利用,就要主动 remove 掉这个 close 头。 94 | 95 | - 写超时 96 | 97 | 首先网关什么时候开始计算服务的超时时间,如果从调用 writeAndFlush 开始就计算,这其实是包含了 Netty 对 HTTP 的 encode 时间和从队列里把请求发出去即 flush 的时间,这样是对后端服务不公平的,所以需要在真正 flush 成功后开始计时,这样是和服务端最接近的,当然还包含了网络往返时间和内核协议栈处理的时间,这个不可避免,但基本不变。 98 | 99 | 所以我们是 flush 成功回调后开始启动超时任务,这里就有个注意的地方,如果 flush 不能快速回调,比如来了一个大的 post 请求,body 部分比较大,而 Netty 发送的时候第一次默认是发 1k 的大小,如果还没有发完,则增大发送的大小继续发,如果在 Netty 在 16 次后还没有发送完成,则不会再继续发送,而是提交一个 flushTask 到任务队列,待下次执行到后再发送。这时 flush 回调的时间就比较大,导致这样的请求不能及时关闭,而且后端服务 Tomcat 会一直阻塞在读 body 的地方,基于上面的分析,所以我们需要一个写超时,对大的 body 请求,通过写超时来及时关闭。 100 | 101 | ## 全链路超时机制 102 | 103 | 下面是我们在整个链路超时处理的机制。 104 | 105 | ![超时处理机制](https://pic.imgdb.cn/item/60544af1524f85ce290ab657.jpg) 106 | 107 | - 协议解析超时 108 | - 等待队列超时 109 | - 建连超时 110 | - 等待连接超时 111 | - 写前检查是否超时 112 | - 写超时 113 | - 响应超时 114 | 115 | ## 监控报警 116 | 117 | 网关业务方能看到的是监控和报警,我们是实现秒级别报警和秒级别的监控,监控数据定时上报给我们的管理系统,由管理系统负责聚合统计,落盘到 influxdb。我们对 HTTP 协议做了全面的监控和报警,无论是协议层的还是服务层的: 118 | 119 | - 协议层 120 | 121 | - 攻击性请求,只发头,不发/发部分 body,采样落盘,还原现场,并报警 122 | - Line or Head or Body 过大的请求,采样落盘,还原现场,并报警 123 | 124 | - 应用层 125 | - 耗时监控,有慢请求,超时请求,以及 tp99,tp999 等 126 | - qps 监控和报警 127 | - 带宽监控和报警,支持对请求和响应的行,头,body 单独监控。 128 | - 响应码监控,特别是 400,和 404 129 | - 连接监控,我们对接入端的连接,以及和后端服务的连接,后端服务连接上待发送字节大小也都做了监控 130 | - 失败请求监控 131 | - 流量抖动报警,这是非常有必要的,流量抖动要么是出了问题,要么就是出问题的前兆。 132 | 133 | ## 性能优化实践 134 | 135 | ### 对象池技术 136 | 137 | 对于高并发系统,频繁的创建对象不仅有分配内存的开销外,还有对 GC 会造成压力,我们在实现时会对频繁使用的比如线程池的任务 task,StringBuffer 等会做写重用,减少频繁的申请内存的开销。 138 | 139 | ### 上下文切换 140 | 141 | 高并发系统,通常都采用异步设计,异步化后,不得不考虑线程上下文切换的问题,我们的线程模型如下: 142 | 143 | ![上下文切换](https://pic.imgdb.cn/item/60544b99524f85ce290b1cdf.jpg) 144 | 145 | 我们整个网关没有涉及到 IO 操作,但我们在业务逻辑这块还是和 Netty 的 IO 编解码线程异步,是有两个原因,1)是防止开发写的代码有阻塞,2)是业务逻辑打日志可能会比较多,在突发的情况下,在 push 线程时,支持用 Netty 的 IO 线程替代,这里做的工作比较少,这里有异步修改为同步后(通过修改配置调整),CPU 的上下文切换减少 20%,进而提高了整体的吞吐量,就是不能为了异步而异步,zull2 的设计和我们的类似。 146 | 147 | ### GC 优化 148 | 149 | 在高并发系统,GC 的优化不可避免,我们在用了对象池技术和堆外内存时,对象很少进入老年代,另外我们年轻代会设置的比较大,而且 SurvivorRatio=2,晋升年龄设置最大 15,尽量对象在年轻代就回收掉,但监控发现老年代的内存还是会缓慢增长。 150 | 151 | 通过 dump 分析,我们每个后端服务创建一个连接,都时有一个 socket,socket 的 AbstractPlainSocketImpl,而 AbstractPlainSocketImpl 就重写了 Object 类的 finalize 方法,实现如下: 152 | 153 | ```java 154 | /** 155 | * Cleans up if the user forgets to close it. 156 | */ 157 | protected void finalize() throws IOException { 158 | close(); 159 | } 160 | ``` 161 | 162 | 是为了我们没有主动关闭连接,做的一个兜底,在 GC 回收的时候,先把对应的连接资源给释放了,由于 finalize 的机制是通过 JVM 的 Finalizer 线程来处理的,而且 Finalizer 线程的优先级不高,默认是 8,需要等到 Finalizer 线程把 ReferenceQueue 的对象对于的 finalize 方法执行完,还要等到下次 GC 时,才能把该对象回收,导致创建连接的这些对象在年轻代不能立即回收,从而进入了老年代,这也是为啥老年代会一直缓慢增长的问题。 163 | 164 | ### 日志 165 | 166 | 高并发下,特别是 Netty 的 IO 线程除了要执行该线程上的 IO 读写操作,还有执行异步任务和定时任务,如果 IO 线程处理不过来队列里的任务,很有可能导致新进来异步任务出现被拒绝的情况。 167 | 168 | 那什么情况下可能呢,IO 是异步读写的问题不大,就是多耗点 CPU,最有可能 block 住 IO 线程的是我们打的日志,目前 Log4j 的 ConsoleAppender 日志 immediateFlush 属性默认为 true,即每次打 log 都是同步写 flush 到磁盘的,这个对于内存操作来说,慢了很多。 169 | 170 | 同时 AsyncAppender 的日志队列满了也会 block 住线程,log4j 默认的 buffer 大小是 128,而且是 block 的,即如果 buffer 的大小达到 128,就阻塞了写日志的线程,在并发写日志量大的的情况下,特别是堆栈很多时,log4j 的 Dispatcher 线程会出现变慢要刷盘,这样 buffer 就不能快速消费,很容易写满日志事件,导致 Netty IO 线程 block 住,所以我们在打日志时,也要注意精简。 171 | -------------------------------------------------------------------------------- /04~接入网关/10~实践案例/2021-爱奇艺-基于 Netty 的长连接网关.md: -------------------------------------------------------------------------------- 1 | # 2021-基于 Netty 的长连接网关 2 | 3 | WebSocket 是目前实现服务端推送的主流技术,恰当使用能够有效提供系统响应能力,提升用户体验。通过 WebSocket 长连接网关可以快速为系统增加数据推送能力,有效减少运维成本,提高开发效率。长连接网关的价值在于它封装了 WebSocket 通信细节,与业务系统解耦,使得长连接网关与业务系统可独立优化迭代,避免重复开发,便于开发与维护。其次,网关提供了简单易用的 HTTP 推送通道,支持多种开发语言接入,便于系统集成和使用。另外,网关采用了分布式架构,可以实现服务的水平扩容、负载均衡与高可用。最后,网关集成了监控与报警,当系统异常时能及时预警,保证服务的健康和稳定。 4 | 5 | 在众多的 WebSocket 实现中,从性能、扩展性、社区支持等方面考虑,最终选择了 Netty。Netty 是一个高性能、事件驱动、异步非阻塞的网络通信框架,在许多知名的开源软件中被广泛使用。WebSocket 是有状态的,无法像直接 HTTP 以集群方式实现负载均衡,长连接建立后即与服务端某个节点保持着会话,因此集群下想要得知会话属于哪个节点,有两种方案,一种是使用类似微服务的注册中心来维护全局的会话映射关系,一种是使用事件广播由各节点自行判断是否持有会话,两种方案对比如下表所示。 6 | 7 | | 方案 | 优点 | 缺点 | 8 | | -------- | -------------------------------------- | ---------------------------------------- | 9 | | 注册中心 | 会话映射关系清晰,集群规模较大时更合适 | 实现复杂,强依赖注册中心,有额外运维成本 | 10 | | 事件广播 | 实现简单更加轻量 | 节点较多时,所有节点均被广播,资源浪费 | 11 | 12 | 综合考虑实现成本与集群规模,选择了轻量级的事件广播方案。实现广播可以选择基于 RocketMQ 的消息广播、基于 Redis 的 Publish/Subscribe、基于 ZooKeeper 的通知等方案,其优缺点对比如下表所示。从吞吐量、实时性、持久化、实现难易等方面考虑,最终选择了 RocketMQ。 13 | 14 | | 方案 | 优点 | 缺点 | 15 | | -------------- | -------------------------- | -------------------------------- | 16 | | 基于 RocketMQ | 吞吐量高、高可用、保证可靠 | 实时性不如 Redis | 17 | | 基于 Redis | 实时性高、实现简单 | 不保证可靠 | 18 | | 基于 ZooKeeper | 实现简单 | 写入性能较差,不适合频繁写入场景 | 19 | 20 | ## 系统架构 21 | 22 | ![Netty & WebSocket 架构](https://img.imgdb.cn/item/6051b557524f85ce299b8bce.jpg) 23 | 24 | 网关的整体流程如下: 25 | 26 | - 客户端与网关任一节点握手建立起长连接,节点将其加入到内存维护的长连接队列。客户端定时向服务端发送心跳消息,如果超过设定的时间仍没有收到心跳,则认为客户端与服务端的长连接已断开,服务端会关闭连接,清理内存中的会话。 27 | - 当业务系统需要向客户端推送数据时,通过网关提供的 HTTP 接口将数据发向网关。 28 | - 网关在接收到推送请求后,将消息写入 RocketMQ。 29 | - 网关作为消费者,以广播模式消费消息,所有节点都会接收到消息。 30 | - 节点接收到消息后判断推送的消息目标是否在自己内存中维护的长连接队列里,如果存在则通过长连接推送数据,否则直接忽略。 31 | 32 | 网关以多节点方式构成集群,每节点负责一部分长连接,可实现负载均衡,当面对海量连接时,也可以通过增加节点的方式分担压力,实现水平扩展。同时,当节点出现宕机时,客户端会尝试重新与其他节点握手建立长连接,保证服务整体的可用性。 33 | 34 | ## 会话管理 35 | 36 | 长连接建立起来后,会话维护在各节点的内存中。SessionManager 组件负责管理会话,内部使用了哈希表维护了 UID 与 UserSession 的关系;UserSession 代表用户维度的会话,一个用户可能会同时建立多个长连接,因此 UserSession 内部同样使用了一个哈希表维护 Channel 与 ChannelSession 的关系。为了避免用户无限制的创建长连接,UserSession 在内部的 ChannelSession 超过一定数量后,会将最早建立的 ChannelSession 关闭,减少服务器资源占用。SessionManager、UserSession、ChannelSession 的关系如下图所示。 37 | 38 | ![SessionManager 组件](https://img.imgdb.cn/item/6051b608524f85ce299bf4a7.jpg) 39 | -------------------------------------------------------------------------------- /04~接入网关/10~实践案例/2022-How Tinder Built Their Own API Gateway.md: -------------------------------------------------------------------------------- 1 | # How Tinder Built Their Own API Gateway 2 | 3 | # Links 4 | 5 | - https://blog.quastor.org/p/tinder-built-api-gateway 6 | -------------------------------------------------------------------------------- /04~接入网关/10~实践案例/Scratch/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wx-chevalier/System-Notes/66ed1f216d8988b2294f59f7490fc6f4739ae97a/04~接入网关/10~实践案例/Scratch/README.md -------------------------------------------------------------------------------- /04~接入网关/10~实践案例/Scratch/基于 Netty 与 Webflux 的网关/README.md: -------------------------------------------------------------------------------- 1 | # Links 2 | 3 | - 如何设计一个高性能网关 https://cubox.pro/c/w3Xk31 -------------------------------------------------------------------------------- /04~接入网关/10~实践案例/Scratch/网关特性.md: -------------------------------------------------------------------------------- 1 | # 网关特性 2 | -------------------------------------------------------------------------------- /04~接入网关/README.md: -------------------------------------------------------------------------------- 1 | # API 接入网关 2 | 3 | 值得一提的是,API 接入网关与本篇的另一章节,服务注册与发现有不少的重叠知识点;不过后者更偏向于可信网络下的服务高性能调用,以及服务之前的可用性保障。 4 | 5 | # Links 6 | 7 | - https://github.com/aCoder2013/blog/issues/35 8 | - https://mp.weixin.qq.com/s/2398vOAeXB7v5CHTBOZPmg 微服务与网关技术(SIA-GateWay) 9 | -------------------------------------------------------------------------------- /INTRODUCTION.md: -------------------------------------------------------------------------------- 1 | # 本篇导读 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 2 | Public License 3 | 4 | By exercising the Licensed Rights (defined below), You accept and agree 5 | to be bound by the terms and conditions of this Creative Commons 6 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 7 | ("Public License"). To the extent this Public License may be 8 | interpreted as a contract, You are granted the Licensed Rights in 9 | consideration of Your acceptance of these terms and conditions, and the 10 | Licensor grants You such rights in consideration of benefits the 11 | Licensor receives from making the Licensed Material available under 12 | these terms and conditions. 13 | 14 | 15 | Section 1 -- Definitions. 16 | 17 | a. Adapted Material means material subject to Copyright and Similar 18 | Rights that is derived from or based upon the Licensed Material 19 | and in which the Licensed Material is translated, altered, 20 | arranged, transformed, or otherwise modified in a manner requiring 21 | permission under the Copyright and Similar Rights held by the 22 | Licensor. For purposes of this Public License, where the Licensed 23 | Material is a musical work, performance, or sound recording, 24 | Adapted Material is always produced where the Licensed Material is 25 | synched in timed relation with a moving image. 26 | 27 | b. Adapter's License means the license You apply to Your Copyright 28 | and Similar Rights in Your contributions to Adapted Material in 29 | accordance with the terms and conditions of this Public License. 30 | 31 | c. BY-NC-SA Compatible License means a license listed at 32 | creativecommons.org/compatiblelicenses, approved by Creative 33 | Commons as essentially the equivalent of this Public License. 34 | 35 | d. Copyright and Similar Rights means copyright and/or similar rights 36 | closely related to copyright including, without limitation, 37 | performance, broadcast, sound recording, and Sui Generis Database 38 | Rights, without regard to how the rights are labeled or 39 | categorized. For purposes of this Public License, the rights 40 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 41 | Rights. 42 | 43 | e. Effective Technological Measures means those measures that, in the 44 | absence of proper authority, may not be circumvented under laws 45 | fulfilling obligations under Article 11 of the WIPO Copyright 46 | Treaty adopted on December 20, 1996, and/or similar international 47 | agreements. 48 | 49 | f. Exceptions and Limitations means fair use, fair dealing, and/or 50 | any other exception or limitation to Copyright and Similar Rights 51 | that applies to Your use of the Licensed Material. 52 | 53 | g. License Elements means the license attributes listed in the name 54 | of a Creative Commons Public License. The License Elements of this 55 | Public License are Attribution, NonCommercial, and ShareAlike. 56 | 57 | h. Licensed Material means the artistic or literary work, database, 58 | or other material to which the Licensor applied this Public 59 | License. 60 | 61 | i. Licensed Rights means the rights granted to You subject to the 62 | terms and conditions of this Public License, which are limited to 63 | all Copyright and Similar Rights that apply to Your use of the 64 | Licensed Material and that the Licensor has authority to license. 65 | 66 | j. Licensor means the individual(s) or entity(ies) granting rights 67 | under this Public License. 68 | 69 | k. NonCommercial means not primarily intended for or directed towards 70 | commercial advantage or monetary compensation. For purposes of 71 | this Public License, the exchange of the Licensed Material for 72 | other material subject to Copyright and Similar Rights by digital 73 | file-sharing or similar means is NonCommercial provided there is 74 | no payment of monetary compensation in connection with the 75 | exchange. 76 | 77 | l. Share means to provide material to the public by any means or 78 | process that requires permission under the Licensed Rights, such 79 | as reproduction, public display, public performance, distribution, 80 | dissemination, communication, or importation, and to make material 81 | available to the public including in ways that members of the 82 | public may access the material from a place and at a time 83 | individually chosen by them. 84 | 85 | m. Sui Generis Database Rights means rights other than copyright 86 | resulting from Directive 96/9/EC of the European Parliament and of 87 | the Council of 11 March 1996 on the legal protection of databases, 88 | as amended and/or succeeded, as well as other essentially 89 | equivalent rights anywhere in the world. 90 | 91 | n. You means the individual or entity exercising the Licensed Rights 92 | under this Public License. Your has a corresponding meaning. 93 | 94 | 95 | Section 2 -- Scope. 96 | 97 | a. License grant. 98 | 99 | 1. Subject to the terms and conditions of this Public License, 100 | the Licensor hereby grants You a worldwide, royalty-free, 101 | non-sublicensable, non-exclusive, irrevocable license to 102 | exercise the Licensed Rights in the Licensed Material to: 103 | 104 | a. reproduce and Share the Licensed Material, in whole or 105 | in part, for NonCommercial purposes only; and 106 | 107 | b. produce, reproduce, and Share Adapted Material for 108 | NonCommercial purposes only. 109 | 110 | 2. Exceptions and Limitations. For the avoidance of doubt, where 111 | Exceptions and Limitations apply to Your use, this Public 112 | License does not apply, and You do not need to comply with 113 | its terms and conditions. 114 | 115 | 3. Term. The term of this Public License is specified in Section 116 | 6(a). 117 | 118 | 4. Media and formats; technical modifications allowed. The 119 | Licensor authorizes You to exercise the Licensed Rights in 120 | all media and formats whether now known or hereafter created, 121 | and to make technical modifications necessary to do so. The 122 | Licensor waives and/or agrees not to assert any right or 123 | authority to forbid You from making technical modifications 124 | necessary to exercise the Licensed Rights, including 125 | technical modifications necessary to circumvent Effective 126 | Technological Measures. For purposes of this Public License, 127 | simply making modifications authorized by this Section 2(a) 128 | (4) never produces Adapted Material. 129 | 130 | 5. Downstream recipients. 131 | 132 | a. Offer from the Licensor -- Licensed Material. Every 133 | recipient of the Licensed Material automatically 134 | receives an offer from the Licensor to exercise the 135 | Licensed Rights under the terms and conditions of this 136 | Public License. 137 | 138 | b. Additional offer from the Licensor -- Adapted Material. 139 | Every recipient of Adapted Material from You 140 | automatically receives an offer from the Licensor to 141 | exercise the Licensed Rights in the Adapted Material 142 | under the conditions of the Adapter's License You apply. 143 | 144 | c. No downstream restrictions. You may not offer or impose 145 | any additional or different terms or conditions on, or 146 | apply any Effective Technological Measures to, the 147 | Licensed Material if doing so restricts exercise of the 148 | Licensed Rights by any recipient of the Licensed 149 | Material. 150 | 151 | 6. No endorsement. Nothing in this Public License constitutes or 152 | may be construed as permission to assert or imply that You 153 | are, or that Your use of the Licensed Material is, connected 154 | with, or sponsored, endorsed, or granted official status by, 155 | the Licensor or others designated to receive attribution as 156 | provided in Section 3(a)(1)(A)(i). 157 | 158 | b. Other rights. 159 | 160 | 1. Moral rights, such as the right of integrity, are not 161 | licensed under this Public License, nor are publicity, 162 | privacy, and/or other similar personality rights; however, to 163 | the extent possible, the Licensor waives and/or agrees not to 164 | assert any such rights held by the Licensor to the limited 165 | extent necessary to allow You to exercise the Licensed 166 | Rights, but not otherwise. 167 | 168 | 2. Patent and trademark rights are not licensed under this 169 | Public License. 170 | 171 | 3. To the extent possible, the Licensor waives any right to 172 | collect royalties from You for the exercise of the Licensed 173 | Rights, whether directly or through a collecting society 174 | under any voluntary or waivable statutory or compulsory 175 | licensing scheme. In all other cases the Licensor expressly 176 | reserves any right to collect such royalties, including when 177 | the Licensed Material is used other than for NonCommercial 178 | purposes. 179 | 180 | 181 | Section 3 -- License Conditions. 182 | 183 | Your exercise of the Licensed Rights is expressly made subject to the 184 | following conditions. 185 | 186 | a. Attribution. 187 | 188 | 1. If You Share the Licensed Material (including in modified 189 | form), You must: 190 | 191 | a. retain the following if it is supplied by the Licensor 192 | with the Licensed Material: 193 | 194 | i. identification of the creator(s) of the Licensed 195 | Material and any others designated to receive 196 | attribution, in any reasonable manner requested by 197 | the Licensor (including by pseudonym if 198 | designated); 199 | 200 | ii. a copyright notice; 201 | 202 | iii. a notice that refers to this Public License; 203 | 204 | iv. a notice that refers to the disclaimer of 205 | warranties; 206 | 207 | v. a URI or hyperlink to the Licensed Material to the 208 | extent reasonably practicable; 209 | 210 | b. indicate if You modified the Licensed Material and 211 | retain an indication of any previous modifications; and 212 | 213 | c. indicate the Licensed Material is licensed under this 214 | Public License, and include the text of, or the URI or 215 | hyperlink to, this Public License. 216 | 217 | 2. You may satisfy the conditions in Section 3(a)(1) in any 218 | reasonable manner based on the medium, means, and context in 219 | which You Share the Licensed Material. For example, it may be 220 | reasonable to satisfy the conditions by providing a URI or 221 | hyperlink to a resource that includes the required 222 | information. 223 | 3. If requested by the Licensor, You must remove any of the 224 | information required by Section 3(a)(1)(A) to the extent 225 | reasonably practicable. 226 | 227 | b. ShareAlike. 228 | 229 | In addition to the conditions in Section 3(a), if You Share 230 | Adapted Material You produce, the following conditions also apply. 231 | 232 | 1. The Adapter's License You apply must be a Creative Commons 233 | license with the same License Elements, this version or 234 | later, or a BY-NC-SA Compatible License. 235 | 236 | 2. You must include the text of, or the URI or hyperlink to, the 237 | Adapter's License You apply. You may satisfy this condition 238 | in any reasonable manner based on the medium, means, and 239 | context in which You Share Adapted Material. 240 | 241 | 3. You may not offer or impose any additional or different terms 242 | or conditions on, or apply any Effective Technological 243 | Measures to, Adapted Material that restrict exercise of the 244 | rights granted under the Adapter's License You apply. 245 | 246 | 247 | Section 4 -- Sui Generis Database Rights. 248 | 249 | Where the Licensed Rights include Sui Generis Database Rights that 250 | apply to Your use of the Licensed Material: 251 | 252 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 253 | to extract, reuse, reproduce, and Share all or a substantial 254 | portion of the contents of the database for NonCommercial purposes 255 | only; 256 | 257 | b. if You include all or a substantial portion of the database 258 | contents in a database in which You have Sui Generis Database 259 | Rights, then the database in which You have Sui Generis Database 260 | Rights (but not its individual contents) is Adapted Material, 261 | including for purposes of Section 3(b); and 262 | 263 | c. You must comply with the conditions in Section 3(a) if You Share 264 | all or a substantial portion of the contents of the database. 265 | 266 | For the avoidance of doubt, this Section 4 supplements and does not 267 | replace Your obligations under this Public License where the Licensed 268 | Rights include other Copyright and Similar Rights. 269 | 270 | 271 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 272 | 273 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 274 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 275 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 276 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 277 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 278 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 279 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 280 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 281 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 282 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 283 | 284 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 285 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 286 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 287 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 288 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 289 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 290 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 291 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 292 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 293 | 294 | c. The disclaimer of warranties and limitation of liability provided 295 | above shall be interpreted in a manner that, to the extent 296 | possible, most closely approximates an absolute disclaimer and 297 | waiver of all liability. 298 | 299 | 300 | Section 6 -- Term and Termination. 301 | 302 | a. This Public License applies for the term of the Copyright and 303 | Similar Rights licensed here. However, if You fail to comply with 304 | this Public License, then Your rights under this Public License 305 | terminate automatically. 306 | 307 | b. Where Your right to use the Licensed Material has terminated under 308 | Section 6(a), it reinstates: 309 | 310 | 1. automatically as of the date the violation is cured, provided 311 | it is cured within 30 days of Your discovery of the 312 | violation; or 313 | 314 | 2. upon express reinstatement by the Licensor. 315 | 316 | For the avoidance of doubt, this Section 6(b) does not affect any 317 | right the Licensor may have to seek remedies for Your violations 318 | of this Public License. 319 | 320 | c. For the avoidance of doubt, the Licensor may also offer the 321 | Licensed Material under separate terms or conditions or stop 322 | distributing the Licensed Material at any time; however, doing so 323 | will not terminate this Public License. 324 | 325 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 326 | License. 327 | 328 | 329 | Section 7 -- Other Terms and Conditions. 330 | 331 | a. The Licensor shall not be bound by any additional or different 332 | terms or conditions communicated by You unless expressly agreed. 333 | 334 | b. Any arrangements, understandings, or agreements regarding the 335 | Licensed Material not stated herein are separate from and 336 | independent of the terms and conditions of this Public License. 337 | 338 | 339 | Section 8 -- Interpretation. 340 | 341 | a. For the avoidance of doubt, this Public License does not, and 342 | shall not be interpreted to, reduce, limit, restrict, or impose 343 | conditions on any use of the Licensed Material that could lawfully 344 | be made without permission under this Public License. 345 | 346 | b. To the extent possible, if any provision of this Public License is 347 | deemed unenforceable, it shall be automatically reformed to the 348 | minimum extent necessary to make it enforceable. If the provision 349 | cannot be reformed, it shall be severed from this Public License 350 | without affecting the enforceability of the remaining terms and 351 | conditions. 352 | 353 | c. No term or condition of this Public License will be waived and no 354 | failure to comply consented to unless expressly agreed to by the 355 | Licensor. 356 | 357 | d. Nothing in this Public License constitutes or may be interpreted 358 | as a limitation upon, or waiver of, any privileges and immunities 359 | that apply to the Licensor or You, including from the legal 360 | processes of any jurisdiction or authority. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![License: CC BY-NC-SA 4.0](https://img.shields.io/badge/License-CC%20BY--NC--SA%204.0-lightgrey.svg) ![](https://parg.co/bDm) 2 | 3 | ![](https://assets.ng-tech.icu/item/20230417205536.png) 4 | 5 | # 服务器与鉴权 6 | 7 | 软件开发就是把一个复杂的问题分解为一系列简单的问题,再把一系列简单的解决方案组合成一个复杂的解决方案。而软件开发中最大的挑战,就是即能够快速高效地针对需求、环境的变化做出改变,也能够持续提供稳定、高可用的服务。 8 | 9 | 在笔者的系列文章中,分门别类地讨论了 Web、服务端、基础架构等某些具体的领域,而本系列关注形而上的软件工程相关的思考方式、通用能力与设计原则。为了更好地阐述,本系列默认使用 Java 为实现语言,不过你也可以在各篇的编程实现章节中查阅 TS、Go、Python、PHP、Rust 等其他版本的实现。 10 | 11 | # Preface | 前言,从编码到软件系统 12 | 13 | 一般来说,在软件工程中,我们往往会面临三类问题:性能问题(The Performant System Problem)、系统的嵌入式问题(the Embedded System Problem)以及复杂领域建模问题(the Complex Domain Problem)。 14 | 15 | ![](https://assets.ng-tech.icu/item/20230417205450.png) 16 | 17 | 系统泛指由一群有关联的个体组成,根据某种规则运作,能完成个别元件不能单独完成的工作的群体。子系统也是由一群有关联的个体所组成的系统,多半会是更大系统中的一部分。 18 | 19 | 关联:系统是由一群有关联的个体组成的,没有关联的个体堆在一起不能成为一个系统。例如,把一个发动机和一台 PC 放在一起不能称之为一个系统,把发动机、底盘、轮胎、车架组合起来才能成为一台汽车。 20 | 21 | 规则:系统内的个体需要按照指定的规则运作,而不是单个个体各自为政。规则规定了系统内个体分工和协作的方式。例如,汽车发动机负责产生动力,然后通过变速器和传动轴,将动力输出到车轮上,从而驱动汽车前进。 22 | 23 | 能力:系统能力与个体能力有本质的差别,系统能力不是个体能力之和,而是产生了新的能力。例如,汽车能够载重前进,而发动机、变速器、传动轴、车轮本身都不具备这样的能力。 24 | 25 | # 模块与组件 26 | 27 | 软件模块(Module)是一套一致而互相有紧密关连的软件组织。它分别包含了程序和数据结构两部分。现代软件开发往往利用模块作为合成的单位。模块的接口表达了由该模块提供的功能和调用它时所需的元素。模块是可能分开被编写的单位。这使它们可再用和允许人员同时协作、编写及研究不同的模块。 28 | 29 | 软件组件定义为自包含的、可编程的、可重用的、与语言无关的软件单元,软件组件可以很容易被用于组装应用程序中。模块和组件都是系统的组成部分,只是从不同的角度拆分系统而已。 30 | 31 | 从逻辑的角度来拆分系统后,得到的单元就是“模块”;从物理的角度来拆分系统后,得到的单元就是“组件”。划分模块的主要目的是职责分离;划分组件的主要目的是单元复用。其实,“组件”的英文 component 也可翻译成中文的“零件”一词,“零件”更容易理解一些,“零件”是一个物理的概念,并且具备“独立且可替换”的特点。 32 | 33 | # 框架与架构 34 | 35 | 软件框架(Software framework)通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。框架是组件规范,提供基础功能的产品。 36 | 37 | 软件架构指软件系统的顶层结构。首先,“系统是一群关联个体组成”,这些“个体”可以是“子系统”“模块”“组件”等;架构需要明确系统包含哪些“个体”。 38 | 39 | 其次,系统中的个体需要“根据某种规则”运作,架构需要明确个体运作和协作的规则。 40 | 41 | 第三,维基百科定义的架构用到了“基础结构”这个说法,我改为“顶层结构”,可以更好地区分系统和子系统,避免将系统架构和子系统架构混淆在一起导致架构层次混乱。 42 | 43 | # 逻辑 44 | 45 | 逻辑的抽象复用可以说是所有软件开发活动中最为重要的一条原则,衡量一个程序员代码水平的重要原则之一就是看他代码中重复代码和相似代码的比例。大量重复代码或相似代码背后反映的是工程师思维的懒惰,因为他觉得复制粘贴或者直接照着抄是最省事的做法。这样做不仅看上去非常的丑陋,而且也非常容易出错,更不用提维护起来的难度。 46 | 47 | # 数据中台 48 | 49 | 产品化是一种技术沉淀,从 CASE BY CASE 支持业务功能过程中,作抽象,把一些通用功能和模块独立出来,配合一些周边的技术工具和便利化的管理界面,形成产品化的结果,更高效的实现一种或多种业务需求,是技术效能提升的方法之一。产品化作得好的系统是一个闭封性优秀的系统,尽量不要外面的使用者关心我内部的实现。 50 | 51 | 模块化是把系统分离的一种设计思路,模块化后可以方便重复使用和插拨到不同的平台,不同的业务逻辑过程中。 52 | 53 | 产品化的系统一般都会在内部实现组件化,把模块独立抽象成组件,分离组件边界和责任,便于独立升级和维护。组件化可以让一个产品化的系统更可维护。 54 | 55 | 平台是一组程序的集合,提供了一系列的接口或界面。平台化是让一个系统具备装载很多不同外部系统的能力。平台化作得好的系统,要吧让外部系统很方便的了解你的内部能力,实现自由方便的插拨。平台化是一种对外的能力,提升的是外部系统与平台系统之间的联接能力,以开放作为设计目标,而不是封闭。 56 | 57 | 我们的系统有很多服务中心,会员中心,商品中心,产易中心和营销中心等。建这些中心的目的是为了业务系统解偶,为了让业务系统能更专注各自的业务逻辑,让通用业务下沉或平移抽出成为服务中心。中心化和组件化的不同一般是远程和本地的区别。以前客户端软件多用组件化设计,而互联网架构多用中心化,服务中心内部可以有不同的组件化设计,业务系统内部也常用组件化的设计。 58 | 中心化可以隔离易变和不易变的逻辑,也可以隔离不同运维策略的业务逻辑。中心化的系统一般符合职责单一原则,在面对对象设计的理论下,中心化可以看作一个逻辑上的大的对象来对待。服务中心是一个完整的业务闭包,可以完成一整组业务逻辑。 59 | 中心化不一定需要服务化。业务单元也可以中心化。 60 | 61 | 服务化,服务化一般是指远程调用的形式,我们关注调用的协议,API 和内容的格式。一个 API 可以独立服务化,一个服务中心也可以通过网关来实现服务化。 62 | 服务化有时候也用本地的二方库,类库包的形式提供服务。 63 | 服务化关注服务的治理策略和方法,手段。服务化关注服务注册,服务协调,服务可用性,服务通讯协议和内容交换。 64 | 65 | SOA 是为了更好的集成,为了隔离不同逻辑之间的互相影响而形成的架构设计理念。服务化是 SOA 的一种实现方式。SPI 是一个接口标准规范,需要别人去实现;API 是标准的实现,可以直接使用。 66 | 67 | 架构师是一个既需要看全局整体又需要攻坚局部瓶颈并能在业务场景中平衡取舍方案的人,中台和前台,后台都需要架构师,架构师有自已的领域经验。 68 | 69 | 架构师是分领域的。架构师通过领域建模来划分业务边界,用领域经验来隔离变化,预测变化。很多系统的重复建设,与架架构的领域分拆不清楚有关系。可以借这个底层的实体关系模型来帮助系统建模,找到所有角色、实体对象和关系。 70 | 领域建模是一个抽象事实的过程。要为将来的业务扩展性和变化作好足够的准备。 71 | 72 | 中台和前台的区别是,前台实现业务逻辑和试错,需要比较多的人,这些人可能会失败。而中台实现通用逻辑和工具,产品,帮助前台快速试错,中台失败的可能性应该比较低,把可能错的事让前台去完成。中台人要少,前台人要多。如果前台人少,中台人多,那这个中台基本上应该归属到前台。中台的重点是产品化和平台化。中台应该是一系列能力的组合,包括组织规范和系统的规范,人和系统的一个方法论,标准。 73 | 74 | 诉求不是需求,而是需求的本质。电商业务中台由一系列业务能力标准、运行机制、业务分析方法论,配置管理和执行系统以及运营服务团队构成的体系,提供各业务方能够快速,低成本创新的能力。 75 | 76 | 定义能力的定义与标准,提供一个能力注册、发现、查询等管理的机制与管理系统;提供全局性的业务或应用注册管理中心,要让业务跑在大平台上,而不是一个或几个平台上。 77 | 78 | 提供一套或多套领域对象建模方法论或标准,比如按领域驱动设计 DDD;提供一系列成熟的案例或解决方案,提供多端化开发规范。 79 | 80 | 定义平台化的标准,定义可以实现平台化的框架的标准,比如框架需要提供模块化、流程化、插件、扩展等功能。平台化的标准是什么?玄难大师有个很好的比喻,如果你的业务能一键上线或下线,就接近平台化了。这里的一键上下线,不是指单纯的切换一个开关,而是要做到部署的代码里不再有和该业务相关的任何代码。 81 | 82 | 平台化的基础构建,定义业务规则的定义,提供一个统一的业务规则平台(非中心,非动态编译引擎),支持自定义 DSL 插件,权限,审批流程等等,最终每个业务方都可形成自己业务领域的知识库。建设元数据管理中心,与业务规则等平台打通。提供一套服务鉴权的机制。可配置固然重要,但其重要性远低于平台化。 83 | 84 | # 软件设计 85 | 86 | 软件设计有以下四大目标:简单、正确、一致、完整,但两大流派 MIT Style (MIT AI Lab 是 LISP 重镇) 和 New Jersey Style (C 和 UNIX 的老家贝尔实验室所在地) 对这些目标的优先级排序不同。MIT Style 认为软件正确性要绝对保证,然后优先级 正确 ~= 一致 > 完整 > 简单,简单这一条还得分,为了接口简单,可以忍受实现复杂。而 New Jersey Style 是正好反过来:首先软件实现得简单,做不到宁愿让接口复杂点,为了简单显然可以牺牲完整性,而正确、一致,那就尽力吧…… 反正得简单。Worse is Better 前面的 Worse 指的就是像 UNIX 这样为简单甚至能放弃正确这种有绝对标准的好的东西,后面的 Better, 指的是更好的生存适应性,这里面不带价值判断,文章作者也为 "Worse Is Better Is Worse" or "Worse is Better is Still Better" 一直在纠结,但这是一个能解释很多现象的准确观察。 87 | 88 | ## 性能 89 | 90 | 以著名的 Twitter 为例,你注册,你发推文,你喜欢别人的推文,这就是它。如果 Twitter 很简单,为什么其他人不能这样做呢?很明显,Twitter 的真正挑战实际上不是“它做什么”,而是它“如何做它能做的事情”。 91 | 92 | Twitter 面临着每天为大约 5 亿用户提供服务的独特挑战。Twitter 解决的难题实际上是一个性能问题。当挑战是表现时,我们是否使用严格类型的语言并不重要。 93 | 94 | ## 系统嵌入 95 | 96 | 嵌入式系统是计算机硬件和软件的组合,其目的是实现对系统的机械或电气方面的控制。我们今天使用的大多数系统都是建立在一个非常复杂的代码层上,如果不是最初编写的,通常会编译成 C 或 C++ 。 97 | 98 | 用这些语言编码不适合胆小的人。在 C 中,没有物体这样的东西; 我们作为人类喜欢物体,因为我们可以很容易地理解它们。C 是程序性的,这使我们用这种语言编写的代码更加难以保持清洁。这些问题还需要了解较低级别的细节。 99 | 100 | C++ 确实让生活变得更好,因为它具有面向对象,但挑战仍然是与较低级别的硬件细节进行有趣的交互。因为我们对这些问题使用的语言并没有那么多的选择,所以在这里讨论 TypeScript 是无关紧要的。 101 | 102 | ## 编程 103 | 104 | ## 架构与复杂性控制 105 | 106 | 对于某些问题,这个挑战不是关于处理更多请求的扩展,而是根据代码库的大小进行扩展。企业公司有复杂的现实问题需要解决。在这些公司中,最大的工程挑战通常是: 107 | 108 | - 能够逻辑地(通过域)将整块的各个部分分成更小的应用程序。然后,物理上(通过有界上下文的微服务)将它们分开,以便可以分配团队来维护它们 109 | 110 | - 处理这些应用程序之间的集成和同步 111 | 112 | - 对域概念进行建模并实际解决域的问题 113 | 114 | - 创建由开发人员和领域专家共享的无处不在(包罗万象)的语言 115 | 116 | - 不会迷失在大量编写的代码中,并且在不破坏现有代码的情况下无法添加新功能 117 | 118 | 我基本上描述了 Domain Driven Design 解决的问题类型。对于这些类型的项目,您甚至不会考虑不使用像 TypeScript 这样的严格类型的语言。 119 | 120 | ## 强/弱类型语言 121 | 122 | 对于设计到复杂领域建模的问题,如果您不选择 TypeScript 而是选择 JavaScript,则需要一些额外的努力才能成功。您不仅需要更加熟悉 vanilla JavaScript 中的对象建模功能,而且还必须知道如何利用面向对象编程的 4 个原则(封装,抽象,继承和多态)。 123 | 124 | 这可能很难做到。JavaScript 自然不具备接口和抽象类的概念。使用 vanilla JavaScript 无法轻松实现 SOLID 设计原则中的“接口隔离”。单独使用 JavaScript 也需要一定程度的纪律作为开发人员才能保持代码清洁,而且一旦代码库足够大,这一点至关重要。您还需要确保您的团队在如何在 JavaScript 中实现通用设计模式方面拥有相同的学科,经验和知识水平。如果没有,你需要引导他们。 125 | 126 | 在像这样的领域驱动项目中,使用严格类型语言的强大好处不是表达可以做什么,而是更多关于使用封装和信息隐藏来通过限制实际允许的域对象来减少错误的表面区域。代码大小通常与复杂域问题相关联,其中大型代码库意味着复杂的域,但情况并非总是如此。当项目的代码量达到一定大小时,跟踪存在的所有内容变得更加困难,并且更容易最终重新实现已编码的内容。 127 | 128 | 复制是精心设计和稳定的软件的敌人。当新开发人员开始在已经很大的代码库上编码时,这一点尤为明显。Visual Studio Code 的自动完成和 Intellisense 有助于浏览大型项目。它适用于 TypeScript,但它在 JavaScript 方面有限。对于我知道将保持简单和小型的项目,或者如果我知道它最终将被丢弃,我将不太愿意推荐 TypeScript 作为必需品。 129 | 130 | ## 生产级别软件与 Demo 131 | 132 | 生产软件是您关心的代码,或者如果它不起作用您将遇到麻烦的代码。这也是你为其编写测试的代码。一般的经验法则是,如果您关心代码,则需要对其进行单元测试。如果您不在乎,请不要进行测试。 133 | 134 | 宠物项目是不言自明的。做你喜欢的事。您没有专业承诺维护任何标准的工艺。 135 | 136 | ## 性能与高可用 137 | 138 | ## 研发效能 139 | 140 | # About 141 | 142 | ## Copyright & More | 延伸阅读 143 | 144 | 笔者所有文章遵循 [知识共享 署名-非商业性使用-禁止演绎 4.0 国际许可协议](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh),欢迎转载,尊重版权。您还可以前往 [NGTE Books](https://ng-tech.icu/books-gallery/) 主页浏览包含知识体系、编程语言、软件工程、模式与架构、Web 与大前端、服务端开发实践与工程架构、分布式基础架构、人工智能与深度学习、产品运营与创业等多类目的书籍列表: 145 | 146 | [![NGTE Books](https://s2.ax1x.com/2020/01/18/19uXtI.png)](https://ng-tech.icu/books-gallery/) 147 | -------------------------------------------------------------------------------- /_sidebar.md: -------------------------------------------------------------------------------- 1 | - [1 01~服务器 [4]](/01~服务器/README.md) 2 | - [1.1 Apache](/01~服务器/Apache/README.md) 3 | 4 | - [1.2 Caddy](/01~服务器/Caddy/README.md) 5 | 6 | - [1.3 Nginx [3]](/01~服务器/Nginx/README.md) 7 | - [1.3.1 HTTP 服务器配置](/01~服务器/Nginx/HTTP%20服务器配置.md) 8 | - [1.3.2 基础配置](/01~服务器/Nginx/基础配置.md) 9 | - [1.3.3 进程模型](/01~服务器/Nginx/进程模型.md) 10 | - [1.4 OpenResty](/01~服务器/OpenResty/README.md) 11 | 12 | - 2 99~参考资料 [2] 13 | - [2.1 Awesome System Design Articles](/99~参考资料/Awesome%20System%20Design%20Articles/README.md) 14 | 15 | - 2.2 BytebyteGo~System Design 101 [1] 16 | - [2.2.1 2023~BytebyteGo~System Design 101](/99~参考资料/BytebyteGo~System%20Design%20101/2023~BytebyteGo~System%20Design%20101.md) 17 | - [3 INTRODUCTION](/INTRODUCTION.md) 18 | - 4 大厂实践 [1] 19 | - 4.1 Amazon [1] 20 | - 4.1.1 AWS [1] 21 | - 4.1.1.1 S3 [1] 22 | - [4.1.1.1.1 Building and operating a pretty big storage system called S3](/大厂实践/Amazon/AWS/S3/2023-Building%20and%20operating%20a%20pretty%20big%20storage%20system%20called%20S3.md) 23 | - [5 权限认证 [6]](/权限认证/README.md) 24 | - [5.1 OAuth [2]](/权限认证/OAuth/README.md) 25 | - 5.1.1 99~参考资料 [1] 26 | - [5.1.1.1 Sheng OAuth2.0 协议](/权限认证/OAuth/99~参考资料/2022-Sheng-OAuth2.0%20协议.md) 27 | - 5.1.2 OIDC [1] 28 | - 5.1.2.1 99~参考资料 [1] 29 | - [5.1.2.1.1 Sheng OIDC 协议](/权限认证/OAuth/OIDC/99~参考资料/2021-Sheng-OIDC%20协议.md) 30 | - [5.2 RBAC](/权限认证/RBAC/README.md) 31 | 32 | - [5.3 SSO [4]](/权限认证/SSO/README.md) 33 | - [5.3.1 CAS](/权限认证/SSO/CAS/README.md) 34 | 35 | - [5.3.2 Hydra](/权限认证/SSO/Hydra/README.md) 36 | 37 | - [5.3.3 Keycloak](/权限认证/SSO/Keycloak/README.md) 38 | 39 | - [5.3.4 Pac4j](/权限认证/SSO/Pac4j/README.md) 40 | 41 | - [5.4 多租户 [1]](/权限认证/多租户/README.md) 42 | - [5.4.1 通用多租户框架](/权限认证/多租户/通用多租户框架.md) 43 | - [5.5 开放平台 [1]](/权限认证/开放平台/README.md) 44 | - [5.5.1 扫码登陆](/权限认证/开放平台/扫码登陆.md) 45 | - [5.6 认证基础 [3]](/权限认证/认证基础/README.md) 46 | - 5.6.1 99~参考资料 [1] 47 | - [5.6.1.1 Password, Session, Cookie, Token, JWT, SSO, OAuth Authentication Explained](/权限认证/认证基础/99~参考资料/2023-Password,%20Session,%20Cookie,%20Token,%20JWT,%20SSO,%20OAuth%20-%20Authentication%20Explained.md) 48 | - 5.6.2 HTTP [2] 49 | - [5.6.2.1 HTTP 认证](/权限认证/认证基础/HTTP/HTTP%20认证.md) 50 | - [5.6.2.2 Session](/权限认证/认证基础/HTTP/Session.md) 51 | - 5.6.3 JWT [2] 52 | - 5.6.3.1 99~参考资料 [1] 53 | - [5.6.3.1.1 2023~Stop using JSON Web tokens for user sessions](/权限认证/认证基础/JWT/99~参考资料/2023~Stop%20using%20JSON%20Web%20tokens%20for%20user%20sessions.md) 54 | - [5.6.3.2 JWT](/权限认证/认证基础/JWT/JWT.md) -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SoftwareEngineering-Series 7 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 34 | 38 | 40 | 45 | 46 |
47 | 64 | 97 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 143 | 144 | 145 | 146 | 155 | 156 | 157 | --------------------------------------------------------------------------------